From 67527e61c44e01199cb327dd526bdf126f028066 Mon Sep 17 00:00:00 2001 From: "SUSE Observability AI (POC)" Date: Thu, 1 Oct 2026 05:47:50 +0000 Subject: [PATCH 1/9] Migrate remaining YAML imports to maintained libraries Keep Kubernetes collection/configuration on the v2 API and installer/test parsing on v3. Replace Windows ghodss conversions with sigs.k8s.io/yaml v1.6.0, preserving YAML-to-JSON schema validation and JSON behavior. Upgrade Testify to v1.12.1 to remove its legacy parser import from both production-reachable helpers and tests. Its maintained v3 requirement advances the workspace to v3.0.5; synchronize and tidy registered modules without changing ignored or unregistered dependency fixtures. Add configuration fixtures for aliases, merges, multiline strings, boolean and integer interpretation, and explicit nulls. Correct stale development commands and maintained-parser references in active documentation. Kubernetes parser/event regressions, installer, profiler, configuration and core telemetry/configuration tests pass. The root and 185 registered submodules tidy successfully. Existing Windows configuration fixtures retain identical JSON conversion. Product package graphs resolve on Linux amd64/arm64. A native cluster-agent amd64 build passes with pinned Go 1.26.6. Diff-aware root lint and the changed installer package lint pass; broader lint retains pre-existing findings. The Kubernetes bundled-events timeout and inherited Windows compile errors also occur at baseline dd1cba03843f4580b1634a1583cc79ac444dbf89. Transitive NetFlow, ordered-map, Cloud Foundry and Datadog OPA parser imports remain; Windows E2E tooling retains Pulumi/ESC dependencies. No blanket parser replacement or upstream agent upgrade is applied. Tracking: https://github.com/StackVista/stackstate/issues/717 --- CLAUDE.md | 18 +++++----- comp/api/api/def/go.mod | 5 ++- comp/api/api/def/go.sum | 14 +++----- .../winregistry/impl/winregistryimpl.go | 2 +- comp/core/agenttelemetry/def/go.mod | 2 +- comp/core/agenttelemetry/def/go.sum | 23 +++---------- comp/core/agenttelemetry/fx/go.mod | 9 ++--- comp/core/agenttelemetry/fx/go.sum | 16 +++------ comp/core/agenttelemetry/impl/go.mod | 9 ++--- comp/core/agenttelemetry/impl/go.sum | 16 +++------ comp/core/config/go.mod | 9 ++--- comp/core/config/go.sum | 16 +++------ comp/core/configsync/go.mod | 8 ++--- comp/core/configsync/go.sum | 16 +++------ .../delegatedauth/api/cloudauth/aws/go.mod | 8 ++--- .../delegatedauth/api/cloudauth/aws/go.sum | 17 +++------- comp/core/delegatedauth/go.mod | 7 ++-- comp/core/delegatedauth/go.sum | 15 +++------ comp/core/flare/types/go.mod | 5 ++- comp/core/flare/types/go.sum | 14 +++----- comp/core/hostname/hostnameinterface/go.mod | 7 ++-- comp/core/hostname/hostnameinterface/go.sum | 21 +++--------- comp/core/ipc/httphelpers/go.mod | 9 ++--- comp/core/ipc/httphelpers/go.sum | 16 +++------ comp/core/ipc/impl/go.mod | 8 ++--- comp/core/ipc/impl/go.sum | 16 +++------ comp/core/ipc/mock/go.mod | 8 ++--- comp/core/ipc/mock/go.sum | 16 +++------ comp/core/log/def/go.mod | 12 ++----- comp/core/log/def/go.sum | 27 +++------------ comp/core/log/fx/go.mod | 9 ++--- comp/core/log/fx/go.sum | 16 +++------ comp/core/log/impl-trace/go.mod | 9 ++--- comp/core/log/impl-trace/go.sum | 16 +++------ comp/core/log/impl/go.mod | 9 ++--- comp/core/log/impl/go.sum | 16 +++------ comp/core/log/mock/go.mod | 2 +- comp/core/log/mock/go.sum | 23 +++---------- comp/core/secrets/fx/go.mod | 8 ++--- comp/core/secrets/fx/go.sum | 15 +++------ comp/core/secrets/impl/go.mod | 8 ++--- comp/core/secrets/impl/go.sum | 15 +++------ comp/core/secrets/mock/go.sum | 12 +++---- comp/core/secrets/noop-impl/go.sum | 14 +++----- comp/core/secrets/utils/go.mod | 12 ++----- comp/core/secrets/utils/go.sum | 27 +++------------ comp/core/status/go.mod | 8 ++--- comp/core/status/go.sum | 20 +++-------- comp/core/status/statusimpl/go.mod | 8 ++--- comp/core/status/statusimpl/go.sum | 15 +++------ comp/core/tagger/def/go.mod | 9 ++--- comp/core/tagger/def/go.sum | 16 +++------ comp/core/tagger/fx-remote/go.mod | 8 ++--- comp/core/tagger/fx-remote/go.sum | 15 +++------ comp/core/tagger/generic_store/go.mod | 7 ++-- comp/core/tagger/generic_store/go.sum | 21 +++--------- comp/core/tagger/impl-remote/go.mod | 11 ++----- comp/core/tagger/impl-remote/go.sum | 15 +++------ comp/core/tagger/origindetection/go.mod | 12 ++----- comp/core/tagger/origindetection/go.sum | 27 +++------------ comp/core/tagger/subscriber/go.mod | 8 ++--- comp/core/tagger/subscriber/go.sum | 21 +++--------- comp/core/tagger/telemetry/go.sum | 12 +++---- comp/core/tagger/types/go.mod | 10 ++---- comp/core/tagger/types/go.sum | 22 +++---------- comp/core/tagger/utils/go.mod | 12 ++----- comp/core/tagger/utils/go.sum | 27 +++------------ comp/core/telemetry/go.mod | 7 ++-- comp/core/telemetry/go.sum | 21 +++--------- comp/def/go.mod | 12 ++----- comp/def/go.sum | 27 +++------------ comp/forwarder/defaultforwarder/go.mod | 8 ++--- comp/forwarder/defaultforwarder/go.sum | 15 +++------ .../orchestrator/orchestratorinterface/go.mod | 8 ++--- .../orchestrator/orchestratorinterface/go.sum | 15 +++------ .../impl/agentprovider/provider_test.go | 2 +- comp/logs-library/go.mod | 7 ++-- comp/logs-library/go.sum | 21 +++--------- comp/logs/agent/config/go.mod | 9 ++--- comp/logs/agent/config/go.sum | 16 +++------ comp/otelcol/collector-contrib/def/go.mod | 7 ++-- comp/otelcol/collector-contrib/def/go.sum | 21 +++--------- comp/otelcol/collector-contrib/impl/go.mod | 4 +-- comp/otelcol/collector-contrib/impl/go.sum | 6 ++-- comp/otelcol/converter/def/go.mod | 6 ++-- comp/otelcol/converter/def/go.sum | 23 +++---------- comp/otelcol/converter/impl/go.mod | 12 +++---- comp/otelcol/converter/impl/go.sum | 16 +++------ comp/otelcol/ddflareextension/def/go.mod | 4 +-- comp/otelcol/ddflareextension/def/go.sum | 14 +++----- comp/otelcol/ddflareextension/impl/go.mod | 4 +-- comp/otelcol/ddflareextension/impl/go.sum | 6 ++-- comp/otelcol/ddprofilingextension/def/go.mod | 4 +-- comp/otelcol/ddprofilingextension/def/go.sum | 14 +++----- comp/otelcol/ddprofilingextension/impl/go.mod | 5 ++- comp/otelcol/ddprofilingextension/impl/go.sum | 7 ++-- comp/otelcol/logsagentpipeline/go.mod | 8 ++--- comp/otelcol/logsagentpipeline/go.sum | 16 +++------ .../logsagentpipelineimpl/go.mod | 9 ++--- .../logsagentpipelineimpl/go.sum | 16 +++------ .../exporter/datadogexporter/go.mod | 6 ++-- .../exporter/datadogexporter/go.sum | 9 +++-- .../exporter/logsagentexporter/go.mod | 7 ++-- .../exporter/logsagentexporter/go.sum | 15 +++------ .../exporter/serializerexporter/go.mod | 7 ++-- .../exporter/serializerexporter/go.sum | 15 +++------ .../otlp/components/metricsclient/go.mod | 8 ++--- .../otlp/components/metricsclient/go.sum | 19 +++-------- .../processor/infraattributesprocessor/go.mod | 7 ++-- .../processor/infraattributesprocessor/go.sum | 15 +++------ comp/otelcol/otlp/testutil/go.mod | 8 ++--- comp/otelcol/otlp/testutil/go.sum | 16 +++------ comp/otelcol/status/impl/go.mod | 8 ++--- comp/otelcol/status/impl/go.sum | 15 +++------ comp/serializer/logscompression/go.mod | 8 ++--- comp/serializer/logscompression/go.sum | 16 +++------ comp/serializer/metricscompression/go.mod | 8 ++--- comp/serializer/metricscompression/go.sum | 16 +++------ comp/trace/agent/def/go.sum | 14 +++----- .../components/shared_features/flares.md | 2 +- go.mod | 9 +++-- go.sum | 8 ++--- internal/tools/go.mod | 6 ++-- internal/tools/go.sum | 11 +++---- internal/tools/modparser/go.mod | 12 ++----- internal/tools/modparser/go.sum | 27 +++------------ internal/tools/worksynchronizer/go.mod | 13 ++------ internal/tools/worksynchronizer/go.sum | 29 +++------------- pkg/aggregator/ckey/go.mod | 9 ++--- pkg/aggregator/ckey/go.sum | 21 +++--------- pkg/api/go.mod | 9 ++--- pkg/api/go.sum | 17 +++------- .../cluster/kubeapi/kubernetes_events.go | 2 +- .../cluster/kubeapi/kubernetes_metrics.go | 2 +- .../kubeapi/kubernetes_topology_config.go | 18 +++++----- .../kubeapi/kubernetes_topology_test.go | 33 ++++++++++++++++--- .../windowscertificate/windows_certificate.go | 2 +- pkg/config/basic/go.mod | 12 ++----- pkg/config/basic/go.sum | 27 +++------------ pkg/config/create/go.mod | 9 ++--- pkg/config/create/go.sum | 17 +++------- pkg/config/env/go.mod | 8 ++--- pkg/config/env/go.sum | 23 +++---------- pkg/config/helper/go.mod | 9 ++--- pkg/config/helper/go.sum | 17 +++------- pkg/config/mock/go.mod | 9 ++--- pkg/config/mock/go.sum | 17 +++------- pkg/config/nodetreemodel/go.mod | 9 ++--- pkg/config/nodetreemodel/go.sum | 17 +++------- pkg/config/remote/go.mod | 7 ++-- pkg/config/remote/go.sum | 11 +++---- pkg/config/render_config/go.mod | 8 +---- pkg/config/render_config/go.sum | 19 ++--------- pkg/config/render_config/render_config.go | 4 +-- pkg/config/setup/go.mod | 9 ++--- pkg/config/setup/go.sum | 17 +++------- pkg/config/structure/go.mod | 9 ++--- pkg/config/structure/go.sum | 17 +++------- pkg/config/teeconfig/go.mod | 7 ++-- pkg/config/teeconfig/go.sum | 23 +++---------- pkg/config/utils/go.mod | 9 ++--- pkg/config/utils/go.sum | 17 +++------- pkg/config/viperconfig/go.mod | 9 ++--- pkg/config/viperconfig/go.sum | 17 +++------- pkg/errors/go.mod | 12 ++----- pkg/errors/go.sum | 27 +++------------ pkg/fleet/installer/go.mod | 7 ++-- pkg/fleet/installer/go.sum | 21 +++--------- .../packages/datadog_agent_extensions.go | 2 +- .../packages/datadog_agent_extensions_test.go | 29 +++++++++++++++- pkg/gohai/go.mod | 8 ++--- pkg/gohai/go.sum | 23 +++---------- pkg/logs/client/go.mod | 8 ++--- pkg/logs/client/go.sum | 16 +++------ pkg/logs/diagnostic/go.mod | 8 ++--- pkg/logs/diagnostic/go.sum | 16 +++------ pkg/logs/message/go.mod | 8 ++--- pkg/logs/message/go.sum | 17 +++------- pkg/logs/metrics/go.mod | 7 ++-- pkg/logs/metrics/go.sum | 21 +++--------- pkg/logs/pipeline/go.mod | 8 ++--- pkg/logs/pipeline/go.sum | 16 +++------ pkg/logs/processor/go.mod | 8 ++--- pkg/logs/processor/go.sum | 16 +++------ pkg/logs/sender/go.mod | 8 ++--- pkg/logs/sender/go.sum | 16 +++------ pkg/logs/sources/go.mod | 8 ++--- pkg/logs/sources/go.sum | 17 +++------- pkg/logs/status/utils/go.mod | 12 ++----- pkg/logs/status/utils/go.sum | 27 +++------------ pkg/logs/util/testutils/go.mod | 3 +- pkg/logs/util/testutils/go.sum | 17 +++------- pkg/metrics/go.mod | 8 ++--- pkg/metrics/go.sum | 16 +++------ pkg/network/driver/go.mod | 8 ++--- pkg/network/driver/go.sum | 21 +++--------- pkg/networkdevice/profile/go.mod | 5 ++- pkg/networkdevice/profile/go.sum | 10 +++--- pkg/networkpath/payload/go.mod | 12 ++----- pkg/networkpath/payload/go.sum | 27 +++------------ pkg/obfuscate/go.mod | 11 ++----- pkg/obfuscate/go.sum | 25 +++----------- .../inframetadata/go.mod | 10 ++---- .../inframetadata/go.sum | 23 +++---------- .../gohai/internal/gohaitest/go.mod | 10 ++---- .../gohai/internal/gohaitest/go.sum | 25 +++----------- .../otlp/attributes/go.mod | 6 ++-- .../otlp/attributes/go.sum | 23 +++---------- pkg/opentelemetry-mapping-go/otlp/logs/go.mod | 11 ++----- pkg/opentelemetry-mapping-go/otlp/logs/go.sum | 23 +++---------- .../otlp/metrics/go.mod | 10 ++---- .../otlp/metrics/go.sum | 21 +++--------- pkg/opentelemetry-mapping-go/otlp/rum/go.mod | 11 ++----- pkg/opentelemetry-mapping-go/otlp/rum/go.sum | 23 +++---------- pkg/orchestrator/model/go.mod | 2 +- pkg/orchestrator/model/go.sum | 23 +++---------- pkg/orchestrator/util/go.mod | 12 ++----- pkg/orchestrator/util/go.sum | 27 +++------------ pkg/process/util/api/go.mod | 9 ++--- pkg/process/util/api/go.sum | 16 +++------ pkg/proto/go.mod | 7 ++-- pkg/proto/go.sum | 13 +++----- pkg/remoteconfig/state/go.mod | 7 ++-- pkg/remoteconfig/state/go.sum | 13 +++----- pkg/security/secl/go.mod | 8 ++--- pkg/security/secl/go.sum | 19 +++-------- pkg/security/seclwin/go.sum | 12 +++---- pkg/serializer/go.mod | 8 ++--- pkg/serializer/go.sum | 15 +++------ pkg/ssi/testutils/go.mod | 6 ++-- pkg/ssi/testutils/go.sum | 18 +++------- pkg/status/health/go.mod | 12 ++----- pkg/status/health/go.sum | 27 +++------------ pkg/tagger/types/go.sum | 12 +++---- pkg/tagset/go.mod | 10 ++---- pkg/tagset/go.sum | 22 +++---------- pkg/telemetry/go.mod | 7 ++-- pkg/telemetry/go.sum | 21 +++--------- pkg/trace/go.mod | 6 ++-- pkg/trace/go.sum | 9 +++-- pkg/trace/log/go.mod | 12 ++----- pkg/trace/log/go.sum | 27 +++------------ pkg/trace/otel/go.mod | 8 ++--- pkg/trace/otel/go.sum | 11 +++---- pkg/trace/stats/go.mod | 8 ++--- pkg/trace/stats/go.sum | 12 +++---- pkg/trace/traceutil/go.mod | 8 ++--- pkg/trace/traceutil/go.sum | 22 +++---------- pkg/util/aws/creds/go.mod | 8 ++--- pkg/util/aws/creds/go.sum | 17 +++------- pkg/util/backoff/go.mod | 12 ++----- pkg/util/backoff/go.sum | 27 +++------------ pkg/util/buf/go.mod | 12 ++----- pkg/util/buf/go.sum | 27 +++------------ pkg/util/cache/go.mod | 12 ++----- pkg/util/cache/go.sum | 27 +++------------ pkg/util/cgroups/go.mod | 8 ++--- pkg/util/cgroups/go.sum | 23 +++---------- pkg/util/common/go.mod | 12 ++----- pkg/util/common/go.sum | 27 +++------------ pkg/util/compression/go.mod | 8 ++--- pkg/util/compression/go.sum | 16 +++------ pkg/util/containers/image/go.mod | 12 ++----- pkg/util/containers/image/go.sum | 27 +++------------ pkg/util/defaultpaths/go.mod | 3 +- pkg/util/defaultpaths/go.sum | 17 +++------- pkg/util/executable/go.mod | 12 ++----- pkg/util/executable/go.sum | 27 +++------------ pkg/util/filesystem/go.mod | 8 ++--- pkg/util/filesystem/go.sum | 23 +++---------- pkg/util/flavor/go.mod | 9 ++--- pkg/util/flavor/go.sum | 17 +++------- pkg/util/fxutil/go.mod | 7 ++-- pkg/util/fxutil/go.sum | 21 +++--------- pkg/util/grpc/go.mod | 8 ++--- pkg/util/grpc/go.sum | 15 +++------ pkg/util/hostinfo/go.mod | 8 ++--- pkg/util/hostinfo/go.sum | 23 +++---------- pkg/util/hostname/validate/go.mod | 8 ++--- pkg/util/hostname/validate/go.sum | 23 +++---------- pkg/util/http/go.mod | 9 ++--- pkg/util/http/go.sum | 17 +++------- pkg/util/json/go.mod | 10 ++---- pkg/util/json/go.sum | 27 +++------------ pkg/util/jsonquery/go.mod | 7 ++-- pkg/util/jsonquery/go.sum | 23 +++---------- .../apiserver/common/namespace/go.mod | 3 +- .../apiserver/common/namespace/go.sum | 17 +++------- pkg/util/log/go.mod | 8 ++--- pkg/util/log/go.sum | 23 +++---------- pkg/util/log/setup/go.mod | 9 ++--- pkg/util/log/setup/go.sum | 17 +++------- pkg/util/option/go.mod | 12 ++----- pkg/util/option/go.sum | 27 +++------------ pkg/util/otel/go.mod | 6 ++-- pkg/util/otel/go.sum | 23 +++---------- pkg/util/prometheus/go.mod | 9 ++--- pkg/util/prometheus/go.sum | 24 +++----------- pkg/util/quantile/go.mod | 8 ++--- pkg/util/quantile/go.sum | 22 +++---------- pkg/util/quantile/sketchtest/go.mod | 12 ++----- pkg/util/quantile/sketchtest/go.sum | 27 +++------------ pkg/util/scrubber/go.mod | 11 ++----- pkg/util/scrubber/go.sum | 24 +++----------- pkg/util/sort/go.mod | 12 ++----- pkg/util/sort/go.sum | 27 +++------------ pkg/util/startstop/go.mod | 12 ++----- pkg/util/startstop/go.sum | 27 +++------------ pkg/util/statstracker/go.mod | 12 ++----- pkg/util/statstracker/go.sum | 27 +++------------ pkg/util/system/go.mod | 8 ++--- pkg/util/system/go.sum | 23 +++---------- pkg/util/testutil/go.mod | 13 ++------ pkg/util/testutil/go.sum | 29 +++------------- pkg/util/utilizationtracker/go.mod | 12 ++----- pkg/util/utilizationtracker/go.sum | 27 +++------------ pkg/util/uuid/go.mod | 2 +- pkg/util/uuid/go.sum | 23 +++---------- pkg/util/winutil/go.mod | 8 ++--- pkg/util/winutil/go.sum | 23 +++---------- pkg/version/go.mod | 12 ++----- pkg/version/go.sum | 27 +++------------ test/e2e-framework/go.mod | 4 +-- test/e2e-framework/go.sum | 7 ++-- test/fakeintake/go.mod | 7 ++-- test/fakeintake/go.sum | 14 +++----- test/new-e2e/go.mod | 6 ++-- test/new-e2e/go.sum | 6 ++-- .../system_probe_config_test.go | 2 +- test/otel/go.mod | 7 ++-- test/otel/go.sum | 11 +++---- tools/build-ddot-byoc/go.mod | 4 +-- tools/build-ddot-byoc/go.sum | 12 +++---- 333 files changed, 1077 insertions(+), 3438 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 900799d141a8..41b3ab63bbca 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,17 +16,17 @@ The StackState Agent is a monitoring and observability agent forked from Datadog ## Common Development Commands ### Initial Setup -```bash -# 1. Install Python dependencies (use virtual environment recommended) -pip install -r requirements.txt -# 2. Install Go tools -invoke install-tools +Install the `dda` development CLI following [the setup guide](docs/public/setup/required.md). It manages the Python environment used by Invoke tasks. + +```bash +# 1. Install Go tools +dda inv install-tools -# 3. Install Go dependencies -invoke deps +# 2. Install Go dependencies +dda inv deps -# 4. Create dev configuration (stackstate.yaml, not datadog.yaml) +# 3. Create dev configuration (stackstate.yaml, not datadog.yaml) echo "api_key: " > dev/dist/stackstate.yaml ``` @@ -64,7 +64,7 @@ invoke test --targets=./pkg/collector/python ### Linting ```bash # Run Go linters (do this before committing) -invoke lint-go +dda inv linter.go # Run linters on specific module/targets invoke linter.go --targets=./pkg/collector/check,./pkg/aggregator diff --git a/comp/api/api/def/go.mod b/comp/api/api/def/go.mod index 9e7ad54276ba..0a83da110225 100644 --- a/comp/api/api/def/go.mod +++ b/comp/api/api/def/go.mod @@ -5,12 +5,11 @@ go 1.25.0 require go.uber.org/fx v1.24.0 require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect go.uber.org/dig v1.19.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect ) diff --git a/comp/api/api/def/go.sum b/comp/api/api/def/go.sum index 1e7c9b2a6c5c..6228232a9ca2 100644 --- a/comp/api/api/def/go.sum +++ b/comp/api/api/def/go.sum @@ -1,9 +1,5 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= @@ -14,9 +10,7 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/checks/winregistry/impl/winregistryimpl.go b/comp/checks/winregistry/impl/winregistryimpl.go index f9c864c6f13a..b2e4fd61a95c 100644 --- a/comp/checks/winregistry/impl/winregistryimpl.go +++ b/comp/checks/winregistry/impl/winregistryimpl.go @@ -31,11 +31,11 @@ import ( "github.com/DataDog/datadog-agent/pkg/metrics" agentLog "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/util/option" - yy "github.com/ghodss/yaml" "github.com/swaggest/jsonschema-go" "github.com/xeipuuv/gojsonschema" "go.yaml.in/yaml/v2" "golang.org/x/sys/windows/registry" + yy "sigs.k8s.io/yaml" ) const ( diff --git a/comp/core/agenttelemetry/def/go.mod b/comp/core/agenttelemetry/def/go.mod index f276b32cd4e0..a594d12ae943 100644 --- a/comp/core/agenttelemetry/def/go.mod +++ b/comp/core/agenttelemetry/def/go.mod @@ -18,7 +18,7 @@ require ( github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/comp/core/agenttelemetry/def/go.sum b/comp/core/agenttelemetry/def/go.sum index da3c602c679f..1480b81b2f9c 100644 --- a/comp/core/agenttelemetry/def/go.sum +++ b/comp/core/agenttelemetry/def/go.sum @@ -1,5 +1,3 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -7,22 +5,14 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -31,8 +21,8 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -40,8 +30,3 @@ golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/agenttelemetry/fx/go.mod b/comp/core/agenttelemetry/fx/go.mod index 10c90d751133..122625dae3e1 100644 --- a/comp/core/agenttelemetry/fx/go.mod +++ b/comp/core/agenttelemetry/fx/go.mod @@ -8,8 +8,6 @@ require ( github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/api/api/def v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/config v0.78.3-rc.2 // indirect @@ -60,7 +58,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -76,20 +73,20 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -99,7 +96,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect diff --git a/comp/core/agenttelemetry/fx/go.sum b/comp/core/agenttelemetry/fx/go.sum index 4e05a997dc78..65e30a5f544c 100644 --- a/comp/core/agenttelemetry/fx/go.sum +++ b/comp/core/agenttelemetry/fx/go.sum @@ -12,8 +12,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -65,8 +63,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -80,7 +76,6 @@ github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4Ul github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -103,8 +98,8 @@ github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -126,8 +121,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= @@ -148,8 +144,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/agenttelemetry/impl/go.mod b/comp/core/agenttelemetry/impl/go.mod index fa74a42382c8..f0d8e3167cb8 100644 --- a/comp/core/agenttelemetry/impl/go.mod +++ b/comp/core/agenttelemetry/impl/go.mod @@ -23,13 +23,11 @@ require ( github.com/DataDog/zstd v1.5.7 github.com/prometheus/client_model v0.6.2 github.com/robfig/cron/v3 v3.0.1 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.yaml.in/yaml/v2 v2.4.4 golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -66,7 +64,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -84,11 +81,11 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -104,7 +101,7 @@ require ( go.uber.org/fx v1.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/core/agenttelemetry/impl/go.sum b/comp/core/agenttelemetry/impl/go.sum index 3583e277e948..720f4541d3a3 100644 --- a/comp/core/agenttelemetry/impl/go.sum +++ b/comp/core/agenttelemetry/impl/go.sum @@ -12,8 +12,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -69,8 +67,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -84,7 +80,6 @@ github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4Ul github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -107,8 +102,8 @@ github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -130,8 +125,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= @@ -152,8 +148,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/config/go.mod b/comp/core/config/go.mod index 006e0bfb9a4b..dbfb5426b7a4 100644 --- a/comp/core/config/go.mod +++ b/comp/core/config/go.mod @@ -13,12 +13,10 @@ require ( github.com/DataDog/datadog-agent/pkg/util/defaultpaths v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/winutil v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/fx v1.24.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/def v0.78.3-rc.2 // indirect @@ -44,7 +42,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -58,8 +55,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect @@ -73,7 +70,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/core/config/go.sum b/comp/core/config/go.sum index 2f53e6942598..7bca182bee8d 100644 --- a/comp/core/config/go.sum +++ b/comp/core/config/go.sum @@ -6,8 +6,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -51,12 +49,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -75,8 +70,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -98,8 +93,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -114,8 +110,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/configsync/go.mod b/comp/core/configsync/go.mod index b662e42c48b5..7e2bf996fd04 100644 --- a/comp/core/configsync/go.mod +++ b/comp/core/configsync/go.mod @@ -14,13 +14,11 @@ require ( github.com/DataDog/datadog-agent/pkg/config/mock v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/config/model v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 go.uber.org/fx v1.24.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -57,7 +55,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -72,7 +69,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -90,7 +86,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/core/configsync/go.sum b/comp/core/configsync/go.sum index c6e2160725d5..6022a0058651 100644 --- a/comp/core/configsync/go.sum +++ b/comp/core/configsync/go.sum @@ -10,8 +10,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -61,8 +59,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -74,7 +70,6 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -93,8 +88,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -116,8 +111,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -136,8 +132,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/delegatedauth/api/cloudauth/aws/go.mod b/comp/core/delegatedauth/api/cloudauth/aws/go.mod index f668fd77e76d..aa86f92b7168 100644 --- a/comp/core/delegatedauth/api/cloudauth/aws/go.mod +++ b/comp/core/delegatedauth/api/cloudauth/aws/go.mod @@ -9,7 +9,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 github.com/aws/aws-sdk-go-v2 v1.41.5 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) require ( @@ -37,7 +37,6 @@ require ( github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/aws/smithy-go v1.24.3 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -50,8 +49,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -61,13 +60,12 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.41.0 // indirect golang.org/x/time v0.15.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/core/delegatedauth/api/cloudauth/aws/go.sum b/comp/core/delegatedauth/api/cloudauth/aws/go.sum index fbca0fb74849..19fe24a8ad70 100644 --- a/comp/core/delegatedauth/api/cloudauth/aws/go.sum +++ b/comp/core/delegatedauth/api/cloudauth/aws/go.sum @@ -9,8 +9,6 @@ github.com/aws/smithy-go v1.24.3 h1:XgOAaUgx+HhVBoP4v8n6HCQoTRDhoMghKqw4LNHsDNg= github.com/aws/smithy-go v1.24.3/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -52,12 +50,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -72,8 +67,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -85,8 +80,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -103,8 +98,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/delegatedauth/go.mod b/comp/core/delegatedauth/go.mod index c19e21a5328b..adb6c998ec8e 100644 --- a/comp/core/delegatedauth/go.mod +++ b/comp/core/delegatedauth/go.mod @@ -14,7 +14,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 github.com/cenkalti/backoff/v5 v5.0.3 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) require ( @@ -43,7 +43,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect github.com/aws/smithy-go v1.24.3 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fatih/color v1.18.0 // indirect @@ -61,7 +60,6 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -77,13 +75,12 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.41.0 // indirect golang.org/x/time v0.15.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/core/delegatedauth/go.sum b/comp/core/delegatedauth/go.sum index b85ea29520aa..c6e971b0dd0e 100644 --- a/comp/core/delegatedauth/go.sum +++ b/comp/core/delegatedauth/go.sum @@ -12,8 +12,6 @@ github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F9 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -65,8 +63,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -89,8 +85,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -112,8 +108,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -131,8 +128,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/flare/types/go.mod b/comp/core/flare/types/go.mod index 9e83bf136b5f..32c07899c89b 100644 --- a/comp/core/flare/types/go.mod +++ b/comp/core/flare/types/go.mod @@ -8,12 +8,11 @@ require ( ) require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect go.uber.org/dig v1.19.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect ) diff --git a/comp/core/flare/types/go.sum b/comp/core/flare/types/go.sum index 1e7c9b2a6c5c..6228232a9ca2 100644 --- a/comp/core/flare/types/go.sum +++ b/comp/core/flare/types/go.sum @@ -1,9 +1,5 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= @@ -14,9 +10,7 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/hostname/hostnameinterface/go.mod b/comp/core/hostname/hostnameinterface/go.mod index 3a6f82ff0541..87eb9adcf4b7 100644 --- a/comp/core/hostname/hostnameinterface/go.mod +++ b/comp/core/hostname/hostnameinterface/go.mod @@ -4,23 +4,20 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/fx v1.24.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/def v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/option v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.10 // indirect go.uber.org/dig v1.19.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect ) diff --git a/comp/core/hostname/hostnameinterface/go.sum b/comp/core/hostname/hostnameinterface/go.sum index 71ab2a7e5a00..5392314376b9 100644 --- a/comp/core/hostname/hostnameinterface/go.sum +++ b/comp/core/hostname/hostnameinterface/go.sum @@ -1,24 +1,14 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= @@ -29,12 +19,9 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/ipc/httphelpers/go.mod b/comp/core/ipc/httphelpers/go.mod index 07fd1f0de6b5..95eb532da783 100644 --- a/comp/core/ipc/httphelpers/go.mod +++ b/comp/core/ipc/httphelpers/go.mod @@ -10,11 +10,9 @@ require ( github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/system v0.78.3-rc.2 github.com/mdlayher/vsock v1.2.1 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -49,7 +47,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -62,8 +59,8 @@ require ( github.com/mdlayher/socket v0.5.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect @@ -78,7 +75,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/core/ipc/httphelpers/go.sum b/comp/core/ipc/httphelpers/go.sum index 962dc810d4db..1a0e2908fc13 100644 --- a/comp/core/ipc/httphelpers/go.sum +++ b/comp/core/ipc/httphelpers/go.sum @@ -6,8 +6,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -51,12 +49,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -75,8 +70,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -98,8 +93,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -116,8 +112,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/ipc/impl/go.mod b/comp/core/ipc/impl/go.mod index 7bba547f8c65..33762f03b41e 100644 --- a/comp/core/ipc/impl/go.mod +++ b/comp/core/ipc/impl/go.mod @@ -13,11 +13,9 @@ require ( github.com/DataDog/datadog-agent/pkg/config/model v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/flavor v0.78.3-rc.2 github.com/gofrs/flock v0.13.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -52,7 +50,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -65,7 +62,6 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -81,7 +77,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/core/ipc/impl/go.sum b/comp/core/ipc/impl/go.sum index 962dc810d4db..1a0e2908fc13 100644 --- a/comp/core/ipc/impl/go.sum +++ b/comp/core/ipc/impl/go.sum @@ -6,8 +6,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -51,12 +49,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -75,8 +70,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -98,8 +93,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -116,8 +112,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/ipc/mock/go.mod b/comp/core/ipc/mock/go.mod index c3a36986e507..fcf4c5482d5b 100644 --- a/comp/core/ipc/mock/go.mod +++ b/comp/core/ipc/mock/go.mod @@ -9,11 +9,9 @@ require ( github.com/DataDog/datadog-agent/pkg/api v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/config/mock v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/config/model v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -49,7 +47,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -63,7 +60,6 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -79,7 +75,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/core/ipc/mock/go.sum b/comp/core/ipc/mock/go.sum index 962dc810d4db..1a0e2908fc13 100644 --- a/comp/core/ipc/mock/go.sum +++ b/comp/core/ipc/mock/go.sum @@ -6,8 +6,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -51,12 +49,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -75,8 +70,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -98,8 +93,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -116,8 +112,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/log/def/go.mod b/comp/core/log/def/go.mod index fdc19825f099..9e7be0c0c8bd 100644 --- a/comp/core/log/def/go.mod +++ b/comp/core/log/def/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/comp/core/log/def go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/core/log/def/go.sum b/comp/core/log/def/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/comp/core/log/def/go.sum +++ b/comp/core/log/def/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/comp/core/log/fx/go.mod b/comp/core/log/fx/go.mod index 6b4a89419ac2..45e15592fdb8 100644 --- a/comp/core/log/fx/go.mod +++ b/comp/core/log/fx/go.mod @@ -7,8 +7,6 @@ require ( github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/config v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect @@ -46,7 +44,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -60,14 +57,14 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -77,7 +74,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/core/log/fx/go.sum b/comp/core/log/fx/go.sum index 2f53e6942598..7bca182bee8d 100644 --- a/comp/core/log/fx/go.sum +++ b/comp/core/log/fx/go.sum @@ -6,8 +6,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -51,12 +49,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -75,8 +70,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -98,8 +93,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -114,8 +110,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/log/impl-trace/go.mod b/comp/core/log/impl-trace/go.mod index 3d6293573eb0..3b613ba8cbb3 100644 --- a/comp/core/log/impl-trace/go.mod +++ b/comp/core/log/impl-trace/go.mod @@ -8,7 +8,7 @@ require ( github.com/DataDog/datadog-agent/pkg/config/env v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/fx v1.24.0 // indirect ) @@ -19,8 +19,6 @@ require ( github.com/DataDog/datadog-agent/pkg/util/log/setup v0.78.3-rc.2 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -51,7 +49,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -65,8 +62,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect @@ -80,7 +77,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/core/log/impl-trace/go.sum b/comp/core/log/impl-trace/go.sum index 2f53e6942598..7bca182bee8d 100644 --- a/comp/core/log/impl-trace/go.sum +++ b/comp/core/log/impl-trace/go.sum @@ -6,8 +6,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -51,12 +49,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -75,8 +70,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -98,8 +93,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -114,8 +110,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/log/impl/go.mod b/comp/core/log/impl/go.mod index 2cb1a1a543a4..89784ac5ca27 100644 --- a/comp/core/log/impl/go.mod +++ b/comp/core/log/impl/go.mod @@ -9,11 +9,9 @@ require ( github.com/DataDog/datadog-agent/pkg/config/mock v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log/setup v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -46,7 +44,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -60,8 +57,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect @@ -76,7 +73,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/core/log/impl/go.sum b/comp/core/log/impl/go.sum index 2f53e6942598..7bca182bee8d 100644 --- a/comp/core/log/impl/go.sum +++ b/comp/core/log/impl/go.sum @@ -6,8 +6,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -51,12 +49,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -75,8 +70,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -98,8 +93,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -114,8 +110,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/log/mock/go.mod b/comp/core/log/mock/go.mod index 026cd8a03c8a..245f1ba0e665 100644 --- a/comp/core/log/mock/go.mod +++ b/comp/core/log/mock/go.mod @@ -12,7 +12,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/comp/core/log/mock/go.sum b/comp/core/log/mock/go.sum index f75dbfe49ae9..befeb0397c22 100644 --- a/comp/core/log/mock/go.sum +++ b/comp/core/log/mock/go.sum @@ -1,23 +1,8 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/secrets/fx/go.mod b/comp/core/secrets/fx/go.mod index 5f8eb7e40cdf..b39ce206366d 100644 --- a/comp/core/secrets/fx/go.mod +++ b/comp/core/secrets/fx/go.mod @@ -7,8 +7,6 @@ require ( github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/api/api/def v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect @@ -48,7 +46,6 @@ require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fatih/color v1.18.0 // indirect @@ -70,7 +67,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -81,7 +77,7 @@ require ( github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -91,7 +87,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect diff --git a/comp/core/secrets/fx/go.sum b/comp/core/secrets/fx/go.sum index 473354aa20a1..218180ef490c 100644 --- a/comp/core/secrets/fx/go.sum +++ b/comp/core/secrets/fx/go.sum @@ -12,8 +12,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -76,8 +74,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -109,8 +105,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -132,8 +128,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= @@ -155,8 +152,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/secrets/impl/go.mod b/comp/core/secrets/impl/go.mod index 9d8ecb383e95..1be3a56a1675 100644 --- a/comp/core/secrets/impl/go.mod +++ b/comp/core/secrets/impl/go.mod @@ -19,14 +19,12 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.78.3-rc.2 github.com/benbjohnson/clock v1.3.5 github.com/json-iterator/go v1.1.12 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.yaml.in/yaml/v2 v2.4.4 golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 golang.org/x/sys v0.47.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -53,7 +51,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fatih/color v1.18.0 // indirect @@ -74,7 +71,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -93,7 +89,7 @@ require ( go.uber.org/fx v1.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/text v0.41.0 // indirect diff --git a/comp/core/secrets/impl/go.sum b/comp/core/secrets/impl/go.sum index fdeb9d4e05dd..5d090b50ee58 100644 --- a/comp/core/secrets/impl/go.sum +++ b/comp/core/secrets/impl/go.sum @@ -12,8 +12,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -80,8 +78,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -113,8 +109,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -136,8 +132,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= @@ -159,8 +156,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/secrets/mock/go.sum b/comp/core/secrets/mock/go.sum index 4cb5cccad8bc..00232dc65225 100644 --- a/comp/core/secrets/mock/go.sum +++ b/comp/core/secrets/mock/go.sum @@ -1,10 +1,6 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/comp/core/secrets/noop-impl/go.sum b/comp/core/secrets/noop-impl/go.sum index 42c6a6137716..3efeb3ab42de 100644 --- a/comp/core/secrets/noop-impl/go.sum +++ b/comp/core/secrets/noop-impl/go.sum @@ -1,5 +1,3 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= @@ -16,14 +14,12 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= @@ -34,12 +30,10 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/secrets/utils/go.mod b/comp/core/secrets/utils/go.mod index 69744be85819..933d0abdd286 100644 --- a/comp/core/secrets/utils/go.mod +++ b/comp/core/secrets/utils/go.mod @@ -3,19 +3,11 @@ module github.com/DataDog/datadog-agent/comp/core/secrets/utils go 1.25.0 require ( - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.yaml.in/yaml/v2 v2.4.4 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/core/secrets/utils/go.sum b/comp/core/secrets/utils/go.sum index d50fba8e7357..00232dc65225 100644 --- a/comp/core/secrets/utils/go.sum +++ b/comp/core/secrets/utils/go.sum @@ -1,25 +1,6 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/comp/core/status/go.mod b/comp/core/status/go.mod index 7e3447df217d..73d25dbdc782 100644 --- a/comp/core/status/go.mod +++ b/comp/core/status/go.mod @@ -7,25 +7,21 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/fatih/color v1.18.0 github.com/spf13/cast v1.10.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/fx v1.24.0 golang.org/x/text v0.41.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect go.uber.org/dig v1.19.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/core/status/go.sum b/comp/core/status/go.sum index e15a32ee4a47..3efeb3ab42de 100644 --- a/comp/core/status/go.sum +++ b/comp/core/status/go.sum @@ -1,5 +1,3 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= @@ -8,25 +6,20 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= @@ -37,15 +30,10 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/status/statusimpl/go.mod b/comp/core/status/statusimpl/go.mod index 49bba23c9069..73dba0c721ad 100644 --- a/comp/core/status/statusimpl/go.mod +++ b/comp/core/status/statusimpl/go.mod @@ -17,13 +17,11 @@ require ( github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 github.com/gorilla/mux v1.8.1 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/fx v1.24.0 golang.org/x/text v0.41.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -51,7 +49,6 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fatih/color v1.18.0 // indirect @@ -69,7 +66,6 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -84,7 +80,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/core/status/statusimpl/go.sum b/comp/core/status/statusimpl/go.sum index 32a2b7e40b8a..91e354f6486e 100644 --- a/comp/core/status/statusimpl/go.sum +++ b/comp/core/status/statusimpl/go.sum @@ -6,8 +6,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -61,8 +59,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -85,8 +81,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -108,8 +104,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -127,8 +124,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/tagger/def/go.mod b/comp/core/tagger/def/go.mod index 5574171811ba..39af85603ac3 100644 --- a/comp/core/tagger/def/go.mod +++ b/comp/core/tagger/def/go.mod @@ -11,8 +11,6 @@ require ( github.com/DataDog/datadog-agent/pkg/tagset v0.78.3-rc.2 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -49,7 +47,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -63,14 +60,14 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/twmb/murmur3 v1.1.8 // indirect @@ -81,7 +78,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/core/tagger/def/go.sum b/comp/core/tagger/def/go.sum index 4406314260d8..9f0a9dd22205 100644 --- a/comp/core/tagger/def/go.sum +++ b/comp/core/tagger/def/go.sum @@ -6,8 +6,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -51,12 +49,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -75,8 +70,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -100,8 +95,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -116,8 +112,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/tagger/fx-remote/go.mod b/comp/core/tagger/fx-remote/go.mod index ba79b42f8e30..986afee56a24 100644 --- a/comp/core/tagger/fx-remote/go.mod +++ b/comp/core/tagger/fx-remote/go.mod @@ -9,8 +9,6 @@ require ( go.uber.org/fx v1.24.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( cloud.google.com/go/auth v0.18.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect @@ -71,7 +69,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect @@ -96,7 +93,6 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/philhofer/fwd v1.2.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -107,7 +103,7 @@ require ( github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/tinylib/msgp v1.6.3 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect @@ -123,7 +119,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect diff --git a/comp/core/tagger/fx-remote/go.sum b/comp/core/tagger/fx-remote/go.sum index faf48cc8eb4b..d6b8aa9d97fa 100644 --- a/comp/core/tagger/fx-remote/go.sum +++ b/comp/core/tagger/fx-remote/go.sum @@ -27,8 +27,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -120,8 +118,6 @@ github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -157,8 +153,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -204,8 +200,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -296,13 +293,9 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/comp/core/tagger/generic_store/go.mod b/comp/core/tagger/generic_store/go.mod index 594f4536495c..e7dac80fe9e2 100644 --- a/comp/core/tagger/generic_store/go.mod +++ b/comp/core/tagger/generic_store/go.mod @@ -4,16 +4,13 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/comp/core/tagger/types v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/tagger/utils v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/core/tagger/generic_store/go.sum b/comp/core/tagger/generic_store/go.sum index 2bdbe9cbda42..c2336837ec7d 100644 --- a/comp/core/tagger/generic_store/go.sum +++ b/comp/core/tagger/generic_store/go.sum @@ -1,17 +1,4 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/comp/core/tagger/impl-remote/go.mod b/comp/core/tagger/impl-remote/go.mod index d80eca8eb1b5..e3b9550f04d1 100644 --- a/comp/core/tagger/impl-remote/go.mod +++ b/comp/core/tagger/impl-remote/go.mod @@ -32,14 +32,11 @@ require ( github.com/cenkalti/backoff/v5 v5.0.3 github.com/google/uuid v1.6.0 github.com/mdlayher/vsock v1.2.1 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 google.golang.org/grpc v1.83.2 ) -require ( - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect require ( cloud.google.com/go/auth v0.18.2 // indirect @@ -82,7 +79,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect @@ -104,7 +100,6 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/philhofer/fwd v1.2.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -131,7 +126,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect diff --git a/comp/core/tagger/impl-remote/go.sum b/comp/core/tagger/impl-remote/go.sum index faf48cc8eb4b..d6b8aa9d97fa 100644 --- a/comp/core/tagger/impl-remote/go.sum +++ b/comp/core/tagger/impl-remote/go.sum @@ -27,8 +27,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -120,8 +118,6 @@ github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -157,8 +153,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -204,8 +200,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -296,13 +293,9 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/comp/core/tagger/origindetection/go.mod b/comp/core/tagger/origindetection/go.mod index 262309207955..dab5323e2e6b 100644 --- a/comp/core/tagger/origindetection/go.mod +++ b/comp/core/tagger/origindetection/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/comp/core/tagger/origindetection go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/core/tagger/origindetection/go.sum b/comp/core/tagger/origindetection/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/comp/core/tagger/origindetection/go.sum +++ b/comp/core/tagger/origindetection/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/comp/core/tagger/subscriber/go.mod b/comp/core/tagger/subscriber/go.mod index c9306cb7e285..0f7293eb1bd7 100644 --- a/comp/core/tagger/subscriber/go.mod +++ b/comp/core/tagger/subscriber/go.mod @@ -8,11 +8,9 @@ require ( github.com/DataDog/datadog-agent/comp/core/telemetry v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/tagger/utils v0.78.3-rc.2 // indirect @@ -23,10 +21,8 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect @@ -39,7 +35,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/time v0.15.0 // indirect google.golang.org/protobuf v1.36.11 // indirect diff --git a/comp/core/tagger/subscriber/go.sum b/comp/core/tagger/subscriber/go.sum index e169f1f29a78..b1cbe942da3d 100644 --- a/comp/core/tagger/subscriber/go.sum +++ b/comp/core/tagger/subscriber/go.sum @@ -3,24 +3,16 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r 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/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw= github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -29,16 +21,14 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= @@ -53,8 +43,9 @@ go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= @@ -62,7 +53,3 @@ golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/tagger/telemetry/go.sum b/comp/core/tagger/telemetry/go.sum index 14dc5f981d4c..f5356fb3e97b 100644 --- a/comp/core/tagger/telemetry/go.sum +++ b/comp/core/tagger/telemetry/go.sum @@ -2,14 +2,10 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -18,15 +14,15 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/tagger/types/go.mod b/comp/core/tagger/types/go.mod index a03ab5162973..4ed2a91a32b9 100644 --- a/comp/core/tagger/types/go.mod +++ b/comp/core/tagger/types/go.mod @@ -5,16 +5,10 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.78.3-rc.2 github.com/DataDog/datadog-agent/comp/core/tagger/utils v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/text v0.2.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/core/tagger/types/go.sum b/comp/core/tagger/types/go.sum index 89d5fa07d6c0..c2336837ec7d 100644 --- a/comp/core/tagger/types/go.sum +++ b/comp/core/tagger/types/go.sum @@ -1,18 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/comp/core/tagger/utils/go.mod b/comp/core/tagger/utils/go.mod index b0e6041aed6b..e0c2c71baa4e 100644 --- a/comp/core/tagger/utils/go.mod +++ b/comp/core/tagger/utils/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/comp/core/tagger/utils go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/core/tagger/utils/go.sum b/comp/core/tagger/utils/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/comp/core/tagger/utils/go.sum +++ b/comp/core/tagger/utils/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/comp/core/telemetry/go.mod b/comp/core/telemetry/go.mod index c370d4b260b4..6f5fbe2c7951 100644 --- a/comp/core/telemetry/go.mod +++ b/comp/core/telemetry/go.mod @@ -7,22 +7,18 @@ require ( github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_model v0.6.2 github.com/prometheus/common v0.67.5 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/fx v1.24.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/def v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/option v0.78.3-rc.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.18.7 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.10 // indirect @@ -30,6 +26,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/comp/core/telemetry/go.sum b/comp/core/telemetry/go.sum index 1bc8f6dd044d..8b60fb05877a 100644 --- a/comp/core/telemetry/go.sum +++ b/comp/core/telemetry/go.sum @@ -3,24 +3,16 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r 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/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw= github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -29,16 +21,14 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= @@ -51,14 +41,11 @@ go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/def/go.mod b/comp/def/go.mod index 555d417a2f90..c059a8802d2b 100644 --- a/comp/def/go.mod +++ b/comp/def/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/comp/def go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/def/go.sum b/comp/def/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/comp/def/go.sum +++ b/comp/def/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/comp/forwarder/defaultforwarder/go.mod b/comp/forwarder/defaultforwarder/go.mod index 631a8d80b602..94130df1c124 100644 --- a/comp/forwarder/defaultforwarder/go.mod +++ b/comp/forwarder/defaultforwarder/go.mod @@ -29,15 +29,13 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 github.com/benbjohnson/clock v1.3.5 github.com/hashicorp/go-multierror v1.1.1 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 go.uber.org/fx v1.24.0 golang.org/x/sync v0.22.0 google.golang.org/protobuf v1.36.11 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -66,7 +64,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fatih/color v1.18.0 // indirect @@ -87,7 +84,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -106,7 +102,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.41.0 // indirect diff --git a/comp/forwarder/defaultforwarder/go.sum b/comp/forwarder/defaultforwarder/go.sum index cfeabbfb5669..d702b8d601b7 100644 --- a/comp/forwarder/defaultforwarder/go.sum +++ b/comp/forwarder/defaultforwarder/go.sum @@ -12,8 +12,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -78,8 +76,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -112,8 +108,8 @@ github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -135,8 +131,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -156,8 +153,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/forwarder/orchestrator/orchestratorinterface/go.mod b/comp/forwarder/orchestrator/orchestratorinterface/go.mod index af2350f0dd99..182c330b48e9 100644 --- a/comp/forwarder/orchestrator/orchestratorinterface/go.mod +++ b/comp/forwarder/orchestrator/orchestratorinterface/go.mod @@ -4,8 +4,6 @@ go 1.25.0 require github.com/DataDog/datadog-agent/comp/forwarder/defaultforwarder v0.78.3-rc.2 -require gopkg.in/yaml.v3 v3.0.1 // indirect - // Internal deps fix version replace github.com/spf13/cast => github.com/DataDog/cast v1.8.0 @@ -59,7 +57,6 @@ require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fatih/color v1.18.0 // indirect @@ -81,7 +78,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -93,7 +89,7 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.3 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -103,7 +99,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/forwarder/orchestrator/orchestratorinterface/go.sum b/comp/forwarder/orchestrator/orchestratorinterface/go.sum index fd0202f48314..7d0437d2f13c 100644 --- a/comp/forwarder/orchestrator/orchestratorinterface/go.sum +++ b/comp/forwarder/orchestrator/orchestratorinterface/go.sum @@ -15,8 +15,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -83,8 +81,6 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -115,8 +111,8 @@ github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -138,8 +134,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -159,8 +156,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/host-profiler/collector/impl/agentprovider/provider_test.go b/comp/host-profiler/collector/impl/agentprovider/provider_test.go index 27fbed7da0b5..76705df6d618 100644 --- a/comp/host-profiler/collector/impl/agentprovider/provider_test.go +++ b/comp/host-profiler/collector/impl/agentprovider/provider_test.go @@ -35,7 +35,7 @@ import ( "go.opentelemetry.io/collector/otelcol/otelcoltest" "go.opentelemetry.io/collector/receiver/otlpreceiver" "go.opentelemetry.io/collector/service/telemetry/otelconftelemetry" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" ) var updateGolden = flag.Bool("update", false, "update golden test files") diff --git a/comp/logs-library/go.mod b/comp/logs-library/go.mod index 2ce2743ab195..1316fe61148f 100644 --- a/comp/logs-library/go.mod +++ b/comp/logs-library/go.mod @@ -8,20 +8,17 @@ require ( go.uber.org/fx v1.24.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/def v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/option v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect go.uber.org/dig v1.19.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect ) diff --git a/comp/logs-library/go.sum b/comp/logs-library/go.sum index 71ab2a7e5a00..5392314376b9 100644 --- a/comp/logs-library/go.sum +++ b/comp/logs-library/go.sum @@ -1,24 +1,14 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= @@ -29,12 +19,9 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/logs/agent/config/go.mod b/comp/logs/agent/config/go.mod index 5f66e6839547..0717a5ca0e55 100644 --- a/comp/logs/agent/config/go.mod +++ b/comp/logs/agent/config/go.mod @@ -12,13 +12,11 @@ require ( github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/pointer v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 - go.yaml.in/yaml/v3 v3.0.4 + go.yaml.in/yaml/v3 v3.0.5 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -48,7 +46,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -62,8 +59,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect diff --git a/comp/logs/agent/config/go.sum b/comp/logs/agent/config/go.sum index 962dc810d4db..1a0e2908fc13 100644 --- a/comp/logs/agent/config/go.sum +++ b/comp/logs/agent/config/go.sum @@ -6,8 +6,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -51,12 +49,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -75,8 +70,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -98,8 +93,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -116,8 +112,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/otelcol/collector-contrib/def/go.mod b/comp/otelcol/collector-contrib/def/go.mod index 6e83049b3beb..5e6950a1bcd8 100644 --- a/comp/otelcol/collector-contrib/def/go.mod +++ b/comp/otelcol/collector-contrib/def/go.mod @@ -41,13 +41,11 @@ require ( go.opentelemetry.io/collector/processor/xprocessor v0.150.0 // indirect go.opentelemetry.io/collector/receiver/xreceiver v0.150.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.21.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -68,7 +66,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -77,7 +74,7 @@ require ( github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -120,7 +117,7 @@ require ( go.opentelemetry.io/proto/otlp v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect golang.org/x/net v0.58.0 // indirect diff --git a/comp/otelcol/collector-contrib/def/go.sum b/comp/otelcol/collector-contrib/def/go.sum index 81c03bf8402f..ef2a9df63b18 100644 --- a/comp/otelcol/collector-contrib/def/go.sum +++ b/comp/otelcol/collector-contrib/def/go.sum @@ -7,8 +7,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -58,10 +56,6 @@ github.com/knadh/koanf/providers/confmap v1.0.0 h1:mHKLJTE7iXEys6deO5p6olAiZdG5z github.com/knadh/koanf/providers/confmap v1.0.0/go.mod h1:txHYHiI2hAtF0/0sCmcuol4IDcuQbKTybiB1nOcUo1A= github.com/knadh/koanf/v2 v2.3.4 h1:fnynNSDlujWE+v83hAp8wKr/cdoxHLO0629SN+U8Urc= github.com/knadh/koanf/v2 v2.3.4/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= @@ -81,8 +75,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY= github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -95,8 +87,6 @@ github.com/prometheus/otlptranslator v1.0.0 h1:s0LJW/iN9dkIH+EnhiD3BlkkP5QVIUVEo github.com/prometheus/otlptranslator v1.0.0/go.mod h1:vRYWnXvI6aWGpsdY/mOT/cbeVRBlPWtBNDb7kGR3uKM= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -109,8 +99,8 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -293,8 +283,9 @@ go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I= golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= @@ -319,7 +310,3 @@ google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsok google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/otelcol/collector-contrib/impl/go.mod b/comp/otelcol/collector-contrib/impl/go.mod index 14de7ed03cff..ef483ef7dcb2 100644 --- a/comp/otelcol/collector-contrib/impl/go.mod +++ b/comp/otelcol/collector-contrib/impl/go.mod @@ -405,7 +405,7 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/stackitcloud/stackit-sdk-go/core v0.23.0 // indirect github.com/stretchr/objx v0.5.3 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/tilinna/clock v1.1.0 // indirect github.com/tinylib/msgp v1.6.3 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect @@ -509,7 +509,7 @@ require ( go.uber.org/zap v1.28.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect go.yaml.in/yaml/v4 v4.0.0-rc.4 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect diff --git a/comp/otelcol/collector-contrib/impl/go.sum b/comp/otelcol/collector-contrib/impl/go.sum index f5b084fee7c3..2ad3108aebcf 100644 --- a/comp/otelcol/collector-contrib/impl/go.sum +++ b/comp/otelcol/collector-contrib/impl/go.sum @@ -875,8 +875,9 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -1175,8 +1176,9 @@ go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U= go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/comp/otelcol/converter/def/go.mod b/comp/otelcol/converter/def/go.mod index 63eca5d69c33..b3724787fbf8 100644 --- a/comp/otelcol/converter/def/go.mod +++ b/comp/otelcol/converter/def/go.mod @@ -7,7 +7,6 @@ require go.opentelemetry.io/collector/confmap v1.57.0 require github.com/gobwas/glob v0.2.3 // indirect require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/hashicorp/go-version v1.9.0 // indirect github.com/knadh/koanf/maps v0.1.2 // indirect @@ -15,12 +14,11 @@ require ( github.com/knadh/koanf/v2 v2.3.4 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect go.opentelemetry.io/collector/featuregate v1.57.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/otelcol/converter/def/go.sum b/comp/otelcol/converter/def/go.sum index 3555e7710944..00803d2ed8a6 100644 --- a/comp/otelcol/converter/def/go.sum +++ b/comp/otelcol/converter/def/go.sum @@ -1,5 +1,3 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -12,20 +10,12 @@ github.com/knadh/koanf/providers/confmap v1.0.0 h1:mHKLJTE7iXEys6deO5p6olAiZdG5z github.com/knadh/koanf/providers/confmap v1.0.0/go.mod h1:txHYHiI2hAtF0/0sCmcuol4IDcuQbKTybiB1nOcUo1A= github.com/knadh/koanf/v2 v2.3.4 h1:fnynNSDlujWE+v83hAp8wKr/cdoxHLO0629SN+U8Urc= github.com/knadh/koanf/v2 v2.3.4/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.opentelemetry.io/collector/confmap v1.57.0 h1:5AuK920dJmV8zxQAiODi2JHPl2r1HmEHHMaBSC+qF5I= go.opentelemetry.io/collector/confmap v1.57.0/go.mod h1:ifmog4kqEMM037qX04qEbom5CcxhmkadLUqhi2Vkuec= go.opentelemetry.io/collector/featuregate v1.57.0 h1:KPDSUKYn6MHwgyGRSGPPcW/G96HH93pxuvvPwM+R8nY= @@ -36,10 +26,5 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/comp/otelcol/converter/impl/go.mod b/comp/otelcol/converter/impl/go.mod index f893dcf56125..e1c795b7a645 100644 --- a/comp/otelcol/converter/impl/go.mod +++ b/comp/otelcol/converter/impl/go.mod @@ -6,7 +6,7 @@ require ( github.com/DataDog/datadog-agent/comp/core/config v0.78.3-rc.2 github.com/DataDog/datadog-agent/comp/core/hostname/hostnameinterface v0.78.3-rc.2 github.com/DataDog/datadog-agent/comp/otelcol/converter/def v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/collector/confmap v1.57.0 go.opentelemetry.io/collector/confmap/provider/envprovider v1.56.0 go.opentelemetry.io/collector/confmap/provider/fileprovider v1.56.0 @@ -16,10 +16,7 @@ require ( go.uber.org/zap v1.28.0 ) -require ( - github.com/gobwas/glob v0.2.3 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require github.com/gobwas/glob v0.2.3 // indirect require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect @@ -56,7 +53,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -76,8 +72,8 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect @@ -92,7 +88,7 @@ require ( go.uber.org/fx v1.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/otelcol/converter/impl/go.sum b/comp/otelcol/converter/impl/go.sum index ec44a0af2d96..a7df6639fcd0 100644 --- a/comp/otelcol/converter/impl/go.sum +++ b/comp/otelcol/converter/impl/go.sum @@ -6,8 +6,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -65,12 +63,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -89,8 +84,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -128,8 +123,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -144,8 +140,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/otelcol/ddflareextension/def/go.mod b/comp/otelcol/ddflareextension/def/go.mod index b47e5d36d995..9950684da986 100644 --- a/comp/otelcol/ddflareextension/def/go.mod +++ b/comp/otelcol/ddflareextension/def/go.mod @@ -10,12 +10,11 @@ require ( ) require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/hashicorp/go-version v1.9.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/stretchr/testify v1.12.1 // indirect go.opentelemetry.io/collector/component v1.57.0 // indirect go.opentelemetry.io/collector/featuregate v1.57.0 // indirect go.opentelemetry.io/collector/pdata v1.57.0 // indirect @@ -24,6 +23,7 @@ require ( go.opentelemetry.io/otel/trace v1.45.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/otelcol/ddflareextension/def/go.sum b/comp/otelcol/ddflareextension/def/go.sum index b35bd65aa32e..2a200d62acab 100644 --- a/comp/otelcol/ddflareextension/def/go.sum +++ b/comp/otelcol/ddflareextension/def/go.sum @@ -2,8 +2,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -22,12 +20,10 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/collector/component v1.57.0 h1:WKIqx2Bs0JaAZxDEhsLradXpYxnwAxVFzWhQUmu2q3w= @@ -60,9 +56,7 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/otelcol/ddflareextension/impl/go.mod b/comp/otelcol/ddflareextension/impl/go.mod index b6da740b839d..e2672940cec1 100644 --- a/comp/otelcol/ddflareextension/impl/go.mod +++ b/comp/otelcol/ddflareextension/impl/go.mod @@ -20,7 +20,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.150.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor v0.150.0 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.150.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/collector/component v1.57.0 go.opentelemetry.io/collector/component/componentstatus v0.150.0 go.opentelemetry.io/collector/component/componenttest v0.150.0 @@ -507,7 +507,7 @@ require ( go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect golang.org/x/mod v0.40.0 // indirect diff --git a/comp/otelcol/ddflareextension/impl/go.sum b/comp/otelcol/ddflareextension/impl/go.sum index b0c5d78e1c5f..9cd49e94cb9a 100644 --- a/comp/otelcol/ddflareextension/impl/go.sum +++ b/comp/otelcol/ddflareextension/impl/go.sum @@ -778,8 +778,9 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -1060,8 +1061,9 @@ go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U= go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/comp/otelcol/ddprofilingextension/def/go.mod b/comp/otelcol/ddprofilingextension/def/go.mod index be80a9145a96..b389c3d3be0a 100644 --- a/comp/otelcol/ddprofilingextension/def/go.mod +++ b/comp/otelcol/ddprofilingextension/def/go.mod @@ -10,12 +10,11 @@ require ( ) require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/hashicorp/go-version v1.9.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/stretchr/testify v1.12.1 // indirect go.opentelemetry.io/collector/component v1.57.0 // indirect go.opentelemetry.io/collector/featuregate v1.57.0 // indirect go.opentelemetry.io/collector/pdata v1.57.0 // indirect @@ -24,6 +23,7 @@ require ( go.opentelemetry.io/otel/trace v1.45.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/comp/otelcol/ddprofilingextension/def/go.sum b/comp/otelcol/ddprofilingextension/def/go.sum index b35bd65aa32e..2a200d62acab 100644 --- a/comp/otelcol/ddprofilingextension/def/go.sum +++ b/comp/otelcol/ddprofilingextension/def/go.sum @@ -2,8 +2,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -22,12 +20,10 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/collector/component v1.57.0 h1:WKIqx2Bs0JaAZxDEhsLradXpYxnwAxVFzWhQUmu2q3w= @@ -60,9 +56,7 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/otelcol/ddprofilingextension/impl/go.mod b/comp/otelcol/ddprofilingextension/impl/go.mod index 6a5d173c29c0..5f0e90f77c41 100644 --- a/comp/otelcol/ddprofilingextension/impl/go.mod +++ b/comp/otelcol/ddprofilingextension/impl/go.mod @@ -13,7 +13,7 @@ require ( github.com/DataDog/datadog-agent/pkg/trace v0.78.3-rc.2 github.com/DataDog/datadog-go/v5 v5.8.3 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog v0.150.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/collector/component v1.57.0 go.opentelemetry.io/collector/component/componentstatus v0.150.0 go.opentelemetry.io/collector/component/componenttest v0.150.0 @@ -38,7 +38,6 @@ require ( github.com/go-openapi/swag/yamlutils v0.25.5 // indirect go.opentelemetry.io/collector/internal/componentalias v0.151.0 // indirect go.opentelemetry.io/collector/pipeline/xpipeline v0.150.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( @@ -250,7 +249,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/mod v0.40.0 // indirect golang.org/x/net v0.58.0 // indirect diff --git a/comp/otelcol/ddprofilingextension/impl/go.sum b/comp/otelcol/ddprofilingextension/impl/go.sum index be11844bb6a5..3d75b7c2e765 100644 --- a/comp/otelcol/ddprofilingextension/impl/go.sum +++ b/comp/otelcol/ddprofilingextension/impl/go.sum @@ -348,8 +348,9 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= @@ -503,8 +504,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -600,7 +602,6 @@ gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= k8s.io/api v0.35.3 h1:pA2fiBc6+N9PDf7SAiluKGEBuScsTzd2uYBkA5RzNWQ= diff --git a/comp/otelcol/logsagentpipeline/go.mod b/comp/otelcol/logsagentpipeline/go.mod index 8eb1d9630085..5fc775ea9597 100644 --- a/comp/otelcol/logsagentpipeline/go.mod +++ b/comp/otelcol/logsagentpipeline/go.mod @@ -4,8 +4,6 @@ go 1.25.0 require github.com/DataDog/datadog-agent/pkg/logs/pipeline v0.78.3-rc.2 -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/agent-payload/v5 v5.0.184 // indirect github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect @@ -60,7 +58,6 @@ require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -76,7 +73,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -87,7 +83,7 @@ require ( github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -97,7 +93,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/otelcol/logsagentpipeline/go.sum b/comp/otelcol/logsagentpipeline/go.sum index b30cc7ba6fed..195e8adf7c87 100644 --- a/comp/otelcol/logsagentpipeline/go.sum +++ b/comp/otelcol/logsagentpipeline/go.sum @@ -16,8 +16,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -71,8 +69,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -84,7 +80,6 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -103,8 +98,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -128,8 +123,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -173,8 +169,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod index 9e146968551a..a127bee34843 100644 --- a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod +++ b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod @@ -24,13 +24,11 @@ require ( github.com/DataDog/datadog-agent/pkg/util/option v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/startstop v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/testutil v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/fx v1.24.0 go.uber.org/zap v1.28.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/agent-payload/v5 v5.0.184 // indirect github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect @@ -77,7 +75,6 @@ require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -93,12 +90,12 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect @@ -111,7 +108,7 @@ require ( go.uber.org/dig v1.19.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum index b30cc7ba6fed..195e8adf7c87 100644 --- a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum +++ b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum @@ -16,8 +16,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -71,8 +69,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -84,7 +80,6 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -103,8 +98,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -128,8 +123,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -173,8 +169,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod b/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod index bb10c093ca4c..429a80e981ea 100644 --- a/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod @@ -23,7 +23,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/otel v0.78.3-rc.2 github.com/DataDog/datadog-go/v5 v5.8.3 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog v0.150.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/collector/component v1.57.0 go.opentelemetry.io/collector/config/confignet v1.56.0 go.opentelemetry.io/collector/config/configoptional v1.56.0 @@ -46,7 +46,6 @@ require ( go.opentelemetry.io/collector/internal/componentalias v0.151.0 // indirect go.opentelemetry.io/collector/pipeline/xpipeline v0.150.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( @@ -199,7 +198,6 @@ require ( github.com/pierrec/lz4/v4 v4.1.26 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -252,7 +250,7 @@ require ( go.uber.org/dig v1.19.0 // indirect go.uber.org/fx v1.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect diff --git a/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum b/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum index bbe9da875a52..76581097cbd4 100644 --- a/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum @@ -190,8 +190,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -243,8 +241,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -393,8 +392,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -477,7 +477,6 @@ gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8= diff --git a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod index 5f2c16d50c5d..cbb1718a1f00 100644 --- a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod @@ -19,7 +19,7 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog v0.150.0 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/collector/component v1.57.0 go.opentelemetry.io/collector/config/configoptional v1.56.0 go.opentelemetry.io/collector/config/configretry v1.56.0 @@ -36,7 +36,6 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect go.opentelemetry.io/collector/internal/componentalias v0.151.0 // indirect go.opentelemetry.io/collector/pipeline/xpipeline v0.150.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( @@ -90,7 +89,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -129,7 +127,6 @@ require ( github.com/philhofer/fwd v1.2.0 // indirect github.com/pierrec/lz4/v4 v4.1.26 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -185,7 +182,7 @@ require ( go.uber.org/fx v1.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect diff --git a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum index de3690df35b6..2312597e31f3 100644 --- a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum @@ -24,8 +24,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -143,8 +141,6 @@ github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcR github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -178,8 +174,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -315,8 +311,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -372,8 +369,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod b/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod index fad556708aba..a43105eb8f11 100644 --- a/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod @@ -29,7 +29,7 @@ require ( github.com/google/go-cmp v0.7.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog v0.150.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.150.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/tinylib/msgp v1.6.3 go.opentelemetry.io/collector/component v1.57.0 go.opentelemetry.io/collector/component/componenttest v0.150.0 @@ -65,7 +65,6 @@ require ( go.opentelemetry.io/collector/internal/componentalias v0.151.0 // indirect go.opentelemetry.io/collector/pipeline/xpipeline v0.150.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( @@ -122,7 +121,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fatih/color v1.18.0 // indirect @@ -164,7 +162,6 @@ require ( github.com/philhofer/fwd v1.2.0 // indirect github.com/pierrec/lz4/v4 v4.1.26 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -212,7 +209,7 @@ require ( go.opentelemetry.io/otel/trace v1.45.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/dig v1.19.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum b/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum index 1f9551e4cdce..1943ad8e812f 100644 --- a/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum @@ -24,8 +24,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -162,8 +160,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -204,8 +200,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -341,8 +337,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -399,9 +396,5 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= diff --git a/comp/otelcol/otlp/components/metricsclient/go.mod b/comp/otelcol/otlp/components/metricsclient/go.mod index eff8dfd0c551..260cb97d37d8 100644 --- a/comp/otelcol/otlp/components/metricsclient/go.mod +++ b/comp/otelcol/otlp/components/metricsclient/go.mod @@ -5,27 +5,23 @@ go 1.26.0 require ( github.com/DataDog/datadog-agent/pkg/trace v0.78.3-rc.2 github.com/DataDog/datadog-go/v5 v5.8.3 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/otel v1.45.0 go.opentelemetry.io/otel/metric v1.45.0 go.opentelemetry.io/otel/sdk/metric v1.45.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/stretchr/objx v0.5.3 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/otel/sdk v1.45.0 // indirect go.opentelemetry.io/otel/trace v1.45.0 // indirect go.uber.org/atomic v1.11.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect ) diff --git a/comp/otelcol/otlp/components/metricsclient/go.sum b/comp/otelcol/otlp/components/metricsclient/go.sum index 290f73270b24..c09c39cb6f88 100644 --- a/comp/otelcol/otlp/components/metricsclient/go.sum +++ b/comp/otelcol/otlp/components/metricsclient/go.sum @@ -7,8 +7,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -19,16 +17,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -39,8 +29,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= @@ -58,6 +48,8 @@ go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOy go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -87,8 +79,5 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/go.mod b/comp/otelcol/otlp/components/processor/infraattributesprocessor/go.mod index 08ae7d6b133b..ee3b60bf84c8 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/go.mod +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/go.mod @@ -16,7 +16,7 @@ require ( github.com/DataDog/datadog-agent/pkg/config/setup v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/option v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/collector/component v1.57.0 go.opentelemetry.io/collector/component/componenttest v0.150.0 go.opentelemetry.io/collector/confmap v1.57.0 @@ -41,7 +41,6 @@ require ( require ( go.opentelemetry.io/collector/internal/componentalias v0.151.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( @@ -104,7 +103,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect @@ -140,7 +138,6 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -167,7 +164,7 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/dig v1.19.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/go.sum b/comp/otelcol/otlp/components/processor/infraattributesprocessor/go.sum index 7a4a21220f6f..600a86f70048 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/go.sum +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/go.sum @@ -29,8 +29,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -149,8 +147,6 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -186,8 +182,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -285,8 +281,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -379,13 +376,9 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/comp/otelcol/otlp/testutil/go.mod b/comp/otelcol/otlp/testutil/go.mod index 4774ebe84987..eed2aad462e9 100644 --- a/comp/otelcol/otlp/testutil/go.mod +++ b/comp/otelcol/otlp/testutil/go.mod @@ -12,13 +12,11 @@ require ( github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/proto v0.78.3-rc.2 github.com/DataDog/sketches-go v1.4.8 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/collector/pdata v1.57.0 google.golang.org/protobuf v1.36.11 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -45,7 +43,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -64,7 +61,6 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -78,7 +74,7 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/otelcol/otlp/testutil/go.sum b/comp/otelcol/otlp/testutil/go.sum index 43b03c421cc5..f7bfef8ababf 100644 --- a/comp/otelcol/otlp/testutil/go.sum +++ b/comp/otelcol/otlp/testutil/go.sum @@ -7,8 +7,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -69,8 +67,6 @@ github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -90,8 +86,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -125,8 +121,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -145,8 +141,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/otelcol/status/impl/go.mod b/comp/otelcol/status/impl/go.mod index 816a110566f8..57be0501e25e 100644 --- a/comp/otelcol/status/impl/go.mod +++ b/comp/otelcol/status/impl/go.mod @@ -11,12 +11,10 @@ require ( github.com/DataDog/datadog-agent/comp/otelcol/ddflareextension/types v0.78.3-rc.2 github.com/DataDog/datadog-agent/comp/otelcol/status/def v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/prometheus v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 - go.yaml.in/yaml/v3 v3.0.4 + github.com/stretchr/testify v1.12.1 + go.yaml.in/yaml/v3 v3.0.5 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -62,7 +60,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fatih/color v1.18.0 // indirect @@ -82,7 +79,6 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect diff --git a/comp/otelcol/status/impl/go.sum b/comp/otelcol/status/impl/go.sum index 374682ab1796..c58080c89eb0 100644 --- a/comp/otelcol/status/impl/go.sum +++ b/comp/otelcol/status/impl/go.sum @@ -54,8 +54,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -141,8 +139,6 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -181,8 +177,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -216,8 +212,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -272,11 +269,7 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8= k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= k8s.io/client-go v0.35.3 h1:s1lZbpN4uI6IxeTM2cpdtrwHcSOBML1ODNTCCfsP1pg= diff --git a/comp/serializer/logscompression/go.mod b/comp/serializer/logscompression/go.mod index c4451db265ce..895299e550ec 100644 --- a/comp/serializer/logscompression/go.mod +++ b/comp/serializer/logscompression/go.mod @@ -7,8 +7,6 @@ require ( github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/config v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect @@ -45,7 +43,6 @@ require ( github.com/DataDog/viper v1.15.1 // indirect github.com/DataDog/zstd v1.5.7 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -59,14 +56,13 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -76,7 +72,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/serializer/logscompression/go.sum b/comp/serializer/logscompression/go.sum index f308d108da3e..89d87d840dd0 100644 --- a/comp/serializer/logscompression/go.sum +++ b/comp/serializer/logscompression/go.sum @@ -8,8 +8,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -55,12 +53,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -79,8 +74,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -102,8 +97,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -118,8 +114,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/serializer/metricscompression/go.mod b/comp/serializer/metricscompression/go.mod index 1c47ca312702..0ac1cbd6091c 100644 --- a/comp/serializer/metricscompression/go.mod +++ b/comp/serializer/metricscompression/go.mod @@ -8,8 +8,6 @@ require ( github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -45,7 +43,6 @@ require ( github.com/DataDog/viper v1.15.1 // indirect github.com/DataDog/zstd v1.5.7 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -59,14 +56,13 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -76,7 +72,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/comp/serializer/metricscompression/go.sum b/comp/serializer/metricscompression/go.sum index f308d108da3e..89d87d840dd0 100644 --- a/comp/serializer/metricscompression/go.sum +++ b/comp/serializer/metricscompression/go.sum @@ -8,8 +8,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -55,12 +53,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -79,8 +74,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -102,8 +97,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -118,8 +114,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/trace/agent/def/go.sum b/comp/trace/agent/def/go.sum index a2dfeaf6fb3f..317bec3f929a 100644 --- a/comp/trace/agent/def/go.sum +++ b/comp/trace/agent/def/go.sum @@ -2,8 +2,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -32,12 +30,10 @@ github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/vmihailenco/msgpack/v4 v4.3.13 h1:A2wsiTbvp63ilDaWmsk2wjx6xZdxQOvpiNlKBGKKXKI= @@ -80,13 +76,11 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/docs/public/components/shared_features/flares.md b/docs/public/components/shared_features/flares.md index 7eabf3d3dc27..2e4a73be6bf7 100644 --- a/docs/public/components/shared_features/flares.md +++ b/docs/public/components/shared_features/flares.md @@ -22,7 +22,7 @@ Example: ```go import ( - yaml "gopkg.in/yaml.v2" + yaml "go.yaml.in/yaml/v2" flare "github.com/DataDog/datadog-agent/comp/core/flare/def" ) diff --git a/go.mod b/go.mod index c520a8a978f6..af1e2c01aa30 100644 --- a/go.mod +++ b/go.mod @@ -225,7 +225,6 @@ require ( github.com/fatih/color v1.18.0 github.com/fatih/structtag v1.2.0 github.com/freddierice/go-losetup v0.0.0-20220711213114-2a14873012db - github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/glaslos/ssdeep v0.4.0 github.com/go-delve/delve v1.26.0 github.com/go-ini/ini v1.67.0 @@ -319,7 +318,7 @@ require ( github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/streadway/amqp v1.1.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/swaggest/jsonschema-go v0.3.70 github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 github.com/tinylib/msgp v1.6.3 @@ -381,7 +380,7 @@ require ( go.uber.org/multierr v1.11.0 go.uber.org/zap v1.28.0 go.yaml.in/yaml/v2 v2.4.4 - go.yaml.in/yaml/v3 v3.0.4 + go.yaml.in/yaml/v3 v3.0.5 go4.org/intern v0.0.0-20230525184215-6c62f75575cb go4.org/mem v0.0.0-20220726221520-4f986261bf13 go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d @@ -993,8 +992,6 @@ require ( go.opentelemetry.io/collector/otelcol/otelcoltest v0.150.0 go.temporal.io/api v1.62.2 go.temporal.io/sdk v1.39.0 - gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.1 gotest.tools v2.2.0+incompatible mvdan.cc/sh/v3 v3.13.0 ) @@ -1212,6 +1209,8 @@ require ( go.yaml.in/yaml/v4 v4.0.0-rc.4 // indirect gopkg.in/neurosnap/sentences.v1 v1.0.7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) // github.com/aws/karpenter-provider-aws requires alpha versions of K8s libraries. We are only using some constants from these packages. diff --git a/go.sum b/go.sum index 43960950c75e..7c5e4ccfccb7 100644 --- a/go.sum +++ b/go.sum @@ -3308,8 +3308,6 @@ github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZ github.com/gammazero/workerpool v1.1.3 h1:WixN4xzukFoN0XSeXF6puqEqFTl2mECI9S6W44HWy9Q= github.com/gammazero/workerpool v1.1.3/go.mod h1:wPjyBLDbyKnUn2XwwyD3EEwo9dHutia9/fwNmSHWACc= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= -github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= github.com/glaslos/ssdeep v0.4.0 h1:w9PtY1HpXbWLYgrL/rvAVkj2ZAMOtDxoGKcBHcUFCLs= @@ -5031,8 +5029,9 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/substrait-io/substrait-go v0.4.2/go.mod h1:qhpnLmrcvAnlZsUyPXZRqldiHapPTXC3t7xFgDi3aQg= @@ -5598,8 +5597,9 @@ go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U= go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= go4.org/intern v0.0.0-20230525184215-6c62f75575cb h1:ae7kzL5Cfdmcecbh22ll7lYP3iuUdnfnhiPcSaDgH/8= diff --git a/internal/tools/go.mod b/internal/tools/go.mod index 034617e85ef2..dd6659a07e6c 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -84,7 +84,6 @@ require ( github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/daixiang0/gci v0.13.7 // indirect github.com/dave/dst v0.27.3 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect github.com/dgryski/go-minhash v0.0.0-20190315135803-ad340ca03076 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect @@ -206,7 +205,6 @@ require ( github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect @@ -245,7 +243,7 @@ require ( github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.3.1 // indirect github.com/stretchr/objx v0.5.3 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tetafro/godot v1.5.4 // indirect github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect @@ -273,7 +271,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect golang.org/x/exp/typeparams v0.0.0-20251125195548-87e1e737ad39 // indirect diff --git a/internal/tools/go.sum b/internal/tools/go.sum index b479fc14863b..4e4a649a42cd 100644 --- a/internal/tools/go.sum +++ b/internal/tools/go.sum @@ -143,8 +143,6 @@ github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo= github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc h1:8WFBn63wegobsYAX0YjD+8suexZDga5CctH4CCTx2+8= @@ -459,8 +457,6 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -551,8 +547,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= @@ -628,8 +624,9 @@ go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= diff --git a/internal/tools/modparser/go.mod b/internal/tools/modparser/go.mod index 0db841f18a7d..135d32f5f2db 100644 --- a/internal/tools/modparser/go.mod +++ b/internal/tools/modparser/go.mod @@ -3,18 +3,10 @@ module github.com/DataDog/datadog-agent/internal/tools/modparser go 1.26.0 require ( - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 golang.org/x/mod v0.40.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect replace github.com/go-openapi/testify/v2 => github.com/go-openapi/testify/v2 v2.4.1 diff --git a/internal/tools/modparser/go.sum b/internal/tools/modparser/go.sum index 49243127bae4..6edb6c38165a 100644 --- a/internal/tools/modparser/go.sum +++ b/internal/tools/modparser/go.sum @@ -1,25 +1,6 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs= golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/tools/worksynchronizer/go.mod b/internal/tools/worksynchronizer/go.mod index d3cae5a28964..6eead5dadc44 100644 --- a/internal/tools/worksynchronizer/go.mod +++ b/internal/tools/worksynchronizer/go.mod @@ -3,18 +3,9 @@ module github.com/DataDog/datadog-agent/internal/tools/worksynchronizer go 1.26.0 require ( - github.com/stretchr/testify v1.11.1 - go.yaml.in/yaml/v3 v3.0.4 + github.com/stretchr/testify v1.12.1 + go.yaml.in/yaml/v3 v3.0.5 golang.org/x/mod v0.40.0 ) -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) - replace github.com/go-openapi/testify/v2 => github.com/go-openapi/testify/v2 v2.4.1 diff --git a/internal/tools/worksynchronizer/go.sum b/internal/tools/worksynchronizer/go.sum index 5c47db128349..6edb6c38165a 100644 --- a/internal/tools/worksynchronizer/go.sum +++ b/internal/tools/worksynchronizer/go.sum @@ -1,27 +1,6 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs= golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/aggregator/ckey/go.mod b/pkg/aggregator/ckey/go.mod index 675a15f02407..3242674568af 100644 --- a/pkg/aggregator/ckey/go.mod +++ b/pkg/aggregator/ckey/go.mod @@ -5,16 +5,11 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/tagset v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/sort v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/twmb/murmur3 v1.1.8 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/aggregator/ckey/go.sum b/pkg/aggregator/ckey/go.sum index bca4ed1f4687..d7823f2c401d 100644 --- a/pkg/aggregator/ckey/go.sum +++ b/pkg/aggregator/ckey/go.sum @@ -1,19 +1,6 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/api/go.mod b/pkg/api/go.mod index 3bb8aab180bf..c1cd083d1463 100644 --- a/pkg/api/go.mod +++ b/pkg/api/go.mod @@ -12,11 +12,9 @@ require ( github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 github.com/gorilla/mux v1.8.1 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -39,7 +37,6 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -52,8 +49,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -63,7 +60,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/api/go.sum b/pkg/api/go.sum index a870ebc84c7b..ebcc327c8324 100644 --- a/pkg/api/go.sum +++ b/pkg/api/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -50,12 +48,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -70,8 +65,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -83,8 +78,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -101,8 +96,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/collector/corechecks/cluster/kubeapi/kubernetes_events.go b/pkg/collector/corechecks/cluster/kubeapi/kubernetes_events.go index aad7ee8b6fee..6eff038a7e87 100644 --- a/pkg/collector/corechecks/cluster/kubeapi/kubernetes_events.go +++ b/pkg/collector/corechecks/cluster/kubeapi/kubernetes_events.go @@ -20,7 +20,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/util/option" cache "github.com/patrickmn/go-cache" - "gopkg.in/yaml.v2" + "go.yaml.in/yaml/v2" v1 "k8s.io/api/core/v1" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" diff --git a/pkg/collector/corechecks/cluster/kubeapi/kubernetes_metrics.go b/pkg/collector/corechecks/cluster/kubeapi/kubernetes_metrics.go index 6aa46b192d44..5e56adf9e3f6 100644 --- a/pkg/collector/corechecks/cluster/kubeapi/kubernetes_metrics.go +++ b/pkg/collector/corechecks/cluster/kubeapi/kubernetes_metrics.go @@ -22,7 +22,7 @@ import ( "time" - "gopkg.in/yaml.v2" + "go.yaml.in/yaml/v2" "k8s.io/api/core/v1" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" diff --git a/pkg/collector/corechecks/cluster/kubeapi/kubernetes_topology_config.go b/pkg/collector/corechecks/cluster/kubeapi/kubernetes_topology_config.go index 2be9adf5b46a..ca9a1bdc097b 100644 --- a/pkg/collector/corechecks/cluster/kubeapi/kubernetes_topology_config.go +++ b/pkg/collector/corechecks/cluster/kubeapi/kubernetes_topology_config.go @@ -8,7 +8,7 @@ import ( pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup" "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/StackVista/stackstate-receiver-go-client/pkg/model/topology" - "gopkg.in/yaml.v2" + "go.yaml.in/yaml/v2" ) const ( @@ -17,14 +17,14 @@ const ( // TopologyConfig is the config of the API server. type TopologyConfig struct { - ClusterName string `yaml:"cluster_name"` - CollectTopology bool `yaml:"collect_topology"` - CollectTimeout int `yaml:"collect_timeout"` - ConfigMapMaxDataSize int `yaml:"configmap_max_datasize"` - CSIPVMapperEnabled bool `yaml:"csi_pv_mapper_enabled"` - Resources ResourcesConfig `yaml:"resources"` - CheckID checkid.ID - Instance topology.Instance + ClusterName string `yaml:"cluster_name"` + CollectTopology bool `yaml:"collect_topology"` + CollectTimeout int `yaml:"collect_timeout"` + ConfigMapMaxDataSize int `yaml:"configmap_max_datasize"` + CSIPVMapperEnabled bool `yaml:"csi_pv_mapper_enabled"` + Resources ResourcesConfig `yaml:"resources"` + CheckID checkid.ID + Instance topology.Instance } type ResourcesConfig struct { diff --git a/pkg/collector/corechecks/cluster/kubeapi/kubernetes_topology_test.go b/pkg/collector/corechecks/cluster/kubeapi/kubernetes_topology_test.go index 36e27a348b0d..71d0dad25e62 100644 --- a/pkg/collector/corechecks/cluster/kubeapi/kubernetes_topology_test.go +++ b/pkg/collector/corechecks/cluster/kubeapi/kubernetes_topology_test.go @@ -110,11 +110,11 @@ func testConfigParsed(t *testing.T, input string, expected TopologyConfig) { func TestConfigurationParsing(t *testing.T) { defaultConfig := TopologyConfig{ // for empty config something is coming from global configuration - ClusterName: pkgconfigsetup.Datadog().GetString("cluster_name"), - CollectTopology: pkgconfigsetup.Datadog().GetBool("collect_kubernetes_topology"), - CollectTimeout: pkgconfigsetup.Datadog().GetInt("collect_kubernetes_timeout"), - ConfigMapMaxDataSize: DefaultConfigMapDataSizeLimit, - CSIPVMapperEnabled: pkgconfigsetup.Datadog().GetBool("kubernetes_csi_pv_mapper_enabled"), + ClusterName: pkgconfigsetup.Datadog().GetString("cluster_name"), + CollectTopology: pkgconfigsetup.Datadog().GetBool("collect_kubernetes_topology"), + CollectTimeout: pkgconfigsetup.Datadog().GetInt("collect_kubernetes_timeout"), + ConfigMapMaxDataSize: DefaultConfigMapDataSizeLimit, + CSIPVMapperEnabled: pkgconfigsetup.Datadog().GetBool("kubernetes_csi_pv_mapper_enabled"), Resources: ResourcesConfig{ Persistentvolumes: true, Persistentvolumeclaims: true, @@ -154,6 +154,29 @@ resources: expectedSimple.ClusterName = "mycluster" expectedSimple.Resources = ResourcesConfig{} testConfigParsed(t, allResourcesAreDisabledConfig, expectedSimple) + + aliasedResourcesConfig := ` +resource_defaults: &resource_defaults + configmaps: no + secrets: false +cluster_name: |- + mycluster +collect_topology: yes +collect_timeout: 0x2a +configmap_max_datasize: null +resources: + <<: *resource_defaults + namespaces: false +` + expectedAliased := defaultConfig + expectedAliased.ClusterName = "mycluster" + expectedAliased.CollectTopology = true + expectedAliased.CollectTimeout = 42 + expectedAliased.ConfigMapMaxDataSize = 0 + expectedAliased.Resources.ConfigMaps = false + expectedAliased.Resources.Secrets = false + expectedAliased.Resources.Namespaces = false + testConfigParsed(t, aliasedResourcesConfig, expectedAliased) } func TestRunClusterCollectors(t *testing.T) { diff --git a/pkg/collector/corechecks/system/windowscertificate/windows_certificate.go b/pkg/collector/corechecks/system/windowscertificate/windows_certificate.go index 27192e4f7e00..3b871688c585 100644 --- a/pkg/collector/corechecks/system/windowscertificate/windows_certificate.go +++ b/pkg/collector/corechecks/system/windowscertificate/windows_certificate.go @@ -18,12 +18,12 @@ import ( "time" "unsafe" - yy "github.com/ghodss/yaml" "github.com/swaggest/jsonschema-go" "github.com/xeipuuv/gojsonschema" yaml "go.yaml.in/yaml/v2" "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" + yy "sigs.k8s.io/yaml" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/aggregator/sender" diff --git a/pkg/config/basic/go.mod b/pkg/config/basic/go.mod index 8d6003f7c46e..64846fa68ce2 100644 --- a/pkg/config/basic/go.mod +++ b/pkg/config/basic/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/config/basic go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/config/basic/go.sum b/pkg/config/basic/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/config/basic/go.sum +++ b/pkg/config/basic/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/config/create/go.mod b/pkg/config/create/go.mod index 0d4d1cd587f3..d0b2ae04ca8f 100644 --- a/pkg/config/create/go.mod +++ b/pkg/config/create/go.mod @@ -10,30 +10,27 @@ require ( github.com/DataDog/datadog-agent/pkg/config/viperconfig v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/config/basic v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/config/helper v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/pkg/config/create/go.sum b/pkg/config/create/go.sum index a1512e440c01..701e8e4c5772 100644 --- a/pkg/config/create/go.sum +++ b/pkg/config/create/go.sum @@ -3,8 +3,6 @@ github.com/DataDog/viper v1.15.1 h1:kcdFE+qPndlWkhU4iEf/WpWQMCyVYHTv5HqvVf+SYJs= github.com/DataDog/viper v1.15.1/go.mod h1:rDLDREOPd+gpEbA8y4Y/5wTvyLqvUiCmDXX0jRZy8mw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -31,10 +29,7 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= @@ -47,15 +42,15 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= @@ -63,8 +58,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/config/env/go.mod b/pkg/config/env/go.mod index ca1297f0f566..6a3c872acb27 100644 --- a/pkg/config/env/go.mod +++ b/pkg/config/env/go.mod @@ -7,11 +7,9 @@ require ( github.com/DataDog/datadog-agent/pkg/util/filesystem v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/system v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/pointer v0.78.3-rc.2 // indirect @@ -19,7 +17,6 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/gofrs/flock v0.13.0 // indirect @@ -27,14 +24,13 @@ require ( github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect github.com/mdlayher/socket v0.5.1 // indirect github.com/mdlayher/vsock v1.2.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/config/env/go.sum b/pkg/config/env/go.sum index f062aff2fa19..7084128be478 100644 --- a/pkg/config/env/go.sum +++ b/pkg/config/env/go.sum @@ -1,7 +1,5 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -13,26 +11,18 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/hectane/go-acl v0.0.0-20230225031251-cdfc9e3acf94 h1:0G5JVG0aJDpWvH1r8idd+wbPcAH14UqIuUMepx3v9rE= github.com/hectane/go-acl v0.0.0-20230225031251-cdfc9e3acf94/go.mod h1:MvF7bZQy5lD2WcLXjFytRVvnTrFvrjo/qGka1MIBOPE= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -41,8 +31,8 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -54,8 +44,3 @@ golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/config/helper/go.mod b/pkg/config/helper/go.mod index e81baf1dccdd..60b28bedc3be 100644 --- a/pkg/config/helper/go.mod +++ b/pkg/config/helper/go.mod @@ -5,11 +5,9 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/config/model v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/config/viperconfig v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/config/basic v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect @@ -17,19 +15,18 @@ require ( github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/pkg/config/helper/go.sum b/pkg/config/helper/go.sum index a1512e440c01..701e8e4c5772 100644 --- a/pkg/config/helper/go.sum +++ b/pkg/config/helper/go.sum @@ -3,8 +3,6 @@ github.com/DataDog/viper v1.15.1 h1:kcdFE+qPndlWkhU4iEf/WpWQMCyVYHTv5HqvVf+SYJs= github.com/DataDog/viper v1.15.1/go.mod h1:rDLDREOPd+gpEbA8y4Y/5wTvyLqvUiCmDXX0jRZy8mw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -31,10 +29,7 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= @@ -47,15 +42,15 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= @@ -63,8 +58,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/config/mock/go.mod b/pkg/config/mock/go.mod index 2afe6847dcb2..d4c14ac9bdae 100644 --- a/pkg/config/mock/go.mod +++ b/pkg/config/mock/go.mod @@ -6,11 +6,9 @@ require ( github.com/DataDog/datadog-agent/pkg/config/create v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/config/model v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/config/setup v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -34,7 +32,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -47,8 +44,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -58,7 +55,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/config/mock/go.sum b/pkg/config/mock/go.sum index 0e81bdb38fc4..d2bdef1f9337 100644 --- a/pkg/config/mock/go.sum +++ b/pkg/config/mock/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -48,12 +46,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -68,8 +63,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -81,8 +76,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -97,8 +92,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/config/nodetreemodel/go.mod b/pkg/config/nodetreemodel/go.mod index 9f655143cf16..82a7eb2fd67e 100644 --- a/pkg/config/nodetreemodel/go.mod +++ b/pkg/config/nodetreemodel/go.mod @@ -13,27 +13,24 @@ require ( github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/spf13/cast v1.10.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 go.yaml.in/yaml/v2 v2.4.4 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/pkg/config/nodetreemodel/go.sum b/pkg/config/nodetreemodel/go.sum index 41cb505fa815..b284b48a5d69 100644 --- a/pkg/config/nodetreemodel/go.sum +++ b/pkg/config/nodetreemodel/go.sum @@ -6,8 +6,6 @@ github.com/DataDog/viper v1.15.1/go.mod h1:rDLDREOPd+gpEbA8y4Y/5wTvyLqvUiCmDXX0j github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -36,11 +34,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -50,15 +45,15 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= @@ -66,8 +61,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/config/remote/go.mod b/pkg/config/remote/go.mod index fcd76d1dfbca..66d93c692f67 100644 --- a/pkg/config/remote/go.mod +++ b/pkg/config/remote/go.mod @@ -19,7 +19,7 @@ require ( github.com/benbjohnson/clock v1.3.5 github.com/coreos/go-semver v0.3.1 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.etcd.io/bbolt v1.4.3 go.uber.org/atomic v1.11.0 google.golang.org/protobuf v1.36.11 @@ -55,7 +55,6 @@ require ( go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect golang.org/x/sync v0.22.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( @@ -83,7 +82,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect @@ -102,7 +100,6 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/philhofer/fwd v1.2.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -118,7 +115,7 @@ require ( go.opentelemetry.io/otel v1.45.0 // indirect go.opentelemetry.io/otel/metric v1.45.0 // indirect go.opentelemetry.io/otel/trace v1.45.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect diff --git a/pkg/config/remote/go.sum b/pkg/config/remote/go.sum index 53b3440e16a7..96684330be65 100644 --- a/pkg/config/remote/go.sum +++ b/pkg/config/remote/go.sum @@ -33,8 +33,6 @@ github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03V github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -130,8 +128,6 @@ github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -171,8 +167,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -218,8 +214,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/pkg/config/render_config/go.mod b/pkg/config/render_config/go.mod index 28ee7ec34022..334f0373e67f 100644 --- a/pkg/config/render_config/go.mod +++ b/pkg/config/render_config/go.mod @@ -4,13 +4,7 @@ go 1.26.0 require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 - go.yaml.in/yaml/v3 v3.0.4 -) - -require ( - github.com/kr/pretty v0.3.1 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + go.yaml.in/yaml/v3 v3.0.5 ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/config/render_config/go.sum b/pkg/config/render_config/go.sum index 8f5f0e82592d..5d33bf7e23d2 100644 --- a/pkg/config/render_config/go.sum +++ b/pkg/config/render_config/go.sum @@ -1,19 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/config/render_config/render_config.go b/pkg/config/render_config/render_config.go index 5deecf8c770c..df38de1b8724 100644 --- a/pkg/config/render_config/render_config.go +++ b/pkg/config/render_config/render_config.go @@ -223,7 +223,7 @@ func lint(destFile string) error { normalized = bytes.TrimSpace(normalized) // [sts] Collapse runs of blank lines on both sides before comparing. The - // gopkg.in/yaml.v3 encoder is non-idempotent around bare null keys (e.g. + // go.yaml.in/yaml/v3 encoder is non-idempotent around bare null keys (e.g. // `api_key:` followed by a blank line and a `## @param` block) — it // arbitrarily adds or removes the blank line depending on what follows in // the file, even though the YAML node tree is identical. The lint's @@ -252,7 +252,7 @@ func lint(destFile string) error { } // collapseBlankLines drops every blank-only line so the lint comparison ignores -// cosmetic whitespace shuffling that the gopkg.in/yaml.v3 encoder introduces +// cosmetic whitespace shuffling that the go.yaml.in/yaml/v3 encoder introduces // around null-value scalar nodes (e.g. it arbitrarily adds or removes the // single blank line between `api_key:` and a following `## @param` block, // even when the YAML node tree is identical). Blank lines in YAML carry no diff --git a/pkg/config/setup/go.mod b/pkg/config/setup/go.mod index 5472d15f5031..9d3eeb427ec2 100644 --- a/pkg/config/setup/go.mod +++ b/pkg/config/setup/go.mod @@ -21,13 +21,11 @@ require ( github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/system v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/winutil v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.yaml.in/yaml/v2 v2.4.4 golang.org/x/sys v0.47.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/secrets/utils v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/config/basic v0.78.3-rc.2 // indirect @@ -40,7 +38,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -53,8 +50,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -63,7 +60,7 @@ require ( github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/time v0.15.0 // indirect diff --git a/pkg/config/setup/go.sum b/pkg/config/setup/go.sum index 0d373952d9c9..fcdf7c5ba6d6 100644 --- a/pkg/config/setup/go.sum +++ b/pkg/config/setup/go.sum @@ -8,8 +8,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -53,13 +51,10 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -71,8 +66,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -84,8 +79,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -100,8 +95,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/config/structure/go.mod b/pkg/config/structure/go.mod index 71223d3e8f97..89ced7931451 100644 --- a/pkg/config/structure/go.mod +++ b/pkg/config/structure/go.mod @@ -12,11 +12,9 @@ require ( github.com/DataDog/datadog-agent/pkg/config/nodetreemodel v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/config/viperconfig v0.78.3-rc.2 github.com/go-viper/mapstructure/v2 v2.5.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/config/basic v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/config/teeconfig v0.78.3-rc.2 // indirect @@ -25,18 +23,17 @@ require ( github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/pkg/config/structure/go.sum b/pkg/config/structure/go.sum index 41cb505fa815..b284b48a5d69 100644 --- a/pkg/config/structure/go.sum +++ b/pkg/config/structure/go.sum @@ -6,8 +6,6 @@ github.com/DataDog/viper v1.15.1/go.mod h1:rDLDREOPd+gpEbA8y4Y/5wTvyLqvUiCmDXX0j github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -36,11 +34,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -50,15 +45,15 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= @@ -66,8 +61,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/config/teeconfig/go.mod b/pkg/config/teeconfig/go.mod index 67c046c236a1..5b2b89834f71 100644 --- a/pkg/config/teeconfig/go.mod +++ b/pkg/config/teeconfig/go.mod @@ -5,19 +5,16 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/config/model v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/time v0.15.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/config/teeconfig/go.sum b/pkg/config/teeconfig/go.sum index f75dbfe49ae9..befeb0397c22 100644 --- a/pkg/config/teeconfig/go.sum +++ b/pkg/config/teeconfig/go.sum @@ -1,23 +1,8 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/config/utils/go.mod b/pkg/config/utils/go.mod index 065864d5ed57..09ae4257bc5a 100644 --- a/pkg/config/utils/go.mod +++ b/pkg/config/utils/go.mod @@ -10,12 +10,10 @@ require ( github.com/DataDog/datadog-agent/pkg/fips v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 golang.org/x/net v0.58.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -36,7 +34,6 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -49,8 +46,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -60,7 +57,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.41.0 // indirect diff --git a/pkg/config/utils/go.sum b/pkg/config/utils/go.sum index 748ed97c826e..1135efabd727 100644 --- a/pkg/config/utils/go.sum +++ b/pkg/config/utils/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -48,12 +46,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -68,8 +63,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -81,8 +76,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -99,8 +94,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/config/viperconfig/go.mod b/pkg/config/viperconfig/go.mod index 510398d1f567..ef57c19150bf 100644 --- a/pkg/config/viperconfig/go.mod +++ b/pkg/config/viperconfig/go.mod @@ -8,28 +8,25 @@ require ( github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/viper v1.15.1 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/pkg/config/viperconfig/go.sum b/pkg/config/viperconfig/go.sum index a1512e440c01..701e8e4c5772 100644 --- a/pkg/config/viperconfig/go.sum +++ b/pkg/config/viperconfig/go.sum @@ -3,8 +3,6 @@ github.com/DataDog/viper v1.15.1 h1:kcdFE+qPndlWkhU4iEf/WpWQMCyVYHTv5HqvVf+SYJs= github.com/DataDog/viper v1.15.1/go.mod h1:rDLDREOPd+gpEbA8y4Y/5wTvyLqvUiCmDXX0jRZy8mw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -31,10 +29,7 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= @@ -47,15 +42,15 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= @@ -63,8 +58,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/errors/go.mod b/pkg/errors/go.mod index ca98b6cb60b2..dbbc373dd02a 100644 --- a/pkg/errors/go.mod +++ b/pkg/errors/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/errors go 1.26.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/errors/go.sum b/pkg/errors/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/errors/go.sum +++ b/pkg/errors/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/fleet/installer/go.mod b/pkg/fleet/installer/go.mod index 12d0b9ced085..88d1b1e88c58 100644 --- a/pkg/fleet/installer/go.mod +++ b/pkg/fleet/installer/go.mod @@ -15,23 +15,21 @@ require ( github.com/google/uuid v1.6.0 github.com/shirou/gopsutil/v4 v4.26.3 github.com/spf13/cobra v1.10.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.etcd.io/bbolt v1.4.3 go.uber.org/atomic v1.11.0 go.uber.org/multierr v1.11.0 go.yaml.in/yaml/v2 v2.4.4 - go.yaml.in/yaml/v3 v3.0.4 + go.yaml.in/yaml/v3 v3.0.5 golang.org/x/net v0.58.0 golang.org/x/sys v0.47.0 golang.org/x/text v0.41.0 gopkg.in/evanphx/json-patch.v4 v4.13.0 - gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/containerd/stargz-snapshotter/estargz v0.18.1 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/cli v29.2.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.4 // indirect @@ -45,7 +43,6 @@ require ( github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/spf13/pflag v1.0.10 // indirect diff --git a/pkg/fleet/installer/go.sum b/pkg/fleet/installer/go.sum index 93ab9086b055..8b443f3b9d08 100644 --- a/pkg/fleet/installer/go.sum +++ b/pkg/fleet/installer/go.sum @@ -7,8 +7,6 @@ github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F9 github.com/containerd/stargz-snapshotter/estargz v0.18.1 h1:cy2/lpgBXDA3cDKSyEfNOFMA/c10O1axL69EU7iirO8= github.com/containerd/stargz-snapshotter/estargz v0.18.1/go.mod h1:ALIEqa7B6oVDsrF37GkGN20SuvG/pIMm7FwP7ZmRb0Q= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/cli v29.2.1+incompatible h1:n3Jt0QVCN65eiVBoUTZQM9mcQICCJt3akW4pKAbKdJg= github.com/docker/cli v29.2.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= @@ -32,10 +30,6 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw= github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= @@ -48,12 +42,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -66,8 +56,8 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -84,8 +74,9 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs= golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= @@ -107,11 +98,7 @@ golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= diff --git a/pkg/fleet/installer/packages/datadog_agent_extensions.go b/pkg/fleet/installer/packages/datadog_agent_extensions.go index 361b64d3f526..a69cf7279cea 100644 --- a/pkg/fleet/installer/packages/datadog_agent_extensions.go +++ b/pkg/fleet/installer/packages/datadog_agent_extensions.go @@ -11,7 +11,7 @@ import ( "path/filepath" "strings" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" "github.com/DataDog/datadog-agent/pkg/fleet/installer/env" "github.com/DataDog/datadog-agent/pkg/fleet/installer/oci" diff --git a/pkg/fleet/installer/packages/datadog_agent_extensions_test.go b/pkg/fleet/installer/packages/datadog_agent_extensions_test.go index e7df9c7ce5fe..e534453b3920 100644 --- a/pkg/fleet/installer/packages/datadog_agent_extensions_test.go +++ b/pkg/fleet/installer/packages/datadog_agent_extensions_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" extensionsPkg "github.com/DataDog/datadog-agent/pkg/fleet/installer/packages/extensions" ) @@ -86,6 +86,33 @@ installer: assert.Nil(t, config.Installer.Registry.Extensions) } +func TestParseRegistryConfigAliases(t *testing.T) { + configContent := ` +registry_defaults: ®istry_defaults + url: registry.example.com + auth: password + username: null + password: |- + first line + second line +installer: + registry: + <<: *registry_defaults + extensions: + datadog-agent: + ddot: *registry_defaults +` + var config datadogAgentConfig + require.NoError(t, yaml.Unmarshal([]byte(configContent), &config)) + registry := config.Installer.Registry + assert.Equal(t, "registry.example.com", registry.URL) + assert.Empty(t, registry.Username) + assert.Equal(t, "first line\nsecond line", registry.Password) + require.Contains(t, registry.Extensions[agentPackage], "ddot") + assert.Equal(t, registry.URL, registry.Extensions[agentPackage]["ddot"].URL) + assert.Equal(t, registry.Password, registry.Extensions[agentPackage]["ddot"].Password) +} + func TestInstallDDOTExtensionIfEnabled_Disabled(t *testing.T) { t.Setenv("DD_OTELCOLLECTOR_ENABLED", "false") ctx := HookContext{Context: context.Background()} diff --git a/pkg/gohai/go.mod b/pkg/gohai/go.mod index b796ce14a877..1b318ec72ea9 100644 --- a/pkg/gohai/go.mod +++ b/pkg/gohai/go.mod @@ -8,27 +8,23 @@ require ( github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/moby/sys/mountinfo v0.7.2 github.com/shirou/gopsutil/v4 v4.26.3 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 golang.org/x/sys v0.47.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/pkg/gohai/go.sum b/pkg/gohai/go.sum index 0ae72b5db40a..0ccd443951a9 100644 --- a/pkg/gohai/go.sum +++ b/pkg/gohai/go.sum @@ -1,5 +1,3 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -7,24 +5,16 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -33,8 +23,8 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -42,8 +32,3 @@ golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/logs/client/go.mod b/pkg/logs/client/go.mod index 051f6ccafd4c..74956c93ab0a 100644 --- a/pkg/logs/client/go.mod +++ b/pkg/logs/client/go.mod @@ -18,12 +18,10 @@ require ( github.com/DataDog/datadog-agent/pkg/util/http v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 golang.org/x/net v0.58.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -56,7 +54,6 @@ require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -71,7 +68,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -91,7 +87,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.41.0 // indirect diff --git a/pkg/logs/client/go.sum b/pkg/logs/client/go.sum index 9552a60742b4..1d1016780c40 100644 --- a/pkg/logs/client/go.sum +++ b/pkg/logs/client/go.sum @@ -12,8 +12,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -63,8 +61,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -76,7 +72,6 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -95,8 +90,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -118,8 +113,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -138,8 +134,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/logs/diagnostic/go.mod b/pkg/logs/diagnostic/go.mod index c73369024ff8..8d81e9dde200 100644 --- a/pkg/logs/diagnostic/go.mod +++ b/pkg/logs/diagnostic/go.mod @@ -8,11 +8,9 @@ require ( github.com/DataDog/datadog-agent/pkg/config/setup v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/logs/message v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/logs/sources v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -45,7 +43,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -59,7 +56,6 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -75,7 +71,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/logs/diagnostic/go.sum b/pkg/logs/diagnostic/go.sum index 962dc810d4db..1a0e2908fc13 100644 --- a/pkg/logs/diagnostic/go.sum +++ b/pkg/logs/diagnostic/go.sum @@ -6,8 +6,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -51,12 +49,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -75,8 +70,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -98,8 +93,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -116,8 +112,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/logs/message/go.mod b/pkg/logs/message/go.mod index 300dab443dd4..8aecfbe5894a 100644 --- a/pkg/logs/message/go.mod +++ b/pkg/logs/message/go.mod @@ -7,11 +7,9 @@ require ( github.com/DataDog/datadog-agent/pkg/logs/sources v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/logs/types v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -40,7 +38,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -53,7 +50,6 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -64,7 +60,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/logs/message/go.sum b/pkg/logs/message/go.sum index 725644c637d0..449f5123d5db 100644 --- a/pkg/logs/message/go.sum +++ b/pkg/logs/message/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -50,12 +48,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -72,8 +67,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -93,8 +88,8 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -111,8 +106,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/logs/metrics/go.mod b/pkg/logs/metrics/go.mod index 87e460c7d036..53c17f24a3d0 100644 --- a/pkg/logs/metrics/go.mod +++ b/pkg/logs/metrics/go.mod @@ -5,11 +5,9 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/telemetry v0.78.3-rc.2 github.com/benbjohnson/clock v1.3.5 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/telemetry v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/def v0.78.3-rc.2 // indirect @@ -17,10 +15,8 @@ require ( github.com/DataDog/datadog-agent/pkg/util/option v0.78.3-rc.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect @@ -33,6 +29,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/pkg/logs/metrics/go.sum b/pkg/logs/metrics/go.sum index fc3cd035b5d1..c0559a1d1856 100644 --- a/pkg/logs/metrics/go.sum +++ b/pkg/logs/metrics/go.sum @@ -5,24 +5,16 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r 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/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw= github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -31,16 +23,14 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= @@ -55,14 +45,11 @@ go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/logs/pipeline/go.mod b/pkg/logs/pipeline/go.mod index 867405a0f522..1daa22aeaf7f 100644 --- a/pkg/logs/pipeline/go.mod +++ b/pkg/logs/pipeline/go.mod @@ -19,12 +19,10 @@ require ( github.com/DataDog/datadog-agent/pkg/logs/status/statusinterface v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/compression v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/startstop v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/agent-payload/v5 v5.0.184 // indirect github.com/DataDog/datadog-agent/comp/core/config v0.78.3-rc.2 // indirect @@ -71,7 +69,6 @@ require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -87,7 +84,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -106,7 +102,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/logs/pipeline/go.sum b/pkg/logs/pipeline/go.sum index b30cc7ba6fed..195e8adf7c87 100644 --- a/pkg/logs/pipeline/go.sum +++ b/pkg/logs/pipeline/go.sum @@ -16,8 +16,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -71,8 +69,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -84,7 +80,6 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -103,8 +98,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -128,8 +123,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -173,8 +169,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/logs/processor/go.mod b/pkg/logs/processor/go.mod index 7162345cef52..cf29d2b2f4e8 100644 --- a/pkg/logs/processor/go.mod +++ b/pkg/logs/processor/go.mod @@ -12,11 +12,9 @@ require ( github.com/DataDog/datadog-agent/pkg/logs/metrics v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/logs/sources v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -53,7 +51,6 @@ require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -69,7 +66,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -89,7 +85,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/logs/processor/go.sum b/pkg/logs/processor/go.sum index e74221951ec0..cd78fd299d28 100644 --- a/pkg/logs/processor/go.sum +++ b/pkg/logs/processor/go.sum @@ -14,8 +14,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -69,8 +67,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -82,7 +78,6 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -101,8 +96,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -126,8 +121,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -171,8 +167,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/logs/sender/go.mod b/pkg/logs/sender/go.mod index ce80bac058f1..ef8198660111 100644 --- a/pkg/logs/sender/go.mod +++ b/pkg/logs/sender/go.mod @@ -16,12 +16,10 @@ require ( github.com/DataDog/datadog-agent/pkg/util/compression v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/benbjohnson/clock v1.3.5 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/config v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect @@ -65,7 +63,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -80,7 +77,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -99,7 +95,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/logs/sender/go.sum b/pkg/logs/sender/go.sum index d078d6f10ae4..91fc4586fade 100644 --- a/pkg/logs/sender/go.sum +++ b/pkg/logs/sender/go.sum @@ -14,8 +14,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -65,8 +63,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -78,7 +74,6 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -97,8 +92,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -120,8 +115,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -140,8 +136,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/logs/sources/go.mod b/pkg/logs/sources/go.mod index 68bc1a8006f1..4c71e1ef54dd 100644 --- a/pkg/logs/sources/go.mod +++ b/pkg/logs/sources/go.mod @@ -7,11 +7,9 @@ require ( github.com/DataDog/datadog-agent/pkg/logs/status/utils v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/statstracker v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -39,7 +37,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -52,7 +49,6 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -63,7 +59,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/logs/sources/go.sum b/pkg/logs/sources/go.sum index 725644c637d0..449f5123d5db 100644 --- a/pkg/logs/sources/go.sum +++ b/pkg/logs/sources/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -50,12 +48,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -72,8 +67,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -93,8 +88,8 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -111,8 +106,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/logs/status/utils/go.mod b/pkg/logs/status/utils/go.mod index beb7cb90ccb1..eadab5bd3116 100644 --- a/pkg/logs/status/utils/go.mod +++ b/pkg/logs/status/utils/go.mod @@ -3,19 +3,11 @@ module github.com/DataDog/datadog-agent/pkg/logs/status/utils go 1.25.0 require ( - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/logs/status/utils/go.sum b/pkg/logs/status/utils/go.sum index e4b982ff7a81..ff2ce5d09b0e 100644 --- a/pkg/logs/status/utils/go.sum +++ b/pkg/logs/status/utils/go.sum @@ -1,25 +1,6 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/logs/util/testutils/go.mod b/pkg/logs/util/testutils/go.mod index cc86c77c1ff8..c4263d9cc652 100644 --- a/pkg/logs/util/testutils/go.mod +++ b/pkg/logs/util/testutils/go.mod @@ -48,6 +48,7 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -57,7 +58,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/logs/util/testutils/go.sum b/pkg/logs/util/testutils/go.sum index 725644c637d0..449f5123d5db 100644 --- a/pkg/logs/util/testutils/go.sum +++ b/pkg/logs/util/testutils/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -50,12 +48,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -72,8 +67,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -93,8 +88,8 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -111,8 +106,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/metrics/go.mod b/pkg/metrics/go.mod index c005bda0ffc3..8c65dfa582ed 100644 --- a/pkg/metrics/go.mod +++ b/pkg/metrics/go.mod @@ -13,12 +13,10 @@ require ( github.com/DataDog/datadog-agent/pkg/util/buf v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/quantile v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/config v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect @@ -60,7 +58,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect @@ -76,7 +73,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -96,7 +92,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/metrics/go.sum b/pkg/metrics/go.sum index 498020f7e7b6..24e0d675efcd 100644 --- a/pkg/metrics/go.sum +++ b/pkg/metrics/go.sum @@ -12,8 +12,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -67,8 +65,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -80,7 +76,6 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -99,8 +94,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -124,8 +119,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -142,8 +138,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/network/driver/go.mod b/pkg/network/driver/go.mod index 9eb831250c34..a400772578e6 100644 --- a/pkg/network/driver/go.mod +++ b/pkg/network/driver/go.mod @@ -7,13 +7,11 @@ require ( github.com/DataDog/datadog-agent/pkg/telemetry v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/winutil v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 golang.org/x/sys v0.47.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/def v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect @@ -23,10 +21,8 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect @@ -38,7 +34,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/time v0.15.0 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/pkg/network/driver/go.sum b/pkg/network/driver/go.sum index e169f1f29a78..b1cbe942da3d 100644 --- a/pkg/network/driver/go.sum +++ b/pkg/network/driver/go.sum @@ -3,24 +3,16 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r 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/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw= github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -29,16 +21,14 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= @@ -53,8 +43,9 @@ go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= @@ -62,7 +53,3 @@ golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/networkdevice/profile/go.mod b/pkg/networkdevice/profile/go.mod index ae9fa3f948b6..d0665d9baedd 100644 --- a/pkg/networkdevice/profile/go.mod +++ b/pkg/networkdevice/profile/go.mod @@ -6,7 +6,7 @@ require ( github.com/invopop/jsonschema v0.12.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/spf13/cobra v1.10.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.yaml.in/yaml/v2 v2.4.4 ) @@ -15,14 +15,13 @@ require gopkg.in/yaml.v3 v3.0.1 // indirect require ( github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mailru/easyjson v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/pkg/networkdevice/profile/go.sum b/pkg/networkdevice/profile/go.sum index 00000c9c0602..e353e600329c 100644 --- a/pkg/networkdevice/profile/go.sum +++ b/pkg/networkdevice/profile/go.sum @@ -4,8 +4,6 @@ github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJ github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= @@ -20,8 +18,6 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= @@ -33,13 +29,15 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/pkg/networkpath/payload/go.mod b/pkg/networkpath/payload/go.mod index fc51e67eff21..547896c5bc9b 100644 --- a/pkg/networkpath/payload/go.mod +++ b/pkg/networkpath/payload/go.mod @@ -4,18 +4,10 @@ go 1.26.0 require ( github.com/DataDog/datadog-agent/pkg/network/payload v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/networkpath/payload/go.sum b/pkg/networkpath/payload/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/networkpath/payload/go.sum +++ b/pkg/networkpath/payload/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/obfuscate/go.mod b/pkg/obfuscate/go.mod index 9130f3532075..50ea8abc1fa1 100644 --- a/pkg/obfuscate/go.mod +++ b/pkg/obfuscate/go.mod @@ -6,26 +6,19 @@ require ( github.com/DataDog/datadog-go/v5 v5.8.3 github.com/DataDog/go-sqllexer v0.2.1 github.com/outcaste-io/ristretto v0.2.3 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/kr/pretty v0.3.1 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/stretchr/objx v0.5.3 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sys v0.47.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/obfuscate/go.sum b/pkg/obfuscate/go.sum index 0e1e0706a1e1..1d73a23d3cf5 100644 --- a/pkg/obfuscate/go.sum +++ b/pkg/obfuscate/go.sum @@ -8,11 +8,8 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cespare/xxhash/v2 v2.1.1/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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -20,24 +17,11 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0= github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -50,12 +34,14 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -86,8 +72,5 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/opentelemetry-mapping-go/inframetadata/go.mod b/pkg/opentelemetry-mapping-go/inframetadata/go.mod index ea69dc53bfa8..c69044570670 100644 --- a/pkg/opentelemetry-mapping-go/inframetadata/go.mod +++ b/pkg/opentelemetry-mapping-go/inframetadata/go.mod @@ -5,32 +5,28 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/serializer v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/collector/pdata v1.57.0 go.opentelemetry.io/otel v1.45.0 go.uber.org/zap v1.28.0 ) -require ( - github.com/cespare/xxhash/v2 v2.3.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require github.com/cespare/xxhash/v2 v2.3.0 // indirect require ( github.com/DataDog/datadog-agent/pkg/trace/log v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/trace/traceutil v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/hashicorp/go-version v1.9.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.opentelemetry.io/collector/component v1.57.0 // indirect go.opentelemetry.io/collector/featuregate v1.57.0 // indirect go.opentelemetry.io/otel/metric v1.45.0 // indirect go.opentelemetry.io/otel/trace v1.45.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/opentelemetry-mapping-go/inframetadata/go.sum b/pkg/opentelemetry-mapping-go/inframetadata/go.sum index 7b3e85381567..c9df74ffe51f 100644 --- a/pkg/opentelemetry-mapping-go/inframetadata/go.sum +++ b/pkg/opentelemetry-mapping-go/inframetadata/go.sum @@ -2,8 +2,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -17,10 +15,6 @@ github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaX github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -28,14 +22,10 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/collector/component v1.57.0 h1:WKIqx2Bs0JaAZxDEhsLradXpYxnwAxVFzWhQUmu2q3w= @@ -72,14 +62,9 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/opentelemetry-mapping-go/inframetadata/gohai/internal/gohaitest/go.mod b/pkg/opentelemetry-mapping-go/inframetadata/gohai/internal/gohaitest/go.mod index 9e655cf9a1d4..ccb4a24f221b 100644 --- a/pkg/opentelemetry-mapping-go/inframetadata/gohai/internal/gohaitest/go.mod +++ b/pkg/opentelemetry-mapping-go/inframetadata/gohai/internal/gohaitest/go.mod @@ -5,19 +5,13 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/inframetadata v0.78.3-rc.2 github.com/DataDog/gohai v0.0.0-20230524154621-4316413895ee - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/opentelemetry-mapping-go/inframetadata/gohai/internal/gohaitest/go.sum b/pkg/opentelemetry-mapping-go/inframetadata/gohai/internal/gohaitest/go.sum index 57c6e8542733..4cec0e125c8e 100644 --- a/pkg/opentelemetry-mapping-go/inframetadata/gohai/internal/gohaitest/go.sum +++ b/pkg/opentelemetry-mapping-go/inframetadata/gohai/internal/gohaitest/go.sum @@ -3,30 +3,14 @@ github.com/DataDog/gohai v0.0.0-20230524154621-4316413895ee/go.mod h1:nTot/Iy0kW github.com/cihub/seelog v0.0.0-20151216151435-d2c6e5aa9fbf/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v3 v3.22.12/go.mod h1:Xd7P1kwZcp5VW52+9XsirIKd/BROzbb2wdX3Kqlz9uI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -35,11 +19,13 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -48,8 +34,5 @@ golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/opentelemetry-mapping-go/otlp/attributes/go.mod b/pkg/opentelemetry-mapping-go/otlp/attributes/go.mod index 0b19bf2ca197..31768b0f8644 100644 --- a/pkg/opentelemetry-mapping-go/otlp/attributes/go.mod +++ b/pkg/opentelemetry-mapping-go/otlp/attributes/go.mod @@ -5,7 +5,7 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/trace/log v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/trace/traceutil v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/collector/component v1.57.0 go.opentelemetry.io/collector/component/componenttest v0.150.0 go.opentelemetry.io/collector/pdata v1.57.0 @@ -19,16 +19,13 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/go-version v1.9.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/collector/featuregate v1.57.0 // indirect go.opentelemetry.io/otel/sdk v1.45.0 // indirect @@ -36,6 +33,7 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect ) diff --git a/pkg/opentelemetry-mapping-go/otlp/attributes/go.sum b/pkg/opentelemetry-mapping-go/otlp/attributes/go.sum index 0428669ce2a9..a3426114fbd0 100644 --- a/pkg/opentelemetry-mapping-go/otlp/attributes/go.sum +++ b/pkg/opentelemetry-mapping-go/otlp/attributes/go.sum @@ -2,8 +2,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -18,10 +16,6 @@ github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaX github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -29,14 +23,10 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/collector/component v1.57.0 h1:WKIqx2Bs0JaAZxDEhsLradXpYxnwAxVFzWhQUmu2q3w= @@ -75,14 +65,9 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/opentelemetry-mapping-go/otlp/logs/go.mod b/pkg/opentelemetry-mapping-go/otlp/logs/go.mod index 7b255ed2eb2c..b43db5115cbb 100644 --- a/pkg/opentelemetry-mapping-go/otlp/logs/go.mod +++ b/pkg/opentelemetry-mapping-go/otlp/logs/go.mod @@ -8,7 +8,7 @@ require ( github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/rum v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/orchestrator/model v0.78.3-rc.2 github.com/DataDog/datadog-api-client-go/v2 v2.56.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/twmb/murmur3 v1.1.8 go.opentelemetry.io/collector/component v1.57.0 go.opentelemetry.io/collector/component/componenttest v0.150.0 @@ -17,10 +17,7 @@ require ( go.uber.org/zap v1.28.0 ) -require ( - github.com/cespare/xxhash/v2 v2.3.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require github.com/cespare/xxhash/v2 v2.3.0 // indirect require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect @@ -32,7 +29,6 @@ require ( github.com/DataDog/mmh3 v0.0.0-20210722141835-012dc69a9e49 // indirect github.com/DataDog/zstd v1.5.7 // indirect github.com/DataDog/zstd_0 v0.0.0-20210310093942-586c1286621f // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/goccy/go-json v0.10.6 // indirect @@ -44,7 +40,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/collector/featuregate v1.57.0 // indirect go.opentelemetry.io/otel/metric v1.45.0 // indirect @@ -53,7 +48,7 @@ require ( go.opentelemetry.io/otel/trace v1.45.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/time v0.15.0 // indirect diff --git a/pkg/opentelemetry-mapping-go/otlp/logs/go.sum b/pkg/opentelemetry-mapping-go/otlp/logs/go.sum index cda42d7f4a32..62a52044c0dd 100644 --- a/pkg/opentelemetry-mapping-go/otlp/logs/go.sum +++ b/pkg/opentelemetry-mapping-go/otlp/logs/go.sum @@ -12,8 +12,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -36,10 +34,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw= github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -49,14 +43,10 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWu github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -99,8 +89,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -134,8 +124,3 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/opentelemetry-mapping-go/otlp/metrics/go.mod b/pkg/opentelemetry-mapping-go/otlp/metrics/go.mod index 9f58b11ae1f5..74ea42b6958a 100644 --- a/pkg/opentelemetry-mapping-go/otlp/metrics/go.mod +++ b/pkg/opentelemetry-mapping-go/otlp/metrics/go.mod @@ -11,7 +11,7 @@ require ( github.com/lightstep/go-expohisto v1.0.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.150.0 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/twmb/murmur3 v1.1.8 go.opentelemetry.io/collector/component v1.57.0 go.opentelemetry.io/collector/component/componenttest v0.150.0 @@ -21,16 +21,12 @@ require ( google.golang.org/protobuf v1.36.11 ) -require ( - go.opentelemetry.io/collector/pdata/xpdata v0.150.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require go.opentelemetry.io/collector/pdata/xpdata v0.150.0 // indirect require ( github.com/DataDog/datadog-agent/pkg/trace/log v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/trace/traceutil v0.78.3-rc.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -42,7 +38,6 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.150.0 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/tinylib/msgp v1.6.3 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/collector/featuregate v1.57.0 // indirect @@ -53,6 +48,7 @@ require ( go.opentelemetry.io/otel/trace v1.45.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect ) diff --git a/pkg/opentelemetry-mapping-go/otlp/metrics/go.sum b/pkg/opentelemetry-mapping-go/otlp/metrics/go.sum index f807cb67812b..fe77f18cee66 100644 --- a/pkg/opentelemetry-mapping-go/otlp/metrics/go.sum +++ b/pkg/opentelemetry-mapping-go/otlp/metrics/go.sum @@ -4,8 +4,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -26,10 +24,6 @@ github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaX github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lightstep/go-expohisto v1.0.0 h1:UPtTS1rGdtehbbAF7o/dhkWLTDI73UifG8LbfQI7cA4= github.com/lightstep/go-expohisto v1.0.0/go.mod h1:xDXD0++Mu2FOaItXtdDfksfgxfV0z1TMPa+e/EUd0cs= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -51,14 +45,10 @@ github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= @@ -109,16 +99,13 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/opentelemetry-mapping-go/otlp/rum/go.mod b/pkg/opentelemetry-mapping-go/otlp/rum/go.mod index 0c8560ef306f..e30f364018e1 100644 --- a/pkg/opentelemetry-mapping-go/otlp/rum/go.mod +++ b/pkg/opentelemetry-mapping-go/otlp/rum/go.mod @@ -3,28 +3,23 @@ module github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/rum go 1.25.0 require ( - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/collector/pdata v1.57.0 go.opentelemetry.io/otel v1.45.0 go.uber.org/zap v1.28.0 ) -require ( - github.com/cespare/xxhash/v2 v2.3.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require github.com/cespare/xxhash/v2 v2.3.0 // indirect require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/hashicorp/go-version v1.9.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect go.opentelemetry.io/collector/featuregate v1.57.0 // indirect go.opentelemetry.io/otel/trace v1.45.0 // indirect go.uber.org/multierr v1.11.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/opentelemetry-mapping-go/otlp/rum/go.sum b/pkg/opentelemetry-mapping-go/otlp/rum/go.sum index f38ef2f2b3b7..774e183fbd9c 100644 --- a/pkg/opentelemetry-mapping-go/otlp/rum/go.sum +++ b/pkg/opentelemetry-mapping-go/otlp/rum/go.sum @@ -2,8 +2,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -11,10 +9,6 @@ github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaX github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -22,14 +16,10 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.opentelemetry.io/collector/featuregate v1.57.0 h1:KPDSUKYn6MHwgyGRSGPPcW/G96HH93pxuvvPwM+R8nY= go.opentelemetry.io/collector/featuregate v1.57.0/go.mod h1:4ga1QBMPEejXXmpyJS8lmaRpknJ3Lb9Bvk6e420bUFU= go.opentelemetry.io/collector/internal/testutil v0.151.0 h1:CFjDItLuqzblItOsnK6IPSdrsOaZCaDjYpB8qWG+XHI= @@ -52,12 +42,7 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/orchestrator/model/go.mod b/pkg/orchestrator/model/go.mod index 19c35ec2fea1..35511c7723b7 100644 --- a/pkg/orchestrator/model/go.mod +++ b/pkg/orchestrator/model/go.mod @@ -12,7 +12,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/pkg/orchestrator/model/go.sum b/pkg/orchestrator/model/go.sum index 5422e584ec98..23a37a0b2ad6 100644 --- a/pkg/orchestrator/model/go.sum +++ b/pkg/orchestrator/model/go.sum @@ -1,25 +1,10 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/orchestrator/util/go.mod b/pkg/orchestrator/util/go.mod index f2e314cd2711..e516b4a1c278 100644 --- a/pkg/orchestrator/util/go.mod +++ b/pkg/orchestrator/util/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/orchestrator/util go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/orchestrator/util/go.sum b/pkg/orchestrator/util/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/orchestrator/util/go.sum +++ b/pkg/orchestrator/util/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/process/util/api/go.mod b/pkg/process/util/api/go.mod index fe565454d2d6..27387631c20e 100644 --- a/pkg/process/util/api/go.mod +++ b/pkg/process/util/api/go.mod @@ -7,11 +7,9 @@ require ( github.com/DataDog/datadog-agent/pkg/config/utils v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/telemetry v0.78.3-rc.2 github.com/gogo/protobuf v1.3.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -47,7 +45,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -63,12 +60,12 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect @@ -83,7 +80,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/process/util/api/go.sum b/pkg/process/util/api/go.sum index 68b76af65129..77f228eaf999 100644 --- a/pkg/process/util/api/go.sum +++ b/pkg/process/util/api/go.sum @@ -18,8 +18,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -73,8 +71,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -86,7 +82,6 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -105,8 +100,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -130,8 +125,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -175,8 +171,4 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/proto/go.mod b/pkg/proto/go.mod index 00329aa53442..e7d6bcab4c2f 100644 --- a/pkg/proto/go.mod +++ b/pkg/proto/go.mod @@ -8,25 +8,22 @@ require ( github.com/golang/mock v1.7.0-rc.1 github.com/google/gofuzz v1.2.0 github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/tinylib/msgp v1.6.3 github.com/vmihailenco/msgpack/v4 v4.3.13 google.golang.org/grpc v1.83.2 google.golang.org/protobuf v1.36.11 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/philhofer/fwd v1.2.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/vmihailenco/tagparser v0.1.2 // indirect go.opentelemetry.io/otel v1.45.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.45.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.41.0 // indirect diff --git a/pkg/proto/go.sum b/pkg/proto/go.sum index 1d727de94ac0..48faddc48836 100644 --- a/pkg/proto/go.sum +++ b/pkg/proto/go.sum @@ -2,8 +2,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -36,13 +34,11 @@ github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/vmihailenco/msgpack/v4 v4.3.13 h1:A2wsiTbvp63ilDaWmsk2wjx6xZdxQOvpiNlKBGKKXKI= @@ -64,6 +60,8 @@ go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJj go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA= go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -122,9 +120,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/remoteconfig/state/go.mod b/pkg/remoteconfig/state/go.mod index 489df522688f..e122c435f80b 100644 --- a/pkg/remoteconfig/state/go.mod +++ b/pkg/remoteconfig/state/go.mod @@ -5,16 +5,13 @@ go 1.26.0 require ( github.com/DataDog/go-tuf v1.1.1-0.5.2 github.com/secure-systems-lab/go-securesystemslib v0.9.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect ) diff --git a/pkg/remoteconfig/state/go.sum b/pkg/remoteconfig/state/go.sum index eb8571879a66..1e96206aa6df 100644 --- a/pkg/remoteconfig/state/go.sum +++ b/pkg/remoteconfig/state/go.sum @@ -1,8 +1,6 @@ github.com/DataDog/go-tuf v1.1.1-0.5.2 h1:YWvghV4ZvrQsPcUw8IOUMSDpqc3W5ruOIC+KJxPknv0= github.com/DataDog/go-tuf v1.1.1-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -10,21 +8,18 @@ github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3x github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/security/secl/go.mod b/pkg/security/secl/go.mod index 2639c0d0fee3..bd95b55dd287 100644 --- a/pkg/security/secl/go.mod +++ b/pkg/security/secl/go.mod @@ -14,28 +14,24 @@ require ( github.com/jellydator/ttlcache/v3 v3.4.0 github.com/skydive-project/go-debouncer v1.0.1 github.com/spf13/cast v1.10.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/weppos/publicsuffix-go v0.50.3 github.com/xeipuuv/gojsonschema v1.2.0 go.uber.org/atomic v1.11.0 - go.yaml.in/yaml/v3 v3.0.4 + go.yaml.in/yaml/v3 v3.0.5 golang.org/x/sys v0.47.0 golang.org/x/text v0.41.0 sigs.k8s.io/yaml v1.6.0 ) require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/security/secl/go.sum b/pkg/security/secl/go.sum index d096193d3096..5e5f7b251540 100644 --- a/pkg/security/secl/go.sum +++ b/pkg/security/secl/go.sum @@ -7,8 +7,6 @@ github.com/charlievieth/strcase v0.0.5 h1:gV4iXVyD6eI5KdfOV+/vIVCKXZwtCWOmDMcu7U github.com/charlievieth/strcase v0.0.5/go.mod h1:FIOYY1aDBMSIOFqmVomHBpoK+bteGlESRsgsdWjrhx8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -26,16 +24,11 @@ github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY= github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/skydive-project/go-debouncer v1.0.1 h1:N75Mdusd65Jjbc7k5t2oo+7qLIdMtSNJKssmpEYuSgo= @@ -45,8 +38,8 @@ github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qq github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/weppos/publicsuffix-go v0.50.3 h1:eT5dcjHQcVDNc0igpFEsGHKIip30feuB2zuuI9eJxiE= github.com/weppos/publicsuffix-go v0.50.3/go.mod h1:/rOa781xBykZhHK/I3QeHo92qdDKVmKZKF7s8qAEM/4= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -62,8 +55,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -85,10 +78,6 @@ golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/pkg/security/seclwin/go.sum b/pkg/security/seclwin/go.sum index 5b407565c85e..0f22d18af671 100644 --- a/pkg/security/seclwin/go.sum +++ b/pkg/security/seclwin/go.sum @@ -5,21 +5,19 @@ github.com/charlievieth/strcase v0.0.5 h1:gV4iXVyD6eI5KdfOV+/vIVCKXZwtCWOmDMcu7U github.com/charlievieth/strcase v0.0.5/go.mod h1:FIOYY1aDBMSIOFqmVomHBpoK+bteGlESRsgsdWjrhx8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY= github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/weppos/publicsuffix-go v0.50.3 h1:eT5dcjHQcVDNc0igpFEsGHKIip30feuB2zuuI9eJxiE= github.com/weppos/publicsuffix-go v0.50.3/go.mod h1:/rOa781xBykZhHK/I3QeHo92qdDKVmKZKF7s8qAEM/4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -30,5 +28,3 @@ golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/serializer/go.mod b/pkg/serializer/go.mod index 3a9549dac1df..1d40b2769b12 100644 --- a/pkg/serializer/go.mod +++ b/pkg/serializer/go.mod @@ -30,13 +30,11 @@ require ( github.com/json-iterator/go v1.1.12 github.com/protocolbuffers/protoscope v0.0.0-20221109213918-8e7a6aafa2c9 github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/twmb/murmur3 v1.1.8 google.golang.org/protobuf v1.36.11 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -88,7 +86,6 @@ require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fatih/color v1.18.0 // indirect @@ -114,7 +111,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -135,7 +131,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/serializer/go.sum b/pkg/serializer/go.sum index df63fbcf9f4f..9d4c8daed3e2 100644 --- a/pkg/serializer/go.sum +++ b/pkg/serializer/go.sum @@ -22,8 +22,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -109,8 +107,6 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -149,8 +145,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -176,8 +172,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -224,9 +221,5 @@ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBN google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= diff --git a/pkg/ssi/testutils/go.mod b/pkg/ssi/testutils/go.mod index 12c4e60c1554..606eada056b2 100644 --- a/pkg/ssi/testutils/go.mod +++ b/pkg/ssi/testutils/go.mod @@ -3,25 +3,23 @@ module github.com/DataDog/datadog-agent/pkg/ssi/testutils go 1.26.0 require ( - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 k8s.io/api v0.35.3 k8s.io/apimachinery v0.35.3 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fxamacker/cbor/v2 v2.9.1 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kr/text v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/text v0.41.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/pkg/ssi/testutils/go.sum b/pkg/ssi/testutils/go.sum index 71acbc4fabdd..b5c0d326d230 100644 --- a/pkg/ssi/testutils/go.sum +++ b/pkg/ssi/testutils/go.sum @@ -1,4 +1,3 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -12,10 +11,6 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -25,29 +20,24 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWu github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.35.3 h1:pA2fiBc6+N9PDf7SAiluKGEBuScsTzd2uYBkA5RzNWQ= k8s.io/api v0.35.3/go.mod h1:9Y9tkBcFwKNq2sxwZTQh1Njh9qHl81D0As56tu42GA4= k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8= diff --git a/pkg/status/health/go.mod b/pkg/status/health/go.mod index 0cc1a3e170d2..609c01045b00 100644 --- a/pkg/status/health/go.mod +++ b/pkg/status/health/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/status/health go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/status/health/go.sum b/pkg/status/health/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/status/health/go.sum +++ b/pkg/status/health/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/tagger/types/go.sum b/pkg/tagger/types/go.sum index 5d19fde92527..c2336837ec7d 100644 --- a/pkg/tagger/types/go.sum +++ b/pkg/tagger/types/go.sum @@ -1,8 +1,4 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/tagset/go.mod b/pkg/tagset/go.mod index 05cde227ccb7..f184cd486d29 100644 --- a/pkg/tagset/go.mod +++ b/pkg/tagset/go.mod @@ -4,17 +4,11 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/util/sort v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/twmb/murmur3 v1.1.8 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/text v0.2.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/tagset/go.sum b/pkg/tagset/go.sum index 5a14dd5d387a..d7823f2c401d 100644 --- a/pkg/tagset/go.sum +++ b/pkg/tagset/go.sum @@ -1,20 +1,6 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/telemetry/go.mod b/pkg/telemetry/go.mod index 52bcfee88e55..8f98935c46d3 100644 --- a/pkg/telemetry/go.mod +++ b/pkg/telemetry/go.mod @@ -7,30 +7,27 @@ require ( go.uber.org/atomic v1.11.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/def v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/fxutil v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/option v0.78.3-rc.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect go.uber.org/dig v1.19.0 // indirect go.uber.org/fx v1.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/pkg/telemetry/go.sum b/pkg/telemetry/go.sum index 24d0ca9b0796..fea1d3fefb33 100644 --- a/pkg/telemetry/go.sum +++ b/pkg/telemetry/go.sum @@ -3,24 +3,16 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r 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/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw= github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -29,16 +21,14 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= @@ -53,14 +43,11 @@ go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/trace/go.mod b/pkg/trace/go.mod index d10f09355687..76c765ec8269 100644 --- a/pkg/trace/go.mod +++ b/pkg/trace/go.mod @@ -26,7 +26,7 @@ require ( github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.150.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/tinylib/msgp v1.6.3 github.com/vmihailenco/msgpack/v4 v4.3.13 go.opentelemetry.io/collector/component v1.57.0 // indirect @@ -69,7 +69,6 @@ require ( go.opentelemetry.io/collector/featuregate v1.57.0 // indirect go.opentelemetry.io/collector/processor/processorhelper v0.150.0 // indirect go.opentelemetry.io/collector/processor/xprocessor v0.150.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( @@ -107,7 +106,6 @@ require ( github.com/philhofer/fwd v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -147,7 +145,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/text v0.41.0 // indirect diff --git a/pkg/trace/go.sum b/pkg/trace/go.sum index 962ac974d6af..fc0f58b0eca7 100644 --- a/pkg/trace/go.sum +++ b/pkg/trace/go.sum @@ -120,8 +120,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -160,8 +158,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -333,8 +332,9 @@ go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -422,7 +422,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8= k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= diff --git a/pkg/trace/log/go.mod b/pkg/trace/log/go.mod index 1f90af90c0a5..c1ac6963b4ad 100644 --- a/pkg/trace/log/go.mod +++ b/pkg/trace/log/go.mod @@ -3,19 +3,11 @@ module github.com/DataDog/datadog-agent/pkg/trace/log go 1.25.0 require ( - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/trace/log/go.sum b/pkg/trace/log/go.sum index e4b982ff7a81..ff2ce5d09b0e 100644 --- a/pkg/trace/log/go.sum +++ b/pkg/trace/log/go.sum @@ -1,25 +1,6 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/trace/otel/go.mod b/pkg/trace/otel/go.mod index 8df728d18d83..91ef9a4ce458 100644 --- a/pkg/trace/otel/go.mod +++ b/pkg/trace/otel/go.mod @@ -16,7 +16,7 @@ require ( github.com/DataDog/datadog-agent/pkg/trace/traceutil v0.78.3-rc.2 github.com/DataDog/datadog-go/v5 v5.8.3 github.com/google/go-cmp v0.7.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/collector/component/componenttest v0.150.0 go.opentelemetry.io/collector/consumer v1.57.0 go.opentelemetry.io/collector/pdata v1.57.0 @@ -26,8 +26,6 @@ require ( google.golang.org/protobuf v1.36.11 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( cloud.google.com/go/auth v0.18.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect @@ -101,7 +99,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -135,7 +132,6 @@ require ( github.com/philhofer/fwd v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -176,7 +172,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect diff --git a/pkg/trace/otel/go.sum b/pkg/trace/otel/go.sum index 91d6f22601ed..9af47124b464 100644 --- a/pkg/trace/otel/go.sum +++ b/pkg/trace/otel/go.sum @@ -37,8 +37,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -176,8 +174,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -225,8 +221,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -326,8 +323,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -439,7 +437,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/trace/stats/go.mod b/pkg/trace/stats/go.mod index 490daefe0430..db9a4c6a2218 100644 --- a/pkg/trace/stats/go.mod +++ b/pkg/trace/stats/go.mod @@ -12,14 +12,12 @@ require ( github.com/DataDog/datadog-go/v5 v5.8.3 github.com/DataDog/sketches-go v1.4.8 github.com/google/gofuzz v1.2.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/collector/pdata v1.57.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d google.golang.org/protobuf v1.36.11 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.78.3-rc.2 // indirect @@ -32,7 +30,6 @@ require ( github.com/DataDog/go-tuf v1.1.1-0.5.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -45,7 +42,6 @@ require ( github.com/philhofer/fwd v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect @@ -61,7 +57,7 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/time v0.15.0 // indirect gopkg.in/ini.v1 v1.67.1 // indirect diff --git a/pkg/trace/stats/go.sum b/pkg/trace/stats/go.sum index abeb90e6a5a9..3415eea61bff 100644 --- a/pkg/trace/stats/go.sum +++ b/pkg/trace/stats/go.sum @@ -14,8 +14,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -72,8 +70,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -96,8 +92,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -172,8 +169,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= @@ -222,7 +219,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8= k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= diff --git a/pkg/trace/traceutil/go.mod b/pkg/trace/traceutil/go.mod index 8bce69166d7c..1bca77683141 100644 --- a/pkg/trace/traceutil/go.mod +++ b/pkg/trace/traceutil/go.mod @@ -5,19 +5,15 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/proto v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/trace/log v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/tinylib/msgp v1.6.3 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/text v0.2.0 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.uber.org/atomic v1.11.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/pkg/trace/traceutil/go.sum b/pkg/trace/traceutil/go.sum index 299406f3b1c6..1d73e1a35a46 100644 --- a/pkg/trace/traceutil/go.sum +++ b/pkg/trace/traceutil/go.sum @@ -1,26 +1,15 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/vmihailenco/msgpack/v4 v4.3.13 h1:A2wsiTbvp63ilDaWmsk2wjx6xZdxQOvpiNlKBGKKXKI= @@ -29,12 +18,9 @@ github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vb github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/aws/creds/go.mod b/pkg/util/aws/creds/go.mod index 3b463db7e9db..c92e0fbf1e3a 100644 --- a/pkg/util/aws/creds/go.mod +++ b/pkg/util/aws/creds/go.mod @@ -7,7 +7,7 @@ require ( github.com/DataDog/datadog-agent/pkg/config/utils v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/http v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) require ( @@ -34,7 +34,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -47,8 +46,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -58,13 +57,12 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.41.0 // indirect golang.org/x/time v0.15.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/aws/creds/go.sum b/pkg/util/aws/creds/go.sum index 748ed97c826e..1135efabd727 100644 --- a/pkg/util/aws/creds/go.sum +++ b/pkg/util/aws/creds/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -48,12 +46,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -68,8 +63,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -81,8 +76,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -99,8 +94,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/backoff/go.mod b/pkg/util/backoff/go.mod index 1c1e6f2d9cb2..7776d0eaa441 100644 --- a/pkg/util/backoff/go.mod +++ b/pkg/util/backoff/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/util/backoff go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/backoff/go.sum b/pkg/util/backoff/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/util/backoff/go.sum +++ b/pkg/util/backoff/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/buf/go.mod b/pkg/util/buf/go.mod index 5cab3e63590e..1e6716307c06 100644 --- a/pkg/util/buf/go.mod +++ b/pkg/util/buf/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/util/buf go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/buf/go.sum b/pkg/util/buf/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/util/buf/go.sum +++ b/pkg/util/buf/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/cache/go.mod b/pkg/util/cache/go.mod index 3155789fcc94..9cddcb460572 100644 --- a/pkg/util/cache/go.mod +++ b/pkg/util/cache/go.mod @@ -4,18 +4,10 @@ go 1.25.0 require ( github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/cache/go.sum b/pkg/util/cache/go.sum index 2336f9be0b90..e37d456af70d 100644 --- a/pkg/util/cache/go.sum +++ b/pkg/util/cache/go.sum @@ -1,25 +1,6 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/cgroups/go.mod b/pkg/util/cgroups/go.mod index b35c318642c7..331e6c3f70af 100644 --- a/pkg/util/cgroups/go.mod +++ b/pkg/util/cgroups/go.mod @@ -7,24 +7,20 @@ require ( github.com/DataDog/datadog-agent/pkg/util/pointer v0.78.3-rc.2 github.com/containerd/cgroups/v3 v3.1.2 github.com/google/go-cmp v0.7.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/coreos/go-systemd/v22 v22.7.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/go-units v0.5.0 // indirect github.com/godbus/dbus/v5 v5.2.2 // indirect github.com/moby/sys/userns v0.1.0 // indirect github.com/opencontainers/runtime-spec v1.3.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/time v0.15.0 // indirect google.golang.org/protobuf v1.36.11 // indirect diff --git a/pkg/util/cgroups/go.sum b/pkg/util/cgroups/go.sum index 7f8a572d53c7..b0ec80e65bb9 100644 --- a/pkg/util/cgroups/go.sum +++ b/pkg/util/cgroups/go.sum @@ -2,40 +2,25 @@ github.com/containerd/cgroups/v3 v3.1.2 h1:OSosXMtkhI6Qove637tg1XgK4q+DhR0mX8Wi8 github.com/containerd/cgroups/v3 v3.1.2/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw= github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/common/go.mod b/pkg/util/common/go.mod index 0927968a3150..494e1068743b 100644 --- a/pkg/util/common/go.mod +++ b/pkg/util/common/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/util/common go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/common/go.sum b/pkg/util/common/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/util/common/go.sum +++ b/pkg/util/common/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/compression/go.mod b/pkg/util/compression/go.mod index f33fc6f7f09e..d7c58ff4e425 100644 --- a/pkg/util/compression/go.mod +++ b/pkg/util/compression/go.mod @@ -9,8 +9,6 @@ require ( github.com/klauspost/compress v1.18.7 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/flare/builder v0.78.3-rc.2 // indirect @@ -45,7 +43,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -59,14 +56,13 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -76,7 +72,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/util/compression/go.sum b/pkg/util/compression/go.sum index f308d108da3e..89d87d840dd0 100644 --- a/pkg/util/compression/go.sum +++ b/pkg/util/compression/go.sum @@ -8,8 +8,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -55,12 +53,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= @@ -79,8 +74,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -102,8 +97,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -118,8 +114,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/containers/image/go.mod b/pkg/util/containers/image/go.mod index 04ef7e10da7e..109f7763baf5 100644 --- a/pkg/util/containers/image/go.mod +++ b/pkg/util/containers/image/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/util/containers/image go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/containers/image/go.sum b/pkg/util/containers/image/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/util/containers/image/go.sum +++ b/pkg/util/containers/image/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/defaultpaths/go.mod b/pkg/util/defaultpaths/go.mod index 50704c7ed62c..f246d39707de 100644 --- a/pkg/util/defaultpaths/go.mod +++ b/pkg/util/defaultpaths/go.mod @@ -46,6 +46,7 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -55,7 +56,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/time v0.15.0 // indirect diff --git a/pkg/util/defaultpaths/go.sum b/pkg/util/defaultpaths/go.sum index 0e81bdb38fc4..d2bdef1f9337 100644 --- a/pkg/util/defaultpaths/go.sum +++ b/pkg/util/defaultpaths/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -48,12 +46,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -68,8 +63,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -81,8 +76,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -97,8 +92,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/executable/go.mod b/pkg/util/executable/go.mod index b1db44c21d2d..be721aa27bee 100644 --- a/pkg/util/executable/go.mod +++ b/pkg/util/executable/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/util/executable go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/executable/go.sum b/pkg/util/executable/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/util/executable/go.sum +++ b/pkg/util/executable/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/filesystem/go.mod b/pkg/util/filesystem/go.mod index dda0ec93048a..b3ed583fb280 100644 --- a/pkg/util/filesystem/go.mod +++ b/pkg/util/filesystem/go.mod @@ -8,25 +8,21 @@ require ( github.com/gofrs/flock v0.13.0 github.com/hectane/go-acl v0.0.0-20230225031251-cdfc9e3acf94 github.com/shirou/gopsutil/v4 v4.26.3 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 golang.org/x/sync v0.22.0 golang.org/x/sys v0.47.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/pkg/util/filesystem/go.sum b/pkg/util/filesystem/go.sum index 5882701511ef..74e716990402 100644 --- a/pkg/util/filesystem/go.sum +++ b/pkg/util/filesystem/go.sum @@ -1,5 +1,3 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -9,26 +7,18 @@ github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= github.com/hectane/go-acl v0.0.0-20230225031251-cdfc9e3acf94 h1:0G5JVG0aJDpWvH1r8idd+wbPcAH14UqIuUMepx3v9rE= github.com/hectane/go-acl v0.0.0-20230225031251-cdfc9e3acf94/go.mod h1:MvF7bZQy5lD2WcLXjFytRVvnTrFvrjo/qGka1MIBOPE= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -38,8 +28,3 @@ golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/flavor/go.mod b/pkg/util/flavor/go.mod index 21a77160ef03..11089b9a859c 100644 --- a/pkg/util/flavor/go.mod +++ b/pkg/util/flavor/go.mod @@ -6,11 +6,9 @@ require ( github.com/DataDog/datadog-agent/pkg/config/mock v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/config/model v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/config/setup v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -35,7 +33,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -48,8 +45,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -59,7 +56,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/util/flavor/go.sum b/pkg/util/flavor/go.sum index 0e81bdb38fc4..d2bdef1f9337 100644 --- a/pkg/util/flavor/go.sum +++ b/pkg/util/flavor/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -48,12 +46,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -68,8 +63,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -81,8 +76,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -97,8 +92,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/fxutil/go.mod b/pkg/util/fxutil/go.mod index ff8545487b2c..390aa42a0faa 100644 --- a/pkg/util/fxutil/go.mod +++ b/pkg/util/fxutil/go.mod @@ -6,20 +6,17 @@ require ( github.com/DataDog/datadog-agent/comp/def v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/option v0.78.3-rc.2 github.com/spf13/cobra v1.10.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/fx v1.24.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/spf13/pflag v1.0.10 // indirect go.uber.org/dig v1.19.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect ) diff --git a/pkg/util/fxutil/go.sum b/pkg/util/fxutil/go.sum index 71ab2a7e5a00..5392314376b9 100644 --- a/pkg/util/fxutil/go.sum +++ b/pkg/util/fxutil/go.sum @@ -1,24 +1,14 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= @@ -29,12 +19,9 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/grpc/go.mod b/pkg/util/grpc/go.mod index 0afbdc39f811..ac11dfaa30cd 100644 --- a/pkg/util/grpc/go.mod +++ b/pkg/util/grpc/go.mod @@ -11,12 +11,10 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system v0.78.3-rc.2 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/mdlayher/vsock v1.2.1 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 google.golang.org/grpc v1.83.2 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( cloud.google.com/go/auth v0.18.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect @@ -49,7 +47,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect @@ -70,7 +67,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/philhofer/fwd v1.2.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -97,7 +93,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect diff --git a/pkg/util/grpc/go.sum b/pkg/util/grpc/go.sum index 848abe198e54..565ba91d88f5 100644 --- a/pkg/util/grpc/go.sum +++ b/pkg/util/grpc/go.sum @@ -25,8 +25,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -116,8 +114,6 @@ github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -153,8 +149,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -198,8 +194,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -290,13 +287,9 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/util/hostinfo/go.mod b/pkg/util/hostinfo/go.mod index a9513ee4c069..116259de75b6 100644 --- a/pkg/util/hostinfo/go.mod +++ b/pkg/util/hostinfo/go.mod @@ -10,28 +10,24 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.78.3-rc.2 github.com/shirou/gopsutil/v4 v4.26.3 github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 golang.org/x/sys v0.47.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/pkg/util/hostinfo/go.sum b/pkg/util/hostinfo/go.sum index de44aece6750..edc0021e0ce5 100644 --- a/pkg/util/hostinfo/go.sum +++ b/pkg/util/hostinfo/go.sum @@ -1,5 +1,3 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -7,26 +5,18 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -35,8 +25,8 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -44,8 +34,3 @@ golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/hostname/validate/go.mod b/pkg/util/hostname/validate/go.mod index 22a3f30db9ee..ff4a54f2fd0c 100644 --- a/pkg/util/hostname/validate/go.mod +++ b/pkg/util/hostname/validate/go.mod @@ -4,19 +4,15 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/pkg/util/hostname/validate/go.sum b/pkg/util/hostname/validate/go.sum index f75dbfe49ae9..befeb0397c22 100644 --- a/pkg/util/hostname/validate/go.sum +++ b/pkg/util/hostname/validate/go.sum @@ -1,23 +1,8 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/http/go.mod b/pkg/util/http/go.mod index 058c15da77ba..ac4c7f74396c 100644 --- a/pkg/util/http/go.mod +++ b/pkg/util/http/go.mod @@ -6,12 +6,10 @@ require ( github.com/DataDog/datadog-agent/pkg/config/mock v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/config/model v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 golang.org/x/net v0.58.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -36,7 +34,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -49,8 +46,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -60,7 +57,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.41.0 // indirect diff --git a/pkg/util/http/go.sum b/pkg/util/http/go.sum index 748ed97c826e..1135efabd727 100644 --- a/pkg/util/http/go.sum +++ b/pkg/util/http/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -48,12 +46,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -68,8 +63,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -81,8 +76,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -99,8 +94,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/json/go.mod b/pkg/util/json/go.mod index 10e5d6f92eef..1057e9ebab0f 100644 --- a/pkg/util/json/go.mod +++ b/pkg/util/json/go.mod @@ -4,19 +4,13 @@ go 1.25.0 require ( github.com/json-iterator/go v1.1.12 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/json/go.sum b/pkg/util/json/go.sum index 097f7a7fc33e..b84217e2f69d 100644 --- a/pkg/util/json/go.sum +++ b/pkg/util/json/go.sum @@ -1,37 +1,18 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/jsonquery/go.mod b/pkg/util/jsonquery/go.mod index 37f70341e682..2245a6e7e3f1 100644 --- a/pkg/util/jsonquery/go.mod +++ b/pkg/util/jsonquery/go.mod @@ -6,21 +6,18 @@ require ( github.com/DataDog/datadog-agent/pkg/util/cache v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/itchyny/gojq v0.12.17 - github.com/stretchr/testify v1.11.1 - go.yaml.in/yaml/v3 v3.0.4 + github.com/stretchr/testify v1.12.1 + go.yaml.in/yaml/v3 v3.0.5 ) require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/itchyny/timefmt-go v0.1.6 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.uber.org/atomic v1.11.0 // indirect golang.org/x/time v0.15.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/jsonquery/go.sum b/pkg/util/jsonquery/go.sum index d3936f0c9df9..4b35bd0dc7db 100644 --- a/pkg/util/jsonquery/go.sum +++ b/pkg/util/jsonquery/go.sum @@ -1,29 +1,14 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/itchyny/gojq v0.12.17 h1:8av8eGduDb5+rvEdaOO+zQUjA04MS0m3Ps8HiD+fceg= github.com/itchyny/gojq v0.12.17/go.mod h1:WBrEMkgAfAGO1LUcGOckBl5O726KPp+OlkKug0I/FEY= github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q= github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/kubernetes/apiserver/common/namespace/go.mod b/pkg/util/kubernetes/apiserver/common/namespace/go.mod index 50d682865d76..173c9f471faf 100644 --- a/pkg/util/kubernetes/apiserver/common/namespace/go.mod +++ b/pkg/util/kubernetes/apiserver/common/namespace/go.mod @@ -44,6 +44,7 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -53,7 +54,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/util/kubernetes/apiserver/common/namespace/go.sum b/pkg/util/kubernetes/apiserver/common/namespace/go.sum index 0e81bdb38fc4..d2bdef1f9337 100644 --- a/pkg/util/kubernetes/apiserver/common/namespace/go.sum +++ b/pkg/util/kubernetes/apiserver/common/namespace/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -48,12 +46,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -68,8 +63,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -81,8 +76,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -97,8 +92,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/log/go.mod b/pkg/util/log/go.mod index ffc3b7cdcf46..16d75b93ba23 100644 --- a/pkg/util/log/go.mod +++ b/pkg/util/log/go.mod @@ -5,20 +5,16 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 go.uber.org/zap v1.28.0 golang.org/x/time v0.15.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.uber.org/multierr v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/log/go.sum b/pkg/util/log/go.sum index fb7fb0d2d3c2..163e2e2e13d3 100644 --- a/pkg/util/log/go.sum +++ b/pkg/util/log/go.sum @@ -1,15 +1,5 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -18,12 +8,7 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/log/setup/go.mod b/pkg/util/log/setup/go.mod index 3015fb260224..89c104c7d611 100644 --- a/pkg/util/log/setup/go.mod +++ b/pkg/util/log/setup/go.mod @@ -7,11 +7,9 @@ require ( github.com/DataDog/datadog-agent/pkg/config/model v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/config/setup v0.78.3-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/delegatedauth v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/comp/core/secrets/def v0.78.3-rc.2 // indirect @@ -35,7 +33,6 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect github.com/DataDog/viper v1.15.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -48,8 +45,8 @@ require ( github.com/mdlayher/vsock v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -59,7 +56,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/pkg/util/log/setup/go.sum b/pkg/util/log/setup/go.sum index 0e81bdb38fc4..d2bdef1f9337 100644 --- a/pkg/util/log/setup/go.sum +++ b/pkg/util/log/setup/go.sum @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -48,12 +46,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -68,8 +63,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -81,8 +76,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -97,8 +92,4 @@ golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/option/go.mod b/pkg/util/option/go.mod index 85898809dc54..98049102c8e5 100644 --- a/pkg/util/option/go.mod +++ b/pkg/util/option/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/util/option go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/option/go.sum b/pkg/util/option/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/util/option/go.sum +++ b/pkg/util/option/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/otel/go.mod b/pkg/util/otel/go.mod index 97d139c847db..5bf47d76647e 100644 --- a/pkg/util/otel/go.mod +++ b/pkg/util/otel/go.mod @@ -4,7 +4,7 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) require ( @@ -17,14 +17,11 @@ require ( require ( github.com/hashicorp/go-version v1.9.0 // indirect go.opentelemetry.io/collector/featuregate v1.57.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( github.com/DataDog/datadog-agent/pkg/trace/log v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/trace/traceutil v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.opentelemetry.io/collector/component v1.57.0 // indirect go.opentelemetry.io/collector/pdata v1.57.0 // indirect go.opentelemetry.io/otel v1.45.0 // indirect @@ -33,6 +30,7 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/otel/go.sum b/pkg/util/otel/go.sum index 7b3e85381567..c9df74ffe51f 100644 --- a/pkg/util/otel/go.sum +++ b/pkg/util/otel/go.sum @@ -2,8 +2,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -17,10 +15,6 @@ github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaX github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -28,14 +22,10 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/collector/component v1.57.0 h1:WKIqx2Bs0JaAZxDEhsLradXpYxnwAxVFzWhQUmu2q3w= @@ -72,14 +62,9 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/prometheus/go.mod b/pkg/util/prometheus/go.mod index 19156ac0ac03..706acbe68017 100644 --- a/pkg/util/prometheus/go.mod +++ b/pkg/util/prometheus/go.mod @@ -5,11 +5,9 @@ go 1.26.0 require ( github.com/prometheus/common v0.67.5 github.com/prometheus/prometheus v0.311.3 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect github.com/aws/aws-sdk-go-v2/config v1.32.14 // indirect @@ -17,22 +15,19 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect github.com/aws/smithy-go v1.24.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/googleapis/gax-go/v2 v2.20.0 // indirect github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853 // indirect github.com/klauspost/compress v1.18.7 // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/procfs v0.20.1 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect go.opentelemetry.io/otel v1.45.0 // indirect go.opentelemetry.io/otel/metric v1.45.0 // indirect go.opentelemetry.io/otel/trace v1.45.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.56.0 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/text v0.41.0 // indirect diff --git a/pkg/util/prometheus/go.sum b/pkg/util/prometheus/go.sum index 8b471b3ce3f7..1fbbd5d3db74 100644 --- a/pkg/util/prometheus/go.sum +++ b/pkg/util/prometheus/go.sum @@ -46,9 +46,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= @@ -77,10 +74,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw= github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -89,9 +82,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_golang/exp v0.0.0-20260325093428-d8591d0db856 h1:1Y6bmpZb8peQCy1IpctnAhIFuyhrdtMaDnETChhSNns= @@ -108,11 +98,8 @@ github.com/prometheus/prometheus v0.311.3 h1:3IrVxQv6v5i/ZCGi6OrYeBhtCwaPTn6Z3DY github.com/prometheus/prometheus v0.311.3/go.mod h1:gjsCxTKtHO1Q8T9333u1s+lUR1OjPyM7ruuGH8RvVyo= github.com/prometheus/sigv4 v0.4.1 h1:EIc3j+8NBea9u1iV6O5ZAN8uvPq2xOIUPcqCTivHuXs= github.com/prometheus/sigv4 v0.4.1/go.mod h1:eu+ZbRvsc5TPiHwqh77OWuCnWK73IdkETYY46P4dXOU= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= @@ -131,6 +118,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -176,11 +165,6 @@ google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8= k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= k8s.io/client-go v0.35.3 h1:s1lZbpN4uI6IxeTM2cpdtrwHcSOBML1ODNTCCfsP1pg= diff --git a/pkg/util/quantile/go.mod b/pkg/util/quantile/go.mod index 2364f8520191..3f1a78e43b6e 100644 --- a/pkg/util/quantile/go.mod +++ b/pkg/util/quantile/go.mod @@ -6,15 +6,11 @@ require ( github.com/DataDog/datadog-agent/pkg/util/quantile/sketchtest v0.78.3-rc.2 github.com/DataDog/sketches-go v1.4.8 github.com/dustin/go-humanize v1.0.1 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/text v0.2.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/pkg/util/quantile/go.sum b/pkg/util/quantile/go.sum index 664bae799846..3d5f6996c105 100644 --- a/pkg/util/quantile/go.sum +++ b/pkg/util/quantile/go.sum @@ -1,28 +1,14 @@ github.com/DataDog/sketches-go v1.4.8 h1:pFk9BNn+Rzv8IMIoPUttoOpOr3bJOqU3P6EP5wK+Lv8= github.com/DataDog/sketches-go v1.4.8/go.mod h1:a/wjRUqzqtGS8qRHRPDCs4EAQfmvPDZGDlMIF5mxXOE= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/quantile/sketchtest/go.mod b/pkg/util/quantile/sketchtest/go.mod index 048b1355d572..ada3e8bd8939 100644 --- a/pkg/util/quantile/sketchtest/go.mod +++ b/pkg/util/quantile/sketchtest/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/util/quantile/sketchtest go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect retract v0.4.0 // see #107 diff --git a/pkg/util/quantile/sketchtest/go.sum b/pkg/util/quantile/sketchtest/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/util/quantile/sketchtest/go.sum +++ b/pkg/util/quantile/sketchtest/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/scrubber/go.mod b/pkg/util/scrubber/go.mod index 278ee164bfc2..4e5d1629cf17 100644 --- a/pkg/util/scrubber/go.mod +++ b/pkg/util/scrubber/go.mod @@ -4,15 +4,8 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 - github.com/stretchr/testify v1.11.1 - go.yaml.in/yaml/v3 v3.0.4 -) - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/text v0.2.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + github.com/stretchr/testify v1.12.1 + go.yaml.in/yaml/v3 v3.0.5 ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/scrubber/go.sum b/pkg/util/scrubber/go.sum index 06e9977bd0d5..c2336837ec7d 100644 --- a/pkg/util/scrubber/go.sum +++ b/pkg/util/scrubber/go.sum @@ -1,20 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/sort/go.mod b/pkg/util/sort/go.mod index 4e00e5bbe07e..d294be89d82c 100644 --- a/pkg/util/sort/go.mod +++ b/pkg/util/sort/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/util/sort go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/sort/go.sum b/pkg/util/sort/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/util/sort/go.sum +++ b/pkg/util/sort/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/startstop/go.mod b/pkg/util/startstop/go.mod index 33ca445cc5db..e445b677d2e4 100644 --- a/pkg/util/startstop/go.mod +++ b/pkg/util/startstop/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/util/startstop go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/startstop/go.sum b/pkg/util/startstop/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/util/startstop/go.sum +++ b/pkg/util/startstop/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/statstracker/go.mod b/pkg/util/statstracker/go.mod index 2711d752d23c..38fe5adea6b4 100644 --- a/pkg/util/statstracker/go.mod +++ b/pkg/util/statstracker/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/util/statstracker go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/statstracker/go.sum b/pkg/util/statstracker/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/util/statstracker/go.sum +++ b/pkg/util/statstracker/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/system/go.mod b/pkg/util/system/go.mod index 3c1f8f7ae71a..38d1854efdc1 100644 --- a/pkg/util/system/go.mod +++ b/pkg/util/system/go.mod @@ -11,30 +11,26 @@ require ( github.com/Microsoft/go-winio v0.6.2 github.com/mdlayher/vsock v1.2.1 github.com/shirou/gopsutil/v4 v4.26.3 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 golang.org/x/sys v0.47.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/gofrs/flock v0.13.0 // indirect github.com/hectane/go-acl v0.0.0-20230225031251-cdfc9e3acf94 // indirect github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect github.com/mdlayher/socket v0.5.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/time v0.15.0 // indirect diff --git a/pkg/util/system/go.sum b/pkg/util/system/go.sum index f062aff2fa19..7084128be478 100644 --- a/pkg/util/system/go.sum +++ b/pkg/util/system/go.sum @@ -1,7 +1,5 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -13,26 +11,18 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/hectane/go-acl v0.0.0-20230225031251-cdfc9e3acf94 h1:0G5JVG0aJDpWvH1r8idd+wbPcAH14UqIuUMepx3v9rE= github.com/hectane/go-acl v0.0.0-20230225031251-cdfc9e3acf94/go.mod h1:MvF7bZQy5lD2WcLXjFytRVvnTrFvrjo/qGka1MIBOPE= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -41,8 +31,8 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= @@ -54,8 +44,3 @@ golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/testutil/go.mod b/pkg/util/testutil/go.mod index f5829fe860a7..5bf0d6149eed 100644 --- a/pkg/util/testutil/go.mod +++ b/pkg/util/testutil/go.mod @@ -3,17 +3,8 @@ module github.com/DataDog/datadog-agent/pkg/util/testutil go 1.25.0 require ( - github.com/stretchr/testify v1.11.1 - go.yaml.in/yaml/v3 v3.0.4 -) - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + github.com/stretchr/testify v1.12.1 + go.yaml.in/yaml/v3 v3.0.5 ) // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/testutil/go.sum b/pkg/util/testutil/go.sum index 16c96a71d3db..c2336837ec7d 100644 --- a/pkg/util/testutil/go.sum +++ b/pkg/util/testutil/go.sum @@ -1,25 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/utilizationtracker/go.mod b/pkg/util/utilizationtracker/go.mod index 6f2022893741..6fac29cf3739 100644 --- a/pkg/util/utilizationtracker/go.mod +++ b/pkg/util/utilizationtracker/go.mod @@ -4,18 +4,10 @@ go 1.25.0 require ( github.com/benbjohnson/clock v1.3.5 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/util/utilizationtracker/go.sum b/pkg/util/utilizationtracker/go.sum index 079696b46573..09ad5bc2e48e 100644 --- a/pkg/util/utilizationtracker/go.sum +++ b/pkg/util/utilizationtracker/go.sum @@ -1,25 +1,6 @@ github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/pkg/util/uuid/go.mod b/pkg/util/uuid/go.mod index 975a9f98d273..63c018d4a012 100644 --- a/pkg/util/uuid/go.mod +++ b/pkg/util/uuid/go.mod @@ -22,7 +22,7 @@ require ( github.com/tklauser/numcpus v0.11.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/pkg/util/uuid/go.sum b/pkg/util/uuid/go.sum index 9e909ef03de5..8dea321fb55e 100644 --- a/pkg/util/uuid/go.sum +++ b/pkg/util/uuid/go.sum @@ -1,5 +1,3 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -7,24 +5,16 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= @@ -33,8 +23,8 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -42,8 +32,3 @@ golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/util/winutil/go.mod b/pkg/util/winutil/go.mod index c6c4139581cb..67131dd37589 100644 --- a/pkg/util/winutil/go.mod +++ b/pkg/util/winutil/go.mod @@ -5,21 +5,17 @@ go 1.25.0 require ( github.com/DataDog/datadog-agent/pkg/util/log v0.78.3-rc.2 github.com/fsnotify/fsnotify v1.9.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.uber.org/atomic v1.11.0 golang.org/x/sys v0.47.0 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/pkg/template v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.78.3-rc.2 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/stretchr/objx v0.5.3 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/pkg/util/winutil/go.sum b/pkg/util/winutil/go.sum index 3cd7730894d2..32629fec3b19 100644 --- a/pkg/util/winutil/go.sum +++ b/pkg/util/winutil/go.sum @@ -1,29 +1,14 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/version/go.mod b/pkg/version/go.mod index 4380fb106830..8005f963abd8 100644 --- a/pkg/version/go.mod +++ b/pkg/version/go.mod @@ -2,17 +2,9 @@ module github.com/DataDog/datadog-agent/pkg/version go 1.25.0 -require github.com/stretchr/testify v1.11.1 +require github.com/stretchr/testify v1.12.1 -require gopkg.in/yaml.v3 v3.0.1 // indirect - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect -) +require go.yaml.in/yaml/v3 v3.0.5 // indirect // This section was automatically added by 'dda inv modules.add-all-replace' command, do not edit manually diff --git a/pkg/version/go.sum b/pkg/version/go.sum index 5a10c3915835..c2336837ec7d 100644 --- a/pkg/version/go.sum +++ b/pkg/version/go.sum @@ -1,23 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/test/e2e-framework/go.mod b/test/e2e-framework/go.mod index 0e908da399fb..2817f0ac4a03 100644 --- a/test/e2e-framework/go.mod +++ b/test/e2e-framework/go.mod @@ -46,9 +46,9 @@ require ( github.com/pulumi/pulumi/sdk/v3 v3.190.0 github.com/pulumiverse/pulumi-time/sdk v0.1.0 github.com/samber/lo v1.52.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.yaml.in/yaml/v2 v2.4.4 - go.yaml.in/yaml/v3 v3.0.4 + go.yaml.in/yaml/v3 v3.0.5 golang.org/x/crypto v0.56.0 golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 gopkg.in/zorkian/go-datadog-api.v2 v2.30.0 diff --git a/test/e2e-framework/go.sum b/test/e2e-framework/go.sum index 777ee4242ac1..a40e3c078f5c 100644 --- a/test/e2e-framework/go.sum +++ b/test/e2e-framework/go.sum @@ -471,8 +471,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/texttheater/golang-levenshtein v1.0.1 h1:+cRNoVrfiwufQPhoMzB6N0Yf/Mqajr6t1lOv8GyGE2U= github.com/texttheater/golang-levenshtein v1.0.1/go.mod h1:PYAKrbF5sAiq9wd+H82hs7gNaen0CplQ9uvm6+enD/8= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= @@ -528,8 +528,9 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/test/fakeintake/go.mod b/test/fakeintake/go.mod index 4473ab67fbc1..13fd50fd6a8f 100644 --- a/test/fakeintake/go.mod +++ b/test/fakeintake/go.mod @@ -19,13 +19,11 @@ require ( github.com/prometheus/client_golang v1.23.2 github.com/samber/lo v1.52.0 github.com/spf13/cobra v1.10.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/tinylib/msgp v1.6.3 google.golang.org/protobuf v1.36.11 ) -require gopkg.in/yaml.v3 v3.0.1 // indirect - require ( github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.78.3-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/network/payload v0.78.3-rc.2 // indirect @@ -35,7 +33,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/clipperhouse/uax29/v2 v2.2.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.18.7 // indirect @@ -44,13 +41,13 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/pflag v1.0.10 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.41.0 // indirect ) diff --git a/test/fakeintake/go.sum b/test/fakeintake/go.sum index 880419121c4e..8e65d58afd44 100644 --- a/test/fakeintake/go.sum +++ b/test/fakeintake/go.sum @@ -18,8 +18,6 @@ github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdoh github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -54,8 +52,6 @@ github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -75,8 +71,8 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/vmihailenco/msgpack/v4 v4.3.13 h1:A2wsiTbvp63ilDaWmsk2wjx6xZdxQOvpiNlKBGKKXKI= @@ -90,6 +86,8 @@ go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -124,7 +122,3 @@ google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJ google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/test/new-e2e/go.mod b/test/new-e2e/go.mod index 0bad27616067..c76173c85f72 100644 --- a/test/new-e2e/go.mod +++ b/test/new-e2e/go.mod @@ -35,7 +35,7 @@ require ( github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.23.0 github.com/pulumi/pulumi/sdk/v3 v3.190.0 github.com/samber/lo v1.52.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/xeipuuv/gojsonschema v1.2.0 go.yaml.in/yaml/v2 v2.4.4 golang.org/x/crypto v0.56.0 @@ -185,7 +185,7 @@ require ( go.opentelemetry.io/otel/metric v1.45.0 // indirect go.opentelemetry.io/otel/trace v1.45.0 // indirect go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 + go.yaml.in/yaml/v3 v3.0.5 golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 golang.org/x/mod v0.40.0 golang.org/x/net v0.58.0 @@ -229,7 +229,6 @@ require ( github.com/go-viper/mapstructure/v2 v2.5.0 github.com/google/go-containerregistry v0.20.7 github.com/hairyhenderson/go-codeowners v0.7.0 - gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -354,6 +353,7 @@ require ( golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/ini.v1 v1.67.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect ) diff --git a/test/new-e2e/go.sum b/test/new-e2e/go.sum index 13967456e644..0515e8b94e32 100644 --- a/test/new-e2e/go.sum +++ b/test/new-e2e/go.sum @@ -600,8 +600,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/texttheater/golang-levenshtein v1.0.1 h1:+cRNoVrfiwufQPhoMzB6N0Yf/Mqajr6t1lOv8GyGE2U= github.com/texttheater/golang-levenshtein v1.0.1/go.mod h1:PYAKrbF5sAiq9wd+H82hs7gNaen0CplQ9uvm6+enD/8= github.com/theckman/httpforwarded v0.4.0 h1:N55vGJT+6ojTnLY3LQCNliJC4TW0P0Pkeys1G1WpX2w= @@ -724,8 +725,9 @@ go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/test/new-e2e/tests/installer/windows/suites/apm-inject-package/system_probe_config_test.go b/test/new-e2e/tests/installer/windows/suites/apm-inject-package/system_probe_config_test.go index 556635cc1927..c6a3686c3ff8 100644 --- a/test/new-e2e/tests/installer/windows/suites/apm-inject-package/system_probe_config_test.go +++ b/test/new-e2e/tests/installer/windows/suites/apm-inject-package/system_probe_config_test.go @@ -12,7 +12,7 @@ import ( "time" "github.com/cenkalti/backoff/v5" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" "github.com/DataDog/datadog-agent/test/e2e-framework/testing/e2e" winawshost "github.com/DataDog/datadog-agent/test/e2e-framework/testing/provisioners/aws/host/windows" diff --git a/test/otel/go.mod b/test/otel/go.mod index e7799e85ebe3..6fe6bf6db16d 100644 --- a/test/otel/go.mod +++ b/test/otel/go.mod @@ -17,7 +17,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/compression v0.78.3-rc.2 github.com/go-logr/logr v1.4.4 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog v0.150.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 go.opentelemetry.io/otel v1.45.0 go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.21.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0 @@ -89,7 +89,6 @@ require ( go.opentelemetry.io/collector/consumer/consumererror v0.150.0 // indirect go.opentelemetry.io/collector/pdata/pprofile v0.151.0 // indirect golang.org/x/sync v0.22.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( @@ -162,7 +161,6 @@ require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -197,7 +195,6 @@ require ( github.com/philhofer/fwd v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -240,7 +237,7 @@ require ( go.uber.org/fx v1.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sys v0.47.0 // indirect diff --git a/test/otel/go.sum b/test/otel/go.sum index 8d74c2ce9b3c..610b6c93339f 100644 --- a/test/otel/go.sum +++ b/test/otel/go.sum @@ -34,8 +34,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -174,8 +172,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -222,8 +218,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -385,8 +382,9 @@ go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -461,7 +459,6 @@ gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8= k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= diff --git a/tools/build-ddot-byoc/go.mod b/tools/build-ddot-byoc/go.mod index c77b30d0bc25..e08fa41248f4 100644 --- a/tools/build-ddot-byoc/go.mod +++ b/tools/build-ddot-byoc/go.mod @@ -10,16 +10,14 @@ require ( require ( cloud.google.com/go/compute/metadata v0.9.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.18.1 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/cli v29.2.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.4 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sirupsen/logrus v1.9.4 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/stretchr/testify v1.12.1 // indirect github.com/vbatts/tar-split v0.12.2 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.22.0 // indirect diff --git a/tools/build-ddot-byoc/go.sum b/tools/build-ddot-byoc/go.sum index 28c3ffd65c31..8de922ea9b71 100644 --- a/tools/build-ddot-byoc/go.sum +++ b/tools/build-ddot-byoc/go.sum @@ -2,8 +2,6 @@ cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdB cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= github.com/containerd/stargz-snapshotter/estargz v0.18.1 h1:cy2/lpgBXDA3cDKSyEfNOFMA/c10O1axL69EU7iirO8= github.com/containerd/stargz-snapshotter/estargz v0.18.1/go.mod h1:ALIEqa7B6oVDsrF37GkGN20SuvG/pIMm7FwP7ZmRb0Q= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/cli v29.2.1+incompatible h1:n3Jt0QVCN65eiVBoUTZQM9mcQICCJt3akW4pKAbKdJg= github.com/docker/cli v29.2.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= @@ -22,21 +20,19 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4= github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= From d3ff12d20a160ddcc73fb87fd9d60c9476e59b7b Mon Sep 17 00:00:00 2001 From: "SUSE Observability AI (POC)" Date: Thu, 1 Oct 2026 05:52:32 +0000 Subject: [PATCH 2/9] Normalize registered module metadata for consistency checks The repository check-mod-tidy workflow identifies 28 redundant indirect requirements for go-internal and 44 missing module checksum entries after workspace synchronization. Apply its normalized metadata without changing selected dependency versions or parser identities. Keep ignored and unregistered module fixtures untouched. Diff-aware lint passes with the pinned Go 1.26.6 toolchain. The complete module consistency check and native build provenance are qualified in the handoff result. Tracking: https://github.com/StackVista/stackstate/issues/717 --- comp/core/agenttelemetry/fx/go.mod | 1 - comp/core/agenttelemetry/fx/go.sum | 1 + comp/core/agenttelemetry/impl/go.mod | 1 - comp/core/agenttelemetry/impl/go.sum | 1 + comp/core/config/go.mod | 1 - comp/core/config/go.sum | 1 + comp/core/configsync/go.sum | 1 + comp/core/delegatedauth/api/cloudauth/aws/go.mod | 1 - comp/core/delegatedauth/api/cloudauth/aws/go.sum | 1 + comp/core/ipc/httphelpers/go.mod | 1 - comp/core/ipc/httphelpers/go.sum | 1 + comp/core/ipc/impl/go.sum | 1 + comp/core/ipc/mock/go.sum | 1 + comp/core/log/fx/go.mod | 1 - comp/core/log/fx/go.sum | 1 + comp/core/log/impl-trace/go.mod | 1 - comp/core/log/impl-trace/go.sum | 1 + comp/core/log/impl/go.mod | 1 - comp/core/log/impl/go.sum | 1 + comp/core/tagger/def/go.mod | 1 - comp/core/tagger/def/go.sum | 1 + comp/logs/agent/config/go.mod | 1 - comp/logs/agent/config/go.sum | 1 + comp/otelcol/converter/impl/go.mod | 1 - comp/otelcol/converter/impl/go.sum | 1 + comp/otelcol/logsagentpipeline/go.sum | 1 + comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod | 1 - comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum | 1 + comp/serializer/logscompression/go.sum | 1 + comp/serializer/metricscompression/go.sum | 1 + pkg/api/go.mod | 1 - pkg/api/go.sum | 1 + pkg/config/create/go.mod | 1 - pkg/config/create/go.sum | 1 + pkg/config/helper/go.mod | 1 - pkg/config/helper/go.sum | 1 + pkg/config/mock/go.mod | 1 - pkg/config/mock/go.sum | 1 + pkg/config/nodetreemodel/go.mod | 1 - pkg/config/nodetreemodel/go.sum | 1 + pkg/config/setup/go.mod | 1 - pkg/config/setup/go.sum | 1 + pkg/config/structure/go.mod | 1 - pkg/config/structure/go.sum | 1 + pkg/config/utils/go.mod | 1 - pkg/config/utils/go.sum | 1 + pkg/config/viperconfig/go.sum | 1 + pkg/logs/client/go.sum | 1 + pkg/logs/diagnostic/go.sum | 1 + pkg/logs/message/go.sum | 1 + pkg/logs/pipeline/go.sum | 1 + pkg/logs/processor/go.sum | 1 + pkg/logs/sender/go.sum | 1 + pkg/logs/sources/go.sum | 1 + pkg/logs/util/testutils/go.mod | 1 - pkg/logs/util/testutils/go.sum | 1 + pkg/metrics/go.sum | 1 + pkg/process/util/api/go.mod | 1 - pkg/process/util/api/go.sum | 1 + pkg/util/aws/creds/go.mod | 1 - pkg/util/aws/creds/go.sum | 1 + pkg/util/compression/go.sum | 1 + pkg/util/defaultpaths/go.mod | 1 - pkg/util/defaultpaths/go.sum | 1 + pkg/util/flavor/go.mod | 1 - pkg/util/flavor/go.sum | 1 + pkg/util/http/go.mod | 1 - pkg/util/http/go.sum | 1 + pkg/util/kubernetes/apiserver/common/namespace/go.mod | 1 - pkg/util/kubernetes/apiserver/common/namespace/go.sum | 1 + pkg/util/log/setup/go.mod | 1 - pkg/util/log/setup/go.sum | 1 + 72 files changed, 44 insertions(+), 28 deletions(-) diff --git a/comp/core/agenttelemetry/fx/go.mod b/comp/core/agenttelemetry/fx/go.mod index 122625dae3e1..36801d3d92ab 100644 --- a/comp/core/agenttelemetry/fx/go.mod +++ b/comp/core/agenttelemetry/fx/go.mod @@ -79,7 +79,6 @@ require ( github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect github.com/spf13/cast v1.10.0 // indirect diff --git a/comp/core/agenttelemetry/fx/go.sum b/comp/core/agenttelemetry/fx/go.sum index 65e30a5f544c..42190c50af13 100644 --- a/comp/core/agenttelemetry/fx/go.sum +++ b/comp/core/agenttelemetry/fx/go.sum @@ -76,6 +76,7 @@ github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4Ul github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/core/agenttelemetry/impl/go.mod b/comp/core/agenttelemetry/impl/go.mod index f0d8e3167cb8..d8e732d887c2 100644 --- a/comp/core/agenttelemetry/impl/go.mod +++ b/comp/core/agenttelemetry/impl/go.mod @@ -85,7 +85,6 @@ require ( github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect github.com/spf13/cast v1.10.0 // indirect diff --git a/comp/core/agenttelemetry/impl/go.sum b/comp/core/agenttelemetry/impl/go.sum index 720f4541d3a3..f69a1c76b0c5 100644 --- a/comp/core/agenttelemetry/impl/go.sum +++ b/comp/core/agenttelemetry/impl/go.sum @@ -80,6 +80,7 @@ github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4Ul github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/core/config/go.mod b/comp/core/config/go.mod index dbfb5426b7a4..9cd0dadf7ae0 100644 --- a/comp/core/config/go.mod +++ b/comp/core/config/go.mod @@ -56,7 +56,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect diff --git a/comp/core/config/go.sum b/comp/core/config/go.sum index 7bca182bee8d..6a9ab90c3ccc 100644 --- a/comp/core/config/go.sum +++ b/comp/core/config/go.sum @@ -52,6 +52,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/core/configsync/go.sum b/comp/core/configsync/go.sum index 6022a0058651..81138872bdfd 100644 --- a/comp/core/configsync/go.sum +++ b/comp/core/configsync/go.sum @@ -70,6 +70,7 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/core/delegatedauth/api/cloudauth/aws/go.mod b/comp/core/delegatedauth/api/cloudauth/aws/go.mod index aa86f92b7168..3121d0035d7b 100644 --- a/comp/core/delegatedauth/api/cloudauth/aws/go.mod +++ b/comp/core/delegatedauth/api/cloudauth/aws/go.mod @@ -50,7 +50,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/comp/core/delegatedauth/api/cloudauth/aws/go.sum b/comp/core/delegatedauth/api/cloudauth/aws/go.sum index 19fe24a8ad70..35ded2b9978a 100644 --- a/comp/core/delegatedauth/api/cloudauth/aws/go.sum +++ b/comp/core/delegatedauth/api/cloudauth/aws/go.sum @@ -53,6 +53,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= diff --git a/comp/core/ipc/httphelpers/go.mod b/comp/core/ipc/httphelpers/go.mod index 95eb532da783..ddc5010b17e6 100644 --- a/comp/core/ipc/httphelpers/go.mod +++ b/comp/core/ipc/httphelpers/go.mod @@ -60,7 +60,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect diff --git a/comp/core/ipc/httphelpers/go.sum b/comp/core/ipc/httphelpers/go.sum index 1a0e2908fc13..951d0dc21783 100644 --- a/comp/core/ipc/httphelpers/go.sum +++ b/comp/core/ipc/httphelpers/go.sum @@ -52,6 +52,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/core/ipc/impl/go.sum b/comp/core/ipc/impl/go.sum index 1a0e2908fc13..951d0dc21783 100644 --- a/comp/core/ipc/impl/go.sum +++ b/comp/core/ipc/impl/go.sum @@ -52,6 +52,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/core/ipc/mock/go.sum b/comp/core/ipc/mock/go.sum index 1a0e2908fc13..951d0dc21783 100644 --- a/comp/core/ipc/mock/go.sum +++ b/comp/core/ipc/mock/go.sum @@ -52,6 +52,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/core/log/fx/go.mod b/comp/core/log/fx/go.mod index 45e15592fdb8..bfc326ea034f 100644 --- a/comp/core/log/fx/go.mod +++ b/comp/core/log/fx/go.mod @@ -58,7 +58,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect diff --git a/comp/core/log/fx/go.sum b/comp/core/log/fx/go.sum index 7bca182bee8d..6a9ab90c3ccc 100644 --- a/comp/core/log/fx/go.sum +++ b/comp/core/log/fx/go.sum @@ -52,6 +52,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/core/log/impl-trace/go.mod b/comp/core/log/impl-trace/go.mod index 3b613ba8cbb3..bb2447775dd5 100644 --- a/comp/core/log/impl-trace/go.mod +++ b/comp/core/log/impl-trace/go.mod @@ -63,7 +63,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect diff --git a/comp/core/log/impl-trace/go.sum b/comp/core/log/impl-trace/go.sum index 7bca182bee8d..6a9ab90c3ccc 100644 --- a/comp/core/log/impl-trace/go.sum +++ b/comp/core/log/impl-trace/go.sum @@ -52,6 +52,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/core/log/impl/go.mod b/comp/core/log/impl/go.mod index 89784ac5ca27..67f2b9ae8c9d 100644 --- a/comp/core/log/impl/go.mod +++ b/comp/core/log/impl/go.mod @@ -58,7 +58,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect diff --git a/comp/core/log/impl/go.sum b/comp/core/log/impl/go.sum index 7bca182bee8d..6a9ab90c3ccc 100644 --- a/comp/core/log/impl/go.sum +++ b/comp/core/log/impl/go.sum @@ -52,6 +52,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/core/tagger/def/go.mod b/comp/core/tagger/def/go.mod index 39af85603ac3..7293222c7ab4 100644 --- a/comp/core/tagger/def/go.mod +++ b/comp/core/tagger/def/go.mod @@ -61,7 +61,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect diff --git a/comp/core/tagger/def/go.sum b/comp/core/tagger/def/go.sum index 9f0a9dd22205..635ce17170f1 100644 --- a/comp/core/tagger/def/go.sum +++ b/comp/core/tagger/def/go.sum @@ -52,6 +52,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/logs/agent/config/go.mod b/comp/logs/agent/config/go.mod index 0717a5ca0e55..3dc1a8d65240 100644 --- a/comp/logs/agent/config/go.mod +++ b/comp/logs/agent/config/go.mod @@ -60,7 +60,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect diff --git a/comp/logs/agent/config/go.sum b/comp/logs/agent/config/go.sum index 1a0e2908fc13..951d0dc21783 100644 --- a/comp/logs/agent/config/go.sum +++ b/comp/logs/agent/config/go.sum @@ -52,6 +52,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/otelcol/converter/impl/go.mod b/comp/otelcol/converter/impl/go.mod index e1c795b7a645..3342bf245e34 100644 --- a/comp/otelcol/converter/impl/go.mod +++ b/comp/otelcol/converter/impl/go.mod @@ -73,7 +73,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect diff --git a/comp/otelcol/converter/impl/go.sum b/comp/otelcol/converter/impl/go.sum index a7df6639fcd0..d5e6d1967b9f 100644 --- a/comp/otelcol/converter/impl/go.sum +++ b/comp/otelcol/converter/impl/go.sum @@ -66,6 +66,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/otelcol/logsagentpipeline/go.sum b/comp/otelcol/logsagentpipeline/go.sum index 195e8adf7c87..490cddc41eaf 100644 --- a/comp/otelcol/logsagentpipeline/go.sum +++ b/comp/otelcol/logsagentpipeline/go.sum @@ -80,6 +80,7 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod index a127bee34843..9825d44e5027 100644 --- a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod +++ b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod @@ -95,7 +95,6 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect diff --git a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum index 195e8adf7c87..490cddc41eaf 100644 --- a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum +++ b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum @@ -80,6 +80,7 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/serializer/logscompression/go.sum b/comp/serializer/logscompression/go.sum index 89d87d840dd0..bfaf0584d90e 100644 --- a/comp/serializer/logscompression/go.sum +++ b/comp/serializer/logscompression/go.sum @@ -56,6 +56,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/comp/serializer/metricscompression/go.sum b/comp/serializer/metricscompression/go.sum index 89d87d840dd0..bfaf0584d90e 100644 --- a/comp/serializer/metricscompression/go.sum +++ b/comp/serializer/metricscompression/go.sum @@ -56,6 +56,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/pkg/api/go.mod b/pkg/api/go.mod index c1cd083d1463..c314c74579c9 100644 --- a/pkg/api/go.mod +++ b/pkg/api/go.mod @@ -50,7 +50,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/pkg/api/go.sum b/pkg/api/go.sum index ebcc327c8324..8b74dc75dfbf 100644 --- a/pkg/api/go.sum +++ b/pkg/api/go.sum @@ -51,6 +51,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= diff --git a/pkg/config/create/go.mod b/pkg/config/create/go.mod index d0b2ae04ca8f..fd1ea146b094 100644 --- a/pkg/config/create/go.mod +++ b/pkg/config/create/go.mod @@ -24,7 +24,6 @@ require ( github.com/magiconair/properties v1.8.10 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect diff --git a/pkg/config/create/go.sum b/pkg/config/create/go.sum index 701e8e4c5772..a2ddeed706b1 100644 --- a/pkg/config/create/go.sum +++ b/pkg/config/create/go.sum @@ -30,6 +30,7 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= diff --git a/pkg/config/helper/go.mod b/pkg/config/helper/go.mod index 60b28bedc3be..eceb5c258334 100644 --- a/pkg/config/helper/go.mod +++ b/pkg/config/helper/go.mod @@ -20,7 +20,6 @@ require ( github.com/magiconair/properties v1.8.10 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect diff --git a/pkg/config/helper/go.sum b/pkg/config/helper/go.sum index 701e8e4c5772..a2ddeed706b1 100644 --- a/pkg/config/helper/go.sum +++ b/pkg/config/helper/go.sum @@ -30,6 +30,7 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= diff --git a/pkg/config/mock/go.mod b/pkg/config/mock/go.mod index d4c14ac9bdae..4d4ce0fe8c71 100644 --- a/pkg/config/mock/go.mod +++ b/pkg/config/mock/go.mod @@ -45,7 +45,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/pkg/config/mock/go.sum b/pkg/config/mock/go.sum index d2bdef1f9337..36f6fca801a0 100644 --- a/pkg/config/mock/go.sum +++ b/pkg/config/mock/go.sum @@ -49,6 +49,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= diff --git a/pkg/config/nodetreemodel/go.mod b/pkg/config/nodetreemodel/go.mod index 82a7eb2fd67e..7a0894481875 100644 --- a/pkg/config/nodetreemodel/go.mod +++ b/pkg/config/nodetreemodel/go.mod @@ -27,7 +27,6 @@ require ( github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect diff --git a/pkg/config/nodetreemodel/go.sum b/pkg/config/nodetreemodel/go.sum index b284b48a5d69..72ad134834a7 100644 --- a/pkg/config/nodetreemodel/go.sum +++ b/pkg/config/nodetreemodel/go.sum @@ -36,6 +36,7 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= diff --git a/pkg/config/setup/go.mod b/pkg/config/setup/go.mod index 9d3eeb427ec2..cb255c4d5aa4 100644 --- a/pkg/config/setup/go.mod +++ b/pkg/config/setup/go.mod @@ -51,7 +51,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/pkg/config/setup/go.sum b/pkg/config/setup/go.sum index fcdf7c5ba6d6..fa9d327ee0ea 100644 --- a/pkg/config/setup/go.sum +++ b/pkg/config/setup/go.sum @@ -55,6 +55,7 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= diff --git a/pkg/config/structure/go.mod b/pkg/config/structure/go.mod index 89ced7931451..23cde3462bd0 100644 --- a/pkg/config/structure/go.mod +++ b/pkg/config/structure/go.mod @@ -27,7 +27,6 @@ require ( github.com/magiconair/properties v1.8.10 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.10 // indirect diff --git a/pkg/config/structure/go.sum b/pkg/config/structure/go.sum index b284b48a5d69..72ad134834a7 100644 --- a/pkg/config/structure/go.sum +++ b/pkg/config/structure/go.sum @@ -36,6 +36,7 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= diff --git a/pkg/config/utils/go.mod b/pkg/config/utils/go.mod index 09ae4257bc5a..cf3737cae1d1 100644 --- a/pkg/config/utils/go.mod +++ b/pkg/config/utils/go.mod @@ -47,7 +47,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/pkg/config/utils/go.sum b/pkg/config/utils/go.sum index 1135efabd727..1854466698df 100644 --- a/pkg/config/utils/go.sum +++ b/pkg/config/utils/go.sum @@ -49,6 +49,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= diff --git a/pkg/config/viperconfig/go.sum b/pkg/config/viperconfig/go.sum index 701e8e4c5772..a2ddeed706b1 100644 --- a/pkg/config/viperconfig/go.sum +++ b/pkg/config/viperconfig/go.sum @@ -30,6 +30,7 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= diff --git a/pkg/logs/client/go.sum b/pkg/logs/client/go.sum index 1d1016780c40..b66e7af26a69 100644 --- a/pkg/logs/client/go.sum +++ b/pkg/logs/client/go.sum @@ -72,6 +72,7 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/pkg/logs/diagnostic/go.sum b/pkg/logs/diagnostic/go.sum index 1a0e2908fc13..951d0dc21783 100644 --- a/pkg/logs/diagnostic/go.sum +++ b/pkg/logs/diagnostic/go.sum @@ -52,6 +52,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/pkg/logs/message/go.sum b/pkg/logs/message/go.sum index 449f5123d5db..74c1064f9b92 100644 --- a/pkg/logs/message/go.sum +++ b/pkg/logs/message/go.sum @@ -51,6 +51,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= diff --git a/pkg/logs/pipeline/go.sum b/pkg/logs/pipeline/go.sum index 195e8adf7c87..490cddc41eaf 100644 --- a/pkg/logs/pipeline/go.sum +++ b/pkg/logs/pipeline/go.sum @@ -80,6 +80,7 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/pkg/logs/processor/go.sum b/pkg/logs/processor/go.sum index cd78fd299d28..b7d252c28f59 100644 --- a/pkg/logs/processor/go.sum +++ b/pkg/logs/processor/go.sum @@ -78,6 +78,7 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/pkg/logs/sender/go.sum b/pkg/logs/sender/go.sum index 91fc4586fade..30484aaf552b 100644 --- a/pkg/logs/sender/go.sum +++ b/pkg/logs/sender/go.sum @@ -74,6 +74,7 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/pkg/logs/sources/go.sum b/pkg/logs/sources/go.sum index 449f5123d5db..74c1064f9b92 100644 --- a/pkg/logs/sources/go.sum +++ b/pkg/logs/sources/go.sum @@ -51,6 +51,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= diff --git a/pkg/logs/util/testutils/go.mod b/pkg/logs/util/testutils/go.mod index c4263d9cc652..f579f1048d76 100644 --- a/pkg/logs/util/testutils/go.mod +++ b/pkg/logs/util/testutils/go.mod @@ -48,7 +48,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/pkg/logs/util/testutils/go.sum b/pkg/logs/util/testutils/go.sum index 449f5123d5db..74c1064f9b92 100644 --- a/pkg/logs/util/testutils/go.sum +++ b/pkg/logs/util/testutils/go.sum @@ -51,6 +51,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= diff --git a/pkg/metrics/go.sum b/pkg/metrics/go.sum index 24e0d675efcd..17f5731172ba 100644 --- a/pkg/metrics/go.sum +++ b/pkg/metrics/go.sum @@ -76,6 +76,7 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/pkg/process/util/api/go.mod b/pkg/process/util/api/go.mod index 27387631c20e..929af8fda0fd 100644 --- a/pkg/process/util/api/go.mod +++ b/pkg/process/util/api/go.mod @@ -65,7 +65,6 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect diff --git a/pkg/process/util/api/go.sum b/pkg/process/util/api/go.sum index 77f228eaf999..124eccee1971 100644 --- a/pkg/process/util/api/go.sum +++ b/pkg/process/util/api/go.sum @@ -82,6 +82,7 @@ github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxza github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/pkg/util/aws/creds/go.mod b/pkg/util/aws/creds/go.mod index c92e0fbf1e3a..3e6a72c16e4c 100644 --- a/pkg/util/aws/creds/go.mod +++ b/pkg/util/aws/creds/go.mod @@ -47,7 +47,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/pkg/util/aws/creds/go.sum b/pkg/util/aws/creds/go.sum index 1135efabd727..1854466698df 100644 --- a/pkg/util/aws/creds/go.sum +++ b/pkg/util/aws/creds/go.sum @@ -49,6 +49,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= diff --git a/pkg/util/compression/go.sum b/pkg/util/compression/go.sum index 89d87d840dd0..bfaf0584d90e 100644 --- a/pkg/util/compression/go.sum +++ b/pkg/util/compression/go.sum @@ -56,6 +56,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= diff --git a/pkg/util/defaultpaths/go.mod b/pkg/util/defaultpaths/go.mod index f246d39707de..ad07ec7207fd 100644 --- a/pkg/util/defaultpaths/go.mod +++ b/pkg/util/defaultpaths/go.mod @@ -46,7 +46,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/pkg/util/defaultpaths/go.sum b/pkg/util/defaultpaths/go.sum index d2bdef1f9337..36f6fca801a0 100644 --- a/pkg/util/defaultpaths/go.sum +++ b/pkg/util/defaultpaths/go.sum @@ -49,6 +49,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= diff --git a/pkg/util/flavor/go.mod b/pkg/util/flavor/go.mod index 11089b9a859c..49db4d5245b1 100644 --- a/pkg/util/flavor/go.mod +++ b/pkg/util/flavor/go.mod @@ -46,7 +46,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/pkg/util/flavor/go.sum b/pkg/util/flavor/go.sum index d2bdef1f9337..36f6fca801a0 100644 --- a/pkg/util/flavor/go.sum +++ b/pkg/util/flavor/go.sum @@ -49,6 +49,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= diff --git a/pkg/util/http/go.mod b/pkg/util/http/go.mod index ac4c7f74396c..dc851db01b96 100644 --- a/pkg/util/http/go.mod +++ b/pkg/util/http/go.mod @@ -47,7 +47,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/pkg/util/http/go.sum b/pkg/util/http/go.sum index 1135efabd727..1854466698df 100644 --- a/pkg/util/http/go.sum +++ b/pkg/util/http/go.sum @@ -49,6 +49,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= diff --git a/pkg/util/kubernetes/apiserver/common/namespace/go.mod b/pkg/util/kubernetes/apiserver/common/namespace/go.mod index 173c9f471faf..cbca62fc2a24 100644 --- a/pkg/util/kubernetes/apiserver/common/namespace/go.mod +++ b/pkg/util/kubernetes/apiserver/common/namespace/go.mod @@ -44,7 +44,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/pkg/util/kubernetes/apiserver/common/namespace/go.sum b/pkg/util/kubernetes/apiserver/common/namespace/go.sum index d2bdef1f9337..36f6fca801a0 100644 --- a/pkg/util/kubernetes/apiserver/common/namespace/go.sum +++ b/pkg/util/kubernetes/apiserver/common/namespace/go.sum @@ -49,6 +49,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= diff --git a/pkg/util/log/setup/go.mod b/pkg/util/log/setup/go.mod index 89c104c7d611..f20640f718c2 100644 --- a/pkg/util/log/setup/go.mod +++ b/pkg/util/log/setup/go.mod @@ -46,7 +46,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/pkg/util/log/setup/go.sum b/pkg/util/log/setup/go.sum index d2bdef1f9337..36f6fca801a0 100644 --- a/pkg/util/log/setup/go.sum +++ b/pkg/util/log/setup/go.sum @@ -49,6 +49,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= From 255584e34d8d8bd8558f1637bf00c3c8de0255f5 Mon Sep 17 00:00:00 2001 From: "SUSE Observability AI (POC)" Date: Thu, 1 Oct 2026 10:46:55 +0000 Subject: [PATCH 3/9] Retain production owners as independently addressable YAML backports Compatible releases still select legacy YAML: goflow2 v1.3.8 remains on legacy v3, ordered-map latest is v2.1.8, go-cfclient's tagged v2.0.0 is older than the selected pseudo-version, and Datadog lightweight OPA is still d2e1e78e0816. Retain the selected exact sources, licenses and tests; change only matching parser imports and required test module metadata. Root and workspace explicitly select all four modules. The independent network-device profile module selects ordered-map too. No old parser module replacement is added. Preserve Datadog OPA lightweight patches. This signed checkpoint preserves source before long upstream and native consumer validation. Qualification is pending and will follow separately. Tracking: https://github.com/StackVista/stackstate/issues/717 --- go.mod | 10 +- go.work | 4 + pkg/networkdevice/profile/go.mod | 2 + third_party/README.md | 32 +- third_party/go-cfclient/.gitignore | 30 + third_party/go-cfclient/LICENSE | 21 + third_party/go-cfclient/Makefile | 42 + third_party/go-cfclient/PROVENANCE.md | 18 + third_party/go-cfclient/README.md | 114 + third_party/go-cfclient/app_update.go | 128 + third_party/go-cfclient/app_update_test.go | 43 + third_party/go-cfclient/app_usage_events.go | 80 + .../go-cfclient/app_usage_events_test.go | 53 + third_party/go-cfclient/appevents.go | 174 + third_party/go-cfclient/appevents_test.go | 84 + third_party/go-cfclient/apps.go | 795 + third_party/go-cfclient/apps_test.go | 639 + third_party/go-cfclient/buildpacks.go | 264 + third_party/go-cfclient/buildpacks_test.go | 375 + third_party/go-cfclient/cf_error.go | 6119 ++++ third_party/go-cfclient/cf_error_test.go | 37 + third_party/go-cfclient/cf_test.go | 224 + third_party/go-cfclient/client.go | 551 + third_party/go-cfclient/client_test.go | 241 + third_party/go-cfclient/domains.go | 314 + third_party/go-cfclient/domains_test.go | 278 + .../go-cfclient/environmentvariablegroups.go | 64 + .../environmentvariablegroups_test.go | 83 + third_party/go-cfclient/error.go | 54 + third_party/go-cfclient/events.go | 95 + third_party/go-cfclient/events_test.go | 57 + third_party/go-cfclient/gen_error.go | 151 + third_party/go-cfclient/go.mod | 19 + third_party/go-cfclient/go.sum | 55 + third_party/go-cfclient/info.go | 104 + third_party/go-cfclient/info_test.go | 249 + third_party/go-cfclient/isolationsegments.go | 263 + .../go-cfclient/isolationsegments_test.go | 184 + third_party/go-cfclient/metadata.go | 165 + third_party/go-cfclient/metadata_test.go | 189 + third_party/go-cfclient/org_quotas.go | 187 + third_party/go-cfclient/org_quotas_test.go | 148 + third_party/go-cfclient/orgs.go | 847 + third_party/go-cfclient/orgs_test.go | 1004 + third_party/go-cfclient/payloads_test.go | 8481 ++++++ third_party/go-cfclient/processes.go | 102 + third_party/go-cfclient/processes_test.go | 28 + third_party/go-cfclient/resource_match.go | 45 + .../go-cfclient/resource_match_test.go | 40 + third_party/go-cfclient/route_mappings.go | 162 + .../go-cfclient/route_mappings_test.go | 90 + third_party/go-cfclient/routes.go | 209 + third_party/go-cfclient/routes_test.go | 200 + third_party/go-cfclient/secgroups.go | 576 + third_party/go-cfclient/secgroups_test.go | 256 + third_party/go-cfclient/service_bindings.go | 181 + .../go-cfclient/service_bindings_test.go | 158 + third_party/go-cfclient/service_brokers.go | 208 + .../go-cfclient/service_brokers_test.go | 31 + third_party/go-cfclient/service_instances.go | 234 + .../go-cfclient/service_instances_test.go | 214 + third_party/go-cfclient/service_keys.go | 204 + third_party/go-cfclient/service_keys_test.go | 287 + .../go-cfclient/service_plan_visibilities.go | 174 + .../service_plan_visibilities_test.go | 52 + third_party/go-cfclient/service_plans.go | 131 + third_party/go-cfclient/service_plans_test.go | 111 + .../go-cfclient/service_usage_events.go | 72 + .../go-cfclient/service_usage_events_test.go | 54 + third_party/go-cfclient/services.go | 128 + third_party/go-cfclient/services_test.go | 66 + third_party/go-cfclient/space_quotas.go | 187 + third_party/go-cfclient/space_quotas_test.go | 134 + third_party/go-cfclient/spaces.go | 829 + third_party/go-cfclient/spaces_test.go | 786 + third_party/go-cfclient/stacks.go | 103 + third_party/go-cfclient/stacks_test.go | 52 + third_party/go-cfclient/stats.go | 59 + third_party/go-cfclient/stats_test.go | 27 + third_party/go-cfclient/tasks.go | 211 + third_party/go-cfclient/tasks_test.go | 292 + third_party/go-cfclient/tools.go | 5 + third_party/go-cfclient/types.go | 8 + .../user_provided_service_instances.go | 189 + .../user_provided_service_instances_test.go | 135 + third_party/go-cfclient/users.go | 205 + third_party/go-cfclient/users_test.go | 272 + third_party/go-cfclient/v3apps.go | 285 + third_party/go-cfclient/v3apps_test.go | 205 + third_party/go-cfclient/v3build.go | 77 + third_party/go-cfclient/v3build_test.go | 29 + third_party/go-cfclient/v3deployments.go | 132 + third_party/go-cfclient/v3deployments_test.go | 132 + third_party/go-cfclient/v3domains.go | 68 + third_party/go-cfclient/v3domains_test.go | 35 + third_party/go-cfclient/v3droplet.go | 106 + third_party/go-cfclient/v3droplet_test.go | 62 + third_party/go-cfclient/v3organizations.go | 178 + .../go-cfclient/v3organizations_test.go | 117 + third_party/go-cfclient/v3packages.go | 192 + third_party/go-cfclient/v3packages_test.go | 100 + third_party/go-cfclient/v3roles.go | 273 + third_party/go-cfclient/v3roles_test.go | 255 + third_party/go-cfclient/v3routes.go | 133 + third_party/go-cfclient/v3routes_test.go | 61 + third_party/go-cfclient/v3security_groups.go | 195 + .../go-cfclient/v3security_groups_test.go | 417 + .../v3service_credential_bindings.go | 97 + .../v3service_credential_bindings_test.go | 51 + .../go-cfclient/v3service_instances.go | 69 + .../go-cfclient/v3service_instances_test.go | 28 + third_party/go-cfclient/v3spaces.go | 228 + third_party/go-cfclient/v3spaces_test.go | 143 + third_party/go-cfclient/v3stacks.go | 66 + third_party/go-cfclient/v3stacks_test.go | 35 + third_party/go-cfclient/v3types.go | 36 + third_party/go-cfclient/v3users.go | 67 + third_party/go-cfclient/v3users_test.go | 34 + .../go-ordered-map/.circleci/circle_build.sh | 13 + .../go-ordered-map/.circleci/config.yml | 23 + third_party/go-ordered-map/.gitignore | 1 + third_party/go-ordered-map/.golangci.yml | 80 + third_party/go-ordered-map/CHANGELOG.md | 38 + third_party/go-ordered-map/LICENSE | 201 + third_party/go-ordered-map/Makefile | 32 + third_party/go-ordered-map/PROVENANCE.md | 18 + third_party/go-ordered-map/README.md | 154 + third_party/go-ordered-map/example_test.go | 67 + third_party/go-ordered-map/go.mod | 16 + third_party/go-ordered-map/go.sum | 24 + third_party/go-ordered-map/json.go | 182 + third_party/go-ordered-map/json_fuzz_test.go | 117 + third_party/go-ordered-map/json_test.go | 338 + third_party/go-ordered-map/orderedmap.go | 296 + third_party/go-ordered-map/orderedmap_test.go | 384 + ...0799dfc1a6893e68418238a831ee79cd9c39b4cfc6 | 2 + ...957e75ff26c7fae64672dae0c0bc0a9fa5b61a05e7 | 2 + third_party/go-ordered-map/utils_test.go | 76 + third_party/go-ordered-map/yaml.go | 71 + third_party/go-ordered-map/yaml_fuzz_test.go | 81 + third_party/go-ordered-map/yaml_test.go | 333 + third_party/goflow2/.gitignore | 1 + third_party/goflow2/Dockerfile | 36 + third_party/goflow2/LICENSE | 29 + third_party/goflow2/Makefile | 128 + third_party/goflow2/PROVENANCE.md | 18 + third_party/goflow2/README.md | 253 + third_party/goflow2/cmd/enricher/main.go | 197 + .../goflow2/cmd/enricher/pb/flowext.pb.go | 830 + .../goflow2/cmd/enricher/pb/flowext.proto | 115 + third_party/goflow2/cmd/goflow2/main.go | 199 + third_party/goflow2/cmd/goflow2/mapping.yaml | 31 + third_party/goflow2/compose/elk/README.md | 25 + .../goflow2/compose/elk/docker-compose.yml | 55 + third_party/goflow2/compose/elk/logstash.conf | 22 + third_party/goflow2/compose/kcg/README.md | 28 + .../goflow2/compose/kcg/clickhouse/create.sh | 125 + .../compose/kcg/clickhouse/protocols.csv | 30 + .../goflow2/compose/kcg/docker-compose.yml | 71 + .../goflow2/compose/kcg/grafana/Dockerfile | 8 + .../compose/kcg/grafana/dashboards.yml | 6 + .../compose/kcg/grafana/dashboards/perfs.json | 2106 ++ .../kcg/grafana/dashboards/viz-ch.json | 643 + .../compose/kcg/grafana/datasources-ch.yml | 26 + .../compose/kcg/prometheus/prometheus.yml | 14 + third_party/goflow2/decoders/decoder.go | 115 + third_party/goflow2/decoders/netflow/ipfix.go | 989 + .../goflow2/decoders/netflow/netflow.go | 534 + .../goflow2/decoders/netflow/netflow_test.go | 416 + third_party/goflow2/decoders/netflow/nfv9.go | 317 + .../goflow2/decoders/netflow/packet.go | 158 + .../decoders/netflow/templates/file/file.go | 204 + .../netflow/templates/memory/memory.go | 73 + .../decoders/netflow/templates/templates.go | 139 + .../goflow2/decoders/netflowlegacy/netflow.go | 59 + .../decoders/netflowlegacy/netflow_test.go | 41 + .../goflow2/decoders/netflowlegacy/packet.go | 96 + .../goflow2/decoders/sflow/datastructure.go | 103 + third_party/goflow2/decoders/sflow/packet.go | 73 + third_party/goflow2/decoders/sflow/sflow.go | 417 + .../goflow2/decoders/sflow/sflow_test.go | 134 + third_party/goflow2/decoders/utils/utils.go | 16 + third_party/goflow2/docs/agents.md | 51 + third_party/goflow2/docs/contributors.md | 13 + third_party/goflow2/docs/logs.md | 2 + third_party/goflow2/docs/protobuf.md | 34 + third_party/goflow2/docs/protocols.md | 111 + third_party/goflow2/format/common/hash.go | 56 + third_party/goflow2/format/common/selector.go | 36 + third_party/goflow2/format/common/text.go | 246 + third_party/goflow2/format/format.go | 66 + third_party/goflow2/format/json/json.go | 41 + .../goflow2/format/protobuf/protobuf.go | 46 + third_party/goflow2/format/text/text.go | 41 + third_party/goflow2/go.mod | 52 + third_party/goflow2/go.sum | 147 + third_party/goflow2/graphics/diagram.png | Bin 0 -> 32488 bytes third_party/goflow2/package/goflow2.env | 1 + third_party/goflow2/package/goflow2.service | 12 + third_party/goflow2/pb/flow.pb.go | 924 + third_party/goflow2/pb/flow.proto | 131 + third_party/goflow2/producer/producer_nf.go | 682 + .../goflow2/producer/producer_nflegacy.go | 81 + third_party/goflow2/producer/producer_sf.go | 349 + third_party/goflow2/producer/producer_test.go | 191 + third_party/goflow2/producer/reflect.go | 233 + .../goflow2/transport/file/transport.go | 101 + third_party/goflow2/transport/kafka/kafka.go | 226 + .../goflow2/transport/kafka/scram_client.go | 39 + third_party/goflow2/transport/transport.go | 68 + third_party/goflow2/utils/metrics.go | 171 + third_party/goflow2/utils/netflow.go | 377 + third_party/goflow2/utils/nflegacy.go | 111 + third_party/goflow2/utils/sflow.go | 170 + third_party/goflow2/utils/sflow_test.go | 92 + third_party/goflow2/utils/stopper.go | 33 + third_party/goflow2/utils/stopper_test.go | 51 + third_party/goflow2/utils/utils.go | 234 + third_party/goflow2/utils/utils_test.go | 93 + third_party/opa/.gitignore | 43 + third_party/opa/.go-version | 1 + third_party/opa/.golangci.yaml | 215 + third_party/opa/.regal/config.yaml | 24 + third_party/opa/.trivyignore | 35 + third_party/opa/.yamllint.yaml | 12 + third_party/opa/ADOPTERS.md | 311 + third_party/opa/CHANGELOG.md | 7533 +++++ third_party/opa/CODE_OF_CONDUCT.md | 3 + third_party/opa/COMMUNITY_GUIDELINES.md | 63 + third_party/opa/CONTRIBUTING.md | 6 + third_party/opa/Dockerfile | 28 + third_party/opa/GOVERNANCE.md | 63 + third_party/opa/LICENSE | 202 + third_party/opa/MAINTAINERS.md | 26 + third_party/opa/Makefile | 565 + third_party/opa/PROVENANCE.md | 18 + third_party/opa/README.md | 104 + third_party/opa/SECURITY.md | 5 + third_party/opa/SECURITY_AUDIT.pdf | Bin 0 -> 283922 bytes third_party/opa/ast/annotations.go | 37 + third_party/opa/ast/builtins.go | 634 + third_party/opa/ast/capabilities.go | 58 + third_party/opa/ast/check.go | 22 + third_party/opa/ast/compare.go | 39 + third_party/opa/ast/compile.go | 127 + third_party/opa/ast/compile_test.go | 97 + third_party/opa/ast/compilehelper.go | 48 + third_party/opa/ast/compilehelper_test.go | 222 + third_party/opa/ast/conflicts.go | 15 + third_party/opa/ast/doc.go | 8 + third_party/opa/ast/env.go | 12 + third_party/opa/ast/errors.go | 46 + third_party/opa/ast/index.go | 20 + third_party/opa/ast/interning.go | 24 + third_party/opa/ast/json/doc.go | 8 + third_party/opa/ast/json/json.go | 15 + third_party/opa/ast/location/doc.go | 8 + third_party/opa/ast/location/location.go | 14 + third_party/opa/ast/map.go | 18 + third_party/opa/ast/parser.go | 49 + third_party/opa/ast/parser_ext.go | 311 + third_party/opa/ast/parser_ext_test.go | 127 + third_party/opa/ast/parser_test.go | 52 + third_party/opa/ast/policy.go | 235 + third_party/opa/ast/policy_test.go | 85 + third_party/opa/ast/pretty.go | 18 + third_party/opa/ast/schema.go | 17 + third_party/opa/ast/strings.go | 14 + third_party/opa/ast/term.go | 306 + third_party/opa/ast/transform.go | 46 + third_party/opa/ast/unify.go | 14 + third_party/opa/ast/varset.go | 17 + third_party/opa/ast/visit.go | 123 + third_party/opa/builtin_metadata.json | 25398 ++++++++++++++++ third_party/opa/bundle/bundle.go | 134 + third_party/opa/bundle/bundle_test.go | 84 + third_party/opa/bundle/doc.go | 8 + third_party/opa/bundle/file.go | 50 + third_party/opa/bundle/filefs.go | 22 + third_party/opa/bundle/hash.go | 32 + third_party/opa/bundle/keys.go | 30 + third_party/opa/bundle/sign.go | 35 + third_party/opa/bundle/store.go | 152 + third_party/opa/bundle/store_test.go | 374 + third_party/opa/bundle/verify.go | 36 + third_party/opa/capabilities.json | 4850 +++ third_party/opa/capabilities/capabilities.go | 19 + third_party/opa/capabilities/doc.go | 8 + third_party/opa/capabilities/v0.17.0.json | 2392 ++ third_party/opa/capabilities/v0.17.1.json | 2392 ++ third_party/opa/capabilities/v0.17.2.json | 2525 ++ third_party/opa/capabilities/v0.17.3.json | 2525 ++ third_party/opa/capabilities/v0.18.0.json | 2685 ++ third_party/opa/capabilities/v0.19.0-rc1.json | 2835 ++ third_party/opa/capabilities/v0.19.0.json | 2858 ++ third_party/opa/capabilities/v0.19.1.json | 2858 ++ third_party/opa/capabilities/v0.19.2.json | 2858 ++ third_party/opa/capabilities/v0.20.0.json | 3064 ++ third_party/opa/capabilities/v0.20.1.json | 3064 ++ third_party/opa/capabilities/v0.20.2.json | 3064 ++ third_party/opa/capabilities/v0.20.3.json | 3064 ++ third_party/opa/capabilities/v0.20.4.json | 3064 ++ third_party/opa/capabilities/v0.20.5.json | 3064 ++ third_party/opa/capabilities/v0.21.0.json | 3086 ++ third_party/opa/capabilities/v0.21.1.json | 3086 ++ third_party/opa/capabilities/v0.22.0.json | 3137 ++ third_party/opa/capabilities/v0.23.0.json | 3168 ++ third_party/opa/capabilities/v0.23.1.json | 3168 ++ third_party/opa/capabilities/v0.23.2.json | 3168 ++ third_party/opa/capabilities/v0.24.0.json | 3243 ++ third_party/opa/capabilities/v0.25.0-rc1.json | 3271 ++ third_party/opa/capabilities/v0.25.0-rc2.json | 3313 ++ third_party/opa/capabilities/v0.25.0-rc3.json | 3313 ++ third_party/opa/capabilities/v0.25.0-rc4.json | 3313 ++ third_party/opa/capabilities/v0.25.0.json | 3355 ++ third_party/opa/capabilities/v0.25.1.json | 3355 ++ third_party/opa/capabilities/v0.25.2.json | 3355 ++ third_party/opa/capabilities/v0.26.0.json | 3383 ++ third_party/opa/capabilities/v0.27.0.json | 3389 +++ third_party/opa/capabilities/v0.27.1.json | 3389 +++ third_party/opa/capabilities/v0.28.0.json | 3458 +++ third_party/opa/capabilities/v0.29.0.json | 3458 +++ third_party/opa/capabilities/v0.29.1.json | 3458 +++ third_party/opa/capabilities/v0.29.2.json | 3458 +++ third_party/opa/capabilities/v0.29.3.json | 3458 +++ third_party/opa/capabilities/v0.29.4.json | 3458 +++ third_party/opa/capabilities/v0.30.0.json | 3458 +++ third_party/opa/capabilities/v0.30.1.json | 3458 +++ third_party/opa/capabilities/v0.30.2.json | 3458 +++ third_party/opa/capabilities/v0.31.0.json | 3512 +++ third_party/opa/capabilities/v0.32.0.json | 3512 +++ third_party/opa/capabilities/v0.32.1.json | 3512 +++ third_party/opa/capabilities/v0.33.0.json | 3534 +++ third_party/opa/capabilities/v0.33.1.json | 3534 +++ third_party/opa/capabilities/v0.34.0.json | 3602 +++ third_party/opa/capabilities/v0.34.1.json | 3602 +++ third_party/opa/capabilities/v0.34.2.json | 3602 +++ third_party/opa/capabilities/v0.35.0.json | 3619 +++ third_party/opa/capabilities/v0.36.0.json | 3721 +++ third_party/opa/capabilities/v0.36.1.json | 3721 +++ third_party/opa/capabilities/v0.37.0.json | 3825 +++ third_party/opa/capabilities/v0.37.1.json | 3825 +++ third_party/opa/capabilities/v0.37.2.json | 3825 +++ third_party/opa/capabilities/v0.38.0.json | 3826 +++ third_party/opa/capabilities/v0.38.1.json | 3826 +++ third_party/opa/capabilities/v0.39.0.json | 3826 +++ third_party/opa/capabilities/v0.40.0.json | 3847 +++ third_party/opa/capabilities/v0.41.0.json | 4007 +++ third_party/opa/capabilities/v0.42.0.json | 4076 +++ third_party/opa/capabilities/v0.42.1.json | 4076 +++ third_party/opa/capabilities/v0.42.2.json | 4076 +++ third_party/opa/capabilities/v0.43.0.json | 4079 +++ third_party/opa/capabilities/v0.43.1.json | 4079 +++ third_party/opa/capabilities/v0.44.0.json | 4190 +++ third_party/opa/capabilities/v0.45.0.json | 4306 +++ third_party/opa/capabilities/v0.46.0.json | 4353 +++ third_party/opa/capabilities/v0.46.1.json | 4353 +++ third_party/opa/capabilities/v0.46.2.json | 4353 +++ third_party/opa/capabilities/v0.46.3.json | 4353 +++ third_party/opa/capabilities/v0.47.0.json | 4422 +++ third_party/opa/capabilities/v0.47.1.json | 4422 +++ third_party/opa/capabilities/v0.47.2.json | 4422 +++ third_party/opa/capabilities/v0.47.3.json | 4422 +++ third_party/opa/capabilities/v0.47.4.json | 4422 +++ third_party/opa/capabilities/v0.48.0.json | 4466 +++ third_party/opa/capabilities/v0.49.0.json | 4466 +++ third_party/opa/capabilities/v0.49.1.json | 4466 +++ third_party/opa/capabilities/v0.49.2.json | 4466 +++ third_party/opa/capabilities/v0.50.0.json | 4598 +++ third_party/opa/capabilities/v0.50.1.json | 4598 +++ third_party/opa/capabilities/v0.50.2.json | 4598 +++ third_party/opa/capabilities/v0.51.0.json | 4598 +++ third_party/opa/capabilities/v0.52.0.json | 4615 +++ third_party/opa/capabilities/v0.53.0.json | 4640 +++ third_party/opa/capabilities/v0.53.1.json | 4640 +++ third_party/opa/capabilities/v0.54.0.json | 4640 +++ third_party/opa/capabilities/v0.55.0.json | 4665 +++ third_party/opa/capabilities/v0.56.0.json | 4688 +++ third_party/opa/capabilities/v0.57.0.json | 4710 +++ third_party/opa/capabilities/v0.57.1.json | 4710 +++ third_party/opa/capabilities/v0.58.0.json | 4710 +++ third_party/opa/capabilities/v0.59.0.json | 4737 +++ third_party/opa/capabilities/v0.60.0.json | 4737 +++ third_party/opa/capabilities/v0.61.0.json | 4737 +++ third_party/opa/capabilities/v0.62.0.json | 4737 +++ third_party/opa/capabilities/v0.62.1.json | 4737 +++ third_party/opa/capabilities/v0.63.0.json | 4781 +++ third_party/opa/capabilities/v0.64.0.json | 4826 +++ third_party/opa/capabilities/v0.64.1.json | 4826 +++ third_party/opa/capabilities/v0.65.0.json | 4826 +++ third_party/opa/capabilities/v0.66.0.json | 4826 +++ third_party/opa/capabilities/v0.67.0.json | 4843 +++ third_party/opa/capabilities/v0.67.1.json | 4843 +++ third_party/opa/capabilities/v0.68.0.json | 4843 +++ third_party/opa/capabilities/v0.69.0.json | 4843 +++ third_party/opa/capabilities/v0.70.0.json | 4843 +++ third_party/opa/capabilities/v1.0.0.json | 4835 +++ third_party/opa/capabilities/v1.0.1.json | 4835 +++ third_party/opa/capabilities/v1.1.0.json | 4835 +++ third_party/opa/capabilities/v1.2.0.json | 4849 +++ third_party/opa/capabilities/v1.3.0.json | 4849 +++ third_party/opa/capabilities/v1.4.0.json | 4849 +++ third_party/opa/capabilities/v1.4.1.json | 4849 +++ third_party/opa/capabilities/v1.4.2.json | 4849 +++ third_party/opa/capabilities/v1.5.0.json | 4849 +++ third_party/opa/capabilities/v1.5.1.json | 4849 +++ third_party/opa/capabilities/v1.6.0.json | 4850 +++ third_party/opa/capabilities/v1.7.0.json | 4850 +++ third_party/opa/capabilities/v1.7.1.json | 4850 +++ third_party/opa/cmd/bench.go | 692 + third_party/opa/cmd/bench_test.go | 1568 + third_party/opa/cmd/build.go | 417 + third_party/opa/cmd/build_test.go | 3209 ++ third_party/opa/cmd/capabilities.go | 141 + third_party/opa/cmd/capabilities_test.go | 144 + third_party/opa/cmd/check.go | 220 + third_party/opa/cmd/check_test.go | 1339 + third_party/opa/cmd/commands.go | 52 + third_party/opa/cmd/deps.go | 175 + third_party/opa/cmd/deps_test.go | 573 + third_party/opa/cmd/doc.go | 5 + third_party/opa/cmd/eval.go | 896 + third_party/opa/cmd/eval_test.go | 3625 +++ third_party/opa/cmd/eval_wasmtarget_test.go | 59 + third_party/opa/cmd/exec.go | 280 + third_party/opa/cmd/exec_test.go | 1576 + third_party/opa/cmd/features.go | 10 + third_party/opa/cmd/filters.go | 39 + third_party/opa/cmd/flags.go | 255 + third_party/opa/cmd/fmt.go | 319 + third_party/opa/cmd/fmt_test.go | 1412 + third_party/opa/cmd/formats/formats.go | 28 + third_party/opa/cmd/inspect.go | 438 + third_party/opa/cmd/inspect_test.go | 2265 ++ third_party/opa/cmd/internal/env/env.go | 50 + third_party/opa/cmd/internal/env/env_test.go | 175 + third_party/opa/cmd/internal/exec/exec.go | 140 + .../opa/cmd/internal/exec/exec_test.go | 245 + .../opa/cmd/internal/exec/json_reporter.go | 86 + .../cmd/internal/exec/json_reporter_test.go | 164 + third_party/opa/cmd/internal/exec/params.go | 55 + .../opa/cmd/internal/exec/params_test.go | 57 + third_party/opa/cmd/internal/exec/parser.go | 23 + .../opa/cmd/internal/exec/parser_test.go | 66 + .../opa/cmd/internal/exec/std_in_reader.go | 25 + .../cmd/internal/exec/std_in_reader_test.go | 53 + third_party/opa/cmd/oracle.go | 209 + third_party/opa/cmd/oracle_test.go | 195 + third_party/opa/cmd/parse.go | 149 + third_party/opa/cmd/parse_test.go | 1200 + third_party/opa/cmd/refactor.go | 202 + third_party/opa/cmd/refactor_test.go | 270 + third_party/opa/cmd/run.go | 467 + third_party/opa/cmd/run_test.go | 464 + third_party/opa/cmd/sign.go | 293 + third_party/opa/cmd/sign_test.go | 184 + third_party/opa/cmd/test.go | 586 + third_party/opa/cmd/test_test.go | 3688 +++ third_party/opa/cmd/utils.go | 30 + third_party/opa/cmd/version.go | 85 + third_party/opa/cmd/version_test.go | 131 + third_party/opa/compile/compile.go | 37 + third_party/opa/compile/compile_test.go | 774 + third_party/opa/compile/doc.go | 8 + third_party/opa/config/config.go | 19 + third_party/opa/config/doc.go | 8 + third_party/opa/cover/cover.go | 37 + third_party/opa/cover/doc.go | 8 + third_party/opa/debug/breakpoint.go | 13 + third_party/opa/debug/debugger.go | 52 + third_party/opa/debug/doc.go | 8 + third_party/opa/debug/event.go | 23 + third_party/opa/debug/frame.go | 16 + third_party/opa/debug/thread.go | 17 + third_party/opa/debug/variable.go | 13 + third_party/opa/dependencies/deps.go | 42 + third_party/opa/dependencies/doc.go | 11 + third_party/opa/docs/.gitignore | 7 + third_party/opa/docs/Makefile | 27 + third_party/opa/docs/README.md | 6 + third_party/opa/docs/devel/DEVELOPMENT.md | 5 + third_party/opa/docs/devel/RELEASE.md | 232 + .../opa/docs/docs/assets/OverviewDiagram.jsx | 16 + third_party/opa/docs/docs/assets/logo.png | Bin 0 -> 38210 bytes .../opa/docs/docs/aws-cloudformation-hooks.md | 504 + third_party/opa/docs/docs/cicd.md | 94 + third_party/opa/docs/docs/cli.md | 22 + .../docs/docs/comparison-to-other-systems.md | 478 + third_party/opa/docs/docs/configuration.md | 1197 + .../docs/contrib-adding-builtin-functions.md | 187 + third_party/opa/docs/docs/contrib-code.md | 188 + .../opa/docs/docs/contrib-development.md | 197 + third_party/opa/docs/docs/contrib-docs.md | 154 + third_party/opa/docs/docs/contributing.md | 75 + .../opa/docs/docs/debugging/debugging-dap.gif | Bin 0 -> 823913 bytes third_party/opa/docs/docs/debugging/index.md | 134 + third_party/opa/docs/docs/deployments.md | 553 + .../opa/docs/docs/docker-authorization.md | 445 + .../opa/docs/docs/editor-and-ide-support.md | 35 + .../opa/docs/docs/envoy/_category_.yaml | 4 + third_party/opa/docs/docs/envoy/debugging.md | 84 + third_party/opa/docs/docs/envoy/index.md | 94 + .../opa/docs/docs/envoy/performance.md | 260 + third_party/opa/docs/docs/envoy/primer.md | 532 + .../opa/docs/docs/envoy/tutorial-gloo-edge.md | 339 + .../opa/docs/docs/envoy/tutorial-istio.md | 220 + .../docs/envoy/tutorial-standalone-envoy.md | 561 + third_party/opa/docs/docs/extensions.md | 538 + .../opa/docs/docs/external-data/index.md | 262 + third_party/opa/docs/docs/faq.md | 439 + .../docs/docs/graphql-api-authorization.md | 520 + .../opa/docs/docs/http-api-authorization.md | 373 + third_party/opa/docs/docs/index.md | 1281 + third_party/opa/docs/docs/integration.md | 484 + third_party/opa/docs/docs/ir.md | 461 + .../opa/docs/docs/kafka-authorization.md | 526 + .../opa/docs/docs/kubernetes/_category_.yaml | 4 + .../opa/docs/docs/kubernetes/debugging.md | 173 + third_party/opa/docs/docs/kubernetes/index.md | 291 + .../opa/docs/docs/kubernetes/primer.md | 563 + .../opa/docs/docs/kubernetes/tutorial.md | 564 + .../management-bundles/assets/thumbprint.png | Bin 0 -> 125695 bytes .../opa/docs/docs/management-bundles/index.md | 1470 + .../opa/docs/docs/management-decision-logs.md | 314 + .../opa/docs/docs/management-discovery.md | 314 + .../assets/ControlPlaneDiagram.jsx | 26 + .../assets/DistributedDiagram.jsx | 40 + .../assets/HostLocalDiagram.jsx | 36 + .../management-introduction/assets/logo.png | Bin 0 -> 38210 bytes .../docs/management-introduction/index.md | 42 + .../opa/docs/docs/management-status.md | 313 + third_party/opa/docs/docs/monitoring.md | 97 + third_party/opa/docs/docs/oauth-oidc.md | 109 + third_party/opa/docs/docs/philosophy/index.md | 209 + third_party/opa/docs/docs/policy-language.md | 3555 +++ .../opa/docs/docs/policy-performance.md | 980 + .../crypto/digest_verification/config.json | 7 + .../crypto/digest_verification/input.json | 8 + .../crypto/digest_verification/intro.md | 6 + .../crypto/digest_verification/policy.rego | 7 + .../crypto/digest_verification/title.txt | 2 + .../envoy_header_manipulation/config.json | 7 + .../envoy_header_manipulation/input.json | 12 + .../envoy_header_manipulation/intro.md | 6 + .../envoy_header_manipulation/policy.rego | 26 + .../envoy_header_manipulation/title.txt | 1 + .../_examples/graphs/reachable/config.json | 7 + .../_examples/graphs/reachable/intro.md | 4 + .../_examples/graphs/reachable/policy.rego | 21 + .../_examples/graphs/reachable/title.txt | 1 + .../graphs/reachable_paths/config.json | 7 + .../_examples/graphs/reachable_paths/intro.md | 1 + .../graphs/reachable_paths/policy.rego | 15 + .../graphs/reachable_paths/title.txt | 1 + .../cidr_contains_array_string/config.json | 7 + .../net/cidr_contains_array_string/intro.md | 1 + .../cidr_contains_array_string/policy.rego | 3 + .../net/cidr_contains_array_string/title.txt | 1 + .../net/cidr_contains_arrays/config.json | 7 + .../net/cidr_contains_arrays/intro.md | 1 + .../net/cidr_contains_arrays/policy.rego | 6 + .../net/cidr_contains_arrays/title.txt | 1 + .../net/cidr_contains_objects/config.json | 7 + .../net/cidr_contains_objects/intro.md | 1 + .../net/cidr_contains_objects/policy.rego | 6 + .../net/cidr_contains_objects/title.txt | 1 + .../net/cidr_contains_strings/config.json | 7 + .../net/cidr_contains_strings/intro.md | 1 + .../net/cidr_contains_strings/policy.rego | 3 + .../net/cidr_contains_strings/title.txt | 1 + .../_examples/rego/rule_metadata/config.json | 7 + .../_examples/rego/rule_metadata/input.json | 7 + .../_examples/rego/rule_metadata/intro.md | 4 + .../_examples/rego/rule_metadata/policy.rego | 21 + .../_examples/rego/rule_metadata/title.txt | 1 + .../_examples/semver/isvalid/config.json | 7 + .../_examples/semver/isvalid/intro.md | 8 + .../_examples/semver/isvalid/policy.rego | 5 + .../_examples/semver/isvalid/title.txt | 1 + .../_examples/time/time_format/config.json | 7 + .../_examples/time/time_format/intro.md | 1 + .../_examples/time/time_format/policy.rego | 4 + .../_examples/time/time_format/title.txt | 1 + .../tokens/sign/empty_json/config.json | 7 + .../tokens/sign/empty_json/policy.rego | 9 + .../tokens/sign/empty_json/title.txt | 1 + .../_examples/tokens/sign/hmac/config.json | 7 + .../_examples/tokens/sign/hmac/policy.rego | 18 + .../_examples/tokens/sign/hmac/title.txt | 1 + .../_examples/tokens/sign/rsa/config.json | 7 + .../_examples/tokens/sign/rsa/policy.rego | 25 + .../_examples/tokens/sign/rsa/title.txt | 1 + .../tokens/sign/sign_raw/config.json | 7 + .../_examples/tokens/sign/sign_raw/intro.md | 3 + .../tokens/sign/sign_raw/policy.rego | 7 + .../_examples/tokens/sign/sign_raw/title.txt | 1 + .../_examples/tokens/verify/cert/config.json | 8 + .../_examples/tokens/verify/cert/intro.md | 3 + .../_examples/tokens/verify/cert/policy.rego | 5 + .../_examples/tokens/verify/cert/title.txt | 1 + .../tokens/verify/cert_single/config.json | 8 + .../tokens/verify/cert_single/intro.md | 4 + .../tokens/verify/cert_single/policy.rego | 8 + .../tokens/verify/cert_single/title.txt | 1 + .../_examples/tokens/verify/jwks/config.json | 8 + .../_examples/tokens/verify/jwks/intro.md | 3 + .../_examples/tokens/verify/jwks/policy.rego | 5 + .../_examples/tokens/verify/jwks/title.txt | 1 + .../tokens/verify/jwks_single/config.json | 8 + .../tokens/verify/jwks_single/data.json | 1 + .../tokens/verify/jwks_single/input.json | 1 + .../tokens/verify/jwks_single/intro.md | 4 + .../tokens/verify/jwks_single/policy.rego | 8 + .../tokens/verify/jwks_single/title.txt | 1 + .../_examples/tokens/verify/sign/config.json | 7 + .../_examples/tokens/verify/sign/intro.md | 1 + .../_examples/tokens/verify/sign/policy.rego | 17 + .../_examples/tokens/verify/sign/title.txt | 1 + .../tokens/verify/sign_raw/config.json | 7 + .../_examples/tokens/verify/sign_raw/intro.md | 1 + .../tokens/verify/sign_raw/policy.rego | 11 + .../tokens/verify/sign_raw/title.txt | 1 + .../policy-reference/builtins/aggregates.mdx | 5 + .../docs/policy-reference/builtins/array.mdx | 4 + .../docs/policy-reference/builtins/bits.mdx | 4 + .../policy-reference/builtins/comparison.mdx | 4 + .../policy-reference/builtins/conversions.mdx | 4 + .../docs/policy-reference/builtins/crypto.mdx | 8 + .../policy-reference/builtins/encoding.mdx | 21 + .../docs/policy-reference/builtins/glob.mdx | 29 + .../docs/policy-reference/builtins/graph.mdx | 7 + .../policy-reference/builtins/graphql.mdx | 37 + .../docs/policy-reference/builtins/http.mdx | 115 + .../docs/policy-reference/builtins/index.mdx | 11 + .../docs/policy-reference/builtins/net.mdx | 36 + .../policy-reference/builtins/numbers.mdx | 4 + .../docs/policy-reference/builtins/object.mdx | 16 + .../docs/policy-reference/builtins/opa.mdx | 23 + .../builtins/providers.aws.mdx | 105 + .../docs/policy-reference/builtins/regex.mdx | 4 + .../docs/policy-reference/builtins/rego.mdx | 77 + .../docs/policy-reference/builtins/semver.mdx | 5 + .../docs/policy-reference/builtins/sets.mdx | 4 + .../policy-reference/builtins/strings.mdx | 9 + .../docs/policy-reference/builtins/time.mdx | 47 + .../docs/policy-reference/builtins/tokens.mdx | 102 + .../policy-reference/builtins/tokensign.mdx | 50 + .../policy-reference/builtins/tracing.mdx | 4 + .../docs/policy-reference/builtins/types.mdx | 4 + .../docs/policy-reference/builtins/units.mdx | 4 + .../docs/policy-reference/builtins/uuid.mdx | 4 + .../opa/docs/docs/policy-reference/index.md | 432 + third_party/opa/docs/docs/policy-testing.md | 715 + third_party/opa/docs/docs/privacy.md | 79 + third_party/opa/docs/docs/rest-api.md | 2216 ++ third_party/opa/docs/docs/security.md | 654 + .../docs/docs/ssh-and-sudo-authorization.md | 438 + third_party/opa/docs/docs/storage.md | 182 + third_party/opa/docs/docs/terraform.md | 914 + third_party/opa/docs/docs/v0-compatibility.md | 188 + third_party/opa/docs/docs/v0-upgrade/index.md | 555 + third_party/opa/docs/docs/wasm.md | 382 + third_party/opa/docs/docusaurus.config.js | 508 + third_party/opa/docs/functions/badge.ts | 56 + .../opa/docs/functions/version-redirect.ts | 37 + third_party/opa/docs/package.json | 27 + third_party/opa/docs/src/Archive.js | 164 + third_party/opa/docs/src/EcosystemEntry.js | 212 + third_party/opa/docs/src/EcosystemFeature.js | 78 + third_party/opa/docs/src/EcosystemLanguage.js | 75 + .../components/BuiltinLegacyRedirect/index.js | 16 + .../src/components/BuiltinSearch/index.js | 154 + .../BuiltinSearch/styles.module.css | 111 + .../docs/src/components/BuiltinTable/index.js | 195 + .../components/BuiltinTable/styles.module.css | 7 + .../opa/docs/src/components/Card/index.js | 28 + .../src/components/Card/styles.module.css | 50 + .../opa/docs/src/components/CardGrid/index.js | 11 + .../src/components/CardGrid/styles.module.css | 37 + .../docs/src/components/CommandDoc/index.js | 129 + .../components/CommandDoc/styles.module.css | 54 + .../docs/src/components/CommandList/index.js | 67 + .../components/CommandList/styles.module.css | 16 + .../src/components/EcosystemEmbed/index.js | 57 + .../components/EcosystemFeatureLink/index.js | 24 + .../components/EvergreenCodeBlock/index.js | 52 + .../docs/src/components/FeedbackForm/index.js | 322 + .../components/FeedbackForm/styles.module.css | 165 + .../docs/src/components/ImageCard/index.js | 45 + .../NavbarItems/CurrentVersionNavbarItem.js | 49 + .../components/NavbarItems/styles.module.css | 25 + .../src/components/PlaygroundExample/index.js | 203 + .../docs/src/components/RunSnippet/index.js | 69 + .../components/RunSnippet/styles.module.css | 39 + .../docs/src/components/SideBySide/Column.js | 9 + .../src/components/SideBySide/Container.js | 7 + .../components/SideBySide/styles.module.css | 36 + .../docs/src/components/SidebarTitle/index.js | 12 + .../src/components/StandaloneLayout/index.js | 27 + .../StandaloneLayout/styles.module.css | 20 + third_party/opa/docs/src/css/custom.css | 50 + third_party/opa/docs/src/data/cli.json | 1 + .../docs/src/data/ecosystem/entries/alfred.md | 22 + .../src/data/ecosystem/entries/alluxio.md | 11 + .../docs/src/data/ecosystem/entries/antlr.md | 11 + .../data/ecosystem/entries/apache-apisix.md | 21 + .../docs/src/data/ecosystem/entries/aserto.md | 24 + .../docs/src/data/ecosystem/entries/atmos.md | 25 + .../src/data/ecosystem/entries/awesome-opa.md | 20 + .../data/ecosystem/entries/aws-api-gateway.md | 9 + .../entries/aws-cloudformation-hook.md | 25 + .../src/data/ecosystem/entries/backstage.md | 15 + .../data/ecosystem/entries/boomerang-bosun.md | 20 + .../docs/src/data/ecosystem/entries/bottle.md | 23 + .../src/data/ecosystem/entries/carbonetes.md | 15 + .../docs/src/data/ecosystem/entries/ceph.md | 24 + .../data/ecosystem/entries/chef-automate.md | 25 + .../src/data/ecosystem/entries/circleci.md | 12 + .../ecosystem/entries/clair-datasource.md | 31 + .../src/data/ecosystem/entries/clojure.md | 16 + .../ecosystem/entries/cloudflare-worker.md | 23 + .../src/data/ecosystem/entries/conftest.md | 51 + .../data/ecosystem/entries/coredns-authz.md | 13 + .../docs/src/data/ecosystem/entries/cosign.md | 25 + ...stom-library-microservice-authorization.md | 17 + .../docs/src/data/ecosystem/entries/dapr.md | 21 + .../ecosystem/entries/dart-authorization.md | 19 + .../entries/dependency-management-data.md | 36 + .../docs/src/data/ecosystem/entries/digger.md | 18 + .../data/ecosystem/entries/docker-machine.md | 15 + .../src/data/ecosystem/entries/easegress.md | 13 + .../entries/elasticsearch-datafiltering.md | 15 + .../ecosystem/entries/emissary-ingress.md | 13 + .../ecosystem/entries/enterprise-contract.md | 22 + .../data/ecosystem/entries/enterprise-opa.md | 78 + .../docs/src/data/ecosystem/entries/env0.md | 40 + .../ecosystem/entries/envoy-authorization.md | 58 + .../data/ecosystem/entries/expressing-or.md | 19 + .../ecosystem/entries/fairwinds-insights.md | 31 + .../docs/src/data/ecosystem/entries/fiber.md | 15 + .../docs/src/data/ecosystem/entries/fig.md | 12 + .../src/data/ecosystem/entries/flask-opa.md | 13 + .../docs/src/data/ecosystem/entries/flipt.md | 27 + .../src/data/ecosystem/entries/gatekeeper.md | 31 + .../src/data/ecosystem/entries/gcp-forseti.md | 22 + .../entries/github-action-opa-rego-test.md | 21 + .../ecosystem/entries/gloo-api-gateway.md | 12 + .../data/ecosystem/entries/google-calendar.md | 15 + .../entries/google-kubernetes-engine.md | 22 + .../data/ecosystem/entries/gradle-plugin.md | 20 + .../src/data/ecosystem/entries/graphql.md | 14 + .../docs/src/data/ecosystem/entries/i2scim.md | 25 + .../src/data/ecosystem/entries/iptables.md | 17 + .../entries/jenkins-job-authorization.md | 19 + .../ecosystem/entries/kafka-authorization.md | 43 + .../ecosystem/entries/kong-authorization.md | 15 + .../entries/kubernetes-authorization.md | 27 + .../entries/kubernetes-provisioning.md | 20 + .../kubernetes-validating-admission.md | 100 + .../src/data/ecosystem/entries/kubescape.md | 24 + .../src/data/ecosystem/entries/kubeshield.md | 23 + .../src/data/ecosystem/entries/legitify.md | 19 + .../src/data/ecosystem/entries/linux-pam.md | 14 + .../docs/src/data/ecosystem/entries/lula.md | 20 + .../docs/src/data/ecosystem/entries/magda.md | 17 + .../docs/src/data/ecosystem/entries/minio.md | 18 + .../docs/src/data/ecosystem/entries/nacp.md | 20 + .../docs/src/data/ecosystem/entries/nginx.md | 11 + .../data/ecosystem/entries/nodejs-express.md | 25 + .../docs/src/data/ecosystem/entries/oauth2.md | 12 + .../docs/src/data/ecosystem/entries/ocpr.md | 41 + .../docs/src/data/ecosystem/entries/oidc.md | 13 + .../data/ecosystem/entries/opa-aspnetcore.md | 21 + .../src/data/ecosystem/entries/opa-csharp.md | 18 + .../ecosystem/entries/opa-dotnet-asp-core.md | 17 + .../src/data/ecosystem/entries/opa-dotnet.md | 21 + .../src/data/ecosystem/entries/opa-errors.md | 27 + .../src/data/ecosystem/entries/opa-golang.md | 18 + .../data/ecosystem/entries/opa-java-client.md | 15 + .../data/ecosystem/entries/opa-java-wasm.md | 19 + .../src/data/ecosystem/entries/opa-java.md | 20 + .../data/ecosystem/entries/opa-playground.md | 34 + .../src/data/ecosystem/entries/opa-python.md | 14 + .../data/ecosystem/entries/opa-springboot.md | 20 + .../data/ecosystem/entries/opa-typescript.md | 19 + .../data/ecosystem/entries/opa-wasm-dotnet.md | 23 + .../data/ecosystem/entries/opa-wasm-java.md | 22 + .../src/data/ecosystem/entries/opa-wasm-js.md | 29 + .../data/ecosystem/entries/opa-wasm-rust.md | 23 + .../docs/src/data/ecosystem/entries/opal.md | 37 + .../ecosystem/entries/open-service-mesh.md | 16 + .../openfaas-function-authorization.md | 15 + .../src/data/ecosystem/entries/optoggles.md | 16 + .../docs/src/data/ecosystem/entries/permit.md | 19 + .../ecosystem/entries/php-authorization.md | 26 + .../data/ecosystem/entries/pomerium-authz.md | 16 + .../ecosystem/entries/pre-commit-hooks.md | 16 + .../ecosystem/entries/principled-evolution.md | 16 + .../docs/src/data/ecosystem/entries/pulumi.md | 30 + .../docs/src/data/ecosystem/entries/raygun.md | 30 + .../docs/src/data/ecosystem/entries/regal.md | 61 + .../ecosystem/entries/rego-cheat-sheet.md | 24 + .../entries/rego-language-comparisons.md | 26 + .../ecosystem/entries/rego-test-assertions.md | 28 + .../src/data/ecosystem/entries/regocpp.md | 13 + .../docs/src/data/ecosystem/entries/rekor.md | 17 + .../src/data/ecosystem/entries/reposaur.md | 15 + .../docs/src/data/ecosystem/entries/rond.md | 34 + .../src/data/ecosystem/entries/sansshell.md | 16 + .../src/data/ecosystem/entries/scalr-iacp.md | 32 + .../src/data/ecosystem/entries/spacelift.md | 42 + .../src/data/ecosystem/entries/sphinx-rego.md | 13 + .../ecosystem/entries/spinnaker-pipeline.md | 17 + .../docs/src/data/ecosystem/entries/spire.md | 28 + .../ecosystem/entries/springsecurity-api.md | 32 + .../ecosystem/entries/sql-datafiltering.md | 15 + .../src/data/ecosystem/entries/strimzi.md | 24 + .../data/ecosystem/entries/styra-academy.md | 26 + .../src/data/ecosystem/entries/styra-das.md | 104 + .../src/data/ecosystem/entries/swift-opa.md | 21 + .../ecosystem/entries/sysdig-image-scanner.md | 21 + .../docs/src/data/ecosystem/entries/tavoai.md | 12 + .../data/ecosystem/entries/terraform-cloud.md | 35 + .../src/data/ecosystem/entries/terraform.md | 38 + .../docs/src/data/ecosystem/entries/topaz.md | 27 + .../docs/src/data/ecosystem/entries/torque.md | 35 + .../ecosystem/entries/traefik-api-gateway.md | 20 + .../docs/src/data/ecosystem/entries/trino.md | 25 + .../src/data/ecosystem/entries/vscode-opa.md | 39 + .../docs/src/data/ecosystem/entries/waltid.md | 34 + .../ecosystem/entries/wirelesssecuritylab.md | 22 + .../src/data/ecosystem/entries/zed-rego.md | 23 + .../feature-categories/createwithopa.md | 6 + .../feature-categories/production.md | 8 + .../data/ecosystem/feature-categories/rego.md | 7 + .../data/ecosystem/feature-categories/tool.md | 6 + .../data/ecosystem/features/debugging-rego.md | 8 + .../src/data/ecosystem/features/editors.md | 8 + .../docs/src/data/ecosystem/features/envoy.md | 7 + .../features/external-data-realtime-push.md | 9 + .../features/external-data-runtime.md | 8 + .../data/ecosystem/features/external-data.md | 9 + .../data/ecosystem/features/go-integration.md | 7 + .../src/data/ecosystem/features/kubernetes.md | 7 + .../data/ecosystem/features/learning-rego.md | 7 + .../features/opa-bundles-discovery.md | 7 + .../data/ecosystem/features/opa-bundles.md | 7 + .../data/ecosystem/features/policy-testing.md | 8 + .../features/rest-api-integration.md | 8 + .../src/data/ecosystem/features/status-api.md | 7 + .../src/data/ecosystem/features/terraform.md | 7 + .../ecosystem/features/wasm-integration.md | 7 + .../src/data/ecosystem/languages/clojure.md | 5 + .../src/data/ecosystem/languages/csharp.md | 5 + .../src/data/ecosystem/languages/golang.md | 5 + .../docs/src/data/ecosystem/languages/java.md | 5 + .../data/ecosystem/languages/javascript.md | 5 + .../docs/src/data/ecosystem/languages/php.md | 5 + .../docs/src/data/ecosystem/languages/rust.md | 5 + .../src/data/ecosystem/languages/swift.md | 7 + .../docs/src/lib/ecosystem/getLogoAsset.js | 42 + .../opa/docs/src/lib/ecosystem/loadPages.js | 32 + .../docs/src/lib/ecosystem/sortPagesByRank.js | 59 + third_party/opa/docs/src/lib/playground.js | 91 + third_party/opa/docs/src/lib/sidebars.js | 153 + .../src/pages/_examples/admin/config.json | 6 + .../docs/src/pages/_examples/admin/data.json | 1 + .../docs/src/pages/_examples/admin/input.json | 11 + .../docs/src/pages/_examples/admin/intro.md | 1 + .../src/pages/_examples/admin/policy.rego | 12 + .../docs/src/pages/_examples/admin/title.txt | 0 .../docs/src/pages/_examples/ai/config.json | 6 + .../opa/docs/src/pages/_examples/ai/data.json | 1 + .../docs/src/pages/_examples/ai/input.json | 17 + .../opa/docs/src/pages/_examples/ai/intro.md | 8 + .../docs/src/pages/_examples/ai/policy.rego | 25 + .../opa/docs/src/pages/_examples/ai/title.txt | 0 .../docs/src/pages/_examples/app/config.json | 6 + .../docs/src/pages/_examples/app/data.json | 1 + .../docs/src/pages/_examples/app/input.json | 9 + .../opa/docs/src/pages/_examples/app/intro.md | 5 + .../docs/src/pages/_examples/app/policy.rego | 13 + .../docs/src/pages/_examples/app/title.txt | 0 .../src/pages/_examples/envoy/config.json | 6 + .../docs/src/pages/_examples/envoy/data.json | 1 + .../docs/src/pages/_examples/envoy/input.json | 14 + .../docs/src/pages/_examples/envoy/intro.md | 8 + .../src/pages/_examples/envoy/policy.rego | 12 + .../docs/src/pages/_examples/envoy/title.txt | 0 .../docs/src/pages/_examples/k8s/config.json | 6 + .../docs/src/pages/_examples/k8s/data.json | 1 + .../docs/src/pages/_examples/k8s/input.json | 29 + .../opa/docs/src/pages/_examples/k8s/intro.md | 8 + .../docs/src/pages/_examples/k8s/policy.rego | 12 + .../docs/src/pages/_examples/k8s/title.txt | 0 .../opa/docs/src/pages/assets/README.md | 4 + .../opa/docs/src/pages/assets/github.png | Bin 0 -> 10947 bytes .../opa/docs/src/pages/assets/icons/audit.png | Bin 0 -> 111697 bytes .../src/pages/assets/icons/performance.png | Bin 0 -> 69700 bytes .../src/pages/assets/icons/productivity.png | Bin 0 -> 124845 bytes .../docs/src/pages/assets/logo-text-dark.png | Bin 0 -> 78726 bytes .../docs/src/pages/assets/logo-text-light.png | Bin 0 -> 120594 bytes .../opa/docs/src/pages/assets/logo.png | Bin 0 -> 38210 bytes .../src/pages/assets/logos/allstate-dark.svg | 35 + .../src/pages/assets/logos/allstate-light.svg | 62 + .../src/pages/assets/logos/atlassian-dark.svg | 24 + .../pages/assets/logos/atlassian-light.svg | 26 + .../src/pages/assets/logos/bankdata-dark.svg | 92 + .../src/pages/assets/logos/bankdata-light.svg | 92 + .../src/pages/assets/logos/bloomberg-dark.svg | 33 + .../pages/assets/logos/bloomberg-light.svg | 33 + .../docs/src/pages/assets/logos/bny-dark.svg | 18 + .../docs/src/pages/assets/logos/bny-light.svg | 18 + .../pages/assets/logos/capital-one-dark.svg | 67 + .../pages/assets/logos/capital-one-light.svg | 8 + .../src/pages/assets/logos/cisco-dark.svg | 23 + .../src/pages/assets/logos/cisco-light.svg | 23 + .../pages/assets/logos/goldman-sachs-dark.svg | 3 + .../assets/logos/goldman-sachs-light.svg | 3 + .../src/pages/assets/logos/intuit-dark.svg | 21 + .../src/pages/assets/logos/intuit-light.svg | 21 + .../assets/logos/marsh-mclennan-dark.svg | 17 + .../assets/logos/marsh-mclennan-light.svg | 128 + .../src/pages/assets/logos/pinterest-dark.svg | 73 + .../pages/assets/logos/pinterest-light.svg | 73 + .../src/pages/assets/logos/sugarcrm-dark.svg | 5 + .../src/pages/assets/logos/sugarcrm-light.svg | 1 + .../src/pages/assets/logos/t-mobile-dark.svg | 55 + .../src/pages/assets/logos/t-mobile-light.svg | 55 + .../pages/assets/logos/tripadvisor-dark.svg | 47 + .../pages/assets/logos/tripadvisor-light.svg | 46 + .../src/pages/assets/logos/vodafone-dark.svg | 128 + .../src/pages/assets/logos/vodafone-light.svg | 128 + .../src/pages/assets/logos/zalando-dark.svg | 42 + .../src/pages/assets/logos/zalando-light.svg | 60 + .../opa/docs/src/pages/assets/slack.png | Bin 0 -> 34407 bytes .../opa/docs/src/pages/assets/styra.svg | 20 + .../assets/logos/github-discussions.png | Bin 0 -> 11445 bytes .../pages/community/assets/logos/github.png | Bin 0 -> 10947 bytes .../pages/community/assets/logos/linkedin.png | Bin 0 -> 2619 bytes .../src/pages/community/assets/logos/opa.png | Bin 0 -> 38210 bytes .../pages/community/assets/logos/slack.png | Bin 0 -> 24800 bytes .../community/assets/logos/stack-overflow.png | Bin 0 -> 20527 bytes .../community/assets/logos/styra-academy.png | Bin 0 -> 10453 bytes .../opa/docs/src/pages/community/index.js | 107 + .../ecosystem/language-logos/clojure.png | Bin 0 -> 14935 bytes .../ecosystem/language-logos/csharp.png | Bin 0 -> 19846 bytes .../ecosystem/language-logos/golang.png | Bin 0 -> 15525 bytes .../assets/ecosystem/language-logos/java.png | Bin 0 -> 17152 bytes .../ecosystem/language-logos/javascript.png | Bin 0 -> 139196 bytes .../assets/ecosystem/language-logos/php.png | Bin 0 -> 10664 bytes .../assets/ecosystem/language-logos/rust.png | Bin 0 -> 10096 bytes .../assets/ecosystem/language-logos/swift.png | Bin 0 -> 20538 bytes .../opa/docs/src/pages/ecosystem/index.js | 190 + .../src/pages/ecosystem/styles.module.css | 87 + third_party/opa/docs/src/pages/index.js | 226 + .../opa/docs/src/pages/index.module.css | 179 + third_party/opa/docs/src/pages/security.mdx | 45 + .../pages/support/assets/logos/paclabs.png | Bin 0 -> 313939 bytes .../src/pages/support/assets/logos/styra.png | Bin 0 -> 10453 bytes .../opa/docs/src/pages/support/index.js | 46 + .../docs/src/theme/ColorModeToggle/index.js | 68 + .../theme/ColorModeToggle/styles.module.css | 28 + .../docs/src/theme/DocItem/Content/index.js | 21 + .../opa/docs/src/theme/MDXComponents.js | 28 + .../src/theme/NavbarItem/ComponentTypes.js | 8 + .../docs/src/theme/NotFound/Content/index.js | 88 + .../opa/docs/src/theme/NotFound/index.js | 20 + .../docs/static/.well-known/traffic-advice | 9 + third_party/opa/docs/static/_redirects | 75 + .../opa/docs/static/apple-touch-icon.png | Bin 0 -> 6110 bytes .../docs/static/external-resources/README.md | 7 + .../external-resources/bundles/envoy/authz | Bin 0 -> 562 bytes .../bundles/helm-kubernetes-quickstart | Bin 0 -> 826 bytes .../external-resources/bundles/istio/authz | Bin 0 -> 545 bytes .../bundles/kubernetes/admission | Bin 0 -> 870 bytes .../opa-horizontal-color.png | Bin 0 -> 120594 bytes .../external-resources/opa-no-text-color.png | Bin 0 -> 51127 bytes third_party/opa/docs/static/favicon-96x96.png | Bin 0 -> 4393 bytes third_party/opa/docs/static/favicon.ico | Bin 0 -> 15086 bytes third_party/opa/docs/static/favicon.png | Bin 0 -> 4393 bytes third_party/opa/docs/static/favicon.svg | 17 + .../img/ecosystem-entry-logos/alfred.png | Bin 0 -> 122187 bytes .../img/ecosystem-entry-logos/alluxio.png | Bin 0 -> 17269 bytes .../img/ecosystem-entry-logos/antlr.png | Bin 0 -> 4735 bytes .../ecosystem-entry-logos/apache-apisix.png | Bin 0 -> 71384 bytes .../img/ecosystem-entry-logos/aserto.png | Bin 0 -> 161500 bytes .../ecosystem-entry-logos/asp-dotnet-core.png | Bin 0 -> 10883 bytes .../img/ecosystem-entry-logos/atmos.png | Bin 0 -> 13285 bytes .../ecosystem-entry-logos/aws-api-gateway.png | Bin 0 -> 72861 bytes .../aws-cloudformation-hook.svg | 18 + .../img/ecosystem-entry-logos/backstage.svg | 1 + .../ecosystem-entry-logos/boomerang-bosun.png | Bin 0 -> 93904 bytes .../img/ecosystem-entry-logos/bottle.png | Bin 0 -> 6820 bytes .../img/ecosystem-entry-logos/carbonetes.png | Bin 0 -> 24456 bytes .../static/img/ecosystem-entry-logos/ceph.png | Bin 0 -> 45196 bytes .../img/ecosystem-entry-logos/circleci.png | Bin 0 -> 31732 bytes .../clair-datasource.png | Bin 0 -> 20980 bytes .../img/ecosystem-entry-logos/clojure.png | Bin 0 -> 14935 bytes .../cloudflare-worker.png | Bin 0 -> 26822 bytes .../img/ecosystem-entry-logos/conftest.png | Bin 0 -> 38210 bytes .../ecosystem-entry-logos/coredns-authz.png | Bin 0 -> 13443 bytes .../img/ecosystem-entry-logos/cosign.png | Bin 0 -> 14825 bytes .../static/img/ecosystem-entry-logos/dapr.png | Bin 0 -> 2298 bytes .../dart-authorization.png | Bin 0 -> 5365 bytes .../img/ecosystem-entry-logos/default.png | Bin 0 -> 38210 bytes .../dependency-management-data.png | Bin 0 -> 66895 bytes .../img/ecosystem-entry-logos/digger.png | Bin 0 -> 9778 bytes .../ecosystem-entry-logos/docker-machine.png | Bin 0 -> 47520 bytes .../img/ecosystem-entry-logos/easegress.svg | 18 + .../elasticsearch-datafiltering.png | Bin 0 -> 125415 bytes .../emissary-ingress.png | Bin 0 -> 21098 bytes .../enterprise-contract.svg | 173 + .../ecosystem-entry-logos/enterprise-opa.png | Bin 0 -> 10453 bytes .../static/img/ecosystem-entry-logos/env0.png | Bin 0 -> 53375 bytes .../envoy-authorization.png | Bin 0 -> 201424 bytes .../ecosystem-entry-logos/expressing-or.png | Bin 0 -> 10453 bytes .../img/ecosystem-entry-logos/fiber.png | Bin 0 -> 24388 bytes .../static/img/ecosystem-entry-logos/fig.png | Bin 0 -> 39671 bytes .../img/ecosystem-entry-logos/flask-opa.png | Bin 0 -> 29273 bytes .../img/ecosystem-entry-logos/flipt.png | Bin 0 -> 46282 bytes .../img/ecosystem-entry-logos/gcp-forseti.png | Bin 0 -> 50473 bytes .../github-action-opa-rego-test.png | Bin 0 -> 18968 bytes .../gloo-api-gateway.png | Bin 0 -> 88920 bytes .../gluu-gateway-authz.png | Bin 0 -> 83913 bytes .../ecosystem-entry-logos/google-calendar.png | Bin 0 -> 7095 bytes .../google-kubernetes-engine.png | Bin 0 -> 62085 bytes .../ecosystem-entry-logos/gradle-plugin.png | Bin 0 -> 10776 bytes .../graphene-graphql.png | Bin 0 -> 120736 bytes .../img/ecosystem-entry-logos/graphql.png | Bin 0 -> 120736 bytes .../img/ecosystem-entry-logos/i2scim.png | Bin 0 -> 14172 bytes .../img/ecosystem-entry-logos/iptables.png | Bin 0 -> 469482 bytes .../istio-authorization-mixer.png | Bin 0 -> 4885 bytes .../jenkins-job-authorization.png | Bin 0 -> 43263 bytes .../kafka-authorization.png | Bin 0 -> 13824 bytes .../kong-authorization.png | Bin 0 -> 20450 bytes .../kubernetes-authorization.png | Bin 0 -> 286328 bytes .../kubernetes-provisioning.png | Bin 0 -> 286328 bytes .../kubernetes-validating-admission.png | Bin 0 -> 286328 bytes .../img/ecosystem-entry-logos/kubescape.png | Bin 0 -> 45257 bytes .../img/ecosystem-entry-logos/kubeshield.png | Bin 0 -> 6813 bytes .../img/ecosystem-entry-logos/legitify.png | Bin 0 -> 55222 bytes .../img/ecosystem-entry-logos/linux-pam.png | Bin 0 -> 469482 bytes .../static/img/ecosystem-entry-logos/lula.svg | 70 + .../img/ecosystem-entry-logos/magda.png | Bin 0 -> 3683 bytes .../img/ecosystem-entry-logos/minio.png | Bin 0 -> 9830 bytes .../static/img/ecosystem-entry-logos/nacp.png | Bin 0 -> 73262 bytes .../img/ecosystem-entry-logos/nginx.png | Bin 0 -> 90067 bytes .../ecosystem-entry-logos/nodejs-express.png | Bin 0 -> 43498 bytes .../img/ecosystem-entry-logos/oauth2.svg | 62 + .../static/img/ecosystem-entry-logos/ocpr.svg | 31 + .../static/img/ecosystem-entry-logos/oidc.png | Bin 0 -> 12921 bytes .../ecosystem-entry-logos/opa-aspnetcore.png | Bin 0 -> 10453 bytes .../img/ecosystem-entry-logos/opa-csharp.png | Bin 0 -> 10453 bytes .../opa-dotnet-asp-core.png | Bin 0 -> 10883 bytes .../img/ecosystem-entry-logos/opa-dotnet.png | Bin 0 -> 10883 bytes .../img/ecosystem-entry-logos/opa-errors.png | Bin 0 -> 10453 bytes .../img/ecosystem-entry-logos/opa-golang.png | Bin 0 -> 38210 bytes .../ecosystem-entry-logos/opa-java-client.png | Bin 0 -> 16703 bytes .../ecosystem-entry-logos/opa-java-wasm.png | Bin 0 -> 10453 bytes .../img/ecosystem-entry-logos/opa-java.png | Bin 0 -> 10453 bytes .../ecosystem-entry-logos/opa-playground.png | Bin 0 -> 38210 bytes .../img/ecosystem-entry-logos/opa-python.png | Bin 0 -> 179111 bytes .../ecosystem-entry-logos/opa-springboot.png | Bin 0 -> 10453 bytes .../ecosystem-entry-logos/opa-typescript.png | Bin 0 -> 10453 bytes .../ecosystem-entry-logos/opa-wasm-dotnet.png | Bin 0 -> 10883 bytes .../ecosystem-entry-logos/opa-wasm-java.png | Bin 0 -> 16703 bytes .../img/ecosystem-entry-logos/opa-wasm-js.png | Bin 0 -> 38732 bytes .../ecosystem-entry-logos/opa-wasm-rust.png | Bin 0 -> 48791 bytes .../static/img/ecosystem-entry-logos/opal.png | Bin 0 -> 646954 bytes .../open-service-mesh.png | Bin 0 -> 16955 bytes .../openfaas-function-authorization.png | Bin 0 -> 8155 bytes .../img/ecosystem-entry-logos/optoggles.png | Bin 0 -> 26755 bytes .../img/ecosystem-entry-logos/permit.png | Bin 0 -> 96116 bytes .../php-authorization.png | Bin 0 -> 10298 bytes .../ecosystem-entry-logos/pomerium-authz.png | Bin 0 -> 13445 bytes .../pre-commit-hooks.png | Bin 0 -> 11526 bytes .../principled-evolution.svg | 377 + .../img/ecosystem-entry-logos/pulumi.png | Bin 0 -> 7051 bytes .../img/ecosystem-entry-logos/raygun.png | Bin 0 -> 157124 bytes .../img/ecosystem-entry-logos/regal.png | Bin 0 -> 38816 bytes .../rego-cheat-sheet.png | Bin 0 -> 10453 bytes .../rego-language-comparisons.png | Bin 0 -> 10453 bytes .../rego-test-assertions.png | Bin 0 -> 18968 bytes .../img/ecosystem-entry-logos/regocpp.svg | 6 + .../img/ecosystem-entry-logos/rekor.svg | 464 + .../img/ecosystem-entry-logos/reposaur.png | Bin 0 -> 7024 bytes .../static/img/ecosystem-entry-logos/rond.png | Bin 0 -> 332292 bytes .../img/ecosystem-entry-logos/scalr-iacp.png | Bin 0 -> 20172 bytes .../img/ecosystem-entry-logos/spacelift.png | Bin 0 -> 13044 bytes .../img/ecosystem-entry-logos/sphinx-rego.png | Bin 0 -> 11719 bytes .../spinnaker-pipeline.png | Bin 0 -> 34603 bytes .../img/ecosystem-entry-logos/spire.png | Bin 0 -> 93433 bytes .../springsecurity-api.png | Bin 0 -> 11931 bytes .../sql-datafiltering.png | Bin 0 -> 89441 bytes .../img/ecosystem-entry-logos/strimzi.png | Bin 0 -> 4812 bytes .../ecosystem-entry-logos/styra-academy.png | Bin 0 -> 10453 bytes .../img/ecosystem-entry-logos/styra-das.png | Bin 0 -> 10453 bytes .../img/ecosystem-entry-logos/swift-opa.png | Bin 0 -> 20538 bytes .../sysdig-image-scanner.png | Bin 0 -> 8782 bytes .../img/ecosystem-entry-logos/tavoai.png | Bin 0 -> 15708 bytes .../ecosystem-entry-logos/terraform-cloud.png | Bin 0 -> 11770 bytes .../img/ecosystem-entry-logos/terraform.png | Bin 0 -> 11770 bytes .../img/ecosystem-entry-logos/topaz.svg | 30 + .../img/ecosystem-entry-logos/torque.png | Bin 0 -> 10227 bytes .../traefik-api-gateway.png | Bin 0 -> 35056 bytes .../img/ecosystem-entry-logos/trino.png | Bin 0 -> 34219 bytes .../img/ecosystem-entry-logos/vscode-opa.png | Bin 0 -> 38210 bytes .../img/ecosystem-entry-logos/waltid.png | Bin 0 -> 26224 bytes .../img/ecosystem-entry-logos/zed-rego.png | Bin 0 -> 38210 bytes .../opa/docs/static/img/footer/cncf-dark.svg | 48 + .../opa/docs/static/img/footer/cncf-light.svg | 1 + .../opa/docs/static/img/nav/github-dark.svg | 1 + .../opa/docs/static/img/nav/github-light.svg | 1 + third_party/opa/docs/static/img/nav/logo.png | Bin 0 -> 38210 bytes .../opa/docs/static/img/nav/slack-dark.svg | 31 + .../opa/docs/static/img/nav/slack-light.svg | 34 + .../opa/docs/static/netlify-forms.html | 18 + third_party/opa/docs/static/robots.txt | 2 + third_party/opa/docs/static/site.webmanifest | 21 + .../docs/static/web-app-manifest-192x192.png | Bin 0 -> 6640 bytes .../docs/static/web-app-manifest-512x512.png | Bin 0 -> 21092 bytes third_party/opa/download/config.go | 15 + third_party/opa/download/doc.go | 8 + third_party/opa/download/download.go | 29 + third_party/opa/download/oci_download.go | 14 + .../opa/download/oci_download_unavailable.go | 59 + third_party/opa/download/oci_downloader.go | 7 + third_party/opa/features/doc.go | 8 + third_party/opa/features/tracing/doc.go | 8 + third_party/opa/features/tracing/tracing.go | 10 + third_party/opa/features/wasm/doc.go | 8 + third_party/opa/features/wasm/wasm.go | 14 + third_party/opa/format/doc.go | 8 + third_party/opa/format/format.go | 86 + third_party/opa/format/format_test.go | 152 + third_party/opa/go.mod | 112 + third_party/opa/go.sum | 314 + third_party/opa/hooks/doc.go | 8 + third_party/opa/hooks/hooks.go | 46 + .../opa/internal/bundle/inspect/inspect.go | 246 + .../internal/bundle/inspect/inspect_test.go | 280 + third_party/opa/internal/bundle/utils.go | 147 + third_party/opa/internal/cidr/merge/merge.go | 367 + .../internal/cmd/genbuiltinmetadata/main.go | 172 + .../internal/cmd/genopacapabilities/main.go | 37 + .../opa/internal/cmd/genversionindex/main.go | 78 + third_party/opa/internal/compiler/utils.go | 95 + .../opa/internal/compiler/utils_test.go | 135 + .../internal/compiler/wasm/opa/callgraph.csv | 2461 ++ .../opa/internal/compiler/wasm/opa/opa.go | 27 + .../opa/internal/compiler/wasm/opa/opa.wasm | Bin 0 -> 436729 bytes .../internal/compiler/wasm/optimizations.go | 271 + .../compiler/wasm/optimizations_test.go | 56 + .../opa/internal/compiler/wasm/wasm.go | 1786 ++ .../opa/internal/compiler/wasm/wasm_test.go | 95 + third_party/opa/internal/config/config.go | 175 + .../opa/internal/config/config_test.go | 486 + third_party/opa/internal/debug/debug.go | 35 + third_party/opa/internal/deepcopy/deepcopy.go | 31 + .../opa/internal/deepcopy/deepcopy_test.go | 31 + .../distributedtracing/distributedtracing.go | 411 + .../opa/internal/edittree/bitvector/README.md | 15 + .../internal/edittree/bitvector/bitvector.go | 206 + .../edittree/bitvector/bitvector_test.go | 106 + .../internal/edittree/bitvector/license.txt | 27 + third_party/opa/internal/edittree/edittree.go | 1186 + .../opa/internal/edittree/edittree_test.go | 951 + .../opa/internal/file/archive/tarball.go | 42 + third_party/opa/internal/file/url/url.go | 42 + third_party/opa/internal/file/url/url_test.go | 50 + .../opa/internal/future/filter_imports.go | 49 + .../opa/internal/future/parser_opts.go | 43 + .../gojsonschema/LICENSE-APACHE-2.0.txt | 202 + .../opa/internal/gojsonschema/README.md | 482 + .../opa/internal/gojsonschema/draft.go | 122 + .../opa/internal/gojsonschema/errors.go | 331 + .../internal/gojsonschema/format_checkers.go | 368 + .../gojsonschema/format_checkers_test.go | 162 + .../opa/internal/gojsonschema/internalLog.go | 37 + .../opa/internal/gojsonschema/jsonContext.go | 73 + .../opa/internal/gojsonschema/jsonLoader.go | 410 + .../internal/gojsonschema/jsonschema_test.go | 198 + .../opa/internal/gojsonschema/locales.go | 472 + .../opa/internal/gojsonschema/result.go | 220 + .../opa/internal/gojsonschema/schema.go | 957 + .../opa/internal/gojsonschema/schemaLoader.go | 206 + .../gojsonschema/schemaLoader_test.go | 223 + .../opa/internal/gojsonschema/schemaPool.go | 230 + .../gojsonschema/schemaReferencePool.go | 64 + .../opa/internal/gojsonschema/schemaType.go | 78 + .../opa/internal/gojsonschema/schema_test.go | 413 + .../opa/internal/gojsonschema/subSchema.go | 151 + .../testdata/draft4/additionalItems.json | 87 + .../testdata/draft4/additionalProperties.json | 98 + .../gojsonschema/testdata/draft4/allOf.json | 112 + .../gojsonschema/testdata/draft4/anyOf.json | 109 + .../gojsonschema/testdata/draft4/default.json | 49 + .../testdata/draft4/definitions.json | 32 + .../testdata/draft4/dependencies.json | 123 + .../gojsonschema/testdata/draft4/enum.json | 72 + .../gojsonschema/testdata/draft4/format.json | 218 + .../gojsonschema/testdata/draft4/items.json | 78 + .../testdata/draft4/maxItems.json | 28 + .../testdata/draft4/maxLength.json | 33 + .../testdata/draft4/maxProperties.json | 38 + .../gojsonschema/testdata/draft4/maximum.json | 47 + .../testdata/draft4/minItems.json | 28 + .../testdata/draft4/minLength.json | 33 + .../testdata/draft4/minProperties.json | 38 + .../gojsonschema/testdata/draft4/minimum.json | 47 + .../testdata/draft4/multipleOf.json | 60 + .../gojsonschema/testdata/draft4/not.json | 96 + .../gojsonschema/testdata/draft4/oneOf.json | 109 + .../testdata/draft4/optional/bignum.json | 107 + .../draft4/optional/ecmascript-regex.json | 13 + .../testdata/draft4/optional/format.json | 223 + .../draft4/optional/zeroTerminatedFloats.json | 15 + .../gojsonschema/testdata/draft4/pattern.json | 34 + .../testdata/draft4/patternProperties.json | 120 + .../testdata/draft4/properties.json | 97 + .../gojsonschema/testdata/draft4/ref.json | 300 + .../testdata/draft4/refRemote.json | 171 + .../testdata/draft4/required.json | 54 + .../gojsonschema/testdata/draft4/type.json | 345 + .../testdata/draft4/uniqueItems.json | 79 + .../testdata/draft6/additionalItems.json | 87 + .../testdata/draft6/additionalProperties.json | 98 + .../gojsonschema/testdata/draft6/allOf.json | 145 + .../gojsonschema/testdata/draft6/anyOf.json | 142 + .../testdata/draft6/boolean_schema.json | 104 + .../gojsonschema/testdata/draft6/const.json | 86 + .../testdata/draft6/contains.json | 95 + .../gojsonschema/testdata/draft6/default.json | 49 + .../testdata/draft6/definitions.json | 32 + .../testdata/draft6/dependencies.json | 172 + .../gojsonschema/testdata/draft6/enum.json | 72 + .../testdata/draft6/exclusiveMaximum.json | 30 + .../testdata/draft6/exclusiveMinimum.json | 30 + .../gojsonschema/testdata/draft6/format.json | 326 + .../gojsonschema/testdata/draft6/items.json | 133 + .../testdata/draft6/maxItems.json | 28 + .../testdata/draft6/maxLength.json | 33 + .../testdata/draft6/maxProperties.json | 38 + .../gojsonschema/testdata/draft6/maximum.json | 28 + .../testdata/draft6/minItems.json | 28 + .../testdata/draft6/minLength.json | 33 + .../testdata/draft6/minProperties.json | 38 + .../gojsonschema/testdata/draft6/minimum.json | 28 + .../testdata/draft6/multipleOf.json | 60 + .../gojsonschema/testdata/draft6/not.json | 117 + .../gojsonschema/testdata/draft6/oneOf.json | 153 + .../testdata/draft6/optional/bignum.json | 105 + .../draft6/optional/ecmascript-regex.json | 13 + .../testdata/draft6/optional/format.json | 458 + .../draft6/optional/zeroTerminatedFloats.json | 15 + .../gojsonschema/testdata/draft6/pattern.json | 34 + .../testdata/draft6/patternProperties.json | 151 + .../testdata/draft6/properties.json | 128 + .../testdata/draft6/propertyNames.json | 78 + .../gojsonschema/testdata/draft6/ref.json | 332 + .../testdata/draft6/refRemote.json | 171 + .../testdata/draft6/required.json | 70 + .../gojsonschema/testdata/draft6/type.json | 345 + .../testdata/draft6/uniqueItems.json | 79 + .../testdata/draft7/additionalItems.json | 87 + .../testdata/draft7/additionalProperties.json | 98 + .../gojsonschema/testdata/draft7/allOf.json | 145 + .../gojsonschema/testdata/draft7/anyOf.json | 142 + .../testdata/draft7/boolean_schema.json | 104 + .../gojsonschema/testdata/draft7/const.json | 86 + .../testdata/draft7/contains.json | 95 + .../gojsonschema/testdata/draft7/default.json | 49 + .../testdata/draft7/definitions.json | 32 + .../testdata/draft7/dependencies.json | 172 + .../gojsonschema/testdata/draft7/enum.json | 72 + .../testdata/draft7/exclusiveMaximum.json | 30 + .../testdata/draft7/exclusiveMinimum.json | 30 + .../gojsonschema/testdata/draft7/format.json | 614 + .../testdata/draft7/if-then-else.json | 188 + .../gojsonschema/testdata/draft7/items.json | 133 + .../testdata/draft7/maxItems.json | 28 + .../testdata/draft7/maxLength.json | 33 + .../testdata/draft7/maxProperties.json | 38 + .../gojsonschema/testdata/draft7/maximum.json | 28 + .../testdata/draft7/minItems.json | 28 + .../testdata/draft7/minLength.json | 33 + .../testdata/draft7/minProperties.json | 38 + .../gojsonschema/testdata/draft7/minimum.json | 28 + .../testdata/draft7/multipleOf.json | 60 + .../gojsonschema/testdata/draft7/not.json | 117 + .../gojsonschema/testdata/draft7/oneOf.json | 153 + .../testdata/draft7/optional/bignum.json | 105 + .../testdata/draft7/optional/content.json | 62 + .../draft7/optional/ecmascript-regex.json | 13 + .../draft7/optional/format/date-time.json | 23 + .../testdata/draft7/optional/format/date.json | 23 + .../draft7/optional/format/email.json | 18 + .../draft7/optional/format/hostname.json | 33 + .../draft7/optional/format/idn-email.json | 18 + .../draft7/optional/format/idn-hostname.json | 29 + .../testdata/draft7/optional/format/ipv4.json | 33 + .../testdata/draft7/optional/format/ipv6.json | 28 + .../draft7/optional/format/iri-reference.json | 43 + .../testdata/draft7/optional/format/iri.json | 48 + .../draft7/optional/format/json-pointer.json | 168 + .../draft7/optional/format/regex.json | 18 + .../format/relative-json-pointer.json | 33 + .../testdata/draft7/optional/format/time.json | 23 + .../draft7/optional/format/uri-reference.json | 43 + .../draft7/optional/format/uri-template.json | 30 + .../testdata/draft7/optional/format/uri.json | 103 + .../draft7/optional/zeroTerminatedFloats.json | 15 + .../gojsonschema/testdata/draft7/pattern.json | 34 + .../testdata/draft7/patternProperties.json | 151 + .../testdata/draft7/properties.json | 128 + .../testdata/draft7/propertyNames.json | 78 + .../gojsonschema/testdata/draft7/ref.json | 332 + .../testdata/draft7/refRemote.json | 171 + .../testdata/draft7/required.json | 70 + .../gojsonschema/testdata/draft7/type.json | 345 + .../testdata/draft7/uniqueItems.json | 79 + .../testdata/extra/file with space.json | 1 + .../testdata/extra/fragment_schema.json | 1 + .../remotes/folder/folderInteger.json | 3 + .../testdata/remotes/integer.json | 3 + .../gojsonschema/testdata/remotes/name.json | 11 + .../testdata/remotes/subSchemas.json | 8 + .../opa/internal/gojsonschema/types.go | 62 + .../opa/internal/gojsonschema/utils.go | 161 + .../opa/internal/gojsonschema/utils_test.go | 58 + .../opa/internal/gojsonschema/validation.go | 837 + third_party/opa/internal/json/patch/patch.go | 45 + .../opa/internal/json/patch/patch_test.go | 87 + third_party/opa/internal/jwx/.gitignore | 31 + third_party/opa/internal/jwx/LICENSE | 21 + third_party/opa/internal/jwx/Makefile | 10 + third_party/opa/internal/jwx/buffer/buffer.go | 112 + .../opa/internal/jwx/buffer/buffer_test.go | 89 + third_party/opa/internal/jwx/jwa/elliptic.go | 11 + third_party/opa/internal/jwx/jwa/key_type.go | 67 + .../opa/internal/jwx/jwa/parameters.go | 29 + third_party/opa/internal/jwx/jwa/signature.go | 78 + third_party/opa/internal/jwx/jwk/ecdsa.go | 120 + .../opa/internal/jwx/jwk/ecdsa_test.go | 248 + third_party/opa/internal/jwx/jwk/headers.go | 178 + .../opa/internal/jwx/jwk/headers_test.go | 174 + third_party/opa/internal/jwx/jwk/interface.go | 71 + third_party/opa/internal/jwx/jwk/jwk.go | 153 + third_party/opa/internal/jwx/jwk/jwk_test.go | 191 + third_party/opa/internal/jwx/jwk/key_ops.go | 67 + third_party/opa/internal/jwx/jwk/rsa.go | 133 + third_party/opa/internal/jwx/jwk/rsa_test.go | 243 + third_party/opa/internal/jwx/jwk/symmetric.go | 41 + .../opa/internal/jwx/jwk/symmetric_test.go | 125 + third_party/opa/internal/jwx/jws/headers.go | 154 + .../opa/internal/jwx/jws/headers_test.go | 122 + third_party/opa/internal/jwx/jws/interface.go | 22 + third_party/opa/internal/jwx/jws/jws.go | 220 + third_party/opa/internal/jwx/jws/jws_test.go | 641 + third_party/opa/internal/jwx/jws/message.go | 26 + .../opa/internal/jwx/jws/sign/ecdsa.go | 90 + .../opa/internal/jwx/jws/sign/ecdsa_test.go | 35 + third_party/opa/internal/jwx/jws/sign/hmac.go | 66 + .../opa/internal/jwx/jws/sign/hmac_test.go | 35 + .../opa/internal/jwx/jws/sign/interface.go | 46 + third_party/opa/internal/jwx/jws/sign/rsa.go | 97 + third_party/opa/internal/jwx/jws/sign/sign.go | 66 + .../opa/internal/jwx/jws/verify/ecdsa.go | 67 + .../opa/internal/jwx/jws/verify/ecdsa_test.go | 35 + .../opa/internal/jwx/jws/verify/hmac.go | 33 + .../opa/internal/jwx/jws/verify/hmac_test.go | 32 + .../opa/internal/jwx/jws/verify/interface.go | 39 + .../opa/internal/jwx/jws/verify/rsa.go | 88 + .../opa/internal/jwx/jws/verify/rsa_test.go | 35 + .../opa/internal/jwx/jws/verify/verify.go | 56 + .../internal/jwx/jws/verify/verify_test.go | 13 + third_party/opa/internal/lcss/README.md | 3 + third_party/opa/internal/lcss/lcss.go | 197 + third_party/opa/internal/lcss/lcss_test.go | 242 + third_party/opa/internal/lcss/qsufsort.go | 169 + third_party/opa/internal/leb128/leb128.go | 170 + .../opa/internal/leb128/leb128_test.go | 207 + third_party/opa/internal/logging/logging.go | 105 + .../opa/internal/logging/logging_test.go | 190 + third_party/opa/internal/merge/merge.go | 64 + third_party/opa/internal/merge/merge_test.go | 72 + third_party/opa/internal/pathwatcher/utils.go | 126 + .../opa/internal/pathwatcher/utils_test.go | 41 + third_party/opa/internal/planner/planner.go | 2600 ++ .../opa/internal/planner/planner_test.go | 1310 + third_party/opa/internal/planner/rules.go | 337 + .../opa/internal/planner/rules_test.go | 178 + third_party/opa/internal/planner/varstack.go | 71 + .../opa/internal/planner/varstack_test.go | 44 + .../opa/internal/presentation/presentation.go | 751 + .../presentation/presentation_test.go | 579 + .../opa/internal/prometheus/prometheus.go | 223 + .../internal/prometheus/prometheus_go1.17.go | 18 + .../internal/prometheus/prometheus_test.go | 204 + .../opa/internal/providers/aws/NOTICE.txt | 3 + .../internal/providers/aws/crypto/compare.go | 30 + .../providers/aws/crypto/compare_test.go | 52 + .../opa/internal/providers/aws/crypto/ecc.go | 114 + .../internal/providers/aws/crypto/ecc_test.go | 277 + third_party/opa/internal/providers/aws/ecr.go | 148 + .../opa/internal/providers/aws/ecr_test.go | 118 + third_party/opa/internal/providers/aws/kms.go | 106 + .../opa/internal/providers/aws/kms_test.go | 96 + .../opa/internal/providers/aws/signing_v4.go | 204 + .../opa/internal/providers/aws/signing_v4a.go | 410 + .../opa/internal/providers/aws/util.go | 39 + .../opa/internal/providers/aws/v4/const.go | 36 + .../internal/providers/aws/v4/header_rules.go | 87 + .../opa/internal/providers/aws/v4/headers.go | 67 + .../opa/internal/providers/aws/v4/host.go | 75 + .../opa/internal/providers/aws/v4/util.go | 59 + .../internal/providers/aws/v4/util_test.go | 75 + third_party/opa/internal/ref/ref.go | 36 + third_party/opa/internal/rego/opa/engine.go | 65 + third_party/opa/internal/rego/opa/options.go | 31 + third_party/opa/internal/report/report.go | 216 + .../opa/internal/report/report_test.go | 199 + third_party/opa/internal/runtime/init/init.go | 261 + .../opa/internal/runtime/init/init_test.go | 528 + third_party/opa/internal/runtime/runtime.go | 62 + third_party/opa/internal/semver/LICENSE | 202 + third_party/opa/internal/semver/semver.go | 235 + .../opa/internal/semver/semver_test.go | 105 + third_party/opa/internal/storage/mock/mock.go | 260 + third_party/opa/internal/strings/strings.go | 82 + third_party/opa/internal/strvals/doc.go | 33 + third_party/opa/internal/strvals/parser.go | 429 + .../opa/internal/strvals/parser_test.go | 529 + third_party/opa/internal/uuid/uuid.go | 115 + third_party/opa/internal/uuid/uuid_test.go | 149 + third_party/opa/internal/version/version.go | 36 + .../opa/internal/wasm/constant/constant.go | 77 + third_party/opa/internal/wasm/encoding/doc.go | 6 + .../internal/wasm/encoding/encoding_test.go | 113 + .../opa/internal/wasm/encoding/reader.go | 965 + .../wasm/encoding/testdata/test1.wasm | Bin 0 -> 409 bytes .../opa/internal/wasm/encoding/writer.go | 778 + .../opa/internal/wasm/instruction/control.go | 183 + .../internal/wasm/instruction/instruction.go | 33 + .../opa/internal/wasm/instruction/memory.go | 39 + .../opa/internal/wasm/instruction/numeric.go | 199 + .../internal/wasm/instruction/parametric.go | 29 + .../opa/internal/wasm/instruction/variable.go | 54 + .../opa/internal/wasm/module/module.go | 385 + .../opa/internal/wasm/module/pretty.go | 84 + .../opa/internal/wasm/opcode/opcode.go | 218 + third_party/opa/internal/wasm/sdk/README.md | 7 + .../wasm/sdk/examples/basic/.gitignore | 2 + .../wasm/sdk/examples/basic/README.md | 44 + .../wasm/sdk/examples/basic/example-1.rego | 3 + .../wasm/sdk/examples/basic/example-2.rego | 3 + .../internal/wasm/sdk/examples/basic/main.go | 106 + .../wasm/sdk/examples/loaders/.gitignore | 2 + .../wasm/sdk/examples/loaders/README.md | 25 + .../wasm/sdk/examples/loaders/example.rego | 3 + .../wasm/sdk/examples/loaders/main.go | 124 + .../wasm/sdk/internal/wasm/bindings.go | 281 + .../internal/wasm/sdk/internal/wasm/pool.go | 368 + .../wasm/sdk/internal/wasm/pool_test.go | 244 + .../opa/internal/wasm/sdk/internal/wasm/vm.go | 825 + .../wasm/sdk/opa/capabilities/capabilities.go | 12 + .../opa/capabilities/capabilities_nowasm.go | 14 + .../opa/internal/wasm/sdk/opa/config.go | 102 + .../internal/wasm/sdk/opa/errors/errors.go | 77 + .../wasm/sdk/opa/loader/file/config.go | 34 + .../wasm/sdk/opa/loader/file/loader.go | 168 + .../wasm/sdk/opa/loader/file/loader_test.go | 130 + .../wasm/sdk/opa/loader/http/config.go | 65 + .../wasm/sdk/opa/loader/http/loader.go | 264 + .../wasm/sdk/opa/loader/http/loader_test.go | 129 + .../internal/wasm/sdk/opa/loader/http/util.go | 43 + .../internal/wasm/sdk/opa/loader/loader.go | 21 + third_party/opa/internal/wasm/sdk/opa/opa.go | 226 + .../internal/wasm/sdk/opa/opa_bench_test.go | 206 + .../opa/internal/wasm/sdk/opa/opa_test.go | 448 + .../wasm/sdk/test/e2e/exceptions.yaml | 3 + .../wasm/sdk/test/e2e/external_test.go | 274 + third_party/opa/internal/wasm/types/types.go | 36 + third_party/opa/internal/wasm/util/util.go | 18 + third_party/opa/ir/doc.go | 8 + third_party/opa/ir/encoding/doc.go | 8 + third_party/opa/ir/encoding/encoding_test.go | 72 + third_party/opa/ir/ir.go | 217 + third_party/opa/ir/pretty.go | 16 + third_party/opa/ir/walk.go | 15 + third_party/opa/keys/doc.go | 8 + third_party/opa/keys/keys.go | 25 + third_party/opa/loader/doc.go | 8 + third_party/opa/loader/errors.go | 12 + third_party/opa/loader/extension/doc.go | 8 + third_party/opa/loader/extension/extension.go | 29 + third_party/opa/loader/filter/doc.go | 8 + third_party/opa/loader/filter/filter.go | 5 + third_party/opa/loader/loader.go | 145 + third_party/opa/loader/loader_test.go | 364 + third_party/opa/logging/doc.go | 8 + third_party/opa/logging/logging.go | 79 + third_party/opa/logging/test/doc.go | 8 + third_party/opa/logging/test/test.go | 16 + third_party/opa/logo/logo-144x144.png | Bin 0 -> 4589 bytes third_party/opa/logo/logo.ico | Bin 0 -> 8380 bytes third_party/opa/logo/logo.png | Bin 0 -> 38210 bytes third_party/opa/logo/logo.svg | 1 + third_party/opa/main.go | 34 + third_party/opa/main_windows.go | 5 + third_party/opa/metrics/doc.go | 8 + third_party/opa/metrics/metrics.go | 57 + .../misc/syntax/sublime/rego.sublime-syntax | 97 + .../opa/misc/syntax/textmate/Rego.tmLanguage | 229 + third_party/opa/netlify.toml | 31 + third_party/opa/plugins/bundle/config.go | 42 + third_party/opa/plugins/bundle/doc.go | 8 + third_party/opa/plugins/bundle/errors.go | 14 + third_party/opa/plugins/bundle/plugin.go | 31 + third_party/opa/plugins/bundle/status.go | 12 + third_party/opa/plugins/discovery/config.go | 25 + .../opa/plugins/discovery/discovery.go | 52 + third_party/opa/plugins/discovery/doc.go | 8 + third_party/opa/plugins/doc.go | 8 + third_party/opa/plugins/logs/doc.go | 8 + third_party/opa/plugins/logs/plugin.go | 65 + third_party/opa/plugins/logs/status/doc.go | 8 + third_party/opa/plugins/logs/status/status.go | 14 + third_party/opa/plugins/plugins.go | 268 + third_party/opa/plugins/plugins_test.go | 47 + third_party/opa/plugins/rest/auth.go | 22 + third_party/opa/plugins/rest/doc.go | 8 + third_party/opa/plugins/rest/gcp.go | 12 + third_party/opa/plugins/rest/rest.go | 54 + .../opa/plugins/server/decoding/config.go | 35 + .../opa/plugins/server/decoding/doc.go | 8 + third_party/opa/plugins/server/doc.go | 8 + .../opa/plugins/server/encoding/config.go | 19 + .../opa/plugins/server/encoding/doc.go | 8 + .../opa/plugins/server/metrics/config.go | 22 + third_party/opa/plugins/server/metrics/doc.go | 8 + third_party/opa/plugins/status/doc.go | 8 + third_party/opa/plugins/status/metrics.go | 9 + third_party/opa/plugins/status/plugin.go | 53 + third_party/opa/profiler/doc.go | 8 + third_party/opa/profiler/profiler.go | 35 + .../opa/proposals/attic/REGO_V2_PROPOSAL.md | 756 + third_party/opa/race.txt | 0 third_party/opa/refactor/doc.go | 8 + third_party/opa/refactor/refactor.go | 30 + third_party/opa/rego/doc.go | 8 + third_party/opa/rego/errors.go | 17 + third_party/opa/rego/plugins.go | 17 + third_party/opa/rego/rego.go | 628 + third_party/opa/rego/rego_test.go | 126 + third_party/opa/rego/resultset.go | 22 + third_party/opa/repl/doc.go | 8 + third_party/opa/repl/errors.go | 16 + third_party/opa/repl/repl.go | 26 + third_party/opa/repl/repl_test.go | 161 + third_party/opa/resolver/doc.go | 8 + third_party/opa/resolver/interface.go | 18 + third_party/opa/resolver/wasm/doc.go | 8 + third_party/opa/resolver/wasm/wasm.go | 20 + third_party/opa/runtime/doc.go | 10 + third_party/opa/runtime/logging.go | 21 + third_party/opa/runtime/runtime.go | 40 + third_party/opa/schemas/doc.go | 8 + third_party/opa/schemas/schemas.go | 13 + third_party/opa/sdk/doc.go | 8 + third_party/opa/sdk/opa.go | 41 + third_party/opa/sdk/opa_test.go | 87 + third_party/opa/sdk/test/doc.go | 8 + third_party/opa/sdk/test/test.go | 65 + .../opa/server/authorizer/authorizer.go | 71 + third_party/opa/server/authorizer/doc.go | 8 + third_party/opa/server/buffer.go | 15 + third_party/opa/server/doc.go | 10 + third_party/opa/server/features.go | 10 + third_party/opa/server/handlers/compress.go | 20 + third_party/opa/server/handlers/decoding.go | 19 + third_party/opa/server/handlers/doc.go | 8 + third_party/opa/server/identifier/certs.go | 18 + third_party/opa/server/identifier/doc.go | 8 + .../opa/server/identifier/identifier.go | 22 + third_party/opa/server/identifier/tls.go | 19 + third_party/opa/server/identifier/token.go | 15 + third_party/opa/server/server.go | 65 + third_party/opa/server/types/doc.go | 8 + third_party/opa/server/types/types.go | 245 + third_party/opa/server/writer/doc.go | 8 + third_party/opa/server/writer/writer.go | 56 + third_party/opa/storage/disk/config.go | 17 + third_party/opa/storage/disk/disk.go | 77 + third_party/opa/storage/disk/doc.go | 8 + third_party/opa/storage/doc.go | 10 + third_party/opa/storage/errors.go | 73 + third_party/opa/storage/inmem/doc.go | 8 + third_party/opa/storage/inmem/inmem.go | 56 + third_party/opa/storage/inmem/opts.go | 35 + third_party/opa/storage/inmem/test/doc.go | 8 + .../opa/storage/inmem/test/testutil.go | 22 + third_party/opa/storage/interface.go | 89 + third_party/opa/storage/path.go | 34 + third_party/opa/storage/storage.go | 53 + third_party/opa/test/authz/doc.go | 8 + third_party/opa/test/authz/testing.go | 43 + third_party/opa/test/cases/cases.go | 26 + third_party/opa/test/cases/doc.go | 8 + third_party/opa/test/e2e/doc.go | 8 + third_party/opa/test/e2e/logs/doc.go | 8 + third_party/opa/test/e2e/logs/utils.go | 16 + third_party/opa/test/e2e/testing.go | 49 + third_party/opa/tester/doc.go | 8 + third_party/opa/tester/reporter.go | 21 + third_party/opa/tester/runner.go | 71 + third_party/opa/tester/runner_test.go | 171 + third_party/opa/topdown/builtins.go | 67 + third_party/opa/topdown/builtins/builtins.go | 123 + third_party/opa/topdown/builtins/doc.go | 8 + third_party/opa/topdown/cache.go | 19 + third_party/opa/topdown/cache/cache.go | 64 + third_party/opa/topdown/cache/doc.go | 8 + third_party/opa/topdown/cancel.go | 18 + .../copypropagation/copypropagation.go | 34 + .../opa/topdown/copypropagation/doc.go | 8 + third_party/opa/topdown/doc.go | 14 + third_party/opa/topdown/errors.go | 54 + third_party/opa/topdown/graphql.go | 485 + third_party/opa/topdown/http.go | 17 + third_party/opa/topdown/instrumentation.go | 21 + third_party/opa/topdown/lineage/doc.go | 8 + third_party/opa/topdown/lineage/lineage.go | 39 + third_party/opa/topdown/print.go | 16 + third_party/opa/topdown/print/doc.go | 8 + third_party/opa/topdown/print/print.go | 14 + third_party/opa/topdown/query.go | 24 + third_party/opa/topdown/trace.go | 112 + third_party/opa/tracing/doc.go | 8 + third_party/opa/tracing/tracing.go | 45 + third_party/opa/types/decode.go | 14 + third_party/opa/types/doc.go | 8 + third_party/opa/types/types.go | 200 + third_party/opa/util/backoff.go | 23 + third_party/opa/util/close.go | 18 + third_party/opa/util/compare.go | 19 + third_party/opa/util/decoding/context.go | 24 + third_party/opa/util/decoding/doc.go | 8 + third_party/opa/util/doc.go | 10 + third_party/opa/util/enumflag.go | 19 + third_party/opa/util/graph.go | 34 + third_party/opa/util/hashmap.go | 20 + third_party/opa/util/json.go | 68 + third_party/opa/util/maps.go | 8 + third_party/opa/util/queue.go | 25 + third_party/opa/util/read_gzip_body.go | 17 + third_party/opa/util/test/benchmark.go | 58 + third_party/opa/util/test/ci_skip.go | 10 + third_party/opa/util/test/ci_skip_darwin.go | 13 + third_party/opa/util/test/doc.go | 10 + third_party/opa/util/test/tempfs.go | 31 + third_party/opa/util/test/tempus.go | 24 + third_party/opa/util/time.go | 38 + third_party/opa/util/wait.go | 19 + third_party/opa/v1/ast/annotations.go | 984 + third_party/opa/v1/ast/annotations_test.go | 1197 + third_party/opa/v1/ast/builtins.go | 3621 +++ third_party/opa/v1/ast/builtins_test.go | 62 + third_party/opa/v1/ast/capabilities.go | 285 + third_party/opa/v1/ast/capabilities_test.go | 311 + third_party/opa/v1/ast/check.go | 1329 + third_party/opa/v1/ast/check_test.go | 2586 ++ third_party/opa/v1/ast/compare.go | 429 + third_party/opa/v1/ast/compare_test.go | 787 + third_party/opa/v1/ast/compile.go | 6120 ++++ third_party/opa/v1/ast/compile_bench_test.go | 47 + third_party/opa/v1/ast/compile_test.go | 11562 +++++++ third_party/opa/v1/ast/compilehelper.go | 62 + third_party/opa/v1/ast/compilehelper_test.go | 232 + third_party/opa/v1/ast/compilemetrics.go | 9 + third_party/opa/v1/ast/conflicts.go | 79 + .../opa/v1/ast/default_module_loader.go | 14 + third_party/opa/v1/ast/doc.go | 36 + third_party/opa/v1/ast/env.go | 528 + third_party/opa/v1/ast/env_test.go | 559 + third_party/opa/v1/ast/errors.go | 124 + third_party/opa/v1/ast/errors_test.go | 41 + third_party/opa/v1/ast/example_test.go | 115 + third_party/opa/v1/ast/fuzz_test.go | 41 + third_party/opa/v1/ast/index.go | 968 + third_party/opa/v1/ast/index_test.go | 1403 + .../opa/v1/ast/internal/scanner/scanner.go | 478 + .../v1/ast/internal/scanner/scanner_test.go | 205 + .../opa/v1/ast/internal/tokens/tokens.go | 149 + third_party/opa/v1/ast/interning.go | 1222 + third_party/opa/v1/ast/interning_test.go | 66 + third_party/opa/v1/ast/json/json.go | 106 + third_party/opa/v1/ast/location/location.go | 132 + .../opa/v1/ast/location/location_test.go | 149 + third_party/opa/v1/ast/map.go | 108 + third_party/opa/v1/ast/map_test.go | 119 + third_party/opa/v1/ast/marshal_test.go | 1138 + third_party/opa/v1/ast/oracle/oracle.go | 382 + third_party/opa/v1/ast/oracle/oracle_test.go | 741 + third_party/opa/v1/ast/parser.go | 3018 ++ third_party/opa/v1/ast/parser_bench_test.go | 242 + third_party/opa/v1/ast/parser_ext.go | 814 + third_party/opa/v1/ast/parser_ext_test.go | 128 + third_party/opa/v1/ast/parser_test.go | 8272 +++++ third_party/opa/v1/ast/policy.go | 2005 ++ third_party/opa/v1/ast/policy_test.go | 1139 + third_party/opa/v1/ast/pretty.go | 82 + third_party/opa/v1/ast/pretty_test.go | 103 + third_party/opa/v1/ast/rego_compiler.go | 17 + third_party/opa/v1/ast/rego_v1.go | 208 + third_party/opa/v1/ast/schema.go | 54 + third_party/opa/v1/ast/schema_test.go | 1656 + third_party/opa/v1/ast/strings.go | 54 + third_party/opa/v1/ast/strings_bench_test.go | 27 + third_party/opa/v1/ast/syncpools.go | 92 + third_party/opa/v1/ast/term.go | 3424 +++ third_party/opa/v1/ast/term_bench_test.go | 498 + third_party/opa/v1/ast/term_test.go | 1577 + .../opa/v1/ast/testdata/_definitions.json | 18864 ++++++++++++ .../00000.stmt | 2 + .../00001.stmt | 2 + .../00002.stmt | 2 + .../00003.stmt | 2 + .../00004.stmt | 2 + .../00005.stmt | 2 + .../00006.stmt | 2 + .../00007.stmt | 2 + .../00008.stmt | 2 + .../00009.stmt | 2 + .../00010.stmt | 2 + .../00011.stmt | 2 + .../00012.stmt | 2 + .../00013.stmt | 2 + .../00014.stmt | 2 + .../00015.stmt | 2 + .../00016.stmt | 2 + .../00017.stmt | 2 + .../00018.stmt | 2 + .../00019.stmt | 2 + .../00020.stmt | 2 + .../00021.stmt | 2 + .../00022.stmt | 2 + .../00023.stmt | 2 + .../00024.stmt | 2 + .../00025.stmt | 2 + .../00026.stmt | 2 + .../00027.stmt | 2 + .../00028.stmt | 2 + .../00029.stmt | 2 + .../00030.stmt | 2 + .../00031.stmt | 2 + .../00032.stmt | 2 + .../00033.stmt | 2 + .../00034.stmt | 2 + .../00035.stmt | 2 + .../00036.stmt | 2 + .../00037.stmt | 2 + .../00038.stmt | 2 + .../00039.stmt | 2 + .../00040.stmt | 2 + .../00041.stmt | 2 + .../00042.stmt | 2 + .../00043.stmt | 2 + .../00044.stmt | 2 + .../00045.stmt | 2 + .../00046.stmt | 2 + .../00047.stmt | 2 + .../00048.stmt | 2 + .../00049.stmt | 2 + .../00050.stmt | 2 + .../00051.stmt | 2 + .../00052.stmt | 2 + .../00053.stmt | 2 + .../00054.stmt | 2 + .../00055.stmt | 2 + .../00056.stmt | 2 + .../00057.stmt | 2 + .../00058.stmt | 2 + .../00059.stmt | 2 + .../00060.stmt | 2 + .../00061.stmt | 2 + .../00062.stmt | 2 + .../00063.stmt | 2 + .../00064.stmt | 2 + .../00065.stmt | 2 + .../00066.stmt | 2 + .../00067.stmt | 2 + .../00068.stmt | 2 + .../00069.stmt | 2 + .../00070.stmt | 2 + .../00071.stmt | 2 + .../00072.stmt | 2 + .../00073.stmt | 2 + .../00074.stmt | 2 + .../00075.stmt | 2 + .../00076.stmt | 2 + .../00077.stmt | 2 + .../00078.stmt | 2 + .../00079.stmt | 2 + .../00080.stmt | 2 + .../00081.stmt | 2 + .../00082.stmt | 2 + .../00083.stmt | 2 + .../00084.stmt | 2 + .../00085.stmt | 2 + .../00086.stmt | 2 + .../00087.stmt | 2 + .../00088.stmt | 2 + .../00089.stmt | 2 + .../00090.stmt | 2 + .../00091.stmt | 2 + .../00092.stmt | 2 + .../00093.stmt | 2 + .../00094.stmt | 2 + .../00095.stmt | 2 + .../00096.stmt | 2 + .../00097.stmt | 2 + .../00098.stmt | 2 + .../00099.stmt | 2 + .../00100.stmt | 2 + .../00101.stmt | 2 + .../00102.stmt | 2 + .../00103.stmt | 2 + .../00104.stmt | 2 + .../00105.stmt | 2 + .../00106.stmt | 2 + .../00107.stmt | 2 + .../00108.stmt | 2 + .../00109.stmt | 2 + .../00110.stmt | 2 + .../00111.stmt | 2 + .../00112.stmt | 2 + .../00113.stmt | 2 + .../00114.stmt | 2 + .../00115.stmt | 2 + .../00116.stmt | 2 + .../00117.stmt | 2 + .../00118.stmt | 2 + .../00119.stmt | 2 + .../00120.stmt | 2 + .../00121.stmt | 2 + .../00122.stmt | 2 + .../00123.stmt | 2 + .../00124.stmt | 2 + .../00125.stmt | 2 + .../00126.stmt | 2 + .../00127.stmt | 2 + .../00128.stmt | 2 + .../00129.stmt | 2 + .../00130.stmt | 2 + .../00131.stmt | 2 + .../00132.stmt | 2 + .../00133.stmt | 2 + .../00134.stmt | 2 + .../00135.stmt | 2 + .../00136.stmt | 2 + .../00137.stmt | 2 + .../00138.stmt | 2 + .../00139.stmt | 2 + .../00140.stmt | 2 + .../00141.stmt | 2 + .../00142.stmt | 2 + .../00143.stmt | 2 + .../00144.stmt | 2 + .../00145.stmt | 2 + .../00146.stmt | 2 + .../00147.stmt | 2 + .../00148.stmt | 2 + .../00149.stmt | 2 + .../00150.stmt | 2 + .../00151.stmt | 2 + .../00152.stmt | 2 + .../00153.stmt | 2 + .../00154.stmt | 2 + .../00155.stmt | 2 + .../00156.stmt | 2 + .../00157.stmt | 2 + .../00158.stmt | 2 + .../00159.stmt | 2 + .../00160.stmt | 2 + .../00161.stmt | 2 + .../00162.stmt | 2 + .../00163.stmt | 2 + .../00164.stmt | 2 + .../00165.stmt | 2 + .../00166.stmt | 2 + .../00167.stmt | 2 + .../00168.stmt | 2 + .../00169.stmt | 2 + .../00170.stmt | 2 + .../00171.stmt | 2 + .../00172.stmt | 2 + .../00173.stmt | 2 + .../00174.stmt | 2 + .../00175.stmt | 2 + .../00176.stmt | 2 + .../00177.stmt | 2 + .../00178.stmt | 2 + .../00179.stmt | 2 + .../00180.stmt | 2 + .../00181.stmt | 2 + .../00182.stmt | 2 + .../00183.stmt | 2 + .../00184.stmt | 2 + .../00185.stmt | 2 + .../00186.stmt | 2 + .../00187.stmt | 2 + .../00188.stmt | 2 + .../00189.stmt | 2 + .../00190.stmt | 2 + .../00191.stmt | 2 + .../00192.stmt | 2 + .../00193.stmt | 2 + .../00194.stmt | 2 + .../00195.stmt | 2 + .../00196.stmt | 2 + .../00197.stmt | 2 + .../00198.stmt | 2 + .../00199.stmt | 2 + .../00200.stmt | 2 + .../00201.stmt | 2 + .../00202.stmt | 2 + .../00203.stmt | 2 + .../00204.stmt | 2 + .../00205.stmt | 2 + .../00206.stmt | 2 + .../00207.stmt | 2 + .../00208.stmt | 2 + .../00209.stmt | 2 + .../00210.stmt | 2 + .../00211.stmt | 2 + .../00212.stmt | 2 + .../00213.stmt | 2 + .../00214.stmt | 2 + .../00215.stmt | 2 + .../00216.stmt | 2 + .../00217.stmt | 2 + .../00218.stmt | 2 + .../00219.stmt | 2 + .../00220.stmt | 2 + .../00221.stmt | 2 + .../00222.stmt | 2 + .../00223.stmt | 2 + .../00224.stmt | 2 + .../00225.stmt | 2 + .../00226.stmt | 2 + .../00227.stmt | 2 + .../00228.stmt | 2 + .../00229.stmt | 2 + .../00230.stmt | 2 + .../00231.stmt | 2 + .../00232.stmt | 2 + .../00233.stmt | 2 + .../00234.stmt | 2 + .../00235.stmt | 2 + .../00236.stmt | 2 + .../00237.stmt | 2 + .../00238.stmt | 2 + .../00239.stmt | 2 + .../00240.stmt | 2 + .../00241.stmt | 2 + .../00242.stmt | 2 + .../00243.stmt | 2 + .../00244.stmt | 2 + .../00245.stmt | 2 + .../00246.stmt | 2 + .../00247.stmt | 2 + .../00248.stmt | 2 + .../00249.stmt | 2 + .../00250.stmt | 2 + .../00251.stmt | 2 + .../00252.stmt | 2 + .../00253.stmt | 2 + .../00254.stmt | 2 + .../00255.stmt | 2 + .../00256.stmt | 2 + .../00257.stmt | 2 + .../00258.stmt | 2 + .../00259.stmt | 2 + .../00260.stmt | 2 + .../00261.stmt | 2 + .../00262.stmt | 2 + .../00263.stmt | 2 + .../00264.stmt | 2 + .../00265.stmt | 2 + .../00266.stmt | 2 + .../00267.stmt | 2 + .../00268.stmt | 2 + .../00269.stmt | 2 + .../00270.stmt | 2 + .../00271.stmt | 2 + .../00272.stmt | 2 + .../00273.stmt | 2 + .../00274.stmt | 2 + .../00275.stmt | 2 + .../00276.stmt | 2 + .../00277.stmt | 2 + .../00278.stmt | 2 + .../00279.stmt | 2 + .../00280.stmt | 2 + .../00281.stmt | 2 + .../00282.stmt | 2 + .../00283.stmt | 2 + .../00284.stmt | 2 + .../00285.stmt | 2 + .../00286.stmt | 2 + .../00287.stmt | 2 + .../00288.stmt | 2 + .../00289.stmt | 2 + .../00290.stmt | 2 + .../00291.stmt | 2 + .../00292.stmt | 2 + .../00293.stmt | 2 + .../00294.stmt | 2 + .../00295.stmt | 2 + .../00296.stmt | 2 + .../00297.stmt | 2 + .../00298.stmt | 2 + .../00299.stmt | 2 + .../00300.stmt | 2 + .../00301.stmt | 2 + .../00302.stmt | 2 + .../00303.stmt | 2 + .../00304.stmt | 2 + .../00305.stmt | 2 + .../00306.stmt | 2 + .../00307.stmt | 2 + .../00308.stmt | 2 + .../00309.stmt | 2 + .../00310.stmt | 2 + .../00311.stmt | 2 + .../00312.stmt | 2 + .../00313.stmt | 2 + .../00314.stmt | 2 + .../00315.stmt | 2 + .../00316.stmt | 2 + .../00317.stmt | 2 + .../00318.stmt | 2 + .../00319.stmt | 2 + .../00320.stmt | 2 + .../00321.stmt | 2 + .../00322.stmt | 2 + .../00323.stmt | 2 + .../00324.stmt | 2 + .../00325.stmt | 2 + .../00326.stmt | 2 + .../00327.stmt | 2 + .../00328.stmt | 2 + .../00329.stmt | 2 + .../00330.stmt | 2 + .../00331.stmt | 2 + .../00332.stmt | 2 + .../00333.stmt | 2 + .../00334.stmt | 2 + .../00335.stmt | 2 + .../00336.stmt | 2 + .../00337.stmt | 2 + .../00338.stmt | 2 + .../00339.stmt | 2 + .../00340.stmt | 2 + .../00341.stmt | 2 + .../00342.stmt | 2 + .../00343.stmt | 2 + .../00344.stmt | 2 + .../00345.stmt | 2 + .../00346.stmt | 2 + .../00347.stmt | 2 + .../00348.stmt | 2 + .../00349.stmt | 2 + .../00350.stmt | 2 + .../00351.stmt | 2 + .../00352.stmt | 2 + .../00353.stmt | 2 + .../00354.stmt | 2 + .../00355.stmt | 2 + .../00356.stmt | 2 + .../00357.stmt | 2 + .../00358.stmt | 2 + .../00359.stmt | 2 + .../00360.stmt | 2 + .../00361.stmt | 2 + .../00362.stmt | 2 + .../00363.stmt | 2 + .../00364.stmt | 2 + .../00365.stmt | 2 + .../00366.stmt | 2 + .../00367.stmt | 2 + .../00368.stmt | 2 + .../00369.stmt | 2 + .../00370.stmt | 2 + .../00371.stmt | 2 + .../00372.stmt | 2 + .../00373.stmt | 2 + .../00374.stmt | 2 + .../00375.stmt | 2 + .../00376.stmt | 2 + .../00377.stmt | 2 + .../00378.stmt | 2 + .../00379.stmt | 2 + .../00380.stmt | 2 + .../00381.stmt | 2 + .../00382.stmt | 2 + .../00383.stmt | 2 + .../00384.stmt | 2 + .../00385.stmt | 2 + .../00386.stmt | 2 + .../00387.stmt | 2 + .../00388.stmt | 2 + .../00389.stmt | 2 + .../00390.stmt | 2 + .../00391.stmt | 2 + .../00392.stmt | 2 + .../00393.stmt | 2 + .../00394.stmt | 2 + .../00395.stmt | 2 + .../00396.stmt | 2 + .../00397.stmt | 2 + .../00398.stmt | 2 + .../00399.stmt | 2 + .../00400.stmt | 2 + .../00401.stmt | 2 + .../00402.stmt | 2 + .../00403.stmt | 2 + .../00404.stmt | 2 + .../00405.stmt | 2 + .../00406.stmt | 2 + .../00407.stmt | 2 + .../00408.stmt | 2 + .../00409.stmt | 2 + .../00410.stmt | 2 + .../00411.stmt | 2 + .../00412.stmt | 2 + .../00413.stmt | 2 + .../00414.stmt | 2 + .../00415.stmt | 2 + .../00416.stmt | 2 + .../00417.stmt | 2 + .../00418.stmt | 2 + .../00419.stmt | 2 + .../00420.stmt | 2 + .../00421.stmt | 2 + .../00422.stmt | 2 + .../00423.stmt | 2 + .../00424.stmt | 2 + .../00425.stmt | 2 + .../00426.stmt | 2 + .../00427.stmt | 2 + .../00428.stmt | 2 + .../00429.stmt | 2 + .../00430.stmt | 2 + .../00431.stmt | 2 + .../00432.stmt | 2 + .../00433.stmt | 2 + .../00434.stmt | 2 + .../00435.stmt | 2 + .../00436.stmt | 2 + .../00437.stmt | 2 + .../00438.stmt | 2 + .../00439.stmt | 2 + .../00440.stmt | 2 + .../00441.stmt | 2 + .../00442.stmt | 2 + .../00443.stmt | 2 + .../00444.stmt | 2 + .../00445.stmt | 2 + .../00446.stmt | 2 + .../00447.stmt | 2 + .../00448.stmt | 2 + .../00449.stmt | 2 + .../00450.stmt | 2 + .../00451.stmt | 2 + .../00452.stmt | 2 + .../00453.stmt | 2 + .../00454.stmt | 2 + .../00455.stmt | 2 + .../00456.stmt | 2 + .../00457.stmt | 2 + .../00458.stmt | 2 + .../00459.stmt | 2 + .../00460.stmt | 2 + .../00461.stmt | 2 + .../00462.stmt | 2 + .../00463.stmt | 2 + .../00464.stmt | 2 + .../00465.stmt | 2 + .../00466.stmt | 2 + .../00467.stmt | 2 + .../00468.stmt | 2 + .../00469.stmt | 2 + .../00470.stmt | 2 + .../00471.stmt | 2 + .../00472.stmt | 2 + .../00473.stmt | 2 + .../00474.stmt | 2 + .../00475.stmt | 2 + .../00476.stmt | 2 + .../00477.stmt | 2 + .../00478.stmt | 2 + .../00479.stmt | 2 + .../00480.stmt | 2 + .../00481.stmt | 2 + .../00482.stmt | 2 + .../00483.stmt | 2 + .../00484.stmt | 2 + .../00485.stmt | 2 + .../00486.stmt | 2 + .../00487.stmt | 2 + .../00488.stmt | 2 + .../00489.stmt | 2 + .../00490.stmt | 2 + .../00491.stmt | 2 + .../00492.stmt | 2 + .../00493.stmt | 2 + .../00494.stmt | 2 + .../00495.stmt | 2 + .../00496.stmt | 2 + .../00497.stmt | 2 + .../00498.stmt | 2 + .../00499.stmt | 2 + .../00500.stmt | 2 + .../00501.stmt | 2 + .../00502.stmt | 2 + .../00503.stmt | 2 + .../00504.stmt | 2 + .../00505.stmt | 2 + .../00506.stmt | 2 + .../00507.stmt | 2 + .../00508.stmt | 2 + .../00509.stmt | 2 + .../00510.stmt | 2 + .../00511.stmt | 2 + .../00512.stmt | 2 + .../00513.stmt | 2 + .../00514.stmt | 2 + .../00515.stmt | 2 + .../00516.stmt | 2 + .../00517.stmt | 2 + .../00518.stmt | 2 + .../00519.stmt | 2 + .../00520.stmt | 2 + .../00521.stmt | 2 + .../00522.stmt | 2 + .../00523.stmt | 2 + .../00524.stmt | 2 + .../00525.stmt | 2 + .../00526.stmt | 2 + .../00527.stmt | 2 + .../00528.stmt | 2 + .../00529.stmt | 2 + .../00530.stmt | 2 + .../00531.stmt | 2 + .../00532.stmt | 2 + .../00533.stmt | 2 + .../00534.stmt | 2 + .../00535.stmt | 2 + .../00536.stmt | 2 + .../00537.stmt | 2 + .../00538.stmt | 2 + .../00539.stmt | 2 + .../00540.stmt | 2 + .../00541.stmt | 2 + .../00542.stmt | 2 + .../00543.stmt | 2 + .../00544.stmt | 2 + .../00545.stmt | 2 + .../00546.stmt | 2 + .../00547.stmt | 2 + .../00548.stmt | 2 + .../00549.stmt | 2 + .../00550.stmt | 2 + .../00551.stmt | 2 + .../00552.stmt | 2 + .../00553.stmt | 2 + .../00554.stmt | 2 + .../00555.stmt | 2 + .../00556.stmt | 2 + .../00557.stmt | 2 + .../00558.stmt | 2 + .../00559.stmt | 2 + .../00560.stmt | 2 + .../00561.stmt | 2 + .../00562.stmt | 2 + .../00563.stmt | 2 + .../00564.stmt | 2 + .../00565.stmt | 2 + .../00566.stmt | 2 + .../00567.stmt | 2 + .../00568.stmt | 2 + .../00569.stmt | 2 + .../00570.stmt | 2 + .../00571.stmt | 2 + .../00572.stmt | 2 + .../00573.stmt | 2 + .../00574.stmt | 2 + .../00575.stmt | 2 + .../00576.stmt | 2 + .../00577.stmt | 2 + .../00578.stmt | 2 + .../00579.stmt | 2 + .../00580.stmt | 2 + .../00581.stmt | 2 + .../00582.stmt | 2 + .../00583.stmt | 2 + .../00584.stmt | 2 + .../00585.stmt | 2 + .../00586.stmt | 2 + .../00587.stmt | 2 + .../00588.stmt | 2 + .../00589.stmt | 2 + .../00590.stmt | 2 + .../00591.stmt | 2 + .../00592.stmt | 2 + .../00593.stmt | 2 + .../00594.stmt | 2 + .../00595.stmt | 2 + .../00596.stmt | 2 + .../00597.stmt | 2 + .../00598.stmt | 2 + .../00599.stmt | 2 + .../00600.stmt | 2 + .../00601.stmt | 2 + .../00602.stmt | 2 + .../00603.stmt | 2 + .../00604.stmt | 2 + .../00605.stmt | 2 + .../00606.stmt | 2 + .../00607.stmt | 2 + .../00608.stmt | 2 + .../00609.stmt | 2 + .../00610.stmt | 2 + .../00611.stmt | 2 + .../00612.stmt | 2 + .../00613.stmt | 2 + .../00614.stmt | 2 + .../00615.stmt | 2 + .../00616.stmt | 2 + .../00617.stmt | 2 + .../00618.stmt | 2 + .../00619.stmt | 2 + .../00620.stmt | 2 + .../00621.stmt | 2 + .../00622.stmt | 2 + .../00623.stmt | 2 + .../00624.stmt | 2 + .../00625.stmt | 2 + .../00626.stmt | 2 + .../00627.stmt | 2 + .../00628.stmt | 2 + .../00629.stmt | 2 + .../00630.stmt | 2 + .../00631.stmt | 2 + .../00632.stmt | 2 + .../00633.stmt | 2 + .../00634.stmt | 2 + .../00635.stmt | 2 + .../00636.stmt | 2 + .../00637.stmt | 2 + .../00638.stmt | 2 + .../00639.stmt | 2 + .../00640.stmt | 2 + .../00641.stmt | 2 + .../00642.stmt | 2 + .../00643.stmt | 2 + .../00644.stmt | 2 + .../00645.stmt | 2 + .../00646.stmt | 2 + .../00647.stmt | 2 + .../00648.stmt | 2 + .../00649.stmt | 2 + .../00650.stmt | 2 + .../00651.stmt | 2 + .../00652.stmt | 2 + .../00653.stmt | 2 + .../00654.stmt | 2 + .../00655.stmt | 2 + .../00656.stmt | 2 + .../00657.stmt | 2 + .../00658.stmt | 2 + .../00659.stmt | 2 + .../00660.stmt | 2 + .../00661.stmt | 2 + .../00662.stmt | 2 + .../00663.stmt | 2 + .../00664.stmt | 2 + .../00665.stmt | 2 + .../00666.stmt | 2 + .../00667.stmt | 2 + .../00668.stmt | 2 + .../00669.stmt | 2 + .../00670.stmt | 2 + .../00671.stmt | 2 + .../00672.stmt | 2 + .../00673.stmt | 2 + .../00674.stmt | 2 + .../00675.stmt | 2 + .../00676.stmt | 2 + .../00677.stmt | 2 + .../00678.stmt | 2 + .../00679.stmt | 2 + .../00680.stmt | 2 + .../00681.stmt | 2 + .../00682.stmt | 2 + .../00683.stmt | 2 + .../00684.stmt | 2 + .../00685.stmt | 2 + .../00686.stmt | 2 + .../00687.stmt | 2 + .../00688.stmt | 2 + .../00689.stmt | 2 + .../00690.stmt | 2 + .../00691.stmt | 2 + .../00692.stmt | 2 + .../00693.stmt | 2 + .../00694.stmt | 2 + .../00695.stmt | 2 + .../00696.stmt | 2 + .../00697.stmt | 2 + .../00698.stmt | 2 + .../00699.stmt | 2 + .../00700.stmt | 2 + .../00701.stmt | 2 + .../00702.stmt | 2 + .../00703.stmt | 2 + .../00704.stmt | 2 + .../00705.stmt | 2 + .../00706.stmt | 2 + .../00707.stmt | 2 + .../00708.stmt | 2 + .../00709.stmt | 2 + .../00710.stmt | 2 + .../00711.stmt | 2 + .../00712.stmt | 2 + .../00713.stmt | 2 + .../00714.stmt | 2 + .../00715.stmt | 2 + .../00716.stmt | 2 + .../00717.stmt | 2 + .../00718.stmt | 2 + .../00719.stmt | 2 + .../00720.stmt | 2 + .../00721.stmt | 2 + .../00722.stmt | 2 + .../00723.stmt | 2 + .../00724.stmt | 2 + .../00725.stmt | 2 + .../00726.stmt | 2 + .../00727.stmt | 2 + .../00728.stmt | 2 + .../00729.stmt | 2 + .../00730.stmt | 2 + .../00731.stmt | 2 + .../00732.stmt | 2 + .../00733.stmt | 2 + .../00734.stmt | 2 + .../00735.stmt | 2 + .../00736.stmt | 2 + .../00737.stmt | 2 + .../00738.stmt | 2 + .../00739.stmt | 2 + .../00740.stmt | 2 + .../00741.stmt | 2 + .../00742.stmt | 2 + .../00743.stmt | 2 + .../00744.stmt | 2 + .../00745.stmt | 2 + .../00746.stmt | 2 + .../00747.stmt | 2 + .../00748.stmt | 2 + .../00749.stmt | 2 + .../00750.stmt | 2 + .../00751.stmt | 2 + .../00752.stmt | 2 + .../00753.stmt | 2 + .../00754.stmt | 2 + .../00755.stmt | 2 + .../00756.stmt | 2 + .../00757.stmt | 2 + .../00758.stmt | 2 + .../00759.stmt | 2 + .../00760.stmt | 2 + .../00761.stmt | 2 + .../00762.stmt | 2 + .../00763.stmt | 2 + .../00764.stmt | 2 + .../00765.stmt | 2 + .../00766.stmt | 2 + .../00767.stmt | 2 + .../00768.stmt | 2 + .../00769.stmt | 2 + .../00770.stmt | 2 + .../00771.stmt | 2 + .../00772.stmt | 2 + .../00773.stmt | 2 + .../00774.stmt | 2 + .../00775.stmt | 2 + .../00776.stmt | 2 + .../00777.stmt | 2 + .../00778.stmt | 2 + .../00779.stmt | 2 + .../00780.stmt | 2 + .../00781.stmt | 2 + .../00782.stmt | 2 + .../00783.stmt | 2 + .../00784.stmt | 2 + .../00785.stmt | 2 + .../00786.stmt | 2 + .../00787.stmt | 2 + .../00788.stmt | 2 + .../00789.stmt | 2 + .../00790.stmt | 2 + .../00791.stmt | 2 + .../00792.stmt | 2 + .../00793.stmt | 2 + .../00794.stmt | 2 + .../00795.stmt | 2 + .../00796.stmt | 2 + .../00797.stmt | 2 + .../00798.stmt | 2 + .../00799.stmt | 2 + .../00800.stmt | 2 + .../00801.stmt | 2 + .../00802.stmt | 2 + .../00803.stmt | 2 + .../00804.stmt | 2 + .../00805.stmt | 2 + .../00806.stmt | 2 + .../00807.stmt | 2 + .../00808.stmt | 2 + .../00809.stmt | 2 + .../00810.stmt | 2 + .../00811.stmt | 2 + .../00812.stmt | 2 + .../00813.stmt | 2 + .../00814.stmt | 2 + .../00815.stmt | 2 + .../00816.stmt | 2 + .../00817.stmt | 2 + .../00818.stmt | 2 + .../00819.stmt | 2 + .../00820.stmt | 2 + .../00821.stmt | 2 + .../00822.stmt | 2 + .../00823.stmt | 2 + .../00824.stmt | 2 + .../00825.stmt | 2 + .../00826.stmt | 2 + .../00827.stmt | 2 + .../00828.stmt | 2 + .../00829.stmt | 2 + .../00830.stmt | 2 + .../00831.stmt | 2 + .../00832.stmt | 2 + .../00833.stmt | 2 + .../00834.stmt | 2 + .../00835.stmt | 2 + .../00836.stmt | 2 + .../00837.stmt | 2 + .../00838.stmt | 2 + .../00839.stmt | 2 + .../00840.stmt | 2 + .../00841.stmt | 2 + .../00842.stmt | 2 + .../00843.stmt | 2 + .../00844.stmt | 2 + .../00845.stmt | 2 + .../00846.stmt | 2 + .../00847.stmt | 2 + .../00848.stmt | 2 + .../00849.stmt | 2 + .../00850.stmt | 2 + .../00851.stmt | 2 + .../00852.stmt | 2 + .../00853.stmt | 2 + .../00854.stmt | 2 + .../00855.stmt | 2 + .../00856.stmt | 2 + .../00857.stmt | 2 + .../00858.stmt | 2 + .../00859.stmt | 2 + .../00860.stmt | 2 + .../00861.stmt | 2 + .../00862.stmt | 2 + .../00863.stmt | 2 + .../00864.stmt | 2 + .../00865.stmt | 2 + .../00866.stmt | 2 + .../00867.stmt | 2 + .../00868.stmt | 2 + .../00869.stmt | 2 + .../00870.stmt | 2 + .../00871.stmt | 2 + .../00872.stmt | 2 + .../00873.stmt | 2 + .../00874.stmt | 2 + .../00875.stmt | 2 + .../00876.stmt | 2 + .../00877.stmt | 2 + .../00878.stmt | 2 + .../00879.stmt | 2 + .../00880.stmt | 2 + .../00881.stmt | 2 + .../00882.stmt | 2 + .../00883.stmt | 2 + .../00884.stmt | 2 + .../00885.stmt | 2 + .../00886.stmt | 2 + .../00887.stmt | 2 + .../00888.stmt | 2 + .../00889.stmt | 2 + .../00890.stmt | 2 + .../00891.stmt | 2 + .../00892.stmt | 2 + .../00893.stmt | 2 + .../00894.stmt | 2 + .../00895.stmt | 2 + .../00896.stmt | 2 + .../00897.stmt | 2 + .../00898.stmt | 2 + .../00899.stmt | 2 + .../00900.stmt | 2 + .../00901.stmt | 2 + .../00902.stmt | 2 + .../00903.stmt | 2 + .../00904.stmt | 2 + .../00905.stmt | 2 + .../00906.stmt | 2 + .../00907.stmt | 2 + .../00908.stmt | 2 + .../00909.stmt | 2 + .../00910.stmt | 2 + .../00911.stmt | 2 + .../00912.stmt | 2 + .../00913.stmt | 2 + .../00914.stmt | 2 + .../00915.stmt | 2 + .../00916.stmt | 2 + .../00917.stmt | 2 + .../00918.stmt | 2 + .../00919.stmt | 2 + .../00920.stmt | 2 + .../00921.stmt | 2 + .../00922.stmt | 2 + .../00923.stmt | 2 + .../00924.stmt | 2 + .../00925.stmt | 2 + .../00926.stmt | 2 + .../00927.stmt | 2 + .../00928.stmt | 2 + .../00929.stmt | 2 + .../00930.stmt | 2 + .../00931.stmt | 2 + .../00932.stmt | 2 + .../00933.stmt | 2 + .../00934.stmt | 2 + .../00935.stmt | 2 + .../00936.stmt | 2 + .../00937.stmt | 2 + .../00938.stmt | 2 + .../00939.stmt | 2 + .../00940.stmt | 2 + .../00941.stmt | 2 + .../00942.stmt | 2 + .../00943.stmt | 2 + .../00944.stmt | 2 + .../00945.stmt | 2 + .../00946.stmt | 2 + .../00947.stmt | 2 + .../00948.stmt | 2 + .../00949.stmt | 2 + .../00950.stmt | 2 + .../00951.stmt | 2 + .../00952.stmt | 2 + .../00953.stmt | 2 + .../00954.stmt | 2 + .../00955.stmt | 2 + .../00956.stmt | 2 + .../00957.stmt | 2 + .../00958.stmt | 2 + .../00959.stmt | 2 + .../00960.stmt | 2 + .../00961.stmt | 2 + .../00962.stmt | 2 + .../00963.stmt | 2 + .../00964.stmt | 2 + .../00965.stmt | 2 + .../00966.stmt | 2 + .../00967.stmt | 2 + .../00968.stmt | 2 + .../00969.stmt | 2 + .../00970.stmt | 2 + .../00971.stmt | 2 + .../00972.stmt | 2 + .../00973.stmt | 2 + .../00974.stmt | 2 + .../00975.stmt | 2 + .../00976.stmt | 2 + .../00977.stmt | 2 + .../00978.stmt | 2 + .../00979.stmt | 2 + .../00980.stmt | 2 + .../00981.stmt | 2 + .../00982.stmt | 2 + .../00983.stmt | 2 + .../00984.stmt | 2 + .../00985.stmt | 2 + .../00986.stmt | 2 + .../00987.stmt | 2 + .../00988.stmt | 2 + .../00989.stmt | 2 + .../00990.stmt | 2 + .../00991.stmt | 2 + .../00992.stmt | 2 + .../00993.stmt | 2 + .../00994.stmt | 2 + .../00995.stmt | 2 + .../00996.stmt | 2 + .../00997.stmt | 2 + .../00998.stmt | 2 + .../00999.stmt | 2 + .../01000.stmt | 2 + .../01001.stmt | 2 + .../01002.stmt | 2 + .../01003.stmt | 2 + .../01004.stmt | 2 + .../01005.stmt | 2 + .../01006.stmt | 2 + .../01007.stmt | 2 + .../01008.stmt | 2 + .../01009.stmt | 2 + .../01010.stmt | 2 + .../01011.stmt | 2 + .../01012.stmt | 2 + .../01013.stmt | 2 + .../01014.stmt | 2 + .../01015.stmt | 2 + .../01016.stmt | 2 + .../01017.stmt | 2 + .../01018.stmt | 2 + .../01019.stmt | 2 + .../01020.stmt | 2 + .../01021.stmt | 2 + .../01022.stmt | 2 + .../01023.stmt | 2 + .../01024.stmt | 2 + .../01025.stmt | 2 + .../01026.stmt | 2 + .../01027.stmt | 2 + .../01028.stmt | 2 + .../01029.stmt | 2 + .../01030.stmt | 2 + .../01031.stmt | 2 + .../01032.stmt | 2 + .../01033.stmt | 2 + .../01034.stmt | 2 + .../01035.stmt | 2 + .../01036.stmt | 2 + .../01037.stmt | 2 + .../01038.stmt | 2 + .../01039.stmt | 2 + .../01040.stmt | 2 + .../01041.stmt | 2 + .../01042.stmt | 2 + .../01043.stmt | 2 + .../01044.stmt | 2 + .../01045.stmt | 2 + .../01046.stmt | 2 + .../01047.stmt | 2 + .../01048.stmt | 2 + .../01049.stmt | 2 + .../01050.stmt | 2 + .../01051.stmt | 2 + .../01052.stmt | 2 + .../01053.stmt | 2 + .../01054.stmt | 2 + .../01055.stmt | 2 + .../01056.stmt | 2 + .../01057.stmt | 2 + .../01058.stmt | 2 + .../01059.stmt | 2 + .../01060.stmt | 2 + .../01061.stmt | 2 + .../01062.stmt | 2 + .../01063.stmt | 2 + .../01064.stmt | 2 + .../01065.stmt | 2 + .../01066.stmt | 2 + .../01067.stmt | 2 + .../01068.stmt | 2 + .../01069.stmt | 2 + .../01070.stmt | 2 + .../01071.stmt | 2 + .../01072.stmt | 2 + .../01073.stmt | 2 + .../01074.stmt | 2 + .../01075.stmt | 2 + .../01076.stmt | 2 + .../01077.stmt | 2 + .../01078.stmt | 2 + .../01079.stmt | 2 + .../01080.stmt | 2 + .../01081.stmt | 2 + .../01082.stmt | 2 + .../01083.stmt | 2 + .../01084.stmt | 2 + .../01085.stmt | 2 + .../01086.stmt | 2 + .../01087.stmt | 2 + .../01088.stmt | 2 + .../01089.stmt | 2 + .../01090.stmt | 2 + .../01091.stmt | 2 + .../01092.stmt | 2 + .../01093.stmt | 2 + .../01094.stmt | 2 + .../01095.stmt | 2 + .../01096.stmt | 2 + .../01097.stmt | 2 + .../01098.stmt | 2 + .../01099.stmt | 2 + .../01100.stmt | 2 + .../01101.stmt | 2 + .../01102.stmt | 2 + .../01103.stmt | 2 + .../01104.stmt | 2 + .../01105.stmt | 2 + .../01106.stmt | 2 + .../01107.stmt | 2 + .../01108.stmt | 2 + .../01109.stmt | 2 + .../01110.stmt | 2 + .../01111.stmt | 2 + .../01112.stmt | 2 + .../01113.stmt | 2 + .../01114.stmt | 2 + .../01115.stmt | 2 + .../01116.stmt | 2 + .../01117.stmt | 2 + .../01118.stmt | 2 + .../01119.stmt | 2 + .../01120.stmt | 2 + .../01121.stmt | 2 + .../01122.stmt | 2 + .../01123.stmt | 2 + .../01124.stmt | 2 + .../01125.stmt | 2 + .../01126.stmt | 2 + .../01127.stmt | 2 + .../01128.stmt | 2 + .../01129.stmt | 2 + .../01130.stmt | 2 + .../01131.stmt | 2 + .../01132.stmt | 2 + .../01133.stmt | 2 + .../01134.stmt | 2 + .../01135.stmt | 2 + .../01136.stmt | 2 + .../01137.stmt | 2 + .../01138.stmt | 2 + .../01139.stmt | 2 + .../01140.stmt | 2 + .../01141.stmt | 2 + .../01142.stmt | 2 + .../01143.stmt | 2 + .../01144.stmt | 2 + .../01145.stmt | 2 + .../01146.stmt | 2 + .../01147.stmt | 2 + .../01148.stmt | 2 + .../01149.stmt | 2 + .../01150.stmt | 2 + .../01151.stmt | 2 + .../01152.stmt | 2 + .../01153.stmt | 2 + .../01154.stmt | 2 + .../01155.stmt | 2 + .../01156.stmt | 2 + .../01157.stmt | 2 + .../01158.stmt | 2 + .../01159.stmt | 2 + .../01160.stmt | 2 + .../01161.stmt | 2 + .../01162.stmt | 2 + .../01163.stmt | 2 + .../01164.stmt | 2 + .../01165.stmt | 2 + .../01166.stmt | 2 + .../01167.stmt | 2 + .../01168.stmt | 2 + .../01169.stmt | 2 + .../01170.stmt | 2 + .../01171.stmt | 2 + .../01172.stmt | 2 + .../01173.stmt | 2 + .../01174.stmt | 2 + .../01175.stmt | 2 + .../01176.stmt | 2 + .../01177.stmt | 2 + .../01178.stmt | 2 + .../01179.stmt | 2 + .../01180.stmt | 2 + .../01181.stmt | 2 + .../01182.stmt | 2 + .../01183.stmt | 2 + .../01184.stmt | 2 + .../01185.stmt | 2 + .../01186.stmt | 2 + .../01187.stmt | 2 + .../01188.stmt | 2 + .../01189.stmt | 2 + .../01190.stmt | 2 + .../01191.stmt | 2 + .../01192.stmt | 2 + .../01193.stmt | 2 + .../01194.stmt | 2 + .../01195.stmt | 2 + .../01196.stmt | 2 + .../01197.stmt | 2 + .../01198.stmt | 2 + .../01199.stmt | 2 + .../01200.stmt | 2 + .../01201.stmt | 2 + .../01202.stmt | 2 + .../01203.stmt | 2 + .../01204.stmt | 2 + .../01205.stmt | 2 + .../01206.stmt | 2 + .../01207.stmt | 2 + .../01208.stmt | 2 + .../01209.stmt | 2 + .../01210.stmt | 2 + .../01211.stmt | 2 + .../01212.stmt | 2 + .../01213.stmt | 2 + .../01214.stmt | 2 + .../01215.stmt | 2 + .../01216.stmt | 2 + .../01217.stmt | 2 + .../01218.stmt | 2 + .../01219.stmt | 2 + .../01220.stmt | 2 + .../01221.stmt | 2 + .../01222.stmt | 2 + .../01223.stmt | 2 + .../01224.stmt | 2 + .../01225.stmt | 2 + .../01226.stmt | 2 + .../01227.stmt | 2 + .../01228.stmt | 2 + .../01229.stmt | 2 + .../01230.stmt | 2 + .../01231.stmt | 2 + .../01232.stmt | 2 + .../01233.stmt | 2 + .../01234.stmt | 2 + .../01235.stmt | 2 + .../01236.stmt | 2 + .../01237.stmt | 2 + .../01238.stmt | 2 + .../01239.stmt | 2 + .../01240.stmt | 2 + .../01241.stmt | 2 + .../01242.stmt | 2 + .../01243.stmt | 2 + .../01244.stmt | 2 + .../01245.stmt | 2 + .../01246.stmt | 2 + .../01247.stmt | 2 + .../01248.stmt | 2 + .../01249.stmt | 2 + .../01250.stmt | 2 + .../01251.stmt | 2 + .../01252.stmt | 2 + .../01253.stmt | 2 + .../01254.stmt | 2 + .../01255.stmt | 2 + .../01256.stmt | 2 + .../01257.stmt | 2 + .../01258.stmt | 2 + .../01259.stmt | 2 + .../01260.stmt | 2 + .../01261.stmt | 2 + .../01262.stmt | 2 + .../01263.stmt | 2 + .../01264.stmt | 2 + .../01265.stmt | 2 + .../01266.stmt | 2 + .../01267.stmt | 2 + .../01268.stmt | 2 + .../01269.stmt | 2 + .../01270.stmt | 2 + .../01271.stmt | 2 + .../01272.stmt | 2 + .../01273.stmt | 2 + .../01274.stmt | 2 + .../01275.stmt | 2 + .../01276.stmt | 2 + .../01277.stmt | 2 + .../01278.stmt | 2 + .../01279.stmt | 2 + .../01280.stmt | 2 + .../01281.stmt | 2 + .../01282.stmt | 2 + .../01283.stmt | 2 + .../01284.stmt | 2 + .../01285.stmt | 2 + .../01286.stmt | 2 + .../01287.stmt | 2 + .../01288.stmt | 2 + .../01289.stmt | 2 + .../01290.stmt | 2 + .../01291.stmt | 2 + .../01292.stmt | 2 + .../01293.stmt | 2 + .../01294.stmt | 2 + .../01295.stmt | 2 + .../01296.stmt | 2 + .../01297.stmt | 2 + .../01298.stmt | 2 + .../01299.stmt | 2 + .../01300.stmt | 2 + .../01301.stmt | 2 + .../01302.stmt | 2 + .../01303.stmt | 2 + .../01304.stmt | 2 + .../01305.stmt | 2 + .../01306.stmt | 2 + .../01307.stmt | 2 + .../01308.stmt | 2 + .../01309.stmt | 2 + .../01310.stmt | 2 + .../01311.stmt | 2 + .../01312.stmt | 2 + .../01313.stmt | 2 + .../01314.stmt | 2 + .../01315.stmt | 2 + .../01316.stmt | 2 + .../01317.stmt | 2 + .../01318.stmt | 2 + .../01319.stmt | 2 + .../01320.stmt | 2 + .../01321.stmt | 2 + .../01322.stmt | 2 + .../01323.stmt | 2 + .../01324.stmt | 2 + .../01325.stmt | 2 + .../01326.stmt | 2 + .../01327.stmt | 2 + .../01328.stmt | 2 + .../01329.stmt | 2 + .../01330.stmt | 2 + .../01331.stmt | 2 + .../01332.stmt | 2 + .../01333.stmt | 2 + .../01334.stmt | 2 + .../01335.stmt | 2 + .../01336.stmt | 2 + .../01337.stmt | 2 + .../01338.stmt | 2 + .../01339.stmt | 2 + .../01340.stmt | 2 + .../01341.stmt | 2 + .../01342.stmt | 2 + .../01343.stmt | 2 + .../01344.stmt | 2 + .../01345.stmt | 2 + .../01346.stmt | 2 + .../01347.stmt | 2 + .../01348.stmt | 2 + .../01349.stmt | 2 + .../01350.stmt | 2 + .../01351.stmt | 2 + .../01352.stmt | 2 + .../01353.stmt | 2 + .../01354.stmt | 2 + .../01355.stmt | 2 + .../01356.stmt | 2 + .../01357.stmt | 2 + .../01358.stmt | 2 + .../01359.stmt | 2 + .../01360.stmt | 2 + .../01361.stmt | 2 + .../01362.stmt | 2 + .../01363.stmt | 2 + .../01364.stmt | 2 + .../01365.stmt | 2 + .../01366.stmt | 2 + .../01367.stmt | 2 + .../01368.stmt | 2 + .../01369.stmt | 2 + .../01370.stmt | 2 + .../01371.stmt | 2 + .../01372.stmt | 2 + .../01373.stmt | 2 + .../01374.stmt | 2 + .../01375.stmt | 2 + .../01376.stmt | 2 + .../01377.stmt | 2 + .../01378.stmt | 2 + .../01379.stmt | 2 + .../01380.stmt | 2 + .../01381.stmt | 2 + .../01382.stmt | 2 + .../01383.stmt | 2 + .../01384.stmt | 2 + .../01385.stmt | 2 + .../01386.stmt | 2 + .../01387.stmt | 2 + .../01388.stmt | 2 + .../01389.stmt | 2 + .../01390.stmt | 2 + .../01391.stmt | 2 + .../01392.stmt | 2 + .../01393.stmt | 2 + .../01394.stmt | 2 + .../01395.stmt | 2 + .../01396.stmt | 2 + .../01397.stmt | 2 + .../01398.stmt | 2 + .../01399.stmt | 2 + .../01400.stmt | 2 + .../01401.stmt | 2 + .../01402.stmt | 2 + .../01403.stmt | 2 + .../01404.stmt | 2 + .../01405.stmt | 2 + .../01406.stmt | 2 + .../01407.stmt | 2 + .../01408.stmt | 2 + .../01409.stmt | 2 + .../01410.stmt | 2 + .../01411.stmt | 2 + .../01412.stmt | 2 + .../01413.stmt | 2 + .../01414.stmt | 2 + .../01415.stmt | 2 + .../01416.stmt | 2 + .../01417.stmt | 2 + .../01418.stmt | 2 + .../01419.stmt | 2 + .../01420.stmt | 2 + .../01421.stmt | 2 + .../01422.stmt | 2 + .../01423.stmt | 2 + .../01424.stmt | 2 + .../01425.stmt | 2 + .../01426.stmt | 2 + .../01427.stmt | 2 + .../01428.stmt | 2 + .../01429.stmt | 2 + .../01430.stmt | 2 + .../01431.stmt | 2 + .../01432.stmt | 2 + .../01433.stmt | 2 + .../01434.stmt | 2 + .../01435.stmt | 2 + .../01436.stmt | 2 + .../01437.stmt | 2 + .../01438.stmt | 2 + .../01439.stmt | 2 + .../01440.stmt | 2 + .../01441.stmt | 2 + .../01442.stmt | 2 + .../01443.stmt | 2 + .../01444.stmt | 2 + .../01445.stmt | 2 + .../01446.stmt | 2 + .../01447.stmt | 2 + .../01448.stmt | 2 + .../01449.stmt | 2 + .../01450.stmt | 2 + .../01451.stmt | 2 + .../01452.stmt | 2 + .../01453.stmt | 2 + .../01454.stmt | 2 + .../01455.stmt | 2 + .../01456.stmt | 2 + .../01457.stmt | 2 + .../01458.stmt | 2 + .../01459.stmt | 2 + .../01460.stmt | 2 + .../01461.stmt | 2 + .../01462.stmt | 2 + .../01463.stmt | 2 + .../01464.stmt | 2 + .../01465.stmt | 2 + .../01466.stmt | 2 + .../01467.stmt | 2 + .../01468.stmt | 2 + .../01469.stmt | 2 + .../01470.stmt | 2 + .../01471.stmt | 2 + .../01472.stmt | 2 + .../01473.stmt | 2 + .../01474.stmt | 2 + .../01475.stmt | 2 + .../01476.stmt | 2 + .../01477.stmt | 2 + .../01478.stmt | 2 + .../01479.stmt | 2 + .../01480.stmt | 2 + .../01481.stmt | 2 + .../01482.stmt | 2 + .../01483.stmt | 2 + .../01484.stmt | 2 + .../01485.stmt | 2 + .../01486.stmt | 2 + .../01487.stmt | 2 + .../01488.stmt | 2 + .../01489.stmt | 2 + .../01490.stmt | 2 + .../01491.stmt | 2 + .../01492.stmt | 2 + .../01493.stmt | 2 + .../01494.stmt | 2 + .../01495.stmt | 2 + .../01496.stmt | 2 + .../01497.stmt | 2 + .../01498.stmt | 2 + .../01499.stmt | 2 + .../01500.stmt | 2 + .../01501.stmt | 2 + .../01502.stmt | 2 + .../01503.stmt | 2 + .../01504.stmt | 2 + .../01505.stmt | 2 + .../01506.stmt | 2 + .../01507.stmt | 2 + .../01508.stmt | 2 + .../01509.stmt | 2 + .../01510.stmt | 2 + .../01511.stmt | 2 + .../01512.stmt | 2 + .../01513.stmt | 2 + .../01514.stmt | 2 + .../01515.stmt | 2 + .../01516.stmt | 2 + .../01517.stmt | 2 + .../01518.stmt | 2 + .../01519.stmt | 2 + .../01520.stmt | 2 + .../01521.stmt | 2 + .../01522.stmt | 2 + .../01523.stmt | 2 + .../01524.stmt | 2 + .../01525.stmt | 2 + .../01526.stmt | 2 + .../01527.stmt | 2 + .../01528.stmt | 2 + .../01529.stmt | 2 + .../01530.stmt | 2 + .../01531.stmt | 2 + .../01532.stmt | 2 + .../01533.stmt | 2 + .../01534.stmt | 2 + .../01535.stmt | 2 + .../01536.stmt | 2 + .../01537.stmt | 2 + .../01538.stmt | 2 + .../01539.stmt | 2 + .../01540.stmt | 2 + .../01541.stmt | 2 + .../01542.stmt | 2 + .../01543.stmt | 2 + .../01544.stmt | 2 + .../01545.stmt | 2 + .../01546.stmt | 2 + .../01547.stmt | 2 + .../01548.stmt | 2 + .../01549.stmt | 2 + .../01550.stmt | 2 + .../01551.stmt | 2 + .../01552.stmt | 2 + .../01553.stmt | 2 + .../01554.stmt | 2 + .../01555.stmt | 2 + .../01556.stmt | 2 + .../01557.stmt | 2 + .../01558.stmt | 2 + .../01559.stmt | 2 + .../01560.stmt | 2 + .../01561.stmt | 2 + .../01562.stmt | 2 + .../01563.stmt | 2 + .../01564.stmt | 2 + .../01565.stmt | 2 + .../01566.stmt | 2 + .../01567.stmt | 2 + .../01568.stmt | 2 + .../01569.stmt | 2 + .../01570.stmt | 2 + .../01571.stmt | 2 + .../01572.stmt | 2 + .../01573.stmt | 2 + .../01574.stmt | 2 + .../01575.stmt | 2 + .../01576.stmt | 2 + .../01577.stmt | 2 + .../01578.stmt | 2 + .../01579.stmt | 2 + .../01580.stmt | 2 + .../01581.stmt | 2 + .../01582.stmt | 2 + .../01583.stmt | 2 + .../01584.stmt | 2 + .../01585.stmt | 2 + .../01586.stmt | 2 + .../01587.stmt | 2 + .../01588.stmt | 2 + .../01589.stmt | 2 + .../01590.stmt | 2 + .../01591.stmt | 2 + .../01592.stmt | 2 + .../01593.stmt | 2 + .../01594.stmt | 2 + .../01595.stmt | 2 + .../01596.stmt | 2 + .../01597.stmt | 2 + .../01598.stmt | 2 + .../01599.stmt | 2 + .../01600.stmt | 2 + .../01601.stmt | 2 + .../01602.stmt | 2 + .../01603.stmt | 2 + .../01604.stmt | 2 + .../01605.stmt | 2 + .../01606.stmt | 2 + .../01607.stmt | 2 + .../01608.stmt | 2 + .../01609.stmt | 2 + .../01610.stmt | 2 + .../01611.stmt | 2 + .../01612.stmt | 2 + .../01613.stmt | 2 + .../01614.stmt | 2 + .../01615.stmt | 2 + .../01616.stmt | 2 + .../01617.stmt | 2 + .../01618.stmt | 2 + .../01619.stmt | 2 + .../01620.stmt | 2 + .../01621.stmt | 2 + .../01622.stmt | 2 + .../01623.stmt | 2 + .../01624.stmt | 2 + .../01625.stmt | 2 + .../01626.stmt | 2 + .../01627.stmt | 2 + .../01628.stmt | 2 + .../01629.stmt | 2 + .../01630.stmt | 2 + .../01631.stmt | 2 + .../01632.stmt | 2 + .../01633.stmt | 2 + .../01634.stmt | 2 + .../01635.stmt | 2 + .../01636.stmt | 2 + .../01637.stmt | 2 + .../01638.stmt | 2 + .../01639.stmt | 2 + .../01640.stmt | 2 + .../01641.stmt | 2 + .../01642.stmt | 2 + .../01643.stmt | 2 + .../01644.stmt | 2 + .../01645.stmt | 2 + .../01646.stmt | 2 + .../01647.stmt | 2 + .../01648.stmt | 2 + .../01649.stmt | 2 + .../01650.stmt | 2 + .../01651.stmt | 2 + .../01652.stmt | 2 + .../01653.stmt | 2 + .../01654.stmt | 2 + .../01655.stmt | 2 + .../01656.stmt | 2 + .../01657.stmt | 2 + .../01658.stmt | 2 + .../01659.stmt | 2 + .../01660.stmt | 2 + .../01661.stmt | 2 + .../01662.stmt | 2 + .../01663.stmt | 2 + .../01664.stmt | 2 + .../01665.stmt | 2 + .../01666.stmt | 2 + .../01667.stmt | 2 + .../01668.stmt | 2 + .../01669.stmt | 2 + .../01670.stmt | 2 + .../01671.stmt | 2 + .../01672.stmt | 2 + .../01673.stmt | 2 + .../01674.stmt | 2 + .../01675.stmt | 2 + .../01676.stmt | 2 + .../01677.stmt | 2 + .../01678.stmt | 2 + .../01679.stmt | 2 + .../01680.stmt | 2 + .../01681.stmt | 2 + .../01682.stmt | 2 + .../01683.stmt | 2 + .../01684.stmt | 2 + .../01685.stmt | 2 + .../01686.stmt | 2 + .../01687.stmt | 2 + .../01688.stmt | 2 + .../01689.stmt | 2 + .../01690.stmt | 2 + .../01691.stmt | 2 + .../01692.stmt | 2 + .../01693.stmt | 2 + .../01694.stmt | 2 + .../01695.stmt | 2 + .../01696.stmt | 2 + .../01697.stmt | 2 + .../01698.stmt | 2 + .../01699.stmt | 2 + .../01700.stmt | 2 + .../01701.stmt | 2 + .../01702.stmt | 2 + .../01703.stmt | 2 + .../01704.stmt | 2 + .../01705.stmt | 2 + .../01706.stmt | 2 + .../01707.stmt | 2 + .../01708.stmt | 2 + .../01709.stmt | 2 + .../01710.stmt | 2 + .../01711.stmt | 2 + .../01712.stmt | 2 + .../01713.stmt | 2 + .../01714.stmt | 2 + .../01715.stmt | 2 + .../01716.stmt | 2 + .../01717.stmt | 2 + .../01718.stmt | 2 + .../01719.stmt | 2 + .../01720.stmt | 2 + .../01721.stmt | 2 + .../01722.stmt | 2 + .../01723.stmt | 2 + .../01724.stmt | 2 + .../01725.stmt | 2 + .../01726.stmt | 2 + .../01727.stmt | 2 + .../01728.stmt | 2 + .../01729.stmt | 2 + .../01730.stmt | 2 + .../01731.stmt | 2 + .../01732.stmt | 2 + .../01733.stmt | 2 + .../01734.stmt | 2 + .../01735.stmt | 2 + .../01736.stmt | 2 + .../01737.stmt | 2 + .../01738.stmt | 2 + .../01739.stmt | 2 + .../01740.stmt | 2 + .../01741.stmt | 2 + .../01742.stmt | 2 + .../01743.stmt | 2 + .../01744.stmt | 2 + .../01745.stmt | 2 + .../01746.stmt | 2 + .../01747.stmt | 2 + .../01748.stmt | 2 + .../01749.stmt | 2 + .../01750.stmt | 2 + .../01751.stmt | 2 + .../01752.stmt | 2 + .../01753.stmt | 2 + .../01754.stmt | 2 + .../01755.stmt | 2 + .../01756.stmt | 2 + .../01757.stmt | 2 + .../01758.stmt | 2 + .../01759.stmt | 2 + .../01760.stmt | 2 + .../01761.stmt | 2 + .../01762.stmt | 2 + .../01763.stmt | 2 + .../01764.stmt | 2 + .../01765.stmt | 2 + .../01766.stmt | 2 + .../01767.stmt | 2 + .../01768.stmt | 2 + .../01769.stmt | 2 + .../01770.stmt | 2 + .../01771.stmt | 2 + .../01772.stmt | 2 + .../01773.stmt | 2 + .../01774.stmt | 2 + .../01775.stmt | 2 + .../01776.stmt | 2 + .../01777.stmt | 2 + .../01778.stmt | 2 + .../01779.stmt | 2 + .../01780.stmt | 2 + .../01781.stmt | 2 + .../01782.stmt | 2 + .../01783.stmt | 2 + .../01784.stmt | 2 + .../01785.stmt | 2 + .../01786.stmt | 2 + .../01787.stmt | 2 + .../01788.stmt | 2 + .../01789.stmt | 2 + .../01790.stmt | 2 + .../01791.stmt | 2 + .../01792.stmt | 2 + .../01793.stmt | 2 + .../01794.stmt | 2 + .../01795.stmt | 2 + .../01796.stmt | 2 + .../01797.stmt | 2 + .../01798.stmt | 2 + .../01799.stmt | 2 + .../01800.stmt | 2 + .../01801.stmt | 2 + .../01802.stmt | 2 + .../01803.stmt | 2 + .../01804.stmt | 2 + .../01805.stmt | 2 + .../01806.stmt | 2 + .../01807.stmt | 2 + .../01808.stmt | 2 + .../01809.stmt | 2 + .../01810.stmt | 2 + .../01811.stmt | 2 + .../01812.stmt | 2 + .../01813.stmt | 2 + .../01814.stmt | 2 + .../01815.stmt | 2 + .../01816.stmt | 2 + .../01817.stmt | 2 + .../01818.stmt | 2 + .../01819.stmt | 2 + .../01820.stmt | 2 + .../01821.stmt | 2 + .../01822.stmt | 2 + .../01823.stmt | 2 + .../01824.stmt | 2 + .../01825.stmt | 2 + .../01826.stmt | 2 + .../01827.stmt | 2 + .../01828.stmt | 2 + .../01829.stmt | 2 + .../01830.stmt | 2 + .../01831.stmt | 2 + .../01832.stmt | 2 + .../01833.stmt | 2 + .../01834.stmt | 2 + .../01835.stmt | 2 + .../01836.stmt | 2 + .../01837.stmt | 2 + .../01838.stmt | 2 + .../01839.stmt | 2 + .../01840.stmt | 2 + .../01841.stmt | 2 + .../01842.stmt | 2 + .../01843.stmt | 2 + .../01844.stmt | 2 + .../01845.stmt | 2 + .../01846.stmt | 2 + .../01847.stmt | 2 + .../01848.stmt | 2 + .../01849.stmt | 2 + .../01850.stmt | 2 + .../01851.stmt | 2 + .../01852.stmt | 2 + .../01853.stmt | 2 + .../01854.stmt | 2 + .../01855.stmt | 2 + .../01856.stmt | 2 + .../01857.stmt | 2 + .../01858.stmt | 2 + .../01859.stmt | 2 + .../01860.stmt | 2 + .../01861.stmt | 2 + .../01862.stmt | 2 + .../01863.stmt | 2 + .../01864.stmt | 2 + .../01865.stmt | 2 + .../01866.stmt | 2 + .../01867.stmt | 2 + .../01868.stmt | 2 + .../01869.stmt | 2 + .../01870.stmt | 2 + .../01871.stmt | 2 + .../01872.stmt | 2 + .../01873.stmt | 2 + .../01874.stmt | 2 + .../01875.stmt | 2 + .../01876.stmt | 2 + .../01877.stmt | 2 + .../01878.stmt | 2 + .../01879.stmt | 2 + .../01880.stmt | 2 + .../01881.stmt | 2 + .../01882.stmt | 2 + .../01883.stmt | 2 + .../01884.stmt | 2 + .../01885.stmt | 2 + .../01886.stmt | 2 + .../01887.stmt | 2 + .../01888.stmt | 2 + .../01889.stmt | 2 + .../01890.stmt | 2 + .../01891.stmt | 2 + .../01892.stmt | 2 + .../01893.stmt | 2 + .../01894.stmt | 2 + .../01895.stmt | 2 + .../01896.stmt | 2 + .../01897.stmt | 2 + .../01898.stmt | 2 + .../01899.stmt | 2 + .../01900.stmt | 2 + .../01901.stmt | 2 + .../01902.stmt | 2 + .../01903.stmt | 2 + .../01904.stmt | 2 + .../01905.stmt | 2 + .../01906.stmt | 2 + .../01907.stmt | 2 + .../01908.stmt | 2 + .../01909.stmt | 2 + .../01910.stmt | 2 + .../01911.stmt | 2 + .../01912.stmt | 2 + .../01913.stmt | 2 + third_party/opa/v1/ast/transform.go | 431 + third_party/opa/v1/ast/transform_test.go | 137 + third_party/opa/v1/ast/unify.go | 240 + third_party/opa/v1/ast/unify_test.go | 124 + third_party/opa/v1/ast/varset.go | 121 + third_party/opa/v1/ast/version_index.json | 1471 + third_party/opa/v1/ast/visit.go | 832 + third_party/opa/v1/ast/visit_bench_test.go | 51 + third_party/opa/v1/ast/visit_test.go | 228 + third_party/opa/v1/bundle/bundle.go | 1845 ++ third_party/opa/v1/bundle/bundle_ext_test.go | 181 + third_party/opa/v1/bundle/bundle_test.go | 2114 ++ third_party/opa/v1/bundle/file.go | 517 + third_party/opa/v1/bundle/file_bench_test.go | 130 + third_party/opa/v1/bundle/file_test.go | 546 + third_party/opa/v1/bundle/filefs.go | 143 + third_party/opa/v1/bundle/filefs_test.go | 64 + third_party/opa/v1/bundle/hash.go | 136 + third_party/opa/v1/bundle/hash_test.go | 123 + third_party/opa/v1/bundle/keys.go | 144 + third_party/opa/v1/bundle/keys_test.go | 346 + third_party/opa/v1/bundle/sign.go | 132 + third_party/opa/v1/bundle/sign_test.go | 226 + third_party/opa/v1/bundle/store.go | 1245 + third_party/opa/v1/bundle/store_test.go | 7179 +++++ third_party/opa/v1/bundle/verify.go | 232 + third_party/opa/v1/bundle/verify_test.go | 315 + .../opa/v1/capabilities/capabilities.go | 18 + .../opa/v1/capabilities/capabilities_test.go | 36 + third_party/opa/v1/compile/compile.go | 1367 + .../opa/v1/compile/compile_bench_test.go | 123 + third_party/opa/v1/compile/compile_test.go | 3713 +++ third_party/opa/v1/config/config.go | 260 + third_party/opa/v1/config/config_test.go | 440 + third_party/opa/v1/cover/cover.go | 309 + third_party/opa/v1/cover/cover_bench_test.go | 74 + third_party/opa/v1/cover/cover_test.go | 246 + third_party/opa/v1/debug/README.md | 218 + third_party/opa/v1/debug/breakpoint.go | 151 + third_party/opa/v1/debug/debugger.go | 912 + third_party/opa/v1/debug/debugger_test.go | 2306 ++ third_party/opa/v1/debug/event.go | 55 + third_party/opa/v1/debug/frame.go | 95 + third_party/opa/v1/debug/latch.go | 38 + third_party/opa/v1/debug/thread.go | 534 + third_party/opa/v1/debug/trace.go | 109 + third_party/opa/v1/debug/variable.go | 182 + third_party/opa/v1/dependencies/deps.go | 464 + .../opa/v1/dependencies/deps_bench_test.go | 82 + third_party/opa/v1/dependencies/deps_test.go | 518 + third_party/opa/v1/dependencies/doc.go | 7 + third_party/opa/v1/doc.go | 9 + third_party/opa/v1/download/config.go | 85 + third_party/opa/v1/download/config_test.go | 100 + third_party/opa/v1/download/download.go | 444 + third_party/opa/v1/download/download_test.go | 1121 + third_party/opa/v1/download/oci_download.go | 461 + .../opa/v1/download/oci_download_test.go | 461 + .../v1/download/oci_download_unavailable.go | 59 + third_party/opa/v1/download/oci_downloader.go | 32 + .../opa/v1/download/testdata/config.layer | 1 + .../opa/v1/download/testdata/latest.manifest | 19 + .../opa/v1/download/testdata/latest.tar.gz | Bin 0 -> 610 bytes .../testdata/latest_bundle_data/.manifest | 1 + .../testdata/latest_bundle_data/data.json | 1 + .../opa/v1/download/testdata/rego_v1.manifest | 19 + .../opa/v1/download/testdata/rego_v1.tar.gz | Bin 0 -> 830 bytes .../rego_v1_bundle_data/a/b/c/data.json | 1 + .../http/policy/policy.rego | 9 + .../opa/v1/download/testdata/signed.manifest | 19 + .../opa/v1/download/testdata/signed.tar.gz | Bin 0 -> 764 bytes .../signed_bundle_data/a/b/c/data.json | 1 + .../http/policy/policy.rego | 1 + third_party/opa/v1/download/testharness.go | 557 + .../opa/v1/features/tracing/tracing.go | 34 + third_party/opa/v1/features/wasm/wasm.go | 94 + third_party/opa/v1/format/format.go | 2255 ++ third_party/opa/v1/format/format_test.go | 1028 + third_party/opa/v1/format/testdata/bench.rego | 802 + .../opa/v1/format/testfiles/v0/test.rego | 223 + .../v1/format/testfiles/v0/test.rego.error | 26 + .../format/testfiles/v0/test.rego.formatted | 243 + .../format/testfiles/v0/test_assignments.rego | 24 + .../v0/test_assignments.rego.formatted | 22 + .../v1/format/testfiles/v0/test_contains.rego | 12 + .../testfiles/v0/test_contains.rego.formatted | 19 + ...ontains.rego.formatted_no_keywords_in_refs | 19 + .../format/testfiles/v0/test_contains_if.rego | 17 + .../v0/test_contains_if.rego.formatted | 17 + .../v0/test_end_of_rule_comment.rego | 7 + .../test_end_of_rule_comment.rego.formatted | 7 + .../v1/format/testfiles/v0/test_every.rego | 19 + .../testfiles/v0/test_every.rego.formatted | 19 + .../testfiles/v0/test_every_with_key.rego | 21 + .../v0/test_every_with_key.rego.formatted | 21 + .../v0/test_fun_args_with_linebreaks.rego | 16 + ...st_fun_args_with_linebreaks.rego.formatted | 16 + .../format/testfiles/v0/test_functions.rego | 48 + .../v0/test_functions.rego.formatted | 52 + .../opa/v1/format/testfiles/v0/test_if.rego | 32 + .../testfiles/v0/test_if.rego.formatted | 34 + ...test_if.rego.formatted_no_keywords_in_refs | 34 + .../v1/format/testfiles/v0/test_if_else.rego | 20 + .../testfiles/v0/test_if_else.rego.formatted | 24 + ...if_else.rego.formatted_no_keywords_in_refs | 24 + .../opa/v1/format/testfiles/v0/test_in.rego | 13 + .../testfiles/v0/test_in.rego.formatted | 13 + ...test_in.rego.formatted_no_keywords_in_refs | 13 + ..._in_operator_with_all_keywords_import.rego | 9 + ...or_with_all_keywords_import.rego.formatted | 9 + .../v0/test_in_operator_with_parenthesis.rego | 19 + ...n_operator_with_parenthesis.rego.formatted | 20 + .../v0/test_in_operator_without_import.rego | 8 + ..._in_operator_without_import.rego.formatted | 10 + .../format/testfiles/v0/test_issue_1560.rego | 29 + .../v0/test_issue_1560.rego.formatted | 29 + .../format/testfiles/v0/test_issue_2299.rego | 40 + .../v0/test_issue_2299.rego.formatted | 40 + .../format/testfiles/v0/test_issue_2420.rego | 11 + .../v0/test_issue_2420.rego.formatted | 11 + .../format/testfiles/v0/test_issue_3836.rego | 6 + .../v0/test_issue_3836.rego.formatted | 6 + .../format/testfiles/v0/test_issue_3849.rego | 33 + .../v0/test_issue_3849.rego.formatted | 33 + .../format/testfiles/v0/test_issue_4606.rego | 6 + .../v0/test_issue_4606.rego.formatted | 8 + .../format/testfiles/v0/test_issue_5348.rego | 5 + .../v0/test_issue_5348.rego.formatted | 5 + .../format/testfiles/v0/test_issue_5449.rego | 3 + .../v0/test_issue_5449.rego.formatted | 5 + ...est_issue_5449_with_contains_ref_rule.rego | 9 + ...5449_with_contains_ref_rule.rego.formatted | 11 + .../v0/test_issue_5449_with_ref_rule.rego | 7 + ...st_issue_5449_with_ref_rule.rego.formatted | 9 + .../test_issue_5537_with_comprehension.rego | 3 + ...sue_5537_with_comprehension.rego.formatted | 3 + .../v0/test_issue_5537_with_ref.rego | 8 + .../test_issue_5537_with_ref.rego.formatted | 9 + .../format/testfiles/v0/test_issue_5798.rego | 9 + .../v0/test_issue_5798.rego.formatted | 9 + .../format/testfiles/v0/test_issue_6161.rego | 8 + .../v0/test_issue_6161.rego.formatted | 8 + .../format/testfiles/v0/test_issue_6330.rego | 45 + .../v0/test_issue_6330.rego.formatted | 31 + .../testfiles/v0/test_issue_6330_1.rego | 6 + .../v0/test_issue_6330_1.rego.formatted | 6 + .../testfiles/v0/test_keywords_in_refs.rego | 171 + .../v0/test_keywords_in_refs.rego.formatted | 231 + ...in_refs.rego.formatted_no_keywords_in_refs | 231 + .../test_keywords_in_refs_keep_brackets.rego | 171 + ...words_in_refs_keep_brackets.rego.formatted | 231 + .../format/testfiles/v0/test_ref_heads.rego | 20 + .../v0/test_ref_heads.rego.formatted | 35 + .../v1/format/testfiles/v0/test_rego_v1.rego | 26 + .../testfiles/v0/test_rego_v1.rego.formatted | 26 + .../v1/format/testfiles/v0/test_unicode.rego | 15 + .../testfiles/v0/test_unicode.rego.formatted | 16 + .../opa/v1/format/testfiles/v0/test_with.rego | 38 + .../testfiles/v0/test_with.rego.formatted | 37 + .../format/testfiles/v0_to_v1/constants.rego | 9 + .../v0_to_v1/constants.rego.formatted | 11 + .../v0_to_v1/deprecated_builtins.rego | 21 + .../v0_to_v1/deprecated_builtins.rego.error | 16 + .../testfiles/v0_to_v1/duplicate_imports.rego | 5 + .../v0_to_v1/duplicate_imports.rego.error | 3 + .../format/testfiles/v0_to_v1/functions.rego | 69 + .../v0_to_v1/functions.rego.formatted | 69 + .../testfiles/v0_to_v1/keyword_errors.rego | 13 + .../v0_to_v1/keyword_errors.rego.error | 5 + .../format/testfiles/v0_to_v1/keywords.rego | 6 + .../v0_to_v1/keywords.rego.formatted | 8 + .../testfiles/v0_to_v1/multi_value.rego | 32 + .../v0_to_v1/multi_value.rego.formatted | 31 + .../multi_value_no_future_imports.rego | 13 + ...lti_value_no_future_imports.rego.formatted | 15 + .../format/testfiles/v0_to_v1/shadowing.rego | 34 + .../testfiles/v0_to_v1/shadowing.rego.error | 13 + .../testfiles/v0_to_v1/single_value.rego | 35 + .../v0_to_v1/single_value.rego.formatted | 35 + .../single_value_no_future_imports.rego | 32 + ...gle_value_no_future_imports.rego.formatted | 34 + .../opa/v1/format/testfiles/v1/test.rego | 222 + .../v1/format/testfiles/v1/test.rego.error | 26 + .../format/testfiles/v1/test.rego.formatted | 234 + .../format/testfiles/v1/test_assignments.rego | 24 + .../v1/test_assignments.rego.formatted | 22 + .../v1/format/testfiles/v1/test_contains.rego | 13 + .../testfiles/v1/test_contains.rego.formatted | 17 + .../format/testfiles/v1/test_contains_if.rego | 10 + .../v1/test_contains_if.rego.formatted | 10 + .../testfiles/v1/test_else_strings.rego | 14 + .../v1/test_else_strings.rego.formatted | 13 + .../v1/test_end_of_rule_comment.rego | 7 + .../test_end_of_rule_comment.rego.formatted | 7 + .../v1/format/testfiles/v1/test_every.rego | 17 + .../testfiles/v1/test_every.rego.formatted | 17 + .../testfiles/v1/test_every_with_key.rego | 19 + .../v1/test_every_with_key.rego.formatted | 19 + .../v1/test_fun_args_with_linebreaks.rego | 16 + ...st_fun_args_with_linebreaks.rego.formatted | 16 + .../format/testfiles/v1/test_functions.rego | 48 + .../v1/test_functions.rego.formatted | 52 + .../testfiles/v1/test_future_kw_import.rego | 21 + .../v1/test_future_kw_import.rego.formatted | 21 + .../v1/format/testfiles/v1/test_grouping.rego | 49 + .../testfiles/v1/test_grouping.rego.formatted | 52 + .../opa/v1/format/testfiles/v1/test_if.rego | 25 + .../testfiles/v1/test_if.rego.formatted | 25 + .../v1/format/testfiles/v1/test_if_else.rego | 18 + .../testfiles/v1/test_if_else.rego.formatted | 22 + .../opa/v1/format/testfiles/v1/test_in.rego | 11 + .../testfiles/v1/test_in.rego.formatted | 11 + ..._in_operator_with_all_keywords_import.rego | 8 + ...or_with_all_keywords_import.rego.formatted | 8 + .../v1/test_in_operator_with_parenthesis.rego | 17 + ...n_operator_with_parenthesis.rego.formatted | 17 + .../v1/test_in_operator_without_import.rego | 8 + ..._in_operator_without_import.rego.formatted | 8 + .../format/testfiles/v1/test_issue_1560.rego | 29 + .../v1/test_issue_1560.rego.formatted | 29 + .../format/testfiles/v1/test_issue_2299.rego | 40 + .../v1/test_issue_2299.rego.formatted | 40 + .../format/testfiles/v1/test_issue_2420.rego | 11 + .../v1/test_issue_2420.rego.formatted | 11 + .../format/testfiles/v1/test_issue_3836.rego | 6 + .../v1/test_issue_3836.rego.formatted | 6 + .../format/testfiles/v1/test_issue_3849.rego | 33 + .../v1/test_issue_3849.rego.formatted | 33 + .../format/testfiles/v1/test_issue_4606.rego | 3 + .../v1/test_issue_4606.rego.formatted | 3 + .../format/testfiles/v1/test_issue_5348.rego | 5 + .../v1/test_issue_5348.rego.formatted | 5 + .../format/testfiles/v1/test_issue_5449.rego | 3 + .../v1/test_issue_5449.rego.formatted | 3 + ...est_issue_5449_with_contains_ref_rule.rego | 7 + ...5449_with_contains_ref_rule.rego.formatted | 7 + .../v1/test_issue_5449_with_ref_rule.rego | 7 + ...st_issue_5449_with_ref_rule.rego.formatted | 7 + .../test_issue_5537_with_comprehension.rego | 3 + ...sue_5537_with_comprehension.rego.formatted | 3 + .../v1/test_issue_5537_with_ref.rego | 8 + .../test_issue_5537_with_ref.rego.formatted | 8 + .../format/testfiles/v1/test_issue_5798.rego | 9 + .../v1/test_issue_5798.rego.formatted | 9 + .../format/testfiles/v1/test_issue_6161.rego | 8 + .../v1/test_issue_6161.rego.formatted | 8 + .../format/testfiles/v1/test_issue_6330.rego | 135 + .../v1/test_issue_6330.rego.formatted | 116 + .../testfiles/v1/test_issue_6330_1.rego | 9 + .../v1/test_issue_6330_1.rego.formatted | 9 + .../testfiles/v1/test_keywords_in_refs.rego | 227 + .../v1/test_keywords_in_refs.rego.formatted | 227 + ...in_refs.rego.formatted_no_keywords_in_refs | 227 + .../test_keywords_in_refs_keep_brackets.rego | 227 + ...words_in_refs_keep_brackets.rego.formatted | 227 + .../format/testfiles/v1/test_ref_heads.rego | 18 + .../v1/test_ref_heads.rego.formatted | 33 + .../v1/format/testfiles/v1/test_rego_v1.rego | 27 + .../testfiles/v1/test_rego_v1.rego.formatted | 28 + .../v1/format/testfiles/v1/test_unicode.rego | 22 + .../testfiles/v1/test_unicode.rego.formatted | 22 + .../opa/v1/format/testfiles/v1/test_with.rego | 38 + .../testfiles/v1/test_with.rego.formatted | 37 + third_party/opa/v1/hooks/hooks.go | 97 + .../opa/v1/ir/encoding/encoding_test.go | 95 + third_party/opa/v1/ir/ir.go | 486 + third_party/opa/v1/ir/marshal.go | 147 + third_party/opa/v1/ir/pretty.go | 44 + third_party/opa/v1/ir/walk.go | 93 + third_party/opa/v1/keys/keys.go | 99 + third_party/opa/v1/keys/keys_test.go | 212 + third_party/opa/v1/loader/errors.go | 62 + .../opa/v1/loader/extension/extension.go | 40 + .../opa/v1/loader/extension/extension_test.go | 56 + third_party/opa/v1/loader/filter/filter.go | 5 + third_party/opa/v1/loader/loader.go | 861 + third_party/opa/v1/loader/loader_test.go | 1490 + .../v1/loader/testdata/embedtest/bar/bar.rego | 4 + .../v1/loader/testdata/embedtest/bar/bar.yaml | 1 + .../testdata/embedtest/baz/qux/qux.json | 1 + .../opa/v1/loader/testdata/embedtest/foo.json | 1 + third_party/opa/v1/logging/logging.go | 274 + third_party/opa/v1/logging/logging_test.go | 181 + third_party/opa/v1/logging/test/test.go | 98 + third_party/opa/v1/metrics/metrics.go | 364 + .../opa/v1/metrics/metrics_bench_test.go | 61 + third_party/opa/v1/metrics/metrics_test.go | 61 + third_party/opa/v1/plugins/bundle/config.go | 256 + .../opa/v1/plugins/bundle/config_test.go | 508 + third_party/opa/v1/plugins/bundle/errors.go | 53 + .../opa/v1/plugins/bundle/errors_test.go | 144 + third_party/opa/v1/plugins/bundle/plugin.go | 875 + .../opa/v1/plugins/bundle/plugin_test.go | 7460 +++++ third_party/opa/v1/plugins/bundle/status.go | 135 + .../opa/v1/plugins/discovery/config.go | 152 + .../opa/v1/plugins/discovery/config_test.go | 185 + .../opa/v1/plugins/discovery/discovery.go | 793 + .../v1/plugins/discovery/discovery_test.go | 4291 +++ third_party/opa/v1/plugins/logs/README.md | 88 + third_party/opa/v1/plugins/logs/buffer.go | 64 + .../opa/v1/plugins/logs/buffer_test.go | 56 + third_party/opa/v1/plugins/logs/encoder.go | 470 + .../opa/v1/plugins/logs/encoder_test.go | 505 + .../opa/v1/plugins/logs/eventBuffer.go | 178 + .../opa/v1/plugins/logs/eventBuffer_test.go | 255 + third_party/opa/v1/plugins/logs/mask.go | 423 + third_party/opa/v1/plugins/logs/mask_test.go | 811 + third_party/opa/v1/plugins/logs/plugin.go | 1225 + .../v1/plugins/logs/plugin_benchmark_test.go | 267 + .../opa/v1/plugins/logs/plugin_test.go | 3921 +++ .../opa/v1/plugins/logs/status/status.go | 63 + third_party/opa/v1/plugins/plugins.go | 1175 + third_party/opa/v1/plugins/plugins_test.go | 513 + third_party/opa/v1/plugins/rest/auth.go | 1166 + third_party/opa/v1/plugins/rest/auth_test.go | 418 + third_party/opa/v1/plugins/rest/aws.go | 1088 + third_party/opa/v1/plugins/rest/aws_test.go | 2020 ++ third_party/opa/v1/plugins/rest/azure.go | 287 + third_party/opa/v1/plugins/rest/azure_test.go | 242 + third_party/opa/v1/plugins/rest/gcp.go | 173 + third_party/opa/v1/plugins/rest/gcp_test.go | 103 + third_party/opa/v1/plugins/rest/rest.go | 366 + third_party/opa/v1/plugins/rest/rest_test.go | 2874 ++ .../opa/v1/plugins/server/decoding/config.go | 102 + .../v1/plugins/server/decoding/config_test.go | 108 + .../opa/v1/plugins/server/encoding/config.go | 91 + .../v1/plugins/server/encoding/config_test.go | 105 + .../opa/v1/plugins/server/metrics/config.go | 93 + .../v1/plugins/server/metrics/config_test.go | 129 + third_party/opa/v1/plugins/status/metrics.go | 174 + third_party/opa/v1/plugins/status/plugin.go | 612 + .../opa/v1/plugins/status/plugin_test.go | 1430 + third_party/opa/v1/profiler/profiler.go | 395 + .../opa/v1/profiler/profiler_bench_test.go | 73 + third_party/opa/v1/profiler/profiler_test.go | 519 + third_party/opa/v1/refactor/refactor.go | 125 + third_party/opa/v1/refactor/refactor_test.go | 397 + third_party/opa/v1/rego/errors.go | 24 + third_party/opa/v1/rego/example_test.go | 1091 + third_party/opa/v1/rego/plugins.go | 43 + third_party/opa/v1/rego/plugins_test.go | 219 + third_party/opa/v1/rego/prepare_test.go | 41 + third_party/opa/v1/rego/rego.go | 3004 ++ third_party/opa/v1/rego/rego_bench_test.go | 508 + third_party/opa/v1/rego/rego_test.go | 3451 +++ .../opa/v1/rego/rego_wasmtarget_test.go | 450 + third_party/opa/v1/rego/resultset.go | 90 + third_party/opa/v1/rego/resultset_test.go | 78 + third_party/opa/v1/rego/testdata/aci/api.rego | 26 + .../opa/v1/rego/testdata/aci/data.json | 12 + .../opa/v1/rego/testdata/aci/framework.rego | 1831 ++ .../opa/v1/rego/testdata/aci/input.json | 12 + .../opa/v1/rego/testdata/aci/policy.rego | 89 + third_party/opa/v1/rego/testdata/ast.json | 7647 +++++ third_party/opa/v1/repl/errors.go | 38 + third_party/opa/v1/repl/example_test.go | 60 + third_party/opa/v1/repl/repl.go | 1646 + third_party/opa/v1/repl/repl_test.go | 3407 +++ .../opa/v1/repl/repl_wasmtarget_test.go | 80 + third_party/opa/v1/resolver/interface.go | 29 + third_party/opa/v1/resolver/wasm/wasm.go | 174 + .../opa/v1/runtime/check_user_linux.go | 23 + third_party/opa/v1/runtime/check_user_unix.go | 25 + .../opa/v1/runtime/check_user_windows.go | 14 + third_party/opa/v1/runtime/doc.go | 6 + third_party/opa/v1/runtime/logging.go | 274 + third_party/opa/v1/runtime/logging_test.go | 375 + third_party/opa/v1/runtime/plugins_test.go | 192 + third_party/opa/v1/runtime/runtime.go | 1078 + third_party/opa/v1/runtime/runtime_test.go | 2191 ++ .../opa/v1/schemas/authorizationPolicy.json | 43 + third_party/opa/v1/schemas/schemas.go | 15 + third_party/opa/v1/schemas/schemas_test.go | 33 + third_party/opa/v1/sdk/RawMapper.go | 17 + third_party/opa/v1/sdk/opa.go | 761 + third_party/opa/v1/sdk/opa_internal_test.go | 58 + third_party/opa/v1/sdk/opa_test.go | 3022 ++ third_party/opa/v1/sdk/options.go | 178 + third_party/opa/v1/sdk/test/test.go | 492 + third_party/opa/v1/sdk/testdata/Makefile | 7 + .../opa/v1/sdk/testdata/bundle/data.json | 6 + third_party/opa/v1/sdk/testdata/disco.tar.gz | Bin 0 -> 122 bytes .../opa/v1/sdk/testdata/v1bundle.tar.gz | Bin 0 -> 281 bytes .../opa/v1/sdk/testdata/v1bundle/.manifest | 3 + .../opa/v1/sdk/testdata/v1bundle/policy.rego | 9 + .../opa/v1/server/authorizer/authorizer.go | 307 + .../v1/server/authorizer/authorizer_test.go | 572 + third_party/opa/v1/server/buffer.go | 47 + third_party/opa/v1/server/cache.go | 53 + third_party/opa/v1/server/cache_test.go | 79 + third_party/opa/v1/server/certs.go | 213 + third_party/opa/v1/server/doc.go | 6 + third_party/opa/v1/server/features.go | 10 + .../opa/v1/server/handlers/compress.go | 194 + .../opa/v1/server/handlers/compress_test.go | 182 + .../opa/v1/server/handlers/decoding.go | 53 + .../opa/v1/server/handlers/handlers.go | 36 + third_party/opa/v1/server/identifier/certs.go | 25 + .../opa/v1/server/identifier/identifier.go | 30 + .../opa/v1/server/identifier/mock_test.go | 21 + .../v1/server/identifier/testdata/.gitignore | 5 + .../v1/server/identifier/testdata/gencerts.sh | 29 + third_party/opa/v1/server/identifier/tls.go | 32 + .../opa/v1/server/identifier/tls_test.go | 129 + third_party/opa/v1/server/identifier/token.go | 33 + .../opa/v1/server/identifier/token_test.go | 63 + third_party/opa/v1/server/server.go | 3184 ++ .../opa/v1/server/server_bench_test.go | 72 + third_party/opa/v1/server/server_test.go | 7042 +++++ third_party/opa/v1/server/types/types.go | 497 + third_party/opa/v1/server/writer/writer.go | 101 + third_party/opa/v1/storage/disk/config.go | 82 + .../opa/v1/storage/disk/config_test.go | 237 + third_party/opa/v1/storage/disk/disk.go | 1029 + third_party/opa/v1/storage/disk/disk_test.go | 1909 ++ third_party/opa/v1/storage/disk/errors.go | 24 + .../opa/v1/storage/disk/example_test.go | 86 + third_party/opa/v1/storage/disk/metrics.go | 51 + third_party/opa/v1/storage/disk/partition.go | 60 + .../opa/v1/storage/disk/partition_test.go | 118 + third_party/opa/v1/storage/disk/paths.go | 150 + third_party/opa/v1/storage/disk/paths_test.go | 63 + third_party/opa/v1/storage/disk/txn.go | 606 + third_party/opa/v1/storage/disk/txn_test.go | 134 + third_party/opa/v1/storage/doc.go | 6 + third_party/opa/v1/storage/errors.go | 121 + third_party/opa/v1/storage/errors_test.go | 27 + third_party/opa/v1/storage/inmem/ast.go | 313 + third_party/opa/v1/storage/inmem/ast_test.go | 200 + .../opa/v1/storage/inmem/example_test.go | 161 + third_party/opa/v1/storage/inmem/inmem.go | 460 + .../opa/v1/storage/inmem/inmem_test.go | 1352 + third_party/opa/v1/storage/inmem/opts.go | 37 + .../opa/v1/storage/inmem/test/testutil.go | 28 + third_party/opa/v1/storage/inmem/txn.go | 483 + third_party/opa/v1/storage/interface.go | 252 + .../opa/v1/storage/internal/errors/errors.go | 43 + .../v1/storage/internal/errors/errors_test.go | 22 + .../opa/v1/storage/internal/ptr/ptr.go | 120 + third_party/opa/v1/storage/path.go | 162 + third_party/opa/v1/storage/path_test.go | 208 + third_party/opa/v1/storage/storage.go | 139 + third_party/opa/v1/storage/storage_test.go | 104 + .../opa/v1/test/authz/authz_bench_test.go | 108 + third_party/opa/v1/test/authz/authz_test.go | 55 + third_party/opa/v1/test/authz/testing.go | 167 + third_party/opa/v1/test/cases/cases.go | 101 + .../v1/test/cases/internal/fmtcases/main.go | 124 + .../test/cases/internal/keywordrefs/main.go | 65 + .../test-keywords-in-ref_v0.yaml.template | 200 + .../test-keywords-in-ref_v1.yaml.template | 199 + .../v0/aggregates/test-aggregates-0001.yaml | 22 + .../v0/aggregates/test-aggregates-0002.yaml | 26 + .../v0/aggregates/test-aggregates-0003.yaml | 20 + .../v0/aggregates/test-aggregates-0004.yaml | 24 + .../v0/aggregates/test-aggregates-0005.yaml | 24 + .../v0/aggregates/test-aggregates-0006.yaml | 16 + .../v0/aggregates/test-aggregates-0007.yaml | 14 + .../v0/aggregates/test-aggregates-0008.yaml | 26 + .../v0/aggregates/test-aggregates-0009.yaml | 24 + .../v0/aggregates/test-aggregates-0010.yaml | 15 + .../v0/aggregates/test-aggregates-0011.yaml | 14 + .../v0/aggregates/test-aggregates-0012.yaml | 14 + .../v0/aggregates/test-aggregates-0013.yaml | 16 + .../v0/aggregates/test-aggregates-0014.yaml | 14 + .../v0/aggregates/test-aggregates-0015.yaml | 26 + .../v0/aggregates/test-aggregates-0016.yaml | 24 + .../v0/aggregates/test-aggregates-0017.yaml | 16 + .../v0/aggregates/test-aggregates-0018.yaml | 16 + .../v0/aggregates/test-aggregates-0019.yaml | 16 + .../v0/aggregates/test-aggregates-0020.yaml | 14 + .../v0/aggregates/test-aggregates-0021.yaml | 26 + .../v0/aggregates/test-aggregates-0022.yaml | 24 + .../v0/aggregates/test-aggregates-0023.yaml | 20 + .../v0/aggregates/test-aggregates-0024.yaml | 20 + .../v0/aggregates/test-aggregates-0025.yaml | 18 + .../v0/aggregates/test-aggregates-0026.yaml | 18 + .../v0/aggregates/test-aggregates-0027.yaml | 24 + .../v0/aggregates/test-aggregates-0028.yaml | 30 + .../test-aggregates-bad-utf8-runes.yaml | 16 + .../v0/aggregates/test-membership.yaml | 540 + .../cases/testdata/v0/all/test-all-0027.yaml | 15 + .../cases/testdata/v0/all/test-all-0028.yaml | 15 + .../cases/testdata/v0/all/test-all-0029.yaml | 15 + .../cases/testdata/v0/all/test-all-0030.yaml | 15 + .../cases/testdata/v0/all/test-all-0031.yaml | 15 + .../cases/testdata/v0/all/test-all-0032.yaml | 15 + .../cases/testdata/v0/all/test-all-0033.yaml | 15 + .../cases/testdata/v0/any/test-any-0034.yaml | 15 + .../cases/testdata/v0/any/test-any-0035.yaml | 15 + .../cases/testdata/v0/any/test-any-0036.yaml | 15 + .../cases/testdata/v0/any/test-any-0037.yaml | 15 + .../cases/testdata/v0/any/test-any-0038.yaml | 15 + .../cases/testdata/v0/any/test-any-0039.yaml | 15 + .../cases/testdata/v0/any/test-any-0040.yaml | 15 + .../v0/arithmetic/test-arithmetic-0810.yaml | 26 + .../v0/arithmetic/test-arithmetic-0811.yaml | 23 + .../v0/arithmetic/test-arithmetic-0812.yaml | 26 + .../v0/arithmetic/test-arithmetic-0813.yaml | 24 + .../v0/arithmetic/test-arithmetic-0814.yaml | 19 + .../v0/arithmetic/test-arithmetic-0815.yaml | 15 + .../v0/arithmetic/test-arithmetic-0816.yaml | 15 + .../v0/arithmetic/test-arithmetic-0817.yaml | 16 + .../v0/arithmetic/test-arithmetic-0818.yaml | 15 + .../v0/arithmetic/test-arithmetic-0819.yaml | 20 + .../v0/arithmetic/test-arithmetic-0820.yaml | 20 + .../v0/arithmetic/test-arithmetic-0821.yaml | 20 + .../v0/arithmetic/test-arithmetic-0822.yaml | 20 + .../v0/arithmetic/test-arithmetic-0823.yaml | 15 + .../v0/arithmetic/test-arithmetic-0824.yaml | 62 + .../v0/arithmetic/test-arithmetic-0825.yaml | 62 + .../test-arithmetic-ll-overflow.yaml | 38 + .../test-arithmetic-minus-type-error.yaml | 28 + .../v0/arithmetic/test-big-int-0001.yaml | 14 + .../testdata/v0/array/test-array-0041.yaml | 19 + .../testdata/v0/array/test-array-0042.yaml | 20 + .../testdata/v0/array/test-array-0043.yaml | 20 + .../testdata/v0/array/test-array-0044.yaml | 17 + .../testdata/v0/array/test-array-0045.yaml | 15 + .../testdata/v0/array/test-array-0046.yaml | 15 + .../testdata/v0/array/test-array-0047.yaml | 15 + .../testdata/v0/array/test-array-0048.yaml | 17 + .../testdata/v0/array/test-array-0049.yaml | 17 + .../testdata/v0/array/test-array-0050.yaml | 15 + .../testdata/v0/array/test-array-0051.yaml | 15 + .../testdata/v0/array/test-array-0052.yaml | 44 + .../test-file-level-assignments.yaml | 49 + .../test-base64builtins-0929.yaml | 14 + .../test-base64builtins-0930.yaml | 14 + .../test-base64builtins-0931.yaml | 14 + .../test-base64builtins-0932.yaml | 14 + .../test-base64builtins-0933.yaml | 14 + .../test-base64builtins-0934.yaml | 14 + .../test-base64builtins-0935.yaml | 26 + .../test-base64urlbuiltins-0935.yaml | 26 + .../test-base64urlbuiltins-0937.yaml | 26 + .../test-base64urlbuiltins-0939.yaml | 24 + .../test-baseandvirtualdocs-0695.yaml | 189 + .../test-baseandvirtualdocs-0696.yaml | 273 + .../test-baseandvirtualdocs-0697.yaml | 293 + .../test-baseandvirtualdocs-0698.yaml | 170 + .../test-baseandvirtualdocs-0699.yaml | 156 + .../test-baseandvirtualdocs-0700.yaml | 153 + .../test-baseandvirtualdocs-0701.yaml | 165 + .../test-baseandvirtualdocs-0702.yaml | 153 + .../test-baseandvirtualdocs-0703.yaml | 156 + .../test-baseandvirtualdocs-0704.yaml | 157 + .../test-baseandvirtualdocs-0705.yaml | 158 + .../v0/bitsand/test-bitsand-0055.yaml | 17 + .../v0/bitsand/test-bitsand-0056.yaml | 17 + .../v0/bitsand/test-bitsand-0057.yaml | 16 + .../v0/bitsnegate/test-bitsnegate-0058.yaml | 17 + .../v0/bitsnegate/test-bitsnegate-0059.yaml | 18 + .../testdata/v0/bitsor/test-bitsor-0052.yaml | 17 + .../testdata/v0/bitsor/test-bitsor-0053.yaml | 17 + .../testdata/v0/bitsor/test-bitsor-0054.yaml | 16 + .../test-bitsshiftleft-0063.yaml | 17 + .../test-bitsshiftleft-0064.yaml | 16 + .../test-bitsshiftleft-0065.yaml | 18 + .../test-bitsshiftleft-0066.yaml | 15 + .../test-bitsshiftleft-0067.yaml | 15 + .../test-bitsshiftright-0068.yaml | 17 + .../test-bitsshiftright-0069.yaml | 16 + .../test-bitsshiftright-0070.yaml | 18 + .../v0/bitsxor/test-bitsxor-0060.yaml | 17 + .../v0/bitsxor/test-bitsxor-0061.yaml | 17 + .../v0/bitsxor/test-bitsxor-0062.yaml | 16 + .../testdata/v0/casts/test-casts-0077.yaml | 14 + .../testdata/v0/casts/test-casts-0078.yaml | 15 + .../testdata/v0/casts/test-casts-0079.yaml | 19 + .../testdata/v0/casts/test-casts-0080.yaml | 14 + .../testdata/v0/casts/test-casts-0081.yaml | 15 + .../testdata/v0/casts/test-casts-0082.yaml | 14 + .../testdata/v0/casts/test-casts-0083.yaml | 15 + .../testdata/v0/casts/test-casts-0824.yaml | 23 + .../testdata/v0/casts/test-casts-0825.yaml | 20 + .../testdata/v0/casts/test-casts-0826.yaml | 20 + .../testdata/v0/casts/test-casts-0827.yaml | 15 + .../test-comparisonexpr-0608.yaml | 21 + .../test-comparisonexpr-0609.yaml | 21 + .../test-comparisonexpr-0610.yaml | 21 + .../test-comparisonexpr-0611.yaml | 21 + .../test-comparisonexpr-0612.yaml | 21 + .../test-comparisonexpr-0613.yaml | 21 + .../test-comparisonexpr-0614.yaml | 13 + .../test-comparisonexpr-0615.yaml | 13 + .../test-comparisonexpr-0616.yaml | 13 + .../test-comparisonexpr-0617.yaml | 13 + .../test-comparisonexpr-0618.yaml | 13 + .../test-comparisonexpr-0619.yaml | 12 + .../test-comparisonexpr-0620.yaml | 48 + .../v0/completedoc/test-completedoc-0495.yaml | 13 + .../v0/completedoc/test-completedoc-0496.yaml | 12 + .../v0/completedoc/test-completedoc-0497.yaml | 12 + .../v0/completedoc/test-completedoc-0498.yaml | 12 + .../v0/completedoc/test-completedoc-0499.yaml | 12 + .../v0/completedoc/test-completedoc-0500.yaml | 12 + .../v0/completedoc/test-completedoc-0501.yaml | 12 + .../v0/completedoc/test-completedoc-0502.yaml | 12 + .../v0/completedoc/test-completedoc-0503.yaml | 12 + .../v0/completedoc/test-completedoc-0504.yaml | 16 + .../v0/completedoc/test-completedoc-0505.yaml | 12 + .../v0/completedoc/test-completedoc-0506.yaml | 18 + .../v0/completedoc/test-completedoc-0507.yaml | 14 + .../v0/completedoc/test-completedoc-0508.yaml | 17 + .../v0/completedoc/test-completedoc-0509.yaml | 18 + .../v0/completedoc/test-completedoc-0510.yaml | 17 + .../test-compositebasedereference-1073.yaml | 19 + .../test-compositebasedereference-1074.yaml | 19 + .../test-compositebasedereference-1075.yaml | 19 + .../test-compositereferences-0743.yaml | 39 + .../test-compositereferences-0744.yaml | 38 + .../test-compositereferences-0745.yaml | 39 + .../test-compositereferences-0746.yaml | 40 + .../test-compositereferences-0747.yaml | 38 + .../test-compositereferences-0748.yaml | 40 + .../test-compositereferences-0749.yaml | 47 + .../test-compositereferences-0750.yaml | 47 + .../test-compositereferences-0751.yaml | 36 + .../test-compositereferences-0752.yaml | 40 + .../test-compositereferences-0753.yaml | 41 + .../test-compositereferences-0754.yaml | 41 + .../test-compositereferences-0755.yaml | 36 + .../test-compositereferences-0756.yaml | 36 + .../test-compositereferences-0757.yaml | 36 + .../test-comprehensions-0781.yaml | 25 + .../test-comprehensions-0782.yaml | 25 + .../test-comprehensions-0783.yaml | 26 + .../test-comprehensions-0784.yaml | 26 + .../test-comprehensions-0785.yaml | 24 + .../test-comprehensions-0786.yaml | 17 + .../test-comprehensions-0787.yaml | 29 + .../test-comprehensions-0788.yaml | 25 + .../test-comprehensions-0789.yaml | 25 + .../test-comprehensions-0790.yaml | 19 + .../test-comprehensions-0791.yaml | 26 + .../test-comprehensions-0792.yaml | 26 + .../test-comprehensions-0793.yaml | 24 + .../test-comprehensions-0794.yaml | 17 + .../test-comprehensions-0795.yaml | 24 + .../test-comprehensions-0796.yaml | 24 + .../test-comprehensions-0797.yaml | 22 + .../test-comprehensions-0798.yaml | 25 + .../test-comprehensions-0799.yaml | 26 + .../test-comprehensions-0800.yaml | 26 + .../test-comprehensions-0801.yaml | 24 + .../test-comprehensions-0802.yaml | 17 + .../test-comprehensions-0803.yaml | 29 + .../test-comprehensions-and-vars.yaml | 17 + .../test-contains-future-keyword.yaml | 68 + .../cryptohmacequal/test-cryptohmacequal.yaml | 84 + .../v0/cryptohmacmd5/test-cryptohmacmd5.yaml | 29 + .../cryptohmacsha1/test-cryptohmacsha1.yaml | 29 + .../test-cryptohmacsha256.yaml | 29 + .../test-cryptohmacsha512.yaml | 29 + .../v0/cryptomd5/test-cryptomd5-0130.yaml | 17 + .../test-cryptoparsersaprivatekey-1.yaml | 36 + .../v0/cryptosha1/test-cryptosha1-0131.yaml | 17 + .../cryptosha256/test-cryptosha256-0132.yaml | 17 + ...-cryptox509parseandverifycertificates.yaml | 138 + ...ryptox509parsecertificaterequest-0125.yaml | 19 + ...ryptox509parsecertificaterequest-0126.yaml | 19 + ...ryptox509parsecertificaterequest-0127.yaml | 19 + ...ryptox509parsecertificaterequest-0128.yaml | 20 + ...ryptox509parsecertificaterequest-0129.yaml | 20 + ...test-cryptox509parsecertificates-0117.yaml | 20 + ...test-cryptox509parsecertificates-0118.yaml | 21 + ...test-cryptox509parsecertificates-0119.yaml | 20 + ...test-cryptox509parsecertificates-0120.yaml | 20 + ...test-cryptox509parsecertificates-0121.yaml | 22 + ...test-cryptox509parsecertificates-0122.yaml | 22 + ...test-cryptox509parsecertificates-0123.yaml | 20 + ...test-cryptox509parsecertificates-0124.yaml | 20 + ...-cryptox509parsecertificates-raw-uris.yaml | 27 + .../test-cryptox509parsekeypairs-0118.yaml | 15 + .../test-cryptox509parsekeypairs-0119.yaml | 15 + .../test-cryptox509parsersaprivatekey-1.yaml | 34 + .../v0/dataderef/test-data-derefs.yaml | 35 + .../test-default-functions.yaml | 40 + .../test-defaultkeyword-0804.yaml | 20 + .../test-defaultkeyword-0805.yaml | 18 + .../test-defaultkeyword-0806.yaml | 18 + .../test-defaultkeyword-0807.yaml | 25 + .../test-defaultkeyword-0808.yaml | 22 + .../test-defaultkeyword-0809.yaml | 25 + .../v0/disjunction/test-disjunction-0763.yaml | 33 + .../v0/disjunction/test-disjunction-0764.yaml | 27 + .../v0/disjunction/test-disjunction-0765.yaml | 48 + .../v0/disjunction/test-disjunction-0766.yaml | 16 + .../v0/disjunction/test-disjunction-0767.yaml | 37 + .../v0/disjunction/test-disjunction-0768.yaml | 38 + .../v0/disjunction/test-disjunction-0769.yaml | 52 + .../v0/disjunction/test-disjunction-0770.yaml | 20 + .../v0/disjunction/test-disjunction-0771.yaml | 17 + .../v0/disjunction/test-disjunction-0772.yaml | 18 + .../v0/disjunction/test-disjunction-0773.yaml | 14 + .../v0/disjunction/test-disjunction-0774.yaml | 16 + .../v0/disjunction/test-disjunction-0775.yaml | 211 + .../v0/disjunction/test-disjunction-0776.yaml | 18 + .../v0/elsekeyword/test-elsekeyword-1054.yaml | 16 + .../v0/elsekeyword/test-elsekeyword-1055.yaml | 16 + .../v0/elsekeyword/test-elsekeyword-1056.yaml | 17 + .../v0/elsekeyword/test-elsekeyword-1057.yaml | 18 + .../v0/elsekeyword/test-elsekeyword-1058.yaml | 26 + .../v0/elsekeyword/test-elsekeyword-1059.yaml | 28 + .../v0/elsekeyword/test-elsekeyword-1060.yaml | 18 + .../v0/elsekeyword/test-elsekeyword-1061.yaml | 18 + .../v0/elsekeyword/test-elsekeyword-1062.yaml | 20 + .../v0/elsekeyword/test-elsekeyword-1063.yaml | 28 + .../v0/elsekeyword/test-elsekeyword-1064.yaml | 34 + .../v0/elsekeyword/test-elsekeyword-1065.yaml | 18 + .../v0/elsekeyword/test-elsekeyword-1066.yaml | 25 + .../v0/elsekeyword/test-elsekeyword-1067.yaml | 27 + .../test-embeddedvirtualdoc-0976.yaml | 45 + .../testdata/v0/eqexpr/test-eqexpr-0545.yaml | 13 + .../testdata/v0/eqexpr/test-eqexpr-0546.yaml | 13 + .../testdata/v0/eqexpr/test-eqexpr-0547.yaml | 18 + .../testdata/v0/eqexpr/test-eqexpr-0548.yaml | 18 + .../testdata/v0/eqexpr/test-eqexpr-0549.yaml | 19 + .../testdata/v0/eqexpr/test-eqexpr-0550.yaml | 13 + .../testdata/v0/eqexpr/test-eqexpr-0551.yaml | 13 + .../testdata/v0/eqexpr/test-eqexpr-0552.yaml | 20 + .../testdata/v0/eqexpr/test-eqexpr-0553.yaml | 13 + .../testdata/v0/eqexpr/test-eqexpr-0554.yaml | 13 + .../testdata/v0/eqexpr/test-eqexpr-0555.yaml | 13 + .../testdata/v0/eqexpr/test-eqexpr-0556.yaml | 14 + .../testdata/v0/eqexpr/test-eqexpr-0557.yaml | 14 + .../testdata/v0/eqexpr/test-eqexpr-0558.yaml | 14 + .../testdata/v0/eqexpr/test-eqexpr-0559.yaml | 14 + .../testdata/v0/eqexpr/test-eqexpr-0560.yaml | 14 + .../testdata/v0/eqexpr/test-eqexpr-0561.yaml | 14 + .../testdata/v0/eqexpr/test-eqexpr-0562.yaml | 14 + .../testdata/v0/eqexpr/test-eqexpr-0563.yaml | 19 + .../testdata/v0/eqexpr/test-eqexpr-0564.yaml | 16 + .../testdata/v0/eqexpr/test-eqexpr-0565.yaml | 18 + .../testdata/v0/eqexpr/test-eqexpr-0566.yaml | 25 + .../testdata/v0/eqexpr/test-eqexpr-0567.yaml | 18 + .../testdata/v0/eqexpr/test-eqexpr-0568.yaml | 20 + .../testdata/v0/eqexpr/test-eqexpr-0569.yaml | 20 + .../testdata/v0/eqexpr/test-eqexpr-0570.yaml | 20 + .../testdata/v0/eqexpr/test-eqexpr-0571.yaml | 20 + .../testdata/v0/eqexpr/test-eqexpr-0572.yaml | 22 + .../testdata/v0/eqexpr/test-eqexpr-0573.yaml | 31 + .../testdata/v0/eqexpr/test-eqexpr-0574.yaml | 16 + .../testdata/v0/eqexpr/test-eqexpr-0575.yaml | 16 + .../testdata/v0/eqexpr/test-eqexpr-0576.yaml | 16 + .../testdata/v0/eqexpr/test-eqexpr-0577.yaml | 18 + .../testdata/v0/eqexpr/test-eqexpr-0578.yaml | 18 + .../testdata/v0/eqexpr/test-eqexpr-0579.yaml | 25 + .../testdata/v0/eqexpr/test-eqexpr-0580.yaml | 28 + .../testdata/v0/eqexpr/test-eqexpr-0581.yaml | 27 + .../testdata/v0/eqexpr/test-eqexpr-0582.yaml | 27 + .../testdata/v0/eqexpr/test-eqexpr-0583.yaml | 19 + .../testdata/v0/eqexpr/test-eqexpr-0584.yaml | 16 + .../testdata/v0/eqexpr/test-eqexpr-0585.yaml | 16 + .../testdata/v0/eqexpr/test-eqexpr-0586.yaml | 18 + .../testdata/v0/eqexpr/test-eqexpr-0587.yaml | 18 + .../testdata/v0/eqexpr/test-eqexpr-0588.yaml | 28 + .../testdata/v0/eqexpr/test-eqexpr-0589.yaml | 30 + .../testdata/v0/eqexpr/test-eqexpr-0590.yaml | 31 + .../testdata/v0/eqexpr/test-eqexpr-0591.yaml | 31 + .../testdata/v0/eqexpr/test-eqexpr-0592.yaml | 18 + .../testdata/v0/eqexpr/test-eqexpr-0593.yaml | 25 + .../testdata/v0/eqexpr/test-eqexpr-0594.yaml | 29 + .../testdata/v0/eqexpr/test-eqexpr-0595.yaml | 17 + .../testdata/v0/eqexpr/test-eqexpr-0596.yaml | 16 + .../testdata/v0/eqexpr/test-eqexpr-0597.yaml | 17 + .../testdata/v0/eqexpr/test-eqexpr-0598.yaml | 34 + .../testdata/v0/eqexpr/test-eqexpr-0599.yaml | 20 + .../evaltermexpr/test-evaltermexpr-0525.yaml | 12 + .../evaltermexpr/test-evaltermexpr-0526.yaml | 13 + .../evaltermexpr/test-evaltermexpr-0527.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0528.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0529.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0530.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0531.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0532.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0533.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0534.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0535.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0536.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0537.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0538.yaml | 19 + .../evaltermexpr/test-evaltermexpr-0539.yaml | 13 + .../evaltermexpr/test-evaltermexpr-0540.yaml | 18 + .../evaltermexpr/test-evaltermexpr-0541.yaml | 24 + .../evaltermexpr/test-evaltermexpr-0542.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0543.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0544.yaml | 21 + .../test/cases/testdata/v0/every/every.yaml | 249 + .../v0/every/non_iterable_domain.yaml | 151 + .../cases/testdata/v0/every/textbook.yaml | 149 + .../v0/example/test-example-1070.yaml | 88 + .../v0/example/test-example-1071.yaml | 85 + .../v0/example/test-example-1072.yaml | 102 + .../v0/fix1863/test-fix1863-0706.yaml | 23 + .../v0/fix1863/test-fix1863-0707.yaml | 20 + .../v0/fix1863/test-fix1863-0708.yaml | 25 + .../v0/functionerrors/test-conflicts.yaml | 22 + .../test-functionerrors-1012.yaml | 33 + .../test-functionerrors-1013.yaml | 21 + .../test-functionerrors-1014.yaml | 22 + ...nctionerrors-undefined-builtin-result.yaml | 13 + .../v0/functions/test-functions-0990.yaml | 279 + .../v0/functions/test-functions-0991.yaml | 200 + .../v0/functions/test-functions-0992.yaml | 201 + .../v0/functions/test-functions-0993.yaml | 201 + .../v0/functions/test-functions-0994.yaml | 200 + .../v0/functions/test-functions-0995.yaml | 201 + .../v0/functions/test-functions-0996.yaml | 203 + .../v0/functions/test-functions-0997.yaml | 203 + .../v0/functions/test-functions-0998.yaml | 204 + .../v0/functions/test-functions-0999.yaml | 204 + .../v0/functions/test-functions-1000.yaml | 201 + .../v0/functions/test-functions-1001.yaml | 201 + .../v0/functions/test-functions-1002.yaml | 201 + .../v0/functions/test-functions-1003.yaml | 201 + .../v0/functions/test-functions-1004.yaml | 200 + .../v0/functions/test-functions-1005.yaml | 201 + .../v0/functions/test-functions-1006.yaml | 201 + .../v0/functions/test-functions-1007.yaml | 201 + .../v0/functions/test-functions-1008.yaml | 201 + .../v0/functions/test-functions-1009.yaml | 203 + .../v0/functions/test-functions-1010.yaml | 204 + .../v0/functions/test-functions-1011.yaml | 201 + .../v0/functions/test-functions-default.yaml | 100 + ...test-functions-nested-with-early-exit.yaml | 100 + .../functions/test-functions-unused-arg.yaml | 13 + .../v0/globmatch/test-globmatch-0133.yaml | 16 + .../v0/globmatch/test-globmatch-0134.yaml | 16 + .../v0/globmatch/test-globmatch-0135.yaml | 16 + .../v0/globmatch/test-globmatch-0136.yaml | 16 + .../v0/globmatch/test-globmatch-0137.yaml | 16 + .../v0/globmatch/test-globmatch-0138.yaml | 16 + .../v0/globmatch/test-globmatch-0139.yaml | 16 + .../v0/globmatch/test-globmatch-0140.yaml | 16 + .../v0/globmatch/test-globmatch-0141.yaml | 16 + .../v0/globmatch/test-globmatch-0142.yaml | 16 + .../v0/globmatch/test-globmatch-0143.yaml | 16 + .../v0/globmatch/test-globmatch-0144.yaml | 16 + .../v0/globmatch/test-globmatch-0145.yaml | 16 + .../v0/globmatch/test-globmatch-0146.yaml | 16 + .../v0/globmatch/test-globmatch-0147.yaml | 16 + .../v0/globmatch/test-globmatch-0148.yaml | 16 + .../v0/globmatch/test-globmatch-0149.yaml | 16 + .../v0/globmatch/test-globmatch-0150.yaml | 16 + .../v0/globmatch/test-globmatch-0151.yaml | 16 + .../v0/globmatch/test-globmatch-0152.yaml | 16 + .../v0/globmatch/test-globmatch-0153.yaml | 16 + .../v0/globmatch/test-globmatch-0154.yaml | 16 + .../v0/globmatch/test-globmatch-0155.yaml | 16 + .../v0/globmatch/test-globmatch-0156.yaml | 16 + .../v0/globmatch/test-globmatch-0157.yaml | 16 + .../v0/globmatch/test-globmatch-0158.yaml | 16 + .../v0/globmatch/test-globmatch-0159.yaml | 30 + .../globmatch/test-globmatch-issue-5273.yaml | 16 + .../globmatch/test-globmatch-issue-5283.yaml | 25 + .../test-globquotemeta-0159.yaml | 16 + .../v0/globsmatch/test-globsmatch-0865.yaml | 14 + .../v0/globsmatch/test-globsmatch-0866.yaml | 13 + .../v0/globsmatch/test-globsmatch-0867.yaml | 17 + .../v0/globsmatch/test-globsmatch-0868.yaml | 22 + .../v0/globsmatch/test-globsmatch-0869.yaml | 14 + .../v0/globsmatch/test-globsmatch-0870.yaml | 13 + .../v0/graphql/test-graphql-basic-ast.yaml | 308 + .../v0/graphql/test-graphql-is-valid.yaml | 176 + .../test-graphql-parse-and-verify.yaml | 186 + .../v0/graphql/test-graphql-parse-query.yaml | 479 + .../v0/graphql/test-graphql-parse-schema.yaml | 688 + .../v0/graphql/test-graphql-parse.yaml | 181 + .../graphql/test-graphql-schema-is-valid.yaml | 777 + .../v0/helloworld/test-helloworld-1.yaml | 50 + .../v0/hexbuiltins/test-hexbuiltins-0939.yaml | 13 + .../v0/hexbuiltins/test-hexbuiltins-0940.yaml | 13 + .../v0/hexbuiltins/test-hexbuiltins-0941.yaml | 14 + .../cases/testdata/v0/indexing/array-any.yaml | 19 + .../test-indirectreferences-0758.yaml | 19 + .../test-indirectreferences-0759.yaml | 15 + .../test-indirectreferences-0760.yaml | 21 + .../test-indirectreferences-0761.yaml | 19 + .../test-indirectreferences-0762.yaml | 19 + .../v0/inputvalues/test-inputvalues-0977.yaml | 65 + .../v0/inputvalues/test-inputvalues-0978.yaml | 68 + .../v0/inputvalues/test-inputvalues-0979.yaml | 75 + .../v0/inputvalues/test-inputvalues-0980.yaml | 110 + .../v0/inputvalues/test-inputvalues-0981.yaml | 106 + .../v0/inputvalues/test-inputvalues-0982.yaml | 71 + .../v0/inputvalues/test-inputvalues-0983.yaml | 71 + .../intersection/test-intersection-0352.yaml | 14 + .../intersection/test-intersection-0353.yaml | 14 + .../intersection/test-intersection-0354.yaml | 17 + .../intersection/test-intersection-0355.yaml | 17 + .../intersection/test-intersection-0356.yaml | 19 + .../test-invalidkeyerror-0176.yaml | 15 + .../test-invalidkeyerror-0177.yaml | 15 + .../v0/jsonbuiltins/test-is-valid.yaml | 73 + .../test-json-marshal-with-options.yaml | 144 + .../jsonbuiltins/test-jsonbuiltins-0924.yaml | 14 + .../jsonbuiltins/test-jsonbuiltins-0925.yaml | 18 + .../jsonbuiltins/test-jsonbuiltins-0926.yaml | 21 + .../jsonbuiltins/test-jsonbuiltins-0927.yaml | 19 + .../jsonbuiltins/test-jsonbuiltins-0928.yaml | 15 + .../jsonbuiltins/test-marshal-large-ints.yaml | 14 + .../v0/jsonfilter/test-jsonfilter-0218.yaml | 18 + .../v0/jsonfilter/test-jsonfilter-0219.yaml | 19 + .../v0/jsonfilter/test-jsonfilter-0220.yaml | 19 + .../v0/jsonfilter/test-jsonfilter-0221.yaml | 19 + .../v0/jsonfilter/test-jsonfilter-0222.yaml | 17 + .../v0/jsonfilter/test-jsonfilter-0223.yaml | 15 + .../v0/jsonfilter/test-jsonfilter-0224.yaml | 15 + .../v0/jsonfilter/test-jsonfilter-0225.yaml | 18 + .../v0/jsonfilter/test-jsonfilter-0226.yaml | 18 + .../v0/jsonfilter/test-jsonfilter-0227.yaml | 19 + .../v0/jsonfilter/test-jsonfilter-0228.yaml | 19 + .../test-jsonfilteridempotent-0229.yaml | 19 + .../cases/testdata/v0/jsonpatch/coverage.yaml | 12 + .../v0/jsonpatch/json-patch-tests.yaml | 776 + .../test/cases/testdata/v0/jsonpatch/set.yaml | 68 + .../v0/jsonremove/test-jsonremove-0230.yaml | 19 + .../v0/jsonremove/test-jsonremove-0231.yaml | 18 + .../v0/jsonremove/test-jsonremove-0232.yaml | 18 + .../v0/jsonremove/test-jsonremove-0233.yaml | 18 + .../v0/jsonremove/test-jsonremove-0234.yaml | 16 + .../v0/jsonremove/test-jsonremove-0235.yaml | 16 + .../v0/jsonremove/test-jsonremove-0236.yaml | 15 + .../v0/jsonremove/test-jsonremove-0237.yaml | 15 + .../v0/jsonremove/test-jsonremove-0238.yaml | 16 + .../v0/jsonremove/test-jsonremove-0239.yaml | 17 + .../v0/jsonremove/test-jsonremove-0240.yaml | 20 + .../v0/jsonremove/test-jsonremove-0241.yaml | 18 + .../v0/jsonremove/test-jsonremove-0242.yaml | 19 + .../v0/jsonremove/test-jsonremove-0243.yaml | 18 + .../v0/jsonremove/test-jsonremove-0244.yaml | 18 + .../v0/jsonremove/test-jsonremove-0245.yaml | 18 + .../v0/jsonremove/test-jsonremove-0246.yaml | 18 + .../v0/jsonremove/test-jsonremove-0247.yaml | 18 + .../v0/jsonremove/test-jsonremove-0248.yaml | 18 + .../v0/jsonremove/test-jsonremove-0249.yaml | 18 + .../v0/jsonremove/test-jsonremove-0250.yaml | 18 + .../v0/jsonremove/test-jsonremove-0251.yaml | 20 + .../v0/jsonremove/test-jsonremove-0252.yaml | 20 + .../v0/jsonremove/test-jsonremove-0253.yaml | 20 + .../v0/jsonremove/test-jsonremove-0254.yaml | 20 + .../test-jsonremoveidempotent-0255.yaml | 21 + .../v0/jsonschema/test-json-match_schema.yaml | 104 + .../jsonschema/test-json-verify_schema.yaml | 50 + .../v0/jwtbuiltins/test-jwtbuiltins-0389.yaml | 19 + .../v0/jwtbuiltins/test-jwtbuiltins-0390.yaml | 19 + .../v0/jwtbuiltins/test-jwtbuiltins-0391.yaml | 15 + .../v0/jwtbuiltins/test-jwtbuiltins-0392.yaml | 15 + .../v0/jwtbuiltins/test-jwtbuiltins-0393.yaml | 15 + .../v0/jwtbuiltins/test-jwtbuiltins-0394.yaml | 17 + .../v0/jwtbuiltins/test-jwtbuiltins-0395.yaml | 17 + .../v0/jwtbuiltins/test-jwtbuiltins-0396.yaml | 17 + .../v0/jwtbuiltins/test-jwtbuiltins-0397.yaml | 19 + .../v0/jwtbuiltins/test-jwtbuiltins-0398.yaml | 19 + .../v0/jwtbuiltins/test-jwtbuiltins-0399.yaml | 24 + .../v0/jwtbuiltins/test-jwtbuiltins-0400.yaml | 19 + .../test-jwtdecodeverify-0449.yaml | 18 + .../test-jwtdecodeverify-0450.yaml | 17 + .../test-jwtdecodeverify-0451.yaml | 17 + .../test-jwtdecodeverify-0452.yaml | 18 + .../test-jwtdecodeverify-0453.yaml | 17 + .../test-jwtdecodeverify-0454.yaml | 18 + .../test-jwtdecodeverify-0455.yaml | 17 + .../test-jwtdecodeverify-0456.yaml | 19 + .../test-jwtdecodeverify-0457.yaml | 17 + .../test-jwtdecodeverify-0458.yaml | 17 + .../test-jwtdecodeverify-0459.yaml | 18 + .../test-jwtdecodeverify-0460.yaml | 19 + .../test-jwtdecodeverify-0461.yaml | 19 + .../test-jwtdecodeverify-0462.yaml | 17 + .../test-jwtdecodeverify-0463.yaml | 17 + .../test-jwtdecodeverify-0464.yaml | 17 + .../test-jwtdecodeverify-0465.yaml | 18 + .../test-jwtdecodeverify-0466.yaml | 18 + .../test-jwtdecodeverify-0467.yaml | 18 + .../test-jwtdecodeverify-0468.yaml | 21 + .../test-jwtdecodeverify-0469.yaml | 17 + .../test-jwtdecodeverify-0470.yaml | 19 + .../test-jwtdecodeverify-0471.yaml | 21 + .../test-jwtdecodeverify-0472.yaml | 17 + .../test-jwtdecodeverify-0473.yaml | 17 + .../test-jwtdecodeverify-0474.yaml | 17 + .../test-jwtdecodeverify-0475.yaml | 17 + .../test-jwtdecodeverify-0476.yaml | 44 + .../test-jwtdecodeverify-0477.yaml | 40 + .../test-jwtdecodeverify-0478.yaml | 19 + .../test-jwtdecodeverify-0479.yaml | 17 + .../test-jwtdecodeverify-0480.yaml | 19 + .../test-jwtdecodeverify-0481.yaml | 17 + .../test-jwtdecodeverify-0482.yaml | 17 + .../test-jwtdecodeverify-0483.yaml | 19 + .../test-jwtdecodeverify-0484.yaml | 19 + .../test-jwtdecodeverify-0485.yaml | 19 + .../test-jwtdecodeverify-0486.yaml | 19 + .../test-jwtdecodeverify-0487.yaml | 19 + .../test-jwtdecodeverify-0488.yaml | 19 + .../test-jwtdecodeverify-0489.yaml | 17 + .../test-jwtdecodeverify-0490.yaml | 17 + .../test-jwtdecodeverify-0491.yaml | 17 + ...test-jwtdecodeverify-invalid-exp-type.yaml | 16 + ...test-jwtdecodeverify-invalid-nbf-type.yaml | 16 + ...codeverify-missing-iss-while-required.yaml | 18 + .../test-jwtencodesign-0492.yaml | 14 + .../test-jwtencodesign-0493.yaml | 14 + .../test-jwtencodesign-0494.yaml | 14 + ...test-jwtencodesign-integer-timestamps.yaml | 17 + .../test-jwtencodesign-set-data.yaml | 17 + .../test-jwtencodesignheadererrors-0379.yaml | 15 + .../test-jwtencodesignheadererrors-0380.yaml | 15 + .../test-jwtencodesignheadererrors-0381.yaml | 15 + .../test-jwtencodesignheadererrors-0382.yaml | 15 + .../test-jwtencodesignheadererrors-0383.yaml | 15 + .../test-jwtencodesignpayloaderrors-0376.yaml | 15 + .../test-jwtencodesignpayloaderrors-0377.yaml | 15 + .../test-jwtencodesignpayloaderrors-0378.yaml | 15 + .../test-jwtencodesignraw-0384.yaml | 12 + .../test-jwtencodesignraw-0385.yaml | 11 + .../test-jwtencodesignraw-0386.yaml | 11 + .../test-jwtencodesignraw-0387.yaml | 11 + .../test-jwtencodesignraw-0388.yaml | 18 + .../test-jwtverifyhs256-0440.yaml | 14 + .../test-jwtverifyhs256-0441.yaml | 14 + .../test-jwtverifyhs256-0442.yaml | 15 + .../test-jwtverifyhs384-0443.yaml | 14 + .../test-jwtverifyhs384-0444.yaml | 14 + .../test-jwtverifyhs384-0445.yaml | 15 + .../test-jwtverifyhs512-0446.yaml | 14 + .../test-jwtverifyhs512-0447.yaml | 14 + .../test-jwtverifyhs512-0448.yaml | 15 + .../jwtverifyrsa/test-jwtverifyrsa-0401.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0402.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0403.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0404.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0405.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0406.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0407.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0408.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0409.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0410.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0411.yaml | 15 + .../jwtverifyrsa/test-jwtverifyrsa-0412.yaml | 15 + .../jwtverifyrsa/test-jwtverifyrsa-0413.yaml | 15 + .../jwtverifyrsa/test-jwtverifyrsa-0414.yaml | 15 + .../jwtverifyrsa/test-jwtverifyrsa-0415.yaml | 15 + .../jwtverifyrsa/test-jwtverifyrsa-0416.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0417.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0418.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0419.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0420.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0421.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0422.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0423.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0424.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0425.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0426.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0427.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0428.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0429.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0430.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0431.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0432.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0433.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0434.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0435.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0436.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0437.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0438.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0439.yaml | 14 + .../v0/keywordrefs/test-keyword-as.yaml | 200 + .../v0/keywordrefs/test-keyword-default.yaml | 200 + .../v0/keywordrefs/test-keyword-else.yaml | 200 + .../v0/keywordrefs/test-keyword-false.yaml | 200 + .../v0/keywordrefs/test-keyword-import.yaml | 200 + .../v0/keywordrefs/test-keyword-not.yaml | 200 + .../v0/keywordrefs/test-keyword-null.yaml | 200 + .../v0/keywordrefs/test-keyword-package.yaml | 200 + .../v0/keywordrefs/test-keyword-some.yaml | 200 + .../v0/keywordrefs/test-keyword-true.yaml | 200 + .../v0/keywordrefs/test-keyword-with.yaml | 200 + .../v0/negation/test-negation-0777.yaml | 14 + .../v0/negation/test-negation-0778.yaml | 13 + .../v0/negation/test-negation-0779.yaml | 18 + .../v0/negation/test-negation-0780.yaml | 20 + .../test-negation-data-ref-with-var.yaml | 73 + .../test-nestedreferences-0709.yaml | 27 + .../test-nestedreferences-0710.yaml | 31 + .../test-nestedreferences-0711.yaml | 24 + .../test-nestedreferences-0712.yaml | 31 + .../test-nestedreferences-0713.yaml | 30 + .../test-nestedreferences-0714.yaml | 21 + .../test-nestedreferences-0715.yaml | 25 + .../test-nestedreferences-0716.yaml | 14 + .../test-nestedreferences-0717.yaml | 25 + .../test-nestedreferences-0718.yaml | 24 + .../test-nestedreferences-0719.yaml | 22 + .../test-nestedreferences-0720.yaml | 28 + .../test-nestedreferences-0721.yaml | 27 + .../test-nestedreferences-0722.yaml | 29 + .../test-nestedreferences-0723.yaml | 32 + .../test-nestedreferences-0724.yaml | 36 + .../test-nestedreferences-0725.yaml | 31 + .../test-netcidrcontains-0092.yaml | 16 + .../test-netcidrcontains-0093.yaml | 16 + .../test-netcidrcontains-0094.yaml | 16 + .../test-netcidrcontains-0095.yaml | 16 + .../test-netcidrcontains-0096.yaml | 16 + .../test-netcidrcontains-0097.yaml | 16 + .../test-netcidrcontains-0098.yaml | 16 + .../test-netcidrcontains-0099.yaml | 16 + .../test-netcidrcontains-0100.yaml | 15 + .../test-netcidrcontains-0101.yaml | 15 + .../test-netcidrcontains-0102.yaml | 16 + .../test-netcidrcontains-0103.yaml | 16 + .../test-netcidrcontainsmatches-0104.yaml | 17 + .../test-netcidrcontainsmatches-0105.yaml | 19 + .../test-netcidrcontainsmatches-0106.yaml | 19 + .../test-netcidrcontainsmatches-0107.yaml | 21 + .../test-netcidrcontainsmatches-0108.yaml | 20 + .../test-netcidrcontainsmatches-0109.yaml | 23 + .../test-netcidrcontainsmatches-0110.yaml | 21 + .../test-netcidrcontainsmatches-0111.yaml | 18 + .../test-netcidrcontainsmatches-0112.yaml | 19 + .../test-netcidrexpand-0113.yaml | 18 + .../test-netcidrexpand-0114.yaml | 18 + .../test-netcidrexpand-0115.yaml | 15 + .../test-netcidrexpand-0116.yaml | 15 + .../test-netcidrintersects-0086.yaml | 16 + .../test-netcidrintersects-0087.yaml | 16 + .../test-netcidrintersects-0088.yaml | 16 + .../test-netcidrintersects-0089.yaml | 16 + .../test-netcidrintersects-0090.yaml | 15 + .../test-netcidrintersects-0091.yaml | 15 + .../test_netcidrisvalid-0001.yaml | 67 + .../test-ipv6-with-and-without-prefix.yaml | 60 + .../netcidrmerge/test-netcidrmerge0117.yaml | 214 + .../test-netcidroverlap-0084.yaml | 16 + .../test-netcidroverlap-0085.yaml | 16 + .../netlookupipaddr/test-netlookupipaddr.yaml | 46 + .../numbersrange/test-numbersrange-0256.yaml | 16 + .../numbersrange/test-numbersrange-0257.yaml | 21 + .../numbersrange/test-numbersrange-0258.yaml | 38 + .../numbersrange/test-numbersrange-0259.yaml | 14 + .../numbersrange/test-numbersrange-0260.yaml | 17 + .../numbersrange/test-numbersrange-0261.yaml | 17 + .../test-numbersrangestep.yaml | 103 + .../objectfilter/test-objectfilter-0300.yaml | 19 + .../objectfilter/test-objectfilter-0301.yaml | 17 + .../objectfilter/test-objectfilter-0302.yaml | 17 + .../objectfilter/test-objectfilter-0303.yaml | 17 + .../objectfilter/test-objectfilter-0304.yaml | 19 + .../objectfilter/test-objectfilter-0305.yaml | 15 + .../objectfilter/test-objectfilter-0306.yaml | 15 + .../objectfilter/test-objectfilter-0307.yaml | 15 + .../objectfilter/test-objectfilter-0308.yaml | 15 + .../objectfilter/test-objectfilter-0309.yaml | 18 + .../objectfilter/test-objectfilter-0310.yaml | 18 + .../objectfilter/test-objectfilter-0311.yaml | 18 + .../objectfilter/test-objectfilter-0312.yaml | 18 + .../objectfilter/test-objectfilter-0313.yaml | 18 + .../objectfilter/test-objectfilter-0314.yaml | 18 + .../objectfilter/test-objectfilter-0315.yaml | 18 + .../objectfilter/test-objectfilter-0316.yaml | 18 + .../objectfilter/test-objectfilter-0317.yaml | 18 + .../test-objectfilteridempotent-0319.yaml | 21 + .../test-objectfilternonstringkey-0318.yaml | 16 + .../v0/objectget/test-objectget-0262.yaml | 15 + .../v0/objectget/test-objectget-0263.yaml | 15 + .../v0/objectget/test-objectget-0264.yaml | 15 + .../v0/objectget/test-objectget-0265.yaml | 15 + .../v0/objectget/test-objectget-0266.yaml | 16 + .../v0/objectget/test-objectget-0267.yaml | 15 + .../v0/objectget/test-objectget-path.yaml | 125 + .../v0/objectkeys/test-objectkeys.yaml | 70 + .../objectremove/test-objectremove-0279.yaml | 17 + .../objectremove/test-objectremove-0280.yaml | 16 + .../objectremove/test-objectremove-0281.yaml | 16 + .../objectremove/test-objectremove-0282.yaml | 16 + .../objectremove/test-objectremove-0283.yaml | 16 + .../objectremove/test-objectremove-0284.yaml | 15 + .../objectremove/test-objectremove-0285.yaml | 18 + .../objectremove/test-objectremove-0286.yaml | 18 + .../objectremove/test-objectremove-0287.yaml | 18 + .../objectremove/test-objectremove-0288.yaml | 18 + .../objectremove/test-objectremove-0289.yaml | 18 + .../objectremove/test-objectremove-0290.yaml | 18 + .../objectremove/test-objectremove-0291.yaml | 18 + .../objectremove/test-objectremove-0292.yaml | 18 + .../objectremove/test-objectremove-0293.yaml | 18 + .../objectremove/test-objectremove-0294.yaml | 18 + .../objectremove/test-objectremove-0295.yaml | 18 + .../objectremove/test-objectremove-0296.yaml | 18 + .../objectremove/test-objectremove-0297.yaml | 18 + .../test-objectremoveidempotent-0298.yaml | 21 + .../test-objectremovenonstringkey-0299.yaml | 16 + .../v0/objectunion/test-objectunion-0268.yaml | 15 + .../v0/objectunion/test-objectunion-0269.yaml | 16 + .../v0/objectunion/test-objectunion-0270.yaml | 16 + .../v0/objectunion/test-objectunion-0271.yaml | 17 + .../v0/objectunion/test-objectunion-0272.yaml | 19 + .../v0/objectunion/test-objectunion-0273.yaml | 19 + .../v0/objectunion/test-objectunion-0274.yaml | 16 + .../v0/objectunion/test-objectunion-0275.yaml | 19 + .../v0/objectunion/test-objectunion-0276.yaml | 20 + .../v0/objectunion/test-objectunion-0277.yaml | 18 + .../v0/objectunion/test-objectunion-0278.yaml | 18 + .../objectunionn/test-objectunionn-0001.yaml | 80 + .../test-partialdocconstants-0984.yaml | 26 + .../test-partialdocconstants-0985.yaml | 34 + .../test-partialdocconstants-0986.yaml | 49 + .../test-partialdocconstants-0987.yaml | 42 + .../test-partialdocconstants-0988.yaml | 45 + .../test-partialdocconstants-0989.yaml | 47 + .../v0/partialiter/test-partialiter-001.yaml | 40 + .../test-partialobjectdoc-0519.yaml | 19 + .../test-partialobjectdoc-0520.yaml | 21 + .../test-partialobjectdoc-0521.yaml | 39 + .../test-partialobjectdoc-0522.yaml | 41 + .../test-partialobjectdoc-0523.yaml | 18 + .../test-partialobjectdoc-0524.yaml | 18 + .../test-partialobjectdoc-ref.yaml | 22 + .../v0/partialobjectdoc/test-wasm-cases.yaml | 106 + .../v0/partialsetdoc/test-issue-3369.yaml | 21 + .../v0/partialsetdoc/test-issue-3376.yaml | 19 + .../v0/partialsetdoc/test-issue-3819.yaml | 25 + .../test-partialsetdoc-0511.yaml | 24 + .../test-partialsetdoc-0512.yaml | 24 + .../test-partialsetdoc-0513.yaml | 20 + .../test-partialsetdoc-0514.yaml | 20 + .../test-partialsetdoc-0515.yaml | 32 + .../test-partialsetdoc-0516.yaml | 31 + .../test-partialsetdoc-0517.yaml | 29 + .../test-partialsetdoc-0518.yaml | 18 + .../v0/planner-ir/test-array-ir-unify.yaml | 47 + .../v0/planner-ir/test-call-dynamic.yaml | 94 + .../v0/providers-aws/aws-sign_req-errors.yaml | 175 + .../v0/providers-aws/aws-sign_req.yaml | 310 + .../testdata/v0/rand/test-rand.intn.yaml | 14 + .../v0/reachable/test-reachable-0322.yaml | 16 + .../v0/reachable/test-reachable-0323.yaml | 24 + .../v0/reachable/test-reachable-0324.yaml | 29 + .../v0/reachable/test-reachable-0325.yaml | 24 + .../v0/reachable/test-reachable-0326.yaml | 18 + .../v0/reachable/test-reachable-0327.yaml | 19 + .../v0/reachable/test-reachable-0328.yaml | 18 + .../reachable/test-reachable-paths-0422.yaml | 191 + .../reachable/test-reachable-paths-1022.yaml | 129 + .../v0/refheads/test-generic-refs.yaml | 256 + .../v0/refheads/test-refs-as-rule-heads.yaml | 360 + .../v0/refheads/test-regressions.yaml | 155 + .../v0/regexfind/test-regexfind-0334.yaml | 19 + .../v0/regexfind/test-regexfind-0335.yaml | 18 + .../v0/regexfind/test-regexfind-0336.yaml | 17 + .../test-regexfindallstringsubmatch-0337.yaml | 17 + .../test-regexfindallstringsubmatch-0338.yaml | 18 + .../test-regexfindallstringsubmatch-0339.yaml | 18 + .../test-regexfindallstringsubmatch-0340.yaml | 20 + .../test-regexfindallstringsubmatch-0341.yaml | 20 + .../test-regexfindallstringsubmatch-0342.yaml | 22 + .../test-regexfindallstringsubmatch-0343.yaml | 18 + ...egexfindallstringsubmatch-large-input.yaml | 16 + .../regexisvalid/test-regexisvalid-0329.yaml | 16 + .../regexisvalid/test-regexisvalid-0330.yaml | 14 + .../regexisvalid/test-regexisvalid-0331.yaml | 14 + .../v0/regexmatch/test-regexmatch-0855.yaml | 14 + .../v0/regexmatch/test-regexmatch-0856.yaml | 13 + .../v0/regexmatch/test-regexmatch-0857.yaml | 15 + .../v0/regexmatch/test-regexmatch-0858.yaml | 22 + .../v0/regexmatch/test-regexmatch-0859.yaml | 14 + .../v0/regexmatch/test-regexmatch-0860.yaml | 13 + .../v0/regexmatch/test-regexmatch-0861.yaml | 14 + .../test-regexmatchtemplate-0332.yaml | 16 + .../test-regexmatchtemplate-0333.yaml | 16 + .../regexreplace/test-regexreplace-0001.yaml | 40 + .../v0/regexsplit/test-regexsplit-0862.yaml | 14 + .../v0/regexsplit/test-regexsplit-0863.yaml | 18 + .../v0/regexsplit/test-regexsplit-0864.yaml | 16 + .../test-regometadatachain-1.yaml | 99 + .../test-regometadatarule-1.yaml | 50 + .../test-regoparsemodule-0320.yaml | 23 + .../test-regoparsemodule-0321.yaml | 17 + .../v0/rendertemplate/rendertemplate.yaml | 49 + .../v0/replacen/test-replacen-0374.yaml | 41 + .../v0/replacen/test-replacen-0375.yaml | 17 + .../replacen/test-replacen-bad-operands.yaml | 38 + .../test-semvercompare-0344.yaml | 15 + .../test-semvercompare-0345.yaml | 15 + .../test-semvercompare-0346.yaml | 15 + .../test-semvercompare-0347.yaml | 16 + .../test-semvercompare-0348.yaml | 16 + .../test-semverisvalid-0349.yaml | 15 + .../test-semverisvalid-0350.yaml | 15 + .../test-semverisvalid-0351.yaml | 15 + .../testdata/v0/sets/test-sets-0871.yaml | 19 + .../testdata/v0/sets/test-sets-0872.yaml | 26 + .../testdata/v0/sets/test-sets-0873.yaml | 15 + .../testdata/v0/sets/test-sets-0874.yaml | 26 + .../testdata/v0/sets/test-sets-0875.yaml | 24 + .../testdata/v0/sets/test-sets-0876.yaml | 23 + .../testdata/v0/sprintf/test-sprintf.yaml | 26 + .../v0/strings/test-anyprefixmatch.yaml | 341 + .../v0/strings/test-anysuffixmatch.yaml | 341 + .../v0/strings/test-strings-0877.yaml | 14 + .../v0/strings/test-strings-0878.yaml | 13 + .../v0/strings/test-strings-0879.yaml | 20 + .../v0/strings/test-strings-0880.yaml | 20 + .../v0/strings/test-strings-0881.yaml | 15 + .../v0/strings/test-strings-0882.yaml | 14 + .../v0/strings/test-strings-0883.yaml | 26 + .../v0/strings/test-strings-0884.yaml | 13 + .../v0/strings/test-strings-0885.yaml | 26 + .../v0/strings/test-strings-0886.yaml | 26 + .../v0/strings/test-strings-0887.yaml | 24 + .../v0/strings/test-strings-0888.yaml | 14 + .../v0/strings/test-strings-0889.yaml | 20 + .../v0/strings/test-strings-0890.yaml | 20 + .../v0/strings/test-strings-0891.yaml | 21 + .../v0/strings/test-strings-0892.yaml | 27 + .../v0/strings/test-strings-0893.yaml | 20 + .../v0/strings/test-strings-0894.yaml | 20 + .../v0/strings/test-strings-0895.yaml | 14 + .../v0/strings/test-strings-0896.yaml | 13 + .../v0/strings/test-strings-0897.yaml | 14 + .../v0/strings/test-strings-0898.yaml | 13 + .../v0/strings/test-strings-0899.yaml | 14 + .../v0/strings/test-strings-0900.yaml | 13 + .../v0/strings/test-strings-0901.yaml | 14 + .../v0/strings/test-strings-0902.yaml | 14 + .../v0/strings/test-strings-0903.yaml | 14 + .../v0/strings/test-strings-0904.yaml | 14 + .../v0/strings/test-strings-0905.yaml | 16 + .../v0/strings/test-strings-0906.yaml | 14 + .../v0/strings/test-strings-0907.yaml | 14 + .../v0/strings/test-strings-0908.yaml | 14 + .../v0/strings/test-strings-0909.yaml | 14 + .../v0/strings/test-strings-0910.yaml | 14 + .../v0/strings/test-strings-0911.yaml | 14 + .../v0/strings/test-strings-0912.yaml | 14 + .../v0/strings/test-strings-0913.yaml | 14 + .../v0/strings/test-strings-0914.yaml | 14 + .../v0/strings/test-strings-0915.yaml | 14 + .../v0/strings/test-strings-0916.yaml | 14 + .../v0/strings/test-strings-0917.yaml | 14 + .../v0/strings/test-strings-0918.yaml | 14 + .../v0/strings/test-strings-0919.yaml | 14 + .../v0/strings/test-strings-0920.yaml | 14 + .../v0/strings/test-strings-0921.yaml | 14 + .../v0/strings/test-strings-0922.yaml | 14 + .../v0/strings/test-strings-0923.yaml | 14 + .../v0/strings/test-strings-0924.yaml | 71 + .../v0/strings/test-strings-0925.yaml | 42 + .../v0/strings/test-strings-0926.yaml | 26 + .../strings/test-strings-indexof-unicode.yaml | 57 + .../cases/testdata/v0/subset/test-subset.yaml | 437 + .../testdata/v0/time/test-time-0947.yaml | 17 + .../testdata/v0/time/test-time-0948.yaml | 77 + .../testdata/v0/time/test-time-0949.yaml | 50 + .../testdata/v0/time/test-time-0950.yaml | 14 + .../testdata/v0/time/test-time-0951.yaml | 21 + .../testdata/v0/time/test-time-0952.yaml | 21 + .../testdata/v0/time/test-time-0953.yaml | 21 + .../testdata/v0/time/test-time-0954.yaml | 21 + .../testdata/v0/time/test-time-0955.yaml | 20 + .../testdata/v0/time/test-time-0956.yaml | 21 + .../testdata/v0/time/test-time-0957.yaml | 21 + .../testdata/v0/time/test-time-0958.yaml | 21 + .../testdata/v0/time/test-time-0959.yaml | 20 + .../testdata/v0/time/test-time-0960.yaml | 15 + .../testdata/v0/time/test-time-0961.yaml | 15 + .../testdata/v0/time/test-time-0962.yaml | 15 + .../testdata/v0/time/test-time-0963.yaml | 15 + .../testdata/v0/time/test-time-0964.yaml | 15 + .../testdata/v0/time/test-time-0965.yaml | 15 + .../testdata/v0/time/test-time-0966.yaml | 15 + .../testdata/v0/time/test-time-0967.yaml | 20 + .../testdata/v0/time/test-time-0968.yaml | 27 + .../testdata/v0/time/test-time-0969.yaml | 26 + .../testdata/v0/time/test-time-0970.yaml | 42 + .../testdata/v0/time/test-time-0971.yaml | 40 + .../v0/toarray/test-toarray-0071.yaml | 17 + .../v0/toarray/test-toarray-0072.yaml | 17 + .../v0/toarray/test-toarray-0073.yaml | 15 + .../test-topdowndynamicdispatch-1068.yaml | 33 + .../testdata/v0/toset/test-toset-0074.yaml | 15 + .../testdata/v0/toset/test-toset-0075.yaml | 17 + .../testdata/v0/toset/test-toset-0076.yaml | 15 + .../testdata/v0/trim/test-trim-0362.yaml | 17 + .../testdata/v0/trim/test-trim-0363.yaml | 17 + .../v0/trimleft/test-trimleft-0364.yaml | 17 + .../v0/trimleft/test-trimleft-0365.yaml | 17 + .../v0/trimprefix/test-trimprefix-0366.yaml | 17 + .../v0/trimprefix/test-trimprefix-0367.yaml | 17 + .../v0/trimright/test-trimright-0368.yaml | 17 + .../v0/trimright/test-trimright-0369.yaml | 17 + .../v0/trimspace/test-trimspace-0372.yaml | 17 + .../v0/trimspace/test-trimspace-0373.yaml | 17 + .../v0/trimsuffix/test-trimsuffix-0370.yaml | 17 + .../v0/trimsuffix/test-trimsuffix-0371.yaml | 17 + .../testdata/v0/type/test-regressions.yaml | 89 + .../v0/typebuiltin/test-typebuiltin-0828.yaml | 19 + .../v0/typebuiltin/test-typebuiltin-0829.yaml | 14 + .../v0/typebuiltin/test-typebuiltin-0830.yaml | 14 + .../v0/typebuiltin/test-typebuiltin-0831.yaml | 18 + .../v0/typebuiltin/test-typebuiltin-0832.yaml | 19 + .../v0/typebuiltin/test-typebuiltin-0833.yaml | 14 + .../v0/typebuiltin/test-typebuiltin-0834.yaml | 14 + .../v0/typebuiltin/test-typebuiltin-0835.yaml | 18 + .../v0/typebuiltin/test-typebuiltin-0836.yaml | 17 + .../v0/typebuiltin/test-typebuiltin-0837.yaml | 14 + .../v0/typebuiltin/test-typebuiltin-0838.yaml | 14 + .../v0/typebuiltin/test-typebuiltin-0839.yaml | 18 + .../v0/typebuiltin/test-typebuiltin-0840.yaml | 17 + .../v0/typebuiltin/test-typebuiltin-0841.yaml | 14 + .../v0/typebuiltin/test-typebuiltin-0842.yaml | 17 + .../v0/typebuiltin/test-typebuiltin-0843.yaml | 14 + .../v0/typebuiltin/test-typebuiltin-0844.yaml | 15 + .../v0/typebuiltin/test-typebuiltin-0845.yaml | 14 + .../v0/typebuiltin/test-typebuiltin-0846.yaml | 14 + .../v0/typebuiltin/test-typebuiltin-0847.yaml | 14 + .../test-typenamebuiltin-0848.yaml | 14 + .../test-typenamebuiltin-0849.yaml | 14 + .../test-typenamebuiltin-0850.yaml | 14 + .../test-typenamebuiltin-0851.yaml | 14 + .../test-typenamebuiltin-0852.yaml | 14 + .../test-typenamebuiltin-0853.yaml | 14 + .../test-typenamebuiltin-0854.yaml | 15 + .../testdata/v0/undos/test-undos-0599.yaml | 18 + .../testdata/v0/undos/test-undos-0600.yaml | 18 + .../testdata/v0/undos/test-undos-0601.yaml | 18 + .../testdata/v0/undos/test-undos-0602.yaml | 22 + .../testdata/v0/undos/test-undos-0603.yaml | 18 + .../testdata/v0/undos/test-undos-0604.yaml | 18 + .../testdata/v0/undos/test-undos-0605.yaml | 18 + .../testdata/v0/undos/test-undos-0606.yaml | 22 + .../testdata/v0/undos/test-undos-0607.yaml | 22 + .../testdata/v0/union/test-union-0357.yaml | 14 + .../testdata/v0/union/test-union-0358.yaml | 16 + .../testdata/v0/union/test-union-0359.yaml | 19 + .../testdata/v0/union/test-union-0360.yaml | 24 + .../testdata/v0/union/test-union-0361.yaml | 23 + .../testdata/v0/units/test-issue-4856.yaml | 35 + .../units/test-parse-bytes-comparisons.yaml | 112 + .../v0/units/test-parse-bytes-errors.yaml | 86 + .../testdata/v0/units/test-parse-bytes.yaml | 456 + .../units/test-parse-units-comparisons.yaml | 112 + .../v0/units/test-parse-units-errors.yaml | 86 + .../testdata/v0/units/test-parse-units.yaml | 432 + .../v0/units/test-units-precision.yaml | 13 + .../v0/urlbuiltins/test-urlbuiltins-0939.yaml | 14 + .../v0/urlbuiltins/test-urlbuiltins-0940.yaml | 14 + .../v0/urlbuiltins/test-urlbuiltins-0941.yaml | 14 + .../v0/urlbuiltins/test-urlbuiltins-0942.yaml | 14 + .../v0/urlbuiltins/test-urlbuiltins-0943.yaml | 14 + .../v0/urlbuiltins/test-urlbuiltins-0944.yaml | 14 + .../v0/urlbuiltins/test-urlbuiltins-0945.yaml | 14 + .../v0/urlbuiltins/test-urlbuiltins-0946.yaml | 14 + .../v0/urlbuiltins/test-urlbuiltins-1076.yaml | 35 + .../v0/uuid/test-uuid-input-formats.yaml | 52 + .../v0/uuid/test-uuid-parse-rule.yaml | 19 + .../testdata/v0/uuid/test-uuid-parse.yaml | 64 + .../test-varreferences-0726.yaml | 17 + .../test-varreferences-0727.yaml | 20 + .../test-varreferences-0728.yaml | 16 + .../test-varreferences-0729.yaml | 29 + .../test-varreferences-0730.yaml | 21 + .../test-varreferences-0731.yaml | 20 + .../test-varreferences-0732.yaml | 34 + .../test-varreferences-0733.yaml | 20 + .../test-varreferences-0734.yaml | 25 + .../test-varreferences-0735.yaml | 20 + .../test-varreferences-0736.yaml | 25 + .../test-varreferences-0737.yaml | 20 + .../test-varreferences-0738.yaml | 26 + .../test-varreferences-0739.yaml | 22 + .../test-varreferences-0740.yaml | 16 + .../test-varreferences-0741.yaml | 23 + .../test-varreferences-0742.yaml | 15 + .../v0/virtualdocs/test-virtualdocs-0620.yaml | 23 + .../v0/virtualdocs/test-virtualdocs-0621.yaml | 25 + .../v0/virtualdocs/test-virtualdocs-0622.yaml | 27 + .../v0/virtualdocs/test-virtualdocs-0623.yaml | 17 + .../v0/virtualdocs/test-virtualdocs-0624.yaml | 24 + .../v0/virtualdocs/test-virtualdocs-0625.yaml | 26 + .../v0/virtualdocs/test-virtualdocs-0626.yaml | 20 + .../v0/virtualdocs/test-virtualdocs-0627.yaml | 21 + .../v0/virtualdocs/test-virtualdocs-0628.yaml | 22 + .../v0/virtualdocs/test-virtualdocs-0629.yaml | 24 + .../v0/virtualdocs/test-virtualdocs-0630.yaml | 23 + .../v0/virtualdocs/test-virtualdocs-0631.yaml | 23 + .../v0/virtualdocs/test-virtualdocs-0632.yaml | 29 + .../v0/virtualdocs/test-virtualdocs-0633.yaml | 30 + .../v0/virtualdocs/test-virtualdocs-0634.yaml | 22 + .../v0/virtualdocs/test-virtualdocs-0635.yaml | 22 + .../v0/virtualdocs/test-virtualdocs-0636.yaml | 22 + .../v0/virtualdocs/test-virtualdocs-0637.yaml | 29 + .../v0/virtualdocs/test-virtualdocs-0638.yaml | 30 + .../v0/virtualdocs/test-virtualdocs-0639.yaml | 29 + .../v0/virtualdocs/test-virtualdocs-0640.yaml | 22 + .../v0/virtualdocs/test-virtualdocs-0641.yaml | 22 + .../v0/virtualdocs/test-virtualdocs-0642.yaml | 28 + .../v0/virtualdocs/test-virtualdocs-0643.yaml | 28 + .../v0/virtualdocs/test-virtualdocs-0644.yaml | 26 + .../v0/virtualdocs/test-virtualdocs-0645.yaml | 23 + .../v0/virtualdocs/test-virtualdocs-0646.yaml | 28 + .../v0/virtualdocs/test-virtualdocs-0647.yaml | 21 + .../v0/virtualdocs/test-virtualdocs-0648.yaml | 23 + .../v0/virtualdocs/test-virtualdocs-0649.yaml | 25 + .../v0/virtualdocs/test-virtualdocs-0650.yaml | 27 + .../v0/virtualdocs/test-virtualdocs-0651.yaml | 23 + .../v0/virtualdocs/test-virtualdocs-0652.yaml | 24 + .../v0/virtualdocs/test-virtualdocs-0653.yaml | 31 + .../v0/virtualdocs/test-virtualdocs-0654.yaml | 37 + .../v0/virtualdocs/test-virtualdocs-0655.yaml | 44 + .../v0/virtualdocs/test-virtualdocs-0656.yaml | 45 + .../v0/virtualdocs/test-virtualdocs-0657.yaml | 39 + .../v0/virtualdocs/test-virtualdocs-0658.yaml | 26 + .../v0/virtualdocs/test-virtualdocs-0659.yaml | 25 + .../v0/virtualdocs/test-virtualdocs-0660.yaml | 27 + .../v0/virtualdocs/test-virtualdocs-0661.yaml | 27 + .../v0/virtualdocs/test-virtualdocs-0662.yaml | 29 + .../v0/virtualdocs/test-virtualdocs-0663.yaml | 16 + .../v0/virtualdocs/test-virtualdocs-0664.yaml | 16 + .../v0/virtualdocs/test-virtualdocs-0665.yaml | 16 + .../v0/virtualdocs/test-virtualdocs-0666.yaml | 16 + .../v0/virtualdocs/test-virtualdocs-0667.yaml | 16 + .../v0/virtualdocs/test-virtualdocs-0668.yaml | 21 + .../v0/virtualdocs/test-virtualdocs-0669.yaml | 21 + .../v0/virtualdocs/test-virtualdocs-0670.yaml | 20 + .../v0/virtualdocs/test-virtualdocs-0671.yaml | 26 + .../v0/virtualdocs/test-virtualdocs-0672.yaml | 22 + .../v0/virtualdocs/test-virtualdocs-0673.yaml | 21 + .../v0/virtualdocs/test-virtualdocs-0674.yaml | 22 + .../v0/virtualdocs/test-virtualdocs-0675.yaml | 22 + .../v0/virtualdocs/test-virtualdocs-0676.yaml | 24 + .../v0/virtualdocs/test-virtualdocs-0677.yaml | 16 + .../v0/virtualdocs/test-virtualdocs-0678.yaml | 18 + .../v0/virtualdocs/test-virtualdocs-0679.yaml | 18 + .../v0/virtualdocs/test-virtualdocs-0680.yaml | 20 + .../v0/virtualdocs/test-virtualdocs-0681.yaml | 24 + .../v0/virtualdocs/test-virtualdocs-0682.yaml | 34 + .../v0/virtualdocs/test-virtualdocs-0683.yaml | 34 + .../v0/virtualdocs/test-virtualdocs-0684.yaml | 25 + .../v0/virtualdocs/test-virtualdocs-0685.yaml | 26 + .../v0/virtualdocs/test-virtualdocs-0686.yaml | 30 + .../v0/virtualdocs/test-virtualdocs-0687.yaml | 21 + .../v0/virtualdocs/test-virtualdocs-0688.yaml | 29 + .../v0/virtualdocs/test-virtualdocs-0689.yaml | 20 + .../v0/virtualdocs/test-virtualdocs-0690.yaml | 19 + .../v0/virtualdocs/test-virtualdocs-0691.yaml | 19 + .../v0/virtualdocs/test-virtualdocs-0692.yaml | 19 + .../v0/virtualdocs/test-virtualdocs-0693.yaml | 24 + .../v0/virtualdocs/test-virtualdocs-0694.yaml | 23 + .../test-virtualdocs-undefined.yaml | 46 + .../v0/walkbuiltin/test-walkbuiltin-0970.yaml | 23 + .../v0/walkbuiltin/test-walkbuiltin-0971.yaml | 34 + .../v0/walkbuiltin/test-walkbuiltin-0972.yaml | 26 + .../v0/walkbuiltin/test-walkbuiltin-0973.yaml | 43 + .../v0/walkbuiltin/test-walkbuiltin-0974.yaml | 30 + .../v0/walkbuiltin/test-walkbuiltin-0975.yaml | 33 + .../test-walkbuiltin-wildcard-path.yaml | 29 + .../test-with-and-ndbcache-issue.yaml | 17 + .../withkeyword/test-with-builtin-mock.yaml | 452 + .../withkeyword/test-with-function-mock.yaml | 128 + .../test-with-function-mocks-issue-5299.yaml | 60 + .../v0/withkeyword/test-withkeyword-1015.yaml | 22 + .../v0/withkeyword/test-withkeyword-1016.yaml | 22 + .../v0/withkeyword/test-withkeyword-1017.yaml | 24 + .../v0/withkeyword/test-withkeyword-1018.yaml | 26 + .../v0/withkeyword/test-withkeyword-1019.yaml | 23 + .../v0/withkeyword/test-withkeyword-1020.yaml | 29 + .../v0/withkeyword/test-withkeyword-1021.yaml | 30 + .../v0/withkeyword/test-withkeyword-1022.yaml | 36 + .../v0/withkeyword/test-withkeyword-1023.yaml | 45 + .../v0/withkeyword/test-withkeyword-1024.yaml | 22 + .../v0/withkeyword/test-withkeyword-1025.yaml | 25 + .../v0/withkeyword/test-withkeyword-1026.yaml | 30 + .../v0/withkeyword/test-withkeyword-1027.yaml | 18 + .../v0/withkeyword/test-withkeyword-1028.yaml | 25 + .../v0/withkeyword/test-withkeyword-1029.yaml | 28 + .../v0/withkeyword/test-withkeyword-1030.yaml | 19 + .../v0/withkeyword/test-withkeyword-1031.yaml | 22 + .../v0/withkeyword/test-withkeyword-1032.yaml | 25 + .../v0/withkeyword/test-withkeyword-1033.yaml | 23 + .../v0/withkeyword/test-withkeyword-1034.yaml | 28 + .../v0/withkeyword/test-withkeyword-1035.yaml | 21 + .../v0/withkeyword/test-withkeyword-1036.yaml | 17 + .../v0/withkeyword/test-withkeyword-1037.yaml | 18 + .../v0/withkeyword/test-withkeyword-1038.yaml | 13 + .../v0/withkeyword/test-withkeyword-1039.yaml | 29 + .../v0/withkeyword/test-withkeyword-1040.yaml | 23 + .../v0/withkeyword/test-withkeyword-1041.yaml | 23 + .../v0/withkeyword/test-withkeyword-1042.yaml | 20 + .../v0/withkeyword/test-withkeyword-1043.yaml | 20 + .../v0/withkeyword/test-withkeyword-1044.yaml | 20 + .../v0/withkeyword/test-withkeyword-1045.yaml | 20 + .../v0/withkeyword/test-withkeyword-1046.yaml | 39 + .../v0/withkeyword/test-withkeyword-1047.yaml | 21 + .../v0/withkeyword/test-withkeyword-1048.yaml | 20 + .../v0/withkeyword/test-withkeyword-1049.yaml | 21 + .../v0/withkeyword/test-withkeyword-1050.yaml | 19 + .../v0/withkeyword/test-withkeyword-1051.yaml | 25 + .../v0/withkeyword/test-withkeyword-1052.yaml | 20 + .../v0/withkeyword/test-withkeyword-1053.yaml | 30 + .../v0/withkeyword/test-withkeyword-1054.yaml | 77 + .../v1/aggregates/test-aggregates-0001.yaml | 22 + .../v1/aggregates/test-aggregates-0002.yaml | 27 + .../v1/aggregates/test-aggregates-0003.yaml | 20 + .../v1/aggregates/test-aggregates-0004.yaml | 24 + .../v1/aggregates/test-aggregates-0005.yaml | 24 + .../v1/aggregates/test-aggregates-0006.yaml | 16 + .../v1/aggregates/test-aggregates-0007.yaml | 14 + .../v1/aggregates/test-aggregates-0008.yaml | 26 + .../v1/aggregates/test-aggregates-0009.yaml | 25 + .../v1/aggregates/test-aggregates-0010.yaml | 15 + .../v1/aggregates/test-aggregates-0011.yaml | 16 + .../v1/aggregates/test-aggregates-0012.yaml | 14 + .../v1/aggregates/test-aggregates-0013.yaml | 17 + .../v1/aggregates/test-aggregates-0014.yaml | 14 + .../v1/aggregates/test-aggregates-0015.yaml | 27 + .../v1/aggregates/test-aggregates-0016.yaml | 24 + .../v1/aggregates/test-aggregates-0017.yaml | 17 + .../v1/aggregates/test-aggregates-0018.yaml | 16 + .../v1/aggregates/test-aggregates-0019.yaml | 17 + .../v1/aggregates/test-aggregates-0020.yaml | 14 + .../v1/aggregates/test-aggregates-0021.yaml | 26 + .../v1/aggregates/test-aggregates-0022.yaml | 25 + .../v1/aggregates/test-aggregates-0023.yaml | 20 + .../v1/aggregates/test-aggregates-0024.yaml | 20 + .../v1/aggregates/test-aggregates-0025.yaml | 18 + .../v1/aggregates/test-aggregates-0026.yaml | 18 + .../v1/aggregates/test-aggregates-0027.yaml | 24 + .../v1/aggregates/test-aggregates-0028.yaml | 30 + .../test-aggregates-bad-utf8-runes.yaml | 15 + .../v1/aggregates/test-membership.yaml | 529 + .../v1/arithmetic/test-arithmetic-0810.yaml | 26 + .../v1/arithmetic/test-arithmetic-0811.yaml | 23 + .../v1/arithmetic/test-arithmetic-0812.yaml | 26 + .../v1/arithmetic/test-arithmetic-0813.yaml | 24 + .../v1/arithmetic/test-arithmetic-0814.yaml | 19 + .../v1/arithmetic/test-arithmetic-0815.yaml | 15 + .../v1/arithmetic/test-arithmetic-0816.yaml | 15 + .../v1/arithmetic/test-arithmetic-0817.yaml | 16 + .../v1/arithmetic/test-arithmetic-0818.yaml | 14 + .../v1/arithmetic/test-arithmetic-0819.yaml | 20 + .../v1/arithmetic/test-arithmetic-0820.yaml | 20 + .../v1/arithmetic/test-arithmetic-0821.yaml | 20 + .../v1/arithmetic/test-arithmetic-0822.yaml | 20 + .../v1/arithmetic/test-arithmetic-0823.yaml | 15 + .../v1/arithmetic/test-arithmetic-0824.yaml | 62 + .../v1/arithmetic/test-arithmetic-0825.yaml | 62 + .../test-arithmetic-minus-type-error.yaml | 26 + .../v1/arithmetic/test-big-int-0001.yaml | 13 + .../testdata/v1/array/test-array-0041.yaml | 19 + .../testdata/v1/array/test-array-0042.yaml | 20 + .../testdata/v1/array/test-array-0043.yaml | 20 + .../testdata/v1/array/test-array-0044.yaml | 17 + .../testdata/v1/array/test-array-0045.yaml | 15 + .../testdata/v1/array/test-array-0046.yaml | 15 + .../testdata/v1/array/test-array-0047.yaml | 15 + .../testdata/v1/array/test-array-0048.yaml | 17 + .../testdata/v1/array/test-array-0049.yaml | 17 + .../testdata/v1/array/test-array-0050.yaml | 15 + .../testdata/v1/array/test-array-0051.yaml | 15 + .../testdata/v1/array/test-array-0052.yaml | 44 + .../test-file-level-assignments.yaml | 61 + .../test-base64builtins-0929.yaml | 14 + .../test-base64builtins-0930.yaml | 14 + .../test-base64builtins-0931.yaml | 15 + .../test-base64builtins-0932.yaml | 14 + .../test-base64builtins-0933.yaml | 14 + .../test-base64builtins-0934.yaml | 14 + .../test-base64builtins-0935.yaml | 26 + .../test-base64urlbuiltins-0935.yaml | 24 + .../test-base64urlbuiltins-0937.yaml | 24 + .../test-base64urlbuiltins-0939.yaml | 24 + .../test-baseandvirtualdocs-0695.yaml | 189 + .../test-baseandvirtualdocs-0696.yaml | 273 + .../test-baseandvirtualdocs-0697.yaml | 297 + .../test-baseandvirtualdocs-0698.yaml | 170 + .../test-baseandvirtualdocs-0699.yaml | 156 + .../test-baseandvirtualdocs-0700.yaml | 153 + .../test-baseandvirtualdocs-0701.yaml | 165 + .../test-baseandvirtualdocs-0702.yaml | 153 + .../test-baseandvirtualdocs-0703.yaml | 156 + .../test-baseandvirtualdocs-0704.yaml | 157 + .../test-baseandvirtualdocs-0705.yaml | 158 + .../v1/bitsand/test-bitsand-0055.yaml | 17 + .../v1/bitsand/test-bitsand-0056.yaml | 17 + .../v1/bitsand/test-bitsand-0057.yaml | 16 + .../v1/bitsnegate/test-bitsnegate-0058.yaml | 17 + .../v1/bitsnegate/test-bitsnegate-0059.yaml | 16 + .../testdata/v1/bitsor/test-bitsor-0052.yaml | 17 + .../testdata/v1/bitsor/test-bitsor-0053.yaml | 17 + .../testdata/v1/bitsor/test-bitsor-0054.yaml | 16 + .../test-bitsshiftleft-0063.yaml | 17 + .../test-bitsshiftleft-0064.yaml | 16 + .../test-bitsshiftleft-0065.yaml | 16 + .../test-bitsshiftleft-0066.yaml | 15 + .../test-bitsshiftleft-0067.yaml | 15 + .../test-bitsshiftright-0068.yaml | 17 + .../test-bitsshiftright-0069.yaml | 16 + .../test-bitsshiftright-0070.yaml | 16 + .../v1/bitsxor/test-bitsxor-0060.yaml | 17 + .../v1/bitsxor/test-bitsxor-0061.yaml | 17 + .../v1/bitsxor/test-bitsxor-0062.yaml | 16 + .../testdata/v1/casts/test-casts-0824.yaml | 23 + .../testdata/v1/casts/test-casts-0825.yaml | 20 + .../testdata/v1/casts/test-casts-0826.yaml | 20 + .../testdata/v1/casts/test-casts-0827.yaml | 14 + .../testdata/v1/casts/test-casts-0828.yaml | 90 + .../test-comparisonexpr-0608.yaml | 21 + .../test-comparisonexpr-0609.yaml | 21 + .../test-comparisonexpr-0610.yaml | 21 + .../test-comparisonexpr-0611.yaml | 21 + .../test-comparisonexpr-0612.yaml | 21 + .../test-comparisonexpr-0613.yaml | 21 + .../test-comparisonexpr-0614.yaml | 13 + .../test-comparisonexpr-0615.yaml | 13 + .../test-comparisonexpr-0616.yaml | 13 + .../test-comparisonexpr-0617.yaml | 13 + .../test-comparisonexpr-0618.yaml | 13 + .../test-comparisonexpr-0619.yaml | 12 + .../test-comparisonexpr-0620.yaml | 46 + .../v1/completedoc/test-completedoc-0495.yaml | 13 + .../v1/completedoc/test-completedoc-0496.yaml | 12 + .../v1/completedoc/test-completedoc-0497.yaml | 12 + .../v1/completedoc/test-completedoc-0498.yaml | 12 + .../v1/completedoc/test-completedoc-0499.yaml | 12 + .../v1/completedoc/test-completedoc-0500.yaml | 12 + .../v1/completedoc/test-completedoc-0501.yaml | 12 + .../v1/completedoc/test-completedoc-0502.yaml | 12 + .../v1/completedoc/test-completedoc-0503.yaml | 12 + .../v1/completedoc/test-completedoc-0504.yaml | 16 + .../v1/completedoc/test-completedoc-0505.yaml | 12 + .../v1/completedoc/test-completedoc-0506.yaml | 18 + .../v1/completedoc/test-completedoc-0507.yaml | 14 + .../v1/completedoc/test-completedoc-0508.yaml | 17 + .../v1/completedoc/test-completedoc-0509.yaml | 18 + .../v1/completedoc/test-completedoc-0510.yaml | 17 + .../test-compositebasedereference-1073.yaml | 19 + .../test-compositebasedereference-1074.yaml | 19 + .../test-compositebasedereference-1075.yaml | 19 + .../test-compositereferences-0743.yaml | 39 + .../test-compositereferences-0744.yaml | 38 + .../test-compositereferences-0745.yaml | 39 + .../test-compositereferences-0746.yaml | 40 + .../test-compositereferences-0747.yaml | 38 + .../test-compositereferences-0748.yaml | 40 + .../test-compositereferences-0749.yaml | 47 + .../test-compositereferences-0750.yaml | 47 + .../test-compositereferences-0751.yaml | 36 + .../test-compositereferences-0752.yaml | 40 + .../test-compositereferences-0753.yaml | 41 + .../test-compositereferences-0754.yaml | 41 + .../test-compositereferences-0755.yaml | 36 + .../test-compositereferences-0756.yaml | 36 + .../test-compositereferences-0757.yaml | 36 + .../test-comprehensions-0781.yaml | 25 + .../test-comprehensions-0782.yaml | 25 + .../test-comprehensions-0783.yaml | 26 + .../test-comprehensions-0784.yaml | 26 + .../test-comprehensions-0785.yaml | 24 + .../test-comprehensions-0786.yaml | 17 + .../test-comprehensions-0787.yaml | 29 + .../test-comprehensions-0788.yaml | 25 + .../test-comprehensions-0789.yaml | 25 + .../test-comprehensions-0790.yaml | 19 + .../test-comprehensions-0791.yaml | 26 + .../test-comprehensions-0792.yaml | 26 + .../test-comprehensions-0793.yaml | 24 + .../test-comprehensions-0794.yaml | 17 + .../test-comprehensions-0795.yaml | 24 + .../test-comprehensions-0796.yaml | 24 + .../test-comprehensions-0797.yaml | 22 + .../test-comprehensions-0798.yaml | 25 + .../test-comprehensions-0799.yaml | 26 + .../test-comprehensions-0800.yaml | 26 + .../test-comprehensions-0801.yaml | 24 + .../test-comprehensions-0802.yaml | 17 + .../test-comprehensions-0803.yaml | 29 + .../test-comprehensions-and-vars.yaml | 18 + .../test-contains-future-keyword.yaml | 63 + .../cryptohmacequal/test-cryptohmacequal.yaml | 77 + .../v1/cryptohmacmd5/test-cryptohmacmd5.yaml | 33 + .../cryptohmacsha1/test-cryptohmacsha1.yaml | 33 + .../test-cryptohmacsha256.yaml | 33 + .../test-cryptohmacsha512.yaml | 33 + .../v1/cryptomd5/test-cryptomd5-0130.yaml | 16 + .../test-cryptoparsersaprivatekey-1.yaml | 37 + .../v1/cryptosha1/test-cryptosha1-0131.yaml | 17 + .../cryptosha256/test-cryptosha256-0132.yaml | 17 + ...-cryptox509parseandverifycertificates.yaml | 130 + ...ryptox509parsecertificaterequest-0125.yaml | 19 + ...ryptox509parsecertificaterequest-0126.yaml | 19 + ...ryptox509parsecertificaterequest-0127.yaml | 19 + ...ryptox509parsecertificaterequest-0128.yaml | 19 + ...ryptox509parsecertificaterequest-0129.yaml | 19 + ...test-cryptox509parsecertificates-0117.yaml | 20 + ...test-cryptox509parsecertificates-0118.yaml | 21 + ...test-cryptox509parsecertificates-0119.yaml | 20 + ...test-cryptox509parsecertificates-0120.yaml | 20 + ...test-cryptox509parsecertificates-0121.yaml | 22 + ...test-cryptox509parsecertificates-0122.yaml | 22 + ...test-cryptox509parsecertificates-0123.yaml | 19 + ...test-cryptox509parsecertificates-0124.yaml | 19 + ...-cryptox509parsecertificates-raw-uris.yaml | 25 + .../test-cryptox509parsekeypairs-0118.yaml | 94 + .../test-cryptox509parsekeypairs-0119.yaml | 14 + .../test-cryptox509parsersaprivatekey-1.yaml | 32 + .../v1/dataderef/test-data-derefs.yaml | 38 + .../test-default-functions.yaml | 40 + .../test-defaultkeyword-0804.yaml | 20 + .../test-defaultkeyword-0805.yaml | 18 + .../test-defaultkeyword-0806.yaml | 18 + .../test-defaultkeyword-0807.yaml | 25 + .../test-defaultkeyword-0808.yaml | 22 + .../test-defaultkeyword-0809.yaml | 25 + .../v1/disjunction/test-disjunction-0763.yaml | 33 + .../v1/disjunction/test-disjunction-0764.yaml | 27 + .../v1/disjunction/test-disjunction-0765.yaml | 48 + .../v1/disjunction/test-disjunction-0766.yaml | 16 + .../v1/disjunction/test-disjunction-0767.yaml | 37 + .../v1/disjunction/test-disjunction-0768.yaml | 38 + .../v1/disjunction/test-disjunction-0769.yaml | 52 + .../v1/disjunction/test-disjunction-0770.yaml | 20 + .../v1/disjunction/test-disjunction-0771.yaml | 17 + .../v1/disjunction/test-disjunction-0772.yaml | 18 + .../v1/disjunction/test-disjunction-0773.yaml | 14 + .../v1/disjunction/test-disjunction-0774.yaml | 16 + .../v1/disjunction/test-disjunction-0775.yaml | 208 + .../v1/disjunction/test-disjunction-0776.yaml | 18 + .../v1/elsekeyword/test-elsekeyword-1054.yaml | 14 + .../v1/elsekeyword/test-elsekeyword-1055.yaml | 14 + .../v1/elsekeyword/test-elsekeyword-1056.yaml | 15 + .../v1/elsekeyword/test-elsekeyword-1057.yaml | 16 + .../v1/elsekeyword/test-elsekeyword-1058.yaml | 26 + .../v1/elsekeyword/test-elsekeyword-1059.yaml | 28 + .../v1/elsekeyword/test-elsekeyword-1060.yaml | 16 + .../v1/elsekeyword/test-elsekeyword-1061.yaml | 18 + .../v1/elsekeyword/test-elsekeyword-1062.yaml | 20 + .../v1/elsekeyword/test-elsekeyword-1063.yaml | 24 + .../v1/elsekeyword/test-elsekeyword-1064.yaml | 34 + .../v1/elsekeyword/test-elsekeyword-1065.yaml | 16 + .../v1/elsekeyword/test-elsekeyword-1066.yaml | 21 + .../v1/elsekeyword/test-elsekeyword-1067.yaml | 25 + .../test-embeddedvirtualdoc-0976.yaml | 45 + .../testdata/v1/eqexpr/test-eqexpr-0545.yaml | 13 + .../testdata/v1/eqexpr/test-eqexpr-0546.yaml | 13 + .../testdata/v1/eqexpr/test-eqexpr-0547.yaml | 18 + .../testdata/v1/eqexpr/test-eqexpr-0548.yaml | 18 + .../testdata/v1/eqexpr/test-eqexpr-0549.yaml | 19 + .../testdata/v1/eqexpr/test-eqexpr-0550.yaml | 13 + .../testdata/v1/eqexpr/test-eqexpr-0551.yaml | 13 + .../testdata/v1/eqexpr/test-eqexpr-0552.yaml | 20 + .../testdata/v1/eqexpr/test-eqexpr-0553.yaml | 13 + .../testdata/v1/eqexpr/test-eqexpr-0554.yaml | 13 + .../testdata/v1/eqexpr/test-eqexpr-0555.yaml | 13 + .../testdata/v1/eqexpr/test-eqexpr-0556.yaml | 14 + .../testdata/v1/eqexpr/test-eqexpr-0557.yaml | 14 + .../testdata/v1/eqexpr/test-eqexpr-0558.yaml | 14 + .../testdata/v1/eqexpr/test-eqexpr-0559.yaml | 14 + .../testdata/v1/eqexpr/test-eqexpr-0560.yaml | 14 + .../testdata/v1/eqexpr/test-eqexpr-0561.yaml | 14 + .../testdata/v1/eqexpr/test-eqexpr-0562.yaml | 14 + .../testdata/v1/eqexpr/test-eqexpr-0563.yaml | 19 + .../testdata/v1/eqexpr/test-eqexpr-0564.yaml | 16 + .../testdata/v1/eqexpr/test-eqexpr-0565.yaml | 18 + .../testdata/v1/eqexpr/test-eqexpr-0566.yaml | 25 + .../testdata/v1/eqexpr/test-eqexpr-0567.yaml | 18 + .../testdata/v1/eqexpr/test-eqexpr-0568.yaml | 20 + .../testdata/v1/eqexpr/test-eqexpr-0569.yaml | 20 + .../testdata/v1/eqexpr/test-eqexpr-0570.yaml | 20 + .../testdata/v1/eqexpr/test-eqexpr-0571.yaml | 20 + .../testdata/v1/eqexpr/test-eqexpr-0572.yaml | 22 + .../testdata/v1/eqexpr/test-eqexpr-0573.yaml | 31 + .../testdata/v1/eqexpr/test-eqexpr-0574.yaml | 16 + .../testdata/v1/eqexpr/test-eqexpr-0575.yaml | 16 + .../testdata/v1/eqexpr/test-eqexpr-0576.yaml | 16 + .../testdata/v1/eqexpr/test-eqexpr-0577.yaml | 18 + .../testdata/v1/eqexpr/test-eqexpr-0578.yaml | 18 + .../testdata/v1/eqexpr/test-eqexpr-0579.yaml | 25 + .../testdata/v1/eqexpr/test-eqexpr-0580.yaml | 28 + .../testdata/v1/eqexpr/test-eqexpr-0581.yaml | 27 + .../testdata/v1/eqexpr/test-eqexpr-0582.yaml | 27 + .../testdata/v1/eqexpr/test-eqexpr-0583.yaml | 19 + .../testdata/v1/eqexpr/test-eqexpr-0584.yaml | 16 + .../testdata/v1/eqexpr/test-eqexpr-0585.yaml | 16 + .../testdata/v1/eqexpr/test-eqexpr-0586.yaml | 18 + .../testdata/v1/eqexpr/test-eqexpr-0587.yaml | 18 + .../testdata/v1/eqexpr/test-eqexpr-0588.yaml | 28 + .../testdata/v1/eqexpr/test-eqexpr-0589.yaml | 30 + .../testdata/v1/eqexpr/test-eqexpr-0590.yaml | 31 + .../testdata/v1/eqexpr/test-eqexpr-0591.yaml | 31 + .../testdata/v1/eqexpr/test-eqexpr-0592.yaml | 18 + .../testdata/v1/eqexpr/test-eqexpr-0593.yaml | 25 + .../testdata/v1/eqexpr/test-eqexpr-0594.yaml | 29 + .../testdata/v1/eqexpr/test-eqexpr-0595.yaml | 17 + .../testdata/v1/eqexpr/test-eqexpr-0596.yaml | 15 + .../testdata/v1/eqexpr/test-eqexpr-0597.yaml | 17 + .../testdata/v1/eqexpr/test-eqexpr-0598.yaml | 34 + .../testdata/v1/eqexpr/test-eqexpr-0599.yaml | 22 + .../evaltermexpr/test-evaltermexpr-0525.yaml | 12 + .../evaltermexpr/test-evaltermexpr-0526.yaml | 13 + .../evaltermexpr/test-evaltermexpr-0527.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0528.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0529.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0530.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0531.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0532.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0533.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0534.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0535.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0536.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0537.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0538.yaml | 19 + .../evaltermexpr/test-evaltermexpr-0539.yaml | 13 + .../evaltermexpr/test-evaltermexpr-0540.yaml | 18 + .../evaltermexpr/test-evaltermexpr-0541.yaml | 24 + .../evaltermexpr/test-evaltermexpr-0542.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0543.yaml | 14 + .../evaltermexpr/test-evaltermexpr-0544.yaml | 21 + .../test/cases/testdata/v1/every/every.yaml | 212 + .../v1/every/non_iterable_domain.yaml | 141 + .../cases/testdata/v1/every/textbook.yaml | 138 + .../v1/example/test-example-1070.yaml | 88 + .../v1/example/test-example-1071.yaml | 85 + .../v1/example/test-example-1072.yaml | 102 + .../v1/fix1863/test-fix1863-0706.yaml | 23 + .../v1/fix1863/test-fix1863-0707.yaml | 20 + .../v1/fix1863/test-fix1863-0708.yaml | 25 + .../v1/functionerrors/test-conflicts.yaml | 28 + .../test-functionerrors-1012.yaml | 32 + .../test-functionerrors-1013.yaml | 20 + .../test-functionerrors-1014.yaml | 21 + ...nctionerrors-undefined-builtin-result.yaml | 14 + .../v1/functions/test-functions-0990.yaml | 277 + .../v1/functions/test-functions-0991.yaml | 198 + .../v1/functions/test-functions-0992.yaml | 199 + .../v1/functions/test-functions-0993.yaml | 199 + .../v1/functions/test-functions-0994.yaml | 198 + .../v1/functions/test-functions-0995.yaml | 199 + .../v1/functions/test-functions-0996.yaml | 201 + .../v1/functions/test-functions-0997.yaml | 201 + .../v1/functions/test-functions-0998.yaml | 202 + .../v1/functions/test-functions-0999.yaml | 202 + .../v1/functions/test-functions-1000.yaml | 199 + .../v1/functions/test-functions-1001.yaml | 199 + .../v1/functions/test-functions-1002.yaml | 199 + .../v1/functions/test-functions-1003.yaml | 199 + .../v1/functions/test-functions-1004.yaml | 198 + .../v1/functions/test-functions-1005.yaml | 199 + .../v1/functions/test-functions-1006.yaml | 199 + .../v1/functions/test-functions-1007.yaml | 199 + .../v1/functions/test-functions-1008.yaml | 199 + .../v1/functions/test-functions-1009.yaml | 201 + .../v1/functions/test-functions-1010.yaml | 202 + .../v1/functions/test-functions-1011.yaml | 199 + .../v1/functions/test-functions-default.yaml | 86 + ...test-functions-nested-with-early-exit.yaml | 93 + .../functions/test-functions-unused-arg.yaml | 12 + .../v1/globmatch/test-globmatch-0133.yaml | 16 + .../v1/globmatch/test-globmatch-0134.yaml | 16 + .../v1/globmatch/test-globmatch-0135.yaml | 16 + .../v1/globmatch/test-globmatch-0136.yaml | 16 + .../v1/globmatch/test-globmatch-0137.yaml | 16 + .../v1/globmatch/test-globmatch-0138.yaml | 16 + .../v1/globmatch/test-globmatch-0139.yaml | 16 + .../v1/globmatch/test-globmatch-0140.yaml | 16 + .../v1/globmatch/test-globmatch-0141.yaml | 16 + .../v1/globmatch/test-globmatch-0142.yaml | 16 + .../v1/globmatch/test-globmatch-0143.yaml | 16 + .../v1/globmatch/test-globmatch-0144.yaml | 16 + .../v1/globmatch/test-globmatch-0145.yaml | 16 + .../v1/globmatch/test-globmatch-0146.yaml | 16 + .../v1/globmatch/test-globmatch-0147.yaml | 16 + .../v1/globmatch/test-globmatch-0148.yaml | 16 + .../v1/globmatch/test-globmatch-0149.yaml | 16 + .../v1/globmatch/test-globmatch-0150.yaml | 16 + .../v1/globmatch/test-globmatch-0151.yaml | 16 + .../v1/globmatch/test-globmatch-0152.yaml | 16 + .../v1/globmatch/test-globmatch-0153.yaml | 16 + .../v1/globmatch/test-globmatch-0154.yaml | 16 + .../v1/globmatch/test-globmatch-0155.yaml | 16 + .../v1/globmatch/test-globmatch-0156.yaml | 16 + .../v1/globmatch/test-globmatch-0157.yaml | 16 + .../v1/globmatch/test-globmatch-0158.yaml | 16 + .../v1/globmatch/test-globmatch-0159.yaml | 30 + .../globmatch/test-globmatch-issue-5273.yaml | 15 + .../globmatch/test-globmatch-issue-5283.yaml | 26 + .../test-globquotemeta-0159.yaml | 16 + .../v1/globsmatch/test-globsmatch-0865.yaml | 14 + .../v1/globsmatch/test-globsmatch-0866.yaml | 13 + .../v1/globsmatch/test-globsmatch-0867.yaml | 14 + .../v1/globsmatch/test-globsmatch-0868.yaml | 22 + .../v1/globsmatch/test-globsmatch-0869.yaml | 14 + .../v1/globsmatch/test-globsmatch-0870.yaml | 13 + .../v1/graphql/test-graphql-basic-ast.yaml | 265 + .../v1/graphql/test-graphql-is-valid.yaml | 279 + .../test-graphql-parse-and-verify.yaml | 197 + .../v1/graphql/test-graphql-parse-query.yaml | 509 + .../v1/graphql/test-graphql-parse-schema.yaml | 744 + .../v1/graphql/test-graphql-parse.yaml | 192 + .../graphql/test-graphql-schema-is-valid.yaml | 838 + .../v1/helloworld/test-helloworld-1.yaml | 24 + .../v1/hexbuiltins/test-hexbuiltins-0939.yaml | 13 + .../v1/hexbuiltins/test-hexbuiltins-0940.yaml | 13 + .../v1/hexbuiltins/test-hexbuiltins-0941.yaml | 14 + .../cases/testdata/v1/indexing/array-any.yaml | 19 + .../test-indirectreferences-0758.yaml | 19 + .../test-indirectreferences-0759.yaml | 15 + .../test-indirectreferences-0760.yaml | 21 + .../test-indirectreferences-0761.yaml | 19 + .../test-indirectreferences-0762.yaml | 19 + .../v1/inputvalues/test-inputvalues-0977.yaml | 65 + .../v1/inputvalues/test-inputvalues-0978.yaml | 68 + .../v1/inputvalues/test-inputvalues-0979.yaml | 75 + .../v1/inputvalues/test-inputvalues-0980.yaml | 110 + .../v1/inputvalues/test-inputvalues-0981.yaml | 106 + .../v1/inputvalues/test-inputvalues-0982.yaml | 71 + .../v1/inputvalues/test-inputvalues-0983.yaml | 71 + .../intersection/test-intersection-0352.yaml | 14 + .../intersection/test-intersection-0353.yaml | 14 + .../intersection/test-intersection-0354.yaml | 17 + .../intersection/test-intersection-0355.yaml | 17 + .../intersection/test-intersection-0356.yaml | 19 + .../test-invalidkeyerror-0176.yaml | 14 + .../test-invalidkeyerror-0177.yaml | 14 + .../v1/jsonbuiltins/test-is-valid.yaml | 72 + .../test-json-marshal-with-options.yaml | 143 + .../jsonbuiltins/test-jsonbuiltins-0924.yaml | 14 + .../jsonbuiltins/test-jsonbuiltins-0925.yaml | 18 + .../jsonbuiltins/test-jsonbuiltins-0926.yaml | 21 + .../jsonbuiltins/test-jsonbuiltins-0927.yaml | 19 + .../jsonbuiltins/test-jsonbuiltins-0928.yaml | 14 + .../jsonbuiltins/test-marshal-large-ints.yaml | 14 + .../v1/jsonfilter/test-jsonfilter-0218.yaml | 18 + .../v1/jsonfilter/test-jsonfilter-0219.yaml | 19 + .../v1/jsonfilter/test-jsonfilter-0220.yaml | 19 + .../v1/jsonfilter/test-jsonfilter-0221.yaml | 19 + .../v1/jsonfilter/test-jsonfilter-0222.yaml | 17 + .../v1/jsonfilter/test-jsonfilter-0223.yaml | 15 + .../v1/jsonfilter/test-jsonfilter-0224.yaml | 15 + .../v1/jsonfilter/test-jsonfilter-0225.yaml | 18 + .../v1/jsonfilter/test-jsonfilter-0226.yaml | 18 + .../v1/jsonfilter/test-jsonfilter-0227.yaml | 19 + .../v1/jsonfilter/test-jsonfilter-0228.yaml | 19 + .../test-jsonfilteridempotent-0229.yaml | 19 + .../cases/testdata/v1/jsonpatch/coverage.yaml | 14 + .../v1/jsonpatch/json-patch-tests.yaml | 794 + .../test/cases/testdata/v1/jsonpatch/set.yaml | 82 + .../v1/jsonremove/test-jsonremove-0230.yaml | 19 + .../v1/jsonremove/test-jsonremove-0231.yaml | 18 + .../v1/jsonremove/test-jsonremove-0232.yaml | 18 + .../v1/jsonremove/test-jsonremove-0233.yaml | 18 + .../v1/jsonremove/test-jsonremove-0234.yaml | 16 + .../v1/jsonremove/test-jsonremove-0235.yaml | 16 + .../v1/jsonremove/test-jsonremove-0236.yaml | 15 + .../v1/jsonremove/test-jsonremove-0237.yaml | 15 + .../v1/jsonremove/test-jsonremove-0238.yaml | 16 + .../v1/jsonremove/test-jsonremove-0239.yaml | 17 + .../v1/jsonremove/test-jsonremove-0240.yaml | 20 + .../v1/jsonremove/test-jsonremove-0241.yaml | 18 + .../v1/jsonremove/test-jsonremove-0242.yaml | 19 + .../v1/jsonremove/test-jsonremove-0243.yaml | 17 + .../v1/jsonremove/test-jsonremove-0244.yaml | 18 + .../v1/jsonremove/test-jsonremove-0245.yaml | 18 + .../v1/jsonremove/test-jsonremove-0246.yaml | 18 + .../v1/jsonremove/test-jsonremove-0247.yaml | 18 + .../v1/jsonremove/test-jsonremove-0248.yaml | 18 + .../v1/jsonremove/test-jsonremove-0249.yaml | 18 + .../v1/jsonremove/test-jsonremove-0250.yaml | 18 + .../v1/jsonremove/test-jsonremove-0251.yaml | 18 + .../v1/jsonremove/test-jsonremove-0252.yaml | 18 + .../v1/jsonremove/test-jsonremove-0253.yaml | 17 + .../v1/jsonremove/test-jsonremove-0254.yaml | 18 + .../test-jsonremoveidempotent-0255.yaml | 21 + .../v1/jsonschema/test-json-match_schema.yaml | 98 + .../jsonschema/test-json-verify_schema.yaml | 54 + .../v1/jwtbuiltins/test-jwtbuiltins-0389.yaml | 19 + .../v1/jwtbuiltins/test-jwtbuiltins-0390.yaml | 19 + .../v1/jwtbuiltins/test-jwtbuiltins-0391.yaml | 15 + .../v1/jwtbuiltins/test-jwtbuiltins-0392.yaml | 15 + .../v1/jwtbuiltins/test-jwtbuiltins-0393.yaml | 15 + .../v1/jwtbuiltins/test-jwtbuiltins-0394.yaml | 15 + .../v1/jwtbuiltins/test-jwtbuiltins-0395.yaml | 15 + .../v1/jwtbuiltins/test-jwtbuiltins-0396.yaml | 15 + .../v1/jwtbuiltins/test-jwtbuiltins-0397.yaml | 19 + .../v1/jwtbuiltins/test-jwtbuiltins-0398.yaml | 19 + .../v1/jwtbuiltins/test-jwtbuiltins-0399.yaml | 24 + .../v1/jwtbuiltins/test-jwtbuiltins-0400.yaml | 19 + .../test-jwtdecodeverify-0449.yaml | 17 + .../test-jwtdecodeverify-0450.yaml | 16 + .../test-jwtdecodeverify-0451.yaml | 16 + .../test-jwtdecodeverify-0452.yaml | 17 + .../test-jwtdecodeverify-0453.yaml | 16 + .../test-jwtdecodeverify-0454.yaml | 17 + .../test-jwtdecodeverify-0455.yaml | 16 + .../test-jwtdecodeverify-0456.yaml | 18 + .../test-jwtdecodeverify-0457.yaml | 16 + .../test-jwtdecodeverify-0458.yaml | 16 + .../test-jwtdecodeverify-0459.yaml | 17 + .../test-jwtdecodeverify-0460.yaml | 18 + .../test-jwtdecodeverify-0461.yaml | 18 + .../test-jwtdecodeverify-0462.yaml | 16 + .../test-jwtdecodeverify-0463.yaml | 16 + .../test-jwtdecodeverify-0464.yaml | 16 + .../test-jwtdecodeverify-0465.yaml | 17 + .../test-jwtdecodeverify-0466.yaml | 17 + .../test-jwtdecodeverify-0467.yaml | 17 + .../test-jwtdecodeverify-0468.yaml | 20 + .../test-jwtdecodeverify-0469.yaml | 16 + .../test-jwtdecodeverify-0470.yaml | 18 + .../test-jwtdecodeverify-0471.yaml | 20 + .../test-jwtdecodeverify-0472.yaml | 16 + .../test-jwtdecodeverify-0473.yaml | 16 + .../test-jwtdecodeverify-0474.yaml | 16 + .../test-jwtdecodeverify-0475.yaml | 16 + .../test-jwtdecodeverify-0476.yaml | 46 + .../test-jwtdecodeverify-0477.yaml | 42 + .../test-jwtdecodeverify-0478.yaml | 18 + .../test-jwtdecodeverify-0479.yaml | 16 + .../test-jwtdecodeverify-0480.yaml | 18 + .../test-jwtdecodeverify-0481.yaml | 16 + .../test-jwtdecodeverify-0482.yaml | 16 + .../test-jwtdecodeverify-0483.yaml | 18 + .../test-jwtdecodeverify-0484.yaml | 18 + .../test-jwtdecodeverify-0485.yaml | 18 + .../test-jwtdecodeverify-0486.yaml | 18 + .../test-jwtdecodeverify-0487.yaml | 18 + .../test-jwtdecodeverify-0488.yaml | 18 + .../test-jwtdecodeverify-0489.yaml | 16 + .../test-jwtdecodeverify-0490.yaml | 16 + .../test-jwtdecodeverify-0491.yaml | 16 + ...test-jwtdecodeverify-invalid-exp-type.yaml | 14 + ...test-jwtdecodeverify-invalid-nbf-type.yaml | 14 + ...codeverify-missing-iss-while-required.yaml | 16 + .../test-jwtencodesign-0492.yaml | 14 + .../test-jwtencodesign-0493.yaml | 14 + .../test-jwtencodesign-0494.yaml | 14 + ...test-jwtencodesign-integer-timestamps.yaml | 17 + .../test-jwtencodesign-set-data.yaml | 17 + .../test-jwtencodesignheadererrors-0379.yaml | 15 + .../test-jwtencodesignheadererrors-0380.yaml | 15 + .../test-jwtencodesignheadererrors-0381.yaml | 15 + .../test-jwtencodesignheadererrors-0382.yaml | 15 + .../test-jwtencodesignheadererrors-0383.yaml | 15 + .../test-jwtencodesignpayloaderrors-0376.yaml | 15 + .../test-jwtencodesignpayloaderrors-0377.yaml | 15 + .../test-jwtencodesignpayloaderrors-0378.yaml | 15 + .../test-jwtencodesignraw-0384.yaml | 9 + .../test-jwtencodesignraw-0385.yaml | 9 + .../test-jwtencodesignraw-0386.yaml | 9 + .../test-jwtencodesignraw-0387.yaml | 9 + .../test-jwtencodesignraw-0388.yaml | 9 + .../test-jwtverifyhs256-0440.yaml | 14 + .../test-jwtverifyhs256-0441.yaml | 14 + .../test-jwtverifyhs256-0442.yaml | 15 + .../test-jwtverifyhs384-0443.yaml | 14 + .../test-jwtverifyhs384-0444.yaml | 14 + .../test-jwtverifyhs384-0445.yaml | 15 + .../test-jwtverifyhs512-0446.yaml | 14 + .../test-jwtverifyhs512-0447.yaml | 14 + .../test-jwtverifyhs512-0448.yaml | 15 + .../jwtverifyrsa/test-jwtverifyrsa-0401.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0402.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0403.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0404.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0405.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0406.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0407.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0408.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0409.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0410.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0411.yaml | 15 + .../jwtverifyrsa/test-jwtverifyrsa-0412.yaml | 15 + .../jwtverifyrsa/test-jwtverifyrsa-0413.yaml | 15 + .../jwtverifyrsa/test-jwtverifyrsa-0414.yaml | 15 + .../jwtverifyrsa/test-jwtverifyrsa-0415.yaml | 15 + .../jwtverifyrsa/test-jwtverifyrsa-0416.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0417.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0418.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0419.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0420.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0421.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0422.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0423.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0424.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0425.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0426.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0427.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0428.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0429.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0430.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0431.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0432.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0433.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0434.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0435.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0436.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0437.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0438.yaml | 14 + .../jwtverifyrsa/test-jwtverifyrsa-0439.yaml | 14 + .../v1/keywordrefs/test-keyword-as.yaml | 199 + .../v1/keywordrefs/test-keyword-contains.yaml | 199 + .../v1/keywordrefs/test-keyword-default.yaml | 199 + .../v1/keywordrefs/test-keyword-else.yaml | 199 + .../v1/keywordrefs/test-keyword-every.yaml | 199 + .../v1/keywordrefs/test-keyword-false.yaml | 199 + .../v1/keywordrefs/test-keyword-if.yaml | 199 + .../v1/keywordrefs/test-keyword-import.yaml | 199 + .../v1/keywordrefs/test-keyword-in.yaml | 199 + .../v1/keywordrefs/test-keyword-not.yaml | 199 + .../v1/keywordrefs/test-keyword-null.yaml | 199 + .../v1/keywordrefs/test-keyword-package.yaml | 199 + .../v1/keywordrefs/test-keyword-some.yaml | 199 + .../v1/keywordrefs/test-keyword-true.yaml | 199 + .../v1/keywordrefs/test-keyword-with.yaml | 199 + .../v1/negation/test-negation-0777.yaml | 13 + .../v1/negation/test-negation-0778.yaml | 12 + .../v1/negation/test-negation-0779.yaml | 18 + .../v1/negation/test-negation-0780.yaml | 20 + .../test-negation-data-ref-with-var.yaml | 77 + .../test-nestedreferences-0709.yaml | 27 + .../test-nestedreferences-0710.yaml | 31 + .../test-nestedreferences-0711.yaml | 24 + .../test-nestedreferences-0712.yaml | 31 + .../test-nestedreferences-0713.yaml | 30 + .../test-nestedreferences-0714.yaml | 21 + .../test-nestedreferences-0715.yaml | 25 + .../test-nestedreferences-0716.yaml | 14 + .../test-nestedreferences-0717.yaml | 25 + .../test-nestedreferences-0718.yaml | 24 + .../test-nestedreferences-0719.yaml | 22 + .../test-nestedreferences-0720.yaml | 28 + .../test-nestedreferences-0721.yaml | 27 + .../test-nestedreferences-0722.yaml | 29 + .../test-nestedreferences-0723.yaml | 32 + .../test-nestedreferences-0724.yaml | 36 + .../test-nestedreferences-0725.yaml | 31 + .../test-netcidrcontains-0092.yaml | 16 + .../test-netcidrcontains-0093.yaml | 16 + .../test-netcidrcontains-0094.yaml | 16 + .../test-netcidrcontains-0095.yaml | 16 + .../test-netcidrcontains-0096.yaml | 16 + .../test-netcidrcontains-0097.yaml | 16 + .../test-netcidrcontains-0098.yaml | 16 + .../test-netcidrcontains-0099.yaml | 16 + .../test-netcidrcontains-0100.yaml | 14 + .../test-netcidrcontains-0101.yaml | 14 + .../test-netcidrcontains-0102.yaml | 16 + .../test-netcidrcontains-0103.yaml | 16 + .../test-netcidrcontainsmatches-0104.yaml | 17 + .../test-netcidrcontainsmatches-0105.yaml | 19 + .../test-netcidrcontainsmatches-0106.yaml | 19 + .../test-netcidrcontainsmatches-0107.yaml | 19 + .../test-netcidrcontainsmatches-0108.yaml | 20 + .../test-netcidrcontainsmatches-0109.yaml | 23 + .../test-netcidrcontainsmatches-0110.yaml | 19 + .../test-netcidrcontainsmatches-0111.yaml | 15 + .../test-netcidrcontainsmatches-0112.yaml | 19 + .../test-netcidrexpand-0113.yaml | 18 + .../test-netcidrexpand-0114.yaml | 18 + .../test-netcidrexpand-0115.yaml | 15 + .../test-netcidrexpand-0116.yaml | 14 + .../test-netcidrintersects-0086.yaml | 16 + .../test-netcidrintersects-0087.yaml | 16 + .../test-netcidrintersects-0088.yaml | 16 + .../test-netcidrintersects-0089.yaml | 16 + .../test-netcidrintersects-0090.yaml | 14 + .../test-netcidrintersects-0091.yaml | 14 + .../test_netcidrisvalid-0001.yaml | 67 + .../test-ipv6-with-and-without-prefix.yaml | 60 + .../netcidrmerge/test-netcidrmerge0117.yaml | 214 + .../netlookupipaddr/test-netlookupipaddr.yaml | 46 + .../numbersrange/test-numbersrange-0256.yaml | 16 + .../numbersrange/test-numbersrange-0257.yaml | 21 + .../numbersrange/test-numbersrange-0258.yaml | 38 + .../numbersrange/test-numbersrange-0259.yaml | 14 + .../numbersrange/test-numbersrange-0260.yaml | 14 + .../numbersrange/test-numbersrange-0261.yaml | 14 + .../test-numbersrange-issue-7269.yaml | 12 + .../test-numbersrangestep.yaml | 103 + .../objectfilter/test-objectfilter-0300.yaml | 19 + .../objectfilter/test-objectfilter-0301.yaml | 17 + .../objectfilter/test-objectfilter-0302.yaml | 17 + .../objectfilter/test-objectfilter-0303.yaml | 17 + .../objectfilter/test-objectfilter-0304.yaml | 19 + .../objectfilter/test-objectfilter-0305.yaml | 15 + .../objectfilter/test-objectfilter-0306.yaml | 15 + .../objectfilter/test-objectfilter-0307.yaml | 15 + .../objectfilter/test-objectfilter-0308.yaml | 15 + .../objectfilter/test-objectfilter-0309.yaml | 17 + .../objectfilter/test-objectfilter-0310.yaml | 17 + .../objectfilter/test-objectfilter-0311.yaml | 17 + .../objectfilter/test-objectfilter-0312.yaml | 17 + .../objectfilter/test-objectfilter-0313.yaml | 17 + .../objectfilter/test-objectfilter-0314.yaml | 17 + .../objectfilter/test-objectfilter-0315.yaml | 17 + .../objectfilter/test-objectfilter-0316.yaml | 17 + .../objectfilter/test-objectfilter-0317.yaml | 17 + .../test-objectfilteridempotent-0319.yaml | 21 + .../test-objectfilternonstringkey-0318.yaml | 16 + .../v1/objectget/test-objectget-0262.yaml | 15 + .../v1/objectget/test-objectget-0263.yaml | 15 + .../v1/objectget/test-objectget-0264.yaml | 15 + .../v1/objectget/test-objectget-0265.yaml | 15 + .../v1/objectget/test-objectget-0266.yaml | 16 + .../v1/objectget/test-objectget-0267.yaml | 15 + .../v1/objectget/test-objectget-path.yaml | 126 + .../v1/objectkeys/test-objectkeys.yaml | 77 + .../objectremove/test-objectremove-0279.yaml | 17 + .../objectremove/test-objectremove-0280.yaml | 16 + .../objectremove/test-objectremove-0281.yaml | 16 + .../objectremove/test-objectremove-0282.yaml | 16 + .../objectremove/test-objectremove-0283.yaml | 16 + .../objectremove/test-objectremove-0284.yaml | 15 + .../objectremove/test-objectremove-0285.yaml | 18 + .../objectremove/test-objectremove-0286.yaml | 18 + .../objectremove/test-objectremove-0287.yaml | 18 + .../objectremove/test-objectremove-0288.yaml | 18 + .../objectremove/test-objectremove-0289.yaml | 18 + .../objectremove/test-objectremove-0290.yaml | 18 + .../objectremove/test-objectremove-0291.yaml | 18 + .../objectremove/test-objectremove-0292.yaml | 18 + .../objectremove/test-objectremove-0293.yaml | 18 + .../objectremove/test-objectremove-0294.yaml | 18 + .../objectremove/test-objectremove-0295.yaml | 18 + .../objectremove/test-objectremove-0296.yaml | 18 + .../objectremove/test-objectremove-0297.yaml | 18 + .../test-objectremoveidempotent-0298.yaml | 21 + .../test-objectremovenonstringkey-0299.yaml | 16 + .../v1/objectunion/test-objectunion-0268.yaml | 15 + .../v1/objectunion/test-objectunion-0269.yaml | 16 + .../v1/objectunion/test-objectunion-0270.yaml | 16 + .../v1/objectunion/test-objectunion-0271.yaml | 17 + .../v1/objectunion/test-objectunion-0272.yaml | 19 + .../v1/objectunion/test-objectunion-0273.yaml | 19 + .../v1/objectunion/test-objectunion-0274.yaml | 16 + .../v1/objectunion/test-objectunion-0275.yaml | 19 + .../v1/objectunion/test-objectunion-0276.yaml | 20 + .../v1/objectunion/test-objectunion-0277.yaml | 18 + .../v1/objectunion/test-objectunion-0278.yaml | 18 + .../objectunionn/test-objectunionn-0001.yaml | 83 + .../test-partialdocconstants-0984.yaml | 26 + .../test-partialdocconstants-0985.yaml | 34 + .../test-partialdocconstants-0986.yaml | 49 + .../test-partialdocconstants-0987.yaml | 42 + .../test-partialdocconstants-0988.yaml | 45 + .../test-partialdocconstants-0989.yaml | 47 + .../v1/partialiter/test-partialiter-001.yaml | 46 + .../test-partialobjectdoc-0519.yaml | 19 + .../test-partialobjectdoc-0520.yaml | 21 + .../test-partialobjectdoc-0521.yaml | 39 + .../test-partialobjectdoc-0522.yaml | 41 + .../test-partialobjectdoc-0523.yaml | 18 + .../test-partialobjectdoc-0524.yaml | 18 + .../test-partialobjectdoc-ref.yaml | 21 + .../v1/partialobjectdoc/test-wasm-cases.yaml | 132 + .../v1/partialsetdoc/test-issue-3369.yaml | 20 + .../v1/partialsetdoc/test-issue-3376.yaml | 21 + .../v1/partialsetdoc/test-issue-3819.yaml | 27 + .../test-partialsetdoc-0511.yaml | 24 + .../test-partialsetdoc-0512.yaml | 24 + .../test-partialsetdoc-0513.yaml | 20 + .../test-partialsetdoc-0514.yaml | 20 + .../test-partialsetdoc-0515.yaml | 32 + .../test-partialsetdoc-0516.yaml | 31 + .../test-partialsetdoc-0517.yaml | 29 + .../test-partialsetdoc-0518.yaml | 18 + .../v1/planner-ir/test-array-ir-unify.yaml | 55 + .../v1/planner-ir/test-call-dynamic.yaml | 168 + .../v1/providers-aws/aws-sign_req-errors.yaml | 189 + .../v1/providers-aws/aws-sign_req.yaml | 346 + .../testdata/v1/rand/test-rand.intn.yaml | 13 + .../v1/reachable/test-reachable-0322.yaml | 16 + .../v1/reachable/test-reachable-0323.yaml | 24 + .../v1/reachable/test-reachable-0324.yaml | 29 + .../v1/reachable/test-reachable-0325.yaml | 24 + .../v1/reachable/test-reachable-0326.yaml | 18 + .../v1/reachable/test-reachable-0327.yaml | 19 + .../v1/reachable/test-reachable-0328.yaml | 18 + .../reachable/test-reachable-paths-0422.yaml | 137 + .../reachable/test-reachable-paths-1022.yaml | 81 + .../v1/refheads/test-generic-refs.yaml | 273 + .../v1/refheads/test-refs-as-rule-heads.yaml | 355 + .../v1/refheads/test-regressions.yaml | 180 + .../v1/regexfind/test-regexfind-0334.yaml | 19 + .../v1/regexfind/test-regexfind-0335.yaml | 18 + .../v1/regexfind/test-regexfind-0336.yaml | 17 + .../test-regexfindallstringsubmatch-0337.yaml | 17 + .../test-regexfindallstringsubmatch-0338.yaml | 18 + .../test-regexfindallstringsubmatch-0339.yaml | 18 + .../test-regexfindallstringsubmatch-0340.yaml | 20 + .../test-regexfindallstringsubmatch-0341.yaml | 20 + .../test-regexfindallstringsubmatch-0342.yaml | 22 + .../test-regexfindallstringsubmatch-0343.yaml | 17 + ...egexfindallstringsubmatch-large-input.yaml | 14 + .../regexisvalid/test-regexisvalid-0329.yaml | 16 + .../regexisvalid/test-regexisvalid-0330.yaml | 14 + .../regexisvalid/test-regexisvalid-0331.yaml | 14 + .../v1/regexmatch/test-regexmatch-0861.yaml | 14 + .../test-regexmatchtemplate-0332.yaml | 16 + .../test-regexmatchtemplate-0333.yaml | 16 + .../regexreplace/test-regexreplace-0001.yaml | 40 + .../v1/regexsplit/test-regexsplit-0862.yaml | 14 + .../v1/regexsplit/test-regexsplit-0863.yaml | 18 + .../v1/regexsplit/test-regexsplit-0864.yaml | 16 + .../test-regometadatachain-1.yaml | 96 + .../test-regometadatarule-1.yaml | 48 + .../test-regoparsemodule-0320.yaml | 23 + .../test-regoparsemodule-0321.yaml | 17 + .../v1/rendertemplate/rendertemplate.yaml | 54 + .../v1/replacen/test-replacen-0374.yaml | 41 + .../v1/replacen/test-replacen-0375.yaml | 17 + .../replacen/test-replacen-bad-operands.yaml | 37 + .../test-semvercompare-0344.yaml | 15 + .../test-semvercompare-0345.yaml | 15 + .../test-semvercompare-0346.yaml | 15 + .../test-semvercompare-0347.yaml | 15 + .../test-semvercompare-0348.yaml | 16 + .../test-semverisvalid-0349.yaml | 15 + .../test-semverisvalid-0350.yaml | 15 + .../test-semverisvalid-0351.yaml | 15 + .../testdata/v1/sets/test-sets-0871.yaml | 19 + .../testdata/v1/sets/test-sets-0872.yaml | 26 + .../testdata/v1/sets/test-sets-0873.yaml | 15 + .../testdata/v1/sets/test-sets-0874.yaml | 26 + .../testdata/v1/sets/test-sets-0875.yaml | 24 + .../testdata/v1/sets/test-sets-0876.yaml | 23 + .../testdata/v1/sprintf/test-sprintf.yaml | 24 + .../v1/strings/test-anyprefixmatch.yaml | 430 + .../v1/strings/test-anysuffixmatch.yaml | 430 + .../v1/strings/test-strings-0877.yaml | 14 + .../v1/strings/test-strings-0878.yaml | 13 + .../v1/strings/test-strings-0879.yaml | 20 + .../v1/strings/test-strings-0880.yaml | 20 + .../v1/strings/test-strings-0881.yaml | 14 + .../v1/strings/test-strings-0882.yaml | 14 + .../v1/strings/test-strings-0883.yaml | 25 + .../v1/strings/test-strings-0884.yaml | 13 + .../v1/strings/test-strings-0885.yaml | 26 + .../v1/strings/test-strings-0886.yaml | 26 + .../v1/strings/test-strings-0887.yaml | 24 + .../v1/strings/test-strings-0888.yaml | 14 + .../v1/strings/test-strings-0889.yaml | 25 + .../v1/strings/test-strings-0890.yaml | 25 + .../v1/strings/test-strings-0891.yaml | 25 + .../v1/strings/test-strings-0892.yaml | 26 + .../v1/strings/test-strings-0893.yaml | 25 + .../v1/strings/test-strings-0894.yaml | 25 + .../v1/strings/test-strings-0895.yaml | 14 + .../v1/strings/test-strings-0896.yaml | 13 + .../v1/strings/test-strings-0897.yaml | 14 + .../v1/strings/test-strings-0898.yaml | 13 + .../v1/strings/test-strings-0899.yaml | 14 + .../v1/strings/test-strings-0900.yaml | 13 + .../v1/strings/test-strings-0901.yaml | 14 + .../v1/strings/test-strings-0902.yaml | 14 + .../v1/strings/test-strings-0903.yaml | 14 + .../v1/strings/test-strings-0904.yaml | 14 + .../v1/strings/test-strings-0905.yaml | 16 + .../v1/strings/test-strings-0906.yaml | 14 + .../v1/strings/test-strings-0907.yaml | 14 + .../v1/strings/test-strings-0908.yaml | 14 + .../v1/strings/test-strings-0909.yaml | 14 + .../v1/strings/test-strings-0910.yaml | 14 + .../v1/strings/test-strings-0911.yaml | 14 + .../v1/strings/test-strings-0912.yaml | 14 + .../v1/strings/test-strings-0913.yaml | 14 + .../v1/strings/test-strings-0914.yaml | 14 + .../v1/strings/test-strings-0915.yaml | 14 + .../v1/strings/test-strings-0916.yaml | 14 + .../v1/strings/test-strings-0917.yaml | 14 + .../v1/strings/test-strings-0918.yaml | 14 + .../v1/strings/test-strings-0919.yaml | 14 + .../v1/strings/test-strings-0920.yaml | 14 + .../v1/strings/test-strings-0921.yaml | 14 + .../v1/strings/test-strings-0922.yaml | 14 + .../v1/strings/test-strings-0923.yaml | 14 + .../v1/strings/test-strings-0924.yaml | 71 + .../v1/strings/test-strings-0925.yaml | 47 + .../v1/strings/test-strings-0926.yaml | 29 + .../strings/test-strings-indexof-unicode.yaml | 47 + .../cases/testdata/v1/subset/test-subset.yaml | 415 + .../testdata/v1/time/test-time-0947.yaml | 17 + .../testdata/v1/time/test-time-0948.yaml | 64 + .../testdata/v1/time/test-time-0949.yaml | 48 + .../testdata/v1/time/test-time-0950.yaml | 14 + .../testdata/v1/time/test-time-0951.yaml | 21 + .../testdata/v1/time/test-time-0952.yaml | 21 + .../testdata/v1/time/test-time-0953.yaml | 21 + .../testdata/v1/time/test-time-0954.yaml | 21 + .../testdata/v1/time/test-time-0955.yaml | 19 + .../testdata/v1/time/test-time-0956.yaml | 21 + .../testdata/v1/time/test-time-0957.yaml | 21 + .../testdata/v1/time/test-time-0958.yaml | 21 + .../testdata/v1/time/test-time-0959.yaml | 19 + .../testdata/v1/time/test-time-0960.yaml | 15 + .../testdata/v1/time/test-time-0961.yaml | 15 + .../testdata/v1/time/test-time-0962.yaml | 15 + .../testdata/v1/time/test-time-0963.yaml | 15 + .../testdata/v1/time/test-time-0964.yaml | 15 + .../testdata/v1/time/test-time-0965.yaml | 15 + .../testdata/v1/time/test-time-0966.yaml | 15 + .../testdata/v1/time/test-time-0967.yaml | 19 + .../testdata/v1/time/test-time-0968.yaml | 26 + .../testdata/v1/time/test-time-0969.yaml | 26 + .../testdata/v1/time/test-time-0970.yaml | 66 + .../testdata/v1/time/test-time-0971.yaml | 33 + .../test-topdowndynamicdispatch-1068.yaml | 33 + .../testdata/v1/trim/test-trim-0362.yaml | 17 + .../testdata/v1/trim/test-trim-0363.yaml | 17 + .../v1/trimleft/test-trimleft-0364.yaml | 17 + .../v1/trimleft/test-trimleft-0365.yaml | 17 + .../v1/trimprefix/test-trimprefix-0366.yaml | 17 + .../v1/trimprefix/test-trimprefix-0367.yaml | 17 + .../v1/trimright/test-trimright-0368.yaml | 17 + .../v1/trimright/test-trimright-0369.yaml | 17 + .../v1/trimspace/test-trimspace-0372.yaml | 17 + .../v1/trimspace/test-trimspace-0373.yaml | 17 + .../v1/trimsuffix/test-trimsuffix-0370.yaml | 17 + .../v1/trimsuffix/test-trimsuffix-0371.yaml | 17 + .../testdata/v1/type/test-regressions.yaml | 88 + .../v1/typebuiltin/test-typebuiltin-0828.yaml | 19 + .../v1/typebuiltin/test-typebuiltin-0829.yaml | 14 + .../v1/typebuiltin/test-typebuiltin-0830.yaml | 14 + .../v1/typebuiltin/test-typebuiltin-0831.yaml | 18 + .../v1/typebuiltin/test-typebuiltin-0832.yaml | 19 + .../v1/typebuiltin/test-typebuiltin-0833.yaml | 14 + .../v1/typebuiltin/test-typebuiltin-0834.yaml | 14 + .../v1/typebuiltin/test-typebuiltin-0835.yaml | 18 + .../v1/typebuiltin/test-typebuiltin-0836.yaml | 17 + .../v1/typebuiltin/test-typebuiltin-0837.yaml | 14 + .../v1/typebuiltin/test-typebuiltin-0838.yaml | 14 + .../v1/typebuiltin/test-typebuiltin-0839.yaml | 18 + .../v1/typebuiltin/test-typebuiltin-0840.yaml | 17 + .../v1/typebuiltin/test-typebuiltin-0841.yaml | 14 + .../v1/typebuiltin/test-typebuiltin-0842.yaml | 17 + .../v1/typebuiltin/test-typebuiltin-0843.yaml | 14 + .../v1/typebuiltin/test-typebuiltin-0844.yaml | 15 + .../v1/typebuiltin/test-typebuiltin-0845.yaml | 14 + .../v1/typebuiltin/test-typebuiltin-0846.yaml | 14 + .../v1/typebuiltin/test-typebuiltin-0847.yaml | 14 + .../test-typenamebuiltin-0848.yaml | 14 + .../test-typenamebuiltin-0849.yaml | 14 + .../test-typenamebuiltin-0850.yaml | 14 + .../test-typenamebuiltin-0851.yaml | 14 + .../test-typenamebuiltin-0852.yaml | 14 + .../test-typenamebuiltin-0853.yaml | 14 + .../test-typenamebuiltin-0854.yaml | 15 + .../testdata/v1/undos/test-undos-0599.yaml | 18 + .../testdata/v1/undos/test-undos-0600.yaml | 18 + .../testdata/v1/undos/test-undos-0601.yaml | 18 + .../testdata/v1/undos/test-undos-0602.yaml | 22 + .../testdata/v1/undos/test-undos-0603.yaml | 18 + .../testdata/v1/undos/test-undos-0604.yaml | 18 + .../testdata/v1/undos/test-undos-0605.yaml | 18 + .../testdata/v1/undos/test-undos-0606.yaml | 22 + .../testdata/v1/undos/test-undos-0607.yaml | 22 + .../testdata/v1/union/test-union-0357.yaml | 14 + .../testdata/v1/union/test-union-0358.yaml | 16 + .../testdata/v1/union/test-union-0359.yaml | 19 + .../testdata/v1/union/test-union-0360.yaml | 24 + .../testdata/v1/union/test-union-0361.yaml | 23 + .../testdata/v1/units/test-issue-4856.yaml | 35 + .../units/test-parse-bytes-comparisons.yaml | 112 + .../v1/units/test-parse-bytes-errors.yaml | 86 + .../testdata/v1/units/test-parse-bytes.yaml | 530 + .../units/test-parse-units-comparisons.yaml | 112 + .../v1/units/test-parse-units-errors.yaml | 86 + .../testdata/v1/units/test-parse-units.yaml | 530 + .../v1/units/test-units-precision.yaml | 13 + .../v1/urlbuiltins/test-urlbuiltins-0939.yaml | 14 + .../v1/urlbuiltins/test-urlbuiltins-0940.yaml | 14 + .../v1/urlbuiltins/test-urlbuiltins-0941.yaml | 14 + .../v1/urlbuiltins/test-urlbuiltins-0942.yaml | 14 + .../v1/urlbuiltins/test-urlbuiltins-0943.yaml | 14 + .../v1/urlbuiltins/test-urlbuiltins-0944.yaml | 14 + .../v1/urlbuiltins/test-urlbuiltins-0945.yaml | 14 + .../v1/urlbuiltins/test-urlbuiltins-0946.yaml | 14 + .../v1/urlbuiltins/test-urlbuiltins-1076.yaml | 44 + .../v1/uuid/test-uuid-input-formats.yaml | 50 + .../v1/uuid/test-uuid-parse-rule.yaml | 19 + .../testdata/v1/uuid/test-uuid-parse.yaml | 61 + .../test-varreferences-0726.yaml | 17 + .../test-varreferences-0727.yaml | 20 + .../test-varreferences-0728.yaml | 16 + .../test-varreferences-0729.yaml | 29 + .../test-varreferences-0730.yaml | 21 + .../test-varreferences-0731.yaml | 20 + .../test-varreferences-0732.yaml | 34 + .../test-varreferences-0733.yaml | 20 + .../test-varreferences-0734.yaml | 25 + .../test-varreferences-0735.yaml | 20 + .../test-varreferences-0736.yaml | 25 + .../test-varreferences-0737.yaml | 20 + .../test-varreferences-0738.yaml | 26 + .../test-varreferences-0739.yaml | 22 + .../test-varreferences-0740.yaml | 16 + .../test-varreferences-0741.yaml | 23 + .../test-varreferences-0742.yaml | 15 + .../v1/virtualdocs/test-virtualdocs-0620.yaml | 23 + .../v1/virtualdocs/test-virtualdocs-0621.yaml | 25 + .../v1/virtualdocs/test-virtualdocs-0622.yaml | 27 + .../v1/virtualdocs/test-virtualdocs-0623.yaml | 17 + .../v1/virtualdocs/test-virtualdocs-0624.yaml | 24 + .../v1/virtualdocs/test-virtualdocs-0625.yaml | 26 + .../v1/virtualdocs/test-virtualdocs-0626.yaml | 20 + .../v1/virtualdocs/test-virtualdocs-0627.yaml | 21 + .../v1/virtualdocs/test-virtualdocs-0628.yaml | 22 + .../v1/virtualdocs/test-virtualdocs-0629.yaml | 24 + .../v1/virtualdocs/test-virtualdocs-0630.yaml | 23 + .../v1/virtualdocs/test-virtualdocs-0631.yaml | 23 + .../v1/virtualdocs/test-virtualdocs-0632.yaml | 29 + .../v1/virtualdocs/test-virtualdocs-0633.yaml | 30 + .../v1/virtualdocs/test-virtualdocs-0634.yaml | 22 + .../v1/virtualdocs/test-virtualdocs-0635.yaml | 22 + .../v1/virtualdocs/test-virtualdocs-0636.yaml | 22 + .../v1/virtualdocs/test-virtualdocs-0637.yaml | 29 + .../v1/virtualdocs/test-virtualdocs-0638.yaml | 30 + .../v1/virtualdocs/test-virtualdocs-0639.yaml | 29 + .../v1/virtualdocs/test-virtualdocs-0640.yaml | 22 + .../v1/virtualdocs/test-virtualdocs-0641.yaml | 22 + .../v1/virtualdocs/test-virtualdocs-0642.yaml | 28 + .../v1/virtualdocs/test-virtualdocs-0643.yaml | 28 + .../v1/virtualdocs/test-virtualdocs-0644.yaml | 26 + .../v1/virtualdocs/test-virtualdocs-0645.yaml | 23 + .../v1/virtualdocs/test-virtualdocs-0646.yaml | 28 + .../v1/virtualdocs/test-virtualdocs-0647.yaml | 21 + .../v1/virtualdocs/test-virtualdocs-0648.yaml | 22 + .../v1/virtualdocs/test-virtualdocs-0649.yaml | 25 + .../v1/virtualdocs/test-virtualdocs-0650.yaml | 27 + .../v1/virtualdocs/test-virtualdocs-0651.yaml | 23 + .../v1/virtualdocs/test-virtualdocs-0652.yaml | 24 + .../v1/virtualdocs/test-virtualdocs-0653.yaml | 31 + .../v1/virtualdocs/test-virtualdocs-0654.yaml | 37 + .../v1/virtualdocs/test-virtualdocs-0655.yaml | 44 + .../v1/virtualdocs/test-virtualdocs-0656.yaml | 45 + .../v1/virtualdocs/test-virtualdocs-0657.yaml | 39 + .../v1/virtualdocs/test-virtualdocs-0658.yaml | 26 + .../v1/virtualdocs/test-virtualdocs-0659.yaml | 25 + .../v1/virtualdocs/test-virtualdocs-0660.yaml | 27 + .../v1/virtualdocs/test-virtualdocs-0661.yaml | 27 + .../v1/virtualdocs/test-virtualdocs-0662.yaml | 29 + .../v1/virtualdocs/test-virtualdocs-0663.yaml | 16 + .../v1/virtualdocs/test-virtualdocs-0664.yaml | 16 + .../v1/virtualdocs/test-virtualdocs-0665.yaml | 16 + .../v1/virtualdocs/test-virtualdocs-0666.yaml | 16 + .../v1/virtualdocs/test-virtualdocs-0667.yaml | 16 + .../v1/virtualdocs/test-virtualdocs-0668.yaml | 21 + .../v1/virtualdocs/test-virtualdocs-0669.yaml | 21 + .../v1/virtualdocs/test-virtualdocs-0670.yaml | 20 + .../v1/virtualdocs/test-virtualdocs-0671.yaml | 26 + .../v1/virtualdocs/test-virtualdocs-0672.yaml | 22 + .../v1/virtualdocs/test-virtualdocs-0673.yaml | 21 + .../v1/virtualdocs/test-virtualdocs-0674.yaml | 22 + .../v1/virtualdocs/test-virtualdocs-0675.yaml | 22 + .../v1/virtualdocs/test-virtualdocs-0676.yaml | 24 + .../v1/virtualdocs/test-virtualdocs-0677.yaml | 16 + .../v1/virtualdocs/test-virtualdocs-0678.yaml | 18 + .../v1/virtualdocs/test-virtualdocs-0679.yaml | 18 + .../v1/virtualdocs/test-virtualdocs-0680.yaml | 20 + .../v1/virtualdocs/test-virtualdocs-0681.yaml | 24 + .../v1/virtualdocs/test-virtualdocs-0682.yaml | 34 + .../v1/virtualdocs/test-virtualdocs-0683.yaml | 34 + .../v1/virtualdocs/test-virtualdocs-0684.yaml | 25 + .../v1/virtualdocs/test-virtualdocs-0685.yaml | 26 + .../v1/virtualdocs/test-virtualdocs-0686.yaml | 30 + .../v1/virtualdocs/test-virtualdocs-0687.yaml | 21 + .../v1/virtualdocs/test-virtualdocs-0688.yaml | 29 + .../v1/virtualdocs/test-virtualdocs-0689.yaml | 20 + .../v1/virtualdocs/test-virtualdocs-0690.yaml | 19 + .../v1/virtualdocs/test-virtualdocs-0691.yaml | 19 + .../v1/virtualdocs/test-virtualdocs-0692.yaml | 19 + .../v1/virtualdocs/test-virtualdocs-0693.yaml | 24 + .../v1/virtualdocs/test-virtualdocs-0694.yaml | 23 + .../test-virtualdocs-undefined.yaml | 42 + .../v1/walkbuiltin/test-walkbuiltin-0970.yaml | 41 + .../v1/walkbuiltin/test-walkbuiltin-0971.yaml | 58 + .../v1/walkbuiltin/test-walkbuiltin-0972.yaml | 44 + .../v1/walkbuiltin/test-walkbuiltin-0973.yaml | 58 + .../v1/walkbuiltin/test-walkbuiltin-0974.yaml | 28 + .../v1/walkbuiltin/test-walkbuiltin-0975.yaml | 33 + .../test-walkbuiltin-issue-7656.yaml | 26 + .../test-walkbuiltin-wildcard-path.yaml | 28 + .../test-with-and-ndbcache-issue.yaml | 17 + .../withkeyword/test-with-builtin-mock.yaml | 534 + .../withkeyword/test-with-function-mock.yaml | 215 + .../test-with-function-mocks-issue-5299.yaml | 73 + .../v1/withkeyword/test-withkeyword-1015.yaml | 22 + .../v1/withkeyword/test-withkeyword-1016.yaml | 22 + .../v1/withkeyword/test-withkeyword-1017.yaml | 24 + .../v1/withkeyword/test-withkeyword-1018.yaml | 26 + .../v1/withkeyword/test-withkeyword-1019.yaml | 22 + .../v1/withkeyword/test-withkeyword-1020.yaml | 29 + .../v1/withkeyword/test-withkeyword-1021.yaml | 30 + .../v1/withkeyword/test-withkeyword-1022.yaml | 36 + .../v1/withkeyword/test-withkeyword-1023.yaml | 45 + .../v1/withkeyword/test-withkeyword-1024.yaml | 22 + .../v1/withkeyword/test-withkeyword-1025.yaml | 25 + .../v1/withkeyword/test-withkeyword-1026.yaml | 30 + .../v1/withkeyword/test-withkeyword-1027.yaml | 18 + .../v1/withkeyword/test-withkeyword-1028.yaml | 25 + .../v1/withkeyword/test-withkeyword-1029.yaml | 28 + .../v1/withkeyword/test-withkeyword-1030.yaml | 19 + .../v1/withkeyword/test-withkeyword-1031.yaml | 22 + .../v1/withkeyword/test-withkeyword-1032.yaml | 25 + .../v1/withkeyword/test-withkeyword-1033.yaml | 23 + .../v1/withkeyword/test-withkeyword-1034.yaml | 28 + .../v1/withkeyword/test-withkeyword-1035.yaml | 22 + .../v1/withkeyword/test-withkeyword-1036.yaml | 17 + .../v1/withkeyword/test-withkeyword-1037.yaml | 18 + .../v1/withkeyword/test-withkeyword-1038.yaml | 13 + .../v1/withkeyword/test-withkeyword-1039.yaml | 29 + .../v1/withkeyword/test-withkeyword-1040.yaml | 23 + .../v1/withkeyword/test-withkeyword-1041.yaml | 23 + .../v1/withkeyword/test-withkeyword-1042.yaml | 20 + .../v1/withkeyword/test-withkeyword-1043.yaml | 20 + .../v1/withkeyword/test-withkeyword-1044.yaml | 20 + .../v1/withkeyword/test-withkeyword-1045.yaml | 20 + .../v1/withkeyword/test-withkeyword-1046.yaml | 39 + .../v1/withkeyword/test-withkeyword-1047.yaml | 21 + .../v1/withkeyword/test-withkeyword-1048.yaml | 20 + .../v1/withkeyword/test-withkeyword-1049.yaml | 21 + .../v1/withkeyword/test-withkeyword-1050.yaml | 19 + .../v1/withkeyword/test-withkeyword-1051.yaml | 25 + .../v1/withkeyword/test-withkeyword-1052.yaml | 20 + .../v1/withkeyword/test-withkeyword-1053.yaml | 29 + .../v1/withkeyword/test-withkeyword-1054.yaml | 72 + third_party/opa/v1/test/cli/smoke/.gitignore | 1 + third_party/opa/v1/test/cli/smoke/.manifest | 3 + third_party/opa/v1/test/cli/smoke/data.yaml | 3 + .../v1/test/cli/smoke/golden-bundle.tar.gz | Bin 0 -> 179 bytes third_party/opa/v1/test/cli/smoke/input.json | 1 + .../opa/v1/test/cli/smoke/namespace/data.json | 3 + third_party/opa/v1/test/cli/smoke/test.rego | 8 + .../e2e/authz/authz_bench_integration_test.go | 129 + third_party/opa/v1/test/e2e/authz/disk.go | 24 + third_party/opa/v1/test/e2e/authz/nodisk.go | 15 + .../test/e2e/certrefresh/certrefresh_test.go | 191 + .../test/e2e/certrefresh/testdata/.gitignore | 4 + .../test/e2e/certrefresh/testdata/gencerts.sh | 31 + .../test/e2e/concurrency/concurrency_test.go | 123 + .../test/e2e/diagnostics/diagnostics_test.go | 101 + .../distributedtracing_test.go | 915 + third_party/opa/v1/test/e2e/h2c/h2c_test.go | 68 + third_party/opa/v1/test/e2e/http/http_test.go | 156 + .../console_decision_logger_benchmark_test.go | 18 + .../console/console_decision_logger_test.go | 133 + .../remote_decision_logger_benchmark_test.go | 159 + third_party/opa/v1/test/e2e/logs/utils.go | 134 + .../opa/v1/test/e2e/metrics/metrics_test.go | 249 + third_party/opa/v1/test/e2e/oci/oci_test.go | 123 + .../opa/v1/test/e2e/print/print_test.go | 149 + .../opa/v1/test/e2e/shutdown/shutdown_test.go | 51 + third_party/opa/v1/test/e2e/testing.go | 510 + .../opa/v1/test/e2e/tls/testdata/.gitignore | 4 + .../opa/v1/test/e2e/tls/testdata/gencerts.sh | 35 + third_party/opa/v1/test/e2e/tls/tls_test.go | 259 + .../authz/authz_bench_integration_test.go | 186 + .../opa/v1/test/e2e/wasm/authz/disk.go | 24 + .../opa/v1/test/e2e/wasm/authz/nodisk.go | 15 + .../v1/test/scheduler/scheduler_bench_test.go | 357 + .../opa/v1/test/scheduler/scheduler_test.go | 483 + .../testdata/data_10nodes_30pods.json | 5569 ++++ .../opa/v1/test/wasm/assets/001_eq.yaml | 89 + .../v1/test/wasm/assets/002_iteration.yaml | 75 + .../v1/test/wasm/assets/003_comparison.yaml | 89 + .../opa/v1/test/wasm/assets/004_negation.yaml | 21 + .../v1/test/wasm/assets/005_references.yaml | 14 + .../wasm/assets/006_pattern_matching.yaml | 41 + .../opa/v1/test/wasm/assets/007_complete.yaml | 203 + .../v1/test/wasm/assets/008_functions.yaml | 129 + .../opa/v1/test/wasm/assets/009_default.yaml | 52 + .../opa/v1/test/wasm/assets/010_else.yaml | 123 + .../v1/test/wasm/assets/011_partialsets.yaml | 149 + .../test/wasm/assets/012_partialobjects.yaml | 78 + .../opa/v1/test/wasm/assets/013_virtual.yaml | 245 + .../test/wasm/assets/014_comprehensions.yaml | 45 + .../opa/v1/test/wasm/assets/015_results.yaml | 34 + .../opa/v1/test/wasm/assets/016_with.yaml | 337 + .../opa/v1/test/wasm/assets/017_strings.yaml | 30 + .../opa/v1/test/wasm/assets/018_builtins.yaml | 457 + .../019_call_indirect_optimization.yaml | 203 + third_party/opa/v1/test/wasm/assets/test.js | 389 + .../test/wasm/cmd/wasm-rego-testgen/main.go | 254 + third_party/opa/v1/tester/fixture_test.go | 63 + third_party/opa/v1/tester/reporter.go | 363 + third_party/opa/v1/tester/reporter_test.go | 1494 + .../opa/v1/tester/runer_compile_test.go | 474 + third_party/opa/v1/tester/runner.go | 1305 + third_party/opa/v1/tester/runner_test.go | 1095 + third_party/opa/v1/tester/test_tracer.go | 28 + third_party/opa/v1/topdown/aggregates.go | 302 + .../opa/v1/topdown/aggregates_bench_test.go | 119 + third_party/opa/v1/topdown/arithmetic.go | 240 + third_party/opa/v1/topdown/array.go | 105 + third_party/opa/v1/topdown/binary.go | 50 + third_party/opa/v1/topdown/bindings.go | 401 + third_party/opa/v1/topdown/bindings_test.go | 103 + third_party/opa/v1/topdown/bits.go | 88 + third_party/opa/v1/topdown/builtins.go | 224 + .../opa/v1/topdown/builtins/builtins.go | 329 + third_party/opa/v1/topdown/builtins_test.go | 45 + third_party/opa/v1/topdown/cache.go | 363 + third_party/opa/v1/topdown/cache/cache.go | 574 + .../opa/v1/topdown/cache/cache_test.go | 855 + .../opa/v1/topdown/cache_bench_test.go | 48 + third_party/opa/v1/topdown/cache_test.go | 61 + third_party/opa/v1/topdown/cancel.go | 33 + third_party/opa/v1/topdown/casts.go | 131 + third_party/opa/v1/topdown/cidr.go | 419 + third_party/opa/v1/topdown/cidr_test.go | 46 + third_party/opa/v1/topdown/comparison.go | 48 + .../copypropagation/copypropagation.go | 497 + .../v1/topdown/copypropagation/unionfind.go | 131 + .../topdown/copypropagation/unionfind_test.go | 220 + third_party/opa/v1/topdown/crypto.go | 783 + third_party/opa/v1/topdown/crypto_test.go | 894 + third_party/opa/v1/topdown/doc.go | 10 + third_party/opa/v1/topdown/encoding.go | 406 + third_party/opa/v1/topdown/errors.go | 149 + third_party/opa/v1/topdown/errors_test.go | 117 + third_party/opa/v1/topdown/eval.go | 4322 +++ third_party/opa/v1/topdown/eval_test.go | 1704 ++ third_party/opa/v1/topdown/example_test.go | 303 + third_party/opa/v1/topdown/exported_test.go | 212 + third_party/opa/v1/topdown/glob.go | 127 + third_party/opa/v1/topdown/glob_bench_test.go | 100 + third_party/opa/v1/topdown/glob_test.go | 183 + third_party/opa/v1/topdown/graphql.go | 692 + .../opa/v1/topdown/graphql_bench_test.go | 400 + third_party/opa/v1/topdown/graphql_test.go | 1002 + third_party/opa/v1/topdown/http.go | 1640 + third_party/opa/v1/topdown/http_fixup.go | 8 + .../opa/v1/topdown/http_fixup_darwin.go | 13 + third_party/opa/v1/topdown/http_slow_test.go | 223 + third_party/opa/v1/topdown/http_test.go | 3849 +++ third_party/opa/v1/topdown/input.go | 100 + third_party/opa/v1/topdown/input_test.go | 152 + third_party/opa/v1/topdown/instrumentation.go | 63 + third_party/opa/v1/topdown/json.go | 405 + third_party/opa/v1/topdown/json_bench_test.go | 512 + third_party/opa/v1/topdown/json_test.go | 117 + third_party/opa/v1/topdown/jsonschema.go | 130 + third_party/opa/v1/topdown/jsonschema_test.go | 338 + third_party/opa/v1/topdown/lineage/lineage.go | 84 + .../opa/v1/topdown/lineage/lineage_test.go | 201 + third_party/opa/v1/topdown/net.go | 64 + third_party/opa/v1/topdown/net_test.go | 222 + third_party/opa/v1/topdown/numbers.go | 201 + .../opa/v1/topdown/numbers_bench_test.go | 78 + third_party/opa/v1/topdown/numbers_test.go | 107 + third_party/opa/v1/topdown/object.go | 235 + .../opa/v1/topdown/object_bench_test.go | 110 + third_party/opa/v1/topdown/object_test.go | 93 + third_party/opa/v1/topdown/parse.go | 60 + third_party/opa/v1/topdown/parse_bytes.go | 157 + third_party/opa/v1/topdown/parse_units.go | 125 + third_party/opa/v1/topdown/print.go | 86 + third_party/opa/v1/topdown/print/print.go | 21 + third_party/opa/v1/topdown/print_test.go | 238 + third_party/opa/v1/topdown/providers.go | 211 + third_party/opa/v1/topdown/query.go | 639 + third_party/opa/v1/topdown/query_test.go | 320 + third_party/opa/v1/topdown/reachable.go | 151 + third_party/opa/v1/topdown/regex.go | 281 + .../opa/v1/topdown/regex_bench_test.go | 95 + third_party/opa/v1/topdown/regex_template.go | 122 + .../opa/v1/topdown/regex_template_test.go | 49 + third_party/opa/v1/topdown/regex_test.go | 163 + third_party/opa/v1/topdown/resolver.go | 118 + third_party/opa/v1/topdown/runtime.go | 130 + third_party/opa/v1/topdown/runtime_test.go | 87 + third_party/opa/v1/topdown/save.go | 523 + third_party/opa/v1/topdown/save_test.go | 65 + third_party/opa/v1/topdown/semver.go | 59 + third_party/opa/v1/topdown/sets.go | 94 + third_party/opa/v1/topdown/sets_bench_test.go | 205 + third_party/opa/v1/topdown/sets_test.go | 63 + third_party/opa/v1/topdown/strings.go | 796 + .../opa/v1/topdown/strings_bench_test.go | 395 + third_party/opa/v1/topdown/subset.go | 242 + third_party/opa/v1/topdown/template.go | 47 + third_party/opa/v1/topdown/test.go | 30 + .../opa/v1/topdown/testdata/.gitignore | 4 + .../cases/test-systemdocument-1069.yaml | 28 + .../opa/v1/topdown/testdata/gencerts.sh | 35 + third_party/opa/v1/topdown/time.go | 341 + third_party/opa/v1/topdown/time_test.go | 46 + third_party/opa/v1/topdown/tokens.go | 1329 + .../opa/v1/topdown/tokens_bench_test.go | 170 + third_party/opa/v1/topdown/tokens_test.go | 1157 + .../opa/v1/topdown/topdown_bench_test.go | 984 + .../v1/topdown/topdown_partial_bench_test.go | 80 + .../opa/v1/topdown/topdown_partial_test.go | 5248 ++++ third_party/opa/v1/topdown/topdown_test.go | 2479 ++ third_party/opa/v1/topdown/trace.go | 895 + third_party/opa/v1/topdown/trace_test.go | 1515 + third_party/opa/v1/topdown/type.go | 82 + third_party/opa/v1/topdown/type_name.go | 36 + third_party/opa/v1/topdown/uuid.go | 56 + third_party/opa/v1/topdown/uuid_test.go | 104 + third_party/opa/v1/topdown/walk.go | 163 + third_party/opa/v1/tracing/tracing.go | 55 + third_party/opa/v1/types/decode.go | 191 + third_party/opa/v1/types/types.go | 1204 + third_party/opa/v1/types/types_bench_test.go | 95 + third_party/opa/v1/types/types_test.go | 491 + third_party/opa/v1/util/backoff.go | 42 + third_party/opa/v1/util/channel.go | 32 + third_party/opa/v1/util/close.go | 22 + third_party/opa/v1/util/compare.go | 169 + third_party/opa/v1/util/compare_test.go | 54 + third_party/opa/v1/util/decoding/context.go | 31 + third_party/opa/v1/util/doc.go | 6 + third_party/opa/v1/util/enumflag.go | 59 + third_party/opa/v1/util/enumflag_test.go | 43 + third_party/opa/v1/util/graph.go | 90 + third_party/opa/v1/util/graph_test.go | 176 + third_party/opa/v1/util/hashmap.go | 271 + third_party/opa/v1/util/hashmap_test.go | 182 + third_party/opa/v1/util/json.go | 133 + third_party/opa/v1/util/json_test.go | 138 + third_party/opa/v1/util/maps.go | 34 + third_party/opa/v1/util/maps_test.go | 18 + third_party/opa/v1/util/performance.go | 75 + third_party/opa/v1/util/performance_test.go | 17 + third_party/opa/v1/util/queue.go | 113 + third_party/opa/v1/util/queue_test.go | 84 + third_party/opa/v1/util/read_gzip_body.go | 81 + third_party/opa/v1/util/test/benchmark.go | 265 + third_party/opa/v1/util/test/ci_skip.go | 10 + .../opa/v1/util/test/ci_skip_darwin.go | 15 + third_party/opa/v1/util/test/doc.go | 6 + third_party/opa/v1/util/test/tempfs.go | 90 + third_party/opa/v1/util/test/tempus.go | 55 + third_party/opa/v1/util/test/zeroreader.go | 20 + third_party/opa/v1/util/time.go | 48 + third_party/opa/v1/util/wait.go | 34 + third_party/opa/v1/util/wait_test.go | 43 + third_party/opa/v1/version/version.go | 58 + third_party/opa/v1/version/wasm.go | 13 + third_party/opa/version/doc.go | 8 + third_party/opa/version/version.go | 27 + third_party/opa/version/wasm.go | 14 + third_party/opa/wasm/Dockerfile | 50 + third_party/opa/wasm/Makefile | 171 + third_party/opa/wasm/README.md | 51 + third_party/opa/wasm/src/aggregates.c | 369 + third_party/opa/wasm/src/aggregates.h | 15 + third_party/opa/wasm/src/arithmetic.c | 246 + third_party/opa/wasm/src/arithmetic.h | 19 + third_party/opa/wasm/src/array.c | 89 + third_party/opa/wasm/src/array.h | 8 + third_party/opa/wasm/src/bits-builtins.c | 243 + third_party/opa/wasm/src/bits-builtins.h | 13 + third_party/opa/wasm/src/cidr.c | 340 + third_party/opa/wasm/src/cidr.h | 9 + third_party/opa/wasm/src/comparisons.c | 38 + third_party/opa/wasm/src/comparisons.h | 8 + third_party/opa/wasm/src/context.c | 82 + third_party/opa/wasm/src/context.h | 28 + third_party/opa/wasm/src/conversions.c | 34 + third_party/opa/wasm/src/conversions.h | 8 + third_party/opa/wasm/src/encoding.c | 340 + third_party/opa/wasm/src/encoding.h | 15 + third_party/opa/wasm/src/error.c | 21 + third_party/opa/wasm/src/error.h | 6 + third_party/opa/wasm/src/glob-compiler.cc | 154 + third_party/opa/wasm/src/glob-compiler.h | 9 + third_party/opa/wasm/src/glob-lexer.cc | 304 + third_party/opa/wasm/src/glob-lexer.h | 69 + third_party/opa/wasm/src/glob-parser.cc | 269 + third_party/opa/wasm/src/glob-parser.h | 41 + third_party/opa/wasm/src/glob.cc | 120 + third_party/opa/wasm/src/glob.h | 16 + third_party/opa/wasm/src/graphs.c | 86 + third_party/opa/wasm/src/graphs.h | 8 + third_party/opa/wasm/src/json.c | 1080 + third_party/opa/wasm/src/json.h | 50 + third_party/opa/wasm/src/lib/assert.h | 8 + third_party/opa/wasm/src/lib/bits.h | 12 + third_party/opa/wasm/src/lib/ctype.c | 33 + third_party/opa/wasm/src/lib/ctype.h | 34 + third_party/opa/wasm/src/lib/errno.c | 1 + third_party/opa/wasm/src/lib/errno.h | 139 + third_party/opa/wasm/src/lib/inttypes.h | 4 + third_party/opa/wasm/src/lib/locale.c | 13 + third_party/opa/wasm/src/lib/locale.h | 21 + third_party/opa/wasm/src/lib/math.c | 209 + third_party/opa/wasm/src/lib/math.h | 261 + third_party/opa/wasm/src/lib/printf.c | 858 + third_party/opa/wasm/src/lib/printf.h | 59 + third_party/opa/wasm/src/lib/signal.h | 16 + third_party/opa/wasm/src/lib/stdio.c | 51 + third_party/opa/wasm/src/lib/stdio.h | 79 + third_party/opa/wasm/src/lib/stdlib.c | 161 + third_party/opa/wasm/src/lib/stdlib.h | 90 + third_party/opa/wasm/src/lib/string.c | 118 + third_party/opa/wasm/src/lib/string.h | 40 + third_party/opa/wasm/src/lib/time.h | 40 + third_party/opa/wasm/src/lib/unistd.h | 4 + third_party/opa/wasm/src/lib/wchar.c | 68 + third_party/opa/wasm/src/lib/wchar.h | 92 + third_party/opa/wasm/src/lib/wctype.h | 38 + third_party/opa/wasm/src/libc++/atomic | 32 + third_party/opa/wasm/src/libc++/hash.cc | 561 + third_party/opa/wasm/src/libc++/minimal.cc | 42 + third_party/opa/wasm/src/libc++/mutex | 18 + third_party/opa/wasm/src/libc++/mutex.cc | 74 + third_party/opa/wasm/src/libmpdec/basearith.c | 658 + third_party/opa/wasm/src/libmpdec/basearith.h | 222 + third_party/opa/wasm/src/libmpdec/bits.h | 192 + third_party/opa/wasm/src/libmpdec/constants.c | 132 + third_party/opa/wasm/src/libmpdec/constants.h | 90 + third_party/opa/wasm/src/libmpdec/context.c | 286 + third_party/opa/wasm/src/libmpdec/convolute.c | 174 + third_party/opa/wasm/src/libmpdec/convolute.h | 50 + third_party/opa/wasm/src/libmpdec/crt.c | 179 + third_party/opa/wasm/src/libmpdec/crt.h | 47 + third_party/opa/wasm/src/libmpdec/difradix2.c | 173 + third_party/opa/wasm/src/libmpdec/difradix2.h | 48 + third_party/opa/wasm/src/libmpdec/fnt.c | 81 + third_party/opa/wasm/src/libmpdec/fnt.h | 49 + third_party/opa/wasm/src/libmpdec/fourstep.c | 257 + third_party/opa/wasm/src/libmpdec/fourstep.h | 48 + third_party/opa/wasm/src/libmpdec/io.c | 1578 + third_party/opa/wasm/src/libmpdec/io.h | 59 + third_party/opa/wasm/src/libmpdec/memory.c | 297 + third_party/opa/wasm/src/libmpdec/memory.h | 51 + third_party/opa/wasm/src/libmpdec/mpdecimal.c | 8411 +++++ third_party/opa/wasm/src/libmpdec/mpdecimal.h | 812 + .../opa/wasm/src/libmpdec/numbertheory.c | 132 + .../opa/wasm/src/libmpdec/numbertheory.h | 78 + third_party/opa/wasm/src/libmpdec/sixstep.c | 214 + third_party/opa/wasm/src/libmpdec/sixstep.h | 48 + third_party/opa/wasm/src/libmpdec/transpose.c | 276 + third_party/opa/wasm/src/libmpdec/transpose.h | 62 + third_party/opa/wasm/src/libmpdec/typearith.h | 669 + third_party/opa/wasm/src/libmpdec/umodarith.h | 650 + third_party/opa/wasm/src/malloc.c | 720 + third_party/opa/wasm/src/malloc.h | 38 + third_party/opa/wasm/src/memoize.c | 57 + third_party/opa/wasm/src/memoize.h | 12 + third_party/opa/wasm/src/mpd.c | 542 + third_party/opa/wasm/src/mpd.h | 29 + third_party/opa/wasm/src/numbers.c | 103 + third_party/opa/wasm/src/numbers.h | 8 + third_party/opa/wasm/src/object.c | 651 + third_party/opa/wasm/src/object.h | 16 + third_party/opa/wasm/src/re2/re2/bitmap256.h | 117 + third_party/opa/wasm/src/re2/re2/bitstate.cc | 389 + third_party/opa/wasm/src/re2/re2/compile.cc | 1253 + third_party/opa/wasm/src/re2/re2/dfa.cc | 2135 ++ third_party/opa/wasm/src/re2/re2/nfa.cc | 725 + third_party/opa/wasm/src/re2/re2/onepass.cc | 639 + third_party/opa/wasm/src/re2/re2/parse.cc | 2482 ++ .../opa/wasm/src/re2/re2/perl_groups.cc | 119 + third_party/opa/wasm/src/re2/re2/pod_array.h | 55 + third_party/opa/wasm/src/re2/re2/prog.cc | 998 + third_party/opa/wasm/src/re2/re2/prog.h | 436 + third_party/opa/wasm/src/re2/re2/re2.cc | 1369 + third_party/opa/wasm/src/re2/re2/re2.h | 1013 + third_party/opa/wasm/src/re2/re2/regexp.cc | 993 + third_party/opa/wasm/src/re2/re2/regexp.h | 660 + third_party/opa/wasm/src/re2/re2/simplify.cc | 679 + .../opa/wasm/src/re2/re2/sparse_array.h | 392 + third_party/opa/wasm/src/re2/re2/sparse_set.h | 264 + .../opa/wasm/src/re2/re2/stringpiece.cc | 67 + .../opa/wasm/src/re2/re2/stringpiece.h | 210 + third_party/opa/wasm/src/re2/re2/tostring.cc | 355 + .../opa/wasm/src/re2/re2/unicode_casefold.cc | 582 + .../opa/wasm/src/re2/re2/unicode_casefold.h | 78 + .../opa/wasm/src/re2/re2/unicode_groups.cc | 6269 ++++ .../opa/wasm/src/re2/re2/unicode_groups.h | 67 + third_party/opa/wasm/src/re2/re2/walker-inl.h | 250 + third_party/opa/wasm/src/re2/util/logging.h | 114 + third_party/opa/wasm/src/re2/util/mix.h | 41 + third_party/opa/wasm/src/re2/util/mutex.h | 148 + third_party/opa/wasm/src/re2/util/rune.cc | 260 + third_party/opa/wasm/src/re2/util/strutil.cc | 149 + third_party/opa/wasm/src/re2/util/strutil.h | 21 + third_party/opa/wasm/src/re2/util/utf.h | 50 + third_party/opa/wasm/src/re2/util/util.h | 42 + third_party/opa/wasm/src/regex.cc | 198 + third_party/opa/wasm/src/regex.h | 18 + third_party/opa/wasm/src/set.c | 189 + third_party/opa/wasm/src/set.h | 13 + third_party/opa/wasm/src/std.h | 47 + third_party/opa/wasm/src/str.c | 282 + third_party/opa/wasm/src/str.h | 25 + third_party/opa/wasm/src/strings.c | 1129 + third_party/opa/wasm/src/strings.h | 28 + third_party/opa/wasm/src/types.c | 68 + third_party/opa/wasm/src/types.h | 15 + third_party/opa/wasm/src/undefined.symbols | 6 + third_party/opa/wasm/src/unicode.c | 667 + third_party/opa/wasm/src/unicode.h | 24 + third_party/opa/wasm/src/value.c | 1759 ++ third_party/opa/wasm/src/value.h | 181 + third_party/opa/wasm/test.js | 124 + third_party/opa/wasm/tests/test-glob.cc | 276 + third_party/opa/wasm/tests/test-regex.cc | 29 + third_party/opa/wasm/tests/test.c | 3729 +++ third_party/opa/wasm/tests/test.h | 51 + third_party/opa/wasm/tests/undefined.symbols | 2 + 6747 files changed, 1150218 insertions(+), 19 deletions(-) create mode 100644 third_party/go-cfclient/.gitignore create mode 100644 third_party/go-cfclient/LICENSE create mode 100644 third_party/go-cfclient/Makefile create mode 100644 third_party/go-cfclient/PROVENANCE.md create mode 100644 third_party/go-cfclient/README.md create mode 100644 third_party/go-cfclient/app_update.go create mode 100644 third_party/go-cfclient/app_update_test.go create mode 100644 third_party/go-cfclient/app_usage_events.go create mode 100644 third_party/go-cfclient/app_usage_events_test.go create mode 100644 third_party/go-cfclient/appevents.go create mode 100644 third_party/go-cfclient/appevents_test.go create mode 100644 third_party/go-cfclient/apps.go create mode 100644 third_party/go-cfclient/apps_test.go create mode 100644 third_party/go-cfclient/buildpacks.go create mode 100644 third_party/go-cfclient/buildpacks_test.go create mode 100644 third_party/go-cfclient/cf_error.go create mode 100644 third_party/go-cfclient/cf_error_test.go create mode 100644 third_party/go-cfclient/cf_test.go create mode 100644 third_party/go-cfclient/client.go create mode 100644 third_party/go-cfclient/client_test.go create mode 100644 third_party/go-cfclient/domains.go create mode 100644 third_party/go-cfclient/domains_test.go create mode 100644 third_party/go-cfclient/environmentvariablegroups.go create mode 100644 third_party/go-cfclient/environmentvariablegroups_test.go create mode 100644 third_party/go-cfclient/error.go create mode 100644 third_party/go-cfclient/events.go create mode 100644 third_party/go-cfclient/events_test.go create mode 100644 third_party/go-cfclient/gen_error.go create mode 100644 third_party/go-cfclient/go.mod create mode 100644 third_party/go-cfclient/go.sum create mode 100644 third_party/go-cfclient/info.go create mode 100644 third_party/go-cfclient/info_test.go create mode 100644 third_party/go-cfclient/isolationsegments.go create mode 100644 third_party/go-cfclient/isolationsegments_test.go create mode 100644 third_party/go-cfclient/metadata.go create mode 100644 third_party/go-cfclient/metadata_test.go create mode 100644 third_party/go-cfclient/org_quotas.go create mode 100644 third_party/go-cfclient/org_quotas_test.go create mode 100644 third_party/go-cfclient/orgs.go create mode 100644 third_party/go-cfclient/orgs_test.go create mode 100644 third_party/go-cfclient/payloads_test.go create mode 100644 third_party/go-cfclient/processes.go create mode 100644 third_party/go-cfclient/processes_test.go create mode 100644 third_party/go-cfclient/resource_match.go create mode 100644 third_party/go-cfclient/resource_match_test.go create mode 100644 third_party/go-cfclient/route_mappings.go create mode 100644 third_party/go-cfclient/route_mappings_test.go create mode 100644 third_party/go-cfclient/routes.go create mode 100644 third_party/go-cfclient/routes_test.go create mode 100644 third_party/go-cfclient/secgroups.go create mode 100644 third_party/go-cfclient/secgroups_test.go create mode 100644 third_party/go-cfclient/service_bindings.go create mode 100644 third_party/go-cfclient/service_bindings_test.go create mode 100644 third_party/go-cfclient/service_brokers.go create mode 100644 third_party/go-cfclient/service_brokers_test.go create mode 100644 third_party/go-cfclient/service_instances.go create mode 100644 third_party/go-cfclient/service_instances_test.go create mode 100644 third_party/go-cfclient/service_keys.go create mode 100644 third_party/go-cfclient/service_keys_test.go create mode 100644 third_party/go-cfclient/service_plan_visibilities.go create mode 100644 third_party/go-cfclient/service_plan_visibilities_test.go create mode 100644 third_party/go-cfclient/service_plans.go create mode 100644 third_party/go-cfclient/service_plans_test.go create mode 100644 third_party/go-cfclient/service_usage_events.go create mode 100644 third_party/go-cfclient/service_usage_events_test.go create mode 100644 third_party/go-cfclient/services.go create mode 100644 third_party/go-cfclient/services_test.go create mode 100644 third_party/go-cfclient/space_quotas.go create mode 100644 third_party/go-cfclient/space_quotas_test.go create mode 100644 third_party/go-cfclient/spaces.go create mode 100644 third_party/go-cfclient/spaces_test.go create mode 100644 third_party/go-cfclient/stacks.go create mode 100644 third_party/go-cfclient/stacks_test.go create mode 100644 third_party/go-cfclient/stats.go create mode 100644 third_party/go-cfclient/stats_test.go create mode 100644 third_party/go-cfclient/tasks.go create mode 100644 third_party/go-cfclient/tasks_test.go create mode 100644 third_party/go-cfclient/tools.go create mode 100644 third_party/go-cfclient/types.go create mode 100644 third_party/go-cfclient/user_provided_service_instances.go create mode 100644 third_party/go-cfclient/user_provided_service_instances_test.go create mode 100644 third_party/go-cfclient/users.go create mode 100644 third_party/go-cfclient/users_test.go create mode 100644 third_party/go-cfclient/v3apps.go create mode 100644 third_party/go-cfclient/v3apps_test.go create mode 100644 third_party/go-cfclient/v3build.go create mode 100644 third_party/go-cfclient/v3build_test.go create mode 100644 third_party/go-cfclient/v3deployments.go create mode 100644 third_party/go-cfclient/v3deployments_test.go create mode 100644 third_party/go-cfclient/v3domains.go create mode 100644 third_party/go-cfclient/v3domains_test.go create mode 100644 third_party/go-cfclient/v3droplet.go create mode 100644 third_party/go-cfclient/v3droplet_test.go create mode 100644 third_party/go-cfclient/v3organizations.go create mode 100644 third_party/go-cfclient/v3organizations_test.go create mode 100644 third_party/go-cfclient/v3packages.go create mode 100644 third_party/go-cfclient/v3packages_test.go create mode 100644 third_party/go-cfclient/v3roles.go create mode 100644 third_party/go-cfclient/v3roles_test.go create mode 100644 third_party/go-cfclient/v3routes.go create mode 100644 third_party/go-cfclient/v3routes_test.go create mode 100644 third_party/go-cfclient/v3security_groups.go create mode 100644 third_party/go-cfclient/v3security_groups_test.go create mode 100644 third_party/go-cfclient/v3service_credential_bindings.go create mode 100644 third_party/go-cfclient/v3service_credential_bindings_test.go create mode 100644 third_party/go-cfclient/v3service_instances.go create mode 100644 third_party/go-cfclient/v3service_instances_test.go create mode 100644 third_party/go-cfclient/v3spaces.go create mode 100644 third_party/go-cfclient/v3spaces_test.go create mode 100644 third_party/go-cfclient/v3stacks.go create mode 100644 third_party/go-cfclient/v3stacks_test.go create mode 100644 third_party/go-cfclient/v3types.go create mode 100644 third_party/go-cfclient/v3users.go create mode 100644 third_party/go-cfclient/v3users_test.go create mode 100755 third_party/go-ordered-map/.circleci/circle_build.sh create mode 100644 third_party/go-ordered-map/.circleci/config.yml create mode 100644 third_party/go-ordered-map/.gitignore create mode 100644 third_party/go-ordered-map/.golangci.yml create mode 100644 third_party/go-ordered-map/CHANGELOG.md create mode 100644 third_party/go-ordered-map/LICENSE create mode 100644 third_party/go-ordered-map/Makefile create mode 100644 third_party/go-ordered-map/PROVENANCE.md create mode 100644 third_party/go-ordered-map/README.md create mode 100644 third_party/go-ordered-map/example_test.go create mode 100644 third_party/go-ordered-map/go.mod create mode 100644 third_party/go-ordered-map/go.sum create mode 100644 third_party/go-ordered-map/json.go create mode 100644 third_party/go-ordered-map/json_fuzz_test.go create mode 100644 third_party/go-ordered-map/json_test.go create mode 100644 third_party/go-ordered-map/orderedmap.go create mode 100644 third_party/go-ordered-map/orderedmap_test.go create mode 100644 third_party/go-ordered-map/testdata/fuzz/FuzzRoundTripJSON/62c005f96216d8ba8f62ac0799dfc1a6893e68418238a831ee79cd9c39b4cfc6 create mode 100644 third_party/go-ordered-map/testdata/fuzz/FuzzRoundTripJSON/8093511184ad3e258aa13b957e75ff26c7fae64672dae0c0bc0a9fa5b61a05e7 create mode 100644 third_party/go-ordered-map/utils_test.go create mode 100644 third_party/go-ordered-map/yaml.go create mode 100644 third_party/go-ordered-map/yaml_fuzz_test.go create mode 100644 third_party/go-ordered-map/yaml_test.go create mode 100644 third_party/goflow2/.gitignore create mode 100644 third_party/goflow2/Dockerfile create mode 100644 third_party/goflow2/LICENSE create mode 100644 third_party/goflow2/Makefile create mode 100644 third_party/goflow2/PROVENANCE.md create mode 100644 third_party/goflow2/README.md create mode 100644 third_party/goflow2/cmd/enricher/main.go create mode 100644 third_party/goflow2/cmd/enricher/pb/flowext.pb.go create mode 100644 third_party/goflow2/cmd/enricher/pb/flowext.proto create mode 100644 third_party/goflow2/cmd/goflow2/main.go create mode 100644 third_party/goflow2/cmd/goflow2/mapping.yaml create mode 100644 third_party/goflow2/compose/elk/README.md create mode 100644 third_party/goflow2/compose/elk/docker-compose.yml create mode 100644 third_party/goflow2/compose/elk/logstash.conf create mode 100644 third_party/goflow2/compose/kcg/README.md create mode 100755 third_party/goflow2/compose/kcg/clickhouse/create.sh create mode 100644 third_party/goflow2/compose/kcg/clickhouse/protocols.csv create mode 100644 third_party/goflow2/compose/kcg/docker-compose.yml create mode 100644 third_party/goflow2/compose/kcg/grafana/Dockerfile create mode 100644 third_party/goflow2/compose/kcg/grafana/dashboards.yml create mode 100644 third_party/goflow2/compose/kcg/grafana/dashboards/perfs.json create mode 100644 third_party/goflow2/compose/kcg/grafana/dashboards/viz-ch.json create mode 100644 third_party/goflow2/compose/kcg/grafana/datasources-ch.yml create mode 100644 third_party/goflow2/compose/kcg/prometheus/prometheus.yml create mode 100644 third_party/goflow2/decoders/decoder.go create mode 100644 third_party/goflow2/decoders/netflow/ipfix.go create mode 100644 third_party/goflow2/decoders/netflow/netflow.go create mode 100644 third_party/goflow2/decoders/netflow/netflow_test.go create mode 100644 third_party/goflow2/decoders/netflow/nfv9.go create mode 100644 third_party/goflow2/decoders/netflow/packet.go create mode 100644 third_party/goflow2/decoders/netflow/templates/file/file.go create mode 100644 third_party/goflow2/decoders/netflow/templates/memory/memory.go create mode 100644 third_party/goflow2/decoders/netflow/templates/templates.go create mode 100644 third_party/goflow2/decoders/netflowlegacy/netflow.go create mode 100644 third_party/goflow2/decoders/netflowlegacy/netflow_test.go create mode 100644 third_party/goflow2/decoders/netflowlegacy/packet.go create mode 100644 third_party/goflow2/decoders/sflow/datastructure.go create mode 100644 third_party/goflow2/decoders/sflow/packet.go create mode 100644 third_party/goflow2/decoders/sflow/sflow.go create mode 100644 third_party/goflow2/decoders/sflow/sflow_test.go create mode 100644 third_party/goflow2/decoders/utils/utils.go create mode 100644 third_party/goflow2/docs/agents.md create mode 100644 third_party/goflow2/docs/contributors.md create mode 100644 third_party/goflow2/docs/logs.md create mode 100644 third_party/goflow2/docs/protobuf.md create mode 100644 third_party/goflow2/docs/protocols.md create mode 100644 third_party/goflow2/format/common/hash.go create mode 100644 third_party/goflow2/format/common/selector.go create mode 100644 third_party/goflow2/format/common/text.go create mode 100644 third_party/goflow2/format/format.go create mode 100644 third_party/goflow2/format/json/json.go create mode 100644 third_party/goflow2/format/protobuf/protobuf.go create mode 100644 third_party/goflow2/format/text/text.go create mode 100644 third_party/goflow2/go.mod create mode 100644 third_party/goflow2/go.sum create mode 100644 third_party/goflow2/graphics/diagram.png create mode 100644 third_party/goflow2/package/goflow2.env create mode 100644 third_party/goflow2/package/goflow2.service create mode 100644 third_party/goflow2/pb/flow.pb.go create mode 100644 third_party/goflow2/pb/flow.proto create mode 100644 third_party/goflow2/producer/producer_nf.go create mode 100644 third_party/goflow2/producer/producer_nflegacy.go create mode 100644 third_party/goflow2/producer/producer_sf.go create mode 100644 third_party/goflow2/producer/producer_test.go create mode 100644 third_party/goflow2/producer/reflect.go create mode 100644 third_party/goflow2/transport/file/transport.go create mode 100644 third_party/goflow2/transport/kafka/kafka.go create mode 100644 third_party/goflow2/transport/kafka/scram_client.go create mode 100644 third_party/goflow2/transport/transport.go create mode 100644 third_party/goflow2/utils/metrics.go create mode 100644 third_party/goflow2/utils/netflow.go create mode 100644 third_party/goflow2/utils/nflegacy.go create mode 100644 third_party/goflow2/utils/sflow.go create mode 100644 third_party/goflow2/utils/sflow_test.go create mode 100644 third_party/goflow2/utils/stopper.go create mode 100644 third_party/goflow2/utils/stopper_test.go create mode 100644 third_party/goflow2/utils/utils.go create mode 100644 third_party/goflow2/utils/utils_test.go create mode 100644 third_party/opa/.gitignore create mode 100644 third_party/opa/.go-version create mode 100644 third_party/opa/.golangci.yaml create mode 100644 third_party/opa/.regal/config.yaml create mode 100644 third_party/opa/.trivyignore create mode 100644 third_party/opa/.yamllint.yaml create mode 100644 third_party/opa/ADOPTERS.md create mode 100644 third_party/opa/CHANGELOG.md create mode 100644 third_party/opa/CODE_OF_CONDUCT.md create mode 100644 third_party/opa/COMMUNITY_GUIDELINES.md create mode 100644 third_party/opa/CONTRIBUTING.md create mode 100644 third_party/opa/Dockerfile create mode 100644 third_party/opa/GOVERNANCE.md create mode 100644 third_party/opa/LICENSE create mode 100644 third_party/opa/MAINTAINERS.md create mode 100644 third_party/opa/Makefile create mode 100644 third_party/opa/PROVENANCE.md create mode 100644 third_party/opa/README.md create mode 100644 third_party/opa/SECURITY.md create mode 100644 third_party/opa/SECURITY_AUDIT.pdf create mode 100644 third_party/opa/ast/annotations.go create mode 100644 third_party/opa/ast/builtins.go create mode 100644 third_party/opa/ast/capabilities.go create mode 100644 third_party/opa/ast/check.go create mode 100644 third_party/opa/ast/compare.go create mode 100644 third_party/opa/ast/compile.go create mode 100644 third_party/opa/ast/compile_test.go create mode 100644 third_party/opa/ast/compilehelper.go create mode 100644 third_party/opa/ast/compilehelper_test.go create mode 100644 third_party/opa/ast/conflicts.go create mode 100644 third_party/opa/ast/doc.go create mode 100644 third_party/opa/ast/env.go create mode 100644 third_party/opa/ast/errors.go create mode 100644 third_party/opa/ast/index.go create mode 100644 third_party/opa/ast/interning.go create mode 100644 third_party/opa/ast/json/doc.go create mode 100644 third_party/opa/ast/json/json.go create mode 100644 third_party/opa/ast/location/doc.go create mode 100644 third_party/opa/ast/location/location.go create mode 100644 third_party/opa/ast/map.go create mode 100644 third_party/opa/ast/parser.go create mode 100644 third_party/opa/ast/parser_ext.go create mode 100644 third_party/opa/ast/parser_ext_test.go create mode 100644 third_party/opa/ast/parser_test.go create mode 100644 third_party/opa/ast/policy.go create mode 100644 third_party/opa/ast/policy_test.go create mode 100644 third_party/opa/ast/pretty.go create mode 100644 third_party/opa/ast/schema.go create mode 100644 third_party/opa/ast/strings.go create mode 100644 third_party/opa/ast/term.go create mode 100644 third_party/opa/ast/transform.go create mode 100644 third_party/opa/ast/unify.go create mode 100644 third_party/opa/ast/varset.go create mode 100644 third_party/opa/ast/visit.go create mode 100644 third_party/opa/builtin_metadata.json create mode 100644 third_party/opa/bundle/bundle.go create mode 100644 third_party/opa/bundle/bundle_test.go create mode 100644 third_party/opa/bundle/doc.go create mode 100644 third_party/opa/bundle/file.go create mode 100644 third_party/opa/bundle/filefs.go create mode 100644 third_party/opa/bundle/hash.go create mode 100644 third_party/opa/bundle/keys.go create mode 100644 third_party/opa/bundle/sign.go create mode 100644 third_party/opa/bundle/store.go create mode 100644 third_party/opa/bundle/store_test.go create mode 100644 third_party/opa/bundle/verify.go create mode 100644 third_party/opa/capabilities.json create mode 100644 third_party/opa/capabilities/capabilities.go create mode 100644 third_party/opa/capabilities/doc.go create mode 100644 third_party/opa/capabilities/v0.17.0.json create mode 100644 third_party/opa/capabilities/v0.17.1.json create mode 100644 third_party/opa/capabilities/v0.17.2.json create mode 100644 third_party/opa/capabilities/v0.17.3.json create mode 100644 third_party/opa/capabilities/v0.18.0.json create mode 100644 third_party/opa/capabilities/v0.19.0-rc1.json create mode 100644 third_party/opa/capabilities/v0.19.0.json create mode 100644 third_party/opa/capabilities/v0.19.1.json create mode 100644 third_party/opa/capabilities/v0.19.2.json create mode 100644 third_party/opa/capabilities/v0.20.0.json create mode 100644 third_party/opa/capabilities/v0.20.1.json create mode 100644 third_party/opa/capabilities/v0.20.2.json create mode 100644 third_party/opa/capabilities/v0.20.3.json create mode 100644 third_party/opa/capabilities/v0.20.4.json create mode 100644 third_party/opa/capabilities/v0.20.5.json create mode 100644 third_party/opa/capabilities/v0.21.0.json create mode 100644 third_party/opa/capabilities/v0.21.1.json create mode 100644 third_party/opa/capabilities/v0.22.0.json create mode 100644 third_party/opa/capabilities/v0.23.0.json create mode 100644 third_party/opa/capabilities/v0.23.1.json create mode 100644 third_party/opa/capabilities/v0.23.2.json create mode 100644 third_party/opa/capabilities/v0.24.0.json create mode 100644 third_party/opa/capabilities/v0.25.0-rc1.json create mode 100644 third_party/opa/capabilities/v0.25.0-rc2.json create mode 100644 third_party/opa/capabilities/v0.25.0-rc3.json create mode 100644 third_party/opa/capabilities/v0.25.0-rc4.json create mode 100644 third_party/opa/capabilities/v0.25.0.json create mode 100644 third_party/opa/capabilities/v0.25.1.json create mode 100644 third_party/opa/capabilities/v0.25.2.json create mode 100644 third_party/opa/capabilities/v0.26.0.json create mode 100644 third_party/opa/capabilities/v0.27.0.json create mode 100644 third_party/opa/capabilities/v0.27.1.json create mode 100644 third_party/opa/capabilities/v0.28.0.json create mode 100644 third_party/opa/capabilities/v0.29.0.json create mode 100644 third_party/opa/capabilities/v0.29.1.json create mode 100644 third_party/opa/capabilities/v0.29.2.json create mode 100644 third_party/opa/capabilities/v0.29.3.json create mode 100644 third_party/opa/capabilities/v0.29.4.json create mode 100644 third_party/opa/capabilities/v0.30.0.json create mode 100644 third_party/opa/capabilities/v0.30.1.json create mode 100644 third_party/opa/capabilities/v0.30.2.json create mode 100644 third_party/opa/capabilities/v0.31.0.json create mode 100644 third_party/opa/capabilities/v0.32.0.json create mode 100644 third_party/opa/capabilities/v0.32.1.json create mode 100644 third_party/opa/capabilities/v0.33.0.json create mode 100644 third_party/opa/capabilities/v0.33.1.json create mode 100644 third_party/opa/capabilities/v0.34.0.json create mode 100644 third_party/opa/capabilities/v0.34.1.json create mode 100644 third_party/opa/capabilities/v0.34.2.json create mode 100644 third_party/opa/capabilities/v0.35.0.json create mode 100644 third_party/opa/capabilities/v0.36.0.json create mode 100644 third_party/opa/capabilities/v0.36.1.json create mode 100644 third_party/opa/capabilities/v0.37.0.json create mode 100644 third_party/opa/capabilities/v0.37.1.json create mode 100644 third_party/opa/capabilities/v0.37.2.json create mode 100644 third_party/opa/capabilities/v0.38.0.json create mode 100644 third_party/opa/capabilities/v0.38.1.json create mode 100644 third_party/opa/capabilities/v0.39.0.json create mode 100644 third_party/opa/capabilities/v0.40.0.json create mode 100644 third_party/opa/capabilities/v0.41.0.json create mode 100644 third_party/opa/capabilities/v0.42.0.json create mode 100644 third_party/opa/capabilities/v0.42.1.json create mode 100644 third_party/opa/capabilities/v0.42.2.json create mode 100644 third_party/opa/capabilities/v0.43.0.json create mode 100644 third_party/opa/capabilities/v0.43.1.json create mode 100644 third_party/opa/capabilities/v0.44.0.json create mode 100644 third_party/opa/capabilities/v0.45.0.json create mode 100644 third_party/opa/capabilities/v0.46.0.json create mode 100644 third_party/opa/capabilities/v0.46.1.json create mode 100644 third_party/opa/capabilities/v0.46.2.json create mode 100644 third_party/opa/capabilities/v0.46.3.json create mode 100644 third_party/opa/capabilities/v0.47.0.json create mode 100644 third_party/opa/capabilities/v0.47.1.json create mode 100644 third_party/opa/capabilities/v0.47.2.json create mode 100644 third_party/opa/capabilities/v0.47.3.json create mode 100644 third_party/opa/capabilities/v0.47.4.json create mode 100644 third_party/opa/capabilities/v0.48.0.json create mode 100644 third_party/opa/capabilities/v0.49.0.json create mode 100644 third_party/opa/capabilities/v0.49.1.json create mode 100644 third_party/opa/capabilities/v0.49.2.json create mode 100644 third_party/opa/capabilities/v0.50.0.json create mode 100644 third_party/opa/capabilities/v0.50.1.json create mode 100644 third_party/opa/capabilities/v0.50.2.json create mode 100644 third_party/opa/capabilities/v0.51.0.json create mode 100644 third_party/opa/capabilities/v0.52.0.json create mode 100644 third_party/opa/capabilities/v0.53.0.json create mode 100644 third_party/opa/capabilities/v0.53.1.json create mode 100644 third_party/opa/capabilities/v0.54.0.json create mode 100644 third_party/opa/capabilities/v0.55.0.json create mode 100644 third_party/opa/capabilities/v0.56.0.json create mode 100644 third_party/opa/capabilities/v0.57.0.json create mode 100644 third_party/opa/capabilities/v0.57.1.json create mode 100644 third_party/opa/capabilities/v0.58.0.json create mode 100644 third_party/opa/capabilities/v0.59.0.json create mode 100644 third_party/opa/capabilities/v0.60.0.json create mode 100644 third_party/opa/capabilities/v0.61.0.json create mode 100644 third_party/opa/capabilities/v0.62.0.json create mode 100644 third_party/opa/capabilities/v0.62.1.json create mode 100644 third_party/opa/capabilities/v0.63.0.json create mode 100644 third_party/opa/capabilities/v0.64.0.json create mode 100644 third_party/opa/capabilities/v0.64.1.json create mode 100644 third_party/opa/capabilities/v0.65.0.json create mode 100644 third_party/opa/capabilities/v0.66.0.json create mode 100644 third_party/opa/capabilities/v0.67.0.json create mode 100644 third_party/opa/capabilities/v0.67.1.json create mode 100644 third_party/opa/capabilities/v0.68.0.json create mode 100644 third_party/opa/capabilities/v0.69.0.json create mode 100644 third_party/opa/capabilities/v0.70.0.json create mode 100644 third_party/opa/capabilities/v1.0.0.json create mode 100644 third_party/opa/capabilities/v1.0.1.json create mode 100644 third_party/opa/capabilities/v1.1.0.json create mode 100644 third_party/opa/capabilities/v1.2.0.json create mode 100644 third_party/opa/capabilities/v1.3.0.json create mode 100644 third_party/opa/capabilities/v1.4.0.json create mode 100644 third_party/opa/capabilities/v1.4.1.json create mode 100644 third_party/opa/capabilities/v1.4.2.json create mode 100644 third_party/opa/capabilities/v1.5.0.json create mode 100644 third_party/opa/capabilities/v1.5.1.json create mode 100644 third_party/opa/capabilities/v1.6.0.json create mode 100644 third_party/opa/capabilities/v1.7.0.json create mode 100644 third_party/opa/capabilities/v1.7.1.json create mode 100644 third_party/opa/cmd/bench.go create mode 100644 third_party/opa/cmd/bench_test.go create mode 100644 third_party/opa/cmd/build.go create mode 100644 third_party/opa/cmd/build_test.go create mode 100644 third_party/opa/cmd/capabilities.go create mode 100644 third_party/opa/cmd/capabilities_test.go create mode 100644 third_party/opa/cmd/check.go create mode 100644 third_party/opa/cmd/check_test.go create mode 100644 third_party/opa/cmd/commands.go create mode 100644 third_party/opa/cmd/deps.go create mode 100644 third_party/opa/cmd/deps_test.go create mode 100644 third_party/opa/cmd/doc.go create mode 100644 third_party/opa/cmd/eval.go create mode 100755 third_party/opa/cmd/eval_test.go create mode 100644 third_party/opa/cmd/eval_wasmtarget_test.go create mode 100644 third_party/opa/cmd/exec.go create mode 100644 third_party/opa/cmd/exec_test.go create mode 100644 third_party/opa/cmd/features.go create mode 100644 third_party/opa/cmd/filters.go create mode 100644 third_party/opa/cmd/flags.go create mode 100644 third_party/opa/cmd/fmt.go create mode 100644 third_party/opa/cmd/fmt_test.go create mode 100644 third_party/opa/cmd/formats/formats.go create mode 100644 third_party/opa/cmd/inspect.go create mode 100644 third_party/opa/cmd/inspect_test.go create mode 100644 third_party/opa/cmd/internal/env/env.go create mode 100644 third_party/opa/cmd/internal/env/env_test.go create mode 100644 third_party/opa/cmd/internal/exec/exec.go create mode 100644 third_party/opa/cmd/internal/exec/exec_test.go create mode 100644 third_party/opa/cmd/internal/exec/json_reporter.go create mode 100644 third_party/opa/cmd/internal/exec/json_reporter_test.go create mode 100644 third_party/opa/cmd/internal/exec/params.go create mode 100644 third_party/opa/cmd/internal/exec/params_test.go create mode 100644 third_party/opa/cmd/internal/exec/parser.go create mode 100644 third_party/opa/cmd/internal/exec/parser_test.go create mode 100644 third_party/opa/cmd/internal/exec/std_in_reader.go create mode 100644 third_party/opa/cmd/internal/exec/std_in_reader_test.go create mode 100644 third_party/opa/cmd/oracle.go create mode 100644 third_party/opa/cmd/oracle_test.go create mode 100644 third_party/opa/cmd/parse.go create mode 100644 third_party/opa/cmd/parse_test.go create mode 100644 third_party/opa/cmd/refactor.go create mode 100644 third_party/opa/cmd/refactor_test.go create mode 100644 third_party/opa/cmd/run.go create mode 100644 third_party/opa/cmd/run_test.go create mode 100644 third_party/opa/cmd/sign.go create mode 100644 third_party/opa/cmd/sign_test.go create mode 100644 third_party/opa/cmd/test.go create mode 100644 third_party/opa/cmd/test_test.go create mode 100644 third_party/opa/cmd/utils.go create mode 100644 third_party/opa/cmd/version.go create mode 100644 third_party/opa/cmd/version_test.go create mode 100644 third_party/opa/compile/compile.go create mode 100644 third_party/opa/compile/compile_test.go create mode 100644 third_party/opa/compile/doc.go create mode 100644 third_party/opa/config/config.go create mode 100644 third_party/opa/config/doc.go create mode 100644 third_party/opa/cover/cover.go create mode 100644 third_party/opa/cover/doc.go create mode 100644 third_party/opa/debug/breakpoint.go create mode 100644 third_party/opa/debug/debugger.go create mode 100644 third_party/opa/debug/doc.go create mode 100644 third_party/opa/debug/event.go create mode 100644 third_party/opa/debug/frame.go create mode 100644 third_party/opa/debug/thread.go create mode 100644 third_party/opa/debug/variable.go create mode 100644 third_party/opa/dependencies/deps.go create mode 100644 third_party/opa/dependencies/doc.go create mode 100644 third_party/opa/docs/.gitignore create mode 100644 third_party/opa/docs/Makefile create mode 100644 third_party/opa/docs/README.md create mode 100644 third_party/opa/docs/devel/DEVELOPMENT.md create mode 100644 third_party/opa/docs/devel/RELEASE.md create mode 100644 third_party/opa/docs/docs/assets/OverviewDiagram.jsx create mode 100644 third_party/opa/docs/docs/assets/logo.png create mode 100644 third_party/opa/docs/docs/aws-cloudformation-hooks.md create mode 100644 third_party/opa/docs/docs/cicd.md create mode 100644 third_party/opa/docs/docs/cli.md create mode 100644 third_party/opa/docs/docs/comparison-to-other-systems.md create mode 100644 third_party/opa/docs/docs/configuration.md create mode 100644 third_party/opa/docs/docs/contrib-adding-builtin-functions.md create mode 100644 third_party/opa/docs/docs/contrib-code.md create mode 100644 third_party/opa/docs/docs/contrib-development.md create mode 100644 third_party/opa/docs/docs/contrib-docs.md create mode 100644 third_party/opa/docs/docs/contributing.md create mode 100644 third_party/opa/docs/docs/debugging/debugging-dap.gif create mode 100644 third_party/opa/docs/docs/debugging/index.md create mode 100644 third_party/opa/docs/docs/deployments.md create mode 100644 third_party/opa/docs/docs/docker-authorization.md create mode 100644 third_party/opa/docs/docs/editor-and-ide-support.md create mode 100644 third_party/opa/docs/docs/envoy/_category_.yaml create mode 100644 third_party/opa/docs/docs/envoy/debugging.md create mode 100644 third_party/opa/docs/docs/envoy/index.md create mode 100644 third_party/opa/docs/docs/envoy/performance.md create mode 100644 third_party/opa/docs/docs/envoy/primer.md create mode 100644 third_party/opa/docs/docs/envoy/tutorial-gloo-edge.md create mode 100644 third_party/opa/docs/docs/envoy/tutorial-istio.md create mode 100644 third_party/opa/docs/docs/envoy/tutorial-standalone-envoy.md create mode 100644 third_party/opa/docs/docs/extensions.md create mode 100644 third_party/opa/docs/docs/external-data/index.md create mode 100644 third_party/opa/docs/docs/faq.md create mode 100644 third_party/opa/docs/docs/graphql-api-authorization.md create mode 100644 third_party/opa/docs/docs/http-api-authorization.md create mode 100644 third_party/opa/docs/docs/index.md create mode 100644 third_party/opa/docs/docs/integration.md create mode 100644 third_party/opa/docs/docs/ir.md create mode 100644 third_party/opa/docs/docs/kafka-authorization.md create mode 100644 third_party/opa/docs/docs/kubernetes/_category_.yaml create mode 100644 third_party/opa/docs/docs/kubernetes/debugging.md create mode 100644 third_party/opa/docs/docs/kubernetes/index.md create mode 100644 third_party/opa/docs/docs/kubernetes/primer.md create mode 100644 third_party/opa/docs/docs/kubernetes/tutorial.md create mode 100644 third_party/opa/docs/docs/management-bundles/assets/thumbprint.png create mode 100644 third_party/opa/docs/docs/management-bundles/index.md create mode 100644 third_party/opa/docs/docs/management-decision-logs.md create mode 100644 third_party/opa/docs/docs/management-discovery.md create mode 100644 third_party/opa/docs/docs/management-introduction/assets/ControlPlaneDiagram.jsx create mode 100644 third_party/opa/docs/docs/management-introduction/assets/DistributedDiagram.jsx create mode 100644 third_party/opa/docs/docs/management-introduction/assets/HostLocalDiagram.jsx create mode 100644 third_party/opa/docs/docs/management-introduction/assets/logo.png create mode 100644 third_party/opa/docs/docs/management-introduction/index.md create mode 100644 third_party/opa/docs/docs/management-status.md create mode 100644 third_party/opa/docs/docs/monitoring.md create mode 100644 third_party/opa/docs/docs/oauth-oidc.md create mode 100644 third_party/opa/docs/docs/philosophy/index.md create mode 100644 third_party/opa/docs/docs/policy-language.md create mode 100644 third_party/opa/docs/docs/policy-performance.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/input.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/input.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/input.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/time/time_format/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/time/time_format/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/time/time_format/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/time/time_format/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/empty_json/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/empty_json/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/empty_json/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/hmac/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/hmac/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/hmac/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/rsa/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/rsa/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/rsa/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/data.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/input.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/config.json create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/intro.md create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/policy.rego create mode 100644 third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/title.txt create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/aggregates.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/array.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/bits.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/comparison.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/conversions.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/crypto.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/encoding.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/glob.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/graph.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/graphql.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/http.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/index.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/net.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/numbers.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/object.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/opa.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/providers.aws.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/regex.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/rego.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/semver.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/sets.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/strings.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/time.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/tokens.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/tokensign.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/tracing.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/types.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/units.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/builtins/uuid.mdx create mode 100644 third_party/opa/docs/docs/policy-reference/index.md create mode 100644 third_party/opa/docs/docs/policy-testing.md create mode 100644 third_party/opa/docs/docs/privacy.md create mode 100644 third_party/opa/docs/docs/rest-api.md create mode 100644 third_party/opa/docs/docs/security.md create mode 100644 third_party/opa/docs/docs/ssh-and-sudo-authorization.md create mode 100644 third_party/opa/docs/docs/storage.md create mode 100644 third_party/opa/docs/docs/terraform.md create mode 100644 third_party/opa/docs/docs/v0-compatibility.md create mode 100644 third_party/opa/docs/docs/v0-upgrade/index.md create mode 100644 third_party/opa/docs/docs/wasm.md create mode 100644 third_party/opa/docs/docusaurus.config.js create mode 100644 third_party/opa/docs/functions/badge.ts create mode 100644 third_party/opa/docs/functions/version-redirect.ts create mode 100644 third_party/opa/docs/package.json create mode 100644 third_party/opa/docs/src/Archive.js create mode 100644 third_party/opa/docs/src/EcosystemEntry.js create mode 100644 third_party/opa/docs/src/EcosystemFeature.js create mode 100644 third_party/opa/docs/src/EcosystemLanguage.js create mode 100644 third_party/opa/docs/src/components/BuiltinLegacyRedirect/index.js create mode 100644 third_party/opa/docs/src/components/BuiltinSearch/index.js create mode 100644 third_party/opa/docs/src/components/BuiltinSearch/styles.module.css create mode 100644 third_party/opa/docs/src/components/BuiltinTable/index.js create mode 100644 third_party/opa/docs/src/components/BuiltinTable/styles.module.css create mode 100644 third_party/opa/docs/src/components/Card/index.js create mode 100644 third_party/opa/docs/src/components/Card/styles.module.css create mode 100644 third_party/opa/docs/src/components/CardGrid/index.js create mode 100644 third_party/opa/docs/src/components/CardGrid/styles.module.css create mode 100644 third_party/opa/docs/src/components/CommandDoc/index.js create mode 100644 third_party/opa/docs/src/components/CommandDoc/styles.module.css create mode 100644 third_party/opa/docs/src/components/CommandList/index.js create mode 100644 third_party/opa/docs/src/components/CommandList/styles.module.css create mode 100644 third_party/opa/docs/src/components/EcosystemEmbed/index.js create mode 100644 third_party/opa/docs/src/components/EcosystemFeatureLink/index.js create mode 100644 third_party/opa/docs/src/components/EvergreenCodeBlock/index.js create mode 100644 third_party/opa/docs/src/components/FeedbackForm/index.js create mode 100644 third_party/opa/docs/src/components/FeedbackForm/styles.module.css create mode 100644 third_party/opa/docs/src/components/ImageCard/index.js create mode 100644 third_party/opa/docs/src/components/NavbarItems/CurrentVersionNavbarItem.js create mode 100644 third_party/opa/docs/src/components/NavbarItems/styles.module.css create mode 100644 third_party/opa/docs/src/components/PlaygroundExample/index.js create mode 100644 third_party/opa/docs/src/components/RunSnippet/index.js create mode 100644 third_party/opa/docs/src/components/RunSnippet/styles.module.css create mode 100644 third_party/opa/docs/src/components/SideBySide/Column.js create mode 100644 third_party/opa/docs/src/components/SideBySide/Container.js create mode 100644 third_party/opa/docs/src/components/SideBySide/styles.module.css create mode 100644 third_party/opa/docs/src/components/SidebarTitle/index.js create mode 100644 third_party/opa/docs/src/components/StandaloneLayout/index.js create mode 100644 third_party/opa/docs/src/components/StandaloneLayout/styles.module.css create mode 100644 third_party/opa/docs/src/css/custom.css create mode 100644 third_party/opa/docs/src/data/cli.json create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/alfred.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/alluxio.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/antlr.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/apache-apisix.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/aserto.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/atmos.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/awesome-opa.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/aws-api-gateway.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/aws-cloudformation-hook.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/backstage.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/boomerang-bosun.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/bottle.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/carbonetes.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/ceph.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/chef-automate.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/circleci.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/clair-datasource.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/clojure.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/cloudflare-worker.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/conftest.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/coredns-authz.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/cosign.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/custom-library-microservice-authorization.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/dapr.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/dart-authorization.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/dependency-management-data.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/digger.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/docker-machine.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/easegress.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/elasticsearch-datafiltering.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/emissary-ingress.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/enterprise-contract.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/enterprise-opa.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/env0.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/envoy-authorization.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/expressing-or.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/fairwinds-insights.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/fiber.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/fig.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/flask-opa.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/flipt.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/gatekeeper.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/gcp-forseti.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/github-action-opa-rego-test.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/gloo-api-gateway.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/google-calendar.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/google-kubernetes-engine.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/gradle-plugin.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/graphql.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/i2scim.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/iptables.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/jenkins-job-authorization.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/kafka-authorization.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/kong-authorization.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/kubernetes-authorization.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/kubernetes-provisioning.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/kubernetes-validating-admission.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/kubescape.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/kubeshield.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/legitify.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/linux-pam.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/lula.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/magda.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/minio.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/nacp.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/nginx.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/nodejs-express.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/oauth2.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/ocpr.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/oidc.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-aspnetcore.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-csharp.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-dotnet-asp-core.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-dotnet.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-errors.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-golang.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-java-client.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-java-wasm.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-java.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-playground.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-python.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-springboot.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-typescript.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-wasm-dotnet.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-wasm-java.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-wasm-js.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opa-wasm-rust.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/opal.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/open-service-mesh.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/openfaas-function-authorization.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/optoggles.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/permit.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/php-authorization.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/pomerium-authz.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/pre-commit-hooks.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/principled-evolution.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/pulumi.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/raygun.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/regal.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/rego-cheat-sheet.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/rego-language-comparisons.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/rego-test-assertions.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/regocpp.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/rekor.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/reposaur.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/rond.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/sansshell.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/scalr-iacp.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/spacelift.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/sphinx-rego.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/spinnaker-pipeline.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/spire.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/springsecurity-api.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/sql-datafiltering.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/strimzi.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/styra-academy.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/styra-das.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/swift-opa.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/sysdig-image-scanner.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/tavoai.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/terraform-cloud.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/terraform.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/topaz.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/torque.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/traefik-api-gateway.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/trino.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/vscode-opa.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/waltid.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/wirelesssecuritylab.md create mode 100644 third_party/opa/docs/src/data/ecosystem/entries/zed-rego.md create mode 100644 third_party/opa/docs/src/data/ecosystem/feature-categories/createwithopa.md create mode 100644 third_party/opa/docs/src/data/ecosystem/feature-categories/production.md create mode 100644 third_party/opa/docs/src/data/ecosystem/feature-categories/rego.md create mode 100644 third_party/opa/docs/src/data/ecosystem/feature-categories/tool.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/debugging-rego.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/editors.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/envoy.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/external-data-realtime-push.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/external-data-runtime.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/external-data.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/go-integration.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/kubernetes.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/learning-rego.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/opa-bundles-discovery.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/opa-bundles.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/policy-testing.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/rest-api-integration.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/status-api.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/terraform.md create mode 100644 third_party/opa/docs/src/data/ecosystem/features/wasm-integration.md create mode 100644 third_party/opa/docs/src/data/ecosystem/languages/clojure.md create mode 100644 third_party/opa/docs/src/data/ecosystem/languages/csharp.md create mode 100644 third_party/opa/docs/src/data/ecosystem/languages/golang.md create mode 100644 third_party/opa/docs/src/data/ecosystem/languages/java.md create mode 100644 third_party/opa/docs/src/data/ecosystem/languages/javascript.md create mode 100644 third_party/opa/docs/src/data/ecosystem/languages/php.md create mode 100644 third_party/opa/docs/src/data/ecosystem/languages/rust.md create mode 100644 third_party/opa/docs/src/data/ecosystem/languages/swift.md create mode 100644 third_party/opa/docs/src/lib/ecosystem/getLogoAsset.js create mode 100644 third_party/opa/docs/src/lib/ecosystem/loadPages.js create mode 100644 third_party/opa/docs/src/lib/ecosystem/sortPagesByRank.js create mode 100644 third_party/opa/docs/src/lib/playground.js create mode 100644 third_party/opa/docs/src/lib/sidebars.js create mode 100644 third_party/opa/docs/src/pages/_examples/admin/config.json create mode 100644 third_party/opa/docs/src/pages/_examples/admin/data.json create mode 100644 third_party/opa/docs/src/pages/_examples/admin/input.json create mode 100644 third_party/opa/docs/src/pages/_examples/admin/intro.md create mode 100644 third_party/opa/docs/src/pages/_examples/admin/policy.rego create mode 100644 third_party/opa/docs/src/pages/_examples/admin/title.txt create mode 100644 third_party/opa/docs/src/pages/_examples/ai/config.json create mode 100644 third_party/opa/docs/src/pages/_examples/ai/data.json create mode 100644 third_party/opa/docs/src/pages/_examples/ai/input.json create mode 100644 third_party/opa/docs/src/pages/_examples/ai/intro.md create mode 100644 third_party/opa/docs/src/pages/_examples/ai/policy.rego create mode 100644 third_party/opa/docs/src/pages/_examples/ai/title.txt create mode 100644 third_party/opa/docs/src/pages/_examples/app/config.json create mode 100644 third_party/opa/docs/src/pages/_examples/app/data.json create mode 100644 third_party/opa/docs/src/pages/_examples/app/input.json create mode 100644 third_party/opa/docs/src/pages/_examples/app/intro.md create mode 100644 third_party/opa/docs/src/pages/_examples/app/policy.rego create mode 100644 third_party/opa/docs/src/pages/_examples/app/title.txt create mode 100644 third_party/opa/docs/src/pages/_examples/envoy/config.json create mode 100644 third_party/opa/docs/src/pages/_examples/envoy/data.json create mode 100644 third_party/opa/docs/src/pages/_examples/envoy/input.json create mode 100644 third_party/opa/docs/src/pages/_examples/envoy/intro.md create mode 100644 third_party/opa/docs/src/pages/_examples/envoy/policy.rego create mode 100644 third_party/opa/docs/src/pages/_examples/envoy/title.txt create mode 100644 third_party/opa/docs/src/pages/_examples/k8s/config.json create mode 100644 third_party/opa/docs/src/pages/_examples/k8s/data.json create mode 100644 third_party/opa/docs/src/pages/_examples/k8s/input.json create mode 100644 third_party/opa/docs/src/pages/_examples/k8s/intro.md create mode 100644 third_party/opa/docs/src/pages/_examples/k8s/policy.rego create mode 100644 third_party/opa/docs/src/pages/_examples/k8s/title.txt create mode 100644 third_party/opa/docs/src/pages/assets/README.md create mode 100644 third_party/opa/docs/src/pages/assets/github.png create mode 100644 third_party/opa/docs/src/pages/assets/icons/audit.png create mode 100644 third_party/opa/docs/src/pages/assets/icons/performance.png create mode 100644 third_party/opa/docs/src/pages/assets/icons/productivity.png create mode 100644 third_party/opa/docs/src/pages/assets/logo-text-dark.png create mode 100644 third_party/opa/docs/src/pages/assets/logo-text-light.png create mode 100644 third_party/opa/docs/src/pages/assets/logo.png create mode 100644 third_party/opa/docs/src/pages/assets/logos/allstate-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/allstate-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/atlassian-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/atlassian-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/bankdata-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/bankdata-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/bloomberg-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/bloomberg-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/bny-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/bny-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/capital-one-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/capital-one-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/cisco-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/cisco-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/goldman-sachs-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/goldman-sachs-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/intuit-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/intuit-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/marsh-mclennan-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/marsh-mclennan-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/pinterest-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/pinterest-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/sugarcrm-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/sugarcrm-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/t-mobile-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/t-mobile-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/tripadvisor-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/tripadvisor-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/vodafone-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/vodafone-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/zalando-dark.svg create mode 100644 third_party/opa/docs/src/pages/assets/logos/zalando-light.svg create mode 100644 third_party/opa/docs/src/pages/assets/slack.png create mode 100644 third_party/opa/docs/src/pages/assets/styra.svg create mode 100644 third_party/opa/docs/src/pages/community/assets/logos/github-discussions.png create mode 100644 third_party/opa/docs/src/pages/community/assets/logos/github.png create mode 100644 third_party/opa/docs/src/pages/community/assets/logos/linkedin.png create mode 100644 third_party/opa/docs/src/pages/community/assets/logos/opa.png create mode 100644 third_party/opa/docs/src/pages/community/assets/logos/slack.png create mode 100644 third_party/opa/docs/src/pages/community/assets/logos/stack-overflow.png create mode 100644 third_party/opa/docs/src/pages/community/assets/logos/styra-academy.png create mode 100644 third_party/opa/docs/src/pages/community/index.js create mode 100644 third_party/opa/docs/src/pages/ecosystem/assets/ecosystem/language-logos/clojure.png create mode 100644 third_party/opa/docs/src/pages/ecosystem/assets/ecosystem/language-logos/csharp.png create mode 100644 third_party/opa/docs/src/pages/ecosystem/assets/ecosystem/language-logos/golang.png create mode 100644 third_party/opa/docs/src/pages/ecosystem/assets/ecosystem/language-logos/java.png create mode 100644 third_party/opa/docs/src/pages/ecosystem/assets/ecosystem/language-logos/javascript.png create mode 100644 third_party/opa/docs/src/pages/ecosystem/assets/ecosystem/language-logos/php.png create mode 100644 third_party/opa/docs/src/pages/ecosystem/assets/ecosystem/language-logos/rust.png create mode 100644 third_party/opa/docs/src/pages/ecosystem/assets/ecosystem/language-logos/swift.png create mode 100644 third_party/opa/docs/src/pages/ecosystem/index.js create mode 100644 third_party/opa/docs/src/pages/ecosystem/styles.module.css create mode 100644 third_party/opa/docs/src/pages/index.js create mode 100644 third_party/opa/docs/src/pages/index.module.css create mode 100644 third_party/opa/docs/src/pages/security.mdx create mode 100644 third_party/opa/docs/src/pages/support/assets/logos/paclabs.png create mode 100644 third_party/opa/docs/src/pages/support/assets/logos/styra.png create mode 100644 third_party/opa/docs/src/pages/support/index.js create mode 100644 third_party/opa/docs/src/theme/ColorModeToggle/index.js create mode 100644 third_party/opa/docs/src/theme/ColorModeToggle/styles.module.css create mode 100644 third_party/opa/docs/src/theme/DocItem/Content/index.js create mode 100644 third_party/opa/docs/src/theme/MDXComponents.js create mode 100644 third_party/opa/docs/src/theme/NavbarItem/ComponentTypes.js create mode 100644 third_party/opa/docs/src/theme/NotFound/Content/index.js create mode 100644 third_party/opa/docs/src/theme/NotFound/index.js create mode 100644 third_party/opa/docs/static/.well-known/traffic-advice create mode 100644 third_party/opa/docs/static/_redirects create mode 100644 third_party/opa/docs/static/apple-touch-icon.png create mode 100644 third_party/opa/docs/static/external-resources/README.md create mode 100644 third_party/opa/docs/static/external-resources/bundles/envoy/authz create mode 100644 third_party/opa/docs/static/external-resources/bundles/helm-kubernetes-quickstart create mode 100644 third_party/opa/docs/static/external-resources/bundles/istio/authz create mode 100644 third_party/opa/docs/static/external-resources/bundles/kubernetes/admission create mode 100644 third_party/opa/docs/static/external-resources/opa-horizontal-color.png create mode 100644 third_party/opa/docs/static/external-resources/opa-no-text-color.png create mode 100644 third_party/opa/docs/static/favicon-96x96.png create mode 100644 third_party/opa/docs/static/favicon.ico create mode 100644 third_party/opa/docs/static/favicon.png create mode 100644 third_party/opa/docs/static/favicon.svg create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/alfred.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/alluxio.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/antlr.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/apache-apisix.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/aserto.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/asp-dotnet-core.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/atmos.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/aws-api-gateway.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/aws-cloudformation-hook.svg create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/backstage.svg create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/boomerang-bosun.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/bottle.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/carbonetes.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/ceph.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/circleci.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/clair-datasource.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/clojure.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/cloudflare-worker.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/conftest.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/coredns-authz.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/cosign.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/dapr.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/dart-authorization.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/default.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/dependency-management-data.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/digger.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/docker-machine.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/easegress.svg create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/elasticsearch-datafiltering.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/emissary-ingress.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/enterprise-contract.svg create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/enterprise-opa.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/env0.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/envoy-authorization.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/expressing-or.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/fiber.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/fig.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/flask-opa.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/flipt.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/gcp-forseti.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/github-action-opa-rego-test.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/gloo-api-gateway.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/gluu-gateway-authz.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/google-calendar.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/google-kubernetes-engine.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/gradle-plugin.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/graphene-graphql.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/graphql.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/i2scim.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/iptables.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/istio-authorization-mixer.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/jenkins-job-authorization.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/kafka-authorization.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/kong-authorization.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/kubernetes-authorization.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/kubernetes-provisioning.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/kubernetes-validating-admission.png create mode 100755 third_party/opa/docs/static/img/ecosystem-entry-logos/kubescape.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/kubeshield.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/legitify.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/linux-pam.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/lula.svg create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/magda.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/minio.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/nacp.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/nginx.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/nodejs-express.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/oauth2.svg create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/ocpr.svg create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/oidc.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-aspnetcore.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-csharp.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-dotnet-asp-core.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-dotnet.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-errors.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-golang.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-java-client.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-java-wasm.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-java.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-playground.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-python.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-springboot.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-typescript.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-wasm-dotnet.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-wasm-java.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-wasm-js.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opa-wasm-rust.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/opal.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/open-service-mesh.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/openfaas-function-authorization.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/optoggles.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/permit.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/php-authorization.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/pomerium-authz.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/pre-commit-hooks.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/principled-evolution.svg create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/pulumi.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/raygun.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/regal.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/rego-cheat-sheet.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/rego-language-comparisons.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/rego-test-assertions.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/regocpp.svg create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/rekor.svg create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/reposaur.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/rond.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/scalr-iacp.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/spacelift.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/sphinx-rego.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/spinnaker-pipeline.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/spire.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/springsecurity-api.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/sql-datafiltering.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/strimzi.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/styra-academy.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/styra-das.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/swift-opa.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/sysdig-image-scanner.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/tavoai.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/terraform-cloud.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/terraform.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/topaz.svg create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/torque.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/traefik-api-gateway.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/trino.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/vscode-opa.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/waltid.png create mode 100644 third_party/opa/docs/static/img/ecosystem-entry-logos/zed-rego.png create mode 100644 third_party/opa/docs/static/img/footer/cncf-dark.svg create mode 100644 third_party/opa/docs/static/img/footer/cncf-light.svg create mode 100644 third_party/opa/docs/static/img/nav/github-dark.svg create mode 100644 third_party/opa/docs/static/img/nav/github-light.svg create mode 100644 third_party/opa/docs/static/img/nav/logo.png create mode 100644 third_party/opa/docs/static/img/nav/slack-dark.svg create mode 100644 third_party/opa/docs/static/img/nav/slack-light.svg create mode 100644 third_party/opa/docs/static/netlify-forms.html create mode 100644 third_party/opa/docs/static/robots.txt create mode 100644 third_party/opa/docs/static/site.webmanifest create mode 100644 third_party/opa/docs/static/web-app-manifest-192x192.png create mode 100644 third_party/opa/docs/static/web-app-manifest-512x512.png create mode 100644 third_party/opa/download/config.go create mode 100644 third_party/opa/download/doc.go create mode 100644 third_party/opa/download/download.go create mode 100644 third_party/opa/download/oci_download.go create mode 100644 third_party/opa/download/oci_download_unavailable.go create mode 100644 third_party/opa/download/oci_downloader.go create mode 100644 third_party/opa/features/doc.go create mode 100644 third_party/opa/features/tracing/doc.go create mode 100644 third_party/opa/features/tracing/tracing.go create mode 100644 third_party/opa/features/wasm/doc.go create mode 100644 third_party/opa/features/wasm/wasm.go create mode 100644 third_party/opa/format/doc.go create mode 100644 third_party/opa/format/format.go create mode 100644 third_party/opa/format/format_test.go create mode 100644 third_party/opa/go.mod create mode 100644 third_party/opa/go.sum create mode 100644 third_party/opa/hooks/doc.go create mode 100644 third_party/opa/hooks/hooks.go create mode 100644 third_party/opa/internal/bundle/inspect/inspect.go create mode 100644 third_party/opa/internal/bundle/inspect/inspect_test.go create mode 100644 third_party/opa/internal/bundle/utils.go create mode 100644 third_party/opa/internal/cidr/merge/merge.go create mode 100644 third_party/opa/internal/cmd/genbuiltinmetadata/main.go create mode 100644 third_party/opa/internal/cmd/genopacapabilities/main.go create mode 100644 third_party/opa/internal/cmd/genversionindex/main.go create mode 100644 third_party/opa/internal/compiler/utils.go create mode 100644 third_party/opa/internal/compiler/utils_test.go create mode 100644 third_party/opa/internal/compiler/wasm/opa/callgraph.csv create mode 100644 third_party/opa/internal/compiler/wasm/opa/opa.go create mode 100755 third_party/opa/internal/compiler/wasm/opa/opa.wasm create mode 100644 third_party/opa/internal/compiler/wasm/optimizations.go create mode 100644 third_party/opa/internal/compiler/wasm/optimizations_test.go create mode 100644 third_party/opa/internal/compiler/wasm/wasm.go create mode 100644 third_party/opa/internal/compiler/wasm/wasm_test.go create mode 100644 third_party/opa/internal/config/config.go create mode 100644 third_party/opa/internal/config/config_test.go create mode 100644 third_party/opa/internal/debug/debug.go create mode 100644 third_party/opa/internal/deepcopy/deepcopy.go create mode 100644 third_party/opa/internal/deepcopy/deepcopy_test.go create mode 100644 third_party/opa/internal/distributedtracing/distributedtracing.go create mode 100644 third_party/opa/internal/edittree/bitvector/README.md create mode 100644 third_party/opa/internal/edittree/bitvector/bitvector.go create mode 100644 third_party/opa/internal/edittree/bitvector/bitvector_test.go create mode 100644 third_party/opa/internal/edittree/bitvector/license.txt create mode 100644 third_party/opa/internal/edittree/edittree.go create mode 100644 third_party/opa/internal/edittree/edittree_test.go create mode 100644 third_party/opa/internal/file/archive/tarball.go create mode 100644 third_party/opa/internal/file/url/url.go create mode 100644 third_party/opa/internal/file/url/url_test.go create mode 100644 third_party/opa/internal/future/filter_imports.go create mode 100644 third_party/opa/internal/future/parser_opts.go create mode 100644 third_party/opa/internal/gojsonschema/LICENSE-APACHE-2.0.txt create mode 100644 third_party/opa/internal/gojsonschema/README.md create mode 100644 third_party/opa/internal/gojsonschema/draft.go create mode 100644 third_party/opa/internal/gojsonschema/errors.go create mode 100644 third_party/opa/internal/gojsonschema/format_checkers.go create mode 100644 third_party/opa/internal/gojsonschema/format_checkers_test.go create mode 100644 third_party/opa/internal/gojsonschema/internalLog.go create mode 100644 third_party/opa/internal/gojsonschema/jsonContext.go create mode 100644 third_party/opa/internal/gojsonschema/jsonLoader.go create mode 100644 third_party/opa/internal/gojsonschema/jsonschema_test.go create mode 100644 third_party/opa/internal/gojsonschema/locales.go create mode 100644 third_party/opa/internal/gojsonschema/result.go create mode 100644 third_party/opa/internal/gojsonschema/schema.go create mode 100644 third_party/opa/internal/gojsonschema/schemaLoader.go create mode 100644 third_party/opa/internal/gojsonschema/schemaLoader_test.go create mode 100644 third_party/opa/internal/gojsonschema/schemaPool.go create mode 100644 third_party/opa/internal/gojsonschema/schemaReferencePool.go create mode 100644 third_party/opa/internal/gojsonschema/schemaType.go create mode 100644 third_party/opa/internal/gojsonschema/schema_test.go create mode 100644 third_party/opa/internal/gojsonschema/subSchema.go create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/additionalItems.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/additionalProperties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/allOf.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/anyOf.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/default.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/definitions.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/dependencies.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/enum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/format.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/items.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/maxItems.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/maxLength.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/maxProperties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/maximum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/minItems.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/minLength.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/minProperties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/minimum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/multipleOf.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/not.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/oneOf.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/optional/bignum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/optional/ecmascript-regex.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/optional/format.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/optional/zeroTerminatedFloats.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/pattern.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/patternProperties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/properties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/ref.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/refRemote.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/required.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/type.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft4/uniqueItems.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/additionalItems.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/additionalProperties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/allOf.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/anyOf.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/boolean_schema.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/const.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/contains.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/default.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/definitions.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/dependencies.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/enum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/exclusiveMaximum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/exclusiveMinimum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/format.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/items.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/maxItems.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/maxLength.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/maxProperties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/maximum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/minItems.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/minLength.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/minProperties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/minimum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/multipleOf.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/not.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/oneOf.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/optional/bignum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/optional/ecmascript-regex.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/optional/format.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/optional/zeroTerminatedFloats.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/pattern.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/patternProperties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/properties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/propertyNames.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/ref.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/refRemote.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/required.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/type.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft6/uniqueItems.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/additionalItems.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/additionalProperties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/allOf.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/anyOf.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/boolean_schema.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/const.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/contains.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/default.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/definitions.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/dependencies.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/enum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/exclusiveMaximum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/exclusiveMinimum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/format.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/if-then-else.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/items.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/maxItems.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/maxLength.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/maxProperties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/maximum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/minItems.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/minLength.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/minProperties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/minimum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/multipleOf.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/not.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/oneOf.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/bignum.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/content.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/ecmascript-regex.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/date-time.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/date.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/email.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/hostname.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/idn-email.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/idn-hostname.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/ipv4.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/ipv6.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/iri-reference.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/iri.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/json-pointer.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/regex.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/relative-json-pointer.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/time.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/uri-reference.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/uri-template.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/uri.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/optional/zeroTerminatedFloats.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/pattern.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/patternProperties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/properties.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/propertyNames.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/ref.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/refRemote.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/required.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/type.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/draft7/uniqueItems.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/extra/file with space.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/extra/fragment_schema.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/remotes/folder/folderInteger.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/remotes/integer.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/remotes/name.json create mode 100644 third_party/opa/internal/gojsonschema/testdata/remotes/subSchemas.json create mode 100644 third_party/opa/internal/gojsonschema/types.go create mode 100644 third_party/opa/internal/gojsonschema/utils.go create mode 100644 third_party/opa/internal/gojsonschema/utils_test.go create mode 100644 third_party/opa/internal/gojsonschema/validation.go create mode 100644 third_party/opa/internal/json/patch/patch.go create mode 100644 third_party/opa/internal/json/patch/patch_test.go create mode 100644 third_party/opa/internal/jwx/.gitignore create mode 100644 third_party/opa/internal/jwx/LICENSE create mode 100644 third_party/opa/internal/jwx/Makefile create mode 100644 third_party/opa/internal/jwx/buffer/buffer.go create mode 100644 third_party/opa/internal/jwx/buffer/buffer_test.go create mode 100644 third_party/opa/internal/jwx/jwa/elliptic.go create mode 100644 third_party/opa/internal/jwx/jwa/key_type.go create mode 100644 third_party/opa/internal/jwx/jwa/parameters.go create mode 100644 third_party/opa/internal/jwx/jwa/signature.go create mode 100644 third_party/opa/internal/jwx/jwk/ecdsa.go create mode 100644 third_party/opa/internal/jwx/jwk/ecdsa_test.go create mode 100644 third_party/opa/internal/jwx/jwk/headers.go create mode 100644 third_party/opa/internal/jwx/jwk/headers_test.go create mode 100644 third_party/opa/internal/jwx/jwk/interface.go create mode 100644 third_party/opa/internal/jwx/jwk/jwk.go create mode 100644 third_party/opa/internal/jwx/jwk/jwk_test.go create mode 100644 third_party/opa/internal/jwx/jwk/key_ops.go create mode 100644 third_party/opa/internal/jwx/jwk/rsa.go create mode 100644 third_party/opa/internal/jwx/jwk/rsa_test.go create mode 100644 third_party/opa/internal/jwx/jwk/symmetric.go create mode 100644 third_party/opa/internal/jwx/jwk/symmetric_test.go create mode 100644 third_party/opa/internal/jwx/jws/headers.go create mode 100644 third_party/opa/internal/jwx/jws/headers_test.go create mode 100644 third_party/opa/internal/jwx/jws/interface.go create mode 100644 third_party/opa/internal/jwx/jws/jws.go create mode 100644 third_party/opa/internal/jwx/jws/jws_test.go create mode 100644 third_party/opa/internal/jwx/jws/message.go create mode 100644 third_party/opa/internal/jwx/jws/sign/ecdsa.go create mode 100644 third_party/opa/internal/jwx/jws/sign/ecdsa_test.go create mode 100644 third_party/opa/internal/jwx/jws/sign/hmac.go create mode 100644 third_party/opa/internal/jwx/jws/sign/hmac_test.go create mode 100644 third_party/opa/internal/jwx/jws/sign/interface.go create mode 100644 third_party/opa/internal/jwx/jws/sign/rsa.go create mode 100644 third_party/opa/internal/jwx/jws/sign/sign.go create mode 100644 third_party/opa/internal/jwx/jws/verify/ecdsa.go create mode 100644 third_party/opa/internal/jwx/jws/verify/ecdsa_test.go create mode 100644 third_party/opa/internal/jwx/jws/verify/hmac.go create mode 100644 third_party/opa/internal/jwx/jws/verify/hmac_test.go create mode 100644 third_party/opa/internal/jwx/jws/verify/interface.go create mode 100644 third_party/opa/internal/jwx/jws/verify/rsa.go create mode 100644 third_party/opa/internal/jwx/jws/verify/rsa_test.go create mode 100644 third_party/opa/internal/jwx/jws/verify/verify.go create mode 100644 third_party/opa/internal/jwx/jws/verify/verify_test.go create mode 100644 third_party/opa/internal/lcss/README.md create mode 100644 third_party/opa/internal/lcss/lcss.go create mode 100644 third_party/opa/internal/lcss/lcss_test.go create mode 100644 third_party/opa/internal/lcss/qsufsort.go create mode 100644 third_party/opa/internal/leb128/leb128.go create mode 100644 third_party/opa/internal/leb128/leb128_test.go create mode 100644 third_party/opa/internal/logging/logging.go create mode 100644 third_party/opa/internal/logging/logging_test.go create mode 100644 third_party/opa/internal/merge/merge.go create mode 100644 third_party/opa/internal/merge/merge_test.go create mode 100644 third_party/opa/internal/pathwatcher/utils.go create mode 100644 third_party/opa/internal/pathwatcher/utils_test.go create mode 100644 third_party/opa/internal/planner/planner.go create mode 100644 third_party/opa/internal/planner/planner_test.go create mode 100644 third_party/opa/internal/planner/rules.go create mode 100644 third_party/opa/internal/planner/rules_test.go create mode 100644 third_party/opa/internal/planner/varstack.go create mode 100644 third_party/opa/internal/planner/varstack_test.go create mode 100644 third_party/opa/internal/presentation/presentation.go create mode 100644 third_party/opa/internal/presentation/presentation_test.go create mode 100644 third_party/opa/internal/prometheus/prometheus.go create mode 100644 third_party/opa/internal/prometheus/prometheus_go1.17.go create mode 100644 third_party/opa/internal/prometheus/prometheus_test.go create mode 100644 third_party/opa/internal/providers/aws/NOTICE.txt create mode 100644 third_party/opa/internal/providers/aws/crypto/compare.go create mode 100644 third_party/opa/internal/providers/aws/crypto/compare_test.go create mode 100644 third_party/opa/internal/providers/aws/crypto/ecc.go create mode 100644 third_party/opa/internal/providers/aws/crypto/ecc_test.go create mode 100644 third_party/opa/internal/providers/aws/ecr.go create mode 100644 third_party/opa/internal/providers/aws/ecr_test.go create mode 100644 third_party/opa/internal/providers/aws/kms.go create mode 100644 third_party/opa/internal/providers/aws/kms_test.go create mode 100644 third_party/opa/internal/providers/aws/signing_v4.go create mode 100644 third_party/opa/internal/providers/aws/signing_v4a.go create mode 100644 third_party/opa/internal/providers/aws/util.go create mode 100644 third_party/opa/internal/providers/aws/v4/const.go create mode 100644 third_party/opa/internal/providers/aws/v4/header_rules.go create mode 100644 third_party/opa/internal/providers/aws/v4/headers.go create mode 100644 third_party/opa/internal/providers/aws/v4/host.go create mode 100644 third_party/opa/internal/providers/aws/v4/util.go create mode 100644 third_party/opa/internal/providers/aws/v4/util_test.go create mode 100644 third_party/opa/internal/ref/ref.go create mode 100644 third_party/opa/internal/rego/opa/engine.go create mode 100644 third_party/opa/internal/rego/opa/options.go create mode 100644 third_party/opa/internal/report/report.go create mode 100644 third_party/opa/internal/report/report_test.go create mode 100644 third_party/opa/internal/runtime/init/init.go create mode 100644 third_party/opa/internal/runtime/init/init_test.go create mode 100644 third_party/opa/internal/runtime/runtime.go create mode 100644 third_party/opa/internal/semver/LICENSE create mode 100644 third_party/opa/internal/semver/semver.go create mode 100644 third_party/opa/internal/semver/semver_test.go create mode 100644 third_party/opa/internal/storage/mock/mock.go create mode 100644 third_party/opa/internal/strings/strings.go create mode 100644 third_party/opa/internal/strvals/doc.go create mode 100644 third_party/opa/internal/strvals/parser.go create mode 100644 third_party/opa/internal/strvals/parser_test.go create mode 100644 third_party/opa/internal/uuid/uuid.go create mode 100644 third_party/opa/internal/uuid/uuid_test.go create mode 100644 third_party/opa/internal/version/version.go create mode 100644 third_party/opa/internal/wasm/constant/constant.go create mode 100644 third_party/opa/internal/wasm/encoding/doc.go create mode 100644 third_party/opa/internal/wasm/encoding/encoding_test.go create mode 100644 third_party/opa/internal/wasm/encoding/reader.go create mode 100644 third_party/opa/internal/wasm/encoding/testdata/test1.wasm create mode 100644 third_party/opa/internal/wasm/encoding/writer.go create mode 100644 third_party/opa/internal/wasm/instruction/control.go create mode 100644 third_party/opa/internal/wasm/instruction/instruction.go create mode 100644 third_party/opa/internal/wasm/instruction/memory.go create mode 100644 third_party/opa/internal/wasm/instruction/numeric.go create mode 100644 third_party/opa/internal/wasm/instruction/parametric.go create mode 100644 third_party/opa/internal/wasm/instruction/variable.go create mode 100644 third_party/opa/internal/wasm/module/module.go create mode 100644 third_party/opa/internal/wasm/module/pretty.go create mode 100644 third_party/opa/internal/wasm/opcode/opcode.go create mode 100644 third_party/opa/internal/wasm/sdk/README.md create mode 100644 third_party/opa/internal/wasm/sdk/examples/basic/.gitignore create mode 100644 third_party/opa/internal/wasm/sdk/examples/basic/README.md create mode 100644 third_party/opa/internal/wasm/sdk/examples/basic/example-1.rego create mode 100644 third_party/opa/internal/wasm/sdk/examples/basic/example-2.rego create mode 100644 third_party/opa/internal/wasm/sdk/examples/basic/main.go create mode 100644 third_party/opa/internal/wasm/sdk/examples/loaders/.gitignore create mode 100644 third_party/opa/internal/wasm/sdk/examples/loaders/README.md create mode 100644 third_party/opa/internal/wasm/sdk/examples/loaders/example.rego create mode 100644 third_party/opa/internal/wasm/sdk/examples/loaders/main.go create mode 100644 third_party/opa/internal/wasm/sdk/internal/wasm/bindings.go create mode 100644 third_party/opa/internal/wasm/sdk/internal/wasm/pool.go create mode 100644 third_party/opa/internal/wasm/sdk/internal/wasm/pool_test.go create mode 100644 third_party/opa/internal/wasm/sdk/internal/wasm/vm.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/capabilities/capabilities.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/capabilities/capabilities_nowasm.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/config.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/errors/errors.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/loader/file/config.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/loader/file/loader.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/loader/file/loader_test.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/loader/http/config.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/loader/http/loader.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/loader/http/loader_test.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/loader/http/util.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/loader/loader.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/opa.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/opa_bench_test.go create mode 100644 third_party/opa/internal/wasm/sdk/opa/opa_test.go create mode 100644 third_party/opa/internal/wasm/sdk/test/e2e/exceptions.yaml create mode 100644 third_party/opa/internal/wasm/sdk/test/e2e/external_test.go create mode 100644 third_party/opa/internal/wasm/types/types.go create mode 100644 third_party/opa/internal/wasm/util/util.go create mode 100644 third_party/opa/ir/doc.go create mode 100644 third_party/opa/ir/encoding/doc.go create mode 100644 third_party/opa/ir/encoding/encoding_test.go create mode 100644 third_party/opa/ir/ir.go create mode 100644 third_party/opa/ir/pretty.go create mode 100644 third_party/opa/ir/walk.go create mode 100644 third_party/opa/keys/doc.go create mode 100644 third_party/opa/keys/keys.go create mode 100644 third_party/opa/loader/doc.go create mode 100644 third_party/opa/loader/errors.go create mode 100644 third_party/opa/loader/extension/doc.go create mode 100644 third_party/opa/loader/extension/extension.go create mode 100644 third_party/opa/loader/filter/doc.go create mode 100644 third_party/opa/loader/filter/filter.go create mode 100644 third_party/opa/loader/loader.go create mode 100644 third_party/opa/loader/loader_test.go create mode 100644 third_party/opa/logging/doc.go create mode 100644 third_party/opa/logging/logging.go create mode 100644 third_party/opa/logging/test/doc.go create mode 100644 third_party/opa/logging/test/test.go create mode 100644 third_party/opa/logo/logo-144x144.png create mode 100644 third_party/opa/logo/logo.ico create mode 100644 third_party/opa/logo/logo.png create mode 100644 third_party/opa/logo/logo.svg create mode 100644 third_party/opa/main.go create mode 100644 third_party/opa/main_windows.go create mode 100644 third_party/opa/metrics/doc.go create mode 100644 third_party/opa/metrics/metrics.go create mode 100644 third_party/opa/misc/syntax/sublime/rego.sublime-syntax create mode 100644 third_party/opa/misc/syntax/textmate/Rego.tmLanguage create mode 100644 third_party/opa/netlify.toml create mode 100644 third_party/opa/plugins/bundle/config.go create mode 100644 third_party/opa/plugins/bundle/doc.go create mode 100644 third_party/opa/plugins/bundle/errors.go create mode 100644 third_party/opa/plugins/bundle/plugin.go create mode 100644 third_party/opa/plugins/bundle/status.go create mode 100644 third_party/opa/plugins/discovery/config.go create mode 100644 third_party/opa/plugins/discovery/discovery.go create mode 100644 third_party/opa/plugins/discovery/doc.go create mode 100644 third_party/opa/plugins/doc.go create mode 100644 third_party/opa/plugins/logs/doc.go create mode 100644 third_party/opa/plugins/logs/plugin.go create mode 100644 third_party/opa/plugins/logs/status/doc.go create mode 100644 third_party/opa/plugins/logs/status/status.go create mode 100644 third_party/opa/plugins/plugins.go create mode 100644 third_party/opa/plugins/plugins_test.go create mode 100644 third_party/opa/plugins/rest/auth.go create mode 100644 third_party/opa/plugins/rest/doc.go create mode 100644 third_party/opa/plugins/rest/gcp.go create mode 100644 third_party/opa/plugins/rest/rest.go create mode 100644 third_party/opa/plugins/server/decoding/config.go create mode 100644 third_party/opa/plugins/server/decoding/doc.go create mode 100644 third_party/opa/plugins/server/doc.go create mode 100644 third_party/opa/plugins/server/encoding/config.go create mode 100644 third_party/opa/plugins/server/encoding/doc.go create mode 100644 third_party/opa/plugins/server/metrics/config.go create mode 100644 third_party/opa/plugins/server/metrics/doc.go create mode 100644 third_party/opa/plugins/status/doc.go create mode 100644 third_party/opa/plugins/status/metrics.go create mode 100644 third_party/opa/plugins/status/plugin.go create mode 100644 third_party/opa/profiler/doc.go create mode 100644 third_party/opa/profiler/profiler.go create mode 100644 third_party/opa/proposals/attic/REGO_V2_PROPOSAL.md create mode 100644 third_party/opa/race.txt create mode 100644 third_party/opa/refactor/doc.go create mode 100644 third_party/opa/refactor/refactor.go create mode 100644 third_party/opa/rego/doc.go create mode 100644 third_party/opa/rego/errors.go create mode 100644 third_party/opa/rego/plugins.go create mode 100644 third_party/opa/rego/rego.go create mode 100644 third_party/opa/rego/rego_test.go create mode 100644 third_party/opa/rego/resultset.go create mode 100644 third_party/opa/repl/doc.go create mode 100644 third_party/opa/repl/errors.go create mode 100644 third_party/opa/repl/repl.go create mode 100644 third_party/opa/repl/repl_test.go create mode 100644 third_party/opa/resolver/doc.go create mode 100644 third_party/opa/resolver/interface.go create mode 100644 third_party/opa/resolver/wasm/doc.go create mode 100644 third_party/opa/resolver/wasm/wasm.go create mode 100644 third_party/opa/runtime/doc.go create mode 100644 third_party/opa/runtime/logging.go create mode 100644 third_party/opa/runtime/runtime.go create mode 100644 third_party/opa/schemas/doc.go create mode 100644 third_party/opa/schemas/schemas.go create mode 100644 third_party/opa/sdk/doc.go create mode 100644 third_party/opa/sdk/opa.go create mode 100644 third_party/opa/sdk/opa_test.go create mode 100644 third_party/opa/sdk/test/doc.go create mode 100644 third_party/opa/sdk/test/test.go create mode 100644 third_party/opa/server/authorizer/authorizer.go create mode 100644 third_party/opa/server/authorizer/doc.go create mode 100644 third_party/opa/server/buffer.go create mode 100644 third_party/opa/server/doc.go create mode 100644 third_party/opa/server/features.go create mode 100644 third_party/opa/server/handlers/compress.go create mode 100644 third_party/opa/server/handlers/decoding.go create mode 100644 third_party/opa/server/handlers/doc.go create mode 100644 third_party/opa/server/identifier/certs.go create mode 100644 third_party/opa/server/identifier/doc.go create mode 100644 third_party/opa/server/identifier/identifier.go create mode 100644 third_party/opa/server/identifier/tls.go create mode 100644 third_party/opa/server/identifier/token.go create mode 100644 third_party/opa/server/server.go create mode 100644 third_party/opa/server/types/doc.go create mode 100644 third_party/opa/server/types/types.go create mode 100644 third_party/opa/server/writer/doc.go create mode 100644 third_party/opa/server/writer/writer.go create mode 100644 third_party/opa/storage/disk/config.go create mode 100644 third_party/opa/storage/disk/disk.go create mode 100644 third_party/opa/storage/disk/doc.go create mode 100644 third_party/opa/storage/doc.go create mode 100644 third_party/opa/storage/errors.go create mode 100644 third_party/opa/storage/inmem/doc.go create mode 100644 third_party/opa/storage/inmem/inmem.go create mode 100644 third_party/opa/storage/inmem/opts.go create mode 100644 third_party/opa/storage/inmem/test/doc.go create mode 100644 third_party/opa/storage/inmem/test/testutil.go create mode 100644 third_party/opa/storage/interface.go create mode 100644 third_party/opa/storage/path.go create mode 100644 third_party/opa/storage/storage.go create mode 100644 third_party/opa/test/authz/doc.go create mode 100644 third_party/opa/test/authz/testing.go create mode 100644 third_party/opa/test/cases/cases.go create mode 100644 third_party/opa/test/cases/doc.go create mode 100644 third_party/opa/test/e2e/doc.go create mode 100644 third_party/opa/test/e2e/logs/doc.go create mode 100644 third_party/opa/test/e2e/logs/utils.go create mode 100644 third_party/opa/test/e2e/testing.go create mode 100644 third_party/opa/tester/doc.go create mode 100644 third_party/opa/tester/reporter.go create mode 100644 third_party/opa/tester/runner.go create mode 100644 third_party/opa/tester/runner_test.go create mode 100644 third_party/opa/topdown/builtins.go create mode 100644 third_party/opa/topdown/builtins/builtins.go create mode 100644 third_party/opa/topdown/builtins/doc.go create mode 100644 third_party/opa/topdown/cache.go create mode 100644 third_party/opa/topdown/cache/cache.go create mode 100644 third_party/opa/topdown/cache/doc.go create mode 100644 third_party/opa/topdown/cancel.go create mode 100644 third_party/opa/topdown/copypropagation/copypropagation.go create mode 100644 third_party/opa/topdown/copypropagation/doc.go create mode 100644 third_party/opa/topdown/doc.go create mode 100644 third_party/opa/topdown/errors.go create mode 100644 third_party/opa/topdown/graphql.go create mode 100644 third_party/opa/topdown/http.go create mode 100644 third_party/opa/topdown/instrumentation.go create mode 100644 third_party/opa/topdown/lineage/doc.go create mode 100644 third_party/opa/topdown/lineage/lineage.go create mode 100644 third_party/opa/topdown/print.go create mode 100644 third_party/opa/topdown/print/doc.go create mode 100644 third_party/opa/topdown/print/print.go create mode 100644 third_party/opa/topdown/query.go create mode 100644 third_party/opa/topdown/trace.go create mode 100644 third_party/opa/tracing/doc.go create mode 100644 third_party/opa/tracing/tracing.go create mode 100644 third_party/opa/types/decode.go create mode 100644 third_party/opa/types/doc.go create mode 100644 third_party/opa/types/types.go create mode 100644 third_party/opa/util/backoff.go create mode 100644 third_party/opa/util/close.go create mode 100644 third_party/opa/util/compare.go create mode 100644 third_party/opa/util/decoding/context.go create mode 100644 third_party/opa/util/decoding/doc.go create mode 100644 third_party/opa/util/doc.go create mode 100644 third_party/opa/util/enumflag.go create mode 100644 third_party/opa/util/graph.go create mode 100644 third_party/opa/util/hashmap.go create mode 100644 third_party/opa/util/json.go create mode 100644 third_party/opa/util/maps.go create mode 100644 third_party/opa/util/queue.go create mode 100644 third_party/opa/util/read_gzip_body.go create mode 100644 third_party/opa/util/test/benchmark.go create mode 100644 third_party/opa/util/test/ci_skip.go create mode 100644 third_party/opa/util/test/ci_skip_darwin.go create mode 100644 third_party/opa/util/test/doc.go create mode 100644 third_party/opa/util/test/tempfs.go create mode 100644 third_party/opa/util/test/tempus.go create mode 100644 third_party/opa/util/time.go create mode 100644 third_party/opa/util/wait.go create mode 100644 third_party/opa/v1/ast/annotations.go create mode 100644 third_party/opa/v1/ast/annotations_test.go create mode 100644 third_party/opa/v1/ast/builtins.go create mode 100644 third_party/opa/v1/ast/builtins_test.go create mode 100644 third_party/opa/v1/ast/capabilities.go create mode 100644 third_party/opa/v1/ast/capabilities_test.go create mode 100644 third_party/opa/v1/ast/check.go create mode 100644 third_party/opa/v1/ast/check_test.go create mode 100644 third_party/opa/v1/ast/compare.go create mode 100644 third_party/opa/v1/ast/compare_test.go create mode 100644 third_party/opa/v1/ast/compile.go create mode 100644 third_party/opa/v1/ast/compile_bench_test.go create mode 100644 third_party/opa/v1/ast/compile_test.go create mode 100644 third_party/opa/v1/ast/compilehelper.go create mode 100644 third_party/opa/v1/ast/compilehelper_test.go create mode 100644 third_party/opa/v1/ast/compilemetrics.go create mode 100644 third_party/opa/v1/ast/conflicts.go create mode 100644 third_party/opa/v1/ast/default_module_loader.go create mode 100644 third_party/opa/v1/ast/doc.go create mode 100644 third_party/opa/v1/ast/env.go create mode 100644 third_party/opa/v1/ast/env_test.go create mode 100644 third_party/opa/v1/ast/errors.go create mode 100644 third_party/opa/v1/ast/errors_test.go create mode 100644 third_party/opa/v1/ast/example_test.go create mode 100644 third_party/opa/v1/ast/fuzz_test.go create mode 100644 third_party/opa/v1/ast/index.go create mode 100644 third_party/opa/v1/ast/index_test.go create mode 100644 third_party/opa/v1/ast/internal/scanner/scanner.go create mode 100644 third_party/opa/v1/ast/internal/scanner/scanner_test.go create mode 100644 third_party/opa/v1/ast/internal/tokens/tokens.go create mode 100644 third_party/opa/v1/ast/interning.go create mode 100644 third_party/opa/v1/ast/interning_test.go create mode 100644 third_party/opa/v1/ast/json/json.go create mode 100644 third_party/opa/v1/ast/location/location.go create mode 100644 third_party/opa/v1/ast/location/location_test.go create mode 100644 third_party/opa/v1/ast/map.go create mode 100644 third_party/opa/v1/ast/map_test.go create mode 100644 third_party/opa/v1/ast/marshal_test.go create mode 100644 third_party/opa/v1/ast/oracle/oracle.go create mode 100644 third_party/opa/v1/ast/oracle/oracle_test.go create mode 100644 third_party/opa/v1/ast/parser.go create mode 100644 third_party/opa/v1/ast/parser_bench_test.go create mode 100644 third_party/opa/v1/ast/parser_ext.go create mode 100644 third_party/opa/v1/ast/parser_ext_test.go create mode 100644 third_party/opa/v1/ast/parser_test.go create mode 100644 third_party/opa/v1/ast/policy.go create mode 100644 third_party/opa/v1/ast/policy_test.go create mode 100644 third_party/opa/v1/ast/pretty.go create mode 100644 third_party/opa/v1/ast/pretty_test.go create mode 100644 third_party/opa/v1/ast/rego_compiler.go create mode 100644 third_party/opa/v1/ast/rego_v1.go create mode 100644 third_party/opa/v1/ast/schema.go create mode 100644 third_party/opa/v1/ast/schema_test.go create mode 100644 third_party/opa/v1/ast/strings.go create mode 100644 third_party/opa/v1/ast/strings_bench_test.go create mode 100644 third_party/opa/v1/ast/syncpools.go create mode 100644 third_party/opa/v1/ast/term.go create mode 100644 third_party/opa/v1/ast/term_bench_test.go create mode 100644 third_party/opa/v1/ast/term_test.go create mode 100644 third_party/opa/v1/ast/testdata/_definitions.json create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00000.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00001.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00002.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00003.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00004.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00005.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00006.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00007.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00008.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00009.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00010.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00011.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00012.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00013.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00014.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00015.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00016.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00017.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00018.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00019.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00020.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00021.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00022.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00023.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00024.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00025.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00026.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00027.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00028.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00029.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00030.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00031.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00032.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00033.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00034.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00035.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00036.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00037.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00038.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00039.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00040.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00041.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00042.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00043.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00044.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00045.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00046.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00047.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00048.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00049.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00050.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00051.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00052.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00053.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00054.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00055.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00056.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00057.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00058.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00059.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00060.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00061.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00062.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00063.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00064.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00065.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00066.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00067.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00068.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00069.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00070.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00071.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00072.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00073.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00074.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00075.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00076.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00077.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00078.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00079.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00080.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00081.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00082.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00083.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00084.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00085.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00086.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00087.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00088.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00089.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00090.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00091.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00092.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00093.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00094.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00095.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00096.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00097.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00098.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00099.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00100.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00101.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00102.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00103.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00104.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00105.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00106.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00107.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00108.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00109.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00110.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00111.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00112.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00113.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00114.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00115.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00116.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00117.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00118.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00119.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00120.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00121.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00122.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00123.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00124.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00125.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00126.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00127.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00128.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00129.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00130.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00131.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00132.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00133.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00134.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00135.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00136.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00137.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00138.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00139.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00140.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00141.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00142.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00143.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00144.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00145.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00146.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00147.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00148.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00149.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00150.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00151.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00152.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00153.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00154.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00155.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00156.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00157.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00158.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00159.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00160.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00161.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00162.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00163.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00164.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00165.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00166.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00167.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00168.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00169.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00170.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00171.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00172.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00173.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00174.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00175.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00176.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00177.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00178.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00179.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00180.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00181.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00182.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00183.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00184.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00185.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00186.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00187.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00188.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00189.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00190.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00191.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00192.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00193.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00194.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00195.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00196.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00197.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00198.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00199.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00200.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00201.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00202.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00203.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00204.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00205.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00206.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00207.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00208.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00209.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00210.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00211.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00212.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00213.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00214.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00215.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00216.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00217.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00218.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00219.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00220.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00221.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00222.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00223.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00224.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00225.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00226.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00227.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00228.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00229.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00230.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00231.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00232.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00233.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00234.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00235.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00236.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00237.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00238.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00239.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00240.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00241.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00242.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00243.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00244.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00245.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00246.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00247.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00248.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00249.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00250.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00251.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00252.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00253.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00254.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00255.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00256.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00257.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00258.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00259.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00260.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00261.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00262.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00263.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00264.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00265.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00266.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00267.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00268.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00269.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00270.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00271.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00272.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00273.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00274.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00275.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00276.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00277.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00278.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00279.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00280.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00281.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00282.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00283.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00284.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00285.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00286.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00287.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00288.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00289.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00290.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00291.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00292.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00293.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00294.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00295.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00296.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00297.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00298.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00299.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00300.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00301.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00302.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00303.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00304.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00305.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00306.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00307.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00308.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00309.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00310.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00311.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00312.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00313.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00314.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00315.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00316.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00317.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00318.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00319.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00320.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00321.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00322.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00323.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00324.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00325.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00326.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00327.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00328.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00329.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00330.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00331.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00332.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00333.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00334.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00335.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00336.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00337.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00338.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00339.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00340.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00341.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00342.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00343.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00344.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00345.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00346.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00347.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00348.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00349.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00350.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00351.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00352.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00353.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00354.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00355.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00356.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00357.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00358.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00359.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00360.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00361.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00362.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00363.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00364.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00365.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00366.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00367.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00368.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00369.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00370.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00371.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00372.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00373.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00374.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00375.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00376.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00377.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00378.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00379.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00380.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00381.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00382.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00383.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00384.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00385.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00386.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00387.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00388.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00389.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00390.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00391.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00392.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00393.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00394.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00395.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00396.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00397.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00398.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00399.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00400.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00401.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00402.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00403.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00404.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00405.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00406.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00407.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00408.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00409.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00410.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00411.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00412.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00413.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00414.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00415.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00416.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00417.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00418.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00419.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00420.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00421.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00422.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00423.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00424.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00425.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00426.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00427.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00428.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00429.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00430.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00431.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00432.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00433.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00434.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00435.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00436.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00437.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00438.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00439.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00440.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00441.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00442.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00443.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00444.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00445.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00446.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00447.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00448.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00449.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00450.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00451.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00452.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00453.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00454.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00455.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00456.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00457.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00458.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00459.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00460.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00461.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00462.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00463.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00464.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00465.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00466.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00467.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00468.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00469.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00470.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00471.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00472.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00473.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00474.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00475.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00476.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00477.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00478.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00479.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00480.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00481.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00482.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00483.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00484.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00485.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00486.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00487.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00488.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00489.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00490.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00491.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00492.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00493.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00494.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00495.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00496.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00497.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00498.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00499.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00500.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00501.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00502.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00503.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00504.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00505.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00506.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00507.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00508.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00509.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00510.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00511.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00512.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00513.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00514.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00515.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00516.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00517.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00518.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00519.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00520.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00521.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00522.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00523.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00524.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00525.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00526.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00527.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00528.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00529.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00530.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00531.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00532.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00533.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00534.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00535.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00536.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00537.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00538.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00539.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00540.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00541.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00542.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00543.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00544.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00545.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00546.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00547.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00548.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00549.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00550.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00551.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00552.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00553.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00554.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00555.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00556.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00557.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00558.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00559.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00560.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00561.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00562.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00563.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00564.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00565.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00566.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00567.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00568.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00569.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00570.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00571.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00572.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00573.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00574.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00575.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00576.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00577.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00578.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00579.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00580.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00581.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00582.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00583.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00584.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00585.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00586.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00587.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00588.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00589.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00590.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00591.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00592.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00593.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00594.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00595.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00596.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00597.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00598.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00599.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00600.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00601.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00602.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00603.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00604.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00605.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00606.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00607.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00608.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00609.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00610.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00611.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00612.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00613.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00614.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00615.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00616.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00617.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00618.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00619.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00620.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00621.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00622.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00623.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00624.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00625.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00626.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00627.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00628.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00629.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00630.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00631.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00632.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00633.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00634.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00635.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00636.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00637.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00638.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00639.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00640.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00641.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00642.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00643.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00644.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00645.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00646.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00647.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00648.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00649.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00650.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00651.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00652.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00653.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00654.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00655.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00656.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00657.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00658.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00659.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00660.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00661.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00662.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00663.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00664.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00665.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00666.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00667.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00668.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00669.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00670.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00671.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00672.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00673.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00674.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00675.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00676.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00677.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00678.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00679.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00680.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00681.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00682.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00683.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00684.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00685.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00686.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00687.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00688.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00689.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00690.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00691.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00692.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00693.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00694.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00695.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00696.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00697.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00698.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00699.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00700.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00701.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00702.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00703.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00704.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00705.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00706.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00707.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00708.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00709.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00710.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00711.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00712.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00713.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00714.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00715.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00716.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00717.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00718.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00719.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00720.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00721.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00722.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00723.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00724.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00725.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00726.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00727.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00728.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00729.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00730.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00731.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00732.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00733.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00734.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00735.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00736.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00737.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00738.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00739.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00740.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00741.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00742.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00743.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00744.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00745.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00746.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00747.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00748.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00749.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00750.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00751.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00752.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00753.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00754.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00755.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00756.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00757.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00758.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00759.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00760.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00761.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00762.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00763.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00764.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00765.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00766.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00767.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00768.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00769.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00770.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00771.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00772.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00773.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00774.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00775.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00776.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00777.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00778.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00779.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00780.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00781.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00782.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00783.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00784.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00785.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00786.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00787.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00788.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00789.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00790.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00791.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00792.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00793.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00794.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00795.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00796.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00797.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00798.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00799.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00800.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00801.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00802.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00803.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00804.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00805.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00806.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00807.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00808.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00809.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00810.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00811.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00812.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00813.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00814.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00815.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00816.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00817.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00818.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00819.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00820.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00821.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00822.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00823.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00824.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00825.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00826.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00827.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00828.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00829.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00830.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00831.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00832.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00833.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00834.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00835.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00836.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00837.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00838.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00839.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00840.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00841.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00842.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00843.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00844.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00845.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00846.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00847.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00848.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00849.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00850.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00851.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00852.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00853.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00854.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00855.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00856.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00857.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00858.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00859.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00860.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00861.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00862.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00863.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00864.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00865.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00866.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00867.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00868.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00869.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00870.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00871.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00872.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00873.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00874.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00875.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00876.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00877.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00878.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00879.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00880.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00881.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00882.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00883.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00884.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00885.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00886.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00887.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00888.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00889.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00890.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00891.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00892.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00893.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00894.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00895.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00896.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00897.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00898.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00899.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00900.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00901.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00902.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00903.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00904.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00905.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00906.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00907.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00908.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00909.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00910.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00911.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00912.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00913.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00914.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00915.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00916.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00917.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00918.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00919.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00920.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00921.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00922.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00923.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00924.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00925.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00926.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00927.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00928.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00929.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00930.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00931.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00932.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00933.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00934.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00935.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00936.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00937.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00938.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00939.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00940.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00941.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00942.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00943.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00944.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00945.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00946.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00947.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00948.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00949.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00950.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00951.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00952.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00953.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00954.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00955.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00956.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00957.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00958.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00959.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00960.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00961.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00962.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00963.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00964.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00965.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00966.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00967.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00968.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00969.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00970.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00971.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00972.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00973.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00974.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00975.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00976.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00977.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00978.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00979.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00980.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00981.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00982.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00983.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00984.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00985.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00986.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00987.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00988.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00989.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00990.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00991.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00992.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00993.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00994.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00995.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00996.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00997.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00998.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00999.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01000.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01001.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01002.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01003.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01004.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01005.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01006.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01007.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01008.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01009.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01010.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01011.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01012.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01013.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01014.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01015.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01016.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01017.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01018.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01019.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01020.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01021.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01022.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01023.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01024.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01025.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01026.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01027.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01028.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01029.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01030.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01031.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01032.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01033.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01034.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01035.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01036.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01037.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01038.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01039.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01040.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01041.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01042.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01043.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01044.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01045.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01046.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01047.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01048.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01049.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01050.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01051.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01052.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01053.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01054.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01055.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01056.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01057.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01058.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01059.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01060.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01061.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01062.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01063.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01064.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01065.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01066.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01067.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01068.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01069.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01070.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01071.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01072.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01073.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01074.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01075.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01076.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01077.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01078.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01079.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01080.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01081.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01082.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01083.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01084.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01085.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01086.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01087.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01088.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01089.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01090.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01091.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01092.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01093.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01094.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01095.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01096.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01097.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01098.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01099.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01100.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01101.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01102.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01103.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01104.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01105.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01106.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01107.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01108.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01109.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01110.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01111.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01112.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01113.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01114.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01115.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01116.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01117.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01118.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01119.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01120.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01121.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01122.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01123.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01124.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01125.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01126.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01127.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01128.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01129.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01130.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01131.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01132.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01133.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01134.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01135.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01136.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01137.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01138.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01139.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01140.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01141.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01142.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01143.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01144.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01145.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01146.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01147.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01148.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01149.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01150.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01151.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01152.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01153.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01154.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01155.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01156.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01157.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01158.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01159.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01160.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01161.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01162.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01163.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01164.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01165.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01166.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01167.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01168.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01169.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01170.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01171.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01172.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01173.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01174.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01175.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01176.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01177.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01178.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01179.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01180.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01181.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01182.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01183.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01184.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01185.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01186.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01187.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01188.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01189.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01190.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01191.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01192.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01193.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01194.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01195.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01196.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01197.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01198.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01199.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01200.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01201.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01202.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01203.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01204.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01205.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01206.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01207.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01208.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01209.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01210.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01211.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01212.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01213.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01214.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01215.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01216.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01217.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01218.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01219.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01220.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01221.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01222.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01223.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01224.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01225.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01226.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01227.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01228.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01229.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01230.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01231.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01232.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01233.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01234.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01235.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01236.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01237.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01238.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01239.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01240.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01241.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01242.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01243.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01244.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01245.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01246.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01247.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01248.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01249.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01250.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01251.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01252.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01253.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01254.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01255.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01256.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01257.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01258.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01259.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01260.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01261.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01262.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01263.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01264.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01265.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01266.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01267.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01268.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01269.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01270.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01271.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01272.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01273.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01274.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01275.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01276.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01277.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01278.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01279.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01280.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01281.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01282.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01283.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01284.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01285.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01286.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01287.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01288.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01289.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01290.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01291.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01292.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01293.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01294.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01295.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01296.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01297.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01298.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01299.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01300.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01301.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01302.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01303.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01304.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01305.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01306.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01307.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01308.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01309.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01310.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01311.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01312.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01313.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01314.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01315.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01316.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01317.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01318.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01319.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01320.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01321.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01322.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01323.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01324.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01325.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01326.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01327.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01328.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01329.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01330.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01331.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01332.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01333.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01334.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01335.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01336.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01337.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01338.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01339.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01340.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01341.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01342.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01343.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01344.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01345.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01346.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01347.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01348.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01349.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01350.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01351.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01352.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01353.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01354.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01355.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01356.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01357.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01358.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01359.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01360.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01361.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01362.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01363.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01364.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01365.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01366.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01367.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01368.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01369.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01370.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01371.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01372.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01373.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01374.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01375.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01376.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01377.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01378.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01379.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01380.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01381.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01382.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01383.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01384.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01385.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01386.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01387.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01388.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01389.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01390.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01391.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01392.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01393.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01394.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01395.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01396.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01397.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01398.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01399.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01400.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01401.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01402.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01403.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01404.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01405.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01406.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01407.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01408.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01409.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01410.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01411.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01412.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01413.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01414.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01415.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01416.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01417.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01418.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01419.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01420.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01421.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01422.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01423.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01424.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01425.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01426.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01427.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01428.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01429.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01430.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01431.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01432.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01433.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01434.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01435.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01436.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01437.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01438.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01439.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01440.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01441.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01442.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01443.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01444.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01445.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01446.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01447.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01448.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01449.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01450.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01451.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01452.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01453.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01454.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01455.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01456.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01457.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01458.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01459.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01460.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01461.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01462.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01463.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01464.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01465.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01466.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01467.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01468.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01469.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01470.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01471.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01472.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01473.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01474.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01475.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01476.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01477.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01478.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01479.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01480.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01481.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01482.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01483.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01484.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01485.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01486.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01487.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01488.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01489.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01490.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01491.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01492.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01493.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01494.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01495.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01496.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01497.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01498.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01499.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01500.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01501.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01502.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01503.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01504.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01505.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01506.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01507.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01508.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01509.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01510.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01511.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01512.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01513.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01514.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01515.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01516.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01517.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01518.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01519.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01520.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01521.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01522.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01523.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01524.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01525.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01526.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01527.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01528.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01529.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01530.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01531.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01532.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01533.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01534.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01535.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01536.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01537.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01538.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01539.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01540.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01541.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01542.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01543.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01544.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01545.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01546.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01547.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01548.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01549.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01550.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01551.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01552.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01553.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01554.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01555.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01556.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01557.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01558.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01559.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01560.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01561.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01562.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01563.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01564.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01565.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01566.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01567.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01568.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01569.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01570.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01571.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01572.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01573.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01574.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01575.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01576.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01577.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01578.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01579.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01580.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01581.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01582.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01583.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01584.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01585.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01586.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01587.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01588.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01589.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01590.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01591.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01592.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01593.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01594.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01595.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01596.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01597.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01598.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01599.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01600.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01601.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01602.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01603.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01604.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01605.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01606.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01607.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01608.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01609.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01610.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01611.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01612.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01613.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01614.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01615.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01616.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01617.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01618.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01619.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01620.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01621.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01622.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01623.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01624.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01625.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01626.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01627.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01628.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01629.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01630.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01631.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01632.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01633.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01634.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01635.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01636.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01637.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01638.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01639.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01640.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01641.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01642.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01643.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01644.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01645.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01646.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01647.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01648.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01649.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01650.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01651.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01652.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01653.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01654.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01655.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01656.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01657.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01658.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01659.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01660.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01661.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01662.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01663.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01664.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01665.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01666.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01667.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01668.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01669.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01670.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01671.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01672.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01673.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01674.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01675.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01676.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01677.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01678.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01679.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01680.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01681.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01682.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01683.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01684.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01685.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01686.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01687.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01688.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01689.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01690.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01691.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01692.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01693.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01694.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01695.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01696.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01697.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01698.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01699.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01700.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01701.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01702.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01703.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01704.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01705.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01706.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01707.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01708.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01709.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01710.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01711.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01712.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01713.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01714.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01715.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01716.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01717.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01718.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01719.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01720.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01721.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01722.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01723.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01724.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01725.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01726.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01727.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01728.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01729.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01730.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01731.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01732.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01733.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01734.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01735.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01736.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01737.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01738.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01739.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01740.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01741.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01742.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01743.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01744.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01745.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01746.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01747.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01748.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01749.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01750.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01751.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01752.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01753.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01754.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01755.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01756.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01757.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01758.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01759.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01760.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01761.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01762.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01763.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01764.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01765.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01766.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01767.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01768.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01769.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01770.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01771.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01772.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01773.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01774.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01775.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01776.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01777.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01778.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01779.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01780.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01781.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01782.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01783.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01784.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01785.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01786.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01787.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01788.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01789.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01790.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01791.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01792.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01793.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01794.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01795.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01796.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01797.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01798.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01799.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01800.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01801.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01802.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01803.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01804.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01805.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01806.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01807.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01808.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01809.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01810.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01811.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01812.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01813.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01814.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01815.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01816.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01817.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01818.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01819.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01820.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01821.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01822.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01823.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01824.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01825.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01826.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01827.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01828.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01829.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01830.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01831.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01832.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01833.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01834.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01835.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01836.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01837.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01838.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01839.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01840.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01841.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01842.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01843.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01844.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01845.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01846.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01847.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01848.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01849.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01850.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01851.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01852.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01853.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01854.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01855.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01856.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01857.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01858.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01859.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01860.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01861.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01862.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01863.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01864.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01865.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01866.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01867.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01868.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01869.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01870.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01871.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01872.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01873.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01874.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01875.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01876.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01877.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01878.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01879.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01880.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01881.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01882.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01883.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01884.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01885.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01886.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01887.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01888.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01889.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01890.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01891.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01892.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01893.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01894.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01895.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01896.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01897.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01898.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01899.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01900.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01901.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01902.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01903.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01904.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01905.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01906.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01907.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01908.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01909.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01910.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01911.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01912.stmt create mode 100644 third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01913.stmt create mode 100644 third_party/opa/v1/ast/transform.go create mode 100644 third_party/opa/v1/ast/transform_test.go create mode 100644 third_party/opa/v1/ast/unify.go create mode 100644 third_party/opa/v1/ast/unify_test.go create mode 100644 third_party/opa/v1/ast/varset.go create mode 100644 third_party/opa/v1/ast/version_index.json create mode 100644 third_party/opa/v1/ast/visit.go create mode 100644 third_party/opa/v1/ast/visit_bench_test.go create mode 100644 third_party/opa/v1/ast/visit_test.go create mode 100644 third_party/opa/v1/bundle/bundle.go create mode 100644 third_party/opa/v1/bundle/bundle_ext_test.go create mode 100644 third_party/opa/v1/bundle/bundle_test.go create mode 100644 third_party/opa/v1/bundle/file.go create mode 100644 third_party/opa/v1/bundle/file_bench_test.go create mode 100644 third_party/opa/v1/bundle/file_test.go create mode 100644 third_party/opa/v1/bundle/filefs.go create mode 100644 third_party/opa/v1/bundle/filefs_test.go create mode 100644 third_party/opa/v1/bundle/hash.go create mode 100644 third_party/opa/v1/bundle/hash_test.go create mode 100644 third_party/opa/v1/bundle/keys.go create mode 100644 third_party/opa/v1/bundle/keys_test.go create mode 100644 third_party/opa/v1/bundle/sign.go create mode 100644 third_party/opa/v1/bundle/sign_test.go create mode 100644 third_party/opa/v1/bundle/store.go create mode 100644 third_party/opa/v1/bundle/store_test.go create mode 100644 third_party/opa/v1/bundle/verify.go create mode 100644 third_party/opa/v1/bundle/verify_test.go create mode 100644 third_party/opa/v1/capabilities/capabilities.go create mode 100644 third_party/opa/v1/capabilities/capabilities_test.go create mode 100644 third_party/opa/v1/compile/compile.go create mode 100644 third_party/opa/v1/compile/compile_bench_test.go create mode 100644 third_party/opa/v1/compile/compile_test.go create mode 100644 third_party/opa/v1/config/config.go create mode 100644 third_party/opa/v1/config/config_test.go create mode 100644 third_party/opa/v1/cover/cover.go create mode 100644 third_party/opa/v1/cover/cover_bench_test.go create mode 100644 third_party/opa/v1/cover/cover_test.go create mode 100644 third_party/opa/v1/debug/README.md create mode 100644 third_party/opa/v1/debug/breakpoint.go create mode 100644 third_party/opa/v1/debug/debugger.go create mode 100644 third_party/opa/v1/debug/debugger_test.go create mode 100644 third_party/opa/v1/debug/event.go create mode 100644 third_party/opa/v1/debug/frame.go create mode 100644 third_party/opa/v1/debug/latch.go create mode 100644 third_party/opa/v1/debug/thread.go create mode 100644 third_party/opa/v1/debug/trace.go create mode 100644 third_party/opa/v1/debug/variable.go create mode 100644 third_party/opa/v1/dependencies/deps.go create mode 100644 third_party/opa/v1/dependencies/deps_bench_test.go create mode 100644 third_party/opa/v1/dependencies/deps_test.go create mode 100644 third_party/opa/v1/dependencies/doc.go create mode 100644 third_party/opa/v1/doc.go create mode 100644 third_party/opa/v1/download/config.go create mode 100644 third_party/opa/v1/download/config_test.go create mode 100644 third_party/opa/v1/download/download.go create mode 100644 third_party/opa/v1/download/download_test.go create mode 100644 third_party/opa/v1/download/oci_download.go create mode 100644 third_party/opa/v1/download/oci_download_test.go create mode 100644 third_party/opa/v1/download/oci_download_unavailable.go create mode 100644 third_party/opa/v1/download/oci_downloader.go create mode 100644 third_party/opa/v1/download/testdata/config.layer create mode 100644 third_party/opa/v1/download/testdata/latest.manifest create mode 100644 third_party/opa/v1/download/testdata/latest.tar.gz create mode 100644 third_party/opa/v1/download/testdata/latest_bundle_data/.manifest create mode 100644 third_party/opa/v1/download/testdata/latest_bundle_data/data.json create mode 100644 third_party/opa/v1/download/testdata/rego_v1.manifest create mode 100644 third_party/opa/v1/download/testdata/rego_v1.tar.gz create mode 100644 third_party/opa/v1/download/testdata/rego_v1_bundle_data/a/b/c/data.json create mode 100644 third_party/opa/v1/download/testdata/rego_v1_bundle_data/http/policy/policy.rego create mode 100644 third_party/opa/v1/download/testdata/signed.manifest create mode 100644 third_party/opa/v1/download/testdata/signed.tar.gz create mode 100644 third_party/opa/v1/download/testdata/signed_bundle_data/a/b/c/data.json create mode 100644 third_party/opa/v1/download/testdata/signed_bundle_data/http/policy/policy.rego create mode 100644 third_party/opa/v1/download/testharness.go create mode 100644 third_party/opa/v1/features/tracing/tracing.go create mode 100644 third_party/opa/v1/features/wasm/wasm.go create mode 100644 third_party/opa/v1/format/format.go create mode 100644 third_party/opa/v1/format/format_test.go create mode 100644 third_party/opa/v1/format/testdata/bench.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test.rego.error create mode 100644 third_party/opa/v1/format/testfiles/v0/test.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_assignments.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_assignments.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_contains.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_contains.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_contains.rego.formatted_no_keywords_in_refs create mode 100644 third_party/opa/v1/format/testfiles/v0/test_contains_if.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_contains_if.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_end_of_rule_comment.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_end_of_rule_comment.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_every.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_every.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_every_with_key.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_every_with_key.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_fun_args_with_linebreaks.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_fun_args_with_linebreaks.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_functions.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_functions.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_if.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_if.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_if.rego.formatted_no_keywords_in_refs create mode 100644 third_party/opa/v1/format/testfiles/v0/test_if_else.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_if_else.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_if_else.rego.formatted_no_keywords_in_refs create mode 100644 third_party/opa/v1/format/testfiles/v0/test_in.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_in.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_in.rego.formatted_no_keywords_in_refs create mode 100644 third_party/opa/v1/format/testfiles/v0/test_in_operator_with_all_keywords_import.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_in_operator_with_all_keywords_import.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_in_operator_with_parenthesis.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_in_operator_with_parenthesis.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_in_operator_without_import.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_in_operator_without_import.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_1560.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_1560.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_2299.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_2299.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_2420.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_2420.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_3836.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_3836.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_3849.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_3849.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_4606.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_4606.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5348.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5348.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5449.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5449.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_contains_ref_rule.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_contains_ref_rule.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_ref_rule.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_ref_rule.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_comprehension.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_comprehension.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_ref.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_ref.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5798.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_5798.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_6161.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_6161.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_6330.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_6330.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_6330_1.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_issue_6330_1.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs.rego.formatted_no_keywords_in_refs create mode 100644 third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs_keep_brackets.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs_keep_brackets.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_ref_heads.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_ref_heads.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_rego_v1.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_rego_v1.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_unicode.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_unicode.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0/test_with.rego create mode 100644 third_party/opa/v1/format/testfiles/v0/test_with.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/constants.rego create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/constants.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/deprecated_builtins.rego create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/deprecated_builtins.rego.error create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/duplicate_imports.rego create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/duplicate_imports.rego.error create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/functions.rego create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/functions.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/keyword_errors.rego create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/keyword_errors.rego.error create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/keywords.rego create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/keywords.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/multi_value.rego create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/multi_value.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/multi_value_no_future_imports.rego create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/multi_value_no_future_imports.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/shadowing.rego create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/shadowing.rego.error create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/single_value.rego create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/single_value.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/single_value_no_future_imports.rego create mode 100644 third_party/opa/v1/format/testfiles/v0_to_v1/single_value_no_future_imports.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test.rego.error create mode 100644 third_party/opa/v1/format/testfiles/v1/test.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_assignments.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_assignments.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_contains.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_contains.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_contains_if.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_contains_if.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_else_strings.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_else_strings.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_end_of_rule_comment.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_end_of_rule_comment.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_every.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_every.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_every_with_key.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_every_with_key.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_fun_args_with_linebreaks.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_fun_args_with_linebreaks.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_functions.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_functions.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_future_kw_import.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_future_kw_import.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_grouping.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_grouping.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_if.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_if.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_if_else.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_if_else.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_in.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_in.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_in_operator_with_all_keywords_import.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_in_operator_with_all_keywords_import.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_in_operator_with_parenthesis.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_in_operator_with_parenthesis.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_in_operator_without_import.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_in_operator_without_import.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_1560.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_1560.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_2299.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_2299.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_2420.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_2420.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_3836.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_3836.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_3849.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_3849.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_4606.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_4606.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5348.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5348.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5449.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5449.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_contains_ref_rule.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_contains_ref_rule.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_ref_rule.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_ref_rule.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_comprehension.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_comprehension.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_ref.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_ref.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5798.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_5798.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_6161.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_6161.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_6330.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_6330.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_6330_1.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_issue_6330_1.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs.rego.formatted_no_keywords_in_refs create mode 100644 third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs_keep_brackets.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs_keep_brackets.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_ref_heads.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_ref_heads.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_rego_v1.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_rego_v1.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_unicode.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_unicode.rego.formatted create mode 100644 third_party/opa/v1/format/testfiles/v1/test_with.rego create mode 100644 third_party/opa/v1/format/testfiles/v1/test_with.rego.formatted create mode 100644 third_party/opa/v1/hooks/hooks.go create mode 100644 third_party/opa/v1/ir/encoding/encoding_test.go create mode 100644 third_party/opa/v1/ir/ir.go create mode 100644 third_party/opa/v1/ir/marshal.go create mode 100644 third_party/opa/v1/ir/pretty.go create mode 100644 third_party/opa/v1/ir/walk.go create mode 100644 third_party/opa/v1/keys/keys.go create mode 100644 third_party/opa/v1/keys/keys_test.go create mode 100644 third_party/opa/v1/loader/errors.go create mode 100644 third_party/opa/v1/loader/extension/extension.go create mode 100644 third_party/opa/v1/loader/extension/extension_test.go create mode 100644 third_party/opa/v1/loader/filter/filter.go create mode 100644 third_party/opa/v1/loader/loader.go create mode 100644 third_party/opa/v1/loader/loader_test.go create mode 100644 third_party/opa/v1/loader/testdata/embedtest/bar/bar.rego create mode 100644 third_party/opa/v1/loader/testdata/embedtest/bar/bar.yaml create mode 100644 third_party/opa/v1/loader/testdata/embedtest/baz/qux/qux.json create mode 100644 third_party/opa/v1/loader/testdata/embedtest/foo.json create mode 100644 third_party/opa/v1/logging/logging.go create mode 100644 third_party/opa/v1/logging/logging_test.go create mode 100644 third_party/opa/v1/logging/test/test.go create mode 100644 third_party/opa/v1/metrics/metrics.go create mode 100644 third_party/opa/v1/metrics/metrics_bench_test.go create mode 100644 third_party/opa/v1/metrics/metrics_test.go create mode 100644 third_party/opa/v1/plugins/bundle/config.go create mode 100644 third_party/opa/v1/plugins/bundle/config_test.go create mode 100644 third_party/opa/v1/plugins/bundle/errors.go create mode 100644 third_party/opa/v1/plugins/bundle/errors_test.go create mode 100644 third_party/opa/v1/plugins/bundle/plugin.go create mode 100644 third_party/opa/v1/plugins/bundle/plugin_test.go create mode 100644 third_party/opa/v1/plugins/bundle/status.go create mode 100644 third_party/opa/v1/plugins/discovery/config.go create mode 100644 third_party/opa/v1/plugins/discovery/config_test.go create mode 100644 third_party/opa/v1/plugins/discovery/discovery.go create mode 100644 third_party/opa/v1/plugins/discovery/discovery_test.go create mode 100644 third_party/opa/v1/plugins/logs/README.md create mode 100644 third_party/opa/v1/plugins/logs/buffer.go create mode 100644 third_party/opa/v1/plugins/logs/buffer_test.go create mode 100644 third_party/opa/v1/plugins/logs/encoder.go create mode 100644 third_party/opa/v1/plugins/logs/encoder_test.go create mode 100644 third_party/opa/v1/plugins/logs/eventBuffer.go create mode 100644 third_party/opa/v1/plugins/logs/eventBuffer_test.go create mode 100644 third_party/opa/v1/plugins/logs/mask.go create mode 100644 third_party/opa/v1/plugins/logs/mask_test.go create mode 100644 third_party/opa/v1/plugins/logs/plugin.go create mode 100644 third_party/opa/v1/plugins/logs/plugin_benchmark_test.go create mode 100644 third_party/opa/v1/plugins/logs/plugin_test.go create mode 100644 third_party/opa/v1/plugins/logs/status/status.go create mode 100644 third_party/opa/v1/plugins/plugins.go create mode 100644 third_party/opa/v1/plugins/plugins_test.go create mode 100644 third_party/opa/v1/plugins/rest/auth.go create mode 100644 third_party/opa/v1/plugins/rest/auth_test.go create mode 100644 third_party/opa/v1/plugins/rest/aws.go create mode 100644 third_party/opa/v1/plugins/rest/aws_test.go create mode 100644 third_party/opa/v1/plugins/rest/azure.go create mode 100644 third_party/opa/v1/plugins/rest/azure_test.go create mode 100644 third_party/opa/v1/plugins/rest/gcp.go create mode 100644 third_party/opa/v1/plugins/rest/gcp_test.go create mode 100644 third_party/opa/v1/plugins/rest/rest.go create mode 100644 third_party/opa/v1/plugins/rest/rest_test.go create mode 100644 third_party/opa/v1/plugins/server/decoding/config.go create mode 100644 third_party/opa/v1/plugins/server/decoding/config_test.go create mode 100644 third_party/opa/v1/plugins/server/encoding/config.go create mode 100644 third_party/opa/v1/plugins/server/encoding/config_test.go create mode 100644 third_party/opa/v1/plugins/server/metrics/config.go create mode 100644 third_party/opa/v1/plugins/server/metrics/config_test.go create mode 100644 third_party/opa/v1/plugins/status/metrics.go create mode 100644 third_party/opa/v1/plugins/status/plugin.go create mode 100644 third_party/opa/v1/plugins/status/plugin_test.go create mode 100644 third_party/opa/v1/profiler/profiler.go create mode 100644 third_party/opa/v1/profiler/profiler_bench_test.go create mode 100644 third_party/opa/v1/profiler/profiler_test.go create mode 100644 third_party/opa/v1/refactor/refactor.go create mode 100644 third_party/opa/v1/refactor/refactor_test.go create mode 100644 third_party/opa/v1/rego/errors.go create mode 100644 third_party/opa/v1/rego/example_test.go create mode 100644 third_party/opa/v1/rego/plugins.go create mode 100644 third_party/opa/v1/rego/plugins_test.go create mode 100644 third_party/opa/v1/rego/prepare_test.go create mode 100644 third_party/opa/v1/rego/rego.go create mode 100644 third_party/opa/v1/rego/rego_bench_test.go create mode 100644 third_party/opa/v1/rego/rego_test.go create mode 100644 third_party/opa/v1/rego/rego_wasmtarget_test.go create mode 100644 third_party/opa/v1/rego/resultset.go create mode 100644 third_party/opa/v1/rego/resultset_test.go create mode 100644 third_party/opa/v1/rego/testdata/aci/api.rego create mode 100644 third_party/opa/v1/rego/testdata/aci/data.json create mode 100644 third_party/opa/v1/rego/testdata/aci/framework.rego create mode 100644 third_party/opa/v1/rego/testdata/aci/input.json create mode 100644 third_party/opa/v1/rego/testdata/aci/policy.rego create mode 100644 third_party/opa/v1/rego/testdata/ast.json create mode 100644 third_party/opa/v1/repl/errors.go create mode 100644 third_party/opa/v1/repl/example_test.go create mode 100644 third_party/opa/v1/repl/repl.go create mode 100644 third_party/opa/v1/repl/repl_test.go create mode 100644 third_party/opa/v1/repl/repl_wasmtarget_test.go create mode 100644 third_party/opa/v1/resolver/interface.go create mode 100644 third_party/opa/v1/resolver/wasm/wasm.go create mode 100644 third_party/opa/v1/runtime/check_user_linux.go create mode 100644 third_party/opa/v1/runtime/check_user_unix.go create mode 100644 third_party/opa/v1/runtime/check_user_windows.go create mode 100644 third_party/opa/v1/runtime/doc.go create mode 100644 third_party/opa/v1/runtime/logging.go create mode 100644 third_party/opa/v1/runtime/logging_test.go create mode 100644 third_party/opa/v1/runtime/plugins_test.go create mode 100644 third_party/opa/v1/runtime/runtime.go create mode 100644 third_party/opa/v1/runtime/runtime_test.go create mode 100644 third_party/opa/v1/schemas/authorizationPolicy.json create mode 100644 third_party/opa/v1/schemas/schemas.go create mode 100644 third_party/opa/v1/schemas/schemas_test.go create mode 100644 third_party/opa/v1/sdk/RawMapper.go create mode 100644 third_party/opa/v1/sdk/opa.go create mode 100644 third_party/opa/v1/sdk/opa_internal_test.go create mode 100644 third_party/opa/v1/sdk/opa_test.go create mode 100644 third_party/opa/v1/sdk/options.go create mode 100644 third_party/opa/v1/sdk/test/test.go create mode 100644 third_party/opa/v1/sdk/testdata/Makefile create mode 100644 third_party/opa/v1/sdk/testdata/bundle/data.json create mode 100644 third_party/opa/v1/sdk/testdata/disco.tar.gz create mode 100644 third_party/opa/v1/sdk/testdata/v1bundle.tar.gz create mode 100644 third_party/opa/v1/sdk/testdata/v1bundle/.manifest create mode 100644 third_party/opa/v1/sdk/testdata/v1bundle/policy.rego create mode 100644 third_party/opa/v1/server/authorizer/authorizer.go create mode 100644 third_party/opa/v1/server/authorizer/authorizer_test.go create mode 100644 third_party/opa/v1/server/buffer.go create mode 100644 third_party/opa/v1/server/cache.go create mode 100644 third_party/opa/v1/server/cache_test.go create mode 100644 third_party/opa/v1/server/certs.go create mode 100644 third_party/opa/v1/server/doc.go create mode 100644 third_party/opa/v1/server/features.go create mode 100644 third_party/opa/v1/server/handlers/compress.go create mode 100644 third_party/opa/v1/server/handlers/compress_test.go create mode 100644 third_party/opa/v1/server/handlers/decoding.go create mode 100644 third_party/opa/v1/server/handlers/handlers.go create mode 100644 third_party/opa/v1/server/identifier/certs.go create mode 100644 third_party/opa/v1/server/identifier/identifier.go create mode 100644 third_party/opa/v1/server/identifier/mock_test.go create mode 100644 third_party/opa/v1/server/identifier/testdata/.gitignore create mode 100755 third_party/opa/v1/server/identifier/testdata/gencerts.sh create mode 100644 third_party/opa/v1/server/identifier/tls.go create mode 100644 third_party/opa/v1/server/identifier/tls_test.go create mode 100644 third_party/opa/v1/server/identifier/token.go create mode 100644 third_party/opa/v1/server/identifier/token_test.go create mode 100644 third_party/opa/v1/server/server.go create mode 100644 third_party/opa/v1/server/server_bench_test.go create mode 100644 third_party/opa/v1/server/server_test.go create mode 100644 third_party/opa/v1/server/types/types.go create mode 100644 third_party/opa/v1/server/writer/writer.go create mode 100644 third_party/opa/v1/storage/disk/config.go create mode 100644 third_party/opa/v1/storage/disk/config_test.go create mode 100644 third_party/opa/v1/storage/disk/disk.go create mode 100644 third_party/opa/v1/storage/disk/disk_test.go create mode 100644 third_party/opa/v1/storage/disk/errors.go create mode 100644 third_party/opa/v1/storage/disk/example_test.go create mode 100644 third_party/opa/v1/storage/disk/metrics.go create mode 100644 third_party/opa/v1/storage/disk/partition.go create mode 100644 third_party/opa/v1/storage/disk/partition_test.go create mode 100644 third_party/opa/v1/storage/disk/paths.go create mode 100644 third_party/opa/v1/storage/disk/paths_test.go create mode 100644 third_party/opa/v1/storage/disk/txn.go create mode 100644 third_party/opa/v1/storage/disk/txn_test.go create mode 100644 third_party/opa/v1/storage/doc.go create mode 100644 third_party/opa/v1/storage/errors.go create mode 100644 third_party/opa/v1/storage/errors_test.go create mode 100644 third_party/opa/v1/storage/inmem/ast.go create mode 100644 third_party/opa/v1/storage/inmem/ast_test.go create mode 100644 third_party/opa/v1/storage/inmem/example_test.go create mode 100644 third_party/opa/v1/storage/inmem/inmem.go create mode 100644 third_party/opa/v1/storage/inmem/inmem_test.go create mode 100644 third_party/opa/v1/storage/inmem/opts.go create mode 100644 third_party/opa/v1/storage/inmem/test/testutil.go create mode 100644 third_party/opa/v1/storage/inmem/txn.go create mode 100644 third_party/opa/v1/storage/interface.go create mode 100644 third_party/opa/v1/storage/internal/errors/errors.go create mode 100644 third_party/opa/v1/storage/internal/errors/errors_test.go create mode 100644 third_party/opa/v1/storage/internal/ptr/ptr.go create mode 100644 third_party/opa/v1/storage/path.go create mode 100644 third_party/opa/v1/storage/path_test.go create mode 100644 third_party/opa/v1/storage/storage.go create mode 100644 third_party/opa/v1/storage/storage_test.go create mode 100644 third_party/opa/v1/test/authz/authz_bench_test.go create mode 100644 third_party/opa/v1/test/authz/authz_test.go create mode 100644 third_party/opa/v1/test/authz/testing.go create mode 100644 third_party/opa/v1/test/cases/cases.go create mode 100644 third_party/opa/v1/test/cases/internal/fmtcases/main.go create mode 100644 third_party/opa/v1/test/cases/internal/keywordrefs/main.go create mode 100644 third_party/opa/v1/test/cases/internal/keywordrefs/test-keywords-in-ref_v0.yaml.template create mode 100644 third_party/opa/v1/test/cases/internal/keywordrefs/test-keywords-in-ref_v1.yaml.template create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0002.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0003.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0004.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0005.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0006.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0007.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0008.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0009.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0010.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0011.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0012.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0013.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0014.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0015.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0016.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0017.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0018.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0019.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0020.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0021.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0022.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0023.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0024.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0025.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0026.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0027.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0028.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-bad-utf8-runes.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/aggregates/test-membership.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/all/test-all-0027.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/all/test-all-0028.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/all/test-all-0029.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/all/test-all-0030.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/all/test-all-0031.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/all/test-all-0032.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/all/test-all-0033.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/any/test-any-0034.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/any/test-any-0035.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/any/test-any-0036.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/any/test-any-0037.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/any/test-any-0038.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/any/test-any-0039.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/any/test-any-0040.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0810.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0811.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0812.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0813.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0814.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0815.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0816.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0817.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0818.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0819.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0820.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0821.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0822.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0823.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0824.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0825.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-ll-overflow.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-minus-type-error.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-big-int-0001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/array/test-array-0041.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/array/test-array-0042.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/array/test-array-0043.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/array/test-array-0044.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/array/test-array-0045.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/array/test-array-0046.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/array/test-array-0047.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/array/test-array-0048.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/array/test-array-0049.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/array/test-array-0050.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/array/test-array-0051.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/array/test-array-0052.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/assignments/test-file-level-assignments.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0929.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0930.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0931.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0932.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0933.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0934.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0935.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/base64urlbuiltins/test-base64urlbuiltins-0935.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/base64urlbuiltins/test-base64urlbuiltins-0937.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/base64urlbuiltins/test-base64urlbuiltins-0939.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0695.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0696.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0697.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0698.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0699.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0700.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0701.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0702.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0703.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0704.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0705.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsand/test-bitsand-0055.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsand/test-bitsand-0056.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsand/test-bitsand-0057.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsnegate/test-bitsnegate-0058.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsnegate/test-bitsnegate-0059.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsor/test-bitsor-0052.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsor/test-bitsor-0053.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsor/test-bitsor-0054.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0063.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0064.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0065.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0066.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0067.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsshiftright/test-bitsshiftright-0068.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsshiftright/test-bitsshiftright-0069.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsshiftright/test-bitsshiftright-0070.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsxor/test-bitsxor-0060.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsxor/test-bitsxor-0061.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/bitsxor/test-bitsxor-0062.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0077.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0078.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0079.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0080.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0081.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0082.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0083.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0824.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0825.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0826.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0827.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0608.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0609.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0610.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0611.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0612.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0613.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0614.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0615.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0616.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0617.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0618.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0619.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0620.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0495.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0496.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0497.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0498.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0499.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0500.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0501.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0502.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0503.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0504.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0505.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0506.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0507.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0508.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0509.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0510.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositebasedereference/test-compositebasedereference-1073.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositebasedereference/test-compositebasedereference-1074.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositebasedereference/test-compositebasedereference-1075.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0743.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0744.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0745.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0746.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0747.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0748.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0749.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0750.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0751.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0752.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0753.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0754.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0755.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0756.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0757.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0781.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0782.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0783.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0784.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0785.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0786.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0787.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0788.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0789.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0790.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0791.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0792.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0793.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0794.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0795.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0796.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0797.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0798.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0799.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0800.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0801.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0802.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0803.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-and-vars.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/containskeyword/test-contains-future-keyword.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptohmacequal/test-cryptohmacequal.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptohmacmd5/test-cryptohmacmd5.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptohmacsha1/test-cryptohmacsha1.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptohmacsha256/test-cryptohmacsha256.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptohmacsha512/test-cryptohmacsha512.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptomd5/test-cryptomd5-0130.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptoparsersaprivatekeys/test-cryptoparsersaprivatekey-1.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptosha1/test-cryptosha1-0131.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptosha256/test-cryptosha256-0132.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parseandverifycertificates/test-cryptox509parseandverifycertificates.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0125.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0126.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0127.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0128.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0129.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0117.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0118.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0119.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0120.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0121.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0122.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0123.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0124.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-raw-uris.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsekeypair/test-cryptox509parsekeypairs-0118.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsekeypair/test-cryptox509parsekeypairs-0119.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/cryptox509parsersaprivatekey/test-cryptox509parsersaprivatekey-1.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/dataderef/test-data-derefs.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-default-functions.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0804.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0805.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0806.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0807.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0808.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0809.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0763.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0764.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0765.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0766.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0767.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0768.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0769.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0770.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0771.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0772.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0773.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0774.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0775.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0776.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1054.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1055.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1056.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1057.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1058.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1059.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1060.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1061.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1062.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1063.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1064.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1065.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1066.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1067.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/embeddedvirtualdoc/test-embeddedvirtualdoc-0976.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0545.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0546.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0547.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0548.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0549.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0550.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0551.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0552.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0553.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0554.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0555.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0556.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0557.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0558.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0559.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0560.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0561.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0562.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0563.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0564.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0565.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0566.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0567.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0568.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0569.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0570.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0571.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0572.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0573.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0574.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0575.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0576.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0577.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0578.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0579.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0580.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0581.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0582.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0583.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0584.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0585.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0586.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0587.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0588.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0589.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0590.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0591.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0592.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0593.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0594.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0595.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0596.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0597.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0598.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0599.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0525.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0526.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0527.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0528.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0529.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0530.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0531.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0532.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0533.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0534.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0535.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0536.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0537.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0538.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0539.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0540.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0541.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0542.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0543.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0544.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/every/every.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/every/non_iterable_domain.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/every/textbook.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/example/test-example-1070.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/example/test-example-1071.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/example/test-example-1072.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/fix1863/test-fix1863-0706.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/fix1863/test-fix1863-0707.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/fix1863/test-fix1863-0708.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-conflicts.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-1012.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-1013.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-1014.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-undefined-builtin-result.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0990.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0991.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0992.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0993.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0994.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0995.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0996.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0997.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0998.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0999.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1000.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1002.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1003.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1004.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1005.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1006.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1007.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1008.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1009.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1010.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1011.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-default.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-nested-with-early-exit.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-unused-arg.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0133.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0134.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0135.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0136.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0137.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0138.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0139.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0140.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0141.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0142.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0143.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0144.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0145.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0146.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0147.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0148.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0149.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0150.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0151.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0152.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0153.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0154.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0155.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0156.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0157.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0158.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0159.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-issue-5273.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-issue-5283.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globquotemeta/test-globquotemeta-0159.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0865.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0866.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0867.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0868.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0869.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0870.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-basic-ast.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-is-valid.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse-and-verify.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse-query.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse-schema.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-schema-is-valid.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/helloworld/test-helloworld-1.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/hexbuiltins/test-hexbuiltins-0939.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/hexbuiltins/test-hexbuiltins-0940.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/hexbuiltins/test-hexbuiltins-0941.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/indexing/array-any.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0758.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0759.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0760.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0761.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0762.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0977.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0978.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0979.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0980.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0981.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0982.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0983.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0352.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0353.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0354.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0355.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0356.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/invalidkeyerror/test-invalidkeyerror-0176.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/invalidkeyerror/test-invalidkeyerror-0177.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-is-valid.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-json-marshal-with-options.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0924.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0925.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0926.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0927.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0928.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-marshal-large-ints.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0218.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0219.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0220.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0221.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0222.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0223.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0224.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0225.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0226.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0227.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0228.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonfilteridempotent/test-jsonfilteridempotent-0229.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonpatch/coverage.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonpatch/json-patch-tests.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonpatch/set.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0230.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0231.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0232.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0233.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0234.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0235.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0236.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0237.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0238.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0239.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0240.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0241.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0242.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0243.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0244.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0245.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0246.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0247.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0248.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0249.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0250.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0251.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0252.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0253.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0254.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonremoveidempotent/test-jsonremoveidempotent-0255.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonschema/test-json-match_schema.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jsonschema/test-json-verify_schema.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0389.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0390.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0391.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0392.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0393.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0394.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0395.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0396.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0397.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0398.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0399.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0400.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0449.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0450.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0451.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0452.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0453.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0454.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0455.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0456.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0457.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0458.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0459.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0460.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0461.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0462.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0463.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0464.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0465.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0466.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0467.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0468.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0469.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0470.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0471.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0472.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0473.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0474.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0475.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0476.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0477.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0478.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0479.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0480.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0481.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0482.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0483.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0484.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0485.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0486.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0487.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0488.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0489.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0490.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0491.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-invalid-exp-type.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-invalid-nbf-type.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-missing-iss-while-required.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-0492.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-0493.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-0494.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-integer-timestamps.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-set-data.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0379.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0380.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0381.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0382.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0383.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0376.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0377.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0378.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0384.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0385.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0386.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0387.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0388.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs256/test-jwtverifyhs256-0440.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs256/test-jwtverifyhs256-0441.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs256/test-jwtverifyhs256-0442.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs384/test-jwtverifyhs384-0443.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs384/test-jwtverifyhs384-0444.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs384/test-jwtverifyhs384-0445.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs512/test-jwtverifyhs512-0446.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs512/test-jwtverifyhs512-0447.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs512/test-jwtverifyhs512-0448.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0401.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0402.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0403.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0404.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0405.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0406.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0407.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0408.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0409.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0410.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0411.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0412.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0413.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0414.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0415.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0416.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0417.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0418.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0419.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0420.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0421.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0422.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0423.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0424.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0425.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0426.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0427.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0428.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0429.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0430.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0431.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0432.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0433.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0434.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0435.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0436.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0437.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0438.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0439.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-as.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-default.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-else.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-false.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-import.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-not.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-null.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-package.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-some.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-true.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-with.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0777.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0778.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0779.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0780.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-data-ref-with-var.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0709.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0710.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0711.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0712.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0713.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0714.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0715.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0716.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0717.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0718.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0719.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0720.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0721.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0722.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0723.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0724.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0725.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0092.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0093.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0094.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0095.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0096.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0097.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0098.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0099.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0100.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0101.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0102.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0103.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0104.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0105.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0106.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0107.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0108.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0109.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0110.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0111.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0112.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0113.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0114.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0115.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0116.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0086.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0087.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0088.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0089.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0090.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0091.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrisvalid/test_netcidrisvalid-0001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrmerge/test-ipv6-with-and-without-prefix.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidrmerge/test-netcidrmerge0117.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidroverlap/test-netcidroverlap-0084.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netcidroverlap/test-netcidroverlap-0085.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/netlookupipaddr/test-netlookupipaddr.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0256.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0257.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0258.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0259.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0260.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0261.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/numbersrangestep/test-numbersrangestep.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0300.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0301.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0302.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0303.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0304.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0305.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0306.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0307.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0308.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0309.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0310.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0311.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0312.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0313.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0314.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0315.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0316.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0317.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilteridempotent/test-objectfilteridempotent-0319.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectfilternonstringkey/test-objectfilternonstringkey-0318.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0262.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0263.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0264.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0265.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0266.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0267.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-path.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectkeys/test-objectkeys.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0279.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0280.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0281.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0282.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0283.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0284.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0285.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0286.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0287.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0288.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0289.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0290.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0291.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0292.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0293.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0294.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0295.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0296.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0297.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremoveidempotent/test-objectremoveidempotent-0298.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectremovenonstringkey/test-objectremovenonstringkey-0299.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0268.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0269.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0270.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0271.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0272.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0273.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0274.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0275.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0276.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0277.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0278.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/objectunionn/test-objectunionn-0001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0984.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0985.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0986.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0987.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0988.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0989.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialiter/test-partialiter-001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0519.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0520.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0521.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0522.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0523.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0524.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-ref.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-wasm-cases.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-issue-3369.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-issue-3376.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-issue-3819.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0511.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0512.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0513.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0514.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0515.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0516.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0517.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0518.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/planner-ir/test-array-ir-unify.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/planner-ir/test-call-dynamic.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/providers-aws/aws-sign_req-errors.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/providers-aws/aws-sign_req.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/rand/test-rand.intn.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0322.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0323.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0324.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0325.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0326.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0327.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0328.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-paths-0422.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-paths-1022.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/refheads/test-generic-refs.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/refheads/test-refs-as-rule-heads.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/refheads/test-regressions.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexfind/test-regexfind-0334.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexfind/test-regexfind-0335.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexfind/test-regexfind-0336.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0337.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0338.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0339.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0340.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0341.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0342.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0343.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-large-input.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexisvalid/test-regexisvalid-0329.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexisvalid/test-regexisvalid-0330.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexisvalid/test-regexisvalid-0331.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0855.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0856.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0857.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0858.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0859.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0860.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0861.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexmatchtemplate/test-regexmatchtemplate-0332.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexmatchtemplate/test-regexmatchtemplate-0333.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexreplace/test-regexreplace-0001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexsplit/test-regexsplit-0862.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexsplit/test-regexsplit-0863.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regexsplit/test-regexsplit-0864.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regometadatachain/test-regometadatachain-1.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regometadatarule/test-regometadatarule-1.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regoparsemodule/test-regoparsemodule-0320.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/regoparsemodule/test-regoparsemodule-0321.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/rendertemplate/rendertemplate.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/replacen/test-replacen-0374.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/replacen/test-replacen-0375.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/replacen/test-replacen-bad-operands.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0344.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0345.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0346.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0347.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0348.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/semverisvalid/test-semverisvalid-0349.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/semverisvalid/test-semverisvalid-0350.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/semverisvalid/test-semverisvalid-0351.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0871.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0872.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0873.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0874.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0875.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0876.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/sprintf/test-sprintf.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-anyprefixmatch.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-anysuffixmatch.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0877.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0878.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0879.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0880.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0881.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0882.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0883.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0884.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0885.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0886.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0887.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0888.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0889.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0890.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0891.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0892.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0893.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0894.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0895.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0896.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0897.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0898.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0899.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0900.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0901.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0902.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0903.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0904.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0905.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0906.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0907.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0908.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0909.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0910.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0911.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0912.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0913.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0914.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0915.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0916.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0917.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0918.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0919.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0920.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0921.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0922.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0923.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0924.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0925.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0926.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-indexof-unicode.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/subset/test-subset.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0947.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0948.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0949.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0950.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0951.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0952.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0953.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0954.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0955.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0956.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0957.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0958.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0959.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0960.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0961.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0962.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0963.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0964.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0965.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0966.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0967.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0968.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0969.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0970.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/time/test-time-0971.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/toarray/test-toarray-0071.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/toarray/test-toarray-0072.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/toarray/test-toarray-0073.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/topdowndynamicdispatch/test-topdowndynamicdispatch-1068.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/toset/test-toset-0074.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/toset/test-toset-0075.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/toset/test-toset-0076.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/trim/test-trim-0362.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/trim/test-trim-0363.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/trimleft/test-trimleft-0364.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/trimleft/test-trimleft-0365.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/trimprefix/test-trimprefix-0366.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/trimprefix/test-trimprefix-0367.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/trimright/test-trimright-0368.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/trimright/test-trimright-0369.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/trimspace/test-trimspace-0372.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/trimspace/test-trimspace-0373.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/trimsuffix/test-trimsuffix-0370.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/trimsuffix/test-trimsuffix-0371.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/type/test-regressions.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0828.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0829.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0830.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0831.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0832.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0833.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0834.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0835.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0836.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0837.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0838.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0839.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0840.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0841.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0842.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0843.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0844.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0845.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0846.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0847.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0848.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0849.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0850.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0851.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0852.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0853.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0854.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0599.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0600.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0601.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0602.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0603.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0604.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0605.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0606.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0607.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/union/test-union-0357.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/union/test-union-0358.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/union/test-union-0359.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/union/test-union-0360.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/union/test-union-0361.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/units/test-issue-4856.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/units/test-parse-bytes-comparisons.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/units/test-parse-bytes-errors.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/units/test-parse-bytes.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/units/test-parse-units-comparisons.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/units/test-parse-units-errors.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/units/test-parse-units.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/units/test-units-precision.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0939.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0940.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0941.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0942.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0943.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0944.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0945.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0946.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-1076.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/uuid/test-uuid-input-formats.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/uuid/test-uuid-parse-rule.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/uuid/test-uuid-parse.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0726.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0727.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0728.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0729.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0730.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0731.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0732.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0733.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0734.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0735.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0736.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0737.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0738.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0739.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0740.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0741.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0742.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0620.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0621.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0622.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0623.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0624.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0625.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0626.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0627.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0628.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0629.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0630.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0631.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0632.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0633.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0634.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0635.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0636.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0637.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0638.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0639.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0640.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0641.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0642.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0643.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0644.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0645.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0646.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0647.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0648.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0649.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0650.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0651.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0652.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0653.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0654.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0655.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0656.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0657.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0658.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0659.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0660.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0661.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0662.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0663.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0664.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0665.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0666.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0667.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0668.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0669.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0670.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0671.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0672.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0673.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0674.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0675.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0676.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0677.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0678.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0679.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0680.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0681.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0682.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0683.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0684.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0685.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0686.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0687.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0688.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0689.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0690.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0691.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0692.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0693.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0694.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-undefined.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0970.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0971.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0972.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0973.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0974.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0975.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-wildcard-path.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-and-ndbcache-issue.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-builtin-mock.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-function-mock.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-function-mocks-issue-5299.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1015.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1016.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1017.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1018.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1019.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1020.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1021.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1022.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1023.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1024.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1025.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1026.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1027.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1028.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1029.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1030.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1031.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1032.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1033.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1034.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1035.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1036.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1037.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1038.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1039.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1040.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1041.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1042.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1043.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1044.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1045.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1046.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1047.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1048.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1049.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1050.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1051.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1052.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1053.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1054.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0002.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0003.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0004.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0005.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0006.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0007.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0008.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0009.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0010.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0011.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0012.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0013.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0014.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0015.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0016.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0017.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0018.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0019.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0020.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0021.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0022.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0023.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0024.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0025.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0026.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0027.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0028.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-bad-utf8-runes.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/aggregates/test-membership.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0810.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0811.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0812.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0813.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0814.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0815.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0816.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0817.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0818.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0819.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0820.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0821.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0822.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0823.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0824.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0825.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-minus-type-error.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-big-int-0001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/array/test-array-0041.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/array/test-array-0042.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/array/test-array-0043.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/array/test-array-0044.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/array/test-array-0045.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/array/test-array-0046.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/array/test-array-0047.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/array/test-array-0048.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/array/test-array-0049.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/array/test-array-0050.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/array/test-array-0051.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/array/test-array-0052.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/assignments/test-file-level-assignments.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0929.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0930.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0931.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0932.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0933.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0934.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0935.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/base64urlbuiltins/test-base64urlbuiltins-0935.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/base64urlbuiltins/test-base64urlbuiltins-0937.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/base64urlbuiltins/test-base64urlbuiltins-0939.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0695.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0696.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0697.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0698.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0699.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0700.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0701.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0702.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0703.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0704.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0705.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsand/test-bitsand-0055.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsand/test-bitsand-0056.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsand/test-bitsand-0057.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsnegate/test-bitsnegate-0058.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsnegate/test-bitsnegate-0059.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsor/test-bitsor-0052.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsor/test-bitsor-0053.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsor/test-bitsor-0054.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0063.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0064.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0065.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0066.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0067.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsshiftright/test-bitsshiftright-0068.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsshiftright/test-bitsshiftright-0069.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsshiftright/test-bitsshiftright-0070.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsxor/test-bitsxor-0060.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsxor/test-bitsxor-0061.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/bitsxor/test-bitsxor-0062.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0824.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0825.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0826.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0827.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0828.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0608.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0609.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0610.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0611.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0612.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0613.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0614.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0615.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0616.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0617.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0618.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0619.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0620.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0495.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0496.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0497.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0498.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0499.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0500.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0501.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0502.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0503.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0504.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0505.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0506.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0507.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0508.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0509.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0510.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositebasedereference/test-compositebasedereference-1073.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositebasedereference/test-compositebasedereference-1074.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositebasedereference/test-compositebasedereference-1075.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0743.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0744.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0745.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0746.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0747.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0748.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0749.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0750.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0751.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0752.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0753.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0754.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0755.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0756.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0757.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0781.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0782.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0783.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0784.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0785.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0786.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0787.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0788.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0789.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0790.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0791.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0792.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0793.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0794.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0795.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0796.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0797.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0798.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0799.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0800.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0801.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0802.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0803.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-and-vars.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/containskeyword/test-contains-future-keyword.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptohmacequal/test-cryptohmacequal.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptohmacmd5/test-cryptohmacmd5.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptohmacsha1/test-cryptohmacsha1.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptohmacsha256/test-cryptohmacsha256.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptohmacsha512/test-cryptohmacsha512.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptomd5/test-cryptomd5-0130.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptoparsersaprivatekeys/test-cryptoparsersaprivatekey-1.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptosha1/test-cryptosha1-0131.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptosha256/test-cryptosha256-0132.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parseandverifycertificates/test-cryptox509parseandverifycertificates.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0125.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0126.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0127.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0128.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0129.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0117.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0118.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0119.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0120.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0121.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0122.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0123.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0124.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-raw-uris.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsekeypair/test-cryptox509parsekeypairs-0118.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsekeypair/test-cryptox509parsekeypairs-0119.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/cryptox509parsersaprivatekey/test-cryptox509parsersaprivatekey-1.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/dataderef/test-data-derefs.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-default-functions.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0804.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0805.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0806.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0807.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0808.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0809.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0763.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0764.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0765.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0766.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0767.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0768.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0769.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0770.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0771.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0772.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0773.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0774.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0775.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0776.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1054.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1055.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1056.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1057.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1058.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1059.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1060.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1061.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1062.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1063.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1064.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1065.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1066.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1067.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/embeddedvirtualdoc/test-embeddedvirtualdoc-0976.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0545.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0546.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0547.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0548.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0549.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0550.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0551.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0552.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0553.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0554.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0555.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0556.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0557.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0558.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0559.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0560.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0561.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0562.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0563.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0564.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0565.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0566.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0567.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0568.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0569.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0570.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0571.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0572.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0573.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0574.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0575.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0576.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0577.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0578.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0579.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0580.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0581.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0582.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0583.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0584.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0585.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0586.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0587.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0588.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0589.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0590.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0591.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0592.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0593.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0594.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0595.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0596.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0597.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0598.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0599.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0525.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0526.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0527.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0528.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0529.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0530.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0531.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0532.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0533.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0534.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0535.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0536.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0537.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0538.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0539.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0540.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0541.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0542.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0543.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0544.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/every/every.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/every/non_iterable_domain.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/every/textbook.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/example/test-example-1070.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/example/test-example-1071.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/example/test-example-1072.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/fix1863/test-fix1863-0706.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/fix1863/test-fix1863-0707.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/fix1863/test-fix1863-0708.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-conflicts.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-1012.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-1013.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-1014.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-undefined-builtin-result.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0990.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0991.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0992.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0993.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0994.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0995.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0996.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0997.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0998.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0999.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1000.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1002.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1003.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1004.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1005.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1006.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1007.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1008.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1009.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1010.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1011.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-default.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-nested-with-early-exit.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-unused-arg.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0133.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0134.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0135.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0136.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0137.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0138.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0139.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0140.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0141.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0142.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0143.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0144.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0145.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0146.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0147.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0148.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0149.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0150.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0151.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0152.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0153.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0154.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0155.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0156.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0157.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0158.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0159.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-issue-5273.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-issue-5283.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globquotemeta/test-globquotemeta-0159.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0865.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0866.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0867.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0868.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0869.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0870.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-basic-ast.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-is-valid.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse-and-verify.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse-query.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse-schema.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-schema-is-valid.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/helloworld/test-helloworld-1.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/hexbuiltins/test-hexbuiltins-0939.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/hexbuiltins/test-hexbuiltins-0940.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/hexbuiltins/test-hexbuiltins-0941.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/indexing/array-any.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0758.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0759.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0760.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0761.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0762.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0977.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0978.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0979.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0980.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0981.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0982.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0983.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0352.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0353.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0354.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0355.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0356.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/invalidkeyerror/test-invalidkeyerror-0176.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/invalidkeyerror/test-invalidkeyerror-0177.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-is-valid.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-json-marshal-with-options.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0924.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0925.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0926.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0927.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0928.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-marshal-large-ints.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0218.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0219.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0220.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0221.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0222.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0223.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0224.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0225.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0226.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0227.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0228.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonfilteridempotent/test-jsonfilteridempotent-0229.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonpatch/coverage.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonpatch/json-patch-tests.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonpatch/set.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0230.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0231.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0232.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0233.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0234.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0235.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0236.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0237.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0238.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0239.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0240.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0241.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0242.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0243.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0244.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0245.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0246.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0247.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0248.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0249.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0250.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0251.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0252.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0253.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0254.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonremoveidempotent/test-jsonremoveidempotent-0255.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonschema/test-json-match_schema.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jsonschema/test-json-verify_schema.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0389.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0390.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0391.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0392.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0393.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0394.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0395.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0396.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0397.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0398.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0399.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0400.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0449.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0450.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0451.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0452.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0453.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0454.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0455.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0456.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0457.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0458.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0459.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0460.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0461.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0462.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0463.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0464.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0465.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0466.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0467.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0468.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0469.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0470.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0471.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0472.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0473.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0474.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0475.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0476.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0477.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0478.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0479.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0480.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0481.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0482.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0483.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0484.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0485.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0486.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0487.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0488.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0489.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0490.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0491.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-invalid-exp-type.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-invalid-nbf-type.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-missing-iss-while-required.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-0492.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-0493.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-0494.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-integer-timestamps.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-set-data.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0379.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0380.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0381.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0382.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0383.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0376.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0377.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0378.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0384.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0385.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0386.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0387.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0388.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs256/test-jwtverifyhs256-0440.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs256/test-jwtverifyhs256-0441.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs256/test-jwtverifyhs256-0442.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs384/test-jwtverifyhs384-0443.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs384/test-jwtverifyhs384-0444.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs384/test-jwtverifyhs384-0445.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs512/test-jwtverifyhs512-0446.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs512/test-jwtverifyhs512-0447.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs512/test-jwtverifyhs512-0448.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0401.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0402.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0403.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0404.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0405.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0406.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0407.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0408.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0409.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0410.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0411.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0412.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0413.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0414.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0415.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0416.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0417.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0418.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0419.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0420.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0421.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0422.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0423.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0424.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0425.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0426.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0427.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0428.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0429.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0430.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0431.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0432.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0433.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0434.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0435.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0436.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0437.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0438.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0439.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-as.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-contains.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-default.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-else.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-every.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-false.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-if.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-import.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-in.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-not.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-null.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-package.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-some.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-true.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-with.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0777.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0778.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0779.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0780.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-data-ref-with-var.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0709.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0710.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0711.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0712.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0713.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0714.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0715.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0716.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0717.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0718.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0719.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0720.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0721.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0722.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0723.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0724.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0725.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0092.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0093.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0094.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0095.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0096.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0097.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0098.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0099.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0100.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0101.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0102.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0103.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0104.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0105.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0106.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0107.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0108.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0109.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0110.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0111.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0112.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0113.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0114.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0115.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0116.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0086.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0087.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0088.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0089.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0090.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0091.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrisvalid/test_netcidrisvalid-0001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrmerge/test-ipv6-with-and-without-prefix.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netcidrmerge/test-netcidrmerge0117.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/netlookupipaddr/test-netlookupipaddr.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0256.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0257.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0258.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0259.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0260.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0261.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-issue-7269.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/numbersrangestep/test-numbersrangestep.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0300.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0301.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0302.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0303.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0304.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0305.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0306.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0307.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0308.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0309.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0310.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0311.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0312.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0313.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0314.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0315.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0316.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0317.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilteridempotent/test-objectfilteridempotent-0319.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectfilternonstringkey/test-objectfilternonstringkey-0318.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0262.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0263.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0264.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0265.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0266.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0267.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-path.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectkeys/test-objectkeys.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0279.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0280.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0281.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0282.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0283.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0284.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0285.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0286.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0287.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0288.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0289.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0290.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0291.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0292.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0293.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0294.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0295.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0296.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0297.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremoveidempotent/test-objectremoveidempotent-0298.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectremovenonstringkey/test-objectremovenonstringkey-0299.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0268.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0269.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0270.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0271.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0272.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0273.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0274.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0275.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0276.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0277.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0278.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/objectunionn/test-objectunionn-0001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0984.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0985.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0986.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0987.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0988.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0989.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialiter/test-partialiter-001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0519.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0520.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0521.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0522.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0523.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0524.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-ref.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-wasm-cases.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-issue-3369.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-issue-3376.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-issue-3819.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0511.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0512.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0513.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0514.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0515.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0516.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0517.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0518.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/planner-ir/test-array-ir-unify.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/planner-ir/test-call-dynamic.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/providers-aws/aws-sign_req-errors.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/providers-aws/aws-sign_req.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/rand/test-rand.intn.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0322.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0323.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0324.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0325.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0326.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0327.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0328.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-paths-0422.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-paths-1022.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/refheads/test-generic-refs.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/refheads/test-refs-as-rule-heads.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/refheads/test-regressions.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexfind/test-regexfind-0334.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexfind/test-regexfind-0335.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexfind/test-regexfind-0336.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0337.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0338.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0339.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0340.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0341.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0342.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0343.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-large-input.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexisvalid/test-regexisvalid-0329.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexisvalid/test-regexisvalid-0330.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexisvalid/test-regexisvalid-0331.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexmatch/test-regexmatch-0861.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexmatchtemplate/test-regexmatchtemplate-0332.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexmatchtemplate/test-regexmatchtemplate-0333.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexreplace/test-regexreplace-0001.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexsplit/test-regexsplit-0862.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexsplit/test-regexsplit-0863.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regexsplit/test-regexsplit-0864.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regometadatachain/test-regometadatachain-1.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regometadatarule/test-regometadatarule-1.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regoparsemodule/test-regoparsemodule-0320.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/regoparsemodule/test-regoparsemodule-0321.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/rendertemplate/rendertemplate.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/replacen/test-replacen-0374.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/replacen/test-replacen-0375.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/replacen/test-replacen-bad-operands.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0344.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0345.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0346.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0347.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0348.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/semverisvalid/test-semverisvalid-0349.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/semverisvalid/test-semverisvalid-0350.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/semverisvalid/test-semverisvalid-0351.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0871.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0872.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0873.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0874.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0875.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0876.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/sprintf/test-sprintf.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-anyprefixmatch.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-anysuffixmatch.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0877.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0878.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0879.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0880.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0881.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0882.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0883.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0884.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0885.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0886.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0887.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0888.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0889.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0890.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0891.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0892.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0893.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0894.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0895.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0896.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0897.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0898.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0899.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0900.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0901.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0902.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0903.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0904.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0905.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0906.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0907.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0908.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0909.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0910.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0911.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0912.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0913.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0914.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0915.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0916.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0917.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0918.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0919.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0920.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0921.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0922.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0923.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0924.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0925.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0926.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-indexof-unicode.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/subset/test-subset.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0947.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0948.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0949.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0950.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0951.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0952.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0953.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0954.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0955.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0956.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0957.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0958.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0959.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0960.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0961.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0962.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0963.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0964.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0965.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0966.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0967.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0968.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0969.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0970.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/time/test-time-0971.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/topdowndynamicdispatch/test-topdowndynamicdispatch-1068.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/trim/test-trim-0362.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/trim/test-trim-0363.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/trimleft/test-trimleft-0364.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/trimleft/test-trimleft-0365.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/trimprefix/test-trimprefix-0366.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/trimprefix/test-trimprefix-0367.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/trimright/test-trimright-0368.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/trimright/test-trimright-0369.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/trimspace/test-trimspace-0372.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/trimspace/test-trimspace-0373.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/trimsuffix/test-trimsuffix-0370.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/trimsuffix/test-trimsuffix-0371.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/type/test-regressions.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0828.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0829.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0830.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0831.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0832.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0833.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0834.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0835.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0836.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0837.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0838.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0839.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0840.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0841.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0842.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0843.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0844.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0845.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0846.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0847.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0848.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0849.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0850.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0851.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0852.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0853.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0854.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0599.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0600.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0601.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0602.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0603.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0604.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0605.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0606.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0607.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/union/test-union-0357.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/union/test-union-0358.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/union/test-union-0359.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/union/test-union-0360.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/union/test-union-0361.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/units/test-issue-4856.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/units/test-parse-bytes-comparisons.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/units/test-parse-bytes-errors.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/units/test-parse-bytes.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/units/test-parse-units-comparisons.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/units/test-parse-units-errors.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/units/test-parse-units.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/units/test-units-precision.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0939.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0940.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0941.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0942.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0943.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0944.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0945.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0946.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-1076.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/uuid/test-uuid-input-formats.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/uuid/test-uuid-parse-rule.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/uuid/test-uuid-parse.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0726.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0727.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0728.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0729.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0730.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0731.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0732.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0733.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0734.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0735.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0736.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0737.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0738.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0739.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0740.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0741.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0742.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0620.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0621.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0622.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0623.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0624.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0625.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0626.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0627.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0628.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0629.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0630.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0631.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0632.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0633.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0634.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0635.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0636.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0637.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0638.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0639.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0640.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0641.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0642.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0643.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0644.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0645.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0646.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0647.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0648.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0649.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0650.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0651.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0652.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0653.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0654.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0655.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0656.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0657.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0658.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0659.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0660.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0661.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0662.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0663.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0664.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0665.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0666.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0667.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0668.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0669.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0670.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0671.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0672.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0673.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0674.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0675.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0676.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0677.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0678.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0679.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0680.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0681.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0682.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0683.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0684.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0685.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0686.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0687.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0688.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0689.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0690.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0691.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0692.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0693.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0694.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-undefined.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0970.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0971.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0972.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0973.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0974.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0975.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-issue-7656.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-wildcard-path.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-and-ndbcache-issue.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-builtin-mock.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-function-mock.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-function-mocks-issue-5299.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1015.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1016.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1017.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1018.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1019.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1020.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1021.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1022.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1023.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1024.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1025.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1026.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1027.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1028.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1029.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1030.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1031.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1032.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1033.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1034.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1035.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1036.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1037.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1038.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1039.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1040.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1041.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1042.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1043.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1044.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1045.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1046.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1047.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1048.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1049.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1050.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1051.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1052.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1053.yaml create mode 100644 third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1054.yaml create mode 100644 third_party/opa/v1/test/cli/smoke/.gitignore create mode 100644 third_party/opa/v1/test/cli/smoke/.manifest create mode 100644 third_party/opa/v1/test/cli/smoke/data.yaml create mode 100644 third_party/opa/v1/test/cli/smoke/golden-bundle.tar.gz create mode 100644 third_party/opa/v1/test/cli/smoke/input.json create mode 100644 third_party/opa/v1/test/cli/smoke/namespace/data.json create mode 100644 third_party/opa/v1/test/cli/smoke/test.rego create mode 100644 third_party/opa/v1/test/e2e/authz/authz_bench_integration_test.go create mode 100644 third_party/opa/v1/test/e2e/authz/disk.go create mode 100644 third_party/opa/v1/test/e2e/authz/nodisk.go create mode 100644 third_party/opa/v1/test/e2e/certrefresh/certrefresh_test.go create mode 100644 third_party/opa/v1/test/e2e/certrefresh/testdata/.gitignore create mode 100755 third_party/opa/v1/test/e2e/certrefresh/testdata/gencerts.sh create mode 100644 third_party/opa/v1/test/e2e/concurrency/concurrency_test.go create mode 100644 third_party/opa/v1/test/e2e/diagnostics/diagnostics_test.go create mode 100644 third_party/opa/v1/test/e2e/distributedtracing/distributedtracing_test.go create mode 100644 third_party/opa/v1/test/e2e/h2c/h2c_test.go create mode 100644 third_party/opa/v1/test/e2e/http/http_test.go create mode 100644 third_party/opa/v1/test/e2e/logs/console/console_decision_logger_benchmark_test.go create mode 100644 third_party/opa/v1/test/e2e/logs/console/console_decision_logger_test.go create mode 100644 third_party/opa/v1/test/e2e/logs/remote/remote_decision_logger_benchmark_test.go create mode 100644 third_party/opa/v1/test/e2e/logs/utils.go create mode 100644 third_party/opa/v1/test/e2e/metrics/metrics_test.go create mode 100644 third_party/opa/v1/test/e2e/oci/oci_test.go create mode 100644 third_party/opa/v1/test/e2e/print/print_test.go create mode 100644 third_party/opa/v1/test/e2e/shutdown/shutdown_test.go create mode 100644 third_party/opa/v1/test/e2e/testing.go create mode 100644 third_party/opa/v1/test/e2e/tls/testdata/.gitignore create mode 100755 third_party/opa/v1/test/e2e/tls/testdata/gencerts.sh create mode 100644 third_party/opa/v1/test/e2e/tls/tls_test.go create mode 100644 third_party/opa/v1/test/e2e/wasm/authz/authz_bench_integration_test.go create mode 100644 third_party/opa/v1/test/e2e/wasm/authz/disk.go create mode 100644 third_party/opa/v1/test/e2e/wasm/authz/nodisk.go create mode 100644 third_party/opa/v1/test/scheduler/scheduler_bench_test.go create mode 100644 third_party/opa/v1/test/scheduler/scheduler_test.go create mode 100644 third_party/opa/v1/test/scheduler/testdata/data_10nodes_30pods.json create mode 100644 third_party/opa/v1/test/wasm/assets/001_eq.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/002_iteration.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/003_comparison.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/004_negation.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/005_references.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/006_pattern_matching.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/007_complete.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/008_functions.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/009_default.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/010_else.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/011_partialsets.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/012_partialobjects.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/013_virtual.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/014_comprehensions.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/015_results.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/016_with.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/017_strings.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/018_builtins.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/019_call_indirect_optimization.yaml create mode 100644 third_party/opa/v1/test/wasm/assets/test.js create mode 100644 third_party/opa/v1/test/wasm/cmd/wasm-rego-testgen/main.go create mode 100644 third_party/opa/v1/tester/fixture_test.go create mode 100644 third_party/opa/v1/tester/reporter.go create mode 100644 third_party/opa/v1/tester/reporter_test.go create mode 100644 third_party/opa/v1/tester/runer_compile_test.go create mode 100644 third_party/opa/v1/tester/runner.go create mode 100644 third_party/opa/v1/tester/runner_test.go create mode 100644 third_party/opa/v1/tester/test_tracer.go create mode 100644 third_party/opa/v1/topdown/aggregates.go create mode 100644 third_party/opa/v1/topdown/aggregates_bench_test.go create mode 100644 third_party/opa/v1/topdown/arithmetic.go create mode 100644 third_party/opa/v1/topdown/array.go create mode 100644 third_party/opa/v1/topdown/binary.go create mode 100644 third_party/opa/v1/topdown/bindings.go create mode 100644 third_party/opa/v1/topdown/bindings_test.go create mode 100644 third_party/opa/v1/topdown/bits.go create mode 100644 third_party/opa/v1/topdown/builtins.go create mode 100644 third_party/opa/v1/topdown/builtins/builtins.go create mode 100644 third_party/opa/v1/topdown/builtins_test.go create mode 100644 third_party/opa/v1/topdown/cache.go create mode 100644 third_party/opa/v1/topdown/cache/cache.go create mode 100644 third_party/opa/v1/topdown/cache/cache_test.go create mode 100644 third_party/opa/v1/topdown/cache_bench_test.go create mode 100644 third_party/opa/v1/topdown/cache_test.go create mode 100644 third_party/opa/v1/topdown/cancel.go create mode 100644 third_party/opa/v1/topdown/casts.go create mode 100644 third_party/opa/v1/topdown/cidr.go create mode 100644 third_party/opa/v1/topdown/cidr_test.go create mode 100644 third_party/opa/v1/topdown/comparison.go create mode 100644 third_party/opa/v1/topdown/copypropagation/copypropagation.go create mode 100644 third_party/opa/v1/topdown/copypropagation/unionfind.go create mode 100644 third_party/opa/v1/topdown/copypropagation/unionfind_test.go create mode 100644 third_party/opa/v1/topdown/crypto.go create mode 100644 third_party/opa/v1/topdown/crypto_test.go create mode 100644 third_party/opa/v1/topdown/doc.go create mode 100644 third_party/opa/v1/topdown/encoding.go create mode 100644 third_party/opa/v1/topdown/errors.go create mode 100644 third_party/opa/v1/topdown/errors_test.go create mode 100644 third_party/opa/v1/topdown/eval.go create mode 100644 third_party/opa/v1/topdown/eval_test.go create mode 100644 third_party/opa/v1/topdown/example_test.go create mode 100644 third_party/opa/v1/topdown/exported_test.go create mode 100644 third_party/opa/v1/topdown/glob.go create mode 100644 third_party/opa/v1/topdown/glob_bench_test.go create mode 100644 third_party/opa/v1/topdown/glob_test.go create mode 100644 third_party/opa/v1/topdown/graphql.go create mode 100644 third_party/opa/v1/topdown/graphql_bench_test.go create mode 100644 third_party/opa/v1/topdown/graphql_test.go create mode 100644 third_party/opa/v1/topdown/http.go create mode 100644 third_party/opa/v1/topdown/http_fixup.go create mode 100644 third_party/opa/v1/topdown/http_fixup_darwin.go create mode 100644 third_party/opa/v1/topdown/http_slow_test.go create mode 100644 third_party/opa/v1/topdown/http_test.go create mode 100644 third_party/opa/v1/topdown/input.go create mode 100644 third_party/opa/v1/topdown/input_test.go create mode 100644 third_party/opa/v1/topdown/instrumentation.go create mode 100644 third_party/opa/v1/topdown/json.go create mode 100644 third_party/opa/v1/topdown/json_bench_test.go create mode 100644 third_party/opa/v1/topdown/json_test.go create mode 100644 third_party/opa/v1/topdown/jsonschema.go create mode 100644 third_party/opa/v1/topdown/jsonschema_test.go create mode 100644 third_party/opa/v1/topdown/lineage/lineage.go create mode 100644 third_party/opa/v1/topdown/lineage/lineage_test.go create mode 100644 third_party/opa/v1/topdown/net.go create mode 100644 third_party/opa/v1/topdown/net_test.go create mode 100644 third_party/opa/v1/topdown/numbers.go create mode 100644 third_party/opa/v1/topdown/numbers_bench_test.go create mode 100644 third_party/opa/v1/topdown/numbers_test.go create mode 100644 third_party/opa/v1/topdown/object.go create mode 100644 third_party/opa/v1/topdown/object_bench_test.go create mode 100644 third_party/opa/v1/topdown/object_test.go create mode 100644 third_party/opa/v1/topdown/parse.go create mode 100644 third_party/opa/v1/topdown/parse_bytes.go create mode 100644 third_party/opa/v1/topdown/parse_units.go create mode 100644 third_party/opa/v1/topdown/print.go create mode 100644 third_party/opa/v1/topdown/print/print.go create mode 100644 third_party/opa/v1/topdown/print_test.go create mode 100644 third_party/opa/v1/topdown/providers.go create mode 100644 third_party/opa/v1/topdown/query.go create mode 100644 third_party/opa/v1/topdown/query_test.go create mode 100644 third_party/opa/v1/topdown/reachable.go create mode 100644 third_party/opa/v1/topdown/regex.go create mode 100644 third_party/opa/v1/topdown/regex_bench_test.go create mode 100644 third_party/opa/v1/topdown/regex_template.go create mode 100644 third_party/opa/v1/topdown/regex_template_test.go create mode 100644 third_party/opa/v1/topdown/regex_test.go create mode 100644 third_party/opa/v1/topdown/resolver.go create mode 100644 third_party/opa/v1/topdown/runtime.go create mode 100644 third_party/opa/v1/topdown/runtime_test.go create mode 100644 third_party/opa/v1/topdown/save.go create mode 100644 third_party/opa/v1/topdown/save_test.go create mode 100644 third_party/opa/v1/topdown/semver.go create mode 100644 third_party/opa/v1/topdown/sets.go create mode 100644 third_party/opa/v1/topdown/sets_bench_test.go create mode 100644 third_party/opa/v1/topdown/sets_test.go create mode 100644 third_party/opa/v1/topdown/strings.go create mode 100644 third_party/opa/v1/topdown/strings_bench_test.go create mode 100644 third_party/opa/v1/topdown/subset.go create mode 100644 third_party/opa/v1/topdown/template.go create mode 100644 third_party/opa/v1/topdown/test.go create mode 100644 third_party/opa/v1/topdown/testdata/.gitignore create mode 100644 third_party/opa/v1/topdown/testdata/cases/test-systemdocument-1069.yaml create mode 100755 third_party/opa/v1/topdown/testdata/gencerts.sh create mode 100644 third_party/opa/v1/topdown/time.go create mode 100644 third_party/opa/v1/topdown/time_test.go create mode 100644 third_party/opa/v1/topdown/tokens.go create mode 100644 third_party/opa/v1/topdown/tokens_bench_test.go create mode 100644 third_party/opa/v1/topdown/tokens_test.go create mode 100644 third_party/opa/v1/topdown/topdown_bench_test.go create mode 100644 third_party/opa/v1/topdown/topdown_partial_bench_test.go create mode 100644 third_party/opa/v1/topdown/topdown_partial_test.go create mode 100644 third_party/opa/v1/topdown/topdown_test.go create mode 100644 third_party/opa/v1/topdown/trace.go create mode 100644 third_party/opa/v1/topdown/trace_test.go create mode 100644 third_party/opa/v1/topdown/type.go create mode 100644 third_party/opa/v1/topdown/type_name.go create mode 100644 third_party/opa/v1/topdown/uuid.go create mode 100644 third_party/opa/v1/topdown/uuid_test.go create mode 100644 third_party/opa/v1/topdown/walk.go create mode 100644 third_party/opa/v1/tracing/tracing.go create mode 100644 third_party/opa/v1/types/decode.go create mode 100644 third_party/opa/v1/types/types.go create mode 100644 third_party/opa/v1/types/types_bench_test.go create mode 100644 third_party/opa/v1/types/types_test.go create mode 100644 third_party/opa/v1/util/backoff.go create mode 100644 third_party/opa/v1/util/channel.go create mode 100644 third_party/opa/v1/util/close.go create mode 100644 third_party/opa/v1/util/compare.go create mode 100644 third_party/opa/v1/util/compare_test.go create mode 100644 third_party/opa/v1/util/decoding/context.go create mode 100644 third_party/opa/v1/util/doc.go create mode 100644 third_party/opa/v1/util/enumflag.go create mode 100644 third_party/opa/v1/util/enumflag_test.go create mode 100644 third_party/opa/v1/util/graph.go create mode 100644 third_party/opa/v1/util/graph_test.go create mode 100644 third_party/opa/v1/util/hashmap.go create mode 100644 third_party/opa/v1/util/hashmap_test.go create mode 100644 third_party/opa/v1/util/json.go create mode 100644 third_party/opa/v1/util/json_test.go create mode 100644 third_party/opa/v1/util/maps.go create mode 100644 third_party/opa/v1/util/maps_test.go create mode 100644 third_party/opa/v1/util/performance.go create mode 100644 third_party/opa/v1/util/performance_test.go create mode 100644 third_party/opa/v1/util/queue.go create mode 100644 third_party/opa/v1/util/queue_test.go create mode 100644 third_party/opa/v1/util/read_gzip_body.go create mode 100644 third_party/opa/v1/util/test/benchmark.go create mode 100644 third_party/opa/v1/util/test/ci_skip.go create mode 100644 third_party/opa/v1/util/test/ci_skip_darwin.go create mode 100644 third_party/opa/v1/util/test/doc.go create mode 100644 third_party/opa/v1/util/test/tempfs.go create mode 100644 third_party/opa/v1/util/test/tempus.go create mode 100644 third_party/opa/v1/util/test/zeroreader.go create mode 100644 third_party/opa/v1/util/time.go create mode 100644 third_party/opa/v1/util/wait.go create mode 100644 third_party/opa/v1/util/wait_test.go create mode 100644 third_party/opa/v1/version/version.go create mode 100644 third_party/opa/v1/version/wasm.go create mode 100644 third_party/opa/version/doc.go create mode 100644 third_party/opa/version/version.go create mode 100644 third_party/opa/version/wasm.go create mode 100644 third_party/opa/wasm/Dockerfile create mode 100644 third_party/opa/wasm/Makefile create mode 100644 third_party/opa/wasm/README.md create mode 100644 third_party/opa/wasm/src/aggregates.c create mode 100644 third_party/opa/wasm/src/aggregates.h create mode 100644 third_party/opa/wasm/src/arithmetic.c create mode 100644 third_party/opa/wasm/src/arithmetic.h create mode 100644 third_party/opa/wasm/src/array.c create mode 100644 third_party/opa/wasm/src/array.h create mode 100644 third_party/opa/wasm/src/bits-builtins.c create mode 100644 third_party/opa/wasm/src/bits-builtins.h create mode 100644 third_party/opa/wasm/src/cidr.c create mode 100644 third_party/opa/wasm/src/cidr.h create mode 100644 third_party/opa/wasm/src/comparisons.c create mode 100644 third_party/opa/wasm/src/comparisons.h create mode 100644 third_party/opa/wasm/src/context.c create mode 100644 third_party/opa/wasm/src/context.h create mode 100644 third_party/opa/wasm/src/conversions.c create mode 100644 third_party/opa/wasm/src/conversions.h create mode 100644 third_party/opa/wasm/src/encoding.c create mode 100644 third_party/opa/wasm/src/encoding.h create mode 100644 third_party/opa/wasm/src/error.c create mode 100644 third_party/opa/wasm/src/error.h create mode 100644 third_party/opa/wasm/src/glob-compiler.cc create mode 100644 third_party/opa/wasm/src/glob-compiler.h create mode 100644 third_party/opa/wasm/src/glob-lexer.cc create mode 100644 third_party/opa/wasm/src/glob-lexer.h create mode 100644 third_party/opa/wasm/src/glob-parser.cc create mode 100644 third_party/opa/wasm/src/glob-parser.h create mode 100644 third_party/opa/wasm/src/glob.cc create mode 100644 third_party/opa/wasm/src/glob.h create mode 100644 third_party/opa/wasm/src/graphs.c create mode 100644 third_party/opa/wasm/src/graphs.h create mode 100644 third_party/opa/wasm/src/json.c create mode 100644 third_party/opa/wasm/src/json.h create mode 100644 third_party/opa/wasm/src/lib/assert.h create mode 100644 third_party/opa/wasm/src/lib/bits.h create mode 100644 third_party/opa/wasm/src/lib/ctype.c create mode 100644 third_party/opa/wasm/src/lib/ctype.h create mode 100644 third_party/opa/wasm/src/lib/errno.c create mode 100644 third_party/opa/wasm/src/lib/errno.h create mode 100644 third_party/opa/wasm/src/lib/inttypes.h create mode 100644 third_party/opa/wasm/src/lib/locale.c create mode 100644 third_party/opa/wasm/src/lib/locale.h create mode 100644 third_party/opa/wasm/src/lib/math.c create mode 100644 third_party/opa/wasm/src/lib/math.h create mode 100644 third_party/opa/wasm/src/lib/printf.c create mode 100644 third_party/opa/wasm/src/lib/printf.h create mode 100644 third_party/opa/wasm/src/lib/signal.h create mode 100644 third_party/opa/wasm/src/lib/stdio.c create mode 100644 third_party/opa/wasm/src/lib/stdio.h create mode 100644 third_party/opa/wasm/src/lib/stdlib.c create mode 100644 third_party/opa/wasm/src/lib/stdlib.h create mode 100644 third_party/opa/wasm/src/lib/string.c create mode 100644 third_party/opa/wasm/src/lib/string.h create mode 100644 third_party/opa/wasm/src/lib/time.h create mode 100644 third_party/opa/wasm/src/lib/unistd.h create mode 100644 third_party/opa/wasm/src/lib/wchar.c create mode 100644 third_party/opa/wasm/src/lib/wchar.h create mode 100644 third_party/opa/wasm/src/lib/wctype.h create mode 100644 third_party/opa/wasm/src/libc++/atomic create mode 100644 third_party/opa/wasm/src/libc++/hash.cc create mode 100644 third_party/opa/wasm/src/libc++/minimal.cc create mode 100644 third_party/opa/wasm/src/libc++/mutex create mode 100644 third_party/opa/wasm/src/libc++/mutex.cc create mode 100644 third_party/opa/wasm/src/libmpdec/basearith.c create mode 100644 third_party/opa/wasm/src/libmpdec/basearith.h create mode 100644 third_party/opa/wasm/src/libmpdec/bits.h create mode 100644 third_party/opa/wasm/src/libmpdec/constants.c create mode 100644 third_party/opa/wasm/src/libmpdec/constants.h create mode 100644 third_party/opa/wasm/src/libmpdec/context.c create mode 100644 third_party/opa/wasm/src/libmpdec/convolute.c create mode 100644 third_party/opa/wasm/src/libmpdec/convolute.h create mode 100644 third_party/opa/wasm/src/libmpdec/crt.c create mode 100644 third_party/opa/wasm/src/libmpdec/crt.h create mode 100644 third_party/opa/wasm/src/libmpdec/difradix2.c create mode 100644 third_party/opa/wasm/src/libmpdec/difradix2.h create mode 100644 third_party/opa/wasm/src/libmpdec/fnt.c create mode 100644 third_party/opa/wasm/src/libmpdec/fnt.h create mode 100644 third_party/opa/wasm/src/libmpdec/fourstep.c create mode 100644 third_party/opa/wasm/src/libmpdec/fourstep.h create mode 100644 third_party/opa/wasm/src/libmpdec/io.c create mode 100644 third_party/opa/wasm/src/libmpdec/io.h create mode 100644 third_party/opa/wasm/src/libmpdec/memory.c create mode 100644 third_party/opa/wasm/src/libmpdec/memory.h create mode 100644 third_party/opa/wasm/src/libmpdec/mpdecimal.c create mode 100644 third_party/opa/wasm/src/libmpdec/mpdecimal.h create mode 100644 third_party/opa/wasm/src/libmpdec/numbertheory.c create mode 100644 third_party/opa/wasm/src/libmpdec/numbertheory.h create mode 100644 third_party/opa/wasm/src/libmpdec/sixstep.c create mode 100644 third_party/opa/wasm/src/libmpdec/sixstep.h create mode 100644 third_party/opa/wasm/src/libmpdec/transpose.c create mode 100644 third_party/opa/wasm/src/libmpdec/transpose.h create mode 100644 third_party/opa/wasm/src/libmpdec/typearith.h create mode 100644 third_party/opa/wasm/src/libmpdec/umodarith.h create mode 100644 third_party/opa/wasm/src/malloc.c create mode 100644 third_party/opa/wasm/src/malloc.h create mode 100644 third_party/opa/wasm/src/memoize.c create mode 100644 third_party/opa/wasm/src/memoize.h create mode 100644 third_party/opa/wasm/src/mpd.c create mode 100644 third_party/opa/wasm/src/mpd.h create mode 100644 third_party/opa/wasm/src/numbers.c create mode 100644 third_party/opa/wasm/src/numbers.h create mode 100644 third_party/opa/wasm/src/object.c create mode 100644 third_party/opa/wasm/src/object.h create mode 100644 third_party/opa/wasm/src/re2/re2/bitmap256.h create mode 100644 third_party/opa/wasm/src/re2/re2/bitstate.cc create mode 100644 third_party/opa/wasm/src/re2/re2/compile.cc create mode 100644 third_party/opa/wasm/src/re2/re2/dfa.cc create mode 100644 third_party/opa/wasm/src/re2/re2/nfa.cc create mode 100644 third_party/opa/wasm/src/re2/re2/onepass.cc create mode 100644 third_party/opa/wasm/src/re2/re2/parse.cc create mode 100644 third_party/opa/wasm/src/re2/re2/perl_groups.cc create mode 100644 third_party/opa/wasm/src/re2/re2/pod_array.h create mode 100644 third_party/opa/wasm/src/re2/re2/prog.cc create mode 100644 third_party/opa/wasm/src/re2/re2/prog.h create mode 100644 third_party/opa/wasm/src/re2/re2/re2.cc create mode 100644 third_party/opa/wasm/src/re2/re2/re2.h create mode 100644 third_party/opa/wasm/src/re2/re2/regexp.cc create mode 100644 third_party/opa/wasm/src/re2/re2/regexp.h create mode 100644 third_party/opa/wasm/src/re2/re2/simplify.cc create mode 100644 third_party/opa/wasm/src/re2/re2/sparse_array.h create mode 100644 third_party/opa/wasm/src/re2/re2/sparse_set.h create mode 100644 third_party/opa/wasm/src/re2/re2/stringpiece.cc create mode 100644 third_party/opa/wasm/src/re2/re2/stringpiece.h create mode 100644 third_party/opa/wasm/src/re2/re2/tostring.cc create mode 100644 third_party/opa/wasm/src/re2/re2/unicode_casefold.cc create mode 100644 third_party/opa/wasm/src/re2/re2/unicode_casefold.h create mode 100644 third_party/opa/wasm/src/re2/re2/unicode_groups.cc create mode 100644 third_party/opa/wasm/src/re2/re2/unicode_groups.h create mode 100644 third_party/opa/wasm/src/re2/re2/walker-inl.h create mode 100644 third_party/opa/wasm/src/re2/util/logging.h create mode 100644 third_party/opa/wasm/src/re2/util/mix.h create mode 100644 third_party/opa/wasm/src/re2/util/mutex.h create mode 100644 third_party/opa/wasm/src/re2/util/rune.cc create mode 100644 third_party/opa/wasm/src/re2/util/strutil.cc create mode 100644 third_party/opa/wasm/src/re2/util/strutil.h create mode 100644 third_party/opa/wasm/src/re2/util/utf.h create mode 100644 third_party/opa/wasm/src/re2/util/util.h create mode 100644 third_party/opa/wasm/src/regex.cc create mode 100644 third_party/opa/wasm/src/regex.h create mode 100644 third_party/opa/wasm/src/set.c create mode 100644 third_party/opa/wasm/src/set.h create mode 100644 third_party/opa/wasm/src/std.h create mode 100644 third_party/opa/wasm/src/str.c create mode 100644 third_party/opa/wasm/src/str.h create mode 100644 third_party/opa/wasm/src/strings.c create mode 100644 third_party/opa/wasm/src/strings.h create mode 100644 third_party/opa/wasm/src/types.c create mode 100644 third_party/opa/wasm/src/types.h create mode 100644 third_party/opa/wasm/src/undefined.symbols create mode 100644 third_party/opa/wasm/src/unicode.c create mode 100644 third_party/opa/wasm/src/unicode.h create mode 100644 third_party/opa/wasm/src/value.c create mode 100644 third_party/opa/wasm/src/value.h create mode 100644 third_party/opa/wasm/test.js create mode 100644 third_party/opa/wasm/tests/test-glob.cc create mode 100644 third_party/opa/wasm/tests/test-regex.cc create mode 100644 third_party/opa/wasm/tests/test.c create mode 100644 third_party/opa/wasm/tests/test.h create mode 100644 third_party/opa/wasm/tests/undefined.symbols diff --git a/go.mod b/go.mod index af1e2c01aa30..f5f2548937f8 100644 --- a/go.mod +++ b/go.mod @@ -1278,8 +1278,8 @@ replace k8s.io/kube-state-metrics/v2 v2.13.1-0.20241025121156-110f03d7331f => gi // among with the Connect, Bind and Accept requests replace github.com/iceber/iouring-go => github.com/lebauce/iouring-go v0.0.0-20250513121434-2d4fb49003b5 -// Fork to remove some text/template usage, https://github.com/DataDog/opa/tree/lightweight-1.7.1 -replace github.com/open-policy-agent/opa => github.com/DataDog/opa v0.0.0-20251126100856-d2e1e78e0816 +// Preserve Datadog lightweight OPA patches; see third_party/opa/PROVENANCE.md. +replace github.com/open-policy-agent/opa => ./third_party/opa // TODO: Remove this replace once v0.148.0 is released @@ -1463,3 +1463,9 @@ replace ( github.com/DataDog/datadog-agent/test/new-e2e => ./test/new-e2e github.com/DataDog/datadog-agent/test/otel => ./test/otel ) + +replace github.com/netsampler/goflow2 => ./third_party/goflow2 + +replace github.com/wk8/go-ordered-map/v2 => ./third_party/go-ordered-map + +replace github.com/cloudfoundry-community/go-cfclient/v2 => ./third_party/go-cfclient diff --git a/go.work b/go.work index 12c13b2fa4f4..90a69d0c5e20 100644 --- a/go.work +++ b/go.work @@ -6,6 +6,10 @@ godebug tlsmlkem=0 use ( . + ./third_party/go-cfclient + ./third_party/go-ordered-map + ./third_party/goflow2 + ./third_party/opa comp/api/api/def comp/core/agenttelemetry/def comp/core/agenttelemetry/fx diff --git a/pkg/networkdevice/profile/go.mod b/pkg/networkdevice/profile/go.mod index d0665d9baedd..dc12162bc936 100644 --- a/pkg/networkdevice/profile/go.mod +++ b/pkg/networkdevice/profile/go.mod @@ -206,3 +206,5 @@ replace ( ) replace github.com/go-openapi/testify/v2 => github.com/go-openapi/testify/v2 v2.4.1 + +replace github.com/wk8/go-ordered-map/v2 => ../../../third_party/go-ordered-map diff --git a/third_party/README.md b/third_party/README.md index e723f102418a..1b89986236d7 100644 --- a/third_party/README.md +++ b/third_party/README.md @@ -1,20 +1,18 @@ -# Third Party code +# Temporary owning-library YAML backports -All files under this folder are owned by other entitites than DataDog. +These independently addressable modules retain the selected upstream APIs and +source commits. Each module's `PROVENANCE.md` records its source and removal +condition. They are temporarily owned under +https://github.com/StackVista/stackstate/issues/717. -While our normal build process is to reference upstream projects by URL of a -release artifact, sometimes this is not feasible. Typical cases are: +The root module and workspace explicitly select all four sources; the independent +network-device profile module also selects ordered-map. External Go consumers +must repeat the appropriate source selections because dependency `replace` +directives do not propagate. OPA retains the Datadog lightweight source rather +than switching to upstream OPA. -1. The dependency has been abandoned and we need to maintain it ourselves. -1. That we need a small subset of files from an upstream repository ahead of their release. -1. We are actively working to add features to a dependency, and are co-developing those - along with our own use. - -In the first case, this code may be long lived. In all other cases, we expect -to drop the code from this repository when the features are available in an -upstream release. - -# Rules - -- You must only copy projects that are published under a license that permits this copying. -- All projects must have an explicit owner(s), listed in the file DATADOG.md in the tree. +The modules retain their original logical paths, so they are outside the agent's +module release registry. Validate each with `GOWORK=off go mod tidy` and upstream +unit fixtures separately, then run the agent module consistency and consumer +checks. Upstream CI configuration and vendored snapshots are excluded; upstream +source, tests, fixtures, licenses and attribution are retained. diff --git a/third_party/go-cfclient/.gitignore b/third_party/go-cfclient/.gitignore new file mode 100644 index 000000000000..06e1eff75058 --- /dev/null +++ b/third_party/go-cfclient/.gitignore @@ -0,0 +1,30 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test +_workspace + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +vendor + +# GoLand +.idea \ No newline at end of file diff --git a/third_party/go-cfclient/LICENSE b/third_party/go-cfclient/LICENSE new file mode 100644 index 000000000000..cb2ec6c50df7 --- /dev/null +++ b/third_party/go-cfclient/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2017 Long Nguyen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/third_party/go-cfclient/Makefile b/third_party/go-cfclient/Makefile new file mode 100644 index 000000000000..b7412e582918 --- /dev/null +++ b/third_party/go-cfclient/Makefile @@ -0,0 +1,42 @@ +GOLANG_CI_LINT_VERSION := $(shell golangci-lint --version 2>/dev/null) + +.PHONY: all +all: test lint + +.PHONY: clean +clean: ## Clean testcache and delete build output + go clean -testcache + +.PHONY: test +test: ## Run the unit tests + go test -v -race + +.PHONY: generate +generate: ## Generate fakes + go generate + +.PHONY: lint-prepare +lint-prepare: +ifdef GOLANG_CI_LINT_VERSION + @echo "Found golangci-lint $(GOLANG_CI_LINT_VERSION)" +else + @echo "Installing golangci-lint" + curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s latest + @echo "[OK] golangci-lint installed" +endif + +.PHONY: lint +lint: lint-prepare ## Run the golangci linter + golangci-lint run + +.PHONY: tidy +tidy: ## Remove unused dependencies + go mod tidy + +.PHONY: list +list: ## Print the current module's dependencies. + go list -m all + +# Absolutely awesome: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html +help: ## Print help for each make target + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' \ No newline at end of file diff --git a/third_party/go-cfclient/PROVENANCE.md b/third_party/go-cfclient/PROVENANCE.md new file mode 100644 index 000000000000..39628cd2d5ca --- /dev/null +++ b/third_party/go-cfclient/PROVENANCE.md @@ -0,0 +1,18 @@ +# Temporary maintained-YAML backport + +Source: https://github.com/cloudfoundry-community/go-cfclient +Exact source commit: `3d15366c582080a7cafa6aeb28d2f5fe9c3146d1` (v2.0.1-0.20230503155151-3d15366c5820). +Logical module identity: `github.com/cloudfoundry-community/go-cfclient/v2`. + +Upstream source, tests, fixtures, licenses and attribution are retained. Upstream +CI metadata and vendored dependency snapshots are excluded; dependency sources +continue to resolve through Go modules. The only code edits select the matching +maintained YAML v2/v3 API, including typed YAML node methods and tests. Module +metadata changes select those parsers and satisfy their Go minima. + +Tracking and temporary ownership: https://github.com/StackVista/stackstate/issues/717 +Removal condition: adopt a compatible owning-library release (or Datadog +lightweight OPA patch release) with maintained YAML and passing consumer tests, +then remove this source and its explicit root/workspace/consumer selections. +Dependency replacements do not propagate: external consumers must explicitly +select this independently addressable nested module as well. diff --git a/third_party/go-cfclient/README.md b/third_party/go-cfclient/README.md new file mode 100644 index 000000000000..84788de57156 --- /dev/null +++ b/third_party/go-cfclient/README.md @@ -0,0 +1,114 @@ +# go-cfclient + +## Overview +`cfclient` is a package to assist you in writing apps that need to interact with the [Cloud Foundry](http://cloudfoundry.org) +v2 cloud controller API. + +## v2 go-cfclient deprecated +The v2 version of the client and corresponding v2 cloud controller (CC) API is deprecated. Please start using the v3 version of this +client and CC API. This v2 branch is only kept around to support critical bug fixes. + +## Upgrading the v2 go-cfclient +If you're currently using an old version of the go-cfclient and need to upgrade to the latest version that still +supports the v2 CC API, then you'll need to go get the "new" v2 module. + +```shell +$ go get -u github.com/cloudfoundry-community/go-cfclient/v2 +``` + +Update your go import statements as necessary in your go source files, then finally: +```shell +$ go mod tidy +``` + +## Usage +``` +go get github.com/cloudfoundry-community/go-cfclient/v2 +``` +Some example code: + +```go +package main + +import ( + "fmt" + + "github.com/cloudfoundry-community/go-cfclient/v2" +) + +func main() { + c := &cfclient.Config{ + ApiAddress: "https://api.10.244.0.34.xip.io", + Username: "admin", + Password: "secret", + } + client, _ := cfclient.NewClient(c) + apps, _ := client.ListApps() + fmt.Println(apps) +} +``` + +### Paging Results + +The API supports paging results via query string parameters. All of the v3 ListV3*ByQuery functions support paging. Only a subset of v2 function calls support paging the results: + +- ListSpacesByQuery +- ListOrgsByQuery +- ListAppsByQuery +- ListServiceInstancesByQuery +- ListUsersByQuery + +You can iterate over the results page-by-page using a function similar to this one: + +```go +func processSpacesOnePageAtATime(client *cfclient.Client) error { + page := 1 + pageSize := 50 + + q := url.Values{} + q.Add("results-per-page", strconv.Itoa(pageSize)) + + for { + // get the current page of spaces + q.Set("page", strconv.Itoa(page)) + spaces, err := client.ListSpacesByQuery(q) + if err != nil { + fmt.Printf("Error getting spaces by query: %s", err) + return err + } + + // do something with each space + fmt.Printf("Page %d:\n", page) + for _, s := range spaces { + fmt.Println(" " + s.Name) + } + + // if we hit an empty page or partial page, that means we're done + if len(spaces) < pageSize { + break + } + + // next page + page++ + } + return nil +} +``` + +## Development + +```shell +make all +``` + +### Errors + +If the Cloud Foundry error definitions change at +then the error predicate functions in this package need to be regenerated. + +To do this, simply use Go to regenerate the code: + +```shell +make generate +``` + diff --git a/third_party/go-cfclient/app_update.go b/third_party/go-cfclient/app_update.go new file mode 100644 index 000000000000..458b366c754b --- /dev/null +++ b/third_party/go-cfclient/app_update.go @@ -0,0 +1,128 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/pkg/errors" +) + +type UpdateResponse struct { + Metadata Meta `json:"metadata"` + Entity UpdateResponseEntity `json:"entity"` +} + +type AppUpdateResource struct { + Name string `json:"name,omitempty"` + Memory int `json:"memory,omitempty"` + Instances int `json:"instances,omitempty"` + DiskQuota int `json:"disk_quota,omitempty"` + SpaceGuid string `json:"space_guid,omitempty"` + StackGuid string `json:"stack_guid,omitempty"` + State AppState `json:"state,omitempty"` + Command string `json:"command,omitempty"` + Buildpack string `json:"buildpack,omitempty"` + HealthCheckHttpEndpoint string `json:"health_check_http_endpoint,omitempty"` + HealthCheckType string `json:"health_check_type,omitempty"` + HealthCheckTimeout int `json:"health_check_timeout,omitempty"` + Diego bool `json:"diego,omitempty"` + EnableSSH bool `json:"enable_ssh,omitempty"` + DockerImage string `json:"docker_image,omitempty"` + DockerCredentials map[string]interface{} `json:"docker_credentials_json,omitempty"` + Environment map[string]interface{} `json:"environment_json,omitempty"` + StagingFailedReason string `json:"staging_failed_reason,omitempty"` + StagingFailedDescription string `json:"staging_failed_description,omitempty"` + Ports []int `json:"ports,omitempty"` +} + +type UpdateResponseEntity struct { + Name string `json:"name"` + Production bool `json:"production"` + SpaceGuid string `json:"space_guid"` + StackGuid string `json:"stack_guid"` + Buildpack string `json:"buildpack"` + DetectedBuildpack string `json:"detected_buildpack"` + DetectedBuildpackGuid string `json:"detected_buildpack_guid"` + Environment map[string]interface{} `json:"environment_json"` + Memory int `json:"memory"` + Instances int `json:"instances"` + DiskQuota int `json:"disk_quota"` + State string `json:"state"` + Version string `json:"version"` + Command string `json:"command"` + Console bool `json:"console"` + Debug string `json:"debug"` + StagingTaskId string `json:"staging_task_id"` + PackageState string `json:"package_state"` + HealthCheckHttpEndpoint string `json:"health_check_http_endpoint"` + HealthCheckType string `json:"health_check_type"` + HealthCheckTimeout int `json:"health_check_timeout"` + StagingFailedReason string `json:"staging_failed_reason"` + StagingFailedDescription string `json:"staging_failed_description"` + Diego bool `json:"diego,omitempty"` + DockerImage string `json:"docker_image"` + DockerCredentials struct { + Username string `json:"username"` + Password string `json:"password"` + } `json:"docker_credentials"` + PackageUpdatedAt string `json:"package_updated_at"` + DetectedStartCommand string `json:"detected_start_command"` + EnableSSH bool `json:"enable_ssh"` + Ports []int `json:"ports"` + SpaceURL string `json:"space_url"` + StackURL string `json:"stack_url"` + RoutesURL string `json:"routes_url"` + EventsURL string `json:"events_url"` + ServiceBindingsUrl string `json:"service_bindings_url"` + RouteMappingsUrl string `json:"route_mappings_url"` +} + +func (c *Client) UpdateApp(guid string, aur AppUpdateResource) (UpdateResponse, error) { + var updateResponse UpdateResponse + + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(aur) + if err != nil { + return UpdateResponse{}, err + } + req := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/apps/%s", guid), buf) + resp, err := c.DoRequest(req) + if err != nil { + return UpdateResponse{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return UpdateResponse{}, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return UpdateResponse{}, err + } + err = json.Unmarshal(body, &updateResponse) + if err != nil { + return UpdateResponse{}, err + } + return updateResponse, nil +} + +func (c *Client) RestageApp(guid string) (UpdateResponse, error) { + var result UpdateResponse + + req := c.NewRequest("POST", "/v2/apps/"+guid+"/restage") + resp, err := c.DoRequest(req) + if err != nil { + return result, errors.Wrap(err, "Error restaging app:") + } + + defer resp.Body.Close() + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return result, err + } + + return result, nil +} diff --git a/third_party/go-cfclient/app_update_test.go b/third_party/go-cfclient/app_update_test.go new file mode 100644 index 000000000000..7d405ca3d786 --- /dev/null +++ b/third_party/go-cfclient/app_update_test.go @@ -0,0 +1,43 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestUpdateApp(t *testing.T) { + Convey("Update app", t, func() { + setup(MockRoute{"PUT", "/v2/apps/97f7e56b-addf-4d26-be82-998a06600011", []string{AppUpdatePayload}, "", 201, "", nil}, t) + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + aur := AppUpdateResource{Name: "NewName", DiskQuota: 1024, Instances: 1, Memory: 65} + ret, err := client.UpdateApp("97f7e56b-addf-4d26-be82-998a06600011", aur) + So(err, ShouldBeNil) + So(ret.Entity.Memory, ShouldEqual, 65) + So(ret.Entity.Instances, ShouldEqual, 1) + So(ret.Entity.DiskQuota, ShouldEqual, 1024) + So(ret.Entity.Name, ShouldEqual, "NewName") + }) +} + +func TestRestageApp(t *testing.T) { + Convey("Restage app", t, func() { + setup(MockRoute{"POST", "/v2/apps/97f7e56b-addf-4d26-be82-998a06600011/restage", []string{appRestagePayload}, "", 201, "", nil}, t) + client, err := NewClient(&Config{ + ApiAddress: server.URL, + Token: "foobar", + }) + So(err, ShouldBeNil) + + resp, err := client.RestageApp("97f7e56b-addf-4d26-be82-998a06600011") + So(err, ShouldBeNil) + So(resp.Metadata.Guid, ShouldEqual, "97f7e56b-addf-4d26-be82-998a06600011") + So(resp.Entity.Name, ShouldEqual, "name-2047") + So(resp.Entity.EnableSSH, ShouldBeTrue) + }) +} diff --git a/third_party/go-cfclient/app_usage_events.go b/third_party/go-cfclient/app_usage_events.go new file mode 100644 index 000000000000..227c4fe32421 --- /dev/null +++ b/third_party/go-cfclient/app_usage_events.go @@ -0,0 +1,80 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/url" + + "github.com/pkg/errors" +) + +type AppUsageEvent struct { + GUID string `json:"guid"` + CreatedAt string `json:"created_at"` + State string `json:"state"` + PreviousState string `json:"previous_state"` + MemoryInMbPerInstance int `json:"memory_in_mb_per_instance"` + PreviousMemoryInMbPerInstance int `json:"previous_memory_in_mb_per_instance"` + InstanceCount int `json:"instance_count"` + PreviousInstanceCount int `json:"previous_instance_count"` + AppGUID string `json:"app_guid"` + SpaceGUID string `json:"space_guid"` + SpaceName string `json:"space_name"` + OrgGUID string `json:"org_guid"` + BuildpackGUID string `json:"buildpack_guid"` + BuildpackName string `json:"buildpack_name"` + PackageState string `json:"package_state"` + PreviousPackageState string `json:"previous_package_state"` + ParentAppGUID string `json:"parent_app_guid"` + ParentAppName string `json:"parent_app_name"` + ProcessType string `json:"process_type"` + TaskName string `json:"task_name"` + TaskGUID string `json:"task_guid"` + c *Client +} + +type AppUsageEventsResponse struct { + TotalResults int `json:"total_results"` + Pages int `json:"total_pages"` + NextURL string `json:"next_url"` + Resources []AppUsageEventResource `json:"resources"` +} + +type AppUsageEventResource struct { + Meta Meta `json:"metadata"` + Entity AppUsageEvent `json:"entity"` +} + +// ListAppUsageEventsByQuery lists all events matching the provided query. +func (c *Client) ListAppUsageEventsByQuery(query url.Values) ([]AppUsageEvent, error) { + var appUsageEvents []AppUsageEvent + requestURL := fmt.Sprintf("/v2/app_usage_events?%s", query.Encode()) + for { + var appUsageEventsResponse AppUsageEventsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "error requesting events") + } + defer resp.Body.Close() + if err := json.NewDecoder(resp.Body).Decode(&appUsageEventsResponse); err != nil { + return nil, errors.Wrap(err, "error unmarshaling events") + } + for _, e := range appUsageEventsResponse.Resources { + e.Entity.GUID = e.Meta.Guid + e.Entity.CreatedAt = e.Meta.CreatedAt + e.Entity.c = c + appUsageEvents = append(appUsageEvents, e.Entity) + } + requestURL = appUsageEventsResponse.NextURL + if requestURL == "" { + break + } + } + return appUsageEvents, nil +} + +// ListAppUsageEvents lists all unfiltered events. +func (c *Client) ListAppUsageEvents() ([]AppUsageEvent, error) { + return c.ListAppUsageEventsByQuery(nil) +} diff --git a/third_party/go-cfclient/app_usage_events_test.go b/third_party/go-cfclient/app_usage_events_test.go new file mode 100644 index 000000000000..13fc1c9d5d17 --- /dev/null +++ b/third_party/go-cfclient/app_usage_events_test.go @@ -0,0 +1,53 @@ +package cfclient + +import ( + "net/url" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListAppUsageEvents(t *testing.T) { + Convey("List App Usage Events", t, func() { + setup(MockRoute{"GET", "/v2/app_usage_events", []string{listAppUsageEventsPayload, listAppUsageEventsPayloadPage2}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + appUsageEvents, err := client.ListAppUsageEvents() + So(err, ShouldBeNil) + + So(len(appUsageEvents), ShouldEqual, 4) + So(appUsageEvents[0].GUID, ShouldEqual, "b32241a5-5508-4d42-893c-360e42a300b6") + So(appUsageEvents[0].CreatedAt, ShouldEqual, "2016-06-08T16:41:33Z") + }) +} + +func TestListAppUsageEventsByQuery(t *testing.T) { + Convey("List App Usage Events", t, func() { + setup(MockRoute{"GET", "/v2/app_usage_events", []string{listAppUsageEventsPayload, listAppUsageEventsPayloadPage2}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + var query = url.Values{ + "results-per-page": []string{ + "2", + }, + } + appUsageEvents, err := client.ListAppUsageEventsByQuery(query) + So(err, ShouldBeNil) + + So(len(appUsageEvents), ShouldEqual, 4) + So(appUsageEvents[0].GUID, ShouldEqual, "b32241a5-5508-4d42-893c-360e42a300b6") + So(appUsageEvents[0].CreatedAt, ShouldEqual, "2016-06-08T16:41:33Z") + }) +} diff --git a/third_party/go-cfclient/appevents.go b/third_party/go-cfclient/appevents.go new file mode 100644 index 000000000000..1f6c76922565 --- /dev/null +++ b/third_party/go-cfclient/appevents.go @@ -0,0 +1,174 @@ +package cfclient + +import ( + "encoding/json" + "io/ioutil" + "time" + + "github.com/pkg/errors" +) + +// Exported event constants +const ( + AppCrash = "app.crash" + AppStart = "audit.app.start" + AppStop = "audit.app.stop" + AppUpdate = "audit.app.update" + AppCreate = "audit.app.create" + AppDelete = "audit.app.delete-request" + AppSSHAuth = "audit.app.ssh-authorized" + AppSSHUnauth = "audit.app.ssh-unauthorized" + AppRestage = "audit.app.restage" + AppMapRoute = "audit.app.map-route" + AppUnmapRoute = "audit.app.unmap-route" +) + +// Exported filter constants +const ( + FilterTimestamp = "timestamp" + FilterActee = "actee" +) + +// ValidOperators global variable for all valid operators in a query +var ValidOperators = []string{":", ">=", "<=", "<", ">", "IN"} + +// AppEventResponse the entire response +type AppEventResponse struct { + Results int `json:"total_results"` + Pages int `json:"total_pages"` + PrevURL string `json:"prev_url"` + NextURL string `json:"next_url"` + Resources []AppEventResource `json:"resources"` +} + +// AppEventResource the event resources +type AppEventResource struct { + Meta Meta `json:"metadata"` + Entity AppEventEntity `json:"entity"` +} + +// AppEventQuery a struct for defining queries like 'q=filter>value' or 'q=filter IN a,b,c' +type AppEventQuery struct { + Filter string + Operator string + Value string +} + +// The AppEventEntity the actual app event body +type AppEventEntity struct { + // EventTypes are app.crash, audit.app.start, audit.app.stop, audit.app.update, audit.app.create, audit.app.delete-request + EventType string `json:"type"` + // The GUID of the actor. + Actor string `json:"actor"` + // The actor type, user or app + ActorType string `json:"actor_type"` + // The name of the actor. + ActorName string `json:"actor_name"` + // The GUID of the actee. + Actee string `json:"actee"` + // The actee type, space, app or v3-app + ActeeType string `json:"actee_type"` + // The name of the actee. + ActeeName string `json:"actee_name"` + // Timestamp format "2016-02-26T13:29:44Z". The event creation time. + Timestamp time.Time `json:"timestamp"` + MetaData struct { + // app.crash event fields + ExitDescription string `json:"exit_description,omitempty"` + ExitReason string `json:"reason,omitempty"` + ExitStatus string `json:"exit_status,omitempty"` + + Request struct { + Name string `json:"name,omitempty"` + Instances float64 `json:"instances,omitempty"` + State string `json:"state,omitempty"` + Memory float64 `json:"memory,omitempty"` + EnvironmentVars string `json:"environment_json,omitempty"` + DockerCredentials string `json:"docker_credentials_json,omitempty"` + // audit.app.create event fields + Console bool `json:"console,omitempty"` + Buildpack string `json:"buildpack,omitempty"` + Space string `json:"space_guid,omitempty"` + HealthcheckType string `json:"health_check_type,omitempty"` + HealthcheckTimeout float64 `json:"health_check_timeout,omitempty"` + Production bool `json:"production,omitempty"` + // app.crash event fields + Index float64 `json:"index,omitempty"` + } `json:"request"` + } `json:"metadata"` +} + +// ListAppEvents returns all app events based on eventType +func (c *Client) ListAppEvents(eventType string) ([]AppEventEntity, error) { + return c.ListAppEventsByQuery(eventType, nil) +} + +// ListAppEventsByQuery returns all app events based on eventType and queries +func (c *Client) ListAppEventsByQuery(eventType string, queries []AppEventQuery) ([]AppEventEntity, error) { + var events []AppEventEntity + + if eventType != AppCrash && eventType != AppStart && eventType != AppStop && eventType != AppUpdate && eventType != AppCreate && + eventType != AppDelete && eventType != AppSSHAuth && eventType != AppSSHUnauth && eventType != AppRestage && + eventType != AppMapRoute && eventType != AppUnmapRoute { + return nil, errors.New("Unsupported app event type " + eventType) + } + + var query = "/v2/events?q=type:" + eventType + // adding the additional queries + if len(queries) > 0 { + for _, eventQuery := range queries { + if eventQuery.Filter != FilterTimestamp && eventQuery.Filter != FilterActee { + return nil, errors.New("Unsupported query filter type " + eventQuery.Filter) + } + if !stringInSlice(eventQuery.Operator, ValidOperators) { + return nil, errors.New("Unsupported query operator type " + eventQuery.Operator) + } + query += "&q=" + eventQuery.Filter + eventQuery.Operator + eventQuery.Value + } + } + + for { + eventResponse, err := c.getAppEventsResponse(query) + if err != nil { + return []AppEventEntity{}, err + } + for _, event := range eventResponse.Resources { + events = append(events, event.Entity) + } + query = eventResponse.NextURL + if query == "" { + break + } + } + + return events, nil +} + +func (c *Client) getAppEventsResponse(query string) (AppEventResponse, error) { + var eventResponse AppEventResponse + r := c.NewRequest("GET", query) + resp, err := c.DoRequest(r) + if err != nil { + return AppEventResponse{}, errors.Wrap(err, "Error requesting appevents") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return AppEventResponse{}, errors.Wrap(err, "Error reading appevents response body") + } + + err = json.Unmarshal(resBody, &eventResponse) + if err != nil { + return AppEventResponse{}, errors.Wrap(err, "Error unmarshalling appevent") + } + return eventResponse, nil +} + +func stringInSlice(str string, list []string) bool { + for _, v := range list { + if v == str { + return true + } + } + return false +} diff --git a/third_party/go-cfclient/appevents_test.go b/third_party/go-cfclient/appevents_test.go new file mode 100644 index 000000000000..21b5bd4a2f76 --- /dev/null +++ b/third_party/go-cfclient/appevents_test.go @@ -0,0 +1,84 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListAppEvents(t *testing.T) { + Convey("List App Events", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/events", []string{listAppsCreatedEventPayload}, "", 200, "q=type:audit.app.create", nil}, + {"GET", "/v2/events2", []string{listAppsCreatedEventPayload2}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + _, err = client.ListAppEvents("blub") + So(err.Error(), ShouldEqual, "Unsupported app event type blub") + appEvents, err := client.ListAppEvents(AppCreate) + So(err, ShouldBeNil) + So(len(appEvents), ShouldEqual, 3) + So(appEvents[0].MetaData.Request.State, ShouldEqual, "STOPPED") + So(appEvents[1].EventType, ShouldEqual, AppCrash) + So(appEvents[1].MetaData.Request.State, ShouldEqual, "") + So(appEvents[1].MetaData.ExitReason, ShouldEqual, "CRASHED") + So(appEvents[2].MetaData.Request.State, ShouldEqual, "STARTED") + }) +} + +func TestListAppEventsByQuery(t *testing.T) { + Convey("List App Events By Query", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/events", []string{listAppsCreatedEventPayload}, "", 200, "q=type:audit.app.create&q=actee:3ca436ff-67a8-468a-8c7d-27ec68a6cfe5", nil}, + {"GET", "/v2/events2", []string{listAppsCreatedEventPayload2}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + _, err = client.ListAppEventsByQuery("blub", []AppEventQuery{}) + So(err.Error(), ShouldEqual, "Unsupported app event type blub") + + appEventQuery := AppEventQuery{ + Filter: "nofilter", + Operator: ":", + Value: "retlifon", + } + _, err = client.ListAppEventsByQuery(AppCreate, []AppEventQuery{appEventQuery}) + So(err.Error(), ShouldEqual, "Unsupported query filter type nofilter") + + appEventQuery = AppEventQuery{ + Filter: FilterTimestamp, + Operator: "not", + Value: "retlifon", + } + _, err = client.ListAppEventsByQuery(AppCreate, []AppEventQuery{appEventQuery}) + So(err.Error(), ShouldEqual, "Unsupported query operator type not") + + appEventQuery = AppEventQuery{ + Filter: FilterActee, + Operator: ":", + Value: "3ca436ff-67a8-468a-8c7d-27ec68a6cfe5", + } + appEvents, err := client.ListAppEventsByQuery(AppCreate, []AppEventQuery{appEventQuery}) + So(err, ShouldBeNil) + So(len(appEvents), ShouldEqual, 3) + So(appEvents[0].MetaData.Request.State, ShouldEqual, "STOPPED") + So(appEvents[1].EventType, ShouldEqual, AppCrash) + So(appEvents[1].MetaData.Request.State, ShouldEqual, "") + So(appEvents[1].MetaData.ExitReason, ShouldEqual, "CRASHED") + So(appEvents[2].MetaData.Request.State, ShouldEqual, "STARTED") + }) +} diff --git a/third_party/go-cfclient/apps.go b/third_party/go-cfclient/apps.go new file mode 100644 index 000000000000..d4cba0cbced2 --- /dev/null +++ b/third_party/go-cfclient/apps.go @@ -0,0 +1,795 @@ +package cfclient + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/url" + "os" + "strconv" + "strings" + "time" + + "github.com/pkg/errors" +) + +type AppResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []AppResource `json:"resources"` +} + +type AppResource struct { + Meta Meta `json:"metadata"` + Entity App `json:"entity"` +} + +type AppState string + +const ( + APP_STOPPED AppState = "STOPPED" + APP_STARTED AppState = "STARTED" +) + +type HealthCheckType string + +const ( + HEALTH_HTTP HealthCheckType = "http" + HEALTH_PORT HealthCheckType = "port" + HEALTH_PROCESS HealthCheckType = "process" +) + +type DockerCredentials struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` +} + +type AppCreateRequest struct { + Name string `json:"name"` + SpaceGuid string `json:"space_guid"` + // Memory for the app, in MB + Memory int `json:"memory,omitempty"` + // Instances to startup + Instances int `json:"instances,omitempty"` + // Disk quota in MB + DiskQuota int `json:"disk_quota,omitempty"` + StackGuid string `json:"stack_guid,omitempty"` + // Desired state of the app. Either "STOPPED" or "STARTED" + State AppState `json:"state,omitempty"` + // Command to start an app + Command string `json:"command,omitempty"` + // Buildpack to build the app. Three options: + // 1. Blank for autodetection + // 2. GIT url + // 3. Name of an installed buildpack + Buildpack string `json:"buildpack,omitempty"` + // Endpoint to check if an app is healthy + HealthCheckHttpEndpoint string `json:"health_check_http_endpoint,omitempty"` + // How to check if an app is healthy. Defaults to HEALTH_PORT if not specified + HealthCheckType HealthCheckType `json:"health_check_type,omitempty"` + HealthCheckTimeout int `json:"health_check_timeout,omitempty"` + Diego bool `json:"diego,omitempty"` + EnableSSH bool `json:"enable_ssh,omitempty"` + DockerImage string `json:"docker_image,omitempty"` + DockerCredentials DockerCredentials `json:"docker_credentials,omitempty"` + Environment map[string]interface{} `json:"environment_json,omitempty"` +} + +type App struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Name string `json:"name"` + Memory int `json:"memory"` + Instances int `json:"instances"` + DiskQuota int `json:"disk_quota"` + SpaceGuid string `json:"space_guid"` + StackGuid string `json:"stack_guid"` + State string `json:"state"` + PackageState string `json:"package_state"` + Command string `json:"command"` + Buildpack string `json:"buildpack"` + DetectedBuildpack string `json:"detected_buildpack"` + DetectedBuildpackGuid string `json:"detected_buildpack_guid"` + HealthCheckHttpEndpoint string `json:"health_check_http_endpoint"` + HealthCheckType string `json:"health_check_type"` + HealthCheckTimeout int `json:"health_check_timeout"` + Diego bool `json:"diego"` + EnableSSH bool `json:"enable_ssh"` + DetectedStartCommand string `json:"detected_start_command"` + DockerImage string `json:"docker_image"` + DockerCredentialsJSON map[string]interface{} `json:"docker_credentials_json"` + DockerCredentials DockerCredentials `json:"docker_credentials"` + Environment map[string]interface{} `json:"environment_json"` + StagingFailedReason string `json:"staging_failed_reason"` + StagingFailedDescription string `json:"staging_failed_description"` + Ports []int `json:"ports"` + SpaceURL string `json:"space_url"` + SpaceData SpaceResource `json:"space"` + PackageUpdatedAt string `json:"package_updated_at"` + c *Client +} + +type AppInstance struct { + State string `json:"state"` + Since sinceTime `json:"since"` +} + +type AppStats struct { + State string `json:"state"` + Stats struct { + Name string `json:"name"` + Uris []string `json:"uris"` + Host string `json:"host"` + Port int `json:"port"` + Uptime int `json:"uptime"` + MemQuota int `json:"mem_quota"` + DiskQuota int `json:"disk_quota"` + FdsQuota int `json:"fds_quota"` + Usage struct { + Time statTime `json:"time"` + CPU float64 `json:"cpu"` + Mem int `json:"mem"` + Disk int `json:"disk"` + } `json:"usage"` + } `json:"stats"` +} + +type AppSummary struct { + Guid string `json:"guid"` + Name string `json:"name"` + ServiceCount int `json:"service_count"` + RunningInstances int `json:"running_instances"` + SpaceGuid string `json:"space_guid"` + StackGuid string `json:"stack_guid"` + Buildpack string `json:"buildpack"` + DetectedBuildpack string `json:"detected_buildpack"` + Environment map[string]interface{} `json:"environment_json"` + Memory int `json:"memory"` + Instances int `json:"instances"` + DiskQuota int `json:"disk_quota"` + State string `json:"state"` + Command string `json:"command"` + PackageState string `json:"package_state"` + HealthCheckType string `json:"health_check_type"` + HealthCheckTimeout int `json:"health_check_timeout"` + StagingFailedReason string `json:"staging_failed_reason"` + StagingFailedDescription string `json:"staging_failed_description"` + Diego bool `json:"diego"` + DockerImage string `json:"docker_image"` + DetectedStartCommand string `json:"detected_start_command"` + EnableSSH bool `json:"enable_ssh"` + DockerCredentials map[string]interface{} `json:"docker_credentials_json"` +} + +type AppEnv struct { + // These can have arbitrary JSON so need to map to interface{} + Environment map[string]interface{} `json:"environment_json"` + StagingEnv map[string]interface{} `json:"staging_env_json"` + RunningEnv map[string]interface{} `json:"running_env_json"` + SystemEnv map[string]interface{} `json:"system_env_json"` + ApplicationEnv map[string]interface{} `json:"application_env_json"` +} + +// Custom time types to handle non-RFC3339 formatting in API JSON + +type sinceTime struct { + time.Time +} + +func (s *sinceTime) UnmarshalJSON(b []byte) (err error) { + timeFlt, err := strconv.ParseFloat(string(b), 64) + if err != nil { + return err + } + time := time.Unix(int64(timeFlt), 0) + *s = sinceTime{time} + return nil +} + +func (s sinceTime) ToTime() time.Time { + t, err := time.Parse(time.UnixDate, s.Format(time.UnixDate)) + if err != nil { + panic(err) + } + return t +} + +type statTime struct { + time.Time +} + +func (s *statTime) UnmarshalJSON(b []byte) (err error) { + timeString, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + + possibleFormats := [...]string{time.RFC3339, time.RFC3339Nano, "2006-01-02 15:04:05 -0700", "2006-01-02 15:04:05 MST"} + + var value time.Time + for _, possibleFormat := range possibleFormats { + if value, err = time.Parse(possibleFormat, timeString); err == nil { + *s = statTime{value} + return nil + } + } + + return fmt.Errorf("%s was not in any of the expected Date Formats %v", timeString, possibleFormats) +} + +func (s statTime) ToTime() time.Time { + t, err := time.Parse(time.UnixDate, s.Format(time.UnixDate)) + if err != nil { + panic(err) + } + return t +} + +func (a *App) Space() (Space, error) { + var spaceResource SpaceResource + r := a.c.NewRequest("GET", a.SpaceURL) + resp, err := a.c.DoRequest(r) + if err != nil { + return Space{}, errors.Wrap(err, "Error requesting space") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return Space{}, errors.Wrap(err, "Error reading space response") + } + + err = json.Unmarshal(resBody, &spaceResource) + if err != nil { + return Space{}, errors.Wrap(err, "Error unmarshalling body") + } + return a.c.mergeSpaceResource(spaceResource), nil +} + +func (a *App) Summary() (AppSummary, error) { + var appSummary AppSummary + requestUrl := fmt.Sprintf("/v2/apps/%s/summary", a.Guid) + r := a.c.NewRequest("GET", requestUrl) + resp, err := a.c.DoRequest(r) + if err != nil { + return AppSummary{}, errors.Wrap(err, "Error requesting app summary") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return AppSummary{}, errors.Wrap(err, "Error reading app summary body") + } + err = json.Unmarshal(resBody, &appSummary) + if err != nil { + return AppSummary{}, errors.Wrap(err, "Error unmarshalling app summary") + } + return appSummary, nil +} + +// ListAppsByQueryWithLimits queries totalPages app info. When totalPages is +// less and equal than 0, it queries all app info +// When there are no more than totalPages apps on server side, all apps info will be returned +func (c *Client) ListAppsByQueryWithLimits(query url.Values, totalPages int) ([]App, error) { + return c.listApps("/v2/apps", query, totalPages) +} + +func (c *Client) ListAppsByQuery(query url.Values) ([]App, error) { + return c.listApps("/v2/apps", query, -1) +} + +// GetAppByGuidNoInlineCall will fetch app info including space and orgs information +// Without using inline-relations-depth=2 call +func (c *Client) GetAppByGuidNoInlineCall(guid string) (App, error) { + var appResource AppResource + r := c.NewRequest("GET", "/v2/apps/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return App{}, errors.Wrap(err, "Error requesting apps") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return App{}, errors.Wrap(err, "Error reading app response body") + } + + err = json.Unmarshal(resBody, &appResource) + if err != nil { + return App{}, errors.Wrap(err, "Error unmarshalling app") + } + app := c.mergeAppResource(appResource) + + // If no Space Information no need to check org. + if app.SpaceGuid != "" { + // Getting Spaces Resource + space, err := app.Space() + if err != nil { + return App{}, errors.Wrap(err, "Unable to get the Space for the app "+app.Name) + } else { + app.SpaceData.Entity = space + } + + // Getting orgResource + org, err := app.SpaceData.Entity.Org() + if err != nil { + return App{}, errors.Wrap(err, "Unable to get the Org for the app "+app.Name) + } else { + app.SpaceData.Entity.OrgData.Entity = org + } + } + + return app, nil +} + +func (c *Client) ListApps() ([]App, error) { + q := url.Values{} + q.Set("inline-relations-depth", "2") + return c.ListAppsByQuery(q) +} + +func (c *Client) ListAppsByRoute(routeGuid string) ([]App, error) { + return c.listApps(fmt.Sprintf("/v2/routes/%s/apps", routeGuid), url.Values{}, -1) +} + +func (c *Client) ListAppsBySpaceGuid(spaceGuid string) ([]App, error) { + return c.listApps(fmt.Sprintf("/v2/spaces/%s/apps", spaceGuid), url.Values{}, -1) +} + +func (c *Client) listApps(path string, query url.Values, totalPages int) ([]App, error) { + requestUrl := path + "?" + query.Encode() + pages := 0 + apps := []App{} + for { + var appResp AppResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + + if err != nil { + return nil, errors.Wrap(err, "Error requesting apps") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading app request") + } + + err = json.Unmarshal(resBody, &appResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling app") + } + for _, app := range appResp.Resources { + apps = append(apps, c.mergeAppResource(app)) + } + + requestUrl = appResp.NextUrl + if requestUrl == "" || query.Get("page") != "" { + break + } + + pages++ + if totalPages > 0 && pages >= totalPages { + break + } + } + return apps, nil +} + +func (c *Client) GetAppInstances(guid string) (map[string]AppInstance, error) { + var appInstances map[string]AppInstance + + requestURL := fmt.Sprintf("/v2/apps/%s/instances", guid) + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting app instances") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading app instances") + } + err = json.Unmarshal(resBody, &appInstances) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling app instances") + } + return appInstances, nil +} + +func (c *Client) GetAppEnv(guid string) (AppEnv, error) { + var appEnv AppEnv + + requestURL := fmt.Sprintf("/v2/apps/%s/env", guid) + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return appEnv, errors.Wrap(err, "Error requesting app env") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return appEnv, errors.Wrap(err, "Error reading app env") + } + err = json.Unmarshal(resBody, &appEnv) + if err != nil { + return appEnv, errors.Wrap(err, "Error unmarshalling app env") + } + return appEnv, nil +} + +func (c *Client) GetAppRoutes(guid string) ([]Route, error) { + return c.fetchRoutes(fmt.Sprintf("/v2/apps/%s/routes", guid)) +} + +func (c *Client) GetAppStats(guid string) (map[string]AppStats, error) { + var appStats map[string]AppStats + + requestURL := fmt.Sprintf("/v2/apps/%s/stats", guid) + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting app stats") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading app stats") + } + err = json.Unmarshal(resBody, &appStats) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling app stats") + } + return appStats, nil +} + +func (c *Client) KillAppInstance(guid string, index string) error { + requestURL := fmt.Sprintf("/v2/apps/%s/instances/%s", guid, index) + r := c.NewRequest("DELETE", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return errors.Wrapf(err, "Error stopping app %s at index %s", guid, index) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error stopping app %s at index %s", guid, index) + } + return nil +} + +func (c *Client) GetAppByGuid(guid string) (App, error) { + var appResource AppResource + r := c.NewRequest("GET", "/v2/apps/"+guid+"?inline-relations-depth=2") + resp, err := c.DoRequest(r) + if err != nil { + return App{}, errors.Wrap(err, "Error requesting apps") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return App{}, errors.Wrap(err, "Error reading app response body") + } + + err = json.Unmarshal(resBody, &appResource) + if err != nil { + return App{}, errors.Wrap(err, "Error unmarshalling app") + } + return c.mergeAppResource(appResource), nil +} + +func (c *Client) AppByGuid(guid string) (App, error) { + return c.GetAppByGuid(guid) +} + +// AppByName takes an appName, and GUIDs for a space and org, and performs +// the API lookup with those query parameters set to return you the desired +// App object. +func (c *Client) AppByName(appName, spaceGuid, orgGuid string) (App, error) { + query := url.Values{} + query.Add("q", fmt.Sprintf("organization_guid:%s", orgGuid)) + query.Add("q", fmt.Sprintf("space_guid:%s", spaceGuid)) + query.Add("q", fmt.Sprintf("name:%s", appName)) + apps, err := c.ListAppsByQuery(query) + if err != nil { + return App{}, err + } + if len(apps) == 0 { + cfErr := NewAppNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, appName) + return App{}, cfErr + } + return apps[0], nil +} + +// UploadAppBits uploads the application's contents +func (c *Client) UploadAppBits(file io.Reader, appGUID string) error { + requestFile, err := ioutil.TempFile("", "requests") + if err != nil { + return errors.Wrap(err, "Could not create temp file for app bits") + } + + defer func() { + requestFile.Close() + os.Remove(requestFile.Name()) + }() + + writer := multipart.NewWriter(requestFile) + err = writer.WriteField("resources", "[]") + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits", appGUID) + } + + part, err := writer.CreateFormFile("application", "application.zip") + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits", appGUID) + } + + _, err = io.Copy(part, file) + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits, failed to copy all bytes", appGUID) + } + + err = writer.Close() + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits, failed to close multipart writer", appGUID) + } + + _, err = requestFile.Seek(0, 0) + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits, failed to seek beginning of file", appGUID) + } + fileStats, err := requestFile.Stat() + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits, failed to get temp file stats", appGUID) + } + + requestURL := fmt.Sprintf("/v2/apps/%s/bits", appGUID) + r := c.NewRequestWithBody("PUT", requestURL, requestFile) + req, err := r.toHTTP() + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits", appGUID) + } + + req.ContentLength = fileStats.Size() + contentType := fmt.Sprintf("multipart/form-data; boundary=%s", writer.Boundary()) + req.Header.Set("Content-Type", contentType) + + resp, err := c.Do(req) + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits", appGUID) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return errors.Wrapf(err, "Error uploading app %s bits, response code: %d", appGUID, resp.StatusCode) + } + + return nil +} + +// GetAppBits downloads the application's bits as a tar file +func (c *Client) GetAppBits(guid string) (io.ReadCloser, error) { + requestURL := fmt.Sprintf("/v2/apps/%s/download", guid) + req := c.NewRequest("GET", requestURL) + resp, err := c.DoRequestWithoutRedirects(req) + if err != nil { + return nil, errors.Wrapf(err, "Error downloading app %s bits, API request failed", guid) + } + defer resp.Body.Close() + if isResponseRedirect(resp) { + // directly download the bits from blobstore using a non cloud controller transport + // some blobstores will return a 400 if an Authorization header is sent + blobStoreLocation := resp.Header.Get("Location") + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: c.Config.SkipSslValidation}, + } + client := &http.Client{Transport: tr} + resp, err = client.Get(blobStoreLocation) + if err != nil { + return nil, errors.Wrapf(err, "Error downloading app %s bits from blobstore", guid) + } + } else { + return nil, errors.Wrapf(err, "Error downloading app %s bits, expected redirect to blobstore", guid) + } + return resp.Body, nil +} + +// GetDropletBits downloads the application's droplet bits as a tar file +func (c *Client) GetDropletBits(guid string) (io.ReadCloser, error) { + requestURL := fmt.Sprintf("/v2/apps/%s/droplet/download", guid) + req := c.NewRequest("GET", requestURL) + resp, err := c.DoRequestWithoutRedirects(req) + if err != nil { + return nil, errors.Wrapf(err, "Error downloading droplet %s bits, API request failed", guid) + } + defer resp.Body.Close() + if isResponseRedirect(resp) { + // directly download the bits from blobstore using a non cloud controller transport + // some blobstores will return a 400 if an Authorization header is sent + blobStoreLocation := resp.Header.Get("Location") + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: c.Config.SkipSslValidation}, + } + client := &http.Client{Transport: tr} + resp, err = client.Get(blobStoreLocation) + if err != nil { + return nil, errors.Wrapf(err, "Error downloading droplet %s bits from blobstore", guid) + } + } else { + return nil, errors.Wrapf(err, "Error downloading droplet %s bits, expected redirect to blobstore", guid) + } + return resp.Body, nil +} + +// GetDropletBits downloads the application's droplet bits as a tar file +// Returns the GUID, job URL for monitoring, and an error +func (c *Client) UploadDropletBits(dropletReader io.Reader, appGUID string) (string, error) { + dropletFile, err := ioutil.TempFile("", "droplet") + if err != nil { + return "", errors.Wrap(err, "Could not create temp file for droplet bits") + } + + defer func() { + dropletFile.Close() + os.Remove(dropletFile.Name()) + }() + + writer := multipart.NewWriter(dropletFile) + part, err := writer.CreateFormFile("droplet", "droplet.tgz") + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet", appGUID) + } + + _, err = io.Copy(part, dropletReader) + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet, failed to copy all bytes", appGUID) + } + + err = writer.Close() + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet, failed to close multipart writer", appGUID) + } + + _, err = dropletFile.Seek(0, 0) + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet, failed to seek beginning of file", appGUID) + } + fileStats, err := dropletFile.Stat() + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet, failed to get temp file stats", appGUID) + } + + requestURL := fmt.Sprintf("/v2/apps/%s/droplet/upload", appGUID) + r := c.NewRequestWithBody("PUT", requestURL, dropletFile) + req, err := r.toHTTP() + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet", appGUID) + } + + req.ContentLength = fileStats.Size() + req.Header.Set("Content-Type", writer.FormDataContentType()) + + resp, err := c.Do(req) + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet", appGUID) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return "", errors.Wrapf(err, "Error uploading app %s droplet, response code: %d", appGUID, resp.StatusCode) + } + + var respObj struct { + Metadata struct { + GUID string `json:"guid"` + URL string `json:"url"` + } `json:"metadata"` + } + + if err = json.NewDecoder(resp.Body).Decode(&respObj); err != nil { + return "", errors.Wrapf(err, "Error parsing response") + } + + return respObj.Metadata.URL, nil +} + +// CreateApp creates a new empty application that still needs it's +// app bit uploaded and to be started +func (c *Client) CreateApp(req AppCreateRequest) (App, error) { + var appResp AppResource + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return App{}, err + } + r := c.NewRequestWithBody("POST", "/v2/apps", buf) + resp, err := c.DoRequest(r) + if err != nil { + return App{}, errors.Wrapf(err, "Error creating app %s", req.Name) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return App{}, errors.Wrapf(err, "Error creating app %s, response code: %d", req.Name, resp.StatusCode) + } + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return App{}, errors.Wrapf(err, "Error reading app %s http response body", req.Name) + } + err = json.Unmarshal(resBody, &appResp) + if err != nil { + return App{}, errors.Wrapf(err, "Error deserializing app %s response", req.Name) + } + return c.mergeAppResource(appResp), nil +} + +func (c *Client) StartApp(guid string) error { + startRequest := strings.NewReader(`{ "state": "STARTED" }`) + resp, err := c.DoRequest(c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/apps/%s", guid), startRequest)) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error starting app %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) StopApp(guid string) error { + stopRequest := strings.NewReader(`{ "state": "STOPPED" }`) + resp, err := c.DoRequest(c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/apps/%s", guid), stopRequest)) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error stopping app %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) DeleteApp(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/apps/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting app %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) RestartApp(guid string) error { + var err error + err = c.StopApp(guid) + if err != nil { + return err + } + + err = c.StartApp(guid) + if err != nil { + return err + } + + return nil +} + +func (c *Client) mergeAppResource(app AppResource) App { + app.Entity.Guid = app.Meta.Guid + app.Entity.CreatedAt = app.Meta.CreatedAt + app.Entity.UpdatedAt = app.Meta.UpdatedAt + app.Entity.SpaceData.Entity.Guid = app.Entity.SpaceData.Meta.Guid + app.Entity.SpaceData.Entity.OrgData.Entity.Guid = app.Entity.SpaceData.Entity.OrgData.Meta.Guid + app.Entity.c = c + return app.Entity +} + +func isResponseRedirect(res *http.Response) bool { + switch res.StatusCode { + case http.StatusTemporaryRedirect, http.StatusPermanentRedirect, http.StatusMovedPermanently, http.StatusFound, http.StatusSeeOther: + return true + } + return false +} diff --git a/third_party/go-cfclient/apps_test.go b/third_party/go-cfclient/apps_test.go new file mode 100644 index 000000000000..533943e200d7 --- /dev/null +++ b/third_party/go-cfclient/apps_test.go @@ -0,0 +1,639 @@ +package cfclient + +import ( + "bytes" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + "time" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListApps(t *testing.T) { + Convey("List Apps", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/apps", []string{listAppsPayload}, "Test-golang", 200, "inline-relations-depth=2", nil}, + {"GET", "/v2/appsPage2", []string{listAppsPayloadPage2}, "Test-golang", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + UserAgent: "Test-golang", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + apps, err := client.ListApps() + assertAppList(apps, err) + }) +} + +func TestListAppsByRoute(t *testing.T) { + Convey("List Apps by Route", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/routes/a3fb8d86-4620-4725-b643-0b5122a432e0/apps", []string{listAppsPayload}, "Test-golang", 200, "", nil}, + {"GET", "/v2/appsPage2", []string{listAppsPayloadPage2}, "Test-golang", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + UserAgent: "Test-golang", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + apps, err := client.ListAppsByRoute("a3fb8d86-4620-4725-b643-0b5122a432e0") + assertAppList(apps, err) + }) +} + +func assertAppList(apps []App, err error) { + So(err, ShouldBeNil) + + So(len(apps), ShouldEqual, 2) + So(apps[0].Guid, ShouldEqual, "af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c") + So(apps[0].CreatedAt, ShouldEqual, "2014-10-10T21:03:13+00:00") + So(apps[0].UpdatedAt, ShouldEqual, "2014-11-10T14:07:31+00:00") + So(apps[0].Name, ShouldEqual, "app-test") + So(apps[0].Memory, ShouldEqual, 256) + So(apps[0].Instances, ShouldEqual, 1) + So(apps[0].DiskQuota, ShouldEqual, 1024) + So(apps[0].SpaceGuid, ShouldEqual, "8efd7c5c-d83c-4786-b399-b7bd548839e1") + So(apps[0].StackGuid, ShouldEqual, "2c531037-68a2-4e2c-a9e0-71f9d0abf0d4") + So(apps[0].State, ShouldEqual, "STARTED") + So(apps[0].Command, ShouldEqual, "") + So(apps[0].Buildpack, ShouldEqual, "https://github.com/cloudfoundry/buildpack-go.git") + So(apps[0].DetectedBuildpack, ShouldEqual, "") + So(apps[0].DetectedBuildpackGuid, ShouldEqual, "0d22f6a1-76c5-417f-ac6c-d9d21463ecbc") + So(apps[0].HealthCheckHttpEndpoint, ShouldEqual, "") + So(apps[0].HealthCheckType, ShouldEqual, "port") + So(apps[0].HealthCheckTimeout, ShouldEqual, 0) + So(apps[0].Diego, ShouldEqual, true) + So(apps[0].EnableSSH, ShouldEqual, true) + So(apps[0].DetectedStartCommand, ShouldEqual, "app-launching-service-broker") + So(apps[0].DockerImage, ShouldEqual, "") + So(apps[0].DockerCredentialsJSON["redacted_message"], ShouldEqual, "[PRIVATE DATA HIDDEN]") + So(apps[0].Environment["FOOBAR"], ShouldEqual, "QUX") + So(apps[0].StagingFailedReason, ShouldEqual, "") + So(apps[0].StagingFailedDescription, ShouldEqual, "") + So(apps[0].PackageState, ShouldEqual, "PENDING") + So(len(apps[0].Ports), ShouldEqual, 1) + So(apps[0].Ports[0], ShouldEqual, 8080) + + So(apps[1].Guid, ShouldEqual, "f9ad202b-76dd-44ec-b7c2-fd2417a561e8") + So(apps[1].Name, ShouldEqual, "app-test2") +} + +func TestGetAppByGuidNoInlineCall(t *testing.T) { + Convey("App By GUID", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2", []string{appPayload}, "Test-golang", 200, "", nil}, + {"GET", "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73", []string{spacePayload}, "Test-golang", 200, "", nil}, + {"GET", "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2", []string{orgPayload}, "Test-golang", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + UserAgent: "Test-golang", + } + + client, err := NewClient(c) + So(err, ShouldBeNil) + + app, err := client.GetAppByGuidNoInlineCall("9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + + So(app.Guid, ShouldEqual, "9902530c-c634-4864-a189-71d763cb12e2") + So(app.Name, ShouldEqual, "test-env") + }) + + Convey("App By GUID and space returns error", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2", []string{appPayload}, "Test-golang", 200, "", nil}, + {"GET", "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73", []string{"error"}, "Test-golang", 500, "", nil}, + {"GET", "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2", []string{"error"}, "Test-golang", 500, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + UserAgent: "Test-golang", + } + + client, err := NewClient(c) + So(err, ShouldBeNil) + + _, err = client.GetAppByGuidNoInlineCall("9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldNotBeNil) + + So(err.Error(), ShouldStartWith, "Unable to get the Space for the app test-env") + }) + + Convey("App By GUID with environment variables with different types", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2", []string{appPayloadWithEnvironment}, "Test-golang", 200, "", nil}, + {"GET", "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73", []string{spacePayload}, "Test-golang", 200, "", nil}, + {"GET", "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2", []string{orgPayload}, "Test-golang", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + UserAgent: "Test-golang", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + app, err := client.GetAppByGuidNoInlineCall("9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + So(app.Environment["string"], ShouldEqual, "string") + So(app.Environment["int"], ShouldEqual, 1) + }) +} + +func TestAppByGuid(t *testing.T) { + Convey("App By GUID", t, func() { + setup(MockRoute{"GET", "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2", []string{appPayload}, "", 200, "inline-relations-depth=2", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + app, err := client.GetAppByGuid("9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + + So(app.Guid, ShouldEqual, "9902530c-c634-4864-a189-71d763cb12e2") + So(app.Name, ShouldEqual, "test-env") + }) + + Convey("App By GUID with environment variables with different types", t, func() { + setup(MockRoute{"GET", "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2", []string{appPayloadWithEnvironment}, "", 200, "inline-relations-depth=2", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + app, err := client.GetAppByGuid("9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + + So(app.Environment["string"], ShouldEqual, "string") + So(app.Environment["int"], ShouldEqual, 1) + }) +} + +func TestGetAppInstances(t *testing.T) { + Convey("App completely running", t, func() { + setup(MockRoute{"GET", "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2/instances", []string{appInstancePayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + appInstances, err := client.GetAppInstances("9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + + So(appInstances["0"].State, ShouldEqual, "RUNNING") + So(appInstances["1"].State, ShouldEqual, "RUNNING") + + var d0 float64 = 1455210430.5104606 + var d1 float64 = 1455210430.3912115 + date0 := time.Unix(int64(d0), 0) + date1 := time.Unix(int64(d1), 0) + + So(appInstances["0"].Since.Format(time.UnixDate), ShouldEqual, date0.Format(time.UnixDate)) + So(appInstances["1"].Since.Format(time.UnixDate), ShouldEqual, date1.Format(time.UnixDate)) + So(appInstances["0"].Since.ToTime(), ShouldHaveSameTypeAs, date0) + So(appInstances["1"].Since.ToTime(), ShouldHaveSameTypeAs, date1) + + }) + + Convey("App partially running", t, func() { + setup(MockRoute{"GET", "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2/instances", []string{appInstanceUnhealthyPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + appInstances, err := client.GetAppInstances("9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + + So(appInstances["0"].State, ShouldEqual, "RUNNING") + So(appInstances["1"].State, ShouldEqual, "STARTING") + + var d0 float64 = 1455210430.5104606 + var d1 float64 = 1455210430.3912115 + date0 := time.Unix(int64(d0), 0) + date1 := time.Unix(int64(d1), 0) + + So(appInstances["0"].Since.Format(time.UnixDate), ShouldEqual, date0.Format(time.UnixDate)) + So(appInstances["1"].Since.Format(time.UnixDate), ShouldEqual, date1.Format(time.UnixDate)) + So(appInstances["0"].Since.ToTime(), ShouldHaveSameTypeAs, date0) + So(appInstances["1"].Since.ToTime(), ShouldHaveSameTypeAs, date1) + + }) +} + +func TestGetAppStats(t *testing.T) { + Convey("App stats completely running", t, func() { + setup(MockRoute{"GET", "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2/stats", []string{appStatsPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + appStats, err := client.GetAppStats("9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + + So(appStats["0"].State, ShouldEqual, "RUNNING") + So(appStats["1"].State, ShouldEqual, "RUNNING") + So(appStats["2"].State, ShouldEqual, "RUNNING") + So(appStats["3"].State, ShouldEqual, "RUNNING") + + date0, _ := time.Parse("2006-01-02 15:04:05 -0700", "2016-09-17 15:46:17 +0000") + date1, _ := time.Parse("2006-01-02 15:04:05 -0700", "2016-09-17 15:46:17 +0000") + date2, _ := time.Parse(time.RFC3339Nano, "2017-04-06T20:32:19.273294439Z") + date3, _ := time.Parse("2006-01-02 15:04:05 MST", "2017-04-12 15:27:44 UTC") + + So(appStats["0"].Stats.Usage.Time.Format(time.UnixDate), ShouldEqual, date0.Format(time.UnixDate)) + So(appStats["1"].Stats.Usage.Time.Format(time.RFC3339), ShouldEqual, date1.Format(time.RFC3339)) + So(appStats["2"].Stats.Usage.Time.Format(time.RFC3339Nano), ShouldEqual, date2.Format(time.RFC3339Nano)) + So(appStats["3"].Stats.Usage.Time.Format("2006-01-02 15:04:05 MST"), ShouldEqual, date3.Format("2006-01-02 15:04:05 MST")) + So(appStats["0"].Stats.Usage.Time.ToTime(), ShouldHaveSameTypeAs, date0) + So(appStats["1"].Stats.Usage.Time.ToTime(), ShouldHaveSameTypeAs, date1) + So(appStats["2"].Stats.Usage.Time.ToTime(), ShouldHaveSameTypeAs, date2) + So(appStats["3"].Stats.Usage.Time.ToTime(), ShouldHaveSameTypeAs, date3) + So(appStats["0"].Stats.Usage.CPU, ShouldEqual, 0.36580239597146486) + So(appStats["1"].Stats.Usage.CPU, ShouldEqual, 0.33857742931636664) + So(appStats["2"].Stats.Usage.CPU, ShouldEqual, 0.33857742931636664) + So(appStats["3"].Stats.Usage.CPU, ShouldEqual, 0.33857742931636664) + So(appStats["0"].Stats.Usage.Mem, ShouldEqual, 518123520) + So(appStats["1"].Stats.Usage.Mem, ShouldEqual, 530731008) + So(appStats["2"].Stats.Usage.Mem, ShouldEqual, 530731008) + So(appStats["3"].Stats.Usage.Mem, ShouldEqual, 530731008) + So(appStats["0"].Stats.Usage.Disk, ShouldEqual, 151150592) + So(appStats["1"].Stats.Usage.Disk, ShouldEqual, 151150592) + So(appStats["2"].Stats.Usage.Disk, ShouldEqual, 151150592) + So(appStats["3"].Stats.Usage.Disk, ShouldEqual, 151150592) + + }) +} + +func TestGetAppRoutes(t *testing.T) { + Convey("List app routes", t, func() { + setup(MockRoute{"GET", "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2/routes", []string{appRoutesPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + routes, err := client.GetAppRoutes("9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + + So(len(routes), ShouldEqual, 1) + So(routes[0].Guid, ShouldEqual, "311d34d1-c045-4853-845f-05132377ad7d") + So(routes[0].Host, ShouldEqual, "host-36") + So(routes[0].Path, ShouldEqual, "/foo") + So(routes[0].DomainGuid, ShouldEqual, "40a499f7-198a-4289-9aa2-605ba43f92ee") + So(routes[0].SpaceGuid, ShouldEqual, "c7c0dd06-b078-43d7-adcb-3974cd785fdd") + So(routes[0].ServiceInstanceGuid, ShouldEqual, "") + So(routes[0].Port, ShouldEqual, 0) + + }) +} + +func TestUploadAppBits(t *testing.T) { + Convey("Upload app bits", t, func() { + expectedPayload := "this should really be zipped binary data" + mr := MockRoute{ + Method: "PUT-FILE", + Endpoint: "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2/bits", + Status: 201, + PostForm: &expectedPayload, + Output: []string{""}, + } + setup(mr, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + bits := bytes.NewBufferString(expectedPayload) + err = client.UploadAppBits(bits, "9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + }) +} + +func TestGetAppBits(t *testing.T) { + Convey("Get app bits", t, func() { + + next := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/gzip") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("apptarbinarydata")) + }) + s := httptest.NewServer(next) + defer s.Close() + + mr := MockRouteWithRedirect{ + MockRoute: MockRoute{ + Method: "GET", + Endpoint: "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2/download", + Status: 302, + Output: []string{""}, + }, + RedirectLocation: s.URL, + } + setupWithRedirect(mr, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + bits, err := client.GetAppBits("9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + So(bits, ShouldNotBeNil) + + var download string + if b, err := ioutil.ReadAll(bits); err == nil { + download = string(b) + } + So(download, ShouldEqual, "apptarbinarydata") + }) +} + +func TestUploadDropletBits(t *testing.T) { + Convey("Upload droplet bits", t, func() { + expectedPayload := "this should really be tar'd and gzipped binary data" + mr := MockRoute{ + Method: "PUT-FILE", + Endpoint: "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2/droplet/upload", + Status: 201, + PostForm: &expectedPayload, + Output: []string{`{"metadata":{"guid": "123", "url":"abc"}}`}, + } + setup(mr, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + bits := bytes.NewBufferString(expectedPayload) + _, err = client.UploadDropletBits(bits, "9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + }) +} + +func TestGetDropletBits(t *testing.T) { + Convey("Get droplet bits", t, func() { + + next := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/gzip") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("apptarbinarydata")) + }) + s := httptest.NewServer(next) + defer s.Close() + + mr := MockRouteWithRedirect{ + MockRoute: MockRoute{ + Method: "GET", + Endpoint: "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2/droplet/download", + Status: 302, + Output: []string{""}, + }, + RedirectLocation: s.URL, + } + setupWithRedirect(mr, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + bits, err := client.GetDropletBits("9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + So(bits, ShouldNotBeNil) + + var download string + if b, err := ioutil.ReadAll(bits); err == nil { + download = string(b) + } + So(download, ShouldEqual, "apptarbinarydata") + }) +} + +func TestKillAppInstance(t *testing.T) { + Convey("Kills an app instance", t, func() { + setup(MockRoute{"DELETE", "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2/instances/0", []string{""}, "", 204, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + So(client.KillAppInstance("9902530c-c634-4864-a189-71d763cb12e2", "0"), ShouldBeNil) + }) +} + +func TestAppEnv(t *testing.T) { + Convey("Find app space", t, func() { + setup(MockRoute{"GET", "/v2/apps/a7c47787-a982-467c-95d7-9ab17cbcc918/env", []string{appEnvPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + appEnv, err := client.GetAppEnv("a7c47787-a982-467c-95d7-9ab17cbcc918") + So(err, ShouldBeNil) + So(appEnv.StagingEnv, ShouldResemble, map[string]interface{}{"STAGING_ENV": "staging_value"}) + So(appEnv.RunningEnv, ShouldResemble, map[string]interface{}{"RUNNING_ENV": "running_value"}) + So(appEnv.Environment, ShouldResemble, map[string]interface{}{"env_var": "env_val"}) + So(appEnv.SystemEnv["VCAP_SERVICES"].(map[string]interface{})["abc"], ShouldEqual, 123) + So(appEnv.ApplicationEnv["VCAP_APPLICATION"].(map[string]interface{})["application_name"], ShouldEqual, "name-2245") + }) +} + +func TestAppSummary(t *testing.T) { + Convey("Get app summary", t, func() { + setup(MockRoute{"GET", "/v2/apps/b5f0d1bd-a3a9-40a4-af1a-312ad26e5379/summary", []string{appSummaryPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + app := &App{ + Guid: "b5f0d1bd-a3a9-40a4-af1a-312ad26e5379", + c: client, + } + + summary, err := app.Summary() + So(err, ShouldBeNil) + + So(summary.Guid, ShouldEqual, "b5f0d1bd-a3a9-40a4-af1a-312ad26e5379") + So(summary.Name, ShouldEqual, "test-app") + So(summary.ServiceCount, ShouldEqual, 1) + So(summary.RunningInstances, ShouldEqual, 1) + So(summary.SpaceGuid, ShouldEqual, "494d8b64-8181-4183-a6d3-6279db8fec6e") + So(summary.StackGuid, ShouldEqual, "67e019a3-322a-407a-96e0-178e95bd0e55") + So(summary.Buildpack, ShouldEqual, "ruby_buildpack") + So(summary.DetectedBuildpack, ShouldEqual, "") + So(summary.Memory, ShouldEqual, 256) + So(summary.Instances, ShouldEqual, 1) + So(summary.DiskQuota, ShouldEqual, 512) + So(summary.State, ShouldEqual, "STARTED") + So(summary.Command, ShouldEqual, "") + So(summary.PackageState, ShouldEqual, "STAGED") + So(summary.HealthCheckType, ShouldEqual, "port") + So(summary.HealthCheckTimeout, ShouldEqual, 0) + So(summary.StagingFailedReason, ShouldEqual, "") + So(summary.StagingFailedDescription, ShouldEqual, "") + So(summary.Diego, ShouldEqual, true) + So(summary.DockerImage, ShouldEqual, "") + So(summary.DetectedStartCommand, ShouldEqual, "rackup -p $PORT") + So(summary.EnableSSH, ShouldEqual, true) + So(summary.DockerCredentials["redacted_message"], ShouldEqual, "[PRIVATE DATA HIDDEN]") + }) +} + +func TestAppSpace(t *testing.T) { + Convey("Find app space", t, func() { + setup(MockRoute{"GET", "/v2/spaces/foobar", []string{spacePayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + app := &App{ + Guid: "123", + Name: "test app", + SpaceURL: "/v2/spaces/foobar", + c: client, + } + space, err := app.Space() + So(err, ShouldBeNil) + + So(space.Name, ShouldEqual, "test-space") + So(space.Guid, ShouldEqual, "a72fa1e8-c694-47b3-85f2-55f61fd00d73") + }) +} + +func TestDeleteApps(t *testing.T) { + Convey("Delete app", t, func() { + setup(MockRoute{"DELETE", "/v2/apps/a537761f-9d93-4b30-af17-3d73dbca181b", []string{""}, "", 204, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteApp("a537761f-9d93-4b30-af17-3d73dbca181b") + So(err, ShouldBeNil) + }) +} + +func TestCreateApp(t *testing.T) { + Convey("Delete app", t, func() { + setup(MockRoute{"POST", "/v2/apps", []string{appPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + req := AppCreateRequest{ + Name: "test-env", + SpaceGuid: "a72fa1e8-c694-47b3-85f2-55f61fd00d73", + } + app, err := client.CreateApp(req) + So(err, ShouldBeNil) + So(app.Guid, ShouldEqual, "9902530c-c634-4864-a189-71d763cb12e2") + So(app.Name, ShouldEqual, "test-env") + So(app.SpaceGuid, ShouldEqual, "a72fa1e8-c694-47b3-85f2-55f61fd00d73") + }) +} + +func TestStartApp(t *testing.T) { + Convey("Start app", t, func() { + expectedBody := `{ "state": "STARTED" }` + setup(MockRoute{"PUT", "/v2/apps/a537761f-9d93-4b30-af17-3d73dbca181b", []string{appPayload}, "", http.StatusCreated, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + So(client.StartApp("a537761f-9d93-4b30-af17-3d73dbca181b"), ShouldBeNil) + }) +} + +func TestStopApp(t *testing.T) { + Convey("Stop app", t, func() { + expectedBody := `{ "state": "STOPPED" }` + setup(MockRoute{"PUT", "/v2/apps/a537761f-9d93-4b30-af17-3d73dbca181b", []string{appPayload}, "", http.StatusCreated, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + So(client.StopApp("a537761f-9d93-4b30-af17-3d73dbca181b"), ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/buildpacks.go b/third_party/go-cfclient/buildpacks.go new file mode 100644 index 000000000000..3ac6978a8381 --- /dev/null +++ b/third_party/go-cfclient/buildpacks.go @@ -0,0 +1,264 @@ +package cfclient + +import ( + "encoding/json" + "io" + "io/ioutil" + "mime/multipart" + "os" + + "fmt" + "net/http" + + "github.com/pkg/errors" +) + +type BuildpackResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []BuildpackResource `json:"resources"` +} + +type BuildpackResource struct { + Meta Meta `json:"metadata"` + Entity Buildpack `json:"entity"` +} + +type Buildpack struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Name string `json:"name"` + Enabled bool `json:"enabled"` + Locked bool `json:"locked"` + Position int `json:"position"` + Filename string `json:"filename"` + Stack string `json:"stack"` + c *Client +} + +type BuildpackRequest struct { + // These are all pointers to the values so that we can tell + // whether people wanted position 0, or enable/unlock values, + // vs whether they didn't specify them and want them unchanged/default. + Name *string `json:"name,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Locked *bool `json:"locked,omitempty"` + Position *int `json:"position,omitempty"` + Stack *string `json:"stack,omitempty"` +} + +func (c *Client) CreateBuildpack(bpr *BuildpackRequest) (*Buildpack, error) { + if bpr.Name == nil || *bpr.Name == "" { + return nil, errors.New("Unable to create a buidlpack with no name") + } + requestUrl := "/v2/buildpacks" + req := c.NewRequest("POST", requestUrl) + req.obj = bpr + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error creating buildpack:") + } + defer resp.Body.Close() + bp, err := c.handleBuildpackResp(resp) + if err != nil { + return nil, errors.Wrap(err, "Error creating buildpack:") + } + return &bp, nil +} + +func (c *Client) ListBuildpacks() ([]Buildpack, error) { + var buildpacks []Buildpack + requestUrl := "/v2/buildpacks" + for { + buildpackResp, err := c.getBuildpackResponse(requestUrl) + if err != nil { + return []Buildpack{}, err + } + for _, buildpack := range buildpackResp.Resources { + buildpacks = append(buildpacks, c.mergeBuildpackResource(buildpack)) + } + requestUrl = buildpackResp.NextUrl + if requestUrl == "" { + break + } + } + return buildpacks, nil +} + +func (c *Client) DeleteBuildpack(guid string, async bool) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/buildpacks/%s?async=%t", guid, async))) + if err != nil { + return err + } + defer resp.Body.Close() + if (async && (resp.StatusCode != http.StatusAccepted)) || (!async && (resp.StatusCode != http.StatusNoContent)) { + return errors.Wrapf(err, "Error deleting buildpack %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) getBuildpackResponse(requestUrl string) (BuildpackResponse, error) { + var buildpackResp BuildpackResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return BuildpackResponse{}, errors.Wrap(err, "Error requesting buildpacks") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return BuildpackResponse{}, errors.Wrap(err, "Error reading buildpack request") + } + err = json.Unmarshal(resBody, &buildpackResp) + if err != nil { + return BuildpackResponse{}, errors.Wrap(err, "Error unmarshalling buildpack") + } + return buildpackResp, nil +} + +func (c *Client) mergeBuildpackResource(buildpack BuildpackResource) Buildpack { + buildpack.Entity.Guid = buildpack.Meta.Guid + buildpack.Entity.CreatedAt = buildpack.Meta.CreatedAt + buildpack.Entity.UpdatedAt = buildpack.Meta.UpdatedAt + buildpack.Entity.c = c + return buildpack.Entity +} + +func (c *Client) GetBuildpackByGuid(buildpackGUID string) (Buildpack, error) { + requestUrl := fmt.Sprintf("/v2/buildpacks/%s", buildpackGUID) + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return Buildpack{}, errors.Wrap(err, "Error requesting buildpack info") + } + + return c.handleBuildpackResp(resp) +} + +func (c *Client) handleBuildpackResp(resp *http.Response) (Buildpack, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return Buildpack{}, err + } + var buildpackResource BuildpackResource + if err := json.Unmarshal(body, &buildpackResource); err != nil { + return Buildpack{}, err + } + return c.mergeBuildpackResource(buildpackResource), nil +} + +func (b *Buildpack) Upload(file io.Reader, fileName string) error { + var capturedErr error + tempFile("requests", func(requestFile *os.File, err error) { + if err != nil { + capturedErr = err + return + } + writer := multipart.NewWriter(requestFile) + part, err := writer.CreateFormFile("buildpack", fileName) + + if err != nil { + _ = writer.Close() + capturedErr = err + return + } + + _, err = io.Copy(part, file) + if err != nil { + capturedErr = fmt.Errorf("Error creating upload: %s", err.Error()) + return + } + + err = writer.Close() + if err != nil { + capturedErr = err + return + } + + _, err = requestFile.Seek(0, 0) + if err != nil { + capturedErr = fmt.Errorf("Error seeking beginning of file: %s", err) + } + fileStats, err := requestFile.Stat() + if err != nil { + capturedErr = fmt.Errorf("Error getting file info: %s", err) + } + + req, err := http.NewRequest("PUT", fmt.Sprintf("%s/v2/buildpacks/%s/bits", b.c.Config.ApiAddress, b.Guid), requestFile) + if err != nil { + capturedErr = err + return + } + + req.ContentLength = fileStats.Size() + contentType := fmt.Sprintf("multipart/form-data; boundary=%s", writer.Boundary()) + req.Header.Set("Content-Type", contentType) + resp, err := b.c.Do(req) // client.Do() handles the HTTP status code checking for us + if err != nil { + capturedErr = err + return + } + defer resp.Body.Close() + }) + + return errors.Wrap(capturedErr, "Error uploading buildpack:") +} + +func (b *Buildpack) Update(bpr *BuildpackRequest) error { + requestUrl := fmt.Sprintf("/v2/buildpacks/%s", b.Guid) + req := b.c.NewRequest("PUT", requestUrl) + req.obj = bpr + resp, err := b.c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error updating buildpack:") + } + defer resp.Body.Close() + newBp, err := b.c.handleBuildpackResp(resp) + if err != nil { + return errors.Wrap(err, "Error updating buildpack:") + } + b.Name = newBp.Name + b.Locked = newBp.Locked + b.Enabled = newBp.Enabled + return nil +} + +func (bpr *BuildpackRequest) Lock() { + b := true + bpr.Locked = &b +} +func (bpr *BuildpackRequest) Unlock() { + b := false + bpr.Locked = &b +} +func (bpr *BuildpackRequest) Enable() { + b := true + bpr.Enabled = &b +} +func (bpr *BuildpackRequest) Disable() { + b := false + bpr.Enabled = &b +} +func (bpr *BuildpackRequest) SetPosition(i int) { + bpr.Position = &i +} +func (bpr *BuildpackRequest) SetName(s string) { + bpr.Name = &s +} +func (bpr *BuildpackRequest) SetStack(s string) { + bpr.Stack = &s +} + +func tempFile(namePrefix string, cb func(tmpFile *os.File, err error)) { + tmpFile, err := ioutil.TempFile("", namePrefix) + + defer func() { + _ = tmpFile.Close() + _ = os.Remove(tmpFile.Name()) + }() + + cb(tmpFile, err) +} diff --git a/third_party/go-cfclient/buildpacks_test.go b/third_party/go-cfclient/buildpacks_test.go new file mode 100644 index 000000000000..43aa8e39cc88 --- /dev/null +++ b/third_party/go-cfclient/buildpacks_test.go @@ -0,0 +1,375 @@ +package cfclient + +import ( + "bytes" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListBuildpacks(t *testing.T) { + Convey("List buildpack", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/buildpacks", []string{listBuildpacksPayload}, "", 200, "", nil}, + {"GET", "/v2/buildpacksPage2", []string{listBuildpacksPayload2}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpacks, err := client.ListBuildpacks() + So(err, ShouldBeNil) + + So(len(buildpacks), ShouldEqual, 6) + So(buildpacks[0].Guid, ShouldEqual, "c92b6f5f-d2a4-413a-b515-647d059723aa") + So(buildpacks[0].CreatedAt, ShouldEqual, "2016-06-08T16:41:31Z") + So(buildpacks[0].UpdatedAt, ShouldEqual, "2016-06-08T16:41:26Z") + So(buildpacks[0].Name, ShouldEqual, "name_1") + So(buildpacks[0].Stack, ShouldEqual, "cflinuxfs2") + }) +} + +func TestGetBuildpackByGuid(t *testing.T) { + Convey("A buildpack", t, func() { + setup(MockRoute{"GET", "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", []string{buildpackPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpack, err := client.GetBuildpackByGuid("c92b6f5f-d2a4-413a-b515-647d059723aa") + So(err, ShouldBeNil) + + So(buildpack.Guid, ShouldEqual, "c92b6f5f-d2a4-413a-b515-647d059723aa") + So(buildpack.CreatedAt, ShouldEqual, "2016-06-08T16:41:31Z") + So(buildpack.UpdatedAt, ShouldEqual, "2016-06-08T16:41:26Z") + So(buildpack.Name, ShouldEqual, "name_1") + So(buildpack.Stack, ShouldEqual, "cflinuxfs2") + }) + Convey("A buildpack with no stack", t, func() { + setup(MockRoute{"GET", "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", []string{buildpackPayloadBackwardsCompat}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpack, err := client.GetBuildpackByGuid("c92b6f5f-d2a4-413a-b515-647d059723aa") + So(err, ShouldBeNil) + + So(buildpack.Guid, ShouldEqual, "c92b6f5f-d2a4-413a-b515-647d059723aa") + So(buildpack.CreatedAt, ShouldEqual, "2016-06-08T16:41:31Z") + So(buildpack.UpdatedAt, ShouldEqual, "2016-06-08T16:41:26Z") + So(buildpack.Name, ShouldEqual, "name_1") + So(buildpack.Stack, ShouldEqual, "") + }) +} + +func TestUploadBuildpack(t *testing.T) { + Convey("Uploading a buildpack succeeds", t, func() { + expectedPayload := "this should really be zipped binary data" + setup(MockRoute{"PUT-FILE", "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa/bits", []string{buildpackUploadPayload}, "", 200, "", &expectedPayload}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + bits := bytes.NewBufferString(expectedPayload) + buildpack := Buildpack{Guid: "c92b6f5f-d2a4-413a-b515-647d059723aa", c: client} + err = buildpack.Upload(bits, "test.zip") + So(err, ShouldBeNil) + }) + Convey("Uploading a buildpack throws an error in the event of failure", t, func() { + expectedPayload := "this should really be zipped binary data" + setup(MockRoute{"PUT-FILE", "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa/bits", []string{buildpackUploadPayload}, "", 400, "", &expectedPayload}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + bits := bytes.NewBufferString(expectedPayload) + buildpack := Buildpack{Guid: "c92b6f5f-d2a4-413a-b515-647d059723aa", c: client} + err = buildpack.Upload(bits, "test.zip") + So(err, ShouldNotBeNil) + }) +} + +func TestUpdateBuildpack(t *testing.T) { + Convey("Updating a buildpack succeeds", t, func() { + expectedPayload := `{"name":"renamed-buildpack","enabled":true,"locked":true,"position":100}` + setup(MockRoute{"PUT", "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", []string{buildpackUpdatePayload}, "", 200, "", &expectedPayload}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpack := Buildpack{ + Guid: "c92b6f5f-d2a4-413a-b515-647d059723aa", + c: client, + } + buildpackRequest := &BuildpackRequest{} + buildpackRequest.SetName("renamed-buildpack") + buildpackRequest.Lock() + buildpackRequest.Enable() + buildpackRequest.SetPosition(100) + + err = buildpack.Update(buildpackRequest) + So(err, ShouldBeNil) + }) + Convey("Updating a buildpack doesn't accidentally unlock, rename, disable, or reorder the buildpack", t, func() { + expectedPayload := `{}` + setup(MockRoute{"PUT", "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", []string{buildpackUpdatePayload}, "", 200, "", &expectedPayload}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpack := Buildpack{ + Guid: "c92b6f5f-d2a4-413a-b515-647d059723aa", + c: client, + } + buildpackRequest := &BuildpackRequest{} + + err = buildpack.Update(buildpackRequest) + So(err, ShouldBeNil) + }) + Convey("Unlocking, disabling, reordering to 0, and removing name from buildpacks is still possible", t, func() { + expectedPayload := `{"name":"","enabled":false,"locked":false,"position":0}` + setup(MockRoute{"PUT", "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", []string{buildpackUpdatePayload}, "", 200, "", &expectedPayload}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpack := Buildpack{ + Guid: "c92b6f5f-d2a4-413a-b515-647d059723aa", + c: client, + } + buildpackRequest := &BuildpackRequest{} + buildpackRequest.SetName("") + buildpackRequest.Unlock() + buildpackRequest.Disable() + buildpackRequest.SetPosition(0) + + err = buildpack.Update(buildpackRequest) + So(err, ShouldBeNil) + }) + Convey("Updating a buildpack returns an error in the event of failure", t, func() { + expectedPayload := `{}` + setup(MockRoute{"PUT", "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", []string{buildpackUpdatePayload}, "", 400, "", &expectedPayload}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpack := Buildpack{ + Guid: "c92b6f5f-d2a4-413a-b515-647d059723aa", + c: client, + } + + buildpackRequest := &BuildpackRequest{} + + err = buildpack.Update(buildpackRequest) + So(err, ShouldNotBeNil) + }) + Convey("It is possible to update a buildpack stack from null, to a value", t, func() { + expectedPayload := `{"stack":"cflinuxfs2"}` + setup(MockRoute{"PUT", "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", []string{buildpackUpdatePayload}, "", 400, "", &expectedPayload}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpack := Buildpack{ + Guid: "c92b6f5f-d2a4-413a-b515-647d059723aa", + c: client, + } + buildpackRequest := &BuildpackRequest{} + buildpackRequest.SetStack("cflinuxfs2") + err = buildpack.Update(buildpackRequest) + So(err, ShouldNotBeNil) + }) + Convey("Updating without a stack specified doesn't change anything", t, func() { + expectedPayload := `{"name":"new-name"}` + setup(MockRoute{"PUT", "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", []string{buildpackUpdatePayload}, "", 400, "", &expectedPayload}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpack := Buildpack{ + Guid: "c92b6f5f-d2a4-413a-b515-647d059723aa", + c: client, + } + buildpackRequest := &BuildpackRequest{} + buildpackRequest.SetName("new-name") + err = buildpack.Update(buildpackRequest) + So(err, ShouldNotBeNil) + }) +} + +func TestCreateBuildpack(t *testing.T) { + Convey("Creating a buildpack succeeds", t, func() { + expectedPayload := `{"name":"test-buildpack","enabled":true,"locked":true,"position":10,"stack":"cflinuxfs2"}` + setup(MockRoute{"POST", "/v2/buildpacks", []string{buildpackCreatePayload}, "", 200, "", &expectedPayload}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpackRequest := &BuildpackRequest{} + buildpackRequest.SetName("test-buildpack") + buildpackRequest.Lock() + buildpackRequest.Enable() + buildpackRequest.SetPosition(10) + buildpackRequest.SetStack("cflinuxfs2") + + bp, err := client.CreateBuildpack(buildpackRequest) + So(err, ShouldBeNil) + So(bp.Guid, ShouldEqual, "c92b6f5f-d2a4-413a-b515-647d059723aa") + So(bp.Name, ShouldEqual, "test-buildpack") + So(bp.Enabled, ShouldBeTrue) + So(bp.Locked, ShouldBeFalse) + So(bp.Position, ShouldEqual, 10) + So(bp.Stack, ShouldEqual, "cflinuxfs2") + }) + Convey("Creating a buildpack doesn't accidentally unlock, rename, disable, or reorder the buildpack", t, func() { + expectedPayload := `{"name":"test-buildpack"}` + setup(MockRoute{"POST", "/v2/buildpacks", []string{buildpackCreatePayload}, "", 200, "", &expectedPayload}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpackRequest := &BuildpackRequest{} + buildpackRequest.SetName("test-buildpack") + + bp, err := client.CreateBuildpack(buildpackRequest) + So(err, ShouldBeNil) + So(bp, ShouldNotBeNil) + }) + Convey("Creating a buildpack as unlocked/disabled/order 0 works", t, func() { + expectedPayload := `{"name":"test-buildpack","enabled":false,"locked":false,"position":0}` + setup(MockRoute{"POST", "/v2/buildpacks", []string{buildpackCreatePayload}, "", 200, "", &expectedPayload}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpackRequest := &BuildpackRequest{} + buildpackRequest.SetName("test-buildpack") + buildpackRequest.Unlock() + buildpackRequest.Disable() + buildpackRequest.SetPosition(0) + + bp, err := client.CreateBuildpack(buildpackRequest) + So(err, ShouldBeNil) + So(bp, ShouldNotBeNil) + }) + Convey("Creating a buildpack returns an error in the event of failure", t, func() { + expectedPayload := `{"name":"test-buildpack"}` + setup(MockRoute{"POST", "/v2/buildpacks", []string{buildpackCreatePayload}, "", 400, "", &expectedPayload}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpackRequest := &BuildpackRequest{} + buildpackRequest.SetName("test-buildpack") + + bp, err := client.CreateBuildpack(buildpackRequest) + So(err, ShouldNotBeNil) + So(bp, ShouldBeNil) + }) + Convey("Creating a buildpack fails if the request has no name set", t, func() { + setup(MockRoute{"POST", "/v2/buildpacks", []string{buildpackCreatePayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + buildpackRequest := &BuildpackRequest{} + bp, err := client.CreateBuildpack(buildpackRequest) + So(err, ShouldNotBeNil) + So(bp, ShouldBeNil) + }) +} + +func TestDeleteBuildpack(t *testing.T) { + Convey("Delete buildpack synchronously", t, func() { + setup(MockRoute{"DELETE", "/v2/buildpacks/b2a35f0c-d5ad-4a59-bea7-461711d96b0d", []string{""}, "", 204, "async=false", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteBuildpack("b2a35f0c-d5ad-4a59-bea7-461711d96b0d", false) + So(err, ShouldBeNil) + }) + + Convey("Delete buildpack asynchronously", t, func() { + setup(MockRoute{"DELETE", "/v2/buildpacks/b2a35f0c-d5ad-4a59-bea7-461711d96b0d", []string{""}, "", 202, "async=true", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteBuildpack("b2a35f0c-d5ad-4a59-bea7-461711d96b0d", true) + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/cf_error.go b/third_party/go-cfclient/cf_error.go new file mode 100644 index 000000000000..77a0454a3a3d --- /dev/null +++ b/third_party/go-cfclient/cf_error.go @@ -0,0 +1,6119 @@ +package cfclient + +// Code generated by go generate. DO NOT EDIT. +// This file was generated by robots at +// 2022-01-11 09:36:11.804559 +1030 ACDT m=+0.186550187 + +import ( + stderrors "errors" + + pkgerrors "github.com/pkg/errors" +) + +// NewInvalidAuthTokenError returns a new CloudFoundryError +// that IsInvalidAuthTokenError will return true for +func NewInvalidAuthTokenError() CloudFoundryError { + return CloudFoundryError{ + Code: 1000, + ErrorCode: "CF-InvalidAuthToken", + Description: "Invalid Auth Token", + } +} + +// IsInvalidAuthTokenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 1000 +// - HTTP code: 401 +// - message: "Invalid Auth Token" +func IsInvalidAuthTokenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 1000 +} + +// NewMessageParseError returns a new CloudFoundryError +// that IsMessageParseError will return true for +func NewMessageParseError() CloudFoundryError { + return CloudFoundryError{ + Code: 1001, + ErrorCode: "CF-MessageParseError", + Description: "Request invalid due to parse error: %s", + } +} + +// IsMessageParseError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 1001 +// - HTTP code: 400 +// - message: "Request invalid due to parse error: %s" +func IsMessageParseError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 1001 +} + +// NewInvalidRelationError returns a new CloudFoundryError +// that IsInvalidRelationError will return true for +func NewInvalidRelationError() CloudFoundryError { + return CloudFoundryError{ + Code: 1002, + ErrorCode: "CF-InvalidRelation", + Description: "%s", + } +} + +// IsInvalidRelationError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 1002 +// - HTTP code: 400 +// - message: "%s" +func IsInvalidRelationError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 1002 +} + +// NewInvalidContentTypeError returns a new CloudFoundryError +// that IsInvalidContentTypeError will return true for +func NewInvalidContentTypeError() CloudFoundryError { + return CloudFoundryError{ + Code: 1003, + ErrorCode: "CF-InvalidContentType", + Description: "Invalid content type, expected: %s", + } +} + +// IsInvalidContentTypeError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 1003 +// - HTTP code: 400 +// - message: "Invalid content type, expected: %s" +func IsInvalidContentTypeError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 1003 +} + +// NewBadRequestError returns a new CloudFoundryError +// that IsBadRequestError will return true for +func NewBadRequestError() CloudFoundryError { + return CloudFoundryError{ + Code: 1004, + ErrorCode: "CF-BadRequest", + Description: "Bad request: %s", + } +} + +// IsBadRequestError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 1004 +// - HTTP code: 400 +// - message: "Bad request: %s" +func IsBadRequestError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 1004 +} + +// NewNotFoundError returns a new CloudFoundryError +// that IsNotFoundError will return true for +func NewNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 10000, + ErrorCode: "CF-NotFound", + Description: "Unknown request", + } +} + +// IsNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10000 +// - HTTP code: 404 +// - message: "Unknown request" +func IsNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10000 +} + +// NewServerError returns a new CloudFoundryError +// that IsServerError will return true for +func NewServerError() CloudFoundryError { + return CloudFoundryError{ + Code: 10001, + ErrorCode: "CF-ServerError", + Description: "Server error", + } +} + +// IsServerError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10001 +// - HTTP code: 500 +// - message: "Server error" +func IsServerError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10001 +} + +// NewNotAuthenticatedError returns a new CloudFoundryError +// that IsNotAuthenticatedError will return true for +func NewNotAuthenticatedError() CloudFoundryError { + return CloudFoundryError{ + Code: 10002, + ErrorCode: "CF-NotAuthenticated", + Description: "Authentication error", + } +} + +// IsNotAuthenticatedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10002 +// - HTTP code: 401 +// - message: "Authentication error" +func IsNotAuthenticatedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10002 +} + +// NewNotAuthorizedError returns a new CloudFoundryError +// that IsNotAuthorizedError will return true for +func NewNotAuthorizedError() CloudFoundryError { + return CloudFoundryError{ + Code: 10003, + ErrorCode: "CF-NotAuthorized", + Description: "You are not authorized to perform the requested action", + } +} + +// IsNotAuthorizedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10003 +// - HTTP code: 403 +// - message: "You are not authorized to perform the requested action" +func IsNotAuthorizedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10003 +} + +// NewInvalidRequestError returns a new CloudFoundryError +// that IsInvalidRequestError will return true for +func NewInvalidRequestError() CloudFoundryError { + return CloudFoundryError{ + Code: 10004, + ErrorCode: "CF-InvalidRequest", + Description: "The request is invalid", + } +} + +// IsInvalidRequestError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10004 +// - HTTP code: 400 +// - message: "The request is invalid" +func IsInvalidRequestError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10004 +} + +// NewBadQueryParameterError returns a new CloudFoundryError +// that IsBadQueryParameterError will return true for +func NewBadQueryParameterError() CloudFoundryError { + return CloudFoundryError{ + Code: 10005, + ErrorCode: "CF-BadQueryParameter", + Description: "The query parameter is invalid: %s", + } +} + +// IsBadQueryParameterError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10005 +// - HTTP code: 400 +// - message: "The query parameter is invalid: %s" +func IsBadQueryParameterError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10005 +} + +// NewAssociationNotEmptyError returns a new CloudFoundryError +// that IsAssociationNotEmptyError will return true for +func NewAssociationNotEmptyError() CloudFoundryError { + return CloudFoundryError{ + Code: 10006, + ErrorCode: "CF-AssociationNotEmpty", + Description: "Please delete the %s associations for your %s.", + } +} + +// IsAssociationNotEmptyError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10006 +// - HTTP code: 400 +// - message: "Please delete the %s associations for your %s." +func IsAssociationNotEmptyError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10006 +} + +// NewInsufficientScopeError returns a new CloudFoundryError +// that IsInsufficientScopeError will return true for +func NewInsufficientScopeError() CloudFoundryError { + return CloudFoundryError{ + Code: 10007, + ErrorCode: "CF-InsufficientScope", + Description: "Your token lacks the necessary scopes to access this resource.", + } +} + +// IsInsufficientScopeError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10007 +// - HTTP code: 403 +// - message: "Your token lacks the necessary scopes to access this resource." +func IsInsufficientScopeError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10007 +} + +// NewUnprocessableEntityError returns a new CloudFoundryError +// that IsUnprocessableEntityError will return true for +func NewUnprocessableEntityError() CloudFoundryError { + return CloudFoundryError{ + Code: 10008, + ErrorCode: "CF-UnprocessableEntity", + Description: "%s", + } +} + +// IsUnprocessableEntityError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10008 +// - HTTP code: 422 +// - message: "%s" +func IsUnprocessableEntityError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10008 +} + +// NewUnableToPerformError returns a new CloudFoundryError +// that IsUnableToPerformError will return true for +func NewUnableToPerformError() CloudFoundryError { + return CloudFoundryError{ + Code: 10009, + ErrorCode: "CF-UnableToPerform", + Description: "%s could not be completed: %s", + } +} + +// IsUnableToPerformError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10009 +// - HTTP code: 400 +// - message: "%s could not be completed: %s" +func IsUnableToPerformError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10009 +} + +// NewResourceNotFoundError returns a new CloudFoundryError +// that IsResourceNotFoundError will return true for +func NewResourceNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 10010, + ErrorCode: "CF-ResourceNotFound", + Description: "%s", + } +} + +// IsResourceNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10010 +// - HTTP code: 404 +// - message: "%s" +func IsResourceNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10010 +} + +// NewDatabaseError returns a new CloudFoundryError +// that IsDatabaseError will return true for +func NewDatabaseError() CloudFoundryError { + return CloudFoundryError{ + Code: 10011, + ErrorCode: "CF-DatabaseError", + Description: "Database error", + } +} + +// IsDatabaseError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10011 +// - HTTP code: 500 +// - message: "Database error" +func IsDatabaseError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10011 +} + +// NewOrderByParameterInvalidError returns a new CloudFoundryError +// that IsOrderByParameterInvalidError will return true for +func NewOrderByParameterInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 10012, + ErrorCode: "CF-OrderByParameterInvalid", + Description: "Cannot order by: %s", + } +} + +// IsOrderByParameterInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10012 +// - HTTP code: 500 +// - message: "Cannot order by: %s" +func IsOrderByParameterInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10012 +} + +// NewRateLimitExceededError returns a new CloudFoundryError +// that IsRateLimitExceededError will return true for +func NewRateLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 10013, + ErrorCode: "CF-RateLimitExceeded", + Description: "Rate Limit Exceeded", + } +} + +// IsRateLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10013 +// - HTTP code: 429 +// - message: "Rate Limit Exceeded" +func IsRateLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10013 +} + +// NewIPBasedRateLimitExceededError returns a new CloudFoundryError +// that IsIPBasedRateLimitExceededError will return true for +func NewIPBasedRateLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 10014, + ErrorCode: "CF-IPBasedRateLimitExceeded", + Description: "Rate Limit Exceeded: Unauthenticated requests from this IP address have exceeded the limit. Please log in.", + } +} + +// IsIPBasedRateLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10014 +// - HTTP code: 429 +// - message: "Rate Limit Exceeded: Unauthenticated requests from this IP address have exceeded the limit. Please log in." +func IsIPBasedRateLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10014 +} + +// NewServiceUnavailableError returns a new CloudFoundryError +// that IsServiceUnavailableError will return true for +func NewServiceUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 10015, + ErrorCode: "CF-ServiceUnavailable", + Description: "%s", + } +} + +// IsServiceUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10015 +// - HTTP code: 503 +// - message: "%s" +func IsServiceUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10015 +} + +// NewServiceBrokerRateLimitExceededError returns a new CloudFoundryError +// that IsServiceBrokerRateLimitExceededError will return true for +func NewServiceBrokerRateLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 10016, + ErrorCode: "CF-ServiceBrokerRateLimitExceeded", + Description: "Service broker concurrent request limit exceeded", + } +} + +// IsServiceBrokerRateLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10016 +// - HTTP code: 429 +// - message: "Service broker concurrent request limit exceeded" +func IsServiceBrokerRateLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10016 +} + +// NewUserInvalidError returns a new CloudFoundryError +// that IsUserInvalidError will return true for +func NewUserInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 20001, + ErrorCode: "CF-UserInvalid", + Description: "The user info is invalid: %s", + } +} + +// IsUserInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20001 +// - HTTP code: 400 +// - message: "The user info is invalid: %s" +func IsUserInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20001 +} + +// NewUaaIdTakenError returns a new CloudFoundryError +// that IsUaaIdTakenError will return true for +func NewUaaIdTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 20002, + ErrorCode: "CF-UaaIdTaken", + Description: "The UAA ID is taken: %s", + } +} + +// IsUaaIdTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20002 +// - HTTP code: 400 +// - message: "The UAA ID is taken: %s" +func IsUaaIdTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20002 +} + +// NewUserNotFoundError returns a new CloudFoundryError +// that IsUserNotFoundError will return true for +func NewUserNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 20003, + ErrorCode: "CF-UserNotFound", + Description: "The user could not be found: %s", + } +} + +// IsUserNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20003 +// - HTTP code: 404 +// - message: "The user could not be found: %s" +func IsUserNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20003 +} + +// NewUaaUnavailableError returns a new CloudFoundryError +// that IsUaaUnavailableError will return true for +func NewUaaUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 20004, + ErrorCode: "CF-UaaUnavailable", + Description: "The UAA service is currently unavailable", + } +} + +// IsUaaUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20004 +// - HTTP code: 503 +// - message: "The UAA service is currently unavailable" +func IsUaaUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20004 +} + +// NewUaaEndpointDisabledError returns a new CloudFoundryError +// that IsUaaEndpointDisabledError will return true for +func NewUaaEndpointDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 20005, + ErrorCode: "CF-UaaEndpointDisabled", + Description: "The UAA endpoint needed is disabled", + } +} + +// IsUaaEndpointDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20005 +// - HTTP code: 501 +// - message: "The UAA endpoint needed is disabled" +func IsUaaEndpointDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20005 +} + +// NewUserIsInMultipleOriginsError returns a new CloudFoundryError +// that IsUserIsInMultipleOriginsError will return true for +func NewUserIsInMultipleOriginsError() CloudFoundryError { + return CloudFoundryError{ + Code: 20006, + ErrorCode: "CF-UserIsInMultipleOrigins", + Description: "The user exists in multiple origins. Specify an origin for the requested user from: %s", + } +} + +// IsUserIsInMultipleOriginsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20006 +// - HTTP code: 400 +// - message: "The user exists in multiple origins. Specify an origin for the requested user from: %s" +func IsUserIsInMultipleOriginsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20006 +} + +// NewUserWithOriginNotFoundError returns a new CloudFoundryError +// that IsUserWithOriginNotFoundError will return true for +func NewUserWithOriginNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 20007, + ErrorCode: "CF-UserWithOriginNotFound", + Description: "The user could not be found, %s", + } +} + +// IsUserWithOriginNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20007 +// - HTTP code: 404 +// - message: "The user could not be found, %s" +func IsUserWithOriginNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20007 +} + +// NewOutOfRouterGroupPortsError returns a new CloudFoundryError +// that IsOutOfRouterGroupPortsError will return true for +func NewOutOfRouterGroupPortsError() CloudFoundryError { + return CloudFoundryError{ + Code: 21008, + ErrorCode: "CF-OutOfRouterGroupPorts", + Description: "There are no more ports available for router group: %s. Please contact your administrator for more information.", + } +} + +// IsOutOfRouterGroupPortsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 21008 +// - HTTP code: 403 +// - message: "There are no more ports available for router group: %s. Please contact your administrator for more information." +func IsOutOfRouterGroupPortsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 21008 +} + +// NewOrganizationInvalidError returns a new CloudFoundryError +// that IsOrganizationInvalidError will return true for +func NewOrganizationInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 30001, + ErrorCode: "CF-OrganizationInvalid", + Description: "The organization info is invalid: %s", + } +} + +// IsOrganizationInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30001 +// - HTTP code: 400 +// - message: "The organization info is invalid: %s" +func IsOrganizationInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30001 +} + +// NewOrganizationNameTakenError returns a new CloudFoundryError +// that IsOrganizationNameTakenError will return true for +func NewOrganizationNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 30002, + ErrorCode: "CF-OrganizationNameTaken", + Description: "The organization name is taken: %s", + } +} + +// IsOrganizationNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30002 +// - HTTP code: 400 +// - message: "The organization name is taken: %s" +func IsOrganizationNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30002 +} + +// NewOrganizationNotFoundError returns a new CloudFoundryError +// that IsOrganizationNotFoundError will return true for +func NewOrganizationNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 30003, + ErrorCode: "CF-OrganizationNotFound", + Description: "The organization could not be found: %s", + } +} + +// IsOrganizationNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30003 +// - HTTP code: 404 +// - message: "The organization could not be found: %s" +func IsOrganizationNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30003 +} + +// NewLastManagerInOrgError returns a new CloudFoundryError +// that IsLastManagerInOrgError will return true for +func NewLastManagerInOrgError() CloudFoundryError { + return CloudFoundryError{ + Code: 30004, + ErrorCode: "CF-LastManagerInOrg", + Description: "Cannot remove last Org Manager in org", + } +} + +// IsLastManagerInOrgError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30004 +// - HTTP code: 403 +// - message: "Cannot remove last Org Manager in org" +func IsLastManagerInOrgError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30004 +} + +// NewLastBillingManagerInOrgError returns a new CloudFoundryError +// that IsLastBillingManagerInOrgError will return true for +func NewLastBillingManagerInOrgError() CloudFoundryError { + return CloudFoundryError{ + Code: 30005, + ErrorCode: "CF-LastBillingManagerInOrg", + Description: "Cannot remove last Billing Manager in org", + } +} + +// IsLastBillingManagerInOrgError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30005 +// - HTTP code: 403 +// - message: "Cannot remove last Billing Manager in org" +func IsLastBillingManagerInOrgError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30005 +} + +// NewLastUserInOrgError returns a new CloudFoundryError +// that IsLastUserInOrgError will return true for +func NewLastUserInOrgError() CloudFoundryError { + return CloudFoundryError{ + Code: 30006, + ErrorCode: "CF-LastUserInOrg", + Description: "Cannot remove last User in org", + } +} + +// IsLastUserInOrgError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30006 +// - HTTP code: 403 +// - message: "Cannot remove last User in org" +func IsLastUserInOrgError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30006 +} + +// NewOrganizationAlreadySetError returns a new CloudFoundryError +// that IsOrganizationAlreadySetError will return true for +func NewOrganizationAlreadySetError() CloudFoundryError { + return CloudFoundryError{ + Code: 30007, + ErrorCode: "CF-OrganizationAlreadySet", + Description: "Cannot change organization", + } +} + +// IsOrganizationAlreadySetError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30007 +// - HTTP code: 400 +// - message: "Cannot change organization" +func IsOrganizationAlreadySetError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30007 +} + +// NewSpaceInvalidError returns a new CloudFoundryError +// that IsSpaceInvalidError will return true for +func NewSpaceInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 40001, + ErrorCode: "CF-SpaceInvalid", + Description: "The app space info is invalid: %s", + } +} + +// IsSpaceInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 40001 +// - HTTP code: 400 +// - message: "The app space info is invalid: %s" +func IsSpaceInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 40001 +} + +// NewSpaceNameTakenError returns a new CloudFoundryError +// that IsSpaceNameTakenError will return true for +func NewSpaceNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 40002, + ErrorCode: "CF-SpaceNameTaken", + Description: "The app space name is taken: %s", + } +} + +// IsSpaceNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 40002 +// - HTTP code: 400 +// - message: "The app space name is taken: %s" +func IsSpaceNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 40002 +} + +// NewSpaceUserNotInOrgError returns a new CloudFoundryError +// that IsSpaceUserNotInOrgError will return true for +func NewSpaceUserNotInOrgError() CloudFoundryError { + return CloudFoundryError{ + Code: 40003, + ErrorCode: "CF-SpaceUserNotInOrg", + Description: "The app space and the user are not in the same org: %s", + } +} + +// IsSpaceUserNotInOrgError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 40003 +// - HTTP code: 400 +// - message: "The app space and the user are not in the same org: %s" +func IsSpaceUserNotInOrgError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 40003 +} + +// NewSpaceNotFoundError returns a new CloudFoundryError +// that IsSpaceNotFoundError will return true for +func NewSpaceNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 40004, + ErrorCode: "CF-SpaceNotFound", + Description: "The app space could not be found: %s", + } +} + +// IsSpaceNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 40004 +// - HTTP code: 404 +// - message: "The app space could not be found: %s" +func IsSpaceNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 40004 +} + +// NewServiceInstanceNameEmptyError returns a new CloudFoundryError +// that IsServiceInstanceNameEmptyError will return true for +func NewServiceInstanceNameEmptyError() CloudFoundryError { + return CloudFoundryError{ + Code: 60001, + ErrorCode: "CF-ServiceInstanceNameEmpty", + Description: "Service instance name is required.", + } +} + +// IsServiceInstanceNameEmptyError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60001 +// - HTTP code: 400 +// - message: "Service instance name is required." +func IsServiceInstanceNameEmptyError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60001 +} + +// NewServiceInstanceNameTakenError returns a new CloudFoundryError +// that IsServiceInstanceNameTakenError will return true for +func NewServiceInstanceNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 60002, + ErrorCode: "CF-ServiceInstanceNameTaken", + Description: "The service instance name is taken: %s", + } +} + +// IsServiceInstanceNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60002 +// - HTTP code: 400 +// - message: "The service instance name is taken: %s" +func IsServiceInstanceNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60002 +} + +// NewServiceInstanceInvalidError returns a new CloudFoundryError +// that IsServiceInstanceInvalidError will return true for +func NewServiceInstanceInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 60003, + ErrorCode: "CF-ServiceInstanceInvalid", + Description: "The service instance is invalid: %s", + } +} + +// IsServiceInstanceInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60003 +// - HTTP code: 400 +// - message: "The service instance is invalid: %s" +func IsServiceInstanceInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60003 +} + +// NewServiceInstanceNotFoundError returns a new CloudFoundryError +// that IsServiceInstanceNotFoundError will return true for +func NewServiceInstanceNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 60004, + ErrorCode: "CF-ServiceInstanceNotFound", + Description: "The service instance could not be found: %s", + } +} + +// IsServiceInstanceNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60004 +// - HTTP code: 404 +// - message: "The service instance could not be found: %s" +func IsServiceInstanceNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60004 +} + +// NewServiceInstanceQuotaExceededError returns a new CloudFoundryError +// that IsServiceInstanceQuotaExceededError will return true for +func NewServiceInstanceQuotaExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 60005, + ErrorCode: "CF-ServiceInstanceQuotaExceeded", + Description: "You have exceeded your organization's services limit.", + } +} + +// IsServiceInstanceQuotaExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60005 +// - HTTP code: 400 +// - message: "You have exceeded your organization's services limit." +func IsServiceInstanceQuotaExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60005 +} + +// NewPreviouslyUsedAs_ServiceInstancePaidQuotaExceededError returns a new CloudFoundryError +// that IsPreviouslyUsedAs_ServiceInstancePaidQuotaExceededError will return true for +func NewPreviouslyUsedAs_ServiceInstancePaidQuotaExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 60006, + ErrorCode: "CF-PreviouslyUsedAs_ServiceInstancePaidQuotaExceeded", + Description: "You have exceeded your organization's services limit.", + } +} + +// IsPreviouslyUsedAs_ServiceInstancePaidQuotaExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60006 +// - HTTP code: 400 +// - message: "You have exceeded your organization's services limit." +func IsPreviouslyUsedAs_ServiceInstancePaidQuotaExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60006 +} + +// NewServiceInstanceServicePlanNotAllowedError returns a new CloudFoundryError +// that IsServiceInstanceServicePlanNotAllowedError will return true for +func NewServiceInstanceServicePlanNotAllowedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60007, + ErrorCode: "CF-ServiceInstanceServicePlanNotAllowed", + Description: "The service instance cannot be created because paid service plans are not allowed.", + } +} + +// IsServiceInstanceServicePlanNotAllowedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60007 +// - HTTP code: 400 +// - message: "The service instance cannot be created because paid service plans are not allowed." +func IsServiceInstanceServicePlanNotAllowedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60007 +} + +// NewServiceInstanceDuplicateNotAllowedError returns a new CloudFoundryError +// that IsServiceInstanceDuplicateNotAllowedError will return true for +func NewServiceInstanceDuplicateNotAllowedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60008, + ErrorCode: "CF-ServiceInstanceDuplicateNotAllowed", + Description: "An instance of this service is already present in this space. Some services only support one instance per space.", + } +} + +// IsServiceInstanceDuplicateNotAllowedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60008 +// - HTTP code: 400 +// - message: "An instance of this service is already present in this space. Some services only support one instance per space." +func IsServiceInstanceDuplicateNotAllowedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60008 +} + +// NewServiceInstanceNameTooLongError returns a new CloudFoundryError +// that IsServiceInstanceNameTooLongError will return true for +func NewServiceInstanceNameTooLongError() CloudFoundryError { + return CloudFoundryError{ + Code: 60009, + ErrorCode: "CF-ServiceInstanceNameTooLong", + Description: "You have requested an invalid service instance name. Names are limited to 255 characters.", + } +} + +// IsServiceInstanceNameTooLongError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60009 +// - HTTP code: 400 +// - message: "You have requested an invalid service instance name. Names are limited to 255 characters." +func IsServiceInstanceNameTooLongError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60009 +} + +// NewServiceInstanceOrganizationNotAuthorizedError returns a new CloudFoundryError +// that IsServiceInstanceOrganizationNotAuthorizedError will return true for +func NewServiceInstanceOrganizationNotAuthorizedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60010, + ErrorCode: "CF-ServiceInstanceOrganizationNotAuthorized", + Description: "A service instance for the selected plan cannot be created in this organization. The plan is visible because another organization you belong to has access to it.", + } +} + +// IsServiceInstanceOrganizationNotAuthorizedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60010 +// - HTTP code: 403 +// - message: "A service instance for the selected plan cannot be created in this organization. The plan is visible because another organization you belong to has access to it." +func IsServiceInstanceOrganizationNotAuthorizedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60010 +} + +// NewServiceInstanceDeprovisionFailedError returns a new CloudFoundryError +// that IsServiceInstanceDeprovisionFailedError will return true for +func NewServiceInstanceDeprovisionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60011, + ErrorCode: "CF-ServiceInstanceDeprovisionFailed", + Description: "The service broker reported an error during deprovisioning: %s", + } +} + +// IsServiceInstanceDeprovisionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60011 +// - HTTP code: 409 +// - message: "The service broker reported an error during deprovisioning: %s" +func IsServiceInstanceDeprovisionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60011 +} + +// NewServiceInstanceSpaceQuotaExceededError returns a new CloudFoundryError +// that IsServiceInstanceSpaceQuotaExceededError will return true for +func NewServiceInstanceSpaceQuotaExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 60012, + ErrorCode: "CF-ServiceInstanceSpaceQuotaExceeded", + Description: "You have exceeded your space's services limit.", + } +} + +// IsServiceInstanceSpaceQuotaExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60012 +// - HTTP code: 400 +// - message: "You have exceeded your space's services limit." +func IsServiceInstanceSpaceQuotaExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60012 +} + +// NewServiceInstanceServicePlanNotAllowedBySpaceQuotaError returns a new CloudFoundryError +// that IsServiceInstanceServicePlanNotAllowedBySpaceQuotaError will return true for +func NewServiceInstanceServicePlanNotAllowedBySpaceQuotaError() CloudFoundryError { + return CloudFoundryError{ + Code: 60013, + ErrorCode: "CF-ServiceInstanceServicePlanNotAllowedBySpaceQuota", + Description: "The service instance cannot be created because paid service plans are not allowed for your space.", + } +} + +// IsServiceInstanceServicePlanNotAllowedBySpaceQuotaError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60013 +// - HTTP code: 400 +// - message: "The service instance cannot be created because paid service plans are not allowed for your space." +func IsServiceInstanceServicePlanNotAllowedBySpaceQuotaError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60013 +} + +// NewServiceInstanceSpaceChangeNotAllowedError returns a new CloudFoundryError +// that IsServiceInstanceSpaceChangeNotAllowedError will return true for +func NewServiceInstanceSpaceChangeNotAllowedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60014, + ErrorCode: "CF-ServiceInstanceSpaceChangeNotAllowed", + Description: "Cannot update space for service instance.", + } +} + +// IsServiceInstanceSpaceChangeNotAllowedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60014 +// - HTTP code: 400 +// - message: "Cannot update space for service instance." +func IsServiceInstanceSpaceChangeNotAllowedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60014 +} + +// NewServiceInstanceTagsTooLongError returns a new CloudFoundryError +// that IsServiceInstanceTagsTooLongError will return true for +func NewServiceInstanceTagsTooLongError() CloudFoundryError { + return CloudFoundryError{ + Code: 60015, + ErrorCode: "CF-ServiceInstanceTagsTooLong", + Description: "Combined length of tags for service %s must be 2048 characters or less.", + } +} + +// IsServiceInstanceTagsTooLongError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60015 +// - HTTP code: 400 +// - message: "Combined length of tags for service %s must be 2048 characters or less." +func IsServiceInstanceTagsTooLongError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60015 +} + +// NewAsyncServiceInstanceOperationInProgressError returns a new CloudFoundryError +// that IsAsyncServiceInstanceOperationInProgressError will return true for +func NewAsyncServiceInstanceOperationInProgressError() CloudFoundryError { + return CloudFoundryError{ + Code: 60016, + ErrorCode: "CF-AsyncServiceInstanceOperationInProgress", + Description: "An operation for service instance %s is in progress.", + } +} + +// IsAsyncServiceInstanceOperationInProgressError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60016 +// - HTTP code: 409 +// - message: "An operation for service instance %s is in progress." +func IsAsyncServiceInstanceOperationInProgressError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60016 +} + +// NewServiceInstanceRouteBindingSpaceMismatchError returns a new CloudFoundryError +// that IsServiceInstanceRouteBindingSpaceMismatchError will return true for +func NewServiceInstanceRouteBindingSpaceMismatchError() CloudFoundryError { + return CloudFoundryError{ + Code: 60017, + ErrorCode: "CF-ServiceInstanceRouteBindingSpaceMismatch", + Description: "The service instance and the route are in different spaces.", + } +} + +// IsServiceInstanceRouteBindingSpaceMismatchError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60017 +// - HTTP code: 400 +// - message: "The service instance and the route are in different spaces." +func IsServiceInstanceRouteBindingSpaceMismatchError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60017 +} + +// NewServiceInstanceSpaceNotAuthorizedError returns a new CloudFoundryError +// that IsServiceInstanceSpaceNotAuthorizedError will return true for +func NewServiceInstanceSpaceNotAuthorizedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60018, + ErrorCode: "CF-ServiceInstanceSpaceNotAuthorized", + Description: "A service instance for the selected plan cannot be created in this space.", + } +} + +// IsServiceInstanceSpaceNotAuthorizedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60018 +// - HTTP code: 403 +// - message: "A service instance for the selected plan cannot be created in this space." +func IsServiceInstanceSpaceNotAuthorizedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60018 +} + +// NewServiceInstanceRouteServiceURLInvalidError returns a new CloudFoundryError +// that IsServiceInstanceRouteServiceURLInvalidError will return true for +func NewServiceInstanceRouteServiceURLInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 60019, + ErrorCode: "CF-ServiceInstanceRouteServiceURLInvalid", + Description: "The route service URL is invalid: %s", + } +} + +// IsServiceInstanceRouteServiceURLInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60019 +// - HTTP code: 400 +// - message: "The route service URL is invalid: %s" +func IsServiceInstanceRouteServiceURLInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60019 +} + +// NewServiceInstanceRouteServiceRequiresDiegoError returns a new CloudFoundryError +// that IsServiceInstanceRouteServiceRequiresDiegoError will return true for +func NewServiceInstanceRouteServiceRequiresDiegoError() CloudFoundryError { + return CloudFoundryError{ + Code: 60020, + ErrorCode: "CF-ServiceInstanceRouteServiceRequiresDiego", + Description: "Route services are only supported for apps on Diego. Unbind the service instance from the route or enable Diego for the app.", + } +} + +// IsServiceInstanceRouteServiceRequiresDiegoError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60020 +// - HTTP code: 400 +// - message: "Route services are only supported for apps on Diego. Unbind the service instance from the route or enable Diego for the app." +func IsServiceInstanceRouteServiceRequiresDiegoError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60020 +} + +// NewServiceInstanceRouteServiceDisabledError returns a new CloudFoundryError +// that IsServiceInstanceRouteServiceDisabledError will return true for +func NewServiceInstanceRouteServiceDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 60021, + ErrorCode: "CF-ServiceInstanceRouteServiceDisabled", + Description: "Support for route services is disabled", + } +} + +// IsServiceInstanceRouteServiceDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60021 +// - HTTP code: 403 +// - message: "Support for route services is disabled" +func IsServiceInstanceRouteServiceDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60021 +} + +// NewAppPortMappingRequiresDiegoError returns a new CloudFoundryError +// that IsAppPortMappingRequiresDiegoError will return true for +func NewAppPortMappingRequiresDiegoError() CloudFoundryError { + return CloudFoundryError{ + Code: 60022, + ErrorCode: "CF-AppPortMappingRequiresDiego", + Description: "App ports are supported for Diego apps only.", + } +} + +// IsAppPortMappingRequiresDiegoError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60022 +// - HTTP code: 400 +// - message: "App ports are supported for Diego apps only." +func IsAppPortMappingRequiresDiegoError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60022 +} + +// NewRoutePortNotEnabledOnAppError returns a new CloudFoundryError +// that IsRoutePortNotEnabledOnAppError will return true for +func NewRoutePortNotEnabledOnAppError() CloudFoundryError { + return CloudFoundryError{ + Code: 60023, + ErrorCode: "CF-RoutePortNotEnabledOnApp", + Description: "Routes can only be mapped to ports already enabled for the application.", + } +} + +// IsRoutePortNotEnabledOnAppError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60023 +// - HTTP code: 400 +// - message: "Routes can only be mapped to ports already enabled for the application." +func IsRoutePortNotEnabledOnAppError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60023 +} + +// NewMultipleAppPortsMappedDiegoToDeaError returns a new CloudFoundryError +// that IsMultipleAppPortsMappedDiegoToDeaError will return true for +func NewMultipleAppPortsMappedDiegoToDeaError() CloudFoundryError { + return CloudFoundryError{ + Code: 60024, + ErrorCode: "CF-MultipleAppPortsMappedDiegoToDea", + Description: "The app has routes mapped to multiple ports. Multiple ports are supported for Diego only. Please unmap routes from all but one app port. Multiple routes can be mapped to the same port if desired.", + } +} + +// IsMultipleAppPortsMappedDiegoToDeaError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60024 +// - HTTP code: 400 +// - message: "The app has routes mapped to multiple ports. Multiple ports are supported for Diego only. Please unmap routes from all but one app port. Multiple routes can be mapped to the same port if desired." +func IsMultipleAppPortsMappedDiegoToDeaError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60024 +} + +// NewVolumeMountServiceDisabledError returns a new CloudFoundryError +// that IsVolumeMountServiceDisabledError will return true for +func NewVolumeMountServiceDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 60025, + ErrorCode: "CF-VolumeMountServiceDisabled", + Description: "Support for volume mount services is disabled", + } +} + +// IsVolumeMountServiceDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60025 +// - HTTP code: 403 +// - message: "Support for volume mount services is disabled" +func IsVolumeMountServiceDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60025 +} + +// NewDockerAppToDeaError returns a new CloudFoundryError +// that IsDockerAppToDeaError will return true for +func NewDockerAppToDeaError() CloudFoundryError { + return CloudFoundryError{ + Code: 60026, + ErrorCode: "CF-DockerAppToDea", + Description: "Docker apps cannot run on DEAs", + } +} + +// IsDockerAppToDeaError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60026 +// - HTTP code: 400 +// - message: "Docker apps cannot run on DEAs" +func IsDockerAppToDeaError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60026 +} + +// NewServiceInstanceRecursiveDeleteFailedError returns a new CloudFoundryError +// that IsServiceInstanceRecursiveDeleteFailedError will return true for +func NewServiceInstanceRecursiveDeleteFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60027, + ErrorCode: "CF-ServiceInstanceRecursiveDeleteFailed", + Description: "Deletion of service instance %s failed because one or more associated resources could not be deleted.%s", + } +} + +// IsServiceInstanceRecursiveDeleteFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60027 +// - HTTP code: 502 +// - message: "Deletion of service instance %s failed because one or more associated resources could not be deleted.\n\n%s" +func IsServiceInstanceRecursiveDeleteFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60027 +} + +// NewManagedServiceInstanceNotFoundError returns a new CloudFoundryError +// that IsManagedServiceInstanceNotFoundError will return true for +func NewManagedServiceInstanceNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 60028, + ErrorCode: "CF-ManagedServiceInstanceNotFound", + Description: "The service instance could not be found: %s", + } +} + +// IsManagedServiceInstanceNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60028 +// - HTTP code: 404 +// - message: "The service instance could not be found: %s" +func IsManagedServiceInstanceNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60028 +} + +// NewServiceInstanceWithInaccessiblePlanNotUpdateableError returns a new CloudFoundryError +// that IsServiceInstanceWithInaccessiblePlanNotUpdateableError will return true for +func NewServiceInstanceWithInaccessiblePlanNotUpdateableError() CloudFoundryError { + return CloudFoundryError{ + Code: 60029, + ErrorCode: "CF-ServiceInstanceWithInaccessiblePlanNotUpdateable", + Description: "Cannot update %s of a service instance that belongs to inaccessible plan", + } +} + +// IsServiceInstanceWithInaccessiblePlanNotUpdateableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60029 +// - HTTP code: 403 +// - message: "Cannot update %s of a service instance that belongs to inaccessible plan" +func IsServiceInstanceWithInaccessiblePlanNotUpdateableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60029 +} + +// NewServiceInstanceProvisionFailedError returns a new CloudFoundryError +// that IsServiceInstanceProvisionFailedError will return true for +func NewServiceInstanceProvisionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60030, + ErrorCode: "CF-ServiceInstanceProvisionFailed", + Description: "The service broker reported an error during provisioning: %s", + } +} + +// IsServiceInstanceProvisionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60030 +// - HTTP code: 400 +// - message: "The service broker reported an error during provisioning: %s" +func IsServiceInstanceProvisionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60030 +} + +// NewRuntimeInvalidError returns a new CloudFoundryError +// that IsRuntimeInvalidError will return true for +func NewRuntimeInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 70001, + ErrorCode: "CF-RuntimeInvalid", + Description: "The runtime is invalid: %s", + } +} + +// IsRuntimeInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 70001 +// - HTTP code: 400 +// - message: "The runtime is invalid: %s" +func IsRuntimeInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 70001 +} + +// NewRuntimeNameTakenError returns a new CloudFoundryError +// that IsRuntimeNameTakenError will return true for +func NewRuntimeNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 70002, + ErrorCode: "CF-RuntimeNameTaken", + Description: "The runtime name is taken: %s", + } +} + +// IsRuntimeNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 70002 +// - HTTP code: 400 +// - message: "The runtime name is taken: %s" +func IsRuntimeNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 70002 +} + +// NewRuntimeNotFoundError returns a new CloudFoundryError +// that IsRuntimeNotFoundError will return true for +func NewRuntimeNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 70003, + ErrorCode: "CF-RuntimeNotFound", + Description: "The runtime could not be found: %s", + } +} + +// IsRuntimeNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 70003 +// - HTTP code: 404 +// - message: "The runtime could not be found: %s" +func IsRuntimeNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 70003 +} + +// NewFrameworkInvalidError returns a new CloudFoundryError +// that IsFrameworkInvalidError will return true for +func NewFrameworkInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 80001, + ErrorCode: "CF-FrameworkInvalid", + Description: "The framework is invalid: %s", + } +} + +// IsFrameworkInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 80001 +// - HTTP code: 400 +// - message: "The framework is invalid: %s" +func IsFrameworkInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 80001 +} + +// NewFrameworkNameTakenError returns a new CloudFoundryError +// that IsFrameworkNameTakenError will return true for +func NewFrameworkNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 80002, + ErrorCode: "CF-FrameworkNameTaken", + Description: "The framework name is taken: %s", + } +} + +// IsFrameworkNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 80002 +// - HTTP code: 400 +// - message: "The framework name is taken: %s" +func IsFrameworkNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 80002 +} + +// NewFrameworkNotFoundError returns a new CloudFoundryError +// that IsFrameworkNotFoundError will return true for +func NewFrameworkNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 80003, + ErrorCode: "CF-FrameworkNotFound", + Description: "The framework could not be found: %s", + } +} + +// IsFrameworkNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 80003 +// - HTTP code: 404 +// - message: "The framework could not be found: %s" +func IsFrameworkNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 80003 +} + +// NewServiceBindingInvalidError returns a new CloudFoundryError +// that IsServiceBindingInvalidError will return true for +func NewServiceBindingInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 90001, + ErrorCode: "CF-ServiceBindingInvalid", + Description: "The service binding is invalid: %s", + } +} + +// IsServiceBindingInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90001 +// - HTTP code: 400 +// - message: "The service binding is invalid: %s" +func IsServiceBindingInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90001 +} + +// NewServiceBindingDifferentSpacesError returns a new CloudFoundryError +// that IsServiceBindingDifferentSpacesError will return true for +func NewServiceBindingDifferentSpacesError() CloudFoundryError { + return CloudFoundryError{ + Code: 90002, + ErrorCode: "CF-ServiceBindingDifferentSpaces", + Description: "The app and the service are not in the same app space: %s", + } +} + +// IsServiceBindingDifferentSpacesError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90002 +// - HTTP code: 400 +// - message: "The app and the service are not in the same app space: %s" +func IsServiceBindingDifferentSpacesError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90002 +} + +// NewServiceBindingAppServiceTakenError returns a new CloudFoundryError +// that IsServiceBindingAppServiceTakenError will return true for +func NewServiceBindingAppServiceTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 90003, + ErrorCode: "CF-ServiceBindingAppServiceTaken", + Description: "%s", + } +} + +// IsServiceBindingAppServiceTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90003 +// - HTTP code: 400 +// - message: "%s" +func IsServiceBindingAppServiceTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90003 +} + +// NewServiceBindingNotFoundError returns a new CloudFoundryError +// that IsServiceBindingNotFoundError will return true for +func NewServiceBindingNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 90004, + ErrorCode: "CF-ServiceBindingNotFound", + Description: "The service binding could not be found: %s", + } +} + +// IsServiceBindingNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90004 +// - HTTP code: 404 +// - message: "The service binding could not be found: %s" +func IsServiceBindingNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90004 +} + +// NewUnbindableServiceError returns a new CloudFoundryError +// that IsUnbindableServiceError will return true for +func NewUnbindableServiceError() CloudFoundryError { + return CloudFoundryError{ + Code: 90005, + ErrorCode: "CF-UnbindableService", + Description: "The service instance doesn't support binding.", + } +} + +// IsUnbindableServiceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90005 +// - HTTP code: 400 +// - message: "The service instance doesn't support binding." +func IsUnbindableServiceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90005 +} + +// NewInvalidLoggingServiceBindingError returns a new CloudFoundryError +// that IsInvalidLoggingServiceBindingError will return true for +func NewInvalidLoggingServiceBindingError() CloudFoundryError { + return CloudFoundryError{ + Code: 90006, + ErrorCode: "CF-InvalidLoggingServiceBinding", + Description: "The service is attempting to stream logs from your application, but is not registered as a logging service. Please contact the service provider.", + } +} + +// IsInvalidLoggingServiceBindingError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90006 +// - HTTP code: 502 +// - message: "The service is attempting to stream logs from your application, but is not registered as a logging service. Please contact the service provider." +func IsInvalidLoggingServiceBindingError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90006 +} + +// NewServiceFetchBindingParametersNotSupportedError returns a new CloudFoundryError +// that IsServiceFetchBindingParametersNotSupportedError will return true for +func NewServiceFetchBindingParametersNotSupportedError() CloudFoundryError { + return CloudFoundryError{ + Code: 90007, + ErrorCode: "CF-ServiceFetchBindingParametersNotSupported", + Description: "This service does not support fetching service binding parameters.", + } +} + +// IsServiceFetchBindingParametersNotSupportedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90007 +// - HTTP code: 400 +// - message: "This service does not support fetching service binding parameters." +func IsServiceFetchBindingParametersNotSupportedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90007 +} + +// NewAsyncServiceBindingOperationInProgressError returns a new CloudFoundryError +// that IsAsyncServiceBindingOperationInProgressError will return true for +func NewAsyncServiceBindingOperationInProgressError() CloudFoundryError { + return CloudFoundryError{ + Code: 90008, + ErrorCode: "CF-AsyncServiceBindingOperationInProgress", + Description: "An operation for the service binding between app %s and service instance %s is in progress.", + } +} + +// IsAsyncServiceBindingOperationInProgressError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90008 +// - HTTP code: 409 +// - message: "An operation for the service binding between app %s and service instance %s is in progress." +func IsAsyncServiceBindingOperationInProgressError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90008 +} + +// NewAppInvalidError returns a new CloudFoundryError +// that IsAppInvalidError will return true for +func NewAppInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 100001, + ErrorCode: "CF-AppInvalid", + Description: "The app is invalid: %s", + } +} + +// IsAppInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100001 +// - HTTP code: 400 +// - message: "The app is invalid: %s" +func IsAppInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100001 +} + +// NewAppNameTakenError returns a new CloudFoundryError +// that IsAppNameTakenError will return true for +func NewAppNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 100002, + ErrorCode: "CF-AppNameTaken", + Description: "The app name is taken: %s", + } +} + +// IsAppNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100002 +// - HTTP code: 400 +// - message: "The app name is taken: %s" +func IsAppNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100002 +} + +// NewAppNotFoundError returns a new CloudFoundryError +// that IsAppNotFoundError will return true for +func NewAppNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 100004, + ErrorCode: "CF-AppNotFound", + Description: "The app could not be found: %s", + } +} + +// IsAppNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100004 +// - HTTP code: 404 +// - message: "The app could not be found: %s" +func IsAppNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100004 +} + +// NewAppMemoryQuotaExceededError returns a new CloudFoundryError +// that IsAppMemoryQuotaExceededError will return true for +func NewAppMemoryQuotaExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 100005, + ErrorCode: "CF-AppMemoryQuotaExceeded", + Description: "You have exceeded your organization's memory limit: %s", + } +} + +// IsAppMemoryQuotaExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100005 +// - HTTP code: 400 +// - message: "You have exceeded your organization's memory limit: %s" +func IsAppMemoryQuotaExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100005 +} + +// NewAppMemoryInvalidError returns a new CloudFoundryError +// that IsAppMemoryInvalidError will return true for +func NewAppMemoryInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 100006, + ErrorCode: "CF-AppMemoryInvalid", + Description: "You have specified an invalid amount of memory for your application.", + } +} + +// IsAppMemoryInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100006 +// - HTTP code: 400 +// - message: "You have specified an invalid amount of memory for your application." +func IsAppMemoryInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100006 +} + +// NewQuotaInstanceMemoryLimitExceededError returns a new CloudFoundryError +// that IsQuotaInstanceMemoryLimitExceededError will return true for +func NewQuotaInstanceMemoryLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 100007, + ErrorCode: "CF-QuotaInstanceMemoryLimitExceeded", + Description: "You have exceeded the instance memory limit for your organization's quota.", + } +} + +// IsQuotaInstanceMemoryLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100007 +// - HTTP code: 400 +// - message: "You have exceeded the instance memory limit for your organization's quota." +func IsQuotaInstanceMemoryLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100007 +} + +// NewQuotaInstanceLimitExceededError returns a new CloudFoundryError +// that IsQuotaInstanceLimitExceededError will return true for +func NewQuotaInstanceLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 100008, + ErrorCode: "CF-QuotaInstanceLimitExceeded", + Description: "You have exceeded the instance limit for your organization's quota.", + } +} + +// IsQuotaInstanceLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100008 +// - HTTP code: 400 +// - message: "You have exceeded the instance limit for your organization's quota." +func IsQuotaInstanceLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100008 +} + +// NewAppMemoryInsufficientForSidecarsError returns a new CloudFoundryError +// that IsAppMemoryInsufficientForSidecarsError will return true for +func NewAppMemoryInsufficientForSidecarsError() CloudFoundryError { + return CloudFoundryError{ + Code: 100009, + ErrorCode: "CF-AppMemoryInsufficientForSidecars", + Description: "The requested memory allocation is not large enough to run all of your sidecar processes.", + } +} + +// IsAppMemoryInsufficientForSidecarsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100009 +// - HTTP code: 400 +// - message: "The requested memory allocation is not large enough to run all of your sidecar processes." +func IsAppMemoryInsufficientForSidecarsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100009 +} + +// NewServicePlanInvalidError returns a new CloudFoundryError +// that IsServicePlanInvalidError will return true for +func NewServicePlanInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 110001, + ErrorCode: "CF-ServicePlanInvalid", + Description: "The service plan is invalid: %s", + } +} + +// IsServicePlanInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 110001 +// - HTTP code: 400 +// - message: "The service plan is invalid: %s" +func IsServicePlanInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 110001 +} + +// NewServicePlanNameTakenError returns a new CloudFoundryError +// that IsServicePlanNameTakenError will return true for +func NewServicePlanNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 110002, + ErrorCode: "CF-ServicePlanNameTaken", + Description: "The service plan name is taken: %s", + } +} + +// IsServicePlanNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 110002 +// - HTTP code: 400 +// - message: "The service plan name is taken: %s" +func IsServicePlanNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 110002 +} + +// NewServicePlanNotFoundError returns a new CloudFoundryError +// that IsServicePlanNotFoundError will return true for +func NewServicePlanNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 110003, + ErrorCode: "CF-ServicePlanNotFound", + Description: "The service plan could not be found: %s", + } +} + +// IsServicePlanNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 110003 +// - HTTP code: 404 +// - message: "The service plan could not be found: %s" +func IsServicePlanNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 110003 +} + +// NewServicePlanNotUpdateableError returns a new CloudFoundryError +// that IsServicePlanNotUpdateableError will return true for +func NewServicePlanNotUpdateableError() CloudFoundryError { + return CloudFoundryError{ + Code: 110004, + ErrorCode: "CF-ServicePlanNotUpdateable", + Description: "The service does not support changing plans.", + } +} + +// IsServicePlanNotUpdateableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 110004 +// - HTTP code: 400 +// - message: "The service does not support changing plans." +func IsServicePlanNotUpdateableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 110004 +} + +// NewServiceInvalidError returns a new CloudFoundryError +// that IsServiceInvalidError will return true for +func NewServiceInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 120001, + ErrorCode: "CF-ServiceInvalid", + Description: "The service is invalid: %s", + } +} + +// IsServiceInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 120001 +// - HTTP code: 400 +// - message: "The service is invalid: %s" +func IsServiceInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 120001 +} + +// NewServiceLabelTakenError returns a new CloudFoundryError +// that IsServiceLabelTakenError will return true for +func NewServiceLabelTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 120002, + ErrorCode: "CF-ServiceLabelTaken", + Description: "The service label is taken: %s", + } +} + +// IsServiceLabelTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 120002 +// - HTTP code: 400 +// - message: "The service label is taken: %s" +func IsServiceLabelTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 120002 +} + +// NewServiceNotFoundError returns a new CloudFoundryError +// that IsServiceNotFoundError will return true for +func NewServiceNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 120003, + ErrorCode: "CF-ServiceNotFound", + Description: "The service could not be found: %s", + } +} + +// IsServiceNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 120003 +// - HTTP code: 404 +// - message: "The service could not be found: %s" +func IsServiceNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 120003 +} + +// NewServiceFetchInstanceParametersNotSupportedError returns a new CloudFoundryError +// that IsServiceFetchInstanceParametersNotSupportedError will return true for +func NewServiceFetchInstanceParametersNotSupportedError() CloudFoundryError { + return CloudFoundryError{ + Code: 120004, + ErrorCode: "CF-ServiceFetchInstanceParametersNotSupported", + Description: "This service does not support fetching service instance parameters.", + } +} + +// IsServiceFetchInstanceParametersNotSupportedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 120004 +// - HTTP code: 400 +// - message: "This service does not support fetching service instance parameters." +func IsServiceFetchInstanceParametersNotSupportedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 120004 +} + +// NewDomainInvalidError returns a new CloudFoundryError +// that IsDomainInvalidError will return true for +func NewDomainInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 130001, + ErrorCode: "CF-DomainInvalid", + Description: "The domain is invalid: %s", + } +} + +// IsDomainInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130001 +// - HTTP code: 400 +// - message: "The domain is invalid: %s" +func IsDomainInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130001 +} + +// NewDomainNotFoundError returns a new CloudFoundryError +// that IsDomainNotFoundError will return true for +func NewDomainNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 130002, + ErrorCode: "CF-DomainNotFound", + Description: "The domain could not be found: %s", + } +} + +// IsDomainNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130002 +// - HTTP code: 404 +// - message: "The domain could not be found: %s" +func IsDomainNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130002 +} + +// NewDomainNameTakenError returns a new CloudFoundryError +// that IsDomainNameTakenError will return true for +func NewDomainNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 130003, + ErrorCode: "CF-DomainNameTaken", + Description: "The domain name is taken: %s", + } +} + +// IsDomainNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130003 +// - HTTP code: 400 +// - message: "The domain name is taken: %s" +func IsDomainNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130003 +} + +// NewPathInvalidError returns a new CloudFoundryError +// that IsPathInvalidError will return true for +func NewPathInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 130004, + ErrorCode: "CF-PathInvalid", + Description: "The path is invalid: %s", + } +} + +// IsPathInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130004 +// - HTTP code: 400 +// - message: "The path is invalid: %s" +func IsPathInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130004 +} + +// NewTotalPrivateDomainsExceededError returns a new CloudFoundryError +// that IsTotalPrivateDomainsExceededError will return true for +func NewTotalPrivateDomainsExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 130005, + ErrorCode: "CF-TotalPrivateDomainsExceeded", + Description: "The number of private domains exceeds the quota for organization: %s", + } +} + +// IsTotalPrivateDomainsExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130005 +// - HTTP code: 400 +// - message: "The number of private domains exceeds the quota for organization: %s" +func IsTotalPrivateDomainsExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130005 +} + +// NewServiceDoesNotSupportRoutesError returns a new CloudFoundryError +// that IsServiceDoesNotSupportRoutesError will return true for +func NewServiceDoesNotSupportRoutesError() CloudFoundryError { + return CloudFoundryError{ + Code: 130006, + ErrorCode: "CF-ServiceDoesNotSupportRoutes", + Description: "This service does not support route binding.", + } +} + +// IsServiceDoesNotSupportRoutesError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130006 +// - HTTP code: 400 +// - message: "This service does not support route binding." +func IsServiceDoesNotSupportRoutesError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130006 +} + +// NewRouteAlreadyBoundToServiceInstanceError returns a new CloudFoundryError +// that IsRouteAlreadyBoundToServiceInstanceError will return true for +func NewRouteAlreadyBoundToServiceInstanceError() CloudFoundryError { + return CloudFoundryError{ + Code: 130007, + ErrorCode: "CF-RouteAlreadyBoundToServiceInstance", + Description: "A route may only be bound to a single service instance", + } +} + +// IsRouteAlreadyBoundToServiceInstanceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130007 +// - HTTP code: 400 +// - message: "A route may only be bound to a single service instance" +func IsRouteAlreadyBoundToServiceInstanceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130007 +} + +// NewServiceInstanceAlreadyBoundToSameRouteError returns a new CloudFoundryError +// that IsServiceInstanceAlreadyBoundToSameRouteError will return true for +func NewServiceInstanceAlreadyBoundToSameRouteError() CloudFoundryError { + return CloudFoundryError{ + Code: 130008, + ErrorCode: "CF-ServiceInstanceAlreadyBoundToSameRoute", + Description: "The route and service instance are already bound.", + } +} + +// IsServiceInstanceAlreadyBoundToSameRouteError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130008 +// - HTTP code: 400 +// - message: "The route and service instance are already bound." +func IsServiceInstanceAlreadyBoundToSameRouteError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130008 +} + +// NewInternalDomainCannotBeDeletedError returns a new CloudFoundryError +// that IsInternalDomainCannotBeDeletedError will return true for +func NewInternalDomainCannotBeDeletedError() CloudFoundryError { + return CloudFoundryError{ + Code: 130009, + ErrorCode: "CF-InternalDomainCannotBeDeleted", + Description: "The domain '%s' cannot be deleted. It is reserved by the platform.", + } +} + +// IsInternalDomainCannotBeDeletedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130009 +// - HTTP code: 422 +// - message: "The domain '%s' cannot be deleted. It is reserved by the platform." +func IsInternalDomainCannotBeDeletedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130009 +} + +// NewRouteServiceCannotBeBoundToInternalRouteError returns a new CloudFoundryError +// that IsRouteServiceCannotBeBoundToInternalRouteError will return true for +func NewRouteServiceCannotBeBoundToInternalRouteError() CloudFoundryError { + return CloudFoundryError{ + Code: 130010, + ErrorCode: "CF-RouteServiceCannotBeBoundToInternalRoute", + Description: "Route services cannot be bound to internal routes.", + } +} + +// IsRouteServiceCannotBeBoundToInternalRouteError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130010 +// - HTTP code: 400 +// - message: "Route services cannot be bound to internal routes." +func IsRouteServiceCannotBeBoundToInternalRouteError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130010 +} + +// NewLegacyApiWithoutDefaultSpaceError returns a new CloudFoundryError +// that IsLegacyApiWithoutDefaultSpaceError will return true for +func NewLegacyApiWithoutDefaultSpaceError() CloudFoundryError { + return CloudFoundryError{ + Code: 140001, + ErrorCode: "CF-LegacyApiWithoutDefaultSpace", + Description: "A legacy api call requiring a default app space was called, but no default app space is set for the user.", + } +} + +// IsLegacyApiWithoutDefaultSpaceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 140001 +// - HTTP code: 400 +// - message: "A legacy api call requiring a default app space was called, but no default app space is set for the user." +func IsLegacyApiWithoutDefaultSpaceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 140001 +} + +// NewAppPackageInvalidError returns a new CloudFoundryError +// that IsAppPackageInvalidError will return true for +func NewAppPackageInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 150001, + ErrorCode: "CF-AppPackageInvalid", + Description: "The app package is invalid: %s", + } +} + +// IsAppPackageInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150001 +// - HTTP code: 400 +// - message: "The app package is invalid: %s" +func IsAppPackageInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150001 +} + +// NewAppPackageNotFoundError returns a new CloudFoundryError +// that IsAppPackageNotFoundError will return true for +func NewAppPackageNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 150002, + ErrorCode: "CF-AppPackageNotFound", + Description: "The app package could not be found: %s", + } +} + +// IsAppPackageNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150002 +// - HTTP code: 404 +// - message: "The app package could not be found: %s" +func IsAppPackageNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150002 +} + +// NewInsufficientRunningResourcesAvailableError returns a new CloudFoundryError +// that IsInsufficientRunningResourcesAvailableError will return true for +func NewInsufficientRunningResourcesAvailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 150003, + ErrorCode: "CF-InsufficientRunningResourcesAvailable", + Description: "One or more instances could not be started because of insufficient running resources.", + } +} + +// IsInsufficientRunningResourcesAvailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150003 +// - HTTP code: 503 +// - message: "One or more instances could not be started because of insufficient running resources." +func IsInsufficientRunningResourcesAvailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150003 +} + +// NewPackageBitsAlreadyUploadedError returns a new CloudFoundryError +// that IsPackageBitsAlreadyUploadedError will return true for +func NewPackageBitsAlreadyUploadedError() CloudFoundryError { + return CloudFoundryError{ + Code: 150004, + ErrorCode: "CF-PackageBitsAlreadyUploaded", + Description: "Bits may be uploaded only once. Create a new package to upload different bits.", + } +} + +// IsPackageBitsAlreadyUploadedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150004 +// - HTTP code: 400 +// - message: "Bits may be uploaded only once. Create a new package to upload different bits." +func IsPackageBitsAlreadyUploadedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150004 +} + +// NewBlobstoreNotLocalError returns a new CloudFoundryError +// that IsBlobstoreNotLocalError will return true for +func NewBlobstoreNotLocalError() CloudFoundryError { + return CloudFoundryError{ + Code: 150005, + ErrorCode: "CF-BlobstoreNotLocal", + Description: "Downloading blobs can only be done directly to the blobstore.", + } +} + +// IsBlobstoreNotLocalError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150005 +// - HTTP code: 400 +// - message: "Downloading blobs can only be done directly to the blobstore." +func IsBlobstoreNotLocalError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150005 +} + +// NewBlobstoreUnavailableError returns a new CloudFoundryError +// that IsBlobstoreUnavailableError will return true for +func NewBlobstoreUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 150006, + ErrorCode: "CF-BlobstoreUnavailable", + Description: "Failed to perform operation due to blobstore unavailability.", + } +} + +// IsBlobstoreUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150006 +// - HTTP code: 502 +// - message: "Failed to perform operation due to blobstore unavailability." +func IsBlobstoreUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150006 +} + +// NewBlobstoreError returns a new CloudFoundryError +// that IsBlobstoreError will return true for +func NewBlobstoreError() CloudFoundryError { + return CloudFoundryError{ + Code: 150007, + ErrorCode: "CF-BlobstoreError", + Description: "Failed to perform blobstore operation after three retries.", + } +} + +// IsBlobstoreError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150007 +// - HTTP code: 500 +// - message: "Failed to perform blobstore operation after three retries." +func IsBlobstoreError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150007 +} + +// NewDockerImageMissingError returns a new CloudFoundryError +// that IsDockerImageMissingError will return true for +func NewDockerImageMissingError() CloudFoundryError { + return CloudFoundryError{ + Code: 150008, + ErrorCode: "CF-DockerImageMissing", + Description: "Docker credentials can only be supplied for apps with a 'docker_image'", + } +} + +// IsDockerImageMissingError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150008 +// - HTTP code: 400 +// - message: "Docker credentials can only be supplied for apps with a 'docker_image'" +func IsDockerImageMissingError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150008 +} + +// NewAppRecursiveDeleteFailedError returns a new CloudFoundryError +// that IsAppRecursiveDeleteFailedError will return true for +func NewAppRecursiveDeleteFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 150009, + ErrorCode: "CF-AppRecursiveDeleteFailed", + Description: "Deletion of app %s failed because one or more associated resources could not be deleted.%s", + } +} + +// IsAppRecursiveDeleteFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150009 +// - HTTP code: 502 +// - message: "Deletion of app %s failed because one or more associated resources could not be deleted.\n\n%s" +func IsAppRecursiveDeleteFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150009 +} + +// NewAppBitsUploadInvalidError returns a new CloudFoundryError +// that IsAppBitsUploadInvalidError will return true for +func NewAppBitsUploadInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 160001, + ErrorCode: "CF-AppBitsUploadInvalid", + Description: "The app upload is invalid: %s", + } +} + +// IsAppBitsUploadInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 160001 +// - HTTP code: 400 +// - message: "The app upload is invalid: %s" +func IsAppBitsUploadInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 160001 +} + +// NewAppBitsCopyInvalidError returns a new CloudFoundryError +// that IsAppBitsCopyInvalidError will return true for +func NewAppBitsCopyInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 160002, + ErrorCode: "CF-AppBitsCopyInvalid", + Description: "The app copy is invalid: %s", + } +} + +// IsAppBitsCopyInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 160002 +// - HTTP code: 400 +// - message: "The app copy is invalid: %s" +func IsAppBitsCopyInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 160002 +} + +// NewAppResourcesFileModeInvalidError returns a new CloudFoundryError +// that IsAppResourcesFileModeInvalidError will return true for +func NewAppResourcesFileModeInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 160003, + ErrorCode: "CF-AppResourcesFileModeInvalid", + Description: "The resource file mode is invalid: %s", + } +} + +// IsAppResourcesFileModeInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 160003 +// - HTTP code: 400 +// - message: "The resource file mode is invalid: %s" +func IsAppResourcesFileModeInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 160003 +} + +// NewAppResourcesFilePathInvalidError returns a new CloudFoundryError +// that IsAppResourcesFilePathInvalidError will return true for +func NewAppResourcesFilePathInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 160004, + ErrorCode: "CF-AppResourcesFilePathInvalid", + Description: "The resource file path is invalid: %s", + } +} + +// IsAppResourcesFilePathInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 160004 +// - HTTP code: 400 +// - message: "The resource file path is invalid: %s" +func IsAppResourcesFilePathInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 160004 +} + +// NewStagingError returns a new CloudFoundryError +// that IsStagingError will return true for +func NewStagingError() CloudFoundryError { + return CloudFoundryError{ + Code: 170001, + ErrorCode: "CF-StagingError", + Description: "Staging error: %s", + } +} + +// IsStagingError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170001 +// - HTTP code: 400 +// - message: "Staging error: %s" +func IsStagingError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170001 +} + +// NewNotStagedError returns a new CloudFoundryError +// that IsNotStagedError will return true for +func NewNotStagedError() CloudFoundryError { + return CloudFoundryError{ + Code: 170002, + ErrorCode: "CF-NotStaged", + Description: "App has not finished staging", + } +} + +// IsNotStagedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170002 +// - HTTP code: 400 +// - message: "App has not finished staging" +func IsNotStagedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170002 +} + +// NewNoAppDetectedError returns a new CloudFoundryError +// that IsNoAppDetectedError will return true for +func NewNoAppDetectedError() CloudFoundryError { + return CloudFoundryError{ + Code: 170003, + ErrorCode: "CF-NoAppDetectedError", + Description: "An app was not successfully detected by any available buildpack", + } +} + +// IsNoAppDetectedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170003 +// - HTTP code: 400 +// - message: "An app was not successfully detected by any available buildpack" +func IsNoAppDetectedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170003 +} + +// NewBuildpackCompileFailedError returns a new CloudFoundryError +// that IsBuildpackCompileFailedError will return true for +func NewBuildpackCompileFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 170004, + ErrorCode: "CF-BuildpackCompileFailed", + Description: "App staging failed in the buildpack compile phase", + } +} + +// IsBuildpackCompileFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170004 +// - HTTP code: 400 +// - message: "App staging failed in the buildpack compile phase" +func IsBuildpackCompileFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170004 +} + +// NewBuildpackReleaseFailedError returns a new CloudFoundryError +// that IsBuildpackReleaseFailedError will return true for +func NewBuildpackReleaseFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 170005, + ErrorCode: "CF-BuildpackReleaseFailed", + Description: "App staging failed in the buildpack release phase", + } +} + +// IsBuildpackReleaseFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170005 +// - HTTP code: 400 +// - message: "App staging failed in the buildpack release phase" +func IsBuildpackReleaseFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170005 +} + +// NewNoBuildpacksFoundError returns a new CloudFoundryError +// that IsNoBuildpacksFoundError will return true for +func NewNoBuildpacksFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 170006, + ErrorCode: "CF-NoBuildpacksFound", + Description: "There are no buildpacks available", + } +} + +// IsNoBuildpacksFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170006 +// - HTTP code: 400 +// - message: "There are no buildpacks available" +func IsNoBuildpacksFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170006 +} + +// NewStagingTimeExpiredError returns a new CloudFoundryError +// that IsStagingTimeExpiredError will return true for +func NewStagingTimeExpiredError() CloudFoundryError { + return CloudFoundryError{ + Code: 170007, + ErrorCode: "CF-StagingTimeExpired", + Description: "Staging time expired: %s", + } +} + +// IsStagingTimeExpiredError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170007 +// - HTTP code: 504 +// - message: "Staging time expired: %s" +func IsStagingTimeExpiredError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170007 +} + +// NewInsufficientResourcesError returns a new CloudFoundryError +// that IsInsufficientResourcesError will return true for +func NewInsufficientResourcesError() CloudFoundryError { + return CloudFoundryError{ + Code: 170008, + ErrorCode: "CF-InsufficientResources", + Description: "Insufficient resources", + } +} + +// IsInsufficientResourcesError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170008 +// - HTTP code: 400 +// - message: "Insufficient resources" +func IsInsufficientResourcesError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170008 +} + +// NewNoCompatibleCellError returns a new CloudFoundryError +// that IsNoCompatibleCellError will return true for +func NewNoCompatibleCellError() CloudFoundryError { + return CloudFoundryError{ + Code: 170009, + ErrorCode: "CF-NoCompatibleCell", + Description: "Found no compatible cell", + } +} + +// IsNoCompatibleCellError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170009 +// - HTTP code: 400 +// - message: "Found no compatible cell" +func IsNoCompatibleCellError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170009 +} + +// NewStagerUnavailableError returns a new CloudFoundryError +// that IsStagerUnavailableError will return true for +func NewStagerUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 170010, + ErrorCode: "CF-StagerUnavailable", + Description: "Stager is unavailable: %s", + } +} + +// IsStagerUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170010 +// - HTTP code: 503 +// - message: "Stager is unavailable: %s" +func IsStagerUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170010 +} + +// NewStagerError returns a new CloudFoundryError +// that IsStagerError will return true for +func NewStagerError() CloudFoundryError { + return CloudFoundryError{ + Code: 170011, + ErrorCode: "CF-StagerError", + Description: "Stager error: %s", + } +} + +// IsStagerError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170011 +// - HTTP code: 500 +// - message: "Stager error: %s" +func IsStagerError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170011 +} + +// NewRunnerInvalidRequestError returns a new CloudFoundryError +// that IsRunnerInvalidRequestError will return true for +func NewRunnerInvalidRequestError() CloudFoundryError { + return CloudFoundryError{ + Code: 170014, + ErrorCode: "CF-RunnerInvalidRequest", + Description: "Runner invalid request: %s", + } +} + +// IsRunnerInvalidRequestError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170014 +// - HTTP code: 500 +// - message: "Runner invalid request: %s" +func IsRunnerInvalidRequestError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170014 +} + +// NewRunnerUnavailableError returns a new CloudFoundryError +// that IsRunnerUnavailableError will return true for +func NewRunnerUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 170015, + ErrorCode: "CF-RunnerUnavailable", + Description: "Runner is unavailable: %s", + } +} + +// IsRunnerUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170015 +// - HTTP code: 503 +// - message: "Runner is unavailable: %s" +func IsRunnerUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170015 +} + +// NewRunnerError returns a new CloudFoundryError +// that IsRunnerError will return true for +func NewRunnerError() CloudFoundryError { + return CloudFoundryError{ + Code: 170016, + ErrorCode: "CF-RunnerError", + Description: "Runner error: %s", + } +} + +// IsRunnerError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170016 +// - HTTP code: 500 +// - message: "Runner error: %s" +func IsRunnerError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170016 +} + +// NewStagingInProgressError returns a new CloudFoundryError +// that IsStagingInProgressError will return true for +func NewStagingInProgressError() CloudFoundryError { + return CloudFoundryError{ + Code: 170017, + ErrorCode: "CF-StagingInProgress", + Description: "Only one build can be STAGING at a time per application.", + } +} + +// IsStagingInProgressError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170017 +// - HTTP code: 422 +// - message: "Only one build can be STAGING at a time per application." +func IsStagingInProgressError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170017 +} + +// NewInvalidTaskAddressError returns a new CloudFoundryError +// that IsInvalidTaskAddressError will return true for +func NewInvalidTaskAddressError() CloudFoundryError { + return CloudFoundryError{ + Code: 170018, + ErrorCode: "CF-InvalidTaskAddress", + Description: "Invalid config: %s", + } +} + +// IsInvalidTaskAddressError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170018 +// - HTTP code: 500 +// - message: "Invalid config: %s" +func IsInvalidTaskAddressError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170018 +} + +// NewTaskError returns a new CloudFoundryError +// that IsTaskError will return true for +func NewTaskError() CloudFoundryError { + return CloudFoundryError{ + Code: 170019, + ErrorCode: "CF-TaskError", + Description: "Task failed: %s", + } +} + +// IsTaskError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170019 +// - HTTP code: 500 +// - message: "Task failed: %s" +func IsTaskError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170019 +} + +// NewTaskWorkersUnavailableError returns a new CloudFoundryError +// that IsTaskWorkersUnavailableError will return true for +func NewTaskWorkersUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 170020, + ErrorCode: "CF-TaskWorkersUnavailable", + Description: "Task workers are unavailable: %s", + } +} + +// IsTaskWorkersUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170020 +// - HTTP code: 503 +// - message: "Task workers are unavailable: %s" +func IsTaskWorkersUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170020 +} + +// NewInvalidTaskRequestError returns a new CloudFoundryError +// that IsInvalidTaskRequestError will return true for +func NewInvalidTaskRequestError() CloudFoundryError { + return CloudFoundryError{ + Code: 170021, + ErrorCode: "CF-InvalidTaskRequest", + Description: "The task request is invalid: %s", + } +} + +// IsInvalidTaskRequestError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170021 +// - HTTP code: 422 +// - message: "The task request is invalid: %s" +func IsInvalidTaskRequestError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170021 +} + +// NewServiceGatewayError returns a new CloudFoundryError +// that IsServiceGatewayError will return true for +func NewServiceGatewayError() CloudFoundryError { + return CloudFoundryError{ + Code: 180002, + ErrorCode: "CF-ServiceGatewayError", + Description: "Service gateway internal error: %s", + } +} + +// IsServiceGatewayError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 180002 +// - HTTP code: 503 +// - message: "Service gateway internal error: %s" +func IsServiceGatewayError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 180002 +} + +// NewServiceNotImplementedError returns a new CloudFoundryError +// that IsServiceNotImplementedError will return true for +func NewServiceNotImplementedError() CloudFoundryError { + return CloudFoundryError{ + Code: 180003, + ErrorCode: "CF-ServiceNotImplemented", + Description: "Operation not supported for service", + } +} + +// IsServiceNotImplementedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 180003 +// - HTTP code: 501 +// - message: "Operation not supported for service" +func IsServiceNotImplementedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 180003 +} + +// NewSDSNotAvailableError returns a new CloudFoundryError +// that IsSDSNotAvailableError will return true for +func NewSDSNotAvailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 180004, + ErrorCode: "CF-SDSNotAvailable", + Description: "No serialization service backends available", + } +} + +// IsSDSNotAvailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 180004 +// - HTTP code: 501 +// - message: "No serialization service backends available" +func IsSDSNotAvailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 180004 +} + +// NewFileError returns a new CloudFoundryError +// that IsFileError will return true for +func NewFileError() CloudFoundryError { + return CloudFoundryError{ + Code: 190001, + ErrorCode: "CF-FileError", + Description: "File error: %s", + } +} + +// IsFileError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 190001 +// - HTTP code: 400 +// - message: "File error: %s" +func IsFileError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 190001 +} + +// NewStatsError returns a new CloudFoundryError +// that IsStatsError will return true for +func NewStatsError() CloudFoundryError { + return CloudFoundryError{ + Code: 200001, + ErrorCode: "CF-StatsError", + Description: "Stats error: %s", + } +} + +// IsStatsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 200001 +// - HTTP code: 400 +// - message: "Stats error: %s" +func IsStatsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 200001 +} + +// NewStatsUnavailableError returns a new CloudFoundryError +// that IsStatsUnavailableError will return true for +func NewStatsUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 200002, + ErrorCode: "CF-StatsUnavailable", + Description: "Stats unavailable: %s", + } +} + +// IsStatsUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 200002 +// - HTTP code: 503 +// - message: "Stats unavailable: %s" +func IsStatsUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 200002 +} + +// NewAppStoppedStatsError returns a new CloudFoundryError +// that IsAppStoppedStatsError will return true for +func NewAppStoppedStatsError() CloudFoundryError { + return CloudFoundryError{ + Code: 200003, + ErrorCode: "CF-AppStoppedStatsError", + Description: "Could not fetch stats for stopped app: %s", + } +} + +// IsAppStoppedStatsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 200003 +// - HTTP code: 400 +// - message: "Could not fetch stats for stopped app: %s" +func IsAppStoppedStatsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 200003 +} + +// NewRouteInvalidError returns a new CloudFoundryError +// that IsRouteInvalidError will return true for +func NewRouteInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 210001, + ErrorCode: "CF-RouteInvalid", + Description: "The route is invalid: %s", + } +} + +// IsRouteInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210001 +// - HTTP code: 400 +// - message: "The route is invalid: %s" +func IsRouteInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210001 +} + +// NewRouteNotFoundError returns a new CloudFoundryError +// that IsRouteNotFoundError will return true for +func NewRouteNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 210002, + ErrorCode: "CF-RouteNotFound", + Description: "The route could not be found: %s", + } +} + +// IsRouteNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210002 +// - HTTP code: 404 +// - message: "The route could not be found: %s" +func IsRouteNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210002 +} + +// NewRouteHostTakenError returns a new CloudFoundryError +// that IsRouteHostTakenError will return true for +func NewRouteHostTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 210003, + ErrorCode: "CF-RouteHostTaken", + Description: "The host is taken: %s", + } +} + +// IsRouteHostTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210003 +// - HTTP code: 400 +// - message: "The host is taken: %s" +func IsRouteHostTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210003 +} + +// NewRoutePathTakenError returns a new CloudFoundryError +// that IsRoutePathTakenError will return true for +func NewRoutePathTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 210004, + ErrorCode: "CF-RoutePathTaken", + Description: "The path is taken: %s", + } +} + +// IsRoutePathTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210004 +// - HTTP code: 400 +// - message: "The path is taken: %s" +func IsRoutePathTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210004 +} + +// NewRoutePortTakenError returns a new CloudFoundryError +// that IsRoutePortTakenError will return true for +func NewRoutePortTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 210005, + ErrorCode: "CF-RoutePortTaken", + Description: "The port is taken: %s", + } +} + +// IsRoutePortTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210005 +// - HTTP code: 400 +// - message: "The port is taken: %s" +func IsRoutePortTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210005 +} + +// NewRouteMappingTakenError returns a new CloudFoundryError +// that IsRouteMappingTakenError will return true for +func NewRouteMappingTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 210006, + ErrorCode: "CF-RouteMappingTaken", + Description: "The route mapping is taken: %s", + } +} + +// IsRouteMappingTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210006 +// - HTTP code: 400 +// - message: "The route mapping is taken: %s" +func IsRouteMappingTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210006 +} + +// NewRouteMappingNotFoundError returns a new CloudFoundryError +// that IsRouteMappingNotFoundError will return true for +func NewRouteMappingNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 210007, + ErrorCode: "CF-RouteMappingNotFound", + Description: "The route mapping could not be found: %s", + } +} + +// IsRouteMappingNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210007 +// - HTTP code: 404 +// - message: "The route mapping could not be found: %s" +func IsRouteMappingNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210007 +} + +// NewRouterGroupNotFoundError returns a new CloudFoundryError +// that IsRouterGroupNotFoundError will return true for +func NewRouterGroupNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 210009, + ErrorCode: "CF-RouterGroupNotFound", + Description: "The router group could not be found: %s", + } +} + +// IsRouterGroupNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210009 +// - HTTP code: 404 +// - message: "The router group could not be found: %s" +func IsRouterGroupNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210009 +} + +// NewInstancesError returns a new CloudFoundryError +// that IsInstancesError will return true for +func NewInstancesError() CloudFoundryError { + return CloudFoundryError{ + Code: 220001, + ErrorCode: "CF-InstancesError", + Description: "Instances error: %s", + } +} + +// IsInstancesError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 220001 +// - HTTP code: 400 +// - message: "Instances error: %s" +func IsInstancesError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 220001 +} + +// NewInstancesUnavailableError returns a new CloudFoundryError +// that IsInstancesUnavailableError will return true for +func NewInstancesUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 220002, + ErrorCode: "CF-InstancesUnavailable", + Description: "Instances information unavailable: %s", + } +} + +// IsInstancesUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 220002 +// - HTTP code: 503 +// - message: "Instances information unavailable: %s" +func IsInstancesUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 220002 +} + +// NewEventNotFoundError returns a new CloudFoundryError +// that IsEventNotFoundError will return true for +func NewEventNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 230002, + ErrorCode: "CF-EventNotFound", + Description: "Event could not be found: %s", + } +} + +// IsEventNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 230002 +// - HTTP code: 404 +// - message: "Event could not be found: %s" +func IsEventNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 230002 +} + +// NewQuotaDefinitionNotFoundError returns a new CloudFoundryError +// that IsQuotaDefinitionNotFoundError will return true for +func NewQuotaDefinitionNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 240001, + ErrorCode: "CF-QuotaDefinitionNotFound", + Description: "Quota Definition could not be found: %s", + } +} + +// IsQuotaDefinitionNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 240001 +// - HTTP code: 404 +// - message: "Quota Definition could not be found: %s" +func IsQuotaDefinitionNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 240001 +} + +// NewQuotaDefinitionNameTakenError returns a new CloudFoundryError +// that IsQuotaDefinitionNameTakenError will return true for +func NewQuotaDefinitionNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 240002, + ErrorCode: "CF-QuotaDefinitionNameTaken", + Description: "Quota Definition is taken: %s", + } +} + +// IsQuotaDefinitionNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 240002 +// - HTTP code: 400 +// - message: "Quota Definition is taken: %s" +func IsQuotaDefinitionNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 240002 +} + +// NewQuotaDefinitionInvalidError returns a new CloudFoundryError +// that IsQuotaDefinitionInvalidError will return true for +func NewQuotaDefinitionInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 240003, + ErrorCode: "CF-QuotaDefinitionInvalid", + Description: "Quota Definition is invalid: %s", + } +} + +// IsQuotaDefinitionInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 240003 +// - HTTP code: 400 +// - message: "Quota Definition is invalid: %s" +func IsQuotaDefinitionInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 240003 +} + +// NewQuotaDefinitionMemoryLimitInvalidError returns a new CloudFoundryError +// that IsQuotaDefinitionMemoryLimitInvalidError will return true for +func NewQuotaDefinitionMemoryLimitInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 240004, + ErrorCode: "CF-QuotaDefinitionMemoryLimitInvalid", + Description: "Quota Definition memory limit cannot be less than -1", + } +} + +// IsQuotaDefinitionMemoryLimitInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 240004 +// - HTTP code: 400 +// - message: "Quota Definition memory limit cannot be less than -1" +func IsQuotaDefinitionMemoryLimitInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 240004 +} + +// NewStackInvalidError returns a new CloudFoundryError +// that IsStackInvalidError will return true for +func NewStackInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 250001, + ErrorCode: "CF-StackInvalid", + Description: "The stack is invalid: %s", + } +} + +// IsStackInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 250001 +// - HTTP code: 400 +// - message: "The stack is invalid: %s" +func IsStackInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 250001 +} + +// NewStackNameTakenError returns a new CloudFoundryError +// that IsStackNameTakenError will return true for +func NewStackNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 250002, + ErrorCode: "CF-StackNameTaken", + Description: "The stack name is taken: %s", + } +} + +// IsStackNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 250002 +// - HTTP code: 400 +// - message: "The stack name is taken: %s" +func IsStackNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 250002 +} + +// NewStackNotFoundError returns a new CloudFoundryError +// that IsStackNotFoundError will return true for +func NewStackNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 250003, + ErrorCode: "CF-StackNotFound", + Description: "The stack could not be found: %s", + } +} + +// IsStackNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 250003 +// - HTTP code: 404 +// - message: "The stack could not be found: %s" +func IsStackNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 250003 +} + +// NewServicePlanVisibilityInvalidError returns a new CloudFoundryError +// that IsServicePlanVisibilityInvalidError will return true for +func NewServicePlanVisibilityInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 260001, + ErrorCode: "CF-ServicePlanVisibilityInvalid", + Description: "Service Plan Visibility is invalid: %s", + } +} + +// IsServicePlanVisibilityInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 260001 +// - HTTP code: 400 +// - message: "Service Plan Visibility is invalid: %s" +func IsServicePlanVisibilityInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 260001 +} + +// NewServicePlanVisibilityAlreadyExistsError returns a new CloudFoundryError +// that IsServicePlanVisibilityAlreadyExistsError will return true for +func NewServicePlanVisibilityAlreadyExistsError() CloudFoundryError { + return CloudFoundryError{ + Code: 260002, + ErrorCode: "CF-ServicePlanVisibilityAlreadyExists", + Description: "This combination of ServicePlan and Organization is already taken: %s", + } +} + +// IsServicePlanVisibilityAlreadyExistsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 260002 +// - HTTP code: 400 +// - message: "This combination of ServicePlan and Organization is already taken: %s" +func IsServicePlanVisibilityAlreadyExistsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 260002 +} + +// NewServicePlanVisibilityNotFoundError returns a new CloudFoundryError +// that IsServicePlanVisibilityNotFoundError will return true for +func NewServicePlanVisibilityNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 260003, + ErrorCode: "CF-ServicePlanVisibilityNotFound", + Description: "The service plan visibility could not be found: %s", + } +} + +// IsServicePlanVisibilityNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 260003 +// - HTTP code: 404 +// - message: "The service plan visibility could not be found: %s" +func IsServicePlanVisibilityNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 260003 +} + +// NewServiceBrokerInvalidError returns a new CloudFoundryError +// that IsServiceBrokerInvalidError will return true for +func NewServiceBrokerInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 270001, + ErrorCode: "CF-ServiceBrokerInvalid", + Description: "Service broker is invalid: %s", + } +} + +// IsServiceBrokerInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270001 +// - HTTP code: 400 +// - message: "Service broker is invalid: %s" +func IsServiceBrokerInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270001 +} + +// NewServiceBrokerNameTakenError returns a new CloudFoundryError +// that IsServiceBrokerNameTakenError will return true for +func NewServiceBrokerNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 270002, + ErrorCode: "CF-ServiceBrokerNameTaken", + Description: "The service broker name is taken", + } +} + +// IsServiceBrokerNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270002 +// - HTTP code: 400 +// - message: "The service broker name is taken" +func IsServiceBrokerNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270002 +} + +// NewServiceBrokerUrlTakenError returns a new CloudFoundryError +// that IsServiceBrokerUrlTakenError will return true for +func NewServiceBrokerUrlTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 270003, + ErrorCode: "CF-ServiceBrokerUrlTaken", + Description: "The service broker url is taken: %s", + } +} + +// IsServiceBrokerUrlTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270003 +// - HTTP code: 400 +// - message: "The service broker url is taken: %s" +func IsServiceBrokerUrlTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270003 +} + +// NewServiceBrokerNotFoundError returns a new CloudFoundryError +// that IsServiceBrokerNotFoundError will return true for +func NewServiceBrokerNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 270004, + ErrorCode: "CF-ServiceBrokerNotFound", + Description: "The service broker was not found: %s", + } +} + +// IsServiceBrokerNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270004 +// - HTTP code: 404 +// - message: "The service broker was not found: %s" +func IsServiceBrokerNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270004 +} + +// NewServiceBrokerNotRemovableError returns a new CloudFoundryError +// that IsServiceBrokerNotRemovableError will return true for +func NewServiceBrokerNotRemovableError() CloudFoundryError { + return CloudFoundryError{ + Code: 270010, + ErrorCode: "CF-ServiceBrokerNotRemovable", + Description: "Can not remove brokers that have associated service instances: %s", + } +} + +// IsServiceBrokerNotRemovableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270010 +// - HTTP code: 400 +// - message: "Can not remove brokers that have associated service instances: %s" +func IsServiceBrokerNotRemovableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270010 +} + +// NewServiceBrokerUrlInvalidError returns a new CloudFoundryError +// that IsServiceBrokerUrlInvalidError will return true for +func NewServiceBrokerUrlInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 270011, + ErrorCode: "CF-ServiceBrokerUrlInvalid", + Description: "%s is not a valid URL", + } +} + +// IsServiceBrokerUrlInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270011 +// - HTTP code: 400 +// - message: "%s is not a valid URL" +func IsServiceBrokerUrlInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270011 +} + +// NewServiceBrokerCatalogInvalidError returns a new CloudFoundryError +// that IsServiceBrokerCatalogInvalidError will return true for +func NewServiceBrokerCatalogInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 270012, + ErrorCode: "CF-ServiceBrokerCatalogInvalid", + Description: "Service broker catalog is invalid: %s", + } +} + +// IsServiceBrokerCatalogInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270012 +// - HTTP code: 502 +// - message: "Service broker catalog is invalid: %s" +func IsServiceBrokerCatalogInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270012 +} + +// NewServiceBrokerDashboardClientFailureError returns a new CloudFoundryError +// that IsServiceBrokerDashboardClientFailureError will return true for +func NewServiceBrokerDashboardClientFailureError() CloudFoundryError { + return CloudFoundryError{ + Code: 270013, + ErrorCode: "CF-ServiceBrokerDashboardClientFailure", + Description: "Service broker dashboard clients could not be modified: %s", + } +} + +// IsServiceBrokerDashboardClientFailureError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270013 +// - HTTP code: 502 +// - message: "Service broker dashboard clients could not be modified: %s" +func IsServiceBrokerDashboardClientFailureError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270013 +} + +// NewServiceBrokerAsyncRequiredError returns a new CloudFoundryError +// that IsServiceBrokerAsyncRequiredError will return true for +func NewServiceBrokerAsyncRequiredError() CloudFoundryError { + return CloudFoundryError{ + Code: 270014, + ErrorCode: "CF-ServiceBrokerAsyncRequired", + Description: "This service plan requires client support for asynchronous service operations.", + } +} + +// IsServiceBrokerAsyncRequiredError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270014 +// - HTTP code: 400 +// - message: "This service plan requires client support for asynchronous service operations." +func IsServiceBrokerAsyncRequiredError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270014 +} + +// NewServiceDashboardClientMissingUrlError returns a new CloudFoundryError +// that IsServiceDashboardClientMissingUrlError will return true for +func NewServiceDashboardClientMissingUrlError() CloudFoundryError { + return CloudFoundryError{ + Code: 270015, + ErrorCode: "CF-ServiceDashboardClientMissingUrl", + Description: "Service broker returned dashboard client configuration without a dashboard URL", + } +} + +// IsServiceDashboardClientMissingUrlError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270015 +// - HTTP code: 502 +// - message: "Service broker returned dashboard client configuration without a dashboard URL" +func IsServiceDashboardClientMissingUrlError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270015 +} + +// NewServiceBrokerUrlBasicAuthNotSupportedError returns a new CloudFoundryError +// that IsServiceBrokerUrlBasicAuthNotSupportedError will return true for +func NewServiceBrokerUrlBasicAuthNotSupportedError() CloudFoundryError { + return CloudFoundryError{ + Code: 270016, + ErrorCode: "CF-ServiceBrokerUrlBasicAuthNotSupported", + Description: "User name and password fields in the broker URI are not supported", + } +} + +// IsServiceBrokerUrlBasicAuthNotSupportedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270016 +// - HTTP code: 400 +// - message: "User name and password fields in the broker URI are not supported" +func IsServiceBrokerUrlBasicAuthNotSupportedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270016 +} + +// NewServiceBrokerRespondedAsyncWhenNotAllowedError returns a new CloudFoundryError +// that IsServiceBrokerRespondedAsyncWhenNotAllowedError will return true for +func NewServiceBrokerRespondedAsyncWhenNotAllowedError() CloudFoundryError { + return CloudFoundryError{ + Code: 270017, + ErrorCode: "CF-ServiceBrokerRespondedAsyncWhenNotAllowed", + Description: "The service broker responded asynchronously to a request, but the accepts_incomplete query parameter was false or not given.", + } +} + +// IsServiceBrokerRespondedAsyncWhenNotAllowedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270017 +// - HTTP code: 502 +// - message: "The service broker responded asynchronously to a request, but the accepts_incomplete query parameter was false or not given." +func IsServiceBrokerRespondedAsyncWhenNotAllowedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270017 +} + +// NewServiceBrokerConcurrencyError returns a new CloudFoundryError +// that IsServiceBrokerConcurrencyError will return true for +func NewServiceBrokerConcurrencyError() CloudFoundryError { + return CloudFoundryError{ + Code: 270018, + ErrorCode: "CF-ServiceBrokerConcurrencyError", + Description: "The service broker could not perform this operation in parallel with other running operations", + } +} + +// IsServiceBrokerConcurrencyError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270018 +// - HTTP code: 422 +// - message: "The service broker could not perform this operation in parallel with other running operations" +func IsServiceBrokerConcurrencyError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270018 +} + +// NewServiceBrokerCatalogIncompatibleError returns a new CloudFoundryError +// that IsServiceBrokerCatalogIncompatibleError will return true for +func NewServiceBrokerCatalogIncompatibleError() CloudFoundryError { + return CloudFoundryError{ + Code: 270019, + ErrorCode: "CF-ServiceBrokerCatalogIncompatible", + Description: "Service broker catalog is incompatible: %s", + } +} + +// IsServiceBrokerCatalogIncompatibleError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270019 +// - HTTP code: 502 +// - message: "Service broker catalog is incompatible: %s" +func IsServiceBrokerCatalogIncompatibleError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270019 +} + +// NewServiceBrokerRequestRejectedError returns a new CloudFoundryError +// that IsServiceBrokerRequestRejectedError will return true for +func NewServiceBrokerRequestRejectedError() CloudFoundryError { + return CloudFoundryError{ + Code: 270020, + ErrorCode: "CF-ServiceBrokerRequestRejected", + Description: "The service broker rejected the request. Status Code: %s. Please check that the URL points to a valid service broker.", + } +} + +// IsServiceBrokerRequestRejectedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270020 +// - HTTP code: 502 +// - message: "The service broker rejected the request. Status Code: %s. Please check that the URL points to a valid service broker." +func IsServiceBrokerRequestRejectedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270020 +} + +// NewServiceBrokerRequestMalformedError returns a new CloudFoundryError +// that IsServiceBrokerRequestMalformedError will return true for +func NewServiceBrokerRequestMalformedError() CloudFoundryError { + return CloudFoundryError{ + Code: 270021, + ErrorCode: "CF-ServiceBrokerRequestMalformed", + Description: "The service broker returned an invalid response: expected valid JSON object in body. Please check that the URL points to a valid service broker.", + } +} + +// IsServiceBrokerRequestMalformedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270021 +// - HTTP code: 502 +// - message: "The service broker returned an invalid response: expected valid JSON object in body. Please check that the URL points to a valid service broker." +func IsServiceBrokerRequestMalformedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270021 +} + +// NewBuildpackNameStackTakenError returns a new CloudFoundryError +// that IsBuildpackNameStackTakenError will return true for +func NewBuildpackNameStackTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 290000, + ErrorCode: "CF-BuildpackNameStackTaken", + Description: "The buildpack name %s is already in use for the stack %s", + } +} + +// IsBuildpackNameStackTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290000 +// - HTTP code: 422 +// - message: "The buildpack name %s is already in use for the stack %s" +func IsBuildpackNameStackTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290000 +} + +// NewBuildpackNameTakenError returns a new CloudFoundryError +// that IsBuildpackNameTakenError will return true for +func NewBuildpackNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 290001, + ErrorCode: "CF-BuildpackNameTaken", + Description: "The buildpack name is already in use: %s", + } +} + +// IsBuildpackNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290001 +// - HTTP code: 400 +// - message: "The buildpack name is already in use: %s" +func IsBuildpackNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290001 +} + +// NewBuildpackBitsUploadInvalidError returns a new CloudFoundryError +// that IsBuildpackBitsUploadInvalidError will return true for +func NewBuildpackBitsUploadInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 290002, + ErrorCode: "CF-BuildpackBitsUploadInvalid", + Description: "The buildpack upload is invalid: %s", + } +} + +// IsBuildpackBitsUploadInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290002 +// - HTTP code: 400 +// - message: "The buildpack upload is invalid: %s" +func IsBuildpackBitsUploadInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290002 +} + +// NewBuildpackInvalidError returns a new CloudFoundryError +// that IsBuildpackInvalidError will return true for +func NewBuildpackInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 290003, + ErrorCode: "CF-BuildpackInvalid", + Description: "Buildpack is invalid: %s", + } +} + +// IsBuildpackInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290003 +// - HTTP code: 400 +// - message: "Buildpack is invalid: %s" +func IsBuildpackInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290003 +} + +// NewCustomBuildpacksDisabledError returns a new CloudFoundryError +// that IsCustomBuildpacksDisabledError will return true for +func NewCustomBuildpacksDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 290004, + ErrorCode: "CF-CustomBuildpacksDisabled", + Description: "Custom buildpacks are disabled", + } +} + +// IsCustomBuildpacksDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290004 +// - HTTP code: 400 +// - message: "Custom buildpacks are disabled" +func IsCustomBuildpacksDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290004 +} + +// NewBuildpackLockedError returns a new CloudFoundryError +// that IsBuildpackLockedError will return true for +func NewBuildpackLockedError() CloudFoundryError { + return CloudFoundryError{ + Code: 290005, + ErrorCode: "CF-BuildpackLocked", + Description: "The buildpack is locked", + } +} + +// IsBuildpackLockedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290005 +// - HTTP code: 409 +// - message: "The buildpack is locked" +func IsBuildpackLockedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290005 +} + +// NewJobTimeoutError returns a new CloudFoundryError +// that IsJobTimeoutError will return true for +func NewJobTimeoutError() CloudFoundryError { + return CloudFoundryError{ + Code: 290006, + ErrorCode: "CF-JobTimeout", + Description: "The job execution has timed out.", + } +} + +// IsJobTimeoutError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290006 +// - HTTP code: 524 +// - message: "The job execution has timed out." +func IsJobTimeoutError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290006 +} + +// NewSpaceDeleteTimeoutError returns a new CloudFoundryError +// that IsSpaceDeleteTimeoutError will return true for +func NewSpaceDeleteTimeoutError() CloudFoundryError { + return CloudFoundryError{ + Code: 290007, + ErrorCode: "CF-SpaceDeleteTimeout", + Description: "Deletion of space %s timed out before all resources within could be deleted", + } +} + +// IsSpaceDeleteTimeoutError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290007 +// - HTTP code: 524 +// - message: "Deletion of space %s timed out before all resources within could be deleted" +func IsSpaceDeleteTimeoutError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290007 +} + +// NewSpaceDeletionFailedError returns a new CloudFoundryError +// that IsSpaceDeletionFailedError will return true for +func NewSpaceDeletionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 290008, + ErrorCode: "CF-SpaceDeletionFailed", + Description: "Deletion of space %s failed because one or more resources within could not be deleted.%s", + } +} + +// IsSpaceDeletionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290008 +// - HTTP code: 502 +// - message: "Deletion of space %s failed because one or more resources within could not be deleted.\n\n%s" +func IsSpaceDeletionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290008 +} + +// NewOrganizationDeleteTimeoutError returns a new CloudFoundryError +// that IsOrganizationDeleteTimeoutError will return true for +func NewOrganizationDeleteTimeoutError() CloudFoundryError { + return CloudFoundryError{ + Code: 290009, + ErrorCode: "CF-OrganizationDeleteTimeout", + Description: "Delete of organization %s timed out before all resources within could be deleted", + } +} + +// IsOrganizationDeleteTimeoutError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290009 +// - HTTP code: 524 +// - message: "Delete of organization %s timed out before all resources within could be deleted" +func IsOrganizationDeleteTimeoutError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290009 +} + +// NewOrganizationDeletionFailedError returns a new CloudFoundryError +// that IsOrganizationDeletionFailedError will return true for +func NewOrganizationDeletionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 290010, + ErrorCode: "CF-OrganizationDeletionFailed", + Description: "Deletion of organization %s failed because one or more resources within could not be deleted.%s", + } +} + +// IsOrganizationDeletionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290010 +// - HTTP code: 502 +// - message: "Deletion of organization %s failed because one or more resources within could not be deleted.\n\n%s" +func IsOrganizationDeletionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290010 +} + +// NewNonrecursiveSpaceDeletionFailedError returns a new CloudFoundryError +// that IsNonrecursiveSpaceDeletionFailedError will return true for +func NewNonrecursiveSpaceDeletionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 290011, + ErrorCode: "CF-NonrecursiveSpaceDeletionFailed", + Description: "Resource inside space %s must first be deleted, or specify recursive delete.", + } +} + +// IsNonrecursiveSpaceDeletionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290011 +// - HTTP code: 400 +// - message: "Resource inside space %s must first be deleted, or specify recursive delete." +func IsNonrecursiveSpaceDeletionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290011 +} + +// NewBitsServiceError returns a new CloudFoundryError +// that IsBitsServiceError will return true for +func NewBitsServiceError() CloudFoundryError { + return CloudFoundryError{ + Code: 290012, + ErrorCode: "CF-BitsServiceError", + Description: "The bits service returned an error: %s", + } +} + +// IsBitsServiceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290012 +// - HTTP code: 500 +// - message: "The bits service returned an error: %s" +func IsBitsServiceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290012 +} + +// NewSpaceRolesDeletionTimeoutError returns a new CloudFoundryError +// that IsSpaceRolesDeletionTimeoutError will return true for +func NewSpaceRolesDeletionTimeoutError() CloudFoundryError { + return CloudFoundryError{ + Code: 290013, + ErrorCode: "CF-SpaceRolesDeletionTimeout", + Description: "Deletion of roles for space %s timed out before all roles could be deleted", + } +} + +// IsSpaceRolesDeletionTimeoutError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290013 +// - HTTP code: 524 +// - message: "Deletion of roles for space %s timed out before all roles could be deleted" +func IsSpaceRolesDeletionTimeoutError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290013 +} + +// NewOrganizationRolesDeletionFailedError returns a new CloudFoundryError +// that IsOrganizationRolesDeletionFailedError will return true for +func NewOrganizationRolesDeletionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 290014, + ErrorCode: "CF-OrganizationRolesDeletionFailed", + Description: "Failed to delete one or more roles for organization %s", + } +} + +// IsOrganizationRolesDeletionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290014 +// - HTTP code: 502 +// - message: "Failed to delete one or more roles for organization %s" +func IsOrganizationRolesDeletionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290014 +} + +// NewSpaceRolesDeletionFailedError returns a new CloudFoundryError +// that IsSpaceRolesDeletionFailedError will return true for +func NewSpaceRolesDeletionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 290016, + ErrorCode: "CF-SpaceRolesDeletionFailed", + Description: "Failed to delete one or more roles for space %s", + } +} + +// IsSpaceRolesDeletionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290016 +// - HTTP code: 502 +// - message: "Failed to delete one or more roles for space %s" +func IsSpaceRolesDeletionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290016 +} + +// NewSecurityGroupInvalidError returns a new CloudFoundryError +// that IsSecurityGroupInvalidError will return true for +func NewSecurityGroupInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 300001, + ErrorCode: "CF-SecurityGroupInvalid", + Description: "The security group is invalid: %s", + } +} + +// IsSecurityGroupInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 300001 +// - HTTP code: 400 +// - message: "The security group is invalid: %s" +func IsSecurityGroupInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 300001 +} + +// NewSecurityGroupNotFoundError returns a new CloudFoundryError +// that IsSecurityGroupNotFoundError will return true for +func NewSecurityGroupNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 300002, + ErrorCode: "CF-SecurityGroupNotFound", + Description: "The security group could not be found: %s", + } +} + +// IsSecurityGroupNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 300002 +// - HTTP code: 404 +// - message: "The security group could not be found: %s" +func IsSecurityGroupNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 300002 +} + +// NewSecurityGroupStagingDefaultInvalidError returns a new CloudFoundryError +// that IsSecurityGroupStagingDefaultInvalidError will return true for +func NewSecurityGroupStagingDefaultInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 300003, + ErrorCode: "CF-SecurityGroupStagingDefaultInvalid", + Description: "The security group could not be found: %s", + } +} + +// IsSecurityGroupStagingDefaultInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 300003 +// - HTTP code: 400 +// - message: "The security group could not be found: %s" +func IsSecurityGroupStagingDefaultInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 300003 +} + +// NewSecurityGroupRunningDefaultInvalidError returns a new CloudFoundryError +// that IsSecurityGroupRunningDefaultInvalidError will return true for +func NewSecurityGroupRunningDefaultInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 300004, + ErrorCode: "CF-SecurityGroupRunningDefaultInvalid", + Description: "The security group could not be found: %s", + } +} + +// IsSecurityGroupRunningDefaultInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 300004 +// - HTTP code: 400 +// - message: "The security group could not be found: %s" +func IsSecurityGroupRunningDefaultInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 300004 +} + +// NewSecurityGroupNameTakenError returns a new CloudFoundryError +// that IsSecurityGroupNameTakenError will return true for +func NewSecurityGroupNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 300005, + ErrorCode: "CF-SecurityGroupNameTaken", + Description: "The security group name is taken: %s", + } +} + +// IsSecurityGroupNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 300005 +// - HTTP code: 400 +// - message: "The security group name is taken: %s" +func IsSecurityGroupNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 300005 +} + +// NewSpaceQuotaDefinitionInvalidError returns a new CloudFoundryError +// that IsSpaceQuotaDefinitionInvalidError will return true for +func NewSpaceQuotaDefinitionInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 310001, + ErrorCode: "CF-SpaceQuotaDefinitionInvalid", + Description: "Space Quota Definition is invalid: %s", + } +} + +// IsSpaceQuotaDefinitionInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310001 +// - HTTP code: 400 +// - message: "Space Quota Definition is invalid: %s" +func IsSpaceQuotaDefinitionInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310001 +} + +// NewSpaceQuotaDefinitionNameTakenError returns a new CloudFoundryError +// that IsSpaceQuotaDefinitionNameTakenError will return true for +func NewSpaceQuotaDefinitionNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 310002, + ErrorCode: "CF-SpaceQuotaDefinitionNameTaken", + Description: "The space quota definition name is taken: %s", + } +} + +// IsSpaceQuotaDefinitionNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310002 +// - HTTP code: 400 +// - message: "The space quota definition name is taken: %s" +func IsSpaceQuotaDefinitionNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310002 +} + +// NewSpaceQuotaMemoryLimitExceededError returns a new CloudFoundryError +// that IsSpaceQuotaMemoryLimitExceededError will return true for +func NewSpaceQuotaMemoryLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310003, + ErrorCode: "CF-SpaceQuotaMemoryLimitExceeded", + Description: "You have exceeded your space's memory limit: %s", + } +} + +// IsSpaceQuotaMemoryLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310003 +// - HTTP code: 400 +// - message: "You have exceeded your space's memory limit: %s" +func IsSpaceQuotaMemoryLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310003 +} + +// NewSpaceQuotaInstanceMemoryLimitExceededError returns a new CloudFoundryError +// that IsSpaceQuotaInstanceMemoryLimitExceededError will return true for +func NewSpaceQuotaInstanceMemoryLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310004, + ErrorCode: "CF-SpaceQuotaInstanceMemoryLimitExceeded", + Description: "You have exceeded the instance memory limit for your space's quota.", + } +} + +// IsSpaceQuotaInstanceMemoryLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310004 +// - HTTP code: 400 +// - message: "You have exceeded the instance memory limit for your space's quota." +func IsSpaceQuotaInstanceMemoryLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310004 +} + +// NewSpaceQuotaTotalRoutesExceededError returns a new CloudFoundryError +// that IsSpaceQuotaTotalRoutesExceededError will return true for +func NewSpaceQuotaTotalRoutesExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310005, + ErrorCode: "CF-SpaceQuotaTotalRoutesExceeded", + Description: "You have exceeded the total routes for your space's quota.", + } +} + +// IsSpaceQuotaTotalRoutesExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310005 +// - HTTP code: 400 +// - message: "You have exceeded the total routes for your space's quota." +func IsSpaceQuotaTotalRoutesExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310005 +} + +// NewOrgQuotaTotalRoutesExceededError returns a new CloudFoundryError +// that IsOrgQuotaTotalRoutesExceededError will return true for +func NewOrgQuotaTotalRoutesExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310006, + ErrorCode: "CF-OrgQuotaTotalRoutesExceeded", + Description: "You have exceeded the total routes for your organization's quota.", + } +} + +// IsOrgQuotaTotalRoutesExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310006 +// - HTTP code: 400 +// - message: "You have exceeded the total routes for your organization's quota." +func IsOrgQuotaTotalRoutesExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310006 +} + +// NewSpaceQuotaDefinitionNotFoundError returns a new CloudFoundryError +// that IsSpaceQuotaDefinitionNotFoundError will return true for +func NewSpaceQuotaDefinitionNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 310007, + ErrorCode: "CF-SpaceQuotaDefinitionNotFound", + Description: "Space Quota Definition could not be found: %s", + } +} + +// IsSpaceQuotaDefinitionNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310007 +// - HTTP code: 404 +// - message: "Space Quota Definition could not be found: %s" +func IsSpaceQuotaDefinitionNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310007 +} + +// NewSpaceQuotaInstanceLimitExceededError returns a new CloudFoundryError +// that IsSpaceQuotaInstanceLimitExceededError will return true for +func NewSpaceQuotaInstanceLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310008, + ErrorCode: "CF-SpaceQuotaInstanceLimitExceeded", + Description: "You have exceeded the instance limit for your space's quota.", + } +} + +// IsSpaceQuotaInstanceLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310008 +// - HTTP code: 400 +// - message: "You have exceeded the instance limit for your space's quota." +func IsSpaceQuotaInstanceLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310008 +} + +// NewOrgQuotaTotalReservedRoutePortsExceededError returns a new CloudFoundryError +// that IsOrgQuotaTotalReservedRoutePortsExceededError will return true for +func NewOrgQuotaTotalReservedRoutePortsExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310009, + ErrorCode: "CF-OrgQuotaTotalReservedRoutePortsExceeded", + Description: "You have exceeded the total reserved route ports for your organization's quota.", + } +} + +// IsOrgQuotaTotalReservedRoutePortsExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310009 +// - HTTP code: 400 +// - message: "You have exceeded the total reserved route ports for your organization's quota." +func IsOrgQuotaTotalReservedRoutePortsExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310009 +} + +// NewSpaceQuotaTotalReservedRoutePortsExceededError returns a new CloudFoundryError +// that IsSpaceQuotaTotalReservedRoutePortsExceededError will return true for +func NewSpaceQuotaTotalReservedRoutePortsExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310010, + ErrorCode: "CF-SpaceQuotaTotalReservedRoutePortsExceeded", + Description: "You have exceeded the total reserved route ports for your space's quota.", + } +} + +// IsSpaceQuotaTotalReservedRoutePortsExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310010 +// - HTTP code: 400 +// - message: "You have exceeded the total reserved route ports for your space's quota." +func IsSpaceQuotaTotalReservedRoutePortsExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310010 +} + +// NewDiegoDisabledError returns a new CloudFoundryError +// that IsDiegoDisabledError will return true for +func NewDiegoDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 320001, + ErrorCode: "CF-DiegoDisabled", + Description: "Diego has not been enabled.", + } +} + +// IsDiegoDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 320001 +// - HTTP code: 400 +// - message: "Diego has not been enabled." +func IsDiegoDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 320001 +} + +// NewDiegoDockerBuildpackConflictError returns a new CloudFoundryError +// that IsDiegoDockerBuildpackConflictError will return true for +func NewDiegoDockerBuildpackConflictError() CloudFoundryError { + return CloudFoundryError{ + Code: 320002, + ErrorCode: "CF-DiegoDockerBuildpackConflict", + Description: "You cannot specify a custom buildpack and a docker image at the same time.", + } +} + +// IsDiegoDockerBuildpackConflictError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 320002 +// - HTTP code: 400 +// - message: "You cannot specify a custom buildpack and a docker image at the same time." +func IsDiegoDockerBuildpackConflictError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 320002 +} + +// NewDockerDisabledError returns a new CloudFoundryError +// that IsDockerDisabledError will return true for +func NewDockerDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 320003, + ErrorCode: "CF-DockerDisabled", + Description: "Docker support has not been enabled.", + } +} + +// IsDockerDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 320003 +// - HTTP code: 400 +// - message: "Docker support has not been enabled." +func IsDockerDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 320003 +} + +// NewStagingBackendInvalidError returns a new CloudFoundryError +// that IsStagingBackendInvalidError will return true for +func NewStagingBackendInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 320004, + ErrorCode: "CF-StagingBackendInvalid", + Description: "The request staging completion endpoint only handles apps desired to stage on the Diego backend.", + } +} + +// IsStagingBackendInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 320004 +// - HTTP code: 403 +// - message: "The request staging completion endpoint only handles apps desired to stage on the Diego backend." +func IsStagingBackendInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 320004 +} + +// NewBackendSelectionNotAuthorizedError returns a new CloudFoundryError +// that IsBackendSelectionNotAuthorizedError will return true for +func NewBackendSelectionNotAuthorizedError() CloudFoundryError { + return CloudFoundryError{ + Code: 320005, + ErrorCode: "CF-BackendSelectionNotAuthorized", + Description: "You cannot select the backend on which to run this application", + } +} + +// IsBackendSelectionNotAuthorizedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 320005 +// - HTTP code: 403 +// - message: "You cannot select the backend on which to run this application" +func IsBackendSelectionNotAuthorizedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 320005 +} + +// NewRevisionsEnabledError returns a new CloudFoundryError +// that IsRevisionsEnabledError will return true for +func NewRevisionsEnabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 320006, + ErrorCode: "CF-RevisionsEnabled", + Description: "V2 restaging is disabled when your app has revisions enabled", + } +} + +// IsRevisionsEnabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 320006 +// - HTTP code: 400 +// - message: "V2 restaging is disabled when your app has revisions enabled" +func IsRevisionsEnabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 320006 +} + +// NewFeatureFlagNotFoundError returns a new CloudFoundryError +// that IsFeatureFlagNotFoundError will return true for +func NewFeatureFlagNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 330000, + ErrorCode: "CF-FeatureFlagNotFound", + Description: "The feature flag could not be found: %s", + } +} + +// IsFeatureFlagNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 330000 +// - HTTP code: 404 +// - message: "The feature flag could not be found: %s" +func IsFeatureFlagNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 330000 +} + +// NewFeatureFlagInvalidError returns a new CloudFoundryError +// that IsFeatureFlagInvalidError will return true for +func NewFeatureFlagInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 330001, + ErrorCode: "CF-FeatureFlagInvalid", + Description: "The feature flag is invalid: %s", + } +} + +// IsFeatureFlagInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 330001 +// - HTTP code: 400 +// - message: "The feature flag is invalid: %s" +func IsFeatureFlagInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 330001 +} + +// NewFeatureDisabledError returns a new CloudFoundryError +// that IsFeatureDisabledError will return true for +func NewFeatureDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 330002, + ErrorCode: "CF-FeatureDisabled", + Description: "Feature Disabled: %s", + } +} + +// IsFeatureDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 330002 +// - HTTP code: 403 +// - message: "Feature Disabled: %s" +func IsFeatureDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 330002 +} + +// NewUserProvidedServiceInstanceNotFoundError returns a new CloudFoundryError +// that IsUserProvidedServiceInstanceNotFoundError will return true for +func NewUserProvidedServiceInstanceNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 340001, + ErrorCode: "CF-UserProvidedServiceInstanceNotFound", + Description: "The service instance could not be found: %s", + } +} + +// IsUserProvidedServiceInstanceNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 340001 +// - HTTP code: 404 +// - message: "The service instance could not be found: %s" +func IsUserProvidedServiceInstanceNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 340001 +} + +// NewUserProvidedServiceInstanceHandlerNeededError returns a new CloudFoundryError +// that IsUserProvidedServiceInstanceHandlerNeededError will return true for +func NewUserProvidedServiceInstanceHandlerNeededError() CloudFoundryError { + return CloudFoundryError{ + Code: 340002, + ErrorCode: "CF-UserProvidedServiceInstanceHandlerNeeded", + Description: "Please use the User Provided Services API to manage this resource.", + } +} + +// IsUserProvidedServiceInstanceHandlerNeededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 340002 +// - HTTP code: 400 +// - message: "Please use the User Provided Services API to manage this resource." +func IsUserProvidedServiceInstanceHandlerNeededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 340002 +} + +// NewProcessInvalidError returns a new CloudFoundryError +// that IsProcessInvalidError will return true for +func NewProcessInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 350001, + ErrorCode: "CF-ProcessInvalid", + Description: "The process is invalid: %s", + } +} + +// IsProcessInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 350001 +// - HTTP code: 400 +// - message: "The process is invalid: %s" +func IsProcessInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 350001 +} + +// NewUnableToDeleteError returns a new CloudFoundryError +// that IsUnableToDeleteError will return true for +func NewUnableToDeleteError() CloudFoundryError { + return CloudFoundryError{ + Code: 350002, + ErrorCode: "CF-UnableToDelete", + Description: "Unable to perform delete action: %s", + } +} + +// IsUnableToDeleteError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 350002 +// - HTTP code: 400 +// - message: "Unable to perform delete action: %s" +func IsUnableToDeleteError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 350002 +} + +// NewProcessNotFoundError returns a new CloudFoundryError +// that IsProcessNotFoundError will return true for +func NewProcessNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 350003, + ErrorCode: "CF-ProcessNotFound", + Description: "The process could not be found: %s", + } +} + +// IsProcessNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 350003 +// - HTTP code: 404 +// - message: "The process could not be found: %s" +func IsProcessNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 350003 +} + +// NewServiceKeyNameTakenError returns a new CloudFoundryError +// that IsServiceKeyNameTakenError will return true for +func NewServiceKeyNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 360001, + ErrorCode: "CF-ServiceKeyNameTaken", + Description: "The service key name is taken: %s", + } +} + +// IsServiceKeyNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 360001 +// - HTTP code: 400 +// - message: "The service key name is taken: %s" +func IsServiceKeyNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 360001 +} + +// NewServiceKeyInvalidError returns a new CloudFoundryError +// that IsServiceKeyInvalidError will return true for +func NewServiceKeyInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 360002, + ErrorCode: "CF-ServiceKeyInvalid", + Description: "The service key is invalid: %s", + } +} + +// IsServiceKeyInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 360002 +// - HTTP code: 400 +// - message: "The service key is invalid: %s" +func IsServiceKeyInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 360002 +} + +// NewServiceKeyNotFoundError returns a new CloudFoundryError +// that IsServiceKeyNotFoundError will return true for +func NewServiceKeyNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 360003, + ErrorCode: "CF-ServiceKeyNotFound", + Description: "The service key could not be found: %s", + } +} + +// IsServiceKeyNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 360003 +// - HTTP code: 404 +// - message: "The service key could not be found: %s" +func IsServiceKeyNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 360003 +} + +// NewServiceKeyNotSupportedError returns a new CloudFoundryError +// that IsServiceKeyNotSupportedError will return true for +func NewServiceKeyNotSupportedError() CloudFoundryError { + return CloudFoundryError{ + Code: 360004, + ErrorCode: "CF-ServiceKeyNotSupported", + Description: "%s", + } +} + +// IsServiceKeyNotSupportedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 360004 +// - HTTP code: 400 +// - message: "%s" +func IsServiceKeyNotSupportedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 360004 +} + +// NewServiceKeyCredentialStoreUnavailableError returns a new CloudFoundryError +// that IsServiceKeyCredentialStoreUnavailableError will return true for +func NewServiceKeyCredentialStoreUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 360005, + ErrorCode: "CF-ServiceKeyCredentialStoreUnavailable", + Description: "Credential store is unavailable", + } +} + +// IsServiceKeyCredentialStoreUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 360005 +// - HTTP code: 503 +// - message: "Credential store is unavailable" +func IsServiceKeyCredentialStoreUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 360005 +} + +// NewRoutingApiUnavailableError returns a new CloudFoundryError +// that IsRoutingApiUnavailableError will return true for +func NewRoutingApiUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 370001, + ErrorCode: "CF-RoutingApiUnavailable", + Description: "The Routing API is currently unavailable", + } +} + +// IsRoutingApiUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 370001 +// - HTTP code: 503 +// - message: "The Routing API is currently unavailable" +func IsRoutingApiUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 370001 +} + +// NewRoutingApiDisabledError returns a new CloudFoundryError +// that IsRoutingApiDisabledError will return true for +func NewRoutingApiDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 370003, + ErrorCode: "CF-RoutingApiDisabled", + Description: "Routing API is disabled", + } +} + +// IsRoutingApiDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 370003 +// - HTTP code: 403 +// - message: "Routing API is disabled" +func IsRoutingApiDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 370003 +} + +// NewEnvironmentVariableGroupInvalidError returns a new CloudFoundryError +// that IsEnvironmentVariableGroupInvalidError will return true for +func NewEnvironmentVariableGroupInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 380001, + ErrorCode: "CF-EnvironmentVariableGroupInvalid", + Description: "The Environment Variable Group is invalid: %s", + } +} + +// IsEnvironmentVariableGroupInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 380001 +// - HTTP code: 400 +// - message: "The Environment Variable Group is invalid: %s" +func IsEnvironmentVariableGroupInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 380001 +} + +// NewDropletUploadInvalidError returns a new CloudFoundryError +// that IsDropletUploadInvalidError will return true for +func NewDropletUploadInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 380002, + ErrorCode: "CF-DropletUploadInvalid", + Description: "The droplet upload is invalid: %s", + } +} + +// IsDropletUploadInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 380002 +// - HTTP code: 400 +// - message: "The droplet upload is invalid: %s" +func IsDropletUploadInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 380002 +} + +// NewServiceInstanceUnshareFailedError returns a new CloudFoundryError +// that IsServiceInstanceUnshareFailedError will return true for +func NewServiceInstanceUnshareFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 390001, + ErrorCode: "CF-ServiceInstanceUnshareFailed", + Description: "Unshare of service instance failed: %s", + } +} + +// IsServiceInstanceUnshareFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390001 +// - HTTP code: 502 +// - message: "Unshare of service instance failed: \n\n%s" +func IsServiceInstanceUnshareFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390001 +} + +// NewServiceInstanceDeletionSharesExistsError returns a new CloudFoundryError +// that IsServiceInstanceDeletionSharesExistsError will return true for +func NewServiceInstanceDeletionSharesExistsError() CloudFoundryError { + return CloudFoundryError{ + Code: 390002, + ErrorCode: "CF-ServiceInstanceDeletionSharesExists", + Description: "Service instances must be unshared before they can be deleted. Unsharing %s will automatically delete any bindings that have been made to applications in other spaces.", + } +} + +// IsServiceInstanceDeletionSharesExistsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390002 +// - HTTP code: 422 +// - message: "Service instances must be unshared before they can be deleted. Unsharing %s will automatically delete any bindings that have been made to applications in other spaces." +func IsServiceInstanceDeletionSharesExistsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390002 +} + +// NewSharedServiceInstanceCannotBeRenamedError returns a new CloudFoundryError +// that IsSharedServiceInstanceCannotBeRenamedError will return true for +func NewSharedServiceInstanceCannotBeRenamedError() CloudFoundryError { + return CloudFoundryError{ + Code: 390003, + ErrorCode: "CF-SharedServiceInstanceCannotBeRenamed", + Description: "Service instances that have been shared cannot be renamed", + } +} + +// IsSharedServiceInstanceCannotBeRenamedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390003 +// - HTTP code: 422 +// - message: "Service instances that have been shared cannot be renamed" +func IsSharedServiceInstanceCannotBeRenamedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390003 +} + +// NewSharedServiceInstanceNotUpdatableInTargetSpaceError returns a new CloudFoundryError +// that IsSharedServiceInstanceNotUpdatableInTargetSpaceError will return true for +func NewSharedServiceInstanceNotUpdatableInTargetSpaceError() CloudFoundryError { + return CloudFoundryError{ + Code: 390004, + ErrorCode: "CF-SharedServiceInstanceNotUpdatableInTargetSpace", + Description: "You cannot update service instances that have been shared with you", + } +} + +// IsSharedServiceInstanceNotUpdatableInTargetSpaceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390004 +// - HTTP code: 403 +// - message: "You cannot update service instances that have been shared with you" +func IsSharedServiceInstanceNotUpdatableInTargetSpaceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390004 +} + +// NewSharedServiceInstanceNotDeletableInTargetSpaceError returns a new CloudFoundryError +// that IsSharedServiceInstanceNotDeletableInTargetSpaceError will return true for +func NewSharedServiceInstanceNotDeletableInTargetSpaceError() CloudFoundryError { + return CloudFoundryError{ + Code: 390005, + ErrorCode: "CF-SharedServiceInstanceNotDeletableInTargetSpace", + Description: "You cannot delete service instances that have been shared with you", + } +} + +// IsSharedServiceInstanceNotDeletableInTargetSpaceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390005 +// - HTTP code: 403 +// - message: "You cannot delete service instances that have been shared with you" +func IsSharedServiceInstanceNotDeletableInTargetSpaceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390005 +} + +// NewMaintenanceInfoNotSupportedError returns a new CloudFoundryError +// that IsMaintenanceInfoNotSupportedError will return true for +func NewMaintenanceInfoNotSupportedError() CloudFoundryError { + return CloudFoundryError{ + Code: 390006, + ErrorCode: "CF-MaintenanceInfoNotSupported", + Description: "The service broker does not support upgrades for service instances created from this plan.", + } +} + +// IsMaintenanceInfoNotSupportedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390006 +// - HTTP code: 422 +// - message: "The service broker does not support upgrades for service instances created from this plan." +func IsMaintenanceInfoNotSupportedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390006 +} + +// NewMaintenanceInfoNotSemverError returns a new CloudFoundryError +// that IsMaintenanceInfoNotSemverError will return true for +func NewMaintenanceInfoNotSemverError() CloudFoundryError { + return CloudFoundryError{ + Code: 390007, + ErrorCode: "CF-MaintenanceInfoNotSemver", + Description: "maintenance_info.version should be a semantic version.", + } +} + +// IsMaintenanceInfoNotSemverError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390007 +// - HTTP code: 422 +// - message: "maintenance_info.version should be a semantic version." +func IsMaintenanceInfoNotSemverError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390007 +} + +// NewMaintenanceInfoNotUpdatableWhenChangingPlanError returns a new CloudFoundryError +// that IsMaintenanceInfoNotUpdatableWhenChangingPlanError will return true for +func NewMaintenanceInfoNotUpdatableWhenChangingPlanError() CloudFoundryError { + return CloudFoundryError{ + Code: 390008, + ErrorCode: "CF-MaintenanceInfoNotUpdatableWhenChangingPlan", + Description: "maintenance_info should not be changed when switching to different plan.", + } +} + +// IsMaintenanceInfoNotUpdatableWhenChangingPlanError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390008 +// - HTTP code: 422 +// - message: "maintenance_info should not be changed when switching to different plan." +func IsMaintenanceInfoNotUpdatableWhenChangingPlanError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390008 +} + +// NewMaintenanceInfoConflictError returns a new CloudFoundryError +// that IsMaintenanceInfoConflictError will return true for +func NewMaintenanceInfoConflictError() CloudFoundryError { + return CloudFoundryError{ + Code: 390009, + ErrorCode: "CF-MaintenanceInfoConflict", + Description: "maintenance_info.version requested is invalid. Please ensure the catalog is up to date and you are providing a version supported by this service plan.", + } +} + +// IsMaintenanceInfoConflictError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390009 +// - HTTP code: 422 +// - message: "maintenance_info.version requested is invalid. Please ensure the catalog is up to date and you are providing a version supported by this service plan." +func IsMaintenanceInfoConflictError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390009 +} + +// NewBuildpackStacksDontMatchError returns a new CloudFoundryError +// that IsBuildpackStacksDontMatchError will return true for +func NewBuildpackStacksDontMatchError() CloudFoundryError { + return CloudFoundryError{ + Code: 390011, + ErrorCode: "CF-BuildpackStacksDontMatch", + Description: "Uploaded buildpack stack (%s) does not match %s", + } +} + +// IsBuildpackStacksDontMatchError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390011 +// - HTTP code: 422 +// - message: "Uploaded buildpack stack (%s) does not match %s" +func IsBuildpackStacksDontMatchError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390011 +} + +// NewBuildpackStackDoesNotExistError returns a new CloudFoundryError +// that IsBuildpackStackDoesNotExistError will return true for +func NewBuildpackStackDoesNotExistError() CloudFoundryError { + return CloudFoundryError{ + Code: 390012, + ErrorCode: "CF-BuildpackStackDoesNotExist", + Description: "Uploaded buildpack stack (%s) does not exist", + } +} + +// IsBuildpackStackDoesNotExistError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390012 +// - HTTP code: 422 +// - message: "Uploaded buildpack stack (%s) does not exist" +func IsBuildpackStackDoesNotExistError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390012 +} + +// NewBuildpackZipError returns a new CloudFoundryError +// that IsBuildpackZipError will return true for +func NewBuildpackZipError() CloudFoundryError { + return CloudFoundryError{ + Code: 390013, + ErrorCode: "CF-BuildpackZipError", + Description: "Buildpack zip error: %s", + } +} + +// IsBuildpackZipError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390013 +// - HTTP code: 422 +// - message: "Buildpack zip error: %s" +func IsBuildpackZipError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390013 +} + +// NewDeploymentsDisabledError returns a new CloudFoundryError +// that IsDeploymentsDisabledError will return true for +func NewDeploymentsDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 390014, + ErrorCode: "CF-DeploymentsDisabled", + Description: "Deployments cannot be created due to manifest property 'temporary_disable_deployments'", + } +} + +// IsDeploymentsDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390014 +// - HTTP code: 403 +// - message: "Deployments cannot be created due to manifest property 'temporary_disable_deployments'" +func IsDeploymentsDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390014 +} + +// NewNoCurrentEncryptionKeyError returns a new CloudFoundryError +// that IsNoCurrentEncryptionKeyError will return true for +func NewNoCurrentEncryptionKeyError() CloudFoundryError { + return CloudFoundryError{ + Code: 390015, + ErrorCode: "CF-NoCurrentEncryptionKey", + Description: "Please set the desired encryption key in the manifest at ‘cc.database_encryption.current_key_label’", + } +} + +// IsNoCurrentEncryptionKeyError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390015 +// - HTTP code: 422 +// - message: "Please set the desired encryption key in the manifest at ‘cc.database_encryption.current_key_label’" +func IsNoCurrentEncryptionKeyError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390015 +} + +// NewScaleDisabledDuringDeploymentError returns a new CloudFoundryError +// that IsScaleDisabledDuringDeploymentError will return true for +func NewScaleDisabledDuringDeploymentError() CloudFoundryError { + return CloudFoundryError{ + Code: 390016, + ErrorCode: "CF-ScaleDisabledDuringDeployment", + Description: "Cannot scale this process while a deployment is in flight.", + } +} + +// IsScaleDisabledDuringDeploymentError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390016 +// - HTTP code: 422 +// - message: "Cannot scale this process while a deployment is in flight." +func IsScaleDisabledDuringDeploymentError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390016 +} + +// NewProcessUpdateDisabledDuringDeploymentError returns a new CloudFoundryError +// that IsProcessUpdateDisabledDuringDeploymentError will return true for +func NewProcessUpdateDisabledDuringDeploymentError() CloudFoundryError { + return CloudFoundryError{ + Code: 390017, + ErrorCode: "CF-ProcessUpdateDisabledDuringDeployment", + Description: "Cannot update this process while a deployment is in flight.", + } +} + +// IsProcessUpdateDisabledDuringDeploymentError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390017 +// - HTTP code: 422 +// - message: "Cannot update this process while a deployment is in flight." +func IsProcessUpdateDisabledDuringDeploymentError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390017 +} + +// NewLabelLimitExceededError returns a new CloudFoundryError +// that IsLabelLimitExceededError will return true for +func NewLabelLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 390020, + ErrorCode: "CF-LabelLimitExceeded", + Description: "Failed to add %d labels because it would exceed maximum of %d", + } +} + +// IsLabelLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390020 +// - HTTP code: 422 +// - message: "Failed to add %d labels because it would exceed maximum of %d" +func IsLabelLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390020 +} + +// NewAnnotationLimitExceededError returns a new CloudFoundryError +// that IsAnnotationLimitExceededError will return true for +func NewAnnotationLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 390023, + ErrorCode: "CF-AnnotationLimitExceeded", + Description: "Failed to add %d annotations because it would exceed maximum of %d", + } +} + +// IsAnnotationLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390023 +// - HTTP code: 422 +// - message: "Failed to add %d annotations because it would exceed maximum of %d" +func IsAnnotationLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390023 +} + +// NewStopDisabledDuringDeploymentError returns a new CloudFoundryError +// that IsStopDisabledDuringDeploymentError will return true for +func NewStopDisabledDuringDeploymentError() CloudFoundryError { + return CloudFoundryError{ + Code: 390024, + ErrorCode: "CF-StopDisabledDuringDeployment", + Description: "Cannot stop the app while it is deploying, please cancel the deployment before stopping the app.", + } +} + +// IsStopDisabledDuringDeploymentError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390024 +// - HTTP code: 422 +// - message: "Cannot stop the app while it is deploying, please cancel the deployment before stopping the app." +func IsStopDisabledDuringDeploymentError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390024 +} + +// NewKubernetesRouteResourceError returns a new CloudFoundryError +// that IsKubernetesRouteResourceError will return true for +func NewKubernetesRouteResourceError() CloudFoundryError { + return CloudFoundryError{ + Code: 400001, + ErrorCode: "CF-KubernetesRouteResourceError", + Description: "Failed to create/update/delete Route resource with guid '%s' on Kubernetes", + } +} + +// IsKubernetesRouteResourceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 400001 +// - HTTP code: 422 +// - message: "Failed to create/update/delete Route resource with guid '%s' on Kubernetes" +func IsKubernetesRouteResourceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 400001 +} + +// NewKpackImageError returns a new CloudFoundryError +// that IsKpackImageError will return true for +func NewKpackImageError() CloudFoundryError { + return CloudFoundryError{ + Code: 400002, + ErrorCode: "CF-KpackImageError", + Description: "Failed to %s Image resource for staging: '%s'", + } +} + +// IsKpackImageError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 400002 +// - HTTP code: 422 +// - message: "Failed to %s Image resource for staging: '%s'" +func IsKpackImageError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 400002 +} + +// NewKpackBuilderError returns a new CloudFoundryError +// that IsKpackBuilderError will return true for +func NewKpackBuilderError() CloudFoundryError { + return CloudFoundryError{ + Code: 400003, + ErrorCode: "CF-KpackBuilderError", + Description: "Failed to %s Builder resource: '%s'", + } +} + +// IsKpackBuilderError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 400003 +// - HTTP code: 422 +// - message: "Failed to %s Builder resource: '%s'" +func IsKpackBuilderError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 400003 +} + +// NewEiriniLRPError returns a new CloudFoundryError +// that IsEiriniLRPError will return true for +func NewEiriniLRPError() CloudFoundryError { + return CloudFoundryError{ + Code: 410001, + ErrorCode: "CF-EiriniLRPError", + Description: "Failed to %s LRP resource: '%s'", + } +} + +// IsEiriniLRPError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 410001 +// - HTTP code: 422 +// - message: "Failed to %s LRP resource: '%s'" +func IsEiriniLRPError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 410001 +} + +func cloudFoundryError(err error) (cferr CloudFoundryError, ok bool) { + type causer interface { + Cause() error + } + if _, isCauser := err.(causer); isCauser { + cause := pkgerrors.Cause(err) + cferr, ok = cause.(CloudFoundryError) + } else { + ok = stderrors.As(err, &cferr) + } + return cferr, ok +} diff --git a/third_party/go-cfclient/cf_error_test.go b/third_party/go-cfclient/cf_error_test.go new file mode 100644 index 000000000000..d4fbd651d4ca --- /dev/null +++ b/third_party/go-cfclient/cf_error_test.go @@ -0,0 +1,37 @@ +package cfclient + +import ( + stderrors "errors" + "fmt" + "testing" + + pkgerrors "github.com/pkg/errors" +) + +func TestIsSpaceNotFoundError(t *testing.T) { + tests := []struct { + name string + error + want bool + }{ + {"std/errors error", stderrors.New("is not"), false}, + {"pkg/errors error", pkgerrors.New("is not"), false}, + {"unwrapped CloudFoundry error", CloudFoundryError{ + Code: 40004, + }, true}, + {"pkg wrapped CloudFoundry error", pkgerrors.Wrap(CloudFoundryError{ + Code: 40004, + }, ""), true}, + {"std wrapped CloudFoundry error", fmt.Errorf("%w", CloudFoundryError{ + Code: 40004, + }), true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := IsSpaceNotFoundError(tt.error) + if got != tt.want { + t.Errorf("got %v, want %v", got, tt.want) + } + }) + } +} diff --git a/third_party/go-cfclient/cf_test.go b/third_party/go-cfclient/cf_test.go new file mode 100644 index 000000000000..ff446dfb6d77 --- /dev/null +++ b/third_party/go-cfclient/cf_test.go @@ -0,0 +1,224 @@ +package cfclient + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "strings" + "testing" + + "github.com/go-martini/martini" + "github.com/martini-contrib/render" +) + +var ( + mux *http.ServeMux + server *httptest.Server + fakeUAAServer *httptest.Server +) + +type MockRoute struct { + Method string + Endpoint string + Output []string + UserAgent string + Status int + QueryString string + PostForm *string +} + +type MockRouteWithRedirect struct { + MockRoute + RedirectLocation string +} + +func setup(mock MockRoute, t *testing.T) { + setupMultiple([]MockRoute{mock}, t) +} + +func setupWithRedirect(mock MockRouteWithRedirect, t *testing.T) { + setupMultipleWithRedirect([]MockRouteWithRedirect{mock}, t) +} + +func testQueryString(QueryString string, QueryStringExp string, t *testing.T) { + t.Helper() + if QueryStringExp == "" { + return + } + + value, _ := url.QueryUnescape(QueryString) + if QueryStringExp != value { + t.Errorf("Error: Query string '%s' should be equal to '%s'", QueryStringExp, value) + } +} + +func testUserAgent(UserAgent string, UserAgentExp string, t *testing.T) { + t.Helper() + if len(UserAgentExp) < 1 { + UserAgentExp = "Go-CF-client/1.1" + } + if UserAgent != UserAgentExp { + t.Errorf("Error: Agent %s should be equal to %s", UserAgent, UserAgentExp) + } +} + +func testReqBody(req *http.Request, postFormBody *string, t *testing.T) { + t.Helper() + if postFormBody != nil { + if body, err := ioutil.ReadAll(req.Body); err != nil { + t.Error("No request body but expected one") + } else { + defer req.Body.Close() + if strings.TrimSpace(string(body)) != strings.TrimSpace(*postFormBody) { + t.Errorf("Expected request body (%s) does not equal request body (%s)", *postFormBody, body) + } + } + } +} + +func testBodyContains(req *http.Request, expected *string, t *testing.T) { + t.Helper() + if expected != nil { + if body, err := ioutil.ReadAll(req.Body); err != nil { + t.Error("No request body but expected one") + } else { + defer req.Body.Close() + if !strings.Contains(string(body), *expected) { + t.Errorf("Expected request body (%s) was not found in actual request body (%s)", *expected, body) + } + } + } +} + +func setupMultiple(mockEndpoints []MockRoute, t *testing.T) { + mockEndpointsWithRedirect := make([]MockRouteWithRedirect, len(mockEndpoints)) + for i, mock := range mockEndpoints { + mockEndpointsWithRedirect[i] = MockRouteWithRedirect{ + MockRoute: mock, + RedirectLocation: "", + } + } + setupMultipleWithRedirect(mockEndpointsWithRedirect, t) +} + +func setupMultipleWithRedirect(mockEndpoints []MockRouteWithRedirect, t *testing.T) { + mux = http.NewServeMux() + server = httptest.NewServer(mux) + fakeUAAServer = FakeUAAServer(3) + m := martini.New() + m.Use(render.Renderer()) + r := martini.NewRouter() + for _, mock := range mockEndpoints { + method := mock.Method + endpoint := mock.Endpoint + output := mock.Output + if len(output) == 0 { + t.Fatal("Mock output cannot be an array of length 0, did you mean to use []string{\"\"}?") + } + userAgent := mock.UserAgent + status := mock.Status + queryString := mock.QueryString + postFormBody := mock.PostForm + redirectLocation := mock.RedirectLocation + switch method { + case "GET": + count := 0 + r.Get(endpoint, func(res http.ResponseWriter, req *http.Request) (int, string) { + testUserAgent(req.Header.Get("User-Agent"), userAgent, t) + testQueryString(req.URL.RawQuery, queryString, t) + if redirectLocation != "" { + res.Header().Add("Location", redirectLocation) + } + singleOutput := output[count] + count++ + return status, singleOutput + }) + case "POST": + r.Post(endpoint, func(req *http.Request) (int, string) { + testUserAgent(req.Header.Get("User-Agent"), userAgent, t) + testQueryString(req.URL.RawQuery, queryString, t) + testReqBody(req, postFormBody, t) + return status, output[0] + }) + case "DELETE": + r.Delete(endpoint, func(req *http.Request) (int, string) { + testUserAgent(req.Header.Get("User-Agent"), userAgent, t) + testQueryString(req.URL.RawQuery, queryString, t) + return status, output[0] + }) + case "PUT": + r.Put(endpoint, func(req *http.Request) (int, string) { + testUserAgent(req.Header.Get("User-Agent"), userAgent, t) + testQueryString(req.URL.RawQuery, queryString, t) + testReqBody(req, postFormBody, t) + return status, output[0] + }) + case "PATCH": + r.Patch(endpoint, func(req *http.Request) (int, string) { + testUserAgent(req.Header.Get("User-Agent"), userAgent, t) + testQueryString(req.URL.RawQuery, queryString, t) + testReqBody(req, postFormBody, t) + return status, output[0] + }) + case "PUT-FILE": + r.Put(endpoint, func(req *http.Request) (int, string) { + testUserAgent(req.Header.Get("User-Agent"), userAgent, t) + testBodyContains(req, postFormBody, t) + return status, output[0] + }) + } + } + r.Get("/v2/info", func(r render.Render) { + r.JSON(200, map[string]interface{}{ + "authorization_endpoint": fakeUAAServer.URL, + "token_endpoint": fakeUAAServer.URL, + "logging_endpoint": server.URL, + "name": "", + "build": "", + "support": "https://support.example.net", + "version": 0, + "description": "", + "min_cli_version": "6.23.0", + "min_recommended_cli_version": "6.23.0", + "api_version": "2.103.0", + "app_ssh_endpoint": "ssh.example.net:2222", + "app_ssh_host_key_fingerprint": "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:01", + "app_ssh_oauth_client": "ssh-proxy", + "doppler_logging_endpoint": "wss://doppler.example.net:443", + "routing_endpoint": "https://api.example.net/routing", + }) + + }) + + m.Action(r.Handle) + mux.Handle("/", m) +} + +func FakeUAAServer(expiresIn int) *httptest.Server { + mux := http.NewServeMux() + server := httptest.NewServer(mux) + m := martini.New() + m.Use(render.Renderer()) + r := martini.NewRouter() + count := 1 + r.Post("/oauth/token", func(r render.Render) { + r.JSON(200, map[string]interface{}{ + "token_type": "bearer", + "access_token": "foobar" + strconv.Itoa(count), + "refresh_token": "barfoo", + "expires_in": expiresIn, + }) + count = count + 1 + }) + r.NotFound(func() string { return "" }) + m.Action(r.Handle) + mux.Handle("/", m) + return server +} + +func teardown() { + server.Close() + fakeUAAServer.Close() +} diff --git a/third_party/go-cfclient/client.go b/third_party/go-cfclient/client.go new file mode 100644 index 000000000000..951fb0c48466 --- /dev/null +++ b/third_party/go-cfclient/client.go @@ -0,0 +1,551 @@ +package cfclient + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" + + "time" + + "github.com/pkg/errors" + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/clientcredentials" +) + +// Client used to communicate with Cloud Foundry +type Client struct { + Config Config + Endpoint Endpoint +} + +type Endpoint struct { + DopplerEndpoint string `json:"doppler_logging_endpoint"` + LoggingEndpoint string `json:"logging_endpoint"` + AuthEndpoint string `json:"authorization_endpoint"` + TokenEndpoint string `json:"token_endpoint"` + AppSSHEndpoint string `json:"app_ssh_endpoint"` + AppSSHOauthClient string `json:"app_ssh_oauth_client"` +} + +// Config is used to configure the creation of a client +type Config struct { + ApiAddress string `json:"api_url"` + Username string `json:"user"` + Password string `json:"password"` + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + SkipSslValidation bool `json:"skip_ssl_validation"` + HttpClient *http.Client + Token string `json:"auth_token"` + TokenSource oauth2.TokenSource + tokenSourceDeadline *time.Time + UserAgent string `json:"user_agent"` + Origin string `json:"-"` +} + +type LoginHint struct { + Origin string `json:"origin"` +} + +// Request is used to help build up a request +type Request struct { + method string + url string + params url.Values + body io.Reader + obj interface{} +} + +type cfHomeConfig struct { + AccessToken string + RefreshToken string + Target string + AuthorizationEndpoint string + OrganizationFields struct { + Name string + } + SpaceFields struct { + Name string + } + SSLDisabled bool +} + +func NewConfigFromCF() (*Config, error) { + return NewConfigFromCFHome("") +} + +func NewConfigFromCFHome(cfHomeDir string) (*Config, error) { + var err error + + if cfHomeDir == "" { + cfHomeDir = os.Getenv("CF_HOME") + if cfHomeDir == "" { + cfHomeDir, err = os.UserHomeDir() + if err != nil { + return nil, err + } + } + } + + cfHomeConfig, err := loadCFHomeConfig(cfHomeDir) + if err != nil { + return nil, err + } + + cfg := DefaultConfig() + cfg.Token = cfHomeConfig.AccessToken + cfg.ApiAddress = cfHomeConfig.Target + cfg.SkipSslValidation = cfHomeConfig.SSLDisabled + + return cfg, nil +} + +func loadCFHomeConfig(cfHomeDir string) (*cfHomeConfig, error) { + cfConfigDir := filepath.Join(cfHomeDir, ".cf") + cfJSON, err := ioutil.ReadFile(filepath.Join(cfConfigDir, "config.json")) + if err != nil { + return nil, err + } + + var cfg cfHomeConfig + err = json.Unmarshal(cfJSON, &cfg) + if err == nil { + if len(cfg.AccessToken) > len("bearer ") { + cfg.AccessToken = cfg.AccessToken[len("bearer "):] + } + } + + return &cfg, nil +} + +// DefaultConfig creates a default config object used by CF client +func DefaultConfig() *Config { + return &Config{ + ApiAddress: "http://api.bosh-lite.com", + Username: "admin", + Password: "admin", + Token: "", + SkipSslValidation: false, + HttpClient: http.DefaultClient, + UserAgent: "Go-CF-client/1.1", + } +} + +func DefaultEndpoint() *Endpoint { + return &Endpoint{ + DopplerEndpoint: "wss://doppler.10.244.0.34.xip.io:443", + LoggingEndpoint: "wss://loggregator.10.244.0.34.xip.io:443", + TokenEndpoint: "https://uaa.10.244.0.34.xip.io", + AuthEndpoint: "https://login.10.244.0.34.xip.io", + } +} + +// NewClient returns a new client +func NewClient(config *Config) (client *Client, err error) { + // bootstrap the config + defConfig := DefaultConfig() + + if len(config.ApiAddress) == 0 { + config.ApiAddress = defConfig.ApiAddress + } + + if len(config.Username) == 0 { + config.Username = defConfig.Username + } + + if len(config.Password) == 0 { + config.Password = defConfig.Password + } + + if len(config.Token) == 0 { + config.Token = defConfig.Token + } + + if len(config.UserAgent) == 0 { + config.UserAgent = defConfig.UserAgent + } + + if config.HttpClient == nil { + config.HttpClient = defConfig.HttpClient + } + + if config.HttpClient.Transport == nil { + config.HttpClient.Transport = shallowDefaultTransport() + } + + var tp *http.Transport + + switch t := config.HttpClient.Transport.(type) { + case *http.Transport: + tp = t + case *oauth2.Transport: + if bt, ok := t.Base.(*http.Transport); ok { + tp = bt + } + } + + if tp != nil { + if tp.TLSClientConfig == nil { + tp.TLSClientConfig = &tls.Config{} + } + tp.TLSClientConfig.InsecureSkipVerify = config.SkipSslValidation + } + + config.ApiAddress = strings.TrimRight(config.ApiAddress, "/") + + client = &Client{ + Config: *config, + } + + if err := client.refreshEndpoint(); err != nil { + return nil, err + } + + return client, nil +} + +func shallowDefaultTransport() *http.Transport { + defaultTransport := http.DefaultTransport.(*http.Transport) + return &http.Transport{ + Proxy: defaultTransport.Proxy, + TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout, + ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout, + } +} + +func getUserAuth(ctx context.Context, config Config, endpoint *Endpoint) (Config, error) { + authConfig := &oauth2.Config{ + ClientID: "cf", + Scopes: []string{""}, + Endpoint: oauth2.Endpoint{ + AuthURL: endpoint.AuthEndpoint + "/oauth/auth", + TokenURL: endpoint.TokenEndpoint + "/oauth/token", + }, + } + if config.Origin != "" { + loginHint := LoginHint{config.Origin} + origin, err := json.Marshal(loginHint) + if err != nil { + return config, errors.Wrap(err, "Error creating login_hint") + } + val := url.Values{} + val.Set("login_hint", string(origin)) + authConfig.Endpoint.TokenURL = fmt.Sprintf("%s?%s", authConfig.Endpoint.TokenURL, val.Encode()) + } + + token, err := authConfig.PasswordCredentialsToken(ctx, config.Username, config.Password) + if err != nil { + return config, errors.Wrap(err, "Error getting token") + } + + config.tokenSourceDeadline = &token.Expiry + config.TokenSource = authConfig.TokenSource(ctx, token) + config.HttpClient = oauth2.NewClient(ctx, config.TokenSource) + + return config, err +} + +func getClientAuth(ctx context.Context, config Config, endpoint *Endpoint) Config { + authConfig := &clientcredentials.Config{ + ClientID: config.ClientID, + ClientSecret: config.ClientSecret, + TokenURL: endpoint.TokenEndpoint + "/oauth/token", + } + + config.TokenSource = authConfig.TokenSource(ctx) + config.HttpClient = authConfig.Client(ctx) + return config +} + +// getUserTokenAuth initializes client credentials from existing bearer token. +func getUserTokenAuth(ctx context.Context, config Config, endpoint *Endpoint) Config { + authConfig := &oauth2.Config{ + ClientID: "cf", + Scopes: []string{""}, + Endpoint: oauth2.Endpoint{ + AuthURL: endpoint.AuthEndpoint + "/oauth/auth", + TokenURL: endpoint.TokenEndpoint + "/oauth/token", + }, + } + + // Token is expected to have no "bearer" prefix + token := &oauth2.Token{ + AccessToken: config.Token, + TokenType: "Bearer"} + + config.TokenSource = authConfig.TokenSource(ctx, token) + config.HttpClient = oauth2.NewClient(ctx, config.TokenSource) + + return config +} + +func getInfo(api string, httpClient *http.Client) (*Endpoint, error) { + var endpoint Endpoint + + if api == "" { + return DefaultEndpoint(), nil + } + + resp, err := httpClient.Get(api + "/v2/info") + if err != nil { + return nil, err + } + defer resp.Body.Close() + + err = decodeBody(resp, &endpoint) + if err != nil { + return nil, err + } + + return &endpoint, err +} + +// NewRequest is used to create a new Request +func (c *Client) NewRequest(method, path string) *Request { + r := &Request{ + method: method, + url: c.Config.ApiAddress + path, + params: make(map[string][]string), + } + return r +} + +// NewRequestWithBody is used to create a new request with +// arbigtrary body io.Reader. +func (c *Client) NewRequestWithBody(method, path string, body io.Reader) *Request { + r := c.NewRequest(method, path) + + // Set request body + r.body = body + + return r +} + +// DoRequest runs a request with our client +func (c *Client) DoRequest(r *Request) (*http.Response, error) { + req, err := r.toHTTP() + if err != nil { + return nil, err + } + return c.Do(req) +} + +// DoRequestWithoutRedirects executes the request without following redirects +func (c *Client) DoRequestWithoutRedirects(r *Request) (*http.Response, error) { + prevCheckRedirect := c.Config.HttpClient.CheckRedirect + c.Config.HttpClient.CheckRedirect = func(httpReq *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + defer func() { + c.Config.HttpClient.CheckRedirect = prevCheckRedirect + }() + return c.DoRequest(r) +} + +func (c *Client) Do(req *http.Request) (*http.Response, error) { + req.Header.Set("User-Agent", c.Config.UserAgent) + if req.Body != nil && req.Header.Get("Content-type") == "" { + req.Header.Set("Content-type", "application/json") + } + + resp, err := c.Config.HttpClient.Do(req) + if err != nil { + return nil, err + } + + if resp.StatusCode >= http.StatusBadRequest { + return c.handleError(resp) + } + + return resp, nil +} + +func (c *Client) handleError(resp *http.Response) (*http.Response, error) { + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return resp, CloudFoundryHTTPError{ + StatusCode: resp.StatusCode, + Status: resp.Status, + Body: body, + } + } + + // Unmarshal V2 error response + if strings.HasPrefix(resp.Request.URL.Path, "/v2/") { + var cfErr CloudFoundryError + if err := json.Unmarshal(body, &cfErr); err != nil { + return resp, CloudFoundryHTTPError{ + StatusCode: resp.StatusCode, + Status: resp.Status, + Body: body, + } + } + return nil, cfErr + } + + // Unmarshal a V3 error response and convert it into a V2 model + var cfErrorsV3 CloudFoundryErrorsV3 + if err := json.Unmarshal(body, &cfErrorsV3); err != nil { + return resp, CloudFoundryHTTPError{ + StatusCode: resp.StatusCode, + Status: resp.Status, + Body: body, + } + } + return nil, NewCloudFoundryErrorFromV3Errors(cfErrorsV3) +} + +func (c *Client) refreshEndpoint() error { + // we want to keep the Timeout value from config.HttpClient + timeout := c.Config.HttpClient.Timeout + + ctx := context.Background() + ctx = context.WithValue(ctx, oauth2.HTTPClient, c.Config.HttpClient) + + endpoint, err := getInfo(c.Config.ApiAddress, oauth2.NewClient(ctx, nil)) + + if err != nil { + return errors.Wrap(err, "Could not get api /v2/info") + } + + switch { + case c.Config.Token != "": + c.Config = getUserTokenAuth(ctx, c.Config, endpoint) + case c.Config.ClientID != "": + c.Config = getClientAuth(ctx, c.Config, endpoint) + default: + c.Config, err = getUserAuth(ctx, c.Config, endpoint) + if err != nil { + return err + } + } + // make sure original Timeout value will be used + if c.Config.HttpClient.Timeout != timeout { + c.Config.HttpClient.Timeout = timeout + } + + c.Endpoint = *endpoint + return nil +} + +// toHTTP converts the request to an HTTP Request +func (r *Request) toHTTP() (*http.Request, error) { + + // Check if we should encode the body + if r.body == nil && r.obj != nil { + b, err := encodeBody(r.obj) + if err != nil { + return nil, err + } + r.body = b + } + + // Create the HTTP Request + return http.NewRequest(r.method, r.url, r.body) +} + +// decodeBody is used to JSON decode a body +func decodeBody(resp *http.Response, out interface{}) error { + defer resp.Body.Close() + dec := json.NewDecoder(resp.Body) + return dec.Decode(out) +} + +// encodeBody is used to encode a request body +func encodeBody(obj interface{}) (io.Reader, error) { + buf := bytes.NewBuffer(nil) + enc := json.NewEncoder(buf) + if err := enc.Encode(obj); err != nil { + return nil, err + } + return buf, nil +} + +func (c *Client) GetToken() (string, error) { + if c.Config.tokenSourceDeadline != nil && c.Config.tokenSourceDeadline.Before(time.Now()) { + if err := c.refreshEndpoint(); err != nil { + return "", err + } + } + + token, err := c.Config.TokenSource.Token() + if err != nil { + return "", errors.Wrap(err, "Error getting bearer token") + } + return "bearer " + token.AccessToken, nil +} + +var ErrPreventRedirect = errors.New("prevent-redirect") + +func (c *Client) GetSSHCode() (string, error) { + authorizeUrl, err := url.Parse(c.Endpoint.TokenEndpoint) + if err != nil { + return "", err + } + + values := url.Values{} + values.Set("response_type", "code") + values.Set("grant_type", "authorization_code") + values.Set("client_id", c.Endpoint.AppSSHOauthClient) // client_id,used by cf server + + authorizeUrl.Path = "/oauth/authorize" + authorizeUrl.RawQuery = values.Encode() + + req, err := http.NewRequest("GET", authorizeUrl.String(), nil) + if err != nil { + return "", err + } + + token, err := c.GetToken() + if err != nil { + return "", err + } + + req.Header.Add("authorization", token) + httpClient := &http.Client{ + CheckRedirect: func(req *http.Request, _ []*http.Request) error { + return ErrPreventRedirect + }, + Timeout: 30 * time.Second, + Transport: &http.Transport{ + DisableKeepAlives: true, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: c.Config.SkipSslValidation, + }, + Proxy: http.ProxyFromEnvironment, + TLSHandshakeTimeout: 10 * time.Second, + }, + } + + resp, err := httpClient.Do(req) + if err == nil { + return "", errors.New("authorization server did not redirect with one time code") + } + defer resp.Body.Close() + if netErr, ok := err.(*url.Error); !ok || netErr.Err != ErrPreventRedirect { + return "", errors.New(fmt.Sprintf("error requesting one time code from server: %s", err.Error())) + } + + loc, err := resp.Location() + if err != nil { + return "", errors.New(fmt.Sprintf("error getting the redirected location: %s", err.Error())) + } + + codes := loc.Query()["code"] + if len(codes) != 1 { + return "", errors.New("unable to acquire one time code from authorization response") + } + + return codes[0], nil +} diff --git a/third_party/go-cfclient/client_test.go b/third_party/go-cfclient/client_test.go new file mode 100644 index 000000000000..6b7538cbbc37 --- /dev/null +++ b/third_party/go-cfclient/client_test.go @@ -0,0 +1,241 @@ +package cfclient + +import ( + "io/ioutil" + "net/http" + "os" + "path" + "testing" + "time" + + . "github.com/smartystreets/goconvey/convey" +) + +const cfCLIConfig = ` +{ + "ConfigVersion": 3, + "Target": "https://api.sys.example.com", + "APIVersion": "2.164.0", + "AuthorizationEndpoint": "https://login.sys.example.com", + "DopplerEndPoint": "wss://doppler.sys.example.com:443", + "UaaEndpoint": "https://uaa.sys.example.com", + "RoutingAPIEndpoint": "https://api.sys.example.com/routing", + "AccessToken": "bearer secret-bearer-token", + "SSHOAuthClient": "ssh-proxy", + "UAAOAuthClient": "cf", + "UAAOAuthClientSecret": "", + "UAAGrantType": "", + "RefreshToken": "secret-refresh-token", + "OrganizationFields": { + "GUID": "42754be1-f558-4d28-9c06-c706f6641245", + "Name": "system", + "QuotaDefinition": { + "guid": "", + "name": "", + "memory_limit": 0, + "instance_memory_limit": 0, + "total_routes": 0, + "total_services": 0, + "non_basic_services_allowed": false, + "app_instance_limit": 0, + "total_reserved_route_ports": 0 + } + }, + "SpaceFields": { + "GUID": "e42ccfe9-04bf-4cbc-ae16-f26741778a71", + "Name": "system", + "AllowSSH": true + }, + "SSLDisabled": true, + "AsyncTimeout": 0, + "Trace": "", + "ColorEnabled": "", + "Locale": "", + "PluginRepos": [ + { + "Name": "CF-Community", + "URL": "https://plugins.cloudfoundry.org" + } + ], + "MinCLIVersion": "6.23.0", + "MinRecommendedCLIVersion": "6.23.0" +} +` + +func TestNewConfigFromCFHome(t *testing.T) { + Convey("New config from CF_HOME", t, func() { + cfHomeDir, err := ioutil.TempDir("", "cf_home") + So(err, ShouldBeNil) + + configDir := path.Join(cfHomeDir, ".cf") + err = os.MkdirAll(configDir, 0744) + So(err, ShouldBeNil) + + configPath := path.Join(configDir, "config.json") + err = os.WriteFile(configPath, []byte(cfCLIConfig), 0744) + So(err, ShouldBeNil) + + cfg, err := NewConfigFromCFHome(cfHomeDir) + So(err, ShouldBeNil) + + So(cfg.Token, ShouldEqual, "secret-bearer-token") + So(cfg.ApiAddress, ShouldEqual, "https://api.sys.example.com") + So(cfg.SkipSslValidation, ShouldBeTrue) + }) +} + +func TestDefaultConfig(t *testing.T) { + Convey("Default config", t, func() { + c := DefaultConfig() + So(c.ApiAddress, ShouldEqual, "http://api.bosh-lite.com") + So(c.Username, ShouldEqual, "admin") + So(c.Password, ShouldEqual, "admin") + So(c.SkipSslValidation, ShouldEqual, false) + So(c.Token, ShouldEqual, "") + So(c.UserAgent, ShouldEqual, "Go-CF-client/1.1") + }) +} + +func TestRemovalofTrailingSlashOnAPIAddress(t *testing.T) { + Convey("Test removal of trailing slash of the API Address", t, func() { + setup(MockRoute{"GET", "/v2/organizations", []string{listOrgsPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL + "/", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + So(client.Config.ApiAddress, ShouldNotEndWith, "/") + }) +} + +func TestMakeRequest(t *testing.T) { + Convey("Test making request b", t, func() { + setup(MockRoute{"GET", "/v2/organizations", []string{listOrgsPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Username: "foo", + Password: "bar", + SkipSslValidation: true, + } + client, err := NewClient(c) + So(err, ShouldBeNil) + req := client.NewRequest("GET", "/v2/organizations") + resp, err := client.DoRequest(req) + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + }) +} + +func TestMakeRequestFailure(t *testing.T) { + Convey("Test making request b", t, func() { + setup(MockRoute{"GET", "/v2/organizations", []string{listOrgsPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Username: "foo", + Password: "bar", + SkipSslValidation: true, + } + client, err := NewClient(c) + So(err, ShouldBeNil) + req := client.NewRequest("GET", "/v2/organizations") + req.url = "%gh&%ij" + resp, err := client.DoRequest(req) + So(resp, ShouldBeNil) + So(err, ShouldNotBeNil) + }) +} + +func TestMakeRequestWithTimeout(t *testing.T) { + Convey("Test making request b", t, func() { + setup(MockRoute{"GET", "/v2/organizations", []string{listOrgsPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Username: "foo", + Password: "bar", + SkipSslValidation: true, + HttpClient: &http.Client{Timeout: 10 * time.Nanosecond}, + } + client, err := NewClient(c) + So(err, ShouldNotBeNil) + So(client, ShouldBeNil) + }) +} + +func TestHTTPErrorHandling(t *testing.T) { + Convey("Test making request b", t, func() { + setup(MockRoute{"GET", "/v2/organizations", []string{"502 Bad Gateway"}, "", 502, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Username: "foo", + Password: "bar", + SkipSslValidation: true, + } + client, err := NewClient(c) + So(err, ShouldBeNil) + req := client.NewRequest("GET", "/v2/organizations") + resp, err := client.DoRequest(req) + So(err, ShouldNotBeNil) + So(resp, ShouldNotBeNil) + + httpErr := err.(CloudFoundryHTTPError) + So(httpErr.StatusCode, ShouldEqual, 502) + So(httpErr.Status, ShouldEqual, "502 Bad Gateway") + So(string(httpErr.Body), ShouldEqual, "502 Bad Gateway") + }) +} + +func TestTokenRefresh(t *testing.T) { + Convey("Test making request", t, func() { + setup(MockRoute{"GET", "/v2/organizations", []string{listOrgsPayload}, "", 200, "", nil}, t) + fakeUAAServer = FakeUAAServer(1) + c := &Config{ + ApiAddress: server.URL, + Username: "foo", + Password: "bar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + token, err := client.GetToken() + So(err, ShouldBeNil) + So(token, ShouldEqual, "bearer foobar2") + + for i := 0; i < 5; i++ { + token, _ = client.GetToken() + if token == "bearer foobar3" { + break + } + time.Sleep(time.Second) + } + So(token, ShouldEqual, "bearer foobar3") + }) +} + +func TestEndpointRefresh(t *testing.T) { + Convey("Test expiring endpoint", t, func() { + setup(MockRoute{"GET", "/v2/organizations", []string{listOrgsPayload}, "", 200, "", nil}, t) + fakeUAAServer = FakeUAAServer(0) + + c := &Config{ + ApiAddress: server.URL, + Username: "foo", + Password: "bar", + } + + client, err := NewClient(c) + So(err, ShouldBeNil) + + lastTokenSource := client.Config.TokenSource + for i := 1; i < 5; i++ { + _, err := client.GetToken() + So(err, ShouldBeNil) + So(client.Config.TokenSource, ShouldNotEqual, lastTokenSource) + lastTokenSource = client.Config.TokenSource + } + }) +} diff --git a/third_party/go-cfclient/domains.go b/third_party/go-cfclient/domains.go new file mode 100644 index 000000000000..b33e767a7b3b --- /dev/null +++ b/third_party/go-cfclient/domains.go @@ -0,0 +1,314 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" + + "github.com/pkg/errors" +) + +type DomainsResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []DomainResource `json:"resources"` +} + +type SharedDomainsResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []SharedDomainResource `json:"resources"` +} + +type DomainResource struct { + Meta Meta `json:"metadata"` + Entity Domain `json:"entity"` +} + +type SharedDomainResource struct { + Meta Meta `json:"metadata"` + Entity SharedDomain `json:"entity"` +} + +type Domain struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + OwningOrganizationGuid string `json:"owning_organization_guid"` + OwningOrganizationUrl string `json:"owning_organization_url"` + SharedOrganizationsUrl string `json:"shared_organizations_url"` + c *Client +} + +type SharedDomain struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + RouterGroupGuid string `json:"router_group_guid"` + RouterGroupType string `json:"router_group_type"` + Internal bool `json:"internal"` + c *Client +} + +func (c *Client) ListDomainsByQuery(query url.Values) ([]Domain, error) { + var domains []Domain + requestURL := "/v2/private_domains?" + query.Encode() + for { + var domainResp DomainsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting domains") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading domains request") + } + + err = json.Unmarshal(resBody, &domainResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling domains") + } + for _, domain := range domainResp.Resources { + domain.Entity.Guid = domain.Meta.Guid + domain.Entity.CreatedAt = domain.Meta.CreatedAt + domain.Entity.UpdatedAt = domain.Meta.UpdatedAt + domain.Entity.c = c + domains = append(domains, domain.Entity) + } + requestURL = domainResp.NextUrl + if requestURL == "" { + break + } + } + return domains, nil +} + +func (c *Client) ListDomains() ([]Domain, error) { + return c.ListDomainsByQuery(nil) +} + +func (c *Client) ListSharedDomainsByQuery(query url.Values) ([]SharedDomain, error) { + var domains []SharedDomain + requestURL := "/v2/shared_domains?" + query.Encode() + for { + var domainResp SharedDomainsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting shared domains") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading shared domains request") + } + + err = json.Unmarshal(resBody, &domainResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling shared domains") + } + for _, domain := range domainResp.Resources { + domain.Entity.Guid = domain.Meta.Guid + domain.Entity.CreatedAt = domain.Meta.CreatedAt + domain.Entity.UpdatedAt = domain.Meta.UpdatedAt + domain.Entity.c = c + domains = append(domains, domain.Entity) + } + requestURL = domainResp.NextUrl + if requestURL == "" { + break + } + } + return domains, nil +} + +func (c *Client) ListSharedDomains() ([]SharedDomain, error) { + return c.ListSharedDomainsByQuery(nil) +} + +func (c *Client) GetSharedDomainByGuid(guid string) (SharedDomain, error) { + r := c.NewRequest("GET", "/v2/shared_domains/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return SharedDomain{}, errors.Wrap(err, "Error requesting shared domain") + } + defer resp.Body.Close() + retval, err := c.handleSharedDomainResp(resp) + return *retval, err +} + +func (c *Client) CreateSharedDomain(name string, internal bool, router_group_guid string) (*SharedDomain, error) { + req := c.NewRequest("POST", "/v2/shared_domains") + params := map[string]interface{}{ + "name": name, + "internal": internal, + } + + if strings.TrimSpace(router_group_guid) != "" { + params["router_group_guid"] = router_group_guid + } + + req.obj = params + + resp, err := c.DoRequest(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, errors.Wrapf(err, "Error creating shared domain %s, response code: %d", name, resp.StatusCode) + } + return c.handleSharedDomainResp(resp) +} + +func (c *Client) DeleteSharedDomain(guid string, async bool) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/shared_domains/%s?async=%t", guid, async))) + if err != nil { + return err + } + defer resp.Body.Close() + if (async && (resp.StatusCode != http.StatusAccepted)) || (!async && (resp.StatusCode != http.StatusNoContent)) { + return errors.Wrapf(err, "Error deleting organization %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) GetDomainByName(name string) (Domain, error) { + q := url.Values{} + q.Set("q", "name:"+name) + domains, err := c.ListDomainsByQuery(q) + if err != nil { + return Domain{}, errors.Wrapf(err, "Error during domain lookup %s", name) + } + if len(domains) == 0 { + cfErr := NewDomainNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, name) + return Domain{}, cfErr + } + return domains[0], nil +} + +func (c *Client) GetDomainByGuid(guid string) (Domain, error) { + r := c.NewRequest("GET", "/v2/private_domains/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return Domain{}, errors.Wrap(err, "Error requesting private domain") + } + defer resp.Body.Close() + retval, err := c.handleDomainResp(resp) + return *retval, err +} + +func (c *Client) GetSharedDomainByName(name string) (SharedDomain, error) { + q := url.Values{} + q.Set("q", "name:"+name) + domains, err := c.ListSharedDomainsByQuery(q) + if err != nil { + return SharedDomain{}, errors.Wrapf(err, "Error during shared domain lookup %s", name) + } + if len(domains) == 0 { + cfErr := NewDomainNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, name) + return SharedDomain{}, cfErr + } + return domains[0], nil +} + +func (c *Client) CreateDomain(name, orgGuid string) (*Domain, error) { + req := c.NewRequest("POST", "/v2/private_domains") + req.obj = map[string]interface{}{ + "name": name, + "owning_organization_guid": orgGuid, + } + resp, err := c.DoRequest(req) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusCreated { + return nil, errors.Wrapf(err, "Error creating domain %s, response code: %d", name, resp.StatusCode) + } + return c.handleDomainResp(resp) +} + +func (c *Client) DeleteDomain(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/private_domains/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting domain %s, response code: %d", guid, resp.StatusCode) + } + return nil +} +func (c *Client) handleDomainResp(resp *http.Response) (*Domain, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + var domainResource DomainResource + err = json.Unmarshal(body, &domainResource) + if err != nil { + return nil, err + } + return c.mergeDomainResource(domainResource), nil +} + +func (c *Client) handleSharedDomainResp(resp *http.Response) (*SharedDomain, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + var domainResource SharedDomainResource + err = json.Unmarshal(body, &domainResource) + if err != nil { + return nil, err + } + return c.mergeSharedDomainResource(domainResource), nil +} + +func (c *Client) getDomainsResponse(requestURL string) (DomainsResponse, error) { + var domainResp DomainsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return DomainsResponse{}, errors.Wrap(err, "Error requesting domains") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return DomainsResponse{}, errors.Wrap(err, "Error reading domains request") + } + err = json.Unmarshal(resBody, &domainResp) + if err != nil { + return DomainsResponse{}, errors.Wrap(err, "Error unmarshalling org") + } + return domainResp, nil +} + +func (c *Client) mergeDomainResource(domainResource DomainResource) *Domain { + domainResource.Entity.Guid = domainResource.Meta.Guid + domainResource.Entity.c = c + domainResource.Entity.CreatedAt = domainResource.Meta.CreatedAt + domainResource.Entity.UpdatedAt = domainResource.Meta.UpdatedAt + return &domainResource.Entity +} + +func (c *Client) mergeSharedDomainResource(domainResource SharedDomainResource) *SharedDomain { + domainResource.Entity.Guid = domainResource.Meta.Guid + domainResource.Entity.c = c + domainResource.Entity.CreatedAt = domainResource.Meta.CreatedAt + domainResource.Entity.UpdatedAt = domainResource.Meta.UpdatedAt + return &domainResource.Entity +} diff --git a/third_party/go-cfclient/domains_test.go b/third_party/go-cfclient/domains_test.go new file mode 100644 index 000000000000..d1099b9c066a --- /dev/null +++ b/third_party/go-cfclient/domains_test.go @@ -0,0 +1,278 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListDomains(t *testing.T) { + Convey("List domains", t, func() { + setup(MockRoute{"GET", "/v2/private_domains", []string{listDomainsPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + domains, err := client.ListDomains() + So(err, ShouldBeNil) + + So(len(domains), ShouldEqual, 4) + So(domains[0].Guid, ShouldEqual, "b2a35f0c-d5ad-4a59-bea7-461711d96b0d") + So(domains[0].Name, ShouldEqual, "vcap.me") + So(domains[0].OwningOrganizationGuid, ShouldEqual, "4cf3bc47-eccd-4662-9322-7833c3bdcded") + So(domains[0].OwningOrganizationUrl, ShouldEqual, "/v2/organizations/4cf3bc47-eccd-4662-9322-7833c3bdcded") + So(domains[0].SharedOrganizationsUrl, ShouldEqual, "/v2/private_domains/b2a35f0c-d5ad-4a59-bea7-461711d96b0d/shared_organizations") + }) +} + +func TestListSharedDomains(t *testing.T) { + Convey("List shared domains", t, func() { + setup(MockRoute{"GET", "/v2/shared_domains", []string{listSharedDomainsPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + domains, err := client.ListSharedDomains() + So(err, ShouldBeNil) + + So(len(domains), ShouldEqual, 1) + So(domains[0].Guid, ShouldEqual, "91977695-8ad9-40db-858f-4df782603ec3") + So(domains[0].Name, ShouldEqual, "domain-49.example.com") + So(domains[0].RouterGroupGuid, ShouldEqual, "my-random-guid") + So(domains[0].RouterGroupType, ShouldEqual, "tcp") + }) + + Convey("List shared domain by guid", t, func() { + setup(MockRoute{"GET", "/v2/shared_domains/91977695-8ad9-40db-858f-4df782603ec3", []string{listSharedDomainByGuidPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + domain, err := client.GetSharedDomainByGuid("91977695-8ad9-40db-858f-4df782603ec3") + So(err, ShouldBeNil) + + So(domain.Guid, ShouldEqual, "91977695-8ad9-40db-858f-4df782603ec3") + So(domain.Name, ShouldEqual, "apps.some.random.cf.installation.example.com") + So(domain.CreatedAt, ShouldEqual, "2016-06-08T16:41:37Z") + So(domain.UpdatedAt, ShouldEqual, "2016-06-08T16:41:26Z") + }) + +} + +func TestGetDomainByName(t *testing.T) { + Convey("Get domain by name", t, func() { + setup(MockRoute{"GET", "/v2/private_domains", []string{listDomainsPayload}, "", 200, "q=name:vcap.me", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + domain, err := client.GetDomainByName("vcap.me") + So(err, ShouldBeNil) + + So(domain.Guid, ShouldEqual, "b2a35f0c-d5ad-4a59-bea7-461711d96b0d") + So(domain.Name, ShouldEqual, "vcap.me") + }) + Convey("Get domain by name with an endpoint that returns a 404", t, func() { + setup(MockRoute{"GET", "/v2/private_domains", []string{listDomainsEmptyResponse}, "", 404, "q=name:vcap.me", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + _, listErr := client.GetDomainByName("vcap.me") + So(listErr, ShouldNotBeNil) + }) + Convey("Get domain by name for a non-existing domain", t, func() { + setup(MockRoute{"GET", "/v2/private_domains", []string{listDomainsEmptyResponse}, "", 200, "q=name:idontexist", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + _, listErr := client.GetDomainByName("idontexist") + So(listErr, ShouldNotBeNil) + }) + Convey("Get private domain by guid", t, func() { + setup(MockRoute{"GET", "/v2/private_domains/b2a35f0c-d5ad-4a59-bea7-461711d96b0d", []string{listDomainByGuidPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + domain, err := client.GetDomainByGuid("b2a35f0c-d5ad-4a59-bea7-461711d96b0d") + So(err, ShouldBeNil) + + So(domain.Guid, ShouldEqual, "b2a35f0c-d5ad-4a59-bea7-461711d96b0d") + So(domain.Name, ShouldEqual, "vcap.me") + So(domain.CreatedAt, ShouldEqual, "2016-06-08T16:41:39Z") + So(domain.UpdatedAt, ShouldEqual, "2016-06-08T16:41:26Z") + }) +} + +func TestGetSharedDomainByName(t *testing.T) { + Convey("Get shared domain by name", t, func() { + setup(MockRoute{"GET", "/v2/shared_domains", []string{listSharedDomainsPayload}, "", 200, "q=name:domain-49.example.com", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + domain, err := client.GetSharedDomainByName("domain-49.example.com") + So(err, ShouldBeNil) + + So(domain.Guid, ShouldEqual, "91977695-8ad9-40db-858f-4df782603ec3") + So(domain.Name, ShouldEqual, "domain-49.example.com") + }) + Convey("Get shared domain by name with an endpoint that returns a 404", t, func() { + setup(MockRoute{"GET", "/v2/shared_domains", []string{listDomainsEmptyResponse}, "", 404, "q=name:domain-49.example.com", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + _, listErr := client.GetSharedDomainByName("domain-49.example.com") + So(listErr, ShouldNotBeNil) + }) + Convey("Get shared domain by name for a non-existing domain", t, func() { + setup(MockRoute{"GET", "/v2/shared_domains", []string{listDomainsEmptyResponse}, "", 200, "q=name:idontexist", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + _, listErr := client.GetSharedDomainByName("idontexist") + So(listErr, ShouldNotBeNil) + }) +} + +func TestCreateDomain(t *testing.T) { + Convey("Create domain", t, func() { + setup(MockRoute{"POST", "/v2/private_domains", []string{postDomainPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + domain, err := client.CreateDomain("exmaple.com", "8483e4f1-d3a3-43e2-ab8c-b05ea40ef8db") + So(err, ShouldBeNil) + + So(domain.Guid, ShouldEqual, "b98aeca1-22b9-49f9-8428-3ace9ea2ba11") + }) +} + +func TestCreateSharedDomain(t *testing.T) { + Convey("Create external shared domain", t, func() { + setup(MockRoute{"POST", "/v2/shared_domains", []string{postExternalSharedDomainPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + domain, err := client.CreateSharedDomain("shared-example.com", false, "8483e4f1-d3a3-43e2-ab8c-b05ea40ef8db") + So(err, ShouldBeNil) + So(domain.Internal, ShouldBeFalse) + So(domain.RouterGroupType, ShouldEqual, "tcp") + }) + + Convey("Create external shared domain", t, func() { + setup(MockRoute{"POST", "/v2/shared_domains", []string{postInternalSharedDomainPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + domain, err := client.CreateSharedDomain("shared-example.com", false, "8483e4f1-d3a3-43e2-ab8c-b05ea40ef8db") + So(err, ShouldBeNil) + So(domain.Internal, ShouldBeTrue) + So(domain.RouterGroupType, ShouldBeBlank) + }) +} + +func TestDeleteDomain(t *testing.T) { + Convey("Delete domain", t, func() { + setup(MockRoute{"DELETE", "/v2/private_domains/b2a35f0c-d5ad-4a59-bea7-461711d96b0d", []string{""}, "", 204, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteDomain("b2a35f0c-d5ad-4a59-bea7-461711d96b0d") + So(err, ShouldBeNil) + }) +} + +func TestDeleteSharedDomain(t *testing.T) { + Convey("Delete shared domain synchronously", t, func() { + setup(MockRoute{"DELETE", "/v2/shared_domains/b2a35f0c-d5ad-4a59-bea7-461711d96b0d", []string{""}, "", 204, "async=false", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteSharedDomain("b2a35f0c-d5ad-4a59-bea7-461711d96b0d", false) + So(err, ShouldBeNil) + }) + + Convey("Delete shared domain synchronously", t, func() { + setup(MockRoute{"DELETE", "/v2/shared_domains/b2a35f0c-d5ad-4a59-bea7-461711d96b0d", []string{""}, "", 202, "async=true", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteSharedDomain("b2a35f0c-d5ad-4a59-bea7-461711d96b0d", true) + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/environmentvariablegroups.go b/third_party/go-cfclient/environmentvariablegroups.go new file mode 100644 index 000000000000..4d8da1825f8b --- /dev/null +++ b/third_party/go-cfclient/environmentvariablegroups.go @@ -0,0 +1,64 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" +) + +type EnvironmentVariableGroup map[string]interface{} + +func (c *Client) GetRunningEnvironmentVariableGroup() (EnvironmentVariableGroup, error) { + return c.getEnvironmentVariableGroup(true) +} + +func (c *Client) GetStagingEnvironmentVariableGroup() (EnvironmentVariableGroup, error) { + return c.getEnvironmentVariableGroup(false) +} + +func (c *Client) getEnvironmentVariableGroup(running bool) (EnvironmentVariableGroup, error) { + evgType := "staging" + if running { + evgType = "running" + } + + req := c.NewRequest("GET", fmt.Sprintf("/v2/config/environment_variable_groups/%s", evgType)) + resp, err := c.DoRequest(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + evg := EnvironmentVariableGroup{} + err = json.NewDecoder(resp.Body).Decode(&evg) + return evg, err +} + +func (c *Client) SetRunningEnvironmentVariableGroup(evg EnvironmentVariableGroup) error { + return c.setEnvironmentVariableGroup(evg, true) +} + +func (c *Client) SetStagingEnvironmentVariableGroup(evg EnvironmentVariableGroup) error { + return c.setEnvironmentVariableGroup(evg, false) +} + +func (c *Client) setEnvironmentVariableGroup(evg EnvironmentVariableGroup, running bool) error { + evgType := "staging" + if running { + evgType = "running" + } + + marshalled, err := json.Marshal(evg) + if err != nil { + return err + } + + req := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/config/environment_variable_groups/%s", evgType), bytes.NewBuffer(marshalled)) + resp, err := c.DoRequest(req) + if err != nil { + return err + } + defer resp.Body.Close() + + return nil +} diff --git a/third_party/go-cfclient/environmentvariablegroups_test.go b/third_party/go-cfclient/environmentvariablegroups_test.go new file mode 100644 index 000000000000..97ab1b7c4f7c --- /dev/null +++ b/third_party/go-cfclient/environmentvariablegroups_test.go @@ -0,0 +1,83 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestEnvionmentVariableGroups(t *testing.T) { + Convey("List Running Environment Variable Group", t, func() { + setup(MockRoute{"GET", "/v2/config/environment_variable_groups/running", []string{getEVGPayload}, "", 200, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + evg, err := client.GetRunningEnvironmentVariableGroup() + So(err, ShouldBeNil) + So(evg["foo"], ShouldEqual, "bar") + So(evg["val"], ShouldEqual, 3) + }) + + Convey("List Staging Environment Variable Group", t, func() { + setup(MockRoute{"GET", "/v2/config/environment_variable_groups/staging", []string{getEVGPayload}, "", 200, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + evg, err := client.GetStagingEnvironmentVariableGroup() + So(err, ShouldBeNil) + So(evg["foo"], ShouldEqual, "bar") + So(evg["val"], ShouldEqual, 3) + }) + + Convey("Set Running Environment Variable Group", t, func() { + setup(MockRoute{"PUT", "/v2/config/environment_variable_groups/running", []string{getEVGPayload}, "", 200, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + body := EnvironmentVariableGroup{ + "foo": "bar", + "val": 3, + } + + err = client.SetRunningEnvironmentVariableGroup(body) + So(err, ShouldBeNil) + }) + + Convey("Set Staging Environment Variable Group", t, func() { + setup(MockRoute{"PUT", "/v2/config/environment_variable_groups/staging", []string{getEVGPayload}, "", 200, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + body := EnvironmentVariableGroup{ + "foo": "bar", + "val": 3, + } + + err = client.SetStagingEnvironmentVariableGroup(body) + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/error.go b/third_party/go-cfclient/error.go new file mode 100644 index 000000000000..e4decdf88be0 --- /dev/null +++ b/third_party/go-cfclient/error.go @@ -0,0 +1,54 @@ +package cfclient + +//go:generate go run gen_error.go + +import ( + "fmt" +) + +type CloudFoundryError struct { + Code int `json:"code"` + ErrorCode string `json:"error_code"` + Description string `json:"description"` +} + +type CloudFoundryErrorsV3 struct { + Errors []CloudFoundryErrorV3 `json:"errors"` +} + +type CloudFoundryErrorV3 struct { + Code int `json:"code"` + Title string `json:"title"` + Detail string `json:"detail"` +} + +// CF APIs v3 can return multiple errors, we take the first one and convert it into a V2 model +func NewCloudFoundryErrorFromV3Errors(cfErrorsV3 CloudFoundryErrorsV3) CloudFoundryError { + if len(cfErrorsV3.Errors) == 0 { + return CloudFoundryError{ + 0, + "GO-Client-No-Errors", + "No Errors in response from V3", + } + } + + return CloudFoundryError{ + cfErrorsV3.Errors[0].Code, + cfErrorsV3.Errors[0].Title, + cfErrorsV3.Errors[0].Detail, + } +} + +func (cfErr CloudFoundryError) Error() string { + return fmt.Sprintf("cfclient error (%s|%d): %s", cfErr.ErrorCode, cfErr.Code, cfErr.Description) +} + +type CloudFoundryHTTPError struct { + StatusCode int + Status string + Body []byte +} + +func (e CloudFoundryHTTPError) Error() string { + return fmt.Sprintf("cfclient: HTTP error (%d): %s", e.StatusCode, e.Status) +} diff --git a/third_party/go-cfclient/events.go b/third_party/go-cfclient/events.go new file mode 100644 index 000000000000..6c42c565b297 --- /dev/null +++ b/third_party/go-cfclient/events.go @@ -0,0 +1,95 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/url" + + "github.com/pkg/errors" +) + +// EventsResponse is a type that wraps a collection of event resources. +type EventsResponse struct { + TotalResults int `json:"total_results"` + Pages int `json:"total_pages"` + NextURL string `json:"next_url"` + Resources []EventResource `json:"resources"` +} + +// EventResource is a type that contains metadata and the entity for an event. +type EventResource struct { + Meta Meta `json:"metadata"` + Entity Event `json:"entity"` +} + +// Event is a type that contains event data. +type Event struct { + GUID string `json:"guid"` + Type string `json:"type"` + CreatedAt string `json:"created_at"` + Actor string `json:"actor"` + ActorType string `json:"actor_type"` + ActorName string `json:"actor_name"` + ActorUsername string `json:"actor_username"` + Actee string `json:"actee"` + ActeeType string `json:"actee_type"` + ActeeName string `json:"actee_name"` + OrganizationGUID string `json:"organization_guid"` + SpaceGUID string `json:"space_guid"` + Metadata map[string]interface{} `json:"metadata"` + c *Client +} + +// ListEventsByQuery lists all events matching the provided query. +func (c *Client) ListEventsByQuery(query url.Values) ([]Event, error) { + var events []Event + requestURL := fmt.Sprintf("/v2/events?%s", query.Encode()) + for { + var eventResp EventsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "error requesting events") + } + defer resp.Body.Close() + if err := json.NewDecoder(resp.Body).Decode(&eventResp); err != nil { + return nil, errors.Wrap(err, "error unmarshaling events") + } + for _, e := range eventResp.Resources { + e.Entity.GUID = e.Meta.Guid + e.Entity.CreatedAt = e.Meta.CreatedAt + e.Entity.c = c + events = append(events, e.Entity) + } + requestURL = eventResp.NextURL + if requestURL == "" { + break + } + } + return events, nil +} + +// ListEvents lists all unfiltered events. +func (c *Client) ListEvents() ([]Event, error) { + return c.ListEventsByQuery(nil) +} + +// TotalEventsByQuery returns the number of events matching the provided query. +func (c *Client) TotalEventsByQuery(query url.Values) (int, error) { + r := c.NewRequest("GET", fmt.Sprintf("/v2/events?%s", query.Encode())) + resp, err := c.DoRequest(r) + if err != nil { + return 0, errors.Wrap(err, "error requesting events") + } + defer resp.Body.Close() + var apiResp EventsResponse + if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil { + return 0, errors.Wrap(err, "error unmarshaling events") + } + return apiResp.TotalResults, nil +} + +// TotalEvents returns the number of unfiltered events. +func (c *Client) TotalEvents() (int, error) { + return c.TotalEventsByQuery(nil) +} diff --git a/third_party/go-cfclient/events_test.go b/third_party/go-cfclient/events_test.go new file mode 100644 index 000000000000..cdaf67ea1eb4 --- /dev/null +++ b/third_party/go-cfclient/events_test.go @@ -0,0 +1,57 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListEvents(t *testing.T) { + Convey("List Events", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/events", []string{listEventsPage1Payload}, "", 200, "", nil}, + {"GET", "/v2/events-2", []string{listEventsPage2Payload}, "", 200, "page=2", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + events, err := client.ListEvents() + So(err, ShouldBeNil) + + So(len(events), ShouldEqual, 4) + So(events[0].GUID, ShouldEqual, "b8ede8e1-afc8-40a1-baae-236a0a77b27b") + So(events[0].Actor, ShouldEqual, "guid-008640fc-d316-4602-9251-c8d09bbdc750") + So(events[0].CreatedAt, ShouldEqual, "2016-06-08T16:41:23Z") + So(events[0].Metadata, ShouldHaveLength, 3) + So(events[0].Metadata["name-188"], ShouldEqual, "value-188") + So(events[0].Metadata["name-189"], ShouldEqual, 189) + So(events[0].Metadata["name-190"], ShouldEqual, true) + }) +} + +func TestTotalEvents(t *testing.T) { + Convey("Total Events", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/events", []string{totalEventsPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + total, err := client.TotalEvents() + So(err, ShouldBeNil) + + So(total, ShouldEqual, 4) + }) +} diff --git a/third_party/go-cfclient/gen_error.go b/third_party/go-cfclient/gen_error.go new file mode 100644 index 000000000000..948d1d6f98be --- /dev/null +++ b/third_party/go-cfclient/gen_error.go @@ -0,0 +1,151 @@ +//go:build tools +// +build tools + +package main + +import ( + "bytes" + "go/format" + "io/ioutil" + "log" + "net/http" + "sort" + "strings" + "text/template" + "time" + + "go.yaml.in/yaml/v2" +) + +type ( + CFCode int + HTTPCode int +) + +type Definition struct { + CFCode `yaml:"-"` + Name string `yaml:"name"` + HTTPCode `yaml:"http_code"` + Message string `yaml:"message"` +} + +func main() { + log.SetFlags(log.Lshortfile) + const url = "https://raw.githubusercontent.com/cloudfoundry/cloud_controller_ng/master/errors/v2.yml" + resp, err := http.Get(url) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + + var m map[CFCode]Definition + + if err := yaml.Unmarshal(body, &m); err != nil { + log.Fatal(err) + } + + var definitions []Definition + + for c, d := range m { + d.CFCode = c + definitions = append(definitions, d) + } + + sort.Slice(definitions, func(i, j int) bool { + return definitions[i].CFCode < definitions[j].CFCode + }) + + buf := &bytes.Buffer{} + + if err := packageTemplate.Execute(buf, struct { + Timestamp time.Time + Definitions []Definition + }{ + Timestamp: time.Now(), + Definitions: definitions, + }); err != nil { + log.Fatal(err) + } + + dst, err := format.Source(buf.Bytes()) + if err != nil { + log.Printf("%s", buf.Bytes()) + log.Fatal(err) + } + + if err := ioutil.WriteFile("cf_error.go", dst, 0600); err != nil { + log.Fatal(err) + } +} + +// destutter ensures that s does not end in "Error". +func destutter(s string) string { + return strings.TrimSuffix(s, "Error") +} + +// cleanMessage removes any characters which will cause go generate to fail +func cleanMessage(s string) string { + return strings.Replace(s, "\n", "", -1) +} + +var packageTemplate = template.Must(template.New("").Funcs(template.FuncMap{ + "destutter": destutter, + "cleanMessage": cleanMessage, +}).Parse(` +package cfclient + +// Code generated by go generate. DO NOT EDIT. +// This file was generated by robots at +// {{ .Timestamp }} + +import ( + stderrors "errors" + + pkgerrors "github.com/pkg/errors" +) + +{{- range .Definitions }} +{{$isMethod := printf "Is%sError" (.Name | destutter) }} +{{$newMethod := printf "New%sError" (.Name | destutter) }} +// {{ $newMethod }} returns a new CloudFoundryError +// that {{ $isMethod }} will return true for +func {{ $newMethod }}() CloudFoundryError { + return CloudFoundryError{ + Code: {{ .CFCode }}, + ErrorCode: "CF-{{ .Name }}", + Description: "{{ .Message | cleanMessage }}", + } +} + +// {{ $isMethod }} returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: {{ .CFCode }} +// - HTTP code: {{ .HTTPCode }} +// - message: {{ printf "%q" .Message }} +func Is{{ .Name | destutter }}Error(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == {{ .CFCode }} +} +{{- end }} + +func cloudFoundryError(err error) (cferr CloudFoundryError, ok bool) { + type causer interface { + Cause() error + } + if _, isCauser := err.(causer); isCauser { + cause := pkgerrors.Cause(err) + cferr, ok = cause.(CloudFoundryError) + } else { + ok = stderrors.As(err, &cferr) + } + return cferr, ok +} +`)) diff --git a/third_party/go-cfclient/go.mod b/third_party/go-cfclient/go.mod new file mode 100644 index 000000000000..c95add18395c --- /dev/null +++ b/third_party/go-cfclient/go.mod @@ -0,0 +1,19 @@ +module github.com/cloudfoundry-community/go-cfclient/v2 + +go 1.15 + +require ( + github.com/Masterminds/semver v1.4.2 + github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect + github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.8 // indirect + github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11 + github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 // indirect + github.com/pkg/errors v0.8.1 + github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a + golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 + golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1 + google.golang.org/protobuf v1.28.0 // indirect + go.yaml.in/yaml/v2 v2.4.4 // indirect +) diff --git a/third_party/go-cfclient/go.sum b/third_party/go-cfclient/go.sum new file mode 100644 index 000000000000..215107b64d96 --- /dev/null +++ b/third_party/go-cfclient/go.sum @@ -0,0 +1,55 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11 h1:YFh+sjyJTMQSYjKwM4dFKhJPJC/wfo98tPUc17HdoYw= +github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:Ah2dBMoxZEqk118as2T4u4fjfXarE0pPnMJaArZQZsI= +github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso= +github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1 h1:VeAkjQVzKLmu+JnFcK96TPbkuaTIqwGGAzQ9hgwPjVg= +golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/third_party/go-cfclient/info.go b/third_party/go-cfclient/info.go new file mode 100644 index 000000000000..5e204d495512 --- /dev/null +++ b/third_party/go-cfclient/info.go @@ -0,0 +1,104 @@ +package cfclient + +import ( + "encoding/json" + + "github.com/Masterminds/semver" + "github.com/pkg/errors" +) + +// Info is metadata about a Cloud Foundry deployment +type Info struct { + Name string `json:"name"` + Build string `json:"build"` + Support string `json:"support"` + Version int `json:"version"` + Description string `json:"description"` + AuthorizationEndpoint string `json:"authorization_endpoint"` + TokenEndpoint string `json:"token_endpoint"` + MinCLIVersion string `json:"min_cli_version"` + MinRecommendedCLIVersion string `json:"min_recommended_cli_version"` + APIVersion string `json:"api_version"` + AppSSHEndpoint string `json:"app_ssh_endpoint"` + AppSSHHostKeyFingerprint string `json:"app_ssh_host_key_fingerprint"` + AppSSHOauthClient string `json:"app_ssh_oauth_client"` + DopplerLoggingEndpoint string `json:"doppler_logging_endpoint"` + RoutingEndpoint string `json:"routing_endpoint"` + User string `json:"user,omitempty"` +} + +type V3Version struct { + Links struct { + CCV3 struct { + Meta struct { + Version string `json:"version"` + } `json:"meta"` + } `json:"cloud_controller_v3"` + } `json:"links"` +} + +// GetInfo retrieves Info from the Cloud Controller API +func (c *Client) GetInfo() (*Info, error) { + r := c.NewRequest("GET", "/v2/info") + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting info") + } + defer resp.Body.Close() + var i Info + err = json.NewDecoder(resp.Body).Decode(&i) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling info") + } + return &i, nil +} + +func (c *Client) SupportsMetadataAPI() (bool, error) { + r := c.NewRequest("GET", "/") + resp, err := c.DoRequest(r) + if err != nil { + return false, errors.Wrap(err, "Error requesting info") + } + defer resp.Body.Close() + var v3 V3Version + err = json.NewDecoder(resp.Body).Decode(&v3) + if err != nil { + return false, errors.Wrap(err, "Error unmarshalling info") + } + + minimumSupportedVersion := semver.MustParse("3.66.0") + actualVersion, err := semver.NewVersion(v3.Links.CCV3.Meta.Version) + if err != nil { + return false, errors.Wrap(err, "Error parsing semver") + } + if !actualVersion.LessThan(minimumSupportedVersion) { + return true, nil + } + + return false, nil +} + +func (c *Client) SupportsSpaceSupporterRole() (bool, error) { + r := c.NewRequest("GET", "/") + resp, err := c.DoRequest(r) + if err != nil { + return false, errors.Wrap(err, "Error requesting info") + } + defer resp.Body.Close() + var v3 V3Version + err = json.NewDecoder(resp.Body).Decode(&v3) + if err != nil { + return false, errors.Wrap(err, "Error unmarshalling info") + } + + minimumSupportedVersion := semver.MustParse("3.102.0") + actualVersion, err := semver.NewVersion(v3.Links.CCV3.Meta.Version) + if err != nil { + return false, errors.Wrap(err, "Error parsing semver") + } + if !actualVersion.LessThan(minimumSupportedVersion) { + return true, nil + } + + return false, nil +} diff --git a/third_party/go-cfclient/info_test.go b/third_party/go-cfclient/info_test.go new file mode 100644 index 000000000000..867c1ebbc22a --- /dev/null +++ b/third_party/go-cfclient/info_test.go @@ -0,0 +1,249 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestGetInfo(t *testing.T) { + Convey("Get info", t, func() { + setupMultiple(nil, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + info, err := client.GetInfo() + So(err, ShouldBeNil) + + So(info.MinCLIVersion, ShouldEqual, "6.23.0") + }) + + Convey("Doesn't support metadata api", t, func() { + setupMultiple([]MockRoute{ + { + Method: "GET", + Endpoint: "/", + Status: 200, + Output: []string{`{ + "links": { + "cloud_controller_v3": { + "href": "https://api.dev.cfdev.sh/v3", + "meta": { + "version": "3.65.0" + } + } + } + }`}, + }, + }, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + supports, err := client.SupportsMetadataAPI() + So(err, ShouldBeNil) + + So(supports, ShouldEqual, false) + }) + + Convey("Support metadata api", t, func() { + setupMultiple([]MockRoute{ + { + Method: "GET", + Endpoint: "/", + Status: 200, + Output: []string{`{ + "links": { + "cloud_controller_v3": { + "href": "https://api.dev.cfdev.sh/v3", + "meta": { + "version": "3.66.0" + } + } + } + }`}, + }, + }, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + supports, err := client.SupportsMetadataAPI() + So(err, ShouldBeNil) + + So(supports, ShouldEqual, true) + }) + + Convey("CAPI reports a version that is not semver", t, func() { + setupMultiple([]MockRoute{ + { + Method: "GET", + Endpoint: "/", + Status: 200, + Output: []string{`{ + "links": { + "cloud_controller_v3": { + "href": "https://api.dev.cfdev.sh/v3", + "meta": { + "version": "non-semver" + } + } + } + }`}, + }, + }, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + _, err = client.SupportsMetadataAPI() + So(err, ShouldBeError) + }) + + Convey("the api version is lexicographically smaller than the required version, but is larger in semver", t, func() { + setupMultiple([]MockRoute{ + { + Method: "GET", + Endpoint: "/", + Status: 200, + Output: []string{`{ + "links": { + "cloud_controller_v3": { + "href": "https://api.dev.cfdev.sh/v3", + "meta": { + "version": "3.101.0" + } + } + } + }`}, + }, + }, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + supports, err := client.SupportsMetadataAPI() + So(err, ShouldBeNil) + + So(supports, ShouldEqual, true) + }) + + Convey("Supports space supporter min version", t, func() { + setupMultiple([]MockRoute{ + { + Method: "GET", + Endpoint: "/", + Status: 200, + Output: []string{`{ + "links": { + "cloud_controller_v3": { + "href": "https://api.dev.cfdev.sh/v3", + "meta": { + "version": "3.102.0" + } + } + } + }`}, + }, + }, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + supports, err := client.SupportsSpaceSupporterRole() + So(err, ShouldBeNil) + + So(supports, ShouldEqual, true) + }) + + Convey("Supports space supporter greater than min version", t, func() { + setupMultiple([]MockRoute{ + { + Method: "GET", + Endpoint: "/", + Status: 200, + Output: []string{`{ + "links": { + "cloud_controller_v3": { + "href": "https://api.dev.cfdev.sh/v3", + "meta": { + "version": "3.103.0" + } + } + } + }`}, + }, + }, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + supports, err := client.SupportsSpaceSupporterRole() + So(err, ShouldBeNil) + + So(supports, ShouldEqual, true) + }) + + Convey("Does not supports space supporter", t, func() { + setupMultiple([]MockRoute{ + { + Method: "GET", + Endpoint: "/", + Status: 200, + Output: []string{`{ + "links": { + "cloud_controller_v3": { + "href": "https://api.dev.cfdev.sh/v3", + "meta": { + "version": "3.101.0" + } + } + } + }`}, + }, + }, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + supports, err := client.SupportsSpaceSupporterRole() + So(err, ShouldBeNil) + + So(supports, ShouldEqual, false) + }) +} diff --git a/third_party/go-cfclient/isolationsegments.go b/third_party/go-cfclient/isolationsegments.go new file mode 100644 index 000000000000..5635df6088fa --- /dev/null +++ b/third_party/go-cfclient/isolationsegments.go @@ -0,0 +1,263 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "time" + + "github.com/pkg/errors" +) + +type IsolationSegment struct { + GUID string `json:"guid"` + Name string `json:"name"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + c *Client +} + +type IsolationSegementResponse struct { + GUID string `json:"guid"` + Name string `json:"name"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Links struct { + Self struct { + Href string `json:"href"` + } `json:"self"` + Spaces struct { + Href string `json:"href"` + } `json:"spaces"` + Organizations struct { + Href string `json:"href"` + } `json:"organizations"` + } `json:"links"` +} + +type ListIsolationSegmentsResponse struct { + Pagination Pagination `json:"pagination"` + Resources []IsolationSegementResponse `json:"resources"` +} + +func (c *Client) CreateIsolationSegment(name string) (*IsolationSegment, error) { + req := c.NewRequest("POST", "/v3/isolation_segments") + req.obj = map[string]interface{}{ + "name": name, + } + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating isolation segment") + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating isolation segment %s, response code: %d", name, resp.StatusCode) + } + return respBodyToIsolationSegment(resp.Body, c) +} + +func respBodyToIsolationSegment(body io.ReadCloser, c *Client) (*IsolationSegment, error) { + bodyRaw, err := ioutil.ReadAll(body) + if err != nil { + return nil, err + } + isr := IsolationSegementResponse{} + err = json.Unmarshal(bodyRaw, &isr) + if err != nil { + return nil, err + } + + return &IsolationSegment{ + GUID: isr.GUID, + Name: isr.Name, + CreatedAt: isr.CreatedAt, + UpdatedAt: isr.UpdatedAt, + c: c, + }, nil +} + +func (c *Client) GetIsolationSegmentByGUID(guid string) (*IsolationSegment, error) { + var isr IsolationSegementResponse + r := c.NewRequest("GET", "/v3/isolation_segments/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting isolation segment by GUID") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading isolation segment response body") + } + + err = json.Unmarshal(resBody, &isr) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling isolation segment response") + } + return &IsolationSegment{Name: isr.Name, GUID: isr.GUID, CreatedAt: isr.CreatedAt, UpdatedAt: isr.UpdatedAt, c: c}, nil +} + +func (c *Client) ListIsolationSegmentsByQuery(query url.Values) ([]IsolationSegment, error) { + var iss []IsolationSegment + requestURL := "/v3/isolation_segments" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + for { + var isr ListIsolationSegmentsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting isolation segments") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading isolation segment request") + } + + err = json.Unmarshal(resBody, &isr) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling isolation segment") + } + + for _, is := range isr.Resources { + iss = append(iss, IsolationSegment{ + Name: is.Name, + GUID: is.GUID, + CreatedAt: is.CreatedAt, + UpdatedAt: is.UpdatedAt, + c: c, + }) + } + + requestURL = isr.Pagination.Next.Href + if requestURL == "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, err + } + } + return iss, nil +} + +func (c *Client) ListIsolationSegments() ([]IsolationSegment, error) { + return c.ListIsolationSegmentsByQuery(nil) +} + +// TODO listOrgsForIsolationSegments +// TODO listSpacesForIsolationSegments +// TODO setDefaultIsolationSegmentForOrg + +func (c *Client) DeleteIsolationSegmentByGUID(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v3/isolation_segments/%s", guid))) + if err != nil { + return errors.Wrap(err, "Error during sending DELETE request for isolation segments") + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("Error deleting isolation segment %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (i *IsolationSegment) Delete() error { + return i.c.DeleteIsolationSegmentByGUID(i.GUID) +} + +func (c *Client) AddIsolationSegmentToOrg(isolationSegmentGUID, orgGUID string) error { + isoSegment := IsolationSegment{GUID: isolationSegmentGUID, c: c} + return isoSegment.AddOrg(orgGUID) +} + +func (c *Client) RemoveIsolationSegmentFromOrg(isolationSegmentGUID, orgGUID string) error { + isoSegment := IsolationSegment{GUID: isolationSegmentGUID, c: c} + return isoSegment.RemoveOrg(orgGUID) +} + +func (c *Client) AddIsolationSegmentToSpace(isolationSegmentGUID, spaceGUID string) error { + isoSegment := IsolationSegment{GUID: isolationSegmentGUID, c: c} + return isoSegment.AddSpace(spaceGUID) +} + +func (c *Client) RemoveIsolationSegmentFromSpace(isolationSegmentGUID, spaceGUID string) error { + isoSegment := IsolationSegment{GUID: isolationSegmentGUID, c: c} + return isoSegment.RemoveSpace(spaceGUID) +} + +func (i *IsolationSegment) AddOrg(orgGuid string) error { + if i == nil || i.c == nil { + return errors.New("No communication handle.") + } + req := i.c.NewRequest("POST", fmt.Sprintf("/v3/isolation_segments/%s/relationships/organizations", i.GUID)) + type Entry struct { + GUID string `json:"guid"` + } + req.obj = map[string]interface{}{ + "data": []Entry{{GUID: orgGuid}}, + } + resp, err := i.c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error during adding org to isolation segment") + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Error adding org %s to isolation segment %s, response code: %d", orgGuid, i.Name, resp.StatusCode) + } + return nil +} + +func (i *IsolationSegment) RemoveOrg(orgGuid string) error { + if i == nil || i.c == nil { + return errors.New("No communication handle.") + } + req := i.c.NewRequest("DELETE", fmt.Sprintf("/v3/isolation_segments/%s/relationships/organizations/%s", i.GUID, orgGuid)) + resp, err := i.c.DoRequest(req) + if err != nil { + return errors.Wrapf(err, "Error during removing org %s in isolation segment %s", orgGuid, i.Name) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("Error deleting org %s in isolation segment %s, response code: %d", orgGuid, i.Name, resp.StatusCode) + } + return nil +} + +func (i *IsolationSegment) AddSpace(spaceGuid string) error { + if i == nil || i.c == nil { + return errors.New("No communication handle.") + } + req := i.c.NewRequest("PUT", fmt.Sprintf("/v2/spaces/%s", spaceGuid)) + req.obj = map[string]interface{}{ + "isolation_segment_guid": i.GUID, + } + resp, err := i.c.DoRequest(req) + if err != nil { + return errors.Wrapf(err, "Error during adding space %s to isolation segment %s", spaceGuid, i.Name) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return fmt.Errorf("Error adding space to isolation segment %s, response code: %d", i.Name, resp.StatusCode) + } + return nil +} + +func (i *IsolationSegment) RemoveSpace(spaceGuid string) error { + if i == nil || i.c == nil { + return errors.New("No communication handle.") + } + req := i.c.NewRequest("DELETE", fmt.Sprintf("/v2/spaces/%s/isolation_segment", spaceGuid)) + resp, err := i.c.DoRequest(req) + if err != nil { + return errors.Wrapf(err, "Error during deleting space %s in isolation segment %s", spaceGuid, i.Name) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("Error deleting space %s from isolation segment %s, response code: %d", spaceGuid, i.Name, resp.StatusCode) + } + return nil +} diff --git a/third_party/go-cfclient/isolationsegments_test.go b/third_party/go-cfclient/isolationsegments_test.go new file mode 100644 index 000000000000..749526e4765a --- /dev/null +++ b/third_party/go-cfclient/isolationsegments_test.go @@ -0,0 +1,184 @@ +package cfclient + +import ( + "net/http" + "testing" + "time" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestCreateIsolationSegment(t *testing.T) { + Convey("Create Isolation Segment", t, func() { + mocks := []MockRoute{ + {"POST", "/v3/isolation_segments", []string{createIsolationSegmentPayload}, "", http.StatusCreated, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + name := "TheKittenIsTheShark" + + isolationsegment, err := client.CreateIsolationSegment(name) + So(err, ShouldBeNil) + + So(isolationsegment.Name, ShouldEqual, name) + So(isolationsegment.GUID, ShouldEqual, "323f211e-fea3-4161-9bd1-615392327913") + So(isolationsegment.CreatedAt.String(), ShouldEqual, time.Date(2016, 10, 19, 20, 25, 04, 0, time.FixedZone("UTC", 0)).String()) + So(isolationsegment.UpdatedAt.String(), ShouldEqual, time.Date(2016, 11, 8, 16, 41, 26, 0, time.FixedZone("UTC", 0)).String()) + }) +} + +func TestGetIsolationSegmentByGUID(t *testing.T) { + Convey("Request existing Isolation Segment", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/isolation_segments/323f211e-fea3-4161-9bd1-615392327913", []string{createIsolationSegmentPayload}, "", http.StatusOK, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + name := "TheKittenIsTheShark" + + isolationsegment, err := client.GetIsolationSegmentByGUID("323f211e-fea3-4161-9bd1-615392327913") + So(err, ShouldBeNil) + + So(isolationsegment.Name, ShouldEqual, name) + So(isolationsegment.GUID, ShouldEqual, "323f211e-fea3-4161-9bd1-615392327913") + So(isolationsegment.CreatedAt.String(), ShouldEqual, time.Date(2016, 10, 19, 20, 25, 04, 0, time.FixedZone("UTC", 0)).String()) + So(isolationsegment.UpdatedAt.String(), ShouldEqual, time.Date(2016, 11, 8, 16, 41, 26, 0, time.FixedZone("UTC", 0)).String()) + }) + + Convey("Request non-existing Isolation Segment", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/isolation_segments/323f211e-fea3-4161--9bd1-615392327913", []string{createIsolationSegmentPayload}, "", http.StatusOK, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + isolationsegment, err := client.GetIsolationSegmentByGUID("does not exit") + So(err, ShouldNotBeNil) + So(isolationsegment, ShouldBeNil) + }) +} + +func TestListIsolationSegments(t *testing.T) { + Convey("Request list of all Isolation Segments", t, func() { + setup(MockRoute{"GET", "/v3/isolation_segments", []string{listIsolationSegmentsPayloadPage1, listIsolationSegmentsPayloadPage2}, "", http.StatusOK, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + isolationsegment, err := client.ListIsolationSegments() + So(err, ShouldBeNil) + So(isolationsegment, ShouldNotBeNil) + So(len(isolationsegment), ShouldEqual, 4) + + So(isolationsegment[0].Name, ShouldEqual, "shared") + So(isolationsegment[0].GUID, ShouldEqual, "033b4c58-12bb-499a-b05d-4b6fc9e2993b") + So(isolationsegment[0].CreatedAt.String(), ShouldEqual, time.Date(2017, 4, 2, 11, 22, 4, 0, time.FixedZone("UTC", 0)).String()) + So(isolationsegment[0].UpdatedAt.String(), ShouldEqual, time.Date(2017, 4, 2, 11, 22, 4, 0, time.FixedZone("UTC", 0)).String()) + + So(isolationsegment[1].Name, ShouldEqual, "my_segment") + So(isolationsegment[1].GUID, ShouldEqual, "23d0baf4-9d3c-44d8-b2dc-1767bcdad1e0") + So(isolationsegment[1].CreatedAt.String(), ShouldEqual, time.Date(2017, 4, 7, 11, 20, 16, 0, time.FixedZone("UTC", 0)).String()) + So(isolationsegment[1].UpdatedAt.String(), ShouldEqual, time.Date(2017, 4, 7, 11, 20, 16, 0, time.FixedZone("UTC", 0)).String()) + + So(isolationsegment[2].Name, ShouldEqual, "shared1") + So(isolationsegment[2].GUID, ShouldEqual, "abcdefg12-12bb-499a-b05d-4b6fc9e2993b") + So(isolationsegment[2].CreatedAt.String(), ShouldEqual, time.Date(2017, 5, 2, 11, 22, 4, 0, time.FixedZone("UTC", 0)).String()) + So(isolationsegment[2].UpdatedAt.String(), ShouldEqual, time.Date(2017, 5, 2, 11, 22, 4, 0, time.FixedZone("UTC", 0)).String()) + + So(isolationsegment[3].Name, ShouldEqual, "my_segment1") + So(isolationsegment[3].GUID, ShouldEqual, "abcdef123-9d3c-44d8-b2dc-1767bcdad1e0") + So(isolationsegment[3].CreatedAt.String(), ShouldEqual, time.Date(2017, 5, 7, 11, 20, 16, 0, time.FixedZone("UTC", 0)).String()) + So(isolationsegment[3].UpdatedAt.String(), ShouldEqual, time.Date(2017, 5, 7, 11, 20, 16, 0, time.FixedZone("UTC", 0)).String()) + }) +} + +func TestDeleteIsolationSegmentByGUID(t *testing.T) { + Convey("Delete an Isolation Segment by GUID", t, func() { + mocks := []MockRoute{ + {"DELETE", "/v3/isolation_segments/033b4c58-12bb-499a-b05d-4b6fc9e2993b", []string{""}, "", http.StatusNoContent, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteIsolationSegmentByGUID("033b4c58-12bb-499a-b05d-4b6fc9e2993b") + So(err, ShouldBeNil) + + err = client.DeleteIsolationSegmentByGUID("theKittenIsTheShark") + So(err, ShouldNotBeNil) + }) +} + +func TestIsolationSegmentMethods(t *testing.T) { + + postData := `{"data":[{"guid":"theKittenIsTheShark"}]}` + + Convey("Request list of all Isolation Segments", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/isolation_segments", []string{listIsolationSegmentsPayload}, "", http.StatusOK, "", nil}, + {"DELETE", "/v3/isolation_segments/033b4c58-12bb-499a-b05d-4b6fc9e2993b", []string{""}, "", http.StatusNoContent, "", nil}, + {"POST", "/v3/isolation_segments/033b4c58-12bb-499a-b05d-4b6fc9e2993b/relationships/organizations", []string{""}, "", http.StatusOK, "", &postData}, + {"DELETE", "/v3/isolation_segments/033b4c58-12bb-499a-b05d-4b6fc9e2993b/relationships/organizations/theKittenIsTheShark", []string{""}, "", http.StatusNoContent, "", nil}, + {"PUT", "/v2/spaces/theKittenIsTheShark", []string{""}, "", http.StatusCreated, "", nil}, + {"DELETE", "/v2/spaces/theKittenIsTheShark/isolation_segment", []string{""}, "", http.StatusNoContent, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + isolationsegment, err := client.ListIsolationSegments() + + So(err, ShouldBeNil) + So(isolationsegment, ShouldNotBeNil) + So(len(isolationsegment), ShouldEqual, 2) + + errAddOrg := isolationsegment[0].AddOrg("theKittenIsTheShark") + So(errAddOrg, ShouldBeNil) + + errRemOrg := isolationsegment[0].RemoveOrg("theKittenIsTheShark") + So(errRemOrg, ShouldBeNil) + + errAddSpace := isolationsegment[0].AddSpace("theKittenIsTheShark") + So(errAddSpace, ShouldBeNil) + + errRemSpace := isolationsegment[0].RemoveSpace("theKittenIsTheShark") + So(errRemSpace, ShouldBeNil) + + errDel := isolationsegment[0].Delete() + So(errDel, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/metadata.go b/third_party/go-cfclient/metadata.go new file mode 100644 index 000000000000..661944baf9ab --- /dev/null +++ b/third_party/go-cfclient/metadata.go @@ -0,0 +1,165 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + + "github.com/pkg/errors" +) + +type MetadataHolder struct { + Metadata Metadata `json:"metadata"` +} + +type Metadata struct { + Annotations map[string]interface{} `json:"annotations"` + Labels map[string]interface{} `json:"labels"` +} + +func (m *Metadata) AddAnnotation(key string, value string) { + if m.Annotations == nil { + m.Annotations = make(map[string]interface{}) + } + m.Annotations[key] = value +} + +func (m *Metadata) RemoveAnnotation(key string) { + if m.Annotations == nil { + m.Annotations = make(map[string]interface{}) + } + m.Annotations[key] = nil +} + +func (m *Metadata) AddLabel(prefix, key string, value string) { + if m.Labels == nil { + m.Labels = make(map[string]interface{}) + } + if len(prefix) > 0 { + m.Labels[fmt.Sprintf("%s/%s", prefix, key)] = value + } else { + m.Labels[key] = value + } +} + +func (m *Metadata) RemoveLabel(prefix, key string) { + if m.Labels == nil { + m.Labels = make(map[string]interface{}) + } + if len(prefix) > 0 { + m.Labels[fmt.Sprintf("%s/%s", prefix, key)] = nil + } else { + m.Labels[key] = nil + } +} + +func (m *Metadata) Clear() *Metadata { + metadata := &Metadata{} + for key := range m.Annotations { + if strings.Contains(key, "/") { + metadata.RemoveAnnotation(strings.Split(key, "/")[1]) + } + metadata.RemoveAnnotation(key) + } + for key := range m.Labels { + metadata.RemoveLabel("", key) + } + return metadata +} + +func (c *Client) UpdateOrgMetadata(orgGUID string, metadata Metadata) error { + holder := MetadataHolder{} + holder.Metadata = metadata + requestURL := fmt.Sprintf("/v3/organizations/%s", orgGUID) + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(holder) + if err != nil { + return err + } + r := c.NewRequestWithBody("PATCH", requestURL, buf) + resp, err := c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "Error updating metadata for org %s, response code: %d", orgGUID, resp.StatusCode) + } + return nil +} + +func (c *Client) UpdateSpaceMetadata(spaceGUID string, metadata Metadata) error { + holder := MetadataHolder{} + holder.Metadata = metadata + requestURL := fmt.Sprintf("/v3/spaces/%s", spaceGUID) + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(holder) + if err != nil { + return err + } + r := c.NewRequestWithBody("PATCH", requestURL, buf) + resp, err := c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "Error updating metadata for space %s, response code: %d", spaceGUID, resp.StatusCode) + } + return nil +} + +func (c *Client) OrgMetadata(orgGUID string) (*Metadata, error) { + requestURL := fmt.Sprintf("/v3/organizations/%s", orgGUID) + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return &Metadata{}, errors.Wrap(err, "Error requesting space info") + } + defer resp.Body.Close() + return c.handleMetadataResp(resp) +} + +func (c *Client) SpaceMetadata(spaceGUID string) (*Metadata, error) { + requestURL := fmt.Sprintf("/v3/spaces/%s", spaceGUID) + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return &Metadata{}, errors.Wrap(err, "Error requesting space info") + } + defer resp.Body.Close() + return c.handleMetadataResp(resp) +} + +func (c *Client) RemoveOrgMetadata(orgGUID string) error { + metadata, err := c.OrgMetadata(orgGUID) + if err != nil { + return err + } + return c.UpdateOrgMetadata(orgGUID, *metadata.Clear()) +} + +func (c *Client) RemoveSpaceMetadata(spaceGUID string) error { + metadata, err := c.SpaceMetadata(spaceGUID) + if err != nil { + return err + } + return c.UpdateSpaceMetadata(spaceGUID, *metadata.Clear()) +} + +func (c *Client) handleMetadataResp(resp *http.Response) (*Metadata, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return &Metadata{}, err + } + var metadataResource MetadataHolder + err = json.Unmarshal(body, &metadataResource) + if err != nil { + return &Metadata{}, err + } + return &metadataResource.Metadata, nil +} diff --git a/third_party/go-cfclient/metadata_test.go b/third_party/go-cfclient/metadata_test.go new file mode 100644 index 000000000000..2aee3f564038 --- /dev/null +++ b/third_party/go-cfclient/metadata_test.go @@ -0,0 +1,189 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestUpdateOrgMetadata(t *testing.T) { + Convey("Set metadata on org", t, func() { + updateOrgMetadatPayload := `{"metadata":{"annotations":{"hello":"world"},"labels":{"foo":"bar"}}}` + mocks := []MockRoute{ + {"PATCH", "/v3/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", []string{""}, "", 200, "", &updateOrgMetadatPayload}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + metadata := Metadata{} + metadata.AddAnnotation("hello", "world") + metadata.AddLabel("", "foo", "bar") + + err = client.UpdateOrgMetadata("3b6f763f-aae1-4177-9b93-f2de6f2a48f2", metadata) + So(err, ShouldBeNil) + }) + + Convey("Remove metadata on org", t, func() { + updateOrgMetadatPayload := `{"metadata":{"annotations":{"hello":null},"labels":{"foo":null}}}` + mocks := []MockRoute{ + {"PATCH", "/v3/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", []string{""}, "", 200, "", &updateOrgMetadatPayload}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + metadata := Metadata{} + metadata.RemoveAnnotation("hello") + metadata.RemoveLabel("", "foo") + + err = client.UpdateOrgMetadata("3b6f763f-aae1-4177-9b93-f2de6f2a48f2", metadata) + So(err, ShouldBeNil) + }) +} +func TestUpdateSpaceMetadata(t *testing.T) { + Convey("Set metadata on org", t, func() { + updateSpaceMetadataPayload := `{"metadata":{"annotations":{"hello":"world"},"labels":{"foo":"bar"}}}` + mocks := []MockRoute{ + {"PATCH", "/v3/spaces/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", []string{""}, "", 200, "", &updateSpaceMetadataPayload}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + metadata := Metadata{} + metadata.AddAnnotation("hello", "world") + metadata.AddLabel("", "foo", "bar") + + err = client.UpdateSpaceMetadata("3b6f763f-aae1-4177-9b93-f2de6f2a48f2", metadata) + So(err, ShouldBeNil) + }) + + Convey("Remove metadata on space", t, func() { + updateSpaceMetadataPayload := `{"metadata":{"annotations":{"hello":null},"labels":{"foo":null}}}` + mocks := []MockRoute{ + {"PATCH", "/v3/spaces/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", []string{""}, "", 200, "", &updateSpaceMetadataPayload}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + metadata := Metadata{} + metadata.RemoveAnnotation("hello") + metadata.RemoveLabel("", "foo") + + err = client.UpdateSpaceMetadata("3b6f763f-aae1-4177-9b93-f2de6f2a48f2", metadata) + So(err, ShouldBeNil) + }) +} + +func TestSpaceMetadata(t *testing.T) { + Convey("Getting space metadata", t, func() { + spaceMetadataPayload := `{"guid": "3b6f763f-aae1-4177-9b93-f2de6f2a48f2","name": "space2","metadata":{"annotations":{"hello":"world"},"labels":{"foo":"bar"}}}` + mocks := []MockRoute{ + {"GET", "/v3/spaces/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", []string{spaceMetadataPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + metadata, err := client.SpaceMetadata("3b6f763f-aae1-4177-9b93-f2de6f2a48f2") + So(err, ShouldBeNil) + So(metadata, ShouldNotBeNil) + So(metadata.Labels, ShouldContainKey, "foo") + So(metadata.Annotations, ShouldContainKey, "hello") + }) + +} + +func TestOrgMetadata(t *testing.T) { + Convey("Getting org metadata", t, func() { + orgMetadataPayload := `{"guid": "3b6f763f-aae1-4177-9b93-f2de6f2a48f2","name": "space2","metadata":{"annotations":{"hello":"world"},"labels":{"foo":"bar"}}}` + mocks := []MockRoute{ + {"GET", "/v3/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", []string{orgMetadataPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + metadata, err := client.OrgMetadata("3b6f763f-aae1-4177-9b93-f2de6f2a48f2") + So(err, ShouldBeNil) + So(metadata, ShouldNotBeNil) + So(metadata.Labels, ShouldContainKey, "foo") + So(metadata.Annotations, ShouldContainKey, "hello") + }) +} + +func TestRemoveOrgMetadata(t *testing.T) { + Convey("Removing org metadata", t, func() { + orgMetadataPayload := `{"guid": "3b6f763f-aae1-4177-9b93-f2de6f2a48f2","name": "space2","metadata":{"annotations":{"hello":"world","prefix/foo":"bar"},"labels":{"foo":"bar"}}}` + updateMetadataPayload := `{"metadata":{"annotations":{"foo":null,"hello":null,"prefix/foo":null},"labels":{"foo":null}}}` + mocks := []MockRoute{ + {"GET", "/v3/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", []string{orgMetadataPayload}, "", 200, "", nil}, + {"PATCH", "/v3/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", []string{""}, "", 200, "", &updateMetadataPayload}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.RemoveOrgMetadata("3b6f763f-aae1-4177-9b93-f2de6f2a48f2") + So(err, ShouldBeNil) + }) +} + +func TestRemoveSpaceMetadata(t *testing.T) { + Convey("Removing org metadata", t, func() { + spaceMetadataPayload := `{"guid": "3b6f763f-aae1-4177-9b93-f2de6f2a48f2","name": "space2","metadata":{"annotations":{"hello":"world","prefix/foo":"bar"},"labels":{"foo":"bar"}}}` + updateMetadataPayload := `{"metadata":{"annotations":{"foo":null,"hello":null,"prefix/foo":null},"labels":{"foo":null}}}` + mocks := []MockRoute{ + {"GET", "/v3/spaces/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", []string{spaceMetadataPayload}, "", 200, "", nil}, + {"PATCH", "/v3/spaces/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", []string{""}, "", 200, "", &updateMetadataPayload}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.RemoveSpaceMetadata("3b6f763f-aae1-4177-9b93-f2de6f2a48f2") + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/org_quotas.go b/third_party/go-cfclient/org_quotas.go new file mode 100644 index 000000000000..f9a866ecddad --- /dev/null +++ b/third_party/go-cfclient/org_quotas.go @@ -0,0 +1,187 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type OrgQuotasResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []OrgQuotasResource `json:"resources"` +} + +type OrgQuotasResource struct { + Meta Meta `json:"metadata"` + Entity OrgQuota `json:"entity"` +} + +type OrgQuotaRequest struct { + Name string `json:"name"` + NonBasicServicesAllowed bool `json:"non_basic_services_allowed"` + TotalServices int `json:"total_services"` + TotalRoutes int `json:"total_routes"` + TotalPrivateDomains int `json:"total_private_domains"` + MemoryLimit int `json:"memory_limit"` + TrialDBAllowed bool `json:"trial_db_allowed"` + InstanceMemoryLimit int `json:"instance_memory_limit"` + AppInstanceLimit int `json:"app_instance_limit"` + AppTaskLimit int `json:"app_task_limit"` + TotalServiceKeys int `json:"total_service_keys"` + TotalReservedRoutePorts int `json:"total_reserved_route_ports"` +} + +type OrgQuota struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + NonBasicServicesAllowed bool `json:"non_basic_services_allowed"` + TotalServices int `json:"total_services"` + TotalRoutes int `json:"total_routes"` + TotalPrivateDomains int `json:"total_private_domains"` + MemoryLimit int `json:"memory_limit"` + TrialDBAllowed bool `json:"trial_db_allowed"` + InstanceMemoryLimit int `json:"instance_memory_limit"` + AppInstanceLimit int `json:"app_instance_limit"` + AppTaskLimit int `json:"app_task_limit"` + TotalServiceKeys int `json:"total_service_keys"` + TotalReservedRoutePorts int `json:"total_reserved_route_ports"` + c *Client +} + +func (c *Client) ListOrgQuotasByQuery(query url.Values) ([]OrgQuota, error) { + var orgQuotas []OrgQuota + requestURL := "/v2/quota_definitions?" + query.Encode() + for { + orgQuotasResp, err := c.getOrgQuotasResponse(requestURL) + if err != nil { + return []OrgQuota{}, err + } + for _, org := range orgQuotasResp.Resources { + org.Entity.Guid = org.Meta.Guid + org.Entity.CreatedAt = org.Meta.CreatedAt + org.Entity.UpdatedAt = org.Meta.UpdatedAt + org.Entity.c = c + orgQuotas = append(orgQuotas, org.Entity) + } + requestURL = orgQuotasResp.NextUrl + if requestURL == "" { + break + } + } + return orgQuotas, nil +} + +func (c *Client) ListOrgQuotas() ([]OrgQuota, error) { + return c.ListOrgQuotasByQuery(nil) +} + +func (c *Client) GetOrgQuotaByName(name string) (OrgQuota, error) { + q := url.Values{} + q.Set("q", "name:"+name) + orgQuotas, err := c.ListOrgQuotasByQuery(q) + if err != nil { + return OrgQuota{}, err + } + if len(orgQuotas) != 1 { + return OrgQuota{}, fmt.Errorf("Unable to find org quota " + name) + } + return orgQuotas[0], nil +} + +func (c *Client) getOrgQuotasResponse(requestURL string) (OrgQuotasResponse, error) { + var orgQuotasResp OrgQuotasResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return OrgQuotasResponse{}, errors.Wrap(err, "Error requesting org quotas") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return OrgQuotasResponse{}, errors.Wrap(err, "Error reading org quotas body") + } + err = json.Unmarshal(resBody, &orgQuotasResp) + if err != nil { + return OrgQuotasResponse{}, errors.Wrap(err, "Error unmarshalling org quotas") + } + return orgQuotasResp, nil +} + +func (c *Client) CreateOrgQuota(orgQuote OrgQuotaRequest) (*OrgQuota, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(orgQuote) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("POST", "/v2/quota_definitions", buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleOrgQuotaResp(resp) +} + +func (c *Client) UpdateOrgQuota(orgQuotaGUID string, orgQuota OrgQuotaRequest) (*OrgQuota, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(orgQuota) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/quota_definitions/%s", orgQuotaGUID), buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleOrgQuotaResp(resp) +} + +func (c *Client) DeleteOrgQuota(guid string, async bool) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/quota_definitions/%s?async=%t", guid, async))) + if err != nil { + return err + } + defer resp.Body.Close() + if (async && (resp.StatusCode != http.StatusAccepted)) || (!async && (resp.StatusCode != http.StatusNoContent)) { + return errors.Wrapf(err, "Error deleting organization %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) handleOrgQuotaResp(resp *http.Response) (*OrgQuota, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + var orgQuotasResource OrgQuotasResource + err = json.Unmarshal(body, &orgQuotasResource) + if err != nil { + return nil, err + } + return c.mergeOrgQuotaResource(orgQuotasResource), nil +} + +func (c *Client) mergeOrgQuotaResource(orgQuotaResource OrgQuotasResource) *OrgQuota { + orgQuotaResource.Entity.Guid = orgQuotaResource.Meta.Guid + orgQuotaResource.Entity.CreatedAt = orgQuotaResource.Meta.CreatedAt + orgQuotaResource.Entity.UpdatedAt = orgQuotaResource.Meta.UpdatedAt + orgQuotaResource.Entity.c = c + return &orgQuotaResource.Entity +} diff --git a/third_party/go-cfclient/org_quotas_test.go b/third_party/go-cfclient/org_quotas_test.go new file mode 100644 index 000000000000..f06a6aa0bebb --- /dev/null +++ b/third_party/go-cfclient/org_quotas_test.go @@ -0,0 +1,148 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListOrgQuotas(t *testing.T) { + Convey("List Org Quotas", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/quota_definitions", []string{listOrgQuotasPayloadPage1}, "", 200, "", nil}, + {"GET", "/v2/quota_definitions_page_2", []string{listOrgQuotasPayloadPage2}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + orgQuotas, err := client.ListOrgQuotas() + So(err, ShouldBeNil) + + So(len(orgQuotas), ShouldEqual, 2) + So(orgQuotas[0].Guid, ShouldEqual, "6f9d3100-44ab-49e2-a4f8-9d7d67651ae7") + So(orgQuotas[0].Name, ShouldEqual, "test-1") + So(orgQuotas[0].NonBasicServicesAllowed, ShouldEqual, true) + So(orgQuotas[0].TotalServices, ShouldEqual, -1) + So(orgQuotas[0].TotalRoutes, ShouldEqual, 100) + So(orgQuotas[0].TotalPrivateDomains, ShouldEqual, -1) + So(orgQuotas[0].MemoryLimit, ShouldEqual, 102400) + So(orgQuotas[0].TrialDBAllowed, ShouldEqual, false) + So(orgQuotas[0].InstanceMemoryLimit, ShouldEqual, -1) + So(orgQuotas[0].AppInstanceLimit, ShouldEqual, -1) + So(orgQuotas[0].AppTaskLimit, ShouldEqual, -1) + So(orgQuotas[0].TotalServiceKeys, ShouldEqual, -1) + So(orgQuotas[0].TotalReservedRoutePorts, ShouldEqual, -1) + }) +} + +func TestGetOrgQuotaByName(t *testing.T) { + Convey("Get Org Quota By Name", t, func() { + setup(MockRoute{"GET", "/v2/quota_definitions", []string{listOrgQuotasPayloadPage2}, "", 200, "q=name:default2", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + orgQuota, err := client.GetOrgQuotaByName("default2") + So(err, ShouldBeNil) + + So(orgQuota.Guid, ShouldEqual, "a537761f-9d93-4b30-af17-3d73dbca181b") + So(orgQuota.Name, ShouldEqual, "test-2") + So(orgQuota.NonBasicServicesAllowed, ShouldEqual, false) + So(orgQuota.TotalServices, ShouldEqual, 10) + So(orgQuota.TotalRoutes, ShouldEqual, 20) + So(orgQuota.TotalPrivateDomains, ShouldEqual, 30) + So(orgQuota.MemoryLimit, ShouldEqual, 40) + So(orgQuota.TrialDBAllowed, ShouldEqual, true) + So(orgQuota.InstanceMemoryLimit, ShouldEqual, 50) + So(orgQuota.AppInstanceLimit, ShouldEqual, 60) + So(orgQuota.AppTaskLimit, ShouldEqual, 70) + So(orgQuota.TotalServiceKeys, ShouldEqual, 80) + So(orgQuota.TotalReservedRoutePorts, ShouldEqual, 90) + }) +} + +func TestCreateOrgQuota(t *testing.T) { + Convey("Create Org Quota", t, func() { + setup(MockRoute{"POST", "/v2/quota_definitions", []string{orgQuotaPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + orgQuotaRequest := OrgQuotaRequest{ + Name: "test-2", + } + + orgQuota, err := client.CreateOrgQuota(orgQuotaRequest) + So(err, ShouldBeNil) + + So(orgQuota.Name, ShouldEqual, "test-2") + + }) +} + +func TestUpdateOrgQuota(t *testing.T) { + Convey("Create Update Quota", t, func() { + setup(MockRoute{"PUT", "/v2/quota_definitions/9ffd7c5c-d83c-4786-b399-b7bd54883977", []string{orgQuotaPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + orgQuotaRequest := OrgQuotaRequest{ + Name: "test-2", + } + + orgQuota, err := client.UpdateOrgQuota("9ffd7c5c-d83c-4786-b399-b7bd54883977", orgQuotaRequest) + So(err, ShouldBeNil) + + So(orgQuota.Name, ShouldEqual, "test-2") + + }) +} + +func TestDeleteOrgQuota(t *testing.T) { + Convey("Delete org quota synchronously", t, func() { + setup(MockRoute{"DELETE", "/v2/quota_definitions/b2a35f0c-d5ad-4a59-bea7-461711d96b0d", []string{""}, "", 204, "async=false", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteOrgQuota("b2a35f0c-d5ad-4a59-bea7-461711d96b0d", false) + So(err, ShouldBeNil) + }) + + Convey("Delete org quota asynchronously", t, func() { + setup(MockRoute{"DELETE", "/v2/quota_definitions/b2a35f0c-d5ad-4a59-bea7-461711d96b0d", []string{""}, "", 202, "async=true", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteOrgQuota("b2a35f0c-d5ad-4a59-bea7-461711d96b0d", true) + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/orgs.go b/third_party/go-cfclient/orgs.go new file mode 100644 index 000000000000..d2e656b79f35 --- /dev/null +++ b/third_party/go-cfclient/orgs.go @@ -0,0 +1,847 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type OrgResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []OrgResource `json:"resources"` +} + +type OrgResource struct { + Meta Meta `json:"metadata"` + Entity Org `json:"entity"` +} + +type OrgUserResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextURL string `json:"next_url"` + Resources []UserResource `json:"resources"` +} + +type Org struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Name string `json:"name"` + Status string `json:"status"` + QuotaDefinitionGuid string `json:"quota_definition_guid"` + DefaultIsolationSegmentGuid string `json:"default_isolation_segment_guid"` + c *Client +} + +type OrgSummary struct { + Guid string `json:"guid"` + Name string `json:"name"` + Status string `json:"status"` + Spaces []OrgSummarySpaces `json:"spaces"` +} + +type OrgSummarySpaces struct { + Guid string `json:"guid"` + Name string `json:"name"` + ServiceCount int `json:"service_count"` + AppCount int `json:"app_count"` + MemDevTotal int `json:"mem_dev_total"` + MemProdTotal int `json:"mem_prod_total"` +} + +type OrgRequest struct { + Name string `json:"name"` + Status string `json:"status,omitempty"` + QuotaDefinitionGuid string `json:"quota_definition_guid,omitempty"` + DefaultIsolationSegmentGuid string `json:"default_isolation_segment_guid,omitempty"` +} + +func (c *Client) ListOrgsByQuery(query url.Values) ([]Org, error) { + var orgs []Org + requestURL := "/v2/organizations?" + query.Encode() + for { + orgResp, err := c.getOrgResponse(requestURL) + if err != nil { + return []Org{}, err + } + for _, org := range orgResp.Resources { + orgs = append(orgs, c.mergeOrgResource(org)) + } + requestURL = orgResp.NextUrl + if requestURL == "" || query.Get("page") != "" { + break + } + } + return orgs, nil +} + +func (c *Client) ListOrgs() ([]Org, error) { + return c.ListOrgsByQuery(nil) +} + +func (c *Client) GetOrgByName(name string) (Org, error) { + var org Org + q := url.Values{} + q.Set("q", "name:"+name) + orgs, err := c.ListOrgsByQuery(q) + if err != nil { + return org, err + } + if len(orgs) == 0 { + cfErr := NewOrganizationNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, name) + return org, cfErr + } + return orgs[0], nil +} + +func (c *Client) GetOrgByGuid(guid string) (Org, error) { + var orgRes OrgResource + r := c.NewRequest("GET", "/v2/organizations/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return Org{}, err + } + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return Org{}, err + } + err = json.Unmarshal(body, &orgRes) + if err != nil { + return Org{}, err + } + return c.mergeOrgResource(orgRes), nil +} + +func (c *Client) OrgSpaces(guid string) ([]Space, error) { + return c.fetchSpaces(fmt.Sprintf("/v2/organizations/%s/spaces", guid), url.Values{}) +} + +func (o *Org) Summary() (OrgSummary, error) { + var orgSummary OrgSummary + requestURL := fmt.Sprintf("/v2/organizations/%s/summary", o.Guid) + r := o.c.NewRequest("GET", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return OrgSummary{}, errors.Wrap(err, "Error requesting org summary") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return OrgSummary{}, errors.Wrap(err, "Error reading org summary body") + } + err = json.Unmarshal(resBody, &orgSummary) + if err != nil { + return OrgSummary{}, errors.Wrap(err, "Error unmarshalling org summary") + } + return orgSummary, nil +} + +func (o *Org) Quota() (*OrgQuota, error) { + var orgQuota *OrgQuota + var orgQuotaResource OrgQuotasResource + if o.QuotaDefinitionGuid == "" { + return nil, nil + } + requestURL := fmt.Sprintf("/v2/quota_definitions/%s", o.QuotaDefinitionGuid) + r := o.c.NewRequest("GET", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return &OrgQuota{}, errors.Wrap(err, "Error requesting org quota") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return &OrgQuota{}, errors.Wrap(err, "Error reading org quota body") + } + err = json.Unmarshal(resBody, &orgQuotaResource) + if err != nil { + return &OrgQuota{}, errors.Wrap(err, "Error unmarshalling org quota") + } + orgQuota = &orgQuotaResource.Entity + orgQuota.Guid = orgQuotaResource.Meta.Guid + orgQuota.c = o.c + return orgQuota, nil +} + +func (c *Client) ListOrgUsersByQuery(orgGUID string, query url.Values) ([]User, error) { + var users []User + requestURL := fmt.Sprintf("/v2/organizations/%s/users?%s", orgGUID, query.Encode()) + for { + omResp, err := c.getOrgUserResponse(requestURL) + if err != nil { + return []User{}, err + } + for _, u := range omResp.Resources { + users = append(users, c.mergeUserResource(u)) + } + requestURL = omResp.NextURL + if requestURL == "" { + break + } + } + return users, nil +} + +func (c *Client) ListOrgUsers(orgGUID string) ([]User, error) { + return c.ListOrgUsersByQuery(orgGUID, nil) +} + +func (c *Client) listOrgRolesByQuery(orgGUID, role string, query url.Values) ([]User, error) { + var users []User + requestURL := fmt.Sprintf("/v2/organizations/%s/%s?%s", orgGUID, role, query.Encode()) + for { + omResp, err := c.getOrgUserResponse(requestURL) + if err != nil { + return []User{}, err + } + for _, u := range omResp.Resources { + users = append(users, c.mergeUserResource(u)) + } + requestURL = omResp.NextURL + if requestURL == "" { + break + } + } + return users, nil +} + +func (c *Client) ListOrgManagersByQuery(orgGUID string, query url.Values) ([]User, error) { + return c.listOrgRolesByQuery(orgGUID, "managers", query) +} + +func (c *Client) ListOrgManagers(orgGUID string) ([]User, error) { + return c.ListOrgManagersByQuery(orgGUID, nil) +} + +func (c *Client) ListOrgAuditorsByQuery(orgGUID string, query url.Values) ([]User, error) { + return c.listOrgRolesByQuery(orgGUID, "auditors", query) +} + +func (c *Client) ListOrgAuditors(orgGUID string) ([]User, error) { + return c.ListOrgAuditorsByQuery(orgGUID, nil) +} + +func (c *Client) ListOrgBillingManagersByQuery(orgGUID string, query url.Values) ([]User, error) { + return c.listOrgRolesByQuery(orgGUID, "billing_managers", query) +} + +func (c *Client) ListOrgBillingManagers(orgGUID string) ([]User, error) { + return c.ListOrgBillingManagersByQuery(orgGUID, nil) +} + +func (c *Client) AssociateOrgManager(orgGUID, userGUID string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateManager(userGUID) +} + +func (c *Client) AssociateOrgManagerByUsername(orgGUID, name string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateManagerByUsername(name) +} + +func (c *Client) AssociateOrgManagerByUsernameAndOrigin(orgGUID, name, origin string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateManagerByUsernameAndOrigin(name, origin) +} + +func (c *Client) AssociateOrgUser(orgGUID, userGUID string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateUser(userGUID) +} + +func (c *Client) AssociateOrgAuditor(orgGUID, userGUID string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateAuditor(userGUID) +} + +func (c *Client) AssociateOrgUserByUsername(orgGUID, name string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateUserByUsername(name) +} + +func (c *Client) AssociateOrgUserByUsernameAndOrigin(orgGUID, name, origin string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateUserByUsernameAndOrigin(name, origin) +} + +func (c *Client) AssociateOrgAuditorByUsername(orgGUID, name string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateAuditorByUsername(name) +} + +func (c *Client) AssociateOrgAuditorByUsernameAndOrigin(orgGUID, name, origin string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateAuditorByUsernameAndOrigin(name, origin) +} + +func (c *Client) AssociateOrgBillingManager(orgGUID, userGUID string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateBillingManager(userGUID) +} + +func (c *Client) AssociateOrgBillingManagerByUsername(orgGUID, name string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateBillingManagerByUsername(name) +} + +func (c *Client) AssociateOrgBillingManagerByUsernameAndOrigin(orgGUID, name, origin string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateBillingManagerByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveOrgManager(orgGUID, userGUID string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveManager(userGUID) +} + +func (c *Client) RemoveOrgManagerByUsername(orgGUID, name string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveManagerByUsername(name) +} + +func (c *Client) RemoveOrgManagerByUsernameAndOrigin(orgGUID, name, origin string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveManagerByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveOrgUser(orgGUID, userGUID string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveUser(userGUID) +} + +func (c *Client) RemoveOrgAuditor(orgGUID, userGUID string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveAuditor(userGUID) +} + +func (c *Client) RemoveOrgUserByUsername(orgGUID, name string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveUserByUsername(name) +} + +func (c *Client) RemoveOrgUserByUsernameAndOrigin(orgGUID, name, origin string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveUserByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveOrgAuditorByUsername(orgGUID, name string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveAuditorByUsername(name) +} + +func (c *Client) RemoveOrgAuditorByUsernameAndOrigin(orgGUID, name, origin string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveAuditorByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveOrgBillingManager(orgGUID, userGUID string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveBillingManager(userGUID) +} + +func (c *Client) RemoveOrgBillingManagerByUsername(orgGUID, name string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveBillingManagerByUsername(name) +} + +func (c *Client) RemoveOrgBillingManagerByUsernameAndOrigin(orgGUID, name, origin string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveBillingManagerByUsernameAndOrigin(name, origin) +} + +func (c *Client) ListOrgSpaceQuotas(orgGUID string) ([]SpaceQuota, error) { + org := Org{Guid: orgGUID, c: c} + return org.ListSpaceQuotas() +} + +func (c *Client) ListOrgPrivateDomains(orgGUID string) ([]Domain, error) { + org := Org{Guid: orgGUID, c: c} + return org.ListPrivateDomains() +} + +func (c *Client) ShareOrgPrivateDomain(orgGUID, privateDomainGUID string) (*Domain, error) { + org := Org{Guid: orgGUID, c: c} + return org.SharePrivateDomain(privateDomainGUID) +} + +func (c *Client) UnshareOrgPrivateDomain(orgGUID, privateDomainGUID string) error { + org := Org{Guid: orgGUID, c: c} + return org.UnsharePrivateDomain(privateDomainGUID) +} + +func (o *Org) ListSpaceQuotas() ([]SpaceQuota, error) { + var spaceQuotas []SpaceQuota + requestURL := fmt.Sprintf("/v2/organizations/%s/space_quota_definitions", o.Guid) + for { + spaceQuotasResp, err := o.c.getSpaceQuotasResponse(requestURL) + if err != nil { + return []SpaceQuota{}, err + } + for _, resource := range spaceQuotasResp.Resources { + spaceQuotas = append(spaceQuotas, *o.c.mergeSpaceQuotaResource(resource)) + } + requestURL = spaceQuotasResp.NextUrl + if requestURL == "" { + break + } + } + return spaceQuotas, nil +} + +func (o *Org) ListPrivateDomains() ([]Domain, error) { + var domains []Domain + requestURL := fmt.Sprintf("/v2/organizations/%s/private_domains", o.Guid) + for { + domainsResp, err := o.c.getDomainsResponse(requestURL) + if err != nil { + return []Domain{}, err + } + for _, resource := range domainsResp.Resources { + domains = append(domains, *o.c.mergeDomainResource(resource)) + } + requestURL = domainsResp.NextUrl + if requestURL == "" { + break + } + } + return domains, nil +} + +func (o *Org) SharePrivateDomain(privateDomainGUID string) (*Domain, error) { + requestURL := fmt.Sprintf("/v2/organizations/%s/private_domains/%s", o.Guid, privateDomainGUID) + r := o.c.NewRequest("PUT", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, errors.Wrapf(err, "Error sharing domain %s for org %s, response code: %d", privateDomainGUID, o.Guid, resp.StatusCode) + } + return o.c.handleDomainResp(resp) +} + +func (o *Org) UnsharePrivateDomain(privateDomainGUID string) error { + requestURL := fmt.Sprintf("/v2/organizations/%s/private_domains/%s", o.Guid, privateDomainGUID) + r := o.c.NewRequest("DELETE", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error unsharing domain %s for org %s, response code: %d", privateDomainGUID, o.Guid, resp.StatusCode) + } + return nil +} + +func (o *Org) associateRole(userGUID, role string) (Org, error) { + requestURL := fmt.Sprintf("/v2/organizations/%s/%s/%s", o.Guid, role, userGUID) + r := o.c.NewRequest("PUT", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return Org{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Org{}, errors.Wrapf(err, "Error associating %s %s, response code: %d", role, userGUID, resp.StatusCode) + } + return o.c.handleOrgResp(resp) +} + +func (o *Org) associateRoleByUsernameAndOrigin(name, role, origin string) (Org, error) { + requestURL := fmt.Sprintf("/v2/organizations/%s/%s", o.Guid, role) + buf := bytes.NewBuffer(nil) + payload := make(map[string]string) + payload["username"] = name + if origin != "" { + payload["origin"] = origin + } + err := json.NewEncoder(buf).Encode(payload) + if err != nil { + return Org{}, err + } + r := o.c.NewRequestWithBody("PUT", requestURL, buf) + resp, err := o.c.DoRequest(r) + if err != nil { + return Org{}, err + } + if resp.StatusCode != http.StatusCreated { + return Org{}, errors.Wrapf(err, "Error associating %s %s, response code: %d", role, name, resp.StatusCode) + } + return o.c.handleOrgResp(resp) +} + +func (o *Org) AssociateManager(userGUID string) (Org, error) { + return o.associateRole(userGUID, "managers") +} + +func (o *Org) AssociateManagerByUsername(name string) (Org, error) { + return o.associateRoleByUsernameAndOrigin(name, "managers", "") +} + +func (o *Org) AssociateManagerByUsernameAndOrigin(name, origin string) (Org, error) { + return o.associateRoleByUsernameAndOrigin(name, "managers", origin) +} + +func (o *Org) AssociateUser(userGUID string) (Org, error) { + requestURL := fmt.Sprintf("/v2/organizations/%s/users/%s", o.Guid, userGUID) + r := o.c.NewRequest("PUT", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return Org{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Org{}, errors.Wrapf(err, "Error associating user %s, response code: %d", userGUID, resp.StatusCode) + } + return o.c.handleOrgResp(resp) +} + +func (o *Org) AssociateAuditor(userGUID string) (Org, error) { + return o.associateRole(userGUID, "auditors") +} + +func (o *Org) AssociateAuditorByUsername(name string) (Org, error) { + return o.associateRoleByUsernameAndOrigin(name, "auditors", "") +} + +func (o *Org) AssociateAuditorByUsernameAndOrigin(name, origin string) (Org, error) { + return o.associateRoleByUsernameAndOrigin(name, "auditors", origin) +} + +func (o *Org) AssociateBillingManager(userGUID string) (Org, error) { + return o.associateRole(userGUID, "billing_managers") +} + +func (o *Org) AssociateBillingManagerByUsername(name string) (Org, error) { + return o.associateRoleByUsernameAndOrigin(name, "billing_managers", "") +} +func (o *Org) AssociateBillingManagerByUsernameAndOrigin(name, origin string) (Org, error) { + return o.associateRoleByUsernameAndOrigin(name, "billing_managers", origin) +} + +func (o *Org) AssociateUserByUsername(name string) (Org, error) { + return o.associateUserByUsernameAndOrigin(name, "") +} + +func (o *Org) AssociateUserByUsernameAndOrigin(name, origin string) (Org, error) { + return o.associateUserByUsernameAndOrigin(name, origin) +} + +func (o *Org) associateUserByUsernameAndOrigin(name, origin string) (Org, error) { + requestURL := fmt.Sprintf("/v2/organizations/%s/users", o.Guid) + buf := bytes.NewBuffer(nil) + payload := make(map[string]string) + payload["username"] = name + if origin != "" { + payload["origin"] = origin + } + err := json.NewEncoder(buf).Encode(payload) + if err != nil { + return Org{}, err + } + r := o.c.NewRequestWithBody("PUT", requestURL, buf) + resp, err := o.c.DoRequest(r) + if err != nil { + return Org{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Org{}, errors.Wrapf(err, "Error associating user %s, response code: %d", name, resp.StatusCode) + } + return o.c.handleOrgResp(resp) +} + +func (o *Org) removeRole(userGUID, role string) error { + requestURL := fmt.Sprintf("/v2/organizations/%s/%s/%s", o.Guid, role, userGUID) + r := o.c.NewRequest("DELETE", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error removing %s %s, response code: %d", role, userGUID, resp.StatusCode) + } + return nil +} + +func (o *Org) removeRoleByUsernameAndOrigin(name, role, origin string) error { + var requestURL string + var method string + buf := bytes.NewBuffer(nil) + payload := make(map[string]string) + payload["username"] = name + if origin != "" { + requestURL = fmt.Sprintf("/v2/organizations/%s/%s/remove", o.Guid, role) + method = "POST" + payload["origin"] = origin + } else { + requestURL = fmt.Sprintf("/v2/organizations/%s/%s", o.Guid, role) + method = "DELETE" + } + err := json.NewEncoder(buf).Encode(payload) + if err != nil { + return err + } + + r := o.c.NewRequestWithBody(method, requestURL, buf) + resp, err := o.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error removing manager %s, response code: %d", name, resp.StatusCode) + } + return nil +} + +func (o *Org) RemoveManager(userGUID string) error { + return o.removeRole(userGUID, "managers") +} + +func (o *Org) RemoveManagerByUsername(name string) error { + return o.removeRoleByUsernameAndOrigin(name, "managers", "") +} +func (o *Org) RemoveManagerByUsernameAndOrigin(name, origin string) error { + return o.removeRoleByUsernameAndOrigin(name, "managers", origin) +} + +func (o *Org) RemoveAuditor(userGUID string) error { + return o.removeRole(userGUID, "auditors") +} + +func (o *Org) RemoveAuditorByUsername(name string) error { + return o.removeRoleByUsernameAndOrigin(name, "auditors", "") +} +func (o *Org) RemoveAuditorByUsernameAndOrigin(name, origin string) error { + return o.removeRoleByUsernameAndOrigin(name, "auditors", origin) +} + +func (o *Org) RemoveBillingManager(userGUID string) error { + return o.removeRole(userGUID, "billing_managers") +} + +func (o *Org) RemoveBillingManagerByUsername(name string) error { + return o.removeRoleByUsernameAndOrigin(name, "billing_managers", "") +} + +func (o *Org) RemoveBillingManagerByUsernameAndOrigin(name, origin string) error { + return o.removeRoleByUsernameAndOrigin(name, "billing_managers", origin) +} + +func (o *Org) RemoveUser(userGUID string) error { + requestURL := fmt.Sprintf("/v2/organizations/%s/users/%s", o.Guid, userGUID) + r := o.c.NewRequest("DELETE", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error removing user %s, response code: %d", userGUID, resp.StatusCode) + } + return nil +} + +func (o *Org) RemoveUserByUsername(name string) error { + return o.removeUserByUsernameAndOrigin(name, "") +} + +func (o *Org) RemoveUserByUsernameAndOrigin(name, origin string) error { + return o.removeUserByUsernameAndOrigin(name, origin) +} + +func (o *Org) removeUserByUsernameAndOrigin(name, origin string) error { + var requestURL string + var method string + buf := bytes.NewBuffer(nil) + payload := make(map[string]string) + payload["username"] = name + if origin != "" { + payload["origin"] = origin + requestURL = fmt.Sprintf("/v2/organizations/%s/users/remove", o.Guid) + method = "POST" + } else { + requestURL = fmt.Sprintf("/v2/organizations/%s/users", o.Guid) + method = "DELETE" + } + err := json.NewEncoder(buf).Encode(payload) + if err != nil { + return err + } + r := o.c.NewRequestWithBody(method, requestURL, buf) + resp, err := o.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error removing user %s, response code: %d", name, resp.StatusCode) + } + return nil +} + +func (c *Client) CreateOrg(req OrgRequest) (Org, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return Org{}, err + } + r := c.NewRequestWithBody("POST", "/v2/organizations", buf) + resp, err := c.DoRequest(r) + if err != nil { + return Org{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Org{}, errors.Wrapf(err, "Error creating organization, response code: %d", resp.StatusCode) + } + return c.handleOrgResp(resp) +} + +func (c *Client) UpdateOrg(orgGUID string, orgRequest OrgRequest) (Org, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(orgRequest) + if err != nil { + return Org{}, err + } + r := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/organizations/%s", orgGUID), buf) + resp, err := c.DoRequest(r) + if err != nil { + return Org{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Org{}, errors.Wrapf(err, "Error updating organization, response code: %d", resp.StatusCode) + } + return c.handleOrgResp(resp) +} + +func (c *Client) DeleteOrg(guid string, recursive, async bool) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/organizations/%s?recursive=%t&async=%t", guid, recursive, async))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting organization %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) getOrgResponse(requestURL string) (OrgResponse, error) { + var orgResp OrgResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return OrgResponse{}, errors.Wrap(err, "Error requesting orgs") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return OrgResponse{}, errors.Wrap(err, "Error reading org request") + } + err = json.Unmarshal(resBody, &orgResp) + if err != nil { + return OrgResponse{}, errors.Wrap(err, "Error unmarshalling org") + } + return orgResp, nil +} + +func (c *Client) fetchOrgs(requestURL string) ([]Org, error) { + var orgs []Org + for { + orgResp, err := c.getOrgResponse(requestURL) + if err != nil { + return []Org{}, err + } + for _, org := range orgResp.Resources { + orgs = append(orgs, c.mergeOrgResource(org)) + } + requestURL = orgResp.NextUrl + if requestURL == "" { + break + } + } + return orgs, nil +} + +func (c *Client) handleOrgResp(resp *http.Response) (Org, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return Org{}, err + } + var orgResource OrgResource + err = json.Unmarshal(body, &orgResource) + if err != nil { + return Org{}, err + } + return c.mergeOrgResource(orgResource), nil +} + +func (c *Client) getOrgUserResponse(requestURL string) (OrgUserResponse, error) { + var omResp OrgUserResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return OrgUserResponse{}, errors.Wrap(err, "error requesting org managers") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return OrgUserResponse{}, errors.Wrap(err, "error reading org managers response body") + } + if err := json.Unmarshal(resBody, &omResp); err != nil { + return OrgUserResponse{}, errors.Wrap(err, "error unmarshaling org managers") + } + return omResp, nil +} + +func (c *Client) mergeOrgResource(org OrgResource) Org { + org.Entity.Guid = org.Meta.Guid + org.Entity.CreatedAt = org.Meta.CreatedAt + org.Entity.UpdatedAt = org.Meta.UpdatedAt + org.Entity.c = c + return org.Entity +} + +func (c *Client) DefaultIsolationSegmentForOrg(orgGUID, isolationSegmentGUID string) error { + return c.updateOrgDefaultIsolationSegment(orgGUID, map[string]interface{}{"guid": isolationSegmentGUID}) +} + +func (c *Client) ResetDefaultIsolationSegmentForOrg(orgGUID string) error { + return c.updateOrgDefaultIsolationSegment(orgGUID, nil) +} + +func (c *Client) updateOrgDefaultIsolationSegment(orgGUID string, data interface{}) error { + requestURL := fmt.Sprintf("/v3/organizations/%s/relationships/default_isolation_segment", orgGUID) + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(map[string]interface{}{"data": data}) + if err != nil { + return err + } + r := c.NewRequestWithBody("PATCH", requestURL, buf) + resp, err := c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "Error setting default isolation segment for org %s, response code: %d", orgGUID, resp.StatusCode) + } + return nil +} diff --git a/third_party/go-cfclient/orgs_test.go b/third_party/go-cfclient/orgs_test.go new file mode 100644 index 000000000000..f2cbae34e61f --- /dev/null +++ b/third_party/go-cfclient/orgs_test.go @@ -0,0 +1,1004 @@ +package cfclient + +import ( + "github.com/pkg/errors" + "net/url" + "reflect" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListOrgs(t *testing.T) { + Convey("List Orgs", t, func() { + setup(MockRoute{"GET", "/v2/organizations", []string{listOrgsPayload, listOrgsPayloadPage2}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + orgs, err := client.ListOrgs() + So(err, ShouldBeNil) + + So(len(orgs), ShouldEqual, 4) + So(orgs[0].Guid, ShouldEqual, "a537761f-9d93-4b30-af17-3d73dbca181b") + So(orgs[0].Name, ShouldEqual, "demo") + }) +} + +func TestListOrgsByQuery(t *testing.T) { + Convey("List Orgs", t, func() { + setup(MockRoute{"GET", "/v2/organizations", []string{listOrgsPayload, listOrgsPayloadPage2}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + var query = url.Values{ + "results-per-page": []string{ + "2", + }, + } + orgs, err := client.ListOrgsByQuery(query) + So(err, ShouldBeNil) + + So(len(orgs), ShouldEqual, 4) + So(orgs[0].Guid, ShouldEqual, "a537761f-9d93-4b30-af17-3d73dbca181b") + So(orgs[0].Name, ShouldEqual, "demo") + }) +} + +func TestGetOrgByName(t *testing.T) { + Convey("Get org by name", t, func() { + setup(MockRoute{"GET", "/v2/organizations", []string{getOrgByNamePayload}, "", 200, "q=name:demo77", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org, err := client.GetOrgByName("demo77") + So(err, ShouldBeNil) + + So(org.Guid, ShouldEqual, "4156a4a0-6092-40ab-98bf-114051d1561d") + So(org.Name, ShouldEqual, "demo77") + }) +} + +func TestGetOrgByNameNotFound(t *testing.T) { + Convey("Get org by name not found", t, func() { + setup(MockRoute{"GET", "/v2/organizations", []string{emptyResources}, "", 200, "q=name:does-not-exist", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org, err := client.GetOrgByName("does-not-exist") + So(err, ShouldNotBeNil) + So(IsOrganizationNotFoundError(err), ShouldBeTrue) + + cause := errors.Cause(err) + cfErr, ok := cause.(CloudFoundryError) + if !ok { + t.Fatalf("Expected an error of type CloudFoundryError, but got %s", reflect.TypeOf(err)) + } + So(cfErr.ErrorCode, ShouldEqual, "CF-OrganizationNotFound") + So(cfErr.Code, ShouldEqual, 30003) + So(cfErr.Description, ShouldEqual, "The organization could not be found: does-not-exist") + + So(org.Guid, ShouldEqual, "") + So(org.Name, ShouldEqual, "") + }) +} + +func TestGetOrgByGuid(t *testing.T) { + Convey("Get org by GUID", t, func() { + setup(MockRoute{"GET", "/v2/organizations/1c0e6074-777f-450e-9abc-c42f39d9b75b", []string{orgByGuidPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org, err := client.GetOrgByGuid("1c0e6074-777f-450e-9abc-c42f39d9b75b") + So(err, ShouldBeNil) + + So(org.Guid, ShouldEqual, "1c0e6074-777f-450e-9abc-c42f39d9b75b") + So(org.Name, ShouldEqual, "name-1716") + }) +} + +func TestOrgSpaces(t *testing.T) { + Convey("Get spaces by org", t, func() { + setup(MockRoute{"GET", "/v2/organizations/foo/spaces", []string{orgSpacesPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + spaces, err := client.OrgSpaces("foo") + So(err, ShouldBeNil) + + So(len(spaces), ShouldEqual, 1) + So(spaces[0].Guid, ShouldEqual, "b8aff561-175d-45e8-b1e7-67e2aedb03b6") + So(spaces[0].Name, ShouldEqual, "test") + }) +} + +func TestListOrgUsers(t *testing.T) { + Convey("Get Org Users for an org", t, func() { + setup(MockRoute{"GET", "/v2/organizations/foo/users", []string{listOrgPeoplePayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + users, err := client.ListOrgUsers("foo") + So(err, ShouldBeNil) + So(len(users), ShouldEqual, 2) + So(users[0].Username, ShouldEqual, "user1") + So(users[1].Username, ShouldEqual, "user2") + }) +} + +func TestListOrgManagers(t *testing.T) { + Convey("Get Org Managers for an org", t, func() { + setup(MockRoute{"GET", "/v2/organizations/foo/managers", []string{listOrgPeoplePayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + managers, err := client.ListOrgManagers("foo") + So(err, ShouldBeNil) + So(len(managers), ShouldEqual, 2) + So(managers[0].Username, ShouldEqual, "user1") + So(managers[1].Username, ShouldEqual, "user2") + }) +} + +func TestListOrgAuditors(t *testing.T) { + Convey("Get Org Auditors for an org", t, func() { + setup(MockRoute{"GET", "/v2/organizations/foo/auditors", []string{listOrgPeoplePayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + users, err := client.ListOrgAuditors("foo") + So(err, ShouldBeNil) + So(len(users), ShouldEqual, 2) + So(users[0].Username, ShouldEqual, "user1") + So(users[1].Username, ShouldEqual, "user2") + }) +} +func TestListBillingManagers(t *testing.T) { + Convey("Get Billing Manager for an org", t, func() { + setup(MockRoute{"GET", "/v2/organizations/foo/billing_managers", []string{listOrgPeoplePayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + managers, err := client.ListOrgBillingManagers("foo") + So(err, ShouldBeNil) + So(len(managers), ShouldEqual, 2) + So(managers[0].Username, ShouldEqual, "user1") + So(managers[1].Username, ShouldEqual, "user2") + }) +} + +func TestOrgSummary(t *testing.T) { + Convey("Get org summary", t, func() { + setup(MockRoute{"GET", "/v2/organizations/06dcedd4-1f24-49a6-adc1-cce9131a1b2c/summary", []string{orgSummaryPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "06dcedd4-1f24-49a6-adc1-cce9131a1b2c", + c: client, + } + summary, err := org.Summary() + So(err, ShouldBeNil) + + So(summary.Guid, ShouldEqual, "06dcedd4-1f24-49a6-adc1-cce9131a1b2c") + So(summary.Name, ShouldEqual, "system") + So(summary.Status, ShouldEqual, "active") + + spaces := summary.Spaces + So(len(spaces), ShouldEqual, 1) + So(spaces[0].Guid, ShouldEqual, "494d8b64-8181-4183-a6d3-6279db8fec6e") + So(spaces[0].Name, ShouldEqual, "test") + So(spaces[0].ServiceCount, ShouldEqual, 1) + So(spaces[0].AppCount, ShouldEqual, 2) + So(spaces[0].MemDevTotal, ShouldEqual, 32) + So(spaces[0].MemProdTotal, ShouldEqual, 64) + }) +} + +func TestOrgQuota(t *testing.T) { + Convey("Get org quota", t, func() { + setup(MockRoute{"GET", "/v2/quota_definitions/a537761f-9d93-4b30-af17-3d73dbca181b", []string{orgQuotaPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + QuotaDefinitionGuid: "a537761f-9d93-4b30-af17-3d73dbca181b", + c: client, + } + orgQuota, err := org.Quota() + So(err, ShouldBeNil) + + So(orgQuota.Guid, ShouldEqual, "a537761f-9d93-4b30-af17-3d73dbca181b") + So(orgQuota.Name, ShouldEqual, "test-2") + So(orgQuota.NonBasicServicesAllowed, ShouldEqual, false) + So(orgQuota.TotalServices, ShouldEqual, 10) + So(orgQuota.TotalRoutes, ShouldEqual, 20) + So(orgQuota.TotalPrivateDomains, ShouldEqual, 30) + So(orgQuota.MemoryLimit, ShouldEqual, 40) + So(orgQuota.TrialDBAllowed, ShouldEqual, true) + So(orgQuota.InstanceMemoryLimit, ShouldEqual, 50) + So(orgQuota.AppInstanceLimit, ShouldEqual, 60) + So(orgQuota.AppTaskLimit, ShouldEqual, 70) + So(orgQuota.TotalServiceKeys, ShouldEqual, 80) + So(orgQuota.TotalReservedRoutePorts, ShouldEqual, 90) + }) +} + +func TestCreateOrg(t *testing.T) { + Convey("Create org", t, func() { + setup(MockRoute{"POST", "/v2/organizations", []string{createOrgPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org, err := client.CreateOrg(OrgRequest{Name: "my-org"}) + So(err, ShouldBeNil) + So(org.Guid, ShouldEqual, "22b3b0a0-6511-47e5-8f7a-93bbd2ff446e") + }) +} + +func TestUpdateOrg(t *testing.T) { + Convey("Update org", t, func() { + setup(MockRoute{"PUT", "/v2/organizations/22b3b0a0-6511-47e5-8f7a-93bbd2ff446e", []string{createOrgPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org, err := client.UpdateOrg("22b3b0a0-6511-47e5-8f7a-93bbd2ff446e", OrgRequest{Name: "my-org"}) + So(err, ShouldBeNil) + So(org.Guid, ShouldEqual, "22b3b0a0-6511-47e5-8f7a-93bbd2ff446e") + }) +} + +func TestDeleteOrg(t *testing.T) { + Convey("Delete org", t, func() { + setup(MockRoute{"DELETE", "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b", []string{""}, "", 204, "recursive=false&async=false", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteOrg("a537761f-9d93-4b30-af17-3d73dbca181b", false, false) + So(err, ShouldBeNil) + }) +} + +func TestAssociateManager(t *testing.T) { + Convey("Associate manager", t, func() { + setup(MockRoute{"PUT", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/managers/user-guid", []string{associateOrgUserPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newOrg, err := org.AssociateManager("user-guid") + So(err, ShouldBeNil) + So(newOrg.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateAuditor(t *testing.T) { + Convey("Associate auditor", t, func() { + setup(MockRoute{"PUT", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/auditors/user-guid", []string{associateOrgUserPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newOrg, err := org.AssociateAuditor("user-guid") + So(err, ShouldBeNil) + So(newOrg.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateBillingManager(t *testing.T) { + Convey("Associate billing manager", t, func() { + setup(MockRoute{"PUT", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/billing_managers/user-guid", []string{associateOrgUserPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newOrg, err := org.AssociateBillingManager("user-guid") + So(err, ShouldBeNil) + So(newOrg.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateUser(t *testing.T) { + Convey("Associate user", t, func() { + setup(MockRoute{"PUT", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/users/user-guid", []string{associateOrgUserPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newOrg, err := org.AssociateUser("user-guid") + So(err, ShouldBeNil) + So(newOrg.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateManagerByUsername(t *testing.T) { + Convey("Associate manager by username", t, func() { + expectedBody := `{"username":"user-name"}` + setup(MockRoute{"PUT", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/managers", []string{associateOrgUserPayload}, "", 201, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newOrg, err := org.AssociateManagerByUsername("user-name") + So(err, ShouldBeNil) + So(newOrg.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateManagerByUsernameAndOrigin(t *testing.T) { + Convey("Associate manager by username and origin", t, func() { + expectedBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"PUT", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/managers", []string{associateOrgUserPayload}, "", 201, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newOrg, err := org.AssociateManagerByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + So(newOrg.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateAuditorByUsername(t *testing.T) { + Convey("Associate auditor by username", t, func() { + expectedBody := `{"username":"user-name"}` + setup(MockRoute{"PUT", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/auditors", []string{associateOrgUserPayload}, "", 201, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newOrg, err := org.AssociateAuditorByUsername("user-name") + So(err, ShouldBeNil) + So(newOrg.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateAuditorByUsernameAndOrigin(t *testing.T) { + Convey("Associate auditor by username and origin", t, func() { + expectedBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"PUT", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/auditors", []string{associateOrgUserPayload}, "", 201, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newOrg, err := org.AssociateAuditorByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + So(newOrg.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateBillingManagerByUsername(t *testing.T) { + Convey("Associate billing manager by username", t, func() { + expectedBody := `{"username":"user-name"}` + setup(MockRoute{"PUT", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/billing_managers", []string{associateOrgUserPayload}, "", 201, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newOrg, err := org.AssociateBillingManagerByUsername("user-name") + So(err, ShouldBeNil) + So(newOrg.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateBillingManagerByUsernameAndOrigin(t *testing.T) { + Convey("Associate billing manager by username and origin", t, func() { + expectedBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"PUT", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/billing_managers", []string{associateOrgUserPayload}, "", 201, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newOrg, err := org.AssociateBillingManagerByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + So(newOrg.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateUserByUsername(t *testing.T) { + Convey("Associate user by username", t, func() { + expectedBody := `{"username":"user-name"}` + setup(MockRoute{"PUT", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/users", []string{associateOrgUserPayload}, "", 201, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newOrg, err := org.AssociateUserByUsername("user-name") + So(err, ShouldBeNil) + So(newOrg.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateUserByUsernameAndOrigin(t *testing.T) { + Convey("Associate user by username and origin", t, func() { + expectedBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"PUT", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/users", []string{associateOrgUserPayload}, "", 201, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newOrg, err := org.AssociateUserByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + So(newOrg.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestRemoveManager(t *testing.T) { + Convey("Remove manager", t, func() { + setup(MockRoute{"DELETE", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/managers/user-guid", []string{""}, "", 204, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = org.RemoveManager("user-guid") + So(err, ShouldBeNil) + }) +} + +func TestRemoveAuditor(t *testing.T) { + Convey("Remove auditor", t, func() { + setup(MockRoute{"DELETE", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/auditors/user-guid", []string{""}, "", 204, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = org.RemoveAuditor("user-guid") + So(err, ShouldBeNil) + }) +} + +func TestRemoveBillingManager(t *testing.T) { + Convey("Remove billing manager", t, func() { + setup(MockRoute{"DELETE", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/billing_managers/user-guid", []string{""}, "", 204, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = org.RemoveBillingManager("user-guid") + So(err, ShouldBeNil) + }) +} + +func TestRemoveUser(t *testing.T) { + Convey("Remove user", t, func() { + setup(MockRoute{"DELETE", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/users/user-guid", []string{""}, "", 204, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = org.RemoveUser("user-guid") + So(err, ShouldBeNil) + }) +} + +func TestRemoveManagerByUsername(t *testing.T) { + Convey("Remove manager by username", t, func() { + expectedBody := `{"username":"user-name"}` + setup(MockRoute{"DELETE", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/managers", []string{""}, "", 204, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = org.RemoveManagerByUsername("user-name") + So(err, ShouldBeNil) + }) +} + +func TestRemoveManagerByUsernameAndOrigin(t *testing.T) { + Convey("Remove manager by username and origin", t, func() { + expectedBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"POST", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/managers/remove", []string{""}, "", 204, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = org.RemoveManagerByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + }) +} + +func TestRemoveAuditorByUsername(t *testing.T) { + Convey("Remove auditor by username", t, func() { + expectedBody := `{"username":"user-name"}` + setup(MockRoute{"DELETE", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/auditors", []string{""}, "", 204, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = org.RemoveAuditorByUsername("user-name") + So(err, ShouldBeNil) + }) +} +func TestRemoveAuditorByUsernameAndOrigin(t *testing.T) { + Convey("Remove auditor by username", t, func() { + expectedBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"POST", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/auditors/remove", []string{""}, "", 204, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = org.RemoveAuditorByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + }) +} + +func TestRemoveBillingManagerByUsername(t *testing.T) { + Convey("Remove billing manager by username", t, func() { + expectedBody := `{"username":"user-name"}` + setup(MockRoute{"DELETE", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/billing_managers", []string{""}, "", 204, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = org.RemoveBillingManagerByUsername("user-name") + So(err, ShouldBeNil) + }) +} + +func TestRemoveBillingManagerByUsernameAndOrigin(t *testing.T) { + Convey("Remove billing manager by username and origin", t, func() { + expectedBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"POST", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/billing_managers/remove", []string{""}, "", 204, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = org.RemoveBillingManagerByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + }) +} + +func TestRemoveUserByUsername(t *testing.T) { + Convey("Remove user by username", t, func() { + expectedBody := `{"username":"user-name"}` + setup(MockRoute{"DELETE", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/users", []string{""}, "", 204, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = org.RemoveUserByUsername("user-name") + So(err, ShouldBeNil) + }) +} + +func TestRemoveUserByUsernameAndOrigin(t *testing.T) { + Convey("Remove user by username and origin", t, func() { + expectedBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"POST", "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/users/remove", []string{""}, "", 204, "", &expectedBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + org := &Org{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = org.RemoveUserByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + }) +} + +func TestListOrgSpaceQuotas(t *testing.T) { + Convey("List Org Space Quotas", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/organizations/06dcedd4-1f24-49a6-adc1-cce9131a1b2c/space_quota_definitions", []string{listSpaceQuotasPayloadPage1}, "", 200, "", nil}, + {"GET", "/v2/space_quota_definitions_page_2", []string{listSpaceQuotasPayloadPage2}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + spaceQuotas, err := client.ListOrgSpaceQuotas("06dcedd4-1f24-49a6-adc1-cce9131a1b2c") + So(err, ShouldBeNil) + + So(len(spaceQuotas), ShouldEqual, 2) + So(spaceQuotas[0].Guid, ShouldEqual, "889aa2ed-a883-4cc0-abe5-804b2503f15d") + So(spaceQuotas[0].Name, ShouldEqual, "test-1") + So(spaceQuotas[0].NonBasicServicesAllowed, ShouldEqual, true) + So(spaceQuotas[0].TotalServices, ShouldEqual, -1) + So(spaceQuotas[0].TotalRoutes, ShouldEqual, 100) + So(spaceQuotas[0].MemoryLimit, ShouldEqual, 102400) + So(spaceQuotas[0].InstanceMemoryLimit, ShouldEqual, -1) + So(spaceQuotas[0].AppInstanceLimit, ShouldEqual, -1) + So(spaceQuotas[0].AppTaskLimit, ShouldEqual, -1) + So(spaceQuotas[0].TotalServiceKeys, ShouldEqual, -1) + So(spaceQuotas[0].TotalReservedRoutePorts, ShouldEqual, -1) + }) +} + +func TestListOrgPrivateDomains(t *testing.T) { + Convey("List Org Space Quotas", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/organizations/06dcedd4-1f24-49a6-adc1-cce9131a1b2c/private_domains", []string{listDomainsPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + privateDomains, err := client.ListOrgPrivateDomains("06dcedd4-1f24-49a6-adc1-cce9131a1b2c") + So(err, ShouldBeNil) + + So(len(privateDomains), ShouldEqual, 4) + + }) +} + +func TestShareOrgPrivateDomain(t *testing.T) { + Convey("Share Org Private Domain", t, func() { + mocks := []MockRoute{ + {"PUT", "/v2/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/private_domains/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", []string{sharePrivateDomainPayload}, "", 201, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + domain, err := client.ShareOrgPrivateDomain("3b6f763f-aae1-4177-9b93-f2de6f2a48f2", "3b6f763f-aae1-4177-9b93-f2de6f2a48f2") + So(err, ShouldBeNil) + + So(domain.Guid, ShouldEqual, "3b6f763f-aae1-4177-9b93-f2de6f2a48f2") + + }) +} + +func TestUnshareOrgPrivateDomain(t *testing.T) { + Convey("Unshare Org Private Domain", t, func() { + mocks := []MockRoute{ + {"DELETE", "/v2/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/private_domains/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", []string{sharePrivateDomainPayload}, "", 201, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.UnshareOrgPrivateDomain("3b6f763f-aae1-4177-9b93-f2de6f2a48f2", "3b6f763f-aae1-4177-9b93-f2de6f2a48f2") + So(err, ShouldBeNil) + + }) +} + +func TestDefaultIsolationSegmentForOrg(t *testing.T) { + Convey("set Default IsolationSegment", t, func() { + defaultIsolationSegmentPayload := `{"data":{"guid":"3b6f763f-aae1-4177-9b93-f2de6f2a48f2"}}` + mocks := []MockRoute{ + {"PATCH", "/v3/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/relationships/default_isolation_segment", []string{""}, "", 200, "", &defaultIsolationSegmentPayload}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DefaultIsolationSegmentForOrg("3b6f763f-aae1-4177-9b93-f2de6f2a48f2", "3b6f763f-aae1-4177-9b93-f2de6f2a48f2") + So(err, ShouldBeNil) + + }) +} + +func TestResetDefaultIsolationSegmentForOrg(t *testing.T) { + Convey("Reset Default IsolationSegment", t, func() { + resetIsolationSegmentPayload := `{"data":null}` + mocks := []MockRoute{ + {"PATCH", "/v3/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/relationships/default_isolation_segment", []string{""}, "", 200, "", &resetIsolationSegmentPayload}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.ResetDefaultIsolationSegmentForOrg("3b6f763f-aae1-4177-9b93-f2de6f2a48f2") + So(err, ShouldBeNil) + + }) +} diff --git a/third_party/go-cfclient/payloads_test.go b/third_party/go-cfclient/payloads_test.go new file mode 100644 index 000000000000..e1b8ffcd9169 --- /dev/null +++ b/third_party/go-cfclient/payloads_test.go @@ -0,0 +1,8481 @@ +package cfclient + +const listEventsPage1Payload = `{ + "total_results": 4, + "total_pages": 2, + "prev_url": null, + "next_url": "/v2/events-2?page=2", + "resources": [ + { + "metadata": { + "guid": "b8ede8e1-afc8-40a1-baae-236a0a77b27b", + "url": "/v2/events/b8ede8e1-afc8-40a1-baae-236a0a77b27b", + "created_at": "2016-06-08T16:41:23Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "type": "name-167", + "actor": "guid-008640fc-d316-4602-9251-c8d09bbdc750", + "actor_type": "name-168", + "actor_name": "name-169", + "actee": "guid-e7790fa4-be2b-4a0f-aa82-c124342b0bb4", + "actee_type": "name-170", + "actee_name": "name-171", + "timestamp": "2016-06-08T16:41:23Z", + "metadata": { + "name-188": "value-188", + "name-189": 189, + "name-190": true + }, + "space_guid": "3a1368e7-e3b7-46af-a98d-57b9c71445e7", + "organization_guid": "86aa12ee-8c4f-4b26-b391-2be6c1730dbc" + } + }, + { + "metadata": { + "guid": "2ccf53a8-d0eb-4807-9bb9-7dd844e65267", + "url": "/v2/events/2ccf53a8-d0eb-4807-9bb9-7dd844e65267", + "created_at": "2016-06-08T16:41:23Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "type": "name-175", + "actor": "guid-52cb38f4-d52d-4201-87e3-8e5650efc8c1", + "actor_type": "name-176", + "actor_name": "name-177", + "actee": "guid-5c4214a1-f295-42ae-bd92-38e7cb8be538", + "actee_type": "name-178", + "actee_name": "name-179", + "timestamp": "2016-06-08T16:41:23Z", + "metadata": { + + }, + "space_guid": "10f0fd9d-dd68-428e-8e93-c00bb8eff0a6", + "organization_guid": "aa3fdaaa-42c8-4141-bc22-9792c37aa62f" + } + }, + { + "metadata": { + "guid": "4f9b1fef-2ce4-4877-85e1-0da9114a92cb", + "url": "/v2/events/4f9b1fef-2ce4-4877-85e1-0da9114a92cb", + "created_at": "2016-06-08T16:41:23Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "type": "name-183", + "actor": "guid-d3be7ed1-1c08-43f6-a20d-5cd9287492e1", + "actor_type": "name-184", + "actor_name": "name-185", + "actee": "guid-9844795f-943d-47ed-a997-2c04ee611f5a", + "actee_type": "name-186", + "actee_name": "name-187", + "timestamp": "2016-06-08T16:41:23Z", + "metadata": { + + }, + "space_guid": "f9ef235c-25df-4aa1-bcb4-15eec6f92146", + "organization_guid": "24a920db-f551-46af-bf73-e1db972f652a" + } + } + ] +}` + +const listEventsPage2Payload = `{ + "total_results": 4, + "total_pages": 2, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "4f9b1fef-2ce4-4877-85e1-0da9114a92cb", + "url": "/v2/events/4f9b1fef-2ce4-4877-85e1-0da9114a92cb", + "created_at": "2016-06-08T16:41:23Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "type": "name-183", + "actor": "guid-d3be7ed1-1c08-43f6-a20d-5cd9287492e1", + "actor_type": "name-184", + "actor_name": "name-185", + "actee": "guid-9844795f-943d-47ed-a997-2c04ee611f5a", + "actee_type": "name-186", + "actee_name": "name-187", + "timestamp": "2016-06-08T16:41:23Z", + "metadata": { + + }, + "space_guid": "f9ef235c-25df-4aa1-bcb4-15eec6f92146", + "organization_guid": "24a920db-f551-46af-bf73-e1db972f652a" + } + } + ] +}` + +const totalEventsPayload = `{ + "total_results": 4, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [] +}` + +const listOrgsPayload = `{ +"total_results": 4, +"total_pages": 2, +"prev_url": null, +"next_url": "/v2/organizations?results-per-page=2&page=2", +"resources": [ + { + "metadata": { + "guid": "a537761f-9d93-4b30-af17-3d73dbca181b", + "url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b", + "created_at": "2014-09-24T13:54:53+00:00", + "updated_at": null + }, + "entity": { + "name": "demo", + "billing_enabled": false, + "quota_definition_guid": "183599e0-d535-4559-8675-7b6ddb5cc42d", + "status": "active", + "quota_definition_url": "/v2/quota_definitions/183599e0-d535-4559-8675-7b6ddb5cc42d", + "spaces_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/spaces", + "domains_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/domains", + "private_domains_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/private_domains", + "users_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/users", + "managers_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/managers", + "billing_managers_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/billing_managers", + "auditors_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/auditors", + "app_events_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/app_events", + "space_quota_definitions_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/space_quota_definitions" + } + }, + { + "metadata": { + "guid": "da0dba14-6064-4f7a-b15a-ff9e677e49b2", + "url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2", + "created_at": "2014-09-26T13:36:41+00:00", + "updated_at": null + }, + "entity": { + "name": "test", + "billing_enabled": false, + "quota_definition_guid": "183599e0-d535-4559-8675-7b6ddb5cc42d", + "status": "active", + "quota_definition_url": "/v2/quota_definitions/183599e0-d535-4559-8675-7b6ddb5cc42d", + "spaces_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/spaces", + "domains_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/domains", + "private_domains_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/private_domains", + "users_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/users", + "managers_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/managers", + "billing_managers_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/billing_managers", + "auditors_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/auditors", + "app_events_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/app_events", + "space_quota_definitions_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/space_quota_definitions" + } + } +] +}` + +const listOrgsPayloadPage2 = `{ +"total_results": 4, +"total_pages": 2, +"prev_url": null, +"next_url": null, +"resources": [ + { + "metadata": { + "guid": "a537761f-9d93-4b30-af17-3d73dbca181b", + "url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b", + "created_at": "2014-09-24T13:54:53+00:00", + "updated_at": null + }, + "entity": { + "name": "demo", + "billing_enabled": false, + "quota_definition_guid": "183599e0-d535-4559-8675-7b6ddb5cc42d", + "status": "active", + "quota_definition_url": "/v2/quota_definitions/183599e0-d535-4559-8675-7b6ddb5cc42d", + "spaces_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/spaces", + "domains_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/domains", + "private_domains_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/private_domains", + "users_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/users", + "managers_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/managers", + "billing_managers_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/billing_managers", + "auditors_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/auditors", + "app_events_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/app_events", + "space_quota_definitions_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b/space_quota_definitions" + } + }, + { + "metadata": { + "guid": "da0dba14-6064-4f7a-b15a-ff9e677e49b2", + "url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2", + "created_at": "2014-09-26T13:36:41+00:00", + "updated_at": null + }, + "entity": { + "name": "test", + "billing_enabled": false, + "quota_definition_guid": "183599e0-d535-4559-8675-7b6ddb5cc42d", + "status": "active", + "quota_definition_url": "/v2/quota_definitions/183599e0-d535-4559-8675-7b6ddb5cc42d", + "spaces_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/spaces", + "domains_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/domains", + "private_domains_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/private_domains", + "users_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/users", + "managers_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/managers", + "billing_managers_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/billing_managers", + "auditors_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/auditors", + "app_events_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/app_events", + "space_quota_definitions_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/space_quota_definitions" + } + } +] +}` + +const getOrgByNamePayload = `{ +"total_results": 1, +"total_pages": 1, +"prev_url": null, +"next_url": null, +"resources": [ + { + "metadata": { + "guid": "4156a4a0-6092-40ab-98bf-114051d1561d", + "url": "/v2/organizations/4156a4a0-6092-40ab-98bf-114051d1561d", + "created_at": "2014-09-24T13:54:53+00:00", + "updated_at": null + }, + "entity": { + "name": "demo77", + "billing_enabled": false, + "quota_definition_guid": "183599e0-d535-4559-8675-7b6ddb5cc42d", + "status": "active", + "quota_definition_url": "/v2/quota_definitions/183599e0-d535-4559-8675-7b6ddb5cc42d", + "spaces_url": "/v2/organizations/4156a4a0-6092-40ab-98bf-114051d1561d/spaces", + "domains_url": "/v2/organizations/4156a4a0-6092-40ab-98bf-114051d1561d/domains", + "private_domains_url": "/v2/organizations/4156a4a0-6092-40ab-98bf-114051d1561d/private_domains", + "users_url": "/v2/organizations/4156a4a0-6092-40ab-98bf-114051d1561d/users", + "managers_url": "/v2/organizations/4156a4a0-6092-40ab-98bf-114051d1561d/managers", + "billing_managers_url": "/v2/organizations/4156a4a0-6092-40ab-98bf-114051d1561d/billing_managers", + "auditors_url": "/v2/organizations/4156a4a0-6092-40ab-98bf-114051d1561d/auditors", + "app_events_url": "/v2/organizations/4156a4a0-6092-40ab-98bf-114051d1561d/app_events", + "space_quota_definitions_url": "/v2/organizations/4156a4a0-6092-40ab-98bf-114051d1561d/space_quota_definitions" + } + } +] +}` + +const listOrgPeoplePayload = ` +{ + "total_results": 2, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "uaa-id-231", + "url": "/v2/users/uaa-id-231", + "created_at": "2016-06-08T16:41:34Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "admin": false, + "active": false, + "default_space_guid": null, + "username": "user1", + "spaces_url": "/v2/users/uaa-id-231/spaces", + "organizations_url": "/v2/users/uaa-id-231/organizations", + "managed_organizations_url": "/v2/users/uaa-id-231/managed_organizations", + "billing_managed_organizations_url": "/v2/users/uaa-id-231/billing_managed_organizations", + "audited_organizations_url": "/v2/users/uaa-id-231/audited_organizations", + "managed_spaces_url": "/v2/users/uaa-id-231/managed_spaces", + "audited_spaces_url": "/v2/users/uaa-id-231/audited_spaces" + } + }, + { + "metadata": { + "guid": "uaa-id-232", + "url": "/v2/users/uaa-id-232", + "created_at": "2016-06-08T16:41:34Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "admin": false, + "active": false, + "default_space_guid": null, + "username": "user2", + "spaces_url": "/v2/users/uaa-id-232/spaces", + "organizations_url": "/v2/users/uaa-id-232/organizations", + "managed_organizations_url": "/v2/users/uaa-id-232/managed_organizations", + "billing_managed_organizations_url": "/v2/users/uaa-id-232/billing_managed_organizations", + "audited_organizations_url": "/v2/users/uaa-id-232/audited_organizations", + "managed_spaces_url": "/v2/users/uaa-id-232/managed_spaces", + "audited_spaces_url": "/v2/users/uaa-id-232/audited_spaces" + } + } + ] +} +` + +const orgByGuidPayload = `{ + "metadata": { + "guid": "1c0e6074-777f-450e-9abc-c42f39d9b75b", + "url": "/v2/organizations/1c0e6074-777f-450e-9abc-c42f39d9b75b", + "created_at": "2016-06-08T16:41:33Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-1716", + "billing_enabled": false, + "quota_definition_guid": "769e777f-92b6-4ba0-9e48-5f77e6293670", + "status": "active", + "quota_definition_url": "/v2/quota_definitions/769e777f-92b6-4ba0-9e48-5f77e6293670", + "spaces_url": "/v2/organizations/1c0e6074-777f-450e-9abc-c42f39d9b75b/spaces", + "domains_url": "/v2/organizations/1c0e6074-777f-450e-9abc-c42f39d9b75b/domains", + "private_domains_url": "/v2/organizations/1c0e6074-777f-450e-9abc-c42f39d9b75b/private_domains", + "users_url": "/v2/organizations/1c0e6074-777f-450e-9abc-c42f39d9b75b/users", + "managers_url": "/v2/organizations/1c0e6074-777f-450e-9abc-c42f39d9b75b/managers", + "billing_managers_url": "/v2/organizations/1c0e6074-777f-450e-9abc-c42f39d9b75b/billing_managers", + "auditors_url": "/v2/organizations/1c0e6074-777f-450e-9abc-c42f39d9b75b/auditors", + "app_events_url": "/v2/organizations/1c0e6074-777f-450e-9abc-c42f39d9b75b/app_events", + "space_quota_definitions_url": "/v2/organizations/1c0e6074-777f-450e-9abc-c42f39d9b75b/space_quota_definitions" + } +}` + +const createOrgPayload = `{ + "metadata": { + "guid": "22b3b0a0-6511-47e5-8f7a-93bbd2ff446e", + "url": "/v2/organizations/22b3b0a0-6511-47e5-8f7a-93bbd2ff446e", + "created_at": "2016-06-08T16:41:33Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "my-org-name", + "billing_enabled": false, + "quota_definition_guid": "b7887f5c-34bb-40c5-9778-577572e4fb2d", + "status": "active", + "quota_definition_url": "/v2/quota_definitions/b7887f5c-34bb-40c5-9778-577572e4fb2d", + "spaces_url": "/v2/organizations/22b3b0a0-6511-47e5-8f7a-93bbd2ff446e/spaces", + "domains_url": "/v2/organizations/22b3b0a0-6511-47e5-8f7a-93bbd2ff446e/domains", + "private_domains_url": "/v2/organizations/22b3b0a0-6511-47e5-8f7a-93bbd2ff446e/private_domains", + "users_url": "/v2/organizations/22b3b0a0-6511-47e5-8f7a-93bbd2ff446e/users", + "managers_url": "/v2/organizations/22b3b0a0-6511-47e5-8f7a-93bbd2ff446e/managers", + "billing_managers_url": "/v2/organizations/22b3b0a0-6511-47e5-8f7a-93bbd2ff446e/billing_managers", + "auditors_url": "/v2/organizations/22b3b0a0-6511-47e5-8f7a-93bbd2ff446e/auditors", + "app_events_url": "/v2/organizations/22b3b0a0-6511-47e5-8f7a-93bbd2ff446e/app_events", + "space_quota_definitions_url": "/v2/organizations/22b3b0a0-6511-47e5-8f7a-93bbd2ff446e/space_quota_definitions" + } +}` + +const orgSpacesPayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "b8aff561-175d-45e8-b1e7-67e2aedb03b6", + "url": "/v2/spaces/b8aff561-175d-45e8-b1e7-67e2aedb03b6", + "created_at": "2014-11-12T17:56:22+00:00", + "updated_at": null + }, + "entity": { + "name": "test", + "organization_guid": "0c69f181-2d31-4326-ac33-be2b114a5f99", + "space_quota_definition_guid": null, + "organization_url": "/v2/organizations/0c69f181-2d31-4326-ac33-be2b114a5f99", + "developers_url": "/v2/spaces/b8aff561-175d-45e8-b1e7-67e2aedb03b6/developers", + "managers_url": "/v2/spaces/b8aff561-175d-45e8-b1e7-67e2aedb03b6/managers", + "auditors_url": "/v2/spaces/b8aff561-175d-45e8-b1e7-67e2aedb03b6/auditors", + "apps_url": "/v2/spaces/b8aff561-175d-45e8-b1e7-67e2aedb03b6/apps", + "routes_url": "/v2/spaces/b8aff561-175d-45e8-b1e7-67e2aedb03b6/routes", + "domains_url": "/v2/spaces/b8aff561-175d-45e8-b1e7-67e2aedb03b6/domains", + "service_instances_url": "/v2/spaces/b8aff561-175d-45e8-b1e7-67e2aedb03b6/service_instances", + "app_events_url": "/v2/spaces/b8aff561-175d-45e8-b1e7-67e2aedb03b6/app_events", + "events_url": "/v2/spaces/b8aff561-175d-45e8-b1e7-67e2aedb03b6/events", + "security_groups_url": "/v2/spaces/b8aff561-175d-45e8-b1e7-67e2aedb03b6/security_groups" + } + } + ] +}` + +const orgSummaryPayload = `{ + "guid": "06dcedd4-1f24-49a6-adc1-cce9131a1b2c", + "name": "system", + "status": "active", + "spaces": [ + { + "guid": "494d8b64-8181-4183-a6d3-6279db8fec6e", + "name": "test", + "service_count": 1, + "app_count": 2, + "mem_dev_total": 32, + "mem_prod_total": 64 + } + ] +}` + +const orgQuotaPayload = `{ + "metadata": { + "guid": "a537761f-9d93-4b30-af17-3d73dbca181b", + "url": "/v2/quota_definitions/a537761f-9d93-4b30-af17-3d73dbca181b", + "created_at": "2017-01-18T16:39:10Z", + "updated_at": "2017-01-18T16:46:20Z" + }, + "entity": { + "name": "test-2", + "non_basic_services_allowed": false, + "total_services": 10, + "total_routes": 20, + "total_private_domains": 30, + "memory_limit": 40, + "trial_db_allowed": true, + "instance_memory_limit": 50, + "app_instance_limit": 60, + "app_task_limit": 70, + "total_service_keys": 80, + "total_reserved_route_ports": 90 + } +}` + +const associateOrgUserPayload = `{ + "metadata": { + "guid": "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + "url": "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56", + "created_at": "2016-06-08T16:41:34Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-1735", + "billing_enabled": false, + "quota_definition_guid": "84eed1c7-cc2d-4823-a578-081fef03ba7d", + "status": "active", + "quota_definition_url": "/v2/quota_definitions/84eed1c7-cc2d-4823-a578-081fef03ba7d", + "spaces_url": "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/spaces", + "domains_url": "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/domains", + "private_domains_url": "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/private_domains", + "users_url": "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/users", + "managers_url": "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/managers", + "billing_managers_url": "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/billing_managers", + "auditors_url": "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/auditors", + "app_events_url": "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/app_events", + "space_quota_definitions_url": "/v2/organizations/bc7b4caf-f4b8-4d85-b126-0729b9351e56/space_quota_definitions" + } +}` + +const listOrgQuotasPayloadPage1 = `{ + "total_results": 2, + "total_pages": 2, + "prev_url": null, + "next_url": "/v2/quota_definitions_page_2", + "resources": [ + { + "metadata": { + "guid": "6f9d3100-44ab-49e2-a4f8-9d7d67651ae7", + "url": "/v2/quota_definitions/6f9d3100-44ab-49e2-a4f8-9d7d67651ae7", + "created_at": "2017-01-18T16:39:10Z", + "updated_at": "2017-01-18T16:46:20Z" + }, + "entity": { + "name": "test-1", + "non_basic_services_allowed": true, + "total_services": -1, + "total_routes": 100, + "total_private_domains": -1, + "memory_limit": 102400, + "trial_db_allowed": false, + "instance_memory_limit": -1, + "app_instance_limit": -1, + "app_task_limit": -1, + "total_service_keys": -1, + "total_reserved_route_ports": -1 + } + } + ] +}` + +const listOrgQuotasPayloadPage2 = `{ + "total_results": 2, + "total_pages": 2, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "a537761f-9d93-4b30-af17-3d73dbca181b", + "url": "/v2/quota_definitions/a537761f-9d93-4b30-af17-3d73dbca181b", + "created_at": "2017-01-18T16:39:10Z", + "updated_at": "2017-01-18T16:46:20Z" + }, + "entity": { + "name": "test-2", + "non_basic_services_allowed": false, + "total_services": 10, + "total_routes": 20, + "total_private_domains": 30, + "memory_limit": 40, + "trial_db_allowed": true, + "instance_memory_limit": 50, + "app_instance_limit": 60, + "app_task_limit": 70, + "total_service_keys": 80, + "total_reserved_route_ports": 90 + } + } + ] +}` + +const emptyResources = `{ + "total_results": 0, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [] +}` + +const listSpacesPayload = `{ + "total_results": 8, + "total_pages": 2, + "prev_url": null, + "next_url": "/v2/spacesPage2", + "resources": [ + { + "metadata": { + "guid": "8efd7c5c-d83c-4786-b399-b7bd548839e1", + "url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1", + "created_at": "2014-09-24T13:54:54+00:00", + "updated_at": "2014-09-24T13:54:54+00:00" + }, + "entity": { + "name": "dev", + "organization_guid": "a537761f-9d93-4b30-af17-3d73dbca181b", + "space_quota_definition_guid": null, + "organization_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b", + "developers_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/developers", + "managers_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/managers", + "auditors_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/auditors", + "apps_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/apps", + "routes_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/routes", + "domains_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/domains", + "service_instances_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/service_instances", + "app_events_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/app_events", + "events_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/events", + "security_groups_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/security_groups" + } + }, + { + "metadata": { + "guid": "657b5923-7de0-486a-9928-b4d78ee24931", + "url": "/v2/spaces/657b5923-7de0-486a-9928-b4d78ee24931", + "created_at": "2014-09-26T13:37:31+00:00", + "updated_at": "2014-09-26T13:37:31+00:00" + }, + "entity": { + "name": "demo", + "organization_guid": "da0dba14-6064-4f7a-b15a-ff9e677e49b2", + "space_quota_definition_guid": null, + "organization_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2", + "developers_url": "/v2/spaces/657b5923-7de0-486a-9928-b4d78ee24931/developers", + "managers_url": "/v2/spaces/657b5923-7de0-486a-9928-b4d78ee24931/managers", + "auditors_url": "/v2/spaces/657b5923-7de0-486a-9928-b4d78ee24931/auditors", + "apps_url": "/v2/spaces/657b5923-7de0-486a-9928-b4d78ee24931/apps", + "routes_url": "/v2/spaces/657b5923-7de0-486a-9928-b4d78ee24931/routes", + "domains_url": "/v2/spaces/657b5923-7de0-486a-9928-b4d78ee24931/domains", + "service_instances_url": "/v2/spaces/657b5923-7de0-486a-9928-b4d78ee24931/service_instances", + "app_events_url": "/v2/spaces/657b5923-7de0-486a-9928-b4d78ee24931/app_events", + "events_url": "/v2/spaces/657b5923-7de0-486a-9928-b4d78ee24931/events", + "security_groups_url": "/v2/spaces/657b5923-7de0-486a-9928-b4d78ee24931/security_groups" + } + } + ] +}` + +const listSpacesPayloadPage2 = `{ + "total_results": 8, + "total_pages": 2, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "9ffd7c5c-d83c-4786-b399-b7bd54883977", + "url": "/v2/spaces/9ffd7c5c-d83c-4786-b399-b7bd54883977", + "created_at": "2014-09-24T13:54:54+00:00", + "updated_at": "2014-09-24T13:54:54+00:00" + }, + "entity": { + "name": "test", + "organization_guid": "a537761f-9d93-4b30-af17-3d73dbca181b", + "space_quota_definition_guid": null, + "organization_url": "/v2/organizations/b737761f-9d93-4b30-af17-3d73dbca18aa", + "developers_url": "/v2/spaces/9ffd7c5c-d83c-4786-b399-b7bd54883977/developers", + "managers_url": "/v2/spaces/9ffd7c5c-d83c-4786-b399-b7bd54883977/managers", + "auditors_url": "/v2/spaces/9ffd7c5c-d83c-4786-b399-b7bd54883977/auditors", + "apps_url": "/v2/spaces/9ffd7c5c-d83c-4786-b399-b7bd54883977/apps", + "routes_url": "/v2/spaces/9ffd7c5c-d83c-4786-b399-b7bd54883977/routes", + "domains_url": "/v2/spaces/9ffd7c5c-d83c-4786-b399-b7bd54883977/domains", + "service_instances_url": "/v2/spaces/9ffd7c5c-d83c-4786-b399-b7bd54883977/service_inst2ances", + "app_events_url": "/v2/spaces/9ffd7c5c-d83c-4786-b399-b7bd54883977/app_events", + "events_url": "/v2/spaces/9ffd7c5c-d83c-4786-b399-b7bd54883977/events", + "security_groups_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/security_groups" + } + }, + { + "metadata": { + "guid": "329b5923-7de0-486a-9928-b4d78ee24982", + "url": "/v2/spaces/329b5923-7de0-486a-9928-b4d78ee24982", + "created_at": "2014-09-26T13:37:31+00:00", + "updated_at": "2014-09-26T13:37:31+00:00" + }, + "entity": { + "name": "prod", + "organization_guid": "da0dba14-6064-4f7a-b15a-ff9e677e49b2", + "space_quota_definition_guid": null, + "organization_url": "/v2/organizations/ad0dba14-6064-4f7a-b15a-ff9e677e492b", + "developers_url": "/v2/spaces/329b5923-7de0-486a-9928-b4d78ee24982/developers", + "managers_url": "/v2/spaces/329b5923-7de0-486a-9928-b4d78ee24982/managers", + "auditors_url": "/v2/spaces/329b5923-7de0-486a-9928-b4d78ee24982/auditors", + "apps_url": "/v2/spaces/329b5923-7de0-486a-9928-b4d78ee24982/apps", + "routes_url": "/v2/spaces/329b5923-7de0-486a-9928-b4d78ee24982/routes", + "domains_url": "/v2/spaces/329b5923-7de0-486a-9928-b4d78ee24982/domains", + "service_instances_url": "/v2/spaces/329b5923-7de0-486a-9928-b4d78ee24982/service_instances", + "app_events_url": "/v2/spaces/329b5923-7de0-486a-9928-b4d78ee24982/app_events", + "events_url": "/v2/spaces/329b5923-7de0-486a-9928-b4d78ee24982/events", + "security_groups_url": "/v2/spaces/329b5923-7de0-486a-9928-b4d78ee24982/security_groups" + } + } + ] +}` + +const listSpacePeoplePayload = `{ + "total_results": 2, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "uaa-id-411", + "url": "/v2/users/uaa-id-411", + "created_at": "2016-06-08T16:41:42Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "admin": false, + "active": false, + "default_space_guid": null, + "username": "user1", + "spaces_url": "/v2/users/uaa-id-411/spaces", + "organizations_url": "/v2/users/uaa-id-411/organizations", + "managed_organizations_url": "/v2/users/uaa-id-411/managed_organizations", + "billing_managed_organizations_url": "/v2/users/uaa-id-411/billing_managed_organizations", + "audited_organizations_url": "/v2/users/uaa-id-411/audited_organizations", + "managed_spaces_url": "/v2/users/uaa-id-411/managed_spaces", + "audited_spaces_url": "/v2/users/uaa-id-411/audited_spaces" + } + }, + { + "metadata": { + "guid": "uaa-id-412", + "url": "/v2/users/uaa-id-412", + "created_at": "2016-06-08T16:41:42Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "admin": false, + "active": false, + "default_space_guid": null, + "username": "user2", + "spaces_url": "/v2/users/uaa-id-412/spaces", + "organizations_url": "/v2/users/uaa-id-412/organizations", + "managed_organizations_url": "/v2/users/uaa-id-412/managed_organizations", + "billing_managed_organizations_url": "/v2/users/uaa-id-412/billing_managed_organizations", + "audited_organizations_url": "/v2/users/uaa-id-412/audited_organizations", + "managed_spaces_url": "/v2/users/uaa-id-412/managed_spaces", + "audited_spaces_url": "/v2/users/uaa-id-412/audited_spaces" + } + } + ] +}` + +const listSpaceServiceInstancesPayload = `{ + "total_results": 2, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "9547e9ed-e460-4abe-bda3-7070b9835917", + "url": "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917", + "created_at": "2016-06-08T16:41:41Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-2104", + "credentials": { + "creds-key-60": "creds-val-60" + }, + "service_plan_guid": "fcf57f7f-3c51-49b2-b252-dc24e0f7dcab", + "space_guid": "f858c6b3-f6b1-4ae8-81dd-8e8747657fbe", + "gateway_data": null, + "dashboard_url": null, + "type": "managed_service_instance", + "last_operation": null, + "tags": [ + + ], + "maintenance_info": {}, + "space_url": "/v2/spaces/f858c6b3-f6b1-4ae8-81dd-8e8747657fbe", + "service_plan_url": "/v2/service_plans/fcf57f7f-3c51-49b2-b252-dc24e0f7dcab", + "service_bindings_url": "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917/service_bindings", + "service_keys_url": "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917/service_keys", + "routes_url": "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917/routes", + "shared_from_url": "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917/shared_from", + "shared_to_url": "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917/shared_to", + "service_instance_parameters_url": "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917/parameters" + } + }, + { + "metadata": { + "guid": "07d2f44a-9031-11ec-b909-0242ac120002", + "url": "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002", + "created_at": "2016-07-08T16:41:41Z", + "updated_at": "2016-07-08T16:41:26Z" + }, + "entity": { + "name": "name-2105", + "credentials": { + "creds-key-61": "creds-val-61" + }, + "service_plan_guid": "22b3ae01-280d-41aa-9e97-68d47680003d", + "space_guid": "f858c6b3-f6b1-4ae8-81dd-8e8747657fbe", + "gateway_data": null, + "dashboard_url": null, + "type": "managed_service_instance", + "last_operation": null, + "tags": [ + + ], + "maintenance_info": {}, + "space_url": "/v2/spaces/f858c6b3-f6b1-4ae8-81dd-8e8747657fbe", + "service_plan_url": "/v2/service_plans/22b3ae01-280d-41aa-9e97-68d47680003d", + "service_bindings_url": "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002/service_bindings", + "service_keys_url": "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002/service_keys", + "routes_url": "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002/routes", + "shared_from_url": "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002/shared_from", + "shared_to_url": "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002/shared_to", + "service_instance_parameters_url": "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002/parameters" + } + } + ] +}` + +const spaceByGuidPayload = `{ + "metadata": { + "guid": "8efd7c5c-d83c-4786-b399-b7bd548839e1", + "url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1", + "created_at": "2014-09-24T13:54:54+00:00", + "updated_at": null + }, + "entity": { + "name": "dev", + "organization_guid": "a537761f-9d93-4b30-af17-3d73dbca181b", + "space_quota_definition_guid": null, + "organization_url": "/v2/organizations/a537761f-9d93-4b30-af17-3d73dbca181b", + "developers_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/developers", + "managers_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/managers", + "auditors_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/auditors", + "apps_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/apps", + "routes_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/routes", + "domains_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/domains", + "service_instances_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/service_instances", + "app_events_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/app_events", + "events_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/events", + "security_groups_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/security_groups" + } +}` + +const associateSpaceUserPayload = `{ + "metadata": { + "guid": "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + "url": "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56", + "created_at": "2016-06-08T16:41:34Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-1735", + "organization_guid": "227161e2-667b-483f-9821-77257a38997f", + "space_quota_definition_guid": null, + "allow_ssh": true, + "organization_url": "/v2/organizations/227161e2-667b-483f-9821-77257a38997f", + "developers_url": "/v2/spaces/20e7a265-f50d-4c14-ac7e-42c52f9d9cd7/developers", + "managers_url": "/v2/spaces/20e7a265-f50d-4c14-ac7e-42c52f9d9cd7/managers", + "auditors_url": "/v2/spaces/20e7a265-f50d-4c14-ac7e-42c52f9d9cd7/auditors", + "apps_url": "/v2/spaces/20e7a265-f50d-4c14-ac7e-42c52f9d9cd7/apps", + "routes_url": "/v2/spaces/20e7a265-f50d-4c14-ac7e-42c52f9d9cd7/routes", + "domains_url": "/v2/spaces/20e7a265-f50d-4c14-ac7e-42c52f9d9cd7/domains", + "service_instances_url": "/v2/spaces/20e7a265-f50d-4c14-ac7e-42c52f9d9cd7/service_instances", + "app_events_url": "/v2/spaces/20e7a265-f50d-4c14-ac7e-42c52f9d9cd7/app_events", + "events_url": "/v2/spaces/20e7a265-f50d-4c14-ac7e-42c52f9d9cd7/events", + "security_groups_url": "/v2/spaces/20e7a265-f50d-4c14-ac7e-42c52f9d9cd7/security_groups" + } +}` + +const appSummaryPayload = `{ + "guid": "b5f0d1bd-a3a9-40a4-af1a-312ad26e5379", + "urls": [ + "test-app.local.pcfdev.io" + ], + "routes": [ + { + "guid": "0b44af3e-77e0-4821-abd6-18d8c79309e6", + "host": "test-app", + "port": null, + "path": "", + "domain": { + "guid": "0b183484-45cc-4855-94d4-892f80f20c13", + "name": "local.pcfdev.io" + } + } + ], + "service_count": 1, + "service_names": [ + "test-service" + ], + "running_instances": 1, + "name": "test-app", + "production": false, + "space_guid": "494d8b64-8181-4183-a6d3-6279db8fec6e", + "stack_guid": "67e019a3-322a-407a-96e0-178e95bd0e55", + "buildpack": "ruby_buildpack", + "detected_buildpack": "", + "detected_buildpack_guid": "d5860c89-fb0a-49f4-a8b7-3220ff91c91d", + "environment_json": {}, + "memory": 256, + "instances": 1, + "disk_quota": 512, + "state": "STARTED", + "version": "fa47ec0a-adba-4cc5-b0ee-a8570dc49b3d", + "command": null, + "console": false, + "debug": null, + "staging_task_id": "a21d69a7-0878-4841-ab53-4b515397dc27", + "package_state": "STAGED", + "health_check_type": "port", + "health_check_timeout": null, + "staging_failed_reason": null, + "staging_failed_description": null, + "diego": true, + "docker_image": null, + "package_updated_at": "2017-02-05T12:18:04Z", + "detected_start_command": "rackup -p $PORT", + "enable_ssh": true, + "docker_credentials_json": { + "redacted_message": "[PRIVATE DATA HIDDEN]" + }, + "ports": null +}` + +const spaceSummaryPayload = `{ + "guid": "494d8b64-8181-4183-a6d3-6279db8fec6e", + "name": "test", + "apps": [ + { + "guid": "b5f0d1bd-a3a9-40a4-af1a-312ad26e5379", + "urls": [ + "test-app.local.pcfdev.io" + ], + "routes": [ + { + "guid": "0b44af3e-77e0-4821-abd6-18d8c79309e6", + "host": "test-app", + "port": null, + "path": "", + "domain": { + "guid": "0b183484-45cc-4855-94d4-892f80f20c13", + "name": "local.pcfdev.io" + } + } + ], + "service_count": 1, + "service_names": [ + "test-service" + ], + "running_instances": 1, + "name": "test-app", + "production": false, + "space_guid": "494d8b64-8181-4183-a6d3-6279db8fec6e", + "stack_guid": "67e019a3-322a-407a-96e0-178e95bd0e55", + "buildpack": "ruby_buildpack", + "detected_buildpack": "", + "detected_buildpack_guid": "d5860c89-fb0a-49f4-a8b7-3220ff91c91d", + "environment_json": {}, + "memory": 256, + "instances": 1, + "disk_quota": 512, + "state": "STARTED", + "version": "fa47ec0a-adba-4cc5-b0ee-a8570dc49b3d", + "command": null, + "console": false, + "debug": null, + "staging_task_id": "a21d69a7-0878-4841-ab53-4b515397dc27", + "package_state": "STAGED", + "health_check_type": "port", + "health_check_timeout": null, + "staging_failed_reason": null, + "staging_failed_description": null, + "diego": true, + "docker_image": null, + "package_updated_at": "2017-02-05T12:18:04Z", + "detected_start_command": "rackup -p $PORT", + "enable_ssh": true, + "docker_credentials_json": { + "redacted_message": "[PRIVATE DATA HIDDEN]" + }, + "ports": null + } + ], + "services": [ + { + "guid": "3c5c758c-6b76-46f6-89d5-677909bfc975", + "name": "test-service", + "bound_app_count": 1, + "last_operation": { + "type": "create", + "state": "succeeded", + "description": "", + "updated_at": "2017-02-05T11:56:14Z", + "created_at": "2017-02-05T11:56:14Z" + }, + "dashboard_url": null, + "service_broker_name": "broker-name", + "maintenance_info": { + "version": "1.0.0", + "description": "OS image update.\nExpect downtime." + }, + "service_plan": { + "guid": "25e717d2-59a1-4cd2-a792-04508f816776", + "name": "test-plan", + "maintenance_info": { + "version": "2.0.0", + "description": "Stemcell update.\nExpect downtime." + }, + "service": { + "guid": "84c238f4-3961-4b10-8406-9003374c1f2b", + "label": "test-service", + "provider": null, + "version": null + } + } + } + ] +}` + +const spaceRolesPayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "uaa-id-363", + "url": "/v2/users/uaa-id-363", + "created_at": "2016-06-08T16:41:40Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "admin": false, + "active": false, + "default_space_guid": null, + "username": "everything@example.com", + "space_roles": [ + "space_developer", + "space_manager", + "space_auditor" + ], + "spaces_url": "/v2/users/uaa-id-363/spaces", + "organizations_url": "/v2/users/uaa-id-363/organizations", + "managed_organizations_url": "/v2/users/uaa-id-363/managed_organizations", + "billing_managed_organizations_url": "/v2/users/uaa-id-363/billing_managed_organizations", + "audited_organizations_url": "/v2/users/uaa-id-363/audited_organizations", + "managed_spaces_url": "/v2/users/uaa-id-363/managed_spaces", + "audited_spaces_url": "/v2/users/uaa-id-363/audited_spaces" + } + } + ] +}` + +const listV3SpaceRolesBySpaceGUIDPayload = `{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&space_guids=spaceGUID1" + }, + "last": { + "href": "https://api.example.org/v3/roles?page=2&per_page=2&space_guids=spaceGUID1" + }, + "next": { + "href": "https://api.example.org/v3/rolespage2?page=2&per_page=2&space_guids=spaceGUID1" + }, + "previous": null + }, + "resources": [ + { + "guid": "roleGUID1", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "space_developer", + "relationships": { + "user": { + "data": { + "guid": "userGUID1" + } + }, + "space": { + "data": { + "guid": "spaceGUID1" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID1" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID1" + }, + "space": { + "href": "https://api.example.org/v3/spaces/spaceGUID1" + } + } + }, + { + "guid": "roleGUID2", + "created_at": "2047-11-10T17:19:12Z", + "updated_at": "2047-11-10T17:19:12Z", + "type": "space_auditor", + "relationships": { + "user": { + "data": { + "guid": "userGUID2" + } + }, + "space": { + "data": { + "guid": "spaceGUID1" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID2" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID2" + }, + "space": { + "href": "https://api.example.org/v3/spaces/spaceGUID1" + } + } + } + ] +}` + +const listV3SpaceRolesBySpaceGuidPayloadPage2 = `{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&space_guids=spaceGUID1" + }, + "last": { + "href": "https://api.example.org/v3/rolespage2?page=2&per_page=2&space_guids=spaceGUID1" + }, + "next": null, + "previous": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&space_guids=spaceGUID1" + } + }, + "resources": [ + { + "guid": "roleGUID3", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "space_manager", + "relationships": { + "user": { + "data": { + "guid": "userGUID2" + } + }, + "space": { + "data": { + "guid": "spaceGUID1" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID3" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID2" + }, + "space": { + "href": "https://api.example.org/v3/spaces/spaceGUID1" + } + } + } + ] +}` + +const listV3SpaceRoleUsersBySpaceGUIDPayload = `{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&include=user&space_guids=spaceGUID1" + }, + "last": { + "href": "https://api.example.org/v3/roles?page=2&per_page=2&include=user&space_guids=spaceGUID1" + }, + "next": { + "href": "https://api.example.org/v3/rolespage2?page=2&per_page=2&include=user&space_guids=spaceGUID1" + }, + "previous": null + }, + "resources": [ + { + "guid": "roleGUID1", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "space_supporter", + "relationships": { + "user": { + "data": { + "guid": "userGUID1" + } + }, + "space": { + "data": { + "guid": "spaceGUID1" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID1" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID1" + }, + "space": { + "href": "https://api.example.org/v3/spaces/spaceGUID1" + } + } + }, + { + "guid": "roleGUID2", + "created_at": "2047-11-10T17:19:12Z", + "updated_at": "2047-11-10T17:19:12Z", + "type": "space_supporter", + "relationships": { + "user": { + "data": { + "guid": "userGUID2" + } + }, + "space": { + "data": { + "guid": "spaceGUID1" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID2" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID2" + }, + "space": { + "href": "https://api.example.org/v3/spaces/spaceGUID1" + } + } + } + ], + "included": { + "users": [ + { + "guid": "userGUID1", + "created_at": "2022-05-25T23:57:45Z", + "updated_at": "2022-05-25T23:57:45Z", + "username": "user1", + "presentation_name": "user1", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/userGUID1" + } + } + }, + { + "guid": "userGUID2", + "created_at": "2022-05-25T23:57:45Z", + "updated_at": "2022-05-25T23:57:45Z", + "username": "user2", + "presentation_name": "user2", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/userGUID2" + } + } + } + ] + } +}` + +const listV3SpaceRoleUsersBySpaceGUIDPayloadPage2 = `{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&include=user&space_guids=spaceGUID1" + }, + "last": { + "href": "https://api.example.org/v3/rolespage2?page=2&per_page=2&include=user&space_guids=spaceGUID1" + }, + "next": null, + "previous": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&include=user&space_guids=spaceGUID1" + } + }, + "resources": [ + { + "guid": "roleGUID3", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "space_supporter", + "relationships": { + "user": { + "data": { + "guid": "userGUID2" + } + }, + "space": { + "data": { + "guid": "spaceGUID1" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID3" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID2" + }, + "space": { + "href": "https://api.example.org/v3/spaces/spaceGUID1" + } + } + } + ], + "included": { + "users": [ + { + "guid": "userGUID3", + "created_at": "2022-05-25T23:57:45Z", + "updated_at": "2022-05-25T23:57:45Z", + "username": "user3", + "presentation_name": "user3", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/userGUID3" + } + } + } + ] + } +}` + +const listV3SpaceRolesBySpaceGUIDAndTypePayload = `{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&include=user&space_guids=spaceGUID1&types=space_supporter" + }, + "last": { + "href": "https://api.example.org/v3/roles?page=2&per_page=2&include=user&space_guids=spaceGUID1&types=space_supporter" + }, + "next": { + "href": "https://api.example.org/v3/rolespage2?page=2&per_page=2&include=user&space_guids=spaceGUID1&types=space_supporter" + }, + "previous": null + }, + "resources": [ + { + "guid": "roleGUID1", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "space_supporter", + "relationships": { + "user": { + "data": { + "guid": "userGUID1" + } + }, + "space": { + "data": { + "guid": "spaceGUID1" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID1" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID1" + }, + "space": { + "href": "https://api.example.org/v3/spaces/spaceGUID1" + } + } + }, + { + "guid": "roleGUID2", + "created_at": "2047-11-10T17:19:12Z", + "updated_at": "2047-11-10T17:19:12Z", + "type": "space_supporter", + "relationships": { + "user": { + "data": { + "guid": "userGUID2" + } + }, + "space": { + "data": { + "guid": "spaceGUID1" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID2" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID2" + }, + "space": { + "href": "https://api.example.org/v3/spaces/spaceGUID1" + } + } + } + ], + "included": { + "users": [ + { + "guid": "userGUID1", + "created_at": "2022-05-25T23:57:45Z", + "updated_at": "2022-05-25T23:57:45Z", + "username": "user1", + "presentation_name": "user1", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/userGUID1" + } + } + }, + { + "guid": "userGUID2", + "created_at": "2022-05-25T23:57:45Z", + "updated_at": "2022-05-25T23:57:45Z", + "username": "user2", + "presentation_name": "user2", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/userGUID2" + } + } + } + ] + } +}` + +const listV3SpaceRolesBySpaceGuidAndTypePayloadPage2 = `{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&include=user&space_guids=spaceGUID1&types=space_supporter" + }, + "last": { + "href": "https://api.example.org/v3/rolespage2?page=2&per_page=2&include=user&space_guids=spaceGUID1&types=space_supporter" + }, + "next": null, + "previous": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&include=user&space_guids=spaceGUID1&types=space_supporter" + } + }, + "resources": [ + { + "guid": "roleGUID3", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "space_supporter", + "relationships": { + "user": { + "data": { + "guid": "userGUID2" + } + }, + "space": { + "data": { + "guid": "spaceGUID1" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID3" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID2" + }, + "space": { + "href": "https://api.example.org/v3/spaces/spaceGUID1" + } + } + } + ], + "included": { + "users": [ + { + "guid": "userGUID3", + "created_at": "2022-05-25T23:57:45Z", + "updated_at": "2022-05-25T23:57:45Z", + "username": "user3", + "presentation_name": "user3", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/userGUID3" + } + } + } + ] + } +}` + +const listV3OrganizationRolesByOrganizationGUIDPayload = `{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&include=user&organization_guids=orgGUID1" + }, + "last": { + "href": "https://api.example.org/v3/roles?page=2&per_page=2&include=user&organization_guids=orgGUID1" + }, + "next": { + "href": "https://api.example.org/v3/rolespage2?page=2&per_page=2&include=user&organization_guids=orgGUID1" + }, + "previous": null + }, + "resources": [ + { + "guid": "roleGUID1", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "organization_auditor", + "relationships": { + "user": { + "data": { + "guid": "userGUID1" + } + }, + "space": { + "data": null + }, + "organization": { + "data": { + "guid": "orgGUID1" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID1" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID1" + }, + "org": { + "href": "https://api.example.org/v3/organization/orgGUID1" + } + } + }, + { + "guid": "roleGUID2", + "created_at": "2047-11-10T17:19:12Z", + "updated_at": "2047-11-10T17:19:12Z", + "type": "organization_auditor", + "relationships": { + "user": { + "data": { + "guid": "userGUID2" + } + }, + "space": { + "data": null + }, + "organization": { + "data": { + "guid": "orgGUID1" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID2" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID2" + }, + "organization": { + "href": "https://api.example.org/v3/organization/orgGUID1" + } + } + } + ], + "included": { + "users": [ + { + "guid": "userGUID1", + "created_at": "2022-05-25T23:57:45Z", + "updated_at": "2022-05-25T23:57:45Z", + "username": "user1", + "presentation_name": "user1", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/userGUID1" + } + } + }, + { + "guid": "userGUID2", + "created_at": "2022-05-25T23:57:45Z", + "updated_at": "2022-05-25T23:57:45Z", + "username": "user2", + "presentation_name": "user2", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/userGUID2" + } + } + } + ] + } +}` + +const listV3OrganizationRolesByOrganizationGuidPayloadPage2 = `{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&include=user&organiziation_guids=orgGUID1" + }, + "last": { + "href": "https://api.example.org/v3/rolespage2?page=2&per_page=2&include=user&organiziation_guids=orgGUID1" + }, + "next": null, + "previous": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&include=user&organiziation_guids=orgGUID1" + } + }, + "resources": [ + { + "guid": "roleGUID3", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "organization_auditor", + "relationships": { + "user": { + "data": { + "guid": "userGUID2" + } + }, + "space": { + "data": null + }, + "organization": { + "data": { + "guid": "spaceGUID1" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID3" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID2" + }, + "organization": { + "href": "https://api.example.org/v3/organization/orgGUID1" + } + } + } + ], + "included": { + "users": [ + { + "guid": "userGUID3", + "created_at": "2022-05-25T23:57:45Z", + "updated_at": "2022-05-25T23:57:45Z", + "username": "user3", + "presentation_name": "user3", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/userGUID3" + } + } + } + ] + } +}` + +const listV3OrganizationRolesByOrganizationGUIDAndTypePayload = `{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&include=user&organization_guids=orgGUID1&types=organization_auditor" + }, + "last": { + "href": "https://api.example.org/v3/roles?page=2&per_page=2&include=user&organization_guids=orgGUID1&types=organization_auditor" + }, + "next": { + "href": "https://api.example.org/v3/rolespage2?page=2&per_page=2&include=user&organization_guids=orgGUID1&types=organization_auditor" + }, + "previous": null + }, + "resources": [ + { + "guid": "roleGUID1", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "organization_auditor", + "relationships": { + "user": { + "data": { + "guid": "userGUID1" + } + }, + "space": { + "data": null + }, + "organization": { + "data": { + "guid": "orgGUID1" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID1" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID1" + }, + "org": { + "href": "https://api.example.org/v3/organization/orgGUID1" + } + } + }, + { + "guid": "roleGUID2", + "created_at": "2047-11-10T17:19:12Z", + "updated_at": "2047-11-10T17:19:12Z", + "type": "organization_auditor", + "relationships": { + "user": { + "data": { + "guid": "userGUID2" + } + }, + "space": { + "data": null + }, + "organization": { + "data": { + "guid": "orgGUID1" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID2" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID2" + }, + "organization": { + "href": "https://api.example.org/v3/organization/orgGUID1" + } + } + } + ], + "included": { + "users": [ + { + "guid": "userGUID1", + "created_at": "2022-05-25T23:57:45Z", + "updated_at": "2022-05-25T23:57:45Z", + "username": "user1", + "presentation_name": "user1", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/userGUID1" + } + } + }, + { + "guid": "userGUID2", + "created_at": "2022-05-25T23:57:45Z", + "updated_at": "2022-05-25T23:57:45Z", + "username": "user2", + "presentation_name": "user2", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/userGUID2" + } + } + } + ] + } +}` + +const listV3OrganizationRolesByOrganizationGuidAndTypePayloadPage2 = `{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&include=user&organiziation_guids=orgGUID1&types=organization_auditor" + }, + "last": { + "href": "https://api.example.org/v3/rolespage2?page=2&per_page=2&include=user&organiziation_guids=orgGUID1&types=organization_auditor" + }, + "next": null, + "previous": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&include=user&organiziation_guids=orgGUID1&types=organization_auditor" + } + }, + "resources": [ + { + "guid": "roleGUID3", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "organization_auditor", + "relationships": { + "user": { + "data": { + "guid": "userGUID2" + } + }, + "space": { + "data": null + }, + "organization": { + "data": { + "guid": "spaceGUID1" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID3" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID2" + }, + "organization": { + "href": "https://api.example.org/v3/organization/orgGUID1" + } + } + } + ], + "included": { + "users": [ + { + "guid": "userGUID3", + "created_at": "2022-05-25T23:57:45Z", + "updated_at": "2022-05-25T23:57:45Z", + "username": "user3", + "presentation_name": "user3", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/userGUID3" + } + } + } + ] + } +}` + +const listV3SpaceRolesByUserGuidPayload = `{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2&user_guids=userGUID1" + }, + "last": { + "href": "https://api.example.org/v3/roles?page=2&per_page=2&user_guids=userGUID1" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "roleGUID1", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "space_developer", + "relationships": { + "user": { + "data": { + "guid": "userGUID1" + } + }, + "space": { + "data": { + "guid": "spaceGUID1" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID1" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID1" + }, + "space": { + "href": "https://api.example.org/v3/spaces/spaceGUID1" + } + } + }, + { + "guid": "roleGUID4", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "space_manager", + "relationships": { + "user": { + "data": { + "guid": "userGUID1" + } + }, + "space": { + "data": { + "guid": "spaceGUID2" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID4" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID1" + }, + "space": { + "href": "https://api.example.org/v3/spaces/spaceGUID2" + } + } + } + ] +}` + +const listV3spaceRolesBySpaceAndUserGuidPayload = `{ + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=1&space_guids=spaceGUID2&user_guids=userGUID1" + }, + "last": { + "href": "https://api.example.org/v3/roles?page=1&per_page=1&space_guids=spaceGUID2&user_guids=userGUID1" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "roleGUID4", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "space_manager", + "relationships": { + "user": { + "data": { + "guid": "userGUID1" + } + }, + "space": { + "data": { + "guid": "spaceGUID2" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/roleGUID4" + }, + "user": { + "href": "https://api.example.org/v3/users/userGUID1" + }, + "space": { + "href": "https://api.example.org/v3/spaces/spaceGUID2" + } + } + } + ] +}` + +const spaceQuotaPayload = `{ + "metadata": { + "guid": "9ffd7c5c-d83c-4786-b399-b7bd54883977", + "url": "/v2/space_quota_definitions/9ffd7c5c-d83c-4786-b399-b7bd54883977", + "created_at": "2017-02-04T18:11:49Z", + "updated_at": "2017-02-04T18:11:49Z" + }, + "entity": { + "name": "test-2", + "organization_guid": "06dcedd4-1f24-49a6-adc1-cce9131a1b2c", + "non_basic_services_allowed": false, + "total_services": 10, + "total_routes": 20, + "memory_limit": 30, + "instance_memory_limit": 40, + "app_instance_limit": 50, + "app_task_limit": 60, + "total_service_keys": 70, + "total_reserved_route_ports": 80, + "organization_url": "/v2/organizations/06dcedd4-1f24-49a6-adc1-cce9131a1b2c", + "spaces_url": "/v2/space_quota_definitions/9ffd7c5c-d83c-4786-b399-b7bd54883977/spaces" + } +}` + +const listSpaceQuotasPayloadPage1 = `{ + "total_results": 2, + "total_pages": 2, + "prev_url": null, + "next_url": "/v2/space_quota_definitions_page_2", + "resources": [ + { + "metadata": { + "guid": "889aa2ed-a883-4cc0-abe5-804b2503f15d", + "url": "/v2/space_quota_definitions/889aa2ed-a883-4cc0-abe5-804b2503f15d", + "created_at": "2017-02-04T18:11:49Z", + "updated_at": "2017-02-04T18:11:49Z" + }, + "entity": { + "name": "test-1", + "organization_guid": "06dcedd4-1f24-49a6-adc1-cce9131a1b2c", + "non_basic_services_allowed": true, + "total_services": -1, + "total_routes": 100, + "memory_limit": 102400, + "instance_memory_limit": -1, + "app_instance_limit": -1, + "app_task_limit": -1, + "total_service_keys": -1, + "total_reserved_route_ports": -1, + "organization_url": "/v2/organizations/06dcedd4-1f24-49a6-adc1-cce9131a1b2c", + "spaces_url": "/v2/space_quota_definitions/889aa2ed-a883-4cc0-abe5-804b2503f15d/spaces" + } + } + ] +}` + +const listSpaceQuotasPayloadPage2 = `{ + "total_results": 2, + "total_pages": 2, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "9ffd7c5c-d83c-4786-b399-b7bd54883977", + "url": "/v2/space_quota_definitions/9ffd7c5c-d83c-4786-b399-b7bd54883977", + "created_at": "2017-02-04T18:11:49Z", + "updated_at": "2017-02-04T18:11:49Z" + }, + "entity": { + "name": "test-2", + "organization_guid": "06dcedd4-1f24-49a6-adc1-cce9131a1b2c", + "non_basic_services_allowed": false, + "total_services": 10, + "total_routes": 20, + "memory_limit": 30, + "instance_memory_limit": 40, + "app_instance_limit": 50, + "app_task_limit": 60, + "total_service_keys": 70, + "total_reserved_route_ports": 80, + "organization_url": "/v2/organizations/06dcedd4-1f24-49a6-adc1-cce9131a1b2c", + "spaces_url": "/v2/space_quota_definitions/9ffd7c5c-d83c-4786-b399-b7bd54883977/spaces" + } + } + ] +}` + +const listSecGroupsPayload = `{ + "total_results": 28, + "total_pages": 1, + "prev_url": null, + "next_url": "/v2/security_groupsPage2", + "resources": [ + { + "metadata": { + "guid": "af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c", + "url": "/v2/security_groups/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c", + "created_at": "2015-12-04T11:15:55Z", + "updated_at": null + }, + "entity": { + "name": "secgroup-test", + "rules": [ + { + "destination": "1.1.1.1", + "ports": "443,4443", + "protocol": "tcp" + }, + { + "destination": "1.2.3.4", + "ports": "1111", + "protocol": "udp" + } + ], + "running_default": true, + "staging_default": true, + "spaces_url": "/v2/security_groups/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c/spaces", + "spaces": [] + } + } + ] +}` + +const listSecGroupsPayloadPage2 = `{ + "total_results": 28, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "f9ad202b-76dd-44ec-b7c2-fd2417a561e8", + "url": "/v2/security_groups/f9ad202b-76dd-44ec-b7c2-fd2417a561e8", + "created_at": "2015-12-04T11:15:55Z", + "updated_at": null + }, + "entity": { + "name": "secgroup-test2", + "rules": [ + { + "destination": "2.2.2.2", + "ports": "2222", + "protocol": "udp" + }, + { + "destination": "4.3.2.1", + "ports": "443,4443", + "protocol": "tcp" + } + ], + "running_default": false, + "staging_default": false, + "spaces_url": "/v2/security_groups/f9ad202b-76dd-44ec-b7c2-fd2417a561e8/spaces", + "spaces": [ + { + "metadata": { + "guid": "e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4", + "url": "/v2/spaces/e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4", + "created_at": "2014-10-27T10:49:37Z", + "updated_at": "2015-01-21T15:30:52Z" + }, + "entity": { + "name": "space-test", + "organization_guid": "82338ba1-bc08-4576-aad1-9a5b4693b386", + "space_quota_definition_guid": null, + "allow_ssh": true, + "organization_url": "/v2/organizations/82338ba1-bc08-4576-aad1-9a5b4693b386", + "developers_url": "/v2/spaces/e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4/developers", + "managers_url": "/v2/spaces/e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4/managers", + "auditors_url": "/v2/spaces/e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4/auditors", + "apps_url": "/v2/spaces/e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4/apps", + "routes_url": "/v2/spaces/e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4/routes", + "domains_url": "/v2/spaces/e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4/domains", + "service_instances_url": "/v2/spaces/e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4/service_instances", + "app_events_url": "/v2/spaces/e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4/app_events", + "events_url": "/v2/spaces/e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4/events", + "security_groups_url": "/v2/spaces/e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4/security_groups" + } + }, + { + "metadata": { + "guid": "a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333", + "url": "/v2/spaces/a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333", + "created_at": "2014-10-27T10:49:37Z", + "updated_at": "2015-01-21T15:30:52Z" + }, + "entity": { + "name": "space-test2", + "organization_guid": "82338ba1-bc08-4576-aad1-9a5b4693b386", + "space_quota_definition_guid": null, + "allow_ssh": true, + "organization_url": "/v2/organizations/82338ba1-bc08-4576-aad1-9a5b4693b386", + "developers_url": "/v2/spaces/a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333/developers", + "managers_url": "/v2/spaces/a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333/managers", + "auditors_url": "/v2/spaces/a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333/auditors", + "apps_url": "/v2/spaces/a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333/apps", + "routes_url": "/v2/spaces/a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333/routes", + "domains_url": "/v2/spaces/a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333/domains", + "service_instances_url": "/v2/spaces/a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333/service_instances", + "app_events_url": "/v2/spaces/a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333/app_events", + "events_url": "/v2/spaces/a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333/events", + "security_groups_url": "/v2/spaces/a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333/security_groups" + } + }, + { + "metadata": { + "guid": "c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1", + "url": "/v2/spaces/c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1", + "created_at": "2014-10-27T10:49:37Z", + "updated_at": "2015-01-21T15:30:52Z" + }, + "entity": { + "name": "space-test3", + "organization_guid": "82338ba1-bc08-4576-aad1-9a5b4693b386", + "space_quota_definition_guid": null, + "allow_ssh": true, + "organization_url": "/v2/organizations/82338ba1-bc08-4576-aad1-9a5b4693b386", + "developers_url": "/v2/spaces/c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1/developers", + "managers_url": "/v2/spaces/c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1/managers", + "auditors_url": "/v2/spaces/c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1/auditors", + "apps_url": "/v2/spaces/c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1/apps", + "routes_url": "/v2/spaces/c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1/routes", + "domains_url": "/v2/spaces/c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1/domains", + "service_instances_url": "/v2/spaces/c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1/service_instances", + "app_events_url": "/v2/spaces/c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1/app_events", + "events_url": "/v2/spaces/c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1/events", + "security_groups_url": "/v2/spaces/c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1/security_groups" + } + } + ] + } + } + ] +}` + +const listRunningSecGroupsPayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "3014fb5b-cb2c-4ac5-952c-e3a7e04ab028", + "url": "/v2/config/running_security_groups/3014fb5b-cb2c-4ac5-952c-e3a7e04ab028", + "created_at": "2016-06-08T16:41:21Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-6", + "rules": [ + { + "protocol": "udp", + "ports": "8080", + "destination": "198.41.191.47/1" + } + ], + "running_default": true, + "staging_default": false + } + } + ] +}` + +const listStagingSecGroupsPayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "611bd883-4b93-403f-af92-3283de22e3f0", + "url": "/v2/config/staging_security_groups/611bd883-4b93-403f-af92-3283de22e3f0", + "created_at": "2016-06-08T16:41:27Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-1372", + "rules": [ + { + "protocol": "udp", + "ports": "8080", + "destination": "198.41.191.47/1" + } + ], + "running_default": false, + "staging_default": true + } + } + ] +}` + +const listV3SecurityGroupsPayload = `{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/security_groups?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/security_groups?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "guid-1", + "name": "my-group1", + "globally_enabled": { + "running": true, + "staging": false + }, + "rules": [ + { + "protocol": "tcp", + "destination": "1.2.3.4/10", + "ports": "443,80,8080" + }, + { + "protocol": "icmp", + "destination": "1.2.3.4/12", + "type": 8, + "code": 0, + "description": "test-desc-1" + } + ], + "relationships": { + "staging_spaces": { + "data": [ + { "guid": "space-guid-1" }, + { "guid": "space-guid-2" } + ] + }, + "running_spaces": { + "data": [] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/security_groups/guid-1" + } + } + }, + { + "guid": "guid-2", + "name": "my-group2", + "globally_enabled": { + "running": false, + "staging": true + }, + "rules": [ + { + "protocol": "tcp", + "destination": "1.2.3.4/14", + "ports": "443,80,8080" + }, + { + "protocol": "icmp", + "destination": "1.2.3.4/16", + "type": 5, + "code": 0, + "description": "test-desc-2" + } + ], + "relationships": { + "staging_spaces": { + "data": [ + { "guid": "space-guid-3" }, + { "guid": "space-guid-4" } + ] + }, + "running_spaces": { + "data": [ + { "guid": "space-guid-5" }, + { "guid": "space-guid-6" } + ] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/security_groups/guid-2" + } + } + } + ] +}` + +const listV3SecurityGroupsByGuidPayload = `{ + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/security_groups?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/security_groups?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "guid-1", + "name": "my-group1", + "globally_enabled": { + "running": true, + "staging": false + }, + "rules": [ + { + "protocol": "tcp", + "destination": "1.2.3.4/10", + "ports": "443,80,8080" + }, + { + "protocol": "icmp", + "destination": "1.2.3.4/12", + "type": 8, + "code": 0, + "description": "test-desc-1" + } + ], + "relationships": { + "staging_spaces": { + "data": [ + { "guid": "space-guid-1" }, + { "guid": "space-guid-2" } + ] + }, + "running_spaces": { + "data": [] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/security_groups/guid-1" + } + } + } + ] +}` + +const genericV3SecurityGroupPayload = `{ + "guid": "guid-1", + "name": "my-sec-group", + "globally_enabled": { + "running": true, + "staging": false + }, + "rules": [ + { + "protocol": "tcp", + "destination": "10.10.10.0/24", + "ports": "443,80,8080" + }, + { + "protocol": "icmp", + "destination": "10.10.11.0/24", + "type": 8, + "code": 0, + "description": "Allow ping requests to private services" + } + ], + "relationships": { + "staging_spaces": { + "data": [] + }, + "running_spaces": { + "data": [ + { "guid": "space-guid-1" }, + { "guid": "space-guid-2" } + ] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/security_groups/guid-1" + } + } +}` + +const listAppsPayload = `{ + "total_results": 28, + "total_pages": 1, + "prev_url": null, + "next_url": "/v2/appsPage2", + "resources": [ + { + "metadata": { + "guid": "af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c", + "url": "/v2/apps/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c", + "created_at": "2014-10-10T21:03:13+00:00", + "updated_at": "2014-11-10T14:07:31+00:00" + }, + "entity": { + "name": "app-test", + "production": false, + "space_guid": "8efd7c5c-d83c-4786-b399-b7bd548839e1", + "stack_guid": "2c531037-68a2-4e2c-a9e0-71f9d0abf0d4", + "buildpack": "https://github.com/cloudfoundry/buildpack-go.git", + "detected_buildpack": "", + "detected_buildpack_guid": "0d22f6a1-76c5-417f-ac6c-d9d21463ecbc", + "environment_json": { + "FOOBAR": "QUX" + }, + "memory": 256, + "instances": 1, + "disk_quota": 1024, + "state": "STARTED", + "version": "97ef1272-9eb6-4839-9df1-5ed4f55b5c45", + "command": null, + "console": false, + "debug": null, + "staging_task_id": "5879c8d06a10491a879734162000def8", + "package_state": "PENDING", + "health_check_http_endpoint": null, + "health_check_type": "port", + "health_check_timeout": null, + "staging_failed_reason": null, + "staging_failed_description": null, + "diego": true, + "docker_image": null, + "package_updated_at": "2014-11-10T14:08:50+00:00", + "detected_start_command": "app-launching-service-broker", + "enable_ssh": true, + "docker_credentials_json": { + "redacted_message": "[PRIVATE DATA HIDDEN]" + }, + "ports": [ + 8080 + ], + "space_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1", + "stack_url": "/v2/stacks/2c531037-68a2-4e2c-a9e0-71f9d0abf0d4", + "events_url": "/v2/apps/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c/events", + "service_bindings_url": "/v2/apps/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c/service_bindings", + "routes_url": "/v2/apps/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c/routes" + } + } + ] +}` + +const listAppsPayloadPage2 = `{ + "total_results": 28, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "f9ad202b-76dd-44ec-b7c2-fd2417a561e8", + "url": "/v2/apps/f9ad202b-76dd-44ec-b7c2-fd2417a561e8", + "created_at": "2014-10-10T21:03:13+00:00", + "updated_at": "2014-11-10T14:07:31+00:00" + }, + "entity": { + "name": "app-test2", + "production": false, + "space_guid": "8efd7c5c-d83c-4786-b399-b7bd548839e1", + "stack_guid": "2c531037-68a2-4e2c-a9e0-71f9d0abf0d4", + "buildpack": "https://github.com/cloudfoundry/buildpack-go.git", + "detected_buildpack": null, + "environment_json": { + "FOOBAR": "QUX" + }, + "memory": 256, + "instances": 1, + "disk_quota": 1024, + "state": "STARTED", + "version": "97ef1272-9eb6-4839-9df1-5ed4f55b5c45", + "command": null, + "console": false, + "debug": null, + "staging_task_id": "5879c8d06a10491a879734162000def8", + "package_state": "PENDING", + "health_check_timeout": null, + "staging_failed_reason": null, + "docker_image": null, + "package_updated_at": "2014-11-10T14:08:50+00:00", + "detected_start_command": "app-launching-service-broker", + "space_url": "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1", + "stack_url": "/v2/stacks/2c531037-68a2-4e2c-a9e0-71f9d0abf0d4", + "events_url": "/v2/apps/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c/events", + "service_bindings_url": "/v2/apps/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c/service_bindings", + "routes_url": "/v2/apps/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c/routes" + } + } + ] +}` + +const appPayload = `{ + "metadata": { + "guid": "9902530c-c634-4864-a189-71d763cb12e2", + "url": "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2", + "created_at": "2014-11-07T23:11:39+00:00", + "updated_at": "2014-11-07T23:12:03+00:00" + }, + "entity": { + "name": "test-env", + "production": false, + "space_guid": "a72fa1e8-c694-47b3-85f2-55f61fd00d73", + "stack_guid": "2c531037-68a2-4e2c-a9e0-71f9d0abf0d4", + "buildpack": null, + "detected_buildpack": "Ruby", + "environment_json": {}, + "memory": 256, + "instances": 1, + "disk_quota": 1024, + "state": "STARTED", + "version": "0d2f5607-ab6a-4abd-91fe-222cde1ea0f8", + "command": null, + "console": false, + "debug": null, + "staging_task_id": "46267d4a98ae4f4390aed29975453d60", + "package_state": "STAGED", + "health_check_timeout": null, + "staging_failed_reason": null, + "docker_image": null, + "package_updated_at": "2014-11-07T23:12:58+00:00", + "detected_start_command": "rackup -p $PORT", + "space_url": "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73", + "stack_url": "/v2/stacks/2c531037-68a2-4e2c-a9e0-71f9d0abf0d4", + "events_url": "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2/events", + "service_bindings_url": "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2/service_bindings", + "routes_url": "/v2/apps/9902530c-c634-4864-a189-71d763cb12e2/routes" + } +}` + +const appEnvPayload = `{ + "staging_env_json": { + "STAGING_ENV": "staging_value" + }, + "running_env_json": { + "RUNNING_ENV": "running_value" + }, + "environment_json": { + "env_var": "env_val" + }, + "system_env_json": { + "VCAP_SERVICES": { + "abc": 123 + } + }, + "application_env_json": { + "VCAP_APPLICATION": { + "limits": { + "fds": 16384, + "mem": 1024, + "disk": 1024 + }, + "application_name": "name-2245", + "application_uris": [ + + ], + "name": "name-2245", + "space_name": "name-2246", + "space_id": "3309d44f-78ae-4058-99e1-c50469d1e043", + "uris": [ + + ], + "users": null, + "application_id": "a7c47787-a982-467c-95d7-9ab17cbcc918", + "version": "ed59723c-d691-4d4f-ac5b-f174266c988f", + "application_version": "ed59723c-d691-4d4f-ac5b-f174266c988f" + } + } +}` + +const appPayloadWithEnvironment = `{ + "metadata": { + }, + "entity": { + "environment_json": {"string": "string", "int": 1} + } +}` + +const appInstancePayload = `{ + "0": { + "state": "RUNNING", + "since": 1455210430.5104606, + "debug_ip": null, + "debug_port": null, + "console_ip": null, + "console_port": null + }, + "1": { + "state": "RUNNING", + "since": 1455210430.3912115, + "debug_ip": null, + "debug_port": null, + "console_ip": null, + "console_port": null + } +}` + +const appInstanceUnhealthyPayload = `{ + "0": { + "state": "RUNNING", + "since": 1455210430.5104606, + "debug_ip": null, + "debug_port": null, + "console_ip": null, + "console_port": null + }, + "1": { + "state": "STARTING", + "since": 1455210430.3912115, + "debug_ip": null, + "debug_port": null, + "console_ip": null, + "console_port": null + } +}` + +const appStatsPayload = `{ + "0": { + "state": "RUNNING", + "stats": { + "name": "example-app", + "uris": [ + "example-app.example.com", + "example-app-route2.example.com" + ], + "host": "192.168.1.100", + "port": 61297, + "uptime": 411118, + "mem_quota": 536870912, + "disk_quota": 1073741824, + "fds_quota": 16384, + "usage": { + "time": "2016-09-17 15:46:17 +0000", + "cpu": 0.36580239597146486, + "mem": 518123520, + "disk": 151150592 + } + } + }, + "1": { + "state": "RUNNING", + "stats": { + "name": "example-app", + "uris": [ + "example-app.example.com", + "example-app-route2.example.com" + ], + "host": "192.168.1.101", + "port": 61388, + "uptime": 419568, + "mem_quota": 536870912, + "disk_quota": 1073741824, + "fds_quota": 16384, + "usage": { + "time": "2016-09-17T15:46:17Z", + "cpu": 0.33857742931636664, + "mem": 530731008, + "disk": 151150592 + } + } + }, + "2": { + "state": "RUNNING", + "stats": { + "name": "example-app", + "uris": [ + "example-app.example.com", + "example-app-route2.example.com" + ], + "host": "192.168.1.102", + "port": 61389, + "uptime": 419568, + "mem_quota": 536870912, + "disk_quota": 1073741824, + "fds_quota": 16384, + "usage": { + "time": "2017-04-06T20:32:19.273294439Z", + "cpu": 0.33857742931636664, + "mem": 530731008, + "disk": 151150592 + } + } + }, + "3": { + "state": "RUNNING", + "stats": { + "name": "example-app", + "uris": [ + "example-app.example.com", + "example-app-route2.example.com" + ], + "host": "192.168.1.102", + "port": 61389, + "uptime": 419568, + "mem_quota": 536870912, + "disk_quota": 1073741824, + "fds_quota": 16384, + "usage": { + "time": "2017-04-12 15:27:44 UTC", + "cpu": 0.33857742931636664, + "mem": 530731008, + "disk": 151150592 + } + } + } +}` + +const appRoutesPayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "311d34d1-c045-4853-845f-05132377ad7d", + "url": "/v2/routes/311d34d1-c045-4853-845f-05132377ad7d", + "created_at": "2016-06-08T16:41:44Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "host": "host-36", + "path": "/foo", + "domain_guid": "40a499f7-198a-4289-9aa2-605ba43f92ee", + "space_guid": "c7c0dd06-b078-43d7-adcb-3974cd785fdd", + "service_instance_guid": null, + "port": null, + "domain_url": "/v2/private_domains/40a499f7-198a-4289-9aa2-605ba43f92ee", + "space_url": "/v2/spaces/c7c0dd06-b078-43d7-adcb-3974cd785fdd", + "apps_url": "/v2/routes/311d34d1-c045-4853-845f-05132377ad7d/apps", + "route_mappings_url": "/v2/routes/311d34d1-c045-4853-845f-05132377ad7d/route_mappings" + } + } + ] +}` + +const spacePayload = `{ + "metadata": { + "guid": "a72fa1e8-c694-47b3-85f2-55f61fd00d73", + "url": "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73", + "created_at": "2014-11-03T16:47:24+00:00", + "updated_at": null + }, + "entity": { + "name": "test-space", + "organization_guid": "da0dba14-6064-4f7a-b15a-ff9e677e49b2", + "space_quota_definition_guid": null, + "organization_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2", + "developers_url": "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73/developers", + "managers_url": "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73/managers", + "auditors_url": "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73/auditors", + "apps_url": "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73/apps", + "routes_url": "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73/routes", + "domains_url": "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73/domains", + "service_instances_url": "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73/service_instances", + "app_events_url": "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73/app_events", + "events_url": "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73/events", + "security_groups_url": "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73/security_groups" + } +}` + +const spaceServiceOfferingsPayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "6ec97e21-e879-4f72-9a80-8ec550ffec30", + "url": "/v2/services/6ec97e21-e879-4f72-9a80-8ec550ffec30", + "created_at": "2017-10-03T23:47:16Z", + "updated_at": "2017-10-10T20:53:28Z" + }, + "entity": { + "label": "cool-service", + "provider": null, + "url": null, + "description": "Cool service for CF", + "long_description": null, + "version": null, + "info_url": null, + "active": 1, + "bindable": 1, + "unique_id": "beb05948-0fa8-48bf-bfb2-7b09e6687d37", + "extra": "{\"displayName\":\"Super cool service\",\"longDescription\":\"Very cool service.\",\"documentationUrl\":\"https://readthedocs.org\"}", + "tags": [ + "cool" + ], + "requires": [ + + ], + "documentation_url": null, + "service_broker_guid": "6b3da2f0-c530-4f2b-8fd2-7cd68a21e907", + "plan_updateable": 1, + "service_plans_url": "/v2/services/6ec97e21-e879-4f72-9a80-8ec550ffec30/service_plans" + } + } + ] +}` + +const orgPayload = `{ + "metadata": { + "guid": "da0dba14-6064-4f7a-b15a-ff9e677e49b2", + "url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2", + "created_at": "2014-09-26T13:36:41+00:00", + "updated_at": null + }, + "entity": { + "name": "test-org", + "billing_enabled": false, + "quota_definition_guid": "183599e0-d535-4559-8675-7b6ddb5cc42d", + "status": "active", + "quota_definition_url": "/v2/quota_definitions/183599e0-d535-4559-8675-7b6ddb5cc42d", + "spaces_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/spaces", + "domains_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/domains", + "private_domains_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/private_domains", + "users_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/users", + "managers_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/managers", + "billing_managers_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/billing_managers", + "auditors_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/auditors", + "app_events_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/app_events", + "space_quota_definitions_url": "/v2/organizations/da0dba14-6064-4f7a-b15a-ff9e677e49b2/space_quota_definitions" + } +}` + +const listServiceBindingsPayloadPage1 = `{ + "total_results": 2, + "total_pages": 2, + "prev_url": null, + "next_url": "/v2/service_bindings2", + "resources": [ + { + "metadata": { + "guid": "aa599bb3-4811-405a-bbe3-a68c7c55afc8", + "url": "/v2/service_bindings/aa599bb3-4811-405a-bbe3-a68c7c55afc8", + "created_at": "2016-06-08T16:41:43Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "app_guid": "b26e7e98-f002-41a8-a663-1b60f808a92a", + "service_instance_guid": "bde206e0-1ee8-48ad-b794-44c857633d50", + "credentials": { + "creds-key-66": "creds-val-66" + }, + "binding_options": { + + }, + "gateway_data": null, + "gateway_name": "", + "syslog_drain_url": null, + "volume_mounts": [ + + ], + "app_url": "/v2/apps/b26e7e98-f002-41a8-a663-1b60f808a92a", + "service_instance_url": "/v2/service_instances/bde206e0-1ee8-48ad-b794-44c857633d50" + } + } + ] +}` + +const listServiceBindingsPayloadPage2 = `{ + "total_results": 2, + "total_pages": 2, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "8201b87d-b273-4fdf-8dd4-4b42ce970cc7", + "url": "/v2/service_bindings/aa599bb3-4811-405a-bbe3-a68c7c55afc8", + "created_at": "2016-06-08T16:41:43Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "app_guid": "636bbf83-5b54-488d-9528-066f680a99dc", + "service_instance_guid": "c3023201-44f5-4dc8-a903-c69c9eba9809", + "credentials": { + "creds-key-66": "creds-val-66" + }, + "binding_options": { + + }, + "gateway_data": null, + "gateway_name": "", + "syslog_drain_url": null, + "volume_mounts": [ + + ], + "app_url": "/v2/apps/636bbf83-5b54-488d-9528-066f680a99dc", + "service_instance_url": "/v2/service_instances/c3023201-44f5-4dc8-a903-c69c9eba9809" + } + } + ] +}` + +const serviceBindingByGuidPayload = `{ + "metadata": { + "guid": "foo-bar-baz", + "url": "/v2/service_bindings/foo-bar-baz", + "created_at": "2017-06-22T03:46:24Z", + "updated_at": "2017-06-22T03:46:24Z" + }, + "entity": { + "app_guid": "app-bar-baz", + "service_instance_guid": "instance-bar-baz", + "credentials": { + "host": "host.bar.baz", + "port": 5432 + }, + "binding_options": { + }, + "gateway_data": null, + "gateway_name": "", + "syslog_drain_url": null, + "volume_mounts": [ + ], + "app_url": "/v2/apps/app-bar-baz", + "service_instance_url": "/v2/service_instances/instance-bar-baz" + } +} +` + +const setV3AppEnvironmentVariablesPayload = `{ + "var": { + "RAILS_ENV": "production", + "DEBUG": "false" + }, + "links": { + "self": { + "href": "https://api.example.org/v3/apps/[guid]/environment_variables" + }, + "app": { + "href": "https://api.example.org/v3/apps/[guid]" + } + } +}` + +const listServicePlansPayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "6fecf53b-7553-4cb3-b97e-930f9c4e3385", + "url": "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385", + "created_at": "2016-06-08T16:41:30Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-1575", + "free": false, + "description": "desc-109", + "service_guid": "1ccab853-87c9-45a6-bf99-603032d17fe5", + "extra": null, + "unique_id": "1bc2884c-ee3d-4f82-a78b-1a657f79aeac", + "public": true, + "active": true, + "bindable": true, + "plan_updateable": true, + "service_url": "/v2/services/1ccab853-87c9-45a6-bf99-603032d17fe5", + "service_instances_url": "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385/service_instances" + } + } + ] +}` + +const getServicePlanByGuidPayload = `{ + "metadata": { + "guid": "6fecf53b-7553-4cb3-b97e-930f9c4e3385", + "url": "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385", + "created_at": "2016-06-08T16:41:30Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-1575", + "free": false, + "description": "desc-109", + "service_guid": "1ccab853-87c9-45a6-bf99-603032d17fe5", + "extra": null, + "unique_id": "1bc2884c-ee3d-4f82-a78b-1a657f79aeac", + "public": true, + "active": true, + "bindable": true, + "plan_updateable": true, + "service_url": "/v2/services/1ccab853-87c9-45a6-bf99-603032d17fe5", + "service_instances_url": "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385/service_instances" + } +} +` + +const privateServicePlanPayload = `{ + "metadata": { + "guid": "6fecf53b-7553-4cb3-b97e-930f9c4e3385", + "url": "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385", + "created_at": "2016-06-08T16:41:30Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-1575", + "free": false, + "description": "desc-109", + "service_guid": "1ccab853-87c9-45a6-bf99-603032d17fe5", + "extra": null, + "unique_id": "1bc2884c-ee3d-4f82-a78b-1a657f79aeac", + "public": false, + "active": true, + "bindable": true, + "plan_updateable": true, + "service_url": "/v2/services/1ccab853-87c9-45a6-bf99-603032d17fe5", + "service_instances_url": "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385/service_instances" + } +} +` + +const listServicePayload = `{ + "total_results": 22, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "a3d76c01-c08a-4505-b06d-8603265682a3", + "url": "/v2/services/a3d76c01-c08a-4505-b06d-8603265682a3", + "created_at": "2014-09-24T14:10:51+00:00", + "updated_at": "2014-10-08T00:06:30+00:00" + }, + "entity": { + "label": "nats", + "provider": null, + "url": null, + "description": "NATS is a lightweight cloud messaging system", + "long_description": null, + "version": null, + "info_url": null, + "active": true, + "bindable": true, + "unique_id": "b9310aba-2fa4-11e4-b626-a6c5e4d22fb7", + "extra": "", + "tags": [ + "nats", + "mbus", + "pubsub" + ], + "requires": [], + "documentation_url": null, + "service_broker_guid": "a4bdf03a-f0c4-43f9-9c77-f434da91404f", + "plan_updateable": false, + "service_plans_url": "/v2/services/a3d76c01-c08a-4505-b06d-8603265682a3/service_plans" + } + }, + { + "metadata": { + "guid": "ab9ad9c8-1f51-463a-ae3a-5082e9f04ae6", + "url": "/v2/services/ab9ad9c8-1f51-463a-ae3a-5082e9f04ae6", + "created_at": "2014-09-24T14:10:51+00:00", + "updated_at": "2014-10-08T00:06:30+00:00" + }, + "entity": { + "label": "etcd", + "provider": null, + "url": null, + "description": "Etcd key-value storage", + "long_description": null, + "version": null, + "info_url": null, + "active": true, + "bindable": true, + "unique_id": "211411a0-2da1-11e4-852f-a6c5e4d22fb7", + "extra": "", + "tags": [ + "etcd", + "keyvalue", + "etcd-0.4.6" + ], + "requires": [], + "documentation_url": null, + "service_broker_guid": "a4bdf03a-f0c4-43f9-9c77-f434da91404f", + "plan_updateable": false, + "service_plans_url": "/v2/services/ab9ad9c8-1f51-463a-ae3a-5082e9f04ae6/service_plans" + } + } + ] +}` + +const listServicePlanVisibilitiesPayload = `{ + "total_results": 4, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "d1b5ea55-f354-4f43-b52e-53045747adb9", + "url": "/v2/service_plan_visibilities/d1b5ea55-f354-4f43-b52e-53045747adb9", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "service_plan_guid": "62cb572c-e9ca-4c9f-b822-8292db1d9a96", + "organization_guid": "81df84f3-8ce0-4c92-990a-3760b6ff66bd", + "service_plan_url": "/v2/service_plans/62cb572c-e9ca-4c9f-b822-8292db1d9a96", + "organization_url": "/v2/organizations/81df84f3-8ce0-4c92-990a-3760b6ff66bd" + } + }, + { + "metadata": { + "guid": "332331a3-7b6c-413b-a2e4-edf90ac47fa9", + "url": "/v2/service_plan_visibilities/332331a3-7b6c-413b-a2e4-edf90ac47fa9", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "service_plan_guid": "c505f2ec-81ed-4091-b194-b8e905f32b24", + "organization_guid": "99b61b74-09d6-47db-9568-a835e42d0a1d", + "service_plan_url": "/v2/service_plans/c505f2ec-81ed-4091-b194-b8e905f32b24", + "organization_url": "/v2/organizations/99b61b74-09d6-47db-9568-a835e42d0a1d" + } + } + ] +}` + +const postServicePlanVisibilityPayload = `{ + "metadata": { + "guid": "f740b01a-4afe-4435-aedd-0a8308a7e7d6", + "url": "/v2/service_plan_visibilities/f740b01a-4afe-4435-aedd-0a8308a7e7d6", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "service_plan_guid": "ab5780a9-ac8e-4412-9496-4512e865011a", + "organization_guid": "55d0ff39-dac9-431f-ba6d-83f37381f1c3", + "service_plan_url": "/v2/service_plans/ab5780a9-ac8e-4412-9496-4512e865011a", + "organization_url": "/v2/organizations/55d0ff39-dac9-431f-ba6d-83f37381f1c3" + } +}` + +const listAppsCreatedEventPayload = `{ + "total_results": 3, + "total_pages": 2, + "prev_url": null, + "next_url": "/v2/events2", + "resources": [ + { + "metadata": { + "guid": "49ab122b-82b9-4623-8a13-24e585e32e66", + "url": "/v2/events/49ab122b-82b9-4623-8a13-24e585e32e66", + "created_at": "2016-02-26T13:00:21Z", + "updated_at": null + }, + "entity": { + "type": "audit.app.update", + "actor": "fbf30c43-436e-40e4-8ace-31970b52ce89", + "actor_type": "user", + "actor_name": "team-toad@sap.com", + "actee": "3ca436ff-67a8-468a-8c7d-27ec68a6cfe5", + "actee_type": "app", + "actee_name": "authentication-v1-pre-blue", + "timestamp": "2016-02-26T13:00:21Z", + "metadata": { + "request": { + "state": "STOPPED" + } + }, + "space_guid": "08582a96-cbef-463c-822e-bda8d4284cc7", + "organization_guid": "bfdcdf09-a3b8-46f4-ab74-d494efefe5b4" + } + }, + { + "metadata": { + "guid": "49ab122b-82b9-4623-8a13-24e585e32e66", + "url": "/v2/events/49ab122b-82b9-4623-8a13-24e585e32e66", + "created_at": "2016-02-26T13:00:21Z", + "updated_at": "2016-02-26T13:00:21Z" + }, + "entity": { + "type": "app.crash", + "actor": "fbf30c43-436e-40e4-8ace-31970b52ce89", + "actor_type": "app", + "actor_name": "authentication-v1-pre-blue", + "actee": "3ca436ff-67a8-468a-8c7d-27ec68a6cfe5", + "actee_type": "app", + "actee_name": "authentication-v1-pre-blue", + "timestamp": "2016-02-26T13:00:21Z", + "metadata": { + "instance": "", + "index": 0, + "exit_description": "2 error(s) occurred:\n\n* 1 error(s) occurred:\n\n* Exited with status 4\n* 2 error(s) occurred:\n\n* cancelled\n* cancelled", + "reason": "CRASHED" + }, + "space_guid": "08582a96-cbef-463c-822e-bda8d4284cc7", + "organization_guid": "bfdcdf09-a3b8-46f4-ab74-d494efefe5b4" + } + } + ] + }` +const listAppsCreatedEventPayload2 = `{ + "total_results": 3, + "total_pages": 2, + "prev_url": "/v2/events", + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "8e8f83c7-3fc3-4127-9359-ae391380b971", + "url": "/v2/events/8e8f83c7-3fc3-4127-9359-ae391380b971", + "created_at": "2016-02-26T13:00:21Z", + "updated_at": null + }, + "entity": { + "type": "audit.app.update", + "actor": "fbf30c43-436e-40e4-8ace-31970b52ce89", + "actor_type": "user", + "actor_name": "team-toad@sap.com", + "actee": "3ca436ff-67a8-468a-8c7d-27ec68a6cfe5", + "actee_type": "app", + "actee_name": "authentication-v1-pre-blue", + "timestamp": "2016-02-26T13:00:21Z", + "metadata": { + "request": { + "health_check_timeout": 180, + "buildpack": "nodejs_buildpack", + "command": "PRIVATE DATA HIDDEN", + "state": "STARTED" + } + }, + "space_guid": "08582a96-cbef-463c-822e-bda8d4284cc7", + "organization_guid": "bfdcdf09-a3b8-46f4-ab74-d494efefe5b4" + } + } + ] + }` + +var serviceInstancePayload = `{ + "metadata": { + "guid": "8423ca96-90ad-411f-b77a-0907844949fc", + "url": "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc", + "created_at": "2016-10-21T18:22:56Z", + "updated_at": "2016-10-21T18:22:56Z" + }, + "entity": { + "name": "fortunes-db", + "credentials": {}, + "service_guid": "440ce9d9-b108-4bbe-80b4-08338f3cc25b", + "service_plan_guid": "f48419f7-4717-4706-86e4-a24973848a77", + "space_guid": "21e5fdc7-5131-4743-8447-6373cf336a77", + "gateway_data": null, + "dashboard_url": "https://p-mysql.system.example.com/manage/instances/8423ca96-90ad-411f-b77a-0907844949fc", + "type": "managed_service_instance", + "last_operation": { + "type": "create", + "state": "succeeded", + "description": "", + "updated_at": null, + "created_at": "2016-10-21T18:22:56Z" + }, + "tags": [], + "space_url": "/v2/spaces/21e5fdc7-5131-4743-8447-6373cf336a77", + "service_plan_url": "/v2/service_plans/f48419f7-4717-4706-86e4-a24973848a77", + "service_bindings_url": "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/service_bindings", + "service_keys_url": "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/service_keys", + "routes_url": "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/routes", + "service_url": "/v2/services/440ce9d9-b108-4bbe-80b4-08338f3cc25b" + } +}` + +var serviceInstanceParamsPayload = `{ + "foo": "bar", + "baz": 42 +}` + +var listServiceInstancePayload = `{ + "total_results": 2, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "8423ca96-90ad-411f-b77a-0907844949fc", + "url": "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc", + "created_at": "2016-10-21T18:22:56Z", + "updated_at": null + }, + "entity": { + "name": "fortunes-db", + "credentials": {}, + "service_guid": "440ce9d9-b108-4bbe-80b4-08338f3cc25b", + "service_plan_guid": "f48419f7-4717-4706-86e4-a24973848a77", + "space_guid": "21e5fdc7-5131-4743-8447-6373cf336a77", + "gateway_data": null, + "dashboard_url": "https://p-mysql.system.example.com/manage/instances/8423ca96-90ad-411f-b77a-0907844949fc", + "type": "managed_service_instance", + "last_operation": { + "type": "create", + "state": "succeeded", + "description": "", + "updated_at": null, + "created_at": "2016-10-21T18:22:56Z" + }, + "tags": [], + "space_url": "/v2/spaces/21e5fdc7-5131-4743-8447-6373cf336a77", + "service_plan_url": "/v2/service_plans/f48419f7-4717-4706-86e4-a24973848a77", + "service_bindings_url": "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/service_bindings", + "service_keys_url": "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/service_keys", + "routes_url": "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/routes", + "service_url": "/v2/services/440ce9d9-b108-4bbe-80b4-08338f3cc25b" + } + }, + { + "metadata": { + "guid": "8423ca96-90ad-411f-b77a-0907844949fc", + "url": "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc", + "created_at": "2016-10-21T18:22:56Z", + "updated_at": null + }, + "entity": { + "name": "fortunes-db", + "credentials": {}, + "service_guid": "440ce9d9-b108-4bbe-80b4-08338f3cc25b", + "service_plan_guid": "f48419f7-4717-4706-86e4-a24973848a77", + "space_guid": "21e5fdc7-5131-4743-8447-6373cf336a77", + "gateway_data": null, + "dashboard_url": "https://p-mysql.system.example.com/manage/instances/8423ca96-90ad-411f-b77a-0907844949fc", + "type": "managed_service_instance", + "last_operation": { + "type": "create", + "state": "succeeded", + "description": "", + "updated_at": null, + "created_at": "2016-10-21T18:22:56Z" + }, + "tags": [], + "space_url": "/v2/spaces/21e5fdc7-5131-4743-8447-6373cf336a77", + "service_plan_url": "/v2/service_plans/f48419f7-4717-4706-86e4-a24973848a77", + "service_bindings_url": "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/service_bindings", + "service_keys_url": "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/service_keys", + "routes_url": "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/routes", + "service_url": "/v2/services/440ce9d9-b108-4bbe-80b4-08338f3cc25b" + } + } + ] +}` + +const userProvidedServiceInstancePayload = `{ + "metadata": { + "guid": "e9358711-0ad9-4f2a-b3dc-289d47c17c87", + "url": "/v2/user_provided_service_instances/e9358711-0ad9-4f2a-b3dc-289d47c17c87", + "created_at": "2016-06-08T16:41:33Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-1700", + "credentials": { + "creds-key-58": "creds-val-58" + }, + "space_guid": "22236d1a-d9c7-44b7-bdad-2bb079a6c4a1", + "type": "user_provided_service_instance", + "syslog_drain_url": "https://foo.com/url-104", + "route_service_url": null, + "space_url": "/v2/spaces/22236d1a-d9c7-44b7-bdad-2bb079a6c4a1", + "service_bindings_url": "/v2/user_provided_service_instances/e9358711-0ad9-4f2a-b3dc-289d47c17c87/service_bindings", + "routes_url": "/v2/user_provided_service_instances/e9358711-0ad9-4f2a-b3dc-289d47c17c87/routes" + } +}` + +const listUserProvidedServiceInstancePayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "54e4c645-7d20-4271-8c27-8cc904e1e7ee", + "url": "/v2/user_provided_service_instances/54e4c645-7d20-4271-8c27-8cc904e1e7ee", + "created_at": "2016-06-08T16:41:33Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-1696", + "credentials": { + "creds-key-57": "creds-val-57" + }, + "space_guid": "87d14ac2-f396-460e-a523-dc1d77aba35a", + "type": "user_provided_service_instance", + "syslog_drain_url": "https://foo.com/url-103", + "route_service_url": null, + "space_url": "/v2/spaces/87d14ac2-f396-460e-a523-dc1d77aba35a", + "service_bindings_url": "/v2/user_provided_service_instances/54e4c645-7d20-4271-8c27-8cc904e1e7ee/service_bindings", + "routes_url": "/v2/user_provided_service_instances/54e4c645-7d20-4271-8c27-8cc904e1e7ee/routes" + } + } + ] +}` + +const listRoutesPayloadPage1 string = `{ + "total_results": 2, + "total_pages": 2, + "prev_url": null, + "next_url": "/v2/routes_page_2", + "resources": [ + { + "metadata": { + "guid": "24707add-83b8-4fd8-a8f4-b7297199c805", + "url": "/v2/routes/24707add-83b8-4fd8-a8f4-b7297199c805", + "created_at": "2017-02-06T14:13:57Z", + "updated_at": "2017-02-06T14:13:57Z" + }, + "entity": { + "host": "test-1", + "path": "/foo", + "domain_guid": "0b183484-45cc-4855-94d4-892f80f20c13", + "space_guid": "494d8b64-8181-4183-a6d3-6279db8fec6e", + "service_instance_guid": null, + "port": null, + "domain_url": "/v2/shared_domains/0b183484-45cc-4855-94d4-892f80f20c13", + "space_url": "/v2/spaces/494d8b64-8181-4183-a6d3-6279db8fec6e", + "apps_url": "/v2/routes/24707add-83b8-4fd8-a8f4-b7297199c805/apps", + "route_mappings_url": "/v2/routes/24707add-83b8-4fd8-a8f4-b7297199c805/route_mappings" + } + } + ] +}` + +const listRoutesPayloadPage2 string = `{ + "total_results": 2, + "total_pages": 2, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "1aba0d30-eb57-4543-a805-0d1c77171b4d", + "url": "/v2/routes/1aba0d30-eb57-4543-a805-0d1c77171b4d", + "created_at": "2017-02-07T03:57:17Z", + "updated_at": "2017-02-07T03:57:17Z" + }, + "entity": { + "host": "test-2", + "path": "", + "domain_guid": "0b183484-45cc-4855-94d4-892f80f20c13", + "space_guid": "494d8b64-8181-4183-a6d3-6279db8fec6e", + "service_instance_guid": null, + "port": null, + "domain_url": "/v2/shared_domains/0b183484-45cc-4855-94d4-892f80f20c13", + "space_url": "/v2/spaces/494d8b64-8181-4183-a6d3-6279db8fec6e", + "apps_url": "/v2/routes/1aba0d30-eb57-4543-a805-0d1c77171b4d/apps", + "route_mappings_url": "/v2/routes/1aba0d30-eb57-4543-a805-0d1c77171b4d/route_mappings" + } + } + ] +}` + +const bindRoute string = ` +{ + "metadata": { + "guid": "7803de15-a20f-4dea-bf17-37de56629582", + "url": "/v2/routes/7803de15-a20f-4dea-bf17-37de56629582", + "created_at": "2016-06-08T16:41:28Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "host": "foo-host", + "path": "", + "domain_guid": "08167353-32da-4ed9-9ef5-aa7b31bbc009", + "space_guid": "b65a9a76-8c55-460b-9162-18b396da66cf", + "service_instance_guid": null, + "port": null, + "domain_url": "/v2/shared_domains/08167353-32da-4ed9-9ef5-aa7b31bbc009", + "space_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf", + "apps_url": "/v2/routes/7803de15-a20f-4dea-bf17-37de56629582/apps", + "route_mappings_url": "/v2/routes/7803de15-a20f-4dea-bf17-37de56629582/route_mappings" + } +} +` + +const createRoute string = ` +{ + "metadata": { + "guid": "b3fe6f31-e897-4e02-b49e-263ca96b4e3a", + "url": "/v2/routes/b3fe6f31-e897-4e02-b49e-263ca96b4e3a", + "created_at": "2018-05-24T22:16:36Z", + "updated_at": "2018-05-24T22:16:36Z" + }, + "entity": { + "host": "foo-host", + "path": "", + "domain_guid": "08167353-32da-4ed9-9ef5-aa7b31bbc009", + "space_guid": "b65a9a76-8c55-460b-9162-18b396da66cf", + "service_instance_guid": null, + "port": null, + "domain_url": "/v2/shared_domains/08167353-32da-4ed9-9ef5-aa7b31bbc009", + "domain": { + "metadata": { + "guid": "08167353-32da-4ed9-9ef5-aa7b31bbc009", + "url": "/v2/shared_domains/08167353-32da-4ed9-9ef5-aa7b31bbc009", + "created_at": "2018-05-11T00:28:15Z", + "updated_at": "2018-05-11T00:28:15Z" + }, + "entity": { + "name": "apps.pcf.example.com", + "router_group_guid": null, + "router_group_type": null + } + }, + "space_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf", + "space": { + "metadata": { + "guid": "b65a9a76-8c55-460b-9162-18b396da66cf", + "url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf", + "created_at": "2018-05-18T02:52:35Z", + "updated_at": "2018-05-18T02:52:35Z" + }, + "entity": { + "name": "dev", + "organization_guid": "de84b21e-dd45-4a58-b483-83be15cf1b00", + "space_quota_definition_guid": null, + "isolation_segment_guid": null, + "allow_ssh": true, + "organization_url": "/v2/organizations/de84b21e-dd45-4a58-b483-83be15cf1b00", + "developers_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/developers", + "managers_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/managers", + "auditors_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/auditors", + "apps_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/apps", + "routes_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/routes", + "domains_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/domains", + "service_instances_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/service_instances", + "app_events_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/app_events", + "events_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/events", + "security_groups_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/security_groups", + "staging_security_groups_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/staging_security_groups" + } + }, + "apps_url": "/v2/routes/b3fe6f31-e897-4e02-b49e-263ca96b4e3a/apps", + "apps": [ + + ], + "route_mappings_url": "/v2/routes/b3fe6f31-e897-4e02-b49e-263ca96b4e3a/route_mappings" + } +} +` + +const createTcpRoute string = ` +{ + "metadata": { + "guid": "78fe5006-1d1c-41ba-94de-eb7002241b82", + "url": "/v2/routes/78fe5006-1d1c-41ba-94de-eb7002241b82", + "created_at": "2017-05-24T19:04:34Z", + "updated_at": null + }, + "entity": { + "host": "", + "path": "", + "domain_guid": "08167353-32da-4ed9-9ef5-aa7b31bbc009", + "space_guid": "b65a9a76-8c55-460b-9162-18b396da66cf", + "service_instance_guid": null, + "port": 1099, + "domain_url": "/v2/shared_domains/08167353-32da-4ed9-9ef5-aa7b31bbc009", + "domain": { + "metadata": { + "guid": "08167353-32da-4ed9-9ef5-aa7b31bbc009", + "url": "/v2/shared_domains/08167353-32da-4ed9-9ef5-aa7b31bbc009", + "created_at": "2017-01-17T17:54:46Z", + "updated_at": null + }, + "entity": { + "name": "tcp.main.example.com", + "router_group_guid": "b4c90165-5689-4a7e-4cfc-f55dc41f3e22", + "router_group_type": null + } + }, + "space_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf", + "space": { + "metadata": { + "guid": "b65a9a76-8c55-460b-9162-18b396da66cf", + "url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf", + "created_at": "2016-12-09T15:06:17Z", + "updated_at": null + }, + "entity": { + "name": "system", + "organization_guid": "236c6d93-7cfb-4d4a-bc76-9a9cc2bc8e58", + "space_quota_definition_guid": null, + "allow_ssh": true, + "organization_url": "/v2/organizations/236c6d93-7cfb-4d4a-bc76-9a9cc2bc8e58", + "developers_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/developers", + "managers_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/managers", + "auditors_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/auditors", + "apps_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/apps", + "routes_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/routes", + "domains_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/domains", + "service_instances_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/service_instances", + "app_events_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/app_events", + "events_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/events", + "security_groups_url": "/v2/spaces/b65a9a76-8c55-460b-9162-18b396da66cf/security_groups" + } + }, + "apps_url": "/v2/routes/78fe5006-1d1c-41ba-94de-eb7002241b82/apps", + "apps": [ + + ], + "route_mappings_url": "/v2/routes/78fe5006-1d1c-41ba-94de-eb7002241b82/route_mappings" + } +}` + +const listStacksPayloadPage1 string = `{ + "total_results": 2, + "total_pages": 2, + "prev_url": null, + "next_url": "/v2/stacks_page_2", + "resources": [ + { + "metadata": { + "guid": "67e019a3-322a-407a-96e0-178e95bd0e55", + "url": "/v2/stacks/67e019a3-322a-407a-96e0-178e95bd0e55", + "created_at": "2017-01-18T16:39:11Z", + "updated_at": "2017-01-18T16:39:11Z" + }, + "entity": { + "name": "cflinuxfs2", + "description": "Cloud Foundry Linux-based filesystem" + } + } + ] +}` + +const listStacksPayloadPage2 string = `{ + "total_results": 2, + "total_pages": 2, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "a9be2e10-0164-401d-94e0-88455d614844", + "url": "/v2/stacks/a9be2e10-0164-401d-94e0-88455d614844", + "created_at": "2017-01-18T16:39:11Z", + "updated_at": "2017-01-18T16:39:11Z" + }, + "entity": { + "name": "windows2012R2", + "description": "Experimental Windows runtime" + } + } + ] +}` + +const stackByGuidPayload = `{ + "metadata": { + "guid": "a9be2e10-0164-401d-94e0-88455d614844", + "url": "/v2/stacks/a9be2e10-0164-401d-94e0-88455d614844", + "created_at": "2017-01-18T16:39:11Z", + "updated_at": "2017-01-18T16:39:11Z" + }, + "entity": { + "name": "windows2012R2", + "description": "Experimental Windows runtime" + } +}` + +const listTasksByAppPayloadPage1 string = ` +{ + "pagination": { + "total_results": 4, + "total_pages": 2, + "first": { + "href": "https://api.run.example.com/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5/tasks?page=1&per_page=2" + }, + "last": { + "href": "https://api.run.example.com/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5/tasks?page=2&per_page=2" + }, + "next": { + "href": "https://api.run.example.com/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5/tasks?page=2&per_page=2" + }, + "previous": null + }, + "resources": [ + { + "guid": "d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa", + "sequence_id": 1, + "name": "hello", + "state": "SUCCEEDED", + "memory_in_mb": 512, + "disk_in_mb": 1024, + "result": { + "failure_reason": null + }, + "droplet_guid": "740ebd2b-162b-469a-bd72-3edb96fabd9a", + "metadata": { + "labels": { }, + "annotations": { } + }, + "created_at": "2016-05-04T17:00:41Z", + "updated_at": "2016-05-04T17:00:42Z", + "relationships": { + "app": { + "data": { + "guid": "ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/tasks/d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa" + }, + "app": { + "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + }, + "cancel": { + "href": "https://api.example.org/v3/tasks/d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa/actions/cancel", + "method": "POST" + }, + "droplet": { + "href": "https://api.example.org/v3/droplets/740ebd2b-162b-469a-bd72-3edb96fabd9a" + } + } + }, + { + "guid": "63b4cd89-fd8b-4bf1-a311-7174fcc907d6", + "sequence_id": 2, + "name": "migrate", + "state": "FAILED", + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "result": { + "failure_reason": "Exited with status 1" + }, + "droplet_guid": "740ebd2b-162b-469a-bd72-3edb96fabd9a", + "metadata": { + "labels": { }, + "annotations": { } + }, + "created_at": "2016-05-04T17:00:43Z", + "updated_at": "2016-05-04T17:00:44Z", + "relationships": { + "app": { + "data": { + "guid": "ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/tasks/63b4cd89-fd8b-4bf1-a311-7174fcc907d6" + }, + "app": { + "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + }, + "cancel": { + "href": "https://api.example.org/v3/tasks/63b4cd89-fd8b-4bf1-a311-7174fcc907d6/actions/cancel", + "method": "POST" + }, + "droplet": { + "href": "https://api.example.org/v3/droplets/740ebd2b-162b-469a-bd72-3edb96fabd9a" + } + } + } + ] +} +` + +const listTasksByAppPayloadPage2 string = ` +{ + "pagination": { + "total_results": 4, + "total_pages": 2, + "first": { + "href": "https://api.run.example.com/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5/tasks?page=1&per_page=2" + }, + "last": { + "href": "https://api.run.example.com/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5/tasks?page=2&per_page=2" + }, + "next": null, + "previous":{ + "href": "https://api.run.example.com/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5/tasks?page=1&per_page=2" + } + }, + "resources": [ + { + "guid": "abcdefc-99a3-4e6a-af91-a44b4ab7b6fa", + "sequence_id": 3, + "name": "hi", + "state": "SUCCEEDED", + "memory_in_mb": 512, + "disk_in_mb": 1024, + "result": { + "failure_reason": null + }, + "droplet_guid": "740ebd2b-162b-469a-bd72-3edb96fabd9a", + "metadata": { + "labels": { }, + "annotations": { } + }, + "created_at": "2016-05-04T17:00:44Z", + "updated_at": "2016-05-04T17:00:45Z", + "relationships": { + "app": { + "data": { + "guid": "ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/tasks/abcdefc-99a3-4e6a-af91-a44b4ab7b6fa" + }, + "app": { + "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + }, + "cancel": { + "href": "https://api.example.org/v3/tasks/abcdefc-99a3-4e6a-af91-a44b4ab7b6fa/actions/cancel", + "method": "POST" + }, + "droplet": { + "href": "https://api.example.org/v3/droplets/740ebd2b-162b-469a-bd72-3edb96fabd9a" + } + } + }, + { + "guid": "hijklm9-fd8b-4bf1-a311-7174fcc907d6", + "sequence_id": 4, + "name": "hello2", + "state": "SUCCEEDED", + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "result": { + "failure_reason": "Exited with status 1" + }, + "droplet_guid": "740ebd2b-162b-469a-bd72-3edb96fabd9a", + "metadata": { + "labels": { }, + "annotations": { } + }, + "created_at": "2016-05-04T17:00:46Z", + "updated_at": "2016-05-04T17:00:47Z", + "relationships": { + "app": { + "data": { + "guid": "ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/tasks/hijklm9-fd8b-4bf1-a311-7174fcc907d6" + }, + "app": { + "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + }, + "cancel": { + "href": "https://api.example.org/v3/tasks/hijklm9-fd8b-4bf1-a311-7174fcc907d6/actions/cancel", + "method": "POST" + }, + "droplet": { + "href": "https://api.example.org/v3/droplets/740ebd2b-162b-469a-bd72-3edb96fabd9a" + } + } + } + ] +} +` + +const listTasksPayloadPage1 string = ` +{ + "pagination": { + "total_results": 4, + "total_pages": 2, + "first": { + "href ": "https: //api.run.example.com/v3/tasks?page=1&per_page=2" + }, + "last": { + "href": "https://api.run.example.com/v3/tasks?page=2&per_page=2" + }, + "next": { + "href": "https://api.run.example.com/v3/tasks?page=2&per_page=2" + }, + "previous": null + }, + "resources": [{ + "guid": "d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa", + "sequence_id": 1, + "name": "hello", + "state": "SUCCEEDED", + "memory_in_mb": 512, + "disk_in_mb": 1024, + "result": { + "failure_reason": null + }, + "droplet_guid": "740ebd2b-162b-469a-bd72-3edb96fabd9a", + "metadata": { + "labels": {}, + "annotations": {} + }, + "created_at": "2016-05-04T17:00:41Z", + "updated_at": "2016-05-04T17:00:42Z", + "links": { + "self": { + "href": "https://api.example.org/v3/tasks/d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa" + }, + "app": { + "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + }, + "cancel": { + "href": "https://api.example.org/v3/tasks/d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa/actions/cancel", + "method": "POST" + }, + "droplet": { + "href": "https://api.example.org/v3/droplets/740ebd2b-162b-469a-bd72-3edb96fabd9a" + } + } + }, + { + "guid": "63b4cd89-fd8b-4bf1-a311-7174fcc907d6", + "sequence_id": 2, + "name": "migrate", + "state": "FAILED", + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "result": { + "failure_reason": "Exited with status 1" + }, + "droplet_guid": "740ebd2b-162b-469a-bd72-3edb96fabd9a", + "metadata": { + "labels": {}, + "annotations": {} + }, + "created_at": "2016-05-04T17:00:43Z", + "updated_at": "2016-05-04T17:00:44Z", + "links": { + "self": { + "href": "https://api.example.org/v3/tasks/63b4cd89-fd8b-4bf1-a311-7174fcc907d6" + }, + "app": { + "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + }, + "cancel": { + "href": "https://api.example.org/v3/tasks/63b4cd89-fd8b-4bf1-a311-7174fcc907d6/actions/cancel", + "method": "POST" + }, + "droplet": { + "href": "https://api.example.org/v3/droplets/740ebd2b-162b-469a-bd72-3edb96fabd9a" + } + } + } + ] +}` + +const listTasksPayloadPage2 string = ` +{ + "pagination": { + "total_results": 4, + "total_pages": 2, + "first": { + "href": "https://api.run.example.com/v3/tasks?page=1&per_page=2" + }, + "last": { + "href": "https://api.run.example.com/v3/tasks?page=2&per_page=2" + }, + "next": null, + "previous": { + "href": "https://api.run.example.com/v3/tasks?page=1&per_page=2" + } + }, + "resources": [ + { + "guid": "abcdefc-99a3-4e6a-af91-a44b4ab7b6fa", + "sequence_id": 3, + "name": "hi", + "state": "SUCCEEDED", + "memory_in_mb": 512, + "disk_in_mb": 1024, + "result": { + "failure_reason": null + }, + "droplet_guid": "740ebd2b-162b-469a-bd72-3edb96fabd9a", + "metadata": { + "labels": { }, + "annotations": { } + }, + "created_at": "2016-05-04T17:00:44Z", + "updated_at": "2016-05-04T17:00:45Z", + "links": { + "self": { + "href": "https://api.example.org/v3/tasks/abcdefc-99a3-4e6a-af91-a44b4ab7b6fa" + }, + "app": { + "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + }, + "cancel": { + "href": "https://api.example.org/v3/tasks/abcdefc-99a3-4e6a-af91-a44b4ab7b6fa/actions/cancel", + "method": "POST" + }, + "droplet": { + "href": "https://api.example.org/v3/droplets/740ebd2b-162b-469a-bd72-3edb96fabd9a" + } + } + }, + { + "guid": "hijklm9-fd8b-4bf1-a311-7174fcc907d6", + "sequence_id": 4, + "name": "hello2", + "state": "SUCCEEDED", + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "result": { + "failure_reason": "Exited with status 1" + }, + "droplet_guid": "740ebd2b-162b-469a-bd72-3edb96fabd9a", + "metadata": { + "labels": { }, + "annotations": { } + }, + "created_at": "2016-05-04T17:00:46Z", + "updated_at": "2016-05-04T17:00:47Z", + "links": { + "self": { + "href": "https://api.example.org/v3/tasks/hijklm9-fd8b-4bf1-a311-7174fcc907d6" + }, + "app": { + "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + }, + "cancel": { + "href": "https://api.example.org/v3/tasks/hijklm9-fd8b-4bf1-a311-7174fcc907d6/actions/cancel", + "method": "POST" + }, + "droplet": { + "href": "https://api.example.org/v3/droplets/740ebd2b-162b-469a-bd72-3edb96fabd9a" + } + } + } + ] +} +` + +const createTaskPayload = ` +{ + "guid": "d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa", + "sequence_id": 1, + "name": "migrate", + "command": "rake db:migrate", + "state": "RUNNING", + "memory_in_mb": 512, + "disk_in_mb": 1024, + "result": { + "failure_reason": null + }, + "droplet_guid": "740ebd2b-162b-469a-bd72-3edb96fabd9a", + "created_at": "2016-05-04T17:00:41Z", + "updated_at": "2016-05-04T17:00:42Z", + "links": { + "self": { + "href": "https://api.example.org/v3/tasks/d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa" + }, + "app": { + "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + }, + "droplet": { + "href": "https://api.example.org/v3/droplets/740ebd2b-162b-469a-bd72-3edb96fabd9a" + } + } +} +` + +const errorV3Payload = `{ + "errors": [ + { + "code": 10008, + "title": "CF-UnprocessableEntity", + "detail": "something went wrong" + } + ] +} +` + +const listDomainsPayload = `{ + "total_results": 4, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "b2a35f0c-d5ad-4a59-bea7-461711d96b0d", + "url": "/v2/private_domains/b2a35f0c-d5ad-4a59-bea7-461711d96b0d", + "created_at": "2016-06-08T16:41:39Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "vcap.me", + "owning_organization_guid": "4cf3bc47-eccd-4662-9322-7833c3bdcded", + "owning_organization_url": "/v2/organizations/4cf3bc47-eccd-4662-9322-7833c3bdcded", + "shared_organizations_url": "/v2/private_domains/b2a35f0c-d5ad-4a59-bea7-461711d96b0d/shared_organizations" + } + }, + { + "metadata": { + "guid": "28db6393-cc6f-4318-a63c-f4009e8842bc", + "url": "/v2/private_domains/28db6393-cc6f-4318-a63c-f4009e8842bc", + "created_at": "2016-06-08T16:41:39Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "domain-61.example.com", + "owning_organization_guid": "c262280e-0ccc-4e13-918a-6852f2d1e3a0", + "owning_organization_url": "/v2/organizations/c262280e-0ccc-4e13-918a-6852f2d1e3a0", + "shared_organizations_url": "/v2/private_domains/28db6393-cc6f-4318-a63c-f4009e8842bc/shared_organizations" + } + }, + { + "metadata": { + "guid": "a16ffec7-5fab-4447-861e-c38da6548c6d", + "url": "/v2/private_domains/a16ffec7-5fab-4447-861e-c38da6548c6d", + "created_at": "2016-06-08T16:41:39Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "domain-62.example.com", + "owning_organization_guid": "68f69961-f751-4b52-907c-4469009fdf74", + "owning_organization_url": "/v2/organizations/68f69961-f751-4b52-907c-4469009fdf74", + "shared_organizations_url": "/v2/private_domains/a16ffec7-5fab-4447-861e-c38da6548c6d/shared_organizations" + } + }, + { + "metadata": { + "guid": "4168cdaf-1586-41a6-9e5f-d8c715c332f5", + "url": "/v2/private_domains/4168cdaf-1586-41a6-9e5f-d8c715c332f5", + "created_at": "2016-06-08T16:41:39Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "domain-63.example.com", + "owning_organization_guid": "8d8ed1ba-f7f3-48f1-8d9a-2dfaad91335b", + "owning_organization_url": "/v2/organizations/8d8ed1ba-f7f3-48f1-8d9a-2dfaad91335b", + "shared_organizations_url": "/v2/private_domains/4168cdaf-1586-41a6-9e5f-d8c715c332f5/shared_organizations" + } + } + ] +}` + +const listSharedDomainsPayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "91977695-8ad9-40db-858f-4df782603ec3", + "url": "/v2/shared_domains/91977695-8ad9-40db-858f-4df782603ec3", + "created_at": "2016-06-08T16:41:37Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "domain-49.example.com", + "router_group_guid": "my-random-guid", + "router_group_type": "tcp" + } + } + ] +}` + +const listSharedDomainByGuidPayload = `{ + "metadata": { + "guid": "91977695-8ad9-40db-858f-4df782603ec3", + "url": "/v2/shared_domains/91977695-8ad9-40db-858f-4df782603ec3", + "created_at": "2016-06-08T16:41:37Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "apps.some.random.cf.installation.example.com", + "internal": false, + "router_group_guid": null, + "router_group_type": null + } +} +` + +const listDomainByGuidPayload = `{ + "metadata": { + "guid": "b2a35f0c-d5ad-4a59-bea7-461711d96b0d", + "url": "/v2/private_domains/b2a35f0c-d5ad-4a59-bea7-461711d96b0d", + "created_at": "2016-06-08T16:41:39Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "vcap.me", + "owning_organization_guid": "4cf3bc47-eccd-4662-9322-7833c3bdcded", + "owning_organization_url": "/v2/organizations/4cf3bc47-eccd-4662-9322-7833c3bdcded", + "shared_organizations_url": "/v2/private_domains/b2a35f0c-d5ad-4a59-bea7-461711d96b0d/shared_organizations" + } +} +` + +const listDomainsEmptyResponse = `{ + "total_results": 0, + "total_pages": 0, + "prev_url": null, + "next_url": null, + "resources": [] +}` + +const postDomainPayload = `{ + "metadata": { + "guid": "b98aeca1-22b9-49f9-8428-3ace9ea2ba11", + "url": "/v2/private_domains/b98aeca1-22b9-49f9-8428-3ace9ea2ba11", + "created_at": "2016-06-08T16:41:39Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "exmaple.com", + "owning_organization_guid": "8483e4f1-d3a3-43e2-ab8c-b05ea40ef8db", + "owning_organization_url": "/v2/organizations/8483e4f1-d3a3-43e2-ab8c-b05ea40ef8db", + "shared_organizations_url": "/v2/private_domains/b98aeca1-22b9-49f9-8428-3ace9ea2ba11/shared_organizations" + } +}` + +const getDomainPayload = `{ + "metadata": { + "guid": "369373be-7864-4bb9-a8ef-8274da1f8c8b", + "url": "/v2/private_domains/369373be-7864-4bb9-a8ef-8274da1f8c8b", + "created_at": "2016-06-08T16:41:39Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "example.com", + "owning_organization_guid": "8483e4f1-d3a3-43e2-ab8c-b05ea40ef8db", + "owning_organization_url": "/v2/organizations/8483e4f1-d3a3-43e2-ab8c-b05ea40ef8db", + "shared_organizations_url": "/v2/private_domains/369373be-7864-4bb9-a8ef-8274da1f8c8b/shared_organizations" + } +}` + +const postExternalSharedDomainPayload = `{ + "metadata": { + "guid": "b98aeca1-22b9-49f9-8428-3ace9ea2ba11", + "url": "/v2/shared_domains/b98aeca1-22b9-49f9-8428-3ace9ea2ba11", + "created_at": "2016-06-08T16:41:39Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "shared-exmaple.com", + "internal": false, + "router_group_guid": "8483e4f1-d3a3-43e2-ab8c-b05ea40ef8db", + "router_group_type": "tcp" + } +}` + +const postInternalSharedDomainPayload = `{ + "metadata": { + "guid": "b98aeca1-22b9-49f9-8428-3ace9ea2ba11", + "url": "/v2/shared_domains/b98aeca1-22b9-49f9-8428-3ace9ea2ba11", + "created_at": "2016-06-08T16:41:39Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "shared-exmaple.com", + "internal": true + } +}` + +const listBuildpacksPayload = `{ + "total_results": 3, + "total_pages": 1, + "prev_url": null, + "next_url": "/v2/buildpacksPage2", + "resources": [ + { + "metadata": { + "guid": "c92b6f5f-d2a4-413a-b515-647d059723aa", + "url": "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name_1", + "position": 1, + "enabled": true, + "locked": false, + "filename": "name-1616", + "stack": "cflinuxfs2" + } + }, + { + "metadata": { + "guid": "4de2ac22-ef36-4d62-9698-5f2b426748a9", + "url": "/v2/buildpacks/4de2ac22-ef36-4d62-9698-5f2b426748a9", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name_2", + "position": 2, + "enabled": true, + "locked": false, + "filename": "name-1617", + "stack": "cflinuxfs2" + } + }, + { + "metadata": { + "guid": "79f16936-56f1-41d5-a4c4-f0e9a8877791", + "url": "/v2/buildpacks/79f16936-56f1-41d5-a4c4-f0e9a8877791", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name_3", + "position": 3, + "enabled": true, + "locked": false, + "filename": "name-1618", + "stack": "cflinuxfs2" + } + } + ] +}` + +const listBuildpacksPayload2 = `{ + "total_results": 3, + "total_pages": 1, + "prev_url": "/v2/buildpacks", + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "c92b6f5f-d2a4-413a-b515-647d059723aa", + "url": "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name_1", + "position": 1, + "enabled": true, + "locked": false, + "filename": "name-1616", + "stack": "cflinuxfs2" + } + }, + { + "metadata": { + "guid": "4de2ac22-ef36-4d62-9698-5f2b426748a9", + "url": "/v2/buildpacks/4de2ac22-ef36-4d62-9698-5f2b426748a9", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name_2", + "position": 2, + "enabled": true, + "locked": false, + "filename": "name-1617", + "stack": "cflinuxfs2" + } + }, + { + "metadata": { + "guid": "79f16936-56f1-41d5-a4c4-f0e9a8877791", + "url": "/v2/buildpacks/79f16936-56f1-41d5-a4c4-f0e9a8877791", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name_3", + "position": 3, + "enabled": true, + "locked": false, + "filename": "name-1618", + "stack": "cflinuxfs2" + } + } + ] +}` + +const buildpackPayloadBackwardsCompat = `{ + "metadata": { + "guid": "c92b6f5f-d2a4-413a-b515-647d059723aa", + "url": "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name_1", + "position": 1, + "enabled": true, + "locked": false, + "filename": "name-1616" + } +}` + +const buildpackPayload = `{ + "metadata": { + "guid": "c92b6f5f-d2a4-413a-b515-647d059723aa", + "url": "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name_1", + "position": 1, + "enabled": true, + "locked": false, + "filename": "name-1616", + "stack": "cflinuxfs2" + } +}` + +const userByGUIDPayload = `{ + "metadata": { + "guid": "72ccf759-43aa-4954-903f-7d892c268e80", + "url": "/v2/users/72ccf759-43aa-4954-903f-7d892c268e80", + "created_at": "2017-11-03T01:33:31Z", + "updated_at": "2017-11-03T01:33:31Z" + }, + "entity": { + "admin": false, + "active": false, + "default_space_guid": null, + "username": "user@example.com", + "spaces_url": "/v2/users/72ccf759-43aa-4954-903f-7d892c268e80/spaces", + "organizations_url": "/v2/users/72ccf759-43aa-4954-903f-7d892c268e80/organizations", + "managed_organizations_url": "/v2/users/72ccf759-43aa-4954-903f-7d892c268e80/managed_organizations", + "billing_managed_organizations_url": "/v2/users/72ccf759-43aa-4954-903f-7d892c268e80/billing_managed_organizations", + "audited_organizations_url": "/v2/users/72ccf759-43aa-4954-903f-7d892c268e80/audited_organizations", + "managed_spaces_url": "/v2/users/72ccf759-43aa-4954-903f-7d892c268e80/managed_spaces", + "audited_spaces_url": "/v2/users/72ccf759-43aa-4954-903f-7d892c268e80/audited_spaces" + } +}` + +const listUsersPayload = `{ + "total_results": 8, + "total_pages": 2, + "prev_url": null, + "next_url": "/v2/usersPage2", + "resources": [ + { + "metadata": { + "guid": "ccec6d06-5f71-48a0-a4c5-c91a1d9f2fac", + "url": "/v2/users/ccec6d06-5f71-48a0-a4c5-c91a1d9f2fac", + "created_at": "2017-01-13T10:50:21Z", + "updated_at": "2017-01-27T12:20:08Z" + }, + "entity": { + "admin": false, + "active": false, + "default_space_guid": null, + "username": "testUser1", + "spaces_url": "/v2/users/ccec6d06-5f71-48a0-a4c5-c91a1d9f2fac/spaces", + "organizations_url": "/v2/users/ccec6d06-5f71-48a0-a4c5-c91a1d9f2fac/organizations", + "managed_organizations_url": "/v2/users/ccec6d06-5f71-48a0-a4c5-c91a1d9f2fac/managed_organizations", + "billing_managed_organizations_url": "/v2/users/ccec6d06-5f71-48a0-a4c5-c91a1d9f2fac/billing_managed_organizations", + "audited_organizations_url": "/v2/users/ccec6d06-5f71-48a0-a4c5-c91a1d9f2fac/audited_organizations", + "managed_spaces_url": "/v2/users/ccec6d06-5f71-48a0-a4c5-c91a1d9f2fac/managed_spaces", + "audited_spaces_url": "/v2/users/ccec6d06-5f71-48a0-a4c5-c91a1d9f2fac/audited_spaces" + } + }, + { + "metadata": { + "guid": "f97f5699-c920-4633-aa23-bd70f3db0808", + "url": "/v2/users/f97f5699-c920-4633-aa23-bd70f3db0808", + "created_at": "2017-01-17T15:08:45Z", + "updated_at": "2017-01-27T12:23:17Z" + }, + "entity": { + "admin": false, + "active": true, + "default_space_guid": null, + "username": "testUser2", + "spaces_url": "/v2/users/f97f5699-c920-4633-aa23-bd70f3db0808/spaces", + "organizations_url": "/v2/users/f97f5699-c920-4633-aa23-bd70f3db0808/organizations", + "managed_organizations_url": "/v2/users/f97f5699-c920-4633-aa23-bd70f3db0808/managed_organizations", + "billing_managed_organizations_url": "/v2/users/f97f5699-c920-4633-aa23-bd70f3db0808/billing_managed_organizations", + "audited_organizations_url": "/v2/users/f97f5699-c920-4633-aa23-bd70f3db0808/audited_organizations", + "managed_spaces_url": "/v2/users/f97f5699-c920-4633-aa23-bd70f3db0808/managed_spaces", + "audited_spaces_url": "/v2/users/f97f5699-c920-4633-aa23-bd70f3db0808/audited_spaces" + } + } + ] +}` + +const listUsersPayloadPage2 = `{ + "total_results": 8, + "total_pages": 2, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "cadd6389-fcf6-4928-84f0-6153556bf693", + "url": "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693", + "created_at": "2017-01-04T06:27:51Z", + "updated_at": "2017-01-27T12:21:19Z" + }, + "entity": { + "admin": false, + "active": false, + "default_space_guid": null, + "username": "testUser3", + "spaces_url": "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/spaces", + "organizations_url": "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/organizations", + "managed_organizations_url": "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/managed_organizations", + "billing_managed_organizations_url": "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/billing_managed_organizations", + "audited_organizations_url": "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/audited_organizations", + "managed_spaces_url": "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/managed_spaces", + "audited_spaces_url": "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/audited_spaces" + } + }, + { + "metadata": { + "guid": "79c854b0-c12a-41b7-8d3c-fdd6e116e385", + "url": "/v2/users/79c854b0-c12a-41b7-8d3c-fdd6e116e385", + "created_at": "2017-01-05T14:50:42Z", + "updated_at": "2017-01-27T12:23:17Z" + }, + "entity": { + "admin": false, + "active": false, + "default_space_guid": null, + "username": "testUser4", + "spaces_url": "/v2/users/79c854b0-c12a-41b7-8d3c-fdd6e116e385/spaces", + "organizations_url": "/v2/users/79c854b0-c12a-41b7-8d3c-fdd6e116e385/organizations", + "managed_organizations_url": "/v2/users/79c854b0-c12a-41b7-8d3c-fdd6e116e385/managed_organizations", + "billing_managed_organizations_url": "/v2/users/79c854b0-c12a-41b7-8d3c-fdd6e116e385/billing_managed_organizations", + "audited_organizations_url": "/v2/users/79c854b0-c12a-41b7-8d3c-fdd6e116e385/audited_organizations", + "managed_spaces_url": "/v2/users/79c854b0-c12a-41b7-8d3c-fdd6e116e385/managed_spaces", + "audited_spaces_url": "/v2/users/79c854b0-c12a-41b7-8d3c-fdd6e116e385/audited_spaces" + } + } + ] +}` + +const listUserSpacesPayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "9881c79e-d269-4a53-9d77-cb21b745356e", + "url": "/v2/spaces/9881c79e-d269-4a53-9d77-cb21b745356e", + "created_at": "2016-06-08T16:41:37Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "dev", + "organization_guid": "6a2a2d18-7620-43cf-a332-353824b431b2", + "space_quota_definition_guid": null, + "allow_ssh": true, + "organization_url": "/v2/organizations/6a2a2d18-7620-43cf-a332-353824b431b2", + "developers_url": "/v2/spaces/9881c79e-d269-4a53-9d77-cb21b745356e/developers", + "managers_url": "/v2/spaces/9881c79e-d269-4a53-9d77-cb21b745356e/managers", + "auditors_url": "/v2/spaces/9881c79e-d269-4a53-9d77-cb21b745356e/auditors", + "apps_url": "/v2/spaces/9881c79e-d269-4a53-9d77-cb21b745356e/apps", + "routes_url": "/v2/spaces/9881c79e-d269-4a53-9d77-cb21b745356e/routes", + "domains_url": "/v2/spaces/9881c79e-d269-4a53-9d77-cb21b745356e/domains", + "service_instances_url": "/v2/spaces/9881c79e-d269-4a53-9d77-cb21b745356e/service_instances", + "app_events_url": "/v2/spaces/9881c79e-d269-4a53-9d77-cb21b745356e/app_events", + "events_url": "/v2/spaces/9881c79e-d269-4a53-9d77-cb21b745356e/events", + "security_groups_url": "/v2/spaces/9881c79e-d269-4a53-9d77-cb21b745356e/security_groups", + "staging_security_groups_url": "/v2/spaces/9881c79e-d269-4a53-9d77-cb21b745356e/staging_security_groups" + } + } + ] +} +` + +const listUserOrgsPayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "9881c79e-d269-4a53-9d77-cb21b745356e", + "url": "/v2/organizations/9881c79e-d269-4a53-9d77-cb21b745356e", + "created_at": "2016-06-08T16:41:37Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "dev", + "billing_enabled": false, + "quota_definition_guid": "6a2a2d18-7620-43cf-a332-353824b431b2", + "status": "active", + "quota_definition_url": "/v2/quota_definitions/6a2a2d18-7620-43cf-a332-353824b431b2", + "spaces_url": "/v2/organizations/9881c79e-d269-4a53-9d77-cb21b745356e/spaces", + "domains_url": "/v2/organizations/9881c79e-d269-4a53-9d77-cb21b745356e/domains", + "private_domains_url": "/v2/organizations/9881c79e-d269-4a53-9d77-cb21b745356e/private_domains", + "users_url": "/v2/organizations/9881c79e-d269-4a53-9d77-cb21b745356e/users", + "managers_url": "/v2/organizations/9881c79e-d269-4a53-9d77-cb21b745356e/managers", + "billing_managers_url": "/v2/organizations/9881c79e-d269-4a53-9d77-cb21b745356e/billing_managers", + "auditors_url": "/v2/organizations/9881c79e-d269-4a53-9d77-cb21b745356e/auditors", + "app_events_url": "/v2/organizations/9881c79e-d269-4a53-9d77-cb21b745356e/app_events", + "space_quota_definitions_url": "/v2/organizations/9881c79e-d269-4a53-9d77-cb21b745356e/space_quota_definitions" + } + } + ] +} +` + +const createUserPayload = `{ + "metadata": { + "guid": "guid-cb24b36d-4656-468e-a50d-b53113ac6177", + "url": "/v2/users/guid-cb24b36d-4656-468e-a50d-b53113ac6177", + "created_at": "2016-06-08T16:41:37Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "admin": false, + "active": false, + "default_space_guid": null, + "spaces_url": "/v2/users/guid-cb24b36d-4656-468e-a50d-b53113ac6177/spaces", + "organizations_url": "/v2/users/guid-cb24b36d-4656-468e-a50d-b53113ac6177/organizations", + "managed_organizations_url": "/v2/users/guid-cb24b36d-4656-468e-a50d-b53113ac6177/managed_organizations", + "billing_managed_organizations_url": "/v2/users/guid-cb24b36d-4656-468e-a50d-b53113ac6177/billing_managed_organizations", + "audited_organizations_url": "/v2/users/guid-cb24b36d-4656-468e-a50d-b53113ac6177/audited_organizations", + "managed_spaces_url": "/v2/users/guid-cb24b36d-4656-468e-a50d-b53113ac6177/managed_spaces", + "audited_spaces_url": "/v2/users/guid-cb24b36d-4656-468e-a50d-b53113ac6177/audited_spaces" + } +}` + +const createIsolationSegmentPayload = `{ + "guid": "323f211e-fea3-4161-9bd1-615392327913", + "name": "TheKittenIsTheShark", + "created_at": "2016-10-19T20:25:04Z", + "updated_at": "2016-11-08T16:41:26Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/323f211e-fea3-4161-9bd1-615392327913" + }, + "spaces": { + "href": "https://api.example.org/v3/isolation_segments/323f211e-fea3-4161-9bd1-615392327913/relationships/spaces" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/323f211e-fea3-4161-9bd1-615392327913/relationships/organizations" + } + } +}` + +const listV3DomainsPayload = ` + { + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/domains?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/domains?page=2&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "3a5d3d89-3f89-4f05-8188-8a2b298c79d5", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "name": "test-domain.com", + "internal": false, + "metadata": { + "labels": { }, + "annotations": { } + }, + "relationships": { + "organization": { + "data": { "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" } + }, + "shared_organizations": { + "data": [ + {"guid": "404f3d89-3f89-6z72-8188-751b298d88d5"}, + {"guid": "416d3d89-3f89-8h67-2189-123b298d3592"} + ] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/3a3f3d89-3f89-4f05-8188-751b298c79d5" + }, + "route_reservations": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/route_reservations" + }, + "shared_organizations": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/relationships/shared_organizations" + } + } + } + ] +}` + +const listV3OrganizationsPayload = `{ + "pagination": { + "total_results": 2, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/organizations?page=1&per_page=1" + }, + "last": { + "href": "https://api.example.org/v3/organizations?page=2&per_page=1" + }, + "next": { + "href": "https://api.example.org/v3/organizations?page=2&per_page=1" + }, + "previous": null + }, + "resources": [ + { + "guid": "org-guid", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-org-1", + "relationships": { + "quota": { + "data": { + "guid": "quota-guid" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organizations/org-guid" + }, + "domains": { + "href": "https://api.example.org/v3/organizations/org-guid/domains" + }, + "default_domain": { + "href": "https://api.example.org/v3/organizations/org-guid/domains/default" + }, + "quota": { + "href": "https://api.example.org/v3/organization_quotas/quota-guid" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +}` + +const listV3OrganizationsPayloadPage2 = `{ + "pagination": { + "total_results": 2, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/organizations?page=1&per_page=1" + }, + "last": { + "href": "https://api.example.org/v3/organizations?page=2&per_page=1" + }, + "next": null, + "previous": { + "href": "https://api.example.org/v3/organizations?page=2&per_page=1" + } + }, + "resources": [ + { + "guid": "org-guid-2", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-org-2", + "relationships": { + "quota": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organizations/org-guid-2" + }, + "domains": { + "href": "https://api.example.org/v3/organizations/org-guid-2/domains" + }, + "default_domain": { + "href": "https://api.example.org/v3/organizations/org-guid-2/domains/default" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +}` + +const updateV3OrganizationPayload = ` +{ + "guid": "org-guid", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-org", + "suspended": false, + "relationships": { + "quota": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organizations/org-guid" + }, + "domains": { + "href": "https://api.example.org/v3/organizations/org-guid/domains" + }, + "default_domain": { + "href": "https://api.example.org/v3/organizations/org-guid/domains/default" + } + }, + "metadata": { + "labels": { + "ORG_KEY": "org_value" + }, + "annotations": {} + } +}` + +const getV3OrganizationPayload = `{ + "guid": "org-guid", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-org", + "relationships": { + "quota": { + "data": { + "guid": "quota-guid" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organizations/org-guid" + }, + "domains": { + "href": "https://api.example.org/v3/organizations/org-guid/domains" + }, + "default_domain": { + "href": "https://api.example.org/v3/organizations/org-guid/domains/default" + } + }, + "metadata": { + "labels": { + "ORG_KEY": "org_value" + }, + "annotations": {} + } +}` + +const createV3OrganizationPayload = `{ + "guid": "org-guid", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-org", + "relationships": { + "quota": { + "data": { + "guid": "quota-guid" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organizations/org-guid" + }, + "domains": { + "href": "https://api.example.org/v3/organizations/org-guid/domains" + }, + "default_domain": { + "href": "https://api.example.org/v3/organizations/org-guid/domains/default" + } + }, + "metadata": { + "labels": { + "ORG_KEY": "org_value" + }, + "annotations": {} + } +}` + +const listV3SpacesPayload = `{ + "pagination": { + "total_results": 2, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/spaces?page=1&per_page=1" + }, + "last": { + "href": "https://api.example.org/v3/spaces?page=2&per_page=1" + }, + "next": { + "href": "https://api.example.org/v3/spaces?page=2&per_page=1" + }, + "previous": null + }, + "resources": [ + { + "guid": "space-guid", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-space-1", + "relationships": { + "organization": { + "data": { + "guid": "org-guid" + } + }, + "quota": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/spaces/space-guid" + }, + "features": { + "href": "https://api.example.org/v3/spaces/space-guid/features" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/org-guid" + }, + "apply_manifest": { + "href": "https://api.example.org/v3/spaces/space-guid/actions/apply_manifest", + "method": "POST" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +}` + +const listV3SpacesPayloadPage2 = `{ + "pagination": { + "total_results": 2, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/spaces?page=1&per_page=1" + }, + "last": { + "href": "https://api.example.org/v3/spaces?page=2&per_page=1" + }, + "next": null, + "previous": { + "href": "https://api.example.org/v3/spaces?page=2&per_page=1" + } + }, + "resources": [ + { + "guid": "space-guid-2", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-space-2", + "relationships": { + "organization": { + "data": { + "guid": "org-guid" + } + }, + "quota": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/spaces/space-guid-2" + }, + "features": { + "href": "https://api.example.org/v3/spaces/space-guid-2/features" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/org-guid" + }, + "apply_manifest": { + "href": "https://api.example.org/v3/spaces/space-guid-2/actions/apply_manifest", + "method": "POST" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +}` + +const listV3SpaceUsersPayload = `{ + "pagination": { + "total_results": 2, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/spaces/space-guid/users?page=1&per_page=1" + }, + "last": { + "href": "https://api.example.org/v3/spaces/space-guid/users?page=2&per_page=1" + }, + "next": { + "href": "https://api.example.org/v3/spaces/space-guid/users?page=2&per_page=1" + }, + "previous": null + }, + "resources": [ + { + "guid": "10a93b89-3f89-4f05-7238-8a2b123c79l9", + "created_at": "2019-03-08T01:06:18Z", + "updated_at": "2019-03-08T01:06:18Z", + "username": "some-name-1", + "presentation_name": "some-name-1", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations":{} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/10a93b89-3f89-4f05-7238-8a2b123c79l9" + } + } + } + ] +}` + +const listV3SpaceUsersPayloadPage2 = `{ + "pagination": { + "total_results": 2, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/spaces/space-guid/users?page=1&per_page=1" + }, + "last": { + "href": "https://api.example.org/v3/spaces/space-guid/users?page=2&per_page=1" + }, + "next": null, + "previous": { + "href": "https://api.example.org/v3/spaces/space-guid/users?page=1&per_page=1" + } + }, + "resources": [ + { + "guid": "9da93b89-3f89-4f05-7238-8a2b123c79l9", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "username": "some-name-2", + "presentation_name": "some-name-2", + "origin": "ldap", + "metadata": { + "labels": {}, + "annotations":{} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/9da93b89-3f89-4f05-7238-8a2b123c79l9" + } + } + } + ] +}` + +const updateV3SpacePayload = ` +{ + "guid": "space-guid", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-space", + "relationships": { + "organization": { + "data": { + "guid": "org-guid" + } + }, + "quota": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/spaces/space-guid" + }, + "features": { + "href": "https://api.example.org/v3/spaces/space-guid/features" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/org-guid" + }, + "apply_manifest": { + "href": "https://api.example.org/v3/spaces/space-guid/actions/apply_manifest", + "method": "POST" + } + }, + "metadata": { + "labels": { + "SPACE_KEY": "space_value" + }, + "annotations": {} + } +}` + +const getV3SpacePayload = `{ + "guid": "space-guid", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-space", + "relationships": { + "organization": { + "data": { + "guid": "org-guid" + } + }, + "quota": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/spaces/space-guid" + }, + "features": { + "href": "https://api.example.org/v3/spaces/space-guid/features" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/org-guid" + }, + "apply_manifest": { + "href": "https://api.example.org/v3/spaces/space-guid/actions/apply_manifest", + "method": "POST" + } + }, + "metadata": { + "labels": { + "SPACE_KEY": "space_value" + }, + "annotations": {} + } +}` + +const createV3RoutePayload = `{ + "guid": "cbad697f-cac1-48f4-9017-ac08f39dfb31", + "host": "a-hostname", + "path": "/some_path", + "url": "a-hostname.a-domain.com/some_path", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z", + "metadata": { + "labels": { "key": "value" }, + "annotations": { "note": "detailed information" } + }, + "relationships": { + "space": { + "data": { + "guid": "885a8cb3-c07b-4856-b448-eeb10bf36236" + } + }, + "domain": { + "data": { + "guid": "0b5f3633-194c-42d2-9408-972366617e0e" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31" + }, + "space": { + "href": "https://api.example.org/v3/spaces/885a8cb3-c07b-4856-b448-eeb10bf36236" + }, + "domain": { + "href": "https://api.example.org/v3/domains/0b5f3633-194c-42d2-9408-972366617e0e" + }, + "destinations": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31/destinations" + } + } +}` + +const createV3SpaceRolePayload = `{ + "guid": "b9f59ab2-2b09-438e-bebb-30e8704ffb89", + "created_at": "2022-05-31T20:14:13Z", + "updated_at": "2022-05-31T20:14:13Z", + "type": "space_supporter", + "relationships": { + "user": { + "data": { + "guid": "c4958204-6b65-43ea-832b-e4c57aea6641" + } + }, + "space": { + "data": { + "guid": "b40a40c8-58b7-49a0-b47d-9d6fe5d72905" + } + }, + "organization": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/b9f59ab2-2b09-438e-bebb-30e8704ffb89" + }, + "user": { + "href": "https://api.example.org/v3/users/c4958204-6b65-43ea-832b-e4c57aea6641" + }, + "space": { + "href": "https://api.example.org/v3/spaces/b40a40c8-58b7-49a0-b47d-9d6fe5d72905" + } + } +}` + +const createV3OrganizationRolePayload = `{ + "guid": "21cbfaeb-bff7-4cfd-a7a9-6c13ec76f246", + "created_at": "2022-05-31T18:19:42Z", + "updated_at": "2022-05-31T18:19:42Z", + "type": "organization_user", + "relationships": { + "user": { + "data": { + "guid": "ac2e02c9-2c5c-4712-a620-a68449d263c3" + } + }, + "organization": { + "data": { + "guid": "fa8a8346-0d92-4729-870c-77ee1934f973" + } + }, + "space": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/21cbfaeb-bff7-4cfd-a7a9-6c13ec76f246" + }, + "user": { + "href": "https://api.example.org/v3/users/ac2e02c9-2c5c-4712-a620-a68449d263c3" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/fa8a8346-0d92-4729-870c-77ee1934f973" + } + } +}` + +const createV3SpacePayload = `{ + "guid": "space-guid", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-space", + "relationships": { + "organization": { + "data": { + "guid": "org-guid" + } + }, + "quota": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/spaces/space-guid" + }, + "features": { + "href": "https://api.example.org/v3/spaces/space-guid/features" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/org-guid" + }, + "apply_manifest": { + "href": "https://api.example.org/v3/spaces/space-guid/actions/apply_manifest", + "method": "POST" + } + }, + "metadata": { + "labels": { + "SPACE_KEY": "space_value" + }, + "annotations": {} + } +}` + +const getV3AppPayload = `{ + "guid": "1cb006ee-fb05-47e1-b541-c34179ddc446", + "name": "my_app", + "state": "STOPPED", + "created_at": "2016-03-17T21:41:30Z", + "updated_at": "2016-06-08T16:41:26Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": ["java_buildpack"], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/processes" + }, + "route_mappings": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/route_mappings" + }, + "packages": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/packages" + }, + "environment_variables": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/environment_variables" + }, + "current_droplet": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets/current" + }, + "droplets": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets" + }, + "tasks": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/tasks" + }, + "start": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/start", + "method": "POST" + }, + "stop": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": {}, + "annotations": { + "contacts": "Bill tel(1111111) email(bill@fixme), Bob tel(222222) pager(3333333#555) email(bob@fixme)" + } + } +}` + +const getV3AppEnvPayload = `{ + "staging_env_json": { + "GEM_CACHE": "http://gem-cache.example.org" + }, + "running_env_json": { + "HTTP_PROXY": "http://proxy.example.org" + }, + "environment_variables": { + "RAILS_ENV": "production" + }, + "system_env_json": { + "VCAP_SERVICES": { + "mysql": [ + { + "name": "db-for-my-app", + "label": "mysql", + "tags": ["relational", "sql"], + "plan": "xlarge", + "credentials": { + "username": "user", + "password": "top-secret" + }, + "syslog_drain_url": "https://syslog.example.org/drain", + "provider": null + } + ] + } + }, + "application_env_json": { + "VCAP_APPLICATION": { + "limits": { + "fds": 16384 + }, + "application_name": "my_app", + "application_uris": [ "my_app.example.org" ], + "name": "my_app", + "space_name": "my_space", + "space_id": "2f35885d-0c9d-4423-83ad-fd05066f8576", + "uris": [ "my_app.example.org" ], + "users": null + } + } +}` + +const createV3AppPayload = `{ + "guid": "app-guid", + "name": "my-app", + "state": "STOPPED", + "created_at": "2016-03-17T21:41:30Z", + "updated_at": "2016-06-08T16:41:26Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": ["java_buildpack"], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "space-guid" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/apps/app-guid" + }, + "space": { + "href": "https://api.example.org/v3/spaces/space-guid" + }, + "processes": { + "href": "https://api.example.org/v3/apps/app-guid/processes" + }, + "route_mappings": { + "href": "https://api.example.org/v3/apps/app-guid/route_mappings" + }, + "packages": { + "href": "https://api.example.org/v3/apps/app-guid/packages" + }, + "environment_variables": { + "href": "https://api.example.org/v3/apps/app-guid/environment_variables" + }, + "current_droplet": { + "href": "https://api.example.org/v3/apps/app-guid/droplets/current" + }, + "droplets": { + "href": "https://api.example.org/v3/apps/app-guid/droplets" + }, + "tasks": { + "href": "https://api.example.org/v3/apps/app-guid/tasks" + }, + "start": { + "href": "https://api.example.org/v3/apps/app-guid/actions/start", + "method": "POST" + }, + "stop": { + "href": "https://api.example.org/v3/apps/app-guid/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } +}` + +const createV3BuildPayload = `{ + "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16", + "created_at": "2016-03-28T23:39:34Z", + "updated_at": "2016-06-08T16:41:26Z", + "created_by": { + "guid": "3cb4e243-bed4-49d5-8739-f8b45abdec1c", + "name": "bill", + "email": "bill@example.com" + }, + "state": "STAGING", + "error": null, + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": [ "ruby_buildpack" ], + "stack": "cflinuxfs2" + } + }, + "package": { + "guid": "8e4da443-f255-499c-8b47-b3729b5b7432" + }, + "droplet": null, + "metadata": { + "labels": { }, + "annotations": { } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/builds/585bc3c1-3743-497d-88b0-403ad6b56d16" + }, + "app": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" + } + } +} +` + +const listIsolationSegmentsPayload = `{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/isolation_segments?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/isolation_segments?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "033b4c58-12bb-499a-b05d-4b6fc9e2993b", + "name": "shared", + "created_at": "2017-04-02T11:22:04Z", + "updated_at": "2017-04-02T11:22:04Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/033b4c58-12bb-499a-b05d-4b6fc9e2993b" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/033b4c58-12bb-499a-b05d-4b6fc9e2993b/organizations" + }, + "spaces": { + "href": "https://api.example.org/v3/isolation_segments/033b4c58-12bb-499a-b05d-4b6fc9e2993b/relationships/spaces" + } + } + }, + { + "guid": "23d0baf4-9d3c-44d8-b2dc-1767bcdad1e0", + "name": "my_segment", + "created_at": "2017-04-07T11:20:16Z", + "updated_at": "2017-04-07T11:20:16Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/23d0baf4-9d3c-44d8-b2dc-1767bcdad1e0" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/23d0baf4-9d3c-44d8-b2dc-1767bcdad1e0/organizations" + }, + "spaces": { + "href": "https://api.example.org/v3/isolation_segments/23d0baf4-9d3c-44d8-b2dc-1767bcdad1e0/relationships/spaces" + } + } + } + ] +}` + +const listIsolationSegmentsPayloadPage1 = `{ + "pagination": { + "total_results": 4, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/isolation_segments?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/isolation_segments?page=2&per_page=2" + }, + "next": { + "href": "https://api.example.org/v3/isolation_segments?page=2&per_page=2" + }, + "previous": null + }, + "resources": [ + { + "guid": "033b4c58-12bb-499a-b05d-4b6fc9e2993b", + "name": "shared", + "created_at": "2017-04-02T11:22:04Z", + "updated_at": "2017-04-02T11:22:04Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/033b4c58-12bb-499a-b05d-4b6fc9e2993b" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/033b4c58-12bb-499a-b05d-4b6fc9e2993b/organizations" + }, + "spaces": { + "href": "https://api.example.org/v3/isolation_segments/033b4c58-12bb-499a-b05d-4b6fc9e2993b/relationships/spaces" + } + } + }, + { + "guid": "23d0baf4-9d3c-44d8-b2dc-1767bcdad1e0", + "name": "my_segment", + "created_at": "2017-04-07T11:20:16Z", + "updated_at": "2017-04-07T11:20:16Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/23d0baf4-9d3c-44d8-b2dc-1767bcdad1e0" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/23d0baf4-9d3c-44d8-b2dc-1767bcdad1e0/organizations" + }, + "spaces": { + "href": "https://api.example.org/v3/isolation_segments/23d0baf4-9d3c-44d8-b2dc-1767bcdad1e0/relationships/spaces" + } + } + } + ] +}` + +const listIsolationSegmentsPayloadPage2 = `{ + "pagination": { + "total_results": 4, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/isolation_segments?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/isolation_segments?page=2&per_page=2" + }, + "next": null, + "previous": { + "href": "https://api.example.org/v3/isolation_segments?page=1&per_page=2" + } + }, + "resources": [ + { + "guid": "abcdefg12-12bb-499a-b05d-4b6fc9e2993b", + "name": "shared1", + "created_at": "2017-05-02T11:22:04Z", + "updated_at": "2017-05-02T11:22:04Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/abcdefg12-12bb-499a-b05d-4b6fc9e2993b" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/abcdefg12-12bb-499a-b05d-4b6fc9e2993b/organizations" + }, + "spaces": { + "href": "https://api.example.org/v3/isolation_segments/abcdefg12-12bb-499a-b05d-4b6fc9e2993b/relationships/spaces" + } + } + }, + { + "guid": "abcdef123-9d3c-44d8-b2dc-1767bcdad1e0", + "name": "my_segment1", + "created_at": "2017-05-07T11:20:16Z", + "updated_at": "2017-05-07T11:20:16Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/abcdef123-9d3c-44d8-b2dc-1767bcdad1e0" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/abcdef123-9d3c-44d8-b2dc-1767bcdad1e0/organizations" + }, + "spaces": { + "href": "https://api.example.org/v3/isolation_segments/abcdef123-9d3c-44d8-b2dc-1767bcdad1e0/relationships/spaces" + } + } + } + ] +}` + +const startV3AppPayload = `{ + "guid": "1cb006ee-fb05-47e1-b541-c34179ddc446", + "name": "my_app", + "state": "STARTED", + "created_at": "2016-03-17T21:41:30Z", + "updated_at": "2016-03-18T11:32:30Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": ["java_buildpack"], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/processes" + }, + "route_mappings": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/route_mappings" + }, + "packages": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/packages" + }, + "environment_variables": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/environment_variables" + }, + "current_droplet": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets/current" + }, + "droplets": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets" + }, + "tasks": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/tasks" + }, + "start": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/start", + "method": "POST" + }, + "stop": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } +}` + +const updateV3AppPayload = `{ + "guid": "1cb006ee-fb05-47e1-b541-c34179ddc446", + "name": "my_app", + "state": "STARTED", + "created_at": "2016-03-17T21:41:30Z", + "updated_at": "2016-03-18T11:32:30Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": ["java_buildpack"], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/processes" + }, + "route_mappings": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/route_mappings" + }, + "packages": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/packages" + }, + "environment_variables": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/environment_variables" + }, + "current_droplet": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets/current" + }, + "droplets": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets" + }, + "tasks": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/tasks" + }, + "start": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/start", + "method": "POST" + }, + "stop": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": { + "environment": "production", + "internet-facing": "false" + }, + "annotations": {} + } +}` + +const currentDropletV3AppPayload = `{ + "data": { + "guid": "9d8e007c-ce52-4ea7-8a57-f2825d2c6b39" + }, + "links": { + "self": { + "href": "https://api.example.org/v3/apps/d4c91047-7b29-4fda-b7f9-04033e5c9c9f/relationships/current_droplet" + }, + "related": { + "href": "https://api.example.org/v3/apps/d4c91047-7b29-4fda-b7f9-04033e5c9c9f/droplets/current" + } + } +}` + +const getV3CurrentAppDropletPayload = `{ + "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16", + "state": "STAGED", + "error": null, + "lifecycle": { + "type": "buildpack", + "data": {} + }, + "execution_metadata": "", + "process_types": { + "rake": "bundle exec rake", + "web": "bundle exec rackup config.ru -p $PORT" + }, + "checksum": { + "type": "sha256", + "value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "buildpacks": [ + { + "name": "ruby_buildpack", + "detect_output": "ruby 1.6.14", + "version": "1.1.1.", + "buildpack_name": "ruby" + } + ], + "stack": "cflinuxfs3", + "image": null, + "created_at": "2016-03-28T23:39:34Z", + "updated_at": "2016-03-28T23:39:47Z", + "relationships": { + "app": { + "data": { + "guid": "7b34f1cf-7e73-428a-bb5a-8a17a8058396" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16" + }, + "package": { + "href": "https://api.example.org/v3/packages/8222f76a-9e09-4360-b3aa-1ed329945e92" + }, + "app": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" + }, + "assign_current_droplet": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396/relationships/current_droplet", + "method": "PATCH" + }, + "download": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16/download" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } +}` + +const listV3AppsPayload = `{ + "pagination": { + "total_results": 2, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/apps?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/apps?page=2&per_page=2" + }, + "next": { + "href": "https://api.example.org/v3/apps?page=2&per_page=2" + }, + "previous": null + }, + "resources": [ + { + "guid": "1cb006ee-fb05-47e1-b541-c34179ddc446", + "name": "my_app", + "state": "STARTED", + "created_at": "2016-03-17T21:41:30Z", + "updated_at": "2016-03-18T11:32:30Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": ["java_buildpack"], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/processes" + }, + "route_mappings": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/route_mappings" + }, + "packages": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/packages" + }, + "environment_variables": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/environment_variables" + }, + "current_droplet": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets/current" + }, + "droplets": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets" + }, + "tasks": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/tasks" + }, + "start": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/start", + "method": "POST" + }, + "stop": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +}` + +const listV3AppsPayloadPage2 = `{ + "pagination": { + "total_results": 2, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/apps?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/apps?page=2&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "02b4ec9b-94c7-4468-9c23-4e906191a0f8", + "name": "my_app2", + "state": "STOPPED", + "created_at": "1970-01-01T00:00:02Z", + "updated_at": "2016-06-08T16:41:26Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": ["ruby_buildpack", "staticfile_buildpack"], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "https://api.example.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/processes" + }, + "route_mappings": { + "href": "https://api.example.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/route_mappings" + }, + "packages": { + "href": "https://api.example.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/packages" + }, + "environment_variables": { + "href": "https://api.example.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/environment_variables" + }, + "current_droplet": { + "href": "https://api.example.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/droplets/current" + }, + "droplets": { + "href": "https://api.example.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/droplets" + }, + "tasks": { + "href": "https://api.example.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/tasks" + }, + "start": { + "href": "https://api.example.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/actions/start", + "method": "POST" + }, + "stop": { + "href": "https://api.example.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +}` + +const listV3ServiceInstancesPayload = `{ + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/service_instances?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/service_instances?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "85ccdcad-d725-4109-bca4-fd6ba062b5c8", + "created_at": "2017-11-17T13:54:21Z", + "updated_at": "2017-11-17T13:54:21Z", + "name": "my_service_instance", + "relationships": { + "space": { + "data": { + "guid": "ae0031f9-dd49-461c-a945-df40e77c39cb" + } + } + }, + "metadata": { + "labels": { }, + "annotations": { } + }, + "links": { + "space": { + "href": "https://api.example.org/v3/spaces/ae0031f9-dd49-461c-a945-df40e77c39cb" + } + } + } + ] +}` + +const listV3RoutesPayload = `{ + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/routes?page=1&per_page=1" + }, + "last": { + "href": "https://api.example.org/v3/routes?page=1&per_page=1" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "cbad697f-cac1-48f4-9017-ac08f39dfb31", + "host": "a-hostname", + "path": "/some_path", + "url": "a-hostname.a-domain.com/some_path", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z", + "metadata": { + "labels": {}, + "annotations": {} + }, + "relationships": { + "space": { + "data": { + "guid": "885a8cb3-c07b-4856-b448-eeb10bf36236" + } + }, + "domain": { + "data": { + "guid": "0b5f3633-194c-42d2-9408-972366617e0e" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31" + }, + "space": { + "href": "https://api.example.org/v3/spaces/885a8cb3-c07b-4856-b448-eeb10bf36236" + }, + "domain": { + "href": "https://api.example.org/v3/domains/0b5f3633-194c-42d2-9408-972366617e0e" + }, + "destinations": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31/destinations" + } + } + } + ] +}` + +const listPackagesForV3AppPayloadPage1 = `{ + "pagination": { + "total_results": 2, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69/packages?page=1&per_page=1" + }, + "last": { + "href": "https://api.example.org/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69/packages?page=2&per_page=1" + }, + "next": { + "href": "https://api.example.org/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69/packages?page=2&per_page=1" + }, + "previous": null + }, + "resources": [ + { + "guid": "752edab0-2147-4f58-9c25-cd72ad8c3561", + "type": "bits", + "data": { + "error": null, + "checksum": { + "type": "sha256", + "value": null + } + }, + "state": "READY", + "created_at": "2016-03-17T21:41:09Z", + "updated_at": "2016-06-08T16:41:26Z", + "links": { + "self": { + "href": "https://api.example.org/v3/packages/752edab0-2147-4f58-9c25-cd72ad8c3561" + }, + "upload": { + "href": "https://api.example.org/v3/packages/752edab0-2147-4f58-9c25-cd72ad8c3561/upload", + "method": "POST" + }, + "download": { + "href": "https://api.example.org/v3/packages/752edab0-2147-4f58-9c25-cd72ad8c3561/download", + "method": "GET" + }, + "app": { + "href": "https://api.example.org/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +}` + +const listPackagesForV3AppPayloadPage2 = `{ + "pagination": { + "total_results": 2, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69/packages?page=1&per_page=1" + }, + "last": { + "href": "https://api.example.org/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69/packages?page=2&per_page=1" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "2345ab-2147-4f58-9c25-cd72ad8c3561", + "type": "bits", + "data": { + "error": null, + "checksum": { + "type": "sha256", + "value": null + } + }, + "state": "READY", + "created_at": "2016-03-17T21:41:09Z", + "updated_at": "2016-06-08T16:41:26Z", + "links": { + "self": { + "href": "https://api.example.org/v3/packages/2345ab-2147-4f58-9c25-cd72ad8c3561" + }, + "upload": { + "href": "https://api.example.org/v3/packages/2345ab-2147-4f58-9c25-cd72ad8c3561/upload", + "method": "POST" + }, + "download": { + "href": "https://api.example.org/v3/packages/2345ab-2147-4f58-9c25-cd72ad8c3561/download", + "method": "GET" + }, + "app": { + "href": "https://api.example.org/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +}` + +const copyPackageV3Payload = `{ + "guid": "fec72fc1-e453-4463-a86d-5df426f337a3", + "type": "docker", + "data": { + "image": "http://awesome-sauce.example.org" + }, + "state": "COPYING", + "created_at": "2016-03-17T21:41:09Z", + "updated_at": "2016-06-08T16:41:26Z", + "links": { + "self": { + "href": "https://api.example.org/v3/packages/fec72fc1-e453-4463-a86d-5df426f337a3" + }, + "app": { + "href": "https://api.example.org/v3/apps/36208a68-562d-4f51-94ea-28bd8553a271" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } +}` + +const getServiceKeyByGuidPayload = `{ + "metadata": { + "guid": "6ad2cc9b-1996-49a3-9538-dfc0da3b1f32", + "url": "/v2/service_keys/6ad2cc9b-1996-49a3-9538-dfc0da3b1f32", + "created_at": "2016-06-08T16:41:23Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-140", + "service_instance_guid": "ca567b3d-e142-4139-94e3-1e0c010ba728", + "credentials": { + "creds-key-7": "creds-val-7" + }, + "service_instance_url": "/v2/service_instances/ca567b3d-e142-4139-94e3-1e0c010ba728", + "service_key_parameters_url": "/v2/service_keys/6ad2cc9b-1996-49a3-9538-dfc0da3b1f32/parameters" + } +} +` + +const listServiceKeysPayloadPage1 = `{ + "total_results": 4, + "total_pages": 2, + "prev_url": null, + "next_url": "/v2/service_keys2", + "resources": [ + { + "metadata": { + "guid": "3b933598-64ed-4613-a0f5-b7e8c0379368", + "url": "/v2/service_keys/3b933598-64ed-4613-a0f5-b7e8c0379368", + "created_at": "2016-08-01T15:17:35Z", + "updated_at": "2016-08-01T15:17:35Z" + }, + "entity": { + "name": "RedisMonitoringKey", + "service_instance_guid": "ad98f310-a3a0-47aa-9116-f8295d41a9b2", + "credentials": { + "host": "10.10.10.10", + "password": "some-password", + "port": 12345 + }, + "service_instance_url": "/v2/service_instances/ad98f310-a3a0-47aa-9116-f8295d41a9b2" + } + }, + { + "metadata": { + "guid": "8be3911b-c621-4467-8866-f8b924aaee57", + "url": "/v2/service_keys/8be3911b-c621-4467-8866-f8b924aaee57", + "created_at": "2017-05-16T12:14:46Z", + "updated_at": "2017-05-16T12:14:46Z" + }, + "entity": { + "name": "test01_key", + "service_instance_guid": "ecf26687-e176-4784-b181-b3c942fecb62", + "credentials": { + "jms": "nhp://100.100.100.100:9008", + "js_uri": "http://100.100.100.100:9008", + "amqp": "amqp://100.100.100.100:9008", + "nhp": "nhp://100.100.100.100:9009", + "mqtt": "tcp://100.100.100.100:9008", + "name": "fcf26687-e176-4784-b181-b3c942fecb62", + "nsp": "nsp://100.100.100.100:9008", + "userid": "cfu-9be3911b-c621-4467-8866-f8b924aaee57", + "uri": "nhp://100.100.100.100:9008", + "uriInfos": [ + { + "host": "100.100.100.100", + "port": 9008 + } + ] + }, + "service_instance_url": "/v2/service_instances/fcf26687-e176-4784-b181-b3c942fecb62" + } + } + ] +}` + +const listServiceKeysPayloadPage2 = `{ + "total_results": 4, + "total_pages": 2, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "3b933598-64ed-4613-a0f5-b7e8c0379368", + "url": "/v2/service_keys/3b933598-64ed-4613-a0f5-b7e8c0379368", + "created_at": "2016-08-01T15:17:35Z", + "updated_at": "2016-08-01T15:17:35Z" + }, + "entity": { + "name": "RedisMonitoringKey", + "service_instance_guid": "ad98f310-a3a0-47aa-9116-f8295d41a9b2", + "credentials": { + "host": "10.10.10.10", + "password": "some-password", + "port": 12345 + }, + "service_instance_url": "/v2/service_instances/ad98f310-a3a0-47aa-9116-f8295d41a9b2" + } + }, + { + "metadata": { + "guid": "8be3911b-c621-4467-8866-f8b924aaee57", + "url": "/v2/service_keys/8be3911b-c621-4467-8866-f8b924aaee57", + "created_at": "2017-05-16T12:14:46Z", + "updated_at": "2017-05-16T12:14:46Z" + }, + "entity": { + "name": "test01_key", + "service_instance_guid": "ecf26687-e176-4784-b181-b3c942fecb62", + "credentials": { + "jms": "nhp://100.100.100.100:9008", + "js_uri": "http://100.100.100.100:9008", + "amqp": "amqp://100.100.100.100:9008", + "nhp": "nhp://100.100.100.100:9009", + "mqtt": "tcp://100.100.100.100:9008", + "name": "fcf26687-e176-4784-b181-b3c942fecb62", + "nsp": "nsp://100.100.100.100:9008", + "userid": "cfu-9be3911b-c621-4467-8866-f8b924aaee57", + "uri": "nhp://100.100.100.100:9008", + "uriInfos": [ + { + "host": "100.100.100.100", + "port": 9008 + } + ] + }, + "service_instance_url": "/v2/service_instances/fcf26687-e176-4784-b181-b3c942fecb62" + } + } + ] +}` + +const getServiceKeyPayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "8be3911b-c621-4467-8866-f8b924aaee57", + "url": "/v2/service_keys/8be3911b-c621-4467-8866-f8b924aaee57", + "created_at": "2017-05-16T12:14:46Z", + "updated_at": "2017-05-16T12:14:46Z" + }, + "entity": { + "name": "test01_key", + "service_instance_guid": "ecf26687-e176-4784-b181-b3c942fecb62", + "credentials": { + "jms": "nhp://100.100.100.100:9008", + "js_uri": "http://100.100.100.100:9008", + "amqp": "amqp://100.100.100.100:9008", + "nhp": "nhp://100.100.100.100:9009", + "mqtt": "tcp://100.100.100.100:9008", + "name": "ecf26687-e176-4784-b181-b3c942fecb62", + "nsp": "nsp://100.100.100.100:9008", + "userid": "cfu-9be3911b-c621-4467-8866-f8b924aaee57", + "uri": "nhp://100.100.100.100:9008", + "uriInfos": [ + { + "host": "100.100.100.100", + "port": 9008 + } + ] + }, + "service_instance_url": "/v2/service_instances/ecf26687-e176-4784-b181-b3c942fecb62" + } + } + ] +}` + +const getServiceKeysPayload = `{ + "total_results": 2, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "8be3911b-c621-4467-8866-f8b924aaee57", + "url": "/v2/service_keys/8be3911b-c621-4467-8866-f8b924aaee57", + "created_at": "2017-05-16T12:14:46Z", + "updated_at": "2017-05-16T12:14:46Z" + }, + "entity": { + "name": "test01_key", + "service_instance_guid": "ecf26687-e176-4784-b181-b3c942fecb62", + "credentials": { + "jms": "nhp://100.100.100.100:9008", + "js_uri": "http://100.100.100.100:9008", + "amqp": "amqp://100.100.100.100:9008", + "nhp": "nhp://100.100.100.100:9009", + "mqtt": "tcp://100.100.100.100:9008", + "name": "ecf26687-e176-4784-b181-b3c942fecb62", + "nsp": "nsp://100.100.100.100:9008", + "userid": "cfu-9be3911b-c621-4467-8866-f8b924aaee57", + "uri": "nhp://100.100.100.100:9008", + "uriInfos": [ + { + "host": "100.100.100.100", + "port": 9008 + } + ] + }, + "service_instance_url": "/v2/service_instances/ecf26687-e176-4784-b181-b3c942fecb62" + } + }, + { + "metadata": { + "guid": "9361b5dc-9262-4a83-8969-9b3469211884", + "url": "/v2/service_keys/9361b5dc-9262-4a83-8969-9b3469211884", + "created_at": "2017-10-24T17:00:46Z", + "updated_at": "2017-10-24T17:00:46Z" + }, + "entity": { + "name": "test02_key", + "service_instance_guid": "ecf26687-e176-4784-b181-b3c942fecb62", + "credentials": { + "jms": "nhp://100.100.47.11:9008", + "js_uri": "http://100.100.47.11:9008", + "amqp": "amqp://100.100.47.11:9008", + "nhp": "nhp://100.100.47.11:9009", + "mqtt": "tcp://100.100.47.11.100:9008", + "name": "ecf26687-e176-4784-b181-b3c942fecb62", + "nsp": "nsp://100.100.47.11:9008", + "userid": "cfu-7fb486c4-4d9e-49cb-b121-3241ece5dc10", + "uri": "nhp://100.100.47.11:9008", + "uriInfos": [ + { + "host": "100.100.47.11", + "port": 9008 + } + ] + }, + "service_instance_url": "/v2/service_instances/ecf26687-e176-4784-b181-b3c942fecb62" + } + } + + ] +}` + +const postServiceKeysPayload = `{ + "metadata": { + "guid": "9361b5dc-9262-4a83-8969-9b3469211884", + "url": "/v2/service_keys/9361b5dc-9262-4a83-8969-9b3469211884", + "created_at": "2017-10-25T17:31:23Z", + "updated_at": "2017-10-25T17:31:23Z" + }, + "entity": { + "name": "key1", + "service_instance_guid": "ecf26687-e176-4784-b181-b3c942fecb62", + "credentials": { + "jms": "nhp://100.100.47.11:9008", + "js_uri": "http://100.100.47.11:9008", + "amqp": "amqp://100.100.47.11:9008", + "nhp": "nhp://100.100.47.11:9009", + "mqtt": "tcp://100.100.47.11.100:9008", + "name": "ecf26687-e176-4784-b181-b3c942fecb62", + "nsp": "nsp://100.100.47.11:9008", + "userid": "cfu-7fb486c4-4d9e-49cb-b121-3241ece5dc10", + "uri": "nhp://100.100.47.11:9008", + "uriInfos": [ + { + "host": "100.100.47.11", + "port": 9008 + } + ] + }, + "service_instance_url": "/v2/service_instances/ecf26687-e176-4784-b181-b3c942fecb62" + } +}` + +const postServiceKeysDuplicatePayload = `{ + "description": "The service key name is taken: key1", + "error_code": "CF-ServiceKeyNameTaken", + "code": 360001 +}` + +const postServiceKeysBadPayload = `{ + "description": "The service key name is taken: key1", + "error_code": "CF-ServiceKeyNameTaken", + "code": 360001 +` + +const listServiceBrokersPayload = ` +{ + "total_results": 3, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "90a413fd-a636-4133-8bfb-a94b07839e96", + "url": "/v2/service_brokers/90a413fd-a636-4133-8bfb-a94b07839e96", + "created_at": "2016-06-08T16:41:22Z", + "updated_at": "2016-06-08T16:41:22Z" + }, + "entity": { + "name": "name-85", + "broker_url": "https://foo.com/url-2", + "auth_username": "auth_username-2", + "space_guid": "1d43e64d-ed64-43dd-9046-11f422bd407b" + } + } + ] +} +` + +const getServiceByGuidPayload = ` +{ + "metadata": { + "guid": "53f52780-e93c-4af7-a96c-6958311c40e5", + "url": "/v2/services/53f52780-e93c-4af7-a96c-6958311c40e5", + "created_at": "2016-06-08T16:41:32Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "label": "label-58", + "provider": null, + "url": null, + "description": "desc-135", + "long_description": null, + "version": null, + "info_url": null, + "active": true, + "bindable": true, + "unique_id": "c181996b-f233-43d1-8901-3a43eafcaacf", + "extra": null, + "tags": [ + + ], + "requires": [ + + ], + "documentation_url": null, + "service_broker_guid": "0e7250aa-364f-42c2-8fd2-808b0224376f", + "plan_updateable": false, + "service_plans_url": "/v2/services/53f52780-e93c-4af7-a96c-6958311c40e5/service_plans" + } +} +` + +const buildpackUploadPayload = `{ "metadata":{ "guid": "my-job-guid" } }` + +const buildpackUpdatePayload = ` +{ + "metadata": { + "guid": "c92b6f5f-d2a4-413a-b515-647d059723aa", + "url": "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:31Z" + }, + "entity": { + "name": "renamed-buildpack", + "position": 2, + "enabled": true, + "locked": true, + "filename": "my-file", + "stack": "cflinuxfs2" + } +}` +const buildpackCreatePayload = ` +{ + "metadata": { + "guid": "c92b6f5f-d2a4-413a-b515-647d059723aa", + "url": "/v2/buildpacks/c92b6f5f-d2a4-413a-b515-647d059723aa", + "created_at": "2016-06-08T16:41:31Z", + "updated_at": "2016-06-08T16:41:31Z" + }, + "entity": { + "name": "test-buildpack", + "position": 10, + "enabled": true, + "locked": false, + "filename": null, + "stack": "cflinuxfs2" + } +}` + +const listAppUsageEventsPayload = ` +{ + "total_results": 4, + "total_pages": 2, + "prev_url": null, + "next_url": "/v2/app_usage_events?results-per-page=2&page=2", + "resources": [ + { + "metadata": { + "guid": "b32241a5-5508-4d42-893c-360e42a300b6", + "url": "/v2/app_usage_events/b32241a5-5508-4d42-893c-360e42a300b6", + "created_at": "2016-06-08T16:41:33Z" + }, + "entity": { + "state": "STARTED", + "previous_state": null, + "memory_in_mb_per_instance": 564, + "previous_memory_in_mb_per_instance": null, + "instance_count": 1, + "previous_instance_count": null, + "app_guid": "guid-d9fbb7f8-cba5-44a2-b720-c24f1fe5e1c4", + "app_name": "name-1663", + "space_guid": "guid-5e28f12f-9d80-473e-b826-537b148eb338", + "space_name": "name-1664", + "org_guid": "guid-036444f4-f2f5-4ea8-a353-e73330ca0f0a", + "buildpack_guid": "guid-df37754c-819b-4697-a523-4b457d3c83dd", + "buildpack_name": "name-1665", + "package_state": "STAGED", + "previous_package_state": null, + "parent_app_guid": null, + "parent_app_name": null, + "process_type": "web", + "task_name": null, + "task_guid": null + } + }, + { + "metadata": { + "guid": "b32241a5-5508-4d42-893c-360e42a300a8", + "url": "/v2/app_usage_events/b32241a5-5508-4d42-893c-360e42a300a8", + "created_at": "2016-06-08T16:41:33Z" + }, + "entity": { + "state": "STARTED", + "previous_state": null, + "memory_in_mb_per_instance": 564, + "previous_memory_in_mb_per_instance": null, + "instance_count": 1, + "previous_instance_count": null, + "app_guid": "guid-d9fbb7f8-cba5-44a2-b720-c24f1fe5e1c4", + "app_name": "name-1663", + "space_guid": "guid-5e28f12f-9d80-473e-b826-537b148eb338", + "space_name": "name-1664", + "org_guid": "guid-036444f4-f2f5-4ea8-a353-e73330ca0f0a", + "buildpack_guid": "guid-df37754c-819b-4697-a523-4b457d3c83dd", + "buildpack_name": "name-1665", + "package_state": "STARTED", + "previous_package_state": null, + "parent_app_guid": null, + "parent_app_name": null, + "process_type": "web", + "task_name": null, + "task_guid": null + } + } + ] +}` + +const listAppUsageEventsPayloadPage2 = ` +{ + "total_results": 4, + "total_pages": 2, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "b32241a5-5508-4d42-893c-360e42a300b6", + "url": "/v2/app_usage_events/b32241a5-5508-4d42-893c-360e42a300b6", + "created_at": "2016-06-08T16:41:33Z" + }, + "entity": { + "state": "STOPPED", + "previous_state": null, + "memory_in_mb_per_instance": 564, + "previous_memory_in_mb_per_instance": null, + "instance_count": 1, + "previous_instance_count": null, + "app_guid": "guid-d9fbb7f8-cba5-44a2-b720-c24f1fe5e1c4", + "app_name": "name-1663", + "space_guid": "guid-5e28f12f-9d80-473e-b826-537b148eb338", + "space_name": "name-1664", + "org_guid": "guid-036444f4-f2f5-4ea8-a353-e73330ca0f0a", + "buildpack_guid": "guid-df37754c-819b-4697-a523-4b457d3c83dd", + "buildpack_name": "name-1665", + "package_state": "STAGED", + "previous_package_state": null, + "parent_app_guid": null, + "parent_app_name": null, + "process_type": "web", + "task_name": null, + "task_guid": null + } + }, + { + "metadata": { + "guid": "b32241a5-5508-4d42-893c-360e42a300a8", + "url": "/v2/app_usage_events/b32241a5-5508-4d42-893c-360e42a300a8", + "created_at": "2016-06-08T16:41:33Z" + }, + "entity": { + "state": "CRASHED", + "previous_state": null, + "memory_in_mb_per_instance": 564, + "previous_memory_in_mb_per_instance": null, + "instance_count": 1, + "previous_instance_count": null, + "app_guid": "guid-d9fbb7f8-cba5-44a2-b720-c24f1fe5e1c4", + "app_name": "name-1663", + "space_guid": "guid-5e28f12f-9d80-473e-b826-537b148eb338", + "space_name": "name-1664", + "org_guid": "guid-036444f4-f2f5-4ea8-a353-e73330ca0f0a", + "buildpack_guid": "guid-df37754c-819b-4697-a523-4b457d3c83dd", + "buildpack_name": "name-1665", + "package_state": "STARTED", + "previous_package_state": null, + "parent_app_guid": null, + "parent_app_name": null, + "process_type": "web", + "task_name": null, + "task_guid": null + } + } + ] +}` + +const listServiceUsageEventsPayload = ` +{ + "total_results": 4, + "total_pages": 2, + "prev_url": null, + "next_url": "/v2/service_usage_events?results-per-page=2&page=2", + "resources": [ + { + "metadata": { + "guid": "985c09c5-bf5a-44eb-a260-41c532dc0f1d", + "url": "/v2/service_usage_events/985c09c5-bf5a-44eb-a260-41c532dc0f1d", + "created_at": "2016-06-08T16:41:39Z" + }, + "entity": { + "state": "CREATED", + "org_guid": "guid-396a8cb9-5524-4a2b-8e9e-2bfc70edb58d", + "space_guid": "guid-be1f6fe3-e63a-41a3-b196-3fc084022823", + "space_name": "name-1981", + "service_instance_guid": "guid-f93250f7-7ef5-4b02-8d33-353919ce8358", + "service_instance_name": "name-1982", + "service_instance_type": "type-5", + "service_plan_guid": "guid-e9d2d5a0-69a6-46ef-bac5-43f3ed177614", + "service_plan_name": "name-1983", + "service_guid": "guid-34916716-31d7-40c1-9afd-f312996c9654", + "service_label": "label-64" + } + }, + { + "metadata": { + "guid": "985c09c5-bf5a-44eb-a260-41c532dc0f2a", + "url": "/v2/service_usage_events/985c09c5-bf5a-44eb-a260-41c532dc0f1d", + "created_at": "2016-06-08T16:41:39Z" + }, + "entity": { + "state": "DELETED", + "org_guid": "guid-396a8cb9-5524-4a2b-8e9e-2bfc70edb58d", + "space_guid": "guid-be1f6fe3-e63a-41a3-b196-3fc084022823", + "space_name": "name-1981", + "service_instance_guid": "guid-f93250f7-7ef5-4b02-8d33-353919ce8358", + "service_instance_name": "name-1983", + "service_instance_type": "type-5", + "service_plan_guid": "guid-e9d2d5a0-69a6-46ef-bac5-43f3ed177614", + "service_plan_name": "name-1984", + "service_guid": "guid-34916716-31d7-40c1-9afd-f312996c9655", + "service_label": "label-65" + } + } + ] +}` + +const listServiceUsageEventsPayloadPage2 = ` +{ + "total_results": 4, + "total_pages": 2, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "985c09c5-bf5a-44eb-a260-41c532dc0f3f", + "url": "/v2/service_usage_events/985c09c5-bf5a-44eb-a260-41c532dc0f1d", + "created_at": "2016-06-08T16:41:39Z" + }, + "entity": { + "state": "CREATED", + "org_guid": "guid-396a8cb9-5524-4a2b-8e9e-2bfc70edb58d", + "space_guid": "guid-be1f6fe3-e63a-41a3-b196-3fc084022823", + "space_name": "name-1988", + "service_instance_guid": "guid-f93250f7-7ef5-4b02-8d33-353919ce8358", + "service_instance_name": "name-1988", + "service_instance_type": "type-5", + "service_plan_guid": "guid-e9d2d5a0-69a6-46ef-bac5-43f3ed177614", + "service_plan_name": "name-1988", + "service_guid": "guid-34916716-31d7-40c1-9afd-f312996c9654", + "service_label": "label-64" + } + }, + { + "metadata": { + "guid": "985c09c5-bf5a-44eb-a260-41c532dc0f6d", + "url": "/v2/service_usage_events/985c09c5-bf5a-44eb-a260-41c532dc0f1d", + "created_at": "2016-06-08T16:41:39Z" + }, + "entity": { + "state": "UPDATED", + "org_guid": "guid-396a8cb9-5524-4a2b-8e9e-2bfc70edb58d", + "space_guid": "guid-be1f6fe3-e63a-41a3-b196-3fc084022823", + "space_name": "name-1985", + "service_instance_guid": "guid-f93250f7-7ef5-4b02-8d33-353919ce8358", + "service_instance_name": "name-1985", + "service_instance_type": "type-5", + "service_plan_guid": "guid-e9d2d5a0-69a6-46ef-bac5-43f3ed177614", + "service_plan_name": "name-1985", + "service_guid": "guid-34916716-31d7-40c1-9afd-f312996c9655", + "service_label": "label-65" + } + } + ] +}` + +const sharePrivateDomainPayload = `{ + "metadata": { + "guid": "3b6f763f-aae1-4177-9b93-f2de6f2a48f2", + "url": "/v2/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2", + "created_at": "2016-06-08T16:41:35Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "name": "name-1777", + "billing_enabled": false, + "quota_definition_guid": "8737180b-8587-4244-a37d-a12bffcc24b5", + "status": "active", + "quota_definition_url": "/v2/quota_definitions/8737180b-8587-4244-a37d-a12bffcc24b5", + "spaces_url": "/v2/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/spaces", + "domains_url": "/v2/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/domains", + "private_domains_url": "/v2/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/private_domains", + "users_url": "/v2/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/users", + "managers_url": "/v2/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/managers", + "billing_managers_url": "/v2/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/billing_managers", + "auditors_url": "/v2/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/auditors", + "app_events_url": "/v2/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/app_events", + "space_quota_definitions_url": "/v2/organizations/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/space_quota_definitions" + } +} +` +const AppUpdatePayload = ` +{ + "metadata": { + "guid": "97f7e56b-addf-4d26-be82-998a06600011", + "url": "/v2/apps/97f7e56b-addf-4d26-be82-998a06600011", + "created_at": "2018-03-04T16:05:36Z", + "updated_at": "2018-03-06T20:46:26Z" + }, + "entity": { + "name": "NewName", + "production": false, + "space_guid": "d17d98b9-5f88-44c6-8dbc-6036c9607ce0", + "stack_guid": "46ff2259-d51b-48a3-ac7e-72a86c51d85c", + "buildpack": "go_buildpack", + "detected_buildpack": "", + "detected_buildpack_guid": "d42d6cd9-7c1f-4a01-9251-178b51d1ed47", + "environment_json": { + "GOPACKAGENAME": "MichaelIsMetal" + }, + "memory": 65, + "instances": 1, + "disk_quota": 1024, + "state": "STOPPED", + "version": "ac75afb9-bfa6-426e-b0f6-96866e295f2c", + "command": "MichaelIsMetal", + "console": false, + "debug": "", + "staging_task_id": "6379fc80-a71b-4848-812f-520faec910a5", + "package_state": "STAGED", + "health_check_http_endpoint": "", + "health_check_type": "port", + "health_check_timeout": 0, + "staging_failed_reason": "", + "staging_failed_description": "", + "diego": true, + "docker_image": "", + "docker_credentials": { + "username": "", + "password": "" + }, + "package_updated_at": "2018-03-04T21:31:10Z", + "detected_start_command": "./bin/MichaelIsMetal", + "enable_ssh": true, + "ports": [ + 8080 + ], + "space_url": "/v2/spaces/d17d98b9-5f88-44c6-8dbc-6036c9607ce0", + "stack_url": "/v2/stacks/46ff2259-d51b-48a3-ac7e-72a86c51d85c", + "routes_url": "/v2/apps/97f7e56b-addf-4d26-be82-998a06600011/routes", + "events_url": "/v2/apps/97f7e56b-addf-4d26-be82-998a06600011/events", + "service_bindings_url": "/v2/apps/97f7e56b-addf-4d26-be82-998a06600011/service_bindings", + "route_mappings_url": "/v2/apps/97f7e56b-addf-4d26-be82-998a06600011/route_mappings" + } +}` + +const appRestagePayload = `{ + "metadata": { + "guid": "97f7e56b-addf-4d26-be82-998a06600011", + "url": "/v2/apps/97f7e56b-addf-4d26-be82-998a06600011", + "created_at": "2016-06-08T16:41:40Z", + "updated_at": "2016-06-08T16:41:40Z" + }, + "entity": { + "name": "name-2047", + "production": false, + "space_guid": "b1787767-ca42-4fcf-989e-2530fe2987a5", + "stack_guid": "00caa65b-55f4-4c72-8e61-011c20189462", + "buildpack": null, + "detected_buildpack": null, + "detected_buildpack_guid": null, + "environment_json": null, + "memory": 1024, + "instances": 1, + "disk_quota": 1024, + "state": "STARTED", + "version": "4de62557-7599-422e-93ab-49e6b6ede56b", + "command": null, + "console": false, + "debug": null, + "staging_task_id": null, + "package_state": "PENDING", + "health_check_http_endpoint": "", + "health_check_type": "port", + "health_check_timeout": null, + "staging_failed_reason": null, + "staging_failed_description": null, + "diego": false, + "docker_image": null, + "docker_credentials": { + "username": null, + "password": null + }, + "package_updated_at": "2016-06-08T16:41:40Z", + "detected_start_command": "", + "enable_ssh": true, + "ports": null + } +}` + +const postServiceBindingPayload = `{ + "metadata": { + "guid": "4e690cd4-66ef-4052-a23d-0d748316f18c", + "url": "/v2/service_bindings/4e690cd4-66ef-4052-a23d-0d748316f18c", + "created_at": "2016-06-08T16:41:42Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "app_guid": "081d55a0-1bfa-4e51-8d08-273f764988db", + "service_instance_guid": "a0029c76-7017-4a74-94b0-54a04ad94b80", + "credentials": { + "creds-key-63": "creds-val-63" + }, + "name": "prod-db", + "binding_options": { + }, + "gateway_data": null, + "gateway_name": "", + "syslog_drain_url": null, + "volume_mounts": [ + ], + "app_url": "/v2/apps/081d55a0-1bfa-4e51-8d08-273f764988db", + "service_instance_url": "/v2/user_provided_service_instances/a0029c76-7017-4a74-94b0-54a04ad94b80" + } +}` + +const postRouteMappingsPayload = `{ + "metadata": { + "guid": "f869fa46-22b1-40ee-b491-58e321345528", + "url": "/v2/route_mappings/f869fa46-22b1-40ee-b491-58e321345528", + "created_at": "2016-06-08T16:41:42Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "app_port": 8888, + "app_guid": "fa23ddfc-b635-4205-8283-844c53122888", + "route_guid": "e00fb1e1-f7d4-4e36-9912-f76a587e9858", + "app_url": "/v2/apps/fa23ddfc-b635-4205-8283-844c53122888", + "route_url": "/v2/routes/e00fb1e1-f7d4-4e36-9912-f76a587e9858" + } +}` + +const listRouteMappingsPayload = `{ + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "63603ed7-bd4a-4475-a371-5b34381e0cf7", + "url": "/v2/route_mappings/63603ed7-bd4a-4475-a371-5b34381e0cf7", + "created_at": "2016-06-08T16:41:42Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "app_port": 8888, + "app_guid": "ee8b175a-2228-4931-be8a-1f6445bd63bc", + "route_guid": "eb1c4fcd-7d6d-41d2-bd2f-5811f53b6677", + "app_url": "/v2/apps/ee8b175a-2228-4931-be8a-1f6445bd63bc", + "route_url": "/v2/routes/eb1c4fcd-7d6d-41d2-bd2f-5811f53b6677" + } + }, + { + "metadata": { + "guid": "63603ed7-bd4a-4475-a371-5b34381e0cf8", + "url": "/v2/route_mappings/63603ed7-bd4a-4475-a371-5b34381e0cf8", + "created_at": "2016-06-08T16:41:42Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "app_port": 8888, + "app_guid": "ee8b175a-2228-4931-be8a-1f6445bd63bd", + "route_guid": "eb1c4fcd-7d6d-41d2-bd2f-5811f53b6678", + "app_url": "/v2/apps/ee8b175a-2228-4931-be8a-1f6445bd63bd", + "route_url": "/v2/routes/eb1c4fcd-7d6d-41d2-bd2f-5811f53b6678" + } + } + ] +}` + +const getRouteByGuidPayload = `{ + "metadata": { + "guid": "49df626e-8e87-4366-9fbc-f82186824e21", + "url": "/v2/route_mappings/49df626e-8e87-4366-9fbc-f82186824e21", + "created_at": "2016-06-08T16:41:42Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "space_guid": "e374dd83-1cea-40d4-84dc-6ac4fb9b2145", + "host": "host" + } +}` + +const getRouteMappingByGuidPayload = `{ + "metadata": { + "guid": "93eb2527-81b9-4e15-8ba0-2fd8dd8c0c1c", + "url": "/v2/route_mappings/93eb2527-81b9-4e15-8ba0-2fd8dd8c0c1c", + "created_at": "2016-06-08T16:41:42Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "entity": { + "app_port": 8888, + "app_guid": "caf3e3a9-1f64-46d3-a0d5-a3d4ae3f4be4", + "route_guid": "34931bf5-79d0-4303-b082-df023b3305ce", + "app_url": "/v2/apps/caf3e3a9-1f64-46d3-a0d5-a3d4ae3f4be4", + "route_url": "/v2/routes/34931bf5-79d0-4303-b082-df023b3305ce" + } +}` + +const getEVGPayload = `{ + "foo": "bar", + "val": 3 +}` + +const listProcessesPayload1 = `{ + "pagination": { + "total_results": 26, + "total_pages": 2, + "first": { + "href": "https://api.run.example.com/v3/processes?page=1&per_page=20" + }, + "last": { + "href": "https://api.run.example.com/v3/processes?page=2&per_page=20" + }, + "next": { + "href": "https://api.run.example.com/v3/processes?page=2&per_page=20" + }, + "previous": null + }, + "resources": [ + { + "guid": "30200dd1-64c0-451a-8c37-3c91e05c6741", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 2, + "memory_in_mb": 256, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-05-22T21:06:42Z", + "updated_at": "2018-06-05T21:07:08Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/30200dd1-64c0-451a-8c37-3c91e05c6741" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/30200dd1-64c0-451a-8c37-3c91e05c6741/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/30200dd1-64c0-451a-8c37-3c91e05c6741" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/60fa9e9b-c6eb-47a7-92ad-f438e775d234" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/30200dd1-64c0-451a-8c37-3c91e05c6741/stats" + } + } + }, + { + "guid": "0a8eb31c-0450-465d-90a1-837d92895504", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 6, + "memory_in_mb": 128, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-05-22T21:06:43Z", + "updated_at": "2018-06-05T21:07:07Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/0a8eb31c-0450-465d-90a1-837d92895504" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/0a8eb31c-0450-465d-90a1-837d92895504/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/0a8eb31c-0450-465d-90a1-837d92895504" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/60fa9e9b-c6eb-47a7-92ad-f438e775d234" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/0a8eb31c-0450-465d-90a1-837d92895504/stats" + } + } + }, + { + "guid": "c55dca62-ff2e-4641-a4ec-9bea56f987b2", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-05-22T21:17:50Z", + "updated_at": "2018-06-05T21:19:26Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/c55dca62-ff2e-4641-a4ec-9bea56f987b2" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/c55dca62-ff2e-4641-a4ec-9bea56f987b2/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/c55dca62-ff2e-4641-a4ec-9bea56f987b2" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/d5ecae4a-3bfc-4302-af74-bee452108316" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/c55dca62-ff2e-4641-a4ec-9bea56f987b2/stats" + } + } + }, + { + "guid": "72fab46a-8bcd-4941-b556-99694627c402", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-05-29T20:03:48Z", + "updated_at": "2018-05-29T20:07:40Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/72fab46a-8bcd-4941-b556-99694627c402" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/72fab46a-8bcd-4941-b556-99694627c402/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/72fab46a-8bcd-4941-b556-99694627c402" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/6d6eaa00-1fba-4c31-9121-db9a72299240" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/72fab46a-8bcd-4941-b556-99694627c402/stats" + } + } + }, + { + "guid": "46b49bad-e469-418d-9f22-2fa8eb8a9144", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 2, + "memory_in_mb": 256, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-05-30T01:19:06Z", + "updated_at": "2018-06-05T21:07:07Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/46b49bad-e469-418d-9f22-2fa8eb8a9144" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/46b49bad-e469-418d-9f22-2fa8eb8a9144/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/46b49bad-e469-418d-9f22-2fa8eb8a9144" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/60fa9e9b-c6eb-47a7-92ad-f438e775d234" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/46b49bad-e469-418d-9f22-2fa8eb8a9144/stats" + } + } + }, + { + "guid": "74600cfb-9cc9-4026-8473-f638f44ade12", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 6, + "memory_in_mb": 128, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-05-30T01:19:06Z", + "updated_at": "2018-06-05T21:07:07Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/74600cfb-9cc9-4026-8473-f638f44ade12" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/74600cfb-9cc9-4026-8473-f638f44ade12/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/74600cfb-9cc9-4026-8473-f638f44ade12" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/60fa9e9b-c6eb-47a7-92ad-f438e775d234" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/74600cfb-9cc9-4026-8473-f638f44ade12/stats" + } + } + }, + { + "guid": "d782ee5f-8cff-451a-ad30-a0101c7fb430", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 256, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-05-30T17:57:29Z", + "updated_at": "2018-05-30T18:20:20Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/d782ee5f-8cff-451a-ad30-a0101c7fb430" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/d782ee5f-8cff-451a-ad30-a0101c7fb430/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/d782ee5f-8cff-451a-ad30-a0101c7fb430" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/6d6eaa00-1fba-4c31-9121-db9a72299240" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/d782ee5f-8cff-451a-ad30-a0101c7fb430/stats" + } + } + }, + { + "guid": "1b4d1907-3a54-46e6-ac73-d4732af2a28b", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 2, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": 360 + } + }, + "created_at": "2018-06-05T19:41:09Z", + "updated_at": "2018-06-05T21:06:35Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/1b4d1907-3a54-46e6-ac73-d4732af2a28b" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/1b4d1907-3a54-46e6-ac73-d4732af2a28b/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/1b4d1907-3a54-46e6-ac73-d4732af2a28b" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/60fa9e9b-c6eb-47a7-92ad-f438e775d234" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/1b4d1907-3a54-46e6-ac73-d4732af2a28b/stats" + } + } + }, + { + "guid": "bec57af6-4b84-4698-a366-1c4de1f4cc1e", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "none", + "data": { + "timeout": null + } + }, + "created_at": "2018-06-05T19:41:09Z", + "updated_at": "2018-06-05T21:06:14Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/bec57af6-4b84-4698-a366-1c4de1f4cc1e" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/bec57af6-4b84-4698-a366-1c4de1f4cc1e/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/bec57af6-4b84-4698-a366-1c4de1f4cc1e" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/60fa9e9b-c6eb-47a7-92ad-f438e775d234" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/bec57af6-4b84-4698-a366-1c4de1f4cc1e/stats" + } + } + }, + { + "guid": "5d449f8d-999a-4720-9296-5b6afce15a69", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 2048, + "disk_in_mb": 1024, + "health_check": { + "type": "none", + "data": { + "timeout": null + } + }, + "created_at": "2018-06-05T19:41:09Z", + "updated_at": "2018-06-05T21:06:20Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/5d449f8d-999a-4720-9296-5b6afce15a69" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/5d449f8d-999a-4720-9296-5b6afce15a69/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/5d449f8d-999a-4720-9296-5b6afce15a69" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/60fa9e9b-c6eb-47a7-92ad-f438e775d234" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/5d449f8d-999a-4720-9296-5b6afce15a69/stats" + } + } + }, + { + "guid": "57a598af-c4a6-4ade-8db6-05679ae092f5", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 2, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": 180 + } + }, + "created_at": "2018-06-05T19:45:25Z", + "updated_at": "2018-06-05T21:10:23Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/57a598af-c4a6-4ade-8db6-05679ae092f5" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/57a598af-c4a6-4ade-8db6-05679ae092f5/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/57a598af-c4a6-4ade-8db6-05679ae092f5" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/1c17814c-e195-4ea5-9be2-6d01d9bf6007" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/57a598af-c4a6-4ade-8db6-05679ae092f5/stats" + } + } + }, + { + "guid": "79616d63-1075-4c02-a058-574a01314206", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "none", + "data": { + "timeout": null + } + }, + "created_at": "2018-06-05T21:04:37Z", + "updated_at": "2018-06-05T21:04:48Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/79616d63-1075-4c02-a058-574a01314206" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/79616d63-1075-4c02-a058-574a01314206/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/79616d63-1075-4c02-a058-574a01314206" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/60fa9e9b-c6eb-47a7-92ad-f438e775d234" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/79616d63-1075-4c02-a058-574a01314206/stats" + } + } + }, + { + "guid": "a2298d27-57fe-41e9-ad1e-f540427a0949", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 2, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": 360 + } + }, + "created_at": "2018-06-05T21:04:37Z", + "updated_at": "2018-06-05T21:06:34Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/a2298d27-57fe-41e9-ad1e-f540427a0949" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/a2298d27-57fe-41e9-ad1e-f540427a0949/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/a2298d27-57fe-41e9-ad1e-f540427a0949" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/60fa9e9b-c6eb-47a7-92ad-f438e775d234" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/a2298d27-57fe-41e9-ad1e-f540427a0949/stats" + } + } + }, + { + "guid": "a98f0649-4e31-435a-bb61-a96364c366a4", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 2048, + "disk_in_mb": 1024, + "health_check": { + "type": "none", + "data": { + "timeout": null + } + }, + "created_at": "2018-06-05T21:04:38Z", + "updated_at": "2018-06-05T21:04:49Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/a98f0649-4e31-435a-bb61-a96364c366a4" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/a98f0649-4e31-435a-bb61-a96364c366a4/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/a98f0649-4e31-435a-bb61-a96364c366a4" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/60fa9e9b-c6eb-47a7-92ad-f438e775d234" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/a98f0649-4e31-435a-bb61-a96364c366a4/stats" + } + } + }, + { + "guid": "6aa28ed8-d377-4225-b853-80001e64113b", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 3, + "memory_in_mb": 64, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-06-05T21:07:29Z", + "updated_at": "2018-06-05T21:08:02Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/6aa28ed8-d377-4225-b853-80001e64113b" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/6aa28ed8-d377-4225-b853-80001e64113b/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/6aa28ed8-d377-4225-b853-80001e64113b" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/2bd2080e-b9d7-4911-a607-a4e5adb00b59" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/6aa28ed8-d377-4225-b853-80001e64113b/stats" + } + } + }, + { + "guid": "dfc0f648-a3c8-4f46-b0a4-425136f238c6", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 2, + "memory_in_mb": 64, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-06-05T21:08:19Z", + "updated_at": "2018-06-05T21:08:41Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/dfc0f648-a3c8-4f46-b0a4-425136f238c6" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/dfc0f648-a3c8-4f46-b0a4-425136f238c6/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/dfc0f648-a3c8-4f46-b0a4-425136f238c6" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/2bd2080e-b9d7-4911-a607-a4e5adb00b59" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/dfc0f648-a3c8-4f46-b0a4-425136f238c6/stats" + } + } + }, + { + "guid": "ba159a14-a3b5-4711-95c7-4bf89ff89a6c", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 2, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": 180 + } + }, + "created_at": "2018-06-05T21:08:57Z", + "updated_at": "2018-06-05T21:10:23Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/ba159a14-a3b5-4711-95c7-4bf89ff89a6c" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/ba159a14-a3b5-4711-95c7-4bf89ff89a6c/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/ba159a14-a3b5-4711-95c7-4bf89ff89a6c" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/1c17814c-e195-4ea5-9be2-6d01d9bf6007" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/ba159a14-a3b5-4711-95c7-4bf89ff89a6c/stats" + } + } + }, + { + "guid": "c7d20ea3-028b-4559-9f0c-a61966abc163", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 3, + "memory_in_mb": 256, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-06-05T21:11:44Z", + "updated_at": "2018-06-05T21:12:00Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/c7d20ea3-028b-4559-9f0c-a61966abc163" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/c7d20ea3-028b-4559-9f0c-a61966abc163/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/c7d20ea3-028b-4559-9f0c-a61966abc163" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/7443e429-eee3-48ea-89cb-c9d3234c7fbf" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/c7d20ea3-028b-4559-9f0c-a61966abc163/stats" + } + } + }, + { + "guid": "956ca6e5-6560-49c6-ae01-843144b24ec5", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": 120 + } + }, + "created_at": "2018-06-05T21:12:01Z", + "updated_at": "2018-06-05T21:12:54Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/956ca6e5-6560-49c6-ae01-843144b24ec5" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/956ca6e5-6560-49c6-ae01-843144b24ec5/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/956ca6e5-6560-49c6-ae01-843144b24ec5" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/7443e429-eee3-48ea-89cb-c9d3234c7fbf" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/956ca6e5-6560-49c6-ae01-843144b24ec5/stats" + } + } + }, + { + "guid": "787fd0c1-d827-4c9e-975b-acd73267a583", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-06-29T18:31:10Z", + "updated_at": "2018-06-29T18:31:10Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/787fd0c1-d827-4c9e-975b-acd73267a583" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/787fd0c1-d827-4c9e-975b-acd73267a583/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/787fd0c1-d827-4c9e-975b-acd73267a583" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/60fa9e9b-c6eb-47a7-92ad-f438e775d234" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/787fd0c1-d827-4c9e-975b-acd73267a583/stats" + } + } + } + ] +}` + +const listProcessesPayload2 = `{ + "pagination": { + "total_results": 26, + "total_pages": 2, + "first": { + "href": "https://api.run.example.com/v3/processes?page=1&per_page=20" + }, + "last": { + "href": "https://api.run.example.com/v3/processes?page=2&per_page=20" + }, + "next": null, + "previous": { + "href": "https://api.run.example.com/v3/processes?page=1&per_page=20" + } + }, + "resources": [ + { + "guid": "09eb0d25-75b0-48e1-b45f-1636ea5bbe0c", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-06-29T18:32:35Z", + "updated_at": "2018-06-29T18:32:35Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/09eb0d25-75b0-48e1-b45f-1636ea5bbe0c" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/09eb0d25-75b0-48e1-b45f-1636ea5bbe0c/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/09eb0d25-75b0-48e1-b45f-1636ea5bbe0c" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/60fa9e9b-c6eb-47a7-92ad-f438e775d234" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/09eb0d25-75b0-48e1-b45f-1636ea5bbe0c/stats" + } + } + }, + { + "guid": "00944be5-fe20-43ba-abe2-8fcc41ee2edc", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-07-13T20:17:23Z", + "updated_at": "2018-07-18T17:20:59Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/00944be5-fe20-43ba-abe2-8fcc41ee2edc" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/00944be5-fe20-43ba-abe2-8fcc41ee2edc/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/00944be5-fe20-43ba-abe2-8fcc41ee2edc" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/a40da3ef-f0a3-4374-a7af-9c867382b30c" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/00944be5-fe20-43ba-abe2-8fcc41ee2edc/stats" + } + } + }, + { + "guid": "750ce4f3-3c28-4acc-9b50-357dc7dde27a", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-07-17T14:46:37Z", + "updated_at": "2018-07-17T14:46:37Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/750ce4f3-3c28-4acc-9b50-357dc7dde27a" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/750ce4f3-3c28-4acc-9b50-357dc7dde27a/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/750ce4f3-3c28-4acc-9b50-357dc7dde27a" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/a40da3ef-f0a3-4374-a7af-9c867382b30c" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/750ce4f3-3c28-4acc-9b50-357dc7dde27a/stats" + } + } + }, + { + "guid": "2274c4c4-7e6e-413f-b976-e8210ddcc748", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 256, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-08-06T21:45:14Z", + "updated_at": "2018-08-07T18:11:33Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/2274c4c4-7e6e-413f-b976-e8210ddcc748" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/2274c4c4-7e6e-413f-b976-e8210ddcc748/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/2274c4c4-7e6e-413f-b976-e8210ddcc748" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/939eb497-671b-46cb-abd2-26e89129cc5b" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/2274c4c4-7e6e-413f-b976-e8210ddcc748/stats" + } + } + }, + { + "guid": "bc69b3fe-0d80-4508-a1d2-9bc53269287f", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 128, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "created_at": "2018-08-07T22:27:14Z", + "updated_at": "2018-08-08T16:09:41Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/bc69b3fe-0d80-4508-a1d2-9bc53269287f" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/bc69b3fe-0d80-4508-a1d2-9bc53269287f/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/bc69b3fe-0d80-4508-a1d2-9bc53269287f" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/a40da3ef-f0a3-4374-a7af-9c867382b30c" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/bc69b3fe-0d80-4508-a1d2-9bc53269287f/stats" + } + } + }, + { + "guid": "4b32fba5-ec34-40e1-9511-0a25c8f93d8f", + "type": "worker", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 1024, + "disk_in_mb": 1024, + "health_check": { + "type": "process", + "data": { + "timeout": null + } + }, + "created_at": "2018-08-07T22:28:44Z", + "updated_at": "2018-08-08T16:09:41Z", + "links": { + "self": { + "href": "https://api.run.example.com/v3/processes/4b32fba5-ec34-40e1-9511-0a25c8f93d8f" + }, + "scale": { + "href": "https://api.run.example.com/v3/processes/4b32fba5-ec34-40e1-9511-0a25c8f93d8f/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.run.example.com/v3/apps/bc69b3fe-0d80-4508-a1d2-9bc53269287f" + }, + "space": { + "href": "https://api.run.example.com/v3/spaces/a40da3ef-f0a3-4374-a7af-9c867382b30c" + }, + "stats": { + "href": "https://api.run.example.com/v3/processes/4b32fba5-ec34-40e1-9511-0a25c8f93d8f/stats" + } + } + } + ] +}` + +const getV3DeploymentPayload = `{ + "guid": "59c3d133-2b83-46f3-960e-7765a129aea4", + "state": "DEPLOYING", + "status": { + "value": "ACTIVE", + "reason": "DEPLOYING", + "details": { + "last_successful_healthcheck": "2018-04-25T22:42:10Z" + } + }, + "strategy": "rolling", + "droplet": { + "guid": "44ccfa61-dbcf-4a0d-82fe-f668e9d2a962" + }, + "previous_droplet": { + "guid": "cc6bc315-bd06-49ce-92c2-bc3ad45268c2" + }, + "new_processes": [ + { + "guid": "fd5d3e60-f88c-4c37-b1ae-667cfc65a856", + "type": "web" + } + ], + "revision": { + "guid": "56126cba-656a-4eba-a81e-7e9951b2df57", + "version": 1 + }, + "created_at": "2018-04-25T22:42:10Z", + "updated_at": "2018-04-25T22:42:10Z", + "metadata": { + "labels": { }, + "annotations": { } + }, + "relationships": { + "app": { + "data": { + "guid": "305cea31-5a44-45ca-b51b-e89c7a8ef8b2" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/deployments/59c3d133-2b83-46f3-960e-7765a129aea4" + }, + "app": { + "href": "https://api.example.org/v3/apps/305cea31-5a44-45ca-b51b-e89c7a8ef8b2" + } + } +}` + +const getProcessStatsPayload1 = `{ + "resources": [ + { + "type": "web", + "index": 0, + "state": "RUNNING", + "usage": { + "time": "2016-03-23T23:17:30.476314154Z", + "cpu": 0.00038711029163348665, + "mem": 19177472, + "disk": 69705728 + }, + "host": "10.244.16.10", + "instance_ports": [ + { + "external": 64546, + "internal": 8080, + "external_tls_proxy_port": 61002, + "internal_tls_proxy_port": 61003 + } + ], + "uptime": 9042, + "mem_quota": 268435456, + "disk_quota": 1073741824, + "fds_quota": 16384, + "isolation_segment": "example_iso_segment", + "details": null + } + ] +}` +const listValidResources = `[ + { + "sha1": "002d760bea1be268e27077412e11a320d0f164d3", + "size": 36 + }, + { + "sha1": "a9993e364706816aba3e25717850c26c9cd0d89d", + "size": 1 + } +]` + +const listV3StacksPayload = `{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/stacks?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/stacks?page=1&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "guid-1", + "created_at": "2018-11-09T22:43:28Z", + "updated_at": "2018-11-09T22:43:28Z", + "name": "my-stack-1", + "description": "This is my first stack!", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/stacks/guid-1" + } + } + }, + { + "guid": "guid-2", + "created_at": "2018-11-09T22:43:29Z", + "updated_at": "2018-11-09T22:43:29Z", + "name": "my-stack-2", + "description": "This is my second stack!", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/stacks/guid-2" + } + } + } + ] +}` + +const listV3ServiceCredentialBindingsPayload = `{ + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/service_instances?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/service_instances?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "d9634934-8e1f-4c2d-bb33-fa5df019cf9d", + "created_at": "2022-02-17T17:17:44Z", + "updated_at": "2022-02-17T17:17:44Z", + "name": "my_service_key", + "type": "key", + "relationships": { + "service_instance": { + "data": { + "guid": "85ccdcad-d725-4109-bca4-fd6ba062b5c8" + } + } + }, + "metadata": { + "labels": { }, + "annotations": { } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_credential_bindings/d9634934-8e1f-4c2d-bb33-fa5df019cf9d" + }, + "details": { + "href": "https://api.example.org/v3/service_credential_bindings/d9634934-8e1f-4c2d-bb33-fa5df019cf9d/details" + }, + "service_instance": { + "href": "https://api.example.org/v3/service_instances/85ccdcad-d725-4109-bca4-fd6ba062b5c8" + }, + "parameters": { + "href": "https://api.example.org/v3/service_credential_bindings/d9634934-8e1f-4c2d-bb33-fa5df019cf9d/parameters" + } + } + } + ] +}` + +const GetV3ServiceCredentialBindingsByGUIDPayload = `{ + "guid": "d9634934-8e1f-4c2d-bb33-fa5df019cf9d", + "created_at": "2022-02-17T17:17:44Z", + "updated_at": "2022-02-17T17:17:44Z", + "name": "my_service_key", + "type": "key", + "last_operation": { + "type": "create", + "state": "succeeded", + "description": "", + "created_at": "2022-02-17T17:17:44Z", + "updated_at": "2022-02-17T17:17:44Z" + }, + "relationships": { + "service_instance": { + "data": { + "guid": "85ccdcad-d725-4109-bca4-fd6ba062b5c8" + } + } + }, + "metadata": { + "labels": { }, + "annotations": { } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_credential_bindings/d9634934-8e1f-4c2d-bb33-fa5df019cf9d" + }, + "details": { + "href": "https://api.example.org/v3/service_credential_bindings/d9634934-8e1f-4c2d-bb33-fa5df019cf9d/details" + }, + "service_instance": { + "href": "https://api.example.org/v3/service_instances/85ccdcad-d725-4109-bca4-fd6ba062b5c8" + }, + "parameters": { + "href": "https://api.example.org/v3/service_credential_bindings/d9634934-8e1f-4c2d-bb33-fa5df019cf9d/parameters" + } + } +}` + +const listV3UsersPayload = `{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/users?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/users?page=2&per_page=2" + }, + "next": { + "href": "https://api.example.org/v3/userspage2?page=2&per_page=2" + }, + "previous": null + }, + "resources": [ + { + "guid": "16f43d50-43a2-4981-bae8-633e8248a637", + "created_at": "2022-08-02T21:37:52Z", + "updated_at": "2022-08-02T21:37:52Z", + "username": "smoke_tests", + "presentation_name": "smoke_tests", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/16f43d50-43a2-4981-bae8-633e8248a637" + } + } + }, + { + "guid": "test1", + "created_at": "2022-08-02T21:40:34Z", + "updated_at": "2022-08-02T21:40:34Z", + "username": "test1", + "presentation_name": "test1", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/test1" + } + } + } + ] +}` + +const listV3UsersPayloadPage2 = `{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/users?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/users?page=2&per_page=2" + }, + "next": { + "href": "" + }, + "previous": { + "href": "https://api.example.org/v3/users?page=1&per_page=2" + } + }, + "resources": [ + { + "guid": "test2", + "created_at": "2022-08-02T21:41:59Z", + "updated_at": "2022-08-02T21:41:59Z", + "username": "test2", + "presentation_name": "test2", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/test2" + } + } + } + ] +}` diff --git a/third_party/go-cfclient/processes.go b/third_party/go-cfclient/processes.go new file mode 100644 index 000000000000..f3b729d34d30 --- /dev/null +++ b/third_party/go-cfclient/processes.go @@ -0,0 +1,102 @@ +package cfclient + +import ( + "encoding/json" + "net/url" +) + +// ProcessListResponse is the json body returned from the API +type ProcessListResponse struct { + Pagination Pagination `json:"pagination"` + Processes []Process `json:"resources"` +} + +// Process represents a running process in a container. +type Process struct { + GUID string `json:"guid"` + Type string `json:"type"` + Instances int `json:"instances"` + MemoryInMB int `json:"memory_in_mb"` + DiskInMB int `json:"disk_in_mb"` + Ports []int `json:"ports,omitempty"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + HealthCheck struct { + Type string `json:"type"` + Data struct { + Timeout int `json:"timeout"` + InvocationTimeout int `json:"invocation_timeout"` + Endpoint string `json:"endpoint"` + } `json:"data"` + } `json:"health_check"` + Links struct { + Self Link `json:"self"` + Scale Link `json:"scale"` + App Link `json:"app"` + Space Link `json:"space"` + Stats Link `json:"stats"` + } `json:"links"` +} + +// ListAllProcesses will call the v3 processes api +func (c *Client) ListAllProcesses() ([]Process, error) { + return c.ListAllProcessesByQuery(url.Values{}) +} + +// ListAllProcessesByQuery will call the v3 processes api +func (c *Client) ListAllProcessesByQuery(query url.Values) ([]Process, error) { + var allProcesses []Process + + requestURL := "/v3/processes" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + for { + resp, err := c.getProcessPage(requestURL) + if err != nil { + return nil, err + } + + if resp.Pagination.TotalResults == 0 { + return nil, nil + } + + if allProcesses == nil { + allProcesses = make([]Process, 0, resp.Pagination.TotalResults) + } + + allProcesses = append(allProcesses, resp.Processes...) + if resp.Pagination.Next.Href == "" { + break + } + + requestURL = resp.Pagination.Next.Href + if requestURL == "" { + return allProcesses, nil + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, err + } + } + + return allProcesses, nil +} + +func (c *Client) getProcessPage(requestURL string) (*ProcessListResponse, error) { + req := c.NewRequest("GET", requestURL) + + resp, err := c.DoRequest(req) + if err != nil { + return nil, err + } + + procResp := new(ProcessListResponse) + defer resp.Body.Close() + err = json.NewDecoder(resp.Body).Decode(procResp) + if err != nil { + return nil, err + } + + return procResp, nil +} diff --git a/third_party/go-cfclient/processes_test.go b/third_party/go-cfclient/processes_test.go new file mode 100644 index 000000000000..7984a42ded56 --- /dev/null +++ b/third_party/go-cfclient/processes_test.go @@ -0,0 +1,28 @@ +package cfclient + +import ( + "net/url" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListProcesses(t *testing.T) { + Convey("List Processes", t, func() { + setup(MockRoute{"GET", "/v3/processes", []string{listProcessesPayload1, listProcessesPayload2}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + q := url.Values{} + q.Add("per_page", "20") + procs, err := client.ListAllProcessesByQuery(q) + So(err, ShouldBeNil) + + So(procs, ShouldHaveLength, 26) + }) +} diff --git a/third_party/go-cfclient/resource_match.go b/third_party/go-cfclient/resource_match.go new file mode 100644 index 000000000000..70b541328ad5 --- /dev/null +++ b/third_party/go-cfclient/resource_match.go @@ -0,0 +1,45 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "io/ioutil" + + "github.com/pkg/errors" +) + +// The Resource match Api retruns the response in the following data structure +type Resource struct { + Sha1 string `json:"sha1"` + Size int `json:"size"` +} + +// ResourceMatch matches given resource list of SHA / file size pairs against +// the Cloud Controller cache, and reports the subset which describes already +// existing files +func (c *Client) ResourceMatch(resources []Resource) ([]Resource, error) { + + var resourcesList []Resource + emptyResource := make([]Resource, 0) + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(resources) + if err != nil { + return emptyResource, errors.Wrapf(err, "Error reading Resource List") + } + r := c.NewRequestWithBody("PUT", "/v2/resource_match", buf) + resp, err := c.DoRequest(r) + if err != nil { + return emptyResource, errors.Wrapf(err, "Error uploading Resource List") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return emptyResource, errors.Wrapf(err, "Error reading Resources http response body") + } + err = json.Unmarshal(resBody, &resourcesList) + if err != nil { + return emptyResource, errors.Wrapf(err, "Error reading Resources http response body") + } + return resourcesList, nil + +} diff --git a/third_party/go-cfclient/resource_match_test.go b/third_party/go-cfclient/resource_match_test.go new file mode 100644 index 000000000000..06cd82379e0c --- /dev/null +++ b/third_party/go-cfclient/resource_match_test.go @@ -0,0 +1,40 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestResourceMatch(t *testing.T) { + Convey("Resource Match", t, func() { + mocks := []MockRoute{ + {"PUT", "/v2/resource_match", []string{listValidResources}, "Test-golang", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + UserAgent: "Test-golang", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + ResourceList := []Resource{} + ResourceList = append(ResourceList, Resource{Sha1: "002d760bea1be268e27077412e11a320d0f164d3", Size: 36}) + ResourceList = append(ResourceList, Resource{Sha1: "a9993e364706816aba3e25717850c26c9cd0d89d", Size: 1}) + + ResApps, err := client.ResourceMatch(ResourceList) + assertResourceList(ResApps, err) + }) +} + +func assertResourceList(resList []Resource, err error) { + So(err, ShouldBeNil) + So(len(resList), ShouldEqual, 2) + So(resList[0].Sha1, ShouldEqual, "002d760bea1be268e27077412e11a320d0f164d3") + So(resList[0].Size, ShouldEqual, 36) + So(resList[1].Sha1, ShouldEqual, "a9993e364706816aba3e25717850c26c9cd0d89d") + So(resList[1].Size, ShouldEqual, 1) + +} diff --git a/third_party/go-cfclient/route_mappings.go b/third_party/go-cfclient/route_mappings.go new file mode 100644 index 000000000000..c4e327f29330 --- /dev/null +++ b/third_party/go-cfclient/route_mappings.go @@ -0,0 +1,162 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type RouteMappingRequest struct { + AppGUID string `json:"app_guid"` + RouteGUID string `json:"route_guid"` + AppPort int `json:"app_port"` +} + +type RouteMappingResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []RouteMappingResource `json:"resources"` +} + +type RouteMapping struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + AppPort int `json:"app_port"` + AppGUID string `json:"app_guid"` + RouteGUID string `json:"route_guid"` + AppUrl string `json:"app_url"` + RouteUrl string `json:"route_url"` + c *Client +} + +type RouteMappingResource struct { + Meta Meta `json:"metadata"` + Entity RouteMapping `json:"entity"` +} + +func (c *Client) MappingAppAndRoute(req RouteMappingRequest) (*RouteMapping, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("POST", "/v2/route_mappings", buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleMappingResp(resp) +} + +func (c *Client) ListRouteMappings() ([]*RouteMapping, error) { + return c.ListRouteMappingsByQuery(nil) +} + +func (c *Client) ListRouteMappingsByQuery(query url.Values) ([]*RouteMapping, error) { + var routeMappings []*RouteMapping + var routeMappingsResp RouteMappingResponse + pages := 0 + + requestUrl := "/v2/route_mappings?" + query.Encode() + for { + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting route mappings") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading route mappings request:") + } + + err = json.Unmarshal(resBody, &routeMappingsResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling route mappings") + } + + for _, routeMapping := range routeMappingsResp.Resources { + routeMappings = append(routeMappings, c.mergeRouteMappingResource(routeMapping)) + } + requestUrl = routeMappingsResp.NextUrl + if requestUrl == "" { + break + } + pages++ + totalPages := routeMappingsResp.Pages + if totalPages > 0 && pages >= totalPages { + break + } + } + return routeMappings, nil +} + +func (c *Client) GetRouteMappingByGuid(guid string) (*RouteMapping, error) { + var routeMapping RouteMappingResource + requestUrl := fmt.Sprintf("/v2/route_mappings/%s", guid) + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting route mapping") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading route mapping response body") + } + err = json.Unmarshal(resBody, &routeMapping) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling route mapping") + } + routeMapping.Entity.Guid = routeMapping.Meta.Guid + routeMapping.Entity.CreatedAt = routeMapping.Meta.CreatedAt + routeMapping.Entity.UpdatedAt = routeMapping.Meta.UpdatedAt + routeMapping.Entity.c = c + return &routeMapping.Entity, nil +} + +func (c *Client) DeleteRouteMapping(guid string) error { + requestUrl := fmt.Sprintf("/v2/route_mappings/%s?", guid) + resp, err := c.DoRequest(c.NewRequest("DELETE", requestUrl)) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting route mapping %s, response code %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) handleMappingResp(resp *http.Response) (*RouteMapping, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + var mappingResource RouteMappingResource + err = json.Unmarshal(body, &mappingResource) + if err != nil { + return nil, err + } + return c.mergeRouteMappingResource(mappingResource), nil +} + +func (c *Client) mergeRouteMappingResource(mapping RouteMappingResource) *RouteMapping { + mapping.Entity.Guid = mapping.Meta.Guid + mapping.Entity.CreatedAt = mapping.Meta.CreatedAt + mapping.Entity.UpdatedAt = mapping.Meta.UpdatedAt + mapping.Entity.c = c + return &mapping.Entity +} diff --git a/third_party/go-cfclient/route_mappings_test.go b/third_party/go-cfclient/route_mappings_test.go new file mode 100644 index 000000000000..aba4cf066dca --- /dev/null +++ b/third_party/go-cfclient/route_mappings_test.go @@ -0,0 +1,90 @@ +package cfclient + +import ( + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestMappingAppAndRoute(t *testing.T) { + Convey("Mapping app and route", t, func() { + setup(MockRoute{"POST", "/v2/route_mappings", []string{postRouteMappingsPayload}, "", http.StatusCreated, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + mappingRequest := RouteMappingRequest{AppGUID: "fa23ddfc-b635-4205-8283-844c53122888", RouteGUID: "e00fb1e1-f7d4-4e36-9912-f76a587e9858", AppPort: 8888} + + mapping, err := client.MappingAppAndRoute(mappingRequest) + So(err, ShouldBeNil) + So(mapping.Guid, ShouldEqual, "f869fa46-22b1-40ee-b491-58e321345528") + So(mapping.AppGUID, ShouldEqual, "fa23ddfc-b635-4205-8283-844c53122888") + So(mapping.RouteGUID, ShouldEqual, "e00fb1e1-f7d4-4e36-9912-f76a587e9858") + }) +} + +func TestListRouteMappings(t *testing.T) { + Convey("List route mappings", t, func() { + setup(MockRoute{"GET", "/v2/route_mappings", []string{listRouteMappingsPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + routeMappings, err := client.ListRouteMappings() + So(err, ShouldBeNil) + + So(len(routeMappings), ShouldEqual, 2) + So(routeMappings[0].Guid, ShouldEqual, "63603ed7-bd4a-4475-a371-5b34381e0cf7") + So(routeMappings[1].Guid, ShouldEqual, "63603ed7-bd4a-4475-a371-5b34381e0cf8") + So(routeMappings[0].AppGUID, ShouldEqual, "ee8b175a-2228-4931-be8a-1f6445bd63bc") + So(routeMappings[1].AppGUID, ShouldEqual, "ee8b175a-2228-4931-be8a-1f6445bd63bd") + So(routeMappings[0].RouteGUID, ShouldEqual, "eb1c4fcd-7d6d-41d2-bd2f-5811f53b6677") + So(routeMappings[1].RouteGUID, ShouldEqual, "eb1c4fcd-7d6d-41d2-bd2f-5811f53b6678") + }) +} + +func TestGetRouteMappingByGuid(t *testing.T) { + Convey("Get route mapping by guid", t, func() { + setup(MockRoute{"GET", "/v2/route_mappings/93eb2527-81b9-4e15-8ba0-2fd8dd8c0c1c", []string{getRouteMappingByGuidPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + routeMapping, err := client.GetRouteMappingByGuid("93eb2527-81b9-4e15-8ba0-2fd8dd8c0c1c") + So(err, ShouldBeNil) + So(routeMapping.Guid, ShouldEqual, "93eb2527-81b9-4e15-8ba0-2fd8dd8c0c1c") + So(routeMapping.AppGUID, ShouldEqual, "caf3e3a9-1f64-46d3-a0d5-a3d4ae3f4be4") + So(routeMapping.RouteGUID, ShouldEqual, "34931bf5-79d0-4303-b082-df023b3305ce") + }) +} + +func TestDeleteRouteMapping(t *testing.T) { + Convey("Delete route mapping", t, func() { + setup(MockRoute{"DELETE", "/v2/route_mappings/93eb2527-81b9-4e15-8ba0-2fd8dd8c0c1c", []string{""}, "", http.StatusNoContent, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteRouteMapping("93eb2527-81b9-4e15-8ba0-2fd8dd8c0c1c") + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/routes.go b/third_party/go-cfclient/routes.go new file mode 100644 index 000000000000..6b222d7c7e73 --- /dev/null +++ b/third_party/go-cfclient/routes.go @@ -0,0 +1,209 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type RoutesResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []RoutesResource `json:"resources"` +} + +type RoutesResource struct { + Meta Meta `json:"metadata"` + Entity Route `json:"entity"` +} + +type RouteRequest struct { + DomainGuid string `json:"domain_guid"` + SpaceGuid string `json:"space_guid"` + Host string `json:"host"` // required for http routes + Path string `json:"path"` + Port int `json:"port"` +} + +type Route struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Host string `json:"host"` + Path string `json:"path"` + DomainGuid string `json:"domain_guid"` + DomainURL string `json:"domain_url"` + SpaceGuid string `json:"space_guid"` + ServiceInstanceGuid string `json:"service_instance_guid"` + Port int `json:"port"` + c *Client +} + +// CreateRoute creates a regular http route +func (c *Client) CreateRoute(routeRequest RouteRequest) (Route, error) { + routesResource, err := c.createRoute("/v2/routes", routeRequest) + if nil != err { + return Route{}, err + } + return c.mergeRouteResource(routesResource), nil +} + +// CreateTcpRoute creates a TCP route +func (c *Client) CreateTcpRoute(routeRequest RouteRequest) (Route, error) { + routesResource, err := c.createRoute("/v2/routes?generate_port=true", routeRequest) + if nil != err { + return Route{}, err + } + return c.mergeRouteResource(routesResource), nil +} + +// BindRoute associates the specified route with the application +func (c *Client) BindRoute(routeGUID, appGUID string) error { + resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/routes/%s/apps/%s", routeGUID, appGUID))) + if err != nil { + return errors.Wrapf(err, "Error binding route %s to app %s", routeGUID, appGUID) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return fmt.Errorf("Error binding route %s to app %s, response code: %d", routeGUID, appGUID, resp.StatusCode) + } + return nil +} + +func (c *Client) GetRouteByGuid(guid string) (Route, error) { + var route RoutesResource + + r := c.NewRequest("GET", fmt.Sprintf("/v2/routes/%s", guid)) + resp, err := c.DoRequest(r) + if err != nil { + return route.Entity, errors.Wrap(err, "Error requesting route") + } + defer resp.Body.Close() + + err = json.NewDecoder(resp.Body).Decode(&route) + if err != nil { + return route.Entity, errors.Wrap(err, "Error unmarshalling route response body") + } + + route.Entity.Guid = route.Meta.Guid + route.Entity.CreatedAt = route.Meta.CreatedAt + route.Entity.UpdatedAt = route.Meta.UpdatedAt + route.Entity.c = c + return route.Entity, nil +} + +func (c *Client) ListRoutesByQuery(query url.Values) ([]Route, error) { + return c.fetchRoutes("/v2/routes?" + query.Encode()) +} + +func (c *Client) fetchRoutes(requestUrl string) ([]Route, error) { + var routes []Route + for { + routesResp, err := c.getRoutesResponse(requestUrl) + if err != nil { + return []Route{}, err + } + for _, route := range routesResp.Resources { + route.Entity.Guid = route.Meta.Guid + route.Entity.CreatedAt = route.Meta.CreatedAt + route.Entity.UpdatedAt = route.Meta.UpdatedAt + route.Entity.c = c + routes = append(routes, route.Entity) + } + requestUrl = routesResp.NextUrl + if requestUrl == "" { + break + } + } + return routes, nil +} + +func (c *Client) ListRoutes() ([]Route, error) { + return c.ListRoutesByQuery(nil) +} + +func (r *Route) Domain() (*Domain, error) { + req := r.c.NewRequest("GET", r.DomainURL) + resp, err := r.c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "requesting domain for route "+r.DomainURL) + } + + defer resp.Body.Close() + var domain DomainResource + if err = json.NewDecoder(resp.Body).Decode(&domain); err != nil { + return nil, errors.Wrap(err, "unmarshalling domain") + } + + return r.c.mergeDomainResource(domain), nil +} + +func (c *Client) getRoutesResponse(requestUrl string) (RoutesResponse, error) { + var routesResp RoutesResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return RoutesResponse{}, errors.Wrap(err, "Error requesting routes") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return RoutesResponse{}, errors.Wrap(err, "Error reading routes body") + } + err = json.Unmarshal(resBody, &routesResp) + if err != nil { + return RoutesResponse{}, errors.Wrap(err, "Error unmarshalling routes") + } + return routesResp, nil +} + +func (c *Client) createRoute(requestUrl string, routeRequest RouteRequest) (RoutesResource, error) { + var routeResp RoutesResource + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(routeRequest) + if err != nil { + return RoutesResource{}, errors.Wrap(err, "Error creating route - failed to serialize request body") + } + r := c.NewRequestWithBody("POST", requestUrl, buf) + resp, err := c.DoRequest(r) + if err != nil { + return RoutesResource{}, errors.Wrap(err, "Error creating route") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return RoutesResource{}, errors.Wrap(err, "Error creating route") + } + err = json.Unmarshal(resBody, &routeResp) + if err != nil { + return RoutesResource{}, errors.Wrap(err, "Error unmarshalling routes") + } + routeResp.Entity.c = c + return routeResp, nil +} + +func (c *Client) DeleteRoute(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/routes/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting route %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) mergeRouteResource(rr RoutesResource) Route { + rr.Entity.Guid = rr.Meta.Guid + rr.Entity.CreatedAt = rr.Meta.CreatedAt + rr.Entity.UpdatedAt = rr.Meta.UpdatedAt + rr.Entity.c = c + return rr.Entity +} diff --git a/third_party/go-cfclient/routes_test.go b/third_party/go-cfclient/routes_test.go new file mode 100644 index 000000000000..84aafb279e82 --- /dev/null +++ b/third_party/go-cfclient/routes_test.go @@ -0,0 +1,200 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListRoutes(t *testing.T) { + Convey("List Routes", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/routes", []string{listRoutesPayloadPage1}, "", 200, "", nil}, + {"GET", "/v2/routes_page_2", []string{listRoutesPayloadPage2}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + routes, err := client.ListRoutes() + So(err, ShouldBeNil) + + So(len(routes), ShouldEqual, 2) + So(routes[0].Guid, ShouldEqual, "24707add-83b8-4fd8-a8f4-b7297199c805") + So(routes[0].Host, ShouldEqual, "test-1") + So(routes[0].Path, ShouldEqual, "/foo") + So(routes[0].DomainGuid, ShouldEqual, "0b183484-45cc-4855-94d4-892f80f20c13") + So(routes[0].SpaceGuid, ShouldEqual, "494d8b64-8181-4183-a6d3-6279db8fec6e") + So(routes[0].ServiceInstanceGuid, ShouldEqual, "") + So(routes[0].Port, ShouldEqual, 0) + }) +} + +func TestGetRouteByGuid(t *testing.T) { + Convey("Get Route", t, func() { + setup(MockRoute{"GET", "/v2/routes/49df626e-8e87-4366-9fbc-f82186824e21", []string{getRouteByGuidPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + route, err := client.GetRouteByGuid("49df626e-8e87-4366-9fbc-f82186824e21") + So(err, ShouldBeNil) + So(route.Guid, ShouldEqual, "49df626e-8e87-4366-9fbc-f82186824e21") + So(route.Host, ShouldEqual, "host") + So(route.SpaceGuid, ShouldEqual, "e374dd83-1cea-40d4-84dc-6ac4fb9b2145") + }) +} + +func TestCreateRoute(t *testing.T) { + Convey("Create HTTP Route", t, func() { + setup(MockRoute{"POST", "/v2/routes", []string{createRoute}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + routeRequest := RouteRequest{ + DomainGuid: "08167353-32da-4ed9-9ef5-aa7b31bbc009", + SpaceGuid: "b65a9a76-8c55-460b-9162-18b396da66cf", + Host: "foo-host", + } + + route, err := client.CreateRoute(routeRequest) + So(err, ShouldBeNil) + + So(route.Guid, ShouldEqual, "b3fe6f31-e897-4e02-b49e-263ca96b4e3a") + So(route.SpaceGuid, ShouldEqual, "b65a9a76-8c55-460b-9162-18b396da66cf") + So(route.DomainGuid, ShouldEqual, "08167353-32da-4ed9-9ef5-aa7b31bbc009") + So(route.Host, ShouldEqual, "foo-host") + So(route.Port, ShouldEqual, 0) + So(route.Path, ShouldEqual, "") + So(route.ServiceInstanceGuid, ShouldEqual, "") + }) +} + +func TestCreateTcpRoute(t *testing.T) { + Convey("Create TCP Route", t, func() { + setup(MockRoute{"POST", "/v2/routes", []string{createTcpRoute}, "", 201, "generate_port=true", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + routeRequest := RouteRequest{ + DomainGuid: "08167353-32da-4ed9-9ef5-aa7b31bbc009", + SpaceGuid: "b65a9a76-8c55-460b-9162-18b396da66cf", + } + + route, err := client.CreateTcpRoute(routeRequest) + So(err, ShouldBeNil) + + So(route.Guid, ShouldEqual, "78fe5006-1d1c-41ba-94de-eb7002241b82") + So(route.SpaceGuid, ShouldEqual, "b65a9a76-8c55-460b-9162-18b396da66cf") + So(route.DomainGuid, ShouldEqual, "08167353-32da-4ed9-9ef5-aa7b31bbc009") + So(route.Port, ShouldEqual, 1099) + }) +} + +func TestBindRoute(t *testing.T) { + Convey("Bind route", t, func() { + Convey("When a successful status code is returned", func() { + setup(MockRoute{"PUT", "/v2/routes/7803de15-a20f-4dea-bf17-37de56629582/apps/ce5d0e27-3048-4024-80cb-fafbae9c3161", []string{bindRoute}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.BindRoute("7803de15-a20f-4dea-bf17-37de56629582", "ce5d0e27-3048-4024-80cb-fafbae9c3161") + So(err, ShouldBeNil) + + }) + Convey("When an error status code is returned", func() { + setup(MockRoute{"PUT", "/v2/routes/7803de15-a20f-4dea-bf17-37de56629582/apps/ce5d0e27-3048-4024-80cb-fafbae9c3161", []string{""}, "", 400, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.BindRoute("7803de15-a20f-4dea-bf17-37de56629582", "ce5d0e27-3048-4024-80cb-fafbae9c3161") + So(err, ShouldNotBeNil) + So(err.Error(), ShouldStartWith, "Error binding route 7803de15-a20f-4dea-bf17-37de56629582 to app ce5d0e27-3048-4024-80cb-fafbae9c3161") + }) + }) +} + +func TestDeleteRoute(t *testing.T) { + Convey("Delete route", t, func() { + Convey("When a successful status code is returned", func() { + setup(MockRoute{"DELETE", "/v2/routes/a537761f-9d93-4b30-af17-3d73dbca181b", []string{""}, "", 204, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteRoute("a537761f-9d93-4b30-af17-3d73dbca181b") + So(err, ShouldBeNil) + }) + + Convey("When an error status code is returned", func() { + setup(MockRoute{"DELETE", "/v2/routes/a537761f-9d93-4b30-af17-3d73dbca181b", []string{""}, "", 404, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteRoute("a537761f-9d93-4b30-af17-3d73dbca181b") + So(err, ShouldNotBeNil) + }) + }) +} + +func TestRouteDomain(t *testing.T) { + Convey("Find route domain", t, func() { + setup(MockRoute{"GET", "/v2/private_domains/foobar", []string{getDomainPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + route := Route{ + Guid: "route-guid", + DomainURL: "/v2/private_domains/foobar", + c: client, + } + + domain, err := route.Domain() + So(err, ShouldBeNil) + So(domain.Guid, ShouldEqual, "369373be-7864-4bb9-a8ef-8274da1f8c8b") + So(domain.Name, ShouldEqual, "example.com") + }) +} diff --git a/third_party/go-cfclient/secgroups.go b/third_party/go-cfclient/secgroups.go new file mode 100644 index 000000000000..5d6db1e67fcf --- /dev/null +++ b/third_party/go-cfclient/secgroups.go @@ -0,0 +1,576 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "reflect" + "strings" + + "github.com/Masterminds/semver" + "github.com/pkg/errors" +) + +type SecGroupResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []SecGroupResource `json:"resources"` +} + +type SecGroupCreateResponse struct { + Code int `json:"code"` + ErrorCode string `json:"error_code"` + Description string `json:"description"` +} + +type SecGroupResource struct { + Meta Meta `json:"metadata"` + Entity SecGroup `json:"entity"` +} + +type SecGroup struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Rules []SecGroupRule `json:"rules"` + Running bool `json:"running_default"` + Staging bool `json:"staging_default"` + SpacesURL string `json:"spaces_url"` + StagingSpacesURL string `json:"staging_spaces_url"` + SpacesData []SpaceResource `json:"spaces"` + StagingSpacesData []SpaceResource `json:"staging_spaces"` + c *Client +} + +type SecGroupRule struct { + Protocol string `json:"protocol"` + Ports string `json:"ports,omitempty"` // e.g. "4000-5000,9142" + Destination string `json:"destination"` // CIDR Format + Description string `json:"description,omitempty"` // Optional description + Code int `json:"code"` // ICMP code + Type int `json:"type"` // ICMP type. Only valid if Protocol=="icmp" + Log bool `json:"log,omitempty"` // If true, log this rule +} + +var MinStagingSpacesVersion *semver.Version = getMinStagingSpacesVersion() + +func (c *Client) ListSecGroups() (secGroups []SecGroup, err error) { + requestURL := "/v2/security_groups?inline-relations-depth=1" + for requestURL != "" { + var secGroupResp SecGroupResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting sec groups") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading sec group response body") + } + + err = json.Unmarshal(resBody, &secGroupResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling sec group") + } + + for _, secGroup := range secGroupResp.Resources { + secGroup.Entity.Guid = secGroup.Meta.Guid + secGroup.Entity.CreatedAt = secGroup.Meta.CreatedAt + secGroup.Entity.UpdatedAt = secGroup.Meta.UpdatedAt + secGroup.Entity.c = c + for i, space := range secGroup.Entity.SpacesData { + space.Entity.Guid = space.Meta.Guid + secGroup.Entity.SpacesData[i] = space + } + if len(secGroup.Entity.SpacesData) == 0 { + spaces, err := secGroup.Entity.ListSpaceResources() + if err != nil { + return nil, err + } + secGroup.Entity.SpacesData = append(secGroup.Entity.SpacesData, spaces...) + } + if len(secGroup.Entity.StagingSpacesData) == 0 { + spaces, err := secGroup.Entity.ListStagingSpaceResources() + if err != nil { + return nil, err + } + secGroup.Entity.StagingSpacesData = append(secGroup.Entity.SpacesData, spaces...) + } + secGroups = append(secGroups, secGroup.Entity) + } + + requestURL = secGroupResp.NextUrl + resp.Body.Close() + } + return secGroups, nil +} + +func (c *Client) ListRunningSecGroups() ([]SecGroup, error) { + secGroups := make([]SecGroup, 0) + requestURL := "/v2/config/running_security_groups" + for requestURL != "" { + var secGroupResp SecGroupResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + + if err != nil { + return nil, errors.Wrap(err, "Error requesting sec groups") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading sec group response body") + } + + err = json.Unmarshal(resBody, &secGroupResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling sec group") + } + + for _, secGroup := range secGroupResp.Resources { + secGroup.Entity.Guid = secGroup.Meta.Guid + secGroup.Entity.CreatedAt = secGroup.Meta.CreatedAt + secGroup.Entity.UpdatedAt = secGroup.Meta.UpdatedAt + secGroup.Entity.c = c + + secGroups = append(secGroups, secGroup.Entity) + } + + requestURL = secGroupResp.NextUrl + resp.Body.Close() + } + return secGroups, nil +} + +func (c *Client) ListStagingSecGroups() ([]SecGroup, error) { + secGroups := make([]SecGroup, 0) + requestURL := "/v2/config/staging_security_groups" + for requestURL != "" { + var secGroupResp SecGroupResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + + if err != nil { + return nil, errors.Wrap(err, "Error requesting sec groups") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading sec group response body") + } + + err = json.Unmarshal(resBody, &secGroupResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling sec group") + } + + for _, secGroup := range secGroupResp.Resources { + secGroup.Entity.Guid = secGroup.Meta.Guid + secGroup.Entity.CreatedAt = secGroup.Meta.CreatedAt + secGroup.Entity.UpdatedAt = secGroup.Meta.UpdatedAt + secGroup.Entity.c = c + + secGroups = append(secGroups, secGroup.Entity) + } + + requestURL = secGroupResp.NextUrl + resp.Body.Close() + } + return secGroups, nil +} + +func (c *Client) GetSecGroupByName(name string) (secGroup SecGroup, err error) { + requestURL := "/v2/security_groups?q=name:" + name + var secGroupResp SecGroupResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + + if err != nil { + return secGroup, errors.Wrap(err, "Error requesting sec groups") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return secGroup, errors.Wrap(err, "Error reading sec group response body") + } + + err = json.Unmarshal(resBody, &secGroupResp) + if err != nil { + return secGroup, errors.Wrap(err, "Error unmarshaling sec group") + } + if len(secGroupResp.Resources) == 0 { + cfErr := NewSecurityGroupNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, name) + return secGroup, cfErr + } + secGroup = secGroupResp.Resources[0].Entity + secGroup.Guid = secGroupResp.Resources[0].Meta.Guid + secGroup.CreatedAt = secGroupResp.Resources[0].Meta.CreatedAt + secGroup.UpdatedAt = secGroupResp.Resources[0].Meta.UpdatedAt + secGroup.c = c + + resp.Body.Close() + return secGroup, nil +} + +func (secGroup *SecGroup) ListSpaceResources() ([]SpaceResource, error) { + var spaceResources []SpaceResource + requestURL := secGroup.SpacesURL + for requestURL != "" { + spaceResp, err := secGroup.c.getSpaceResponse(requestURL) + if err != nil { + return []SpaceResource{}, err + } + for i, spaceRes := range spaceResp.Resources { + spaceRes.Entity.Guid = spaceRes.Meta.Guid + spaceRes.Entity.CreatedAt = spaceRes.Meta.CreatedAt + spaceRes.Entity.UpdatedAt = spaceRes.Meta.UpdatedAt + spaceResp.Resources[i] = spaceRes + } + spaceResources = append(spaceResources, spaceResp.Resources...) + requestURL = spaceResp.NextUrl + } + return spaceResources, nil +} + +func (secGroup *SecGroup) ListStagingSpaceResources() ([]SpaceResource, error) { + var spaceResources []SpaceResource + requestURL := secGroup.StagingSpacesURL + for requestURL != "" { + spaceResp, err := secGroup.c.getSpaceResponse(requestURL) + if err != nil { + // if this is a 404, let's make sure that it's not because we're on a legacy system + if cause := errors.Cause(err); cause != nil { + if httpErr, ok := cause.(CloudFoundryHTTPError); ok { + if httpErr.StatusCode == http.StatusNotFound { + info, infoErr := secGroup.c.GetInfo() + if infoErr != nil { + return nil, infoErr + } + + apiVersion, versionErr := semver.NewVersion(info.APIVersion) + if versionErr != nil { + return nil, versionErr + } + + if MinStagingSpacesVersion.GreaterThan(apiVersion) { + // this is probably not really an error, we're just trying to use a non-existent api + return nil, nil + } + } + } + } + + return []SpaceResource{}, err + } + for i, spaceRes := range spaceResp.Resources { + spaceRes.Entity.Guid = spaceRes.Meta.Guid + spaceRes.Entity.CreatedAt = spaceRes.Meta.CreatedAt + spaceRes.Entity.UpdatedAt = spaceRes.Meta.UpdatedAt + spaceResp.Resources[i] = spaceRes + } + spaceResources = append(spaceResources, spaceResp.Resources...) + requestURL = spaceResp.NextUrl + } + return spaceResources, nil +} + +/* +CreateSecGroup contacts the CF endpoint for creating a new security group. +name: the name to give to the created security group +rules: A slice of rule objects that describe the rules that this security group enforces. + This can technically be nil or an empty slice - we won't judge you +spaceGuids: The security group will be associated with the spaces specified by the contents of this slice. + If nil, the security group will not be associated with any spaces initially. +*/ +func (c *Client) CreateSecGroup(name string, rules []SecGroupRule, spaceGuids []string) (*SecGroup, error) { + return c.secGroupCreateHelper("/v2/security_groups", "POST", name, rules, spaceGuids) +} + +/* +UpdateSecGroup contacts the CF endpoint to update an existing security group. +guid: identifies the security group that you would like to update. +name: the new name to give to the security group +rules: A slice of rule objects that describe the rules that this security group enforces. + If this is left nil, the rules will not be changed. +spaceGuids: The security group will be associated with the spaces specified by the contents of this slice. + If nil, the space associations will not be changed. +*/ +func (c *Client) UpdateSecGroup(guid, name string, rules []SecGroupRule, spaceGuids []string) (*SecGroup, error) { + return c.secGroupCreateHelper("/v2/security_groups/"+guid, "PUT", name, rules, spaceGuids) +} + +/* +DeleteSecGroup contacts the CF endpoint to delete an existing security group. +guid: Indentifies the security group to be deleted. +*/ +func (c *Client) DeleteSecGroup(guid string) error { + // Perform the DELETE and check for errors + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/security_groups/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +GetSecGroup contacts the CF endpoint for fetching the info for a particular security group. +guid: Identifies the security group to fetch information from +*/ +func (c *Client) GetSecGroup(guid string) (*SecGroup, error) { + // Perform the GET and check for errors + resp, err := c.DoRequest(c.NewRequest("GET", "/v2/security_groups/"+guid)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + // get the json out of the response body + return respBodyToSecGroup(resp.Body, c) +} + +/* +BindSecGroup contacts the CF endpoint to associate a space with a security group +secGUID: identifies the security group to add a space to +spaceGUID: identifies the space to associate +*/ +func (c *Client) BindSecGroup(secGUID, spaceGUID string) error { + // Perform the PUT and check for errors + resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/security_groups/%s/spaces/%s", secGUID, spaceGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +BindSpaceStagingSecGroup contacts the CF endpoint to associate a space with a security group for staging functions only +secGUID: identifies the security group to add a space to +spaceGUID: identifies the space to associate +*/ +func (c *Client) BindStagingSecGroupToSpace(secGUID, spaceGUID string) error { + // Perform the PUT and check for errors + resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/security_groups/%s/staging_spaces/%s", secGUID, spaceGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +BindRunningSecGroup contacts the CF endpoint to associate a security group +secGUID: identifies the security group to add a space to +*/ +func (c *Client) BindRunningSecGroup(secGUID string) error { + // Perform the PUT and check for errors + resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/config/running_security_groups/%s", secGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +UnbindRunningSecGroup contacts the CF endpoint to dis-associate a security group +secGUID: identifies the security group to add a space to +*/ +func (c *Client) UnbindRunningSecGroup(secGUID string) error { + // Perform the DELETE and check for errors + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/config/running_security_groups/%s", secGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +BindStagingSecGroup contacts the CF endpoint to associate a space with a security group +secGUID: identifies the security group to add a space to +*/ +func (c *Client) BindStagingSecGroup(secGUID string) error { + // Perform the PUT and check for errors + resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/config/staging_security_groups/%s", secGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +UnbindStagingSecGroup contacts the CF endpoint to dis-associate a space with a security group +secGUID: identifies the security group to add a space to +*/ +func (c *Client) UnbindStagingSecGroup(secGUID string) error { + // Perform the DELETE and check for errors + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/config/staging_security_groups/%s", secGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +UnbindSecGroup contacts the CF endpoint to dissociate a space from a security group +secGUID: identifies the security group to remove a space from +spaceGUID: identifies the space to dissociate from the security group +*/ +func (c *Client) UnbindSecGroup(secGUID, spaceGUID string) error { + // Perform the DELETE and check for errors + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/security_groups/%s/spaces/%s", secGUID, spaceGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +// Reads most security group response bodies into a SecGroup object +func respBodyToSecGroup(body io.ReadCloser, c *Client) (*SecGroup, error) { + // get the json from the response body + bodyRaw, err := ioutil.ReadAll(body) + if err != nil { + return nil, errors.Wrap(err, "Could not read response body") + } + jStruct := SecGroupResource{} + // make it a SecGroup + err = json.Unmarshal(bodyRaw, &jStruct) + if err != nil { + return nil, errors.Wrap(err, "Could not unmarshal response body as json") + } + // pull a few extra fields from other places + ret := jStruct.Entity + ret.Guid = jStruct.Meta.Guid + ret.CreatedAt = jStruct.Meta.CreatedAt + ret.UpdatedAt = jStruct.Meta.UpdatedAt + ret.c = c + return &ret, nil +} + +func convertStructToMap(st interface{}) map[string]interface{} { + reqRules := make(map[string]interface{}) + + v := reflect.ValueOf(st) + t := reflect.TypeOf(st) + + for i := 0; i < v.NumField(); i++ { + key := strings.ToLower(t.Field(i).Name) + typ := v.FieldByName(t.Field(i).Name).Kind().String() + structTag := t.Field(i).Tag.Get("json") + jsonName := strings.TrimSpace(strings.Split(structTag, ",")[0]) + value := v.FieldByName(t.Field(i).Name) + + // if jsonName is not empty use it for the key + if jsonName != "" { + key = jsonName + } + + switch typ { + case "string": + if !(value.String() == "" && strings.Contains(structTag, "omitempty")) { + reqRules[key] = value.String() + } + case "int": + reqRules[key] = value.Int() + default: + reqRules[key] = value.Interface() + } + } + + return reqRules +} + +// Create and Update secGroup pretty much do the same thing, so this function abstracts those out. +func (c *Client) secGroupCreateHelper(url, method, name string, rules []SecGroupRule, spaceGuids []string) (*SecGroup, error) { + reqRules := make([]map[string]interface{}, len(rules)) + + for i, rule := range rules { + reqRules[i] = convertStructToMap(rule) + protocol := strings.ToLower(reqRules[i]["protocol"].(string)) + + // if not icmp protocol need to remove the Code/Type fields + if protocol != "icmp" { + delete(reqRules[i], "code") + delete(reqRules[i], "type") + } + } + + req := c.NewRequest(method, url) + // set up request body + inputs := map[string]interface{}{ + "name": name, + "rules": reqRules, + } + + if spaceGuids != nil { + inputs["space_guids"] = spaceGuids + } + req.obj = inputs + // fire off the request and check for problems + resp, err := c.DoRequest(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { // Both create and update should give 201 CREATED + var response SecGroupCreateResponse + + bodyRaw, _ := ioutil.ReadAll(resp.Body) + + err = json.Unmarshal(bodyRaw, &response) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling response") + } + + return nil, fmt.Errorf(`Request failed CF API returned with status code %d +------------------------------- +Error Code %s +Code %d +Description %s`, + resp.StatusCode, response.ErrorCode, response.Code, response.Description) + } + // get the json from the response body + return respBodyToSecGroup(resp.Body, c) +} + +func getMinStagingSpacesVersion() *semver.Version { + v, _ := semver.NewVersion("2.68.0") + return v +} diff --git a/third_party/go-cfclient/secgroups_test.go b/third_party/go-cfclient/secgroups_test.go new file mode 100644 index 000000000000..051fd47266b8 --- /dev/null +++ b/third_party/go-cfclient/secgroups_test.go @@ -0,0 +1,256 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListSecGroups(t *testing.T) { + Convey("List SecGroups", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/security_groups", []string{listSecGroupsPayload}, "", 200, "inline-relations-depth=1", nil}, + {"GET", "/v2/security_groupsPage2", []string{listSecGroupsPayloadPage2}, "", 200, "", nil}, + {"GET", "/v2/security_groups/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c/spaces", []string{emptyResources}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + secGroups, err := client.ListSecGroups() + So(err, ShouldBeNil) + + So(len(secGroups), ShouldEqual, 2) + So(secGroups[0].Guid, ShouldEqual, "af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c") + So(secGroups[0].Name, ShouldEqual, "secgroup-test") + So(secGroups[0].Running, ShouldEqual, true) + So(secGroups[0].Staging, ShouldEqual, true) + So(secGroups[0].Rules[0].Protocol, ShouldEqual, "tcp") + So(secGroups[0].Rules[0].Ports, ShouldEqual, "443,4443") + So(secGroups[0].Rules[0].Destination, ShouldEqual, "1.1.1.1") + So(secGroups[0].Rules[1].Protocol, ShouldEqual, "udp") + So(secGroups[0].Rules[1].Ports, ShouldEqual, "1111") + So(secGroups[0].Rules[1].Destination, ShouldEqual, "1.2.3.4") + So(secGroups[0].SpacesURL, ShouldEqual, "/v2/security_groups/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c/spaces") + So(secGroups[0].SpacesData, ShouldBeEmpty) + So(secGroups[1].Guid, ShouldEqual, "f9ad202b-76dd-44ec-b7c2-fd2417a561e8") + So(secGroups[1].Name, ShouldEqual, "secgroup-test2") + So(secGroups[1].Running, ShouldEqual, false) + So(secGroups[1].Staging, ShouldEqual, false) + So(secGroups[1].Rules[0].Protocol, ShouldEqual, "udp") + So(secGroups[1].Rules[0].Ports, ShouldEqual, "2222") + So(secGroups[1].Rules[0].Destination, ShouldEqual, "2.2.2.2") + So(secGroups[1].Rules[1].Protocol, ShouldEqual, "tcp") + So(secGroups[1].Rules[1].Ports, ShouldEqual, "443,4443") + So(secGroups[1].Rules[1].Destination, ShouldEqual, "4.3.2.1") + So(secGroups[1].SpacesData[0].Entity.Guid, ShouldEqual, "e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4") + So(secGroups[1].SpacesData[0].Entity.Name, ShouldEqual, "space-test") + So(secGroups[1].SpacesData[1].Entity.Guid, ShouldEqual, "a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333") + So(secGroups[1].SpacesData[1].Entity.Name, ShouldEqual, "space-test2") + So(secGroups[1].SpacesData[2].Entity.Guid, ShouldEqual, "c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1") + So(secGroups[1].SpacesData[2].Entity.Name, ShouldEqual, "space-test3") + }) +} + +func TestSecGroupListSpaceResources(t *testing.T) { + Convey("List Space Resources", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/security_groups/123/spaces", []string{listSpacesPayload}, "", 200, "", nil}, + {"GET", "/v2/spacesPage2", []string{listSpacesPayloadPage2}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + secGroup := &SecGroup{ + Guid: "123", + Name: "test-sec-group", + SpacesURL: "/v2/security_groups/123/spaces", + c: client, + } + spaces, err := secGroup.ListSpaceResources() + So(err, ShouldBeNil) + + So(len(spaces), ShouldEqual, 4) + So(spaces[0].Entity.Guid, ShouldEqual, "8efd7c5c-d83c-4786-b399-b7bd548839e1") + So(spaces[0].Entity.Name, ShouldEqual, "dev") + So(spaces[1].Entity.Guid, ShouldEqual, "657b5923-7de0-486a-9928-b4d78ee24931") + So(spaces[1].Entity.Name, ShouldEqual, "demo") + So(spaces[2].Entity.Guid, ShouldEqual, "9ffd7c5c-d83c-4786-b399-b7bd54883977") + So(spaces[2].Entity.Name, ShouldEqual, "test") + So(spaces[3].Entity.Guid, ShouldEqual, "329b5923-7de0-486a-9928-b4d78ee24982") + So(spaces[3].Entity.Name, ShouldEqual, "prod") + }) +} + +func TestBindStagingSecGroupToSpaces(t *testing.T) { + Convey("Associate a security group to a space for staging", t, func() { + mocks := []MockRoute{ + {"PUT", "/v2/security_groups/8efd7c5c-d83c-4786-b399-b7bd548839e1/staging_spaces/329b5923-7de0-486a-9928-b4d78ee24982", []string{""}, "", 201, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.BindStagingSecGroupToSpace("8efd7c5c-d83c-4786-b399-b7bd548839e1", "329b5923-7de0-486a-9928-b4d78ee24982") + So(err, ShouldBeNil) + }) +} + +func TestNegativeBindStagingSecGroupToSpaces(t *testing.T) { + Convey("Try to associate a security group to a space for staging on a pre-2.68.0 API", t, func() { + mocks := []MockRoute{ + {"PUT", "/v2/security_groups/8efd7c5c-d83c-4786-b399-b7bd548839e1/staging_spaces/329b5923-7de0-486a-9928-b4d78ee24982", []string{""}, "", 404, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.BindStagingSecGroupToSpace("8efd7c5c-d83c-4786-b399-b7bd548839e1", "329b5923-7de0-486a-9928-b4d78ee24982") + So(err, ShouldNotBeNil) + }) +} + +func TestListRunningSecGroups(t *testing.T) { + Convey("List Running SecGroups", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/config/running_security_groups", []string{listRunningSecGroupsPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + secGroups, err := client.ListRunningSecGroups() + So(err, ShouldBeNil) + + So(len(secGroups), ShouldEqual, 1) + for i := range secGroups { + So(secGroups[i].Running, ShouldBeTrue) + } + }) +} + +func TestListStagingSecGroups(t *testing.T) { + Convey("List Staging SecGroups", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/config/staging_security_groups", []string{listStagingSecGroupsPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + secGroups, err := client.ListStagingSecGroups() + So(err, ShouldBeNil) + + So(len(secGroups), ShouldEqual, 1) + for i := range secGroups { + So(secGroups[i].Staging, ShouldBeTrue) + } + }) +} + +func TestBindRunningSecGroups(t *testing.T) { + Convey("Unbind Running Sec Groups", t, func() { + mocks := []MockRoute{ + {"PUT", "/v2/config/running_security_groups/8efd7c5c-d83c-4786-b399-b7bd548839e1", []string{""}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.BindRunningSecGroup("8efd7c5c-d83c-4786-b399-b7bd548839e1") + So(err, ShouldBeNil) + }) +} + +func TestUnbindRunningSecGroups(t *testing.T) { + Convey("Unbind Running Sec Groups", t, func() { + mocks := []MockRoute{ + {"DELETE", "/v2/config/running_security_groups/8efd7c5c-d83c-4786-b399-b7bd548839e1", []string{""}, "", 204, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.UnbindRunningSecGroup("8efd7c5c-d83c-4786-b399-b7bd548839e1") + So(err, ShouldBeNil) + }) +} + +func TestBindStagingSecGroups(t *testing.T) { + Convey("Unbind Staging Sec Groups", t, func() { + mocks := []MockRoute{ + {"PUT", "/v2/config/staging_security_groups/8efd7c5c-d83c-4786-b399-b7bd548839e1", []string{""}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.BindStagingSecGroup("8efd7c5c-d83c-4786-b399-b7bd548839e1") + So(err, ShouldBeNil) + }) +} + +func TestUnbindStagingSecGroups(t *testing.T) { + Convey("Unbind Staging Sec Groups", t, func() { + mocks := []MockRoute{ + {"DELETE", "/v2/config/staging_security_groups/8efd7c5c-d83c-4786-b399-b7bd548839e1", []string{""}, "", 204, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.UnbindStagingSecGroup("8efd7c5c-d83c-4786-b399-b7bd548839e1") + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/service_bindings.go b/third_party/go-cfclient/service_bindings.go new file mode 100644 index 000000000000..b495897e8471 --- /dev/null +++ b/third_party/go-cfclient/service_bindings.go @@ -0,0 +1,181 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type ServiceBindingsResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + Resources []ServiceBindingResource `json:"resources"` + NextUrl string `json:"next_url"` +} + +type ServiceBindingResource struct { + Meta Meta `json:"metadata"` + Entity ServiceBinding `json:"entity"` +} + +type ServiceBinding struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + AppGuid string `json:"app_guid"` + ServiceInstanceGuid string `json:"service_instance_guid"` + Credentials interface{} `json:"credentials"` + BindingOptions interface{} `json:"binding_options"` + GatewayData interface{} `json:"gateway_data"` + GatewayName string `json:"gateway_name"` + SyslogDrainUrl string `json:"syslog_drain_url"` + VolumeMounts interface{} `json:"volume_mounts"` + AppUrl string `json:"app_url"` + ServiceInstanceUrl string `json:"service_instance_url"` + c *Client +} + +func (c *Client) ListServiceBindingsByQuery(query url.Values) ([]ServiceBinding, error) { + var serviceBindings []ServiceBinding + requestUrl := "/v2/service_bindings?" + query.Encode() + + for { + var serviceBindingsResp ServiceBindingsResponse + + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting service bindings") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading service bindings request:") + } + + err = json.Unmarshal(resBody, &serviceBindingsResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling service bindings") + } + for _, serviceBinding := range serviceBindingsResp.Resources { + serviceBinding.Entity.Guid = serviceBinding.Meta.Guid + serviceBinding.Entity.CreatedAt = serviceBinding.Meta.CreatedAt + serviceBinding.Entity.UpdatedAt = serviceBinding.Meta.UpdatedAt + serviceBinding.Entity.c = c + serviceBindings = append(serviceBindings, serviceBinding.Entity) + } + requestUrl = serviceBindingsResp.NextUrl + if requestUrl == "" { + break + } + } + + return serviceBindings, nil +} + +func (c *Client) ListServiceBindings() ([]ServiceBinding, error) { + return c.ListServiceBindingsByQuery(nil) +} + +func (c *Client) GetServiceBindingByGuid(guid string) (ServiceBinding, error) { + var serviceBinding ServiceBindingResource + r := c.NewRequest("GET", "/v2/service_bindings/"+url.QueryEscape(guid)) + resp, err := c.DoRequest(r) + if err != nil { + return ServiceBinding{}, errors.Wrap(err, "Error requesting serving binding") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServiceBinding{}, errors.Wrap(err, "Error reading service binding response body") + } + err = json.Unmarshal(resBody, &serviceBinding) + if err != nil { + return ServiceBinding{}, errors.Wrap(err, "Error unmarshalling service binding") + } + serviceBinding.Entity.Guid = serviceBinding.Meta.Guid + serviceBinding.Entity.CreatedAt = serviceBinding.Meta.CreatedAt + serviceBinding.Entity.UpdatedAt = serviceBinding.Meta.UpdatedAt + serviceBinding.Entity.c = c + return serviceBinding.Entity, nil +} + +func (c *Client) ServiceBindingByGuid(guid string) (ServiceBinding, error) { + return c.GetServiceBindingByGuid(guid) +} + +func (c *Client) DeleteServiceBinding(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/service_bindings/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting service binding %s, response code %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) CreateServiceBinding(appGUID, serviceInstanceGUID string) (*ServiceBinding, error) { + req := c.NewRequest("POST", "/v2/service_bindings") + req.obj = map[string]interface{}{ + "app_guid": appGUID, + "service_instance_guid": serviceInstanceGUID, + } + resp, err := c.DoRequest(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, errors.Wrapf(err, "Error binding app %s to service instance %s, response code %d", appGUID, serviceInstanceGUID, resp.StatusCode) + } + return c.handleServiceBindingResp(resp) +} + +func (c *Client) CreateRouteServiceBinding(routeGUID, serviceInstanceGUID string) error { + req := c.NewRequest("PUT", fmt.Sprintf("/v2/user_provided_service_instances/%s/routes/%s", serviceInstanceGUID, routeGUID)) + resp, err := c.DoRequest(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return errors.Wrapf(err, "Error binding route %s to service instance %s, response code %d", routeGUID, serviceInstanceGUID, resp.StatusCode) + } + return nil +} + +func (c *Client) DeleteRouteServiceBinding(routeGUID, serviceInstanceGUID string) error { + req := c.NewRequest("DELETE", fmt.Sprintf("/v2/service_instances/%s/routes/%s", serviceInstanceGUID, routeGUID)) + resp, err := c.DoRequest(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "Error deleting bound route %s from service instance %s, response code %d", routeGUID, serviceInstanceGUID, resp.StatusCode) + } + return nil +} + +func (c *Client) handleServiceBindingResp(resp *http.Response) (*ServiceBinding, error) { + defer resp.Body.Close() + var sb ServiceBindingResource + err := json.NewDecoder(resp.Body).Decode(&sb) + if err != nil { + return nil, err + } + return c.mergeServiceBindingResource(sb), nil +} + +func (c *Client) mergeServiceBindingResource(serviceBinding ServiceBindingResource) *ServiceBinding { + serviceBinding.Entity.Guid = serviceBinding.Meta.Guid + serviceBinding.Entity.c = c + return &serviceBinding.Entity +} diff --git a/third_party/go-cfclient/service_bindings_test.go b/third_party/go-cfclient/service_bindings_test.go new file mode 100644 index 000000000000..7b028356e9ba --- /dev/null +++ b/third_party/go-cfclient/service_bindings_test.go @@ -0,0 +1,158 @@ +package cfclient + +import ( + "net/http" + "reflect" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListServiceBindings(t *testing.T) { + Convey("List Service Bindings", t, func() { + mocks := []MockRoute{ + { + Method: "GET", + Endpoint: "/v2/service_bindings", + Output: []string{listServiceBindingsPayloadPage1}, + Status: 200, + }, + { + Method: "GET", + Endpoint: "/v2/service_bindings2", + Output: []string{listServiceBindingsPayloadPage2}, + Status: 200, + }, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + serviceBindings, err := client.ListServiceBindings() + So(err, ShouldBeNil) + + So(len(serviceBindings), ShouldEqual, 2) + So(serviceBindings[0].Guid, ShouldEqual, "aa599bb3-4811-405a-bbe3-a68c7c55afc8") + So(serviceBindings[0].AppGuid, ShouldEqual, "b26e7e98-f002-41a8-a663-1b60f808a92a") + So(serviceBindings[0].ServiceInstanceGuid, ShouldEqual, "bde206e0-1ee8-48ad-b794-44c857633d50") + So(reflect.DeepEqual( + serviceBindings[0].Credentials, + map[string]interface{}{"creds-key-66": "creds-val-66"}), ShouldBeTrue) + So(serviceBindings[0].BindingOptions, ShouldBeEmpty) + So(serviceBindings[0].GatewayData, ShouldBeNil) + So(serviceBindings[0].GatewayName, ShouldEqual, "") + So(serviceBindings[0].SyslogDrainUrl, ShouldEqual, "") + So(serviceBindings[0].VolumeMounts, ShouldBeEmpty) + So(serviceBindings[0].AppUrl, ShouldEqual, "/v2/apps/b26e7e98-f002-41a8-a663-1b60f808a92a") + So(serviceBindings[0].ServiceInstanceUrl, ShouldEqual, "/v2/service_instances/bde206e0-1ee8-48ad-b794-44c857633d50") + So(serviceBindings[1].Guid, ShouldEqual, "8201b87d-b273-4fdf-8dd4-4b42ce970cc7") + So(serviceBindings[1].AppGuid, ShouldEqual, "636bbf83-5b54-488d-9528-066f680a99dc") + So(serviceBindings[1].ServiceInstanceGuid, ShouldEqual, "c3023201-44f5-4dc8-a903-c69c9eba9809") + So(reflect.DeepEqual( + serviceBindings[1].Credentials, + map[string]interface{}{"creds-key-66": "creds-val-66"}), ShouldBeTrue) + So(serviceBindings[1].BindingOptions, ShouldBeEmpty) + So(serviceBindings[1].GatewayData, ShouldBeNil) + So(serviceBindings[1].GatewayName, ShouldEqual, "") + So(serviceBindings[1].SyslogDrainUrl, ShouldEqual, "") + So(serviceBindings[1].VolumeMounts, ShouldBeEmpty) + So(serviceBindings[1].AppUrl, ShouldEqual, "/v2/apps/636bbf83-5b54-488d-9528-066f680a99dc") + So(serviceBindings[1].ServiceInstanceUrl, ShouldEqual, "/v2/service_instances/c3023201-44f5-4dc8-a903-c69c9eba9809") + + }) +} +func TestServiceBindingByGuid(t *testing.T) { + Convey("Service Binding By Guid", t, func() { + setup(MockRoute{"GET", "/v2/service_bindings/foo-bar-baz", []string{serviceBindingByGuidPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + serviceBinding, err := client.GetServiceBindingByGuid("foo-bar-baz") + So(err, ShouldBeNil) + + So(serviceBinding.Guid, ShouldEqual, "foo-bar-baz") + So(serviceBinding.AppGuid, ShouldEqual, "app-bar-baz") + }) +} + +func TestDeleteServiceBinding(t *testing.T) { + Convey("Delete service binding", t, func() { + setup(MockRoute{"DELETE", "/v2/service_bindings/guid", []string{""}, "", http.StatusNoContent, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteServiceBinding("guid") + So(err, ShouldBeNil) + }) +} + +func TestCreateServiceBinding(t *testing.T) { + Convey("Create service binding", t, func() { + body := `{"app_guid":"app-guid","service_instance_guid":"service-instance-guid"}` + setup(MockRoute{"POST", "/v2/service_bindings", []string{postServiceBindingPayload}, "", http.StatusCreated, "", &body}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + binding, err := client.CreateServiceBinding("app-guid", "service-instance-guid") + So(err, ShouldBeNil) + So(binding.Guid, ShouldEqual, "4e690cd4-66ef-4052-a23d-0d748316f18c") + So(binding.AppGuid, ShouldEqual, "081d55a0-1bfa-4e51-8d08-273f764988db") + So(binding.ServiceInstanceGuid, ShouldEqual, "a0029c76-7017-4a74-94b0-54a04ad94b80") + }) +} + +func TestCreateRouteServiceBinding(t *testing.T) { + Convey("Create route service binding", t, func() { + setup(MockRoute{"PUT", "/v2/user_provided_service_instances/5badd282-6e07-4fc6-a8c4-78be99040774/routes/237d9236-7997-4b1a-be8d-2aaf2d85421a", []string{""}, "", http.StatusCreated, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.CreateRouteServiceBinding("237d9236-7997-4b1a-be8d-2aaf2d85421a", "5badd282-6e07-4fc6-a8c4-78be99040774") + So(err, ShouldBeNil) + }) +} + +func TestDeleteRouteServiceBinding(t *testing.T) { + Convey("Delete route service binding", t, func() { + setup(MockRoute{"DELETE", "/v2/service_instances/5badd282-6e07-4fc6-a8c4-78be99040774/routes/237d9236-7997-4b1a-be8d-2aaf2d85421a", []string{""}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteRouteServiceBinding("237d9236-7997-4b1a-be8d-2aaf2d85421a", "5badd282-6e07-4fc6-a8c4-78be99040774") + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/service_brokers.go b/third_party/go-cfclient/service_brokers.go new file mode 100644 index 000000000000..a3392b2fae30 --- /dev/null +++ b/third_party/go-cfclient/service_brokers.go @@ -0,0 +1,208 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type ServiceBrokerResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []ServiceBrokerResource `json:"resources"` +} + +type ServiceBrokerResource struct { + Meta Meta `json:"metadata"` + Entity ServiceBroker `json:"entity"` +} + +type UpdateServiceBrokerRequest struct { + Name string `json:"name"` + BrokerURL string `json:"broker_url"` + Username string `json:"auth_username"` + Password string `json:"auth_password"` +} + +type CreateServiceBrokerRequest struct { + Name string `json:"name"` + BrokerURL string `json:"broker_url"` + Username string `json:"auth_username"` + Password string `json:"auth_password"` + SpaceGUID string `json:"space_guid,omitempty"` +} + +type ServiceBroker struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + BrokerURL string `json:"broker_url"` + Username string `json:"auth_username"` + Password string `json:"auth_password"` + SpaceGUID string `json:"space_guid,omitempty"` +} + +func (c *Client) DeleteServiceBroker(guid string) error { + requestURL := fmt.Sprintf("/v2/service_brokers/%s", guid) + r := c.NewRequest("DELETE", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting service broker %s, response code: %d", guid, resp.StatusCode) + } + return nil + +} + +func (c *Client) UpdateServiceBroker(guid string, usb UpdateServiceBrokerRequest) (ServiceBroker, error) { + var serviceBrokerResource ServiceBrokerResource + + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(usb) + if err != nil { + return ServiceBroker{}, err + } + req := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/service_brokers/%s", guid), buf) + resp, err := c.DoRequest(req) + if err != nil { + return ServiceBroker{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return ServiceBroker{}, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServiceBroker{}, err + } + err = json.Unmarshal(body, &serviceBrokerResource) + if err != nil { + return ServiceBroker{}, err + } + serviceBrokerResource.Entity.Guid = serviceBrokerResource.Meta.Guid + return serviceBrokerResource.Entity, nil +} + +func (c *Client) CreateServiceBroker(csb CreateServiceBrokerRequest) (ServiceBroker, error) { + var serviceBrokerResource ServiceBrokerResource + + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(csb) + if err != nil { + return ServiceBroker{}, err + } + req := c.NewRequestWithBody("POST", "/v2/service_brokers", buf) + resp, err := c.DoRequest(req) + if err != nil { + return ServiceBroker{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return ServiceBroker{}, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServiceBroker{}, err + } + err = json.Unmarshal(body, &serviceBrokerResource) + if err != nil { + return ServiceBroker{}, err + } + + serviceBrokerResource.Entity.Guid = serviceBrokerResource.Meta.Guid + return serviceBrokerResource.Entity, nil +} + +func (c *Client) ListServiceBrokersByQuery(query url.Values) ([]ServiceBroker, error) { + var sbs []ServiceBroker + requestURL := "/v2/service_brokers?" + query.Encode() + for { + serviceBrokerResp, err := c.getServiceBrokerResponse(requestURL) + if err != nil { + return []ServiceBroker{}, err + } + for _, sb := range serviceBrokerResp.Resources { + sb.Entity.Guid = sb.Meta.Guid + sb.Entity.CreatedAt = sb.Meta.CreatedAt + sb.Entity.UpdatedAt = sb.Meta.UpdatedAt + sbs = append(sbs, sb.Entity) + } + requestURL = serviceBrokerResp.NextUrl + if requestURL == "" { + break + } + } + return sbs, nil +} + +func (c *Client) ListServiceBrokers() ([]ServiceBroker, error) { + return c.ListServiceBrokersByQuery(nil) +} + +func (c *Client) GetServiceBrokerByGuid(guid string) (ServiceBroker, error) { + var serviceBrokerRes ServiceBrokerResource + r := c.NewRequest("GET", "/v2/service_brokers/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return ServiceBroker{}, err + } + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return ServiceBroker{}, err + } + err = json.Unmarshal(body, &serviceBrokerRes) + if err != nil { + return ServiceBroker{}, err + } + serviceBrokerRes.Entity.Guid = serviceBrokerRes.Meta.Guid + serviceBrokerRes.Entity.CreatedAt = serviceBrokerRes.Meta.CreatedAt + serviceBrokerRes.Entity.UpdatedAt = serviceBrokerRes.Meta.UpdatedAt + return serviceBrokerRes.Entity, nil +} + +func (c *Client) GetServiceBrokerByName(name string) (ServiceBroker, error) { + q := url.Values{} + q.Set("q", "name:"+name) + sbs, err := c.ListServiceBrokersByQuery(q) + if err != nil { + return ServiceBroker{}, err + } + if len(sbs) == 0 { + cfErr := NewServiceBrokerNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, name) + return ServiceBroker{}, cfErr + } + return sbs[0], nil +} + +func (c *Client) getServiceBrokerResponse(requestURL string) (ServiceBrokerResponse, error) { + var serviceBrokerResp ServiceBrokerResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return ServiceBrokerResponse{}, errors.Wrap(err, "Error requesting Service Brokers") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return ServiceBrokerResponse{}, errors.Wrap(err, "Error reading Service Broker request") + } + err = json.Unmarshal(resBody, &serviceBrokerResp) + if err != nil { + return ServiceBrokerResponse{}, errors.Wrap(err, "Error unmarshalling Service Broker") + } + return serviceBrokerResp, nil +} diff --git a/third_party/go-cfclient/service_brokers_test.go b/third_party/go-cfclient/service_brokers_test.go new file mode 100644 index 000000000000..4e6b8f9f9e8c --- /dev/null +++ b/third_party/go-cfclient/service_brokers_test.go @@ -0,0 +1,31 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListServiceBrokers(t *testing.T) { + Convey("List Service Brokers", t, func() { + setup(MockRoute{"GET", "/v2/service_brokers", []string{listServiceBrokersPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + servicePlans, err := client.ListServiceBrokers() + So(err, ShouldBeNil) + + So(len(servicePlans), ShouldEqual, 1) + So(servicePlans[0].Guid, ShouldEqual, "90a413fd-a636-4133-8bfb-a94b07839e96") + So(servicePlans[0].Name, ShouldEqual, "name-85") + So(servicePlans[0].BrokerURL, ShouldEqual, "https://foo.com/url-2") + So(servicePlans[0].Username, ShouldEqual, "auth_username-2") + So(servicePlans[0].SpaceGUID, ShouldEqual, "1d43e64d-ed64-43dd-9046-11f422bd407b") + So(servicePlans[0].Password, ShouldBeEmpty) + }) +} diff --git a/third_party/go-cfclient/service_instances.go b/third_party/go-cfclient/service_instances.go new file mode 100644 index 000000000000..070c262e4226 --- /dev/null +++ b/third_party/go-cfclient/service_instances.go @@ -0,0 +1,234 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type ServiceInstancesResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []ServiceInstanceResource `json:"resources"` +} + +type ServiceInstanceRequest struct { + Name string `json:"name"` + SpaceGuid string `json:"space_guid"` + ServicePlanGuid string `json:"service_plan_guid"` + Parameters map[string]interface{} `json:"parameters,omitempty"` + Tags []string `json:"tags,omitempty"` +} + +type ServiceInstanceUpdateRequest struct { + Name string `json:"name,omitempty"` + ServicePlanGuid string `json:"service_plan_guid,omitempty"` + Parameters map[string]interface{} `json:"parameters,omitempty"` + Tags []string `json:"tags,omitempty"` +} + +type ServiceInstanceResource struct { + Meta Meta `json:"metadata"` + Entity ServiceInstance `json:"entity"` +} + +type ServiceInstance struct { + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Credentials map[string]interface{} `json:"credentials"` + ServicePlanGuid string `json:"service_plan_guid"` + SpaceGuid string `json:"space_guid"` + DashboardUrl string `json:"dashboard_url"` + Type string `json:"type"` + LastOperation LastOperation `json:"last_operation"` + Tags []string `json:"tags"` + ServiceGuid string `json:"service_guid"` + SpaceUrl string `json:"space_url"` + ServicePlanUrl string `json:"service_plan_url"` + ServiceBindingsUrl string `json:"service_bindings_url"` + ServiceKeysUrl string `json:"service_keys_url"` + ServiceInstanceParametersUrl string `json:"service_instance_parameters_url"` + SharedFromUrl string `json:"shared_from_url"` + SharedToUrl string `json:"shared_to_url"` + RoutesUrl string `json:"routes_url"` + ServiceUrl string `json:"service_url"` + Guid string `json:"guid"` + c *Client +} + +type LastOperation struct { + Type string `json:"type"` + State string `json:"state"` + Description string `json:"description"` + UpdatedAt string `json:"updated_at"` + CreatedAt string `json:"created_at"` +} + +func (c *Client) ListServiceInstancesByQuery(query url.Values) ([]ServiceInstance, error) { + var instances []ServiceInstance + + requestUrl := "/v2/service_instances?" + query.Encode() + for { + sir, err := c.getServiceInstancesResponse(requestUrl) + if err != nil { + return instances, err + } + for _, instance := range sir.Resources { + instances = append(instances, c.mergeServiceInstance(instance)) + } + requestUrl = sir.NextUrl + if requestUrl == "" || query.Get("page") != "" { + break + } + } + return instances, nil +} + +func (c *Client) ListServiceInstances() ([]ServiceInstance, error) { + return c.ListServiceInstancesByQuery(nil) +} + +func (c *Client) GetServiceInstanceParams(guid string) (map[string]interface{}, error) { + req := c.NewRequest("GET", "/v2/service_instances/"+guid+"/parameters") + res, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error requesting service instance parameters") + } + + defer res.Body.Close() + + var result map[string]interface{} + err = json.NewDecoder(res.Body).Decode(&result) + if err != nil { + return nil, errors.Wrap(err, "Error JSON parsing service instance parameters") + } + + return result, nil +} + +func (c *Client) GetServiceInstanceByGuid(guid string) (ServiceInstance, error) { + var sir ServiceInstanceResource + req := c.NewRequest("GET", "/v2/service_instances/"+guid) + res, err := c.DoRequest(req) + if err != nil { + return ServiceInstance{}, errors.Wrap(err, "Error requesting service instance") + } + defer res.Body.Close() + data, err := ioutil.ReadAll(res.Body) + if err != nil { + return ServiceInstance{}, errors.Wrap(err, "Error reading service instance response") + } + err = json.Unmarshal(data, &sir) + if err != nil { + return ServiceInstance{}, errors.Wrap(err, "Error JSON parsing service instance response") + } + return c.mergeServiceInstance(sir), nil +} + +func (c *Client) ServiceInstanceByGuid(guid string) (ServiceInstance, error) { + return c.GetServiceInstanceByGuid(guid) +} + +func (c *Client) getServiceInstancesResponse(requestUrl string) (ServiceInstancesResponse, error) { + var serviceInstancesResponse ServiceInstancesResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return ServiceInstancesResponse{}, errors.Wrap(err, "Error requesting service instances") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return ServiceInstancesResponse{}, errors.Wrap(err, "Error reading service instance request") + } + err = json.Unmarshal(resBody, &serviceInstancesResponse) + if err != nil { + return ServiceInstancesResponse{}, errors.Wrap(err, "Error unmarshalling service instance") + } + return serviceInstancesResponse, nil +} + +func (c *Client) mergeServiceInstance(instance ServiceInstanceResource) ServiceInstance { + instance.Entity.Guid = instance.Meta.Guid + instance.Entity.CreatedAt = instance.Meta.CreatedAt + instance.Entity.UpdatedAt = instance.Meta.UpdatedAt + instance.Entity.c = c + return instance.Entity +} + +func (c *Client) CreateServiceInstance(req ServiceInstanceRequest) (ServiceInstance, error) { + var sir ServiceInstanceResource + + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return ServiceInstance{}, err + } + + r := c.NewRequestWithBody("POST", "/v2/service_instances?accepts_incomplete=true", buf) + + res, err := c.DoRequest(r) + if err != nil { + return ServiceInstance{}, err + } + + defer res.Body.Close() + if res.StatusCode != http.StatusAccepted && res.StatusCode != http.StatusCreated { + return ServiceInstance{}, errors.Wrapf(err, "Error creating service, response code: %d", res.StatusCode) + } + + data, err := ioutil.ReadAll(res.Body) + if err != nil { + return ServiceInstance{}, errors.Wrap(err, "Error reading service instance response") + } + + err = json.Unmarshal(data, &sir) + if err != nil { + return ServiceInstance{}, errors.Wrap(err, "Error JSON parsing service instance response") + } + + return c.mergeServiceInstance(sir), nil +} + +func (c *Client) UpdateSI(serviceInstanceGuid string, req ServiceInstanceUpdateRequest, async bool) error { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return err + } + return c.UpdateServiceInstance(serviceInstanceGuid, buf, async) +} + +func (c *Client) UpdateServiceInstance(serviceInstanceGuid string, updatedConfiguration io.Reader, async bool) error { + u := fmt.Sprintf("/v2/service_instances/%s?accepts_incomplete=%t", serviceInstanceGuid, async) + resp, err := c.DoRequest(c.NewRequestWithBody("PUT", u, updatedConfiguration)) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusAccepted { + return errors.Wrapf(err, "Error updating service instance %s, response code %d", serviceInstanceGuid, resp.StatusCode) + } + return nil +} + +func (c *Client) DeleteServiceInstance(guid string, recursive, async bool) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/service_instances/%s?recursive=%t&accepts_incomplete=%t&async=%t", guid, recursive, async, async))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusAccepted { + return errors.Wrapf(err, "Error deleting service instance %s, response code %d", guid, resp.StatusCode) + } + return nil +} diff --git a/third_party/go-cfclient/service_instances_test.go b/third_party/go-cfclient/service_instances_test.go new file mode 100644 index 000000000000..34a829e2eebc --- /dev/null +++ b/third_party/go-cfclient/service_instances_test.go @@ -0,0 +1,214 @@ +package cfclient + +import ( + "net/http" + "strings" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListServicesInstances(t *testing.T) { + Convey("List Service Instances", t, func() { + setup(MockRoute{"GET", "/v2/service_instances", []string{listServiceInstancePayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + instances, err := client.ListServiceInstances() + So(err, ShouldBeNil) + + So(len(instances), ShouldEqual, 2) + So(instances[0].Guid, ShouldEqual, "8423ca96-90ad-411f-b77a-0907844949fc") + So(instances[0].Name, ShouldEqual, "fortunes-db") + }) +} + +func TestGetServiceInstanceParams(t *testing.T) { + Convey("Service instance parameters", t, func() { + setup(MockRoute{"GET", "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/parameters", []string{serviceInstanceParamsPayload}, "", 200, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + params, err := client.GetServiceInstanceParams("8423ca96-90ad-411f-b77a-0907844949fc") + So(err, ShouldBeNil) + + So(params, ShouldContainKey, "foo") + So(params, ShouldContainKey, "baz") + So(params["foo"], ShouldEqual, "bar") + So(params["baz"], ShouldEqual, 42) + }) +} + +func TestServiceInstanceByGuid(t *testing.T) { + Convey("Service instance by Guid", t, func() { + setup(MockRoute{"GET", "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc", []string{serviceInstancePayload}, "", 200, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + service, err := client.GetServiceInstanceByGuid("8423ca96-90ad-411f-b77a-0907844949fc") + So(err, ShouldBeNil) + + expected := ServiceInstance{ + Guid: "8423ca96-90ad-411f-b77a-0907844949fc", + CreatedAt: "2016-10-21T18:22:56Z", + UpdatedAt: "2016-10-21T18:22:56Z", + Credentials: map[string]interface{}{}, + Name: "fortunes-db", + LastOperation: LastOperation{ + Type: "create", + State: "succeeded", + Description: "", + UpdatedAt: "", + CreatedAt: "2016-10-21T18:22:56Z", + }, + Tags: []string{}, + ServiceGuid: "440ce9d9-b108-4bbe-80b4-08338f3cc25b", + ServicePlanGuid: "f48419f7-4717-4706-86e4-a24973848a77", + SpaceGuid: "21e5fdc7-5131-4743-8447-6373cf336a77", + DashboardUrl: "https://p-mysql.system.example.com/manage/instances/8423ca96-90ad-411f-b77a-0907844949fc", + Type: "managed_service_instance", + SpaceUrl: "/v2/spaces/21e5fdc7-5131-4743-8447-6373cf336a77", + ServicePlanUrl: "/v2/service_plans/f48419f7-4717-4706-86e4-a24973848a77", + ServiceBindingsUrl: "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/service_bindings", + ServiceKeysUrl: "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/service_keys", + RoutesUrl: "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/routes", + ServiceUrl: "/v2/services/440ce9d9-b108-4bbe-80b4-08338f3cc25b", + c: client, + } + So(service, ShouldResemble, expected) + }) +} + +func TestCreateServiceInstance(t *testing.T) { + Convey("Create service instance", t, func() { + setup(MockRoute{"POST", "/v2/service_instances", []string{serviceInstancePayload}, "", 202, "accepts_incomplete=true", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + req := ServiceInstanceRequest{ + Name: "test-service", + ServicePlanGuid: "f48419f7-4717-4706-86e4-a24973848a77", + SpaceGuid: "21e5fdc7-5131-4743-8447-6373cf336a77", + } + + service, err := client.CreateServiceInstance(req) + So(err, ShouldBeNil) + + expected := ServiceInstance{ + Guid: "8423ca96-90ad-411f-b77a-0907844949fc", + CreatedAt: "2016-10-21T18:22:56Z", + UpdatedAt: "2016-10-21T18:22:56Z", + Credentials: map[string]interface{}{}, + Name: "fortunes-db", + LastOperation: LastOperation{ + Type: "create", + State: "succeeded", + Description: "", + UpdatedAt: "", + CreatedAt: "2016-10-21T18:22:56Z", + }, + Tags: []string{}, + ServiceGuid: "440ce9d9-b108-4bbe-80b4-08338f3cc25b", + ServicePlanGuid: "f48419f7-4717-4706-86e4-a24973848a77", + SpaceGuid: "21e5fdc7-5131-4743-8447-6373cf336a77", + DashboardUrl: "https://p-mysql.system.example.com/manage/instances/8423ca96-90ad-411f-b77a-0907844949fc", + Type: "managed_service_instance", + SpaceUrl: "/v2/spaces/21e5fdc7-5131-4743-8447-6373cf336a77", + ServicePlanUrl: "/v2/service_plans/f48419f7-4717-4706-86e4-a24973848a77", + ServiceBindingsUrl: "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/service_bindings", + ServiceKeysUrl: "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/service_keys", + RoutesUrl: "/v2/service_instances/8423ca96-90ad-411f-b77a-0907844949fc/routes", + ServiceUrl: "/v2/services/440ce9d9-b108-4bbe-80b4-08338f3cc25b", + c: client, + } + So(service, ShouldResemble, expected) + }) +} + +func TestUpdateSIt(t *testing.T) { + Convey("Update SI", t, func() { + expectedPayload := "{\"name\":\"test-cs\",\"parameters\":{\"git\":{\"label\":\"master\",\"uri\":\"https://github.com/cloudfoundry-community/go-cfclient.git\"}},\"tags\":[\"tag1\",\"tag2\",\"tag3\"]}" + req := ServiceInstanceUpdateRequest{ + Name: "test-cs", + Tags: []string{"tag1", "tag2", "tag3"}, + Parameters: map[string]interface{}{ + "git": map[string]interface{}{ + "uri": "https://github.com/cloudfoundry-community/go-cfclient.git", + "label": "master", + }, + }, + } + + setup(MockRoute{"PUT", "/v2/service_instances/guid", []string{""}, "", http.StatusAccepted, "accepts_incomplete=false", &expectedPayload}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.UpdateSI("guid", req, false) + So(err, ShouldBeNil) + }) +} + +func TestUpdateServiceInstance(t *testing.T) { + Convey("Update service instance", t, func() { + updateBody := "myUpdate" + + setup(MockRoute{"PUT", "/v2/service_instances/guid", []string{""}, "", http.StatusAccepted, "accepts_incomplete=false", &updateBody}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.UpdateServiceInstance("guid", strings.NewReader(updateBody), false) + So(err, ShouldBeNil) + }) +} + +func TestDeleteServiceInstance(t *testing.T) { + Convey("Delete service instance", t, func() { + setup(MockRoute{"DELETE", "/v2/service_instances/guid", []string{""}, "", http.StatusAccepted, "recursive=true&accepts_incomplete=false&async=false", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteServiceInstance("guid", true, false) + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/service_keys.go b/third_party/go-cfclient/service_keys.go new file mode 100644 index 000000000000..5c0806dc61b9 --- /dev/null +++ b/third_party/go-cfclient/service_keys.go @@ -0,0 +1,204 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type ServiceKeysResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + Resources []ServiceKeyResource `json:"resources"` + NextUrl string `json:"next_url"` +} + +type ServiceKeyResource struct { + Meta Meta `json:"metadata"` + Entity ServiceKey `json:"entity"` +} + +type CreateServiceKeyRequest struct { + Name string `json:"name"` + ServiceInstanceGuid string `json:"service_instance_guid"` + Parameters interface{} `json:"parameters,omitempty"` +} + +type ServiceKey struct { + Name string `json:"name"` + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + ServiceInstanceGuid string `json:"service_instance_guid"` + Credentials interface{} `json:"credentials"` + ServiceInstanceUrl string `json:"service_instance_url"` + c *Client +} + +func (c *Client) ListServiceKeysByQuery(query url.Values) ([]ServiceKey, error) { + var serviceKeys []ServiceKey + requestUrl := "/v2/service_keys?" + query.Encode() + + for { + var serviceKeysResp ServiceKeysResponse + + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting service keys") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading service keys request:") + } + + err = json.Unmarshal(resBody, &serviceKeysResp) + if err != nil { + return nil, errors.Wrapf(err, "Error unmarshaling service keys: %q", string(resBody)) + } + for _, serviceKey := range serviceKeysResp.Resources { + serviceKey.Entity.Guid = serviceKey.Meta.Guid + serviceKey.Entity.CreatedAt = serviceKey.Meta.CreatedAt + serviceKey.Entity.UpdatedAt = serviceKey.Meta.UpdatedAt + serviceKey.Entity.c = c + serviceKeys = append(serviceKeys, serviceKey.Entity) + } + + requestUrl = serviceKeysResp.NextUrl + if requestUrl == "" { + break + } + } + + return serviceKeys, nil +} + +func (c *Client) GetServiceKeyByGuid(guid string) (ServiceKey, error) { + var serviceKey ServiceKeyResource + r := c.NewRequest("GET", "/v2/service_keys/"+url.QueryEscape(guid)) + resp, err := c.DoRequest(r) + if err != nil { + return ServiceKey{}, errors.Wrap(err, "Error requesting serving Key") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServiceKey{}, errors.Wrap(err, "Error reading service Key response body") + } + err = json.Unmarshal(resBody, &serviceKey) + if err != nil { + return ServiceKey{}, errors.Wrap(err, "Error unmarshalling service Key") + } + serviceKey.Entity.Guid = serviceKey.Meta.Guid + serviceKey.Entity.c = c + return serviceKey.Entity, nil +} + +func (c *Client) ListServiceKeys() ([]ServiceKey, error) { + return c.ListServiceKeysByQuery(nil) +} + +func (c *Client) GetServiceKeyByName(name string) (ServiceKey, error) { + var serviceKey ServiceKey + q := url.Values{} + q.Set("q", "name:"+name) + serviceKeys, err := c.ListServiceKeysByQuery(q) + if err != nil { + return serviceKey, err + } + if len(serviceKeys) == 0 { + cfErr := NewServiceKeyNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, name) + return ServiceKey{}, cfErr + } + return serviceKeys[0], nil +} + +// GetServiceKeyByInstanceGuid is deprecated in favor of GetServiceKeysByInstanceGuid +func (c *Client) GetServiceKeyByInstanceGuid(guid string) (ServiceKey, error) { + q := url.Values{} + q.Set("q", "service_instance_guid:"+guid) + serviceKeys, err := c.ListServiceKeysByQuery(q) + if err != nil { + return ServiceKey{}, err + } + if len(serviceKeys) == 0 { + cfErr := NewServiceKeyNotFoundError() + return ServiceKey{}, cfErr + } + return serviceKeys[0], nil +} + +// GetServiceKeysByInstanceGuid returns the service keys for a service instance. +// If none are found, it returns an error. +func (c *Client) GetServiceKeysByInstanceGuid(guid string) ([]ServiceKey, error) { + q := url.Values{} + q.Set("q", "service_instance_guid:"+guid) + serviceKeys, err := c.ListServiceKeysByQuery(q) + if err != nil { + return serviceKeys, err + } + if len(serviceKeys) == 0 { + cfErr := NewServiceKeyNotFoundError() + return serviceKeys, cfErr + } + return serviceKeys, nil +} + +// CreateServiceKey creates a service key from the request. If a service key +// exists already, it returns an error containing `CF-ServiceKeyNameTaken` +func (c *Client) CreateServiceKey(csr CreateServiceKeyRequest) (ServiceKey, error) { + var serviceKeyResource ServiceKeyResource + + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(csr) + if err != nil { + return ServiceKey{}, err + } + req := c.NewRequestWithBody("POST", "/v2/service_keys", buf) + resp, err := c.DoRequest(req) + if err != nil { + return ServiceKey{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return ServiceKey{}, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServiceKey{}, err + } + err = json.Unmarshal(body, &serviceKeyResource) + if err != nil { + return ServiceKey{}, err + } + + return c.mergeServiceKey(serviceKeyResource), nil +} + +// DeleteServiceKey removes a service key instance +func (c *Client) DeleteServiceKey(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/service_keys/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting service instance key %s, response code %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) mergeServiceKey(key ServiceKeyResource) ServiceKey { + key.Entity.Guid = key.Meta.Guid + key.Entity.CreatedAt = key.Meta.CreatedAt + key.Entity.UpdatedAt = key.Meta.UpdatedAt + key.Entity.c = c + return key.Entity +} diff --git a/third_party/go-cfclient/service_keys_test.go b/third_party/go-cfclient/service_keys_test.go new file mode 100644 index 000000000000..ff2d72dc76ef --- /dev/null +++ b/third_party/go-cfclient/service_keys_test.go @@ -0,0 +1,287 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListServiceKeys(t *testing.T) { + Convey("List Service Keys", t, func() { + mocks := []MockRoute{ + { + Method: "GET", + Endpoint: "/v2/service_keys", + Output: []string{listServiceKeysPayloadPage1}, + Status: 200, + }, + { + Method: "GET", + Endpoint: "/v2/service_keys2", + Output: []string{listServiceKeysPayloadPage2}, + Status: 200, + }, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + serviceKeys, err := client.ListServiceKeys() + So(err, ShouldBeNil) + + So(len(serviceKeys), ShouldEqual, 4) + So(serviceKeys[0].Guid, ShouldEqual, "3b933598-64ed-4613-a0f5-b7e8c0379368") + So(serviceKeys[0].Name, ShouldEqual, "RedisMonitoringKey") + So(serviceKeys[0].ServiceInstanceGuid, ShouldEqual, "ad98f310-a3a0-47aa-9116-f8295d41a9b2") + So(serviceKeys[0].Credentials, ShouldNotEqual, nil) + So(serviceKeys[0].ServiceInstanceUrl, ShouldEqual, "/v2/service_instances/ad98f310-a3a0-47aa-9116-f8295d41a9b2") + So(serviceKeys[1].Guid, ShouldEqual, "8be3911b-c621-4467-8866-f8b924aaee57") + So(serviceKeys[1].Name, ShouldEqual, "test01_key") + So(serviceKeys[1].ServiceInstanceGuid, ShouldEqual, "ecf26687-e176-4784-b181-b3c942fecb62") + So(serviceKeys[1].Credentials, ShouldNotEqual, nil) + m := serviceKeys[1].Credentials.(map[string]interface{}) + So(m["uri"], ShouldEqual, "nhp://100.100.100.100:9008") + So(serviceKeys[1].ServiceInstanceUrl, ShouldEqual, "/v2/service_instances/fcf26687-e176-4784-b181-b3c942fecb62") + So(serviceKeys[2].Guid, ShouldEqual, "3b933598-64ed-4613-a0f5-b7e8c0379368") + So(serviceKeys[2].Name, ShouldEqual, "RedisMonitoringKey") + So(serviceKeys[2].ServiceInstanceGuid, ShouldEqual, "ad98f310-a3a0-47aa-9116-f8295d41a9b2") + So(serviceKeys[2].Credentials, ShouldNotEqual, nil) + So(serviceKeys[2].ServiceInstanceUrl, ShouldEqual, "/v2/service_instances/ad98f310-a3a0-47aa-9116-f8295d41a9b2") + So(serviceKeys[3].Guid, ShouldEqual, "8be3911b-c621-4467-8866-f8b924aaee57") + So(serviceKeys[3].Name, ShouldEqual, "test01_key") + So(serviceKeys[3].ServiceInstanceGuid, ShouldEqual, "ecf26687-e176-4784-b181-b3c942fecb62") + So(serviceKeys[3].Credentials, ShouldNotEqual, nil) + m = serviceKeys[3].Credentials.(map[string]interface{}) + So(m["uri"], ShouldEqual, "nhp://100.100.100.100:9008") + So(serviceKeys[3].ServiceInstanceUrl, ShouldEqual, "/v2/service_instances/fcf26687-e176-4784-b181-b3c942fecb62") + }) +} + +func TestGetServiceKeyByName(t *testing.T) { + Convey("Get service key by name", t, func() { + setup(MockRoute{"GET", "/v2/service_keys", []string{getServiceKeyPayload}, "", 200, "q=name:test01_key", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + serviceKey, err := client.GetServiceKeyByName("test01_key") + So(err, ShouldBeNil) + + So(serviceKey, ShouldNotBeNil) + So(serviceKey.Name, ShouldEqual, "test01_key") + So(serviceKey.ServiceInstanceGuid, ShouldEqual, "ecf26687-e176-4784-b181-b3c942fecb62") + So(serviceKey.Credentials, ShouldNotEqual, nil) + So(serviceKey.ServiceInstanceUrl, ShouldEqual, "/v2/service_instances/ecf26687-e176-4784-b181-b3c942fecb62") + }) +} + +func TestGetServiceKeyByGuid(t *testing.T) { + Convey("Get service key by guid", t, func() { + setup(MockRoute{"GET", "/v2/service_keys/6ad2cc9b-1996-49a3-9538-dfc0da3b1f32", []string{getServiceKeyByGuidPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + serviceKey, err := client.GetServiceKeyByGuid("6ad2cc9b-1996-49a3-9538-dfc0da3b1f32") + So(err, ShouldBeNil) + + So(serviceKey, ShouldNotBeNil) + So(serviceKey.Name, ShouldEqual, "name-140") + So(serviceKey.ServiceInstanceGuid, ShouldEqual, "ca567b3d-e142-4139-94e3-1e0c010ba728") + So(serviceKey.Credentials, ShouldNotEqual, nil) + So(serviceKey.ServiceInstanceUrl, ShouldEqual, "/v2/service_instances/ca567b3d-e142-4139-94e3-1e0c010ba728") + }) +} + +func TestGetServiceKeyByInstanceGuid(t *testing.T) { + Convey("Get service key by instance guid", t, func() { + setup(MockRoute{"GET", "/v2/service_keys", []string{getServiceKeyPayload}, "", 200, "q=service_instance_guid:ecf26687-e176-4784-b181-b3c942fecb62", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + serviceKey, err := client.GetServiceKeyByInstanceGuid("ecf26687-e176-4784-b181-b3c942fecb62") + So(err, ShouldBeNil) + + So(serviceKey, ShouldNotBeNil) + So(serviceKey.Name, ShouldEqual, "test01_key") + So(serviceKey.ServiceInstanceGuid, ShouldEqual, "ecf26687-e176-4784-b181-b3c942fecb62") + So(serviceKey.Credentials, ShouldNotEqual, nil) + So(serviceKey.ServiceInstanceUrl, ShouldEqual, "/v2/service_instances/ecf26687-e176-4784-b181-b3c942fecb62") + }) +} + +func TestGetServiceKeysByInstanceGuid(t *testing.T) { + Convey("Get service keys by instance guid", t, func() { + setup(MockRoute{"GET", "/v2/service_keys", []string{getServiceKeysPayload}, "", 200, "q=service_instance_guid:ecf26687-e176-4784-b181-b3c942fecb62", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + serviceKeys, err := client.GetServiceKeysByInstanceGuid("ecf26687-e176-4784-b181-b3c942fecb62") + So(err, ShouldBeNil) + So(len(serviceKeys), ShouldEqual, 2) + + So(serviceKeys[0].Name, ShouldEqual, "test01_key") + So(serviceKeys[0].ServiceInstanceGuid, ShouldEqual, "ecf26687-e176-4784-b181-b3c942fecb62") + So(serviceKeys[0].Credentials, ShouldNotEqual, nil) + So(serviceKeys[0].ServiceInstanceUrl, ShouldEqual, "/v2/service_instances/ecf26687-e176-4784-b181-b3c942fecb62") + + So(serviceKeys[1].Name, ShouldEqual, "test02_key") + So(serviceKeys[1].ServiceInstanceGuid, ShouldEqual, "ecf26687-e176-4784-b181-b3c942fecb62") + So(serviceKeys[1].Credentials, ShouldNotEqual, nil) + So(serviceKeys[1].ServiceInstanceUrl, ShouldEqual, "/v2/service_instances/ecf26687-e176-4784-b181-b3c942fecb62") + }) +} + +func TestCreateServiceKey(t *testing.T) { + Convey("Create a service key succeeds", t, func() { + setup(MockRoute{"POST", "/v2/service_keys", []string{postServiceKeysPayload}, "", 201, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + csr := CreateServiceKeyRequest{ + Name: "key1", + ServiceInstanceGuid: "ecf26687-e176-4784-b181-b3c942fecb62", + } + + key, err := client.CreateServiceKey(csr) + So(err, ShouldBeNil) + + So(key.Name, ShouldEqual, "key1") + So(key.ServiceInstanceUrl, ShouldEqual, "/v2/service_instances/ecf26687-e176-4784-b181-b3c942fecb62") + }) + + Convey("Create a service key with parameters succeeds", t, func() { + setup(MockRoute{"POST", "/v2/service_keys", []string{postServiceKeysPayload}, "", 201, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + csr := CreateServiceKeyRequest{ + Name: "key1", + ServiceInstanceGuid: "ecf26687-e176-4784-b181-b3c942fecb62", + Parameters: map[string]interface{}{ + "read-only": true, + "username": "user1", + "connections": 6, + }, + } + + _, err = client.CreateServiceKey(csr) + So(err, ShouldBeNil) + }) + + Convey("Delete a service key succeeds", t, func() { + setup(MockRoute{"DELETE", "/v2/service_keys/ecf26687-e176-4784-b181-b3c942fecb62", []string{""}, "", 200, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteServiceKey("ecf26687-e176-4784-b181-b3c942fecb62") + So(err, ShouldBeNil) + }) + + Convey("Create a duplicate service key", t, func() { + setup(MockRoute{"POST", "/v2/service_keys", []string{postServiceKeysDuplicatePayload}, "", 400, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + csr := CreateServiceKeyRequest{ + Name: "key1", + ServiceInstanceGuid: "ecf26687-e176-4784-b181-b3c942fecb62", + } + + key, err := client.CreateServiceKey(csr) + So(err.Error(), ShouldEqual, "cfclient error (CF-ServiceKeyNameTaken|360001): The service key name is taken: key1") + + So(key.Name, ShouldEqual, "") + }) + + Convey("Gets a bad JSON response", t, func() { + setup(MockRoute{"POST", "/v2/service_keys", []string{postServiceKeysBadPayload}, "", 201, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + csr := CreateServiceKeyRequest{ + Name: "key1", + ServiceInstanceGuid: "ecf26687-e176-4784-b181-b3c942fecb62", + } + + key, err := client.CreateServiceKey(csr) + So(err.Error(), ShouldEqual, "unexpected end of JSON input") + + So(key.Name, ShouldEqual, "") + }) + + Convey("Gets an unexpected HTTP status code", t, func() { + setup(MockRoute{"POST", "/v2/service_keys", []string{""}, "", 202, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + csr := CreateServiceKeyRequest{ + Name: "key1", + ServiceInstanceGuid: "ecf26687-e176-4784-b181-b3c942fecb62", + } + + key, err := client.CreateServiceKey(csr) + So(err.Error(), ShouldEqual, "CF API returned with status code 202") + + So(key.Name, ShouldEqual, "") + }) +} diff --git a/third_party/go-cfclient/service_plan_visibilities.go b/third_party/go-cfclient/service_plan_visibilities.go new file mode 100644 index 000000000000..b875b37ae756 --- /dev/null +++ b/third_party/go-cfclient/service_plan_visibilities.go @@ -0,0 +1,174 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type ServicePlanVisibilitiesResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []ServicePlanVisibilityResource `json:"resources"` +} + +type ServicePlanVisibilityResource struct { + Meta Meta `json:"metadata"` + Entity ServicePlanVisibility `json:"entity"` +} + +type ServicePlanVisibility struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + ServicePlanGuid string `json:"service_plan_guid"` + OrganizationGuid string `json:"organization_guid"` + ServicePlanUrl string `json:"service_plan_url"` + OrganizationUrl string `json:"organization_url"` + c *Client +} + +func (c *Client) ListServicePlanVisibilitiesByQuery(query url.Values) ([]ServicePlanVisibility, error) { + var servicePlanVisibilities []ServicePlanVisibility + requestUrl := "/v2/service_plan_visibilities?" + query.Encode() + for { + var servicePlanVisibilitiesResp ServicePlanVisibilitiesResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting service plan visibilities") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading service plan visibilities request:") + } + + err = json.Unmarshal(resBody, &servicePlanVisibilitiesResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling service plan visibilities") + } + for _, servicePlanVisibility := range servicePlanVisibilitiesResp.Resources { + servicePlanVisibility.Entity.Guid = servicePlanVisibility.Meta.Guid + servicePlanVisibility.Entity.CreatedAt = servicePlanVisibility.Meta.CreatedAt + servicePlanVisibility.Entity.UpdatedAt = servicePlanVisibility.Meta.UpdatedAt + servicePlanVisibility.Entity.c = c + servicePlanVisibilities = append(servicePlanVisibilities, servicePlanVisibility.Entity) + } + requestUrl = servicePlanVisibilitiesResp.NextUrl + if requestUrl == "" { + break + } + } + return servicePlanVisibilities, nil +} + +func (c *Client) ListServicePlanVisibilities() ([]ServicePlanVisibility, error) { + return c.ListServicePlanVisibilitiesByQuery(nil) +} + +func (c *Client) GetServicePlanVisibilityByGuid(guid string) (ServicePlanVisibility, error) { + r := c.NewRequest("GET", "/v2/service_plan_visibilities/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return ServicePlanVisibility{}, err + } + defer resp.Body.Close() + return respBodyToServicePlanVisibility(resp.Body, c) +} + +// a uniqueID is the id of the service in the catalog and not in cf internal db +func (c *Client) CreateServicePlanVisibilityByUniqueId(uniqueId string, organizationGuid string) (ServicePlanVisibility, error) { + q := url.Values{} + q.Set("q", fmt.Sprintf("unique_id:%s", uniqueId)) + plans, err := c.ListServicePlansByQuery(q) + if err != nil { + return ServicePlanVisibility{}, errors.Wrap(err, fmt.Sprintf("Couldn't find a service plan with unique_id: %s", uniqueId)) + } + return c.CreateServicePlanVisibility(plans[0].Guid, organizationGuid) +} + +func (c *Client) CreateServicePlanVisibility(servicePlanGuid string, organizationGuid string) (ServicePlanVisibility, error) { + req := c.NewRequest("POST", "/v2/service_plan_visibilities") + req.obj = map[string]interface{}{ + "service_plan_guid": servicePlanGuid, + "organization_guid": organizationGuid, + } + resp, err := c.DoRequest(req) + if err != nil { + return ServicePlanVisibility{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return ServicePlanVisibility{}, errors.Wrapf(err, "Error creating service plan visibility, response code: %d", resp.StatusCode) + } + return respBodyToServicePlanVisibility(resp.Body, c) +} + +func (c *Client) DeleteServicePlanVisibilityByPlanAndOrg(servicePlanGuid string, organizationGuid string, async bool) error { + q := url.Values{} + q.Set("q", fmt.Sprintf("organization_guid:%s;service_plan_guid:%s", organizationGuid, servicePlanGuid)) + plans, err := c.ListServicePlanVisibilitiesByQuery(q) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("Couldn't find a service plan visibility for service plan %s and org %s", servicePlanGuid, organizationGuid)) + } + if len(plans) != 1 { + return fmt.Errorf("Query for a service plan visibility did not return exactly one result when searching for a service plan visibility for service plan %s and org %s", + servicePlanGuid, organizationGuid) + } + return c.DeleteServicePlanVisibility(plans[0].Guid, async) +} + +func (c *Client) DeleteServicePlanVisibility(guid string, async bool) error { + req := c.NewRequest("DELETE", fmt.Sprintf("/v2/service_plan_visibilities/%s?async=%v", guid, async)) + resp, err := c.DoRequest(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting service plan visibility, response code: %d", resp.StatusCode) + } + return nil +} + +func (c *Client) UpdateServicePlanVisibility(guid string, servicePlanGuid string, organizationGuid string) (ServicePlanVisibility, error) { + req := c.NewRequest("PUT", "/v2/service_plan_visibilities/"+guid) + req.obj = map[string]interface{}{ + "service_plan_guid": servicePlanGuid, + "organization_guid": organizationGuid, + } + resp, err := c.DoRequest(req) + if err != nil { + return ServicePlanVisibility{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return ServicePlanVisibility{}, errors.Wrapf(err, "Error updating service plan visibility, response code: %d", resp.StatusCode) + } + return respBodyToServicePlanVisibility(resp.Body, c) +} + +func respBodyToServicePlanVisibility(body io.ReadCloser, c *Client) (ServicePlanVisibility, error) { + bodyRaw, err := ioutil.ReadAll(body) + if err != nil { + return ServicePlanVisibility{}, err + } + servicePlanVisibilityRes := ServicePlanVisibilityResource{} + err = json.Unmarshal(bodyRaw, &servicePlanVisibilityRes) + if err != nil { + return ServicePlanVisibility{}, err + } + servicePlanVisibility := servicePlanVisibilityRes.Entity + servicePlanVisibility.Guid = servicePlanVisibilityRes.Meta.Guid + servicePlanVisibility.CreatedAt = servicePlanVisibilityRes.Meta.CreatedAt + servicePlanVisibility.UpdatedAt = servicePlanVisibilityRes.Meta.UpdatedAt + servicePlanVisibility.c = c + return servicePlanVisibility, nil +} diff --git a/third_party/go-cfclient/service_plan_visibilities_test.go b/third_party/go-cfclient/service_plan_visibilities_test.go new file mode 100644 index 000000000000..4ba87fe517cf --- /dev/null +++ b/third_party/go-cfclient/service_plan_visibilities_test.go @@ -0,0 +1,52 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListServicePlanVisibilities(t *testing.T) { + Convey("List service plan visibilities", t, func() { + setup(MockRoute{"GET", "/v2/service_plan_visibilities", []string{listServicePlanVisibilitiesPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + servicePlanVisibilities, err := client.ListServicePlanVisibilities() + So(err, ShouldBeNil) + + So(len(servicePlanVisibilities), ShouldEqual, 2) + So(servicePlanVisibilities[0].Guid, ShouldEqual, "d1b5ea55-f354-4f43-b52e-53045747adb9") + So(servicePlanVisibilities[0].ServicePlanGuid, ShouldEqual, "62cb572c-e9ca-4c9f-b822-8292db1d9a96") + So(servicePlanVisibilities[0].OrganizationGuid, ShouldEqual, "81df84f3-8ce0-4c92-990a-3760b6ff66bd") + So(servicePlanVisibilities[0].ServicePlanUrl, ShouldEqual, "/v2/service_plans/62cb572c-e9ca-4c9f-b822-8292db1d9a96") + So(servicePlanVisibilities[0].OrganizationUrl, ShouldEqual, "/v2/organizations/81df84f3-8ce0-4c92-990a-3760b6ff66bd") + }) +} + +func TestCreateServicePlanVisibility(t *testing.T) { + Convey("Create service plan visibility", t, func() { + setup(MockRoute{"POST", "/v2/service_plan_visibilities", []string{postServicePlanVisibilityPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + servicePlanVisibility, err := client.CreateServicePlanVisibility("ab5780a9-ac8e-4412-9496-4512e865011a", "55d0ff39-dac9-431f-ba6d-83f37381f1c3") + So(err, ShouldBeNil) + + So(servicePlanVisibility.Guid, ShouldEqual, "f740b01a-4afe-4435-aedd-0a8308a7e7d6") + So(servicePlanVisibility.ServicePlanGuid, ShouldEqual, "ab5780a9-ac8e-4412-9496-4512e865011a") + So(servicePlanVisibility.OrganizationGuid, ShouldEqual, "55d0ff39-dac9-431f-ba6d-83f37381f1c3") + So(servicePlanVisibility.ServicePlanUrl, ShouldEqual, "/v2/service_plans/ab5780a9-ac8e-4412-9496-4512e865011a") + So(servicePlanVisibility.OrganizationUrl, ShouldEqual, "/v2/organizations/55d0ff39-dac9-431f-ba6d-83f37381f1c3") + }) +} diff --git a/third_party/go-cfclient/service_plans.go b/third_party/go-cfclient/service_plans.go new file mode 100644 index 000000000000..2fa0961809c4 --- /dev/null +++ b/third_party/go-cfclient/service_plans.go @@ -0,0 +1,131 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/url" + + "github.com/pkg/errors" +) + +type ServicePlansResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []ServicePlanResource `json:"resources"` +} + +type ServicePlanResource struct { + Meta Meta `json:"metadata"` + Entity ServicePlan `json:"entity"` +} + +type ServicePlan struct { + Name string `json:"name"` + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Free bool `json:"free"` + Description string `json:"description"` + ServiceGuid string `json:"service_guid"` + Extra interface{} `json:"extra"` + UniqueId string `json:"unique_id"` + Public bool `json:"public"` + Active bool `json:"active"` + Bindable bool `json:"bindable"` + PlanUpdateable bool `json:"plan_updateable"` + ServiceUrl string `json:"service_url"` + ServiceInstancesUrl string `json:"service_instances_url"` + c *Client +} + +func (c *Client) ListServicePlansByQuery(query url.Values) ([]ServicePlan, error) { + var servicePlans []ServicePlan + requestURL := "/v2/service_plans?" + query.Encode() + for { + var servicePlansResp ServicePlansResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting service plans") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading service plans request:") + } + err = json.Unmarshal(resBody, &servicePlansResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling service plans") + } + for _, servicePlan := range servicePlansResp.Resources { + servicePlan.Entity.Guid = servicePlan.Meta.Guid + servicePlan.Entity.CreatedAt = servicePlan.Meta.CreatedAt + servicePlan.Entity.UpdatedAt = servicePlan.Meta.UpdatedAt + servicePlan.Entity.c = c + servicePlans = append(servicePlans, servicePlan.Entity) + } + requestURL = servicePlansResp.NextUrl + if requestURL == "" { + break + } + } + return servicePlans, nil +} + +func (c *Client) ListServicePlans() ([]ServicePlan, error) { + return c.ListServicePlansByQuery(nil) +} + +func (c *Client) GetServicePlanByGUID(guid string) (*ServicePlan, error) { + var ( + plan *ServicePlan + planResponse ServicePlanResource + ) + + r := c.NewRequest("GET", "/v2/service_plans/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + + err = json.Unmarshal(body, &planResponse) + if err != nil { + return nil, err + } + + planResponse.Entity.Guid = planResponse.Meta.Guid + planResponse.Entity.CreatedAt = planResponse.Meta.CreatedAt + planResponse.Entity.UpdatedAt = planResponse.Meta.UpdatedAt + plan = &planResponse.Entity + + return plan, nil +} + +func (c *Client) MakeServicePlanPublic(servicePlanGUID string) error { + return c.setPlanGlobalVisibility(servicePlanGUID, true) +} + +func (c *Client) MakeServicePlanPrivate(servicePlanGUID string) error { + return c.setPlanGlobalVisibility(servicePlanGUID, false) +} + +func (c *Client) setPlanGlobalVisibility(servicePlanGUID string, public bool) error { + bodyString := fmt.Sprintf(`{"public": %t}`, public) + req := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/service_plans/%s", servicePlanGUID), bytes.NewBufferString(bodyString)) + + resp, err := c.DoRequest(req) + if err != nil { + return err + } + defer resp.Body.Close() + return nil +} diff --git a/third_party/go-cfclient/service_plans_test.go b/third_party/go-cfclient/service_plans_test.go new file mode 100644 index 000000000000..9637cc69aa69 --- /dev/null +++ b/third_party/go-cfclient/service_plans_test.go @@ -0,0 +1,111 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListServicePlans(t *testing.T) { + Convey("List Service Plans", t, func() { + setup(MockRoute{"GET", "/v2/service_plans", []string{listServicePlansPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + servicePlans, err := client.ListServicePlans() + So(err, ShouldBeNil) + + So(len(servicePlans), ShouldEqual, 1) + So(servicePlans[0].Guid, ShouldEqual, "6fecf53b-7553-4cb3-b97e-930f9c4e3385") + So(servicePlans[0].Name, ShouldEqual, "name-1575") + So(servicePlans[0].Description, ShouldEqual, "desc-109") + So(servicePlans[0].ServiceGuid, ShouldEqual, "1ccab853-87c9-45a6-bf99-603032d17fe5") + So(servicePlans[0].Extra, ShouldBeNil) + So(servicePlans[0].UniqueId, ShouldEqual, "1bc2884c-ee3d-4f82-a78b-1a657f79aeac") + So(servicePlans[0].Public, ShouldEqual, true) + So(servicePlans[0].Active, ShouldEqual, true) + So(servicePlans[0].Bindable, ShouldEqual, true) + So(servicePlans[0].PlanUpdateable, ShouldEqual, true) + So(servicePlans[0].ServiceUrl, ShouldEqual, "/v2/services/1ccab853-87c9-45a6-bf99-603032d17fe5") + So(servicePlans[0].ServiceInstancesUrl, ShouldEqual, "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385/service_instances") + }) +} + +func TestGetServicePlanByGuid(t *testing.T) { + Convey("List Service Plans", t, func() { + setup(MockRoute{"GET", "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385", []string{getServicePlanByGuidPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + servicePlan, err := client.GetServicePlanByGUID("6fecf53b-7553-4cb3-b97e-930f9c4e3385") + So(err, ShouldBeNil) + So(servicePlan.Guid, ShouldEqual, "6fecf53b-7553-4cb3-b97e-930f9c4e3385") + So(servicePlan.Name, ShouldEqual, "name-1575") + So(servicePlan.Description, ShouldEqual, "desc-109") + So(servicePlan.ServiceGuid, ShouldEqual, "1ccab853-87c9-45a6-bf99-603032d17fe5") + So(servicePlan.Extra, ShouldBeNil) + So(servicePlan.UniqueId, ShouldEqual, "1bc2884c-ee3d-4f82-a78b-1a657f79aeac") + So(servicePlan.Public, ShouldEqual, true) + So(servicePlan.Active, ShouldEqual, true) + So(servicePlan.Bindable, ShouldEqual, true) + So(servicePlan.PlanUpdateable, ShouldEqual, true) + So(servicePlan.ServiceUrl, ShouldEqual, "/v2/services/1ccab853-87c9-45a6-bf99-603032d17fe5") + So(servicePlan.ServiceInstancesUrl, ShouldEqual, "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385/service_instances") + }) +} + +func TestMakeServicePlanPublic(t *testing.T) { + Convey("Make Service Plan public", t, func() { + setupMultiple([]MockRoute{ + {"GET", "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385", []string{privateServicePlanPayload}, "", 200, "", nil}, + {"PUT", "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385", []string{getServicePlanByGuidPayload}, "", 201, "", nil}, + }, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + servicePlan, err := client.GetServicePlanByGUID("6fecf53b-7553-4cb3-b97e-930f9c4e3385") + So(err, ShouldBeNil) + So(servicePlan.Public, ShouldBeFalse) + + err = client.MakeServicePlanPublic("6fecf53b-7553-4cb3-b97e-930f9c4e3385") + So(err, ShouldBeNil) + }) +} + +func TestMakeServicePlanPrivate(t *testing.T) { + Convey("Make Service Plan private", t, func() { + setupMultiple([]MockRoute{ + {"GET", "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385", []string{getServicePlanByGuidPayload}, "", 200, "", nil}, + {"PUT", "/v2/service_plans/6fecf53b-7553-4cb3-b97e-930f9c4e3385", []string{privateServicePlanPayload}, "", 201, "", nil}, + }, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + servicePlan, err := client.GetServicePlanByGUID("6fecf53b-7553-4cb3-b97e-930f9c4e3385") + So(err, ShouldBeNil) + So(servicePlan.Public, ShouldBeTrue) + + err = client.MakeServicePlanPrivate("6fecf53b-7553-4cb3-b97e-930f9c4e3385") + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/service_usage_events.go b/third_party/go-cfclient/service_usage_events.go new file mode 100644 index 000000000000..17fb8a2b1e1f --- /dev/null +++ b/third_party/go-cfclient/service_usage_events.go @@ -0,0 +1,72 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/url" + + "github.com/pkg/errors" +) + +type ServiceUsageEvent struct { + GUID string `json:"guid"` + CreatedAt string `json:"created_at"` + State string `json:"state"` + OrgGUID string `json:"org_guid"` + SpaceGUID string `json:"space_guid"` + SpaceName string `json:"space_name"` + ServiceInstanceGUID string `json:"service_instance_guid"` + ServiceInstanceName string `json:"service_instance_name"` + ServiceInstanceType string `json:"service_instance_type"` + ServicePlanGUID string `json:"service_plan_guid"` + ServicePlanName string `json:"service_plan_name"` + ServiceGUID string `json:"service_guid"` + ServiceLabel string `json:"service_label"` + c *Client +} + +type ServiceUsageEventsResponse struct { + TotalResults int `json:"total_results"` + Pages int `json:"total_pages"` + NextURL string `json:"next_url"` + Resources []ServiceUsageEventResource `json:"resources"` +} + +type ServiceUsageEventResource struct { + Meta Meta `json:"metadata"` + Entity ServiceUsageEvent `json:"entity"` +} + +// ListServiceUsageEventsByQuery lists all events matching the provided query. +func (c *Client) ListServiceUsageEventsByQuery(query url.Values) ([]ServiceUsageEvent, error) { + var serviceUsageEvents []ServiceUsageEvent + requestURL := fmt.Sprintf("/v2/service_usage_events?%s", query.Encode()) + for { + var serviceUsageEventsResponse ServiceUsageEventsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "error requesting events") + } + defer resp.Body.Close() + if err := json.NewDecoder(resp.Body).Decode(&serviceUsageEventsResponse); err != nil { + return nil, errors.Wrap(err, "error unmarshaling events") + } + for _, e := range serviceUsageEventsResponse.Resources { + e.Entity.GUID = e.Meta.Guid + e.Entity.CreatedAt = e.Meta.CreatedAt + e.Entity.c = c + serviceUsageEvents = append(serviceUsageEvents, e.Entity) + } + requestURL = serviceUsageEventsResponse.NextURL + if requestURL == "" { + break + } + } + return serviceUsageEvents, nil +} + +// ListServiceUsageEvents lists all unfiltered events. +func (c *Client) ListServiceUsageEvents() ([]ServiceUsageEvent, error) { + return c.ListServiceUsageEventsByQuery(nil) +} diff --git a/third_party/go-cfclient/service_usage_events_test.go b/third_party/go-cfclient/service_usage_events_test.go new file mode 100644 index 000000000000..9d54a97ab51d --- /dev/null +++ b/third_party/go-cfclient/service_usage_events_test.go @@ -0,0 +1,54 @@ +package cfclient + +import ( + "net/url" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListServiceUsageEvents(t *testing.T) { + Convey("List Service Usage Events", t, func() { + + setup(MockRoute{"GET", "/v2/service_usage_events", []string{listServiceUsageEventsPayload, listServiceUsageEventsPayloadPage2}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + serviceUsageEvents, err := client.ListServiceUsageEvents() + So(err, ShouldBeNil) + + So(len(serviceUsageEvents), ShouldEqual, 4) + So(serviceUsageEvents[0].GUID, ShouldEqual, "985c09c5-bf5a-44eb-a260-41c532dc0f1d") + So(serviceUsageEvents[0].CreatedAt, ShouldEqual, "2016-06-08T16:41:39Z") + }) +} + +func TestListServiceUsageEventsByQuery(t *testing.T) { + Convey("List Service Usage Events", t, func() { + setup(MockRoute{"GET", "/v2/service_usage_events", []string{listServiceUsageEventsPayload, listServiceUsageEventsPayloadPage2}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + var query = url.Values{ + "results-per-page": []string{ + "2", + }, + } + serviceUsageEvents, err := client.ListServiceUsageEventsByQuery(query) + So(err, ShouldBeNil) + + So(len(serviceUsageEvents), ShouldEqual, 4) + So(serviceUsageEvents[0].GUID, ShouldEqual, "985c09c5-bf5a-44eb-a260-41c532dc0f1d") + So(serviceUsageEvents[0].CreatedAt, ShouldEqual, "2016-06-08T16:41:39Z") + }) +} diff --git a/third_party/go-cfclient/services.go b/third_party/go-cfclient/services.go new file mode 100644 index 000000000000..b93e4c874d7a --- /dev/null +++ b/third_party/go-cfclient/services.go @@ -0,0 +1,128 @@ +package cfclient + +import ( + "encoding/json" + "io/ioutil" + "net/url" + + "github.com/pkg/errors" +) + +type ServicesResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []ServicesResource `json:"resources"` +} + +type ServicesResource struct { + Meta Meta `json:"metadata"` + Entity Service `json:"entity"` +} + +type Service struct { + Guid string `json:"guid"` + Label string `json:"label"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Description string `json:"description"` + Active bool `json:"active"` + Bindable bool `json:"bindable"` + ServiceBrokerGuid string `json:"service_broker_guid"` + ServiceBrokerName string `json:"service_broker_name"` + PlanUpdateable bool `json:"plan_updateable"` + Tags []string `json:"tags"` + UniqueID string `json:"unique_id"` + Extra string `json:"extra"` + Requires []string `json:"requires"` + InstancesRetrievable bool `json:"instances_retrievable"` + BindingsRetrievable bool `json:"bindings_retrievable"` + c *Client +} + +type ServiceSummary struct { + Guid string `json:"guid"` + Name string `json:"name"` + BoundAppCount int `json:"bound_app_count"` + DashboardURL string `json:"dashboard_url"` + ServiceBrokerName string `json:"service_broker_name"` + MaintenanceInfo MaintenanceInfo `json:"maintenance_info"` + ServicePlan struct { + Guid string `json:"guid"` + Name string `json:"name"` + MaintenanceInfo MaintenanceInfo `json:"maintenance_info"` + Service struct { + Guid string `json:"guid"` + Label string `json:"label"` + Provider string `json:"provider"` + Version string `json:"version"` + } `json:"service"` + } `json:"service_plan"` +} + +type MaintenanceInfo struct { + Version string `json:"version"` + Description string `json:"description"` +} + +func (c *Client) GetServiceByGuid(guid string) (Service, error) { + var serviceRes ServicesResource + r := c.NewRequest("GET", "/v2/services/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return Service{}, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return Service{}, err + } + err = json.Unmarshal(body, &serviceRes) + if err != nil { + return Service{}, err + } + serviceRes.Entity.Guid = serviceRes.Meta.Guid + serviceRes.Entity.CreatedAt = serviceRes.Meta.CreatedAt + serviceRes.Entity.UpdatedAt = serviceRes.Meta.UpdatedAt + return serviceRes.Entity, nil + +} + +func (c *Client) ListServicesByQuery(query url.Values) ([]Service, error) { + var services []Service + requestURL := "/v2/services?" + query.Encode() + for { + var serviceResp ServicesResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting services") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading services request:") + } + + err = json.Unmarshal(resBody, &serviceResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling services") + } + for _, service := range serviceResp.Resources { + service.Entity.Guid = service.Meta.Guid + service.Entity.CreatedAt = service.Meta.CreatedAt + service.Entity.UpdatedAt = service.Meta.UpdatedAt + service.Entity.c = c + services = append(services, service.Entity) + } + requestURL = serviceResp.NextUrl + if requestURL == "" { + break + } + } + return services, nil +} + +func (c *Client) ListServices() ([]Service, error) { + return c.ListServicesByQuery(nil) +} diff --git a/third_party/go-cfclient/services_test.go b/third_party/go-cfclient/services_test.go new file mode 100644 index 000000000000..bb1b90b1f66f --- /dev/null +++ b/third_party/go-cfclient/services_test.go @@ -0,0 +1,66 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListServices(t *testing.T) { + Convey("List Services", t, func() { + setup(MockRoute{"GET", "/v2/services", []string{listServicePayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + services, err := client.ListServices() + So(err, ShouldBeNil) + + So(len(services), ShouldEqual, 2) + So(services[0].Guid, ShouldEqual, "a3d76c01-c08a-4505-b06d-8603265682a3") + So(services[0].Label, ShouldEqual, "nats") + So(services[0].Description, ShouldEqual, "NATS is a lightweight cloud messaging system") + So(services[0].Active, ShouldEqual, true) + So(services[0].Bindable, ShouldEqual, true) + So(services[0].PlanUpdateable, ShouldEqual, false) + So(services[1].ServiceBrokerGuid, ShouldEqual, "a4bdf03a-f0c4-43f9-9c77-f434da91404f") + So(services[1].Guid, ShouldEqual, "ab9ad9c8-1f51-463a-ae3a-5082e9f04ae6") + So(services[1].Label, ShouldEqual, "etcd") + So(services[1].Description, ShouldEqual, "Etcd key-value storage") + So(services[1].Active, ShouldEqual, true) + So(services[1].Bindable, ShouldEqual, true) + So(services[1].PlanUpdateable, ShouldEqual, false) + So(services[1].ServiceBrokerGuid, ShouldEqual, "a4bdf03a-f0c4-43f9-9c77-f434da91404f") + So(len(services[1].Tags), ShouldEqual, 3) + So(services[1].Tags[0], ShouldEqual, "etcd") + So(services[1].Tags[1], ShouldEqual, "keyvalue") + So(services[1].Tags[2], ShouldEqual, "etcd-0.4.6") + }) +} + +func TestGetServiceByGuid(t *testing.T) { + Convey("Get Service By Guid", t, func() { + setup(MockRoute{"GET", "/v2/services/53f52780-e93c-4af7-a96c-6958311c40e5", []string{getServiceByGuidPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + service, err := client.GetServiceByGuid("53f52780-e93c-4af7-a96c-6958311c40e5") + So(err, ShouldBeNil) + + So(service.Guid, ShouldEqual, "53f52780-e93c-4af7-a96c-6958311c40e5") + So(service.Label, ShouldEqual, "label-58") + So(service.Description, ShouldEqual, "desc-135") + So(service.Active, ShouldEqual, true) + So(service.Bindable, ShouldEqual, true) + So(service.PlanUpdateable, ShouldEqual, false) + }) +} diff --git a/third_party/go-cfclient/space_quotas.go b/third_party/go-cfclient/space_quotas.go new file mode 100644 index 000000000000..216ba5097978 --- /dev/null +++ b/third_party/go-cfclient/space_quotas.go @@ -0,0 +1,187 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type SpaceQuotasResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []SpaceQuotasResource `json:"resources"` +} + +type SpaceQuotasResource struct { + Meta Meta `json:"metadata"` + Entity SpaceQuota `json:"entity"` +} + +type SpaceQuotaRequest struct { + Name string `json:"name"` + OrganizationGuid string `json:"organization_guid"` + NonBasicServicesAllowed bool `json:"non_basic_services_allowed"` + TotalServices int `json:"total_services"` + TotalRoutes int `json:"total_routes"` + MemoryLimit int `json:"memory_limit"` + InstanceMemoryLimit int `json:"instance_memory_limit"` + AppInstanceLimit int `json:"app_instance_limit"` + AppTaskLimit int `json:"app_task_limit"` + TotalServiceKeys int `json:"total_service_keys"` + TotalReservedRoutePorts int `json:"total_reserved_route_ports"` +} + +type SpaceQuota struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Name string `json:"name"` + OrganizationGuid string `json:"organization_guid"` + NonBasicServicesAllowed bool `json:"non_basic_services_allowed"` + TotalServices int `json:"total_services"` + TotalRoutes int `json:"total_routes"` + MemoryLimit int `json:"memory_limit"` + InstanceMemoryLimit int `json:"instance_memory_limit"` + AppInstanceLimit int `json:"app_instance_limit"` + AppTaskLimit int `json:"app_task_limit"` + TotalServiceKeys int `json:"total_service_keys"` + TotalReservedRoutePorts int `json:"total_reserved_route_ports"` + c *Client +} + +func (c *Client) ListSpaceQuotasByQuery(query url.Values) ([]SpaceQuota, error) { + var spaceQuotas []SpaceQuota + requestUrl := "/v2/space_quota_definitions?" + query.Encode() + for { + spaceQuotasResp, err := c.getSpaceQuotasResponse(requestUrl) + if err != nil { + return []SpaceQuota{}, err + } + for _, space := range spaceQuotasResp.Resources { + space.Entity.Guid = space.Meta.Guid + space.Entity.CreatedAt = space.Meta.CreatedAt + space.Entity.UpdatedAt = space.Meta.UpdatedAt + space.Entity.c = c + spaceQuotas = append(spaceQuotas, space.Entity) + } + requestUrl = spaceQuotasResp.NextUrl + if requestUrl == "" { + break + } + } + return spaceQuotas, nil +} + +func (c *Client) ListSpaceQuotas() ([]SpaceQuota, error) { + return c.ListSpaceQuotasByQuery(nil) +} + +func (c *Client) GetSpaceQuotaByName(name string) (SpaceQuota, error) { + q := url.Values{} + q.Set("q", "name:"+name) + spaceQuotas, err := c.ListSpaceQuotasByQuery(q) + if err != nil { + return SpaceQuota{}, err + } + if len(spaceQuotas) != 1 { + return SpaceQuota{}, fmt.Errorf("Unable to find space quota " + name) + } + return spaceQuotas[0], nil +} + +func (c *Client) getSpaceQuotasResponse(requestUrl string) (SpaceQuotasResponse, error) { + var spaceQuotasResp SpaceQuotasResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return SpaceQuotasResponse{}, errors.Wrap(err, "Error requesting space quotas") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return SpaceQuotasResponse{}, errors.Wrap(err, "Error reading space quotas body") + } + err = json.Unmarshal(resBody, &spaceQuotasResp) + if err != nil { + return SpaceQuotasResponse{}, errors.Wrap(err, "Error unmarshalling space quotas") + } + return spaceQuotasResp, nil +} + +func (c *Client) AssignSpaceQuota(quotaGUID, spaceGUID string) error { + // Perform the PUT and check for errors + resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/space_quota_definitions/%s/spaces/%s", quotaGUID, spaceGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { // 201 + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +func (c *Client) CreateSpaceQuota(spaceQuote SpaceQuotaRequest) (*SpaceQuota, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(spaceQuote) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("POST", "/v2/space_quota_definitions", buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleSpaceQuotaResp(resp) +} + +func (c *Client) UpdateSpaceQuota(spaceQuotaGUID string, spaceQuote SpaceQuotaRequest) (*SpaceQuota, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(spaceQuote) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/space_quota_definitions/%s", spaceQuotaGUID), buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleSpaceQuotaResp(resp) +} + +func (c *Client) handleSpaceQuotaResp(resp *http.Response) (*SpaceQuota, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + var spaceQuotasResource SpaceQuotasResource + err = json.Unmarshal(body, &spaceQuotasResource) + if err != nil { + return nil, err + } + return c.mergeSpaceQuotaResource(spaceQuotasResource), nil +} + +func (c *Client) mergeSpaceQuotaResource(spaceQuote SpaceQuotasResource) *SpaceQuota { + spaceQuote.Entity.Guid = spaceQuote.Meta.Guid + spaceQuote.Entity.CreatedAt = spaceQuote.Meta.CreatedAt + spaceQuote.Entity.UpdatedAt = spaceQuote.Meta.UpdatedAt + spaceQuote.Entity.c = c + return &spaceQuote.Entity +} diff --git a/third_party/go-cfclient/space_quotas_test.go b/third_party/go-cfclient/space_quotas_test.go new file mode 100644 index 000000000000..535d66eea45f --- /dev/null +++ b/third_party/go-cfclient/space_quotas_test.go @@ -0,0 +1,134 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListSpaceQuotas(t *testing.T) { + Convey("List Space Quotas", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/space_quota_definitions", []string{listSpaceQuotasPayloadPage1}, "", 200, "", nil}, + {"GET", "/v2/space_quota_definitions_page_2", []string{listSpaceQuotasPayloadPage2}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + spaceQuotas, err := client.ListSpaceQuotas() + So(err, ShouldBeNil) + + So(len(spaceQuotas), ShouldEqual, 2) + So(spaceQuotas[0].Guid, ShouldEqual, "889aa2ed-a883-4cc0-abe5-804b2503f15d") + So(spaceQuotas[0].Name, ShouldEqual, "test-1") + So(spaceQuotas[0].NonBasicServicesAllowed, ShouldEqual, true) + So(spaceQuotas[0].TotalServices, ShouldEqual, -1) + So(spaceQuotas[0].TotalRoutes, ShouldEqual, 100) + So(spaceQuotas[0].MemoryLimit, ShouldEqual, 102400) + So(spaceQuotas[0].InstanceMemoryLimit, ShouldEqual, -1) + So(spaceQuotas[0].AppInstanceLimit, ShouldEqual, -1) + So(spaceQuotas[0].AppTaskLimit, ShouldEqual, -1) + So(spaceQuotas[0].TotalServiceKeys, ShouldEqual, -1) + So(spaceQuotas[0].TotalReservedRoutePorts, ShouldEqual, -1) + }) +} + +func TestGetSpaceQuotaByName(t *testing.T) { + Convey("Get Space Quota By Name", t, func() { + setup(MockRoute{"GET", "/v2/space_quota_definitions", []string{listSpaceQuotasPayloadPage2}, "", 200, "q=name:test-2", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + spaceQuota, err := client.GetSpaceQuotaByName("test-2") + So(err, ShouldBeNil) + + So(spaceQuota.Guid, ShouldEqual, "9ffd7c5c-d83c-4786-b399-b7bd54883977") + So(spaceQuota.Name, ShouldEqual, "test-2") + So(spaceQuota.NonBasicServicesAllowed, ShouldEqual, false) + So(spaceQuota.TotalServices, ShouldEqual, 10) + So(spaceQuota.TotalRoutes, ShouldEqual, 20) + So(spaceQuota.MemoryLimit, ShouldEqual, 30) + So(spaceQuota.InstanceMemoryLimit, ShouldEqual, 40) + So(spaceQuota.AppInstanceLimit, ShouldEqual, 50) + So(spaceQuota.AppTaskLimit, ShouldEqual, 60) + So(spaceQuota.TotalServiceKeys, ShouldEqual, 70) + So(spaceQuota.TotalReservedRoutePorts, ShouldEqual, 80) + }) +} + +func TestCreateSpaceQuota(t *testing.T) { + Convey("Create Space Quota", t, func() { + setup(MockRoute{"POST", "/v2/space_quota_definitions", []string{spaceQuotaPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + spaceQuotaRequest := SpaceQuotaRequest{ + Name: "test-2", + OrganizationGuid: "06dcedd4-1f24-49a6-adc1-cce9131a1b2c", + } + + spaceQuota, err := client.CreateSpaceQuota(spaceQuotaRequest) + So(err, ShouldBeNil) + + So(spaceQuota.Name, ShouldEqual, "test-2") + So(spaceQuota.OrganizationGuid, ShouldEqual, "06dcedd4-1f24-49a6-adc1-cce9131a1b2c") + + }) +} + +func TestUpdateSpaceQuota(t *testing.T) { + Convey("Create Update Quota", t, func() { + setup(MockRoute{"PUT", "/v2/space_quota_definitions/9ffd7c5c-d83c-4786-b399-b7bd54883977", []string{spaceQuotaPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + spaceQuotaRequest := SpaceQuotaRequest{ + Name: "test-2", + OrganizationGuid: "06dcedd4-1f24-49a6-adc1-cce9131a1b2c", + } + + spaceQuota, err := client.UpdateSpaceQuota("9ffd7c5c-d83c-4786-b399-b7bd54883977", spaceQuotaRequest) + So(err, ShouldBeNil) + + So(spaceQuota.Name, ShouldEqual, "test-2") + So(spaceQuota.OrganizationGuid, ShouldEqual, "06dcedd4-1f24-49a6-adc1-cce9131a1b2c") + + }) +} + +func TestAssignSpaceQuota(t *testing.T) { + Convey("Assign Space Quota", t, func() { + setup(MockRoute{"PUT", "/v2/space_quota_definitions/9ffd7c5c-d83c-4786-b399-b7bd54883977/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1", []string{""}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.AssignSpaceQuota("9ffd7c5c-d83c-4786-b399-b7bd54883977", "8efd7c5c-d83c-4786-b399-b7bd548839e1") + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/spaces.go b/third_party/go-cfclient/spaces.go new file mode 100644 index 000000000000..c85e5b0051ae --- /dev/null +++ b/third_party/go-cfclient/spaces.go @@ -0,0 +1,829 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + + "github.com/pkg/errors" +) + +type SpaceRequest struct { + Name string `json:"name"` + OrganizationGuid string `json:"organization_guid"` + DeveloperGuid []string `json:"developer_guids,omitempty"` + ManagerGuid []string `json:"manager_guids,omitempty"` + AuditorGuid []string `json:"auditor_guids,omitempty"` + DomainGuid []string `json:"domain_guids,omitempty"` + SecurityGroupGuids []string `json:"security_group_guids,omitempty"` + SpaceQuotaDefGuid string `json:"space_quota_definition_guid,omitempty"` + IsolationSegmentGuid string `json:"isolation_segment_guid,omitempty"` + AllowSSH bool `json:"allow_ssh"` +} + +type SpaceResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []SpaceResource `json:"resources"` +} + +type SpaceResource struct { + Meta Meta `json:"metadata"` + Entity Space `json:"entity"` +} + +type ServicePlanEntity struct { + Name string `json:"name"` + Free bool `json:"free"` + Public bool `json:"public"` + Active bool `json:"active"` + Description string `json:"description"` + ServiceOfferingGUID string `json:"service_guid"` + ServiceOffering ServiceOfferingResource `json:"service"` +} + +type ServiceOfferingExtra struct { + DisplayName string `json:"displayName"` + DocumentationURL string `json:"documentationURL"` + LongDescription string `json:"longDescription"` +} + +type ServiceOfferingEntity struct { + Label string + Description string + Provider string `json:"provider"` + BrokerGUID string `json:"service_broker_guid"` + Requires []string `json:"requires"` + ServicePlans []interface{} `json:"service_plans"` + Extra ServiceOfferingExtra +} + +type ServiceOfferingResource struct { + Metadata Meta + Entity ServiceOfferingEntity +} + +type ServiceOfferingResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + PrevUrl string `json:"prev_url"` + Resources []ServiceOfferingResource `json:"resources"` +} + +type SpaceUserResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextURL string `json:"next_url"` + Resources []UserResource `json:"resources"` +} + +type Space struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Name string `json:"name"` + OrganizationGuid string `json:"organization_guid"` + OrgURL string `json:"organization_url"` + OrgData OrgResource `json:"organization"` + QuotaDefinitionGuid string `json:"space_quota_definition_guid"` + IsolationSegmentGuid string `json:"isolation_segment_guid"` + AllowSSH bool `json:"allow_ssh"` + c *Client +} + +type SpaceSummary struct { + Guid string `json:"guid"` + Name string `json:"name"` + Apps []AppSummary `json:"apps"` + Services []ServiceSummary `json:"services"` +} + +type SpaceRoleResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []SpaceRoleResource `json:"resources"` +} + +type SpaceRoleResource struct { + Meta Meta `json:"metadata"` + Entity SpaceRole `json:"entity"` +} + +type SpaceRole struct { + Guid string `json:"guid"` + Admin bool `json:"admin"` + Active bool `json:"active"` + DefaultSpaceGuid string `json:"default_space_guid"` + Username string `json:"username"` + SpaceRoles []string `json:"space_roles"` + SpacesUrl string `json:"spaces_url"` + OrganizationsUrl string `json:"organizations_url"` + ManagedOrganizationsUrl string `json:"managed_organizations_url"` + BillingManagedOrganizationsUrl string `json:"billing_managed_organizations_url"` + AuditedOrganizationsUrl string `json:"audited_organizations_url"` + ManagedSpacesUrl string `json:"managed_spaces_url"` + AuditedSpacesUrl string `json:"audited_spaces_url"` + c *Client +} + +func (s *Space) Org() (Org, error) { + var orgResource OrgResource + r := s.c.NewRequest("GET", s.OrgURL) + resp, err := s.c.DoRequest(r) + if err != nil { + return Org{}, errors.Wrap(err, "Error requesting org") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return Org{}, errors.Wrap(err, "Error reading org request") + } + + err = json.Unmarshal(resBody, &orgResource) + if err != nil { + return Org{}, errors.Wrap(err, "Error unmarshaling org") + } + return s.c.mergeOrgResource(orgResource), nil +} + +func (s *Space) Quota() (*SpaceQuota, error) { + var spaceQuota *SpaceQuota + var spaceQuotaResource SpaceQuotasResource + if s.QuotaDefinitionGuid == "" { + return nil, nil + } + requestUrl := fmt.Sprintf("/v2/space_quota_definitions/%s", s.QuotaDefinitionGuid) + r := s.c.NewRequest("GET", requestUrl) + resp, err := s.c.DoRequest(r) + if err != nil { + return &SpaceQuota{}, errors.Wrap(err, "Error requesting space quota") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return &SpaceQuota{}, errors.Wrap(err, "Error reading space quota body") + } + err = json.Unmarshal(resBody, &spaceQuotaResource) + if err != nil { + return &SpaceQuota{}, errors.Wrap(err, "Error unmarshalling space quota") + } + spaceQuota = &spaceQuotaResource.Entity + spaceQuota.Guid = spaceQuotaResource.Meta.Guid + spaceQuota.c = s.c + return spaceQuota, nil +} + +func (s *Space) Summary() (SpaceSummary, error) { + var spaceSummary SpaceSummary + requestUrl := fmt.Sprintf("/v2/spaces/%s/summary", s.Guid) + r := s.c.NewRequest("GET", requestUrl) + resp, err := s.c.DoRequest(r) + if err != nil { + return SpaceSummary{}, errors.Wrap(err, "Error requesting space summary") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return SpaceSummary{}, errors.Wrap(err, "Error reading space summary body") + } + err = json.Unmarshal(resBody, &spaceSummary) + if err != nil { + return SpaceSummary{}, errors.Wrap(err, "Error unmarshalling space summary") + } + return spaceSummary, nil +} + +func (s *Space) Roles() ([]SpaceRole, error) { + var roles []SpaceRole + requestUrl := fmt.Sprintf("/v2/spaces/%s/user_roles", s.Guid) + for { + rolesResp, err := s.c.getSpaceRolesResponse(requestUrl) + if err != nil { + return roles, err + } + for _, role := range rolesResp.Resources { + role.Entity.Guid = role.Meta.Guid + role.Entity.c = s.c + roles = append(roles, role.Entity) + } + requestUrl = rolesResp.NextUrl + if requestUrl == "" { + break + } + } + return roles, nil +} + +func (c *Client) CreateSpace(req SpaceRequest) (Space, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return Space{}, err + } + r := c.NewRequestWithBody("POST", "/v2/spaces", buf) + resp, err := c.DoRequest(r) + if err != nil { + return Space{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Space{}, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleSpaceResp(resp) +} + +func (c *Client) UpdateSpace(spaceGUID string, req SpaceRequest) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.Update(req) +} + +func (c *Client) DeleteSpace(guid string, recursive, async bool) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/spaces/%s?recursive=%t&async=%t", guid, recursive, async))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting space %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) ListSpaceManagersByQuery(spaceGUID string, query url.Values) ([]User, error) { + return c.listSpaceUsersByRoleAndQuery(spaceGUID, "managers", query) +} + +func (c *Client) ListSpaceManagers(spaceGUID string) ([]User, error) { + return c.ListSpaceManagersByQuery(spaceGUID, nil) +} + +func (c *Client) ListSpaceAuditorsByQuery(spaceGUID string, query url.Values) ([]User, error) { + return c.listSpaceUsersByRoleAndQuery(spaceGUID, "auditors", query) +} + +func (c *Client) ListSpaceAuditors(spaceGUID string) ([]User, error) { + return c.ListSpaceAuditorsByQuery(spaceGUID, nil) +} + +func (c *Client) ListSpaceDevelopersByQuery(spaceGUID string, query url.Values) ([]User, error) { + return c.listSpaceUsersByRoleAndQuery(spaceGUID, "developers", query) +} + +func (c *Client) listSpaceUsersByRoleAndQuery(spaceGUID, role string, query url.Values) ([]User, error) { + var users []User + requestURL := fmt.Sprintf("/v2/spaces/%s/%s?%s", spaceGUID, role, query.Encode()) + for { + userResp, err := c.getUserResponse(requestURL) + if err != nil { + return []User{}, err + } + for _, u := range userResp.Resources { + users = append(users, c.mergeUserResource(u)) + } + requestURL = userResp.NextUrl + if requestURL == "" { + break + } + } + return users, nil +} + +func (c *Client) ListSpaceDevelopers(spaceGUID string) ([]User, error) { + return c.ListSpaceDevelopersByQuery(spaceGUID, nil) +} + +func (c *Client) ListSpaceServiceInstances(spaceGUID string) ([]ServiceInstance, error) { + return c.ListSpaceServiceInstancesByQuery(spaceGUID, nil) +} + +func (c *Client) ListSpaceServiceInstancesByQuery(spaceGUID string, query url.Values) ([]ServiceInstance, error) { + var instances []ServiceInstance + requestURL := fmt.Sprintf("/v2/spaces/%s/service_instances?%s", spaceGUID, query.Encode()) + for { + res, err := c.getServiceInstancesResponse(requestURL) + if err != nil { + return instances, err + } + for _, instance := range res.Resources { + instances = append(instances, c.mergeServiceInstance(instance)) + } + requestURL = res.NextUrl + if requestURL == "" || query.Get("page") != "" { + break + } + } + return instances, nil +} + +func (c *Client) AssociateSpaceDeveloper(spaceGUID, userGUID string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateDeveloper(userGUID) +} + +func (c *Client) AssociateSpaceDeveloperByUsername(spaceGUID, name string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateDeveloperByUsername(name) +} + +func (c *Client) AssociateSpaceDeveloperByUsernameAndOrigin(spaceGUID, name, origin string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateDeveloperByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveSpaceDeveloper(spaceGUID, userGUID string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveDeveloper(userGUID) +} + +func (c *Client) RemoveSpaceDeveloperByUsername(spaceGUID, name string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveDeveloperByUsername(name) +} + +func (c *Client) RemoveSpaceDeveloperByUsernameAndOrigin(spaceGUID, name, origin string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveDeveloperByUsernameAndOrigin(name, origin) +} + +func (c *Client) AssociateSpaceAuditor(spaceGUID, userGUID string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateAuditor(userGUID) +} + +func (c *Client) AssociateSpaceAuditorByUsername(spaceGUID, name string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateAuditorByUsername(name) +} + +func (c *Client) AssociateSpaceAuditorByUsernameAndOrigin(spaceGUID, name, origin string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateAuditorByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveSpaceAuditor(spaceGUID, userGUID string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveAuditor(userGUID) +} + +func (c *Client) RemoveSpaceAuditorByUsername(spaceGUID, name string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveAuditorByUsername(name) +} + +func (c *Client) RemoveSpaceAuditorByUsernameAndOrigin(spaceGUID, name, origin string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveAuditorByUsernameAndOrigin(name, origin) +} + +func (c *Client) AssociateSpaceManager(spaceGUID, userGUID string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateManager(userGUID) +} + +func (c *Client) AssociateSpaceManagerByUsername(spaceGUID, name string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateManagerByUsername(name) +} + +func (c *Client) AssociateSpaceManagerByUsernameAndOrigin(spaceGUID, name, origin string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateManagerByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveSpaceManager(spaceGUID, userGUID string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveManager(userGUID) +} + +func (c *Client) RemoveSpaceManagerByUsername(spaceGUID, name string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveManagerByUsername(name) +} + +func (c *Client) RemoveSpaceManagerByUsernameAndOrigin(spaceGUID, name, origin string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveManagerByUsernameAndOrigin(name, origin) +} + +func (s *Space) AssociateDeveloper(userGUID string) (Space, error) { + return s.associateRole(userGUID, "developers") +} + +func (s *Space) AssociateDeveloperByUsername(name string) (Space, error) { + return s.associateUserByRole(name, "developers", "") +} + +func (s *Space) AssociateDeveloperByUsernameAndOrigin(name, origin string) (Space, error) { + return s.associateUserByRole(name, "developers", origin) +} + +func (s *Space) RemoveDeveloper(userGUID string) error { + return s.removeRole(userGUID, "developers") +} + +func (s *Space) RemoveDeveloperByUsername(name string) error { + return s.removeUserByRole(name, "developers", "") +} + +func (s *Space) RemoveDeveloperByUsernameAndOrigin(name, origin string) error { + return s.removeUserByRole(name, "developers", origin) +} + +func (s *Space) AssociateAuditor(userGUID string) (Space, error) { + return s.associateRole(userGUID, "auditors") +} + +func (s *Space) AssociateAuditorByUsername(name string) (Space, error) { + return s.associateUserByRole(name, "auditors", "") +} + +func (s *Space) AssociateAuditorByUsernameAndOrigin(name, origin string) (Space, error) { + return s.associateUserByRole(name, "auditors", origin) +} + +func (s *Space) RemoveAuditor(userGUID string) error { + return s.removeRole(userGUID, "auditors") +} + +func (s *Space) RemoveAuditorByUsername(name string) error { + return s.removeUserByRole(name, "auditors", "") +} + +func (s *Space) RemoveAuditorByUsernameAndOrigin(name, origin string) error { + return s.removeUserByRole(name, "auditors", origin) +} + +func (s *Space) AssociateManager(userGUID string) (Space, error) { + return s.associateRole(userGUID, "managers") +} + +func (s *Space) AssociateManagerByUsername(name string) (Space, error) { + return s.associateUserByRole(name, "managers", "") +} + +func (s *Space) AssociateManagerByUsernameAndOrigin(name, origin string) (Space, error) { + return s.associateUserByRole(name, "managers", origin) +} + +func (s *Space) RemoveManager(userGUID string) error { + return s.removeRole(userGUID, "managers") +} + +func (s *Space) RemoveManagerByUsername(name string) error { + return s.removeUserByRole(name, "managers", "") +} +func (s *Space) RemoveManagerByUsernameAndOrigin(name, origin string) error { + return s.removeUserByRole(name, "managers", origin) +} + +func (s *Space) associateRole(userGUID, role string) (Space, error) { + requestUrl := fmt.Sprintf("/v2/spaces/%s/%s/%s", s.Guid, role, userGUID) + r := s.c.NewRequest("PUT", requestUrl) + resp, err := s.c.DoRequest(r) + if err != nil { + return Space{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Space{}, errors.Wrapf(err, "Error associating %s %s, response code: %d", role, userGUID, resp.StatusCode) + } + return s.c.handleSpaceResp(resp) +} + +func (s *Space) associateUserByRole(name, role, origin string) (Space, error) { + requestUrl := fmt.Sprintf("/v2/spaces/%s/%s", s.Guid, role) + buf := bytes.NewBuffer(nil) + payload := make(map[string]string) + payload["username"] = name + if origin != "" { + payload["origin"] = origin + } + err := json.NewEncoder(buf).Encode(payload) + if err != nil { + return Space{}, err + } + r := s.c.NewRequestWithBody("PUT", requestUrl, buf) + resp, err := s.c.DoRequest(r) + if err != nil { + return Space{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Space{}, errors.Wrapf(err, "Error associating %s %s, response code: %d", role, name, resp.StatusCode) + } + return s.c.handleSpaceResp(resp) +} + +func (s *Space) removeRole(userGUID, role string) error { + requestUrl := fmt.Sprintf("/v2/spaces/%s/%s/%s", s.Guid, role, userGUID) + r := s.c.NewRequest("DELETE", requestUrl) + resp, err := s.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error removing %s %s, response code: %d", role, userGUID, resp.StatusCode) + } + return nil +} + +func (s *Space) removeUserByRole(name, role, origin string) error { + var requestURL string + var method string + + buf := bytes.NewBuffer(nil) + payload := make(map[string]string) + payload["username"] = name + if origin != "" { + payload["origin"] = origin + requestURL = fmt.Sprintf("/v2/spaces/%s/%s/remove", s.Guid, role) + method = "POST" + } else { + requestURL = fmt.Sprintf("/v2/spaces/%s/%s", s.Guid, role) + method = "DELETE" + } + err := json.NewEncoder(buf).Encode(payload) + if err != nil { + return err + } + r := s.c.NewRequestWithBody(method, requestURL, buf) + resp, err := s.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "Error removing %s %s, response code: %d", role, name, resp.StatusCode) + } + return nil +} + +func (c *Client) ListSpaceSecGroups(spaceGUID string) (secGroups []SecGroup, err error) { + space := Space{Guid: spaceGUID, c: c} + return space.ListSecGroups() +} + +func (s *Space) ListSecGroups() (secGroups []SecGroup, err error) { + requestURL := fmt.Sprintf("/v2/spaces/%s/security_groups?inline-relations-depth=1", s.Guid) + for requestURL != "" { + var secGroupResp SecGroupResponse + r := s.c.NewRequest("GET", requestURL) + resp, err := s.c.DoRequest(r) + + if err != nil { + return nil, errors.Wrap(err, "Error requesting sec groups") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading sec group response body") + } + + err = json.Unmarshal(resBody, &secGroupResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling sec group") + } + + for _, secGroup := range secGroupResp.Resources { + secGroup.Entity.Guid = secGroup.Meta.Guid + secGroup.Entity.c = s.c + for i, space := range secGroup.Entity.SpacesData { + space.Entity.Guid = space.Meta.Guid + secGroup.Entity.SpacesData[i] = space + } + if len(secGroup.Entity.SpacesData) == 0 { + spaces, err := secGroup.Entity.ListSpaceResources() + if err != nil { + return nil, err + } + secGroup.Entity.SpacesData = append(secGroup.Entity.SpacesData, spaces...) + } + secGroups = append(secGroups, secGroup.Entity) + } + + requestURL = secGroupResp.NextUrl + resp.Body.Close() + } + return secGroups, nil +} + +func (s *Space) GetServiceOfferings() (ServiceOfferingResponse, error) { + var response ServiceOfferingResponse + requestURL := fmt.Sprintf("/v2/spaces/%s/services", s.Guid) + req := s.c.NewRequest("GET", requestURL) + + resp, err := s.c.DoRequest(req) + if err != nil { + return ServiceOfferingResponse{}, errors.Wrap(err, "Error requesting service offerings") + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServiceOfferingResponse{}, errors.Wrap(err, "Error reading service offering response") + } + + err = json.Unmarshal(body, &response) + if err != nil { + return ServiceOfferingResponse{}, errors.Wrap(err, "Error unmarshalling service offering response") + } + + return response, nil +} + +func (s *Space) Update(req SpaceRequest) (Space, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return Space{}, err + } + r := s.c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/spaces/%s", s.Guid), buf) + resp, err := s.c.DoRequest(r) + if err != nil { + return Space{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Space{}, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return s.c.handleSpaceResp(resp) +} + +func (c *Client) ListSpacesByQuery(query url.Values) ([]Space, error) { + return c.fetchSpaces("/v2/spaces", query) +} + +func (c *Client) ListSpacesByOrgGuid(orgGuid string) ([]Space, error) { + return c.fetchSpaces(fmt.Sprintf("/v2/organizations/%s/spaces", orgGuid), url.Values{}) +} + +func (c *Client) ListSpaces() ([]Space, error) { + return c.ListSpacesByQuery(nil) +} + +func (c *Client) fetchSpaces(path string, query url.Values) ([]Space, error) { + requestUrl := path + "?" + query.Encode() + var spaces []Space + for { + spaceResp, err := c.getSpaceResponse(requestUrl) + if err != nil { + return []Space{}, err + } + for _, space := range spaceResp.Resources { + spaces = append(spaces, c.mergeSpaceResource(space)) + } + requestUrl = spaceResp.NextUrl + if requestUrl == "" || query.Get("page") != "" { + break + } + } + return spaces, nil +} + +func (c *Client) GetSpaceByName(spaceName string, orgGuid string) (Space, error) { + query := url.Values{} + query.Add("q", fmt.Sprintf("organization_guid:%s", orgGuid)) + query.Add("q", fmt.Sprintf("name:%s", spaceName)) + spaces, err := c.ListSpacesByQuery(query) + if err != nil { + return Space{}, err + } + + if len(spaces) == 0 { + cfErr := NewSpaceNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, spaceName) + return Space{}, cfErr + } + + return spaces[0], nil + +} + +func (c *Client) GetSpaceByGuid(spaceGUID string) (Space, error) { + requestUrl := fmt.Sprintf("/v2/spaces/%s", spaceGUID) + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return Space{}, errors.Wrap(err, "Error requesting space info") + } + defer resp.Body.Close() + return c.handleSpaceResp(resp) +} + +func (c *Client) getSpaceResponse(requestUrl string) (SpaceResponse, error) { + var spaceResp SpaceResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return SpaceResponse{}, errors.Wrap(err, "Error requesting spaces") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return SpaceResponse{}, errors.Wrap(err, "Error reading space request") + } + err = json.Unmarshal(resBody, &spaceResp) + if err != nil { + return SpaceResponse{}, errors.Wrap(err, "Error unmarshalling space") + } + return spaceResp, nil +} + +func (c *Client) getSpaceRolesResponse(requestUrl string) (SpaceRoleResponse, error) { + var roleResp SpaceRoleResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return roleResp, errors.Wrap(err, "Error requesting space roles") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return roleResp, errors.Wrap(err, "Error reading space roles request") + } + err = json.Unmarshal(resBody, &roleResp) + if err != nil { + return roleResp, errors.Wrap(err, "Error unmarshalling space roles") + } + return roleResp, nil +} + +func (c *Client) handleSpaceResp(resp *http.Response) (Space, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return Space{}, err + } + var spaceResource SpaceResource + err = json.Unmarshal(body, &spaceResource) + if err != nil { + return Space{}, err + } + return c.mergeSpaceResource(spaceResource), nil +} + +func (c *Client) mergeSpaceResource(space SpaceResource) Space { + space.Entity.Guid = space.Meta.Guid + space.Entity.CreatedAt = space.Meta.CreatedAt + space.Entity.UpdatedAt = space.Meta.UpdatedAt + space.Entity.c = c + return space.Entity +} + +type serviceOfferingExtra ServiceOfferingExtra + +func (resource *ServiceOfferingExtra) UnmarshalJSON(rawData []byte) error { + if string(rawData) == "null" { + return nil + } + + extra := serviceOfferingExtra{} + + unquoted, err := strconv.Unquote(string(rawData)) + if err != nil { + return err + } + + err = json.Unmarshal([]byte(unquoted), &extra) + if err != nil { + return err + } + + *resource = ServiceOfferingExtra(extra) + + return nil +} + +func (c *Client) IsolationSegmentForSpace(spaceGUID, isolationSegmentGUID string) error { + return c.updateSpaceIsolationSegment(spaceGUID, map[string]interface{}{"guid": isolationSegmentGUID}) +} + +func (c *Client) ResetIsolationSegmentForSpace(spaceGUID string) error { + return c.updateSpaceIsolationSegment(spaceGUID, nil) +} + +func (c *Client) updateSpaceIsolationSegment(spaceGUID string, data interface{}) error { + requestURL := fmt.Sprintf("/v3/spaces/%s/relationships/isolation_segment", spaceGUID) + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(map[string]interface{}{"data": data}) + if err != nil { + return err + } + r := c.NewRequestWithBody("PATCH", requestURL, buf) + resp, err := c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "Error setting isolation segment for space %s, response code: %d", spaceGUID, resp.StatusCode) + } + return nil +} diff --git a/third_party/go-cfclient/spaces_test.go b/third_party/go-cfclient/spaces_test.go new file mode 100644 index 000000000000..11b4a198a07e --- /dev/null +++ b/third_party/go-cfclient/spaces_test.go @@ -0,0 +1,786 @@ +package cfclient + +import ( + "fmt" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListSpaces(t *testing.T) { + Convey("List Space", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/spaces", []string{listSpacesPayload}, "", 200, "", nil}, + {"GET", "/v2/spacesPage2", []string{listSpacesPayloadPage2}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + spaces, err := client.ListSpaces() + So(err, ShouldBeNil) + + So(len(spaces), ShouldEqual, 4) + So(spaces[0].Guid, ShouldEqual, "8efd7c5c-d83c-4786-b399-b7bd548839e1") + So(spaces[0].CreatedAt, ShouldEqual, "2014-09-24T13:54:54+00:00") + So(spaces[0].UpdatedAt, ShouldEqual, "2014-09-24T13:54:54+00:00") + So(spaces[0].Name, ShouldEqual, "dev") + So(spaces[0].OrganizationGuid, ShouldEqual, "a537761f-9d93-4b30-af17-3d73dbca181b") + So(spaces[1].Guid, ShouldEqual, "657b5923-7de0-486a-9928-b4d78ee24931") + So(spaces[1].CreatedAt, ShouldEqual, "2014-09-26T13:37:31+00:00") + So(spaces[1].UpdatedAt, ShouldEqual, "2014-09-26T13:37:31+00:00") + So(spaces[1].Name, ShouldEqual, "demo") + So(spaces[1].OrganizationGuid, ShouldEqual, "da0dba14-6064-4f7a-b15a-ff9e677e49b2") + So(spaces[2].Guid, ShouldEqual, "9ffd7c5c-d83c-4786-b399-b7bd54883977") + So(spaces[2].CreatedAt, ShouldEqual, "2014-09-24T13:54:54+00:00") + So(spaces[2].UpdatedAt, ShouldEqual, "2014-09-24T13:54:54+00:00") + So(spaces[2].Name, ShouldEqual, "test") + So(spaces[2].OrganizationGuid, ShouldEqual, "a537761f-9d93-4b30-af17-3d73dbca181b") + So(spaces[3].Guid, ShouldEqual, "329b5923-7de0-486a-9928-b4d78ee24982") + So(spaces[3].CreatedAt, ShouldEqual, "2014-09-26T13:37:31+00:00") + So(spaces[3].UpdatedAt, ShouldEqual, "2014-09-26T13:37:31+00:00") + So(spaces[3].Name, ShouldEqual, "prod") + So(spaces[3].OrganizationGuid, ShouldEqual, "da0dba14-6064-4f7a-b15a-ff9e677e49b2") + }) +} + +func TestListSpaceSecGroups(t *testing.T) { + Convey("List Space SecGroups", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1/security_groups", []string{listSecGroupsPayload}, "", 200, "inline-relations-depth=1", nil}, + {"GET", "/v2/security_groupsPage2", []string{listSecGroupsPayloadPage2}, "", 200, "", nil}, + {"GET", "/v2/security_groups/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c/spaces", []string{emptyResources}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + secGroups, err := client.ListSpaceSecGroups("8efd7c5c-d83c-4786-b399-b7bd548839e1") + So(err, ShouldBeNil) + + So(len(secGroups), ShouldEqual, 2) + So(secGroups[0].Guid, ShouldEqual, "af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c") + So(secGroups[0].Name, ShouldEqual, "secgroup-test") + So(secGroups[0].Running, ShouldEqual, true) + So(secGroups[0].Staging, ShouldEqual, true) + So(secGroups[0].Rules[0].Protocol, ShouldEqual, "tcp") + So(secGroups[0].Rules[0].Ports, ShouldEqual, "443,4443") + So(secGroups[0].Rules[0].Destination, ShouldEqual, "1.1.1.1") + So(secGroups[0].Rules[1].Protocol, ShouldEqual, "udp") + So(secGroups[0].Rules[1].Ports, ShouldEqual, "1111") + So(secGroups[0].Rules[1].Destination, ShouldEqual, "1.2.3.4") + So(secGroups[0].SpacesURL, ShouldEqual, "/v2/security_groups/af15c29a-6bde-4a9b-8cdf-43aa0d4b7e3c/spaces") + So(secGroups[0].SpacesData, ShouldBeEmpty) + So(secGroups[1].Guid, ShouldEqual, "f9ad202b-76dd-44ec-b7c2-fd2417a561e8") + So(secGroups[1].Name, ShouldEqual, "secgroup-test2") + So(secGroups[1].Running, ShouldEqual, false) + So(secGroups[1].Staging, ShouldEqual, false) + So(secGroups[1].Rules[0].Protocol, ShouldEqual, "udp") + So(secGroups[1].Rules[0].Ports, ShouldEqual, "2222") + So(secGroups[1].Rules[0].Destination, ShouldEqual, "2.2.2.2") + So(secGroups[1].Rules[1].Protocol, ShouldEqual, "tcp") + So(secGroups[1].Rules[1].Ports, ShouldEqual, "443,4443") + So(secGroups[1].Rules[1].Destination, ShouldEqual, "4.3.2.1") + So(secGroups[1].SpacesData[0].Entity.Guid, ShouldEqual, "e0a0d1bf-ad74-4b3c-8f4a-0c33859a54e4") + So(secGroups[1].SpacesData[0].Entity.Name, ShouldEqual, "space-test") + So(secGroups[1].SpacesData[1].Entity.Guid, ShouldEqual, "a2a0d1bf-ad74-4b3c-8f4a-0c33859a5333") + So(secGroups[1].SpacesData[1].Entity.Name, ShouldEqual, "space-test2") + So(secGroups[1].SpacesData[2].Entity.Guid, ShouldEqual, "c7a0d1bf-ad74-4b3c-8f4a-0c33859adsa1") + So(secGroups[1].SpacesData[2].Entity.Name, ShouldEqual, "space-test3") + }) +} + +func TestListSpaceManagers(t *testing.T) { + Convey("ListSpaceManagers()", t, func() { + setup(MockRoute{"GET", "/v2/spaces/foo/managers", []string{listSpacePeoplePayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + users, err := client.ListSpaceManagers("foo") + So(err, ShouldBeNil) + So(len(users), ShouldEqual, 2) + So(users[0].Username, ShouldEqual, "user1") + So(users[1].Username, ShouldEqual, "user2") + }) +} + +func TestListSpaceAuditors(t *testing.T) { + Convey("ListSpaceAuditors()", t, func() { + setup(MockRoute{"GET", "/v2/spaces/foo/auditors", []string{listSpacePeoplePayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + users, err := client.ListSpaceAuditors("foo") + So(err, ShouldBeNil) + So(len(users), ShouldEqual, 2) + So(users[0].Username, ShouldEqual, "user1") + So(users[1].Username, ShouldEqual, "user2") + }) +} + +func TestListSpaceDevelopers(t *testing.T) { + Convey("ListSpaceDevelopers()", t, func() { + setup(MockRoute{"GET", "/v2/spaces/foo/developers", []string{listSpacePeoplePayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + users, err := client.ListSpaceDevelopers("foo") + So(err, ShouldBeNil) + So(len(users), ShouldEqual, 2) + So(users[0].Username, ShouldEqual, "user1") + So(users[1].Username, ShouldEqual, "user2") + }) +} + +func TestListSpaceServiceInstances(t *testing.T) { + Convey("ListSpaceServiceInstances()", t, func() { + setup(MockRoute{"GET", "/v2/spaces/494d8b64-8181-4183-a6d3-6279db8fec6e/service_instances", []string{listSpaceServiceInstancesPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + serviceInstances, err := client.ListSpaceServiceInstances("494d8b64-8181-4183-a6d3-6279db8fec6e") + So(err, ShouldBeNil) + + So(len(serviceInstances), ShouldEqual, 2) + So(serviceInstances[0].Guid, ShouldEqual, "9547e9ed-e460-4abe-bda3-7070b9835917") + So(serviceInstances[0].Name, ShouldEqual, "name-2104") + So(serviceInstances[0].Credentials, ShouldHaveLength, 1) + So(serviceInstances[0].ServicePlanGuid, ShouldEqual, "fcf57f7f-3c51-49b2-b252-dc24e0f7dcab") + So(serviceInstances[0].SpaceGuid, ShouldEqual, "f858c6b3-f6b1-4ae8-81dd-8e8747657fbe") + So(serviceInstances[0].DashboardUrl, ShouldBeEmpty) + So(serviceInstances[0].Type, ShouldEqual, "managed_service_instance") + So(serviceInstances[0].Tags, ShouldBeEmpty) + So(serviceInstances[0].ServiceGuid, ShouldBeEmpty) + So(serviceInstances[0].ServiceInstanceParametersUrl, ShouldEqual, "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917/parameters") + So(serviceInstances[0].SharedFromUrl, ShouldEqual, "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917/shared_from") + So(serviceInstances[0].SharedToUrl, ShouldEqual, "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917/shared_to") + So(serviceInstances[0].SpaceUrl, ShouldEqual, "/v2/spaces/f858c6b3-f6b1-4ae8-81dd-8e8747657fbe") + So(serviceInstances[0].ServicePlanUrl, ShouldEqual, "/v2/service_plans/fcf57f7f-3c51-49b2-b252-dc24e0f7dcab") + So(serviceInstances[0].ServiceBindingsUrl, ShouldEqual, "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917/service_bindings") + So(serviceInstances[0].ServiceKeysUrl, ShouldEqual, "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917/service_keys") + So(serviceInstances[0].RoutesUrl, ShouldEqual, "/v2/service_instances/9547e9ed-e460-4abe-bda3-7070b9835917/routes") + So(serviceInstances[0].ServiceUrl, ShouldEqual, "") + So(serviceInstances[1].Guid, ShouldEqual, "07d2f44a-9031-11ec-b909-0242ac120002") + So(serviceInstances[1].Name, ShouldEqual, "name-2105") + So(serviceInstances[1].Credentials, ShouldHaveLength, 1) + So(serviceInstances[1].ServicePlanGuid, ShouldEqual, "22b3ae01-280d-41aa-9e97-68d47680003d") + So(serviceInstances[1].SpaceGuid, ShouldEqual, "f858c6b3-f6b1-4ae8-81dd-8e8747657fbe") + So(serviceInstances[1].DashboardUrl, ShouldBeEmpty) + So(serviceInstances[1].Type, ShouldEqual, "managed_service_instance") + So(serviceInstances[1].Tags, ShouldBeEmpty) + So(serviceInstances[1].ServiceGuid, ShouldBeEmpty) + So(serviceInstances[1].ServiceInstanceParametersUrl, ShouldEqual, "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002/parameters") + So(serviceInstances[1].SharedFromUrl, ShouldEqual, "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002/shared_from") + So(serviceInstances[1].SharedToUrl, ShouldEqual, "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002/shared_to") + So(serviceInstances[1].SpaceUrl, ShouldEqual, "/v2/spaces/f858c6b3-f6b1-4ae8-81dd-8e8747657fbe") + So(serviceInstances[1].ServicePlanUrl, ShouldEqual, "/v2/service_plans/22b3ae01-280d-41aa-9e97-68d47680003d") + So(serviceInstances[1].ServiceBindingsUrl, ShouldEqual, "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002/service_bindings") + So(serviceInstances[1].ServiceKeysUrl, ShouldEqual, "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002/service_keys") + So(serviceInstances[1].RoutesUrl, ShouldEqual, "/v2/service_instances/07d2f44a-9031-11ec-b909-0242ac120002/routes") + So(serviceInstances[1].ServiceUrl, ShouldEqual, "") + }) +} + +func TestCreateSpace(t *testing.T) { + Convey("Create Space", t, func() { + setup(MockRoute{"POST", "/v2/spaces", []string{spacePayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + spaceRequest := SpaceRequest{Name: "test-space", OrganizationGuid: "da0dba14-6064-4f7a-b15a-ff9e677e49b2", AllowSSH: false} + + space, err := client.CreateSpace(spaceRequest) + So(err, ShouldBeNil) + + So(space.Name, ShouldEqual, "test-space") + So(space.OrganizationGuid, ShouldEqual, "da0dba14-6064-4f7a-b15a-ff9e677e49b2") + So(space.AllowSSH, ShouldEqual, false) + }) +} + +func TestUpdateSpace(t *testing.T) { + Convey("Update Space", t, func() { + setup(MockRoute{"PUT", "/v2/spaces/a72fa1e8-c694-47b3-85f2-55f61fd00d73", []string{spacePayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + updateSpaceRequest := SpaceRequest{ + Name: "test-space", + AllowSSH: false, + } + + space, err := client.UpdateSpace("a72fa1e8-c694-47b3-85f2-55f61fd00d73", updateSpaceRequest) + So(err, ShouldBeNil) + + So(space.Guid, ShouldEqual, "a72fa1e8-c694-47b3-85f2-55f61fd00d73") + So(space.Name, ShouldEqual, "test-space") + So(space.OrganizationGuid, ShouldEqual, "da0dba14-6064-4f7a-b15a-ff9e677e49b2") + So(space.AllowSSH, ShouldEqual, false) + }) +} + +func TestDeleteSpace(t *testing.T) { + Convey("Delete space", t, func() { + setup(MockRoute{"DELETE", "/v2/spaces/a537761f-9d93-4b30-af17-3d73dbca181b", []string{""}, "", 204, "recursive=false&async=false", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteSpace("a537761f-9d93-4b30-af17-3d73dbca181b", false, false) + So(err, ShouldBeNil) + }) +} +func TestSpaceOrg(t *testing.T) { + Convey("Find space org", t, func() { + setup(MockRoute{"GET", "/v2/org/foobar", []string{orgPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "123", + Name: "test space", + OrgURL: "/v2/org/foobar", + c: client, + } + org, err := space.Org() + So(err, ShouldBeNil) + + So(org.Name, ShouldEqual, "test-org") + So(org.Guid, ShouldEqual, "da0dba14-6064-4f7a-b15a-ff9e677e49b2") + }) +} + +func TestSpaceQuota(t *testing.T) { + Convey("Get space quota", t, func() { + setup(MockRoute{"GET", "/v2/space_quota_definitions/9ffd7c5c-d83c-4786-b399-b7bd54883977", []string{spaceQuotaPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + QuotaDefinitionGuid: "9ffd7c5c-d83c-4786-b399-b7bd54883977", + c: client, + } + + spaceQuota, err := space.Quota() + So(err, ShouldBeNil) + + So(spaceQuota.Guid, ShouldEqual, "9ffd7c5c-d83c-4786-b399-b7bd54883977") + So(spaceQuota.Name, ShouldEqual, "test-2") + So(spaceQuota.NonBasicServicesAllowed, ShouldEqual, false) + So(spaceQuota.TotalServices, ShouldEqual, 10) + So(spaceQuota.TotalRoutes, ShouldEqual, 20) + So(spaceQuota.MemoryLimit, ShouldEqual, 30) + So(spaceQuota.InstanceMemoryLimit, ShouldEqual, 40) + So(spaceQuota.AppInstanceLimit, ShouldEqual, 50) + So(spaceQuota.AppTaskLimit, ShouldEqual, 60) + So(spaceQuota.TotalServiceKeys, ShouldEqual, 70) + So(spaceQuota.TotalReservedRoutePorts, ShouldEqual, 80) + }) +} + +func TestSpaceSummary(t *testing.T) { + Convey("Get space summary", t, func() { + setup(MockRoute{"GET", "/v2/spaces/494d8b64-8181-4183-a6d3-6279db8fec6e/summary", []string{spaceSummaryPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "494d8b64-8181-4183-a6d3-6279db8fec6e", + c: client, + } + + summary, err := space.Summary() + So(err, ShouldBeNil) + + So(summary.Guid, ShouldEqual, "494d8b64-8181-4183-a6d3-6279db8fec6e") + So(summary.Name, ShouldEqual, "test") + + So(len(summary.Apps), ShouldEqual, 1) + So(summary.Apps[0].Guid, ShouldEqual, "b5f0d1bd-a3a9-40a4-af1a-312ad26e5379") + So(summary.Apps[0].Name, ShouldEqual, "test-app") + So(summary.Apps[0].ServiceCount, ShouldEqual, 1) + So(summary.Apps[0].RunningInstances, ShouldEqual, 1) + So(summary.Apps[0].SpaceGuid, ShouldEqual, "494d8b64-8181-4183-a6d3-6279db8fec6e") + So(summary.Apps[0].StackGuid, ShouldEqual, "67e019a3-322a-407a-96e0-178e95bd0e55") + So(summary.Apps[0].Buildpack, ShouldEqual, "ruby_buildpack") + So(summary.Apps[0].DetectedBuildpack, ShouldEqual, "") + So(summary.Apps[0].Memory, ShouldEqual, 256) + So(summary.Apps[0].Instances, ShouldEqual, 1) + So(summary.Apps[0].DiskQuota, ShouldEqual, 512) + So(summary.Apps[0].State, ShouldEqual, "STARTED") + So(summary.Apps[0].Command, ShouldEqual, "") + So(summary.Apps[0].PackageState, ShouldEqual, "STAGED") + So(summary.Apps[0].HealthCheckType, ShouldEqual, "port") + So(summary.Apps[0].HealthCheckTimeout, ShouldEqual, 0) + So(summary.Apps[0].StagingFailedReason, ShouldEqual, "") + So(summary.Apps[0].StagingFailedDescription, ShouldEqual, "") + So(summary.Apps[0].Diego, ShouldEqual, true) + So(summary.Apps[0].DockerImage, ShouldEqual, "") + So(summary.Apps[0].DetectedStartCommand, ShouldEqual, "rackup -p $PORT") + So(summary.Apps[0].EnableSSH, ShouldEqual, true) + So(summary.Apps[0].DockerCredentials["redacted_message"], ShouldEqual, "[PRIVATE DATA HIDDEN]") + + So(len(summary.Services), ShouldEqual, 1) + So(summary.Services[0].Guid, ShouldEqual, "3c5c758c-6b76-46f6-89d5-677909bfc975") + So(summary.Services[0].Name, ShouldEqual, "test-service") + So(summary.Services[0].BoundAppCount, ShouldEqual, 1) + So(summary.Services[0].ServiceBrokerName, ShouldEqual, "broker-name") + So(summary.Services[0].ServicePlan.Service.Label, ShouldEqual, "test-service") + }) +} + +func TestSpaceRoles(t *testing.T) { + Convey("Get space roles", t, func() { + setup(MockRoute{"GET", "/v2/spaces/494d8b64-8181-4183-a6d3-6279db8fec6e/user_roles", []string{spaceRolesPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "494d8b64-8181-4183-a6d3-6279db8fec6e", + c: client, + } + + roles, err := space.Roles() + So(err, ShouldBeNil) + + So(len(roles), ShouldEqual, 1) + So(roles[0].Guid, ShouldEqual, "uaa-id-363") + So(roles[0].Admin, ShouldEqual, false) + So(roles[0].Active, ShouldEqual, false) + So(roles[0].DefaultSpaceGuid, ShouldEqual, "") + So(roles[0].Username, ShouldEqual, "everything@example.com") + So(roles[0].SpaceRoles, ShouldResemble, []string{"space_developer", "space_manager", "space_auditor"}) + So(roles[0].SpacesUrl, ShouldEqual, "/v2/users/uaa-id-363/spaces") + So(roles[0].OrganizationsUrl, ShouldEqual, "/v2/users/uaa-id-363/organizations") + So(roles[0].ManagedOrganizationsUrl, ShouldEqual, "/v2/users/uaa-id-363/managed_organizations") + So(roles[0].BillingManagedOrganizationsUrl, ShouldEqual, "/v2/users/uaa-id-363/billing_managed_organizations") + So(roles[0].AuditedOrganizationsUrl, ShouldEqual, "/v2/users/uaa-id-363/audited_organizations") + So(roles[0].ManagedSpacesUrl, ShouldEqual, "/v2/users/uaa-id-363/managed_spaces") + So(roles[0].AuditedSpacesUrl, ShouldEqual, "/v2/users/uaa-id-363/audited_spaces") + }) +} + +func TestAssociateSpaceAuditorByUsername(t *testing.T) { + Convey("Associate auditor by username", t, func() { + requestBody := `{"username":"user-name"}` + setup(MockRoute{"PUT", "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56/auditors", []string{associateSpaceUserPayload}, "", 201, "", &requestBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newSpace, err := space.AssociateAuditorByUsername("user-name") + So(err, ShouldBeNil) + So(newSpace.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateSpaceAuditorByUsernameAndOrigin(t *testing.T) { + Convey("Associate auditor by username and origin", t, func() { + requestBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"PUT", "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56/auditors", []string{associateSpaceUserPayload}, "", 201, "", &requestBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newSpace, err := space.AssociateAuditorByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + So(newSpace.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateSpaceDeveloperByUsername(t *testing.T) { + Convey("Associate developer by username", t, func() { + requestBody := `{"username":"user-name"}` + setup(MockRoute{"PUT", "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56/developers", []string{associateSpaceUserPayload}, "", 201, "", &requestBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newSpace, err := space.AssociateDeveloperByUsername("user-name") + So(err, ShouldBeNil) + So(newSpace.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateSpaceDeveloperByUsernameAndOrigin(t *testing.T) { + Convey("Associate developer by username and origin", t, func() { + requestBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"PUT", "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56/developers", []string{associateSpaceUserPayload}, "", 201, "", &requestBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newSpace, err := space.AssociateDeveloperByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + So(newSpace.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateSpaceManagerByUsername(t *testing.T) { + Convey("Associate manager by username", t, func() { + requestBody := `{"username":"user-name"}` + setup(MockRoute{"PUT", "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56/managers", []string{associateSpaceUserPayload}, "", 201, "", &requestBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newSpace, err := space.AssociateManagerByUsername("user-name") + So(err, ShouldBeNil) + So(newSpace.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestAssociateSpaceManagerByUsernameAndOrigin(t *testing.T) { + Convey("Associate manager by username and origin", t, func() { + requestBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"PUT", "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56/managers", []string{associateSpaceUserPayload}, "", 201, "", &requestBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + newSpace, err := space.AssociateManagerByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + So(newSpace.Guid, ShouldEqual, "bc7b4caf-f4b8-4d85-b126-0729b9351e56") + }) +} + +func TestRemoveSpaceDeveloperByUsername(t *testing.T) { + Convey("Remove developer by username", t, func() { + requestBody := `{"username":"user-name"}` + setup(MockRoute{"DELETE", "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56/developers", []string{""}, "", 200, "", &requestBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = space.RemoveDeveloperByUsername("user-name") + So(err, ShouldBeNil) + }) +} +func TestRemoveSpaceDeveloperByUsernameAndOrigin(t *testing.T) { + Convey("Remove developer by username and origin", t, func() { + requestBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"POST", "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56/developers/remove", []string{""}, "", 200, "", &requestBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = space.RemoveDeveloperByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + }) +} +func TestRemoveSpaceAuditorByUsername(t *testing.T) { + Convey("Remove auditor by username", t, func() { + requestBody := `{"username":"user-name"}` + setup(MockRoute{"DELETE", "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56/auditors", []string{""}, "", 200, "", &requestBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = space.RemoveAuditorByUsername("user-name") + So(err, ShouldBeNil) + }) +} + +func TestRemoveSpaceAuditorByUsernameAndOrigin(t *testing.T) { + Convey("Remove auditor by username and origin", t, func() { + requestBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"POST", "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56/auditors/remove", []string{""}, "", 200, "", &requestBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = space.RemoveAuditorByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + }) +} + +func TestRemoveSpaceManagerByUsername(t *testing.T) { + Convey("Remove manager by username", t, func() { + requestBody := `{"username":"user-name"}` + setup(MockRoute{"DELETE", "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56/managers", []string{""}, "", 200, "", &requestBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = space.RemoveManagerByUsername("user-name") + So(err, ShouldBeNil) + }) +} + +func TestRemoveSpaceManagerByUsernameAndOrigin(t *testing.T) { + Convey("Remove manager by username and origin", t, func() { + requestBody := `{"origin":"ldap","username":"user-name"}` + setup(MockRoute{"POST", "/v2/spaces/bc7b4caf-f4b8-4d85-b126-0729b9351e56/managers/remove", []string{""}, "", 200, "", &requestBody}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: "bc7b4caf-f4b8-4d85-b126-0729b9351e56", + c: client, + } + + err = space.RemoveManagerByUsernameAndOrigin("user-name", "ldap") + So(err, ShouldBeNil) + }) +} + +func TestGetSpaceByGuid(t *testing.T) { + Convey("List Space", t, func() { + setup(MockRoute{"GET", "/v2/spaces/8efd7c5c-d83c-4786-b399-b7bd548839e1", []string{spaceByGuidPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space, err := client.GetSpaceByGuid("8efd7c5c-d83c-4786-b399-b7bd548839e1") + So(err, ShouldBeNil) + + So(space.Guid, ShouldEqual, "8efd7c5c-d83c-4786-b399-b7bd548839e1") + So(space.Name, ShouldEqual, "dev") + }) +} + +func TestGetSpaceServiceOfferings(t *testing.T) { + guid := `8efd7c5c-d83c-4786-b399-b7bd548839e1` + Convey("Get service offerings for space", t, func() { + setup(MockRoute{ + Method: "GET", + Endpoint: fmt.Sprintf("/v2/spaces/%s/services", guid), + Output: []string{spaceServiceOfferingsPayload}, + UserAgent: "", + Status: 200, + QueryString: "", + PostForm: nil, + }, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + space := &Space{ + Guid: guid, + c: client, + } + + offerings, err := space.GetServiceOfferings() + So(offerings, ShouldNotBeEmpty) + So(err, ShouldBeNil) + }) +} + +func TestIsolationSegmentForSpace(t *testing.T) { + Convey("set Default IsolationSegment", t, func() { + defaultIsolationSegmentPayload := `{"data":{"guid":"3b6f763f-aae1-4177-9b93-f2de6f2a48f2"}}` + mocks := []MockRoute{ + {"PATCH", "/v3/spaces/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/relationships/isolation_segment", []string{""}, "", 200, "", &defaultIsolationSegmentPayload}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.IsolationSegmentForSpace("3b6f763f-aae1-4177-9b93-f2de6f2a48f2", "3b6f763f-aae1-4177-9b93-f2de6f2a48f2") + So(err, ShouldBeNil) + + }) +} + +func TestResetIsolationSegmentForSpace(t *testing.T) { + Convey("Reset IsolationSegment", t, func() { + resetIsolationSegmentPayload := `{"data":null}` + mocks := []MockRoute{ + {"PATCH", "/v3/spaces/3b6f763f-aae1-4177-9b93-f2de6f2a48f2/relationships/isolation_segment", []string{""}, "", 200, "", &resetIsolationSegmentPayload}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.ResetIsolationSegmentForSpace("3b6f763f-aae1-4177-9b93-f2de6f2a48f2") + So(err, ShouldBeNil) + + }) +} diff --git a/third_party/go-cfclient/stacks.go b/third_party/go-cfclient/stacks.go new file mode 100644 index 000000000000..fe3fc9c2a270 --- /dev/null +++ b/third_party/go-cfclient/stacks.go @@ -0,0 +1,103 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/url" + + "github.com/pkg/errors" +) + +type StacksResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []StacksResource `json:"resources"` +} + +type StacksResource struct { + Meta Meta `json:"metadata"` + Entity Stack `json:"entity"` +} + +type Stack struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Description string `json:"description"` + c *Client +} + +func (c *Client) ListStacksByQuery(query url.Values) ([]Stack, error) { + var stacks []Stack + requestURL := "/v2/stacks?" + query.Encode() + for { + stacksResp, err := c.getStacksResponse(requestURL) + if err != nil { + return []Stack{}, err + } + for _, stack := range stacksResp.Resources { + stack.Entity.Guid = stack.Meta.Guid + stack.Entity.CreatedAt = stack.Meta.CreatedAt + stack.Entity.UpdatedAt = stack.Meta.UpdatedAt + stack.Entity.c = c + stacks = append(stacks, stack.Entity) + } + requestURL = stacksResp.NextUrl + if requestURL == "" { + break + } + } + return stacks, nil +} + +func (c *Client) ListStacks() ([]Stack, error) { + return c.ListStacksByQuery(nil) +} + +func (c *Client) GetStackByGuid(stackGUID string) (Stack, error) { + var stacksRes StacksResource + requestURL := fmt.Sprintf("/v2/stacks/%s", stackGUID) + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return Stack{}, errors.Wrap(err, "Error requesting stack info") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return Stack{}, errors.Wrap(err, "Error reading stack body") + } + err = json.Unmarshal(resBody, &stacksRes) + if err != nil { + return Stack{}, errors.Wrap(err, "Error unmarshalling stack") + } + + stacksRes.Entity.Guid = stacksRes.Meta.Guid + stacksRes.Entity.CreatedAt = stacksRes.Meta.CreatedAt + stacksRes.Entity.UpdatedAt = stacksRes.Meta.UpdatedAt + stacksRes.Entity.c = c + + return stacksRes.Entity, nil +} + +func (c *Client) getStacksResponse(requestURL string) (StacksResponse, error) { + var stacksResp StacksResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return StacksResponse{}, errors.Wrap(err, "Error requesting stacks") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return StacksResponse{}, errors.Wrap(err, "Error reading stacks body") + } + err = json.Unmarshal(resBody, &stacksResp) + if err != nil { + return StacksResponse{}, errors.Wrap(err, "Error unmarshalling stacks") + } + return stacksResp, nil +} diff --git a/third_party/go-cfclient/stacks_test.go b/third_party/go-cfclient/stacks_test.go new file mode 100644 index 000000000000..c0b9d1e8663e --- /dev/null +++ b/third_party/go-cfclient/stacks_test.go @@ -0,0 +1,52 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListStacks(t *testing.T) { + Convey("List Stacks", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/stacks", []string{listStacksPayloadPage1}, "", 200, "", nil}, + {"GET", "/v2/stacks_page_2", []string{listStacksPayloadPage2}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + stacks, err := client.ListStacks() + So(err, ShouldBeNil) + + So(len(stacks), ShouldEqual, 2) + So(stacks[0].Guid, ShouldEqual, "67e019a3-322a-407a-96e0-178e95bd0e55") + So(stacks[0].Name, ShouldEqual, "cflinuxfs2") + So(stacks[0].Description, ShouldEqual, "Cloud Foundry Linux-based filesystem") + }) +} + +func TestGetStackByGuid(t *testing.T) { + Convey("Get Stack By Guid", t, func() { + setup(MockRoute{"GET", "/v2/stacks/a9be2e10-0164-401d-94e0-88455d614844", []string{stackByGuidPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + stack, err := client.GetStackByGuid("a9be2e10-0164-401d-94e0-88455d614844") + So(err, ShouldBeNil) + + So(stack.Guid, ShouldEqual, "a9be2e10-0164-401d-94e0-88455d614844") + So(stack.Name, ShouldEqual, "windows2012R2") + So(stack.Description, ShouldEqual, "Experimental Windows runtime") + }) +} diff --git a/third_party/go-cfclient/stats.go b/third_party/go-cfclient/stats.go new file mode 100644 index 000000000000..2bd86e7c705e --- /dev/null +++ b/third_party/go-cfclient/stats.go @@ -0,0 +1,59 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "github.com/pkg/errors" + "net/http" +) + +// StatsGetResponse is the json body returned from the API +type StatsGetResponse struct { + Stats []Stats `json:"resources"` +} + +// Stats represents the stats of a process +type Stats struct { + Type string `json:"type"` + Index int `json:"index"` + State string `json:"state"` + Usage struct { + Time string `json:"time"` + CPU float64 `json:"cpu"` + Mem int `json:"mem"` + Disk int `json:"disk"` + } `json:"usage"` + Host string `json:"host"` + InstancePorts []struct { + External int `json:"external"` + Internal int `json:"internal"` + ExternalTLSProxyPort int `json:"external_tls_proxy_port"` + InternalTLSProxyPort int `json:"internal_tls_proxy_port"` + } `json:"instance_ports"` + Uptime int `json:"uptime"` + MemQuota int `json:"mem_quota"` + DiskQuota int `json:"disk_quota"` + FdsQuota int `json:"fds_quota"` + IsolationSegment string `json:"isolation_segment"` + Details string `json:"details"` +} + +func (c *Client) GetProcessStats(processGUID string) ([]Stats, error) { + req := c.NewRequest("GET", "/v3/processes/"+processGUID+"/stats") + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "error getting stats for v3 process") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error getting stats with GUID [%s], response code: %d", processGUID, resp.StatusCode) + } + + statsResp := new(StatsGetResponse) + err = json.NewDecoder(resp.Body).Decode(statsResp) + if err != nil { + return nil, fmt.Errorf("error decoding stats with GUID [%s], response code: %d", processGUID, resp.StatusCode) + } + return statsResp.Stats, nil +} diff --git a/third_party/go-cfclient/stats_test.go b/third_party/go-cfclient/stats_test.go new file mode 100644 index 000000000000..065a898a0afb --- /dev/null +++ b/third_party/go-cfclient/stats_test.go @@ -0,0 +1,27 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestGetProcessStats(t *testing.T) { + Convey("Get Process Stats", t, func() { + setup(MockRoute{"GET", "/v3/processes/9902530c-c634-4864-a189-71d763cb12e2/stats", []string{getProcessStatsPayload1}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + stats, err := client.GetProcessStats("9902530c-c634-4864-a189-71d763cb12e2") + So(err, ShouldBeNil) + + So(stats[0].State, ShouldEqual, "RUNNING") + So(stats[0].Type, ShouldEqual, "web") + + }) +} diff --git a/third_party/go-cfclient/tasks.go b/third_party/go-cfclient/tasks.go new file mode 100644 index 000000000000..538b5e04a4a1 --- /dev/null +++ b/third_party/go-cfclient/tasks.go @@ -0,0 +1,211 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "time" + + "github.com/pkg/errors" +) + +// TaskListResponse is the JSON response from the API. +type TaskListResponse struct { + Pagination Pagination `json:"pagination"` + Tasks []Task `json:"resources"` +} + +// Task is a description of a task element. +type Task struct { + GUID string `json:"guid"` + SequenceID int `json:"sequence_id"` + Name string `json:"name"` + Command string `json:"command"` + State string `json:"state"` + MemoryInMb int `json:"memory_in_mb"` + DiskInMb int `json:"disk_in_mb"` + Result struct { + FailureReason string `json:"failure_reason"` + } `json:"result"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DropletGUID string `json:"droplet_guid"` + Relationships struct { + App V3ToOneRelationship `json:"app"` + } `json:"relationships"` + Links struct { + Self Link `json:"self"` + App Link `json:"app"` + Droplet Link `json:"droplet"` + } `json:"links"` +} + +// TaskRequest is a v3 JSON object as described in: +// http://v3-apidocs.cloudfoundry.org/version/3.0.0/index.html#create-a-task +type TaskRequest struct { + Command string `json:"command"` + Name string `json:"name"` + MemoryInMegabyte int `json:"memory_in_mb"` + DiskInMegabyte int `json:"disk_in_mb"` + DropletGUID string `json:"droplet_guid"` +} + +// ListTasks returns all tasks the user has access to. +// See http://v3-apidocs.cloudfoundry.org/version/3.12.0/index.html#list-tasks +func (c *Client) ListTasks() ([]Task, error) { + return c.ListTasksByQuery(nil) +} + +// ListTasksByQuery returns all tasks the user has access to, with query parameters. +// See http://v3-apidocs.cloudfoundry.org/version/3.12.0/index.html#list-tasks +func (c *Client) ListTasksByQuery(query url.Values) ([]Task, error) { + return c.taskListHelper("/v3/tasks", query) +} + +// TasksByApp returns task structures which aligned to an app identified by the given guid. +// See: http://v3-apidocs.cloudfoundry.org/version/3.12.0/index.html#list-tasks-for-an-app +func (c *Client) TasksByApp(guid string) ([]Task, error) { + return c.TasksByAppByQuery(guid, url.Values{}) +} + +// TasksByAppByQuery returns task structures which aligned to an app identified by the given guid +// and filtered by the given query parameters. +// See: http://v3-apidocs.cloudfoundry.org/version/3.12.0/index.html#list-tasks-for-an-app +func (c *Client) TasksByAppByQuery(guid string, query url.Values) ([]Task, error) { + uri := fmt.Sprintf("/v3/apps/%s/tasks", guid) + return c.taskListHelper(uri, query) +} + +func (c *Client) taskListHelper(requestURL string, query url.Values) ([]Task, error) { + var tasks []Task + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 tasks") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 tasks, response code: %d", resp.StatusCode) + } + + var data TaskListResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 tasks") + } + + tasks = append(tasks, data.Tasks...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 tasks") + } + } + + return tasks, nil +} + +func createReader(tr TaskRequest) (io.Reader, error) { + rmap := make(map[string]string) + rmap["command"] = tr.Command + if tr.Name != "" { + rmap["name"] = tr.Name + } + // setting droplet GUID causing issues + if tr.MemoryInMegabyte != 0 { + rmap["memory_in_mb"] = fmt.Sprintf("%d", tr.MemoryInMegabyte) + } + if tr.DiskInMegabyte != 0 { + rmap["disk_in_mb"] = fmt.Sprintf("%d", tr.DiskInMegabyte) + } + + bodyReader := bytes.NewBuffer(nil) + enc := json.NewEncoder(bodyReader) + if err := enc.Encode(rmap); err != nil { + return nil, errors.Wrap(err, "Error during encoding task request") + } + return bodyReader, nil +} + +// CreateTask creates a new task in CF system and returns its structure. +func (c *Client) CreateTask(tr TaskRequest) (task Task, err error) { + bodyReader, err := createReader(tr) + if err != nil { + return task, err + } + + request := fmt.Sprintf("/v3/apps/%s/tasks", tr.DropletGUID) + req := c.NewRequestWithBody("POST", request, bodyReader) + + resp, err := c.DoRequest(req) + if err != nil { + return task, errors.Wrap(err, "Error creating task") + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return task, errors.Wrap(err, "Error reading task after creation") + } + + err = json.Unmarshal(body, &task) + if err != nil { + return task, errors.Wrap(err, "Error unmarshaling task") + } + return task, err +} + +// GetTaskByGuid returns a task structure by requesting it with the tasks GUID. +func (c *Client) GetTaskByGuid(guid string) (task Task, err error) { + request := fmt.Sprintf("/v3/tasks/%s", guid) + req := c.NewRequest("GET", request) + + resp, err := c.DoRequest(req) + if err != nil { + return task, errors.Wrap(err, "Error requesting task") + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return task, errors.Wrap(err, "Error reading task") + } + + err = json.Unmarshal(body, &task) + if err != nil { + return task, errors.Wrap(err, "Error unmarshaling task") + } + return task, err +} + +func (c *Client) TaskByGuid(guid string) (task Task, err error) { + return c.GetTaskByGuid(guid) +} + +// TerminateTask cancels a task identified by its GUID. +func (c *Client) TerminateTask(guid string) error { + req := c.NewRequest("PUT", fmt.Sprintf("/v3/tasks/%s/cancel", guid)) + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error terminating task") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusAccepted { + return errors.Wrapf(err, "Failed terminating task, response status code %d", resp.StatusCode) + } + return nil +} diff --git a/third_party/go-cfclient/tasks_test.go b/third_party/go-cfclient/tasks_test.go new file mode 100644 index 000000000000..7abb2070a5a9 --- /dev/null +++ b/third_party/go-cfclient/tasks_test.go @@ -0,0 +1,292 @@ +package cfclient + +import ( + "testing" + "time" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListTasks(t *testing.T) { + Convey("List Tasks", t, func() { + setup(MockRoute{"GET", "/v3/tasks", []string{listTasksPayloadPage1, listTasksPayloadPage2}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + task, err := client.ListTasks() + So(err, ShouldBeNil) + + So(len(task), ShouldEqual, 4) + + So(task[0].GUID, ShouldEqual, "d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa") + So(task[0].State, ShouldEqual, "SUCCEEDED") + So(task[0].SequenceID, ShouldEqual, 1) + So(task[0].MemoryInMb, ShouldEqual, 512) + So(task[0].DiskInMb, ShouldEqual, 1024) + So(task[0].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 41, 0, time.FixedZone("UTC", 0)).String()) + + So(task[1].GUID, ShouldEqual, "63b4cd89-fd8b-4bf1-a311-7174fcc907d6") + So(task[1].State, ShouldEqual, "FAILED") + So(task[1].SequenceID, ShouldEqual, 2) + So(task[1].MemoryInMb, ShouldEqual, 1024) + So(task[1].DiskInMb, ShouldEqual, 1024) + So(task[1].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 43, 0, time.FixedZone("UTC", 0)).String()) + + So(task[2].GUID, ShouldEqual, "abcdefc-99a3-4e6a-af91-a44b4ab7b6fa") + So(task[2].State, ShouldEqual, "SUCCEEDED") + So(task[2].SequenceID, ShouldEqual, 3) + So(task[2].MemoryInMb, ShouldEqual, 512) + So(task[2].DiskInMb, ShouldEqual, 1024) + So(task[2].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 44, 0, time.FixedZone("UTC", 0)).String()) + + So(task[3].GUID, ShouldEqual, "hijklm9-fd8b-4bf1-a311-7174fcc907d6") + So(task[3].State, ShouldEqual, "SUCCEEDED") + So(task[3].SequenceID, ShouldEqual, 4) + So(task[3].MemoryInMb, ShouldEqual, 1024) + So(task[3].DiskInMb, ShouldEqual, 1024) + So(task[3].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 46, 0, time.FixedZone("UTC", 0)).String()) + }) +} +func TestListTasksByQuery(t *testing.T) { + Convey("List Tasks", t, func() { + setup(MockRoute{"GET", "/v3/tasks", []string{listTasksPayloadPage1, listTasksPayloadPage2}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + task, err := client.ListTasksByQuery(nil) + So(err, ShouldBeNil) + + So(len(task), ShouldEqual, 4) + + So(task[0].GUID, ShouldEqual, "d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa") + So(task[0].State, ShouldEqual, "SUCCEEDED") + So(task[0].SequenceID, ShouldEqual, 1) + So(task[0].MemoryInMb, ShouldEqual, 512) + So(task[0].DiskInMb, ShouldEqual, 1024) + So(task[0].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 41, 0, time.FixedZone("UTC", 0)).String()) + + So(task[1].GUID, ShouldEqual, "63b4cd89-fd8b-4bf1-a311-7174fcc907d6") + So(task[1].State, ShouldEqual, "FAILED") + So(task[1].SequenceID, ShouldEqual, 2) + So(task[1].MemoryInMb, ShouldEqual, 1024) + So(task[1].DiskInMb, ShouldEqual, 1024) + So(task[1].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 43, 0, time.FixedZone("UTC", 0)).String()) + + So(task[3].GUID, ShouldEqual, "hijklm9-fd8b-4bf1-a311-7174fcc907d6") + So(task[3].State, ShouldEqual, "SUCCEEDED") + So(task[3].SequenceID, ShouldEqual, 4) + So(task[3].MemoryInMb, ShouldEqual, 1024) + So(task[3].DiskInMb, ShouldEqual, 1024) + So(task[3].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 46, 0, time.FixedZone("UTC", 0)).String()) + }) +} + +func TestCreateTask(t *testing.T) { + Convey("Create Task", t, func() { + mocks := []MockRoute{ + {"POST", "/v3/apps/740ebd2b-162b-469a-bd72-3edb96fabd9a/tasks", []string{createTaskPayload}, "", 201, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + tr := TaskRequest{ + Command: "rake db:migrate", + Name: "migrate", + MemoryInMegabyte: 512, + DiskInMegabyte: 1024, + DropletGUID: "740ebd2b-162b-469a-bd72-3edb96fabd9a", + } + task, err := client.CreateTask(tr) + So(err, ShouldBeNil) + + So(task.Command, ShouldEqual, "rake db:migrate") + So(task.Name, ShouldEqual, "migrate") + So(task.DiskInMb, ShouldEqual, 1024) + So(task.MemoryInMb, ShouldEqual, 512) + So(task.DropletGUID, ShouldEqual, "740ebd2b-162b-469a-bd72-3edb96fabd9a") + }) +} + +func TestCreateTaskFails(t *testing.T) { + Convey("Create Task fails", t, func() { + mocks := []MockRoute{ + {"POST", "/v3/apps/740ebd2b-162b-469a-bd72-3edb96fabd9a/tasks", []string{errorV3Payload}, "", 400, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + tr := TaskRequest{ + Command: "rake db:migrate", + Name: "migrate", + MemoryInMegabyte: 512, + DiskInMegabyte: 1024, + DropletGUID: "740ebd2b-162b-469a-bd72-3edb96fabd9a", + } + + task, err := client.CreateTask(tr) + So(err.Error(), ShouldEqual, "Error creating task: cfclient error (CF-UnprocessableEntity|10008): something went wrong") + So(task.Name, ShouldBeEmpty) + }) +} + +func TestTerminateTask(t *testing.T) { + Convey("Terminate Task", t, func() { + mocks := []MockRoute{ + {"PUT", "/v3/tasks/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/cancel", []string{""}, "", 202, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + errTerm := client.TerminateTask("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") + So(errTerm, ShouldBeNil) + }) +} + +func TestGetTask(t *testing.T) { + Convey("Create Task", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/tasks/740ebd2b-162b-469a-bd72-3edb96fabd9a", []string{createTaskPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + task, err := client.GetTaskByGuid("740ebd2b-162b-469a-bd72-3edb96fabd9a") + So(err, ShouldBeNil) + + So(task.Command, ShouldEqual, "rake db:migrate") + So(task.Name, ShouldEqual, "migrate") + So(task.DiskInMb, ShouldEqual, 1024) + So(task.MemoryInMb, ShouldEqual, 512) + So(task.DropletGUID, ShouldEqual, "740ebd2b-162b-469a-bd72-3edb96fabd9a") + }) +} + +func TestTasksByApp(t *testing.T) { + Convey("List Tasks by App", t, func() { + setup(MockRoute{"GET", "/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5/tasks", []string{listTasksByAppPayloadPage1, listTasksByAppPayloadPage2}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + task, err := client.TasksByApp("ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5") + So(err, ShouldBeNil) + + So(len(task), ShouldEqual, 4) + + So(task[0].GUID, ShouldEqual, "d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa") + So(task[0].State, ShouldEqual, "SUCCEEDED") + So(task[0].SequenceID, ShouldEqual, 1) + So(task[0].MemoryInMb, ShouldEqual, 512) + So(task[0].DiskInMb, ShouldEqual, 1024) + So(task[0].Relationships.App.Data.GUID, ShouldEqual, "ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5") + So(task[0].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 41, 0, time.FixedZone("UTC", 0)).String()) + + So(task[1].GUID, ShouldEqual, "63b4cd89-fd8b-4bf1-a311-7174fcc907d6") + So(task[1].State, ShouldEqual, "FAILED") + So(task[1].SequenceID, ShouldEqual, 2) + So(task[1].MemoryInMb, ShouldEqual, 1024) + So(task[1].DiskInMb, ShouldEqual, 1024) + So(task[1].Relationships.App.Data.GUID, ShouldEqual, "ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5") + So(task[1].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 43, 0, time.FixedZone("UTC", 0)).String()) + + So(task[2].GUID, ShouldEqual, "abcdefc-99a3-4e6a-af91-a44b4ab7b6fa") + So(task[2].State, ShouldEqual, "SUCCEEDED") + So(task[2].SequenceID, ShouldEqual, 3) + So(task[2].MemoryInMb, ShouldEqual, 512) + So(task[2].DiskInMb, ShouldEqual, 1024) + So(task[2].Relationships.App.Data.GUID, ShouldEqual, "ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5") + So(task[2].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 44, 0, time.FixedZone("UTC", 0)).String()) + + So(task[3].GUID, ShouldEqual, "hijklm9-fd8b-4bf1-a311-7174fcc907d6") + So(task[3].State, ShouldEqual, "SUCCEEDED") + So(task[3].SequenceID, ShouldEqual, 4) + So(task[3].MemoryInMb, ShouldEqual, 1024) + So(task[3].DiskInMb, ShouldEqual, 1024) + So(task[3].Relationships.App.Data.GUID, ShouldEqual, "ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5") + So(task[3].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 46, 0, time.FixedZone("UTC", 0)).String()) + }) +} + +func TestTasksByAppByQuery(t *testing.T) { + Convey("List Tasks by App", t, func() { + setup(MockRoute{"GET", "/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5/tasks", []string{listTasksByAppPayloadPage1, listTasksByAppPayloadPage2}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + task, err := client.TasksByAppByQuery("ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5", nil) + So(err, ShouldBeNil) + + So(len(task), ShouldEqual, 4) + + So(task[0].GUID, ShouldEqual, "d5cc22ec-99a3-4e6a-af91-a44b4ab7b6fa") + So(task[0].State, ShouldEqual, "SUCCEEDED") + So(task[0].SequenceID, ShouldEqual, 1) + So(task[0].MemoryInMb, ShouldEqual, 512) + So(task[0].DiskInMb, ShouldEqual, 1024) + So(task[0].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 41, 0, time.FixedZone("UTC", 0)).String()) + + So(task[1].GUID, ShouldEqual, "63b4cd89-fd8b-4bf1-a311-7174fcc907d6") + So(task[1].State, ShouldEqual, "FAILED") + So(task[1].SequenceID, ShouldEqual, 2) + So(task[1].MemoryInMb, ShouldEqual, 1024) + So(task[1].DiskInMb, ShouldEqual, 1024) + So(task[1].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 43, 0, time.FixedZone("UTC", 0)).String()) + + So(task[2].GUID, ShouldEqual, "abcdefc-99a3-4e6a-af91-a44b4ab7b6fa") + So(task[2].State, ShouldEqual, "SUCCEEDED") + So(task[2].SequenceID, ShouldEqual, 3) + So(task[2].MemoryInMb, ShouldEqual, 512) + So(task[2].DiskInMb, ShouldEqual, 1024) + So(task[2].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 44, 0, time.FixedZone("UTC", 0)).String()) + + So(task[3].GUID, ShouldEqual, "hijklm9-fd8b-4bf1-a311-7174fcc907d6") + So(task[3].State, ShouldEqual, "SUCCEEDED") + So(task[3].SequenceID, ShouldEqual, 4) + So(task[3].MemoryInMb, ShouldEqual, 1024) + So(task[3].DiskInMb, ShouldEqual, 1024) + So(task[3].CreatedAt.String(), ShouldEqual, time.Date(2016, 05, 04, 17, 00, 46, 0, time.FixedZone("UTC", 0)).String()) + }) +} diff --git a/third_party/go-cfclient/tools.go b/third_party/go-cfclient/tools.go new file mode 100644 index 000000000000..dd6139498611 --- /dev/null +++ b/third_party/go-cfclient/tools.go @@ -0,0 +1,5 @@ +package cfclient + +import ( + _ "go.yaml.in/yaml/v2" +) diff --git a/third_party/go-cfclient/types.go b/third_party/go-cfclient/types.go new file mode 100644 index 000000000000..279106bfa40b --- /dev/null +++ b/third_party/go-cfclient/types.go @@ -0,0 +1,8 @@ +package cfclient + +type Meta struct { + Guid string `json:"guid"` + Url string `json:"url"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} diff --git a/third_party/go-cfclient/user_provided_service_instances.go b/third_party/go-cfclient/user_provided_service_instances.go new file mode 100644 index 000000000000..7b398a5f143e --- /dev/null +++ b/third_party/go-cfclient/user_provided_service_instances.go @@ -0,0 +1,189 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type UserProvidedServiceInstancesResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []UserProvidedServiceInstanceResource `json:"resources"` +} + +type UserProvidedServiceInstanceResource struct { + Meta Meta `json:"metadata"` + Entity UserProvidedServiceInstance `json:"entity"` +} + +type UserProvidedServiceInstance struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Credentials map[string]interface{} `json:"credentials"` + SpaceGuid string `json:"space_guid"` + Type string `json:"type"` + Tags []string `json:"tags"` + SpaceUrl string `json:"space_url"` + ServiceBindingsUrl string `json:"service_bindings_url"` + RoutesUrl string `json:"routes_url"` + RouteServiceUrl string `json:"route_service_url"` + SyslogDrainUrl string `json:"syslog_drain_url"` + c *Client +} + +type UserProvidedServiceInstanceRequest struct { + Name string `json:"name"` + Credentials map[string]interface{} `json:"credentials"` + SpaceGuid string `json:"space_guid"` + Tags []string `json:"tags"` + RouteServiceUrl string `json:"route_service_url"` + SyslogDrainUrl string `json:"syslog_drain_url"` +} + +func (c *Client) ListUserProvidedServiceInstancesByQuery(query url.Values) ([]UserProvidedServiceInstance, error) { + var instances []UserProvidedServiceInstance + + requestURL := "/v2/user_provided_service_instances?" + query.Encode() + for { + var sir UserProvidedServiceInstancesResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting user provided service instances") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading user provided service instances request:") + } + + err = json.Unmarshal(resBody, &sir) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling user provided service instances") + } + for _, instance := range sir.Resources { + instance.Entity.Guid = instance.Meta.Guid + instance.Entity.CreatedAt = instance.Meta.CreatedAt + instance.Entity.UpdatedAt = instance.Meta.UpdatedAt + instance.Entity.c = c + instances = append(instances, instance.Entity) + } + + requestURL = sir.NextUrl + if requestURL == "" { + break + } + } + return instances, nil +} + +func (c *Client) ListUserProvidedServiceInstances() ([]UserProvidedServiceInstance, error) { + return c.ListUserProvidedServiceInstancesByQuery(nil) +} + +func (c *Client) GetUserProvidedServiceInstanceByGuid(guid string) (UserProvidedServiceInstance, error) { + var sir UserProvidedServiceInstanceResource + req := c.NewRequest("GET", "/v2/user_provided_service_instances/"+guid) + res, err := c.DoRequest(req) + if err != nil { + return UserProvidedServiceInstance{}, errors.Wrap(err, "Error requesting user provided service instance") + } + defer res.Body.Close() + data, err := ioutil.ReadAll(res.Body) + if err != nil { + return UserProvidedServiceInstance{}, errors.Wrap(err, "Error reading user provided service instance response") + } + err = json.Unmarshal(data, &sir) + if err != nil { + return UserProvidedServiceInstance{}, errors.Wrap(err, "Error JSON parsing user provided service instance response") + } + sir.Entity.Guid = sir.Meta.Guid + sir.Entity.CreatedAt = sir.Meta.CreatedAt + sir.Entity.UpdatedAt = sir.Meta.UpdatedAt + sir.Entity.c = c + return sir.Entity, nil +} + +func (c *Client) UserProvidedServiceInstanceByGuid(guid string) (UserProvidedServiceInstance, error) { + return c.GetUserProvidedServiceInstanceByGuid(guid) +} + +func (c *Client) CreateUserProvidedServiceInstance(req UserProvidedServiceInstanceRequest) (*UserProvidedServiceInstance, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("POST", "/v2/user_provided_service_instances", buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + + return c.handleUserProvidedServiceInstanceResp(resp) +} + +func (c *Client) DeleteUserProvidedServiceInstance(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/user_provided_service_instances/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting user provided service instance %s, response code %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) UpdateUserProvidedServiceInstance(guid string, req UserProvidedServiceInstanceRequest) (*UserProvidedServiceInstance, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/user_provided_service_instances/%s", guid), buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleUserProvidedServiceInstanceResp(resp) +} + +func (c *Client) handleUserProvidedServiceInstanceResp(resp *http.Response) (*UserProvidedServiceInstance, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + var upsResource UserProvidedServiceInstanceResource + err = json.Unmarshal(body, &upsResource) + if err != nil { + return nil, err + } + return c.mergeUserProvidedServiceInstanceResource(upsResource), nil +} + +func (c *Client) mergeUserProvidedServiceInstanceResource(ups UserProvidedServiceInstanceResource) *UserProvidedServiceInstance { + ups.Entity.Guid = ups.Meta.Guid + ups.Entity.CreatedAt = ups.Meta.CreatedAt + ups.Entity.UpdatedAt = ups.Meta.UpdatedAt + ups.Entity.c = c + return &ups.Entity +} diff --git a/third_party/go-cfclient/user_provided_service_instances_test.go b/third_party/go-cfclient/user_provided_service_instances_test.go new file mode 100644 index 000000000000..2a944015930c --- /dev/null +++ b/third_party/go-cfclient/user_provided_service_instances_test.go @@ -0,0 +1,135 @@ +package cfclient + +import ( + "reflect" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestUserProvidedServiceInstanceByGuid(t *testing.T) { + Convey("Service instance by Guid", t, func() { + setup(MockRoute{"GET", "/v2/user_provided_service_instances/e9358711-0ad9-4f2a-b3dc-289d47c17c87", []string{userProvidedServiceInstancePayload}, "", 200, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + instance, err := client.GetUserProvidedServiceInstanceByGuid("e9358711-0ad9-4f2a-b3dc-289d47c17c87") + So(err, ShouldBeNil) + + So(instance.Guid, ShouldEqual, "e9358711-0ad9-4f2a-b3dc-289d47c17c87") + So(reflect.DeepEqual( + instance.Credentials, + map[string]interface{}{"creds-key-58": "creds-val-58"}), ShouldBeTrue) + So(instance.Name, ShouldEqual, "name-1700") + So(instance.SpaceGuid, ShouldEqual, "22236d1a-d9c7-44b7-bdad-2bb079a6c4a1") + So(instance.RouteServiceUrl, ShouldEqual, "") + So(instance.Type, ShouldEqual, "user_provided_service_instance") + So(instance.SpaceUrl, ShouldEqual, "/v2/spaces/22236d1a-d9c7-44b7-bdad-2bb079a6c4a1") + So(instance.SyslogDrainUrl, ShouldEqual, "https://foo.com/url-104") + So(instance.ServiceBindingsUrl, ShouldEqual, "/v2/user_provided_service_instances/e9358711-0ad9-4f2a-b3dc-289d47c17c87/service_bindings") + So(instance.RoutesUrl, ShouldEqual, "/v2/user_provided_service_instances/e9358711-0ad9-4f2a-b3dc-289d47c17c87/routes") + }) +} + +func TestListUserProvidedServiceInstances(t *testing.T) { + Convey("List Service Instances", t, func() { + setup(MockRoute{"GET", "/v2/user_provided_service_instances", []string{listUserProvidedServiceInstancePayload}, "", 200, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + instances, err := client.ListUserProvidedServiceInstances() + So(err, ShouldBeNil) + + instance := instances[0] + So(instance.Guid, ShouldEqual, "54e4c645-7d20-4271-8c27-8cc904e1e7ee") + So(reflect.DeepEqual( + instance.Credentials, + map[string]interface{}{"creds-key-57": "creds-val-57"}), ShouldBeTrue) + So(instance.Name, ShouldEqual, "name-1696") + So(instance.SpaceGuid, ShouldEqual, "87d14ac2-f396-460e-a523-dc1d77aba35a") + So(instance.RouteServiceUrl, ShouldEqual, "") + So(instance.Type, ShouldEqual, "user_provided_service_instance") + So(instance.SpaceUrl, ShouldEqual, "/v2/spaces/87d14ac2-f396-460e-a523-dc1d77aba35a") + So(instance.SyslogDrainUrl, ShouldEqual, "https://foo.com/url-103") + So(instance.ServiceBindingsUrl, ShouldEqual, "/v2/user_provided_service_instances/54e4c645-7d20-4271-8c27-8cc904e1e7ee/service_bindings") + So(instance.RoutesUrl, ShouldEqual, "/v2/user_provided_service_instances/54e4c645-7d20-4271-8c27-8cc904e1e7ee/routes") + }) +} + +func TestCreateUserProvidedServiceInstance(t *testing.T) { + Convey("Create User Provided Service Instance", t, func() { + setup(MockRoute{"POST", "/v2/user_provided_service_instances", []string{userProvidedServiceInstancePayload}, "", 201, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + + client, err := NewClient(c) + So(err, ShouldBeNil) + + upsRequest := UserProvidedServiceInstanceRequest{Credentials: map[string]interface{}{"creds-key-58": "creds-val-58"}, SyslogDrainUrl: "https://foo.com/url-104"} + upsInstance, err := client.CreateUserProvidedServiceInstance(upsRequest) + + So(err, ShouldBeNil) + So(upsInstance.Guid, ShouldEqual, "e9358711-0ad9-4f2a-b3dc-289d47c17c87") + So(upsInstance.Name, ShouldEqual, "name-1700") + So(upsInstance.Credentials["creds-key-58"], ShouldEqual, "creds-val-58") + So(upsInstance.SyslogDrainUrl, ShouldEqual, "https://foo.com/url-104") + }) +} + +func TestDeleteUserProvidedServiceInstance(t *testing.T) { + Convey("Delete User Provided Service Instance", t, func() { + setup(MockRoute{"DELETE", "/v2/user_provided_service_instances/e9358711-0ad9-4f2a-b3dc-289d47c17c87", []string{""}, "", 204, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteUserProvidedServiceInstance("e9358711-0ad9-4f2a-b3dc-289d47c17c87") + So(err, ShouldBeNil) + }) +} + +func TestUpdateUserProvidedServiceInstance(t *testing.T) { + Convey("Update User Provided Service Instance", t, func() { + setup(MockRoute{"PUT", "/v2/user_provided_service_instances/e9358711-0ad9-4f2a-b3dc-289d47c17c87", []string{userProvidedServiceInstancePayload}, "", 201, "", nil}, t) + defer teardown() + + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + + client, err := NewClient(c) + So(err, ShouldBeNil) + + upsRequest := UserProvidedServiceInstanceRequest{Credentials: map[string]interface{}{"creds-key-58": "creds-val-58"}, SyslogDrainUrl: "https://foo.com/url-104"} + upsInstance, err := client.UpdateUserProvidedServiceInstance("e9358711-0ad9-4f2a-b3dc-289d47c17c87", upsRequest) + + So(err, ShouldBeNil) + So(upsInstance.Guid, ShouldEqual, "e9358711-0ad9-4f2a-b3dc-289d47c17c87") + So(upsInstance.Name, ShouldEqual, "name-1700") + So(upsInstance.Credentials["creds-key-58"], ShouldEqual, "creds-val-58") + So(upsInstance.SyslogDrainUrl, ShouldEqual, "https://foo.com/url-104") + }) +} diff --git a/third_party/go-cfclient/users.go b/third_party/go-cfclient/users.go new file mode 100644 index 000000000000..9af6d9da763e --- /dev/null +++ b/third_party/go-cfclient/users.go @@ -0,0 +1,205 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type UserRequest struct { + Guid string `json:"guid"` + DefaultSpaceGuid string `json:"default_space_guid,omitempty"` +} + +type Users []User + +type User struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Admin bool `json:"admin"` + Active bool `json:"active"` + DefaultSpaceGUID string `json:"default_space_guid"` + Username string `json:"username"` + SpacesURL string `json:"spaces_url"` + OrgsURL string `json:"organizations_url"` + ManagedOrgsURL string `json:"managed_organizations_url"` + BillingManagedOrgsURL string `json:"billing_managed_organizations_url"` + AuditedOrgsURL string `json:"audited_organizations_url"` + ManagedSpacesURL string `json:"managed_spaces_url"` + AuditedSpacesURL string `json:"audited_spaces_url"` + c *Client +} + +type UserResource struct { + Meta Meta `json:"metadata"` + Entity User `json:"entity"` +} + +type UserResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []UserResource `json:"resources"` +} + +// GetUserByGUID retrieves the user with the provided guid. +func (c *Client) GetUserByGUID(guid string) (User, error) { + var userRes UserResource + r := c.NewRequest("GET", "/v2/users/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return User{}, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return User{}, err + } + err = json.Unmarshal(body, &userRes) + if err != nil { + return User{}, err + } + return c.mergeUserResource(userRes), nil +} + +func (c *Client) ListUsersByQuery(query url.Values) (Users, error) { + var users []User + requestUrl := "/v2/users?" + query.Encode() + for { + userResp, err := c.getUserResponse(requestUrl) + if err != nil { + return []User{}, err + } + for _, user := range userResp.Resources { + user.Entity.Guid = user.Meta.Guid + user.Entity.CreatedAt = user.Meta.CreatedAt + user.Entity.UpdatedAt = user.Meta.UpdatedAt + user.Entity.c = c + users = append(users, user.Entity) + } + requestUrl = userResp.NextUrl + if requestUrl == "" || query.Get("page") != "" { + break + } + } + return users, nil +} + +func (c *Client) ListUsers() (Users, error) { + return c.ListUsersByQuery(nil) +} + +func (c *Client) ListUserSpaces(userGuid string) ([]Space, error) { + return c.fetchSpaces(fmt.Sprintf("/v2/users/%s/spaces", userGuid), url.Values{}) +} + +func (c *Client) ListUserAuditedSpaces(userGuid string) ([]Space, error) { + return c.fetchSpaces(fmt.Sprintf("/v2/users/%s/audited_spaces", userGuid), url.Values{}) +} + +func (c *Client) ListUserManagedSpaces(userGuid string) ([]Space, error) { + return c.fetchSpaces(fmt.Sprintf("/v2/users/%s/managed_spaces", userGuid), url.Values{}) +} + +func (c *Client) ListUserOrgs(userGuid string) ([]Org, error) { + return c.fetchOrgs(fmt.Sprintf("/v2/users/%s/organizations", userGuid)) +} + +func (c *Client) ListUserManagedOrgs(userGuid string) ([]Org, error) { + return c.fetchOrgs(fmt.Sprintf("/v2/users/%s/managed_organizations", userGuid)) +} + +func (c *Client) ListUserAuditedOrgs(userGuid string) ([]Org, error) { + return c.fetchOrgs(fmt.Sprintf("/v2/users/%s/audited_organizations", userGuid)) +} + +func (c *Client) ListUserBillingManagedOrgs(userGuid string) ([]Org, error) { + return c.fetchOrgs(fmt.Sprintf("/v2/users/%s/billing_managed_organizations", userGuid)) +} + +func (c *Client) CreateUser(req UserRequest) (User, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return User{}, err + } + r := c.NewRequestWithBody("POST", "/v2/users", buf) + resp, err := c.DoRequest(r) + if err != nil { + return User{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return User{}, errors.Wrapf(err, "Error creating user, response code: %d", resp.StatusCode) + } + body, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return User{}, err + } + var userResource UserResource + err = json.Unmarshal(body, &userResource) + if err != nil { + return User{}, err + } + user := userResource.Entity + user.Guid = userResource.Meta.Guid + user.c = c + return user, nil +} + +func (c *Client) DeleteUser(userGuid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/users/%s", userGuid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting user %s, response code: %d", userGuid, resp.StatusCode) + } + return nil +} + +func (u Users) GetUserByUsername(username string) User { + for _, user := range u { + if user.Username == username { + return user + } + } + return User{} +} + +func (c *Client) getUserResponse(requestUrl string) (UserResponse, error) { + var userResp UserResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return UserResponse{}, errors.Wrap(err, "Error requesting users") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return UserResponse{}, errors.Wrap(err, "Error reading user request") + } + err = json.Unmarshal(resBody, &userResp) + if err != nil { + return UserResponse{}, errors.Wrap(err, "Error unmarshalling user") + } + return userResp, nil +} + +func (c *Client) mergeUserResource(u UserResource) User { + u.Entity.Guid = u.Meta.Guid + u.Entity.CreatedAt = u.Meta.CreatedAt + u.Entity.UpdatedAt = u.Meta.UpdatedAt + u.Entity.c = c + return u.Entity +} diff --git a/third_party/go-cfclient/users_test.go b/third_party/go-cfclient/users_test.go new file mode 100644 index 000000000000..bf5418926676 --- /dev/null +++ b/third_party/go-cfclient/users_test.go @@ -0,0 +1,272 @@ +package cfclient + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestGetUserByGUID(t *testing.T) { + Convey("Get user by GUID", t, func() { + setup(MockRoute{"GET", "/v2/users/72ccf759-43aa-4954-903f-7d892c268e80", []string{userByGUIDPayload}, "", 200, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + user, err := client.GetUserByGUID("72ccf759-43aa-4954-903f-7d892c268e80") + So(err, ShouldBeNil) + + So(user.Guid, ShouldEqual, "72ccf759-43aa-4954-903f-7d892c268e80") + So(user.Username, ShouldEqual, "user@example.com") + }) +} + +func TestListUsers(t *testing.T) { + Convey("List Users", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/users", []string{listUsersPayload}, "", 200, "", nil}, + {"GET", "/v2/usersPage2", []string{listUsersPayloadPage2}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + users, err := client.ListUsers() + So(err, ShouldBeNil) + + So(len(users), ShouldEqual, 4) + So(users[0].Guid, ShouldEqual, "ccec6d06-5f71-48a0-a4c5-c91a1d9f2fac") + So(users[0].Username, ShouldEqual, "testUser1") + So(users[1].Guid, ShouldEqual, "f97f5699-c920-4633-aa23-bd70f3db0808") + So(users[1].Username, ShouldEqual, "testUser2") + So(users[2].Guid, ShouldEqual, "cadd6389-fcf6-4928-84f0-6153556bf693") + So(users[2].Username, ShouldEqual, "testUser3") + So(users[3].Guid, ShouldEqual, "79c854b0-c12a-41b7-8d3c-fdd6e116e385") + So(users[3].Username, ShouldEqual, "testUser4") + }) +} + +func TestListUserSpaces(t *testing.T) { + Convey("List User Spaces", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/spaces", []string{listUserSpacesPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + spaces, err := client.ListUserSpaces("cadd6389-fcf6-4928-84f0-6153556bf693") + So(err, ShouldBeNil) + + So(len(spaces), ShouldEqual, 1) + So(spaces[0].Guid, ShouldEqual, "9881c79e-d269-4a53-9d77-cb21b745356e") + So(spaces[0].Name, ShouldEqual, "dev") + So(spaces[0].OrganizationGuid, ShouldEqual, "6a2a2d18-7620-43cf-a332-353824b431b2") + }) +} + +func TestListUserManagedSpaces(t *testing.T) { + Convey("List User Audited Spaces", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/managed_spaces", []string{listUserSpacesPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + spaces, err := client.ListUserManagedSpaces("cadd6389-fcf6-4928-84f0-6153556bf693") + So(err, ShouldBeNil) + + So(len(spaces), ShouldEqual, 1) + So(spaces[0].Guid, ShouldEqual, "9881c79e-d269-4a53-9d77-cb21b745356e") + So(spaces[0].Name, ShouldEqual, "dev") + So(spaces[0].OrganizationGuid, ShouldEqual, "6a2a2d18-7620-43cf-a332-353824b431b2") + }) +} + +func TestListUserAuditedSpaces(t *testing.T) { + Convey("List User Managed Spaces", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/audited_spaces", []string{listUserSpacesPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + spaces, err := client.ListUserAuditedSpaces("cadd6389-fcf6-4928-84f0-6153556bf693") + So(err, ShouldBeNil) + + So(len(spaces), ShouldEqual, 1) + So(spaces[0].Guid, ShouldEqual, "9881c79e-d269-4a53-9d77-cb21b745356e") + So(spaces[0].Name, ShouldEqual, "dev") + So(spaces[0].OrganizationGuid, ShouldEqual, "6a2a2d18-7620-43cf-a332-353824b431b2") + }) +} + +func TestListUserOrgs(t *testing.T) { + Convey("List User Spaces", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/organizations", []string{listUserOrgsPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + orgs, err := client.ListUserOrgs("cadd6389-fcf6-4928-84f0-6153556bf693") + So(err, ShouldBeNil) + + So(len(orgs), ShouldEqual, 1) + So(orgs[0].Guid, ShouldEqual, "9881c79e-d269-4a53-9d77-cb21b745356e") + So(orgs[0].Name, ShouldEqual, "dev") + So(orgs[0].QuotaDefinitionGuid, ShouldEqual, "6a2a2d18-7620-43cf-a332-353824b431b2") + }) +} + +func TestListUserManagedOrgs(t *testing.T) { + Convey("List User Spaces", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/managed_organizations", []string{listUserOrgsPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + orgs, err := client.ListUserManagedOrgs("cadd6389-fcf6-4928-84f0-6153556bf693") + So(err, ShouldBeNil) + + So(len(orgs), ShouldEqual, 1) + So(orgs[0].Guid, ShouldEqual, "9881c79e-d269-4a53-9d77-cb21b745356e") + So(orgs[0].Name, ShouldEqual, "dev") + So(orgs[0].QuotaDefinitionGuid, ShouldEqual, "6a2a2d18-7620-43cf-a332-353824b431b2") + }) +} + +func TestListUserAuditedOrgs(t *testing.T) { + Convey("List User Audited Spaces", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/audited_organizations", []string{listUserOrgsPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + orgs, err := client.ListUserAuditedOrgs("cadd6389-fcf6-4928-84f0-6153556bf693") + So(err, ShouldBeNil) + + So(len(orgs), ShouldEqual, 1) + So(orgs[0].Guid, ShouldEqual, "9881c79e-d269-4a53-9d77-cb21b745356e") + So(orgs[0].Name, ShouldEqual, "dev") + So(orgs[0].QuotaDefinitionGuid, ShouldEqual, "6a2a2d18-7620-43cf-a332-353824b431b2") + }) +} + +func TestUserBillingManagedOrgs(t *testing.T) { + Convey("List User Managed Spaces", t, func() { + mocks := []MockRoute{ + {"GET", "/v2/users/cadd6389-fcf6-4928-84f0-6153556bf693/billing_managed_organizations", []string{listUserOrgsPayload}, "", 200, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + orgs, err := client.ListUserBillingManagedOrgs("cadd6389-fcf6-4928-84f0-6153556bf693") + So(err, ShouldBeNil) + + So(len(orgs), ShouldEqual, 1) + So(orgs[0].Guid, ShouldEqual, "9881c79e-d269-4a53-9d77-cb21b745356e") + So(orgs[0].Name, ShouldEqual, "dev") + So(orgs[0].QuotaDefinitionGuid, ShouldEqual, "6a2a2d18-7620-43cf-a332-353824b431b2") + }) +} + +func TestGetUserByUsername(t *testing.T) { + Convey("Get User by Username", t, func() { + user1 := User{Guid: "ccec6d06-5f71-48a0-a4c5-c91a1d9f2fac", Username: "testUser1"} + user2 := User{Guid: "f97f5699-c920-4633-aa23-bd70f3db0808", Username: "testUser2"} + user3 := User{Guid: "cadd6389-fcf6-4928-84f0-6153556bf693", Username: "testUser3"} + user4 := User{Guid: "79c854b0-c12a-41b7-8d3c-fdd6e116e385", Username: "testUser4"} + users := Users{user1, user2, user3, user4} + + So(users.GetUserByUsername("testUser1"), ShouldResemble, user1) + So(users.GetUserByUsername("testUser2"), ShouldResemble, user2) + So(users.GetUserByUsername("testUser3"), ShouldResemble, user3) + So(users.GetUserByUsername("testUser4"), ShouldResemble, user4) + }) +} + +func TestCreateUser(t *testing.T) { + Convey("Create user", t, func() { + setup(MockRoute{"POST", "/v2/users", []string{createUserPayload}, "", 201, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + user, err := client.CreateUser(UserRequest{Guid: "guid-cb24b36d-4656-468e-a50d-b53113ac6177"}) + So(err, ShouldBeNil) + So(user.Guid, ShouldEqual, "guid-cb24b36d-4656-468e-a50d-b53113ac6177") + }) +} + +func TestDeleteUser(t *testing.T) { + Convey("Delete user", t, func() { + setup(MockRoute{"DELETE", "/v2/users/guid-cb24b36d-4656-468e-a50d-b53113ac6177", []string{""}, "", 204, "", nil}, t) + defer teardown() + c := &Config{ + ApiAddress: server.URL, + Token: "foobar", + } + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteUser("guid-cb24b36d-4656-468e-a50d-b53113ac6177") + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/v3apps.go b/third_party/go-cfclient/v3apps.go new file mode 100644 index 000000000000..90a5806f0df3 --- /dev/null +++ b/third_party/go-cfclient/v3apps.go @@ -0,0 +1,285 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type V3App struct { + Name string `json:"name,omitempty"` + State string `json:"state,omitempty"` + Lifecycle V3Lifecycle `json:"lifecycle,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +type V3Lifecycle struct { + Type string `json:"type,omitempty"` + BuildpackData V3BuildpackLifecycle `json:"data,omitempty"` +} + +type V3BuildpackLifecycle struct { + Buildpacks []string `json:"buildpacks,omitempty"` + Stack string `json:"stack,omitempty"` +} + +type CreateV3AppRequest struct { + Name string + SpaceGUID string + EnvironmentVariables map[string]string + Lifecycle *V3Lifecycle + Metadata *V3Metadata +} + +type UpdateV3AppRequest struct { + Name string `json:"name"` + Lifecycle *V3Lifecycle `json:"lifecycle"` + Metadata *V3Metadata `json:"metadata"` +} + +func (c *Client) CreateV3App(r CreateV3AppRequest) (*V3App, error) { + req := c.NewRequest("POST", "/v3/apps") + params := map[string]interface{}{ + "name": r.Name, + "relationships": map[string]interface{}{ + "space": V3ToOneRelationship{ + Data: V3Relationship{ + GUID: r.SpaceGUID, + }, + }, + }, + } + if len(r.EnvironmentVariables) > 0 { + params["environment_variables"] = r.EnvironmentVariables + } + if r.Lifecycle != nil { + params["lifecycle"] = r.Lifecycle + } + if r.Metadata != nil { + params["metadata"] = r.Metadata + } + + req.obj = params + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating v3 app %s, response code: %d", r.Name, resp.StatusCode) + } + + var app V3App + if err := json.NewDecoder(resp.Body).Decode(&app); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app JSON") + } + + return &app, nil +} + +func (c *Client) GetV3AppByGUID(guid string) (*V3App, error) { + req := c.NewRequest("GET", "/v3/apps/"+guid) + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while getting v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting v3 app with GUID [%s], response code: %d", guid, resp.StatusCode) + } + + var app V3App + if err := json.NewDecoder(resp.Body).Decode(&app); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app JSON") + } + + return &app, nil +} + +func (c *Client) StartV3App(guid string) (*V3App, error) { + req := c.NewRequest("POST", "/v3/apps/"+guid+"/actions/start") + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while starting v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error starting v3 app with GUID [%s], response code: %d", guid, resp.StatusCode) + } + + var app V3App + if err := json.NewDecoder(resp.Body).Decode(&app); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app JSON") + } + + return &app, nil +} + +func (c *Client) DeleteV3App(guid string) error { + req := c.NewRequest("DELETE", "/v3/apps/"+guid) + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error while deleting v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("Error deleting v3 app with GUID [%s], response code: %d", guid, resp.StatusCode) + } + + return nil +} + +func (c *Client) UpdateV3App(appGUID string, r UpdateV3AppRequest) (*V3App, error) { + req := c.NewRequest("PATCH", "/v3/apps/"+appGUID) + params := make(map[string]interface{}) + if r.Name != "" { + params["name"] = r.Name + } + if r.Lifecycle != nil { + params["lifecycle"] = r.Lifecycle + } + if r.Metadata != nil { + params["metadata"] = r.Metadata + } + if len(params) > 0 { + req.obj = params + } + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while updating v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error updating v3 app %s, response code: %d", appGUID, resp.StatusCode) + } + + var app V3App + if err := json.NewDecoder(resp.Body).Decode(&app); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app JSON") + } + + return &app, nil +} + +type listV3AppsResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3App `json:"resources,omitempty"` +} + +func (c *Client) ListV3AppsByQuery(query url.Values) ([]V3App, error) { + var apps []V3App + requestURL := "/v3/apps" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 apps") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 apps, response code: %d", resp.StatusCode) + } + + var data listV3AppsResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 apps") + } + + apps = append(apps, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 apps") + } + } + + return apps, nil +} + +func extractPathFromURL(requestURL string) (string, error) { + url, err := url.Parse(requestURL) + if err != nil { + return "", err + } + result := url.Path + if q := url.Query().Encode(); q != "" { + result = result + "?" + q + } + return result, nil +} + +type V3AppEnvironment struct { + EnvVars map[string]string `json:"environment_variables,omitempty"` + StagingEnv map[string]string `json:"staging_env_json,omitempty"` + RunningEnv map[string]string `json:"running_env_json,omitempty"` + SystemEnvVars map[string]json.RawMessage `json:"system_env_json,omitempty"` // VCAP_SERVICES + AppEnvVars map[string]json.RawMessage `json:"application_env_json,omitempty"` // VCAP_APPLICATION +} + +func (c *Client) GetV3AppEnvironment(appGUID string) (V3AppEnvironment, error) { + var result V3AppEnvironment + + resp, err := c.DoRequest(c.NewRequest("GET", "/v3/apps/"+appGUID+"/env")) + if err != nil { + return result, errors.Wrapf(err, "Error requesting app env for %s", appGUID) + } + + defer resp.Body.Close() + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return result, errors.Wrap(err, "Error parsing JSON for app env") + } + + return result, nil +} + +type V3EnvVar struct { + Var map[string]*string `json:"var"` +} + +type v3EnvVarResponse struct { + V3EnvVar + Links map[string]Link `json:"links"` +} + +func (c *Client) SetV3AppEnvVariables(appGUID string, envRequest V3EnvVar) (V3EnvVar, error) { + var result v3EnvVarResponse + + req := c.NewRequest("PATCH", "/v3/apps/"+appGUID+"/environment_variables") + req.obj = envRequest + + resp, err := c.DoRequest(req) + if err != nil { + return result.V3EnvVar, errors.Wrapf(err, "Error setting app env variables for %s", appGUID) + } + + defer resp.Body.Close() + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return result.V3EnvVar, errors.Wrap(err, "Error parsing JSON for app env") + } + + return result.V3EnvVar, nil +} diff --git a/third_party/go-cfclient/v3apps_test.go b/third_party/go-cfclient/v3apps_test.go new file mode 100644 index 000000000000..cd318157a4cc --- /dev/null +++ b/third_party/go-cfclient/v3apps_test.go @@ -0,0 +1,205 @@ +package cfclient + +import ( + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestCreateV3App(t *testing.T) { + Convey("Create V3 App", t, func() { + expectedBody := `{"environment_variables":{"FOO":"BAR"},"name":"my-app","relationships":{"space":{"data":{"guid":"space-guid"}}}}` + setup(MockRoute{"POST", "/v3/apps", []string{createV3AppPayload}, "", http.StatusCreated, "", &expectedBody}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + app, err := client.CreateV3App(CreateV3AppRequest{ + Name: "my-app", + SpaceGUID: "space-guid", + EnvironmentVariables: map[string]string{"FOO": "BAR"}, + }) + So(err, ShouldBeNil) + So(app, ShouldNotBeNil) + + So(app.GUID, ShouldEqual, "app-guid") + So(app.Relationships["space"].Data.GUID, ShouldEqual, "space-guid") + So(app.Lifecycle.Type, ShouldEqual, "buildpack") + So(app.Lifecycle.BuildpackData.Buildpacks, ShouldHaveLength, 1) + So(app.Lifecycle.BuildpackData.Buildpacks[0], ShouldEqual, "java_buildpack") + So(app.Lifecycle.BuildpackData.Stack, ShouldEqual, "cflinuxfs2") + So(app.Links["space"].Href, ShouldEqual, "https://api.example.org/v3/spaces/space-guid") + So(app.Metadata.Annotations, ShouldHaveLength, 0) + }) +} + +func TestGetV3App(t *testing.T) { + Convey("Get V3 App", t, func() { + setup(MockRoute{"GET", "/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446", []string{getV3AppPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + app, err := client.GetV3AppByGUID("1cb006ee-fb05-47e1-b541-c34179ddc446") + So(err, ShouldBeNil) + So(app, ShouldNotBeNil) + + So(app.GUID, ShouldEqual, "1cb006ee-fb05-47e1-b541-c34179ddc446") + So(app.Name, ShouldEqual, "my_app") + So(app.Lifecycle.Type, ShouldEqual, "buildpack") + So(app.Lifecycle.BuildpackData.Buildpacks, ShouldHaveLength, 1) + So(app.Lifecycle.BuildpackData.Buildpacks[0], ShouldEqual, "java_buildpack") + So(app.Lifecycle.BuildpackData.Stack, ShouldEqual, "cflinuxfs2") + So(app.Links["space"].Href, ShouldEqual, "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576") + So(app.Metadata.Annotations, ShouldHaveLength, 1) + So(app.Metadata.Annotations["contacts"], ShouldEqual, "Bill tel(1111111) email(bill@fixme), Bob tel(222222) pager(3333333#555) email(bob@fixme)") + }) +} + +func TestGetV3AppEnv(t *testing.T) { + Convey("Get V3 App Environment", t, func() { + setup(MockRoute{"GET", "/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/env", []string{getV3AppEnvPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + env, err := client.GetV3AppEnvironment("1cb006ee-fb05-47e1-b541-c34179ddc446") + So(err, ShouldBeNil) + + So(env.EnvVars, ShouldHaveLength, 1) + So(env.EnvVars["RAILS_ENV"], ShouldEqual, "production") + + So(env.StagingEnv, ShouldHaveLength, 1) + So(env.StagingEnv["GEM_CACHE"], ShouldEqual, "http://gem-cache.example.org") + + So(env.RunningEnv, ShouldHaveLength, 1) + So(env.RunningEnv["HTTP_PROXY"], ShouldEqual, "http://proxy.example.org") + + So(env.SystemEnvVars, ShouldHaveLength, 1) + So(env.SystemEnvVars, ShouldContainKey, "VCAP_SERVICES") + + So(env.AppEnvVars, ShouldHaveLength, 1) + So(env.AppEnvVars, ShouldContainKey, "VCAP_APPLICATION") + }) +} + +func TestSetV3AppEnvVariables(t *testing.T) { + Convey("Get V3 App Environment", t, func() { + setup(MockRoute{"PATCH", "/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/environment_variables", []string{setV3AppEnvironmentVariablesPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + falseVar := "false" + env, err := client.SetV3AppEnvVariables("1cb006ee-fb05-47e1-b541-c34179ddc446", + V3EnvVar{Var: map[string]*string{ + "DEBUG": &falseVar, + "USER": nil, + }}, + ) + So(err, ShouldBeNil) + So(env.Var, ShouldHaveLength, 2) + So(*env.Var["RAILS_ENV"], ShouldEqual, "production") + So(*env.Var["DEBUG"], ShouldEqual, "false") + }) +} + +func TestStartV3App(t *testing.T) { + Convey("Start V3 App", t, func() { + setup(MockRoute{"POST", "/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/start", []string{startV3AppPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + app, err := client.StartV3App("1cb006ee-fb05-47e1-b541-c34179ddc446") + So(err, ShouldBeNil) + So(app, ShouldNotBeNil) + + So(app.State, ShouldEqual, "STARTED") + So(app.GUID, ShouldEqual, "1cb006ee-fb05-47e1-b541-c34179ddc446") + So(app.Name, ShouldEqual, "my_app") + So(app.Lifecycle.Type, ShouldEqual, "buildpack") + So(app.Lifecycle.BuildpackData.Buildpacks, ShouldHaveLength, 1) + So(app.Lifecycle.BuildpackData.Buildpacks[0], ShouldEqual, "java_buildpack") + So(app.Lifecycle.BuildpackData.Stack, ShouldEqual, "cflinuxfs2") + So(app.Links["space"].Href, ShouldEqual, "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576") + So(app.Metadata.Annotations, ShouldHaveLength, 0) + }) +} + +func TestDeleteV3App(t *testing.T) { + Convey("Delete V3 App", t, func() { + setup(MockRoute{"DELETE", "/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446", []string{""}, "", http.StatusAccepted, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteV3App("1cb006ee-fb05-47e1-b541-c34179ddc446") + So(err, ShouldBeNil) + }) +} + +func TestUpdateV3App(t *testing.T) { + Convey("Update V3 App", t, func() { + setup(MockRoute{"PATCH", "/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446", []string{updateV3AppPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + app, err := client.UpdateV3App("1cb006ee-fb05-47e1-b541-c34179ddc446", UpdateV3AppRequest{}) + So(err, ShouldBeNil) + So(app, ShouldNotBeNil) + + So(app.GUID, ShouldEqual, "1cb006ee-fb05-47e1-b541-c34179ddc446") + So(app.State, ShouldEqual, "STARTED") + So(app.Name, ShouldEqual, "my_app") + So(app.Lifecycle.Type, ShouldEqual, "buildpack") + So(app.Lifecycle.BuildpackData.Buildpacks, ShouldHaveLength, 1) + So(app.Lifecycle.BuildpackData.Buildpacks[0], ShouldEqual, "java_buildpack") + So(app.Lifecycle.BuildpackData.Stack, ShouldEqual, "cflinuxfs2") + So(app.Links["space"].Href, ShouldEqual, "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576") + So(app.Metadata.Annotations, ShouldHaveLength, 0) + So(app.Metadata.Labels, ShouldHaveLength, 2) + So(app.Metadata.Labels["environment"], ShouldEqual, "production") + So(app.Metadata.Labels["internet-facing"], ShouldEqual, "false") + }) +} + +func TestListV3AppsByQuery(t *testing.T) { + Convey("List V3 Apps", t, func() { + setup(MockRoute{"GET", "/v3/apps", []string{listV3AppsPayload, listV3AppsPayloadPage2}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + apps, err := client.ListV3AppsByQuery(nil) + So(err, ShouldBeNil) + So(apps, ShouldHaveLength, 2) + + So(apps[0].Name, ShouldEqual, "my_app") + So(apps[1].Name, ShouldEqual, "my_app2") + + So(apps[1].State, ShouldEqual, "STOPPED") + + So(apps[0].Lifecycle.BuildpackData.Buildpacks[0], ShouldEqual, "java_buildpack") + So(apps[1].Lifecycle.BuildpackData.Buildpacks[0], ShouldEqual, "ruby_buildpack") + So(apps[1].Lifecycle.BuildpackData.Buildpacks[1], ShouldEqual, "staticfile_buildpack") + }) +} diff --git a/third_party/go-cfclient/v3build.go b/third_party/go-cfclient/v3build.go new file mode 100644 index 000000000000..432f8910e943 --- /dev/null +++ b/third_party/go-cfclient/v3build.go @@ -0,0 +1,77 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/pkg/errors" +) + +type V3Build struct { + State string `json:"state,omitempty"` + Error string `json:"error,omitempty"` + Lifecycle V3Lifecycle `json:"lifecycle,omitempty"` + Package V3Relationship `json:"package,omitempty"` + Droplet V3Relationship `json:"droplet,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + CreatedBy V3CreatedBy `json:"created_by,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +type V3CreatedBy struct { + GUID string `json:"guid,omitempty"` + Name string `json:"name,omitempty"` + Email string `json:"email,omitempty"` +} + +func (c *Client) GetV3BuildByGUID(buildGUID string) (*V3Build, error) { + resp, err := c.DoRequest(c.NewRequest("GET", "/v3/builds/"+buildGUID)) + if err != nil { + return nil, errors.Wrap(err, "Error getting V3 build") + } + defer resp.Body.Close() + + var build V3Build + if err := json.NewDecoder(resp.Body).Decode(&build); err != nil { + return nil, errors.Wrap(err, "Error reading V3 build JSON") + } + + return &build, nil +} + +func (c *Client) CreateV3Build(packageGUID string, lifecycle *V3Lifecycle, metadata *V3Metadata) (*V3Build, error) { + req := c.NewRequest("POST", "/v3/builds") + params := map[string]interface{}{ + "package": map[string]interface{}{ + "guid": packageGUID, + }, + } + if lifecycle != nil { + params["lifecycle"] = lifecycle + } + if metadata != nil { + params["metadata"] = metadata + } + req.obj = params + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 build") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating v3 build, response code: %d", resp.StatusCode) + } + + var build V3Build + if err := json.NewDecoder(resp.Body).Decode(&build); err != nil { + return nil, errors.Wrap(err, "Error reading V3 Build JSON") + } + + return &build, nil +} diff --git a/third_party/go-cfclient/v3build_test.go b/third_party/go-cfclient/v3build_test.go new file mode 100644 index 000000000000..0c39d39e507c --- /dev/null +++ b/third_party/go-cfclient/v3build_test.go @@ -0,0 +1,29 @@ +package cfclient + +import ( + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestCreateV3Build(t *testing.T) { + Convey("Get V3 App Environment", t, func() { + body := `{"metadata":{"labels":{"foo":"bar"}},"package":{"guid":"package-guid"}}` + setup(MockRoute{"POST", "/v3/builds", []string{createV3BuildPayload}, "", http.StatusCreated, "", &body}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + build, err := client.CreateV3Build("package-guid", nil, + &V3Metadata{Labels: map[string]string{"foo": "bar"}}) + So(err, ShouldBeNil) + So(build, ShouldNotBeNil) + + So(build.GUID, ShouldEqual, "585bc3c1-3743-497d-88b0-403ad6b56d16") + So(build.CreatedBy.Name, ShouldEqual, "bill") + So(build.Package.GUID, ShouldEqual, "8e4da443-f255-499c-8b47-b3729b5b7432") + }) +} diff --git a/third_party/go-cfclient/v3deployments.go b/third_party/go-cfclient/v3deployments.go new file mode 100644 index 000000000000..2314e95a9e1d --- /dev/null +++ b/third_party/go-cfclient/v3deployments.go @@ -0,0 +1,132 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/pkg/errors" +) + +type CreateV3DeploymentOptionalParameters struct { + Droplet *V3Relationship `json:"droplet,omitempty"` + Revision *V3DeploymentRevision `json:"revision,omitempty"` + Strategy *string `json:"strategy,omitempty"` + Metadata *V3Metadata `json:"metadata,omitempty"` +} + +type createV3DeploymentRequest struct { + *CreateV3DeploymentOptionalParameters `json:",inline"` + Relationships struct { + App V3ToOneRelationship `json:"app"` + } `json:"relationships"` +} + +type V3DeploymentRevision struct { + GUID string `json:"guid"` + Version int `json:"version"` +} + +type V3ProcessReference struct { + GUID string `json:"guid"` + Type string `type:"type"` +} + +type V3DeploymentStatus struct { + Value string `json:"value"` + Reason string `json:"reason"` + Details map[string]string `json:"details"` +} + +type V3Deployment struct { + GUID string `json:"guid"` + State string `json:"state"` + Status V3DeploymentStatus `json:"status"` + Strategy string `json:"strategy"` + Droplet V3Relationship `json:"droplet"` + PreviousDroplet V3Relationship `json:"previous_droplet"` + NewProcesses []V3ProcessReference `json:"new_processes"` + Revision V3DeploymentRevision `json:"revision"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` +} + +func (c *Client) GetV3Deployment(deploymentGUID string) (*V3Deployment, error) { + req := c.NewRequest("GET", "/v3/deployments/"+deploymentGUID) + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error getting deployment") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting deployment with GUID [%s], response code: %d", deploymentGUID, resp.StatusCode) + } + + var r V3Deployment + if err := json.NewDecoder(resp.Body).Decode(&r); err != nil { + return nil, errors.Wrap(err, "Error reading deployment response JSON") + } + + return &r, nil +} + +func (c *Client) CreateV3Deployment(appGUID string, optionalParams *CreateV3DeploymentOptionalParameters) (*V3Deployment, error) { + // validate the params + if optionalParams != nil { + if optionalParams.Droplet != nil && optionalParams.Revision != nil { + return nil, errors.New("droplet and revision cannot both be set") + } + } + + requestBody := createV3DeploymentRequest{} + requestBody.CreateV3DeploymentOptionalParameters = optionalParams + + requestBody.Relationships = struct { + App V3ToOneRelationship "json:\"app\"" + }{ + App: V3ToOneRelationship{ + Data: V3Relationship{ + GUID: appGUID, + }, + }, + } + + req := c.NewRequest("POST", "/v3/deployments") + req.obj = requestBody + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error creating deployment") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating deployment for app GUID [%s], response code: %d", appGUID, resp.StatusCode) + } + + var r V3Deployment + if err = json.NewDecoder(resp.Body).Decode(&r); err != nil { + return nil, errors.Wrap(err, "Error reading deployment response JSON") + } + + return &r, nil +} + +func (c *Client) CancelV3Deployment(deploymentGUID string) error { + req := c.NewRequest("POST", "/v3/deployments/"+deploymentGUID+"/actions/cancel") + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error canceling deployment") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Error canceling deployment [%s], response code: %d", deploymentGUID, resp.StatusCode) + } + + return nil +} diff --git a/third_party/go-cfclient/v3deployments_test.go b/third_party/go-cfclient/v3deployments_test.go new file mode 100644 index 000000000000..81918833e60e --- /dev/null +++ b/third_party/go-cfclient/v3deployments_test.go @@ -0,0 +1,132 @@ +package cfclient + +import ( + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestGetDeployment(t *testing.T) { + Convey("Get V3 Deployment", t, func() { + setup(MockRoute{"GET", "/v3/deployments/59c3d133-2b83-46f3-960e-7765a129aea4", []string{getV3DeploymentPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + resp, err := client.GetV3Deployment("59c3d133-2b83-46f3-960e-7765a129aea4") + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + + So(resp.GUID, ShouldEqual, "59c3d133-2b83-46f3-960e-7765a129aea4") + So(resp.Status.Reason, ShouldEqual, "DEPLOYING") + So(resp.Relationships["app"].Data.GUID, ShouldEqual, "305cea31-5a44-45ca-b51b-e89c7a8ef8b2") + }) +} + +func TestCreateDeployment(t *testing.T) { + Convey("Create V3 Deployment without optional parameters", t, func() { + body := `{"relationships":{"app":{"data":{"guid":"305cea31-5a44-45ca-b51b-e89c7a8ef8b2"}}}}` + setup(MockRoute{"POST", "/v3/deployments", []string{getV3DeploymentPayload}, "", http.StatusCreated, "", &body}, t) + + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + resp, err := client.CreateV3Deployment("305cea31-5a44-45ca-b51b-e89c7a8ef8b2", nil) + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + + So(resp.GUID, ShouldEqual, "59c3d133-2b83-46f3-960e-7765a129aea4") + So(resp.Status.Reason, ShouldEqual, "DEPLOYING") + So(resp.Relationships["app"].Data.GUID, ShouldEqual, "305cea31-5a44-45ca-b51b-e89c7a8ef8b2") + }) + + Convey("Create V3 Deployment with droplet", t, func() { + body := `{"droplet":{"guid":"44ccfa61-dbcf-4a0d-82fe-f668e9d2a962"},"relationships":{"app":{"data":{"guid":"305cea31-5a44-45ca-b51b-e89c7a8ef8b2"}}}}` + setup(MockRoute{"POST", "/v3/deployments", []string{getV3DeploymentPayload}, "", http.StatusCreated, "", &body}, t) + + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + resp, err := client.CreateV3Deployment("305cea31-5a44-45ca-b51b-e89c7a8ef8b2", &CreateV3DeploymentOptionalParameters{ + Droplet: &V3Relationship{ + GUID: "44ccfa61-dbcf-4a0d-82fe-f668e9d2a962", + }, + }) + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + + So(resp.GUID, ShouldEqual, "59c3d133-2b83-46f3-960e-7765a129aea4") + So(resp.Status.Reason, ShouldEqual, "DEPLOYING") + So(resp.Relationships["app"].Data.GUID, ShouldEqual, "305cea31-5a44-45ca-b51b-e89c7a8ef8b2") + }) + + Convey("Create V3 Deployment with revision", t, func() { + body := `{"revision":{"guid":"56126cba-656a-4eba-a81e-7e9951b2df57","version":1},"relationships":{"app":{"data":{"guid":"305cea31-5a44-45ca-b51b-e89c7a8ef8b2"}}}}` + setup(MockRoute{"POST", "/v3/deployments", []string{getV3DeploymentPayload}, "", http.StatusCreated, "", &body}, t) + + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + resp, err := client.CreateV3Deployment("305cea31-5a44-45ca-b51b-e89c7a8ef8b2", &CreateV3DeploymentOptionalParameters{ + Revision: &V3DeploymentRevision{ + GUID: "56126cba-656a-4eba-a81e-7e9951b2df57", + Version: 1, + }, + }) + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + + So(resp.GUID, ShouldEqual, "59c3d133-2b83-46f3-960e-7765a129aea4") + So(resp.Status.Reason, ShouldEqual, "DEPLOYING") + So(resp.Relationships["app"].Data.GUID, ShouldEqual, "305cea31-5a44-45ca-b51b-e89c7a8ef8b2") + }) + + Convey("Create V3 Deployment with revision and droplet", t, func() { + body := `{"droplet":{"guid":"44ccfa61-dbcf-4a0d-82fe-f668e9d2a962"},"revision":{"guid":"56126cba-656a-4eba-a81e-7e9951b2df57","version":1},"relationships":{"app":{"data":{"guid":"305cea31-5a44-45ca-b51b-e89c7a8ef8b2"}}}}` + setup(MockRoute{"POST", "/v3/deployments", []string{getV3DeploymentPayload}, "", http.StatusCreated, "", &body}, t) + + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + resp, err := client.CreateV3Deployment("305cea31-5a44-45ca-b51b-e89c7a8ef8b2", &CreateV3DeploymentOptionalParameters{ + Droplet: &V3Relationship{ + GUID: "44ccfa61-dbcf-4a0d-82fe-f668e9d2a962", + }, + Revision: &V3DeploymentRevision{ + GUID: "56126cba-656a-4eba-a81e-7e9951b2df57", + Version: 1, + }, + }) + So(err, ShouldNotBeNil) + So(resp, ShouldBeNil) + }) +} + +func TestCancelV3Deployment(t *testing.T) { + Convey("Cancel V3 deployment", t, func() { + setup(MockRoute{"POST", "/v3/deployments/59c3d133-2b83-46f3-960e-7765a129aea4/actions/cancel", []string{""}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.CancelV3Deployment("59c3d133-2b83-46f3-960e-7765a129aea4") + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/v3domains.go b/third_party/go-cfclient/v3domains.go new file mode 100644 index 000000000000..16b76329953a --- /dev/null +++ b/third_party/go-cfclient/v3domains.go @@ -0,0 +1,68 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "time" + + "github.com/pkg/errors" +) + +type DomainRelationships struct { + Organization V3ToOneRelationship `json:"organization"` + SharedOrganizations V3ToManyRelationships `json:"shared_organizations"` +} + +type V3Domain struct { + Guid string `json:"guid"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Name string `json:"name"` + Internal bool `json:"internal"` + Metadata Metadata `json:"metadata"` + Relationships DomainRelationships `json:"relationships"` + Links map[string]Link `json:"links"` +} + +type listV3DomainsResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Domain `json:"resources,omitempty"` +} + +func (c *Client) ListV3Domains(query url.Values) ([]V3Domain, error) { + var domains []V3Domain + requestURL := "/v3/domains" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + resp, err := c.DoRequest(c.NewRequest("GET", requestURL)) + if err != nil { + return nil, errors.Wrapf(err, "Error getting domains") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 app domains, response code: %d", resp.StatusCode) + } + + var data listV3DomainsResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 app domains") + } + + domains = append(domains, data.Resources...) + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 domains") + } + } + return domains, nil +} diff --git a/third_party/go-cfclient/v3domains_test.go b/third_party/go-cfclient/v3domains_test.go new file mode 100644 index 000000000000..163c1849e0e0 --- /dev/null +++ b/third_party/go-cfclient/v3domains_test.go @@ -0,0 +1,35 @@ +package cfclient + +import ( + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListV3Domains(t *testing.T) { + Convey("List V3 Domains ", t, func() { + setup(MockRoute{"GET", "/v3/domains", []string{listV3DomainsPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + resp, err := client.ListV3Domains(nil) + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + + So(resp, ShouldHaveLength, 1) + So(resp[0].Name, ShouldEqual, "test-domain.com") + So(resp[0].Guid, ShouldEqual, "3a5d3d89-3f89-4f05-8188-8a2b298c79d5") + So(resp[0].Internal, ShouldEqual, false) + So(resp[0].Relationships.Organization.Data.GUID, ShouldEqual, "3a3f3d89-3f89-4f05-8188-751b298c79d5") + So(resp[0].Relationships.SharedOrganizations.Data[0].GUID, ShouldEqual, "404f3d89-3f89-6z72-8188-751b298d88d5") + So(resp[0].Relationships.SharedOrganizations.Data[1].GUID, ShouldEqual, "416d3d89-3f89-8h67-2189-123b298d3592") + So(resp[0].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5") + So(resp[0].Links["organization"].Href, ShouldEqual, "https://api.example.org/v3/organizations/3a3f3d89-3f89-4f05-8188-751b298c79d5") + So(resp[0].Links["route_reservations"].Href, ShouldEqual, "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/route_reservations") + So(resp[0].Links["shared_organizations"].Href, ShouldEqual, "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/relationships/shared_organizations") + }) +} diff --git a/third_party/go-cfclient/v3droplet.go b/third_party/go-cfclient/v3droplet.go new file mode 100644 index 000000000000..d3a453d3ffb6 --- /dev/null +++ b/third_party/go-cfclient/v3droplet.go @@ -0,0 +1,106 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/pkg/errors" +) + +// V3Droplet is the result of staging an application package. +// There are two types (lifecycles) of droplets: buildpack and +// docker. In the case of buildpacks, the droplet contains the +// bits produced by the buildpack. +type V3Droplet struct { + State string `json:"state,omitempty"` + Error string `json:"error,omitempty"` + Lifecycle V3Lifecycle `json:"lifecycle,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Links map[string]Link `json:"links,omitempty"` + ExecutionMetadata string `json:"execution_metadata,omitempty"` + ProcessTypes map[string]string `json:"process_types,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` + + // Only specified when the droplet is using the Docker lifecycle. + Image string `json:"image,omitempty"` + + // The following fields are specified when the droplet is using + // the buildpack lifecycle. + Checksum struct { + Type string `json:"type,omitempty"` + Value string `json:"value,omitempty"` + } `json:"checksum,omitempty"` + Stack string `json:"stack,omitempty"` + Buildpacks []V3DetectedBuildpack `json:"buildpacks,omitempty"` +} + +type V3DetectedBuildpack struct { + Name string `json:"name,omitempty"` // system buildpack name + BuildpackName string `json:"buildpack_name,omitempty"` // name reported by the buildpack + DetectOutput string `json:"detect_output,omitempty"` // output during detect process + Version string `json:"version,omitempty"` +} + +type CurrentDropletV3Response struct { + Data V3Relationship `json:"data,omitempty"` + Links map[string]Link `json:"links,omitempty"` +} + +func (c *Client) SetCurrentDropletForV3App(appGUID, dropletGUID string) (*CurrentDropletV3Response, error) { + req := c.NewRequest("PATCH", "/v3/apps/"+appGUID+"/relationships/current_droplet") + req.obj = V3ToOneRelationship{Data: V3Relationship{GUID: dropletGUID}} + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error setting droplet for v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error setting droplet for v3 app with GUID [%s], response code: %d", appGUID, resp.StatusCode) + } + + var r CurrentDropletV3Response + if err := json.NewDecoder(resp.Body).Decode(&r); err != nil { + return nil, errors.Wrap(err, "Error reading droplet response JSON") + } + + return &r, nil +} + +func (c *Client) GetCurrentDropletForV3App(appGUID string) (*V3Droplet, error) { + req := c.NewRequest("GET", "/v3/apps/"+appGUID+"/droplets/current") + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error getting droplet for v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting droplet for v3 app with GUID [%s], response code: %d", appGUID, resp.StatusCode) + } + + var r V3Droplet + if err := json.NewDecoder(resp.Body).Decode(&r); err != nil { + return nil, errors.Wrap(err, "Error reading droplet response JSON") + } + + return &r, nil +} + +func (c *Client) DeleteDroplet(dropletGUID string) error { + req := c.NewRequest("DELETE", "/v3/droplets/"+dropletGUID) + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrapf(err, "Error deleting droplet %s", dropletGUID) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("Error deleting droplet %s with response code %d", dropletGUID, resp.StatusCode) + } + + return nil +} diff --git a/third_party/go-cfclient/v3droplet_test.go b/third_party/go-cfclient/v3droplet_test.go new file mode 100644 index 000000000000..d6b7be570e8b --- /dev/null +++ b/third_party/go-cfclient/v3droplet_test.go @@ -0,0 +1,62 @@ +package cfclient + +import ( + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestSetCurrentDropletForV3App(t *testing.T) { + Convey("Set Droplet for V3 App", t, func() { + body := `{"data":{"guid":"3fc0916f-2cea-4f3a-ae53-048388baa6bd"}}` + setup(MockRoute{"PATCH", "/v3/apps/9d8e007c-ce52-4ea7-8a57-f2825d2c6b39/relationships/current_droplet", []string{currentDropletV3AppPayload}, "", http.StatusOK, "", &body}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + resp, err := client.SetCurrentDropletForV3App("9d8e007c-ce52-4ea7-8a57-f2825d2c6b39", "3fc0916f-2cea-4f3a-ae53-048388baa6bd") + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + + So(resp.Data.GUID, ShouldEqual, "9d8e007c-ce52-4ea7-8a57-f2825d2c6b39") + So(resp.Links["self"].Href, ShouldEqual, "https://api.example.org/v3/apps/d4c91047-7b29-4fda-b7f9-04033e5c9c9f/relationships/current_droplet") + So(resp.Links["related"].Href, ShouldEqual, "https://api.example.org/v3/apps/d4c91047-7b29-4fda-b7f9-04033e5c9c9f/droplets/current") + }) +} + +func TestGetCurrentDropletForV3App(t *testing.T) { + Convey("Get Droplet for V3 App", t, func() { + setup(MockRoute{"GET", "/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396/droplets/current", []string{getV3CurrentAppDropletPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + resp, err := client.GetCurrentDropletForV3App("7b34f1cf-7e73-428a-bb5a-8a17a8058396") + So(err, ShouldBeNil) + So(resp, ShouldNotBeNil) + + So(resp.GUID, ShouldEqual, "585bc3c1-3743-497d-88b0-403ad6b56d16") + So(resp.Links["self"].Href, ShouldEqual, "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16") + So(resp.Links["assign_current_droplet"].Href, ShouldEqual, "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396/relationships/current_droplet") + So(resp.Links["assign_current_droplet"].Method, ShouldEqual, "PATCH") + }) +} + +func TestDeleteDroplet(t *testing.T) { + Convey("Delete Droplet", t, func() { + setup(MockRoute{"DELETE", "/v3/droplets/59c3d133-2b83-46f3-960e-7765a129aea4", []string{""}, "", http.StatusAccepted, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteDroplet("59c3d133-2b83-46f3-960e-7765a129aea4") + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/v3organizations.go b/third_party/go-cfclient/v3organizations.go new file mode 100644 index 000000000000..6eb0d28ae0a4 --- /dev/null +++ b/third_party/go-cfclient/v3organizations.go @@ -0,0 +1,178 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type V3Organization struct { + Name string `json:"name,omitempty"` + GUID string `json:"guid,omitempty"` + Suspended *bool `json:"suspended,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +type CreateV3OrganizationRequest struct { + Name string + Suspended *bool `json:"suspended,omitempty"` + Metadata *V3Metadata +} + +type UpdateV3OrganizationRequest struct { + Name string + Suspended *bool `json:"suspended,omitempty"` + Metadata *V3Metadata +} + +func (c *Client) CreateV3Organization(r CreateV3OrganizationRequest) (*V3Organization, error) { + req := c.NewRequest("POST", "/v3/organizations") + params := map[string]interface{}{ + "name": r.Name, + } + if r.Suspended != nil { + params["suspended"] = r.Suspended + } + if r.Metadata != nil { + params["metadata"] = r.Metadata + } + + req.obj = params + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 organization") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating v3 organization %s, response code: %d", r.Name, resp.StatusCode) + } + + var organization V3Organization + if err := json.NewDecoder(resp.Body).Decode(&organization); err != nil { + return nil, errors.Wrap(err, "Error reading v3 organization JSON") + } + + return &organization, nil +} + +func (c *Client) GetV3OrganizationByGUID(organizationGUID string) (*V3Organization, error) { + req := c.NewRequest("GET", "/v3/organizations/"+organizationGUID) + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while getting v3 organization") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting v3 organization with GUID [%s], response code: %d", organizationGUID, resp.StatusCode) + } + + var organization V3Organization + if err := json.NewDecoder(resp.Body).Decode(&organization); err != nil { + return nil, errors.Wrap(err, "Error reading v3 organization JSON") + } + + return &organization, nil +} + +func (c *Client) DeleteV3Organization(organizationGUID string) error { + req := c.NewRequest("DELETE", "/v3/organizations/"+organizationGUID) + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error while deleting v3 organization") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("Error deleting v3 organization with GUID [%s], response code: %d", organizationGUID, resp.StatusCode) + } + + return nil +} + +func (c *Client) UpdateV3Organization(organizationGUID string, r UpdateV3OrganizationRequest) (*V3Organization, error) { + req := c.NewRequest("PATCH", "/v3/organizations/"+organizationGUID) + params := make(map[string]interface{}) + if r.Name != "" { + params["name"] = r.Name + } + if r.Suspended != nil { + params["suspended"] = r.Suspended + } + if r.Metadata != nil { + params["metadata"] = r.Metadata + } + if len(params) > 0 { + req.obj = params + } + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while updating v3 organization") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error updating v3 organization %s, response code: %d", organizationGUID, resp.StatusCode) + } + + var organization V3Organization + if err := json.NewDecoder(resp.Body).Decode(&organization); err != nil { + return nil, errors.Wrap(err, "Error reading v3 organization JSON") + } + + return &organization, nil +} + +type listV3OrganizationsResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Organization `json:"resources,omitempty"` +} + +func (c *Client) ListV3OrganizationsByQuery(query url.Values) ([]V3Organization, error) { + var organizations []V3Organization + requestURL := "/v3/organizations" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 organizations") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 organizations, response code: %d", resp.StatusCode) + } + + var data listV3OrganizationsResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 organizations") + } + + organizations = append(organizations, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 organizations") + } + } + + return organizations, nil +} diff --git a/third_party/go-cfclient/v3organizations_test.go b/third_party/go-cfclient/v3organizations_test.go new file mode 100644 index 000000000000..b6e2f89e74b7 --- /dev/null +++ b/third_party/go-cfclient/v3organizations_test.go @@ -0,0 +1,117 @@ +package cfclient + +import ( + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestCreateV3Organization(t *testing.T) { + Convey("Create V3 Organization", t, func() { + expectedBody := `{"name":"my-org"}` + setup(MockRoute{"POST", "/v3/organizations", []string{createV3OrganizationPayload}, "", http.StatusCreated, "", &expectedBody}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + organization, err := client.CreateV3Organization(CreateV3OrganizationRequest{ + Name: "my-org", + }) + So(err, ShouldBeNil) + So(organization, ShouldNotBeNil) + + So(organization.GUID, ShouldEqual, "org-guid") + So(organization.Relationships["quota"].Data.GUID, ShouldEqual, "quota-guid") + So(organization.Links["domains"].Href, ShouldEqual, "https://api.example.org/v3/organizations/org-guid/domains") + So(organization.Metadata.Annotations, ShouldHaveLength, 0) + So(organization.Metadata.Labels, ShouldContainKey, "ORG_KEY") + So(organization.Metadata.Labels["ORG_KEY"], ShouldEqual, "org_value") + }) +} + +func TestGetV3Organization(t *testing.T) { + Convey("Get V3 Organization", t, func() { + setup(MockRoute{"GET", "/v3/organizations/org-guid", []string{getV3OrganizationPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + organization, err := client.GetV3OrganizationByGUID("org-guid") + So(err, ShouldBeNil) + So(organization, ShouldNotBeNil) + + So(organization.GUID, ShouldEqual, "org-guid") + So(organization.Relationships["quota"].Data.GUID, ShouldEqual, "quota-guid") + So(organization.Links["domains"].Href, ShouldEqual, "https://api.example.org/v3/organizations/org-guid/domains") + So(organization.Metadata.Annotations, ShouldHaveLength, 0) + So(organization.Metadata.Labels, ShouldContainKey, "ORG_KEY") + So(organization.Metadata.Labels["ORG_KEY"], ShouldEqual, "org_value") + }) +} + +func TestDeleteV3Organization(t *testing.T) { + Convey("Delete V3 Organization", t, func() { + setup(MockRoute{"DELETE", "/v3/organizations/org-guid", []string{""}, "", http.StatusAccepted, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteV3Organization("org-guid") + So(err, ShouldBeNil) + }) +} + +func TestUpdateV3Organization(t *testing.T) { + Convey("Update V3 Organization", t, func() { + setup(MockRoute{"PATCH", "/v3/organizations/org-guid", []string{updateV3OrganizationPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + organization, err := client.UpdateV3Organization("org-guid", UpdateV3OrganizationRequest{ + Name: "my-org", + }) + So(err, ShouldBeNil) + So(organization, ShouldNotBeNil) + + So(organization.Name, ShouldEqual, "my-org") + So(organization.GUID, ShouldEqual, "org-guid") + So(organization.Relationships["quota"].Data.GUID, ShouldEqual, "") + So(organization.Links["domains"].Href, ShouldEqual, "https://api.example.org/v3/organizations/org-guid/domains") + So(organization.Metadata.Annotations, ShouldHaveLength, 0) + So(organization.Metadata.Labels, ShouldContainKey, "ORG_KEY") + So(organization.Metadata.Labels["ORG_KEY"], ShouldEqual, "org_value") + }) +} + +func TestListV3OrganizationsByQuery(t *testing.T) { + Convey("List V3 Organizations", t, func() { + setup(MockRoute{"GET", "/v3/organizations", []string{listV3OrganizationsPayload, listV3OrganizationsPayloadPage2}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + organizations, err := client.ListV3OrganizationsByQuery(nil) + So(err, ShouldBeNil) + So(organizations, ShouldHaveLength, 2) + + So(organizations[0].Name, ShouldEqual, "my-org-1") + So(organizations[1].Name, ShouldEqual, "my-org-2") + + So(organizations[0].Relationships["quota"].Data.GUID, ShouldEqual, "quota-guid") + So(organizations[0].Links["domains"].Href, ShouldEqual, "https://api.example.org/v3/organizations/org-guid/domains") + So(organizations[1].Relationships["quota"].Data.GUID, ShouldEqual, "") + So(organizations[1].Links["domains"].Href, ShouldEqual, "https://api.example.org/v3/organizations/org-guid-2/domains") + }) +} diff --git a/third_party/go-cfclient/v3packages.go b/third_party/go-cfclient/v3packages.go new file mode 100644 index 000000000000..91905a8a0b41 --- /dev/null +++ b/third_party/go-cfclient/v3packages.go @@ -0,0 +1,192 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type V3PackageState string + +const ( + AwaitingUpload V3PackageState = "AWAITING_UPLOAD" + ProcessingUpload V3PackageState = "PROCESSING_UPLOAD" + Ready V3PackageState = "READY" + Failed V3PackageState = "FAILED" + Copying V3PackageState = "COPYING" + Expired V3PackageState = "EXPIRED" +) + +type V3Package struct { + Type string `json:"type,omitempty"` // bits or docker + Data json.RawMessage `json:"data,omitempty"` // depends on value of Type + State V3PackageState `json:"state,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +func (v *V3Package) BitsData() (V3BitsPackage, error) { + var bits V3BitsPackage + if v.Type != "bits" { + return bits, errors.New("this package is not of type bits") + } + + if err := json.Unmarshal(v.Data, &bits); err != nil { + return bits, err + } + + return bits, nil +} + +func (v *V3Package) DockerData() (V3DockerPackage, error) { + var docker V3DockerPackage + if v.Type != "docker" { + return docker, errors.New("this package is not of type docker") + } + + if err := json.Unmarshal(v.Data, &docker); err != nil { + return docker, err + } + + return docker, nil +} + +// V3BitsPackage is the data for V3Packages of type bits. +// It provides an upload link to which a zip file should be uploaded. +type V3BitsPackage struct { + Error string `json:"error,omitempty"` + Checksum struct { + Type string `json:"type,omitempty"` // eg. sha256 + Value string `json:"value,omitempty"` // populated after the bits are uploaded + } `json:"checksum,omitempty"` +} + +// V3DockerPackage is the data for V3Packages of type docker. +// It references a docker image from a registry. +type V3DockerPackage struct { + Image string `json:"image,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` +} + +type listV3PackagesResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Package `json:"resources,omitempty"` +} + +func (c *Client) ListPackagesForAppV3(appGUID string, query url.Values) ([]V3Package, error) { + var packages []V3Package + requestURL := "/v3/apps/" + appGUID + "/packages" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + resp, err := c.DoRequest(c.NewRequest("GET", requestURL)) + if err != nil { + return nil, errors.Wrapf(err, "Error requesting packages for app %s", appGUID) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 app packages, response code: %d", resp.StatusCode) + } + + var data listV3PackagesResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 app packages") + } + + packages = append(packages, data.Resources...) + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 packages") + } + } + return packages, nil +} + +// CopyPackageV3 makes a copy of a package that is associated with one app +// and associates the copy with a new app. +func (c *Client) CopyPackageV3(packageGUID, appGUID string) (*V3Package, error) { + req := c.NewRequest("POST", "/v3/packages?source_guid="+packageGUID) + req.obj = map[string]interface{}{ + "relationships": map[string]interface{}{ + "app": V3ToOneRelationship{ + Data: V3Relationship{ + GUID: appGUID, + }, + }, + }, + } + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while copying v3 package") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error copying v3 package %s, response code: %d", packageGUID, resp.StatusCode) + } + + var pkg V3Package + if err := json.NewDecoder(resp.Body).Decode(&pkg); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app package") + } + + return &pkg, nil +} + +type v3DockerPackageData struct { + Image string `json:"image"` + *DockerCredentials +} + +type createV3DockerPackageRequest struct { + Type string `json:"type"` + Relationships map[string]V3ToOneRelationship `json:"relationships"` + Data v3DockerPackageData `json:"data"` +} + +// CreateV3DockerPackage creates a Docker package +func (c *Client) CreateV3DockerPackage(image string, appGUID string, dockerCredentials *DockerCredentials) (*V3Package, error) { + req := c.NewRequest("POST", "/v3/packages") + req.obj = createV3DockerPackageRequest{ + Type: "docker", + Relationships: map[string]V3ToOneRelationship{ + "app": {Data: V3Relationship{GUID: appGUID}}, + }, + Data: v3DockerPackageData{ + Image: image, + DockerCredentials: dockerCredentials, + }, + } + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while copying v3 package") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("error creating v3 docker package, response code: %d", resp.StatusCode) + } + + var pkg V3Package + if err := json.NewDecoder(resp.Body).Decode(&pkg); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app package") + } + + return &pkg, nil +} diff --git a/third_party/go-cfclient/v3packages_test.go b/third_party/go-cfclient/v3packages_test.go new file mode 100644 index 000000000000..58f7a50d7fa0 --- /dev/null +++ b/third_party/go-cfclient/v3packages_test.go @@ -0,0 +1,100 @@ +package cfclient + +import ( + "encoding/json" + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListPackagesForAppV3(t *testing.T) { + Convey("List Package for V3 Apps", t, func() { + setup(MockRoute{"GET", "/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69/packages", []string{listPackagesForV3AppPayloadPage1, listPackagesForV3AppPayloadPage2}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + packages, err := client.ListPackagesForAppV3("f2efe391-2b5b-4836-8518-ad93fa9ebf69", nil) + So(err, ShouldBeNil) + So(packages, ShouldHaveLength, 2) + + So(packages[0].Type, ShouldEqual, "bits") + So(packages[0].State, ShouldEqual, "READY") + So(packages[0].Links["app"].Href, ShouldEqual, "https://api.example.org/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69") + So(packages[0].Links["download"].Href, ShouldEqual, "https://api.example.org/v3/packages/752edab0-2147-4f58-9c25-cd72ad8c3561/download") + So(packages[1].Type, ShouldEqual, "bits") + So(packages[1].State, ShouldEqual, "READY") + So(packages[1].Links["app"].Href, ShouldEqual, "https://api.example.org/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69") + So(packages[1].Links["download"].Href, ShouldEqual, "https://api.example.org/v3/packages/2345ab-2147-4f58-9c25-cd72ad8c3561/download") + + }) +} + +func TestCopyPackageV3(t *testing.T) { + Convey("Copy V3 Package", t, func() { + expectedBody := `{"relationships":{"app":{"data":{"guid":"app-guid"}}}}` + setup(MockRoute{"POST", "/v3/packages", []string{copyPackageV3Payload}, "", http.StatusCreated, "source_guid=package-guid", &expectedBody}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + pkg, err := client.CopyPackageV3("package-guid", "app-guid") + So(err, ShouldBeNil) + + So(pkg.State, ShouldEqual, "COPYING") + So(pkg.Type, ShouldEqual, "docker") + So(pkg.GUID, ShouldEqual, "fec72fc1-e453-4463-a86d-5df426f337a3") + + docker, err := pkg.DockerData() + So(err, ShouldBeNil) + So(docker.Image, ShouldEqual, "http://awesome-sauce.example.org") + }) +} + +func TestV3PackageDataDocker(t *testing.T) { + Convey("V3 Package Data [type=docker]", t, func() { + Convey("Errors when type=bits", func() { + p := V3Package{Type: "bits"} + _, err := p.DockerData() + So(err, ShouldNotBeNil) + }) + + Convey("Unmarshals docker package", func() { + p := V3Package{ + Type: "docker", + Data: json.RawMessage(`{"image":"nginx","username":"admin","password":"password"}`), + } + d, err := p.DockerData() + So(err, ShouldBeNil) + So(d.Image, ShouldEqual, "nginx") + So(d.Username, ShouldEqual, "admin") + So(d.Password, ShouldEqual, "password") + }) + }) +} + +func TestV3PackageDataBits(t *testing.T) { + Convey("V3 Package Data [type=bits]", t, func() { + Convey("Errors when type=docker", func() { + p := V3Package{Type: "docker"} + _, err := p.BitsData() + So(err, ShouldNotBeNil) + }) + + Convey("Unmarshals docker package", func() { + p := V3Package{ + Type: "bits", + Data: json.RawMessage(`{"error":"None","checksum":{"type":"sha256","value":"foo"}}`), + } + b, err := p.BitsData() + So(err, ShouldBeNil) + So(b.Error, ShouldEqual, "None") + So(b.Checksum.Type, ShouldEqual, "sha256") + }) + }) +} diff --git a/third_party/go-cfclient/v3roles.go b/third_party/go-cfclient/v3roles.go new file mode 100644 index 000000000000..dce289f2660b --- /dev/null +++ b/third_party/go-cfclient/v3roles.go @@ -0,0 +1,273 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +// V3Role implements role object. Roles control access to resources in organizations and spaces. Roles are assigned to users. +type V3Role struct { + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Type string `json:"type,omitempty"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Links map[string]Link `json:"links,omitempty"` +} + +type Included struct { + Users []V3User `json:"users,omitempty"` + Organizations []V3Organization `json:"organizations,omitempty"` + Spaces []V3Space `json:"spaces,omitempty"` +} + +type listV3RolesResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Role `json:"resources,omitempty"` + Included Included `json:"included,omitempty"` +} + +type createV3SpaceRoleRequest struct { + RoleType string `json:"type"` + Relationships spaceUserRelationships `json:"relationships"` +} + +type createV3OrganizationRoleRequest struct { + RoleType string `json:"type"` + Relationships orgUserRelationships `json:"relationships"` +} + +type spaceUserRelationships struct { + Space V3ToOneRelationship `json:"space"` + User V3ToOneRelationship `json:"user"` +} + +type orgUserRelationships struct { + Org V3ToOneRelationship `json:"organization"` + User V3ToOneRelationship `json:"user"` +} + +func (c *Client) CreateV3SpaceRole(spaceGUID, userGUID, roleType string) (*V3Role, error) { + spaceRel := V3ToOneRelationship{Data: V3Relationship{GUID: spaceGUID}} + userRel := V3ToOneRelationship{Data: V3Relationship{GUID: userGUID}} + req := c.NewRequest("POST", "/v3/roles") + req.obj = createV3SpaceRoleRequest{ + RoleType: roleType, + Relationships: spaceUserRelationships{Space: spaceRel, User: userRel}, + } + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 role") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("error creating v3 role, response code: %d", resp.StatusCode) + } + + var role V3Role + if err := json.NewDecoder(resp.Body).Decode(&role); err != nil { + return nil, errors.Wrap(err, "Error reading v3 role") + } + + return &role, nil +} + +func (c *Client) CreateV3OrganizationRole(orgGUID, userGUID, roleType string) (*V3Role, error) { + orgRel := V3ToOneRelationship{Data: V3Relationship{GUID: orgGUID}} + userRel := V3ToOneRelationship{Data: V3Relationship{GUID: userGUID}} + req := c.NewRequest("POST", "/v3/roles") + req.obj = createV3OrganizationRoleRequest{ + RoleType: roleType, + Relationships: orgUserRelationships{Org: orgRel, User: userRel}, + } + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 role") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("error creating v3 role, response code: %d", resp.StatusCode) + } + + var role V3Role + if err := json.NewDecoder(resp.Body).Decode(&role); err != nil { + return nil, errors.Wrap(err, "Error reading v3 role") + } + + return &role, nil +} + +// ListV3RolesByQuery retrieves roles based on query +func (c *Client) ListV3RolesByQuery(query url.Values) ([]V3Role, error) { + var roles []V3Role + requestURL, err := url.Parse("/v3/roles") + if err != nil { + return nil, err + } + requestURL.RawQuery = query.Encode() + + for { + r := c.NewRequest("GET", fmt.Sprintf("%s?%s", requestURL.Path, requestURL.RawQuery)) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 space roles") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 space roles, response code: %d", resp.StatusCode) + } + + var data listV3RolesResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 space roles") + } + + roles = append(roles, data.Resources...) + + requestURL, err = url.Parse(data.Pagination.Next.Href) + if err != nil { + return nil, errors.Wrap(err, "Error parsing next page URL") + } + if requestURL.String() == "" { + break + } + } + + return roles, nil +} + +func (c *Client) ListV3RoleUsersByQuery(query url.Values) ([]V3User, error) { + var users []V3User + requestURL, err := url.Parse("/v3/roles") + if err != nil { + return nil, err + } + requestURL.RawQuery = query.Encode() + + for { + r := c.NewRequest("GET", fmt.Sprintf("%s?%s", requestURL.Path, requestURL.RawQuery)) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 roles") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 roles, response code: %d", resp.StatusCode) + } + + var data listV3RolesResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 roles") + } + + users = append(users, data.Included.Users...) + + requestURL, err = url.Parse(data.Pagination.Next.Href) + if err != nil { + return nil, errors.Wrap(err, "Error parsing next page URL") + } + if requestURL.String() == "" { + break + } + } + + return users, nil +} + +func (c *Client) ListV3RoleAndUsersByQuery(query url.Values) ([]V3Role, []V3User, error) { + var roles []V3Role + var users []V3User + requestURL, err := url.Parse("/v3/roles") + if err != nil { + return nil, nil, err + } + requestURL.RawQuery = query.Encode() + + for { + r := c.NewRequest("GET", fmt.Sprintf("%s?%s", requestURL.Path, requestURL.RawQuery)) + resp, err := c.DoRequest(r) + if err != nil { + return nil, nil, errors.Wrap(err, "Error requesting v3 roles") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, nil, fmt.Errorf("Error listing v3 roles, response code: %d", resp.StatusCode) + } + + var data listV3RolesResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, nil, errors.Wrap(err, "Error parsing JSON from list v3 roles") + } + + roles = append(roles, data.Resources...) + users = append(users, data.Included.Users...) + + requestURL, err = url.Parse(data.Pagination.Next.Href) + if err != nil { + return nil, nil, errors.Wrap(err, "Error parsing next page URL") + } + if requestURL.String() == "" { + break + } + } + + return roles, users, nil +} + +// ListV3SpaceRolesByGUID retrieves roles based on query +func (c *Client) ListV3SpaceRolesByGUID(spaceGUID string) ([]V3Role, []V3User, error) { + query := url.Values{} + query["space_guids"] = []string{spaceGUID} + query["include"] = []string{"user"} + return c.ListV3RoleAndUsersByQuery(query) +} + +// ListV3SpaceRolesByGUIDAndType retrieves roles based on query +func (c *Client) ListV3SpaceRolesByGUIDAndType(spaceGUID string, roleType string) ([]V3User, error) { + query := url.Values{} + query["space_guids"] = []string{spaceGUID} + query["types"] = []string{roleType} + query["include"] = []string{"user"} + return c.ListV3RoleUsersByQuery(query) +} + +// ListV3SpaceRolesByGUIDAndType retrieves roles based on query +func (c *Client) ListV3OrganizationRolesByGUIDAndType(orgGUID string, roleType string) ([]V3User, error) { + query := url.Values{} + query["organization_guids"] = []string{orgGUID} + query["types"] = []string{roleType} + query["include"] = []string{"user"} + return c.ListV3RoleUsersByQuery(query) +} + +// ListV3OrganizationRolesByGUID retrieves roles based on query +func (c *Client) ListV3OrganizationRolesByGUID(orgGUID string) ([]V3Role, []V3User, error) { + query := url.Values{} + query["organization_guids"] = []string{orgGUID} + query["include"] = []string{"user"} + return c.ListV3RoleAndUsersByQuery(query) +} + +func (c *Client) DeleteV3Role(roleGUID string) error { + req := c.NewRequest("DELETE", "/v3/roles/"+roleGUID) + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error while deleting v3 role") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("Error deleting v3 role with GUID [%s], response code: %d", roleGUID, resp.StatusCode) + } + + return nil +} diff --git a/third_party/go-cfclient/v3roles_test.go b/third_party/go-cfclient/v3roles_test.go new file mode 100644 index 000000000000..91c60717479b --- /dev/null +++ b/third_party/go-cfclient/v3roles_test.go @@ -0,0 +1,255 @@ +package cfclient + +import ( + "net/http" + "net/url" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListV3SpaceRolesByQuery(t *testing.T) { + Convey("List V3 Space Roles By Space GUID", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/roles", []string{listV3SpaceRolesBySpaceGUIDPayload}, "", http.StatusOK, "space_guids=spaceGUID1", nil}, + {"GET", "/v3/rolespage2", []string{listV3SpaceRolesBySpaceGuidPayloadPage2}, "", http.StatusOK, "page=2&per_page=2&space_guids=spaceGUID1", nil}, + } + setupMultiple(mocks, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + query := url.Values{} + query["space_guids"] = []string{"spaceGUID1"} + + roles, err := client.ListV3RolesByQuery(query) + So(err, ShouldBeNil) + So(roles, ShouldHaveLength, 3) + + So(roles[0].Type, ShouldEqual, "space_developer") + So(roles[1].Type, ShouldEqual, "space_auditor") + So(roles[2].Type, ShouldEqual, "space_manager") + + So(roles[0].GUID, ShouldEqual, "roleGUID1") + So(roles[0].Relationships["user"].Data.GUID, ShouldEqual, "userGUID1") + So(roles[0].Relationships["space"].Data.GUID, ShouldEqual, "spaceGUID1") + So(roles[0].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/roles/roleGUID1") + So(roles[1].GUID, ShouldEqual, "roleGUID2") + So(roles[1].Relationships["user"].Data.GUID, ShouldEqual, "userGUID2") + So(roles[1].Relationships["space"].Data.GUID, ShouldEqual, "spaceGUID1") + So(roles[1].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/roles/roleGUID2") + So(roles[2].GUID, ShouldEqual, "roleGUID3") + So(roles[2].Relationships["user"].Data.GUID, ShouldEqual, "userGUID2") + So(roles[2].Relationships["space"].Data.GUID, ShouldEqual, "spaceGUID1") + So(roles[2].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/roles/roleGUID3") + }) + + Convey("List V3 Space Roles By User GUID", t, func() { + setup(MockRoute{"GET", "/v3/roles", []string{listV3SpaceRolesByUserGuidPayload}, "", http.StatusOK, "user_guids=userGUID1", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + query := url.Values{} + query["user_guids"] = []string{"userGUID1"} + + roles, err := client.ListV3RolesByQuery(query) + So(err, ShouldBeNil) + So(roles, ShouldHaveLength, 2) + + So(roles[0].Type, ShouldEqual, "space_developer") + So(roles[1].Type, ShouldEqual, "space_manager") + + So(roles[0].GUID, ShouldEqual, "roleGUID1") + So(roles[0].Relationships["user"].Data.GUID, ShouldEqual, "userGUID1") + So(roles[0].Relationships["space"].Data.GUID, ShouldEqual, "spaceGUID1") + So(roles[0].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/roles/roleGUID1") + So(roles[1].GUID, ShouldEqual, "roleGUID4") + So(roles[1].Relationships["user"].Data.GUID, ShouldEqual, "userGUID1") + So(roles[1].Relationships["space"].Data.GUID, ShouldEqual, "spaceGUID2") + So(roles[1].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/roles/roleGUID4") + }) + + Convey("List V3 Space Users By Space Guid and User GUID", t, func() { + setup(MockRoute{"GET", "/v3/roles", []string{listV3spaceRolesBySpaceAndUserGuidPayload}, "", http.StatusOK, "space_guids=spaceGUID2&user_guids=userGUID1", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + query := url.Values{} + query["space_guids"] = []string{"spaceGUID2"} + query["user_guids"] = []string{"userGUID1"} + + roles, err := client.ListV3RolesByQuery(query) + So(err, ShouldBeNil) + So(roles, ShouldHaveLength, 1) + + So(roles[0].Type, ShouldEqual, "space_manager") + + So(roles[0].GUID, ShouldEqual, "roleGUID4") + So(roles[0].Relationships["user"].Data.GUID, ShouldEqual, "userGUID1") + So(roles[0].Relationships["space"].Data.GUID, ShouldEqual, "spaceGUID2") + So(roles[0].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/roles/roleGUID4") + }) + +} + +func TestListV3SpaceRolesByGUID(t *testing.T) { + Convey("List V3 Space Roles By Space GUID and type", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/roles", []string{listV3SpaceRoleUsersBySpaceGUIDPayload}, "", http.StatusOK, "include=user&space_guids=spaceGUID1", nil}, + {"GET", "/v3/rolespage2", []string{listV3SpaceRoleUsersBySpaceGUIDPayloadPage2}, "", http.StatusOK, "page=2&per_page=2&include=user&space_guids=spaceGUID1", nil}, + } + setupMultiple(mocks, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + roles, users, err := client.ListV3SpaceRolesByGUID("spaceGUID1") + So(err, ShouldBeNil) + So(roles, ShouldHaveLength, 3) + So(users, ShouldHaveLength, 3) + + So(users[0].Username, ShouldEqual, "user1") + So(users[1].Username, ShouldEqual, "user2") + So(users[2].Username, ShouldEqual, "user3") + }) +} + +func TestListV3SpaceRolesByGUIDAndType(t *testing.T) { + Convey("List V3 Space Roles By Space GUID and type", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/roles", []string{listV3SpaceRolesBySpaceGUIDAndTypePayload}, "", http.StatusOK, "include=user&space_guids=spaceGUID1&types=space_supporter", nil}, + {"GET", "/v3/rolespage2", []string{listV3SpaceRolesBySpaceGuidAndTypePayloadPage2}, "", http.StatusOK, "page=2&per_page=2&include=user&space_guids=spaceGUID1&types=space_supporter", nil}, + } + setupMultiple(mocks, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + users, err := client.ListV3SpaceRolesByGUIDAndType("spaceGUID1", "space_supporter") + So(err, ShouldBeNil) + So(users, ShouldHaveLength, 3) + + So(users[0].Username, ShouldEqual, "user1") + So(users[1].Username, ShouldEqual, "user2") + So(users[2].Username, ShouldEqual, "user3") + }) +} + +func TestListV3OrgRolesByGUID(t *testing.T) { + Convey("List V3 Org Roles By Org GUID and type", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/roles", []string{listV3OrganizationRolesByOrganizationGUIDPayload}, "", http.StatusOK, "include=user&organization_guids=orgGUID1", nil}, + {"GET", "/v3/rolespage2", []string{listV3OrganizationRolesByOrganizationGuidPayloadPage2}, "", http.StatusOK, "page=2&per_page=2&include=user&organization_guids=orgGUID1", nil}, + } + setupMultiple(mocks, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + roles, users, err := client.ListV3OrganizationRolesByGUID("orgGUID1") + So(err, ShouldBeNil) + So(roles, ShouldHaveLength, 3) + So(users, ShouldHaveLength, 3) + + So(users[0].Username, ShouldEqual, "user1") + So(users[1].Username, ShouldEqual, "user2") + So(users[2].Username, ShouldEqual, "user3") + }) +} + +func TestListV3OrgRolesByGUIDAndType(t *testing.T) { + Convey("List V3 Org Roles By Org GUID and type", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/roles", []string{listV3OrganizationRolesByOrganizationGUIDAndTypePayload}, "", http.StatusOK, "include=user&organization_guids=orgGUID1&types=organization_auditor", nil}, + {"GET", "/v3/rolespage2", []string{listV3OrganizationRolesByOrganizationGuidAndTypePayloadPage2}, "", http.StatusOK, "page=2&per_page=2&include=user&organization_guids=orgGUID1&types=organization_auditor", nil}, + } + setupMultiple(mocks, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + users, err := client.ListV3OrganizationRolesByGUIDAndType("orgGUID1", "organization_auditor") + So(err, ShouldBeNil) + So(users, ShouldHaveLength, 3) + + So(users[0].Username, ShouldEqual, "user1") + So(users[1].Username, ShouldEqual, "user2") + So(users[2].Username, ShouldEqual, "user3") + }) +} + +func TestCreateV3SpaceRoles(t *testing.T) { + Convey("Create V3 Space Role", t, func() { + setup(MockRoute{"POST", "/v3/roles", []string{createV3SpaceRolePayload}, "", http.StatusCreated, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + role, err := client.CreateV3SpaceRole( + "b40a40c8-58b7-49a0-b47d-9d6fe5d72905", + "c4958204-6b65-43ea-832b-e4c57aea6641", + "space_supporter", + ) + So(err, ShouldBeNil) + So(role.Type, ShouldEqual, "space_supporter") + So(role.GUID, ShouldEqual, "b9f59ab2-2b09-438e-bebb-30e8704ffb89") + So(role.Links["self"].Href, ShouldEqual, "https://api.example.org/v3/roles/b9f59ab2-2b09-438e-bebb-30e8704ffb89") + So(role.Links["user"].Href, ShouldEqual, "https://api.example.org/v3/users/c4958204-6b65-43ea-832b-e4c57aea6641") + So(role.Links["space"].Href, ShouldEqual, "https://api.example.org/v3/spaces/b40a40c8-58b7-49a0-b47d-9d6fe5d72905") + }) +} + +func TestCreateV3OrgRoles(t *testing.T) { + Convey("Create V3 Org Role", t, func() { + setup(MockRoute{"POST", "/v3/roles", []string{createV3OrganizationRolePayload}, "", http.StatusCreated, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + role, err := client.CreateV3OrganizationRole( + "fa8a8346-0d92-4729-870c-77ee1934f973", + "ac2e02c9-2c5c-4712-a620-a68449d263c3", + "organization_user", + ) + So(err, ShouldBeNil) + So(role.Type, ShouldEqual, "organization_user") + So(role.GUID, ShouldEqual, "21cbfaeb-bff7-4cfd-a7a9-6c13ec76f246") + So(role.Links["self"].Href, ShouldEqual, "https://api.example.org/v3/roles/21cbfaeb-bff7-4cfd-a7a9-6c13ec76f246") + So(role.Links["user"].Href, ShouldEqual, "https://api.example.org/v3/users/ac2e02c9-2c5c-4712-a620-a68449d263c3") + So(role.Links["organization"].Href, ShouldEqual, "https://api.example.org/v3/organizations/fa8a8346-0d92-4729-870c-77ee1934f973") + }) +} + +func TestDeleteV3Role(t *testing.T) { + Convey("Delete V3 Role", t, func() { + setup(MockRoute{"DELETE", "/v3/roles/1cb006ee-fb05-47e1-b541-c34179ddc446", []string{""}, "", http.StatusAccepted, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteV3Role("1cb006ee-fb05-47e1-b541-c34179ddc446") + So(err, ShouldBeNil) + }) +} diff --git a/third_party/go-cfclient/v3routes.go b/third_party/go-cfclient/v3routes.go new file mode 100644 index 000000000000..fe16f22e2058 --- /dev/null +++ b/third_party/go-cfclient/v3routes.go @@ -0,0 +1,133 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "time" + + "github.com/pkg/errors" +) + +type V3Route struct { + Guid string `json:"guid"` + Host string `json:"host"` + Path string `json:"path"` + Url string `json:"url"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Metadata Metadata `json:"metadata"` + Destinations []Destination `json:"destinations"` + Relationships map[string]V3ToOneRelationship `json:"relationships"` + Links map[string]Link `json:"links"` +} + +type listV3RouteResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Route `json:"resources,omitempty"` +} + +type Destination struct { + GUID string `json:"guid"` + App struct { + GUID string `json:"guid"` + Process struct { + Type string `json:"type"` + } `json:"process"` + } `json:"app"` + Weight interface{} `json:"weight"` + Port int `json:"port"` + Protocol string `json:"protocol"` +} + +func (c *Client) ListV3Routes() ([]V3Route, error) { + return c.ListV3RoutesByQuery(nil) +} + +func (c *Client) ListV3RoutesByQuery(query url.Values) ([]V3Route, error) { + var routes []V3Route + requestURL := "/v3/routes" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 service instances") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error listing v3 service instances, response code: %d", resp.StatusCode) + } + + var data listV3RouteResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 service instances") + } + + routes = append(routes, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 service instances") + } + } + + return routes, nil +} + +type CreateV3RouteOptionalParameters struct { + Host string `json:"host,omitempty"` + Path string `json:"path,omitempty"` + Metadata Metadata `json:"metadata,omitempty"` +} + +type routeRelationships struct { + Space V3ToOneRelationship `json:"space"` + Domain V3ToOneRelationship `json:"domain"` +} + +type createV3RouteRequest struct { + Relationships routeRelationships `json:"relationships"` + *CreateV3RouteOptionalParameters +} + +func (c *Client) CreateV3Route( + spaceGUID string, + domainGUID string, + opt *CreateV3RouteOptionalParameters, +) (*V3Route, error) { + + spaceRel := V3ToOneRelationship{Data: V3Relationship{GUID: spaceGUID}} + domainRel := V3ToOneRelationship{Data: V3Relationship{GUID: domainGUID}} + + req := c.NewRequest("POST", "/v3/routes") + req.obj = createV3RouteRequest{ + Relationships: routeRelationships{Space: spaceRel, Domain: domainRel}, + CreateV3RouteOptionalParameters: opt, + } + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 route") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("error creating v3 route, response code: %d", resp.StatusCode) + } + + var route V3Route + if err := json.NewDecoder(resp.Body).Decode(&route); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app package") + } + + return &route, nil +} diff --git a/third_party/go-cfclient/v3routes_test.go b/third_party/go-cfclient/v3routes_test.go new file mode 100644 index 000000000000..8afd8f495851 --- /dev/null +++ b/third_party/go-cfclient/v3routes_test.go @@ -0,0 +1,61 @@ +package cfclient + +import ( + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListV3Routes(t *testing.T) { + Convey("List V3 Routes", t, func() { + setup(MockRoute{"GET", "/v3/routes", []string{listV3RoutesPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + routes, err := client.ListV3Routes() + So(err, ShouldBeNil) + So(routes, ShouldHaveLength, 1) + + So(routes[0].Host, ShouldEqual, "a-hostname") + So(routes[0].Path, ShouldEqual, "/some_path") + So(routes[0].Url, ShouldEqual, "a-hostname.a-domain.com/some_path") + + So(routes[0].Relationships["space"].Data.GUID, ShouldEqual, "885a8cb3-c07b-4856-b448-eeb10bf36236") + So(routes[0].Relationships["domain"].Data.GUID, ShouldEqual, "0b5f3633-194c-42d2-9408-972366617e0e") + + So(routes[0].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31") + So(routes[0].Links["space"].Href, ShouldEqual, "https://api.example.org/v3/spaces/885a8cb3-c07b-4856-b448-eeb10bf36236") + So(routes[0].Links["domain"].Href, ShouldEqual, "https://api.example.org/v3/domains/0b5f3633-194c-42d2-9408-972366617e0e") + So(routes[0].Links["destinations"].Href, ShouldEqual, "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31/destinations") + }) +} + +func TestCreateV3Routes(t *testing.T) { + Convey("Create V3 Route", t, func() { + setup(MockRoute{"POST", "/v3/routes", []string{createV3RoutePayload}, "", http.StatusCreated, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + route, err := client.CreateV3Route( + "885a8cb3-c07b-4856-b448-eeb10bf36236", + "0b5f3633-194c-42d2-9408-972366617e0e", + nil, + ) + So(err, ShouldBeNil) + So(route.Host, ShouldEqual, "a-hostname") + So(route.Path, ShouldEqual, "/some_path") + So(route.Relationships["space"].Data.GUID, ShouldEqual, "885a8cb3-c07b-4856-b448-eeb10bf36236") + So(route.Relationships["domain"].Data.GUID, ShouldEqual, "0b5f3633-194c-42d2-9408-972366617e0e") + So(route.Links["self"].Href, ShouldEqual, "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31") + So(route.Links["space"].Href, ShouldEqual, "https://api.example.org/v3/spaces/885a8cb3-c07b-4856-b448-eeb10bf36236") + So(route.Links["domain"].Href, ShouldEqual, "https://api.example.org/v3/domains/0b5f3633-194c-42d2-9408-972366617e0e") + So(route.Links["destinations"].Href, ShouldEqual, "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31/destinations") + }) +} diff --git a/third_party/go-cfclient/v3security_groups.go b/third_party/go-cfclient/v3security_groups.go new file mode 100644 index 000000000000..57c9f4d25d88 --- /dev/null +++ b/third_party/go-cfclient/v3security_groups.go @@ -0,0 +1,195 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +// V3SecurityGroup implements the security group object. Security groups are collections of egress traffic rules that can be applied to the staging or running state of applications. +type V3SecurityGroup struct { + Name string `json:"name,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + GloballyEnabled V3GloballyEnabled `json:"globally_enabled,omitempty"` + Rules []V3Rule `json:"rules,omitempty"` + Relationships map[string]V3ToManyRelationships `json:"relationships,omitempty"` + Links map[string]Link `json:"links,omitempty"` +} + +// V3GloballyEnabled object controls if the group is applied globally to the lifecycle of all applications +type V3GloballyEnabled struct { + Running bool `json:"running,omitempty"` + Staging bool `json:"staging,omitempty"` +} + +// V3Rule is an object that provide a rule that will be applied by a security group +type V3Rule struct { + Protocol string `json:"protocol,omitempty"` + Destination string `json:"destination,omitempty"` + Ports string `json:"ports,omitempty"` + Type *int `json:"type,omitempty"` + Code *int `json:"code,omitempty"` + Description string `json:"description,omitempty"` + Log bool `json:"log,omitempty"` +} + +type listV3SecurityGroupResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3SecurityGroup `json:"resources,omitempty"` +} + +// ListV3SecurityGroupsByQuery retrieves security groups based on query +func (c *Client) ListV3SecurityGroupsByQuery(query url.Values) ([]V3SecurityGroup, error) { + var securityGroups []V3SecurityGroup + requestURL, err := url.Parse("/v3/security_groups") + if err != nil { + return nil, err + } + requestURL.RawQuery = query.Encode() + + for { + r := c.NewRequest("GET", fmt.Sprintf("%s?%s", requestURL.Path, requestURL.RawQuery)) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 security groups") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 security groups, response code: %d", resp.StatusCode) + } + + var data listV3SecurityGroupResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 security groups") + } + + securityGroups = append(securityGroups, data.Resources...) + + requestURL, err = url.Parse(data.Pagination.Next.Href) + if err != nil { + return nil, errors.Wrap(err, "Error parsing next page URL") + } + if requestURL.String() == "" { + break + } + } + + return securityGroups, nil +} + +// CreateV3SecurityGroupRequest implements an object that is passed to CreateV3SecurityGroup method +type CreateV3SecurityGroupRequest struct { + Name string `json:"name"` + GloballyEnabled *V3GloballyEnabled `json:"globally_enabled,omitempty"` + Rules []*V3Rule `json:"rules,omitempty"` + Relationships map[string]V3ToManyRelationships `json:"relationships,omitempty"` +} + +// CreateV3SecurityGroup creates security group from CreateV3SecurityGroupRequest +func (c *Client) CreateV3SecurityGroup(r CreateV3SecurityGroupRequest) (*V3SecurityGroup, error) { + req := c.NewRequest("POST", "/v3/security_groups") + + buf := bytes.NewBuffer(nil) + enc := json.NewEncoder(buf) + if err := enc.Encode(r); err != nil { + return nil, err + } + req.body = buf + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 security group") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating v3 security group %s, response code: %d", r.Name, resp.StatusCode) + } + + var securitygroup V3SecurityGroup + if err := json.NewDecoder(resp.Body).Decode(&securitygroup); err != nil { + return nil, errors.Wrap(err, "Error reading v3 security group JSON") + } + + return &securitygroup, nil +} + +// DeleteV3SecurityGroup deletes security group by GUID +func (c *Client) DeleteV3SecurityGroup(GUID string) error { + req := c.NewRequest("DELETE", "/v3/security_groups/"+GUID) + + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error while deleting v3 security group") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("Error deleting v3 security group with GUID [%s], response code: %d", GUID, resp.StatusCode) + } + return nil +} + +// UpdateV3SecurityGroupRequest implements an object that is passed to UpdateV3SecurityGroup method +type UpdateV3SecurityGroupRequest struct { + Name string `json:"name,omitempty"` + GloballyEnabled *V3GloballyEnabled `json:"globally_enabled,omitempty"` + Rules []*V3Rule `json:"rules,omitempty"` +} + +// UpdateV3SecurityGroup updates security group by GUID and from UpdateV3SecurityGroupRequest +func (c *Client) UpdateV3SecurityGroup(GUID string, r UpdateV3SecurityGroupRequest) (*V3SecurityGroup, error) { + req := c.NewRequest("PATCH", "/v3/security_groups/"+GUID) + buf := bytes.NewBuffer(nil) + enc := json.NewEncoder(buf) + if err := enc.Encode(r); err != nil { + return nil, err + } + req.body = buf + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while updating v3 security group") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error updating v3 security group %s, response code: %d", GUID, resp.StatusCode) + } + + var securityGroup V3SecurityGroup + if err := json.NewDecoder(resp.Body).Decode(&securityGroup); err != nil { + return nil, errors.Wrap(err, "Error reading v3 security group JSON") + } + + return &securityGroup, nil +} + +// GetV3SecurityGroupByGUID retrieves security group base on provided GUID +func (c *Client) GetV3SecurityGroupByGUID(GUID string) (*V3SecurityGroup, error) { + req := c.NewRequest("GET", "/v3/security_groups/"+GUID) + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while getting v3 security group") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting v3 security group with GUID [%s], response code: %d", GUID, resp.StatusCode) + } + + var securityGroup V3SecurityGroup + if err := json.NewDecoder(resp.Body).Decode(&securityGroup); err != nil { + return nil, errors.Wrap(err, "Error reading v3 security group JSON") + } + + return &securityGroup, nil +} diff --git a/third_party/go-cfclient/v3security_groups_test.go b/third_party/go-cfclient/v3security_groups_test.go new file mode 100644 index 000000000000..e263cbad3e57 --- /dev/null +++ b/third_party/go-cfclient/v3security_groups_test.go @@ -0,0 +1,417 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "net/http" + "net/url" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListV3SecurityGroupsByQuery(t *testing.T) { + Convey("List All V3 Security Groups", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/security_groups", []string{listV3SecurityGroupsPayload}, "", http.StatusOK, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + securityGroups, err := client.ListV3SecurityGroupsByQuery(nil) + So(err, ShouldBeNil) + So(securityGroups, ShouldHaveLength, 2) + + So(securityGroups[0].Name, ShouldEqual, "my-group1") + So(securityGroups[0].GUID, ShouldEqual, "guid-1") + So(securityGroups[1].Name, ShouldEqual, "my-group2") + So(securityGroups[1].GUID, ShouldEqual, "guid-2") + + So(securityGroups[0].GloballyEnabled.Running, ShouldEqual, true) + So(securityGroups[0].Rules[0].Protocol, ShouldEqual, "tcp") + So(securityGroups[0].Rules[0].Destination, ShouldEqual, "1.2.3.4/10") + So(securityGroups[0].Rules[0].Ports, ShouldEqual, "443,80,8080") + So(*securityGroups[0].Rules[1].Type, ShouldEqual, 8) + So(*securityGroups[0].Rules[1].Code, ShouldEqual, 0) + So(securityGroups[0].Rules[1].Description, ShouldEqual, "test-desc-1") + So(securityGroups[0].Relationships["staging_spaces"].Data[0].GUID, ShouldEqual, "space-guid-1") + So(securityGroups[0].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/security_groups/guid-1") + So(securityGroups[1].GloballyEnabled.Staging, ShouldEqual, true) + So(securityGroups[1].Rules[1].Protocol, ShouldEqual, "icmp") + So(securityGroups[1].Rules[1].Destination, ShouldEqual, "1.2.3.4/16") + So(securityGroups[1].Rules[0].Ports, ShouldEqual, "443,80,8080") + So(*securityGroups[1].Rules[1].Type, ShouldEqual, 5) + So(*securityGroups[1].Rules[1].Code, ShouldEqual, 0) + So(securityGroups[1].Rules[1].Description, ShouldEqual, "test-desc-2") + So(securityGroups[1].Relationships["running_spaces"].Data[0].GUID, ShouldEqual, "space-guid-5") + So(securityGroups[1].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/security_groups/guid-2") + }) + + Convey("List V3 Security Groups By GUID", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/security_groups", []string{listV3SecurityGroupsByGuidPayload}, "", http.StatusOK, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + query := url.Values{} + query["guids"] = []string{"guid-1"} + + securityGroups, err := client.ListV3SecurityGroupsByQuery(query) + So(err, ShouldBeNil) + So(securityGroups, ShouldHaveLength, 1) + + So(securityGroups[0].Name, ShouldEqual, "my-group1") + So(securityGroups[0].GUID, ShouldEqual, "guid-1") + + So(securityGroups[0].GloballyEnabled.Running, ShouldEqual, true) + So(securityGroups[0].Rules[0].Protocol, ShouldEqual, "tcp") + So(securityGroups[0].Rules[0].Destination, ShouldEqual, "1.2.3.4/10") + So(securityGroups[0].Rules[0].Ports, ShouldEqual, "443,80,8080") + So(*securityGroups[0].Rules[1].Type, ShouldEqual, 8) + So(*securityGroups[0].Rules[1].Code, ShouldEqual, 0) + So(securityGroups[0].Rules[1].Description, ShouldEqual, "test-desc-1") + So(securityGroups[0].Relationships["staging_spaces"].Data[0].GUID, ShouldEqual, "space-guid-1") + So(securityGroups[0].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/security_groups/guid-1") + }) +} + +func TestCreateV3SecurityGroup(t *testing.T) { + Convey("Create V3 Security Group With Minimal Parameters", t, func() { + expectedRequestBody := `{"name":"my-sec-group"}` + expectedResponseBody := `{"guid":"guid-1", "name":"my-sec-group", "globally_enabled": {"running": false,"staging": false}, "rules": []}` + setup(MockRoute{"POST", "/v3/security_groups", []string{expectedResponseBody}, "", http.StatusCreated, "", &expectedRequestBody}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + securityGroups, err := client.CreateV3SecurityGroup(CreateV3SecurityGroupRequest{ + Name: "my-sec-group", + }) + So(err, ShouldBeNil) + So(securityGroups, ShouldNotBeNil) + So(securityGroups.GUID, ShouldEqual, "guid-1") + So(securityGroups.Name, ShouldEqual, "my-sec-group") + So(securityGroups.GloballyEnabled.Running, ShouldEqual, false) + So(securityGroups.GloballyEnabled.Staging, ShouldEqual, false) + So(len(securityGroups.Rules), ShouldEqual, 0) + }) + + Convey("Create V3 Security Group With Optional Parameters", t, func() { + requestBody := `{ + "name": "my-sec-group", + "globally_enabled": { + "running": true + }, + "rules": [ + { + "protocol": "tcp", + "destination": "10.10.10.0/24", + "ports": "443,80,8080" + }, + { + "protocol": "icmp", + "destination": "10.10.11.0/24", + "type": 8, + "code": 0, + "description": "Allow ping requests to private services" + } + ], + "relationships": { + "running_spaces": { + "data": [ + { + "guid": "space-guid-1" + }, + { + "guid": "space-guid-2" + } + ] + } + } + }` + buffer := new(bytes.Buffer) + err := json.Compact(buffer, []byte(requestBody)) + So(err, ShouldBeNil) + expectedRequestBody := buffer.String() + setup(MockRoute{"POST", "/v3/security_groups", []string{genericV3SecurityGroupPayload}, "", http.StatusCreated, "", &expectedRequestBody}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + icmpType := 8 + icmpCode := 0 + createV3SecGroupRequest := CreateV3SecurityGroupRequest{ + Name: "my-sec-group", + GloballyEnabled: &V3GloballyEnabled{ + Running: true, + Staging: false, + }, + Rules: []*V3Rule{ + { + Protocol: "tcp", + Destination: "10.10.10.0/24", + Ports: "443,80,8080", + }, + { + Protocol: "icmp", + Destination: "10.10.11.0/24", + Type: &icmpType, + Code: &icmpCode, + Description: "Allow ping requests to private services", + }, + }, + Relationships: map[string]V3ToManyRelationships{ + "running_spaces": { + Data: []V3Relationship{ + { + GUID: "space-guid-1", + }, + { + GUID: "space-guid-2", + }, + }, + }, + }, + } + + securityGroups, err := client.CreateV3SecurityGroup(createV3SecGroupRequest) + So(err, ShouldBeNil) + So(securityGroups, ShouldNotBeNil) + So(securityGroups.GUID, ShouldEqual, "guid-1") + So(securityGroups.Name, ShouldEqual, "my-sec-group") + So(securityGroups.GloballyEnabled.Running, ShouldEqual, true) + So(securityGroups.GloballyEnabled.Staging, ShouldEqual, false) + So(securityGroups.Rules[0].Protocol, ShouldEqual, "tcp") + So(securityGroups.Rules[0].Destination, ShouldEqual, "10.10.10.0/24") + So(securityGroups.Rules[0].Ports, ShouldEqual, "443,80,8080") + So(securityGroups.Rules[1].Protocol, ShouldEqual, "icmp") + So(securityGroups.Rules[1].Destination, ShouldEqual, "10.10.11.0/24") + So(*securityGroups.Rules[1].Type, ShouldEqual, 8) + So(*securityGroups.Rules[1].Code, ShouldEqual, 0) + So(securityGroups.Rules[1].Description, ShouldEqual, "Allow ping requests to private services") + So(len(securityGroups.Relationships["staging_spaces"].Data), ShouldEqual, 0) + So(securityGroups.Relationships["running_spaces"].Data[0].GUID, ShouldEqual, "space-guid-1") + So(securityGroups.Relationships["running_spaces"].Data[1].GUID, ShouldEqual, "space-guid-2") + So(securityGroups.Links["self"].Href, ShouldEqual, "https://api.example.org/v3/security_groups/guid-1") + }) + Convey("Create V3 Security Group with empty icmp type and code and no name", t, func() { + expectedRequestBody := `{"name":"","rules":[{"protocol":"icmp","destination":"10.10.11.0/24"}]}` + expectedResponseBody := `{ + "errors": [ + { + "code": 10008, + "detail": "Rules[0]: code is required for protocols of type ICMP, Rules[0]: code must be an integer between -1 and 255 (inclusive), Rules[0]: type is required for protocols of type ICMP, Rules[0]: type must be an integer between -1 and 255 (inclusive), Name can't be blank, Name must be a string", + "title": "CF-UnprocessableEntity" + } + ] + }` + setup(MockRoute{"POST", "/v3/security_groups", []string{expectedResponseBody}, "", http.StatusUnprocessableEntity, "", &expectedRequestBody}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + _, err = client.CreateV3SecurityGroup(CreateV3SecurityGroupRequest{ + Rules: []*V3Rule{ + { + Protocol: "icmp", + Destination: "10.10.11.0/24", + }, + }, + }) + So(err, ShouldNotBeNil) + So(err.Error(), ShouldContainSubstring, "code is required for protocols of type ICMP") + So(err.Error(), ShouldContainSubstring, "type is required for protocols of type ICMP") + So(err.Error(), ShouldContainSubstring, "Name can't be blank, Name must be a string") + + }) +} + +func TestDeleteV3SecurityGroup(t *testing.T) { + Convey("Delete V3 Security Group", t, func() { + setup(MockRoute{"DELETE", "/v3/security_groups/security-group-guid", []string{""}, "", http.StatusAccepted, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteV3SecurityGroup("security-group-guid") + So(err, ShouldBeNil) + }) +} + +func TestUpdateV3SecurityGroup(t *testing.T) { + Convey("Update V3 Security Group with empty type and code", t, func() { + expectedRequestBody := `{"rules":[{"protocol":"icmp","destination":"10.10.11.0/24"}]}` + expectedResponseBody := `{ + "errors": [ + { + "code": 10008, + "detail": "Rules[0]: code is required for protocols of type ICMP, Rules[0]: code must be an integer between -1 and 255 (inclusive), Rules[0]: type is required for protocols of type ICMP, Rules[0]: type must be an integer between -1 and 255 (inclusive)", + "title": "CF-UnprocessableEntity" + } + ] + }` + setup(MockRoute{"PATCH", "/v3/security_groups/guid-1", []string{expectedResponseBody}, "", http.StatusUnprocessableEntity, "", &expectedRequestBody}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + _, err = client.UpdateV3SecurityGroup("guid-1", UpdateV3SecurityGroupRequest{ + Rules: []*V3Rule{ + { + Protocol: "icmp", + Destination: "10.10.11.0/24", + }, + }, + }) + So(err, ShouldNotBeNil) + So(err.Error(), ShouldContainSubstring, "code is required for protocols of type ICMP") + So(err.Error(), ShouldContainSubstring, "type is required for protocols of type ICMP") + }) + + Convey("Update name of V3 Security Group", t, func() { + expectedRequestBody := `{"name":"my-sec-group"}` + expectedResponseBody := `{"guid":"guid-1", "name":"my-sec-group", "globally_enabled": {"running": false,"staging": false}, "rules": []}` + setup(MockRoute{"PATCH", "/v3/security_groups/guid-1", []string{expectedResponseBody}, "", http.StatusOK, "", &expectedRequestBody}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + securityGroups, err := client.UpdateV3SecurityGroup("guid-1", UpdateV3SecurityGroupRequest{ + Name: "my-sec-group", + }) + So(err, ShouldBeNil) + So(securityGroups, ShouldNotBeNil) + So(securityGroups.GUID, ShouldEqual, "guid-1") + So(securityGroups.Name, ShouldEqual, "my-sec-group") + So(securityGroups.GloballyEnabled.Running, ShouldEqual, false) + So(securityGroups.GloballyEnabled.Staging, ShouldEqual, false) + So(len(securityGroups.Rules), ShouldEqual, 0) + }) + + Convey("Update V3 Security Group With Optional Parameters", t, func() { + requestBody := `{ + "name": "my-sec-group", + "globally_enabled": { + "running": true + }, + "rules": [ + { + "protocol": "tcp", + "destination": "10.10.10.0/24", + "ports": "443,80,8080" + }, + { + "protocol": "icmp", + "destination": "10.10.11.0/24", + "type": 8, + "code": 0, + "description": "Allow ping requests to private services" + } + ] + }` + buffer := new(bytes.Buffer) + err := json.Compact(buffer, []byte(requestBody)) + So(err, ShouldBeNil) + expectedRequestBody := buffer.String() + setup(MockRoute{"PATCH", "/v3/security_groups/guid-1", []string{genericV3SecurityGroupPayload}, "", http.StatusOK, "", &expectedRequestBody}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + icmpType := 8 + icmpCode := 0 + updateV3SecGroupRequest := UpdateV3SecurityGroupRequest{ + Name: "my-sec-group", + GloballyEnabled: &V3GloballyEnabled{ + Running: true, + Staging: false, + }, + Rules: []*V3Rule{ + { + Protocol: "tcp", + Destination: "10.10.10.0/24", + Ports: "443,80,8080", + }, + { + Protocol: "icmp", + Destination: "10.10.11.0/24", + Type: &icmpType, + Code: &icmpCode, + Description: "Allow ping requests to private services", + }, + }, + } + + securityGroups, err := client.UpdateV3SecurityGroup("guid-1", updateV3SecGroupRequest) + So(err, ShouldBeNil) + So(securityGroups, ShouldNotBeNil) + So(securityGroups.GUID, ShouldEqual, "guid-1") + So(securityGroups.Name, ShouldEqual, "my-sec-group") + So(securityGroups.GloballyEnabled.Running, ShouldEqual, true) + So(securityGroups.GloballyEnabled.Staging, ShouldEqual, false) + So(securityGroups.Rules[0].Protocol, ShouldEqual, "tcp") + So(securityGroups.Rules[0].Destination, ShouldEqual, "10.10.10.0/24") + So(securityGroups.Rules[0].Ports, ShouldEqual, "443,80,8080") + So(securityGroups.Rules[1].Protocol, ShouldEqual, "icmp") + So(securityGroups.Rules[1].Destination, ShouldEqual, "10.10.11.0/24") + So(*securityGroups.Rules[1].Type, ShouldEqual, 8) + So(*securityGroups.Rules[1].Code, ShouldEqual, 0) + So(securityGroups.Rules[1].Description, ShouldEqual, "Allow ping requests to private services") + So(len(securityGroups.Relationships["staging_spaces"].Data), ShouldEqual, 0) + So(securityGroups.Relationships["running_spaces"].Data[0].GUID, ShouldEqual, "space-guid-1") + So(securityGroups.Relationships["running_spaces"].Data[1].GUID, ShouldEqual, "space-guid-2") + So(securityGroups.Links["self"].Href, ShouldEqual, "https://api.example.org/v3/security_groups/guid-1") + }) +} + +func TestGetV3SecurityGroup(t *testing.T) { + Convey("Get V3 Security Group", t, func() { + setup(MockRoute{"GET", "/v3/security_groups/guid-1", []string{genericV3SecurityGroupPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + securityGroup, err := client.GetV3SecurityGroupByGUID("guid-1") + So(err, ShouldBeNil) + So(securityGroup, ShouldNotBeNil) + So(securityGroup.GUID, ShouldEqual, "guid-1") + So(securityGroup.Name, ShouldEqual, "my-sec-group") + So(securityGroup.GloballyEnabled.Running, ShouldEqual, true) + So(securityGroup.GloballyEnabled.Staging, ShouldEqual, false) + So(securityGroup.Rules[0].Protocol, ShouldEqual, "tcp") + So(securityGroup.Rules[0].Destination, ShouldEqual, "10.10.10.0/24") + So(securityGroup.Rules[0].Ports, ShouldEqual, "443,80,8080") + So(securityGroup.Rules[1].Protocol, ShouldEqual, "icmp") + So(securityGroup.Rules[1].Destination, ShouldEqual, "10.10.11.0/24") + So(*securityGroup.Rules[1].Type, ShouldEqual, 8) + So(*securityGroup.Rules[1].Code, ShouldEqual, 0) + So(securityGroup.Rules[1].Description, ShouldEqual, "Allow ping requests to private services") + So(len(securityGroup.Relationships["staging_spaces"].Data), ShouldEqual, 0) + So(securityGroup.Relationships["running_spaces"].Data[0].GUID, ShouldEqual, "space-guid-1") + So(securityGroup.Relationships["running_spaces"].Data[1].GUID, ShouldEqual, "space-guid-2") + So(securityGroup.Links["self"].Href, ShouldEqual, "https://api.example.org/v3/security_groups/guid-1") + }) +} diff --git a/third_party/go-cfclient/v3service_credential_bindings.go b/third_party/go-cfclient/v3service_credential_bindings.go new file mode 100644 index 000000000000..0a06c151a768 --- /dev/null +++ b/third_party/go-cfclient/v3service_credential_bindings.go @@ -0,0 +1,97 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "time" + + "github.com/pkg/errors" +) + +// V3ServiceCredentialBindings implements the service credential binding object. a credential binding can be a binding between apps and a service instance or a service key +type V3ServiceCredentialBindings struct { + GUID string `json:"guid"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Name string `json:"name"` + Type string `json:"type"` + LastOperation LastOperation `json:"last_operation"` + Metadata Metadata `json:"metadata"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Links map[string]Link `json:"links"` +} + +type listV3ServiceCredentialBindingsResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3ServiceCredentialBindings `json:"resources,omitempty"` +} + +// ListV3ServiceCredentialBindings retrieves all service credential bindings +func (c *Client) ListV3ServiceCredentialBindings() ([]V3ServiceCredentialBindings, error) { + return c.ListV3ServiceCredentialBindingsByQuery(nil) +} + +// ListV3ServiceCredentialBindingsByQuery retrieves service credential bindings using a query +func (c *Client) ListV3ServiceCredentialBindingsByQuery(query url.Values) ([]V3ServiceCredentialBindings, error) { + var svcCredentialBindings []V3ServiceCredentialBindings + requestURL := "/v3/service_credential_bindings" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 service credential bindings") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error listing v3 service credential bindings, response code: %d", resp.StatusCode) + } + + var data listV3ServiceCredentialBindingsResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 service credential bindings") + } + + svcCredentialBindings = append(svcCredentialBindings, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 service credential bindings") + } + } + + return svcCredentialBindings, nil +} + +// GetV3ServiceCredentialBindingsByGUID retrieves the service credential binding based on the provided guid +func (c *Client) GetV3ServiceCredentialBindingsByGUID(GUID string) (*V3ServiceCredentialBindings, error) { + requestURL := fmt.Sprintf("/v3/service_credential_bindings/%s", GUID) + req := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(req) + + if err != nil { + return nil, errors.Wrap(err, "Error while getting v3 service credential binding") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting v3 service credential binding with GUID [%s], response code: %d", GUID, resp.StatusCode) + } + + var svcCredentialBindings V3ServiceCredentialBindings + if err := json.NewDecoder(resp.Body).Decode(&svcCredentialBindings); err != nil { + return nil, errors.Wrap(err, "Error reading v3 service credential binding JSON") + } + + return &svcCredentialBindings, nil +} diff --git a/third_party/go-cfclient/v3service_credential_bindings_test.go b/third_party/go-cfclient/v3service_credential_bindings_test.go new file mode 100644 index 000000000000..ca8f4829a270 --- /dev/null +++ b/third_party/go-cfclient/v3service_credential_bindings_test.go @@ -0,0 +1,51 @@ +package cfclient + +import ( + "fmt" + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListV3ServiceCredentialBindingsByQuery(t *testing.T) { + Convey("List V3 Service Credential Bindings", t, func() { + setup(MockRoute{"GET", "/v3/service_credential_bindings", []string{listV3ServiceCredentialBindingsPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + serviceCredentialsBindings, err := client.ListV3ServiceCredentialBindings() + So(err, ShouldBeNil) + So(serviceCredentialsBindings, ShouldHaveLength, 1) + + So(serviceCredentialsBindings[0].Name, ShouldEqual, "my_service_key") + So(serviceCredentialsBindings[0].Type, ShouldEqual, "key") + + So(serviceCredentialsBindings[0].Relationships["service_instance"].Data.GUID, ShouldEqual, "85ccdcad-d725-4109-bca4-fd6ba062b5c8") + So(serviceCredentialsBindings[0].Links["service_instance"].Href, ShouldEqual, "https://api.example.org/v3/service_instances/85ccdcad-d725-4109-bca4-fd6ba062b5c8") + }) +} + +func TestGetV3ServiceCredentialBindingsByGUID(t *testing.T) { + Convey("Get V3 Service Credential Binding by GUID", t, func() { + GUID := "d9634934-8e1f-4c2d-bb33-fa5df019cf9d" + setup(MockRoute{"GET", fmt.Sprintf("/v3/service_credential_bindings/%s", GUID), []string{GetV3ServiceCredentialBindingsByGUIDPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + serviceCredentialsBinding, err := client.GetV3ServiceCredentialBindingsByGUID(GUID) + So(err, ShouldBeNil) + + So(serviceCredentialsBinding.Name, ShouldEqual, "my_service_key") + So(serviceCredentialsBinding.Type, ShouldEqual, "key") + + So(serviceCredentialsBinding.Relationships["service_instance"].Data.GUID, ShouldEqual, "85ccdcad-d725-4109-bca4-fd6ba062b5c8") + So(serviceCredentialsBinding.Links["service_instance"].Href, ShouldEqual, "https://api.example.org/v3/service_instances/85ccdcad-d725-4109-bca4-fd6ba062b5c8") + }) +} diff --git a/third_party/go-cfclient/v3service_instances.go b/third_party/go-cfclient/v3service_instances.go new file mode 100644 index 000000000000..43f42712d9dc --- /dev/null +++ b/third_party/go-cfclient/v3service_instances.go @@ -0,0 +1,69 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "time" + + "github.com/pkg/errors" +) + +type V3ServiceInstance struct { + Guid string `json:"guid"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Name string `json:"name"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Metadata Metadata `json:"metadata"` + Links map[string]Link `json:"links"` +} + +type listV3ServiceInstancesResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3ServiceInstance `json:"resources,omitempty"` +} + +func (c *Client) ListV3ServiceInstances() ([]V3ServiceInstance, error) { + return c.ListV3ServiceInstancesByQuery(nil) +} + +func (c *Client) ListV3ServiceInstancesByQuery(query url.Values) ([]V3ServiceInstance, error) { + var svcInstances []V3ServiceInstance + requestURL := "/v3/service_instances" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 service instances") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error listing v3 service instances, response code: %d", resp.StatusCode) + } + + var data listV3ServiceInstancesResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 service instances") + } + + svcInstances = append(svcInstances, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 service instances") + } + } + + return svcInstances, nil +} diff --git a/third_party/go-cfclient/v3service_instances_test.go b/third_party/go-cfclient/v3service_instances_test.go new file mode 100644 index 000000000000..80ae9ef9e287 --- /dev/null +++ b/third_party/go-cfclient/v3service_instances_test.go @@ -0,0 +1,28 @@ +package cfclient + +import ( + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListV3ServiceInstancesByQuery(t *testing.T) { + Convey("List V3 Service Instances", t, func() { + setup(MockRoute{"GET", "/v3/service_instances", []string{listV3ServiceInstancesPayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + services, err := client.ListV3ServiceInstances() + So(err, ShouldBeNil) + So(services, ShouldHaveLength, 1) + + So(services[0].Name, ShouldEqual, "my_service_instance") + + So(services[0].Relationships["space"].Data.GUID, ShouldEqual, "ae0031f9-dd49-461c-a945-df40e77c39cb") + So(services[0].Links["space"].Href, ShouldEqual, "https://api.example.org/v3/spaces/ae0031f9-dd49-461c-a945-df40e77c39cb") + }) +} diff --git a/third_party/go-cfclient/v3spaces.go b/third_party/go-cfclient/v3spaces.go new file mode 100644 index 000000000000..3f5e4da0e641 --- /dev/null +++ b/third_party/go-cfclient/v3spaces.go @@ -0,0 +1,228 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type V3Space struct { + Name string `json:"name,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +type CreateV3SpaceRequest struct { + Name string + OrgGUID string + Metadata *V3Metadata +} + +type UpdateV3SpaceRequest struct { + Name string + Metadata *V3Metadata +} + +type V3SpaceUsers struct { + Name string `json:"name,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +func (c *Client) CreateV3Space(r CreateV3SpaceRequest) (*V3Space, error) { + req := c.NewRequest("POST", "/v3/spaces") + params := map[string]interface{}{ + "name": r.Name, + "relationships": map[string]interface{}{ + "organization": V3ToOneRelationship{ + Data: V3Relationship{ + GUID: r.OrgGUID, + }, + }, + }, + } + if r.Metadata != nil { + params["metadata"] = r.Metadata + } + + req.obj = params + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 space") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating v3 space %s, response code: %d", r.Name, resp.StatusCode) + } + + var space V3Space + if err := json.NewDecoder(resp.Body).Decode(&space); err != nil { + return nil, errors.Wrap(err, "Error reading v3 space JSON") + } + + return &space, nil +} + +func (c *Client) GetV3SpaceByGUID(spaceGUID string) (*V3Space, error) { + req := c.NewRequest("GET", "/v3/spaces/"+spaceGUID) + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while getting v3 space") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting v3 space with GUID [%s], response code: %d", spaceGUID, resp.StatusCode) + } + + var space V3Space + if err := json.NewDecoder(resp.Body).Decode(&space); err != nil { + return nil, errors.Wrap(err, "Error reading v3 space JSON") + } + + return &space, nil +} + +func (c *Client) DeleteV3Space(spaceGUID string) error { + req := c.NewRequest("DELETE", "/v3/spaces/"+spaceGUID) + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error while deleting v3 space") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("Error deleting v3 space with GUID [%s], response code: %d", spaceGUID, resp.StatusCode) + } + + return nil +} + +func (c *Client) UpdateV3Space(spaceGUID string, r UpdateV3SpaceRequest) (*V3Space, error) { + req := c.NewRequest("PATCH", "/v3/spaces/"+spaceGUID) + params := make(map[string]interface{}) + if r.Name != "" { + params["name"] = r.Name + } + if r.Metadata != nil { + params["metadata"] = r.Metadata + } + if len(params) > 0 { + req.obj = params + } + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while updating v3 space") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error updating v3 space %s, response code: %d", spaceGUID, resp.StatusCode) + } + + var space V3Space + if err := json.NewDecoder(resp.Body).Decode(&space); err != nil { + return nil, errors.Wrap(err, "Error reading v3 space JSON") + } + + return &space, nil +} + +type listV3SpacesResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Space `json:"resources,omitempty"` +} + +func (c *Client) ListV3SpacesByQuery(query url.Values) ([]V3Space, error) { + var spaces []V3Space + requestURL := "/v3/spaces" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 spaces") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 spaces, response code: %d", resp.StatusCode) + } + + var data listV3SpacesResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 spaces") + } + + spaces = append(spaces, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 spaces") + } + } + + return spaces, nil +} + +type listV3SpaceUsersResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3User `json:"resources,omitempty"` +} + +// ListV3SpaceUsers lists users by space GUID +func (c *Client) ListV3SpaceUsers(spaceGUID string) ([]V3User, error) { + var users []V3User + requestURL := "/v3/spaces/" + spaceGUID + "/users" + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 space users") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 space users, response code: %d", resp.StatusCode) + } + + var data listV3SpaceUsersResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 space users") + } + users = append(users, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 space users") + } + } + + return users, nil +} diff --git a/third_party/go-cfclient/v3spaces_test.go b/third_party/go-cfclient/v3spaces_test.go new file mode 100644 index 000000000000..79808a5e3d6f --- /dev/null +++ b/third_party/go-cfclient/v3spaces_test.go @@ -0,0 +1,143 @@ +package cfclient + +import ( + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestCreateV3Space(t *testing.T) { + Convey("Create V3 Space", t, func() { + expectedBody := `{"name":"my-space","relationships":{"organization":{"data":{"guid":"org-guid"}}}}` + setup(MockRoute{"POST", "/v3/spaces", []string{createV3SpacePayload}, "", http.StatusCreated, "", &expectedBody}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + space, err := client.CreateV3Space(CreateV3SpaceRequest{ + Name: "my-space", + OrgGUID: "org-guid", + }) + So(err, ShouldBeNil) + So(space, ShouldNotBeNil) + + So(space.GUID, ShouldEqual, "space-guid") + So(space.Relationships["organization"].Data.GUID, ShouldEqual, "org-guid") + So(space.Links["organization"].Href, ShouldEqual, "https://api.example.org/v3/organizations/org-guid") + So(space.Metadata.Annotations, ShouldHaveLength, 0) + So(space.Metadata.Labels, ShouldContainKey, "SPACE_KEY") + So(space.Metadata.Labels["SPACE_KEY"], ShouldEqual, "space_value") + }) +} + +func TestGetV3Space(t *testing.T) { + Convey("Get V3 Space", t, func() { + setup(MockRoute{"GET", "/v3/spaces/space-guid", []string{getV3SpacePayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + space, err := client.GetV3SpaceByGUID("space-guid") + So(err, ShouldBeNil) + So(space, ShouldNotBeNil) + + So(space.GUID, ShouldEqual, "space-guid") + So(space.Relationships["organization"].Data.GUID, ShouldEqual, "org-guid") + So(space.Links["organization"].Href, ShouldEqual, "https://api.example.org/v3/organizations/org-guid") + So(space.Metadata.Annotations, ShouldHaveLength, 0) + So(space.Metadata.Labels, ShouldContainKey, "SPACE_KEY") + So(space.Metadata.Labels["SPACE_KEY"], ShouldEqual, "space_value") + }) +} + +func TestDeleteV3Space(t *testing.T) { + Convey("Delete V3 Space", t, func() { + setup(MockRoute{"DELETE", "/v3/spaces/space-guid", []string{""}, "", http.StatusAccepted, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + err = client.DeleteV3Space("space-guid") + So(err, ShouldBeNil) + }) +} + +func TestUpdateV3Space(t *testing.T) { + Convey("Update V3 Space", t, func() { + setup(MockRoute{"PATCH", "/v3/spaces/space-guid", []string{updateV3SpacePayload}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + space, err := client.UpdateV3Space("space-guid", UpdateV3SpaceRequest{ + Name: "my-space", + }) + So(err, ShouldBeNil) + So(space, ShouldNotBeNil) + + So(space.Name, ShouldEqual, "my-space") + So(space.GUID, ShouldEqual, "space-guid") + So(space.Relationships["organization"].Data.GUID, ShouldEqual, "org-guid") + So(space.Links["organization"].Href, ShouldEqual, "https://api.example.org/v3/organizations/org-guid") + So(space.Metadata.Annotations, ShouldHaveLength, 0) + So(space.Metadata.Labels, ShouldContainKey, "SPACE_KEY") + So(space.Metadata.Labels["SPACE_KEY"], ShouldEqual, "space_value") + }) +} + +func TestListV3SpacesByQuery(t *testing.T) { + Convey("List V3 Spaces", t, func() { + setup(MockRoute{"GET", "/v3/spaces", []string{listV3SpacesPayload, listV3SpacesPayloadPage2}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + spaces, err := client.ListV3SpacesByQuery(nil) + So(err, ShouldBeNil) + So(spaces, ShouldHaveLength, 2) + + So(spaces[0].Name, ShouldEqual, "my-space-1") + So(spaces[1].Name, ShouldEqual, "my-space-2") + + So(spaces[0].Relationships["organization"].Data.GUID, ShouldEqual, "org-guid") + So(spaces[0].Links["organization"].Href, ShouldEqual, "https://api.example.org/v3/organizations/org-guid") + So(spaces[1].Relationships["organization"].Data.GUID, ShouldEqual, "org-guid") + So(spaces[1].Links["organization"].Href, ShouldEqual, "https://api.example.org/v3/organizations/org-guid") + }) +} + +func TestListV3SpaceUsersByQuery(t *testing.T) { + Convey("List V3 Space Users", t, func() { + setup(MockRoute{"GET", "/v3/spaces/space-guid/users", []string{listV3SpaceUsersPayload, listV3SpaceUsersPayloadPage2}, "", http.StatusOK, "", nil}, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + users, err := client.ListV3SpaceUsers("space-guid") + So(err, ShouldBeNil) + So(users, ShouldHaveLength, 2) + + So(users[0].Username, ShouldEqual, "some-name-1") + So(users[1].Username, ShouldEqual, "some-name-2") + + So(users[0].PresentationName, ShouldEqual, "some-name-1") + So(users[0].Origin, ShouldEqual, "uaa") + So(users[0].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/users/10a93b89-3f89-4f05-7238-8a2b123c79l9") + So(users[1].PresentationName, ShouldEqual, "some-name-2") + So(users[1].Origin, ShouldEqual, "ldap") + So(users[1].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/users/9da93b89-3f89-4f05-7238-8a2b123c79l9") + }) +} diff --git a/third_party/go-cfclient/v3stacks.go b/third_party/go-cfclient/v3stacks.go new file mode 100644 index 000000000000..74647d6a00e9 --- /dev/null +++ b/third_party/go-cfclient/v3stacks.go @@ -0,0 +1,66 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +// V3Stack implements stack object. Stacks are the base operating system and file system that your application will execute in. A stack is how you configure applications to run against different operating systems (like Windows or Linux) and different versions of those operating systems. +type V3Stack struct { + Name string `json:"name,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Description string `json:"description,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +type listV3StacksResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Stack `json:"resources,omitempty"` +} + +// ListV3StacksByQuery retrieves stacks based on query +func (c *Client) ListV3StacksByQuery(query url.Values) ([]V3Stack, error) { + var stacks []V3Stack + requestURL, err := url.Parse("/v3/stacks") + if err != nil { + return nil, err + } + requestURL.RawQuery = query.Encode() + + for { + r := c.NewRequest("GET", fmt.Sprintf("%s?%s", requestURL.Path, requestURL.RawQuery)) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 stacks") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 stacks, response code: %d", resp.StatusCode) + } + + var data listV3StacksResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 stacks") + } + + stacks = append(stacks, data.Resources...) + + requestURL, err = url.Parse(data.Pagination.Next.Href) + if err != nil { + return nil, errors.Wrap(err, "Error parsing next page URL") + } + if requestURL.String() == "" { + break + } + } + + return stacks, nil +} diff --git a/third_party/go-cfclient/v3stacks_test.go b/third_party/go-cfclient/v3stacks_test.go new file mode 100644 index 000000000000..aab7e6ae7205 --- /dev/null +++ b/third_party/go-cfclient/v3stacks_test.go @@ -0,0 +1,35 @@ +package cfclient + +import ( + "net/http" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListV3StacksByQuery(t *testing.T) { + Convey("List All V3 stacks", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/stacks", []string{listV3StacksPayload}, "", http.StatusOK, "", nil}, + } + setupMultiple(mocks, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + stacks, err := client.ListV3StacksByQuery(nil) + So(err, ShouldBeNil) + So(stacks, ShouldHaveLength, 2) + + So(stacks[0].Name, ShouldEqual, "my-stack-1") + So(stacks[0].Description, ShouldEqual, "This is my first stack!") + So(stacks[0].GUID, ShouldEqual, "guid-1") + So(stacks[0].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/stacks/guid-1") + So(stacks[1].Name, ShouldEqual, "my-stack-2") + So(stacks[1].Description, ShouldEqual, "This is my second stack!") + So(stacks[1].GUID, ShouldEqual, "guid-2") + So(stacks[1].Links["self"].Href, ShouldEqual, "https://api.example.org/v3/stacks/guid-2") + }) +} diff --git a/third_party/go-cfclient/v3types.go b/third_party/go-cfclient/v3types.go new file mode 100644 index 000000000000..65bbd4eb7921 --- /dev/null +++ b/third_party/go-cfclient/v3types.go @@ -0,0 +1,36 @@ +package cfclient + +// Pagination is used by the V3 apis +type Pagination struct { + TotalResults int `json:"total_results"` + TotalPages int `json:"total_pages"` + First Link `json:"first"` + Last Link `json:"last"` + Next Link `json:"next"` + Previous Link `json:"previous"` +} + +// Link is a HATEOAS-style link for v3 apis +type Link struct { + Href string `json:"href"` + Method string `json:"method,omitempty"` +} + +// V3ToOneRelationship is a relationship to a single object +type V3ToOneRelationship struct { + Data V3Relationship `json:"data,omitempty"` +} + +// V3ToManyRelationships is a relationship to multiple objects +type V3ToManyRelationships struct { + Data []V3Relationship `json:"data,omitempty"` +} + +type V3Relationship struct { + GUID string `json:"guid,omitempty"` +} + +type V3Metadata struct { + Labels map[string]string `json:"labels,omitempty"` + Annotations map[string]string `json:"annotations,omitempty"` +} diff --git a/third_party/go-cfclient/v3users.go b/third_party/go-cfclient/v3users.go new file mode 100644 index 000000000000..c66aaa54eb6b --- /dev/null +++ b/third_party/go-cfclient/v3users.go @@ -0,0 +1,67 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +// V3User implements the user object +type V3User struct { + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Username string `json:"username,omitempty"` + PresentationName string `json:"presentation_name,omitempty"` + Origin string `json:"origin,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +type listV3UsersResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3User `json:"resources,omitempty"` +} + +// ListV3UsersByQuery by query +func (c *Client) ListV3UsersByQuery(query url.Values) ([]V3User, error) { + var users []V3User + requestURL, err := url.Parse("/v3/users") + if err != nil { + return nil, err + } + requestURL.RawQuery = query.Encode() + + for { + r := c.NewRequest("GET", fmt.Sprintf("%s?%s", requestURL.Path, requestURL.RawQuery)) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 users") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 users, response code: %d", resp.StatusCode) + } + + var data listV3UsersResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 users") + } + + users = append(users, data.Resources...) + + requestURL, err = url.Parse(data.Pagination.Next.Href) + if err != nil { + return nil, errors.Wrap(err, "Error parsing next page URL") + } + if requestURL.String() == "" { + break + } + } + + return users, nil +} diff --git a/third_party/go-cfclient/v3users_test.go b/third_party/go-cfclient/v3users_test.go new file mode 100644 index 000000000000..05d4df97754f --- /dev/null +++ b/third_party/go-cfclient/v3users_test.go @@ -0,0 +1,34 @@ +package cfclient + +import ( + "net/http" + "net/url" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestListV3UserByQuery(t *testing.T) { + Convey("List V3 Users by Query", t, func() { + mocks := []MockRoute{ + {"GET", "/v3/users", []string{listV3UsersPayload}, "", http.StatusOK, "", nil}, + {"GET", "/v3/userspage2", []string{listV3UsersPayloadPage2}, "", http.StatusOK, "page=2&per_page=2", nil}, + } + setupMultiple(mocks, t) + defer teardown() + + c := &Config{ApiAddress: server.URL, Token: "foobar"} + client, err := NewClient(c) + So(err, ShouldBeNil) + + query := url.Values{} + users, err := client.ListV3UsersByQuery(query) + So(err, ShouldBeNil) + So(users, ShouldHaveLength, 3) + + So(users[0].Username, ShouldEqual, "smoke_tests") + So(users[1].Username, ShouldEqual, "test1") + So(users[2].Username, ShouldEqual, "test2") + }) + +} diff --git a/third_party/go-ordered-map/.circleci/circle_build.sh b/third_party/go-ordered-map/.circleci/circle_build.sh new file mode 100755 index 000000000000..398f5e7c94db --- /dev/null +++ b/third_party/go-ordered-map/.circleci/circle_build.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -ex + +# might as well run a little longer +export FUZZ_TIME=20s + +# there are too many golangci plugins that don't work for 1.19 just yet, so just skip linting for it +if [[ "$GO_VER" == 1.18.* ]]; then + make +else + make test_with_fuzz +fi diff --git a/third_party/go-ordered-map/.circleci/config.yml b/third_party/go-ordered-map/.circleci/config.yml new file mode 100644 index 000000000000..c77a890e17d1 --- /dev/null +++ b/third_party/go-ordered-map/.circleci/config.yml @@ -0,0 +1,23 @@ +version: 2.1 + +jobs: + test: + parameters: + golang-version: + type: string + docker: + - image: cimg/go:<< parameters.golang-version >> + steps: + - checkout + - run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.45.2 + - run: .circleci/circle_build.sh + +workflows: + test-workflow: + jobs: + - test: + matrix: + parameters: + golang-version: + - '1.18' + - '1.19' diff --git a/third_party/go-ordered-map/.gitignore b/third_party/go-ordered-map/.gitignore new file mode 100644 index 000000000000..57872d0f1e5f --- /dev/null +++ b/third_party/go-ordered-map/.gitignore @@ -0,0 +1 @@ +/vendor/ diff --git a/third_party/go-ordered-map/.golangci.yml b/third_party/go-ordered-map/.golangci.yml new file mode 100644 index 000000000000..2417df10d92c --- /dev/null +++ b/third_party/go-ordered-map/.golangci.yml @@ -0,0 +1,80 @@ +run: + tests: false + +linters: + disable-all: true + enable: + - asciicheck + - bidichk + - bodyclose + - containedctx + - contextcheck + - decorder + - depguard + - dogsled + - dupl + - durationcheck + - errcheck + - errchkjson + # FIXME: commented out as it crashes with 1.18 for now + # - errname + - errorlint + - exportloopref + - forbidigo + - funlen + - gci + - gochecknoglobals + - gochecknoinits + - gocognit + - goconst + - gocritic + - gocyclo + - godox + - gofmt + - gofumpt + - goheader + - goimports + - gomnd + - gomoddirectives + - gomodguard + - goprintffuncname + - gosec + - gosimple + - govet + - grouper + - ifshort + - importas + - ineffassign + - lll + - maintidx + - makezero + - misspell + - nakedret + - nilerr + - nilnil + - noctx + - nolintlint + - paralleltest + - prealloc + - predeclared + - promlinter + # FIXME: doesn't support 1.18 yet + # - revive + - rowserrcheck + - sqlclosecheck + - staticcheck + - structcheck + - stylecheck + - tagliatelle + - tenv + - testpackage + - thelper + - tparallel + - typecheck + - unconvert + - unparam + - unused + - varcheck + - varnamelen + - wastedassign + - whitespace diff --git a/third_party/go-ordered-map/CHANGELOG.md b/third_party/go-ordered-map/CHANGELOG.md new file mode 100644 index 000000000000..f27126f84ffc --- /dev/null +++ b/third_party/go-ordered-map/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog + +[comment]: # (Changes since last release go here) + +## 2.1.8 - Jun 27th 2023 + +* Added support for YAML serialization/deserialization + +## 2.1.7 - Apr 13th 2023 + +* Renamed test_utils.go to utils_test.go + +## 2.1.6 - Feb 15th 2023 + +* Added `GetAndMoveToBack()` and `GetAndMoveToFront()` methods + +## 2.1.5 - Dec 13th 2022 + +* Added `Value()` method + +## 2.1.4 - Dec 12th 2022 + +* Fixed a bug with UTF-8 special characters in JSON keys + +## 2.1.3 - Dec 11th 2022 + +* Added support for JSON marshalling/unmarshalling of wrapper of primitive types + +## 2.1.2 - Dec 10th 2022 +* Allowing to pass options to `New`, to give a capacity hint, or initial data +* Allowing to deserialize nested ordered maps from JSON without having to explicitly instantiate them +* Added the `AddPairs` method + +## 2.1.1 - Dec 9th 2022 +* Fixing a bug with JSON marshalling + +## 2.1.0 - Dec 7th 2022 +* Added support for JSON serialization/deserialization diff --git a/third_party/go-ordered-map/LICENSE b/third_party/go-ordered-map/LICENSE new file mode 100644 index 000000000000..8dada3edaf50 --- /dev/null +++ b/third_party/go-ordered-map/LICENSE @@ -0,0 +1,201 @@ + 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/third_party/go-ordered-map/Makefile b/third_party/go-ordered-map/Makefile new file mode 100644 index 000000000000..6e0e18a1b9ac --- /dev/null +++ b/third_party/go-ordered-map/Makefile @@ -0,0 +1,32 @@ +.DEFAULT_GOAL := all + +.PHONY: all +all: test_with_fuzz lint + +# the TEST_FLAGS env var can be set to eg run only specific tests +TEST_COMMAND = go test -v -count=1 -race -cover $(TEST_FLAGS) + +.PHONY: test +test: + $(TEST_COMMAND) + +.PHONY: bench +bench: + go test -bench=. + +FUZZ_TIME ?= 10s + +# see https://github.com/golang/go/issues/46312 +# and https://stackoverflow.com/a/72673487/4867444 +# if we end up having more fuzz tests +.PHONY: test_with_fuzz +test_with_fuzz: + $(TEST_COMMAND) -fuzz=FuzzRoundTripJSON -fuzztime=$(FUZZ_TIME) + $(TEST_COMMAND) -fuzz=FuzzRoundTripYAML -fuzztime=$(FUZZ_TIME) + +.PHONY: fuzz +fuzz: test_with_fuzz + +.PHONY: lint +lint: + golangci-lint run diff --git a/third_party/go-ordered-map/PROVENANCE.md b/third_party/go-ordered-map/PROVENANCE.md new file mode 100644 index 000000000000..a4980edfbcbf --- /dev/null +++ b/third_party/go-ordered-map/PROVENANCE.md @@ -0,0 +1,18 @@ +# Temporary maintained-YAML backport + +Source: https://github.com/wk8/go-ordered-map +Exact source commit: `85ca4a2b29d3241fa4513f82be3d38fe2392a791` (v2.1.8). +Logical module identity: `github.com/wk8/go-ordered-map/v2`. + +Upstream source, tests, fixtures, licenses and attribution are retained. Upstream +CI metadata and vendored dependency snapshots are excluded; dependency sources +continue to resolve through Go modules. The only code edits select the matching +maintained YAML v2/v3 API, including typed YAML node methods and tests. Module +metadata changes select those parsers and satisfy their Go minima. + +Tracking and temporary ownership: https://github.com/StackVista/stackstate/issues/717 +Removal condition: adopt a compatible owning-library release (or Datadog +lightweight OPA patch release) with maintained YAML and passing consumer tests, +then remove this source and its explicit root/workspace/consumer selections. +Dependency replacements do not propagate: external consumers must explicitly +select this independently addressable nested module as well. diff --git a/third_party/go-ordered-map/README.md b/third_party/go-ordered-map/README.md new file mode 100644 index 000000000000..b02894443773 --- /dev/null +++ b/third_party/go-ordered-map/README.md @@ -0,0 +1,154 @@ +[![Go Reference](https://pkg.go.dev/badge/github.com/wk8/go-ordered-map/v2.svg)](https://pkg.go.dev/github.com/wk8/go-ordered-map/v2) +[![Build Status](https://circleci.com/gh/wk8/go-ordered-map.svg?style=svg)](https://app.circleci.com/pipelines/github/wk8/go-ordered-map) + +# Golang Ordered Maps + +Same as regular maps, but also remembers the order in which keys were inserted, akin to [Python's `collections.OrderedDict`s](https://docs.python.org/3.7/library/collections.html#ordereddict-objects). + +It offers the following features: +* optimal runtime performance (all operations are constant time) +* optimal memory usage (only one copy of values, no unnecessary memory allocation) +* allows iterating from newest or oldest keys indifferently, without memory copy, allowing to `break` the iteration, and in time linear to the number of keys iterated over rather than the total length of the ordered map +* supports any generic types for both keys and values. If you're running go < 1.18, you can use [version 1](https://github.com/wk8/go-ordered-map/tree/v1) that takes and returns generic `interface{}`s instead of using generics +* idiomatic API, akin to that of [`container/list`](https://golang.org/pkg/container/list) +* support for JSON and YAML marshalling + +## Documentation + +[The full documentation is available on pkg.go.dev](https://pkg.go.dev/github.com/wk8/go-ordered-map/v2). + +## Installation +```bash +go get -u github.com/wk8/go-ordered-map/v2 +``` + +Or use your favorite golang vendoring tool! + +## Supported go versions + +Go >= 1.18 is required to use version >= 2 of this library, as it uses generics. + +If you're running go < 1.18, you can use [version 1](https://github.com/wk8/go-ordered-map/tree/v1) instead. + +## Example / usage + +```go +package main + +import ( + "fmt" + + "github.com/wk8/go-ordered-map/v2" +) + +func main() { + om := orderedmap.New[string, string]() + + om.Set("foo", "bar") + om.Set("bar", "baz") + om.Set("coucou", "toi") + + fmt.Println(om.Get("foo")) // => "bar", true + fmt.Println(om.Get("i dont exist")) // => "", false + + // iterating pairs from oldest to newest: + for pair := om.Oldest(); pair != nil; pair = pair.Next() { + fmt.Printf("%s => %s\n", pair.Key, pair.Value) + } // prints: + // foo => bar + // bar => baz + // coucou => toi + + // iterating over the 2 newest pairs: + i := 0 + for pair := om.Newest(); pair != nil; pair = pair.Prev() { + fmt.Printf("%s => %s\n", pair.Key, pair.Value) + i++ + if i >= 2 { + break + } + } // prints: + // coucou => toi + // bar => baz +} +``` + +An `OrderedMap`'s keys must implement `comparable`, and its values can be anything, for example: + +```go +type myStruct struct { + payload string +} + +func main() { + om := orderedmap.New[int, *myStruct]() + + om.Set(12, &myStruct{"foo"}) + om.Set(1, &myStruct{"bar"}) + + value, present := om.Get(12) + if !present { + panic("should be there!") + } + fmt.Println(value.payload) // => foo + + for pair := om.Oldest(); pair != nil; pair = pair.Next() { + fmt.Printf("%d => %s\n", pair.Key, pair.Value.payload) + } // prints: + // 12 => foo + // 1 => bar +} +``` + +Also worth noting that you can provision ordered maps with a capacity hint, as you would do by passing an optional hint to `make(map[K]V, capacity`): +```go +om := orderedmap.New[int, *myStruct](28) +``` + +You can also pass in some initial data to store in the map: +```go +om := orderedmap.New[int, string](orderedmap.WithInitialData[int, string]( + orderedmap.Pair[int, string]{ + Key: 12, + Value: "foo", + }, + orderedmap.Pair[int, string]{ + Key: 28, + Value: "bar", + }, +)) +``` + +`OrderedMap`s also support JSON serialization/deserialization, and preserves order: + +```go +// serialization +data, err := json.Marshal(om) +... + +// deserialization +om := orderedmap.New[string, string]() // or orderedmap.New[int, any](), or any type you expect +err := json.Unmarshal(data, &om) +... +``` + +Similarly, it also supports YAML serialization/deserialization using the yaml.v3 package, which also preserves order: + +```go +// serialization +data, err := yaml.Marshal(om) +... + +// deserialization +om := orderedmap.New[string, string]() // or orderedmap.New[int, any](), or any type you expect +err := yaml.Unmarshal(data, &om) +... +``` + +## Alternatives + +There are several other ordered map golang implementations out there, but I believe that at the time of writing none of them offer the same functionality as this library; more specifically: +* [iancoleman/orderedmap](https://github.com/iancoleman/orderedmap) only accepts `string` keys, its `Delete` operations are linear +* [cevaris/ordered_map](https://github.com/cevaris/ordered_map) uses a channel for iterations, and leaks goroutines if the iteration is interrupted before fully traversing the map +* [mantyr/iterator](https://github.com/mantyr/iterator) also uses a channel for iterations, and its `Delete` operations are linear +* [samdolan/go-ordered-map](https://github.com/samdolan/go-ordered-map) adds unnecessary locking (users should add their own locking instead if they need it), its `Delete` and `Get` operations are linear, iterations trigger a linear memory allocation diff --git a/third_party/go-ordered-map/example_test.go b/third_party/go-ordered-map/example_test.go new file mode 100644 index 000000000000..aeb23be1c7b2 --- /dev/null +++ b/third_party/go-ordered-map/example_test.go @@ -0,0 +1,67 @@ +package orderedmap_test + +import ( + "encoding/json" + "fmt" + + "github.com/wk8/go-ordered-map/v2" +) + +func Example() { + om := orderedmap.New[string, string](3) + + om.Set("foo", "bar") + om.Set("bar", "baz") + om.Set("coucou", "toi") + + fmt.Println("## Get operations: ##") + fmt.Println(om.Get("foo")) + fmt.Println(om.Get("i dont exist")) + fmt.Println(om.Value("coucou")) + + fmt.Println("## Iterating over pairs from oldest to newest: ##") + for pair := om.Oldest(); pair != nil; pair = pair.Next() { + fmt.Printf("%s => %s\n", pair.Key, pair.Value) + } + + fmt.Println("## Iterating over the 2 newest pairs: ##") + i := 0 + for pair := om.Newest(); pair != nil; pair = pair.Prev() { + fmt.Printf("%s => %s\n", pair.Key, pair.Value) + i++ + if i >= 2 { + break + } + } + + fmt.Println("## JSON serialization: ##") + data, err := json.Marshal(om) + if err != nil { + panic(err) + } + fmt.Println(string(data)) + + fmt.Println("## JSON deserialization: ##") + om2 := orderedmap.New[string, string]() + if err := json.Unmarshal(data, &om2); err != nil { + panic(err) + } + fmt.Println(om2.Oldest().Key) + + // Output: + // ## Get operations: ## + // bar true + // false + // toi + // ## Iterating over pairs from oldest to newest: ## + // foo => bar + // bar => baz + // coucou => toi + // ## Iterating over the 2 newest pairs: ## + // coucou => toi + // bar => baz + // ## JSON serialization: ## + // {"foo":"bar","bar":"baz","coucou":"toi"} + // ## JSON deserialization: ## + // foo +} diff --git a/third_party/go-ordered-map/go.mod b/third_party/go-ordered-map/go.mod new file mode 100644 index 000000000000..8df1cec92a3f --- /dev/null +++ b/third_party/go-ordered-map/go.mod @@ -0,0 +1,16 @@ +module github.com/wk8/go-ordered-map/v2 + +go 1.18 + +require ( + github.com/bahlo/generic-list-go v0.2.0 + github.com/buger/jsonparser v1.1.1 + github.com/mailru/easyjson v0.7.7 + github.com/stretchr/testify v1.12.1 + go.yaml.in/yaml/v3 v3.0.5 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect +) diff --git a/third_party/go-ordered-map/go.sum b/third_party/go-ordered-map/go.sum new file mode 100644 index 000000000000..6a094c83a3c4 --- /dev/null +++ b/third_party/go-ordered-map/go.sum @@ -0,0 +1,24 @@ +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/third_party/go-ordered-map/json.go b/third_party/go-ordered-map/json.go new file mode 100644 index 000000000000..a545b536b33d --- /dev/null +++ b/third_party/go-ordered-map/json.go @@ -0,0 +1,182 @@ +package orderedmap + +import ( + "bytes" + "encoding" + "encoding/json" + "fmt" + "reflect" + "unicode/utf8" + + "github.com/buger/jsonparser" + "github.com/mailru/easyjson/jwriter" +) + +var ( + _ json.Marshaler = &OrderedMap[int, any]{} + _ json.Unmarshaler = &OrderedMap[int, any]{} +) + +// MarshalJSON implements the json.Marshaler interface. +func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) { //nolint:funlen + if om == nil || om.list == nil { + return []byte("null"), nil + } + + writer := jwriter.Writer{} + writer.RawByte('{') + + for pair, firstIteration := om.Oldest(), true; pair != nil; pair = pair.Next() { + if firstIteration { + firstIteration = false + } else { + writer.RawByte(',') + } + + switch key := any(pair.Key).(type) { + case string: + writer.String(key) + case encoding.TextMarshaler: + writer.RawByte('"') + writer.Raw(key.MarshalText()) + writer.RawByte('"') + case int: + writer.IntStr(key) + case int8: + writer.Int8Str(key) + case int16: + writer.Int16Str(key) + case int32: + writer.Int32Str(key) + case int64: + writer.Int64Str(key) + case uint: + writer.UintStr(key) + case uint8: + writer.Uint8Str(key) + case uint16: + writer.Uint16Str(key) + case uint32: + writer.Uint32Str(key) + case uint64: + writer.Uint64Str(key) + default: + + // this switch takes care of wrapper types around primitive types, such as + // type myType string + switch keyValue := reflect.ValueOf(key); keyValue.Type().Kind() { + case reflect.String: + writer.String(keyValue.String()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + writer.Int64Str(keyValue.Int()) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + writer.Uint64Str(keyValue.Uint()) + default: + return nil, fmt.Errorf("unsupported key type: %T", key) + } + } + + writer.RawByte(':') + // the error is checked at the end of the function + writer.Raw(json.Marshal(pair.Value)) //nolint:errchkjson + } + + writer.RawByte('}') + + return dumpWriter(&writer) +} + +func dumpWriter(writer *jwriter.Writer) ([]byte, error) { + if writer.Error != nil { + return nil, writer.Error + } + + var buf bytes.Buffer + buf.Grow(writer.Size()) + if _, err := writer.DumpTo(&buf); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error { + if om.list == nil { + om.initialize(0) + } + + return jsonparser.ObjectEach( + data, + func(keyData []byte, valueData []byte, dataType jsonparser.ValueType, offset int) error { + if dataType == jsonparser.String { + // jsonparser removes the enclosing quotes; we need to restore them to make a valid JSON + valueData = data[offset-len(valueData)-2 : offset] + } + + var key K + var value V + + switch typedKey := any(&key).(type) { + case *string: + s, err := decodeUTF8(keyData) + if err != nil { + return err + } + *typedKey = s + case encoding.TextUnmarshaler: + if err := typedKey.UnmarshalText(keyData); err != nil { + return err + } + case *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64: + if err := json.Unmarshal(keyData, typedKey); err != nil { + return err + } + default: + // this switch takes care of wrapper types around primitive types, such as + // type myType string + switch reflect.TypeOf(key).Kind() { + case reflect.String: + s, err := decodeUTF8(keyData) + if err != nil { + return err + } + + convertedKeyData := reflect.ValueOf(s).Convert(reflect.TypeOf(key)) + reflect.ValueOf(&key).Elem().Set(convertedKeyData) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if err := json.Unmarshal(keyData, &key); err != nil { + return err + } + default: + return fmt.Errorf("unsupported key type: %T", key) + } + } + + if err := json.Unmarshal(valueData, &value); err != nil { + return err + } + + om.Set(key, value) + return nil + }) +} + +func decodeUTF8(input []byte) (string, error) { + remaining, offset := input, 0 + runes := make([]rune, 0, len(remaining)) + + for len(remaining) > 0 { + r, size := utf8.DecodeRune(remaining) + if r == utf8.RuneError && size <= 1 { + return "", fmt.Errorf("not a valid UTF-8 string (at position %d): %s", offset, string(input)) + } + + runes = append(runes, r) + remaining = remaining[size:] + offset += size + } + + return string(runes), nil +} diff --git a/third_party/go-ordered-map/json_fuzz_test.go b/third_party/go-ordered-map/json_fuzz_test.go new file mode 100644 index 000000000000..f556cea49ddb --- /dev/null +++ b/third_party/go-ordered-map/json_fuzz_test.go @@ -0,0 +1,117 @@ +package orderedmap + +// Adapted from https://github.com/dvyukov/go-fuzz-corpus/blob/c42c1b2/json/json.go + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func FuzzRoundTripJSON(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + for _, testCase := range []struct { + name string + constructor func() any + // should be a function that asserts that 2 objects of the type returned by constructor are equal + equalityAssertion func(*testing.T, any, any) bool + }{ + { + name: "with a string -> string map", + constructor: func() any { return &OrderedMap[string, string]{} }, + equalityAssertion: assertOrderedMapsEqual[string, string], + }, + { + name: "with a string -> int map", + constructor: func() any { return &OrderedMap[string, int]{} }, + equalityAssertion: assertOrderedMapsEqual[string, int], + }, + { + name: "with a string -> any map", + constructor: func() any { return &OrderedMap[string, any]{} }, + equalityAssertion: assertOrderedMapsEqual[string, any], + }, + { + name: "with a struct with map fields", + constructor: func() any { return new(testFuzzStruct) }, + equalityAssertion: assertTestFuzzStructEqual, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + v1 := testCase.constructor() + if json.Unmarshal(data, v1) != nil { + return + } + + jsonData, err := json.Marshal(v1) + require.NoError(t, err) + + v2 := testCase.constructor() + require.NoError(t, json.Unmarshal(jsonData, v2)) + + if !assert.True(t, testCase.equalityAssertion(t, v1, v2), "failed with input data %q", string(data)) { + // look at that what the standard lib does with regular map, to help with debugging + + var m1 map[string]any + require.NoError(t, json.Unmarshal(data, &m1)) + + mapJsonData, err := json.Marshal(m1) + require.NoError(t, err) + + var m2 map[string]any + require.NoError(t, json.Unmarshal(mapJsonData, &m2)) + + t.Logf("initial data = %s", string(data)) + t.Logf("unmarshalled map = %v", m1) + t.Logf("re-marshalled from map = %s", string(mapJsonData)) + t.Logf("re-marshalled from test obj = %s", string(jsonData)) + t.Logf("re-unmarshalled map = %s", m2) + } + }) + } + }) +} + +// only works for fairly basic maps, that's why it's just in this file +func assertOrderedMapsEqual[K comparable, V any](t *testing.T, v1, v2 any) bool { + om1, ok1 := v1.(*OrderedMap[K, V]) + om2, ok2 := v2.(*OrderedMap[K, V]) + + if !assert.True(t, ok1, "v1 not an orderedmap") || + !assert.True(t, ok2, "v2 not an orderedmap") { + return false + } + + success := assert.Equal(t, om1.Len(), om2.Len(), "om1 and om2 have different lengths: %d vs %d", om1.Len(), om2.Len()) + + for i, pair1, pair2 := 0, om1.Oldest(), om2.Oldest(); pair1 != nil && pair2 != nil; i, pair1, pair2 = i+1, pair1.Next(), pair2.Next() { + success = assert.Equal(t, pair1.Key, pair2.Key, "different keys at position %d: %v vs %v", i, pair1.Key, pair2.Key) && success + success = assert.Equal(t, pair1.Value, pair2.Value, "different values at position %d: %v vs %v", i, pair1.Value, pair2.Value) && success + } + + return success +} + +type testFuzzStruct struct { + M1 *OrderedMap[int, any] + M2 *OrderedMap[int, string] + M3 *OrderedMap[string, string] +} + +func assertTestFuzzStructEqual(t *testing.T, v1, v2 any) bool { + s1, ok := v1.(*testFuzzStruct) + s2, ok := v2.(*testFuzzStruct) + + if !assert.True(t, ok, "v1 not an testFuzzStruct") || + !assert.True(t, ok, "v2 not an testFuzzStruct") { + return false + } + + success := assertOrderedMapsEqual[int, any](t, s1.M1, s2.M1) + success = assertOrderedMapsEqual[int, string](t, s1.M2, s2.M2) && success + success = assertOrderedMapsEqual[string, string](t, s1.M3, s2.M3) && success + + return success +} diff --git a/third_party/go-ordered-map/json_test.go b/third_party/go-ordered-map/json_test.go new file mode 100644 index 000000000000..42b89ab36259 --- /dev/null +++ b/third_party/go-ordered-map/json_test.go @@ -0,0 +1,338 @@ +package orderedmap + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// to test marshalling TextMarshalers and unmarshalling TextUnmarshalers +type marshallable int + +func (m marshallable) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf("#%d#", m)), nil +} + +func (m *marshallable) UnmarshalText(text []byte) error { + if len(text) < 3 { + return errors.New("too short") + } + if text[0] != '#' || text[len(text)-1] != '#' { + return errors.New("missing prefix or suffix") + } + + value, err := strconv.Atoi(string(text[1 : len(text)-1])) + if err != nil { + return err + } + + *m = marshallable(value) + return nil +} + +func TestMarshalJSON(t *testing.T) { + t.Run("int key", func(t *testing.T) { + om := New[int, any]() + om.Set(1, "bar") + om.Set(7, "baz") + om.Set(2, 28) + om.Set(3, 100) + om.Set(4, "baz") + om.Set(5, "28") + om.Set(6, "100") + om.Set(8, "baz") + om.Set(8, "baz") + om.Set(9, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque auctor augue accumsan mi maximus, quis viverra massa pretium. Phasellus imperdiet sapien a interdum sollicitudin. Duis at commodo lectus, a lacinia sem.") + + b, err := json.Marshal(om) + assert.NoError(t, err) + assert.Equal(t, `{"1":"bar","7":"baz","2":28,"3":100,"4":"baz","5":"28","6":"100","8":"baz","9":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque auctor augue accumsan mi maximus, quis viverra massa pretium. Phasellus imperdiet sapien a interdum sollicitudin. Duis at commodo lectus, a lacinia sem."}`, string(b)) + }) + + t.Run("string key", func(t *testing.T) { + om := New[string, any]() + om.Set("test", "bar") + om.Set("abc", true) + + b, err := json.Marshal(om) + assert.NoError(t, err) + assert.Equal(t, `{"test":"bar","abc":true}`, string(b)) + }) + + t.Run("typed string key", func(t *testing.T) { + type myString string + om := New[myString, any]() + om.Set("test", "bar") + om.Set("abc", true) + + b, err := json.Marshal(om) + assert.NoError(t, err) + assert.Equal(t, `{"test":"bar","abc":true}`, string(b)) + }) + + t.Run("typed int key", func(t *testing.T) { + type myInt uint32 + om := New[myInt, any]() + om.Set(1, "bar") + om.Set(7, "baz") + om.Set(2, 28) + om.Set(3, 100) + om.Set(4, "baz") + + b, err := json.Marshal(om) + assert.NoError(t, err) + assert.Equal(t, `{"1":"bar","7":"baz","2":28,"3":100,"4":"baz"}`, string(b)) + }) + + t.Run("TextMarshaller key", func(t *testing.T) { + om := New[marshallable, any]() + om.Set(marshallable(1), "bar") + om.Set(marshallable(28), true) + + b, err := json.Marshal(om) + assert.NoError(t, err) + assert.Equal(t, `{"#1#":"bar","#28#":true}`, string(b)) + }) + + t.Run("empty map", func(t *testing.T) { + om := New[string, any]() + + b, err := json.Marshal(om) + assert.NoError(t, err) + assert.Equal(t, `{}`, string(b)) + }) +} + +func TestUnmarshallJSON(t *testing.T) { + t.Run("int key", func(t *testing.T) { + data := `{"1":"bar","7":"baz","2":28,"3":100,"4":"baz","5":"28","6":"100","8":"baz"}` + + om := New[int, any]() + require.NoError(t, json.Unmarshal([]byte(data), &om)) + + assertOrderedPairsEqual(t, om, + []int{1, 7, 2, 3, 4, 5, 6, 8}, + []any{"bar", "baz", float64(28), float64(100), "baz", "28", "100", "baz"}) + }) + + t.Run("string key", func(t *testing.T) { + data := `{"test":"bar","abc":true}` + + om := New[string, any]() + require.NoError(t, json.Unmarshal([]byte(data), &om)) + + assertOrderedPairsEqual(t, om, + []string{"test", "abc"}, + []any{"bar", true}) + }) + + t.Run("typed string key", func(t *testing.T) { + data := `{"test":"bar","abc":true}` + + type myString string + om := New[myString, any]() + require.NoError(t, json.Unmarshal([]byte(data), &om)) + + assertOrderedPairsEqual(t, om, + []myString{"test", "abc"}, + []any{"bar", true}) + }) + + t.Run("typed int key", func(t *testing.T) { + data := `{"1":"bar","7":"baz","2":28,"3":100,"4":"baz","5":"28","6":"100","8":"baz"}` + + type myInt uint32 + om := New[myInt, any]() + require.NoError(t, json.Unmarshal([]byte(data), &om)) + + assertOrderedPairsEqual(t, om, + []myInt{1, 7, 2, 3, 4, 5, 6, 8}, + []any{"bar", "baz", float64(28), float64(100), "baz", "28", "100", "baz"}) + }) + + t.Run("TextUnmarshaler key", func(t *testing.T) { + data := `{"#1#":"bar","#28#":true}` + + om := New[marshallable, any]() + require.NoError(t, json.Unmarshal([]byte(data), &om)) + + assertOrderedPairsEqual(t, om, + []marshallable{1, 28}, + []any{"bar", true}) + }) + + t.Run("when fed with an input that's not an object", func(t *testing.T) { + for _, data := range []string{"true", `["foo"]`, "42", `"foo"`} { + om := New[int, any]() + require.Error(t, json.Unmarshal([]byte(data), &om)) + } + }) + + t.Run("empty map", func(t *testing.T) { + data := `{}` + + om := New[int, any]() + require.NoError(t, json.Unmarshal([]byte(data), &om)) + + assertLenEqual(t, om, 0) + }) +} + +// const specialCharacters = "\\\\/\"\b\f\n\r\t\x00\uffff\ufffd世界\u007f\u00ff\U0010FFFF" +const specialCharacters = "\uffff\ufffd世界\u007f\u00ff\U0010FFFF" + +func TestJSONSpecialCharacters(t *testing.T) { + baselineMap := map[string]any{specialCharacters: specialCharacters} + baselineData, err := json.Marshal(baselineMap) + require.NoError(t, err) // baseline proves this key is supported by official json library + t.Logf("specialCharacters: %#v as []rune:%v", specialCharacters, []rune(specialCharacters)) + t.Logf("baseline json data: %s", baselineData) + + t.Run("marshal special characters", func(t *testing.T) { + om := New[string, any]() + om.Set(specialCharacters, specialCharacters) + b, err := json.Marshal(om) + require.NoError(t, err) + require.Equal(t, baselineData, b) + + type myString string + om2 := New[myString, myString]() + om2.Set(specialCharacters, specialCharacters) + b, err = json.Marshal(om2) + require.NoError(t, err) + require.Equal(t, baselineData, b) + }) + + t.Run("unmarshall special characters", func(t *testing.T) { + om := New[string, any]() + require.NoError(t, json.Unmarshal(baselineData, &om)) + assertOrderedPairsEqual(t, om, + []string{specialCharacters}, + []any{specialCharacters}) + + type myString string + om2 := New[myString, myString]() + require.NoError(t, json.Unmarshal(baselineData, &om2)) + assertOrderedPairsEqual(t, om2, + []myString{specialCharacters}, + []myString{specialCharacters}) + }) +} + +// to test structs that have nested map fields +type nestedMaps struct { + X int `json:"x" yaml:"x"` + M *OrderedMap[string, []*OrderedMap[int, *OrderedMap[string, any]]] `json:"m" yaml:"m"` +} + +func TestJSONRoundTrip(t *testing.T) { + for _, testCase := range []struct { + name string + input string + targetFactory func() any + isPrettyPrinted bool + }{ + { + name: "", + input: `{ + "x": 28, + "m": { + "foo": [ + { + "12": { + "i": 12, + "b": true, + "n": null, + "m": { + "a": "b", + "c": 28 + } + }, + "28": { + "a": false, + "b": [ + 1, + 2, + 3 + ] + } + }, + { + "3": { + "c": null, + "d": 87 + }, + "4": { + "e": true + }, + "5": { + "f": 4, + "g": 5, + "h": 6 + } + } + ], + "bar": [ + { + "5": { + "foo": "bar" + } + } + ] + } +}`, + targetFactory: func() any { return &nestedMaps{} }, + isPrettyPrinted: true, + }, + { + name: "with UTF-8 special chars in key", + input: `{"�":0}`, + targetFactory: func() any { return &OrderedMap[string, int]{} }, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + target := testCase.targetFactory() + + require.NoError(t, json.Unmarshal([]byte(testCase.input), target)) + + var ( + out []byte + err error + ) + if testCase.isPrettyPrinted { + out, err = json.MarshalIndent(target, "", " ") + } else { + out, err = json.Marshal(target) + } + + if assert.NoError(t, err) { + assert.Equal(t, strings.TrimSpace(testCase.input), string(out)) + } + }) + } +} + +func BenchmarkMarshalJSON(b *testing.B) { + om := New[int, any]() + om.Set(1, "bar") + om.Set(7, "baz") + om.Set(2, 28) + om.Set(3, 100) + om.Set(4, "baz") + om.Set(5, "28") + om.Set(6, "100") + om.Set(8, "baz") + om.Set(8, "baz") + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, _ = json.Marshal(om) + } +} diff --git a/third_party/go-ordered-map/orderedmap.go b/third_party/go-ordered-map/orderedmap.go new file mode 100644 index 000000000000..0647141919ae --- /dev/null +++ b/third_party/go-ordered-map/orderedmap.go @@ -0,0 +1,296 @@ +// Package orderedmap implements an ordered map, i.e. a map that also keeps track of +// the order in which keys were inserted. +// +// All operations are constant-time. +// +// Github repo: https://github.com/wk8/go-ordered-map +// +package orderedmap + +import ( + "fmt" + + list "github.com/bahlo/generic-list-go" +) + +type Pair[K comparable, V any] struct { + Key K + Value V + + element *list.Element[*Pair[K, V]] +} + +type OrderedMap[K comparable, V any] struct { + pairs map[K]*Pair[K, V] + list *list.List[*Pair[K, V]] +} + +type initConfig[K comparable, V any] struct { + capacity int + initialData []Pair[K, V] +} + +type InitOption[K comparable, V any] func(config *initConfig[K, V]) + +// WithCapacity allows giving a capacity hint for the map, akin to the standard make(map[K]V, capacity). +func WithCapacity[K comparable, V any](capacity int) InitOption[K, V] { + return func(c *initConfig[K, V]) { + c.capacity = capacity + } +} + +// WithInitialData allows passing in initial data for the map. +func WithInitialData[K comparable, V any](initialData ...Pair[K, V]) InitOption[K, V] { + return func(c *initConfig[K, V]) { + c.initialData = initialData + if c.capacity < len(initialData) { + c.capacity = len(initialData) + } + } +} + +// New creates a new OrderedMap. +// options can either be one or several InitOption[K, V], or a single integer, +// which is then interpreted as a capacity hint, à la make(map[K]V, capacity). +func New[K comparable, V any](options ...any) *OrderedMap[K, V] { //nolint:varnamelen + orderedMap := &OrderedMap[K, V]{} + + var config initConfig[K, V] + for _, untypedOption := range options { + switch option := untypedOption.(type) { + case int: + if len(options) != 1 { + invalidOption() + } + config.capacity = option + + case InitOption[K, V]: + option(&config) + + default: + invalidOption() + } + } + + orderedMap.initialize(config.capacity) + orderedMap.AddPairs(config.initialData...) + + return orderedMap +} + +const invalidOptionMessage = `when using orderedmap.New[K,V]() with options, either provide one or several InitOption[K, V]; or a single integer which is then interpreted as a capacity hint, à la make(map[K]V, capacity).` //nolint:lll + +func invalidOption() { panic(invalidOptionMessage) } + +func (om *OrderedMap[K, V]) initialize(capacity int) { + om.pairs = make(map[K]*Pair[K, V], capacity) + om.list = list.New[*Pair[K, V]]() +} + +// Get looks for the given key, and returns the value associated with it, +// or V's nil value if not found. The boolean it returns says whether the key is present in the map. +func (om *OrderedMap[K, V]) Get(key K) (val V, present bool) { + if pair, present := om.pairs[key]; present { + return pair.Value, true + } + + return +} + +// Load is an alias for Get, mostly to present an API similar to `sync.Map`'s. +func (om *OrderedMap[K, V]) Load(key K) (V, bool) { + return om.Get(key) +} + +// Value returns the value associated with the given key or the zero value. +func (om *OrderedMap[K, V]) Value(key K) (val V) { + if pair, present := om.pairs[key]; present { + val = pair.Value + } + return +} + +// GetPair looks for the given key, and returns the pair associated with it, +// or nil if not found. The Pair struct can then be used to iterate over the ordered map +// from that point, either forward or backward. +func (om *OrderedMap[K, V]) GetPair(key K) *Pair[K, V] { + return om.pairs[key] +} + +// Set sets the key-value pair, and returns what `Get` would have returned +// on that key prior to the call to `Set`. +func (om *OrderedMap[K, V]) Set(key K, value V) (val V, present bool) { + if pair, present := om.pairs[key]; present { + oldValue := pair.Value + pair.Value = value + return oldValue, true + } + + pair := &Pair[K, V]{ + Key: key, + Value: value, + } + pair.element = om.list.PushBack(pair) + om.pairs[key] = pair + + return +} + +// AddPairs allows setting multiple pairs at a time. It's equivalent to calling +// Set on each pair sequentially. +func (om *OrderedMap[K, V]) AddPairs(pairs ...Pair[K, V]) { + for _, pair := range pairs { + om.Set(pair.Key, pair.Value) + } +} + +// Store is an alias for Set, mostly to present an API similar to `sync.Map`'s. +func (om *OrderedMap[K, V]) Store(key K, value V) (V, bool) { + return om.Set(key, value) +} + +// Delete removes the key-value pair, and returns what `Get` would have returned +// on that key prior to the call to `Delete`. +func (om *OrderedMap[K, V]) Delete(key K) (val V, present bool) { + if pair, present := om.pairs[key]; present { + om.list.Remove(pair.element) + delete(om.pairs, key) + return pair.Value, true + } + return +} + +// Len returns the length of the ordered map. +func (om *OrderedMap[K, V]) Len() int { + if om == nil || om.pairs == nil { + return 0 + } + return len(om.pairs) +} + +// Oldest returns a pointer to the oldest pair. It's meant to be used to iterate on the ordered map's +// pairs from the oldest to the newest, e.g.: +// for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { fmt.Printf("%v => %v\n", pair.Key, pair.Value) } +func (om *OrderedMap[K, V]) Oldest() *Pair[K, V] { + if om == nil || om.list == nil { + return nil + } + return listElementToPair(om.list.Front()) +} + +// Newest returns a pointer to the newest pair. It's meant to be used to iterate on the ordered map's +// pairs from the newest to the oldest, e.g.: +// for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { fmt.Printf("%v => %v\n", pair.Key, pair.Value) } +func (om *OrderedMap[K, V]) Newest() *Pair[K, V] { + if om == nil || om.list == nil { + return nil + } + return listElementToPair(om.list.Back()) +} + +// Next returns a pointer to the next pair. +func (p *Pair[K, V]) Next() *Pair[K, V] { + return listElementToPair(p.element.Next()) +} + +// Prev returns a pointer to the previous pair. +func (p *Pair[K, V]) Prev() *Pair[K, V] { + return listElementToPair(p.element.Prev()) +} + +func listElementToPair[K comparable, V any](element *list.Element[*Pair[K, V]]) *Pair[K, V] { + if element == nil { + return nil + } + return element.Value +} + +// KeyNotFoundError may be returned by functions in this package when they're called with keys that are not present +// in the map. +type KeyNotFoundError[K comparable] struct { + MissingKey K +} + +func (e *KeyNotFoundError[K]) Error() string { + return fmt.Sprintf("missing key: %v", e.MissingKey) +} + +// MoveAfter moves the value associated with key to its new position after the one associated with markKey. +// Returns an error iff key or markKey are not present in the map. If an error is returned, +// it will be a KeyNotFoundError. +func (om *OrderedMap[K, V]) MoveAfter(key, markKey K) error { + elements, err := om.getElements(key, markKey) + if err != nil { + return err + } + om.list.MoveAfter(elements[0], elements[1]) + return nil +} + +// MoveBefore moves the value associated with key to its new position before the one associated with markKey. +// Returns an error iff key or markKey are not present in the map. If an error is returned, +// it will be a KeyNotFoundError. +func (om *OrderedMap[K, V]) MoveBefore(key, markKey K) error { + elements, err := om.getElements(key, markKey) + if err != nil { + return err + } + om.list.MoveBefore(elements[0], elements[1]) + return nil +} + +func (om *OrderedMap[K, V]) getElements(keys ...K) ([]*list.Element[*Pair[K, V]], error) { + elements := make([]*list.Element[*Pair[K, V]], len(keys)) + for i, k := range keys { + pair, present := om.pairs[k] + if !present { + return nil, &KeyNotFoundError[K]{k} + } + elements[i] = pair.element + } + return elements, nil +} + +// MoveToBack moves the value associated with key to the back of the ordered map, +// i.e. makes it the newest pair in the map. +// Returns an error iff key is not present in the map. If an error is returned, +// it will be a KeyNotFoundError. +func (om *OrderedMap[K, V]) MoveToBack(key K) error { + _, err := om.GetAndMoveToBack(key) + return err +} + +// MoveToFront moves the value associated with key to the front of the ordered map, +// i.e. makes it the oldest pair in the map. +// Returns an error iff key is not present in the map. If an error is returned, +// it will be a KeyNotFoundError. +func (om *OrderedMap[K, V]) MoveToFront(key K) error { + _, err := om.GetAndMoveToFront(key) + return err +} + +// GetAndMoveToBack combines Get and MoveToBack in the same call. If an error is returned, +// it will be a KeyNotFoundError. +func (om *OrderedMap[K, V]) GetAndMoveToBack(key K) (val V, err error) { + if pair, present := om.pairs[key]; present { + val = pair.Value + om.list.MoveToBack(pair.element) + } else { + err = &KeyNotFoundError[K]{key} + } + + return +} + +// GetAndMoveToFront combines Get and MoveToFront in the same call. If an error is returned, +// it will be a KeyNotFoundError. +func (om *OrderedMap[K, V]) GetAndMoveToFront(key K) (val V, err error) { + if pair, present := om.pairs[key]; present { + val = pair.Value + om.list.MoveToFront(pair.element) + } else { + err = &KeyNotFoundError[K]{key} + } + + return +} diff --git a/third_party/go-ordered-map/orderedmap_test.go b/third_party/go-ordered-map/orderedmap_test.go new file mode 100644 index 000000000000..a3137eb054a0 --- /dev/null +++ b/third_party/go-ordered-map/orderedmap_test.go @@ -0,0 +1,384 @@ +package orderedmap + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBasicFeatures(t *testing.T) { + n := 100 + om := New[int, int]() + + // set(i, 2 * i) + for i := 0; i < n; i++ { + assertLenEqual(t, om, i) + oldValue, present := om.Set(i, 2*i) + assertLenEqual(t, om, i+1) + + assert.Equal(t, 0, oldValue) + assert.False(t, present) + } + + // get what we just set + for i := 0; i < n; i++ { + value, present := om.Get(i) + + assert.Equal(t, 2*i, value) + assert.Equal(t, value, om.Value(i)) + assert.True(t, present) + } + + // get pairs of what we just set + for i := 0; i < n; i++ { + pair := om.GetPair(i) + + assert.NotNil(t, pair) + assert.Equal(t, 2*i, pair.Value) + } + + // forward iteration + i := 0 + for pair := om.Oldest(); pair != nil; pair = pair.Next() { + assert.Equal(t, i, pair.Key) + assert.Equal(t, 2*i, pair.Value) + i++ + } + // backward iteration + i = n - 1 + for pair := om.Newest(); pair != nil; pair = pair.Prev() { + assert.Equal(t, i, pair.Key) + assert.Equal(t, 2*i, pair.Value) + i-- + } + + // forward iteration starting from known key + i = 42 + for pair := om.GetPair(i); pair != nil; pair = pair.Next() { + assert.Equal(t, i, pair.Key) + assert.Equal(t, 2*i, pair.Value) + i++ + } + + // double values for pairs with even keys + for j := 0; j < n/2; j++ { + i = 2 * j + oldValue, present := om.Set(i, 4*i) + + assert.Equal(t, 2*i, oldValue) + assert.True(t, present) + } + // and delete pairs with odd keys + for j := 0; j < n/2; j++ { + i = 2*j + 1 + assertLenEqual(t, om, n-j) + value, present := om.Delete(i) + assertLenEqual(t, om, n-j-1) + + assert.Equal(t, 2*i, value) + assert.True(t, present) + + // deleting again shouldn't change anything + value, present = om.Delete(i) + assertLenEqual(t, om, n-j-1) + assert.Equal(t, 0, value) + assert.False(t, present) + } + + // get the whole range + for j := 0; j < n/2; j++ { + i = 2 * j + value, present := om.Get(i) + assert.Equal(t, 4*i, value) + assert.Equal(t, value, om.Value(i)) + assert.True(t, present) + + i = 2*j + 1 + value, present = om.Get(i) + assert.Equal(t, 0, value) + assert.Equal(t, value, om.Value(i)) + assert.False(t, present) + } + + // check iterations again + i = 0 + for pair := om.Oldest(); pair != nil; pair = pair.Next() { + assert.Equal(t, i, pair.Key) + assert.Equal(t, 4*i, pair.Value) + i += 2 + } + i = 2 * ((n - 1) / 2) + for pair := om.Newest(); pair != nil; pair = pair.Prev() { + assert.Equal(t, i, pair.Key) + assert.Equal(t, 4*i, pair.Value) + i -= 2 + } +} + +func TestUpdatingDoesntChangePairsOrder(t *testing.T) { + om := New[string, any]() + om.Set("foo", "bar") + om.Set("wk", 28) + om.Set("po", 100) + om.Set("bar", "baz") + + oldValue, present := om.Set("po", 102) + assert.Equal(t, 100, oldValue) + assert.True(t, present) + + assertOrderedPairsEqual(t, om, + []string{"foo", "wk", "po", "bar"}, + []any{"bar", 28, 102, "baz"}) +} + +func TestDeletingAndReinsertingChangesPairsOrder(t *testing.T) { + om := New[string, any]() + om.Set("foo", "bar") + om.Set("wk", 28) + om.Set("po", 100) + om.Set("bar", "baz") + + // delete a pair + oldValue, present := om.Delete("po") + assert.Equal(t, 100, oldValue) + assert.True(t, present) + + // re-insert the same pair + oldValue, present = om.Set("po", 100) + assert.Nil(t, oldValue) + assert.False(t, present) + + assertOrderedPairsEqual(t, om, + []string{"foo", "wk", "bar", "po"}, + []any{"bar", 28, "baz", 100}) +} + +func TestEmptyMapOperations(t *testing.T) { + om := New[string, any]() + + oldValue, present := om.Get("foo") + assert.Nil(t, oldValue) + assert.Nil(t, om.Value("foo")) + assert.False(t, present) + + oldValue, present = om.Delete("bar") + assert.Nil(t, oldValue) + assert.False(t, present) + + assertLenEqual(t, om, 0) + + assert.Nil(t, om.Oldest()) + assert.Nil(t, om.Newest()) +} + +type dummyTestStruct struct { + value string +} + +func TestPackUnpackStructs(t *testing.T) { + om := New[string, dummyTestStruct]() + om.Set("foo", dummyTestStruct{"foo!"}) + om.Set("bar", dummyTestStruct{"bar!"}) + + value, present := om.Get("foo") + assert.True(t, present) + assert.Equal(t, value, om.Value("foo")) + if assert.NotNil(t, value) { + assert.Equal(t, "foo!", value.value) + } + + value, present = om.Set("bar", dummyTestStruct{"baz!"}) + assert.True(t, present) + if assert.NotNil(t, value) { + assert.Equal(t, "bar!", value.value) + } + + value, present = om.Get("bar") + assert.Equal(t, value, om.Value("bar")) + assert.True(t, present) + if assert.NotNil(t, value) { + assert.Equal(t, "baz!", value.value) + } +} + +// shamelessly stolen from https://github.com/python/cpython/blob/e19a91e45fd54a56e39c2d12e6aaf4757030507f/Lib/test/test_ordered_dict.py#L55-L61 +func TestShuffle(t *testing.T) { + ranLen := 100 + + for _, n := range []int{0, 10, 20, 100, 1000, 10000} { + t.Run(fmt.Sprintf("shuffle test with %d items", n), func(t *testing.T) { + om := New[string, string]() + + keys := make([]string, n) + values := make([]string, n) + + for i := 0; i < n; i++ { + // we prefix with the number to ensure that we don't get any duplicates + keys[i] = fmt.Sprintf("%d_%s", i, randomHexString(t, ranLen)) + values[i] = randomHexString(t, ranLen) + + value, present := om.Set(keys[i], values[i]) + assert.Equal(t, "", value) + assert.False(t, present) + } + + assertOrderedPairsEqual(t, om, keys, values) + }) + } +} + +func TestMove(t *testing.T) { + om := New[int, any]() + om.Set(1, "bar") + om.Set(2, 28) + om.Set(3, 100) + om.Set(4, "baz") + om.Set(5, "28") + om.Set(6, "100") + om.Set(7, "baz") + om.Set(8, "baz") + + err := om.MoveAfter(2, 3) + assert.Nil(t, err) + assertOrderedPairsEqual(t, om, + []int{1, 3, 2, 4, 5, 6, 7, 8}, + []any{"bar", 100, 28, "baz", "28", "100", "baz", "baz"}) + + err = om.MoveBefore(6, 4) + assert.Nil(t, err) + assertOrderedPairsEqual(t, om, + []int{1, 3, 2, 6, 4, 5, 7, 8}, + []any{"bar", 100, 28, "100", "baz", "28", "baz", "baz"}) + + err = om.MoveToBack(3) + assert.Nil(t, err) + assertOrderedPairsEqual(t, om, + []int{1, 2, 6, 4, 5, 7, 8, 3}, + []any{"bar", 28, "100", "baz", "28", "baz", "baz", 100}) + + err = om.MoveToFront(5) + assert.Nil(t, err) + assertOrderedPairsEqual(t, om, + []int{5, 1, 2, 6, 4, 7, 8, 3}, + []any{"28", "bar", 28, "100", "baz", "baz", "baz", 100}) + + err = om.MoveToFront(100) + assert.Equal(t, &KeyNotFoundError[int]{100}, err) +} + +func TestGetAndMove(t *testing.T) { + om := New[int, any]() + om.Set(1, "bar") + om.Set(2, 28) + om.Set(3, 100) + om.Set(4, "baz") + om.Set(5, "28") + om.Set(6, "100") + om.Set(7, "baz") + om.Set(8, "baz") + + value, err := om.GetAndMoveToBack(3) + assert.Nil(t, err) + assert.Equal(t, 100, value) + assertOrderedPairsEqual(t, om, + []int{1, 2, 4, 5, 6, 7, 8, 3}, + []any{"bar", 28, "baz", "28", "100", "baz", "baz", 100}) + + value, err = om.GetAndMoveToFront(5) + assert.Nil(t, err) + assert.Equal(t, "28", value) + assertOrderedPairsEqual(t, om, + []int{5, 1, 2, 4, 6, 7, 8, 3}, + []any{"28", "bar", 28, "baz", "100", "baz", "baz", 100}) + + value, err = om.GetAndMoveToBack(100) + assert.Equal(t, &KeyNotFoundError[int]{100}, err) +} + +func TestAddPairs(t *testing.T) { + om := New[int, any]() + om.AddPairs( + Pair[int, any]{ + Key: 28, + Value: "foo", + }, + Pair[int, any]{ + Key: 12, + Value: "bar", + }, + Pair[int, any]{ + Key: 28, + Value: "baz", + }, + ) + + assertOrderedPairsEqual(t, om, + []int{28, 12}, + []any{"baz", "bar"}) +} + +// sadly, we can't test the "actual" capacity here, see https://github.com/golang/go/issues/52157 +func TestNewWithCapacity(t *testing.T) { + zero := New[int, string](0) + assert.Empty(t, zero.Len()) + + assert.PanicsWithValue(t, invalidOptionMessage, func() { + _ = New[int, string](1, 2) + }) + assert.PanicsWithValue(t, invalidOptionMessage, func() { + _ = New[int, string](1, 2, 3) + }) + + om := New[int, string](-1) + om.Set(1337, "quarante-deux") + assert.Equal(t, 1, om.Len()) +} + +func TestNewWithOptions(t *testing.T) { + t.Run("wih capacity", func(t *testing.T) { + om := New[string, any](WithCapacity[string, any](98)) + assert.Equal(t, 0, om.Len()) + }) + + t.Run("with initial data", func(t *testing.T) { + om := New[string, int](WithInitialData( + Pair[string, int]{ + Key: "a", + Value: 1, + }, + Pair[string, int]{ + Key: "b", + Value: 2, + }, + Pair[string, int]{ + Key: "c", + Value: 3, + }, + )) + + assertOrderedPairsEqual(t, om, + []string{"a", "b", "c"}, + []int{1, 2, 3}) + }) + + t.Run("with an invalid option type", func(t *testing.T) { + assert.PanicsWithValue(t, invalidOptionMessage, func() { + _ = New[int, string]("foo") + }) + }) +} + +func TestNilMap(t *testing.T) { + // we want certain behaviors of a nil ordered map to be the same as they are for standard nil maps + var om *OrderedMap[int, any] + + t.Run("len", func(t *testing.T) { + assert.Equal(t, 0, om.Len()) + }) + + t.Run("iterating - akin to range", func(t *testing.T) { + assert.Nil(t, om.Oldest()) + assert.Nil(t, om.Newest()) + }) +} diff --git a/third_party/go-ordered-map/testdata/fuzz/FuzzRoundTripJSON/62c005f96216d8ba8f62ac0799dfc1a6893e68418238a831ee79cd9c39b4cfc6 b/third_party/go-ordered-map/testdata/fuzz/FuzzRoundTripJSON/62c005f96216d8ba8f62ac0799dfc1a6893e68418238a831ee79cd9c39b4cfc6 new file mode 100644 index 000000000000..7e3db0878d80 --- /dev/null +++ b/third_party/go-ordered-map/testdata/fuzz/FuzzRoundTripJSON/62c005f96216d8ba8f62ac0799dfc1a6893e68418238a831ee79cd9c39b4cfc6 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\"\xcc\":0}") diff --git a/third_party/go-ordered-map/testdata/fuzz/FuzzRoundTripJSON/8093511184ad3e258aa13b957e75ff26c7fae64672dae0c0bc0a9fa5b61a05e7 b/third_party/go-ordered-map/testdata/fuzz/FuzzRoundTripJSON/8093511184ad3e258aa13b957e75ff26c7fae64672dae0c0bc0a9fa5b61a05e7 new file mode 100644 index 000000000000..3f1f65eca83f --- /dev/null +++ b/third_party/go-ordered-map/testdata/fuzz/FuzzRoundTripJSON/8093511184ad3e258aa13b957e75ff26c7fae64672dae0c0bc0a9fa5b61a05e7 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{}") diff --git a/third_party/go-ordered-map/utils_test.go b/third_party/go-ordered-map/utils_test.go new file mode 100644 index 000000000000..9f1cc3bf906e --- /dev/null +++ b/third_party/go-ordered-map/utils_test.go @@ -0,0 +1,76 @@ +package orderedmap + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +// assertOrderedPairsEqual asserts that the map contains the given keys and values +// from oldest to newest. +func assertOrderedPairsEqual[K comparable, V any]( + t *testing.T, orderedMap *OrderedMap[K, V], expectedKeys []K, expectedValues []V, +) { + t.Helper() + + assertOrderedPairsEqualFromNewest(t, orderedMap, expectedKeys, expectedValues) + assertOrderedPairsEqualFromOldest(t, orderedMap, expectedKeys, expectedValues) +} + +func assertOrderedPairsEqualFromNewest[K comparable, V any]( + t *testing.T, orderedMap *OrderedMap[K, V], expectedKeys []K, expectedValues []V, +) { + t.Helper() + + if assert.Equal(t, len(expectedKeys), len(expectedValues)) && assert.Equal(t, len(expectedKeys), orderedMap.Len()) { + i := orderedMap.Len() - 1 + for pair := orderedMap.Newest(); pair != nil; pair = pair.Prev() { + assert.Equal(t, expectedKeys[i], pair.Key, "from newest index=%d on key", i) + assert.Equal(t, expectedValues[i], pair.Value, "from newest index=%d on value", i) + i-- + } + } +} + +func assertOrderedPairsEqualFromOldest[K comparable, V any]( + t *testing.T, orderedMap *OrderedMap[K, V], expectedKeys []K, expectedValues []V, +) { + t.Helper() + + if assert.Equal(t, len(expectedKeys), len(expectedValues)) && assert.Equal(t, len(expectedKeys), orderedMap.Len()) { + i := 0 + for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { + assert.Equal(t, expectedKeys[i], pair.Key, "from oldest index=%d on key", i) + assert.Equal(t, expectedValues[i], pair.Value, "from oldest index=%d on value", i) + i++ + } + } +} + +func assertLenEqual[K comparable, V any](t *testing.T, orderedMap *OrderedMap[K, V], expectedLen int) { + t.Helper() + + assert.Equal(t, expectedLen, orderedMap.Len()) + + // also check the list length, for good measure + assert.Equal(t, expectedLen, orderedMap.list.Len()) +} + +func randomHexString(t *testing.T, length int) string { + t.Helper() + + b := length / 2 //nolint:gomnd + randBytes := make([]byte, b) + + if n, err := rand.Read(randBytes); err != nil || n != b { + if err == nil { + err = fmt.Errorf("only got %v random bytes, expected %v", n, b) + } + t.Fatal(err) + } + + return hex.EncodeToString(randBytes) +} diff --git a/third_party/go-ordered-map/yaml.go b/third_party/go-ordered-map/yaml.go new file mode 100644 index 000000000000..5366401ab830 --- /dev/null +++ b/third_party/go-ordered-map/yaml.go @@ -0,0 +1,71 @@ +package orderedmap + +import ( + "fmt" + + "go.yaml.in/yaml/v3" +) + +var ( + _ yaml.Marshaler = &OrderedMap[int, any]{} + _ yaml.Unmarshaler = &OrderedMap[int, any]{} +) + +// MarshalYAML implements the yaml.Marshaler interface. +func (om *OrderedMap[K, V]) MarshalYAML() (interface{}, error) { + if om == nil { + return []byte("null"), nil + } + + node := yaml.Node{ + Kind: yaml.MappingNode, + } + + for pair := om.Oldest(); pair != nil; pair = pair.Next() { + key, value := pair.Key, pair.Value + + keyNode := &yaml.Node{} + + // serialize key to yaml, then deserialize it back into the node + // this is a hack to get the correct tag for the key + if err := keyNode.Encode(key); err != nil { + return nil, err + } + + valueNode := &yaml.Node{} + if err := valueNode.Encode(value); err != nil { + return nil, err + } + + node.Content = append(node.Content, keyNode, valueNode) + } + + return &node, nil +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (om *OrderedMap[K, V]) UnmarshalYAML(value *yaml.Node) error { + if value.Kind != yaml.MappingNode { + return fmt.Errorf("pipeline must contain YAML mapping, has %v", value.Kind) + } + + if om.list == nil { + om.initialize(0) + } + + for index := 0; index < len(value.Content); index += 2 { + var key K + var val V + + if err := value.Content[index].Decode(&key); err != nil { + return err + } + if err := value.Content[index+1].Decode(&val); err != nil { + return err + } + + om.Set(key, val) + } + + return nil +} diff --git a/third_party/go-ordered-map/yaml_fuzz_test.go b/third_party/go-ordered-map/yaml_fuzz_test.go new file mode 100644 index 000000000000..453a6a9496d9 --- /dev/null +++ b/third_party/go-ordered-map/yaml_fuzz_test.go @@ -0,0 +1,81 @@ +package orderedmap + +// Adapted from https://github.com/dvyukov/go-fuzz-corpus/blob/c42c1b2/json/json.go + +import ( + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.yaml.in/yaml/v3" + "testing" +) + +func FuzzRoundTripYAML(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + for _, testCase := range []struct { + name string + constructor func() any + // should be a function that asserts that 2 objects of the type returned by constructor are equal + equalityAssertion func(*testing.T, any, any) bool + }{ + { + name: "with a string -> string map", + constructor: func() any { return &OrderedMap[string, string]{} }, + equalityAssertion: assertOrderedMapsEqual[string, string], + }, + { + name: "with a string -> int map", + constructor: func() any { return &OrderedMap[string, int]{} }, + equalityAssertion: assertOrderedMapsEqual[string, int], + }, + { + name: "with a string -> any map", + constructor: func() any { return &OrderedMap[string, any]{} }, + equalityAssertion: assertOrderedMapsEqual[string, any], + }, + { + name: "with a struct with map fields", + constructor: func() any { return new(testFuzzStruct) }, + equalityAssertion: assertTestFuzzStructEqual, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + v1 := testCase.constructor() + if yaml.Unmarshal(data, v1) != nil { + return + } + t.Log(data) + t.Log(v1) + + yamlData, err := yaml.Marshal(v1) + require.NoError(t, err) + t.Log(string(yamlData)) + + v2 := testCase.constructor() + err = yaml.Unmarshal(yamlData, v2) + if err != nil { + t.Log(string(yamlData)) + t.Fatal(err) + } + + if !assert.True(t, testCase.equalityAssertion(t, v1, v2), "failed with input data %q", string(data)) { + // look at that what the standard lib does with regular map, to help with debugging + + var m1 map[string]any + require.NoError(t, yaml.Unmarshal(data, &m1)) + + mapJsonData, err := yaml.Marshal(m1) + require.NoError(t, err) + + var m2 map[string]any + require.NoError(t, yaml.Unmarshal(mapJsonData, &m2)) + + t.Logf("initial data = %s", string(data)) + t.Logf("unmarshalled map = %v", m1) + t.Logf("re-marshalled from map = %s", string(mapJsonData)) + t.Logf("re-marshalled from test obj = %s", string(yamlData)) + t.Logf("re-unmarshalled map = %s", m2) + } + }) + } + }) +} diff --git a/third_party/go-ordered-map/yaml_test.go b/third_party/go-ordered-map/yaml_test.go new file mode 100644 index 000000000000..dcf3df6599c1 --- /dev/null +++ b/third_party/go-ordered-map/yaml_test.go @@ -0,0 +1,333 @@ +package orderedmap + +import ( + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.yaml.in/yaml/v3" + "testing" +) + +func TestMarshalYAML(t *testing.T) { + t.Run("int key", func(t *testing.T) { + om := New[int, any]() + om.Set(1, "bar") + om.Set(7, "baz") + om.Set(2, 28) + om.Set(3, 100) + om.Set(4, "baz") + om.Set(5, "28") + om.Set(6, "100") + om.Set(8, "baz") + om.Set(8, "baz") + om.Set(9, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque auctor augue accumsan mi maximus, quis viverra massa pretium. Phasellus imperdiet sapien a interdum sollicitudin. Duis at commodo lectus, a lacinia sem.") + + b, err := yaml.Marshal(om) + + expected := `1: bar +7: baz +2: 28 +3: 100 +4: baz +5: "28" +6: "100" +8: baz +9: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque auctor augue accumsan mi maximus, quis viverra massa pretium. Phasellus imperdiet sapien a interdum sollicitudin. Duis at commodo lectus, a lacinia sem. +` + assert.NoError(t, err) + assert.Equal(t, expected, string(b)) + }) + + t.Run("string key", func(t *testing.T) { + om := New[string, any]() + om.Set("test", "bar") + om.Set("abc", true) + + b, err := yaml.Marshal(om) + assert.NoError(t, err) + expected := `test: bar +abc: true +` + assert.Equal(t, expected, string(b)) + }) + + t.Run("typed string key", func(t *testing.T) { + type myString string + om := New[myString, any]() + om.Set("test", "bar") + om.Set("abc", true) + + b, err := yaml.Marshal(om) + assert.NoError(t, err) + assert.Equal(t, `test: bar +abc: true +`, string(b)) + }) + + t.Run("typed int key", func(t *testing.T) { + type myInt uint32 + om := New[myInt, any]() + om.Set(1, "bar") + om.Set(7, "baz") + om.Set(2, 28) + om.Set(3, 100) + om.Set(4, "baz") + + b, err := yaml.Marshal(om) + assert.NoError(t, err) + assert.Equal(t, `1: bar +7: baz +2: 28 +3: 100 +4: baz +`, string(b)) + }) + + t.Run("TextMarshaller key", func(t *testing.T) { + om := New[marshallable, any]() + om.Set(marshallable(1), "bar") + om.Set(marshallable(28), true) + + b, err := yaml.Marshal(om) + assert.NoError(t, err) + assert.Equal(t, `'#1#': bar +'#28#': true +`, string(b)) + }) + + t.Run("empty map with 0 elements", func(t *testing.T) { + om := New[string, any]() + + b, err := yaml.Marshal(om) + assert.NoError(t, err) + assert.Equal(t, "{}\n", string(b)) + }) + + t.Run("empty map with no elements (null)", func(t *testing.T) { + om := &OrderedMap[string, string]{} + + b, err := yaml.Marshal(om) + assert.NoError(t, err) + assert.Equal(t, "{}\n", string(b)) + }) +} + +func TestUnmarshallYAML(t *testing.T) { + t.Run("int key", func(t *testing.T) { + data := ` +1: bar +7: baz +2: 28 +3: 100 +4: baz +5: "28" +6: "100" +8: baz +` + om := New[int, any]() + require.NoError(t, yaml.Unmarshal([]byte(data), &om)) + + assertOrderedPairsEqual(t, om, + []int{1, 7, 2, 3, 4, 5, 6, 8}, + []any{"bar", "baz", 28, 100, "baz", "28", "100", "baz"}) + + // serialize back to yaml to make sure things are equal + }) + + t.Run("string key", func(t *testing.T) { + data := `{"test":"bar","abc":true}` + + om := New[string, any]() + require.NoError(t, yaml.Unmarshal([]byte(data), &om)) + + assertOrderedPairsEqual(t, om, + []string{"test", "abc"}, + []any{"bar", true}) + }) + + t.Run("typed string key", func(t *testing.T) { + data := `{"test":"bar","abc":true}` + + type myString string + om := New[myString, any]() + require.NoError(t, yaml.Unmarshal([]byte(data), &om)) + + assertOrderedPairsEqual(t, om, + []myString{"test", "abc"}, + []any{"bar", true}) + }) + + t.Run("typed int key", func(t *testing.T) { + data := ` +1: bar +7: baz +2: 28 +3: 100 +4: baz +5: "28" +6: "100" +8: baz +` + type myInt uint32 + om := New[myInt, any]() + require.NoError(t, yaml.Unmarshal([]byte(data), &om)) + + assertOrderedPairsEqual(t, om, + []myInt{1, 7, 2, 3, 4, 5, 6, 8}, + []any{"bar", "baz", 28, 100, "baz", "28", "100", "baz"}) + }) + + t.Run("TextUnmarshaler key", func(t *testing.T) { + data := `{"#1#":"bar","#28#":true}` + + om := New[marshallable, any]() + require.NoError(t, yaml.Unmarshal([]byte(data), &om)) + + assertOrderedPairsEqual(t, om, + []marshallable{1, 28}, + []any{"bar", true}) + }) + + t.Run("when fed with an input that's not an object", func(t *testing.T) { + for _, data := range []string{"true", `["foo"]`, "42", `"foo"`} { + om := New[int, any]() + require.Error(t, yaml.Unmarshal([]byte(data), &om)) + } + }) + + t.Run("empty map", func(t *testing.T) { + data := `{}` + + om := New[int, any]() + require.NoError(t, yaml.Unmarshal([]byte(data), &om)) + + assertLenEqual(t, om, 0) + }) +} + +func TestYAMLSpecialCharacters(t *testing.T) { + baselineMap := map[string]any{specialCharacters: specialCharacters} + baselineData, err := yaml.Marshal(baselineMap) + require.NoError(t, err) // baseline proves this key is supported by official yaml library + t.Logf("specialCharacters: %#v as []rune:%v", specialCharacters, []rune(specialCharacters)) + t.Logf("baseline yaml data: %s", baselineData) + + t.Run("marshal special characters", func(t *testing.T) { + om := New[string, any]() + om.Set(specialCharacters, specialCharacters) + b, err := yaml.Marshal(om) + require.NoError(t, err) + require.Equal(t, baselineData, b) + + type myString string + om2 := New[myString, myString]() + om2.Set(specialCharacters, specialCharacters) + b, err = yaml.Marshal(om2) + require.NoError(t, err) + require.Equal(t, baselineData, b) + }) + + t.Run("unmarshall special characters", func(t *testing.T) { + om := New[string, any]() + require.NoError(t, yaml.Unmarshal(baselineData, &om)) + assertOrderedPairsEqual(t, om, + []string{specialCharacters}, + []any{specialCharacters}) + + type myString string + om2 := New[myString, myString]() + require.NoError(t, yaml.Unmarshal(baselineData, &om2)) + assertOrderedPairsEqual(t, om2, + []myString{specialCharacters}, + []myString{specialCharacters}) + }) +} + +func TestYAMLRoundTrip(t *testing.T) { + for _, testCase := range []struct { + name string + input string + targetFactory func() any + }{ + { + name: "empty map", + input: "{}\n", + targetFactory: func() any { + return &OrderedMap[string, any]{} + }, + }, + { + name: "", + input: `x: 28 +m: + bar: + - 5: + foo: bar + foo: + - 12: + b: true + i: 12 + m: + a: b + c: 28 + "n": null + 28: + a: false + b: + - 1 + - 2 + - 3 + - 3: + c: null + d: 87 + 4: + e: true + 5: + f: 4 + g: 5 + h: 6 +`, + targetFactory: func() any { return &nestedMaps{} }, + }, + { + name: "with UTF-8 special chars in key", + input: "�: 0\n", + targetFactory: func() any { return &OrderedMap[string, int]{} }, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + target := testCase.targetFactory() + + require.NoError(t, yaml.Unmarshal([]byte(testCase.input), target)) + + var ( + out []byte + err error + ) + + out, err = yaml.Marshal(target) + + if assert.NoError(t, err) { + assert.Equal(t, testCase.input, string(out)) + } + }) + } +} + +func BenchmarkMarshalYAML(b *testing.B) { + om := New[int, any]() + om.Set(1, "bar") + om.Set(7, "baz") + om.Set(2, 28) + om.Set(3, 100) + om.Set(4, "baz") + om.Set(5, "28") + om.Set(6, "100") + om.Set(8, "baz") + om.Set(8, "baz") + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, _ = yaml.Marshal(om) + } +} diff --git a/third_party/goflow2/.gitignore b/third_party/goflow2/.gitignore new file mode 100644 index 000000000000..a261f2917554 --- /dev/null +++ b/third_party/goflow2/.gitignore @@ -0,0 +1 @@ +dist/* diff --git a/third_party/goflow2/Dockerfile b/third_party/goflow2/Dockerfile new file mode 100644 index 000000000000..a836949f3e2e --- /dev/null +++ b/third_party/goflow2/Dockerfile @@ -0,0 +1,36 @@ +FROM golang:alpine as builder +ARG LDFLAGS="" + +RUN apk --update --no-cache add git build-base gcc + +COPY . /build +WORKDIR /build + +RUN go build -ldflags "${LDFLAGS}" -o goflow2 cmd/goflow2/main.go + +FROM alpine:latest +ARG src_dir +ARG VERSION="" +ARG CREATED="" +ARG DESCRIPTION="" +ARG NAME="" +ARG MAINTAINER="" +ARG URL="" +ARG LICENSE="" +ARG REV="" + +LABEL org.opencontainers.image.created="${CREATED}" +LABEL org.opencontainers.image.authors="${MAINTAINER}" +LABEL org.opencontainers.image.url="${URL}" +LABEL org.opencontainers.image.title="${NAME}" +LABEL org.opencontainers.image.version="${VERSION}" +LABEL org.opencontainers.image.description="${DESCRIPTION}" +LABEL org.opencontainers.image.licenses="${LICENSE}" +LABEL org.opencontainers.image.revision="${REV}" + +RUN apk update --no-cache && \ + adduser -S -D -H -h / flow +USER flow +COPY --from=builder /build/goflow2 / + +ENTRYPOINT ["./goflow2"] diff --git a/third_party/goflow2/LICENSE b/third_party/goflow2/LICENSE new file mode 100644 index 000000000000..a2fee077c3c2 --- /dev/null +++ b/third_party/goflow2/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2021, NetSampler +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/goflow2/Makefile b/third_party/goflow2/Makefile new file mode 100644 index 000000000000..6c935c38705e --- /dev/null +++ b/third_party/goflow2/Makefile @@ -0,0 +1,128 @@ +EXTENSION ?= +DIST_DIR ?= dist/ +GOOS ?= linux +ARCH ?= $(shell uname -m) +BUILDINFOSDET ?= + +DOCKER_REPO ?= netsampler/ +NAME := goflow2 +VERSION ?= $(shell git describe --abbrev --long HEAD) +ABBREV ?= $(shell git rev-parse --short HEAD) +COMMIT ?= $(shell git rev-parse HEAD) +TAG ?= $(shell git describe --tags --abbrev=0 HEAD) +VERSION_PKG ?= $(shell echo $(VERSION) | sed 's/^v//g') +LICENSE := BSD-3-Clause +URL := https://github.com/netsampler/goflow2 +DESCRIPTION := GoFlow2: Open-Source and Scalable Network Sample Collector +DATE := $(shell date +%FT%T%z) +BUILDINFOS ?= ($(DATE)$(BUILDINFOSDET)) +LDFLAGS ?= '-X main.version=$(VERSION) -X main.buildinfos=$(BUILDINFOS)' +MAINTAINER := lspgn@users.noreply.github.com +DOCKER_BIN ?= docker +DOCKER_CMD ?= build +DOCKER_SUFFIX ?= + +OUTPUT := $(DIST_DIR)goflow2-$(VERSION_PKG)-$(GOOS)-$(ARCH)$(EXTENSION) + +.PHONY: proto +proto: + @echo generating protobuf + protoc --go_opt=paths=source_relative --go_out=. pb/*.proto + protoc --go_opt=paths=source_relative --go_out=. cmd/enricher/pb/*.proto + +.PHONY: vet +vet: + go vet cmd/goflow2/main.go + +.PHONY: test +test: + go test -v ./... + +.PHONY: prepare +prepare: + mkdir -p $(DIST_DIR) + +PHONY: clean +clean: + rm -rf $(DIST_DIR) + +.PHONY: build +build: prepare + go build -ldflags $(LDFLAGS) -o $(OUTPUT) cmd/goflow2/main.go + +.PHONY: docker +docker: + $(DOCKER_BIN) $(DOCKER_CMD) \ + --build-arg LDFLAGS=$(LDFLAGS) \ + --build-arg CREATED="$(DATE)" \ + --build-arg MAINTAINER="$(MAINTAINER)" \ + --build-arg URL="$(URL)" \ + --build-arg NAME="$(NAME)" \ + --build-arg DESCRIPTION="$(DESCRIPTION)" \ + --build-arg LICENSE="$(LICENSE)" \ + --build-arg VERSION="$(VERSION)" \ + --build-arg REV="$(COMMIT)" \ + -t $(DOCKER_REPO)$(NAME):$(ABBREV)$(DOCKER_SUFFIX) . + +.PHONY: push-docker +push-docker: + $(DOCKER_BIN) push $(DOCKER_REPO)$(NAME):$(ABBREV)$(DOCKER_SUFFIX) + +.PHONY: docker-manifest +docker-manifest: + $(DOCKER_BIN) manifest create $(DOCKER_REPO)$(NAME):$(ABBREV) \ + --amend $(DOCKER_REPO)$(NAME):$(ABBREV)-amd64 \ + --amend $(DOCKER_REPO)$(NAME):$(ABBREV)-arm64 + $(DOCKER_BIN) manifest push $(DOCKER_REPO)$(NAME):$(ABBREV) + + $(DOCKER_BIN) manifest create $(DOCKER_REPO)$(NAME):latest \ + --amend $(DOCKER_REPO)$(NAME):$(ABBREV)-amd64 \ + --amend $(DOCKER_REPO)$(NAME):$(ABBREV)-arm64 + $(DOCKER_BIN) manifest push $(DOCKER_REPO)$(NAME):latest + +.PHONY: docker-manifest-buildx +docker-manifest-buildx: + $(DOCKER_BIN) buildx imagetools create \ + -t $(DOCKER_REPO)$(NAME):$(ABBREV) \ + $(DOCKER_REPO)$(NAME):$(ABBREV)-amd64 \ + $(DOCKER_REPO)$(NAME):$(ABBREV)-arm64 + +.PHONY: docker-manifest-release +docker-manifest-release: + $(DOCKER_BIN) manifest create $(DOCKER_REPO)$(NAME):$(VERSION) \ + --amend $(DOCKER_REPO)$(NAME):$(ABBREV)-amd64 \ + --amend $(DOCKER_REPO)$(NAME):$(ABBREV)-arm64 + $(DOCKER_BIN) manifest push $(DOCKER_REPO)$(NAME):$(VERSION) + +.PHONY: docker-manifest-release-buildx +docker-manifest-release-buildx: + $(DOCKER_BIN) buildx imagetools create \ + -t $(DOCKER_REPO)$(NAME):$(VERSION) \ + $(DOCKER_REPO)$(NAME):$(ABBREV)-amd64 \ + $(DOCKER_REPO)$(NAME):$(ABBREV)-arm64 + +.PHONY: package-deb +package-deb: prepare + fpm -s dir -t deb -n $(NAME) -v $(VERSION_PKG) \ + --maintainer "$(MAINTAINER)" \ + --description "$(DESCRIPTION)" \ + --url "$(URL)" \ + --architecture $(ARCH) \ + --license "$(LICENSE)" \ + --package $(DIST_DIR) \ + $(OUTPUT)=/usr/bin/goflow2 \ + package/goflow2.service=/lib/systemd/system/goflow2.service \ + package/goflow2.env=/etc/default/goflow2 + +.PHONY: package-rpm +package-rpm: prepare + fpm -s dir -t rpm -n $(NAME) -v $(VERSION_PKG) \ + --maintainer "$(MAINTAINER)" \ + --description "$(DESCRIPTION)" \ + --url "$(URL)" \ + --architecture $(ARCH) \ + --license "$(LICENSE) "\ + --package $(DIST_DIR) \ + $(OUTPUT)=/usr/bin/goflow2 \ + package/goflow2.service=/lib/systemd/system/goflow2.service \ + package/goflow2.env=/etc/default/goflow2 diff --git a/third_party/goflow2/PROVENANCE.md b/third_party/goflow2/PROVENANCE.md new file mode 100644 index 000000000000..905be3b32333 --- /dev/null +++ b/third_party/goflow2/PROVENANCE.md @@ -0,0 +1,18 @@ +# Temporary maintained-YAML backport + +Source: https://github.com/netsampler/goflow2 +Exact source commit: `69a6eaf99e205ed3cd02d29f044a01659107dd02` (v1.3.3). +Logical module identity: `github.com/netsampler/goflow2`. + +Upstream source, tests, fixtures, licenses and attribution are retained. Upstream +CI metadata and vendored dependency snapshots are excluded; dependency sources +continue to resolve through Go modules. The only code edits select the matching +maintained YAML v2/v3 API, including typed YAML node methods and tests. Module +metadata changes select those parsers and satisfy their Go minima. + +Tracking and temporary ownership: https://github.com/StackVista/stackstate/issues/717 +Removal condition: adopt a compatible owning-library release (or Datadog +lightweight OPA patch release) with maintained YAML and passing consumer tests, +then remove this source and its explicit root/workspace/consumer selections. +Dependency replacements do not propagate: external consumers must explicitly +select this independently addressable nested module as well. diff --git a/third_party/goflow2/README.md b/third_party/goflow2/README.md new file mode 100644 index 000000000000..36804b2402e8 --- /dev/null +++ b/third_party/goflow2/README.md @@ -0,0 +1,253 @@ +# GoFlow2 + +[![Build Status](https://github.com/netsampler/goflow2/workflows/Build/badge.svg)](https://github.com/netsampler/goflow2/actions?query=workflow%3ABuild) +[![Go Reference](https://pkg.go.dev/badge/github.com/netsampler/goflow2.svg)](https://pkg.go.dev/github.com/netsampler/goflow2) + +This application is a NetFlow/IPFIX/sFlow collector in Go. + +It gathers network information (IP, interfaces, routers) from different flow protocols, +serializes it in a common format. + +You will want to use GoFlow if: +* You receive a decent amount of network samples and need horizontal scalability +* Have protocol diversity and need a consistent format +* Require raw samples and build aggregation and custom enrichment + +This software is the entry point of a pipeline. The storage, transport, enrichment, graphing, alerting are +not provided. + +![GoFlow2 System diagram](/graphics/diagram.png) + +## Origins + +This work is a fork of a previous [open-source GoFlow code](https://github.com/cloudflare/goflow) built and used at Cloudflare. +It lives in its own GitHub organization to be maintained more easily. + +Among the differences with the original code: +The serializer and transport options have been revamped to make this program more user-friendly +and target new use-cases like logging providers. +Minimal changes in the decoding libraries. + +## Modularity + +In order to enable load-balancing and optimizations, the GoFlow library has a `decoder` which converts +the payload of a flow packet into a Go structure. + +The `producer` functions (one per protocol) then converts those structures into a protobuf (`pb/flow.pb`) +which contains the fields a network engineer is interested in. +The flow packets usually contains multiples samples +This acts as an abstraction of a sample. + +The `format` directory offers various utilities to process the protobuf. It can convert + +The `transport` provides different way of processing the protobuf. Either sending it via Kafka or +send it to a file (or stdout). + +GoFlow2 is a wrapper of all the functions and chains thems. + +You can build your own collector using this base and replace parts: +* Use different transport (e.g: RabbitMQ instead of Kafka) +* Convert to another format (e.g: Cap'n Proto, Avro, instead of protobuf) +* Decode different samples (e.g: not only IP networks, add MPLS) +* Different metrics system (e.g: [OpenTelemetry](https://opentelemetry.io/)) + +### Protocol difference + +The sampling protocols have distinct features: + +**sFlow** is a stateless protocol which sends the full header of a packet with router information +(interfaces, destination AS) while **NetFlow/IPFIX** rely on templates that contain fields (e.g: source IPv6). + +The sampling rate in NetFlow/IPFIX is provided by **Option Data Sets**. This is why it can take a few minutes +for the packets to be decoded until all the templates are received (**Option Template** and **Data Template**). + +Both of these protocols bundle multiple samples (**Data Set** in NetFlow/IPFIX and **Flow Sample** in sFlow) +in one packet. + +The advantages of using an abstract network flow format, such as protobuf, is it enables summing over the +protocols (e.g: per ASN or per port, rather than per (ASN, router) and (port, router)). + +To read more about the protocols and how they are mapped inside, check out [page](/docs/protocols.md) + +### Features of GoFlow2 + +Collection: +* NetFlow v5 +* IPFIX/NetFlow v9 (sampling rate provided by the Option Data Set) +* sFlow v5 + +(adding NetFlow v1,7,8 is being evaluated) + +Production: +* Convert to protobuf or json +* Prints to the console/file +* Sends to Kafka and partition + +Monitoring via Prometheus metrics + +## Get started + +To read about agents that samples network traffic, check this [page](/docs/agents.md). + +To set up the collector, download the latest release corresponding to your OS +and run the following command (the binaries have a suffix with the version): + +```bash +$ ./goflow2 +``` + +By default, this command will launch an sFlow collector on port `:6343` and +a NetFlowV9/IPFIX collector on port `:2055`. + +By default, the samples received will be printed in JSON format on the stdout. + +```json +{ + "Type": "SFLOW_5", + "TimeFlowEnd": 1621820000, + "TimeFlowStart": 1621820000, + "TimeReceived": 1621820000, + "Bytes": 70, + "Packets": 1, + "SamplingRate": 100, + "SamplerAddress": "192.168.1.254", + "DstAddr": "10.0.0.1", + "DstMac": "ff:ff:ff:ff:ff:ff", + "SrcAddr": "192.168.1.1", + "SrcMac": "ff:ff:ff:ff:ff:ff", + "InIf": 1, + "OutIf": 2, + "Etype": 2048, + "EtypeName": "IPv4", + "Proto": 6, + "ProtoName": "TCP", + "SrcPort": 443, + "DstPort": 46344, + "FragmentId": 54044, + "FragmentOffset": 16384, + ... + "IPTTL": 64, + "IPTos": 0, + "TCPFlags": 16, +} +``` + +If you are using a log integration (e.g: Loki with Promtail, Splunk, Fluentd, Google Cloud Logs, etc.), +just send the output into a file. +```bash +$ ./goflow2 -transport.file /var/logs/goflow2.log +``` + +To enable Kafka and send protobuf, use the following arguments: +```bash +$ ./goflow2 -transport=kafka -transport.kafka.brokers=localhost:9092 -transport.kafka.topic=flows -format=pb +``` + +By default, the distribution will be randomized. +To partition the feed (any field of the protobuf is available), the following options can be used: +``` +-transport.kafka.hashing=true \ +-format.hash=SamplerAddress,DstAS +``` + +By default, compression is disabled when sending data to Kafka. +To change the kafka compression type of the producer side configure the following option: +``` +-transport.kafka.compression.type=gzip +``` +The list of codecs is available in the [Sarama documentation](https://pkg.go.dev/github.com/Shopify/sarama#CompressionCodec). + + +By default, the collector will listen for IPFIX/NetFlow V9 on port 2055 +and sFlow on port 6343. +To change the sockets binding, you can set the `-listen` argument and a URI +for each protocol (`netflow`, `sflow` and `nfl` as scheme) separated by a comma. +For instance, to create 4 parallel sockets of sFlow and one of NetFlow V5, you can use: + +```bash +$ ./goflow2 -listen 'sflow://:6343?count=4,nfl://:2055' +``` + +### Docker + +You can also run directly with a container: +``` +$ sudo docker run -p 6343:6343/udp -p 2055:2055/udp -ti netsampler/goflow2:latest +``` + +### Mapping extra fields + +In the case of exotic template fields or extra payload not supported by GoFlow2 +of out the box, it is possible to pass a mapping file using `-mapping mapping.yaml`. +A [sample file](cmd/goflow2/mapping.yaml) is available in the `cmd/goflow2` directory. + +For instance, certain devices producing IPFIX use `ingressPhysicalInterface` (id: 252) +and do not use `ingressInterface` (id: 10). Using the following you can have the interface mapped +in the InIf protobuf field without changing the code. + +```yaml +ipfix: + mapping: + - field: 252 + destination: InIf + - field: 253 + destination: OutIf +``` + +### Output format considerations + +The JSON format is advised only when consuming a small amount of data directly. +For bigger workloads, the protobuf output format provides a binary representation +and is preferred. +It can also be extended with enrichment as long as the user keep the same IDs. + +If you want to develop applications, build `pb/flow.proto` into the language you want: +When adding custom fields, picking a field ID ≥ 1000 is suggested. + +Check the docs for more information about [compiling protobuf](/docs/protobuf.md). + +## Flow Pipeline + +A basic enrichment tool is available in the `cmd/enricher` directory. +You need to load the Maxmind GeoIP ASN and Country databases using `-db.asn` and `-db.country`. + +Running a flow enrichment system is as simple as a pipe. +Once you plug the stdin of the enricher to the stdout of GoFlow in protobuf, +the source and destination IP addresses will automatically be mapped +with a database for Autonomous System Number and Country. +Similar output options as GoFlow are provided. + +```bash +$ ./goflow2 -transport.file.sep= -format=pb -format.protobuf.fixedlen=true | ./enricher -db.asn path-to/GeoLite2-ASN.mmdb -db.country path-to/GeoLite2-Country.mmdb +``` + +For a more scalable production setting, Kafka and protobuf are recommended. +Stream operations (aggregation and filtering) can be done with stream-processor tools. +For instance Flink, or the more recent Kafka Streams and kSQLdb. +Direct storage can be done with data-warehouses like Clickhouse. + +In some cases, the consumer will require protobuf messages to be prefixed by +length. To do this, use the flag `-format.protobuf.fixedlen=true`. + +This repository contains [examples of pipelines](./compose) with docker-compose. +The available pipelines are: +* [Kafka+Clickhouse+Grafana](./compose/kcg) +* [Logstash+Elastic+Kibana](./compose/elk) + +## User stories + +Are you using GoFlow2 in production at scale? Add yourself here! + +### Contributions + +This project welcomes pull-requests, whether it's documentation, +instrumentation (e.g: docker-compose, metrics), internals (protocol libraries), +integration (new CLI feature) or else! +Just make sure to check for the use-cases via an issue. + +This software would not exist without the testing and commits from +its users and [contributors](docs/contributors.md). + +## License + +Licensed under the BSD-3 License. diff --git a/third_party/goflow2/cmd/enricher/main.go b/third_party/goflow2/cmd/enricher/main.go new file mode 100644 index 000000000000..81b44810f208 --- /dev/null +++ b/third_party/goflow2/cmd/enricher/main.go @@ -0,0 +1,197 @@ +package main + +import ( + "bufio" + "bytes" + "context" + "encoding/binary" + "flag" + "fmt" + "io" + "net" + "net/http" + "os" + "strings" + + "github.com/oschwald/geoip2-golang" + + "github.com/golang/protobuf/proto" + flowmessage "github.com/netsampler/goflow2/cmd/enricher/pb" + + // import various formatters + "github.com/netsampler/goflow2/format" + _ "github.com/netsampler/goflow2/format/json" + _ "github.com/netsampler/goflow2/format/protobuf" + _ "github.com/netsampler/goflow2/format/text" + + // import various transports + "github.com/netsampler/goflow2/transport" + _ "github.com/netsampler/goflow2/transport/file" + _ "github.com/netsampler/goflow2/transport/kafka" + + "github.com/prometheus/client_golang/prometheus/promhttp" + log "github.com/sirupsen/logrus" +) + +var ( + version = "" + buildinfos = "" + AppVersion = "Enricher " + version + " " + buildinfos + + DbAsn = flag.String("db.asn", "", "IP->ASN database") + DbCountry = flag.String("db.country", "", "IP->Country database") + + LogLevel = flag.String("loglevel", "info", "Log level") + LogFmt = flag.String("logfmt", "normal", "Log formatter") + + SamplingRate = flag.Int("samplingrate", 0, "Set sampling rate (values > 0)") + + Format = flag.String("format", "json", fmt.Sprintf("Choose the format (available: %s)", strings.Join(format.GetFormats(), ", "))) + Transport = flag.String("transport", "file", fmt.Sprintf("Choose the transport (available: %s)", strings.Join(transport.GetTransports(), ", "))) + + MetricsAddr = flag.String("metrics.addr", ":8081", "Metrics address") + MetricsPath = flag.String("metrics.path", "/metrics", "Metrics path") + + TemplatePath = flag.String("templates.path", "/templates", "NetFlow/IPFIX templates list") + + Version = flag.Bool("v", false, "Print version") +) + +func httpServer() { + http.Handle(*MetricsPath, promhttp.Handler()) + log.Fatal(http.ListenAndServe(*MetricsAddr, nil)) +} + +func MapAsn(db *geoip2.Reader, addr []byte, dest *uint32) { + entry, err := db.ASN(net.IP(addr)) + if err != nil { + return + } + *dest = uint32(entry.AutonomousSystemNumber) +} +func MapCountry(db *geoip2.Reader, addr []byte, dest *string) { + entry, err := db.Country(net.IP(addr)) + if err != nil { + return + } + *dest = entry.Country.IsoCode +} + +func MapFlow(dbAsn, dbCountry *geoip2.Reader, msg *flowmessage.FlowMessageExt) { + if dbAsn != nil { + MapAsn(dbAsn, msg.SrcAddr, &(msg.SrcAs)) + MapAsn(dbAsn, msg.DstAddr, &(msg.DstAs)) + } + if dbCountry != nil { + MapCountry(dbCountry, msg.SrcAddr, &(msg.SrcCountry)) + MapCountry(dbCountry, msg.DstAddr, &(msg.DstCountry)) + } +} + +func main() { + flag.Parse() + + if *Version { + fmt.Println(AppVersion) + os.Exit(0) + } + + lvl, _ := log.ParseLevel(*LogLevel) + log.SetLevel(lvl) + + var dbAsn, dbCountry *geoip2.Reader + var err error + if *DbAsn != "" { + dbAsn, err = geoip2.Open(*DbAsn) + if err != nil { + log.Fatal(err) + } + defer dbAsn.Close() + } + + if *DbCountry != "" { + dbCountry, err = geoip2.Open(*DbCountry) + if err != nil { + log.Fatal(err) + } + defer dbCountry.Close() + } + + ctx := context.Background() + + formatter, err := format.FindFormat(ctx, *Format) + if err != nil { + log.Fatal(err) + } + + transporter, err := transport.FindTransport(ctx, *Transport) + if err != nil { + log.Fatal(err) + } + defer transporter.Close(ctx) + + switch *LogFmt { + case "json": + log.SetFormatter(&log.JSONFormatter{}) + } + + log.Info("Starting enricher") + + go httpServer() + + rdr := bufio.NewReader(os.Stdin) + + msg := &flowmessage.FlowMessageExt{} + lenBufSize := binary.MaxVarintLen64 + for { + msgLen, err := rdr.Peek(lenBufSize) + if err != nil && err != io.EOF { + log.Error(err) + continue + } + + l, vn := proto.DecodeVarint(msgLen) + if l == 0 { + continue + } + + _, err = rdr.Discard(vn) + if err != nil { + log.Error(err) + continue + } + + line := make([]byte, l) + + _, err = io.ReadFull(rdr, line) + if err != nil && err != io.EOF { + log.Error(err) + continue + } + line = bytes.TrimSuffix(line, []byte("\n")) + + err = proto.Unmarshal(line, msg) + if err != nil { + log.Error(err) + continue + } + + MapFlow(dbAsn, dbCountry, msg) + + if *SamplingRate > 0 { + msg.SamplingRate = uint64(*SamplingRate) + } + + key, data, err := formatter.Format(msg) + if err != nil { + log.Error(err) + continue + } + + err = transporter.Send(key, data) + if err != nil { + log.Error(err) + continue + } + } +} diff --git a/third_party/goflow2/cmd/enricher/pb/flowext.pb.go b/third_party/goflow2/cmd/enricher/pb/flowext.pb.go new file mode 100644 index 000000000000..6bfd79a778e9 --- /dev/null +++ b/third_party/goflow2/cmd/enricher/pb/flowext.pb.go @@ -0,0 +1,830 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.21.4 +// source: cmd/enricher/pb/flowext.proto + +package flowpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FlowMessageExt_FlowType int32 + +const ( + FlowMessageExt_FLOWUNKNOWN FlowMessageExt_FlowType = 0 + FlowMessageExt_SFLOW_5 FlowMessageExt_FlowType = 1 + FlowMessageExt_NETFLOW_V5 FlowMessageExt_FlowType = 2 + FlowMessageExt_NETFLOW_V9 FlowMessageExt_FlowType = 3 + FlowMessageExt_IPFIX FlowMessageExt_FlowType = 4 +) + +// Enum value maps for FlowMessageExt_FlowType. +var ( + FlowMessageExt_FlowType_name = map[int32]string{ + 0: "FLOWUNKNOWN", + 1: "SFLOW_5", + 2: "NETFLOW_V5", + 3: "NETFLOW_V9", + 4: "IPFIX", + } + FlowMessageExt_FlowType_value = map[string]int32{ + "FLOWUNKNOWN": 0, + "SFLOW_5": 1, + "NETFLOW_V5": 2, + "NETFLOW_V9": 3, + "IPFIX": 4, + } +) + +func (x FlowMessageExt_FlowType) Enum() *FlowMessageExt_FlowType { + p := new(FlowMessageExt_FlowType) + *p = x + return p +} + +func (x FlowMessageExt_FlowType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FlowMessageExt_FlowType) Descriptor() protoreflect.EnumDescriptor { + return file_cmd_enricher_pb_flowext_proto_enumTypes[0].Descriptor() +} + +func (FlowMessageExt_FlowType) Type() protoreflect.EnumType { + return &file_cmd_enricher_pb_flowext_proto_enumTypes[0] +} + +func (x FlowMessageExt_FlowType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use FlowMessageExt_FlowType.Descriptor instead. +func (FlowMessageExt_FlowType) EnumDescriptor() ([]byte, []int) { + return file_cmd_enricher_pb_flowext_proto_rawDescGZIP(), []int{0, 0} +} + +type FlowMessageExt struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type FlowMessageExt_FlowType `protobuf:"varint,1,opt,name=type,proto3,enum=flowpb.FlowMessageExt_FlowType" json:"type,omitempty"` + TimeReceived uint64 `protobuf:"varint,2,opt,name=time_received,json=timeReceived,proto3" json:"time_received,omitempty"` + SequenceNum uint32 `protobuf:"varint,4,opt,name=sequence_num,json=sequenceNum,proto3" json:"sequence_num,omitempty"` + SamplingRate uint64 `protobuf:"varint,3,opt,name=sampling_rate,json=samplingRate,proto3" json:"sampling_rate,omitempty"` + FlowDirection uint32 `protobuf:"varint,42,opt,name=flow_direction,json=flowDirection,proto3" json:"flow_direction,omitempty"` + // Sampler information + SamplerAddress []byte `protobuf:"bytes,11,opt,name=sampler_address,json=samplerAddress,proto3" json:"sampler_address,omitempty"` + // Found inside packet + TimeFlowStart uint64 `protobuf:"varint,38,opt,name=time_flow_start,json=timeFlowStart,proto3" json:"time_flow_start,omitempty"` + TimeFlowEnd uint64 `protobuf:"varint,5,opt,name=time_flow_end,json=timeFlowEnd,proto3" json:"time_flow_end,omitempty"` + TimeFlowStartMs uint64 `protobuf:"varint,63,opt,name=time_flow_start_ms,json=timeFlowStartMs,proto3" json:"time_flow_start_ms,omitempty"` + TimeFlowEndMs uint64 `protobuf:"varint,64,opt,name=time_flow_end_ms,json=timeFlowEndMs,proto3" json:"time_flow_end_ms,omitempty"` + // Size of the sampled packet + Bytes uint64 `protobuf:"varint,9,opt,name=bytes,proto3" json:"bytes,omitempty"` + Packets uint64 `protobuf:"varint,10,opt,name=packets,proto3" json:"packets,omitempty"` + // Source/destination addresses + SrcAddr []byte `protobuf:"bytes,6,opt,name=src_addr,json=srcAddr,proto3" json:"src_addr,omitempty"` + DstAddr []byte `protobuf:"bytes,7,opt,name=dst_addr,json=dstAddr,proto3" json:"dst_addr,omitempty"` + // Layer 3 protocol (IPv4/IPv6/ARP/MPLS...) + Etype uint32 `protobuf:"varint,30,opt,name=etype,proto3" json:"etype,omitempty"` + // Layer 4 protocol + Proto uint32 `protobuf:"varint,20,opt,name=proto,proto3" json:"proto,omitempty"` + // Ports for UDP and TCP + SrcPort uint32 `protobuf:"varint,21,opt,name=src_port,json=srcPort,proto3" json:"src_port,omitempty"` + DstPort uint32 `protobuf:"varint,22,opt,name=dst_port,json=dstPort,proto3" json:"dst_port,omitempty"` + // Interfaces + InIf uint32 `protobuf:"varint,18,opt,name=in_if,json=inIf,proto3" json:"in_if,omitempty"` + OutIf uint32 `protobuf:"varint,19,opt,name=out_if,json=outIf,proto3" json:"out_if,omitempty"` + // Ethernet information + SrcMac uint64 `protobuf:"varint,27,opt,name=src_mac,json=srcMac,proto3" json:"src_mac,omitempty"` + DstMac uint64 `protobuf:"varint,28,opt,name=dst_mac,json=dstMac,proto3" json:"dst_mac,omitempty"` + // Vlan + SrcVlan uint32 `protobuf:"varint,33,opt,name=src_vlan,json=srcVlan,proto3" json:"src_vlan,omitempty"` + DstVlan uint32 `protobuf:"varint,34,opt,name=dst_vlan,json=dstVlan,proto3" json:"dst_vlan,omitempty"` + // 802.1q VLAN in sampled packet + VlanId uint32 `protobuf:"varint,29,opt,name=vlan_id,json=vlanId,proto3" json:"vlan_id,omitempty"` + // VRF + IngressVrfId uint32 `protobuf:"varint,39,opt,name=ingress_vrf_id,json=ingressVrfId,proto3" json:"ingress_vrf_id,omitempty"` + EgressVrfId uint32 `protobuf:"varint,40,opt,name=egress_vrf_id,json=egressVrfId,proto3" json:"egress_vrf_id,omitempty"` + // IP and TCP special flags + IpTos uint32 `protobuf:"varint,23,opt,name=ip_tos,json=ipTos,proto3" json:"ip_tos,omitempty"` + ForwardingStatus uint32 `protobuf:"varint,24,opt,name=forwarding_status,json=forwardingStatus,proto3" json:"forwarding_status,omitempty"` + IpTtl uint32 `protobuf:"varint,25,opt,name=ip_ttl,json=ipTtl,proto3" json:"ip_ttl,omitempty"` + TcpFlags uint32 `protobuf:"varint,26,opt,name=tcp_flags,json=tcpFlags,proto3" json:"tcp_flags,omitempty"` + IcmpType uint32 `protobuf:"varint,31,opt,name=icmp_type,json=icmpType,proto3" json:"icmp_type,omitempty"` + IcmpCode uint32 `protobuf:"varint,32,opt,name=icmp_code,json=icmpCode,proto3" json:"icmp_code,omitempty"` + Ipv6FlowLabel uint32 `protobuf:"varint,37,opt,name=ipv6_flow_label,json=ipv6FlowLabel,proto3" json:"ipv6_flow_label,omitempty"` + // Fragments (IPv4/IPv6) + FragmentId uint32 `protobuf:"varint,35,opt,name=fragment_id,json=fragmentId,proto3" json:"fragment_id,omitempty"` + FragmentOffset uint32 `protobuf:"varint,36,opt,name=fragment_offset,json=fragmentOffset,proto3" json:"fragment_offset,omitempty"` + BiFlowDirection uint32 `protobuf:"varint,41,opt,name=bi_flow_direction,json=biFlowDirection,proto3" json:"bi_flow_direction,omitempty"` + // Autonomous system information + SrcAs uint32 `protobuf:"varint,14,opt,name=src_as,json=srcAs,proto3" json:"src_as,omitempty"` + DstAs uint32 `protobuf:"varint,15,opt,name=dst_as,json=dstAs,proto3" json:"dst_as,omitempty"` + NextHop []byte `protobuf:"bytes,12,opt,name=next_hop,json=nextHop,proto3" json:"next_hop,omitempty"` + NextHopAs uint32 `protobuf:"varint,13,opt,name=next_hop_as,json=nextHopAs,proto3" json:"next_hop_as,omitempty"` + // Prefix size + SrcNet uint32 `protobuf:"varint,16,opt,name=src_net,json=srcNet,proto3" json:"src_net,omitempty"` + DstNet uint32 `protobuf:"varint,17,opt,name=dst_net,json=dstNet,proto3" json:"dst_net,omitempty"` + // BGP information + BgpNextHop []byte `protobuf:"bytes,100,opt,name=bgp_next_hop,json=bgpNextHop,proto3" json:"bgp_next_hop,omitempty"` + BgpCommunities []uint32 `protobuf:"varint,101,rep,packed,name=bgp_communities,json=bgpCommunities,proto3" json:"bgp_communities,omitempty"` + AsPath []uint32 `protobuf:"varint,102,rep,packed,name=as_path,json=asPath,proto3" json:"as_path,omitempty"` + // MPLS information + HasMpls bool `protobuf:"varint,53,opt,name=has_mpls,json=hasMpls,proto3" json:"has_mpls,omitempty"` + MplsCount uint32 `protobuf:"varint,54,opt,name=mpls_count,json=mplsCount,proto3" json:"mpls_count,omitempty"` + Mpls_1Ttl uint32 `protobuf:"varint,55,opt,name=mpls_1_ttl,json=mpls1Ttl,proto3" json:"mpls_1_ttl,omitempty"` // First TTL + Mpls_1Label uint32 `protobuf:"varint,56,opt,name=mpls_1_label,json=mpls1Label,proto3" json:"mpls_1_label,omitempty"` // First Label + Mpls_2Ttl uint32 `protobuf:"varint,57,opt,name=mpls_2_ttl,json=mpls2Ttl,proto3" json:"mpls_2_ttl,omitempty"` // Second TTL + Mpls_2Label uint32 `protobuf:"varint,58,opt,name=mpls_2_label,json=mpls2Label,proto3" json:"mpls_2_label,omitempty"` // Second Label + Mpls_3Ttl uint32 `protobuf:"varint,59,opt,name=mpls_3_ttl,json=mpls3Ttl,proto3" json:"mpls_3_ttl,omitempty"` // Third TTL + Mpls_3Label uint32 `protobuf:"varint,60,opt,name=mpls_3_label,json=mpls3Label,proto3" json:"mpls_3_label,omitempty"` // Third Label + MplsLastTtl uint32 `protobuf:"varint,61,opt,name=mpls_last_ttl,json=mplsLastTtl,proto3" json:"mpls_last_ttl,omitempty"` // Last TTL + MplsLastLabel uint32 `protobuf:"varint,62,opt,name=mpls_last_label,json=mplsLastLabel,proto3" json:"mpls_last_label,omitempty"` // Last Label + MplsLabelIp []byte `protobuf:"bytes,65,opt,name=mpls_label_ip,json=mplsLabelIp,proto3" json:"mpls_label_ip,omitempty"` // MPLS TOP Label IP + ObservationDomainId uint32 `protobuf:"varint,70,opt,name=observation_domain_id,json=observationDomainId,proto3" json:"observation_domain_id,omitempty"` + ObservationPointId uint32 `protobuf:"varint,71,opt,name=observation_point_id,json=observationPointId,proto3" json:"observation_point_id,omitempty"` + SrcCountry string `protobuf:"bytes,1000,opt,name=src_country,json=srcCountry,proto3" json:"src_country,omitempty"` + DstCountry string `protobuf:"bytes,1001,opt,name=dst_country,json=dstCountry,proto3" json:"dst_country,omitempty"` +} + +func (x *FlowMessageExt) Reset() { + *x = FlowMessageExt{} + if protoimpl.UnsafeEnabled { + mi := &file_cmd_enricher_pb_flowext_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FlowMessageExt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlowMessageExt) ProtoMessage() {} + +func (x *FlowMessageExt) ProtoReflect() protoreflect.Message { + mi := &file_cmd_enricher_pb_flowext_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlowMessageExt.ProtoReflect.Descriptor instead. +func (*FlowMessageExt) Descriptor() ([]byte, []int) { + return file_cmd_enricher_pb_flowext_proto_rawDescGZIP(), []int{0} +} + +func (x *FlowMessageExt) GetType() FlowMessageExt_FlowType { + if x != nil { + return x.Type + } + return FlowMessageExt_FLOWUNKNOWN +} + +func (x *FlowMessageExt) GetTimeReceived() uint64 { + if x != nil { + return x.TimeReceived + } + return 0 +} + +func (x *FlowMessageExt) GetSequenceNum() uint32 { + if x != nil { + return x.SequenceNum + } + return 0 +} + +func (x *FlowMessageExt) GetSamplingRate() uint64 { + if x != nil { + return x.SamplingRate + } + return 0 +} + +func (x *FlowMessageExt) GetFlowDirection() uint32 { + if x != nil { + return x.FlowDirection + } + return 0 +} + +func (x *FlowMessageExt) GetSamplerAddress() []byte { + if x != nil { + return x.SamplerAddress + } + return nil +} + +func (x *FlowMessageExt) GetTimeFlowStart() uint64 { + if x != nil { + return x.TimeFlowStart + } + return 0 +} + +func (x *FlowMessageExt) GetTimeFlowEnd() uint64 { + if x != nil { + return x.TimeFlowEnd + } + return 0 +} + +func (x *FlowMessageExt) GetTimeFlowStartMs() uint64 { + if x != nil { + return x.TimeFlowStartMs + } + return 0 +} + +func (x *FlowMessageExt) GetTimeFlowEndMs() uint64 { + if x != nil { + return x.TimeFlowEndMs + } + return 0 +} + +func (x *FlowMessageExt) GetBytes() uint64 { + if x != nil { + return x.Bytes + } + return 0 +} + +func (x *FlowMessageExt) GetPackets() uint64 { + if x != nil { + return x.Packets + } + return 0 +} + +func (x *FlowMessageExt) GetSrcAddr() []byte { + if x != nil { + return x.SrcAddr + } + return nil +} + +func (x *FlowMessageExt) GetDstAddr() []byte { + if x != nil { + return x.DstAddr + } + return nil +} + +func (x *FlowMessageExt) GetEtype() uint32 { + if x != nil { + return x.Etype + } + return 0 +} + +func (x *FlowMessageExt) GetProto() uint32 { + if x != nil { + return x.Proto + } + return 0 +} + +func (x *FlowMessageExt) GetSrcPort() uint32 { + if x != nil { + return x.SrcPort + } + return 0 +} + +func (x *FlowMessageExt) GetDstPort() uint32 { + if x != nil { + return x.DstPort + } + return 0 +} + +func (x *FlowMessageExt) GetInIf() uint32 { + if x != nil { + return x.InIf + } + return 0 +} + +func (x *FlowMessageExt) GetOutIf() uint32 { + if x != nil { + return x.OutIf + } + return 0 +} + +func (x *FlowMessageExt) GetSrcMac() uint64 { + if x != nil { + return x.SrcMac + } + return 0 +} + +func (x *FlowMessageExt) GetDstMac() uint64 { + if x != nil { + return x.DstMac + } + return 0 +} + +func (x *FlowMessageExt) GetSrcVlan() uint32 { + if x != nil { + return x.SrcVlan + } + return 0 +} + +func (x *FlowMessageExt) GetDstVlan() uint32 { + if x != nil { + return x.DstVlan + } + return 0 +} + +func (x *FlowMessageExt) GetVlanId() uint32 { + if x != nil { + return x.VlanId + } + return 0 +} + +func (x *FlowMessageExt) GetIngressVrfId() uint32 { + if x != nil { + return x.IngressVrfId + } + return 0 +} + +func (x *FlowMessageExt) GetEgressVrfId() uint32 { + if x != nil { + return x.EgressVrfId + } + return 0 +} + +func (x *FlowMessageExt) GetIpTos() uint32 { + if x != nil { + return x.IpTos + } + return 0 +} + +func (x *FlowMessageExt) GetForwardingStatus() uint32 { + if x != nil { + return x.ForwardingStatus + } + return 0 +} + +func (x *FlowMessageExt) GetIpTtl() uint32 { + if x != nil { + return x.IpTtl + } + return 0 +} + +func (x *FlowMessageExt) GetTcpFlags() uint32 { + if x != nil { + return x.TcpFlags + } + return 0 +} + +func (x *FlowMessageExt) GetIcmpType() uint32 { + if x != nil { + return x.IcmpType + } + return 0 +} + +func (x *FlowMessageExt) GetIcmpCode() uint32 { + if x != nil { + return x.IcmpCode + } + return 0 +} + +func (x *FlowMessageExt) GetIpv6FlowLabel() uint32 { + if x != nil { + return x.Ipv6FlowLabel + } + return 0 +} + +func (x *FlowMessageExt) GetFragmentId() uint32 { + if x != nil { + return x.FragmentId + } + return 0 +} + +func (x *FlowMessageExt) GetFragmentOffset() uint32 { + if x != nil { + return x.FragmentOffset + } + return 0 +} + +func (x *FlowMessageExt) GetBiFlowDirection() uint32 { + if x != nil { + return x.BiFlowDirection + } + return 0 +} + +func (x *FlowMessageExt) GetSrcAs() uint32 { + if x != nil { + return x.SrcAs + } + return 0 +} + +func (x *FlowMessageExt) GetDstAs() uint32 { + if x != nil { + return x.DstAs + } + return 0 +} + +func (x *FlowMessageExt) GetNextHop() []byte { + if x != nil { + return x.NextHop + } + return nil +} + +func (x *FlowMessageExt) GetNextHopAs() uint32 { + if x != nil { + return x.NextHopAs + } + return 0 +} + +func (x *FlowMessageExt) GetSrcNet() uint32 { + if x != nil { + return x.SrcNet + } + return 0 +} + +func (x *FlowMessageExt) GetDstNet() uint32 { + if x != nil { + return x.DstNet + } + return 0 +} + +func (x *FlowMessageExt) GetBgpNextHop() []byte { + if x != nil { + return x.BgpNextHop + } + return nil +} + +func (x *FlowMessageExt) GetBgpCommunities() []uint32 { + if x != nil { + return x.BgpCommunities + } + return nil +} + +func (x *FlowMessageExt) GetAsPath() []uint32 { + if x != nil { + return x.AsPath + } + return nil +} + +func (x *FlowMessageExt) GetHasMpls() bool { + if x != nil { + return x.HasMpls + } + return false +} + +func (x *FlowMessageExt) GetMplsCount() uint32 { + if x != nil { + return x.MplsCount + } + return 0 +} + +func (x *FlowMessageExt) GetMpls_1Ttl() uint32 { + if x != nil { + return x.Mpls_1Ttl + } + return 0 +} + +func (x *FlowMessageExt) GetMpls_1Label() uint32 { + if x != nil { + return x.Mpls_1Label + } + return 0 +} + +func (x *FlowMessageExt) GetMpls_2Ttl() uint32 { + if x != nil { + return x.Mpls_2Ttl + } + return 0 +} + +func (x *FlowMessageExt) GetMpls_2Label() uint32 { + if x != nil { + return x.Mpls_2Label + } + return 0 +} + +func (x *FlowMessageExt) GetMpls_3Ttl() uint32 { + if x != nil { + return x.Mpls_3Ttl + } + return 0 +} + +func (x *FlowMessageExt) GetMpls_3Label() uint32 { + if x != nil { + return x.Mpls_3Label + } + return 0 +} + +func (x *FlowMessageExt) GetMplsLastTtl() uint32 { + if x != nil { + return x.MplsLastTtl + } + return 0 +} + +func (x *FlowMessageExt) GetMplsLastLabel() uint32 { + if x != nil { + return x.MplsLastLabel + } + return 0 +} + +func (x *FlowMessageExt) GetMplsLabelIp() []byte { + if x != nil { + return x.MplsLabelIp + } + return nil +} + +func (x *FlowMessageExt) GetObservationDomainId() uint32 { + if x != nil { + return x.ObservationDomainId + } + return 0 +} + +func (x *FlowMessageExt) GetObservationPointId() uint32 { + if x != nil { + return x.ObservationPointId + } + return 0 +} + +func (x *FlowMessageExt) GetSrcCountry() string { + if x != nil { + return x.SrcCountry + } + return "" +} + +func (x *FlowMessageExt) GetDstCountry() string { + if x != nil { + return x.DstCountry + } + return "" +} + +var File_cmd_enricher_pb_flowext_proto protoreflect.FileDescriptor + +var file_cmd_enricher_pb_flowext_proto_rawDesc = []byte{ + 0x0a, 0x1d, 0x63, 0x6d, 0x64, 0x2f, 0x65, 0x6e, 0x72, 0x69, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x70, + 0x62, 0x2f, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x06, 0x66, 0x6c, 0x6f, 0x77, 0x70, 0x62, 0x22, 0x9b, 0x10, 0x0a, 0x0e, 0x46, 0x6c, 0x6f, 0x77, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x78, 0x74, 0x12, 0x33, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x70, + 0x62, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x78, 0x74, + 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x23, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, + 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x71, 0x75, + 0x65, 0x6e, 0x63, 0x65, 0x4e, 0x75, 0x6d, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x61, 0x6d, 0x70, 0x6c, + 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, + 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, + 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x2a, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x66, 0x6c, 0x6f, 0x77, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x73, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x26, 0x0a, 0x0f, + 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, + 0x26, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x66, 0x6c, 0x6f, + 0x77, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x69, 0x6d, + 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x45, 0x6e, 0x64, 0x12, 0x2b, 0x0a, 0x12, 0x74, 0x69, 0x6d, 0x65, + 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x73, 0x18, 0x3f, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x4d, 0x73, 0x12, 0x27, 0x0a, 0x10, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x66, 0x6c, + 0x6f, 0x77, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x6d, 0x73, 0x18, 0x40, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x45, 0x6e, 0x64, 0x4d, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x19, + 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x07, 0x73, 0x72, 0x63, 0x41, 0x64, 0x64, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x64, 0x73, 0x74, + 0x41, 0x64, 0x64, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x74, 0x79, 0x70, 0x65, 0x18, 0x1e, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x65, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x19, 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x15, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x07, 0x73, 0x72, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x64, + 0x73, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x64, + 0x73, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x13, 0x0a, 0x05, 0x69, 0x6e, 0x5f, 0x69, 0x66, 0x18, + 0x12, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x69, 0x6e, 0x49, 0x66, 0x12, 0x15, 0x0a, 0x06, 0x6f, + 0x75, 0x74, 0x5f, 0x69, 0x66, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6f, 0x75, 0x74, + 0x49, 0x66, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x72, 0x63, 0x5f, 0x6d, 0x61, 0x63, 0x18, 0x1b, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x72, 0x63, 0x4d, 0x61, 0x63, 0x12, 0x17, 0x0a, 0x07, 0x64, + 0x73, 0x74, 0x5f, 0x6d, 0x61, 0x63, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x64, 0x73, + 0x74, 0x4d, 0x61, 0x63, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x76, 0x6c, 0x61, 0x6e, + 0x18, 0x21, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x72, 0x63, 0x56, 0x6c, 0x61, 0x6e, 0x12, + 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74, 0x5f, 0x76, 0x6c, 0x61, 0x6e, 0x18, 0x22, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x64, 0x73, 0x74, 0x56, 0x6c, 0x61, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x76, 0x6c, + 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x76, 0x6c, 0x61, + 0x6e, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x76, + 0x72, 0x66, 0x5f, 0x69, 0x64, 0x18, 0x27, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x69, 0x6e, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x56, 0x72, 0x66, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x65, 0x67, 0x72, + 0x65, 0x73, 0x73, 0x5f, 0x76, 0x72, 0x66, 0x5f, 0x69, 0x64, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0b, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x56, 0x72, 0x66, 0x49, 0x64, 0x12, 0x15, 0x0a, + 0x06, 0x69, 0x70, 0x5f, 0x74, 0x6f, 0x73, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, + 0x70, 0x54, 0x6f, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, + 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x70, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x19, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x69, 0x70, 0x54, 0x74, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x63, 0x70, 0x5f, + 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x74, 0x63, 0x70, + 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, + 0x20, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x12, + 0x26, 0x0a, 0x0f, 0x69, 0x70, 0x76, 0x36, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x18, 0x25, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x69, 0x70, 0x76, 0x36, 0x46, 0x6c, + 0x6f, 0x77, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x72, 0x61, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x66, 0x72, + 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x72, 0x61, 0x67, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x24, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0e, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x69, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x64, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x62, 0x69, + 0x46, 0x6c, 0x6f, 0x77, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, + 0x06, 0x73, 0x72, 0x63, 0x5f, 0x61, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, + 0x72, 0x63, 0x41, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x64, 0x73, 0x74, 0x41, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6e, + 0x65, 0x78, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6e, + 0x65, 0x78, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x1e, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, + 0x6f, 0x70, 0x5f, 0x61, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6e, 0x65, 0x78, + 0x74, 0x48, 0x6f, 0x70, 0x41, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x72, 0x63, 0x5f, 0x6e, 0x65, + 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x72, 0x63, 0x4e, 0x65, 0x74, 0x12, + 0x17, 0x0a, 0x07, 0x64, 0x73, 0x74, 0x5f, 0x6e, 0x65, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x06, 0x64, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x62, 0x67, 0x70, 0x5f, + 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, + 0x62, 0x67, 0x70, 0x4e, 0x65, 0x78, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x27, 0x0a, 0x0f, 0x62, 0x67, + 0x70, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x65, 0x20, + 0x03, 0x28, 0x0d, 0x52, 0x0e, 0x62, 0x67, 0x70, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x66, + 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x61, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, + 0x68, 0x61, 0x73, 0x5f, 0x6d, 0x70, 0x6c, 0x73, 0x18, 0x35, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x68, 0x61, 0x73, 0x4d, 0x70, 0x6c, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x70, 0x6c, 0x73, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x36, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6d, 0x70, 0x6c, + 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x31, + 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x37, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x70, 0x6c, 0x73, + 0x31, 0x54, 0x74, 0x6c, 0x12, 0x20, 0x0a, 0x0c, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x31, 0x5f, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x18, 0x38, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x70, 0x6c, 0x73, + 0x31, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x32, + 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x39, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x70, 0x6c, 0x73, + 0x32, 0x54, 0x74, 0x6c, 0x12, 0x20, 0x0a, 0x0c, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x32, 0x5f, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x70, 0x6c, 0x73, + 0x32, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x33, + 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x3b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x70, 0x6c, 0x73, + 0x33, 0x54, 0x74, 0x6c, 0x12, 0x20, 0x0a, 0x0c, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x33, 0x5f, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x70, 0x6c, 0x73, + 0x33, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, + 0x70, 0x6c, 0x73, 0x4c, 0x61, 0x73, 0x74, 0x54, 0x74, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x70, + 0x6c, 0x73, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x3e, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6d, 0x70, 0x6c, 0x73, 0x4c, 0x61, 0x73, 0x74, 0x4c, 0x61, 0x62, + 0x65, 0x6c, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x5f, 0x69, 0x70, 0x18, 0x41, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6d, 0x70, 0x6c, 0x73, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x49, 0x70, 0x12, 0x32, 0x0a, 0x15, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x46, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x6f, 0x62, + 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x47, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, + 0x73, 0x72, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0xe8, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x73, 0x72, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0xe9, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x73, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, + 0x22, 0x53, 0x0a, 0x08, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, 0x0b, + 0x46, 0x4c, 0x4f, 0x57, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, + 0x07, 0x53, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x35, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x45, + 0x54, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x56, 0x35, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x45, + 0x54, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x56, 0x39, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x50, + 0x46, 0x49, 0x58, 0x10, 0x04, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x65, 0x74, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2f, 0x67, + 0x6f, 0x66, 0x6c, 0x6f, 0x77, 0x32, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x65, 0x6e, 0x72, 0x69, 0x63, + 0x68, 0x65, 0x72, 0x2f, 0x70, 0x62, 0x3b, 0x66, 0x6c, 0x6f, 0x77, 0x70, 0x62, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_cmd_enricher_pb_flowext_proto_rawDescOnce sync.Once + file_cmd_enricher_pb_flowext_proto_rawDescData = file_cmd_enricher_pb_flowext_proto_rawDesc +) + +func file_cmd_enricher_pb_flowext_proto_rawDescGZIP() []byte { + file_cmd_enricher_pb_flowext_proto_rawDescOnce.Do(func() { + file_cmd_enricher_pb_flowext_proto_rawDescData = protoimpl.X.CompressGZIP(file_cmd_enricher_pb_flowext_proto_rawDescData) + }) + return file_cmd_enricher_pb_flowext_proto_rawDescData +} + +var file_cmd_enricher_pb_flowext_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_cmd_enricher_pb_flowext_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_cmd_enricher_pb_flowext_proto_goTypes = []interface{}{ + (FlowMessageExt_FlowType)(0), // 0: flowpb.FlowMessageExt.FlowType + (*FlowMessageExt)(nil), // 1: flowpb.FlowMessageExt +} +var file_cmd_enricher_pb_flowext_proto_depIdxs = []int32{ + 0, // 0: flowpb.FlowMessageExt.type:type_name -> flowpb.FlowMessageExt.FlowType + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_cmd_enricher_pb_flowext_proto_init() } +func file_cmd_enricher_pb_flowext_proto_init() { + if File_cmd_enricher_pb_flowext_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_cmd_enricher_pb_flowext_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FlowMessageExt); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_cmd_enricher_pb_flowext_proto_rawDesc, + NumEnums: 1, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_cmd_enricher_pb_flowext_proto_goTypes, + DependencyIndexes: file_cmd_enricher_pb_flowext_proto_depIdxs, + EnumInfos: file_cmd_enricher_pb_flowext_proto_enumTypes, + MessageInfos: file_cmd_enricher_pb_flowext_proto_msgTypes, + }.Build() + File_cmd_enricher_pb_flowext_proto = out.File + file_cmd_enricher_pb_flowext_proto_rawDesc = nil + file_cmd_enricher_pb_flowext_proto_goTypes = nil + file_cmd_enricher_pb_flowext_proto_depIdxs = nil +} diff --git a/third_party/goflow2/cmd/enricher/pb/flowext.proto b/third_party/goflow2/cmd/enricher/pb/flowext.proto new file mode 100644 index 000000000000..a779b925942f --- /dev/null +++ b/third_party/goflow2/cmd/enricher/pb/flowext.proto @@ -0,0 +1,115 @@ +syntax = "proto3"; +package flowpb; +option go_package = "github.com/netsampler/goflow2/cmd/enricher/pb;flowpb"; + +message FlowMessageExt { + + enum FlowType { + FLOWUNKNOWN = 0; + SFLOW_5 = 1; + NETFLOW_V5 = 2; + NETFLOW_V9 = 3; + IPFIX = 4; + } + FlowType type = 1; + + uint64 time_received = 2; + uint32 sequence_num = 4; + uint64 sampling_rate = 3; + + uint32 flow_direction = 42; + + // Sampler information + bytes sampler_address = 11; + + // Found inside packet + uint64 time_flow_start = 38; + uint64 time_flow_end = 5; + uint64 time_flow_start_ms = 63; + uint64 time_flow_end_ms = 64; + + // Size of the sampled packet + uint64 bytes = 9; + uint64 packets = 10; + + // Source/destination addresses + bytes src_addr = 6; + bytes dst_addr = 7; + + // Layer 3 protocol (IPv4/IPv6/ARP/MPLS...) + uint32 etype = 30; + + // Layer 4 protocol + uint32 proto = 20; + + // Ports for UDP and TCP + uint32 src_port = 21; + uint32 dst_port = 22; + + // Interfaces + uint32 in_if = 18; + uint32 out_if = 19; + + // Ethernet information + uint64 src_mac = 27; + uint64 dst_mac = 28; + + // Vlan + uint32 src_vlan = 33; + uint32 dst_vlan = 34; + // 802.1q VLAN in sampled packet + uint32 vlan_id = 29; + + // VRF + uint32 ingress_vrf_id = 39; + uint32 egress_vrf_id = 40; + + // IP and TCP special flags + uint32 ip_tos = 23; + uint32 forwarding_status = 24; + uint32 ip_ttl = 25; + uint32 tcp_flags = 26; + uint32 icmp_type = 31; + uint32 icmp_code = 32; + uint32 ipv6_flow_label = 37; + // Fragments (IPv4/IPv6) + uint32 fragment_id = 35; + uint32 fragment_offset = 36; + uint32 bi_flow_direction = 41; + + // Autonomous system information + uint32 src_as = 14; + uint32 dst_as = 15; + + bytes next_hop = 12; + uint32 next_hop_as = 13; + + // Prefix size + uint32 src_net = 16; + uint32 dst_net = 17; + + // BGP information + bytes bgp_next_hop = 100; + repeated uint32 bgp_communities = 101; + repeated uint32 as_path = 102; + + // MPLS information + bool has_mpls = 53; + uint32 mpls_count = 54; + uint32 mpls_1_ttl = 55; // First TTL + uint32 mpls_1_label = 56; // First Label + uint32 mpls_2_ttl = 57; // Second TTL + uint32 mpls_2_label = 58; // Second Label + uint32 mpls_3_ttl = 59; // Third TTL + uint32 mpls_3_label = 60; // Third Label + uint32 mpls_last_ttl = 61; // Last TTL + uint32 mpls_last_label = 62; // Last Label + bytes mpls_label_ip = 65; // MPLS TOP Label IP + + uint32 observation_domain_id = 70; + uint32 observation_point_id = 71; + + string src_country = 1000; + string dst_country = 1001; + +} diff --git a/third_party/goflow2/cmd/goflow2/main.go b/third_party/goflow2/cmd/goflow2/main.go new file mode 100644 index 000000000000..99a7a18129d9 --- /dev/null +++ b/third_party/goflow2/cmd/goflow2/main.go @@ -0,0 +1,199 @@ +package main + +import ( + "context" + "flag" + "fmt" + "net/http" + "net/url" + "os" + "strconv" + "strings" + "sync" + + // import various formatters + "github.com/netsampler/goflow2/format" + _ "github.com/netsampler/goflow2/format/json" + _ "github.com/netsampler/goflow2/format/protobuf" + _ "github.com/netsampler/goflow2/format/text" + + // import various transports + "github.com/netsampler/goflow2/transport" + _ "github.com/netsampler/goflow2/transport/file" + _ "github.com/netsampler/goflow2/transport/kafka" + + // import various NetFlow/IPFIX templates + "github.com/netsampler/goflow2/decoders/netflow/templates" + _ "github.com/netsampler/goflow2/decoders/netflow/templates/file" + _ "github.com/netsampler/goflow2/decoders/netflow/templates/memory" + + "github.com/netsampler/goflow2/utils" + "github.com/prometheus/client_golang/prometheus/promhttp" + log "github.com/sirupsen/logrus" +) + +var ( + version = "" + buildinfos = "" + AppVersion = "GoFlow2 " + version + " " + buildinfos + + ReusePort = flag.Bool("reuseport", false, "Enable so_reuseport") + ListenAddresses = flag.String("listen", "sflow://:6343,netflow://:2055", "listen addresses") + + Workers = flag.Int("workers", 1, "Number of workers per collector") + LogLevel = flag.String("loglevel", "info", "Log level") + LogFmt = flag.String("logfmt", "normal", "Log formatter") + + NetFlowTemplates = flag.String("netflow.templates", "memory", fmt.Sprintf("Choose the format (available: %s)", strings.Join(templates.GetTemplates(), ", "))) + + Format = flag.String("format", "json", fmt.Sprintf("Choose the format (available: %s)", strings.Join(format.GetFormats(), ", "))) + Transport = flag.String("transport", "file", fmt.Sprintf("Choose the transport (available: %s)", strings.Join(transport.GetTransports(), ", "))) + + MetricsAddr = flag.String("metrics.addr", ":8080", "Metrics address") + MetricsPath = flag.String("metrics.path", "/metrics", "Metrics path") + + TemplatePath = flag.String("templates.path", "/templates", "NetFlow/IPFIX templates list") + + MappingFile = flag.String("mapping", "", "Configuration file for custom mappings") + + Version = flag.Bool("v", false, "Print version") +) + +func httpServer( /*state *utils.StateNetFlow*/ ) { + http.Handle(*MetricsPath, promhttp.Handler()) + //http.HandleFunc(*TemplatePath, state.ServeHTTPTemplates) + log.Fatal(http.ListenAndServe(*MetricsAddr, nil)) +} + +func main() { + flag.Parse() + + if *Version { + fmt.Println(AppVersion) + os.Exit(0) + } + + lvl, _ := log.ParseLevel(*LogLevel) + log.SetLevel(lvl) + + var config utils.ProducerConfig + if *MappingFile != "" { + f, err := os.Open(*MappingFile) + if err != nil { + log.Fatal(err) + } + config, err = utils.LoadMapping(f) + f.Close() + if err != nil { + log.Fatal(err) + } + } + + ctx := context.Background() + + formatter, err := format.FindFormat(ctx, *Format) + if err != nil { + log.Fatal(err) + } + + transporter, err := transport.FindTransport(ctx, *Transport) + if err != nil { + log.Fatal(err) + } + defer transporter.Close(ctx) + + // the following is only useful when parsing NetFlowV9/IPFIX (template-based flow) + templateSystem, err := templates.FindTemplateSystem(ctx, *NetFlowTemplates) + if err != nil { + log.Fatal(err) + } + defer templateSystem.Close(ctx) + + switch *LogFmt { + case "json": + log.SetFormatter(&log.JSONFormatter{}) + } + + log.Info("Starting GoFlow2") + + go httpServer() + //go httpServer(sNF) + + wg := &sync.WaitGroup{} + + for _, listenAddress := range strings.Split(*ListenAddresses, ",") { + wg.Add(1) + go func(listenAddress string) { + defer wg.Done() + listenAddrUrl, err := url.Parse(listenAddress) + if err != nil { + log.Fatal(err) + } + numSockets := 1 + if listenAddrUrl.Query().Has("count") { + if numSocketsTmp, err := strconv.ParseUint(listenAddrUrl.Query().Get("count"), 10, 64); err != nil { + log.Fatal(err) + } else { + numSockets = int(numSocketsTmp) + } + } + if numSockets == 0 { + numSockets = 1 + } + + hostname := listenAddrUrl.Hostname() + port, err := strconv.ParseUint(listenAddrUrl.Port(), 10, 64) + if err != nil { + log.Errorf("Port %s could not be converted to integer", listenAddrUrl.Port()) + return + } + + logFields := log.Fields{ + "scheme": listenAddrUrl.Scheme, + "hostname": hostname, + "port": port, + "count": numSockets, + } + + log.WithFields(logFields).Info("Starting collection") + + for i := 0; i < numSockets; i++ { + if listenAddrUrl.Scheme == "sflow" { + sSFlow := &utils.StateSFlow{ + Format: formatter, + Transport: transporter, + Logger: log.StandardLogger(), + Config: config, + } + err = sSFlow.FlowRoutine(*Workers, hostname, int(port), *ReusePort) + } else if listenAddrUrl.Scheme == "netflow" { + sNF := utils.NewStateNetFlow() + sNF.Format = formatter + sNF.Transport = transporter + sNF.Logger = log.StandardLogger() + sNF.Config = config + sNF.TemplateSystem = templateSystem + err = sNF.FlowRoutine(*Workers, hostname, int(port), *ReusePort) + } else if listenAddrUrl.Scheme == "nfl" { + sNFL := &utils.StateNFLegacy{ + Format: formatter, + Transport: transporter, + Logger: log.StandardLogger(), + } + err = sNFL.FlowRoutine(*Workers, hostname, int(port), *ReusePort) + } else { + log.Errorf("scheme %s does not exist", listenAddrUrl.Scheme) + return + } + + if err != nil { + log.WithFields(logFields).Fatal(err) + } + } + + }(listenAddress) + + } + + wg.Wait() +} diff --git a/third_party/goflow2/cmd/goflow2/mapping.yaml b/third_party/goflow2/cmd/goflow2/mapping.yaml new file mode 100644 index 000000000000..bc0fb58ad0f1 --- /dev/null +++ b/third_party/goflow2/cmd/goflow2/mapping.yaml @@ -0,0 +1,31 @@ +ipfix: + mapping: + - field: 7 # IPFIX_FIELD_sourceTransportPort + destination: CustomInteger1 + - field: 11 # IPFIX_FIELD_destinationTransportPort + destination: CustomInteger2 + # penprovided: false + # pen: 0 + - field: 137 + destination: CustomList_1 + penprovided: true + pen: 2636 +netflowv9: + mapping: + - field: 7 + destination: CustomInteger1 + - field: 11 + destination: CustomInteger2 + - field: 34 # samplingInterval + destination: SamplingRate + endian: little +sflow: + mapping: + - layer: 4 # Layer 4: TCP or UDP + offset: 0 # Source port + length: 16 # 2 bytes + destination: CustomInteger1 + - layer: 4 + offset: 16 # Destination port + length: 16 # 2 bytes + destination: CustomInteger2 \ No newline at end of file diff --git a/third_party/goflow2/compose/elk/README.md b/third_party/goflow2/compose/elk/README.md new file mode 100644 index 000000000000..1fa01ee9c5da --- /dev/null +++ b/third_party/goflow2/compose/elk/README.md @@ -0,0 +1,25 @@ +# Flows + Logstash + Elastic + Kibana + +Clickhouse is a powerful data warehouse. + +A sample [docker-compose](./docker-compose.yml) is provided. +It's composed of: +* GoFlow2 +* Logstash +* Elastic +* Kibana + +To start the containers, use: +```bash +$ docker-compose up +``` + +This command will automatically build the GoFlow2 container. + +GoFlow2 collects NetFlow v9/IPFIX and sFlow packets and logs them into a file (`/var/log/goflow/goflow.log`). +Logstash collects the log messages, parse the JSON and sends to Elastic. +Kibana can be used to visualize the data. You can access the dashboard at http://localhost:5601. + +This stack requires to create an [index pattern](http://localhost:5601/app/management/kibana/indexPatterns/create). +Define the index pattern to be `logstash-*`. Select `@timestamp` to be the time filter. +You can then visualize flows in the [Discover](http://localhost:5601/app/discover) section. \ No newline at end of file diff --git a/third_party/goflow2/compose/elk/docker-compose.yml b/third_party/goflow2/compose/elk/docker-compose.yml new file mode 100644 index 000000000000..834769059c77 --- /dev/null +++ b/third_party/goflow2/compose/elk/docker-compose.yml @@ -0,0 +1,55 @@ +version: "3" +services: + goflow2: + build: + context: ../../ + dockerfile: Dockerfile + args: + VERSION: compose + LDFLAGS: -X main.version=compose + image: netsampler/goflow2 + user: root # because docker-compose mount as root + ports: + - '8080:8080' + - '6343:6343/udp' + - '2055:2055/udp' + command: + - -transport=file + - -transport.file=/var/log/goflow/goflow2.log + - -format=json + restart: always + logging: + driver: gelf + options: + gelf-address: "udp://localhost:12201" + tag: "flows" + volumes: + - logs:/var/log/goflow + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:7.13.0 + environment: + - discovery.type=single-node + ports: + - 9200:9200 + kibana: + image: docker.elastic.co/kibana/kibana:7.13.0 + ports: + - 5601:5601 + depends_on: + - elasticsearch + - logstash + logstash: + image: docker.elastic.co/logstash/logstash:7.13.0 + user: root # because docker-compose mount as root + links: + - elasticsearch + volumes: + - ./logstash.conf:/etc/logstash/logstash.conf + - logs:/var/log/goflow + command: logstash -f /etc/logstash/logstash.conf + ports: + - 12201:12201/udp + depends_on: + - elasticsearch +volumes: + logs: diff --git a/third_party/goflow2/compose/elk/logstash.conf b/third_party/goflow2/compose/elk/logstash.conf new file mode 100644 index 000000000000..0fea621c01b6 --- /dev/null +++ b/third_party/goflow2/compose/elk/logstash.conf @@ -0,0 +1,22 @@ +input { + gelf { + port => 12201 + } + file { + path => "/var/log/goflow/*.log" + type => "log" + } +} +filter { + json { + source => "message" + target => "flow" + remove_field => ["message"] + } +} +output { + elasticsearch { + hosts => ["elasticsearch:9200"] + index => "logstash-%{+YYYY-MM-dd}" + } +} \ No newline at end of file diff --git a/third_party/goflow2/compose/kcg/README.md b/third_party/goflow2/compose/kcg/README.md new file mode 100644 index 000000000000..17959d65d67f --- /dev/null +++ b/third_party/goflow2/compose/kcg/README.md @@ -0,0 +1,28 @@ +# Flows + Kafka + Clicklhouse + Grafana + Prometheus + +Clickhouse is a powerful data warehouse. + +A sample [docker-compose](./docker-compose.yml) is provided. +It's composed of: +* Apache Kafka +* Apache Zookeeper +* GoFlow2 +* Prometheus +* Clickhouse +* Grafana + +To start the containers, use: +```bash +$ docker-compose up +``` + +This command will automatically build Grafana and GoFlow2 containers. + +GoFlow2 collects NetFlow v9/IPFIX and sFlow packets and sends as a protobuf into Kafka. +Zookeeper coordinates Kafka and can also be used by Clickhouse to ensure replication. +Prometheus scrapes the metrics of the collector. +Clickhouse consumes from Kafka, stores raw data and aggregates over specific columns +using `MATERIALIZED TABLES` and `VIEWS` defined in a [schema file](./clickhouse/create.sh). + +You can visualize the data in Grafana at http://localhost:3000 (credentials: admin/admin) with the +pre-made dashboards. diff --git a/third_party/goflow2/compose/kcg/clickhouse/create.sh b/third_party/goflow2/compose/kcg/clickhouse/create.sh new file mode 100755 index 000000000000..0ccf95d56a35 --- /dev/null +++ b/third_party/goflow2/compose/kcg/clickhouse/create.sh @@ -0,0 +1,125 @@ +#!/bin/bash +set -e + +clickhouse client -n <<-EOSQL + + CREATE DATABASE IF NOT EXISTS dictionaries; + + CREATE DICTIONARY IF NOT EXISTS dictionaries.protocols ( + proto UInt8, + name String, + description String + ) + PRIMARY KEY proto + LAYOUT(FLAT()) + SOURCE (FILE(path '/var/lib/clickhouse/user_files/protocols.csv' format 'CSVWithNames')) + LIFETIME(3600); + + CREATE TABLE IF NOT EXISTS flows + ( + time_received UInt64, + time_flow_start UInt64, + + sequence_num UInt32, + sampling_rate UInt64, + sampler_address FixedString(16), + + src_addr FixedString(16), + dst_addr FixedString(16), + + src_as UInt32, + dst_as UInt32, + + etype UInt32, + proto UInt32, + + src_port UInt32, + dst_port UInt32, + + bytes UInt64, + packets UInt64 + ) ENGINE = Kafka() + SETTINGS + kafka_broker_list = 'kafka:9092', + kafka_topic_list = 'flows', + kafka_group_name = 'clickhouse', + kafka_format = 'Protobuf', + kafka_schema = 'flow.proto:FlowMessage'; + + CREATE TABLE IF NOT EXISTS flows_raw + ( + date Date, + time_received DateTime, + time_flow_start DateTime, + + sequence_num UInt32, + sampling_rate UInt64, + sampler_address FixedString(16), + + src_addr FixedString(16), + dst_addr FixedString(16), + + src_as UInt32, + dst_as UInt32, + + etype UInt32, + proto UInt32, + + src_port UInt32, + dst_port UInt32, + + bytes UInt64, + packets UInt64 + ) ENGINE = MergeTree() + PARTITION BY date + ORDER BY time_received; + + CREATE MATERIALIZED VIEW IF NOT EXISTS flows_raw_view TO flows_raw + AS SELECT + toDate(time_received) AS date, + * + FROM flows; + + CREATE TABLE IF NOT EXISTS flows_5m + ( + date Date, + timeslot DateTime, + + src_as UInt32, + dst_as UInt32, + + etypeMap Nested ( + etype UInt32, + bytes UInt64, + packets UInt64, + count UInt64 + ), + + bytes UInt64, + packets UInt64, + count UInt64 + ) ENGINE = SummingMergeTree() + PARTITION BY date + ORDER BY (date, timeslot, src_as, dst_as, \`etypeMap.etype\`); + + CREATE MATERIALIZED VIEW IF NOT EXISTS flows_5m_view TO flows_5m + AS + SELECT + date, + toStartOfFiveMinute(time_received) AS timeslot, + src_as, + dst_as, + + [etype] AS \`etypeMap.etype\`, + [bytes] AS \`etypeMap.bytes\`, + [packets] AS \`etypeMap.packets\`, + [count] AS \`etypeMap.count\`, + + sum(bytes) AS bytes, + sum(packets) AS packets, + count() AS count + + FROM flows_raw + GROUP BY date, timeslot, src_as, dst_as, \`etypeMap.etype\`; + +EOSQL diff --git a/third_party/goflow2/compose/kcg/clickhouse/protocols.csv b/third_party/goflow2/compose/kcg/clickhouse/protocols.csv new file mode 100644 index 000000000000..20065a9d9c8f --- /dev/null +++ b/third_party/goflow2/compose/kcg/clickhouse/protocols.csv @@ -0,0 +1,30 @@ +proto,name,description +0,HOPOPT,IPv6 Hop-by-Hop Option +1,ICMP,Internet Control Message +2,IGMP,Internet Group Management +4,IPv4,IPv4 encapsulation +6,TCP,Transmission Control Protocol +8,EGP,Exterior Gateway Protocol +9,IGP,Interior Gateway Protocol +16,CHAOS,Chaos +17,UDP,User Datagram Protocol +27,RDP,Reliable Data Protocol +41,IPv6,IPv6 encapsulation +43,IPv6-Route,Routing Header for IPv6 +44,IPv6-Frag,Fragment Header for IPv6 +45,IDRP,Inter-Domain Routing Protocol +46,RSVP,Reservation Protocol +47,GRE,Generic Routing Encapsulation +50,ESP,Encap Security Payload +51,AH,Authentication Header +55,MOBILE,IP Mobility +58,IPv6-ICMP,ICMP for IPv6 +59,IPv6-NoNxt,No Next Header for IPv6 +60,IPv6-Opts,Destination Options for IPv6 +88,EIGRP,EIGRP +89,OSPFIGP,OSPFIGP +92,MTP,Multicast Transport Protocol +94,IPIP,IP-within-IP Encapsulation Protocol +97,ETHERIP,Ethernet-within-IP Encapsulation +98,ENCAP,Encapsulation Header +112,VRRP,Virtual Router Redundancy Protocol \ No newline at end of file diff --git a/third_party/goflow2/compose/kcg/docker-compose.yml b/third_party/goflow2/compose/kcg/docker-compose.yml new file mode 100644 index 000000000000..01ef81a31073 --- /dev/null +++ b/third_party/goflow2/compose/kcg/docker-compose.yml @@ -0,0 +1,71 @@ +version: "3" +services: + zookeeper: + image: bitnami/zookeeper:3.7.1 + ports: + - 2181:2181 + environment: + - ALLOW_ANONYMOUS_LOGIN=yes + restart: always + kafka: + image: bitnami/kafka:3.4.0 + ports: + - 9092:9092 + environment: + - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 + - ALLOW_PLAINTEXT_LISTENER=yes + - KAFKA_DELETE_TOPIC_ENABLE=true + restart: always + depends_on: + - zookeeper + grafana: + image: grafana/grafana:9.4.3 + environment: + - GF_INSTALL_PLUGINS=vertamedia-clickhouse-datasource + # - GF_INSTALL_PLUGINS=grafana-clickhouse-datasource + # - GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=vertamedia-clickhouse-datasource + ports: + - 3000:3000 + restart: always + volumes: + - ./grafana/datasources-ch.yml:/etc/grafana/provisioning/datasources/datasources-ch.yml + - ./grafana/dashboards.yml:/etc/grafana/provisioning/dashboards/dashboards.yml + - ./grafana/dashboards:/var/lib/grafana/dashboards + prometheus: + image: prom/prometheus:v2.37.6 + ports: + - 9090:9090 + restart: always + volumes: + - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + goflow2: + build: + context: ../../ + dockerfile: Dockerfile + args: + VERSION: compose + LDFLAGS: -X main.version=compose + image: netsampler/goflow2 + depends_on: + - kafka + ports: + - 8080:8080 + - 6343:6343/udp + - 2055:2055/udp + restart: always + command: + - -transport.kafka.brokers=kafka:9092 + - -transport=kafka + - -transport.kafka.topic=flows + - -format=pb + - -format.protobuf.fixedlen=true + db: + image: clickhouse/clickhouse-server:22.8.14.53-alpine + ports: + - 8123:8123 + volumes: + - ./clickhouse:/docker-entrypoint-initdb.d/ + - ../../pb/flow.proto:/var/lib/clickhouse/format_schemas/flow.proto + - ./clickhouse/protocols.csv:/var/lib/clickhouse/user_files/protocols.csv + depends_on: + - kafka diff --git a/third_party/goflow2/compose/kcg/grafana/Dockerfile b/third_party/goflow2/compose/kcg/grafana/Dockerfile new file mode 100644 index 000000000000..424914ac0b78 --- /dev/null +++ b/third_party/goflow2/compose/kcg/grafana/Dockerfile @@ -0,0 +1,8 @@ +FROM ubuntu AS builder + +RUN apt-get update && apt-get install -y git +RUN git clone https://github.com/Vertamedia/clickhouse-grafana.git + +FROM grafana/grafana:9.1.7 + +COPY --from=builder /clickhouse-grafana /var/lib/grafana/plugins \ No newline at end of file diff --git a/third_party/goflow2/compose/kcg/grafana/dashboards.yml b/third_party/goflow2/compose/kcg/grafana/dashboards.yml new file mode 100644 index 000000000000..909a621ca187 --- /dev/null +++ b/third_party/goflow2/compose/kcg/grafana/dashboards.yml @@ -0,0 +1,6 @@ +- name: 'default' + org_id: 1 + folder: '' + type: file + options: + folder: /var/lib/grafana/dashboards \ No newline at end of file diff --git a/third_party/goflow2/compose/kcg/grafana/dashboards/perfs.json b/third_party/goflow2/compose/kcg/grafana/dashboards/perfs.json new file mode 100644 index 000000000000..957b6904132c --- /dev/null +++ b/third_party/goflow2/compose/kcg/grafana/dashboards/perfs.json @@ -0,0 +1,2106 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Metrics about the NetFlow+sFlow collector", + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 2, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 53, + "panels": [], + "repeat": null, + "title": "Totals", + "type": "row" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "Prometheus", + "format": "pps", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 9, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(rate(flow_process_nf_flowset_records_sum[5m]))", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "title": "NetFlows Total", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "Prometheus", + "format": "pps", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 10, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(rate(flow_process_sf_samples_sum[5m]))", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "title": "sFlows Total", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 54, + "panels": [], + "repeat": null, + "title": "Flows UDP Metrics", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 10, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 51, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/Total*/", + "color": "#DEDAF7", + "fill": 0, + "stack": false + } + ], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(udp_traffic_bytes[5m])) by (remote_ip)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ remote_ip }}", + "refId": "A" + }, + { + "expr": "sum(rate(udp_traffic_bytes[5m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "Total", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "UDP2Kafka Bandwidth", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 10, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 52, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/Total*/", + "color": "#DEDAF7", + "fill": 0, + "stack": false + } + ], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(udp_traffic_packets[5m])) by (remote_ip)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ remote_ip }}", + "refId": "A" + }, + { + "expr": "sum(rate(udp_traffic_packets[5m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "Total", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "UDP2Kafka packets", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 10, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 1, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/Total*/", + "color": "#DEDAF7", + "fill": 0, + "stack": false + } + ], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_traffic_bytes{type=\"sFlow\"}[5m])) by (remote_ip)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ remote_ip }}", + "refId": "A" + }, + { + "aggregator": "sum", + "alias": "$tag_type | Total", + "currentTagKey": "", + "currentTagValue": "", + "downsampleAggregator": "avg", + "downsampleFillPolicy": "none", + "downsampleInterval": "", + "expr": "sum(rate(flow_traffic_bytes{type=\"sFlow\"}[5m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "sFlow | Total", + "metric": "goflow.flow_traffic_bytes", + "refId": "B", + "shouldComputeRate": true, + "tags": { + "type": "sFlow" + } + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "sFlow UDP Bandwidth", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 10, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/Total*/", + "color": "#DEDAF7", + "fill": 0, + "stack": false + } + ], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_traffic_packets{type=\"sFlow\"}[5m])) by (remote_ip)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ remote_ip }}", + "refId": "A" + }, + { + "aggregator": "sum", + "alias": "$tag_type | Total", + "currentTagKey": "", + "currentTagValue": "", + "downsampleAggregator": "avg", + "downsampleFillPolicy": "none", + "downsampleInterval": "", + "expr": "sum(rate(flow_traffic_packets{type=\"sFlow\"}[5m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "sFlow | Total", + "metric": "goflow-kafka.flow_traffic_packets", + "refId": "B", + "shouldComputeRate": true, + "tags": { + "type": "sFlow" + } + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "sFlow UDP Packets", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 10, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 23 + }, + "id": 48, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/Total*/", + "color": "#DEDAF7", + "fill": 0, + "stack": false + } + ], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_traffic_bytes{type=\"NetFlow\"}[5m])) by (remote_ip)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ remote_ip }}", + "refId": "A" + }, + { + "expr": "sum(rate(flow_traffic_bytes{type=\"NetFlow\"}[5m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "NetFlow | Total", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "NetFlow UDP Bandwidth", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 10, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 23 + }, + "id": 47, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/Total*/", + "color": "#DEDAF7", + "fill": 0, + "stack": false + } + ], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_traffic_packets{type=\"NetFlow\"}[5m])) by (remote_ip)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ remote_ip }}", + "refId": "A" + }, + { + "aggregator": "sum", + "alias": "$tag_type | Total", + "currentTagKey": "", + "currentTagValue": "", + "downsampleAggregator": "avg", + "downsampleFillPolicy": "none", + "downsampleInterval": "", + "expr": "sum(rate(flow_traffic_packets{type=\"NetFlow\"}[5m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "NetFlow | Total", + "metric": "goflow-kafka.flow_traffic_packets", + "refId": "B", + "shouldComputeRate": true, + "tags": { + "type": "NetFlow" + } + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "NetFlow UDP Packets", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 30 + }, + "id": 55, + "panels": [], + "repeat": null, + "title": "NetFlow metrics", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 3, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_process_nf_flowset_records_sum[5m])) by (version,type)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ version }} | {{ type }} ", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "NetFlows by type and version", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_process_nf_errors_count[5m])) by (error)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ error }}", + "metric": "goflow-kafka.flow_process_nf_errors_count", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "NetFlows errors", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "hertz", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 38 + }, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_process_nf_templates_count[5m])) by (version,router,type)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ version }} | {{ router }} | {{ type }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "NetFlow templates", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 38 + }, + "id": 7, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_process_nf_flowset_records_sum[5m])) by (router,version)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ router }} | {{ version }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "NetFlows - DataFlowSets by router and version", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 0, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 45 + }, + "id": 14, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_process_nf_delay_summary_seconds[5m])) by (quantile,router,version)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "Q{{ quantile }} | {{ router }} | {{ version }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Time between flow and processing", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 52 + }, + "id": 56, + "panels": [], + "repeat": null, + "title": "sFlow metrics", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 53 + }, + "id": 5, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_process_sf_samples_sum[5m])) by (version,type)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ version }} | {{ type }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "sFlows by type", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 53 + }, + "id": 8, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_process_sf_samples_records_sum{type=\"FlowSample\"}[5m])) by (agent,version)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ agent }} | {{ version }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "sFlows Flow records by agent and version", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 60 + }, + "id": 57, + "panels": [], + "repeat": null, + "title": "NetFlow decoder metrics", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 61 + }, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "rightSide": false, + "show": true, + "sort": "current", + "sortDesc": false, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(flow_summary_decoding_time_us{name=\"NetFlow\"}) by (quantile)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "Q{{ quantile }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "NetFlow decoding time quantiles", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 61 + }, + "id": 12, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "rightSide": false, + "show": true, + "sort": "current", + "sortDesc": false, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(flow_summary_processing_time_us{name=\"NetFlow\"}) by (quantile)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "Q{{ quantile }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "NetFlow processing time quantiles", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 61 + }, + "id": 13, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "rightSide": false, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_decoder_count{name=\"NetFlow\"}[5m])) by (worker)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "Worker {{ worker }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "NetFlow worker rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 68 + }, + "id": 58, + "panels": [], + "repeat": null, + "title": "sFlow decoder metrics", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 69 + }, + "id": 15, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "sort": "current", + "sortDesc": false, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(flow_summary_decoding_time_us{name=\"sFlow\"}) by (quantile)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "Q{{ quantile }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "sFlow decoding time quantiles", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 69 + }, + "id": 16, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "sort": "current", + "sortDesc": false, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(flow_summary_decoding_time_us{name=\"sFlow\"}) by (quantile)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "Q{{ quantile }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "sFlow processing time quantiles", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "µs", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 69 + }, + "id": 17, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "rightSide": false, + "show": true, + "sort": null, + "sortDesc": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(flow_decoder_count{name=\"sFlow\"}[5m])) by (worker)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "Worker {{ worker }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "sFlow worker rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": false, + "schemaVersion": 16, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "utc", + "title": "GoFlow - Internals", + "uid": "4U5xQZPmz", + "version": 2 +} \ No newline at end of file diff --git a/third_party/goflow2/compose/kcg/grafana/dashboards/viz-ch.json b/third_party/goflow2/compose/kcg/grafana/dashboards/viz-ch.json new file mode 100644 index 000000000000..e92ecda0c10f --- /dev/null +++ b/third_party/goflow2/compose/kcg/grafana/dashboards/viz-ch.json @@ -0,0 +1,643 @@ +{ + "annotations": { + "list": [ + { + "$$hashKey": "object:631", + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "ClickHouse", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxDataPoints": 200, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "database": "default", + "dateColDataType": "date", + "dateLoading": false, + "dateTimeColDataType": "time_flow_start", + "dateTimeType": "DATETIME", + "datetimeLoading": false, + "format": "time_series", + "group": [], + "intervalFactor": 1, + "metricColumn": "none", + "query": "SELECT\n t,\n sum(sumbytes) AS sumbytes\nFROM (\n SELECT\n $timeSeries AS t,\n sum(bytes*sampling_rate) as sumbytes\n FROM $table\n WHERE $timeFilter\n GROUP BY t\n\n UNION ALL\n\n SELECT\n intDiv($from+number*$interval, $interval)*$interval*1000 AS t,\n 0 AS sumbytes\n FROM numbers(intDiv($to-$from, $interval))\n)\nGROUP BY t\nORDER BY t", "refId": "A", + "round": "0s", + "select": [ + [ + { + "params": [ + "bytes" + ], + "type": "column" + } + ] + ], + "table": "flows_raw", + "tableLoading": false, + "timeColumn": "date_inserted", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "Instant traffic", + "type": "timeseries" + }, + { + "columns": [], + "datasource": "ClickHouse", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fontSize": "100%", + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 7, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": 1, + "desc": true + }, + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 0, + "mappingType": 1, + "pattern": ".*_port", + "thresholds": [], + "type": "number", + "unit": "none" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "sumbytes", + "thresholds": [], + "type": "number", + "unit": "decbytes" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 0, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "database": "default", + "dateColDataType": "date", + "dateLoading": false, + "dateTimeColDataType": "time_flow_start", + "dateTimeType": "DATETIME", + "datetimeLoading": false, + "format": "table", + "group": [], + "intervalFactor": 1, + "metricColumn": "none", + "query": "SELECT\n if(etype = 0x800, IPv4NumToString(reinterpretAsUInt32(substring(reverse(src_addr), 13,4))), IPv6NumToString(src_addr)) as srcip,\n sum(bytes*sampling_rate) AS sumbytes\nFROM $table\nWHERE $timeFilter\nGROUP BY srcip\nORDER BY sumbytes DESC", + "refId": "A", + "round": "0s", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "column" + } + ] + ], + "table": "flows_raw", + "tableLoading": false, + "timeColumn": "time", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "Top source IPs", + "transform": "table", + "type": "table" + }, + { + "columns": [], + "datasource": "ClickHouse", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fontSize": "100%", + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 9, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": 1, + "desc": true + }, + "styles": [ + { + "$$hashKey": "object:1506", + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "$$hashKey": "object:1507", + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 0, + "mappingType": 1, + "pattern": "port", + "thresholds": [], + "type": "number", + "unit": "none" + }, + { + "$$hashKey": "object:1508", + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "sumbytes", + "thresholds": [], + "type": "number", + "unit": "decbytes" + }, + { + "$$hashKey": "object:1509", + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 0, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "database": "default", + "dateColDataType": "date", + "dateLoading": false, + "dateTimeColDataType": "time_flow_start", + "dateTimeType": "DATETIME", + "datetimeLoading": false, + "extrapolate": true, + "format": "table", + "group": [], + "intervalFactor": 1, + "metricColumn": "none", + "query": "WITH dictGetString('dictionaries.protocols', 'name', toUInt64(proto)) AS protoName\nSELECT\n if(protoName = '', toString(proto), protoName) || '/' || toString(src_port) as port,\n sum(bytes*sampling_rate) AS sumbytes\nFROM $table\nWHERE $timeFilter\nGROUP BY port\nORDER BY sumbytes DESC", + "refId": "A", + "round": "0s", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "column" + } + ] + ], + "table": "flows_raw", + "tableLoading": false, + "timeColumn": "time", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "Top source ports", + "transform": "table", + "type": "table" + }, + { + "columns": [], + "datasource": "ClickHouse", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fontSize": "100%", + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 10, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": 1, + "desc": true + }, + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 0, + "mappingType": 1, + "pattern": ".*_port", + "thresholds": [], + "type": "number", + "unit": "none" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "sumbytes", + "thresholds": [], + "type": "number", + "unit": "decbytes" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 0, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "database": "default", + "dateColDataType": "date", + "dateLoading": false, + "dateTimeColDataType": "time_flow_start", + "dateTimeType": "DATETIME", + "datetimeLoading": false, + "format": "table", + "group": [], + "intervalFactor": 1, + "metricColumn": "none", + "query": "SELECT\n if(etype = 0x800, IPv4NumToString(reinterpretAsUInt32(substring(reverse(dst_addr), 13,4))), IPv6NumToString(dst_addr)) as dstip,\n sum(bytes*sampling_rate) AS sumbytes\nFROM $table\nWHERE $timeFilter\nGROUP BY dstip\nORDER BY sumbytes DESC", + "refId": "A", + "round": "0s", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "column" + } + ] + ], + "table": "flows_raw", + "tableLoading": false, + "timeColumn": "time", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "Top destination IPs", + "transform": "table", + "type": "table" + }, + { + "columns": [], + "datasource": "ClickHouse", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fontSize": "100%", + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 11, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": 1, + "desc": false + }, + "styles": [ + { + "$$hashKey": "object:1428", + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "$$hashKey": "object:1429", + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 0, + "mappingType": 1, + "pattern": "port", + "thresholds": [], + "type": "number", + "unit": "none" + }, + { + "$$hashKey": "object:1430", + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "sumbytes", + "thresholds": [], + "type": "number", + "unit": "decbytes" + }, + { + "$$hashKey": "object:1431", + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 0, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "database": "default", + "dateColDataType": "date", + "dateLoading": false, + "dateTimeColDataType": "time_flow_start", + "dateTimeType": "DATETIME", + "datetimeLoading": false, + "extrapolate": true, + "format": "table", + "group": [], + "intervalFactor": 1, + "metricColumn": "none", + "query": "WITH dictGetString('dictionaries.protocols', 'name', toUInt64(proto)) AS protoName\nSELECT\n if(protoName = '', toString(proto), protoName) || '/' || toString(dst_port) as port,\n sum(bytes*sampling_rate) AS sumbytes\nFROM $table\nWHERE $timeFilter\nGROUP BY port\nORDER BY sumbytes DESC", + "refId": "A", + "round": "0s", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "column" + } + ] + ], + "table": "flows_raw", + "tableLoading": false, + "timeColumn": "time", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "Top destination ports", + "transform": "table", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 25, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "Traffic (ClickHouse)", + "uid": "tkNEAd9Zk", + "version": 1 +} \ No newline at end of file diff --git a/third_party/goflow2/compose/kcg/grafana/datasources-ch.yml b/third_party/goflow2/compose/kcg/grafana/datasources-ch.yml new file mode 100644 index 000000000000..60e93dce6a20 --- /dev/null +++ b/third_party/goflow2/compose/kcg/grafana/datasources-ch.yml @@ -0,0 +1,26 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + url: http://prometheus:9090 + version: 1 + editable: true + - name: ClickHouse + type: vertamedia-clickhouse-datasource + typeLogoUrl: '' + access: proxy + url: http://db:8123 + password: '' + user: '' + database: '' + basicAuth: false + basicAuthUser: '' + basicAuthPassword: '' + withCredentials: false + isDefault: true + secureJsonFields: {} + version: 3 + readOnly: false diff --git a/third_party/goflow2/compose/kcg/prometheus/prometheus.yml b/third_party/goflow2/compose/kcg/prometheus/prometheus.yml new file mode 100644 index 000000000000..b7745309b2cc --- /dev/null +++ b/third_party/goflow2/compose/kcg/prometheus/prometheus.yml @@ -0,0 +1,14 @@ +global: + scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. +alerting: + alertmanagers: + - static_configs: + - targets: + +rule_files: + +scrape_configs: + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090', 'goflow2:8080'] \ No newline at end of file diff --git a/third_party/goflow2/decoders/decoder.go b/third_party/goflow2/decoders/decoder.go new file mode 100644 index 000000000000..8eebaf3bd473 --- /dev/null +++ b/third_party/goflow2/decoders/decoder.go @@ -0,0 +1,115 @@ +package decoder + +import ( + "time" +) + +type Message interface{} +type MessageDecoded interface{} + +type DecoderFunc func(Message interface{}) error +type DoneCallback func(string, int, time.Time, time.Time) +type ErrorCallback func(string, int, time.Time, time.Time, error) + +// Worker structure +type Worker struct { + Id int + DecoderParams DecoderParams + WorkerPool chan chan Message + Name string + InMsg chan Message + Quit chan bool +} + +// Create a worker and add it to the pool. +func CreateWorker(workerPool chan chan Message, decoderParams DecoderParams, id int, name string) Worker { + return Worker{ + Id: id, + DecoderParams: decoderParams, + WorkerPool: workerPool, + Name: name, + InMsg: make(chan Message), + Quit: make(chan bool), + } +} + +// Start the worker. Launches a goroutine to process NFv9 messages. +// The worker will add its input channel of NFv9 messages to decode to the pool. +func (w Worker) Start() { + go func() { + //log.Debugf("Worker %v started", w.Id) + for { + select { + case <-w.Quit: + break + case w.WorkerPool <- w.InMsg: + msg := <-w.InMsg + timeTrackStart := time.Now() + err := w.DecoderParams.DecoderFunc(msg) + timeTrackStop := time.Now() + + if err != nil && w.DecoderParams.ErrorCallback != nil { + w.DecoderParams.ErrorCallback(w.Name, w.Id, timeTrackStart, timeTrackStop, err) + } else if err == nil && w.DecoderParams.DoneCallback != nil { + w.DecoderParams.DoneCallback(w.Name, w.Id, timeTrackStart, timeTrackStop) + } + } + } + //log.Debugf("Worker %v done", w.Id) + }() +} + +// Stop the worker. +func (w Worker) Stop() { + //log.Debugf("Stopping worker %v", w.Id) + w.Quit <- true +} + +// Processor structure +type Processor struct { + workerpool chan chan Message + workerlist []Worker + DecoderParams DecoderParams + Name string +} + +// Decoder structure. Define the function to call and the config specific to the type of packets. +type DecoderParams struct { + DecoderFunc DecoderFunc + DoneCallback DoneCallback + ErrorCallback ErrorCallback +} + +// Create a message processor which is going to create all the workers and set-up the pool. +func CreateProcessor(numWorkers int, decoderParams DecoderParams, name string) Processor { + processor := Processor{ + workerpool: make(chan chan Message), + workerlist: make([]Worker, numWorkers), + DecoderParams: decoderParams, + Name: name, + } + for i := 0; i < numWorkers; i++ { + worker := CreateWorker(processor.workerpool, decoderParams, i, name) + processor.workerlist[i] = worker + } + return processor +} + +// Start message processor +func (p Processor) Start() { + for _, worker := range p.workerlist { + worker.Start() + } +} + +func (p Processor) Stop() { + for _, worker := range p.workerlist { + worker.Stop() + } +} + +// Send a message to be decoded to the pool. +func (p Processor) ProcessMessage(msg Message) { + sendChannel := <-p.workerpool + sendChannel <- msg +} diff --git a/third_party/goflow2/decoders/netflow/ipfix.go b/third_party/goflow2/decoders/netflow/ipfix.go new file mode 100644 index 000000000000..954b7d38cc2f --- /dev/null +++ b/third_party/goflow2/decoders/netflow/ipfix.go @@ -0,0 +1,989 @@ +package netflow + +import ( + "fmt" + "time" +) + +const ( + IPFIX_FIELD_Reserved = 0 + IPFIX_FIELD_octetDeltaCount = 1 + IPFIX_FIELD_packetDeltaCount = 2 + IPFIX_FIELD_deltaFlowCount = 3 + IPFIX_FIELD_protocolIdentifier = 4 + IPFIX_FIELD_ipClassOfService = 5 + IPFIX_FIELD_tcpControlBits = 6 + IPFIX_FIELD_sourceTransportPort = 7 + IPFIX_FIELD_sourceIPv4Address = 8 + IPFIX_FIELD_sourceIPv4PrefixLength = 9 + IPFIX_FIELD_ingressInterface = 10 + IPFIX_FIELD_destinationTransportPort = 11 + IPFIX_FIELD_destinationIPv4Address = 12 + IPFIX_FIELD_destinationIPv4PrefixLength = 13 + IPFIX_FIELD_egressInterface = 14 + IPFIX_FIELD_ipNextHopIPv4Address = 15 + IPFIX_FIELD_bgpSourceAsNumber = 16 + IPFIX_FIELD_bgpDestinationAsNumber = 17 + IPFIX_FIELD_bgpNextHopIPv4Address = 18 + IPFIX_FIELD_postMCastPacketDeltaCount = 19 + IPFIX_FIELD_postMCastOctetDeltaCount = 20 + IPFIX_FIELD_flowEndSysUpTime = 21 + IPFIX_FIELD_flowStartSysUpTime = 22 + IPFIX_FIELD_postOctetDeltaCount = 23 + IPFIX_FIELD_postPacketDeltaCount = 24 + IPFIX_FIELD_minimumIpTotalLength = 25 + IPFIX_FIELD_maximumIpTotalLength = 26 + IPFIX_FIELD_sourceIPv6Address = 27 + IPFIX_FIELD_destinationIPv6Address = 28 + IPFIX_FIELD_sourceIPv6PrefixLength = 29 + IPFIX_FIELD_destinationIPv6PrefixLength = 30 + IPFIX_FIELD_flowLabelIPv6 = 31 + IPFIX_FIELD_icmpTypeCodeIPv4 = 32 + IPFIX_FIELD_igmpType = 33 + IPFIX_FIELD_samplingInterval = 34 + IPFIX_FIELD_samplingAlgorithm = 35 + IPFIX_FIELD_flowActiveTimeout = 36 + IPFIX_FIELD_flowIdleTimeout = 37 + IPFIX_FIELD_engineType = 38 + IPFIX_FIELD_engineId = 39 + IPFIX_FIELD_exportedOctetTotalCount = 40 + IPFIX_FIELD_exportedMessageTotalCount = 41 + IPFIX_FIELD_exportedFlowRecordTotalCount = 42 + IPFIX_FIELD_ipv4RouterSc = 43 + IPFIX_FIELD_sourceIPv4Prefix = 44 + IPFIX_FIELD_destinationIPv4Prefix = 45 + IPFIX_FIELD_mplsTopLabelType = 46 + IPFIX_FIELD_mplsTopLabelIPv4Address = 47 + IPFIX_FIELD_samplerId = 48 + IPFIX_FIELD_samplerMode = 49 + IPFIX_FIELD_samplerRandomInterval = 50 + IPFIX_FIELD_classId = 51 + IPFIX_FIELD_minimumTTL = 52 + IPFIX_FIELD_maximumTTL = 53 + IPFIX_FIELD_fragmentIdentification = 54 + IPFIX_FIELD_postIpClassOfService = 55 + IPFIX_FIELD_sourceMacAddress = 56 + IPFIX_FIELD_postDestinationMacAddress = 57 + IPFIX_FIELD_vlanId = 58 + IPFIX_FIELD_postVlanId = 59 + IPFIX_FIELD_ipVersion = 60 + IPFIX_FIELD_flowDirection = 61 + IPFIX_FIELD_ipNextHopIPv6Address = 62 + IPFIX_FIELD_bgpNextHopIPv6Address = 63 + IPFIX_FIELD_ipv6ExtensionHeaders = 64 + IPFIX_FIELD_mplsTopLabelStackSection = 70 + IPFIX_FIELD_mplsLabelStackSection2 = 71 + IPFIX_FIELD_mplsLabelStackSection3 = 72 + IPFIX_FIELD_mplsLabelStackSection4 = 73 + IPFIX_FIELD_mplsLabelStackSection5 = 74 + IPFIX_FIELD_mplsLabelStackSection6 = 75 + IPFIX_FIELD_mplsLabelStackSection7 = 76 + IPFIX_FIELD_mplsLabelStackSection8 = 77 + IPFIX_FIELD_mplsLabelStackSection9 = 78 + IPFIX_FIELD_mplsLabelStackSection10 = 79 + IPFIX_FIELD_destinationMacAddress = 80 + IPFIX_FIELD_postSourceMacAddress = 81 + IPFIX_FIELD_interfaceName = 82 + IPFIX_FIELD_interfaceDescription = 83 + IPFIX_FIELD_samplerName = 84 + IPFIX_FIELD_octetTotalCount = 85 + IPFIX_FIELD_packetTotalCount = 86 + IPFIX_FIELD_flagsAndSamplerId = 87 + IPFIX_FIELD_fragmentOffset = 88 + IPFIX_FIELD_forwardingStatus = 89 + IPFIX_FIELD_mplsVpnRouteDistinguisher = 90 + IPFIX_FIELD_mplsTopLabelPrefixLength = 91 + IPFIX_FIELD_srcTrafficIndex = 92 + IPFIX_FIELD_dstTrafficIndex = 93 + IPFIX_FIELD_applicationDescription = 94 + IPFIX_FIELD_applicationId = 95 + IPFIX_FIELD_applicationName = 96 + IPFIX_FIELD_postIpDiffServCodePoint = 98 + IPFIX_FIELD_multicastReplicationFactor = 99 + IPFIX_FIELD_className = 100 + IPFIX_FIELD_classificationEngineId = 101 + IPFIX_FIELD_layer2packetSectionOffset = 102 + IPFIX_FIELD_layer2packetSectionSize = 103 + IPFIX_FIELD_layer2packetSectionData = 104 + IPFIX_FIELD_bgpNextAdjacentAsNumber = 128 + IPFIX_FIELD_bgpPrevAdjacentAsNumber = 129 + IPFIX_FIELD_exporterIPv4Address = 130 + IPFIX_FIELD_exporterIPv6Address = 131 + IPFIX_FIELD_droppedOctetDeltaCount = 132 + IPFIX_FIELD_droppedPacketDeltaCount = 133 + IPFIX_FIELD_droppedOctetTotalCount = 134 + IPFIX_FIELD_droppedPacketTotalCount = 135 + IPFIX_FIELD_flowEndReason = 136 + IPFIX_FIELD_commonPropertiesId = 137 + IPFIX_FIELD_observationPointId = 138 + IPFIX_FIELD_icmpTypeCodeIPv6 = 139 + IPFIX_FIELD_mplsTopLabelIPv6Address = 140 + IPFIX_FIELD_lineCardId = 141 + IPFIX_FIELD_portId = 142 + IPFIX_FIELD_meteringProcessId = 143 + IPFIX_FIELD_exportingProcessId = 144 + IPFIX_FIELD_templateId = 145 + IPFIX_FIELD_wlanChannelId = 146 + IPFIX_FIELD_wlanSSID = 147 + IPFIX_FIELD_flowId = 148 + IPFIX_FIELD_observationDomainId = 149 + IPFIX_FIELD_flowStartSeconds = 150 + IPFIX_FIELD_flowEndSeconds = 151 + IPFIX_FIELD_flowStartMilliseconds = 152 + IPFIX_FIELD_flowEndMilliseconds = 153 + IPFIX_FIELD_flowStartMicroseconds = 154 + IPFIX_FIELD_flowEndMicroseconds = 155 + IPFIX_FIELD_flowStartNanoseconds = 156 + IPFIX_FIELD_flowEndNanoseconds = 157 + IPFIX_FIELD_flowStartDeltaMicroseconds = 158 + IPFIX_FIELD_flowEndDeltaMicroseconds = 159 + IPFIX_FIELD_systemInitTimeMilliseconds = 160 + IPFIX_FIELD_flowDurationMilliseconds = 161 + IPFIX_FIELD_flowDurationMicroseconds = 162 + IPFIX_FIELD_observedFlowTotalCount = 163 + IPFIX_FIELD_ignoredPacketTotalCount = 164 + IPFIX_FIELD_ignoredOctetTotalCount = 165 + IPFIX_FIELD_notSentFlowTotalCount = 166 + IPFIX_FIELD_notSentPacketTotalCount = 167 + IPFIX_FIELD_notSentOctetTotalCount = 168 + IPFIX_FIELD_destinationIPv6Prefix = 169 + IPFIX_FIELD_sourceIPv6Prefix = 170 + IPFIX_FIELD_postOctetTotalCount = 171 + IPFIX_FIELD_postPacketTotalCount = 172 + IPFIX_FIELD_flowKeyIndicator = 173 + IPFIX_FIELD_postMCastPacketTotalCount = 174 + IPFIX_FIELD_postMCastOctetTotalCount = 175 + IPFIX_FIELD_icmpTypeIPv4 = 176 + IPFIX_FIELD_icmpCodeIPv4 = 177 + IPFIX_FIELD_icmpTypeIPv6 = 178 + IPFIX_FIELD_icmpCodeIPv6 = 179 + IPFIX_FIELD_udpSourcePort = 180 + IPFIX_FIELD_udpDestinationPort = 181 + IPFIX_FIELD_tcpSourcePort = 182 + IPFIX_FIELD_tcpDestinationPort = 183 + IPFIX_FIELD_tcpSequenceNumber = 184 + IPFIX_FIELD_tcpAcknowledgementNumber = 185 + IPFIX_FIELD_tcpWindowSize = 186 + IPFIX_FIELD_tcpUrgentPointer = 187 + IPFIX_FIELD_tcpHeaderLength = 188 + IPFIX_FIELD_ipHeaderLength = 189 + IPFIX_FIELD_totalLengthIPv4 = 190 + IPFIX_FIELD_payloadLengthIPv6 = 191 + IPFIX_FIELD_ipTTL = 192 + IPFIX_FIELD_nextHeaderIPv6 = 193 + IPFIX_FIELD_mplsPayloadLength = 194 + IPFIX_FIELD_ipDiffServCodePoint = 195 + IPFIX_FIELD_ipPrecedence = 196 + IPFIX_FIELD_fragmentFlags = 197 + IPFIX_FIELD_octetDeltaSumOfSquares = 198 + IPFIX_FIELD_octetTotalSumOfSquares = 199 + IPFIX_FIELD_mplsTopLabelTTL = 200 + IPFIX_FIELD_mplsLabelStackLength = 201 + IPFIX_FIELD_mplsLabelStackDepth = 202 + IPFIX_FIELD_mplsTopLabelExp = 203 + IPFIX_FIELD_ipPayloadLength = 204 + IPFIX_FIELD_udpMessageLength = 205 + IPFIX_FIELD_isMulticast = 206 + IPFIX_FIELD_ipv4IHL = 207 + IPFIX_FIELD_ipv4Options = 208 + IPFIX_FIELD_tcpOptions = 209 + IPFIX_FIELD_paddingOctets = 210 + IPFIX_FIELD_collectorIPv4Address = 211 + IPFIX_FIELD_collectorIPv6Address = 212 + IPFIX_FIELD_exportInterface = 213 + IPFIX_FIELD_exportProtocolVersion = 214 + IPFIX_FIELD_exportTransportProtocol = 215 + IPFIX_FIELD_collectorTransportPort = 216 + IPFIX_FIELD_exporterTransportPort = 217 + IPFIX_FIELD_tcpSynTotalCount = 218 + IPFIX_FIELD_tcpFinTotalCount = 219 + IPFIX_FIELD_tcpRstTotalCount = 220 + IPFIX_FIELD_tcpPshTotalCount = 221 + IPFIX_FIELD_tcpAckTotalCount = 222 + IPFIX_FIELD_tcpUrgTotalCount = 223 + IPFIX_FIELD_ipTotalLength = 224 + IPFIX_FIELD_postNATSourceIPv4Address = 225 + IPFIX_FIELD_postNATDestinationIPv4Address = 226 + IPFIX_FIELD_postNAPTSourceTransportPort = 227 + IPFIX_FIELD_postNAPTDestinationTransportPort = 228 + IPFIX_FIELD_natOriginatingAddressRealm = 229 + IPFIX_FIELD_natEvent = 230 + IPFIX_FIELD_initiatorOctets = 231 + IPFIX_FIELD_responderOctets = 232 + IPFIX_FIELD_firewallEvent = 233 + IPFIX_FIELD_ingressVRFID = 234 + IPFIX_FIELD_egressVRFID = 235 + IPFIX_FIELD_VRFname = 236 + IPFIX_FIELD_postMplsTopLabelExp = 237 + IPFIX_FIELD_tcpWindowScale = 238 + IPFIX_FIELD_biflowDirection = 239 + IPFIX_FIELD_ethernetHeaderLength = 240 + IPFIX_FIELD_ethernetPayloadLength = 241 + IPFIX_FIELD_ethernetTotalLength = 242 + IPFIX_FIELD_dot1qVlanId = 243 + IPFIX_FIELD_dot1qPriority = 244 + IPFIX_FIELD_dot1qCustomerVlanId = 245 + IPFIX_FIELD_dot1qCustomerPriority = 246 + IPFIX_FIELD_metroEvcId = 247 + IPFIX_FIELD_metroEvcType = 248 + IPFIX_FIELD_pseudoWireId = 249 + IPFIX_FIELD_pseudoWireType = 250 + IPFIX_FIELD_pseudoWireControlWord = 251 + IPFIX_FIELD_ingressPhysicalInterface = 252 + IPFIX_FIELD_egressPhysicalInterface = 253 + IPFIX_FIELD_postDot1qVlanId = 254 + IPFIX_FIELD_postDot1qCustomerVlanId = 255 + IPFIX_FIELD_ethernetType = 256 + IPFIX_FIELD_postIpPrecedence = 257 + IPFIX_FIELD_collectionTimeMilliseconds = 258 + IPFIX_FIELD_exportSctpStreamId = 259 + IPFIX_FIELD_maxExportSeconds = 260 + IPFIX_FIELD_maxFlowEndSeconds = 261 + IPFIX_FIELD_messageMD5Checksum = 262 + IPFIX_FIELD_messageScope = 263 + IPFIX_FIELD_minExportSeconds = 264 + IPFIX_FIELD_minFlowStartSeconds = 265 + IPFIX_FIELD_opaqueOctets = 266 + IPFIX_FIELD_sessionScope = 267 + IPFIX_FIELD_maxFlowEndMicroseconds = 268 + IPFIX_FIELD_maxFlowEndMilliseconds = 269 + IPFIX_FIELD_maxFlowEndNanoseconds = 270 + IPFIX_FIELD_minFlowStartMicroseconds = 271 + IPFIX_FIELD_minFlowStartMilliseconds = 272 + IPFIX_FIELD_minFlowStartNanoseconds = 273 + IPFIX_FIELD_collectorCertificate = 274 + IPFIX_FIELD_exporterCertificate = 275 + IPFIX_FIELD_dataRecordsReliability = 276 + IPFIX_FIELD_observationPointType = 277 + IPFIX_FIELD_newConnectionDeltaCount = 278 + IPFIX_FIELD_connectionSumDurationSeconds = 279 + IPFIX_FIELD_connectionTransactionId = 280 + IPFIX_FIELD_postNATSourceIPv6Address = 281 + IPFIX_FIELD_postNATDestinationIPv6Address = 282 + IPFIX_FIELD_natPoolId = 283 + IPFIX_FIELD_natPoolName = 284 + IPFIX_FIELD_anonymizationFlags = 285 + IPFIX_FIELD_anonymizationTechnique = 286 + IPFIX_FIELD_informationElementIndex = 287 + IPFIX_FIELD_p2pTechnology = 288 + IPFIX_FIELD_tunnelTechnology = 289 + IPFIX_FIELD_encryptedTechnology = 290 + IPFIX_FIELD_basicList = 291 + IPFIX_FIELD_subTemplateList = 292 + IPFIX_FIELD_subTemplateMultiList = 293 + IPFIX_FIELD_bgpValidityState = 294 + IPFIX_FIELD_IPSecSPI = 295 + IPFIX_FIELD_greKey = 296 + IPFIX_FIELD_natType = 297 + IPFIX_FIELD_initiatorPackets = 298 + IPFIX_FIELD_responderPackets = 299 + IPFIX_FIELD_observationDomainName = 300 + IPFIX_FIELD_selectionSequenceId = 301 + IPFIX_FIELD_selectorId = 302 + IPFIX_FIELD_informationElementId = 303 + IPFIX_FIELD_selectorAlgorithm = 304 + IPFIX_FIELD_samplingPacketInterval = 305 + IPFIX_FIELD_samplingPacketSpace = 306 + IPFIX_FIELD_samplingTimeInterval = 307 + IPFIX_FIELD_samplingTimeSpace = 308 + IPFIX_FIELD_samplingSize = 309 + IPFIX_FIELD_samplingPopulation = 310 + IPFIX_FIELD_samplingProbability = 311 + IPFIX_FIELD_dataLinkFrameSize = 312 + IPFIX_FIELD_ipHeaderPacketSection = 313 + IPFIX_FIELD_ipPayloadPacketSection = 314 + IPFIX_FIELD_dataLinkFrameSection = 315 + IPFIX_FIELD_mplsLabelStackSection = 316 + IPFIX_FIELD_mplsPayloadPacketSection = 317 + IPFIX_FIELD_selectorIdTotalPktsObserved = 318 + IPFIX_FIELD_selectorIdTotalPktsSelected = 319 + IPFIX_FIELD_absoluteError = 320 + IPFIX_FIELD_relativeError = 321 + IPFIX_FIELD_observationTimeSeconds = 322 + IPFIX_FIELD_observationTimeMilliseconds = 323 + IPFIX_FIELD_observationTimeMicroseconds = 324 + IPFIX_FIELD_observationTimeNanoseconds = 325 + IPFIX_FIELD_digestHashValue = 326 + IPFIX_FIELD_hashIPPayloadOffset = 327 + IPFIX_FIELD_hashIPPayloadSize = 328 + IPFIX_FIELD_hashOutputRangeMin = 329 + IPFIX_FIELD_hashOutputRangeMax = 330 + IPFIX_FIELD_hashSelectedRangeMin = 331 + IPFIX_FIELD_hashSelectedRangeMax = 332 + IPFIX_FIELD_hashDigestOutput = 333 + IPFIX_FIELD_hashInitialiserValue = 334 + IPFIX_FIELD_selectorName = 335 + IPFIX_FIELD_upperCILimit = 336 + IPFIX_FIELD_lowerCILimit = 337 + IPFIX_FIELD_confidenceLevel = 338 + IPFIX_FIELD_informationElementDataType = 339 + IPFIX_FIELD_informationElementDescription = 340 + IPFIX_FIELD_informationElementName = 341 + IPFIX_FIELD_informationElementRangeBegin = 342 + IPFIX_FIELD_informationElementRangeEnd = 343 + IPFIX_FIELD_informationElementSemantics = 344 + IPFIX_FIELD_informationElementUnits = 345 + IPFIX_FIELD_privateEnterpriseNumber = 346 + IPFIX_FIELD_virtualStationInterfaceId = 347 + IPFIX_FIELD_virtualStationInterfaceName = 348 + IPFIX_FIELD_virtualStationUUID = 349 + IPFIX_FIELD_virtualStationName = 350 + IPFIX_FIELD_layer2SegmentId = 351 + IPFIX_FIELD_layer2OctetDeltaCount = 352 + IPFIX_FIELD_layer2OctetTotalCount = 353 + IPFIX_FIELD_ingressUnicastPacketTotalCount = 354 + IPFIX_FIELD_ingressMulticastPacketTotalCount = 355 + IPFIX_FIELD_ingressBroadcastPacketTotalCount = 356 + IPFIX_FIELD_egressUnicastPacketTotalCount = 357 + IPFIX_FIELD_egressBroadcastPacketTotalCount = 358 + IPFIX_FIELD_monitoringIntervalStartMilliSeconds = 359 + IPFIX_FIELD_monitoringIntervalEndMilliSeconds = 360 + IPFIX_FIELD_portRangeStart = 361 + IPFIX_FIELD_portRangeEnd = 362 + IPFIX_FIELD_portRangeStepSize = 363 + IPFIX_FIELD_portRangeNumPorts = 364 + IPFIX_FIELD_staMacAddress = 365 + IPFIX_FIELD_staIPv4Address = 366 + IPFIX_FIELD_wtpMacAddress = 367 + IPFIX_FIELD_ingressInterfaceType = 368 + IPFIX_FIELD_egressInterfaceType = 369 + IPFIX_FIELD_rtpSequenceNumber = 370 + IPFIX_FIELD_userName = 371 + IPFIX_FIELD_applicationCategoryName = 372 + IPFIX_FIELD_applicationSubCategoryName = 373 + IPFIX_FIELD_applicationGroupName = 374 + IPFIX_FIELD_originalFlowsPresent = 375 + IPFIX_FIELD_originalFlowsInitiated = 376 + IPFIX_FIELD_originalFlowsCompleted = 377 + IPFIX_FIELD_distinctCountOfSourceIPAddress = 378 + IPFIX_FIELD_distinctCountOfDestinationIPAddress = 379 + IPFIX_FIELD_distinctCountOfSourceIPv4Address = 380 + IPFIX_FIELD_distinctCountOfDestinationIPv4Address = 381 + IPFIX_FIELD_distinctCountOfSourceIPv6Address = 382 + IPFIX_FIELD_distinctCountOfDestinationIPv6Address = 383 + IPFIX_FIELD_valueDistributionMethod = 384 + IPFIX_FIELD_rfc3550JitterMilliseconds = 385 + IPFIX_FIELD_rfc3550JitterMicroseconds = 386 + IPFIX_FIELD_rfc3550JitterNanoseconds = 387 + IPFIX_FIELD_dot1qDEI = 388 + IPFIX_FIELD_dot1qCustomerDEI = 389 + IPFIX_FIELD_flowSelectorAlgorithm = 390 + IPFIX_FIELD_flowSelectedOctetDeltaCount = 391 + IPFIX_FIELD_flowSelectedPacketDeltaCount = 392 + IPFIX_FIELD_flowSelectedFlowDeltaCount = 393 + IPFIX_FIELD_selectorIDTotalFlowsObserved = 394 + IPFIX_FIELD_selectorIDTotalFlowsSelected = 395 + IPFIX_FIELD_samplingFlowInterval = 396 + IPFIX_FIELD_samplingFlowSpacing = 397 + IPFIX_FIELD_flowSamplingTimeInterval = 398 + IPFIX_FIELD_flowSamplingTimeSpacing = 399 + IPFIX_FIELD_hashFlowDomain = 400 + IPFIX_FIELD_transportOctetDeltaCount = 401 + IPFIX_FIELD_transportPacketDeltaCount = 402 + IPFIX_FIELD_originalExporterIPv4Address = 403 + IPFIX_FIELD_originalExporterIPv6Address = 404 + IPFIX_FIELD_originalObservationDomainId = 405 + IPFIX_FIELD_intermediateProcessId = 406 + IPFIX_FIELD_ignoredDataRecordTotalCount = 407 + IPFIX_FIELD_dataLinkFrameType = 408 + IPFIX_FIELD_sectionOffset = 409 + IPFIX_FIELD_sectionExportedOctets = 410 + IPFIX_FIELD_dot1qServiceInstanceTag = 411 + IPFIX_FIELD_dot1qServiceInstanceId = 412 + IPFIX_FIELD_dot1qServiceInstancePriority = 413 + IPFIX_FIELD_dot1qCustomerSourceMacAddress = 414 + IPFIX_FIELD_dot1qCustomerDestinationMacAddress = 415 + IPFIX_FIELD_postLayer2OctetDeltaCount = 417 + IPFIX_FIELD_postMCastLayer2OctetDeltaCount = 418 + IPFIX_FIELD_postLayer2OctetTotalCount = 420 + IPFIX_FIELD_postMCastLayer2OctetTotalCount = 421 + IPFIX_FIELD_minimumLayer2TotalLength = 422 + IPFIX_FIELD_maximumLayer2TotalLength = 423 + IPFIX_FIELD_droppedLayer2OctetDeltaCount = 424 + IPFIX_FIELD_droppedLayer2OctetTotalCount = 425 + IPFIX_FIELD_ignoredLayer2OctetTotalCount = 426 + IPFIX_FIELD_notSentLayer2OctetTotalCount = 427 + IPFIX_FIELD_layer2OctetDeltaSumOfSquares = 428 + IPFIX_FIELD_layer2OctetTotalSumOfSquares = 429 + IPFIX_FIELD_layer2FrameDeltaCount = 430 + IPFIX_FIELD_layer2FrameTotalCount = 431 + IPFIX_FIELD_pseudoWireDestinationIPv4Address = 432 + IPFIX_FIELD_ignoredLayer2FrameTotalCount = 433 + IPFIX_FIELD_mibObjectValueInteger = 434 + IPFIX_FIELD_mibObjectValueOctetString = 435 + IPFIX_FIELD_mibObjectValueOID = 436 + IPFIX_FIELD_mibObjectValueBits = 437 + IPFIX_FIELD_mibObjectValueIPAddress = 438 + IPFIX_FIELD_mibObjectValueCounter = 439 + IPFIX_FIELD_mibObjectValueGauge = 440 + IPFIX_FIELD_mibObjectValueTimeTicks = 441 + IPFIX_FIELD_mibObjectValueUnsigned = 442 + IPFIX_FIELD_mibObjectValueTable = 443 + IPFIX_FIELD_mibObjectValueRow = 444 + IPFIX_FIELD_mibObjectIdentifier = 445 + IPFIX_FIELD_mibSubIdentifier = 446 + IPFIX_FIELD_mibIndexIndicator = 447 + IPFIX_FIELD_mibCaptureTimeSemantics = 448 + IPFIX_FIELD_mibContextEngineID = 449 + IPFIX_FIELD_mibContextName = 450 + IPFIX_FIELD_mibObjectName = 451 + IPFIX_FIELD_mibObjectDescription = 452 + IPFIX_FIELD_mibObjectSyntax = 453 + IPFIX_FIELD_mibModuleName = 454 + IPFIX_FIELD_mobileIMSI = 455 + IPFIX_FIELD_mobileMSISDN = 456 + IPFIX_FIELD_httpStatusCode = 457 + IPFIX_FIELD_sourceTransportPortsLimit = 458 + IPFIX_FIELD_httpRequestMethod = 459 + IPFIX_FIELD_httpRequestHost = 460 + IPFIX_FIELD_httpRequestTarget = 461 + IPFIX_FIELD_httpMessageVersion = 462 + IPFIX_FIELD_natInstanceID = 463 + IPFIX_FIELD_internalAddressRealm = 464 + IPFIX_FIELD_externalAddressRealm = 465 + IPFIX_FIELD_natQuotaExceededEvent = 466 + IPFIX_FIELD_natThresholdEvent = 467 +) + +type IPFIXPacket struct { + Version uint16 + Length uint16 + ExportTime uint32 + SequenceNumber uint32 + ObservationDomainId uint32 + FlowSets []interface{} +} + +type IPFIXOptionsTemplateFlowSet struct { + FlowSetHeader + Records []IPFIXOptionsTemplateRecord +} + +type IPFIXOptionsTemplateRecord struct { + TemplateId uint16 + FieldCount uint16 + ScopeFieldCount uint16 + Options []Field + Scopes []Field +} + +func IPFIXTypeToString(typeId uint16) string { + + nameList := map[uint16]string{ + 0: "Reserved", + 1: "octetDeltaCount", + 2: "packetDeltaCount", + 3: "deltaFlowCount", + 4: "protocolIdentifier", + 5: "ipClassOfService", + 6: "tcpControlBits", + 7: "sourceTransportPort", + 8: "sourceIPv4Address", + 9: "sourceIPv4PrefixLength", + 10: "ingressInterface", + 11: "destinationTransportPort", + 12: "destinationIPv4Address", + 13: "destinationIPv4PrefixLength", + 14: "egressInterface", + 15: "ipNextHopIPv4Address", + 16: "bgpSourceAsNumber", + 17: "bgpDestinationAsNumber", + 18: "bgpNextHopIPv4Address", + 19: "postMCastPacketDeltaCount", + 20: "postMCastOctetDeltaCount", + 21: "flowEndSysUpTime", + 22: "flowStartSysUpTime", + 23: "postOctetDeltaCount", + 24: "postPacketDeltaCount", + 25: "minimumIpTotalLength", + 26: "maximumIpTotalLength", + 27: "sourceIPv6Address", + 28: "destinationIPv6Address", + 29: "sourceIPv6PrefixLength", + 30: "destinationIPv6PrefixLength", + 31: "flowLabelIPv6", + 32: "icmpTypeCodeIPv4", + 33: "igmpType", + 34: "samplingInterval", + 35: "samplingAlgorithm", + 36: "flowActiveTimeout", + 37: "flowIdleTimeout", + 38: "engineType", + 39: "engineId", + 40: "exportedOctetTotalCount", + 41: "exportedMessageTotalCount", + 42: "exportedFlowRecordTotalCount", + 43: "ipv4RouterSc", + 44: "sourceIPv4Prefix", + 45: "destinationIPv4Prefix", + 46: "mplsTopLabelType", + 47: "mplsTopLabelIPv4Address", + 48: "samplerId", + 49: "samplerMode", + 50: "samplerRandomInterval", + 51: "classId", + 52: "minimumTTL", + 53: "maximumTTL", + 54: "fragmentIdentification", + 55: "postIpClassOfService", + 56: "sourceMacAddress", + 57: "postDestinationMacAddress", + 58: "vlanId", + 59: "postVlanId", + 60: "ipVersion", + 61: "flowDirection", + 62: "ipNextHopIPv6Address", + 63: "bgpNextHopIPv6Address", + 64: "ipv6ExtensionHeaders", + 65: "Assigned for NetFlow v9 compatibility", + 66: "Assigned for NetFlow v9 compatibility", + 67: "Assigned for NetFlow v9 compatibility", + 68: "Assigned for NetFlow v9 compatibility", + 69: "Assigned for NetFlow v9 compatibility", + 70: "mplsTopLabelStackSection", + 71: "mplsLabelStackSection2", + 72: "mplsLabelStackSection3", + 73: "mplsLabelStackSection4", + 74: "mplsLabelStackSection5", + 75: "mplsLabelStackSection6", + 76: "mplsLabelStackSection7", + 77: "mplsLabelStackSection8", + 78: "mplsLabelStackSection9", + 79: "mplsLabelStackSection10", + 80: "destinationMacAddress", + 81: "postSourceMacAddress", + 82: "interfaceName", + 83: "interfaceDescription", + 84: "samplerName", + 85: "octetTotalCount", + 86: "packetTotalCount", + 87: "flagsAndSamplerId", + 88: "fragmentOffset", + 89: "forwardingStatus", + 90: "mplsVpnRouteDistinguisher", + 91: "mplsTopLabelPrefixLength", + 92: "srcTrafficIndex", + 93: "dstTrafficIndex", + 94: "applicationDescription", + 95: "applicationId", + 96: "applicationName", + 97: "Assigned for NetFlow v9 compatibility", + 98: "postIpDiffServCodePoint", + 99: "multicastReplicationFactor", + 100: "className", + 101: "classificationEngineId", + 102: "layer2packetSectionOffset", + 103: "layer2packetSectionSize", + 104: "layer2packetSectionData", + 128: "bgpNextAdjacentAsNumber", + 129: "bgpPrevAdjacentAsNumber", + 130: "exporterIPv4Address", + 131: "exporterIPv6Address", + 132: "droppedOctetDeltaCount", + 133: "droppedPacketDeltaCount", + 134: "droppedOctetTotalCount", + 135: "droppedPacketTotalCount", + 136: "flowEndReason", + 137: "commonPropertiesId", + 138: "observationPointId", + 139: "icmpTypeCodeIPv6", + 140: "mplsTopLabelIPv6Address", + 141: "lineCardId", + 142: "portId", + 143: "meteringProcessId", + 144: "exportingProcessId", + 145: "templateId", + 146: "wlanChannelId", + 147: "wlanSSID", + 148: "flowId", + 149: "observationDomainId", + 150: "flowStartSeconds", + 151: "flowEndSeconds", + 152: "flowStartMilliseconds", + 153: "flowEndMilliseconds", + 154: "flowStartMicroseconds", + 155: "flowEndMicroseconds", + 156: "flowStartNanoseconds", + 157: "flowEndNanoseconds", + 158: "flowStartDeltaMicroseconds", + 159: "flowEndDeltaMicroseconds", + 160: "systemInitTimeMilliseconds", + 161: "flowDurationMilliseconds", + 162: "flowDurationMicroseconds", + 163: "observedFlowTotalCount", + 164: "ignoredPacketTotalCount", + 165: "ignoredOctetTotalCount", + 166: "notSentFlowTotalCount", + 167: "notSentPacketTotalCount", + 168: "notSentOctetTotalCount", + 169: "destinationIPv6Prefix", + 170: "sourceIPv6Prefix", + 171: "postOctetTotalCount", + 172: "postPacketTotalCount", + 173: "flowKeyIndicator", + 174: "postMCastPacketTotalCount", + 175: "postMCastOctetTotalCount", + 176: "icmpTypeIPv4", + 177: "icmpCodeIPv4", + 178: "icmpTypeIPv6", + 179: "icmpCodeIPv6", + 180: "udpSourcePort", + 181: "udpDestinationPort", + 182: "tcpSourcePort", + 183: "tcpDestinationPort", + 184: "tcpSequenceNumber", + 185: "tcpAcknowledgementNumber", + 186: "tcpWindowSize", + 187: "tcpUrgentPointer", + 188: "tcpHeaderLength", + 189: "ipHeaderLength", + 190: "totalLengthIPv4", + 191: "payloadLengthIPv6", + 192: "ipTTL", + 193: "nextHeaderIPv6", + 194: "mplsPayloadLength", + 195: "ipDiffServCodePoint", + 196: "ipPrecedence", + 197: "fragmentFlags", + 198: "octetDeltaSumOfSquares", + 199: "octetTotalSumOfSquares", + 200: "mplsTopLabelTTL", + 201: "mplsLabelStackLength", + 202: "mplsLabelStackDepth", + 203: "mplsTopLabelExp", + 204: "ipPayloadLength", + 205: "udpMessageLength", + 206: "isMulticast", + 207: "ipv4IHL", + 208: "ipv4Options", + 209: "tcpOptions", + 210: "paddingOctets", + 211: "collectorIPv4Address", + 212: "collectorIPv6Address", + 213: "exportInterface", + 214: "exportProtocolVersion", + 215: "exportTransportProtocol", + 216: "collectorTransportPort", + 217: "exporterTransportPort", + 218: "tcpSynTotalCount", + 219: "tcpFinTotalCount", + 220: "tcpRstTotalCount", + 221: "tcpPshTotalCount", + 222: "tcpAckTotalCount", + 223: "tcpUrgTotalCount", + 224: "ipTotalLength", + 225: "postNATSourceIPv4Address", + 226: "postNATDestinationIPv4Address", + 227: "postNAPTSourceTransportPort", + 228: "postNAPTDestinationTransportPort", + 229: "natOriginatingAddressRealm", + 230: "natEvent", + 231: "initiatorOctets", + 232: "responderOctets", + 233: "firewallEvent", + 234: "ingressVRFID", + 235: "egressVRFID", + 236: "VRFname", + 237: "postMplsTopLabelExp", + 238: "tcpWindowScale", + 239: "biflowDirection", + 240: "ethernetHeaderLength", + 241: "ethernetPayloadLength", + 242: "ethernetTotalLength", + 243: "dot1qVlanId", + 244: "dot1qPriority", + 245: "dot1qCustomerVlanId", + 246: "dot1qCustomerPriority", + 247: "metroEvcId", + 248: "metroEvcType", + 249: "pseudoWireId", + 250: "pseudoWireType", + 251: "pseudoWireControlWord", + 252: "ingressPhysicalInterface", + 253: "egressPhysicalInterface", + 254: "postDot1qVlanId", + 255: "postDot1qCustomerVlanId", + 256: "ethernetType", + 257: "postIpPrecedence", + 258: "collectionTimeMilliseconds", + 259: "exportSctpStreamId", + 260: "maxExportSeconds", + 261: "maxFlowEndSeconds", + 262: "messageMD5Checksum", + 263: "messageScope", + 264: "minExportSeconds", + 265: "minFlowStartSeconds", + 266: "opaqueOctets", + 267: "sessionScope", + 268: "maxFlowEndMicroseconds", + 269: "maxFlowEndMilliseconds", + 270: "maxFlowEndNanoseconds", + 271: "minFlowStartMicroseconds", + 272: "minFlowStartMilliseconds", + 273: "minFlowStartNanoseconds", + 274: "collectorCertificate", + 275: "exporterCertificate", + 276: "dataRecordsReliability", + 277: "observationPointType", + 278: "newConnectionDeltaCount", + 279: "connectionSumDurationSeconds", + 280: "connectionTransactionId", + 281: "postNATSourceIPv6Address", + 282: "postNATDestinationIPv6Address", + 283: "natPoolId", + 284: "natPoolName", + 285: "anonymizationFlags", + 286: "anonymizationTechnique", + 287: "informationElementIndex", + 288: "p2pTechnology", + 289: "tunnelTechnology", + 290: "encryptedTechnology", + 291: "basicList", + 292: "subTemplateList", + 293: "subTemplateMultiList", + 294: "bgpValidityState", + 295: "IPSecSPI", + 296: "greKey", + 297: "natType", + 298: "initiatorPackets", + 299: "responderPackets", + 300: "observationDomainName", + 301: "selectionSequenceId", + 302: "selectorId", + 303: "informationElementId", + 304: "selectorAlgorithm", + 305: "samplingPacketInterval", + 306: "samplingPacketSpace", + 307: "samplingTimeInterval", + 308: "samplingTimeSpace", + 309: "samplingSize", + 310: "samplingPopulation", + 311: "samplingProbability", + 312: "dataLinkFrameSize", + 313: "ipHeaderPacketSection", + 314: "ipPayloadPacketSection", + 315: "dataLinkFrameSection", + 316: "mplsLabelStackSection", + 317: "mplsPayloadPacketSection", + 318: "selectorIdTotalPktsObserved", + 319: "selectorIdTotalPktsSelected", + 320: "absoluteError", + 321: "relativeError", + 322: "observationTimeSeconds", + 323: "observationTimeMilliseconds", + 324: "observationTimeMicroseconds", + 325: "observationTimeNanoseconds", + 326: "digestHashValue", + 327: "hashIPPayloadOffset", + 328: "hashIPPayloadSize", + 329: "hashOutputRangeMin", + 330: "hashOutputRangeMax", + 331: "hashSelectedRangeMin", + 332: "hashSelectedRangeMax", + 333: "hashDigestOutput", + 334: "hashInitialiserValue", + 335: "selectorName", + 336: "upperCILimit", + 337: "lowerCILimit", + 338: "confidenceLevel", + 339: "informationElementDataType", + 340: "informationElementDescription", + 341: "informationElementName", + 342: "informationElementRangeBegin", + 343: "informationElementRangeEnd", + 344: "informationElementSemantics", + 345: "informationElementUnits", + 346: "privateEnterpriseNumber", + 347: "virtualStationInterfaceId", + 348: "virtualStationInterfaceName", + 349: "virtualStationUUID", + 350: "virtualStationName", + 351: "layer2SegmentId", + 352: "layer2OctetDeltaCount", + 353: "layer2OctetTotalCount", + 354: "ingressUnicastPacketTotalCount", + 355: "ingressMulticastPacketTotalCount", + 356: "ingressBroadcastPacketTotalCount", + 357: "egressUnicastPacketTotalCount", + 358: "egressBroadcastPacketTotalCount", + 359: "monitoringIntervalStartMilliSeconds", + 360: "monitoringIntervalEndMilliSeconds", + 361: "portRangeStart", + 362: "portRangeEnd", + 363: "portRangeStepSize", + 364: "portRangeNumPorts", + 365: "staMacAddress", + 366: "staIPv4Address", + 367: "wtpMacAddress", + 368: "ingressInterfaceType", + 369: "egressInterfaceType", + 370: "rtpSequenceNumber", + 371: "userName", + 372: "applicationCategoryName", + 373: "applicationSubCategoryName", + 374: "applicationGroupName", + 375: "originalFlowsPresent", + 376: "originalFlowsInitiated", + 377: "originalFlowsCompleted", + 378: "distinctCountOfSourceIPAddress", + 379: "distinctCountOfDestinationIPAddress", + 380: "distinctCountOfSourceIPv4Address", + 381: "distinctCountOfDestinationIPv4Address", + 382: "distinctCountOfSourceIPv6Address", + 383: "distinctCountOfDestinationIPv6Address", + 384: "valueDistributionMethod", + 385: "rfc3550JitterMilliseconds", + 386: "rfc3550JitterMicroseconds", + 387: "rfc3550JitterNanoseconds", + 388: "dot1qDEI", + 389: "dot1qCustomerDEI", + 390: "flowSelectorAlgorithm", + 391: "flowSelectedOctetDeltaCount", + 392: "flowSelectedPacketDeltaCount", + 393: "flowSelectedFlowDeltaCount", + 394: "selectorIDTotalFlowsObserved", + 395: "selectorIDTotalFlowsSelected", + 396: "samplingFlowInterval", + 397: "samplingFlowSpacing", + 398: "flowSamplingTimeInterval", + 399: "flowSamplingTimeSpacing", + 400: "hashFlowDomain", + 401: "transportOctetDeltaCount", + 402: "transportPacketDeltaCount", + 403: "originalExporterIPv4Address", + 404: "originalExporterIPv6Address", + 405: "originalObservationDomainId", + 406: "intermediateProcessId", + 407: "ignoredDataRecordTotalCount", + 408: "dataLinkFrameType", + 409: "sectionOffset", + 410: "sectionExportedOctets", + 411: "dot1qServiceInstanceTag", + 412: "dot1qServiceInstanceId", + 413: "dot1qServiceInstancePriority", + 414: "dot1qCustomerSourceMacAddress", + 415: "dot1qCustomerDestinationMacAddress", + 416: "", + 417: "postLayer2OctetDeltaCount", + 418: "postMCastLayer2OctetDeltaCount", + 419: "", + 420: "postLayer2OctetTotalCount", + 421: "postMCastLayer2OctetTotalCount", + 422: "minimumLayer2TotalLength", + 423: "maximumLayer2TotalLength", + 424: "droppedLayer2OctetDeltaCount", + 425: "droppedLayer2OctetTotalCount", + 426: "ignoredLayer2OctetTotalCount", + 427: "notSentLayer2OctetTotalCount", + 428: "layer2OctetDeltaSumOfSquares", + 429: "layer2OctetTotalSumOfSquares", + 430: "layer2FrameDeltaCount", + 431: "layer2FrameTotalCount", + 432: "pseudoWireDestinationIPv4Address", + 433: "ignoredLayer2FrameTotalCount", + 434: "mibObjectValueInteger", + 435: "mibObjectValueOctetString", + 436: "mibObjectValueOID", + 437: "mibObjectValueBits", + 438: "mibObjectValueIPAddress", + 439: "mibObjectValueCounter", + 440: "mibObjectValueGauge", + 441: "mibObjectValueTimeTicks", + 442: "mibObjectValueUnsigned", + 443: "mibObjectValueTable", + 444: "mibObjectValueRow", + 445: "mibObjectIdentifier", + 446: "mibSubIdentifier", + 447: "mibIndexIndicator", + 448: "mibCaptureTimeSemantics", + 449: "mibContextEngineID", + 450: "mibContextName", + 451: "mibObjectName", + 452: "mibObjectDescription", + 453: "mibObjectSyntax", + 454: "mibModuleName", + 455: "mobileIMSI", + 456: "mobileMSISDN", + 457: "httpStatusCode", + 458: "sourceTransportPortsLimit", + 459: "httpRequestMethod", + 460: "httpRequestHost", + 461: "httpRequestTarget", + 462: "httpMessageVersion", + 463: "natInstanceID", + 464: "internalAddressRealm", + 465: "externalAddressRealm", + 466: "natQuotaExceededEvent", + 467: "natThresholdEvent", + } + + if typeId >= 105 && typeId <= 127 { + return "Assigned for NetFlow v9 compatibility" + } else if typeId >= 468 && typeId <= 32767 { + return "Unassigned" + } else { + return nameList[typeId] + } +} + +func (flowSet IPFIXOptionsTemplateFlowSet) String(TypeToString func(uint16) string) string { + str := fmt.Sprintf(" Id %v\n", flowSet.Id) + str += fmt.Sprintf(" Length: %v\n", flowSet.Length) + str += fmt.Sprintf(" Records (%v records):\n", len(flowSet.Records)) + + for j, record := range flowSet.Records { + str += fmt.Sprintf(" - Record %v:\n", j) + str += fmt.Sprintf(" TemplateId: %v\n", record.TemplateId) + str += fmt.Sprintf(" FieldCount: %v\n", record.FieldCount) + str += fmt.Sprintf(" ScopeFieldCount: %v\n", record.ScopeFieldCount) + + str += fmt.Sprintf(" Scopes (%v):\n", len(record.Scopes)) + + for k, field := range record.Scopes { + str += fmt.Sprintf(" - %v. %v (%v): %v\n", k, TypeToString(field.Type), field.Type, field.Length) + } + + str += fmt.Sprintf(" Options (%v):\n", len(record.Options)) + + for k, field := range record.Options { + str += fmt.Sprintf(" - %v. %v (%v): %v\n", k, TypeToString(field.Type), field.Type, field.Length) + } + + } + + return str +} + +func (p IPFIXPacket) String() string { + str := "Flow Packet\n" + str += "------------\n" + str += fmt.Sprintf(" Version: %v\n", p.Version) + str += fmt.Sprintf(" Length: %v\n", p.Length) + + exportTime := time.Unix(int64(p.ExportTime), 0) + str += fmt.Sprintf(" ExportTime: %v\n", exportTime.String()) + str += fmt.Sprintf(" SequenceNumber: %v\n", p.SequenceNumber) + str += fmt.Sprintf(" ObservationDomainId: %v\n", p.ObservationDomainId) + str += fmt.Sprintf(" FlowSets (%v):\n", len(p.FlowSets)) + + for i, flowSet := range p.FlowSets { + switch flowSet := flowSet.(type) { + case TemplateFlowSet: + str += fmt.Sprintf(" - TemplateFlowSet %v:\n", i) + str += flowSet.String(IPFIXTypeToString) + case IPFIXOptionsTemplateFlowSet: + str += fmt.Sprintf(" - OptionsTemplateFlowSet %v:\n", i) + str += flowSet.String(IPFIXTypeToString) + case DataFlowSet: + str += fmt.Sprintf(" - DataFlowSet %v:\n", i) + str += flowSet.String(IPFIXTypeToString) + case OptionsDataFlowSet: + str += fmt.Sprintf(" - OptionsDataFlowSet %v:\n", i) + str += flowSet.String(IPFIXTypeToString, IPFIXTypeToString) + default: + str += fmt.Sprintf(" - (unknown type) %v: %v\n", i, flowSet) + } + } + + return str +} diff --git a/third_party/goflow2/decoders/netflow/netflow.go b/third_party/goflow2/decoders/netflow/netflow.go new file mode 100644 index 000000000000..cd17bc81dd67 --- /dev/null +++ b/third_party/goflow2/decoders/netflow/netflow.go @@ -0,0 +1,534 @@ +package netflow + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "sync" + + "github.com/netsampler/goflow2/decoders/netflow/templates" + "github.com/netsampler/goflow2/decoders/utils" +) + +type FlowBaseTemplateSet map[uint16]map[uint32]map[uint16]interface{} + +type NetFlowTemplateSystem interface { + GetTemplate(version uint16, obsDomainId uint32, templateId uint16) (interface{}, error) + AddTemplate(version uint16, obsDomainId uint32, template interface{}) +} + +// Transition structure to ease the conversion with the new template systems +type TemplateWrapper struct { + Ctx context.Context + Key string + Inner templates.TemplateInterface +} + +func (w *TemplateWrapper) getTemplateId(template interface{}) (templateId uint16) { + switch templateIdConv := template.(type) { + case IPFIXOptionsTemplateRecord: + templateId = templateIdConv.TemplateId + case NFv9OptionsTemplateRecord: + templateId = templateIdConv.TemplateId + case TemplateRecord: + templateId = templateIdConv.TemplateId + } + return templateId +} + +func (w TemplateWrapper) GetTemplate(version uint16, obsDomainId uint32, templateId uint16) (interface{}, error) { + return w.Inner.GetTemplate(w.Ctx, &templates.TemplateKey{w.Key, version, obsDomainId, templateId}) +} + +func (w TemplateWrapper) AddTemplate(version uint16, obsDomainId uint32, template interface{}) { + w.Inner.AddTemplate(w.Ctx, &templates.TemplateKey{w.Key, version, obsDomainId, w.getTemplateId(template)}, template) +} + +func DecodeNFv9OptionsTemplateSet(payload *bytes.Buffer) ([]NFv9OptionsTemplateRecord, error) { + var records []NFv9OptionsTemplateRecord + var err error + for payload.Len() >= 4 { + optsTemplateRecord := NFv9OptionsTemplateRecord{} + err = utils.BinaryDecoder(payload, &optsTemplateRecord.TemplateId, &optsTemplateRecord.ScopeLength, &optsTemplateRecord.OptionLength) + if err != nil { + return records, err + } + + sizeScope := int(optsTemplateRecord.ScopeLength) / 4 + sizeOptions := int(optsTemplateRecord.OptionLength) / 4 + if sizeScope < 0 || sizeOptions < 0 { + return records, fmt.Errorf("Error decoding OptionsTemplateSet: negative length.") + } + + fields := make([]Field, sizeScope) + for i := 0; i < sizeScope; i++ { + field := Field{} + if err := DecodeField(payload, &field, false); err != nil { + return records, err + } + fields[i] = field + } + optsTemplateRecord.Scopes = fields + + fields = make([]Field, sizeOptions) + for i := 0; i < sizeOptions; i++ { + field := Field{} + if err := DecodeField(payload, &field, false); err != nil { + return records, err + } + fields[i] = field + } + optsTemplateRecord.Options = fields + + records = append(records, optsTemplateRecord) + } + + return records, err +} + +func DecodeField(payload *bytes.Buffer, field *Field, pen bool) error { + err := utils.BinaryDecoder(payload, &field.Type, &field.Length) + if pen && err == nil && field.Type&0x8000 != 0 { + field.PenProvided = true + err = utils.BinaryDecoder(payload, &field.Pen) + } + return err +} + +func DecodeIPFIXOptionsTemplateSet(payload *bytes.Buffer) ([]IPFIXOptionsTemplateRecord, error) { + var records []IPFIXOptionsTemplateRecord + var err error + for payload.Len() >= 4 { + optsTemplateRecord := IPFIXOptionsTemplateRecord{} + err = utils.BinaryDecoder(payload, &optsTemplateRecord.TemplateId, &optsTemplateRecord.FieldCount, &optsTemplateRecord.ScopeFieldCount) + if err != nil { + return records, err + } + + fields := make([]Field, int(optsTemplateRecord.ScopeFieldCount)) + for i := 0; i < int(optsTemplateRecord.ScopeFieldCount); i++ { + field := Field{} + if err := DecodeField(payload, &field, true); err != nil { + return records, err + } + fields[i] = field + } + optsTemplateRecord.Scopes = fields + + optionsSize := int(optsTemplateRecord.FieldCount) - int(optsTemplateRecord.ScopeFieldCount) + if optionsSize < 0 { + return records, fmt.Errorf("Error decoding OptionsTemplateSet: negative length.") + } + fields = make([]Field, optionsSize) + for i := 0; i < optionsSize; i++ { + field := Field{} + if err := DecodeField(payload, &field, true); err != nil { + return records, err + } + fields[i] = field + } + optsTemplateRecord.Options = fields + + records = append(records, optsTemplateRecord) + } + + return records, nil +} + +func DecodeTemplateSet(version uint16, payload *bytes.Buffer) ([]TemplateRecord, error) { + var records []TemplateRecord + var err error + for payload.Len() >= 4 { + templateRecord := TemplateRecord{} + err = utils.BinaryDecoder(payload, &templateRecord.TemplateId, &templateRecord.FieldCount) + if err != nil { + return records, err + } + + if int(templateRecord.FieldCount) < 0 { + return records, fmt.Errorf("Error decoding TemplateSet: zero count.") + } + + fields := make([]Field, int(templateRecord.FieldCount)) + for i := 0; i < int(templateRecord.FieldCount); i++ { + field := Field{} + err := utils.BinaryDecoder(payload, &field.Type, &field.Length) + if err == nil && version == 10 && field.Type&0x8000 != 0 { + field.PenProvided = true + field.Type = field.Type ^ 0x8000 + err = utils.BinaryDecoder(payload, &field.Pen) + } + if err != nil { + return records, err + } + fields[i] = field + } + templateRecord.Fields = fields + records = append(records, templateRecord) + } + + return records, nil +} + +func GetTemplateSize(version uint16, template []Field) int { + sum := 0 + for _, templateField := range template { + if templateField.Length == 0xffff { + continue + } + + sum += int(templateField.Length) + } + return sum +} + +func DecodeDataSetUsingFields(version uint16, payload *bytes.Buffer, listFields []Field) []DataField { + for payload.Len() >= GetTemplateSize(version, listFields) { + + dataFields := make([]DataField, len(listFields)) + for i, templateField := range listFields { + + finalLength := int(templateField.Length) + if templateField.Length == 0xffff { + var variableLen8 byte + var variableLen16 uint16 + err := utils.BinaryDecoder(payload, &variableLen8) + if err != nil { + return []DataField{} + } + if variableLen8 == 0xff { + err := utils.BinaryDecoder(payload, &variableLen16) + if err != nil { + return []DataField{} + } + finalLength = int(variableLen16) + } else { + finalLength = int(variableLen8) + } + } + + value := payload.Next(finalLength) + nfvalue := DataField{ + Type: templateField.Type, + PenProvided: templateField.PenProvided, + Pen: templateField.Pen, + Value: value, + } + dataFields[i] = nfvalue + } + return dataFields + } + return []DataField{} +} + +type ErrorTemplateNotFound struct { + version uint16 + obsDomainId uint32 + templateId uint16 + typeTemplate string +} + +func NewErrorTemplateNotFound(version uint16, obsDomainId uint32, templateId uint16, typeTemplate string) *ErrorTemplateNotFound { + return &ErrorTemplateNotFound{ + version: version, + obsDomainId: obsDomainId, + templateId: templateId, + typeTemplate: typeTemplate, + } +} + +func (e *ErrorTemplateNotFound) Error() string { + return fmt.Sprintf("No %v template %v found for and domain id %v", e.typeTemplate, e.templateId, e.obsDomainId) +} + +func DecodeOptionsDataSet(version uint16, payload *bytes.Buffer, listFieldsScopes, listFieldsOption []Field) ([]OptionsDataRecord, error) { + var records []OptionsDataRecord + + listFieldsScopesSize := GetTemplateSize(version, listFieldsScopes) + listFieldsOptionSize := GetTemplateSize(version, listFieldsOption) + + for payload.Len() >= listFieldsScopesSize+listFieldsOptionSize { + scopeValues := DecodeDataSetUsingFields(version, payload, listFieldsScopes) + optionValues := DecodeDataSetUsingFields(version, payload, listFieldsOption) + + record := OptionsDataRecord{ + ScopesValues: scopeValues, + OptionsValues: optionValues, + } + + records = append(records, record) + } + return records, nil +} + +func DecodeDataSet(version uint16, payload *bytes.Buffer, listFields []Field) ([]DataRecord, error) { + var records []DataRecord + + listFieldsSize := GetTemplateSize(version, listFields) + for payload.Len() >= listFieldsSize { + values := DecodeDataSetUsingFields(version, payload, listFields) + + record := DataRecord{ + Values: values, + } + + records = append(records, record) + } + return records, nil +} + +func (ts *BasicTemplateSystem) GetTemplates() map[uint16]map[uint32]map[uint16]interface{} { + ts.templateslock.RLock() + tmp := ts.templates + ts.templateslock.RUnlock() + return tmp +} + +func (ts *BasicTemplateSystem) AddTemplate(version uint16, obsDomainId uint32, template interface{}) { + ts.templateslock.Lock() + defer ts.templateslock.Unlock() + _, exists := ts.templates[version] + if exists != true { + ts.templates[version] = make(map[uint32]map[uint16]interface{}) + } + _, exists = ts.templates[version][obsDomainId] + if exists != true { + ts.templates[version][obsDomainId] = make(map[uint16]interface{}) + } + var templateId uint16 + switch templateIdConv := template.(type) { + case IPFIXOptionsTemplateRecord: + templateId = templateIdConv.TemplateId + case NFv9OptionsTemplateRecord: + templateId = templateIdConv.TemplateId + case TemplateRecord: + templateId = templateIdConv.TemplateId + } + ts.templates[version][obsDomainId][templateId] = template +} + +func (ts *BasicTemplateSystem) GetTemplate(version uint16, obsDomainId uint32, templateId uint16) (interface{}, error) { + ts.templateslock.RLock() + defer ts.templateslock.RUnlock() + templatesVersion, okver := ts.templates[version] + if okver { + templatesObsDom, okobs := templatesVersion[obsDomainId] + if okobs { + template, okid := templatesObsDom[templateId] + if okid { + return template, nil + } + } + } + return nil, NewErrorTemplateNotFound(version, obsDomainId, templateId, "info") +} + +type BasicTemplateSystem struct { + templates FlowBaseTemplateSet + templateslock *sync.RWMutex +} + +func CreateTemplateSystem() *BasicTemplateSystem { + ts := &BasicTemplateSystem{ + templates: make(FlowBaseTemplateSet), + templateslock: &sync.RWMutex{}, + } + return ts +} + +func DecodeMessage(payload *bytes.Buffer, templates NetFlowTemplateSystem) (interface{}, error) { + return DecodeMessageContext(context.Background(), payload, "", templates) +} + +func DecodeMessageContext(ctx context.Context, payload *bytes.Buffer, templateKey string, tpli NetFlowTemplateSystem) (interface{}, error) { + var size uint16 + packetNFv9 := NFv9Packet{} + packetIPFIX := IPFIXPacket{} + var returnItem interface{} + + var version uint16 + var obsDomainId uint32 + if err := binary.Read(payload, binary.BigEndian, &version); err != nil { + return nil, fmt.Errorf("Error decoding version: %v", err) + } + + if version == 9 { + err := utils.BinaryDecoder(payload, &packetNFv9.Count, &packetNFv9.SystemUptime, &packetNFv9.UnixSeconds, &packetNFv9.SequenceNumber, &packetNFv9.SourceId) + if err != nil { + return nil, fmt.Errorf("Error decoding NetFlow v9 header: %v", err) + } + size = packetNFv9.Count + packetNFv9.Version = version + returnItem = *(&packetNFv9) + obsDomainId = packetNFv9.SourceId + } else if version == 10 { + err := utils.BinaryDecoder(payload, &packetIPFIX.Length, &packetIPFIX.ExportTime, &packetIPFIX.SequenceNumber, &packetIPFIX.ObservationDomainId) + if err != nil { + return nil, fmt.Errorf("Error decoding IPFIX header: %v", err) + } + size = packetIPFIX.Length + packetIPFIX.Version = version + returnItem = *(&packetIPFIX) + obsDomainId = packetIPFIX.ObservationDomainId + } else { + return nil, fmt.Errorf("NetFlow/IPFIX version error: %d", version) + } + + for i := 0; ((i < int(size) && version == 9) || version == 10) && payload.Len() > 0; i++ { + fsheader := FlowSetHeader{} + if err := utils.BinaryDecoder(payload, &fsheader); err != nil { + return returnItem, fmt.Errorf("Error decoding FlowSet header: %v", err) + } + + nextrelpos := int(fsheader.Length) - binary.Size(fsheader) + if nextrelpos < 0 { + return returnItem, fmt.Errorf("Error decoding packet: non-terminated stream") + } + + var flowSet interface{} + + if fsheader.Id == 0 && version == 9 { + templateReader := bytes.NewBuffer(payload.Next(nextrelpos)) + records, err := DecodeTemplateSet(version, templateReader) + if err != nil { + return returnItem, fmt.Errorf("Error decoding FlowSet header: %v", err) + } + templatefs := TemplateFlowSet{ + FlowSetHeader: fsheader, + Records: records, + } + + flowSet = templatefs + + if tpli != nil { + for _, record := range records { + tpli.AddTemplate(version, obsDomainId, record) + //tpli.AddTemplate(ctx, templates.NewTemplateKey(templateKey, version, obsDomainId, record.TemplateId), record) + } + } + + } else if fsheader.Id == 1 && version == 9 { + templateReader := bytes.NewBuffer(payload.Next(nextrelpos)) + records, err := DecodeNFv9OptionsTemplateSet(templateReader) + if err != nil { + return returnItem, fmt.Errorf("Error decoding NetFlow OptionsTemplateSet: %v", err) + } + optsTemplatefs := NFv9OptionsTemplateFlowSet{ + FlowSetHeader: fsheader, + Records: records, + } + flowSet = optsTemplatefs + + if tpli != nil { + for _, record := range records { + tpli.AddTemplate(version, obsDomainId, record) + //tpli.AddTemplate(ctx, templates.NewTemplateKey(templateKey, version, obsDomainId, record.TemplateId), record) + } + } + + } else if fsheader.Id == 2 && version == 10 { + templateReader := bytes.NewBuffer(payload.Next(nextrelpos)) + records, err := DecodeTemplateSet(version, templateReader) + if err != nil { + return returnItem, fmt.Errorf("Error decoding IPFIX TemplateSet: %v", err) + } + templatefs := TemplateFlowSet{ + FlowSetHeader: fsheader, + Records: records, + } + flowSet = templatefs + + if tpli != nil { + for _, record := range records { + tpli.AddTemplate(version, obsDomainId, record) + //tpli.AddTemplate(ctx, templates.NewTemplateKey(templateKey, version, obsDomainId, record.TemplateId), record) + } + } + + } else if fsheader.Id == 3 && version == 10 { + templateReader := bytes.NewBuffer(payload.Next(nextrelpos)) + records, err := DecodeIPFIXOptionsTemplateSet(templateReader) + if err != nil { + return returnItem, fmt.Errorf("Error decoding IPFIX OptionsTemplateSet: %v", err) + } + optsTemplatefs := IPFIXOptionsTemplateFlowSet{ + FlowSetHeader: fsheader, + Records: records, + } + flowSet = optsTemplatefs + + if tpli != nil { + for _, record := range records { + tpli.AddTemplate(version, obsDomainId, record) + //tpli.AddTemplate(ctx, templates.NewTemplateKey(templateKey, version, obsDomainId, record.TemplateId), record) + } + } + + } else if fsheader.Id >= 256 { + dataReader := bytes.NewBuffer(payload.Next(nextrelpos)) + + if tpli == nil { + continue + } + + template, err := tpli.GetTemplate(version, obsDomainId, fsheader.Id) + //template, err := tpli.GetTemplate(ctx, templates.NewTemplateKey(templateKey, version, obsDomainId, fsheader.Id)) + + if err == nil { + switch templatec := template.(type) { + case TemplateRecord: + records, err := DecodeDataSet(version, dataReader, templatec.Fields) + if err != nil { + return returnItem, fmt.Errorf("Error decoding DataSet: %v", err) + } + datafs := DataFlowSet{ + FlowSetHeader: fsheader, + Records: records, + } + flowSet = datafs + case IPFIXOptionsTemplateRecord: + records, err := DecodeOptionsDataSet(version, dataReader, templatec.Scopes, templatec.Options) + if err != nil { + return returnItem, fmt.Errorf("Error decoding DataSet: %v", err) + } + + datafs := OptionsDataFlowSet{ + FlowSetHeader: fsheader, + Records: records, + } + flowSet = datafs + case NFv9OptionsTemplateRecord: + records, err := DecodeOptionsDataSet(version, dataReader, templatec.Scopes, templatec.Options) + if err != nil { + return returnItem, fmt.Errorf("Error decoding OptionDataSet: %v", err) + } + + datafs := OptionsDataFlowSet{ + FlowSetHeader: fsheader, + Records: records, + } + flowSet = datafs + } + } else { + return returnItem, err + } + } else { + return returnItem, fmt.Errorf("Error with ID %d", fsheader.Id) + } + + if version == 9 && flowSet != nil { + packetNFv9.FlowSets = append(packetNFv9.FlowSets, flowSet) + } else if version == 10 && flowSet != nil { + packetIPFIX.FlowSets = append(packetIPFIX.FlowSets, flowSet) + } + } + + if version == 9 { + return packetNFv9, nil + } else if version == 10 { + return packetIPFIX, nil + } else { + return returnItem, fmt.Errorf("Unknown version: %d", version) + } +} diff --git a/third_party/goflow2/decoders/netflow/netflow_test.go b/third_party/goflow2/decoders/netflow/netflow_test.go new file mode 100644 index 000000000000..5097c7d7b684 --- /dev/null +++ b/third_party/goflow2/decoders/netflow/netflow_test.go @@ -0,0 +1,416 @@ +package netflow + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDecodeNetFlowV9(t *testing.T) { + templates := CreateTemplateSystem() + + // Decode a template + template := []byte{ + 0x00, 0x09, 0x00, 0x01, 0xb3, 0xbf, 0xf6, 0x83, 0x61, 0x8a, 0xa3, 0xa8, 0x32, 0x01, 0xee, 0x98, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x64, 0x01, 0x04, 0x00, 0x17, 0x00, 0x02, 0x00, 0x04, + 0x00, 0x01, 0x00, 0x04, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x04, + 0x00, 0x0e, 0x00, 0x04, 0x00, 0x15, 0x00, 0x04, 0x00, 0x16, 0x00, 0x04, 0x00, 0x07, 0x00, 0x02, + 0x00, 0x0b, 0x00, 0x02, 0x00, 0x10, 0x00, 0x04, 0x00, 0x11, 0x00, 0x04, 0x00, 0x12, 0x00, 0x04, + 0x00, 0x09, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x01, + 0x00, 0x05, 0x00, 0x01, 0x00, 0x3d, 0x00, 0x01, 0x00, 0x59, 0x00, 0x01, 0x00, 0x30, 0x00, 0x02, + 0x00, 0xea, 0x00, 0x04, 0x00, 0xeb, 0x00, 0x04, + } + buf := bytes.NewBuffer(template) + dec, err := DecodeMessage(buf, templates) + assert.Nil(t, err) + assert.NotNil(t, dec) + decNfv9 := dec.(NFv9Packet) + assert.Equal(t, + NFv9Packet{ + Version: 9, + Count: 1, + SystemUptime: 0xb3bff683, + UnixSeconds: 0x618aa3a8, + SequenceNumber: 838987416, + SourceId: 256, + FlowSets: []interface{}{ + TemplateFlowSet{ + FlowSetHeader: FlowSetHeader{Id: 0x0, Length: 100}, + Records: []TemplateRecord{ + { + TemplateId: 260, + FieldCount: 23, + Fields: []Field{ + {PenProvided: false, Type: 0x2, Length: 0x4}, + {PenProvided: false, Type: 0x1, Length: 0x4}, + {PenProvided: false, Type: 0x8, Length: 0x4}, + {PenProvided: false, Type: 0xc, Length: 0x4}, + {PenProvided: false, Type: 0xa, Length: 0x4}, + {PenProvided: false, Type: 0xe, Length: 0x4}, + {PenProvided: false, Type: 0x15, Length: 0x4}, + {PenProvided: false, Type: 0x16, Length: 0x4}, + {PenProvided: false, Type: 0x7, Length: 0x2}, + {PenProvided: false, Type: 0xb, Length: 0x2}, + {PenProvided: false, Type: 0x10, Length: 0x4}, + {PenProvided: false, Type: 0x11, Length: 0x4}, + {PenProvided: false, Type: 0x12, Length: 0x4}, + {PenProvided: false, Type: 0x9, Length: 0x1}, + {PenProvided: false, Type: 0xd, Length: 0x1}, + {PenProvided: false, Type: 0x4, Length: 0x1}, + {PenProvided: false, Type: 0x6, Length: 0x1}, + {PenProvided: false, Type: 0x5, Length: 0x1}, + {PenProvided: false, Type: 0x3d, Length: 0x1}, + {PenProvided: false, Type: 0x59, Length: 0x1}, + {PenProvided: false, Type: 0x30, Length: 0x2}, + {PenProvided: false, Type: 0xea, Length: 0x4}, + {PenProvided: false, Type: 0xeb, Length: 0x4}, + }, + }, + }, + }, + }, + }, decNfv9) + assert.Equal(t, + `Flow Packet +------------ + Version: 9 + Count: 1 + SystemUptime: 3015702147 + UnixSeconds: 2021-11-09 16:36:56 +0000 UTC + SequenceNumber: 838987416 + SourceId: 256 + FlowSets (1): + - TemplateFlowSet 0: + Id 0 + Length: 100 + Records (1 records): + - 0. Record: + TemplateId: 260 + FieldCount: 23 + Fields (23): + - 0. IN_PKTS (2/false): 4 + - 1. IN_BYTES (1/false): 4 + - 2. IPV4_SRC_ADDR (8/false): 4 + - 3. IPV4_DST_ADDR (12/false): 4 + - 4. INPUT_SNMP (10/false): 4 + - 5. OUTPUT_SNMP (14/false): 4 + - 6. LAST_SWITCHED (21/false): 4 + - 7. FIRST_SWITCHED (22/false): 4 + - 8. L4_SRC_PORT (7/false): 2 + - 9. L4_DST_PORT (11/false): 2 + - 10. SRC_AS (16/false): 4 + - 11. DST_AS (17/false): 4 + - 12. BGP_IPV4_NEXT_HOP (18/false): 4 + - 13. SRC_MASK (9/false): 1 + - 14. DST_MASK (13/false): 1 + - 15. PROTOCOL (4/false): 1 + - 16. TCP_FLAGS (6/false): 1 + - 17. SRC_TOS (5/false): 1 + - 18. DIRECTION (61/false): 1 + - 19. FORWARDING STATUS (89/false): 1 + - 20. FLOW_SAMPLER_ID (48/false): 2 + - 21. Unassigned (234/false): 4 + - 22. Unassigned (235/false): 4 +`, + decNfv9.String()) + + // Decode some data using the above template + data := []byte{ + 0x00, 0x09, 0x00, 0x15, 0xb3, 0xbf, 0xf6, 0x83, 0x61, 0x8a, 0xa3, 0xa8, 0x32, 0x01, 0xee, 0x9c, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x04, 0x05, 0x5c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xdc, + 0xc6, 0x26, 0x78, 0xde, 0x58, 0x79, 0xd9, 0xd0, 0x00, 0x00, 0x01, 0x62, 0x00, 0x00, 0x01, 0x30, + 0xb3, 0xbf, 0xe6, 0xf9, 0xb3, 0xbf, 0xe6, 0xf9, 0x01, 0xbb, 0x3b, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x18, 0x0e, 0x06, 0x10, 0x00, 0x00, 0x40, 0x00, + 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x0b, + 0xb8, 0x6d, 0x47, 0xa2, 0xc4, 0x5b, 0xad, 0x61, 0xe0, 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, 0x01, + 0x30, 0xb3, 0xbf, 0xe8, 0x1c, 0xb3, 0xbf, 0xe6, 0xf9, 0x01, 0xbb, 0x7b, 0x99, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x18, 0x0d, 0x06, 0x10, 0x48, 0x00, 0x40, + 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x05, 0xdc, 0xc6, 0x26, 0x78, 0xd3, 0x5b, 0xa5, 0xd2, 0xee, 0x00, 0x00, 0x01, 0x62, 0x00, 0x00, + 0x01, 0x75, 0xb3, 0xbf, 0xe6, 0xfc, 0xb3, 0xbf, 0xe6, 0xfc, 0x00, 0x50, 0x8f, 0xb8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x95, 0xae, 0x3b, 0x18, 0x0e, 0x06, 0x10, 0x00, 0x00, + 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x05, 0xdc, 0x5f, 0x64, 0x56, 0x42, 0x5b, 0xa9, 0x1a, 0xbe, 0x00, 0x00, 0x01, 0x61, 0x00, + 0x00, 0x01, 0x31, 0xb3, 0xbf, 0xe6, 0xfc, 0xb3, 0xbf, 0xe6, 0xfc, 0x00, 0x50, 0xbf, 0xc3, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x18, 0x0e, 0x06, 0x10, 0x28, + 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x05, 0xdc, 0xc6, 0x26, 0x78, 0xc6, 0x5b, 0xab, 0x33, 0x34, 0x00, 0x00, 0x01, 0x62, + 0x00, 0x00, 0x01, 0x31, 0xb3, 0xbf, 0xe6, 0xfc, 0xb3, 0xbf, 0xe6, 0xfc, 0x01, 0xbb, 0xf9, 0xd5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x18, 0x0e, 0x06, 0x10, + 0x00, 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x05, 0xdc, 0xc6, 0x26, 0x78, 0x83, 0x4e, 0xf2, 0x8c, 0x81, 0x00, 0x00, 0x01, + 0x62, 0x00, 0x00, 0x01, 0x31, 0xb3, 0xbf, 0xe6, 0xfe, 0xb3, 0xbf, 0xe6, 0xfe, 0x01, 0xbb, 0xb3, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x18, 0x18, 0x06, + 0x10, 0x00, 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x05, 0xdc, 0xc6, 0x26, 0x78, 0xb8, 0x5b, 0xaa, 0xab, 0x01, 0x00, 0x00, + 0x01, 0x62, 0x00, 0x00, 0x01, 0x31, 0xb3, 0xbf, 0xe6, 0xff, 0xb3, 0xbf, 0xe6, 0xff, 0x01, 0xbb, + 0xe5, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x18, 0x0e, + 0x06, 0x10, 0x00, 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xdc, 0xc6, 0x26, 0x78, 0xc5, 0x5b, 0xa5, 0x22, 0x65, 0x00, + 0x00, 0x01, 0x62, 0x00, 0x00, 0x01, 0x69, 0xb3, 0xbf, 0xe7, 0x00, 0xb3, 0xbf, 0xe7, 0x00, 0x01, + 0xbb, 0x3c, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x95, 0xae, 0x31, 0x18, + 0x0e, 0x06, 0x10, 0x00, 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xdc, 0x8f, 0xf4, 0x38, 0x1a, 0x5b, 0xa4, 0xc7, 0x3a, + 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, 0x01, 0x75, 0xb3, 0xbf, 0xe7, 0x01, 0xb3, 0xbf, 0xe7, 0x01, + 0x01, 0xbb, 0x49, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x95, 0xae, 0x3b, + 0x17, 0x0e, 0x06, 0x10, 0x28, 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xb0, 0xc7, 0xe8, 0xb2, 0x49, 0x5b, 0xaf, 0x83, + 0x0c, 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, 0x01, 0x30, 0xb3, 0xbf, 0xe7, 0x02, 0xb3, 0xbf, 0xe7, + 0x02, 0x01, 0xbb, 0x96, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, + 0x00, 0x16, 0x0d, 0x06, 0x10, 0x28, 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xdc, 0xc6, 0x26, 0x78, 0xd8, 0x58, 0x7c, + 0x1f, 0x58, 0x00, 0x00, 0x01, 0x62, 0x00, 0x00, 0x01, 0x30, 0xb3, 0xbf, 0xe7, 0x02, 0xb3, 0xbf, + 0xe7, 0x02, 0x01, 0xbb, 0x16, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, + 0x00, 0x00, 0x18, 0x0e, 0x06, 0x10, 0x00, 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xdc, 0xc6, 0x26, 0x78, 0xdc, 0x5b, + 0xaf, 0x13, 0x88, 0x00, 0x00, 0x01, 0x62, 0x00, 0x00, 0x01, 0x30, 0xb3, 0xbf, 0xe7, 0x02, 0xb3, + 0xbf, 0xe7, 0x02, 0x01, 0xbb, 0x79, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, + 0xdf, 0x00, 0x00, 0x18, 0x0d, 0x06, 0x10, 0x00, 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xdc, 0xcd, 0xea, 0xaf, 0x66, + 0x5b, 0xa1, 0xfc, 0x11, 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, 0x01, 0x69, 0xb3, 0xbf, 0xe7, 0x03, + 0xb3, 0xbf, 0xe7, 0x03, 0x01, 0xbb, 0x79, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc2, 0x95, 0xae, 0x31, 0x18, 0x0e, 0x06, 0x10, 0x28, 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, + 0x02, 0x60, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x0b, 0x20, 0x8a, 0xc7, 0x10, + 0xcc, 0x5b, 0xa6, 0xb0, 0x14, 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, 0x01, 0x69, 0xb3, 0xbf, 0xe7, + 0x04, 0xb3, 0xbf, 0xe4, 0xba, 0x04, 0xaa, 0x22, 0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc2, 0x95, 0xae, 0x31, 0x18, 0x0e, 0x11, 0x00, 0x28, 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, + 0x00, 0x02, 0x60, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xd8, 0xb9, 0x21, + 0xdc, 0x64, 0x5b, 0xac, 0x7f, 0x22, 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, 0x01, 0x30, 0xb3, 0xbf, + 0xe7, 0x04, 0xb3, 0xbf, 0xe7, 0x04, 0x01, 0xbb, 0x1b, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x16, 0x0d, 0x06, 0x18, 0x28, 0x00, 0x40, 0x00, 0x01, 0x60, + 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xdc, 0xb9, + 0x15, 0x3d, 0x5c, 0x4e, 0xe8, 0x7a, 0x02, 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, 0x01, 0x30, 0xb3, + 0xbf, 0xe7, 0x05, 0xb3, 0xbf, 0xe7, 0x05, 0x88, 0xb3, 0xd0, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x16, 0x16, 0x06, 0x10, 0x28, 0x00, 0x40, 0x00, 0x01, + 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, + 0xd4, 0x20, 0xfe, 0x7b, 0x5b, 0xab, 0x61, 0x86, 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, 0x01, 0x31, + 0xb3, 0xbf, 0xe7, 0x06, 0xb3, 0xbf, 0xe7, 0x06, 0xd3, 0xc9, 0xc3, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x13, 0x0e, 0x06, 0x10, 0x28, 0x00, 0x40, 0x00, + 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, + 0x90, 0xc6, 0x26, 0x78, 0xc3, 0x25, 0xa5, 0xad, 0xb8, 0x00, 0x00, 0x01, 0x62, 0x00, 0x00, 0x01, + 0x31, 0xb3, 0xbf, 0xe7, 0x08, 0xb3, 0xbf, 0xe7, 0x08, 0x01, 0xbb, 0x58, 0x64, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x18, 0x12, 0x06, 0x10, 0x00, 0x00, 0x40, + 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x05, 0xdc, 0x8f, 0xf4, 0x39, 0x32, 0x4e, 0xe6, 0x08, 0xb1, 0x00, 0x00, 0x01, 0x61, 0x00, 0x00, + 0x01, 0x30, 0xb3, 0xbf, 0xe7, 0x08, 0xb3, 0xbf, 0xe7, 0x08, 0x01, 0xbb, 0xb2, 0x9a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x17, 0x17, 0x06, 0x10, 0x28, 0x00, + 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x05, 0x3c, 0xc6, 0x26, 0x78, 0xb6, 0x25, 0xa4, 0xf7, 0xb2, 0x00, 0x00, 0x01, 0x62, 0x00, + 0x00, 0x01, 0x30, 0xb3, 0xbf, 0xe7, 0x09, 0xb3, 0xbf, 0xe7, 0x09, 0x01, 0xbb, 0xdd, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x18, 0x12, 0x06, 0x10, 0x00, + 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x05, 0x64, 0xcd, 0xb9, 0xd8, 0x12, 0x52, 0x8e, 0x0d, 0x65, 0x00, 0x00, 0x01, 0x61, + 0x00, 0x00, 0x01, 0x30, 0xb3, 0xbf, 0xe7, 0x09, 0xb3, 0xbf, 0xe7, 0x09, 0x00, 0x50, 0x94, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xdf, 0x00, 0x00, 0x18, 0x14, 0x06, 0x10, + 0x28, 0x00, 0x40, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } + buf = bytes.NewBuffer(data[:89]) // truncate: we don't want to test for everything + dec, err = DecodeMessage(buf, templates) + assert.Nil(t, err) + assert.NotNil(t, dec) + decNfv9 = dec.(NFv9Packet) + assert.Equal(t, + NFv9Packet{ + Version: 9, + Count: 21, + SystemUptime: 3015702147, + UnixSeconds: 1636475816, + SequenceNumber: 838987420, + SourceId: 256, + FlowSets: []interface{}{ + DataFlowSet{ + FlowSetHeader: FlowSetHeader{ + Id: 260, + Length: 1372, + }, + Records: []DataRecord{ + // Truncated! + { + Values: []DataField{ + { + PenProvided: false, + Type: 2, + Pen: 0, + Value: []uint8{0x00, 0x00, 0x00, 0x01}, + }, + { + PenProvided: false, + Type: 1, + Pen: 0, + Value: []uint8{0x00, 0x00, 0x05, 0xdc}, + }, + { + PenProvided: false, + Type: 8, + Pen: 0, + Value: []uint8{0xc6, 0x26, 0x78, 0xde}, + }, + { + PenProvided: false, + Type: 12, + Pen: 0, + Value: []uint8{0x58, 0x79, 0xd9, 0xd0}, + }, + { + PenProvided: false, + Type: 10, + Pen: 0, + Value: []uint8{0x00, 0x00, 0x01, 0x62}, + }, + { + PenProvided: false, + Type: 14, + Pen: 0, + Value: []uint8{0x00, 0x00, 0x01, 0x30}, + }, + { + PenProvided: false, + Type: 21, + Pen: 0, + Value: []uint8{0xb3, 0xbf, 0xe6, 0xf9}, + }, + { + PenProvided: false, + Type: 22, + Pen: 0, + Value: []uint8{0xb3, 0xbf, 0xe6, 0xf9}, + }, + { + PenProvided: false, + Type: 7, + Pen: 0, + Value: []uint8{0x01, 0xbb}, + }, + { + PenProvided: false, + Type: 11, + Pen: 0, + Value: []uint8{0x3b, 0x50}, + }, + { + PenProvided: false, + Type: 16, + Pen: 0, + Value: []uint8{0x00, 0x00, 0x00, 0x00}, + }, + { + PenProvided: false, + Type: 17, + Pen: 0, + Value: []uint8{0x00, 0x00, 0x00, 0x00}, + }, + { + PenProvided: false, + Type: 18, + Pen: 0, + Value: []uint8{0xfc, 0xdf, 0x00, 0x00}, + }, + { + PenProvided: false, + Type: 9, + Pen: 0, + Value: []uint8{0x18}, + }, + { + PenProvided: false, + Type: 13, + Pen: 0, + Value: []uint8{0x0e}, + }, + { + PenProvided: false, + Type: 4, + Pen: 0, + Value: []uint8{0x06}, + }, + { + PenProvided: false, + Type: 6, + Pen: 0, + Value: []uint8{0x10}, + }, + { + PenProvided: false, + Type: 5, + Pen: 0, + Value: []uint8{0x00}, + }, + { + PenProvided: false, + Type: 61, + Pen: 0, + Value: []uint8{0x00}, + }, + { + PenProvided: false, + Type: 89, + Pen: 0, + Value: []uint8{0x40}, + }, + { + PenProvided: false, + Type: 48, + Pen: 0, + Value: []uint8{0x00, 0x01}, + }, + { + PenProvided: false, + Type: 234, + Pen: 0, + Value: []uint8{0x60, 0x00, 0x00, 0x02}, + }, + { + PenProvided: false, + Type: 235, + Pen: 0, + Value: []uint8{0x60, 0x00, 0x00, 0x00}, + }, + }, + }, + }, + }, + }, + }, decNfv9) + assert.Equal(t, + `Flow Packet +------------ + Version: 9 + Count: 21 + SystemUptime: 3015702147 + UnixSeconds: 2021-11-09 16:36:56 +0000 UTC + SequenceNumber: 838987420 + SourceId: 256 + FlowSets (1): + - DataFlowSet 0: + Id 260 + Length: 1372 + Records (1 records): + - Record 0: + Values (23): + - 0. IN_PKTS (2): [0 0 0 1] + - 1. IN_BYTES (1): [0 0 5 220] + - 2. IPV4_SRC_ADDR (8): [198 38 120 222] + - 3. IPV4_DST_ADDR (12): [88 121 217 208] + - 4. INPUT_SNMP (10): [0 0 1 98] + - 5. OUTPUT_SNMP (14): [0 0 1 48] + - 6. LAST_SWITCHED (21): [179 191 230 249] + - 7. FIRST_SWITCHED (22): [179 191 230 249] + - 8. L4_SRC_PORT (7): [1 187] + - 9. L4_DST_PORT (11): [59 80] + - 10. SRC_AS (16): [0 0 0 0] + - 11. DST_AS (17): [0 0 0 0] + - 12. BGP_IPV4_NEXT_HOP (18): [252 223 0 0] + - 13. SRC_MASK (9): [24] + - 14. DST_MASK (13): [14] + - 15. PROTOCOL (4): [6] + - 16. TCP_FLAGS (6): [16] + - 17. SRC_TOS (5): [0] + - 18. DIRECTION (61): [0] + - 19. FORWARDING STATUS (89): [64] + - 20. FLOW_SAMPLER_ID (48): [0 1] + - 21. Unassigned (234): [96 0 0 2] + - 22. Unassigned (235): [96 0 0 0] +`, + decNfv9.String()) +} diff --git a/third_party/goflow2/decoders/netflow/nfv9.go b/third_party/goflow2/decoders/netflow/nfv9.go new file mode 100644 index 000000000000..64fe227d8801 --- /dev/null +++ b/third_party/goflow2/decoders/netflow/nfv9.go @@ -0,0 +1,317 @@ +package netflow + +import ( + "fmt" + "time" +) + +const ( + NFV9_FIELD_IN_BYTES = 1 + NFV9_FIELD_IN_PKTS = 2 + NFV9_FIELD_FLOWS = 3 + NFV9_FIELD_PROTOCOL = 4 + NFV9_FIELD_SRC_TOS = 5 + NFV9_FIELD_TCP_FLAGS = 6 + NFV9_FIELD_L4_SRC_PORT = 7 + NFV9_FIELD_IPV4_SRC_ADDR = 8 + NFV9_FIELD_SRC_MASK = 9 + NFV9_FIELD_INPUT_SNMP = 10 + NFV9_FIELD_L4_DST_PORT = 11 + NFV9_FIELD_IPV4_DST_ADDR = 12 + NFV9_FIELD_DST_MASK = 13 + NFV9_FIELD_OUTPUT_SNMP = 14 + NFV9_FIELD_IPV4_NEXT_HOP = 15 + NFV9_FIELD_SRC_AS = 16 + NFV9_FIELD_DST_AS = 17 + NFV9_FIELD_BGP_IPV4_NEXT_HOP = 18 + NFV9_FIELD_MUL_DST_PKTS = 19 + NFV9_FIELD_MUL_DST_BYTES = 20 + NFV9_FIELD_LAST_SWITCHED = 21 + NFV9_FIELD_FIRST_SWITCHED = 22 + NFV9_FIELD_OUT_BYTES = 23 + NFV9_FIELD_OUT_PKTS = 24 + NFV9_FIELD_MIN_PKT_LNGTH = 25 + NFV9_FIELD_MAX_PKT_LNGTH = 26 + NFV9_FIELD_IPV6_SRC_ADDR = 27 + NFV9_FIELD_IPV6_DST_ADDR = 28 + NFV9_FIELD_IPV6_SRC_MASK = 29 + NFV9_FIELD_IPV6_DST_MASK = 30 + NFV9_FIELD_IPV6_FLOW_LABEL = 31 + NFV9_FIELD_ICMP_TYPE = 32 + NFV9_FIELD_MUL_IGMP_TYPE = 33 + NFV9_FIELD_SAMPLING_INTERVAL = 34 + NFV9_FIELD_SAMPLING_ALGORITHM = 35 + NFV9_FIELD_FLOW_ACTIVE_TIMEOUT = 36 + NFV9_FIELD_FLOW_INACTIVE_TIMEOUT = 37 + NFV9_FIELD_ENGINE_TYPE = 38 + NFV9_FIELD_ENGINE_ID = 39 + NFV9_FIELD_TOTAL_BYTES_EXP = 40 + NFV9_FIELD_TOTAL_PKTS_EXP = 41 + NFV9_FIELD_TOTAL_FLOWS_EXP = 42 + NFV9_FIELD_IPV4_SRC_PREFIX = 44 + NFV9_FIELD_IPV4_DST_PREFIX = 45 + NFV9_FIELD_MPLS_TOP_LABEL_TYPE = 46 + NFV9_FIELD_MPLS_TOP_LABEL_IP_ADDR = 47 + NFV9_FIELD_FLOW_SAMPLER_ID = 48 + NFV9_FIELD_FLOW_SAMPLER_MODE = 49 + NFV9_FIELD_FLOW_SAMPLER_RANDOM_INTERVAL = 50 + NFV9_FIELD_MIN_TTL = 52 + NFV9_FIELD_MAX_TTL = 53 + NFV9_FIELD_IPV4_IDENT = 54 + NFV9_FIELD_DST_TOS = 55 + NFV9_FIELD_IN_SRC_MAC = 56 + NFV9_FIELD_OUT_DST_MAC = 57 + NFV9_FIELD_SRC_VLAN = 58 + NFV9_FIELD_DST_VLAN = 59 + NFV9_FIELD_IP_PROTOCOL_VERSION = 60 + NFV9_FIELD_DIRECTION = 61 + NFV9_FIELD_IPV6_NEXT_HOP = 62 + NFV9_FIELD_BGP_IPV6_NEXT_HOP = 63 + NFV9_FIELD_IPV6_OPTION_HEADERS = 64 + NFV9_FIELD_MPLS_LABEL_1 = 70 + NFV9_FIELD_MPLS_LABEL_2 = 71 + NFV9_FIELD_MPLS_LABEL_3 = 72 + NFV9_FIELD_MPLS_LABEL_4 = 73 + NFV9_FIELD_MPLS_LABEL_5 = 74 + NFV9_FIELD_MPLS_LABEL_6 = 75 + NFV9_FIELD_MPLS_LABEL_7 = 76 + NFV9_FIELD_MPLS_LABEL_8 = 77 + NFV9_FIELD_MPLS_LABEL_9 = 78 + NFV9_FIELD_MPLS_LABEL_10 = 79 + NFV9_FIELD_IN_DST_MAC = 80 + NFV9_FIELD_OUT_SRC_MAC = 81 + NFV9_FIELD_IF_NAME = 82 + NFV9_FIELD_IF_DESC = 83 + NFV9_FIELD_SAMPLER_NAME = 84 + NFV9_FIELD_IN_PERMANENT_BYTES = 85 + NFV9_FIELD_IN_PERMANENT_PKTS = 86 + NFV9_FIELD_FRAGMENT_OFFSET = 88 + NFV9_FIELD_FORWARDING_STATUS = 89 + NFV9_FIELD_MPLS_PAL_RD = 90 + NFV9_FIELD_MPLS_PREFIX_LEN = 91 + NFV9_FIELD_SRC_TRAFFIC_INDEX = 92 + NFV9_FIELD_DST_TRAFFIC_INDEX = 93 + NFV9_FIELD_APPLICATION_DESCRIPTION = 94 + NFV9_FIELD_APPLICATION_TAG = 95 + NFV9_FIELD_APPLICATION_NAME = 96 + NFV9_FIELD_postipDiffServCodePoint = 98 + NFV9_FIELD_replication_factor = 99 + NFV9_FIELD_layer2packetSectionOffset = 102 + NFV9_FIELD_layer2packetSectionSize = 103 + NFV9_FIELD_layer2packetSectionData = 104 +) + +type NFv9Packet struct { + Version uint16 + Count uint16 + SystemUptime uint32 + UnixSeconds uint32 + SequenceNumber uint32 + SourceId uint32 + FlowSets []interface{} +} + +type NFv9OptionsTemplateFlowSet struct { + FlowSetHeader + Records []NFv9OptionsTemplateRecord +} + +type NFv9OptionsTemplateRecord struct { + TemplateId uint16 + ScopeLength uint16 + OptionLength uint16 + Scopes []Field + Options []Field +} + +func NFv9TypeToString(typeId uint16) string { + + nameList := map[uint16]string{ + 1: "IN_BYTES", + 2: "IN_PKTS", + 3: "FLOWS", + 4: "PROTOCOL", + 5: "SRC_TOS", + 6: "TCP_FLAGS", + 7: "L4_SRC_PORT", + 8: "IPV4_SRC_ADDR", + 9: "SRC_MASK", + 10: "INPUT_SNMP", + 11: "L4_DST_PORT", + 12: "IPV4_DST_ADDR", + 13: "DST_MASK", + 14: "OUTPUT_SNMP", + 15: "IPV4_NEXT_HOP", + 16: "SRC_AS", + 17: "DST_AS", + 18: "BGP_IPV4_NEXT_HOP", + 19: "MUL_DST_PKTS", + 20: "MUL_DST_BYTES", + 21: "LAST_SWITCHED", + 22: "FIRST_SWITCHED", + 23: "OUT_BYTES", + 24: "OUT_PKTS", + 25: "MIN_PKT_LNGTH", + 26: "MAX_PKT_LNGTH", + 27: "IPV6_SRC_ADDR", + 28: "IPV6_DST_ADDR", + 29: "IPV6_SRC_MASK", + 30: "IPV6_DST_MASK", + 31: "IPV6_FLOW_LABEL", + 32: "ICMP_TYPE", + 33: "MUL_IGMP_TYPE", + 34: "SAMPLING_INTERVAL", + 35: "SAMPLING_ALGORITHM", + 36: "FLOW_ACTIVE_TIMEOUT", + 37: "FLOW_INACTIVE_TIMEOUT", + 38: "ENGINE_TYPE", + 39: "ENGINE_ID", + 40: "TOTAL_BYTES_EXP", + 41: "TOTAL_PKTS_EXP", + 42: "TOTAL_FLOWS_EXP", + 43: "*Vendor Proprietary*", + 44: "IPV4_SRC_PREFIX", + 45: "IPV4_DST_PREFIX", + 46: "MPLS_TOP_LABEL_TYPE", + 47: "MPLS_TOP_LABEL_IP_ADDR", + 48: "FLOW_SAMPLER_ID", + 49: "FLOW_SAMPLER_MODE", + 50: "FLOW_SAMPLER_RANDOM_INTERVAL", + 51: "*Vendor Proprietary*", + 52: "MIN_TTL", + 53: "MAX_TTL", + 54: "IPV4_IDENT", + 55: "DST_TOS", + 56: "IN_SRC_MAC", + 57: "OUT_DST_MAC", + 58: "SRC_VLAN", + 59: "DST_VLAN", + 60: "IP_PROTOCOL_VERSION", + 61: "DIRECTION", + 62: "IPV6_NEXT_HOP", + 63: "BPG_IPV6_NEXT_HOP", + 64: "IPV6_OPTION_HEADERS", + 65: "*Vendor Proprietary*", + 66: "*Vendor Proprietary*", + 67: "*Vendor Proprietary*", + 68: "*Vendor Proprietary*", + 69: "*Vendor Proprietary*", + 70: "MPLS_LABEL_1", + 71: "MPLS_LABEL_2", + 72: "MPLS_LABEL_3", + 73: "MPLS_LABEL_4", + 74: "MPLS_LABEL_5", + 75: "MPLS_LABEL_6", + 76: "MPLS_LABEL_7", + 77: "MPLS_LABEL_8", + 78: "MPLS_LABEL_9", + 79: "MPLS_LABEL_10", + 80: "IN_DST_MAC", + 81: "OUT_SRC_MAC", + 82: "IF_NAME", + 83: "IF_DESC", + 84: "SAMPLER_NAME", + 85: "IN_ PERMANENT _BYTES", + 86: "IN_ PERMANENT _PKTS", + 87: "*Vendor Proprietary*", + 88: "FRAGMENT_OFFSET", + 89: "FORWARDING STATUS", + 90: "MPLS PAL RD", + 91: "MPLS PREFIX LEN", + 92: "SRC TRAFFIC INDEX", + 93: "DST TRAFFIC INDEX", + 94: "APPLICATION DESCRIPTION", + 95: "APPLICATION TAG", + 96: "APPLICATION NAME", + 98: "postipDiffServCodePoint", + 99: "replication factor", + 100: "DEPRECATED", + 102: "layer2packetSectionOffset", + 103: "layer2packetSectionSize", + 104: "layer2packetSectionData", + 234: "ingressVRFID", + 235: "egressVRFID", + } + + if typeId > 104 || typeId == 0 { + return "Unassigned" + } else { + return nameList[typeId] + } +} + +func NFv9ScopeToString(scopeId uint16) string { + nameList := map[uint16]string{ + 1: "System", + 2: "Interface", + 3: "Line Card", + 4: "NetFlow Cache", + 5: "Template", + } + + if scopeId >= 1 && scopeId <= 5 { + return nameList[scopeId] + } else { + return "Unassigned" + } +} + +func (flowSet NFv9OptionsTemplateFlowSet) String(TypeToString func(uint16) string) string { + str := fmt.Sprintf(" Id %v\n", flowSet.Id) + str += fmt.Sprintf(" Length: %v\n", flowSet.Length) + str += fmt.Sprintf(" Records (%v records):\n", len(flowSet.Records)) + + for j, record := range flowSet.Records { + str += fmt.Sprintf(" - Record %v:\n", j) + str += fmt.Sprintf(" TemplateId: %v\n", record.TemplateId) + str += fmt.Sprintf(" ScopeLength: %v\n", record.ScopeLength) + str += fmt.Sprintf(" OptionLength: %v\n", record.OptionLength) + str += fmt.Sprintf(" Scopes (%v):\n", len(record.Scopes)) + + for k, field := range record.Scopes { + str += fmt.Sprintf(" - %v. %v (%v): %v\n", k, NFv9ScopeToString(field.Type), field.Type, field.Length) + } + + str += fmt.Sprintf(" Options (%v):\n", len(record.Options)) + + for k, field := range record.Options { + str += fmt.Sprintf(" - %v. %v (%v): %v\n", k, TypeToString(field.Type), field.Type, field.Length) + } + } + + return str +} + +func (p NFv9Packet) String() string { + str := "Flow Packet\n" + str += "------------\n" + str += fmt.Sprintf(" Version: %v\n", p.Version) + str += fmt.Sprintf(" Count: %v\n", p.Count) + + unixSeconds := time.Unix(int64(p.UnixSeconds), 0) + str += fmt.Sprintf(" SystemUptime: %v\n", p.SystemUptime) + str += fmt.Sprintf(" UnixSeconds: %v\n", unixSeconds.UTC().String()) + str += fmt.Sprintf(" SequenceNumber: %v\n", p.SequenceNumber) + str += fmt.Sprintf(" SourceId: %v\n", p.SourceId) + str += fmt.Sprintf(" FlowSets (%v):\n", len(p.FlowSets)) + + for i, flowSet := range p.FlowSets { + switch flowSet := flowSet.(type) { + case TemplateFlowSet: + str += fmt.Sprintf(" - TemplateFlowSet %v:\n", i) + str += flowSet.String(NFv9TypeToString) + case NFv9OptionsTemplateFlowSet: + str += fmt.Sprintf(" - OptionsTemplateFlowSet %v:\n", i) + str += flowSet.String(NFv9TypeToString) + case DataFlowSet: + str += fmt.Sprintf(" - DataFlowSet %v:\n", i) + str += flowSet.String(NFv9TypeToString) + case OptionsDataFlowSet: + str += fmt.Sprintf(" - OptionsDataFlowSet %v:\n", i) + str += flowSet.String(NFv9TypeToString, NFv9ScopeToString) + default: + str += fmt.Sprintf(" - (unknown type) %v: %v\n", i, flowSet) + } + } + return str +} diff --git a/third_party/goflow2/decoders/netflow/packet.go b/third_party/goflow2/decoders/netflow/packet.go new file mode 100644 index 000000000000..3e3707d641ec --- /dev/null +++ b/third_party/goflow2/decoders/netflow/packet.go @@ -0,0 +1,158 @@ +package netflow + +import ( + "fmt" +) + +// FlowSetHeader contains fields shared by all Flow Sets (DataFlowSet, +// TemplateFlowSet, OptionsTemplateFlowSet). +type FlowSetHeader struct { + // FlowSet ID: + // 0 for TemplateFlowSet + // 1 for OptionsTemplateFlowSet + // 256-65535 for DataFlowSet (used as TemplateId) + Id uint16 + + // The total length of this FlowSet in bytes (including padding). + Length uint16 +} + +// TemplateFlowSet is a collection of templates that describe structure of Data +// Records (actual NetFlow data). +type TemplateFlowSet struct { + FlowSetHeader + + // List of Template Records + Records []TemplateRecord +} + +// DataFlowSet is a collection of Data Records (actual NetFlow data) and Options +// Data Records (meta data). +type DataFlowSet struct { + FlowSetHeader + + Records []DataRecord +} + +type OptionsDataFlowSet struct { + FlowSetHeader + + Records []OptionsDataRecord +} + +// TemplateRecord is a single template that describes structure of a Flow Record +// (actual Netflow data). +type TemplateRecord struct { + // Each of the newly generated Template Records is given a unique + // Template ID. This uniqueness is local to the Observation Domain that + // generated the Template ID. Template IDs of Data FlowSets are numbered + // from 256 to 65535. + TemplateId uint16 + + // Number of fields in this Template Record. Because a Template FlowSet + // usually contains multiple Template Records, this field allows the + // Collector to determine the end of the current Template Record and + // the start of the next. + FieldCount uint16 + + // List of fields in this Template Record. + Fields []Field +} + +type DataRecord struct { + Values []DataField +} + +// OptionsDataRecord is meta data sent alongide actual NetFlow data. Combined +// with OptionsTemplateRecord it can be decoded to a single data row. +type OptionsDataRecord struct { + // List of Scope values stored in raw format as []byte + ScopesValues []DataField + + // List of Optons values stored in raw format as []byte + OptionsValues []DataField +} + +// Field describes type and length of a single value in a Flow Data Record. +// Field does not contain the record value itself it is just a description of +// what record value will look like. +type Field struct { + // A numeric value that represents the type of field. + PenProvided bool + Type uint16 + + // The length (in bytes) of the field. + Length uint16 + + Pen uint32 +} + +type DataField struct { + // A numeric value that represents the type of field. + PenProvided bool + Type uint16 + Pen uint32 + + // The value (in bytes) of the field. + Value interface{} + //Value []byte +} + +func (flowSet OptionsDataFlowSet) String(TypeToString func(uint16) string, ScopeToString func(uint16) string) string { + str := fmt.Sprintf(" Id %v\n", flowSet.Id) + str += fmt.Sprintf(" Length: %v\n", flowSet.Length) + str += fmt.Sprintf(" Records (%v records):\n", len(flowSet.Records)) + + for j, record := range flowSet.Records { + str += fmt.Sprintf(" - Record %v:\n", j) + str += fmt.Sprintf(" Scopes (%v):\n", len(record.ScopesValues)) + + for k, value := range record.ScopesValues { + str += fmt.Sprintf(" - %v. %v (%v): %v\n", k, ScopeToString(value.Type), value.Type, value.Value) + } + + str += fmt.Sprintf(" Options (%v):\n", len(record.OptionsValues)) + + for k, value := range record.OptionsValues { + str += fmt.Sprintf(" - %v. %v (%v): %v\n", k, TypeToString(value.Type), value.Type, value.Value) + } + } + + return str +} + +func (flowSet DataFlowSet) String(TypeToString func(uint16) string) string { + str := fmt.Sprintf(" Id %v\n", flowSet.Id) + str += fmt.Sprintf(" Length: %v\n", flowSet.Length) + str += fmt.Sprintf(" Records (%v records):\n", len(flowSet.Records)) + + for j, record := range flowSet.Records { + str += fmt.Sprintf(" - Record %v:\n", j) + str += fmt.Sprintf(" Values (%v):\n", len(record.Values)) + + for k, value := range record.Values { + str += fmt.Sprintf(" - %v. %v (%v): %v\n", k, TypeToString(value.Type), value.Type, value.Value) + } + } + + return str +} + +func (flowSet TemplateFlowSet) String(TypeToString func(uint16) string) string { + str := fmt.Sprintf(" Id %v\n", flowSet.Id) + str += fmt.Sprintf(" Length: %v\n", flowSet.Length) + str += fmt.Sprintf(" Records (%v records):\n", len(flowSet.Records)) + + for j, record := range flowSet.Records { + str += fmt.Sprintf(" - %v. Record:\n", j) + str += fmt.Sprintf(" TemplateId: %v\n", record.TemplateId) + str += fmt.Sprintf(" FieldCount: %v\n", record.FieldCount) + str += fmt.Sprintf(" Fields (%v):\n", len(record.Fields)) + + for k, field := range record.Fields { + str += fmt.Sprintf(" - %v. %v (%v/%v): %v\n", k, TypeToString(field.Type), field.Type, field.PenProvided, field.Length) + } + } + + return str +} diff --git a/third_party/goflow2/decoders/netflow/templates/file/file.go b/third_party/goflow2/decoders/netflow/templates/file/file.go new file mode 100644 index 000000000000..476babbe4109 --- /dev/null +++ b/third_party/goflow2/decoders/netflow/templates/file/file.go @@ -0,0 +1,204 @@ +package file + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "github.com/netsampler/goflow2/decoders/netflow" + "github.com/netsampler/goflow2/decoders/netflow/templates" + "github.com/netsampler/goflow2/decoders/netflow/templates/memory" + "os" + "sync" +) + +type TemplateFileObject struct { + Key *templates.TemplateKey + Data *TemplateFileData +} + +type TemplateFileData struct { + Type string + Data interface{} +} + +func (d *TemplateFileData) UnmarshalJSON(b []byte) error { + var s struct { + Type string + Data interface{} `json:"-"` + } + if err := json.Unmarshal(b, &s); err != nil { + return err + } + + switch s.Type { + case "NFv9OptionsTemplateRecord": + newS := new(struct { + Type string + Data netflow.NFv9OptionsTemplateRecord + }) + if err := json.Unmarshal(b, newS); err != nil { + return err + } + d.Type = newS.Type + d.Data = newS.Data + case "TemplateRecord": + newS := new(struct { + Type string + Data netflow.TemplateRecord + }) + if err := json.Unmarshal(b, newS); err != nil { + return err + } + d.Type = newS.Type + d.Data = newS.Data + case "IPFIXOptionsTemplateRecord": + newS := new(struct { + Type string + Data netflow.IPFIXOptionsTemplateRecord + }) + if err := json.Unmarshal(b, newS); err != nil { + return err + } + d.Type = newS.Type + d.Data = newS.Data + } + + return nil +} + +type TemplateFile struct { + Templates []*TemplateFileObject `json:"templates"` +} + +func (f *TemplateFile) Add(key *templates.TemplateKey, data interface{}) { + var typeName string + + switch data.(type) { + case netflow.NFv9OptionsTemplateRecord: + typeName = "NFv9OptionsTemplateRecord" + case netflow.TemplateRecord: + typeName = "TemplateRecord" + case netflow.IPFIXOptionsTemplateRecord: + typeName = "IPFIXOptionsTemplateRecord" + default: + return + } + + f.Templates = append(f.Templates, &TemplateFileObject{ + Key: key, + Data: &TemplateFileData{ + Type: typeName, + Data: data, + }, + }) +} + +func NewTemplateFile() *TemplateFile { + return &TemplateFile{ + Templates: make([]*TemplateFileObject, 0), + } +} + +type FileDriver struct { + memDriver *memory.MemoryDriver + path string + lock *sync.Mutex +} + +func (d *FileDriver) Prepare() error { + d.memDriver = memory.Driver + d.lock = &sync.Mutex{} + flag.StringVar(&d.path, "netflow.templates.file.path", "./templates.json", "Path of file to store templates") + return nil +} + +func (d *FileDriver) Init(ctx context.Context) error { + var err error + if err = d.memDriver.Init(ctx); err != nil { + return err + } + + f, err := os.OpenFile(d.path, os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + return err + } + defer f.Close() + dec := json.NewDecoder(f) + tf := NewTemplateFile() + if err = dec.Decode(tf); err != nil { + // log error + } + for _, template := range tf.Templates { + if err := d.memDriver.AddTemplate(ctx, template.Key, template.Data.Data); err != nil { + // log error + continue + } + } + + return nil +} + +func (d *FileDriver) Close(ctx context.Context) error { + if err := d.memDriver.Close(ctx); err != nil { + return err + } + return nil +} + +func (d *FileDriver) ListTemplates(ctx context.Context, ch chan *templates.TemplateKey) error { + return d.memDriver.ListTemplates(ctx, ch) +} + +func (d *FileDriver) AddTemplate(ctx context.Context, key *templates.TemplateKey, template interface{}) error { + d.lock.Lock() + defer d.lock.Unlock() + if err := d.memDriver.AddTemplate(ctx, key, template); err != nil { + return err + } + + tf := NewTemplateFile() + + ch := make(chan *templates.TemplateKey, 5) + go func() { + if err := d.memDriver.ListTemplates(ctx, ch); err != nil { + // log error + close(ch) + } + }() + for key := range ch { + if key == nil { + break + } + if template, err := d.memDriver.GetTemplate(ctx, key); err != nil { + // log error + continue + } else { + tf.Add(key, template) + } + + } + + tmpPath := fmt.Sprintf("%s-tmp", d.path) + f, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + return err + } + + enc := json.NewEncoder(f) + if err := enc.Encode(tf); err != nil { + f.Close() + return err + } + + return os.Rename(tmpPath, d.path) +} + +func (d *FileDriver) GetTemplate(ctx context.Context, key *templates.TemplateKey) (interface{}, error) { + return d.memDriver.GetTemplate(ctx, key) +} + +func init() { + d := &FileDriver{} + templates.RegisterTemplateDriver("file", d) +} diff --git a/third_party/goflow2/decoders/netflow/templates/memory/memory.go b/third_party/goflow2/decoders/netflow/templates/memory/memory.go new file mode 100644 index 000000000000..0f16b241e1d4 --- /dev/null +++ b/third_party/goflow2/decoders/netflow/templates/memory/memory.go @@ -0,0 +1,73 @@ +package memory + +import ( + "context" + "github.com/netsampler/goflow2/decoders/netflow/templates" + "sync" +) + +var ( + Driver = &MemoryDriver{} +) + +type templateData struct { + key *templates.TemplateKey + data interface{} +} + +type MemoryDriver struct { + lock *sync.RWMutex + templates map[string]templateData +} + +func (d *MemoryDriver) Prepare() error { + // could have an expiry + return nil +} + +func (d *MemoryDriver) Init(context.Context) error { + d.lock = &sync.RWMutex{} + d.templates = make(map[string]templateData) + return nil +} + +func (d *MemoryDriver) Close(context.Context) error { + return nil +} + +func (d *MemoryDriver) ListTemplates(ctx context.Context, ch chan *templates.TemplateKey) error { + d.lock.RLock() + defer d.lock.RUnlock() + for _, v := range d.templates { + select { + case ch <- v.key: + case <-ctx.Done(): + return ctx.Err() + } + } + select { + case ch <- nil: + } + return nil +} + +func (d *MemoryDriver) AddTemplate(ctx context.Context, key *templates.TemplateKey, template interface{}) error { + d.lock.Lock() + defer d.lock.Unlock() + + d.templates[key.String()] = templateData{ + key: key, + data: template, + } + return nil +} + +func (d *MemoryDriver) GetTemplate(ctx context.Context, key *templates.TemplateKey) (interface{}, error) { + d.lock.RLock() + defer d.lock.RUnlock() + return d.templates[key.String()].data, nil +} + +func init() { + templates.RegisterTemplateDriver("memory", Driver) +} diff --git a/third_party/goflow2/decoders/netflow/templates/templates.go b/third_party/goflow2/decoders/netflow/templates/templates.go new file mode 100644 index 000000000000..525e6b10e1d3 --- /dev/null +++ b/third_party/goflow2/decoders/netflow/templates/templates.go @@ -0,0 +1,139 @@ +package templates + +import ( + "context" + "fmt" + "strconv" + "strings" + "sync" +) + +var ( + templateDrivers = make(map[string]TemplateDriver) // might be better to change into "factory" + lock = &sync.RWMutex{} +) + +type TemplateDriver interface { + TemplateInterface + + Prepare() error // Prepare driver (eg: flag registration) + Init(context.Context) error // Initialize driver (eg: parse keying) + Close(context.Context) error // Close drive (eg: close file) +} + +type TemplateKey struct { + TemplateKey string + Version uint16 + ObsDomainId uint32 + TemplateId uint16 +} + +func NewTemplateKey(templateKey string, version uint16, obsDomainId uint32, templateId uint16) *TemplateKey { + return &TemplateKey{ + TemplateKey: templateKey, + Version: version, + ObsDomainId: obsDomainId, + TemplateId: templateId, + } +} + +func (k *TemplateKey) String() string { + return fmt.Sprintf("%s-%d-%d-%d", k.TemplateKey, k.Version, k.ObsDomainId, k.TemplateId) +} + +func ParseTemplateKey(key string, k *TemplateKey) error { + if k != nil { + return nil + } + var version uint16 + var obsDomainId uint32 + var templateId uint16 + + keySplit := strings.Split(key, "-") + if len(keySplit) != 4 { + return fmt.Errorf("template key format is invalid") + } + templateKey := keySplit[0] + if val, err := strconv.ParseUint(keySplit[1], 10, 64); err != nil { + return fmt.Errorf("template key version is invalid") + } else { + version = uint16(val) + } + if val, err := strconv.ParseUint(keySplit[2], 10, 64); err != nil { + fmt.Errorf("template key observation domain I Dis invalid") + } else { + obsDomainId = uint32(val) + } + if val, err := strconv.ParseUint(keySplit[3], 10, 64); err != nil { + fmt.Errorf("template key template ID is invalid") + } else { + templateId = uint16(val) + } + + k.TemplateKey = templateKey + k.Version = version + k.ObsDomainId = obsDomainId + k.TemplateId = templateId + + return nil +} + +type TemplateInterface interface { + ListTemplates(ctx context.Context, ch chan *TemplateKey) error + GetTemplate(ctx context.Context, key *TemplateKey) (interface{}, error) + AddTemplate(ctx context.Context, key *TemplateKey, template interface{}) error // add expiration +} + +type TemplateSystem struct { + driver TemplateDriver +} + +func (t *TemplateSystem) ListTemplates(ctx context.Context, ch chan *TemplateKey) error { + return t.driver.ListTemplates(ctx, ch) +} + +func (t *TemplateSystem) AddTemplate(ctx context.Context, key *TemplateKey, template interface{}) error { + return t.driver.AddTemplate(ctx, key, template) +} + +func (t *TemplateSystem) GetTemplate(ctx context.Context, key *TemplateKey) (interface{}, error) { + return t.driver.GetTemplate(ctx, key) +} + +func (t *TemplateSystem) Close(ctx context.Context) error { + return t.driver.Close(ctx) +} + +func RegisterTemplateDriver(name string, t TemplateDriver) { + lock.Lock() + templateDrivers[name] = t + lock.Unlock() + + if err := t.Prepare(); err != nil { + panic(err) + } +} + +func FindTemplateSystem(ctx context.Context, name string) (*TemplateSystem, error) { + lock.RLock() + t, ok := templateDrivers[name] + lock.RUnlock() + if !ok { + return nil, fmt.Errorf("Template %s not found", name) + } + + err := t.Init(ctx) + return &TemplateSystem{t}, err +} + +func GetTemplates() []string { + lock.RLock() + defer lock.RUnlock() + t := make([]string, len(templateDrivers)) + var i int + for k, _ := range templateDrivers { + t[i] = k + i++ + } + return t +} diff --git a/third_party/goflow2/decoders/netflowlegacy/netflow.go b/third_party/goflow2/decoders/netflowlegacy/netflow.go new file mode 100644 index 000000000000..c73a54e47cf4 --- /dev/null +++ b/third_party/goflow2/decoders/netflowlegacy/netflow.go @@ -0,0 +1,59 @@ +package netflowlegacy + +import ( + "bytes" + "fmt" + + "github.com/netsampler/goflow2/decoders/utils" +) + +type ErrorVersion struct { + version uint16 +} + +func NewErrorVersion(version uint16) *ErrorVersion { + return &ErrorVersion{ + version: version, + } +} + +func (e *ErrorVersion) Error() string { + return fmt.Sprintf("Unknown NetFlow version %v (only decodes v5)", e.version) +} + +func DecodeMessage(payload *bytes.Buffer) (interface{}, error) { + var version uint16 + err := utils.BinaryDecoder(payload, &version) + if err != nil { + return nil, err + } + packet := PacketNetFlowV5{} + if version == 5 { + packet.Version = version + + utils.BinaryDecoder(payload, + &(packet.Count), + &(packet.SysUptime), + &(packet.UnixSecs), + &(packet.UnixNSecs), + &(packet.FlowSequence), + &(packet.EngineType), + &(packet.EngineId), + &(packet.SamplingInterval), + ) + + packet.Records = make([]RecordsNetFlowV5, int(packet.Count)) + for i := 0; i < int(packet.Count) && payload.Len() >= 48; i++ { + record := RecordsNetFlowV5{} + err := utils.BinaryDecoder(payload, &record) + if err != nil { + return packet, err + } + packet.Records[i] = record + } + + return packet, nil + } else { + return nil, NewErrorVersion(version) + } +} diff --git a/third_party/goflow2/decoders/netflowlegacy/netflow_test.go b/third_party/goflow2/decoders/netflowlegacy/netflow_test.go new file mode 100644 index 000000000000..076db5d074e4 --- /dev/null +++ b/third_party/goflow2/decoders/netflowlegacy/netflow_test.go @@ -0,0 +1,41 @@ +package netflowlegacy + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDecodeNetFlowV5(t *testing.T) { + data := []byte{ + 0x00, 0x05, 0x00, 0x06, 0x00, 0x82, 0xc3, 0x48, 0x5b, 0xcd, 0xba, 0x1b, 0x05, 0x97, 0x6d, 0xc7, + 0x00, 0x00, 0x64, 0x3d, 0x08, 0x08, 0x00, 0x00, 0x0a, 0x80, 0x02, 0x79, 0x0a, 0x80, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x4e, + 0x00, 0x82, 0x9b, 0x8c, 0x00, 0x82, 0x9b, 0x90, 0x1f, 0x90, 0xb9, 0x18, 0x00, 0x1b, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x02, 0x77, 0x0a, 0x81, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x94, + 0x00, 0x82, 0x95, 0xa9, 0x00, 0x82, 0x9a, 0xfb, 0x1f, 0x90, 0xc1, 0x2c, 0x00, 0x12, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x81, 0x02, 0x01, 0x0a, 0x80, 0x02, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xc2, + 0x00, 0x82, 0x95, 0xa9, 0x00, 0x82, 0x9a, 0xfc, 0xc1, 0x2c, 0x1f, 0x90, 0x00, 0x16, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x02, 0x01, 0x0a, 0x80, 0x02, 0x79, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0xf1, + 0x00, 0x82, 0x9b, 0x8c, 0x00, 0x82, 0x9b, 0x8f, 0xb9, 0x18, 0x1f, 0x90, 0x00, 0x1b, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x02, 0x01, 0x0a, 0x80, 0x02, 0x79, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x2e, + 0x00, 0x82, 0x9b, 0x90, 0x00, 0x82, 0x9b, 0x9d, 0xb9, 0x1a, 0x1f, 0x90, 0x00, 0x1b, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x02, 0x79, 0x0a, 0x80, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x0b, 0xac, + 0x00, 0x82, 0x9b, 0x90, 0x00, 0x82, 0x9b, 0x9d, 0x1f, 0x90, 0xb9, 0x1a, 0x00, 0x1b, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } + buf := bytes.NewBuffer(data) + + dec, err := DecodeMessage(buf) + assert.Nil(t, err) + assert.NotNil(t, dec) + decNfv5 := dec.(PacketNetFlowV5) + assert.Equal(t, uint16(5), decNfv5.Version) + assert.Equal(t, uint16(9), decNfv5.Records[0].Input) +} diff --git a/third_party/goflow2/decoders/netflowlegacy/packet.go b/third_party/goflow2/decoders/netflowlegacy/packet.go new file mode 100644 index 000000000000..078bba4dff7e --- /dev/null +++ b/third_party/goflow2/decoders/netflowlegacy/packet.go @@ -0,0 +1,96 @@ +package netflowlegacy + +import ( + "encoding/binary" + "fmt" + "net" + "time" +) + +type PacketNetFlowV5 struct { + Version uint16 + Count uint16 + SysUptime uint32 + UnixSecs uint32 + UnixNSecs uint32 + FlowSequence uint32 + EngineType uint8 + EngineId uint8 + SamplingInterval uint16 + Records []RecordsNetFlowV5 +} + +type RecordsNetFlowV5 struct { + SrcAddr uint32 + DstAddr uint32 + NextHop uint32 + Input uint16 + Output uint16 + DPkts uint32 + DOctets uint32 + First uint32 + Last uint32 + SrcPort uint16 + DstPort uint16 + Pad1 byte + TCPFlags uint8 + Proto uint8 + Tos uint8 + SrcAS uint16 + DstAS uint16 + SrcMask uint8 + DstMask uint8 + Pad2 uint16 +} + +func (p PacketNetFlowV5) String() string { + str := "NetFlow v5 Packet\n" + str += "-----------------\n" + str += fmt.Sprintf(" Version: %v\n", p.Version) + str += fmt.Sprintf(" Count: %v\n", p.Count) + + unixSeconds := time.Unix(int64(p.UnixSecs), int64(p.UnixNSecs)) + str += fmt.Sprintf(" SystemUptime: %v\n", time.Duration(p.SysUptime)*time.Millisecond) + str += fmt.Sprintf(" UnixSeconds: %v\n", unixSeconds.String()) + str += fmt.Sprintf(" FlowSequence: %v\n", p.FlowSequence) + str += fmt.Sprintf(" EngineType: %v\n", p.EngineType) + str += fmt.Sprintf(" EngineId: %v\n", p.EngineId) + str += fmt.Sprintf(" SamplingInterval: %v\n", p.SamplingInterval) + str += fmt.Sprintf(" Records (%v):\n", len(p.Records)) + + for i, record := range p.Records { + str += fmt.Sprintf(" Record %v:\n", i) + str += record.String() + } + return str +} + +func (r RecordsNetFlowV5) String() string { + srcaddr := make(net.IP, 4) + binary.BigEndian.PutUint32(srcaddr, r.SrcAddr) + dstaddr := make(net.IP, 4) + binary.BigEndian.PutUint32(dstaddr, r.DstAddr) + nexthop := make(net.IP, 4) + binary.BigEndian.PutUint32(nexthop, r.NextHop) + + str := fmt.Sprintf(" SrcAddr: %v\n", srcaddr.String()) + str += fmt.Sprintf(" DstAddr: %v\n", dstaddr.String()) + str += fmt.Sprintf(" NextHop: %v\n", nexthop.String()) + str += fmt.Sprintf(" Input: %v\n", r.Input) + str += fmt.Sprintf(" Output: %v\n", r.Output) + str += fmt.Sprintf(" DPkts: %v\n", r.DPkts) + str += fmt.Sprintf(" DOctets: %v\n", r.DOctets) + str += fmt.Sprintf(" First: %v\n", time.Duration(r.First)*time.Millisecond) + str += fmt.Sprintf(" Last: %v\n", time.Duration(r.Last)*time.Millisecond) + str += fmt.Sprintf(" SrcPort: %v\n", r.SrcPort) + str += fmt.Sprintf(" DstPort: %v\n", r.DstPort) + str += fmt.Sprintf(" TCPFlags: %v\n", r.TCPFlags) + str += fmt.Sprintf(" Proto: %v\n", r.Proto) + str += fmt.Sprintf(" Tos: %v\n", r.Tos) + str += fmt.Sprintf(" SrcAS: %v\n", r.SrcAS) + str += fmt.Sprintf(" DstAS: %v\n", r.DstAS) + str += fmt.Sprintf(" SrcMask: %v\n", r.SrcMask) + str += fmt.Sprintf(" DstMask: %v\n", r.DstMask) + + return str +} diff --git a/third_party/goflow2/decoders/sflow/datastructure.go b/third_party/goflow2/decoders/sflow/datastructure.go new file mode 100644 index 000000000000..670652a2cfd5 --- /dev/null +++ b/third_party/goflow2/decoders/sflow/datastructure.go @@ -0,0 +1,103 @@ +package sflow + +type SampledHeader struct { + Protocol uint32 + FrameLength uint32 + Stripped uint32 + OriginalLength uint32 + HeaderData []byte +} + +type SampledEthernet struct { + Length uint32 + SrcMac []byte + DstMac []byte + EthType uint32 +} + +type SampledIP_Base struct { + Length uint32 + Protocol uint32 + SrcIP []byte + DstIP []byte + SrcPort uint32 + DstPort uint32 + TcpFlags uint32 +} + +type SampledIPv4 struct { + Base SampledIP_Base + Tos uint32 +} + +type SampledIPv6 struct { + Base SampledIP_Base + Priority uint32 +} + +type ExtendedSwitch struct { + SrcVlan uint32 + SrcPriority uint32 + DstVlan uint32 + DstPriority uint32 +} + +type ExtendedRouter struct { + NextHopIPVersion uint32 + NextHop []byte + SrcMaskLen uint32 + DstMaskLen uint32 +} + +type ExtendedGateway struct { + NextHopIPVersion uint32 + NextHop []byte + AS uint32 + SrcAS uint32 + SrcPeerAS uint32 + ASDestinations uint32 + ASPathType uint32 + ASPathLength uint32 + ASPath []uint32 + CommunitiesLength uint32 + Communities []uint32 + LocalPref uint32 +} + +type IfCounters struct { + IfIndex uint32 + IfType uint32 + IfSpeed uint64 + IfDirection uint32 + IfStatus uint32 + IfInOctets uint64 + IfInUcastPkts uint32 + IfInMulticastPkts uint32 + IfInBroadcastPkts uint32 + IfInDiscards uint32 + IfInErrors uint32 + IfInUnknownProtos uint32 + IfOutOctets uint64 + IfOutUcastPkts uint32 + IfOutMulticastPkts uint32 + IfOutBroadcastPkts uint32 + IfOutDiscards uint32 + IfOutErrors uint32 + IfPromiscuousMode uint32 +} + +type EthernetCounters struct { + Dot3StatsAlignmentErrors uint32 + Dot3StatsFCSErrors uint32 + Dot3StatsSingleCollisionFrames uint32 + Dot3StatsMultipleCollisionFrames uint32 + Dot3StatsSQETestErrors uint32 + Dot3StatsDeferredTransmissions uint32 + Dot3StatsLateCollisions uint32 + Dot3StatsExcessiveCollisions uint32 + Dot3StatsInternalMacTransmitErrors uint32 + Dot3StatsCarrierSenseErrors uint32 + Dot3StatsFrameTooLongs uint32 + Dot3StatsInternalMacReceiveErrors uint32 + Dot3StatsSymbolErrors uint32 +} diff --git a/third_party/goflow2/decoders/sflow/packet.go b/third_party/goflow2/decoders/sflow/packet.go new file mode 100644 index 000000000000..647f83db39c6 --- /dev/null +++ b/third_party/goflow2/decoders/sflow/packet.go @@ -0,0 +1,73 @@ +package sflow + +type Packet struct { + Version uint32 + IPVersion uint32 + AgentIP []byte + SubAgentId uint32 + SequenceNumber uint32 + Uptime uint32 + SamplesCount uint32 + Samples []interface{} +} + +type SampleHeader struct { + Format uint32 + Length uint32 + + SampleSequenceNumber uint32 + SourceIdType uint32 + SourceIdValue uint32 +} + +type FlowSample struct { + Header SampleHeader + + SamplingRate uint32 + SamplePool uint32 + Drops uint32 + Input uint32 + Output uint32 + FlowRecordsCount uint32 + Records []FlowRecord +} + +type CounterSample struct { + Header SampleHeader + + CounterRecordsCount uint32 + Records []CounterRecord +} + +type ExpandedFlowSample struct { + Header SampleHeader + + SamplingRate uint32 + SamplePool uint32 + Drops uint32 + InputIfFormat uint32 + InputIfValue uint32 + OutputIfFormat uint32 + OutputIfValue uint32 + FlowRecordsCount uint32 + Records []FlowRecord +} + +type RecordHeader struct { + DataFormat uint32 + Length uint32 +} + +type FlowRecord struct { + Header RecordHeader + Data interface{} +} + +type FlowRecordRaw struct { + Data []byte +} + +type CounterRecord struct { + Header RecordHeader + Data interface{} +} diff --git a/third_party/goflow2/decoders/sflow/sflow.go b/third_party/goflow2/decoders/sflow/sflow.go new file mode 100644 index 000000000000..4251443851f6 --- /dev/null +++ b/third_party/goflow2/decoders/sflow/sflow.go @@ -0,0 +1,417 @@ +package sflow + +import ( + "bytes" + "errors" + "fmt" + + "github.com/netsampler/goflow2/decoders/utils" +) + +const ( + FORMAT_EXT_SWITCH = 1001 + FORMAT_EXT_ROUTER = 1002 + FORMAT_EXT_GATEWAY = 1003 + FORMAT_RAW_PKT = 1 + FORMAT_ETH = 2 + FORMAT_IPV4 = 3 + FORMAT_IPV6 = 4 +) + +type ErrorDecodingSFlow struct { + msg string +} + +func NewErrorDecodingSFlow(msg string) *ErrorDecodingSFlow { + return &ErrorDecodingSFlow{ + msg: msg, + } +} + +func (e *ErrorDecodingSFlow) Error() string { + return fmt.Sprintf("Error decoding sFlow: %v", e.msg) +} + +type ErrorDataFormat struct { + dataformat uint32 +} + +func NewErrorDataFormat(dataformat uint32) *ErrorDataFormat { + return &ErrorDataFormat{ + dataformat: dataformat, + } +} + +func (e *ErrorDataFormat) Error() string { + return fmt.Sprintf("Unknown data format %v", e.dataformat) +} + +type ErrorIPVersion struct { + version uint32 +} + +func NewErrorIPVersion(version uint32) *ErrorIPVersion { + return &ErrorIPVersion{ + version: version, + } +} + +func (e *ErrorIPVersion) Error() string { + return fmt.Sprintf("Unknown IP version: %v", e.version) +} + +type ErrorVersion struct { + version uint32 +} + +func NewErrorVersion(version uint32) *ErrorVersion { + return &ErrorVersion{ + version: version, + } +} + +func (e *ErrorVersion) Error() string { + return fmt.Sprintf("Unknown sFlow version %v (supported v5)", e.version) +} + +func DecodeCounterRecord(header *RecordHeader, payload *bytes.Buffer) (CounterRecord, error) { + counterRecord := CounterRecord{ + Header: *header, + } + switch (*header).DataFormat { + case 1: + ifCounters := IfCounters{} + err := utils.BinaryDecoder(payload, &ifCounters) + if err != nil { + return counterRecord, err + } + counterRecord.Data = ifCounters + case 2: + ethernetCounters := EthernetCounters{} + err := utils.BinaryDecoder(payload, ðernetCounters) + if err != nil { + return counterRecord, err + } + counterRecord.Data = ethernetCounters + default: + counterRecord.Data = &FlowRecordRaw{ + Data: payload.Next(int(header.Length)), + } + } + + return counterRecord, nil +} + +func DecodeIP(payload *bytes.Buffer) (uint32, []byte, error) { + var ipVersion uint32 + err := utils.BinaryDecoder(payload, &ipVersion) + if err != nil { + return 0, nil, err + } + var ip []byte + if ipVersion == 1 { + ip = make([]byte, 4) + } else if ipVersion == 2 { + ip = make([]byte, 16) + } else { + return ipVersion, ip, NewErrorIPVersion(ipVersion) + } + if payload.Len() >= len(ip) { + err := utils.BinaryDecoder(payload, &ip) + if err != nil { + return 0, nil, err + } + } else { + return ipVersion, ip, NewErrorDecodingSFlow(fmt.Sprintf("Not enough data: %v, needs %v.", payload.Len(), len(ip))) + } + return ipVersion, ip, nil +} + +func DecodeFlowRecord(header *RecordHeader, payload *bytes.Buffer) (FlowRecord, error) { + flowRecord := FlowRecord{ + Header: *header, + } + switch (*header).DataFormat { + case FORMAT_EXT_SWITCH: + extendedSwitch := ExtendedSwitch{} + err := utils.BinaryDecoder(payload, &extendedSwitch) + if err != nil { + return flowRecord, err + } + flowRecord.Data = extendedSwitch + case FORMAT_RAW_PKT: + sampledHeader := SampledHeader{} + err := utils.BinaryDecoder(payload, &(sampledHeader.Protocol), &(sampledHeader.FrameLength), &(sampledHeader.Stripped), &(sampledHeader.OriginalLength)) + if err != nil { + return flowRecord, err + } + sampledHeader.HeaderData = payload.Bytes() + flowRecord.Data = sampledHeader + case FORMAT_IPV4: + sampledIPBase := SampledIP_Base{ + SrcIP: make([]byte, 4), + DstIP: make([]byte, 4), + } + err := utils.BinaryDecoder(payload, &sampledIPBase) + if err != nil { + return flowRecord, err + } + sampledIPv4 := SampledIPv4{ + Base: sampledIPBase, + } + err = utils.BinaryDecoder(payload, &(sampledIPv4.Tos)) + if err != nil { + return flowRecord, err + } + flowRecord.Data = sampledIPv4 + case FORMAT_IPV6: + sampledIPBase := SampledIP_Base{ + SrcIP: make([]byte, 16), + DstIP: make([]byte, 16), + } + err := utils.BinaryDecoder(payload, &sampledIPBase) + if err != nil { + return flowRecord, err + } + sampledIPv6 := SampledIPv6{ + Base: sampledIPBase, + } + err = utils.BinaryDecoder(payload, &(sampledIPv6.Priority)) + if err != nil { + return flowRecord, err + } + flowRecord.Data = sampledIPv6 + case FORMAT_EXT_ROUTER: + extendedRouter := ExtendedRouter{} + + ipVersion, ip, err := DecodeIP(payload) + if err != nil { + return flowRecord, err + } + extendedRouter.NextHopIPVersion = ipVersion + extendedRouter.NextHop = ip + err = utils.BinaryDecoder(payload, &(extendedRouter.SrcMaskLen), &(extendedRouter.DstMaskLen)) + if err != nil { + return flowRecord, err + } + flowRecord.Data = extendedRouter + case FORMAT_EXT_GATEWAY: + extendedGateway := ExtendedGateway{} + ipVersion, ip, err := DecodeIP(payload) + if err != nil { + return flowRecord, err + } + extendedGateway.NextHopIPVersion = ipVersion + extendedGateway.NextHop = ip + err = utils.BinaryDecoder(payload, &(extendedGateway.AS), &(extendedGateway.SrcAS), &(extendedGateway.SrcPeerAS), + &(extendedGateway.ASDestinations)) + if err != nil { + return flowRecord, err + } + var asPath []uint32 + if extendedGateway.ASDestinations != 0 { + err := utils.BinaryDecoder(payload, &(extendedGateway.ASPathType), &(extendedGateway.ASPathLength)) + if err != nil { + return flowRecord, err + } + if int(extendedGateway.ASPathLength) > payload.Len()-4 { + return flowRecord, errors.New(fmt.Sprintf("Invalid AS path length: %v.", extendedGateway.ASPathLength)) + } + asPath = make([]uint32, extendedGateway.ASPathLength) + if len(asPath) > 0 { + err = utils.BinaryDecoder(payload, asPath) + if err != nil { + return flowRecord, err + } + } + } + extendedGateway.ASPath = asPath + + err = utils.BinaryDecoder(payload, &(extendedGateway.CommunitiesLength)) + if err != nil { + return flowRecord, err + } + if int(extendedGateway.CommunitiesLength) > payload.Len()-4 { + return flowRecord, errors.New(fmt.Sprintf("Invalid Communities length: %v.", extendedGateway.ASPathLength)) + } + communities := make([]uint32, extendedGateway.CommunitiesLength) + if len(communities) > 0 { + err = utils.BinaryDecoder(payload, communities) + if err != nil { + return flowRecord, err + } + } + err = utils.BinaryDecoder(payload, &(extendedGateway.LocalPref)) + if err != nil { + return flowRecord, err + } + extendedGateway.Communities = communities + + flowRecord.Data = extendedGateway + default: + //return flowRecord, errors.New(fmt.Sprintf("Unknown data format %v.", (*header).DataFormat)) + flowRecord.Data = &FlowRecordRaw{ + Data: payload.Next(int(header.Length)), + } + } + return flowRecord, nil +} + +func DecodeSample(header *SampleHeader, payload *bytes.Buffer) (interface{}, error) { + format := (*header).Format + var sample interface{} + + err := utils.BinaryDecoder(payload, &((*header).SampleSequenceNumber)) + if err != nil { + return sample, err + } + if format == FORMAT_RAW_PKT || format == FORMAT_ETH { + var sourceId uint32 + err = utils.BinaryDecoder(payload, &sourceId) + if err != nil { + return sample, err + } + + (*header).SourceIdType = sourceId >> 24 + (*header).SourceIdValue = sourceId & 0x00ffffff + } else if format == FORMAT_IPV4 || format == FORMAT_IPV6 { + err = utils.BinaryDecoder(payload, &((*header).SourceIdType), &((*header).SourceIdValue)) + if err != nil { + return sample, err + } + } else { + return nil, NewErrorDataFormat(format) + } + + var recordsCount uint32 + var flowSample FlowSample + var counterSample CounterSample + var expandedFlowSample ExpandedFlowSample + if format == FORMAT_RAW_PKT { + flowSample = FlowSample{ + Header: *header, + } + err = utils.BinaryDecoder(payload, &(flowSample.SamplingRate), &(flowSample.SamplePool), + &(flowSample.Drops), &(flowSample.Input), &(flowSample.Output), &(flowSample.FlowRecordsCount)) + if err != nil { + return sample, err + } + recordsCount = flowSample.FlowRecordsCount + flowSample.Records = make([]FlowRecord, recordsCount) + sample = flowSample + } else if format == FORMAT_ETH || format == FORMAT_IPV6 { + err = utils.BinaryDecoder(payload, &recordsCount) + if err != nil { + return sample, err + } + counterSample = CounterSample{ + Header: *header, + CounterRecordsCount: recordsCount, + } + counterSample.Records = make([]CounterRecord, recordsCount) + sample = counterSample + } else if format == FORMAT_IPV4 { + expandedFlowSample = ExpandedFlowSample{ + Header: *header, + } + err = utils.BinaryDecoder(payload, &(expandedFlowSample.SamplingRate), &(expandedFlowSample.SamplePool), + &(expandedFlowSample.Drops), &(expandedFlowSample.InputIfFormat), &(expandedFlowSample.InputIfValue), + &(expandedFlowSample.OutputIfFormat), &(expandedFlowSample.OutputIfValue), &(expandedFlowSample.FlowRecordsCount)) + if err != nil { + return sample, err + } + recordsCount = expandedFlowSample.FlowRecordsCount + expandedFlowSample.Records = make([]FlowRecord, recordsCount) + sample = expandedFlowSample + } + for i := 0; i < int(recordsCount) && payload.Len() >= 8; i++ { + recordHeader := RecordHeader{} + err = utils.BinaryDecoder(payload, &(recordHeader.DataFormat), &(recordHeader.Length)) + if err != nil { + return sample, err + } + if int(recordHeader.Length) > payload.Len() { + break + } + recordReader := bytes.NewBuffer(payload.Next(int(recordHeader.Length))) + if format == FORMAT_RAW_PKT || format == FORMAT_IPV4 { + record, err := DecodeFlowRecord(&recordHeader, recordReader) + if err != nil { + continue + } + if format == FORMAT_RAW_PKT { + flowSample.Records[i] = record + } else if format == FORMAT_IPV4 { + expandedFlowSample.Records[i] = record + } + } else if format == FORMAT_ETH || format == FORMAT_IPV6 { + record, err := DecodeCounterRecord(&recordHeader, recordReader) + if err != nil { + continue + } + counterSample.Records[i] = record + } + } + return sample, nil +} + +func DecodeMessage(payload *bytes.Buffer) (interface{}, error) { + var version uint32 + err := utils.BinaryDecoder(payload, &version) + if err != nil { + return nil, err + } + packetV5 := Packet{} + if version == 5 { + packetV5.Version = version + err = utils.BinaryDecoder(payload, &(packetV5.IPVersion)) + if err != nil { + return packetV5, err + } + var ip []byte + if packetV5.IPVersion == 1 { + ip = make([]byte, 4) + err = utils.BinaryDecoder(payload, ip) + if err != nil { + return packetV5, err + } + } else if packetV5.IPVersion == 2 { + ip = make([]byte, 16) + err = utils.BinaryDecoder(payload, ip) + if err != nil { + return packetV5, err + } + } else { + return nil, NewErrorIPVersion(packetV5.IPVersion) + } + + packetV5.AgentIP = ip + err = utils.BinaryDecoder(payload, &(packetV5.SubAgentId), &(packetV5.SequenceNumber), &(packetV5.Uptime), &(packetV5.SamplesCount)) + if err != nil { + return packetV5, err + } + packetV5.Samples = make([]interface{}, int(packetV5.SamplesCount)) + for i := 0; i < int(packetV5.SamplesCount) && payload.Len() >= 8; i++ { + header := SampleHeader{} + err = utils.BinaryDecoder(payload, &(header.Format), &(header.Length)) + if err != nil { + return packetV5, err + } + if int(header.Length) > payload.Len() { + break + } + sampleReader := bytes.NewBuffer(payload.Next(int(header.Length))) + + sample, err := DecodeSample(&header, sampleReader) + if err != nil { + continue + } else { + packetV5.Samples[i] = sample + } + } + + return packetV5, nil + } else { + return nil, NewErrorVersion(version) + } +} diff --git a/third_party/goflow2/decoders/sflow/sflow_test.go b/third_party/goflow2/decoders/sflow/sflow_test.go new file mode 100644 index 000000000000..e9ed52c8771c --- /dev/null +++ b/third_party/goflow2/decoders/sflow/sflow_test.go @@ -0,0 +1,134 @@ +package sflow + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSFlowDecode(t *testing.T) { + data := []byte{ + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0xac, 0x10, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0xaa, 0x67, 0xee, 0xaa, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x13, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xaa, 0x00, 0x00, 0x04, 0x13, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4e, 0x00, 0xff, 0x12, 0x34, + 0x35, 0x1b, 0xff, 0xab, 0xcd, 0xef, 0xab, 0x64, 0x81, 0x00, 0x00, 0x20, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x3c, 0x5c, 0x07, 0x00, 0x00, 0x7c, 0x01, 0x48, 0xa0, 0xac, 0x10, 0x20, 0xfe, 0xac, 0x10, + 0x20, 0xf1, 0x08, 0x00, 0x97, 0x61, 0xa9, 0x48, 0x0c, 0xb2, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x00, 0x00, + } + buf := bytes.NewBuffer(data) + _, err := DecodeMessage(buf) + assert.Nil(t, err) +} + +func TestExpandedSFlowDecode(t *testing.T) { + data := getExpandedSFlowDecode() + + buf := bytes.NewBuffer(data) + _, err := DecodeMessage(buf) + assert.Nil(t, err) +} + +func getExpandedSFlowDecode() []byte { + return []byte{ + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xa7, 0x72, 0xc2, 0x0f, 0x76, 0x73, 0x48, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0xdc, 0x20, 0x90, 0x93, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0xa4, + 0x00, 0x00, 0x3f, 0xff, 0x04, 0x38, 0xec, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x42, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x52, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xea, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, + 0x08, 0xec, 0xf5, 0x2a, 0x8f, 0xbe, 0x74, 0x83, 0xef, 0x30, 0x65, 0xb7, 0x81, 0x00, 0x00, 0x1e, + 0x08, 0x00, 0x45, 0x00, 0x05, 0xd4, 0x3b, 0xba, 0x40, 0x00, 0x3f, 0x06, 0xbd, 0x99, 0xb9, 0x3b, + 0xdc, 0x93, 0x58, 0xee, 0x4e, 0x13, 0x01, 0xbb, 0xcf, 0xd6, 0x45, 0xb7, 0x1b, 0xc0, 0xd5, 0xb8, + 0xff, 0x24, 0x80, 0x10, 0x00, 0x04, 0x01, 0x55, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0xc8, 0xc8, + 0x56, 0x95, 0x00, 0x34, 0xf6, 0x0f, 0xe8, 0x1d, 0xbd, 0x41, 0x45, 0x92, 0x4c, 0xc2, 0x71, 0xe0, + 0xeb, 0x2e, 0x35, 0x17, 0x7c, 0x2f, 0xb9, 0xa8, 0x05, 0x92, 0x0e, 0x03, 0x1b, 0x50, 0x53, 0x0c, + 0xe5, 0x7d, 0x86, 0x75, 0x32, 0x8a, 0xcc, 0xe2, 0x26, 0xa8, 0x90, 0x21, 0x78, 0xbf, 0xce, 0x7a, + 0xf8, 0xb5, 0x8d, 0x48, 0xe4, 0xaa, 0xfe, 0x26, 0x34, 0xe0, 0xad, 0xb9, 0xec, 0x79, 0x74, 0xd8, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xdc, 0x20, 0x90, 0x93, 0x27, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x42, 0xa4, 0x00, 0x00, 0x3f, 0xff, 0x04, 0x39, 0x2c, 0xd9, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x4b, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xca, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x80, 0xda, 0xb1, 0x22, 0xfb, 0xd9, 0xcf, 0x74, 0x83, 0xef, 0x30, 0x65, 0xb7, + 0x81, 0x00, 0x00, 0x17, 0x08, 0x00, 0x45, 0x00, 0x05, 0xb4, 0xe2, 0x28, 0x40, 0x00, 0x3f, 0x06, + 0x15, 0x0f, 0xc3, 0xb5, 0xaf, 0x26, 0x05, 0x92, 0xc6, 0x9e, 0x00, 0x50, 0x0f, 0xb3, 0x35, 0x8e, + 0x36, 0x02, 0xa1, 0x01, 0xed, 0xb0, 0x80, 0x10, 0x00, 0x3b, 0xf7, 0xd4, 0x00, 0x00, 0x01, 0x01, + 0x08, 0x0a, 0xd2, 0xe8, 0xac, 0xbe, 0x00, 0x36, 0xbc, 0x3c, 0x37, 0x36, 0xc4, 0x80, 0x3f, 0x66, + 0x33, 0xc5, 0x50, 0xa6, 0x63, 0xb2, 0x92, 0xc3, 0x6a, 0x7a, 0x80, 0x65, 0x0b, 0x22, 0x62, 0xfe, + 0x16, 0x9c, 0xab, 0x55, 0x03, 0x47, 0xa6, 0x54, 0x63, 0xa5, 0xbc, 0x17, 0x8e, 0x5a, 0xf6, 0xbc, + 0x24, 0x52, 0xe9, 0xd2, 0x7b, 0x08, 0xe8, 0xc2, 0x6b, 0x05, 0x1c, 0xc0, 0x61, 0xb4, 0xe0, 0x43, + 0x59, 0x62, 0xbf, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xdc, 0x04, 0x12, 0xa0, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0xa8, 0x00, 0x00, 0x3f, 0xff, 0xa4, 0x06, 0x9f, 0x9b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0xa8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x42, 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x05, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x39, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xf2, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x74, 0x83, 0xef, 0x30, 0x65, 0xb7, 0x28, 0x99, + 0x3a, 0x4e, 0x89, 0x27, 0x81, 0x00, 0x05, 0x39, 0x08, 0x00, 0x45, 0x18, 0x05, 0xdc, 0x8e, 0x5c, + 0x40, 0x00, 0x3a, 0x06, 0x53, 0x77, 0x89, 0x4a, 0xcc, 0xd5, 0x59, 0xbb, 0xa9, 0x55, 0x07, 0x8f, + 0xad, 0xdc, 0xf2, 0x9b, 0x09, 0xb4, 0xce, 0x1d, 0xbc, 0xee, 0x80, 0x10, 0x75, 0x40, 0x58, 0x02, + 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0xb0, 0x18, 0x5b, 0x6f, 0xd7, 0xd6, 0x8b, 0x47, 0xee, 0x6a, + 0x03, 0x0b, 0x9b, 0x52, 0xb1, 0xca, 0x61, 0x4b, 0x84, 0x57, 0x75, 0xc4, 0xb2, 0x18, 0x11, 0x39, + 0xce, 0x5d, 0x2a, 0x38, 0x91, 0x29, 0x76, 0x11, 0x7d, 0xc1, 0xcc, 0x5c, 0x4b, 0x0a, 0xde, 0xbb, + 0xa8, 0xad, 0x9d, 0x88, 0x36, 0x8b, 0xc0, 0x02, 0x87, 0xa7, 0xa5, 0x1c, 0xd9, 0x85, 0x71, 0x85, + 0x68, 0x2b, 0x59, 0xc6, 0x2c, 0x3c, 0x84, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xdc, + 0x20, 0x90, 0x93, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0xa4, 0x00, 0x00, 0x3f, 0xff, + 0x04, 0x39, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0xa4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0xe9, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x05, 0xf2, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0xda, 0xb1, 0x22, 0xfb, + 0xd9, 0xcf, 0x74, 0x83, 0xef, 0x30, 0x65, 0xb7, 0x81, 0x00, 0x00, 0x17, 0x08, 0x00, 0x45, 0x00, + 0x05, 0xdc, 0x7e, 0x42, 0x40, 0x00, 0x3f, 0x06, 0x12, 0x4d, 0xb9, 0x66, 0xdb, 0x43, 0x67, 0xc2, + 0xa9, 0x20, 0x63, 0x75, 0x57, 0xae, 0x6d, 0xbf, 0x59, 0x7c, 0x93, 0x71, 0x09, 0x67, 0x80, 0x10, + 0x00, 0xeb, 0xfc, 0x16, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x40, 0x96, 0x88, 0x38, 0x36, 0xe1, + 0x64, 0xc7, 0x1b, 0x43, 0xbc, 0x0e, 0x1f, 0x81, 0x6d, 0x39, 0xf6, 0x12, 0x0c, 0xea, 0xc0, 0xea, + 0x7b, 0xc1, 0x77, 0xe2, 0x92, 0x6a, 0xbf, 0xbe, 0x84, 0xd9, 0x00, 0x18, 0x57, 0x49, 0x92, 0x72, + 0x8f, 0xa3, 0x78, 0x45, 0x6f, 0xc6, 0x98, 0x8f, 0x71, 0xb0, 0xc5, 0x52, 0x7d, 0x8a, 0x82, 0xef, + 0x52, 0xdb, 0xe9, 0xdc, 0x0a, 0x52, 0xdb, 0x06, 0x51, 0x80, 0x80, 0xa9, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0xdc, 0x20, 0x90, 0x93, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0xa4, + 0x00, 0x00, 0x3f, 0xff, 0x04, 0x39, 0xac, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x42, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0xa5, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x03, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xf2, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, + 0x90, 0xe2, 0xba, 0x89, 0x21, 0xad, 0x74, 0x83, 0xef, 0x30, 0x65, 0xb7, 0x81, 0x00, 0x03, 0xbd, + 0x08, 0x00, 0x45, 0x00, 0x05, 0xdc, 0x76, 0xa2, 0x40, 0x00, 0x38, 0x06, 0xac, 0x75, 0x33, 0x5b, + 0x74, 0x6c, 0xc3, 0xb5, 0xae, 0x87, 0x1f, 0x40, 0x80, 0x68, 0xab, 0xbb, 0x2f, 0x90, 0x01, 0xee, + 0x3a, 0xaf, 0x80, 0x10, 0x00, 0xeb, 0x8e, 0xf4, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x34, 0xc0, + 0xff, 0x26, 0xac, 0x90, 0xd5, 0xc4, 0xcc, 0xd7, 0xa4, 0xa5, 0x5b, 0xa3, 0x79, 0x33, 0xc1, 0x25, + 0xcd, 0x84, 0xdc, 0xaa, 0x37, 0xc9, 0xe3, 0xab, 0xc6, 0xb4, 0xeb, 0xe3, 0x8d, 0x72, 0x06, 0xd1, + 0x5a, 0x1f, 0x9a, 0x8b, 0xe9, 0x9a, 0xf7, 0x33, 0x35, 0xe5, 0xca, 0x67, 0xba, 0x04, 0xf9, 0x3c, + 0x27, 0xff, 0xa3, 0xca, 0x5e, 0x90, 0xf9, 0xc7, 0xd1, 0xe4, 0xf8, 0xf5, 0x7a, 0x14, 0xdc, 0x1c, + 0xb1, 0xde, 0x63, 0x75, 0xb2, 0x65, 0x27, 0xf0, 0x0d, 0x29, 0xc5, 0x56, 0x60, 0x4a, 0x50, 0x10, + 0x00, 0x77, 0xc0, 0xef, 0x00, 0x00, 0x74, 0xcf, 0x8a, 0x79, 0x87, 0x77, 0x75, 0x64, 0x75, 0xeb, + 0xa4, 0x56, 0xb4, 0xd8, 0x70, 0xca, 0xe6, 0x11, 0xbb, 0x9f, 0xa1, 0x63, 0x95, 0xa1, 0xb4, 0x81, + 0x8d, 0x50, 0xe0, 0xd5, 0xa9, 0x2c, 0xd7, 0x8f, 0xfe, 0x78, 0xce, 0xff, 0x5a, 0xa6, 0xb6, 0xb9, + 0xf1, 0xe9, 0x5f, 0xda, 0xcb, 0xf3, 0x62, 0x61, 0x5f, 0x2b, 0x32, 0x95, 0x5d, 0x96, 0x2e, 0xef, + 0x32, 0x04, 0xff, 0xcc, 0x76, 0xba, 0x49, 0xab, 0x92, 0xa7, 0xf1, 0xcc, 0x52, 0x68, 0xde, 0x94, + 0x90, 0xdb, 0x1b, 0xa0, 0x28, 0x8a, 0xf8, 0x64, 0x55, 0x9c, 0x9b, 0xf6, 0x9c, 0x44, 0xd9, 0x68, + 0xc0, 0xe5, 0x2c, 0xe1, 0x3d, 0x29, 0x19, 0xef, 0x8b, 0x0c, 0x9d, 0x0a, 0x7e, 0xcd, 0xc2, 0xe9, + 0x85, 0x6b, 0x85, 0xb3, 0x97, 0xbe, 0xc6, 0x26, 0xd2, 0xe5, 0x2e, 0x90, 0xa9, 0xac, 0xe3, 0xd8, + 0xef, 0xbd, 0x7b, 0x40, 0xf8, 0xb7, 0xe3, 0xc3, 0x8d, 0xa7, 0x38, 0x0f, 0x87, 0x7a, 0x50, 0x62, + 0xc8, 0xb8, 0xa4, 0x52, 0x6e, 0xdc, 0x92, 0x7f, 0xe6, 0x8d, 0x45, 0x39, 0xfd, 0x06, 0x6e, 0xd9, + 0xb5, 0x65, 0xac, 0xae, 0x2b, 0x8d, 0xea, 0xcf, 0xa2, 0x98, 0x0b, 0xc6, 0x43, 0x2e, 0xa7, 0x71, + 0x99, 0x2b, 0xea, 0xc3, 0x9c, 0x27, 0x74, 0x9e, 0xd5, 0x11, 0x60, 0x7a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7b, 0xd6, 0x2a, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } +} diff --git a/third_party/goflow2/decoders/utils/utils.go b/third_party/goflow2/decoders/utils/utils.go new file mode 100644 index 000000000000..a36e3b2be04d --- /dev/null +++ b/third_party/goflow2/decoders/utils/utils.go @@ -0,0 +1,16 @@ +package utils + +import ( + "encoding/binary" + "io" +) + +func BinaryDecoder(payload io.Reader, dests ...interface{}) error { + for _, dest := range dests { + err := binary.Read(payload, binary.BigEndian, dest) + if err != nil { + return err + } + } + return nil +} diff --git a/third_party/goflow2/docs/agents.md b/third_party/goflow2/docs/agents.md new file mode 100644 index 000000000000..2f254f4a7dd0 --- /dev/null +++ b/third_party/goflow2/docs/agents.md @@ -0,0 +1,51 @@ +# Agents + +There are various agents that can send samples to a flow collector. + +## Hardware + +### Juniper + +In the latest versions, Juniper supports sFlow and IPFIX protocols. + +[Documentation](https://www.juniper.net/documentation/us/en/software/junos/network-mgmt/topics/topic-map/sflow-monitoring-technology.html). + +Sample configuration: +``` +set protocols sflow collector 10.0.0.1 +set protocols sflow collector udp-port 6343 +set protocols sflow interface ge-0/0/0 +set protocols sflow sample-rate 2048 +``` + +## Software + +### hsflowd + +[Documentation](https://sflow.net/host-sflow-linux-config.php). + +Sample packets using pcap, iptables nflog and many more. Uses sFlow. + +Sample configuration: +``` +sflow { + collector { ip = 10.0.0.1 udpport = 6343 } + pcap { dev = eth0 } +} +``` + +Run with +```bash +$ hsflowd -d -f hsflowd.conf +``` + +### nProbe + +[Documentation](https://www.ntop.org/guides/nprobe/) + +Sample packets using pcap, iptables nflog and many more. Uses NetFlow v9 or IPFIX. + +Run with +```bash +$ nprobe -i eth0 -n 10.0.0.1:2055 -V 10 +``` \ No newline at end of file diff --git a/third_party/goflow2/docs/contributors.md b/third_party/goflow2/docs/contributors.md new file mode 100644 index 000000000000..90922e5a10a5 --- /dev/null +++ b/third_party/goflow2/docs/contributors.md @@ -0,0 +1,13 @@ +# Contributors + +A special thank you to all the contributors of GoFlow. +* [debugloop](https://github.com/debugloop) +* [simPod](https://github.com/simPod) +* [mmlb](https://github.com/mmlb) +* [kanocz](https://github.com/kanocz) +* [morrowc](https://github.com/morrowc) +* [SuperQ](https://github.com/SuperQ) +* [shyam334](https://github.com/shyam334) +* [leoluk](https://github.com/leoluk) + +and many more! \ No newline at end of file diff --git a/third_party/goflow2/docs/logs.md b/third_party/goflow2/docs/logs.md new file mode 100644 index 000000000000..ec55956619af --- /dev/null +++ b/third_party/goflow2/docs/logs.md @@ -0,0 +1,2 @@ +# Logs + diff --git a/third_party/goflow2/docs/protobuf.md b/third_party/goflow2/docs/protobuf.md new file mode 100644 index 000000000000..9617f7fd7e30 --- /dev/null +++ b/third_party/goflow2/docs/protobuf.md @@ -0,0 +1,34 @@ +# Protobuf + +The `.proto` files contains a list of fields that are populated by GoFlow2. + +If the fields are changed, the schema needs to be recompiled +in order to use it. + +The compilation is dependent on the language. +Keep in mind the protobuf source code and libraries changes often and this page may be outdated. + +For other languages, refer to the [official guide](https://developers.google.com/protocol-buffers). + +## Compile for Golang + +The following two tools are required: +* [protoc](https://github.com/protocolbuffers/protobuf), a protobuf compiler, written in C +* [protoc-gen-go](https://github.com/protocolbuffers/protobuf-go), a Go plugin for protoc that can compile protobuf for Golang + +The release page in the respective GitHub repositories should provide binaries distributions. Unzip/Untar if necessary. +Make sure that the two binaries are in your ``$PATH``. On Mac OS you can add the files to `/usr/local/bin` for instance. + +From the root of the repository, run the following command: + +```bash +$ protoc --go_opt=paths=source_relative --go_out=. pb/*.proto +``` + +This will compile the main protobuf schema into the `pb` directory. + +You can also run the command which will also compile the protobuf for the sample enricher. + +```bash +$ make proto +``` \ No newline at end of file diff --git a/third_party/goflow2/docs/protocols.md b/third_party/goflow2/docs/protocols.md new file mode 100644 index 000000000000..f27076d50fe9 --- /dev/null +++ b/third_party/goflow2/docs/protocols.md @@ -0,0 +1,111 @@ +# Protocols + +You can find information on the protocols in the links below: +* [sFlow](https://sflow.org/developers/specifications.php) +* [NetFlow v5](https://www.cisco.com/c/en/us/td/docs/net_mgmt/netflow_collection_engine/3-6/user/guide/format.html) +* [NetFlow v9](https://www.cisco.com/en/US/technologies/tk648/tk362/technologies_white_paper09186a00800a3db9.html) +* [IPFIX](https://www.iana.org/assignments/ipfix/ipfix.xhtml) + +The mapping to the protobuf format is listed in the table below. + +| Field | Description | NetFlow v5 | sFlow | NetFlow v9 | IPFIX | +| - | - | - | - | - | - | +|Type|Type of flow message|NETFLOW_V5|SFLOW_5|NETFLOW_V9|IPFIX| +|TimeReceived|Timestamp of when the message was received|Included|Included|Included|Included| +|SequenceNum|Sequence number of the flow packet|Included|Included|Included|Included| +|SamplingRate|Sampling rate of the flow|Included|Included|Included|Included| +|FlowDirection|Direction of the flow| | |DIRECTION (61)|flowDirection (61)| +|SamplerAddress|Address of the device that generated the packet|IP source of packet|Agent IP|IP source of packet|IP source of packet| +|TimeFlowStart|Time the flow started|System uptime and first|=TimeReceived|System uptime and FIRST_SWITCHED (22)|flowStartXXX (150, 152, 154, 156)| +|TimeFlowEnd|Time the flow ended|System uptime and last|=TimeReceived|System uptime and LAST_SWITCHED (23)|flowEndXXX (151, 153, 155, 157)| +|Bytes|Number of bytes in flow|dOctets|Length of sample|IN_BYTES (1) OUT_BYTES (23)|octetDeltaCount (1) postOctetDeltaCount (23)| +|Packets|Number of packets in flow|dPkts|=1|IN_PKTS (2) OUT_PKTS (24)|packetDeltaCount (1) postPacketDeltaCount (24)| +|SrcAddr|Source address (IP)|srcaddr (IPv4 only)|Included|Included|IPV4_SRC_ADDR (8) IPV6_SRC_ADDR (27)|sourceIPv4Address/sourceIPv6Address (8/27)| +|DstAddr|Destination address (IP)|dstaddr (IPv4 only)|Included|Included|IPV4_DST_ADDR (12) IPV6_DST_ADDR (28)|destinationIPv4Address (12)destinationIPv6Address (28)| +|Etype|Ethernet type (0x86dd for IPv6...)|IPv4|Included|Included|Included| +|Proto|Protocol (UDP, TCP, ICMP...)|prot|Included|PROTOCOL (4)|protocolIdentifier (4)| +|SrcPort|Source port (when UDP/TCP/SCTP)|srcport|Included|L4_SRC_PORT (7)|sourceTransportPort (7)| +|DstPort|Destination port (when UDP/TCP/SCTP)|dstport|Included|L4_DST_PORT (11)|destinationTransportPort (11)| +|InIf|Input interface|input|Included|INPUT_SNMP (10)|ingressInterface (10)| +|OutIf|Output interface|output|Included|OUTPUT_SNMP (14)|egressInterface (14)| +|SrcMac|Source mac address| |Included|IN_SRC_MAC (56)|sourceMacAddress (56)| +|DstMac|Destination mac address| |Included|OUT_DST_MAC (57)|postDestinationMacAddress (57)| +|SrcVlan|Source VLAN ID| |From ExtendedSwitch|SRC_VLAN (58)|vlanId (58)| +|DstVlan|Destination VLAN ID| |From ExtendedSwitch|DST_VLAN (59)|postVlanId (59)| +|VlanId|802.11q VLAN ID| |Included|SRC_VLAN (58)|vlanId (58)| +|IngressVrfID|VRF ID| | | |ingressVRFID (234)| +|EgressVrfID|VRF ID| | | |egressVRFID (235)| +|IPTos|IP Type of Service|tos|Included|SRC_TOS (5)|ipClassOfService (5)| +|ForwardingStatus|Forwarding status| | |FORWARDING_STATUS (89)|forwardingStatus (89)| +|IPTTL|IP Time to Live| |Included|IPTTL (52)|minimumTTL (52| +|TCPFlags|TCP flags|tcp_flags|Included|TCP_FLAGS (6)|tcpControlBits (6)| +|IcmpType|ICMP Type| |Included|ICMP_TYPE (32)|icmpTypeXXX (176, 178) icmpTypeCodeXXX (32, 139)| +|IcmpCode|ICMP Code| |Included|ICMP_TYPE (32)|icmpCodeXXX (177, 179) icmpTypeCodeXXX (32, 139)| +|IPv6FlowLabel|IPv6 Flow Label| |Included|IPV6_FLOW_LABEL (31)|flowLabelIPv6 (31)| +|FragmentId|IP Fragment ID| |Included|IPV4_IDENT (54)|fragmentIdentification (54)| +|FragmentOffset|IP Fragment Offset| |Included|FRAGMENT_OFFSET (88)|fragmentOffset (88) and fragmentFlags (197)| +|BiFlowDirection|BiFlow Identification| | | |biflowDirection (239)| +|SrcAS|Source AS number|src_as|From ExtendedGateway|SRC_AS (16)|bgpSourceAsNumber (16)| +|DstAS|Destination AS number|dst_as|From ExtendedGateway|DST_AS (17)|bgpDestinationAsNumber (17)| +|NextHop|Nexthop address|nexthop|From ExtendedRouter|IPV4_NEXT_HOP (15) IPV6_NEXT_HOP (62)|ipNextHopIPv4Address (15) ipNextHopIPv6Address (62)| +|NextHopAS|Nexthop AS number| |From ExtendedGateway| | | +|SrcNet|Source address mask|src_mask|From ExtendedRouter|SRC_MASK (9) IPV6_SRC_MASK (29)|sourceIPv4PrefixLength (9) sourceIPv6PrefixLength (29)| +|DstNet|Destination address mask|dst_mask|From ExtendedRouter|DST_MASK (13) IPV6_DST_MASK (30)|destinationIPv4PrefixLength (13) destinationIPv6PrefixLength (30)| +|BgpNextHop|BGP Nexthop address| |From ExtendedGateway|BGP_IPV4_NEXT_HOP (18) BGP_IPV6_NEXT_HOP (63)|bgpNextHopIPv4Address (18) bgpNextHopIPv6Address (63)| +|BgpCommunities|BGP Communities| |From ExtendedGateway| | | +|ASPath|AS Path| |From ExtendedGateway| | | +|SrcNet|Source address mask|src_mask|From ExtendedRouter|SRC_MASK (9) IPV6_SRC_MASK (29)|sourceIPv4PrefixLength (9) sourceIPv6PrefixLength (29)| +|DstNet|Destination address mask|dst_mask|From ExtendedRouter|DST_MASK (13) IPV6_DST_MASK (30)|destinationIPv4PrefixLength (13) destinationIPv6PrefixLength (30)| +|HasMPLS|Indicates the presence of MPLS header||Included||| +|MPLSCount|Count of MPLS layers||Included||| +|MPLSxTTL|TTL of the MPLS label||Included||| +|MPLSxLabel|MPLS label||Included||| + +## Add new custom fields + +If you are using enterprise fields that you need decoded +or if you are looking for specific bytes inside the packet sample. + +This feature is only available when sending Protobufs (no text output). + +The [`mapping.yaml`](../cmd/goflow2/mapping.yaml) example file +will collect source and destination port again, use it with `-mapping=mapping.yaml` in the CLI. + +Data coming from the flows can be added to the protobuf either as an unsigned/signed integer a slice of bytes. + +The `sflow` section allow to extract data from packet samples inside sFlow and inside IPFIX (dataframe). +The following layers are available: +* 0: no offset +* 3: network layer, offsets to IP/IPv6 header +* 4: transport layer, offsets to TCP/UDP header +* 7: application layer, offsets to the TCP/UDP payload + + +```yaml +ipfix: + mapping: + - field: 7 # NetFlow or IPFIX field ID + destination: CustomInteger1 # Name of the field inside the Protobuf + penprovided: false # Has an enterprise number (optional) + pen: 0 # Enterprise number (optional) +netflowv9: + mapping: [] + # ... similar to above, Enterprise number will not be supported +sflow: + mapping: + - layer: 4 # Layer + offset: 0 # Source port + length: 16 # 2 bytes + destination: CustomInteger1 +``` + +Without editing and recompiling the [protobuf](../pb/flow.proto), you can use up to 5 integers and 5 slices of bytes: + +```protobuf + // Custom allocations + uint64 CustomInteger1 = 1001; + [...] + + bytes CustomBytes1 = 1011; + [...] +``` diff --git a/third_party/goflow2/format/common/hash.go b/third_party/goflow2/format/common/hash.go new file mode 100644 index 000000000000..1d901860350b --- /dev/null +++ b/third_party/goflow2/format/common/hash.go @@ -0,0 +1,56 @@ +package common + +import ( + "flag" + "fmt" + "reflect" + "strings" + "sync" +) + +var ( + fieldsVar string + fields []string // Hashing fields + + hashDeclared bool + hashDeclaredLock = &sync.Mutex{} +) + +func HashFlag() { + hashDeclaredLock.Lock() + defer hashDeclaredLock.Unlock() + + if hashDeclared { + return + } + hashDeclared = true + flag.StringVar(&fieldsVar, "format.hash", "SamplerAddress", "List of fields to do hashing, separated by commas") + +} + +func ManualHashInit() error { + fields = strings.Split(fieldsVar, ",") + return nil +} + +func HashProtoLocal(msg interface{}) string { + return HashProto(fields, msg) +} + +func HashProto(fields []string, msg interface{}) string { + var keyStr string + + if msg != nil { + vfm := reflect.ValueOf(msg) + vfm = reflect.Indirect(vfm) + + for _, kf := range fields { + fieldValue := vfm.FieldByName(kf) + if fieldValue.IsValid() { + keyStr += fmt.Sprintf("%v-", fieldValue) + } + } + } + + return keyStr +} diff --git a/third_party/goflow2/format/common/selector.go b/third_party/goflow2/format/common/selector.go new file mode 100644 index 000000000000..531c716feff3 --- /dev/null +++ b/third_party/goflow2/format/common/selector.go @@ -0,0 +1,36 @@ +package common + +import ( + "flag" + "strings" + "sync" +) + +var ( + selectorVar string + selector []string // Hashing fields + selectorTag string // Hashing fields + + selectorDeclared bool + selectorDeclaredLock = &sync.Mutex{} +) + +func SelectorFlag() { + selectorDeclaredLock.Lock() + defer selectorDeclaredLock.Unlock() + + if selectorDeclared { + return + } + selectorDeclared = true + flag.StringVar(&selectorVar, "format.selector", "", "List of fields to do keep in output") + flag.StringVar(&selectorTag, "format.tag", "", "Use format tag") +} + +func ManualSelectorInit() error { + if selectorVar == "" { + return nil + } + selector = strings.Split(selectorVar, ",") + return nil +} diff --git a/third_party/goflow2/format/common/text.go b/third_party/goflow2/format/common/text.go new file mode 100644 index 000000000000..d97ed68294e8 --- /dev/null +++ b/third_party/goflow2/format/common/text.go @@ -0,0 +1,246 @@ +package common + +import ( + "encoding/binary" + "fmt" + "net" + "reflect" + "strings" +) + +const ( + FORMAT_TYPE_UNKNOWN = iota + FORMAT_TYPE_STRING_FUNC + FORMAT_TYPE_STRING + FORMAT_TYPE_INTEGER + FORMAT_TYPE_IP + FORMAT_TYPE_MAC + FORMAT_TYPE_BYTES +) + +var ( + EtypeName = map[uint32]string{ + 0x806: "ARP", + 0x800: "IPv4", + 0x86dd: "IPv6", + } + ProtoName = map[uint32]string{ + 1: "ICMP", + 6: "TCP", + 17: "UDP", + 58: "ICMPv6", + 132: "SCTP", + } + IcmpTypeName = map[uint32]string{ + 0: "EchoReply", + 3: "DestinationUnreachable", + 8: "Echo", + 9: "RouterAdvertisement", + 10: "RouterSolicitation", + 11: "TimeExceeded", + } + Icmp6TypeName = map[uint32]string{ + 1: "DestinationUnreachable", + 2: "PacketTooBig", + 3: "TimeExceeded", + 128: "EchoRequest", + 129: "EchoReply", + 133: "RouterSolicitation", + 134: "RouterAdvertisement", + } + + TextFields = map[string]int{ + "Type": FORMAT_TYPE_STRING_FUNC, + "SamplerAddress": FORMAT_TYPE_IP, + "SrcAddr": FORMAT_TYPE_IP, + "DstAddr": FORMAT_TYPE_IP, + "SrcMac": FORMAT_TYPE_MAC, + "DstMac": FORMAT_TYPE_MAC, + "NextHop": FORMAT_TYPE_IP, + "MPLSLabelIP": FORMAT_TYPE_IP, + } + + RenderExtras = map[string]RenderExtraFunction{ + "EtypeName": RenderExtraFunctionEtypeName, + "ProtoName": RenderExtraFunctionProtoName, + "IcmpName": RenderExtraFunctionIcmpName, + } +) + +/* +func AddTextField(name string, jtype int) { + TextFields = append(TextFields, name) + TextFieldsTypes = append(TextFieldsTypes, jtype) +}*/ + +type RenderExtraFunction func(interface{}) string + +func RenderExtraFetchNumbers(msg interface{}, fields []string) []uint64 { + vfm := reflect.ValueOf(msg) + vfm = reflect.Indirect(vfm) + + values := make([]uint64, len(fields)) + for i, kf := range fields { + fieldValue := vfm.FieldByName(kf) + if fieldValue.IsValid() { + values[i] = fieldValue.Uint() + } + } + + return values +} + +func RenderExtraFunctionEtypeName(msg interface{}) string { + num := RenderExtraFetchNumbers(msg, []string{"Etype"}) + return EtypeName[uint32(num[0])] +} + +func RenderExtraFunctionProtoName(msg interface{}) string { + num := RenderExtraFetchNumbers(msg, []string{"Proto"}) + return ProtoName[uint32(num[0])] +} +func RenderExtraFunctionIcmpName(msg interface{}) string { + num := RenderExtraFetchNumbers(msg, []string{"Proto", "IcmpCode", "IcmpType"}) + return IcmpCodeType(uint32(num[0]), uint32(num[1]), uint32(num[2])) +} + +func IcmpCodeType(proto, icmpCode, icmpType uint32) string { + if proto == 1 { + return IcmpTypeName[icmpType] + } else if proto == 58 { + return Icmp6TypeName[icmpType] + } + return "" +} + +func RenderIP(addr []byte) string { + if addr == nil || (len(addr) != 4 && len(addr) != 16) { + return "" + } + + return net.IP(addr).String() +} + +func FormatMessageReflectText(msg interface{}, ext string) string { + return FormatMessageReflectCustom(msg, ext, "", " ", "=", false) +} + +func FormatMessageReflectJSON(msg interface{}, ext string) string { + return fmt.Sprintf("{%s}", FormatMessageReflectCustom(msg, ext, "\"", ",", ":", true)) +} + +func ExtractTag(name, original string, tag reflect.StructTag) string { + lookup, ok := tag.Lookup(name) + if !ok { + return original + } + before, _, _ := strings.Cut(lookup, ",") + return before +} + +func FormatMessageReflectCustom(msg interface{}, ext, quotes, sep, sign string, null bool) string { + customSelector := selector + reMap := make(map[string]string) + + vfm := reflect.ValueOf(msg) + vfm = reflect.Indirect(vfm) + vft := vfm.Type() + + if len(customSelector) == 0 || selectorTag != "" { + /* + // we would need proto v2 + msgR := msg.ProtoReflect() + customSelector = make([]string, msgR.Fields().Len()) + for i := 0; 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 literal 0 HcmV?d00001 diff --git a/third_party/goflow2/package/goflow2.env b/third_party/goflow2/package/goflow2.env new file mode 100644 index 000000000000..a54def8fa5c8 --- /dev/null +++ b/third_party/goflow2/package/goflow2.env @@ -0,0 +1 @@ +GOFLOW2_ARGS= \ No newline at end of file diff --git a/third_party/goflow2/package/goflow2.service b/third_party/goflow2/package/goflow2.service new file mode 100644 index 000000000000..49584419cc7a --- /dev/null +++ b/third_party/goflow2/package/goflow2.service @@ -0,0 +1,12 @@ +[Unit] +Description=GoFlow2 +After=network.target + +[Service] +Type=simple +EnvironmentFile=/etc/default/goflow2 +WorkingDirectory=/usr/share/goflow2 +ExecStart=/usr/bin/goflow2 $GOFLOW2_ARGS + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/third_party/goflow2/pb/flow.pb.go b/third_party/goflow2/pb/flow.pb.go new file mode 100644 index 000000000000..4b3402ceb1c9 --- /dev/null +++ b/third_party/goflow2/pb/flow.pb.go @@ -0,0 +1,924 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.21.4 +// source: pb/flow.proto + +package flowpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FlowMessage_FlowType int32 + +const ( + FlowMessage_FLOWUNKNOWN FlowMessage_FlowType = 0 + FlowMessage_SFLOW_5 FlowMessage_FlowType = 1 + FlowMessage_NETFLOW_V5 FlowMessage_FlowType = 2 + FlowMessage_NETFLOW_V9 FlowMessage_FlowType = 3 + FlowMessage_IPFIX FlowMessage_FlowType = 4 +) + +// Enum value maps for FlowMessage_FlowType. +var ( + FlowMessage_FlowType_name = map[int32]string{ + 0: "FLOWUNKNOWN", + 1: "SFLOW_5", + 2: "NETFLOW_V5", + 3: "NETFLOW_V9", + 4: "IPFIX", + } + FlowMessage_FlowType_value = map[string]int32{ + "FLOWUNKNOWN": 0, + "SFLOW_5": 1, + "NETFLOW_V5": 2, + "NETFLOW_V9": 3, + "IPFIX": 4, + } +) + +func (x FlowMessage_FlowType) Enum() *FlowMessage_FlowType { + p := new(FlowMessage_FlowType) + *p = x + return p +} + +func (x FlowMessage_FlowType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FlowMessage_FlowType) Descriptor() protoreflect.EnumDescriptor { + return file_pb_flow_proto_enumTypes[0].Descriptor() +} + +func (FlowMessage_FlowType) Type() protoreflect.EnumType { + return &file_pb_flow_proto_enumTypes[0] +} + +func (x FlowMessage_FlowType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use FlowMessage_FlowType.Descriptor instead. +func (FlowMessage_FlowType) EnumDescriptor() ([]byte, []int) { + return file_pb_flow_proto_rawDescGZIP(), []int{0, 0} +} + +type FlowMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type FlowMessage_FlowType `protobuf:"varint,1,opt,name=type,proto3,enum=flowpb.FlowMessage_FlowType" json:"type,omitempty"` + TimeReceived uint64 `protobuf:"varint,2,opt,name=time_received,json=timeReceived,proto3" json:"time_received,omitempty"` + SequenceNum uint32 `protobuf:"varint,4,opt,name=sequence_num,json=sequenceNum,proto3" json:"sequence_num,omitempty"` + SamplingRate uint64 `protobuf:"varint,3,opt,name=sampling_rate,json=samplingRate,proto3" json:"sampling_rate,omitempty"` + FlowDirection uint32 `protobuf:"varint,42,opt,name=flow_direction,json=flowDirection,proto3" json:"flow_direction,omitempty"` + // Sampler information + SamplerAddress []byte `protobuf:"bytes,11,opt,name=sampler_address,json=samplerAddress,proto3" json:"sampler_address,omitempty"` + // Found inside packet + TimeFlowStart uint64 `protobuf:"varint,38,opt,name=time_flow_start,json=timeFlowStart,proto3" json:"time_flow_start,omitempty"` + TimeFlowEnd uint64 `protobuf:"varint,5,opt,name=time_flow_end,json=timeFlowEnd,proto3" json:"time_flow_end,omitempty"` + TimeFlowStartMs uint64 `protobuf:"varint,63,opt,name=time_flow_start_ms,json=timeFlowStartMs,proto3" json:"time_flow_start_ms,omitempty"` + TimeFlowEndMs uint64 `protobuf:"varint,64,opt,name=time_flow_end_ms,json=timeFlowEndMs,proto3" json:"time_flow_end_ms,omitempty"` + // Size of the sampled packet + Bytes uint64 `protobuf:"varint,9,opt,name=bytes,proto3" json:"bytes,omitempty"` + Packets uint64 `protobuf:"varint,10,opt,name=packets,proto3" json:"packets,omitempty"` + // Source/destination addresses + SrcAddr []byte `protobuf:"bytes,6,opt,name=src_addr,json=srcAddr,proto3" json:"src_addr,omitempty"` + DstAddr []byte `protobuf:"bytes,7,opt,name=dst_addr,json=dstAddr,proto3" json:"dst_addr,omitempty"` + // Layer 3 protocol (IPv4/IPv6/ARP/MPLS...) + Etype uint32 `protobuf:"varint,30,opt,name=etype,proto3" json:"etype,omitempty"` + // Layer 4 protocol + Proto uint32 `protobuf:"varint,20,opt,name=proto,proto3" json:"proto,omitempty"` + // Ports for UDP and TCP + SrcPort uint32 `protobuf:"varint,21,opt,name=src_port,json=srcPort,proto3" json:"src_port,omitempty"` + DstPort uint32 `protobuf:"varint,22,opt,name=dst_port,json=dstPort,proto3" json:"dst_port,omitempty"` + // Interfaces + InIf uint32 `protobuf:"varint,18,opt,name=in_if,json=inIf,proto3" json:"in_if,omitempty"` + OutIf uint32 `protobuf:"varint,19,opt,name=out_if,json=outIf,proto3" json:"out_if,omitempty"` + // Ethernet information + SrcMac uint64 `protobuf:"varint,27,opt,name=src_mac,json=srcMac,proto3" json:"src_mac,omitempty"` + DstMac uint64 `protobuf:"varint,28,opt,name=dst_mac,json=dstMac,proto3" json:"dst_mac,omitempty"` + // Vlan + SrcVlan uint32 `protobuf:"varint,33,opt,name=src_vlan,json=srcVlan,proto3" json:"src_vlan,omitempty"` + DstVlan uint32 `protobuf:"varint,34,opt,name=dst_vlan,json=dstVlan,proto3" json:"dst_vlan,omitempty"` + // 802.1q VLAN in sampled packet + VlanId uint32 `protobuf:"varint,29,opt,name=vlan_id,json=vlanId,proto3" json:"vlan_id,omitempty"` + // VRF + IngressVrfId uint32 `protobuf:"varint,39,opt,name=ingress_vrf_id,json=ingressVrfId,proto3" json:"ingress_vrf_id,omitempty"` + EgressVrfId uint32 `protobuf:"varint,40,opt,name=egress_vrf_id,json=egressVrfId,proto3" json:"egress_vrf_id,omitempty"` + // IP and TCP special flags + IpTos uint32 `protobuf:"varint,23,opt,name=ip_tos,json=ipTos,proto3" json:"ip_tos,omitempty"` + ForwardingStatus uint32 `protobuf:"varint,24,opt,name=forwarding_status,json=forwardingStatus,proto3" json:"forwarding_status,omitempty"` + IpTtl uint32 `protobuf:"varint,25,opt,name=ip_ttl,json=ipTtl,proto3" json:"ip_ttl,omitempty"` + TcpFlags uint32 `protobuf:"varint,26,opt,name=tcp_flags,json=tcpFlags,proto3" json:"tcp_flags,omitempty"` + IcmpType uint32 `protobuf:"varint,31,opt,name=icmp_type,json=icmpType,proto3" json:"icmp_type,omitempty"` + IcmpCode uint32 `protobuf:"varint,32,opt,name=icmp_code,json=icmpCode,proto3" json:"icmp_code,omitempty"` + Ipv6FlowLabel uint32 `protobuf:"varint,37,opt,name=ipv6_flow_label,json=ipv6FlowLabel,proto3" json:"ipv6_flow_label,omitempty"` + // Fragments (IPv4/IPv6) + FragmentId uint32 `protobuf:"varint,35,opt,name=fragment_id,json=fragmentId,proto3" json:"fragment_id,omitempty"` + FragmentOffset uint32 `protobuf:"varint,36,opt,name=fragment_offset,json=fragmentOffset,proto3" json:"fragment_offset,omitempty"` + BiFlowDirection uint32 `protobuf:"varint,41,opt,name=bi_flow_direction,json=biFlowDirection,proto3" json:"bi_flow_direction,omitempty"` + // Autonomous system information + SrcAs uint32 `protobuf:"varint,14,opt,name=src_as,json=srcAs,proto3" json:"src_as,omitempty"` + DstAs uint32 `protobuf:"varint,15,opt,name=dst_as,json=dstAs,proto3" json:"dst_as,omitempty"` + NextHop []byte `protobuf:"bytes,12,opt,name=next_hop,json=nextHop,proto3" json:"next_hop,omitempty"` + NextHopAs uint32 `protobuf:"varint,13,opt,name=next_hop_as,json=nextHopAs,proto3" json:"next_hop_as,omitempty"` + // Prefix size + SrcNet uint32 `protobuf:"varint,16,opt,name=src_net,json=srcNet,proto3" json:"src_net,omitempty"` + DstNet uint32 `protobuf:"varint,17,opt,name=dst_net,json=dstNet,proto3" json:"dst_net,omitempty"` + // BGP information + BgpNextHop []byte `protobuf:"bytes,100,opt,name=bgp_next_hop,json=bgpNextHop,proto3" json:"bgp_next_hop,omitempty"` + BgpCommunities []uint32 `protobuf:"varint,101,rep,packed,name=bgp_communities,json=bgpCommunities,proto3" json:"bgp_communities,omitempty"` + AsPath []uint32 `protobuf:"varint,102,rep,packed,name=as_path,json=asPath,proto3" json:"as_path,omitempty"` + // MPLS information + HasMpls bool `protobuf:"varint,53,opt,name=has_mpls,json=hasMpls,proto3" json:"has_mpls,omitempty"` + MplsCount uint32 `protobuf:"varint,54,opt,name=mpls_count,json=mplsCount,proto3" json:"mpls_count,omitempty"` + Mpls_1Ttl uint32 `protobuf:"varint,55,opt,name=mpls_1_ttl,json=mpls1Ttl,proto3" json:"mpls_1_ttl,omitempty"` // First TTL + Mpls_1Label uint32 `protobuf:"varint,56,opt,name=mpls_1_label,json=mpls1Label,proto3" json:"mpls_1_label,omitempty"` // First Label + Mpls_2Ttl uint32 `protobuf:"varint,57,opt,name=mpls_2_ttl,json=mpls2Ttl,proto3" json:"mpls_2_ttl,omitempty"` // Second TTL + Mpls_2Label uint32 `protobuf:"varint,58,opt,name=mpls_2_label,json=mpls2Label,proto3" json:"mpls_2_label,omitempty"` // Second Label + Mpls_3Ttl uint32 `protobuf:"varint,59,opt,name=mpls_3_ttl,json=mpls3Ttl,proto3" json:"mpls_3_ttl,omitempty"` // Third TTL + Mpls_3Label uint32 `protobuf:"varint,60,opt,name=mpls_3_label,json=mpls3Label,proto3" json:"mpls_3_label,omitempty"` // Third Label + MplsLastTtl uint32 `protobuf:"varint,61,opt,name=mpls_last_ttl,json=mplsLastTtl,proto3" json:"mpls_last_ttl,omitempty"` // Last TTL + MplsLastLabel uint32 `protobuf:"varint,62,opt,name=mpls_last_label,json=mplsLastLabel,proto3" json:"mpls_last_label,omitempty"` // Last Label + MplsLabelIp []byte `protobuf:"bytes,65,opt,name=mpls_label_ip,json=mplsLabelIp,proto3" json:"mpls_label_ip,omitempty"` // MPLS TOP Label IP + ObservationDomainId uint32 `protobuf:"varint,70,opt,name=observation_domain_id,json=observationDomainId,proto3" json:"observation_domain_id,omitempty"` + ObservationPointId uint32 `protobuf:"varint,71,opt,name=observation_point_id,json=observationPointId,proto3" json:"observation_point_id,omitempty"` + // Custom allocations + CustomInteger_1 uint64 `protobuf:"varint,1001,opt,name=custom_integer_1,json=customInteger1,proto3" json:"custom_integer_1,omitempty"` + CustomInteger_2 uint64 `protobuf:"varint,1002,opt,name=custom_integer_2,json=customInteger2,proto3" json:"custom_integer_2,omitempty"` + CustomInteger_3 uint64 `protobuf:"varint,1003,opt,name=custom_integer_3,json=customInteger3,proto3" json:"custom_integer_3,omitempty"` + CustomInteger_4 uint64 `protobuf:"varint,1004,opt,name=custom_integer_4,json=customInteger4,proto3" json:"custom_integer_4,omitempty"` + CustomInteger_5 uint64 `protobuf:"varint,1005,opt,name=custom_integer_5,json=customInteger5,proto3" json:"custom_integer_5,omitempty"` + CustomBytes_1 []byte `protobuf:"bytes,1011,opt,name=custom_bytes_1,json=customBytes1,proto3" json:"custom_bytes_1,omitempty"` + CustomBytes_2 []byte `protobuf:"bytes,1012,opt,name=custom_bytes_2,json=customBytes2,proto3" json:"custom_bytes_2,omitempty"` + CustomBytes_3 []byte `protobuf:"bytes,1013,opt,name=custom_bytes_3,json=customBytes3,proto3" json:"custom_bytes_3,omitempty"` + CustomBytes_4 []byte `protobuf:"bytes,1014,opt,name=custom_bytes_4,json=customBytes4,proto3" json:"custom_bytes_4,omitempty"` + CustomBytes_5 []byte `protobuf:"bytes,1015,opt,name=custom_bytes_5,json=customBytes5,proto3" json:"custom_bytes_5,omitempty"` + CustomList_1 []uint32 `protobuf:"varint,1021,rep,packed,name=custom_list_1,json=customList1,proto3" json:"custom_list_1,omitempty"` +} + +func (x *FlowMessage) Reset() { + *x = FlowMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_pb_flow_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FlowMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlowMessage) ProtoMessage() {} + +func (x *FlowMessage) ProtoReflect() protoreflect.Message { + mi := &file_pb_flow_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlowMessage.ProtoReflect.Descriptor instead. +func (*FlowMessage) Descriptor() ([]byte, []int) { + return file_pb_flow_proto_rawDescGZIP(), []int{0} +} + +func (x *FlowMessage) GetType() FlowMessage_FlowType { + if x != nil { + return x.Type + } + return FlowMessage_FLOWUNKNOWN +} + +func (x *FlowMessage) GetTimeReceived() uint64 { + if x != nil { + return x.TimeReceived + } + return 0 +} + +func (x *FlowMessage) GetSequenceNum() uint32 { + if x != nil { + return x.SequenceNum + } + return 0 +} + +func (x *FlowMessage) GetSamplingRate() uint64 { + if x != nil { + return x.SamplingRate + } + return 0 +} + +func (x *FlowMessage) GetFlowDirection() uint32 { + if x != nil { + return x.FlowDirection + } + return 0 +} + +func (x *FlowMessage) GetSamplerAddress() []byte { + if x != nil { + return x.SamplerAddress + } + return nil +} + +func (x *FlowMessage) GetTimeFlowStart() uint64 { + if x != nil { + return x.TimeFlowStart + } + return 0 +} + +func (x *FlowMessage) GetTimeFlowEnd() uint64 { + if x != nil { + return x.TimeFlowEnd + } + return 0 +} + +func (x *FlowMessage) GetTimeFlowStartMs() uint64 { + if x != nil { + return x.TimeFlowStartMs + } + return 0 +} + +func (x *FlowMessage) GetTimeFlowEndMs() uint64 { + if x != nil { + return x.TimeFlowEndMs + } + return 0 +} + +func (x *FlowMessage) GetBytes() uint64 { + if x != nil { + return x.Bytes + } + return 0 +} + +func (x *FlowMessage) GetPackets() uint64 { + if x != nil { + return x.Packets + } + return 0 +} + +func (x *FlowMessage) GetSrcAddr() []byte { + if x != nil { + return x.SrcAddr + } + return nil +} + +func (x *FlowMessage) GetDstAddr() []byte { + if x != nil { + return x.DstAddr + } + return nil +} + +func (x *FlowMessage) GetEtype() uint32 { + if x != nil { + return x.Etype + } + return 0 +} + +func (x *FlowMessage) GetProto() uint32 { + if x != nil { + return x.Proto + } + return 0 +} + +func (x *FlowMessage) GetSrcPort() uint32 { + if x != nil { + return x.SrcPort + } + return 0 +} + +func (x *FlowMessage) GetDstPort() uint32 { + if x != nil { + return x.DstPort + } + return 0 +} + +func (x *FlowMessage) GetInIf() uint32 { + if x != nil { + return x.InIf + } + return 0 +} + +func (x *FlowMessage) GetOutIf() uint32 { + if x != nil { + return x.OutIf + } + return 0 +} + +func (x *FlowMessage) GetSrcMac() uint64 { + if x != nil { + return x.SrcMac + } + return 0 +} + +func (x *FlowMessage) GetDstMac() uint64 { + if x != nil { + return x.DstMac + } + return 0 +} + +func (x *FlowMessage) GetSrcVlan() uint32 { + if x != nil { + return x.SrcVlan + } + return 0 +} + +func (x *FlowMessage) GetDstVlan() uint32 { + if x != nil { + return x.DstVlan + } + return 0 +} + +func (x *FlowMessage) GetVlanId() uint32 { + if x != nil { + return x.VlanId + } + return 0 +} + +func (x *FlowMessage) GetIngressVrfId() uint32 { + if x != nil { + return x.IngressVrfId + } + return 0 +} + +func (x *FlowMessage) GetEgressVrfId() uint32 { + if x != nil { + return x.EgressVrfId + } + return 0 +} + +func (x *FlowMessage) GetIpTos() uint32 { + if x != nil { + return x.IpTos + } + return 0 +} + +func (x *FlowMessage) GetForwardingStatus() uint32 { + if x != nil { + return x.ForwardingStatus + } + return 0 +} + +func (x *FlowMessage) GetIpTtl() uint32 { + if x != nil { + return x.IpTtl + } + return 0 +} + +func (x *FlowMessage) GetTcpFlags() uint32 { + if x != nil { + return x.TcpFlags + } + return 0 +} + +func (x *FlowMessage) GetIcmpType() uint32 { + if x != nil { + return x.IcmpType + } + return 0 +} + +func (x *FlowMessage) GetIcmpCode() uint32 { + if x != nil { + return x.IcmpCode + } + return 0 +} + +func (x *FlowMessage) GetIpv6FlowLabel() uint32 { + if x != nil { + return x.Ipv6FlowLabel + } + return 0 +} + +func (x *FlowMessage) GetFragmentId() uint32 { + if x != nil { + return x.FragmentId + } + return 0 +} + +func (x *FlowMessage) GetFragmentOffset() uint32 { + if x != nil { + return x.FragmentOffset + } + return 0 +} + +func (x *FlowMessage) GetBiFlowDirection() uint32 { + if x != nil { + return x.BiFlowDirection + } + return 0 +} + +func (x *FlowMessage) GetSrcAs() uint32 { + if x != nil { + return x.SrcAs + } + return 0 +} + +func (x *FlowMessage) GetDstAs() uint32 { + if x != nil { + return x.DstAs + } + return 0 +} + +func (x *FlowMessage) GetNextHop() []byte { + if x != nil { + return x.NextHop + } + return nil +} + +func (x *FlowMessage) GetNextHopAs() uint32 { + if x != nil { + return x.NextHopAs + } + return 0 +} + +func (x *FlowMessage) GetSrcNet() uint32 { + if x != nil { + return x.SrcNet + } + return 0 +} + +func (x *FlowMessage) GetDstNet() uint32 { + if x != nil { + return x.DstNet + } + return 0 +} + +func (x *FlowMessage) GetBgpNextHop() []byte { + if x != nil { + return x.BgpNextHop + } + return nil +} + +func (x *FlowMessage) GetBgpCommunities() []uint32 { + if x != nil { + return x.BgpCommunities + } + return nil +} + +func (x *FlowMessage) GetAsPath() []uint32 { + if x != nil { + return x.AsPath + } + return nil +} + +func (x *FlowMessage) GetHasMpls() bool { + if x != nil { + return x.HasMpls + } + return false +} + +func (x *FlowMessage) GetMplsCount() uint32 { + if x != nil { + return x.MplsCount + } + return 0 +} + +func (x *FlowMessage) GetMpls_1Ttl() uint32 { + if x != nil { + return x.Mpls_1Ttl + } + return 0 +} + +func (x *FlowMessage) GetMpls_1Label() uint32 { + if x != nil { + return x.Mpls_1Label + } + return 0 +} + +func (x *FlowMessage) GetMpls_2Ttl() uint32 { + if x != nil { + return x.Mpls_2Ttl + } + return 0 +} + +func (x *FlowMessage) GetMpls_2Label() uint32 { + if x != nil { + return x.Mpls_2Label + } + return 0 +} + +func (x *FlowMessage) GetMpls_3Ttl() uint32 { + if x != nil { + return x.Mpls_3Ttl + } + return 0 +} + +func (x *FlowMessage) GetMpls_3Label() uint32 { + if x != nil { + return x.Mpls_3Label + } + return 0 +} + +func (x *FlowMessage) GetMplsLastTtl() uint32 { + if x != nil { + return x.MplsLastTtl + } + return 0 +} + +func (x *FlowMessage) GetMplsLastLabel() uint32 { + if x != nil { + return x.MplsLastLabel + } + return 0 +} + +func (x *FlowMessage) GetMplsLabelIp() []byte { + if x != nil { + return x.MplsLabelIp + } + return nil +} + +func (x *FlowMessage) GetObservationDomainId() uint32 { + if x != nil { + return x.ObservationDomainId + } + return 0 +} + +func (x *FlowMessage) GetObservationPointId() uint32 { + if x != nil { + return x.ObservationPointId + } + return 0 +} + +func (x *FlowMessage) GetCustomInteger_1() uint64 { + if x != nil { + return x.CustomInteger_1 + } + return 0 +} + +func (x *FlowMessage) GetCustomInteger_2() uint64 { + if x != nil { + return x.CustomInteger_2 + } + return 0 +} + +func (x *FlowMessage) GetCustomInteger_3() uint64 { + if x != nil { + return x.CustomInteger_3 + } + return 0 +} + +func (x *FlowMessage) GetCustomInteger_4() uint64 { + if x != nil { + return x.CustomInteger_4 + } + return 0 +} + +func (x *FlowMessage) GetCustomInteger_5() uint64 { + if x != nil { + return x.CustomInteger_5 + } + return 0 +} + +func (x *FlowMessage) GetCustomBytes_1() []byte { + if x != nil { + return x.CustomBytes_1 + } + return nil +} + +func (x *FlowMessage) GetCustomBytes_2() []byte { + if x != nil { + return x.CustomBytes_2 + } + return nil +} + +func (x *FlowMessage) GetCustomBytes_3() []byte { + if x != nil { + return x.CustomBytes_3 + } + return nil +} + +func (x *FlowMessage) GetCustomBytes_4() []byte { + if x != nil { + return x.CustomBytes_4 + } + return nil +} + +func (x *FlowMessage) GetCustomBytes_5() []byte { + if x != nil { + return x.CustomBytes_5 + } + return nil +} + +func (x *FlowMessage) GetCustomList_1() []uint32 { + if x != nil { + return x.CustomList_1 + } + return nil +} + +var File_pb_flow_proto protoreflect.FileDescriptor + +var file_pb_flow_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x70, 0x62, 0x2f, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x06, 0x66, 0x6c, 0x6f, 0x77, 0x70, 0x62, 0x22, 0x90, 0x13, 0x0a, 0x0b, 0x46, 0x6c, 0x6f, 0x77, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x70, 0x62, 0x2e, 0x46, + 0x6c, 0x6f, 0x77, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x69, 0x6d, + 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x12, 0x21, + 0x0a, 0x0c, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x4e, 0x75, + 0x6d, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x61, + 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, + 0x6e, 0x67, 0x52, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x64, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, + 0x66, 0x6c, 0x6f, 0x77, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, + 0x0f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x26, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x22, + 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x65, 0x6e, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x45, + 0x6e, 0x64, 0x12, 0x2b, 0x0a, 0x12, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x73, 0x18, 0x3f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, + 0x74, 0x69, 0x6d, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4d, 0x73, 0x12, + 0x27, 0x0a, 0x10, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x65, 0x6e, 0x64, + 0x5f, 0x6d, 0x73, 0x18, 0x40, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x46, + 0x6c, 0x6f, 0x77, 0x45, 0x6e, 0x64, 0x4d, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x18, + 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x07, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x72, 0x63, 0x41, + 0x64, 0x64, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x64, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x14, + 0x0a, 0x05, 0x65, 0x74, 0x79, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x65, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x14, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x72, + 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x72, + 0x63, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74, 0x5f, 0x70, 0x6f, 0x72, + 0x74, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x64, 0x73, 0x74, 0x50, 0x6f, 0x72, 0x74, + 0x12, 0x13, 0x0a, 0x05, 0x69, 0x6e, 0x5f, 0x69, 0x66, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x04, 0x69, 0x6e, 0x49, 0x66, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x5f, 0x69, 0x66, 0x18, + 0x13, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6f, 0x75, 0x74, 0x49, 0x66, 0x12, 0x17, 0x0a, 0x07, + 0x73, 0x72, 0x63, 0x5f, 0x6d, 0x61, 0x63, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, + 0x72, 0x63, 0x4d, 0x61, 0x63, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x73, 0x74, 0x5f, 0x6d, 0x61, 0x63, + 0x18, 0x1c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x64, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x12, 0x19, + 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x76, 0x6c, 0x61, 0x6e, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x73, 0x72, 0x63, 0x56, 0x6c, 0x61, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74, + 0x5f, 0x76, 0x6c, 0x61, 0x6e, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x64, 0x73, 0x74, + 0x56, 0x6c, 0x61, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x76, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x1d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x76, 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x24, 0x0a, + 0x0e, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x76, 0x72, 0x66, 0x5f, 0x69, 0x64, 0x18, + 0x27, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x56, 0x72, + 0x66, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x76, 0x72, + 0x66, 0x5f, 0x69, 0x64, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x65, 0x67, 0x72, 0x65, + 0x73, 0x73, 0x56, 0x72, 0x66, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x70, 0x5f, 0x74, 0x6f, + 0x73, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x70, 0x54, 0x6f, 0x73, 0x12, 0x2b, + 0x0a, 0x11, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x69, + 0x70, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x70, 0x54, + 0x74, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x63, 0x70, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, + 0x1a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x74, 0x63, 0x70, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, + 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x1f, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, + 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x08, 0x69, 0x63, 0x6d, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x69, 0x70, 0x76, + 0x36, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x25, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0d, 0x69, 0x70, 0x76, 0x36, 0x46, 0x6c, 0x6f, 0x77, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x23, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, + 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x24, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x66, 0x72, 0x61, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x62, + 0x69, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x29, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x62, 0x69, 0x46, 0x6c, 0x6f, 0x77, 0x44, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x72, 0x63, 0x5f, 0x61, + 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x72, 0x63, 0x41, 0x73, 0x12, 0x15, + 0x0a, 0x06, 0x64, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x64, 0x73, 0x74, 0x41, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, 0x6f, + 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6e, 0x65, 0x78, 0x74, 0x48, 0x6f, 0x70, + 0x12, 0x1e, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x61, 0x73, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x48, 0x6f, 0x70, 0x41, 0x73, + 0x12, 0x17, 0x0a, 0x07, 0x73, 0x72, 0x63, 0x5f, 0x6e, 0x65, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x06, 0x73, 0x72, 0x63, 0x4e, 0x65, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x73, 0x74, + 0x5f, 0x6e, 0x65, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x64, 0x73, 0x74, 0x4e, + 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x62, 0x67, 0x70, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, + 0x6f, 0x70, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x62, 0x67, 0x70, 0x4e, 0x65, 0x78, + 0x74, 0x48, 0x6f, 0x70, 0x12, 0x27, 0x0a, 0x0f, 0x62, 0x67, 0x70, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, + 0x75, 0x6e, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x65, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0e, 0x62, + 0x67, 0x70, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x17, 0x0a, + 0x07, 0x61, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x66, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, + 0x61, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x6d, 0x70, + 0x6c, 0x73, 0x18, 0x35, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x4d, 0x70, 0x6c, + 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x36, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6d, 0x70, 0x6c, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x31, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x37, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x70, 0x6c, 0x73, 0x31, 0x54, 0x74, 0x6c, 0x12, 0x20, + 0x0a, 0x0c, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x31, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x38, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x70, 0x6c, 0x73, 0x31, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x32, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x39, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x70, 0x6c, 0x73, 0x32, 0x54, 0x74, 0x6c, 0x12, 0x20, + 0x0a, 0x0c, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x32, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x3a, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x70, 0x6c, 0x73, 0x32, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x33, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x3b, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x70, 0x6c, 0x73, 0x33, 0x54, 0x74, 0x6c, 0x12, 0x20, + 0x0a, 0x0c, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x33, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x3c, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x70, 0x6c, 0x73, 0x33, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x74, + 0x6c, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x70, 0x6c, 0x73, 0x4c, 0x61, 0x73, + 0x74, 0x54, 0x74, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x3e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6d, + 0x70, 0x6c, 0x73, 0x4c, 0x61, 0x73, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x22, 0x0a, 0x0d, + 0x6d, 0x70, 0x6c, 0x73, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0x41, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6d, 0x70, 0x6c, 0x73, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x49, 0x70, + 0x12, 0x32, 0x0a, 0x15, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x13, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x47, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x12, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x5f, 0x31, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, + 0x31, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x69, 0x6e, 0x74, 0x65, + 0x67, 0x65, 0x72, 0x5f, 0x32, 0x18, 0xea, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x63, 0x75, + 0x73, 0x74, 0x6f, 0x6d, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x32, 0x12, 0x29, 0x0a, 0x10, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x5f, 0x33, + 0x18, 0xeb, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x49, + 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x33, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x5f, 0x34, 0x18, 0xec, 0x07, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, + 0x72, 0x34, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x69, 0x6e, 0x74, + 0x65, 0x67, 0x65, 0x72, 0x5f, 0x35, 0x18, 0xed, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x63, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x35, 0x12, 0x25, 0x0a, + 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x31, 0x18, + 0xf3, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x42, 0x79, + 0x74, 0x65, 0x73, 0x31, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x5f, 0x32, 0x18, 0xf4, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x63, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x42, 0x79, 0x74, 0x65, 0x73, 0x32, 0x12, 0x25, 0x0a, 0x0e, 0x63, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x33, 0x18, 0xf5, 0x07, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x42, 0x79, 0x74, 0x65, + 0x73, 0x33, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x5f, 0x34, 0x18, 0xf6, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x42, 0x79, 0x74, 0x65, 0x73, 0x34, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x35, 0x18, 0xf7, 0x07, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x42, 0x79, 0x74, 0x65, 0x73, 0x35, + 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, + 0x31, 0x18, 0xfd, 0x07, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x4c, 0x69, 0x73, 0x74, 0x31, 0x22, 0x53, 0x0a, 0x08, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x4c, 0x4f, 0x57, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x35, 0x10, 0x01, 0x12, + 0x0e, 0x0a, 0x0a, 0x4e, 0x45, 0x54, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x56, 0x35, 0x10, 0x02, 0x12, + 0x0e, 0x0a, 0x0a, 0x4e, 0x45, 0x54, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x56, 0x39, 0x10, 0x03, 0x12, + 0x09, 0x0a, 0x05, 0x49, 0x50, 0x46, 0x49, 0x58, 0x10, 0x04, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x65, 0x74, 0x73, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x72, 0x2f, 0x67, 0x6f, 0x66, 0x6c, 0x6f, 0x77, 0x32, 0x2f, 0x70, 0x62, 0x3b, 0x66, + 0x6c, 0x6f, 0x77, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_pb_flow_proto_rawDescOnce sync.Once + file_pb_flow_proto_rawDescData = file_pb_flow_proto_rawDesc +) + +func file_pb_flow_proto_rawDescGZIP() []byte { + file_pb_flow_proto_rawDescOnce.Do(func() { + file_pb_flow_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_flow_proto_rawDescData) + }) + return file_pb_flow_proto_rawDescData +} + +var file_pb_flow_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_pb_flow_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_pb_flow_proto_goTypes = []interface{}{ + (FlowMessage_FlowType)(0), // 0: flowpb.FlowMessage.FlowType + (*FlowMessage)(nil), // 1: flowpb.FlowMessage +} +var file_pb_flow_proto_depIdxs = []int32{ + 0, // 0: flowpb.FlowMessage.type:type_name -> flowpb.FlowMessage.FlowType + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_pb_flow_proto_init() } +func file_pb_flow_proto_init() { + if File_pb_flow_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_pb_flow_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FlowMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_pb_flow_proto_rawDesc, + NumEnums: 1, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_pb_flow_proto_goTypes, + DependencyIndexes: file_pb_flow_proto_depIdxs, + EnumInfos: file_pb_flow_proto_enumTypes, + MessageInfos: file_pb_flow_proto_msgTypes, + }.Build() + File_pb_flow_proto = out.File + file_pb_flow_proto_rawDesc = nil + file_pb_flow_proto_goTypes = nil + file_pb_flow_proto_depIdxs = nil +} diff --git a/third_party/goflow2/pb/flow.proto b/third_party/goflow2/pb/flow.proto new file mode 100644 index 000000000000..1cbaae8d52c1 --- /dev/null +++ b/third_party/goflow2/pb/flow.proto @@ -0,0 +1,131 @@ +syntax = "proto3"; +package flowpb; +option go_package = "github.com/netsampler/goflow2/pb;flowpb"; + +message FlowMessage { + + enum FlowType { + FLOWUNKNOWN = 0; + SFLOW_5 = 1; + NETFLOW_V5 = 2; + NETFLOW_V9 = 3; + IPFIX = 4; + } + FlowType type = 1; + + uint64 time_received = 2; + uint32 sequence_num = 4; + uint64 sampling_rate = 3; + + uint32 flow_direction = 42; + + // Sampler information + bytes sampler_address = 11; + + // Found inside packet + uint64 time_flow_start = 38; + uint64 time_flow_end = 5; + uint64 time_flow_start_ms = 63; + uint64 time_flow_end_ms = 64; + + // Size of the sampled packet + uint64 bytes = 9; + uint64 packets = 10; + + // Source/destination addresses + bytes src_addr = 6; + bytes dst_addr = 7; + + // Layer 3 protocol (IPv4/IPv6/ARP/MPLS...) + uint32 etype = 30; + + // Layer 4 protocol + uint32 proto = 20; + + // Ports for UDP and TCP + uint32 src_port = 21; + uint32 dst_port = 22; + + // Interfaces + uint32 in_if = 18; + uint32 out_if = 19; + + // Ethernet information + uint64 src_mac = 27; + uint64 dst_mac = 28; + + // Vlan + uint32 src_vlan = 33; + uint32 dst_vlan = 34; + // 802.1q VLAN in sampled packet + uint32 vlan_id = 29; + + // VRF + uint32 ingress_vrf_id = 39; + uint32 egress_vrf_id = 40; + + // IP and TCP special flags + uint32 ip_tos = 23; + uint32 forwarding_status = 24; + uint32 ip_ttl = 25; + uint32 tcp_flags = 26; + uint32 icmp_type = 31; + uint32 icmp_code = 32; + uint32 ipv6_flow_label = 37; + // Fragments (IPv4/IPv6) + uint32 fragment_id = 35; + uint32 fragment_offset = 36; + uint32 bi_flow_direction = 41; + + // Autonomous system information + uint32 src_as = 14; + uint32 dst_as = 15; + + bytes next_hop = 12; + uint32 next_hop_as = 13; + + // Prefix size + uint32 src_net = 16; + uint32 dst_net = 17; + + // BGP information + bytes bgp_next_hop = 100; + repeated uint32 bgp_communities = 101; + repeated uint32 as_path = 102; + + // MPLS information + bool has_mpls = 53; + uint32 mpls_count = 54; + uint32 mpls_1_ttl = 55; // First TTL + uint32 mpls_1_label = 56; // First Label + uint32 mpls_2_ttl = 57; // Second TTL + uint32 mpls_2_label = 58; // Second Label + uint32 mpls_3_ttl = 59; // Third TTL + uint32 mpls_3_label = 60; // Third Label + uint32 mpls_last_ttl = 61; // Last TTL + uint32 mpls_last_label = 62; // Last Label + bytes mpls_label_ip = 65; // MPLS TOP Label IP + + uint32 observation_domain_id = 70; + uint32 observation_point_id = 71; + + // Custom fields: start after ID 1000: + // uint32 my_custom_field = 1000; + + + // Custom allocations + uint64 custom_integer_1 = 1001; + uint64 custom_integer_2 = 1002; + uint64 custom_integer_3 = 1003; + uint64 custom_integer_4 = 1004; + uint64 custom_integer_5 = 1005; + + bytes custom_bytes_1 = 1011; + bytes custom_bytes_2 = 1012; + bytes custom_bytes_3 = 1013; + bytes custom_bytes_4 = 1014; + bytes custom_bytes_5 = 1015; + + repeated uint32 custom_list_1 = 1021; + +} diff --git a/third_party/goflow2/producer/producer_nf.go b/third_party/goflow2/producer/producer_nf.go new file mode 100644 index 000000000000..07a00f310fee --- /dev/null +++ b/third_party/goflow2/producer/producer_nf.go @@ -0,0 +1,682 @@ +package producer + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "net" + "sync" + "time" + + "github.com/netsampler/goflow2/decoders/netflow" + flowmessage "github.com/netsampler/goflow2/pb" +) + +type SamplingRateSystem interface { + GetSamplingRate(version uint16, obsDomainId uint32) (uint32, error) + AddSamplingRate(version uint16, obsDomainId uint32, samplingRate uint32) +} + +type basicSamplingRateSystem struct { + sampling map[uint16]map[uint32]uint32 + samplinglock *sync.RWMutex +} + +func CreateSamplingSystem() SamplingRateSystem { + ts := &basicSamplingRateSystem{ + sampling: make(map[uint16]map[uint32]uint32), + samplinglock: &sync.RWMutex{}, + } + return ts +} + +func (s *basicSamplingRateSystem) AddSamplingRate(version uint16, obsDomainId uint32, samplingRate uint32) { + s.samplinglock.Lock() + defer s.samplinglock.Unlock() + _, exists := s.sampling[version] + if exists != true { + s.sampling[version] = make(map[uint32]uint32) + } + s.sampling[version][obsDomainId] = samplingRate +} + +func (s *basicSamplingRateSystem) GetSamplingRate(version uint16, obsDomainId uint32) (uint32, error) { + s.samplinglock.RLock() + defer s.samplinglock.RUnlock() + samplingVersion, okver := s.sampling[version] + if okver { + samplingRate, okid := samplingVersion[obsDomainId] + if okid { + return samplingRate, nil + } + return 0, errors.New("") // TBC + } + return 0, errors.New("") // TBC +} + +type SingleSamplingRateSystem struct { + Sampling uint32 +} + +func (s *SingleSamplingRateSystem) AddSamplingRate(version uint16, obsDomainId uint32, samplingRate uint32) { +} + +func (s *SingleSamplingRateSystem) GetSamplingRate(version uint16, obsDomainId uint32) (uint32, error) { + return s.Sampling, nil +} + +func NetFlowLookFor(dataFields []netflow.DataField, typeId uint16) (bool, interface{}) { + for _, dataField := range dataFields { + if dataField.Type == typeId { + return true, dataField.Value + } + } + return false, nil +} + +func NetFlowPopulate(dataFields []netflow.DataField, typeId uint16, addr interface{}) bool { + exists, value := NetFlowLookFor(dataFields, typeId) + if exists && value != nil { + valueBytes, ok := value.([]byte) + valueReader := bytes.NewReader(valueBytes) + if ok { + switch addrt := addr.(type) { + case *(net.IP): + *addrt = valueBytes + case *(time.Time): + t := uint64(0) + binary.Read(valueReader, binary.BigEndian, &t) + t64 := int64(t / 1000) + *addrt = time.Unix(t64, 0) + default: + binary.Read(valueReader, binary.BigEndian, addr) + } + } + } + return exists +} + +func WriteUDecoded(o uint64, out interface{}) error { + switch t := out.(type) { + case *byte: + *t = byte(o) + case *uint16: + *t = uint16(o) + case *uint32: + *t = uint32(o) + case *uint64: + *t = o + default: + return errors.New("The parameter is not a pointer to a byte/uint16/uint32/uint64 structure") + } + return nil +} + +func WriteDecoded(o int64, out interface{}) error { + switch t := out.(type) { + case *int8: + *t = int8(o) + case *int16: + *t = int16(o) + case *int32: + *t = int32(o) + case *int64: + *t = o + default: + return errors.New("The parameter is not a pointer to a int8/int16/int32/int64 structure") + } + return nil +} + +func DecodeUNumber(b []byte, out interface{}) error { + var o uint64 + l := len(b) + switch l { + case 1: + o = uint64(b[0]) + case 2: + o = uint64(binary.BigEndian.Uint16(b)) + case 4: + o = uint64(binary.BigEndian.Uint32(b)) + case 8: + o = binary.BigEndian.Uint64(b) + default: + if l < 8 { + var iter uint + for i := range b { + o |= uint64(b[i]) << uint(8*(uint(l)-iter-1)) + iter++ + } + } else { + return errors.New(fmt.Sprintf("Non-regular number of bytes for a number: %v", l)) + } + } + return WriteUDecoded(o, out) +} + +func DecodeUNumberLE(b []byte, out interface{}) error { + var o uint64 + l := len(b) + switch l { + case 1: + o = uint64(b[0]) + case 2: + o = uint64(binary.LittleEndian.Uint16(b)) + case 4: + o = uint64(binary.LittleEndian.Uint32(b)) + case 8: + o = binary.LittleEndian.Uint64(b) + default: + if l < 8 { + var iter uint + for i := range b { + o |= uint64(b[i]) << uint(8*(iter)) + iter++ + } + } else { + return errors.New(fmt.Sprintf("Non-regular number of bytes for a number: %v", l)) + } + } + return WriteUDecoded(o, out) +} + +func DecodeNumber(b []byte, out interface{}) error { + var o int64 + l := len(b) + switch l { + case 1: + o = int64(int8(b[0])) + case 2: + o = int64(int16(binary.BigEndian.Uint16(b))) + case 4: + o = int64(int32(binary.BigEndian.Uint32(b))) + case 8: + o = int64(binary.BigEndian.Uint64(b)) + default: + if l < 8 { + var iter int + for i := range b { + o |= int64(b[i]) << int(8*(int(l)-iter-1)) + iter++ + } + } else { + return errors.New(fmt.Sprintf("Non-regular number of bytes for a number: %v", l)) + } + } + return WriteDecoded(o, out) +} + +func DecodeNumberLE(b []byte, out interface{}) error { + var o int64 + l := len(b) + switch l { + case 1: + o = int64(int8(b[0])) + case 2: + o = int64(int16(binary.LittleEndian.Uint16(b))) + case 4: + o = int64(int32(binary.LittleEndian.Uint32(b))) + case 8: + o = int64(binary.LittleEndian.Uint64(b)) + default: + if l < 8 { + var iter int + for i := range b { + o |= int64(b[i]) << int(8*(iter)) + iter++ + } + } else { + return errors.New(fmt.Sprintf("Non-regular number of bytes for a number: %v", l)) + } + } + return WriteDecoded(o, out) +} + +func allZeroes(v []byte) bool { + for _, b := range v { + if b != 0 { + return false + } + } + return true +} + +func addrReplaceCheck(dstAddr *[]byte, v []byte, eType *uint32, ipv6 bool) { + if (len(*dstAddr) == 0 && len(v) > 0) || + (len(*dstAddr) != 0 && len(v) > 0 && !allZeroes(v)) { + *dstAddr = v + + if ipv6 { + *eType = 0x86dd + } else { + *eType = 0x800 + } + + } +} + +func ConvertNetFlowDataSet(version uint16, baseTime uint32, uptime uint32, record []netflow.DataField, mapperNetFlow *NetFlowMapper, mapperSFlow *SFlowMapper) *flowmessage.FlowMessage { + flowMessage := &flowmessage.FlowMessage{} + var time uint64 + + if version == 9 { + flowMessage.Type = flowmessage.FlowMessage_NETFLOW_V9 + } else if version == 10 { + flowMessage.Type = flowmessage.FlowMessage_IPFIX + } + + for i := range record { + df := record[i] + + v, ok := df.Value.([]byte) + if !ok { + continue + } + + MapCustomNetFlow(flowMessage, df, mapperNetFlow) + + if df.PenProvided { + continue + } + + switch df.Type { + + case netflow.IPFIX_FIELD_observationPointId: + DecodeUNumber(v, &(flowMessage.ObservationPointId)) + + // Statistics + case netflow.NFV9_FIELD_IN_BYTES: + DecodeUNumber(v, &(flowMessage.Bytes)) + case netflow.NFV9_FIELD_IN_PKTS: + DecodeUNumber(v, &(flowMessage.Packets)) + case netflow.NFV9_FIELD_OUT_BYTES: + DecodeUNumber(v, &(flowMessage.Bytes)) + case netflow.NFV9_FIELD_OUT_PKTS: + DecodeUNumber(v, &(flowMessage.Packets)) + + // L4 + case netflow.NFV9_FIELD_L4_SRC_PORT: + DecodeUNumber(v, &(flowMessage.SrcPort)) + case netflow.NFV9_FIELD_L4_DST_PORT: + DecodeUNumber(v, &(flowMessage.DstPort)) + case netflow.NFV9_FIELD_PROTOCOL: + DecodeUNumber(v, &(flowMessage.Proto)) + + // Network + case netflow.NFV9_FIELD_SRC_AS: + DecodeUNumber(v, &(flowMessage.SrcAs)) + case netflow.NFV9_FIELD_DST_AS: + DecodeUNumber(v, &(flowMessage.DstAs)) + + // Interfaces + case netflow.NFV9_FIELD_INPUT_SNMP: + DecodeUNumber(v, &(flowMessage.InIf)) + case netflow.NFV9_FIELD_OUTPUT_SNMP: + DecodeUNumber(v, &(flowMessage.OutIf)) + + case netflow.NFV9_FIELD_FORWARDING_STATUS: + DecodeUNumber(v, &(flowMessage.ForwardingStatus)) + case netflow.NFV9_FIELD_SRC_TOS: + DecodeUNumber(v, &(flowMessage.IpTos)) + case netflow.NFV9_FIELD_TCP_FLAGS: + DecodeUNumber(v, &(flowMessage.TcpFlags)) + case netflow.NFV9_FIELD_MIN_TTL: + DecodeUNumber(v, &(flowMessage.IpTtl)) + + // IP + case netflow.NFV9_FIELD_IP_PROTOCOL_VERSION: + if len(v) > 0 { + if v[0] == 4 { + flowMessage.Etype = 0x800 + } else if v[0] == 6 { + flowMessage.Etype = 0x86dd + } + } + + case netflow.NFV9_FIELD_IPV4_SRC_ADDR: + addrReplaceCheck(&(flowMessage.SrcAddr), v, &(flowMessage.Etype), false) + + case netflow.NFV9_FIELD_IPV4_DST_ADDR: + addrReplaceCheck(&(flowMessage.DstAddr), v, &(flowMessage.Etype), false) + + case netflow.NFV9_FIELD_SRC_MASK: + DecodeUNumber(v, &(flowMessage.SrcNet)) + case netflow.NFV9_FIELD_DST_MASK: + DecodeUNumber(v, &(flowMessage.DstNet)) + + case netflow.NFV9_FIELD_IPV6_SRC_ADDR: + addrReplaceCheck(&(flowMessage.SrcAddr), v, &(flowMessage.Etype), true) + + case netflow.NFV9_FIELD_IPV6_DST_ADDR: + addrReplaceCheck(&(flowMessage.DstAddr), v, &(flowMessage.Etype), true) + + case netflow.NFV9_FIELD_IPV6_SRC_MASK: + DecodeUNumber(v, &(flowMessage.SrcNet)) + case netflow.NFV9_FIELD_IPV6_DST_MASK: + DecodeUNumber(v, &(flowMessage.DstNet)) + + case netflow.NFV9_FIELD_IPV4_NEXT_HOP: + flowMessage.NextHop = v + case netflow.NFV9_FIELD_BGP_IPV4_NEXT_HOP: + flowMessage.BgpNextHop = v + + case netflow.NFV9_FIELD_IPV6_NEXT_HOP: + flowMessage.NextHop = v + case netflow.NFV9_FIELD_BGP_IPV6_NEXT_HOP: + flowMessage.BgpNextHop = v + + // ICMP + case netflow.NFV9_FIELD_ICMP_TYPE: + var icmpTypeCode uint16 + DecodeUNumber(v, &icmpTypeCode) + flowMessage.IcmpType = uint32(icmpTypeCode >> 8) + flowMessage.IcmpCode = uint32(icmpTypeCode & 0xff) + case netflow.IPFIX_FIELD_icmpTypeCodeIPv6: + var icmpTypeCode uint16 + DecodeUNumber(v, &icmpTypeCode) + flowMessage.IcmpType = uint32(icmpTypeCode >> 8) + flowMessage.IcmpCode = uint32(icmpTypeCode & 0xff) + case netflow.IPFIX_FIELD_icmpTypeIPv4: + DecodeUNumber(v, &(flowMessage.IcmpType)) + case netflow.IPFIX_FIELD_icmpTypeIPv6: + DecodeUNumber(v, &(flowMessage.IcmpType)) + case netflow.IPFIX_FIELD_icmpCodeIPv4: + DecodeUNumber(v, &(flowMessage.IcmpCode)) + case netflow.IPFIX_FIELD_icmpCodeIPv6: + DecodeUNumber(v, &(flowMessage.IcmpCode)) + + // Mac + case netflow.NFV9_FIELD_IN_SRC_MAC: + DecodeUNumber(v, &(flowMessage.SrcMac)) + case netflow.NFV9_FIELD_IN_DST_MAC: + DecodeUNumber(v, &(flowMessage.DstMac)) + case netflow.NFV9_FIELD_OUT_SRC_MAC: + DecodeUNumber(v, &(flowMessage.SrcMac)) + case netflow.NFV9_FIELD_OUT_DST_MAC: + DecodeUNumber(v, &(flowMessage.DstMac)) + + case netflow.NFV9_FIELD_SRC_VLAN: + DecodeUNumber(v, &(flowMessage.VlanId)) + DecodeUNumber(v, &(flowMessage.SrcVlan)) + case netflow.NFV9_FIELD_DST_VLAN: + DecodeUNumber(v, &(flowMessage.DstVlan)) + + case netflow.IPFIX_FIELD_ingressVRFID: + DecodeUNumber(v, &(flowMessage.IngressVrfId)) + case netflow.IPFIX_FIELD_egressVRFID: + DecodeUNumber(v, &(flowMessage.EgressVrfId)) + + case netflow.NFV9_FIELD_IPV4_IDENT: + DecodeUNumber(v, &(flowMessage.FragmentId)) + case netflow.NFV9_FIELD_FRAGMENT_OFFSET: + var fragOffset uint32 + DecodeUNumber(v, &fragOffset) + flowMessage.FragmentOffset |= fragOffset + case netflow.IPFIX_FIELD_fragmentFlags: + var ipFlags uint32 + DecodeUNumber(v, &ipFlags) + flowMessage.FragmentOffset |= ipFlags + case netflow.NFV9_FIELD_IPV6_FLOW_LABEL: + DecodeUNumber(v, &(flowMessage.Ipv6FlowLabel)) + + case netflow.IPFIX_FIELD_biflowDirection: + DecodeUNumber(v, &(flowMessage.BiFlowDirection)) + + case netflow.NFV9_FIELD_DIRECTION: + DecodeUNumber(v, &(flowMessage.FlowDirection)) + + // MPLS + case netflow.IPFIX_FIELD_mplsTopLabelStackSection: + var mplsLabel uint32 + DecodeUNumber(v, &mplsLabel) + flowMessage.Mpls_1Label = uint32(mplsLabel >> 4) + flowMessage.HasMpls = true + case netflow.IPFIX_FIELD_mplsLabelStackSection2: + var mplsLabel uint32 + DecodeUNumber(v, &mplsLabel) + flowMessage.Mpls_2Label = uint32(mplsLabel >> 4) + case netflow.IPFIX_FIELD_mplsLabelStackSection3: + var mplsLabel uint32 + DecodeUNumber(v, &mplsLabel) + flowMessage.Mpls_3Label = uint32(mplsLabel >> 4) + case netflow.IPFIX_FIELD_mplsTopLabelIPv4Address: + flowMessage.MplsLabelIp = v + case netflow.IPFIX_FIELD_mplsTopLabelIPv6Address: + flowMessage.MplsLabelIp = v + + default: + if version == 9 { + // NetFlow v9 time works with a differential based on router's uptime + switch df.Type { + case netflow.NFV9_FIELD_FIRST_SWITCHED: + var timeFirstSwitched uint32 + DecodeUNumber(v, &timeFirstSwitched) + timeDiff := (uptime - timeFirstSwitched) + flowMessage.TimeFlowStart = uint64(baseTime - timeDiff/1000) + flowMessage.TimeFlowStartMs = uint64(baseTime)*1000 - uint64(timeDiff) + case netflow.NFV9_FIELD_LAST_SWITCHED: + var timeLastSwitched uint32 + DecodeUNumber(v, &timeLastSwitched) + timeDiff := (uptime - timeLastSwitched) + flowMessage.TimeFlowEnd = uint64(baseTime - timeDiff/1000) + flowMessage.TimeFlowEndMs = uint64(baseTime)*1000 - uint64(timeDiff) + } + } else if version == 10 { + switch df.Type { + case netflow.IPFIX_FIELD_flowStartSeconds: + DecodeUNumber(v, &time) + flowMessage.TimeFlowStart = time + flowMessage.TimeFlowStartMs = time * 1000 + case netflow.IPFIX_FIELD_flowStartMilliseconds: + DecodeUNumber(v, &time) + flowMessage.TimeFlowStart = time / 1000 + flowMessage.TimeFlowStartMs = time + case netflow.IPFIX_FIELD_flowStartMicroseconds: + DecodeUNumber(v, &time) + flowMessage.TimeFlowStart = time / 1000000 + flowMessage.TimeFlowStartMs = time / 1000 + case netflow.IPFIX_FIELD_flowStartNanoseconds: + DecodeUNumber(v, &time) + flowMessage.TimeFlowStart = time / 1000000000 + flowMessage.TimeFlowStartMs = time / 1000000 + case netflow.IPFIX_FIELD_flowEndSeconds: + DecodeUNumber(v, &time) + flowMessage.TimeFlowEnd = time + flowMessage.TimeFlowEndMs = time * 1000 + case netflow.IPFIX_FIELD_flowEndMilliseconds: + DecodeUNumber(v, &time) + flowMessage.TimeFlowEnd = time / 1000 + flowMessage.TimeFlowEndMs = time + case netflow.IPFIX_FIELD_flowEndMicroseconds: + DecodeUNumber(v, &time) + flowMessage.TimeFlowEnd = time / 1000000 + flowMessage.TimeFlowEndMs = time / 1000 + case netflow.IPFIX_FIELD_flowEndNanoseconds: + DecodeUNumber(v, &time) + flowMessage.TimeFlowEnd = time / 1000000000 + flowMessage.TimeFlowEndMs = time / 1000000 + case netflow.IPFIX_FIELD_flowStartDeltaMicroseconds: + DecodeUNumber(v, &time) + flowMessage.TimeFlowStart = uint64(baseTime) - time/1000000 + flowMessage.TimeFlowStartMs = uint64(baseTime)*1000 - time/1000 + case netflow.IPFIX_FIELD_flowEndDeltaMicroseconds: + DecodeUNumber(v, &time) + flowMessage.TimeFlowEnd = uint64(baseTime) - time/1000000 + flowMessage.TimeFlowEndMs = uint64(baseTime)*1000 - time/1000 + // RFC7133 + case netflow.IPFIX_FIELD_dataLinkFrameSize: + DecodeUNumber(v, &(flowMessage.Bytes)) + flowMessage.Packets = 1 + case netflow.IPFIX_FIELD_dataLinkFrameSection: + ParseEthernetHeader(flowMessage, v, mapperSFlow) + flowMessage.Packets = 1 + if flowMessage.Bytes == 0 { + flowMessage.Bytes = uint64(len(v)) + } + } + } + } + + } + + return flowMessage +} + +func SearchNetFlowDataSetsRecords(version uint16, baseTime uint32, uptime uint32, dataRecords []netflow.DataRecord, mapperNetFlow *NetFlowMapper, mapperSFlow *SFlowMapper) []*flowmessage.FlowMessage { + var flowMessageSet []*flowmessage.FlowMessage + for _, record := range dataRecords { + fmsg := ConvertNetFlowDataSet(version, baseTime, uptime, record.Values, mapperNetFlow, mapperSFlow) + if fmsg != nil { + flowMessageSet = append(flowMessageSet, fmsg) + } + } + return flowMessageSet +} + +func SearchNetFlowDataSets(version uint16, baseTime uint32, uptime uint32, dataFlowSet []netflow.DataFlowSet, mapperNetFlow *NetFlowMapper, mapperSFlow *SFlowMapper) []*flowmessage.FlowMessage { + var flowMessageSet []*flowmessage.FlowMessage + for _, dataFlowSetItem := range dataFlowSet { + fmsg := SearchNetFlowDataSetsRecords(version, baseTime, uptime, dataFlowSetItem.Records, mapperNetFlow, mapperSFlow) + if fmsg != nil { + flowMessageSet = append(flowMessageSet, fmsg...) + } + } + return flowMessageSet +} + +func SearchNetFlowOptionDataSets(dataFlowSet []netflow.OptionsDataFlowSet) (uint32, bool) { + var samplingRate uint32 + var found bool + for _, dataFlowSetItem := range dataFlowSet { + for _, record := range dataFlowSetItem.Records { + b := NetFlowPopulate(record.OptionsValues, 305, &samplingRate) + if b { + return samplingRate, b + } + b = NetFlowPopulate(record.OptionsValues, 50, &samplingRate) + if b { + return samplingRate, b + } + b = NetFlowPopulate(record.OptionsValues, 34, &samplingRate) + if b { + return samplingRate, b + } + } + } + return samplingRate, found +} + +func SplitNetFlowSets(packetNFv9 netflow.NFv9Packet) ([]netflow.DataFlowSet, []netflow.TemplateFlowSet, []netflow.NFv9OptionsTemplateFlowSet, []netflow.OptionsDataFlowSet) { + var dataFlowSet []netflow.DataFlowSet + var templatesFlowSet []netflow.TemplateFlowSet + var optionsTemplatesFlowSet []netflow.NFv9OptionsTemplateFlowSet + var optionsDataFlowSet []netflow.OptionsDataFlowSet + for _, flowSet := range packetNFv9.FlowSets { + switch tFlowSet := flowSet.(type) { + case netflow.TemplateFlowSet: + templatesFlowSet = append(templatesFlowSet, tFlowSet) + case netflow.NFv9OptionsTemplateFlowSet: + optionsTemplatesFlowSet = append(optionsTemplatesFlowSet, tFlowSet) + case netflow.DataFlowSet: + dataFlowSet = append(dataFlowSet, tFlowSet) + case netflow.OptionsDataFlowSet: + optionsDataFlowSet = append(optionsDataFlowSet, tFlowSet) + } + } + return dataFlowSet, templatesFlowSet, optionsTemplatesFlowSet, optionsDataFlowSet +} + +func SplitIPFIXSets(packetIPFIX netflow.IPFIXPacket) ([]netflow.DataFlowSet, []netflow.TemplateFlowSet, []netflow.IPFIXOptionsTemplateFlowSet, []netflow.OptionsDataFlowSet) { + var dataFlowSet []netflow.DataFlowSet + var templatesFlowSet []netflow.TemplateFlowSet + var optionsTemplatesFlowSet []netflow.IPFIXOptionsTemplateFlowSet + var optionsDataFlowSet []netflow.OptionsDataFlowSet + for _, flowSet := range packetIPFIX.FlowSets { + switch tFlowSet := flowSet.(type) { + case netflow.TemplateFlowSet: + templatesFlowSet = append(templatesFlowSet, tFlowSet) + case netflow.IPFIXOptionsTemplateFlowSet: + optionsTemplatesFlowSet = append(optionsTemplatesFlowSet, tFlowSet) + case netflow.DataFlowSet: + dataFlowSet = append(dataFlowSet, tFlowSet) + case netflow.OptionsDataFlowSet: + optionsDataFlowSet = append(optionsDataFlowSet, tFlowSet) + } + } + return dataFlowSet, templatesFlowSet, optionsTemplatesFlowSet, optionsDataFlowSet +} + +func ProcessMessageNetFlow(msgDec interface{}, samplingRateSys SamplingRateSystem) ([]*flowmessage.FlowMessage, error) { + return ProcessMessageNetFlowConfig(msgDec, samplingRateSys, nil) +} + +// Convert a NetFlow datastructure to a FlowMessage protobuf +// Does not put sampling rate +func ProcessMessageNetFlowConfig(msgDec interface{}, samplingRateSys SamplingRateSystem, config *ProducerConfigMapped) ([]*flowmessage.FlowMessage, error) { + seqnum := uint32(0) + var baseTime uint32 + var uptime uint32 + + var flowMessageSet []*flowmessage.FlowMessage + + switch msgDecConv := msgDec.(type) { + case netflow.NFv9Packet: + dataFlowSet, _, _, optionDataFlowSet := SplitNetFlowSets(msgDecConv) + + seqnum = msgDecConv.SequenceNumber + baseTime = msgDecConv.UnixSeconds + uptime = msgDecConv.SystemUptime + obsDomainId := msgDecConv.SourceId + + var cfg *NetFlowMapper + if config != nil { + cfg = config.NetFlowV9 + } + flowMessageSet = SearchNetFlowDataSets(9, baseTime, uptime, dataFlowSet, cfg, nil) + samplingRate, found := SearchNetFlowOptionDataSets(optionDataFlowSet) + if samplingRateSys != nil { + if found { + samplingRateSys.AddSamplingRate(9, obsDomainId, samplingRate) + } else { + samplingRate, _ = samplingRateSys.GetSamplingRate(9, obsDomainId) + } + } + for _, fmsg := range flowMessageSet { + fmsg.SequenceNum = seqnum + fmsg.SamplingRate = uint64(samplingRate) + } + case netflow.IPFIXPacket: + dataFlowSet, _, _, optionDataFlowSet := SplitIPFIXSets(msgDecConv) + + seqnum = msgDecConv.SequenceNumber + baseTime = msgDecConv.ExportTime + obsDomainId := msgDecConv.ObservationDomainId + + var cfgIpfix *NetFlowMapper + var cfgSflow *SFlowMapper + if config != nil { + cfgIpfix = config.IPFIX + cfgSflow = config.SFlow + } + flowMessageSet = SearchNetFlowDataSets(10, baseTime, uptime, dataFlowSet, cfgIpfix, cfgSflow) + + samplingRate, found := SearchNetFlowOptionDataSets(optionDataFlowSet) + if samplingRateSys != nil { + if found { + samplingRateSys.AddSamplingRate(10, obsDomainId, samplingRate) + } else { + samplingRate, _ = samplingRateSys.GetSamplingRate(10, obsDomainId) + } + } + for _, fmsg := range flowMessageSet { + fmsg.SequenceNum = seqnum + fmsg.SamplingRate = uint64(samplingRate) + fmsg.ObservationDomainId = obsDomainId + } + default: + return flowMessageSet, errors.New("Bad NetFlow/IPFIX version") + } + + return flowMessageSet, nil +} diff --git a/third_party/goflow2/producer/producer_nflegacy.go b/third_party/goflow2/producer/producer_nflegacy.go new file mode 100644 index 000000000000..8acb3eeaa5ce --- /dev/null +++ b/third_party/goflow2/producer/producer_nflegacy.go @@ -0,0 +1,81 @@ +package producer + +import ( + "encoding/binary" + "errors" + "net" + + "github.com/netsampler/goflow2/decoders/netflowlegacy" + flowmessage "github.com/netsampler/goflow2/pb" +) + +func ConvertNetFlowLegacyRecord(baseTime uint32, uptime uint32, record netflowlegacy.RecordsNetFlowV5) *flowmessage.FlowMessage { + flowMessage := &flowmessage.FlowMessage{} + + flowMessage.Type = flowmessage.FlowMessage_NETFLOW_V5 + + timeDiffFirst := (uptime - record.First) + timeDiffLast := (uptime - record.Last) + flowMessage.TimeFlowStart = uint64(baseTime - timeDiffFirst/1000) + flowMessage.TimeFlowStartMs = uint64(baseTime)*1000 - uint64(timeDiffFirst) + flowMessage.TimeFlowEnd = uint64(baseTime - timeDiffLast/1000) + flowMessage.TimeFlowEndMs = uint64(baseTime)*1000 - uint64(timeDiffLast) + + v := make(net.IP, 4) + binary.BigEndian.PutUint32(v, record.NextHop) + flowMessage.NextHop = v + v = make(net.IP, 4) + binary.BigEndian.PutUint32(v, record.SrcAddr) + flowMessage.SrcAddr = v + v = make(net.IP, 4) + binary.BigEndian.PutUint32(v, record.DstAddr) + flowMessage.DstAddr = v + + flowMessage.Etype = 0x800 + flowMessage.SrcAs = uint32(record.SrcAS) + flowMessage.DstAs = uint32(record.DstAS) + flowMessage.SrcNet = uint32(record.SrcMask) + flowMessage.DstNet = uint32(record.DstMask) + flowMessage.Proto = uint32(record.Proto) + flowMessage.TcpFlags = uint32(record.TCPFlags) + flowMessage.IpTos = uint32(record.Tos) + flowMessage.InIf = uint32(record.Input) + flowMessage.OutIf = uint32(record.Output) + flowMessage.SrcPort = uint32(record.SrcPort) + flowMessage.DstPort = uint32(record.DstPort) + flowMessage.Packets = uint64(record.DPkts) + flowMessage.Bytes = uint64(record.DOctets) + + return flowMessage +} + +func SearchNetFlowLegacyRecords(baseTime uint32, uptime uint32, dataRecords []netflowlegacy.RecordsNetFlowV5) []*flowmessage.FlowMessage { + var flowMessageSet []*flowmessage.FlowMessage + for _, record := range dataRecords { + fmsg := ConvertNetFlowLegacyRecord(baseTime, uptime, record) + if fmsg != nil { + flowMessageSet = append(flowMessageSet, fmsg) + } + } + return flowMessageSet +} + +func ProcessMessageNetFlowLegacy(msgDec interface{}) ([]*flowmessage.FlowMessage, error) { + switch packet := msgDec.(type) { + case netflowlegacy.PacketNetFlowV5: + seqnum := packet.FlowSequence + samplingRate := packet.SamplingInterval + baseTime := packet.UnixSecs + uptime := packet.SysUptime + + flowMessageSet := SearchNetFlowLegacyRecords(baseTime, uptime, packet.Records) + for _, fmsg := range flowMessageSet { + fmsg.SequenceNum = seqnum + fmsg.SamplingRate = uint64(samplingRate) + } + + return flowMessageSet, nil + default: + return []*flowmessage.FlowMessage{}, errors.New("Bad NetFlow v5 version") + } +} diff --git a/third_party/goflow2/producer/producer_sf.go b/third_party/goflow2/producer/producer_sf.go new file mode 100644 index 000000000000..5ff5a871dec0 --- /dev/null +++ b/third_party/goflow2/producer/producer_sf.go @@ -0,0 +1,349 @@ +package producer + +import ( + "encoding/binary" + "errors" + "net" + + "github.com/netsampler/goflow2/decoders/sflow" + flowmessage "github.com/netsampler/goflow2/pb" +) + +func GetSFlowFlowSamples(packet *sflow.Packet) []interface{} { + var flowSamples []interface{} + for _, sample := range packet.Samples { + switch sample.(type) { + case sflow.FlowSample: + flowSamples = append(flowSamples, sample) + case sflow.ExpandedFlowSample: + flowSamples = append(flowSamples, sample) + } + } + return flowSamples +} + +func ParseSampledHeader(flowMessage *flowmessage.FlowMessage, sampledHeader *sflow.SampledHeader) error { + return ParseSampledHeaderConfig(flowMessage, sampledHeader, nil) +} + +func ParseEthernetHeader(flowMessage *flowmessage.FlowMessage, data []byte, config *SFlowMapper) { + var hasMpls bool + var countMpls uint32 + var firstLabelMpls uint32 + var firstTtlMpls uint8 + var secondLabelMpls uint32 + var secondTtlMpls uint8 + var thirdLabelMpls uint32 + var thirdTtlMpls uint8 + var lastLabelMpls uint32 + var lastTtlMpls uint8 + + var nextHeader byte + var tcpflags byte + srcIP := net.IP{} + dstIP := net.IP{} + offset := 14 + + var srcMac uint64 + var dstMac uint64 + + var tos byte + var ttl byte + var identification uint16 + var fragOffset uint16 + var flowLabel uint32 + + var srcPort uint16 + var dstPort uint16 + + for _, configLayer := range GetSFlowConfigLayer(config, 0) { + extracted := GetBytes(data, configLayer.Offset, configLayer.Length) + MapCustom(flowMessage, extracted, configLayer.Destination, configLayer.Endian) + } + + etherType := data[12:14] + + dstMac = binary.BigEndian.Uint64(append([]byte{0, 0}, data[0:6]...)) + srcMac = binary.BigEndian.Uint64(append([]byte{0, 0}, data[6:12]...)) + (*flowMessage).SrcMac = srcMac + (*flowMessage).DstMac = dstMac + + encap := true + iterations := 0 + for encap && iterations <= 1 { + encap = false + + if etherType[0] == 0x81 && etherType[1] == 0x0 { // VLAN 802.1Q + (*flowMessage).VlanId = uint32(binary.BigEndian.Uint16(data[14:16])) + offset += 4 + etherType = data[16:18] + } + + if etherType[0] == 0x88 && etherType[1] == 0x47 { // MPLS + iterateMpls := true + hasMpls = true + for iterateMpls { + if len(data) < offset+5 { + iterateMpls = false + break + } + label := binary.BigEndian.Uint32(append([]byte{0}, data[offset:offset+3]...)) >> 4 + //exp := data[offset+2] > 1 + bottom := data[offset+2] & 1 + mplsTtl := data[offset+3] + offset += 4 + + if bottom == 1 || label <= 15 || offset > len(data) { + if data[offset]&0xf0>>4 == 4 { + etherType = []byte{0x8, 0x0} + } else if data[offset]&0xf0>>4 == 6 { + etherType = []byte{0x86, 0xdd} + } + iterateMpls = false + } + + if countMpls == 0 { + firstLabelMpls = label + firstTtlMpls = mplsTtl + } else if countMpls == 1 { + secondLabelMpls = label + secondTtlMpls = mplsTtl + } else if countMpls == 2 { + thirdLabelMpls = label + thirdTtlMpls = mplsTtl + } else { + lastLabelMpls = label + lastTtlMpls = mplsTtl + } + countMpls++ + } + } + + for _, configLayer := range GetSFlowConfigLayer(config, 3) { + extracted := GetBytes(data, offset*8+configLayer.Offset, configLayer.Length) + MapCustom(flowMessage, extracted, configLayer.Destination, configLayer.Endian) + } + + if etherType[0] == 0x8 && etherType[1] == 0x0 { // IPv4 + if len(data) >= offset+20 { + nextHeader = data[offset+9] + srcIP = data[offset+12 : offset+16] + dstIP = data[offset+16 : offset+20] + tos = data[offset+1] + ttl = data[offset+8] + + identification = binary.BigEndian.Uint16(data[offset+4 : offset+6]) + fragOffset = binary.BigEndian.Uint16(data[offset+6 : offset+8]) + + offset += 20 + } + } else if etherType[0] == 0x86 && etherType[1] == 0xdd { // IPv6 + if len(data) >= offset+40 { + nextHeader = data[offset+6] + srcIP = data[offset+8 : offset+24] + dstIP = data[offset+24 : offset+40] + + tostmp := uint32(binary.BigEndian.Uint16(data[offset : offset+2])) + tos = uint8(tostmp & 0x0ff0 >> 4) + ttl = data[offset+7] + + flowLabel = binary.BigEndian.Uint32(data[offset : offset+4]) + + offset += 40 + + } + } else if etherType[0] == 0x8 && etherType[1] == 0x6 { // ARP + } /*else { + return errors.New(fmt.Sprintf("Unknown EtherType: %v\n", etherType)) + } */ + + for _, configLayer := range GetSFlowConfigLayer(config, 4) { + extracted := GetBytes(data, offset*8+configLayer.Offset, configLayer.Length) + MapCustom(flowMessage, extracted, configLayer.Destination, configLayer.Endian) + } + + appOffset := 0 + if len(data) >= offset+4 && (nextHeader == 17 || nextHeader == 6) { + srcPort = binary.BigEndian.Uint16(data[offset+0 : offset+2]) + dstPort = binary.BigEndian.Uint16(data[offset+2 : offset+4]) + } + + if nextHeader == 17 { + appOffset = 8 + } + + if len(data) > offset+13 && nextHeader == 6 { + tcpflags = data[offset+13] + + appOffset = int(data[13]>>4) * 4 + } + + // ICMP and ICMPv6 + if len(data) >= offset+2 && (nextHeader == 1 || nextHeader == 58) { + (*flowMessage).IcmpType = uint32(data[offset+0]) + (*flowMessage).IcmpCode = uint32(data[offset+1]) + } + + if appOffset > 0 { + for _, configLayer := range GetSFlowConfigLayer(config, 7) { + extracted := GetBytes(data, (offset+appOffset)*8+configLayer.Offset, configLayer.Length) + MapCustom(flowMessage, extracted, configLayer.Destination, configLayer.Endian) + } + } + + iterations++ + } + + (*flowMessage).HasMpls = hasMpls + (*flowMessage).MplsCount = countMpls + (*flowMessage).Mpls_1Label = firstLabelMpls + (*flowMessage).Mpls_1Ttl = uint32(firstTtlMpls) + (*flowMessage).Mpls_2Label = secondLabelMpls + (*flowMessage).Mpls_2Ttl = uint32(secondTtlMpls) + (*flowMessage).Mpls_3Label = thirdLabelMpls + (*flowMessage).Mpls_3Ttl = uint32(thirdTtlMpls) + (*flowMessage).MplsLastLabel = lastLabelMpls + (*flowMessage).MplsLastTtl = uint32(lastTtlMpls) + + (*flowMessage).Etype = uint32(binary.BigEndian.Uint16(etherType[0:2])) + (*flowMessage).Ipv6FlowLabel = flowLabel & 0xFFFFF + + (*flowMessage).SrcPort = uint32(srcPort) + (*flowMessage).DstPort = uint32(dstPort) + + (*flowMessage).SrcAddr = srcIP + (*flowMessage).DstAddr = dstIP + (*flowMessage).Proto = uint32(nextHeader) + (*flowMessage).IpTos = uint32(tos) + (*flowMessage).IpTtl = uint32(ttl) + (*flowMessage).TcpFlags = uint32(tcpflags) + + (*flowMessage).FragmentId = uint32(identification) + (*flowMessage).FragmentOffset = uint32(fragOffset) +} + +func ParseSampledHeaderConfig(flowMessage *flowmessage.FlowMessage, sampledHeader *sflow.SampledHeader, config *SFlowMapper) error { + data := (*sampledHeader).HeaderData + switch (*sampledHeader).Protocol { + case 1: // Ethernet + ParseEthernetHeader(flowMessage, data, config) + } + return nil +} + +func SearchSFlowSamples(samples []interface{}) []*flowmessage.FlowMessage { + return SearchSFlowSamples(samples) +} + +func SearchSFlowSamplesConfig(samples []interface{}, config *SFlowMapper) []*flowmessage.FlowMessage { + var flowMessageSet []*flowmessage.FlowMessage + + for _, flowSample := range samples { + var records []sflow.FlowRecord + + flowMessage := &flowmessage.FlowMessage{} + flowMessage.Type = flowmessage.FlowMessage_SFLOW_5 + + switch flowSample := flowSample.(type) { + case sflow.FlowSample: + records = flowSample.Records + flowMessage.SamplingRate = uint64(flowSample.SamplingRate) + flowMessage.InIf = flowSample.Input + flowMessage.OutIf = flowSample.Output + case sflow.ExpandedFlowSample: + records = flowSample.Records + flowMessage.SamplingRate = uint64(flowSample.SamplingRate) + flowMessage.InIf = flowSample.InputIfValue + flowMessage.OutIf = flowSample.OutputIfValue + } + + ipNh := net.IP{} + ipSrc := net.IP{} + ipDst := net.IP{} + flowMessage.Packets = 1 + for _, record := range records { + switch recordData := record.Data.(type) { + case sflow.SampledHeader: + flowMessage.Bytes = uint64(recordData.FrameLength) + ParseSampledHeaderConfig(flowMessage, &recordData, config) + case sflow.SampledIPv4: + ipSrc = recordData.Base.SrcIP + ipDst = recordData.Base.DstIP + flowMessage.SrcAddr = ipSrc + flowMessage.DstAddr = ipDst + flowMessage.Bytes = uint64(recordData.Base.Length) + flowMessage.Proto = recordData.Base.Protocol + flowMessage.SrcPort = recordData.Base.SrcPort + flowMessage.DstPort = recordData.Base.DstPort + flowMessage.IpTos = recordData.Tos + flowMessage.Etype = 0x800 + case sflow.SampledIPv6: + ipSrc = recordData.Base.SrcIP + ipDst = recordData.Base.DstIP + flowMessage.SrcAddr = ipSrc + flowMessage.DstAddr = ipDst + flowMessage.Bytes = uint64(recordData.Base.Length) + flowMessage.Proto = recordData.Base.Protocol + flowMessage.SrcPort = recordData.Base.SrcPort + flowMessage.DstPort = recordData.Base.DstPort + flowMessage.IpTos = recordData.Priority + flowMessage.Etype = 0x86dd + case sflow.ExtendedRouter: + ipNh = recordData.NextHop + flowMessage.NextHop = ipNh + flowMessage.SrcNet = recordData.SrcMaskLen + flowMessage.DstNet = recordData.DstMaskLen + case sflow.ExtendedGateway: + ipNh = recordData.NextHop + flowMessage.BgpNextHop = ipNh + flowMessage.BgpCommunities = recordData.Communities + flowMessage.AsPath = recordData.ASPath + if len(recordData.ASPath) > 0 { + flowMessage.DstAs = recordData.ASPath[len(recordData.ASPath)-1] + flowMessage.NextHopAs = recordData.ASPath[0] + } else { + flowMessage.DstAs = recordData.AS + } + if recordData.SrcAS > 0 { + flowMessage.SrcAs = recordData.SrcAS + } else { + flowMessage.SrcAs = recordData.AS + } + case sflow.ExtendedSwitch: + flowMessage.SrcVlan = recordData.SrcVlan + flowMessage.DstVlan = recordData.DstVlan + } + } + flowMessageSet = append(flowMessageSet, flowMessage) + } + return flowMessageSet +} + +func ProcessMessageSFlow(msgDec interface{}) ([]*flowmessage.FlowMessage, error) { + return ProcessMessageSFlowConfig(msgDec, nil) +} + +func ProcessMessageSFlowConfig(msgDec interface{}, config *ProducerConfigMapped) ([]*flowmessage.FlowMessage, error) { + switch packet := msgDec.(type) { + case sflow.Packet: + seqnum := packet.SequenceNumber + var agent net.IP + agent = packet.AgentIP + + var cfg *SFlowMapper + if config != nil { + cfg = config.SFlow + } + + flowSamples := GetSFlowFlowSamples(&packet) + flowMessageSet := SearchSFlowSamplesConfig(flowSamples, cfg) + for _, fmsg := range flowMessageSet { + fmsg.SamplerAddress = agent + fmsg.SequenceNum = seqnum + } + + return flowMessageSet, nil + default: + return []*flowmessage.FlowMessage{}, errors.New("Bad sFlow version") + } +} diff --git a/third_party/goflow2/producer/producer_test.go b/third_party/goflow2/producer/producer_test.go new file mode 100644 index 000000000000..f3e622f164eb --- /dev/null +++ b/third_party/goflow2/producer/producer_test.go @@ -0,0 +1,191 @@ +package producer + +import ( + "testing" + + "github.com/netsampler/goflow2/decoders/netflow" + "github.com/netsampler/goflow2/decoders/sflow" + "github.com/stretchr/testify/assert" +) + +func TestProcessMessageNetFlow(t *testing.T) { + records := []netflow.DataRecord{ + netflow.DataRecord{ + Values: []netflow.DataField{ + netflow.DataField{ + Type: netflow.NFV9_FIELD_IPV4_SRC_ADDR, + Value: []byte{10, 0, 0, 1}, + }, + }, + }, + } + dfs := []interface{}{ + netflow.DataFlowSet{ + Records: records, + }, + } + + pktnf9 := netflow.NFv9Packet{ + FlowSets: dfs, + } + testsr := &SingleSamplingRateSystem{1} + _, err := ProcessMessageNetFlow(pktnf9, testsr) + assert.Nil(t, err) + + pktipfix := netflow.IPFIXPacket{ + FlowSets: dfs, + } + _, err = ProcessMessageNetFlow(pktipfix, testsr) + assert.Nil(t, err) +} + +func TestProcessMessageSFlow(t *testing.T) { + sh := sflow.SampledHeader{ + FrameLength: 10, + Protocol: 1, + HeaderData: []byte{ + 0xff, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xff, 0xab, 0xcd, 0xef, 0xab, 0xbc, 0x86, 0xdd, 0x60, 0x2e, + 0xc4, 0xec, 0x01, 0xcc, 0x06, 0x40, 0xfd, 0x01, 0x00, 0x00, 0xff, 0x01, 0x82, 0x10, 0xcd, 0xff, + 0xff, 0x1c, 0x00, 0x00, 0x01, 0x50, 0xfd, 0x01, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x02, 0xff, + 0xff, 0x93, 0x00, 0x00, 0x02, 0x46, 0xcf, 0xca, 0x00, 0x50, 0x05, 0x15, 0x21, 0x6f, 0xa4, 0x9c, + 0xf4, 0x59, 0x80, 0x18, 0x08, 0x09, 0x8c, 0x86, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x2a, 0x85, + 0xee, 0x9e, 0x64, 0x5c, 0x27, 0x28, + }, + } + pkt := sflow.Packet{ + Version: 5, + Samples: []interface{}{ + sflow.FlowSample{ + SamplingRate: 1, + Records: []sflow.FlowRecord{ + sflow.FlowRecord{ + Data: sh, + }, + }, + }, + sflow.ExpandedFlowSample{ + SamplingRate: 1, + Records: []sflow.FlowRecord{ + sflow.FlowRecord{ + Data: sh, + }, + }, + }, + }, + } + _, err := ProcessMessageSFlow(pkt) + assert.Nil(t, err) +} + +func TestExpandedSFlowDecode(t *testing.T) { + flowMessages, err := ProcessMessageSFlow(getSflowPacket()) + flowMessage := flowMessages[0] + + assert.Nil(t, err) + + assert.Equal(t, []byte{0x05, 0x05, 0x05, 0x05}, flowMessage.BgpNextHop) + assert.Equal(t, []uint32{3936619448, 3936619708, 3936623548}, flowMessage.BgpCommunities) + assert.Equal(t, []uint32{456}, flowMessage.AsPath) + assert.Equal(t, []byte{0x09, 0x09, 0x09, 0x09}, flowMessage.NextHop) +} + +func getSflowPacket() sflow.Packet { + return sflow.Packet{ + Version: 5, + IPVersion: 1, + AgentIP: []uint8{1, 2, 3, 4}, + SubAgentId: 0, + SequenceNumber: 3178205882, + Uptime: 3011091704, + SamplesCount: 1, + Samples: []interface{}{ + sflow.FlowSample{ + Header: sflow.SampleHeader{ + Format: 1, + Length: 662, + SampleSequenceNumber: 2757962272, + SourceIdType: 0, + SourceIdValue: 1000100, + }, + SamplingRate: 16383, + SamplePool: 639948256, + Drops: 0, + Input: 1000100, + Output: 1000005, + FlowRecordsCount: 4, + Records: []sflow.FlowRecord{ + sflow.FlowRecord{ + Header: sflow.RecordHeader{ + DataFormat: 1001, + Length: 16, + }, + Data: sflow.ExtendedSwitch{ + SrcVlan: 952, + SrcPriority: 0, + DstVlan: 952, + DstPriority: 0, + }, + }, + sflow.FlowRecord{ + Header: sflow.RecordHeader{ + DataFormat: 1, + Length: 144, + }, + Data: sflow.SampledHeader{ + Protocol: 1, + FrameLength: 1522, + Stripped: 4, + OriginalLength: 128, + HeaderData: []byte{ + 0x74, 0x83, 0xef, 0x2e, 0xc3, 0xc5, 0xac, 0x1f, 0x6b, 0x2c, 0x43, 0x36, 0x81, 0x00, 0x03, 0xb8, + 0x08, 0x00, 0x45, 0x00, 0x05, 0xdc, 0x59, 0xa5, 0x40, 0x00, 0x40, 0x06, 0x0a, 0xb8, 0xb9, 0x3b, + 0xdf, 0xb6, 0x32, 0x44, 0x05, 0x89, 0x23, 0x78, 0xc9, 0x06, 0x24, 0x6c, 0x0b, 0xf4, 0xd9, 0xce, + 0x9c, 0x66, 0x50, 0x10, 0x00, 0x1e, 0x29, 0x8a, 0x00, 0x00, 0xb4, 0x7e, 0xb7, 0xfd, 0x16, 0x3e, + 0x19, 0x97, 0xa8, 0xb4, 0x2a, 0xf7, 0x49, 0x96, 0xf4, 0x0e, 0xef, 0xa7, 0x55, 0x93, 0x27, 0x6f, + 0x1e, 0x20, 0xe1, 0x04, 0x2f, 0x36, 0x18, 0xfe, 0x7b, 0x88, 0x1f, 0xc9, 0x57, 0xbc, 0x71, 0x43, + 0x3d, 0x1c, 0x6c, 0xb0, 0x3d, 0xf7, 0x51, 0x48, 0x68, 0x94, 0x47, 0x00, 0xd3, 0x1a, 0x9d, 0xdb, + 0x2f, 0x1e, 0x39, 0xcf, 0xfd, 0x96, 0x79, 0xdf, 0xb0, 0x2d, 0x02, 0x6e, 0x72, 0xf5, 0x29, 0x73, + }, + }, + }, + sflow.FlowRecord{ + Header: sflow.RecordHeader{ + DataFormat: 1003, + Length: 56, + }, + Data: sflow.ExtendedGateway{ + NextHopIPVersion: 1, + NextHop: []uint8{5, 5, 5, 5}, + AS: 123, + SrcAS: 0, + SrcPeerAS: 0, + ASDestinations: 1, + ASPathType: 2, + ASPathLength: 1, + ASPath: []uint32{456}, + CommunitiesLength: 3, + Communities: []uint32{ + 3936619448, + 3936619708, + 3936623548, + }, + LocalPref: 170, + }, + }, + sflow.FlowRecord{ + Header: sflow.RecordHeader{ + DataFormat: 1002, + Length: 16, + }, + Data: sflow.ExtendedRouter{ + NextHopIPVersion: 1, + NextHop: []uint8{9, 9, 9, 9}, + SrcMaskLen: 26, + DstMaskLen: 22, + }, + }, + }, + }, + }, + } +} diff --git a/third_party/goflow2/producer/reflect.go b/third_party/goflow2/producer/reflect.go new file mode 100644 index 000000000000..91a2a415cc79 --- /dev/null +++ b/third_party/goflow2/producer/reflect.go @@ -0,0 +1,233 @@ +package producer + +import ( + "fmt" + "reflect" + + "github.com/netsampler/goflow2/decoders/netflow" + flowmessage "github.com/netsampler/goflow2/pb" +) + +type EndianType string + +var ( + BigEndian EndianType = "big" + LittleEndian EndianType = "little" +) + +func GetBytes(d []byte, offset int, length int) []byte { + if length == 0 { + return nil + } + leftBytes := offset / 8 + rightBytes := (offset + length) / 8 + if (offset+length)%8 != 0 { + rightBytes += 1 + } + if leftBytes >= len(d) { + return nil + } + if rightBytes > len(d) { + rightBytes = len(d) + } + chunk := make([]byte, rightBytes-leftBytes) + + offsetMod8 := (offset % 8) + shiftAnd := byte(0xff >> (8 - offsetMod8)) + + var shifted byte + for i := range chunk { + j := len(chunk) - 1 - i + cur := d[j+leftBytes] + chunk[j] = (cur << offsetMod8) | shifted + shifted = shiftAnd & cur + } + last := len(chunk) - 1 + shiftAndLast := byte(0xff << ((8 - ((offset + length) % 8)) % 8)) + chunk[last] = chunk[last] & shiftAndLast + return chunk +} + +func IsUInt(k reflect.Kind) bool { + return k == reflect.Uint8 || k == reflect.Uint16 || k == reflect.Uint32 || k == reflect.Uint64 +} + +func IsInt(k reflect.Kind) bool { + return k == reflect.Int8 || k == reflect.Int16 || k == reflect.Int32 || k == reflect.Int64 +} + +func MapCustomNetFlow(flowMessage *flowmessage.FlowMessage, df netflow.DataField, mapper *NetFlowMapper) { + if mapper == nil { + return + } + mapped, ok := mapper.Map(df) + if ok { + v := df.Value.([]byte) + MapCustom(flowMessage, v, mapped.Destination, mapped.Endian) + } +} + +func MapCustom(flowMessage *flowmessage.FlowMessage, v []byte, destination string, endianness EndianType) { + vfm := reflect.ValueOf(flowMessage) + vfm = reflect.Indirect(vfm) + + fieldValue := vfm.FieldByName(destination) + + if fieldValue.IsValid() { + typeDest := fieldValue.Type() + fieldValueAddr := fieldValue.Addr() + + if typeDest.Kind() == reflect.Slice { + + if typeDest.Elem().Kind() == reflect.Uint8 { + fieldValue.SetBytes(v) + } else { + item := reflect.New(typeDest.Elem()) + + if IsUInt(typeDest.Elem().Kind()) { + if endianness == LittleEndian { + DecodeUNumberLE(v, item.Interface()) + } else { + DecodeUNumber(v, item.Interface()) + } + } else if IsUInt(typeDest.Elem().Kind()) { + if endianness == LittleEndian { + DecodeUNumberLE(v, item.Interface()) + } else { + DecodeUNumber(v, item.Interface()) + } + } + + itemi := reflect.Indirect(item) + tmpFieldValue := reflect.Append(fieldValue, itemi) + fieldValue.Set(tmpFieldValue) + } + + } else if fieldValueAddr.IsValid() && IsUInt(typeDest.Kind()) { + if endianness == LittleEndian { + DecodeUNumberLE(v, fieldValueAddr.Interface()) + } else { + DecodeUNumber(v, fieldValueAddr.Interface()) + } + } else if fieldValueAddr.IsValid() && IsInt(typeDest.Kind()) { + if endianness == LittleEndian { + DecodeUNumberLE(v, fieldValueAddr.Interface()) + } else { + DecodeUNumber(v, fieldValueAddr.Interface()) + } + } + } +} + +type NetFlowMapField struct { + PenProvided bool `json:"penprovided" yaml:"penprovided"` + Type uint16 `json:"field" yaml:"field"` + Pen uint32 `json:"pen" yaml:"pen"` + + Destination string `json:"destination" yaml:"destination"` + Endian EndianType `json:"endianness" yaml:"endianness"` + //DestinationLength uint8 `json:"dlen"` // could be used if populating a slice of uint16 that aren't in protobuf +} + +type IPFIXProducerConfig struct { + Mapping []NetFlowMapField `json:"mapping"` + //PacketMapping []SFlowMapField `json:"packet-mapping"` // for embedded frames: use sFlow configuration +} + +type NetFlowV9ProducerConfig struct { + Mapping []NetFlowMapField `json:"mapping"` +} + +type SFlowMapField struct { + Layer int `json:"layer"` + Offset int `json:"offset"` // offset in bits + Length int `json:"length"` // length in bits + + Destination string `json:"destination" yaml:"destination"` + Endian EndianType `json:"endianness" yaml:"endianness"` + //DestinationLength uint8 `json:"dlen"` +} + +type SFlowProducerConfig struct { + Mapping []SFlowMapField `json:"mapping"` +} + +type ProducerConfig struct { + IPFIX IPFIXProducerConfig `json:"ipfix"` + NetFlowV9 NetFlowV9ProducerConfig `json:"netflowv9"` + SFlow SFlowProducerConfig `json:"sflow"` // also used for IPFIX data frames + + // should do a rename map list for when printing +} + +type DataMap struct { + Destination string + Endian EndianType +} + +type NetFlowMapper struct { + data map[string]DataMap // maps field to destination +} + +func (m *NetFlowMapper) Map(field netflow.DataField) (DataMap, bool) { + mapped, found := m.data[fmt.Sprintf("%v-%d-%d", field.PenProvided, field.Pen, field.Type)] + return mapped, found +} + +func MapFieldsNetFlow(fields []NetFlowMapField) *NetFlowMapper { + ret := make(map[string]DataMap) + for _, field := range fields { + ret[fmt.Sprintf("%v-%d-%d", field.PenProvided, field.Pen, field.Type)] = DataMap{Destination: field.Destination, Endian: field.Endian} + } + return &NetFlowMapper{ret} +} + +type DataMapLayer struct { + Offset int + Length int + Destination string + Endian EndianType +} + +type SFlowMapper struct { + data map[int][]DataMapLayer // map layer to list of offsets +} + +func GetSFlowConfigLayer(m *SFlowMapper, layer int) []DataMapLayer { + if m == nil { + return nil + } + return m.data[layer] +} + +func MapFieldsSFlow(fields []SFlowMapField) *SFlowMapper { + ret := make(map[int][]DataMapLayer) + for _, field := range fields { + retLayerEntry := DataMapLayer{ + Offset: field.Offset, + Length: field.Length, + Destination: field.Destination, + Endian: field.Endian, + } + retLayer := ret[field.Layer] + retLayer = append(retLayer, retLayerEntry) + ret[field.Layer] = retLayer + } + return &SFlowMapper{ret} +} + +type ProducerConfigMapped struct { + IPFIX *NetFlowMapper `json:"ipfix"` + NetFlowV9 *NetFlowMapper `json:"netflowv9"` + SFlow *SFlowMapper `json:"sflow"` +} + +func NewProducerConfigMapped(config *ProducerConfig) *ProducerConfigMapped { + newCfg := &ProducerConfigMapped{} + if config != nil { + newCfg.IPFIX = MapFieldsNetFlow(config.IPFIX.Mapping) + newCfg.NetFlowV9 = MapFieldsNetFlow(config.NetFlowV9.Mapping) + newCfg.SFlow = MapFieldsSFlow(config.SFlow.Mapping) + } + return newCfg +} diff --git a/third_party/goflow2/transport/file/transport.go b/third_party/goflow2/transport/file/transport.go new file mode 100644 index 000000000000..5143c8b0b636 --- /dev/null +++ b/third_party/goflow2/transport/file/transport.go @@ -0,0 +1,101 @@ +package file + +import ( + "context" + "flag" + "fmt" + "github.com/netsampler/goflow2/transport" + "io" + "os" + "os/signal" + "sync" + "syscall" +) + +type FileDriver struct { + fileDestination string + lineSeparator string + w io.Writer + file *os.File + lock *sync.RWMutex + q chan bool +} + +func (d *FileDriver) Prepare() error { + flag.StringVar(&d.fileDestination, "transport.file", "", "File/console output (empty for stdout)") + flag.StringVar(&d.lineSeparator, "transport.file.sep", "\n", "Line separator") + // idea: add terminal coloring based on key partitioning (if any) + return nil +} + +func (d *FileDriver) openFile() error { + file, err := os.OpenFile(d.fileDestination, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + d.file = file + d.w = d.file + return err +} + +func (d *FileDriver) Init(context.Context) error { + d.q = make(chan bool, 1) + + if d.fileDestination == "" { + d.w = os.Stdout + } else { + var err error + + d.lock.Lock() + err = d.openFile() + d.lock.Unlock() + if err != nil { + return err + } + + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGHUP) + go func() { + for { + select { + case <-c: + d.lock.Lock() + d.file.Close() + d.openFile() + d.lock.Unlock() + // if there is an error, keeps using the old file + case <-d.q: + return + } + } + }() + + } + return nil +} + +func (d *FileDriver) Send(key, data []byte) error { + d.lock.RLock() + w := d.w + d.lock.RUnlock() + _, err := fmt.Fprint(w, string(data)+d.lineSeparator) + return err +} + +func (d *FileDriver) Close(context.Context) error { + if d.fileDestination != "" { + d.lock.Lock() + d.file.Close() + d.lock.Unlock() + signal.Ignore(syscall.SIGHUP) + } + close(d.q) + return nil +} + +func init() { + d := &FileDriver{ + lock: &sync.RWMutex{}, + } + transport.RegisterTransportDriver("file", d) +} diff --git a/third_party/goflow2/transport/kafka/kafka.go b/third_party/goflow2/transport/kafka/kafka.go new file mode 100644 index 000000000000..702ea5c60b8b --- /dev/null +++ b/third_party/goflow2/transport/kafka/kafka.go @@ -0,0 +1,226 @@ +package kafka + +import ( + "context" + "crypto/tls" + "crypto/x509" + "errors" + "flag" + "fmt" + "os" + "strings" + "time" + + sarama "github.com/Shopify/sarama" + "github.com/netsampler/goflow2/transport" + "github.com/netsampler/goflow2/utils" + + log "github.com/sirupsen/logrus" +) + +type KafkaDriver struct { + kafkaTLS bool + kafkaSASL string + kafkaSCRAM string + kafkaTopic string + kafkaSrv string + kafkaBrk string + kafkaMaxMsgBytes int + kafkaFlushBytes int + kafkaFlushFrequency time.Duration + + kafkaLogErrors bool + + kafkaHashing bool + kafkaVersion string + kafkaCompressionCodec string + + producer sarama.AsyncProducer + + q chan bool +} + +type KafkaSASLAlgorithm string + +const ( + KAFKA_SASL_NONE KafkaSASLAlgorithm = "none" + KAFKA_SASL_PLAIN KafkaSASLAlgorithm = "plain" + KAFKA_SASL_SCRAM_SHA256 KafkaSASLAlgorithm = "scram-sha256" + KAFKA_SASL_SCRAM_SHA512 KafkaSASLAlgorithm = "scram-sha512" +) + +var ( + compressionCodecs = map[string]sarama.CompressionCodec{ + strings.ToLower(sarama.CompressionNone.String()): sarama.CompressionNone, + strings.ToLower(sarama.CompressionGZIP.String()): sarama.CompressionGZIP, + strings.ToLower(sarama.CompressionSnappy.String()): sarama.CompressionSnappy, + strings.ToLower(sarama.CompressionLZ4.String()): sarama.CompressionLZ4, + strings.ToLower(sarama.CompressionZSTD.String()): sarama.CompressionZSTD, + } + + saslAlgorithms = map[KafkaSASLAlgorithm]bool{ + KAFKA_SASL_PLAIN: true, + KAFKA_SASL_SCRAM_SHA256: true, + KAFKA_SASL_SCRAM_SHA512: true, + } + saslAlgorithmsList = []string{ + string(KAFKA_SASL_NONE), + string(KAFKA_SASL_PLAIN), + string(KAFKA_SASL_SCRAM_SHA256), + string(KAFKA_SASL_SCRAM_SHA512), + } +) + +func (d *KafkaDriver) Prepare() error { + flag.BoolVar(&d.kafkaTLS, "transport.kafka.tls", false, "Use TLS to connect to Kafka") + flag.StringVar(&d.kafkaSASL, "transport.kafka.sasl", "none", + fmt.Sprintf( + "Use SASL to connect to Kafka, available settings: %s (TLS is recommended and the environment variables KAFKA_SASL_USER and KAFKA_SASL_PASS need to be set)", + strings.Join(saslAlgorithmsList, ", "))) + + flag.StringVar(&d.kafkaTopic, "transport.kafka.topic", "flow-messages", "Kafka topic to produce to") + flag.StringVar(&d.kafkaSrv, "transport.kafka.srv", "", "SRV record containing a list of Kafka brokers (or use brokers)") + flag.StringVar(&d.kafkaBrk, "transport.kafka.brokers", "127.0.0.1:9092,[::1]:9092", "Kafka brokers list separated by commas") + flag.IntVar(&d.kafkaMaxMsgBytes, "transport.kafka.maxmsgbytes", 1000000, "Kafka max message bytes") + flag.IntVar(&d.kafkaFlushBytes, "transport.kafka.flushbytes", int(sarama.MaxRequestSize), "Kafka flush bytes") + flag.DurationVar(&d.kafkaFlushFrequency, "transport.kafka.flushfreq", time.Second*5, "Kafka flush frequency") + + flag.BoolVar(&d.kafkaLogErrors, "transport.kafka.log.err", false, "Log Kafka errors") + flag.BoolVar(&d.kafkaHashing, "transport.kafka.hashing", false, "Enable partition hashing") + + //flag.StringVar(&d.kafkaKeying, "transport.kafka.key", "SamplerAddress,DstAS", "Kafka list of fields to do hashing on (partition) separated by commas") + flag.StringVar(&d.kafkaVersion, "transport.kafka.version", "2.8.0", "Kafka version") + flag.StringVar(&d.kafkaCompressionCodec, "transport.kafka.compression", "", "Kafka default compression") + + return nil +} + +func (d *KafkaDriver) Init(context.Context) error { + kafkaConfigVersion, err := sarama.ParseKafkaVersion(d.kafkaVersion) + if err != nil { + return err + } + + kafkaConfig := sarama.NewConfig() + kafkaConfig.Version = kafkaConfigVersion + kafkaConfig.Producer.Return.Successes = false + kafkaConfig.Producer.Return.Errors = d.kafkaLogErrors + kafkaConfig.Producer.MaxMessageBytes = d.kafkaMaxMsgBytes + kafkaConfig.Producer.Flush.Bytes = d.kafkaFlushBytes + kafkaConfig.Producer.Flush.Frequency = d.kafkaFlushFrequency + + if d.kafkaCompressionCodec != "" { + /* + // when upgrading sarama, replace with: + // note: if the library adds more codecs, they will be supported natively + var cc *sarama.CompressionCodec + + if err := cc.UnmarshalText([]byte(d.kafkaCompressionCodec)); err != nil { + return err + } + kafkaConfig.Producer.Compression = *cc + */ + + if cc, ok := compressionCodecs[strings.ToLower(d.kafkaCompressionCodec)]; !ok { + return errors.New("compression codec does not exist") + } else { + kafkaConfig.Producer.Compression = cc + } + } + + if d.kafkaTLS { + rootCAs, err := x509.SystemCertPool() + if err != nil { + return errors.New(fmt.Sprintf("Error initializing TLS: %v", err)) + } + kafkaConfig.Net.TLS.Enable = true + kafkaConfig.Net.TLS.Config = &tls.Config{RootCAs: rootCAs} + } + + if d.kafkaHashing { + kafkaConfig.Producer.Partitioner = sarama.NewHashPartitioner + } + + kafkaSASL := KafkaSASLAlgorithm(d.kafkaSASL) + if d.kafkaSASL != "" && kafkaSASL != KAFKA_SASL_NONE { + _, ok := saslAlgorithms[KafkaSASLAlgorithm(strings.ToLower(d.kafkaSASL))] + if !ok { + return errors.New("SASL algorithm does not exist") + } + + kafkaConfig.Net.SASL.Enable = true + kafkaConfig.Net.SASL.User = os.Getenv("KAFKA_SASL_USER") + kafkaConfig.Net.SASL.Password = os.Getenv("KAFKA_SASL_PASS") + if kafkaConfig.Net.SASL.User == "" && kafkaConfig.Net.SASL.Password == "" { + return errors.New("Kafka SASL config from environment was unsuccessful. KAFKA_SASL_USER and KAFKA_SASL_PASS need to be set.") + } + + if kafkaSASL == KAFKA_SASL_SCRAM_SHA256 || kafkaSASL == KAFKA_SASL_SCRAM_SHA512 { + kafkaConfig.Net.SASL.Handshake = true + + if kafkaSASL == KAFKA_SASL_SCRAM_SHA512 { + kafkaConfig.Net.SASL.SCRAMClientGeneratorFunc = func() sarama.SCRAMClient { + return &XDGSCRAMClient{HashGeneratorFcn: SHA512} + } + kafkaConfig.Net.SASL.Mechanism = sarama.SASLTypeSCRAMSHA512 + } else if kafkaSASL == KAFKA_SASL_SCRAM_SHA256 { + kafkaConfig.Net.SASL.SCRAMClientGeneratorFunc = func() sarama.SCRAMClient { + return &XDGSCRAMClient{HashGeneratorFcn: SHA256} + } + kafkaConfig.Net.SASL.Mechanism = sarama.SASLTypeSCRAMSHA256 + } + } + } + + var addrs []string + if d.kafkaSrv != "" { + addrs, _ = utils.GetServiceAddresses(d.kafkaSrv) + } else { + addrs = strings.Split(d.kafkaBrk, ",") + } + + kafkaProducer, err := sarama.NewAsyncProducer(addrs, kafkaConfig) + if err != nil { + return err + } + d.producer = kafkaProducer + + d.q = make(chan bool) + + if d.kafkaLogErrors { + go func() { + for { + select { + case msg := <-kafkaProducer.Errors(): + //if log != nil { + log.Error(msg) + //} + case <-d.q: + return + } + } + }() + } + + return err +} + +func (d *KafkaDriver) Send(key, data []byte) error { + d.producer.Input() <- &sarama.ProducerMessage{ + Topic: d.kafkaTopic, + Key: sarama.ByteEncoder(key), + Value: sarama.ByteEncoder(data), + } + return nil +} + +func (d *KafkaDriver) Close(context.Context) error { + d.producer.Close() + close(d.q) + return nil +} + +func init() { + d := &KafkaDriver{} + transport.RegisterTransportDriver("kafka", d) +} diff --git a/third_party/goflow2/transport/kafka/scram_client.go b/third_party/goflow2/transport/kafka/scram_client.go new file mode 100644 index 000000000000..1490f6a26e94 --- /dev/null +++ b/third_party/goflow2/transport/kafka/scram_client.go @@ -0,0 +1,39 @@ +package kafka + +// From https://github.com/Shopify/sarama/blob/main/examples/sasl_scram_client/scram_client.go + +import ( + "crypto/sha256" + "crypto/sha512" + + "github.com/xdg-go/scram" +) + +var ( + SHA256 scram.HashGeneratorFcn = sha256.New + SHA512 scram.HashGeneratorFcn = sha512.New +) + +type XDGSCRAMClient struct { + *scram.Client + *scram.ClientConversation + scram.HashGeneratorFcn +} + +func (x *XDGSCRAMClient) Begin(userName, password, authzID string) (err error) { + x.Client, err = x.HashGeneratorFcn.NewClient(userName, password, authzID) + if err != nil { + return err + } + x.ClientConversation = x.Client.NewConversation() + return nil +} + +func (x *XDGSCRAMClient) Step(challenge string) (response string, err error) { + response, err = x.ClientConversation.Step(challenge) + return +} + +func (x *XDGSCRAMClient) Done() bool { + return x.ClientConversation.Done() +} diff --git a/third_party/goflow2/transport/transport.go b/third_party/goflow2/transport/transport.go new file mode 100644 index 000000000000..11e9c6786316 --- /dev/null +++ b/third_party/goflow2/transport/transport.go @@ -0,0 +1,68 @@ +package transport + +import ( + "context" + "fmt" + "sync" +) + +var ( + transportDrivers = make(map[string]TransportDriver) + lock = &sync.RWMutex{} +) + +type TransportDriver interface { + Prepare() error // Prepare driver (eg: flag registration) + Init(context.Context) error // Initialize driver (eg: start connections, open files...) + Close(context.Context) error // Close driver (eg: close connections and files...) + Send(key, data []byte) error // Send a formatted message +} + +type TransportInterface interface { + Send(key, data []byte) error +} + +type Transport struct { + driver TransportDriver +} + +func (t *Transport) Close(ctx context.Context) { + t.driver.Close(ctx) +} +func (t *Transport) Send(key, data []byte) error { + return t.driver.Send(key, data) +} + +func RegisterTransportDriver(name string, t TransportDriver) { + lock.Lock() + transportDrivers[name] = t + lock.Unlock() + + if err := t.Prepare(); err != nil { + panic(err) + } +} + +func FindTransport(ctx context.Context, name string) (*Transport, error) { + lock.RLock() + t, ok := transportDrivers[name] + lock.RUnlock() + if !ok { + return nil, fmt.Errorf("Transport %s not found", name) + } + + err := t.Init(ctx) + return &Transport{t}, err +} + +func GetTransports() []string { + lock.RLock() + defer lock.RUnlock() + t := make([]string, len(transportDrivers)) + var i int + for k, _ := range transportDrivers { + t[i] = k + i++ + } + return t +} diff --git a/third_party/goflow2/utils/metrics.go b/third_party/goflow2/utils/metrics.go new file mode 100644 index 000000000000..eb3f23158054 --- /dev/null +++ b/third_party/goflow2/utils/metrics.go @@ -0,0 +1,171 @@ +package utils + +import ( + "strconv" + "time" + + "github.com/prometheus/client_golang/prometheus" +) + +var ( + MetricTrafficBytes = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_traffic_bytes", + Help: "Bytes received by the application.", + }, + []string{"remote_ip", "local_ip", "local_port", "type"}, + ) + MetricTrafficPackets = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_traffic_packets", + Help: "Packets received by the application.", + }, + []string{"remote_ip", "local_ip", "local_port", "type"}, + ) + MetricPacketSizeSum = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Name: "flow_traffic_summary_size_bytes", + Help: "Summary of packet size.", + Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, + }, + []string{"remote_ip", "local_ip", "local_port", "type"}, + ) + DecoderStats = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_decoder_count", + Help: "Decoder processed count.", + }, + []string{"worker", "name"}, + ) + DecoderErrors = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_decoder_error_count", + Help: "Decoder processed error count.", + }, + []string{"worker", "name"}, + ) + DecoderTime = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Name: "flow_summary_decoding_time_us", + Help: "Decoding time summary.", + Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, + }, + []string{"name"}, + ) + DecoderProcessTime = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Name: "flow_summary_processing_time_us", + Help: "Processing time summary.", + Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, + }, + []string{"name"}, + ) + NetFlowStats = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_process_nf_count", + Help: "NetFlows processed.", + }, + []string{"router", "version"}, + ) + NetFlowErrors = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_process_nf_errors_count", + Help: "NetFlows processed errors.", + }, + []string{"router", "error"}, + ) + NetFlowSetRecordsStatsSum = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_process_nf_flowset_records_sum", + Help: "NetFlows FlowSets sum of records.", + }, + []string{"router", "version", "type"}, // data-template, data, opts... + ) + NetFlowSetStatsSum = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_process_nf_flowset_sum", + Help: "NetFlows FlowSets sum.", + }, + []string{"router", "version", "type"}, // data-template, data, opts... + ) + NetFlowTimeStatsSum = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Name: "flow_process_nf_delay_summary_seconds", + Help: "NetFlows time difference between time of flow and processing.", + Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, + }, + []string{"router", "version"}, + ) + NetFlowTemplatesStats = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_process_nf_templates_count", + Help: "NetFlows Template count.", + }, + []string{"router", "version", "obs_domain_id", "template_id", "type"}, // options/template + ) + SFlowStats = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_process_sf_count", + Help: "sFlows processed.", + }, + []string{"router", "agent", "version"}, + ) + SFlowErrors = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_process_sf_errors_count", + Help: "sFlows processed errors.", + }, + []string{"router", "error"}, + ) + SFlowSampleStatsSum = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_process_sf_samples_sum", + Help: "SFlows samples sum.", + }, + []string{"router", "agent", "version", "type"}, // counter, flow, expanded... + ) + SFlowSampleRecordsStatsSum = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "flow_process_sf_samples_records_sum", + Help: "SFlows samples sum of records.", + }, + []string{"router", "agent", "version", "type"}, // data-template, data, opts... + ) +) + +func init() { + prometheus.MustRegister(MetricTrafficBytes) + prometheus.MustRegister(MetricTrafficPackets) + prometheus.MustRegister(MetricPacketSizeSum) + + prometheus.MustRegister(DecoderStats) + prometheus.MustRegister(DecoderErrors) + prometheus.MustRegister(DecoderTime) + prometheus.MustRegister(DecoderProcessTime) + + prometheus.MustRegister(NetFlowStats) + prometheus.MustRegister(NetFlowErrors) + prometheus.MustRegister(NetFlowSetRecordsStatsSum) + prometheus.MustRegister(NetFlowSetStatsSum) + prometheus.MustRegister(NetFlowTimeStatsSum) + prometheus.MustRegister(NetFlowTemplatesStats) + + prometheus.MustRegister(SFlowStats) + prometheus.MustRegister(SFlowErrors) + prometheus.MustRegister(SFlowSampleStatsSum) + prometheus.MustRegister(SFlowSampleRecordsStatsSum) +} + +func DefaultAccountCallback(name string, id int, start, end time.Time) { + DecoderProcessTime.With( + prometheus.Labels{ + "name": name, + }). + Observe(float64((end.Sub(start)).Nanoseconds()) / 1000) + DecoderStats.With( + prometheus.Labels{ + "worker": strconv.Itoa(id), + "name": name, + }). + Inc() +} diff --git a/third_party/goflow2/utils/netflow.go b/third_party/goflow2/utils/netflow.go new file mode 100644 index 000000000000..0923ee3b7955 --- /dev/null +++ b/third_party/goflow2/utils/netflow.go @@ -0,0 +1,377 @@ +package utils + +import ( + "bytes" + "context" + "sync" + "time" + + "github.com/netsampler/goflow2/decoders/netflow" + "github.com/netsampler/goflow2/decoders/netflow/templates" + "github.com/netsampler/goflow2/format" + flowmessage "github.com/netsampler/goflow2/pb" + "github.com/netsampler/goflow2/producer" + "github.com/netsampler/goflow2/transport" + "github.com/prometheus/client_golang/prometheus" +) + +/* +type TemplateSystem struct { + key string + templates *netflow.BasicTemplateSystem +} + +func (s *TemplateSystem) AddTemplate(version uint16, obsDomainId uint32, template interface{}) { + s.templates.AddTemplate(version, obsDomainId, template) + + typeStr := "options_template" + var templateId uint16 + switch templateIdConv := template.(type) { + case netflow.IPFIXOptionsTemplateRecord: + templateId = templateIdConv.TemplateId + case netflow.NFv9OptionsTemplateRecord: + templateId = templateIdConv.TemplateId + case netflow.TemplateRecord: + templateId = templateIdConv.TemplateId + typeStr = "template" + } + NetFlowTemplatesStats.With( + prometheus.Labels{ + "router": s.key, + "version": strconv.Itoa(int(version)), + "obs_domain_id": strconv.Itoa(int(obsDomainId)), + "template_id": strconv.Itoa(int(templateId)), + "type": typeStr, + }). + Inc() +} + +func (s *TemplateSystem) GetTemplate(version uint16, obsDomainId uint32, templateId uint16) (interface{}, error) { + return s.templates.GetTemplate(version, obsDomainId, templateId) +} +*/ + +type StateNetFlow struct { + stopper + + Format format.FormatInterface + Transport transport.TransportInterface + Logger Logger + /*templateslock *sync.RWMutex + templates map[string]*TemplateSystem*/ + + samplinglock *sync.RWMutex + sampling map[string]producer.SamplingRateSystem + + Config *producer.ProducerConfig + configMapped *producer.ProducerConfigMapped + + TemplateSystem templates.TemplateInterface + + ctx context.Context +} + +func NewStateNetFlow() *StateNetFlow { + return &StateNetFlow{ + ctx: context.Background(), + samplinglock: &sync.RWMutex{}, + sampling: make(map[string]producer.SamplingRateSystem), + } +} + +func (s *StateNetFlow) DecodeFlow(msg interface{}) error { + pkt := msg.(BaseMessage) + buf := bytes.NewBuffer(pkt.Payload) + + key := pkt.Src.String() + samplerAddress := pkt.Src + if samplerAddress.To4() != nil { + samplerAddress = samplerAddress.To4() + } + + s.samplinglock.RLock() + sampling, ok := s.sampling[key] + s.samplinglock.RUnlock() + if !ok { + sampling = producer.CreateSamplingSystem() + s.samplinglock.Lock() + s.sampling[key] = sampling + s.samplinglock.Unlock() + } + + ts := uint64(time.Now().UTC().Unix()) + if pkt.SetTime { + ts = uint64(pkt.RecvTime.UTC().Unix()) + } + + timeTrackStart := time.Now() + msgDec, err := netflow.DecodeMessageContext(s.ctx, buf, key, netflow.TemplateWrapper{s.ctx, key, s.TemplateSystem}) + if err != nil { + switch err.(type) { + case *netflow.ErrorTemplateNotFound: + NetFlowErrors.With( + prometheus.Labels{ + "router": key, + "error": "template_not_found", + }). + Inc() + default: + NetFlowErrors.With( + prometheus.Labels{ + "router": key, + "error": "error_decoding", + }). + Inc() + } + return err + } + + var flowMessageSet []*flowmessage.FlowMessage + + switch msgDecConv := msgDec.(type) { + case netflow.NFv9Packet: + NetFlowStats.With( + prometheus.Labels{ + "router": key, + "version": "9", + }). + Inc() + + for _, fs := range msgDecConv.FlowSets { + switch fsConv := fs.(type) { + case netflow.TemplateFlowSet: + NetFlowSetStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "9", + "type": "TemplateFlowSet", + }). + Inc() + + NetFlowSetRecordsStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "9", + "type": "TemplateFlowSet", + }). + Add(float64(len(fsConv.Records))) + + case netflow.NFv9OptionsTemplateFlowSet: + NetFlowSetStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "9", + "type": "OptionsTemplateFlowSet", + }). + Inc() + + NetFlowSetRecordsStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "9", + "type": "OptionsTemplateFlowSet", + }). + Add(float64(len(fsConv.Records))) + + case netflow.OptionsDataFlowSet: + NetFlowSetStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "9", + "type": "OptionsDataFlowSet", + }). + Inc() + + NetFlowSetRecordsStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "9", + "type": "OptionsDataFlowSet", + }). + Add(float64(len(fsConv.Records))) + case netflow.DataFlowSet: + NetFlowSetStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "9", + "type": "DataFlowSet", + }). + Inc() + + NetFlowSetRecordsStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "9", + "type": "DataFlowSet", + }). + Add(float64(len(fsConv.Records))) + } + } + flowMessageSet, err = producer.ProcessMessageNetFlowConfig(msgDecConv, sampling, s.configMapped) + + for _, fmsg := range flowMessageSet { + fmsg.TimeReceived = ts + fmsg.SamplerAddress = samplerAddress + timeDiff := fmsg.TimeReceived - fmsg.TimeFlowEnd + NetFlowTimeStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "9", + }). + Observe(float64(timeDiff)) + } + case netflow.IPFIXPacket: + NetFlowStats.With( + prometheus.Labels{ + "router": key, + "version": "10", + }). + Inc() + + for _, fs := range msgDecConv.FlowSets { + switch fsConv := fs.(type) { + case netflow.TemplateFlowSet: + NetFlowSetStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "10", + "type": "TemplateFlowSet", + }). + Inc() + + NetFlowSetRecordsStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "10", + "type": "TemplateFlowSet", + }). + Add(float64(len(fsConv.Records))) + + case netflow.IPFIXOptionsTemplateFlowSet: + NetFlowSetStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "10", + "type": "OptionsTemplateFlowSet", + }). + Inc() + + NetFlowSetRecordsStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "10", + "type": "OptionsTemplateFlowSet", + }). + Add(float64(len(fsConv.Records))) + + case netflow.OptionsDataFlowSet: + + NetFlowSetStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "10", + "type": "OptionsDataFlowSet", + }). + Inc() + + NetFlowSetRecordsStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "10", + "type": "OptionsDataFlowSet", + }). + Add(float64(len(fsConv.Records))) + + case netflow.DataFlowSet: + NetFlowSetStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "10", + "type": "DataFlowSet", + }). + Inc() + + NetFlowSetRecordsStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "10", + "type": "DataFlowSet", + }). + Add(float64(len(fsConv.Records))) + } + } + flowMessageSet, err = producer.ProcessMessageNetFlowConfig(msgDecConv, sampling, s.configMapped) + + for _, fmsg := range flowMessageSet { + fmsg.TimeReceived = ts + fmsg.SamplerAddress = samplerAddress + timeDiff := fmsg.TimeReceived - fmsg.TimeFlowEnd + NetFlowTimeStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "10", + }). + Observe(float64(timeDiff)) + } + } + + timeTrackStop := time.Now() + DecoderTime.With( + prometheus.Labels{ + "name": "NetFlow", + }). + Observe(float64((timeTrackStop.Sub(timeTrackStart)).Nanoseconds()) / 1000) + + for _, fmsg := range flowMessageSet { + if s.Format != nil { + key, data, err := s.Format.Format(fmsg) + + if err != nil && s.Logger != nil { + s.Logger.Error(err) + } + if err == nil && s.Transport != nil { + err = s.Transport.Send(key, data) + if err != nil { + s.Logger.Error(err) + } + } + } + } + + return nil +} + +/* +func (s *StateNetFlow) ServeHTTPTemplates(w http.ResponseWriter, r *http.Request) { + tmp := make(map[string]map[uint16]map[uint32]map[uint16]interface{}) + s.templateslock.RLock() + for key, templatesrouterstr := range s.templates { + templatesrouter := templatesrouterstr.templates.GetTemplates() + tmp[key] = templatesrouter + } + s.templateslock.RUnlock() + enc := json.NewEncoder(w) + enc.Encode(tmp) +} + +func (s *StateNetFlow) InitTemplates() { + s.templates = make(map[string]*TemplateSystem) + s.templateslock = &sync.RWMutex{} + s.sampling = make(map[string]producer.SamplingRateSystem) + s.samplinglock = &sync.RWMutex{} +}*/ + +func (s *StateNetFlow) initConfig() { + s.configMapped = producer.NewProducerConfigMapped(s.Config) +} + +func (s *StateNetFlow) FlowRoutine(workers int, addr string, port int, reuseport bool) error { + if err := s.start(); err != nil { + return err + } + //s.InitTemplates() + s.initConfig() + return UDPStoppableRoutine(s.stopCh, "NetFlow", s.DecodeFlow, workers, addr, port, reuseport, s.Logger) +} + +// FlowRoutineCtx? diff --git a/third_party/goflow2/utils/nflegacy.go b/third_party/goflow2/utils/nflegacy.go new file mode 100644 index 000000000000..dcfc36dbeecf --- /dev/null +++ b/third_party/goflow2/utils/nflegacy.go @@ -0,0 +1,111 @@ +package utils + +import ( + "bytes" + "time" + + "github.com/netsampler/goflow2/decoders/netflowlegacy" + "github.com/netsampler/goflow2/format" + flowmessage "github.com/netsampler/goflow2/pb" + "github.com/netsampler/goflow2/producer" + "github.com/netsampler/goflow2/transport" + "github.com/prometheus/client_golang/prometheus" +) + +type StateNFLegacy struct { + stopper + + Format format.FormatInterface + Transport transport.TransportInterface + Logger Logger +} + +func NewStateNFLegacy() *StateNFLegacy { + return &StateNFLegacy{} +} + +func (s *StateNFLegacy) DecodeFlow(msg interface{}) error { + pkt := msg.(BaseMessage) + buf := bytes.NewBuffer(pkt.Payload) + key := pkt.Src.String() + samplerAddress := pkt.Src + if samplerAddress.To4() != nil { + samplerAddress = samplerAddress.To4() + } + + ts := uint64(time.Now().UTC().Unix()) + if pkt.SetTime { + ts = uint64(pkt.RecvTime.UTC().Unix()) + } + + timeTrackStart := time.Now() + msgDec, err := netflowlegacy.DecodeMessage(buf) + + if err != nil { + switch err.(type) { + case *netflowlegacy.ErrorVersion: + NetFlowErrors.With( + prometheus.Labels{ + "router": key, + "error": "error_version", + }). + Inc() + } + return err + } + + switch msgDecConv := msgDec.(type) { + case netflowlegacy.PacketNetFlowV5: + NetFlowStats.With( + prometheus.Labels{ + "router": key, + "version": "5", + }). + Inc() + NetFlowSetStatsSum.With( + prometheus.Labels{ + "router": key, + "version": "5", + "type": "DataFlowSet", + }). + Add(float64(msgDecConv.Count)) + } + + var flowMessageSet []*flowmessage.FlowMessage + flowMessageSet, err = producer.ProcessMessageNetFlowLegacy(msgDec) + + timeTrackStop := time.Now() + DecoderTime.With( + prometheus.Labels{ + "name": "NetFlowV5", + }). + Observe(float64((timeTrackStop.Sub(timeTrackStart)).Nanoseconds()) / 1000) + + for _, fmsg := range flowMessageSet { + fmsg.TimeReceived = ts + fmsg.SamplerAddress = samplerAddress + + if s.Format != nil { + key, data, err := s.Format.Format(fmsg) + + if err != nil && s.Logger != nil { + s.Logger.Error(err) + } + if err == nil && s.Transport != nil { + err = s.Transport.Send(key, data) + if err != nil { + s.Logger.Error(err) + } + } + } + } + + return nil +} + +func (s *StateNFLegacy) FlowRoutine(workers int, addr string, port int, reuseport bool) error { + if err := s.start(); err != nil { + return err + } + return UDPStoppableRoutine(s.stopCh, "NetFlowV5", s.DecodeFlow, workers, addr, port, reuseport, s.Logger) +} diff --git a/third_party/goflow2/utils/sflow.go b/third_party/goflow2/utils/sflow.go new file mode 100644 index 000000000000..27223bcc096a --- /dev/null +++ b/third_party/goflow2/utils/sflow.go @@ -0,0 +1,170 @@ +package utils + +import ( + "bytes" + "net" + "time" + + "github.com/netsampler/goflow2/decoders/sflow" + "github.com/netsampler/goflow2/format" + flowmessage "github.com/netsampler/goflow2/pb" + "github.com/netsampler/goflow2/producer" + "github.com/netsampler/goflow2/transport" + "github.com/prometheus/client_golang/prometheus" +) + +type StateSFlow struct { + stopper + + Format format.FormatInterface + Transport transport.TransportInterface + Logger Logger + + Config *producer.ProducerConfig + configMapped *producer.ProducerConfigMapped +} + +func NewStateSFlow() *StateSFlow { + return &StateSFlow{} +} + +func (s *StateSFlow) DecodeFlow(msg interface{}) error { + pkt := msg.(BaseMessage) + buf := bytes.NewBuffer(pkt.Payload) + key := pkt.Src.String() + + ts := uint64(time.Now().UTC().Unix()) + if pkt.SetTime { + ts = uint64(pkt.RecvTime.UTC().Unix()) + } + + timeTrackStart := time.Now() + msgDec, err := sflow.DecodeMessage(buf) + + if err != nil { + switch err.(type) { + case *sflow.ErrorVersion: + SFlowErrors.With( + prometheus.Labels{ + "router": key, + "error": "error_version", + }). + Inc() + case *sflow.ErrorIPVersion: + SFlowErrors.With( + prometheus.Labels{ + "router": key, + "error": "error_ip_version", + }). + Inc() + case *sflow.ErrorDataFormat: + SFlowErrors.With( + prometheus.Labels{ + "router": key, + "error": "error_data_format", + }). + Inc() + default: + SFlowErrors.With( + prometheus.Labels{ + "router": key, + "error": "error_decoding", + }). + Inc() + } + return err + } + + switch msgDecConv := msgDec.(type) { + case sflow.Packet: + agentStr := net.IP(msgDecConv.AgentIP).String() + SFlowStats.With( + prometheus.Labels{ + "router": key, + "agent": agentStr, + "version": "5", + }). + Inc() + + for _, samples := range msgDecConv.Samples { + typeStr := "unknown" + countRec := 0 + switch samplesConv := samples.(type) { + case sflow.FlowSample: + typeStr = "FlowSample" + countRec = len(samplesConv.Records) + case sflow.CounterSample: + typeStr = "CounterSample" + if samplesConv.Header.Format == 4 { + typeStr = "Expanded" + typeStr + } + countRec = len(samplesConv.Records) + case sflow.ExpandedFlowSample: + typeStr = "ExpandedFlowSample" + countRec = len(samplesConv.Records) + } + SFlowSampleStatsSum.With( + prometheus.Labels{ + "router": key, + "agent": agentStr, + "version": "5", + "type": typeStr, + }). + Inc() + + SFlowSampleRecordsStatsSum.With( + prometheus.Labels{ + "router": key, + "agent": agentStr, + "version": "5", + "type": typeStr, + }). + Add(float64(countRec)) + } + + } + + var flowMessageSet []*flowmessage.FlowMessage + flowMessageSet, err = producer.ProcessMessageSFlowConfig(msgDec, s.configMapped) + + timeTrackStop := time.Now() + DecoderTime.With( + prometheus.Labels{ + "name": "sFlow", + }). + Observe(float64((timeTrackStop.Sub(timeTrackStart)).Nanoseconds()) / 1000) + + for _, fmsg := range flowMessageSet { + fmsg.TimeReceived = ts + fmsg.TimeFlowStart = ts + fmsg.TimeFlowEnd = ts + + if s.Format != nil { + key, data, err := s.Format.Format(fmsg) + + if err != nil && s.Logger != nil { + s.Logger.Error(err) + } + if err == nil && s.Transport != nil { + err = s.Transport.Send(key, data) + if err != nil { + s.Logger.Error(err) + } + } + } + } + + return nil +} + +func (s *StateSFlow) initConfig() { + s.configMapped = producer.NewProducerConfigMapped(s.Config) +} + +func (s *StateSFlow) FlowRoutine(workers int, addr string, port int, reuseport bool) error { + if err := s.start(); err != nil { + return err + } + s.initConfig() + return UDPStoppableRoutine(s.stopCh, "sFlow", s.DecodeFlow, workers, addr, port, reuseport, s.Logger) +} diff --git a/third_party/goflow2/utils/sflow_test.go b/third_party/goflow2/utils/sflow_test.go new file mode 100644 index 000000000000..76f3316253a8 --- /dev/null +++ b/third_party/goflow2/utils/sflow_test.go @@ -0,0 +1,92 @@ +package utils + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestDecodeFlowExpandedSFlow(t *testing.T) { + msg := BaseMessage{ + Src: []byte{}, + Port: 1, + Payload: getExpandedSFlowDecode(), + } + + s := &StateSFlow{} + + assert.Nil(t, s.DecodeFlow(msg)) +} + +func getExpandedSFlowDecode() []byte { + return []byte{ + 0, 0, 0, 5, 0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 5, 167, 139, 219, 5, 118, + 138, 184, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 220, 2, 144, 194, 214, 0, 0, 0, 0, + 0, 5, 6, 164, 0, 0, 3, 255, 6, 6, 189, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 6, 164, 0, 0, 0, 0, 0, 5, 6, 171, 0, 0, 0, 2, 0, 0, 3, 233, 0, 0, 0, 6, + 0, 0, 5, 7, 0, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 144, 0, 0, 0, 1, 0, 0, 5, 234, 0, 0, 0, 4, 0, 0, 0, 128, 8, 6, 168, 250, + 146, 253, 116, 131, 239, 8, 101, 183, 129, 0, 5, 7, 8, 0, 9, 0, 5, 212, 0, 2, 4, 0, + 3, 6, 252, 8, 9, 187, 169, 1, 4, 7, 186, 201, 1, 187, 249, 6, 160, 7, 5, 240, 6, 4, + 4, 0, 0, 6, 0, 123, 119, 210, 0, 0, 165, 105, 7, 171, 145, 234, 102, 0, 252, 187, 162, 227, + 104, 188, 126, 232, 156, 164, 2, 115, 6, 100, 0, 185, 6, 4, 119, 5, 213, 1, 215, 208, 8, 4, + 118, 183, 241, 225, 130, 186, 2, 250, 220, 153, 189, 3, 4, 4, 1, 8, 210, 119, 172, 9, 164, 233, + 1, 8, 171, 226, 196, 195, 3, 152, 9, 5, 6, 181, 4, 7, 0, 0, 0, 3, 0, 0, 0, 220, + 9, 107, 215, 156, 0, 0, 0, 0, 0, 5, 6, 165, 0, 0, 3, 255, 226, 123, 0, 100, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5, 6, 165, 0, 0, 0, 0, 0, 5, 6, 164, 0, 0, 0, 2, + 0, 0, 3, 233, 0, 0, 0, 6, 0, 0, 3, 184, 0, 0, 0, 0, 0, 0, 3, 184, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 144, 0, 0, 0, 1, 0, 0, 5, 190, 0, 0, 0, 4, + 0, 0, 0, 128, 116, 131, 239, 8, 101, 183, 144, 226, 186, 134, 8, 1, 129, 0, 3, 184, 8, 0, + 9, 0, 5, 168, 7, 127, 4, 0, 4, 6, 163, 211, 185, 9, 220, 7, 0, 254, 3, 8, 0, 9, + 130, 136, 179, 1, 2, 2, 7, 5, 250, 4, 128, 6, 0, 1, 7, 1, 0, 0, 1, 1, 8, 0, + 6, 9, 250, 9, 4, 113, 121, 4, 160, 125, 0, 4, 9, 209, 241, 194, 190, 148, 161, 186, 6, 192, + 246, 190, 170, 2, 238, 190, 128, 221, 223, 1, 218, 225, 3, 9, 7, 226, 220, 231, 127, 3, 3, 252, + 7, 9, 161, 247, 218, 8, 8, 174, 133, 4, 213, 245, 149, 218, 5, 4, 200, 128, 139, 5, 0, 115, + 0, 0, 0, 3, 0, 0, 0, 220, 2, 144, 194, 215, 0, 0, 0, 0, 0, 5, 6, 164, 0, 0, + 3, 255, 6, 6, 253, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 164, 0, 0, 0, 0, + 0, 5, 6, 171, 0, 0, 0, 2, 0, 0, 3, 233, 0, 0, 0, 6, 0, 0, 0, 104, 0, 0, + 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 144, 0, 0, 0, 1, + 0, 0, 5, 242, 0, 0, 0, 4, 0, 0, 0, 128, 116, 131, 239, 7, 9, 1, 116, 131, 239, 8, + 101, 183, 129, 0, 0, 104, 8, 0, 9, 0, 5, 220, 152, 143, 4, 0, 1, 6, 5, 179, 9, 187, + 191, 101, 190, 2, 144, 182, 0, 0, 130, 4, 252, 4, 160, 192, 138, 8, 219, 124, 128, 6, 0, 235, + 180, 213, 0, 0, 1, 1, 8, 0, 9, 124, 6, 1, 9, 1, 252, 3, 194, 8, 195, 209, 115, 1, + 5, 152, 204, 2, 6, 4, 1, 119, 254, 9, 1, 170, 0, 192, 2, 7, 190, 9, 149, 5, 101, 2, + 128, 122, 0, 190, 1, 109, 188, 175, 4, 8, 152, 1, 142, 108, 2, 100, 2, 124, 125, 195, 5, 8, + 233, 126, 7, 4, 243, 4, 3, 153, 0, 0, 0, 3, 0, 0, 0, 220, 5, 1, 150, 6, 0, 0, + 0, 0, 0, 5, 6, 167, 0, 0, 3, 255, 6, 5, 105, 220, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 6, 167, 0, 0, 0, 0, 0, 5, 6, 164, 0, 0, 0, 2, 0, 0, 3, 233, 0, 0, + 0, 6, 0, 0, 5, 7, 0, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 144, 0, 0, 0, 1, 0, 0, 2, 2, 0, 0, 0, 4, 0, 0, 0, 128, 116, 131, + 239, 8, 101, 183, 152, 3, 130, 1, 196, 153, 129, 0, 5, 7, 8, 0, 9, 0, 2, 0, 0, 0, + 4, 0, 126, 7, 119, 188, 185, 9, 221, 8, 2, 116, 144, 0, 9, 139, 3, 112, 2, 0, 8, 124, + 255, 251, 0, 0, 131, 2, 0, 0, 0, 246, 3, 3, 107, 5, 0, 0, 0, 0, 9, 173, 2, 217, + 6, 248, 0, 0, 9, 173, 2, 217, 8, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 6, 9, 153, + 215, 157, 0, 255, 0, 8, 1, 0, 9, 8, 9, 6, 164, 103, 9, 5, 0, 0, 0, 3, 0, 0, + 0, 152, 5, 201, 2, 175, 0, 0, 0, 0, 0, 5, 6, 5, 0, 0, 3, 255, 1, 8, 9, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 0, 0, 0, 0, 0, 5, 6, 164, 0, 0, + 0, 2, 0, 0, 3, 233, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, + 0, 4, 0, 0, 0, 0, 116, 131, 239, 8, 101, 183, 218, 177, 4, 251, 217, 207, 8, 0, 9, 0, + 0, 8, 0, 0, 0, 0, 9, 7, 8, 161, 106, 3, 109, 6, 185, 9, 220, 215, 0, 123, 9, 184, + 0, 8, 116, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 3, 130, 6, + 0, 0, 0, 3, 0, 0, 0, 220, 2, 144, 194, 216, 0, 0, 0, 0, 0, 5, 6, 164, 0, 0, + 3, 255, 6, 7, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 164, 0, 0, 0, 0, + 0, 5, 6, 165, 0, 0, 0, 2, 0, 0, 3, 233, 0, 0, 0, 6, 0, 0, 3, 202, 0, 0, + 0, 0, 0, 0, 3, 202, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 144, 0, 0, 0, 1, + 0, 0, 5, 242, 0, 0, 0, 4, 0, 0, 0, 128, 144, 226, 186, 135, 4, 241, 116, 131, 239, 8, + 101, 183, 129, 0, 3, 202, 8, 0, 9, 0, 5, 220, 147, 0, 4, 0, 7, 6, 225, 131, 1, 159, + 7, 185, 195, 181, 170, 8, 9, 117, 7, 175, 8, 3, 191, 135, 190, 150, 196, 102, 0, 6, 0, 119, + 116, 113, 0, 0, 201, 244, 240, 206, 2, 117, 4, 139, 8, 4, 240, 223, 247, 123, 6, 0, 239, 0, + 9, 116, 152, 153, 191, 0, 124, 2, 7, 8, 3, 178, 166, 150, 3, 218, 163, 175, 121, 8, 4, 210, + 4, 5, 166, 5, 178, 1, 6, 222, 172, 186, 6, 241, 232, 8, 188, 192, 2, 220, 128, 1, 8, 7, + 194, 130, 220, 5, 2, 0, 158, 195, 0, 4, 3, 2, 160, 158, 157, 2, 102, 3, 7, 3, 0, 0, + 1, 3, 3, 4, 1, 1, 4, 2, 187, 255, 188, 3, 4, 138, 9, 180, 104, 233, 212, 239, 123, 237, + 112, 8, 133, 129, 152, 138, 7, 195, 8, 171, 237, 3, 4, 223, 116, 214, 151, 9, 151, 102, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + } +} diff --git a/third_party/goflow2/utils/stopper.go b/third_party/goflow2/utils/stopper.go new file mode 100644 index 000000000000..153b1bd174e1 --- /dev/null +++ b/third_party/goflow2/utils/stopper.go @@ -0,0 +1,33 @@ +package utils + +import ( + "errors" +) + +// ErrAlreadyStarted error happens when you try to start twice a flow routine +var ErrAlreadyStarted = errors.New("the routine is already started") + +// stopper mechanism, common for all the flow routines +type stopper struct { + stopCh chan struct{} +} + +func (s *stopper) start() error { + if s.stopCh != nil { + return ErrAlreadyStarted + } + s.stopCh = make(chan struct{}) + return nil +} + +func (s *stopper) Shutdown() { + if s.stopCh != nil { + select { + case <-s.stopCh: + default: + close(s.stopCh) + } + + s.stopCh = nil + } +} diff --git a/third_party/goflow2/utils/stopper_test.go b/third_party/goflow2/utils/stopper_test.go new file mode 100644 index 000000000000..f76e7bfe8ab9 --- /dev/null +++ b/third_party/goflow2/utils/stopper_test.go @@ -0,0 +1,51 @@ +package utils + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestStopper(t *testing.T) { + r := routine{} + require.False(t, r.Running) + require.NoError(t, r.StartRoutine()) + assert.True(t, r.Running) + r.Shutdown() + assert.Eventually(t, func() bool { + return r.Running == false + }, time.Second, time.Millisecond) + + // after shutdown, we can start it again + require.NoError(t, r.StartRoutine()) + assert.True(t, r.Running) +} + +func TestStopper_CannotStartTwice(t *testing.T) { + r := routine{} + require.False(t, r.Running) + require.NoError(t, r.StartRoutine()) + assert.ErrorIs(t, r.StartRoutine(), ErrAlreadyStarted) +} + +type routine struct { + stopper + Running bool +} + +func (p *routine) StartRoutine() error { + if err := p.start(); err != nil { + return err + } + p.Running = true + waitForGoRoutine := make(chan struct{}) + go func() { + close(waitForGoRoutine) + <-p.stopCh + p.Running = false + }() + <-waitForGoRoutine + return nil +} diff --git a/third_party/goflow2/utils/utils.go b/third_party/goflow2/utils/utils.go new file mode 100644 index 000000000000..bddd4460815b --- /dev/null +++ b/third_party/goflow2/utils/utils.go @@ -0,0 +1,234 @@ +package utils + +import ( + "errors" + "fmt" + "io" + "net" + "strconv" + "sync" + "time" + + reuseport "github.com/libp2p/go-reuseport" + decoder "github.com/netsampler/goflow2/decoders" + "github.com/netsampler/goflow2/decoders/netflow" + flowmessage "github.com/netsampler/goflow2/pb" + "github.com/netsampler/goflow2/producer" + "github.com/prometheus/client_golang/prometheus" + "go.yaml.in/yaml/v2" +) + +type ProducerConfig *producer.ProducerConfig + +func LoadMapping(f io.Reader) (ProducerConfig, error) { + config := &producer.ProducerConfig{} + dec := yaml.NewDecoder(f) + err := dec.Decode(config) + return config, err +} + +func GetServiceAddresses(srv string) (addrs []string, err error) { + _, srvs, err := net.LookupSRV("", "", srv) + if err != nil { + return nil, errors.New(fmt.Sprintf("Service discovery: %v\n", err)) + } + for _, srv := range srvs { + addrs = append(addrs, net.JoinHostPort(srv.Target, strconv.Itoa(int(srv.Port)))) + } + return addrs, nil +} + +type Logger interface { + Printf(string, ...interface{}) + Errorf(string, ...interface{}) + Warnf(string, ...interface{}) + Warn(...interface{}) + Error(...interface{}) + Debug(...interface{}) + Debugf(string, ...interface{}) + Infof(string, ...interface{}) + Fatalf(string, ...interface{}) +} + +type BaseMessage struct { + Src net.IP + Port int + Payload []byte + + SetTime bool + RecvTime time.Time +} + +type Transport interface { + Send([]*flowmessage.FlowMessage) +} + +type Formatter interface { + Format([]*flowmessage.FlowMessage) +} + +/* +type DefaultLogTransport struct { +} + + func (s *DefaultLogTransport) Publish(msgs []*flowmessage.FlowMessage) { + for _, msg := range msgs { + fmt.Printf("%v\n", FlowMessageToString(msg)) + } + } + +type DefaultJSONTransport struct { +} + + func (s *DefaultJSONTransport) Publish(msgs []*flowmessage.FlowMessage) { + for _, msg := range msgs { + fmt.Printf("%v\n", FlowMessageToJSON(msg)) + } + } +*/ +type DefaultErrorCallback struct { + Logger Logger +} + +func (cb *DefaultErrorCallback) Callback(name string, id int, start, end time.Time, err error) { + if _, ok := err.(*netflow.ErrorTemplateNotFound); ok { + return + } + if cb.Logger != nil { + cb.Logger.Errorf("Error from: %v (%v) duration: %v. %v", name, id, end.Sub(start), err) + } +} + +func UDPRoutine(name string, decodeFunc decoder.DecoderFunc, workers int, addr string, port int, sockReuse bool, logger Logger) error { + return UDPStoppableRoutine(make(chan struct{}), name, decodeFunc, workers, addr, port, sockReuse, logger) +} + +// UDPStoppableRoutine runs a UDPRoutine that can be stopped by closing the stopCh passed as argument +func UDPStoppableRoutine(stopCh <-chan struct{}, name string, decodeFunc decoder.DecoderFunc, workers int, addr string, port int, sockReuse bool, logger Logger) error { + ecb := DefaultErrorCallback{ + Logger: logger, + } + + decoderParams := decoder.DecoderParams{ + DecoderFunc: decodeFunc, + DoneCallback: DefaultAccountCallback, + ErrorCallback: ecb.Callback, + } + + processor := decoder.CreateProcessor(workers, decoderParams, name) + processor.Start() + + addrUDP := net.UDPAddr{ + IP: net.ParseIP(addr), + Port: port, + } + + var udpconn *net.UDPConn + var err error + + if sockReuse { + pconn, err := reuseport.ListenPacket("udp", addrUDP.String()) + if err != nil { + return err + } + defer pconn.Close() + var ok bool + udpconn, ok = pconn.(*net.UDPConn) + if !ok { + return err + } + } else { + udpconn, err = net.ListenUDP("udp", &addrUDP) + if err != nil { + return err + } + defer udpconn.Close() + } + + payload := make([]byte, 9000) + + localIP := addrUDP.IP.String() + if addrUDP.IP == nil { + localIP = "" + } + + type udpData struct { + size int + pktAddr *net.UDPAddr + payload []byte + } + + udpDataCh := make(chan udpData) + defer close(udpDataCh) + + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + for { + u := udpData{} + u.size, u.pktAddr, _ = udpconn.ReadFromUDP(payload) + if u.size == 0 { // Ignore 0 byte packets. + continue + } + u.payload = make([]byte, u.size) + copy(u.payload, payload[0:u.size]) + select { + case <-stopCh: + return + default: + udpDataCh <- u + } + } + }() + func() { + for { + select { + case u := <-udpDataCh: + process(u.size, u.payload, u.pktAddr, processor, localIP, addrUDP, name) + case <-stopCh: + return + } + } + }() + + for _ = range udpDataCh { + // drain + } + wg.Wait() + return nil +} + +func process(size int, payload []byte, pktAddr *net.UDPAddr, processor decoder.Processor, localIP string, addrUDP net.UDPAddr, name string) { + baseMessage := BaseMessage{ + Src: pktAddr.IP, + Port: pktAddr.Port, + Payload: payload, + } + processor.ProcessMessage(baseMessage) + + MetricTrafficBytes.With( + prometheus.Labels{ + "remote_ip": pktAddr.IP.String(), + "local_ip": localIP, + "local_port": strconv.Itoa(addrUDP.Port), + "type": name, + }). + Add(float64(size)) + MetricTrafficPackets.With( + prometheus.Labels{ + "remote_ip": pktAddr.IP.String(), + "local_ip": localIP, + "local_port": strconv.Itoa(addrUDP.Port), + "type": name, + }). + Inc() + MetricPacketSizeSum.With( + prometheus.Labels{ + "remote_ip": pktAddr.IP.String(), + "local_ip": localIP, + "local_port": strconv.Itoa(addrUDP.Port), + "type": name, + }). + Observe(float64(size)) +} diff --git a/third_party/goflow2/utils/utils_test.go b/third_party/goflow2/utils/utils_test.go new file mode 100644 index 000000000000..1f7a880f2cd1 --- /dev/null +++ b/third_party/goflow2/utils/utils_test.go @@ -0,0 +1,93 @@ +package utils + +import ( + "fmt" + "net" + "testing" + "time" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCancelUDPRoutine(t *testing.T) { + testTimeout := time.After(10 * time.Second) + port, err := getFreeUDPPort() + require.NoError(t, err) + dp := dummyFlowProcessor{} + go func() { + require.NoError(t, dp.FlowRoutine("127.0.0.1", port)) + }() + + // wait slightly so we give time to the server to accept requests + time.Sleep(100 * time.Millisecond) + + sendMessage := func(msg string) error { + conn, err := net.Dial("udp", fmt.Sprintf("127.0.0.1:%d", port)) + if err != nil { + return err + } + defer conn.Close() + _, err = conn.Write([]byte(msg)) + return err + } + require.NoError(t, sendMessage("message 1")) + require.NoError(t, sendMessage("message 2")) + require.NoError(t, sendMessage("message 3")) + + readMessage := func() string { + select { + case msg := <-dp.receivedMessages: + return string(msg.(BaseMessage).Payload) + case <-testTimeout: + require.Fail(t, "test timed out while waiting for message") + return "" + } + } + + // in UDP, messages might arrive out of order or duplicate, so whe just verify they arrive + // to avoid flaky tests + require.Contains(t, []string{"message 1", "message 2", "message 3"}, readMessage()) + require.Contains(t, []string{"message 1", "message 2", "message 3"}, readMessage()) + require.Contains(t, []string{"message 1", "message 2", "message 3"}, readMessage()) + + dp.Shutdown() + time.Sleep(100 * time.Millisecond) + + _ = sendMessage("no more messages should be processed") + + select { + case msg := <-dp.receivedMessages: + assert.Fail(t, fmt.Sprint(msg)) + default: + // everything is correct + } +} + +type dummyFlowProcessor struct { + stopper + receivedMessages chan interface{} +} + +func (d *dummyFlowProcessor) FlowRoutine(host string, port int) error { + _ = d.start() + d.receivedMessages = make(chan interface{}) + return UDPStoppableRoutine(d.stopCh, "test_udp", func(msg interface{}) error { + d.receivedMessages <- msg + return nil + }, 3, host, port, false, logrus.StandardLogger()) +} + +func getFreeUDPPort() (int, error) { + a, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") + if err != nil { + return 0, err + } + l, err := net.ListenUDP("udp", a) + if err != nil { + return 0, err + } + defer l.Close() + return l.LocalAddr().(*net.UDPAddr).Port, nil +} diff --git a/third_party/opa/.gitignore b/third_party/opa/.gitignore new file mode 100644 index 000000000000..ab9c6a2d379f --- /dev/null +++ b/third_party/opa/.gitignore @@ -0,0 +1,43 @@ +# development environment +.DS_Store +.vscode +.idea +*~ +*.swp + +# build artifacts +coverage.txt +opa_* +.Dockerfile_* +_release +wasm/_obj +_test +!*_test.go +site.tar.gz +policy.wasm +.npm +.gitbook +.go +release-notes.md +bundle.tar.gz + +# ci artifacts +fuzzit +ast-fuzzer +ast-fuzzer.a + +# runtime artifacts +policies + +# Local Netlify folder +.netlify + +# man pages +man + +# generated when running local website build +docs/website/.hugo_build.lock +docs/website/data/versions + +# generated when building windows binary to add icon and product version +resource.syso \ No newline at end of file diff --git a/third_party/opa/.go-version b/third_party/opa/.go-version new file mode 100644 index 000000000000..2f4320f67fe0 --- /dev/null +++ b/third_party/opa/.go-version @@ -0,0 +1 @@ +1.24.4 diff --git a/third_party/opa/.golangci.yaml b/third_party/opa/.golangci.yaml new file mode 100644 index 000000000000..56bac582543d --- /dev/null +++ b/third_party/opa/.golangci.yaml @@ -0,0 +1,215 @@ +run: + timeout: 5m + +issues: + max-same-issues: 0 # don't hide issues in CI runs because they are the same type + exclude-dirs: + - internal/gojsonschema + - internal/jwx + exclude-rules: + - path: ast/ + linters: + - staticcheck + text: "SA1019" + - path: bundle/ + linters: + - staticcheck + text: "SA1019" + - path: capabilities/ + linters: + - staticcheck + text: "SA1019" + - path: compile/ + linters: + - staticcheck + text: "SA1019" + - path: config/ + linters: + - staticcheck + text: "SA1019" + - path: cover/ + linters: + - staticcheck + text: "SA1019" + - path: debug/ + linters: + - staticcheck + text: "SA1019" + - path: dependencies/ + linters: + - staticcheck + text: "SA1019" + - path: download/ + linters: + - staticcheck + text: "SA1019" + - path: format/ + linters: + - staticcheck + text: "SA1019" + - path: hooks/ + linters: + - staticcheck + text: "SA1019" + - path: ir/ + linters: + - staticcheck + text: "SA1019" + - path: keys/ + linters: + - staticcheck + text: "SA1019" + - path: loader/ + linters: + - staticcheck + text: "SA1019" + - path: logging/ + linters: + - staticcheck + text: "SA1019" + - path: metrics/ + linters: + - staticcheck + text: "SA1019" + - path: plugins/ + linters: + - staticcheck + text: "SA1019" + - path: profiler/ + linters: + - staticcheck + text: "SA1019" + - path: refactor/ + linters: + - staticcheck + text: "SA1019" + - path: repl/ + linters: + - staticcheck + text: "SA1019" + - path: rego/ + linters: + - staticcheck + text: "SA1019" + - path: resolver/ + linters: + - staticcheck + text: "SA1019" + - path: runtime/ + linters: + - staticcheck + text: "SA1019" + - path: schemas/ + linters: + - staticcheck + text: "SA1019" + - path: sdk/ + linters: + - staticcheck + text: "SA1019" + - path: server/ + linters: + - staticcheck + text: "SA1019" + - path: storage/ + linters: + - staticcheck + text: "SA1019" + - path: tester/ + linters: + - staticcheck + text: "SA1019" + - path: topdown/ + linters: + - staticcheck + text: "SA1019" + - path: tracing/ + linters: + - staticcheck + text: "SA1019" + - path: types/ + linters: + - staticcheck + text: "SA1019" + - path: util/ + linters: + - staticcheck + text: "SA1019" + - path: version/ + linters: + - staticcheck + text: "SA1019" + +linters-settings: + lll: + line-length: 200 + gocritic: + disabled-checks: + - appendAssign + # NOTE(ae): this one should be enabled, but there were too + # many violations to fix in one go... revisit later + - singleCaseSwitch + # Reasonable rule, but not sure what to replace with in + # many locations, so disabling for now + - exitAfterDefer + # The following 3 rules are disabled from the perfomance tag + # enabled further down. The first two are reasonable, but not + # super important. appendCombine is really nice though! And + # should be enabled. Just many places to fix.. + - hugeParam + - preferFprint + - appendCombine + enabled-checks: + # NOTE that these are rules enabled in addition to the default set + - filepathJoin + - dupImport + - redundantSprint + - stringConcatSimplify + enabled-tags: + - performance + settings: + ifElseChain: + # ridiculous value set for now, but this should be + # lowered to something more reasonable, as the rule + # is reasonable (replace long if-else chains with + # switch)... just too many violations right now + minThreshold: 10 + govet: + enable: + - deepequalerrors + - nilness + perfsprint: + # only rule disabled by default, but it's a good one + err-error: true + revive: + rules: + # this mainly complains about us using min/max for variable names, + # which seems like an unlikely source of actual issues + - name: redefines-builtin-id + disabled: true + - name: unused-receiver + disabled: false + +linters: + disable-all: true + enable: + - mirror + - errcheck + - govet + - ineffassign + - intrange + - revive # replacement for golint + - gofmt + - goimports + - unused + - misspell + - usetesting + - typecheck + - staticcheck + - gosimple + - prealloc + - unconvert + - copyloopvar + - perfsprint + - gocritic + # - gosec # too many false positives diff --git a/third_party/opa/.regal/config.yaml b/third_party/opa/.regal/config.yaml new file mode 100644 index 000000000000..45da0a40842a --- /dev/null +++ b/third_party/opa/.regal/config.yaml @@ -0,0 +1,24 @@ +ignore: + files: + - "docs/*" + - "**/test/*" + - "**/testdata/*" + - "**/testfiles/*" + +rules: + idiomatic: + directory-package-mismatch: + exclude-test-suffix: true + ignore: + files: + - "docs" + - "internal/wasm/sdk/examples/*" + style: + line-length: + ignore: + files: + - "docs" + +project: + roots: + - build/policy diff --git a/third_party/opa/.trivyignore b/third_party/opa/.trivyignore new file mode 100644 index 000000000000..74dc0da0a35c --- /dev/null +++ b/third_party/opa/.trivyignore @@ -0,0 +1,35 @@ +# We're not directly using nor running these dependencies, and hence they're not applicable +# +# * github.com/satori/go.uuid - used by a dependency that imports containerd... the containerd import +# used here is not vulnerable though. +CVE-2021-3538 + +# * go.etcd.io/etcd - we don't run etcd as part of the OPA deployment +CVE-2018-1098 +CVE-2018-1099 + +# * k8s.io/kubernetes - we don't run kubernetes as part of the OPA deployment +CVE-2019-1002101 +CVE-2019-11250 +CVE-2019-11253 +CVE-2019-11254 +CVE-2020-8552 +CVE-2020-8554 +CVE-2020-8555 +CVE-2020-8557 +CVE-2020-8558 +CVE-2020-8559 +CVE-2020-8561 +CVE-2020-8562 +CVE-2020-8563 +CVE-2020-8564 +CVE-2020-8565 +CVE-2021-25735 +CVE-2021-25740 +CVE-2021-25741 + +# * github.com/emicklei/go-restful - we don't use its code in our handlers +CVE-2022-1996 + +# github.com/dgrijalva/jwt-go -- vulnerable version used by docker/distribution above +CVE-2020-26160 diff --git a/third_party/opa/.yamllint.yaml b/third_party/opa/.yamllint.yaml new file mode 100644 index 000000000000..7fd66d2e0798 --- /dev/null +++ b/third_party/opa/.yamllint.yaml @@ -0,0 +1,12 @@ +extends: default + +rules: + # Some of our testcases end up going past the default 80 character limit. + line-length: false + braces: + max-spaces-inside: 1 + comments: + min-spaces-from-content: 1 + indentation: + ignore: + - test-time-* diff --git a/third_party/opa/ADOPTERS.md b/third_party/opa/ADOPTERS.md new file mode 100644 index 000000000000..b495100b3ec9 --- /dev/null +++ b/third_party/opa/ADOPTERS.md @@ -0,0 +1,311 @@ +# Adopters + + + + +This is a list of organizations that have spoken publicly about their adoption or +production users that have added themselves (in alphabetical order): + +* [2U, Inc](https://2u.com) has incorporated OPA into their SDLC for both Terraform and Kubernetes deployments. + Shift left! + +* [APIwiz](https://www.apiwiz.io) has implemented OPA as a centralized service to enforce consistent + and secure authorization decisions across all internal APIs. By delegating authorization logic to OPA, + APIwiz streamlines access control, ensuring robust security throughout the platform. Furthermore, OPA + has been seamlessly integrated into APIwiz's API Builder, enabling users to embed policy-driven workflows. + This integration provides precise control over workflows, enhancing both the security and efficiency of + the platform's operations. + +* [Appsflyer](https://www.appsflyer.com/) uses OPA to make consistent + authorization decisions by hundreds of microservices for UI and API data + access. All authorization decisions are delegated to OPA that is deployed as a + central service. The decisions are driven by flexible policy rules that take + into consideration data privacy regulations and policies, data consents and + application level access permissions. For more information, see the [Appsflyer + Engineering Blog post](https://medium.com/appsflyer/authorization-solution-for-microservices-architecture-a2ac0c3c510b). + +* [Atlassian](https://www.atlassian.com/) uses OPA in a heterogeneous cloud + environment for microservice API authorization. OPA is deployed per-host and + inside of their Slauth (AAA) system. Policies are tagged and categorized + (e.g., platform, service, etc.) and distributed via S3. Custom log infrastructure + consumes decision logs. For more information see this talk from [OPA Summit 2019](https://www.youtube.com/watch?v=nvRTO8xjmrg). + +* [Bisnode](https://www.dnb.com/en-gb/about-us/we-are-now-dun-bradstreet.html) + (Dun & Bradstreet) uses OPA for a wide range of use cases, + including microservice authorization, fine grained kubernetes authorization, + validating and mutating admission control and CI/CD pipeline testing. Built + and maintains some OPA related tools and libraries, primarily to help + integrate OPA in the Java/JVM ecosystem, [see `github.com/Bisnode`](https://github.com/Bisnode). + +* [bol.com](https://www.bol.com/) uses OPA for a mix of + validating and mutating admission control use cases in their + Kubernetes clusters. Use cases include patching image pull secrets, + load balancer properties, and tolerations based on contextual + information stored on namespaces. OPA is deployed on multiple + clusters with ~100 nodes and ~300 namespaces total. + +* [BNY Mellon](https://www.bnymellon.com/) uses OPA as a sidecar to enforce access + control over applications based on external context coming from AD and other + internal services. For more information see this talk from [QCon 2019](https://www.infoq.com/presentations/opa-spring-boot-hocon/). + +* [Capital One](https://www.capitalone.com/) uses OPA to enforce a variety of + admission control policies across their Kubernetes clusters including image + registry allowlisting, label requirements, resource requirements, container + privileges, etc. For more information see this talk from [KubeCon US 2018](https://www.youtube.com/watch?v=CDDsjMOtJ-c&t=6m35s) + and this talk from [OPA Summit 2019](https://www.youtube.com/watch?v=vkvWZuqSk5M). + +* [Chef](https://www.chef.io/) integrates OPA to implement IAM-style + access control and enumerate user->resource permissions in Chef + Automate V2. The integration utilizes OPA's Partial Evaluation + feature to reduce evaluation time (in exchange for higher update + latency.) A high-level description can be found [in this blog + post](https://blog.chef.io/2019/01/24/introducing-the-chef-automate-identity-access-management-version-two-iam-v2-beta/), + and the code is Open Source, [see + `github.com/chef/automate`](https://github.com/chef/automate/tree/master/components/authz-service). + +* [cluetec.de](https://cluetec.de) primarily uses OPA to enforce fine-grained authorization + and data-filtering policies in its Spring-based microservices and multi-tenant SaaS. Policies + are mapped to tenant-specific domains and used to enrich the database queries without any code + modifications. OPA is also used to enforce admission control policies and RBAC in multi-tenant + Kubernetes clusters. + +* [Cloudflare](https://www.cloudflare.com/) uses OPA as a validating + admission controller to prevent conflicting Ingresses in their + Kubernetes clusters that host a mix of production and test + workloads. + +* [Cloudsmith](https://www.cloudsmith.com/) uses OPA to allow organizations to define, enforce, + and monitor policies across the artifact lifecycle. Cloudsmith users can leverage EPSS-based logic + in their Rego policies for more granular, data-informed decisions around vulnerability management. + For more information on how Cloudsmith uses Exploit Prediction Scoring System (EPSS) in OPA policies, + check out the [Cloudsmith Blog](https://cloudsmith.com/blog/cloudsmith-introduces-epss-scoring-in-enterprise-policy-management-epm). + +* [ControlPlane](https://control-plane.io) uses OPA to enforce enterprise-friendly + policy for safe adoption of Kubernetes, Istio, and cloud services. OPA policies + are validated and tested individually and en masse with unit tests and conftest. + This enables developers to validate local changes against production policies, + minimise engineering feedback loops, and reduce CI cycle time. Policies are + tested as "SDLC guardrails", then re-validated at deployment time by a range of + OPA-based admission controllers, covering single-tenant environments and hard + multi-tenancy configurations. + +* [Elastic](https://www.elastic.co/) uses OPA in its Cloud Security offering to enable CSPM and KSPM solutions, helping customers adhere to best practices + defined in CIS benchmarks by tracking misconfigurations on AWS, GCP and Azure. the code is Open Source, see [Security Policies](https://github.com/elastic/cloudbeat/tree/main/security-policies). + +* [Facets.cloud](https://www.facets.cloud/) is a DevOps platform designed to streamline software development and deployment processes. + The integration of Open Policy Agent (OPA) has been a key factor in developing our [Guardrails Policy](https://readme.facets.cloud/docs/guardrail-policy) feature. + Managed using OPA, this feature enables our customers to set rules that align their software blueprints(detailed architectural designs of their software) - with established standards. + The Guardrails Policy feature has optimized resource management, minimized redundancy in policy definitions, and ensured comprehensive adherence to organizations’ best practices. + +* [Fugue](https://fugue.co) was a cloud security SaaS that uses OPA to + classify compliance violations and security risks in AWS and Azure + accounts and generate compliance reports and notifications. Now part of + [Snyk](https://snyk.com). + +* [Goldman Sachs](https://www.goldmansachs.com/) uses OPA to enforce admission control + policies in their multi-tenant Kubernetes clusters as well as for _provisioning_ + RBAC, PV, and Quota resources that are central to the security and operation of + these clusters. For more information see this talk from [KubeCon US 2019](https://www.youtube.com/watch?v=lYHr_UaHsYQ). + +* [Google Cloud](https://cloud.google.com/) uses OPA to validate Google Cloud + product's configurations in several products and tools, including + [Anthos Config Management](https://cloud.google.com/anthos/config-management), + [GKE Policy Automation](https://github.com/google/gke-policy-automation) or + [Config Validator](https://github.com/GoogleCloudPlatform/policy-library). See + [Creating policy-compliant Google Cloud resources article](https://cloud.google.com/architecture/policy-compliant-resources) + for example use cases. + +* [Infracost](https://www.infracost.io/) shows cloud cost estimates for Terraform. + It uses OPA to enable users to create cost policies, and setup guardrails such + as "this change puts the monthly costs above $10K, which is the budget for this + product. Consider asking the team lead to review it". See [the docs](https://www.infracost.io/docs/features/cost_policies/) for details. + +* [Intuit](https://www.intuit.com/company/) uses OPA as a validating + and mutating admission controller to implement various security, + multi-tenancy, and risk management policies across approximately 50 + clusters and 1,000 namespaces. For more information on how Intuit + uses OPA see [this talk from KubeCon Seattle 2018](https://youtu.be/CDDsjMOtJ-c?t=980). + +* [Jetstack](https://www.jetstack.io) uses OPA on customer projects to validate + resources deployed to Kubernetes environments are conformant with + organization rules. This has involved both validating and mutating resources + as well as the following related projects: conftest, konstraint, and + Gatekeeper. Jetstack also uses OPA via the Golang API in _Jetstack Secure_ to + automate the checking of resources against our best practice recommendations. + +* [Marsh McLennan](https://www.marshmclennan.com) uses OPA Gatekeeper in their + Kubernetes clusters, and OPA as an authorization decision point by many + applications for ingress traffic. Some applications also use OPA as a rules + engine. + +* [Medallia](https://www.medallia.com/) uses OPA to audit AWS + resources for compliance violations. The policies search across + state from Terraform and AWS APIs to identify security violations + and identify high-risk configurations. The policies ingest 1,000s of + AWS resources to generate the final report. + +* [Mercari](https://www.mercari.com/) uses OPA to enforce admission control + policies in their multi-tenant Kubernetes clusters. It helps maintain + the governance of the cluster, checking that developers are following + the best practices in the admission controller. They also use [confest](https://github.com/open-policy-agent/conftest) to + enforce policies in their CI/CD pipeline. + +* [Mia-Platform](https://mia-platform.eu/) uses OPA to run RBAC authorization policies + distributed within the application microservices. They built [Rönd](https://github.com/rond-authz/rond) + sidecar to intercept API invocation in the kubernetes ecosystem and created an extensible + RBAC solution that protects the application with little-to-none changes to the existing codebase. + +* [Netflix](https://www.netflix.com) uses OPA as a method of enforcing + access control in microservices across a variety of languages and + frameworks for thousands of instances in their cloud + infrastructure. Netflix takes advantage of OPA's ability to bring in + contextual information and data from remote resources in order to + evaluate policies in a flexible and consistent manner. For a + description of how Netflix has architected access control with OPA + check out [this talk from KubeCon Austin 2017](https://www.youtube.com/watch?v=R6tUNpRpdnY). + +* [Pinterest](https://www.pinterest.com/) uses OPA to solve multiple policy-related use cases + including access control in Kafka, Envoy, and Jenkins! At peak, their Kafka-OPA + integration handles ~400K QPS without caching. With caching the system + handles ~8.5M QPS. For more information see this talk from [OPA Summit 2019](https://www.youtube.com/watch?v=LhgxFICWsA8). + +* [Pix4D](https://www.pix4d.com/) uses OPA to run and define RBAC authorization policies for + the users of its cloud platform. Defining the policies in OPA ensures a single source of + controls and a consistent policy enforcement for any microservices. It operates as a + sidecar to a Django application exposing access roles of users over resources. + +* [Plex Systems](https://www.plex.com) uses OPA to enforce policy throughout + their entire release process; from local development to continuous production + audits. The CI/CD pipelines at Plex leverage [conftest](https://github.com/instrumenta/conftest), + a policy enforcement tool that relies on OPA, to automatically reject changes that do not adhere + to defined policies. Plex also uses + [Gatekeeper](https://github.com/open-policy-agent/gatekeeper), a Kubernetes policy controller, as + a means to enforce policies within their Kubernetes clusters. The general-purpose nature of OPA + has enabled Plex to have a consistent means of policy enforcement, + no matter the environment. + +* [Splash](https://splashthat.com) uses OPA to handle fine-grained authorization + across its entire platform, implemented as both a sidecar in Kubernetes and a separate + container on bare instances. Policies and datasets are recompiled and updated based + on changes to users' roles and permissions. + +* [SAP/InfraBox](https://github.com/SAP/Infrabox) integrates OPA to + implement authorization over HTTP API resources. OPA policies + evaluate user and permission data replicated from Postgres to make + access control decisions over projects, collaborators, jobs, + etc. SAP/Infrabox is used in production within SAP and has several + external users. + +* [Terminus Software](https://terminus.com/) uses OPA for microservice authorization. + +* [T-Mobile](https://www.t-mobile.com) uses OPA as a core component for their + [MagTape](https://github.com/tmobile/magtape/) project that enforces best + practices and secure configurations across their fleet of Kubernetes + clusters (more info in [this blog post](https://opensource.t-mobile.com/blog/posts/rolling-out-the-magenta-tape/)). + T-Mobile also leverages OPA to enforce authorization workflows within their + Corporate Delivery Platform (CI/CD). + +* [Tremolo Security](https://www.tremolosecurity.com/) uses OPA at a + London-based financial services company to inject annotations and + volume mount parameters into Kubernetes Pods so that workloads can + connect to off-cluster CIFS drives and SQL Server + instances. Policies are based on external context sourced from + OpenUnison. Ability to validate policies offline is a huge win + because the clusters are air-gapped. For more information on how + Tremolo Security uses OPA see [this blog post](https://www.tremolosecurity.com/beyond-rbac-in-openshift-open-policy-agent/). + +* [Tripadvisor](http://tripadvisor.com/) uses OPA to enforce + admission control policies in Kubernetes. In the process of rolling out OPA, + they created an integration testing framework that verifies clusters are accepting + and rejecting the right objects when OPA is deployed. For more information see + this talk from [OPA Summit 2019](https://www.youtube.com/watch?v=X09c1eXvCFM). + +* [Very Good Security (VGS)](https://www.vgs.io/) integrates OPA to + implement a fine-grained permission system and enumerate + user->resource permissions in their product. The backend is + architected as a collection of (polyglot) microservices running on + Kubernetes that offload policy decisions to OPA sidecars. VGS has + implemented a synchronization protocol on top of the Bundle and + Status APIs so that the system can determine when permission updates + have propagated. For more details on the VGS use case see this + [blog post](https://www.verygoodsecurity.com/blog/posts/building-a-fine-grained-permission-system-in-a-distributed-environment). + +* [VNG Cloud](https://www.vngcloud.vn/en/home) [Identity and Access Management (IAM)](https://iam.vngcloud.vn/) + use OPA as a policy-based decision engine for authorization. IAM provides administrators with fine-grained + access control to VNG Cloud resources and help centralize and manage permissions to access resources. + Specifically, OPA is integrated to evaluate policies to make the decision about denying or allowing incoming requests. + +* [Wiz](https://www.wiz.io/) helps every organization rapidly remove the most critical + risks in their cloud estate. It simply connects in minutes, requires zero agents, and + automatically correlates the entire security stack to uncover the most pressing issues. + Wiz policies leverage Open Policy Agent (OPA) for a unified framework across the + cloud-native stack. Whether for configurations, compliance, IaC, and more, OPA enables + teams to move faster in the cloud. For more information on how Wiz uses OPA, [contact Wiz](https://www.wiz.io/contact/). + +* [Xenit AB](https://www.xenit.se/) uses OPA to implement fine-grained control + over resource formulation in its managed Kubernetes service as well as several + customer-specific implementations. For more information, see the Kubernetes Terraform library + [OPA Gatekeeper module](https://github.com/XenitAB/terraform-modules/tree/main/modules/kubernetes/gatekeeper) and + [OPA Gatekeeper policy library](https://github.com/XenitAB/gatekeeper-library). + +* [Yelp](https://www.yelp.com/) use OPA and Envoy to enforce authorization policies + across a fleet of microservices that evolved out of a monolithic architecture. + For more information see this talk from [KubeCon US 2019](https://www.youtube.com/watch?v=Z6aN3Smt-9M). + +In addition, there are several production adopters that prefer to +remain anonymous. + +* **A Fortune 100 company** uses OPA to implement validating admission + control and fine-grained authorization policies on ~10 Kubernetes + clusters with ~1,000 nodes. They also integrate OPA into their PKI + as part of a Certificate RA that serves these clusters. + +This is a list of adopters in early stages of production or +pre-production (in alphabetical order): + +* [Aserto](https://www.aserto.com/) is a venture-backed developer API company + that helps developers easily build permissions and roles into their SaaS + applications. Aserto uses OPA as its core engine, and has contributed projects + such as [Open Policy Containers](https://openpolicycontainers.com/) and + [OPA Runtime](https://github.com/aserto-dev/runtime) that make it easier for + developers to incorporate OPA policies and the OPA engine into their applications. + +* [Cyral](https://www.cyral.com/) is a venture-funded data security + company. Still in stealth mode but using OPA to manage and enforce + fine-grained authorization policies. + +* [Permit.io](https://permit.io) Uses a combination of OPA and OPAL + to power fine-grained authorization policies at the core of the Permit.io platform. + Permit.io leverages the power of OPA's Rego language, + generating new Rego code on the fly from its UI policy editor. + The team behind Permit.io contributes to the OPA ecosystem - creating opens-source projects like + [OPAL- making OPA event-driven)](https://github.com/permitio/opal) + and [OPToggles - sync Frontend with open-policy](https://github.com/permitio/OPToggles). + +* [Scalr](https://scalr.com/) is a remote operations backend for Terraform + that helps users scale their Terraform usage through automation and collaboration. + [Scalr uses OPA](https://docs.scalr.com/en/latest/opa.html) to validate Terraform + code against organization standards and allows for approvals prior to a Terraform apply. + +* [Spacelift](https://spacelift.io) is a specialized CI/CD platform + for infrastructure-as-code. Spacelift is [using OPA](https://docs.spacelift.io/concepts/policy) to provide flexible, + fine-grained controls at various application decision points, including + automated code review, defining access levels or blocking execution of + unwanted code. + +* [Magda](https://github.com/magda-io/magda) is a federated, Kubernetes-based, open-source data catalog system. Working as Magda's central authorisation policy engine, OPA helps not only the API endpoint authorisation. Magda also uses its partial evaluation feature to translate datasets authorisation decisions to other database-specific DSLs (e.g. SQL or Elasticsearch DSL) and use them for dataset authorisation enforcement in different databases. + +* [VodafoneZiggo](https://www.vodafoneziggo.nl/) Is a Dutch telecommunications company that uses OPA to power authorisation decisions in our internal developer platform based on Backstage, it is also used as a way to enforce and validate component metadata that is onboarded as software components into the Backstage software catalog. + +Other adopters that have gone into production or various stages of +testing include: + +* [Cisco](https://www.cisco.com/) +* [Nefeli Networks](https://nefeli.io) +* [SolarWinds](https://www.solarwinds.com/) via [Lee Calcote](https://github.com/leecalcote) +* [State Street Corporation](http://www.statestreet.com/) +* [PITS Global Data Recovery Services](https://www.pitsdatarecovery.net/) + +If you have adopted OPA and would like to be included in this list, +feel free to submit a PR updating this file or +[open an issue](https://github.com/open-policy-agent/opa/issues/new?assignees=&labels=adopt-opa&template=adopt-opa.yaml&title=organization_name+has+adopted+OPA). diff --git a/third_party/opa/CHANGELOG.md b/third_party/opa/CHANGELOG.md new file mode 100644 index 000000000000..59ae566ecace --- /dev/null +++ b/third_party/opa/CHANGELOG.md @@ -0,0 +1,7533 @@ +# Change Log + +All notable changes to this project will be documented in this file. This +project adheres to [Semantic Versioning](http://semver.org/). + +## 1.7.1 + +This is a bug fix release addressing two issues for users that include OPA's CLI in their own application's CLI: + - A missing symbol in the `cmd` package (`cmd.RootCommand`) + - A possible panic in the `opa parse` command + +## 1.7.0 + +This release contains a mix of new features, performance improvements, and bugfixes. Notably: + +- Improved OPA SDK/API for better extensibility + +### SDK Improvements + +The OPA SDK/API has been improved to provide better extensibility an more points of integration for developers. + +- ast: Add `DefaultModuleLoader` ([#7794](https://github.com/open-policy-agent/opa/pull/7794)) authored by @srenatus +- ast: Add feature registration from the outside ([#7782](https://github.com/open-policy-agent/opa/pull/7782)) authored by @srenatus +- bundle: Add support for bundle store and activation plugins ([#7771](https://github.com/open-policy-agent/opa/pull/7771)) authored by @philipaconrad +- cmd: Allow branding ([#7797](https://github.com/open-policy-agent/opa/pull/7797)) authored by @srenatus +- decisionlogs: Add custom fields grab bag ([#7793](https://github.com/open-policy-agent/opa/pull/7793)) authored by @srenatus +- plugins: allow registering handlerfuncs with name+path ([#7769](https://github.com/open-policy-agent/opa/pull/7769)) authored by @srenatus +- rego: Expose `QueryTracers`, `tracing.Options` and `Cancel` from `QueryContext` ([#7767](https://github.com/open-policy-agent/opa/pull/7767)) authored by @philipaconrad +- rego: Pass along `TracingOpts` into `EvalContext` ([#7778](https://github.com/open-policy-agent/opa/pull/7778)) authored by @srenatus +- runtime: add `ExtraDiscoveryOpts` to `runtime.Params` ([#7766](https://github.com/open-policy-agent/opa/pull/7766)) authored by @srenatus +- sdk: Allow for setting default options for all instances ([#7760](https://github.com/open-policy-agent/opa/pull/7760)) authored by @srenatus +- server: Add hooks wiring + new hooks for inter-query caches ([#7775](https://github.com/open-policy-agent/opa/pull/7775)) authored by @srenatus +- server: Ensure that wrapped middlewares all support `http.Flusher` ([#7772](https://github.com/open-policy-agent/opa/pull/7772)) authored by @srenatus +- server/authorizer: Allow adding paths to validator ([#7792](https://github.com/open-policy-agent/opa/pull/7792)) authored by @philipaconrad +- server+plugins: Allow plugins to inject http handler middlewares ([#7789](https://github.com/open-policy-agent/opa/pull/7789)) authored by @srenatus reported by @deeglaze +- store+runtime: Extension points for custom stores ([#7779](https://github.com/open-policy-agent/opa/pull/7779)) authored by @srenatus +- test+eval: Add helper to smuggle compiler through context ([#7790](https://github.com/open-policy-agent/opa/pull/7790)) authored by @srenatus +- tester: Support `uint64` and `float64` metrics in `runBenchmark` ([#7761](https://github.com/open-policy-agent/opa/pull/7761)) authored by @srenatus + +### Runtime, Tooling + +- build: Show a warning when .manifest is ignored ([#7807](https://github.com/open-policy-agent/opa/pull/7807)) authored by @charlieegan3 +- cli: Avoid os.Exit() in Run() funcs ([#7788](https://github.com/open-policy-agent/opa/pull/7788)) authored by @srenatus +- config: Keep unknown env replacements ([#7786](https://github.com/open-policy-agent/opa/pull/7786)) authored by @srenatus +- format: Not bracketing keywords in imports ([#7742](https://github.com/open-policy-agent/opa/issues/7742)) authored by @johanfylling +- loader: Add bundle lazy loading mode across the runtime. ([#7768](https://github.com/open-policy-agent/opa/pull/7768)) authored by @philipaconrad +- loader: Pass bundle name in `AsBundle()` ([#7798](https://github.com/open-policy-agent/opa/pull/7798)) authored by @srenatus +- opa exec: stop plugins before exit ([#7760](https://github.com/open-policy-agent/opa/pull/7760)) authored by @srenatus +- plugins/discovery: Make `Factories()` merge the factories ([#7777](https://github.com/open-policy-agent/opa/pull/7777)) authored by @srenatus +- plugins/discovery: Replace environment variables after evaluation ([#7787](https://github.com/open-policy-agent/opa/pull/7787)) authored by @philipaconrad +- plugins/logs: Add experimental intermediate results field ([#7796](https://github.com/open-policy-agent/opa/pull/7796)) authored by @philipaconrad +- report: Fetching latest OPA release version from GitHub ([#7756](https://github.com/open-policy-agent/opa/pull/7756)) authored by @johanfylling + OPA will no longer send telemetry data when fetching the latest release version. +- runtime: Allow enabling NDBCache by default ([#7780](https://github.com/open-policy-agent/opa/pull/7780)) authored by @srenatus +- server+logging: Add `BatchDecisionID` field to Decision Logs ([#7791](https://github.com/open-policy-agent/opa/pull/7791)) authored by @philipaconrad +- store: Improve conflicting root error message ([#7806](https://github.com/open-policy-agent/opa/issues/7806)) authored by @charlieegan3 + +### Compiler, Topdown and Rego + +- perf: AST compiler optimizations ([#7740](https://github.com/open-policy-agent/opa/pull/7740)) authored by @anderseknert + +### Docs, Website + +**Note:** While we have been working on the new website we have been showing +the edge documentation contents (as contents and framework changes often must +go hand in hand). Now that the website development pace has slowed and the +functionality is more stable, we will be returning to showing the documentation +content from the latest release instead. Please use the +[edge documentation site](https://edge--opa-docs.netlify.app/) +to review new changes. PR previews are also based on the latest branch commit. +This change will be made to show the v1.7.0 release shortly after publishing. + +- docs: Add examples for crypto.sha256 and base64.encode built-in functions ([#7762](https://github.com/open-policy-agent/opa/pull/7762)) authored by @ToluGIT +- docs: Break out the built-in categories in policy ref ([#7722](https://github.com/open-policy-agent/opa/pull/7722)) authored by @sky3n3t +- docs: Correctly spell NetBSD ([#7738](https://github.com/open-policy-agent/opa/pull/7738)) authored by @iamleot +- docs: Fix a number of minor docs typos ([#7799](https://github.com/open-policy-agent/opa/pull/7799)) authored by @charlieegan3 +- docs: Fix `/docs/envoy-authorization/` `404` ([#7755](https://github.com/open-policy-agent/opa/issues/7755) authored by @charlieegan3 +- docs: Remove link to OPA playground share ([#7750](https://github.com/open-policy-agent/opa/pull/7750)) authored by @charlieegan3 +- docs: Revise docs index page wording ([#7805](https://github.com/open-policy-agent/opa/pull/7805)) authored by @charlieegan3 +- docs: Update warning note in GraphQL API docs ([#7737](https://github.com/open-policy-agent/opa/pull/7737)) authored by @charlieegan3 +- website: Add wildcard CORS for data/versions.json ([#7784](https://github.com/open-policy-agent/opa/pull/7784)) authored by @charlieegan3 +- website: Ensure no hscroll on built-in tables ([#7773](https://github.com/open-policy-agent/opa/pull/7773)) authored by @charlieegan3 +- website: Render versions under `/data/versions.json` ([#7783](https://github.com/open-policy-agent/opa/pull/7783)) authored by @charlieegan3 +- website: Set mobile and desktop tab sizes ([#7774](https://github.com/open-policy-agent/opa/pull/7774)) authored by @charlieegan3 +- website: Show link to the edge release of the docs ([#7776](https://github.com/open-policy-agent/opa/pull/7776)) authored by @charlieegan3 + +### Miscellaneous + +- Benchmark fixes ([#7765](https://github.com/open-policy-agent/opa/pull/7765)) authored by @anderseknert +- Use Regal for linting Rego ([#7752](https://github.com/open-policy-agent/opa/pull/7752)) authored by @anderseknert +- Use shorthand form for types ([#7757](https://github.com/open-policy-agent/opa/pull/7757)) authored by @anderseknert +- .github: Use types for issues ([#7751](https://github.com/open-policy-agent/opa/pull/7751)) authored by @charlieegan3 +- build: Add top-level token permissions for workflows ([#7795](https://github.com/open-policy-agent/opa/pull/7795)) authored by @timothyklee + +- docs/build: Link checker fixes ([#7743](https://github.com/open-policy-agent/opa/pull/7743)) authored by @charlieegan3 +- Dependency updates; notably: + - build(deps): bump github.com/containerd/containerd/v2 from 2.1.1 to 2.1.3 + - build(deps): bump google.golang.org/grpc from 1.73.0 to 1.74.2 + - build(deps): bump go.opentelemetry.io deps from 1.36.0/0.61.0 to 1.37.0/0.62.0 + +## 1.6.0 + +This release contains a mix of new features, performance improvements, and bugfixes. Notably: + +- Improvements to the OPA website and documentation +- Allowing keywords in Rego references +- Parallel test execution +- Faster built-in function execution + +### Modernized OPA Website ([#7037](https://github.com/open-policy-agent/opa/issues/7037)) + +We're continuing to modernize the OPA website with a new design and improved user experience. + +Some highlights: + +- [Builtins](https://www.openpolicyagent.org/docs/policy-reference#built-in-functions): You can now search them on the docs page! +- Sidebar redesign: Making it easier to find what you're looking for in our docs +- Feedback forms: Closing the feedback loop between docs authors and readers -- Please let us know if you dislike, or like, a docs page. +- [Downloads page](https://www.openpolicyagent.org/docs#1-download-opa): Find your OS' installation instructions on a less cluttered page! +- And much more + +Authored by @sky3n3t and @charlieegan3 + +### Allowing keywords in Rego references ([#7709](https://github.com/open-policy-agent/opa/pull/7709)) + +Previously, Rego references could not contain terms that conflict with Rego keywords such as `package`, `if`, `else`, `not`, etc. +in certain constructs: + +```rego +package example + +allow if { + input.package.source # not allowed (before v1.6.0) + input["package"].destination # allowed +} +``` + +The constraints for valid Rego references have been relaxed to allow keywords. +The above example is now valid and will no longer cause a compilation error. + +Authored by @johanfylling + +### Parallel Test Execution ([#7442](https://github.com/open-policy-agent/opa/issues/7442)) + +By default, OPA will now run tests in parallel (defaulting to one parallel execution thread per available CPU core), significantly speeding up test execution time for large test suites. +The performance boost is closely tied to the number of tests in your project and your selected parallelism level. For larger projects and default settings, 2-3x performance gains have been measured on a MacBook Pro. + +Parallelism can be disabled to run tests sequentially by setting the `--parallel` flag to `1`. E.g. `opa test . --parallel=1`. + +Authored by @sspaink reported by @anderseknert + +### Faster Builtin Function Evaluation + +The builtin context, an internal construct of OPA's evaluation engine, was previously provided to every builtin function. +As it turns out, only very few of them actually need it, for caching, cancellation, or lookups. +Those builtins are still provided with a builtin context, but for calls to all other builtins, we save the memory required by it. +The impact is tremendous: Even though the size of a single builtin context is only about 270 bytes, in an example application (Regal), this change brings about 360 MB of reduced memory usage! + +Authored by @anderseknert + +### Runtime, Tooling, SDK + +- cmd/check: `opa check --bundle` report virtual/base doc conflicts ([#7701](https://github.com/open-policy-agent/opa/pull/7701)) authored by @anderseknert + When `opa check` is used with the `--bundle` flag, an error will be reported if the provided json/yaml data has a conflicting overlap with the virtual documents generated by Rego rules. Such conflicts are ambiguous and can lead to unexpected evaluation results, and should be resolved. +- cmd/inspect: Fixing missing annotations location in `opa inspect` with JSON format ([#7459](https://github.com/open-policy-agent/opa/issues/7459)) authored by @johanfylling reported by @mostealth +- cmd/parse: Expose `--v0-compatible` flag ([#7668](https://github.com/open-policy-agent/opa/pull/7668)) authored by @tsandall +- cmd/refactor: Fix src:dst parsing to deal with colons ([#7648](https://github.com/open-policy-agent/opa/pull/7648)) authored by @tsandall +- metrics: Fix restartable timer bug. ([#7669](https://github.com/open-policy-agent/opa/pull/7669)) authored by @philipaconrad +- metrics: Prealloc maps + add benchmark ([#7664](https://github.com/open-policy-agent/opa/pull/7664)) authored by @philipaconrad +- oracle: Add support for some and every ([#7716](https://github.com/open-policy-agent/opa/pull/7716)) authored by @charlieegan3 +- oracle: Support object refs in FindDefinition ([#7711](https://github.com/open-policy-agent/opa/pull/7711)) authored by @charlieegan3 +- plugin/decision: Check if event is too large after compression ([#7526](https://github.com/open-policy-agent/opa/issues/7526)) authored by @sspaink +- runtime,server: Replace gorilla/mux dependency with http.ServeMux ([#7676](https://github.com/open-policy-agent/opa/pull/7676)) authored by @anderseknert + **Note**: This is a potentially breaking change for go API users directly interfacing with the OPA server's routing. +- server: Fix deferred metrics timers. ([#7671](https://github.com/open-policy-agent/opa/pull/7671)) authored by @philipaconrad +- server: Fix query url when opa is served not from root path ([#7644](https://github.com/open-policy-agent/opa/pull/7644)) authored by @olegKoshmeliuk + **Note**: This is only applicable for the web UI hosted by OPA on its root path (`/`) and OPA is served at some other path than root. + +### Compiler, Topdown and Rego + +- ast: Ensure surplus leading zeros always error ([#7726](https://github.com/open-policy-agent/opa/pull/7726)) authored by @charlieegan3 + **Note**: Primitive Rego number values with leading zeros (e.g. `0123`) are now considered invalid at time of parsing and will generate an error. If you're impacted by this change, please update your policies to not have numbers with leading zeros. E.g. `0123` should be changed to `123`. +- ast: Fixing type-checker schema cache race condition for inlined schemas ([#7679](https://github.com/open-policy-agent/opa/issues/7679), [7571](https://github.com/open-policy-agent/opa/issues/7571)) authored by @johanfylling reported by @daniel-petrov-gig +- perf: Improve performance when referencing "global" in loop ([#7654](https://github.com/open-policy-agent/opa/issues/7654)) authored by @anderseknert +- topdown: Fix issue where path in `walk` would get mutated ([#7656](https://github.com/open-policy-agent/opa/issues/7656)) authored by @anderseknert reported by @robmyersrobmyers +- topdown/http: Lenient application/json Content-Type header ([#6684](https://github.com/open-policy-agent/opa/issues/6684)) authored by @sspaink reported by @mrvanes + +### Docs, Website, Ecosystem + +- adopters: add Pix4D as adopters for its RBAC service ([#7645](https://github.com/open-policy-agent/opa/pull/7645)) authored by @marcaurele +- api: Expand docs for RegisterBuiltin — no thread-safety ([#7667](https://github.com/open-policy-agent/opa/issues/7667)) authored by @anderseknert reported by @parth-mehta-989 +- docs: Added a search function for the builtins section of policy-reference ([#7704](https://github.com/open-policy-agent/opa/pull/7704)) authored by @sky3n3t +- docs: Add another OR note in AND section ([#7706](https://github.com/open-policy-agent/opa/pull/7706)) authored by @charlieegan3 +- docs: Add basic docs covering CI/CD use case ([#7703](https://github.com/open-policy-agent/opa/pull/7703)) authored by @charlieegan3 +- docs: Add current ecosystem contribution docs ([#7678](https://github.com/open-policy-agent/opa/pull/7678)) authored by @charlieegan3 +- docs: Add EvergreenCodeBlock for code with version ([#7706](github.com/open-policy-agent/opa/pull/7706)) authored by @charlieegan3 +- docs: Add feedback form for user reported issues ([#7662](https://github.com/open-policy-agent/opa/pull/7662)) authored by @charlieegan3 +- docs: Address broken links ([#7661](https://github.com/open-policy-agent/opa/pull/7661)) authored by @charlieegan3 +- docs: Archive explain that only latest patch is shown ([#7682](https://github.com/open-policy-agent/opa/pull/7682)) authored by @charlieegan3 +- docs: Fix bug where the search match respects case ([#7713](https://github.com/open-policy-agent/opa/pull/7713)) authored by @sky3n3t +- docs: Hide feedback pop-up forever if dismissed ([#7674](https://github.com/open-policy-agent/opa/pull/7674)) authored by @charlieegan3 +- docs: Improve bundle structure documentation ([#7683](https://github.com/open-policy-agent/opa/pull/7683)) authored by @charlieegan3 +- docs: Improve explanations for initial examples ([#7677](https://github.com/open-policy-agent/opa/pull/7677)) authored by @charlieegan3 +- docs: Install/Download Instruction Update ([#7687](https://github.com/open-policy-agent/opa/pull/7687)) authored by @charlieegan3 +- docs: Move code example data inside the PlaygroundComponent ([#7724](https://github.com/open-policy-agent/opa/pull/7724)) authored by @sky3n3t +- docs: policy-reference, update sig algs formatting ([#7685](https://github.com/open-policy-agent/opa/pull/7685)) authored by @charlieegan3 +- docs: Redirect old admission control link ([#7730](https://github.com/open-policy-agent/opa/pull/7730)) authored by @charlieegan3 +- docs: Refactored Networking Reference docs ([#7686](https://github.com/open-policy-agent/opa/pull/7686)) authored by @sky3n3t +- docs: Revise sidebar order and layout ([#7731](https://github.com/open-policy-agent/opa/pull/7731)) authored by @charlieegan3 +- docs: Reworked existing policy examples to use PlaygroundExample ([#7690](https://github.com/open-policy-agent/opa/pull/7690)) authored by @sky3n3t +- docs: Show a feedback popup on the docs site ([#7663](https://github.com/open-policy-agent/opa/pull/7663)) authored by @charlieegan3 +- docs: Show edge rather than latest release ([#7717](https://github.com/open-policy-agent/opa/pull/7717)) authored by @charlieegan3 +- docs: Show TOC on CLI page ([#7712](https://github.com/open-policy-agent/opa/pull/7712)) authored by @charlieegan3 +- docs: Update colors for feedback form in dark mode ([#7691](https://github.com/open-policy-agent/opa/pull/7691)) authored by @charlieegan3 +- docs: Update policy-ref allowing anchor linking ([#7675](https://github.com/open-policy-agent/opa/pull/7675)) authored by @charlieegan3 +- docs: Update rego in deployment examples ([#7707](https://github.com/open-policy-agent/opa/pull/7707)) authored by @charlieegan3 +- docs: Update sidebar ([#7723](https://github.com/open-policy-agent/opa/pull/7723)) authored by @charlieegan3 +- website: Disable cancel script ([#7719](https://github.com/open-policy-agent/opa/pull/7719)) authored by @charlieegan3 +- website: Explain automation in RELEASE.md ([#7721](https://github.com/open-policy-agent/opa/pull/7721)) authored by @charlieegan3 +- website: Fix badge endpoints ([#7653](https://github.com/open-policy-agent/opa/pull/7653)) authored by @charlieegan3 +- website: Refactor site components with CSS modules ([#7666](https://github.com/open-policy-agent/opa/pull/7666)) authored by @charlieegan3 +- website: Update docusaurus components to 3.8.1 ([#7718](https://github.com/open-policy-agent/opa/pull/7718)) authored by @charlieegan3 + +### Miscellaneous + +- build: Better detection of go changes ([#7696](https://github.com/open-policy-agent/opa/pull/7696)) authored by @charlieegan3 +- build: Bump golang 1.24.3 -> 1.24.4 ([#7672](https://github.com/open-policy-agent/opa/pull/7672)) authored by @srenatus +- Adding Clarification to merge instructions when cutting a patch release ([#7660](https://github.com/open-policy-agent/opa/pull/7660)) authored by @johanfylling +- build: Make summary failure source clearer ([#7697](https://github.com/open-policy-agent/opa/pull/7697)) authored by @charlieegan3 +- build: Skip jobs for non docs changes ([#7688](https://github.com/open-policy-agent/opa/pull/7688)) authored by @charlieegan3 +- deps: Use `google.golang.org/protobuf` ([#7655](https://github.com/open-policy-agent/opa/pull/7655)) authored by @sspaink +- perf: Simplify interning ([#7714](https://github.com/open-policy-agent/opa/pull/7714)) authored by @anderseknert +- perf: Only pass built-in context to calls depending on it ([#7728](https://github.com/open-policy-agent/opa/pull/7728)) authored by @anderseknert +- perf: Improve built-in `concat` performance ([#7702](https://github.com/open-policy-agent/opa/pull/7702)) authored by @anderseknert +- perf: More efficient data/v1 POST handler ([#7673](https://github.com/open-policy-agent/opa/pull/7673)) authored by @anderseknert +- test: Fix flaky TestRaisingHTTPClientQueryError ([#7698](https://github.com/open-policy-agent/opa/pull/7698)) authored by @sspaink +- test: Fix flaky topdown query cache tests ([#7590](https://github.com/open-policy-agent/opa/issues/7590)) authored by @sspaink +- Dependency updates; notably: + - build(deps): Bump gqlparser from v2.5.27 to v2.5.28 ([#7699](https://github.com/open-policy-agent/opa/issues/7699)) authored by @robmyersrobmyers + - build(deps): bump github.com/go-logr/logr from 1.4.2 to 1.4.3 + - build(deps): bump github.com/vektah/gqlparser/v2 from 2.5.26 to 2.5.27 + - build(deps): bump golang.org/x/net from 0.39.0 to 0.40.0 + - build(deps): bump google.golang.org/grpc from 1.72.0 to 1.72.2 + - build(deps): bump oras.land/oras-go/v2 from 2.5.0 to 2.6.0 + - build(deps): bump go.opentelemetry.io deps to 1.36.0/0.61.0 + +## 1.5.1 + +This is a bug fix release addressing a regression to the [walk](https://www.openpolicyagent.org/docs/policy-reference#builtin-graph-walk) built-in function, introduced in v1.5.0. See [#7656](https://github.com/open-policy-agent/opa/issues/7656) (authored by @anderseknert reported by @robmyersrobmyers) + +## 1.5.0 + +This release contains a mix of new features, performance improvements, and bugfixes. Among others: + +- Support for AWS SSO credentials provider +- Support for signing client assertions with Azure Keyvault +- Faster `object.get`, `walk` and builtin-function evaluation +- Improved guardrails in the parser +- Improvements to decision logging + +### Modernized OPA Website ([#7037](https://github.com/open-policy-agent/opa/issues/7037)) + +The [OPA website](https://www.openpolicyagent.org/) has been modernized with a new design and improved user experience. + +The new site is based on Docusaurus and React which makes it easier to build live functionality and add non-documentation resources. +This lays the groundwork for even more improvements in the future! + +Documentation for older OPA versions are still available in the [version archive](https://www.openpolicyagent.org/docs/archive). + +Authored by @charlieegan3 + +### Runtime, Tooling, SDK + +- ast: Only use JSON-escaped literal when needed in ref to string convertion ([#7550](https://github.com/open-policy-agent/opa/issues/7550)) reported and authored by @xubinzheng +- ast: Parser recursion depth guard ([#7568](https://github.com/open-policy-agent/opa/pull/7568)) authored by @thevilledev +- ast: Retaining `SomeDecl` `Location` field when compiler resolves refs ([#7543](https://github.com/open-policy-agent/opa/issues/7543)) authored by @johanfylling +- bundle: Setting default rego-version in bundle API ([#7588](https://github.com/open-policy-agent/opa/issues/7588)) authored by @johanfylling reported by @xubinzheng +- perf: Improved "baseline" metrics of opa bench for trivial queries ([#7580](https://github.com/open-policy-agent/opa/pull/7580)) authored by @anderseknert +- plugins/decision: Don't drop adaptive uncompressed size limit on upload ([#7562](https://github.com/open-policy-agent/opa/issues/7562)) authored by @sspaink +- plugins/decision: Set config boundaries to upload_size_limit_bytes (#7563) (authored by @sspaink) +- plugins/rest: Add support for AWS SSO credentials provider ([#7527](https://github.com/open-policy-agent/opa/pull/7527)) authored by @efiShtain +- plugins/rest: Support signing of client assertions with Azure Keyvault ([#7462](https://github.com/open-policy-agent/opa/issues/7462)) reported and authored by @Od1nB +- plugins/status: Support graceful shutdown timeout ([#7576](https://github.com/open-policy-agent/opa/issues/6676)) authored by @sspaink +- rego: Don't generate JSON values for wildcard/generated keys in result set ([#7567](https://github.com/open-policy-agent/opa/pull/7567)) authored by @anderseknert +- runtime: Don't override user set version `commit` and `timestamp` ([#7471](https://github.com/open-policy-agent/opa/issues/7471)) reported by @kastl-ars authored by @sspaink + +### Planner, Topdown and Rego + +- planner: Deal with var-for-function replacement in indirect calls ([#5311](https://github.com/open-policy-agent/opa/issues/5311)) authored by @srenatus +- topdown: Faster `object.get` built-in function ([#7593](https://github.com/open-policy-agent/opa/pull/7593)) authored by @anderseknert +- topdown: Faster `walk` built-in function ([#7612](https://github.com/open-policy-agent/opa/pull/7612)) authored by @anderseknert +- topdown: Improved default rule value inlining ( ([#1418](https://github.com/open-policy-agent/opa/issues/1418)) authored by @johanfylling +- topdown: Improved GraphQL error handling ([#7622](https://github.com/open-policy-agent/opa/issues/7622)) reported and authored by @robmyersrobmyers + +### Docs, Website, Ecosystem + +- docs: Fix helm-kubernetes-quickstart bundle ([#7606](https://github.com/open-policy-agent/opa/pull/7606)) reported and authored by @nejec +- docs: Add Swift-OPA to the Ecosystem Page ([#7610](https://github.com/open-policy-agent/opa/pull/7610)) authored by @charlieegan3 +- docs: Add Tutorial Redirects ([#7603]https://github.com/open-policy-agent/opa/issues/7603) reported by @nataraj24 authored by @charlieegan3 +- Fix links in README ([#7633](https://github.com/open-policy-agent/opa/pull/7633)) authored by @ffjlabo + +### Miscellaneous + +- github_actions: Adding monthly check for broken hyperlinks ([#7537](https://github.com/open-policy-agent/opa/pull/7537)) authored by @sspaink +- perf: Extended interning ([#7636](https://github.com/open-policy-agent/opa/pull/7636)) authored by @anderseknert +- perf: `Ref.String()` shortcut on single var term ref ([#7595](https://github.com/open-policy-agent/opa/pull/7595)) authored by @anderseknert +- refactor: Don't return error from `opaTest` ([#7560](https://github.com/open-policy-agent/opa/pull/7560)) authored by @sspaink +- refactor: Remove internal/gqlparser and use upstream dependency instead. ([#7520](https://github.com/open-policy-agent/opa/issues/7520)) authored by @robmyersrobmyers +- test: Fix flaky TestContextErrorHandling ([#7587](https://github.com/open-policy-agent/opa/pull/7587)) authored by @sspaink +- Apply modernize linter fixes ([#7599](https://github.com/open-policy-agent/opa/pull/7599)) authored by @anderseknert +- Use `any` in place of `interface{}` ([#7566](https://github.com/open-policy-agent/opa/pull/7566)) authored by @anderseknert +- Dependency updates; notably: + - build: bump go from 1.24.0 to 1.24.3 + - build(deps): bump containerd to v2.1.1 ([#7627](https://github.com/open-policy-agent/opa/issues/7627)) authored by @johanfylling reported by @robmyersrobmyers + - build(deps): bump github.com/fsnotify/fsnotify from 1.8.0 to 1.9.0 + - build(deps): bump github.com/prometheus/client_golang from 1.21.1 to 1.22.0 + - build(deps): bump github.com/prometheus/client_model from 0.6.1 to 0.6.2 + - build(deps): bump golang.org/x/net from 0.38.0 to 0.39.0 + - build(deps): bump google.golang.org/grpc from 1.71.1 to 1.72.0 + +## 1.4.2 + +This is a bug fix release addressing the missing `capabilities/v1.4.1.json` in the v1.4.1 release. + +## 1.4.1 + +This is a security fix release for the fixes published in Go [1.24.1](https://groups.google.com/g/golang-announce/c/4t3lzH3I0eI) and [1.24.2](https://groups.google.com/g/golang-announce/c/Y2uBTVKjBQk) + +- build: bump go to 1.24.2 (#7544) (authored by @sspaink) + Addressing `CVE-2025-22870` and `CVE-2025-22871` vulnerabilities in the Go runtime. + +## 1.4.0 + +This release contains a security fix addressing CVE-2025-46569. +It also includes a mix of new features, bugfixes, and dependency updates. + +#### Security Fix: CVE-2025-46569 - OPA server Data API HTTP path injection of Rego ([GHSA-6m8w-jc87-6cr7](https://github.com/open-policy-agent/opa/security/advisories/GHSA-6m8w-jc87-6cr7)) + +A vulnerability in the OPA server's [Data API](https://www.openpolicyagent.org/docs/latest/rest-api/#data-api) allows an attacker to craft the HTTP path in a way that injects Rego code into the query that is evaluated. +The evaluation result cannot be made to return any other data than what is generated by the requested path, but this path can be misdirected, and the injected Rego code can be crafted to make the query succeed or fail; opening up for oracle attacks or, given the right circumstances, erroneous policy decision results. +Furthermore, the injected code can be crafted to be computationally expensive, resulting in a Denial Of Service (DoS) attack. + +**Users are only impacted if all of the following apply:** + +* OPA is deployed as a standalone server (rather than being used as a Go library) +* The OPA server is exposed outside of the local host in an untrusted environment. +* The configured [authorization policy](https://www.openpolicyagent.org/docs/latest/security/#authentication-and-authorization) does not do exact matching of the input.path attribute when deciding if the request should be allowed. + +**or, if all of the following apply:** + +* OPA is deployed as a standalone server. +* The service connecting to OPA allows 3rd parties to insert unsanitised text into the path of the HTTP request to OPA’s Data API. + +Note: With **no** [Authorization Policy](https://www.openpolicyagent.org/docs/latest/security/#authentication-and-authorization) configured for restricting API access (the default configuration), the RESTful [Data API](https://www.openpolicyagent.org/docs/latest/rest-api/#data-api) provides access for managing Rego policies; and the RESTful [Query API](https://www.openpolicyagent.org/docs/latest/rest-api/#query-api) facilitates advanced queries. +Full access to these APIs provides both simpler, and broader access than what the security issue describes here can facilitate. +As such, OPA servers exposed to a network are **not** considered affected by the attack described here if they are knowingly not restricting access through an Authorization Policy. + +This issue affects all versions of OPA prior to 1.4.0. + +See the [Security Advisory](https://github.com/open-policy-agent/opa/security/advisories/GHSA-6m8w-jc87-6cr7) for more details. + +Reported by @GamrayW, @HyouKash, @AdrienIT, authored by @johanfylling + +### Runtime, Tooling, SDK + +- ast: Adding `rego_v1` feature to `--v0-compatible` capabilities ([#7474](https://github.com/open-policy-agent/opa/pull/7474)) authored by @johanfylling +- executable: Add version and icon to OPA windows executable ([#3171](https://github.com/open-policy-agent/opa/issues/3171)) authored by @sspaink reported by @christophwille +- format: Don't panic on format due to unexpected comments ([#6330](https://github.com/open-policy-agent/opa/issues/6330)) authored by @sspaink reported by @sirpi +- format: Avoid modifying strings when formatting ([#6220](https://github.com/open-policy-agent/opa/issues/6220)) authored by @sspaink reported by @zregvart +- plugins/status: FIFO buffer channel for status events to prevent slow status API blocking ([#7522](https://github.com/open-policy-agent/opa/pull/7522)) authored by @sspaink + +### Topdown and Rego + +- gqlparser: Add JSON annotation in `internal/gqlparser/ast` to Position fields ([#7509](https://github.com/open-policy-agent/opa/pull/7509)) authored by @robmyersrobmyers +- graphql: Cache GraphQL schema parse results ([#7457](https://github.com/open-policy-agent/opa/pull/7457)) authored by @robmyersrobmyers +- topdown: Handling default functions in Partial Eval ([#7220](https://github.com/open-policy-agent/opa/issues/7220)) authored by @johanfylling +- topdown: Fix wall clock time init for `PartialRun()` ([#7490](https://github.com/open-policy-agent/opa/issues/7490)) authored by @srenatus +- topdown: Zero alloc lower/upper unless changed ([#7472](https://github.com/open-policy-agent/opa/pull/7472)) authored by @anderseknert + +### Docs, Website, Ecosystem + +- adopters: Cloudsmith adds support for OPA ([#7498](https://github.com/open-policy-agent/opa/pull/7498)) authored by @ndouglas-cloudsmith +- docs: Fixed broken docs link ([#7452](https://github.com/open-policy-agent/opa/issues/7452)) reported and authored by @fvarg00 +- docs: Update built-in function examples for OPA v1 ([#7514](https://github.com/open-policy-agent/opa/issues/7514)) reported and authored by @robmyersrobmyers +- docs: Add link to inline schema annotations ([#7496](https://github.com/open-policy-agent/opa/pull/7496)) authored by @kmadan +- docs: Add manual trigger to integration docs ([#7473](https://github.com/open-policy-agent/opa/pull/7473)) authored by @charlieegan3 +- docs: Point path versioned requests to new sites ([#7531](https://github.com/open-policy-agent/opa/pull/7531)) authored by @charlieegan3 +- docs: Update community slack inviter link ([#7488](https://github.com/open-policy-agent/opa/pull/7488), [#7493](https://github.com/open-policy-agent/opa/pull/7493)) authored by @charlieegan3 +- docs: Set versioned docs links to point to archive ([#7528](https://github.com/open-policy-agent/opa/pull/7528)) authored by @charlieegan3 +- docs: Update helm-kubernetes-quickstart bundle ([#7469](https://github.com/open-policy-agent/opa/pull/7469)) authored by @johanfylling +- docs: Update opa-docker-authz example to use ghcr and v0.10 release tag ([#7513](https://github.com/open-policy-agent/opa/pull/7513)) authored by @larhauga +- docs: Fix post merge badge ([#7532](https://github.com/open-policy-agent/opa/pull/7532)) authored by @sspaink +- docs: Improve request headers documentation in REST APIs ([#7524](https://github.com/open-policy-agent/opa/pull/7524)) authored by @ali-jalaal +- docs: Update edge links to use `/docs/edge/` path ([#7529](https://github.com/open-policy-agent/opa/pull/7529)) authored by @charlieegan3 +- ecosystem: Add NACP integration ([#7503](https://github.com/open-policy-agent/opa/pull/7503)) authored by @charlieegan3 +- ecosystem: Update traefik integration docs ([#7506](https://github.com/open-policy-agent/opa/pull/7506)) authored by @charlieegan3 +- ecosystem: Add Principled Evolution integration ([#7495](https://github.com/open-policy-agent/opa/pull/7495)) authored by @kmadan +- ecosystem: Add tavo to ecosystem integration ([#7511](https://github.com/open-policy-agent/opa/pull/7511)) authored by @percyding-tavo + +### Miscellaneous + +- Dependency updates; notably: + - build(deps): bump github.com/hypermodeinc/badger from v4.6.0 to v4.7.0 + - build(deps): bump github.com/spf13/viper from 1.18.2 to 1.20.1 + - build(deps): bump golang.org/x/net from 0.37.0 to 0.38.0 + - build(deps): bump google.golang.org/grpc from 1.71.0 to 1.71.1 + - build(deps): bump oras.land/oras-go/v2 from 2.3.1 to 2.5.0 + +## 1.3.0 + +This release contains a mix of features, bugfixes, and dependency updates. + +### New Buffer Option for Decision Logs ([#5724](https://github.com/open-policy-agent/opa/issues/5724)) + +A new, optional, buffering mechanism has been added to decision logging. +The default buffer is designed around making precise memory footprint guarantees, which can produce lock contention at high loads, negatively impacting query performance. +The new event-based buffer is designed to reduce lock contention and improve performance at high loads, but sacrifices the memory footprint guarantees of the default buffer. + +The new event-based buffer is enabled by setting the `decision_logs.reporting.buffer_type` [configuration option](https://www.openpolicyagent.org/docs/latest/configuration/#decision-logs) to `event`. + +For more details, see the decision log plugin [README](https://github.com/open-policy-agent/opa/blob/main/v1/plugins/logs/README.md). + +Reported by @mjungsbluth, authored by @sspaink + +### OpenTelemetry: HTTP Support and Expanded Batch Span Configuration ([#7412](https://github.com/open-policy-agent/opa/issues/7412)) + +Distributed tracing through OpenTelemetry has been extended to support HTTP collectors (enabled by setting the `distributed_tracing.type` configuration option to `http`). +Additionally, configuration has been expanded with fine-grained batch span processor [options](https://www.openpolicyagent.org/docs/latest/configuration/#distributed-tracing). + +Authored and reported by @sqyang94 + +### Runtime, Tooling, SDK + +- compile: Require multi-term entrypoint paths for optimized bundle building ([#7321](https://github.com/open-policy-agent/opa/issues/7321)) authored by @johanfylling reported by @nikpivkin +- fmt: Allow one liner rule grouping ([#6760](https://github.com/open-policy-agent/opa/issues/6760)) authored by @anderseknert +- fmt: Fix v0-compatible fmt with stdin ([#7409](https://github.com/open-policy-agent/opa/issues/7409)) authored and reported by @charlieegan3 +- ir: Fix nil pointer deref in Unmarshal() when handling IsSetStmt ([#7415](https://github.com/open-policy-agent/opa/issues/7415)) authored and reported by @KrisKennawayDD +- planner: Fix Wasm vs non-Wasm evaluation difference bug related to the overeager optimization of ref head rules ([#7439](https://github.com/open-policy-agent/opa/pull/7439)) authored by @srenatus +- sdk: Removing repeat args from sub-func call ([#7443](https://github.com/open-policy-agent/opa/pull/7443)) authored by @alingse +- tester: Including parameterized test cases in test report counter ([#7407](https://github.com/open-policy-agent/opa/issues/7407)) authored by @johanfylling +- tester: Only including failed sub-test cases in report summary when non-verbose ([#7426](https://github.com/open-policy-agent/opa/pull/7426)) authored by @johanfylling + +### Docs, Website, Ecosystem + +- docs: Add some notes about AI assisted patches ([#7436](https://github.com/open-policy-agent/opa/pull/7436)) authored by @charlieegan3 +- docs: Add query_parameters_to_set ([#7405](https://github.com/open-policy-agent/opa/pull/7405)) authored by @sedovmik +- docs: Delete reference to license key in Envoy tutorial ([#7466](https://github.com/open-policy-agent/opa/pull/7466)) authored by @joostholslag +- docs: Fix typo in Envoy tutorial ([#7464](https://github.com/open-policy-agent/opa/pull/7464)) authored by @joostholslag +- docs: Update slack inviter link ([#7450](https://github.com/open-policy-agent/opa/pull/7450)) authored by @charlieegan3 +- docs: Update terraform examples ([#7429](https://github.com/open-policy-agent/opa/pull/7429)) authored by @charlieegan3 +- docs: Simplify `kind` usage instruction in Envoy tutorial ([#7465](https://github.com/open-policy-agent/opa/pull/7465)) authored by @joostholslag + +### Miscellaneous + +- Enable unused-receiver linter (revive) ([#7448](https://github.com/open-policy-agent/opa/pull/7448)) authored by @anderseknert +- Dependency updates; notably: + - build(deps): bump github.com/containerd/containerd from 1.7.26 to 1.7.27 + - build(deps): bump github.com/dgraph-io/badger/v4 from 4.5.1 to 4.6.0 + - build(deps): bump github.com/opencontainers/image-spec from 1.1.0 to 1.1.1 + - build(deps): bump github.com/prometheus/client_golang 1.21.0 to 1.21.1 + - build(deps): bump golang.org/x/net from 0.35.0 to 0.37.0 + - build(deps): bump golang.org/x/time from 0.10.0 to 0.11.0 + - build(deps): bump google.golang.org/grpc from 1.70.0 to 1.71.0 + - build(deps): bump go.opentelemetry.io deps to 1.35.0/0.60.0 + +## 1.2.0 + +This release contains a mix of features, performance improvements, and bugfixes. + +### Parameterized Rego Tests ([#2176](https://github.com/open-policy-agent/opa/issues/2176)) + +Rego tests now support parameterization, allowing a single test rule to include multiple, hierarchical, named test cases. +This feature is useful for data-driven testing, where a single test rule can be used for multiple test cases with different inputs and expected outputs. + +```rego +package example_test + +test_concat[note] if { + some note, tc in { + "empty + empty": { + "a": [], + "b": [], + "exp": [], + }, + "empty + filled": { + "a": [], + "b": [1, 2], + "exp": [1, 2], + }, + "filled + filled": { + "a": [1, 2], + "b": [3, 4], + "exp": [1, 2, 3], # Faulty expectation, this test case will fail + }, + } + + act := array.concat(tc.a, tc.b) + act == tc.exp +} +``` + +```cmd +$ opa test example_test.rego +example_test.rego: +data.example_test.test_concat: FAIL (263.375µs) + empty + empty: PASS + empty + filled: PASS + filled + filled: FAIL +-------------------------------------------------------------------------------- +FAIL: 1/1 +``` + +See the [documentation](https://www.openpolicyagent.org/docs/latest/policy-testing/#parameterized-tests-and-data-driven-testing) for more information. + +Authored by @johanfylling, reported by @anderseknert + +### Performance Improvements + +- perf: Add ref.CopyNonGround ([#7350](https://github.com/open-policy-agent/opa/pull/7350)) authored by @anderseknert +- perf: `opa fmt` 3x faster formatting ([#7341](https://github.com/open-policy-agent/opa/pull/7341)) authored by @anderseknert +- perf: Cost of indexing greatly reduced ([#7370](https://github.com/open-policy-agent/opa/pull/7370)) authored by @anderseknert +- perf: Eval optimizations ([#7367](https://github.com/open-policy-agent/opa/pull/7367)) authored by @anderseknert +- perf: Intern annotation terms ([#7365](https://github.com/open-policy-agent/opa/pull/7365)) authored by @anderseknert +- perf: Slightly more efficient policy scanning ([#7368](https://github.com/open-policy-agent/opa/pull/7368)) authored by @anderseknert +- perf: Switch to a faster xxhash package ([7362](https://github.com/open-policy-agent/opa/pull/7362)) authored by @Juneezee +- perf: Use GetByValue to avoid boxing to interface{} ([#7372](https://github.com/open-policy-agent/opa/pull/7372)) authored by @anderseknert +- perf: Various small improvements ([#7357](https://github.com/open-policy-agent/opa/pull/7357)) authored by @anderseknert +- perf: Improve storage lookup performance ([#7336](https://github.com/open-policy-agent/opa/pull/7336)) authored by @anderseknert +- perf: optimize iteration ([#7327](https://github.com/open-policy-agent/opa/pull/7327)) authored by @anderseknert + +### Topdown and Rego + +- rego+topdown: Allow providing custom base cache ([#7329](https://github.com/open-policy-agent/opa/pull/7329)) authored by @anderseknert + +### Runtime, Tooling, SDK + +- ast: Add missing `BuildAnnotationSet` to `ast` v0 ([#7347](https://github.com/open-policy-agent/opa/issues/7347)) authored by @anderseknert +- ast: Eliminate allocation in Value.Find, and other improvements ([#7319](https://github.com/open-policy-agent/opa/pull/7319)) authored by @anderseknert +- ast: Use byte for RuleKind and DocKind ([#7332](https://github.com/open-policy-agent/opa/pull/7332)) authored by @anderseknert +- ast.InterfaceToValue: add test case for `[]byte` ([#7379](https://github.com/open-policy-agent/opa/pull/7379)) authored by @dennygursky +- ast: support []string and ast.Value in ast.InterfaceToValue ([#7306](https://github.com/open-policy-agent/opa/pull/7306)) authored by @regeda +- bundle: Fixing issue where `--v0-compatible` isn't respected for custom bundles ([#7338](https://github.com/open-policy-agent/opa/pull/7338)) authored by @johanfylling +- cmd: Handle failing tests in `opa test --bench` ([#7205](https://github.com/open-policy-agent/opa/issues/7205)) authored by @anderseknert +- cmd: Add decision ID to `opa exec` output ([#7373](https://github.com/open-policy-agent/opa/pull/7373)) authored by @anderseknert +- oracle: Make oracle public under v1/ast/oracle ([#7265](https://github.com/open-policy-agent/opa/issues/7265)) authored by @anderseknert +- oracle: Allow passing own compiler to oracle ([#7354](https://github.com/open-policy-agent/opa/pull/7354)) authored by @anderseknert +- plugins/discovery: Enable tracing for discovery plugin ([#7299](https://github.com/open-policy-agent/opa/pull/7299)) authored by @mjungsbluth +- plugins/rest: Do not attach authorization header in bearerAuthPlugin if response is a redirect ([#7308](https://github.com/open-policy-agent/opa/pull/7308)) authored by @carabasdaniel +- server+distributedtracing: Add Additional Resource Attributes for OpenTelemetry ([#7322](https://github.com/open-policy-agent/opa/issues/7322)) authored by @briankahoot reported by @briankahoot +- util: Add util.HasherMap ([#7363](https://github.com/open-policy-agent/opa/pull/7363)) authored by @anderseknert + +### Docs, Website, Ecosystem + +- docs: Add support link to README ([#7359](https://github.com/open-policy-agent/opa/pull/7359)) (authored by @anderseknert) +- docs: Update example bundle to be v1 compatible ([#7342](https://github.com/open-policy-agent/opa/pull/7342)) authored by @ashutosh-narkar +- docs: Add note about v1.0 addr behaviour ([#7360](https://github.com/open-policy-agent/opa/issues/7360)) authored by @charlieegan3 reported by @ali-jalaal +- docs: Update homepage examples to drop `v1 import` ([#7391](https://github.com/open-policy-agent/opa/pull/7391)) authored by @charlieegan3 +- docs: Updating `--v1-compatible` mentions outside the v1 upgrade guide and v0 compatibility docs ([#7337](https://github.com/open-policy-agent/opa/pull/7337)) authored by @johanfylling +- docs: Fixed invalid links to examples ([#7326](https://github.com/open-policy-agent/opa/pull/7326)) authored by @JonathanDeLaCruzEncora +- MAINTAINERS: Add Anders and Charlie as maintainers ([#7318](https://github.com/open-policy-agent/opa/pull/7318)) authored by @charlieegan3 + +### Miscellaneous + +- build+test: Add `make test-short` task (#7364) (authored by @anderseknert) +- build: Add gocritic linter ([#7377](https://github.com/open-policy-agent/opa/pull/7377)) authored by @anderseknert +- build: Add nilness linter from govet ([#7335](https://github.com/open-policy-agent/opa/pull/7335)) authored by @anderseknert +- build: Add perfsprint linter ([#7334](https://github.com/open-policy-agent/opa/pull/7334)) authored by @anderseknert +- ci: Tagging release binaries with build version ([#7395](https://github.com/open-policy-agent/opa/pull/7395), [#7397](https://github.com/open-policy-agent/opa/pull/7397), [#7400](https://github.com/open-policy-agent/opa/pull/7400)) authored by @johanfylling +- test: fix race in `TestIntraQueryCache_ClientError` and `TestInterQueryCache_ClientError` ([#7280](https://github.com/open-policy-agent/opa/pull/7280)) authored by @Juneezee +- misc: Use Go 1.22+ int ranges ([#7328](https://github.com/open-policy-agent/opa/pull/7328)) authored by @anderseknert +- Dependency updates; notably: + - build: bump go from 1.23.5 to 1.24.0 + - build(deps): bump github.com/agnivade/levenshtein from 1.2.0 to 1.2.1 + - build(deps): bump github.com/containerd/containerd from 1.7.25 to 1.7.26 + - build(deps): bump github.com/google/go-cmp from 0.6.0 to 0.7.0 + - build(deps): bump github.com/prometheus/client_golang + - build(deps): bump github.com/spf13/cobra from 1.8.1 to 1.9.1 + - build(deps): bump github.com/spf13/pflag from 1.0.5 to 1.0.6 + - build(deps): bump golang.org/x/net from 0.34.0 to 0.35.0 + - build(deps): bump golang.org/x/time from 0.9.0 to 0.10.0 + - build(deps): bump ossf/scorecard-action from 2.4.0 to 2.4.1 + - Bump golangci-lint from v1.60.1 to 1.64.5 + +## 1.1.0 + +This release contains a mix of features, performance improvements, and bugfixes. + +### Performance Improvements + +- ast: Remove jsonOptions from AST nodes and terms ([#7281](https://github.com/open-policy-agent/opa/pull/7281)) authored by @anderseknert +- ast+plugins: Optimize activation of bundles with no inter-bundle path overlap ([#7144](https://github.com/open-policy-agent/opa/issues/7144)) authored and reported by @sqyang94 +- bundle: Optimizing rego-version management in bundle activation ([#7296](https://github.com/open-policy-agent/opa/pull/7296)) authored by @johanfylling +- cmd: Don't generate JSON from result in `opa bench` ([#7291](https://github.com/open-policy-agent/opa/issues/7291)) authored by @anderseknert +- topdown: Adding configurable token cache to `io.jwt` token verification built-ins ([#7274](https://github.com/open-policy-agent/opa/pull/7274)) authored by @johanfylling +- topdown: Reduce allocations in hot path ([#7288](https://github.com/open-policy-agent/opa/pull/7288)) authored by @anderseknert +- perf: Improvements to terms and built-in functions ([#7284](https://github.com/open-policy-agent/opa/pull/7284)) authored by @anderseknert +- perf: add Regorus ACI benchmark tests ([#7298](https://github.com/open-policy-agent/opa/pull/7298)) authored by @anderseknert +- plugins: Don't use reflect.DeepEqual for errors ([#7238](https://github.com/open-policy-agent/opa/issues/7238)) authored by @anderseknert +- testing: replace reflect.DeepEqual where possible ([#7286](https://github.com/open-policy-agent/opa/pull/7286)) authored by @anderseknert + +### Topdown and Rego + +- topdown: Fix out of range error in `numbers.range` built-in ([#7269](https://github.com/open-policy-agent/opa/issues/7269)) authored by @anderseknert +- topdown+rego+server: Allow opt-in for evaluating non-det builtins in PE ([#6496](https://github.com/open-policy-agent/opa/issues/6496)) authored by @srenatus + +### Runtime, Tooling, SDK + +- bundle: Add info about the correct rego version to parse modules on the store ([#7278](https://github.com/open-policy-agent/opa/pull/7278)) co-authored by @ashutosh-narkar and @johanfylling +- bundle+plugins: Fixing issue where bundle plugin could panic on reconfiguration (SDK use) ([#7297](https://github.com/open-policy-agent/opa/issues/7297)) authored by @johanfylling reported by @carabasdaniel +- cmd: Fix printed representation of ref head rules in `opa repl` ([#7301](https://github.com/open-policy-agent/opa/issues/7301)) authored by @anderseknert reported by @tsandall +- cmd: Respect `--v0-compatible` for `opa eval` partial eval support modules ([#7251](https://github.com/open-policy-agent/opa/pull/7251)) authored by @johanfylling +- golangci: fix invalid `linter-settings` configuration name ([#7244](https://github.com/open-policy-agent/opa/pull/7244)) authored by @Juneezee +- plugins/logs: Add support for masking with array keys ([#6883](https://github.com/open-policy-agent/opa/issues/6883)) authored by @charlieegan3 +- tester: code nitpicks ([#7252](https://github.com/open-policy-agent/opa/pull/7252)) authored by @srenatus +- util: Add util.Keys and util.KeysSorted ([#7285](https://github.com/open-policy-agent/opa/pull/7285)) authored by @anderseknert + +### Docs, Website, Ecosystem + +- docs: Update docker compose file in HTTP API tutorial and use addr for binding ([#7264](https://github.com/open-policy-agent/opa/issues/7264)) authored and reported by @zanliffick +- docs: Make 'ancient' warnings closable ([#7253](https://github.com/open-policy-agent/opa/issues/7253)) authored by @srenatus reported by @konradzagozda +- docs: Redirect opa-1 to v0-upgrade ([#7259](https://github.com/open-policy-agent/opa/pull/7259)) authored by @charlieegan3 +- docs: Use preformatted strings in fmt help ([#7263](https://github.com/open-policy-agent/opa/pull/7263)) authored by @charlieegan3 +- docs: Fix typo in k8s primer ([#7242](https://github.com/open-policy-agent/opa/pull/7242)) authored by @vicentinileonardo +- docs: Formatting and wording fixes ([#7268](https://github.com/open-policy-agent/opa/pull/7268)) authored by @kamilturek +- docs: Update output document of Envoy plugin. ([#7241](https://github.com/open-policy-agent/opa/pull/7241)) authored by @regeda + +### Miscellaneous + +- ci(nightly): Remove vendor w/o modproxy check ([#7292](https://github.com/open-policy-agent/opa/pull/7292)) authored by @srenatus +- Dependency updates; notably: + - build(go): bump to 1.23.5 ([7279](https://github.com/open-policy-agent/opa/pull/7279)) authored by @srenatus + - build(deps): upgrade github.com/dgraph-io/badger to v4 (4.5.1) ([#7239](https://github.com/open-policy-agent/opa/pull/7239)) authored by @Juneezee + - build(deps): bump github.com/containerd/containerd from 1.7.24 to 1.7.25 + - build(deps): bump github.com/tchap/go-patricia/v2 from 2.3.1 to 2.3.2 + - build(deps): bump golang.org/x/net from 0.33.0 to 0.34.0 + - build(deps): bump golang.org/x/time from 0.8.0 to 0.9.0 + - build(deps): bump google.golang.org/grpc from 1.69.2 to 1.70.0 + - build(deps): bump go.opentelemetry.io deps to 1.34.0/0.59.0 + +## 1.0.1 + +This is a bug fix release addressing the following issues: + +- build(go): bump to 1.23.5 (authored by @srenatus). + Addressing `CVE-2024-45341` and `CVE-2024-45336` vulnerabilities in the Go runtime. +- bundle: Add info about the correct rego version to parse modules on the store, co-authored by @ashutosh-narkar and @johanfylling in [#7278](https://github.com/open-policy-agent/opa/pull/7278). + Fixing an issue where the rego-version for individual modules was lost during bundle deactivation (bundle lifecycle) if this version diverged from the active runtime rego-version. + This could cause reloading of v0 bundles to fail when OPA was not running with the `--v0-compatible` flag. + +## 1.0.0 + +> **_NOTES:_** +> +> * The minimum version of Go required to build the OPA module is **1.22** + +We are excited to announce **OPA 1.0**, a milestone release consolidating an improved developer experience for the future of Policy as Code. +The release makes new functionality designed to simplify policy writing and improve the language's consistency the default. + +### Changes to Rego in OPA 1.0 + +Below we highlight some key changes to the defaults in OPA 1.0: + +- Using `if` for all rule definitions and `contains` for multi-value rules is now mandatory, not just when using the `rego.v1` import. +- Other new keywords (`every`, `in`) are available without any imports. +- Previously requirements that were only run in "strict mode" (like `opa check --strict`) are now the default. Duplicate imports and imports which shadow each other are no longer allowed. +- OPA 1.0 comes with a range of backwards compatibility features to aid your migrations, please see the [v0 compatibility guide](https://www.openpolicyagent.org/docs/latest/v0-compatibility/) +if you must continue to support v0 Rego. + +Read more about the OPA 1.0 announcement on the [OPA blog](https://blog.openpolicyagent.org/). + +Following are other changes that are included in OPA 1.0. + +### Improvements to memory allocations + +PRs [#7172](https://github.com/open-policy-agent/opa/pull/7172), [#7190](https://github.com/open-policy-agent/opa/pull/7190), +[#7193](https://github.com/open-policy-agent/opa/pull/7193), [#7165](https://github.com/open-policy-agent/opa/pull/7165), +[#7168](https://github.com/open-policy-agent/opa/pull/7168), [#7191](https://github.com/open-policy-agent/opa/pull/7191) & +[#7222](https://github.com/open-policy-agent/opa/pull/7222) together improve the memory performance of OPA. Key strategies +include reusing pointers and optimizing array and object operations, minimizing intermediate object creation, and using `sync.Pool` +to manage memory-heavy operations. These changes cumulatively greatly reduced the number of allocations and improved +evaluation speed by 10-20%. Additional benchmarks highlighted significant memory and speed improvements in custom +function evaluation. + +Authored by @anderseknert. + +### Wrap http.RoundTripper for SDK users + +PR [#7180](https://github.com/open-policy-agent/opa/pull/7180) adds an `EvalHTTPRoundTrip` EvalOption and query-level `WithHTTPRoundTrip` option. +Both use a new function type which converts an `http.Transport` configured by topdown to an `http.RoundTripper`. +This supports use cases requiring the customization of the `http.send` built in behavior. + +Authored by @evankanderson. + +### Improvements to scientific notation parsing in `units.parse` + +PR [#7147](https://github.com/open-policy-agent/opa/pull/7147) extends the behaviour of `extractNumAndUnit` to support +scientific notation values. This means values such as `1e3KB` can now be handled by this function. + +Authored by @berdanA. + +### Support customized buckets `bundle_loading_duration_ns` metric + +PR [#7156](https://github.com/open-policy-agent/opa/pull/7156) extends OPA’s Prometheus configuration to allow the +setting of user defined buckets for metrics. This aids when debugging the loading of slow bundles. + +Authored by @jwu730-1. + +### Test suite performance improvements + +PR [#7126](https://github.com/open-policy-agent/opa/pull/7126) updates tests to improve performance. Topdown and `storage/disk/` +tests now run around 50% and 75% faster respectively. + +Authored by @philipaconrad. + +### OPA 1.0 Preparation + +- Update v1 capabilities by @johanfylling in [#7216](https://github.com/open-policy-agent/opa/pull/7216) +- v1 API by @johanfylling in [#7215](https://github.com/open-policy-agent/opa/pull/7215) +- Updating formatter to not drop `rego.v1` and `future.keywords` imports for v1 by @johanfylling in [#7224](https://github.com/open-policy-agent/opa/pull/7224) +- Update docs and server binding address per OPA 1.0 specs by @ashutosh-narkar & @charlieegan3 in [#7140](https://github.com/open-policy-agent/opa/pull/7140) +- Renaming `--rego-v1` cmd flag to `--v0-v1` by @johanfylling in [#7225](https://github.com/open-policy-agent/opa/pull/7225) + + +### Topdown and Rego + +- Provide a more useful error message when there are conflicting default rules by @tjons in [#7164](https://github.com/open-policy-agent/opa/pull/7164) +- Fix test flakes in `topdown/cache` by @evankanderson in [#7188](https://github.com/open-policy-agent/opa/pull/7188) +- Add description to all built-in function args and return values by @anderseknert in [#7153](https://github.com/open-policy-agent/opa/pull/7153) +- Built-in function `to_number` now rejects "Inf", "Infinity" and "NaN" values by @sikehish in [#7203](https://github.com/open-policy-agent/opa/pull/7203) +- Update eval_cancel_error logic to separate context canceled, timeout errors by @mchitten in [#7202](https://github.com/open-policy-agent/opa/pull/7202) + +### Runtime, Tooling, SDK + +- Respect runtime rego-version in RESTful policy API by @johanfylling in [#7183](https://github.com/open-policy-agent/opa/pull/7183) +- Debugger: allow YAML to be used as input by @anderseknert in [#7178](https://github.com/open-policy-agent/opa/pull/7178) +- `opa build`: provide an option to preserve print statements for the "wasm" target (#7194) by @me-viper in [#7195](https://github.com/open-policy-agent/opa/pull/7195) +- Fix improper formatter behavior when comprehension contains comment by @tjons in [#7169](https://github.com/open-policy-agent/opa/pull/7169) +- runtime: send version report less often when OPA long-running by @srenatus in [#7211](https://github.com/open-policy-agent/opa/pull/7211) +- `opa eval`: Return error if illegal arguments passed with `--unknowns` flag by @kd-labs in [#7149](https://github.com/open-policy-agent/opa/pull/7149) +- Enable direct error handling for bundle plugin trigger method by @torwunder in [#7143](https://github.com/open-policy-agent/opa/pull/7143) + +### Docs, Website, Ecosystem + +- Add VodafoneZiggo as adopters by @Parsifal-M in [#7154](https://github.com/open-policy-agent/opa/pull/7154) +- Add opa-java-wasm to docs by @andreaTP in [#7199](https://github.com/open-policy-agent/opa/pull/7199) + +### Dependency Updates + +- (build) golangci-lint: v1.59.1 -> v1.60.1 by @srenatus in [#7175](https://github.com/open-policy-agent/opa/pull/7175) +- github.com/containerd/containerd: v1.7.23 -> v1.7.24 +- github.com/fsnotify/fsnotify: v1.7.0 -> v1.8.0 +- golang.org/x/net: v0.30.0 -> v0.33.0 +- golang.org/x/time: v0.7.0 -> v0.8.0 +- google.golang.org/grpc: v1.67.1 -> v1.69.2 +- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp: v0.53.0 -> v0.58.0 +- go.opentelemetry.io/otel: v1.28.0 -> v1.33.0 +- go.opentelemetry.io/otel/exporters/otlp/otlptrace: v1.28.0 -> v1.33.0 +- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc: v1.28.0 -> v1.33.0 +- go.opentelemetry.io/otel/sdk: v1.28.0 -> v1.33.0 +- go.opentelemetry.io/otel/trace: v1.28.0 -> v1.33.0 + + +## 0.70.0 + +This release contains a mix of features, performance improvements, and bugfixes. + +### Optimized read mode for OPA's in-memory store ([#7125](https://github.com/open-policy-agent/opa/pull/7125)) + +A new optimized read mode has been added to the default in-memory store, where data written to the store is eagerly converted +to AST values (the data format used during evaluation). This removes the time spent converting raw data values to AST +during policy evaluation, thereby improving performance. + +The memory footprint of the store will increase, as processed AST values generally take up more space in memory than the +corresponding raw data values, but overall memory usage of OPA might remain more stable over time, as pre-converted data +is shared across evaluations and isn't recomputed for each evaluation, which can cause spikes in memory usage. + +This mode can be enabled for `opa run`, `opa eval`, and `opa bench` by setting the `--optimize-store-for-read-speed` flag. + +More information about this feature can be found [here](https://www.openpolicyagent.org/docs/v0.70.0/policy-performance/#storage-optimization). + +Co-authored by @johanfylling and @ashutosh-narkar. + +### Topdown and Rego +- topdown: Use new Inter-Query Value Cache for `json.match_schema` built-in function ([#7011](https://github.com/open-policy-agent/opa/issues/7011)) authored by @anderseknert reported by @lcarva +- ast: Fix location text attribute for multi-value rules with generated body ([#7128](https://github.com/open-policy-agent/opa/issues/7128)) authored by @anderseknert +- ast: Fix regression in `opa check` where a file that referenced non-provided schemas failed validation ([#7124](https://github.com/open-policy-agent/opa/pull/7124)) authored by @tjons +- test/cases/testdata: Fix bug in test by replacing unification by explicit equality check ([#7093](https://github.com/open-policy-agent/opa/pull/7093)) authored by @matajoh +- ast: Replace use of yaml.v2 library with yaml.v3. The earlier version would parse `yes`/`no` values as boolean. The usage of yaml.v2 in the parser was unintentional and now has been updated to yaml.v3 ([#7090](https://github.com/open-policy-agent/opa/issues/7090)) authored by @anderseknert + +### Runtime, Tooling, SDK +- cmd: Make `opa check` respect `--ignore` when `--bundle` flag is set ([#7136](https://github.com/open-policy-agent/opa/issues/7136)) authored by @anderseknert +- server/writer: Properly handle result encoding errors which earlier on failure would emit logs such as `superfluous call to WriteHeader()` while still returning `200` HTTP status code. Now, errors encoding the payload properly lead to `500` HTTP status code, without extra logs. Also use Header().Set() not Header().Add() to avoid duplicate content-type headers ([#7114](https://github.com/open-policy-agent/opa/pull/7114)) authored by @srenatus +- cmd: Support `file://` format for TLS key material file flags in `opa run` ([#7094](https://github.com/open-policy-agent/opa/pull/7094)) authored by @alexrohozneanu +- plugins/rest/azure: Support managed identity for App Service / Container Apps ([#7085](https://github.com/open-policy-agent/opa/issues/7085)) reported and authored by @apc-kamezaki +- debug: Fix step-over behaviour when exiting partial rules ([#7096](https://github.com/open-policy-agent/opa/pull/7096)) authored by @johanfylling +- util+plugins: Fix potential memory leaks with explicit timer cancellation ([#7089](https://github.com/open-policy-agent/opa/pull/7089)) authored by @philipaconrad + +### Docs, Website, Ecosystem +- docs: Fix OCI example with updated flag used by the ORAS CLI ([#7130](https://github.com/open-policy-agent/opa/pull/7130)) authored by @b3n3d17 +- docs: Delete Atom editor from supported editor integrations ([#7111](https://github.com/open-policy-agent/opa/pull/7111)) authored by @KaranbirSingh7 +- docs/website: Add Styra OPA ASP.NET Core SDK integration ([#7073](https://github.com/open-policy-agent/opa/pull/7073)) authored by @philipaconrad +- docs/website: Update compatibility information on the rego-cpp integration ([#7078](https://github.com/open-policy-agent/opa/pull/7078)) authored by @matajoh + +### Miscellaneous +- Dependency updates; notably: + - build(deps): bump github.com/containerd/containerd from 1.7.22 to 1.7.23 + - build(deps): bump github.com/prometheus/client_golang from 1.20.4 to 1.20.5 + - build(deps): bump golang.org/x/net from 0.29.0 to 0.30.0 + - build(deps): bump golang.org/x/time from 0.6.0 to 0.7.0 + - build(deps): bump google.golang.org/grpc from 1.67.0 to 1.67.1 + +## 0.69.0 + +This release contains a mix of features, bugfixes and necessary tooling and test changes required to support the upcoming OPA `1.0` release. + + +### Inter-Query Value Cache ([#6908](https://github.com/open-policy-agent/opa/issues/6908)) + +OPA now has a new inter-query value cache added to the SDK. It is intended to be used for values that are expensive to +compute and can be reused across multiple queries. The cache can be leveraged by built-in functions to store values +that otherwise aren't appropriate for the existing inter-query cache; for instance when the entry size isn't an +appropriate or primary limiting factor for cache eviction. + +The default size of the inter-query value cache is unbounded, but can be configured via the +`caching.inter_query_builtin_value_cache.max_num_entries` configuration field. OPA will drop random items from the cache +if this limit is exceeded. + +The cache is used by the `regex` and `glob` built-in functions, which previously had individual, non-configurable +caches with a max entry size of `100` each. + +Currently, the cache is only exercised when running OPA in server mode (ie. `opa run -s`). Also this feature is unsupported +for WASM. + +Authored by @ashutosh-narkar, reported by @amirsalarsafaei + +### Topdown and Rego + +- Future-proofing tests in the `ast`, `topdown`, `rego` etc. packages to be `1.0` compatible (authored by @johanfylling) +- ast: Attach annotation to static part of rule ref ([#7050](https://github.com/open-policy-agent/opa/issues/7050)) authored by @anderseknert +- ast: Make `Module.String()` include `if`/`contains` for v1 modules ([#6973](https://github.com/open-policy-agent/opa/issues/6973)) authored by @johanfylling reported by @nikpivkin +- topdown/http: Stop `http.send` latency timer when an error is encountered ([#7007](https://github.com/open-policy-agent/opa/pull/7007)) authored by @lukyer +- ast/compile: Refactor local variable replacement and replace declared variables in `with`'s target ([#6979](https://github.com/open-policy-agent/opa/issues/6979)) authored by @srenatus reported by @bluebrown +- ast: Update type checker to cache schema types ([#6970](https://github.com/open-policy-agent/opa/pull/6970)) authored by @nikpivkin +- test: Fix indentation in a YAML test case ([#7039](https://github.com/open-policy-agent/opa/pull/7039)) authored by @matajoh +- format: Bracketing keyword ref elements in formatter output ([#7010](https://github.com/open-policy-agent/opa/pull/7010)) authored by @johanfylling + +### Runtime, Tooling, SDK + +- Future-proofing tests in the `sdk`, `downlaod`, `server` , `cmd` etc. packages to be `1.0` compatible (authored by @johanfylling) +- cmd: Add `--v0-compatible` flag to make OPA behave as `v0.x` post `v1.0` release ([#7065](https://github.com/open-policy-agent/opa/pull/7065)) authored by @johanfylling +- util: Strip UTF-8 BOM from input JSON when found ([#6988](https://github.com/open-policy-agent/opa/issues/6988)) authored by @anderseknert reported by @adhilto +- plugins/rest: Support reading AWS token from the filesystem for the AWS container credential provider ([#6997](https://github.com/open-policy-agent/opa/pull/6997)) authored by @cmaddalozzo +- debug: Add `RegoOption` launch option to debugger for setting custom Rego options ([#7045](https://github.com/open-policy-agent/opa/issues/7045)) authored by @johanfylling +- debug: Always include `Input` and `Data` variable scopes to ease discoverability of the scopes ([#7074](https://github.com/open-policy-agent/opa/pull/7074)) authored by @johanfylling +- wasm: Fix arithmetic comparison for large numbers, caused by an integer overflow ([#6991](https://github.com/open-policy-agent/opa/issues/6991)) authored by @Ptroger + +### Docs, Website, Ecosystem + +- Add Marsh McLennan to adopters ([#7060](https://github.com/open-policy-agent/opa/issues/7060)) authored by @anderseknert reported by @pratimsc +- Add APIwiz to adopters ([#7067](https://github.com/open-policy-agent/opa/pull/7067)) authored by @anderseknert +- docs: Fix misnomer in OPA-Istio tutorial to document Istio's AuthorizationPolicy API ([#6984](https://github.com/open-policy-agent/opa/pull/6984)) authored by @tjons +- docs: Readme updates to highlight more up-to-date information about OPA ([#7066](https://github.com/open-policy-agent/opa/pull/7066)) authored by @charlieegan3 +- docs: Update documentation to show Debug API uses ([#7036](https://github.com/open-policy-agent/opa/pull/7036)) authored by @charlieegan3 +- docs: Simplify the OPA-Istio tutorial example policy ([#7059](https://github.com/open-policy-agent/opa/pull/7059)) authored by @anderseknert +- website: Update policy examples on the OPA home page to be `1.0` compatible ([#7033](https://github.com/open-policy-agent/opa/pull/7033)) authored by @charlieegan3 + +### Miscellaneous + +- build: Bump github.com/golang/glob, remove replace directive ([#7024](https://github.com/open-policy-agent/opa/issues/7024)) authored by @srenatus reported by @mmannerm +- Dependency updates; notably: + - build(deps): bump github.com/containerd/containerd from 1.7.21 to 1.7.22 + - build(deps): bump github.com/prometheus/client_golang from 1.20.2 to 1.20.4 + - build(deps): bump go.uber.org/automaxprocs from 1.5.3 to 1.6.0 + - build(deps): bump golang.org/x/net from 0.28.0 to 0.29.0 + - build(deps): bump google.golang.org/grpc from 1.66.0 to 1.67.0 + - build(go): bump 1.22.5 to 1.23.1 ([#7006](https://github.com/open-policy-agent/opa/pull/7006)) authored by @srenatus + +## 0.68.0 + +This release contains a mix of features and bugfixes. + +### Breaking Changes + +#### `entrypoint` annotation implies `document` scope ([#6798](https://github.com/open-policy-agent/opa/issues/6798)) + +The [entrypoint annotation's](https://www.openpolicyagent.org/docs/latest/policy-language/#entrypoint) scope requirement +has changed from `rule` to `document` ([https://github.com/open-policy-agent/opa/issues/6798](#6798)). +Furthermore, if no `scope` annotation is declared for a METADATA block preceding a rule, the presence of an `entrypoint` +annotation with a `true` value will assign the block a `document` scope, where the `rule` scope is otherwise the default. + +In practice, a rule entrypoint always point to the entire document and not a particular rule definition. The previous behavior was a bug, and one we've now addressed. + +Authored by @anderseknert + +### Topdown and Rego + +- ast: Fixing nil-pointer dereference in compiler for partial rule edge case ([#6930](https://github.com/open-policy-agent/opa/issues/6930)) authored by @johanfylling +- ast+parser: Add hint to future-proof imports ([6968](https://github.com/open-policy-agent/opa/pull/6968)) authored by @srenatus +- topdown: Adding unification scope to virtual-cache key. Fixing issue where false positive cache hits can occur when unification "restricts" the scope of ref-head rule evaluation ([#6926](https://github.com/open-policy-agent/opa/issues/6926)) authored by @johanfylling reported by @anderseknert +- topdown: Marshal JWT encode sign inputs as JSON ([#6934](https://github.com/open-policy-agent/opa/pull/6934)) authored by @charlieegan3 + +### Runtime, Tooling, SDK + +- ast: Make type checker `copy` method copy all values ([#6949](https://github.com/open-policy-agent/opa/pull/6949)) authored by @anderseknert +- ast: Include term locations in rule heads when requested ([#6860](https://github.com/open-policy-agent/opa/issues/6860)) authored by @anderseknert +- debug: Adding experimental debugger SDK ([#6876](https://github.com/open-policy-agent/opa/issues/6876)) authored by @johanfylling +- distributedtracing: allow OpenTelemetry resource attributes to be configured under distributed_tracing config ([#6942](https://github.com/open-policy-agent/opa/issues/6942)) authored and reported by @brettmc +- download: Fixing issue when saving OCI bundles on disk ([#6939](https://github.com/open-policy-agent/opa/issues/6939)) authored and reported by @Sergey-Kizimov +- logging: Always include HTTP request context in incoming req context ([#6951](https://github.com/open-policy-agent/opa/issues/6951)) authored by @ashutosh-narkar reported by @alvarogomez93 +- plugins/bundle: Avoid race-condition during bundle reconfiguration and activation ([#6849](https://github.com/open-policy-agent/opa/issues/6849)) authored by @ashutosh-narkar reported by @Pushpalanka +- plugins/bundle: Escape reserved chars used in persisted bundle directory name ([#6915](https://github.com/open-policy-agent/opa/issues/6915)) authored by @ashutosh-narkar reported by @alvarogomez93 +- plugins/rest: Support AWS_CONTAINER_CREDENTIALS_FULL_URI metadata endpoint ([#6893](https://github.com/open-policy-agent/opa/issues/6893)) authored and reported by @mbamber +- util+server: Fix bug around chunked request handling. ([#6904](https://github.com/open-policy-agent/opa/issues/6904)) authored by @philipaconrad reported by @David-Wobrock +- `opa exec`: This command never supported "pretty" formatting (`--format=pretty` or `-f pretty`), only `json`. Passing `pretty` is now invalid. ([#6923](https://github.com/open-policy-agent/opa/pull/6923)) authored by @srenatus + Note that the flag is now unnecessary, but it's kept so existing calls like `opa exec -fjson ...` remain valid. + +#### Security Fix: CVE-2024-8260 ([#6933](https://github.com/open-policy-agent/opa/pull/6933)) + +This release includes a fix where OPA would accept UNC locations on Windows. Reading those could leak NTLM hashes. +The attack vector would include an adversary tricking the user in passing an UNC path to OPA, e.g. `opa eval -d $FILE`. +UNC paths are now forbidden. If this is an issue for you, please reach out on Slack or GitHub issues. + +Reported by Shelly Raban +Authored by @ashutosh-narkar + +### Docs, Website, Ecosystem + +- docs: Suggest using `opa-config.yaml` as name for config file (#6966) ([#6959](https://github.com/open-policy-agent/opa/issues/6959)) authored by @anderseknert +- docs: Add documentation for OPA Spring Boot integration ([#6898](https://github.com/open-policy-agent/opa/pull/6898)) authored by @charlieegan3 +- docs: Update Istio tutorial ([#6896](https://github.com/open-policy-agent/opa/pull/6896)) authored by @Pindar +- docs: Update contrib docs ([#6974](https://github.com/open-policy-agent/opa/pull/6974)) authored by @charlieegan3 +- docs: Add Lula to the OPA ecosystem ([#6902](https://github.com/open-policy-agent/opa/pull/6902)) authored by @brandtkeller +- docs: Add github action policy testing automation ([#6954](https://github.com/open-policy-agent/opa/pull/6954)) authored by @oycyc +- docs: Mention `http.send` in inter-query cache config docs ([#6953](https://github.com/open-policy-agent/opa/pull/6953)) authored by @anderseknert +- docs+topdown: Fixing typos in built-in descriptions ([#6940](https://github.com/open-policy-agent/opa/pull/6940)) authored by @msorens + +### Miscellaneous + +- build: Make it possible to build only wasm testcases ([#6920](https://github.com/open-policy-agent/opa/pull/6920)) authored by @andreaTP +- Dependency updates; notably: + - build(deps): bump github.com/containerd/containerd from 1.7.20 to 1.7.21 + - build(deps): bump github.com/prometheus/client_golang from 1.19.1 to 1.20.2 + - build(deps): bump golang.org/x/net from 0.27.0 to 0.28.0 + - build(deps): bump golang.org/x/time from 0.5.0 to 0.6.0 + - build(deps): bump google.golang.org/grpc from 1.65.0 to 1.66.0 + +## 0.67.1 + +This is a bug fix release addressing the following issue: + +- util+server: Fix bug around chunked request handling ([#6906](https://github.com/open-policy-agent/opa/pull/6906)) authored by @philipaconrad, reported by @David-Wobrock. A request handling bug was introduced in ([#6868](https://github.com/open-policy-agent/opa/pull/6868)), which caused OPA to treat all incoming chunked requests as if they had zero-length request bodies. + +## 0.67.0 + +This release contains a mix of features, a new builtin function (`strings.count`), performance improvements, and bugfixes. + +### Breaking Change + +#### Request Body Size Limits + +OPA now automatically rejects very large requests ([#6868](https://github.com/open-policy-agent/opa/pull/6868)) authored by @philipaconrad. +Requests with a `Content-Length` larger than 128 MB uncompressed, and gzipped requests with payloads that decompress to +larger than 256 MB will be rejected, as part of hardening OPA against denial-of-service attacks. Previously, a large +enough request could cause an OPA instance to run out of memory in low-memory sidecar deployment scenarios, just from +attempting to read the request body into memory. + +These changes allow improvements in memory usage for the OPA HTTP server, and help OPA deployments avoid some accidental out-of-memory situations. + +For most users, no changes will be needed to continue using OPA. However, to control this behavior, two new configuration +keys are available: `server.decoding.max_length` and `server.decoding.gzip.max_length`. These control the max size in +bytes to allow for an incoming request payload, and the maximum size in bytes to allow for a decompressed gzip request payload, respectively. + +Here's an example OPA configuration using the new keys: + +```yaml +# Set max request size to 64 MB and max gzip size (decompressed) to be 128 MB. +server: + decoding: + max_length: 67108864 + gzip: + max_length: 134217728 +``` + +### Topdown and Rego + +- topdown: New `strings.count` builtin which returns the number of non-overlapping instances of a substring in a string ([#6827](https://github.com/open-policy-agent/opa/issues/6827)) authored by @Manish-Giri +- format: Produce error when `--rego-v1` formatted module has rule name conflicting with keyword ([#6833](https://github.com/open-policy-agent/opa/issues/6833)) authored by @johanfylling +- topdown: Add cap to caches for regex and glob built-in functions ([#6828](https://github.com/open-policy-agent/opa/issues/6828)) authored by @johanfylling. This fixes possible memory leaks where caches grow uncontrollably when large amounts of regexes or globs are generated or originate from the input document. + +### Runtime, Tooling, SDK +- repl: Add support for correctly loading bundle modules ([#6872](https://github.com/open-policy-agent/opa/issues/6872)) authored by @ashutosh-narkar +- plugins/discovery: Allow un-registration of discovery listener ([#6851](https://github.com/open-policy-agent/opa/pull/6851)) authored by @mjungsbluth. The discovery plugin allows OPA to register a bundle download status listener but previously did not offer a method to unregister that listener +- plugins/logs: Reduce amount of work performed inside global lock in decision log plugin ([#6859](https://github.com/open-policy-agent/opa/pull/6859)) authored by @johanfylling +- plugins/rest: Add a new client credential attribute to support Azure Workload Identity. This would allow workloads deployed on an Azure Kubernetes Services (AKS) cluster to authenticate and access Azure cloud resources ([#6802](https://github.com/open-policy-agent/opa/pull/6802)) authored by @ledbutter +- cmd/inspect: Add ability for opa inspect to inspect a single file outside of any bundle ([#6873](https://github.com/open-policy-agent/opa/pull/6873)) authored by @tjons +- cmd+bundle: Add `--follow-symlinks` flag to the `opa build` command to allow users to build directories with symlinked files, and have the contents of those symlinked files included in the built bundle ([#6800](https://github.com/open-policy-agent/opa/pull/6800)) authored by @tjons +- server: Add missing handling in the server for the `explain=fails` query value ([#6886](https://github.com/open-policy-agent/opa/pull/6886)) authored by @acamatcisco + +### Docs, Website, Ecosystem +- docs: Update bundle section with an example of a manifest with `rego_version` and `file_rego_versions` attributes ([#6885](https://github.com/open-policy-agent/opa/pull/6885)) authored by @ashutosh-narkar +- docs: Better link language SDKs to make them more discoverable ([#6866](https://github.com/open-policy-agent/opa/pull/6866)) authored by @charlieegan3 + +### Miscellaneous + +- ci: Add the OpenSSF Scorecard Github Action to help evaluate the OPA project's security posture ([#6848](https://github.com/open-policy-agent/opa/pull/6848)) authored by @harshitasao +- Dependency updates; notably: + - build(go): bump golang from 1.22.4 to 1.22.5 + - build(deps): bump github.com/containerd/containerd from 1.7.18 to 1.7.20 + - build(deps): bump golang.org/x/net from 0.26.0 to 0.27.0 + - build(deps): bump google.golang.org/grpc from 1.64.0 to 1.65.0 + - build(deps): bump go.opentelemetry.io modules ([#6847](https://github.com/open-policy-agent/opa/pull/6847)) + +## 0.66.0 + +This release contains a mix of features, performance improvements, and bugfixes. + +### Improved Test Reports ([2546](https://github.com/open-policy-agent/opa/issues/2546)) + +The `opa test` command now includes a new `--var-values` flag that enriches reporting of failed tests with the values and locations for variables in the failing expression. +E.g.: + +``` +FAILURES +-------------------------------------------------------------------------------- +data.test.test_my_policy: FAIL (0ms) + + test.rego:8: + x == y + z + | | | + | | 3 + | y + z: 5 + | y: 2 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +test.rego: +data.test.test_foo: FAIL (0ms) +-------------------------------------------------------------------------------- +FAIL: 1/1 +``` + +Authored by @johanfylling, reported by @grosser. + +### Reading stdin in `opa exec` ([#6538](https://github.com/open-policy-agent/opa/issues/6538)) + +The `opa exec` command now supports reading `input` documents from stdin with the `--stdin-input` (`-I`) flag. +E.g.: + +```shell +$ echo '{"user": "alice"}' | opa exec --stdin-input --bundle my_bundle +``` + +Authored by @colinjlacy, reported by @humbertoc-silva. + +### Topdown and Rego + +- ast: Fix blanket "unexpected assign token" error message / usability issue ([#6563](https://github.com/open-policy-agent/opa/issues/6563)) authored by @anderseknert +- ast: Fix wrong location on metadata parse errors on first line ([#6587](https://github.com/open-policy-agent/opa/issues/6587)) authored by @anderseknert +- ast: Fix/inspect unknowns in with stmt ([#6812](https://github.com/open-policy-agent/opa/issues/6812)) authored by @johanfylling reported by @surajupadhyay01 +- ast: Include original text in annotation location text attribute ([#6779](https://github.com/open-policy-agent/opa/issues/6779)) authored by @anderseknert +- ast: Expanding nested expressions in `every` domain ([#6790](https://github.com/open-policy-agent/opa/issues/6790)) authored by @johanfylling reported by @anakrish +- topdown: Add http.send request attribute to ignore headers for caching key ([#6642](https://github.com/open-policy-agent/opa/issues/6642)) authored and reported by @rudrakhp + +### Runtime, Tooling, SDK + +- build: Use chainguard images from dockerhub ([#6830](https://github.com/open-policy-agent/opa/pull/6830)) authored by @srenatus +- bundle: Preallocate buffers for file contents. ([#6818](https://github.com/open-policy-agent/opa/pull/6818)) authored by @philipaconrad +- plugins: Reduce locks during decision logging ([#6797](https://github.com/open-policy-agent/opa/pull/6797)) authored by @mjungsbluth +- plugins/rest: Do local map modification in OAuth2 client credentials flow ([#6769](https://github.com/open-policy-agent/opa/issues/6769)) authored and reported by @eubaranov +- loader: Use a better error message when trying to merge non-objects ([#6803](https://github.com/open-policy-agent/opa/issues/6803)) authored by @anderseknert +- server/authorizer: Fix gzip payload handling ([#6804](https://github.com/open-policy-agent/opa/issues/6804)) authored by @philipaconrad reported by @nevumx + +### Docs, Website, Ecosystem + +- docs: Remove missing prometheus metric `go_memstats_gc_cpu_fraction` ([#6783](https://github.com/open-policy-agent/opa/issues/6783)) authored by @philipaconrad +- docs: Mention that default functions may not evaluate ([#6265](https://github.com/open-policy-agent/opa/issues/6265)) authored by @anderseknert +- docs: Fix spelling and grammar of `an HTTP` ([#6786](https://github.com/open-policy-agent/opa/pull/6786)) authored by @jdbaldry +- docs/website: Add vs code and zed to ecosystem page ([#6788](https://github.com/open-policy-agent/opa/pull/6788)) authored by @charlieegan3 +- docs/website: Add Flipt to the OPA ecosystem ([#6781](https://github.com/open-policy-agent/opa/pull/6781)) authored by @markphelps +- docs/website: Add Flipt blog to their ecosystem page ([#6789](https://github.com/open-policy-agent/opa/pull/6789)) authored by @charlieegan3 +- docs/website: Revise language SDK content ([#6811](https://github.com/open-policy-agent/opa/pull/6811)) authored by @charlieegan3 + +### Miscellaneous + +- Dependency updates; notably: + - build(go): bump golang from 1.22.3 to 1.22.4 + - build(deps): bump github.com/containerd/containerd from 1.7.17 to 1.7.18 + - build(deps): bump golang.org/x/net from 0.25.0 to 0.26.0 + +## 0.65.0 + +This release contains a mix of features and bugfixes. + +### Runtime, Tooling, SDK + +- ast: Include annotations in rule AST, to help external tooling analyzing the AST ([#6771](https://github.com/open-policy-agent/opa/pull/6771)) authored by @ashutosh-narkar +- aws: Always read HTTP response body, to re-use persistent connections for non-200 responses ([#6734](https://github.com/open-policy-agent/opa/pull/6734)) authored by @johanneslarsson +- plugins/discovery: Update comparison logic for overrides ([#6723](https://github.com/open-policy-agent/opa/pull/6723)) authored by @ashutosh-narkar +- plugins/logs: Include http request context in decision logs ([#6693](https://github.com/open-policy-agent/opa/issues/6693)) authored by @ashutosh-narkar reported by @stiidk +- plugins/rest: Disable the Authorization header for ECR redirects ([6728](https://github.com/open-policy-agent/opa/pull/6728)) authored by @gdlg reported by @vazquezf2000 +- runtime: Fix OpenTelemetry graceful shutdown ([#6651](https://github.com/open-policy-agent/opa/issues/6651)) authored by @nicolaschotard and @David-Wobrock reported by @nicolaschotard + +### Topdown and Rego + +- topdown: Asserting the `every` domain is a collection type before evaluation ([#6762](https://github.com/open-policy-agent/opa/issues/6762)) authored by @johanfylling reported by @anderseknert + +### Miscellaneous + +- docs: Add arrays to composite values section ([#6727](https://github.com/open-policy-agent/opa/issues/6727)) authored by @anderseknert reported by @SpecLad +- docs: Add remainder operator to grammar ([#6767](https://github.com/open-policy-agent/opa/pull/6767)) authored by @anderseknert +- docs: Fix dynamic metadata object in docs ([#6709](https://github.com/open-policy-agent/opa/pull/6709)) authored by @antonioberben +- docs: Use best practice package name in test examples ([#6731](https://github.com/open-policy-agent/opa/pull/6731)) authored by @asleire +- docs: Update query API doc with details about overriding the def decision path ([#6745](https://github.com/open-policy-agent/opa/pull/6745)) authored by @ashutosh-narkar +- ci: pin GitHub Actions macos runner version and build for darwin/amd64 ([#6720](https://github.com/open-policy-agent/opa/issues/6720)) reported and authored by @suzuki-shunsuke +- Dependency updates; notably: + - build(go): bump golang from 1.22.2 to 1.22.3 + - build(deps): bump github.com/containerd/containerd from 1.7.15 to 1.7.17 + - build(deps): bump github.com/prometheus/client_golang + - build(deps): bump golang.org/x/net from 0.24.0 to 0.25.0 + - build(deps): bump google.golang.org/grpc from 1.63.2 to 1.64.0 + +### Breaking changes + +A new [IsSetStmt](https://www.openpolicyagent.org/docs/latest/ir/#issetstmt) statement has been added to the intermediate representation (IR). +This is a breaking change for custom IR evaluators, which must interpret this statement in IR plans generated by this OPA version and later. +No actions are required for Wasm users, as long as Wasm modules are built by this OPA version or later. + +## 0.64.1 + +This is a bug fix release addressing the following issues: + +- ci: Pin GitHub Actions macos runner version. The architecture of the GitHub Actions Runner `macos-latest` was changed from `amd64` to `arm64` and as a result `darwin/amd64` binary wasn't released ([#6720](https://github.com/open-policy-agent/opa/issues/6720)) authored by @suzuki-shunsuke +- plugins/discovery: Update comparison logic used in the discovery plugin for handling overrides. This fixes a panic that resulted from the comparison of uncomparable types ([#6723](https://github.com/open-policy-agent/opa/pull/6723)) authored by @ashutosh-narkar + +## 0.64.0 + +> **_NOTES:_** +> +> * The minimum version of Go required to build the OPA module is **1.21** + +This release contains a mix of features, a new builtin function (`json.marshal_with_options()`), performance improvements, and bugfixes. + +### Breaking Change + +#### Bootstrap configuration overrides Discovered configuration + +Previously if Discovery was enabled, other features like bundle downloading and status reporting could not be configured manually. +The reason for this was to prevent OPAs being deployed that could not be controlled through discovery. It's possible that +the system serving the discovered config is unaware of all options locally available in OPA. Hence, we relax the configuration +check when discovery is enabled so that the bootstrap configuration can contain plugin configurations. In case of conflicts, +the bootstrap configuration for plugins wins. These local configuration overrides from the bootstrap configuration are included +in the Status API messages so that management systems can get visibility into the local overrides. + +**In general, the bootstrap configuration overrides the discovered configuration.** Previously this was not the case for all +configuration fields. For example, if the discovered configuration changes the `labels` section, only labels that are +additional compared to the bootstrap configuration are used, all other changes are ignored. This implies labels in the +bootstrap configuration override those in the discovered configuration. But for fields such as `default_decision`, `default_authorization_decision`, +`nd_builtin_cache`, the discovered configuration would override the bootstrap configuration. Now the behavior is more consistent +for the entire configuration and helps to avoid accidental configuration errors. ([#5722](https://github.com/open-policy-agent/opa/issues/5722)) authored by @ashutosh-narkar + +### Add `rego_version` attribute to the bundle manifest + +A new global `rego_version` attribute is added to the bundle manifest, to inform the OPA runtime about what Rego version (`v0`/`v1`) to +use while parsing/compiling contained Rego files. There is also a new `file_rego_versions` attribute which allows individual +files to override the global Rego version specified by `rego_version`. + +When the version of the contained Rego is advertised by the bundle through this attribute, it is not required to run OPA with the +`--v1-compatible` (or future `--v0-compatible`) flag in order to correctly parse, compile and evaluate the bundle's modules. + +A bundle's `rego_version` attribute takes precedence over any applied `--v1-compatible`/`--v0-compatible` flag. ([#6578](https://github.com/open-policy-agent/opa/issues/6578)) authored by @johanfylling + +### Runtime, Tooling, SDK +- compile: Fix panic from CLI + metadata entrypoint overlaps. The panic occurs when `opa build` was provided an entrypoint from both a CLI flag, and via entrypoint metadata annotation. ([#6661](https://github.com/open-policy-agent/opa/issues/6661)) authored by @philipaconrad +- cmd/deps: Improve memory footprint and execution time of `deps` command for policies with high dependency connectivity ([#6685](https://github.com/open-policy-agent/opa/issues/6685)) authored by @johanfylling +- server: Keep default decision path in-sync with manager's config ([#6697](https://github.com/open-policy-agent/opa/issues/6697)) authored by @ashutosh-narkar +- server: Remove unnecessary AST-to-JSON conversions ([#6665](https://github.com/open-policy-agent/opa/pull/6665)) and ([#6669](https://github.com/open-policy-agent/opa/pull/6669)) authored by @koponen-styra +- sdk: Allow customizations of the plugin manager via SDK ([#6662](https://github.com/open-policy-agent/opa/issues/6662)) authored by @xico42 +- sdk: Fix issue where active parser options aren't propagated to module reload during bundle activation resulting in errors while activating bundles with `v1` syntax ([#6689](https://github.com/open-policy-agent/opa/pull/6689)) authored by @xico42 +- plugins/rest: Close response body in OAuth2 client credentials flow ([#6708](https://github.com/open-policy-agent/opa/pull/6708)) authored by @johanneslarsson + +### Topdown and Rego +- ast: Import `rego.v1` in `v0` support modules when applicable ([#6450](https://github.com/open-policy-agent/opa/issues/6450)) authored by @johanfylling +- rego: Set query Rego version from configured imports ([#6701](https://github.com/open-policy-agent/opa/issues/6701)) authored by @johanfylling +- topdown: New `json.marshal_with_options()` builtin for indented/"pretty-printed" and/or line-prefixed JSON ([#6630](https://github.com/open-policy-agent/opa/issues/6630)) authored by @sean-r-williams + +### Docs, Website, Ecosystem +- Add Raygun to ecosystem projects ([#6712](https://github.com/open-policy-agent/opa/pull/6712)) authored by @johndbro1 +- Add env0 to ecosystem projects ([#6658](https://github.com/open-policy-agent/opa/pull/6658)) authored by @yarivg +- Add Rego Language Comparisons to ecosystem projects ([#6663](https://github.com/open-policy-agent/opa/pull/6663)) authored by @charlieegan3 +- docs/configuration: Tidy up headers in Services section ([#6695](https://github.com/open-policy-agent/opa/pull/6695)) authored by @tsandall +- docs: Use cuboid rather than cube to explain concepts of sets and composite values in policy-language section of documentation ([#6691](https://github.com/open-policy-agent/opa/pull/6691)) authored by @kd-labs + +### Miscellaneous +- go.{mod,sum}: Update the `go` stanza of OPA's `go.mod` to `go 1.21`. OPA, used as Go dependency, requires at least `go 1.21`, and thus works with all officially supported Go versions (`1.21.x` and `1.22.x`) ([#6678](https://github.com/open-policy-agent/opa/pull/6678)) authored by @srenatus +- ci: Update Github Actions for Node 20. This change updates the `upload-artifact` and `download-artifact` Github actions to the latest version (v4) ([#6670](https://github.com/open-policy-agent/opa/pull/6670)) authored by @philipaconrad +- build: Update WASM Rego test generation docker command to address CVE-2022-24765 in Git ([#6703](https://github.com/open-policy-agent/opa/issues/6703)) authored by @ashutosh-narkar +- Dependency updates; notably: + - build(go): bump 1.22.1 -> 1.22.2 ([#6672](https://github.com/open-policy-agent/opa/pull/6672)) authored by @srenatus + - build(deps): bump aquasecurity/trivy-action from 0.18.0 to 0.19.0 + - build(deps): bump github.com/containerd/containerd from 1.7.14 to 1.7.15 + - build(deps): bump github.com/prometheus/client_model from 0.5.0 to 0.6.1 + - build(deps): bump golang.org/x/net from 0.22.0 to 0.24.0 + - build(deps): bump google.golang.org/grpc from 1.62.1 to 1.63.2 + + +## 0.63.0 + +This release contains a mix of features, performance improvements, and bugfixes. + +### Runtime, Tooling, SDK + +- cmd/exec: Add `--timeout` flag to `opa exec` to prevent infinite hangs. ([#6613](https://github.com/open-policy-agent/opa/issues/6613)) authored by @philipaconrad +- download: Surface bundle download errors via debug logging ([#6609](https://github.com/open-policy-agent/opa/issues/6609)) authored by @ashutosh-narkar reported by @nevumx +- topdown: Fixing overactive Early Exit suppression ([#6566](https://github.com/open-policy-agent/opa/issues/6566)) authored by @johanfylling reported by @ashwinhb +- plugins/rest: Add support to get temp creds via AssumeRole ([#6634](https://github.com/open-policy-agent/opa/pull/6634)) authored by @ashutosh-narkar + +### Topdown and Rego + +- topdown: Adding a new `crypto.x509.parse_and_verify_certificates_with_options` built-in function. ([#5882](https://github.com/open-policy-agent/opa/issues/5882)) authored by @yogisinha reported by @IxDay +- format: Preserve brackets around set union operation ([#6588](https://github.com/open-policy-agent/opa/issues/6588)) authored by @ashutosh-narkar reported by @HarshPathakhp +- aws: Support for Unsigned Payload or provided content sha256 in AWS signing ([#6581](https://github.com/open-policy-agent/opa/pull/6611)) authored by @prasanthj + +### Docs + Website + Ecosystem + +- ADOPTERS.md: Add Facets.cloud to the list ([#6640](https://github.com/open-policy-agent/opa/issues/6640)) authored by @ashutosh-narkar reported by @samarthya-gupta1 +- docs: Mention homebrew install option ([#6622](https://github.com/open-policy-agent/opa/issues/6622)) authored by @anderseknert +- docs: Add Rego v1 keywords to list of reserved names ([#6649](https://github.com/open-policy-agent/opa/pull/6649)) authored by @anderseknert +- docs: Add Tunnelmole as an open source tunneling option in the Cloudformation hooks documentation ([#6626](https://github.com/open-policy-agent/opa/pull/6626)) authored by @robbie-cahill +- docs: Add docs on using env vars in place of CLI flags ([#6631](https://github.com/open-policy-agent/opa/pull/6631)) authored by @anderseknert +- docs: Adding integration for Backstage ([#6629](https://github.com/open-policy-agent/opa/pull/6629)) authored by @Parsifal-M +- docs: Clear up some uses of future keywords ([#6653](https://github.com/open-policy-agent/opa/pull/6653)) authored by @charlieegan3 +- docs: Update delta bundle patch doc for remove op ([#6645](https://github.com/open-policy-agent/opa/pull/6645)) authored by @0marq +- docs: Fix typo in `Debugging OPA` ([#6637](https://github.com/open-policy-agent/opa/pull/6637)) authored by @setchy + +### Miscellaneous + +- chore: Remove repetitive words ([#6644](https://github.com/open-policy-agent/opa/pull/6644)) authored by @occupyhabit +- Dependency updates; notably: + - build(deps): bump github.com/containerd/containerd from 1.7.13 to 1.7.14 + - build(deps): bump github.com/golang/protobuf from 1.5.3 to 1.5.4 + - build(deps): bump google.golang.org/grpc from 1.62.0 to 1.62.1 + +## 0.62.1 + +This is a security fix release for the fixes published in [Golang 1.22.1](https://groups.google.com/g/golang-announce/c/5pwGVUPoMbg). + +OPA servers using `--authentication=tls` would be affected: crafted malicious client +certificates could cause a panic in the server. + +Also, crafted server certificates could panic OPA's HTTP clients, in bundle plugin, +status and decision logs; and `http.send` calls that verify TLS. + +This affects all crypto/tls clients, and servers that set Config.ClientAuth to +VerifyClientCertIfGiven or RequireAndVerifyClientCert. The default behavior is +for TLS servers to not verify client certificates. + +This is CVE-2024-24783 (https://pkg.go.dev/vuln/GO-2024-2598). + +Note that there are other security fixes in this Golang release, but whether or not +OPA is affected is harder to tell. An update is advised. + + +### Miscellaneous + +- Add Trino to OPA ecosystem (authored by @mosabua) +- update: ADOPTERS.md (#6608) (authored by @fredmaggiowski) + + +## 0.62.0 + +> **_NOTES:_** +> +> * The minimum version of Go required to build the OPA module is **1.20** + +This release contains a mix of improvements and bugfixes. + +### Runtime, Tooling, SDK + +- cmd: Add environment variable backups for command-line flags ([#6508](https://github.com/open-policy-agent/opa/pull/6508)) authored by @colinjlacy +- download/oci: Add missing `WithBundleParserOpts` method to OCI downloader ([#6571](https://github.com/open-policy-agent/opa/pull/6571)) authored by @slonka +- logging: avoid `%!F(MISSING)` in logs by skipping calls to the `{Debug,Info,Warn,Error}f` functions when there are no arguments ([#6555](https://github.com/open-policy-agent/opa/pull/6555)) authored by @srenatus + +### Topdown and Rego + +- ast+cmd: Allow bundle to contain calls to unknown Rego functions when inspected ([#6591](https://github.com/open-policy-agent/opa/issues/6591)) authored by @johanfylling +- topdown/http: Respect `raise_error` flag during input validation ([#6553](https://github.com/open-policy-agent/opa/pull/6553)) authored by @ashutosh-narkar + +### Docs + Website + Ecosystem + +- Add OpaDotNet to ecosystem projects ([#6554](https://github.com/open-policy-agent/opa/pull/6554)) authored by @me-viper +- Add updated logos for Permit.io and OPAL ([#6562](https://github.com/open-policy-agent/opa/pull/6562)) authored by @danielbass37 +- docs: Update description of the url path usage when accessing values inside object and array documents for v1/data GET and POST ([#6567](https://github.com/open-policy-agent/opa/pull/6567)) authored by @ashutosh-narkar +- docs: Use `application/yaml` instead of `application/x-yaml` as the former is now a recognized content type ([#6565](https://github.com/open-policy-agent/opa/pull/6565)) authored by @anderseknert + +### Miscellaneous +- Add Elastic to ADOPTERS.md ([#6568](https://github.com/open-policy-agent/opa/pull/6568)) authored by @orouz +- Dependency updates; notably: + - bump golang 1.21.5 -> 1.22 ([#6595](https://github.com/open-policy-agent/opa/pull/6595)) authored by @srenatus + - bump google.golang.org/grpc from 1.61.0 to 1.62.0 + - bump golang.org/x/net from 0.19.0 to 0.21.0 + - bump github.com/containerd/containerd from 1.7.12 to 1.7.13 + - bump aquasecurity/trivy-action from 0.16.1 to 0.17.0 + - bump github.com/prometheus/client_golang from 1.18.0 to 1.19.0 + - bump github.com/opencontainers/image-spec from 1.1.0-rc5 to 1.1.0-rc6 + + +## 0.61.0 + +This release contains a mix of new features and bugfixes. + +### Runtime, SDK + +- Adding `--v1-compatible` flag to all previously unsupported command line commands ([#6520](https://github.com/open-policy-agent/opa/issues/6520)) authored by @johanfylling +- Don't load files in tarball exceeding `size_limit_bytes` ([#6514](https://github.com/open-policy-agent/opa/issues/6514)) authored by @anderseknert reported by @dolevf +- Allow TLS cipher suites to be set for the OPA server ([#6537](https://github.com/open-policy-agent/opa/pull/6537)) authored by @ashutosh-narkar +- Removing deprecated fields and functions related to rego-v1 compatibility ([#6542](https://github.com/open-policy-agent/opa/pull/6542)) authored by @johanfylling +- bundle: Make func newDescriptor and withCloser public ([#6517](https://github.com/open-policy-agent/opa/pull/6517)) authored by @antgubarev +- runtime/logging: Do not panic when rctx is missing ([#6506](https://github.com/open-policy-agent/opa/pull/6506)) authored by @srenatus + +### Topdown + +- topdown: Clean expired `http.send` cache entries periodically ([#5320](https://github.com/open-policy-agent/opa/issues/5320)) authored by @rudrakhp reported by @lukyer + +### Docs + +- docs: Add documentation for new cache config parameters ([#6518](https://github.com/open-policy-agent/opa/pull/6518)) authored by @rudrakhp +- docs: Update docker-authorization.md to use new plugin version ([#6539](https://github.com/open-policy-agent/opa/pull/6539)) authored by @denis-accesa +- docs: Fix a typo in _index.md ([#6491](https://github.com/open-policy-agent/opa/pull/6491)) authored by @trungnguyen +- docs: Add a new debugging page ([#6513](https://github.com/open-policy-agent/opa/pull/6513)) authored by @charlieegan3 +- docs: Update log masking policy examples to be Rego v1 compatible ([#6545](https://github.com/open-policy-agent/opa/pull/6545)) authored by @ashutosh-narkar +- docs: Update version for non docs pages ([#6526](https://github.com/open-policy-agent/opa/pull/6526)) authored by @charlieegan3 +- Integrations, Ecosystem: + - docs: Add dependency-management-data logo ([#6543](https://github.com/open-policy-agent/opa/pull/6543)) authored by @jamietanna + - docs: Updated Rond links ([#6524](https://github.com/open-policy-agent/opa/pull/6524)) authored by @ugho16 + - docs: Correctly size integration logos ([#6544](https://github.com/open-policy-agent/opa/pull/6544)) authored by @charlieegan3 + - docs: Validate ecosystem keys ([#6522](https://github.com/open-policy-agent/opa/pull/6522)) authored by @charlieegan3 + +### Miscellaneous + +- linters+testdata: Reformat all yaml testcases for linting. ([#6511](https://github.com/open-policy-agent/opa/pull/6511)) authored by @philipaconrad +- Dependency updates, notably: + - bump github.com/containerd/containerd from 1.7.11 to 1.7.12 + - bump github.com/go-logr/logr from 1.3.0 to 1.4.1 + - bump github.com/google/uuid from 1.5.0 to 1.6.0 + - bump github.com/prometheus/client_golang from v1.16.0 to v1.18.0 + - bump google.golang.org/grpc from 1.60.1 to 1.61.0 + +## 0.60.0 + +### Runtime, Tooling, SDK +- OPA can be run in 1.0 compatibility mode by using the new `--v1-compatible` flag. When this mode is enabled, the current release of OPA will behave as OPA `v1.0` will eventually behave by default. This flag is currently supported on the `build`, `check`, `fmt`, `eval` and `test` commands ([#6478](https://github.com/open-policy-agent/opa/pull/6478)) authored by @johanfylling +- Extend the telemetry report to include the minimum compatible version of policies loaded into OPA ([#6361](https://github.com/open-policy-agent/opa/issues/6361)) co-authored by @srenatus and @ashutosh-narkar +- server: Support fsnotify based reloading of certificate, key and CA cert pool when they change on disk ([#5788](https://github.com/open-policy-agent/opa/issues/5788)) authored by @charlieegan3 +- Add option on the unit test runner to surface builtin errors. This should help with debugging errors generated while running unit tests ([#6489](https://github.com/open-policy-agent/opa/issues/6489)) authored by @jalseth +- Fix issue in `opa fmt` where the assignment operator and term in the rule head of chain rules are removed from the re-written rule head ([#6467](https://github.com/open-policy-agent/opa/issues/6467)) authored by @anderseknert +- cmd/fmt: Replace dependency on `diff` tool with an external golang library function ([#6284](https://github.com/open-policy-agent/opa/issues/6284)) authored by @colinjlacy + +### Topdown and Rego +- topdown/providers: Preserve user provided http headers in the `providers.aws.sign_req` builtin command ([#6456](https://github.com/open-policy-agent/opa/pull/6456)) authored by @c2zwdjnlcg +- rego: Allow custom builtin function registration to provide a description for the builtin ([#6449](https://github.com/open-policy-agent/opa/issues/6449)) authored by @lcarva +- ast+cmd: Allow bundle to contain calls to unknown functions when inspected ([#6457](https://github.com/open-policy-agent/opa/issues/6457)) authored by @johanfylling + +### Docs +- Add section on the changes proposed for a future OPA v1.0 and update Rego examples to be OPA v1.0 compliant([#6453](https://github.com/open-policy-agent/opa/issues/6453)) authored by @johanfylling +- Clarify behavior of the `sprintf` builtin command when used with the `%T` marker ([#6487](https://github.com/open-policy-agent/opa/issues/6487)) authored by @lcarva + +### Website + Ecosystem +- Ecosystem: Digger ([#6464](https://github.com/open-policy-agent/opa/pull/6464)) authored by @anderseknert + +### Miscellaneous +- Update `Makefile` to allow custom `GOFLAGS` to be provided to the golang executable ([#6458](https://github.com/open-policy-agent/opa/issues/6458)) authored by @cova-fe +- Dependency updates; notably: + - bump golang 1.21.4 -> 1.21.5 ([#6460](https://github.com/open-policy-agent/opa/pull/6460)) authored by @srenatus + - bump aquasecurity/trivy-action from 0.14.0 to 0.16.0 + - bump github.com/containerd/containerd from 1.7.9 to 1.7.11 + - bump google.golang.org/grpc from 1.59.0 to 1.60.1 + - bump github.com/google/uuid from 1.4.0 to 1.5.0 + +## 0.59.0 + +This release adds tooling to help prepare existing policies for the upcoming OPA 1.0 release. +It also contains a mix of improvements, bugfixes and security fixes for third-party libraries. + +> **_NOTES:_** +> +> * All published OPA images now run with a non-root uid/gid. The `uid:gid` is set to `1000:1000` for all images. As a result + there is no longer a need for the `-rootless` image variant and hence it will not be published as part of future releases. + This change is in line with container security best practices. OPA can still be run with root privileges by explicitly setting the user, + either with the `--user` argument for `docker run`, or by specifying the `securityContext` in the Kubernetes Pod specification. + +### Rego v1 + +The upcoming release of OPA 1.0, which will be released at a future date, will introduce breaking changes to the Rego language. Most notably: + +* the keywords that currently must be imported through `import future.keywords` into a module before use will be part of the Rego language by default, without the need to first import them. +* the `if` keyword will be required before the body of a rule. +* the `contains` keyword will be required when declaring a multi-value rule (partial set rule). +* deprecated built-in functions will be removed. + +This current release (`0.59.0`) introduces a new `--rego-v1` flag to the `opa fmt` and `opa check` commands to facilitate the transition of existing policies to be compatible with the 1.0 syntax. + +When used with `opa fmt`, the `--rego-v1` flag will format the module(s) according to the new Rego syntax in OPA 1.0. +Formatted modules are compatible with both the current version of OPA and 1.0. +Modules using deprecated built-ins will terminate formatting with an error. Future versions of OPA will support rewriting applicable function calls with equivalent Rego compatible with 1.0. + +When used with `opa check`, the `--rego-v1` flag will check that the modules are compatible with both the current version of OPA and 1.0. + +#### Relevant Changes + +- cmd: Adding `--rego-v1` flag to `check` cmd ([#6429](https://github.com/open-policy-agent/opa/issues/6429)) authored by @johanfylling +- cmd & format: Adding rego-v1 mode to `opa fmt` ([#6297](https://github.com/open-policy-agent/opa/issues/6297)) authored by @johanfylling +- ast: Adding capability feature for the `rego.v1` import (#6375) (authored by @johanfylling) +- ast: Skip if keyword requirement for default rule (`rego.v1`) ([#6356](https://github.com/open-policy-agent/opa/pull/6356)) authored by @ashutosh-narkar +- rego.v1: Fixing erroneous missing value assignment error ([#6364](https://github.com/open-policy-agent/opa/issues/6364)) authored by @johanfylling +- rego.v1: Improving support for rules with chained bodies ([#6370](https://github.com/open-policy-agent/opa/issues/6370)) authored by @johanfylling +- ast: Add `rego.v1` import ([#6247](https://github.com/open-policy-agent/opa/issues/6247)) introduced in OPA 0.58.0, authored by @johanfylling + +### Runtime, Tooling, SDK + +- ast: Adding `rule_head_refs` capabilities feature flag ([#6334](https://github.com/open-policy-agent/opa/issues/6334)) authored by @johanfylling +- build: Remove rootless image variant ([#4295](https://github.com/open-policy-agent/opa/issues/4295)) authored by @ashutosh-narkar +- discovery: Make status updates non blocking (#6345) ([#6343](https://github.com/open-policy-agent/opa/issues/6343)) authored by @charlieegan3 +- plugins/rest: Masks X-AMZ-SECURITY-TOKEN header in decision logs ([#5848](https://github.com/open-policy-agent/opa/issues/5848)) authored by @colinjlacy reported by @jwineinger +- wasm: Fix re2 bug ([#6376](https://github.com/open-policy-agent/opa/issues/6376)) authored by @srenatus reported by @sandhose +- ast: Add ExcludeLocationFile JSON marshalling option ([#6398](https://github.com/open-policy-agent/opa/pull/6398)) (authored by @anderseknert) +- cmd: Add options to the filter to only load rego files ([#6317](https://github.com/open-policy-agent/opa/issues/6317)) authored by @tjons +- ast: Add minimum compatible version computation to compiler ([#6348](https://github.com/open-policy-agent/opa/pull/6348)) authored by @tsandall +- internal/planner: Insert general ref head objects starting from the leaves, not root. ([#6401](https://github.com/open-policy-agent/opa/pull/6401)) authored by @srenatus +- internal/planner: Don't plan superfluous Equal/NotEqualStmts ([#6386](https://github.com/open-policy-agent/opa/pull/6386)) authored by @srenatus + +### Topdown and Rego + +- ast: Allowing packages to be declared within the dynamic extent of a rule ([#6387](https://github.com/open-policy-agent/opa/issues/6387)) authored by @johanfylling +- ast: Disallow root document shadowing in leading term of rule refs ([#6291](https://github.com/open-policy-agent/opa/issues/6291)) authored by @johanfylling +- topdown: Add a new builtin function `strings.render_template` to render templated strings ([#6371](https://github.com/open-policy-agent/opa/issues/6371)) authored by @RDVasavada +- topdown/crypto: Add URIStrings field to JSON certs ([#6416](https://github.com/open-policy-agent/opa/issues/6416)) authored by @charlieegan3 reported by @kenjenkins +- ast: change ident token string ([#6435](https://github.com/open-policy-agent/opa/pull/6435)) authored by @tsandall + +### Miscellaneous + +- chore: Fix IDE warnings and remove usage of several deprecated fields. ([#6397](https://github.com/open-policy-agent/opa/pull/6397)) authored by @willbeason +- chore: Disable verbose output in wasm-sdk-e2e-test ([#6434](https://github.com/open-policy-agent/opa/pull/6434)) authored by @tsandall +- deps: group otel deps ([#6407](https://github.com/open-policy-agent/opa/pull/6407/files)) authored by @srenatus +- test: add environment variable tests ([#6420](https://github.com/open-policy-agent/opa/pull/6420)) authored by @robhafner +- Docs & Website: + - docs: Add dependency-management-data to the Ecosystem ([#6436](https://github.com/open-policy-agent/opa/pull/6436)) authored by @jamietanna + - docs: Add docs for dynamic_metadata feature in opa-envoy-plugin ([#6389](https://github.com/open-policy-agent/opa/pull/6389)) authored by @tjons + - docs: Fixed XACML Policy in documentation (Comparing to Other Systems) to be XACML 3.0 compliant ([#6438](https://github.com/open-policy-agent/opa/pull/6438)) authored by @cdanger + - docs: Update docs on rego.v1 / OPA 1.0 ([#6365](https://github.com/open-policy-agent/opa/pull/6365)) authored by @anderseknert + - docs: Update spinnaker integration ([#6414](https://github.com/open-policy-agent/opa/pull/6414)) authored by @charlieegan3 + - docs: Add legitify to ecosystem ([#6369](https://github.com/open-policy-agent/opa/pull/6369)) authored by @charlieegan3 + - docs: add cheat sheet link ([#6362](https://github.com/open-policy-agent/opa/pull/6362)) authored by @charlieegan3 + - docs: add newstack blog to regal ([#6372](https://github.com/open-policy-agent/opa/pull/6372)) authored by @charlieegan3 + - docs: Disk storage broken link ([#6425](https://github.com/open-policy-agent/opa/pull/6425)) authored by @francoisauclair911 + - docs: Update istio envoy tutorial to use AuthorizationPolicy ([#6426](https://github.com/open-policy-agent/opa/pull/6426)) authored by @tjons +- Dependency updates; notably: + - golang from 1.21.3 to 1.21.4 + - OpenTelemetry (contrib) 1.21.0/0.46.1 + +## 0.58.0 + +> **_NOTES:_** +> +> * All published OPA images now run with a non-root uid/gid. The `uid:gid` is set to `1000:1000` for all images. As a result + there is no longer a need for the `-rootless` image variant and hence it will not be published as part of future releases. + This change is in line with container security best practices. OPA can still be run with root privileges by explicitly setting the user, + either with the `--user` argument for `docker run`, or by specifying the `securityContext` in the Kubernetes Pod specification. + +This release contains a mix of performance improvements, bugfixes and security fixes for third-party libraries. + +### Runtime, Tooling, SDK +- cmd/test: Display lines not covered if code coverage threshold not met in verbose reporting mode ([#2562](https://github.com/open-policy-agent/opa/issues/2562)) authored by @johanfylling +- cmd/test: Don't round up test coverage calculation as it could lead to inaccurate code coverage results ([#6307](https://github.com/open-policy-agent/opa/issues/6307)) authored by @anderseknert +- cmd/fmt: Don't format functions without a value to include `= true` as it is implied ([#6323](https://github.com/open-policy-agent/opa/pull/6323)) authored by @anderseknert +- server: Remove deprecated partial query parameter from REST API. This option has been deprecated since `v0.23.0` ([#2266](https://github.com/open-policy-agent/opa/issues/2266)) authored by @ashutosh-narkar +- Add support for configurable prometheus buckets for the `http_request_duration_seconds` metric ([#6238](https://github.com/open-policy-agent/opa/issues/6238)) authored by @AdrianArnautu +- plugins/bundle: Update bundle plugin state on a reconfigure operation when existing bundle is not modified ([#6311](https://github.com/open-policy-agent/opa/pull/6311)) authored by @asadk12 +- internal/pathwatcher: Fix how paths to watch by a fsnotify watcher are determined to avoid monitoring unintended directories and files ([#6277](https://github.com/open-policy-agent/opa/pull/6277)) authored by @ashutosh-narkar + +### Topdown and Rego +- topdown: Fix issue with build optimization producing support modules with forbidden characters in first var of rule ref ([#6338](https://github.com/open-policy-agent/opa/issues/6338)) authored by @johanfylling +- topdown: Fix panic in build optimization when policy contains rules with a general ref in the head ([#6339](https://github.com/open-policy-agent/opa/issues/6339)) authored by @johanfylling +- topdown: Avoid unnecessary conversion of small numbers by caching them and thereby helping to speed up some arithmetic operations ([#6021](https://github.com/open-policy-agent/opa/issues/6021)) authored by @ashutosh-narkar +- ast+rego: Disable compiler stages for IR-based eval paths ([#6335](https://github.com/open-policy-agent/opa/pull/6335)) authored by @srenatus +- built-in/walk: Skip path creation if path is assigned a wildcard to achieve faster `walk`-ing ([#6267](https://github.com/open-policy-agent/opa/pull/6267)) authored by @anderseknert +- ast: Add regression test for edge case where partial rule hides recursion cycle ([#6318](https://github.com/open-policy-agent/opa/pull/6318)) authored by @johanfylling + +### Docs +- Drop EXPERIMENTAL status of reported prom metrics ([#6298](https://github.com/open-policy-agent/opa/issues/6298)) authored by @ashutosh-narkar +- Update documentation on GCS bundles for case where the resource (the object in the GCS bucket) contains slashes (`/`) or other special characters ([#6264](https://github.com/open-policy-agent/opa/pull/6264)) authored by @dennisg +- Provide a more clear description of negation in the policy language section ([#6275](https://github.com/open-policy-agent/opa/pull/6275)) authored by @gusega + +### Website + Ecosystem +- Fix un-versioned built-in docs issue so that only the built-ins for a given doc version are displayed ([#6269](https://github.com/open-policy-agent/opa/issues/6269)) authored by @charlieegan3 + +### Miscellaneous +- ci: Remove `hub` tool in GitHub workflows in favor of [GitHub CLI](https://cli.github.com/) tool ([#6326](https://github.com/open-policy-agent/opa/issues/6326)) authored by @ashutosh-narkar +- Dependency updates; notably: + - bump go.opentelemetry.io modules ([#6292](https://github.com/open-policy-agent/opa/issues/6292)) authored by @cksidharthan + - aquasecurity/trivy-action from 0.12.0 to 0.13.0 + - github.com/containerd/containerd from 1.7.6 to 1.7.7 + - github.com/fsnotify/fsnotify from 1.6.0 to 1.7.0 + - golang.org/x/net from 0.15.0 to 0.17.0 + - google.golang.org/grpc from 1.58.2 to 1.59.0 (addresses vulnerability [GHSA-m425-mq94-257g](https://github.com/advisories/GHSA-m425-mq94-257g)) + - oras.land/oras-go/v2 from 2.3.0 to 2.3.1 + - sigs.k8s.io/yaml from 1.3.0 to 1.4.0 + +## 0.57.1 + +This is a bug fix release addressing the following security issues: + +### Golang security fix GO-2023-2102 + +> A malicious HTTP/2 client which rapidly creates requests and immediately resets them can cause excessive server resource consumption. + +### OpenTelemetry-Go Contrib security fix CVE-2023-45142 + +> Denial of service in otelhttp due to unbound cardinality metrics. + +## 0.57.0 + +This release contains an updated Rego syntax to allow general references in rule heads, and a mix of new features and bugfixes. + +### Support for General References in Rule Heads + +In OPA `0.56.0`, we introduced support for general references in rule heads as an experimental feature. +It has now graduated to a fully supported feature, and is no longer experimental. + +A general reference is a reference with variables at arbitrary locations. +In Rego, [partial rules](https://www.openpolicyagent.org/docs/latest/#partial-rules) are used for generating sets and objects. +In previous versions of OPA, variables were only allowed in the very last position in the rule's reference. +Now, Rego has been expanded to allow rules to be declared with general references in their head, with variables at arbitrary locations. +This allows for generating nested dynamic object structures: + +```rego +package example + +import future.keywords + +# Converting a flat list of users to a mapping by "role" and then "id". +users_by_role[role][id] := user if { + some user in data.users + id := user.id + role := user.role +} + +# Explicit "admin" key override to the above mapping. +users_by_role.admin[id] := user if { + some user in data.admins + id := user.id +} + +# Leaf entries can be multi-value. +users_by_country[country] contains user.id if { + some user in data.users + country := user.country +} +``` + +See the [documentation](https://www.openpolicyagent.org/docs/latest/policy-language/#variables-in-rule-head-references) for more information. + +Authored by @johanfylling. + +### Runtime, Tooling, SDK + +- ast/runtime: Extend type checking for authz policies ([#6213](https://github.com/open-policy-agent/opa/issues/6213)) authored by @ashutosh-narkar +- server: Add test case for bundle update - query API handler scenario ([#4792](https://github.com/open-policy-agent/opa/issues/4792)) authored by @ashutosh-narkar + +### Topdown and Rego + +- ast: Accept short-form else bodies ([#6157](https://github.com/open-policy-agent/opa/issues/6157)) authored by @Ronnie-personal +- plugins: Surface AWS authentication error details ([#6232](https://github.com/open-policy-agent/opa/issues/6232)) authored by @ashutosh-narkar +- topdown: Builtin function to parse uuid with google/uuid library ([#6173](https://github.com/open-policy-agent/opa/issues/6173)) authored by @Od1nB + +### Miscellaneous + +- ast: Add location to single entry rule head ref ([#6199](https://github.com/open-policy-agent/opa/issues/6199)) authored by @Ronnie-personal +- ast: Add option to marshal location text ([#6213](https://github.com/open-policy-agent/opa/issues/6213)) authored by @charlieegan3 +- types: New algorithm for (Any).Union + new benchmarks ([#6228](https://github.com/open-policy-agent/opa/pull/6228)) authored by @philipaconrad +- Updates to documentation and website authored by @charlieegan3 + - docs: Link to expressing or post (#6236) (authored by @charlieegan3) + - docs: Use links on support page (#6249) (authored by @charlieegan3) +- Dependency updates; notably: + - golang from 1.21 to 1.21.1 + - golang.org/x/net from 0.14.0 to 0.15.0 + - google.golang.org/grpc from 1.57.0 to 1.58.2 + - github.com/containerd/containerd from 1.7.4 to 1.7.6 + +## 0.56.0 + +This release contains a mix of new features, bugfixes and a new builtin function. + +### Support for General References in Rule Heads (Experimental) + +A new experimental feature in OPA is support for general refs in rule heads. Where a general ref is a reference with variables at arbitrary locations. + +```rego +package example + +import future.keywords + +# Converting a flat list of users to a mapping by "role" and then "id". +users_by_role[role][id] := user if { + some user in data.users + id := user.id + role := user.role +} + +# Explicit "admin" key override to the above mapping. +users_by_role.admin[id] := user if { + some user in data.admins + id := user.id +} + +# Leaf entries can be multi-value. +users_by_country[country] contains user.id if { + some user in data.users + country := user.country +} +``` + +General refs are currently not supported by the OPA planner, making this feature unsupported for Wasm and IR. + +Note: this feature is disabled by default, and needs to be enabled by setting the `EXPERIMENTAL_GENERAL_RULE_REFS` environment variable (once the feature is complete - supports Wasm and IR - this requirement will be dropped). + +Authored by @johanfylling. + +### New Built-In Function: `numbers.range_step` + +Similar to the `numbers.range` built-in function, `numbers.range_step` returns an array of numbers in a given range. The new built-in function also allows you to control the _step between each entry_. + +See [the documentation on the new built-in](https://www.openpolicyagent.org/docs/v0.56.0/policy-reference/#builtin-numbers-numbersrange_step) +for all the details. + +Authored by @sspaink. + +### New Ecosystem page on The Website + +The OPA Ecosystem of related integrations has been refreshed and moved to a more prominent location on [the website](https://www.openpolicyagent.org/ecosystem/). + +If you're interested to add any new integrations you've been working on, please see the [docs here](https://github.com/open-policy-agent/opa/tree/main/docs#opa-ecosystem) (updates to existing integrations are very welcome too!). + +### Runtime, Tooling, SDK + +- ast: Update strict error check message for unused args ([#6125](https://github.com/open-policy-agent/opa/pull/6125)) authored by @ashutosh-narkar +- ast: Remove unnecessary nil check ([#6155](https://github.com/open-policy-agent/opa/pull/6155)) authored by @Juneezee +- cmd: Make `opa test -z` fail with failing tests ([#6126](https://github.com/open-policy-agent/opa/issues/6126)) authored by @fdaguin +- cmd: Fix `opa test` `--ignore` when used together with `--bundle` ([#6185](https://github.com/open-policy-agent/opa/pull/6185)) authored by @joaobrandt +- cmd: Adding `--fail-non-empty` flag to `opa exec` ([#6153](https://github.com/open-policy-agent/opa/pull/6153)) authored by @Ronnie-personal +- download: Add `opa_no_oci` flag to build without containerd ([#6159](https://github.com/open-policy-agent/opa/pull/6159)) authored by @slonka +- download: Remove not required basedir for oci bundles & add test to verify signature verification ([#6145](https://github.com/open-policy-agent/opa/pull/6145)) authored by @gitu +- fmt: Trim trailing whitespace in comments ([#6161](https://github.com/open-policy-agent/opa/issues/6161)) authored by @anderseknert +- fmt: Remove dedup comment function in opa fmt ([#6165](https://github.com/open-policy-agent/opa/pull/6165)) authored by @anderseknert +- runtime: Always read .tar.gz file provided in argument as a bundle ([#5879](https://github.com/open-policy-agent/opa/issues/5879)) authored by @yogisinha +- server/authorizer: Inline readBody ([#6156](https://github.com/open-policy-agent/opa/pull/6156)) authored by @srenatus +- test: Bind test server to localhost interface ([#6162](https://github.com/open-policy-agent/opa/issues/6162)) authored by @anderseknert + +### Topdown and Rego + +- ast: Including "child" rules when fetching rules by ref ([#6182](https://github.com/open-policy-agent/opa/issues/6182)) authored by @johanfylling +- ast: Making partial object key rules contribute to dynamic portion of object type ([#6138](https://github.com/open-policy-agent/opa/issues/6138)) authored by @johanfylling +- rego: Expose PrepareOption, add BuiltinFuncs ([#6188](https://github.com/open-policy-agent/opa/pull/6188)) authored by @srenatus +- topdown: Support force cache even when server doesn't set the Date header ([#6175](https://github.com/open-policy-agent/opa/pull/6175)) authored by @c2zwdjnlcg +- topdown: Partial-eval for partial object/set ref head rules ([#6094](https://github.com/open-policy-agent/opa/issues/6094)) authored by @johanfylling + +### Miscellaneous + +- Updates to Documentation and Website (authored by: @anderseknert, @ashutosh-narkar, @atkrad, @charlieegan3, @hmoazzem, @johndbro1, @Pushkarm029, @srenatus and @testwill) +- Dependency updates; notably: + - golang: from 1.20.6 to 1.21 (authored by @ashutosh-narkar amd @srenatus) + - golang.org/x/net from 0.12.0 to 0.14.0 + - google.golang.org/grpc from 1.56.2 to 1.57.0 + - oras.land/oras-go/v2 from 2.2.1 to 2.3.0 + - Replace ghodss/yaml with sigs.k8s.io/yaml ([#6195](https://github.com/open-policy-agent/opa/pull/6195)) authored by @mrueg + +### Breaking changes + +Since its introduction in 0.34.0, the `--exit-zero-on-skipped` option always made the `opa test` command return an exit code 0. When used, it now returns the exit code 0 only if no failed tests were found. + +Test runs on existing projects using `--exit-zero-on-skipped` will fail if any failed tests were inhibited by this behavior. + +## 0.55.0 + +> **_NOTES:_** +> +> * All published OPA images now run with a non-root uid/gid. The `uid:gid` is set to `1000:1000` for all images. As a result +> there is no longer a need for the `-rootless` image variant and hence it will be not be published as part of future releases. +> This change is in line with container security best practices. OPA can still be run with root privileges by explicitly setting the user, +> either with the `--user` argument for `docker run`, or by specifying the `securityContext` in the Kubernetes Pod specification. +> +> * The minimum version of Go required to build the OPA module is **1.19** + +This release contains a mix of new features, bugfixes and a new builtin function. + +### Honor `default` keyword on functions + +Previously if a function was defined with a `default` value, OPA would ignore it. Now the `default` function is honored +if all functions with the same name are undefined. For example, + +```rego +package example + +default clamp_positive(x) := 0 + +clamp_positive(x) = x { + x > 0 +} +``` + +``` +$ opa eval -d example.rego 'data.example.clamp_positive(1)' -f pretty +1 +``` + +``` +$ opa eval -d example.rego 'data.example.clamp_positive(-1)' -f pretty +0 +``` + +The value of a `default` function follows the same conditions as that of a `default` rule. In addition, a `default` +function satisfies the following properties: + +- same arity as other functions with the same name +- arguments should only be plain variables ie. no composite values +- argument names should not be repeated + +> **_NOTE:_** +> +> `default` functions used to be previously ignored. If existing policies contain `default` functions, ensure that they conform +> to the properties mentioned above. Otherwise, those policies will fail to evaluate. + +Authored by @ashutosh-narkar. + +### New Built-In Function: crypto.parse_private_keys + +`crypto.parse_private_keys` returns zero or more private keys from the given encoded string containing DER certificate data. +If the input contains a list of one or more concatenated PEM blocks, then the built-in will output the parsed private keys +represented as objects. + +See [the documentation on the new built-in](https://www.openpolicyagent.org/docs/v0.55.0/policy-reference/#builtin-crypto-cryptoparse_private_keys) +for all the details. + +Authored by @volck. + +### Runtime, Tooling, SDK + +- plugins/rest: Add AWS KMS support for OAuth2 Client Credentials JWT authentication ([#5942](https://github.com/open-policy-agent/opa/pull/5942)) authored by @prasanthu +- sdk: Update input object to conform to the format expected by decision log masking ([#6090](https://github.com/open-policy-agent/opa/pull/6090)) authored by @epaulson10 +- sdk: Add option for specifying decision ID to SDK. Users can use this to control the ID that gets included in the decision logs ([#6101](https://github.com/open-policy-agent/opa/pull/6101)) authored by @brianchhun-chime +- cmd: Add `discard` output format to `opa eval` which discards the result while still showing the output of eval flags like `--profile` ([#6103](https://github.com/open-policy-agent/opa/pull/6103)) authored by @26tanishabanik +- Make rootless deprecation messages more explicit as all published OPA images now run with non-root uid/gid ([#6091](https://github.com/open-policy-agent/opa/pull/6091)) authored by @charlieegan3 +- download/oci: Add support for Docker Registry v2 authentication scheme ([#6045](https://github.com/open-policy-agent/opa/pull/6045)) authored by @gitu and @DerGut +- plugins/discovery: Ensure discovery plugin doesn't erase its own config on the plugin manager ([#6070](https://github.com/open-policy-agent/opa/pull/6070)) authored by @blacksails + +### Topdown and Rego + +- ast: Add `WithRoots` compiler option that allows callers to set the roots to include in the output bundle manifest ([#6088](https://github.com/open-policy-agent/opa/pull/6088)) authored by @kubaj +- rego: Parse store modules iff modules set on the Rego object. This change assumes that while using the Rego package, the compiler and store are kept in-sync, and thereby attempts to avoid a race during the compilation process ([#6081](https://github.com/open-policy-agent/opa/pull/6081)) authored by @ashutosh-narkar + +### Docs + +- docs/envoy: Update the standalone Envoy tutorial to use [kind](https://kind.sigs.k8s.io/), updated Envoy version etc. ([#6105](https://github.com/open-policy-agent/opa/pull/6105)) authored by @charlieegan3 + +### Website + Ecosystem + +- Ecosystem: + - Carbonetes BrainIAC ([#6073](https://github.com/open-policy-agent/opa/pull/6073)) authored by @jaysonsantos05 + +- Website: + - Reorganize relevant doc sections and OPA Ecosystem projects to have a closer integration between them ([#6064](https://github.com/open-policy-agent/opa/issues/6064)) authored by @charlieegan3 + +### Miscellaneous +- chore: Update comments on some exported functions and clean up instances where the same package was imported multiple times (authored by @testwill) +- Fix issue in the OPA release patch scripts related to `CRLF` line terminations in the patch output ([#6069](https://github.com/open-policy-agent/opa/pull/6069)) authored by @johanfylling +- Dependency bumps, notably: + - golang from 1.20.5 to 1.20.6 + - oras.land/oras-go/v2 from 2.2.0 to 2.2.1 + - google.golang.org/grpc from 1.56.1 to 1.56.2 + - github.com/containerd/containerd from 1.6.19 to 1.7.2 + - golang.org/x/net from 0.11.0 to 0.12.0 + - go.uber.org/automaxprocs from 1.5.2 to 1.5.3 + - go.opentelemetry.io/otel from v1.14.0 to v1.16.0 ([#6062](https://github.com/open-policy-agent/opa/pull/6062)) authored by @srenatus with feedback from @ghaskins and @zregvart + +## 0.54.0 + +This release focuses on bug fixes, but also includes some improvements to the SDK and commandline. + +Note: This will be the last OPA release to support building with Golang 1.18. (Golang 1.21 is expected to be released in August. Keeping the support for 1.18 is blocking OPA from upgrading OpenTelemetry.) + +### Topdown and Rego + +- Add unwrap functionality to topdown.Error ([#5890](https://github.com/open-policy-agent/opa/issues/5890)) authored by @ajith-sub reported by @ajith-sub +- Lazy obj performance ([#6009](https://github.com/open-policy-agent/opa/issues/6009)) authored by @johanfylling reported by @kubaj +- ast: Only realizing `lazyObj` when compared against other object type ([6060](https://github.com/open-policy-agent/opa/pull/6060)) (authored by @johanfylling) +- ast: Fixing issue in type-checker where partial objects couldn't have key overrides of divergent type ([#5972](https://github.com/open-policy-agent/opa/issues/5972)) authored by @johanfylling +- planner: CallDynamic regression fix ([#5964](https://github.com/open-policy-agent/opa/issues/5964)) authored by @srenatus +- fmt: Fix `fmt` panic in comprehension with comments ([#5798](https://github.com/open-policy-agent/opa/issues/5798)) authored by @Trolloldem reported by @Djoust +- topdown: Format integer numbers without exponent ([#6013](https://github.com/open-policy-agent/opa/issues/6013)) authored by @kenjenkins reported by @kenjenkins +- topdown: Fix panic in partial eval with ref head rule ([#6027](https://github.com/open-policy-agent/opa/issues/6027)) authored by @srenatus +- Fixed a bug in `object.union_n` where nested objects were mutated ([#5975](https://github.com/open-policy-agent/opa/issues/5975)) authored by @qshu-splunk +- Fixed the issue of the `object.subset` method failing to correctly compare array relationships ([5968](https://github.com/open-policy-agent/opa/issues/5968)) authored by @DCRUNNN +- topdown: Fixed caching race condition issue in `http.send` ([#5997](https://github.com/open-policy-agent/opa/pull/5997)) authored by @ashutosh-narkar +- Allow time formatting constants in rego `time.format` and `time.parse_ns` ([#5945](https://github.com/open-policy-agent/opa/issues/5945)) authored by @tjons + +### Runtime, Tooling, SDK + +- Add `--schema` flag to `opa test` ([#5923](https://github.com/open-policy-agent/opa/issues/5923)) authored by @renatosc +- Add ability to specify namespace for optimized files ([#5933](https://github.com/open-policy-agent/opa/issues/5933)) authored by @ashutosh-narkar reported by @deezkay +- Fix for the issue when OPA throws misleading error (storage_not_found_error) message while loading the delta bundle when persist property in config is true. ([#5959](https://github.com/open-policy-agent/opa/issues/5959)) authored by @yogisinha reported by @jnethery +- cmd: Update storage when a file remove op is detected ([#5986](https://github.com/open-policy-agent/opa/issues/5986)) authored by @boranx +- cmd: Add support for watch mode in opa test ([#1719](https://github.com/open-policy-agent/opa/issues/1719)) authored by @ashutosh-narkar reported by @Fox32 +- download: Pass request to docker.Authorizer ([#5902](https://github.com/open-policy-agent/opa/issues/5902)) authored by @DerGut reported by @carabasdaniel +- plugins/discovery: Fix discovery erasing `persistence_directory` config ([#6042](https://github.com/open-policy-agent/opa/pull/6042)) authored by @blacksails +- plugins/discovery: Fix persistence of discovery bundle ([#6048](https://github.com/open-policy-agent/opa/pull/6048)) (authored by @bdjgs) +- Add tracing to bundle/discovery download ([#5967](https://github.com/open-policy-agent/opa/issues/5967)) authored by @mjungsbluth +- Fallback on embedded timezone database if `tzdata` is not found on filesystem ([6038](https://github.com/open-policy-agent/opa/pull/6038)) authored by @charlieegan3 +- extensibility: Adding hooks (plugins, discovery, sdk) ([#6053](https://github.com/open-policy-agent/opa/pull/6053)) authored by @srenatus +- sdk: allow passing in a separate `Store` implementation in SDK ([5962](https://github.com/open-policy-agent/opa/pull/5962)) authored by @srenatus +- config: Show "extra", unknown fields in `/v1/config` API result ([6056](https://github.com/open-policy-agent/opa/pull/6056)) authored by @srenatus + +### Miscellaneous +- Disable provenance attestations in buildx ([#5877](https://github.com/open-policy-agent/opa/issues/5877)) authored by @ashutosh-narkar reported by @JasonMan34 +- build: configure SELinux labels for Docker volumes ([#6054](https://github.com/open-policy-agent/opa/issues/6054)) authored by @zregvart reported by @zregvart +- Dependency bumps, notably: + - golang from 1.20.4 to 1.20.5 + - github.com/prometheus/client_golang from from 1.15.1 to v1.16.0 + +## 0.53.1 + +This is a bug fix release addressing the following issues: + +### Runtime, Tooling, SDK +- plugins/logs: Previously while passing the decision log plugins's status to the Status API, the plugin held the mutex while a status upload was in process. This had the potential to block new decisions from being written to the plugin's buffer. To avoid this situation, a local copy of plugin's status is created ([#5966](https://github.com/open-policy-agent/opa/pull/5966)) authored by @ashutosh-narkar +- download: Public docker repositories require an authorization handshake where the client needs to respond to challenges marked by the `WWW-Authenticate` header of a `401 Unauthorized` response. Errors were returned when downloading a public image as it was assumed that authorization is not necessary for public repositories. This fix addresses this issue by challenging any `401 Unauthorized` responses by passing it to the docker.Authorizer ([#5902](https://github.com/open-policy-agent/opa/issues/5902)) authored by @DerGut +- `opa fmt`: Fix panic encountered while processing policies with comprehensions written on multiple lines with comments in these lines ([#5798](https://github.com/open-policy-agent/opa/issues/5798)) authored by @Trolloldem + +### Topdown and Rego +- built-in function `object.subset`: Fix an issue in `object.subset` related to incorrect results being generated when arrays are provided as an input ([#5968](https://github.com/open-policy-agent/opa/issues/5968)) authored by @DCRUNNN +- planner: Fix the optimization check for overlapping ref rules ([#5964](https://github.com/open-policy-agent/opa/issues/5964)) authored by @srenatus + +## 0.53.0 + +This release contains some enhancements, bugfixes, and a new builtin function. + +### Runtime, Tooling, SDK + +- status: Ensure Status plugin is correctly reconfigured to register or unregister Prometheus Collectors based on the state provided in OPA's active config ([#5918](https://github.com/open-policy-agent/opa/issues/5918)) authored by @johanfylling +- `opa eval`: Update OPA eval's `--profile-sort` flag description to highlight the valid options to sort the profile results ([#5924](https://github.com/open-policy-agent/opa/issues/5924)) authored by @ecbenezra +- `opa fmt`: Fix cases in which invalid code was generated due to parentheses being improperly handled ([#5537](https://github.com/open-policy-agent/opa/issues/5537)) authored by @Trolloldem +- rest: Allow users to configure the AWS STS domain when using Web Identity Credentials ([#5915](https://github.com/open-policy-agent/opa/issues/5915)) authored by @johanfylling +- status: Add an OPA environment information Gauge to Prometheus metrics to capture information like OPA version ([#5852](https://github.com/open-policy-agent/opa/issues/5852)) authored by @jmoghisi +- server: Add ability to configure Unix socket permissions if OPA is listening on a Unix socket ([#5888](https://github.com/open-policy-agent/opa/pull/5888)) authored by @ashutosh-narkar +- loader: Allow extensions to the `loader` package that provide ability to register handlers for certain file extensions. This feature is currently **EXPERIMENTAL** ([#5940](https://github.com/open-policy-agent/opa/pull/5940)) authored by @srenatus + +### Topdown and Rego + +- New built-in function `crypto.x509.parse_keypair`: Returns a key pair from a pair of PEM or base64 encoded strings of data. See [the documentation on the new built-in](https://www.openpolicyagent.org/docs/v0.53.0/policy-reference/#builtin-crypto-cryptox509parse_keypair) for all the details. ([#5853](https://github.com/open-policy-agent/opa/issues/5853)) authored by @volck. +- ast: Abort query evaluation if the compiler has errors. These errors will be exposed via the Status API if enabled ([#5947](https://github.com/open-policy-agent/opa/issues/5947)) authored by @johanfylling +- `io.jwt.decode_verify`: Fix issue where token verification succeeded in case where `iss` constraint was required but JWT did not contain it ([#5850](https://github.com/open-policy-agent/opa/issues/5850)) authored by @AleksanderBrzozowski +- wasm: Fix memory leaks in WASM when incrementally adding or removing data ([#5785](https://github.com/open-policy-agent/opa/issues/5785)) and ([#5901](https://github.com/open-policy-agent/opa/issues/5901)) authored by @ctelfer-sophos +- `http.send`: Add a new option to the `http.send` input object which allows policy authors to specify a retry count for executing a HTTP request. Retries are performed with an exponential backoff delay ([#5891](https://github.com/open-policy-agent/opa/pull/5891)) authored by @ashutosh-narkar +- ast: Fix issue with `_` matching only scalars in rule indexing for arrays ([#5916](https://github.com/open-policy-agent/opa/pull/5916)) authored by @jaspervdj +- rego: Allow for extending the Rego evaluation targets with plugins ([#5939](https://github.com/open-policy-agent/opa/pull/5939)) authored by @srenatus + +### Miscellaneous + +- Add PITS Global Data Recovery Services to ADOPTERS.md (authored by @pheianox) +- Avoid unnecessary byte/string conversion by using alternative functions/methods ([#5944](https://github.com/open-policy-agent/opa/pull/5944)) authored by @Juneezee +- False positive finding of [CVE-2022-3517](https://github.com/advisories/GHSA-f8q6-p94x-37v3) addressed by removing the dead code ([#5941](https://github.com/open-policy-agent/opa/pull/5941)) authored by @testwill +- Dependency bumps, notably: + - golang from 1.20.3 to 1.20.4 + - golang.org/x/net from 0.9.0 to 0.10.0 + - google.golang.org/grpc from 1.54.0 to 1.55.0 + - oras.land/oras-go/v2 from 2.0.2 to 2.2.0 + - github.com/prometheus/client_golang from 1.15.0 to 1.15.1 + +## 0.52.0 + +This release contains some enhancements, bugfixes, and a new builtin function. + +### Allow Adding Labels via Discovery + +Previously OPA did not allow any updates to the labels provided in the boot configuration via the discovered (ie. service) +config. This was done to avoid breaking the discovery configuration. But there are use cases where labels can serve as a convenient +way to pass information that could be used in policies, status updates or decision logs. This change allows +additional labels to be configured in the service config which are then made available during runtime. + +See [the Discovery documentation](https://www.openpolicyagent.org/docs/v0.52.0/management-discovery/#limitations) +for more details. + +Authored by @mjungsbluth. + +### New Built-In Function: crypto.hmac.equal + +`crypto.hmac.equal` provides a convenient way to compare hashes generated by the MD5, SHA-1, SHA-256 and SHA-512 hashing algorithms. + +Below is a real world example of how this built-in function can be utilized. Imagine our server is registered as a +GitHub webhook which subscribes to certain events on GitHub.com. Now we want to limit requests to those coming from GitHub. +One of the ways to do that is to first set up a secret token and validate the information. Once we create the token on GitHub, +we'll set up an environment variable that stores this token and makes it available to OPA via the `opa.runtime` built-in. +In the case of GitHub webhooks the validation is done by comparing the hash signature received in the `X-Hub-Signature-256` +header and calculating a hash using the secret token and payload body. The `check_signature` rule implements this logic. + +```rego +package example + +import input.attributes.request.http as http_request + +allow { + http_request.method == "POST" + input.parsed_path = ["workflows", "github", "webhooks"] + check_signature +} + +check_signature { + secret_key := opa.runtime().env.GITHUB_SECRET_KEY + hash_body := crypto.hmac.sha256(http_request.raw_body, secret_key) + expected_signature := concat("", ["sha256=", hash_body]) + header_signature = http_request.headers["X-Hub-Signature-256"] + crypto.hmac.equal(header_signature, expected_signature) +} +``` + +See [the documentation on the new built-in](https://www.openpolicyagent.org/docs/v0.52.0/policy-reference/#builtin-crypto-cryptohmacequal) +for all the details. + +Authored by @sandokandias. + +### Extend Authentication Methods Supported by OCI Downloader + +Previously the OCI Downloader had support for only three types of authentication methods, namely `Client TLS Certificates`, +`Basic Authentication` and `Bearer Token`. This change adds support for other authentication methods such as [AWS Signature](https://www.openpolicyagent.org/docs/v0.52.0/configuration/#aws-signature), +[GCP Metadata Token](https://www.openpolicyagent.org/docs/v0.52.0/configuration/#gcp-metadata-token). See [the documentation](https://www.openpolicyagent.org/docs/v0.52.0/configuration/#using-private-image-from-oci-repositories) +for more details. + +Authored by @DerGut. + +### Update Profiler Output With Number of Generated Expressions + +The number of EVAL/REDO counts in the profile result are sometimes difficult to understand. This is mainly due to the +fact that the compiler rewrites expressions and assigns the same location to each generated expression and the profiler +keys the counters by the location. To provide more clarity, the profile output now includes the number of generated +expressions for each given expression thereby helping to better understand the result and also how the evaluation works. + +Here is an example of the updated profiler output with the new `NUM GEN EXPR` column: + +```ruby ++----------+----------+----------+--------------+-------------+ +| TIME | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION | ++----------+----------+----------+--------------+-------------+ +| 20.291µs | 3 | 3 | 3 | test.rego:7 | +| 1µs | 1 | 1 | 1 | test.rego:6 | +| 2.333µs | 1 | 1 | 1 | test.rego:5 | +| 6.333µs | 1 | 1 | 1 | test.rego:4 | +| 84.75µs | 1 | 1 | 1 | data | ++----------+----------+----------+--------------+-------------+ +``` + +See [the Profiling documentation](https://www.openpolicyagent.org/docs/v0.52.0/policy-performance/#profiling) +for more details. + +Authored by @ashutosh-narkar. + +### Runtime, Tooling, SDK + +- bundle: Add ability to load bundles from an arbitrary filesystem ([#5833](https://github.com/open-policy-agent/opa/issues/5833)) authored by @kjothen +- server: Add a note to explicitly point out if OPA binds to the 0.0.0.0 interface on server initialization ([#5090](https://github.com/open-policy-agent/opa/issues/5090)) authored by @Parsifal-M +- Include trace and span identifier in decision logs to help with correlating logs and trace data ([#5230](https://github.com/open-policy-agent/opa/issues/5230)) authored by @ashutosh-narkar + +### Topdown and Rego + +- ast: Disallow partial object rules to have other partial object rule within their immediate extent ([#5855](https://github.com/open-policy-agent/opa/issues/5855)) authored by @johanfylling +- ast: Disallow multi-value rules to have other rules in their extent ([#5813](https://github.com/open-policy-agent/opa/issues/5813)) authored by @johanfylling +- ast: Set result of groundness check on indexer's AllRules func so that rule evaluation for complete rules is not skipped ([#5857](https://github.com/open-policy-agent/opa/issues/5857)) authored by @ashutosh-narkar +- rego: Fix duplicate text in error message during module parsing ([#5837](https://github.com/open-policy-agent/opa/pull/5837)) authored by @TzlilSwimmer123 +- planner: Fix bugs that have an impact on IR ([#5829](https://github.com/open-policy-agent/opa/pull/5829)) and Wasm usage ([#5839](https://github.com/open-policy-agent/opa/pull/5839)) authored by @srenatus +- ast: Include information about the location of rule value and reference in the AST's JSON representation based on the provided custom parsing options ([#5790](https://github.com/open-policy-agent/opa/issues/5790)) authored by @Trolloldem +- ast: Fix issue with unset annotation data when custom parsing options provided ([#5826](https://github.com/open-policy-agent/opa/issues/5826)) authored by @charlieegan3 + +### Docs + +- docs/rest-api: Update Compile API docs to include some use-cases ([#5858](https://github.com/open-policy-agent/opa/pull/5858)) authored by @charlieegan3 +- docs/extensions: Add Nondeterministic field to the Rego object initialization in the code example for the Custom Built-in Function section ([#5861](https://github.com/open-policy-agent/opa/pull/5861)) (authored by @RmStorm) + + +### Website + Ecosystem + +- Ecosystem: + - Reposaur ([#5854](https://github.com/open-policy-agent/opa/pull/5854)) authored by @charlieegan3 + - Update logo for Torque integration ([#5810](https://github.com/open-policy-agent/opa/pull/5810)) authored by @shirabendor-quali + +- Website: + - Reorganize the `MISCELLANEOUS` section to improve content navigation ([#4614](https://github.com/open-policy-agent/opa/issues/4614)) authored by @lakhanjindam + +### Miscellaneous + +- Dependency bumps, notably: + - golang from 1.20.2 to 1.20.3 + - golang.org/x/net from 0.8.0 to 0.9.0 + - github.com/prometheus/client_golang from 1.14.0 to 1.15.0 + + +## 0.51.0 + +This release contains improvements to monitoring and an assortment of fixes and improvements. + +### Monitoring + +#### Surface unauthorized request count from OPA HTTP API authz handler via Status API + +Currently when OPA's HTTP server rejects requests per +the [authz policy](https://www.openpolicyagent.org/docs/latest/security/#authentication-and-authorization), +this is not accounted for via the management APIs. +This change adds that count in the metric registry that is +part of the Status API for more visibility. + +([#3378](https://github.com/open-policy-agent/opa/issues/3378)) authored by @ashutosh-narkar. + +#### Surface more decision log errors via Status API + +Previously in [5732](https://github.com/open-policy-agent/opa/pull/5732), +we updated the decision log plugin to +surface errors via the Status API. However, in that change +certain events like encoder errors and log drops due to +buffer size limits had no metrics associated with them. +This change adds more metrics for these events so that they +can be surfaced via the Status API. + +([#5637](https://github.com/open-policy-agent/opa/issues/5637)) authored by @ashutosh-narkar. + +#### Include truncated HTTP response in logs + +This change updates the client debug log to include +the full HTTP response in case of non-200 status codes. +Recording the response in the logs can help to provide +more information to debug error scenarios. + +([#2961](https://github.com/open-policy-agent/opa/issues/2961)) authored by @ashutosh-narkar reported by @gshively11. + +### Topdown and Rego + +- Wasm: Add native support for `object.union_n` built-in function (authored by @Azanul) + +### Fixes + +- ast: Properly set the reported location of unused variables in strict-mode errors. ([#5662](https://github.com/open-policy-agent/opa/issues/5662)) authored by @boranx +- fmt: report wrong arity for built-in functions. ([#5646](https://github.com/open-policy-agent/opa/issues/5646)) authored by @Trolloldem +- topdown: http.send(): Ensuring intra-query caching consistency. ([#5736](https://github.com/open-policy-agent/opa/issues/5736)) authored by @johanfylling +- Performance improvements to decision logging. + Specifically, by removing superfluous json encoding roundtrip and double work in AST conversion of to-be-logged events. (authored by @srenatus) + +### Docs, Website, and Ecosystem + +- Fix typo in documentation (authored by @eternaltyro) +- Update TLS authentication docs (authored by @charlieegan3) +- Clarification in docs about checksums of Windows executables (authored by @Ronnie-personal) +- docs: Small fix to context placement in integration (authored by @craigpastro) +- docs/website: Fix floating navbar anchor issue ([5774](https://github.com/open-policy-agent/opa/issues/5774)) authored by @charlieegan3 reported by @kristiansvalland + +### Miscellaneous + +- Update -debug images to use Chainguard images ([5544](https://github.com/open-policy-agent/opa/issues/5544)) (authored by @charlieegan3) +- Various third-party dependencies were updated. + +## 0.50.2 + +This is a bug fix release that addresses a regression in 0.50.1. +This regression impacts policies with rules that, as its else-value, assign a comprehension containing variables. +Such rules would cause the compilation of the policy to fail with a `rego_unsafe_var_error` error. + +E.g. the following policy would fail to compile with a `policy.rego:5: rego_unsafe_var_error: var x is unsafe` error: +```rego +package example + +p { + false +} else := [x | x := 1] +``` + +### Fixes + +- ast: Fixing bug where comprehensions in rule else-heads weren't rewritten correctly ([#5771](https://github.com/open-policy-agent/opa/issues/5771)) authored by @johanfylling reported by @davidmdm + +## 0.50.1 + +This is a bug fix release addressing the following issues: + +### Fixes + +- ast/compile: Guard recursive module equality check. ([#5756](https://github.com/open-policy-agent/opa/issues/5756)) authored by @philipaconrad. + Resolves a performance regression when using large bundles. +- ast: Relaxing strict-mode check for unused args in else-branching functions ([#5758](https://github.com/open-policy-agent/opa/issues/5758)) authored by @johanfylling reported by @ethanjli. + +### Miscellaneous + +- Use normalized policy paths as compiler module keys and store IDs (authored by @ashutosh-narkar). + Resolves an issue with bundle loading on Windows. + +## 0.50.0 + +This release contains a mix of new features, bugfixes, security fixes, optimizations and build updates related to +OPA's published images. + +### New Built-in Functions: JSON Schema Verification and Validation + +These new built-in functions add functionality to verify and validate JSON Schema ([#5486](https://github.com/open-policy-agent/opa/pull/5486)) (co-authored by @jkulvich and @johanfylling). + +- `json.verify_schema`: Checks that the input is a valid JSON schema object +- `json.match_schema`: Checks that the document matches the JSON schema + +See the [documentation](https://www.openpolicyagent.org/docs/v0.50.0/policy-reference/#object) for all details. + +### Annotations scoped to `package` carries across modules + +`package` scoped schema annotations are now applied across modules instead of only local to the module where +it's declared ([#5251](https://github.com/open-policy-agent/opa/issues/5251)) (authored by @johanfylling). This change may cause compile-time errors and behavioural changes to +type checking when the `schemas` annotation is used, and to rules calling the `rego.metadata.chain()` built-in function: + + - Existing projects with the same package declared in multiple files will trigger a `rego_type_error: package annotation redeclared` +error _if_ two or more of these are annotated with the `package` scope. + - If using the `package` scope, the `schemas` annotation will be applied to type checking also for rules declared in +another file than the annotation declaration, as long as the package is the same. + - The chain of metadata returned by the `rego.metadata.chain()` built-in function will now contain an entry for the +package even if the annotations are declared in another file, if the scope is `package`. + +### Remote bundle URL shorthand for `run` command + +To load a remote bundle using `opa run`, the `set` directive can be provided multiple times as shown below: +``` + $ opa run -s --set "services.default.url=https://example.com" \ + --set "bundles.example.service=default" \ + --set "bundles.example.resource=/bundles/bundle.tar.gz" \ + --set "bundles.example.persist=true" +``` + +The following command can be used as a shorthand to easily start OPA with a remote bundle ([#5674](https://github.com/open-policy-agent/opa/issues/5674)) (authored by @anderseknert): +``` +$ opa run -s https://example.com/bundles/bundle.tar.gz +``` + +### Performance Improvements for `json.patch` Built-in Function + +Performance improvements in `json.patch` were achieved with the introduction of a new `EditTree` data structure, +which is built for applying in-place modifications to an `ast.Term`, and can render the final result of all edits efficiently +by applying all patches in a JSON-Patch sequence rapidly, and then collapsing all edits at the end with minimal wasted `ast.Term` copying (authored by @philipaconrad). +For more details and benchmarks refer [#5494](https://github.com/open-policy-agent/opa/pull/5494) and [#5390](https://github.com/open-policy-agent/opa/pull/5390). + +### Surface decision log errors via status API + +Errors encountered during decision log uploads will now be surfaced via the Status API in addition to being logged. This +functionality should give users greater visibility into any issues OPA may face while processing, uploading logs etc ([#5637](https://github.com/open-policy-agent/opa/issues/5637)) (authored by @ashutosh-narkar). + +See the [documentation](https://www.openpolicyagent.org/docs/v0.50.0/management-status/#status-service-api) for more details. + +### OPA Published Images Update + +All published OPA images now run with a non-root uid/gid. The `uid:gid` is set to `1000:1000` for all images. As a result +there is no longer a need for the `-rootless` image variant and hence it will be not be published as part of future releases. +This change is in line with container security best practices. OPA can still be run with root privileges by explicitly setting the user, +either with the `--user` argument for `docker run`, or by specifying the `securityContext` in the Kubernetes Pod specification. + + +### Runtime, Tooling, SDK + +- server: Support compression of response payloads if HTTP client supports it ([#5310](https://github.com/open-policy-agent/opa/issues/5310)) authored by @AdrianArnautu +- bundle: Ensure the bundle resulting from merging a set of bundles does not contain `nil` data ([#5703](https://github.com/open-policy-agent/opa/issues/5703)) authored by @anderseknert +- repl: Use lowercase for repl commands only and keep any provided arguments as-is ([#5229](https://github.com/open-policy-agent/opa/issues/5229)) authored by @Trolloldem +- metrics: New endpoint `/metrics/alloc_bytes` to show OPA's memory utilization ([#5715](https://github.com/open-policy-agent/opa/pull/5715)) authored by @anderseknert +- server: When using OPA TLS authorization, authz policy authors will now have access to the client certificates +presented as part of the TLS connection. This new data will be available under the key `client_certificates` ([#5538](https://github.com/open-policy-agent/opa/issues/5538)) authored by @charlieegan3 +- server: Use streaming implementation of json.Decode rather than using an intermediate buffer for the incoming request ([#5661](https://github.com/open-policy-agent/opa/pull/5661)) authored by @anderseknert + +### Topdown and Rego + +- ast: Extend compiler `strict` mode check to include unused arguments ([#5602](https://github.com/open-policy-agent/opa/issues/5602)) authored by @boranx. This change may cause +compile-time errors for policies that have unused arguments in the scope when the `strict` mode is enabled. These +variables could be replaced with `_` (wildcard) or get cleaned up if they are not intended to be used in the body of the functions. +- ast: Respect inlined `schemas` annotations even if `--schema` flag isn't used ([#5506](https://github.com/open-policy-agent/opa/issues/5506)) authored by @johanfylling +- ast: Force type-checker to respect `allow_net` capability when fetching remote schemas ([#5670](https://github.com/open-policy-agent/opa/issues/5670)) authored by @johanfylling +- ast/parse: Provide custom parsing options that allow location information of AST nodes to be included in their JSON +representation. This location information can be used by tools that work with the OPA AST ([#3143](https://github.com/open-policy-agent/opa/issues/3143)) authored by @charlieegan3 + +### Docs + +- docs/policy-reference: Fix typo in policy reference doc ([#5654](https://github.com/open-policy-agent/opa/pull/5654)) authored by @alvarogomez93 +- docs/extensions: Fix sample code provided in the custom built-in implementation example ([#5666](https://github.com/open-policy-agent/opa/pull/5666)) authored by @Ronnie-personal +- docs/bundles: Clarify delta bundle behavior when it contains an empty list of patch operations ([#5629](https://github.com/open-policy-agent/opa/issues/5629)) authored by @charlieegan3 +- docs/http-api-authz: Update the HTTP API authz tutorial with steps related to proper bundle creation ([#5682](https://github.com/open-policy-agent/opa/pull/5682)) authored by @lamoboos223 +- Fix broken 'future keywords' url link ([#5686](https://github.com/open-policy-agent/opa/pull/5686)) authored by @neelanjan00 + + +### Website + Ecosystem + +- Ecosystem: + - Styra Load ([#5659](https://github.com/open-policy-agent/opa/pull/5659)) authored by @charlieegan3 + +- Website: + - Update OPA documentation search to use Algolia v3 ([#5706](https://github.com/open-policy-agent/opa/pull/5706)) authored by @Parsifal-M + - Drop Google Universal Analytics (UA) code as part of Google Analytics 4 migration (authored by @chalin) + +### Miscellaneous + +- Dependency bumps, notably: + - golang from 1.20.1 to 1.20.2 + - github.com/containerd/containerd from 1.6.16 to 1.6.19 + - github.com/golang/protobuf from 1.5.2 to 1.5.3 + - golang.org/x/net from 0.5.0 to 0.8.0 + - google.golang.org/grpc from 1.52.3 to 1.53.0 + - OpenTelemetry-related dependencies (#5701) + + +## 0.49.2 + +This release migrates the [ORAS Go library](oras.land/oras-go/v2) from v1.2.2 to v2. +The earlier version of the library had a dependency on the [docker](github.com/docker/docker) +package. That version of the docker package had some reported vulnerabilities such as +CVE-2022-41716, CVE-2022-41720. The ORAS Go library v2 removes the dependency on the docker package. + +## 0.49.1 + +This is a bug fix release addressing the following Golang security issues: + +### Golang security fix CVE-2022-41723 + +> A maliciously crafted HTTP/2 stream could cause excessive CPU consumption in the HPACK decoder, sufficient to cause a +> denial of service from a small number of small requests. + +### Golang security fix CVE-2022-41724 + +> Large handshake records may cause panics in crypto/tls. Both clients and servers may send large TLS handshake records +> which cause servers and clients, respectively, to panic when attempting to construct responses. + +### Golang security fix CVE-2022-41722 + +> A path traversal vulnerability exists in filepath.Clean on Windows. On Windows, the filepath.Clean function could +> transform an invalid path such as "a/../c:/b" into the valid path "c:\b". This transformation of a relative +> (if invalid) path into an absolute path could enable a directory traversal attack. +> After fix, the filepath.Clean function transforms this path into the relative (but still invalid) path ".\c:\b". + +## 0.49.0 + +This release focuses on bugfixes and documentation improvements, as well as a few small performance improvements. + +### Runtime, Tooling, SDK + +- runtime: Update rule index's trie node scalar handling so that numerics compare correctly ([#5585](https://github.com/open-policy-agent/opa/issues/5585)) authored by @ashutosh-narkar reported by @alvarogomez93 +- ast: Improve error information when metadata yaml fails to compile ([#4475](https://github.com/open-policy-agent/opa/issues/4475)) authored and reported by @johanfylling +- bundle: Retain metadata annotations for Wasm entrypoints during inspection ([#5588](https://github.com/open-policy-agent/opa/issues/5588)) authored and reported by @johanfylling +- compile: Allow object generating rules to be annotated as entrypoints ([#5577](https://github.com/open-policy-agent/opa/issues/5577)) authored and reported by @johanfylling +- plugins/discovery: Support for persisting and loading discovery bundle from disk ([#2886](https://github.com/open-policy-agent/opa/issues/2886)) authored by @ashutosh-narkar reported by @anderseknert +- perf: Use `json.Encode` to avoid extra allocation (authored by @anderseknert) +- `opa inspect`: Fix prefix error when inspecting bundle from root ([#5503](https://github.com/open-policy-agent/opa/issues/5503)) authored by @harikannan512 reported by @HarshPathakhp +- topdown: `http.send` to cache responses based on status code ([#5617](https://github.com/open-policy-agent/opa/issues/5617)) authored by @ashutosh-narkar +- types: Add GoDoc about named types (authored by @wata727) +- deps: Remove `github.com/pkg/errors` dependency (authored by @Iceber) + + +### Docs + +- Update entrypoint documentation ([#5565](https://github.com/open-policy-agent/opa/issues/5565)) authored by @johanfylling reported by @robertgartman +- Add missing folder argument in bundle build example (authored by @charlieegan3) +- Clarify `crypto.x509.parse_certificates` docs (authored by @charlieegan3) +- Added AWS S3 Web Identity Credentials info to tutorial (authored by @vishrana) +- docs/graphql: non-nullable id argument and typo fix (authored by @philipaconrad) + +### Website + Ecosystem + +- Ecosystem: + - ccbr (authored by @niuzhi) + +- Website: + - Show prominent warning when viewing old docs (authored by @charlieegan3) + - Prevent navbar clipping on narrow screens + sticky nav (authored by @charlieegan3) + +### Miscellaneous + +Dependency bumps: +- build: bump golang 1.19.4 -> 1.19.5 (authored by @yanggangtony) +- ci: aquasecurity/trivy-action from 0.8.0 to 0.9.0 +- github.com/containerd/containerd from 1.6.15 to 1.6.16 +- google.golang.org/grpc from 1.51.0 to 1.52.3 + +## 0.48.0 + +This release rolls in security fixes from recent patch releases, along with +a number of bugfixes, and a new builtin function. + +### Improved error reporting available in `opa eval` + +A common frustration when writing policies in OPA is when an error happens, +causing a rule to unexpectedly return `undefined`. Using +`--strict-builtin-errors` would allow finding the first error encountered +during evaluation, but terminates execution immediately. + +To improve the debugging experience, it is now possible to display *all* of +the errors encountered during normal evaluation of a policy, via the new +`--show-builtin-errors` option. + +Consider the following error-filled policy, `multi-error.rego`: + +```rego +package play + +this_errors(number) := result { + result := number / 0 +} + +this_errors_too(number) := result { + result := number / 0 +} + +res1 := this_errors(1) + +res2 := this_errors_too(1) +``` + +Using `--strict-builtin-errors`, we would only see the first divide by zero +error: + + opa eval --strict-builtin-errors -d multi-error.rego data.play + +``` +1 error occurred: multi-error.rego:4: eval_builtin_error: div: divide by zero +``` + +Using `--show-builtin-errors` shows both divide by zero issues though: + + opa eval --show-builtin-errors -d multi-error.rego data.play -f pretty + +``` +2 errors occurred: +multi-error.rego:4: eval_builtin_error: div: divide by zero +multi-error.rego:8: eval_builtin_error: div: divide by zero +``` + +By showing more errors up front, we hope this will improve the overall +policy writing experience. + +### New Built-in Function: `time.format` + +It is now possible to format a time value from nanoseconds to a formatted +timestamp string via a built-in function. The builtin accepts 3 argument +formats, each allowing for different options: + + 1. A number representing the nanoseconds since the epoch (UTC). + 2. A two-element array of the nanoseconds, and a timezone string. + 3. A three-element array of nanoseconds, timezone string, and a layout + string (same format as for `time.parse_ns`). + +See [the documentation](https://www.openpolicyagent.org/docs/v0.48.0/policy-reference/#builtin-time-timeformat) +for all details. + +Implemented by @burnerlee. + +### Optimization in rule indexing + +Previously, every time the evaluator looked up a rule in the index, OPA +performed checks for grounded refs over the entire index *before* looking +up the rule. + +Now, OPA performs all groundedness checks once at index construction time, +which keeps index lookup times much more consistent as the number of +indexed rules scales up. + +Policies with large numbers of index-ready rules can expect a small +performance lift, proportional to the number of indexed rules. + +### Bundle fetching with AWS Signing Version 4A + +AWS has recently developed an extension to SigV4 called Signature Version +4A (SigV4A) which enables signatures that are valid in more than one AWS +Region. This new signature method is required for signing multi-region API +requests, such as Amazon S3 Multi-Region Access Points (MRAP). + +OPA now supports this new request signing method for bundle fetching, which +means that you can use an S3 MRAP as a bundle source. This is configured +via the new `services[].credentials.s3_signing.signature_version` +field. + +See the [the documentation](https://www.openpolicyagent.org/docs/v0.48.0/configuration/#aws-signature) +for more details. + +Implemented by @jwineinger + +### Runtime + +- rego: Check store modules before skipping parsing (authored by @charlieegan3) +- topdown/rego: Add BuiltinErrorList support to rego package, add to eval command (authored by @charlieegan3) +- topdown: Fix evaluator's re-wrapping of `NDBCache` errors (authored by @srenatus) +- Fix potential memory leak from `http.send` in interquery cache (authored by @asleire) +- ast/parser: Detect function rule head + `contains` keyword ([#5525](https://github.com/open-policy-agent/opa/issues/5525)) authored and reported by @philipaconrad +- ast/visit: Add `SomeDecl` to visitor walks ([#5480](https://github.com/open-policy-agent/opa/issues/5480)) authored by @srenatus +- ast/visit: Include `LazyObject` in visitor walks ([#5479](https://github.com/open-policy-agent/opa/issues/5479)) authored by @srenatus reported by @benweint + +### Tooling, SDK + +- topdown: cache undefined rule evaluations ([#593](https://github.com/open-policy-agent/opa/issues/593)) authored by @edpaget reported by @tsdandall +- topdown: Specify host verification policy for http redirects ([#5388](https://github.com/open-policy-agent/opa/issues/5388)) authored and reported by @ashutosh-narkar +- providers/aws: Refactor + Fix 2x Authorization header append issue ([#5472](https://github.com/open-policy-agent/opa/issues/5472)) authored by @philipaconrad reported by @Hiieu +- Add support to enable ND builtin cache via discovery ([#5457](https://github.com/open-policy-agent/opa/issues/5457)) authored by @ashutosh-narkar reported by @asadali +- format: Only use ref heads for all rule heads if necessary ([#5449](https://github.com/open-policy-agent/opa/issues/5449)) authored and reported by @srenatus +- `opa inspect`: Fix path of data namespaces on windows (authored by @shm12) +- ast+cmd: Only enforcing `schemas` annotations if `--schema` flag is used (authored by @johanfylling) +- sdk: Allow use of a query tracer (authored by @charlieegan3) +- sdk: Allow use of metrics, profilers, and instrumentation (authored by @charlieegan3) +- sdk: Return provenance information in Result types (authored by @charlieegan3) +- sdk: Allow use of StrictBuiltinErrors (authored by @charlieegan3) +- Allow print calls in IR (authored by @anderseknert) +- tester/runner: Fix panic'ing case in utility function ([#5496](https://github.com/open-policy-agent/opa/issues/5496)) authored and reported by @philipaconrad + +### Docs + +- Community page updates (authored by @anderseknert) +- Update Hugo version, update deprecated Page fields (authored by @charlieegan3) +- docs: Update TLS-based Authentication Example ([#5521](https://github.com/open-policy-agent/opa/issues/5521)) authored by @charlieegan3 reported by @jjthom87 +- docs: Update opa eval flags to link to bundle docs (authored by @charlieegan3) +- docs: Make SDK first option for Go integraton (authored by @anderseknert) +- docs: Fix typo on Policy Language page. (authored by @mcdonagj) +- docs/integrations: Update kubescape repo links (authored by @dwertent) +- docs/oci: Corrected config section (authored by @ogazitt) +- website/frontpage: Update Learn More links (authored by @pauly4it) + +- integrations.yaml: Ensure inventors listed in organizations (authored by @anderseknert) +- integrations: Fix malformed inventors item (authored by @anderseknert) +- Add Digraph to ADOPTERS.md (authored by @jamesphlewis) + +### Miscellaneous + +- Remove changelog maintainer mention filter (authored by @anderseknert) +- Chore: Fix len check in the `ast/visit_test` error message (authored by @boranx) +- `opa inspect`: Fix wrong windows bundle tar files path separator (authored by @shm12) +- Add CHANGELOG.md to website build triggers (authored by @srenatus) + +Dependency bumps: +- Golang 1.19.3 -> 1.19.4 +- github.com/containerd/containerd from 1.6.10 -> 1.6.15 +- github.com/dgraph-io/badger/v3 +- golang.org/x/net to 0.5.0 +- json5 and postcss-modules +- oras.land/oras-go from 1.2.1 -> 1.2.2 + +CI/Distribution fixes: +- Update base images for non debug builds (authored by @charlieegan3) +- Remove deprecated linters in golangci config (authored by @yanggangtony) + +## 0.47.4 + +This is a bug fix release addressing a panic in `opa test`. + + - tester/runner: Fix panic'ing case in utility function. ([#5496](https://github.com/open-policy-agent/opa/issues/5496)) authored by @philipaconrad + +## 0.47.3 + +This is a bug fix release addressing an issue that prevented OPA from fetching bundles stored in S3 buckets. + + - providers/aws: Refactor + fix 2x Authorization header append issue. ([#5472](https://github.com/open-policy-agent/opa/issues/5472)) authored by @philipaconrad, reported by @Hiieu + +## 0.47.2 and 0.46.3 + +This is a second security fix to address CVE-2022-41717/GO-2022-1144. + +We previously believed that upgrading the Golang version and its stdlib would be sufficient +to address the problem. It turns out we also need to bump the x/net dependency to v0.4.0., +a version that hadn't existed when v0.46.2 was released. + +This release bumps the golang.org/x/net dependency to v0.4.0, and contains no other +changes over v0.46.2. + +Note that the affected code is OPA's HTTP server. So if you're using OPA as a Golang library, +or if your confident that your OPA's HTTP interface is protected by other means (as it should +be -- not exposed to the public internet), you're OK. + +## 0.47.1 and 0.46.2 + +This is a bug fix release addressing two issues: one security issue, and one bug +related to formatting backwards-compatibility. + +### Golang security fix CVE-2022-41717 + +> An attacker can cause excessive memory growth in a Go server accepting HTTP/2 requests. + +Since we advise against running an OPA service exposed to the general public of the +internet, potential attackers would be limited to people that are already capable of +sending direct requests to the OPA service. + +### `opa fmt` and backwards compatibility ([#5449](https://github.com/open-policy-agent/opa/issues/5449)) + +In v0.46.1, it was possible that `opa fmt` would format a rule in such a way that: + +1. Before formatting, it was working fine with older OPA versions, and +2. after formatting, it would only work with OPA version >= 0.46.1. + +This backwards incompatibility wasn't intended, and has now been fixed. + +## 0.47.0 + +This release contains a mix of bugfixes, optimizations, and new features. + +### New Built-in Function: `object.keys` + +It is now possible to conveniently retrieve an object's keys via a built-in function. + +Before, you had to resort to constructs like + +```rego +import future.keywords.in + +keys[k] { + _ = input[k] +} + +allow if "my_key" in keys +``` + +Now, you can simply do + +```rego +import future.keywords.in + +allow if "my_key" in object.keys(input) +``` + +See [the documentation](https://www.openpolicyagent.org/docs/v0.47.0/policy-reference/#builtin-object-objectkeys) +for all details. + +Implemented by @kevinswiber. + +### New Built-in Function: AWS Signature v4 Request Signing + +It is now possible to use a built-in function to prepare a request with a signature, so that +it can be used with AWS endpoints that use request signing for authentication. + +See this example: + +```rego +req := {"method": "get", "url": "https://examplebucket.s3.amazonaws.com/data"} +aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", +} +example_verify_resource { + resp := http.send(providers.aws.sign_req(req, aws_config, time.now_ns())) + # process response from AWS ... +} +``` + +See [the documentation on the new built-in](https://www.openpolicyagent.org/docs/v0.47.0/policy-reference/#providers.aws) +for all details. + +Reported by @jicowan and implemented by @philipaconrad. + +### Performance improvements for `object.get` and `in` operator + +Before, using `object.get` and `in` had come with a performance penalty that wasn't +to be expected just from the look of the calls: Since they have been implemented using +built-in functions (obvious for `object.get`, not obvious for `"admin" in input.user.roles`), +all of their operands had to be read from the store (if applicable) and converted into +AST types. + +Now, we use shallow references ("lazy objects") for store reads in the evaluator. +In these two cases, this can bring huge performance improvements, when the object +argument of these two calls is a ref into the base document (like `data.users`): + +```rego +object.get(data.roles, input.role, []) +{ "id": 12 } in data.users +``` + +### Tooling, SDK, and Runtime + +- `opa eval`: Added `--strict` to enable strict code checking in evaluation ([#5182](https://github.com/open-policy-agent/opa/issues/5182)) authored by @Parsifal-M +- `opa fmt`: Remove `{ true }` block following `else` head +- `opa fmt`: Generate new wildcards for else and chained function heads in the parser ([#5347](https://github.com/open-policy-agent/opa/issues/5347)). This fixes superfluous + introductions of `_1` instead of `_` in when formatting functions that use wildcard arguments, like `f(_) := true`. +- `opa fmt`: Fix assignment rewrite in else formatting ([#5348](https://github.com/open-policy-agent/opa/issues/5348)) +- OCI Download: Set auth credentials only if needed ([#5212](https://github.com/open-policy-agent/opa/issues/5212)) authored by @carabasdaniel +- Server: Differentiate between "missing" and "undefined doc" in default decision ([#5344](https://github.com/open-policy-agent/opa/issues/5344)) + +### Topdown and Rego + +- `http.send`: Fix interquery cache size calculation with concurrent requests ([#5359](https://github.com/open-policy-agent/opa/issues/5359)) reported and authored by @asleire +- `http.send`: Remove socket query param for unix sockets ([#5313](https://github.com/open-policy-agent/opa/issues/5313)) reported and authored by @michivi +- Annotations: Add type coercion guards to avoid panics ([#5368](https://github.com/open-policy-agent/opa/issues/5368)) +- Compiler: Provide more accurate error locations for `some` with unused vars ([#4238](https://github.com/open-policy-agent/opa/issues/4238)) +- Optimization: Read lazy objects from the store ([#5325](https://github.com/open-policy-agent/opa/issues/5325)). This improves the performance of `x in data.foo` and `object.get(data.bar, ...)` calls significantly. +- Partial Evaluation: Skip comprehensions when checking eqs in copy propagation ([#5367](https://github.com/open-policy-agent/opa/issues/5367)). This fixes a bug when optimization on bundles would change the outcome of the subsequent evaluation. +- Parser: Fix else error handling with ref heads -- errors had occurred at a later stage then desired, because an edge case slipped through the earlier check. +- Planner/IR: Fix ref heads processing -- the CallDynamic optimization wasn't planned properly; a bug introduced with ref heads. + +### Documentation + +- Builtins: Mention base64 URL encoding specifically ([#5406](https://github.com/open-policy-agent/opa/issues/5406)) reported by @phi1010 +- Builtins: Include behavior with sets in `json.patch` ([#5328](https://github.com/open-policy-agent/opa/issues/5328)) +- Comparison: small fix to table to match sample code and other tables (authored by @anlandu) +- Builtins: Document reference timestamp behavior for `time.parse_ns` +- Typo fixes, authored by @deining +- Golang integration: update example code, move SDK above low-level packages + +### Website + Ecosystem + +- Ecosystem: + - Add Easegress (authored by @localvar) + - Add Terraform Cloud +- Website: Updated Footer Color ([#5254](https://github.com/open-policy-agent/opa/issues/5254)), reported and authored by @UtkarshMishra12 +- Website: Add "canonical" link to latest to help with SEO and ancient pages being returned by search engines. +- Website: Add experimental "OPA version" badge. (Still needs to be tested more thorougly before advertisting it.) + +### Miscellaneous + +- Dependency bumps: Notably, we're now using wasmtime-go v3 +- CI fixes: + - Move performance tests to nightly tests + - CLI: add simple bundle build tests + - Nightly: Revamp how we're doing fuzz testing + +## 0.46.1 + +This is bugfix release to resolve an issue in the release pipeline. Everything else is +the same as 0.46.0. + +## 0.46.0 + +This release contains a mix of bugfixes, optimizations, and new features. + +### New language feature: refs in rule heads + +With this version of OPA, we can use a shorthand for defining deeply-nested structures +in Rego: + +Before, we had to use multiple packages, and hence multiple files to define a structure +like this: +```json +{ + "method": { + "get": { + "allowed": true + } + "post": { + "allowed": true + } + } +} +``` + +```rego +package method.get +default allowed := false +allowed { ... } +``` + + +```rego +package method.post +default allowed := false +allowed { ... } +``` + +Now, we can define those rules in single package (and file): + +```rego +package method +import future.keywords.if +default get.allowed := false +get.allowed if { ... } + +default post.allowed := false +post.allowed if { ... } +``` + +Note that in this example, the use of the future keyword `if` is mandatory +for backwards-compatibility: without it, `get.allowed` would be interpreted +as `get["allowed"]`, a definition of a partial set rule. + +Currently, variables may only appear in the last part of the rule head: + +```rego +package method +import future.keywords.if + +endpoints[ep].allowed if ep := "/v1/data" # invalid +repos.get.endpoint[x] if x := "/v1/data" # valid +``` + +The valid rule defines this structure: +```json +{ + "method": { + "repos": { + "get": { + "endpoint": { + "/v1/data": true + } + } + } + } +} +``` + +To define a nested key-value pair, we would use + +```rego +package method +import future.keywords.if + +repos.get.endpoint[x] = y if { + x := "/v1/data" + y := "example" +} +``` + +Multi-value rules (previously referred to as "partial set rules") that are +nested like this need to use `contains` future keyword, to differentiate them +from the "last part is a variable" case mentioned just above: + +```rego +package method +import future.keywords.contains + +repos.get.endpoint contains x if x := "/v1/data" +``` + +This rule defines the same structure, but with multiple values instead of a key: +```json +{ + "method": { + "repos": { + "get": { + "endpoint": ["/v1/data"] + } + } + } +} +``` + +To ensure that it's safe to build OPA policies for older OPA versions, a new +capabilities field was introduced: "features". It's a free-form string array: + +```json +{ + "features": [ + "rule_head_ref_string_prefixes" + ] +} +``` + +If this key is not present, the compiler will reject ref-heads. This could be +case when building bundles for older OPA version using their capabilities. + + +### Entrypoint annotations in rule metadata + +It is now possible to annotate a rule with `entrypoint: true`, and it will +automatically be picked up by the tooling that expected `--entrypoint` (`-e`) +parameters before. + +For example, to build this rego policy into a wasm module, you had to pass +an entrypoint: + +```rego +package test +allow { + input.x +} +``` +- `opa build --target wasm --entrypoint test/allow policy.rego` + +With the annotation: +```rego +package test + +# METADATA +# entrypoint: true +allow { + input.x +} +``` +- `opa build --target wasm policy.rego` + +The places where entrypoints are taken from metadata are: + +1. Building optimized bundles +2. Building Wasm bundles +3. Building Plan bundles +4. Using optimization with `opa eval` + +Knowing a module's entrypoints can also help in different analysis tasks. + +### New Built-in Functon: `graphql.schema_is_valid` + +The new built-in allows checking schemas: + +```rego +schema := ` + extend type User { + id: ID! + } + extend type Product { + upc: String! + } + union _Entity = Product | User + extend type Query { + entity: _Entity + } +` +valid_schema_example { + graphql.schema_is_valid(schema) +} +``` + +Requested by @olegroom. + +### New Built-in Functon: `net.cidr_is_valid` + +The new built-in function allows checking if a string is a valid CIDR. + +```rego +valid_cidr_example { + net.cidr_is_valid("192.168.0.0/24") +} +``` + +Authored by @ricardomaraschini. + +### Tooling, SDK, and Runtime + +- `opa build`: exit with failure on empty signing key ([#4972](https://github.com/open-policy-agent/opa/issues/4972)) authored by @Joffref reported by @caldwecr +- `opa exec`: add `--fail` and `--fail-defined` flags ([#5007](https://github.com/open-policy-agent/opa/issues/5007)) authored by @byronic reported by @phantlantis +- `opa exec`: convert slashes of explicit bundles (Windows) ([#5134](https://github.com/open-policy-agent/opa/issues/5134)) reported by @peterchenadded +- `opa test`: check coverage limit range `[0, 100]` ([#5284](https://github.com/open-policy-agent/opa/issues/5284)) authored by @hzliangbin reported by @aholmis +- `opa build`+`opa check`: respect capabilities for parsing, i.e. future keywords ([#5323](https://github.com/open-policy-agent/opa/issues/5323)) reported by @TheLunaticScripter +- `opa bench --e2e`: support providing OPA config ([#4899](https://github.com/open-policy-agent/opa/issues/4899)) +- `opa eval`: new explain mode, `--explain=debug`, that includes unifcations in traces (authored by @jaspervdj) + +- Decision logs: Allow rule-based dropping of decision log entries ([#3945](https://github.com/open-policy-agent/opa/issues/3945)) authored by @mariusblarsen and @iamatwork +- Decision Logs: Include the `req_id` attribute in the decision logs ([#5006](https://github.com/open-policy-agent/opa/issues/5006)) reported and authored by @humbertoc-silva +- Plugins: export OpenTelemetry TracerProvider for use in plugins (authored by @vinhph0906) + + +### Compiler + Topdown + +- `graph.reachable_path`: fix issue with missing subpaths ([#4666](https://github.com/open-policy-agent/opa/issues/4666)) authored by @fredallen-wk +- `http.send`: Ensure `force_cache` attribute ignores `Date` header ([#4960](https://github.com/open-policy-agent/opa/issues/4960)) reported by @bartandacc +- `with`: Allow replacing functions with rules ([#5299](https://github.com/open-policy-agent/opa/issues/5299)) +- Evaluation: Skip default functions in full extent ([#5202](https://github.com/open-policy-agent/opa/issues/5202)) reported by @ericjkao +- Evaluation: capture more cases of conflicts in function evaluation ([#5272](https://github.com/open-policy-agent/opa/issues/5272)) +- Rule Indexing: fix incorrect results from indexing `glob.match` even if output is captured ([#5283](https://github.com/open-policy-agent/opa/issues/5283)) + +- Planner: various correctness fixes: [#5271](https://github.com/open-policy-agent/opa/issues/5271), [#5265](https://github.com/open-policy-agent/opa/issues/5265), [#5252](https://github.com/open-policy-agent/opa/issues/5252) + +- Builtins: Refactor registration functions and signatures (authored by @philipaconrad) +- Compiler: Speed up typechecker when working with Refs (authored by @philipaconrad) +- Trace: add `UnifyOp` to tracer events (authored by @jaspervdj) + +### Documentation + +- Envoy Tutorial: use latest proxy_init (v8) +- Envoy Plugin: Add note about new config param to skip body parsing +- Policy Reference: Add `semver` examples +- Contributing Code: Provide some tips for style fixes + +### Website + Ecosystem + +- Website: Make "outdated version" banner red if looked-at version is ancient +- Ecosystem: Add CircleCI and Topaz + +### Miscellaneous + +- Code Cleanup: + - Don't use the deprecated `ioutil` functions + - Use `t.Setenv` in tests + - Use `t.TempDir` to create temporary test directory (authored by @Juneezee) + - Linters: add `unconvert` and `tenv` +- internal/strvals: port helm strvals fix (CLI --set arguments), reported by @pjbgf, helm fix authored by @mattfarina +- Wasm: Update README + +- Dependency bumps, notably: + - Golang: 1.19.2 -> 1.19.3 + - golang.org/x/text 0.3.7 -> 0.4.0 + - oras.land/oras-go 1.2.0 -> 1.2.1 + +## 0.45.0 + +This release contains a mix of bugfixes, optimizations, and new features. + +### Improved Decision Logging with `nd_builtin_cache` + +OPA has several non-deterministic built-ins, such as `rand.intn` and +`http.send` that can make debugging policies from decision log results +a surprisingly tricky and involved process. To improve the situation +around debugging policies that use those built-ins, OPA now provides +an opt-in system for caching the inputs and outputs of these built-ins +during policy evaluation, and can include this information in decision +log entries. + +A new top-level config key is used to enable the non-deterministic +builtin caching feature, as shown below: + + nd_builtin_cache: true + +This data is exposed to OPA's [decision log masking system](https://www.openpolicyagent.org/docs/v0.45.0/management-decision-logs/#masking-sensitive-data) +under the `/nd_builtin_cache` path, which allows masking or dropping +sensitive values from decision logs selectively. This can be useful +in situations where only some information about a non-deterministic +built-in was needed, or the arguments to the built-in involved +sensitive data. + +To prevent unexpected decision log size growth from non-deterministic +built-ins like `http.send`, the new cache information is included in +decision logs on a best-effort basis. If a decision log event exceeds +the `decision_logs.reporting.upload_size_limit_bytes` limit for an OPA +instance, OPA will reattempt uploading it, after dropping the non- +deterministic builtin cache information from the event. This behavior +will trigger a log error when it happens, and will increment the +`decision_logs_nd_builtin_cache_dropped` metrics counter, so that it +will be possible to debug cases where the cache information is unexpectedly +missing from a decision log entry. + +#### Decision Logging Example + +To observe the change in decision logging we can run OPA in server mode +with `nd_builtin_cache` enabled: + +```bash +opa run -s --set=decision_logs.console=true,nd_builtin_cache=true +``` + +After sending it the query `x := rand.intn("a", 15)` we should see +something like the following in the decision logs: + +``` +{..., "msg":"Decision Log", "nd_builtin_cache":{"rand.intn":{"[\"a\",15]":3}}, "query":"assign(x, rand.intn(\"a\", 15))", ..., "result":[{"x":3}], ..., "type":"openpolicyagent.org/decision_logs"} +``` + +The new information is included under the optional `nd_builtin_cache` +JSON key, and shows what arguments were provided for each unique +invocation of `rand.intn`, as well as what the output of that builtin +call was (in this case, `3`). + +If we sent the query `x := rand.intn("a", 15); y := rand.intn("b", 150)"` +we can see how unique input arguments get recorded in the cache: + +``` +{..., "msg":"Decision Log", "nd_builtin_cache":{"rand.intn":{"[\"a\",15]":12,"[\"b\",150]":149}}, "query":"assign(x, rand.intn(\"a\", 15)); assign(y, rand.intn(\"b\", 150))", ..., "result":[{"x":12,"y":149}], ..., "type":"openpolicyagent.org/decision_logs"} +``` + +With this information, it's now easier to debug exactly why a particular +rule is used or why a rule fails when non-deterministic builtins are used in +a policy. + +### New Built-in Function: `regex.replace` + +This release introduces a new builtin for regex-based search/replace on +strings: `regex.replace`. + +See [the built-in functions docs for all the details](https://www.openpolicyagent.org/docs/v0.45.0/policy-reference/#builtin-regex-regexreplace) + +This implementation fixes [#5162](https://github.com/open-policy-agent/opa/issues/5162) and was authored by @boranx. + +### `object.union_n` Optimization + +The `object.union_n` builtin allows easily merging together an array of Objects. + +Unfortunately, as noted in [#4985](https://github.com/open-policy-agent/opa/issues/4985) +its implementation generated unnecessary intermediate copies from doing +pairwise, recursive Object merges. These pairwise merges resulted in poor +performance for large inputs; in many cases worse than writing the +equivalent operation in pure Rego. + +This release changes the `object.union_n` builtin's implementation to use +a more efficient merge algorithm that respects the original implementation's +sequential, left-to-right merging semantics. The `object.union_n` builtin +now provides a 2-3x improvement in speed and memory efficiency over the pure +Rego equivalent. + +### Tooling, SDK, and Runtime + +- cli: Fix doubled CLI hints/errors. ([#5115](https://github.com/open-policy-agent/opa/issues/5115)) authored by @ivanphdz +- cli/test: Add capabilities flag to test command. (authored by @ivanphdz) +- fmt: Fix blank lines after multiline expressions. (authored by @jaspervdj) +- internal/report: Include heap usage in the telemetry report. +- plugins/logs: Improve error message when decision log chunk size is greater than the upload limit. ([#5155](https://github.com/open-policy-agent/opa/issues/5155)) +- ir: Make the `internal/ir` package public as `ir`. + +### Rego + +- ast/parser+formatter: Allow 'if' in rule 'else' statements. +- ast/schema: Add support for recursive json schema elements. ([#5166](https://github.com/open-policy-agent/opa/issues/5166)) authored and reported by @liamg +- ast/schema: Fix race condition in parsing with reused references.(authored by @liamg) +- internal/gojsonschema: Fix race condition in `SetAllowNet`. ([#5187](https://github.com/open-policy-agent/opa/issues/5187)) authored and reported by @liamg +- ast/compiler: Rewrite declared variables in function calls and recursively rewrite local variables in `with` clauses. ([#5148](https://github.com/open-policy-agent/opa/issues/5148)) authored and reported by @liu-du +- ast: Skip rules when parsing a body (or query) to help improve ambiguous parsing cases. + +### Topdown + +- topdown/object: Rework `object.union_n` to use in-place merge algorithm. (reported by @charlesdaniels) +- topdown/jwt_decode_verify: Ensure `exp` and `nbf` fields are numbers when present. ([#5165](https://github.com/open-policy-agent/opa/issues/5165)) authored and reported by @charlieflowers +- topdown: Fix `InterQueryCache` only dropping one entry when over the size limit. (authored by @vinhph0906) +- topdown+builtins: Block all ND builtins from partial evaluation. +- topdown/builtins: Add Rego Object support for GraphQL builtins to improve composability. +- topdown/json: Fix panic in `json.filter` on empty JSON paths. +- topdown/sets_bench_test: Add `intersection` builtin tests. +- topdown/tokens: Protect against nistec panics. ([#5128](https://github.com/open-policy-agent/opa/issues/5218)) + +### Documentation + +- Add IR to integration docs. +- Added Gloo Edge Tutorial with examples. (authored by @Parsifal-M) +- Updated examples for CLI commands. +- Updated section on performance metrics (authored by @hutchins) +- docs/annotations: Add policy example and a link to the policy reference. ([#4937](https://github.com/open-policy-agent/opa/issues/4937)) authored by @Parsifal-M +- docs/policy-language: Be more explicit about future keywords. +- docs/security: Fix token authz example. (authored by @pigletfly) +- docs: Update generated CLI docs. (authored by @charlieflowers) +- docs: Update mentions of `#development` to `#contributors`. (authored by @charlieflowers) + +### Website + Ecosystem + +- website/security: Style improvements. (authored by @orweis) + +### Miscellaneous + +- ci: Add `prealloc` linter check and linter fixes. +- ci: Add govulncheck to Nightly CI. +- build/wasm: Use golang1.16 `go:embed` mechanism. +- util/backoff: Seed from math/rand source. +- version: Use `runtime/debug.BuildInfo`. + +- Dependency bumps, notably: + - build: bump golang 1.19.1 -> 1.19.2 + - build(deps): bump golang.org/x/net + - build(deps): bump internal/gqlparser to v2.5.1 + - build(deps): bump tj-actions/changed-files from 29.0.3 -> 32.0.0 + - deps(build): bump wasmtime-go 0.36.0 -> 1.0.0 (authored by @Parsifal-M) + +## 0.44.0 + +This release contains a number of fixes, two new builtins, a few new features, +and several performance improvements. + +### Security Fixes + +This release includes the security fixes present in the recent v0.43.1 release, +which mitigate CVE-2022-36085 in OPA itself, and CVE-2022-27664 and +CVE-2022-32190 in our Go build tooling. + +See the Release Notes for v0.43.1 for more details. + +### Set Element Addition Optimization + +Rego Set element addition operations did not scale linearly ([#4999](https://github.com/open-policy-agent/opa/pull/4999)) +in the past, and like the Object type before v0.43.0, experienced noticeable +reallocation/memory movement overheads once the Set grew past 120k-150k elements +in size. + +This release introduces different handling of Set internals during element +addition operations to avoid pathological reallocation behavior, and allows +linear performance scaling up into the 500k key range and beyond. + +### Set `union` Built-in Optimization + +The Set `union` builtin allows applying the union operation to a set of sets. + +However, as discovered in [#4979](https://github.com/open-policy-agent/opa/issues/4979), +its implementation generated unnecessary intermediate copies, which resulted in +poor performance; in many cases, worse than writing the equivalent operation in +pure Rego. + +This release improves the `union` builtin's implementation, such that only the +final result set is ever modified, reducing memory allocations and GC pressure. +The `union` builtin is now about 15-30% faster than the equivalent operation in +pure Rego. + +### New Built-in Functions: `strings.any_prefix_match` and `strings.any_suffix_match` + +This release introduces two new builtins, optimized for bulk matching of string +prefixes and suffixes: `strings.any_prefix_match`, and +`strings.any_suffix_match`. +It works with sets and arrays of strings, allowing efficient matching of +collections of prefixes or suffixes against a target string. + +See [the built-in functions docs for all the details](https://www.openpolicyagent.org/docs/v0.42.0/policy-reference/#builtin-strings-stringsany_prefix_match) + +This implementation fixes [#4994](https://github.com/open-policy-agent/opa/issues/4994) and was authored by @cube2222. + +### Tooling, SDK, and Runtime + +- Logger: Allow configuration of the timestamp format ([#2413](https://github.com/open-policy-agent/opa/issues/2413)) +- loader: Add support for fs.FS (authored by @ear7h) + +#### Bundles + +This release includes several bugfixes and improvements around bundle building: + +- cmd: Add optimize flag to OPA eval command to allow building optimized bundles +- cmd/build+compile: Allow opt-out of dependents gathering to allow compilation of more bundles into WASM ([#5035](https://github.com/open-policy-agent/opa/issues/5035)) +- opa build -t wasm|plan: Fail on unmatched entrypoints ([#3957](https://github.com/open-policy-agent/opa/issues/3957)) +- opa build: Fix bundle mode to work with ignore flag +- bundle/status: Include bundle size in status information +- bundle: Remove raw bytes check for lazy bundle loading mode + +#### Storage Fixes + +This release has performance improvements and bugfixes for the disk storage system: + +- storage/disk: Improve handling of in-flight transactions during truncate operations ([#4900](https://github.com/open-policy-agent/opa/issues/4900)) +- storage/inmem: Allow disabling `util.Roundtrip` on Write for improved performance ([#4708](https://github.com/open-policy-agent/opa/issues/4708)) +- storage: Improve multi-bundle data with overlapping roots is handled ([#4998](https://github.com/open-policy-agent/opa/issues/4998)) reported by @sirpi +- storage: Fix issue with policyID in Truncate calls ([#4958](https://github.com/open-policy-agent/opa/issues/4958)) authored by @martinjoha reported by @martinjoha + +#### Rego + +- eval+rego: Support caching output of non-deterministic builtins. ([#1514](https://github.com/open-policy-agent/opa/issues/1514)) + +#### AST and Topdown + +The AST and Topdown module received a number of important bugfixes in this release: + +- ast/term: Fix multiple-reader race condition for Sets/Objects +- ast/compile: Respect unsafeBuiltinMap for 'with' replacements +- ast: Add capacity to array initialization when size is known (authored by @mstrYoda) +- topdown/object: Fix unchecked error case in `object.union_n` builtin ([#5073](https://github.com/open-policy-agent/opa/issues/5073)) +- topdown/reachable: Fix missing operand type checks. ([#4951](https://github.com/open-policy-agent/opa/issues/4951)) +- topdown/units_parse: Avoid extra decimal places for integers +- topdown/type+wasm: Fix inconsistent `is_type` return values. ([#4943](https://github.com/open-policy-agent/opa/issues/4943)) +- builtins: Fix inconsistent error messages in `units.parse*` +- Add query parameter in canonical request of AWS Sigv4 signature to avoid 403 errors from AWS (authored by @sinhaaks) + +#### Test Suite + +- Add error type to `units.*` builtin test assertions +- test/e2e/certrefresh: Add `file.Sync()` to eliminate test failures due to slow disk writes +- topdown/exported_tests: Remove Golang 1.16 x509 exception +- cmd/bench: Fix port collision in utility function used for E2E testing + +### Documentation + +- SECURITY: Migrate policy to web site, update content ([#4272](https://github.com/open-policy-agent/opa/issues/4272)) reported by @adoliver +- Add deprecated flag to all deprecated builtins ([#5072](https://github.com/open-policy-agent/opa/issues/5072)) +- builtins: Update description of `format_int` to say it rounds down +- docs/policy-reference: Update Rego EBNF grammar (authored by @shaded-enmity) +- docs/builtins: Fix typo in `semver.compare` ([#5012](https://github.com/open-policy-agent/opa/issues/5012)) reported by @tetsuya28 +- docs: Fix AWS Signature section in Configuration (authored by @pauly4it) +- docs: Update port and bundle folder for GraphQL tutorial +- docs: Document that function overloading is unsupported +- docs: Fixing related_resources annotations example ([#4982](https://github.com/open-policy-agent/opa/issues/4982)) reported by @humbertoc-silva +- docs: Fixing typo in metadata ([#5018](https://github.com/open-policy-agent/opa/issues/5018)) authored by @cimin0 reported by @cimin0 + +### Website + Ecosystem + +- Update links to opa-kafka-plugin +- Add OCI documentation (authored by @carabasdaniel) +- Add article on using OPA for data filtering in Kafka +- Ecosystem: Add some links to Rönd (authored by @ugho16) +- Add community integration for Fiber (authored by @mstrYoda) +- Add Spacelift Integration (authored by @theseanodell) +- Fix broken link for Minio OPA integration (authored by @unautre) + +- Ecosystem Additions: + - cosign (#5040) (authored by @Dentrax) + +### Miscellaneous + +- Dockerfile: Append root "/" to $PATH ([#5003](https://github.com/open-policy-agent/opa/issues/5003)) authored by @matusf reported by @matusf +- Add VNG Cloud to adopters (authored by @vinhph0906) + +- Dependency bumps, notably: + - build: bump golang: 1.19 -> 1.19.1 + - build: use go 1.19, drop go 1.16 + - build(deps): bump aquasecurity/trivy-action from 0.6.1 -> 0.7.1 + - build(deps): bump github.com/agnivade/levenshtein from 1.0.1 -> 1.1.1 + - build(deps): bump github.com/containerd/containerd from 1.6.6 -> 1.6.8 + - build(deps): bump github.com/go-ini/ini from 1.66.6 -> 1.67.0 + - build(deps): bump github.com/prometheus/client_golang + - build(deps): bump google.golang.org/grpc from 1.48.0 -> 1.49.0 + - build(deps): bump tj-actions/changed-files from 28.0.0 -> 29.0.3 + +- Dependency removals: + - internal: Vendor gqlparser library ([#5065](https://github.com/open-policy-agent/opa/issues/5065)) reported by @vikstrous2 + +## 0.43.1 + +This is a security release fixing the following vulnerabilities: + +- CVE-2022-36085: Respect unsafeBuiltinMap for 'with' replacements in the compiler + + See https://github.com/open-policy-agent/opa/security/advisories/GHSA-f524-rf33-2jjr for all details. + +- CVE-2022-27664 and CVE-2022-32190. + + Fixed by updating the Go version used in our builds to 1.18.6, + see https://groups.google.com/g/golang-announce/c/x49AQzIVX-s. + Note that CVE-2022-32190 is most likely not relevant for OPA's usage of net/url. + But since these CVEs tend to come up in security assessment tooling regardless, + it's better to get it out of the way. +## 0.43.0 + +This release contains a number of fixes, enhancements, and performance improvements. + +### Object Insertion Optimization + +Rego Object insertion operations did not scale linearly ([#4625](https://github.com/open-policy-agent/opa/issues/4625)) +in the past, and experienced noticeable reallocation/memory movement +overheads once the Object grew past 120k-150k keys in size. + +This release introduces different handling of Object internals during insert +operations to avoid pathological reallocation behavior, and allows linear +performance scaling up into the 500k key range and beyond. + +### Tooling, SDK, and Runtime + +- Add lines covered/not covered counts to test coverage report (authored by @FarisR99) +- Plugins: Status and logs plugins now accept any HTTP 2xx status code (authored by @lvisterin) +- Runtime: Generalize OS check for MacOS to other Unix-likes (authored by @iamleot) + +#### Bundles Fixes + +The Bundles system received several bugfixes and performance improvements in this release: + + - Bundle: `opa bundle` command now supports `.yml` files ([#4859](https://github.com/open-policy-agent/opa/issues/4859)) authored by @Joffref reported by @rdrgmnzsakt + - Plugins/Bundle: Use unique temporary files for persisting activated bundles to disk ([#4782](https://github.com/open-policy-agent/opa/issues/4782)) authored by @FredrikAppelros reported by @FredrikAppelros + - Server: Old policy path is now checked for bundle ownership before update ([#4846](https://github.com/open-policy-agent/opa/issues/4846)) + - Storage+Bundle: Old bundle data is now cleaned before new bundle activation ([#4940](https://github.com/open-policy-agent/opa/issues/4940)) + - Bundle: Paths are now normalized before bundle root check occurs to ensure checks are os-independent + +#### Storage Fixes + +The Storage system received mostly bugfixes, with a notable performance improvement for large bundles in this release: + + - storage/inmem: Speed up bundle activation by avoiding unnecessary read operations ([#4898](https://github.com/open-policy-agent/opa/issues/4898)) + - storage/inmem: Paths are now created during truncate operations if they did not exist before + - storage/disk: Symlinks work with relative paths now ([#4869](https://github.com/open-policy-agent/opa/issues/4869)) + +### Rego and Topdown + +The Rego compiler and runtime environment received a number of bugfixes, and a few new features this release, as well as a notable performance improvement for large Objects +(covered above). + +- AST/Compiler: New method for obtaining parsed, but otherwise unprocessed modules is now available ([#4910](https://github.com/open-policy-agent/opa/issues/4910)) +- `object.subset`: Support array + set combination ([#4858](https://github.com/open-policy-agent/opa/issues/4858)) authored by @x-color +- Compiler: Prevent erasure of `print()` statements in the compiler via a `WithEnablePrintStatements` option to `compiler.Compiler` and `compiler.optimizer` (authored by @kevinstyra) +- Topdown fixes: + - AST/Builtins: `type_name` builtin now has more precise type metadata and improved docs + - Topdown/copypropagation: Ref-based tautologies like `input.a == input.a` are no longer eliminated during the copy-propagation pass ([#4848](https://github.com/open-policy-agent/opa/issues/4848)) reported by @johanneskra + - Topdown/parse_units: Use big.Rat for units parsing to avoid floating-point rounding issues on fractional units. ([#4856](https://github.com/open-policy-agent/opa/issues/4856)) reported by @tmos22 + - Topdown: `is_valid` builtins no longer error, and should always return booleans ([#4760](https://github.com/open-policy-agent/opa/issues/4760)) + - Topdown: `glob.match` now can be used without delimiters ([#4923](https://github.com/open-policy-agent/opa/issues/4923)) authored by @vinhph0906 reported by @vinhph0906 + +### Documentation + + - Docs: Add GraphQL API authorization tutorial + - Docs/bundles: Add bundle CLI command documentation ([#3831](https://github.com/open-policy-agent/opa/issues/3831)) authored by @Joffref + - Docs/policy-reference: Remove extra quote in Grammar to fix formatting ([#4915](https://github.com/open-policy-agent/opa/issues/4915)) authored by @friedrichsenm reported by @friedrichsenm + - Docs/policy-testing: Add missing future.keywords imports ([#4849](https://github.com/open-policy-agent/opa/issues/4849)) reported by @robert-elles + - Docs: Add note about counter_server_query_cache_hit metric ([#4389](https://github.com/open-policy-agent/opa/issues/4389)) + - Docs: Kube tutorial includes updated cert install procedure ([#4902](https://github.com/open-policy-agent/opa/issues/4902)) reported by @Imp + - Docs: GraphQL builtins section now includes a note about framework-specific `@directive` definitions in GraphQL schemas + - Docs: Add warning about name collisions in older policies from importing 'future.keywords' + +### Website + Ecosystem + +- Website: Show navbar on smaller devices ([#3353](https://github.com/open-policy-agent/opa/issues/3353)) authored by @Parsifal-M reported by @OBrienCommaJosh +- Website/frontpage: Update front page examples to use the future.keywords imports +- Website/live-blocks: Only pass 'import future.keywords' when needed and supported +- Website/live-blocks: Update codemirror-rego to 1.3.0 +- Website: Fix community page layout/scrolling issues (authored by @mstade) + +- Ecosystem Additions: + - Rond (authored by @ugho16) + - walt.id + +### Miscellaneous + +- Dependency bumps, notably: + - aquasecurity/trivy-action from 0.5.1 to 0.6.1 + - github.com/sirupsen/logrus from 1.8.1 to 1.9.0 + - github.com/vektah/gqlparser/v2 from 2.4.5 to 2.4.6 + - google.golang.org/grpc from 1.47.0 to 1.48.0 + - terser in /docs/website/scripts/live-blocks + - glob-parent in /docs/website/scripts/live-blocks +- Added GKE Policy Automation to ADOPTERS.md (authored by @mikouaj) +- Fix minor code unreachability error (authored by @Abirdcfly) + +## 0.42.2 + +This is a bug fix release that addresses the following: + +- storage/disk: make symlinks work with relative paths ([#4869](https://github.com/open-policy-agent/opa/issues/4869)) +- bundle: Normalize paths before bundle root check + +## 0.42.1 + +This is a bug fix release that addresses the following: + +1. An issue while writing data to the in-memory store at a non-root nonexistent path ([#4855](https://github.com/open-policy-agent/opa/issues/4855)), reported by @wermerb and others. +2. Policies owned by a bundle could be replaced via the REST API because of a missing bundle scope check ([#4846](https://github.com/open-policy-agent/opa/issues/4846)). +3. Adds missing `future.keywords` import for the examples in the policy testing section of the docs ([#4849](https://github.com/open-policy-agent/opa/issues/4849)), reported by @robert-elles. + +## 0.42.0 + +This release contains a number of fixes and enhancements. + +### New built-in function: `object.subset` + +This function checks if a collection is a subset of another collection. +It works on objects, sets, and arrays. + +If both arguments are objects, then the operation is recursive, e.g. `{"c": {"x": {10, 15, 20}}` +is considered a subset of `{"a": "b", "c": {"x": {10, 15, 20, 25}, "y": "z"}`. + +See [the built-in functions docs for all the details](https://www.openpolicyagent.org/docs/v0.42.0/policy-reference/#builtin-object-objectsubset) + +This implementation fixes [#4358](https://github.com/open-policy-agent/opa/issues/4358) and was authored by @charlesdaniels. + +### New keywords: "contains" and "if" + +These new keywords let you increase the expressiveness of your policy code: + +Before + +```rego +package authz +allow { not denied } # `denied` left out for presentation purposes + +deny[msg] { + count(violations) > 0 + msg := sprintf("there are %d violations", [count(violations)]) +} +``` + +After + +```rego +package authz +import future.keywords + +allow if not denied # one expression only => no { ... } needed! + +deny contains msg if { + count(violations) > 0 + msg := sprintf("there are %d violations", [count(violations)]) +} +``` + +Note that rule bodies containing only one expression can be abbreviated when using `if`. + +To use the new keywords, use `import future.keywords.contains` and `import future.keywords.if`; or +import all of them at once via `import future.keywords`. When these future imports are present, the +pretty printer (`opa fmt`) will introduce `contains` and `if` where applicable. + +`if` is allowed in all places to separate the rule head from the body, like +```rego +response[key] = value if { key := "open", y := "sesame" } +``` +_but_ not for partial set rules, unless also using `contains`: +```rego +deny[msg] if msg := "forbidden" # INVALID +deny contains msg if msg := "forbidden" # VALID +``` + +### Tooling, SDK, and Runtime + +- Plugins: + - S3 Plugin: Allow multiple AWS credential providers at once, chained together ([#4791](https://github.com/open-policy-agent/opa/issues/4791)), reported and authored by @abhisek + - Discovery Plugin: Check for empty key config ([#4656](https://github.com/open-policy-agent/opa/issues/4656)) reported by @humbertoc-silva + - Logs Plugin: Update mechanism to escape field paths ([#4717](https://github.com/open-policy-agent/opa/issues/4717)) reported by @pauly4it + - Status Plugin: fix `bundle_failed_load_counter` metric for bundles without revisions ([#4822](https://github.com/open-policy-agent/opa/issues/4822)) reported and authored by @jkbschmid +- Server: The `system.authz` policy now properly supports the interquery caching of `http.send` calls ([#4829](https://github.com/open-policy-agent/opa/issues/4829)), reported by @HarshPathakhp +- `opa bench`: Passing `--e2e` makes the benchmark measure the performance of a query including the server's HTTP handlers and their processing. +- `opa fmt`: Output list _and_ diff changes with `--fail` flag (#4710) (authored by @davidkuridza) +- Disk Storage: Bundles are now streamed into the disk store, and not extracted completely in-memory ([#4539](https://github.com/open-policy-agent/opa/issues/4539)) +- Golang package `repl`: Add a `WithCapabilities` function (authored by @jaspervdj) +- SDK: Allow configurable ID (authored by @rakshasa-1729) +- Windows: User lookups in various code paths have been avoided. They had no use, but are costly, and removing them should increase + the performance of any CLI calls (even `opa version`) on Windows. Fixes [#4646](https://github.com/open-policy-agent/opa/issues/4646). +- Server: Open read storage transaction in Query API handler (not write) + +### Rego and Topdown + +- Runtime Errors: Fix type error message in `count`, `object.filter`, and `object.remove` built-in functions ([#4767](https://github.com/open-policy-agent/opa/issues/4767)) +- Parser: Remove early MHS return in infix parsing, fixing confusing error messages ([#4672](https://github.com/open-policy-agent/opa/issues/4672)) authored by @philipaconrad +- AST: Disallow shadowing of called functions in comprehension heads ([#4762](https://github.com/open-policy-agent/opa/issues/4762)) +- Planner/IR: shadow rule funcs if mocking functions ([#4746](https://github.com/open-policy-agent/opa/issues/4746)) +- Compiler: Fix "every" handling in partial eval: by reordering body for safety differently, and correctly plugging its terms on safe ([#4801](https://github.com/open-policy-agent/opa/pull/4801)), reported by @jguenther-va +- Compiler: fix util.HashMap eq comparison ([#4759](https://github.com/open-policy-agent/opa/pull/4759)) +- Built-ins: use strings.Builder in glob.match() (authored by @charlesdaniels) + +### Documentation + +- Builtins: Fix documentation of `startswith` and `endswith` (authored by @whme) +- Kubenetes Tutorial: Remove unused assignement in example ([#4778](https://github.com/open-policy-agent/opa/issues/4778)) authored by @Joffref +- OCI: Update configuration docs for private images in OCI registries (authored by @carabasdaniel) +- AWS S3 Signing: Fix profile_credentials docs (authored by @wangli1030) + +### Website + Ecosystem + +- Add "Edit on GitHub" button to docs ([#3784](https://github.com/open-policy-agent/opa/issues/3784)) authored by @avinashdesireddy +- Wasm: fix function table markup ([#4664](https://github.com/open-policy-agent/opa/issues/4664)) +- Ecosystem: use location.hash to track open modal ([#4667](https://github.com/open-policy-agent/opa/issues/4667)) + +Note that website changes like these become effective immediately and are not tied to a release. +We still use our release notes to record the nice fixes contributed by our community. + +- Ecosystem Additions: + - Alfred, the self-hosted playground (authored by @dolevf) + - Java Spring tutorial (authored by @psevestre) + - Pulumi + +### Miscellaneous + +- Add Terminus to ADOPTERS.md (#4734) ([#4713](https://github.com/open-policy-agent/opa/issues/4713)) reported by @charlieflowers +- Remove any data attributes not used in the "YAML tests" ([#4813](https://github.com/open-policy-agent/opa/issues/4813)) +- Dependency bumps, notably: + - github.com/prometheus/client_golang 1.12.2 ([#4697](https://github.com/open-policy-agent/opa/issues/4697)) + - github.com/vektah/gqlparser/v2 2.4.5 +- Build process and CI: + - Use Trivy for vulnerability scans in code and container images (authored by @JAORMX) + - Bump golangci-lint to v1.46.2, fix some issues ([#4765](https://github.com/open-policy-agent/opa/issues/4765)) + - Remove npm-opa-wasm test + - Skip flaky darwin tests on PR runs + - Fix flaky oci e2e test ([#4748](https://github.com/open-policy-agent/opa/issues/4748)) authored by @carabasdaniel + - Integrate builtin_metadata.json handling in release process ([#4754](https://github.com/open-policy-agent/opa/issues/4754)) + + +## 0.41.0 + +This release contains a number of fixes and enhancements. + +### GraphQL Built-in Functions + +A new set of built-in functions are now available to validate, parse and verify GraphQL query and schema! Following are +the new built-ins: + + graphql.is_valid: Checks that a GraphQL query is valid against a given schema + graphql.parse: Returns AST objects for a given GraphQL query and schema + graphql.parse_and_verify: Returns a boolean indicating success or failure alongside the parsed ASTs for a given GraphQL query and schema + graphql.parse_query: Returns an AST object for a GraphQL query + graphql.parse_schema: Returns an AST object for a GraphQL schema + +### Built-in Function Metadata + +Built-in function declarations now support additional metadata to specify name and description for function arguments +and return values. The metadata can be programmatically consumed by external tools such as IDE plugins. The built-in +function documentation is created using the new built-in function metadata. +Check out the new look of the [Built-In Reference](https://www.openpolicyagent.org/docs/latest/policy-reference/#built-in-functions) +page! + +Under the hood, a new file called `builtins_metadata.json` is generated via `make generate` which can be consumed by +external tools. + +### Tooling, SDK, and Runtime + +- OCI Downloader: Add logic to skip bundle reloading based on the digest of the OCI artifact ([#4637](https://github.com/open-policy-agent/opa/issues/4637)) authored by @carabasdaniel +- Bundles: Exclude empty manifest from bundle signature ([#4712](https://github.com/open-policy-agent/opa/issues/4712)) authored by @friedrichsenm reported by @friedrichsenm + +### Rego and Topdown + +- units.parse: New built-in for parsing standard metric decimal and binary SI units (e.g., K, Ki, M, Mi, G, Gi) +- format: Fix `opa fmt` location for non-key rules (#4695) (authored by @jaspervdj) +- token: Ignore keys of unknown alg when verifying JWTs with JWKS ([#4699](https://github.com/open-policy-agent/opa/issues/4699)) reported by @lenalebt + +### Documentation + +- Adding Built-in Functions: Add note about `capabilities.json` while creating a new built-in function +- Policy Reference: Add example for `rego.metadata.rule()` built-in function +- Policy Reference: Fix grammar for `import` keyword ([#4689](https://github.com/open-policy-agent/opa/issues/4689)) authored by @mmzeeman reported by @mmzeeman +- Security: Fix command line flag name for file containing the TLS certificate ([#4678](https://github.com/open-policy-agent/opa/issues/4678)) authored by @pramodak reported by @pramodak + +### Website + Ecosystem + +- Update Kubernetes policy examples on the website to use latest kubernetes schema (`apiVersion`: `admission.k8s.io/v1`) (authored by @vicmarbev) +- Ecosystem: + - Add Sansshell (authored by @sfc-gh-jchacon) + - Add Nginx + +### Miscellaneous + +- Various dependency bumps, notably: + - OpenTelemetry-go: 1.6.3 -> 1.7.0 + - go.uber.org/automaxprocs: 1.4.0 -> 1.5.1 + - github.com/containerd/containerd: 1.6.2 -> 1.6.4 + - google.golang.org/grpc: 1.46.0 -> 1.47.0 + - github.com/bytecodealliance/wasmtime-go: 0.35.0 -> 0.36.0 + - github.com/vektah/gqlparser/v2: 2.4.3 -> 2.4.4 +- `make test`: Fix "too many open files" issue on Mac OS +- Remove usage of github.com/pkg/errors package (authored by @imjasonh) + +## 0.40.0 + +This release contains a number of fixes and enhancements. + +### Metadata introspection + +The _rich metadata_ added in the v0.38.0 release can now be introspected +from the policies themselves! + + package example + + # METADATA + # title: Edits by owner only + # description: | + # Only the owner is allowed to edit their data. + deny[{"allowed": false, "message": rego.metadata.rule().description}] { + input.user != input.owner + } + +This snippet will evaluate to + + [{ + "allowed": false, + "message": "Only the owner is allowed to edit their data.\n" + }] + +Both the rule's metadata can be accessed, via `rego.metadata.rule()`, and the +entire chain of metadata attached to the rule via the various scopes that different +metadata annotations can have, via `rego.metadata.chain()`. + +All the details can be found in the documentation of [these new built-in functions](https://www.openpolicyagent.org/docs/v0.40.0/policy-reference/#rego). + +### Function mocking + +It is now possible to **mock functions** in tests! Both built-in and non-built-in +functions can be mocked: + + package authz + import data.jwks.cert + import data.helpers.extract_token + + allow { + [true, _, _] = io.jwt.decode_verify(extract_token(input.headers), {"cert": cert, "iss": "corp.issuer.com"}) + } + + test_allow { + allow + with input.headers as [] + with data.jwks.cert as "mock-cert" + with io.jwt.decode_verify as [true, {}, {}] # mocked built-in + with extract_token as "my-jwt" # mocked non-built-in + } + +For further information about policy testing with data and function mock, see [the Policy Testing docs](https://www.openpolicyagent.org/docs/v0.40.0/policy-testing/#data-and-function-mocking) +All details about `with` can be found in its [Policy Language section](https://www.openpolicyagent.org/docs/v0.40.0/policy-language/#with-keyword). + +### Assignments with `:=` + +Remaining restrictions around the use of `:=` in rules and functions have been lifted ([#4555](https://github.com/open-policy-agent/opa/issues/4555)). +These constructs are now valid: + + check_images(imgs) := x { # function + # ... + } + + allow := x { # rule + # ... + } + + response[key] := object { # partial object rule + # ... + } + +In the wake of this, rules may now be "redeclared", i.e. you can use `:=` for more than one rule body: + + deny := x { + # body 1 + } + deny := x { + # body 2 + } + +This was forbidden before, but didn't serve a real purpose: it would catch trivial-to-catch errors +like + + p := 1 + p := 2 # redeclared + +But it would do no good in more difficult to debug "multiple assignment" problems like + + p := x { + some x in [1, 2, 3] + } + +### Tooling, SDK, and Runtime + +- Status Plugin: Remove activeRevision label on all but one Prometheus metric ([#4584](https://github.com/open-policy-agent/opa/issues/4584)) reported and authored by @costimuraru +- Status: Include bundle type ("snapshot" or "delta") in status information +- `opa capabilities`: Expose capabilities through CLI, and allow using versions when passing `--capabilities v0.39.0` to the various commands ([#4236](https://github.com/open-policy-agent/opa/issues/4236)) authored by @IoannisMatzaris +- Logging: Log warnings at WARN level not ERROR, authored by @damienjburks +- Runtime: Persist activated bundle Etag to store ([#4544](https://github.com/open-policy-agent/opa/issues/4544)) +- `opa eval`: Don't use source locations when formatting partially evaluated output ([#4609](https://github.com/open-policy-agent/opa/issues/4609)) +- `opa inspect`: Fixing an issue where some errors encountered by the inspect command aren't properly reported +- `opa fmt`: Fix a bug with missing whitespace when formatting multiple `with` statements on one indented line ([#4634](https://github.com/open-policy-agent/opa/issues/4634)) + +#### Experimental OCI support + +When configured to do so, OPA's bundle and discovery plugins will retrieve bundles from **any OCI registry**. +Please see [the Services Configuration section](https://www.openpolicyagent.org/docs/v0.40.0/configuration/#services) +for details. + +Note that at this point, it's best considered a "feature preview". Be aware of this: +- Bundles are not cached, but re-retrieved and activated periodically. +- The persistence directory used for storing retrieved OCI artifacts is not yet managed by OPA, + so its content may accumulate. By default, the OCI downloader will use a temporary file location. +- The documentation on how to push bundles to an OCI repository currently only exists in the development + docs, see [OCI.md](https://github.com/open-policy-agent/opa/blob/v0.40.0/docs/devel/OCI.md). + +Thanks to @carabasdaniel for starting the work on this! + +### Rego and Topdown + +- Builtins: Require prefix length for IPv6 in `net.cidr_merge` ([#4596](https://github.com/open-policy-agent/opa/issues/4596)), reported by @alexhu20 +- Builtins: `http.send` can now parse and cache YAML responses, analogous to JSON responses +- Parser: Guard against invalid domains for "some" and "every", reported by @doyensec +- Formatting: Don't add 'in' keyword import when 'every' is there ([#4606](https://github.com/open-policy-agent/opa/issues/4606)) + +### Documentation + +- Policy Language: Reorder Universal Quantification content, stress `every` over other constructions ([#4603](https://github.com/open-policy-agent/opa/issues/4603)) +- Language pages: Use assignment operator where it's allowed. +- SSH Tutorial: Use bundle API +- Annotations: Update "Custom" annotation section +- Cloudformation: Fix markup and add warning related to booleans +- Blogs: mention OAuth2 and OIDC blog posts + +### Website + Ecosystem + +- Redirect previous patch releases to latest patch release ([#4225](https://github.com/open-policy-agent/opa/issues/4225)) +- Add playground button to navbar +- Add SRI to static html files +- Remove right margin on sidebar (#4529) (authored by @orweis) +- Show yellow banner for old version (#4533) +- Remove unused variables to avoid error in strict mode(#4534) (authored by @panpan0000) +- Ecosystem: + - Add AWS CloudFormation Hook + - Add GKE policy automation + - Add permit.io (authored by @ozradi) + - Add Magda (authored by @t83714) + +### Miscellaneous + +- Workflow: no content permissions for GitHub action 'post-release', authored by @naveensrinivasan +- Various dependency bumps, notably: + - OpenTelemetry-go: 1.6.1 -> 1.6.3 + - go.uber.org/automaxprocs: 1.4.0 -> 1.5.1 +- Binaries and Docker images are now built using Go 1.18.1. +- Dockerfile: add source annotation (#4626) + +## 0.39.0 + +This release contains a number of fixes and enhancements. + +### Disk Storage + +The on-disk storage backend has been fully integrated with the OPA server, and +can now be enabled via configuration: + +```yaml +storage: + disk: + directory: /var/opa # put data here + auto_create: true # create directory if it doesn't exist + partitions: # partitioning is important for data storage, + - /users/* # please see the documentation +``` + +It is intended to enable the use of OPA in scenarios where the data needed for +policy evaluation exceeds the available memory. + +The on-disk contents will persist among restarts, but should not be used as a +single source of truth: there are no backup mechanisms, and certain data partitioning +changes will require a start-over. These are things that may get improved in the +future. + +For all the details, please refer to the [configuration](https://www.openpolicyagent.org/docs/v0.39.0/configuration/#disk-storage) +and [detailled Disk Storage section](https://www.openpolicyagent.org/docs/v0.39.0/misc-disk/) +of the documentations. + +### Tooling, SDK, and Runtime + +- Server: Add warning when `input` attribute is missing in `POST /v1/data` API ([#4386](https://github.com/open-policy-agent/opa/issues/4386)) authored by @aflmp +- SDK: Support partial evaluation ([#4240](https://github.com/open-policy-agent/opa/pull/4240)), authored by @kroekle; with a fix to avoid using different state (authored by @Iceber) +- Runtime: Suppress payloads in debug logs for handlers that compress responses (`/metrics` and `/debug/pprof`) (authored by @christian1607) +- `opa test`: Add file path to failing tests to make debugging failing tests easier ([#4457](https://github.com/open-policy-agent/opa/issues/4457)), authored by @liamg +- `opa fmt`: avoid whitespace mixed with tabs on `with` statements ([#4376](https://github.com/open-policy-agent/opa/issues/4376)) reported by @tiwood +- Coverage reporting: Remove duplicates from coverage report ([#4393](https://github.com/open-policy-agent/opa/issues/4393)) reported by @gianna7wu +- Plugins: Fix broken retry logic in decision logs plugin ([#4486](https://github.com/open-policy-agent/opa/issues/4486)) reported by @iamatwork +- Plugins: Update regular polling fallback mechanism for downloader +- Plugins: Support for adding custom parameters and headers for OAuth2 Client Credentials Token request (authored by @srlk) +- Plugins: Log message on unexpected bundle content type ([#4278](https://github.com/open-policy-agent/opa/issues/4278)) +- Plugins: Mask Authorization header value in debug logs ([#4495](https://github.com/open-policy-agent/opa/issues/4495)) +- Docker images: Use GID 1000 in `-rootless` images ([#4380](https://github.com/open-policy-agent/opa/issues/4380)); also warn when using UID/GID 0. +- Runtime: change processed file event log level to info + +### Rego and Topdown + +- Type checker: Skip pattern JSON Schema attribute compilation ([#4426](https://github.com/open-policy-agent/opa/issues/4426)): These are not supported, but could have caused the parsing of a JSON Schema document to fail. +- Topdown: Copy without modifying expr, fixing a bug that could occur when running multiple partial evaluation requests concurrently. +- Compiler strict mode: Raise error on unused imports ([#4354](https://github.com/open-policy-agent/opa/issues/4354)) authored by @damienjburks +- AST: Fix print call rewriting in else rules ([#4489](https://github.com/open-policy-agent/opa/issues/4489)) +- Compiler: Improve error message on missing `with` target ([#4431](https://github.com/open-policy-agent/opa/issues/4431)) reported by @gabrielfern +- Parser: hint about 'every' future keyword import + +### Documentation and Website + +- AWS CloudFormation Hook: New tutorial +- Community: Stretch background so it covers on larger screens ([#4402](https://github.com/open-policy-agent/opa/issues/4402)) authored by @msorens +- Build: Make local dev and PR preview not build everything ([#4379](https://github.com/open-policy-agent/opa/issues/4379)) +- Philosophy: Grammar fixes (authored by @ajonesiii) +- README: Add note about Hugo version mismatch errors (authored by @ogazitt) +- Integrations: Add GraphQL-Graphene (authored by @dolevf), Emissary-Ingress (authored by @tayyabjamadar), rekor-sidekick, +- Integrations CI: ensure referenced software is listed, and logo file names match; allow SVG logos +- Envoy: Update policy primer with new control headers +- Envoy: Update bob_token and alice_token in tutorial (authored by @rokkiter) +- Envoy: Include new configurable gRPC msg sizes (authored by @emaincourt) +- Annotations: add missing title to index (authored by @itaysk) + +### Miscellaneous + +- Various dependency bumps, notably: + - OpenTelemetry-go: 1.4.1 -> 1.6.1 + - Wasmtime-go: 0.34.0 -> 0.35.0 +- Binaries and Docker images are now built using Go 1.18; CI runs build/test for Ubuntu and macos with Go 1.16 and 1.17. +- CI: remove go-fuzz, use native go 1.18 fuzzer + +## 0.38.1 + +This is a bug fix release that addresses one issue when using `opa test` with the +`--bundle` (`-b`) flag, and a policy that uses the `every` keyword. + +There are no other code changes in this release. + +### Fixes + +- Compiler: don't raise an error with unused declared+generated vars + (every) ([#4420](https://github.com/open-policy-agent/opa/issues/4420)), + reported by @kristiansvalland + +## 0.38.0 + +This release contains a number of fixes and enhancements. + +It contains one **backwards-incompatible change** to the JSON representation +of metrics in **Status API** payloads, please see the section below. + +### Rich Metadata + +It is now possible to annotate Rego policies in a way that can be +processed programmatically, using _Rich Metadata_. + + # METADATA + # title: My rule + # description: A rule that determines if x is allowed. + # authors: + # - Jane Austin + allow { + ... + } + +The available keys are: + +- title +- description +- authors +- organizations +- related_resources +- schemas +- scope +- custom + +Custom annotations can be used to annotate rules, packages, and +documents with whatever you specifically need, beyond the generic +keywords. + +Annotations can be retrieved using the [Golang library](https://www.openpolicyagent.org/docs/v0.38.0/annotations/#go-api) +or via the CLI, `opa inspect -a`. + +All the details can be found in the documentation on [Annotations](https://www.openpolicyagent.org/docs/v0.38.0/annotations/). + +### Every Keyword + +A new keyword for explicit iteration is added to Rego: `every`. + +It comes in two forms, iterating values, or keys and values, of a +collection, and asserting that the body evaluates successfully for +each binding of key and value to the collection's elements: + + every k, v in {"foo": "FOO", "bar": "BAR" } { + upper(k) == v + } + +To use it, `import future.keywords.every` or `future.keywords`. + +For further information, please refer to the [Every Keyword docs](https://www.openpolicyagent.org/docs/v0.38.0/policy-language/#every-keyword) +and the new section on [_FOR SOME and FOR ALL_ in the Intro docs](https://www.openpolicyagent.org/docs/v0.38.0/#for-some-and-for-all). + +### Tooling, SDK, and Runtime + +- Compile API: add `disableInlining` option ([#4357](https://github.com/open-policy-agent/opa/issues/4357)) reported and fixed by @srlk +- Status API: add `http_code` to response ([#4259](https://github.com/open-policy-agent/opa/issues/4259)) reported and fixed by @jkbschmid +- Status plugin: publish experimental bundle-related metrics via prometheus endpoint (authored by @rafaelreinert) -- See [Status Metrics](https://www.openpolicyagent.org/docs/v0.38.0/monitoring/#status-metrics) for details. +- SDK: don't panic without config ([#4303](https://github.com/open-policy-agent/opa/issues/4303)) authored by @damienjburks +- Storage: Support index for array appends (for JSON Patch compatibility) +- `opa deps`: Fix pretty printed output to show virtual documents ([#4342](https://github.com/open-policy-agent/opa/issues/4342)) + +### Rego and Topdown + +- Parser: parse 'with' on 'some x in xs' expression ([#4226](https://github.com/open-policy-agent/opa/issues/4226)) +- AST: hash containers on insert/update ([#4345](https://github.com/open-policy-agent/opa/issues/4345)), fixing a data race reported by @skillcoder +- Planner: Fix bug related to undefined results in dynamic lookups + +### Documentation and Website + +- Policy Reference: update EBNF to include "every" and "some x in ..." ([#4216](https://github.com/open-policy-agent/opa/issues/4216)) +- REST API: Update docs on 400 response +- README: Include Google Analytic Instructions +- Envoy primer: use variables instead of objects +- Istio tutorial: expose application to outside traffic +- New "Community" Webpage (authored by @msorens) + +### WebAssembly + +- OPA now uses Wasmtime 0.34.0 to evaluate its Wasm modules. + +### Miscellaneous + +- Build: `make build` now builds without errors (by disabling Wasm) on darwin/arm64 (M1) +- Various dependency bumps. + - OpenTelemetry SDK: 1.4.1 + - github.com/prometheus/client_golang: 1.12.1 + +### Backwards incompatible changes + +The JSON representation of the Status API's payloads -- both for `GET /v1/status` +responses and the metrics sent to a remote Status API endpoint -- have changed: + +Previously, they had been serialized into JSON using the standard library "encoding/json" +methods. However, the metrics coming from the Prometheus integration are only available +in Golang structs generated from Protobuf definitions. For serializing these into JSON, +the standard library functions are unsuited: + +- enums would be converted into numbers, +- field names would be `snake_case`, not `camelCase`, +- and NaNs would cause the encoder to panic. + +Now, we're using the protobuf ecosystem's `jsonpb` package, to serialize the Prometheus +metrics into JSON in a way that is compliant with the Protobuf specification. + +Concretely, what would before be +``` + "metrics": { + "prometheus": { + "go_gc_duration_seconds": { + "help": "A summary of the GC invocation durations.", + "metric": [ + { + "summary": { + "quantile": [ + { + "quantile": 0, + "value": 0.000011799 + }, + { + "quantile": 0.25, + "value": 0.000011905 + }, + { + "quantile": 0.5, + "value": 0.000040002 + }, + { + "quantile": 0.75, + "value": 0.000065238 + }, + { + "quantile": 1, + "value": 0.000104897 + } + ], + "sample_count": 7, + "sample_sum": 0.000309117 + } + } + ], + "name": "go_gc_duration_seconds", + "type": 2 + }, +``` + +is *now*: +``` + "metrics": { + "prometheus": { + "go_gc_duration_seconds": { + "name": "go_gc_duration_seconds", + "help": "A summary of the pause duration of garbage collection cycles.", + "type": "SUMMARY", + "metric": [ + { + "summary": { + "sampleCount": "1", + "sampleSum": 4.1765e-05, + "quantile": [ + { + "quantile": 0, + "value": 4.1765e-05 + }, + { + "quantile": 0.25, + "value": 4.1765e-05 + }, + { + "quantile": 0.5, + "value": 4.1765e-05 + }, + { + "quantile": 0.75, + "value": 4.1765e-05 + }, + { + "quantile": 1, + "value": 4.1765e-05 + } + ] + } + } + ] + }, +``` + +Note that `sample_count` is now `sampleCount`, and the `type` is using the enum's +string representation, `"SUMMARY"`, not `2`. + +Note: For compatibility reasons (the Prometheus golang client doesn't use the V2 +protobuf API), this change uses `jsonpb` and not `protojson`. + +## 0.37.2 + +This is a bugfix release addressing two bugs: + +1. A regression introduced in the formatter fix for CVE-2022-23628. +2. Support indices for appending to an array, conforming to JSON Patch (RFC6902) + for patch bundles. + +### Miscellaneous + +- format: generated vars may have a proper location +- storage: Support index for array appends + +## 0.37.1 + +This is a bug fix release that reverts the github.com/prometheus/client_golang +upgrade in v0.37.0. The upgrade exposed an issue in the serialization of Go +runtime metrics in the Status API +([#4319](https://github.com/open-policy-agent/opa/issues/4319)). + +### Miscellaneous + +- Revert "build(deps): bump github.com/prometheus/client_golang (#4307)" + +## 0.37.0 + +This release contains a number of fixes and enhancements. + +This is the first release that includes a binary and a docker image for +`linux/arm64`, `opa_linux_arm64_static` and `openpolicyagent/opa:0.37.0-static`. +Thanks to @ngraef for contributing the build changes necessary. + +### Strict Mode + +There have been numerous possible checks in the compiler that fall into this category: + +1. They would help avoid common mistakes; **but** +2. Introducing them would potentially break some uncommon, but legitimate use. + +We've thus far refrained from introducing them. **Now**, a new "strict mode" +allows you to opt-in to these checks, and we encourage you to do so! + +With *OPA 1.0*, they will become the new default behaviour. + +For more details, [see the docs on _Compiler Strict Mode_](https://www.openpolicyagent.org/docs/v0.37.0/strict/). + +### Delta Bundles + +Delta bundles provide a more efficient way to make data changes by containing +*patches to data* instead of snapshots. +Using them together with [HTTP Long Polling](https://www.openpolicyagent.org/docs/v0.37.0/management-bundles/#http-long-polling), +you can propagate small changes to bundles without waiting for polling delays. + +See [the documentation](https://www.openpolicyagent.org/docs/v0.37.0/management-bundles/#delta-bundles) +for more details. + + +### Tooling and Runtime + +- Bundles bug fix: Roundtrip manifest before hashing to allow changing the manifest + and still using signature verification of bundles ([#4233](https://github.com/open-policy-agent/opa/issues/4233)), + reported by @CristianJena + +- The test runner now also supports custom builtins, when invoked through the Golang + interface (authored by @MIA-Deltat1995) + +- The compile package and the `opa build` command support a new output format: "plan". + It represents a _query plan_, steps needed to take to evaluate a query (with policies). + The plan format is a JSON encoding of the intermediate representation (IR) used for + compiling queries and policies into Wasm. + + When calling `opa build -t plan ...`, the plan can be found in `plan.json` at the top- + level directory of the resulting bundle.tar.gz. + [See the documentation for details.](https://www.openpolicyagent.org/docs/v0.37.0/ir/). + +- Compiler+Bundles: Metadata to be added to a bundle's manifest can now be provided via `WithMetadata` + ([#4289](https://github.com/open-policy-agent/opa/issues/4289)), authored by @marensws, reported by @johanneslarsson +- Plugins: failures in auth plugin resolution are now output, previously panicked, authored by @jcchavezs +- Plugins: Fix error when initializing empty decision logging or status plugin ([#4291](https://github.com/open-policy-agent/opa/issues/4291)) +- Bundles: Persisted bundle activation failures are treated like failures with + non-persisted bundles ([#3840](https://github.com/open-policy-agent/opa/issues/3840)), reported by @dsoguet +- Server: `http.send` caching now works in system policy `system.authz` ([#3946](https://github.com/open-policy-agent/opa/issues/3946)), + reported by @amrap030. +- Runtime: Apply credentials masking on `opa.runtime().config` ([#4159](https://github.com/open-policy-agent/opa/issues/4159)) +- `opa test`: removing deprecated code for `--show-failure-line` (`-l`), authored by @damienjburks +- `opa eval`: add description to all output formats +- `opa inspect`: unhide command for [bundle inspection](https://www.openpolicyagent.org/docs/v0.37.0/cli/#opa-inspect) + +### Rego and Topdown + +Built-in function enhancements and fixes: + +- `object.union_n`: New built-in for creating the union of more than two objects ([#4012](https://github.com/open-policy-agent/opa/issues/4012)), + reported by @eliw00d +- `graph.reachable_paths`: New built-in to calculate the set of reachable paths in a graph (authored by @justinlindh-wf) +- `indexof_n`: New built-in function to get all the indexes of a specific substring (or character) from a string (authored by @shuheiktgw) +- `indexof`: Improved performance (authored by @shuheiktgw) +- `object.get`: Support nested key array for deeper lookups with default (authored by @charlieegan3) +- `json.is_valid`: Use Golang's `json.Valid` to avoid unnecessary allocations (authored by @kristiansvalland) + +Strict-mode features: + +- Add _duplicate imports_ check ([#2698](https://github.com/open-policy-agent/opa/issues/2698)) reported by @mikol +- _Deprecate_ `any()` and `all()` built-in functions ([#2437](https://github.com/open-policy-agent/opa/issues/2437)) +- Make `input` and `data` reserved keywords ([#2600](https://github.com/open-policy-agent/opa/issues/2600)) reported by @jpeach +- Add _unused local assignment_ check ([#2514](https://github.com/open-policy-agent/opa/issues/2514)) + + +Miscellaneous fixes and enhancements: + +- `format`: don't group iterable when one has defaulted location +- `topdown`: ability to retrieve input and plug bindings in the `Event`, authored by @istalker2 +- `print()` built-in: fix bug when used with `with` modifier and a function call value ([#4227](https://github.com/open-policy-agent/opa/issues/4227)) +- `ast`: don't error when future keyword import is redundant during parsing + +### Documentation + +- A [new "CLI" docs section](https://www.openpolicyagent.org/docs/v0.37.0/cli/) describes the various + OPA CLI commands and their arguments ([#3915](https://github.com/open-policy-agent/opa/issues/3915)) +- Policy Testing: Add reference to rule indexing in the context of test code coverage + ([#4170](https://github.com/open-policy-agent/opa/issues/4170)), reported by @ekcs +- Management: Add hint that S3 regional endpoint should be used with bundles (authored by @danoliver1) +- Many broken links were fixed, thanks to @phelewski +- Fix rendering of details: add detail-tab for collapsable markdown (authored by @bugg123) + +### WebAssembly + +- Add native support for `json.is_valid` built-in function + ([#4140](https://github.com/open-policy-agent/opa/issues/4140)), authored by @kristiansvalland +- Dependencies: bump wasmtime-go from 0.32.0 to 0.33.1 + +### Miscellaneous + +- Publish multi-arch image manifest lists including linux/arm64 ([#2233](https://github.com/open-policy-agent/opa/issues/2233)), + authored by @ngraef, reported by @povilasv +- `logging`: Remove logger `GetFields` function ([#4114](https://github.com/open-policy-agent/opa/issues/4114)), + authored by @viovanov +- Website: add versioned docs for latest version, so when 0.37.0 is released, both + https://www.openpolicyagent.org/docs/v0.37.0/ and https://www.openpolicyagent.org/docs/latest + contain docs, and 0.37.0 can already be used for stable links to versioned docs pages. +- Community: Initial draft of the community badges program +- `make test`: fix "too many open files" issue on Mac OS +- Various dependency bumps + +## 0.36.1 + +This release includes a number of documentation fixes. +It also includes the experimental binary for darwin/arm64. + +There are no code changes. + +### Documentation + +- OpenTelemetry: fix configuration example, authored by @rvalkenaers +- Configuration: fix typo for `tls-cert-refresh-period`, authored by @mattmahn +- SSH and Sudo authorization: Add missing filename +- Integration: fix example policy + +### Release + +- Build darwin/arm64 in post tag workflow + +## 0.36.0 + +This release contains a number of fixes and enhancements. + +### OpenTelemetry and opa exec + +This release adds OpenTelemetry support to OPA. This makes it possible to emit spans to an OpenTelemetry collector via +gRPC on both incoming and outgoing (i.e. http.send) calls in the server. See the updated docs on +[monitoring](https://www.openpolicyagent.org/docs/latest/monitoring/) for more information and configuration options +([#1469](https://github.com/open-policy-agent/opa/issues/1469)) authored by @[rvalkenaers](https://github.com/rvalkenaers) + +This release also adds a new `opa exec` command for doing one-off evaluations of policy against input similar to +`opa eval`, but using the full capabilities of the server (config file, plugins, etc). This is particularly useful in +contexts such as CI/CD or when enforcing policy for infrastructure as code, where one might want to run OPA with remote +bundles and decision logs but without having a running server. See the updated docs on +[Terraform](https://www.openpolicyagent.org/docs/latest/terraform/) for an example use case. +([#3525](https://github.com/open-policy-agent/opa/issues/3525)) + +### Built-in Functions + +- Four new functions for working with HMAC (`crypto.hmac.md5`, `crypto.hmac.sha1`, `crypto.hmac.sha256`, and `crypto.hmac.sha512`) was added ([#1740](https://github.com/open-policy-agent/opa/issues/1740)) reported by @[jshaw86](https://github.com/jshaw86) +- `array.reverse(array)` and `strings.reverse(string)` was added for reversing arrays and strings ([#3736](https://github.com/open-policy-agent/opa/issues/3736)) authored by @[kristiansvalland](https://github.com/kristiansvalland) and @[olamiko](https://github.com/olamiko) +- The `http.send` built-in function now uses a metric for counting inter-query cache hits ([#4023](https://github.com/open-policy-agent/opa/issues/4023)) authored by @[mirayadav](https://github.com/mirayadav) +- An overflow issue with dates very far in the future has been fixed in the `time.*` built-in functions ([#4098](https://github.com/open-policy-agent/opa/issues/4098)) reported by @[morgante](https://github.com/morgante) + +### Tooling + +- A problem with future keyword import of `in` was fixed for `opa fmt` ([#4111](https://github.com/open-policy-agent/opa/issues/4111)) reported by @[keshavprasadms](https://github.com/keshavprasadms) +- An issue with `opa fmt` when refs contained operators was fixed (authored by @[jaspervdj-luminal](https://github.com/jaspervdj-luminal)) +- Fix file renaming check in optimization using `opa build` (authored by @[davidmarne-wf](https://github.com/davidmarne-wf)) +- The `allow_net` capability was added, allowing setting limits on what hosts can be reached in built-ins like `http.send` and `net.lookup_ip_addr` ([#3665](https://github.com/open-policy-agent/opa/issues/3665)) + +### Server + +- A new credential provider for AWS credential files was added ([#2786](https://github.com/open-policy-agent/opa/issues/2786)) reported by @[rgueldem](https://github.com/rgueldem) +- The new `--tls-cert-refresh-period` flag can now be provided to `opa run`. If used with a positive duration, such as "5m" (5 minutes), + "24h", etc, the server will track the certificate and key files' contents. When their content changes, the certificates will be + reloaded ([#2500](https://github.com/open-policy-agent/opa/issues/2500)) reported by @[patoarvizu](https://github.com/patoarvizu) +- A new `v1/status` endpoint was added, providing the same data as the status plugin would send to a remote endpoint ([#4089](https://github.com/open-policy-agent/opa/issues/4089)) +- The HTTP router of OPA is now exposed to the plugin manager ([#2777](https://github.com/open-policy-agent/opa/issues/2777)) authored by @[bhoriuchi](https://github.com/bhoriuchi) reported by @[mneil](https://github.com/mneil) +- Calling `print` now works in decision masking policies +- An unintended switch between long/regular polling on 304 HTTP status was fixed ([#3923](https://github.com/open-policy-agent/opa/issues/3923)) authored by @[floriangasc](https://github.com/floriangasc) +- The error message about prohibited config in the discovery plugin has been improved +- The discovery plugin no longer panics in Trigger() if downloader is nil +- The bundle plugin now ignores service errors for file:// resources +- The bundle plugin file loader was updated to support directories +- A timer to HTTP request was added to the downloader +- The requested_by field in the logging plugin is now optional + +### Rego + +- The error message raised when using `-` with a number and a set is now more specific (as opposed to the correct usage with two sets, or two numbers) ([#1643](https://github.com/open-policy-agent/opa/issues/1643)) +- Fixed an edge case when using print and arrays in unification ([#4078](https://github.com/open-policy-agent/opa/issues/4078)) +- Improved performance of some array operations by caching an array's groundness bit ([#3679](https://github.com/open-policy-agent/opa/issues/3679)) +- ⚠️ Stricter check of arity in undefined function stage ([#4054](https://github.com/open-policy-agent/opa/issues/4054)). + This change will fail evaluation in some unusual cases where it previously would succeed, but these policies should be very uncommon. + + An example policy that previously would succeed but no longer will (wrong arity): + +```rego +package policy + +default p = false +p { + x := is_blue() + input.bar[x] +} + +is_blue(fruit) = y { # doesn't use fruit + y := input.foo +} +``` + +### SDK + +- The `opa.runtime()` built-in is now made available to the SDK ([#4050](https://github.com/open-policy-agent/opa/issues/4050) authored by @[oren-zohar](https://github.com/oren-zohar) and @[cmschuetz](https://github.com/cmschuetz) +- Plugins are now exposed on the SDK object +- The SDK now supports graceful shutdown ([#3980](https://github.com/open-policy-agent/opa/issues/3980)) reported by @[brianchhun-chime](https://github.com/brianchhun-chime) +- `print` output is now sent to the configured logger + +### Website and Documentation + +- All pages in the docs now have a feedback button ([#3664](https://github.com/open-policy-agent/opa/issues/3664)) authored by @[alan-ma](https://github.com/alan-ma) +- The Kafka docs have been updated to use the new Kafka plugin, and to use the OPA management APIs +- The Terraform tutorial was updated to use `opa exec` ([#3965](https://github.com/open-policy-agent/opa/issues/3965)) +- The docs on Contributing as well as the Vendor Guidelines have been updated +- The term "whitelist" has been replaced by "allowlist" across the docs +- A simple destructuring assignment example was added to the docs +- The docs have been reviewed on the use of assignment, equality and comparison operators, to make sure they follow best practice + +### CI + +- SHA256 checksums of CI builds now published to release directory ([#3448](https://github.com/open-policy-agent/opa/issues/3448)) authored by @[johanneslarsson](https://github.com/johanneslarsson) reported by @[raesene](https://github.com/raesene) +- golangci-lint upgraded to v1.43.0 (authored by @[shuheiktgw](https://github.com/shuheiktgw)) +- The build now creates an executable for darwin/arm64. This should work as expected, but is currently tested in the CI pipeline like the other binaries +- PRs targeting the [ecosystem](https://www.openpolicyagent.org/docs/latest/ecosystem/) page are now checked for mistakes using Rego policies + +## 0.35.0 + +This release contains a number of fixes and enhancements. + +### Early Exit Optimization + +This release adds an early exit optimization to the evaluator. With this optimization, the evaluator stops evaluating rules when an answer has been found and subsequent evaluation would not yield any new answers. The optimization is automatically applied to complete rules and functions that meet specific requirements. For more information see the [Early Exit in Rule Evaluation](https://www.openpolicyagent.org/docs/latest/policy-performance/#early-exit-in-rule-evaluation) section in the docs. [#2092](https://github.com/open-policy-agent/opa/issues/2092) + +### Built-in Functions + +- The `net.lookup_ip_addr` function was added to allow policies to resolve hostnames to IPv4/IPv6 addresses ([#3993](https://github.com/open-policy-agent/opa/issues/3993)) +- The `http.send` function has been improved to close TCP connections quickly after receiving the HTTP response and avoid creating HTTP clients unnecessarily when a cached response exists ([#4015](https://github.com/open-policy-agent/opa/issues/4015)). This change reduces the number of open file descriptors required in high-throughput environments and prevents OPA from encountering ulimit errors. + +### Rego + +- `print()` calls in the head of rules no longer cause runtime errors ([#3967](https://github.com/open-policy-agent/opa/issues/3967)) +- Type errors for calls to undefined functions no longer contain rewritten variable names ([#4031](https://github.com/open-policy-agent/opa/issues/4031)) +- The `rego.SkipPartialNamespace` option now correctly sets the flag on the partial evaluation queries (previously it would always set the value to `true`) ([#3996](https://github.com/open-policy-agent/opa/issues/3996)) authored by @[thomascoquet](https://github.com/thomascoquet) +- The internal set implementation has been updated to insert elements in sorted order rather than lazily sorting during comparisons. +- Fixed `import` alias parsing bug identified by fuzzer ([#3988](https://github.com/open-policy-agent/opa/issues/3988)) + +### WebAssembly + +- The Golang SDK will now issue a `grow()` call if the `input` document exceeds the available memory space. +- The `malloc()` implementation will now call `opa_abort` if the `grow()` call fails. + +### Server + +- The decision logger adapts upload chunk sizes based on previous outputs. This allows the decision loggger to encode significantly more decisions into each upload chunk, thereby reducing heap usage for buffered decisions. For more information on the adapative chunking behaviour, see the [Decision Logs](https://www.openpolicyagent.org/docs/latest/management-decision-logs/) page in the docs. +- The decision logger can be configured to send records to a custom plugin as well as an HTTP endpoint at the same time ([#4013](https://github.com/open-policy-agent/opa/issues/4013)) +- `print()` calls from the `system.authz` policy are now included in the logs ([#4048](https://github.com/open-policy-agent/opa/issues/4048)) +- OPA can use an [Azure Managed Identities Token](https://www.openpolicyagent.org/docs/latest/configuration/#azure-managed-identities-token) to authenticate with control plane services ([#3916](https://github.com/open-policy-agent/opa/issues/3916)) authored by @[Scowluga](https://github.com/Scowluga). +- The logging configuration will be correctly applied to service clients so that DEBUG logs are surfaced ([#4071](https://github.com/open-policy-agent/opa/issues/4071)) + +### Tooling + +- The `opa fmt` command will not generate a line-break when there are generated variables in a function call ([#4018](https://github.com/open-policy-agent/opa/issues/4018)) reported by @[torsrex](https://github.com/torsrex) +- The `opa inspect` command no longer prints a blank namespace when a data.json file is included at the root ([#4022](https://github.com/open-policy-agent/opa/issues/4022)) +- The `opa build` command will output debug messages if an optimized entrypoint is discarded. + +### Website and Documentation + +- The website has been updated to build with Hugo 0.88.1 ([#3787](https://github.com/open-policy-agent/opa/issues/3787)) +- The version picker in the documentation is now scrollable ([#3955](https://github.com/open-policy-agent/opa/issues/3955)) authored by @[orweis](https://github.com/orweis) +- The description of the `urlquery` built-in functions have been clarified ([#1592](https://github.com/open-policy-agent/opa/issues/1592)) reported by @[klarose](https://github.com/klarose) +- The decision logger documentation has been improved to cover controls for large-scale environments ([#3976](https://github.com/open-policy-agent/opa/issues/3976)) +- The "strict built-in errors" mode is now covered in the docs along with built-in function error behaviour ([#3686](https://github.com/open-policy-agent/opa/issues/3686)) +- The OAuth2 and OIDC examples around key rotation and caching have been improved + +### CI + +- Issues and PRs that have not seen activity in 30 days will be automatically marked as "inactive" +- The `Makefile` can now produce Docker images for other architectures. We do not yet publish binaries or images for non-amd64 architectures however if you want to build OPA yourself, the `Makefile` does not prohibit it. + +### Backwards Compatibility + +- The diagnostics buffer in the OPA server has been completely removed as part of the deprecation and removal of the diagnostic feature ([#1052](https://github.com/open-policy-agent/opa/issues/1052)) + +## 0.34.2 + +### Fixes + +- ast: Fix print call rewriting for calls in head ([#3967](https://github.com/open-policy-agent/opa/issues/3967)) + +## 0.34.1 + +### Fixes + +- runtime: Fix logging configuration (#3959) ([#3958](https://github.com/open-policy-agent/opa/issues/3958)) + +## 0.34.0 + +This release includes a number of enhancements and fixes. In particular, this +release adds a new keyword for membership and iteration (`in`) and a specialized +built-in function (`print`) for debugging. + +### The `in` operator + +This release adds a new `in` operator that provides syntactic sugar for +references that perform membership tests or iteration on collections (i.e., +arrays, sets, and objects.) The following table shows common patterns for arrays +with the old and new syntax: + +Pattern | Existing Syntax | New Syntax +--- | --- | --- +Check if 7 exists in array | `7 == arr[_]` | `7 in arr` +Check if 7 does not exist in array | n/a (requires helper rule) | `not 7 in arr` +Iterate over the elements of array | `x := arr[_]` | `some x in arr` + +For more information on the `in` operator see [Membership and iteration: +`in`](https://www.openpolicyagent.org/docs/edge/policy-language/#membership-and-iteration-in) +in the docs. + +### The `print` function + +This release adds a new `print` function for debugging purposes. The `print` +function can be used to output any value inside of the policy. The `print` +function has special handling for _undefined_ values so that execution does not +stop if any of the operands are undefined. Instead, a special marker is emitted +in the output. For example: + +```rego +package example + +default allow = false + +allow { + print("the subject's username is:", input.subject.username) + input.subject.username == "admin" +} +``` + +Given the policy above, we can see the output of the `print` function via STDERR when using `opa eval`: + +```bash +echo '{"subject": {"username": "admin"}}' | opa eval -d policy.rego -I -f pretty 'data.example.allow' +``` + +Output: + +``` +the subject's username is: admin +true +``` + +If the username, subject, or entire input document was undefined, the `print` function will still execute: + +```bash +echo '{}' | opa eval -d policy.rego -I -f pretty 'data.example.allow' +``` + +Output: + +``` +the subject's username is: +false +``` + +The `print` function is integrated into the `opa` subcommands, REPL, server, VS +Code extension, and the playground. Library users must opt-in to `print` +statements. For more information see the +[Debugging](https://www.openpolicyagent.org/docs/edge/policy-reference/#debugging) +section in the docs. + +### Enhancements + +- SDK: Allow map of plugins to be passed to SDK ([#3826](https://github.com/open-policy-agent/opa/issues/3826)) authored by @[edpaget](https://github.com/edpaget) +- `opa test`: Change exit status when tests are skipped ([#3773](https://github.com/open-policy-agent/opa/issues/3773)) authored by @[kirk-patton](https://github.com/kirk-patton) +- Bundles: Improve loading performance ([#3860](https://github.com/open-policy-agent/opa/issues/3860)) authored by @[0xAP](https://github.com/0xAP) +- `opa fmt`: Keep new lines in between function arguments ([#3836](https://github.com/open-policy-agent/opa/issues/3836)) reported by @[anbrsap](https://github.com/anbrsap) +- `opa inspect`: Add experimental subcommand for bundle inspection ([#3754](https://github.com/open-policy-agent/opa/issues/3754)) + +### Fixes + +- Bundles/API: When deleting a policy, the check determining if it's bundle-owned was using the path prefix, which would yield false positives under certain circumstances. + It now checks the path properly, piece-by-piece. ([#3863](https://github.com/open-policy-agent/opa/issues/3863) authored by @[edpaget](https://github.com/edpaget) +- CLI: Using `--set` with null value _again_ translates to empty object ([#3846](https://github.com/open-policy-agent/opa/issues/3846)) +- Rego: Forbid dynamic recursion with hidden (`system.*`) document ([#3876](https://github.com/open-policy-agent/opa/issues/3876) +- Rego: Raise conflict errors in functions when output not captured ([#3912](https://github.com/open-policy-agent/opa/issues/3912)) + + This change has the potential to break policies that previously evaluated successfully! + See _Backwards Compatibility_ notes below for details. +- Experimental disk storage: React to "txn too big" errors ([#3879](https://github.com/open-policy-agent/opa/issues/3879)), reported and authored by @[floriangasc](https://github.com/floriangasc) + +### Documentation + +- Kubernetes and Istio: Update tutorials for recent Kubernetes versions ([#3910](https://github.com/open-policy-agent/opa/issues/3910)) authored by @[olamiko](https://github.com/olamiko) +- Deployment: Add section about Capabilities ([#3769](https://github.com/open-policy-agent/opa/issues/3769)) +- Built-in functions: Add warning to `http.send` and extension docs about side-effects in other systems (#3922) ([#3893](https://github.com/open-policy-agent/opa/issues/3893)) +- Docker Authorization: The tutorial now uses a Bundles API server. +- SDK: An example of SDK use is provided. + +### Miscellaneous + +- Runtime: Refactor logger usage -- see below for *Backwards Compatibility* notes. +- Wasm: fix an issue with undefined, plain `input` references ([#3891](https://github.com/open-policy-agent/opa/issues/3891)) +- test/e2e: Extend TestRuntime to avoid global fixture +- types: Fix Arity function to return zero when type is known (#3932) +- Wasm/builder: bump LLVM to 13.0.0, latest versions of wabt and binaryen (#3908) +- Wasm: deal with importing memory in the compiler (#3763) + +### Backwards Compatibility + +* Function return values need to be well-defined: for a single input `x`, the function's + output `f(x)` can only be one value. When evaluating policies, this condition had not + been ensured for function calls that don't make use of their values, like + + ```rego + package p + r { + f(1) + } + f(_) = true + f(_) = false + ``` + + Before, `data.p.r` evaluated to `true`. Now, it will (correctly) return an error: + + eval_conflict_error: functions must not produce multiple outputs for same inputs + + In more realistic settings, this can be encountered when true/false return values + are captured and returned where they don't need to be: + + ```rego + package p + r { + f("any", "baz") + } + f(path, _) = r { + r := path == "any" + } + f(path, x) = r { + r := glob.match(path, ["/"], x) + } + ``` + + In this example, any function input containing `"any"` would make the function yield + two different results: + + 1. The first function body returns `true`, matching the `"any"` argument. + 2. The second function body returns the result of the `glob.match` call -- `false`. + + The fix here would be to _not_ capture the return value in the function bodies: + + ```rego + f(path, _) { + path == "any" + } + f(path, x) { + glob.match(path, ["/"], x) + } + ``` + +* The `github.com/open-policy-agent/opa/runtime#NewLoggingHandler` function now + requires a logger instance. Requiring the logger avoids the need for the + logging handler to depend on the global logrus logger (which is useful for + test purposes.) This change is unlikely to affect users. + +## 0.33.1 + +This is a bugfix release addressing an issue in the formatting of rego code that contains +object literals. With the last release, those objects would under some conditions have their +keys re-ordered, with some of them put into a single line. + +Thanks to @[iainmcgin](https://github.com/iainmcgin) for reporting. + +### Fixes + +- format: make groupIterable sort by row ([#3849](https://github.com/open-policy-agent/opa/issues/3849)) + +## 0.33.0 + +This release includes a number of improvements and fixes. + +### Built-in Functions + +This release introduces `crypto.x509.parse_rsa_private_key` so that policy authors can decode RSA private keys and structure them as JWKs ([#3765](https://github.com/open-policy-agent/opa/issues/3765)). Authored by @[cris-he](https://github.com/cris-he). + +### Fixes + +- Fix object comparison to avoid sorting keys in-place. This prevents the interpreter from generating non-deterministic results when values are inserted into the partial set memoization cache. ([#3819](https://github.com/open-policy-agent/opa/issues/3819)) +- Fix data races in `ast` package caused by sorting `types.Any` instances in-place and shallow-copying module comments when a deep-copy should be performed ([#3793](https://github.com/open-policy-agent/opa/issues/3793)). Reported by @[markushinz](https://github.com/markushinz). +- Fix "file name too long" error caused by bundle loader treating PEM encoded private keys as file paths ([#3766](https://github.com/open-policy-agent/opa/issues/3766)) +- Fix plugins to support manual triggering mode when discovery is disabled ([#3797](https://github.com/open-policy-agent/opa/issues/3797)) + +### Server & Tooling + +- The server now supports policy-based health checks that can inspect the state of plugins and other internal components ([#3759](https://github.com/open-policy-agent/opa/issues/3759)) authored by @[gshively11](https://github.com/gshively11) +- The bundle reader now loads files lazily to avoid hitting file descriptor limits ([#3777](https://github.com/open-policy-agent/opa/issues/3777)). Authored by @[bhoriuchi](https://github.com/bhoriuchi) +- The `opa eval` sub-command supports a `--timeout` option for limiting how long evaluation can run. + +### Rego + +- The type checker now supports variadic arguments on void functions. This change paves the way for `print()` support as well as variadic arguments on all functions. +- The parser now memoizes term parsing. This prevents non-linear runtime for large nested objects and sets. + +### CI & Dependencies + +- Fix spurious build errors in wasm library. +- Update wasmtime dependency to v0.30.0. +- Run PR checks on macOS in addition to Linux ([#3176](https://github.com/open-policy-agent/opa/issues/3176)). + +### Documentation + +- Update the Kubernetes and Envoy (standalone) tutorials to show how the OPA management APIs can be used to distribute policies. + +### Backwards Compatibility + +* The `github.com/open-policy-agent/opa/ast#ArgErrDetail` struct has been + modified to use the new `types.FuncArgs` struct to represent the required + arguments. Callers that depend on the exact structure of the error details + must update to use the `types.FuncArgs` struct. + +## 0.32.1 + +This is a bugfix release to address a problem related to mismatching checksums in the official go mod proxy. +As a consequence, users with code depending on the OPA Go module that bypassed the proxy would see an error like + + go get github.com/google/flatbuffers/go: github.com/google/flatbuffers@v1.12.0: verifying module: checksum mismatch + downloaded: h1:N8EguYFm2wwdpoNcpchQY0tPs85vOJkboFb2dPxmixo= + sum.golang.org: h1:/PtAHvnBY4Kqnx/xCQ3OIV9uYcSFGScBsWI3Oogeh6w= + +**Be aware** that Github's Dependabot feature makes use of that check, and will start to _fail_ for projects using the OPA Go module version 0.32.0. + +There workaround applied to OPA is to replace to flatbuffers dependency's version manually. + +For more information, see +- https://github.com/google/flatbuffers/issues/6466: The issue has been discussed upstream, and a 1.12.1 release has been published to address it. +- https://github.com/dgraph-io/badger/pull/1746: OPA transitively depends on the flatbuffer package because of badger. + +There are *no functional changes* in this bugfix release. +If you use the container images, or the published binaries, of OPA 0.32.0, you are **not affected** by this. + +Many thanks to [James Alseth](https://github.com/jalseth) for triaging this, and engaging with upstream to fix this. + +## 0.32.0 + +This release includes a number of improvements and fixes. + +### 💾 Disk-based Storage (Experimental) + +This release adds a disk-based storage implementation to OPA. The implementation can be found in [github.com/open-policy-agent/storage/disk](https://pkg.go.dev/github.com/open-policy-agent/opa/storage/disk). There is also an example in the [`rego` package](https://pkg.go.dev/github.com/open-policy-agent/opa/rego#pkg-examples) that shows how policies can be evaluated with the disk-based store. The disk-based store is currently only available as a library (i.e., it is not integrated into the rest of OPA yet.) In the next few releases, we are planning to integrate the implementation into the OPA server and provide tooling to help leverage the disk-based store. + +### Built-in Functions + +This release includes a few improvements to existing built-in functions: + +- The `http.send` function now supports UNIX domain sockets ([#3661](https://github.com/open-policy-agent/opa/issues/3661)) authored by @[kirk-patton](https://github.com/kirk-patton) +- The `units.parse_bytes` function now supports E* and P* units ([#2911](https://github.com/open-policy-agent/opa/issues/2911)) +- The `io.jwt.encode_sign` function uses the built-in context randomization source (which is helpful for replay purposes) + +### Server + +This release includes multiple improvements for OPA server deployments in serverless environments: + +- Plugins can now be triggered manually within OPA. This feature allows users extending and customizing OPA to control exactly when operations like bundle downloads and decision log uploads occur. The built-in plugins now include a `trigger` configuration that can be set to `manual` or `periodic` (which is the default). When `manual` triggering is enabled, the plugins WILL NOT perform any periodic/background operations. Instead, the plugins will only execute when the [`Trigger`](https://github.com/open-policy-agent/opa/blob/main/plugins/plugins.go#L101) API is invoked. +- Plugins can now wait for server initialization. When runtime initialization is finished, plugins can be notified. This allows plugins to synchronize their behaviour with server startup. [#3701](https://github.com/open-policy-agent/opa/issues/3701) authored by @[gshively11](https://github.com/gshively11). +- The [Health API](https://www.openpolicyagent.org/docs/latest/rest-api/#health-api) now supports an `exclude-plugin` parameter to control which plugins are checked. [#3713](https://github.com/open-policy-agent/opa/issues/3713) authored by @[gshively11](https://github.com/gshively11). + +### Tooling + +- The compiler no longer fetches remote schemas by default when used as as library. Capabilities have been updated to include an `allow_net` field to control whether network operations can be performed ([#3746](https://github.com/open-policy-agent/opa/issues/3746)). This field is only used to control schema fetching today. In future versions of OPA, the `allow_net` parameter will be used to control other behaviour like `http.send`. +- The `WebAssembly runtime not supported` error message has been improved [#3739](https://github.com/open-policy-agent/opa/pull/3739). + +### Rego + +- Added support for `anyOf` and `allOf` keywords in JSON schema support in the type checker ([#3592](https://github.com/open-policy-agent/opa/issues/3592)) authored by [@jchen10500](https://github.com/jchen10500) and [@juliafriedman8](https://github.com/juliafriedman8). +- Added support for custom JSON result marshalling in the `rego` package. +- Added a new convenience function (`Allowed() bool`) to the `rego.ResultSet` API. +- Improved string-representation construction performance for arrays, sets, and objects. +- Improved the topdown evaluator to support `ast.Value` results from the store so that unnecessary conversions can be avoided. +- Improved the `rego` package to make the wasmtime-go dependency optional at build-time ([#3545](https://github.com/open-policy-agent/opa/issues/3545)). +- Fixed a bug in the comprehension indexer whereby index keys were not constructed correctly leading to incorrect outputs ([#3579](https://github.com/open-policy-agent/opa/issues/3579)). +- Fixed a stack overflow during partial evaluation due to incorrect term rewriting in the copy propagation implementation ([#3071](https://github.com/open-policy-agent/opa/issues/3071)). +- Fixed a bug in partial evaluation when shallow inlinign is enabled that resulted in built-in functions being invoked instead of saved ([#3681](https://github.com/open-policy-agent/opa/issues/3681)). + +### WebAssembly + +- The internal Wasm SDK now supports the inter-query built-in cache. +- The pre-compiled runtime is now built with llvm 12.0.1 and the builder image includes clang-format. +- The internal Wasm SDK has been updated to use wasmtime-go v0.29.0. + +### Documentation + +This release includes a number of documentation improvements: + +- The wasm `opa_eval` arguments have been clarified [#3699](https://github.com/open-policy-agent/opa/issues/3696) +- The contributing and development guide have been moved into a dedicated [Contributing](https://www.openpolicyagent.org/docs/latest/contributing/) section on the website [#3751](https://github.com/open-policy-agent/opa/issues/3751) +- The Envoy standalone tutorial includes cleanup steps now (thanks [@princespaghetti](https://github.com/princespaghetti)) +- Various typos have been fixed by multiple folks (thanks [@Tej-Singh-Rana](https://github.com/Tej-Singh-Rana) [@gujun4990](https://github.com/gujun4990)) +- The Kubernetes ingress validation tutorial has been updated to include new mandatory attributes and newer API versions (thanks [@ereslibre](https://github.com/ereslibre)) +- The recommendations around using OPA Gatekeeper have been improved. + +### Infrastructure + +- OPA is now built with Go v1.17 and CI jobs have been added to ensure OPA builds with older versions of Go. + +### Backwards Compatibility + +The `rego` package no longer relies on build constraints to enable the Wasm runtime. Instead, library users must opt-in to Wasm runtime support by adding an import statement in the Go code: + +```go +import _ "github.com/open-policy-agent/opa/features/wasm" +``` + +This change ensures that (by default) the wasmtime-go blobs are not vendored in projects that embed OPA as a library. If you are currently relying on the Wasm runtime support in the `rego` package (via the `rego.Target("wasm")` option), please update you code to include the import above. See [#3545](https://github.com/open-policy-agent/opa/issues/3545) for more details. + +## 0.31.0 + +This release contains **performance improvements** for evaluating partial sets and objects, +and introduces a new ABI call to OPA's Wasm modules to speed up Wasm evaluations. + +It also comes with an improvement for checking policies -- unsafe declared variables are now caught at compile time. +This means that **some policies** that have been working fine with previous versions, because their unsafe variables +had not ever been queried, will fail to compile with OPA 0.31.0. +See below for details and what to do about that. + +### Spotlights + +#### Partial Sets and Objects Performance + +Resolving an issue ([#822](https://github.com/open-policy-agent/opa/issues/822)) created on July 4th 2018, +OPA can now cache the results of partial sets and partial objects. + +A benchmark that accesses a partial set of increasing size _twice_ shows a saving of more than 50%: + + name old time/op new time/op delta + PartialRuleSetIteration/10-16 230µs ±10% 101µs ± 3% -56.10% (p=0.000 n=10+10) + PartialRuleSetIteration/100-16 13.4ms ± 9% 5.5ms ± 9% -58.74% (p=0.000 n=10+9) + PartialRuleSetIteration/1000-16 1.31s ±10% 0.51s ± 8% -61.12% (p=0.000 n=10+9) + + name old alloc/op new alloc/op delta + PartialRuleSetIteration/10-16 77.7kB ± 0% 35.8kB ± 0% -53.94% (p=0.000 n=10+10) + PartialRuleSetIteration/100-16 3.72MB ± 0% 1.29MB ± 0% -65.26% (p=0.000 n=10+10) + PartialRuleSetIteration/1000-16 365MB ± 0% 114MB ± 0% -68.86% (p=0.000 n=10+10) + + name old allocs/op new allocs/op delta + PartialRuleSetIteration/10-16 1.84k ± 0% 0.69k ± 0% -62.42% (p=0.000 n=10+10) + PartialRuleSetIteration/100-16 99.3k ± 0% 14.5k ± 0% -85.43% (p=0.000 n=10+9) + PartialRuleSetIteration/1000-16 10.0M ± 0% 1.0M ± 0% -89.58% (p=0.000 n=10+9) + +These numbers were gathered querying `fixture[i]; fixture[j]` with a policy of + +```rego +fixture[x] { + x := numbers.range(1, n)[_] +} +``` +where `n` is 10, 100, or 1000. + +There are multiple access patterns that are accounted for: if a _ground_ scalar is used to +access a previously not-cached partial rule, + +```rego +allow { + managers[input.user] # here +} + +managers[x] { + # some logic here +} +``` + +the evaluation algorithm will calculate the set membership of `input.user` _only_, and cache the result. + +If there is a query that requires evaluating the entire partial, however, the algorithm will also cache the entire partial: +```rego +allow { + some person + managers[person] + # more expressions +} + +managers[x] { + # some logic here +} +``` +thus avoiding extra evaluations later on. +The same is true if `managers` was used as a fully materialized set in an execution. + + +This also means that the question about whether to write + +```rego +q = { x | ... } # set comprehension +``` + +or + +```rego +q[x] { ... } # partial set rule +``` + +becomes much less important for policy evaluation performance. + +#### WebAssembly Performance + +OPA-generated Wasm modules have gotten a fast-path evaluation method: +By calling the one-off function + + opa_eval(reserved, entrypoint, data_addr, input_addr, input_len, format) + +which returns a pointer to the serialized result set (in JSON if format is 0, "value" format if 1), +the number of VM calls needed for evaluating a policy via Wasm is drastically reduced. + +The performance benefit is huge: + + name old time/op new time/op delta + WasmRego-16 84.3µs ± 6% 15.1µs ± 0% -82.07% (p=0.008 n=5+5) + +The added `opa_eval` export comes with an ABI bump to version 1.2. +See [#3627](https://github.com/open-policy-agent/opa/pull/3627) for all details. + +Along the same line, we've examined the processing of query evaluations that are Wasm-backed _through the `rego` package_. +This allowed us to avoid unneccessary work ([#3666](https://github.com/open-policy-agent/opa/issues/3666)). + + +#### Unsafe declared variables now cause a compile-time error + +Before this release, local variables that had been _declared_, i.e. introduced via the `some` keyword, had been able +to slip through the safety checks unnoticed. + +For example, a policy like + +```rego +package demo + +q { + input == "open sesame" +} + +p[x] { + some x +} +``` + +would have _not_ caused any error **if `data.demo.p` wasn't queried**. +Querying `data.demo.p` would return an "var requires evaluation" error. + +With this release, the erroneous rule no longer goes unnoticed, but is **caught at compile time**: "var x is unsafe". + +The most likely fix is to remove the rule with the unsafe variable, since it cannot have contributed to a successful +evaluation in previous OPA versions. + +See [#3580](https://github.com/open-policy-agent/opa/issues/3580) for details. + +### Topdown and Rego + +- New built-in function: `crypto.x509.parse_and_verify_certificates` ([#3601](https://github.com/open-policy-agent/opa/issues/3601)), authored by @[jalseth](https://github.com/jalseth) + + This function enables you to verify that there is a chain from a leaf certificate back to the trusted root. +- New built-in function: `rand.intn` generates a random number between `0` and `n` ([#3615](https://github.com/open-policy-agent/opa/issues/3615)), authored by @[base698](https://github.com/base698) + + The function takes a string argument to ensure that the same call, within one policy evaluation, returns the same random number. +- `http.send` enhancement: New `caching_mode` parameter to configure if deserialized or serialized response bodies should be cached ([#3599](https://github.com/open-policy-agent/opa/issues/3599)) +- Custom built-in function enhancement: let custom builtins halt evaluation ([#3534](https://github.com/open-policy-agent/opa/issues/3534)) +- Partial evaluation: Fix stack overflow on certain expressions ([#3559](https://github.com/open-policy-agent/opa/issues/3559)) + +### Tooling + +- Query Profiling: `opa eval --profile` now supports a `--count=#` flag to gather metrics and profiling data over multiple runs, and displays aggregate statistics for the results ([#3651](https://github.com/open-policy-agent/opa/issues/3651)). + + This allows you to gather more robust numbers to assess policy performance. + +- Docker images: Publish static image ([#3633](https://github.com/open-policy-agent/opa/issues/3633)) + + As of this release, you can use the staticly-built Linux binary from a docker image: `openpolicyagent/opa:0.31.0-static`. + It contains the same binary that has been published since release v0.29.4, statically linked to musl, with evaluating Wasm disabled. + +### Fixes + +- Built-in `http.send`: ignore `tls_use_system_certs` setting on Windows. Having this set to _true_ (the default as of v0.29.0) would _always_ return an error on Windows. +- The console decision logger is no longer tied to the general log level ([#3654](https://github.com/open-policy-agent/opa/issues/3654)) +- Update query compiler to reject empty queries ([#3625](https://github.com/open-policy-agent/opa/issues/3625)) +- Partial Evaluation fix: Don't generate comprehension with unsafe variables ([#3557](https://github.com/open-policy-agent/opa/issues/3557)) +- Parser: modules containing _only_ tabs and spaces no longer lead to a runtime panic. +- Wasm: ensure that the desired stack space for the C library calls (64KiB) is not reduced by data segments added in the compiler. + This is achieved by putting the stack first -- stack overflows now become "out of bounds" memory access traps. + Before, it would silently corrupt the static data. + +### Server and Runtime + +- New configuration for Management APIs: using `resource`, the request path for sending decision logs can be configured now ([#3618](https://github.com/open-policy-agent/opa/issues/3618)), authored by @[cbuto](https://github.com/cbuto) + + `/logs` is still the default, but can now be overridden. + With this change, the `partition_name` config becomes deprecated, since its functionality is subsumed by this new configurable. + +### Documentation + +- How to debug? Clarify how to access `Note` events for debugging via explanations ([#3628](https://github.com/open-policy-agent/opa/issues/3628)) authored by @[enori](https://github.com/enori) +- Clarify special characters for key, i.e. what `x["y"]` is necessary because `x.y` isn't valid ([#3638](https://github.com/open-policy-agent/opa/issues/3638)) authored by @[Hongbo-Miao](https://github.com/Hongbo-Miao) +- Management APIs: Remove deprecated fields from docs +- Policy Reference: add missing backtick; `type_name` builtin is natively implemented in Wasm + +## 0.30.2 + +This is a bugfix release that modifies the AWS credential provider to use POST +instead of GET for retrieving AWS STS tokens. The GET method can leak +credentials into the debug log if the AWS STS endpoint is unavailable. + +## 0.30.1 + +This is a bugfix release to correct the behaviour of the `indexof` builtin ([#3606](https://github.com/open-policy-agent/opa/issues/3606)). +In v0.30.0, it only checked the first character of the substring to be found: `indexof("foo", "fox")` erroneously returned 0 instead of -1. + +### Miscellaneous + +- wasm-sdk: Fix typo in non-wasm error message, authored by @[olivierlemasle](https://github.com/olivierlemasle) + +## 0.30.0 + +This release contains a number of enhancements and fixes. + +### Server and Runtime + +- Support listening on abstract Unix Domain Sockets ([#3533](https://github.com/open-policy-agent/opa/issues/3533)) authored by @[amanymous-net](https://github.com/amanymous-net) +- Support minimum TLS version configuration, default to 1.2 ([#3226](https://github.com/open-policy-agent/opa/issues/3226)) authored by @[kale-amruta](https://github.com/kale-amruta) +- Enhancement in REST Plugin: You can now specify a CA cert for remote services implementing the management APIs (bundles, status, decision logs, discovery) ([#1954](https://github.com/open-policy-agent/opa/issues/1954)) +- Bugfix: treat missing/empty roots as owning all paths ([#3521](https://github.com/open-policy-agent/opa/issues/3521)) + + Before, it would have been possible to overwrite a policy that was supplied by a bundle (with an empty manifest, or a manifest without declared roots), due to an erroneous check. + This will now be forbidden, and return a 400 HTTP status, in accordance with the documentation. +- Extend POST v1/query endpoint to accept input, refactor index.html to use fetch() +- Bundle download: In case of download or activation errors, the cached Etag is reset to the last successful activation. Previously OPA would reset the cached Etag entirely, which could trigger unnecessary bundle downloads in edge-case scenarios. + +### Tooling + +- `opa build`: Do not write manifest if empty ([#3480](https://github.com/open-policy-agent/opa/issues/3480)). Under the hood, the manifest metadata is now included in the Equal() function's checks. +- `opa fmt`: Fix incorrect help text ([#3518](https://github.com/open-policy-agent/opa/issues/3518)) authored by @[andrehaland](https://github.com/andrehaland) +- `opa bench`: Do not print nil errors ([#3530](https://github.com/open-policy-agent/opa/issues/3530)) + +### Rego + +- Expose random seeding in rego package ([#3560](https://github.com/open-policy-agent/opa/issues/3560)) +- Enhance `ast.InterfaceToValue` to handle non-native types +- Enhance indexer to understand function args +- Enhance static property lookup of objects: Use binary search +- Fix PE unknown check to avoid saving unnecessarily ([#3552](https://github.com/open-policy-agent/opa/issues/3552)) +- Fix inlining controls for functions ([#3463](https://github.com/open-policy-agent/opa/issues/3463)) +- Fix (shallow) partial eval of ref to empty collection in presence of `with` statement ([#3420](https://github.com/open-policy-agent/opa/issues/3420)) +- Fix cache value size checking during insert operation +- Fix `indexof` when using UTF-8 characters +- Fix `http.send` flaky test + +#### Wasm + +- SDK: update wasmtime-go to 0.28.0, authored by @[olivierlemasle](https://github.com/olivierlemasle) +- Bugfix: count() now counts invalid UTF-8 runes (previously aborted) +- Compiler: emit unreachable instruction after opa_abort() + +### Miscellaneous + +- `make check` now uses golangci-lint via docker, authored by @[willbeason](https://github.com/willbeason) +- The statically-built linux binary is properly used in the make targets that need it, and published to edge binaries. +- Built binaries are now smoke tested on Windows, macos, and Linux. +- Fix test failing with Go 1.17 rc in gojsonschema ([#3589](https://github.com/open-policy-agent/opa/issues/3589)) authored by @[olivierlemasle](https://github.com/olivierlemasle) +- Build: Bump Go version to 1.16.3 ([#3555](https://github.com/open-policy-agent/opa/issues/3555)) +- CI: enable dependabot for wasmtime-go + +#### Documentation + +- OAuth2/OIDC: Fixed `concat` arguments in metadata discovery method ([#3543](https://github.com/open-policy-agent/opa/pull/3543), @[iggbom](https://github.com/iggbom)) +- Policy Reference: syntax highlighting EBNF grammar (@[PatMyron](https://github.com/PatMyron)) +- Extending OPA: fix typo (@[dxps](https://github.com/dxps)) +- Extending OPA: marshal the decision log (@[TheLunaticScripter](https://github.com/TheLunaticScripter)) +- Kubernetes Introduction: fix typo (@[dbaker-rh](https://github.com/dbaker-rh)) +- Envoy: Add guidance for OPA-Envoy benchmarks +- Change default linux download to `opa_linux_amd64_static` + +## 0.29.4 + +This is a bugfix release that re-introduces linux binaries that do not depend on glibc, i.e., run in unmodified Alpine Linux systems. + +### Fixes + +- build: add static (wasm-disabled) linux build (#3511) ([#3499](https://github.com/open-policy-agent/opa/issues/3499)) authored by @[srenatus](https://github.com/srenatus) + +### Miscellaneous + +- bundle: Implement a DirectoryLoader for fs.FS (#3493) ([#3489](https://github.com/open-policy-agent/opa/issues/3489)) authored by @[simongottschlag](https://github.com/simongottschlag) + +## 0.29.3 + +This bugfix release addresses another edge case in function evaluation ([#3505](https://github.com/open-policy-agent/opa/pull/3505)). + +## 0.29.2 + +This is a bugfix release to resolve an issue in topdown's function output caching ([#3501](https://github.com/open-policy-agent/opa/issues/3501)) + +## 0.29.1 + +This is a bugfix release to resolve an issue in the release pipeline. + +## 0.29.0 + +This release contains a number of enhancements and fixes. + +### SDK + +- This release includes a new top-level package to support OPA integrations in Go programs: `github.com/open-policy-agent/opa/sdk`. Users that want to integrate OPA as a library in Go and expose features like bundles and decision logging should use this package. The package is controlled by specifying an OPA configuration file. Hot reloading is supported out-of-the-box. See the GoDoc for [the package docs](https://pkg.go.dev/github.com/open-policy-agent/opa@v0.29.0/sdk) for more details. + +### Server + +- A deadlock in the bundle plugin during shutdown has been resolved ([#3363](https://github.com/open-policy-agent/opa/issues/3363)) +- An issue between bundle signing and bundle persistence when multiple data.json files are included in the bundle has been resolved ([#3472](https://github.com/open-policy-agent/opa/issues/3472)) +- The `github.com/open-policy-agent/opa/runtime#Params` struct now supports a router parameter to enable custom routes on the HTTP server. +- The bundle manifest can now include an extra `metadata` key where arbitrary key-value pairs can be stored. Authored by @[viovanov](https://github.com/viovanov) +- The bundle plugin now supports file:// urls in the `resource` field for test purposes. +- The decision log plugin emits a clearer message at DEBUG instead of INFO when there is no work to do. Authored by [andrewbanchich](https://github.com/andrewbanchich) +- The discovery plugin now supports a `resource` configuration field like the bundle plugin. Similarly, the `resource` is treated as the canonical setting to identify the discovery bundle. + +### Tooling + +- The `opa test` timeout as been increased to 30 seconds when benchmarking ([#3107](https://github.com/open-policy-agent/opa/issues/3107)) +- The `opa eval --schema` flag has been fixed to correctly set the schema when a _single_ schema file is passed +- The `opa build --debug` flag output has been improved for readability +- The `array.items` JSON schema value is now supported by the type checker +- The `opa fmt` subcommand can now exit with a non-zero status when a diff is detected (by passing `--fail`) +- The `opa test` subcommand no longer emits bogus file paths when fed a file:// url + +### Built-in Functions + +- The `http.send` built-in function falls back to the system certificate pool when the `tls_ca_cert` or `tls_ca_cert_env_variable` options are not specified ([#2271](https://github.com/open-policy-agent/opa/issues/2271)) authored by @[olamiko](https://github.com/olamiko) + +### Evaluation + +- The order of support rules emitted by partial evaluation is now deterministic ([#3453](https://github.com/open-policy-agent/opa/issues/3453)) authored by @[andrehaland](https://github.com/andrehaland) +- The big number performance regression caught by the fuzzer has been resolved ([#3262](https://github.com/open-policy-agent/opa/issues/3262)) +- The evaluator has been updated to memoize calls to rules with arguments (functions) within a single query. This avoids recomputing function results when the same input is passed multiple times (similar to how complete rules are memoized.) + +### WebAssembly + +- The `wasm` target no longer panics if the OPA binary does not include a wasm runtime ([#3264](https://github.com/open-policy-agent/opa/issues/3264)) +- The interrupt handling mechanism has been rewritten to make safe use of the wasmtime package. The SDK also returns structured errors now that are more aligned with topdown. ([#3225](https://github.com/open-policy-agent/opa/issues/3225)) +- The SDK provides the subset of required imports now (which is useful for debugging with opa_println in the runtime library if needed.) +- The opa_number_float type has been removed from the value library (it was unused after moving to libmpdec) +- The runtime library builder has been updated to use llvm-12 and the wasmtime-go package has been updated to v0.27.0 + +### Documentation + +- The HTTP API authorization tutorial has been updated to show how to distribute policies using bundles +- The Envoy tutorial has been tweaked to show better path matching examples + +### Infrastructure + +- The release-patch script has been improved to deal with _this file_ in bugfix/patch releases ([#2533](https://github.com/open-policy-agent/opa/issues/2533)) authored by @[jjshanks](https://github.com/jjshanks) +- The Makefile check targets now rely on golangci-lint and many linting errors have been resolved (authored by @[willbeason](https://github.com/willbeason)) +- Multiple nightly fuzzing and data race issues in test cases have been resolved + +## 0.28.0 + +This release includes a number of features, enhancements, and fixes. The default +branch for the Git repository has also been updated to `main`. + +#### Schema Annotations + +This release adds support for _annotations_. Annotations allow users to +declare metadata on rules and packages. Currently, OPA supports one form of +metadata: schema declarations. For example: + +```rego +package example + +# METADATA +# schemas: +# - input: schema.service +deny["service is missing required 'owner' label"] { + input.kind == "Service" + not input.metadata.labels.owner +} + +# METADATA +# schemas: +# - input: schema.deployment +deny["deployment replica count too low for 'production' namespace"] { + input.kind == "Deployment" + input.metadata.namespace == "production" + object.get(input.spec, "replicas", 1) < 3 +} +``` + +Users can include schema annotations in their policies to tell OPA about the +structure of external data loaded under `input` or `data`. By learning the +schema of base documents, OPA can surface mistakes in the policy at authoring +time (e.g., referring to a non-existent field in a JSON object or calling a +built-in function with an invalid value.) For more information on the +annotations and schema support see the [Type +Checking](https://www.openpolicyagent.org/docs/latest/schemas/) page in the +documentation. In the future, annotations will be expanded to support other +kinds of metadata and additional tooling will be added to leverage them. + +### Server + +- The server now automatically sets GOMAXPROCS when running inside of a container that has cgroups applied. This helps the Go runtime avoid consuming too many CPU resources and being throttled by the kernel. ([#3328](https://github.com/open-policy-agent/opa/issues/3328)) +- The server now logs an error if users enable the `token` authentication mode without a corresponding authorization policy. ([#3380](https://github.com/open-policy-agent/opa/issues/3380)) authored by @[kale-amruta](https://github.com/kale-amruta) +- The server now supports a `GET /v1/config` endpoint that returns OPA's active configuration. This API is useful if you need to debug the running configuration in an OPA configured via Discovery. ([#2020](https://github.com/open-policy-agent/opa/issues/2020)) +- The server now respects the `?pretty` option in the v0 API ([#3332](https://github.com/open-policy-agent/opa/issues/3332)) authored by @[clarshad](https://github.com/clarshad) +- The Bundle plugin is more forgiving when it comes to Etag processing on HTTP 304 responses ([#3361](https://github.com/open-policy-agent/opa/issues/3361)) +- The Decision Log plugin now supports a "Decision Per Second" rate limit configuration setting. +- The Status plugin can now be configured to use a custom reporter similar to the Decision Log plugin (e.g., so that Status messages can be sent to AWS Kinesis, etc.) +- The Status plugin now reports the number of decision logs that are dropped due to buffer limits. +- The service clients can authenticate with the Azure Identity OAuth2 implementation the client credentials JWT flow is used ([#3372](https://github.com/open-policy-agent/opa/issues/3372)) +- Library users can now customize the logger used by the plugins by providing the `plugins.Logger` option when creating the plugin manager. + +### Tooling + +- The various OPA subcommands that accept schema files now accept a directory tree of schemas instead of only a single schema. +- The `opa refactor move` subcommand was added to support package renaming use cases ([#3290](https://github.com/open-policy-agent/opa/issues/3290)) +- The `opa check` subcommand now supports a `-s`/`--schema` flag like the `opa eval` subcommand. + +### Documentation + +- The [Management API](https://www.openpolicyagent.org/docs/latest/management-introduction/) docs have been restructured so that each API has a dedicated page. In addition, the [Bundle API](https://www.openpolicyagent.org/docs/latest/management-bundles/#implementations) docs now include getting started steps for cloud-provider specific services (e.g., AWS, GCP, Azure, etc.) + +### Security + +- OPA now supports PKCS8 encoded EC private keys for JWT verification (which includes service authentication, bundle verification, and verification built-in functions) ([#3283](https://github.com/open-policy-agent/opa/issues/3283)). Authored by @[andrehaland](https://github.com/andrehaland). +- The bundle signing and verification APIs have been updated to support custom signers/verififers ([#3336](https://github.com/open-policy-agent/opa/pull/3336)). Authored by @[gshively11](https://github.com/gshively11). + +### Evaluation + +- The `time.diff` function was added to support calculating differences between date/time values ([#3348](https://github.com/open-policy-agent/opa/issues/3348)) authored by @[andrehaland](https://github.com/andrehaland) +- The `units.parse_bytes` function now supports floating-point values ([#3297](https://github.com/open-policy-agent/opa/issues/3297)) authored by @[andy-paine](https://github.com/andy-paine) +- The evaluator was fixed to use correct bindings when evaluating the full-extent of a partial rule set. This issue was causing unexpected undefined results and evaluation errors in some rare cases. ([#3369](https://github.com/open-policy-agent/opa/issues/3369) [#3376](https://github.com/open-policy-agent/opa/issues/3376)) +- The evaluator was fixed to correctly generate package paths when namespacing is disabled partial evaluation. ([#3302](https://github.com/open-policy-agent/opa/issues/3302)). +- The `http.send` function no longer errors out on invalid Expires headers. ([#3284](https://github.com/open-policy-agent/opa/issues/3284)) +- The inter-query cache now serializes elements on insertion thereby reducing memory usage significantly (because deserialized elements carry a ~20x cost.) ([#3042](https://github.com/open-policy-agent/opa/issues/3042)) +- The rule indexer was fixed to correctly handle mapped and non-mapped values which could occur with `glob.match` usage ([#3293](https://github.com/open-policy-agent/opa/issues/3293)) + +### WebAssembly + +- The `opa eval` subcommand now correctly returns the set of all variable bindings and expression values when the `wasm` target is enabled. Previously it returned only set of variable bindings. ([#3281](https://github.com/open-policy-agent/opa/issues/3281)) +- The `glob.match` function now handles the default delimiter correctly. ([#3294](https://github.com/open-policy-agent/opa/issues/3294)) +- The `opa build` subcommand no longer requires a capabilities file when the `wasm` target is enabled. If capabilities are not provided, OPA will use the capabilities for its own version. ([#3270](https://github.com/open-policy-agent/opa/issues/3270)) +- The `opa build` subcommand now dumps the IR emitted by the planner when `--debug` is specified. +- The `opa eval` subcommand no longer panics when a policy fails to type check and the `wasm` target is enabled. +- The comparison functions can now return `false` instead of either being `true` or `undefined`. ([#3271](https://github.com/open-policy-agent/opa/issues/3271)) +- The internal wasm runtime will now correctly return `CancelErr` to indicate cancellation errors (instead of `BuiltinErr` which it returned previously.) +- The internal wasm runtime now correctly handles non-halt built-in errors ([#3320](https://github.com/open-policy-agent/opa/issues/3320)) +- The planner no longer generates unexpected scan statements when negation used over base documents under `data` ([#3279](https://github.com/open-policy-agent/opa/issues/3279)) and ([#3305](https://github.com/open-policy-agent/opa/issues/3305)) +- The planner now correctly discards out-of-scope variables when exiting comprehensions ([#3325](https://github.com/open-policy-agent/opa/issues/3325)) +- The `rego` package no longer panics when the `wasm` target is enabled and undefined functions are encountered ([#3251](https://github.com/open-policy-agent/opa/issues/3251)) +- 🎈 The remaining exceptions in the e2e test framework for the internal wasm runtime have been resolved. + +### Build + +- The `make image` target now uses the CI image for building the Go binary. This avoids platform-specific build issues by building the Go binary inside of Docker. + +## 0.27.1 + +This release contains a fix for crashes experienced when configuring OPA to use S3 signing as service credentials ([#3255](https://github.com/open-policy-agent/opa/issues/3255)). + +In addition to that, we have a small number of enhancements and fixes: + +### Tooling + +- The `eval` subcommand now allows using `--import` without using `--package`. Authored by @[onelittlenightmusic](https://github.com/onelittlenightmusic), [#3240](https://github.com/open-policy-agent/opa/pull/3240). + +## Compiler + +- The `ast` package now exports another method for JSON conversion, `ast.JSONWithOpts`, that allows further options to be set ([#3244](https://github.com/open-policy-agent/opa/pull/3244). + +### Server + +- REST plugins using `s3_signing` as credentials method can now include the specified service in the signature (SigV4). Authored by @[cogwirrel](https://github.com/cogwirrel), [#3210](https://github.com/open-policy-agent/opa/pull/3210). + +### Documentation + +- Remove soon-to-be deprecated `any` and `all` from the [Policy Reference](https://www.openpolicyagent.org/docs/v0.27.1/policy-reference/#aggregates) ([#3241](https://github.com/open-policy-agent/opa/pull/3241)) -- see also [#2437](https://github.com/open-policy-agent/opa/issues/2437). +- Add missing `discovery.service` field to [Discovery configuration](https://www.openpolicyagent.org/docs/v0.27.1/configuration/#discovery) table ([#3237](https://github.com/open-policy-agent/opa/pull/3237)). +- Fix dead links to the Envoy pages ([#3248](https://github.com/open-policy-agent/opa/pull/3248)). + +### WebAssembly + +- Executions using the internal Wasm SDK will now be interrupted when the provided context is done (cancelled or deadline reached). +- The generated Wasm modules could become much smaller: unused functions are replaced by `unreachable` stubs, and the heavyweight runtime components related to regular expressions are excluded when none of the regex-related builtins are used: `glob.match`, `regex.is_valid`, `regex.match`, `regex.is_valid`, and `regex.find_all_string_submatch_n`. +- The Wasm runtime now allows passing in the time to be used for evaluation, enabling callers to control the time-of-day observed by Wasm compiled policies. +- Wasmtime runtime has been updated to the latest version (v0.24.0). + +## 0.27.0 + +This release contains a number of enhancements and bug fixes. + +### Tooling + +- The `eval` subcommand now supports a `-s`/`--schema` flag that accepts a JSON schema for the `input` document. The schema is used when type checking the policy so that invalid references to (or operations on) `input` data are caught at compile time. In the future, the schema support will be expanded to accept multiple schemas and rule-level annotations. See the new [Schemas](https://www.openpolicyagent.org/docs/edge/schemas/) documentation for details. Authored by @[aavarghese](https://github.com/aavarghese) and @[vazirim](https://github.com/vazirim). +- The `eval`, `test`, `bench` and REPL subcommands now supports a `-t`/`--target` flag to set the evaluation engine to use. The default engine is `rego` referring to the standard Rego interpreter in OPA. Users can now select `wasm` to enable Wasm compilation and execution of policies ([#2878](https://github.com/open-policy-agent/opa/issues/2878)). +- The `eval` subcommand now supports a `raw` option for `-f`/`--format` that is useful in bash scripts. Authored by @[jaspervdj-luminal](https://github.com/jaspervdj-luminal). +- The test framework now supports "skippable" tests. Prefix the test name with `todo_` to have the test runner skip the test, e.g., `todo_test_allow { ... }`. +- The `eval` subcommand now correctly supports the `--ignore` flag. Previously the flag was not being applied. + +### Server + +- The `POST /v1/compile` API now supports a `?metrics` query parameter similar to other APIs. Authored by @[jkbschmid](https://github.com/jkbschmid). +- The directory used for persisting downloaded bundles can now be configured. See the [Configuration](https://www.openpolicyagent.org/docs/latest/configuration/) page for details. +- The HTTP Decision Logger plugin no longer blocks server shutdown for the grace period when there are no logs to upload. +- The Bundle plugin now unregisters listeners correctly. This issue would cause listeners to be invoked when bundle updates were dispatched even if the listener was unregistered ([#3190](https://github.com/open-policy-agent/opa/issues/3190)). +- The server now correctly decodes policy IDs in the HTTP request URL. Authored by @[mattmahn](https://github.com/mattmahn) ([#2116](https://github.com/open-policy-agent/opa/issues/2116)). +- The server now configures the `http_request_duration_seconds` metric (for all of the server endpoitns) with smaller, more granular buckets that better map to actual response latencies from OPA. Authored by @[luong-komorebi](https://github.com/luong-komorebi) ([#3196](https://github.com/open-policy-agent/opa/issues/3196)). + +### Security + +- PKCS8 keys are now supported when signing bundles and communicating with control plane services. Previously only PKCS1 keys were supported ([#3116](https://github.com/open-policy-agent/opa/issues/3116)). +- The built-in OPA HTTP API authorizer policy can now return a _reason_ to explain why a request to the OPA API is denied ([#3056](https://github.com/open-policy-agent/opa/issues/3056)). See the [Security](https://www.openpolicyagent.org/docs/edge/security/) documentation for details. Thanks to @[ajanthan](https://github.com/ajanthan) for helping improve this. + +### Compiler + +- The compiler can be configured to emit debug messages that explain comprehension indexing decisions. Debug messages can be enabled when running `opa build` with `--debug`. +- A panic was fixed in one of the rewriting stages when comprehensions were used as object keys ([#2915](https://github.com/open-policy-agent/opa/issues/2915)) + +### Evaluation + +- A bug in big integer comparison was fixed. This issue was discovered when comparing serial numbers from X.509 certificates. Authored by @[andrehaland](https://github.com/andrehaland) ([#3147](https://github.com/open-policy-agent/opa/issues/3147)). +- The `io.jwt.decode_verify` function now uses the environment supplied time-of-day value instead of calling `time.Now()` ([#3105](https://github.com/open-policy-agent/opa/issues/3105)). + +### Documentation + +- The documentation now includes a dedicated section the OPA-Envoy integration. See [https://www.openpolicyagent.org/docs/latest/envoy-introduction/](https://www.openpolicyagent.org/docs/latest/envoy-introduction/) for details. +- The ecosystem page now ranks integrations by number of unique domains instead of the sheer number of references. + +### WebAssembly + +- The `data` document no longer needs to be initialized to an empty object ([#3130](https://github.com/open-policy-agent/opa/issues/3130)). +- The mpd library is now initalized by the module's `Start` function ([#3110](https://github.com/open-policy-agent/opa/issues/3110)). +- The planner now longer re-plans rules blindly when `with` statements are encountered ([#3150](https://github.com/open-policy-agent/opa/issues/3150)). +- The planner and compiler now support dynamic dispatch. Previously the planner would enumerate all functions and invocation was controlled at runtime ([#2936](https://github.com/open-policy-agent/opa/issues/2936)). +- The compiler now inserts memoization instructions into function bodies instead of at callsites. This reduces the number of wasm instructions in the resulting binary ([#3169](https://github.com/open-policy-agent/opa/pull/3169)). +- The wasmtime runtime is now the default runtime used by OPA to execute compiled policies. The new runtime no longer leaks memory when policies are reloaded. +- The planner and compiler now intern strings and booleans and implement a few micro-optimizations to reduce the size of the resulting binary. +- The capabilities support has been updated to include an ABI major and minor version for tracking backwards compatibility on compiled policies ([#3120](https://github.com/open-policy-agent/opa/issues/3120)). + +### Backwards Compatibility + +- The `opa test` subcommand previously supported a `-t` flag as shorthand for `--timeout`. With this release, the `-t` shorthand has been redefined for `--target`. After searching GitHub for examples of `opa test -t` (and finding nothing) we felt comfortable making this backwards incompatible change. +- The Go version used to build the OPA release has been updated from `1.14.9` to `1.15.8`. Because of this, TLS certificates that rely on Common Name for verification are no longer supported and will not work. For more information see https://github.com/golang/go/issues/39568. + +## 0.26.0 + +This release contains a number of enhancements and bug fixes. + +### Built-in Functions + +- This release includes a number of built-in function improvements for Wasm compiled policies. The following built-in functions have been implemented natively and no longer need to be supplied by SDKs: `graph.reachable`, `json.filter`, `json.remove`, `object.get`, `object.remove`, and `object.union`. + +- This release fixes several bugs in the Wasm implementation of certain `regex` built-in functions ([#2962](https://github.com/open-policy-agent/opa/issues/2962)), `format_int` ([#2923](https://github.com/open-policy-agent/opa/issues/2923)) and `round` ([#2999](https://github.com/open-policy-agent/opa/pull/2999)). + +- This release adds `ceil` and `floor` built-in functions. Previously these could be implemented in Rego using `round` however these are more convenient. + +### Enhancements + +- OPA has been extended support [OAuth2 JWT Bearer Grant Type](https://www.openpolicyagent.org/docs/latest/configuration/#oauth2-jwt-bearer-grant-type) and [OAuth2 Client Credential JWT](https://www.openpolicyagent.org/docs/edge/configuration/#oauth2-client-credentials-jwt-authentication) authentication options for communicating with control plane services. This change allows OPA to use services that rely on Ping Identity as well as GCP service accounts for authentication. OPA has also been extended to support [custom authentication plugins](https://www.openpolicyagent.org/docs/edge/configuration/#custom-plugin) (thanks @[gshively11](https://github.com/gshively11)). + +- OPA plugins can now enter a "WARN" state to indicate they are operating in a degraded capacity (thanks @[gshively11](https://github.com/gshively11)). + +- The `opa bench` command can now benchmark partial evaluation queries. The options to enable partial evaluation are shared with `opa eval`. See `opa bench --help` for details. + +- Wasm compiled policies now contain source locations that are included inside of runtime error messages (such as object key conflicts.) In addition, Wasm compiled policies only export the minimal set of APIs described on the [WebAssembly#exports](https://www.openpolicyagent.org/docs/latest/wasm/#exports) page. + +### Fixes + +- ast: Fix parsing of numbers to reject leading zeroes ([#2947](https://github.com/open-policy-agent/opa/issues/2947)) authored by @[LCartwright](https://github.com/LCartwright). +- bundle: Fix loader to only verify bundle keys if configured to do so ([#3028](https://github.com/open-policy-agent/opa/issues/3028)). +- cmd: Fix build to avoid packaging policy.wasm twice ([#3007](https://github.com/open-policy-agent/opa/issues/3007)). +- cmd: Fix pretty-printed PE output to hide spurious blank lines +- server: Fix false-positive in bundle root check that would prevent data updates in some cases ([#2868](https://github.com/open-policy-agent/opa/issues/2868)). +- server: Fix query cache to respect ?instrument option ([#3000](https://github.com/open-policy-agent/opa/issues/3000)). +- server: Fix server to support discovery on inter-query cache configuration +- topdown: Fix PE to avoid generating expressions that do not type check ([#3012](https://github.com/open-policy-agent/opa/issues/3012)). +- wasm: Fix planner to avoid generating a conflict error in some cases ([#2926](https://github.com/open-policy-agent/opa/issues/2926)). +- wasm: Fix planner to generate correct virtual document iteration instructions ([#3065](https://github.com/open-policy-agent/opa/issues/3065)). +- wasm, topdown: Fix with keyword handle to ensure last statement wins ([#3010]((https://github.com/open-policy-agent/opa/issues/3010))). +- wasm: Fix planner to handle assignment conflicts correctly when else keyword is used ([#3031]((https://github.com/open-policy-agent/opa/issues/3031))). + +### Documentation + +- Add new section on integrating policies with OAuth2 and OIDC. +- Update Kubernetes admission control tutorial to work as non-root user. +- Fix link to signing documentation ([#3027](https://github.com/open-policy-agent/opa/issues/3027)) authored by @[princespaghetti](https://github.com/princespaghetti). + +### Backwards Compatibility + +- Previously, OPA deduplicated sets and objects in all cases except when iterating over/referring directly to values generated by partial rules. This inconsistency would only be noticed when running ad-hoc queries or within policies when aggregating the results of array comprehensions (e.g., `count([1 | p[x]])` could observe duplicates in `p`.) This release removes the inconsistency by deduplicating sets and objects in all cases ([#429](https://github.com/open-policy-agent/opa/issues/429)). This was the second oldest open issue on the project. + +### Deprecations + +- OPA now logs warnings when it receives legacy `bundle` config sections instead of the `bundles` section introduced in v0.13.0. + +## 0.25.2 + +This release extends the HTTP server authorizer (`--authorization=basic`) to supply the HTTP message body in the `input` document. See the [Authentication and Authorization](https://www.openpolicyagent.org/docs/edge/security/#authentication-and-authorization) section in the security documentation for details. + +## 0.25.1 + +This release contains a fix for running OPA under Docker with a non-default working directory ([#2974](https://github.com/open-policy-agent/opa/issues/2974)). + +## 0.25.0 + +This release contains a number of improvements and fixes. Importantly, this release includes a notable change to built-in function error handling. See the section below for details. + +### Built-in Function Error Handling + +Previously, built-in function errors would cause policy evaluation to halt immediately. Going forward, by default, built-in function errors no longer halt evaluation. Instead, expressions are treated as false/undefined if any of the invoked built-in functions return errors. + +This change resolves a common issue people face when passing unsanitized input values to built-in functions. For example, prior to this change the expression `io.jwt.decode("GARBAGE")` would halt evaluation of the entire policy because the string is not a valid encoding of a JSON Web Token (JWT). If the expression was `io.jwt.decode(input.token)` and the user passed an invalid string value for `input.token` the same error would occur. With this change, the same expression is simply undefined, i.e., there is no result. This means policies can use negation to test for invalid values. For example: + +```rego +decision := {"allowed": allow, "denial_reason": reason} + +default allow = false + +allow { + io.jwt.verify_hs256(input.token, "secret") + [_, payload, _] := io.jwt.decode(input.token) + payload.role == "admin" +} + +reason["invalid JWT supplied as input"] { + not io.jwt.decode(input.token) +} +``` + +If you require the old behaviour, enable "strict" built-in errors on the query: + +| Caller | Example | +| --- | --- | +| HTTP | `POST /v1/data/example/allow?strict-builtin-errors` | +| Go (Library) | `rego.New(rego.Query("data.example.allow"), rego.StrictBuiltinErrors(true))` | +| CLI | `opa eval --strict-builtin-errors 'data.example.allow'` | + +If you have implemented custom built-in functions and require policy evaluation to halt on error in those built-in functions, modify your built-in functions to return the [topdown.Halt](./topdown/errors.go) error type. + +### Built-in Functions + +This release includes a few new built-in functions: + +- `base64url.encode_no_pad`, `hex.encode`, and `hex.decode` for dealing with encoded data ([#2849](https://github.com/open-policy-agent/opa/issues/2849)) authored by @[johanneslarsson](https://github.com/johanneslarsson) +- `json.patch` for applying JSON patches to values inside of policies ([#2839](https://github.com/open-policy-agent/opa/issues/2839)) authored by @[jaspervdj-luminal](https://github.com/jaspervdj-luminal) +- `json.is_valid` and `yaml.is_valid` for testing validity of encoded values (authored by @[jaspervdj-luminal](https://github.com/jaspervdj-luminal)) + +There were also a few fixes to existing built-in functions: + +- Fix unicode handling in a few string-related functions ([#2799](https://github.com/open-policy-agent/opa/issues/2799)) authored by @[anderseknert](https://github.com/anderseknert) +- Fix `http.send` to override `no-cache` HTTP header when `force_cache` specified ([#2841](https://github.com/open-policy-agent/opa/issues/2841)) authored by @[anderseknert](https://github.com/anderseknert) +- Fix `strings.replace_n` to replace overlapping patterns deterministically ([#2822](https://github.com/open-policy-agent/opa/issues/2822)) +- Fix panic in `units.parse_bytes` when passed a zero-length string ([#2901](https://github.com/open-policy-agent/opa/issues/2901)) + +### Miscellaneous + +This release adds new credential providers for management services: + +- GCP metadata server ([#2938](https://github.com/open-policy-agent/opa/pull/2938)) authored by @[kelseyhightower](https://github.com/kelseyhightower) +- AWS Web Identity credentials ([#2462](https://github.com/open-policy-agent/opa/pull/2725)) authored by @[RichiCoder1](https://github.com/RichiCoder1) +- OAuth2 ([#1205](https://github.com/open-policy-agent/opa/issues/1205)) authored by @[anderseknert](https://github.com/anderseknert) + +In addition the following server features were added: + +- Add shutdown wait period flag to `opa run` (`--shutdown-wait-period`) ([#2764](https://github.com/open-policy-agent/opa/issues/2764)) authored by @[bcarlsson](https://github.com/bcarlsson) +- Add bundle file size limit configuration option (`bundles[_].size_limit_bytes`) to override default 1GiB limit ([#2781](https://github.com/open-policy-agent/opa/issues/2781)) +- Separate decision log and status message logs from access logs (which useful for running OPA at log level `error` while continuing to report decision and status log to console) ([#2733](https://github.com/open-policy-agent/opa/issues/2733)) authored by @[anderseknert](https://github.com/anderseknert) + +### Fixes + +- Fix panic caused by race condition in the decision logger ([#2835](https://github.com/open-policy-agent/opa/pull/2948)) authored by @[kubaj](https://github.com/kubaj) +- Fix decision logger to flush on graceful shutdown ([#780](https://github.com/open-policy-agent/opa/issues/780)) authored by @[anderseknert](https://github.com/anderseknert) +- Fix `--verification-key` handling to accept PEM files ([#2796](https://github.com/open-policy-agent/opa/issues/2796)) +- Fix `--capabilities` flag in `opa build` command ([#2848](https://github.com/open-policy-agent/opa/issues/2848)) authored by @[srenatus](https://github.com/srenatus) +- Fix loading of **signed** persisted bundles ([#2824](https://github.com/open-policy-agent/opa/issues/2824)) +- Fix API response mutation caused by decision log masking ([#2752](https://github.com/open-policy-agent/opa/issues/2752)) authored by @[gshively11](https://github.com/gshively11) +- Fix evaluator to prevent `with` statements from mutating original `input` document ([#2813](https://github.com/open-policy-agent/opa/issues/2813)) +- Fix set iteration runtime to be O(n) instead of O(n^2) ([#2966](https://github.com/open-policy-agent/opa/pull/2966)) +- Increased OPA version telemetry report timeout from 1 second to 5 seconds to deal with slow networks + +### Documentation + +- Improve docs to mention built-in function support in WebAssembly compiled policies +- Improve docs around JWT HMAC encoding ([#2870](https://github.com/open-policy-agent/opa/issues/2870)) authored by @[anderseknert](https://github.com/anderseknert) +- Improve HTTP authorization tutorial steps for zsh ([#2917](https://github.com/open-policy-agent/opa/issues/2917) authored by @[ClaudenirFreitas](https://github.com/ClaudenirFreitas)) +- Improve docs to describe meaning of Prometheus metrics +- Remove mention of unsafe (and unsupported) "none" signature algorithm from JWT documentation + +### WebAssembly + +This release also includes a number of improvements to the Wasm support in OPA. Importantly, OPA now integrates a Wasm runtime that can be used to execute Wasm compiled policies. The runtime is integrated into the existing "topdown" evaluator so that specific portions of the policy can be compiled to Wasm as a performance optimization. When the evaluator executes a policy using the Wasm runtime it emits a special `Wasm` trace event. The Wasm runtime support in OPA is currently considered **experimental** and will be iterated on in coming releases. + +This release also extends the Wasm compiler in OPA to natively support the following built-in functions (in alphabetical order): + +* `base64.encode`, `base64.decode`, `base64url.encode`, and `base64url.decode` +* `glob.match` +* `json.marshal` and `json.unmarshal` +* `net.cidr_contains`, `net.cidr_intersects`, and `net.cidr_overlap` +* `regex.match`, `regex.is_valid`, and `regex.find_all_string_submatch_n` +* `to_number` +* `walk` + +### Backwards Compatibility + +- The `--insecure-addr` flag (which was deprecated in v0.10.0) has been removed completely ([#763](https://github.com/open-policy-agent/opa/issues/763)) + +## 0.24.0 + +This release contains a number of small enhancements and bug fixes. + +### Bundle Persistence + +This release adds support for persisting bundles for recovery purposes. When persistence is enabled, OPA will save activated bundles to disk. On startup, OPA checks for persisted bundles and activates them immediately. This allows OPA to startup if the bundle server is unavailable ([#2097](https://github.com/open-policy-agent/opa/issues/2097)). For more information see the [Bundle](https://www.openpolicyagent.org/docs/latest/management/#bundles) documentation. + +### Built-in Functions + +This release includes a few new built-in functions: + +- `base64.is_valid` for testing if strings are valid base64 encodings ([#2690](https://github.com/open-policy-agent/opa/issues/2690)) authored by @[carlpett](https://github.com/carlpett) +- `net.cidr_merge function` for merging sets of IPs and CIDRs ([#2692](https://github.com/open-policy-agent/opa/issues/2692)) +- `urlquery.decode_object` for parsing URL query parameters into objects ([#2647](https://github.com/open-policy-agent/opa/issues/2647)) authored by @[GBrawl](https://github.com/GBrawl) + +In addition, `http.send` has been enhanced to support caching overrides and in-band error handling ([#2666](https://github.com/open-policy-agent/opa/issues/2666) and [#2187](https://github.com/open-policy-agent/opa/issues/2187)). + +### Fixes + +- Fix `opa build` to support custom built-in functions ([#2738](https://github.com/open-policy-agent/opa/issues/2738)) authored by @[gshively11](https://github.com/gshively11) +- Fix for file watching volume mounted configmaps ([#2588](https://github.com/open-policy-agent/opa/issues/2588)) authored by @[drewwells](https://github.com/drewwells) +- Fix discovery plugin to set last request and last successful request timestamps in status updates ([#2630](https://github.com/open-policy-agent/opa/issues/2630)) +- Fix planner crash on virtual document iteration ([#2601](https://github.com/open-policy-agent/opa/issues/2601)) +- Fix decision logger to requeue failed chunks ([#2724](https://github.com/open-policy-agent/opa/pull/2724) authored by @[anderseknert](https://github.com/anderseknert)) +- Fix object/set implementation in WASM-C library to avoid resizing. +- Fix JSON parser in WASM-C library to copy memory for strings and numbers. +- Improve WASM-C library to recycle object and set element structures while growing. + +In addition, this release contains several fixes for panics identified by fuzzing: + +- ast: Fix compiler to expand exprs in rule args ([#2649](https://github.com/open-policy-agent/opa/issues/2649)) +- ast: Fix output var analysis to accept refs with non-var heads ([#2678](https://github.com/open-policy-agent/opa/issues/2678)) +- ast: Fix panic during local var rewriting ([#2720](https://github.com/open-policy-agent/opa/issues/2720)) +- ast: Fix panic in local var rewriting caused by object corruption ([#2661](https://github.com/open-policy-agent/opa/issues/2661)) +- ast: Fix panic in parser post-processing of expressions ([#2714](https://github.com/open-policy-agent/opa/issues/2714)) +- ast: Fix parser to ignore rules with args and key in head ([#2662](https://github.com/open-policy-agent/opa/issues/2662)) +- ast: Fix object corruption during safety reordering +- types: Fix panic on reference to object with composite key ([#2648](https://github.com/open-policy-agent/opa/issues/2648)) + +### Backwards Compatibility + +- Renamed `timer_rego_builtin_http.send_ns` to `timer_rego_builtin_http_send_ns` to avoid issues with periods in metric keys. +- Removed deprecated `watch` package ([#2265](https://github.com/open-policy-agent/opa/issues/2265)) + +### Miscellaneous + +- Add support for H2C on HTTP listener ([#2739](https://github.com/open-policy-agent/opa/issues/2739) thanks @[srenatus](http://github.com/srenatus)!). +- Add Go version information to `opa version` output (thanks @[srenatus](http://github.com/srenatus)!) +- The official OPA build has been updated to Go v1.14.9. Previously it was using v1.13.7 which is no longer supported (thanks @[srenatus](http://github.com/srenatus)!) + +## 0.23.2 + +This release contains a fix for a regression in v0.23.1 around bundle downloading. The bug caused OPA to cancel bundle downloads prematurely. Users affected by this issue would see the following error message in the OPA logs: + +``` +[ERROR] Bundle download failed: bundle read failed: archive read failed: context canceled + plugin = "bundle" + name = +``` + +## 0.23.1 + +### Fixes + +- plugins/discovery: Set the last request and last successful request in discovery status ([#2630](https://github.com/open-policy-agent/opa/issues/2630)) + +### Miscellaneous + +- plugins/rest: Add response header timeout for REST client + +## 0.23.0 + +### `http.send` Caching + +The `http.send` built-in function now supports caching across policy queries. The `caching.inter_query_builtin_cache.max_size_bytes` configuration setting places a limit on the amount of memory that will be used for built-in function caching. By default, not limit is set. For `http.send`, cache duration is controlled by HTTP response headers. For more details see the [`http.send`](https://www.openpolicyagent.org/docs/latest/policy-reference/#http) documentation. + +### Capabilities + +OPA now supports a _capabilities_ check on policies. The check allows callers to restrict the built-in functions that policies may depend on. If the policies passed to OPA require built-ins not listed in the capabilities structure, an error is returned. The capabilities check is currently supported by the `check` and `build` sub-commands and can be accessed programmatically on the `ast.Compiler` structure. The repository also includes a set of capabilities files for previous versions of OPA under the `capabilities/` directory. + +For example, given the following policy: + +```rego +package example + +deny["missing semantic version"] { + not valid_semantic_version_tag +} + +valid_semantic_version_tag { + semver.is_valid(input.version) +} +``` + +We can check whether it is compatible with different versions of OPA: + +```bash +# OK! +$ opa build ./policies/example.rego --capabilities ./capabilities/v0.22.0.json + +# ERROR! +$ opa build ./policies/example.rego --capabilities ./capabilities/v0.21.1.json +``` + +### Built-in Functions + +This release includes a new built-in function to test if a string is a valid regular expression: `regex.is_valid`. + +### WebAssembly + +* Host environments no longer have to provide the `opa_println` function when instantiating compiled policy modules. +* SDKs no longer have to set the heap top address during initialization. + +### Fixes + +- Add a new inter-query cache to cache responses across queries ([#1753](https://github.com/open-policy-agent/opa/issues/1753)) +- Fix `opa` CLI flags to match documentation ([#2586](https://github.com/open-policy-agent/opa/issues/2586)) authored by @[OmegaVVeapon](https://github.com/OmegaVVeapon) +- Fix rule indexing when multiple glob.match mappers are required ([#2617](https://github.com/open-policy-agent/opa/issues/2617)) +- Fix AST to marshal non-string object keys ([#516](https://github.com/open-policy-agent/opa/issues/516)) +- Fix signature calculation to include port if necessary ([#2568](https://github.com/open-policy-agent/opa/issues/2568)) +- Fix partial evaluation to check function output for false values ([#2573](https://github.com/open-policy-agent/opa/issues/2573)) + +### Miscellaneous + +- Add `http.send` latency to query metrics ([#2034](https://github.com/open-policy-agent/opa/issues/2034)) +- Add support for `opa build` unknowns under `data` ([#2581](https://github.com/open-policy-agent/opa/issues/2581)) +- Add support to wait for plugin readiness before starting server +- Add parameter to set wall clock time during evaluation for replay purposes +- Fix groundness bit on objects during update +- Fix x509 built-in functions to parse PEM or DER inputs +- Fix bundle signing and verification to use standard JWT key ID header +- Optimize AST collections to cache hash values +- Optimize object iteration to avoid hashing +- Optimize evaluator by removing unnecessary term copying + +### Deprecations + +* The `watch` query parameter on the Data API has been deprecated. The query watch feature was unused and the lack of incremental evaluation would have introduced scalability issues for users. The feature will be removed in a future release. + +* The `partial` query parameter on the Data API has been deprecated. Note, this only applies to the `partial` query parameter that the Data API supports, not Partial Evaluation itself. The `partial` parameter allowed users to lazily trigger Partial Evaluation (for optimization purposes) during a policy query. While this is useful for kicking the tires in a development environment, putting optimization into the policy query path is not recommended. If users want to kick the tires with Partial Evaluation, we recommend running the `opa build` command. + +### Backwards Compatibilty + +* The `storage.Indexing` interface has been removed. Storage indexing has not been supported since 0.5.12. It was time to remove the interface. Custom store implementations that may have included no-op implementations of the interface can be updated. + +* The `ast.Array` type has been redefined a struct. Previously `ast.Array` was a type alias for `[]*ast.Term`. This change is backwards incompatible because slice operations can no longer be performed directly on values of type `ast.Array`. To accomodate, the `ast.Array` type now exports functions for the same operations. This change decouples callers from the underlying array implementation which opens up room for future optimizations. + +## 0.22.0 + +### Bundle Signing + +OPA now supports digital signatures for policy bundles. Specifically, a signed bundle is a normal OPA bundle that includes a file named ".signatures.json" that dictates which files should be included in the bundle, what their SHA hashes are, and of course is cryptographically secure. When OPA receives a new bundle, it checks that it has been properly signed using a key that OPA has been configured with out-of-band. Only if that verification succeeds does OPA activate the new bundle; otherwise, OPA continues using its existing bundle and reports an activation failure via the status API and error logging. For more information see https://openpolicyagent.org/docs/latest/management/#signing. Many thanks to @[ashish246](https://github.com/ashish246) who co-designed the feature and provided valuable input to the development process with his proof-of-concept [#1757](https://github.com/open-policy-agent/opa/issues/1757). + +### Optimization Levels + +`opa build` now supports multiple optimization levels. The first level (`--optimize=1`) enables constant folding (based on partial evaluation) that only inlines values that can be computed entirely at build time. The second level (`--optimize=2`) enables the existing (more aggressive) version of partial evaluation that eagerly inlines as much of the policy as possible. For more information on the optimization levels see the [Optimization Levels](https://www.openpolicyagent.org/docs/latest/policy-performance/#optimization-levels) section in the documentation. + +### Built-in Functions + +- `numbers.range` ([#2479](https://github.com/open-policy-agent/opa/issues/2479)) was added to support policies that need to generate a range of integers (e.g., a network port range). +- `semver.is_valid` and `semver.compare` ([#2538](https://github.com/open-policy-agent/opa/pull/2538/)) was added to support policies that need to validate semantic version numbers (authored by @[charlieegan3](https://github.com/charlieegan3)). + +### WebAssembly + +- All [String](https://www.openpolicyagent.org/docs/latest/policy-reference/#strings) built-in functions (except `sprintf`) are now implemented natively inside of Wasm-compiled policies. + +### Fixes + +- A few small issues in the Go integration and `rego` package examples have been resolved ([#2294](https://github.com/open-policy-agent/opa/issues/2294)) and [#2367](https://github.com/open-policy-agent/opa/issues/2367)) authored by @[gaga5lala](https://github.com/gaga5lala). +- The Kubernetes Admission Controller tutorial as been updated to work with recent versions of Kubernetes ([#2467](https://github.com/open-policy-agent/opa/issues/2467) authored by @[gaga5lala](https://github.com/gaga5lala)). +- A few issues in partial evaluation around negation inlining and partial rules have been resolved (e.g., [#2492](https://github.com/open-policy-agent/opa/issues/2492), [#2491](https://github.com/open-policy-agent/opa/issues/2491)). + +### Miscellaneous + +- OPA now supports IMDSv2 for the AWS metadata service. This improves the security posture of OPA deployments in AWS ([#2482](https://github.com/open-policy-agent/opa/issues/2482)) authored by @[nhw76](https://github.com/nhw76). +- Several improvements to the project documentation including a policy style discussion, an integration option comparison, and discussion of bootstrapping and fail-open versus fail-closed modes. +- The project's CI/CD infrastructure has been migrated to GitHub Actions. The new CI/CD infrastructure is designed and implemented to be portable and includes a number of quality-of-life improvements. +- End-to-end query latency with decision logging enabled has been improved by 10%-15% in real-world cases. + +### Backwards Compatibility + +* The `rego.Tracer` and `rego.EvalTracer` API's have been deprecated in favor of + the newer `rego.QueryTracer` and `rego.EvalQueryTracer` API. +* The `tester.Runner#SetCoverageTracer` API has been deprecated in favor of the + newer `test.Runner#SetCoverageQueryTracer` API. + +## 0.21.1 + +This release fixes [#2497](https://github.com/open-policy-agent/opa/issues/2497) where the comprehension indexing optimization produced incorrect results for nested comprehensions that close over variables in the outer scope. This issue only affects policies containing nested comprehensions that are recognized by the indexer (which is a relatively small percentage). + +This release also backports the GitHub Actions migration and a fix to the Wasm library build step. + +## 0.21.0 + +### Features + +* Decision log masks can now mutate decision log events. Previously, the masks could only erase data in the events. With this change, users can implement masks that obfuscate or add information to the decision log events before they are emitted. Thanks to @dkiser for implementing this feature [#2379](https://github.com/open-policy-agent/opa/issues/2379))! + +* This release contains a new built-in function for parsing X.509 Certificate Signing Requests (`crypto.x509.parse_certificate_request`). Thanks to @vivekbagade for implementing this feature [#2402](https://github.com/open-policy-agent/opa/issues/2402)! + +* This release adds support for aggregation and bit arithmetic operations for WebAssembly compiled policies. These functions no longer have to be provided by the host environment. + +### Fixes + +- cmd: Fix bug in --disable-inlining option parsing ([#2196](https://github.com/open-policy-agent/opa/issues/2196)) authored by @[Syn3rman](https://github.com/Syn3rman) +- docs: Improve terraform example to incorporate `child_modules` ([#1772](https://github.com/open-policy-agent/opa/issues/1772)) +- server: Fix panic caused by compiler misuse with bundles ([#2197](https://github.com/open-policy-agent/opa/issues/2197)) +- topdown: Fix incorrect memoization during partial evaluation ([#2455](https://github.com/open-policy-agent/opa/issues/2455)) +- topdown: Fix loss of precision in arithmetic and aggregate builtins ([#2469](https://github.com/open-policy-agent/opa/issues/2469)) + +### Miscellaneous + +* Thanks to @Syn3rman for implementing an improvement to our release process to automatically tag external contributors ([#2323](https://github.com/open-policy-agent/opa/issues/2323))! + +* The coverage and profiling tracers no longer require variable values from the evaluator. This change improves perfomance significantly when coverage or profiling is enabled and policies inspect large data sets. Benchmarks show anywhere from 0.5x to over 30x speedup depending on the policy. + +### Backwards Compatibility + +* `topdown.Tracer` has been deprecated in favor of a newer interface + `topdown.QueryTracer`. +* All tracers (regardless of interface implementation) will now only be checked + for being enabled at the beginning of query evaluation rather than on a + per-event basis. +* `topdown.BuiltinContext#Tracers` has been deprecated in favor of + `topdown.BuiltinContext#QueryTracers`. The older `Tracers` field will be `nil` + starting this release, and eventually removed. + +## 0.20.5 + +### Fixes + +- compile: Change name of result var for wasm binary ([#2441](https://github.com/open-policy-agent/opa/issues/2441)) +- format: Deep copy inputs to avoid mutating the caller's copy ([#2439](https://github.com/open-policy-agent/opa/issues/2439)) + +### Miscellaneous + +- docs: Add `opa_println` to wasm required imports + +## 0.20.4 + +### Fixes + +- format: Refactor wildcard names to rewrite early ([#2430](https://github.com/open-policy-agent/opa/issues/2430)) + +## 0.20.3 + +### Fixes + +- docs/content small output correction on terraform page ([#1772](https://github.com/open-policy-agent/opa/issues/1772)) +- format: Fix wildcards in nested refs + +## 0.20.2 + +### Fixes + +- format: Fix panic with else blocks and comments ([#2420](https://github.com/open-policy-agent/opa/issues/2420)) + +## 0.20.1 + +This release fixes an issue in the Docker image build. The +default ca-certificates were not being included becasue the Docker +image is FROM scratch now. + +## 0.20.0 + +### Major Features + +This release includes a number of features, optimizations, and bugfixes. + +#### Version Reporting + +OPA now determines the latest stable release version using +https://telemetry.openpolicyagent.org. The only information provided to the +telemetry service is the version (e.g., `0.20.0`), a UUIDv4 generated on +startup, and the build platform/architecture (e.g., `darwin, amd64`). This +feature is on by default in `opa run` however it can be easily disabled by +specifying `--skip-version-check` on the command-line. If you are inside the +REPL, type `help` to see the latest version information. If you are running OPA +as a server, OPA will log an INFO level message indicating if OPA is out of +date. Version checking is best-effort. Any errors that occur while communicating +with https://telemetry.openpolicyagent.org are only logged at DEBUG level. For +more information see https://openpolicyagent.org/docs/latest/privacy/. + +#### New `opa build` command + +The `opa build` command can now be used to package OPA policy and data files +into [bundles](https://www.openpolicyagent.org/docs/latest/management-bundles) +that can be easily distributed via HTTP. See `opa build --help` for details. +This change is backwards incompatible. If you were previously relying on `opa +build` to compile policies to wasm, you can still do so: + +```bash +# before v0.20.0 +opa build -d policy.rego 'data.example.allow' + +# v0.20.0 and newer +opa build policy.rego -e example/allow -t wasm +``` + +### Built-in Functions + +This release includes a number of new built-in functions: + +* `graph.reachable` for computing the transitive closure from edge sets. This + function allows users to write policies that traverse organization charts, + security groups, etc. (thanks to @jaspervdj-luminal!) +* `io.jwt.verify_rs512` and other variants (`rs`/`es`/`hs`/`ps`, `384`/`512`) + were added (thanks to @GBrawl!) +* `uuid.rfc4122` for generating UUIDv4s (thanks to @reneklootwijk!) + +This release also includes a few fixes to existing built-in functions: + +* `units.parse_bytes` now supports units without the `B` or `b` suffix (thanks to @GBrawl!) +* `io.jwt.verify_decode` now supports floating-point `nbf` and `exp` claims (thanks to @GBrawl!) +* `array.slice` clamping logic fixed to prevent panic ([#2320](https://github.com/open-policy-agent/opa/issues/2320)). + +### Operations + +* The `opa run` command now supports a `--diagnostic-addr` flag that causes the + server to expose the `/health` and `/metric` endpoint on a different address. + This makes it easier to secure sidecar deployments in Kubernetes because the + main API endpoints can be served on localhost and the diagnostic endpoints can + be served on 0.0.0.0 so that the kubelet and other components can access them + ([#2002](https://github.com/open-policy-agent/opa/issues/2002)). The envoy + tutorial has been updated to show this in action. + +* The AWS credential provided has been updated to support the standard + `AWS_SESSION_TOKEN` and `AWS_SECURITY_TOKEN` environment variables. These are + used when signing S3 bundle requests for an AWS IAM assumed role (thanks to + @kpiotrowski!) + +### WebAssembly + +This release includes a number of improvements for wasm compiled policies. + +* UTF-8 and UTF-16 strings are now fully supported in the internal string + representation ([#1885](https://github.com/open-policy-agent/opa/issues/1885)) +* Numeric values are implemented on top of arbitrary-precision floating point + numbers to avoid loss-of-precision issues. +* The arithemetic, set, array, and type checking built-in function categories + are now supported by the wasm library. This means they do not have to be + implemented by the language-specific opa-wasm SDKs. +* The set and object implementations now use a chained hash set under the hood + ([#2225](https://github.com/open-policy-agent/opa/issues/2225)) + +### Performance + +* OPA will attempt to index collections generated by comprehensions to ensure + linear runtime for policies performing "group-by" operations (e.g., inverting + an objects.) For more information see the [Policy Performance](https://www.openpolicyagent.org/docs/latest/policy-performance/) + page ([#2276](https://github.com/open-policy-agent/opa/issues/2276)). + +### Tooling + +* The OPA extension for VS Code now supports `Go To Definition` inside policies. + This feature uses the new `opa oracle find-definition` command. +* The `opa test` command now includes location information on trace output. +* The `opa fmt` command now preserves `else` block style when possible (thanks to @mikaelcabot!) + +### Documentation + +This release includes several improvements to the website and documentation. + +* Improved terraform tutorial example ([#1772](https://github.com/open-policy-agent/opa/issues/1772)) (thanks to @princespaghetti!) +* Fixed token validation logic in envoy tutorial example ([#2395](https://github.com/open-policy-agent/opa/issues/2395)) (thanks to @princespaghetti!) +* Usability issues on the frontpage have been resolved ([#2205](https://github.com/open-policy-agent/opa/issues/2205), [#2206](https://github.com/open-policy-agent/opa/issues/2206) (thanks to @arunbsar!) +* The [Policy Performance](https://www.openpolicyagent.org/docs/latest/policy-performance/) + page now includes resource utilization guidelines ([#1601](https://github.com/open-policy-agent/opa/issues/1601)) +* By popular demand, the "document model" explanation has been brought back into + existence. It now lives in the [Philosophy](https://www.openpolicyagent.org/docs/latest/philosophy/#the-opa-document-model) + section ([#2284](https://github.com/open-policy-agent/opa/issues/2284)). +* The [Ecosystem](https://www.openpolicyagent.org/docs/latest/ecosystem/) page + implements a simple sorting algorithm that ranks items by amount of related + content. +* The policy cheat sheet has been merged into the [Policy Reference](https://www.openpolicyagent.org/docs/latest/policy-reference/) page. + +### Fixes + +* REPL now correctly displays booleans in tabled output ([#2338](https://github.com/open-policy-agent/opa/issues/2338), thanks to @timakin!) +* Discovery now supports service configuration updates. This makes token refresh easier in distributed environments on AWS. ([#2058](https://github.com/open-policy-agent/opa/issues/2058)) +* Fixed compiler panic if body omitted from `else` statement ([#2353](https://github.com/open-policy-agent/opa/issues/2353)) +* Fixed panic in /health API with the envoy plugin ([#2396](https://github.com/open-policy-agent/opa/issues/2396)) +* Partial Evaluation no longer generates unsafe queries for certain negated expressions ([#2045](https://github.com/open-policy-agent/opa/issues/2045)) +* Partial Evaluation no longer saves an incorrect binding list in some cases ([#2368](https://github.com/open-policy-agent/opa/issues/2368)) +* Output variable analysis no longer visits closures. This makes the analysis easier to use outside of the safety check. +* Rules parsed from expressions now have location information set correctly. + +### Miscellaneous + +* If you are building OPA for debian systems, the Makefile now supports a `make + deb` target. The target requires `dpkg-deb` to be installed. Thanks to @keshto + for contributing this! +* OPA is now built, by default, with CGO disabled. Also, the default Docker + image (`openpolicyagent/opa`) is back to using `FROM scratch`. + +### Backwards Compatibility + +* An internal utility function that unmarshals JSON (`util.UnmarshalJSON`) has + been fixed to return an error if the input bytes contain garbage following a + valid JSON value. In the past, the `util.UnmarshalJSON` function would just + return the valid JSON value and ignore the garbage following it. This change + is backwards incompatible since clients that were previously transmitting bad + data will now receive an error, however, we think it's important to surface + errors rather than hide them ([#2331](https://github.com/open-policy-agent/opa/issues/2331)). + +* The Go plugin/shared library loading feature that was deprecated in v0.14.0 + has finally been removed completely. If you are interested in extending OPA, + see the [Extensions](https://www.openpolicyagent.org/docs/latest/extensions/) + for how to do so at compile-time ([#2049](https://github.com/open-policy-agent/opa/issues/2049)). + +* The `github.com/open-policy-agent/opa/metrics#Counter` interface has been + extended to require an `Add(uint64)` function. This change only affects users + that have implemented their own version of the + `github.com/open-policy-agent/opa/metrics#Metrics` interface (which is the + factory for counters.) + +* As mentioned above, the `opa build` command-line syntax has changed. We think + this is the right time to refresh the command and we are more confident that + the new syntax will remain stable going forward. + +### Deprecation + +* This release deprecates `opa test -l` flag. Since we now display the trace + with line information, this flag is no longer needed. + +* In the next release we plan to deprecate the `?watch` and `?partial` HTTP API + parameters. The `?watch` feature is unused and introduces significant + complexity in the server implementation. The `?partial` parameter lazily + invokes Partial Evaluation _inline_ with policy invocation. This is useful for + development and debug purposes, however, it's not recommended for enforcement + points ot use (since PE optimization can introduce significant latency.) Users + should rely on the new `opa build` command to perform PE on their policies. + See `opa build --help` for more information. + + +## 0.19.2 + +### Fixes + +- plugins: Fix race between manager and plugin startup ([#2343](https://github.com/open-policy-agent/opa/issues/2343)) + +## 0.19.1 + +### Fixes + +- cmd/fmt: Only list files if there were changes ([#2295](https://github.com/open-policy-agent/opa/issues/2295)) + +## 0.19.0 + +### New Parser + +This release includes a new parser implementation that resolves a number +of existing issues with the old parser. As part of implementing the new parser +a small number of backwards incompatible changes have been made. + +#### Backwards Compatibility + +The new parser contains a small number of backwards incompatible changes that +correct questionable behaviour from the old parser. These changes affect +a very small number of actual policies and we feel confident in the decision to +break backwards compatibility here. + +- Numbers no longer lose-precision [#501](https://github.com/open-policy-agent/opa/issues/501) +- Leading commas do not cause objects to lose values [#2198](https://github.com/open-policy-agent/opa/issues/2198) +- Rules wrapped with braces no longer parse [#2199](https://github.com/open-policy-agent/opa/issues/2199) +- Rule names can no longer contain dots/hyphens [#2200](https://github.com/open-policy-agent/opa/issues/2200) +- Object comprehensions now have priority over logical OR in all cases [#2201](https://github.com/open-policy-agent/opa/issues/2201) + +In addition there are a few small changes backwards incompatible changes in APIs: + +- The `message` field on `rego_parse_error` objects contains a human-readable description + of the parse error. The old parser would often report "no match found" to indicate + the input contained invalid syntax. The new parser has slightly more specific + errors. If you integrated with OPA and implemented error handling based on the + content of these human-readable error message strings, your integration may be affected. +- The `github.com/open-policy-agent/opa/format#Bytes` function has been removed (it was unused.) + +#### Benchmark Results + +The output below shows the Go `benchstat` result for master (5a5d2a42) compared to the new parser. + +``` +name old time/op new time/op delta +ParseModuleRulesBase/1-16 210µs ± 1% 4µs ± 1% -98.02% (p=0.008 n=5+5) +ParseModuleRulesBase/10-16 1.39ms ± 1% 0.03ms ± 0% -97.93% (p=0.008 n=5+5) +ParseModuleRulesBase/100-16 13.5ms ± 1% 0.3ms ± 1% -97.93% (p=0.008 n=5+5) +ParseModuleRulesBase/1000-16 148ms ± 5% 3ms ± 6% -97.77% (p=0.008 n=5+5) +ParseStatementBasicCall-16 141µs ± 5% 3µs ± 1% -97.92% (p=0.008 n=5+5) +ParseStatementMixedJSON-16 9.06ms ± 2% 0.07ms ± 1% -99.19% (p=0.008 n=5+5) +ParseStatementSimpleArray/1-16 131µs ± 6% 2µs ± 1% -98.10% (p=0.008 n=5+5) +ParseStatementSimpleArray/10-16 499µs ± 6% 7µs ± 2% -98.54% (p=0.008 n=5+5) +ParseStatementSimpleArray/100-16 4.00ms ± 2% 0.06ms ± 4% -98.58% (p=0.008 n=5+5) +ParseStatementSimpleArray/1000-16 42.0ms ± 3% 0.5ms ± 4% -98.70% (p=0.008 n=5+5) +ParseStatementNestedObjects/1x1-16 233µs ± 6% 4µs ± 3% -98.49% (p=0.008 n=5+5) +ParseStatementNestedObjects/5x1-16 514µs ± 0% 9µs ± 4% -98.33% (p=0.008 n=5+5) +ParseStatementNestedObjects/10x1-16 911µs ± 5% 14µs ± 5% -98.46% (p=0.008 n=5+5) +ParseStatementNestedObjects/1x5-16 4.24ms ± 1% 0.01ms ± 1% -99.82% (p=0.016 n=4+5) +ParseStatementNestedObjects/1x10-16 138ms ± 1% 0ms ± 1% -99.99% (p=0.008 n=5+5) +ParseStatementNestedObjects/5x5-16 714ms ± 0% 5ms ± 5% -99.26% (p=0.016 n=4+5) +ParseBasicABACModule-16 3.12ms ± 3% 0.04ms ± 4% -98.63% (p=0.008 n=5+5) + +name old alloc/op new alloc/op delta +ParseModuleRulesBase/1-16 99.2kB ± 0% 5.7kB ± 0% -94.30% (p=0.008 n=5+5) +ParseModuleRulesBase/10-16 600kB ± 0% 29kB ± 0% -95.16% (p=0.008 n=5+5) +ParseModuleRulesBase/100-16 5.72MB ± 0% 0.27MB ± 0% -95.34% (p=0.008 n=5+5) +ParseModuleRulesBase/1000-16 58.0MB ± 0% 2.7MB ± 0% -95.42% (p=0.008 n=5+5) +ParseStatementBasicCall-16 70.2kB ± 0% 5.0kB ± 0% -92.82% (p=0.008 n=5+5) +ParseStatementMixedJSON-16 3.64MB ± 0% 0.06MB ± 0% -98.34% (p=0.008 n=5+5) +ParseStatementSimpleArray/1-16 63.7kB ± 0% 4.8kB ± 0% -92.42% (p=0.008 n=5+5) +ParseStatementSimpleArray/10-16 205kB ± 0% 8kB ± 0% -96.00% (p=0.008 n=5+5) +ParseStatementSimpleArray/100-16 1.64MB ± 0% 0.05MB ± 0% -97.19% (p=0.008 n=5+5) +ParseStatementSimpleArray/1000-16 16.5MB ± 0% 0.4MB ± 0% -97.50% (p=0.008 n=5+5) +ParseStatementNestedObjects/1x1-16 98.6kB ± 0% 5.7kB ± 0% -94.22% (p=0.008 n=5+5) +ParseStatementNestedObjects/5x1-16 224kB ± 0% 9kB ± 0% -96.05% (p=0.008 n=5+5) +ParseStatementNestedObjects/10x1-16 381kB ± 0% 13kB ± 0% -96.63% (p=0.008 n=5+5) +ParseStatementNestedObjects/1x5-16 1.76MB ± 0% 0.01MB ± 0% -99.38% (p=0.008 n=5+5) +ParseStatementNestedObjects/1x10-16 56.2MB ± 0% 0.0MB ± 0% -99.97% (p=0.008 n=5+5) +ParseStatementNestedObjects/5x5-16 280MB ± 0% 4MB ± 0% -98.67% (p=0.008 n=5+5) +ParseBasicABACModule-16 1.27MB ± 0% 0.04MB ± 0% -97.08% (p=0.008 n=5+5) + +name old allocs/op new allocs/op delta +ParseModuleRulesBase/1-16 2.28k ± 0% 0.07k ± 0% -96.75% (p=0.008 n=5+5) +ParseModuleRulesBase/10-16 16.1k ± 0% 0.5k ± 0% -96.59% (p=0.008 n=5+5) +ParseModuleRulesBase/100-16 159k ± 0% 5k ± 0% -96.64% (p=0.008 n=5+5) +ParseModuleRulesBase/1000-16 1.62M ± 0% 0.05M ± 0% -96.72% (p=0.008 n=5+5) +ParseStatementBasicCall-16 1.36k ± 0% 0.05k ± 0% -96.25% (p=0.008 n=5+5) +ParseStatementMixedJSON-16 105k ± 0% 1k ± 0% ~ (p=0.079 n=4+5) +ParseStatementSimpleArray/1-16 1.34k ± 0% 0.04k ± 0% -97.09% (p=0.008 n=5+5) +ParseStatementSimpleArray/10-16 5.49k ± 0% 0.12k ± 0% -97.90% (p=0.008 n=5+5) +ParseStatementSimpleArray/100-16 47.8k ± 0% 0.8k ± 0% ~ (p=0.079 n=4+5) +ParseStatementSimpleArray/1000-16 481k ± 0% 8k ± 0% -98.33% (p=0.008 n=5+5) +ParseStatementNestedObjects/1x1-16 2.38k ± 0% 0.05k ± 0% -97.82% (p=0.008 n=5+5) +ParseStatementNestedObjects/5x1-16 6.02k ± 0% 0.12k ± 0% -97.94% (p=0.008 n=5+5) +ParseStatementNestedObjects/10x1-16 10.6k ± 0% 0.2k ± 0% -98.01% (p=0.008 n=5+5) +ParseStatementNestedObjects/1x5-16 51.2k ± 0% 0.1k ± 0% ~ (p=0.079 n=4+5) +ParseStatementNestedObjects/1x10-16 1.66M ± 0% 0.00M ± 0% -99.99% (p=0.008 n=5+5) +ParseStatementNestedObjects/5x5-16 8.16M ± 0% 0.07M ± 0% -99.13% (p=0.008 n=5+5) +ParseBasicABACModule-16 36.5k ± 0% 0.7k ± 0% -98.09% (p=0.008 n=5+5) +``` + +### Fixes and Enhancements + +- ast: Add rules/functions that contain errors to the type env ([#2155](https://github.com/open-policy-agent/opa/issues/2155)) +- ast: Fix panic when rule args contain call expressions ([#2081](https://github.com/open-policy-agent/opa/issues/2081)) +- ast: Fix bug in term rewritten when 'input' is passed as an argument ([#2084](https://github.com/open-policy-agent/opa/issues/2084)) +- bundle: Remove extra root name in bundle file ids ([#2117](https://github.com/open-policy-agent/opa/issues/2117)) +- cmd/fmt: Fix to always write formatted file to stdout ([#2235](https://github.com/open-policy-agent/opa/issues/2235)) +- cmd/test: --explain now turns on verbose output ([#2069](https://github.com/open-policy-agent/opa/issues/2069)) +- cmd/test: Default `-v` traces show notes and fails ([#2068](https://github.com/open-policy-agent/opa/issues/2068)) +- docs/website: Fix mobile docs nav menu ([#2074](https://github.com/open-policy-agent/opa/issues/2074)) +- format: Print var if wildcard is used multiple times ([#2053](https://github.com/open-policy-agent/opa/issues/2053)) +- plugins/bundle: Update the downloader's e-tag based on bundle activation ([#2220](https://github.com/open-policy-agent/opa/issues/2220)) +- plugins: Add support to specify bearer token path (which enables token refresh) ([#2241](https://github.com/open-policy-agent/opa/issues/2241)) +- profiler: Fix panic when location is missing by grouping expressions missing a location ([#2134](https://github.com/open-policy-agent/opa/issues/2134)) +- rego: Avoid re-using transactions in compiler ([#2197](https://github.com/open-policy-agent/opa/issues/2197)) +- repl: Add unset-package command ([#2140](https://github.com/open-policy-agent/opa/issues/2140)) +- server: Do not return partial modules /v1/policies output ([#2036](https://github.com/open-policy-agent/opa/issues/2036)) +- server: Specify partial evaluation namespace to avoid conflicts ([#2247](https://github.com/open-policy-agent/opa/issues/2247)) +- topdown: Add time.add_date builtin ([#1990](https://github.com/open-policy-agent/opa/issues/1990)) +- topdown: Fix partial evaluation to save comprehensions correctly ([#2243](https://github.com/open-policy-agent/opa/issues/2243)) +- topdown: Improve pretty trace location details ([#2143](https://github.com/open-policy-agent/opa/issues/2143)) +- topdown: Include HTTP response headers in `http.send` output ([#2238](https://github.com/open-policy-agent/opa/issues/2238)) +- [Multiple](https://github.com/open-policy-agent/opa/commit/3eeb09c3e83749aff31e15bdfff5d82f3224c102) [important](https://github.com/open-policy-agent/opa/commit/29d8fbbef6facc96d03be3e07473d12e38acd843) [improvements](https://github.com/open-policy-agent/opa/commit/c5c85795aaa3701763f98d16308c5944f05f3da4) [to `http.send()`](https://github.com/open-policy-agent/opa/commit/ce92d19f655efffd6bda26006a2f4898cbdb69ed) [thanks to](https://github.com/open-policy-agent/opa/commit/351a7313df35e8de9e9474fd56a1a905cd51e0c1) @jpeach + +### Miscellaneous + +- [Added `man` target in the Makefile for `man` page generation!](https://github.com/open-policy-agent/opa/commit/4c81aa75c05e4dd69408b9c879be40f9f4369a2c) (thanks to @olivierlemasle) +- [Added Sublime Text syntax file](https://github.com/open-policy-agent/opa/blob/master/misc/syntax/sublime/rego.sublime-syntax) +- [Added link to Emacs mode for Rego](https://github.com/psibi/rego-mode) (thanks to @psibi) +- [Added net.cidr_contains_matches built-in function](https://github.com/open-policy-agent/opa/pull/2221/commits/6ae4ed9e6578ffb272604a79b1ef9a944cda7782) +- [Improved support for registering custom built-in functions](https://github.com/open-policy-agent/opa/blob/84b61c647a0d76e62043d6f52510411e8b00d2f0/docs/content/extensions.md) + +## 0.18.0 + +### Features + +- Add `opa bench` and `opa test --bench` sub commands for benchmarking policy evaluation. ([#1424](https://github.com/open-policy-agent/opa/issues/1424)) +- Permit verifying JWT's with a public key +- `http.send` improvements: + - Allow for skipping TLS verification via `tls_insecure_skip_verify` option + - Add `Host` header support + +### New Built-in Functions + +- Bitwise operators ([#1919](https://github.com/open-policy-agent/opa/issues/1919)) + - `bits.or` + - `bits.and` + - `bits.negate` + - `bits.xor` + - `bits.lsh` + - `bits.rsh` +- `json.remove` which works similar to `object.remove` but supports a JSON pointer path. + +### Fixes +- docs: Render tutorials as list ([#2071](https://github.com/open-policy-agent/opa/issues/2071)) +- ast: Fix type check for objects with non-json keys ([#2183](https://github.com/open-policy-agent/opa/issues/2183)) +- ast: Return an error when parsing an empty module ([#2054](https://github.com/open-policy-agent/opa/issues/2054)) +- docs: Fix broken PAM module link ([#2113](https://github.com/open-policy-agent/opa/issues/2113)) +- docs: Fix code fence in kubernetes-primer.md ([#2177](https://github.com/open-policy-agent/opa/issues/2177)) +- topdown: Invoke iterator when evaluating negation ([#2142](https://github.com/open-policy-agent/opa/issues/2142)) +- Correct checkptr errors found with Go 1.14 +- `opa parse`: fix panic when parsing invalid JSON + +### Compatibility Notes + +- The `ast.ParseModule` helper will now return an error if an empty module is provided. + Previously it would return a `nil` error and `nil` module. ([#2054](https://github.com/open-policy-agent/opa/issues/2054)) +- The `cmd` and `tester` packages in OPA will now require Go 1.13+ to compile. Most library users should be unaffected. + +### Miscellaneous + +- bundle: Dedicate `policy.wasm` for the compiled policy. + +## 0.17.3 + +### Fixes + +- vendor: Update xxhash to workaround checkptr errors with Go 1.14 +- cmd/parse: Fix panic when parsing encounters an error + +## 0.17.2 + +### Fixes + +- Add location information into pretty printed trace output. ([#2070](https://github.com/open-policy-agent/opa/issues/2070)) +- Add timeout for `http.send` builtin ([#2099](https://github.com/open-policy-agent/opa/issues/2099)) +- build: Force module mode and using only the vendor directory ([#2063](https://github.com/open-policy-agent/opa/issues/2063)) +- cover: Exclude `some` expressions in coverage report ([#1972](https://github.com/open-policy-agent/opa/issues/1972)) +- docs: How to say "ray-go" ([#2106](https://github.com/open-policy-agent/opa/issues/2106)) +- topdown: Make http.send() caching use full request ([#1980](https://github.com/open-policy-agent/opa/issues/1980)) +- topdown: Wrap all builtin functions for errors normalization ([#2101](https://github.com/open-policy-agent/opa/issues/2101)) +- topdown: http.send use provided CA without client certs ([#1976](https://github.com/open-policy-agent/opa/issues/1976)) + +### Miscellaneous + +- Add `object` manipulation built-ins +- docs: Add link to Rego Playground in table of contents +- docs: Update tutorial with note about consistency +- topdown: Export builtin implementations outside the package + +## 0.17.1 + +### Fixes + +- ast: Fix rewriting vars in rule args ([#2080](https://github.com/open-policy-agent/opa/issues/2080)) + +## 0.17.0 + +### Major Features + +- This release improves partial evaluation to avoid saving statements when they + do not depend on unknowns (e.g., comprehensions, references that require + materializing the full extent of partial sets/objects, etc.) Also, expressions + containing `with` statements are partially evaluated now. + +- This release lets policy authors to de-reference calls (and other terms) + without assigning the result to an intermediate variable. For example, instead + of writing `a := f(x); a.foo == 1` users can now write `f(x).a == 1` directly. + Thanks @jaspervdj-luminal! + +### New Built-in Functions + +This release includes the following new built-in functions: + +- `object.get` built-in function to lookup object keys with a fallback. +- `crypto.md5`, `crypto.sha1`, `crypto.sha256` built-in functions to hash strings. + +### Compatibility Notes + +- The `glob.match` built-in function was not defaulting the delimiter to "." + like the documentation described. This was fixed in + [#2061](https://github.com/open-policy-agent/opa/pull/2061) however the fix + is not backwards compatible. If you are using the `glob.match` built-in + function, you should ensure that a delimiter is being supplied. A search of + .rego files on GitHub only revealed a few instances of the `glob.match` in-use + so we decided to err towards fixing the broken behaviour rather than + preserving buggy behaviour going forward. + +- Related to the fix for [#2031](https://github.com/open-policy-agent/opa/issues/2031) + and the changes with OPA v0.16.0 to use `/` separated `path`'s with + the decision log plugin API. The decision logger will no longer modify + the `server.Info#Path` field. Older versions would substitute `.` for + `/` but this was causing incorrect results. As of v0.16.0 the server has + been updated to provide the correct paths so REST API users are unaffected. + Golang API users of the `plugins.log.Logger#Log` interface may be impacted + if passing `ast.Ref` style strings as a path as it will no longer be changed + to `/` separated. Callers need to do any transformation beforehand. + +### Fixes + +- docs: Update Kubernetes apiVersions to use `apps/v1` instead of `extensions/v1` ([#1977](https://github.com/open-policy-agent/opa/issues/1977)) +- plugins/logs: Leave the path unchanged for decisions ([#2031](https://github.com/open-policy-agent/opa/issues/2031)) +- plugins/bundle: Include last successful request timestamp in status ([#2009](https://github.com/open-policy-agent/opa/issues/2009)) +- plugins/bundle: Pass copy of status to bulk listeners ([#1962](https://github.com/open-policy-agent/opa/issues/1962)) +- rego: Fix panic when partial evaluating with tracers ([#2007](https://github.com/open-policy-agent/opa/issues/2007)) +- rego: Propagate custom builtins to `PartialResult` ([#1792](https://github.com/open-policy-agent/opa/issues/1792)) +- server: Update health check to use plugin status ([#2010](https://github.com/open-policy-agent/opa/issues/2010)) +- topdown: Correct glob default delimeter ([#2039](https://github.com/open-policy-agent/opa/issues/2039)) + +### Miscellaneous + + +- ast: Do not index expressions containing with statements +- ast: Fix panic in module parsing +- ast: Improve visitor performance by avoiding heap allocations +- cmd/build: return error if there is more than one positional argument +- docs: Fix live docs button size +- docs: Fix token validation example in Envoy tutorial + +## 0.16.2 + +This release includes an important bugfix for users that enable +tracing and use the "pretty" trace formatter. + +- topdown: Fix bug in var rewriting during trace formatting ([#2022](https://github.com/open-policy-agent/opa/issues/2022)) + +## 0.16.1 + +### Fixes + +- Fix for `*-rootless` Docker images `USER` being set incorrectly ([#1982](https://github.com/open-policy-agent/opa/issues/1982)) + +## 0.16.0 + +### New Built-in Functions + +- Add `json.filter` to mask/filter nested fields ([#1617](https://github.com/open-policy-agent/opa/issues/1617)) +- Add `net.cidr_expand` to generate CIDR hosts + +### Fixes + +- Reduce server latency for indexed policies by ~30-40% by caching prepared queries across requests ([#1958](https://github.com/open-policy-agent/opa/issues/1567)) +- Improve type checker error and trace output readability ([#1430](https://github.com/open-policy-agent/opa/issues/1430) and [#1208](https://github.com/open-policy-agent/opa/issues/1208)) +- Re-create service clients to pickup certificate changes ([#1898](https://github.com/open-policy-agent/opa/issues/1898)) +- Report full system path for bundle file locations ([#1796](https://github.com/open-policy-agent/opa/issues/1796)) +- Add `status.console` option to log Status messages to console ([#1937](https://github.com/open-policy-agent/opa/issues/1937)) +- Fix `io.jwt.decode_verify` to support multiple keys in JWKS ([#1901](https://github.com/open-policy-agent/opa/issues/1901)) +- Fix `path` decision log field for queries against "/data" ([1532](https://github.com/open-policy-agent/opa/issues/1532)) + +This release also includes: + +- Documentation improvements on how to use the `io.jwt.*` built-in functions for token verification +- Metrics for bundle processing and activation (e.g., read, parse, and compile times) +- Better parse metric reporting in the server + +### Compatibility Notes + +- The fix for #1532 required a backwards incompatible change for v0 and v1 + queries against "/data". This only affects queries against the exact path + "/data", not paths prefixed with "/data/", e.g., "/data/example/allow". Since + queries against "/data" are rare and normally only seen in development + environments, this is a low-impact change. As part of this change, the + `server.Info#Path` field has been changed to use slash-separated paths instead + of the string representation of Rego references (i.e., a dotted path rooted at + "data"). If you are registering a logging callback directly against the server + (e.g., by calling `server.Server#WithDecisionLoggerWithErr`) you will have to + update your logging callback to deal with the new path format. Decision log + consumers should treat a missing/empty `path` field as a query against + "/data". + +## 0.15.1 + +In this release we reached a milestone for Wasm: any Rego policy can +be compiled to Wasm now! In the next few weeks we will focus on +expanding on the set of built-ins supported out-of-the-box and inside +the NodeJS SDK. + +### Fixes + +- bundle: Make the DirectoryLoader public ([#1840](https://github.com/open-policy-agent/opa/issues/1840)) +- topdown: Add raw_body parameter to http.send ([#1903](https://github.com/open-policy-agent/opa/issues/1903)) +- wasm: Update planner to support with keyword ([#1116](https://github.com/open-policy-agent/opa/issues/1116)) + +### Miscellaneous + +- ast: Fix NoWith helper on exprs +- ast: Fix module JSON unmarshalling +- build: Fix build-release.sh by removing obsolete make deps command +- docs: Add JWT verification examples to reference +- docs: Fix introduction to refer to rules consistently +- topdown: Add environment variable to dump tests to disk +- topdown: Provide rewritten query vars in traces +- types: Fix constant select on array types +- wasm: Add support for built-in functions +- wasm: Extend wasm library to support shallow copying +- wasm: Fix JSON string lexing and parsing +- wasm: Fix object insertion operation +- wasm: Fix opa_json_dump to terminate keywords properly +- wasm: Fix opa_set_add to set next element correctly +- wasm: Fix planner to check call expression for false return value +- wasm: Fix planning of virtual document extent +- wasm: Improve calling convention of eval() function +- wasm: Improve planner to reuse local variables +- wasm: Fix planner to plan default rule bodies +- wasm: Remove unnecessary condition statements for scans +- wasm: Store parsed numbers as strings +- website: Replace homepage with new version + +## 0.15.0 + +This release includes many small improvements and bug fixes. + +### Built-in Functions + +This release includes a few new built-in functions for string +manipulation: + +- `trim_left`, `trim_right`, `trim_prefix`, and `trim_suffix` (thanks @hasit) +- `regex.find_all_string_submatch_n` and `strings.replace_n` (thanks @kenfdev) + +### Fixes + +- tester: Fix --timeout to apply to each test case ([#1788](https://github.com/open-policy-agent/opa/issues/1788)) +- ast: Check for undefined functions before safety check ([#1141](https://github.com/open-policy-agent/opa/issues/1141)) +- ast: Fix object corruption during local rewrite ([#1852](https://github.com/open-policy-agent/opa/issues/1852)) +- ast: Fix virtual predicate used for rule index build ([#1863](https://github.com/open-policy-agent/opa/issues/1863)) +- discovery: Fix log level message when on HTTP 304 ([#1826](https://github.com/open-policy-agent/opa/issues/1826)) +- docs: Update Kubernetes primer test to avoid false-positives ([#1794](https://github.com/open-policy-agent/opa/issues/1794)) +- repl: Fix unknown argument processing ([#1670](https://github.com/open-policy-agent/opa/issues/1670)) +- topdown: Fix namespacing to use caller bindings ([#1814](https://github.com/open-policy-agent/opa/issues/1814)) +- topdown: Fix units.parse_bytes implementation to use int64 ([#1815](https://github.com/open-policy-agent/opa/issues/1815)) +- topdown: Fix base document dereference with composite ([#1057](https://github.com/open-policy-agent/opa/issues/1057)) +- wasm: Add support for comprehensions ([#1120](https://github.com/open-policy-agent/opa/issues/1120)) +- wasm: Add support for full virtual document model ([#1117](https://github.com/open-policy-agent/opa/issues/1117)) +- wasm: Remove memory.grow calls on every malloc ([#1121](https://github.com/open-policy-agent/opa/issues/1121)) + +### Miscellaneous + +- build: Migrate to Go modules from Glide for dependency management +- build: Fix *-debug docker images to be ":debug" tag based +- ast: Replace "var" with "some" in SomeDecl#String +- ast: Add map of rewritten vars to Compiler +- topdown: Add API to disable rule indexing for evaluation +- bundle: Add more details to manifest root errors +- bundle: Ensure data paths use `/` separators for key +- bundle: Fix for overwriting data file keys +- cmd: Ensure all errors are in JSON formatted CLI output +- cmd: Add source output format for partial eval +- cmd: Fix opa eval to specify profiler tracer correctly +- discovery: Support `resource` configuration option +- rego: Don't propagate non-threadsafe fields from Rego to preparedQuery + +## 0.14.2 + +- topdown: Fix namespacing to use caller bindings ([#1814](https://github.com/open-policy-agent/opa/issues/1814)) +- file/loader: Standardize on forward slash paths + +## 0.14.1 + +- Fix a number of links in the OPA documentation. +- Fix issue with bundle root path comparisons on Windows. + +## 0.14.0 + +This release includes a large number of improvements to the docs as +well as performance optimizations that improve several end-to-end +benchmarks by ~25%. Also, the `opa eval` and other sub-commands now +accept a `-b` or `--bundle` flag that tell OPA to treat file paths as +bundles (either .tar.gz or directories). This improves behaviour in +large or mixed workspaces. + +### Compatibility Notes + +- Status API messages now include a dump of OPA's Prometheus metric + registry. This increases the Status API message size significantly + (~6KB). If you are indexing the Status API messages, consider + removing the metrics. Nonetheless, for Status API implementations, + having access to the Prometheus metrics is important for monitoring + the health of the OPAs. + +### Built-in Functions + +This release includes a few improvements to built-in functions: + +* A new function for converting SI strings (e.g., "10MB") to numbers: + `units.num_bytes(x)` + ([#1561](https://github.com/open-policy-agent/opa/issues/1561)). This + is useful in the context of Kubernetes if you need to deal with + resource limits and requests. + +* The `io.jwt.verify_*` functions have been extended to support JWKs. + +This release also improves support for providing custom built-in +functions to OPA. See the extensions documentation on openpolicyagent.org. + +### Fixes + +- ast, rego: Refactor unsafe built-in handling ([#1666](https://github.com/open-policy-agent/opa/issues/1666)) +- ast: Fix ordering of rule type checking errors ([#1620](https://github.com/open-policy-agent/opa/issues/1620)) +- ast: Update rule head to track assignments ([#1541](https://github.com/open-policy-agent/opa/issues/1541)) +- ast: Fix bug that allowed recursion in dynamic refs ([#1565](https://github.com/open-policy-agent/opa/issues/1565)) +- ast: Fix parsing of var-like scalars ([#1582](https://github.com/open-policy-agent/opa/issues/1582)) +- docs: Add note about benchmark result page ([#1275](https://github.com/open-policy-agent/opa/issues/1275)) +- docs: Update to show undefined example with != ([#1626](https://github.com/open-policy-agent/opa/issues/1626)) +- docs: Update to use live blocks ([#1650](https://github.com/open-policy-agent/opa/issues/1650)) +- format: Fix formatter to start line after writing comments ([#1560](https://github.com/open-policy-agent/opa/issues/1560)) +- loader: Update to accept file:// URLs. ([#1505](https://github.com/open-policy-agent/opa/issues/1505)) +- server: Improve decision log-related error messages ([#1367](https://github.com/open-policy-agent/opa/issues/1367)) + +### Miscellaneous + +- Add support for fuzzing the ast package in CI +- Add search bar powered by Algolia to the docs +- Add "type" field to decision log events sent to the console +- Add support for := assignments at file level +- Add build commit and version to runtime info +- Fix moduleLoader to copy returned parsed Modules +- Fix panic in /health?bundle=true +- Update the --plugin-dir flag as deprecated +- Update formatter to preserve rule assigmemnts +- Update metrics object to be thread-safe +- Support loading bundles and files w/ Rego API + +## 0.13.5 + +- Fix panic in OPA HTTP server with `/health?bundle=true` when + using bundles loaded from CLI ([#1703](https://github.com/open-policy-agent/opa/issues/1703)). + +## 0.13.4 + +- Fix panic in OPA HTTP server caused by concurrent map writes ([#1666](https://github.com/open-policy-agent/opa/issues/1666)) + +## 0.13.3 + +### Fixes + +- Fix bundle plugin to report error in case bundle manifest roots overlap ([#1635](https://github.com/open-policy-agent/opa/issues/1635)) + +## 0.13.2 + +This release updates OPA to use the latest stable Golang release +(1.12.8) that includes important fixes in the net/http package. See +this +[golang-nuts](https://groups.google.com/forum/#!topic/golang-nuts/fCQWxqxP8aA) +group message for details. + +## 0.13.0 + +### Multiple Bundles + +This release adds support for downloading multiple bundles to OPA +using the new `bundles` key in the configuration. APIs that include +bundle information have been updated to support multiple bundles: + +* Status API messages include the status and revision of each bundle. +* Decision Log API messages include the revision of each bundle. +* Data API responses include the revision of each bundle in the + provenance field if requested. +* Health API waits for all bundles to activate if requested. + +These changes are **backwards compatible**. If you are using the +existing `bundle` key in the configuration, you will not see any +changes in the APIs listed above. + +We recommend that you switch to the new `bundles` key and update +consumers of the above APIs to support multiple bundles. + +For more information on bundles see the [this +page](https://www.openpolicyagent.org/docs/latest/bundles/) in the OPA +documentation. + +### Console Decision Logger + +This release adds support for emitting decision logs to stdout. This +is useful for shipping decision logs directly to existing logging +backends. + +You can enable console decision logging on the command line: + +``` +opa run --server --set decision_logs.console=true +``` + +Console decision logging can be enabled alongside normal and custom +decision logging. + +### Fixes + +- ast: Report safety errors on line where expression starts ([#1497](https://github.com/open-policy-agent/opa/issues/1497)) +- ast: Update rule index to support glob.match ([#1496](https://github.com/open-policy-agent/opa/issues/1496)) +- bundle: Add support for loading YAML files from bundles ([#1471](https://github.com/open-policy-agent/opa/issues/1471)) +- bundle: Cache compiler on storage context ([#1515](https://github.com/open-policy-agent/opa/issues/1515)) +- cmd: Fix double print of rego errors ([#1518](https://github.com/open-policy-agent/opa/issues/1518)) +- docs: Add section on how to express "FOR ALL" in Rego ([#1307](https://github.com/open-policy-agent/opa/issues/1307)) +- docs: Fix mention of reference head var ([#1477](https://github.com/open-policy-agent/opa/issues/1477)) +- docs: Remove cast_xyz functions from docs ([#1405](https://github.com/open-policy-agent/opa/issues/1405)) +- server: Pass transaction in decision log event ([#1543](https://github.com/open-policy-agent/opa/issues/1543)) +- storage: Add safety checks to in-memory store ([#1594](https://github.com/open-policy-agent/opa/issues/1594)) +- topdown: Fix corrupt object panic caused by copy propagation ([#1177](https://github.com/open-policy-agent/opa/issues/1177)) +- topdown: Fix virtual cache to allow composite key terms ([#1197](https://github.com/open-policy-agent/opa/issues/1197)) + +### Miscellaneous + +- OPA sets the User-Agent header in requests made to services. +- `openpolicyagent/opa:edge` Docker images are available now. The + `edge` tag refers to the tip of master. +- OPA supports signing and encoding of JWTs. See [Token + Signing](https://www.openpolicyagent.org/docs/latest/language-reference/#token-signing) + for details. +- Prometheus metrics include cancelled HTTP requests. +- Compiler exposes optional unsafe built-in function check. +- Discovery query can be configured now. See [Discovery + Configuration](https://www.openpolicyagent.org/docs/latest/configuration/#discovery) + for details. +- Optimized rewriteDynamics stage in compiler to reduce allocations. +- OPA subcommands support "fails" explanation now. The "fails" + explanation is similar to the "notes" explanation except that it + prints Fail events instead of Note events. This is useful for among + other things, debugging test failures. +- Partial evaluation can disable inlining on specific virtual + documents. If set correctly this can improve partial evaluation + performance significantly because OPA can avoid computing + cross-products. +- `rego.Rego#PrepareForEVal` now times partial evaluation properly. +- The diagnostics feature deprecated in v0.10.1 has been removed. + +## 0.12.2 + +### Fixes + +- Fix performance impact of bundle activation on policy queries ([#1516](https://github.com/open-policy-agent/opa/issues/1516)) +- Fix log masking to use correct transaction ([#1551](https://github.com/open-policy-agent/opa/pull/1551)) + +## 0.12.1 + +### Fixes + +- Fix deadlock caused by log masking decision evaluation ([#1543](https://github.com/open-policy-agent/opa/issues/1543)) + +### Miscellaneous + +- Add decision log event for undefined decision on `POST /` endpoint + +## 0.12.0 + +This release includes two new features and an important bug fix. + +### Decision Log Masking + +This release includes an important feature for protecting sensitive +information in decision logs: masking. With the new decision log +masking feature you can configure OPA to remove sensitive information +from the `input` and `result` fields of decision log events. See the +[Decision Log](https://www.openpolicyagent.org/docs/edge/decision-logs/#masking-sensitive-data) documentation for details. + +### AWS Signing for Bundle Downloads + +This release adds support for signing bundle download requests using +an AWS signing scheme. This feature allows you to configure OPA to +download bundles directly from S3. See the [Configuration](https://www.openpolicyagent.org/docs/edge/configuration/#aws-signature) +documentation for details. + +### Fixes + +* server: Fix deadlock caused by leaked write transaction ([#1478](https://github.com/open-policy-agent/opa/issues/1478)) + +### Miscellaneous + +- server: Add request headers to authorization input ([#1456](https://github.com/open-policy-agent/opa/issues/1456)) +- rego: Add time zone support to time/date built-in functions +- eval: Add --instrument flag for profiling evaluation via command line + +## 0.11.0 + +### Compatibility Notes + +This release includes a few small but backward incompatible +changes: + +* The compiler will reject functions that redeclare arguments. A + search of public .rego files on GitHub only returned one result + which was contained in the OPA documentation. For example: + + ``` + f(x) { + x := 1 # bad: redeclaration of 'x' + x == 1 # ok + } + ``` + +* Errors returned by built-in calls are no longer coded as + `eval_internal_error`. Instead they are returned as + `eval_builtin_error`. This change is made so callers can + differentiate between actual internal errors and built-in errors + that are result of bad inputs from the policy. + +* The `ast.QueryCompiler#WithInput` function and + `ast.QueryContext#Input` field have been removed because they were + unused and had no affect. + +* The `ast.Compiler` and `ast.QueryCompiler` functions to register + extra changes now require a stage and metric name. + +### Major Features + +This release includes a few notable features and improvements: + +* The `some` keyword allows you to declare local variables to avoid + namespacing issues. See the [Some + Keyword](https://www.openpolicyagent.org/docs/edge/how-do-i-write-policies/#some-keyword) + section in the documentation for more detail. + +* The `opa test`, `eval`, REPL, and HTTP API have been extended with a + new explanation mode for filtering tracing notes. This makes it + easier to see the output of `trace(msg)` calls from your policy. + +* The WebAssembly (Wasm) compiler has been extended to include support for + compiling rules into Wasm. Previously the compiler relied on partial + evaluation to inline all rules. In some cases this is not possible + due to limitations on Rego queries. In coming releases, the Wasm + support will be extended to cover the entire language. + +* The `rego` package has been extended to support prepared + queries. Prepared queries cache the parsed and compiled query ASTs + for re-use across multiple `Eval` calls. For small policies the + speedup can be significant. See the [GoDoc](https://godoc.org/github.com/open-policy-agent/opa/rego#example-Rego-PrepareForEval) for details. + +### Fixes + +- Add Kubernetes admission control debugging tips ([#1039](https://github.com/open-policy-agent/opa/issues/1039)) +- Add docs on health check API endpoint ([#1086](https://github.com/open-policy-agent/opa/issues/1086)) +- Add hardened configuration example to security page ([#1172](https://github.com/open-policy-agent/opa/issues/1172)) +- Add support for with keyword stacking ([#802](https://github.com/open-policy-agent/opa/issues/802)) +- Fix type inferencing on object keys ([#1361](https://github.com/open-policy-agent/opa/issues/1361)) +- Fix simple Kubernetes deployment example ([#874](https://github.com/open-policy-agent/opa/issues/874)) +- Fix bug in data mocking that resulted in wrong iteration behavior ([#1261](https://github.com/open-policy-agent/opa/issues/1261)) +- Fix bug in set deep copy that caused panic ([#1406](https://github.com/open-policy-agent/opa/issues/1406)) +- Fix bug in REPL that prevented rules from being declared ([#1104](https://github.com/open-policy-agent/opa/issues/1104)) + +### Miscellaneous + +- docs: Better documentation for providing the `input` document over HTTP ([#1293](https://github.com/open-policy-agent/opa/issues/1293)) +- docs: Add note about HTTP_PROXY friends ([#1410](https://github.com/open-policy-agent/opa/issues/1410)) +- Add CLI config overrides and ENV injection +- Add additional compiler metrics for each stage +- Add an “edge” release to the docs +- Add param to include bundle activation in /health response +- Add provenance query output +- Add support for graceful shutdown of OPA server +- Improve discovery feature documentation +- Make `json` logs the default and add `json-pretty` +- Raise error when loading empty module in bundle +- Return eval_builtin_error instead of eval_internal_error +- Rewrite == to = in queries passed to the compile API +- docs: Update bundle docs with caching info +- Update logrus to 1.4.0 +- server: Add early exit on PUT /v1/policies +- topdown: Fix set unification partial eval bug +- topdown: Omit rule body from enter/redo events + +## 0.10.7 + +This release publishes the Hugo-based documentation to GitHub Pages :tada: + +### Fixes + +- Add `array.slice` built-in function ([#1243](https://github.com/open-policy-agent/opa/issues/1243)) +- Add `net.cidr_contains` and `net.cidr_intersects` built-ins + ([#1289](https://github.com/open-policy-agent/opa/issues/1289)). This + change deprecates the old `net.cidr_overlap` built-in function. The + latter will be supported for backwards compatibility but new + policies should refer to `net.cidr_contains`. + +### Miscellaneous + +- Bump kube-mgmt container version to 0.8 in tutorial +- Remove unnecessary resizing allocs from AST set and object +- Add Kubernetes Admission Control guide + +## 0.10.6 + +This release migrates the OPA documentation over to Hugo (from +GitBook). Going forward the OPA documentation will be generated using +Hugo and hosted on Netlify (instead of GitHub Pages). The Hugo/Netlify +stack brings us inline with the goal for other CNCF projects and +provides nice features like "preview before merge". + +This release includes a small but backwards incompatible change to the +`http.send` built-in. Previously, `http.send` would _always_ decode +responses as JSON even if the Content-Type was unset or explicitly not +JSON. If you were previously relying on HTTP responses that did not +set the Content-Type correctly, you will need to update your policy to +pass `"force_json_decode": true` as in the `http.send` parameters. + +### Fixes + +- Fix panic in mod operation ([#1245](https://github.com/open-policy-agent/opa/issues/1245)) +- Fix eval tree enumeration to return errors ([#1272](https://github.com/open-policy-agent/opa/issues/1272)) +- Fix http.send to handle non-JSON responses ([#1258](https://github.com/open-policy-agent/opa/issues/1258)) +- Fix backticks in SSH example that were causing problems ([#1260](https://github.com/open-policy-agent/opa/issues/1260)) +- Fix IAM examples to use regex instead of glob syntax ([#1282](https://github.com/open-policy-agent/opa/issues/1282)) + +### Miscellaneous + +- Add support to register custom stages in the compiler +- Add rootless Docker image stream +- Improve hash distribution on objects +- Reduce number of allocs in set membership implementation +- docs: Add homebrew install instruction to the Getting Started tutorial +- docs: Many improvements around := vs ==, best practices, cheatsheet, etc. +- cmd: Add --fail-defined flag to eval subcommand +- server: Fix patch path escaping + +## 0.10.5 + +* These release contians a small but backwards incompatible change to + the custom decision logger API. Custom decision loggers can now + return an error which will cause the OPA to fail-closed. + +### Fixes + +- Fix substring built-in bounds checking ([#1235](https://github.com/open-policy-agent/opa/issues/1235)) +- Add trailing newlines when pretty printing API responses +- Add default Go metrics to Prometheus +- Add pprof endpoint to HTTP server + +## 0.10.4 + +* This release adds support for scoping bundles to specific roots + under `data`. This allows bundles to be used in conjunction with + sidecars like `kube-mgmt` that load local data and policy into + OPA. See the [Bundles](https://www.openpolicyagent.org/docs/latest/management-bundles) + page for more details. + +* This release includes a small but backwards incompatible change to + the Decision Log event format. Instead of including the OPA version + as a top-level field, the OPA version is included in the labels. The + OPA version field was only added in v0.10.3 so this should not + impact many consumers. + +### Fixes + +- Add coverage support to `opa eval` sub-command +- Fix path checking in server to prevent overlapping base and virtual docs ([#1207](https://github.com/open-policy-agent/opa/issues/1207)) +- Fix cmd integration tests to cleanup plugin directory ([#1185](https://github.com/open-policy-agent/opa/issues/1185)) +- Improve TLS support in `http.send` ([#1067](https://github.com/open-policy-agent/opa/issues/ + +## 0.10.3 + +* This release includes support for authentication via client + certificates (thanks @srenatus!) For improvements to authentication + see [#1163](https://github.com/open-policy-agent/opa/issues/1163). + +* This release includes a backwards incompatible change to the + plugin interface. Specifically, when plugins are registered, callers + must provide a factory that can _validate_ configuration before + instantiating the plugin. This allows OPA to ensure that all + configuration is valid before activating changes. Since plugins were + undocumented prior to this release, this change should be low + impact. For details on plugin development see the new Plugins page + on the website. + +* This release includes a backwards incompatible change to the HTTP + decision logger event type. Specifically, "null" inputs are now + handled correctly and decision logs for ad-hoc queries now populate + the "query" field in the event instead of the "path" field. If you + are using consuming decision log events in Go, please switch to the + decision logger framework documented here: https://github.com/open-policy-agent/opa/blob/master/docs/book/plugins.md. + +### Fixes + +- Add OPA version to decision logs ([#1089](https://github.com/open-policy-agent/opa/issues/1089)) +- Add query metrics to decision logs ([#1033](https://github.com/open-policy-agent/opa/issues/1033)) +- Add health endpoint to HTTP server ([#1086](https://github.com/open-policy-agent/opa/issues/1086)) +- Add line of failure in `opa test` ([#961](https://github.com/open-policy-agent/opa/issues/961)) +- Fix panic caused by assignment rewriting ([#1125](https://github.com/open-policy-agent/opa/issues/1125)) +- Fix parser to avoid duplicate comments in AST ([#426](https://github.com/open-policy-agent/opa/issues/426)) +- Fix semantic check for function references ([#1132](https://github.com/open-policy-agent/opa/issues/1132)) +- Fix query API to return 4xx on bad request ([#1081](https://github.com/open-policy-agent/opa/issues/1081)) +- Fix incorrect early exit from ref resolver ([#1110](https://github.com/open-policy-agent/opa/issues/1110)) +- Fix rewriting of assignment values ([#1154](https://github.com/open-policy-agent/opa/issues/1154)) +- Fix resolution inside references ([#1155](https://github.com/open-policy-agent/opa/issues/1155)) +- Fix '^' location of lines starting with tabs ([#1129](https://github.com/open-policy-agent/opa/issues/1129)) +- docs: Update count function doc to mention strings (#1126) ([#1122](https://github.com/open-policy-agent/opa/issues/1122)) + +### Miscellaneous + +- Add tutorial for OPA/Ceph integration using Rook +- Add metrics timer for server handler +- Add support for custom backends in decision logger +- Fix find operation on sets for non-empty refs +- Fix bug in local declaration rewriting +- Fix discovery docs to show a realistic example +- Update decision log event to include error +- Update decision log events to model paths and queries +- Update server and decision logger to represent input properly +- Update server to include decision ID in error events +- Avoid zero values in http.Transport{} in REST client + +### WebAssembly + +- wasm: Add support for composite terms ([#1113](https://github.com/open-policy-agent/opa/issues/1113)) +- wasm: Add support for not keyword ([#1112](https://github.com/open-policy-agent/opa/issues/1112)) +- wasm: Add == operator +- wasm: Add checks on single term and dot stmts +- wasm: Add support for boolean and null literals +- wasm: Add support for pattern matching on composites +- wasm: Fix planner for chained iteration +- wasm: Fix pretty printer writer usage +- wasm: Output filenames in testgen errors +- wasm: Refactor assignment for better typing +- wasm: Remove module dumping from build command +- wasm: Rename ir.LoopStmt to ir.ScanStmt +- wasm: Update tester to allow for missing cases + +## 0.10.2 + +### Fixes + +- Add manifest metadata to bundle data (#1079) ([#1062](https://github.com/open-policy-agent/opa/issues/1062)) +- Add profile command to REPL ([#838](https://github.com/open-policy-agent/opa/issues/838)) +- Add decision ID note in API docs ([#1061](https://github.com/open-policy-agent/opa/issues/1061)) +- Fix formatting of trailing comments in composites ([#1060](https://github.com/open-policy-agent/opa/issues/1060)) +- Fix panic caused by input being set incorrectly ([#1083](https://github.com/open-policy-agent/opa/issues/1083)) +- Fix partial eval to apply saved terms ([#1074](https://github.com/open-policy-agent/opa/issues/1074)) + +### Miscellaneous + +- Add Stringer implementation for expr values +- Add Stringer implementation on metrics object +- Add helper function to compile strings +- Add note to configuration reference about -c flag +- Add support for configuration discovery +- Add support for multiple tracers +- Add trace helper to rego package +- Add code coverage percentage +- Fix REPL to check number of assignment operands +- Fix bug in test runner rule name dedup +- Fix security link in REST API reference +- Fix formatting of empty sets +- Fix incorrect reporting of module parse time +- Fix out of range errors for eq/assign in compiler +- Fix parser to limit size of exponents +- Update compiler to iterate over modules in sort order +- Update OPA front page +- Mark diagnostics feature as deprecated + +## 0.10.1 + +### Fixes + +- Add show debug command to REPL ([#750](https://github.com/open-policy-agent/opa/issues/750)) + +### Miscellaneous + +- Add `glob` built-ins for easier path matching (thanks @aeneasr) +- Add support for specifying services as object + +## 0.10.0 + +### Major Features + +- **Wasm compiler**. This release adds initial/experimental support for + compiling Rego policies into Wasm executables. Wasm executables can be loaded + and executed in compatible Wasm runtimes like V8 (nodejs). You can try this + out by running `opa build`. + +- **Data mocking**. This release adds support for replacing/mocking the `data` + document using the `with` keyword. In the past, `with` only supported the + `input` document. This made it tricky to test context-dependent policies. With + the new `with` keyword support, it's easier to write tests against contextual + policies. + +- **Negation Optimization**. This release includes an optimization in partial + evaluation for dealing with negated statements (`not` keyword). In the past, + OPA would generate a support rule for negated statements. This is harder for + clients to consume and not readily optimized. The optimization computes the + necessary cross-product of the negated query and inlines it into the caller. + This leads to simpler partial evaluation results that are readily optimized, + translated into other query languages (e.g., [SQL and Elasticsearch](https://blog.openpolicyagent.org/write-policy-in-opa-enforce-policy-in-sql-d9d24db93bf4)), + or compiled into Wasm. + +### Fixes + +- Add builtin to verify and decode JWT ([#884](https://github.com/open-policy-agent/opa/issues/884)) +- Add GoDoc sample for using rego.Tracer ([#1002](https://github.com/open-policy-agent/opa/issues/1002)) +- Add built-in function to get runtime info ([#420](https://github.com/open-policy-agent/opa/issues/420)) +- Add support for YAML encoded input values ([#290](https://github.com/open-policy-agent/opa/issues/290)) +- Add support for client certificates ([#684](https://github.com/open-policy-agent/opa/issues/684)) +- Add support for non-zero exit code in eval subcommand ([#981](https://github.com/open-policy-agent/opa/issues/981)) +- Fix == rewriting on embedded terms ([#995](https://github.com/open-policy-agent/opa/issues/995)) +- Fix copy propagation panic in comprehensions ([#1012](https://github.com/open-policy-agent/opa/issues/1012)) +- Implement regex.find_n (#1001) ([#747](https://github.com/open-policy-agent/opa/issues/747)) +- Improve with modifier target error ([#343](https://github.com/open-policy-agent/opa/issues/343)) +- Iterate over smaller set when intersecting ([#531](https://github.com/open-policy-agent/opa/issues/531)) +- Only write one trailing newline at end of file ([#1032](https://github.com/open-policy-agent/opa/issues/1032)) +- Redirect HTTP requests with trailing slashes ([#972](https://github.com/open-policy-agent/opa/issues/972)) +- Update bundle reader to allow relative data.json ([#1019](https://github.com/open-policy-agent/opa/issues/1019)) +- Expose version information via REST API ([#277](https://github.com/open-policy-agent/opa/issues/277)) + +### Miscellaneous + +- Add default decision configuration +- Add extra helpers to loader result +- Add indentation to trace in failure output +- Add router option to the HTTP server +- Add support for headers in http.send (thanks @repenno) +- Deprecating --insecure-addr flag (thanks @repenno) +- Add POST v1/query API for large inputs (thanks @rite2nikhil) +- Remove heap allocations from AST set with open addressing +- Replace siphash with xxhash in AST +- Output traces on failures in verbose mode (thanks @srenatus) +- Rewrite duplicate test rule names (thanks @srenatus) + +## 0.9.2 + +### Miscellaneous Fixes + +- Add option to enable http redirects ([#921](https://github.com/open-policy-agent/opa/issues/921)) +- Add copy propagation to support rules ([#911](https://github.com/open-policy-agent/opa/issues/911)) +- Add support for inlining negated expressions in partial evaluation +- Add deps subcommand to analyze base and virtual document dependencies +- Add partial evaluation support to eval subcommand +- Add `net.cidr_overlap` built-in function (thanks @aeneasr) +- Add `regex.template_match` built-in function (thanks @aeneasr) +- Add external security audit information (thanks @caniszczyk) +- Add initial support for plugin loading (thanks @vrnmthr) +- Fix copy propagator type assertion panic ([#912](https://github.com/open-policy-agent/opa/issues/912)) +- Fix panic in parser error detail construction ([#948](https://github.com/open-policy-agent/opa/issues/948)) +- Fix with value rewriting for call terms ([#916](https://github.com/open-policy-agent/opa/issues/916)) +- Fix coverage flag for test command (thanks @johscheuer) +- Fix compile operation timing in REPL +- Fix to indent 4 spaces instead of a tab (thanks @superbrothers) +- Fix REPL output in policy guide (thanks @ttripp) +- Multiple fixes in the Kubernetes admission controller tutorial (thanks @johscheuer) +- Improve formatting of empty ast.Body ([#909](https://github.com/open-policy-agent/opa/issues/909)) +- Improve Kubernetes admission control policy loading explanation (thanks @rite2nihkil) +- Update http.send test to work without internet access ([#945](https://github.com/open-policy-agent/opa/issues/945)) +- Update test runner to set Fail to true ([#954](https://github.com/open-policy-agent/opa/issues/954)) + +### Security Audit Fixes + +- Improve token authentication docs and handler ([#901](https://github.com/open-policy-agent/opa/issues/901)) +- Link to security docs in tutorials ([#917](https://github.com/open-policy-agent/opa/issues/917)) +- Update bundle reader to cap buffer size ([#920](https://github.com/open-policy-agent/opa/issues/920)) +- Validate queries by checking unsafe builtins ([#919](https://github.com/open-policy-agent/opa/issues/919)) +- Fix XSS in debug page ([#918](https://github.com/open-policy-agent/opa/issues/918)) + +### Miscellaneous + +## 0.9.1 + +### Fixes + +- Add io.jwt.verify_es256 and io.jwt.verify_ps256 built-in functions (@optnfast) +- Add array.concat built-in function ([#851](https://github.com/open-policy-agent/opa/issues/851)) +- Add support for command line bundle loading ([#870](https://github.com/open-policy-agent/opa/issues/870)) +- Add regex split built-in function +- Fix incorrect AST node in Index events ([#859](https://github.com/open-policy-agent/opa/issues/859)) +- Fix terraform tutorial type check errors ([#888](https://github.com/open-policy-agent/opa/issues/888)) +- Fix CONTRIBUTING.md to include sign-off step (@optnfast) +- Improve save set performance ([#860](https://github.com/open-policy-agent/opa/issues/860)) + +## 0.9.0 + +### Major Features + +This release adds two major features to OPA itself. + +- Query Profiler: the `opa eval` subcommand now supports a `--profiler` option + to help policy authors understand the performance profile of their policies. + Give it a shot and let us know if you find it helpful or if you find cases + that could be improved! + +- Compile API: OPA now exposes Partial Evaluation with first-class interfaces. + In prior releases, Partial Evaluation was only used for optimizations + purposes. As of v0.9, callers can use Partial Evaluation via HTTP or Golang to + obtain conditional decisions that can be evaluated on the client-side. + +### Fixes + +- Add ADOPTERS.md file ([#691](https://github.com/open-policy-agent/opa/issues/691)) +- Add time.weekday builtin ([#789](https://github.com/open-policy-agent/opa/issues/789)) +- Fix REPL output for multiple bool exprs ([#850](https://github.com/open-policy-agent/opa/issues/850)) +- Remove support rule if default value is not needed ([#820](https://github.com/open-policy-agent/opa/issues/820)) + +### Miscellaneous + +Here is a short list of notable miscellaneous improvements. + +- Add any/all built-in functions (thanks @vrnmthr) +- Add built-in function to parse Rego modules +- Add copy propagation optimization to partial evaluation output +- Add docs for exercising policies with test framework +- Add extra output formats to eval subcommand +- Add support for providing input to eval via stdin +- Improve parser error readability +- Improve rule index to support unknown values +- Rewrite == with = in compiler +- Update build to enable CGO + +...along with 30+ other fixes and improvements. + +## 0.8.2 + +### Fixes + +- Fix virtual document cache invalidation ([#736](https://github.com/open-policy-agent/opa/issues/736)) +- Fix partial cache invalidation for data changes ([#589](https://github.com/open-policy-agent/opa/issues/589)) +- Fix query to path conversion in decision logger ([#783](https://github.com/open-policy-agent/opa/issues/783)) +- Fix handling of pointers to structs ([#722](https://github.com/open-policy-agent/opa/issues/722), thanks @srenatus) +- Improve sprintf number handling ([#748](https://github.com/open-policy-agent/opa/issues/748)) +- Reduce memory overhead of decision logs ([#705](https://github.com/open-policy-agent/opa/issues/705)) +- Set bundle status in case of HTTP 304 ([#794](https://github.com/open-policy-agent/opa/issues/794)) + +### Miscellaneous + +- Add docs on best practices around identity +- Add built-in function to verify JWTs signed with HS246 (thanks @hbouvier) +- Add built-in function to URL encode objects (thanks @vrnmthr) +- Add query parameters to authorization policy input ([#786](https://github.com/open-policy-agent/opa/pull/786)) +- Add support for listening on a UNIX domain socket ([#692](https://github.com/open-policy-agent/opa/issues/692), thanks @JAORMX) +- Add trace event for rule index lookups ([#716](https://github.com/open-policy-agent/opa/issues/716)) +- Add support for multiple listeners in server (thanks @JAORMX) +- Remove decision log buffer size limit by default +- Update codebase with various go-fmt/ineffassign/mispell fixes (thanks @srenatus) +- Update REPL command to set unknowns +- Update subcommands to support loader filter ([#782](https://github.com/open-policy-agent/opa/issues/782)) +- Update evaluator to cache storage reads +- Update object to keep track of groundness + +## 0.8.1 + +### Fixes + +- Handle escaped paths in data writes ([#695](https://github.com/open-policy-agent/opa/issues/695)) +- Rewrite with modifiers to allow refs as values ([#701](https://github.com/open-policy-agent/opa/issues/701)) + +### Miscellaneous + +- Add Kafka authorization tutorial +- Add URL query encoding built-ins +- Add runtime API to register plugins +- Update eval subcommand to support multiple files or directories (thanks @devenney) +- Update Terraform tutorial for OPA v0.8 +- Fix bug in topdown query ID generation + +## 0.8.0 + +### Major Features + +This release includes a few major features that improve OPA's management +capabilities. + +- Bundles: OPA can be configured to download bundles of policy and data from + remote HTTP servers. This allows administrators to configure OPA to pull down + all of the policy and data required at the enforcement point. When OPA boots + it will download the bundle and active it. OPA will periodically check in with + the server to download new revisions of the bundle. + +- Status: OPA can be configured to report its status to remote HTTP servers. The + status includes a description of the active bundle. This allows administrators + to monitor the status of OPA in a central place. + +- Decision Logs: OPA can be configured to report _decision logs_ to remote HTTP + servers. This allows administrators to audit and debug decisions in a central + place. + +### File Loading Convention + +The command line file loading convention has been changed slightly. If you were +previously loading files with `opa run *` you should use `opa run .` now. OPA +will not namespace data under top-level directory names anymore. The problem +with the old approach was that data layout was dependent on the root directory +name. For example `opa run /some/path1` and `opa run /some/path2` would yield +different results even if both paths contained identical data. + +### Tracing Improvements + +Thanks to @jyoverna for adding a `trace` built-in function that allows policy +authors to include notes in the trace. For example, authors can now embed +`trace` calls in their policies. When OPA encounters a `trace` call it will +include a "note" in the trace. Callers can filter the trace results to show only +notes. This helps diagnose incorrect decisions in large policies. For example: + +``` +package example + +allow { + input.method = allows_methods[_] + trace(sprintf("input method is %v", [input.method])) +} + +allowed_methods = ["GET", "HEAD"] +``` + +### Fixes + +- Add RS256 JWT signature verification built-in function ([#421](https://github.com/open-policy-agent/opa/issues/421)) +- Add X.509 certificate parsing built-in function ([#635](https://github.com/open-policy-agent/opa/issues/635)) +- Fix substring built-in bounds checking ([#465](https://github.com/open-policy-agent/opa/issues/465)) +- Generate support rules for negated expressions ([#623](https://github.com/open-policy-agent/opa/issues/623)) +- Ignore some built-in calls during partial eval ([#622](https://github.com/open-policy-agent/opa/issues/622)) +- Plug comprehensions in partial eval results ([#656](https://github.com/open-policy-agent/opa/issues/656)) +- Report safety errors for generated vars ([#661](https://github.com/open-policy-agent/opa/issues/661)) +- Update partial eval to check call args recursively ([#621](https://github.com/open-policy-agent/opa/issues/621)) + +### Other Notable Changes + +- Add base64 encoding built-in functions +- Add JSON format to test and check subcommands +- Add coverage package and update test subcommand to report coverage +- Add eval subcommand to run queries from the command line (deprecates opa run --eval) +- Add parse subcommand to parse Rego modules and print AST +- Add reminder/reminder (%) operator +- Update rule index to support == +- Update to Go 1.10 +- Various fixes to fmt subcommand and format package +- Fix input and data loading to roundtrip values. Allows loading of []string, []int, etc. + +As well as many other smaller improvements, refactoring, and fixes. + +## 0.7.1 + +### Fixes + +- Use rego.ParsedInput to provide input from form ([#571](https://github.com/open-policy-agent/opa/issues/571)) + +### Miscellaneous + +- Add omitempty tag for ad-hoc query result field +- Fix rego package to check capture vars +- Fix root document assignment in REPL +- Update query compiler to deep copy parsed query + +## 0.7.0 + +### Major Features + +- Nested expressions: now you can write expressions like `(temp_f - 32)*5/9`! + +- Assignment/comparison operators: now you can write `x := ` to + declare local variables and `x == y` when you strictly want to compare two + values (and not bind any variables like with `=`). + +- Prometheus support: now you can hook up Prometheus to OPA and collect + performance metrics on the different APIs. (thanks @rlguarino) + +### New Built-in Functions + +This release adds and improves a bunch of new built-in functions. See the [Language Reference](http://www.openpolicyagent.org/docs/language-reference.html) for details. + +- Add globs_match built-in function (thanks @yashtewari) +- Add HTTP request built-in function +- Add time.clock and time.date built-in functions +- Add n-way set union and intersection built-in functions +- Improve walk built-in function performance for partially ground paths + +### Fixes + +- Fix REPL assignment support ([#615](https://github.com/open-policy-agent/opa/issues/615)) +- Fix panic due to nil term value ([#601](https://github.com/open-policy-agent/opa/issues/601)) +- Fix safety check bug for call args ([#625](https://github.com/open-policy-agent/opa/issues/625)) +- Update Kubernetes Admission Control tutorial ([#567](https://github.com/open-policy-agent/opa/issues/567)) +- Update release script to build for Windows ([#573](https://github.com/open-policy-agent/opa/issues/573)) + +### Miscellaneous + +- Add support for DELETE method in Data API ([#609](https://github.com/open-policy-agent/opa/issues/609)) (thanks @repenno) +- Add basic query performance instrumentation +- Add documentation covering how OPA compares to other systems +- Remove use of unsafe.Pointer for string hashing + +## 0.6.0 + +This release adds initial support for partial evaluation. Partial evaluation +allows callers to mark certain inputs as unknown and then evaluate queries to +produce *new* queries which can be evaluated once inputs become known. + +### Features + +- Add initial implementation of partial evaluation +- Add sort built-in function ([#465](https://github.com/open-policy-agent/opa/issues/465)) +- Add built-in function to check value types + +### Fixes + +- Fix rule arg type inferencing ([#542](https://github.com/open-policy-agent/opa/issues/542)) +- Fix documentation on "else" keyword ([#475](https://github.com/open-policy-agent/opa/issues/475)) +- Fix REPL to deduplicate auto-complete paths ([#432](https://github.com/open-policy-agent/opa/pull/432) +- Improve getting started example ([#532](https://github.com/open-policy-agent/opa/issues/532)) +- Improve handling of forbidden methods in HTTP server ([#445](https://github.com/open-policy-agent/opa/issues/445)) + +### Miscellaneous + +- Refactor sets and objects for constant time lookup + +## 0.5.13 + +### Fixes + +- Improve InterfaceToValue to handle other Go types ([#473](https://github.com/open-policy-agent/opa/issues/473)) +- Fix bug in conflict detection ([#518](https://github.com/open-policy-agent/opa/issues/518)) + +## 0.5.12 + +### Fixes + +- Fix eval of objects/sets containing vars ([#505](https://github.com/open-policy-agent/opa/issues/505)) +- Fix REPL printing of generated vars + +## 0.5.11 + +### Fixes + +- Refactor topdown evaluation/unification ([#131](https://github.com/open-policy-agent/opa/issues/131)) +- Rewrite refs in rule args ([#497](https://github.com/open-policy-agent/opa/issues/497)) + +### Miscellaneous + +- Fix bug in expression formatting +- Fix dynamic rewriting to copy with modifiers +- Fix off-by-one bug in array helper + +## 0.5.10 + +### Fixes + +- Fix index usage for virtual docs ([#490](https://github.com/open-policy-agent/opa/issues/490)) +- Fix match error panic ([#494](https://github.com/open-policy-agent/opa/issues/494)) +- Fix wildcard mangling in rule head ([#480](https://github.com/open-policy-agent/opa/issues/480)) + +### Miscellaneous + +- Add parse_duration_ns to generate nanos based on duration string +- Add product to calculate the product of array or set + +## 0.5.9 + +### Fixes + +- Fix unsafe var errors on functions ([#471](https://github.com/open-policy-agent/opa/issues/471), [#467](https://github.com/open-policy-agent/opa/issues/467)) + +### Miscellaneous + +- Fix docs example of set union +- Fix file watch bug causing panic in server mode +- Modify AST to represent function names as refs +- Refactor runtime to separate init and start +- Refactor test runner to accept Store argument + +## 0.5.8 + +### Fixes + +- Substitute comprehension terms requring eval ([#453](https://github.com/open-policy-agent/opa/issues/453)) + +### Miscellaneous + +- Add alpine-based Docker image +- Add stdin mode to opa fmt +- Fix syntax error in comprehension example +- Improve input parsing performance in V0 API +- Refactor loader to read inputs once (allows use of process substitution) +- Remove backup creation from fmt subcommand +- Remove use of sprintf in formatter + +## 0.5.7 + +This release adds a new `test` subcommand to OPA. The `test` subcommand enables policy unit testing. The unit tests are expressed as rules containing assertions over test data. The `test` subcommand provides a test runner that automatically discovers and executes these test rules. See `opa test --help` for examples. + +### Fixes + +- Fix type error marshalling bug ([#391](https://github.com/open-policy-agent/opa/issues/391)) +- Fix type inference bug ([#381](https://github.com/open-policy-agent/opa/issues/381)) +- Fix unification bug ([#436](https://github.com/open-policy-agent/opa/issues/436)) +- Fix type inferecen bug for partial objects with non-string keys ([#440](https://github.com/open-policy-agent/opa/issues/440)) +- Suppress match errors if closures contained errors ([#438](https://github.com/open-policy-agent/opa/issues/438)) + +## 0.5.6 + +As part of this release, logrus was revendored to deal with the naming issue. If you use logrus, or one of your other dependencies does (such as Docker), be sure to check out https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276. + +### Fixes + +- Fix incorrect REPL interpretation of some exprs ([#433](https://github.com/open-policy-agent/opa/issues/433)) +- Fix inaccurate location information in some parser errors ([#214](https://github.com/open-policy-agent/opa/issues/214)) + +### Miscellaneous + +- Add Terraform Testing tutorial to documentation +- Add shorthand for defining partial documents (e.g., `p[1]` instead of `p[1] { true }`) +- Add walk built-in function to recursively process nested documents +- Refactor Policy API response representations based on usage + +## 0.5.5 + +This release adds [Diagnostics](http://www.openpolicyagent.org/docs/rest-api.html#diagnostics) support to the server. This greatly improves OPA's debuggability when deployed as a daemon. + +### Miscellaneous + +- Fix data race in the parser extensions +- Fix index.html GET requests returning error on empty input +- Fix race condition in watch test +- Fix image version in HTTP API tutorial +- Add metrics command to the REPL +- Limit length of pretty printed values in the REPL +- Simplify input query parameters in data GET requests +- Update server to support pretty explanations + +## 0.5.4 + +### Miscellaneous + +- Properly remove temporary files when running `opa fmt -d` +- Add support for refs with composite operands (e.g,. `p[[x,y]]`) + +## 0.5.3 + +### Fixes + +- Add support for raw strings ([#265](https://github.com/open-policy-agent/opa/issues/265)) +- Add support to cancel compilation after some number of errors ([#249](https://github.com/open-policy-agent/opa/issues/249)) + +### Miscellaneous + +- Add Kubernetes admission control tutorial +- Add tracing support to rego package +- Add watch package for watching changes to queries +- Add dependencies package to perform dependency analysis on ASTs + +## 0.5.2 + +### Fixes + +- Fix mobile view navigation bug +- Fix panic in compiler from concurrent map writes ([#379](https://github.com/open-policy-agent/opa/pull/379) +- Fix ambiguous syntax around body and set comprehensions ([#377](https://github.com/open-policy-agent/opa/issues/377)) + +### Miscellaneous + +- Add support for set and object comprehensions +- Add support for system.main policy in server +- Add transaction support in rego package +- Improve type checking error messages +- Format REPL modules before printing them + +## 0.5.1 + +### Fixes + +- Correct `opa fmt` panic on missing files +- Fix minor site issues + +### Miscellaneous + +- Add rego examples with input and compiler +- Add support for query cancellation + +## 0.5.0 + +### User Functions + +OPA now supports user-defined functions that have the same semantics as built-in +functions. This allows policy authors to quickly define reusable pieces of logic +in Rego without overloading the input document or thinking about variable +safety. + +### Storage Improvements + +The storage layer has been improved to support single-writer/multiple-reader +concurrency. The storage interfaces have been simplified in the process. Users +can rely on https://godoc.org/github.com/open-policy-agent/opa/storage/inmem in +place of the old storage package. + +### Website Refresh + +The website has been redesigned and the documentation has been ported over to +GitBook. + +### `opa check` and `opa fmt` + +OPA supports two new commands that check and format policies. Check out `opa +help` for more information. + +### Miscellaneous + +- Add YAML serialization built-ins +- Add time built-ins + +### Fixes + +- Fixed incorrect source locations on refs and manually constructed terms. All + term locations should be set correctly now. +- Fixed evaluation bug that caused partial sets and partial objects to be + undefined in some cases. + +## 0.4.10 + +This release includes a bunch of new built-in functions to help with string manipulation, JWTs, and more. + +The JSON marshalling built-ins have been renamed. Policies that used `json_unmarshal` and `json_marshal` before will need to be updated to use `json.marshal` and `json.unmarshal` respectively. + +### Misc + +- Add `else` keyword +- Improved undefined built-in error message +- Fixed error message in `-` built-in +- Fixed exit instructions in REPL tutorial +- Relax safety check for built-in outputs + +## 0.4.9 + +This release includes a bunch of cool stuff! + +- Basic type checking for queries and virtual docs ([#312](https://github.com/open-policy-agent/opa/pull/312)) +- Optimizations for HTTP API authorization policies ([#319](https://github.com/open-policy-agent/opa/pull/319)) +- New /v0 API to support webhook integrations ([docs](http://www.openpolicyagent.org/documentation/references/rest-v0)) + +### Fixes + +- Add support for namespaced built-ins ([#314](https://github.com/open-policy-agent/opa/issues/314)) +- Improve logging to include request/response bodies ([#328](https://github.com/open-policy-agent/opa/pull/328)) +- Add basic performance metrics ([#320](https://github.com/open-policy-agent/opa/pull/320)) + +### Miscellaneous + +- Add built-ins to un/marshal JSON +- Add input form to diagnostic page + +## 0.4.8 + +### Miscellaneous + +- Fix top-level navigation links +- Improve file loader error handling + +## 0.4.7 + +### Fixes + +- Fix recursive binding by short-circuiting ref eval ([#298](https://github.com/open-policy-agent/opa/issues/298)) +- Fix reordering for unsafe ref heads ([#297](https://github.com/open-policy-agent/opa/issues/297)) +- Fix rewriting of single term exprs ([#299](https://github.com/open-policy-agent/opa/issues/299)) + +## 0.4.6 + +This release changes the `run` command options: + +- Removed glog in favour of Sirupsen/logrus. This means the command line arguments to control logging have changed. See `run --help` for details. +- Removed `--policy-dir` option. For now, if policy persistence is required, users can treat policies as config files and manage them outside of OPA. Once OPA supports persistence of data (e.g., with file-based storage) then policy persistence will be added back in. + +### Fixes + +- Add support for additional HTTP listener ([#289](https://github.com/open-policy-agent/opa/issues/289)) +- Allow slash in policy id/path ([#292](https://github.com/open-policy-agent/opa/issues/292)) +- Improve request logging ([#281](https://github.com/open-policy-agent/opa/issues/281)) + +### Miscellaneous + +- Add deployment documentation +- Remove erroneous flag.Parse() call +- Remove persist/--policy-dir option +- Replace glog with logrus + +Also, updated Docker tagging so that latest points to most recent release (instead of most recent development build). The most recent development build can still be obtained with the {version}-dev tag. + +## 0.4.5 + +### API security + +This release adds support for TLS, token-based authentication, and authorization in the OPA APIs! + +For details on how to secure the OPA API, go to http://openpolicyagent.org/documentation/references/security. + +### Fixes + +- Fix stray built-in error messages ([#275](https://github.com/open-policy-agent/opa/issues/275)) +- Update error codes and messages throughout ([#237](https://github.com/open-policy-agent/opa/issues/237)) +- [Fix evaluation bug with nested value refs](https://github.com/open-policy-agent/opa/pull/276/commits/e3336cce130eedda08f224ce4f28e19212447dcb) +- [Fix rego.Eval to close transactions](https://github.com/open-policy-agent/opa/pull/276/commits/745bd235127fae6bc22ff870bf62922c9358ccc0) +- [Fix buggy usage of errors.Cause](https://github.com/open-policy-agent/opa/pull/276/commits/bdf43b6a52de639e4f66810306311641ed7eea85) + +### Miscellaneous + +- Updated to support Go 1.8 + +## 0.4.4 + +### Fixes + +- Fix issue in high-level Go API ([#261](https://github.com/open-policy-agent/opa/issues/261)) + +## 0.4.3 + +### Fixes + +- Fix parsing of inline comments ([#258](https://github.com/open-policy-agent/opa/issues/258)) +- Fix unset of input/data in REPL ([#259](https://github.com/open-policy-agent/opa/issues/259)) +- Handle non-string/var values in rule tree lookup ([#257](https://github.com/open-policy-agent/opa/issues/257)) + +## 0.4.2 + +### Rego + +This release changes the Rego syntax. The two main changes are: + +- Expressions are now separated by semicolons (instead of commas). When writing rules, the semicolons are optional. +- Rules are no longer written in the form: `head :- body`. Instead they are written as `head { body }`. + +Also: + +- Set, array, and object literals now support trailing commas. +- To declare a set literal with one element, you must include a trailing comma, e.g., `{ foo, }`. +- Arithmetic and set operations can now be performed with infix notation, e.g., `x = 2 + 1`. +- Sets can be referred to like objects and arrays now ([#243](https://github.com/open-policy-agent/opa/issues/243)). E.g., `p[_].foo = 1 # check if element in has attr foo equal to 1`. + +### Evaluation + +This release changes the evaluation behaviour for packages. Previously, if a package contained no rules OR all of the rules were undefined when evaluated, a query against the package path would return undefined. As of v0.4.2 the query will return an empty object. + +### REST API + +This release changes the Data API to return an HTTP 200 response if the document is undefined. The message body will contain an object without the `result` property. + +### Fixes + +- Allow sets to be treated like objects/arrays + +### Miscellaneous + +- Added high level API for Go users. See `github.com/open-policy-agent/opa/rego` package. +- Improved expression String() function to handle infix operators better. +- Added support for set intersection and union built-ins. See language docs. + +## 0.4.1 + +### Rego + +For more details on these changes see sections in [How Do I Write Policies](http://www.openpolicyagent.org/documentation/how-do-i-write-policies/). + +- Added new **default** keyword. The default keyword can be used to provide a default value for rules with complete definitions. +- Added new **with** keyword. The with keyword can be used to programmatically set the value of the input document inside policies. + +### Fixes + +- Fix input document definition in REPL ([#231](https://github.com/open-policy-agent/opa/issues/231)) +- Fix reference evaluation bug ([#238](https://github.com/open-policy-agent/opa/issues/238)) + +### Miscellaneous + +- Add basic REST API authorization benchmark + +## 0.4.0 + +### REST API changes + +This release contains a few non-backwards compatible changes to the REST API: + +- The `request` document has been renamed to `input`. If you were calling the + GET /data[/path]?request=value you should update to use [POST + requests](http://www.openpolicyagent.org/documentation/references/rest#get-a-document-with-input) + (see below). + +- The API responses have been updated to return results embedded inside a + wrapper object: `{"result": value}`. This will allow OPA to return unambiguous + metadata in future (e.g., pagination, analysis, etc.) If you were previously + consuming Data API GET responses, you should update your code to access the + value under the `"result"` key of the response object. + +- The API models have been updated to use snake_case + ([#222](https://github.com/open-policy-agent/opa/issues/222)). This would only + affect you if you were previously consuming error responses or policy ASTs. + +The Data API has been updated to support the [POST +requests](http://www.openpolicyagent.org/documentation/references/rest#get-a-document-with-input). +This is the recommended way of supplying query inputs. + +### Built-in Function changes + +The built-in framework has been extended to support simplified built-in +implementations: + +- Refactor topdown built-in functions + ([#205](https://github.com/open-policy-agent/opa/issues/205)) + +### Fixes + +- Add cURL note to REST API docs ([#211](https://github.com/open-policy-agent/opa/issues/211)) +- Fix empty request parameter parsing ([#212](https://github.com/open-policy-agent/opa/issues/212)) +- Fix handling of missing input document ([#227](https://github.com/open-policy-agent/opa/issues/227)) +- Improve floating point literal support ([#215](https://github.com/open-policy-agent/opa/issues/215)) +- Improve module parsing errors ([#213](https://github.com/open-policy-agent/opa/issues/213)) +- Fix ast.Number hash and equality +- Fix parsing of escaped strings + +### Miscellaneous + +- Improve evaluation error messages + +## 0.3.1 + +### Fixes + +- Fixed unsafe vars with built-in operator names bug ([#206](https://github.com/open-policy-agent/opa/issues/206)) +- Fixed body to rule conversion bug ([#202](https://github.com/open-policy-agent/opa/issues/202)) +- Improved request parameter handling ([#201](https://github.com/open-policy-agent/opa/issues/201)) + +### Miscellaneous + +- Improved release infrastructure + +## 0.3.0 + +The last major/minor release of 2016! Woohoo! This release contains a few +non-backwards compatible changes to the APIs. + +### Storage API changes + +These changes simplify and clean up the storage.Store interface. This should +make it easier to implement custom stores in the future. + +- Update storage to support context.Context ([#155](https://github.com/open-policy-agent/opa/issues/155)) +- Update underlying number representation ([#154](https://github.com/open-policy-agent/opa/issues/154)) +- Updates to use new storage.Path type ([#159](https://github.com/open-policy-agent/opa/issues/159)) + +### The request Document + +These changes update the language to align query arguments with state stored in +OPA. With these changes, OPA can readily analyze policies and determine +references that refer to state stored in OPA versus query arguments versus local +variables. + +These changes also update how query arguments are provided via the REST API. + +- Updates to how query arguments are handled [#197](https://github.com/open-policy-agent/opa/pull/197) + +### topdown API changes + +- topdown.Context has been renamed to topdown.Topdown to avoid confusion with Golang's context. + +### Fixes + +- Add help topics to REPL ([#172](https://github.com/open-policy-agent/opa/issues/172)) +- Fix error handling bug in Query API ([#183](https://github.com/open-policy-agent/opa/issues/183)) +- Fix handling of prefixed paths with -w flag ([#193](https://github.com/open-policy-agent/opa/issues/193)) +- Improve exit handling in REPL ([#175](https://github.com/open-policy-agent/opa/issues/175)) +- Update parser support for = rules ([#192](https://github.com/open-policy-agent/opa/issues/192)) + +### Miscellaneous + +- Add Visual Studio and Atom plugins +- Add lazy loading of modules during compilation +- Fix bug in serialization of empty objects/arrays + +## 0.2.2 + +### Fixes + +- Add YAML loading and refactor into separate file ([#135](https://github.com/open-policy-agent/opa/issues/135)) +- Add command line flag to eval, print, and exit ([#152](https://github.com/open-policy-agent/opa/issues/152)) +- Add compiler check for consistent rule types ([#147](https://github.com/open-policy-agent/opa/issues/147)) +- Add set_diff built-in ([#133](https://github.com/open-policy-agent/opa/issues/133)) +- Add simple 'show' command to print current module ([#108](https://github.com/open-policy-agent/opa/issues/108)) +- Added examples to 'help' output in REPL ([#151](https://github.com/open-policy-agent/opa/issues/151)) +- Check package declarations for conflicts ([#137](https://github.com/open-policy-agent/opa/issues/137)) +- Deep copy modules in compiler ([#158](https://github.com/open-policy-agent/opa/issues/158)) +- Fix evaluation of refs to set literals ([#149](https://github.com/open-policy-agent/opa/issues/149)) +- Fix indexing usage for refs with intersecting vars ([#153](https://github.com/open-policy-agent/opa/issues/153)) +- Fix output for references iterating sets ([#148](https://github.com/open-policy-agent/opa/issues/148)) +- Fix parser handling of keywords in variable names ([#178](https://github.com/open-policy-agent/opa/issues/178)) +- Improve file loading support ([#163](https://github.com/open-policy-agent/opa/issues/163)) +- Remove conflict error for same key/value pairs ([#165](https://github.com/open-policy-agent/opa/issues/165)) +- Support "data" query in REPL ([#150](https://github.com/open-policy-agent/opa/issues/150)) + +### Miscellaneous + +- Add new compiler harness for ad-hoc queries +- Add tab completion of imports + +## 0.2.1 + +### Improvements + +- Added support for non-ground global values +- Added several new string manipulation built-ins +- Added TextMate grammar file +- Setup Docker image build and push to Docker Hub as part of CI + +## 0.2.0 + +### Language + +- Set literals +- Composite and reference values in Rule head +- Complete Rule definitions containing variables +- Builtins for regular expressions, string concatenation, casting to numbers + +### Compiler + +- Improved error reporting in parser and compiler + +### Evaluation + +- Iteration over base and virtual documents (in the same reference) +- Query tracing and explanation support + +### Storage + +- Pluggable data storage support + +### Documentation + +- GoDoc strings with examples +- REST API specification +- Concise language reference + +### Performance + +- Per-query cache of virtual documents in topdown + +And many other small improvements and fixes. + +## 0.1.0 + +### Language + +- Basic value types: null, boolean, number, string, object, and array +- Reference and variables types +- Array comprehensions +- Built-in functions for basic arithmetic and aggregation +- Incremental and complete rule definitions +- Negation and disjunction +- Module system + +### Compiler + +- Reference resolver to support packages +- Safety check to prevent recursive rules +- Safety checks to ensure successful evaluation will bind all variables in head + and body of rules + +### Evaluation + +- Initial top down query evaluation algorithm + +### Storage + +- Basic in-memory storage that exposes JSON Patch style API + +### Tooling + +- REPL that can be run to experiment with ad-hoc queries + +### APIs + +- Server mode supports HTTP APIs to manage policies, push and query documents, and execute ad-hoc queries. + +### Infrastructure + +- Basic build infrastructure to produce cross-platform builds, run + style/lint/format checks, execute tests, static HTML site + +### Documentation + +- Introductions to policy, policy-enabling, and how OPA works +- Language reference that serves as guide for new users +- Tutorial that introduces users to the REPL +- Tutorial that introduces users to policy-enabling with a Docker Authorization plugin diff --git a/third_party/opa/CODE_OF_CONDUCT.md b/third_party/opa/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..0b23e19ed336 --- /dev/null +++ b/third_party/opa/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +## Community Code of Conduct + +We follow the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). diff --git a/third_party/opa/COMMUNITY_GUIDELINES.md b/third_party/opa/COMMUNITY_GUIDELINES.md new file mode 100644 index 000000000000..eacee362c8f4 --- /dev/null +++ b/third_party/opa/COMMUNITY_GUIDELINES.md @@ -0,0 +1,63 @@ +# OPA Community Guidelines v2.0 + +The [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md) is enforced in all areas of the OPA community, plus the following. + +## Relevancy + +Any content posted or shared in the OPA community should be relevant to the specific Slack Channel or GitHub Category, and generally to the OPA community. If you're unsure about the content you want to share, posting in #help channel in Slack or the Community category in GitHub Discussions is always a safe choice; admins will be around to help guide new members. + +## Spamming + +Excessive re-posting, unnecessary cross-posting, unsolicited advertisements for services or products are not allowed on any of the OPA communication channels and are considered spam. Posting content that is considered spam will be removed and these actions are subject to the same enforcement rules as unacceptable behavior. + +## Vendors + +The OPA community has a rich ecosystem of tools, integrations, and Vendors to support them. Any company whose primary revenue stream includes a cloud-native service or technology is considered a Vendor. As a valuable part of the OPA ecosystem Vendors are encouraged to participate in the community with the expectation that they have good intentions, this means interacting with members with the intent to be helpful and supportive. Any unsolicited advertisements will be removed and are subject to our enforcement rules. + +## End-users + +Companies that use cloud-native services internally, but do not sell any of these services externally, are considered an End User Company in the OPA Community. End User companies are expected to operate with positive intentions, this is not the place to build your marketing funnel for external tools and services. + +## Member Participation + +The OPA community is here for everyone to connect with one another, share information, and build amazing products. By choosing to participate in the OPA community as a Vendor, End User, or general contributor you are agreeing to respect these guidelines. When interacting with any of our social channels, Slack, Twitter, GitHub, and any other channels we participate in, there is an expectation that you will exhibit the values of the OPA community. + +## Values + +### Be Respectful + +Value each other’s ideas, styles and viewpoints. We may not always agree, but disagreement is no excuse for poor manners. Be open to different possibilities and to being wrong. Be respectful in all interactions and communications, especially when debating the merits of different options. Be aware of your impact and how intense interactions may be affecting people. Be direct, constructive and positive. Take responsibility for your impact and your mistakes – if someone says they have been harmed through your words or actions, listen carefully, apologize sincerely, and correct the behavior going forward. + +### Be Direct but Professional + +We are likely to have some discussions about if and when criticism is respectful and when it’s not. We must be able to speak directly when we disagree and when we think we need to improve. We cannot withhold hard truths. Doing so respectfully is hard, doing so when others don’t seem to be listening is harder, and hearing such comments when one is the recipient can be even harder still. We need to be honest and direct, as well as respectful. + +### Be Inclusive + +Seek diverse perspectives. Diversity of views and of people on teams powers innovation, even if it is not always comfortable. Encourage all voices. Help new perspectives be heard and listen actively. If you find yourself dominating a discussion, it is especially important to step back and encourage other voices to join in. Be aware of how much time is taken up by dominant members of the group. Provide alternative ways to contribute or participate when possible. + +Be inclusive of everyone in an interaction, respecting and facilitating people’s participation whether they are: + +- Remote (on video or phone) +- Not native language speakers +- Coming from a different culture +- Using pronouns other than “he” or “she” +- Living in a different time zone +- Facing other challenges to participate +- Think about how you might facilitate alternative ways to contribute or participate. If you find yourself dominating a discussion, step back. Make way for other voices and listen actively to them. + +> These values were inspired by the [Mozilla Community Participation Guidelines](https://www.mozilla.org/en-US/about/governance/policies/participation/) + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior will not be tolerated. An admin may take any action deemed appropriate, up to and including, a warning, a temporary ban(30 days), and a permanent ban for repeated violation of these guidelines. Admins may also remove content that violates the guidelines. + + +## Reporting + +Please reach out to one of the admins below, or use the anonymous feedback form. + +- Peter ONeill (peteroneilljr@styra.com or @peteroneilljr) +- Torin Sandall (torin@styra.com or @tsandall) +- Amye Scavarda Parrin (amye@linuxfoundation.org) +- Anonymous Feedback: https://forms.gle/vFkrC1jMai1q3EZY6 diff --git a/third_party/opa/CONTRIBUTING.md b/third_party/opa/CONTRIBUTING.md new file mode 100644 index 000000000000..45f46eb6c1f6 --- /dev/null +++ b/third_party/opa/CONTRIBUTING.md @@ -0,0 +1,6 @@ +# Contributing + +Thanks for your interest in contributing to the Open Policy Agent (OPA) project! + +Please refer to [OPA's contribution guidelines](https://www.openpolicyagent.org/docs/latest/contributing/) +to find out how you can help. diff --git a/third_party/opa/Dockerfile b/third_party/opa/Dockerfile new file mode 100644 index 000000000000..9a894e85efa6 --- /dev/null +++ b/third_party/opa/Dockerfile @@ -0,0 +1,28 @@ +# Copyright 2019 The OPA Authors. All rights reserved. +# Use of this source code is governed by an Apache2 +# license that can be found in the LICENSE file. + +ARG BASE + +FROM ${BASE} + +LABEL org.opencontainers.image.authors="Torin Sandall " +LABEL org.opencontainers.image.source="https://github.com/open-policy-agent/opa" + +# Any non-zero number will do, and unfortunately a named user will not, as k8s +# pod securityContext runAsNonRoot can't resolve the user ID: +# https://github.com/kubernetes/kubernetes/issues/40958. +ARG USER=1000:1000 +USER ${USER} + +# TARGETOS and TARGETARCH are automatic platform args injected by BuildKit +# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope +ARG TARGETOS +ARG TARGETARCH +ARG BIN_DIR=. +ARG BIN_SUFFIX= +COPY ${BIN_DIR}/opa_${TARGETOS}_${TARGETARCH}${BIN_SUFFIX} /opa +ENV PATH=${PATH}:/ + +ENTRYPOINT ["/opa"] +CMD ["run"] diff --git a/third_party/opa/GOVERNANCE.md b/third_party/opa/GOVERNANCE.md new file mode 100644 index 000000000000..db9ffd5b828f --- /dev/null +++ b/third_party/opa/GOVERNANCE.md @@ -0,0 +1,63 @@ +# Project Governance + +This document defines the governance process for the open-policy-agent GitHub organization. + +The MAINTAINERS.md file in this repository contains the list of OPA project maintainers and their "area of expertise". An area of expertise is defined as a set of repositories or repository subtrees. + +## Voting + +Maintainers use "organizational voting" to approve changes so that no single organization can dominate an area of expertise. + +* "Organizations relevant to a change" are all those organizations with an area of expertise that covers the change. + +* "Organizations with an area of expertise" are those organizations for which there is a maintainer from that organization with that area of expertise. + +* Individuals not associated with or employed by a company or organization are allowed one organization vote. + +* Each company or organization (regardless of the number of maintainers associated with or employed by that company/organization) receives one organization vote. Any maintainer from an organization may cast the vote for that organization. + +For example, consider the following scenario. + +* Two maintainers are employed by Company X, two by Company Y, two by Company Z, and one maintainer is an unaffiliated individual + +* Area of expertise E covers the repository R + +* One maintainer from Company X, two from Company Y, and the un-affiliated individual all have expertise E + +For any change requiring a vote to repository R, three "organization votes" are possible: one for X, one for Y, and one for the un-affiliated individual. + +Unless specified otherwise, a vote passes when greater than fifty percent of the organization votes are in favour. + +## Code Changes + +All code changes should go through the Pull Request (PR) process. PRs should only be merged after receiving approval (via GitHub) from at least one other member of the GitHub team associated with the area(s) of expertise. + +We do not vote formally on every code change, but we do expect that every code change merged has the same community support as if the change were approved by a formal vote. When a merge occurs without sufficient community support, the change should be reverted until the dispute is resolved through discussion. Any team member who feels that a technical decision cannot be reached can call for a formal vote following the rules outlined above in either the PR or a separate issue. + +## Non-code Changes + +Changes that are not PRs will be voted on through GitHub issues. Maintainers should indicate their yes/no vote on that GitHub issue, and after a suitable period of time, the votes will be tallied and the outcome noted. + +The following changes, while governed by the language above, require additional clarification. + +### Changes in Maintainership + +New maintainers for an area of expertise are proposed by an existing maintainer for that area of expertise and are elected by a 2/3 majority of the organizations with that area of expertise. + +Maintainer status expires after 1 year but a request to self-renew can be made within 1 month of expiry. + +Maintainers for an area of expertise can be removed by a 2/3 majority of the organizations with that area of expertise. + +### Changes in Governance + +All changes in Governance require a 2/3 majority organization vote from all areas of expertise. + +### New Repositories + +New repositories require a 2/3 majority organization vote from all areas of expertise. + +## GitHub Project Administration + +Maintainers for an area of expertise belong to the associated GitHub team(s) (e.g., `opa-maintainers`, `gatekeeper-maintainers`, etc.) so that GitHub permissions reasonably follow this governance model. + +Individuals may be added to that repository's GitHub team but need not be added to the MAINTAINERS.md file. This provision enables new subprojects and contributors to be onboarded without immediately creating new maintainers. \ No newline at end of file diff --git a/third_party/opa/LICENSE b/third_party/opa/LICENSE new file mode 100644 index 000000000000..8f71f43fee3f --- /dev/null +++ b/third_party/opa/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/third_party/opa/MAINTAINERS.md b/third_party/opa/MAINTAINERS.md new file mode 100644 index 000000000000..7743aebac3c7 --- /dev/null +++ b/third_party/opa/MAINTAINERS.md @@ -0,0 +1,26 @@ +# Maintainers + +The following table lists OPA project maintainers and areas of expertise in alphabetical order: + +| Name | GitHub | Email | Organization | Repositories/Area of Expertise | Added/Renewed On | +| --- | --- | --- | --- | --- |------------------| +| Anders Eknert | @anderseknert | anders@styra.com | Styra | opa | 2025-01-27 | +| Ash Narkar | @ashutosh-narkar | anarkar4387@gmail.com | Styra | opa, opa-envoy-plugin | 2024-03-31 | +| Charlie Egan | @charlieegan3 | charlie@styra.com | Styra | opa | 2025-01-27 | +| Max Smythe | @maxsmythe | smythe@google.com | Google | frameworks/constraints, gatekeeper, gatekeeper-library, cert-controller | 2024-03-31 | +| Nilekh Chaudhari | @nilekhc | nilekhc@gmail.com | Microsoft | gatekeeper-library | 2024-03-31 | +| Rita Zhang | @ritazh | rita.z.zhang@gmail.com | Microsoft | frameworks/constraints, gatekeeper, gatekeeper-library, cert-controller | 2024-03-31 | +| Sertaç Özercan | @sozercan | sozercan@gmail.com | Microsoft | gatekeeper, gatekeeper-library, cert-controller, gatekeeper-external-data-provider | 2024-03-31 | +| Jaydip Gabani | @JaydipGabani | gabanijaydip@gmail.com | Microsoft | frameworks/constraints, gatekeeper, gatekeeper-library, cert-controller | 2024-11-06 | +| Stephan Renatus | @srenatus | stephan@styra.com | Styra | opa | 2024-03-31 | +| Tim Hinrichs | @timothyhinrichs | timothy.l.hinrichs@gmail.com | Styra | all repositories | 2024-03-31 | +| Torin Sandall | @tsandall | torinsandall@gmail.com | Styra | all repositories | 2024-03-31 | + +## Emeritus + +* [Craig Tabita](https://github.com/ctab) +* [Ernest Wong](https://github.com/chewong) +* [Patrick East](https://github.com/patrick-east) +* [Will Beason](https://github.com/willbeason) +* [Oren Shomron](https://github.com/shomron) +* [Andrew Peabody](https://github.com/apeabody) diff --git a/third_party/opa/Makefile b/third_party/opa/Makefile new file mode 100644 index 000000000000..3e270100f21b --- /dev/null +++ b/third_party/opa/Makefile @@ -0,0 +1,565 @@ +# Copyright 2016 The OPA Authors. All rights reserved. +# Use of this source code is governed by an Apache2 +# license that can be found in the LICENSE file. + +VERSION := $(shell ./build/get-build-version.sh) + +CGO_ENABLED ?= 1 +WASM_ENABLED ?= 1 +GOFLAGS ?= "-buildmode=exe" + +# See https://golang.org/cmd/go/#hdr-Build_modes: +# > -buildmode=exe +# > Build the listed main packages and everything they import into +# > executables. Packages not named main are ignored. +GO := CGO_ENABLED=$(CGO_ENABLED) GOFLAGS="$(GOFLAGS)" go +GO_TEST_TIMEOUT := -timeout 30m + +GOVERSION ?= $(shell cat ./.go-version) +GOARCH := $(shell go env GOARCH) +GOOS := $(shell go env GOOS) + +ifeq ($(GOOS)/$(GOARCH),darwin/arm64) +WASM_ENABLED=0 +endif + +GO_TAGS := -tags= +ifeq ($(WASM_ENABLED),1) +GO_TAGS = -tags=opa_wasm +endif + +GOLANGCI_LINT_VERSION := v1.64.5 +YAML_LINT_VERSION := 0.29.0 +YAML_LINT_FORMAT ?= auto + +DOCKER_RUNNING ?= $(shell docker ps >/dev/null 2>&1 && echo 1 || echo 0) + +# We use root because the windows build, invoked through the ci-go-build-windows +# target, installs the gcc mingw32 cross-compiler. +# For image, it's overridden, so that the built binary isn't root-owned. +DOCKER_UID ?= 0 +DOCKER_GID ?= 0 + +ifeq ($(shell tty > /dev/null && echo 1 || echo 0), 1) +DOCKER_FLAGS := --rm -it +else +DOCKER_FLAGS := --rm +endif + +DOCKER := docker + +# BuildKit is required for automatic platform arg injection (see Dockerfile) +export DOCKER_BUILDKIT := 1 + +# Supported platforms to include in image manifest lists +DOCKER_PLATFORMS := linux/amd64 +DOCKER_PLATFORMS_STATIC := linux/amd64,linux/arm64 + +BIN := opa_$(GOOS)_$(GOARCH) + +# Optional external configuration useful for forks of OPA +DOCKER_IMAGE ?= openpolicyagent/opa +S3_RELEASE_BUCKET ?= opa-releases +FUZZ_TIME ?= 1h +TELEMETRY_URL ?= #Default empty + +BUILD_HOSTNAME := $(shell ./build/get-build-hostname.sh) + +RELEASE_BUILD_IMAGE := golang:$(GOVERSION)-bullseye + +RELEASE_DIR ?= _release/$(VERSION) + +ifneq (,$(TELEMETRY_URL)) +TELEMETRY_FLAG := -X github.com/open-policy-agent/opa/internal/report.ExternalServiceURL=$(TELEMETRY_URL) +endif + +LDFLAGS := "$(TELEMETRY_FLAG) \ + -X github.com/open-policy-agent/opa/version.Hostname=$(BUILD_HOSTNAME)" + + +###################################################### +# +# Development targets +# +###################################################### + +# If you update the 'all' target make sure the 'ci-release-test' target is consistent. +.PHONY: all +all: build test perf wasm-sdk-e2e-test check + +.PHONY: version +version: + @echo $(VERSION) + +.PHONY: release-dir +release-dir: + @echo $(RELEASE_DIR) + +.PHONY: generate +generate: wasm-lib-build +ifeq ($(GOOS),windows) + GOOS=$(shell go env GOOS) GOARCH=$(shell go env GOARCH) go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@v1.5.0 +endif + $(GO) generate + +.PHONY: build +build: go-build + +.PHONY: image +image: + DOCKER_UID=$(shell id -u) DOCKER_GID=$(shell id -g) $(MAKE) ci-go-ci-build-linux ci-go-ci-build-linux-static + @$(MAKE) image-quick + +.PHONY: install +install: generate + $(GO) install $(GO_TAGS) -ldflags $(LDFLAGS) + +.PHONY: test +test: go-test wasm-test + +.PHONY: test-short +test-short: go-test-short + +.PHONY: go-build +go-build: generate + $(GO) build $(GO_TAGS) -o $(BIN) -ldflags $(LDFLAGS) + +.PHONY: go-test +go-test: generate + $(GO) test $(GO_TAGS),slow ./... + +.PHONY: go-test-short +go-test-short: generate + $(GO) test $(GO_TAGS) -short ./... + +.PHONY: race-detector +race-detector: generate + $(GO) test $(GO_TAGS),slow -race -vet=off ./... + +.PHONY: test-coverage +test-coverage: generate + $(GO) test $(GO_TAGS),slow -coverprofile=coverage.txt -covermode=atomic ./... + +.PHONY: perf +perf: generate + $(GO) test $(GO_TAGS),slow $(GO_TEST_TIMEOUT) -run=- -bench=. -benchmem ./... + +.PHONY: perf-noisy +perf-noisy: generate + $(GO) test $(GO_TAGS),slow,noisy $(GO_TEST_TIMEOUT) -run=- -bench=. -benchmem ./... + +.PHONY: wasm-sdk-e2e-test +wasm-sdk-e2e-test: generate + $(GO) test $(GO_TAGS),slow,wasm_sdk_e2e $(GO_TEST_TIMEOUT) ./internal/wasm/sdk/test/e2e + +.PHONY: check +check: +ifeq ($(DOCKER_RUNNING), 1) + docker run --rm -v $(shell pwd):/app:ro,Z -w /app golangci/golangci-lint:${GOLANGCI_LINT_VERSION} golangci-lint run -v +else + @echo "Docker not installed or running. Skipping golangci run." +endif + +.PHONY: fmt +fmt: +ifeq ($(DOCKER_RUNNING), 1) + docker run --rm -v $(shell pwd):/app:Z -w /app golangci/golangci-lint:${GOLANGCI_LINT_VERSION} golangci-lint run -v --fix +else + @echo "Docker not installed or running. Skipping golangci run." +endif + +.PHONY: clean +clean: wasm-lib-clean + rm -f opa_*_* + +.PHONY: fuzz +fuzz: + go test ./ast -fuzz FuzzParseStatementsAndCompileModules -fuzztime ${FUZZ_TIME} -v -run '^$$' + +###################################################### +# +# Documentation targets +# +###################################################### + +# The docs-% pattern target will shim to the +# makefile in ./docs +.PHONY: docs-% +docs-%: + $(MAKE) -C docs $* + +.PHONY: man +man: + ./build/gen-man.sh man + +###################################################### +# +# Linux distro package targets +# +###################################################### + +.PHONY: deb +deb: + VERSION=$(VERSION) ./build/gen-deb.sh + +###################################################### +# +# Wasm targets +# +###################################################### + +.PHONY: wasm-test +wasm-test: wasm-lib-test wasm-rego-test + +.PHONY: wasm-lib-build +wasm-lib-build: +ifeq ($(DOCKER_RUNNING), 1) + @$(MAKE) -C wasm ensure-builder build + cp wasm/_obj/opa.wasm internal/compiler/wasm/opa/opa.wasm + cp wasm/_obj/callgraph.csv internal/compiler/wasm/opa/callgraph.csv +else + @echo "Docker not installed or not running. Skipping OPA-WASM library build." +endif + +.PHONY: wasm-lib-test +wasm-lib-test: +ifeq ($(DOCKER_RUNNING), 1) + @$(MAKE) -C wasm ensure-builder test +else + @echo "Docker not installed or not running. Skipping OPA-WASM library test." +endif + +.PHONY: wasm-rego-test +wasm-rego-test: generate +ifeq ($(DOCKER_RUNNING), 1) + GOVERSION=$(GOVERSION) DOCKER_UID=$(DOCKER_UID) DOCKER_GID=$(DOCKER_GID) ./build/run-wasm-rego-tests.sh +else + @echo "Docker not installed or not running. Skipping Rego-WASM test." +endif + +.PHONY: wasm-lib-clean +wasm-lib-clean: + @$(MAKE) -C wasm clean + +.PHONY: wasm-rego-testgen-install +wasm-rego-testgen-install: + $(GO) install ./v1/test/wasm/cmd/wasm-rego-testgen + +###################################################### +# +# CI targets +# +###################################################### + +CI_GOLANG_DOCKER_MAKE := $(DOCKER) run \ + $(DOCKER_FLAGS) \ + -u $(DOCKER_UID):$(DOCKER_GID) \ + -v $(PWD):/src \ + -w /src \ + -e GOCACHE=/src/.go/cache \ + -e GOARCH=$(GOARCH) \ + -e CGO_ENABLED=$(CGO_ENABLED) \ + -e WASM_ENABLED=$(WASM_ENABLED) \ + -e FUZZ_TIME=$(FUZZ_TIME) \ + -e TELEMETRY_URL=$(TELEMETRY_URL) \ + $(RELEASE_BUILD_IMAGE) + +.PHONY: ci-go-% +ci-go-%: generate + $(CI_GOLANG_DOCKER_MAKE) /bin/bash -c "git config --system --add safe.directory /src && make $*" + +.PHONY: ci-release-test +ci-release-test: generate + $(CI_GOLANG_DOCKER_MAKE) make test perf wasm-sdk-e2e-test check + +.PHONY: ci-check-working-copy +ci-check-working-copy: generate + ./build/check-working-copy.sh + +.PHONY: ci-wasm +ci-wasm: wasm-test + +.PHONY: ci-build-linux +ci-build-linux: ensure-release-dir ensure-linux-toolchain + @$(MAKE) build GOOS=linux + chmod +x opa_linux_$(GOARCH) + mv opa_linux_$(GOARCH) $(RELEASE_DIR)/ + cd $(RELEASE_DIR)/ && shasum -a 256 opa_linux_$(GOARCH) > opa_linux_$(GOARCH).sha256 + +.PHONY: ci-build-linux-static +ci-build-linux-static: ensure-release-dir + @$(MAKE) build GOOS=linux WASM_ENABLED=0 CGO_ENABLED=0 + chmod +x opa_linux_$(GOARCH) + mv opa_linux_$(GOARCH) $(RELEASE_DIR)/opa_linux_$(GOARCH)_static + cd $(RELEASE_DIR)/ && shasum -a 256 opa_linux_$(GOARCH)_static > opa_linux_$(GOARCH)_static.sha256 + +.PHONY: ci-build-darwin +ci-build-darwin: ensure-release-dir + @$(MAKE) build GOOS=darwin + chmod +x opa_darwin_$(GOARCH) + mv opa_darwin_$(GOARCH) $(RELEASE_DIR)/ + cd $(RELEASE_DIR)/ && shasum -a 256 opa_darwin_$(GOARCH) > opa_darwin_$(GOARCH).sha256 + +.PHONY: ci-build-darwin-arm64-static +ci-build-darwin-arm64-static: ensure-release-dir + @$(MAKE) build GOOS=darwin GOARCH=arm64 WASM_ENABLED=0 CGO_ENABLED=0 + chmod +x opa_darwin_arm64 + mv opa_darwin_arm64 $(RELEASE_DIR)/opa_darwin_arm64_static + cd $(RELEASE_DIR)/ && shasum -a 256 opa_darwin_arm64_static > opa_darwin_arm64_static.sha256 + +# NOTE: This target expects to be run as root on some debian/ubuntu variant +# that can install the `gcc-mingw-w64-x86-64` package via apt-get. +.PHONY: ci-build-windows +ci-build-windows: ensure-release-dir + build/ensure-windows-toolchain.sh + @$(MAKE) build GOOS=windows CC=x86_64-w64-mingw32-gcc + mv opa_windows_$(GOARCH) $(RELEASE_DIR)/opa_windows_$(GOARCH).exe + cd $(RELEASE_DIR)/ && shasum -a 256 opa_windows_$(GOARCH).exe > opa_windows_$(GOARCH).exe.sha256 + rm resource.syso + +.PHONY: ensure-release-dir +ensure-release-dir: + mkdir -p $(RELEASE_DIR) + +.PHONY: ensure-executable-bin +ensure-executable-bin: + find $(RELEASE_DIR) -type f ! -name "*.sha256" | xargs chmod +x + +.PHONY: ensure-linux-toolchain +ensure-linux-toolchain: +ifeq ($(CGO_ENABLED),1) + $(eval export CC = $(shell GOARCH=$(GOARCH) build/ensure-linux-toolchain.sh)) +else + @echo "CGO_ENABLED=$(CGO_ENABLED). No need to check gcc toolchain." +endif + +.PHONY: build-all-platforms +build-all-platforms: ci-build-linux ci-build-linux-static ci-build-darwin ci-build-darwin-arm64-static ci-build-windows + +.PHONY: image-quick +image-quick: image-quick-$(GOARCH) + +# % = arch +.PHONY: image-quick-% +image-quick-%: ensure-executable-bin +ifneq ($(GOARCH),arm64) # build only static images for arm64 + $(DOCKER) build \ + -t $(DOCKER_IMAGE):$(VERSION) \ + --build-arg BASE=chainguard/glibc-dynamic \ + --build-arg BIN_DIR=$(RELEASE_DIR) \ + --platform linux/$* \ + . + $(DOCKER) build \ + -t $(DOCKER_IMAGE):$(VERSION)-debug \ + --build-arg BASE=chainguard/glibc-dynamic:latest-dev \ + --build-arg BIN_DIR=$(RELEASE_DIR) \ + --platform linux/$* \ + . +endif + $(DOCKER) build \ + -t $(DOCKER_IMAGE):$(VERSION)-static \ + --build-arg BASE=chainguard/static:latest \ + --build-arg BIN_DIR=$(RELEASE_DIR) \ + --build-arg BIN_SUFFIX=_static \ + --platform linux/$* \ + . + + $(DOCKER) build \ + -t $(DOCKER_IMAGE):$(VERSION)-static-debug \ + --build-arg BASE=chainguard/busybox:latest-glibc \ + --build-arg BIN_DIR=$(RELEASE_DIR) \ + --build-arg BIN_SUFFIX=_static \ + --platform linux/$* \ + . + +# % = base tag +.PHONY: push-manifest-list-% +push-manifest-list-%: ensure-executable-bin + $(DOCKER) buildx build \ + --tag $(DOCKER_IMAGE):$* \ + --build-arg BASE=chainguard/glibc-dynamic:latest \ + --build-arg BIN_DIR=$(RELEASE_DIR) \ + --platform $(DOCKER_PLATFORMS) \ + --provenance=false \ + --push \ + . + $(DOCKER) buildx build \ + --tag $(DOCKER_IMAGE):$*-debug \ + --build-arg BASE=chainguard/glibc-dynamic:latest-dev \ + --build-arg BIN_DIR=$(RELEASE_DIR) \ + --platform $(DOCKER_PLATFORMS) \ + --provenance=false \ + --push \ + . + + $(DOCKER) buildx build \ + --tag $(DOCKER_IMAGE):$*-static \ + --build-arg BASE=chainguard/static:latest \ + --build-arg BIN_DIR=$(RELEASE_DIR) \ + --build-arg BIN_SUFFIX=_static \ + --platform $(DOCKER_PLATFORMS_STATIC) \ + --provenance=false \ + --push \ + . + + $(DOCKER) buildx build \ + --tag $(DOCKER_IMAGE):$*-static-debug \ + --build-arg BASE=chainguard/busybox:latest-glibc \ + --build-arg BIN_DIR=$(RELEASE_DIR) \ + --build-arg BIN_SUFFIX=_static \ + --platform $(DOCKER_PLATFORMS_STATIC) \ + --provenance=false \ + --push \ + . + +.PHONY: ci-image-smoke-test +ci-image-smoke-test: ci-image-smoke-test-$(GOARCH) + +# % = arch +.PHONY: ci-image-smoke-test-% +ci-image-smoke-test-%: image-quick-% +ifneq ($(GOARCH),arm64) # we build only static images for arm64 + $(DOCKER) run --platform linux/$* $(DOCKER_IMAGE):$(VERSION) version + $(DOCKER) run --platform linux/$* $(DOCKER_IMAGE):$(VERSION)-debug version + + $(DOCKER) image inspect $(DOCKER_IMAGE):$(VERSION) |\ + $(DOCKER) run --interactive --platform linux/$* $(DOCKER_IMAGE):$(VERSION) \ + eval --fail --format raw --stdin-input 'input[0].Config.User = "1000:1000"' +endif + $(DOCKER) run --platform linux/$* $(DOCKER_IMAGE):$(VERSION)-static version + +# % = rego/wasm +.PHONY: ci-binary-smoke-test-% +ci-binary-smoke-test-%: + chmod +x "$(RELEASE_DIR)/$(BINARY)" + ./build/binary-smoke-test.sh "$(RELEASE_DIR)/$(BINARY)" "$*" + +.PHONY: push-binary-edge +push-binary-edge: + aws s3 sync $(RELEASE_DIR) s3://$(S3_RELEASE_BUCKET)/edge/ --no-progress --region us-west-1 + +.PHONY: docker-login +docker-login: + @echo "Docker Login..." + @echo ${DOCKER_PASSWORD} | $(DOCKER) login -u ${DOCKER_USER} --password-stdin + +.PHONY: push-image +push-image: docker-login push-manifest-list-$(VERSION) + +.PHONY: push-wasm-builder-image +push-wasm-builder-image: docker-login + $(MAKE) -C wasm push-builder + +.PHONY: deploy-ci +deploy-ci: push-image push-manifest-list-edge push-binary-edge + +.PHONY: release-ci +# Don't tag and push "latest" image tags if the version is a release candidate or a bugfix branch +# where the changes don't exist in main +ifneq (,$(or $(findstring rc,$(VERSION)), $(findstring release-,$(shell git branch --contains HEAD)))) +release-ci: push-image +else +release-ci: push-image push-manifest-list-latest +endif + +.PHONY: netlify +netlify: docs-clean docs-ci docs-build + +# Kept for compatibility. Use `make fuzz` instead. +.PHONY: check-fuzz +check-fuzz: fuzz + +# GOPRIVATE=* causes go to fetch all dependencies from their corresponding VCS +# source, not through the golang-provided proxy services. We're cleaning out +# /src/.go by providing a tmpfs mount, so the `go mod vendor -v` command will +# not be able to use any module cache. +.PHONY: check-go-module +check-go-module: + docker run \ + $(DOCKER_FLAGS) \ + -w /src \ + -v $(PWD):/src:Z \ + -e 'GOPRIVATE=*' \ + --tmpfs /src/.go \ + $(RELEASE_BUILD_IMAGE) \ + /bin/bash -c "git config --system --add safe.directory /src && go mod vendor -v" + +.PHONY: check-yaml-tests +check-yaml-tests: +ifeq ($(DOCKER_RUNNING), 1) + docker run --rm -v $(shell pwd):/data:ro,Z -w /data pipelinecomponents/yamllint:${YAML_LINT_VERSION} yamllint -f $(YAML_LINT_FORMAT) v1/test/cases/testdata +else + @echo "Docker not installed or running. Skipping yamllint run." +endif + +###################################################### +# +# Release targets +# +###################################################### + +.PHONY: release-patch +release-patch: +ifeq ($(GITHUB_TOKEN),) + @echo "\033[0;31mGITHUB_TOKEN environment variable missing.\033[33m Provide a GitHub Personal Access Token (PAT) with the 'read:org' scope.\033[0m" +endif + @$(DOCKER) run $(DOCKER_FLAGS) \ + -e GITHUB_TOKEN=$(GITHUB_TOKEN) \ + -e LAST_VERSION=$(LAST_VERSION) \ + -v $(PWD):/_src:Z \ + ashtalk/python-go-perl:v2 \ + /_src/build/gen-release-patch.sh --version=$(VERSION) --source-url=/_src + +.PHONY: dev-patch +dev-patch: + @$(DOCKER) run $(DOCKER_FLAGS) \ + -v $(PWD):/_src:Z \ + ashtalk/python-go-perl:v2 \ + /_src/build/gen-dev-patch.sh --version=$(VERSION) --source-url=/_src + +# Deprecated targets. To be removed. +.PHONY: build-linux depr-build-linux build-windows depr-build-windows build-darwin depr-build-darwin release release-local +build-linux: deprecation-build-linux +build-windows: deprecation-build-windows +build-darwin: deprecation-build-darwin +release: deprecation-release +release-local: deprecation-release-local + +.PHONY: deprecation-% +deprecation-%: + @echo "----------------------------------------------" + @echo "The '$*' make target is deprecated!" + @echo "----------------------------------------------" + @echo "To run build for your platform, use 'make build'." + @echo "To cross-compile for a specific platform, use the corresponding 'ci-build-*' target." + @echo + @$(MAKE) depr-$* + +depr-build-linux: ensure-release-dir + @$(MAKE) build GOOS=linux CGO_ENABLED=0 WASM_ENABLED=0 + mv opa_linux_$(GOARCH) $(RELEASE_DIR)/ + +depr-build-darwin: ensure-release-dir + @$(MAKE) build GOOS=darwin CGO_ENABLED=0 WASM_ENABLED=0 + mv opa_darwin_$(GOARCH) $(RELEASE_DIR)/ + +depr-build-windows: ensure-release-dir + @$(MAKE) build GOOS=windows CGO_ENABLED=0 WASM_ENABLED=0 + mv opa_windows_$(GOARCH) $(RELEASE_DIR)/opa_windows_$(GOARCH).exe + rm resource.syso + +depr-release: + $(DOCKER) run $(DOCKER_FLAGS) \ + -v $(PWD)/$(RELEASE_DIR):/$(RELEASE_DIR):Z \ + -v $(PWD):/_src:Z \ + -e TELEMETRY_URL=$(TELEMETRY_URL) \ + $(RELEASE_BUILD_IMAGE) \ + /_src/build/build-release.sh --version=$(VERSION) --output-dir=/$(RELEASE_DIR) --source-url=/_src + +depr-release-local: + $(DOCKER) run $(DOCKER_FLAGS) \ + -v $(PWD)/$(RELEASE_DIR):/$(RELEASE_DIR):Z \ + -v $(PWD):/_src:Z \ + -e TELEMETRY_URL=$(TELEMETRY_URL) \ + $(RELEASE_BUILD_IMAGE) \ + /_src/build/build-release.sh --output-dir=/$(RELEASE_DIR) --source-url=/_src diff --git a/third_party/opa/PROVENANCE.md b/third_party/opa/PROVENANCE.md new file mode 100644 index 000000000000..e6840818f75f --- /dev/null +++ b/third_party/opa/PROVENANCE.md @@ -0,0 +1,18 @@ +# Temporary maintained-YAML backport + +Source: https://github.com/DataDog/opa +Exact source commit: `d2e1e78e081663d4b11109032715b68eb9b9d17a` (v0.0.0-20251126100856-d2e1e78e0816). +Logical module identity: `github.com/open-policy-agent/opa`. + +Upstream source, tests, fixtures, licenses and attribution are retained. Upstream +CI metadata and vendored dependency snapshots are excluded; dependency sources +continue to resolve through Go modules. The only code edits select the matching +maintained YAML v2/v3 API, including typed YAML node methods and tests. Module +metadata changes select those parsers and satisfy their Go minima. + +Tracking and temporary ownership: https://github.com/StackVista/stackstate/issues/717 +Removal condition: adopt a compatible owning-library release (or Datadog +lightweight OPA patch release) with maintained YAML and passing consumer tests, +then remove this source and its explicit root/workspace/consumer selections. +Dependency replacements do not propagate: external consumers must explicitly +select this independently addressable nested module as well. diff --git a/third_party/opa/README.md b/third_party/opa/README.md new file mode 100644 index 000000000000..33e51ddefefb --- /dev/null +++ b/third_party/opa/README.md @@ -0,0 +1,104 @@ +# ![logo](./logo/logo-144x144.png) Open Policy Agent + +[![Build Status](https://github.com/open-policy-agent/opa/workflows/Post%20Merge/badge.svg)](https://github.com/open-policy-agent/opa/actions) [![Go Report Card](https://goreportcard.com/badge/open-policy-agent/opa)](https://goreportcard.com/report/open-policy-agent/opa) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1768/badge)](https://bestpractices.coreinfrastructure.org/projects/1768) [![Netlify Status](https://api.netlify.com/api/v1/badges/4a0a092a-8741-4826-a28f-826d4a576cab/deploy-status)](https://app.netlify.com/sites/openpolicyagent/deploys) + +Open Policy Agent (OPA) is an open source, general-purpose policy engine that enables unified, context-aware policy enforcement across the entire stack. + +OPA is proud to be a graduated project in the [Cloud Native Computing Foundation](https://cncf.io) (CNCF) landscape. For details read the CNCF [announcement](https://www.cncf.io/announcements/2021/02/04/cloud-native-computing-foundation-announces-open-policy-agent-graduation/). + +## Get started with OPA + +- Write your first Rego policy with the [Rego Playground](https://play.openpolicyagent.org) or use it to share your work with others for feedback and support. Have a look at the [Access Control examples](https://play.openpolicyagent.org/?example-group=access-control) if you're not sure where to start. +- Install the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=tsandall.opa) to get started locally with live diagnostics, debugging and formatting. See [Editor and IDE Support](https://www.openpolicyagent.org/docs/editor-and-ide-support/) for other supported editors. +- Go to the [OPA Documentation](https://www.openpolicyagent.org/docs/latest/) to + learn about the Rego language as well as how to deploy and integrate OPA. +- Check out the learning resources in the [Learning Rego](https://www.openpolicyagent.org/ecosystem/by-feature/learning-rego/) section of the ecosystem directory. +- Follow the [Running OPA](https://www.openpolicyagent.org/docs/latest/#running-opa) instructions to get started with the OPA CLI locally. +- See [Docker Hub](https://hub.docker.com/r/openpolicyagent/opa/tags/) for container images and the [GitHub releases](https://github.com/open-policy-agent/opa/releases) for binaries. +- Check out the [OPA Roadmap](https://docs.google.com/presentation/d/16QV6gvLDOV3I0_guPC3_19g6jHkEg3X9xqMYgtoCKrs/edit?usp=sharing) to see a high-level snapshot of OPA features in-progress and planned. + +## Want to talk about OPA or get support? + +- Join the [OPA Slack](https://slack.openpolicyagent.org) to talk to other OPA users and maintainers. See `#help` for support. +- Check out the [Community Discussions](https://github.com/orgs/open-policy-agent/discussions) to ask questions. +- See the [Support](https://www.openpolicyagent.org/support/) page for commercial support options. + +## Interested to learn what others are doing with OPA? + +- Browse community projects on the [OPA Ecosystem Directory](http://openpolicyagent.org/ecosystem/) - don't forget to [list your own](https://github.com/open-policy-agent/opa/tree/main/docs#opa-ecosystem)! +- Check out the [ADOPTERS.md](./ADOPTERS.md) file for a list of production adopters. Does your organization use OPA in production? Support the OPA project by submitting a PR to add your organization to the list with a short description of your OPA use cases! + +## Want to integrate OPA? + +- See the high-level [Go SDK](https://www.openpolicyagent.org/docs/latest/integration/#integrating-with-the-go-sdk) or the low-level Go API + [![GoDoc](https://godoc.org/github.com/open-policy-agent/opa?status.svg)](https://godoc.org/github.com/open-policy-agent/opa/rego) + to integrate OPA with services written in Go. +- See the [REST API](https://www.openpolicyagent.org/docs/rest-api.html) + reference to integrate OPA with services written in other languages. +- See the [integration docs](https://www.openpolicyagent.org/docs/latest/integration/) for more options. + +## Want to contribute to OPA? + +- Read the [Contributing Guide](https://www.openpolicyagent.org/docs/latest/contributing/) to learn how to make your first contribution. +- Use [#contributors](https://openpolicyagent.slack.com/archives/C02L1TLPN59) in Slack to talk to other contributors and OPA maintainers. +- File a [GitHub Issue](https://github.com/open-policy-agent/opa/issues) to request features or report bugs. + +## How does OPA work? + +OPA gives you a high-level declarative language to author and enforce policies +across your stack. + +With OPA, you define _rules_ that govern how your system should behave. These +rules exist to answer questions like: + +- Can user X call operation Y on resource Z? +- What clusters should workload W be deployed to? +- What tags must be set on resource R before it's created? + +You integrate services with OPA so that these kinds of policy decisions do not +have to be _hardcoded_ in your service. Services integrate with OPA by +executing _queries_ when policy decisions are needed. + +When you query OPA for a policy decision, OPA evaluates the rules and data +(which you give it) to produce an answer. The policy decision is sent back as +the result of the query. + +For example, in a simple API authorization use case: + +- You write rules that allow (or deny) access to your service APIs. +- Your service queries OPA when it receives API requests. +- OPA returns allow (or deny) decisions to your service. +- Your service _enforces_ the decisions by accepting or rejecting requests accordingly. + +For concrete examples of how to integrate OPA with systems like +[Kubernetes](https://www.openpolicyagent.org/docs/kubernetes), +[Terraform](https://www.openpolicyagent.org/docs/terraform), +[Docker](https://www.openpolicyagent.org/docs/docker-authorization), +[SSH](https://www.openpolicyagent.org/docs/ssh-and-sudo-authorization), +and more, see [openpolicyagent.org](https://www.openpolicyagent.org). + +## Presentations + +- Open Policy Agent (OPA) Intro & Deep Dive @ Kubecon NA 2023: [video](https://www.youtube.com/watch?v=wJkjsvVpj_Q) +- Open Policy Agent (OPA) Intro & Deep Dive @ Kubecon EU 2023: [video](https://www.youtube.com/watch?v=6RNp3m_THw4) +- Running Policy in Hard to Reach Places with WASM & OPA @ CN Wasm Day EU 2023: [video](https://www.youtube.com/watch?v=BdeBhukLwt4) +- OPA maintainers talk @ Kubecon NA 2022: [video](https://www.youtube.com/watch?v=RMiovzGGCfI) +- Open Policy Agent (OPA) Intro & Deep Dive @ Kubecon EU 2022: [video](https://www.youtube.com/watch?v=MhyQxIp1H58) +- Open Policy Agent Intro @ KubeCon EU 2021: [Video](https://www.youtube.com/watch?v=2CgeiWkliaw) +- Using Open Policy Agent to Meet Evolving Policy Requirements @ KubeCon NA 2020: [video](https://www.youtube.com/watch?v=zVuM7F_BTyc) +- Applying Policy Throughout The Application Lifecycle with Open Policy Agent @ CloudNativeCon 2019: [video](https://www.youtube.com/watch?v=cXfsaE6RKfc) +- Open Policy Agent Introduction @ CloudNativeCon EU 2018: [video](https://youtu.be/XEHeexPpgrA), [slides](https://www.slideshare.net/TorinSandall/opa-the-cloud-native-policy-engine) +- Rego Deep Dive @ CloudNativeCon EU 2018: [video](https://youtu.be/4mBJSIhs2xQ), [slides](https://www.slideshare.net/TorinSandall/rego-deep-dive) +- How Netflix Is Solving Authorization Across Their Cloud @ CloudNativeCon US 2017: [video](https://www.youtube.com/watch?v=R6tUNpRpdnY), [slides](https://www.slideshare.net/TorinSandall/how-netflix-is-solving-authorization-across-their-cloud). +- Policy-based Resource Placement in Kubernetes Federation @ LinuxCon Beijing 2017: [slides](https://www.slideshare.net/TorinSandall/policybased-resource-placement-across-hybrid-cloud), [screencast](https://www.youtube.com/watch?v=hRz13baBhfg&feature=youtu.be) +- Enforcing Bespoke Policies In Kubernetes @ KubeCon US 2017: [video](https://www.youtube.com/watch?v=llDI8VvkUj8), [slides](https://www.slideshare.net/TorinSandall/enforcing-bespoke-policies-in-kubernetes) +- Istio's Mixer: Policy Enforcement with Custom Adapters @ CloudNativeCon US 2017: [video](https://www.youtube.com/watch?v=czZLXUqzd24), [slides](https://www.slideshare.net/TorinSandall/istios-mixer-policy-enforcement-with-custom-adapters-cloud-nativecon-17) + +## Security + +A third party security audit was performed by Cure53, you can see the full report [here](SECURITY_AUDIT.pdf). + +Please report vulnerabilities by email to [open-policy-agent-security](mailto:open-policy-agent-security@googlegroups.com). +We will send a confirmation message to acknowledge that we have received the +report and then we will send additional messages to follow up once the issue +has been investigated. diff --git a/third_party/opa/SECURITY.md b/third_party/opa/SECURITY.md new file mode 100644 index 000000000000..d90b7e962ff8 --- /dev/null +++ b/third_party/opa/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +Please refer to the [OPA Security Policy](https://openpolicyagent.org/security) +for details on how to report security issues, our disclosure policy, and how to +receive notifications about security issues. \ No newline at end of file diff --git a/third_party/opa/SECURITY_AUDIT.pdf b/third_party/opa/SECURITY_AUDIT.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ccc3b57f4bca65e1f8ba05a9851b7c9b932827de GIT binary patch literal 283922 zcma&NQ*@?Z5bhav)M3X?$F^DcBM+qTV)ZQJ~!4!@*h+n)ZPIcLtSb>?F3YVG&x ztyND|?fqL1xssR!Ju?FvJo!+`Q1?*lP(C~hF%z+au{As&AETU^y#>IMnDyU@3ZsOT zEx^o~QNq>;U?yf};$UhfAOP1^^ny9(%k6c_Gy7{9WzKH+ z&fx3rzRsWLYwo1&&R~{aVASQ}0RQxHb(VTg@YYXYb+Fvx+5hB&?z!&E6~2Zq&1GZV zM8Kc!*mkGSx5pC=d8-905T@s^8Y$k0o!zf7%TxFV3E%StrZmG(6jEGZKo zPCSGog>+p4$T96Q^|8IbS>FZ2nZAe)A<+YtP}{~#X(UQ2bx_^W+E~`2!Ge(FgUjPS zS8uwQJ8H^9nV|EAu$`Pu0GLppSVI`TRHR60nYRgi3rLz`wtF7cyjjChg#t(z(TU}O zI+rMj_)zQ?lqHagR-gXjV=TJ~=D78!+>YD^kVZTUDOTV=WvXVAVgJ@wB$CFz7W99I z3!~8A12&JNq8Y3%$tp;>R4Id=Q6v7cI+k{70#+9zp`r(@CX3hJ!KnANN)NDeVVzE= z4!z`vZi_EnlwSlep@Qh`4m8hUC=~ zOEU?8mfcL`Qmh>rvNO%R5G~n~#?T^ro0D=S7SR*{2-y2IT=Xbhw3fY@fvAp;843iC z#1M>Uxx1CSDL)*u00BcH!X|D=Vm9dHr6m)JVO%-L$oqKU<2K+Xvq+=31Cs$zSI6Dg zuS3BvJ4p8g8KhRqi{V60&=@qG8nF5dkv14C_uk}Lx!1ZJ-&$r*1`hI4gC z2@~k!g<+D!B80wCYzt3L8_FowR3$LwxFa(_XmylhLR1YZ<@fxxdEOlgre3uG+pM$r z;{wG}InLiud3YC6I{IgD4>V42l~1|L3LPf5+kfB#Lq@@?07ZZwH6Z|sX8Vg3r?jMWGtsf?+uusoJfKbvO_fQ zfT+)NLi5c?+nV?*y4k4sz{6CQzd`IcczT4|AxI|GLE0#*!rRQ_tGMMj3TEPfjqfKf zbswG#iRJyn0ZeM##ANJJfAxu*z%9~x92M#B5rQReKlfm81?eoxouUy8jxfkP&U0LK z^6W2{T+$g6SeJ@T^BAmg#t)~`GCa!BN)u>g6cu(Q09)LCwX|DpyfWklxUTu;H?+#) zdgq@BY0-xa;&R-L&GI=u#PtNLPG6q5ER)Ej7R4qknWdJUq94B{kK9=h-BLnrm8MzL zA#*h}Ofx4h)yg9Srlf^iFZ*Dbh?Q9P<3nR%Ke?iy<2!{9P#ba}OcBR-F2Ex+WBN`d z*eG^DOBI5A8Pk}5DIp-v-w1Ok%6C6b5BBB(_#AK-VfTMu60LHGV|$NX^e61lh6oY$ znRx*$OFOY1e}USEAAF{h@f=|^rj)AKz(AGlk{sw~Z5CVY#l^88A>doNQwmtFRzDL~ ztGWq|t#UFsP`oeVSOr~+@Zv4A%X4>p{$T#Vhe&*e-^CO+~Je^huj zLEGtC0uxTuYRBw&cgrk|!XG*pO4zpklZnk1kV_u*O0g$p1;`|=DijNoD{39LpyKk= zX1Cz+Jwn8})S$xsCm>r5vJN+Kt>}fcWnV08_LA&{g-U~YbkSZcwo7pFQ!D!nG+YAp zWhM3dQRU4%{AJ)sVYejZPQvVHx}`crQMm(6+oY5xr6##M7j0;qFk43u(EFpQqf<7A zFcCP-w>&g^&Rdw~Cx- z7~a=faCYx-S`Pgqw!aq-t^9kJBkC9-K%}7NNA+#UH^zWK7=#TJI-uy%%Oe4I)tIqs z2!FsyT;R$4NDpmzd8KXS4@GSWut0lX@fsM^&KyBeGT)KJaR5^DxtZnWszc)%F-=pp5jOg5OQoP^|DwpLN1UMu~z#|0mLhz;U$WdL%B!u=$z<8$m5!qqVkM7((uo3f)9fy=-HZ`zOYw=P(H^jNoet%UNJT{> zq4Ch*ix_5l=AI#op{>=Iym<^F2O`QYQaT)M{!Hj;X-LYr`~}o5HGK?ZuH3OqI)*!Zw8A}i^oi`1-xfoy$LzVk=tBlDV$&^JhP#mR+n4n;h24K--QzInN4W^Ull=%S_I ziG~w@q7S9!tri~^#)8c6Xwi{S%$4l4LGC%>s(;0O{v}XClv32l>IB8C1QPdyYT~w# z)S^lnEb3C;4bUKW{(sl$UgGR1p4HUw1iiV65+(RWJI>FWg~Mv}VXnoD=+v!WA&6S< z8XywOPwv53cbe8HskJ{G$@vtG!?e`4`5q54)~wA}2b7N2c&_0LM{iWz58@i~N?BO9 zF-%cOcC(r3!+=Iu)c@sV|3go8o4iN*APy)=i=$rTM)x4L%wUrW#Ucn(R$Z?gf#C%k6I=F z780zAJenDdE}az&D9Ba~I!@-!JTcL-t8(qFn3kH1+KQ}X##z0pWB=1_p>aKLt~*!( zj0hoX#@+I+;uk7~=YLriPOQ3NN=lmtPAF;V@QA_P1xUD054feTT~sH!I5}h$cgbTQ z>ETa$16T-8Y`c@sFyvD%3ZN`AtpgMkII<+xwW(ndOL_0$?7NLPwfBdaV;Zhd*!tq- zTx&-&^i+myP^7ofpF)AyO))%ukunx>ZZRL{@Mzk9vh=LtzUacUl}$^y<`_PTb#
rhG&EGxmi}W$$!0XMsw*SO%GN4Qme?TeBPcU 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

fzu$QB{SVz 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�YPO#vDN+VrOZmY^QA}eqEl+dn|FMFH&qN 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 kGNauGU5 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 s{b2@^rUSn)Q true. +// - "[a-z]*" and [0-9]+" -> not true. +var GlobsMatch = v1.GlobsMatch + +/** + * Strings + */ + +var AnyPrefixMatch = v1.AnyPrefixMatch + +var AnySuffixMatch = v1.AnySuffixMatch + +var Concat = v1.Concat + +var FormatInt = v1.FormatInt + +var IndexOf = v1.IndexOf + +var IndexOfN = v1.IndexOfN + +var Substring = v1.Substring + +var Contains = v1.Contains + +var StringCount = v1.StringCount + +var StartsWith = v1.StartsWith + +var EndsWith = v1.EndsWith + +var Lower = v1.Lower + +var Upper = v1.Upper + +var Split = v1.Split + +var Replace = v1.Replace + +var ReplaceN = v1.ReplaceN + +var RegexReplace = v1.RegexReplace + +var Trim = v1.Trim + +var TrimLeft = v1.TrimLeft + +var TrimPrefix = v1.TrimPrefix + +var TrimRight = v1.TrimRight + +var TrimSuffix = v1.TrimSuffix + +var TrimSpace = v1.TrimSpace + +var Sprintf = v1.Sprintf + +var StringReverse = v1.StringReverse + +var RenderTemplate = v1.RenderTemplate + +/** + * Numbers + */ + +// RandIntn returns a random number 0 - n +// Marked non-deterministic because it relies on RNG internally. +var RandIntn = v1.RandIntn + +var NumbersRange = v1.NumbersRange + +var NumbersRangeStep = v1.NumbersRangeStep + +/** + * Units + */ + +var UnitsParse = v1.UnitsParse + +var UnitsParseBytes = v1.UnitsParseBytes + +// +/** + * Type + */ + +// UUIDRFC4122 returns a version 4 UUID string. +// Marked non-deterministic because it relies on RNG internally. +var UUIDRFC4122 = v1.UUIDRFC4122 + +var UUIDParse = v1.UUIDParse + +/** + * JSON + */ + +var JSONFilter = v1.JSONFilter + +var JSONRemove = v1.JSONRemove + +var JSONPatch = v1.JSONPatch + +var ObjectSubset = v1.ObjectSubset + +var ObjectUnion = v1.ObjectUnion + +var ObjectUnionN = v1.ObjectUnionN + +var ObjectRemove = v1.ObjectRemove + +var ObjectFilter = v1.ObjectFilter + +var ObjectGet = v1.ObjectGet + +var ObjectKeys = v1.ObjectKeys + +/* + * Encoding + */ + +var JSONMarshal = v1.JSONMarshal + +var JSONMarshalWithOptions = v1.JSONMarshalWithOptions + +var JSONUnmarshal = v1.JSONUnmarshal + +var JSONIsValid = v1.JSONIsValid + +var Base64Encode = v1.Base64Encode + +var Base64Decode = v1.Base64Decode + +var Base64IsValid = v1.Base64IsValid + +var Base64UrlEncode = v1.Base64UrlEncode + +var Base64UrlEncodeNoPad = v1.Base64UrlEncodeNoPad + +var Base64UrlDecode = v1.Base64UrlDecode + +var URLQueryDecode = v1.URLQueryDecode + +var URLQueryEncode = v1.URLQueryEncode + +var URLQueryEncodeObject = v1.URLQueryEncodeObject + +var URLQueryDecodeObject = v1.URLQueryDecodeObject + +var YAMLMarshal = v1.YAMLMarshal + +var YAMLUnmarshal = v1.YAMLUnmarshal + +// YAMLIsValid verifies the input string is a valid YAML document. +var YAMLIsValid = v1.YAMLIsValid + +var HexEncode = v1.HexEncode + +var HexDecode = v1.HexDecode + +/** + * Tokens + */ + +var JWTDecode = v1.JWTDecode + +var JWTVerifyRS256 = v1.JWTVerifyRS256 + +var JWTVerifyRS384 = v1.JWTVerifyRS384 + +var JWTVerifyRS512 = v1.JWTVerifyRS512 + +var JWTVerifyPS256 = v1.JWTVerifyPS256 + +var JWTVerifyPS384 = v1.JWTVerifyPS384 + +var JWTVerifyPS512 = v1.JWTVerifyPS512 + +var JWTVerifyES256 = v1.JWTVerifyES256 + +var JWTVerifyES384 = v1.JWTVerifyES384 + +var JWTVerifyES512 = v1.JWTVerifyES512 + +var JWTVerifyHS256 = v1.JWTVerifyHS256 + +var JWTVerifyHS384 = v1.JWTVerifyHS384 + +var JWTVerifyHS512 = v1.JWTVerifyHS512 + +// Marked non-deterministic because it relies on time internally. +var JWTDecodeVerify = v1.JWTDecodeVerify + +// Marked non-deterministic because it relies on RNG internally. +var JWTEncodeSignRaw = v1.JWTEncodeSignRaw + +// Marked non-deterministic because it relies on RNG internally. +var JWTEncodeSign = v1.JWTEncodeSign + +/** + * Time + */ + +// Marked non-deterministic because it relies on time directly. +var NowNanos = v1.NowNanos + +var ParseNanos = v1.ParseNanos + +var ParseRFC3339Nanos = v1.ParseRFC3339Nanos + +var ParseDurationNanos = v1.ParseDurationNanos + +var Format = v1.Format + +var Date = v1.Date + +var Clock = v1.Clock + +var Weekday = v1.Weekday + +var AddDate = v1.AddDate + +var Diff = v1.Diff + +/** + * Crypto. + */ + +var CryptoX509ParseCertificates = v1.CryptoX509ParseCertificates + +var CryptoX509ParseAndVerifyCertificates = v1.CryptoX509ParseAndVerifyCertificates + +var CryptoX509ParseAndVerifyCertificatesWithOptions = v1.CryptoX509ParseAndVerifyCertificatesWithOptions + +var CryptoX509ParseCertificateRequest = v1.CryptoX509ParseCertificateRequest + +var CryptoX509ParseKeyPair = v1.CryptoX509ParseKeyPair +var CryptoX509ParseRSAPrivateKey = v1.CryptoX509ParseRSAPrivateKey + +var CryptoParsePrivateKeys = v1.CryptoParsePrivateKeys + +var CryptoMd5 = v1.CryptoMd5 + +var CryptoSha1 = v1.CryptoSha1 + +var CryptoSha256 = v1.CryptoSha256 + +var CryptoHmacMd5 = v1.CryptoHmacMd5 + +var CryptoHmacSha1 = v1.CryptoHmacSha1 + +var CryptoHmacSha256 = v1.CryptoHmacSha256 + +var CryptoHmacSha512 = v1.CryptoHmacSha512 + +var CryptoHmacEqual = v1.CryptoHmacEqual + +/** + * Graphs. + */ + +var WalkBuiltin = v1.WalkBuiltin + +var ReachableBuiltin = v1.ReachableBuiltin + +var ReachablePathsBuiltin = v1.ReachablePathsBuiltin + +/** + * Type + */ + +var IsNumber = v1.IsNumber + +var IsString = v1.IsString + +var IsBoolean = v1.IsBoolean + +var IsArray = v1.IsArray + +var IsSet = v1.IsSet + +var IsObject = v1.IsObject + +var IsNull = v1.IsNull + +/** + * Type Name + */ + +// TypeNameBuiltin returns the type of the input. +var TypeNameBuiltin = v1.TypeNameBuiltin + +/** + * HTTP Request + */ + +// Marked non-deterministic because HTTP request results can be non-deterministic. +var HTTPSend = v1.HTTPSend + +/** + * GraphQL + */ + +// GraphQLParse returns a pair of AST objects from parsing/validation. +var GraphQLParse = v1.GraphQLParse + +// GraphQLParseAndVerify returns a boolean and a pair of AST object from parsing/validation. +var GraphQLParseAndVerify = v1.GraphQLParseAndVerify + +// GraphQLParseQuery parses the input GraphQL query and returns a JSON +// representation of its AST. +var GraphQLParseQuery = v1.GraphQLParseQuery + +// GraphQLParseSchema parses the input GraphQL schema and returns a JSON +// representation of its AST. +var GraphQLParseSchema = v1.GraphQLParseSchema + +// GraphQLIsValid returns true if a GraphQL query is valid with a given +// schema, and returns false for all other inputs. +var GraphQLIsValid = v1.GraphQLIsValid + +// GraphQLSchemaIsValid returns true if the input is valid GraphQL schema, +// and returns false for all other inputs. +var GraphQLSchemaIsValid = v1.GraphQLSchemaIsValid + +/** + * JSON Schema + */ + +// JSONSchemaVerify returns empty string if the input is valid JSON schema +// and returns error string for all other inputs. +var JSONSchemaVerify = v1.JSONSchemaVerify + +// JSONMatchSchema returns empty array if the document matches the JSON schema, +// and returns non-empty array with error objects otherwise. +var JSONMatchSchema = v1.JSONMatchSchema + +/** + * Cloud Provider Helper Functions + */ + +var ProvidersAWSSignReqObj = v1.ProvidersAWSSignReqObj + +/** + * Rego + */ + +var RegoParseModule = v1.RegoParseModule + +var RegoMetadataChain = v1.RegoMetadataChain + +// RegoMetadataRule returns the metadata for the active rule +var RegoMetadataRule = v1.RegoMetadataRule + +/** + * OPA + */ + +// Marked non-deterministic because of unpredictable config/environment-dependent results. +var OPARuntime = v1.OPARuntime + +/** + * Trace + */ + +var Trace = v1.Trace + +/** + * Glob + */ + +var GlobMatch = v1.GlobMatch + +var GlobQuoteMeta = v1.GlobQuoteMeta + +/** + * Networking + */ + +var NetCIDRIntersects = v1.NetCIDRIntersects + +var NetCIDRExpand = v1.NetCIDRExpand + +var NetCIDRContains = v1.NetCIDRContains + +var NetCIDRContainsMatches = v1.NetCIDRContainsMatches + +var NetCIDRMerge = v1.NetCIDRMerge + +var NetCIDRIsValid = v1.NetCIDRIsValid + +// Marked non-deterministic because DNS resolution results can be non-deterministic. +var NetLookupIPAddr = v1.NetLookupIPAddr + +/** + * Semantic Versions + */ + +var SemVerIsValid = v1.SemVerIsValid + +var SemVerCompare = v1.SemVerCompare + +/** + * Printing + */ + +// Print is a special built-in function that writes zero or more operands +// to a message buffer. The caller controls how the buffer is displayed. The +// operands may be of any type. Furthermore, unlike other built-in functions, +// undefined operands DO NOT cause the print() function to fail during +// evaluation. +var Print = v1.Print + +// InternalPrint represents the internal implementation of the print() function. +// The compiler rewrites print() calls to refer to the internal implementation. +var InternalPrint = v1.InternalPrint + +/** + * Deprecated built-ins. + */ + +// SetDiff has been replaced by the minus built-in. +var SetDiff = v1.SetDiff + +// NetCIDROverlap has been replaced by the `net.cidr_contains` built-in. +var NetCIDROverlap = v1.NetCIDROverlap + +// CastArray checks the underlying type of the input. If it is array or set, an array +// containing the values is returned. If it is not an array, an error is thrown. +var CastArray = v1.CastArray + +// CastSet checks the underlying type of the input. +// If it is a set, the set is returned. +// If it is an array, the array is returned in set form (all duplicates removed) +// If neither, an error is thrown +var CastSet = v1.CastSet + +// CastString returns input if it is a string; if not returns error. +// For formatting variables, see sprintf +var CastString = v1.CastString + +// CastBoolean returns input if it is a boolean; if not returns error. +var CastBoolean = v1.CastBoolean + +// CastNull returns null if input is null; if not returns error. +var CastNull = v1.CastNull + +// CastObject returns the given object if it is null; throws an error otherwise +var CastObject = v1.CastObject + +// RegexMatchDeprecated declares `re_match` which has been deprecated. Use `regex.match` instead. +var RegexMatchDeprecated = v1.RegexMatchDeprecated + +// All takes a list and returns true if all of the items +// are true. A collection of length 0 returns true. +var All = v1.All + +// Any takes a collection and returns true if any of the items +// is true. A collection of length 0 returns false. +var Any = v1.Any + +// Builtin represents a built-in function supported by OPA. Every built-in +// function is uniquely identified by a name. +type Builtin = v1.Builtin diff --git a/third_party/opa/ast/capabilities.go b/third_party/opa/ast/capabilities.go new file mode 100644 index 000000000000..bc7278a885ac --- /dev/null +++ b/third_party/opa/ast/capabilities.go @@ -0,0 +1,58 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "io" + + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// VersonIndex contains an index from built-in function name, language feature, +// and future rego keyword to version number. During the build, this is used to +// create an index of the minimum version required for the built-in/feature/kw. +type VersionIndex = v1.VersionIndex + +// In the compiler, we used this to check that we're OK working with ref heads. +// If this isn't present, we'll fail. This is to ensure that older versions of +// OPA can work with policies that we're compiling -- if they don't know ref +// heads, they wouldn't be able to parse them. +const FeatureRefHeadStringPrefixes = v1.FeatureRefHeadStringPrefixes +const FeatureRefHeads = v1.FeatureRefHeads +const FeatureRegoV1 = v1.FeatureRegoV1 +const FeatureRegoV1Import = v1.FeatureRegoV1Import + +// Capabilities defines a structure containing data that describes the capabilities +// or features supported by a particular version of OPA. +type Capabilities = v1.Capabilities + +// WasmABIVersion captures the Wasm ABI version. Its `Minor` version is indicating +// backwards-compatible changes. +type WasmABIVersion = v1.WasmABIVersion + +// CapabilitiesForThisVersion returns the capabilities of this version of OPA. +func CapabilitiesForThisVersion() *Capabilities { + return v1.CapabilitiesForThisVersion(v1.CapabilitiesRegoVersion(DefaultRegoVersion)) +} + +// LoadCapabilitiesJSON loads a JSON serialized capabilities structure from the reader r. +func LoadCapabilitiesJSON(r io.Reader) (*Capabilities, error) { + return v1.LoadCapabilitiesJSON(r) +} + +// LoadCapabilitiesVersion loads a JSON serialized capabilities structure from the specific version. +func LoadCapabilitiesVersion(version string) (*Capabilities, error) { + return v1.LoadCapabilitiesVersion(version) +} + +// LoadCapabilitiesFile loads a JSON serialized capabilities structure from a file. +func LoadCapabilitiesFile(file string) (*Capabilities, error) { + return v1.LoadCapabilitiesFile(file) +} + +// LoadCapabilitiesVersions loads all capabilities versions +func LoadCapabilitiesVersions() ([]string, error) { + return v1.LoadCapabilitiesVersions() +} diff --git a/third_party/opa/ast/check.go b/third_party/opa/ast/check.go new file mode 100644 index 000000000000..4cf00436df1c --- /dev/null +++ b/third_party/opa/ast/check.go @@ -0,0 +1,22 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// UnificationErrDetail describes a type mismatch error when two values are +// unified (e.g., x = [1,2,y]). +type UnificationErrDetail = v1.UnificationErrDetail + +// RefErrUnsupportedDetail describes an undefined reference error where the +// referenced value does not support dereferencing (e.g., scalars). +type RefErrUnsupportedDetail = v1.RefErrUnsupportedDetail + +// RefErrInvalidDetail describes an undefined reference error where the referenced +// value does not support the reference operand (e.g., missing object key, +// invalid key type, etc.) +type RefErrInvalidDetail = v1.RefErrInvalidDetail diff --git a/third_party/opa/ast/compare.go b/third_party/opa/ast/compare.go new file mode 100644 index 000000000000..5e617e992fa1 --- /dev/null +++ b/third_party/opa/ast/compare.go @@ -0,0 +1,39 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// Compare returns an integer indicating whether two AST values are less than, +// equal to, or greater than each other. +// +// If a is less than b, the return value is negative. If a is greater than b, +// the return value is positive. If a is equal to b, the return value is zero. +// +// Different types are never equal to each other. For comparison purposes, types +// are sorted as follows: +// +// nil < Null < Boolean < Number < String < Var < Ref < Array < Object < Set < +// ArrayComprehension < ObjectComprehension < SetComprehension < Expr < SomeDecl +// < With < Body < Rule < Import < Package < Module. +// +// Arrays and Refs are equal if and only if both a and b have the same length +// and all corresponding elements are equal. If one element is not equal, the +// return value is the same as for the first differing element. If all elements +// are equal but a and b have different lengths, the shorter is considered less +// than the other. +// +// Objects are considered equal if and only if both a and b have the same sorted +// (key, value) pairs and are of the same length. Other comparisons are +// consistent but not defined. +// +// Sets are considered equal if and only if the symmetric difference of a and b +// is empty. +// Other comparisons are consistent but not defined. +func Compare(a, b any) int { + return v1.Compare(a, b) +} diff --git a/third_party/opa/ast/compile.go b/third_party/opa/ast/compile.go new file mode 100644 index 000000000000..5a3daa910a7c --- /dev/null +++ b/third_party/opa/ast/compile.go @@ -0,0 +1,127 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// CompileErrorLimitDefault is the default number errors a compiler will allow before +// exiting. +const CompileErrorLimitDefault = 10 + +// Compiler contains the state of a compilation process. +type Compiler = v1.Compiler + +// CompilerStage defines the interface for stages in the compiler. +type CompilerStage = v1.CompilerStage + +// CompilerEvalMode allows toggling certain stages that are only +// needed for certain modes, Concretely, only "topdown" mode will +// have the compiler build comprehension and rule indices. +type CompilerEvalMode = v1.CompilerEvalMode + +const ( + // EvalModeTopdown (default) instructs the compiler to build rule + // and comprehension indices used by topdown evaluation. + EvalModeTopdown = v1.EvalModeTopdown + + // EvalModeIR makes the compiler skip the stages for comprehension + // and rule indices. + EvalModeIR = v1.EvalModeIR +) + +// CompilerStageDefinition defines a compiler stage +type CompilerStageDefinition = v1.CompilerStageDefinition + +// RulesOptions defines the options for retrieving rules by Ref from the +// compiler. +type RulesOptions = v1.RulesOptions + +// QueryContext contains contextual information for running an ad-hoc query. +// +// Ad-hoc queries can be run in the context of a package and imports may be +// included to provide concise access to data. +type QueryContext = v1.QueryContext + +// NewQueryContext returns a new QueryContext object. +func NewQueryContext() *QueryContext { + return v1.NewQueryContext() +} + +// QueryCompiler defines the interface for compiling ad-hoc queries. +type QueryCompiler = v1.QueryCompiler + +// QueryCompilerStage defines the interface for stages in the query compiler. +type QueryCompilerStage = v1.QueryCompilerStage + +// QueryCompilerStageDefinition defines a QueryCompiler stage +type QueryCompilerStageDefinition = v1.QueryCompilerStageDefinition + +// NewCompiler returns a new empty compiler. +func NewCompiler() *Compiler { + return v1.NewCompiler().WithDefaultRegoVersion(DefaultRegoVersion) +} + +// ModuleLoader defines the interface that callers can implement to enable lazy +// loading of modules during compilation. +type ModuleLoader = v1.ModuleLoader + +// SafetyCheckVisitorParams defines the AST visitor parameters to use for collecting +// variables during the safety check. This has to be exported because it's relied on +// by the copy propagation implementation in topdown. +var SafetyCheckVisitorParams = v1.SafetyCheckVisitorParams + +// ComprehensionIndex specifies how the comprehension term can be indexed. The keys +// tell the evaluator what variables to use for indexing. In the future, the index +// could be expanded with more information that would allow the evaluator to index +// a larger fragment of comprehensions (e.g., by closing over variables in the outer +// query.) +type ComprehensionIndex = v1.ComprehensionIndex + +// ModuleTreeNode represents a node in the module tree. The module +// tree is keyed by the package path. +type ModuleTreeNode = v1.ModuleTreeNode + +// TreeNode represents a node in the rule tree. The rule tree is keyed by +// rule path. +type TreeNode = v1.TreeNode + +// NewRuleTree returns a new TreeNode that represents the root +// of the rule tree populated with the given rules. +func NewRuleTree(mtree *ModuleTreeNode) *TreeNode { + return v1.NewRuleTree(mtree) +} + +// Graph represents the graph of dependencies between rules. +type Graph = v1.Graph + +// NewGraph returns a new Graph based on modules. The list function must return +// the rules referred to directly by the ref. +func NewGraph(modules map[string]*Module, list func(Ref) []*Rule) *Graph { + return v1.NewGraph(modules, list) +} + +// GraphTraversal is a Traversal that understands the dependency graph +type GraphTraversal = v1.GraphTraversal + +// NewGraphTraversal returns a Traversal for the dependency graph +func NewGraphTraversal(graph *Graph) *GraphTraversal { + return v1.NewGraphTraversal(graph) +} + +// OutputVarsFromBody returns all variables which are the "output" for +// the given body. For safety checks this means that they would be +// made safe by the body. +func OutputVarsFromBody(c *Compiler, body Body, safe VarSet) VarSet { + return v1.OutputVarsFromBody(c, body, safe) +} + +// OutputVarsFromExpr returns all variables which are the "output" for +// the given expression. For safety checks this means that they would be +// made safe by the expr. +func OutputVarsFromExpr(c *Compiler, expr *Expr, safe VarSet) VarSet { + return v1.OutputVarsFromExpr(c, expr, safe) +} diff --git a/third_party/opa/ast/compile_test.go b/third_party/opa/ast/compile_test.go new file mode 100644 index 000000000000..356084334203 --- /dev/null +++ b/third_party/opa/ast/compile_test.go @@ -0,0 +1,97 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "strings" + "testing" +) + +func TestCompile_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + modules map[string]*Module + expErrs []string + }{ + { + note: "no module rego-version, no v1 violations", + modules: map[string]*Module{ + "test": { + Package: MustParsePackage(`package test`), + Imports: MustParseImports(`import data.foo + import data.bar`), + }, + }, + }, + { + note: "no module rego-version, v1 violations", // default is v0, no errors expected + modules: map[string]*Module{ + "test": { + Package: MustParsePackage(`package test`), + Imports: MustParseImports(`import data.foo + import data.bar as foo`), + }, + }, + }, + { + note: "v0 module, v1 violations", + modules: map[string]*Module{ + "test": MustParseModuleWithOpts(`package test + import data.foo + import data.bar as foo`, + ParserOptions{RegoVersion: RegoV0}), + }, + }, + { + note: "v1 module, v1 violations", + modules: map[string]*Module{ + "test": MustParseModuleWithOpts(`package test + import data.foo + import data.bar as foo`, + ParserOptions{RegoVersion: RegoV1}), + }, + expErrs: []string{ + "3:7: rego_compile_error: import must not shadow import data.foo", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + compiler := NewCompiler() + + compiler.Compile(tc.modules) + + if len(tc.expErrs) > 0 { + assertErrors(t, compiler.Errors, tc.expErrs) + } else if len(compiler.Errors) > 0 { + t.Fatalf("Unexpected errors: %v", compiler.Errors) + } + }) + } +} + +func assertErrors(t *testing.T, actual Errors, expected []string) { + t.Helper() + if len(expected) != len(actual) { + t.Fatalf("Expected %d errors, got %d:\n\n%s\n", len(expected), len(actual), actual.Error()) + } + incorrectErrs := false + for _, e := range expected { + found := false + for _, actual := range actual { + if strings.Contains(actual.Error(), e) { + found = true + break + } + } + if !found { + incorrectErrs = true + } + } + if incorrectErrs { + t.Fatalf("Expected errors:\n\n%s\n\nGot:\n\n%s\n", expected, actual.Error()) + } +} diff --git a/third_party/opa/ast/compilehelper.go b/third_party/opa/ast/compilehelper.go new file mode 100644 index 000000000000..37ede329ea4c --- /dev/null +++ b/third_party/opa/ast/compilehelper.go @@ -0,0 +1,48 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import v1 "github.com/open-policy-agent/opa/v1/ast" + +// CompileModules takes a set of Rego modules represented as strings and +// compiles them for evaluation. The keys of the map are used as filenames. +func CompileModules(modules map[string]string) (*Compiler, error) { + return CompileModulesWithOpt(modules, CompileOpts{ + ParserOptions: ParserOptions{ + RegoVersion: DefaultRegoVersion, + }, + }) +} + +// CompileOpts defines a set of options for the compiler. +type CompileOpts = v1.CompileOpts + +// CompileModulesWithOpt takes a set of Rego modules represented as strings and +// compiles them for evaluation. The keys of the map are used as filenames. +func CompileModulesWithOpt(modules map[string]string, opts CompileOpts) (*Compiler, error) { + if opts.ParserOptions.RegoVersion == RegoUndefined { + opts.ParserOptions.RegoVersion = DefaultRegoVersion + } + + return v1.CompileModulesWithOpt(modules, opts) +} + +// MustCompileModules compiles a set of Rego modules represented as strings. If +// the compilation process fails, this function panics. +func MustCompileModules(modules map[string]string) *Compiler { + return MustCompileModulesWithOpts(modules, CompileOpts{}) +} + +// MustCompileModulesWithOpts compiles a set of Rego modules represented as strings. If +// the compilation process fails, this function panics. +func MustCompileModulesWithOpts(modules map[string]string, opts CompileOpts) *Compiler { + + compiler, err := CompileModulesWithOpt(modules, opts) + if err != nil { + panic(err) + } + + return compiler +} diff --git a/third_party/opa/ast/compilehelper_test.go b/third_party/opa/ast/compilehelper_test.go new file mode 100644 index 000000000000..e6828be8a6f8 --- /dev/null +++ b/third_party/opa/ast/compilehelper_test.go @@ -0,0 +1,222 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "strings" + "testing" +) + +func TestCompileModules_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + modules map[string]string + expErrs []string + }{ + // default rego-version + { + note: "v0 module, no v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + p[x] { + x = "a" + }`, + }, + }, + { + note: "v0 module, v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import data.foo + import data.bar as foo + + p[x] { + x = "a" + }`, + }, + }, + + // cross-rego-version + { + note: "rego.v1 import, no v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import rego.v1 + + p contains x if { + x = "a" + }`, + }, + }, + { + note: "rego.v1 import, v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import rego.v1 + + import data.foo + import data.bar as foo + + p contains x if { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:5: rego_compile_error: import must not shadow import data.foo", + }, + }, + + // NOT default rego-version + { + note: "v1 module, no v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + p contains x if { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v1 module, v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import data.foo + import data.bar as foo + + p contains x if { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:5: rego_parse_error: var cannot be used for rule name", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + _, err := CompileModules(tc.modules) + + if len(tc.expErrs) > 0 { + for _, expErr := range tc.expErrs { + if err := err.Error(); !strings.Contains(err, expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + } +} + +func TestCompileModulesWithOpt_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + modules map[string]string + expErrs []string + }{ + // default rego-version + { + note: "v0 module, no v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + p[x] { + x = "a" + }`, + }, + }, + { + note: "v0 module, v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import data.foo + import data.bar as foo + + p[x] { + x = "a" + }`, + }, + }, + + // cross-rego-version + { + note: "rego.v1 import, no v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import rego.v1 + + p contains x if { + x = "a" + }`, + }, + }, + { + note: "rego.v1 import, v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import rego.v1 + + import data.foo + import data.bar as foo + + p contains x if { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:5: rego_compile_error: import must not shadow import data.foo", + }, + }, + + // NOT default rego-version + { + note: "v1 module, no v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + p contains x if { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v1 module, v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import data.foo + import data.bar as foo + + p contains x if { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:5: rego_parse_error: var cannot be used for rule name", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + _, err := CompileModulesWithOpt(tc.modules, CompileOpts{EnablePrintStatements: true}) + + if len(tc.expErrs) > 0 { + for _, expErr := range tc.expErrs { + if err := err.Error(); !strings.Contains(err, expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + } +} diff --git a/third_party/opa/ast/conflicts.go b/third_party/opa/ast/conflicts.go new file mode 100644 index 000000000000..10edce382c30 --- /dev/null +++ b/third_party/opa/ast/conflicts.go @@ -0,0 +1,15 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// CheckPathConflicts returns a set of errors indicating paths that +// are in conflict with the result of the provided callable. +func CheckPathConflicts(c *Compiler, exists func([]string) (bool, error)) Errors { + return v1.CheckPathConflicts(c, exists) +} diff --git a/third_party/opa/ast/doc.go b/third_party/opa/ast/doc.go new file mode 100644 index 000000000000..ba974e5ba600 --- /dev/null +++ b/third_party/opa/ast/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package ast diff --git a/third_party/opa/ast/env.go b/third_party/opa/ast/env.go new file mode 100644 index 000000000000..ef0ccf89ce8e --- /dev/null +++ b/third_party/opa/ast/env.go @@ -0,0 +1,12 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// TypeEnv contains type info for static analysis such as type checking. +type TypeEnv = v1.TypeEnv diff --git a/third_party/opa/ast/errors.go b/third_party/opa/ast/errors.go new file mode 100644 index 000000000000..722cfc0fb7c8 --- /dev/null +++ b/third_party/opa/ast/errors.go @@ -0,0 +1,46 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// Errors represents a series of errors encountered during parsing, compiling, +// etc. +type Errors = v1.Errors + +const ( + // ParseErr indicates an unclassified parse error occurred. + ParseErr = v1.ParseErr + + // CompileErr indicates an unclassified compile error occurred. + CompileErr = v1.CompileErr + + // TypeErr indicates a type error was caught. + TypeErr = v1.TypeErr + + // UnsafeVarErr indicates an unsafe variable was found during compilation. + UnsafeVarErr = v1.UnsafeVarErr + + // RecursionErr indicates recursion was found during compilation. + RecursionErr = v1.RecursionErr +) + +// IsError returns true if err is an AST error with code. +func IsError(code string, err error) bool { + return v1.IsError(code, err) +} + +// ErrorDetails defines the interface for detailed error messages. +type ErrorDetails = v1.ErrorDetails + +// Error represents a single error caught during parsing, compiling, etc. +type Error = v1.Error + +// NewError returns a new Error object. +func NewError(code string, loc *Location, f string, a ...any) *Error { + return v1.NewError(code, loc, f, a...) +} diff --git a/third_party/opa/ast/index.go b/third_party/opa/ast/index.go new file mode 100644 index 000000000000..7e80bb7716c4 --- /dev/null +++ b/third_party/opa/ast/index.go @@ -0,0 +1,20 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// RuleIndex defines the interface for rule indices. +type RuleIndex v1.RuleIndex + +// IndexResult contains the result of an index lookup. +type IndexResult = v1.IndexResult + +// NewIndexResult returns a new IndexResult object. +func NewIndexResult(kind RuleKind) *IndexResult { + return v1.NewIndexResult(kind) +} diff --git a/third_party/opa/ast/interning.go b/third_party/opa/ast/interning.go new file mode 100644 index 000000000000..29231006aa5b --- /dev/null +++ b/third_party/opa/ast/interning.go @@ -0,0 +1,24 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +func InternedBooleanTerm(b bool) *Term { + return v1.InternedTerm(b) +} + +// InternedIntNumberTerm returns a term with the given integer value. The term is +// cached between -1 to 512, and for values outside of that range, this function +// is equivalent to ast.IntNumberTerm. +func InternedIntNumberTerm(i int) *Term { + return v1.InternedTerm(i) +} + +func HasInternedIntNumberTerm(i int) bool { + return v1.HasInternedIntNumberTerm(i) +} diff --git a/third_party/opa/ast/json/doc.go b/third_party/opa/ast/json/doc.go new file mode 100644 index 000000000000..26aee9b9940a --- /dev/null +++ b/third_party/opa/ast/json/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package json diff --git a/third_party/opa/ast/json/json.go b/third_party/opa/ast/json/json.go new file mode 100644 index 000000000000..8a3a36bb9b18 --- /dev/null +++ b/third_party/opa/ast/json/json.go @@ -0,0 +1,15 @@ +package json + +import v1 "github.com/open-policy-agent/opa/v1/ast/json" + +// Options defines the options for JSON operations, +// currently only marshaling can be configured +type Options = v1.Options + +// MarshalOptions defines the options for JSON marshaling, +// currently only toggling the marshaling of location information is supported +type MarshalOptions = v1.MarshalOptions + +// NodeToggle is a generic struct to allow the toggling of +// settings for different ast node types +type NodeToggle = v1.NodeToggle diff --git a/third_party/opa/ast/location/doc.go b/third_party/opa/ast/location/doc.go new file mode 100644 index 000000000000..b559963a5245 --- /dev/null +++ b/third_party/opa/ast/location/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package location diff --git a/third_party/opa/ast/location/location.go b/third_party/opa/ast/location/location.go new file mode 100644 index 000000000000..f746bb93b31c --- /dev/null +++ b/third_party/opa/ast/location/location.go @@ -0,0 +1,14 @@ +// Package location defines locations in Rego source code. +package location + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// Location records a position in source code +type Location = v1.Location + +// NewLocation returns a new Location object. +func NewLocation(text []byte, file string, row int, col int) *Location { + return v1.NewLocation(text, file, row, col) +} diff --git a/third_party/opa/ast/map.go b/third_party/opa/ast/map.go new file mode 100644 index 000000000000..070ad3e5dedb --- /dev/null +++ b/third_party/opa/ast/map.go @@ -0,0 +1,18 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// ValueMap represents a key/value map between AST term values. Any type of term +// can be used as a key in the map. +type ValueMap = v1.ValueMap + +// NewValueMap returns a new ValueMap. +func NewValueMap() *ValueMap { + return v1.NewValueMap() +} diff --git a/third_party/opa/ast/parser.go b/third_party/opa/ast/parser.go new file mode 100644 index 000000000000..45cd4da06efc --- /dev/null +++ b/third_party/opa/ast/parser.go @@ -0,0 +1,49 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +var RegoV1CompatibleRef = v1.RegoV1CompatibleRef + +// RegoVersion defines the Rego syntax requirements for a module. +type RegoVersion = v1.RegoVersion + +const DefaultRegoVersion = RegoV0 + +const ( + RegoUndefined = v1.RegoUndefined + // RegoV0 is the default, original Rego syntax. + RegoV0 = v1.RegoV0 + // RegoV0CompatV1 requires modules to comply with both the RegoV0 and RegoV1 syntax (as when 'rego.v1' is imported in a module). + // Shortly, RegoV1 compatibility is required, but 'rego.v1' or 'future.keywords' must also be imported. + RegoV0CompatV1 = v1.RegoV0CompatV1 + // RegoV1 is the Rego syntax enforced by OPA 1.0; e.g.: + // future.keywords part of default keyword set, and don't require imports; + // 'if' and 'contains' required in rule heads; + // (some) strict checks on by default. + RegoV1 = v1.RegoV1 +) + +func RegoVersionFromInt(i int) RegoVersion { + return v1.RegoVersionFromInt(i) +} + +// Parser is used to parse Rego statements. +type Parser = v1.Parser + +// ParserOptions defines the options for parsing Rego statements. +type ParserOptions = v1.ParserOptions + +// NewParser creates and initializes a Parser. +func NewParser() *Parser { + return v1.NewParser().WithRegoVersion(DefaultRegoVersion) +} + +func IsFutureKeyword(s string) bool { + return v1.IsFutureKeywordForRegoVersion(s, RegoV0) +} diff --git a/third_party/opa/ast/parser_ext.go b/third_party/opa/ast/parser_ext.go new file mode 100644 index 000000000000..2d5961693214 --- /dev/null +++ b/third_party/opa/ast/parser_ext.go @@ -0,0 +1,311 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "errors" + "fmt" + + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// MustParseBody returns a parsed body. +// If an error occurs during parsing, panic. +func MustParseBody(input string) Body { + return MustParseBodyWithOpts(input, ParserOptions{}) +} + +// MustParseBodyWithOpts returns a parsed body. +// If an error occurs during parsing, panic. +func MustParseBodyWithOpts(input string, opts ParserOptions) Body { + return v1.MustParseBodyWithOpts(input, setDefaultRegoVersion(opts)) +} + +// MustParseExpr returns a parsed expression. +// If an error occurs during parsing, panic. +func MustParseExpr(input string) *Expr { + parsed, err := ParseExpr(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseImports returns a slice of imports. +// If an error occurs during parsing, panic. +func MustParseImports(input string) []*Import { + parsed, err := ParseImports(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseModule returns a parsed module. +// If an error occurs during parsing, panic. +func MustParseModule(input string) *Module { + return MustParseModuleWithOpts(input, ParserOptions{}) +} + +// MustParseModuleWithOpts returns a parsed module. +// If an error occurs during parsing, panic. +func MustParseModuleWithOpts(input string, opts ParserOptions) *Module { + return v1.MustParseModuleWithOpts(input, setDefaultRegoVersion(opts)) +} + +// MustParsePackage returns a Package. +// If an error occurs during parsing, panic. +func MustParsePackage(input string) *Package { + parsed, err := ParsePackage(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseStatements returns a slice of parsed statements. +// If an error occurs during parsing, panic. +func MustParseStatements(input string) []Statement { + parsed, _, err := ParseStatements("", input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseStatement returns exactly one statement. +// If an error occurs during parsing, panic. +func MustParseStatement(input string) Statement { + parsed, err := ParseStatement(input) + if err != nil { + panic(err) + } + return parsed +} + +func MustParseStatementWithOpts(input string, popts ParserOptions) Statement { + return v1.MustParseStatementWithOpts(input, setDefaultRegoVersion(popts)) +} + +// MustParseRef returns a parsed reference. +// If an error occurs during parsing, panic. +func MustParseRef(input string) Ref { + parsed, err := ParseRef(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseRule returns a parsed rule. +// If an error occurs during parsing, panic. +func MustParseRule(input string) *Rule { + parsed, err := ParseRule(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseRuleWithOpts returns a parsed rule. +// If an error occurs during parsing, panic. +func MustParseRuleWithOpts(input string, opts ParserOptions) *Rule { + return v1.MustParseRuleWithOpts(input, setDefaultRegoVersion(opts)) +} + +// MustParseTerm returns a parsed term. +// If an error occurs during parsing, panic. +func MustParseTerm(input string) *Term { + parsed, err := ParseTerm(input) + if err != nil { + panic(err) + } + return parsed +} + +// ParseRuleFromBody returns a rule if the body can be interpreted as a rule +// definition. Otherwise, an error is returned. +func ParseRuleFromBody(module *Module, body Body) (*Rule, error) { + return v1.ParseRuleFromBody(module, body) +} + +// ParseRuleFromExpr returns a rule if the expression can be interpreted as a +// rule definition. +func ParseRuleFromExpr(module *Module, expr *Expr) (*Rule, error) { + return v1.ParseRuleFromExpr(module, expr) +} + +// ParseCompleteDocRuleFromAssignmentExpr returns a rule if the expression can +// be interpreted as a complete document definition declared with the assignment +// operator. +func ParseCompleteDocRuleFromAssignmentExpr(module *Module, lhs, rhs *Term) (*Rule, error) { + return v1.ParseCompleteDocRuleFromAssignmentExpr(module, lhs, rhs) +} + +// ParseCompleteDocRuleFromEqExpr returns a rule if the expression can be +// interpreted as a complete document definition. +func ParseCompleteDocRuleFromEqExpr(module *Module, lhs, rhs *Term) (*Rule, error) { + return v1.ParseCompleteDocRuleFromEqExpr(module, lhs, rhs) +} + +func ParseCompleteDocRuleWithDotsFromTerm(module *Module, term *Term) (*Rule, error) { + return v1.ParseCompleteDocRuleWithDotsFromTerm(module, term) +} + +// ParsePartialObjectDocRuleFromEqExpr returns a rule if the expression can be +// interpreted as a partial object document definition. +func ParsePartialObjectDocRuleFromEqExpr(module *Module, lhs, rhs *Term) (*Rule, error) { + return v1.ParsePartialObjectDocRuleFromEqExpr(module, lhs, rhs) +} + +// ParsePartialSetDocRuleFromTerm returns a rule if the term can be interpreted +// as a partial set document definition. +func ParsePartialSetDocRuleFromTerm(module *Module, term *Term) (*Rule, error) { + return v1.ParsePartialSetDocRuleFromTerm(module, term) +} + +// ParseRuleFromCallEqExpr returns a rule if the term can be interpreted as a +// function definition (e.g., f(x) = y => f(x) = y { true }). +func ParseRuleFromCallEqExpr(module *Module, lhs, rhs *Term) (*Rule, error) { + return v1.ParseRuleFromCallEqExpr(module, lhs, rhs) +} + +// ParseRuleFromCallExpr returns a rule if the terms can be interpreted as a +// function returning true or some value (e.g., f(x) => f(x) = true { true }). +func ParseRuleFromCallExpr(module *Module, terms []*Term) (*Rule, error) { + return v1.ParseRuleFromCallExpr(module, terms) +} + +// ParseImports returns a slice of Import objects. +func ParseImports(input string) ([]*Import, error) { + return v1.ParseImports(input) +} + +// ParseModule returns a parsed Module object. +// For details on Module objects and their fields, see policy.go. +// Empty input will return nil, nil. +func ParseModule(filename, input string) (*Module, error) { + return ParseModuleWithOpts(filename, input, ParserOptions{}) +} + +// ParseModuleWithOpts returns a parsed Module object, and has an additional input ParserOptions +// For details on Module objects and their fields, see policy.go. +// Empty input will return nil, nil. +func ParseModuleWithOpts(filename, input string, popts ParserOptions) (*Module, error) { + return v1.ParseModuleWithOpts(filename, input, setDefaultRegoVersion(popts)) +} + +// ParseBody returns exactly one body. +// If multiple bodies are parsed, an error is returned. +func ParseBody(input string) (Body, error) { + return ParseBodyWithOpts(input, ParserOptions{SkipRules: true}) +} + +// ParseBodyWithOpts returns exactly one body. It does _not_ set SkipRules: true on its own, +// but respects whatever ParserOptions it's been given. +func ParseBodyWithOpts(input string, popts ParserOptions) (Body, error) { + return v1.ParseBodyWithOpts(input, setDefaultRegoVersion(popts)) +} + +// ParseExpr returns exactly one expression. +// If multiple expressions are parsed, an error is returned. +func ParseExpr(input string) (*Expr, error) { + body, err := ParseBody(input) + if err != nil { + return nil, fmt.Errorf("failed to parse expression: %w", err) + } + if len(body) != 1 { + return nil, fmt.Errorf("expected exactly one expression but got: %v", body) + } + return body[0], nil +} + +// ParsePackage returns exactly one Package. +// If multiple statements are parsed, an error is returned. +func ParsePackage(input string) (*Package, error) { + return v1.ParsePackage(input) +} + +// ParseTerm returns exactly one term. +// If multiple terms are parsed, an error is returned. +func ParseTerm(input string) (*Term, error) { + body, err := ParseBody(input) + if err != nil { + return nil, fmt.Errorf("failed to parse term: %w", err) + } + if len(body) != 1 { + return nil, fmt.Errorf("expected exactly one term but got: %v", body) + } + term, ok := body[0].Terms.(*Term) + if !ok { + return nil, fmt.Errorf("expected term but got %v", body[0].Terms) + } + return term, nil +} + +// ParseRef returns exactly one reference. +func ParseRef(input string) (Ref, error) { + term, err := ParseTerm(input) + if err != nil { + return nil, fmt.Errorf("failed to parse ref: %w", err) + } + ref, ok := term.Value.(Ref) + if !ok { + return nil, fmt.Errorf("expected ref but got %v", term) + } + return ref, nil +} + +// ParseRuleWithOpts returns exactly one rule. +// If multiple rules are parsed, an error is returned. +func ParseRuleWithOpts(input string, opts ParserOptions) (*Rule, error) { + return v1.ParseRuleWithOpts(input, setDefaultRegoVersion(opts)) +} + +// ParseRule returns exactly one rule. +// If multiple rules are parsed, an error is returned. +func ParseRule(input string) (*Rule, error) { + return ParseRuleWithOpts(input, ParserOptions{}) +} + +// ParseStatement returns exactly one statement. +// A statement might be a term, expression, rule, etc. Regardless, +// this function expects *exactly* one statement. If multiple +// statements are parsed, an error is returned. +func ParseStatement(input string) (Statement, error) { + stmts, _, err := ParseStatements("", input) + if err != nil { + return nil, err + } + if len(stmts) != 1 { + return nil, errors.New("expected exactly one statement") + } + return stmts[0], nil +} + +func ParseStatementWithOpts(input string, popts ParserOptions) (Statement, error) { + return v1.ParseStatementWithOpts(input, setDefaultRegoVersion(popts)) +} + +// ParseStatements is deprecated. Use ParseStatementWithOpts instead. +func ParseStatements(filename, input string) ([]Statement, []*Comment, error) { + return ParseStatementsWithOpts(filename, input, ParserOptions{}) +} + +// ParseStatementsWithOpts returns a slice of parsed statements. This is the +// default return value from the parser. +func ParseStatementsWithOpts(filename, input string, popts ParserOptions) ([]Statement, []*Comment, error) { + return v1.ParseStatementsWithOpts(filename, input, setDefaultRegoVersion(popts)) +} + +// ParserErrorDetail holds additional details for parser errors. +type ParserErrorDetail = v1.ParserErrorDetail + +func setDefaultRegoVersion(opts ParserOptions) ParserOptions { + if opts.RegoVersion == RegoUndefined { + opts.RegoVersion = DefaultRegoVersion + } + return opts +} diff --git a/third_party/opa/ast/parser_ext_test.go b/third_party/opa/ast/parser_ext_test.go new file mode 100644 index 000000000000..02ca80eefc28 --- /dev/null +++ b/third_party/opa/ast/parser_ext_test.go @@ -0,0 +1,127 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast_test + +import ( + "strings" + "testing" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/format" +) + +func TestParseModule_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + mod string + expRules []string + expErrs []string + }{ + { + note: "v0", // default rego-version + mod: `package test +p[x] { + x = "a" +}`, + expRules: []string{"p"}, + }, + { + note: "import rego.v1", + mod: `package test +import rego.v1 + +p contains x if { + x = "a" +}`, + expRules: []string{"p"}, + }, + { + note: "v1", // NOT default rego-version + mod: `package test +p contains x if { + x = "a" +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: var cannot be used for rule name", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + m, err := ast.ParseModule("test.rego", tc.mod) + + if len(tc.expErrs) > 0 { + for i, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error %d to contain %q, got %q", i, expErr, err.Error()) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if len(m.Rules) != len(tc.expRules) { + t.Fatalf("Expected %d rules, got %d", len(tc.expRules), len(m.Rules)) + } + for i, r := range m.Rules { + if r.Head.Name.String() != tc.expRules[i] { + t.Fatalf("Expected rule %q, got %q", tc.expRules[i], r.Head.Name.String()) + } + } + } + }) + } +} + +func TestParseBody_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + body string + expStmts int + assertSame bool + }{ + { + note: "v0", // default rego-version + body: `x := ["a", "b", "c"][i] +`, + expStmts: 1, + assertSame: true, + }, + { + note: "v1", // NOT default rego-version + body: `some x, i in ["a", "b", "c"] +`, + expStmts: 3, + assertSame: false, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + body, err := ast.ParseBody(tc.body) + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if len(body) != tc.expStmts { + t.Fatalf("Expected %d statements, got %d:%q\n\n", tc.expStmts, len(body), body) + } + + if tc.assertSame { + formatted, err := format.AstWithOpts(body, format.Opts{RegoVersion: ast.RegoV1}) // every body is v1-compatible + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if strings.Compare(string(formatted), tc.body) != 0 { + t.Fatalf("Expected body to be %q, got %q", tc.body, string(formatted)) + } + } + }) + } +} diff --git a/third_party/opa/ast/parser_test.go b/third_party/opa/ast/parser_test.go new file mode 100644 index 000000000000..24487e2d06bb --- /dev/null +++ b/third_party/opa/ast/parser_test.go @@ -0,0 +1,52 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "bytes" + "testing" +) + +func TestParser_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + input string + expStmtCount int + }{ + { + note: "v0", + input: `package test +p[x] { + c = ["a", "b", "c"][i] +}`, + expStmtCount: 2, // package, p + }, + { + note: "v1", + input: `package test +p contains x if { + c = ["a", "b", "c"][i] +}`, + // v1 Keywords are not recognized, and interpreted as individual statements + expStmtCount: 5, // package, p, contains, x, if + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + parser := NewParser(). + WithFilename("test.rego"). + WithReader(bytes.NewBufferString(tc.input)) + stmts, _, err := parser.Parse() + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(stmts) != tc.expStmtCount { + t.Fatalf("Expected %d statements but got %d:\n\n%v", tc.expStmtCount, len(stmts), stmts) + } + }) + } +} diff --git a/third_party/opa/ast/policy.go b/third_party/opa/ast/policy.go new file mode 100644 index 000000000000..5055e8f23f29 --- /dev/null +++ b/third_party/opa/ast/policy.go @@ -0,0 +1,235 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + astJSON "github.com/open-policy-agent/opa/ast/json" + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// DefaultRootDocument is the default root document. +// +// All package directives inside source files are implicitly prefixed with the +// DefaultRootDocument value. +var DefaultRootDocument = v1.DefaultRootDocument + +// InputRootDocument names the document containing query arguments. +var InputRootDocument = v1.InputRootDocument + +// SchemaRootDocument names the document containing external data schemas. +var SchemaRootDocument = v1.SchemaRootDocument + +// FunctionArgRootDocument names the document containing function arguments. +// It's only for internal usage, for referencing function arguments between +// the index and topdown. +var FunctionArgRootDocument = v1.FunctionArgRootDocument + +// FutureRootDocument names the document containing new, to-become-default, +// features. +var FutureRootDocument = v1.FutureRootDocument + +// RegoRootDocument names the document containing new, to-become-default, +// features in a future versioned release. +var RegoRootDocument = v1.RegoRootDocument + +// RootDocumentNames contains the names of top-level documents that can be +// referred to in modules and queries. +// +// Note, the schema document is not currently implemented in the evaluator so it +// is not registered as a root document name (yet). +var RootDocumentNames = v1.RootDocumentNames + +// DefaultRootRef is a reference to the root of the default document. +// +// All refs to data in the policy engine's storage layer are prefixed with this ref. +var DefaultRootRef = v1.DefaultRootRef + +// InputRootRef is a reference to the root of the input document. +// +// All refs to query arguments are prefixed with this ref. +var InputRootRef = v1.InputRootRef + +// SchemaRootRef is a reference to the root of the schema document. +// +// All refs to schema documents are prefixed with this ref. Note, the schema +// document is not currently implemented in the evaluator so it is not +// registered as a root document ref (yet). +var SchemaRootRef = v1.SchemaRootRef + +// RootDocumentRefs contains the prefixes of top-level documents that all +// non-local references start with. +var RootDocumentRefs = v1.RootDocumentRefs + +// SystemDocumentKey is the name of the top-level key that identifies the system +// document. +const SystemDocumentKey = v1.SystemDocumentKey + +// ReservedVars is the set of names that refer to implicitly ground vars. +var ReservedVars = v1.ReservedVars + +// Wildcard represents the wildcard variable as defined in the language. +var Wildcard = v1.Wildcard + +// WildcardPrefix is the special character that all wildcard variables are +// prefixed with when the statement they are contained in is parsed. +const WildcardPrefix = v1.WildcardPrefix + +// Keywords contains strings that map to language keywords. +var Keywords = v1.Keywords + +var KeywordsV0 = v1.KeywordsV0 + +var KeywordsV1 = v1.KeywordsV1 + +func KeywordsForRegoVersion(v RegoVersion) []string { + return v1.KeywordsForRegoVersion(v) +} + +// IsKeyword returns true if s is a language keyword. +func IsKeyword(s string) bool { + return v1.IsKeyword(s) +} + +func IsInKeywords(s string, keywords []string) bool { + return v1.IsInKeywords(s, keywords) +} + +// IsKeywordInRegoVersion returns true if s is a language keyword. +func IsKeywordInRegoVersion(s string, regoVersion RegoVersion) bool { + return v1.IsKeywordInRegoVersion(s, regoVersion) +} + +type ( + // Node represents a node in an AST. Nodes may be statements in a policy module + // or elements of an ad-hoc query, expression, etc. + Node = v1.Node + + // Statement represents a single statement in a policy module. + Statement = v1.Statement +) + +type ( + + // Module represents a collection of policies (defined by rules) + // within a namespace (defined by the package) and optional + // dependencies on external documents (defined by imports). + Module = v1.Module + + // Comment contains the raw text from the comment in the definition. + Comment = v1.Comment + + // Package represents the namespace of the documents produced + // by rules inside the module. + Package = v1.Package + + // Import represents a dependency on a document outside of the policy + // namespace. Imports are optional. + Import = v1.Import + + // Rule represents a rule as defined in the language. Rules define the + // content of documents that represent policy decisions. + Rule = v1.Rule + + // Head represents the head of a rule. + Head = v1.Head + + // Args represents zero or more arguments to a rule. + Args = v1.Args + + // Body represents one or more expressions contained inside a rule or user + // function. + Body = v1.Body + + // Expr represents a single expression contained inside the body of a rule. + Expr = v1.Expr + + // SomeDecl represents a variable declaration statement. The symbols are variables. + SomeDecl = v1.SomeDecl + + Every = v1.Every + + // With represents a modifier on an expression. + With = v1.With +) + +// NewComment returns a new Comment object. +func NewComment(text []byte) *Comment { + return v1.NewComment(text) +} + +// IsValidImportPath returns an error indicating if the import path is invalid. +// If the import path is valid, err is nil. +func IsValidImportPath(v Value) (err error) { + return v1.IsValidImportPath(v) +} + +// NewHead returns a new Head object. If args are provided, the first will be +// used for the key and the second will be used for the value. +func NewHead(name Var, args ...*Term) *Head { + return v1.NewHead(name, args...) +} + +// VarHead creates a head object, initializes its Name, Location, and Options, +// and returns the new head. +func VarHead(name Var, location *Location, jsonOpts *astJSON.Options) *Head { + return v1.VarHead(name, location, jsonOpts) +} + +// RefHead returns a new Head object with the passed Ref. If args are provided, +// the first will be used for the value. +func RefHead(ref Ref, args ...*Term) *Head { + return v1.RefHead(ref, args...) +} + +// DocKind represents the collection of document types that can be produced by rules. +type DocKind = v1.DocKind + +const ( + // CompleteDoc represents a document that is completely defined by the rule. + CompleteDoc = v1.CompleteDoc + + // PartialSetDoc represents a set document that is partially defined by the rule. + PartialSetDoc = v1.PartialSetDoc + + // PartialObjectDoc represents an object document that is partially defined by the rule. + PartialObjectDoc = v1.PartialObjectDoc +) + +type RuleKind = v1.RuleKind + +const ( + SingleValue = v1.SingleValue + MultiValue = v1.MultiValue +) + +// NewBody returns a new Body containing the given expressions. The indices of +// the immediate expressions will be reset. +func NewBody(exprs ...*Expr) Body { + return v1.NewBody(exprs...) +} + +// NewExpr returns a new Expr object. +func NewExpr(terms any) *Expr { + return v1.NewExpr(terms) +} + +// NewBuiltinExpr creates a new Expr object with the supplied terms. +// The builtin operator must be the first term. +func NewBuiltinExpr(terms ...*Term) *Expr { + return v1.NewBuiltinExpr(terms...) +} + +// Copy returns a deep copy of the AST node x. If x is not an AST node, x is returned unmodified. +func Copy(x any) any { + return v1.Copy(x) +} + +// RuleSet represents a collection of rules that produce a virtual document. +type RuleSet = v1.RuleSet + +// NewRuleSet returns a new RuleSet containing the given rules. +func NewRuleSet(rules ...*Rule) RuleSet { + return v1.NewRuleSet(rules...) +} diff --git a/third_party/opa/ast/policy_test.go b/third_party/opa/ast/policy_test.go new file mode 100644 index 000000000000..3e35ba7695ef --- /dev/null +++ b/third_party/opa/ast/policy_test.go @@ -0,0 +1,85 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import "testing" + +func TestRuleString_DefaultRegoVersion(t *testing.T) { + // ast.Rule.String() will respect the rego-version of the ast.Module it is part of. + + tests := []struct { + note string + module string + regoVersion RegoVersion + exp string + }{ + { + note: "v0", + regoVersion: RegoV0, + module: `package a.b.c + +p[x] { x = "a" }`, + exp: `p[x] { x = "a" }`, + }, + { + note: "v1", + regoVersion: RegoV1, + module: `package a.b.c + +p contains x if { x = "a" }`, + exp: `p contains x if { x = "a" }`, + }, + { + note: "default rego-version", + module: `package a.b.c + +p[x] { x = "a" }`, + exp: `p[x] { x = "a" }`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var mod *Module + + if tc.regoVersion == RegoUndefined { + mod = MustParseModule(tc.module) + } else { + mod = MustParseModuleWithOpts(tc.module, ParserOptions{RegoVersion: tc.regoVersion}) + } + + rule := mod.Rules[0] + act := rule.String() + + if act != tc.exp { + t.Fatalf("Expected:\n\n%s\n\nbut got:\n\n%s", tc.exp, act) + } + }) + } +} + +func TestModuleString(t *testing.T) { + + // v0 module + input := `package a.b.c + +import data.foo.bar +import input.xyz + +p = true { not bar } +q = true { xyz.abc = 2 } +wildcard = true { bar[_] = 1 }` + + mod := MustParseModule(input) + + roundtrip, err := ParseModule("", mod.String()) + if err != nil { + t.Fatalf("Unexpected error while parsing roundtripped module: %v", err) + } + + if !roundtrip.Equal(mod) { + t.Fatalf("Expected roundtripped to equal original but:\n\nExpected:\n\n%v\n\nDoes not equal result:\n\n%v", mod, roundtrip) + } +} diff --git a/third_party/opa/ast/pretty.go b/third_party/opa/ast/pretty.go new file mode 100644 index 000000000000..84e42f9aec56 --- /dev/null +++ b/third_party/opa/ast/pretty.go @@ -0,0 +1,18 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "io" + + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// Pretty writes a pretty representation of the AST rooted at x to w. +// +// This is function is intended for debug purposes when inspecting ASTs. +func Pretty(w io.Writer, x any) { + v1.Pretty(w, x) +} diff --git a/third_party/opa/ast/schema.go b/third_party/opa/ast/schema.go new file mode 100644 index 000000000000..979958a3c00a --- /dev/null +++ b/third_party/opa/ast/schema.go @@ -0,0 +1,17 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// SchemaSet holds a map from a path to a schema. +type SchemaSet = v1.SchemaSet + +// NewSchemaSet returns an empty SchemaSet. +func NewSchemaSet() *SchemaSet { + return v1.NewSchemaSet() +} diff --git a/third_party/opa/ast/strings.go b/third_party/opa/ast/strings.go new file mode 100644 index 000000000000..c2c81de8b779 --- /dev/null +++ b/third_party/opa/ast/strings.go @@ -0,0 +1,14 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// TypeName returns a human readable name for the AST element type. +func TypeName(x any) string { + return v1.TypeName(x) +} diff --git a/third_party/opa/ast/term.go b/third_party/opa/ast/term.go new file mode 100644 index 000000000000..202355070f1b --- /dev/null +++ b/third_party/opa/ast/term.go @@ -0,0 +1,306 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "encoding/json" + "io" + + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// Location records a position in source code. +type Location = v1.Location + +// NewLocation returns a new Location object. +func NewLocation(text []byte, file string, row int, col int) *Location { + return v1.NewLocation(text, file, row, col) +} + +// Value declares the common interface for all Term values. Every kind of Term value +// in the language is represented as a type that implements this interface: +// +// - Null, Boolean, Number, String +// - Object, Array, Set +// - Variables, References +// - Array, Set, and Object Comprehensions +// - Calls +type Value = v1.Value + +// InterfaceToValue converts a native Go value x to a Value. +func InterfaceToValue(x any) (Value, error) { + return v1.InterfaceToValue(x) +} + +// ValueFromReader returns an AST value from a JSON serialized value in the reader. +func ValueFromReader(r io.Reader) (Value, error) { + return v1.ValueFromReader(r) +} + +// As converts v into a Go native type referred to by x. +func As(v Value, x any) error { + return v1.As(v, x) +} + +// Resolver defines the interface for resolving references to native Go values. +type Resolver = v1.Resolver + +// ValueResolver defines the interface for resolving references to AST values. +type ValueResolver = v1.ValueResolver + +// UnknownValueErr indicates a ValueResolver was unable to resolve a reference +// because the reference refers to an unknown value. +type UnknownValueErr = v1.UnknownValueErr + +// IsUnknownValueErr returns true if the err is an UnknownValueErr. +func IsUnknownValueErr(err error) bool { + return v1.IsUnknownValueErr(err) +} + +// ValueToInterface returns the Go representation of an AST value. The AST +// value should not contain any values that require evaluation (e.g., vars, +// comprehensions, etc.) +func ValueToInterface(v Value, resolver Resolver) (any, error) { + return v1.ValueToInterface(v, resolver) +} + +// JSON returns the JSON representation of v. The value must not contain any +// refs or terms that require evaluation (e.g., vars, comprehensions, etc.) +func JSON(v Value) (any, error) { + return v1.JSON(v) +} + +// JSONOpt defines parameters for AST to JSON conversion. +type JSONOpt = v1.JSONOpt + +// JSONWithOpt returns the JSON representation of v. The value must not contain any +// refs or terms that require evaluation (e.g., vars, comprehensions, etc.) +func JSONWithOpt(v Value, opt JSONOpt) (any, error) { + return v1.JSONWithOpt(v, opt) +} + +// MustJSON returns the JSON representation of v. The value must not contain any +// refs or terms that require evaluation (e.g., vars, comprehensions, etc.) If +// the conversion fails, this function will panic. This function is mostly for +// test purposes. +func MustJSON(v Value) any { + return v1.MustJSON(v) +} + +// MustInterfaceToValue converts a native Go value x to a Value. If the +// conversion fails, this function will panic. This function is mostly for test +// purposes. +func MustInterfaceToValue(x any) Value { + return v1.MustInterfaceToValue(x) +} + +// Term is an argument to a function. +type Term = v1.Term + +// NewTerm returns a new Term object. +func NewTerm(v Value) *Term { + return v1.NewTerm(v) +} + +// IsConstant returns true if the AST value is constant. +func IsConstant(v Value) bool { + return v1.IsConstant(v) +} + +// IsComprehension returns true if the supplied value is a comprehension. +func IsComprehension(x Value) bool { + return v1.IsComprehension(x) +} + +// ContainsRefs returns true if the Value v contains refs. +func ContainsRefs(v any) bool { + return v1.ContainsRefs(v) +} + +// ContainsComprehensions returns true if the Value v contains comprehensions. +func ContainsComprehensions(v any) bool { + return v1.ContainsComprehensions(v) +} + +// ContainsClosures returns true if the Value v contains closures. +func ContainsClosures(v any) bool { + return v1.ContainsClosures(v) +} + +// IsScalar returns true if the AST value is a scalar. +func IsScalar(v Value) bool { + return v1.IsScalar(v) +} + +// Null represents the null value defined by JSON. +type Null = v1.Null + +// NullTerm creates a new Term with a Null value. +func NullTerm() *Term { + return v1.NullTerm() +} + +// Boolean represents a boolean value defined by JSON. +type Boolean = v1.Boolean + +// BooleanTerm creates a new Term with a Boolean value. +func BooleanTerm(b bool) *Term { + return v1.BooleanTerm(b) +} + +// Number represents a numeric value as defined by JSON. +type Number = v1.Number + +// NumberTerm creates a new Term with a Number value. +func NumberTerm(n json.Number) *Term { + return v1.NumberTerm(n) +} + +// IntNumberTerm creates a new Term with an integer Number value. +func IntNumberTerm(i int) *Term { + return v1.IntNumberTerm(i) +} + +// UIntNumberTerm creates a new Term with an unsigned integer Number value. +func UIntNumberTerm(u uint64) *Term { + return v1.UIntNumberTerm(u) +} + +// FloatNumberTerm creates a new Term with a floating point Number value. +func FloatNumberTerm(f float64) *Term { + return v1.FloatNumberTerm(f) +} + +// String represents a string value as defined by JSON. +type String = v1.String + +// StringTerm creates a new Term with a String value. +func StringTerm(s string) *Term { + return v1.StringTerm(s) +} + +// Var represents a variable as defined by the language. +type Var = v1.Var + +// VarTerm creates a new Term with a Variable value. +func VarTerm(v string) *Term { + return v1.VarTerm(v) +} + +// Ref represents a reference as defined by the language. +type Ref = v1.Ref + +// EmptyRef returns a new, empty reference. +func EmptyRef() Ref { + return v1.EmptyRef() +} + +// PtrRef returns a new reference against the head for the pointer +// s. Path components in the pointer are unescaped. +func PtrRef(head *Term, s string) (Ref, error) { + return v1.PtrRef(head, s) +} + +// RefTerm creates a new Term with a Ref value. +func RefTerm(r ...*Term) *Term { + return v1.RefTerm(r...) +} + +func IsVarCompatibleString(s string) bool { + return v1.IsVarCompatibleString(s) +} + +// QueryIterator defines the interface for querying AST documents with references. +type QueryIterator = v1.QueryIterator + +// ArrayTerm creates a new Term with an Array value. +func ArrayTerm(a ...*Term) *Term { + return v1.ArrayTerm(a...) +} + +// NewArray creates an Array with the terms provided. The array will +// use the provided term slice. +func NewArray(a ...*Term) *Array { + return v1.NewArray(a...) +} + +// Array represents an array as defined by the language. Arrays are similar to the +// same types as defined by JSON with the exception that they can contain Vars +// and References. +type Array = v1.Array + +// Set represents a set as defined by the language. +type Set = v1.Set + +// NewSet returns a new Set containing t. +func NewSet(t ...*Term) Set { + return v1.NewSet(t...) +} + +func SetTerm(t ...*Term) *Term { + return v1.SetTerm(t...) +} + +// Object represents an object as defined by the language. +type Object = v1.Object + +// NewObject creates a new Object with t. +func NewObject(t ...[2]*Term) Object { + return v1.NewObject(t...) +} + +// ObjectTerm creates a new Term with an Object value. +func ObjectTerm(o ...[2]*Term) *Term { + return v1.ObjectTerm(o...) +} + +func LazyObject(blob map[string]any) Object { + return v1.LazyObject(blob) +} + +// Item is a helper for constructing an tuple containing two Terms +// representing a key/value pair in an Object. +func Item(key, value *Term) [2]*Term { + return v1.Item(key, value) +} + +// NOTE(philipc): The only way to get an ObjectKeyIterator should be +// from an Object. This ensures that the iterator can have implementation- +// specific details internally, with no contracts except to the very +// limited interface. +type ObjectKeysIterator = v1.ObjectKeysIterator + +// ArrayComprehension represents an array comprehension as defined in the language. +type ArrayComprehension = v1.ArrayComprehension + +// ArrayComprehensionTerm creates a new Term with an ArrayComprehension value. +func ArrayComprehensionTerm(term *Term, body Body) *Term { + return v1.ArrayComprehensionTerm(term, body) +} + +// ObjectComprehension represents an object comprehension as defined in the language. +type ObjectComprehension = v1.ObjectComprehension + +// ObjectComprehensionTerm creates a new Term with an ObjectComprehension value. +func ObjectComprehensionTerm(key, value *Term, body Body) *Term { + return v1.ObjectComprehensionTerm(key, value, body) +} + +// SetComprehension represents a set comprehension as defined in the language. +type SetComprehension = v1.SetComprehension + +// SetComprehensionTerm creates a new Term with an SetComprehension value. +func SetComprehensionTerm(term *Term, body Body) *Term { + return v1.SetComprehensionTerm(term, body) +} + +// Call represents as function call in the language. +type Call = v1.Call + +// CallTerm returns a new Term with a Call value defined by terms. The first +// term is the operator and the rest are operands. +func CallTerm(terms ...*Term) *Term { + return v1.CallTerm(terms...) +} diff --git a/third_party/opa/ast/transform.go b/third_party/opa/ast/transform.go new file mode 100644 index 000000000000..8c03c4866371 --- /dev/null +++ b/third_party/opa/ast/transform.go @@ -0,0 +1,46 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// Transformer defines the interface for transforming AST elements. If the +// transformer returns nil and does not indicate an error, the AST element will +// be set to nil and no transformations will be applied to children of the +// element. +type Transformer = v1.Transformer + +// Transform iterates the AST and calls the Transform function on the +// Transformer t for x before recursing. +func Transform(t Transformer, x any) (any, error) { + return v1.Transform(t, x) +} + +// TransformRefs calls the function f on all references under x. +func TransformRefs(x any, f func(Ref) (Value, error)) (any, error) { + return v1.TransformRefs(x, f) +} + +// TransformVars calls the function f on all vars under x. +func TransformVars(x any, f func(Var) (Value, error)) (any, error) { + return v1.TransformVars(x, f) +} + +// TransformComprehensions calls the functio nf on all comprehensions under x. +func TransformComprehensions(x any, f func(any) (Value, error)) (any, error) { + return v1.TransformComprehensions(x, f) +} + +// GenericTransformer implements the Transformer interface to provide a utility +// to transform AST nodes using a closure. +type GenericTransformer = v1.GenericTransformer + +// NewGenericTransformer returns a new GenericTransformer that will transform +// AST nodes using the function f. +func NewGenericTransformer(f func(x any) (any, error)) *GenericTransformer { + return v1.NewGenericTransformer(f) +} diff --git a/third_party/opa/ast/unify.go b/third_party/opa/ast/unify.go new file mode 100644 index 000000000000..3cb260272aad --- /dev/null +++ b/third_party/opa/ast/unify.go @@ -0,0 +1,14 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import v1 "github.com/open-policy-agent/opa/v1/ast" + +// Unify returns a set of variables that will be unified when the equality expression defined by +// terms a and b is evaluated. The unifier assumes that variables in the VarSet safe are already +// unified. +func Unify(safe VarSet, a *Term, b *Term) VarSet { + return v1.Unify(safe, a, b) +} diff --git a/third_party/opa/ast/varset.go b/third_party/opa/ast/varset.go new file mode 100644 index 000000000000..9e7db8efdad1 --- /dev/null +++ b/third_party/opa/ast/varset.go @@ -0,0 +1,17 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + v1 "github.com/open-policy-agent/opa/v1/ast" +) + +// VarSet represents a set of variables. +type VarSet = v1.VarSet + +// NewVarSet returns a new VarSet containing the specified variables. +func NewVarSet(vs ...Var) VarSet { + return v1.NewVarSet(vs...) +} diff --git a/third_party/opa/ast/visit.go b/third_party/opa/ast/visit.go new file mode 100644 index 000000000000..f4f2459ecc44 --- /dev/null +++ b/third_party/opa/ast/visit.go @@ -0,0 +1,123 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import v1 "github.com/open-policy-agent/opa/v1/ast" + +// Visitor defines the interface for iterating AST elements. The Visit function +// can return a Visitor w which will be used to visit the children of the AST +// element v. If the Visit function returns nil, the children will not be +// visited. +// Deprecated: use GenericVisitor or another visitor implementation +type Visitor = v1.Visitor + +// BeforeAndAfterVisitor wraps Visitor to provide hooks for being called before +// and after the AST has been visited. +// Deprecated: use GenericVisitor or another visitor implementation +type BeforeAndAfterVisitor = v1.BeforeAndAfterVisitor + +// Walk iterates the AST by calling the Visit function on the Visitor +// v for x before recursing. +// Deprecated: use GenericVisitor.Walk +func Walk(v Visitor, x any) { + v1.Walk(v, x) +} + +// WalkBeforeAndAfter iterates the AST by calling the Visit function on the +// Visitor v for x before recursing. +// Deprecated: use GenericVisitor.Walk +func WalkBeforeAndAfter(v BeforeAndAfterVisitor, x any) { + v1.WalkBeforeAndAfter(v, x) +} + +// WalkVars calls the function f on all vars under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkVars(x any, f func(Var) bool) { + v1.WalkVars(x, f) +} + +// WalkClosures calls the function f on all closures under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkClosures(x any, f func(any) bool) { + v1.WalkClosures(x, f) +} + +// WalkRefs calls the function f on all references under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkRefs(x any, f func(Ref) bool) { + v1.WalkRefs(x, f) +} + +// WalkTerms calls the function f on all terms under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkTerms(x any, f func(*Term) bool) { + v1.WalkTerms(x, f) +} + +// WalkWiths calls the function f on all with modifiers under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkWiths(x any, f func(*With) bool) { + v1.WalkWiths(x, f) +} + +// WalkExprs calls the function f on all expressions under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkExprs(x any, f func(*Expr) bool) { + v1.WalkExprs(x, f) +} + +// WalkBodies calls the function f on all bodies under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkBodies(x any, f func(Body) bool) { + v1.WalkBodies(x, f) +} + +// WalkRules calls the function f on all rules under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkRules(x any, f func(*Rule) bool) { + v1.WalkRules(x, f) +} + +// WalkNodes calls the function f on all nodes under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkNodes(x any, f func(Node) bool) { + v1.WalkNodes(x, f) +} + +// GenericVisitor provides a utility to walk over AST nodes using a +// closure. If the closure returns true, the visitor will not walk +// over AST nodes under x. +type GenericVisitor = v1.GenericVisitor + +// NewGenericVisitor returns a new GenericVisitor that will invoke the function +// f on AST nodes. +func NewGenericVisitor(f func(x any) bool) *GenericVisitor { + return v1.NewGenericVisitor(f) +} + +// BeforeAfterVisitor provides a utility to walk over AST nodes using +// closures. If the before closure returns true, the visitor will not +// walk over AST nodes under x. The after closure is invoked always +// after visiting a node. +type BeforeAfterVisitor = v1.BeforeAfterVisitor + +// NewBeforeAfterVisitor returns a new BeforeAndAfterVisitor that +// will invoke the functions before and after AST nodes. +func NewBeforeAfterVisitor(before func(x any) bool, after func(x any)) *BeforeAfterVisitor { + return v1.NewBeforeAfterVisitor(before, after) +} + +// VarVisitor walks AST nodes under a given node and collects all encountered +// variables. The collected variables can be controlled by specifying +// VarVisitorParams when creating the visitor. +type VarVisitor = v1.VarVisitor + +// VarVisitorParams contains settings for a VarVisitor. +type VarVisitorParams = v1.VarVisitorParams + +// NewVarVisitor returns a new VarVisitor object. +func NewVarVisitor() *VarVisitor { + return v1.NewVarVisitor() +} diff --git a/third_party/opa/builtin_metadata.json b/third_party/opa/builtin_metadata.json new file mode 100644 index 000000000000..ee3432a80497 --- /dev/null +++ b/third_party/opa/builtin_metadata.json @@ -0,0 +1,25398 @@ +{ + "_categories": { + "aggregates": [ + "count", + "max", + "min", + "product", + "sort", + "sum" + ], + "array": [ + "array.concat", + "array.reverse", + "array.slice" + ], + "bits": [ + "bits.and", + "bits.lsh", + "bits.negate", + "bits.or", + "bits.rsh", + "bits.xor" + ], + "comparison": [ + "equal", + "gt", + "gte", + "lt", + "lte", + "neq" + ], + "conversions": [ + "to_number" + ], + "crypto": [ + "crypto.hmac.equal", + "crypto.hmac.md5", + "crypto.hmac.sha1", + "crypto.hmac.sha256", + "crypto.hmac.sha512", + "crypto.md5", + "crypto.parse_private_keys", + "crypto.sha1", + "crypto.sha256", + "crypto.x509.parse_and_verify_certificates", + "crypto.x509.parse_and_verify_certificates_with_options", + "crypto.x509.parse_certificate_request", + "crypto.x509.parse_certificates", + "crypto.x509.parse_keypair", + "crypto.x509.parse_rsa_private_key" + ], + "encoding": [ + "base64.decode", + "base64.encode", + "base64.is_valid", + "base64url.decode", + "base64url.encode", + "base64url.encode_no_pad", + "hex.decode", + "hex.encode", + "json.is_valid", + "json.marshal", + "json.marshal_with_options", + "json.unmarshal", + "urlquery.decode", + "urlquery.decode_object", + "urlquery.encode", + "urlquery.encode_object", + "yaml.is_valid", + "yaml.marshal", + "yaml.unmarshal" + ], + "glob": [ + "glob.match", + "glob.quote_meta" + ], + "graph": [ + "graph.reachable", + "graph.reachable_paths", + "walk" + ], + "graphql": [ + "graphql.is_valid", + "graphql.parse", + "graphql.parse_and_verify", + "graphql.parse_query", + "graphql.parse_schema", + "graphql.schema_is_valid" + ], + "http": [ + "http.send" + ], + "internal": [ + "internal.member_2", + "internal.member_3", + "internal.print", + "internal.test_case" + ], + "net": [ + "net.cidr_contains", + "net.cidr_contains_matches", + "net.cidr_expand", + "net.cidr_intersects", + "net.cidr_is_valid", + "net.cidr_merge", + "net.lookup_ip_addr" + ], + "numbers": [ + "abs", + "ceil", + "div", + "floor", + "minus", + "mul", + "numbers.range", + "numbers.range_step", + "plus", + "rand.intn", + "rem", + "round" + ], + "object": [ + "json.filter", + "json.match_schema", + "json.patch", + "json.remove", + "json.verify_schema", + "object.filter", + "object.get", + "object.keys", + "object.remove", + "object.subset", + "object.union", + "object.union_n" + ], + "opa": [ + "opa.runtime" + ], + "providers.aws": [ + "providers.aws.sign_req" + ], + "regex": [ + "regex.find_all_string_submatch_n", + "regex.find_n", + "regex.globs_match", + "regex.is_valid", + "regex.match", + "regex.replace", + "regex.split", + "regex.template_match" + ], + "rego": [ + "rego.metadata.chain", + "rego.metadata.rule", + "rego.parse_module" + ], + "semver": [ + "semver.compare", + "semver.is_valid" + ], + "sets": [ + "and", + "intersection", + "minus", + "or", + "union" + ], + "strings": [ + "concat", + "contains", + "endswith", + "format_int", + "indexof", + "indexof_n", + "lower", + "replace", + "split", + "sprintf", + "startswith", + "strings.any_prefix_match", + "strings.any_suffix_match", + "strings.count", + "strings.render_template", + "strings.replace_n", + "strings.reverse", + "substring", + "trim", + "trim_left", + "trim_prefix", + "trim_right", + "trim_space", + "trim_suffix", + "upper" + ], + "time": [ + "time.add_date", + "time.clock", + "time.date", + "time.diff", + "time.format", + "time.now_ns", + "time.parse_duration_ns", + "time.parse_ns", + "time.parse_rfc3339_ns", + "time.weekday" + ], + "tokens": [ + "io.jwt.decode", + "io.jwt.decode_verify", + "io.jwt.verify_es256", + "io.jwt.verify_es384", + "io.jwt.verify_es512", + "io.jwt.verify_hs256", + "io.jwt.verify_hs384", + "io.jwt.verify_hs512", + "io.jwt.verify_ps256", + "io.jwt.verify_ps384", + "io.jwt.verify_ps512", + "io.jwt.verify_rs256", + "io.jwt.verify_rs384", + "io.jwt.verify_rs512" + ], + "tokensign": [ + "io.jwt.encode_sign", + "io.jwt.encode_sign_raw" + ], + "tracing": [ + "trace" + ], + "types": [ + "is_array", + "is_boolean", + "is_null", + "is_number", + "is_object", + "is_set", + "is_string", + "type_name" + ], + "units": [ + "units.parse", + "units.parse_bytes" + ], + "uuid": [ + "uuid.parse", + "uuid.rfc4122" + ] + }, + "abs": { + "args": [ + { + "description": "the number to take the absolute value of", + "name": "x", + "type": "number" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the number without its sign.", + "introduced": "v0.17.0", + "result": { + "description": "the absolute value of `x`", + "name": "y", + "type": "number" + }, + "wasm": true + }, + "all": { + "args": [ + { + "type": "any\u003carray[any], set[any]\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "deprecated": true, + "introduced": "v0.17.0", + "result": { + "type": "boolean" + }, + "wasm": true + }, + "and": { + "args": [ + { + "description": "the first set", + "name": "x", + "type": "set[any]" + }, + { + "description": "the second set", + "name": "y", + "type": "set[any]" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the intersection of two sets.", + "infix": "\u0026", + "introduced": "v0.17.0", + "result": { + "description": "the intersection of `x` and `y`", + "name": "z", + "type": "set[any]" + }, + "wasm": true + }, + "any": { + "args": [ + { + "type": "any\u003carray[any], set[any]\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "deprecated": true, + "introduced": "v0.17.0", + "result": { + "type": "boolean" + }, + "wasm": true + }, + "array.concat": { + "args": [ + { + "description": "the first array", + "name": "x", + "type": "array[any]" + }, + { + "description": "the second array", + "name": "y", + "type": "array[any]" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Concatenates two arrays.", + "introduced": "v0.17.0", + "result": { + "description": "the concatenation of `x` and `y`", + "name": "z", + "type": "array[any]" + }, + "wasm": true + }, + "array.reverse": { + "args": [ + { + "description": "the array to be reversed", + "name": "arr", + "type": "array[any]" + } + ], + "available": [ + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the reverse of a given array.", + "introduced": "v0.36.0", + "result": { + "description": "an array containing the elements of `arr` in reverse order", + "name": "rev", + "type": "array[any]" + }, + "wasm": true + }, + "array.slice": { + "args": [ + { + "description": "the array to be sliced", + "name": "arr", + "type": "array[any]" + }, + { + "description": "the start index of the returned slice; if less than zero, it's clamped to 0", + "name": "start", + "type": "number" + }, + { + "description": "the stop index of the returned slice; if larger than `count(arr)`, it's clamped to `count(arr)`", + "name": "stop", + "type": "number" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a slice of a given array. If `start` is greater or equal than `stop`, `slice` is `[]`.", + "introduced": "v0.17.0", + "result": { + "description": "the subslice of `array`, from `start` to `end`, including `arr[start]`, but excluding `arr[end]`", + "name": "slice", + "type": "array[any]" + }, + "wasm": true + }, + "assign": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "infix": ":=", + "introduced": "v0.17.0", + "result": { + "type": "boolean" + }, + "wasm": false + }, + "base64.decode": { + "args": [ + { + "description": "string to decode", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Deserializes the base64 encoded input string.", + "introduced": "v0.17.0", + "result": { + "description": "base64 deserialization of `x`", + "name": "y", + "type": "string" + }, + "wasm": true + }, + "base64.encode": { + "args": [ + { + "description": "string to encode", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Serializes the input string into base64 encoding.", + "introduced": "v0.17.0", + "result": { + "description": "base64 serialization of `x`", + "name": "y", + "type": "string" + }, + "wasm": true + }, + "base64.is_valid": { + "args": [ + { + "description": "string to check", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies the input string is base64 encoded.", + "introduced": "v0.24.0", + "result": { + "description": "`true` if `x` is valid base64 encoded value, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "base64url.decode": { + "args": [ + { + "description": "string to decode", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Deserializes the base64url encoded input string.", + "introduced": "v0.17.0", + "result": { + "description": "base64url deserialization of `x`", + "name": "y", + "type": "string" + }, + "wasm": true + }, + "base64url.encode": { + "args": [ + { + "description": "string to encode", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Serializes the input string into base64url encoding.", + "introduced": "v0.17.0", + "result": { + "description": "base64url serialization of `x`", + "name": "y", + "type": "string" + }, + "wasm": true + }, + "base64url.encode_no_pad": { + "args": [ + { + "description": "string to encode", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Serializes the input string into base64url encoding without padding.", + "introduced": "v0.25.0-rc2", + "result": { + "description": "base64url serialization of `x`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "bits.and": { + "args": [ + { + "description": "the first integer", + "name": "x", + "type": "number" + }, + { + "description": "the second integer", + "name": "y", + "type": "number" + } + ], + "available": [ + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the bitwise \"AND\" of two integers.", + "introduced": "v0.18.0", + "result": { + "description": "the bitwise AND of `x` and `y`", + "name": "z", + "type": "number" + }, + "wasm": true + }, + "bits.lsh": { + "args": [ + { + "description": "the integer to shift", + "name": "x", + "type": "number" + }, + { + "description": "the number of bits to shift", + "name": "s", + "type": "number" + } + ], + "available": [ + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a new integer with its bits shifted `s` bits to the left.", + "introduced": "v0.18.0", + "result": { + "description": "the result of shifting `x` `s` bits to the left", + "name": "z", + "type": "number" + }, + "wasm": true + }, + "bits.negate": { + "args": [ + { + "description": "the integer to negate", + "name": "x", + "type": "number" + } + ], + "available": [ + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the bitwise negation (flip) of an integer.", + "introduced": "v0.18.0", + "result": { + "description": "the bitwise negation of `x`", + "name": "z", + "type": "number" + }, + "wasm": true + }, + "bits.or": { + "args": [ + { + "description": "the first integer", + "name": "x", + "type": "number" + }, + { + "description": "the second integer", + "name": "y", + "type": "number" + } + ], + "available": [ + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the bitwise \"OR\" of two integers.", + "introduced": "v0.18.0", + "result": { + "description": "the bitwise OR of `x` and `y`", + "name": "z", + "type": "number" + }, + "wasm": true + }, + "bits.rsh": { + "args": [ + { + "description": "the integer to shift", + "name": "x", + "type": "number" + }, + { + "description": "the number of bits to shift", + "name": "s", + "type": "number" + } + ], + "available": [ + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a new integer with its bits shifted `s` bits to the right.", + "introduced": "v0.18.0", + "result": { + "description": "the result of shifting `x` `s` bits to the right", + "name": "z", + "type": "number" + }, + "wasm": true + }, + "bits.xor": { + "args": [ + { + "description": "the first integer", + "name": "x", + "type": "number" + }, + { + "description": "the second integer", + "name": "y", + "type": "number" + } + ], + "available": [ + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the bitwise \"XOR\" (exclusive-or) of two integers.", + "introduced": "v0.18.0", + "result": { + "description": "the bitwise XOR of `x` and `y`", + "name": "z", + "type": "number" + }, + "wasm": true + }, + "cast_array": { + "args": [ + { + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "deprecated": true, + "introduced": "v0.17.0", + "result": { + "type": "array[any]" + }, + "wasm": false + }, + "cast_boolean": { + "args": [ + { + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "deprecated": true, + "introduced": "v0.17.0", + "result": { + "type": "boolean" + }, + "wasm": false + }, + "cast_null": { + "args": [ + { + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "deprecated": true, + "introduced": "v0.17.0", + "result": { + "type": "null" + }, + "wasm": false + }, + "cast_object": { + "args": [ + { + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "deprecated": true, + "introduced": "v0.17.0", + "result": { + "type": "object[any: any]" + }, + "wasm": false + }, + "cast_set": { + "args": [ + { + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "deprecated": true, + "introduced": "v0.17.0", + "result": { + "type": "set[any]" + }, + "wasm": false + }, + "cast_string": { + "args": [ + { + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "deprecated": true, + "introduced": "v0.17.0", + "result": { + "type": "string" + }, + "wasm": false + }, + "ceil": { + "args": [ + { + "description": "the number to round", + "name": "x", + "type": "number" + } + ], + "available": [ + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Rounds the number _up_ to the nearest integer.", + "introduced": "v0.26.0", + "result": { + "description": "the result of rounding `x` _up_", + "name": "y", + "type": "number" + }, + "wasm": true + }, + "concat": { + "args": [ + { + "description": "string to use as a delimiter", + "name": "delimiter", + "type": "string" + }, + { + "description": "strings to join", + "name": "collection", + "type": "any\u003carray[string], set[string]\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Joins a set or array of strings with a delimiter.", + "introduced": "v0.17.0", + "result": { + "description": "the joined string", + "name": "output", + "type": "string" + }, + "wasm": true + }, + "contains": { + "args": [ + { + "description": "string to search in", + "name": "haystack", + "type": "string" + }, + { + "description": "substring to look for", + "name": "needle", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns `true` if the search string is included in the base string", + "introduced": "v0.17.0", + "result": { + "description": "result of the containment check", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "count": { + "args": [ + { + "description": "the set/array/object/string to be counted", + "name": "collection", + "type": "any\u003cstring, array[any], object[any: any], set[any]\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": " Count takes a collection or string and returns the number of elements (or characters) in it.", + "introduced": "v0.17.0", + "result": { + "description": "the count of elements, key/val pairs, or characters, respectively.", + "name": "n", + "type": "number" + }, + "wasm": true + }, + "crypto.hmac.equal": { + "args": [ + { + "description": "mac1 to compare", + "name": "mac1", + "type": "string" + }, + { + "description": "mac2 to compare", + "name": "mac2", + "type": "string" + } + ], + "available": [ + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a boolean representing the result of comparing two MACs for equality without leaking timing information.", + "introduced": "v0.52.0", + "result": { + "description": "`true` if the MACs are equals, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "crypto.hmac.md5": { + "args": [ + { + "description": "input string", + "name": "x", + "type": "string" + }, + { + "description": "key to use", + "name": "key", + "type": "string" + } + ], + "available": [ + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a string representing the MD5 HMAC of the input message using the input key.", + "introduced": "v0.36.0", + "result": { + "description": "MD5-HMAC of `x`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "crypto.hmac.sha1": { + "args": [ + { + "description": "input string", + "name": "x", + "type": "string" + }, + { + "description": "key to use", + "name": "key", + "type": "string" + } + ], + "available": [ + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a string representing the SHA1 HMAC of the input message using the input key.", + "introduced": "v0.36.0", + "result": { + "description": "SHA1-HMAC of `x`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "crypto.hmac.sha256": { + "args": [ + { + "description": "input string", + "name": "x", + "type": "string" + }, + { + "description": "key to use", + "name": "key", + "type": "string" + } + ], + "available": [ + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a string representing the SHA256 HMAC of the input message using the input key.", + "introduced": "v0.36.0", + "result": { + "description": "SHA256-HMAC of `x`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "crypto.hmac.sha512": { + "args": [ + { + "description": "input string", + "name": "x", + "type": "string" + }, + { + "description": "key to use", + "name": "key", + "type": "string" + } + ], + "available": [ + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a string representing the SHA512 HMAC of the input message using the input key.", + "introduced": "v0.36.0", + "result": { + "description": "SHA512-HMAC of `x`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "crypto.md5": { + "args": [ + { + "description": "input string", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a string representing the input string hashed with the MD5 function", + "introduced": "v0.17.0", + "result": { + "description": "MD5-hash of `x`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "crypto.parse_private_keys": { + "args": [ + { + "description": "PEM encoded data containing one or more private keys as concatenated blocks. Optionally Base64 encoded.", + "name": "keys", + "type": "string" + } + ], + "available": [ + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns zero or more private keys from the given encoded string containing DER certificate data.\n\nIf the input is empty, the function will return null. The input string should be a list of one or more concatenated PEM blocks. The whole input of concatenated PEM blocks can optionally be Base64 encoded.", + "introduced": "v0.55.0", + "result": { + "description": "parsed private keys represented as objects", + "name": "output", + "type": "array[object[string: any]]" + }, + "wasm": false + }, + "crypto.sha1": { + "args": [ + { + "description": "input string", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a string representing the input string hashed with the SHA1 function", + "introduced": "v0.17.0", + "result": { + "description": "SHA1-hash of `x`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "crypto.sha256": { + "args": [ + { + "description": "input string", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a string representing the input string hashed with the SHA256 function", + "introduced": "v0.17.0", + "result": { + "description": "SHA256-hash of `x`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "crypto.x509.parse_and_verify_certificates": { + "args": [ + { + "description": "base64 encoded DER or PEM data containing two or more certificates where the first is a root CA, the last is a leaf certificate, and all others are intermediate CAs", + "name": "certs", + "type": "string" + } + ], + "available": [ + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns one or more certificates from the given string containing PEM\nor base64 encoded DER certificates after verifying the supplied certificates form a complete\ncertificate chain back to a trusted root.\n\nThe first certificate is treated as the root and the last is treated as the leaf,\nwith all others being treated as intermediates.", + "introduced": "v0.31.0", + "result": { + "description": "array of `[valid, certs]`: if the input certificate chain could be verified then `valid` is `true` and `certs` is an array of X.509 certificates represented as objects; if the input certificate chain could not be verified then `valid` is `false` and `certs` is `[]`", + "name": "output", + "type": "array\u003cboolean, array[object[string: any]]\u003e" + }, + "wasm": false + }, + "crypto.x509.parse_and_verify_certificates_with_options": { + "args": [ + { + "description": "base64 encoded DER or PEM data containing two or more certificates where the first is a root CA, the last is a leaf certificate, and all others are intermediate CAs", + "name": "certs", + "type": "string" + }, + { + "description": "object containing extra configs to verify the validity of certificates. `options` object supports four fields which maps to same fields in [x509.VerifyOptions struct](https://pkg.go.dev/crypto/x509#VerifyOptions). `DNSName`, `CurrentTime`: Nanoseconds since the Unix Epoch as a number, `MaxConstraintComparisons` and `KeyUsages`. `KeyUsages` is list and can have possible values as in: `\"KeyUsageAny\"`, `\"KeyUsageServerAuth\"`, `\"KeyUsageClientAuth\"`, `\"KeyUsageCodeSigning\"`, `\"KeyUsageEmailProtection\"`, `\"KeyUsageIPSECEndSystem\"`, `\"KeyUsageIPSECTunnel\"`, `\"KeyUsageIPSECUser\"`, `\"KeyUsageTimeStamping\"`, `\"KeyUsageOCSPSigning\"`, `\"KeyUsageMicrosoftServerGatedCrypto\"`, `\"KeyUsageNetscapeServerGatedCrypto\"`, `\"KeyUsageMicrosoftCommercialCodeSigning\"`, `\"KeyUsageMicrosoftKernelCodeSigning\"` ", + "name": "options", + "type": "object[string: any]" + } + ], + "available": [ + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns one or more certificates from the given string containing PEM\nor base64 encoded DER certificates after verifying the supplied certificates form a complete\ncertificate chain back to a trusted root. A config option passed as the second argument can\nbe used to configure the validation options used.\n\nThe first certificate is treated as the root and the last is treated as the leaf,\nwith all others being treated as intermediates.", + "introduced": "v0.63.0", + "result": { + "description": "array of `[valid, certs]`: if the input certificate chain could be verified then `valid` is `true` and `certs` is an array of X.509 certificates represented as objects; if the input certificate chain could not be verified then `valid` is `false` and `certs` is `[]`", + "name": "output", + "type": "array\u003cboolean, array[object[string: any]]\u003e" + }, + "wasm": false + }, + "crypto.x509.parse_certificate_request": { + "args": [ + { + "description": "base64 string containing either a PEM encoded or DER CSR or a string containing a PEM CSR", + "name": "csr", + "type": "string" + } + ], + "available": [ + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a PKCS #10 certificate signing request from the given PEM-encoded PKCS#10 certificate signing request.", + "introduced": "v0.21.0", + "result": { + "description": "X.509 CSR represented as an object", + "name": "output", + "type": "object[string: any]" + }, + "wasm": false + }, + "crypto.x509.parse_certificates": { + "args": [ + { + "description": "base64 encoded DER or PEM data containing one or more certificates or a PEM string of one or more certificates", + "name": "certs", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns zero or more certificates from the given encoded string containing\nDER certificate data.\n\nIf the input is empty, the function will return null. The input string should be a list of one or more\nconcatenated PEM blocks. The whole input of concatenated PEM blocks can optionally be Base64 encoded.", + "introduced": "v0.17.0", + "result": { + "description": "parsed X.509 certificates represented as objects", + "name": "output", + "type": "array[object[string: any]]" + }, + "wasm": false + }, + "crypto.x509.parse_keypair": { + "args": [ + { + "description": "string containing PEM or base64 encoded DER certificates", + "name": "cert", + "type": "string" + }, + { + "description": "string containing PEM or base64 encoded DER keys", + "name": "pem", + "type": "string" + } + ], + "available": [ + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a valid key pair", + "introduced": "v0.53.0", + "result": { + "description": "if key pair is valid, returns the tls.certificate(https://pkg.go.dev/crypto/tls#Certificate) as an object. If the key pair is invalid, nil and an error are returned.", + "name": "output", + "type": "object[string: any]" + }, + "wasm": false + }, + "crypto.x509.parse_rsa_private_key": { + "args": [ + { + "description": "base64 string containing a PEM encoded RSA private key", + "name": "pem", + "type": "string" + } + ], + "available": [ + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a JWK for signing a JWT from the given PEM-encoded RSA private key.", + "introduced": "v0.33.0", + "result": { + "description": "JWK as an object", + "name": "output", + "type": "object[string: any]" + }, + "wasm": false + }, + "div": { + "args": [ + { + "description": "the dividend", + "name": "x", + "type": "number" + }, + { + "description": "the divisor", + "name": "y", + "type": "number" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Divides the first number by the second number.", + "infix": "/", + "introduced": "v0.17.0", + "result": { + "description": "the result of `x` divided by `y`", + "name": "z", + "type": "number" + }, + "wasm": true + }, + "endswith": { + "args": [ + { + "description": "search string", + "name": "search", + "type": "string" + }, + { + "description": "base string", + "name": "base", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns true if the search string ends with the base string.", + "introduced": "v0.17.0", + "result": { + "description": "result of the suffix check", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "eq": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "infix": "=", + "introduced": "v0.17.0", + "result": { + "type": "boolean" + }, + "wasm": false + }, + "equal": { + "args": [ + { + "name": "x", + "type": "any" + }, + { + "name": "y", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "infix": "==", + "introduced": "v0.17.0", + "result": { + "description": "true if `x` is equal to `y`; false otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "floor": { + "args": [ + { + "description": "the number to round", + "name": "x", + "type": "number" + } + ], + "available": [ + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Rounds the number _down_ to the nearest integer.", + "introduced": "v0.26.0", + "result": { + "description": "the result of rounding `x` _down_", + "name": "y", + "type": "number" + }, + "wasm": true + }, + "format_int": { + "args": [ + { + "description": "number to format", + "name": "number", + "type": "number" + }, + { + "description": "base of number representation to use", + "name": "base", + "type": "number" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the string representation of the number in the given base after rounding it down to an integer value.", + "introduced": "v0.17.0", + "result": { + "description": "formatted number", + "name": "output", + "type": "string" + }, + "wasm": true + }, + "glob.match": { + "args": [ + { + "description": "glob pattern", + "name": "pattern", + "type": "string" + }, + { + "description": "glob pattern delimiters, e.g. `[\".\", \":\"]`, defaults to `[\".\"]` if unset. If `delimiters` is `null`, glob match without delimiter.", + "name": "delimiters", + "type": "any\u003cnull, array[string]\u003e" + }, + { + "description": "string to match against `pattern`", + "name": "match", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Parses and matches strings against the glob notation. Not to be confused with `regex.globs_match`.", + "introduced": "v0.17.0", + "result": { + "description": "true if `match` can be found in `pattern` which is separated by `delimiters`", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "glob.quote_meta": { + "args": [ + { + "description": "glob pattern", + "name": "pattern", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a string which represents a version of the pattern where all asterisks have been escaped.", + "introduced": "v0.17.0", + "result": { + "description": "the escaped string of `pattern`", + "name": "output", + "type": "string" + }, + "wasm": false + }, + "graph.reachable": { + "args": [ + { + "description": "object containing a set or array of neighboring vertices", + "name": "graph", + "type": "object[any: any\u003carray[any], set[any]\u003e]" + }, + { + "description": "set or array of root vertices", + "name": "initial", + "type": "any\u003carray[any], set[any]\u003e" + } + ], + "available": [ + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Computes the set of reachable nodes in the graph from a set of starting nodes.", + "introduced": "v0.20.0", + "result": { + "description": "set of vertices reachable from the `initial` vertices in the directed `graph`", + "name": "output", + "type": "set[any]" + }, + "wasm": true + }, + "graph.reachable_paths": { + "args": [ + { + "description": "object containing a set or array of root vertices", + "name": "graph", + "type": "object[any: any\u003carray[any], set[any]\u003e]" + }, + { + "description": "initial paths", + "name": "initial", + "type": "any\u003carray[any], set[any]\u003e" + } + ], + "available": [ + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Computes the set of reachable paths in the graph from a set of starting nodes.", + "introduced": "v0.37.0", + "result": { + "description": "paths reachable from the `initial` vertices in the directed `graph`", + "name": "output", + "type": "set[array[any]]" + }, + "wasm": false + }, + "graphql.is_valid": { + "args": [ + { + "description": "the GraphQL query", + "name": "query", + "type": "any\u003cstring, object[any: any]\u003e" + }, + { + "description": "the GraphQL schema", + "name": "schema", + "type": "any\u003cstring, object[any: any]\u003e" + } + ], + "available": [ + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Checks that a GraphQL query is valid against a given schema. The query and/or schema can be either GraphQL strings or AST objects from the other GraphQL builtin functions.", + "introduced": "v0.41.0", + "result": { + "description": "`true` if the query is valid under the given schema. `false` otherwise.", + "name": "output", + "type": "boolean" + }, + "wasm": false + }, + "graphql.parse": { + "args": [ + { + "description": "the GraphQL query", + "name": "query", + "type": "any\u003cstring, object[any: any]\u003e" + }, + { + "description": "the GraphQL schema", + "name": "schema", + "type": "any\u003cstring, object[any: any]\u003e" + } + ], + "available": [ + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns AST objects for a given GraphQL query and schema after validating the query against the schema. Returns undefined if errors were encountered during parsing or validation. The query and/or schema can be either GraphQL strings or AST objects from the other GraphQL builtin functions.", + "introduced": "v0.41.0", + "result": { + "description": "`output` is of the form `[query_ast, schema_ast]`. If the GraphQL query is valid given the provided schema, then `query_ast` and `schema_ast` are objects describing the ASTs for the query and schema.", + "name": "output", + "type": "array\u003cobject[any: any], object[any: any]\u003e" + }, + "wasm": false + }, + "graphql.parse_and_verify": { + "args": [ + { + "description": "the GraphQL query", + "name": "query", + "type": "any\u003cstring, object[any: any]\u003e" + }, + { + "description": "the GraphQL schema", + "name": "schema", + "type": "any\u003cstring, object[any: any]\u003e" + } + ], + "available": [ + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a boolean indicating success or failure alongside the parsed ASTs for a given GraphQL query and schema after validating the query against the schema. The query and/or schema can be either GraphQL strings or AST objects from the other GraphQL builtin functions.", + "introduced": "v0.41.0", + "result": { + "description": " `output` is of the form `[valid, query_ast, schema_ast]`. If the query is valid given the provided schema, then `valid` is `true`, and `query_ast` and `schema_ast` are objects describing the ASTs for the GraphQL query and schema. Otherwise, `valid` is `false` and `query_ast` and `schema_ast` are `{}`.", + "name": "output", + "type": "array\u003cboolean, object[any: any], object[any: any]\u003e" + }, + "wasm": false + }, + "graphql.parse_query": { + "args": [ + { + "description": "GraphQL query string", + "name": "query", + "type": "string" + } + ], + "available": [ + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns an AST object for a GraphQL query.", + "introduced": "v0.41.0", + "result": { + "description": "AST object for the GraphQL query.", + "name": "output", + "type": "object[any: any]" + }, + "wasm": false + }, + "graphql.parse_schema": { + "args": [ + { + "description": "GraphQL schema string", + "name": "schema", + "type": "string" + } + ], + "available": [ + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns an AST object for a GraphQL schema.", + "introduced": "v0.41.0", + "result": { + "description": "AST object for the GraphQL schema.", + "name": "output", + "type": "object[any: any]" + }, + "wasm": false + }, + "graphql.schema_is_valid": { + "args": [ + { + "description": "the schema to verify", + "name": "schema", + "type": "any\u003cstring, object[any: any]\u003e" + } + ], + "available": [ + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Checks that the input is a valid GraphQL schema. The schema can be either a GraphQL string or an AST object from the other GraphQL builtin functions.", + "introduced": "v0.46.0", + "result": { + "description": "`true` if the schema is a valid GraphQL schema. `false` otherwise.", + "name": "output", + "type": "boolean" + }, + "wasm": false + }, + "gt": { + "args": [ + { + "name": "x", + "type": "any" + }, + { + "name": "y", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "infix": "\u003e", + "introduced": "v0.17.0", + "result": { + "description": "true if `x` is greater than `y`; false otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "gte": { + "args": [ + { + "name": "x", + "type": "any" + }, + { + "name": "y", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "infix": "\u003e=", + "introduced": "v0.17.0", + "result": { + "description": "true if `x` is greater or equal to `y`; false otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "hex.decode": { + "args": [ + { + "description": "a hex-encoded string", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Deserializes the hex-encoded input string.", + "introduced": "v0.25.0-rc2", + "result": { + "description": "deserialized from `x`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "hex.encode": { + "args": [ + { + "description": "string to encode", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Serializes the input string using hex-encoding.", + "introduced": "v0.25.0-rc2", + "result": { + "description": "serialization of `x` using hex-encoding", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "http.send": { + "args": [ + { + "description": "the HTTP request object", + "name": "request", + "type": "object[string: any]" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a HTTP response to the given HTTP request.", + "introduced": "v0.17.0", + "result": { + "description": "the HTTP response object", + "name": "response", + "type": "object[any: any]" + }, + "wasm": false + }, + "indexof": { + "args": [ + { + "description": "string to search in", + "name": "haystack", + "type": "string" + }, + { + "description": "substring to look for", + "name": "needle", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the index of a substring contained inside a string.", + "introduced": "v0.17.0", + "result": { + "description": "index of first occurrence, `-1` if not found", + "name": "output", + "type": "number" + }, + "wasm": true + }, + "indexof_n": { + "args": [ + { + "description": "string to search in", + "name": "haystack", + "type": "string" + }, + { + "description": "substring to look for", + "name": "needle", + "type": "string" + } + ], + "available": [ + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a list of all the indexes of a substring contained inside a string.", + "introduced": "v0.37.0", + "result": { + "description": "all indices at which `needle` occurs in `haystack`, may be empty", + "name": "output", + "type": "array[number]" + }, + "wasm": false + }, + "internal.member_2": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "available": [ + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "infix": "in", + "introduced": "v0.34.0", + "result": { + "type": "boolean" + }, + "wasm": true + }, + "internal.member_3": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "available": [ + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "infix": "in", + "introduced": "v0.34.0", + "result": { + "type": "boolean" + }, + "wasm": true + }, + "internal.print": { + "args": [ + { + "type": "array[set[any]]" + } + ], + "available": [ + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "introduced": "v0.34.0", + "result": {}, + "wasm": false + }, + "internal.test_case": { + "args": [ + { + "type": "array[any]" + } + ], + "available": [ + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "introduced": "v1.2.0", + "result": {}, + "wasm": false + }, + "intersection": { + "args": [ + { + "description": "set of sets to intersect", + "name": "xs", + "type": "set[set[any]]" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the intersection of the given input sets.", + "introduced": "v0.17.0", + "result": { + "description": "the intersection of all `xs` sets", + "name": "y", + "type": "set[any]" + }, + "wasm": true + }, + "io.jwt.decode": { + "args": [ + { + "description": "JWT token to decode", + "name": "jwt", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Decodes a JSON Web Token and outputs it as an object.", + "introduced": "v0.17.0", + "result": { + "description": "`[header, payload, sig]`, where `header` and `payload` are objects; `sig` is the hexadecimal representation of the signature on the token.", + "name": "output", + "type": "array\u003cobject[any: any], object[any: any], string\u003e" + }, + "wasm": false + }, + "io.jwt.decode_verify": { + "args": [ + { + "description": "JWT token whose signature is to be verified and whose claims are to be checked", + "name": "jwt", + "type": "string" + }, + { + "description": "claim verification constraints", + "name": "constraints", + "type": "object[string: any]" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies a JWT signature under parameterized constraints and decodes the claims if it is valid.\nSupports the following algorithms: HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512, PS256, PS384 and PS512.", + "introduced": "v0.17.0", + "result": { + "description": "`[valid, header, payload]`: if the input token is verified and meets the requirements of `constraints` then `valid` is `true`; `header` and `payload` are objects containing the JOSE header and the JWT claim set; otherwise, `valid` is `false`, `header` and `payload` are `{}`", + "name": "output", + "type": "array\u003cboolean, object[any: any], object[any: any]\u003e" + }, + "wasm": false + }, + "io.jwt.encode_sign": { + "args": [ + { + "description": "JWS Protected Header", + "name": "headers", + "type": "object[string: any]" + }, + { + "description": "JWS Payload", + "name": "payload", + "type": "object[string: any]" + }, + { + "description": "JSON Web Key (RFC7517)", + "name": "key", + "type": "object[string: any]" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Encodes and optionally signs a JSON Web Token. Inputs are taken as objects, not encoded strings (see `io.jwt.encode_sign_raw`).", + "introduced": "v0.17.0", + "result": { + "description": "signed JWT", + "name": "output", + "type": "string" + }, + "wasm": false + }, + "io.jwt.encode_sign_raw": { + "args": [ + { + "description": "JWS Protected Header", + "name": "headers", + "type": "string" + }, + { + "description": "JWS Payload", + "name": "payload", + "type": "string" + }, + { + "description": "JSON Web Key (RFC7517)", + "name": "key", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Encodes and optionally signs a JSON Web Token.", + "introduced": "v0.17.0", + "result": { + "description": "signed JWT", + "name": "output", + "type": "string" + }, + "wasm": false + }, + "io.jwt.verify_es256": { + "args": [ + { + "description": "JWT token whose signature is to be verified", + "name": "jwt", + "type": "string" + }, + { + "description": "PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature", + "name": "certificate", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies if a ES256 JWT signature is valid.", + "introduced": "v0.17.0", + "result": { + "description": "`true` if the signature is valid, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "io.jwt.verify_es384": { + "args": [ + { + "description": "JWT token whose signature is to be verified", + "name": "jwt", + "type": "string" + }, + { + "description": "PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature", + "name": "certificate", + "type": "string" + } + ], + "available": [ + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies if a ES384 JWT signature is valid.", + "introduced": "v0.20.0", + "result": { + "description": "`true` if the signature is valid, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "io.jwt.verify_es512": { + "args": [ + { + "description": "JWT token whose signature is to be verified", + "name": "jwt", + "type": "string" + }, + { + "description": "PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature", + "name": "certificate", + "type": "string" + } + ], + "available": [ + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies if a ES512 JWT signature is valid.", + "introduced": "v0.20.0", + "result": { + "description": "`true` if the signature is valid, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "io.jwt.verify_hs256": { + "args": [ + { + "description": "JWT token whose signature is to be verified", + "name": "jwt", + "type": "string" + }, + { + "description": "plain text secret used to verify the signature", + "name": "secret", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies if a HS256 (secret) JWT signature is valid.", + "introduced": "v0.17.0", + "result": { + "description": "`true` if the signature is valid, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "io.jwt.verify_hs384": { + "args": [ + { + "description": "JWT token whose signature is to be verified", + "name": "jwt", + "type": "string" + }, + { + "description": "plain text secret used to verify the signature", + "name": "secret", + "type": "string" + } + ], + "available": [ + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies if a HS384 (secret) JWT signature is valid.", + "introduced": "v0.20.0", + "result": { + "description": "`true` if the signature is valid, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "io.jwt.verify_hs512": { + "args": [ + { + "description": "JWT token whose signature is to be verified", + "name": "jwt", + "type": "string" + }, + { + "description": "plain text secret used to verify the signature", + "name": "secret", + "type": "string" + } + ], + "available": [ + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies if a HS512 (secret) JWT signature is valid.", + "introduced": "v0.20.0", + "result": { + "description": "`true` if the signature is valid, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "io.jwt.verify_ps256": { + "args": [ + { + "description": "JWT token whose signature is to be verified", + "name": "jwt", + "type": "string" + }, + { + "description": "PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature", + "name": "certificate", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies if a PS256 JWT signature is valid.", + "introduced": "v0.17.0", + "result": { + "description": "`true` if the signature is valid, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "io.jwt.verify_ps384": { + "args": [ + { + "description": "JWT token whose signature is to be verified", + "name": "jwt", + "type": "string" + }, + { + "description": "PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature", + "name": "certificate", + "type": "string" + } + ], + "available": [ + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies if a PS384 JWT signature is valid.", + "introduced": "v0.20.0", + "result": { + "description": "`true` if the signature is valid, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "io.jwt.verify_ps512": { + "args": [ + { + "description": "JWT token whose signature is to be verified", + "name": "jwt", + "type": "string" + }, + { + "description": "PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature", + "name": "certificate", + "type": "string" + } + ], + "available": [ + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies if a PS512 JWT signature is valid.", + "introduced": "v0.20.0", + "result": { + "description": "`true` if the signature is valid, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "io.jwt.verify_rs256": { + "args": [ + { + "description": "JWT token whose signature is to be verified", + "name": "jwt", + "type": "string" + }, + { + "description": "PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature", + "name": "certificate", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies if a RS256 JWT signature is valid.", + "introduced": "v0.17.0", + "result": { + "description": "`true` if the signature is valid, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "io.jwt.verify_rs384": { + "args": [ + { + "description": "JWT token whose signature is to be verified", + "name": "jwt", + "type": "string" + }, + { + "description": "PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature", + "name": "certificate", + "type": "string" + } + ], + "available": [ + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies if a RS384 JWT signature is valid.", + "introduced": "v0.20.0", + "result": { + "description": "`true` if the signature is valid, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "io.jwt.verify_rs512": { + "args": [ + { + "description": "JWT token whose signature is to be verified", + "name": "jwt", + "type": "string" + }, + { + "description": "PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature", + "name": "certificate", + "type": "string" + } + ], + "available": [ + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies if a RS512 JWT signature is valid.", + "introduced": "v0.20.0", + "result": { + "description": "`true` if the signature is valid, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "is_array": { + "args": [ + { + "description": "input value", + "name": "x", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns `true` if the input value is an array.", + "introduced": "v0.17.0", + "result": { + "description": "`true` if `x` is an array, `false` otherwise.", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "is_boolean": { + "args": [ + { + "description": "input value", + "name": "x", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns `true` if the input value is a boolean.", + "introduced": "v0.17.0", + "result": { + "description": "`true` if `x` is an boolean, `false` otherwise.", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "is_null": { + "args": [ + { + "description": "input value", + "name": "x", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns `true` if the input value is null.", + "introduced": "v0.17.0", + "result": { + "description": "`true` if `x` is null, `false` otherwise.", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "is_number": { + "args": [ + { + "description": "input value", + "name": "x", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns `true` if the input value is a number.", + "introduced": "v0.17.0", + "result": { + "description": "`true` if `x` is a number, `false` otherwise.", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "is_object": { + "args": [ + { + "description": "input value", + "name": "x", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns true if the input value is an object", + "introduced": "v0.17.0", + "result": { + "description": "`true` if `x` is an object, `false` otherwise.", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "is_set": { + "args": [ + { + "description": "input value", + "name": "x", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns `true` if the input value is a set.", + "introduced": "v0.17.0", + "result": { + "description": "`true` if `x` is a set, `false` otherwise.", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "is_string": { + "args": [ + { + "description": "input value", + "name": "x", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns `true` if the input value is a string.", + "introduced": "v0.17.0", + "result": { + "description": "`true` if `x` is a string, `false` otherwise.", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "json.filter": { + "args": [ + { + "description": "object to filter", + "name": "object", + "type": "object[any: any]" + }, + { + "description": "JSON string paths", + "name": "paths", + "type": "any\u003carray[any\u003cstring, array[any]\u003e], set[any\u003cstring, array[any]\u003e]\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Filters the object. For example: `json.filter({\"a\": {\"b\": \"x\", \"c\": \"y\"}}, [\"a/b\"])` will result in `{\"a\": {\"b\": \"x\"}}`). Paths are not filtered in-order and are deduplicated before being evaluated.", + "introduced": "v0.17.0", + "result": { + "description": "remaining data from `object` with only keys specified in `paths`", + "name": "filtered", + "type": "any" + }, + "wasm": true + }, + "json.is_valid": { + "args": [ + { + "description": "a JSON string", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies the input string is a valid JSON document.", + "introduced": "v0.25.0-rc1", + "result": { + "description": "`true` if `x` is valid JSON, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "json.marshal": { + "args": [ + { + "description": "the term to serialize", + "name": "x", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Serializes the input term to JSON.", + "introduced": "v0.17.0", + "result": { + "description": "the JSON string representation of `x`", + "name": "y", + "type": "string" + }, + "wasm": true + }, + "json.marshal_with_options": { + "args": [ + { + "description": "the term to serialize", + "name": "x", + "type": "any" + }, + { + "description": "encoding options", + "name": "opts", + "type": "object\u003cindent: string, prefix: string, pretty: boolean\u003e[string: any]" + } + ], + "available": [ + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Serializes the input term JSON, with additional formatting options via the `opts` parameter. `opts` accepts keys `pretty` (enable multi-line/formatted JSON), `prefix` (string to prefix lines with, default empty string) and `indent` (string to indent with, default `\\t`).", + "introduced": "v0.64.0", + "result": { + "description": "the JSON string representation of `x`, with configured prefix/indent string(s) as appropriate", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "json.match_schema": { + "args": [ + { + "description": "document to verify by schema", + "name": "document", + "type": "any\u003cstring, object[any: any]\u003e" + }, + { + "description": "schema to verify document by", + "name": "schema", + "type": "any\u003cstring, object[any: any]\u003e" + } + ], + "available": [ + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Checks that the document matches the JSON schema.", + "introduced": "v0.50.0", + "result": { + "description": "`output` is of the form `[match, errors]`. If the document is valid given the schema, then `match` is `true`, and `errors` is an empty array. Otherwise, `match` is `false` and `errors` is an array of objects describing the error(s).", + "name": "output", + "type": "array\u003cboolean, array[object\u003cdesc: string, error: string, field: string, type: string\u003e]\u003e" + }, + "wasm": false + }, + "json.patch": { + "args": [ + { + "description": "the object to patch", + "name": "object", + "type": "any" + }, + { + "description": "the JSON patches to apply", + "name": "patches", + "type": "array[object\u003cop: string, path: any\u003e[any: any]]" + } + ], + "available": [ + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Patches an object according to RFC6902. For example: `json.patch({\"a\": {\"foo\": 1}}, [{\"op\": \"add\", \"path\": \"/a/bar\", \"value\": 2}])` results in `{\"a\": {\"foo\": 1, \"bar\": 2}`. The patches are applied atomically: if any of them fails, the result will be undefined. Additionally works on sets, where a value contained in the set is considered to be its path.", + "introduced": "v0.25.0", + "result": { + "description": "result obtained after consecutively applying all patch operations in `patches`", + "name": "output", + "type": "any" + }, + "wasm": false + }, + "json.remove": { + "args": [ + { + "description": "object to remove paths from", + "name": "object", + "type": "object[any: any]" + }, + { + "description": "JSON string paths", + "name": "paths", + "type": "any\u003carray[any\u003cstring, array[any]\u003e], set[any\u003cstring, array[any]\u003e]\u003e" + } + ], + "available": [ + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Removes paths from an object. For example: `json.remove({\"a\": {\"b\": \"x\", \"c\": \"y\"}}, [\"a/b\"])` will result in `{\"a\": {\"c\": \"y\"}}`. Paths are not removed in-order and are deduplicated before being evaluated.", + "introduced": "v0.18.0", + "result": { + "description": "result of removing all keys specified in `paths`", + "name": "output", + "type": "any" + }, + "wasm": true + }, + "json.unmarshal": { + "args": [ + { + "description": "a JSON string", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Deserializes the input string.", + "introduced": "v0.17.0", + "result": { + "description": "the term deserialized from `x`", + "name": "y", + "type": "any" + }, + "wasm": true + }, + "json.verify_schema": { + "args": [ + { + "description": "the schema to verify", + "name": "schema", + "type": "any\u003cstring, object[any: any]\u003e" + } + ], + "available": [ + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Checks that the input is a valid JSON schema object. The schema can be either a JSON string or an JSON object.", + "introduced": "v0.50.0", + "result": { + "description": "`output` is of the form `[valid, error]`. If the schema is valid, then `valid` is `true`, and `error` is `null`. Otherwise, `valid` is `false` and `error` is a string describing the error.", + "name": "output", + "type": "array\u003cboolean, any\u003cnull, string\u003e\u003e" + }, + "wasm": false + }, + "lower": { + "args": [ + { + "description": "string that is converted to lower-case", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the input string but with all characters in lower-case.", + "introduced": "v0.17.0", + "result": { + "description": "lower-case of x", + "name": "y", + "type": "string" + }, + "wasm": true + }, + "lt": { + "args": [ + { + "name": "x", + "type": "any" + }, + { + "name": "y", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "infix": "\u003c", + "introduced": "v0.17.0", + "result": { + "description": "true if `x` is less than `y`; false otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "lte": { + "args": [ + { + "name": "x", + "type": "any" + }, + { + "name": "y", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "infix": "\u003c=", + "introduced": "v0.17.0", + "result": { + "description": "true if `x` is less than or equal to `y`; false otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "max": { + "args": [ + { + "description": "the set or array to be searched", + "name": "collection", + "type": "any\u003carray[any], set[any]\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the maximum value in a collection.", + "introduced": "v0.17.0", + "result": { + "description": "the maximum of all elements", + "name": "n", + "type": "any" + }, + "wasm": true + }, + "min": { + "args": [ + { + "description": "the set or array to be searched", + "name": "collection", + "type": "any\u003carray[any], set[any]\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the minimum value in a collection.", + "introduced": "v0.17.0", + "result": { + "description": "the minimum of all elements", + "name": "n", + "type": "any" + }, + "wasm": true + }, + "minus": { + "args": [ + { + "name": "x", + "type": "any\u003cnumber, set[any]\u003e" + }, + { + "name": "y", + "type": "any\u003cnumber, set[any]\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Minus subtracts the second number from the first number or computes the difference between two sets.", + "infix": "-", + "introduced": "v0.17.0", + "result": { + "description": "the difference of `x` and `y`", + "name": "z", + "type": "any\u003cnumber, set[any]\u003e" + }, + "wasm": true + }, + "mul": { + "args": [ + { + "name": "x", + "type": "number" + }, + { + "name": "y", + "type": "number" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Multiplies two numbers.", + "infix": "*", + "introduced": "v0.17.0", + "result": { + "description": "the product of `x` and `y`", + "name": "z", + "type": "number" + }, + "wasm": true + }, + "neq": { + "args": [ + { + "name": "x", + "type": "any" + }, + { + "name": "y", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "infix": "!=", + "introduced": "v0.17.0", + "result": { + "description": "true if `x` is not equal to `y`; false otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "net.cidr_contains": { + "args": [ + { + "description": "CIDR to check against", + "name": "cidr", + "type": "string" + }, + { + "description": "CIDR or IP to check", + "name": "cidr_or_ip", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Checks if a CIDR or IP is contained within another CIDR. `output` is `true` if `cidr_or_ip` (e.g. `127.0.0.64/26` or `127.0.0.1`) is contained within `cidr` (e.g. `127.0.0.1/24`) and `false` otherwise. Supports both IPv4 and IPv6 notations.", + "introduced": "v0.17.0", + "result": { + "description": "`true` if `cidr_or_ip` is contained within `cidr`", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "net.cidr_contains_matches": { + "args": [ + { + "description": "CIDRs to check against", + "name": "cidrs", + "type": "any\u003cstring, array[any\u003cstring, array[any]\u003e], object[string: any\u003cstring, array[any]\u003e], set[any\u003cstring, array[any]\u003e]\u003e" + }, + { + "description": "CIDRs or IPs to check", + "name": "cidrs_or_ips", + "type": "any\u003cstring, array[any\u003cstring, array[any]\u003e], object[string: any\u003cstring, array[any]\u003e], set[any\u003cstring, array[any]\u003e]\u003e" + } + ], + "available": [ + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Checks if collections of cidrs or ips are contained within another collection of cidrs and returns matches. This function is similar to `net.cidr_contains` except it allows callers to pass collections of CIDRs or IPs as arguments and returns the matches (as opposed to a boolean result indicating a match between two CIDRs/IPs).", + "introduced": "v0.19.0-rc1", + "result": { + "description": "tuples identifying matches where `cidrs_or_ips` are contained within `cidrs`", + "name": "output", + "type": "set[array\u003cany, any\u003e]" + }, + "wasm": false + }, + "net.cidr_expand": { + "args": [ + { + "description": "CIDR to expand", + "name": "cidr", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Expands CIDR to set of hosts (e.g., `net.cidr_expand(\"192.168.0.0/30\")` generates 4 hosts: `{\"192.168.0.0\", \"192.168.0.1\", \"192.168.0.2\", \"192.168.0.3\"}`).", + "introduced": "v0.17.0", + "result": { + "description": "set of IP addresses the CIDR `cidr` expands to", + "name": "hosts", + "type": "set[string]" + }, + "wasm": false + }, + "net.cidr_intersects": { + "args": [ + { + "description": "first CIDR", + "name": "cidr1", + "type": "string" + }, + { + "description": "second CIDR", + "name": "cidr2", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Checks if a CIDR intersects with another CIDR (e.g. `192.168.0.0/16` overlaps with `192.168.1.0/24`). Supports both IPv4 and IPv6 notations.", + "introduced": "v0.17.0", + "result": { + "description": "`true` if `cidr1` intersects with `cidr2`", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "net.cidr_is_valid": { + "args": [ + { + "description": "CIDR to validate", + "name": "cidr", + "type": "string" + } + ], + "available": [ + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Parses an IPv4/IPv6 CIDR and returns a boolean indicating if the provided CIDR is valid.", + "introduced": "v0.46.0", + "result": { + "description": "`true` if `cidr` is a valid CIDR", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "net.cidr_merge": { + "args": [ + { + "description": "CIDRs or IP addresses", + "name": "addrs", + "type": "any\u003carray[any\u003cstring\u003e], set[string]\u003e" + } + ], + "available": [ + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Merges IP addresses and subnets into the smallest possible list of CIDRs (e.g., `net.cidr_merge([\"192.0.128.0/24\", \"192.0.129.0/24\"])` generates `{\"192.0.128.0/23\"}`.This function merges adjacent subnets where possible, those contained within others and also removes any duplicates.\nSupports both IPv4 and IPv6 notations. IPv6 inputs need a prefix length (e.g. \"/128\").", + "introduced": "v0.24.0", + "result": { + "description": "smallest possible set of CIDRs obtained after merging the provided list of IP addresses and subnets in `addrs`", + "name": "output", + "type": "set[string]" + }, + "wasm": false + }, + "net.cidr_overlap": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "deprecated": true, + "introduced": "v0.17.0", + "result": { + "type": "boolean" + }, + "wasm": true + }, + "net.lookup_ip_addr": { + "args": [ + { + "description": "domain name to resolve", + "name": "name", + "type": "string" + } + ], + "available": [ + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the set of IP addresses (both v4 and v6) that the passed-in `name` resolves to using the standard name resolution mechanisms available.", + "introduced": "v0.35.0", + "result": { + "description": "IP addresses (v4 and v6) that `name` resolves to", + "name": "addrs", + "type": "set[string]" + }, + "wasm": false + }, + "numbers.range": { + "args": [ + { + "description": "the start of the range", + "name": "a", + "type": "number" + }, + { + "description": "the end of the range (inclusive)", + "name": "b", + "type": "number" + } + ], + "available": [ + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns an array of numbers in the given (inclusive) range. If `a==b`, then `range == [a]`; if `a \u003e b`, then `range` is in descending order.", + "introduced": "v0.22.0", + "result": { + "description": "the range between `a` and `b`", + "name": "range", + "type": "array[number]" + }, + "wasm": true + }, + "numbers.range_step": { + "args": [ + { + "description": "the start of the range", + "name": "a", + "type": "number" + }, + { + "description": "the end of the range (inclusive)", + "name": "b", + "type": "number" + }, + { + "description": "the step between numbers in the range", + "name": "step", + "type": "number" + } + ], + "available": [ + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns an array of numbers in the given (inclusive) range incremented by a positive step.\n\tIf \"a==b\", then \"range == [a]\"; if \"a \u003e b\", then \"range\" is in descending order.\n\tIf the provided \"step\" is less then 1, an error will be thrown.\n\tIf \"b\" is not in the range of the provided \"step\", \"b\" won't be included in the result.\n\t", + "introduced": "v0.56.0", + "result": { + "description": "the range between `a` and `b` in `step` increments", + "name": "range", + "type": "array[number]" + }, + "wasm": false + }, + "object.filter": { + "args": [ + { + "description": "object to filter keys", + "name": "object", + "type": "object[any: any]" + }, + { + "description": "keys to keep in `object`", + "name": "keys", + "type": "any\u003carray[any], object[any: any], set[any]\u003e" + } + ], + "available": [ + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Filters the object by keeping only specified keys. For example: `object.filter({\"a\": {\"b\": \"x\", \"c\": \"y\"}, \"d\": \"z\"}, [\"a\"])` will result in `{\"a\": {\"b\": \"x\", \"c\": \"y\"}}`).", + "introduced": "v0.17.2", + "result": { + "description": "remaining data from `object` with only keys specified in `keys`", + "name": "filtered", + "type": "any" + }, + "wasm": true + }, + "object.get": { + "args": [ + { + "description": "object to get `key` from", + "name": "object", + "type": "object[any: any]" + }, + { + "description": "key to lookup in `object`", + "name": "key", + "type": "any" + }, + { + "description": "default to use when lookup fails", + "name": "default", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns value of an object's key if present, otherwise a default. If the supplied `key` is an `array`, then `object.get` will search through a nested object or array using each key in turn. For example: `object.get({\"a\": [{ \"b\": true }]}, [\"a\", 0, \"b\"], false)` results in `true`.", + "introduced": "v0.17.0", + "result": { + "description": "`object[key]` if present, otherwise `default`", + "name": "value", + "type": "any" + }, + "wasm": true + }, + "object.keys": { + "args": [ + { + "description": "object to get keys from", + "name": "object", + "type": "object[any: any]" + } + ], + "available": [ + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a set of an object's keys. For example: `object.keys({\"a\": 1, \"b\": true, \"c\": \"d\")` results in `{\"a\", \"b\", \"c\"}`.", + "introduced": "v0.47.0", + "result": { + "description": "set of `object`'s keys", + "name": "value", + "type": "set[any]" + }, + "wasm": true + }, + "object.remove": { + "args": [ + { + "description": "object to remove keys from", + "name": "object", + "type": "object[any: any]" + }, + { + "description": "keys to remove from x", + "name": "keys", + "type": "any\u003carray[any], object[any: any], set[any]\u003e" + } + ], + "available": [ + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Removes specified keys from an object.", + "introduced": "v0.17.2", + "result": { + "description": "result of removing the specified `keys` from `object`", + "name": "output", + "type": "any" + }, + "wasm": true + }, + "object.subset": { + "args": [ + { + "description": "object to test if sub is a subset of", + "name": "super", + "type": "any\u003carray[any], object[any: any], set[any]\u003e" + }, + { + "description": "object to test if super is a superset of", + "name": "sub", + "type": "any\u003carray[any], object[any: any], set[any]\u003e" + } + ], + "available": [ + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Determines if an object `sub` is a subset of another object `super`.Object `sub` is a subset of object `super` if and only if every key in `sub` is also in `super`, **and** for all keys which `sub` and `super` share, they have the same value. This function works with objects, sets, arrays and a set of array and set.If both arguments are objects, then the operation is recursive, e.g. `{\"c\": {\"x\": {10, 15, 20}}` is a subset of `{\"a\": \"b\", \"c\": {\"x\": {10, 15, 20, 25}, \"y\": \"z\"}`. If both arguments are sets, then this function checks if every element of `sub` is a member of `super`, but does not attempt to recurse. If both arguments are arrays, then this function checks if `sub` appears contiguously in order within `super`, and also does not attempt to recurse. If `super` is array and `sub` is set, then this function checks if `super` contains every element of `sub` with no consideration of ordering, and also does not attempt to recurse.", + "introduced": "v0.42.0", + "result": { + "description": "`true` if `sub` is a subset of `super`", + "name": "result", + "type": "any" + }, + "wasm": false + }, + "object.union": { + "args": [ + { + "description": "left-hand object", + "name": "a", + "type": "object[any: any]" + }, + { + "description": "right-hand object", + "name": "b", + "type": "object[any: any]" + } + ], + "available": [ + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Creates a new object of the asymmetric union of two objects. For example: `object.union({\"a\": 1, \"b\": 2, \"c\": {\"d\": 3}}, {\"a\": 7, \"c\": {\"d\": 4, \"e\": 5}})` will result in `{\"a\": 7, \"b\": 2, \"c\": {\"d\": 4, \"e\": 5}}`.", + "introduced": "v0.17.2", + "result": { + "description": "a new object which is the result of an asymmetric recursive union of two objects where conflicts are resolved by choosing the key from the right-hand object `b`", + "name": "output", + "type": "any" + }, + "wasm": true + }, + "object.union_n": { + "args": [ + { + "description": "list of objects to merge", + "name": "objects", + "type": "array[object[any: any]]" + } + ], + "available": [ + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Creates a new object that is the asymmetric union of all objects merged from left to right. For example: `object.union_n([{\"a\": 1}, {\"b\": 2}, {\"a\": 3}])` will result in `{\"b\": 2, \"a\": 3}`.", + "introduced": "v0.37.0", + "result": { + "description": "asymmetric recursive union of all objects in `objects`, merged from left to right, where conflicts are resolved by choosing the key from the right-hand object", + "name": "output", + "type": "any" + }, + "wasm": true + }, + "opa.runtime": { + "args": [], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns an object that describes the runtime environment where OPA is deployed.", + "introduced": "v0.17.0", + "result": { + "description": "includes a `config` key if OPA was started with a configuration file; an `env` key containing the environment variables that the OPA process was started with; includes `version` and `commit` keys containing the version and build commit of OPA.", + "name": "output", + "type": "object[string: any]" + }, + "wasm": false + }, + "or": { + "args": [ + { + "name": "x", + "type": "set[any]" + }, + { + "name": "y", + "type": "set[any]" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the union of two sets.", + "infix": "|", + "introduced": "v0.17.0", + "result": { + "description": "the union of `x` and `y`", + "name": "z", + "type": "set[any]" + }, + "wasm": true + }, + "plus": { + "args": [ + { + "name": "x", + "type": "number" + }, + { + "name": "y", + "type": "number" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Plus adds two numbers together.", + "infix": "+", + "introduced": "v0.17.0", + "result": { + "description": "the sum of `x` and `y`", + "name": "z", + "type": "number" + }, + "wasm": true + }, + "print": { + "args": [], + "available": [ + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "introduced": "v0.34.0", + "result": {}, + "wasm": false + }, + "product": { + "args": [ + { + "description": "the set or array of numbers to multiply", + "name": "collection", + "type": "any\u003carray[number], set[number]\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Multiplies elements of an array or set of numbers", + "introduced": "v0.17.0", + "result": { + "description": "the product of all elements", + "name": "n", + "type": "number" + }, + "wasm": true + }, + "providers.aws.sign_req": { + "args": [ + { + "description": "HTTP request object", + "name": "request", + "type": "object[string: any]" + }, + { + "description": "AWS configuration object", + "name": "aws_config", + "type": "object[string: any]" + }, + { + "description": "nanoseconds since the epoch", + "name": "time_ns", + "type": "number" + } + ], + "available": [ + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Signs an HTTP request object for Amazon Web Services. Currently implements [AWS Signature Version 4 request signing](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html) by the `Authorization` header method.", + "introduced": "v0.47.0", + "result": { + "description": "HTTP request object with `Authorization` header", + "name": "signed_request", + "type": "object[any: any]" + }, + "wasm": false + }, + "rand.intn": { + "args": [ + { + "description": "seed string for the random number", + "name": "str", + "type": "string" + }, + { + "description": "upper bound of the random number (exclusive)", + "name": "n", + "type": "number" + } + ], + "available": [ + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a random integer between `0` and `n` (`n` exclusive). If `n` is `0`, then `y` is always `0`. For any given argument pair (`str`, `n`), the output will be consistent throughout a query evaluation.", + "introduced": "v0.31.0", + "result": { + "description": "random integer in the range `[0, abs(n))`", + "name": "y", + "type": "number" + }, + "wasm": false + }, + "re_match": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "deprecated": true, + "introduced": "v0.17.0", + "result": { + "type": "boolean" + }, + "wasm": true + }, + "regex.find_all_string_submatch_n": { + "args": [ + { + "description": "regular expression", + "name": "pattern", + "type": "string" + }, + { + "description": "string to match", + "name": "value", + "type": "string" + }, + { + "description": "number of matches to return; `-1` means all matches", + "name": "number", + "type": "number" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns all successive matches of the expression.", + "introduced": "v0.17.0", + "result": { + "description": "array of all matches", + "name": "output", + "type": "array[array[string]]" + }, + "wasm": true + }, + "regex.find_n": { + "args": [ + { + "description": "regular expression", + "name": "pattern", + "type": "string" + }, + { + "description": "string to match", + "name": "value", + "type": "string" + }, + { + "description": "number of matches to return, if `-1`, returns all matches", + "name": "number", + "type": "number" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the specified number of matches when matching the input against the pattern.", + "introduced": "v0.17.0", + "result": { + "description": "collected matches", + "name": "output", + "type": "array[string]" + }, + "wasm": false + }, + "regex.globs_match": { + "args": [ + { + "description": "first glob-style regular expression", + "name": "glob1", + "type": "string" + }, + { + "description": "second glob-style regular expression", + "name": "glob2", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Checks if the intersection of two glob-style regular expressions matches a non-empty set of non-empty strings.\nThe set of regex symbols is limited for this builtin: only `.`, `*`, `+`, `[`, `-`, `]` and `\\` are treated as special symbols.", + "introduced": "v0.17.0", + "result": { + "description": "true if the intersection of `glob1` and `glob2` matches a non-empty set of non-empty strings", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "regex.is_valid": { + "args": [ + { + "description": "regular expression", + "name": "pattern", + "type": "string" + } + ], + "available": [ + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Checks if a string is a valid regular expression: the detailed syntax for patterns is defined by https://github.com/google/re2/wiki/Syntax.", + "introduced": "v0.23.0", + "result": { + "description": "true if `pattern` is a valid regular expression", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "regex.match": { + "args": [ + { + "description": "regular expression", + "name": "pattern", + "type": "string" + }, + { + "description": "value to match against `pattern`", + "name": "value", + "type": "string" + } + ], + "available": [ + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Matches a string against a regular expression.", + "introduced": "v0.23.0", + "result": { + "description": "true if `value` matches `pattern`", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "regex.replace": { + "args": [ + { + "description": "string being processed", + "name": "s", + "type": "string" + }, + { + "description": "regex pattern to be applied", + "name": "pattern", + "type": "string" + }, + { + "description": "regex value", + "name": "value", + "type": "string" + } + ], + "available": [ + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Find and replaces the text using the regular expression pattern.", + "introduced": "v0.45.0", + "result": { + "description": "string with replaced substrings", + "name": "output", + "type": "string" + }, + "wasm": false + }, + "regex.split": { + "args": [ + { + "description": "regular expression", + "name": "pattern", + "type": "string" + }, + { + "description": "string to match", + "name": "value", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Splits the input string by the occurrences of the given pattern.", + "introduced": "v0.17.0", + "result": { + "description": "the parts obtained by splitting `value`", + "name": "output", + "type": "array[string]" + }, + "wasm": false + }, + "regex.template_match": { + "args": [ + { + "description": "template expression containing `0..n` regular expressions", + "name": "template", + "type": "string" + }, + { + "description": "string to match", + "name": "value", + "type": "string" + }, + { + "description": "start delimiter of the regular expression in `template`", + "name": "delimiter_start", + "type": "string" + }, + { + "description": "end delimiter of the regular expression in `template`", + "name": "delimiter_end", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Matches a string against a pattern, where there pattern may be glob-like", + "introduced": "v0.17.0", + "result": { + "description": "true if `value` matches the `template`", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "rego.metadata.chain": { + "args": [], + "available": [ + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the chain of metadata for the active rule.\nOrdered starting at the active rule, going outward to the most distant node in its package ancestry.\nA chain entry is a JSON document with two members: \"path\", an array representing the path of the node; and \"annotations\", a JSON document containing the annotations declared for the node.\nThe first entry in the chain always points to the active rule, even if it has no declared annotations (in which case the \"annotations\" member is not present).", + "introduced": "v0.40.0", + "result": { + "description": "each array entry represents a node in the path ancestry (chain) of the active rule that also has declared annotations", + "name": "chain", + "type": "array[any]" + }, + "wasm": false + }, + "rego.metadata.rule": { + "args": [], + "available": [ + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns annotations declared for the active rule and using the _rule_ scope.", + "introduced": "v0.40.0", + "result": { + "description": "\"rule\" scope annotations for this rule; empty object if no annotations exist", + "name": "output", + "type": "any" + }, + "wasm": false + }, + "rego.parse_module": { + "args": [ + { + "description": "file name to attach to AST nodes' locations", + "name": "filename", + "type": "string" + }, + { + "description": "Rego module", + "name": "rego", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Parses the input Rego string and returns an object representation of the AST.", + "introduced": "v0.17.0", + "result": { + "description": "AST object for the Rego module", + "name": "output", + "type": "object[string: any]" + }, + "wasm": false + }, + "rem": { + "args": [ + { + "name": "x", + "type": "number" + }, + { + "name": "y", + "type": "number" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the remainder for of `x` divided by `y`, for `y != 0`.", + "infix": "%", + "introduced": "v0.17.0", + "result": { + "description": "the remainder", + "name": "z", + "type": "number" + }, + "wasm": true + }, + "replace": { + "args": [ + { + "description": "string being processed", + "name": "x", + "type": "string" + }, + { + "description": "substring to replace", + "name": "old", + "type": "string" + }, + { + "description": "string to replace `old` with", + "name": "new", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Replace replaces all instances of a sub-string.", + "introduced": "v0.17.0", + "result": { + "description": "string with replaced substrings", + "name": "y", + "type": "string" + }, + "wasm": true + }, + "round": { + "args": [ + { + "description": "the number to round", + "name": "x", + "type": "number" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Rounds the number to the nearest integer.", + "introduced": "v0.17.0", + "result": { + "description": "the result of rounding `x`", + "name": "y", + "type": "number" + }, + "wasm": true + }, + "semver.compare": { + "args": [ + { + "description": "first version string", + "name": "a", + "type": "string" + }, + { + "description": "second version string", + "name": "b", + "type": "string" + } + ], + "available": [ + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Compares valid SemVer formatted version strings.", + "introduced": "v0.22.0", + "result": { + "description": "`-1` if `a \u003c b`; `1` if `a \u003e b`; `0` if `a == b`", + "name": "result", + "type": "number" + }, + "wasm": false + }, + "semver.is_valid": { + "args": [ + { + "description": "input to validate", + "name": "vsn", + "type": "any" + } + ], + "available": [ + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Validates that the input is a valid SemVer string.", + "introduced": "v0.22.0", + "result": { + "description": "`true` if `vsn` is a valid SemVer; `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "set_diff": { + "args": [ + { + "type": "set[any]" + }, + { + "type": "set[any]" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "deprecated": true, + "introduced": "v0.17.0", + "result": { + "type": "set[any]" + }, + "wasm": true + }, + "sort": { + "args": [ + { + "description": "the array or set to be sorted", + "name": "collection", + "type": "any\u003carray[any], set[any]\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a sorted array.", + "introduced": "v0.17.0", + "result": { + "description": "the sorted array", + "name": "n", + "type": "array[any]" + }, + "wasm": true + }, + "split": { + "args": [ + { + "description": "string that is split", + "name": "x", + "type": "string" + }, + { + "description": "delimiter used for splitting", + "name": "delimiter", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Split returns an array containing elements of the input string split on a delimiter.", + "introduced": "v0.17.0", + "result": { + "description": "split parts", + "name": "ys", + "type": "array[string]" + }, + "wasm": true + }, + "sprintf": { + "args": [ + { + "description": "string with formatting verbs", + "name": "format", + "type": "string" + }, + { + "description": "arguments to format into formatting verbs", + "name": "values", + "type": "array[any]" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the given string, formatted.", + "introduced": "v0.17.0", + "result": { + "description": "`format` formatted by the values in `values`", + "name": "output", + "type": "string" + }, + "wasm": false + }, + "startswith": { + "args": [ + { + "description": "search string", + "name": "search", + "type": "string" + }, + { + "description": "base string", + "name": "base", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns true if the search string begins with the base string.", + "introduced": "v0.17.0", + "result": { + "description": "result of the prefix check", + "name": "result", + "type": "boolean" + }, + "wasm": true + }, + "strings.any_prefix_match": { + "args": [ + { + "description": "search string(s)", + "name": "search", + "type": "any\u003cstring, array[string], set[string]\u003e" + }, + { + "description": "base string(s)", + "name": "base", + "type": "any\u003cstring, array[string], set[string]\u003e" + } + ], + "available": [ + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns true if any of the search strings begins with any of the base strings.", + "introduced": "v0.44.0", + "result": { + "description": "result of the prefix check", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "strings.any_suffix_match": { + "args": [ + { + "description": "search string(s)", + "name": "search", + "type": "any\u003cstring, array[string], set[string]\u003e" + }, + { + "description": "base string(s)", + "name": "base", + "type": "any\u003cstring, array[string], set[string]\u003e" + } + ], + "available": [ + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns true if any of the search strings ends with any of the base strings.", + "introduced": "v0.44.0", + "result": { + "description": "result of the suffix check", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "strings.count": { + "args": [ + { + "description": "string to search in", + "name": "search", + "type": "string" + }, + { + "description": "substring to look for", + "name": "substring", + "type": "string" + } + ], + "available": [ + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the number of non-overlapping instances of a substring in a string.", + "introduced": "v0.67.0", + "result": { + "description": "count of occurrences, `0` if not found", + "name": "output", + "type": "number" + }, + "wasm": false + }, + "strings.render_template": { + "args": [ + { + "description": "a templated string", + "name": "value", + "type": "string" + }, + { + "description": "a mapping of template variable keys to values", + "name": "vars", + "type": "object[string: any]" + } + ], + "available": [ + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Renders a templated string with given template variables injected. For a given templated string and key/value mapping, values will be injected into the template where they are referenced by key.\n\tFor examples of templating syntax, see https://pkg.go.dev/text/template", + "introduced": "v0.59.0", + "result": { + "description": "rendered template with template variables injected", + "name": "result", + "type": "string" + }, + "wasm": false + }, + "strings.replace_n": { + "args": [ + { + "description": "replacement pairs", + "name": "patterns", + "type": "object[string: string]" + }, + { + "description": "string to replace substring matches in", + "name": "value", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Replaces a string from a list of old, new string pairs.\nReplacements are performed in the order they appear in the target string, without overlapping matches.\nThe old string comparisons are done in argument order.", + "introduced": "v0.17.0", + "result": { + "description": "string with replaced substrings", + "name": "output", + "type": "string" + }, + "wasm": true + }, + "strings.reverse": { + "args": [ + { + "description": "string to reverse", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Reverses a given string.", + "introduced": "v0.36.0", + "result": { + "description": "reversed string", + "name": "y", + "type": "string" + }, + "wasm": true + }, + "substring": { + "args": [ + { + "description": "string to extract substring from", + "name": "value", + "type": "string" + }, + { + "description": "offset, must be positive", + "name": "offset", + "type": "number" + }, + { + "description": "length of the substring starting from `offset`", + "name": "length", + "type": "number" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the portion of a string for a given `offset` and a `length`. If `length \u003c 0`, `output` is the remainder of the string.", + "introduced": "v0.17.0", + "result": { + "description": "substring of `value` from `offset`, of length `length`", + "name": "output", + "type": "string" + }, + "wasm": true + }, + "sum": { + "args": [ + { + "description": "the set or array of numbers to sum", + "name": "collection", + "type": "any\u003carray[number], set[number]\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Sums elements of an array or set of numbers.", + "introduced": "v0.17.0", + "result": { + "description": "the sum of all elements", + "name": "n", + "type": "number" + }, + "wasm": true + }, + "time.add_date": { + "args": [ + { + "description": "nanoseconds since the epoch", + "name": "ns", + "type": "number" + }, + { + "description": "number of years to add", + "name": "years", + "type": "number" + }, + { + "description": "number of months to add", + "name": "months", + "type": "number" + }, + { + "description": "number of days to add", + "name": "days", + "type": "number" + } + ], + "available": [ + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the nanoseconds since epoch after adding years, months and days to nanoseconds. Month \u0026 day values outside their usual ranges after the operation and will be normalized - for example, October 32 would become November 1. `undefined` if the result would be outside the valid time range that can fit within an `int64`.", + "introduced": "v0.19.0", + "result": { + "description": "nanoseconds since the epoch representing the input time, with years, months and days added", + "name": "output", + "type": "number" + }, + "wasm": false + }, + "time.clock": { + "args": [ + { + "description": "a number representing the nanoseconds since the epoch (UTC); or a two-element array of the nanoseconds, and a timezone string", + "name": "x", + "type": "any\u003cnumber, array\u003cnumber, string\u003e\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the `[hour, minute, second]` of the day for the nanoseconds since epoch.", + "introduced": "v0.17.0", + "result": { + "description": "the `hour`, `minute` (0-59), and `second` (0-59) representing the time of day for the nanoseconds since epoch in the supplied timezone (or UTC)", + "name": "output", + "type": "array\u003cnumber, number, number\u003e" + }, + "wasm": false + }, + "time.date": { + "args": [ + { + "description": "a number representing the nanoseconds since the epoch (UTC); or a two-element array of the nanoseconds, and a timezone string", + "name": "x", + "type": "any\u003cnumber, array\u003cnumber, string\u003e\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the `[year, month, day]` for the nanoseconds since epoch.", + "introduced": "v0.17.0", + "result": { + "description": "an array of `year`, `month` (1-12), and `day` (1-31)", + "name": "date", + "type": "array\u003cnumber, number, number\u003e" + }, + "wasm": false + }, + "time.diff": { + "args": [ + { + "description": "nanoseconds since the epoch; or a two-element array of the nanoseconds, and a timezone string", + "name": "ns1", + "type": "any\u003cnumber, array\u003cnumber, string\u003e\u003e" + }, + { + "description": "nanoseconds since the epoch; or a two-element array of the nanoseconds, and a timezone string", + "name": "ns2", + "type": "any\u003cnumber, array\u003cnumber, string\u003e\u003e" + } + ], + "available": [ + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the difference between two unix timestamps in nanoseconds (with optional timezone strings).", + "introduced": "v0.28.0", + "result": { + "description": "difference between `ns1` and `ns2` (in their supplied timezones, if supplied, or UTC) as array of numbers: `[years, months, days, hours, minutes, seconds]`", + "name": "output", + "type": "array\u003cnumber, number, number, number, number, number\u003e" + }, + "wasm": false + }, + "time.format": { + "args": [ + { + "description": "a number representing the nanoseconds since the epoch (UTC); or a two-element array of the nanoseconds, and a timezone string; or a three-element array of ns, timezone string and a layout string or golang defined formatting constant (see golang supported time formats)", + "name": "x", + "type": "any\u003cnumber, array\u003cnumber, string\u003e, array\u003cnumber, string, string\u003e\u003e" + } + ], + "available": [ + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the formatted timestamp for the nanoseconds since epoch.", + "introduced": "v0.48.0", + "result": { + "description": "the formatted timestamp represented for the nanoseconds since the epoch in the supplied timezone (or UTC)", + "name": "formatted timestamp", + "type": "string" + }, + "wasm": false + }, + "time.now_ns": { + "args": [], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the current time since epoch in nanoseconds.", + "introduced": "v0.17.0", + "result": { + "description": "nanoseconds since epoch", + "name": "now", + "type": "number" + }, + "wasm": false + }, + "time.parse_duration_ns": { + "args": [ + { + "description": "a duration like \"3m\"; see the [Go `time` package documentation](https://golang.org/pkg/time/#ParseDuration) for more details", + "name": "duration", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the duration in nanoseconds represented by a string.", + "introduced": "v0.17.0", + "result": { + "description": "the `duration` in nanoseconds", + "name": "ns", + "type": "number" + }, + "wasm": false + }, + "time.parse_ns": { + "args": [ + { + "description": "format used for parsing, see the [Go `time` package documentation](https://golang.org/pkg/time/#Parse) for more details", + "name": "layout", + "type": "string" + }, + { + "description": "input to parse according to `layout`", + "name": "value", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the time in nanoseconds parsed from the string in the given format. `undefined` if the result would be outside the valid time range that can fit within an `int64`.", + "introduced": "v0.17.0", + "result": { + "description": "`value` in nanoseconds since epoch", + "name": "ns", + "type": "number" + }, + "wasm": false + }, + "time.parse_rfc3339_ns": { + "args": [ + { + "description": "input string to parse in RFC3339 format", + "name": "value", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the time in nanoseconds parsed from the string in RFC3339 format. `undefined` if the result would be outside the valid time range that can fit within an `int64`.", + "introduced": "v0.17.0", + "result": { + "description": "`value` in nanoseconds since epoch", + "name": "ns", + "type": "number" + }, + "wasm": false + }, + "time.weekday": { + "args": [ + { + "description": "a number representing the nanoseconds since the epoch (UTC); or a two-element array of the nanoseconds, and a timezone string", + "name": "x", + "type": "any\u003cnumber, array\u003cnumber, string\u003e\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the day of the week (Monday, Tuesday, ...) for the nanoseconds since epoch.", + "introduced": "v0.17.0", + "result": { + "description": "the weekday represented by `ns` nanoseconds since the epoch in the supplied timezone (or UTC)", + "name": "day", + "type": "string" + }, + "wasm": false + }, + "to_number": { + "args": [ + { + "description": "value to convert", + "name": "x", + "type": "any\u003cnull, boolean, number, string\u003e" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Converts a string, bool, or number value to a number: Strings are converted to numbers using `strconv.Atoi`, Boolean `false` is converted to 0 and `true` is converted to 1.", + "introduced": "v0.17.0", + "result": { + "description": "the numeric representation of `x`", + "name": "num", + "type": "number" + }, + "wasm": true + }, + "trace": { + "args": [ + { + "description": "the note to include", + "name": "note", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Emits `note` as a `Note` event in the query explanation. Query explanations show the exact expressions evaluated by OPA during policy execution. For example, `trace(\"Hello There!\")` includes `Note \"Hello There!\"` in the query explanation. To include variables in the message, use `sprintf`. For example, `person := \"Bob\"; trace(sprintf(\"Hello There! %v\", [person]))` will emit `Note \"Hello There! Bob\"` inside of the explanation.", + "introduced": "v0.17.0", + "result": { + "description": "always `true`", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "trim": { + "args": [ + { + "description": "string to trim", + "name": "value", + "type": "string" + }, + { + "description": "string of characters that are cut off", + "name": "cutset", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns `value` with all leading or trailing instances of the `cutset` characters removed.", + "introduced": "v0.17.0", + "result": { + "description": "string trimmed of `cutset` characters", + "name": "output", + "type": "string" + }, + "wasm": true + }, + "trim_left": { + "args": [ + { + "description": "string to trim", + "name": "value", + "type": "string" + }, + { + "description": "string of characters that are cut off on the left", + "name": "cutset", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns `value` with all leading instances of the `cutset` characters removed.", + "introduced": "v0.17.0", + "result": { + "description": "string left-trimmed of `cutset` characters", + "name": "output", + "type": "string" + }, + "wasm": true + }, + "trim_prefix": { + "args": [ + { + "description": "string to trim", + "name": "value", + "type": "string" + }, + { + "description": "prefix to cut off", + "name": "prefix", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns `value` without the prefix. If `value` doesn't start with `prefix`, it is returned unchanged.", + "introduced": "v0.17.0", + "result": { + "description": "string with `prefix` cut off", + "name": "output", + "type": "string" + }, + "wasm": true + }, + "trim_right": { + "args": [ + { + "description": "string to trim", + "name": "value", + "type": "string" + }, + { + "description": "string of characters that are cut off on the right", + "name": "cutset", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns `value` with all trailing instances of the `cutset` characters removed.", + "introduced": "v0.17.0", + "result": { + "description": "string right-trimmed of `cutset` characters", + "name": "output", + "type": "string" + }, + "wasm": true + }, + "trim_space": { + "args": [ + { + "description": "string to trim", + "name": "value", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Return the given string with all leading and trailing white space removed.", + "introduced": "v0.17.0", + "result": { + "description": "string leading and trailing white space cut off", + "name": "output", + "type": "string" + }, + "wasm": true + }, + "trim_suffix": { + "args": [ + { + "description": "string to trim", + "name": "value", + "type": "string" + }, + { + "description": "suffix to cut off", + "name": "suffix", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns `value` without the suffix. If `value` doesn't end with `suffix`, it is returned unchanged.", + "introduced": "v0.17.0", + "result": { + "description": "string with `suffix` cut off", + "name": "output", + "type": "string" + }, + "wasm": true + }, + "type_name": { + "args": [ + { + "description": "input value", + "name": "x", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the type of its input value.", + "introduced": "v0.17.0", + "result": { + "description": "one of \"null\", \"boolean\", \"number\", \"string\", \"array\", \"object\", \"set\"", + "name": "type", + "type": "string" + }, + "wasm": true + }, + "union": { + "args": [ + { + "description": "set of sets to merge", + "name": "xs", + "type": "set[set[any]]" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the union of the given input sets.", + "introduced": "v0.17.0", + "result": { + "description": "the union of all `xs` sets", + "name": "y", + "type": "set[any]" + }, + "wasm": true + }, + "units.parse": { + "args": [ + { + "description": "the unit to parse", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Converts strings like \"10G\", \"5K\", \"4M\", \"1500m\", and the like into a number.\nThis number can be a non-integer, such as 1.5, 0.22, etc. Scientific notation is supported,\nallowing values such as \"1e-3K\" (1) or \"2.5e6M\" (2.5 million M).\n\nSupports standard metric decimal and binary SI units (e.g., K, Ki, M, Mi, G, Gi, etc.) where\nm, K, M, G, T, P, and E are treated as decimal units and Ki, Mi, Gi, Ti, Pi, and Ei are treated as\nbinary units.\n\nNote that 'm' and 'M' are case-sensitive to allow distinguishing between \"milli\" and \"mega\" units\nrespectively. Other units are case-insensitive.", + "introduced": "v0.41.0", + "result": { + "description": "the parsed number", + "name": "y", + "type": "number" + }, + "wasm": false + }, + "units.parse_bytes": { + "args": [ + { + "description": "the byte unit to parse", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Converts strings like \"10GB\", \"5K\", \"4mb\", or \"1e6KB\" into an integer number of bytes.\n\nSupports standard byte units (e.g., KB, KiB, etc.) where KB, MB, GB, and TB are treated as decimal\nunits, and KiB, MiB, GiB, and TiB are treated as binary units. Scientific notation is supported,\nenabling values like \"1.5e3MB\" (1500MB) or \"2e6GiB\" (2 million GiB).\n\nThe bytes symbol (b/B) in the unit is optional; omitting it will yield the same result (e.g., \"Mi\"\nand \"MiB\" are equivalent).", + "introduced": "v0.17.0", + "result": { + "description": "the parsed number", + "name": "y", + "type": "number" + }, + "wasm": false + }, + "upper": { + "args": [ + { + "description": "string that is converted to upper-case", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns the input string but with all characters in upper-case.", + "introduced": "v0.17.0", + "result": { + "description": "upper-case of x", + "name": "y", + "type": "string" + }, + "wasm": true + }, + "urlquery.decode": { + "args": [ + { + "description": "the URL-encoded string", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Decodes a URL-encoded input string.", + "introduced": "v0.17.0", + "result": { + "description": "URL-encoding deserialization of `x`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "urlquery.decode_object": { + "args": [ + { + "description": "the query string", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Decodes the given URL query string into an object.", + "introduced": "v0.24.0", + "result": { + "description": "the resulting object", + "name": "object", + "type": "object[string: array[string]]" + }, + "wasm": false + }, + "urlquery.encode": { + "args": [ + { + "description": "the string to encode", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Encodes the input string into a URL-encoded string.", + "introduced": "v0.17.0", + "result": { + "description": "URL-encoding serialization of `x`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "urlquery.encode_object": { + "args": [ + { + "description": "the object to encode", + "name": "object", + "type": "object[string: any\u003cstring, array[string], set[string]\u003e]" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Encodes the given object into a URL encoded query string.", + "introduced": "v0.17.0", + "result": { + "description": "the URL-encoded serialization of `object`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "uuid.parse": { + "args": [ + { + "description": "UUID string to parse", + "name": "uuid", + "type": "string" + } + ], + "available": [ + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Parses the string value as an UUID and returns an object with the well-defined fields of the UUID if valid.", + "introduced": "v0.57.0", + "result": { + "description": "Properties of UUID if valid (version, variant, etc). Undefined otherwise.", + "name": "result", + "type": "object[string: any]" + }, + "wasm": false + }, + "uuid.rfc4122": { + "args": [ + { + "description": "seed string", + "name": "k", + "type": "string" + } + ], + "available": [ + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Returns a new UUIDv4.", + "introduced": "v0.20.0", + "result": { + "description": "a version 4 UUID; for any given `k`, the output will be consistent throughout a query evaluation", + "name": "output", + "type": "string" + }, + "wasm": false + }, + "walk": { + "args": [ + { + "description": "value to walk", + "name": "x", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Generates `[path, value]` tuples for all nested documents of `x` (recursively). Queries can use `walk` to traverse documents nested under `x`.", + "introduced": "v0.17.0", + "relation": true, + "result": { + "description": "pairs of `path` and `value`: `path` is an array representing the pointer to `value` in `x`. If `path` is assigned a wildcard (`_`), the `walk` function will skip path creation entirely for faster evaluation.", + "name": "output", + "type": "array\u003carray[any], any\u003e" + }, + "wasm": true + }, + "yaml.is_valid": { + "args": [ + { + "description": "a YAML string", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Verifies the input string is a valid YAML document.", + "introduced": "v0.25.0-rc1", + "result": { + "description": "`true` if `x` is valid YAML, `false` otherwise", + "name": "result", + "type": "boolean" + }, + "wasm": false + }, + "yaml.marshal": { + "args": [ + { + "description": "the term to serialize", + "name": "x", + "type": "any" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Serializes the input term to YAML.", + "introduced": "v0.17.0", + "result": { + "description": "the YAML string representation of `x`", + "name": "y", + "type": "string" + }, + "wasm": false + }, + "yaml.unmarshal": { + "args": [ + { + "description": "a YAML string", + "name": "x", + "type": "string" + } + ], + "available": [ + "v0.17.0", + "v0.17.1", + "v0.17.2", + "v0.17.3", + "v0.18.0", + "v0.19.0-rc1", + "v0.19.0", + "v0.19.1", + "v0.19.2", + "v0.20.0", + "v0.20.1", + "v0.20.2", + "v0.20.3", + "v0.20.4", + "v0.20.5", + "v0.21.0", + "v0.21.1", + "v0.22.0", + "v0.23.0", + "v0.23.1", + "v0.23.2", + "v0.24.0", + "v0.25.0-rc1", + "v0.25.0-rc2", + "v0.25.0-rc3", + "v0.25.0-rc4", + "v0.25.0", + "v0.25.1", + "v0.25.2", + "v0.26.0", + "v0.27.0", + "v0.27.1", + "v0.28.0", + "v0.29.0", + "v0.29.1", + "v0.29.2", + "v0.29.3", + "v0.29.4", + "v0.30.0", + "v0.30.1", + "v0.30.2", + "v0.31.0", + "v0.32.0", + "v0.32.1", + "v0.33.0", + "v0.33.1", + "v0.34.0", + "v0.34.1", + "v0.34.2", + "v0.35.0", + "v0.36.0", + "v0.36.1", + "v0.37.0", + "v0.37.1", + "v0.37.2", + "v0.38.0", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.0", + "v0.42.1", + "v0.42.2", + "v0.43.0", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.0", + "v0.46.1", + "v0.46.2", + "v0.46.3", + "v0.47.0", + "v0.47.1", + "v0.47.2", + "v0.47.3", + "v0.47.4", + "v0.48.0", + "v0.49.0", + "v0.49.1", + "v0.49.2", + "v0.50.0", + "v0.50.1", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.0", + "v0.62.1", + "v0.63.0", + "v0.64.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.0", + "v1.4.1", + "v1.4.2", + "v1.5.0", + "v1.5.1", + "v1.6.0", + "v1.7.0", + "v1.7.1", + "edge" + ], + "description": "Deserializes the input string.", + "introduced": "v0.17.0", + "result": { + "description": "the term deserialized from `x`", + "name": "y", + "type": "any" + }, + "wasm": false + } +} diff --git a/third_party/opa/bundle/bundle.go b/third_party/opa/bundle/bundle.go new file mode 100644 index 000000000000..50ad97349a00 --- /dev/null +++ b/third_party/opa/bundle/bundle.go @@ -0,0 +1,134 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package bundle implements bundle loading. +package bundle + +import ( + "io" + + "github.com/open-policy-agent/opa/ast" + v1 "github.com/open-policy-agent/opa/v1/bundle" +) + +// Common file extensions and file names. +const ( + RegoExt = v1.RegoExt + WasmFile = v1.WasmFile + PlanFile = v1.PlanFile + ManifestExt = v1.ManifestExt + SignaturesFile = v1.SignaturesFile + + DefaultSizeLimitBytes = v1.DefaultSizeLimitBytes + DeltaBundleType = v1.DeltaBundleType + SnapshotBundleType = v1.SnapshotBundleType +) + +// Bundle represents a loaded bundle. The bundle can contain data and policies. +type Bundle = v1.Bundle + +// Raw contains raw bytes representing the bundle's content +type Raw = v1.Raw + +// Patch contains an array of objects wherein each object represents the patch operation to be +// applied to the bundle data. +type Patch = v1.Patch + +// PatchOperation models a single patch operation against a document. +type PatchOperation = v1.PatchOperation + +// SignaturesConfig represents an array of JWTs that encapsulate the signatures for the bundle. +type SignaturesConfig = v1.SignaturesConfig + +// DecodedSignature represents the decoded JWT payload. +type DecodedSignature = v1.DecodedSignature + +// FileInfo contains the hashing algorithm used, resulting digest etc. +type FileInfo = v1.FileInfo + +// NewFile returns a new FileInfo. +func NewFile(name, hash, alg string) FileInfo { + return v1.NewFile(name, hash, alg) +} + +// Manifest represents the manifest from a bundle. The manifest may contain +// metadata such as the bundle revision. +type Manifest = v1.Manifest + +// WasmResolver maps a wasm module to an entrypoint ref. +type WasmResolver = v1.WasmResolver + +// ModuleFile represents a single module contained in a bundle. +type ModuleFile = v1.ModuleFile + +// WasmModuleFile represents a single wasm module contained in a bundle. +type WasmModuleFile = v1.WasmModuleFile + +// PlanModuleFile represents a single plan module contained in a bundle. +// +// NOTE(tsandall): currently the plans are just opaque binary blobs. In the +// future we could inject the entrypoints so that the plans could be executed +// inside of OPA proper like we do for Wasm modules. +type PlanModuleFile = v1.PlanModuleFile + +// Reader contains the reader to load the bundle from. +type Reader = v1.Reader + +// NewReader is deprecated. Use NewCustomReader instead. +func NewReader(r io.Reader) *Reader { + return v1.NewReader(r).WithRegoVersion(ast.DefaultRegoVersion) +} + +// NewCustomReader returns a new Reader configured to use the +// specified DirectoryLoader. +func NewCustomReader(loader DirectoryLoader) *Reader { + return v1.NewCustomReader(loader).WithRegoVersion(ast.DefaultRegoVersion) +} + +// Write is deprecated. Use NewWriter instead. +func Write(w io.Writer, bundle Bundle) error { + return v1.Write(w, bundle) +} + +// Writer implements bundle serialization. +type Writer = v1.Writer + +// NewWriter returns a bundle writer that writes to w. +func NewWriter(w io.Writer) *Writer { + return v1.NewWriter(w) +} + +// Merge accepts a set of bundles and merges them into a single result bundle. If there are +// any conflicts during the merge (e.g., with roots) an error is returned. The result bundle +// will have an empty revision except in the special case where a single bundle is provided +// (and in that case the bundle is just returned unmodified.) +func Merge(bundles []*Bundle) (*Bundle, error) { + return MergeWithRegoVersion(bundles, ast.DefaultRegoVersion, false) +} + +// MergeWithRegoVersion creates a merged bundle from the provided bundles, similar to Merge. +// If more than one bundle is provided, the rego version of the result bundle is set to the provided regoVersion. +// Any Rego files in a bundle of conflicting rego version will be marked in the result's manifest with the rego version +// of its original bundle. If the Rego file already had an overriding rego version, it will be preserved. +// If a single bundle is provided, it will retain any rego version information it already had. If it has none, the +// provided regoVersion will be applied to it. +// If usePath is true, per-file rego-versions will be calculated using the file's ModuleFile.Path; otherwise, the file's +// ModuleFile.URL will be used. +func MergeWithRegoVersion(bundles []*Bundle, regoVersion ast.RegoVersion, usePath bool) (*Bundle, error) { + if regoVersion == ast.RegoUndefined { + regoVersion = ast.DefaultRegoVersion + } + + return v1.MergeWithRegoVersion(bundles, regoVersion, usePath) +} + +// RootPathsOverlap takes in two bundle root paths and returns true if they overlap. +func RootPathsOverlap(pathA string, pathB string) bool { + return v1.RootPathsOverlap(pathA, pathB) +} + +// RootPathsContain takes a set of bundle root paths and returns true if the path is contained. +func RootPathsContain(roots []string, path string) bool { + return v1.RootPathsContain(roots, path) +} diff --git a/third_party/opa/bundle/bundle_test.go b/third_party/opa/bundle/bundle_test.go new file mode 100644 index 000000000000..6d58c3a22396 --- /dev/null +++ b/third_party/opa/bundle/bundle_test.go @@ -0,0 +1,84 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" +) + +func TestRead_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0", // v0 is the default rego-version + module: `package example + +p[x] { + x := "a" +}`, + }, + { + note: "rego.v1 import", + module: `package example +import rego.v1 + +p contains x if { + x := "a" +}`, + }, + { + note: "v1", + module: `package example + +p contains x if { + x := "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: var cannot be used for rule name", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + module := tc.module + files := [][2]string{ + {"test.rego", module}, + } + + buf := archive.MustWriteTarGz(files) + loader := NewTarballLoaderWithBaseURL(buf, "") + br := NewCustomReader(loader) + + bundle, err := br.Read() + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error(s):\n\n%v\n\nbut got nil", tc.expErrs) + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if len(bundle.Modules) != 1 { + t.Fatalf("expected 1 module but got %d", len(bundle.Modules)) + } + } + }) + } +} diff --git a/third_party/opa/bundle/doc.go b/third_party/opa/bundle/doc.go new file mode 100644 index 000000000000..7ec7c9b3328f --- /dev/null +++ b/third_party/opa/bundle/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package bundle diff --git a/third_party/opa/bundle/file.go b/third_party/opa/bundle/file.go new file mode 100644 index 000000000000..ccb7b2351098 --- /dev/null +++ b/third_party/opa/bundle/file.go @@ -0,0 +1,50 @@ +package bundle + +import ( + "io" + + "github.com/open-policy-agent/opa/storage" + v1 "github.com/open-policy-agent/opa/v1/bundle" +) + +// Descriptor contains information about a file and +// can be used to read the file contents. +type Descriptor = v1.Descriptor + +func NewDescriptor(url, path string, reader io.Reader) *Descriptor { + return v1.NewDescriptor(url, path, reader) +} + +type PathFormat = v1.PathFormat + +const ( + Chrooted = v1.Chrooted + SlashRooted = v1.SlashRooted + Passthrough = v1.Passthrough +) + +// DirectoryLoader defines an interface which can be used to load +// files from a directory by iterating over each one in the tree. +type DirectoryLoader = v1.DirectoryLoader + +// NewDirectoryLoader returns a basic DirectoryLoader implementation +// that will load files from a given root directory path. +func NewDirectoryLoader(root string) DirectoryLoader { + return v1.NewDirectoryLoader(root) +} + +// NewTarballLoader is deprecated. Use NewTarballLoaderWithBaseURL instead. +func NewTarballLoader(r io.Reader) DirectoryLoader { + return v1.NewTarballLoader(r) +} + +// NewTarballLoaderWithBaseURL returns a new DirectoryLoader that reads +// files out of a gzipped tar archive. The file URLs will be prefixed +// with the baseURL. +func NewTarballLoaderWithBaseURL(r io.Reader, baseURL string) DirectoryLoader { + return v1.NewTarballLoaderWithBaseURL(r, baseURL) +} + +func NewIterator(raw []Raw) storage.Iterator { + return v1.NewIterator(raw) +} diff --git a/third_party/opa/bundle/filefs.go b/third_party/opa/bundle/filefs.go new file mode 100644 index 000000000000..16e00928dada --- /dev/null +++ b/third_party/opa/bundle/filefs.go @@ -0,0 +1,22 @@ +//go:build go1.16 +// +build go1.16 + +package bundle + +import ( + "io/fs" + + v1 "github.com/open-policy-agent/opa/v1/bundle" +) + +// NewFSLoader returns a basic DirectoryLoader implementation +// that will load files from a fs.FS interface +func NewFSLoader(filesystem fs.FS) (DirectoryLoader, error) { + return v1.NewFSLoader(filesystem) +} + +// NewFSLoaderWithRoot returns a basic DirectoryLoader implementation +// that will load files from a fs.FS interface at the supplied root +func NewFSLoaderWithRoot(filesystem fs.FS, root string) DirectoryLoader { + return v1.NewFSLoaderWithRoot(filesystem, root) +} diff --git a/third_party/opa/bundle/hash.go b/third_party/opa/bundle/hash.go new file mode 100644 index 000000000000..d4cc601dead2 --- /dev/null +++ b/third_party/opa/bundle/hash.go @@ -0,0 +1,32 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + v1 "github.com/open-policy-agent/opa/v1/bundle" +) + +// HashingAlgorithm represents a subset of hashing algorithms implemented in Go +type HashingAlgorithm = v1.HashingAlgorithm + +// Supported values for HashingAlgorithm +const ( + MD5 = v1.MD5 + SHA1 = v1.SHA1 + SHA224 = v1.SHA224 + SHA256 = v1.SHA256 + SHA384 = v1.SHA384 + SHA512 = v1.SHA512 + SHA512224 = v1.SHA512224 + SHA512256 = v1.SHA512256 +) + +// SignatureHasher computes a signature digest for a file with (structured or unstructured) data and policy +type SignatureHasher = v1.SignatureHasher + +// NewSignatureHasher returns a signature hasher suitable for a particular hashing algorithm +func NewSignatureHasher(alg HashingAlgorithm) (SignatureHasher, error) { + return v1.NewSignatureHasher(alg) +} diff --git a/third_party/opa/bundle/keys.go b/third_party/opa/bundle/keys.go new file mode 100644 index 000000000000..99f9b0f165a2 --- /dev/null +++ b/third_party/opa/bundle/keys.go @@ -0,0 +1,30 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package bundle provide helpers that assist in creating the verification and signing key configuration +package bundle + +import ( + v1 "github.com/open-policy-agent/opa/v1/bundle" +) + +// KeyConfig holds the keys used to sign or verify bundles and tokens +// Moved to own package, alias kept for backwards compatibility +type KeyConfig = v1.KeyConfig + +// VerificationConfig represents the key configuration used to verify a signed bundle +type VerificationConfig = v1.VerificationConfig + +// NewVerificationConfig return a new VerificationConfig +func NewVerificationConfig(keys map[string]*KeyConfig, id, scope string, exclude []string) *VerificationConfig { + return v1.NewVerificationConfig(keys, id, scope, exclude) +} + +// SigningConfig represents the key configuration used to generate a signed bundle +type SigningConfig = v1.SigningConfig + +// NewSigningConfig return a new SigningConfig +func NewSigningConfig(key, alg, claimsPath string) *SigningConfig { + return v1.NewSigningConfig(key, alg, claimsPath) +} diff --git a/third_party/opa/bundle/sign.go b/third_party/opa/bundle/sign.go new file mode 100644 index 000000000000..56e25eec9c1a --- /dev/null +++ b/third_party/opa/bundle/sign.go @@ -0,0 +1,35 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package bundle provide helpers that assist in the creating a signed bundle +package bundle + +import ( + v1 "github.com/open-policy-agent/opa/v1/bundle" +) + +// Signer is the interface expected for implementations that generate bundle signatures. +type Signer v1.Signer + +// GenerateSignedToken will retrieve the Signer implementation based on the Plugin specified +// in SigningConfig, and call its implementation of GenerateSignedToken. The signer generates +// a signed token given the list of files to be included in the payload and the bundle +// signing config. The keyID if non-empty, represents the value for the "keyid" claim in the token. +func GenerateSignedToken(files []FileInfo, sc *SigningConfig, keyID string) (string, error) { + return v1.GenerateSignedToken(files, sc, keyID) +} + +// DefaultSigner is the default bundle signing implementation. It signs bundles by generating +// a JWT and signing it using a locally-accessible private key. +type DefaultSigner v1.DefaultSigner + +// GetSigner returns the Signer registered under the given id +func GetSigner(id string) (Signer, error) { + return v1.GetSigner(id) +} + +// RegisterSigner registers a Signer under the given id +func RegisterSigner(id string, s Signer) error { + return v1.RegisterSigner(id, s) +} diff --git a/third_party/opa/bundle/store.go b/third_party/opa/bundle/store.go new file mode 100644 index 000000000000..9659d67bdee5 --- /dev/null +++ b/third_party/opa/bundle/store.go @@ -0,0 +1,152 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "context" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/storage" + v1 "github.com/open-policy-agent/opa/v1/bundle" +) + +// BundlesBasePath is the storage path used for storing bundle metadata +var BundlesBasePath = v1.BundlesBasePath + +// Note: As needed these helpers could be memoized. + +// ManifestStoragePath is the storage path used for the given named bundle manifest. +func ManifestStoragePath(name string) storage.Path { + return v1.ManifestStoragePath(name) +} + +// EtagStoragePath is the storage path used for the given named bundle etag. +func EtagStoragePath(name string) storage.Path { + return v1.EtagStoragePath(name) +} + +// ReadBundleNamesFromStore will return a list of bundle names which have had their metadata stored. +func ReadBundleNamesFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) ([]string, error) { + return v1.ReadBundleNamesFromStore(ctx, store, txn) +} + +// WriteManifestToStore will write the manifest into the storage. This function is called when +// the bundle is activated. +func WriteManifestToStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string, manifest Manifest) error { + return v1.WriteManifestToStore(ctx, store, txn, name, manifest) +} + +// WriteEtagToStore will write the bundle etag into the storage. This function is called when the bundle is activated. +func WriteEtagToStore(ctx context.Context, store storage.Store, txn storage.Transaction, name, etag string) error { + return v1.WriteEtagToStore(ctx, store, txn, name, etag) +} + +// EraseManifestFromStore will remove the manifest from storage. This function is called +// when the bundle is deactivated. +func EraseManifestFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) error { + return v1.EraseManifestFromStore(ctx, store, txn, name) +} + +// ReadWasmModulesFromStore will write Wasm module resolver metadata from the store. +func ReadWasmModulesFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (map[string][]byte, error) { + return v1.ReadWasmModulesFromStore(ctx, store, txn, name) +} + +// ReadBundleRootsFromStore returns the roots in the specified bundle. +// If the bundle is not activated, this function will return +// storage NotFound error. +func ReadBundleRootsFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) ([]string, error) { + return v1.ReadBundleRootsFromStore(ctx, store, txn, name) +} + +// ReadBundleRevisionFromStore returns the revision in the specified bundle. +// If the bundle is not activated, this function will return +// storage NotFound error. +func ReadBundleRevisionFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (string, error) { + return v1.ReadBundleRevisionFromStore(ctx, store, txn, name) +} + +// ReadBundleMetadataFromStore returns the metadata in the specified bundle. +// If the bundle is not activated, this function will return +// storage NotFound error. +func ReadBundleMetadataFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (map[string]any, error) { + return v1.ReadBundleMetadataFromStore(ctx, store, txn, name) +} + +// ReadBundleEtagFromStore returns the etag for the specified bundle. +// If the bundle is not activated, this function will return +// storage NotFound error. +func ReadBundleEtagFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (string, error) { + return v1.ReadBundleEtagFromStore(ctx, store, txn, name) +} + +// ActivateOpts defines options for the Activate API call. +type ActivateOpts = v1.ActivateOpts + +// Activate the bundle(s) by loading into the given Store. This will load policies, data, and record +// the manifest in storage. The compiler provided will have had the polices compiled on it. +func Activate(opts *ActivateOpts) error { + return v1.Activate(setActivateDefaultRegoVersion(opts)) +} + +// DeactivateOpts defines options for the Deactivate API call +type DeactivateOpts = v1.DeactivateOpts + +// Deactivate the bundle(s). This will erase associated data, policies, and the manifest entry from the store. +func Deactivate(opts *DeactivateOpts) error { + return v1.Deactivate(setDeactivateDefaultRegoVersion(opts)) +} + +// LegacyWriteManifestToStore will write the bundle manifest to the older single (unnamed) bundle manifest location. +// Deprecated: Use WriteManifestToStore and named bundles instead. +func LegacyWriteManifestToStore(ctx context.Context, store storage.Store, txn storage.Transaction, manifest Manifest) error { + return v1.LegacyWriteManifestToStore(ctx, store, txn, manifest) +} + +// LegacyEraseManifestFromStore will erase the bundle manifest from the older single (unnamed) bundle manifest location. +// Deprecated: Use WriteManifestToStore and named bundles instead. +func LegacyEraseManifestFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) error { + return v1.LegacyEraseManifestFromStore(ctx, store, txn) +} + +// LegacyReadRevisionFromStore will read the bundle manifest revision from the older single (unnamed) bundle manifest location. +// Deprecated: Use ReadBundleRevisionFromStore and named bundles instead. +func LegacyReadRevisionFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) (string, error) { + return v1.LegacyReadRevisionFromStore(ctx, store, txn) +} + +// ActivateLegacy calls Activate for the bundles but will also write their manifest to the older unnamed store location. +// Deprecated: Use Activate with named bundles instead. +func ActivateLegacy(opts *ActivateOpts) error { + return v1.ActivateLegacy(opts) +} + +func setActivateDefaultRegoVersion(opts *ActivateOpts) *ActivateOpts { + if opts == nil { + return nil + } + + if opts.ParserOptions.RegoVersion == ast.RegoUndefined { + cpy := *opts + cpy.ParserOptions.RegoVersion = ast.DefaultRegoVersion + return &cpy + } + + return opts +} + +func setDeactivateDefaultRegoVersion(opts *DeactivateOpts) *DeactivateOpts { + if opts == nil { + return nil + } + + if opts.ParserOptions.RegoVersion == ast.RegoUndefined { + cpy := *opts + cpy.ParserOptions.RegoVersion = ast.DefaultRegoVersion + return &cpy + } + + return opts +} diff --git a/third_party/opa/bundle/store_test.go b/third_party/opa/bundle/store_test.go new file mode 100644 index 000000000000..fcc6665567e2 --- /dev/null +++ b/third_party/opa/bundle/store_test.go @@ -0,0 +1,374 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/internal/storage/mock" + "github.com/open-policy-agent/opa/metrics" + "github.com/open-policy-agent/opa/storage" +) + +func TestHasRootsOverlap(t *testing.T) { + ctx := context.Background() + + cases := []struct { + note string + storeRoots map[string]*[]string + bundleRoots map[string]*[]string + overlaps bool + }{ + { + note: "no overlap with existing roots", + storeRoots: map[string]*[]string{"bundle1": {"a", "b"}}, + bundleRoots: map[string]*[]string{"bundle2": {"c"}}, + overlaps: false, + }, + { + note: "no overlap with existing roots multiple bundles", + storeRoots: map[string]*[]string{"bundle1": {"a", "b"}}, + bundleRoots: map[string]*[]string{"bundle2": {"c"}, "bundle3": {"d"}}, + overlaps: false, + }, + { + note: "no overlap no existing roots", + storeRoots: map[string]*[]string{}, + bundleRoots: map[string]*[]string{"bundle1": {"a", "b"}}, + overlaps: false, + }, + { + note: "no overlap without existing roots multiple bundles", + storeRoots: map[string]*[]string{}, + bundleRoots: map[string]*[]string{"bundle1": {"a", "b"}, "bundle2": {"c"}}, + overlaps: false, + }, + { + note: "overlap without existing roots multiple bundles", + storeRoots: map[string]*[]string{}, + bundleRoots: map[string]*[]string{"bundle1": {"a", "b"}, "bundle2": {"a", "c"}}, + overlaps: true, + }, + { + note: "overlap with existing roots", + storeRoots: map[string]*[]string{"bundle1": {"a", "b"}}, + bundleRoots: map[string]*[]string{"bundle2": {"c", "a"}}, + overlaps: true, + }, + { + note: "overlap with existing roots multiple bundles", + storeRoots: map[string]*[]string{"bundle1": {"a", "b"}}, + bundleRoots: map[string]*[]string{"bundle2": {"c", "a"}, "bundle3": {"a"}}, + overlaps: true, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + mockStore := mock.New() + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + for name, roots := range tc.storeRoots { + err := WriteManifestToStore(ctx, mockStore, txn, name, Manifest{Roots: roots}) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + } + + bundles := map[string]*Bundle{} + for name, roots := range tc.bundleRoots { + bundles[name] = &Bundle{ + Manifest: Manifest{ + Roots: roots, + }, + } + } + + mockStore.AssertValid(t) + }) + } +} + +func TestActivate_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + customRegoVersion ast.RegoVersion + expErrs []string + }{ + // default rego-version + { + note: "v0 module, no v1 parse-time violations", + module: `package test + p[x] { + x = "a" + }`, + }, + { + note: "v0 module, v1 parse-time violations", + module: `package test + + contains[x] { + x = "a" + }`, + }, + + // cross-rego-version + { + note: "rego.v1 import, no v1 parse-time violations", + module: `package test + import rego.v1 + + p contains x if { + x = "a" + }`, + }, + { + note: "rego.v1 import, v1 parse-time violations", + module: `package test + import rego.v1 + + p contains x { + x = "a" + }`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + }, + }, + + // NOT default rego-version + { + note: "v1 module", + module: `package test + + p contains x if { + x = "a" + }`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + }, + }, + + // custom rego-version + { + note: "v1 module, v1 custom rego-version", + module: `package test + + p contains x if { + x = "a" + }`, + customRegoVersion: ast.RegoV1, + }, + { + note: "v1 module, v1 custom rego-version, v1 parse-time violations", + module: `package test + + p contains x { + x = "a" + }`, + customRegoVersion: ast.RegoV1, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := mock.New() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + compiler := ast.NewCompiler().WithDefaultRegoVersion(ast.RegoV0CompatV1) + m := metrics.New() + + bundleName := "bundle1" + modulePath := "test/policy.rego" + + // We want to make assert that the default rego-version is used, which it is when a module is erased from storage and we don't know what version it has. + // Therefore, we add a module to the store, which is the replaced by the Activate() call, causing an erase. + if err := store.UpsertPolicy(ctx, txn, fmt.Sprintf("%s/%s", bundleName, modulePath), []byte(tc.module)); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + newModule := `package test` + bundles := map[string]*Bundle{ + bundleName: { + Manifest: Manifest{ + Roots: &[]string{"test"}, + }, + Modules: []ModuleFile{ + { + Path: modulePath, + Raw: []byte(newModule), + Parsed: ast.MustParseModule(newModule), + }, + }, + }, + } + + opts := ActivateOpts{ + Ctx: ctx, + Txn: txn, + Store: store, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + } + + if tc.customRegoVersion != ast.RegoUndefined { + opts.ParserOptions.RegoVersion = tc.customRegoVersion + } + + err := Activate(&opts) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got nil for test: %s", tc.note) + } + for _, expErr := range tc.expErrs { + if err := err.Error(); !strings.Contains(err, expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + } +} + +func TestDeactivate_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + customRegoVersion ast.RegoVersion + expErrs []string + }{ + // default rego-version + { + note: "v0 module, no v1 parse-time violations", + module: `package test + p[x] { + x = "a" + }`, + }, + { + note: "v0 module, v1 parse-time violations", + module: `package test + + contains[x] { + x = "a" + }`, + }, + + // cross-rego-version + { + note: "rego.v1 import, no v1 parse-time violations", + module: `package test + import rego.v1 + + p contains x if { + x = "a" + }`, + }, + { + note: "rego.v1 import, v1 parse-time violations", + module: `package test + import rego.v1 + + p contains x { + x = "a" + }`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + }, + }, + + // NOT default rego-version + { + note: "v1 module", + module: `package test + + p contains x if { + x = "a" + }`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + }, + }, + + // custom rego-version + { + note: "v1 module, v1 custom rego-version", + module: `package test + + p contains x if { + x = "a" + }`, + customRegoVersion: ast.RegoV1, + }, + { + note: "v1 module, v1 custom rego-version, v1 parse-time violations", + module: `package test + + p contains x { + x = "a" + }`, + customRegoVersion: ast.RegoV1, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := mock.New() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + bundleName := "bundle1" + modulePath := "test/policy.rego" + + // We want to make assert that the default rego-version is used, which it is when a module is erased from storage and we don't know what version it has. + // Therefore, we add a module to the store, which is the replaced by the Activate() call, causing an erase. + if err := store.UpsertPolicy(ctx, txn, fmt.Sprintf("%s/%s", bundleName, modulePath), []byte(tc.module)); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + opts := DeactivateOpts{ + Ctx: ctx, + Txn: txn, + Store: store, + BundleNames: map[string]struct{}{ + fmt.Sprintf("%s/%s", bundleName, modulePath): {}, + }, + } + + if tc.customRegoVersion != ast.RegoUndefined { + opts.ParserOptions.RegoVersion = tc.customRegoVersion + } + + err := Deactivate(&opts) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got nil for test: %s", tc.note) + } + for _, expErr := range tc.expErrs { + if err := err.Error(); !strings.Contains(err, expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + } +} diff --git a/third_party/opa/bundle/verify.go b/third_party/opa/bundle/verify.go new file mode 100644 index 000000000000..ef2e1e32db09 --- /dev/null +++ b/third_party/opa/bundle/verify.go @@ -0,0 +1,36 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package bundle provide helpers that assist in the bundle signature verification process +package bundle + +import ( + v1 "github.com/open-policy-agent/opa/v1/bundle" +) + +// Verifier is the interface expected for implementations that verify bundle signatures. +type Verifier v1.Verifier + +// VerifyBundleSignature will retrieve the Verifier implementation based +// on the Plugin specified in SignaturesConfig, and call its implementation +// of VerifyBundleSignature. VerifyBundleSignature verifies the bundle signature +// using the given public keys or secret. If a signature is verified, it keeps +// track of the files specified in the JWT payload +func VerifyBundleSignature(sc SignaturesConfig, bvc *VerificationConfig) (map[string]FileInfo, error) { + return v1.VerifyBundleSignature(sc, bvc) +} + +// DefaultVerifier is the default bundle verification implementation. It verifies bundles by checking +// the JWT signature using a locally-accessible public key. +type DefaultVerifier = v1.DefaultVerifier + +// GetVerifier returns the Verifier registered under the given id +func GetVerifier(id string) (Verifier, error) { + return v1.GetVerifier(id) +} + +// RegisterVerifier registers a Verifier under the given id +func RegisterVerifier(id string, v Verifier) error { + return v1.RegisterVerifier(id, v) +} diff --git a/third_party/opa/capabilities.json b/third_party/opa/capabilities.json new file mode 100644 index 000000000000..110c3eca9195 --- /dev/null +++ b/third_party/opa/capabilities.json @@ -0,0 +1,4850 @@ +{ + "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": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "keywords_in_refs", + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/capabilities.go b/third_party/opa/capabilities/capabilities.go new file mode 100644 index 000000000000..ba32cf977e5b --- /dev/null +++ b/third_party/opa/capabilities/capabilities.go @@ -0,0 +1,19 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build go1.16 +// +build go1.16 + +package capabilities + +import ( + "embed" +) + +// FS contains the embedded capabilities/ directory of the built version, +// which has all the capabilities of previous versions: +// "v0.18.0.json" contains the capabilities JSON of version v0.18.0, etc +// +//go:embed *.json +var FS embed.FS diff --git a/third_party/opa/capabilities/doc.go b/third_party/opa/capabilities/doc.go new file mode 100644 index 000000000000..189c2e727ac6 --- /dev/null +++ b/third_party/opa/capabilities/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package capabilities diff --git a/third_party/opa/capabilities/v0.17.0.json b/third_party/opa/capabilities/v0.17.0.json new file mode 100644 index 000000000000..b536350d9012 --- /dev/null +++ b/third_party/opa/capabilities/v0.17.0.json @@ -0,0 +1,2392 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "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_ps256", + "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": "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.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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_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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.17.1.json b/third_party/opa/capabilities/v0.17.1.json new file mode 100644 index 000000000000..b536350d9012 --- /dev/null +++ b/third_party/opa/capabilities/v0.17.1.json @@ -0,0 +1,2392 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "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_ps256", + "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": "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.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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_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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.17.2.json b/third_party/opa/capabilities/v0.17.2.json new file mode 100644 index 000000000000..1fbd4dbcaaec --- /dev/null +++ b/third_party/opa/capabilities/v0.17.2.json @@ -0,0 +1,2525 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "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_ps256", + "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": "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.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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_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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.17.3.json b/third_party/opa/capabilities/v0.17.3.json new file mode 100644 index 000000000000..1fbd4dbcaaec --- /dev/null +++ b/third_party/opa/capabilities/v0.17.3.json @@ -0,0 +1,2525 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "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_ps256", + "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": "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.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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_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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.18.0.json b/third_party/opa/capabilities/v0.18.0.json new file mode 100644 index 000000000000..8b8174d1f380 --- /dev/null +++ b/third_party/opa/capabilities/v0.18.0.json @@ -0,0 +1,2685 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "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_ps256", + "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": "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.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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_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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.19.0-rc1.json b/third_party/opa/capabilities/v0.19.0-rc1.json new file mode 100644 index 000000000000..d660b620c80a --- /dev/null +++ b/third_party/opa/capabilities/v0.19.0-rc1.json @@ -0,0 +1,2835 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "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_ps256", + "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": "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.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.19.0.json b/third_party/opa/capabilities/v0.19.0.json new file mode 100644 index 000000000000..fa9ae4e4714a --- /dev/null +++ b/third_party/opa/capabilities/v0.19.0.json @@ -0,0 +1,2858 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "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_ps256", + "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": "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.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.19.1.json b/third_party/opa/capabilities/v0.19.1.json new file mode 100644 index 000000000000..fa9ae4e4714a --- /dev/null +++ b/third_party/opa/capabilities/v0.19.1.json @@ -0,0 +1,2858 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "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_ps256", + "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": "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.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.19.2.json b/third_party/opa/capabilities/v0.19.2.json new file mode 100644 index 000000000000..fa9ae4e4714a --- /dev/null +++ b/third_party/opa/capabilities/v0.19.2.json @@ -0,0 +1,2858 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "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_ps256", + "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": "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.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.20.0.json b/third_party/opa/capabilities/v0.20.0.json new file mode 100644 index 000000000000..f2d2906839e8 --- /dev/null +++ b/third_party/opa/capabilities/v0.20.0.json @@ -0,0 +1,3064 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.20.1.json b/third_party/opa/capabilities/v0.20.1.json new file mode 100644 index 000000000000..f2d2906839e8 --- /dev/null +++ b/third_party/opa/capabilities/v0.20.1.json @@ -0,0 +1,3064 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.20.2.json b/third_party/opa/capabilities/v0.20.2.json new file mode 100644 index 000000000000..f2d2906839e8 --- /dev/null +++ b/third_party/opa/capabilities/v0.20.2.json @@ -0,0 +1,3064 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.20.3.json b/third_party/opa/capabilities/v0.20.3.json new file mode 100644 index 000000000000..f2d2906839e8 --- /dev/null +++ b/third_party/opa/capabilities/v0.20.3.json @@ -0,0 +1,3064 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.20.4.json b/third_party/opa/capabilities/v0.20.4.json new file mode 100644 index 000000000000..f2d2906839e8 --- /dev/null +++ b/third_party/opa/capabilities/v0.20.4.json @@ -0,0 +1,3064 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.20.5.json b/third_party/opa/capabilities/v0.20.5.json new file mode 100644 index 000000000000..f2d2906839e8 --- /dev/null +++ b/third_party/opa/capabilities/v0.20.5.json @@ -0,0 +1,3064 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.21.0.json b/third_party/opa/capabilities/v0.21.0.json new file mode 100644 index 000000000000..18d1af3de730 --- /dev/null +++ b/third_party/opa/capabilities/v0.21.0.json @@ -0,0 +1,3086 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.21.1.json b/third_party/opa/capabilities/v0.21.1.json new file mode 100644 index 000000000000..18d1af3de730 --- /dev/null +++ b/third_party/opa/capabilities/v0.21.1.json @@ -0,0 +1,3086 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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": "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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.22.0.json b/third_party/opa/capabilities/v0.22.0.json new file mode 100644 index 000000000000..c3f067141452 --- /dev/null +++ b/third_party/opa/capabilities/v0.22.0.json @@ -0,0 +1,3137 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.23.0.json b/third_party/opa/capabilities/v0.23.0.json new file mode 100644 index 000000000000..8eeceda5888b --- /dev/null +++ b/third_party/opa/capabilities/v0.23.0.json @@ -0,0 +1,3168 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.23.1.json b/third_party/opa/capabilities/v0.23.1.json new file mode 100644 index 000000000000..8eeceda5888b --- /dev/null +++ b/third_party/opa/capabilities/v0.23.1.json @@ -0,0 +1,3168 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.23.2.json b/third_party/opa/capabilities/v0.23.2.json new file mode 100644 index 000000000000..8eeceda5888b --- /dev/null +++ b/third_party/opa/capabilities/v0.23.2.json @@ -0,0 +1,3168 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.24.0.json b/third_party/opa/capabilities/v0.24.0.json new file mode 100644 index 000000000000..ccca1210e2e6 --- /dev/null +++ b/third_party/opa/capabilities/v0.24.0.json @@ -0,0 +1,3243 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.25.0-rc1.json b/third_party/opa/capabilities/v0.25.0-rc1.json new file mode 100644 index 000000000000..9dc7c080c3af --- /dev/null +++ b/third_party/opa/capabilities/v0.25.0-rc1.json @@ -0,0 +1,3271 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.25.0-rc2.json b/third_party/opa/capabilities/v0.25.0-rc2.json new file mode 100644 index 000000000000..b5570417ce80 --- /dev/null +++ b/third_party/opa/capabilities/v0.25.0-rc2.json @@ -0,0 +1,3313 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.25.0-rc3.json b/third_party/opa/capabilities/v0.25.0-rc3.json new file mode 100644 index 000000000000..b5570417ce80 --- /dev/null +++ b/third_party/opa/capabilities/v0.25.0-rc3.json @@ -0,0 +1,3313 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.25.0-rc4.json b/third_party/opa/capabilities/v0.25.0-rc4.json new file mode 100644 index 000000000000..b5570417ce80 --- /dev/null +++ b/third_party/opa/capabilities/v0.25.0-rc4.json @@ -0,0 +1,3313 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.25.0.json b/third_party/opa/capabilities/v0.25.0.json new file mode 100644 index 000000000000..650eed4b9829 --- /dev/null +++ b/third_party/opa/capabilities/v0.25.0.json @@ -0,0 +1,3355 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.25.1.json b/third_party/opa/capabilities/v0.25.1.json new file mode 100644 index 000000000000..650eed4b9829 --- /dev/null +++ b/third_party/opa/capabilities/v0.25.1.json @@ -0,0 +1,3355 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.25.2.json b/third_party/opa/capabilities/v0.25.2.json new file mode 100644 index 000000000000..650eed4b9829 --- /dev/null +++ b/third_party/opa/capabilities/v0.25.2.json @@ -0,0 +1,3355 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.26.0.json b/third_party/opa/capabilities/v0.26.0.json new file mode 100644 index 000000000000..6910546b42cc --- /dev/null +++ b/third_party/opa/capabilities/v0.26.0.json @@ -0,0 +1,3383 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ] +} diff --git a/third_party/opa/capabilities/v0.27.0.json b/third_party/opa/capabilities/v0.27.0.json new file mode 100644 index 000000000000..b9046a6ea732 --- /dev/null +++ b/third_party/opa/capabilities/v0.27.0.json @@ -0,0 +1,3389 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + } + ] +} diff --git a/third_party/opa/capabilities/v0.27.1.json b/third_party/opa/capabilities/v0.27.1.json new file mode 100644 index 000000000000..b9046a6ea732 --- /dev/null +++ b/third_party/opa/capabilities/v0.27.1.json @@ -0,0 +1,3389 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + } + ] +} diff --git a/third_party/opa/capabilities/v0.28.0.json b/third_party/opa/capabilities/v0.28.0.json new file mode 100644 index 000000000000..7ff34daf03d0 --- /dev/null +++ b/third_party/opa/capabilities/v0.28.0.json @@ -0,0 +1,3458 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + } + ] +} diff --git a/third_party/opa/capabilities/v0.29.0.json b/third_party/opa/capabilities/v0.29.0.json new file mode 100644 index 000000000000..7ff34daf03d0 --- /dev/null +++ b/third_party/opa/capabilities/v0.29.0.json @@ -0,0 +1,3458 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + } + ] +} diff --git a/third_party/opa/capabilities/v0.29.1.json b/third_party/opa/capabilities/v0.29.1.json new file mode 100644 index 000000000000..7ff34daf03d0 --- /dev/null +++ b/third_party/opa/capabilities/v0.29.1.json @@ -0,0 +1,3458 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + } + ] +} diff --git a/third_party/opa/capabilities/v0.29.2.json b/third_party/opa/capabilities/v0.29.2.json new file mode 100644 index 000000000000..7ff34daf03d0 --- /dev/null +++ b/third_party/opa/capabilities/v0.29.2.json @@ -0,0 +1,3458 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + } + ] +} diff --git a/third_party/opa/capabilities/v0.29.3.json b/third_party/opa/capabilities/v0.29.3.json new file mode 100644 index 000000000000..7ff34daf03d0 --- /dev/null +++ b/third_party/opa/capabilities/v0.29.3.json @@ -0,0 +1,3458 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + } + ] +} diff --git a/third_party/opa/capabilities/v0.29.4.json b/third_party/opa/capabilities/v0.29.4.json new file mode 100644 index 000000000000..7ff34daf03d0 --- /dev/null +++ b/third_party/opa/capabilities/v0.29.4.json @@ -0,0 +1,3458 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + } + ] +} diff --git a/third_party/opa/capabilities/v0.30.0.json b/third_party/opa/capabilities/v0.30.0.json new file mode 100644 index 000000000000..7ff34daf03d0 --- /dev/null +++ b/third_party/opa/capabilities/v0.30.0.json @@ -0,0 +1,3458 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + } + ] +} diff --git a/third_party/opa/capabilities/v0.30.1.json b/third_party/opa/capabilities/v0.30.1.json new file mode 100644 index 000000000000..7ff34daf03d0 --- /dev/null +++ b/third_party/opa/capabilities/v0.30.1.json @@ -0,0 +1,3458 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + } + ] +} diff --git a/third_party/opa/capabilities/v0.30.2.json b/third_party/opa/capabilities/v0.30.2.json new file mode 100644 index 000000000000..7ff34daf03d0 --- /dev/null +++ b/third_party/opa/capabilities/v0.30.2.json @@ -0,0 +1,3458 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + } + ] +} diff --git a/third_party/opa/capabilities/v0.31.0.json b/third_party/opa/capabilities/v0.31.0.json new file mode 100644 index 000000000000..7aec357e8b0d --- /dev/null +++ b/third_party/opa/capabilities/v0.31.0.json @@ -0,0 +1,3512 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.32.0.json b/third_party/opa/capabilities/v0.32.0.json new file mode 100644 index 000000000000..7aec357e8b0d --- /dev/null +++ b/third_party/opa/capabilities/v0.32.0.json @@ -0,0 +1,3512 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.32.1.json b/third_party/opa/capabilities/v0.32.1.json new file mode 100644 index 000000000000..7aec357e8b0d --- /dev/null +++ b/third_party/opa/capabilities/v0.32.1.json @@ -0,0 +1,3512 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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.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": [ + { + "of": { + "type": "string" + }, + "type": "set" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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": "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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "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" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "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" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "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_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": "numbers.range", + "decl": { + "args": [ + { + "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" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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": [ + { + "of": { + "type": "number" + }, + "type": "set" + }, + { + "dynamic": { + "type": "number" + }, + "type": "array" + } + ], + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.33.0.json b/third_party/opa/capabilities/v0.33.0.json new file mode 100644 index 000000000000..cc391d8ae16d --- /dev/null +++ b/third_party/opa/capabilities/v0.33.0.json @@ -0,0 +1,3534 @@ +{ + "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.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.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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": "numbers.range", + "decl": { + "args": [ + { + "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.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.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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.33.1.json b/third_party/opa/capabilities/v0.33.1.json new file mode 100644 index 000000000000..cc391d8ae16d --- /dev/null +++ b/third_party/opa/capabilities/v0.33.1.json @@ -0,0 +1,3534 @@ +{ + "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.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.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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": "numbers.range", + "decl": { + "args": [ + { + "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.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.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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "product", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.34.0.json b/third_party/opa/capabilities/v0.34.0.json new file mode 100644 index 000000000000..91f6512ff0c2 --- /dev/null +++ b/third_party/opa/capabilities/v0.34.0.json @@ -0,0 +1,3602 @@ +{ + "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.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.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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": "numbers.range", + "decl": { + "args": [ + { + "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.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.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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.34.1.json b/third_party/opa/capabilities/v0.34.1.json new file mode 100644 index 000000000000..91f6512ff0c2 --- /dev/null +++ b/third_party/opa/capabilities/v0.34.1.json @@ -0,0 +1,3602 @@ +{ + "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.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.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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": "numbers.range", + "decl": { + "args": [ + { + "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.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.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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.34.2.json b/third_party/opa/capabilities/v0.34.2.json new file mode 100644 index 000000000000..91f6512ff0c2 --- /dev/null +++ b/third_party/opa/capabilities/v0.34.2.json @@ -0,0 +1,3602 @@ +{ + "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.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.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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": "numbers.range", + "decl": { + "args": [ + { + "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.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.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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.35.0.json b/third_party/opa/capabilities/v0.35.0.json new file mode 100644 index 000000000000..612db5adbbf0 --- /dev/null +++ b/third_party/opa/capabilities/v0.35.0.json @@ -0,0 +1,3619 @@ +{ + "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.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.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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.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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.36.0.json b/third_party/opa/capabilities/v0.36.0.json new file mode 100644 index 000000000000..bedf071665ac --- /dev/null +++ b/third_party/opa/capabilities/v0.36.0.json @@ -0,0 +1,3721 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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.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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.36.1.json b/third_party/opa/capabilities/v0.36.1.json new file mode 100644 index 000000000000..bedf071665ac --- /dev/null +++ b/third_party/opa/capabilities/v0.36.1.json @@ -0,0 +1,3721 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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.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": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.37.0.json b/third_party/opa/capabilities/v0.37.0.json new file mode 100644 index 000000000000..e8736d8d0f38 --- /dev/null +++ b/third_party/opa/capabilities/v0.37.0.json @@ -0,0 +1,3825 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.37.1.json b/third_party/opa/capabilities/v0.37.1.json new file mode 100644 index 000000000000..e8736d8d0f38 --- /dev/null +++ b/third_party/opa/capabilities/v0.37.1.json @@ -0,0 +1,3825 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.37.2.json b/third_party/opa/capabilities/v0.37.2.json new file mode 100644 index 000000000000..e8736d8d0f38 --- /dev/null +++ b/third_party/opa/capabilities/v0.37.2.json @@ -0,0 +1,3825 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.38.0.json b/third_party/opa/capabilities/v0.38.0.json new file mode 100644 index 000000000000..d5edb2daa7dc --- /dev/null +++ b/third_party/opa/capabilities/v0.38.0.json @@ -0,0 +1,3826 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "every", + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.38.1.json b/third_party/opa/capabilities/v0.38.1.json new file mode 100644 index 000000000000..d5edb2daa7dc --- /dev/null +++ b/third_party/opa/capabilities/v0.38.1.json @@ -0,0 +1,3826 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "every", + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.39.0.json b/third_party/opa/capabilities/v0.39.0.json new file mode 100644 index 000000000000..d5edb2daa7dc --- /dev/null +++ b/third_party/opa/capabilities/v0.39.0.json @@ -0,0 +1,3826 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "every", + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.40.0.json b/third_party/opa/capabilities/v0.40.0.json new file mode 100644 index 000000000000..2f9e6e640932 --- /dev/null +++ b/third_party/opa/capabilities/v0.40.0.json @@ -0,0 +1,3847 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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_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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "every", + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.41.0.json b/third_party/opa/capabilities/v0.41.0.json new file mode 100644 index 000000000000..cf56e6cacd3a --- /dev/null +++ b/third_party/opa/capabilities/v0.41.0.json @@ -0,0 +1,4007 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": [ + "every", + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} diff --git a/third_party/opa/capabilities/v0.42.0.json b/third_party/opa/capabilities/v0.42.0.json new file mode 100644 index 000000000000..2a57613fb0b3 --- /dev/null +++ b/third_party/opa/capabilities/v0.42.0.json @@ -0,0 +1,4076 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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 + } + ] +} diff --git a/third_party/opa/capabilities/v0.42.1.json b/third_party/opa/capabilities/v0.42.1.json new file mode 100644 index 000000000000..2a57613fb0b3 --- /dev/null +++ b/third_party/opa/capabilities/v0.42.1.json @@ -0,0 +1,4076 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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 + } + ] +} diff --git a/third_party/opa/capabilities/v0.42.2.json b/third_party/opa/capabilities/v0.42.2.json new file mode 100644 index 000000000000..2a57613fb0b3 --- /dev/null +++ b/third_party/opa/capabilities/v0.42.2.json @@ -0,0 +1,4076 @@ +{ + "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.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.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_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_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" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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": [ + { + "of": [ + { + "type": "any" + } + ], + "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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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 + } + ] +} diff --git a/third_party/opa/capabilities/v0.43.0.json b/third_party/opa/capabilities/v0.43.0.json new file mode 100644 index 000000000000..f61c6f85c884 --- /dev/null +++ b/third_party/opa/capabilities/v0.43.0.json @@ -0,0 +1,4079 @@ +{ + "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.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.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_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_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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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 + } + ] +} diff --git a/third_party/opa/capabilities/v0.43.1.json b/third_party/opa/capabilities/v0.43.1.json new file mode 100644 index 000000000000..f61c6f85c884 --- /dev/null +++ b/third_party/opa/capabilities/v0.43.1.json @@ -0,0 +1,4079 @@ +{ + "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.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.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_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_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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": "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" + } + }, + { + "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" + } + }, + { + "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" + } + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "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.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": "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_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" + } + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "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.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" + } + }, + { + "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": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.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.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.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "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.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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 + } + ] +} diff --git a/third_party/opa/capabilities/v0.44.0.json b/third_party/opa/capabilities/v0.44.0.json new file mode 100644 index 000000000000..a44b951193e0 --- /dev/null +++ b/third_party/opa/capabilities/v0.44.0.json @@ -0,0 +1,4190 @@ +{ + "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.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.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_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_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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "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": "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.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": "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_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": "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.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": "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.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.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.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.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 + } + ] +} diff --git a/third_party/opa/capabilities/v0.45.0.json b/third_party/opa/capabilities/v0.45.0.json new file mode 100644 index 000000000000..1d28f21b6561 --- /dev/null +++ b/third_party/opa/capabilities/v0.45.0.json @@ -0,0 +1,4306 @@ +{ + "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.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.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_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_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": "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.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": "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_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": "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.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": "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.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.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.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 + } + ] +} diff --git a/third_party/opa/capabilities/v0.46.0.json b/third_party/opa/capabilities/v0.46.0.json new file mode 100644 index 000000000000..d53a8ebbb516 --- /dev/null +++ b/third_party/opa/capabilities/v0.46.0.json @@ -0,0 +1,4353 @@ +{ + "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.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.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_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_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.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": "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": "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.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": "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.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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.46.1.json b/third_party/opa/capabilities/v0.46.1.json new file mode 100644 index 000000000000..d53a8ebbb516 --- /dev/null +++ b/third_party/opa/capabilities/v0.46.1.json @@ -0,0 +1,4353 @@ +{ + "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.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.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_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_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.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": "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": "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.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": "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.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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.46.2.json b/third_party/opa/capabilities/v0.46.2.json new file mode 100644 index 000000000000..d53a8ebbb516 --- /dev/null +++ b/third_party/opa/capabilities/v0.46.2.json @@ -0,0 +1,4353 @@ +{ + "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.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.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_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_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.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": "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": "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.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": "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.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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.46.3.json b/third_party/opa/capabilities/v0.46.3.json new file mode 100644 index 000000000000..d53a8ebbb516 --- /dev/null +++ b/third_party/opa/capabilities/v0.46.3.json @@ -0,0 +1,4353 @@ +{ + "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.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.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_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_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.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": "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": "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.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": "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.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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.47.0.json b/third_party/opa/capabilities/v0.47.0.json new file mode 100644 index 000000000000..69d7284a112f --- /dev/null +++ b/third_party/opa/capabilities/v0.47.0.json @@ -0,0 +1,4422 @@ +{ + "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.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.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_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_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.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": "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": "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.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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.47.1.json b/third_party/opa/capabilities/v0.47.1.json new file mode 100644 index 000000000000..69d7284a112f --- /dev/null +++ b/third_party/opa/capabilities/v0.47.1.json @@ -0,0 +1,4422 @@ +{ + "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.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.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_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_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.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": "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": "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.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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.47.2.json b/third_party/opa/capabilities/v0.47.2.json new file mode 100644 index 000000000000..69d7284a112f --- /dev/null +++ b/third_party/opa/capabilities/v0.47.2.json @@ -0,0 +1,4422 @@ +{ + "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.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.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_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_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.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": "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": "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.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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.47.3.json b/third_party/opa/capabilities/v0.47.3.json new file mode 100644 index 000000000000..69d7284a112f --- /dev/null +++ b/third_party/opa/capabilities/v0.47.3.json @@ -0,0 +1,4422 @@ +{ + "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.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.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_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_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.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": "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": "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.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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.47.4.json b/third_party/opa/capabilities/v0.47.4.json new file mode 100644 index 000000000000..69d7284a112f --- /dev/null +++ b/third_party/opa/capabilities/v0.47.4.json @@ -0,0 +1,4422 @@ +{ + "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.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.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_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_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.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": "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": "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.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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.48.0.json b/third_party/opa/capabilities/v0.48.0.json new file mode 100644 index 000000000000..a0746f5c1395 --- /dev/null +++ b/third_party/opa/capabilities/v0.48.0.json @@ -0,0 +1,4466 @@ +{ + "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.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.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_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_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.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": "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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.49.0.json b/third_party/opa/capabilities/v0.49.0.json new file mode 100644 index 000000000000..a0746f5c1395 --- /dev/null +++ b/third_party/opa/capabilities/v0.49.0.json @@ -0,0 +1,4466 @@ +{ + "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.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.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_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_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.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": "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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.49.1.json b/third_party/opa/capabilities/v0.49.1.json new file mode 100644 index 000000000000..a0746f5c1395 --- /dev/null +++ b/third_party/opa/capabilities/v0.49.1.json @@ -0,0 +1,4466 @@ +{ + "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.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.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_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_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.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": "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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.49.2.json b/third_party/opa/capabilities/v0.49.2.json new file mode 100644 index 000000000000..a0746f5c1395 --- /dev/null +++ b/third_party/opa/capabilities/v0.49.2.json @@ -0,0 +1,4466 @@ +{ + "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.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.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_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_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.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": "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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.50.0.json b/third_party/opa/capabilities/v0.50.0.json new file mode 100644 index 000000000000..4fe0c2b7fec4 --- /dev/null +++ b/third_party/opa/capabilities/v0.50.0.json @@ -0,0 +1,4598 @@ +{ + "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.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.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_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_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.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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.50.1.json b/third_party/opa/capabilities/v0.50.1.json new file mode 100644 index 000000000000..4fe0c2b7fec4 --- /dev/null +++ b/third_party/opa/capabilities/v0.50.1.json @@ -0,0 +1,4598 @@ +{ + "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.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.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_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_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.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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.50.2.json b/third_party/opa/capabilities/v0.50.2.json new file mode 100644 index 000000000000..4fe0c2b7fec4 --- /dev/null +++ b/third_party/opa/capabilities/v0.50.2.json @@ -0,0 +1,4598 @@ +{ + "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.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.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_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_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.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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.51.0.json b/third_party/opa/capabilities/v0.51.0.json new file mode 100644 index 000000000000..4fe0c2b7fec4 --- /dev/null +++ b/third_party/opa/capabilities/v0.51.0.json @@ -0,0 +1,4598 @@ +{ + "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.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.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_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_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.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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.52.0.json b/third_party/opa/capabilities/v0.52.0.json new file mode 100644 index 000000000000..bfa43f4a597b --- /dev/null +++ b/third_party/opa/capabilities/v0.52.0.json @@ -0,0 +1,4615 @@ +{ + "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.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_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_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.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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.53.0.json b/third_party/opa/capabilities/v0.53.0.json new file mode 100644 index 000000000000..a89d1f859e83 --- /dev/null +++ b/third_party/opa/capabilities/v0.53.0.json @@ -0,0 +1,4640 @@ +{ + "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.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_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.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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.53.1.json b/third_party/opa/capabilities/v0.53.1.json new file mode 100644 index 000000000000..a89d1f859e83 --- /dev/null +++ b/third_party/opa/capabilities/v0.53.1.json @@ -0,0 +1,4640 @@ +{ + "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.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_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.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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.54.0.json b/third_party/opa/capabilities/v0.54.0.json new file mode 100644 index 000000000000..a89d1f859e83 --- /dev/null +++ b/third_party/opa/capabilities/v0.54.0.json @@ -0,0 +1,4640 @@ +{ + "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.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_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.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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.55.0.json b/third_party/opa/capabilities/v0.55.0.json new file mode 100644 index 000000000000..1b0b80322bec --- /dev/null +++ b/third_party/opa/capabilities/v0.55.0.json @@ -0,0 +1,4665 @@ +{ + "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_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.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": "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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.56.0.json b/third_party/opa/capabilities/v0.56.0.json new file mode 100644 index 000000000000..50b9a1969c2d --- /dev/null +++ b/third_party/opa/capabilities/v0.56.0.json @@ -0,0 +1,4688 @@ +{ + "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_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.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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.57.0.json b/third_party/opa/capabilities/v0.57.0.json new file mode 100644 index 000000000000..0ffa520514fd --- /dev/null +++ b/third_party/opa/capabilities/v0.57.0.json @@ -0,0 +1,4710 @@ +{ + "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_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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.57.1.json b/third_party/opa/capabilities/v0.57.1.json new file mode 100644 index 000000000000..0ffa520514fd --- /dev/null +++ b/third_party/opa/capabilities/v0.57.1.json @@ -0,0 +1,4710 @@ +{ + "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_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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.58.0.json b/third_party/opa/capabilities/v0.58.0.json new file mode 100644 index 000000000000..0ffa520514fd --- /dev/null +++ b/third_party/opa/capabilities/v0.58.0.json @@ -0,0 +1,4710 @@ +{ + "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_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.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.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" + ] +} diff --git a/third_party/opa/capabilities/v0.59.0.json b/third_party/opa/capabilities/v0.59.0.json new file mode 100644 index 000000000000..e38edc9482c6 --- /dev/null +++ b/third_party/opa/capabilities/v0.59.0.json @@ -0,0 +1,4737 @@ +{ + "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_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.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/third_party/opa/capabilities/v0.60.0.json b/third_party/opa/capabilities/v0.60.0.json new file mode 100644 index 000000000000..e38edc9482c6 --- /dev/null +++ b/third_party/opa/capabilities/v0.60.0.json @@ -0,0 +1,4737 @@ +{ + "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_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.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/third_party/opa/capabilities/v0.61.0.json b/third_party/opa/capabilities/v0.61.0.json new file mode 100644 index 000000000000..e38edc9482c6 --- /dev/null +++ b/third_party/opa/capabilities/v0.61.0.json @@ -0,0 +1,4737 @@ +{ + "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_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.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/third_party/opa/capabilities/v0.62.0.json b/third_party/opa/capabilities/v0.62.0.json new file mode 100644 index 000000000000..e38edc9482c6 --- /dev/null +++ b/third_party/opa/capabilities/v0.62.0.json @@ -0,0 +1,4737 @@ +{ + "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_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.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/third_party/opa/capabilities/v0.62.1.json b/third_party/opa/capabilities/v0.62.1.json new file mode 100644 index 000000000000..e38edc9482c6 --- /dev/null +++ b/third_party/opa/capabilities/v0.62.1.json @@ -0,0 +1,4737 @@ +{ + "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_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.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/third_party/opa/capabilities/v0.63.0.json b/third_party/opa/capabilities/v0.63.0.json new file mode 100644 index 000000000000..f3d03f5f4cb7 --- /dev/null +++ b/third_party/opa/capabilities/v0.63.0.json @@ -0,0 +1,4781 @@ +{ + "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.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/third_party/opa/capabilities/v0.64.0.json b/third_party/opa/capabilities/v0.64.0.json new file mode 100644 index 000000000000..06c04773c185 --- /dev/null +++ b/third_party/opa/capabilities/v0.64.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/third_party/opa/capabilities/v0.64.1.json b/third_party/opa/capabilities/v0.64.1.json new file mode 100644 index 000000000000..06c04773c185 --- /dev/null +++ b/third_party/opa/capabilities/v0.64.1.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/third_party/opa/capabilities/v0.65.0.json b/third_party/opa/capabilities/v0.65.0.json new file mode 100644 index 000000000000..06c04773c185 --- /dev/null +++ b/third_party/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/third_party/opa/capabilities/v0.66.0.json b/third_party/opa/capabilities/v0.66.0.json new file mode 100644 index 000000000000..06c04773c185 --- /dev/null +++ b/third_party/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/third_party/opa/capabilities/v0.67.0.json b/third_party/opa/capabilities/v0.67.0.json new file mode 100644 index 000000000000..862a4555f9bf --- /dev/null +++ b/third_party/opa/capabilities/v0.67.0.json @@ -0,0 +1,4843 @@ +{ + "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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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/third_party/opa/capabilities/v0.67.1.json b/third_party/opa/capabilities/v0.67.1.json new file mode 100644 index 000000000000..862a4555f9bf --- /dev/null +++ b/third_party/opa/capabilities/v0.67.1.json @@ -0,0 +1,4843 @@ +{ + "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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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/third_party/opa/capabilities/v0.68.0.json b/third_party/opa/capabilities/v0.68.0.json new file mode 100644 index 000000000000..862a4555f9bf --- /dev/null +++ b/third_party/opa/capabilities/v0.68.0.json @@ -0,0 +1,4843 @@ +{ + "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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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/third_party/opa/capabilities/v0.69.0.json b/third_party/opa/capabilities/v0.69.0.json new file mode 100644 index 000000000000..862a4555f9bf --- /dev/null +++ b/third_party/opa/capabilities/v0.69.0.json @@ -0,0 +1,4843 @@ +{ + "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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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/third_party/opa/capabilities/v0.70.0.json b/third_party/opa/capabilities/v0.70.0.json new file mode 100644 index 000000000000..862a4555f9bf --- /dev/null +++ b/third_party/opa/capabilities/v0.70.0.json @@ -0,0 +1,4843 @@ +{ + "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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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/third_party/opa/capabilities/v1.0.0.json b/third_party/opa/capabilities/v1.0.0.json new file mode 100644 index 000000000000..48a87b0c35d7 --- /dev/null +++ b/third_party/opa/capabilities/v1.0.0.json @@ -0,0 +1,4835 @@ +{ + "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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/v1.0.1.json b/third_party/opa/capabilities/v1.0.1.json new file mode 100644 index 000000000000..48a87b0c35d7 --- /dev/null +++ b/third_party/opa/capabilities/v1.0.1.json @@ -0,0 +1,4835 @@ +{ + "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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/v1.1.0.json b/third_party/opa/capabilities/v1.1.0.json new file mode 100644 index 000000000000..48a87b0c35d7 --- /dev/null +++ b/third_party/opa/capabilities/v1.1.0.json @@ -0,0 +1,4835 @@ +{ + "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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/v1.2.0.json b/third_party/opa/capabilities/v1.2.0.json new file mode 100644 index 000000000000..1253c88b307e --- /dev/null +++ b/third_party/opa/capabilities/v1.2.0.json @@ -0,0 +1,4849 @@ +{ + "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": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/v1.3.0.json b/third_party/opa/capabilities/v1.3.0.json new file mode 100644 index 000000000000..1253c88b307e --- /dev/null +++ b/third_party/opa/capabilities/v1.3.0.json @@ -0,0 +1,4849 @@ +{ + "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": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/v1.4.0.json b/third_party/opa/capabilities/v1.4.0.json new file mode 100644 index 000000000000..1253c88b307e --- /dev/null +++ b/third_party/opa/capabilities/v1.4.0.json @@ -0,0 +1,4849 @@ +{ + "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": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/v1.4.1.json b/third_party/opa/capabilities/v1.4.1.json new file mode 100644 index 000000000000..1253c88b307e --- /dev/null +++ b/third_party/opa/capabilities/v1.4.1.json @@ -0,0 +1,4849 @@ +{ + "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": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/v1.4.2.json b/third_party/opa/capabilities/v1.4.2.json new file mode 100644 index 000000000000..1253c88b307e --- /dev/null +++ b/third_party/opa/capabilities/v1.4.2.json @@ -0,0 +1,4849 @@ +{ + "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": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/v1.5.0.json b/third_party/opa/capabilities/v1.5.0.json new file mode 100644 index 000000000000..1253c88b307e --- /dev/null +++ b/third_party/opa/capabilities/v1.5.0.json @@ -0,0 +1,4849 @@ +{ + "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": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/v1.5.1.json b/third_party/opa/capabilities/v1.5.1.json new file mode 100644 index 000000000000..1253c88b307e --- /dev/null +++ b/third_party/opa/capabilities/v1.5.1.json @@ -0,0 +1,4849 @@ +{ + "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": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/v1.6.0.json b/third_party/opa/capabilities/v1.6.0.json new file mode 100644 index 000000000000..110c3eca9195 --- /dev/null +++ b/third_party/opa/capabilities/v1.6.0.json @@ -0,0 +1,4850 @@ +{ + "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": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "keywords_in_refs", + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/v1.7.0.json b/third_party/opa/capabilities/v1.7.0.json new file mode 100644 index 000000000000..110c3eca9195 --- /dev/null +++ b/third_party/opa/capabilities/v1.7.0.json @@ -0,0 +1,4850 @@ +{ + "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": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "keywords_in_refs", + "rego_v1" + ] +} diff --git a/third_party/opa/capabilities/v1.7.1.json b/third_party/opa/capabilities/v1.7.1.json new file mode 100644 index 000000000000..110c3eca9195 --- /dev/null +++ b/third_party/opa/capabilities/v1.7.1.json @@ -0,0 +1,4850 @@ +{ + "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": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_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.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "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" + } + } + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "keywords_in_refs", + "rego_v1" + ] +} diff --git a/third_party/opa/cmd/bench.go b/third_party/opa/cmd/bench.go new file mode 100644 index 000000000000..b0bb908a5c4f --- /dev/null +++ b/third_party/opa/cmd/bench.go @@ -0,0 +1,692 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "net/http" + "os" + "strconv" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + + "github.com/open-policy-agent/opa/v1/server/types" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/runtime" + + "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/cmd/internal/env" + "github.com/open-policy-agent/opa/internal/presentation" + "github.com/open-policy-agent/opa/v1/compile" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/util" +) + +// benchmarkCommandParams are a superset of evalCommandParams +// but not all eval options are exposed with flags. Only the +// ones compatible with running a benchmark. +type benchmarkCommandParams struct { + evalCommandParams + benchMem bool + count int + e2e bool + gracefulShutdownPeriod int + shutdownWaitPeriod int + configFile string +} + +func newBenchmarkEvalParams() benchmarkCommandParams { + return benchmarkCommandParams{ + evalCommandParams: evalCommandParams{ + outputFormat: formats.Flag(formats.Pretty, formats.JSON, formats.GoBench), + target: util.NewEnumFlag(compile.TargetRego, []string{compile.TargetRego, compile.TargetWasm}), + schema: &schemaFlags{}, + capabilities: newCapabilitiesFlag(), + }, + gracefulShutdownPeriod: 10, + } +} + +func initBench(root *cobra.Command, brand string) { + executable := root.Name() + + params := newBenchmarkEvalParams() + + benchCommand := &cobra.Command{ + Use: "bench ", + Short: "Benchmark a Rego query", + Long: `Benchmark a Rego query and print the results. + +The benchmark command works very similar to 'eval' and will evaluate the query in the same fashion. The +evaluation will be repeated a number of times and performance results will be returned. + +Example with bundle and input data: + + ` + executable + ` bench -b ./policy-bundle -i input.json 'data.authz.allow' + +To run benchmarks against a running ` + brand + ` server to evaluate server overhead use the --e2e flag. +To enable more detailed analysis use the --metrics and --benchmem flags. + +The optional "gobench" output format conforms to the Go Benchmark Data Format. +`, + + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := env.CmdFlags.CheckEnvironmentVariables(cmd); err != nil { + return err + } + return validateEvalParams(¶ms.evalCommandParams, args) + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + exit, err := benchMain(args, params, os.Stdout, &goBenchRunner{}) + if err != nil { + // NOTE: err should only be non-nil if a (highly unlikely) + // presentation error occurs. + fmt.Fprintf(os.Stderr, "error: %v\n", err) + } + if exit != 0 { + return newExitError(exit) + } + return nil + }, + } + + // Sub-set of the standard `opa eval ..` flags + addPartialFlag(benchCommand.Flags(), ¶ms.partial, false) + addUnknownsFlag(benchCommand.Flags(), ¶ms.unknowns, []string{"input"}) + addFailFlag(benchCommand.Flags(), ¶ms.fail, true) + addDataFlag(benchCommand.Flags(), ¶ms.dataPaths) + addBundleFlag(benchCommand.Flags(), ¶ms.bundlePaths) + addInputFlag(benchCommand.Flags(), ¶ms.inputPath) + addImportFlag(benchCommand.Flags(), ¶ms.imports) + addPackageFlag(benchCommand.Flags(), ¶ms.pkg) + addQueryStdinFlag(benchCommand.Flags(), ¶ms.stdin) + addInputStdinFlag(benchCommand.Flags(), ¶ms.stdinInput) + addMetricsFlag(benchCommand.Flags(), ¶ms.metrics, true) + addOutputFormat(benchCommand.Flags(), params.outputFormat) + addIgnoreFlag(benchCommand.Flags(), ¶ms.ignore) + addSchemaFlags(benchCommand.Flags(), params.schema) + addTargetFlag(benchCommand.Flags(), params.target) + addV0CompatibleFlag(benchCommand.Flags(), ¶ms.v0Compatible, false) + addV1CompatibleFlag(benchCommand.Flags(), ¶ms.v1Compatible, false) + addReadAstValuesFromStoreFlag(benchCommand.Flags(), ¶ms.ReadAstValuesFromStore, false) + + // Shared benchmark flags + addCountFlag(benchCommand.Flags(), ¶ms.count, "benchmark") + addBenchmemFlag(benchCommand.Flags(), ¶ms.benchMem, true) + + addE2EFlag(benchCommand.Flags(), ¶ms.e2e, false, brand) + addConfigFileFlag(benchCommand.Flags(), ¶ms.configFile) + + benchCommand.Flags().IntVar(¶ms.gracefulShutdownPeriod, "shutdown-grace-period", 10, "set the time (in seconds) that the server will wait to gracefully shut down. This flag is valid in 'e2e' mode only.") + benchCommand.Flags().IntVar(¶ms.shutdownWaitPeriod, "shutdown-wait-period", 0, "set the time (in seconds) that the server will wait before initiating shutdown. This flag is valid in 'e2e' mode only.") + + root.AddCommand(benchCommand) +} + +type benchRunner interface { + run(ctx context.Context, ectx *evalContext, params benchmarkCommandParams, f func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) +} + +func benchMain(args []string, params benchmarkCommandParams, w io.Writer, r benchRunner) (int, error) { + + ctx := context.Background() + + if params.e2e { + err := benchE2E(ctx, args, params, w) + if err != nil { + errRender := renderBenchmarkError(params, err, w) + return 1, errRender + } + return 0, nil + } + + ectx, err := setupEval(args, params.evalCommandParams) + if err != nil { + errRender := renderBenchmarkError(params, err, w) + return 1, errRender + } + + resultHandler := rego.GenerateJSON(func(*ast.Term, *rego.EvalContext) (any, error) { + // Do nothing with the result, as we are only interested in benchmarking evaluation — + // not the potentially slow process of rendering the result. + // Undefined / empty results will still be handled normally (fail the benchmark unless --fail + // is set to false). + return nil, nil + }) + + ectx.regoArgs = append(ectx.regoArgs, resultHandler) + + var benchFunc func(context.Context, ...rego.EvalOption) error + rg := rego.New(ectx.regoArgs...) + + if !params.partial { + // Take the eval context and prepare anything else we possible can before benchmarking the evaluation + pq, err := rg.PrepareForEval(ctx) + if err != nil { + errRender := renderBenchmarkError(params, err, w) + return 1, errRender + } + + benchFunc = func(ctx context.Context, opts ...rego.EvalOption) error { + result, err := pq.Eval(ctx, opts...) + if err != nil { + return err + } else if len(result) == 0 && params.fail { + return errors.New("undefined result") + } + return nil + } + } else { + // As with normal evaluation, prepare as much as possible up front. + pq, err := rg.PrepareForPartial(ctx) + if err != nil { + errRender := renderBenchmarkError(params, err, w) + return 1, errRender + } + + benchFunc = func(ctx context.Context, opts ...rego.EvalOption) error { + result, err := pq.Partial(ctx, opts...) + if err != nil { + return err + } else if len(result.Queries) == 0 && params.fail { + return errors.New("undefined result") + } + return nil + } + } + + // Run the benchmark as many times as specified, re-use the prepared objects for each + for range params.count { + br, err := r.run(ctx, ectx, params, benchFunc) + if err != nil { + errRender := renderBenchmarkError(params, err, w) + return 1, errRender + } + renderBenchmarkResult(params, br, w) + } + + return 0, nil +} + +type goBenchRunner struct { +} + +func (*goBenchRunner) run(ctx context.Context, ectx *evalContext, params benchmarkCommandParams, f func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) { + + var hist, m metrics.Metrics + if params.metrics { + hist = metrics.New() + m = metrics.New() + } + + ectx.evalArgs = append(ectx.evalArgs, rego.EvalMetrics(m)) + + var benchErr error + + br := testing.Benchmark(func(b *testing.B) { + + // Track memory allocations, if enabled + if params.benchMem { + b.ReportAllocs() + } + + // Reset the histogram for each invocation of the bench function + if params.metrics { + hist.Clear() + } + + b.ResetTimer() + for range b.N { + + // Start the timer (might already be started, but that's ok) + b.StartTimer() + + // Perform the evaluation + err := f(ctx, ectx.evalArgs...) + + // Stop the timer while processing the results + b.StopTimer() + if err != nil { + benchErr = err + b.FailNow() + } + + // Add metrics for that evaluation into the top level histogram + if params.metrics { + for name, metric := range m.All() { + // Note: We only support int64 metrics right now, this should cover pretty + // much all the ones we would care about (timers and counters). + switch v := metric.(type) { + case int64: + hist.Histogram(name).Update(v) + } + } + m.Clear() + } + } + + if params.metrics { + reportMetrics(b, hist.All()) + } + }) + + return br, benchErr +} + +func benchE2E(ctx context.Context, args []string, params benchmarkCommandParams, w io.Writer) error { + host := "localhost" + port := 0 + + logger := logging.New() + logger.SetLevel(logging.Error) + + paths := params.dataPaths.v + if len(params.bundlePaths.v) > 0 { + paths = append(paths, params.bundlePaths.v...) + } + + // Because of test concurrency, several instances of this function can be + // running simultaneously, which will result in occasional collisions when + // two goroutines wish to bind the same port for the runtime. + // We fix the issue here by binding port 0; this will result in the OS + // allocating us an open port. + rtParams := runtime.Params{ + Addrs: &[]string{host + ":0"}, + Paths: paths, + Logger: logger, + BundleMode: params.bundlePaths.isFlagSet(), + SkipBundleVerification: true, + EnableVersionCheck: false, + GracefulShutdownPeriod: params.gracefulShutdownPeriod, + ShutdownWaitPeriod: params.shutdownWaitPeriod, + ConfigFile: params.configFile, + V0Compatible: params.v0Compatible, + V1Compatible: params.v1Compatible, + } + + rt, err := runtime.NewRuntime(ctx, rtParams) + if err != nil { + return err + } + + cctx, cancel := context.WithCancel(ctx) + defer cancel() + + initChannel := rt.Manager.ServerInitializedChannel() + + done := make(chan error) + go func() { + done <- rt.Serve(cctx) + }() + + select { + case err := <-done: + if err != nil { + return err + } + case <-initChannel: + break + } + + // Busy loop until server has truly come online to recover the bound port. + // We do this with exponential backoff for wait times, since the server + // typically comes online very quickly. + baseDelay := time.Duration(100) * time.Millisecond + maxDelay := time.Duration(60) * time.Second + retries := 3 // Max of around 1 minute total wait time. + for i := range retries { + if len(rt.Addrs()) == 0 { + delay := util.DefaultBackoff(float64(baseDelay), float64(maxDelay), i) + time.Sleep(delay) + continue + } + // We have an address to parse the port from. + port, err = strconv.Atoi(strings.Split(rt.Addrs()[0], ":")[1]) + if err != nil { + return err + } + break + } + // Check for port still being unbound after retry loop. + if port == 0 { + return errors.New("unable to bind a port for bench testing") + } + + query, err := readQuery(params, args) + if err != nil { + return err + } + + input, err := readInputBytes(params.evalCommandParams) + if err != nil { + return err + } + + // Wrap input in "input" attribute + inp := make(map[string]any) + + if input != nil { + if err = util.Unmarshal(input, &inp); err != nil { + return err + } + } + + body := map[string]any{"input": inp} + + var path string + if params.partial { + path = "compile" + body["query"] = query + if len(params.unknowns) > 0 { + body["unknowns"] = params.unknowns + } + } else { + _, err := ast.ParseBody(query) + if err != nil { + return errors.New("error occurred while parsing query") + } + + if strings.HasPrefix(query, "data.") { + path = strings.ReplaceAll(query, ".", "/") + } else { + path = "query" + body["query"] = query + } + } + + url := fmt.Sprintf("http://%s:%d/v1/%v", host, port, path) + if params.metrics { + url += "?metrics=true" + } + + for range params.count { + br, err := runE2E(params, url, body) + if err != nil { + return err + } + renderBenchmarkResult(params, br, w) + } + return nil +} + +func runE2E(params benchmarkCommandParams, url string, input map[string]any) (testing.BenchmarkResult, error) { + hist := metrics.New() + + var benchErr error + + br := testing.Benchmark(func(b *testing.B) { + + // Track memory allocations, if enabled + if params.benchMem { + b.ReportAllocs() + } + + // Reset the histogram for each invocation of the bench function + hist.Clear() + + b.ResetTimer() + for range b.N { + + // Start the timer + b.StartTimer() + + // Execute the query API call + m, err := e2eQuery(params, url, input) + + // Stop the timer while processing the results + b.StopTimer() + if err != nil { + benchErr = err + b.FailNow() + } + + // Add metrics for that evaluation into the top level histogram + if params.metrics { + for name, metric := range m { + switch v := metric.(type) { + case json.Number: + num, err := v.Int64() + if err != nil { + benchErr = err + b.FailNow() + } + hist.Histogram(name).Update(num) + + } + } + } + } + + if params.metrics { + reportMetrics(b, hist.All()) + } + }) + + return br, benchErr +} + +func e2eQuery(params benchmarkCommandParams, url string, input map[string]any) (types.MetricsV1, error) { + + reqBody, err := json.Marshal(input) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", url, bytes.NewReader(reqBody)) + if err != nil { + return nil, fmt.Errorf("unexpected error: %w", err) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != 200 { + var e map[string]any + if err = util.Unmarshal(body, &e); err != nil { + return nil, err + } + + if _, ok := e["errors"]; !ok { + return nil, fmt.Errorf("request failed, OPA server replied with HTTP %v: %v", resp.StatusCode, e["message"]) + } + + bs, err := json.Marshal(e["errors"]) + if err != nil { + return nil, err + } + + var astErrs ast.Errors + if err = util.Unmarshal(bs, &astErrs); err != nil { + // ignore err + return nil, fmt.Errorf("request failed, OPA server replied with HTTP %v: %v", resp.StatusCode, e["message"]) + } + + return nil, astErrs + } + + if !params.partial { + var result types.DataResponseV1 + if err = util.Unmarshal(body, &result); err != nil { + return nil, err + } + + if result.Result == nil && params.fail { + return nil, errors.New("undefined result") + } + + return result.Metrics, nil + } + + var result types.CompileResponseV1 + if err = util.Unmarshal(body, &result); err != nil { + return nil, err + } + + if params.fail { + if result.Result == nil { + return nil, errors.New("undefined result") + } + + i := *result.Result + + peResult, ok := i.(map[string]any) + if !ok { + return nil, errors.New("invalid result for compile response") + } + + if len(peResult) == 0 { + return nil, errors.New("undefined result") + } + + if val, ok := peResult["queries"]; ok { + queries, ok := val.([]any) + if !ok { + return nil, errors.New("invalid result for output of partial evaluation") + } + + if len(queries) == 0 { + return nil, errors.New("undefined result") + } + } else { + return nil, errors.New("invalid result for output of partial evaluation") + } + } + + return result.Metrics, nil +} + +func readQuery(params benchmarkCommandParams, args []string) (string, error) { + var query string + if params.stdin { + bs, err := io.ReadAll(os.Stdin) + if err != nil { + return "", err + } + query = string(bs) + } else { + query = args[0] + } + return query, nil +} + +func renderBenchmarkResult(params benchmarkCommandParams, br testing.BenchmarkResult, w io.Writer) { + switch params.outputFormat.String() { + case formats.JSON: + _ = presentation.JSON(w, br) + case formats.GoBench: + fmt.Fprintf(w, "BenchmarkOPAEval\t%s", br.String()) + if params.benchMem { + fmt.Fprintf(w, "\t%s", br.MemString()) + } + fmt.Fprintf(w, "\n") + default: + data := [][]string{ + {"samples", strconv.Itoa(br.N)}, + {"ns/op", prettyFormatFloat(float64(br.T.Nanoseconds()) / float64(br.N))}, + } + if params.benchMem { + data = append(data, []string{ + "B/op", strconv.FormatInt(br.AllocedBytesPerOp(), 10), + }, []string{ + "allocs/op", strconv.FormatInt(br.AllocsPerOp(), 10), + }) + } + + for _, k := range util.KeysSorted(br.Extra) { + data = append(data, []string{k, prettyFormatFloat(br.Extra[k])}) + } + + table := tablewriter.NewWriter(w) + table.AppendBulk(data) + table.Render() + } +} + +func renderBenchmarkError(params benchmarkCommandParams, err error, w io.Writer) error { + o := presentation.Output{ + Errors: presentation.NewOutputErrors(err), + } + + switch params.outputFormat.String() { + case formats.JSON: + return presentation.JSON(w, o) + default: + return presentation.Pretty(w, o) + } +} + +// Same format used by testing/benchmark.go to format floating point output strings +// Using this keeps the results consistent between the "pretty" and "gobench" outputs. +func prettyFormatFloat(x float64) string { + // Print all numbers with 10 places before the decimal point + // and small numbers with three sig figs. + var format string + switch y := math.Abs(x); { + case y == 0 || y >= 99.95: + format = "%10.0f" + case y >= 9.995: + format = "%12.1f" + case y >= 0.9995: + format = "%13.2f" + case y >= 0.09995: + format = "%14.3f" + case y >= 0.009995: + format = "%15.4f" + case y >= 0.0009995: + format = "%16.5f" + default: + format = "%17.6f" + } + return fmt.Sprintf(format, x) +} + +func reportMetrics(b *testing.B, m map[string]any) { + // For each histogram add their values to the benchmark results. + // Note: If there are many metrics this gets super verbose. + for histName, metric := range m { + histValues, ok := metric.(map[string]any) + if !ok { + continue + } + for metricName, rawValue := range histValues { + unit := fmt.Sprintf("%s_%s", histName, metricName) + + // Only support histogram metrics that are a float64 or int64, + // this covers the stock implementation in metrics.Metrics + switch v := rawValue.(type) { + case int64: + b.ReportMetric(float64(v), unit) + case float64: + b.ReportMetric(v, unit) + } + } + } +} diff --git a/third_party/opa/cmd/bench_test.go b/third_party/opa/cmd/bench_test.go new file mode 100644 index 000000000000..8d89ad5c222d --- /dev/null +++ b/third_party/opa/cmd/bench_test.go @@ -0,0 +1,1568 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "maps" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/internal/presentation" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +// Minimize the number of tests that *actually* run the benchmarks, they are pretty slow. +// Have one test that exercises the whole flow. +func TestRunBenchmark(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + params := testBenchParams() + + args := []string{"1 + 1"} + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + + // Expect a json serialized benchmark result with histogram fields + var br testing.BenchmarkResult + err = util.UnmarshalJSON(buf.Bytes(), &br) + if err != nil { + t.Fatalf("Unexpected error unmarshalling output: %s", err) + } + + if br.N == 0 || br.T == 0 || br.MemAllocs == 0 || br.MemBytes == 0 { + t.Fatalf("Expected benchmark results to be non-zero, got: %+v", br) + } + + if _, ok := br.Extra["histogram_timer_rego_query_eval_ns_count"]; !ok { + t.Fatalf("Expected benchmark results to contain histogram_timer_rego_query_eval_ns_count, got: %+v", br) + } + + if float64(br.N) != br.Extra["histogram_timer_rego_query_eval_ns_count"] { + t.Fatalf("Expected 'histogram_timer_rego_query_eval_ns_count' to be equal to N") + } +} + +func TestRunBenchmarkWithQueryImport(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + params := testBenchParams() + // We add the rego.v1 import .. + params.imports = newrepeatedStringFlag([]string{"rego.v1"}) + + // .. which provides the 'in' keyword + args := []string{`"a" in ["a", "b", "c"]`} + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + + // Expect a json serialized benchmark result with histogram fields + var br testing.BenchmarkResult + err = util.UnmarshalJSON(buf.Bytes(), &br) + if err != nil { + t.Fatalf("Unexpected error unmarshalling output: %s", err) + } + + if br.N == 0 || br.T == 0 || br.MemAllocs == 0 || br.MemBytes == 0 { + t.Fatalf("Expected benchmark results to be non-zero, got: %+v", br) + } + + if _, ok := br.Extra["histogram_timer_rego_query_eval_ns_count"]; !ok { + t.Fatalf("Expected benchmark results to contain histogram_timer_rego_query_eval_ns_count, got: %+v", br) + } + + if float64(br.N) != br.Extra["histogram_timer_rego_query_eval_ns_count"] { + t.Fatalf("Expected 'histogram_timer_rego_query_eval_ns_count' to be equal to N") + } +} + +func TestRunBenchmarkE2E(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + params := testBenchParams() + params.e2e = true + + args := []string{"1 + 1"} + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + + // Expect a json serialized benchmark result with histogram fields + var br testing.BenchmarkResult + err = util.UnmarshalJSON(buf.Bytes(), &br) + if err != nil { + t.Fatalf("Unexpected error unmarshalling output: %s", err) + } + + if br.N == 0 || br.T == 0 || br.MemAllocs == 0 || br.MemBytes == 0 { + t.Fatalf("Expected benchmark results to be non-zero, got: %+v", br) + } + + if _, ok := br.Extra["histogram_timer_rego_query_eval_ns_count"]; !ok { + t.Fatalf("Expected benchmark results to contain 'histogram_timer_rego_query_eval_ns_count', got: %+v", br) + } + + if float64(br.N) != br.Extra["histogram_timer_rego_query_eval_ns_count"] { + t.Fatalf("Expected 'histogram_timer_rego_query_eval_ns_count' to be equal to N") + } + + if _, ok := br.Extra["histogram_timer_server_handler_ns_count"]; !ok { + t.Fatalf("Expected benchmark results to contain 'histogram_timer_server_handler_ns_count', got: %+v", br) + } + + if float64(br.N) != br.Extra["histogram_timer_server_handler_ns_count"] { + t.Fatalf("Expected 'histogram_timer_server_handler_ns_count' to be equal to N") + } +} + +func TestRunBenchmarkE2EWithOPAConfigFile(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + fs := map[string]string{ + "/config.yaml": `{"decision_logs": {"console": true}}`, + } + + test.WithTempFS(fs, func(testDirRoot string) { + + params := testBenchParams() + params.e2e = true + params.configFile = filepath.Join(testDirRoot, "config.yaml") + + args := []string{"1 + 1"} + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + + // Expect a json serialized benchmark result with histogram fields + var br testing.BenchmarkResult + err = util.UnmarshalJSON(buf.Bytes(), &br) + if err != nil { + t.Fatalf("Unexpected error unmarshalling output: %s", err) + } + + if br.N == 0 || br.T == 0 || br.MemAllocs == 0 || br.MemBytes == 0 { + t.Fatalf("Expected benchmark results to be non-zero, got: %+v", br) + } + + if _, ok := br.Extra["histogram_timer_rego_query_eval_ns_count"]; !ok { + t.Fatalf("Expected benchmark results to contain 'histogram_timer_rego_query_eval_ns_count', got: %+v", br) + } + + if float64(br.N) != br.Extra["histogram_timer_rego_query_eval_ns_count"] { + t.Fatalf("Expected 'histogram_timer_rego_query_eval_ns_count' to be equal to N") + } + + if _, ok := br.Extra["histogram_timer_server_handler_ns_count"]; !ok { + t.Fatalf("Expected benchmark results to contain 'histogram_timer_server_handler_ns_count', got: %+v", br) + } + + if float64(br.N) != br.Extra["histogram_timer_server_handler_ns_count"] { + t.Fatalf("Expected 'histogram_timer_server_handler_ns_count' to be equal to N") + } + }) +} + +func TestRunBenchmarkFailFastE2E(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + params := testBenchParams() + params.fail = true // configured to fail on undefined results + params.e2e = true + + args := []string{"a := 1; a > 2"} + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 1 { + t.Fatalf("Unexpected return code %d, expected 1", rc) + } + + // Expect a json serialized benchmark result with histogram fields + var pr presentation.Output + err = util.UnmarshalJSON(buf.Bytes(), &pr) + if err != nil { + t.Fatalf("Unexpected error unmarshalling output: %s", err) + } + + if len(pr.Errors) != 1 { + t.Fatalf("Expected 1 error in result, got:\n\n%s\n", buf.String()) + } +} + +func TestBenchPartialE2E(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + params := testBenchParams() + params.partial = true + params.fail = true + params.e2e = true + params.unknowns = []string{"input"} + args := []string{"input.x > 0"} + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + + var br testing.BenchmarkResult + err = util.UnmarshalJSON(buf.Bytes(), &br) + if err != nil { + t.Fatalf("Unexpected error unmarshalling output: %s", err) + } + + if br.N == 0 || br.T == 0 || br.MemAllocs == 0 || br.MemBytes == 0 { + t.Fatalf("Expected benchmark results to be non-zero, got: %+v", br) + } + + if _, ok := br.Extra["histogram_timer_rego_partial_eval_ns_count"]; !ok { + t.Fatalf("Expected benchmark results to contain 'histogram_timer_rego_partial_eval_ns_count', got: %+v", br) + } + + if float64(br.N) != br.Extra["histogram_timer_rego_partial_eval_ns_count"] { + t.Fatalf("Expected 'histogram_timer_rego_partial_eval_ns_count' to be equal to N") + } + + if _, ok := br.Extra["histogram_timer_server_handler_ns_count"]; !ok { + t.Fatalf("Expected benchmark results to contain 'histogram_timer_server_handler_ns_count', got: %+v", br) + } + + if float64(br.N) != br.Extra["histogram_timer_server_handler_ns_count"] { + t.Fatalf("Expected 'histogram_timer_server_handler_ns_count' to be equal to N") + } +} + +func TestRunBenchmarkPartialFailFastE2E(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + params := testBenchParams() + params.partial = true + params.unknowns = []string{} + params.fail = true + params.e2e = true + args := []string{"1 == 2"} + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 1 { + t.Fatalf("Unexpected return code %d, expected 1", rc) + } + + actual := buf.String() + expected := `{ + "errors": [ + { + "message": "undefined result" + } + ] +} +` + + if actual != expected { + t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n", expected, actual) + } +} + +func TestRunBenchmarkFailFast(t *testing.T) { + t.Parallel() + + params := testBenchParams() + params.fail = true // configured to fail on undefined results + + args := []string{"a := 1; a > 2"} + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 1 { + t.Fatalf("Unexpected return code %d, expected 1", rc) + } + + // Expect a json serialized benchmark result with histogram fields + var pr presentation.Output + err = util.UnmarshalJSON(buf.Bytes(), &pr) + if err != nil { + t.Fatalf("Unexpected error unmarshalling output: %s", err) + } + + if len(pr.Errors) != 1 { + t.Fatalf("Expected 1 error in result, got:\n\n%s\n", buf.String()) + } +} + +// mockBenchRunner lets us test the bench CLI operations without having to wait ~10 seconds +// while the actual benchmark runner does its thing. +type mockBenchRunner struct { + onRun func(ctx context.Context, ectx *evalContext, params benchmarkCommandParams, f func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) +} + +func (r *mockBenchRunner) run(ctx context.Context, ectx *evalContext, params benchmarkCommandParams, f func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) { + if r.onRun != nil { + return r.onRun(ctx, ectx, params, f) + } + return testing.BenchmarkResult{}, nil +} + +func TestBenchPartial(t *testing.T) { + t.Parallel() + + params := testBenchParams() + params.partial = true + params.fail = true + args := []string{"input=1"} + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &mockBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } +} + +func TestBenchMainErrPreparing(t *testing.T) { + t.Parallel() + + params := testBenchParams() + args := []string{"???"} // query compile error + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &mockBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 1 { + t.Fatalf("Unexpected return code %d, expected 1", rc) + } +} + +func TestBenchMainErrRunningBenchmark(t *testing.T) { + t.Parallel() + + params := testBenchParams() + args := []string{"1+1"} + var buf bytes.Buffer + + mockRunner := &mockBenchRunner{} + mockRunner.onRun = func(_ context.Context, _ *evalContext, _ benchmarkCommandParams, _ func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) { + return testing.BenchmarkResult{}, errors.New("error error error") + } + + rc, err := benchMain(args, params, &buf, mockRunner) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 1 { + t.Fatalf("Unexpected return code %d, expected 1", rc) + } +} + +func TestBenchMainWithCount(t *testing.T) { + t.Parallel() + + params := testBenchParams() + args := []string{"1+1"} + var buf bytes.Buffer + + mockRunner := &mockBenchRunner{} + + params.count = 25 + actualCount := 0 + mockRunner.onRun = func(_ context.Context, _ *evalContext, _ benchmarkCommandParams, _ func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) { + actualCount++ + return testing.BenchmarkResult{}, nil + } + + rc, err := benchMain(args, params, &buf, mockRunner) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + + if actualCount != params.count { + t.Fatalf("Expected benchmark to be run %d times, only ran %d", params.count, actualCount) + } +} + +func TestBenchMainWithNegativeCount(t *testing.T) { + t.Parallel() + + params := testBenchParams() + args := []string{"1+1"} + var buf bytes.Buffer + + mockRunner := &mockBenchRunner{} + + params.count = -1 + actualCount := 0 + mockRunner.onRun = func(_ context.Context, _ *evalContext, _ benchmarkCommandParams, _ func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) { + actualCount++ + return testing.BenchmarkResult{}, nil + } + + rc, err := benchMain(args, params, &buf, mockRunner) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + + if actualCount != 0 { + t.Fatalf("Expected benchmark to not be run, instead ran %d times", actualCount) + } +} + +func validateBenchMainPrep(t *testing.T, args []string, params benchmarkCommandParams) { + t.Helper() + + var buf bytes.Buffer + + mockRunner := &mockBenchRunner{} + + mockRunner.onRun = func(ctx context.Context, ectx *evalContext, _ benchmarkCommandParams, _ func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) { + + // cheat and use the ectx to evalute the query to ensure the input setup on it was valid + r := rego.New(ectx.regoArgs...) + pq, err := r.PrepareForEval(ctx) + if err != nil { + return testing.BenchmarkResult{}, err + } + + rs, err := pq.Eval(ctx, ectx.evalArgs...) + if err != nil { + return testing.BenchmarkResult{}, err + } + + if len(rs) == 0 { + return testing.BenchmarkResult{}, errors.New("expected result, got none") + } + + return testing.BenchmarkResult{}, nil + } + + rc, err := benchMain(args, params, &buf, mockRunner) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } +} + +func TestBenchMainWithJSONInputFile(t *testing.T) { + t.Parallel() + + params := testBenchParams() + files := map[string]string{ + "/input.json": `{"x": 42}`, + } + args := []string{"input.x == 42"} + test.WithTempFS(files, func(path string) { + params.inputPath = filepath.Join(path, "input.json") + + validateBenchMainPrep(t, args, params) + }) +} + +func TestBenchMainWithYAMLInputFile(t *testing.T) { + t.Parallel() + + params := testBenchParams() + files := map[string]string{ + "/input.yaml": `x: 42`, + } + args := []string{"input.x == 42"} + test.WithTempFS(files, func(path string) { + params.inputPath = filepath.Join(path, "input.yaml") + + validateBenchMainPrep(t, args, params) + }) +} + +func TestBenchMainInvalidInputFile(t *testing.T) { + t.Parallel() + + params := testBenchParams() + files := map[string]string{ + "/input.yaml": `x: 42`, + } + args := []string{"1+1"} + test.WithTempFS(files, func(path string) { + params.inputPath = filepath.Join(path, "definitely", "not", "input.yaml") + + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &mockBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + if rc != 1 { + t.Fatalf("Unexpected return code %d, expected 1", rc) + } + }) +} + +func TestBenchMainWithJSONInputFileE2E(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + params := testBenchParams() + params.e2e = true + files := map[string]string{ + "/input.json": `{"x": 42}`, + } + args := []string{"input.x == 42"} + test.WithTempFS(files, func(path string) { + params.inputPath = filepath.Join(path, "input.json") + + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + }) +} + +func TestBenchMainWithYAMLInputFileE2E(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + params := testBenchParams() + params.e2e = true + files := map[string]string{ + "/input.yaml": `x: 42`, + } + args := []string{"input.x == 42"} + test.WithTempFS(files, func(path string) { + params.inputPath = filepath.Join(path, "input.yaml") + + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + }) +} + +func TestBenchMainInvalidInputFileE2E(t *testing.T) { + t.Parallel() + + params := testBenchParams() + params.e2e = true + files := map[string]string{ + "/input.yaml": `x: 42`, + } + args := []string{"1+1"} + test.WithTempFS(files, func(path string) { + params.inputPath = filepath.Join(path, "definitely", "not", "input.yaml") + + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 1 { + t.Fatalf("Unexpected return code %d, expected 1", rc) + } + }) +} + +func TestBenchMainWithBundleData(t *testing.T) { + t.Parallel() + + params := testBenchParams() + + b := testBundle() + + files := map[string]string{ + "bundle.tar.gz": "", + } + + test.WithTempFS(files, func(path string) { + bundlePath := filepath.Join(path, "bundle.tar.gz") + f, err := os.OpenFile(bundlePath, os.O_WRONLY, os.ModePerm) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + err = bundle.Write(f, b) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + err = params.bundlePaths.Set(bundlePath) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + args := []string{"data.a.b.x"} + + validateBenchMainPrep(t, args, params) + }) +} + +func TestBenchMainWithBundleDataE2E(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + params := testBenchParams() + params.e2e = true + + b := testBundle() + + files := map[string]string{ + "bundle.tar.gz": "", + } + + test.WithTempFS(files, func(path string) { + bundlePath := filepath.Join(path, "bundle.tar.gz") + f, err := os.OpenFile(bundlePath, os.O_WRONLY, os.ModePerm) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + err = bundle.Write(f, b) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + err = params.bundlePaths.Set(bundlePath) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + args := []string{"data.a.b.x"} + + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + }) +} + +func TestBenchMainWithDataE2E(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + params := testBenchParams() + params.e2e = true + + mod := `package a.b + import rego.v1 + + x if { + data.a.b.c == 42 + } + ` + + files := map[string]string{ + "p.rego": mod, + } + + test.WithTempFS(files, func(path string) { + err := params.dataPaths.Set(filepath.Join(path, "p.rego")) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + args := []string{"data.a.b.x"} + + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + }) +} + +func TestBenchMainBadQueryE2E(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + params := testBenchParams() + params.e2e = true + args := []string{"foo.bar"} + + var buf bytes.Buffer + + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if rc != 1 { + t.Fatalf("Unexpected return code %d, expected 1", rc) + } +} + +func TestBenchMain_DefaultRegoVersion(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + tests := []struct { + note string + module string + query string + expErrs []string + }{ + // These tests are slow, so we're not being completely exhaustive here. + { + note: "v0 module", + module: `package test +a[x] { + x := 42 +}`, + query: `data.test.a`, + expErrs: []string{ + "mod.rego:2: rego_parse_error: `if` keyword is required before rule body", + "mod.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1 module", + module: `package test +a contains x if { + x := 42 +}`, + query: `data.test.a`, + }, + } + + modes := []struct { + name string + e2e bool + }{ + { + name: "run", + }, + { + name: "e2e", + e2e: true, + }, + } + + for _, mode := range modes { + for _, tc := range tests { + t.Run(fmt.Sprintf("%s, %s", tc.note, mode.name), func(t *testing.T) { + t.Parallel() + + files := map[string]string{ + "mod.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + params := testBenchParams() + _ = params.outputFormat.Set(formats.Pretty) + params.e2e = mode.e2e + + for n := range files { + err := params.dataPaths.Set(filepath.Join(path, n)) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + } + + args := []string{tc.query} + + var buf bytes.Buffer + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + + if len(tc.expErrs) > 0 { + if rc == 0 { + t.Fatalf("Expected non-zero return code") + } + + output := buf.String() + for _, expErr := range tc.expErrs { + if !strings.Contains(output, expErr) { + t.Fatalf("Expected error:\n\n%s\n\ngot:\n\n%s", expErr, output) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + } + }) + }) + } + } +} + +func TestBenchMainCompatibleFlags(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + tests := []struct { + note string + v0Compatible bool + v1Compatible bool + module string + query string + expErrs []string + }{ + // These tests are slow, so we're not being completely exhaustive here. + { + note: "v0, keywords not used", + v0Compatible: true, + module: `package test +a[4] { + 1 == 1 +}`, + query: `data.test.a`, + }, + { + note: "v0, no keywords imported", + v0Compatible: true, + module: `package test +a contains 4 if { + 1 == 1 +}`, + query: `data.test.a`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v1, keywords not used", + v1Compatible: true, + module: `package test +a[4] { + 1 == 1 +}`, + query: `data.test.a`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1, no keywords imported", + v1Compatible: true, + module: `package test +a contains 4 if { + 1 == 1 +}`, + query: `data.test.a`, + }, + { + note: "v0+v1, keywords not used (v0 takes precedence)", + v0Compatible: true, + v1Compatible: true, + module: `package test +a[4] { + 1 == 1 +}`, + query: `data.test.a`, + }, + } + + modes := []struct { + name string + e2e bool + }{ + { + name: "run", + }, + { + name: "e2e", + e2e: true, + }, + } + + for _, mode := range modes { + for _, tc := range tests { + t.Run(fmt.Sprintf("%s, %s", tc.note, mode.name), func(t *testing.T) { + t.Parallel() + + files := map[string]string{ + "mod.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + params := testBenchParams() + _ = params.outputFormat.Set(formats.Pretty) + params.v0Compatible = tc.v0Compatible + params.v1Compatible = tc.v1Compatible + params.e2e = mode.e2e + + for n := range files { + err := params.dataPaths.Set(filepath.Join(path, n)) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + } + + args := []string{tc.query} + + var buf bytes.Buffer + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + + if len(tc.expErrs) > 0 { + if rc == 0 { + t.Fatalf("Expected non-zero return code") + } + + output := buf.String() + for _, expErr := range tc.expErrs { + if !strings.Contains(output, expErr) { + t.Fatalf("Expected error:\n\n%s\n\ngot:\n\n%s", expErr, output) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + } + }) + }) + } + } +} + +func TestBenchMainWithBundleRegoVersion(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + tests := []struct { + note string + bundleRegoVersion int + bundleFileRegoVersions map[string]int + modules map[string]string + query string + expErrs []string + }{ + // These tests are slow, so we're not being completely exhaustive here. + { + note: "v0 bundle", + bundleRegoVersion: 0, + modules: map[string]string{ + "test.rego": `package test +a[4] { + 1 == 1 +}`, + }, + query: `data.test.a`, + }, + { + note: "v0 bundle, no keywords imported", + bundleRegoVersion: 0, + modules: map[string]string{ + "test.rego": `package test +a contains 4 if { + 1 == 1 +}`, + }, + query: `data.test.a`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0 bundle, v1 per-file override", + bundleRegoVersion: 0, + bundleFileRegoVersions: map[string]int{ + "*/test2.rego": 1, + }, + modules: map[string]string{ + "test1.rego": `package test +a[4] { + 1 == 1 +}`, + "test2.rego": `package test +b contains 4 if { + 1 == 1 +}`, + }, + query: `data.test.a`, + }, + { + note: "v1 bundle, keywords not used", + bundleRegoVersion: 1, + modules: map[string]string{ + "test.rego": `package test +a[4] { + 1 == 1 +}`, + }, + query: `data.test.a`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1, no keywords imported", + bundleRegoVersion: 1, + modules: map[string]string{ + "test.rego": `package test +a contains 4 if { + 1 == 1 +}`, + }, + query: `data.test.a`, + }, + } + + bundleTypeCases := []struct { + note string + tar bool + }{ + { + "bundle dir", false, + }, + { + "bundle tar", true, + }, + } + + modes := []struct { + name string + e2e bool + }{ + { + name: "run", + }, + { + name: "e2e", + e2e: true, + }, + } + + for _, bundleType := range bundleTypeCases { + for _, mode := range modes { + for _, tc := range tests { + t.Run(fmt.Sprintf("%s, %s, %s", bundleType.note, tc.note, mode.name), func(t *testing.T) { + t.Parallel() + + files := map[string]string{} + + if bundleType.tar { + files["bundle.tar.gz"] = "" + } else { + maps.Copy(files, tc.modules) + + manifest := bundle.Manifest{ + RegoVersion: &tc.bundleRegoVersion, + FileRegoVersions: tc.bundleFileRegoVersions, + } + manifest.Init() + if b, err := json.Marshal(manifest); err != nil { + t.Fatalf("Unexpected error: %s", err) + } else { + files[".manifest"] = string(b) + } + } + + test.WithTempFS(files, func(root string) { + p := root + if bundleType.tar { + b := bundle.Bundle{ + Manifest: bundle.Manifest{ + RegoVersion: &tc.bundleRegoVersion, + FileRegoVersions: tc.bundleFileRegoVersions, + }, + Data: map[string]any{}, + } + for k, v := range tc.modules { + b.Modules = append(b.Modules, bundle.ModuleFile{ + Path: k, + Raw: []byte(v), + }) + } + p = filepath.Join(root, "bundle.tar.gz") + f, err := os.OpenFile(p, os.O_WRONLY, os.ModePerm) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + err = bundle.Write(f, b) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + } + + params := testBenchParams() + _ = params.outputFormat.Set(formats.Pretty) + + params.e2e = mode.e2e + err := params.bundlePaths.Set(p) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + args := []string{tc.query} + + var buf bytes.Buffer + rc, err := benchMain(args, params, &buf, &goBenchRunner{}) + + if len(tc.expErrs) > 0 { + if rc == 0 { + t.Fatalf("Expected non-zero return code") + } + + output := buf.String() + for _, expErr := range tc.expErrs { + if !strings.Contains(output, expErr) { + t.Fatalf("Expected error:\n\n%s\n\ngot:\n\n%s", expErr, output) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + if rc != 0 { + t.Fatalf("Unexpected return code %d, expected 0", rc) + } + } + }) + }) + } + } + } +} + +func TestRenderBenchmarkResultJSONOutput(t *testing.T) { + t.Parallel() + + params := testBenchParams() + err := params.outputFormat.Set(formats.JSON) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + br := fakeBenchResults() + + var buf bytes.Buffer + renderBenchmarkResult(params, br, &buf) + + actual := buf.String() + + expected := `{ + "N": 134844, + "T": 1088294120, + "Bytes": 0, + "MemAllocs": 8360721, + "MemBytes": 449906736, + "Extra": { + "histogram_timer_rego_query_eval_ns_75%": 4953.75, + "histogram_timer_rego_query_eval_ns_90%": 6309.6, + "histogram_timer_rego_query_eval_ns_95%": 7872.55, + "histogram_timer_rego_query_eval_ns_99%": 14947.34000000001, + "histogram_timer_rego_query_eval_ns_99.9%": 174377.08200000023, + "histogram_timer_rego_query_eval_ns_99.99%": 176301, + "histogram_timer_rego_query_eval_ns_count": 134844, + "histogram_timer_rego_query_eval_ns_max": 176301, + "histogram_timer_rego_query_eval_ns_mean": 5118.3706225680935, + "histogram_timer_rego_query_eval_ns_median": 4312, + "histogram_timer_rego_query_eval_ns_min": 3553, + "histogram_timer_rego_query_eval_ns_stddev": 6587.830963916497 + } +} +` + if actual != expected { + t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n", expected, actual) + } +} + +func TestRenderBenchmarkResultPrettyOutput(t *testing.T) { + t.Parallel() + + params := testBenchParams() + params.benchMem = false + err := params.outputFormat.Set(formats.Pretty) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + br := fakeBenchResults() + + var buf bytes.Buffer + renderBenchmarkResult(params, br, &buf) + + actual := buf.String() + + expected := `+-------------------------------------------+------------+ +| samples | 134844 | +| ns/op | 8071 | +| histogram_timer_rego_query_eval_ns_75% | 4954 | +| histogram_timer_rego_query_eval_ns_90% | 6310 | +| histogram_timer_rego_query_eval_ns_95% | 7873 | +| histogram_timer_rego_query_eval_ns_99% | 14947 | +| histogram_timer_rego_query_eval_ns_99.9% | 174377 | +| histogram_timer_rego_query_eval_ns_99.99% | 176301 | +| histogram_timer_rego_query_eval_ns_count | 134844 | +| histogram_timer_rego_query_eval_ns_max | 176301 | +| histogram_timer_rego_query_eval_ns_mean | 5118 | +| histogram_timer_rego_query_eval_ns_median | 4312 | +| histogram_timer_rego_query_eval_ns_min | 3553 | +| histogram_timer_rego_query_eval_ns_stddev | 6588 | ++-------------------------------------------+------------+ +` + if actual != expected { + t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n", expected, actual) + } +} + +func TestRenderBenchmarkResultPrettyOutputShowAllocs(t *testing.T) { + t.Parallel() + + params := testBenchParams() + params.benchMem = true + err := params.outputFormat.Set(formats.Pretty) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + br := fakeBenchResults() + + var buf bytes.Buffer + renderBenchmarkResult(params, br, &buf) + + actual := buf.String() + + expected := `+-------------------------------------------+------------+ +| samples | 134844 | +| ns/op | 8071 | +| B/op | 3336 | +| allocs/op | 62 | +| histogram_timer_rego_query_eval_ns_75% | 4954 | +| histogram_timer_rego_query_eval_ns_90% | 6310 | +| histogram_timer_rego_query_eval_ns_95% | 7873 | +| histogram_timer_rego_query_eval_ns_99% | 14947 | +| histogram_timer_rego_query_eval_ns_99.9% | 174377 | +| histogram_timer_rego_query_eval_ns_99.99% | 176301 | +| histogram_timer_rego_query_eval_ns_count | 134844 | +| histogram_timer_rego_query_eval_ns_max | 176301 | +| histogram_timer_rego_query_eval_ns_mean | 5118 | +| histogram_timer_rego_query_eval_ns_median | 4312 | +| histogram_timer_rego_query_eval_ns_min | 3553 | +| histogram_timer_rego_query_eval_ns_stddev | 6588 | ++-------------------------------------------+------------+ +` + if actual != expected { + t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n", expected, actual) + } +} + +func TestRenderBenchmarkResultGoBenchOutputShowAllocs(t *testing.T) { + t.Parallel() + + params := testBenchParams() + params.benchMem = true + err := params.outputFormat.Set(formats.GoBench) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + br := fakeBenchResults() + + var buf bytes.Buffer + renderBenchmarkResult(params, br, &buf) + + actual := buf.String() + + if !strings.HasPrefix(actual, "Benchmark") { + t.Fatalf("Expected line output to start with 'Benchmark', got: \n\n%s\n", actual) + } + + if len(strings.Split(strings.TrimSpace(actual), "\n")) != 1 { + t.Fatalf("Expected only a single line of output") + } +} + +func TestRenderBenchmarkErrorJSONOutput(t *testing.T) { + t.Parallel() + + params := testBenchParams() + err := params.outputFormat.Set(formats.JSON) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + var buf bytes.Buffer + + _, err = ast.ParseBody("???") + + err = renderBenchmarkError(params, err, &buf) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + actual := buf.String() + expected := `{ + "errors": [ + { + "message": "illegal token", + "code": "rego_parse_error", + "location": { + "file": "", + "row": 1, + "col": 1 + }, + "details": { + "line": "???", + "idx": 0 + } + } + ] +} +` + + if actual != expected { + t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n", expected, actual) + } +} + +func TestRenderBenchmarkErrorPrettyOutput(t *testing.T) { + t.Parallel() + + params := testBenchParams() + err := params.outputFormat.Set(formats.Pretty) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + testPrettyBenchmarkOutput(t, params) +} + +func TestRenderBenchmarkErrorGoBenchOutput(t *testing.T) { + t.Parallel() + + params := testBenchParams() + err := params.outputFormat.Set(formats.GoBench) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + testPrettyBenchmarkOutput(t, params) +} + +func testPrettyBenchmarkOutput(t *testing.T, params benchmarkCommandParams) { + var buf bytes.Buffer + + _, err := ast.ParseBody("???") + + err = renderBenchmarkError(params, err, &buf) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + actual := buf.String() + expected := `1 error occurred: 1:1: rego_parse_error: illegal token + ??? + ^ +` + if actual != expected { + t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n", expected, actual) + } +} + +func testBenchParams() benchmarkCommandParams { + params := newBenchmarkEvalParams() + params.benchMem = true + params.metrics = true + _ = params.outputFormat.Set(formats.JSON) + params.count = 1 + return params +} + +func fakeBenchResults() testing.BenchmarkResult { + return testing.BenchmarkResult{ + N: 134844, + T: 1088294120, + Bytes: 0, + MemAllocs: 8360721, + MemBytes: 449906736, + Extra: map[string]float64{ + "histogram_timer_rego_query_eval_ns_75%": 4953.75, + "histogram_timer_rego_query_eval_ns_90%": 6309.6, + "histogram_timer_rego_query_eval_ns_95%": 7872.55, + "histogram_timer_rego_query_eval_ns_99%": 14947.34000000001, + "histogram_timer_rego_query_eval_ns_99.9%": 174377.08200000023, + "histogram_timer_rego_query_eval_ns_99.99%": 176301, + "histogram_timer_rego_query_eval_ns_count": 134844, + "histogram_timer_rego_query_eval_ns_max": 176301, + "histogram_timer_rego_query_eval_ns_mean": 5118.3706225680935, + "histogram_timer_rego_query_eval_ns_median": 4312, + "histogram_timer_rego_query_eval_ns_min": 3553, + "histogram_timer_rego_query_eval_ns_stddev": 6587.830963916497, + }, + } +} + +func testBundle() bundle.Bundle { + mod := `package a.b + import rego.v1 + + x if { + data.a.b.c == 42 + } + ` + + return bundle.Bundle{ + Manifest: bundle.Manifest{}, + Data: map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "c": 42, + }, + }, + }, + Modules: []bundle.ModuleFile{ + { + Path: "/a/b/policy.rego", + Raw: []byte(mod), + Parsed: ast.MustParseModule(mod), + }, + }, + } +} diff --git a/third_party/opa/cmd/build.go b/third_party/opa/cmd/build.go new file mode 100644 index 000000000000..a7e647b042f1 --- /dev/null +++ b/third_party/opa/cmd/build.go @@ -0,0 +1,417 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/internal/env" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/compile" + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/util" +) + +const defaultPublicKeyID = "default" + +type buildParams struct { + capabilities *capabilitiesFlag + target *util.EnumFlag + bundleMode bool + pruneUnused bool + optimizationLevel int + entrypoints repeatedStringFlag + outputFile string + revision stringptrFlag + ignore []string + debug bool + algorithm string + key string + scope string + pubKey string + pubKeyID string + claimsFile string + excludeVerifyFiles []string + plugin string + ns string + v0Compatible bool + v1Compatible bool + followSymlinks bool + wasmIncludePrint bool + stderr io.Writer +} + +func newBuildParams() buildParams { + return buildParams{ + capabilities: newCapabilitiesFlag(), + target: util.NewEnumFlag(compile.TargetRego, compile.Targets), + stderr: os.Stderr, + } +} + +func (p *buildParams) regoVersion() ast.RegoVersion { + if p.v0Compatible { + // v0 takes precedence over v1 + return ast.RegoV0 + } + if p.v1Compatible { + return ast.RegoV1 + } + return ast.DefaultRegoVersion +} + +func initBuild(root *cobra.Command, brand string) { + executable := root.Name() + + buildParams := newBuildParams() + + buildCommand := &cobra.Command{ + Use: "build [ [...]]", + Short: `Build an ` + brand + ` bundle`, + Long: `Build an ` + brand + ` bundle. + +The 'build' command packages ` + brand + ` policy and data files into bundles. Bundles are +gzipped tarballs containing policies and data. Paths referring to directories are +loaded recursively. + + $ ls + example.rego + + $ ` + executable + ` build -b . + +You can load bundles into ` + brand + ` on the command-line: + + $ ls + bundle.tar.gz example.rego + + $ ` + executable + ` run bundle.tar.gz + +You can also configure ` + brand + ` to download bundles from remote HTTP endpoints: + + $ ` + executable + ` run --server \ + --set bundles.example.resource=bundle.tar.gz \ + --set services.example.url=http://localhost:8080 + +Inside another terminal in the same directory, serve the bundle via HTTP: + + $ python3 -m http.server --bind localhost 8080 + +For more information on bundles see https://www.openpolicyagent.org/docs/latest/management-bundles/. + +Common Flags +------------ + +When -b is specified the 'build' command assumes paths refer to existing bundle files +or directories following the bundle structure. If multiple bundles are provided, their +contents are merged. If there are any merge conflicts (e.g., due to conflicting bundle +roots), the command fails. When loading an existing bundle file, the .manifest from +the input bundle will be included in the output bundle. Flags that set .manifest fields +(such as --revision) override input bundle .manifest fields. + +The -O flag controls the optimization level. By default, optimization is disabled (-O=0). +When optimization is enabled the 'build' command generates a bundle that is semantically +equivalent to the input files however the structure of the files in the bundle may have +been changed by rewriting, inlining, pruning, etc. Higher optimization levels may result +in longer build times. The --partial-namespace flag can used in conjunction with the -O flag +to specify the namespace for the partially evaluated files in the optimized bundle. + +The 'build' command supports targets (specified by -t): + + rego The default target emits a bundle containing a set of policy and data files + that are semantically equivalent to the input files. If optimizations are + disabled the output may simply contain a copy of the input policy and data + files. If optimization is enabled at least one entrypoint must be supplied, + either via the -e option, or via entrypoint metadata annotations. + + wasm The wasm target emits a bundle containing a WebAssembly module compiled from + the input files for each specified entrypoint. The bundle may contain the + original policy or data files. + + plan The plan target emits a bundle containing a plan, i.e., an intermediate + representation compiled from the input files for each specified entrypoint. + This is for further processing, ` + brand + ` cannot evaluate a "plan bundle" like it + can evaluate a wasm or rego bundle. + +The -e flag tells the 'build' command which documents (entrypoints) will be queried by +the software asking for policy decisions, so that it can focus optimization efforts and +ensure that document is not eliminated by the optimizer. +Note: Unless the --prune-unused flag is used, any rule transitively referring to a +package or rule declared as an entrypoint will also be enumerated as an entrypoint. + +Signing +------- + +The 'build' command can be used to verify the signature of a signed bundle and +also to generate a signature for the output bundle the command creates. + +If the directory path(s) provided to the 'build' command contain a ".signatures.json" file, +it will attempt to verify the signatures included in that file. The bundle files +or directory path(s) to verify must be specified using --bundle. + +For more information on the bundle signing and verification, see +https://www.openpolicyagent.org/docs/latest/management-bundles/#signing. + +Example: + + $ ` + executable + ` build --verification-key /path/to/public_key.pem --signing-key /path/to/private_key.pem --bundle foo + +Where foo has the following structure: + + foo/ + | + +-- bar/ + | | + | +-- data.json + | + +-- policy.rego + | + +-- .manifest + | + +-- .signatures.json + + +The 'build' command will verify the signatures using the public key provided by the --verification-key flag. +The default signing algorithm is RS256 and the --signing-alg flag can be used to specify +a different one. The --verification-key-id and --scope flags can be used to specify the name for the key +provided using the --verification-key flag and scope to use for bundle signature verification respectively. + +If the verification succeeds, the 'build' command will write out an updated ".signatures.json" file +to the output bundle. It will use the key specified by the --signing-key flag to sign +the token in the ".signatures.json" file. + +To include additional claims in the payload use the --claims-file flag to provide a JSON file +containing optional claims. + +For more information on the format of the ".signatures.json" file +see https://www.openpolicyagent.org/docs/latest/management-bundles/#signature-format. + +Capabilities +------------ + +The 'build' command can validate policies against a configurable set of ` + brand + ` capabilities. +The capabilities define the built-in functions and other language features that policies +may depend on. For example, the following capabilities file only permits the policy to +depend on the "plus" built-in function ('+'): + + { + "builtins": [ + { + "name": "plus", + "infix": "+", + "decl": { + "type": "function", + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + } + } + } + ] + } + +Capabilities can be used to validate policies against a specific version of ` + brand + `. +The ` + brand + ` repository contains a set of capabilities files for each ` + brand + ` release. For example, +the following command builds a directory of policies ('./policies') and validates them +against ` + brand + ` v0.22.0: + + ` + executable + ` build ./policies --capabilities v0.22.0 +`, + PreRunE: func(Cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("expected at least one path") + } + return env.CmdFlags.CheckEnvironmentVariables(Cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + if err := dobuild(buildParams, args); err != nil { + fmt.Println("error:", err) + return err + } + return nil + }, + } + + buildCommand.Flags().VarP(buildParams.target, "target", "t", "set the output bundle target type") + buildCommand.Flags().BoolVar(&buildParams.pruneUnused, "prune-unused", false, "exclude dependents of entrypoints") + buildCommand.Flags().BoolVar(&buildParams.debug, "debug", false, "enable debug output") + buildCommand.Flags().IntVarP(&buildParams.optimizationLevel, "optimize", "O", 0, "set optimization level") + buildCommand.Flags().VarP(&buildParams.entrypoints, "entrypoint", "e", "set slash separated entrypoint path") + buildCommand.Flags().VarP(&buildParams.revision, "revision", "r", "set output bundle revision") + buildCommand.Flags().StringVarP(&buildParams.outputFile, "output", "o", "bundle.tar.gz", "set the output filename") + buildCommand.Flags().StringVar(&buildParams.ns, "partial-namespace", "partial", "set the namespace to use for partially evaluated files in an optimized bundle") + buildCommand.Flags().BoolVar(&buildParams.followSymlinks, "follow-symlinks", false, "follow symlinks in the input set of paths when building the bundle") + buildCommand.Flags().BoolVar(&buildParams.wasmIncludePrint, "wasm-include-print", false, "enable print statements inside of WebAssembly modules compiled by the compiler") + + addBundleModeFlag(buildCommand.Flags(), &buildParams.bundleMode, false) + addIgnoreFlag(buildCommand.Flags(), &buildParams.ignore) + addCapabilitiesFlag(buildCommand.Flags(), buildParams.capabilities) + + // bundle verification config + addVerificationKeyFlag(buildCommand.Flags(), &buildParams.pubKey) + addVerificationKeyIDFlag(buildCommand.Flags(), &buildParams.pubKeyID, defaultPublicKeyID) + addSigningAlgFlag(buildCommand.Flags(), &buildParams.algorithm, defaultTokenSigningAlg) + addBundleVerificationScopeFlag(buildCommand.Flags(), &buildParams.scope) + addBundleVerificationExcludeFilesFlag(buildCommand.Flags(), &buildParams.excludeVerifyFiles) + + // bundle signing config + addSigningKeyFlag(buildCommand.Flags(), &buildParams.key) + addSigningPluginFlag(buildCommand.Flags(), &buildParams.plugin) + addClaimsFileFlag(buildCommand.Flags(), &buildParams.claimsFile) + + addV0CompatibleFlag(buildCommand.Flags(), &buildParams.v0Compatible, false) + addV1CompatibleFlag(buildCommand.Flags(), &buildParams.v1Compatible, false) + + root.AddCommand(buildCommand) +} + +func dobuild(params buildParams, args []string) error { + buf := bytes.NewBuffer(nil) + + // generate the bundle verification and signing config + bvc, err := buildVerificationConfig(params.pubKey, params.pubKeyID, params.algorithm, params.scope, params.excludeVerifyFiles) + if err != nil { + return err + } + + bsc, err := buildSigningConfig(params.key, params.algorithm, params.claimsFile, params.plugin) + if err != nil { + return err + } + + if (bvc != nil || bsc != nil) && !params.bundleMode { + return errors.New("enable bundle mode (ie. --bundle) to verify or sign bundle files or directories") + } + + // if manifest files are found in the input directories and the -b flag is not set, this is likely a mistake. + if !params.bundleMode { + for _, arg := range args { + stat, err := os.Stat(arg) + if err != nil || !stat.IsDir() { + continue + } + + if _, err := os.Stat(filepath.Join(arg, ".manifest")); err != nil { + continue + } + + fmt.Fprintf(params.stderr, "Warning: .manifest file found in %q but -b flag not specified. Manifest will be ignored.\n", arg) + } + } + + capabilities := params.capabilities.C + if capabilities == nil { + // ensure custom builtins are properly captured + capabilities = ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(params.regoVersion())) + } + + compiler := compile.New(). + WithCapabilities(capabilities). + WithTarget(params.target.String()). + WithAsBundle(params.bundleMode). + WithPruneUnused(params.pruneUnused). + WithOptimizationLevel(params.optimizationLevel). + WithOutput(buf). + WithEntrypoints(params.entrypoints.v...). + WithRegoAnnotationEntrypoints(true). + WithPaths(args...). + WithFilter(buildCommandLoaderFilter(params.bundleMode, params.ignore)). + WithBundleLazyLoadingMode(bundle.HasExtension()). + WithBundleVerificationConfig(bvc). + WithBundleSigningConfig(bsc). + WithPartialNamespace(params.ns). + WithFollowSymlinks(params.followSymlinks) + + compiler = compiler.WithRegoVersion(params.regoVersion()) + + if params.revision.isSet { + compiler = compiler.WithRevision(*params.revision.v) + } + + if params.debug { + compiler = compiler.WithDebug(params.stderr) + } + + if params.claimsFile == "" { + compiler = compiler.WithBundleVerificationKeyID(params.pubKeyID) + } + + if params.target.String() == compile.TargetPlan { + compiler = compiler.WithEnablePrintStatements(true) + } + + if params.target.String() == compile.TargetWasm { + compiler = compiler.WithEnablePrintStatements(params.wasmIncludePrint) + } + + err = compiler.Build(context.Background()) + if err != nil { + return err + } + + out, err := os.Create(params.outputFile) + if err != nil { + return err + } + + _, err = io.Copy(out, buf) + if err != nil { + return err + } + + return out.Close() +} + +func buildCommandLoaderFilter(bundleMode bool, ignore []string) func(string, os.FileInfo, int) bool { + return func(abspath string, info os.FileInfo, depth int) bool { + if !bundleMode { + if !info.IsDir() && strings.HasSuffix(abspath, ".tar.gz") { + return true + } + } + return ignored(ignore).Apply(abspath, info, depth) + } +} + +func buildVerificationConfig(pubKey, pubKeyID, alg, scope string, excludeFiles []string) (*bundle.VerificationConfig, error) { + if pubKey == "" { + return nil, nil + } + + keyConfig, err := keys.NewKeyConfig(pubKey, alg, scope) + if err != nil { + return nil, err + } + return bundle.NewVerificationConfig(map[string]*keys.Config{pubKeyID: keyConfig}, pubKeyID, scope, excludeFiles), nil +} + +func buildSigningConfig(key, alg, claimsFile, plugin string) (*bundle.SigningConfig, error) { + if key == "" && (plugin != "" || claimsFile != "") { + return nil, errSigningConfigIncomplete + } + if key == "" { + return nil, nil + } + return bundle.NewSigningConfig(key, alg, claimsFile).WithPlugin(plugin), nil +} diff --git a/third_party/opa/cmd/build_test.go b/third_party/opa/cmd/build_test.go new file mode 100644 index 000000000000..149e72242d33 --- /dev/null +++ b/third_party/opa/cmd/build_test.go @@ -0,0 +1,3209 @@ +package cmd + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path" + "path/filepath" + "reflect" + "slices" + "strconv" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestBuildProducesBundle(t *testing.T) { + + files := map[string]string{ + "test.rego": ` + package test + p = 1 + `, + } + + test.WithTempFS(files, func(root string) { + params := newBuildParams() + params.outputFile = path.Join(root, "bundle.tar.gz") + + err := dobuild(params, []string{root}) + if err != nil { + t.Fatal(err) + } + + _, err = loader.NewFileLoader().AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + // Check that manifest is not written given no input manifest and no other flags + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + + for { + f, err := tr.Next() + if err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + if f.Name == "/.manifest" || f.Name == "/data.json" || strings.HasSuffix(f.Name, "/test.rego") { + continue + } + t.Fatal("unexpected file:", f.Name) + } + }) +} + +func TestBuildRespectsCapabilities(t *testing.T) { + tests := []struct { + note string + caps string + policy string + err string + bundleMode bool // build with "-b" flag + }{ + { + note: "builtin defined in caps", + caps: `{ + "builtins": [ + { + "name": "is_foo", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + } + ] + }`, + policy: `package test +p { is_foo("bar") }`, + }, + { + note: "future kw NOT defined in caps", + caps: func() string { + c := ast.CapabilitiesForThisVersion() + c.FutureKeywords = []string{"in"} + c.Features = []string{} + j, err := json.Marshal(c) + if err != nil { + panic(err) + } + return string(j) + }(), + policy: `package test +import future.keywords.if +import future.keywords.in +p if "opa" in input.tools`, + err: "rego_parse_error: unexpected keyword, must be one of [in]", + }, + { + note: "future kw NOT defined in caps, rego-v1 feature", + caps: func() string { + c := ast.CapabilitiesForThisVersion() + c.FutureKeywords = []string{"in"} + c.Features = []string{ast.FeatureRegoV1} + j, err := json.Marshal(c) + if err != nil { + panic(err) + } + return string(j) + }(), + policy: `package test +import future.keywords.if +import future.keywords.in +p if "opa" in input.tools`, + }, + { + note: "future kw are defined in caps", + caps: func() string { + c := ast.CapabilitiesForThisVersion() + c.FutureKeywords = []string{"in", "if"} + j, err := json.Marshal(c) + if err != nil { + panic(err) + } + return string(j) + }(), + policy: `package test +import future.keywords.if +import future.keywords.in +p if "opa" in input.tools`, + }, + { + note: "rego.v1 imported AND defined in capabilities", + caps: func() string { + c := ast.CapabilitiesForThisVersion() + c.Features = []string{ast.FeatureRegoV1Import} + j, err := json.Marshal(c) + if err != nil { + panic(err) + } + return string(j) + }(), + policy: `package test +import rego.v1`, + }, + { + note: "rego.v1 imported AND rego-v1 in capabilities", + caps: func() string { + c := ast.CapabilitiesForThisVersion() + c.Features = []string{ast.FeatureRegoV1} + j, err := json.Marshal(c) + if err != nil { + panic(err) + } + return string(j) + }(), + policy: `package test +import rego.v1`, + }, + } + + // add same tests for bundle-mode == true: + for i := range tests { + tc := tests[i] + tc.bundleMode = true + tc.note += " (as bundle)" + tests = append(tests, tc) + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "capabilities.json": tc.caps, + "test.rego": tc.policy, + } + + test.WithTempFS(files, func(root string) { + caps := newCapabilitiesFlag() + if err := caps.Set(path.Join(root, "capabilities.json")); err != nil { + t.Fatal(err) + } + params := newBuildParams() + params.outputFile = path.Join(root, "bundle.tar.gz") + params.capabilities = caps + params.bundleMode = tc.bundleMode + // Test capabilities are all pre-v1 + params.v0Compatible = true + + err := dobuild(params, []string{root}) + switch { + case err != nil && tc.err != "": + if !strings.Contains(err.Error(), tc.err) { + t.Fatalf("expected err %v, got %v", tc.err, err) + } + return // don't read back bundle below + case err != nil && tc.err == "": + t.Fatalf("unexpected error: %v", err) + case err == nil && tc.err != "": + t.Fatalf("expected error %v, got nil", tc.err) + } + + // check that the resulting bundle is readable + _, err = loader.NewFileLoader().AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + }) + }) + } +} + +func TestBuildFilesystemModeIgnoresTarGz(t *testing.T) { + + files := map[string]string{ + "test.rego": ` + package test + p = 1 + `, + } + + test.WithTempFS(files, func(root string) { + params := newBuildParams() + params.outputFile = path.Join(root, "bundle.tar.gz") + + err := dobuild(params, []string{root}) + if err != nil { + t.Fatal(err) + } + + _, err = loader.NewFileLoader().AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + // Just run the build again to simulate the user doing back-to-back builds. + err = dobuild(params, []string{root}) + if err != nil { + t.Fatal(err) + } + + }) +} + +func TestBuildErrorDoesNotWriteFile(t *testing.T) { + + files := map[string]string{ + "test.rego": ` + package test + + p if { p } + `, + } + + test.WithTempFS(files, func(root string) { + params := newBuildParams() + params.outputFile = path.Join(root, "bundle.tar.gz") + + err := dobuild(params, []string{root}) + exp := fmt.Sprintf("1 error occurred: %s/test.rego:4: rego_recursion_error: rule data.test.p is recursive: data.test.p -> data.test.p", + root) + if err == nil || err.Error() != exp { + t.Fatalf("expected recursion error %q but got: %q", exp, err) + } + + if _, err := os.Stat(params.outputFile); !os.IsNotExist(err) { + t.Fatalf("expected stat \"not found\" error, got %v", err) + } + }) +} + +func TestBuildErrorVerifyNonBundle(t *testing.T) { + + files := map[string]string{ + "test.rego": ` + package test + + p if { p } + `, + } + + test.WithTempFS(files, func(root string) { + params := newBuildParams() + params.outputFile = path.Join(root, "bundle.tar.gz") + params.pubKey = "secret" + + err := dobuild(params, []string{root}) + if err == nil { + t.Fatal("expected error but got nil") + } + + exp := "enable bundle mode (ie. --bundle) to verify or sign bundle files or directories" + if err.Error() != exp { + t.Fatalf("expected error message %v but got %v", exp, err.Error()) + } + }) +} + +func TestBuildVerificationConfigError(t *testing.T) { + if os.Getuid() == 0 { + t.Skip("cannot be run as root") + } + + files := map[string]string{ + "public.pem": "foo", + } + + test.WithTempFS(files, func(rootDir string) { + // simulate error while reading file + err := os.Chmod(filepath.Join(rootDir, "public.pem"), 0111) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, err = buildVerificationConfig(filepath.Join(rootDir, "public.pem"), "default", "", "", nil) + if err == nil { + t.Fatal("Expected error but got nil") + } + }) +} + +func TestBuildSigningConfigError(t *testing.T) { + tests := []struct { + note string + key, plugin, claimsFile string + expErr bool + }{ + { + note: "key+plugin+claimsFile unset", + }, + { + note: "key+claimsFile unset", + plugin: "plugin", + expErr: true, + }, + { + note: "key+plugin unset", + claimsFile: "claims", + expErr: true, + }, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + _, err := buildSigningConfig(tc.key, defaultTokenSigningAlg, tc.claimsFile, tc.plugin) + switch { + case tc.expErr && err == nil: + t.Fatal("Expected error but got nil") + case !tc.expErr && err != nil: + t.Fatalf("Expected no error but got %v", err) + } + }) + } +} + +func TestBuildPlanWithPruneUnused(t *testing.T) { + + files := map[string]string{ + "test.rego": ` + package test + + p contains 1 + + f(x) if { p[x] } + `, + } + + test.WithTempFS(files, func(root string) { + params := newBuildParams() + if err := params.target.Set("plan"); err != nil { + t.Fatal(err) + } + params.pruneUnused = true + params.entrypoints.v = []string{"test"} + params.outputFile = path.Join(root, "bundle.tar.gz") + + err := dobuild(params, []string{root}) + if err != nil { + t.Fatal(err) + } + + _, err = loader.NewFileLoader().AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + // Check that manifest is not written given no input manifest and no other flags + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + + found := false // for plan.json + + for { + f, err := tr.Next() + if err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + switch { + case f.Name == "/plan.json": + found = true + case f.Name == "/.manifest" || f.Name == "/data.json" || strings.HasSuffix(f.Name, "/test.rego"): // expected + default: + t.Errorf("unexpected file: %s", f.Name) + } + } + if !found { + t.Error("plan.json not found") + } + }) +} + +func TestBuildPlanWithPrintStatements(t *testing.T) { + + files := map[string]string{ + "test.rego": ` + package test + + p if { print("hello") } + `, + } + + test.WithTempFS(files, func(root string) { + params := newBuildParams() + if err := params.target.Set("plan"); err != nil { + t.Fatal(err) + } + params.entrypoints.v = []string{"test"} + params.outputFile = path.Join(root, "bundle.tar.gz") + + err := dobuild(params, []string{root}) + if err != nil { + t.Fatal(err) + } + + _, err = loader.NewFileLoader().AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + var found bool + + for { + f, err := tr.Next() + if err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + if f.Name == "/plan.json" { + found = true + plan, err := io.ReadAll(tr) + if err != nil { + t.Fatal(err) + } + if !strings.Contains(string(plan), "internal.print") { + t.Error("expected plan.json to contain reference to internal.print built-in function") + } + } + } + + if !found { + t.Error("plan.json not found") + } + }) +} + +func TestBuildPlanWithRegoEntrypointAnnotations(t *testing.T) { + + tests := []struct { + note string + files map[string]string + err error + v0Compatible bool + }{ + { + note: "annotated entrypoint", + files: map[string]string{ + "test.rego": ` +# METADATA +# entrypoint: true +package test + +p contains 1 + +f(x) if { p[x] } + `, + }, + err: nil, + }, + { + note: "set generation with annotated entrypoint (v0)", + v0Compatible: true, + files: map[string]string{ + "test.rego": ` +package test + +# METADATA +# entrypoint: true +p[x] { + {"a", "b"}[x] +} + `, + }, + err: nil, + }, + { + note: "set generation with annotated entrypoint (contains if)", + files: map[string]string{ + "test.rego": ` +package test + +# METADATA +# entrypoint: true +p contains x if { + {"a", "b"}[x] +} + `, + }, + err: nil, + }, + { + note: "object generation with annotated entrypoint (v0)", + v0Compatible: true, + files: map[string]string{ + "test.rego": ` +package test + +# METADATA +# entrypoint: true +p[i] := x { + x := ["a", "b"][i] +} + `, + }, + err: nil, + }, + { + note: "object generation with annotated entrypoint (if)", + files: map[string]string{ + "test.rego": ` +package test + +# METADATA +# entrypoint: true +p[i] if { + {"a", "b"}[i] +} + `, + }, + err: nil, + }, + { + note: "dots in head with annotated entrypoint", + files: map[string]string{ + "test.rego": ` +package test + +# METADATA +# entrypoint: true +p.a.b if { + true +} + `, + }, + err: nil, + }, + { + note: "dots in head object generation with annotated entrypoint", + files: map[string]string{ + "test.rego": ` +package test + +# METADATA +# entrypoint: true +p.a.b[i] := x if { + x := ["a", "b"][i] +} + `, + }, + err: nil, + }, + { + note: "no annotated entrypoint", + files: map[string]string{ + "test.rego": ` +package test + +p contains 1 + +f(x) if { p[x] } +`, + }, + err: errors.New("plan compilation requires at least one entrypoint"), + }, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(tc.files, func(root string) { + params := newBuildParams() + if err := params.target.Set("plan"); err != nil { + t.Fatal(err) + } + params.pruneUnused = true + params.outputFile = path.Join(root, "bundle.tar.gz") + params.v0Compatible = tc.v0Compatible + + // Build should fail if entrypoint is not discovered from annotations. + err := dobuild(params, []string{root}) + if err != nil { + if tc.err == nil || tc.err.Error() != err.Error() { + t.Fatal(err) + } + return // Bail out if this was an expected test failure. + } + + // Attempt to load up the built bundle. + _, err = loader.NewFileLoader().AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + }) + }) + } +} + +func TestBuildWasmWithAnnotations(t *testing.T) { + tests := []struct { + note string + files map[string]string + entrypoints []string + manifest string + }{ + { + note: "last rule is annotated entrypoint", + files: map[string]string{ + "test.rego": ` +package test + +# METADATA +# title: P1 +p1 := 1 + +# METADATA +# title: P2 +# entrypoint: true +p2 := 2 +`, + }, + manifest: ` +{ + "revision":"", + "rego_version": %REGO_VERSION%, + "roots":[""], + "wasm":[{ + "entrypoint":"test/p2", + "module":"/policy.wasm", + "annotations":[{ + "scope":"document", + "title":"P2", + "entrypoint":true + }] + }] +} +`, + }, + { + note: "last rule is (not annotated) entrypoint", + files: map[string]string{ + "test.rego": ` +package test + +# METADATA +# title: P1 +p1 := 1 + +# METADATA +# title: P2 +p2 := 2 +`, + }, + entrypoints: []string{"test/p2"}, + manifest: ` +{ + "revision":"", + "rego_version": %REGO_VERSION%, + "roots":[""], + "wasm":[{ + "entrypoint":"test/p2", + "module":"/policy.wasm", + "annotations":[{ + "scope":"rule", + "title":"P2" + }] + }] +} +`, + }, + { + note: "rules in multiple files are entrypoints", + files: map[string]string{ + "test1.rego": ` +package test + +# METADATA +# title: P1 +p1 := 1 + +# METADATA +# title: P2 +# entrypoint: true +p2 := 2 +`, + "test2.rego": ` +package test + +# METADATA +# title: P3 +p3 := 3 + +# METADATA +# title: P4 +p4 := 4 +`, + "test3.rego": ` +package test.foo + +# METADATA +# title: BAR +# entrypoint: true +bar := "baz" +`, + }, + entrypoints: []string{"test/p3"}, + manifest: ` +{ + "revision":"", + "rego_version": %REGO_VERSION%, + "roots":[""], + "wasm":[{ + "entrypoint":"test/p3", + "annotations":[{"scope":"rule","title":"P3"}], + "module":"/policy.wasm" + },{ + "entrypoint":"test/foo/bar", + "module":"/policy.wasm", + "annotations":[{ + "scope":"document", + "title":"BAR", + "entrypoint":true + }] + },{ + "entrypoint":"test/p2", + "module":"/policy.wasm", + "annotations":[{ + "scope":"document", + "title":"P2", + "entrypoint":true + }] + }] +} +`, + }, + { + note: "rule with multiple metadata blocks", + files: map[string]string{ + "test.rego": ` +package test + +# METADATA +# title: P doc +# scope: document +# entrypoint: true + +# METADATA +# title: P +p := 1 +`, + }, + manifest: ` +{ + "revision":"", + "rego_version": %REGO_VERSION%, + "roots":[""], + "wasm":[{ + "entrypoint":"test/p", + "module":"/policy.wasm", + "annotations":[{ + "scope":"document", + "title":"P doc", + "entrypoint":true + },{ + "scope":"rule", + "title":"P" + }] + }] +} +`, + }, + + // Package annotations are not injected into manifest, as package definition is always retained in Rego source. + { + note: "package is annotated entrypoint", + files: map[string]string{ + "test.rego": ` +# METADATA +# title: PKG +# entrypoint: true +package test + +# METADATA +# title: P1 +p1 := 1 + +# METADATA +# title: P2 +p2 := 2 +`, + }, + manifest: ` +{ + "revision":"", + "rego_version": %REGO_VERSION%, + "roots":[""], + "wasm":[{ + "entrypoint":"test", + "module":"/policy.wasm" + }] +} +`, + }, + { + note: "package is (not annotated) entrypoint", + files: map[string]string{ + "test.rego": ` +package test + +# METADATA +# title: P1 +p1 := 1 + +# METADATA +# title: P2 +p2 := 2 +`, + }, + entrypoints: []string{"test"}, + manifest: ` +{ + "revision":"", + "rego_version": %REGO_VERSION%, + "roots":[""], + "wasm":[{ + "entrypoint":"test", + "module":"/policy.wasm" + }] +} +`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(tc.files, func(root string) { + params := newBuildParams() + if err := params.target.Set("wasm"); err != nil { + t.Fatal(err) + } + params.pruneUnused = true + params.outputFile = path.Join(root, "bundle.tar.gz") + params.entrypoints.v = tc.entrypoints + + // Build should fail if entrypoint is not discovered from annotations. + err := dobuild(params, []string{root}) + if err != nil { + t.Fatal(err) + } + + _, err = loader.NewFileLoader().AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + // Check that manifest has expected content + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + + expManifest := strings.ReplaceAll(tc.manifest, "%REGO_VERSION%", + strconv.Itoa(ast.DefaultRegoVersion.Int())) + + found := false + for { + f, err := tr.Next() + if err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + if f.Name == "/.manifest" { + found = true + data, err := io.ReadAll(tr) + if err != nil { + t.Fatal(err) + } + manifest := util.MustUnmarshalJSON(data) + if !reflect.DeepEqual(manifest, util.MustUnmarshalJSON([]byte(expManifest))) { + t.Fatalf("expected manifest\n\n%v\n\nbut got\n\n%v", expManifest, string(util.MustMarshalJSON(manifest))) + } + break + } + } + + if !found { + t.Fatal("no manifest found in bundle") + } + }) + }) + } +} + +func TestBuildBundleModeIgnoreFlag(t *testing.T) { + + files := map[string]string{ + "/a/b/d/data.json": `{"e": "f"}`, + "/policy.rego": "package foo\n p = 1", + "/policy_test.rego": "package foo\n test_p { p }", + "/roles/policy.rego": "package bar\n p = 1", + "/roles/policy_test.rego": "package bar\n test_p { p }", + "/deeper/dir/path/than/others/policy.rego": "package baz\n p = 1", + "/deeper/dir/path/than/others/policy_test.rego": "package baz\n test_p { p }", + } + + test.WithTempFS(files, func(root string) { + params := newBuildParams() + params.outputFile = path.Join(root, "bundle.tar.gz") + params.bundleMode = true + params.ignore = []string{"*_test.rego"} + + err := dobuild(params, []string{root}) + if err != nil { + t.Fatal(err) + } + + _, err = loader.NewFileLoader().AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + // Check that test files are not included in the output bundle + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + + files := []string{} + + for { + f, err := tr.Next() + if err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + + files = append(files, filepath.Base(f.Name)) + } + + // We additionally expect a manifest file + expected := 5 + if len(files) != expected { + t.Fatalf("expected %v files but got %v", expected, len(files)) + } + }) +} + +func TestBuildBundleModeWithManifestRegoVersion(t *testing.T) { + tests := []struct { + note string + roots []string + files map[string]string + expManifest string + expErrs []string + v0Compatible bool + v1Compatible bool + capabilities *ast.Capabilities + }{ + { + note: "v0 bundle rego-version", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "test.rego": `package test + +p[42] { + input.x == 1 +}`, + }, + expManifest: `{"revision":"","roots":[""],"rego_version":0}`, + }, + { + note: "v1 bundle rego-version", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "test.rego": `package test + +p contains 42 if { + input.x == 1 +}`, + }, + expManifest: `{"revision":"","roots":[""],"rego_version":1}`, + }, + { + note: "v0 bundle rego-version, v1 per-file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "test1.rego": `package test + +p[1] { + input.x == 1 +}`, + "test2.rego": `package test + +p contains 2 if { + input.x == 1 +}`, + }, + expManifest: `{"revision":"","roots":[""],"rego_version":0,"file_rego_versions":{"%ROOT%/test2.rego":1}}`, + }, + { + note: "v0 bundle rego-version, v1 per-file override, missing v1 keywords in v1 file", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "test1.rego": `package test + +p[1] { + input.x == 1 +}`, + "test2.rego": `package test + +p[2] { + input.x == 1 +}`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0 bundle rego-version, v1 per-file override, v1 keywords but no v1 imports in v0 file", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "test1.rego": `package test + +p contains 1 if { + input.x == 1 +}`, + "test2.rego": `package test + +p contains 2 if { + input.x == 1 +}`, + }, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "multiple bundles with different rego-versions, v0-compatible", + v0Compatible: true, + roots: []string{"bundle1", "bundle2"}, + files: map[string]string{ + "bundle1/.manifest": `{ + "roots": ["test1"], + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "bundle1/test1.rego": `package test1 +p[1] { + input.x == 1 +}`, + "bundle1/test2.rego": `package test1 +p contains 2 if { + input.x == 1 +}`, + "bundle2/.manifest": `{ + "roots": ["test2"], + "rego_version": 1, + "file_rego_versions": { + "*/test4.rego": 0 + } +}`, + "bundle2/test3.rego": `package test2 +p contains 3 if { + input.x == 1 +}`, + "bundle2/test4.rego": `package test2 +p[4] { + input.x == 1 +}`, + }, + expManifest: `{"revision":"","roots":["test1","test2"],"rego_version":0,"file_rego_versions":{"%ROOT%/bundle1/test2.rego":1,"%ROOT%/bundle2/test3.rego":1}}`, + }, + { + note: "multiple bundles with different rego-versions, v0-compatible, no rego_v1 capabilities feature", + v0Compatible: true, + roots: []string{"bundle1", "bundle2"}, + files: map[string]string{ + "bundle1/.manifest": `{ + "roots": ["test1"], + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "bundle1/test1.rego": `package test1 +p[1] { + input.x == 1 +}`, + "bundle1/test2.rego": `package test1 +p contains 2 if { + input.x == 1 +}`, + "bundle2/.manifest": `{ + "roots": ["test2"], + "rego_version": 1, + "file_rego_versions": { + "*/test4.rego": 0 + } +}`, + "bundle2/test3.rego": `package test2 +p contains 3 if { + input.x == 1 +}`, + "bundle2/test4.rego": `package test2 +p[4] { + input.x == 1 +}`, + }, + capabilities: capsWithoutFeat(ast.RegoV0, ast.FeatureRegoV1), + expErrs: []string{ + // capabilities doesn't include rego_v1 feature, which must be respected + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "multiple bundles with different rego-versions, v0-compatible, rego_v1 capabilities feature", + v0Compatible: true, + roots: []string{"bundle1", "bundle2"}, + files: map[string]string{ + "bundle1/.manifest": `{ + "roots": ["test1"], + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "bundle1/test1.rego": `package test1 +p[1] { + input.x == 1 +}`, + "bundle1/test2.rego": `package test1 +p contains 2 if { + input.x == 1 +}`, + "bundle2/.manifest": `{ + "roots": ["test2"], + "rego_version": 1, + "file_rego_versions": { + "*/test4.rego": 0 + } +}`, + "bundle2/test3.rego": `package test2 +p contains 3 if { + input.x == 1 +}`, + "bundle2/test4.rego": `package test2 +p[4] { + input.x == 1 +}`, + "capabilities.json": func() string { + caps := ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)) + caps.Features = append(caps.Features, ast.FeatureRegoV1) + bs, err := json.Marshal(caps) + if err != nil { + t.Fatal(err) + } + return string(bs) + }(), + }, + expManifest: `{"revision":"","roots":["test1","test2"],"rego_version":0,"file_rego_versions":{"%ROOT%/bundle1/test2.rego":1,"%ROOT%/bundle2/test3.rego":1}}`, + }, + { + note: "multiple bundles with different rego-versions, v1-compatible", + v1Compatible: true, + roots: []string{"bundle1", "bundle2"}, + files: map[string]string{ + "bundle1/.manifest": `{ + "roots": ["test1"], + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "bundle1/test1.rego": `package test1 +p[1] { + input.x == 1 +}`, + "bundle1/test2.rego": `package test1 +p contains 2 if { + input.x == 1 +}`, + "bundle2/.manifest": `{ + "roots": ["test2"], + "rego_version": 1, + "file_rego_versions": { + "*/test4.rego": 0 + } +}`, + "bundle2/test3.rego": `package test2 +p contains 3 if { + input.x == 1 +}`, + "bundle2/test4.rego": `package test2 +p[4] { + input.x == 1 +}`, + }, + expManifest: `{"revision":"","roots":["test1","test2"],"rego_version":1,"file_rego_versions":{"%ROOT%/bundle1/test1.rego":0,"%ROOT%/bundle2/test4.rego":0}}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(tc.files, func(root string) { + params := newBuildParams() + params.outputFile = path.Join(root, "bundle.tar.gz") + params.bundleMode = true + params.v0Compatible = tc.v0Compatible + params.v1Compatible = tc.v1Compatible + + if tc.capabilities != nil { + params.capabilities = newCapabilitiesFlag() + params.capabilities.C = tc.capabilities + } + + if _, ok := tc.files["capabilities.json"]; ok { + _ = params.capabilities.Set(path.Join(root, "capabilities.json")) + } + + var roots []string + if len(tc.roots) == 0 { + roots = []string{root} + } else { + for _, r := range tc.roots { + roots = append(roots, path.Join(root, r)) + } + } + err := dobuild(params, roots) + if tc.expErrs != nil { + if err == nil { + t.Fatal("expected error but got none") + } + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected error:\n\n%q\n\nbut got:\n\n%v", expErr, err) + } + } + } else { + if err != nil { + t.Fatal(err) + } + + _, err = loader.NewFileLoader().AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer func() { + _ = f.Close() + }() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + + for { + f, err := tr.Next() + if err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + + if f.Name == "/.manifest" { + b, err := io.ReadAll(tr) + if err != nil { + t.Fatal(err) + } + expManifest := strings.ReplaceAll(tc.expManifest, "%ROOT%", root) + if !strings.Contains(string(b), expManifest) { + t.Fatalf("expected manifest:\n\n%v\n\nbut got:\n\n%v", expManifest, string(b)) + } + } + } + } + }) + }) + } +} + +func capsWithoutFeat(regoVersion ast.RegoVersion, feat ...string) *ast.Capabilities { + caps := ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(regoVersion)) + + feats := make([]string, 0, len(caps.Features)) + for _, f := range caps.Features { + skip := slices.Contains(feat, f) + if !skip { + feats = append(feats, f) + } + } + caps.Features = feats + + return caps +} + +func TestBuildBundleFromOtherBundles(t *testing.T) { + type bundleInfo map[string]string + + tests := []struct { + note string + v0Compatible bool + v1Compatible bool + capabilities *ast.Capabilities + bundles map[string]bundleInfo + expBundle bundleInfo + expErrs []string + }{ + { + note: "single bundle", + bundles: map[string]bundleInfo{ + "bundle.tar.gz": { + "policy.rego": `package test + +p := input.x == 1 +`, + }, + }, + expBundle: bundleInfo{ + "/data.json": `{} +`, + "/.manifest": `{"revision":"","roots":[""],"rego_version":%DEFAULT_REGO_VERSION%} +`, + "%ROOT%/bundle.tar.gz/policy.rego": `package test + +p := input.x == 1 +`, + }, + }, + { + note: "single bundle, --v1-compatible", + v1Compatible: true, + bundles: map[string]bundleInfo{ + "bundle.tar.gz": { + "policy.rego": `package test +p if { + input.x == 1 +}`, + }, + }, + expBundle: bundleInfo{ + "/data.json": `{} +`, + "/.manifest": `{"revision":"","roots":[""],"rego_version":1} +`, + "%ROOT%/bundle.tar.gz/policy.rego": `package test + +p if { + input.x == 1 +} +`, + }, + }, + { + note: "single v0 bundle", + bundles: map[string]bundleInfo{ + "bundle.tar.gz": { + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +p { + input.x == 1 +}`, + }, + }, + expBundle: bundleInfo{ + "/data.json": `{} +`, + "/.manifest": `{"revision":"","roots":[""],"rego_version":0} +`, + "%ROOT%/bundle.tar.gz/policy.rego": `package test + +p { + input.x == 1 +} +`, + }, + }, + { + note: "single v0 bundle, --v1-compatible", + v1Compatible: true, + bundles: map[string]bundleInfo{ + "bundle.tar.gz": { + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +p { + input.x == 1 +}`, + }, + }, + // We don't expect parse/compile errors, as the bundle rego-version is 0, which overrides the --v1-compatible flag. + expBundle: bundleInfo{ + "/data.json": `{} +`, + "/.manifest": `{"revision":"","roots":[""],"rego_version":0} +`, + "%ROOT%/bundle.tar.gz/policy.rego": `package test + +p { + input.x == 1 +} +`, + }, + }, + { + note: "single v1 bundle, --v0-compatible", + v1Compatible: true, + bundles: map[string]bundleInfo{ + "bundle.tar.gz": { + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +p if { + input.x == 1 +}`, + }, + }, + // We don't expect parse/compile errors, as the bundle rego-version is 0, which overrides the --v1-compatible flag. + expBundle: bundleInfo{ + "/data.json": `{} +`, + "/.manifest": `{"revision":"","roots":[""],"rego_version":1} +`, + "%ROOT%/bundle.tar.gz/policy.rego": `package test + +p if { + input.x == 1 +} +`, + }, + }, + { + note: "single v0 bundle, v1 per-file override", + bundles: map[string]bundleInfo{ + "bundle.tar.gz": { + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy_1.rego": 1 + } +}`, + "policy_0.rego": `package test +p { + input.x == 1 +}`, + "policy_1.rego": `package test +q contains 1 if { + input.x == 1 +}`, + }, + }, + expBundle: bundleInfo{ + "/data.json": `{} +`, + "/.manifest": `{"revision":"","roots":[""],"rego_version":0,"file_rego_versions":{"%ROOT%/bundle.tar.gz/policy_1.rego":1}} +`, + "%ROOT%/bundle.tar.gz/policy_0.rego": `package test + +p { + input.x == 1 +} +`, + "%ROOT%/bundle.tar.gz/policy_1.rego": `package test + +q contains 1 if { + input.x == 1 +} +`, + }, + }, + { + note: "single v0 bundle, v1 per-file override, --v1-compatible", + v1Compatible: true, + bundles: map[string]bundleInfo{ + "bundle.tar.gz": { + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy_1.rego": 1 + } +}`, + "policy_0.rego": `package test +p { + input.x == 1 +}`, + "policy_1.rego": `package test +q contains 1 if { + input.x == 1 +}`, + }, + }, + expBundle: bundleInfo{ + "/data.json": `{} +`, + "/.manifest": `{"revision":"","roots":[""],"rego_version":0,"file_rego_versions":{"%ROOT%/bundle.tar.gz/policy_1.rego":1}} +`, + "%ROOT%/bundle.tar.gz/policy_0.rego": `package test + +p { + input.x == 1 +} +`, + "%ROOT%/bundle.tar.gz/policy_1.rego": `package test + +q contains 1 if { + input.x == 1 +} +`, + }, + }, + { + note: "single v1 bundle, v0 per-file override", + bundles: map[string]bundleInfo{ + "bundle.tar.gz": { + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy_0.rego": 0 + } +}`, + "policy_0.rego": `package test +p { + input.x == 1 +}`, + "policy_1.rego": `package test +q contains 1 if { + input.x == 1 +}`, + }, + }, + expBundle: bundleInfo{ + "/data.json": `{} +`, + "/.manifest": `{"revision":"","roots":[""],"rego_version":1,"file_rego_versions":{"%ROOT%/bundle.tar.gz/policy_0.rego":0}} +`, + "%ROOT%/bundle.tar.gz/policy_0.rego": `package test + +p { + input.x == 1 +} +`, + "%ROOT%/bundle.tar.gz/policy_1.rego": `package test + +q contains 1 if { + input.x == 1 +} +`, + }, + }, + { + note: "single v1 bundle, v0 per-file override, --v0-compatible", + v0Compatible: true, + bundles: map[string]bundleInfo{ + "bundle.tar.gz": { + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy_0.rego": 0 + } +}`, + "policy_0.rego": `package test +p { + input.x == 1 +}`, + "policy_1.rego": `package test +q contains 1 if { + input.x == 1 +}`, + }, + }, + expBundle: bundleInfo{ + "/data.json": `{} +`, + "/.manifest": `{"revision":"","roots":[""],"rego_version":1,"file_rego_versions":{"%ROOT%/bundle.tar.gz/policy_0.rego":0}} +`, + "%ROOT%/bundle.tar.gz/policy_0.rego": `package test + +p { + input.x == 1 +} +`, + "%ROOT%/bundle.tar.gz/policy_1.rego": `package test + +q contains 1 if { + input.x == 1 +} +`, + }, + }, + { + note: "single v1 bundle, v0 per-file override, --v0-compatible, no rego_v1 capabilities feature", + v0Compatible: true, + capabilities: capsWithoutFeat(ast.RegoV0, ast.FeatureRegoV1), + bundles: map[string]bundleInfo{ + "bundle.tar.gz": { + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy_0.rego": 0 + } +}`, + "policy_0.rego": `package test +p { + input.x == 1 +}`, + "policy_1.rego": `package test +q contains 1 if { + input.x == 1 +}`, + }, + }, + expErrs: []string{ + // capabilities doesn't include rego_v1 feature, which must be respected + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "v0 bundle + v1 bundle, --v0-compatible", + v0Compatible: true, + bundles: map[string]bundleInfo{ + "bundle_v0.tar.gz": { + ".manifest": `{"roots": ["test1"], "rego_version": 0}`, + "policy.rego": `package test1 +p { + input.x == 1 +}`, + }, + "bundle_v1.tar.gz": { + ".manifest": `{"roots": ["test2"], "rego_version": 1}`, + "policy.rego": `package test2 +q contains 1 if { + input.x == 1 +}`, + }, + }, + expBundle: bundleInfo{ + "/data.json": `{} +`, + "/.manifest": `{"revision":"","roots":["test1","test2"],"rego_version":0,"file_rego_versions":{"%ROOT%/bundle_v1.tar.gz/policy.rego":1}} +`, + "%ROOT%/bundle_v0.tar.gz/policy.rego": `package test1 + +p { + input.x == 1 +} +`, + "%ROOT%/bundle_v1.tar.gz/policy.rego": `package test2 + +q contains 1 if { + input.x == 1 +} +`, + }, + }, + { + note: "v0 bundle + v1 bundle, --v0-compatible, no rego_v1 capabilities feature", + v0Compatible: true, + capabilities: capsWithoutFeat(ast.RegoV0, ast.FeatureRegoV1), + bundles: map[string]bundleInfo{ + "bundle_v0.tar.gz": { + ".manifest": `{"roots": ["test1"], "rego_version": 0}`, + "policy.rego": `package test1 +p { + input.x == 1 +}`, + }, + "bundle_v1.tar.gz": { + ".manifest": `{"roots": ["test2"], "rego_version": 1}`, + "policy.rego": `package test2 +q contains 1 if { + input.x == 1 +}`, + }, + }, + expErrs: []string{ + // capabilities inferred from --v0-compatible doesn't include rego_v1 feature, which must be respected + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "v0 bundle + v1 bundle, --v1-compatible", + v1Compatible: true, + bundles: map[string]bundleInfo{ + "bundle_v0.tar.gz": { + ".manifest": `{"roots": ["test1"], "rego_version": 0}`, + "policy.rego": `package test1 +p { + input.x == 1 +}`, + }, + "bundle_v1.tar.gz": { + ".manifest": `{"roots": ["test2"], "rego_version": 1}`, + "policy.rego": `package test2 +q contains 1 if { + input.x == 1 +}`, + }, + }, + expBundle: bundleInfo{ + "/data.json": `{} +`, + // We get a v1 bundle with a v0 per-file override + "/.manifest": `{"revision":"","roots":["test1","test2"],"rego_version":1,"file_rego_versions":{"%ROOT%/bundle_v0.tar.gz/policy.rego":0}} +`, + "%ROOT%/bundle_v0.tar.gz/policy.rego": `package test1 + +p { + input.x == 1 +} +`, + "%ROOT%/bundle_v1.tar.gz/policy.rego": `package test2 + +q contains 1 if { + input.x == 1 +} +`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(nil, func(root string) { + var roots []string + for name, files := range tc.bundles { + p := filepath.Join(root, name) + roots = append(roots, p) + filePairs := make([][2]string, 0, len(files)) + for k, v := range files { + filePairs = append(filePairs, [2]string{k, v}) + } + buf := archive.MustWriteTarGz(filePairs) + bf, err := os.Create(p) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + params := newBuildParams() + params.outputFile = path.Join(root, "bundle.tar.gz") + params.bundleMode = true + params.v0Compatible = tc.v0Compatible + params.v1Compatible = tc.v1Compatible + + if tc.capabilities != nil { + params.capabilities.C = tc.capabilities + } + + err := dobuild(params, roots) + if tc.expErrs != nil { + if err == nil { + t.Fatal("expected error but got none") + } + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected error:\n\n%q\n\nbut got:\n\n%v", expErr, err) + } + } + } else { + if err != nil { + t.Fatal(err) + } + + _, err = loader.NewFileLoader().AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer func() { + _ = f.Close() + }() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + + for { + f, err := tr.Next() + if err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + + found := false + for expName, expVal := range tc.expBundle { + expName = strings.ReplaceAll(expName, "%ROOT%", root) + if f.Name == expName { + found = true + b, err := io.ReadAll(tr) + if err != nil { + t.Fatal(err) + } + expVal = strings.ReplaceAll(expVal, "%ROOT%", root) + expVal = strings.ReplaceAll(expVal, "%DEFAULT_REGO_VERSION%", + strconv.Itoa(ast.DefaultRegoVersion.Int())) + if string(b) != expVal { + t.Fatalf("expected %v:\n\n%v\n\nbut got:\n\n%v", expName, expVal, string(b)) + } + break + } + } + if !found { + t.Fatalf("unexpected file in bundle: %v", f.Name) + } + } + } + }) + }) + } +} + +func TestBuild_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + files map[string]string + expFiles map[string]string + expErrs []string + }{ + { + note: "v0 module", + files: map[string]string{ + "test.rego": `package test + p[x] { + x := 42 + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1 module", + files: map[string]string{ + "test.rego": `package test + + p contains x if { + x := 42 + }`, + }, + expFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":1} +`, + "test.rego": `package test + +p contains x if { + x := 42 +} +`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(tc.files, func(root string) { + params := newBuildParams() + params.outputFile = path.Join(root, "bundle.tar.gz") + + err := dobuild(params, []string{root}) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatal("expected error but got nil") + } + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected error:\n\n%v\n\ngot:\n\n%v", expErr, err) + } + } + } else { + if err != nil { + t.Fatal(err) + } + + fl := loader.NewFileLoader() + _, err = fl.AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + // Check that manifest is not written given no input manifest and no other flags + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + + foundFiles := map[string]struct{}{} + for { + f, err := tr.Next() + if err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + foundFiles[path.Base(f.Name)] = struct{}{} + expectedFile := tc.expFiles[path.Base(f.Name)] + if expectedFile != "" { + data, err := io.ReadAll(tr) + if err != nil { + t.Fatal(err) + } + actualFile := string(data) + if actualFile != expectedFile { + t.Fatalf("expected file %s to be:\n\n%v\n\ngot:\n\n%v", f.Name, expectedFile, actualFile) + } + } + } + + for expectedFile := range tc.expFiles { + if _, ok := foundFiles[expectedFile]; !ok { + t.Fatalf("expected file %s not found in bundle, got: %v", expectedFile, foundFiles) + } + } + } + }) + }) + } +} + +func TestBuildWithRegoV1Capability(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + capabilities *ast.Capabilities + files map[string]string + expFiles map[string]string + expErrs []string + }{ + { + note: "v0 module, v0-compatible, no capabilities", + v0Compatible: true, + files: map[string]string{ + "test.rego": `package test + p[x] { + x := 42 + }`, + }, + expFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":0} +`, + "test.rego": `package test + +p[x] { + x := 42 +} +`, + }, + }, + { + note: "v0 module, v0-compatible, v0 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + files: map[string]string{ + "test.rego": `package test + p[x] { + x := 42 + }`, + }, + expFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":0} +`, + "test.rego": `package test + +p[x] { + x := 42 +} +`, + }, + }, + { + note: "v0 module, v0-compatible, v1 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + files: map[string]string{ + "test.rego": `package test + p[x] { + x := 42 + }`, + }, + expFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":0} +`, + "test.rego": `package test + +p[x] { + x := 42 +} +`, + }, + }, + + { + note: "v0 module, not v0-compatible, no capabilities", + files: map[string]string{ + "test.rego": `package test + p[x] { + x := 42 + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0 module, not v0-compatible, v0 capabilities", + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + files: map[string]string{ + "test.rego": `package test + p[x] { + x := 42 + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0 module, not v0-compatible, v0 capabilities without rego_v1 feature", + capabilities: capsWithoutFeat(ast.RegoV0, ast.FeatureRegoV1), + files: map[string]string{ + "test.rego": `package test + p[x] { + x := 42 + }`, + }, + expErrs: []string{ + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "v0 module, not v0-compatible, v1 capabilities", + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + files: map[string]string{ + "test.rego": `package test + p[x] { + x := 42 + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + + { + note: "v1 module, v0-compatible, no capabilities", + v0Compatible: true, + files: map[string]string{ + "test.rego": `package test + + p contains x if { + x := 42 + }`, + }, + expErrs: []string{ + "test.rego:3: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v1 module, v0-compatible, v0 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + files: map[string]string{ + "test.rego": `package test + + p contains x if { + x := 42 + }`, + }, + expErrs: []string{ + "test.rego:3: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v1 module, v0-compatible, v1 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + files: map[string]string{ + "test.rego": `package test + + p contains x if { + x := 42 + }`, + }, + expErrs: []string{ + "test.rego:3: rego_parse_error: var cannot be used for rule name", + }, + }, + + { + note: "v1 module, not v0-compatible, no capabilities", + files: map[string]string{ + "test.rego": `package test + + p contains x if { + x := 42 + }`, + }, + expFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":1} +`, + "test.rego": `package test + +p contains x if { + x := 42 +} +`, + }, + }, + { + note: "v1 module, not v0-compatible, v0 capabilities", + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + files: map[string]string{ + "test.rego": `package test + + p contains x if { + x := 42 + }`, + }, + }, + { + note: "v1 module, not v0-compatible, v0 capabilities without rego_v1 feature", + capabilities: capsWithoutFeat(ast.RegoV0, ast.FeatureRegoV1), + files: map[string]string{ + "test.rego": `package test + + p contains x if { + x := 42 + }`, + }, + expErrs: []string{ + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "v1 module, not v0-compatible, v1 capabilities", + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + files: map[string]string{ + "test.rego": `package test + + p contains x if { + x := 42 + }`, + }, + expFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":1} +`, + "test.rego": `package test + +p contains x if { + x := 42 +} +`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(tc.files, func(root string) { + params := newBuildParams() + params.outputFile = path.Join(root, "bundle.tar.gz") + params.v0Compatible = tc.v0Compatible + params.capabilities.C = tc.capabilities + + err := dobuild(params, []string{root}) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatal("expected error but got nil") + } + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected error:\n\n%v\n\ngot:\n\n%v", expErr, err) + } + } + } else { + if err != nil { + t.Fatal(err) + } + + fl := loader.NewFileLoader() + _, err = fl.AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + // Check that manifest is not written given no input manifest and no other flags + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + + foundFiles := map[string]struct{}{} + for { + f, err := tr.Next() + if err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + foundFiles[path.Base(f.Name)] = struct{}{} + expectedFile := tc.expFiles[path.Base(f.Name)] + if expectedFile != "" { + data, err := io.ReadAll(tr) + if err != nil { + t.Fatal(err) + } + actualFile := string(data) + if actualFile != expectedFile { + t.Fatalf("expected file %s to be:\n\n%v\n\ngot:\n\n%v", f.Name, expectedFile, actualFile) + } + } + } + + for expectedFile := range tc.expFiles { + if _, ok := foundFiles[expectedFile]; !ok { + t.Fatalf("expected file %s not found in bundle, got: %v", expectedFile, foundFiles) + } + } + } + }) + }) + } +} + +func TestBuildWithCompatibleFlags(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + v1Compatible bool + files map[string]string + expectedFiles map[string]string + expectedErr string + }{ + { + note: "v0 compatibility: policy with no rego.v1 or future.keywords imports", + v0Compatible: true, + files: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + expectedErr: "rego_parse_error", + }, + { + note: "v0 compatibility: policy with rego.v1 imports", + v0Compatible: true, + files: map[string]string{ + "test.rego": `package test + import rego.v1 + allow if { + 1 < 2 + }`, + }, + // Imports are preserved + expectedFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":0} +`, + "test.rego": `package test + +import rego.v1 + +allow if { + 1 < 2 +} +`, + }, + }, + { + note: "v0 compatibility: policy with future.keywords imports", + v0Compatible: true, + files: map[string]string{ + "test.rego": `package test + import future.keywords.if + allow if { + 1 < 2 + }`, + }, + // Imports are preserved + expectedFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":0} +`, + "test.rego": `package test + +import future.keywords.if + +allow if { + 1 < 2 +} +`, + }, + }, + { + note: "v1 compatibility: policy with no rego.v1 or future.keywords imports", + v1Compatible: true, + files: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + // Imports are not added in + expectedFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":1} +`, + "test.rego": `package test + +allow if { + 1 < 2 +} +`, + }, + }, + { + note: "v1 compatibility: policy with rego.v1 import", + v1Compatible: true, + files: map[string]string{ + "test.rego": `package test + import rego.v1 + allow if { + 1 < 2 + }`, + }, + // the rego.v1 import is kept to maximize compatibility surface + expectedFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":1} +`, + "test.rego": `package test + +import rego.v1 + +allow if { + 1 < 2 +} +`, + }, + }, + { + note: "v1 compatibility: policy with future.keywords import", + v1Compatible: true, + files: map[string]string{ + "test.rego": `package test + import future.keywords.if + allow if { + 1 < 2 + }`, + }, + // future.keywords imports are kept to maximize compatibility surface + expectedFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":1} +`, + "test.rego": `package test + +import future.keywords.if + +allow if { + 1 < 2 +} +`, + }, + }, + { + note: "v1 compatibility: policy with rego.v1 and future.keywords imports", + v1Compatible: true, + files: map[string]string{ + "test.rego": `package test + import rego.v1 + import future.keywords.if + allow if { + 1 < 2 + }`, + }, + // future.keywords are dropped as these are covered by rego.v1 + expectedFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":1} +`, + "test.rego": `package test + +import rego.v1 + +allow if { + 1 < 2 +} +`, + }, + }, + { + note: "v1 compatibility: missing keywords", + v1Compatible: true, + files: map[string]string{ + "test.rego": `package test + allow[1] { + 1 < 2 + }`, + }, + expectedErr: "rego_parse_error", + }, + // v0 takes precedence over v1 + { + note: "v0+v1 compatibility: policy with no rego.v1 or future.keywords imports", + v0Compatible: true, + v1Compatible: true, + files: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + expectedErr: "rego_parse_error", + }, + { + note: "v0+v1 compatibility: policy with rego.v1 imports", + v0Compatible: true, + v1Compatible: true, + files: map[string]string{ + "test.rego": `package test + import rego.v1 + allow if { + 1 < 2 + }`, + }, + // Imports are preserved + expectedFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":0} +`, + "test.rego": `package test + +import rego.v1 + +allow if { + 1 < 2 +} +`, + }, + }, + { + note: "v0+v1 compatibility: policy with future.keywords imports", + v0Compatible: true, + v1Compatible: true, + files: map[string]string{ + "test.rego": `package test + import future.keywords.if + allow if { + 1 < 2 + }`, + }, + // Imports are preserved + expectedFiles: map[string]string{ + ".manifest": `{"revision":"","roots":[""],"rego_version":0} +`, + "test.rego": `package test + +import future.keywords.if + +allow if { + 1 < 2 +} +`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(tc.files, func(root string) { + params := newBuildParams() + params.outputFile = path.Join(root, "bundle.tar.gz") + params.v0Compatible = tc.v0Compatible + params.v1Compatible = tc.v1Compatible + + err := dobuild(params, []string{root}) + + if tc.expectedErr != "" { + if err == nil { + t.Fatal("expected error but got nil") + } + if !strings.Contains(err.Error(), tc.expectedErr) { + t.Fatalf("expected error %v, got %v", tc.expectedErr, err) + } + } else { + if err != nil { + t.Fatal(err) + } + + fl := loader.NewFileLoader() + if tc.v1Compatible { + fl = fl.WithRegoVersion(ast.RegoV1) + } + _, err = fl.AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + // Check that manifest is not written given no input manifest and no other flags + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + + foundFiles := map[string]struct{}{} + for { + f, err := tr.Next() + if err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + foundFiles[path.Base(f.Name)] = struct{}{} + expectedFile := tc.expectedFiles[path.Base(f.Name)] + if expectedFile != "" { + data, err := io.ReadAll(tr) + if err != nil { + t.Fatal(err) + } + actualFile := string(data) + if actualFile != expectedFile { + t.Fatalf("expected file %s to be:\n\n%v\n\ngot:\n\n%v", f.Name, expectedFile, actualFile) + } + } + } + + for expectedFile := range tc.expectedFiles { + if _, ok := foundFiles[expectedFile]; !ok { + t.Fatalf("expected file %s not found in bundle, got: %v", expectedFile, foundFiles) + } + } + } + }) + }) + } +} + +func TestBuildOptimizedWithRegoVersion(t *testing.T) { + tests := []struct { + note string + v1Compatible bool + regoV1ImportCapable bool + files map[string]string + expectedFiles map[string]string + }{ + { + note: "v0, no future keywords", + v1Compatible: false, + regoV1ImportCapable: true, + files: map[string]string{ + "test.rego": `package test +# METADATA +# entrypoint: true +p[v] { + v := input.v +} +`, + }, + expectedFiles: map[string]string{ + "/.manifest": `{"revision":"","roots":[""],"rego_version":0} +`, + // rego.v1 import added to optimized support module + "/optimized/test.rego": `package test + +import rego.v1 + +p contains __local0__1 if { + __local0__1 = input.v +} +`, + }, + }, + { + note: "v0, No future keywords, not rego.v1 import capable", + v1Compatible: false, + regoV1ImportCapable: false, + files: map[string]string{ + "test.rego": `package test +# METADATA +# entrypoint: true +p[v] { + v := input.v +} +`, + }, + expectedFiles: map[string]string{ + "/.manifest": `{"revision":"","roots":[""],"rego_version":0} +`, + // rego.v1 import NOT added to optimized support module + "/optimized/test.rego": `package test + +p[__local0__1] { + __local0__1 = input.v +} +`, + }, + }, + { + note: "v1, No imports", + v1Compatible: true, + regoV1ImportCapable: true, + files: map[string]string{ + "test.rego": `package test +# METADATA +# entrypoint: true +p[k] contains v if { + k := "foo" + v := input.v +} +`, + }, + expectedFiles: map[string]string{ + "/.manifest": `{"revision":"","roots":[""],"rego_version":1} +`, + "/optimized/test/p.rego": `package test.p + +foo contains __local1__1 if { + __local1__1 = input.v +} +`, + }, + }, + { + note: "v1, rego.v1 imported", + v1Compatible: true, + regoV1ImportCapable: true, + files: map[string]string{ + "test.rego": `package test +import rego.v1 +# METADATA +# entrypoint: true +p[k] contains v if { + k := "foo" + v := input.v +} +`, + }, + // Note: the rego.v1 import isn't added to the optimized module. + // This is ok, as the bundle was built with the --v1-compatible flag, + // and is tagged with a rego-version to inform the consumer. + expectedFiles: map[string]string{ + "/.manifest": `{"revision":"","roots":[""],"rego_version":1} +`, + "/optimized/test/p.rego": `package test.p + +foo contains __local1__1 if { + __local1__1 = input.v +} +`, + }, + }, + { + note: "v1, future.keywords imported", + v1Compatible: true, + regoV1ImportCapable: true, + files: map[string]string{ + "test.rego": `package test +import future.keywords +# METADATA +# entrypoint: true +p[k] contains v if { + k := "foo" + v := input.v +} +`, + }, + expectedFiles: map[string]string{ + "/.manifest": `{"revision":"","roots":[""],"rego_version":1} +`, + "/optimized/test/p.rego": `package test.p + +foo contains __local1__1 if { + __local1__1 = input.v +} +`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(tc.files, func(root string) { + params := newBuildParams() + params.outputFile = path.Join(root, "bundle.tar.gz") + params.v0Compatible = !tc.v1Compatible + params.v1Compatible = tc.v1Compatible + params.optimizationLevel = 1 + + if !tc.regoV1ImportCapable { + caps := newCapabilitiesFlag() + caps.C = ast.CapabilitiesForThisVersion() + caps.C.Features = []string{ + ast.FeatureRefHeadStringPrefixes, + ast.FeatureRefHeads, + } + params.capabilities = caps + } + + err := dobuild(params, []string{root}) + + if err != nil { + t.Fatal(err) + } + + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + + foundFiles := map[string]struct{}{} + for { + f, err := tr.Next() + if err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + foundFiles[f.Name] = struct{}{} + expectedFile := tc.expectedFiles[f.Name] + if expectedFile != "" { + data, err := io.ReadAll(tr) + if err != nil { + t.Fatal(err) + } + actualFile := string(data) + if actualFile != expectedFile { + t.Fatalf("expected file %s to be:\n\n%v\n\ngot:\n\n%v", f.Name, expectedFile, actualFile) + } + } + } + + for expectedFile := range tc.expectedFiles { + if _, ok := foundFiles[expectedFile]; !ok { + t.Fatalf("expected file %s not found in bundle, got: %v", expectedFile, foundFiles) + } + } + }) + }) + } +} + +// TestBuildWithFollowSymlinks tests that the build command follows symlinks when building a bundle. +// This test uses a local tmp filesystem to create a directory with a symlink to a file in it's root +// and a local file in the bundle directory, and verifies that the built bundle contains both the symlink +// and the regular file. +// There's probably some common utilities that could be extracted at some point but for now this code is +// local to the test until we need to reuse it elsewhere. +func TestBuildWithFollowSymlinks(t *testing.T) { + rootDir := t.TempDir() + bundleDir := path.Join(rootDir, "bundle") + err := os.Mkdir(bundleDir, 0777) + if err != nil { + t.Fatal(err) + } + + // create a regular file in our temp bundle directory + err = os.WriteFile(filepath.Join(bundleDir, "foo.rego"), []byte("package foo\none = 1"), 0777) + if err != nil { + t.Fatal(err) + } + + // create a regular file in the root directory of our tmp directory that we will symlink into the bundle directory later + err = os.WriteFile(filepath.Join(rootDir, "bar.rego"), []byte("package foo\ntwo = 2"), 0777) + if err != nil { + t.Fatal(err) + } + + // create a symlink in the bundle directory to the file in the root directory + err = os.Symlink(filepath.Join(rootDir, "bar.rego"), filepath.Join(bundleDir, "bar.rego")) + if err != nil { + t.Fatal(err) + } + + params := newBuildParams() + params.outputFile = path.Join(rootDir, "test.tar.gz") + params.bundleMode = true + params.followSymlinks = true + params.v1Compatible = true + + err = dobuild(params, []string{bundleDir}) + if err != nil { + t.Fatal(err) + } + + // verify that the bundle is a loadable bundle + _, err = loader.NewFileLoader().AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + + // map of file name -> file content + expectedFiles := map[string]string{ + bundleDir + "/foo.rego": "package foo\n\none := 1", + bundleDir + "/bar.rego": "package foo\n\ntwo := 2", + "/.manifest": `{"revision":"","roots":[""],"rego_version":1}`, + "/data.json": "{}", + } + + foundFiles := make(map[string]string, 4) + for f, err := tr.Next(); err != io.EOF; f, err = tr.Next() { + if err != nil { + t.Fatal(err) + } + + // ensure that all the files are regular files in the bundle + // and that no symlinks were copied + if mode := f.FileInfo().Mode(); !mode.IsRegular() { + t.Fatalf("expected regular file for file %s but got %s", f.FileInfo().Name(), mode.String()) + } + // read the file content + data, err := io.ReadAll(tr) + if err != nil { + t.Fatalf("failed to read file %s: %v", f.FileInfo().Name(), err) + } + foundFiles[f.Name] = string(data) + } + + if len(foundFiles) != 4 { + t.Fatalf("expected four files in bundle but got %d", len(foundFiles)) + } + + for name, contents := range foundFiles { + // trim added whitespace because it's annoying and makes the test less readable + contents := strings.Trim(contents, "\n") + // check that the file content matches the expected content + expectedContent, ok := expectedFiles[name] + if !ok { + t.Fatalf("unexpected file %s in bundle", name) + } + + if contents != expectedContent { + t.Fatalf("expected file %s to contain:\n\n%v\n\ngot:\n\n%v", name, expectedContent, contents) + } + } +} + +// TestBuildWithFollowSymlinksEntireDir tests that the build command can build a bundle from a symlinked directory. +// This test uses a local tmp filesystem to create a directory with a local file in the bundle directory, and +// verifies that the built bundle contains the files from the symlinked directory. +func TestBuildWithFollowSymlinksEntireDir(t *testing.T) { + rootDir := t.TempDir() + defer func() { + if err := os.RemoveAll(rootDir); err != nil { + t.Fatal(err) + } + }() + bundleDir := path.Join(rootDir, "src") + err := os.Mkdir(bundleDir, 0777) + if err != nil { + t.Fatal(err) + } + + // create a regular file in our temp bundle directory + err = os.WriteFile(filepath.Join(bundleDir, "foo.rego"), []byte("package foo\none = 1"), 0777) + if err != nil { + t.Fatal(err) + } + + symlinkDir := path.Join(rootDir, "symlink") + err = os.Mkdir(symlinkDir, 0777) + if err != nil { + t.Fatal(err) + } + + // create a symlink in the symlink directory to the src directory + err = os.Symlink(bundleDir, filepath.Join(symlinkDir, "linked")) + if err != nil { + t.Fatal(err) + } + + params := newBuildParams() + params.outputFile = path.Join(rootDir, "test.tar.gz") + params.bundleMode = true + params.followSymlinks = true + params.v1Compatible = true + + err = dobuild(params, []string{symlinkDir + "/linked/"}) + if err != nil { + t.Fatal(err) + } + + // verify that the bundle is a loadable bundle + _, err = loader.NewFileLoader().AsBundle(params.outputFile) + if err != nil { + t.Fatal(err) + } + + f, err := os.Open(params.outputFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + + // map of file name -> file content + expectedFiles := map[string]string{ + path.Join(symlinkDir, "linked", "foo.rego"): "package foo\n\none := 1", + "/.manifest": `{"revision":"","roots":[""],"rego_version":1}`, + "/data.json": "{}", + } + + foundFiles := make(map[string]string, 3) + for f, err := tr.Next(); err != io.EOF; f, err = tr.Next() { + if err != nil { + t.Fatal(err) + } + + // ensure that all the files are regular files in the bundle + // and that no symlinks were copied + if mode := f.FileInfo().Mode(); !mode.IsRegular() { + t.Fatalf("expected regular file for file %s but got %s", f.FileInfo().Name(), mode.String()) + } + // read the file content + data, err := io.ReadAll(tr) + if err != nil { + t.Fatalf("failed to read file %s: %v", f.FileInfo().Name(), err) + } + foundFiles[f.Name] = string(data) + } + + if len(foundFiles) != 3 { + t.Fatalf("expected three files in bundle but got %d", len(foundFiles)) + } + + for name, contents := range foundFiles { + // trim added whitespace because it's annoying and makes the test less readable + contents := strings.Trim(contents, "\n") + // check that the file content matches the expected content + expectedContent, ok := expectedFiles[name] + if !ok { + t.Fatalf("unexpected file %s in bundle", name) + } + + if contents != expectedContent { + t.Fatalf("expected file %s to contain:\n\n%v\n\ngot:\n\n%v", name, expectedContent, contents) + } + } +} + +func TestBuildManifestWarning(t *testing.T) { + testCases := map[string]struct { + files map[string]string + bundleMode bool + buildArgs []string + expectedStderr func(root string) string + }{ + "warns when manifest ignored": { + files: map[string]string{ + "bundle/.manifest": `{"revision":"1.0.0","roots":["foo"]}`, + "bundle/data.json": `{"data": "value"}`, + }, + bundleMode: false, + buildArgs: []string{"bundle"}, + expectedStderr: func(root string) string { + return fmt.Sprintf("Warning: .manifest file found in %q but -b flag not specified. Manifest will be ignored.\n", path.Join(root, "bundle")) + }, + }, + "no warning when bundle mode enabled": { + files: map[string]string{ + "bundle/.manifest": `{"revision":"1.0.0","roots":["foo"]}`, + "bundle/foo/data.json": `{"data": "value"}`, + }, + bundleMode: true, + buildArgs: []string{"bundle"}, + expectedStderr: func(root string) string { + return "" + }, + }, + "no warning when no manifest exists": { + files: map[string]string{ + "bundle/data.json": `{"data": "value"}`, + }, + bundleMode: false, + buildArgs: []string{"bundle"}, + expectedStderr: func(root string) string { + return "" + }, + }, + "warns for multiple bundles with manifests": { + files: map[string]string{ + "bundle1/.manifest": `{"revision":"1.0.0","roots":["foo"]}`, + "bundle1/foo/data.json": `{"data1": "value1"}`, + "bundle2/.manifest": `{"revision":"2.0.0","roots":["bar"]}`, + "bundle2/bar/data.json": `{"data2": "value2"}`, + "bundle3/baz/data.json": `{"data3": "value3"}`, + }, + bundleMode: false, + buildArgs: []string{"bundle1", "bundle2", "bundle3"}, + expectedStderr: func(root string) string { + return fmt.Sprintf(`Warning: .manifest file found in %q but -b flag not specified. Manifest will be ignored. +Warning: .manifest file found in %q but -b flag not specified. Manifest will be ignored. +`, + path.Join(root, "bundle1"), path.Join(root, "bundle2")) + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + var stderr bytes.Buffer + test.WithTempFS(tc.files, func(root string) { + params := newBuildParams() + params.outputFile = path.Join(root, "output.tar.gz") + params.bundleMode = tc.bundleMode + params.stderr = &stderr + + var args []string + for _, arg := range tc.buildArgs { + args = append(args, path.Join(root, arg)) + } + + err := dobuild(params, args) + if err != nil { + t.Fatal(err) + } + + stderrOutput := stderr.String() + expectedStderr := tc.expectedStderr(root) + + if stderrOutput != expectedStderr { + t.Fatalf("Expected stderr:\n%q\nGot:\n%q", expectedStderr, stderrOutput) + } + }) + }) + } +} diff --git a/third_party/opa/cmd/capabilities.go b/third_party/opa/cmd/capabilities.go new file mode 100644 index 000000000000..ddbde5053024 --- /dev/null +++ b/third_party/opa/cmd/capabilities.go @@ -0,0 +1,141 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/open-policy-agent/opa/cmd/internal/env" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/spf13/cobra" +) + +type capabilitiesParams struct { + showCurrent bool + version string + file string + v0Compatible bool +} + +func (p *capabilitiesParams) regoVersion() ast.RegoVersion { + if p.v0Compatible { + return ast.RegoV0 + } + return ast.DefaultRegoVersion +} + +func initCapabilities(root *cobra.Command, brand string) { + executable := root.Name() + + capabilitiesParams := capabilitiesParams{} + + var capabilitiesCommand = &cobra.Command{ + Use: "capabilities", + Short: "Print the capabilities of " + brand, + Long: `Show capabilities for ` + brand + `. + +The 'capabilities' command prints the ` + brand + ` capabilities, prior to and including the version of ` + brand + ` used. + +Print a list of all existing capabilities version names + + $ ` + executable + ` capabilities + v0.17.0 + v0.17.1 + ... + v0.37.1 + v0.37.2 + v0.38.0 + ... + +Print the capabilities of the current version + + $ ` + executable + ` capabilities --current + { + "builtins": [...], + "future_keywords": [...], + "wasm_abi_versions": [...] + } + +Print the capabilities of a specific version + + $ ` + executable + ` capabilities --version v0.32.1 + { + "builtins": [...], + "future_keywords": null, + "wasm_abi_versions": [...] + } + +Print the capabilities of a capabilities file + + $ ` + executable + ` capabilities --file ./capabilities/v0.32.1.json + { + "builtins": [...], + "future_keywords": null, + "wasm_abi_versions": [...] + } + +`, + PreRunE: func(cmd *cobra.Command, _ []string) error { + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + RunE: func(cmd *cobra.Command, _ []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + cs, err := doCapabilities(capabilitiesParams) + if err != nil { + return err + } + fmt.Println(cs) + return nil + }, + } + capabilitiesCommand.Flags().BoolVar(&capabilitiesParams.showCurrent, "current", false, "print current capabilities") + capabilitiesCommand.Flags().StringVar(&capabilitiesParams.version, "version", "", "print capabilities of a specific version") + capabilitiesCommand.Flags().StringVar(&capabilitiesParams.file, "file", "", "print capabilities defined by a file") + addV0CompatibleFlag(capabilitiesCommand.Flags(), &capabilitiesParams.v0Compatible, false) + + root.AddCommand(capabilitiesCommand) +} + +func doCapabilities(params capabilitiesParams) (string, error) { + var ( + c *ast.Capabilities + err error + ) + + if len(params.version) > 0 { + c, err = ast.LoadCapabilitiesVersion(params.version) + } else if len(params.file) > 0 { + c, err = ast.LoadCapabilitiesFile(params.file) + } else if params.showCurrent { + c = ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(params.regoVersion())) + } else { + return showVersions() + } + + if err != nil { + return "", err + } + + bs, err := json.MarshalIndent(c, "", " ") + if err != nil { + return "", err + } + return string(bs), nil + +} + +func showVersions() (string, error) { + cvs, err := ast.LoadCapabilitiesVersions() + if err != nil { + return "", err + } + + t := strings.Join(cvs, "\n") + return t, nil +} diff --git a/third_party/opa/cmd/capabilities_test.go b/third_party/opa/cmd/capabilities_test.go new file mode 100644 index 000000000000..24d47d39fb7a --- /dev/null +++ b/third_party/opa/cmd/capabilities_test.go @@ -0,0 +1,144 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "bytes" + "path" + "slices" + "sort" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestCapabilitiesNoArgs(t *testing.T) { + t.Run("test with no arguments", func(t *testing.T) { + _, err := doCapabilities(capabilitiesParams{}) + if err != nil { + t.Fatal("expected success", err) + } + }) +} + +func TestCapabilitiesVersion(t *testing.T) { + t.Run("test with version", func(t *testing.T) { + params := capabilitiesParams{ + version: "v0.39.0", + } + _, err := doCapabilities(params) + if err != nil { + t.Fatal("expected success", err) + } + }) +} + +func TestCapabilitiesFile(t *testing.T) { + t.Run("test with file", func(t *testing.T) { + files := map[string]string{ + "test-capabilities.json": ` + { + "builtins": [ + { + "name": "plus", + "infix": "+", + "decl": { + "type": "function", + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + } + } + } + ] + } + `, + } + + test.WithTempFS(files, func(root string) { + params := capabilitiesParams{ + file: path.Join(root, "test-capabilities.json"), + } + _, err := doCapabilities(params) + + if err != nil { + t.Fatal("expected success", err) + } + }) + + }) +} + +func TestCapabilitiesCurrent(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + expFeatures []string + expFutureKeywords []string + }{ + { + note: "current", + expFeatures: []string{ + ast.FeatureRegoV1, + ast.FeatureKeywordsInRefs, + }, + }, + { + note: "current --v0-compatible", + v0Compatible: true, + expFeatures: []string{ + ast.FeatureRefHeadStringPrefixes, + ast.FeatureRefHeads, + ast.FeatureRegoV1Import, + ast.FeatureRegoV1, + ast.FeatureKeywordsInRefs, + }, + expFutureKeywords: []string{ + "in", + "every", + "contains", + "if", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + // These are sorted in the output + sort.Strings(tc.expFutureKeywords) + sort.Strings(tc.expFeatures) + + params := capabilitiesParams{ + showCurrent: true, + v0Compatible: tc.v0Compatible, + } + capsStr, err := doCapabilities(params) + if err != nil { + t.Fatal("expected success", err) + } + + caps, err := ast.LoadCapabilitiesJSON(bytes.NewReader([]byte(capsStr))) + if err != nil { + t.Fatal("expected success", err) + } + + if !slices.Equal(caps.Features, tc.expFeatures) { + t.Errorf("expected features:\n\n%v\n\nbut got:\n\n%v", tc.expFeatures, caps.Features) + } + + if !slices.Equal(caps.FutureKeywords, tc.expFutureKeywords) { + t.Errorf("expected future keywords:\n\n%v\n\nbut got:\n\n%v", tc.expFutureKeywords, caps.FutureKeywords) + } + }) + } +} diff --git a/third_party/opa/cmd/check.go b/third_party/opa/cmd/check.go new file mode 100644 index 000000000000..10dfbead89ac --- /dev/null +++ b/third_party/opa/cmd/check.go @@ -0,0 +1,220 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "errors" + "fmt" + "io/fs" + "maps" + "os" + + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/cmd/internal/env" + pr "github.com/open-policy-agent/opa/internal/presentation" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/util" +) + +type checkParams struct { + format *util.EnumFlag + errLimit int + ignore []string + bundleMode bool + capabilities *capabilitiesFlag + schema *schemaFlags + strict bool + regoV1 bool + v0Compatible bool + v1Compatible bool +} + +func newCheckParams() checkParams { + return checkParams{ + format: formats.Flag(formats.Pretty, formats.JSON), + capabilities: newCapabilitiesFlag(), + schema: &schemaFlags{}, + } +} + +func (p *checkParams) regoVersion() ast.RegoVersion { + // The '--rego-v1' flag takes precedence over the '--v1-compatible' flag. + if p.regoV1 { + return ast.RegoV0CompatV1 + } + // The '--v0-compatible' flag takes precedence over the '--v1-compatible' flag. + if p.v0Compatible { + return ast.RegoV0 + } + if p.v1Compatible { + return ast.RegoV1 + } + return ast.DefaultRegoVersion +} + +func checkModules(params checkParams, args []string) error { + // ensure custom builtins are properly captured + capabilities := params.capabilities.C + if capabilities == nil { + capabilities = ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(params.regoVersion())) + } + + l := loader.NewFileLoader(). + WithRegoVersion(params.regoVersion()). + WithProcessAnnotation(true). + WithBundleLazyLoadingMode(bundle.HasExtension()). + WithCapabilities(capabilities) + + ss, err := loader.Schemas(params.schema.path) + if err != nil { + return err + } + + compiler := ast.NewCompiler(). + SetErrorLimit(params.errLimit). + WithCapabilities(capabilities). + WithSchemas(ss). + WithEnablePrintStatements(true). + WithStrict(params.strict). + WithUseTypeCheckAnnotations(true) + + var modules map[string]*ast.Module + + if params.bundleMode { + bundles := make([]*bundle.Bundle, 0, len(args)) + for _, path := range args { + b, err := l.WithSkipBundleVerification(true).WithFilter(filterFromPaths(params.ignore)).AsBundle(path) + if err != nil { + return err + } + bundles = append(bundles, b) + } + b, err := bundle.Merge(bundles) + if err != nil { + return err + } + + modules = maps.Clone(b.ParsedModules("")) + if len(b.Data) > 0 { + compiler = compiler.WithPathConflictsCheck(mapFinder(b.Data)) + } + } else { + result, err := l.Filtered(args, ignoredOnlyRego(params.ignore).Apply) + if err != nil { + return err + } + + modules = result.ParsedModules() + } + + if compiler.Compile(modules); compiler.Failed() { + return compiler.Errors + } + + return nil +} + +// emulate storage.NonEmpty without having to create storage / transaction +// returned function returns false, nil when m or path is empty +func mapFinder(m map[string]any) func(path []string) (bool, error) { + if len(m) == 0 { + return emptyMapFinder + } + return func(path []string) (bool, error) { + if len(path) == 0 { + return false, nil + } + + node := m + for _, key := range path { + if val, ok := node[key]; ok { + if subMap, ok := val.(map[string]any); ok { + node = subMap + } else { + return true, nil + } + } else { + return false, nil + } + } + return true, nil + } +} + +func emptyMapFinder(path []string) (bool, error) { + return false, nil +} + +func filterFromPaths(paths []string) loader.Filter { + return func(abspath string, info fs.FileInfo, depth int) bool { + return ignored(paths).Apply(abspath, info, depth) + } +} + +func outputErrors(format string, err error) { + out := os.Stdout + if err != nil { + out = os.Stderr + } + + switch format { + case formats.JSON: + if err := pr.JSON(out, pr.Output{Errors: pr.NewOutputErrors(err)}); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + } + default: + fmt.Fprintln(out, err) + } +} + +func initCheck(root *cobra.Command, _ string) { + checkParams := newCheckParams() + + checkCommand := &cobra.Command{ + Use: "check [path [...]]", + Short: "Check Rego source files", + Long: `Check Rego source files for parse and compilation errors. + +If the 'check' command succeeds in parsing and compiling the source file(s), no output +is produced. If the parsing or compiling fails, 'check' will output the errors +and exit with a non-zero exit code.`, + + PreRunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("specify at least one file") + } + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + if err := checkModules(checkParams, args); err != nil { + outputErrors(checkParams.format.String(), err) + return err + } + return nil + }, + } + + addMaxErrorsFlag(checkCommand.Flags(), &checkParams.errLimit) + addIgnoreFlag(checkCommand.Flags(), &checkParams.ignore) + addOutputFormat(checkCommand.Flags(), checkParams.format) + addBundleModeFlag(checkCommand.Flags(), &checkParams.bundleMode, false) + addCapabilitiesFlag(checkCommand.Flags(), checkParams.capabilities) + addSchemaFlags(checkCommand.Flags(), checkParams.schema) + addStrictFlag(checkCommand.Flags(), &checkParams.strict, false) + addRegoV0V1FlagWithDescription(checkCommand.Flags(), &checkParams.regoV1, false, + "check for Rego v0 and v1 compatibility (policies must be compatible with both Rego versions)") + addV0CompatibleFlag(checkCommand.Flags(), &checkParams.v0Compatible, false) + addV1CompatibleFlag(checkCommand.Flags(), &checkParams.v1Compatible, false) + + root.AddCommand(checkCommand) +} diff --git a/third_party/opa/cmd/check_test.go b/third_party/opa/cmd/check_test.go new file mode 100644 index 000000000000..998ac09fafa6 --- /dev/null +++ b/third_party/opa/cmd/check_test.go @@ -0,0 +1,1339 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "encoding/json" + "fmt" + "maps" + "os" + "path" + "path/filepath" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestCheckRespectsCapabilities(t *testing.T) { + tests := []struct { + note string + caps string + policy string + err string + bundleMode bool // check with "-b" flag + }{ + { + note: "builtin defined in caps", + caps: `{ + "builtins": [ + { + "name": "is_foo", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + } + ] + }`, + policy: `package test +p { is_foo("bar") }`, + }, + { + note: "future kw NOT defined in caps", + caps: func() string { + c := ast.CapabilitiesForThisVersion() + c.FutureKeywords = []string{"in"} + c.Features = []string{} + j, err := json.Marshal(c) + if err != nil { + panic(err) + } + return string(j) + }(), + policy: `package test +import future.keywords.if +import future.keywords.in +p if "opa" in input.tools`, + err: "rego_parse_error: unexpected keyword, must be one of [in]", + }, + { + note: "future kw NOT defined in caps, rego-v1 feature", + caps: func() string { + c := ast.CapabilitiesForThisVersion() + c.FutureKeywords = []string{"in"} + c.Features = []string{ast.FeatureRegoV1} + j, err := json.Marshal(c) + if err != nil { + panic(err) + } + return string(j) + }(), + policy: `package test +import future.keywords.if +import future.keywords.in +p if "opa" in input.tools`, + }, + { + note: "future kw are defined in caps", + caps: func() string { + c := ast.CapabilitiesForThisVersion() + c.FutureKeywords = []string{"in", "if"} + j, err := json.Marshal(c) + if err != nil { + panic(err) + } + return string(j) + }(), + policy: `package test +import future.keywords.if +import future.keywords.in +p if "opa" in input.tools`, + }, + { + note: "rego.v1 imported but NOT defined in capabilities", + caps: func() string { + c := ast.CapabilitiesForThisVersion() + c.Features = []string{} + j, err := json.Marshal(c) + if err != nil { + panic(err) + } + return string(j) + }(), + policy: `package test +import rego.v1`, + err: "rego_parse_error: invalid import, `rego.v1` is not supported by current capabilities", + }, + { + note: "rego.v1 imported AND defined in capabilities", + caps: func() string { + c := ast.CapabilitiesForThisVersion() + c.Features = []string{ast.FeatureRegoV1Import} + j, err := json.Marshal(c) + if err != nil { + panic(err) + } + return string(j) + }(), + policy: `package test +import rego.v1`, + }, + { + note: "rego.v1 imported AND rego-v1 in capabilities", + caps: func() string { + c := ast.CapabilitiesForThisVersion() + c.Features = []string{ast.FeatureRegoV1} + j, err := json.Marshal(c) + if err != nil { + panic(err) + } + return string(j) + }(), + policy: `package test +import rego.v1`, + }, + } + + // add same tests for bundle-mode == true: + for i := range tests { + tc := tests[i] + tc.bundleMode = true + tc.note += " (as bundle)" + tests = append(tests, tc) + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "capabilities.json": tc.caps, + "test.rego": tc.policy, + } + + test.WithTempFS(files, func(root string) { + caps := newCapabilitiesFlag() + if err := caps.Set(path.Join(root, "capabilities.json")); err != nil { + t.Fatal(err) + } + params := newCheckParams() + params.capabilities = caps + params.bundleMode = tc.bundleMode + // Capabilities in test cases is pre v1 + params.v0Compatible = true + + err := checkModules(params, []string{root}) + switch { + case err != nil && tc.err != "": + if !strings.Contains(err.Error(), tc.err) { + t.Fatalf("expected err %v, got %v", tc.err, err) + } + return // don't read back bundle below + case err != nil && tc.err == "": + t.Fatalf("unexpected error: %v", err) + case err == nil && tc.err != "": + t.Fatalf("expected error %v, got nil", tc.err) + } + }) + }) + } +} + +func testCheckWithSchemasAnnotationButNoSchemaFlag(policy string) error { + files := map[string]string{ + "test.rego": policy, + } + + var err error + test.WithTempFS(files, func(path string) { + params := newCheckParams() + + err = checkModules(params, []string{path}) + }) + + return err +} + +func TestCheckIgnoresNonRegoFiles(t *testing.T) { + files := map[string]string{ + "test.rego": `package test`, + "test.json": `{"foo": "bar"}`, + "test.yaml": `foo: bar`, + } + + test.WithTempFS(files, func(root string) { + params := newCheckParams() + + err := checkModules(params, []string{root}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + }) +} + +func TestCheckIgnoreBundleMode(t *testing.T) { + t.Parallel() + + files := map[string]string{ + "ignore.rego": `invalid rego`, + "include.rego": `package valid`, + } + + test.WithTempFS(files, func(root string) { + params := newCheckParams() + + params.ignore = []string{"ignore.rego"} + params.bundleMode = true + + err := checkModules(params, []string{root}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + }) +} + +func TestCheckBundleReportsPolicyVsDataConflict(t *testing.T) { + t.Parallel() + + files := map[string]string{ + "policy.rego": "package p\nallow := false\n", + "data.json": `{"p":{"allow":false}}`, + } + + test.WithTempFS(files, func(root string) { + params := newCheckParams() + // Bundle mode required as the check command *should* ignore data entirely otherwise + params.bundleMode = true + + err := checkModules(params, []string{root}) + if err == nil { + t.Fatal("expected error but received none") + } + + exp := fmt.Sprintf( + "1 error occurred: %s:2: rego_compile_error: conflicting rule for data path p/allow found", + filepath.Join(root, "policy.rego"), + ) + if err.Error() != exp { + t.Fatalf("expected error %q, got %q", exp, err.Error()) + } + }) +} + +func TestCheckFailsOnInvalidRego(t *testing.T) { + files := map[string]string{ + "test.rego": `package test +{}`, + "test.json": `{"foo": "bar"}`, + } + expectedError := "rego_parse_error: object cannot be used for rule name" + + test.WithTempFS(files, func(root string) { + params := newCheckParams() + + err := checkModules(params, []string{root}) + if err == nil { + t.Fatalf("expected error %v but received none", expectedError) + } + if !strings.Contains(err.Error(), expectedError) { + t.Fatalf("expected error %v but received %v", expectedError, err) + } + }) +} + +// Assert that 'schemas' annotations with schema refs are only informing the type checker when the --schema flag is used +func TestCheckWithSchemasAnnotationButNoSchemaFlag(t *testing.T) { + policiesWithSchemaRef := []string{` +package test +import rego.v1 +# METADATA +# schemas: +# - input: schema["input"] +p if { + rego.metadata.rule() # presence of rego.metadata.* calls must not trigger unwanted schema evaluation + input.foo == 42 # type mismatch with schema that should be ignored +}`, + ` +package p + +# METADATA +# schemas: +# - data.p.x: schema["nope"] +bug := data.p.x +`} + + for i, pol := range policiesWithSchemaRef { + err := testCheckWithSchemasAnnotationButNoSchemaFlag(pol) + if err != nil { + t.Fatalf("unexpected error from eval policy %d with schema ref: %v", i, err) + } + } + + policyWithInlinedSchema := ` +package test +import rego.v1 +# METADATA +# schemas: +# - input.foo: {"type": "boolean"} +p if { + rego.metadata.rule() # presence of rego.metadata.* calls must not trigger unwanted schema evaluation + input.foo == 42 # type mismatch with schema that should be ignored +}` + + err := testCheckWithSchemasAnnotationButNoSchemaFlag(policyWithInlinedSchema) + // We expect an error here, as inlined schemas are always used for type checking + if !strings.Contains(err.Error(), "rego_type_error: match error") { + t.Fatalf("unexpected error from eval with inlined schema, got: %v", err) + } +} + +func TestCheckRegoV1(t *testing.T) { + cases := []struct { + note string + policy string + expErrs []string + }{ + { + note: "rego.v1 imported, v1 compliant", + policy: `package test +import rego.v1 +p contains x if { + x := [1,2,3] +}`, + }, + { + note: "rego.v1 imported, NOT v1 compliant (parser)", + policy: `package test +import rego.v1 +p contains x { + x := [1,2,3] +} + +q.r`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:7: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "rego.v1 imported, NOT v1 compliant (compiler)", + policy: `package test +import rego.v1 + +import data.foo +import data.bar as foo +`, + expErrs: []string{ + "test.rego:5: rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "keywords imported, v1 compliant", + policy: `package test +import future.keywords.if +import future.keywords.contains +p contains x if { + x := [1,2,3] +}`, + }, + { + note: "keywords imported, NOT v1 compliant", + policy: `package test +import future.keywords.contains +p contains x { + x := [1,2,3] +} + +q.r`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:7: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "keywords imported, NOT v1 compliant (compiler)", + policy: `package test +import future.keywords.if + +input := 1 if { + 1 == 2 +}`, + expErrs: []string{ + "test.rego:4: rego_compile_error: rules must not shadow input (use a different rule name)", + }, + }, + { + note: "no imports, v1 compliant", + policy: `package test +p := 1 +`, + }, + { + note: "no imports, NOT v1 compliant but v0 compliant (compiler)", + policy: `package test +p.x`, + expErrs: []string{ + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "no imports, v1 compliant but NOT v0 compliant", + policy: `package test +p contains x if { + x := [1,2,3] +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: var cannot be used for rule name", // This error actually appears three times: once for 'p'; once for 'contains'; and once for 'x'. All are interpreted as [invalid] rule declarations with no value and body. + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.policy, + } + + test.WithTempFS(files, func(root string) { + params := newCheckParams() + params.regoV1 = true + + err := checkModules(params, []string{root}) + switch { + case err != nil && len(tc.expErrs) > 0: + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected err:\n\n%v\n\ngot:\n\n%v", expErr, err) + } + } + return // don't read back bundle below + case err != nil && len(tc.expErrs) == 0: + t.Fatalf("unexpected error: %v", err) + case err == nil && len(tc.expErrs) > 0: + t.Fatalf("expected error:\n\n%v\n\ngot: none", tc.expErrs) + } + }) + }) + } +} + +func TestCheck_DefaultRegoVersion(t *testing.T) { + cases := []struct { + note string + policy string + expErrs []string + }{ + { + note: "v0 module", + policy: `package test +a[x] { + x := 42 +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1 module", + policy: `package test +a contains x if { + x := 42 +}`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.policy, + } + + test.WithTempFS(files, func(root string) { + params := newCheckParams() + + err := checkModules(params, []string{root}) + switch { + case err != nil && len(tc.expErrs) > 0: + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected err:\n\n%v\n\ngot:\n\n%v", expErr, err) + } + } + return // don't read back bundle below + case err != nil && len(tc.expErrs) == 0: + t.Fatalf("unexpected error: %v", err) + case err == nil && len(tc.expErrs) > 0: + t.Fatalf("expected error:\n\n%v\n\ngot: none", tc.expErrs) + } + }) + }) + } +} + +func TestCheckWithRegoV1Capability(t *testing.T) { + cases := []struct { + note string + v0Compatible bool + capabilities *ast.Capabilities + policy string + expErrs []string + }{ + { + note: "v0 module, v0-compatible, no capabilities", + v0Compatible: true, + policy: `package test +a[x] { + x := 42 +}`, + }, + { + note: "v0 module, v0-compatible, v0 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + policy: `package test +a[x] { + x := 42 +}`, + }, + { + note: "v0 module, v0-compatible, v1 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + policy: `package test +a[x] { + x := 42 +}`, + }, + + { + note: "v0 module, not v0-compatible, no capabilities", + policy: `package test +a[x] { + x := 42 +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0 module, not v0-compatible, v0 capabilities", + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + policy: `package test +a[x] { + x := 42 +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0 module, not v0-compatible, v0 capabilities without rego_v1 feature", + capabilities: capsWithoutFeat(ast.RegoV0, ast.FeatureRegoV1), + policy: `package test +a[x] { + x := 42 +}`, + expErrs: []string{ + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "v0 module, not v0-compatible, v1 capabilities", + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + policy: `package test +a[x] { + x := 42 +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + + { + note: "v1 module, v0-compatible, no capabilities", + v0Compatible: true, + policy: `package test +a contains x if { + x := 42 +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v1 module, v0-compatible, v0 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + policy: `package test +a contains x if { + x := 42 +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v1 module, v0-compatible, v1 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + policy: `package test +a contains x if { + x := 42 +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: var cannot be used for rule name", + }, + }, + + { + note: "v1 module, not v0-compatible, no capabilities", + policy: `package test +a contains x if { + x := 42 +}`, + }, + { + note: "v1 module, not v0-compatible, v0 capabilities", + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + policy: `package test +a contains x if { + x := 42 +}`, + }, + { + note: "v1 module, not v0-compatible, v0 capabilities without rego_v1 feature", + capabilities: capsWithoutFeat(ast.RegoV0, ast.FeatureRegoV1), + policy: `package test +a contains x if { + x := 42 +}`, + expErrs: []string{ + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "v1 module, not v0-compatible, v1 capabilities", + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + policy: `package test +a contains x if { + x := 42 +}`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.policy, + } + + test.WithTempFS(files, func(root string) { + params := newCheckParams() + params.v0Compatible = tc.v0Compatible + params.capabilities.C = tc.capabilities + + err := checkModules(params, []string{root}) + switch { + case err != nil && len(tc.expErrs) > 0: + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected err:\n\n%v\n\ngot:\n\n%v", expErr, err) + } + } + return // don't read back bundle below + case err != nil && len(tc.expErrs) == 0: + t.Fatalf("unexpected error: %v", err) + case err == nil && len(tc.expErrs) > 0: + t.Fatalf("expected error:\n\n%v\n\ngot: none", tc.expErrs) + } + }) + }) + } +} + +func TestCheckCompatibleFlags(t *testing.T) { + cases := []struct { + note string + v0Compatible bool + v1Compatible bool + policy string + expErrs []string + }{ + { + note: "v0, no illegal keywords", + v0Compatible: true, + policy: `package test +p[x] { + x := [1,2,3] +}`, + }, + { + note: "v0, illegal keywords", + v0Compatible: true, + policy: `package test +p contains x if { + x := [1,2,3] +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v0, future.keywords imported", + v0Compatible: true, + policy: `package test +import future.keywords +p contains x if { + x := [1,2,3] +}`, + }, + { + note: "v0, rego.v1 imported", + v0Compatible: true, + policy: `package test +import rego.v1 +p contains x if { + x := [1,2,3] +}`, + }, + { + note: "v1, rego.v1 imported, v1 compliant", + v1Compatible: true, + policy: `package test +import rego.v1 +p contains x if { + x := [1,2,3] +}`, + }, + { + note: "v1, rego.v1 imported, NOT v1 compliant (parser)", + v1Compatible: true, + policy: `package test +import rego.v1 +p contains x { + x := [1,2,3] +} + +q.r`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:7: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1, rego.v1 imported, NOT v1 compliant (compiler)", + v1Compatible: true, + policy: `package test +import rego.v1 + +import data.foo +import data.bar as foo +`, + expErrs: []string{ + "test.rego:5: rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v1, keywords imported, v1 compliant", + v1Compatible: true, + policy: `package test +import future.keywords.if +import future.keywords.contains +p contains x if { + x := [1,2,3] +}`, + }, + { + note: "v1, keywords imported, NOT v1 compliant", + v1Compatible: true, + policy: `package test +import future.keywords.contains +p contains x { + x := [1,2,3] +} + +q.r`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:7: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1, keywords imported, NOT v1 compliant (compiler)", + v1Compatible: true, + policy: `package test +import future.keywords.if + +input := 1 if { + 1 == 2 +}`, + expErrs: []string{ + "test.rego:4: rego_compile_error: rules must not shadow input (use a different rule name)", + }, + }, + { + note: "v1, no imports, v1 compliant", + v1Compatible: true, + policy: `package test +p := 1 +`, + }, + { + note: "v1, no imports, NOT v1 compliant but v0 compliant (compiler)", + v1Compatible: true, + policy: `package test +p.x`, + expErrs: []string{ + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1, no imports, v1 compliant but NOT v0 compliant", + v1Compatible: true, + policy: `package test +p contains x if { + x := [1,2,3] +}`, + }, + // v0 takes precedence over v1 + { + note: "v0+v1, no illegal keywords", + v0Compatible: true, + v1Compatible: true, + policy: `package test +p[x] { + x := [1,2,3] +}`, + }, + { + note: "v0+v1, illegal keywords", + v0Compatible: true, + v1Compatible: true, + policy: `package test +p contains x if { + x := [1,2,3] +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v0+v1, future.keywords imported", + v0Compatible: true, + v1Compatible: true, + policy: `package test +import future.keywords +p contains x if { + x := [1,2,3] +}`, + }, + { + note: "v0+v1, rego.v1 imported", + v0Compatible: true, + v1Compatible: true, + policy: `package test +import rego.v1 +p contains x if { + x := [1,2,3] +}`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.policy, + } + + test.WithTempFS(files, func(root string) { + params := newCheckParams() + params.v0Compatible = tc.v0Compatible + params.v1Compatible = tc.v1Compatible + + err := checkModules(params, []string{root}) + switch { + case err != nil && len(tc.expErrs) > 0: + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected err:\n\n%v\n\ngot:\n\n%v", expErr, err) + } + } + return // don't read back bundle below + case err != nil && len(tc.expErrs) == 0: + t.Fatalf("unexpected error: %v", err) + case err == nil && len(tc.expErrs) > 0: + t.Fatalf("expected error:\n\n%v\n\ngot: none", tc.expErrs) + } + }) + }) + } +} + +func TestCheckWithBundleRegoVersion(t *testing.T) { + cases := []struct { + note string + files map[string]string + expErrs []string + }{ + { + note: "v0.x bundle, illegal keywords", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +p contains x if { + x := [1,2,3] +}`, + }, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v0.x bundle, rego.v1 imported, v1 compliant", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +import rego.v1 +p contains x if { + x := [1,2,3] +}`, + }, + }, + { + note: "v0.x bundle, rego.v1 imported, NOT v1 compliant (parser)", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +import rego.v1 +p contains x { + x := [1,2,3] +} + +q.r`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0.x bundle, rego.v1 imported, NOT v1 compliant (compiler)", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +import rego.v1 + +import data.foo +import data.bar as foo +`, + }, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v0.x bundle, keywords imported, v1 compliant", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +import future.keywords.if +import future.keywords.contains +p contains x if { + x := [1,2,3] +}`, + }, + }, + { + note: "v0.x bundle, no imports, v1 compliant", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +p := 1 +`, + }, + }, + { + note: "v0 bundle, v1 per-file overrides, compliant", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } +}`, + "policy1.rego": `package test +p[x] { + x := [1,2,3] +}`, + "policy2.rego": `package test +q contains x if { + x := [1,2,3] +}`, + }, + }, + { + note: "v0 bundle, v1 per-file overrides (glob), compliant", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/policy2.rego": 1 + } +}`, + "policy1.rego": `package test +p[x] { + x := [1,2,3] +}`, + "policy2.rego": `package test +q contains x if { + x := [1,2,3] +}`, + }, + }, + { + note: "v0 bundle, v1 per-file overrides, incompliant", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } +}`, + "policy1.rego": `package test +p[x] { + x := [1,2,3] +}`, + "policy2.rego": `package test +q[x] { + x := [1,2,3] +}`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + + { + note: "v1.0 bundle, keywords used but not imported", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +p contains x if { + x := [1,2,3] +}`, + }, + }, + { + note: "v1.0 bundle, rego.v1 imported, v1 compliant", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +import rego.v1 +p contains x if { + x := [1,2,3] +}`, + }, + }, + { + note: "v1.0 bundle, rego.v1 imported, NOT v1 compliant (parser)", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +import rego.v1 +p contains x { + x := [1,2,3] +} + +q.r`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1.0 bundle, rego.v1 imported, NOT v1 compliant (compiler)", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +import rego.v1 + +import data.foo +import data.bar as foo +`, + }, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v1.0 bundle, keywords imported, v1 compliant", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +import future.keywords.if +import future.keywords.contains +p contains x if { + x := [1,2,3] +}`, + }, + }, + { + note: "v1.0 bundle, keywords imported, NOT v1 compliant", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +import future.keywords.contains +p contains x { + x := [1,2,3] +} + +q.r`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1.0 bundle, keywords imported, NOT v1 compliant (compiler)", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +import future.keywords.if + +input := 1 if { + 1 == 2 +}`, + }, + expErrs: []string{ + "rego_compile_error: rules must not shadow input (use a different rule name)", + }, + }, + { + note: "v1.0 bundle, no imports, v1 compliant", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +p := 1 +`, + }, + }, + { + note: "v1.0 bundle, no imports, NOT v1 compliant but v0 compliant (compiler)", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +p.x`, + }, + expErrs: []string{ + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1.0 bundle, no imports, v1 compliant but NOT v0 compliant", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +p contains x if { + x := [1,2,3] +}`, + }, + }, + { + note: "v1 bundle, v0 per-file overrides, compliant", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } +}`, + "policy1.rego": `package test +p[x] { + x := [1,2,3] +}`, + "policy2.rego": `package test +q contains x if { + x := [1,2,3] +}`, + }, + }, + { + note: "v1 bundle, v0 per-file overrides (glob), compliant", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "*/policy1.rego": 0 + } +}`, + "policy1.rego": `package test +p[x] { + x := [1,2,3] +}`, + "policy2.rego": `package test +q contains x if { + x := [1,2,3] +}`, + }, + }, + { + note: "v1 bundle, v0 per-file overrides, incompliant", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } +}`, + "policy1.rego": `package test +p contains x if { + x := [1,2,3] +}`, + "policy2.rego": `package test +q contains x if { + x := [1,2,3] +}`, + }, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + }, + }, + } + + bundleTypeCases := []struct { + note string + tar bool + }{ + { + "bundle dir", false, + }, + { + "bundle tar", true, + }, + } + + v1CompatibleFlagCases := []struct { + note string + used bool + }{ + { + "no --v1-compatible", false, + }, + { + "--v1-compatible", true, + }, + } + + for _, bundleType := range bundleTypeCases { + for _, v1CompatibleFlag := range v1CompatibleFlagCases { + for _, tc := range cases { + t.Run(fmt.Sprintf("%s, %s, %s", bundleType.note, v1CompatibleFlag.note, tc.note), func(t *testing.T) { + files := map[string]string{} + + if bundleType.tar { + files["bundle.tar.gz"] = "" + } else { + maps.Copy(files, tc.files) + } + + test.WithTempFS(files, func(root string) { + p := root + if bundleType.tar { + p = filepath.Join(root, "bundle.tar.gz") + files := make([][2]string, 0, len(tc.files)) + for k, v := range tc.files { + files = append(files, [2]string{k, v}) + } + buf := archive.MustWriteTarGz(files) + bf, err := os.Create(p) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + params := newCheckParams() + params.bundleMode = true + params.v1Compatible = v1CompatibleFlag.used + + err := checkModules(params, []string{p}) + switch { + case err != nil && len(tc.expErrs) > 0: + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected err:\n\n%v\n\ngot:\n\n%v", expErr, err) + } + } + return // don't read back bundle below + case err != nil && len(tc.expErrs) == 0: + t.Fatalf("unexpected error: %v", err) + case err == nil && len(tc.expErrs) > 0: + t.Fatalf("expected error:\n\n%v\n\ngot: none", tc.expErrs) + } + }) + }) + } + } + } +} diff --git a/third_party/opa/cmd/commands.go b/third_party/opa/cmd/commands.go new file mode 100644 index 000000000000..dcd8cdb82722 --- /dev/null +++ b/third_party/opa/cmd/commands.go @@ -0,0 +1,52 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "github.com/spf13/cobra" + + iversion "github.com/open-policy-agent/opa/internal/version" +) + +// Backwards compatibility definition. Newer code should use Command. +var RootCommand = Command(nil, "OPA") + +// UserAgent lets you override the OPA UA sent with all the HTTP requests. +// It's another vanity thing -- if you build your own version of OPA, you +// may want to adjust this. +// NOTE(sr): Caution: Please consider this experimental, I have the hunch +// that we'll find a better way to make this adjustment in the future. +func UserAgent(agent string) { + iversion.UserAgent = agent +} + +func Command(rootCommand *cobra.Command, brand string) *cobra.Command { + // rootCommand is the base CLI command that all subcommands are added to. + if rootCommand == nil { + rootCommand = &cobra.Command{ + Use: "opa", + Short: "Open Policy Agent (OPA)", + Long: "An open source project to policy-enable your service.", + } + } + + initBench(rootCommand, brand) + initBuild(rootCommand, brand) + initCapabilities(rootCommand, brand) + initCheck(rootCommand, brand) + initDeps(rootCommand, brand) + initEval(rootCommand, brand) + initExec(rootCommand, brand) + initFmt(rootCommand, brand) + initInspect(rootCommand, brand) + initOracle(rootCommand, brand) + initParse(rootCommand, brand) + initRefactor(rootCommand, brand) + initRun(rootCommand, brand) + initSign(rootCommand, brand) + initTest(rootCommand, brand) + initVersion(rootCommand, brand) + return rootCommand +} diff --git a/third_party/opa/cmd/deps.go b/third_party/opa/cmd/deps.go new file mode 100644 index 000000000000..7386a2e23d6d --- /dev/null +++ b/third_party/opa/cmd/deps.go @@ -0,0 +1,175 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "errors" + "fmt" + "io" + "maps" + "os" + + "github.com/open-policy-agent/opa/internal/presentation" + "github.com/open-policy-agent/opa/v1/dependencies" + + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/cmd/internal/env" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/util" +) + +type depsCommandParams struct { + dataPaths repeatedStringFlag + outputFormat *util.EnumFlag + ignore []string + bundlePaths repeatedStringFlag + v0Compatible bool + v1Compatible bool +} + +func (p *depsCommandParams) regoVersion() ast.RegoVersion { + // The '--v0-compatible' flag takes precedence over the '--v1-compatible' flag. + if p.v0Compatible { + return ast.RegoV0 + } + if p.v1Compatible { + return ast.RegoV1 + } + return ast.DefaultRegoVersion +} + +func newDepsCommandParams() depsCommandParams { + return depsCommandParams{ + outputFormat: formats.Flag(formats.Pretty, formats.JSON), + } +} + +func initDeps(root *cobra.Command, _ string) { + executable := root.Name() + + params := newDepsCommandParams() + + depsCommand := &cobra.Command{ + Use: "deps ", + Short: "Analyze Rego query dependencies", + Long: `Print dependencies of provided query. + +Dependencies are categorized as either base documents, which is any data loaded +from the outside world, or virtual documents, i.e values that are computed from rules. +`, + + Example: ` +Given a policy like this: + + package policy + + allow if is_admin + + is_admin if "admin" in input.user.roles + +To evaluate the dependencies of a simple query (e.g. data.policy.allow), +we'd run ` + executable + ` deps like demonstrated below: + + $ ` + executable + ` deps --data policy.rego data.policy.allow + +------------------+----------------------+ + | BASE DOCUMENTS | VIRTUAL DOCUMENTS | + +------------------+----------------------+ + | input.user.roles | data.policy.allow | + | | data.policy.is_admin | + +------------------+----------------------+ + +From the output we're able to determine that the allow rule depends on +the input.user.roles base document, as well as the virtual document (rule) +data.policy.is_admin. +`, + PreRunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("specify exactly one query argument") + } + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + if err := deps(args, params, os.Stdout); err != nil { + fmt.Fprintln(os.Stderr, err) + return err + } + return nil + }, + } + + addIgnoreFlag(depsCommand.Flags(), ¶ms.ignore) + addDataFlag(depsCommand.Flags(), ¶ms.dataPaths) + addBundleFlag(depsCommand.Flags(), ¶ms.bundlePaths) + addOutputFormat(depsCommand.Flags(), params.outputFormat) + addV1CompatibleFlag(depsCommand.Flags(), ¶ms.v1Compatible, false) + + root.AddCommand(depsCommand) +} + +func deps(args []string, params depsCommandParams, w io.Writer) error { + query, err := ast.ParseBody(args[0]) + if err != nil { + return err + } + + var modules map[string]*ast.Module + + if len(params.dataPaths.v) > 0 { + result, err := loader.NewFileLoader(). + WithBundleLazyLoadingMode(bundle.HasExtension()). + WithRegoVersion(params.regoVersion()). + Filtered(params.dataPaths.v, ignored(params.ignore).Apply) + if err != nil { + return err + } + + modules = result.ParsedModules() + } + + if len(params.bundlePaths.v) > 0 { + modules = make(map[string]*ast.Module, len(params.bundlePaths.v)) + for _, path := range params.bundlePaths.v { + b, err := loader.NewFileLoader().WithBundleLazyLoadingMode(bundle.HasExtension()).WithSkipBundleVerification(true).AsBundle(path) + if err != nil { + return err + } + + maps.Copy(modules, b.ParsedModules(path)) + } + } + + compiler := ast.NewCompiler() + if compiler.Compile(modules); compiler.Failed() { + return compiler.Errors + } + + brs, err := dependencies.Base(compiler, query) + if err != nil { + return err + } + + vrs, err := dependencies.Virtual(compiler, query) + if err != nil { + return err + } + + output := presentation.DepAnalysisOutput{ + Base: brs, + Virtual: vrs, + } + + switch params.outputFormat.String() { + case formats.JSON: + return presentation.JSON(w, output) + default: + return output.Pretty(w) + } +} diff --git a/third_party/opa/cmd/deps_test.go b/third_party/opa/cmd/deps_test.go new file mode 100644 index 000000000000..bfde943a163b --- /dev/null +++ b/third_party/opa/cmd/deps_test.go @@ -0,0 +1,573 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + "io" + "maps" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestDeps_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + query string + expErrs []string + }{ + { + note: "v0 module", + module: `package test +a[x] { + x := 42 +}`, + query: `data.test.p`, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1 module", + module: `package test +a contains x if { + x := 42 +}`, + query: `data.test.a`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.module, + } + + test.WithTempFS(files, func(rootPath string) { + params := newDepsCommandParams() + _ = params.outputFormat.Set(formats.Pretty) + + for f := range files { + _ = params.dataPaths.Set(filepath.Join(rootPath, f)) + } + + err := deps([]string{tc.query}, params, io.Discard) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got nil") + } + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error:\n\n%s\n\ngot:\n\n%s", expErr, err.Error()) + } + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + }) + } +} + +func TestDepsCompatibleFlags(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + v1Compatible bool + module string + query string + expErrs []string + }{ + { + note: "v0, no keywords", + v0Compatible: true, + module: `package test +p[3] { + input.x = 1 +}`, + query: `data.test.p`, + }, + { + note: "v0, keywords not imported, but used", + v0Compatible: true, + module: `package test +p contains 3 if { + input.x = 1 +}`, + query: `data.test.p`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0, keywords imported", + v0Compatible: true, + module: `package test +import future.keywords +p contains 3 if { + input.x = 1 +}`, + query: `data.test.p`, + }, + { + note: "v0, rego.v1 imported", + v0Compatible: true, + module: `package test +import rego.v1 +p contains 3 if { + input.x = 1 +}`, + query: `data.test.p`, + }, + { + note: "v1, no keywords", + v1Compatible: true, + module: `package test +p[3] { + input.x = 1 +}`, + query: `data.test.p`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1, no keyword imports", + v1Compatible: true, + module: `package test +p contains 3 if { + input.x = 1 +}`, + query: `data.test.p`, + }, + { + note: "v1, keywords imported", + v1Compatible: true, + module: `package test +import future.keywords +p contains 3 if { + input.x = 1 +}`, + query: `data.test.p`, + }, + { + note: "v1, rego.v1 imported", + v1Compatible: true, + module: `package test +import rego.v1 +p contains 3 if { + input.x = 1 +}`, + query: `data.test.p`, + }, + // v0 takes precedence over v1 + { + note: "v0+v1, no keywords", + v0Compatible: true, + v1Compatible: true, + module: `package test +p[3] { + input.x = 1 +}`, + query: `data.test.p`, + }, + { + note: "v0+v1, keywords not imported, but used", + v0Compatible: true, + v1Compatible: true, + module: `package test +p contains 3 if { + input.x = 1 +}`, + query: `data.test.p`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0+v1, keywords imported", + v0Compatible: true, + v1Compatible: true, + module: `package test +import future.keywords +p contains 3 if { + input.x = 1 +}`, + query: `data.test.p`, + }, + { + note: "v0+v1, rego.v1 imported", + v0Compatible: true, + v1Compatible: true, + module: `package test +import rego.v1 +p contains 3 if { + input.x = 1 +}`, + query: `data.test.p`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.module, + } + + test.WithTempFS(files, func(rootPath string) { + params := newDepsCommandParams() + params.v0Compatible = tc.v0Compatible + params.v1Compatible = tc.v1Compatible + _ = params.outputFormat.Set(formats.Pretty) + + for f := range files { + _ = params.dataPaths.Set(filepath.Join(rootPath, f)) + } + + err := deps([]string{tc.query}, params, io.Discard) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got nil") + } + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error:\n\n%s\n\ngot:\n\n%s", expErr, err.Error()) + } + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + }) + } +} + +func TestDepsV1WithBundleRegoVersion(t *testing.T) { + tests := []struct { + note string + files map[string]string + query string + expErrs []string + }{ + { + note: "v0.x bundle, no keywords", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +p[3] { + input.x = 1 +}`, + }, + query: `data.test.p`, + }, + { + note: "v0.x bundle, keywords not imported, but used", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +p contains 3 if { + input.x = 1 +}`, + }, + query: `data.test.p`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0.x bundle, keywords imported", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +import future.keywords +p contains 3 if { + input.x = 1 +}`, + }, + query: `data.test.p`, + }, + { + note: "v0.x bundle, rego.v1 imported", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +import rego.v1 +p contains 3 if { + input.x = 1 +}`, + }, + query: `data.test.p`, + }, + { + note: "v0 bundle, v1 per-file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } +}`, + "policy1.rego": `package test +p[3] { + input.x = 1 +}`, + "policy2.rego": `package test +p contains 4 if { + input.x = 1 +}`, + }, + }, + { + note: "v0 bundle, v1 per-file override (glob)", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/bar/*.rego": 1 + } +}`, + "foo/policy1.rego": `package test +p[3] { + input.x = 1 +}`, + "bar/policy2.rego": `package test +p contains 4 if { + input.x = 1 +}`, + }, + }, + { + note: "v0 bundle, v1 per-file override, incompliant", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } +}`, + "policy1.rego": `package test +p[3] { + input.x = 1 +}`, + "policy2.rego": `package test +p[4] { + input.x = 1 +}`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1.0 bundle, no keywords", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +p[3] { + input.x = 1 +}`, + }, + query: `data.test.p`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1.0 bundle, no keyword imports", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +p contains 3 if { + input.x = 1 +}`, + }, + query: `data.test.p`, + }, + { + note: "v1.0 bundle, keywords imported", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +import future.keywords +p contains 3 if { + input.x = 1 +}`, + }, + query: `data.test.p`, + }, + { + note: "v1.0 bundle, rego.v1 imported", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +import rego.v1 +p contains 3 if { + input.x = 1 +}`, + }, + query: `data.test.p`, + }, + { + note: "v1 bundle, v0 per-file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } +}`, + "policy1.rego": `package test +p[3] { + input.x = 1 +}`, + "policy2.rego": `package test +p contains 4 if { + input.x = 1 +}`, + }, + }, + { + note: "v1 bundle, v0 per-file override (glob)", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/foo/*.rego": 0 + } +}`, + "foo/policy1.rego": `package test +p[3] { + input.x = 1 +}`, + "bar/policy2.rego": `package test +p contains 4 if { + input.x = 1 +}`, + }, + }, + { + note: "v1 bundle, v0 per-file override, incompliant", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } +}`, + "policy1.rego": `package test +p contains 3 if { + input.x = 1 +}`, + "policy2.rego": `package test +p contains 4 if { + input.x = 1 +}`, + }, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + } + + bundleTypeCases := []struct { + note string + tar bool + }{ + { + "bundle dir", false, + }, + { + "bundle tar", true, + }, + } + + v1CompatibleFlagCases := []struct { + note string + used bool + }{ + { + "no --v1-compatible", false, + }, + { + "--v1-compatible", true, + }, + } + + for _, bundleType := range bundleTypeCases { + for _, v1CompatibleFlag := range v1CompatibleFlagCases { + for _, tc := range tests { + t.Run(fmt.Sprintf("%s, %s, %s", bundleType.note, v1CompatibleFlag.note, tc.note), func(t *testing.T) { + files := map[string]string{} + + if bundleType.tar { + files["bundle.tar.gz"] = "" + } else { + maps.Copy(files, tc.files) + } + + test.WithTempFS(files, func(root string) { + p := root + if bundleType.tar { + p = filepath.Join(root, "bundle.tar.gz") + files := make([][2]string, 0, len(tc.files)) + for k, v := range tc.files { + files = append(files, [2]string{k, v}) + } + buf := archive.MustWriteTarGz(files) + bf, err := os.Create(p) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + params := newDepsCommandParams() + if err := params.bundlePaths.Set(p); err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + params.v1Compatible = v1CompatibleFlag.used + _ = params.outputFormat.Set(formats.Pretty) + + err := deps([]string{tc.query}, params, io.Discard) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got nil") + } + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error:\n\n%s\n\ngot:\n\n%s", expErr, err.Error()) + } + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + }) + } + } + } +} diff --git a/third_party/opa/cmd/doc.go b/third_party/opa/cmd/doc.go new file mode 100644 index 000000000000..34ad90adefd3 --- /dev/null +++ b/third_party/opa/cmd/doc.go @@ -0,0 +1,5 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd diff --git a/third_party/opa/cmd/eval.go b/third_party/opa/cmd/eval.go new file mode 100644 index 000000000000..5f9f93c3c8f9 --- /dev/null +++ b/third_party/opa/cmd/eval.go @@ -0,0 +1,896 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "os" + "strconv" + "strings" + "time" + + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/cmd/internal/env" + fileurl "github.com/open-policy-agent/opa/internal/file/url" + pr "github.com/open-policy-agent/opa/internal/presentation" + "github.com/open-policy-agent/opa/internal/runtime" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ast/location" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/compile" + "github.com/open-policy-agent/opa/v1/cover" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/profiler" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/lineage" + "github.com/open-policy-agent/opa/v1/util" +) + +var errIllegalUnknownsArg = errors.New("illegal argument with --unknowns, specify string with one or more --unknowns") + +type evalCommandParams struct { + capabilities *capabilitiesFlag + coverage bool + partial bool + unknowns []string + disableInlining []string + nondeterministicBuiltions bool + shallowInlining bool + disableIndexing bool + disableEarlyExit bool + strictBuiltinErrors bool + showBuiltinErrors bool + dataPaths repeatedStringFlag + inputPath string + imports repeatedStringFlag + pkg string + stdin bool + stdinInput bool + explain *util.EnumFlag + metrics bool + instrument bool + ignore []string + outputFormat *util.EnumFlag + profile bool + profileCriteria repeatedStringFlag + profileLimit intFlag + count int + prettyLimit intFlag + fail bool + failDefined bool + bundlePaths repeatedStringFlag + schema *schemaFlags + target *util.EnumFlag + timeout time.Duration + optimizationLevel int + entrypoints repeatedStringFlag + strict bool + v0Compatible bool + v1Compatible bool + traceVarValues bool + ReadAstValuesFromStore bool +} + +func (p *evalCommandParams) regoVersion() ast.RegoVersion { + if p.v0Compatible { + return ast.RegoV0 + } else if p.v1Compatible { + return ast.RegoV1 + } + return ast.DefaultRegoVersion +} + +func newEvalCommandParams() evalCommandParams { + return evalCommandParams{ + capabilities: newCapabilitiesFlag(), + outputFormat: formats.Flag( + formats.JSON, + formats.Values, + formats.Bindings, + formats.Pretty, + formats.Source, + formats.Raw, + formats.Discard, + ), + explain: newExplainFlag([]string{explainModeOff, explainModeFull, explainModeNotes, explainModeFails, explainModeDebug}), + target: util.NewEnumFlag(compile.TargetRego, []string{compile.TargetRego, compile.TargetWasm}), + count: 1, + profileCriteria: newrepeatedStringFlag([]string{}), + profileLimit: newIntFlag(defaultProfileLimit), + prettyLimit: newIntFlag(defaultPrettyLimit), + schema: &schemaFlags{}, + } +} + +func validateEvalParams(p *evalCommandParams, cmdArgs []string) error { + if len(cmdArgs) > 0 && p.stdin { + return errors.New("specify query argument or --stdin but not both") + } else if len(cmdArgs) == 0 && !p.stdin { + return errors.New("specify query argument or --stdin") + } else if len(cmdArgs) > 1 { + return errors.New("specify at most one query argument") + } + if p.stdin && p.stdinInput { + return errors.New("specify --stdin or --stdin-input but not both") + } + if p.stdinInput && p.inputPath != "" { + return errors.New("specify --stdin-input or --input but not both") + } + if p.fail && p.failDefined { + return errors.New("specify --fail or --fail-defined but not both") + } + of := p.outputFormat.String() + if p.partial && of != formats.Pretty && of != formats.JSON && of != formats.Source { + return errors.New("invalid output format for partial evaluation") + } else if !p.partial && of == formats.Source { + return errors.New("invalid output format for evaluation") + } + + // check if illegal arguments is passed with unknowns flag + for _, unknwn := range p.unknowns { + if unknwn == "input" { + continue + } + term, err := ast.ParseTerm(unknwn) + if err != nil { + return err + } + + switch term.Value.(type) { + case ast.Ref: + return nil + default: + return errIllegalUnknownsArg + } + } + + if p.optimizationLevel > 0 { + if len(p.dataPaths.v) > 0 && p.bundlePaths.isFlagSet() { + return errors.New("specify either --data or --bundle flag with optimization level greater than 0") + } + } + + if p.profileLimit.isFlagSet() || p.profileCriteria.isFlagSet() { + p.profile = true + } + if p.profile { + p.metrics = true + } + if p.instrument { + p.metrics = true + } + return nil +} + +const ( + // number of profile results to return by default + defaultProfileLimit = 10 + defaultPrettyLimit = 80 +) + +type regoError struct { + wrapped error +} + +func (regoError) Error() string { + return "rego" +} + +func (r regoError) Unwrap() error { + return r.wrapped +} + +func initEval(root *cobra.Command, _ string) { + executable := root.Name() + + params := newEvalCommandParams() + + evalCommand := &cobra.Command{ + Use: "eval ", + Short: "Evaluate a Rego query", + Long: `Evaluate a Rego query and print the result.`, + Example: ` + +To evaluate a simple query: + + $ ` + executable + ` eval 'x := 1; y := 2; x < y' + +To evaluate a query against JSON data: + + $ ` + executable + ` eval --data data.json 'name := data.names[_]' + +To evaluate a query against JSON data supplied with a file:// URL: + + $ ` + executable + ` eval --data file:///path/to/file.json 'data' + + +File & Bundle Loading +--------------------- + +The --bundle flag will load data files and Rego files contained +in the bundle specified by the path. It can be either a +compressed tar archive bundle file or a directory tree. + + $ ` + executable + ` eval --bundle /some/path 'data' + +Where /some/path contains: + + foo/ + | + +-- bar/ + | | + | +-- data.json + | + +-- baz.rego + | + +-- manifest.yaml + +The JSON file 'foo/bar/data.json' would be loaded and rooted under +'data.foo.bar' and the 'foo/baz.rego' would be loaded and rooted under the +package path contained inside the file. Only data files named data.json or +data.yaml will be loaded. In the example above the manifest.yaml would be +ignored. + +See https://www.openpolicyagent.org/docs/latest/management-bundles/ for more details +on bundle directory structures. + +The --data flag can be used to recursively load ALL *.rego, *.json, and +*.yaml files under the specified directory. + +The -O flag controls the optimization level. By default, optimization is disabled (-O=0). +When optimization is enabled the 'eval' command generates a bundle from the files provided +with either the --bundle or --data flag. This bundle is semantically equivalent to the input +files however the structure of the files in the bundle may have been changed by rewriting, inlining, +pruning, etc. This resulting optimized bundle is used to evaluate the query. If optimization is +enabled at least one entrypoint must be supplied, either via the -e option, or via entrypoint +metadata annotations. + +Output Formats +-------------- + +Set the output format with the --format flag. + + --format=json : output raw query results as JSON + --format=values : output line separated JSON arrays containing expression values + --format=bindings : output line separated JSON objects containing variable bindings + --format=pretty : output query results in a human-readable format + --format=source : output partial evaluation results in a source format + --format=raw : output the values from query results in a scripting friendly format + --format=discard : output the result field as "discarded" when non-nil + +Schema +------ + +The -s/--schema flag provides one or more JSON Schemas used to validate references to the input or data documents. +Loads a single JSON file, applying it to the input document; or all the schema files under the specified directory. + + $ ` + executable + ` eval --data policy.rego --input input.json --schema schema.json + $ ` + executable + ` eval --data policy.rego --input input.json --schema schemas/ + +Capabilities +------------ + +When passing a capabilities definition file via --capabilities, one can restrict which +hosts remote schema definitions can be retrieved from. For example, a capabilities.json +containing + + { + "builtins": [ ... ], + "allow_net": [ "kubernetesjsonschema.dev" ] + } + +would disallow fetching remote schemas from any host but "kubernetesjsonschema.dev". +Setting allow_net to an empty array would prohibit fetching any remote schemas. + +Not providing a capabilities file, or providing a file without an allow_net key, will +permit fetching remote schemas from any host. + +Note that the metaschemas http://json-schema.org/draft-04/schema, http://json-schema.org/draft-06/schema, +and http://json-schema.org/draft-07/schema, are always available, even without network +access. +`, + + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := validateEvalParams(¶ms, args); err != nil { + return err + } + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + defined, err := eval(args, params, os.Stdout) + if err != nil { + if _, ok := err.(regoError); !ok { + fmt.Fprintln(os.Stderr, err) + } + return newExitErrorWrap(2, err) + } + + if (params.fail && !defined) || (params.failDefined && defined) { + return newExitError(1) + } + return nil + }, + } + + // Eval specific flags + evalCommand.Flags().BoolVarP(¶ms.coverage, "coverage", "", false, "report coverage") + evalCommand.Flags().StringArrayVarP(¶ms.disableInlining, "disable-inlining", "", []string{}, "set paths of documents to exclude from inlining") + evalCommand.Flags().BoolVarP(¶ms.shallowInlining, "shallow-inlining", "", false, "disable inlining of rules that depend on unknowns") + evalCommand.Flags().BoolVarP(¶ms.nondeterministicBuiltions, "nondeterminstic-builtins", "", false, "evaluate nondeterministic builtins (if all arguments are known) during partial eval") + evalCommand.Flags().BoolVar(¶ms.disableIndexing, "disable-indexing", false, "disable indexing optimizations") + evalCommand.Flags().BoolVar(¶ms.disableEarlyExit, "disable-early-exit", false, "disable 'early exit' optimizations") + evalCommand.Flags().BoolVarP(¶ms.strictBuiltinErrors, "strict-builtin-errors", "", false, "treat the first built-in function error encountered as fatal") + evalCommand.Flags().BoolVarP(¶ms.showBuiltinErrors, "show-builtin-errors", "", false, "collect and return all encountered built-in errors, built in errors are not fatal") + evalCommand.Flags().BoolVarP(¶ms.instrument, "instrument", "", false, "enable query instrumentation metrics (implies --metrics)") + evalCommand.Flags().BoolVarP(¶ms.profile, "profile", "", false, "perform expression profiling") + evalCommand.Flags().VarP(¶ms.profileCriteria, "profile-sort", "", "set sort order of expression profiler results. Accepts: total_time_ns, num_eval, num_redo, num_gen_expr, file, line. This flag can be repeated.") + evalCommand.Flags().VarP(¶ms.profileLimit, "profile-limit", "", "set number of profiling results to show") + evalCommand.Flags().VarP(¶ms.prettyLimit, "pretty-limit", "", "set limit after which pretty output gets truncated") + evalCommand.Flags().BoolVarP(¶ms.failDefined, "fail-defined", "", false, "exits with non-zero exit code on defined/non-empty result and errors") + evalCommand.Flags().DurationVar(¶ms.timeout, "timeout", 0, "set eval timeout (default unlimited)") + evalCommand.Flags().IntVarP(¶ms.optimizationLevel, "optimize", "O", 0, "set optimization level") + evalCommand.Flags().VarP(¶ms.entrypoints, "entrypoint", "e", "set slash separated entrypoint path") + evalCommand.Flags().BoolVar(¶ms.traceVarValues, "var-values", false, "show local variable values in pretty trace output") + + // Shared flags + addCapabilitiesFlag(evalCommand.Flags(), params.capabilities) + addPartialFlag(evalCommand.Flags(), ¶ms.partial, false) + addUnknownsFlag(evalCommand.Flags(), ¶ms.unknowns, []string{"input"}) + addFailFlag(evalCommand.Flags(), ¶ms.fail, false) + addDataFlag(evalCommand.Flags(), ¶ms.dataPaths) + addBundleFlag(evalCommand.Flags(), ¶ms.bundlePaths) + addInputFlag(evalCommand.Flags(), ¶ms.inputPath) + addImportFlag(evalCommand.Flags(), ¶ms.imports) + addPackageFlag(evalCommand.Flags(), ¶ms.pkg) + addQueryStdinFlag(evalCommand.Flags(), ¶ms.stdin) + addInputStdinFlag(evalCommand.Flags(), ¶ms.stdinInput) + addMetricsFlag(evalCommand.Flags(), ¶ms.metrics, false) + addOutputFormat(evalCommand.Flags(), params.outputFormat) + addIgnoreFlag(evalCommand.Flags(), ¶ms.ignore) + setExplainFlag(evalCommand.Flags(), params.explain) + addSchemaFlags(evalCommand.Flags(), params.schema) + addTargetFlag(evalCommand.Flags(), params.target) + addCountFlag(evalCommand.Flags(), ¶ms.count, "benchmark") + addStrictFlag(evalCommand.Flags(), ¶ms.strict, false) + addV0CompatibleFlag(evalCommand.Flags(), ¶ms.v0Compatible, false) + addV1CompatibleFlag(evalCommand.Flags(), ¶ms.v1Compatible, false) + addReadAstValuesFromStoreFlag(evalCommand.Flags(), ¶ms.ReadAstValuesFromStore, false) + + root.AddCommand(evalCommand) +} + +func eval(args []string, params evalCommandParams, w io.Writer) (bool, error) { + ctx := context.Background() + if params.timeout != 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, params.timeout) + defer cancel() + } + + ectx, err := setupEval(args, params) + if err != nil { + return false, err + } + + ectx.regoArgs = append(ectx.regoArgs, + rego.EnablePrintStatements(true), + rego.PrintHook(topdown.NewPrintHook(os.Stderr))) + + results := make([]pr.Output, ectx.params.count) + profiles := make([][]profiler.ExprStats, ectx.params.count) + timers := make([]map[string]any, ectx.params.count) + + for i := range ectx.params.count { + results[i] = evalOnce(ctx, ectx) + profiles[i] = results[i].Profile + if ts, ok := results[i].Metrics.(metrics.TimerMetrics); ok { + timers[i] = ts.Timers() + } + } + + result := results[0] + + if ectx.params.count > 1 { + result.Profile = nil + result.Metrics = nil + result.AggregatedProfile = profiler.AggregateProfiles(profiles...) + timersAggregated := map[string]any{} + for name := range timers[0] { + var vals []int64 + for _, t := range timers { + val, ok := t[name].(int64) + if !ok { + return false, fmt.Errorf("missing timer for %s", name) + } + vals = append(vals, val) + } + timersAggregated[name] = metrics.Statistics(vals...) + } + result.AggregatedMetrics = timersAggregated + } + + var builtInErrorCount int + if ectx.params.showBuiltinErrors { + builtInErrorCount = len(*(ectx.builtInErrorList)) + } + + switch ectx.params.outputFormat.String() { + case formats.Bindings: + err = pr.Bindings(w, result) + case formats.Values: + err = pr.Values(w, result) + case formats.Pretty: + err = pr.PrettyWithOptions(w, result, pr.PrettyOptions{ + TraceOpts: topdown.PrettyTraceOptions{ + Locations: true, + ExprVariables: ectx.params.traceVarValues, + }, + }) + case formats.Source: + err = pr.Source(w, result) + case formats.Raw: + err = pr.Raw(w, result) + case formats.Discard: + err = pr.Discard(w, result) + default: + err = pr.JSON(w, result) + } + + if err != nil { + return false, err + } else if errorCount := len(result.Errors); errorCount > 0 && errorCount != builtInErrorCount { + // if we only have built-in errors, we don't want to return an error. If + // strict-builtin-errors is set the first built-in error will be returned + // in a result error instead. + + // If the rego package returned an error, return a special error here so + // that the command doesn't print the same error twice. The error will + // have been printed above by the presentation package. + return false, regoError{wrapped: result.Errors} + } else if len(result.Result) == 0 { + return false, nil + } + + return true, nil +} + +func evalOnce(ctx context.Context, ectx *evalContext) pr.Output { + var result pr.Output + var resultErr error + var parsedModules map[string]*ast.Module + + if ectx.metrics != nil { + ectx.metrics.Clear() + } + if ectx.profiler != nil { + ectx.profiler.reset() + } + r := rego.New(append(ectx.regoArgs, rego.CompilerHook(func(c *ast.Compiler) { + ctx = ast.WithCompiler(ctx, c) + }))...) + + if !ectx.params.partial { + var pq rego.PreparedEvalQuery + pq, resultErr = r.PrepareForEval(ctx) + if resultErr == nil { + parsedModules = pq.Modules() + result.Result, resultErr = pq.Eval(ctx, ectx.evalArgs...) + } + } else { + var pq rego.PreparedPartialQuery + pq, resultErr = r.PrepareForPartial(ctx) + if resultErr == nil { + parsedModules = pq.Modules() + result.Partial, resultErr = pq.Partial(ctx, ectx.evalArgs...) + resetExprLocations(result.Partial) + } + } + + result.Errors = pr.NewOutputErrors(resultErr) + if ectx.builtInErrorList != nil { + for _, err := range *(ectx.builtInErrorList) { + result.Errors = append(result.Errors, pr.NewOutputErrors(&err)...) + } + } + + if ectx.params.explain != nil { + switch ectx.params.explain.String() { + case explainModeDebug: + result.Explanation = lineage.Debug(*(ectx.tracer)) + case explainModeFull: + result.Explanation = lineage.Full(*(ectx.tracer)) + case explainModeNotes: + result.Explanation = lineage.Notes(*(ectx.tracer)) + case explainModeFails: + result.Explanation = lineage.Fails(*(ectx.tracer)) + } + } + + if ectx.metrics != nil { + result.Metrics = ectx.metrics + } + + if ectx.params.profile { + sortOrder := pr.DefaultProfileSortOrder + + if len(ectx.params.profileCriteria.v) != 0 { + sortOrder = getProfileSortOrder(strings.Split(ectx.params.profileCriteria.String(), ",")) + } + + result.Profile = ectx.profiler.p.ReportTopNResults(ectx.params.profileLimit.v, sortOrder) + } + + if ectx.params.coverage { + report := ectx.cover.Report(parsedModules) + result.Coverage = &report + } + + return result +} + +type evalContext struct { + params evalCommandParams + metrics metrics.Metrics + profiler *resettableProfiler + cover *cover.Cover + tracer *topdown.BufferTracer + regoArgs []func(*rego.Rego) + evalArgs []rego.EvalOption + builtInErrorList *[]topdown.Error +} + +func setupEval(args []string, params evalCommandParams) (*evalContext, error) { + var query string + + if params.stdin { + bs, err := io.ReadAll(os.Stdin) + if err != nil { + return nil, err + } + query = string(bs) + } else { + query = args[0] + } + + info, err := runtime.Term(runtime.Params{}) + if err != nil { + return nil, err + } + + regoArgs := []func(*rego.Rego){ + rego.Query(query), + rego.Runtime(info), + rego.SetRegoVersion(params.regoVersion()), + rego.StoreReadAST(params.ReadAstValuesFromStore), + rego.SkipBundleVerification(true), + } + + evalArgs := []rego.EvalOption{ + rego.EvalRuleIndexing(!params.disableIndexing), + rego.EvalEarlyExit(!params.disableEarlyExit), + rego.EvalNondeterministicBuiltins(params.nondeterministicBuiltions), + } + + if len(params.imports.v) > 0 { + regoArgs = append(regoArgs, rego.Imports(params.imports.v)) + } + + if params.pkg != "" { + regoArgs = append(regoArgs, rego.Package(params.pkg)) + } + + if len(params.dataPaths.v) > 0 { + if params.optimizationLevel <= 0 { + regoArgs = append(regoArgs, rego.Load(params.dataPaths.v, ignored(params.ignore).Apply)) + } else { + b, err := generateOptimizedBundle(params, false, ignored(params.ignore).Apply, params.dataPaths.v) + if err != nil { + return nil, err + } + + regoArgs = append(regoArgs, rego.ParsedBundle("optimized", b)) + } + } + + if params.bundlePaths.isFlagSet() { + if params.optimizationLevel <= 0 { + for _, bundleDir := range params.bundlePaths.v { + regoArgs = append(regoArgs, rego.LoadBundle(bundleDir)) + } + } else { + b, err := generateOptimizedBundle(params, true, buildCommandLoaderFilter(true, params.ignore), params.bundlePaths.v) + if err != nil { + return nil, err + } + + regoArgs = append(regoArgs, rego.ParsedBundle("optimized", b)) + } + } + + if params.target.IsSet() { + regoArgs = append(regoArgs, rego.Target(params.target.String())) + } + + inputBytes, err := readInputBytes(params) + if err != nil { + return nil, err + } + if inputBytes != nil { + var input any + if err := util.Unmarshal(inputBytes, &input); err != nil { + return nil, fmt.Errorf("unable to parse input: %s", err.Error()) + } + inputValue, err := ast.InterfaceToValue(input) + if err != nil { + return nil, fmt.Errorf("unable to process input: %s", err.Error()) + } + regoArgs = append(regoArgs, rego.ParsedInput(inputValue)) + } + + // -s {file} (one input schema file) + // -s {directory} (one schema directory with input and data schema files) + schemaSet, err := loader.Schemas(params.schema.path) + if err != nil { + return nil, err + } + regoArgs = append(regoArgs, rego.Schemas(schemaSet)) + + var tracer *topdown.BufferTracer + + if params.explain != nil && params.explain.String() != explainModeOff { + tracer = topdown.NewBufferTracer() + evalArgs = append(evalArgs, rego.EvalQueryTracer(tracer)) + + if params.target.String() == compile.TargetWasm { + fmt.Fprintf(os.Stderr, "warning: explain mode \"%v\" is not supported with wasm target\n", params.explain.String()) + } + } + + var m metrics.Metrics + if params.metrics { + m = metrics.New() + + // Use the same metrics for preparing and evaluating + regoArgs = append(regoArgs, rego.Metrics(m)) + evalArgs = append(evalArgs, rego.EvalMetrics(m)) + } + + if params.instrument { + regoArgs = append(regoArgs, rego.Instrument(true)) + evalArgs = append(evalArgs, rego.EvalInstrument(true)) + } + + rp := resettableProfiler{} + if params.profile { + rp.p = profiler.New() + evalArgs = append(evalArgs, rego.EvalQueryTracer(&rp)) + } + + if params.partial { + regoArgs = append(regoArgs, rego.Unknowns(params.unknowns)) + } + + regoArgs = append(regoArgs, rego.DisableInlining(params.disableInlining), rego.ShallowInlining(params.shallowInlining)) + + var c *cover.Cover + + if params.coverage { + c = cover.New() + evalArgs = append(evalArgs, rego.EvalQueryTracer(c)) + } + + if params.strictBuiltinErrors { + regoArgs = append(regoArgs, rego.StrictBuiltinErrors(true)) + if params.showBuiltinErrors { + return nil, errors.New("cannot use --show-builtin-errors with --strict-builtin-errors, --strict-builtin-errors will return the first built-in error encountered immediately") + } + } + + var builtInErrors []topdown.Error + if params.showBuiltinErrors { + regoArgs = append(regoArgs, rego.BuiltinErrorList(&builtInErrors)) + } + + if params.capabilities.C != nil { + regoArgs = append(regoArgs, rego.Capabilities(params.capabilities.C)) + } else { + regoArgs = append(regoArgs, rego.Capabilities(ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(params.regoVersion())))) + } + + if params.strict { + regoArgs = append(regoArgs, rego.Strict(params.strict)) + } + + evalCtx := &evalContext{ + params: params, + metrics: m, + profiler: &rp, + cover: c, + tracer: tracer, + regoArgs: regoArgs, + evalArgs: evalArgs, + builtInErrorList: &builtInErrors, + } + + return evalCtx, nil +} + +type resettableProfiler struct { + p *profiler.Profiler +} + +func (r *resettableProfiler) reset() { + r.p = profiler.New() +} + +func (*resettableProfiler) Enabled() bool { return true } +func (r *resettableProfiler) TraceEvent(ev topdown.Event) { r.p.TraceEvent(ev) } +func (r *resettableProfiler) Config() topdown.TraceConfig { return r.p.Config() } + +func getProfileSortOrder(sortOrder []string) []string { + // convert the sort order slice to a map for faster lookups + sortOrderMap := make(map[string]bool, len(sortOrder)) + for _, cr := range sortOrder { + sortOrderMap[cr] = true + } + + // compare the given sort order and the default + for _, cr := range pr.DefaultProfileSortOrder { + if _, ok := sortOrderMap[cr]; !ok { + sortOrder = append(sortOrder, cr) + } + } + return sortOrder +} + +func readInputBytes(params evalCommandParams) ([]byte, error) { + if params.stdinInput { + return io.ReadAll(os.Stdin) + } else if params.inputPath != "" { + path, err := fileurl.Clean(params.inputPath) + if err != nil { + return nil, err + } + return os.ReadFile(path) + } + return nil, nil +} + +type repeatedStringFlag struct { + v []string + isSet bool +} + +func newrepeatedStringFlag(val []string) repeatedStringFlag { + return repeatedStringFlag{ + v: val, + isSet: false, + } +} + +func (*repeatedStringFlag) Type() string { + return stringType +} + +func (f *repeatedStringFlag) String() string { + return strings.Join(f.v, ",") +} + +func (f *repeatedStringFlag) Set(s string) error { + f.v = append(f.v, s) + f.isSet = true + return nil +} + +func (f *repeatedStringFlag) isFlagSet() bool { + return f.isSet +} + +type intFlag struct { + v int + isSet bool +} + +func newIntFlag(val int) intFlag { + return intFlag{ + v: val, + isSet: false, + } +} + +func (*intFlag) Type() string { + return "int" +} + +func (f *intFlag) String() string { + return strconv.Itoa(f.v) +} + +func (f *intFlag) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 32) + f.v = int(v) + f.isSet = true + return err +} + +func (f *intFlag) isFlagSet() bool { + return f.isSet +} + +// resetExprLocations overwrites the row in the location info for every expression contained in pq. +// The location on every expression is shallow copied to avoid mutating shared state. Overwriting +// the rows ensures that the formatting package does not leave blank lines in between expressions (e.g., +// if expression 1 was saved on L10 and expression 2 was saved on L20 then the formatting package would +// squash the blank lines.) +func resetExprLocations(pq *rego.PartialQueries) { + if pq == nil { + return + } + + vis := &astLocationResetVisitor{} + + for i := range pq.Queries { + ast.NewGenericVisitor(vis.visit).Walk(pq.Queries[i]) + } + + for i := range pq.Support { + ast.NewGenericVisitor(vis.visit).Walk(pq.Support[i]) + } +} + +type astLocationResetVisitor struct { + n int +} + +func (vis *astLocationResetVisitor) visit(x any) bool { + if expr, ok := x.(*ast.Expr); ok { + if expr.Location != nil { + cpy := *expr.Location + cpy.Row = vis.n + expr.Location = &cpy + } else { + expr.Location = location.NewLocation(nil, "", vis.n, 1) + } + vis.n++ + } + return false +} + +func generateOptimizedBundle(params evalCommandParams, asBundle bool, filter loader.Filter, paths []string) (*bundle.Bundle, error) { + capabilities := params.capabilities.C + if capabilities == nil { + capabilities = ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(params.regoVersion())) + } + + compiler := compile.New(). + WithCapabilities(capabilities). + WithTarget(params.target.String()). + WithAsBundle(asBundle). + WithBundleLazyLoadingMode(bundle.HasExtension()). + WithOptimizationLevel(params.optimizationLevel). + WithOutput(bytes.NewBuffer(nil)). + WithEntrypoints(params.entrypoints.v...). + WithRegoAnnotationEntrypoints(true). + WithPaths(paths...). + WithFilter(filter). + WithRegoVersion(params.regoVersion()) + + if err := compiler.Build(context.Background()); err != nil { + return nil, err + } + + return compiler.Bundle(), nil +} diff --git a/third_party/opa/cmd/eval_test.go b/third_party/opa/cmd/eval_test.go new file mode 100755 index 000000000000..5f428515ec58 --- /dev/null +++ b/third_party/opa/cmd/eval_test.go @@ -0,0 +1,3625 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: goconst // string duplication is for test readability. +package cmd + +import ( + "bufio" + "bytes" + "context" + "errors" + "fmt" + "maps" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/internal/presentation" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestEvalWithIllegalUnknownArgs(t *testing.T) { + + tests := []struct { + name string + unknowns string + expectedErr error + }{ + { + name: "happy path: passing input ref as unknown", + unknowns: "input", + expectedErr: nil, + }, + { + name: "happy path: passing input.users ref as unknown", + unknowns: "input.users", + expectedErr: nil, + }, + { + name: "passing multiple refs with ; separated", + unknowns: "input;input.users", + expectedErr: errors.New("expected exactly one term but got: input; input.users"), + }, + { + name: "passing array as unknown", + unknowns: "[input, data.posts]", + expectedErr: errIllegalUnknownsArg, + }, + { + name: "passing set as unknown", + unknowns: "{input, data.posts}", + expectedErr: errIllegalUnknownsArg, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + params := newEvalCommandParams() + params.unknowns = []string{tt.unknowns} + params.partial = true + + err := validateEvalParams(¶ms, []string{"data"}) + + if tt.expectedErr != nil && !strings.EqualFold(err.Error(), tt.expectedErr.Error()) { + t.Errorf("expected %s; got %s", errIllegalUnknownsArg.Error(), err.Error()) + } + }) + } +} + +func TestEvalExitCode(t *testing.T) { + params := newEvalCommandParams() + params.fail = true + + tests := []struct { + note string + query string + wantDefined bool + wantErr bool + }{ + {"defined result", "true=true", true, false}, + {"undefined result", "true = false", false, false}, + {"on error", `{k: v | k = ["a", "a"][_]; v = [0,1][_]}`, false, true}, + } + + var b bytes.Buffer + writer := bufio.NewWriter(&b) + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + defined, err := eval([]string{tc.query}, params, writer) + if tc.wantErr && err == nil { + t.Fatal("wanted error but got success") + } else if !tc.wantErr && err != nil { + t.Fatal("wanted success but got error:", err) + } else if (tc.wantDefined && !defined) || (!tc.wantDefined && defined) { + t.Fatalf("wanted defined %v but got defined %v", tc.wantDefined, defined) + } + }) + } +} + +func TestEvalWithShowBuiltinErrors(t *testing.T) { + files := map[string]string{ + "x.rego": `package x + +p if { + 1/0 +} + +q if { + 1/0 +}`, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.showBuiltinErrors = true + params.dataPaths = newrepeatedStringFlag([]string{path}) + + var buf bytes.Buffer + + defined, err := eval([]string{"data.x"}, params, &buf) + if !defined || err != nil { + t.Fatalf("unexpected undefined or error: %v", err) + } + + var output presentation.Output + + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + + if len(output.Errors) != 2 { + t.Fatalf("Expected 2 errors in result, got:%v", len(output.Errors)) + } + + expectedCode := "eval_builtin_error" + expectedMessage := "div: divide by zero" + + if code := output.Errors[0].Code; code != expectedCode { + t.Fatalf("expected code '%v', got '%v'", expectedCode, code) + } + if msg := output.Errors[0].Message; msg != expectedMessage { + t.Fatalf("expected message '%v', got '%v'", expectedMessage, msg) + } + + if code := output.Errors[1].Code; code != expectedCode { + t.Fatalf("expected code '%v', got '%v'", expectedCode, code) + } + if msg := output.Errors[1].Message; msg != expectedMessage { + t.Fatalf("expected message '%v', got '%v'", expectedMessage, msg) + } + + loc1 := output.Errors[0].Location + if loc1 == nil { + t.Fatal("unexpected nil location") + } + + loc2 := output.Errors[1].Location + if loc2 == nil { + t.Fatal("unexpected nil location") + } + + if loc1.Row == loc2.Row { + t.Fatal("expected 2 distinct error occurrences in policy") + } + }) +} + +func TestEvalWithProfiler(t *testing.T) { + files := map[string]string{ + "x.rego": `package x + +p if { + a := 1 + b := 2 + c := 3 + x = a + b * c +}`, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.profile = true + params.profileCriteria = newrepeatedStringFlag([]string{"line"}) + params.dataPaths = newrepeatedStringFlag([]string{path}) + + var buf bytes.Buffer + + defined, err := eval([]string{"data"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + + var output presentation.Output + + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + + if len(output.Profile) == 0 { + t.Fatal("Expected profile output to be non-empty") + } + + expectedNumEval := []int{3, 1, 1, 1, 1} + expectedNumRedo := []int{3, 1, 1, 1, 1} + expectedRow := []int{7, 6, 5, 4, 1} + expectedNumGenExpr := []int{3, 1, 1, 1, 1} + + for idx, actualExprStat := range output.Profile { + if actualExprStat.NumEval != expectedNumEval[idx] { + t.Fatalf("Index %v: Expected number of evals %v but got %v", idx, expectedNumEval[idx], actualExprStat.NumEval) + } + + if actualExprStat.NumRedo != expectedNumRedo[idx] { + t.Fatalf("Index %v: Expected number of redos %v but got %v", idx, expectedNumRedo[idx], actualExprStat.NumRedo) + } + + if actualExprStat.Location.Row != expectedRow[idx] { + t.Fatalf("Index %v: Expected row %v but got %v", idx, expectedRow[idx], actualExprStat.Location.Row) + } + + if actualExprStat.NumGenExpr != expectedNumGenExpr[idx] { + t.Fatalf("Index %v: Expected number of generated expressions %v but got %v", idx, expectedNumGenExpr[idx], actualExprStat.NumGenExpr) + } + } + }) +} + +func TestEvalWithCoverage(t *testing.T) { + + files := map[string]string{ + "x.rego": `package x + +p = 1`, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.coverage = true + params.dataPaths = newrepeatedStringFlag([]string{path}) + + var buf bytes.Buffer + + defined, err := eval([]string{"data"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + + var output presentation.Output + + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + + if output.Coverage == nil || output.Coverage.Coverage != 100.0 { + t.Fatalf("Expected coverage in output but got: %v", buf.String()) + } + }) +} + +func TestEvalWithOptimizeErrors(t *testing.T) { + files := map[string]string{ + "x.rego": `package x + +p = 1`, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.optimizationLevel = 1 + params.dataPaths = newrepeatedStringFlag([]string{path}) + if err := params.bundlePaths.Set(path); err != nil { + t.Fatal(err) + } + + err := validateEvalParams(¶ms, []string{"data"}) + if err == nil { + t.Fatal("Expected error but got nil") + } + + expected := "specify either --data or --bundle flag with optimization level greater than 0" + if err.Error() != expected { + t.Fatalf("Expected error %v but got %v", expected, err.Error()) + } + + params = newEvalCommandParams() + params.optimizationLevel = 1 + params.dataPaths = newrepeatedStringFlag([]string{path}) + + var buf bytes.Buffer + + _, err = eval([]string{"data.test"}, params, &buf) + if err == nil { + t.Fatal("Expected error but got nil") + } + + expected = "bundle optimizations require at least one entrypoint" + if err.Error() != expected { + t.Fatalf("Expected error %v but got %v", expected, err.Error()) + } + }) +} + +func TestEvalWithOptimize(t *testing.T) { + files := map[string]string{ + "test.rego": ` + package test + + default p = false + p if { q } + q if { input.x = data.foo }`, + "data.json": ` + {"foo": 1}`, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.optimizationLevel = 1 + params.dataPaths = newrepeatedStringFlag([]string{path}) + params.entrypoints = newrepeatedStringFlag([]string{"test/p"}) + + var buf bytes.Buffer + + defined, err := eval([]string{"data.test.p"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + }) +} + +// Ensure that entrypoint annotations don't cause panics when using +// higher levels of optimization. +// Reference: https://github.com/open-policy-agent/opa/issues/5368 +func TestEvalIssue5368(t *testing.T) { + files := map[string]string{ + "test.rego": ` +package system + +object_key_exists(object, key) if { + _ = object[key] +} + +default main = false + +# METADATA +# entrypoint: true +main := results if { + object_key_exists(input, "queries") + results := {key: result | + result := input.queries[key] + } +}`, + "input.json": `{}`, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.optimizationLevel = 2 + params.dataPaths = newrepeatedStringFlag([]string{path}) + params.inputPath = filepath.Join(path, "input.json") + + var buf bytes.Buffer + + defined, err := eval([]string{"data.system.main"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + }) +} + +func TestEvalWithOptimizeBundleData(t *testing.T) { + files := map[string]string{ + "test.rego": ` + package test + + default p = false + p if { q } + q if { input.x = data.foo }`, + "data.json": ` + {"foo": 1}`, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.optimizationLevel = 1 + if err := params.bundlePaths.Set(path); err != nil { + t.Fatal(err) + } + params.entrypoints = newrepeatedStringFlag([]string{"test/p"}) + + var buf bytes.Buffer + + defined, err := eval([]string{"data.test.p"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + }) +} + +func testEvalWithInputFile(t *testing.T, input string, query string, params evalCommandParams) error { + files := map[string]string{ + "input.json": input, + } + + var err error + test.WithTempFS(files, func(path string) { + + params.inputPath = filepath.Join(path, "input.json") + + var buf bytes.Buffer + var defined bool + defined, err = eval([]string{query}, params, &buf) + if !defined || err != nil { + err = fmt.Errorf("Unexpected error or undefined from evaluation: %v", err) + return + } + + var output presentation.Output + + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + + rs := output.Result + if exp, act := true, rs.Allowed(); exp != act { + t.Errorf("expected %v, got %v", exp, act) + } + }) + + return err +} + +func TestEvalWithInvalidInputFile(t *testing.T) { + input := `{badjson` + query := "input.b[0].a == 1" + err := testEvalWithInputFile(t, input, query, newEvalCommandParams()) + if err == nil { + t.Fatalf("expected error but err == nil") + } +} + +func testEvalWithSchemaFile(t *testing.T, input string, query string, schema string, policy string, expTypeErr bool) error { + files := map[string]string{ + "input.json": input, + "schema.json": schema, + } + + policyFilePresent := policy != "" + if policyFilePresent { + files["policy.rego"] = policy + } + + var err error + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.inputPath = filepath.Join(path, "input.json") + if policyFilePresent { + params.dataPaths = newrepeatedStringFlag([]string{path}) + } + params.schema = &schemaFlags{path: filepath.Join(path, "schema.json")} + + var buf bytes.Buffer + defined, evalErr := eval([]string{query}, params, &buf) + if !expTypeErr && (!defined || evalErr != nil) { + err = fmt.Errorf("unexpected error or undefined from evaluation: %v", evalErr) + return + } + + var output presentation.Output + + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + + if expTypeErr { + if len(output.Errors) != 1 || output.Errors[0].Code != "rego_type_error" { + err = fmt.Errorf("expected type conflict, got %v", output.Errors) + } + return + } + + rs := output.Result + if exp, act := true, rs.Allowed(); exp != act { + t.Errorf("expected %v, got %v", exp, act) + } + }) + + return err +} + +func testEvalWithInvalidSchemaFile(input string, query string, schema string) error { + files := map[string]string{ + "input.json": input, + "schema.json": schema, + } + + var err error + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.inputPath = filepath.Join(path, "input.json") + params.schema = &schemaFlags{path: filepath.Join(path, "schemaBad.json")} + + var buf bytes.Buffer + var defined bool + defined, err = eval([]string{query}, params, &buf) + if !defined || err != nil { + err = fmt.Errorf("Unexpected error or undefined from evaluation: %v", err) + return + } + }) + + return err +} + +func testEvalWithSchemasAnnotationButNoSchemaFlag(policy string) error { + query := "data.test.p" + + files := map[string]string{ + "input.json": `{ + "foo": 42 + }`, + "test.rego": policy, + } + + var err error + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.inputPath = filepath.Join(path, "input.json") + params.dataPaths = newrepeatedStringFlag([]string{path}) + + var buf bytes.Buffer + var defined bool + defined, err = eval([]string{query}, params, &buf) + if !defined || err != nil { + err = errors.New(buf.String()) + } + }) + + return err +} + +// Assert that 'schemas' annotations with schema refs are only informing the type checker when the --schema flag is used +func TestEvalWithSchemasAnnotationButNoSchemaFlag(t *testing.T) { + policyWithSchemaRef := ` +package test + +# METADATA +# schemas: +# - input: schema["input"] +p if { + rego.metadata.rule() # presence of rego.metadata.* calls must not trigger unwanted schema evaluation + input.foo == 42 # type mismatch with schema that should be ignored +}` + + err := testEvalWithSchemasAnnotationButNoSchemaFlag(policyWithSchemaRef) + if err != nil { + t.Fatalf("unexpected error from eval with schema ref: %v", err) + } + + policyWithInlinedSchema := ` +package test + +# METADATA +# schemas: +# - input.foo: {"type": "boolean"} +p if { + rego.metadata.rule() # presence of rego.metadata.* calls must not trigger unwanted schema evaluation + input.foo == 42 # type mismatch with schema that should NOT be ignored since it is an inlined schema format +}` + + err = testEvalWithSchemasAnnotationButNoSchemaFlag(policyWithInlinedSchema) + // We expect an error here, as inlined schemas are always used for type checking + if !strings.Contains(err.Error(), `"code": "rego_type_error"`) { + t.Fatalf("unexpected error from eval with inlined schema, got: %v", err) + } +} + +func testReadParamWithSchemaDir(input string, inputSchema string) error { + files := map[string]string{ + "input.json": input, + "schemas/input.json": inputSchema, + "schemas/kubernetes/data-schema.json": inputSchema, + } + + var err error + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.inputPath = filepath.Join(path, "input.json") + params.schema = &schemaFlags{path: filepath.Join(path, "schemas")} + + // Don't assign over "err" or "err =" does nothing. + schemaSet, errSchema := loader.Schemas(params.schema.path) + if errSchema != nil { + err = fmt.Errorf("Unexpected error or undefined from evaluation: %v", errSchema) + return + } + + if schemaSet == nil { + err = errors.New("Schema set is empty") + return + } + + if schemaSet.Get(ast.MustParseRef("schema.input")) == nil { + err = errors.New("Expected schema for input in schemaSet but got none") + return + } + + if schemaSet.Get(ast.MustParseRef(`schema.kubernetes["data-schema"]`)) == nil { + err = errors.New("Expected schemas for data in schemaSet but got none") + return + } + + }) + + return err +} + +func TestEvalWithJSONSchema(t *testing.T) { + + input := `{ + "foo": "a", + "b": [ + { + "a": 1, + "b": [1, 2, 3], + "c": null + } + ] +}` + + schema := `{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/example.json", + "type": "object", + "title": "The root schema", + "description": "The root schema comprises the entire JSON document.", + "required": [ + "foo", + "b" + ], + "properties": { + "foo": { + "$id": "#/properties/foo", + "type": "string", + "title": "The foo schema", + "description": "An explanation about the purpose of this instance." + }, + "b": { + "$id": "#/properties/b", + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "additionalItems": false, + "items": { + "$id": "#/properties/b/items", + "type": "object", + "title": "The items schema", + "description": "An explanation about the purpose of this instance.", + "required": [ + "a", + "b", + "c" + ], + "properties": { + "a": { + "$id": "#/properties/b/items/properties/a", + "type": "integer", + "title": "The a schema", + "description": "An explanation about the purpose of this instance." + }, + "b": { + "$id": "#/properties/b/items/properties/b", + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "additionalItems": false, + "items": { + "$id": "#/properties/b/items/properties/b/items", + "type": "integer", + "title": "The items schema", + "description": "An explanation about the purpose of this instance." + } + }, + "c": { + "$id": "#/properties/b/items/properties/c", + "type": "null", + "title": "The c schema", + "description": "An explanation about the purpose of this instance." + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + }` + + query := "input.b[0].a == 1" + err := testEvalWithSchemaFile(t, input, query, schema, "", false) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + policyWithSchemasAnnotation := ` +package test + +# METADATA +# schemas: +# - input: schema +p if { + input.foo == 42 # type mismatch +}` + err = testEvalWithSchemaFile(t, input, query, schema, policyWithSchemasAnnotation, true) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + policyWithInlinedSchemasAnnotation := ` +package test + +# METADATA +# schemas: +# - input.foo: {"type": "boolean"} +p if { + input.foo == 42 # type mismatch +}` + err = testEvalWithSchemaFile(t, input, query, schema, policyWithInlinedSchemasAnnotation, true) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = testReadParamWithSchemaDir(input, schema) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } +} + +func TestEvalWithInvalidSchemaFile(t *testing.T) { + + input := `{ + "foo": "a", + "b": [ + { + "a": 1, + "b": [1, 2, 3], + "c": null + } + ] + }` + + schema := `{badjson` + + query := "input.b[0].a == 1" + err := testEvalWithSchemaFile(t, input, query, schema, "", false) + if err == nil { + t.Fatalf("expected error but err == nil") + } + + err = testEvalWithInvalidSchemaFile(input, query, schema) + if err == nil { + t.Fatalf("expected error but err == nil") + } +} + +func TestEvalWithSchemaFileWithRemoteRef(t *testing.T) { + + input := `{"metadata": {"clusterName": "NAME"}}` + schemaFmt := `{ + "type": "object", + "properties": { + "metadata": { + "$ref": "%s/v1.14.0/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + } +}` + ts := kubeSchemaServer(t) + t.Cleanup(ts.Close) + + query := "data.p.r" + files := map[string]string{ + "input.json": input, + "schema.json": fmt.Sprintf(schemaFmt, ts.URL), + "p.rego": `package p + +r if { + input.metadata.clusterName == "NAME" +}`, + } + + t.Run("all remote refs disabled", func(t *testing.T) { + test.WithTempFS(files, func(path string) { + params := newEvalCommandParams() + params.inputPath = filepath.Join(path, "input.json") + params.schema = &schemaFlags{path: filepath.Join(path, "schema.json")} + params.capabilities.C = ast.CapabilitiesForThisVersion() + params.capabilities.C.AllowNet = []string{} + _ = params.dataPaths.Set(filepath.Join(path, "p.rego")) + + var buf bytes.Buffer + _, err := eval([]string{query}, params, &buf) + if err == nil { + t.Fatal("expected error, got nil") + } + var output presentation.Output + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + if exp, act := 1, len(output.Errors); exp != act { + t.Fatalf("expected %d errors, got %d", exp, act) + } + if exp, act := "rego_type_error", output.Errors[0].Code; exp != act { + t.Errorf("expected code %v, got %v", exp, act) + } + }) + }) + + t.Run("all remote refs enabled", func(t *testing.T) { + test.WithTempFS(files, func(path string) { + params := newEvalCommandParams() + params.inputPath = filepath.Join(path, "input.json") + params.schema = &schemaFlags{path: filepath.Join(path, "schema.json")} + _ = params.dataPaths.Set(filepath.Join(path, "p.rego")) + + var buf bytes.Buffer + defined, err := eval([]string{query}, params, &buf) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if exp, act := true, defined; exp != act { + t.Errorf("expected defined %v, got %v", exp, act) + } + }) + }) + + t.Run("required remote ref host not enabled", func(t *testing.T) { + test.WithTempFS(files, func(path string) { + params := newEvalCommandParams() + params.inputPath = filepath.Join(path, "input.json") + params.schema = &schemaFlags{path: filepath.Join(path, "schema.json")} + params.capabilities.C = ast.CapabilitiesForThisVersion() + params.capabilities.C.AllowNet = []string{"something.else"} + _ = params.dataPaths.Set(filepath.Join(path, "p.rego")) + + var buf bytes.Buffer + _, err := eval([]string{query}, params, &buf) + if err == nil { + t.Fatal("expected error, got nil") + } + var output presentation.Output + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + if exp, act := 1, len(output.Errors); exp != act { + t.Fatalf("expected %d errors, got %d", exp, act) + } + if exp, act := "rego_type_error", output.Errors[0].Code; exp != act { + t.Errorf("expected code %v, got %v", exp, act) + } + }) + }) + + t.Run("only required remote ref host enabled", func(t *testing.T) { + test.WithTempFS(files, func(path string) { + params := newEvalCommandParams() + params.inputPath = filepath.Join(path, "input.json") + params.schema = &schemaFlags{path: filepath.Join(path, "schema.json")} + params.capabilities.C = ast.CapabilitiesForThisVersion() + params.capabilities.C.AllowNet = []string{"127.0.0.1"} + _ = params.dataPaths.Set(filepath.Join(path, "p.rego")) + + var buf bytes.Buffer + defined, err := eval([]string{query}, params, &buf) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if exp, act := true, defined; exp != act { + t.Errorf("expected defined %v, got %v", exp, act) + } + }) + }) +} + +func TestBuiltinsCapabilities(t *testing.T) { + tests := []struct { + note string + policy string + query string + ruleName string + expectedCode string + expectedMessage string + }{ + { + note: "rego.metadata.chain() not allowed", + policy: "package p\n r := rego.metadata.chain()", + query: "data.p", + ruleName: "rego.metadata.chain", + expectedCode: "rego_type_error", + expectedMessage: "undefined function rego.metadata.chain", + }, + { + note: "rego.metadata.rule() not allowed", + policy: "package p\n r := rego.metadata.rule()", + query: "data.p", + ruleName: "rego.metadata.rule", + expectedCode: "rego_type_error", + expectedMessage: "undefined function rego.metadata.rule", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + + files := map[string]string{ + "p.rego": tc.policy, + } + + test.WithTempFS(files, func(path string) { + params := newEvalCommandParams() + params.capabilities.C = ast.CapabilitiesForThisVersion() + params.capabilities.C.Builtins = removeBuiltin(params.capabilities.C.Builtins, tc.ruleName) + + _ = params.dataPaths.Set(filepath.Join(path, "p.rego")) + + var buf bytes.Buffer + _, err := eval([]string{tc.query}, params, &buf) + if err == nil { + t.Fatal("expected error, got nil") + } + var output presentation.Output + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + if exp, act := 1, len(output.Errors); exp != act { + t.Fatalf("expected %d errors, got %d", exp, act) + } + if code := output.Errors[0].Code; code != tc.expectedCode { + t.Errorf("expected code '%v', got '%v'", tc.expectedCode, code) + } + if msg := output.Errors[0].Message; msg != tc.expectedMessage { + t.Errorf("expected message '%v', got '%v'", tc.expectedMessage, msg) + } + }) + }) + } +} + +func removeBuiltin(builtins []*ast.Builtin, name string) []*ast.Builtin { + var cpy []*ast.Builtin + for _, builtin := range builtins { + if builtin.Name != name { + cpy = append(cpy, builtin) + } + } + return cpy +} + +// Nearly identical to TestEvalWithOptimizeBundleData, but uses +// Rego entrypoint annotations instead of explicitly providing +// the entrypoints as CLI arguments. +func TestEvalWithRegoEntrypointAnnotations(t *testing.T) { + files := map[string]string{ + "test.rego": ` +package test + +default p = false +# METADATA +# entrypoint: true +p if { q } +q if { input.x = data.foo }`, + "data.json": ` +{"foo": 1}`, + } + + test.WithTempFS(files, func(path string) { + params := newEvalCommandParams() + if err := params.bundlePaths.Set(path); err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + + defined, err := eval([]string{"data.test.p"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + }) +} + +func TestEvalReturnsRegoError(t *testing.T) { + buf := new(bytes.Buffer) + _, err := eval([]string{`{k: v | k = ["a", "a"][_]; v = [0,1][_]}`}, newEvalCommandParams(), buf) + if _, ok := err.(regoError); !ok { + t.Fatal("expected regoError but got:", err) + } +} + +func TestEvalWithBundleData(t *testing.T) { + files := map[string]string{ + "x/x.rego": "package x\np = 1", + "x/data.json": `{"b": "bar"}`, + "other/not-data.json": `{"ignored": "data"}`, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + if err := params.bundlePaths.Set(path); err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + + defined, err := eval([]string{"data"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + + var output presentation.Output + + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + + assertResultSet(t, output.Result, `[[{"x": {"p": 1, "b": "bar"}}]]`) + }) +} + +func TestEvalWithBundleDuplicateFileNames(t *testing.T) { + files := map[string]string{ + // bundle a + "a/policy.rego": "package a\np = 1", + "a/.manifest": `{"roots":["a"]}`, + + // bundle b + "b/policy.rego": "package b\nq = 1", + "b/.manifest": `{"roots":["b"]}`, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + if err := params.bundlePaths.Set(filepath.Join(path, "a")); err != nil { + t.Fatal(err) + } + if err := params.bundlePaths.Set(filepath.Join(path, "b")); err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + + defined, err := eval([]string{"data"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + + var output presentation.Output + + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + + assertResultSet(t, output.Result, `[[{"a":{"p":1},"b":{"q":1}}]]`) + }) +} + +func TestEvalWithReadASTValuesFromStore(t *testing.T) { + // Note: This test is a bit of a hack. It's difficult to discern whether AST values were actually read from the store. + // This just ensures that we don't get any unexpected errors when enabling the flag. + + tests := []struct { + note string + readAst bool + }{ + { + note: "read raw data from store", + readAst: false, + }, + { + note: "read AST values from store", + readAst: true, + }, + } + + files := map[string]string{ + "test.rego": ` + package test + p = 1`, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(files, func(path string) { + params := newEvalCommandParams() + params.dataPaths = newrepeatedStringFlag([]string{path}) + params.ReadAstValuesFromStore = tc.readAst + + var buf bytes.Buffer + + defined, err := eval([]string{"data.test.p"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + }) + }) + } +} + +func TestEvalWithStrictBuiltinErrors(t *testing.T) { + params := newEvalCommandParams() + params.strictBuiltinErrors = true + + var buf bytes.Buffer + _, err := eval([]string{"1/0"}, params, &buf) + if err == nil { + t.Fatal("expected error") + } + + params.strictBuiltinErrors = false + buf.Reset() + + _, err = eval([]string{"1/0"}, params, &buf) + if err != nil { + t.Fatal("unexpected error:", err) + } + + if buf.String() != "{}\n" { + t.Fatal("expected undefined output but got:", buf.String()) + } +} + +func assertResultSet(t *testing.T, rs rego.ResultSet, expected string) { + t.Helper() + result := []any{} + + for i := range rs { + values := []any{} + for j := range rs[i].Expressions { + values = append(values, rs[i].Expressions[j].Value) + } + result = append(result, values) + } + + parsedExpected := util.MustUnmarshalJSON([]byte(expected)) + if !reflect.DeepEqual(result, parsedExpected) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", parsedExpected, result) + } +} + +func TestEvalErrorJSONOutput(t *testing.T) { + params := newEvalCommandParams() + err := params.outputFormat.Set(formats.JSON) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + var buf bytes.Buffer + + defined, err := eval([]string{"{1,2,3} == {1,x,3}"}, params, &buf) + if defined && err == nil { + t.Fatalf("Expected an error") + } + + // Only check that it *can* be loaded as valid JSON, and that the errors + // are populated. + var output map[string]any + + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + + if output["errors"] == nil { + t.Fatalf("Expected error to be non-nil") + } +} + +func TestEvalDebugTraceJSONOutput(t *testing.T) { + params := newEvalCommandParams() + err := params.outputFormat.Set(formats.JSON) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + err = params.explain.Set(explainModeFull) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + params.disableIndexing = true + + mod := `package x + + p contains a if { + a := input.z + a == 1 + } + + p contains b if { + b := input.y + b == 1 + } + ` + + input := `{"z": 1}` + + files := map[string]string{ + "policy.rego": mod, + "input.json": input, + } + + var buf bytes.Buffer + var policyFile string + + test.WithTempFS(files, func(path string) { + params.inputPath = filepath.Join(path, "input.json") + policyFile = filepath.Join(path, "policy.rego") + err := params.dataPaths.Set(policyFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + _, err = eval([]string{"data.x.p"}, params, &buf) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + }) + + var output struct { + Explanation []struct { + Op string `json:"Op"` + Node any `json:"Node"` + Location *ast.Location `json:"Location"` + Locals []map[string]any `json:"Locals"` + LocalMetadata map[string]struct { + Name string `json:"name"` + } `json:"LocalMetadata"` + } + } + + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + if len(output.Explanation) == 0 { + t.Fatalf("Expected explanations to be non-nil") + } + + type locationAndVars struct { + location *ast.Location + varBindings map[string]string + } + + var evals []locationAndVars + for _, e := range output.Explanation { + if e.Op == string(topdown.EvalOp) { + bindings := map[string]string{} + for k, v := range e.LocalMetadata { + bindings[k] = v.Name + } + + evals = append(evals, locationAndVars{location: e.Location, varBindings: bindings}) + } + } + + expectedEvalLocationsAndVars := []locationAndVars{ + { + location: ast.NewLocation(nil, policyFile, 4, 3), // a := input.z + varBindings: map[string]string{"__local0__": "a"}, + }, + { + location: ast.NewLocation(nil, policyFile, 5, 3), // a == 1 + varBindings: map[string]string{"__local0__": "a"}, + }, + { + location: ast.NewLocation(nil, policyFile, 9, 3), // b := input.y + varBindings: map[string]string{"__local1__": "b"}, + }, + } + + for _, expected := range expectedEvalLocationsAndVars { + found := false + for _, actual := range evals { + if expected.location.Compare(actual.location) == 0 { + found = true + if !maps.Equal(expected.varBindings, actual.varBindings) { + t.Errorf("Expected var bindings:\n\n\t%+v\n\nGot\n\n\t%+v\n\n", expected.varBindings, actual.varBindings) + } + } + } + if !found { + t.Fatalf("Missing expected eval node in trace: %+v\nGot: %+v\n", expected, evals) + } + } +} + +func TestEvalPrettyTrace(t *testing.T) { + tests := []struct { + note string + query string + includeVars bool + files map[string]string + expected string + }{ + { + note: "simple without vars", + query: "data.test.p", + includeVars: false, + files: map[string]string{ + "test.rego": `package test +import rego.v1 + +p if { + x := 1 + y := 2 + z := 3 + x == z - y +} +`, + }, + expected: `%SKIP_LINE% +query:1 %.*% Enter data.test.p = _ +query:1 %.*% | Eval data.test.p = _ +query:1 %.*% | Index data.test.p (matched 1 rule, early exit) +%.*%/test.rego:4 | Enter data.test.p +%.*%/test.rego:5 | | Eval x = 1 +%.*%/test.rego:6 | | Eval y = 2 +%.*%/test.rego:7 | | Eval z = 3 +%.*%/test.rego:8 | | Eval minus(z, y, __local3__) +%.*%/test.rego:8 | | Eval x = __local3__ +%.*%/test.rego:4 | | Exit data.test.p early +query:1 %.*% | Exit data.test.p = _ +query:1 %.*% Redo data.test.p = _ +query:1 %.*% | Redo data.test.p = _ +%.*%/test.rego:4 | Redo data.test.p +%.*%/test.rego:8 | | Redo x = __local3__ +%.*%/test.rego:8 | | Redo minus(z, y, __local3__) +%.*%/test.rego:7 | | Redo z = 3 +%.*%/test.rego:6 | | Redo y = 2 +%.*%/test.rego:5 | | Redo x = 1 +true +`, + }, + { + note: "simple with vars", + query: "data.test.p", + includeVars: true, + files: map[string]string{ + "test.rego": `package test +import rego.v1 + +p if { + x := 1 + y := 2 + z := 3 + x == z - y +} +`, + }, + expected: `%SKIP_LINE% +query:1 %.*% Enter data.test.p = _ {} +query:1 %.*% | Eval data.test.p = _ {} +query:1 %.*% | Index data.test.p (matched 1 rule, early exit) {} +%.*%/test.rego:4 | Enter data.test.p {} +%.*%/test.rego:5 | | Eval x = 1 {} +%.*%/test.rego:6 | | Eval y = 2 {} +%.*%/test.rego:7 | | Eval z = 3 {} +%.*%/test.rego:8 | | Eval minus(z, y, __local3__) {y: 2, z: 3} +%.*%/test.rego:8 | | Eval x = __local3__ {__local3__: 1, x: 1} +%.*%/test.rego:4 | | Exit data.test.p early {} +query:1 %.*% | Exit data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% Redo data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% | Redo data.test.p = _ {_: true, data.test.p: true} +%.*%/test.rego:4 | Redo data.test.p {} +%.*%/test.rego:8 | | Redo x = __local3__ {__local3__: 1, x: 1} +%.*%/test.rego:8 | | Redo minus(z, y, __local3__) {__local3__: 1, y: 2, z: 3} +%.*%/test.rego:7 | | Redo z = 3 {z: 3} +%.*%/test.rego:6 | | Redo y = 2 {y: 2} +%.*%/test.rego:5 | | Redo x = 1 {x: 1} +true +`, + }, + { + note: "large var", + query: "data.test.p", + includeVars: true, + files: map[string]string{ + "test.rego": `package test +import rego.v1 + +v := { + "foo": ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"], + "bar": ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"], + "baz": ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"], + "qux": ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"], + } + +p if { + x := v + + x.foo[_] == "a" +} +`, + }, + expected: `%SKIP_LINE% +query:1 %.*% Enter data.test.p = _ {} +query:1 %.*% | Eval data.test.p = _ {} +query:1 %.*% | Index data.test.p (matched 1 rule, early exit) {} +%.*%/test.rego:11 | Enter data.test.p {} +%.*%/test.rego:12 | | Eval x = data.test.v {} +%.*%/test.rego:12 | | Index data.test.v (matched 1 rule, early exit) {} +%.*%/test.rego:4 | | Enter data.test.v {} +%.*%/test.rego:4 | | | Eval true {} +%.*%/test.rego:4 | | | Exit data.test.v early {} +%.*%/test.rego:14 | | Eval x.foo[_] = "a" {x: {"bar": ["a", "b", "c", "d", ...} +%.*%/test.rego:11 | | Exit data.test.p early {} +query:1 %.*% | Exit data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% Redo data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% | Redo data.test.p = _ {_: true, data.test.p: true} +%.*%/test.rego:11 | Redo data.test.p {} +%.*%/test.rego:14 | | Redo x.foo[_] = "a" {_: 0, x: {"bar": ["a", "b", "c", "d", ...} +%.*%/test.rego:12 | | Redo x = data.test.v {data.test.v: {"bar": ["a", "b", "c", "d", ..., x: {"bar": ["a", "b", "c", "d", ...} +%.*%/test.rego:4 | | | Redo true {} +true +`, + }, + { + note: "func call", + query: "data.test.p", + includeVars: true, + files: map[string]string{ + "test.rego": `package test +import rego.v1 + +p if { + x := 1 + y := 2 + z := 3 + z == f(x, y) +} + +f(a, b) := c if { + c := a + b +} +`, + }, + expected: `%SKIP_LINE% +query:1 %.*% Enter data.test.p = _ {} +query:1 %.*% | Eval data.test.p = _ {} +query:1 %.*% | Index data.test.p (matched 1 rule, early exit) {} +%.*%/test.rego:4 | Enter data.test.p {} +%.*%/test.rego:5 | | Eval x = 1 {} +%.*%/test.rego:6 | | Eval y = 2 {} +%.*%/test.rego:7 | | Eval z = 3 {} +%.*%/test.rego:8 | | Eval data.test.f(x, y, __local6__) {x: 1, y: 2} +%.*%/test.rego:8 | | Index data.test.f (matched 1 rule) {x: 1, y: 2} +%.*%/test.rego:11 | | Enter data.test.f {} +%.*%/test.rego:12 | | | Eval plus(a, b, __local7__) {a: 1, b: 2} +%.*%/test.rego:12 | | | Eval c = __local7__ {__local7__: 3} +%.*%/test.rego:11 | | | Exit data.test.f {a: 1, b: 2, c: 3} +%.*%/test.rego:8 | | Eval z = __local6__ {__local6__: 3, z: 3} +%.*%/test.rego:4 | | Exit data.test.p early {} +query:1 %.*% | Exit data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% Redo data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% | Redo data.test.p = _ {_: true, data.test.p: true} +%.*%/test.rego:4 | Redo data.test.p {} +%.*%/test.rego:8 | | Redo z = __local6__ {__local6__: 3, z: 3} +%.*%/test.rego:8 | | Redo data.test.f(x, y, __local6__) {__local6__: 3, x: 1, y: 2} +%.*%/test.rego:12 | | | Redo c = __local7__ {__local7__: 3, c: 3} +%.*%/test.rego:12 | | | Redo plus(a, b, __local7__) {__local7__: 3, a: 1, b: 2} +%.*%/test.rego:7 | | Redo z = 3 {z: 3} +%.*%/test.rego:6 | | Redo y = 2 {y: 2} +%.*%/test.rego:5 | | Redo x = 1 {x: 1} +true +`, + }, + { + note: "every", + query: "data.test.p", + includeVars: true, + files: map[string]string{ + "test.rego": `package test +import rego.v1 + +p if { + l := ["a", "b", "c"] + every x in l { + count(x) == 1 + } +} + +f(a, b) := c if { + c := a + b +} +`, + }, + expected: `%SKIP_LINE% +query:1 %.*% Enter data.test.p = _ {} +query:1 %.*% | Eval data.test.p = _ {} +query:1 %.*% | Index data.test.p (matched 1 rule, early exit) {} +%.*%/test.rego:4 | Enter data.test.p {} +%.*%/test.rego:5 | | Eval l = ["a", "b", "c"] {} +%.*%/test.rego:6 | | Eval __local6__ = l {l: ["a", "b", "c"]} +%.*%/test.rego:6 | | Eval every x in __local6__ { count(x, __local7__); __local7__ = 1 } {__local6__: ["a", "b", "c"]} +%.*%/test.rego:6 | | Enter every x in __local6__ { count(x, __local7__); __local7__ = 1 } {__local6__: ["a", "b", "c"]} +%.*%/test.rego:6 | | | Eval __local6__[__local1__] = x {__local6__: ["a", "b", "c"]} +%.*%/test.rego:7 | | | Enter count(x, __local7__); __local7__ = 1 {x: "a"} +%.*%/test.rego:7 | | | | Eval count(x, __local7__) {x: "a"} +%.*%/test.rego:7 | | | | Eval __local7__ = 1 {__local7__: 1} +%.*%/test.rego:7 | | | | Exit count(x, __local7__); __local7__ = 1 early {__local7__: 1, x: "a"} +%.*%/test.rego:7 | | | Redo count(x, __local7__); __local7__ = 1 {__local7__: 1, x: "a"} +%.*%/test.rego:7 | | | | Redo __local7__ = 1 {__local7__: 1} +%.*%/test.rego:7 | | | | Redo count(x, __local7__) {__local7__: 1, x: "a"} +%.*%/test.rego:6 | | | Redo every x in __local6__ { count(x, __local7__); __local7__ = 1 } {__local1__: 0, __local6__: ["a", "b", "c"], x: "a"} +%.*%/test.rego:6 | | | Redo __local6__[__local1__] = x {__local1__: 0, __local6__: ["a", "b", "c"], x: "a"} +%.*%/test.rego:7 | | | Enter count(x, __local7__); __local7__ = 1 {x: "b"} +%.*%/test.rego:7 | | | | Eval count(x, __local7__) {x: "b"} +%.*%/test.rego:7 | | | | Eval __local7__ = 1 {__local7__: 1} +%.*%/test.rego:7 | | | | Exit count(x, __local7__); __local7__ = 1 early {__local7__: 1, x: "b"} +%.*%/test.rego:7 | | | Redo count(x, __local7__); __local7__ = 1 {__local7__: 1, x: "b"} +%.*%/test.rego:7 | | | | Redo __local7__ = 1 {__local7__: 1} +%.*%/test.rego:7 | | | | Redo count(x, __local7__) {__local7__: 1, x: "b"} +%.*%/test.rego:6 | | | Redo every x in __local6__ { count(x, __local7__); __local7__ = 1 } {__local1__: 1, __local6__: ["a", "b", "c"], x: "b"} +%.*%/test.rego:6 | | | Redo __local6__[__local1__] = x {__local1__: 1, __local6__: ["a", "b", "c"], x: "b"} +%.*%/test.rego:7 | | | Enter count(x, __local7__); __local7__ = 1 {x: "c"} +%.*%/test.rego:7 | | | | Eval count(x, __local7__) {x: "c"} +%.*%/test.rego:7 | | | | Eval __local7__ = 1 {__local7__: 1} +%.*%/test.rego:7 | | | | Exit count(x, __local7__); __local7__ = 1 early {__local7__: 1, x: "c"} +%.*%/test.rego:7 | | | Redo count(x, __local7__); __local7__ = 1 {__local7__: 1, x: "c"} +%.*%/test.rego:7 | | | | Redo __local7__ = 1 {__local7__: 1} +%.*%/test.rego:7 | | | | Redo count(x, __local7__) {__local7__: 1, x: "c"} +%.*%/test.rego:6 | | | Redo every x in __local6__ { count(x, __local7__); __local7__ = 1 } {__local1__: 2, __local6__: ["a", "b", "c"], x: "c"} +%.*%/test.rego:6 | | | Redo __local6__[__local1__] = x {__local1__: 2, __local6__: ["a", "b", "c"], x: "c"} +%.*%/test.rego:4 | | Exit data.test.p early {} +query:1 %.*% | Exit data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% Redo data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% | Redo data.test.p = _ {_: true, data.test.p: true} +%.*%/test.rego:4 | Redo data.test.p {} +%.*%/test.rego:6 | | Redo every x in __local6__ { count(x, __local7__); __local7__ = 1 } {__local6__: ["a", "b", "c"]} +%.*%/test.rego:6 | | | Exit every x in __local6__ { count(x, __local7__); __local7__ = 1 } {__local6__: ["a", "b", "c"]} +%.*%/test.rego:6 | | Redo __local6__ = l {__local6__: ["a", "b", "c"], l: ["a", "b", "c"]} +%.*%/test.rego:5 | | Redo l = ["a", "b", "c"] {l: ["a", "b", "c"]} +true +`, + }, + { + note: "rule value", + query: "data.test.p", + includeVars: true, + files: map[string]string{ + "test.rego": `package test +import rego.v1 + +a := 1 + +p if { + a + 1 == 2 + a + 2 == 3 +} +`, + }, + expected: `%SKIP_LINE% +query:1 %.*% Enter data.test.p = _ {} +query:1 %.*% | Eval data.test.p = _ {} +query:1 %.*% | Index data.test.p (matched 1 rule, early exit) {} +%.*%/test.rego:6 | Enter data.test.p {} +%.*%/test.rego:7 | | Eval __local2__ = data.test.a {} +%.*%/test.rego:7 | | Index data.test.a (matched 1 rule, early exit) {} +%.*%/test.rego:4 | | Enter data.test.a {} +%.*%/test.rego:4 | | | Eval true {} +%.*%/test.rego:4 | | | Exit data.test.a early {} +%.*%/test.rego:7 | | Eval plus(__local2__, 1, __local0__) {__local2__: 1} +%.*%/test.rego:7 | | Eval __local0__ = 2 {__local0__: 2} +%.*%/test.rego:8 | | Eval __local3__ = data.test.a {data.test.a: 1} +%.*%/test.rego:8 | | Index data.test.a (matched 1 rule, early exit) {data.test.a: 1} +%.*%/test.rego:8 | | Eval plus(__local3__, 2, __local1__) {__local3__: 1} +%.*%/test.rego:8 | | Eval __local1__ = 3 {__local1__: 3} +%.*%/test.rego:6 | | Exit data.test.p early {} +query:1 %.*% | Exit data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% Redo data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% | Redo data.test.p = _ {_: true, data.test.p: true} +%.*%/test.rego:6 | Redo data.test.p {} +%.*%/test.rego:8 | | Redo __local1__ = 3 {__local1__: 3} +%.*%/test.rego:8 | | Redo plus(__local3__, 2, __local1__) {__local1__: 3, __local3__: 1} +%.*%/test.rego:8 | | Redo __local3__ = data.test.a {__local3__: 1, data.test.a: 1} +%.*%/test.rego:7 | | Redo __local0__ = 2 {__local0__: 2} +%.*%/test.rego:7 | | Redo plus(__local2__, 1, __local0__) {__local0__: 2, __local2__: 1} +%.*%/test.rego:7 | | Redo __local2__ = data.test.a {__local2__: 1, data.test.a: 1} +%.*%/test.rego:4 | | | Redo true {} +true +`, + }, + { + note: "input values", + query: "data.test.p", + includeVars: true, + files: map[string]string{ + "test.rego": `package test +import rego.v1 + +p if { + input.x == 1 + input.x + input.y == input.z +} +`, + "input.json": `{ + "x": 1, + "y": 2, + "z": 3 +}`, + }, + expected: `%SKIP_LINE% +query:1 %.*% Enter data.test.p = _ {} +query:1 %.*% | Eval data.test.p = _ {} +query:1 %.*% | Index data.test.p (matched 1 rule, early exit) {} +%.*%/test.rego:4 | Enter data.test.p {} +%.*%/test.rego:5 | | Eval input.x = 1 {} +%.*%/test.rego:6 | | Eval __local1__ = input.x {} +%.*%/test.rego:6 | | Eval __local2__ = input.y {} +%.*%/test.rego:6 | | Eval plus(__local1__, __local2__, __local0__) {__local1__: 1, __local2__: 2} +%.*%/test.rego:6 | | Eval __local0__ = input.z {__local0__: 3} +%.*%/test.rego:4 | | Exit data.test.p early {} +query:1 %.*% | Exit data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% Redo data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% | Redo data.test.p = _ {_: true, data.test.p: true} +%.*%/test.rego:4 | Redo data.test.p {} +%.*%/test.rego:6 | | Redo __local0__ = input.z {__local0__: 3} +%.*%/test.rego:6 | | Redo plus(__local1__, __local2__, __local0__) {__local0__: 3, __local1__: 1, __local2__: 2} +%.*%/test.rego:6 | | Redo __local2__ = input.y {__local2__: 2} +%.*%/test.rego:6 | | Redo __local1__ = input.x {__local1__: 1} +%.*%/test.rego:5 | | Redo input.x = 1 {} +true +`, + }, + { + note: "data values", + query: "data.test.p", + includeVars: true, + files: map[string]string{ + "test.rego": `package test +import rego.v1 + +p if { + data.x == 1 + data.x + data.y == data.z +} +`, + "data.json": `{ + "x": 1, + "y": 2, + "z": 3 +}`, + }, + expected: `%SKIP_LINE% +query:1 %.*% Enter data.test.p = _ {} +query:1 %.*% | Eval data.test.p = _ {} +query:1 %.*% | Index data.test.p (matched 1 rule, early exit) {} +%.*%/test.rego:4 | Enter data.test.p {} +%.*%/test.rego:5 | | Eval data.x = 1 {} +%.*%/test.rego:6 | | Eval __local1__ = data.x {} +%.*%/test.rego:6 | | Eval __local2__ = data.y {} +%.*%/test.rego:6 | | Eval plus(__local1__, __local2__, __local0__) {__local1__: 1, __local2__: 2} +%.*%/test.rego:6 | | Eval __local0__ = data.z {__local0__: 3} +%.*%/test.rego:4 | | Exit data.test.p early {} +query:1 %.*% | Exit data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% Redo data.test.p = _ {_: true, data.test.p: true} +query:1 %.*% | Redo data.test.p = _ {_: true, data.test.p: true} +%.*%/test.rego:4 | Redo data.test.p {} +%.*%/test.rego:6 | | Redo __local0__ = data.z {__local0__: 3} +%.*%/test.rego:6 | | Redo plus(__local1__, __local2__, __local0__) {__local0__: 3, __local1__: 1, __local2__: 2} +%.*%/test.rego:6 | | Redo __local2__ = data.y {__local2__: 2} +%.*%/test.rego:6 | | Redo __local1__ = data.x {__local1__: 1} +%.*%/test.rego:5 | | Redo data.x = 1 {} +true +`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var buf bytes.Buffer + + test.WithTempFS(tc.files, func(path string) { + params := newEvalCommandParams() + _ = params.bundlePaths.Set(path) + inputFile := filepath.Join(path, "input.json") + if _, err := os.Stat(inputFile); err == nil { + params.inputPath = inputFile + } + _ = params.outputFormat.Set(formats.Pretty) + _ = params.explain.Set(explainModeFull) + params.traceVarValues = tc.includeVars + params.disableIndexing = true + _ = params.bundlePaths.Set(path) + + _, err := eval([]string{tc.query}, params, &buf) + if err != nil { + t.Fatalf("Unexpected error: %s\n\n%s", err, buf.String()) + } + }) + + actual := buf.String() + if !stringsMatch(t, tc.expected, actual) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", tc.expected, actual) + } + }) + } +} + +func stringsMatch(t *testing.T, expected, actual string) bool { + t.Helper() + + var expectedLines []string + for _, l := range strings.Split(expected, "\n") { + if !strings.Contains(l, "%SKIP_LINE%") { + expectedLines = append(expectedLines, l) + } + } + + actualLines := strings.Split(actual, "\n") + + if len(expectedLines) != len(actualLines) { + t.Errorf("Expected %d lines but got %d", len(expectedLines), len(actualLines)) + return false + } + + for i, expectedLine := range expectedLines { + actualLine := actualLines[i] + + expectedParts := strings.Split(expectedLine, "%.*%") + if len(expectedParts) == 1 { + if expectedLine != actualLine { + t.Errorf("Mismatch on line %d. Expected:\n\n%s\n\nGot:\n\n%s", i, expectedLine, actualLine) + return false + } + } else if len(expectedParts) == 2 { + if !strings.HasPrefix(actualLine, expectedParts[0]) { + t.Errorf("Expected line %d to start with:\n\n%s\n\nbut got:\n\n%s", i, expectedParts[0], actualLine) + return false + } + if !strings.HasSuffix(actualLine, expectedParts[1]) { + t.Errorf("Expected line %d to end with:\n\n%s\n\nbut got:\n\n%s", i, expectedParts[1], actualLine) + return false + } + } else { + t.Fatalf("At most one .* is allowed per line but found %d on line %d:\n\n%s", len(expectedParts)-1, i, expectedLine) + return false + } + } + + return true +} + +func TestResetExprLocations(t *testing.T) { + + // Make sure no panic if passed nil. + resetExprLocations(nil) + + // Run partial evaluation on this fake module and check results. + // The content of the module is not very important it just has to generate + // support and cases where the locaiton is unset. The default causes support + // and exprs with no location information. + pq, err := rego.New(rego.Query("data.test.p = x"), rego.Module("test.rego", ` + package test + + default p = false + + p if { + input.x = q[_] + } + + q contains 1 + q contains 2 + `)).Partial(context.Background()) + + if err != nil { + t.Fatal(err) + } + + resetExprLocations(pq) + + var exp int + + vis := ast.NewGenericVisitor(func(x any) bool { + if expr, ok := x.(*ast.Expr); ok { + if expr.Location.Row != exp { + t.Fatalf("Expected %v to have row %v but got %v", expr, exp, expr.Location.Row) + } + exp++ + } + return false + }) + + for i := range pq.Queries { + vis.Walk(pq.Queries[i]) + } + + for i := range pq.Support { + vis.Walk(pq.Support[i]) + } + +} +func kubeSchemaServer(t *testing.T) *httptest.Server { + t.Helper() + bs, err := os.ReadFile("../v1/ast/testdata/_definitions.json") + if err != nil { + t.Fatal(err) + } + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + _, err := w.Write(bs) + if err != nil { + panic(err) + } + })) + return ts +} + +func TestEvalPartialFormattedOutput(t *testing.T) { + + query := `time.clock(input.x) == time.clock(input.y)` + tests := []struct { + format, expected string + }{ + { + format: formats.Pretty, + expected: `+---------+------------------------------------------+ +| Query 1 | time.clock(input.y, time.clock(input.x)) | ++---------+------------------------------------------+ +`}, + { + format: formats.Source, + expected: `# Query 1 +time.clock(input.y, time.clock(input.x)) + +`}, + } + + for _, tc := range tests { + t.Run(tc.format, func(t *testing.T) { + buf := new(bytes.Buffer) + params := newEvalCommandParams() + params.partial = true + _ = params.outputFormat.Set(tc.format) + _, err := eval([]string{query}, params, buf) + if err != nil { + t.Fatal("unexpected error:", err) + } + if actual := buf.String(); actual != tc.expected { + t.Errorf("expected output %q\ngot %q", tc.expected, actual) + } + }) + } +} + +func TestEvalPartialOutput_RegoVersion(t *testing.T) { + tests := []struct { + note string + regoV1ImportCapable bool + v0Compatible bool + query string + module string + expected map[string]string + }{ + { + note: "v0, no future keywords", + v0Compatible: true, + regoV1ImportCapable: true, + query: "data.test.p", + module: `package test + +p[v] { + v := input.v +} +`, + expected: map[string]string{ + formats.Source: `# Query 1 +data.partial.test.p + +# Module 1 +package partial.test + +import rego.v1 + +p contains __local0__1 if __local0__1 = input.v +`, + formats.Pretty: `+-----------+-------------------------------------------------+ +| Query 1 | data.partial.test.p | ++-----------+-------------------------------------------------+ +| Support 1 | package partial.test | +| | | +| | import rego.v1 | +| | | +| | p contains __local0__1 if __local0__1 = input.v | ++-----------+-------------------------------------------------+ +`, + }, + }, + { + note: "v0, no future keywords, not rego.v1 import capable", + v0Compatible: true, + regoV1ImportCapable: false, + query: "data.test.p", + module: `package test + +p[v] { + v := input.v +} +`, + expected: map[string]string{ + formats.Source: `# Query 1 +data.partial.test.p + +# Module 1 +package partial.test + +p[__local0__1] { + __local0__1 = input.v +} +`, + formats.Pretty: `+-----------+-------------------------+ +| Query 1 | data.partial.test.p | ++-----------+-------------------------+ +| Support 1 | package partial.test | +| | | +| | p[__local0__1] { | +| | __local0__1 = input.v | +| | } | ++-----------+-------------------------+ +`, + }, + }, + { + note: "v0, future keywords", + v0Compatible: true, + regoV1ImportCapable: true, + query: "data.test.p", + module: `package test + +import rego.v1 + +p contains v if { + v := input.v +} +`, + expected: map[string]string{ + formats.Source: `# Query 1 +data.partial.test.p + +# Module 1 +package partial.test + +import rego.v1 + +p contains __local0__1 if __local0__1 = input.v +`, + formats.Pretty: `+-----------+-------------------------------------------------+ +| Query 1 | data.partial.test.p | ++-----------+-------------------------------------------------+ +| Support 1 | package partial.test | +| | | +| | import rego.v1 | +| | | +| | p contains __local0__1 if __local0__1 = input.v | ++-----------+-------------------------------------------------+ +`, + }, + }, + { + note: "v1", + regoV1ImportCapable: true, + v0Compatible: false, + query: "data.test.p", + module: `package test + +p contains v if { + v := input.v +} +`, + expected: map[string]string{ + formats.Source: `# Query 1 +data.partial.test.p + +# Module 1 +package partial.test + +p contains __local0__1 if __local0__1 = input.v +`, + formats.Pretty: `+-----------+-------------------------------------------------+ +| Query 1 | data.partial.test.p | ++-----------+-------------------------------------------------+ +| Support 1 | package partial.test | +| | | +| | p contains __local0__1 if __local0__1 = input.v | ++-----------+-------------------------------------------------+ +`, + }, + }, + { + note: "v1, rego.v1 import", + regoV1ImportCapable: true, + v0Compatible: false, + query: "data.test.p", + module: `package test + +import rego.v1 + +p contains v if { + v := input.v +} +`, + expected: map[string]string{ + formats.Source: `# Query 1 +data.partial.test.p + +# Module 1 +package partial.test + +p contains __local0__1 if __local0__1 = input.v +`, + formats.Pretty: `+-----------+-------------------------------------------------+ +| Query 1 | data.partial.test.p | ++-----------+-------------------------------------------------+ +| Support 1 | package partial.test | +| | | +| | p contains __local0__1 if __local0__1 = input.v | ++-----------+-------------------------------------------------+ +`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + for format, expected := range tc.expected { + t.Run(format, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + params := newEvalCommandParams() + _ = params.dataPaths.Set(filepath.Join(path, "test.rego")) + params.partial = true + params.v0Compatible = tc.v0Compatible + _ = params.outputFormat.Set(format) + + if !tc.regoV1ImportCapable { + caps := newCapabilitiesFlag() + caps.C = ast.CapabilitiesForThisVersion() + caps.C.Features = []string{ + ast.FeatureRefHeadStringPrefixes, + ast.FeatureRefHeads, + } + params.capabilities = caps + } + + buf := new(bytes.Buffer) + _, err := eval([]string{tc.query}, params, buf) + if err != nil { + t.Fatal("unexpected error:", err) + } + if actual := buf.String(); actual != expected { + t.Errorf("expected output:\n\n%s\n\ngot:\n\n%s", expected, actual) + } + }) + }) + } + }) + } +} + +func TestEvalDiscardOutput(t *testing.T) { + tests := map[string]struct { + query, format, expected string + params evalCommandParams + }{ + "success example": { + query: "1*2+3", + params: func() evalCommandParams { + params := newEvalCommandParams() + err := params.outputFormat.Set(formats.Discard) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + return params + }(), + expected: `{ + "result": "discarded" +} +`}, + "error example": { + query: "1/0", + params: func() evalCommandParams { + params := newEvalCommandParams() + err := params.outputFormat.Set(formats.Discard) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + return params + }(), + expected: `{} +`}, + "error example show built-in-errors": { + query: "1/0", + params: func() evalCommandParams { + params := newEvalCommandParams() + err := params.outputFormat.Set(formats.Discard) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + params.showBuiltinErrors = true + return params + }(), + expected: `{ + "errors": [ + { + "code": "eval_builtin_error", + "location": { + "col": 1, + "file": "", + "row": 1 + }, + "message": "div: divide by zero" + } + ] +} +`}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + var buf bytes.Buffer + _, err := eval([]string{tc.query}, tc.params, &buf) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + if actual := buf.String(); actual != tc.expected { + t.Errorf("expected output %q\ngot %q", tc.expected, actual) + } + }) + } +} + +func TestEvalDiscardProfilerOutput(t *testing.T) { + params := newEvalCommandParams() + err := params.outputFormat.Set(formats.Discard) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + params.profile = true + + query := "1*2+3" + + var buf bytes.Buffer + _, err = eval([]string{query}, params, &buf) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + var output map[string]any + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + + // assert that the result is set to discarded + result, ok := output["result"].(string) + if !ok { + t.Fatal("error extracting result as string from output") + } + + if result != "discarded" { + t.Fatal("Expected result field to be set to 'discarded'") + } + + // assert that profile is still set + _, ok = output["profile"] + if !ok { + t.Fatal("error in parsing profile output") + } +} + +func TestPolicyWithStrictFlag(t *testing.T) { + testsShouldError := []struct { + note string + v0Compatible bool + policy string + query string + expectedCode string + expectedMessage string + }{ + { + note: "strict mode should error on unused imports", + policy: `package x + import future.keywords.if + import data.foo + foo = 2`, + query: "data.foo", + expectedCode: "rego_compile_error", + expectedMessage: "import data.foo unused", + }, + { + note: "v0 compat, strict mode should error on duplicate imports", + v0Compatible: true, + policy: `package x + import data.bar + import data.bar + foo = bar`, + query: "data.foo", + expectedCode: "rego_compile_error", + expectedMessage: "import must not shadow import data.bar", + }, + { + note: "v0 compat, strict mode should error on unused imports", + v0Compatible: true, + policy: `package x + import future.keywords.if + import data.foo + foo = 2`, + query: "data.foo", + expectedCode: "rego_compile_error", + expectedMessage: "import data.foo unused", + }, + { + note: "v0 compat, strict mode should error when reserved vars data or input is used", + v0Compatible: true, + policy: `package x + data { x = 1}`, + query: "data.foo", + expectedCode: "rego_compile_error", + expectedMessage: "rules must not shadow data (use a different rule name)", + }, + } + + for _, tc := range testsShouldError { + t.Run(tc.note, func(t *testing.T) { + + files := map[string]string{ + "test.rego": tc.policy, + } + + test.WithTempFS(files, func(path string) { + for _, strict := range []bool{true, false} { + params := newEvalCommandParams() + params.strict = strict + params.v0Compatible = tc.v0Compatible + + _ = params.dataPaths.Set(filepath.Join(path, "test.rego")) + + var buf bytes.Buffer + _, err := eval([]string{tc.query}, params, &buf) + + if strict { + if err == nil { + t.Fatal("expected error, got nil") + } + var output presentation.Output + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + + if code := output.Errors[0].Code; code != tc.expectedCode { + t.Errorf("expected code '%v', got '%v'", tc.expectedCode, code) + } + if msg := output.Errors[0].Message; msg != tc.expectedMessage { + t.Errorf("expected message '%v', got '%v'", tc.expectedMessage, msg) + } + } else if err != nil { + var output presentation.Output + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + t.Fatal("unexpected error when non-strict:", output) + } + } + }) + }) + } + + testsShouldPass := []struct { + note string + policy string + query string + }{ + { + note: "This should not error as it is valid", + policy: `package x + import future.keywords.if + foo = 2`, + query: "data.foo", + }, + { + note: "Strict mode should not validate the query, only the policy, this should not error", + policy: `package x + import future.keywords.if + foo = 2`, + query: "x := data.x.foo", + }, + } + for _, tc := range testsShouldPass { + t.Run(tc.note, func(t *testing.T) { + + files := map[string]string{ + "test.rego": tc.policy, + } + + test.WithTempFS(files, func(_ string) { + params := newEvalCommandParams() + params.strict = true + + var buf bytes.Buffer + _, err := eval([]string{tc.query}, params, &buf) + if err != nil { + t.Errorf("Should not error, got error: '%v'", err) + } + }) + }) + } + +} + +func TestBundleWithStrictFlag(t *testing.T) { + testsShouldError := []struct { + note string + v0Compatible bool + policy string + query string + expectedCode string + expectedMessage string + }{ + { + note: "strict mode should error on unused imports in this bundle", + policy: `package x + import data.foo + foo = 2`, + query: "data.foo", + expectedCode: "rego_compile_error", + expectedMessage: "import data.foo unused", + }, + { + note: "v0 compat, strict mode should error on duplicate imports in this bundle", + v0Compatible: true, + policy: `package x + import data.bar + import data.bar + foo = bar`, + query: "data.foo", + expectedCode: "rego_compile_error", + expectedMessage: "import must not shadow import data.bar", + }, + { + note: "v0 compat, strict mode should error on unused imports in this bundle", + v0Compatible: true, + policy: `package x + import data.foo + foo = 2`, + query: "data.foo", + expectedCode: "rego_compile_error", + expectedMessage: "import data.foo unused", + }, + { + note: "v0 compat, strict mode should error when reserved vars data or input is used in this bundle", + v0Compatible: true, + policy: `package x + data { x = 1}`, + query: "data.foo", + expectedCode: "rego_compile_error", + expectedMessage: "rules must not shadow data (use a different rule name)", + }, + } + + for _, tc := range testsShouldError { + t.Run(tc.note, func(t *testing.T) { + + files := map[string]string{ + "test.rego": tc.policy, + } + + test.WithTempFS(files, func(path string) { + for _, strict := range []bool{true, false} { + params := newEvalCommandParams() + if err := params.bundlePaths.Set(path); err != nil { + t.Fatal(err) + } + params.strict = strict + params.v0Compatible = tc.v0Compatible + + var buf bytes.Buffer + _, err := eval([]string{tc.query}, params, &buf) + + if strict { + if err == nil { + t.Fatal("expected error, got nil") + } + var output presentation.Output + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + + if code := output.Errors[0].Code; code != tc.expectedCode { + t.Errorf("expected code '%v', got '%v'", tc.expectedCode, code) + } + if msg := output.Errors[0].Message; msg != tc.expectedMessage { + t.Errorf("expected message '%v', got '%v'", tc.expectedMessage, msg) + } + } else if err != nil { + var output presentation.Output + if err := util.NewJSONDecoder(&buf).Decode(&output); err != nil { + t.Fatal(err) + } + t.Fatal("unexpected error when non-strict:", output) + } + } + }) + }) + } + + testsShouldPass := []struct { + note string + policy string + query string + }{ + { + note: "This bundle should not error as it is valid", + policy: `package x + import future.keywords.if + foo = 2`, + query: "data.foo", + }, + { + note: "Strict mode should not validate the query, only the policy, this bundle should not error", + policy: `package x + import future.keywords.if + foo = 2`, + query: "x := data.x.foo", + }, + } + for _, tc := range testsShouldPass { + t.Run(tc.note, func(t *testing.T) { + + files := map[string]string{ + "test.rego": tc.policy, + } + + test.WithTempFS(files, func(path string) { + params := newEvalCommandParams() + if err := params.bundlePaths.Set(path); err != nil { + t.Fatal(err) + } + params.strict = true + + var buf bytes.Buffer + _, err := eval([]string{tc.query}, params, &buf) + if err != nil { + t.Errorf("Should not error, got error: '%v'", err) + } + }) + }) + } + +} + +func TestIfElseIfElseNoBrace(t *testing.T) { + files := map[string]string{ + "bug.rego": `package bug + + p if false + else := 1 if false + else := 2`, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.optimizationLevel = 1 + params.dataPaths = newrepeatedStringFlag([]string{path}) + params.entrypoints = newrepeatedStringFlag([]string{"bug/p"}) + + var buf bytes.Buffer + + defined, err := eval([]string{"data.bug.p"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + }) +} + +func TestIfElseIfElseBrace(t *testing.T) { + files := map[string]string{ + "bug.rego": `package bug + + p if false + else := 1 if { false } + else := 2`, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.optimizationLevel = 1 + params.dataPaths = newrepeatedStringFlag([]string{path}) + params.entrypoints = newrepeatedStringFlag([]string{"bug/p"}) + + var buf bytes.Buffer + + defined, err := eval([]string{"data.bug.p"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + }) +} + +func TestIfElse(t *testing.T) { + files := map[string]string{ + "bug.rego": `package bug + + p if false + else := 1 `, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.optimizationLevel = 1 + params.dataPaths = newrepeatedStringFlag([]string{path}) + params.entrypoints = newrepeatedStringFlag([]string{"bug/p"}) + + var buf bytes.Buffer + + defined, err := eval([]string{"data.bug.p"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + }) +} + +// TestElseNoIfV0 only applies to v0 Rego +func TestElseNoIfV0(t *testing.T) { + files := map[string]string{ + "bug.rego": `package bug + import future.keywords.if + p if false + else = x { + x=2 + } `, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.optimizationLevel = 1 + params.dataPaths = newrepeatedStringFlag([]string{path}) + params.entrypoints = newrepeatedStringFlag([]string{"bug/p"}) + params.v0Compatible = true + + var buf bytes.Buffer + + defined, err := eval([]string{"data.bug.p"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + }) +} + +func TestElseIf(t *testing.T) { + files := map[string]string{ + "bug.rego": `package bug + + p if false + else := x if { + x=2 + } `, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.optimizationLevel = 1 + params.dataPaths = newrepeatedStringFlag([]string{path}) + params.entrypoints = newrepeatedStringFlag([]string{"bug/p"}) + + var buf bytes.Buffer + + defined, err := eval([]string{"data.bug.p"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + }) +} + +// TestElseIfElseV0 only applies to v0 Rego +func TestElseIfElseV0(t *testing.T) { + files := map[string]string{ + "bug.rego": `package bug + import future.keywords.if + p if false + else := x if { + x=2 + 1==2 + } else =x { + x=3 + }`, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.optimizationLevel = 1 + params.dataPaths = newrepeatedStringFlag([]string{path}) + params.entrypoints = newrepeatedStringFlag([]string{"bug/p"}) + params.v0Compatible = true + + var buf bytes.Buffer + + defined, err := eval([]string{"data.bug.p"}, params, &buf) + if !defined || err != nil { + t.Fatalf("Unexpected undefined or error: %v", err) + } + }) +} + +func TestUnexpectedElseIfElseErr(t *testing.T) { + files := map[string]string{ + "bug.rego": `package bug + + p if false + else := x if { + x=2 + 1==2 + } else + x=3 + `, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.optimizationLevel = 1 + params.dataPaths = newrepeatedStringFlag([]string{path}) + params.entrypoints = newrepeatedStringFlag([]string{"bug/p"}) + + var buf bytes.Buffer + + _, err := eval([]string{"data.bug.p"}, params, &buf) + + // Check if there was an error + if err == nil { + t.Fatalf("expected an error, but got nil") + } + + // Check the error message + errorMessage := err.Error() + expectedErrorMessage := "rego_parse_error: unexpected identifier token: expected else value term or rule body" + if !strings.Contains(errorMessage, expectedErrorMessage) { + t.Fatalf("expected error message to contain '%s', but got '%s'", expectedErrorMessage, errorMessage) + } + }) +} + +func TestUnexpectedElseIfErr(t *testing.T) { + files := map[string]string{ + "bug.rego": `package bug + + q := 1 if false + else := 2 if + `, + } + + test.WithTempFS(files, func(path string) { + + params := newEvalCommandParams() + params.optimizationLevel = 1 + params.dataPaths = newrepeatedStringFlag([]string{path}) + params.entrypoints = newrepeatedStringFlag([]string{"bug/p"}) + + var buf bytes.Buffer + + _, err := eval([]string{"data.bug.p"}, params, &buf) + + // Check if there was an error + if err == nil { + t.Fatalf("expected an error, but got nil") + } + + // Check the error message + errorMessage := err.Error() + expectedErrorMessage := "rego_parse_error: unexpected eof token: rule body expected" + if !strings.Contains(errorMessage, expectedErrorMessage) { + t.Fatalf("expected error message to contain '%s', but got '%s'", expectedErrorMessage, errorMessage) + } + }) +} + +func TestEval_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + modules map[string]string + query string + expErrs []string + }{ + { + note: "v0 module", + modules: map[string]string{ + "test.rego": `package test +a[x] { + x := 42 +}`, + }, + query: `data.test.a`, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1 module", + modules: map[string]string{ + "test.rego": `package test +a contains x if { + x := 42 +}`, + }, + query: `data.test.a`, + }, + } + + setup := []struct { + name string + commandParams func(params *evalCommandParams, path string) + }{ + { + name: "Files", + commandParams: func(params *evalCommandParams, path string) { + params.dataPaths = newrepeatedStringFlag([]string{path}) + }, + }, + { + name: "Bundle", + commandParams: func(params *evalCommandParams, path string) { + if err := params.bundlePaths.Set(path); err != nil { + t.Fatal(err) + } + }, + }, + } + + for _, s := range setup { + for _, tc := range tests { + t.Run(fmt.Sprintf("%s: %s", s.name, tc.note), func(t *testing.T) { + test.WithTempFS(tc.modules, func(path string) { + params := newEvalCommandParams() + _ = params.outputFormat.Set(formats.Pretty) + s.commandParams(¶ms, path) + + var buf bytes.Buffer + + defined, err := eval([]string{tc.query}, params, &buf) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatal("expected error, got none") + } + + actual := buf.String() + for _, expErr := range tc.expErrs { + if !strings.Contains(actual, expErr) { + t.Fatalf("expected error:\n\n%v\n\ngot\n\n%v", expErr, actual) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v, buf: %s", err, buf.String()) + } else if !defined { + t.Fatal("expected result to be defined") + } + } + }) + }) + } + } +} + +func TestEvalPolicyWithCompatibleFlags(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + v1Compatible bool + modules map[string]string + query string + expectedErr string + }{ + { + note: "v0 compatibility: policy with no rego.v1 or future.keywords imports", + v0Compatible: true, + modules: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + query: "data.test.allow", + expectedErr: "rego_parse_error", + }, + { + note: "v0 compatibility: policy with rego.v1 import", + v0Compatible: true, + modules: map[string]string{ + "test.rego": `package test + import rego.v1 + allow if { + 1 < 2 + }`, + }, + query: "data.test.allow", + }, + { + note: "v0 compatibility: policy with future.keywords import", + v0Compatible: true, + modules: map[string]string{ + "test.rego": `package test + import future.keywords + allow if { + 1 < 2 + }`, + }, + query: "data.test.allow", + }, + { + note: "v1 compatibility: policy with no rego.v1 or future.keywords imports", + v1Compatible: true, + modules: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + query: "data.test.allow", + }, + { + note: "v1 compatibility: policy with rego.v1 import", + v1Compatible: true, + modules: map[string]string{ + "test.rego": `package test + import rego.v1 + allow if { + 1 < 2 + }`, + }, + query: "data.test.allow", + }, + { + note: "v1 compatibility: policy with future.keywords import", + v1Compatible: true, + modules: map[string]string{ + "test.rego": `package test + import future.keywords.if + allow if { + 1 < 2 + }`, + }, + query: "data.test.allow", + }, + { + note: "v0 + v1 compatibility: policy with no rego.v1 or future.keywords imports", + v0Compatible: true, + v1Compatible: true, + modules: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + query: "data.test.allow", + expectedErr: "rego_parse_error", + }, + { + note: "v0 + v1 compatibility: policy with rego.v1 import", + v0Compatible: true, + v1Compatible: true, + modules: map[string]string{ + "test.rego": `package test + import rego.v1 + allow if { + 1 < 2 + }`, + }, + query: "data.test.allow", + }, + { + note: "v0 + v1 compatibility: policy with future.keywords import", + v0Compatible: true, + v1Compatible: true, + modules: map[string]string{ + "test.rego": `package test + import future.keywords + allow if { + 1 < 2 + }`, + }, + query: "data.test.allow", + }, + { + note: "v1 compatibility: policy with no rego.v1 or future.keywords imports", + v1Compatible: true, + modules: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + query: "data.test.allow", + }, + } + + setup := []struct { + name string + commandParams func(params *evalCommandParams, path string) + }{ + { + name: "Files", + commandParams: func(params *evalCommandParams, path string) { + params.dataPaths = newrepeatedStringFlag([]string{path}) + }, + }, + { + name: "Bundle", + commandParams: func(params *evalCommandParams, path string) { + if err := params.bundlePaths.Set(path); err != nil { + t.Fatal(err) + } + }, + }, + } + + for _, s := range setup { + for _, tc := range tests { + t.Run(fmt.Sprintf("%s: %s", s.name, tc.note), func(t *testing.T) { + test.WithTempFS(tc.modules, func(path string) { + params := newEvalCommandParams() + s.commandParams(¶ms, path) + params.v0Compatible = tc.v0Compatible + params.v1Compatible = tc.v1Compatible + + var buf bytes.Buffer + + defined, err := eval([]string{tc.query}, params, &buf) + + if tc.expectedErr == "" { + if err != nil { + t.Fatalf("Unexpected error: %v, buf: %s", err, buf.String()) + } else if !defined { + t.Fatal("expected result to be defined") + } + } else { + if err == nil { + t.Fatal("expected error, got none") + } + + actual := buf.String() + if !strings.Contains(actual, tc.expectedErr) { + t.Fatalf("expected error:\n\n%v\n\ngot\n\n%v", tc.expectedErr, actual) + } + } + }) + }) + } + } +} + +func TestEvalPolicyWithRegoV1Capability(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + capabilities *ast.Capabilities + modules map[string]string + expErrs []string + }{ + { + note: "v0 module, v0-compatible, no capabilities", + v0Compatible: true, + modules: map[string]string{ + "test.rego": `package test + allow { + 1 < 2 + }`, + }, + }, + { + note: "v0 module, v0-compatible, v0 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + modules: map[string]string{ + "test.rego": `package test + allow { + 1 < 2 + }`, + }, + }, + { + note: "v0 module, v0-compatible, v1 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + modules: map[string]string{ + "test.rego": `package test + allow { + 1 < 2 + }`, + }, + }, + { + note: "v0 module, not v0-compatible, no capabilities", + v0Compatible: false, + modules: map[string]string{ + "test.rego": `package test + allow { + 1 < 2 + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + }, + }, + { + note: "v0 module, not v0-compatible, v0 capabilities", + v0Compatible: false, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + modules: map[string]string{ + "test.rego": `package test + allow { + 1 < 2 + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + }, + }, + { + note: "v0 module, not v0-compatible, v0 capabilities without rego_v1 feature", + v0Compatible: false, + capabilities: capsWithoutFeat(ast.RegoV0, ast.FeatureRegoV1), + modules: map[string]string{ + "test.rego": `package test + allow { + 1 < 2 + }`, + }, + expErrs: []string{ + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "v0 module, not v0-compatible, v1 capabilities", + v0Compatible: false, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + modules: map[string]string{ + "test.rego": `package test + allow { + 1 < 2 + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + }, + }, + + { + note: "v1 module, v0-compatible, no capabilities", + v0Compatible: true, + modules: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v1 module, v0-compatible, v0 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + modules: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v1 module, v0-compatible, v1 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + modules: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v1 module, not v0-compatible, no capabilities", + v0Compatible: false, + modules: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + }, + { + note: "v1 module, not v0-compatible, v0 capabilities", + v0Compatible: false, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + modules: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + }, + { + note: "v1 module, not v0-compatible, v0 capabilities without rego_v1 feature", + v0Compatible: false, + capabilities: capsWithoutFeat(ast.RegoV0, ast.FeatureRegoV1), + modules: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + expErrs: []string{ + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "v1 module, not v0-compatible, v1 capabilities", + v0Compatible: false, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + modules: map[string]string{ + "test.rego": `package test + allow if { + 1 < 2 + }`, + }, + }, + } + + setup := []struct { + name string + commandParams func(params *evalCommandParams, path string) + }{ + { + name: "Files", + commandParams: func(params *evalCommandParams, path string) { + params.dataPaths = newrepeatedStringFlag([]string{path}) + }, + }, + { + name: "Bundle", + commandParams: func(params *evalCommandParams, path string) { + if err := params.bundlePaths.Set(path); err != nil { + t.Fatal(err) + } + }, + }, + } + + for _, s := range setup { + for _, tc := range tests { + t.Run(fmt.Sprintf("%s: %s", s.name, tc.note), func(t *testing.T) { + test.WithTempFS(tc.modules, func(path string) { + params := newEvalCommandParams() + s.commandParams(¶ms, path) + _ = params.outputFormat.Set(formats.Pretty) + params.v0Compatible = tc.v0Compatible + params.capabilities.C = tc.capabilities + + var buf bytes.Buffer + + defined, err := eval([]string{"data.test.allow"}, params, &buf) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatal("expected error, got none") + } + + actual := buf.String() + for _, expErr := range tc.expErrs { + if !strings.Contains(actual, expErr) { + t.Fatalf("expected error:\n\n%v\n\ngot\n\n%v", expErr, actual) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v, buf: %s", err, buf.String()) + } else if !defined { + t.Fatal("expected result to be defined") + } + } + }) + }) + } + } +} + +func TestEvalPolicyWithBundleRegoVersion(t *testing.T) { + tests := []struct { + note string + files map[string]string + query string + expectedErr string + }{ + { + note: "v0.x bundle, no rego.v1 or future.keywords imports", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +allow if { + 1 < 2 +}`, + }, + query: "data.test.allow", + expectedErr: "rego_parse_error", + }, + { + note: "v0 bundle, v1 per-file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } +}`, + "policy1.rego": `package test +p[1] { + 1 < 2 +} +`, + "policy2.rego": `package test +p contains 2 if { + 1 < 2 +} +`, + }, + query: "data.test.p", + }, + { + note: "v0 bundle, v1 per-file override (glob)", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/bar/*.rego": 1 + } +}`, + "foo/policy1.rego": `package test +p[1] { + 1 < 2 +} +`, + "bar/policy1.rego": `package test +p contains 2 if { + 1 < 2 +} +`, + "bar/policy2.rego": `package test +p contains 3 if { + 1 < 2 +} +`, + }, + query: "data.test.p", + }, + { + note: "v0 bundle, v1 per-file override, incompliant", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } +}`, + "policy1.rego": `package test +p[1] { + 1 < 2 +} +`, + "policy2.rego": `package test +p[2] { + 1 < 2 +} +`, + }, + query: "data.test.p", + expectedErr: "rego_parse_error", + }, + + { + note: "v1.0 bundle, no rego.v1 or future.keywords imports", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +allow if { + 1 < 2 +}`, + }, + query: "data.test.allow", + }, + { + note: "v1.0 bundle, policy with rego.v1 import", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +import rego.v1 +allow if { + 1 < 2 +}`, + }, + query: "data.test.allow", + }, + { + note: "v1.0 bundle, future.keywords import", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +import future.keywords.if +allow if { + 1 < 2 +}`, + }, + query: "data.test.allow", + }, + { + note: "v1.0 bundle, keywords not used", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +allow { + 1 < 2 +}`, + }, + query: "data.test.allow", + expectedErr: "rego_parse_error", + }, + { + note: "v1 bundle, v0 per-file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } +}`, + "policy1.rego": `package test +p[1] { + 1 < 2 +} +`, + "policy2.rego": `package test +p contains 2 if { + 1 < 2 +} +`, + }, + query: "data.test.p", + }, + { + note: "v1 bundle, v0 per-file override (glob)", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/foo/*.rego": 0 + } +}`, + "foo/policy1.rego": `package test +p[1] { + 1 < 2 +} +`, + "foo/policy2.rego": `package test +p[2] { + 1 < 2 +} +`, + "bar/policy1.rego": `package test +p contains 3 if { + 1 < 2 +} +`, + }, + query: "data.test.p", + }, + { + note: "v1 bundle, v0 per-file override, incompliant", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "*/policy2.rego": 0 + } +}`, + "policy1.rego": `package test +p contains 1 if { + input.x == 1 +} +`, + "policy2.rego": `package test +p contains 2 if { + input.x == 1 +} +`, + }, + query: "data.test.p", + expectedErr: "rego_parse_error", + }, + } + + bundleTypeCases := []struct { + note string + tar bool + }{ + { + "bundle dir", false, + }, + { + "bundle tar", true, + }, + } + + v0CompatibleFlagCases := []struct { + note string + used bool + }{ + { + "no --v0-compatible", false, + }, + { + "--v0-compatible", true, + }, + } + + for _, bundleType := range bundleTypeCases { + for _, v0CompatibleFlag := range v0CompatibleFlagCases { + for _, tc := range tests { + t.Run(fmt.Sprintf("%s, %s, %s", bundleType.note, v0CompatibleFlag.note, tc.note), func(t *testing.T) { + files := map[string]string{} + + if bundleType.tar { + files["bundle.tar.gz"] = "" + } else { + maps.Copy(files, tc.files) + } + + test.WithTempFS(files, func(root string) { + p := root + if bundleType.tar { + p = filepath.Join(root, "bundle.tar.gz") + files := make([][2]string, 0, len(tc.files)) + for k, v := range tc.files { + files = append(files, [2]string{k, v}) + } + buf := archive.MustWriteTarGz(files) + bf, err := os.Create(p) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + params := newEvalCommandParams() + params.v0Compatible = v0CompatibleFlag.used + if err := params.bundlePaths.Set(p); err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + + defined, err := eval([]string{tc.query}, params, &buf) + + if tc.expectedErr == "" { + if err != nil { + t.Fatalf("Unexpected error: %v, buf: %s", err, buf.String()) + } else if !defined { + t.Fatal("expected result to be defined") + } + } else { + if err == nil { + t.Fatal("expected error, got none") + } + + actual := buf.String() + if !strings.Contains(actual, tc.expectedErr) { + t.Fatalf("expected error:\n\n%v\n\ngot\n\n%v", tc.expectedErr, actual) + } + } + }) + }) + } + } + } +} + +func TestWithQueryImports(t *testing.T) { + tests := []struct { + note string + query string + imports []string + v0Compatible bool + v1Compatible bool + exp string + expErrs []string + }{ + { + note: "no imports, none required", + query: "1 + 2", + exp: "3\n", + }, + { + note: "future keyword used, future.keywords imported", + query: `"b" in ["a", "b", "c"]`, + imports: []string{"future.keywords.in"}, + exp: "true\n", + }, + { + note: "future keyword used, rego.v1 imported", + query: `"b" in ["a", "b", "c"]`, + imports: []string{"rego.v1"}, + exp: "true\n", + }, + { + note: "future keyword used, invalid rego.v2 imported", + v0Compatible: true, + query: `"b" in ["a", "b", "c"]`, + imports: []string{"rego.v2"}, + expErrs: []string{ + "1:8: rego_parse_error: invalid import `rego.v2`, must be `rego.v1`", + }, + }, + { + note: "future keyword used, no imports (v0)", + v0Compatible: true, + query: `"b" in ["a", "b", "c"]`, + expErrs: []string{ + "1:5: rego_unsafe_var_error: var in is unsafe (hint: `import future.keywords.in` to import a future keyword)", + }, + }, + { + note: "future keyword used, no imports (v1)", + v1Compatible: true, + query: `"b" in ["a", "b", "c"]`, + exp: "true\n", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + params := newEvalCommandParams() + _ = params.outputFormat.Set(formats.Pretty) + params.imports = newrepeatedStringFlag(tc.imports) + params.v0Compatible = tc.v0Compatible + params.v1Compatible = tc.v1Compatible + + var buf bytes.Buffer + + defined, err := eval([]string{tc.query}, params, &buf) + + if len(tc.expErrs) == 0 { + if err != nil { + t.Fatalf("Unexpected error: %v, buf: %s", err, buf.String()) + } + + if !defined { + t.Fatal("expected result to be defined") + } + + if buf.String() != tc.exp { + t.Fatalf("expected:\n\n%s\n\ngot:\n\n%s", tc.exp, buf.String()) + } + } else { + if err == nil { + t.Fatal("expected error, got none") + } + + actual := buf.String() + for _, expErr := range tc.expErrs { + if !strings.Contains(actual, expErr) { + t.Fatalf("expected error:\n\n%v\n\ngot\n\n%v", expErr, actual) + } + } + } + }) + } +} diff --git a/third_party/opa/cmd/eval_wasmtarget_test.go b/third_party/opa/cmd/eval_wasmtarget_test.go new file mode 100644 index 000000000000..85e74293ca01 --- /dev/null +++ b/third_party/opa/cmd/eval_wasmtarget_test.go @@ -0,0 +1,59 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// +build opa_wasm + +package cmd + +import ( + "testing" +) + +func TestEvalWithJSONInputFile(t *testing.T) { + + input := `{ + "foo": "a", + "b": [ + { + "a": 1, + "b": [1, 2, 3], + "c": null + } + ] +}` + query := "input.b[0].a == 1" + + for _, tgt := range []string{"rego", "wasm"} { + t.Run(tgt, func(t *testing.T) { + params := newEvalCommandParams() + params.target.Set(tgt) + err := testEvalWithInputFile(t, input, query, params) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + }) + } +} + +func TestEvalWithYAMLInputFile(t *testing.T) { + input := ` +foo: a +b: + - a: 1 + b: [1, 2, 3] + c: +` + query := "input.b[0].a == 1" + + for _, tgt := range []string{"rego", "wasm"} { + t.Run(tgt, func(t *testing.T) { + params := newEvalCommandParams() + params.target.Set(tgt) + err := testEvalWithInputFile(t, input, query, params) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + }) + } +} diff --git a/third_party/opa/cmd/exec.go b/third_party/opa/cmd/exec.go new file mode 100644 index 000000000000..9765f3d03883 --- /dev/null +++ b/third_party/opa/cmd/exec.go @@ -0,0 +1,280 @@ +package cmd + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/internal/env" + "github.com/open-policy-agent/opa/cmd/internal/exec" + "github.com/open-policy-agent/opa/internal/config" + internal_logging "github.com/open-policy-agent/opa/internal/logging" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/plugins/bundle" + "github.com/open-policy-agent/opa/v1/plugins/discovery" + "github.com/open-policy-agent/opa/v1/plugins/logs" + "github.com/open-policy-agent/opa/v1/plugins/status" + "github.com/open-policy-agent/opa/v1/sdk" + "github.com/open-policy-agent/opa/v1/util" +) + +func initExec(root *cobra.Command, brand string) { + executable := root.Name() + + var bundlePaths repeatedStringFlag + + params := exec.NewParams(os.Stdout) + + execCommand := &cobra.Command{ + Use: `exec [ [...]]`, + Short: "Execute against input files", + Long: `Execute against input files. + +The 'exec' command executes ` + brand + ` against one or more input files. If the paths +refer to directories, ` + brand + ` will execute against files contained inside those +directories, recursively. + +The 'exec' command accepts a --config-file/-c or series of --set options as +arguments. These options behave the same as way as '` + executable + ` run'. Since the 'exec' +command is intended to execute ` + brand + ` in one-shot, the 'exec' command will +manually trigger plugins before and after policy execution: + +Before: Discovery -> Bundle -> Status +After: Decision Logs + +By default, the 'exec' command executes the "default decision" (specified in +the ` + brand + ` configuration) against each input file. This can be overridden by +specifying the --decision argument and pointing at a specific policy decision, + +e.g., ` + executable + ` exec --decision /foo/bar/baz ... +`, + + Example: fmt.Sprintf(` Loading input from stdin: + %s exec [ [...]] --stdin-input [flags] +`, root.Use), + PreRunE: func(cmd *cobra.Command, _ []string) error { + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + params.Paths = args + params.BundlePaths = bundlePaths.v + if err := runExec(params); err != nil { + logging.Get().WithFields(map[string]any{"err": err}).Error("Unexpected error.") + return err + } + return nil + }, + } + addBundleFlag(execCommand.Flags(), &bundlePaths) + addOutputFormat(execCommand.Flags(), params.OutputFormat) + addConfigFileFlag(execCommand.Flags(), ¶ms.ConfigFile) + addConfigOverrides(execCommand.Flags(), ¶ms.ConfigOverrides) + addConfigOverrideFiles(execCommand.Flags(), ¶ms.ConfigOverrideFiles) + execCommand.Flags().StringVarP(¶ms.Decision, "decision", "", "", "set decision to evaluate") + execCommand.Flags().BoolVarP(¶ms.FailDefined, "fail-defined", "", false, "exits with non-zero exit code on defined/non-empty result and errors") + execCommand.Flags().BoolVarP(¶ms.Fail, "fail", "", false, "exits with non-zero exit code on undefined/empty result and errors") + execCommand.Flags().VarP(params.LogLevel, "log-level", "l", "set log level") + execCommand.Flags().Var(params.LogFormat, "log-format", "set log format") + execCommand.Flags().StringVar(¶ms.LogTimestampFormat, "log-timestamp-format", "", "set log timestamp format (OPA_LOG_TIMESTAMP_FORMAT environment variable)") + execCommand.Flags().DurationVar(¶ms.Timeout, "timeout", 0, "set exec timeout with a Go-style duration, such as '5m 30s'. (default unlimited)") + addV0CompatibleFlag(execCommand.Flags(), ¶ms.V0Compatible, false) + addV1CompatibleFlag(execCommand.Flags(), ¶ms.V1Compatible, false) + + root.AddCommand(execCommand) +} + +func runExec(params *exec.Params) error { + ctx := context.Background() + if params.Timeout != 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, params.Timeout) + defer cancel() + } + return runExecWithContext(ctx, params) +} + +func runExecWithContext(ctx context.Context, params *exec.Params) error { + if minimumInputErr := validateMinimumInput(params); minimumInputErr != nil { + return minimumInputErr + } + + stdLogger, consoleLogger, err := setupLogging(params.LogLevel.String(), params.LogFormat.String(), params.LogTimestampFormat) + if err != nil { + return fmt.Errorf("config error: %w", err) + } + + if params.Logger != nil { + stdLogger = params.Logger + } + + config, err := setupConfig(params.ConfigFile, params.ConfigOverrides, params.ConfigOverrideFiles, params.BundlePaths) + if err != nil { + return fmt.Errorf("config error: %w", err) + } + + ready := make(chan struct{}) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: bytes.NewReader(config), + Logger: stdLogger, + ConsoleLogger: consoleLogger, + Ready: ready, + V0Compatible: params.V0Compatible, + V1Compatible: params.V1Compatible, + }) + if err != nil { + return fmt.Errorf("runtime error: %w", err) + } + + if err := triggerPlugins(ctx, opa, []string{discovery.Name, bundle.Name, status.Name}); err != nil { + return fmt.Errorf("runtime error: %w", err) + } + + select { + case <-ctx.Done(): + err := ctx.Err() + if err == context.DeadlineExceeded { + return errors.New("exec error: timed out before OPA was ready. This can happen when a remote bundle is malformed, or the timeout is set too low for normal OPA initialization") + } + // Note(philipc): Previously, exec would simply eat the context + // cancellation error. We now propagate that upwards to the caller. + return err + case <-ready: + // Do nothing; proceed as normal. + } + + if err := exec.Exec(ctx, opa, params); err != nil { + return fmt.Errorf("exec error: %w", err) + } + + if err := triggerPlugins(ctx, opa, []string{logs.Name}); err != nil { + return fmt.Errorf("runtime error: %w", err) + } + + opa.Stop(ctx) // shutdown plugins + return nil +} + +func triggerPlugins(ctx context.Context, opa *sdk.OPA, names []string) error { + for _, name := range names { + if p, ok := opa.Plugin(name).(plugins.Triggerable); ok { + if err := p.Trigger(ctx); err != nil { + return err + } + } + } + return nil +} + +func setupLogging(level, format, timestampFormat string) (logging.Logger, logging.Logger, error) { + lvl, err := internal_logging.GetLevel(level) + if err != nil { + return nil, nil, err + } + + if timestampFormat == "" { + timestampFormat = os.Getenv("OPA_LOG_TIMESTAMP_FORMAT") + } + + logging.Get().SetFormatter(internal_logging.GetFormatter(format, timestampFormat)) + logging.Get().SetLevel(lvl) + + stdLogger := logging.New() + stdLogger.SetLevel(lvl) + stdLogger.SetFormatter(internal_logging.GetFormatter(format, timestampFormat)) + + consoleLogger := logging.New() + consoleLogger.SetFormatter(internal_logging.GetFormatter(format, timestampFormat)) + + return stdLogger, consoleLogger, nil +} + +func setupConfig(file string, overrides []string, overrideFiles []string, bundlePaths []string) ([]byte, error) { + bs, err := config.Load(file, overrides, overrideFiles) + if err != nil { + return nil, err + } + + var root map[string]any + + if err := util.Unmarshal(bs, &root); err != nil { + return nil, err + } + + if err := injectExplicitBundles(root, bundlePaths); err != nil { + return nil, err + } + + // NOTE(tsandall): This could be generalized in the future if we need to + // deal with arbitrary plugins. + + // NOTE(tsandall): Overriding the discovery trigger mode to manual means + // that all plugins will inherit the trigger mode by default. If the plugin + // trigger mode is explicitly set to something other than 'manual' this will + // result in a configuration error. + if cfg, ok := root["discovery"].(map[string]any); ok { + cfg["trigger"] = "manual" + } + + if cfg, ok := root["bundles"].(map[string]any); ok { + for _, x := range cfg { + if bcfg, ok := x.(map[string]any); ok { + bcfg["trigger"] = "manual" + } + } + } + + if cfg, ok := root["decision_logs"].(map[string]any); ok { + if rcfg, ok := cfg["reporting"].(map[string]any); ok { + rcfg["trigger"] = "manual" + } + } + + if cfg, ok := root["status"].(map[string]any); ok { + cfg["trigger"] = "manual" + } + + return json.Marshal(root) +} + +func injectExplicitBundles(root map[string]any, paths []string) error { + if len(paths) == 0 { + return nil + } + + bundles, ok := root["bundles"].(map[string]any) + if !ok { + bundles = map[string]any{} + root["bundles"] = bundles + } + + for i := range paths { + abspath, err := filepath.Abs(paths[i]) + if err != nil { + return err + } + abspath = filepath.ToSlash(abspath) + bundles[fmt.Sprintf("~%d", i)] = map[string]any{ + "resource": fmt.Sprintf("file://%v", abspath), + } + } + + return nil +} + +func validateMinimumInput(params *exec.Params) error { + if !params.StdIn && len(params.Paths) == 0 { + return errors.New("requires at least 1 path arg, or the --stdin-input flag") + } + return nil +} diff --git a/third_party/opa/cmd/exec_test.go b/third_party/opa/cmd/exec_test.go new file mode 100644 index 000000000000..9f8ca2696a48 --- /dev/null +++ b/third_party/opa/cmd/exec_test.go @@ -0,0 +1,1576 @@ +package cmd + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "maps" + "os" + "path/filepath" + "regexp" + "slices" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/cmd/internal/exec" + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/ast" + loggingtest "github.com/open-policy-agent/opa/v1/logging/test" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/sdk" + sdk_test "github.com/open-policy-agent/opa/v1/sdk/test" + "github.com/open-policy-agent/opa/v1/util/test" +) + +type execOutput struct { + Result []execResultItem `json:"result"` +} + +type execResultItem struct { + DecisionID string `json:"decision_id,omitempty"` + Path string `json:"path"` + Error execResultItemError `json:"error,omitempty"` + Result *any `json:"result,omitempty"` +} + +type execResultItemError struct { + Code string `json:"code"` + Message string `json:"message"` +} + +func (r execResultItemError) isEmpty() bool { + return r.Code == "" && r.Message == "" +} + +func toAnyPtr(a any) *any { + return &a +} + +func toStringSlice(a *any) []string { + switch a := (*a).(type) { + case []string: + return a + case []any: + strSlice := make([]string, len(a)) + for i := range a { + strSlice[i] = a[i].(string) + } + return strSlice + } + + return nil +} + +func resultSliceEquals(t *testing.T, expected, output []execResultItem) { + t.Helper() + + if len(expected) != len(output) { + t.Fatalf("Expected %d results but got %d", len(expected), len(output)) + } + + for i := range output { + if expected[i].Path != output[i].Path { + t.Fatalf("Expected path %v but got %v", expected[i].Path, output[i].Path) + } + + if expected[i].Error.isEmpty() { + if !output[i].Error.isEmpty() { + t.Fatalf("Expected no error but got %v", output[i].Error) + } + + if !slices.Equal(toStringSlice(expected[i].Result), toStringSlice(output[i].Result)) { + t.Fatalf("Expected result %v but got %v", expected[i].Result, output[i].Result) + } + + if !uuidPattern.MatchString(output[i].DecisionID) { + t.Fatalf("Expected decision ID to be a UUID but got %v", output[i].DecisionID) + } + } else { + if expected[i].Error.Code != output[i].Error.Code { + t.Fatalf("Expected error code %v but got %v", expected[i].Error.Code, output[i].Error.Code) + } + + if expected[i].Error.Message != output[i].Error.Message { + t.Fatalf("Expected error message %v but got %v", expected[i].Error.Message, output[i].Error.Message) + } + + if output[i].DecisionID != "" { + t.Fatalf("Expected no decision ID but got %v", output[i].DecisionID) + } + } + } +} + +var uuidPattern = regexp.MustCompile(`^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$`) + +func TestExecBasic(t *testing.T) { + + files := map[string]string{ + "test.json": `{"foo": 7}`, + "test2.yaml": `bar: 8`, + "test3.yml": `baz: 9`, + "ignore": `garbage`, // do not recognize this filetype + } + + test.WithTempFS(files, func(dir string) { + + s := sdk_test.MustNewServer(sdk_test.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "test.rego": ` + package system + main contains "hello" + `, + })) + + defer s.Stop() + + var buf bytes.Buffer + params := exec.NewParams(&buf) + _ = params.OutputFormat.Set("json") + params.ConfigOverrides = []string{ + "services.test.url=" + s.URL(), + "bundles.test.resource=/bundles/bundle.tar.gz", + } + + params.Paths = append(params.Paths, dir) + err := runExec(params) + if err != nil { + t.Fatal(err) + } + + var output execOutput + if err := json.Unmarshal(bytes.ReplaceAll(buf.Bytes(), []byte(dir), nil), &output); err != nil { + t.Fatal(err) + } + + resultSliceEquals(t, []execResultItem{ + { + Path: "/test.json", + Result: toAnyPtr([]string{"hello"}), + }, + { + Path: "/test2.yaml", + Result: toAnyPtr([]string{"hello"}), + }, + { + Path: "/test3.yml", + Result: toAnyPtr([]string{"hello"}), + }, + }, output.Result) + }) +} + +func TestExecDecisionOption(t *testing.T) { + + files := map[string]string{ + "test.json": `{"foo": 7}`, + } + + test.WithTempFS(files, func(dir string) { + + s := sdk_test.MustNewServer(sdk_test.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "test.rego": ` + package foo + + main contains "hello" + `, + })) + + defer s.Stop() + + var buf bytes.Buffer + params := exec.NewParams(&buf) + _ = params.OutputFormat.Set("json") + params.Decision = "foo/main" + params.ConfigOverrides = []string{ + "services.test.url=" + s.URL(), + "bundles.test.resource=/bundles/bundle.tar.gz", + } + + params.Paths = append(params.Paths, dir) + err := runExec(params) + if err != nil { + t.Fatal(err) + } + + var output execOutput + if err := json.Unmarshal(bytes.ReplaceAll(buf.Bytes(), []byte(dir), nil), &output); err != nil { + t.Fatal(err) + } + + resultSliceEquals(t, []execResultItem{ + { + Path: "/test.json", + Result: toAnyPtr([]string{"hello"}), + }, + }, output.Result) + }) + +} + +func TestExecBundleFlag(t *testing.T) { + + files := map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package system + + main contains "hello"`, + } + + test.WithTempFS(files, func(dir string) { + + var buf bytes.Buffer + params := exec.NewParams(&buf) + _ = params.OutputFormat.Set("json") + params.BundlePaths = []string{dir + "/bundle/"} + params.Paths = append(params.Paths, dir+"/files/") + + err := runExec(params) + if err != nil { + t.Fatal(err) + } + + var output execOutput + if err := json.Unmarshal(bytes.ReplaceAll(buf.Bytes(), []byte(dir), nil), &output); err != nil { + t.Fatal(err) + } + + resultSliceEquals(t, []execResultItem{ + { + Path: "/files/test.json", + Result: toAnyPtr([]string{"hello"}), + }, + }, output.Result) + }) +} + +func TestExec_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0, module", + module: `package system +main["hello"] { + input.foo == "bar" +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1 module", + module: `package system +main contains "hello" if { + input.foo == "bar" +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.json": `{"foo": "bar"}`, + } + + test.WithTempFS(files, func(dir string) { + s := sdk_test.MustNewServer( + sdk_test.MockBundle("/bundles/bundle.tar.gz", map[string]string{"test.rego": tc.module}), + sdk_test.RawBundles(true), + ) + + defer s.Stop() + + var buf bytes.Buffer + params := exec.NewParams(&buf) + _ = params.OutputFormat.Set("json") + params.ConfigOverrides = []string{ + "services.test.url=" + s.URL(), + "bundles.test.resource=/bundles/bundle.tar.gz", + } + + params.Paths = append(params.Paths, dir) + + if len(tc.expErrs) > 0 { + testLogger := loggingtest.New() + params.Logger = testLogger + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go func(expectedErrors []string) { + err := runExecWithContext(ctx, params) + // Note(philipc): Catch the expected cancellation + // errors, allowing unexpected test failures through. + if err != context.Canceled { + var errs ast.Errors + if errors.As(err, &errs) { + for _, expErr := range expectedErrors { + found := false + for _, e := range errs { + if strings.Contains(e.Error(), expErr) { + found = true + break + } + } + if !found { + t.Errorf("Could not find expected error: %s in %v", expErr, errs) + return + } + } + } else { + t.Error(err) + return + } + } + }(tc.expErrs) + + if !test.Eventually(t, 5*time.Second, func() bool { + for _, expErr := range tc.expErrs { + found := false + for _, e := range testLogger.Entries() { + if strings.Contains(e.Message, expErr) { + found = true + break + } + } + if !found { + return false + } + } + return true + }) { + t.Fatalf("timed out waiting for logged errors:\n\n%v\n\ngot\n\n%v:", tc.expErrs, testLogger.Entries()) + } + } else { + err := runExec(params) + if err != nil { + t.Fatal(err) + } + + var output execOutput + if err := json.Unmarshal(bytes.ReplaceAll(buf.Bytes(), []byte(dir), nil), &output); err != nil { + t.Fatal(err) + } + + resultSliceEquals(t, []execResultItem{ + { + Path: "/test.json", + Result: toAnyPtr([]string{"hello"}), + }, + }, output.Result) + } + }) + }) + } +} + +func TestExecCompatibleFlags(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + v1Compatible bool + module string + expErrs []string + }{ + { + note: "v0, no keywords used", + v0Compatible: true, + module: `package system +main["hello"] { + input.foo == "bar" +}`, + }, + { + note: "v0, no keywords imported", + v0Compatible: true, + module: `package system +main contains "hello" if { + input.foo == "bar" +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: string cannot be used for rule name", + }, + }, + { + note: "v0, keywords imported", + v0Compatible: true, + module: `package system +import future.keywords +main contains "hello" if { + input.foo == "bar" +}`, + }, + { + note: "v0, rego.v1 imported", + v0Compatible: true, + module: `package system +import rego.v1 +main contains "hello" if { + input.foo == "bar" +}`, + }, + + { + note: "v1, no keywords used", + v1Compatible: true, + module: `package system +main["hello"] { + input.foo == "bar" +}`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1, no keywords imported", + v1Compatible: true, + module: `package system +main contains "hello" if { + input.foo == "bar" +}`, + }, + { + note: "v1, keywords imported", + v1Compatible: true, + module: `package system +import future.keywords +main contains "hello" if { + input.foo == "bar" +}`, + }, + { + note: "v1, rego.v1 imported", + v1Compatible: true, + module: `package system +import rego.v1 +main contains "hello" if { + input.foo == "bar" +}`, + }, + + // v0 takes precedence over v1 + { + note: "v0+v1, no keywords used", + v0Compatible: true, + v1Compatible: true, + module: `package system +main["hello"] { + input.foo == "bar" +}`, + }, + { + note: "v0+v1, no keywords imported", + v0Compatible: true, + v1Compatible: true, + module: `package system +main contains "hello" if { + input.foo == "bar" +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: string cannot be used for rule name", + }, + }, + { + note: "v0+v1, keywords imported", + v0Compatible: true, + v1Compatible: true, + module: `package system +import future.keywords +main contains "hello" if { + input.foo == "bar" +}`, + }, + { + note: "v0+v1, rego.v1 imported", + v0Compatible: true, + v1Compatible: true, + module: `package system +import rego.v1 +main contains "hello" if { + input.foo == "bar" +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.json": `{"foo": "bar"}`, + } + + test.WithTempFS(files, func(dir string) { + s := sdk_test.MustNewServer( + sdk_test.MockBundle("/bundles/bundle.tar.gz", map[string]string{"test.rego": tc.module}), + sdk_test.RawBundles(true), + ) + + defer s.Stop() + + var buf bytes.Buffer + params := exec.NewParams(&buf) + params.V0Compatible = tc.v0Compatible + params.V1Compatible = tc.v1Compatible + _ = params.OutputFormat.Set("json") + params.ConfigOverrides = []string{ + "services.test.url=" + s.URL(), + "bundles.test.resource=/bundles/bundle.tar.gz", + } + + params.Paths = append(params.Paths, dir) + + if len(tc.expErrs) > 0 { + testLogger := loggingtest.New() + params.Logger = testLogger + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go func(expectedErrors []string) { + err := runExecWithContext(ctx, params) + // Note(philipc): Catch the expected cancellation + // errors, allowing unexpected test failures through. + if err != context.Canceled { + var errs ast.Errors + if errors.As(err, &errs) { + for _, expErr := range expectedErrors { + found := false + for _, e := range errs { + if strings.Contains(e.Error(), expErr) { + found = true + break + } + } + if !found { + t.Errorf("Could not find expected error: %s in %v", expErr, errs) + return + } + } + } else { + t.Error(err) + return + } + } + }(tc.expErrs) + + if !test.Eventually(t, 5*time.Second, func() bool { + for _, expErr := range tc.expErrs { + found := false + for _, e := range testLogger.Entries() { + if strings.Contains(e.Message, expErr) { + found = true + break + } + } + if !found { + return false + } + } + return true + }) { + t.Fatalf("timed out waiting for logged errors:\n\n%v\n\ngot\n\n%v:", tc.expErrs, testLogger.Entries()) + } + } else { + err := runExec(params) + if err != nil { + t.Fatal(err) + } + + var output execOutput + if err := json.Unmarshal(bytes.ReplaceAll(buf.Bytes(), []byte(dir), nil), &output); err != nil { + t.Fatal(err) + } + + resultSliceEquals(t, []execResultItem{ + { + Path: "/test.json", + Result: toAnyPtr([]string{"hello"}), + }, + }, output.Result) + } + }) + }) + } +} + +func TestExecWithBundleRegoVersion(t *testing.T) { + tests := []struct { + note string + files map[string]string + expErrs []string + }{ + { + note: "v0.x bundle, no keywords used", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package system +main["hello"] { + input.foo == "bar" +}`, + }, + }, + { + note: "v0.x bundle, no keywords imported", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package system +main contains "hello" if { + input.foo == "bar" +}`, + }, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: string cannot be used for rule name", + }, + }, + { + note: "v0.x bundle, keywords imported", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package system +import future.keywords +main contains "hello" if { + input.foo == "bar" +}`, + }, + }, + { + note: "v0.x bundle, rego.v1 imported", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package system +import rego.v1 +main contains "hello" if { + input.foo == "bar" +}`, + }, + }, + { + note: "v0 bundle, v1 per-file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } +}`, + "policy1.rego": `package system +p[42] { + input.foo == "bar" +}`, + "policy2.rego": `package system +main contains "hello" if { + 42 in p +}`, + }, + }, + { + note: "v0 bundle, v1 per-file override (glob)", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/policy2.rego": 1 + } +}`, + "policy1.rego": `package system +p[42] { + input.foo == "bar" +}`, + "policy2.rego": `package system +main contains "hello" if { + 42 in p +}`, + }, + }, + { + note: "v0 bundle, v1 per-file override, incompatible", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } +}`, + "policy1.rego": `package system +p[42] { + input.foo == "bar" +}`, + "policy2.rego": `package system +main["hello"] { + p[_] == 42 +}`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + + { + note: "v1.0 bundle, no keywords used", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package system +main["hello"] { + input.foo == "bar" +}`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1.0 bundle, no keywords imported", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package system +main contains "hello" if { + input.foo == "bar" +}`, + }, + }, + { + note: "v1.0 bundle, keywords imported", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package system +import future.keywords +main contains "hello" if { + input.foo == "bar" +}`, + }, + }, + { + note: "v1.0 bundle, rego.v1 imported", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package system +import rego.v1 +main contains "hello" if { + input.foo == "bar" +}`, + }, + }, + { + note: "v1 bundle, v0 per-file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } +}`, + "policy1.rego": `package system +p[42] { + input.foo == "bar" +}`, + "policy2.rego": `package system +main contains "hello" if { + 42 in p +}`, + }, + }, + { + note: "v1 bundle, v0 per-file override (glob)", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "*/policy1.rego": 0 + } +}`, + "policy1.rego": `package system +p[42] { + input.foo == "bar" +}`, + "policy2.rego": `package system +main contains "hello" if { + 42 in p +}`, + }, + }, + { + note: "v1 bundle, v0 per-file override, incompatible", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } +}`, + "policy1.rego": `package system +p contains 42 { + input.foo == "bar" +}`, + "policy2.rego": `package system +main contains "hello" if { + 42 in p +}`, + }, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + "rego_parse_error: set cannot be used for rule name", + }, + }, + } + + bundleTypeCases := []struct { + note string + tar bool + }{ + { + "bundle dir", false, + }, + { + "bundle tar", true, + }, + } + + v1CompatibleFlagCases := []struct { + note string + used bool + }{ + { + "no --v1-compatible", false, + }, + { + "--v1-compatible", true, + }, + } + + for _, bundleType := range bundleTypeCases { + for _, v1CompatibleFlag := range v1CompatibleFlagCases { + for _, tc := range tests { + t.Run(fmt.Sprintf("%s, %s, %s", bundleType.note, v1CompatibleFlag.note, tc.note), func(t *testing.T) { + files := map[string]string{ + "files/test.json": `{"foo": "bar"}`, + } + if bundleType.tar { + files["bundle.tar.gz"] = "" + } else { + maps.Copy(files, tc.files) + } + + test.WithTempFS(files, func(root string) { + p := root + if bundleType.tar { + p = filepath.Join(root, "bundle.tar.gz") + files := make([][2]string, 0, len(tc.files)) + for k, v := range tc.files { + files = append(files, [2]string{k, v}) + } + buf := archive.MustWriteTarGz(files) + bf, err := os.Create(p) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + var buf bytes.Buffer + params := exec.NewParams(&buf) + params.Paths = append(params.Paths, root+"/files/") + params.BundlePaths = []string{p} + params.V1Compatible = v1CompatibleFlag.used + _ = params.OutputFormat.Set("json") + + if len(tc.expErrs) > 0 { + testLogger := loggingtest.New() + params.Logger = testLogger + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go func() { + err := runExecWithContext(ctx, params) + // we cancelled the context, so we expect that error + if err != nil && err.Error() != "context canceled" { + t.Error(err) + return + } + }() + + if !test.Eventually(t, 5*time.Second, func() bool { + for _, expErr := range tc.expErrs { + found := false + for _, e := range testLogger.Entries() { + if strings.Contains(e.Message, expErr) { + found = true + break + } + } + if !found { + return false + } + } + return true + }) { + t.Fatalf("timed out waiting for logged errors:\n\n%v\n\ngot\n\n%v:", tc.expErrs, testLogger.Entries()) + } + } else { + err := runExec(params) + if err != nil { + t.Fatal(err) + } + + var output execOutput + if err := json.Unmarshal(bytes.ReplaceAll(buf.Bytes(), []byte(root), nil), &output); err != nil { + t.Fatal(err) + } + + resultSliceEquals(t, []execResultItem{ + { + Path: "/files/test.json", + Result: toAnyPtr([]string{"hello"}), + }, + }, output.Result) + } + }) + }) + } + } + } +} + +func TestInvalidConfig(t *testing.T) { + var buf bytes.Buffer + params := exec.NewParams(&buf) + params.Fail = true + params.FailDefined = true + + err := exec.Exec(context.TODO(), nil, params) + if err == nil || err.Error() != "specify --fail or --fail-defined but not both" { + t.Fatalf("Expected error '%s' but got '%s'", "specify --fail or --fail-defined but not both", err.Error()) + } +} + +func TestInvalidConfigAllThree(t *testing.T) { + var buf bytes.Buffer + params := exec.NewParams(&buf) + params.Fail = true + params.FailDefined = true + params.FailNonEmpty = true + + err := exec.Exec(context.TODO(), nil, params) + if err == nil || err.Error() != "specify --fail or --fail-defined but not both" { + t.Fatalf("Expected error '%s' but got '%s'", "specify --fail or --fail-defined but not both", err.Error()) + } +} + +func TestInvalidConfigNonEmptyAndFail(t *testing.T) { + var buf bytes.Buffer + params := exec.NewParams(&buf) + params.FailNonEmpty = true + params.Fail = true + + err := exec.Exec(context.TODO(), nil, params) + if err == nil || err.Error() != "specify --fail-non-empty or --fail but not both" { + t.Fatalf("Expected error '%s' but got '%s'", "specify --fail-non-empty or --fail but not both", err.Error()) + } +} + +func TestInvalidConfigNonEmptyAndFailDefined(t *testing.T) { + var buf bytes.Buffer + params := exec.NewParams(&buf) + params.FailNonEmpty = true + params.FailDefined = true + + err := exec.Exec(context.TODO(), nil, params) + if err == nil || err.Error() != "specify --fail-non-empty or --fail-defined but not both" { + t.Fatalf("Expected error '%s' but got '%s'", "specify --fail-non-empty or --fail-defined but not both", err.Error()) + } +} + +func TestFailFlagCases(t *testing.T) { + + var tests = []struct { + description string + files map[string]string + decision string + expectError bool + expected []byte + fail bool + failDefined bool + failNonEmpty bool + }{ + { + description: "--fail-defined with undefined result", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package system + import rego.v1 + + test_fun := x if { + x = false + x + } + + undefined_test if { + test_fun + }`, + }, + expectError: false, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "error": { + "code": "opa_undefined_error", + "message": "/system/main decision was undefined" + } + }]}`), + failDefined: true, + }, + { + description: "--fail-defined with populated result", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package system + import rego.v1 + + main contains "hello"`, + }, + decision: "", + expectError: true, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "result": ["hello"] + }]}`), + failDefined: true, + }, + { + description: "--fail-defined with true boolean result", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package fail.defined.flag + import rego.v1 + + some_function if { + input.foo == 7 + } + + default fail_test := false + fail_test if { + some_function + }`, + }, + decision: "fail/defined/flag/fail_test", + expectError: true, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "result": true + }]}`), + failDefined: true, + }, + { + description: "--fail-defined with false boolean result", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package fail.defined.flag + import rego.v1 + + default fail_test := false + fail_test if { + false + }`, + }, + decision: "fail/defined/flag/fail_test", + expectError: true, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "result": false + }]}`), + failDefined: true, + }, + { + description: "--fail with undefined result", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package system + import rego.v1 + + test_fun := x if { + x = false + x + } + + undefined_test if { + test_fun + }`, + }, + expectError: true, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "error": { + "code": "opa_undefined_error", + "message": "/system/main decision was undefined" + } + }]}`), + fail: true, + }, + { + description: "--fail with populated result", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package system + import rego.v1 + + main contains "hello"`, + }, + expectError: false, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "result": ["hello"] + }]}`), + fail: true, + }, + { + description: "--fail with true boolean result", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package fail.defined.flag + import rego.v1 + + some_function if { + input.foo == 7 + } + + default fail_test := false + fail_test if { + some_function + }`, + }, + decision: "fail/defined/flag/fail_test", + expectError: false, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "result": true + }]}`), + fail: true, + }, + { + description: "--fail with false boolean result", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package fail.defined.flag + import rego.v1 + + default fail_test := false + fail_test if { + false + }`, + }, + decision: "fail/defined/flag/fail_test", + expectError: false, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "result": false + }]}`), + fail: true, + }, + { + description: "--fail-non-empty with undefined result", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package system + import rego.v1 + + test_fun := x if { + x = false + x + } + + undefined_test if { + test_fun + }`, + }, + expectError: false, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "error": { + "code": "opa_undefined_error", + "message": "/system/main decision was undefined" + } + }]}`), + failNonEmpty: true, + }, + { + description: "--fail-non-empty with populated result", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package system + import rego.v1 + + main contains "hello"`, + }, + decision: "", + expectError: true, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "result": ["hello"] + }]}`), + failNonEmpty: true, + }, + { + description: "--fail-non-empty with true boolean result", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package fail.non.empty.flag + import rego.v1 + + some_function if { + input.foo == 7 + } + + default fail_test := false + fail_test if { + some_function + }`, + }, + decision: "fail/non/empty/flag/fail_test", + expectError: true, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "result": true + }]}`), + failNonEmpty: true, + }, + { + description: "--fail-non-empty with false boolean result", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package fail.non.empty.flag + import rego.v1 + + default fail_test := false + fail_test if { + false + }`, + }, + decision: "fail/non/empty/flag/fail_test", + expectError: true, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "result": false + }]}`), + failNonEmpty: true, + }, + { + description: "--fail-non-empty with an empty array", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package fail.non.empty.flag + import rego.v1 + + default fail_test := ["something", "hello"] + fail_test := [] if { + input.foo == 7 + }`, + }, + decision: "fail/non/empty/flag/fail_test", + expectError: false, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "result": [] + }]}`), + failNonEmpty: true, + }, + { + description: "--fail-non-empty for an empty set coming from a partial rule", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package fail.non.empty.flag + import rego.v1 + + fail_test contains message if { + false + message := "not gonna happen" + }`, + }, + decision: "fail/non/empty/flag/fail_test", + expectError: false, + expected: []byte(`{"result": [{ + "path": "/files/test.json", + "result": [] + }]}`), + failNonEmpty: true, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + test.WithTempFS(tt.files, func(dir string) { + var buf bytes.Buffer + params := exec.NewParams(&buf) + _ = params.OutputFormat.Set("json") + params.BundlePaths = []string{dir + "/bundle/"} + params.Paths = append(params.Paths, dir+"/files/") + if tt.decision != "" { + params.Decision = tt.decision + } + params.FailDefined = tt.failDefined + params.Fail = tt.fail + params.FailNonEmpty = tt.failNonEmpty + + err := runExec(params) + if err != nil && !tt.expectError { + t.Fatal("unexpected error in test") + } + if err == nil && tt.expectError { + t.Fatal("expected error, but none occurred in test") + } + + var output execOutput + if err := json.Unmarshal(bytes.ReplaceAll(buf.Bytes(), []byte(dir), nil), &output); err != nil { + t.Fatal(err) + } + + var expected execOutput + if err := json.Unmarshal(tt.expected, &expected); err != nil { + t.Fatal(err) + } + + resultSliceEquals(t, expected.Result, output.Result) + }) + }) + } +} + +func TestExecWithInvalidInputOptions(t *testing.T) { + tests := []struct { + description string + files map[string]string + stdIn bool + input string + expectError bool + expected string + }{ + { + description: "path passed in as arg should not raise error", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package system + import rego.v1 + + test_fun := x if { + x = false + x + } + + undefined_test if { + test_fun + }`, + }, + expectError: false, + expected: "", + }, + { + description: "no paths passed in as args should raise error if --stdin-input flag not set", + files: map[string]string{ + "bundle/x.rego": `package system + import rego.v1 + + test_fun := x if { + x = false + x + } + + undefined_test if { + test_fun + }`, + }, + expectError: true, + expected: "requires at least 1 path arg, or the --stdin-input flag", + }, + { + description: "should not raise error if --stdin-input flag is set when no paths passed in as args", + files: map[string]string{ + "bundle/x.rego": `package system + import rego.v1 + + test_fun := x if { + x = false + x + } + + undefined_test if { + test_fun + }`, + }, + stdIn: true, + input: `{"foo": 7}`, + expectError: false, + expected: "", + }, + } + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + test.WithTempFS(tt.files, func(dir string) { + var buf bytes.Buffer + params := exec.NewParams(&buf) + _ = params.OutputFormat.Set("json") + params.BundlePaths = []string{dir + "/bundle/"} + if tt.stdIn { + params.StdIn = true + tempFile, err := os.CreateTemp(t.TempDir(), "test") + if err != nil { + t.Fatalf("unexpected error creating temp file: %q", err.Error()) + } + if _, err := tempFile.WriteString(tt.input); err != nil { + t.Fatalf("unexpeced error when writing to temp file: %q", err.Error()) + } + if _, err := tempFile.Seek(0, 0); err != nil { + t.Fatalf("unexpected error when rewinding temp file: %q", err.Error()) + } + oldStdin := os.Stdin + defer func() { + os.Stdin = oldStdin + os.Remove(tempFile.Name()) + }() + os.Stdin = tempFile + } else { + if _, ok := tt.files["files/test.json"]; ok { + params.Paths = append(params.Paths, dir+"/files/") + } + } + + err := runExec(params) + if err != nil && !tt.expectError { + t.Fatalf("unexpected error in test: %q", err.Error()) + } + if err == nil && tt.expectError { + t.Fatalf("expected error %q, but none occurred in test", tt.expected) + } + if err != nil && err.Error() != tt.expected { + t.Fatalf("expected error %q, but got %q", tt.expected, err.Error()) + } + }) + }) + } +} + +func TestExecTimeoutWithMalformedRemoteBundle(t *testing.T) { + test.WithTempFS(map[string]string{}, func(dir string) { + // Note(philipc): We add the "raw bundles" flag so that we can stuff a + // malformed bundle into the mock bundle server. Otherwise, the server + // will just return 503 errors forever, because it won't be able to + // build the bundle on its end. + s := sdk_test.MustNewServer( + sdk_test.RawBundles(true), + sdk_test.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "example.rego": ` + package example + + p := bits.sand(42, 43) # typo of bits.and + `, + })) + + defer s.Stop() + + var buf bytes.Buffer + params := exec.NewParams(&buf) + _ = params.OutputFormat.Set("json") + params.ConfigOverrides = []string{ + "services.test.url=" + s.URL(), + "bundles.test.resource=/bundles/bundle.tar.gz", + } + + // Note(philipc): We can set this timeout almost arbitrarily high or + // low-- the test will time out before it ever succeeds, due to the + // faulty bundle. + params.Timeout = time.Millisecond * 50 + + params.Paths = append(params.Paths, dir) + err := runExec(params) + if err == nil { + t.Fatalf("Expected error, got nil instead.") + } + + exp := "exec error: timed out before OPA was ready." + if !strings.HasPrefix(err.Error(), exp) { + t.Fatalf("Expected error: %s, got %s", exp, err.Error()) + } + }) +} + +func TestExecStopsPlugins(t *testing.T) { + fact := &factory{} + + sdk.SetDefaultOptions(sdk.Options{ + Plugins: map[string]plugins.Factory{ + "test_plugin": fact, + }, + }) + + s := sdk_test.MustNewServer(sdk_test.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "test.rego": ` + package system + main contains "hello" + `, + })) + defer s.Stop() + + cfg := filepath.Join(t.TempDir(), "opa.yaml") + if err := os.WriteFile(cfg, []byte(` +plugins: + test_plugin: {} +`), 0x777); err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + params := exec.NewParams(&buf) + params.ConfigFile = cfg + _ = params.OutputFormat.Set("json") + params.ConfigOverrides = []string{ + "services.test.url=" + s.URL(), + "bundles.test.resource=/bundles/bundle.tar.gz", + } + + dir := t.TempDir() + params.Paths = append(params.Paths, dir) + + err := runExec(params) + if err != nil { + t.Fatal(err) + } + + if !fact.stopped { + t.Errorf("expected plugin to be stopped") + } +} + +type factory struct { + stopped bool + m *plugins.Manager +} + +func (f *factory) New(m *plugins.Manager, _ any) plugins.Plugin { + f.m = m + return f +} + +func (*factory) Validate(*plugins.Manager, []byte) (any, error) { + return nil, nil +} + +func (f *factory) Start(context.Context) error { + f.m.UpdatePluginStatus("test_plugin", &plugins.Status{State: plugins.StateOK}) + return nil +} + +func (f *factory) Stop(context.Context) { + f.stopped = true +} + +func (*factory) Reconfigure(context.Context, any) { +} diff --git a/third_party/opa/cmd/features.go b/third_party/opa/cmd/features.go new file mode 100644 index 000000000000..379abd39216c --- /dev/null +++ b/third_party/opa/cmd/features.go @@ -0,0 +1,10 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build opa_wasm +// +build opa_wasm + +package cmd + +import _ "github.com/open-policy-agent/opa/v1/features/wasm" diff --git a/third_party/opa/cmd/filters.go b/third_party/opa/cmd/filters.go new file mode 100644 index 000000000000..29cfa4113081 --- /dev/null +++ b/third_party/opa/cmd/filters.go @@ -0,0 +1,39 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "os" + "path/filepath" + + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/loader" +) + +type loaderFilter struct { + Ignore []string + OnlyRego bool +} + +func (f loaderFilter) Apply(abspath string, info os.FileInfo, depth int) bool { + // if set to only load rego files, skip all non-rego files + if f.OnlyRego && !info.IsDir() && filepath.Ext(info.Name()) != bundle.RegoExt { + return true + } + for _, s := range f.Ignore { + if loader.GlobExcludeName(s, 1)(abspath, info, depth) { + return true + } + } + return false +} + +func ignored(ignore []string) loaderFilter { + return loaderFilter{Ignore: ignore} +} + +func ignoredOnlyRego(ignore []string) loaderFilter { + return loaderFilter{Ignore: ignore, OnlyRego: true} +} diff --git a/third_party/opa/cmd/flags.go b/third_party/opa/cmd/flags.go new file mode 100644 index 000000000000..dcaebdb6e958 --- /dev/null +++ b/third_party/opa/cmd/flags.go @@ -0,0 +1,255 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + + "github.com/spf13/pflag" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + explainModeOff = "off" + explainModeFull = "full" + explainModeNotes = "notes" + explainModeFails = "fails" + explainModeDebug = "debug" + + stringType = "string" +) + +func addConfigFileFlag(fs *pflag.FlagSet, file *string) { + fs.StringVarP(file, "config-file", "c", "", "set path of configuration file") +} + +func addConfigOverrides(fs *pflag.FlagSet, overrides *[]string) { + fs.StringArrayVar(overrides, "set", []string{}, "override config values on the command line (use commas to specify multiple values)") +} + +func addConfigOverrideFiles(fs *pflag.FlagSet, overrides *[]string) { + fs.StringArrayVar(overrides, "set-file", []string{}, "override config values with files on the command line (use commas to specify multiple values)") +} + +func addFailFlag(fs *pflag.FlagSet, fail *bool, value bool) { + fs.BoolVarP(fail, "fail", "", value, "exits with non-zero exit code on undefined/empty result and errors") +} + +func addDataFlag(fs *pflag.FlagSet, paths *repeatedStringFlag) { + fs.VarP(paths, "data", "d", "set policy or data file(s). This flag can be repeated.") +} + +func addBundleFlag(fs *pflag.FlagSet, paths *repeatedStringFlag) { + fs.VarP(paths, "bundle", "b", "set bundle file(s) or directory path(s). This flag can be repeated.") +} + +func addBundleModeFlag(fs *pflag.FlagSet, bundle *bool, value bool) { + fs.BoolVarP(bundle, "bundle", "b", value, "load paths as bundle files or root directories") +} + +func addInputFlag(fs *pflag.FlagSet, inputPath *string) { + fs.StringVarP(inputPath, "input", "i", "", "set input file path") +} + +func addImportFlag(fs *pflag.FlagSet, imports *repeatedStringFlag) { + fs.VarP(imports, "import", "", "set query import(s). This flag can be repeated.") +} + +func addPackageFlag(fs *pflag.FlagSet, pkg *string) { + fs.StringVarP(pkg, "package", "", "", "set query package") +} + +func addQueryStdinFlag(fs *pflag.FlagSet, stdin *bool) { + fs.BoolVarP(stdin, "stdin", "", false, "read query from stdin") +} + +func addInputStdinFlag(fs *pflag.FlagSet, stdinInput *bool) { + fs.BoolVarP(stdinInput, "stdin-input", "I", false, "read input document from stdin") +} + +func addMetricsFlag(fs *pflag.FlagSet, metrics *bool, value bool) { + fs.BoolVarP(metrics, "metrics", "", value, "report query performance metrics") +} + +func addOutputFormat(fs *pflag.FlagSet, outputFormat *util.EnumFlag) { + fs.VarP(outputFormat, "format", "f", "set output format") +} + +func addListAnnotations(fs *pflag.FlagSet, value *bool) { + fs.BoolVarP(value, "annotations", "a", false, "list annotations") +} + +func addBenchmemFlag(fs *pflag.FlagSet, benchMem *bool, value bool) { + fs.BoolVar(benchMem, "benchmem", value, "report memory allocations with benchmark results") +} + +func addCountFlag(fs *pflag.FlagSet, count *int, cmdType string) { + fs.IntVar(count, "count", 1, "number of times to repeat each "+cmdType) +} + +func addMaxErrorsFlag(fs *pflag.FlagSet, errLimit *int) { + fs.IntVarP(errLimit, "max-errors", "m", ast.CompileErrorLimitDefault, "set the number of errors to allow before compilation fails early") +} + +func addIgnoreFlag(fs *pflag.FlagSet, ignoreNames *[]string) { + fs.StringSliceVarP(ignoreNames, "ignore", "", []string{}, "set file and directory names to ignore during loading (e.g., '.*' excludes hidden files)") +} + +func addSigningAlgFlag(fs *pflag.FlagSet, alg *string, value string) { + fs.StringVarP(alg, "signing-alg", "", value, "name of the signing algorithm") +} + +func addClaimsFileFlag(fs *pflag.FlagSet, file *string) { + fs.StringVarP(file, "claims-file", "", "", "set path of JSON file containing optional claims (see: https://www.openpolicyagent.org/docs/latest/management-bundles/#signature-format)") +} + +func addSigningKeyFlag(fs *pflag.FlagSet, key *string) { + fs.StringVarP(key, "signing-key", "", "", "set the secret (HMAC) or path of the PEM file containing the private key (RSA and ECDSA)") +} + +func addSigningPluginFlag(fs *pflag.FlagSet, plugin *string) { + fs.StringVarP(plugin, "signing-plugin", "", "", "name of the plugin to use for signing/verification (see https://www.openpolicyagent.org/docs/latest/management-bundles/#signature-plugin)") +} + +func addVerificationKeyFlag(fs *pflag.FlagSet, key *string) { + fs.StringVarP(key, "verification-key", "", "", "set the secret (HMAC) or path of the PEM file containing the public key (RSA and ECDSA)") +} + +func addVerificationKeyIDFlag(fs *pflag.FlagSet, keyID *string, value string) { + fs.StringVarP(keyID, "verification-key-id", "", value, "name assigned to the verification key used for bundle verification") +} + +func addBundleVerificationScopeFlag(fs *pflag.FlagSet, scope *string) { + fs.StringVarP(scope, "scope", "", "", "scope to use for bundle signature verification") +} + +func addBundleVerificationSkipFlag(fs *pflag.FlagSet, skip *bool, value bool) { + fs.BoolVarP(skip, "skip-verify", "", value, "disables bundle signature verification") +} + +func addBundleVerificationExcludeFilesFlag(fs *pflag.FlagSet, excludeNames *[]string) { + fs.StringSliceVarP(excludeNames, "exclude-files-verify", "", []string{}, "set file names to exclude during bundle verification") +} + +func addCapabilitiesFlag(fs *pflag.FlagSet, f *capabilitiesFlag) { + fs.VarP(f, "capabilities", "", "set capabilities version or capabilities.json file path") +} + +func addPartialFlag(fs *pflag.FlagSet, partial *bool, value bool) { + fs.BoolVarP(partial, "partial", "p", value, "perform partial evaluation") +} + +func addUnknownsFlag(fs *pflag.FlagSet, unknowns *[]string, value []string) { + fs.StringArrayVarP(unknowns, "unknowns", "u", value, "set paths to treat as unknown during partial evaluation") +} + +type schemaFlags struct { + path string +} + +func addSchemaFlags(fs *pflag.FlagSet, schema *schemaFlags) { + fs.StringVarP(&schema.path, "schema", "s", "", "set schema file path or directory path") +} + +func addTargetFlag(fs *pflag.FlagSet, target *util.EnumFlag) { + fs.VarP(target, "target", "t", "set the runtime to exercise") +} + +func addStrictFlag(fs *pflag.FlagSet, strict *bool, value bool) { + fs.BoolVarP(strict, "strict", "S", value, "enable compiler strict mode") +} + +func addRegoV0V1FlagWithDescription(fs *pflag.FlagSet, regoV1 *bool, value bool, description string) { + fs.BoolVar(regoV1, "v0-v1", value, description) + + // For backwards compatibility + fs.BoolVar(regoV1, "rego-v1", value, description) + _ = fs.MarkHidden("rego-v1") +} + +func addV0CompatibleFlag(fs *pflag.FlagSet, v1Compatible *bool, value bool) { + fs.BoolVar(v1Compatible, "v0-compatible", value, "opt-in to OPA features and behaviors prior to the OPA v1.0 release") +} + +func addV1CompatibleFlag(fs *pflag.FlagSet, v1Compatible *bool, value bool) { + fs.BoolVar(v1Compatible, "v1-compatible", value, "opt-in to OPA features and behaviors that are enabled by default in OPA v1.0") + _ = fs.MarkHidden("v1-compatible") +} + +func addReadAstValuesFromStoreFlag(fs *pflag.FlagSet, readAstValuesFromStore *bool, value bool) { + fs.BoolVar(readAstValuesFromStore, "optimize-store-for-read-speed", value, "optimize default in-memory store for read speed. Has possible negative impact on memory footprint and write speed. See https://www.openpolicyagent.org/docs/latest/policy-performance/#storage-optimization for more details.") +} + +func addE2EFlag(fs *pflag.FlagSet, e2e *bool, value bool, brand string) { + fs.BoolVar(e2e, "e2e", value, "run benchmarks against a running "+brand+" server") +} + +func newExplainFlag(modes []string) *util.EnumFlag { + return util.NewEnumFlag(modes[0], modes) +} + +func setExplainFlag(fs *pflag.FlagSet, explain *util.EnumFlag) { + fs.VarP(explain, "explain", "", "enable query explanations") +} + +type capabilitiesFlag struct { + C *ast.Capabilities + pathOrVersion string +} + +func newCapabilitiesFlag() *capabilitiesFlag { + return &capabilitiesFlag{ + // cannot call ast.CapabilitiesForThisVersion here because + // custom builtins cannot be registered by this point in execution + C: nil, + } +} + +func (*capabilitiesFlag) Type() string { + return stringType +} + +func (f *capabilitiesFlag) String() string { + return f.pathOrVersion +} + +func (f *capabilitiesFlag) Set(s string) error { + f.pathOrVersion = s + var errPath, errVersion error + + f.C, errPath = ast.LoadCapabilitiesFile(s) + if errPath != nil { + f.C, errVersion = ast.LoadCapabilitiesVersion(s) + } + + if errVersion != nil && errPath != nil { + return fmt.Errorf("no such file or capabilities version found: %v", s) + } + return nil +} + +type stringptrFlag struct { + v *string + isSet bool +} + +func (*stringptrFlag) Type() string { + return stringType +} + +func (f *stringptrFlag) String() string { + if f.v == nil { + return "" + } + return *f.v +} + +func (f *stringptrFlag) Set(s string) error { + f.v = &s + f.isSet = true + return nil +} diff --git a/third_party/opa/cmd/fmt.go b/third_party/opa/cmd/fmt.go new file mode 100644 index 000000000000..9e957336625f --- /dev/null +++ b/third_party/opa/cmd/fmt.go @@ -0,0 +1,319 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "bytes" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/sergi/go-diff/diffmatchpatch" + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/internal/env" + fileurl "github.com/open-policy-agent/opa/internal/file/url" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/format" +) + +type fmtCommandParams struct { + overwrite bool + list bool + diff bool + fail bool + regoV1 bool + v0Compatible bool + v1Compatible bool + checkResult bool + dropV0Imports bool + capabilitiesFlag *capabilitiesFlag +} + +func newFmtCommandParams() *fmtCommandParams { + return &fmtCommandParams{ + capabilitiesFlag: newCapabilitiesFlag(), + } +} + +func (p *fmtCommandParams) capabilities() *ast.Capabilities { + if p.capabilitiesFlag != nil && p.capabilitiesFlag.C != nil { + return p.capabilitiesFlag.C + } + return ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(p.regoVersion())) +} + +func (p *fmtCommandParams) regoVersion() ast.RegoVersion { + // The '--rego-v1' flag takes precedence over the '--v1-compatible' flag. + if p.regoV1 { + return ast.RegoV0CompatV1 + } + // The '--v0-compatible' flag takes precedence over the '--v1-compatible' flag. + if p.v0Compatible { + return ast.RegoV0 + } + if p.v1Compatible { + return ast.RegoV1 + } + return ast.DefaultRegoVersion +} + +func opaFmt(args []string, fmtParams *fmtCommandParams) int { + if len(args) == 0 { + if err := formatStdin(fmtParams, os.Stdin, os.Stdout); err != nil { + fmt.Fprintln(os.Stderr, err) + return 1 + } + return 0 + } + + for _, filename := range args { + + var err error + filename, err = fileurl.Clean(filename) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return 1 + } + err = filepath.Walk(filename, func(path string, info os.FileInfo, err error) error { + return formatFile(fmtParams, os.Stdout, path, info, err) + }) + if err != nil { + switch err := err.(type) { + case fmtError: + fmt.Fprintln(os.Stderr, err.msg) + return err.code + default: + fmt.Fprintln(os.Stderr, err.Error()) + return 1 + } + } + } + + return 0 +} + +func formatFile(params *fmtCommandParams, out io.Writer, filename string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + if filepath.Ext(filename) != ".rego" { + return nil + } + + contents, err := os.ReadFile(filename) + if err != nil { + return newError("failed to open file: %v", err) + } + + opts := format.Opts{ + RegoVersion: params.regoVersion(), + DropV0Imports: params.dropV0Imports, + Capabilities: params.capabilities(), + } + + if params.regoV1 { + opts.ParserOptions = &ast.ParserOptions{RegoVersion: ast.RegoV0} + } + + if params.v0Compatible { + // v0 takes precedence over v1 + opts.ParserOptions = &ast.ParserOptions{RegoVersion: ast.RegoV0} + } else if params.v1Compatible { + opts.ParserOptions = &ast.ParserOptions{RegoVersion: ast.RegoV1} + } + + formatted, err := format.SourceWithOpts(filename, contents, opts) + if err != nil { + return newError("failed to format Rego source file: %v", err) + } + + if params.checkResult { + popts := ast.ParserOptions{RegoVersion: params.regoVersion()} + _, err := ast.ParseModuleWithOpts("formatted", string(formatted), popts) + if err != nil { + return newError("%s was successfully formatted, but the result is invalid: %v\n\nTo inspect the formatted Rego, you can turn off this check with --check-result=false.", filename, err) + } + } + + changed := !bytes.Equal(contents, formatted) + + if params.fail && !params.list && !params.diff { + if changed { + return newError("unexpected diff") + } + } + + if params.list { + if changed { + fmt.Fprintln(out, filename) + + if params.fail { + return newError("unexpected diff") + } + } + return nil + } + + if params.diff { + if changed { + diffString := doDiff(contents, formatted) + if _, err := fmt.Fprintln(out, diffString); err != nil { + return newError("failed to print contents: %v", err) + } + if params.fail { + return newError("unexpected diff") + } + } + return nil + } + + if params.overwrite { + outfile, err := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC, info.Mode().Perm()) + if err != nil { + return newError("failed to open file for writing: %v", err) + } + defer outfile.Close() + out = outfile + } + + _, err = out.Write(formatted) + if err != nil { + return newError("failed writing formatted contents: %v", err) + } + + return nil +} + +func formatStdin(params *fmtCommandParams, r io.Reader, w io.Writer) error { + contents, err := io.ReadAll(r) + if err != nil { + return err + } + + opts := format.Opts{ + RegoVersion: params.regoVersion(), + Capabilities: params.capabilities(), + } + + if params.regoV1 { + opts.ParserOptions = &ast.ParserOptions{RegoVersion: ast.RegoV0} + } + + if params.v0Compatible { + // v0 takes precedence over v1 + opts.ParserOptions = &ast.ParserOptions{RegoVersion: ast.RegoV0} + } else if params.v1Compatible { + opts.ParserOptions = &ast.ParserOptions{RegoVersion: ast.RegoV1} + } + + formatted, err := format.SourceWithOpts("stdin", contents, opts) + if err != nil { + return err + } + + _, err = w.Write(formatted) + return err +} + +func doDiff(a, b []byte) (diffString string) { // "a" is old, "b" is new + dmp := diffmatchpatch.New() + diffs := dmp.DiffMain(string(a), string(b), false) + return dmp.DiffPrettyText(diffs) +} + +type fmtError struct { + msg string + code int +} + +func (e fmtError) Error() string { + return fmt.Sprintf("%s (%d)", e.msg, e.code) +} + +func newError(msg string, a ...any) fmtError { + return fmtError{ + msg: fmt.Sprintf(msg, a...), + code: 2, + } +} + +func initFmt(root *cobra.Command, _ string) { + cmd := root.Name() + fmtParams := newFmtCommandParams() + formatCommand := &cobra.Command{ + Use: "fmt [path [...]]", + Short: "Format Rego source files", + Long: `Format Rego source files. + +The 'fmt' command takes a Rego source file and outputs a reformatted version. If no file path +is provided - this tool will use stdin. +The format of the output is not defined specifically; whatever this tool outputs +is considered correct format (with the exception of bugs). + +If the '-w' option is supplied, the 'fmt' command will overwrite the source file +instead of printing to stdout. + +If the '-d' option is supplied, the 'fmt' command will output a diff between the +original and formatted source. + +If the '-l' option is supplied, the 'fmt' command will output the names of files +that would change if formatted. The '-l' option will suppress any other output +to stdout from the 'fmt' command. + +If the '--fail' option is supplied, the 'fmt' command will return a non zero exit +code if a file would be reformatted. + +The 'fmt' command can be run in several compatibility modes for consuming and outputting +different Rego versions: + +* ` + "`" + cmd + ` fmt` + "`" + `: + * v1 Rego is formatted to v1 + * ` + "`" + `rego.v1` + "`" + `/` + "`" + `future.keywords` + "`" + ` imports are NOT removed + * ` + "`" + `rego.v1` + "`" + `/` + "`" + `future.keywords` + "`" + ` imports are NOT added if missing + * v0 rego is rejected +* ` + "`" + cmd + ` fmt --v0-compatible` + "`" + `: + * v0 Rego is formatted to v0 + * v1 Rego is rejected +* ` + "`" + cmd + ` fmt --v0-v1` + "`" + `: + * v0 Rego is formatted to be compatible with v0 AND v1 + * v1 Rego is rejected +* ` + "`" + cmd + ` fmt --v0-v1 --v1-compatible` + "`" + `: + * v1 Rego is formatted to be compatible with v0 AND v1 + * v0 Rego is rejected +`, + PreRunE: func(cmd *cobra.Command, _ []string) error { + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + exit := opaFmt(args, fmtParams) + if exit != 0 { + return newExitError(exit) + } + return nil + }, + } + + formatCommand.Flags().BoolVarP(&fmtParams.overwrite, "write", "w", false, "overwrite the original source file") + formatCommand.Flags().BoolVarP(&fmtParams.list, "list", "l", false, "list all files who would change when formatted") + formatCommand.Flags().BoolVarP(&fmtParams.diff, "diff", "d", false, "only display a diff of the changes") + formatCommand.Flags().BoolVar(&fmtParams.fail, "fail", false, "non zero exit code on reformat") + addRegoV0V1FlagWithDescription(formatCommand.Flags(), &fmtParams.regoV1, false, "format module(s) to be compatible with both Rego v0 and v1") + addV0CompatibleFlag(formatCommand.Flags(), &fmtParams.v0Compatible, false) + addV1CompatibleFlag(formatCommand.Flags(), &fmtParams.v1Compatible, false) + formatCommand.Flags().BoolVar(&fmtParams.checkResult, "check-result", true, "assert that the formatted code is valid and can be successfully parsed") + formatCommand.Flags().BoolVar(&fmtParams.dropV0Imports, "drop-v0-imports", false, "drop v0 imports from the formatted code, such as 'rego.v1' and 'future.keywords'") + addCapabilitiesFlag(formatCommand.Flags(), fmtParams.capabilitiesFlag) + + root.AddCommand(formatCommand) +} diff --git a/third_party/opa/cmd/fmt_test.go b/third_party/opa/cmd/fmt_test.go new file mode 100644 index 000000000000..721e8201d56f --- /dev/null +++ b/third_party/opa/cmd/fmt_test.go @@ -0,0 +1,1412 @@ +package cmd + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/format" + "github.com/open-policy-agent/opa/v1/util/test" +) + +const formattedV0 = `package test + +p { + a == 1 + true + 1 + 3 +} +` + +const formattedV1 = `package test + +p if { + a == 1 + true + 1 + 3 +} +` + +const unformattedV0 = ` + package test + + p { a == 1; true + 1 + 3 + } + + +` + +const unformattedV1 = ` + package test + + p if{ a == 1; true + 1 + 3 + } + + +` + +const singleWrongArity = `package test +import rego.v1 + +p if { + a := 1 + b := 2 + plus(a, b, c) == 3 +} +` + +const MultipleWrongArity = `package test +import rego.v1 + +p if { + x:=5 + y:=7 + z:=6 + plus([x, y]) == 3 + and(true, false, false) == false + plus(a, x, y, z) +} +` + +const ComprehensionCommentShouldNotMoveFormatted = `package test + +f(x) := [x | + some v in x + + # regal ignore:external-reference + x in data.foo +][0] +` + +const ComprehensionCommentShouldNotMoveUnformatted = `package test + +f(x) := [x | + some v in x + # regal ignore:external-reference + x in data.foo +][0] +` + +type errorWriter struct { + ErrMsg string +} + +func (ew errorWriter) Write([]byte) (int, error) { + return 0, errors.New(ew.ErrMsg) +} + +func TestFmtFormatFile(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + unformatted string + formatted string + }{ + { + note: "v0", + params: fmtCommandParams{v0Compatible: true}, + unformatted: unformattedV0, + formatted: formattedV0, + }, + { + note: "v1", + params: fmtCommandParams{}, + unformatted: unformattedV1, + formatted: formattedV1, + }, + { + note: "comment in comprehension", + params: fmtCommandParams{}, + unformatted: ComprehensionCommentShouldNotMoveUnformatted, + formatted: ComprehensionCommentShouldNotMoveFormatted, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + var stdout bytes.Buffer + + files := map[string]string{ + "policy.rego": tc.unformatted, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(&tc.params, &stdout, policyFile, info, err) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + actual := stdout.String() + if actual != tc.formatted { + t.Fatalf("Expected:\n%s\n\nGot:\n%s\n\n", tc.formatted, actual) + } + }) + }) + } +} + +func TestFmtFormatFileFailToReadFile(t *testing.T) { + + params := fmtCommandParams{ + diff: true, + } + + var stdout = bytes.Buffer{} + + files := map[string]string{ + "policy.rego": unformattedV0, + } + + notThere := "notThere.rego" + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(¶ms, &stdout, notThere, info, err) + if err == nil { + t.Fatalf("Expected error, found none") + } + + actual := err.Error() + + if !strings.Contains(actual, notThere) { + t.Fatalf("Expected error message to include %s, got:\n%s\n\n", notThere, actual) + } + }) +} + +func TestFmtFormatFileNoChanges(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + module string + }{ + { + note: "v0", + params: fmtCommandParams{v0Compatible: true}, + module: formattedV0, + }, + { + note: "v1", + params: fmtCommandParams{}, + module: formattedV1, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + var stdout bytes.Buffer + + files := map[string]string{ + "policy.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(&tc.params, &stdout, policyFile, info, err) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + actual := stdout.String() + if actual != tc.module { + t.Fatalf("Expected:%s\n\nGot:\n%s\n\n", tc.module, actual) + } + }) + }) + } +} + +func TestFmtFailFormatFileNoChanges(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + module string + }{ + { + note: "v0", + params: fmtCommandParams{ + v0Compatible: true, + fail: true, + diff: true, + }, + module: formattedV0, + }, + { + note: "v1", + params: fmtCommandParams{ + fail: true, + diff: true, + }, + module: formattedV1, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + var stdout bytes.Buffer + + files := map[string]string{ + "policy.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(&tc.params, &stdout, policyFile, info, err) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + actual := stdout.String() + if len(actual) > 0 { + t.Fatalf("Expected no output, got:\n%v\n\n", actual) + } + }) + }) + } +} + +func TestFmtFormatFileDiff(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + module string + }{ + { + note: "v0", + params: fmtCommandParams{ + v0Compatible: true, + diff: true, + }, + module: formattedV0, + }, + { + note: "v1", + params: fmtCommandParams{ + diff: true, + }, + module: formattedV1, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + var stdout bytes.Buffer + + files := map[string]string{ + "policy.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(&tc.params, &stdout, policyFile, info, err) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + actual := stdout.String() + + if len(actual) > 0 { + t.Fatalf("Expected no output, got:\n%s\n\n", actual) + } + }) + }) + } +} + +func TestFmtFormatFileFailToPrintDiff(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + module string + }{ + { + note: "v0", + params: fmtCommandParams{ + v0Compatible: true, + diff: true, + }, + module: unformattedV0, + }, + { + note: "v1", + params: fmtCommandParams{ + diff: true, + }, + module: unformattedV1, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + errMsg := "io.Write error" + var stdout = errorWriter{ErrMsg: errMsg} + + files := map[string]string{ + "policy.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(&tc.params, &stdout, policyFile, info, err) + if err == nil { + t.Fatalf("Expected error, found none") + } + + actual := err.Error() + + if !strings.Contains(actual, errMsg) { + t.Fatalf("Expected error message to include %s, got:\n%s\n\n", errMsg, actual) + } + }) + }) + } +} + +func TestFmtFormatFileList(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + module string + }{ + { + note: "v0", + params: fmtCommandParams{ + v0Compatible: true, + list: true, + }, + module: formattedV0, + }, + { + note: "v1", + params: fmtCommandParams{ + list: true, + }, + module: formattedV1, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + var stdout bytes.Buffer + + files := map[string]string{ + "policy.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(&tc.params, &stdout, policyFile, info, err) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + actual := strings.TrimSpace(stdout.String()) + + if len(actual) > 0 { + t.Fatalf("Expected no output, got:\n%s\n\n", actual) + } + }) + }) + } +} + +func TestFmtFailFormatFileList(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + module string + }{ + { + note: "v0", + params: fmtCommandParams{ + v0Compatible: true, + fail: true, + list: true, + }, + module: formattedV0, + }, + { + note: "v1", + params: fmtCommandParams{ + fail: true, + list: true, + }, + module: formattedV1, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + var stdout bytes.Buffer + + files := map[string]string{ + "policy.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(&tc.params, &stdout, policyFile, info, err) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + actual := strings.TrimSpace(stdout.String()) + if len(actual) > 0 { + t.Fatalf("Expected no output, got:\n%v\n\n", actual) + } + }) + }) + } +} + +func TestFmtFailFormatFileChangesList(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + module string + }{ + { + note: "v0", + params: fmtCommandParams{ + v0Compatible: true, + fail: true, + list: true, + }, + module: unformattedV0, + }, + { + note: "v1", + params: fmtCommandParams{ + fail: true, + list: true, + }, + module: unformattedV1, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + var stdout bytes.Buffer + + files := map[string]string{ + "policy.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(&tc.params, &stdout, policyFile, info, err) + if err == nil { + t.Fatalf("Unexpected error: %v", err) + } + + actual := strings.TrimSpace(stdout.String()) + if len(actual) == 0 { + t.Fatalf("Expected output, got:\n%v\n\n", actual) + } + }) + }) + } +} + +func TestFmtFailFileNoChanges(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + module string + }{ + { + note: "v0", + params: fmtCommandParams{ + v0Compatible: true, + fail: true, + }, + module: formattedV0, + }, + { + note: "v1", + params: fmtCommandParams{ + fail: true, + }, + module: formattedV1, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "policy.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(&tc.params, io.Discard, policyFile, info, err) + if err != nil { + t.Fatalf("Expected error but did not receive one") + } + }) + }) + } +} + +func TestFmtFailFileChanges(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + module string + }{ + { + note: "v0", + params: fmtCommandParams{ + v0Compatible: true, + fail: true, + }, + module: unformattedV0, + }, + { + note: "v1", + params: fmtCommandParams{ + fail: true, + }, + module: unformattedV1, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "policy.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(&tc.params, io.Discard, policyFile, info, err) + if err == nil { + t.Fatalf("Unexpected error: %s", err) + } + }) + }) + } +} + +func TestFmtFailFileChangesDiff(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + module string + }{ + { + note: "v0", + params: fmtCommandParams{ + v0Compatible: true, + diff: true, + fail: true, + }, + module: unformattedV0, + }, + { + note: "v1", + params: fmtCommandParams{ + diff: true, + fail: true, + }, + module: unformattedV1, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + var stdout bytes.Buffer + + files := map[string]string{ + "policy.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(&tc.params, &stdout, policyFile, info, err) + if err == nil { + t.Fatalf("Unexpected error: %v", err) + } + + actual := strings.TrimSpace(stdout.String()) + if len(actual) == 0 { + t.Fatalf("Expected output, got:\n%v\n\n", actual) + } + }) + }) + } +} + +func TestFmtSingleWrongArityError(t *testing.T) { + params := fmtCommandParams{} + var stdout bytes.Buffer + + files := map[string]string{ + "policy.rego": singleWrongArity, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(¶ms, &stdout, policyFile, info, err) + if err == nil { + t.Fatalf("Expected error but did not receive one") + } + + loc := ast.Location{File: policyFile, Row: 7} + errExp := ast.NewError(ast.TypeErr, &loc, "%s: %s", "plus", "arity mismatch") + errExp.Details = &format.ArityFormatErrDetail{ + Have: []string{"var", "var", "var"}, + Want: []string{"number", "number"}, + } + expectedErrs := ast.Errors(make([]*ast.Error, 1)) + expectedErrs[0] = errExp + expectedSingleWrongArityErr := newError("failed to format Rego source file: %v", fmt.Errorf("%s: %v", policyFile, expectedErrs)) + + if err != expectedSingleWrongArityErr { + t.Fatalf("Expected:%s\n\nGot:%s\n\n", expectedSingleWrongArityErr, err) + } + }) +} + +func TestFmtMultipleWrongArityError(t *testing.T) { + params := fmtCommandParams{} + var stdout bytes.Buffer + + files := map[string]string{ + "policy.rego": MultipleWrongArity, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(¶ms, &stdout, policyFile, info, err) + if err == nil { + t.Fatalf("Expected error but did not receive one") + } + + locations := []ast.Location{ + {File: policyFile, Row: 8}, + {File: policyFile, Row: 9}, + {File: policyFile, Row: 10}, + } + haveStrings := [][]string{ + {"array"}, + {"boolean", "boolean", "boolean"}, + {"var", "var", "var", "var"}, + } + wantStrings := [][]string{ + {"number", "number"}, + {"set[any]", "set[any]"}, + {"number", "number"}, + } + operators := []string{ + "plus", + "and", + "plus", + } + expectedErrs := ast.Errors(make([]*ast.Error, 3)) + for i := range 3 { + loc := locations[i] + errExp := ast.NewError(ast.TypeErr, &loc, "%s: %s", operators[i], "arity mismatch") + errExp.Details = &format.ArityFormatErrDetail{ + Have: haveStrings[i], + Want: wantStrings[i], + } + expectedErrs[i] = errExp + } + expectedMultipleWrongArityErr := newError("failed to format Rego source file: %v", fmt.Errorf("%s: %v", policyFile, expectedErrs)) + + if err != expectedMultipleWrongArityErr { + t.Fatalf("Expected:%s\n\nGot:%s\n\n", expectedMultipleWrongArityErr, err) + } + }) +} + +func TestFmtRegoV1(t *testing.T) { + tests := []struct { + note string + v1Compatible bool + input string + expected string + expectedErr string + }{ + { + note: "no future imports", + input: `package test +p { + input.x == 1 +} + +q.foo { + input.x == 2 +} +`, + expected: `package test + +import rego.v1 + +p if { + input.x == 1 +} + +q contains "foo" if { + input.x == 2 +} +`, + }, + { + note: "future imports", + input: `package test +import future.keywords +p if { + input.x == 1 +} + +q contains "foo" { + input.x == 2 +} +`, + expected: `package test + +import rego.v1 + +p if { + input.x == 1 +} + +q contains "foo" if { + input.x == 2 +} +`, + }, + { + note: "duplicate imports", + input: `package test +import data.foo +import data.bar as foo +`, + expectedErr: `failed to format Rego source file: 1 error occurred: %ROOT%/policy.rego:3: rego_compile_error: import must not shadow import data.foo`, + }, + { + note: "root document overrides", + input: `package test +input { + 1 == 1 +} + +p { + data := 2 +}`, + expectedErr: `failed to format Rego source file: 2 errors occurred: +%ROOT%/policy.rego:2: rego_compile_error: rules must not shadow input (use a different rule name) +%ROOT%/policy.rego:7: rego_compile_error: variables must not shadow data (use a different variable name)`, + }, + { + note: "deprecated built-in", + input: `package test +p { + any([true, false]) +} + +q := all([true, false]) +`, + expectedErr: `failed to format Rego source file: 2 errors occurred: +%ROOT%/policy.rego:3: rego_type_error: deprecated built-in function calls in expression: any +%ROOT%/policy.rego:6: rego_type_error: deprecated built-in function calls in expression: all`, + }, + { + note: "v1 module", + v1Compatible: true, + input: `package test + +p contains x if { + some x in ["a", "b", "c"] +}`, + expected: `package test + +import rego.v1 + +p contains x if { + some x in ["a", "b", "c"] +} +`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + params := fmtCommandParams{ + regoV1: true, + v1Compatible: tc.v1Compatible, + } + + files := map[string]string{ + "policy.rego": tc.input, + } + + var stdout bytes.Buffer + + test.WithTempFS(files, func(root string) { + policyFile := filepath.Join(root, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(¶ms, &stdout, policyFile, info, err) + + if tc.expectedErr != "" { + if err == nil { + t.Fatalf("Expected error but got: %s", stdout.String()) + } + expectedErr := strings.ReplaceAll(tc.expectedErr, "%ROOT%", root) + var actualErr string + switch err := err.(type) { + case fmtError: + actualErr = err.msg + default: + actualErr = err.Error() + } + if actualErr != expectedErr { + t.Fatalf("Expected error:\n\n%s\n\nGot error:\n\n%s\n\n", expectedErr, actualErr) + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + actual := stdout.String() + if actual != tc.expected { + t.Fatalf("Expected:%s\n\nGot:\n%s\n\n", tc.expected, actual) + } + } + }) + }) + } +} + +func TestFmt_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + dropV0Imports bool + input string + expected string + expectedErrs []string + }{ + { + note: "no keywords used", + input: `package test +p { + input.x == 1 +} + +q.foo { + input.x == 2 +} +`, + expectedErrs: []string{ + "policy.rego:2: rego_parse_error: `if` keyword is required before rule body", + "policy.rego:6: rego_parse_error: `if` keyword is required before rule body", + "policy.rego:6: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "no imports", + input: `package test +p if { + input.x == 1 +} + +q contains "foo" if { + input.x == 2 +} +`, + expected: `package test + +p if { + input.x == 1 +} + +q contains "foo" if { + input.x == 2 +} +`, + }, + { + note: "future imports", + input: `package test +import future.keywords +p if { + input.x == 1 +} + +q contains "foo" if { + input.x == 2 +} +`, + // NOTE: We keep the future imports to create the broadest possible compatibility surface + expected: `package test + +import future.keywords + +p if { + input.x == 1 +} + +q contains "foo" if { + input.x == 2 +} +`, + }, + { + note: "future imports, drop v0 imports", + input: `package test +import future.keywords.if +import future.keywords.contains +p if { + input.x == 1 +} + +q contains "foo" if { + input.x == 2 +} +`, + expected: `package test + +import future.keywords.contains +import future.keywords.if + +p if { + input.x == 1 +} + +q contains "foo" if { + input.x == 2 +} +`, + }, + { + note: "rego.v1 import", + input: `package test +import rego.v1 +p if { + input.x == 1 +} + +q contains "foo" if { + input.x == 2 +} +`, + // NOTE: We keep the rego.v1 import to create the broadest possible compatibility surface + expected: `package test + +import rego.v1 + +p if { + input.x == 1 +} + +q contains "foo" if { + input.x == 2 +} +`, + }, + { + note: "rego.v1 import, drop v0 imports", + dropV0Imports: true, + input: `package test +import rego.v1 +p if { + input.x == 1 +} + +q contains "foo" if { + input.x == 2 +} +`, + expected: `package test + +p if { + input.x == 1 +} + +q contains "foo" if { + input.x == 2 +} +`, + }, + { + note: "duplicate imports", + input: `package test +import data.foo +import data.bar as foo +`, + expectedErrs: []string{ + `policy.rego:3: rego_compile_error: import must not shadow import data.foo`, + }, + }, + { + note: "root document overrides", + input: `package test +input if { + 1 == 1 +} + +p if { + data := 2 +}`, + expectedErrs: []string{ + `policy.rego:2: rego_compile_error: rules must not shadow input (use a different rule name)`, + `policy.rego:7: rego_compile_error: variables must not shadow data (use a different variable name)`, + }, + }, + { + note: "deprecated built-in", + input: `package test +p if { + any([true, false]) +} + +q := all([true, false]) +`, + expectedErrs: []string{ + `policy.rego:3: rego_type_error: deprecated built-in function calls in expression: any`, + `policy.rego:6: rego_type_error: deprecated built-in function calls in expression: all`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + params := fmtCommandParams{} + params.dropV0Imports = tc.dropV0Imports + + files := map[string]string{ + "policy.rego": tc.input, + } + + var stdout bytes.Buffer + + test.WithTempFS(files, func(root string) { + policyFile := filepath.Join(root, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(¶ms, &stdout, policyFile, info, err) + + if len(tc.expectedErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got: %s", stdout.String()) + } + + for _, expectedErr := range tc.expectedErrs { + var actualErr string + switch err := err.(type) { + case fmtError: + actualErr = err.msg + default: + actualErr = err.Error() + } + if !strings.Contains(actualErr, expectedErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nGot error:\n\n%s\n\n", expectedErr, actualErr) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + actual := stdout.String() + if actual != tc.expected { + t.Fatalf("Expected:%s\n\nGot:\n%s\n\n", tc.expected, actual) + } + } + }) + }) + } +} + +func TestFmtFormatStdin_KeywordsInRefs(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + unformatted string + formatted string + }{ + { + note: "v0", + params: fmtCommandParams{v0Compatible: true}, + unformatted: `package test.package.import + +p { + foo.if.else +} + +foo.if.else { + true +}`, + formatted: `package test.package.import + +p { + foo.if.else +} + +foo.if.else = true +`, + }, + { + note: "v0, no capability", + params: func() fmtCommandParams { + params := newFmtCommandParams() + params.v0Compatible = true + params.capabilitiesFlag.C = dropCapabilityFeature(ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + ast.FeatureKeywordsInRefs) + return *params + }(), + unformatted: `package test.package.import + +p { + foo.if.else +} + +foo.if.else { + true +}`, + formatted: `package test["package"]["import"] + +p { + foo.if["else"] +} + +foo.if["else"] = true +`, + }, + + { + note: "v1", + params: fmtCommandParams{}, + unformatted: `package test.package.import + +p if { + foo.if.else +} + +foo.if.else if { + true +}`, + formatted: `package test.package.import + +p if { + foo.if.else +} + +foo.if.else := true +`, + }, + { + note: "v1, no capability", + params: func() fmtCommandParams { + params := newFmtCommandParams() + params.capabilitiesFlag.C = dropCapabilityFeature(ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + ast.FeatureKeywordsInRefs) + return *params + }(), + unformatted: `package test.package.import + +p if { + foo.if.else +} + +foo.if.else if { + true +}`, + formatted: `package test["package"]["import"] + +p if { + foo["if"]["else"] +} + +foo["if"]["else"] := true +`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + var stdout bytes.Buffer + + stdin := bytes.NewBufferString(tc.unformatted) + + files := map[string]string{ + "policy.rego": tc.unformatted, + } + + test.WithTempFS(files, func(path string) { + err := formatStdin(&tc.params, stdin, &stdout) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + actual := stdout.String() + if actual != tc.formatted { + t.Fatalf("Expected:\n%s\n\nGot:\n%s\n\n", tc.formatted, actual) + } + }) + }) + } +} + +func TestFmtFormatFile_KeywordsInRefs(t *testing.T) { + cases := []struct { + note string + params fmtCommandParams + unformatted string + formatted string + }{ + { + note: "v0", + params: fmtCommandParams{v0Compatible: true}, + unformatted: `package test.package.import + +p { + foo.if.else +} + +foo.if.else { + true +}`, + formatted: `package test.package.import + +p { + foo.if.else +} + +foo.if.else = true +`, + }, + { + note: "v0, no capability", + params: func() fmtCommandParams { + params := newFmtCommandParams() + params.v0Compatible = true + params.capabilitiesFlag.C = dropCapabilityFeature(ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + ast.FeatureKeywordsInRefs) + return *params + }(), + unformatted: `package test.package.import + +p { + foo.if.else +} + +foo.if.else { + true +}`, + formatted: `package test["package"]["import"] + +p { + foo.if["else"] +} + +foo.if["else"] = true +`, + }, + + { + note: "v1", + params: fmtCommandParams{}, + unformatted: `package test.package.import + +p if { + foo.if.else +} + +foo.if.else if { + true +}`, + formatted: `package test.package.import + +p if { + foo.if.else +} + +foo.if.else := true +`, + }, + { + note: "v1, no capability", + params: func() fmtCommandParams { + params := newFmtCommandParams() + params.capabilitiesFlag.C = dropCapabilityFeature(ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + ast.FeatureKeywordsInRefs) + return *params + }(), + unformatted: `package test.package.import + +p if { + foo.if.else +} + +foo.if.else if { + true +}`, + formatted: `package test["package"]["import"] + +p if { + foo["if"]["else"] +} + +foo["if"]["else"] := true +`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + var stdout bytes.Buffer + + files := map[string]string{ + "policy.rego": tc.unformatted, + } + + test.WithTempFS(files, func(path string) { + policyFile := filepath.Join(path, "policy.rego") + info, err := os.Stat(policyFile) + err = formatFile(&tc.params, &stdout, policyFile, info, err) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + actual := stdout.String() + if actual != tc.formatted { + t.Fatalf("Expected:\n%s\n\nGot:\n%s\n\n", tc.formatted, actual) + } + }) + }) + } +} + +func dropCapabilityFeature(caps *ast.Capabilities, feature string) *ast.Capabilities { + feats := make([]string, 0, len(caps.Features)) + for _, f := range caps.Features { + if f != feature { + feats = append(feats, f) + } + } + caps.Features = feats + return caps +} diff --git a/third_party/opa/cmd/formats/formats.go b/third_party/opa/cmd/formats/formats.go new file mode 100644 index 000000000000..3a10d8522416 --- /dev/null +++ b/third_party/opa/cmd/formats/formats.go @@ -0,0 +1,28 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package formats + +import ( + "github.com/open-policy-agent/opa/v1/util" +) + +type option = string + +const ( + Pretty option = "pretty" + JSON option = "json" + GoBench option = "gobench" + Values option = "values" + Bindings option = "bindings" + Source option = "source" + Raw option = "raw" + Discard option = "discard" +) + +// Returns an enum flag for the given formats, where the first provided format +// will be used as the default format. +func Flag(formats ...option) *util.EnumFlag { + return util.NewEnumFlag(formats[0], formats) +} diff --git a/third_party/opa/cmd/inspect.go b/third_party/opa/cmd/inspect.go new file mode 100644 index 000000000000..758239ee949b --- /dev/null +++ b/third_party/opa/cmd/inspect.go @@ -0,0 +1,438 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "os" + "sort" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/cmd/internal/env" + ib "github.com/open-policy-agent/opa/internal/bundle/inspect" + pr "github.com/open-policy-agent/opa/internal/presentation" + iStrs "github.com/open-policy-agent/opa/internal/strings" + "github.com/open-policy-agent/opa/v1/ast" + astJson "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/util" + + "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" +) + +const ( + maxTableFieldLen = 50 + pageWidth = 80 +) + +type inspectCommandParams struct { + outputFormat *util.EnumFlag + listAnnotations bool + v0Compatible bool + v1Compatible bool +} + +func (p *inspectCommandParams) regoVersion() ast.RegoVersion { + if p.v0Compatible { + return ast.RegoV0 + } + if p.v1Compatible { + return ast.RegoV1 + } + return ast.DefaultRegoVersion +} + +func newInspectCommandParams() inspectCommandParams { + return inspectCommandParams{ + outputFormat: formats.Flag(formats.Pretty, formats.JSON), + listAnnotations: false, + } +} + +func initInspect(root *cobra.Command, brand string) { + executable := root.Name() + + params := newInspectCommandParams() + + inspectCommand := &cobra.Command{ + Use: "inspect [ [...]]", + Short: `Inspect ` + brand + ` bundle(s)`, + Long: `Inspect ` + brand + ` bundle(s). + +The 'inspect' command provides a summary of the contents in ` + brand + ` bundle(s) or a single Rego file. +Bundles are gzipped tarballs containing policies and data. The 'inspect' command reads bundle(s) and lists +the following: + +* packages that are contributed by .rego files +* data locations defined by the data.json and data.yaml files +* manifest data +* signature data +* information about the Wasm module files +* package- and rule annotations + +Example: + + $ ls + bundle.tar.gz + $ ` + executable + ` inspect bundle.tar.gz + +You can provide exactly one ` + brand + ` bundle, to a bundle directory, or direct path to a Rego file to the 'inspect' +command on the command-line. If you provide a path referring to a directory, the 'inspect' command will load that path as +a bundle and summarize its structure and contents. If you provide a path referring to a Rego file, the 'inspect' command +will load that file and summarize its structure and contents. +`, + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := validateInspectParams(¶ms, args); err != nil { + return err + } + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + if err := doInspect(params, args[0], os.Stdout); err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + return err + } + return nil + }, + } + + addOutputFormat(inspectCommand.Flags(), params.outputFormat) + addListAnnotations(inspectCommand.Flags(), ¶ms.listAnnotations) + addV0CompatibleFlag(inspectCommand.Flags(), ¶ms.v0Compatible, false) + addV1CompatibleFlag(inspectCommand.Flags(), ¶ms.v1Compatible, false) + root.AddCommand(inspectCommand) +} + +func doInspect(params inspectCommandParams, path string, out io.Writer) error { + info, err := ib.FileForRegoVersion(params.regoVersion(), path, params.listAnnotations) + if err != nil { + return err + } + + switch params.outputFormat.String() { + case formats.JSON: + astJson.SetOptions(astJson.Options{ + MarshalOptions: astJson.MarshalOptions{ + IncludeLocation: astJson.NodeToggle{ + // Annotation location data is only included if includeAnnotations is set + AnnotationsRef: params.listAnnotations, + }, + }, + }) + defer astJson.SetOptions(astJson.Defaults()) + + return pr.JSON(out, info) + + default: + if hasManifest(info) { + if err := populateManifest(out, info.Manifest); err != nil { + return err + } + } + + if len(info.Namespaces) != 0 { + if err := populateNamespaces(out, info.Namespaces); err != nil { + return err + } + } + + if params.listAnnotations && len(info.Annotations) != 0 { + if err := populateAnnotations(out, info.Annotations); err != nil { + return err + } + } + + return nil + } +} + +func hasManifest(info *ib.Info) bool { + if info.Manifest == nil { + return false + } + return info.Manifest.Revision != "" || len(*info.Manifest.Roots) != 0 || len(info.Manifest.Metadata) != 0 || + info.Manifest.RegoVersion != nil +} + +func validateInspectParams(p *inspectCommandParams, args []string) error { + if len(args) != 1 { + return errors.New("specify exactly one OPA bundle or path") + } + + of := p.outputFormat.String() + if of == formats.JSON || of == formats.Pretty { + return nil + } + return errors.New("invalid output format for inspect command") +} + +func populateManifest(out io.Writer, m *bundle.Manifest) error { + t := generateTableWithKeys(out, "field", "value") + var lines [][]string + + if m.RegoVersion != nil { + lines = append(lines, []string{"Rego Version", truncateTableStr(strconv.Itoa(*m.RegoVersion))}) + } + + if m.Revision != "" { + lines = append(lines, []string{"Revision", truncateTableStr(m.Revision)}) + } + + if len(*m.Roots) != 0 { + roots := *m.Roots + if len(roots) == 1 { + if roots[0] != "" { + lines = append(lines, []string{"Roots", truncateFileName(roots[0])}) + } + } else { + sort.Strings(roots) + for _, root := range roots { + lines = append(lines, []string{"Roots", truncateFileName(root)}) + } + } + } + + if len(m.Metadata) != 0 { + metadata, err := json.Marshal(m.Metadata) + if err != nil { + return err + } + lines = append(lines, []string{"Metadata", truncateTableStr(string(metadata))}) + } + + t.AppendBulk(lines) + if t.NumLines() > 0 { + fmt.Fprintln(out, "MANIFEST:") + t.Render() + } + + return nil +} + +func populateNamespaces(out io.Writer, n map[string][]string) error { + t := generateTableWithKeys(out, "namespace", "file") + // only auto-merge the namespace column + t.SetAutoMergeCells(false) + t.SetAutoMergeCellsByColumnIndex([]int{0}) + var lines [][]string + + for _, k := range util.KeysSorted(n) { + for _, file := range n[k] { + lines = append(lines, []string{k, truncateFileName(file)}) + } + } + + t.AppendBulk(lines) + if t.NumLines() > 0 { + fmt.Fprintln(out, "NAMESPACES:") + t.Render() + } + + return nil +} + +func populateAnnotations(out io.Writer, refs []*ast.AnnotationsRef) error { + if len(refs) > 0 { + fmt.Fprintln(out, "ANNOTATIONS:") + for _, ref := range refs { + printTitle(out, ref) + fmt.Fprintln(out) + + if a := ref.Annotations; a != nil && len(a.Description) > 0 { + fmt.Fprintln(out, a.Description) + fmt.Fprintln(out) + } + + if p := ref.GetPackage(); p != nil { + fmt.Fprintln(out, "Package: ", dropDataPrefix(p.Path)) + } + if r := ref.GetRule(); r != nil { + fmt.Fprintln(out, "Rule: ", r.Head.Ref().String()) + } + if loc := ref.Location; loc != nil { + fmt.Fprintln(out, "Location:", loc.String()) + } + if a := ref.Annotations; a != nil { + if len(a.Scope) > 0 { + fmt.Fprintln(out, "Scope:", a.Scope) + } + if a.Entrypoint { + fmt.Fprintln(out, "Entrypoint:", a.Entrypoint) + } + } + fmt.Fprintln(out) + + if a := ref.Annotations; a != nil { + if len(a.Organizations) > 0 { + fmt.Fprintln(out, "Organizations:") + l := make([]listEntry, 0, len(a.Organizations)) + for _, o := range a.Organizations { + l = append(l, listEntry{"", removeNewLines(o)}) + } + printList(out, l, "") + fmt.Fprintln(out) + } + + if len(a.Authors) > 0 { + fmt.Fprintln(out, "Authors:") + l := make([]listEntry, 0, len(a.Authors)) + for _, a := range a.Authors { + l = append(l, listEntry{"", removeNewLines(a.String())}) + } + printList(out, l, "") + fmt.Fprintln(out) + } + + if len(a.Schemas) > 0 { + // NOTE(johanfylling): The Type Checker will MERGE all applicable schema annotations for a rule + // into one list. Here, child nodes OVERRIDE parent nodes' schema annotations instead (default annot. behavior). + // Should the former behavior be replicated here? + fmt.Fprintln(out, "Schemas:") + l := make([]listEntry, 0, len(a.Schemas)) + for _, s := range a.Schemas { + le := listEntry{key: s.Path.String()} + if len(s.Schema) > 0 { + le.value = s.Schema.String() + } else if s.Definition != nil { + b, _ := json.Marshal(s.Definition) + le.value = string(b) + } + l = append(l, le) + } + printList(out, l, ": ") + fmt.Fprintln(out) + } + + if len(a.RelatedResources) > 0 { + fmt.Fprintln(out, "Related Resources:") + l := make([]listEntry, 0, len(a.RelatedResources)) + for _, res := range a.RelatedResources { + l = append(l, listEntry{removeNewLines(res.Ref.String()), res.Description}) + } + printList(out, l, " ") + fmt.Fprintln(out) + } + if len(a.Custom) > 0 { + fmt.Fprintln(out, "Custom:") + l := make([]listEntry, 0, len(a.Custom)) + for k, v := range a.Custom { + b, _ := json.Marshal(v) + l = append(l, listEntry{k, string(b)}) + } + printList(out, l, ": ") + fmt.Fprintln(out) + } + } + } + } + + return nil +} + +type listEntry struct { + key string + value string +} + +func printList(out io.Writer, list []listEntry, separator string) { + keyLength := 0 + for _, e := range list { + l := len(e.key) + if l > keyLength { + keyLength = l + } + } + for _, e := range list { + var line string + if len(e.value) > 0 { + line = fmt.Sprintf(" %s%s%s%s", + e.key, + separator, + strings.Repeat(" ", keyLength-len(e.key)), + e.value) + } else { + line = fmt.Sprintf(" %v", e.key) + } + fmt.Fprintln(out, truncateStr(line, pageWidth)) + } +} + +func printTitle(out io.Writer, ref *ast.AnnotationsRef) { + var title string + if a := ref.Annotations; a != nil { + t := strings.TrimSpace(a.Title) + if len(t) > 0 { + title = t + } + } + + if len(title) == 0 { + title = dropDataPrefix(ref.Path).String() + } + + fmt.Fprintf(out, "%s\n%s\n", title, strings.Repeat("=", min(len(title), pageWidth))) +} + +func generateTableWithKeys(writer io.Writer, keys ...string) *tablewriter.Table { + table := tablewriter.NewWriter(writer) + aligns := []int{} + hdrs := make([]string, 0, len(keys)) + for _, k := range keys { + hdrs = append(hdrs, strings.Title(k)) //nolint:staticcheck // SA1019, no unicode + aligns = append(aligns, tablewriter.ALIGN_LEFT) + } + table.SetHeader(hdrs) + table.SetAlignment(tablewriter.ALIGN_CENTER) + table.SetAutoMergeCells(true) + table.SetColumnAlignment(aligns) + table.SetRowLine(false) + table.SetAutoWrapText(false) + return table +} + +func truncateTableStr(s string) string { + return truncateStr(s, maxTableFieldLen) +} + +func truncateStr(s string, maxLen int) string { + if len(s) < maxLen { + return s + } + return fmt.Sprintf("%v...", s[:maxLen-3]) +} + +func removeNewLines(s string) string { + return strings.ReplaceAll(s, "\n", " ") +} + +func truncateFileName(s string) string { + if len(s) < maxTableFieldLen { + return s + } + + res, _ := iStrs.TruncateFilePaths(maxTableFieldLen, len(s), s) + return res[s] +} + +// dropDataPrefix drops the first component of the passed Ref +func dropDataPrefix(ref ast.Ref) ast.Ref { + if len(ref) <= 1 { + return ast.EmptyRef() + } + r := ref[1:].Copy() + if s, ok := r[0].Value.(ast.String); ok { + r[0].Value = ast.Var(s) + } + return r +} diff --git a/third_party/opa/cmd/inspect_test.go b/third_party/opa/cmd/inspect_test.go new file mode 100644 index 000000000000..4d0544408f46 --- /dev/null +++ b/third_party/opa/cmd/inspect_test.go @@ -0,0 +1,2265 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "bytes" + "fmt" + "maps" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/util" + + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestDoInspect(t *testing.T) { + files := [][2]string{ + {"/.manifest", `{"revision": "rev", "roots": ["foo", "bar", "fuz", "baz", "a", "x"]}`}, + {"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`}, + {"/example/foo.rego", `package foo`}, + } + + buf := archive.MustWriteTarGz(files) + + test.WithTempFS(nil, func(rootDir string) { + bundleFile := filepath.Join(rootDir, "bundle.tar.gz") + + bf, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + var out bytes.Buffer + params := newInspectCommandParams() + err = params.outputFormat.Set(formats.JSON) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + err = doInspect(params, bundleFile, &out) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + res := `{ + "capabilities": {"features": ["rego_v1"]}, + "manifest": {"revision": "rev", "roots": ["foo", "bar", "fuz", "baz", "a", "x"]}, + "signatures_config": {}, + "namespaces": {"data": ["/data.json"], "data.foo": ["/example/foo.rego"]} + }` + + exp := util.MustUnmarshalJSON([]byte(res)) + result := util.MustUnmarshalJSON(out.Bytes()) + if !reflect.DeepEqual(exp, result) { + t.Fatalf("expected inspect output to be:\n\n%v\n\ngot:\n\n%v", exp, result) + } + }) +} + +func TestDoInspectPretty(t *testing.T) { + + root := fmt.Sprintf("metadata/%v/features", strings.Repeat("foobar", 20)) + + manifest := fmt.Sprintf(`{"revision": "%s", +"roots": ["foo", "bar", "fuz", "http", "a", "x", "%s"], +"metadata": {"hello": "%s"}, +"wasm": [{"entrypoint": "http/example/authz", "module": "/policy.wasm"}, {"entrypoint": "http/example/foo/allow", "module": "/example/policy.wasm"}]}`, strings.Repeat("foobar", 10), root, strings.Repeat("world", 100)) + + files := [][2]string{ + {"/.manifest", manifest}, + {"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`}, + {"/http/example/authz/foo.rego", `package http.example.authz`}, + {"/http/example/authz/data.json", `{"faz": "baz"}`}, + {"/example/foo.rego", `package foo`}, + {"/a/b/y/foo.rego", `package a.b.y`}, + {"/a/xxxxxxxxxxxxxxxxxxxxxx/yyyyyyyyyyyyyyyyyyyy/foo.rego", `package a.b.y`}, + {"/example/policy.wasm", `modules-compiled-as-wasm-binary`}, + {"/http/example/policy.wasm", `modules-compiled-as-wasm-binary`}, + {"/policy.wasm", `modules-compiled-as-wasm-binary`}, + } + + buf := archive.MustWriteTarGz(files) + + test.WithTempFS(nil, func(rootDir string) { + bundleFile := filepath.Join(rootDir, "bundle.tar.gz") + + bf, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + var out bytes.Buffer + err = doInspect(newInspectCommandParams(), bundleFile, &out) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + output := strings.TrimSpace(out.String()) + expected := strings.TrimSpace(` + MANIFEST: ++----------+----------------------------------------------------+ +| FIELD | VALUE | ++----------+----------------------------------------------------+ +| Revision | foobarfoobarfoobarfoobarfoobarfoobarfoobarfooba... | +| Roots | a | +| | bar | +| | foo | +| | fuz | +| | http | +| | metadata/...oobarfoobarfoobarfoobarfoobar/features | +| | x | +| Metadata | {"hello":"worldworldworldworldworldworldworldwo... | ++----------+----------------------------------------------------+ +NAMESPACES: ++-----------------------------+----------------------------------------------------+ +| NAMESPACE | FILE | ++-----------------------------+----------------------------------------------------+ +| data | /data.json | +| data.a.b.y | /a/b/y/foo.rego | +| | /a/...xxxxxxxxxxxxxx/yyyyyyyyyyyyyyyyyyyy/foo.rego | +| data.foo | /example/foo.rego | +| data.http.example.authz | /http/example/authz/foo.rego | +| | /http/example/authz/data.json | +| | /policy.wasm | +| data.http.example.foo.allow | /example/policy.wasm | ++-----------------------------+----------------------------------------------------+ +`) + + if output != expected { + t.Fatalf("Unexpected output. Expected:\n\n%v\n\nGot:\n\n%v", expected, output) + } + + }) +} + +func TestDoInspectPrettyManifestOnlySingleRoot(t *testing.T) { + + root := fmt.Sprintf("metadata/%v/features", strings.Repeat("foobar", 6)) + + manifest := fmt.Sprintf(`{"roots": ["%s"], +"metadata": {"hello": "world"}}`, root) + + files := [][2]string{ + {"/.manifest", manifest}, + } + + buf := archive.MustWriteTarGz(files) + + test.WithTempFS(nil, func(rootDir string) { + bundleFile := filepath.Join(rootDir, "bundle.tar.gz") + + bf, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + var out bytes.Buffer + err = doInspect(newInspectCommandParams(), bundleFile, &out) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + output := strings.TrimSpace(out.String()) + expected := strings.TrimSpace(` +MANIFEST: ++----------+----------------------------------------------------+ +| FIELD | VALUE | ++----------+----------------------------------------------------+ +| Roots | metadata/...oobarfoobarfoobarfoobarfoobar/features | +| Metadata | {"hello":"world"} | ++----------+----------------------------------------------------+ +`) + + if output != expected { + t.Fatalf("Unexpected output. Expected:\n\n%v\n\nGot:\n\n%v", expected, output) + } + + }) +} + +func TestInspectMultiBundleError(t *testing.T) { + params := newInspectCommandParams() + err := validateInspectParams(¶ms, []string{"foo", "bar"}) + if err == nil { + t.Fatal("Expected error but got nil") + } + + exp := "specify exactly one OPA bundle or path" + if err.Error() != exp { + t.Fatalf("Expected error %v but got %v", exp, err.Error()) + } +} + +func TestDoInspectWithAnnotations(t *testing.T) { + + files := map[string]string{ + "x.rego": `# METADATA +# title: pkg-title +# description: pkg-descr +# organizations: +# - pkg-org +# related_resources: +# - https://pkg +# - ref: https://pkg +# description: rr-pkg-note +# authors: +# - pkg-author +# schemas: +# - input: {"type": "boolean"} +# custom: +# pkg: pkg-custom +package test + +# METADATA +# scope: document +# title: doc-title +# description: doc-descr +# organizations: +# - doc-org +# related_resources: +# - https://doc +# - ref: https://doc +# description: rr-doc-note +# authors: +# - doc-author +# schemas: +# - input: {"type": "integer"} +# custom: +# doc: doc-custom + +# METADATA +# title: rule-title +# description: rule-title +# organizations: +# - rule-org +# related_resources: +# - https://rule +# - ref: https://rule +# description: rr-rule-note +# authors: +# - rule-author +# schemas: +# - input: {"type": "string"} +# custom: +# rule: rule-custom +p = 1`, + } + + t.Run("pretty", func(t *testing.T) { + test.WithTempFS(files, func(rootDir string) { + ps := newInspectCommandParams() + ps.listAnnotations = true + var out bytes.Buffer + err := doInspect(ps, rootDir, &out) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + bs := out.Bytes() + idx := bytes.Index(bs, []byte(`ANNOTATIONS`)) // skip NAMESPACE box + output := strings.TrimSpace(string(bs[idx:])) + expected := strings.TrimSpace(fmt.Sprintf(` +ANNOTATIONS: +pkg-title +========= + +pkg-descr + +Package: test +Location: %[1]s/x.rego:16 +Scope: package + +Organizations: + pkg-org + +Authors: + pkg-author + +Schemas: + input: {"type":"boolean"} + +Related Resources: + https://pkg + https://pkg rr-pkg-note + +Custom: + pkg: "pkg-custom" + +doc-title +========= + +doc-descr + +Package: test +Rule: p +Location: %[1]s/x.rego:50 +Scope: document + +Organizations: + doc-org + +Authors: + doc-author + +Schemas: + input: {"type":"integer"} + +Related Resources: + https://doc + https://doc rr-doc-note + +Custom: + doc: "doc-custom" + +rule-title +========== + +rule-title + +Package: test +Rule: p +Location: %[1]s/x.rego:50 +Scope: rule + +Organizations: + rule-org + +Authors: + rule-author + +Schemas: + input: {"type":"string"} + +Related Resources: + https://rule + https://rule rr-rule-note + +Custom: + rule: "rule-custom"`, rootDir)) + + if output != expected { + t.Fatalf("Unexpected output. Expected:\n\n%q\n\nGot:\n\n%q", expected, output) + } + }) + }) + + t.Run("json", func(t *testing.T) { + test.WithTempFS(files, func(rootDir string) { + ps := newInspectCommandParams() + ps.listAnnotations = true + err := ps.outputFormat.Set(formats.JSON) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + var out bytes.Buffer + err = doInspect(ps, rootDir, &out) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + bs := out.Bytes() + expected := strings.TrimSpace(fmt.Sprintf(`{ + "manifest": { + "revision": "", + "roots": [ + "" + ] + }, + "signatures_config": {}, + "namespaces": { + "data.test": [ + "%[1]s/x.rego" + ] + }, + "annotations": [ + { + "annotations": { + "authors": [ + { + "name": "pkg-author" + } + ], + "custom": { + "pkg": "pkg-custom" + }, + "description": "pkg-descr", + "organizations": [ + "pkg-org" + ], + "related_resources": [ + { + "ref": "https://pkg" + }, + { + "description": "rr-pkg-note", + "ref": "https://pkg" + } + ], + "schemas": [ + { + "path": [ + { + "type": "var", + "value": "input" + } + ], + "definition": { + "type": "boolean" + } + } + ], + "scope": "package", + "title": "pkg-title" + }, + "location": { + "file": "%[1]s/x.rego", + "row": 16, + "col": 1 + }, + "path": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "test" + } + ] + }, + { + "annotations": { + "authors": [ + { + "name": "doc-author" + } + ], + "custom": { + "doc": "doc-custom" + }, + "description": "doc-descr", + "organizations": [ + "doc-org" + ], + "related_resources": [ + { + "ref": "https://doc" + }, + { + "description": "rr-doc-note", + "ref": "https://doc" + } + ], + "schemas": [ + { + "path": [ + { + "type": "var", + "value": "input" + } + ], + "definition": { + "type": "integer" + } + } + ], + "scope": "document", + "title": "doc-title" + }, + "location": { + "file": "%[1]s/x.rego", + "row": 50, + "col": 1 + }, + "path": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "test" + }, + { + "type": "string", + "value": "p" + } + ] + }, + { + "annotations": { + "authors": [ + { + "name": "rule-author" + } + ], + "custom": { + "rule": "rule-custom" + }, + "description": "rule-title", + "organizations": [ + "rule-org" + ], + "related_resources": [ + { + "ref": "https://rule" + }, + { + "description": "rr-rule-note", + "ref": "https://rule" + } + ], + "schemas": [ + { + "path": [ + { + "type": "var", + "value": "input" + } + ], + "definition": { + "type": "string" + } + } + ], + "scope": "rule", + "title": "rule-title" + }, + "location": { + "file": "%[1]s/x.rego", + "row": 50, + "col": 1 + }, + "path": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "test" + }, + { + "type": "string", + "value": "p" + } + ] + } + ], + "capabilities": { + "features": [ + "rego_v1" + ] + } +}`, rootDir)) + + exp := util.MustUnmarshalJSON([]byte(expected)) + result := util.MustUnmarshalJSON(bs) + if !reflect.DeepEqual(exp, result) { + t.Fatalf("expected inspect output to be:\n\n%v\n\ngot:\n\n%v", exp, result) + } + }) + }) +} + +func TestDoInspectTarballWithAnnotations(t *testing.T) { + + files := [][2]string{ + {"x.rego", `# METADATA +# title: pkg-title +# description: pkg-descr +# organizations: +# - pkg-org +# related_resources: +# - https://pkg +# - ref: https://pkg +# description: rr-pkg-note +# authors: +# - pkg-author +# schemas: +# - input: {"type": "boolean"} +# custom: +# pkg: pkg-custom +package test + +# METADATA +# scope: document +# title: doc-title +# description: doc-descr +# organizations: +# - doc-org +# related_resources: +# - https://doc +# - ref: https://doc +# description: rr-doc-note +# authors: +# - doc-author +# schemas: +# - input: {"type": "integer"} +# custom: +# doc: doc-custom + +# METADATA +# title: rule-title +# description: rule-title +# organizations: +# - rule-org +# related_resources: +# - https://rule +# - ref: https://rule +# description: rr-rule-note +# authors: +# - rule-author +# schemas: +# - input: {"type": "string"} +# custom: +# rule: rule-custom +p = 1`}, + {".manifest", ` +{ + "revision": "", + "roots": [ + "" + ], + "wasm": [ + { + "entrypoint": "test/a", + "module": "/policy.wasm" + }, + { + "entrypoint": "test/b", + "module": "/policy.wasm", + "annotations": [ + { + "scope": "rule", + "title": "WASM RULE B", + "entrypoint": true + } + ] + } + ] +}`}, + {"policy.wasm", ""}, + } + + buf := archive.MustWriteTarGz(files) + + t.Run("pretty", func(t *testing.T) { + test.WithTempFS(nil, func(rootDir string) { + bundleFile := filepath.Join(rootDir, "bundle.tar.gz") + + bf, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + ps := newInspectCommandParams() + ps.listAnnotations = true + var out bytes.Buffer + + err = doInspect(ps, bundleFile, &out) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + bs := out.Bytes() + idx := bytes.Index(bs, []byte(`ANNOTATIONS`)) // skip NAMESPACE box + output := strings.TrimSpace(string(bs[idx:])) + expected := strings.TrimSpace(` +ANNOTATIONS: +pkg-title +========= + +pkg-descr + +Package: test +Location: /x.rego:16 +Scope: package + +Organizations: + pkg-org + +Authors: + pkg-author + +Schemas: + input: {"type":"boolean"} + +Related Resources: + https://pkg + https://pkg rr-pkg-note + +Custom: + pkg: "pkg-custom" + +WASM RULE B +=========== + +Location: /policy.wasm:0 +Scope: rule +Entrypoint: true + +doc-title +========= + +doc-descr + +Package: test +Rule: p +Location: /x.rego:50 +Scope: document + +Organizations: + doc-org + +Authors: + doc-author + +Schemas: + input: {"type":"integer"} + +Related Resources: + https://doc + https://doc rr-doc-note + +Custom: + doc: "doc-custom" + +rule-title +========== + +rule-title + +Package: test +Rule: p +Location: /x.rego:50 +Scope: rule + +Organizations: + rule-org + +Authors: + rule-author + +Schemas: + input: {"type":"string"} + +Related Resources: + https://rule + https://rule rr-rule-note + +Custom: + rule: "rule-custom"`) + + if output != expected { + t.Fatalf("Unexpected output. Expected:\n\n%q\n\nGot:\n\n%q", expected, output) + } + + }) + }) + + t.Run("json", func(t *testing.T) { + test.WithTempFS(nil, func(rootDir string) { + bundleFile := filepath.Join(rootDir, "bundle.tar.gz") + + bf, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + ps := newInspectCommandParams() + ps.listAnnotations = true + err = ps.outputFormat.Set(formats.JSON) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + var out bytes.Buffer + + err = doInspect(ps, bundleFile, &out) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + expected := strings.TrimSpace(fmt.Sprintf(`{ + "manifest": { + "revision": "", + "roots": [ + "" + ], + "wasm": [ + { + "entrypoint": "test/a", + "module": "/policy.wasm" + }, + { + "entrypoint": "test/b", + "module": "/policy.wasm", + "annotations": [ + { + "entrypoint": true, + "scope": "rule", + "title": "WASM RULE B" + } + ] + } + ] + }, + "signatures_config": {}, + "wasm_modules": [ + { + "entrypoints": [ + "data.test.a", + "data.test.b" + ], + "path": "/policy.wasm", + "url": "%[1]s/policy.wasm" + } + ], + "namespaces": { + "data.test": [ + "/x.rego" + ], + "data.test.a": [ + "/policy.wasm" + ], + "data.test.b": [ + "/policy.wasm" + ] + }, + "annotations": [ + { + "annotations": { + "authors": [ + { + "name": "pkg-author" + } + ], + "custom": { + "pkg": "pkg-custom" + }, + "description": "pkg-descr", + "organizations": [ + "pkg-org" + ], + "related_resources": [ + { + "ref": "https://pkg" + }, + { + "description": "rr-pkg-note", + "ref": "https://pkg" + } + ], + "schemas": [ + { + "path": [ + { + "type": "var", + "value": "input" + } + ], + "definition": { + "type": "boolean" + } + } + ], + "scope": "package", + "title": "pkg-title" + }, + "location": { + "file": "/x.rego", + "row": 16, + "col": 1 + }, + "path": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "test" + } + ] + }, + { + "annotations": { + "entrypoint": true, + "scope": "rule", + "title": "WASM RULE B" + }, + "location": { + "file": "/policy.wasm", + "row": 0, + "col": 0 + }, + "path": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "test" + }, + { + "type": "string", + "value": "b" + } + ] + }, + { + "annotations": { + "authors": [ + { + "name": "doc-author" + } + ], + "custom": { + "doc": "doc-custom" + }, + "description": "doc-descr", + "organizations": [ + "doc-org" + ], + "related_resources": [ + { + "ref": "https://doc" + }, + { + "description": "rr-doc-note", + "ref": "https://doc" + } + ], + "schemas": [ + { + "path": [ + { + "type": "var", + "value": "input" + } + ], + "definition": { + "type": "integer" + } + } + ], + "scope": "document", + "title": "doc-title" + }, + "location": { + "file": "/x.rego", + "row": 50, + "col": 1 + }, + "path": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "test" + }, + { + "type": "string", + "value": "p" + } + ] + }, + { + "annotations": { + "authors": [ + { + "name": "rule-author" + } + ], + "custom": { + "rule": "rule-custom" + }, + "description": "rule-title", + "organizations": [ + "rule-org" + ], + "related_resources": [ + { + "ref": "https://rule" + }, + { + "description": "rr-rule-note", + "ref": "https://rule" + } + ], + "schemas": [ + { + "path": [ + { + "type": "var", + "value": "input" + } + ], + "definition": { + "type": "string" + } + } + ], + "scope": "rule", + "title": "rule-title" + }, + "location": { + "file": "/x.rego", + "row": 50, + "col": 1 + }, + "path": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "test" + }, + { + "type": "string", + "value": "p" + } + ] + } + ], + "capabilities": { + "features": [ + "rego_v1" + ] + } +}`, bundleFile)) + exp := util.MustUnmarshalJSON([]byte(expected)) + + bs := out.Bytes() + result := util.MustUnmarshalJSON(bs) + if !reflect.DeepEqual(exp, result) { + t.Fatalf("expected inspect output to be:\n\n%v\n\ngot:\n\n%v", exp, result) + } + }) + }) +} + +func TestDoInspect_V0Compatible(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + module string + expErrs []string + }{ + { + note: "v0, keywords not used", + v0Compatible: true, + module: `package test +p[v] { + v := input.x +}`, + }, + { + note: "v0, no keywords imported, but used", + v0Compatible: true, + module: `package test +p contains v if { + v := input.x +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v0, keywords imported", + module: `package test +import future.keywords +p contains v if { + v := input.x +}`, + }, + { + note: "v0, rego.v1 imported", + module: `package test +import rego.v1 +p contains v if { + v := input.x +}`, + }, + { + note: "v1, keywords not used", + module: `package test +p[v] { + v := input.x +}`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1, no keywords imported", + module: `package test +p contains v if { + v := input.x +}`, + }, + { + note: "v1, keywords imported", + module: `package test +import future.keywords +p contains v if { + v := input.x +}`, + }, + { + note: "v1, rego.v1 imported", + module: `package test +import rego.v1 +p contains v if { + v := input.x +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(nil, func(rootDir string) { + buf := archive.MustWriteTarGz([][2]string{{"/policy.rego", tc.module}}) + + bundleFile := filepath.Join(rootDir, "bundle.tar.gz") + + bf, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + var out bytes.Buffer + params := newInspectCommandParams() + params.v0Compatible = tc.v0Compatible + err = params.outputFormat.Set(formats.JSON) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + err = doInspect(params, bundleFile, &out) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error:\n\n%v\n\nbut got:\n\n%v", expErr, err.Error()) + } + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + }) + } +} + +func TestDoInspectWithBundleRegoVersion(t *testing.T) { + tests := []struct { + note string + bundleRegoVersion int + files map[string]string + expErrs []string + }{ + { + note: "v0.x bundle, keywords not used", + bundleRegoVersion: 0, + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +p[v] { + v := input.x +}`, + }, + }, + { + note: "v0.x bundle, no keywords imported, but used", + bundleRegoVersion: 0, + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +p contains v if { + v := input.x +}`, + }, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v0.x bundle, keywords imported", + bundleRegoVersion: 0, + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +import future.keywords +p contains v if { + v := input.x +}`, + }, + }, + { + note: "v0.x bundle, rego.v1 imported", + bundleRegoVersion: 0, + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test +import rego.v1 +p contains v if { + v := input.x +}`, + }, + }, + { + note: "v0 bundle, v1 per-file override", + bundleRegoVersion: 0, + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } +}`, + "policy1.rego": `package test +p[1] { + v := input.x +}`, + "policy2.rego": `package test +p contains 2 if { + v := input.x +}`, + }, + }, + { + note: "v0 bundle, v1 per-file override (glob)", + bundleRegoVersion: 0, + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/bar/*.rego": 1 + } +}`, + "foo/policy1.rego": `package test +p[1] { + v := input.x +}`, + "bar/policy2.rego": `package test +p contains 2 if { + v := input.x +}`, + }, + }, + { + note: "v0 bundle, v1 per-file override, incompatible", + bundleRegoVersion: 0, + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } +}`, + "policy1.rego": `package test +p[1] { + v := input.x +}`, + "policy2.rego": `package test +p[2] { + v := input.x +}`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1.0 bundle, keywords not used", + bundleRegoVersion: 1, + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +p[v] { + v := input.x +}`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1.0 bundle, no keywords imported", + bundleRegoVersion: 1, + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +p contains v if { + v := input.x +}`, + }, + }, + { + note: "v1.0 bundle, keywords imported", + bundleRegoVersion: 1, + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +import future.keywords +p contains v if { + v := input.x +}`, + }, + }, + { + note: "v1.0 bundle, rego.v1 imported", + bundleRegoVersion: 1, + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test +import rego.v1 +p contains v if { + v := input.x +}`, + }, + }, + { + note: "v1 bundle, v0 per-file override", + bundleRegoVersion: 1, + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } +}`, + "policy1.rego": `package test +p[1] { + v := input.x +}`, + "policy2.rego": `package test +p contains 2 if { + v := input.x +}`, + }, + }, + { + note: "v1 bundle, v0 per-file override", + bundleRegoVersion: 1, + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/foo/*.rego": 0 + } +}`, + "foo/policy1.rego": `package test +p[1] { + v := input.x +}`, + "bar/policy2.rego": `package test +p contains 2 if { + v := input.x +}`, + }, + }, + { + note: "v1 bundle, v0 per-file override, incompatible", + bundleRegoVersion: 1, + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } +}`, + "policy1.rego": `package test +p contains 1 if { + v := input.x +}`, + "policy2.rego": `package test +p contains 2 if { + v := input.x +}`, + }, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + } + + bundleTypeCases := []struct { + note string + tar bool + }{ + { + "bundle dir", false, + }, + { + "bundle tar", true, + }, + } + + v1CompatibleFlagCases := []struct { + note string + used bool + }{ + { + "no --v1-compatible", false, + }, + { + "--v1-compatible", true, + }, + } + + for _, bundleType := range bundleTypeCases { + for _, v1CompatibleFlag := range v1CompatibleFlagCases { + for _, tc := range tests { + t.Run(fmt.Sprintf("%s, %s, %s", bundleType.note, v1CompatibleFlag.note, tc.note), func(t *testing.T) { + files := map[string]string{} + if bundleType.tar { + files["bundle.tar.gz"] = "" + } else { + maps.Copy(files, tc.files) + } + + test.WithTempFS(files, func(root string) { + p := root + if bundleType.tar { + p = filepath.Join(root, "bundle.tar.gz") + files := make([][2]string, 0, len(tc.files)) + for k, v := range tc.files { + files = append(files, [2]string{k, v}) + } + buf := archive.MustWriteTarGz(files) + bf, err := os.Create(p) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + var out bytes.Buffer + params := newInspectCommandParams() + params.v1Compatible = v1CompatibleFlag.used + err := params.outputFormat.Set(formats.Pretty) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + err = doInspect(params, p, &out) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got output: %s", out.String()) + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error:\n\n%v\n\nbut got:\n\n%v", expErr, err.Error()) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + expOut := fmt.Sprintf(`MANIFEST: ++--------------+-------+ +| FIELD | VALUE | ++--------------+-------+ +| Rego Version | %d | ++--------------+-------+`, + tc.bundleRegoVersion) + if !strings.Contains(out.String(), expOut) { + t.Fatalf("Expected output to contain:\n\n%s\n\nbut got:\n\n%s", expOut, out.String()) + } + } + }) + }) + } + } + } +} + +func TestUnknownRefs(t *testing.T) { + tests := []struct { + note string + files [][2]string + expected string + }{ + { + note: "unknown built-in func call", + files: [][2]string{ + { + "/policy.rego", `package test +p if { + foo.bar(42) + contains("foo", "o") +}`, + }, + }, + // Note: unknown foo.bar() built-in doesn't appear in the output, but also didn't cause an error. + expected: `{ + "manifest": { + "revision": "", + "roots": [ + "" + ] + }, + "signatures_config": {}, + "namespaces": { + "data.test": [ + "/policy.rego" + ] + }, + "capabilities": { + "builtins": [ + { + "name": "contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + } + ], + "features": [ + "rego_v1" + ] + } +}`, + }, + { + // Happy path + note: "known ref replaced inside 'with' stmt", + files: [][2]string{ + {"/policy.rego", `package test + +foo.bar(_) := false + +p if { + foo.bar(42) +} + +mock(_) := true + +test_p if { + p with data.test.foo.bar as mock +}`}, + }, + expected: `{ + "manifest": { + "revision": "", + "roots": [ + "" + ] + }, + "signatures_config": {}, + "namespaces": { + "data.test": [ + "/policy.rego" + ] + }, + "capabilities": { + "features": [ + "rego_v1" + ] + } +}`, + }, + { + note: "unknown ref replaced inside 'with' stmt", + files: [][2]string{ + {"/policy.rego", `package test + +p if { + data.foo.bar(42) +} + +mock(_) := true + +test_p if { + p with data.foo.bar as mock +}`}, + }, + expected: `{ + "manifest": { + "revision": "", + "roots": [ + "" + ] + }, + "signatures_config": {}, + "namespaces": { + "data.test": [ + "/policy.rego" + ] + }, + "capabilities": { + "features": [ + "rego_v1" + ] + } +}`, + }, + { + note: "unknown built-in (var) replaced inside 'with' stmt", + files: [][2]string{ + {"/policy.rego", `package test + +p if { + foo(42) +} + +mock(_) := true + +test_p if { + p with foo as mock +}`}, + }, + expected: `{ + "manifest": { + "revision": "", + "roots": [ + "" + ] + }, + "signatures_config": {}, + "namespaces": { + "data.test": [ + "/policy.rego" + ] + }, + "capabilities": { + "features": [ + "rego_v1" + ] + } +}`, + }, + { + note: "unknown built-in (ref) replaced inside 'with' stmt", + files: [][2]string{ + {"/policy.rego", `package test + +p if { + foo.bar(42) +} + +mock(_) := true + +test_p if { + p with foo.bar as mock +}`}, + }, + expected: `{ + "manifest": { + "revision": "", + "roots": [ + "" + ] + }, + "signatures_config": {}, + "namespaces": { + "data.test": [ + "/policy.rego" + ] + }, + "capabilities": { + "features": [ + "rego_v1" + ] + } +}`, + }, + { + note: "call replaced by unknown data ref inside 'with' stmt", + files: [][2]string{ + {"/policy.rego", `package test + +p if { + foo(42) +} + +foo(_) := false + +test_p if { + p with foo as data.bar +}`}, + }, + expected: `{ + "manifest": { + "revision": "", + "roots": [ + "" + ] + }, + "signatures_config": {}, + "namespaces": { + "data.test": [ + "/policy.rego" + ] + }, + "capabilities": { + "builtins": [ + { + "name": "eq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "=" + } + ], + "features": [ + "rego_v1" + ] + } +}`, + }, + { + note: "call replaced by unknown built-in (var) inside 'with' stmt", + files: [][2]string{ + {"/policy.rego", `package test + +p if { + foo(42) +} + +foo(_) := false + +test_p if { + # bar is unknown built-in + p with foo as bar +}`}, + }, + expected: `{ + "manifest": { + "revision": "", + "roots": [ + "" + ] + }, + "signatures_config": {}, + "namespaces": { + "data.test": [ + "/policy.rego" + ] + }, + "capabilities": { + "features": [ + "rego_v1" + ] + } +}`, + }, + { + note: "call replaced by unknown built-in (ref) inside 'with' stmt", + files: [][2]string{ + {"/policy.rego", `package test + +p if { + foo(42) +} + +foo(_) := false + +test_p if { + # bar.baz is unknown built-in + p with foo as bar.baz +}`}, + }, + expected: `{ + "manifest": { + "revision": "", + "roots": [ + "" + ] + }, + "signatures_config": {}, + "namespaces": { + "data.test": [ + "/policy.rego" + ] + }, + "capabilities": { + "builtins": [ + { + "name": "eq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "=" + } + ], + "features": [ + "rego_v1" + ] + } +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + buf := archive.MustWriteTarGz(tc.files) + + test.WithTempFS(nil, func(rootDir string) { + bundleFile := filepath.Join(rootDir, "bundle.tar.gz") + + bf, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + var out bytes.Buffer + params := newInspectCommandParams() + err = params.outputFormat.Set(formats.JSON) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + err = doInspect(params, bundleFile, &out) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + bs := out.Bytes() + output := strings.TrimSpace(string(bs)) + if output != tc.expected { + t.Fatalf("Unexpected output. Expected:\n\n%s\n\nGot:\n\n%s", tc.expected, output) + } + }) + }) + } +} + +func TestCallToUnknownRegoFunction(t *testing.T) { + files := [][2]string{ + {"/policy.rego", `package test +import data.x.y + +p if { + y(1) == true +} + `}, + } + + buf := archive.MustWriteTarGz(files) + + test.WithTempFS(nil, func(rootDir string) { + bundleFile := filepath.Join(rootDir, "bundle.tar.gz") + + bf, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + var out bytes.Buffer + params := newInspectCommandParams() + err = params.outputFormat.Set(formats.JSON) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + err = doInspect(params, bundleFile, &out) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + bs := out.Bytes() + output := strings.TrimSpace(string(bs)) + // Note: unknown data.x.y() function doesn't appear in the output, but also didn't cause an error. + expected := strings.TrimSpace(`{ + "manifest": { + "revision": "", + "roots": [ + "" + ] + }, + "signatures_config": {}, + "namespaces": { + "data.test": [ + "/policy.rego" + ] + }, + "capabilities": { + "builtins": [ + { + "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": "==" + } + ], + "features": [ + "rego_v1" + ] + } +}`) + + if output != expected { + t.Fatalf("Unexpected output. Expected:\n\n%s\n\nGot:\n\n%s", expected, output) + } + }) +} + +func TestDoInspectSingleFileWithAnnotations(t *testing.T) { + files := map[string]string{ + "/a/xxxxxxxxxxxxxxxxxxxxxx/yyyyyyyyyyyyyyyyyyyy/foo.rego": `# METADATA +# title: pkg-title +# description: pkg-descr +# organizations: +# - pkg-org +# related_resources: +# - https://pkg +# - ref: https://pkg +# description: rr-pkg-note +# authors: +# - pkg-author +# schemas: +# - input: {"type": "boolean"} +# custom: +# pkg: pkg-custom +package test + +# METADATA +# scope: document +# title: doc-title +# description: doc-descr +# organizations: +# - doc-org +# related_resources: +# - https://doc +# - ref: https://doc +# description: rr-doc-note +# authors: +# - doc-author +# schemas: +# - input: {"type": "integer"} +# custom: +# doc: doc-custom + +# METADATA +# title: rule-title +# description: rule-title +# organizations: +# - rule-org +# related_resources: +# - https://rule +# - ref: https://rule +# description: rr-rule-note +# authors: +# - rule-author +# schemas: +# - input: {"type": "string"} +# custom: +# rule: rule-custom +p = 1`, + } + + test.WithTempFS(files, func(rootDir string) { + fileName := rootDir + "/a/xxxxxxxxxxxxxxxxxxxxxx/yyyyyyyyyyyyyyyyyyyy/foo.rego" + ps := newInspectCommandParams() + ps.listAnnotations = true + var out bytes.Buffer + err := doInspect(ps, fileName, &out) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + shortFileName := truncateFileName(fileName) + output := strings.TrimSpace(out.String()) + expected := strings.TrimSpace(fmt.Sprintf(` +NAMESPACES: ++-----------+----------------------------------------------------+ +| NAMESPACE | FILE | ++-----------+----------------------------------------------------+ +| data.test | %[1]s | ++-----------+----------------------------------------------------+ +ANNOTATIONS: +pkg-title +========= + +pkg-descr + +Package: test +Location: %[2]s:16 +Scope: package + +Organizations: + pkg-org + +Authors: + pkg-author + +Schemas: + input: {"type":"boolean"} + +Related Resources: + https://pkg + https://pkg rr-pkg-note + +Custom: + pkg: "pkg-custom" + +doc-title +========= + +doc-descr + +Package: test +Rule: p +Location: %[2]s:50 +Scope: document + +Organizations: + doc-org + +Authors: + doc-author + +Schemas: + input: {"type":"integer"} + +Related Resources: + https://doc + https://doc rr-doc-note + +Custom: + doc: "doc-custom" + +rule-title +========== + +rule-title + +Package: test +Rule: p +Location: %[2]s:50 +Scope: rule + +Organizations: + rule-org + +Authors: + rule-author + +Schemas: + input: {"type":"string"} + +Related Resources: + https://rule + https://rule rr-rule-note + +Custom: + rule: "rule-custom"`, shortFileName, fileName)) + + if output != expected { + t.Fatalf("Unexpected output. Expected:\n\n%q\n\nGot:\n\n%q", expected, output) + } + }) +} + +func TestDoInspectSingleFile(t *testing.T) { + files := map[string]string{ + "/a/xxxxxxxxxxxxxxxxxxxxxx/yyyyyyyyyyyyyyyyyyyy/foo.rego": `# METADATA +# title: pkg-title +# description: pkg-descr +# organizations: +# - pkg-org +# related_resources: +# - https://pkg +# - ref: https://pkg +# description: rr-pkg-note +# authors: +# - pkg-author +# schemas: +# - input: {"type": "boolean"} +# custom: +# pkg: pkg-custom +package test + +# METADATA +# scope: document +# title: doc-title +# description: doc-descr +# organizations: +# - doc-org +# related_resources: +# - https://doc +# - ref: https://doc +# description: rr-doc-note +# authors: +# - doc-author +# schemas: +# - input: {"type": "integer"} +# custom: +# doc: doc-custom + +# METADATA +# title: rule-title +# description: rule-title +# organizations: +# - rule-org +# related_resources: +# - https://rule +# - ref: https://rule +# description: rr-rule-note +# authors: +# - rule-author +# schemas: +# - input: {"type": "string"} +# custom: +# rule: rule-custom +p = 1`, + } + + test.WithTempFS(files, func(rootDir string) { + fileName := rootDir + "/a/xxxxxxxxxxxxxxxxxxxxxx/yyyyyyyyyyyyyyyyyyyy/foo.rego" + ps := newInspectCommandParams() + var out bytes.Buffer + err := doInspect(ps, fileName, &out) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + shortFileName := truncateFileName(fileName) + output := strings.TrimSpace(out.String()) + expected := strings.TrimSpace(fmt.Sprintf(` +NAMESPACES: ++-----------+----------------------------------------------------+ +| NAMESPACE | FILE | ++-----------+----------------------------------------------------+ +| data.test | %s | ++-----------+----------------------------------------------------+ +`, shortFileName)) + + if output != expected { + t.Fatalf("Unexpected output. Expected:\n\n%q\n\nGot:\n\n%q", expected, output) + } + }) +} diff --git a/third_party/opa/cmd/internal/env/env.go b/third_party/opa/cmd/internal/env/env.go new file mode 100644 index 000000000000..9a4e385e67f5 --- /dev/null +++ b/third_party/opa/cmd/internal/env/env.go @@ -0,0 +1,50 @@ +package env + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spf13/viper" +) + +type cmdFlags interface { + CheckEnvironmentVariables(command *cobra.Command) error +} + +type cmdFlagsImpl struct{} + +var ( + CmdFlags cmdFlags = cmdFlagsImpl{} + errorMessagePrefix = "error mapping environment variables to command flags" +) + +const globalPrefix = "opa" + +func (cmdFlagsImpl) CheckEnvironmentVariables(command *cobra.Command) error { + var errs []string + v := viper.New() + v.AutomaticEnv() + if command.Name() == globalPrefix { + v.SetEnvPrefix(command.Name()) + } else { + v.SetEnvPrefix(fmt.Sprintf("%s_%s", globalPrefix, command.Name())) + } + command.Flags().VisitAll(func(f *pflag.Flag) { + configName := f.Name + configName = strings.ReplaceAll(configName, "-", "_") + if !f.Changed && v.IsSet(configName) { + val := v.Get(configName) + err := command.Flags().Set(f.Name, fmt.Sprintf("%v", val)) + if err != nil { + errs = append(errs, err.Error()) + } + } + }) + + if len(errs) == 0 { + return nil + } + return fmt.Errorf("%s: %s", errorMessagePrefix, strings.Join(errs, "; ")) +} diff --git a/third_party/opa/cmd/internal/env/env_test.go b/third_party/opa/cmd/internal/env/env_test.go new file mode 100644 index 000000000000..fd4b63c56a48 --- /dev/null +++ b/third_party/opa/cmd/internal/env/env_test.go @@ -0,0 +1,175 @@ +package env + +import ( + "bytes" + "fmt" + "io" + "strings" + "testing" + + "github.com/spf13/cobra" +) + +func mockRootCmd(writer io.Writer) *cobra.Command { + var rootArgs struct { + IntFlag int + StrFlag string + BoolFlag bool + } + cmd := cobra.Command{ + Use: "opa [opts]", + Short: "test root command", + Long: `test root command`, + PreRunE: func(cmd *cobra.Command, _ []string) error { + return CmdFlags.CheckEnvironmentVariables(cmd) + }, + Run: func(_ *cobra.Command, _ []string) { + fmt.Fprintf(writer, "%v; %v; %v", rootArgs.IntFlag, rootArgs.StrFlag, rootArgs.BoolFlag) + }, + } + cmd.Flags().IntVarP(&rootArgs.IntFlag, "int", "i", 0, "set int") + cmd.Flags().StringVarP(&rootArgs.StrFlag, "some-string", "s", "", "set string") + cmd.Flags().BoolVarP(&rootArgs.BoolFlag, "bool", "b", false, "set bool") + return &cmd +} + +func mockChildCmd(writer io.Writer) *cobra.Command { + var rootArgs struct { + IntFlag int + StrFlag string + BoolFlag bool + } + cmd := cobra.Command{ + Use: "child [opts]", + Short: "test child command", + Long: `test child command`, + PreRunE: func(cmd *cobra.Command, _ []string) error { + return CmdFlags.CheckEnvironmentVariables(cmd) + }, + Run: func(_ *cobra.Command, _ []string) { + fmt.Fprintf(writer, "%v; %v; %v", rootArgs.IntFlag, rootArgs.StrFlag, rootArgs.BoolFlag) + }, + } + cmd.Flags().IntVarP(&rootArgs.IntFlag, "second-int", "i", 100, "set int") + cmd.Flags().StringVarP(&rootArgs.StrFlag, "second-string", "s", "child-string", "set string") + cmd.Flags().BoolVarP(&rootArgs.BoolFlag, "second-bool", "b", true, "set bool") + return &cmd +} + +func TestCmdFlagsImpl_CheckEnvironmentVariables_NoEnvVarsSingleCommand(t *testing.T) { + rootWriter := bytes.NewBuffer([]byte{}) + root := mockRootCmd(rootWriter) + if err := root.PreRunE(root, []string{}); err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + root.Run(root, []string{}) + out := rootWriter.String() + expectation := "0; ; false" + if out != expectation { + t.Fatalf("expected default flag values %q, got %q", expectation, out) + } +} + +func TestCmdFlagsImpl_CheckEnvironmentVariables_OneEnvVarsSingleCommand(t *testing.T) { + rootWriter := bytes.NewBuffer([]byte{}) + root := mockRootCmd(rootWriter) + t.Setenv("OPA_INT", "3") + if err := root.PreRunE(root, []string{}); err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + root.Run(root, []string{}) + out := rootWriter.String() + expectation := "3; ; false" + if out != expectation { + t.Fatalf("expected flag values %q, got %q", expectation, out) + } +} + +func TestCmdFlagsImpl_CheckEnvironmentVariables_AllEnvVarsSingleCommand(t *testing.T) { + rootWriter := bytes.NewBuffer([]byte{}) + root := mockRootCmd(rootWriter) + t.Setenv("OPA_INT", "40") + t.Setenv("OPA_SOME_STRING", "test") + t.Setenv("OPA_BOOL", "true") + if err := root.PreRunE(root, []string{}); err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + root.Run(root, []string{}) + out := rootWriter.String() + expectation := "40; test; true" + if out != expectation { + t.Fatalf("expected flag values %q, got %q", expectation, out) + } +} + +func TestCmdFlagsImpl_CheckEnvironmentVariables_ChildCommandAllEnvVars(t *testing.T) { + root := mockRootCmd(&bytes.Buffer{}) + childWriter := bytes.NewBuffer([]byte{}) + child := mockChildCmd(childWriter) + root.AddCommand(child) + t.Setenv("OPA_CHILD_SECOND_INT", "7") + t.Setenv("OPA_CHILD_SECOND_STRING", "testing child") + t.Setenv("OPA_CHILD_SECOND_BOOL", "false") + if err := child.PreRunE(child, []string{}); err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + child.Run(child, []string{}) + childOut := childWriter.String() + childExpectation := "7; testing child; false" + if childOut != childExpectation { + t.Fatalf("expected child flag values %q, got %q", childExpectation, childOut) + } +} + +func TestCmdFlagsImpl_CheckEnvironmentVariables_ChildCommandReturnsSingleErr(t *testing.T) { + root := mockRootCmd(&bytes.Buffer{}) + child := mockChildCmd(&bytes.Buffer{}) + root.AddCommand(child) + t.Setenv("OPA_CHILD_SECOND_BOOL", "7") + err := child.PreRunE(child, []string{}) + if err == nil { + t.Fatalf("expected error, found none") + } + expectedString := "invalid argument \"7\"" + if !strings.Contains(err.Error(), expectedString) { + t.Fatalf("expected error to include %q, instead got %q", expectedString, err.Error()) + } +} + +func TestCmdFlagsImpl_CheckEnvironmentVariables_ChildCommandReturnsMultipleErr(t *testing.T) { + root := mockRootCmd(&bytes.Buffer{}) + child := mockChildCmd(&bytes.Buffer{}) + root.AddCommand(child) + t.Setenv("OPA_CHILD_SECOND_INT", "true") + t.Setenv("OPA_CHILD_SECOND_BOOL", "7") + err := child.PreRunE(child, []string{"child"}) + expectedString := "invalid argument" + if err == nil { + t.Fatalf("expected error, found none") + } + if !strings.Contains(err.Error(), expectedString) { + t.Fatalf("expected error to include %q, instead got %q", expectedString, err.Error()) + } + if !strings.Contains(err.Error(), "7") { + t.Fatalf("expected error for invalid int 7 as argument for boolean flag") + } + if !strings.Contains(err.Error(), "true") { + t.Fatalf("expected error for invalid int 7 as argument for int flag") + } +} + +func TestCmdFlagsImpl_CheckEnvironmentVariables_ConfirmCommandFlagPrecedence(t *testing.T) { + rootWriter := bytes.NewBuffer([]byte{}) + root := mockRootCmd(rootWriter) + t.Setenv("OPA_INT", "3") + t.Setenv("OPA_BOOL", "true") + root.SetArgs([]string{"-i", "42"}) + if err := root.Execute(); err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + out := rootWriter.String() + expectation := "42; ; true" + if out != expectation { + t.Fatalf("expected flag values %q, got %q", expectation, out) + } +} diff --git a/third_party/opa/cmd/internal/exec/exec.go b/third_party/opa/cmd/internal/exec/exec.go new file mode 100644 index 000000000000..a30c22c94d47 --- /dev/null +++ b/third_party/opa/cmd/internal/exec/exec.go @@ -0,0 +1,140 @@ +package exec + +import ( + "context" + "errors" + "os" + "path" + "path/filepath" + "strings" + + "github.com/open-policy-agent/opa/v1/sdk" +) + +var ( + r *jsonReporter + parsers = map[string]parser{ + ".json": utilParser{}, + ".yaml": utilParser{}, + ".yml": utilParser{}, + } +) + +const stdInPath = "--stdin-input" + +// Exec executes OPA against the supplied files and outputs each result. +// +// NOTE(tsandall): consider expanding functionality: +// +// - specialized output formats (e.g., pretty/non-JSON outputs) +// - exit codes set by convention or policy (e.g,. non-empty set => error) +// - support for new input file formats beyond JSON and YAML +func Exec(ctx context.Context, opa *sdk.OPA, params *Params) error { + if err := params.validateParams(); err != nil { + return err + } + + r = &jsonReporter{w: params.Output, buf: make([]result, 0), ctx: &ctx, opa: opa, params: params, decisionFunc: opa.Decision} + + if params.StdIn { + if err := execOnStdIn(); err != nil { + return err + } + } + + if err := execOnInputFiles(params); err != nil { + return err + } + + if err := r.Close(); err != nil { + return err + } + + return r.ReportFailure() +} + +func execOnStdIn() error { + sr := stdInReader{Reader: os.Stdin} + p := utilParser{} + raw := sr.ReadInput() + input, err := p.Parse(strings.NewReader(raw)) + if err != nil { + return err + } else if input == nil { + return errors.New("cannot execute on empty input; please enter valid json or yaml when using the --stdin-input flag") + } + r.StoreDecision(&input, stdInPath) + return nil +} + +type fileListItem struct { + Path string + Error error +} + +func execOnInputFiles(params *Params) error { + for item := range listAllPaths(params.Paths) { + + if item.Error != nil { + return item.Error + } + + input, err := parse(item.Path) + + if err != nil { + r.Report(result{Path: item.Path, Error: err}) + if params.FailDefined || params.Fail || params.FailNonEmpty { + r.errorCount++ + } + continue + } else if input == nil { + continue + } + r.StoreDecision(input, item.Path) + } + return nil +} + +func listAllPaths(roots []string) chan fileListItem { + ch := make(chan fileListItem) + go func() { + for _, path := range roots { + err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + ch <- fileListItem{Path: path} + return nil + }) + if err != nil { + ch <- fileListItem{Path: path, Error: err} + } + } + close(ch) + }() + return ch +} + +func parse(p string) (*any, error) { + selectedParser, ok := parsers[path.Ext(p)] + if !ok { + return nil, nil + } + + f, err := os.Open(p) + if err != nil { + return nil, err + } + + defer f.Close() + + val, err := selectedParser.Parse(f) + if err != nil { + return nil, err + } + + return &val, nil +} diff --git a/third_party/opa/cmd/internal/exec/exec_test.go b/third_party/opa/cmd/internal/exec/exec_test.go new file mode 100644 index 000000000000..58e352d40793 --- /dev/null +++ b/third_party/opa/cmd/internal/exec/exec_test.go @@ -0,0 +1,245 @@ +package exec + +import ( + "bytes" + "context" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/sdk" + + "github.com/open-policy-agent/opa/v1/util/test" +) + +/* +Most of cmd/internal/exec/exec.go is tested indirectly in cmd/exec_test.go. +This file tests internal functions that are not as easily accessed, particularly +their edge and error cases. +*/ + +func TestParse(t *testing.T) { + files := map[string]string{ + "valid.json": `{"this": "that"}`, + "invalid.json": `{[a",!`, + } + + test.WithTempFS(files, func(rootDir string) { + if val, err := parse(filepath.Join(rootDir, "no parser")); val != nil || err != nil { + t.Fatalf("return values should have been nil passing a file path with no matching extension, got val: %v; err: %s", val, err.Error()) + } + if _, err := parse(filepath.Join(rootDir, "nonexistent.json")); err == nil { + t.Fatalf("should have received an error for passing a file path that does not exist") + } + if _, err := parse(filepath.Join(rootDir, "invalid.json")); err == nil { + t.Fatalf("should have received an error for passing a file with invalid json") + } + if val, err := parse(filepath.Join(rootDir, "valid.json")); err != nil { + t.Fatalf("unexpected error when passing file wiith valid json: %q", err.Error()) + } else { + v := *val + that, ok := v.(map[string]any)["this"] + if !ok { + t.Fatalf("expected parsed data to have key %q with value %q, found none", "this", "that") + } + if that.(string) != "that" { + t.Fatalf("expected parsed data to have key %q with value %q, instead got value %v", "this", "that", that) + } + } + }) +} + +func TestListAllPaths(t *testing.T) { + files := map[string]string{ + "file.json": `{"this": "that"}`, + } + + test.WithTempFS(files, func(rootDir string) { + notFound := "./test/error" + ch := listAllPaths([]string{rootDir, notFound}) + for item := range ch { + if strings.Contains(item.Path, rootDir) { + if item.Error != nil { + t.Errorf("unexpected error for mock file: %q", item.Error) + } + } else if strings.Contains(item.Path, notFound) { + if item.Error == nil { + t.Errorf("expected error for tempDir, found none") + } + } + } + }) +} + +func TestExec(t *testing.T) { + tests := []struct { + description string + files map[string]string + stdIn bool + input string + assertion func(t *testing.T, buf string, err error) + }{ + { + description: "should read from valid JSON file and not raise an error", + files: map[string]string{ + "files/test.json": `{"foo": 7}`, + "bundle/x.rego": `package system + + test_fun := x { + x = false + x + } + + undefined_test { + test_fun + }`, + }, + assertion: func(t *testing.T, _ string, err error) { + if err != nil { + t.Fatalf("unexpected error raised: %q", err.Error()) + } + }, + }, + { + description: "should raise error count if invalid json is found", + files: map[string]string{ + "files/test.json": `{[foo":`, + "bundle/x.rego": `package system + + test_fun := x { + x = false + x + } + + undefined_test { + test_fun + }`, + }, + assertion: func(t *testing.T, _ string, err error) { + if err == nil { + t.Fatalf("expected error, found none") + } + if r.errorCount != 1 { + t.Fatalf("expected r.errorCount to be 1, got %d", r.errorCount) + } + }, + }, + { + description: "should read from stdin-input if flag is set", + files: map[string]string{ + "bundle/x.rego": `package system + + test_fun := x { + x = false + x + } + + undefined_test { + test_fun + }`, + }, + stdIn: true, + input: `{"foo": 7}`, + assertion: func(t *testing.T, _ string, err error) { + if err != nil { + t.Fatalf("unexpected error raised: %q", err.Error()) + } + }, + }, + { + description: "should read from files and stdin-input if flag is set", + files: map[string]string{ + "files/test.json": `{"foo": 8}`, + "bundle/x.rego": `package system + + test_fun := x { + x = false + x + } + + undefined_test { + test_fun + }`, + }, + stdIn: true, + input: `{"foo": 7}`, + assertion: func(t *testing.T, output string, err error) { + if err != nil { + t.Fatalf("unexpected error raised: %q", err.Error()) + } + + exp := `{ + "result": [ + { + "path": "--stdin-input", + "error": { + "code": "opa_undefined_error", + "message": "/system/main decision was undefined" + } + }, + { + "path": "%ROOT%/files/test.json", + "error": { + "code": "opa_undefined_error", + "message": "/system/main decision was undefined" + } + } + ] +} +` + if output != exp { + t.Fatalf("expected output to be:\n\n%s\n\ngot:\n\n%s", exp, output) + } + }, + }, + } + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + test.WithTempFS(tt.files, func(dir string) { + var buf bytes.Buffer + params := NewParams(&buf) + _ = params.OutputFormat.Set("json") + params.BundlePaths = []string{dir + "/bundle/"} + params.FailNonEmpty = true + if tt.stdIn { + params.StdIn = true + tempFile, err := os.CreateTemp(t.TempDir(), "test") + if err != nil { + t.Fatalf("unexpected error creating temp file: %q", err.Error()) + } + if _, err := tempFile.WriteString(tt.input); err != nil { + t.Fatalf("unexpeced error when writing to temp file: %q", err.Error()) + } + if _, err := tempFile.Seek(0, 0); err != nil { + t.Fatalf("unexpected error when rewinding temp file: %q", err.Error()) + } + oldStdin := os.Stdin + defer func() { + os.Stdin = oldStdin + os.Remove(tempFile.Name()) + }() + os.Stdin = tempFile + } + + if _, ok := tt.files["files/test.json"]; ok { + params.Paths = append(params.Paths, dir+"/files/") + } + + ctx := context.Background() + opa, _ := sdk.New(ctx, sdk.Options{ + Config: bytes.NewReader([]byte{}), + Logger: logging.NewNoOpLogger(), + ConsoleLogger: logging.NewNoOpLogger(), + Ready: make(chan struct{}), + V1Compatible: params.V1Compatible, + }) + + err := Exec(ctx, opa, params) + output := strings.ReplaceAll(buf.String(), dir, "%ROOT%") + tt.assertion(t, output, err) + }) + }) + } +} diff --git a/third_party/opa/cmd/internal/exec/json_reporter.go b/third_party/opa/cmd/internal/exec/json_reporter.go new file mode 100644 index 000000000000..f10e28e4a296 --- /dev/null +++ b/third_party/opa/cmd/internal/exec/json_reporter.go @@ -0,0 +1,86 @@ +package exec + +import ( + "context" + "encoding/json" + "fmt" + "io" + "time" + + "github.com/open-policy-agent/opa/v1/sdk" +) + +type result struct { + DecisionID string `json:"decision_id,omitempty"` + Path string `json:"path"` + Error error `json:"error,omitempty"` + Result *any `json:"result,omitempty"` +} + +type jsonReporter struct { + w io.Writer + buf []result + ctx *context.Context + opa *sdk.OPA + params *Params + errorCount int + failCount int + decisionFunc func(ctx context.Context, options sdk.DecisionOptions) (*sdk.DecisionResult, error) +} + +func (jr *jsonReporter) Report(r result) { + jr.buf = append(jr.buf, r) +} + +func (jr *jsonReporter) Close() error { + enc := json.NewEncoder(jr.w) + enc.SetIndent("", " ") + return enc.Encode(struct { + Result []result `json:"result"` + }{ + Result: jr.buf, + }) +} + +func (jr *jsonReporter) StoreDecision(input *any, itemPath string) { + rs, err := jr.decisionFunc(*jr.ctx, sdk.DecisionOptions{ + Path: jr.params.Decision, + Now: time.Now(), + Input: input, + }) + if err != nil { + jr.Report(result{Path: itemPath, Error: err}) + if (jr.params.FailDefined && !sdk.IsUndefinedErr(err)) || (jr.params.Fail && sdk.IsUndefinedErr(err)) || (jr.params.FailNonEmpty && !sdk.IsUndefinedErr(err)) { + jr.errorCount++ + } + return + } + + jr.Report(result{DecisionID: rs.ID, Path: itemPath, Result: &rs.Result}) + + if (jr.params.FailDefined && rs.Result != nil) || (jr.params.Fail && rs.Result == nil) { + jr.failCount++ + } + + if jr.params.FailNonEmpty && rs.Result != nil { + // Check if rs.Result is an array and has one or more members + resultArray, isArray := rs.Result.([]any) + if (!isArray) || (isArray && (len(resultArray) > 0)) { + jr.failCount++ + } + } +} + +func (jr *jsonReporter) ReportFailure() error { + if (jr.params.Fail || jr.params.FailDefined || jr.params.FailNonEmpty) && (jr.failCount > 0 || jr.errorCount > 0) { + if jr.params.Fail { + return fmt.Errorf("there were %d failures and %d errors counted in the results list, and --fail is set", jr.failCount, jr.errorCount) + } + if jr.params.FailDefined { + return fmt.Errorf("there were %d failures and %d errors counted in the results list, and --fail-defined is set", jr.failCount, jr.errorCount) + } + return fmt.Errorf("there were %d failures and %d errors counted in the results list, and --fail-non-empty is set", jr.failCount, jr.errorCount) + } + + return nil +} diff --git a/third_party/opa/cmd/internal/exec/json_reporter_test.go b/third_party/opa/cmd/internal/exec/json_reporter_test.go new file mode 100644 index 000000000000..b1c154faa39b --- /dev/null +++ b/third_party/opa/cmd/internal/exec/json_reporter_test.go @@ -0,0 +1,164 @@ +package exec + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "testing" + + "github.com/open-policy-agent/opa/v1/sdk" +) + +func TestJsonReporter_Close(t *testing.T) { + wr := bytes.NewBuffer([]byte{}) + wrp := &wr + testString := "test" + testData := []result{ + {Path: testString}, + } + jr := jsonReporter{w: *wrp, buf: testData} + if err := jr.Close(); err != nil { + t.Fatalf("unexpected error running jsonReporter.Close: %q", err.Error()) + } + results := struct { + Result []result + }{} + if err := json.Unmarshal(wr.Bytes(), &results); err != nil { + t.Fatalf("unexpected error deserializing results: %q", err.Error()) + } + if results.Result[0].Path != testString { + t.Fatalf("expected result Path to be %q, got %q", testString, results.Result[0].Path) + } +} + +func TestJsonReporter_StoreDecision(t *testing.T) { + testString := "test" + ctx := context.TODO() + tcs := []struct { + Name string + Path string + DecisionFunc func(ctx context.Context, options sdk.DecisionOptions) (*sdk.DecisionResult, error) + Params Params + ExpectedErrorCount int + ExpectedFailureCount int + }{ + { + Name: "should return nil with increased error count if error is raised from decision", + Path: testString, + DecisionFunc: func(_ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { + return nil, errors.New("test") + }, + Params: Params{FailNonEmpty: true}, + ExpectedErrorCount: 1, + ExpectedFailureCount: 0, + }, + { + Name: "should increase failure count if decision result is nil and params.Fail is true", + Path: testString, + DecisionFunc: func(_ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { + return &sdk.DecisionResult{Result: nil}, nil + }, + Params: Params{Fail: true}, + ExpectedErrorCount: 0, + ExpectedFailureCount: 1, + }, + { + Name: "should increase failure count by 2 if decision result is not nil and params.FailDefined and params.FailNonEmpty are true", + Path: testString, + DecisionFunc: func(_ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { + return &sdk.DecisionResult{Result: []string{testString}}, nil + }, + Params: Params{FailDefined: true, FailNonEmpty: true}, + ExpectedErrorCount: 0, + ExpectedFailureCount: 2, + }, + } + + for _, tc := range tcs { + t.Run(tc.Name, func(t *testing.T) { + wr := bytes.NewBuffer([]byte{}) + j := jsonReporter{ + w: wr, + buf: []result{}, + decisionFunc: tc.DecisionFunc, + params: &tc.Params, + ctx: &ctx, + } + j.StoreDecision(nil, testString) + if j.errorCount != tc.ExpectedErrorCount { + t.Fatalf("expected error count to be %d, got %d", tc.ExpectedErrorCount, j.errorCount) + } + if j.failCount != tc.ExpectedFailureCount { + t.Fatalf("expected failure count to be %d, got %d", tc.ExpectedFailureCount, j.failCount) + } + }) + } +} + +func TestJsonReporter_ReportFailure(t *testing.T) { + tcs := []struct { + Name string + Params Params + Errs int + Fails int + IsErr bool + }{ + { + Name: "errors with Fail flagged", + Params: Params{Fail: true}, + Errs: 5, + IsErr: true, + }, + { + Name: "failures with FailDefined flagged", + Params: Params{FailDefined: true}, + Fails: 3, + IsErr: true, + }, + { + Name: "failures and errors with FailNonEmpty flagged", + Params: Params{FailNonEmpty: true}, + Fails: 1, + Errs: 1, + IsErr: true, + }, + { + Name: "no failures nor errors", + Params: Params{Fail: true}, + Fails: 0, + Errs: 0, + IsErr: false, + }, + { + Name: "failures and errors without param flags", + Params: Params{}, + Fails: 2, + Errs: 2, + IsErr: false, + }, + } + + for _, tc := range tcs { + t.Run(tc.Name, func(t *testing.T) { + wr := bytes.NewBuffer([]byte{}) + ctx := context.Background() + j := jsonReporter{ + w: wr, + buf: []result{}, + decisionFunc: func(_ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { + return &sdk.DecisionResult{}, nil + }, + params: &tc.Params, + ctx: &ctx, + } + j.errorCount = tc.Errs + j.failCount = tc.Fails + if err := j.ReportFailure(); tc.IsErr && err == nil { + t.Fatalf("expected error, found none") + } else if !tc.IsErr && err != nil { + t.Fatalf("unexpected error: %q", err.Error()) + } + }) + } +} diff --git a/third_party/opa/cmd/internal/exec/params.go b/third_party/opa/cmd/internal/exec/params.go new file mode 100644 index 000000000000..912484191277 --- /dev/null +++ b/third_party/opa/cmd/internal/exec/params.go @@ -0,0 +1,55 @@ +package exec + +import ( + "errors" + "io" + "time" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/util" +) + +type Params struct { + Paths []string // file paths to execute against + Output io.Writer // output stream to write normal output to + ConfigFile string // OPA configuration file path + ConfigOverrides []string // OPA configuration overrides (--set arguments) + ConfigOverrideFiles []string // OPA configuration overrides (--set-file arguments) + OutputFormat *util.EnumFlag // output format (default: pretty) + LogLevel *util.EnumFlag // log level for plugins + LogFormat *util.EnumFlag // log format for plugins + LogTimestampFormat string // log timestamp format for plugins + BundlePaths []string // explicit paths of bundles to inject into the configuration + Decision string // decision to evaluate (overrides default decision set by configuration) + Fail bool // exits with non-zero exit code on undefined policy decision or empty policy decision result or other errors + FailDefined bool // exits with non-zero exit code on 'not undefined policy decisiondefined' or 'not empty policy decision result' or other errors + FailNonEmpty bool // exits with non-zero exit code on non-empty set (array) results + StdIn bool // pull input from std-in, rather than input files + Timeout time.Duration // timeout to prevent infinite hangs. If set to 0, the command will never time out + V0Compatible bool // use OPA 0.x compatibility mode + V1Compatible bool // use OPA 1.0 compatibility mode + Logger logging.Logger // Logger override. If set to nil, the default logger is used. +} + +func NewParams(w io.Writer) *Params { + return &Params{ + Output: w, + OutputFormat: formats.Flag(formats.JSON), + LogLevel: util.NewEnumFlag("error", []string{"debug", "info", "error"}), + LogFormat: util.NewEnumFlag("json", []string{"text", "json", "json-pretty"}), + } +} + +func (p *Params) validateParams() error { + if p.Fail && p.FailDefined { + return errors.New("specify --fail or --fail-defined but not both") + } + if p.FailNonEmpty && p.Fail { + return errors.New("specify --fail-non-empty or --fail but not both") + } + if p.FailNonEmpty && p.FailDefined { + return errors.New("specify --fail-non-empty or --fail-defined but not both") + } + return nil +} diff --git a/third_party/opa/cmd/internal/exec/params_test.go b/third_party/opa/cmd/internal/exec/params_test.go new file mode 100644 index 000000000000..fb6eb37d2646 --- /dev/null +++ b/third_party/opa/cmd/internal/exec/params_test.go @@ -0,0 +1,57 @@ +package exec + +import ( + "bytes" + "testing" +) + +func TestNewParams(t *testing.T) { + testString := "test" + w := bytes.NewBuffer([]byte{}) + p := NewParams(w) + if _, err := p.Output.Write([]byte(testString)); err != nil { + t.Fatalf("unexpected error writing to params.Output: %q", err) + } + if w.String() != testString { + t.Fatalf("expected params.Output bytes to be %q, got %q", testString, w.String()) + } +} + +func TestParams_validateParams(t *testing.T) { + tcs := []struct { + Name string + Params Params + ShouldError bool + }{ + { + Name: "should return error if p.Fail and p.FailDefined are true", + Params: Params{Fail: true, FailDefined: true}, + ShouldError: true, + }, + { + Name: "should return error if p.FailNonEmpty and p.Fail are true", + Params: Params{Fail: true, FailNonEmpty: true}, + ShouldError: true, + }, + { + Name: "should return error if .FailNonEmpty and p.FailDefined are true", + Params: Params{FailNonEmpty: true, FailDefined: true}, + ShouldError: true, + }, + { + Name: "should not return an error", + Params: Params{Fail: true, FailDefined: false, FailNonEmpty: false}, + ShouldError: false, + }, + } + for _, tc := range tcs { + t.Run(tc.Name, func(t *testing.T) { + err := tc.Params.validateParams() + if tc.ShouldError && err == nil { + t.Fatalf("expected error, saw none") + } else if !tc.ShouldError && err != nil { + t.Fatalf("unexpected error: %q", err.Error()) + } + }) + } +} diff --git a/third_party/opa/cmd/internal/exec/parser.go b/third_party/opa/cmd/internal/exec/parser.go new file mode 100644 index 000000000000..90407019c0ce --- /dev/null +++ b/third_party/opa/cmd/internal/exec/parser.go @@ -0,0 +1,23 @@ +package exec + +import ( + "io" + + "github.com/open-policy-agent/opa/v1/util" +) + +type parser interface { + Parse(io.Reader) (any, error) +} + +type utilParser struct { +} + +func (utilParser) Parse(r io.Reader) (any, error) { + bs, err := io.ReadAll(r) + if err != nil { + return nil, err + } + var x any + return x, util.Unmarshal(bs, &x) +} diff --git a/third_party/opa/cmd/internal/exec/parser_test.go b/third_party/opa/cmd/internal/exec/parser_test.go new file mode 100644 index 000000000000..3676e4f00383 --- /dev/null +++ b/third_party/opa/cmd/internal/exec/parser_test.go @@ -0,0 +1,66 @@ +package exec + +import ( + "bytes" + "encoding/json" + "errors" + "io" + "strings" + "testing" +) + +type errReader int + +func (errReader) Read(_ []byte) (n int, err error) { + return 0, errors.New("test error") +} + +func TestUtilParser_Parse(t *testing.T) { + testJSON := map[string]string{"this": "that"} + b, err := json.Marshal(testJSON) + if err != nil { + t.Fatalf("unexpected error marshalling valid json: %q", err.Error()) + } + tcs := []struct { + Name string + Reader io.Reader + ShouldError bool + Expectation func(x any) + }{ + { + Name: "should return an error if the provided reader raises an error", + Reader: new(errReader), + ShouldError: true, + }, + { + Name: "should return an error for invalid JSON", + Reader: strings.NewReader("{[invalid json"), + ShouldError: true, + }, + { + Name: "should return a valid JSON object", + Reader: bytes.NewBuffer(b), + ShouldError: false, + Expectation: func(x any) { + if val, ok := x.(map[string]any)["this"]; !ok { + t.Fatalf("expected returned value to have key %q, but none was found", "this") + } else if val != "that" { + t.Fatalf("expected returned value to have value %q for key %q, instead got %q", "that", "this", val) + } + }, + }, + } + for _, tc := range tcs { + t.Run(tc.Name, func(t *testing.T) { + up := utilParser{} + res, err := up.Parse(tc.Reader) + if tc.ShouldError { + if err == nil { + t.Fatalf("expected error, found none") + } + } else { + tc.Expectation(res) + } + }) + } +} diff --git a/third_party/opa/cmd/internal/exec/std_in_reader.go b/third_party/opa/cmd/internal/exec/std_in_reader.go new file mode 100644 index 000000000000..85db3ee1c734 --- /dev/null +++ b/third_party/opa/cmd/internal/exec/std_in_reader.go @@ -0,0 +1,25 @@ +package exec + +import ( + "bufio" + "io" + "strings" +) + +type stdInReader struct { + Reader io.Reader +} + +func (sr *stdInReader) ReadInput() string { + var lines []string + in := bufio.NewScanner(sr.Reader) + for { + in.Scan() + line := in.Text() + if len(line) == 0 { + break + } + lines = append(lines, line) + } + return strings.Join(lines, "\n") +} diff --git a/third_party/opa/cmd/internal/exec/std_in_reader_test.go b/third_party/opa/cmd/internal/exec/std_in_reader_test.go new file mode 100644 index 000000000000..87579e3135ba --- /dev/null +++ b/third_party/opa/cmd/internal/exec/std_in_reader_test.go @@ -0,0 +1,53 @@ +package exec + +import ( + "io" + "strings" + "testing" +) + +func TestStdInReader_ReadInput(t *testing.T) { + tcs := []struct { + Name string + Reader io.Reader + ExpectedRes string + }{ + { + Name: "should read multi-line json", + Reader: strings.NewReader(`{ +"this": "that", +"those": "them" +}`), + ExpectedRes: `{ +"this": "that", +"those": "them" +}`, + }, + { + Name: "should read multi-line yaml", + Reader: strings.NewReader(`this: that +those: +- them +- there`), + ExpectedRes: `this: that +those: +- them +- there`, + }, + { + Name: "should read single-line text", + Reader: strings.NewReader("test"), + ExpectedRes: "test", + }, + } + + for _, tc := range tcs { + t.Run(tc.Name, func(t *testing.T) { + sr := stdInReader{Reader: tc.Reader} + res := sr.ReadInput() + if res != tc.ExpectedRes { + t.Errorf("expected read result to be %q, got %q", tc.ExpectedRes, res) + } + }) + } +} diff --git a/third_party/opa/cmd/oracle.go b/third_party/opa/cmd/oracle.go new file mode 100644 index 000000000000..a8f7649869df --- /dev/null +++ b/third_party/opa/cmd/oracle.go @@ -0,0 +1,209 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "errors" + "fmt" + "io" + "os" + "strconv" + "strings" + + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/internal/env" + "github.com/open-policy-agent/opa/internal/presentation" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ast/oracle" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/loader" +) + +type findDefinitionParams struct { + stdinBuffer bool + bundlePaths repeatedStringFlag + v0Compatible bool + v1Compatible bool +} + +func (p *findDefinitionParams) regoVersion() ast.RegoVersion { + // v0 takes precedence over v1 + if p.v0Compatible { + return ast.RegoV0 + } + if p.v1Compatible { + return ast.RegoV1 + } + return ast.DefaultRegoVersion +} + +func initOracle(root *cobra.Command, brand string) { + + var findDefinitionParams findDefinitionParams + + var oracleCommand = &cobra.Command{ + Use: "oracle", + Short: "Answer questions about Rego", + Long: "Answer questions about Rego.", + Hidden: true, + } + + var findDefinitionCommand = &cobra.Command{ + Use: "find-definition", + Short: "Find the location of a definition", + Long: `Find the location of a definition. + +The 'find-definition' command outputs the location of the definition of the symbol +or value referred to by the location passed as a positional argument. The location +should be of the form: + + : + +The offset can be specified as a decimal or hexadecimal number. The output format +specifies the file, row, and column of the definition: + + { + "result": { + "file": "/path/to/some/policy.rego", + "row": 18, + "col": 1 + } + } + +If the 'find-definition' command cannot find a location it will print an error +reason. The exit status will be zero in this case: + + { + "error": "no match found" + } + +If an unexpected error occurs (e.g., a file read error) the subcommand will print +the error reason to stderr and exit with a non-zero status code. + +If the --stdin-buffer flag is supplied the 'find-definition' subcommand will +consume stdin and treat the bytes read as the content of the file referenced +by the input location.`, + PreRunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("expected exactly one position :") + } + if _, _, err := parseFilenameOffset(args[0]); err != nil { + return err + } + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + if err := dofindDefinition(findDefinitionParams, os.Stdin, os.Stdout, args); err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + return err + } + return nil + }, + } + + findDefinitionCommand.Flags().BoolVarP(&findDefinitionParams.stdinBuffer, "stdin-buffer", "", false, "read buffer from stdin") + addBundleFlag(findDefinitionCommand.Flags(), &findDefinitionParams.bundlePaths) + oracleCommand.AddCommand(findDefinitionCommand) + addV0CompatibleFlag(oracleCommand.Flags(), &findDefinitionParams.v0Compatible, false) + addV1CompatibleFlag(oracleCommand.Flags(), &findDefinitionParams.v1Compatible, false) + root.AddCommand(oracleCommand) +} + +func dofindDefinition(params findDefinitionParams, stdin io.Reader, stdout io.Writer, args []string) error { + + filename, offset, err := parseFilenameOffset(args[0]) + if err != nil { + return err + } + + var b *bundle.Bundle + + if len(params.bundlePaths.v) != 0 { + if len(params.bundlePaths.v) > 1 { + return errors.New("not implemented: multiple bundle paths") + } + b, err = loader.NewFileLoader(). + WithBundleLazyLoadingMode(bundle.HasExtension()). + WithSkipBundleVerification(true). + WithFilter(func(_ string, info os.FileInfo, _ int) bool { + // While directories may contain other things of interest for OPA (json, yaml..), + // only .rego will work reliably for the purpose of finding definitions + return strings.HasPrefix(info.Name(), ".rego") + }). + WithRegoVersion(params.regoVersion()). + AsBundle(params.bundlePaths.v[0]) + if err != nil { + return err + } + } + + modules := map[string]*ast.Module{} + + if b != nil { + for _, mf := range b.Modules { + modules[mf.Path] = mf.Parsed + } + } + + var bs []byte + + if params.stdinBuffer { + stat, err := os.Stdin.Stat() + if err != nil { + return err + } + // Only read from stdin when there is something actually there + if (stat.Mode() & os.ModeCharDevice) == 0 { + bs, err = io.ReadAll(stdin) + if err != nil { + return err + } + } + } + + // FindDefinition() will instantiate a new compiler, but we don't need to set the + // default rego-version because the passed modules already have the rego-version from parsing. + result, err := oracle.New().FindDefinition(oracle.DefinitionQuery{ + Buffer: bs, + Filename: filename, + Pos: offset, + Modules: modules, + }) + + if err != nil { + return presentation.JSON(stdout, map[string]any{ + "error": err, + }) + } + + return presentation.JSON(stdout, result) +} + +func parseFilenameOffset(s string) (string, int, error) { + s = strings.TrimPrefix(s, "file://") + + parts := strings.Split(s, ":") + if len(parts) != 2 { + return "", 0, errors.New("expected : argument") + } + + base := 10 + str := parts[1] + if strings.HasPrefix(parts[1], "0x") { + base = 16 + str = parts[1][2:] + } + + offset, err := strconv.ParseInt(str, base, 32) + if err != nil { + return "", 0, err + } + + return parts[0], int(offset), nil +} diff --git a/third_party/opa/cmd/oracle_test.go b/third_party/opa/cmd/oracle_test.go new file mode 100644 index 000000000000..4f90a2a61b25 --- /dev/null +++ b/third_party/opa/cmd/oracle_test.go @@ -0,0 +1,195 @@ +package cmd + +import ( + "bytes" + "errors" + "fmt" + "path" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestOracleFindDefinition(t *testing.T) { + cases := []struct { + note string + v0Compatible bool + onDiskModule string + stdin string + paths []string + }{ + { + note: "v0", + v0Compatible: true, + onDiskModule: `package test + +p { r } + +r = true`, + stdin: `package test + +p { q } + +q = true`, + paths: []string{ + "test.rego:10", + "test.rego:15", + "test.rego:18", + }, + }, + { + note: "v1", + onDiskModule: `package test + +p if { r } + +r = true`, + stdin: `package test + +p if { q } + +q = true`, + paths: []string{ + "test.rego:10", + "test.rego:15", + "test.rego:21", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + stdin := bytes.NewBufferString(tc.stdin) + + files := map[string]string{ + "test.rego": tc.onDiskModule, + "document.txt": "this should not be included", + "ignore.json": `{"neither": "should this"}`, + } + + test.WithTempFS(files, func(rootDir string) { + + params := findDefinitionParams{ + bundlePaths: repeatedStringFlag{ + v: []string{rootDir}, + isSet: true, + }, + stdinBuffer: true, + v0Compatible: tc.v0Compatible, + } + + stdout := bytes.NewBuffer(nil) + + err := dofindDefinition(params, stdin, stdout, []string{path.Join(rootDir, tc.paths[0])}) + expectJSON(t, err, stdout, `{"error": {"code": "oracle_no_match_found"}}`) + + err = dofindDefinition(params, stdin, stdout, []string{path.Join(rootDir, tc.paths[1])}) + expectJSON(t, err, stdout, `{"error": {"code": "oracle_no_definition_found"}}`) + + err = dofindDefinition(params, stdin, stdout, []string{path.Join(rootDir, tc.paths[2])}) + expectJSON(t, err, stdout, fmt.Sprintf(`{"result": { + "file": %q, + "row": 5, + "col": 1 + }}`, path.Join(rootDir, "test.rego"))) + }) + }) + } + +} + +func expectJSON(t *testing.T, err error, buffer *bytes.Buffer, exp string) { + t.Helper() + if err != nil { + t.Fatal(err) + } + var x any + if err := util.UnmarshalJSON(buffer.Bytes(), &x); err != nil { + t.Fatal(err) + } + var y any + if err := util.UnmarshalJSON([]byte(exp), &y); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(x, y) { + t.Fatalf("expected %v but got %v", y, x) + } + buffer.Reset() +} + +func TestOracleParseFilenameOffset(t *testing.T) { + + tests := []struct { + input string + wantFile string + wantPos int + }{ + { + input: "x.rego:10", + wantFile: "x.rego", + wantPos: 10, + }, + { + input: "/x.rego:10", + wantFile: "/x.rego", + wantPos: 10, + }, + { + input: "x.rego:0x10", + wantFile: "x.rego", + wantPos: 16, + }, + { + input: "file://x.rego:10", + wantFile: "x.rego", + wantPos: 10, + }, + } + + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + filename, pos, err := parseFilenameOffset(tc.input) + if err != nil { + t.Fatal(err) + } + if tc.wantFile != filename || tc.wantPos != pos { + t.Fatalf("expected %v:%v but got %v:%v", tc.wantFile, tc.wantPos, filename, pos) + } + }) + } + +} + +func TestOracleParseFilenameOffsetError(t *testing.T) { + + tests := []struct { + input string + wantErr error + }{ + { + input: "x.rego", + wantErr: errors.New("expected : argument"), + }, + { + input: "x.rego:", + wantErr: errors.New("invalid syntax"), + }, + { + input: "x.rego:3.14", + wantErr: errors.New("invalid syntax"), + }, + } + + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + _, _, err := parseFilenameOffset(tc.input) + if err == nil || !strings.Contains(err.Error(), tc.wantErr.Error()) { + t.Fatalf("expected %v but got %v", tc.wantErr, err) + } + }) + } + +} diff --git a/third_party/opa/cmd/parse.go b/third_party/opa/cmd/parse.go new file mode 100644 index 000000000000..9847cfd66316 --- /dev/null +++ b/third_party/opa/cmd/parse.go @@ -0,0 +1,149 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "os" + "strings" + + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/cmd/internal/env" + pr "github.com/open-policy-agent/opa/internal/presentation" + "github.com/open-policy-agent/opa/v1/ast" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/util" +) + +type parseParams struct { + format *util.EnumFlag + jsonInclude string + v0Compatible bool + v1Compatible bool +} + +func (p *parseParams) regoVersion() ast.RegoVersion { + // the '--v0--compatible' flag takes precedence over the '--v1-compatible' flag + if p.v0Compatible { + return ast.RegoV0 + } + if p.v1Compatible { + return ast.RegoV1 + } + return ast.DefaultRegoVersion +} + +var configuredParseParams = parseParams{ + format: formats.Flag(formats.Pretty, formats.JSON), + jsonInclude: "", +} + +func parse(args []string, params *parseParams, stdout io.Writer, stderr io.Writer) int { + if len(args) == 0 { + return 0 + } + + exposeLocation := false + exposeComments := true + for _, opt := range strings.Split(params.jsonInclude, ",") { + value := !strings.HasPrefix(opt, "-") + + if strings.HasSuffix(opt, "locations") { + exposeLocation = value + } + if strings.HasSuffix(opt, "comments") { + exposeComments = value + } + } + + parserOpts := ast.ParserOptions{ + ProcessAnnotation: true, + RegoVersion: params.regoVersion(), + } + if exposeLocation { + astJSON.SetOptions(astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocationText: true, + IncludeLocation: astJSON.NodeToggle{ + Term: true, + Package: true, + Comment: true, + Import: true, + Rule: true, + Head: true, + Expr: true, + SomeDecl: true, + Every: true, + With: true, + Annotations: true, + AnnotationsRef: true, + }, + }, + }) + defer astJSON.SetOptions(astJSON.Defaults()) + } + + result, err := loader.RegoWithOpts(args[0], parserOpts) + if err != nil { + _ = pr.JSON(stderr, pr.Output{Errors: pr.NewOutputErrors(err)}) + return 1 + } + + if !exposeComments { + result.Parsed.Comments = nil + } + + switch params.format.String() { + case formats.JSON: + bs, err := json.MarshalIndent(result.Parsed, "", " ") + if err != nil { + _ = pr.JSON(stderr, pr.Output{Errors: pr.NewOutputErrors(err)}) + return 1 + } + + _, _ = fmt.Fprint(stdout, string(bs)+"\n") + default: + ast.Pretty(stdout, result.Parsed) + } + + return 0 +} + +func initParse(root *cobra.Command, _ string) { + parseCommand := &cobra.Command{ + Use: "parse ", + Short: "Parse Rego source file", + Long: `Parse Rego source file and print AST.`, + PreRunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("no source file specified") + } + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + exit := parse(args, &configuredParseParams, os.Stdout, os.Stderr) + if exit != 0 { + return newExitError(exit) + } + return nil + }, + } + + addOutputFormat(parseCommand.Flags(), configuredParseParams.format) + parseCommand.Flags().StringVarP(&configuredParseParams.jsonInclude, "json-include", "", "", "include or exclude optional elements. By default comments are included. Current options: locations, comments. E.g. --json-include locations,-comments will include locations and exclude comments.") + addV1CompatibleFlag(parseCommand.Flags(), &configuredParseParams.v1Compatible, false) + addV0CompatibleFlag(parseCommand.Flags(), &configuredParseParams.v0Compatible, false) + + root.AddCommand(parseCommand) +} diff --git a/third_party/opa/cmd/parse_test.go b/third_party/opa/cmd/parse_test.go new file mode 100644 index 000000000000..acc59582a4dd --- /dev/null +++ b/third_party/opa/cmd/parse_test.go @@ -0,0 +1,1200 @@ +package cmd + +import ( + "bytes" + "path/filepath" + "strings" + "testing" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestParseExit0(t *testing.T) { + + files := map[string]string{ + "x.rego": `package x + + p = 1 + `, + } + errc, stdout, stderr, _ := testParse(t, files, &configuredParseParams) + if errc != 0 { + t.Fatalf("Expected exit code 0, got %v", errc) + } + if len(stderr) > 0 { + t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr)) + } + + expectedOutput := `module + package + ref + data + "x" + rule + head + ref + p + 1 + body + expr index=0 + true +` + + if got, want := string(stdout), expectedOutput; got != want { + t.Fatalf("Expected output\n%v\n, got\n%v", want, got) + } +} + +func TestParseExit1(t *testing.T) { + + files := map[string]string{ + "x.rego": `???`, + } + errc, _, stderr, _ := testParse(t, files, &configuredParseParams) + if errc != 1 { + t.Fatalf("Expected exit code 1, got %v", errc) + } + if len(stderr) == 0 { + t.Fatalf("Expected output in stderr") + } +} + +func TestParseJSONOutput(t *testing.T) { + + files := map[string]string{ + "x.rego": `package x + + p = 1 + `, + } + errc, stdout, stderr, _ := testParse(t, files, &parseParams{ + format: formats.Flag(formats.JSON, formats.Pretty), + }) + if errc != 0 { + t.Fatalf("Expected exit code 0, got %v", errc) + } + if len(stderr) > 0 { + t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr)) + } + + expectedOutput := `{ + "package": { + "path": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "x" + } + ] + }, + "rules": [ + { + "body": [ + { + "index": 0, + "terms": { + "type": "boolean", + "value": true + } + } + ], + "head": { + "name": "p", + "value": { + "type": "number", + "value": 1 + }, + "ref": [ + { + "type": "var", + "value": "p" + } + ] + } + } + ] +} +` + + if got, want := string(stdout), expectedOutput; got != want { + t.Fatalf("Expected output\n%v\n, got\n%v", want, got) + } +} + +func TestParseJSONOutputWithLocations(t *testing.T) { + + files := map[string]string{ + "x.rego": `package x + +p = 1 +`, + } + errc, stdout, stderr, tempDirPath := testParse(t, files, &parseParams{ + format: formats.Flag(formats.JSON, formats.Pretty), + jsonInclude: "locations", + }) + if errc != 0 { + t.Fatalf("Expected exit code 0, got %v", errc) + } + if len(stderr) > 0 { + t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr)) + } + + expectedOutput := strings.ReplaceAll(`{ + "package": { + "location": { + "file": "TEMPDIR/x.rego", + "row": 1, + "col": 1, + "text": "cGFja2FnZQ==" + }, + "path": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 1, + "col": 9, + "text": "eA==" + }, + "type": "var", + "value": "data" + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 1, + "col": 9, + "text": "eA==" + }, + "type": "string", + "value": "x" + } + ] + }, + "rules": [ + { + "body": [ + { + "index": 0, + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 5, + "text": "MQ==" + }, + "terms": { + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 5, + "text": "MQ==" + }, + "type": "boolean", + "value": true + } + } + ], + "head": { + "name": "p", + "value": { + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 5, + "text": "MQ==" + }, + "type": "number", + "value": 1 + }, + "ref": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 1, + "text": "cA==" + }, + "type": "var", + "value": "p" + } + ], + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 1, + "text": "cCA9IDE=" + } + }, + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 1, + "text": "cCA9IDE=" + } + } + ] +} +`, "TEMPDIR", tempDirPath) + + gotLines := strings.Split(string(stdout), "\n") + wantLines := strings.Split(expectedOutput, "\n") + min := len(gotLines) + if len(wantLines) < min { + min = len(wantLines) + } + + for i := range min { + if gotLines[i] != wantLines[i] { + t.Fatalf("Expected line %d to be\n%v\n, got\n%v", i, wantLines[i], gotLines[i]) + } + } + + if len(gotLines) != len(wantLines) { + t.Fatalf("Expected %d lines, got %d", len(wantLines), len(gotLines)) + } +} + +func TestParseRefsJSONOutput(t *testing.T) { + + files := map[string]string{ + "x.rego": `package x + + a.b.c := true + `, + } + errc, stdout, stderr, _ := testParse(t, files, &parseParams{ + format: formats.Flag(formats.JSON, formats.Pretty), + }) + if errc != 0 { + t.Fatalf("Expected exit code 0, got %v", errc) + } + if len(stderr) > 0 { + t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr)) + } + + expectedOutput := `{ + "package": { + "path": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "x" + } + ] + }, + "rules": [ + { + "body": [ + { + "index": 0, + "terms": { + "type": "boolean", + "value": true + } + } + ], + "head": { + "value": { + "type": "boolean", + "value": true + }, + "assign": true, + "ref": [ + { + "type": "var", + "value": "a" + }, + { + "type": "string", + "value": "b" + }, + { + "type": "string", + "value": "c" + } + ] + } + } + ] +} +` + + if got, want := string(stdout), expectedOutput; got != want { + t.Fatalf("Expected output\n%v\n, got\n%v", want, got) + } +} + +func TestParseRefsJSONOutputWithLocations(t *testing.T) { + + files := map[string]string{ + "x.rego": `package x + +a.b.c := true +`, + } + errc, stdout, stderr, tempDirPath := testParse(t, files, &parseParams{ + format: formats.Flag(formats.JSON, formats.Pretty), + jsonInclude: "locations", + }) + if errc != 0 { + t.Fatalf("Expected exit code 0, got %v", errc) + } + if len(stderr) > 0 { + t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr)) + } + + expectedOutput := strings.ReplaceAll(`{ + "package": { + "location": { + "file": "TEMPDIR/x.rego", + "row": 1, + "col": 1, + "text": "cGFja2FnZQ==" + }, + "path": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 1, + "col": 9, + "text": "eA==" + }, + "type": "var", + "value": "data" + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 1, + "col": 9, + "text": "eA==" + }, + "type": "string", + "value": "x" + } + ] + }, + "rules": [ + { + "body": [ + { + "index": 0, + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 10, + "text": "dHJ1ZQ==" + }, + "terms": { + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 10, + "text": "dHJ1ZQ==" + }, + "type": "boolean", + "value": true + } + } + ], + "head": { + "value": { + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 10, + "text": "dHJ1ZQ==" + }, + "type": "boolean", + "value": true + }, + "assign": true, + "ref": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 1, + "text": "YQ==" + }, + "type": "var", + "value": "a" + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 3, + "text": "Yg==" + }, + "type": "string", + "value": "b" + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 5, + "text": "Yw==" + }, + "type": "string", + "value": "c" + } + ], + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 1, + "text": "YS5iLmMgOj0gdHJ1ZQ==" + } + }, + "location": { + "file": "TEMPDIR/x.rego", + "row": 3, + "col": 1, + "text": "YS5iLmMgOj0gdHJ1ZQ==" + } + } + ] +} +`, "TEMPDIR", tempDirPath) + + gotLines := strings.Split(string(stdout), "\n") + wantLines := strings.Split(expectedOutput, "\n") + min := len(gotLines) + if len(wantLines) < min { + min = len(wantLines) + } + + for i := range min { + if gotLines[i] != wantLines[i] { + t.Fatalf("Expected line %d to be\n%v\n, got\n%v", i, wantLines[i], gotLines[i]) + } + } + + if len(gotLines) != len(wantLines) { + t.Fatalf("Expected %d lines, got %d", len(wantLines), len(gotLines)) + } +} +func TestParseRulesBlockJSONOutputWithLocations(t *testing.T) { + + files := map[string]string{ + "x.rego": `package x +import rego.v1 + +default allow = false +allow = true if { + input.method == "GET" + input.path = ["getUser", user] + input.user == user +} +`, + } + errc, stdout, stderr, tempDirPath := testParse(t, files, &parseParams{ + format: formats.Flag(formats.JSON, formats.Pretty), + jsonInclude: "locations", + }) + if errc != 0 { + t.Fatalf("Expected exit code 0, got %v", errc) + } + if len(stderr) > 0 { + t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr)) + } + + expectedOutput := strings.ReplaceAll(`{ + "package": { + "location": { + "file": "TEMPDIR/x.rego", + "row": 1, + "col": 1, + "text": "cGFja2FnZQ==" + }, + "path": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 1, + "col": 9, + "text": "eA==" + }, + "type": "var", + "value": "data" + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 1, + "col": 9, + "text": "eA==" + }, + "type": "string", + "value": "x" + } + ] + }, + "imports": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 2, + "col": 1, + "text": "aW1wb3J0" + }, + "path": { + "location": { + "file": "TEMPDIR/x.rego", + "row": 2, + "col": 8, + "text": "cmVnby52MQ==" + }, + "type": "ref", + "value": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 2, + "col": 8, + "text": "cmVnbw==" + }, + "type": "var", + "value": "rego" + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 2, + "col": 13, + "text": "djE=" + }, + "type": "string", + "value": "v1" + } + ] + } + } + ], + "rules": [ + { + "body": [ + { + "index": 0, + "location": { + "file": "TEMPDIR/x.rego", + "row": 4, + "col": 1, + "text": "ZGVmYXVsdA==" + }, + "terms": { + "location": { + "file": "TEMPDIR/x.rego", + "row": 4, + "col": 1, + "text": "ZGVmYXVsdA==" + }, + "type": "boolean", + "value": true + } + } + ], + "default": true, + "head": { + "name": "allow", + "value": { + "location": { + "file": "TEMPDIR/x.rego", + "row": 4, + "col": 17, + "text": "ZmFsc2U=" + }, + "type": "boolean", + "value": false + }, + "ref": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 4, + "col": 9, + "text": "YWxsb3c=" + }, + "type": "var", + "value": "allow" + } + ], + "location": { + "file": "TEMPDIR/x.rego", + "row": 4, + "col": 9, + "text": "YWxsb3cgPSBmYWxzZQ==" + } + }, + "location": { + "file": "TEMPDIR/x.rego", + "row": 4, + "col": 1, + "text": "ZGVmYXVsdA==" + } + }, + { + "body": [ + { + "index": 0, + "location": { + "file": "TEMPDIR/x.rego", + "row": 6, + "col": 3, + "text": "aW5wdXQubWV0aG9kID09ICJHRVQi" + }, + "terms": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 6, + "col": 16, + "text": "PT0=" + }, + "type": "ref", + "value": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 6, + "col": 16, + "text": "PT0=" + }, + "type": "var", + "value": "equal" + } + ] + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 6, + "col": 3, + "text": "aW5wdXQubWV0aG9k" + }, + "type": "ref", + "value": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 6, + "col": 3, + "text": "aW5wdXQ=" + }, + "type": "var", + "value": "input" + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 6, + "col": 9, + "text": "bWV0aG9k" + }, + "type": "string", + "value": "method" + } + ] + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 6, + "col": 19, + "text": "IkdFVCI=" + }, + "type": "string", + "value": "GET" + } + ] + }, + { + "index": 1, + "location": { + "file": "TEMPDIR/x.rego", + "row": 7, + "col": 3, + "text": "aW5wdXQucGF0aCA9IFsiZ2V0VXNlciIsIHVzZXJd" + }, + "terms": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 7, + "col": 14, + "text": "PQ==" + }, + "type": "ref", + "value": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 7, + "col": 14, + "text": "PQ==" + }, + "type": "var", + "value": "eq" + } + ] + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 7, + "col": 3, + "text": "aW5wdXQucGF0aA==" + }, + "type": "ref", + "value": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 7, + "col": 3, + "text": "aW5wdXQ=" + }, + "type": "var", + "value": "input" + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 7, + "col": 9, + "text": "cGF0aA==" + }, + "type": "string", + "value": "path" + } + ] + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 7, + "col": 16, + "text": "WyJnZXRVc2VyIiwgdXNlcl0=" + }, + "type": "array", + "value": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 7, + "col": 17, + "text": "ImdldFVzZXIi" + }, + "type": "string", + "value": "getUser" + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 7, + "col": 28, + "text": "dXNlcg==" + }, + "type": "var", + "value": "user" + } + ] + } + ] + }, + { + "index": 2, + "location": { + "file": "TEMPDIR/x.rego", + "row": 8, + "col": 3, + "text": "aW5wdXQudXNlciA9PSB1c2Vy" + }, + "terms": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 8, + "col": 14, + "text": "PT0=" + }, + "type": "ref", + "value": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 8, + "col": 14, + "text": "PT0=" + }, + "type": "var", + "value": "equal" + } + ] + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 8, + "col": 3, + "text": "aW5wdXQudXNlcg==" + }, + "type": "ref", + "value": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 8, + "col": 3, + "text": "aW5wdXQ=" + }, + "type": "var", + "value": "input" + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 8, + "col": 9, + "text": "dXNlcg==" + }, + "type": "string", + "value": "user" + } + ] + }, + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 8, + "col": 17, + "text": "dXNlcg==" + }, + "type": "var", + "value": "user" + } + ] + } + ], + "head": { + "name": "allow", + "value": { + "location": { + "file": "TEMPDIR/x.rego", + "row": 5, + "col": 9, + "text": "dHJ1ZQ==" + }, + "type": "boolean", + "value": true + }, + "ref": [ + { + "location": { + "file": "TEMPDIR/x.rego", + "row": 5, + "col": 1, + "text": "YWxsb3c=" + }, + "type": "var", + "value": "allow" + } + ], + "location": { + "file": "TEMPDIR/x.rego", + "row": 5, + "col": 1, + "text": "YWxsb3cgPSB0cnVl" + } + }, + "location": { + "file": "TEMPDIR/x.rego", + "row": 5, + "col": 1, + "text": "YWxsb3cgPSB0cnVlIGlmIHsKICBpbnB1dC5tZXRob2QgPT0gIkdFVCIKICBpbnB1dC5wYXRoID0gWyJnZXRVc2VyIiwgdXNlcl0KICBpbnB1dC51c2VyID09IHVzZXIKfQ==" + } + } + ] +} +`, "TEMPDIR", tempDirPath) + + gotLines := strings.Split(string(stdout), "\n") + wantLines := strings.Split(expectedOutput, "\n") + min := len(gotLines) + if len(wantLines) < min { + min = len(wantLines) + } + + for i := range min { + if gotLines[i] != wantLines[i] { + t.Fatalf("Expected line %d to be\n%v\n, got\n%v", i, wantLines[i], gotLines[i]) + } + } + + if len(gotLines) != len(wantLines) { + t.Fatalf("Expected %d lines, got %d", len(wantLines), len(gotLines)) + } +} + +func TestParseJSONOutputComments(t *testing.T) { + + files := map[string]string{ + "x.rego": `package x + + # comment + p = 1 + `, + } + errc, stdout, stderr, _ := testParse(t, files, &parseParams{ + format: formats.Flag(formats.JSON, formats.Pretty), + jsonInclude: "comments", + }) + if errc != 0 { + t.Fatalf("Expected exit code 0, got %v", errc) + } + if len(stderr) > 0 { + t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr)) + } + + expectedCommentTextValue := "IGNvbW1lbnQ=" + + if !strings.Contains(string(stdout), expectedCommentTextValue) { + t.Fatalf("Comment text value %q missing in output: %s", expectedCommentTextValue, string(stdout)) + } +} + +func TestParse_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0 module", + module: `package test +a[x] { + x := 42 +}`, + expErrs: []string{ + "`if` keyword is required before rule body", + "`contains` keyword is required for partial set rules", + }, + }, + { + note: "v1 module", + module: `package test +a contains x if { + x := 42 +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.module, + } + + _, _, stderr, _ := testParse(t, files, &parseParams{ + format: formats.Flag(formats.Pretty, formats.JSON), + }) + + if len(tc.expErrs) > 0 { + errs := string(stderr) + for _, expErr := range tc.expErrs { + if !strings.Contains(errs, expErr) { + t.Fatalf("Expected error:\n\n%q\n\ngot:\n\n%s", expErr, errs) + } + } + } else if len(stderr) > 0 { + t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr)) + } + }) + } +} + +func TestParseCompatibleFlags(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + v1Compatible bool + policy string + expErrs []string + }{ + { + note: "v0, keywords not used", + v0Compatible: true, + policy: `package test +p[v] { + v := input.x +}`, + }, + { + note: "v0, keywords not imported", + v0Compatible: true, + policy: `package test +p contains v if { + v := input.x +}`, + expErrs: []string{ + "var cannot be used for rule name", + }, + }, + { + note: "v0, keywords imported", + v0Compatible: true, + policy: `package test +import future.keywords +p contains v if { + v := input.x +}`, + }, + { + note: "v0, rego.v1 imported", + v0Compatible: true, + policy: `package test +import rego.v1 +p contains v if { + v := input.x +}`, + }, + + { + note: "v1, keywords not used", + v1Compatible: true, + policy: `package test +p[v] { + v := input.x +}`, + expErrs: []string{ + "`if` keyword is required before rule body", + "`contains` keyword is required for partial set rules", + }, + }, + { + note: "v1, keywords not imported", + v1Compatible: true, + policy: `package test +p contains v if { + v := input.x +}`, + }, + { + note: "v1, keywords imported", + v1Compatible: true, + policy: `package test +import future.keywords +p contains v if { + v := input.x +}`, + }, + { + note: "v1, rego.v1 imported", + v1Compatible: true, + policy: `package test +import rego.v1 +p contains v if { + v := input.x +}`, + }, + + // v0 takes precedence over v1 + { + note: "v0+v1, keywords not used", + v0Compatible: true, + v1Compatible: true, + policy: `package test +p[v] { + v := input.x +}`, + }, + { + note: "v0+v1, keywords not imported", + v0Compatible: true, + v1Compatible: true, + policy: `package test +p contains v if { + v := input.x +}`, + expErrs: []string{ + "var cannot be used for rule name", + }, + }, + { + note: "v0+1, keywords imported", + v0Compatible: true, + v1Compatible: true, + policy: `package test +import future.keywords +p contains v if { + v := input.x +}`, + }, + { + note: "v0+v1, rego.v1 imported", + v0Compatible: true, + v1Compatible: true, + policy: `package test +import rego.v1 +p contains v if { + v := input.x +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "policy.rego": tc.policy, + } + + _, _, stderr, _ := testParse(t, files, &parseParams{ + format: formats.Flag(formats.Pretty, formats.JSON), + v0Compatible: tc.v0Compatible, + v1Compatible: tc.v1Compatible, + }) + + if len(tc.expErrs) > 0 { + errs := string(stderr) + for _, expErr := range tc.expErrs { + if !strings.Contains(errs, expErr) { + t.Fatalf("Expected error:\n\n%q\n\ngot:\n\n%s", expErr, errs) + } + } + } else if len(stderr) > 0 { + t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr)) + } + }) + } +} + +// Runs parse and returns the exit code, stdout, and stderr contents +func testParse(t *testing.T, files map[string]string, params *parseParams) (int, []byte, []byte, string) { + t.Helper() + + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + var errc int + + var tempDirUsed string + test.WithTempFS(files, func(path string) { + var args []string + for file := range files { + args = append(args, filepath.Join(path, file)) + } + errc = parse(args, params, stdout, stderr) + + tempDirUsed = path + }) + + return errc, stdout.Bytes(), stderr.Bytes(), tempDirUsed +} diff --git a/third_party/opa/cmd/refactor.go b/third_party/opa/cmd/refactor.go new file mode 100644 index 000000000000..c900ee4bd32c --- /dev/null +++ b/third_party/opa/cmd/refactor.go @@ -0,0 +1,202 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "errors" + "fmt" + "io" + "os" + + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/internal/env" + fileurl "github.com/open-policy-agent/opa/internal/file/url" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/format" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/refactor" +) + +type moveCommandParams struct { + mapping repeatedStringFlag + ignore []string + overwrite bool + v0Compatible bool + v1Compatible bool +} + +func (m *moveCommandParams) regoVersion() ast.RegoVersion { + // v0 takes precedence over v1 + if m.v0Compatible { + return ast.RegoV0 + } + if m.v1Compatible { + return ast.RegoV1 + } + return ast.DefaultRegoVersion +} + +func initRefactor(root *cobra.Command, brand string) { + executable := root.Name() + + var moveCommandParams moveCommandParams + + var refactorCommand = &cobra.Command{ + Use: "refactor", + Short: "Refactor Rego file(s)", + Hidden: true, + } + + var moveCommand = &cobra.Command{ + Use: "move [file-path [...]]", + Short: "Rename packages and their references in Rego file(s)", + Long: `Rename packages and their references in Rego file(s). + +The 'move' command takes one or more Rego source file(s) and rewrites package paths and other references in them as per +the mapping defined by the '-p' option. At least one mapping should be provided and should be of the form: + + : + +The 'move' command formats the Rego modules after renaming packages, etc. and prints the formatted modules to stdout by default. +If the '-w' option is supplied, the 'move' command will overwrite the source file instead. + +Example: +-------- + +"policy.rego" contains the below policy: + _ _ _ _ _ _ _ _ _ _ _ _ _ +| package lib.foo | +| | +| default allow = false | +| _ _ _ _ _ _ _ _ _ _ _ _ | + + $ ` + executable + ` refactor move -p data.lib.foo:data.baz.bar policy.rego + +The 'move' command outputs the below policy to stdout with the package name rewritten as per the mapping: + + _ _ _ _ _ _ _ _ _ _ _ _ _ +| package baz.bar | +| | +| default allow = false | +| _ _ _ _ _ _ _ _ _ _ _ _ | +`, + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := validateMoveArgs(args); err != nil { + return err + } + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + if err := doMove(moveCommandParams, args, os.Stdout); err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + return err + } + return nil + }, + } + + moveCommand.Flags().VarP(&moveCommandParams.mapping, "path", "p", "set the mapping that defines how references should be rewritten (ie. :). This flag can be repeated.") + moveCommand.Flags().BoolVarP(&moveCommandParams.overwrite, "write", "w", false, "overwrite the original source file") + addIgnoreFlag(moveCommand.Flags(), &moveCommandParams.ignore) + refactorCommand.AddCommand(moveCommand) + addV0CompatibleFlag(moveCommand.Flags(), &moveCommandParams.v0Compatible, false) + addV1CompatibleFlag(moveCommand.Flags(), &moveCommandParams.v1Compatible, false) + root.AddCommand(refactorCommand) +} + +func doMove(params moveCommandParams, args []string, out io.Writer) error { + if len(params.mapping.v) == 0 { + return errors.New("specify at least one mapping of the form :") + } + + srcDstMap, err := parseSrcDstMap(params.mapping.v) + if err != nil { + return err + } + + result, err := loader.NewFileLoader(). + WithBundleLazyLoadingMode(bundle.HasExtension()). + WithRegoVersion(params.regoVersion()). + Filtered(args, ignored(params.ignore).Apply) + if err != nil { + return err + } + + mq := refactor.MoveQuery{ + Modules: result.ParsedModules(), + SrcDstMapping: srcDstMap, + }.WithValidation(true) + + movedModules, err := refactor.New().Move(mq) + if err != nil { + return err + } + + for filename, mod := range movedModules.Result { + filename, err = fileurl.Clean(filename) + if err != nil { + return err + } + + formatted, err := format.AstWithOpts(mod, format.Opts{RegoVersion: params.regoVersion()}) + if err != nil { + return newError("failed to parse Rego source file: %v", err) + } + + if params.overwrite { + info, err := os.Stat(filename) + if err != nil { + return err + } + + outfile, err := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC, info.Mode()) + if err != nil { + return newError("failed to open file for writing: %v", err) + } + defer outfile.Close() + out = outfile + } + + _, err = out.Write(formatted) + if err != nil { + return newError("failed writing formatted contents: %v", err) + } + } + + return nil +} + +func parseSrcDstMap(data []string) (map[string]string, error) { + result := map[string]string{} + + for _, d := range data { + term, err := ast.ParseTerm("{" + d + "}") + if err != nil { + return nil, newError("failed to parse mapping: %v", err) + } + obj, ok := term.Value.(ast.Object) + if !ok { + return nil, newError("expected mapping of the form :") + } + keys := obj.Keys() + if len(keys) != 1 { + return nil, newError("expected mapping of the form :") + } + result[keys[0].String()] = obj.Get(keys[0]).String() + } + return result, nil +} + +func validateMoveArgs(args []string) error { + if len(args) == 0 { + return errors.New("specify at least one path containing policy files") + } + return nil +} diff --git a/third_party/opa/cmd/refactor_test.go b/third_party/opa/cmd/refactor_test.go new file mode 100644 index 000000000000..ff8c03c693b3 --- /dev/null +++ b/third_party/opa/cmd/refactor_test.go @@ -0,0 +1,270 @@ +package cmd + +import ( + "bytes" + "maps" + "os" + "path/filepath" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/format" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestDoMoveRenamePackage(t *testing.T) { + cases := []struct { + note string + v0Compatible bool + module string + expected *ast.Module + }{ + { + note: "v0", + v0Compatible: true, + module: `package lib.foo + + # this is a comment + default allow = false + + allow { + input.message == "hello" # this is a comment too + }`, + expected: ast.MustParseModuleWithOpts(`package baz.bar + + # this is a comment + default allow = false + + allow { + input.message == "hello" # this is a comment too + }`, ast.ParserOptions{RegoVersion: ast.RegoV0}), + }, + { + note: "v1", + module: `package lib.foo + + # this is a comment + default allow = false + + allow if { + input.message == "hello" # this is a comment too + }`, + expected: ast.MustParseModuleWithOpts(`package baz.bar + + # this is a comment + default allow = false + + allow if { + input.message == "hello" # this is a comment too + }`, ast.ParserOptions{RegoVersion: ast.RegoV1}), + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "policy.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + + mappings := []string{"data.lib.foo:data.baz.bar"} + + params := moveCommandParams{ + mapping: newrepeatedStringFlag(mappings), + v0Compatible: tc.v0Compatible, + } + + var buf bytes.Buffer + + err := doMove(params, []string{path}, &buf) + if err != nil { + t.Fatal(err) + } + + var formatted []byte + if tc.v0Compatible { + formatted = format.MustAstWithOpts(tc.expected, format.Opts{RegoVersion: ast.RegoV0}) + } else { + formatted = format.MustAstWithOpts(tc.expected, format.Opts{RegoVersion: ast.RegoV1}) + } + + if !bytes.Equal(formatted, buf.Bytes()) { + t.Fatalf("Expected module:\n%v\n\nGot:\n%v\n", string(formatted), buf.String()) + } + }) + }) + } +} + +func TestDoMoveOverwriteFile(t *testing.T) { + cases := []struct { + note string + v0Compatible bool + module string + expected *ast.Module + }{ + { + note: "v0", + v0Compatible: true, + module: `package lib.foo + + import data.x.q + + default allow := false + + allow { + input.message == "hello" + } + `, + expected: ast.MustParseModuleWithOpts(`package baz.bar + + import data.hidden.q + + default allow := false + + allow { + input.message == "hello" + }`, ast.ParserOptions{RegoVersion: ast.RegoV0}), + }, + { + note: "v1", + module: `package lib.foo + + import data.x.q + + default allow := false + + allow if { + input.message == "hello" + } + `, + expected: ast.MustParseModuleWithOpts(`package baz.bar + + import data.hidden.q + + default allow := false + + allow if { + input.message == "hello" + }`, ast.ParserOptions{RegoVersion: ast.RegoV1}), + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "policy.rego": tc.module, + } + + test.WithTempFS(files, func(path string) { + + mappings := []string{"data.lib.foo:data.baz.bar", "data.x: data.hidden"} + + params := moveCommandParams{ + mapping: newrepeatedStringFlag(mappings), + overwrite: true, + v0Compatible: tc.v0Compatible, + } + + var buf bytes.Buffer + + err := doMove(params, []string{path}, &buf) + if err != nil { + t.Fatal(err) + } + + data, err := os.ReadFile(filepath.Join(path, "policy.rego")) + if err != nil { + t.Fatal(err) + } + + var actual *ast.Module + if tc.v0Compatible { + actual = ast.MustParseModuleWithOpts(string(data), ast.ParserOptions{RegoVersion: ast.RegoV0}) + } else { + actual = ast.MustParseModuleWithOpts(string(data), ast.ParserOptions{RegoVersion: ast.RegoV1}) + } + + if !tc.expected.Equal(actual) { + t.Fatalf("Expected module:\n%v\n\nGot:\n%v\n", tc.expected, actual) + } + }) + }) + } +} + +func TestParseSrcDstMap(t *testing.T) { + actual, err := parseSrcDstMap([]string{"data.lib.foo:data.baz.bar", "data:data.acme"}) + if err != nil { + t.Fatal(err) + } + + expected := map[string]string{"data.lib.foo": "data.baz.bar", "data": "data.acme"} + + if !maps.Equal(actual, expected) { + t.Fatalf("Expected mapping %v but got %v", expected, actual) + } + + actual, err = parseSrcDstMap([]string{`data.foo:data.bar["baz:qux"]`}) + if err != nil { + t.Fatal(err) + } + + expected = map[string]string{"data.foo": `data.bar["baz:qux"]`} + if !maps.Equal(actual, expected) { + t.Fatalf("Expected mapping %v but got %v", expected, actual) + } + + _, err = parseSrcDstMap([]string{"data.lib.foo:data.baz.bar", "data::data.acme"}) + if err == nil { + t.Fatal("Expected error but got nil") + } + + _, err = parseSrcDstMap([]string{"data.lib.foo:data.baz.bar", "data%data.acme:foo:bar"}) + if err == nil { + t.Fatal("Expected error but got nil") + } + + _, err = parseSrcDstMap([]string{"[1]"}) // invalid term type + if err == nil { + t.Fatal("Expected error but got nil") + } + _, err = parseSrcDstMap([]string{"[1,"}) // parse error + if err == nil { + t.Fatal("Expected error but got nil") + } + _, err = parseSrcDstMap([]string{`{"data.a:data.b, data.c:data.d}`}) // multiple mappings + if err == nil { + t.Fatal("Expected error but got nil") + } +} + +func TestDoMoveNoMapping(t *testing.T) { + err := doMove(moveCommandParams{}, []string{}, os.Stdout) + if err == nil { + t.Fatal("Expected error but got nil") + } + + msg := "specify at least one mapping of the form :" + if err.Error() != msg { + t.Fatalf("Expected error %v but got %v", msg, err.Error()) + } +} + +func TestValidateMoveArgs(t *testing.T) { + err := validateMoveArgs([]string{}) + if err == nil { + t.Fatal("Expected error but got nil") + } + + msg := "specify at least one path containing policy files" + if err.Error() != msg { + t.Fatalf("Expected error %v but got %v", msg, err.Error()) + } + + err = validateMoveArgs([]string{"foo"}) + if err != nil { + t.Fatal(err) + } +} diff --git a/third_party/opa/cmd/run.go b/third_party/opa/cmd/run.go new file mode 100644 index 000000000000..d74e861e03ef --- /dev/null +++ b/third_party/opa/cmd/run.go @@ -0,0 +1,467 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "context" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "os" + "path" + "slices" + "strings" + "time" + + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/internal/env" + fileurl "github.com/open-policy-agent/opa/internal/file/url" + "github.com/open-policy-agent/opa/v1/runtime" + "github.com/open-policy-agent/opa/v1/server" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + defaultAddr = ":8181" // default listening address for server + defaultLocalAddr = "localhost:8181" // default listening address for server bound to localhost + defaultHistoryFile = ".opa_history" // default filename for shell history +) + +type runCmdParams struct { + rt runtime.Params + tlsCertFile string + tlsPrivateKeyFile string + tlsCACertFile string + tlsCertRefresh time.Duration + ignore []string + serverMode bool + skipVersionCheck bool // skipVersionCheck is deprecated. Use disableTelemetry instead + disableTelemetry bool + authentication *util.EnumFlag + authorization *util.EnumFlag + minTLSVersion *util.EnumFlag + logLevel *util.EnumFlag + logFormat *util.EnumFlag + logTimestampFormat string + algorithm string + scope string + pubKey string + pubKeyID string + skipBundleVerify bool + skipKnownSchemaCheck bool + excludeVerifyFiles []string + cipherSuites []string +} + +func newRunParams() runCmdParams { + return runCmdParams{ + rt: runtime.NewParams(), + authentication: util.NewEnumFlag("off", []string{"token", "tls", "off"}), + authorization: util.NewEnumFlag("off", []string{"basic", "off"}), + minTLSVersion: util.NewEnumFlag("1.2", []string{"1.0", "1.1", "1.2", "1.3"}), + logLevel: util.NewEnumFlag("info", []string{"debug", "info", "error"}), + logFormat: util.NewEnumFlag("json", []string{"text", "json", "json-pretty"}), + } +} + +func initRun(root *cobra.Command, brand string) { + executable := root.Name() + cmdParams := newRunParams() + + runCommand := &cobra.Command{ + Use: "run", + Short: `Start ` + brand + ` in interactive or server mode`, + Long: `Start an instance of ` + brand + `. + +To run the interactive shell: + + $ ` + executable + ` run + +To run the server: + + $ ` + executable + ` run -s + +The 'run' command starts an instance of the ` + brand + ` runtime. The ` + brand + ` runtime can be +started as an interactive shell or a server. + +When the runtime is started as a shell, users can define rules and evaluate +expressions interactively. When the runtime is started as a server, ` + brand + ` exposes +an HTTP API for managing policies, reading and writing data, and executing +queries. + +The runtime can be initialized with one or more files that contain policies or +data. If the '--bundle' option is specified the paths will be treated as policy +bundles and loaded following standard bundle conventions. The path can be a +compressed archive file or a directory which will be treated as a bundle. +Without the '--bundle' flag ` + brand + ` will recursively load ALL rego, JSON, and YAML +files. + +When loading from directories, only files with known extensions are considered. +The current set of file extensions that ` + brand + ` will consider are: + + .json # JSON data + .yaml or .yml # YAML data + .rego # Rego file + +Non-bundle data file and directory paths can be prefixed with the desired +destination in the data document with the following syntax: + + : + +To set a data file as the input document in the interactive shell use the +"repl.input" path prefix with the input file: + + repl.input: + +Example: + + $ ` + executable + ` run repl.input:input.json + +Which will load the "input.json" file at path "data.repl.input". + +Use the "help input" command in the interactive shell to see more options. + + +File paths can be specified as URLs to resolve ambiguity in paths containing colons: + + $ ` + executable + ` run file:///c:/path/to/data.json + +URL paths to remote public bundles (http or https) will be parsed as shorthand +configuration equivalent of using repeated --set flags to accomplish the same: + + $ ` + executable + ` run -s https://example.com/bundles/bundle.tar.gz + +The above shorthand command is identical to: + + $ ` + executable + ` run -s --set "services.cli1.url=https://example.com" \ + --set "bundles.cli1.service=cli1" \ + --set "bundles.cli1.resource=/bundles/bundle.tar.gz" \ + --set "bundles.cli1.persist=true" + +The 'run' command can also verify the signature of a signed bundle. +A signed bundle is a normal ` + brand + ` bundle that includes a file +named ".signatures.json". For more information on signed bundles +see https://www.openpolicyagent.org/docs/latest/management-bundles/#signing. + +The key to verify the signature of signed bundle can be provided +using the --verification-key flag. For example, for RSA family of algorithms, +the command expects a PEM file containing the public key. +For HMAC family of algorithms (eg. HS256), the secret can be provided +using the --verification-key flag. + +The --verification-key-id flag can be used to optionally specify a name for the +key provided using the --verification-key flag. + +The --signing-alg flag can be used to specify the signing algorithm. +The 'run' command uses RS256 (by default) as the signing algorithm. + +The --scope flag can be used to specify the scope to use for +bundle signature verification. + +Example: + + $ ` + executable + ` run --verification-key secret --signing-alg HS256 --bundle bundle.tar.gz + +The 'run' command will read the bundle "bundle.tar.gz", check the +".signatures.json" file and perform verification using the provided key. +An error will be generated if "bundle.tar.gz" does not contain a ".signatures.json" file. +For more information on the bundle verification process see +https://www.openpolicyagent.org/docs/latest/management-bundles/#signature-verification. + +The 'run' command can ONLY be used with the --bundle flag to verify signatures +for existing bundle files or directories following the bundle structure. + +To skip bundle verification, use the --skip-verify flag. + +The --watch flag can be used to monitor policy and data file-system changes. When a change is detected, the updated policy +and data is reloaded into OPA. Watching individual files (rather than directories) is generally not recommended as some +updates might cause them to be dropped by OPA. + +OPA will automatically perform type checking based on a schema inferred from known input documents and report any errors +resulting from the schema check. Currently this check is performed on OPA's Authorization Policy Input document and will +be expanded in the future. To disable this, use the --skip-known-schema-check flag. + +The --v0-compatible flag can be used to opt-in to OPA features and behaviors that were the default in OPA v0.x. +Behaviors enabled by this flag include: +- setting OPA's listening address to ":8181" by default, corresponding to listening on every network interface. +- expecting v0 Rego syntax in policy modules instead of the default v1 Rego syntax. + +The --tls-cipher-suites flag can be used to specify the list of enabled TLS 1.0–1.2 cipher suites. Note that TLS 1.3 +cipher suites are not configurable. Following are the supported TLS 1.0 - 1.2 cipher suites (IANA): +TLS_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, +TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, +TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, +TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, +TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, +TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + +See https://godoc.org/crypto/tls#pkg-constants for more information. +`, + PreRunE: func(cmd *cobra.Command, _ []string) error { + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + ctx := context.Background() + addrSetByUser := cmd.Flags().Changed("addr") + rt, err := initRuntime(ctx, cmdParams, args, addrSetByUser) + if err != nil { + fmt.Println("error:", err) + return err + } + return startRuntime(ctx, rt, cmdParams.serverMode) + }, + } + + addConfigFileFlag(runCommand.Flags(), &cmdParams.rt.ConfigFile) + runCommand.Flags().BoolVarP(&cmdParams.serverMode, "server", "s", false, "start the runtime in server mode") + runCommand.Flags().IntVar(&cmdParams.rt.ReadyTimeout, "ready-timeout", 0, "wait (in seconds) for configured plugins before starting server (value <= 0 disables ready check)") + runCommand.Flags().StringVarP(&cmdParams.rt.HistoryPath, "history", "H", historyPath(brand), "set path of history file") + cmdParams.rt.Addrs = runCommand.Flags().StringSliceP("addr", "a", []string{defaultLocalAddr}, "set listening address of the server (e.g., [ip]: for TCP, unix:// for UNIX domain socket)") + cmdParams.rt.DiagnosticAddrs = runCommand.Flags().StringSlice("diagnostic-addr", []string{}, "set read-only diagnostic listening address of the server for /health and /metric APIs (e.g., [ip]: for TCP, unix:// for UNIX domain socket)") + cmdParams.rt.UnixSocketPerm = runCommand.Flags().String("unix-socket-perm", "755", "specify the permissions for the Unix domain socket if used to listen for incoming connections") + runCommand.Flags().BoolVar(&cmdParams.rt.H2CEnabled, "h2c", false, "enable H2C for HTTP listeners") + runCommand.Flags().StringVarP(&cmdParams.rt.OutputFormat, "format", "f", "pretty", "set shell output format, i.e, pretty, json") + runCommand.Flags().BoolVarP(&cmdParams.rt.Watch, "watch", "w", false, "watch command line files for changes") + addV0CompatibleFlag(runCommand.Flags(), &cmdParams.rt.V0Compatible, false) + addV1CompatibleFlag(runCommand.Flags(), &cmdParams.rt.V1Compatible, false) + addMaxErrorsFlag(runCommand.Flags(), &cmdParams.rt.ErrorLimit) + runCommand.Flags().BoolVar(&cmdParams.rt.PprofEnabled, "pprof", false, "enables pprof endpoints") + runCommand.Flags().StringVar(&cmdParams.tlsCertFile, "tls-cert-file", "", "set path of TLS certificate file") + runCommand.Flags().StringVar(&cmdParams.tlsPrivateKeyFile, "tls-private-key-file", "", "set path of TLS private key file") + runCommand.Flags().StringVar(&cmdParams.tlsCACertFile, "tls-ca-cert-file", "", "set path of TLS CA cert file") + runCommand.Flags().DurationVar(&cmdParams.tlsCertRefresh, "tls-cert-refresh-period", 0, "set certificate refresh period") + runCommand.Flags().Var(cmdParams.authentication, "authentication", "set authentication scheme") + runCommand.Flags().Var(cmdParams.authorization, "authorization", "set authorization scheme") + runCommand.Flags().Var(cmdParams.minTLSVersion, "min-tls-version", "set minimum TLS version to be used by "+brand+"'s server") + runCommand.Flags().VarP(cmdParams.logLevel, "log-level", "l", "set log level") + runCommand.Flags().Var(cmdParams.logFormat, "log-format", "set log format") + runCommand.Flags().StringVar(&cmdParams.logTimestampFormat, "log-timestamp-format", "", "set log timestamp format (OPA_LOG_TIMESTAMP_FORMAT environment variable)") + runCommand.Flags().IntVar(&cmdParams.rt.GracefulShutdownPeriod, "shutdown-grace-period", 10, "set the time (in seconds) that the server will wait to gracefully shut down") + runCommand.Flags().IntVar(&cmdParams.rt.ShutdownWaitPeriod, "shutdown-wait-period", 0, "set the time (in seconds) that the server will wait before initiating shutdown") + runCommand.Flags().BoolVar(&cmdParams.skipKnownSchemaCheck, "skip-known-schema-check", false, "disables type checking on known input schemas") + runCommand.Flags().StringSliceVar(&cmdParams.cipherSuites, "tls-cipher-suites", []string{}, "set list of enabled TLS 1.0–1.2 cipher suites (IANA)") + addConfigOverrides(runCommand.Flags(), &cmdParams.rt.ConfigOverrides) + addConfigOverrideFiles(runCommand.Flags(), &cmdParams.rt.ConfigOverrideFiles) + addBundleModeFlag(runCommand.Flags(), &cmdParams.rt.BundleMode, false) + addReadAstValuesFromStoreFlag(runCommand.Flags(), &cmdParams.rt.ReadAstValuesFromStore, false) + + runCommand.Flags().BoolVar(&cmdParams.skipVersionCheck, "skip-version-check", false, "disables anonymous version reporting (see: https://www.openpolicyagent.org/docs/latest/privacy)") + err := runCommand.Flags().MarkDeprecated("skip-version-check", "\"skip-version-check\" is deprecated. Use \"disable-telemetry\" instead") + if err != nil { + fmt.Println("error:", err) + os.Exit(1) + } + + runCommand.Flags().BoolVar(&cmdParams.disableTelemetry, "disable-telemetry", false, "disables anonymous information reporting (see: https://www.openpolicyagent.org/docs/latest/privacy)") + addIgnoreFlag(runCommand.Flags(), &cmdParams.ignore) + + // bundle verification config + addVerificationKeyFlag(runCommand.Flags(), &cmdParams.pubKey) + addVerificationKeyIDFlag(runCommand.Flags(), &cmdParams.pubKeyID, defaultPublicKeyID) + addSigningAlgFlag(runCommand.Flags(), &cmdParams.algorithm, defaultTokenSigningAlg) + addBundleVerificationScopeFlag(runCommand.Flags(), &cmdParams.scope) + addBundleVerificationSkipFlag(runCommand.Flags(), &cmdParams.skipBundleVerify, false) + addBundleVerificationExcludeFilesFlag(runCommand.Flags(), &cmdParams.excludeVerifyFiles) + + usageTemplate := `Usage: + {{.UseLine}} [files] + +Flags: +{{.LocalFlags.FlagUsages | trimRightSpace}} +` + + runCommand.SetUsageTemplate(usageTemplate) + + root.AddCommand(runCommand) +} + +func initRuntime(ctx context.Context, params runCmdParams, args []string, addrSetByUser bool) (*runtime.Runtime, error) { + authenticationSchemes := map[string]server.AuthenticationScheme{ + "token": server.AuthenticationToken, + "tls": server.AuthenticationTLS, + "off": server.AuthenticationOff, + } + + authorizationScheme := map[string]server.AuthorizationScheme{ + "basic": server.AuthorizationBasic, + "off": server.AuthorizationOff, + } + + minTLSVersions := map[string]uint16{ + "1.0": tls.VersionTLS10, + "1.1": tls.VersionTLS11, + "1.2": tls.VersionTLS12, + "1.3": tls.VersionTLS13, + } + + tlsCertFilePath, err := fileurl.Clean(params.tlsCertFile) + if err != nil { + return nil, fmt.Errorf("invalid certificate file path: %w", err) + } + tlsPrivateKeyFilePath, err := fileurl.Clean(params.tlsPrivateKeyFile) + if err != nil { + return nil, fmt.Errorf("invalid certificate private key file path: %w", err) + } + tlsCACertFilePath, err := fileurl.Clean(params.tlsCACertFile) + if err != nil { + return nil, fmt.Errorf("invalid CA certificate file path: %w", err) + } + + cert, err := loadCertificate(tlsCertFilePath, tlsPrivateKeyFilePath) + if err != nil { + return nil, err + } + + params.rt.CertificateFile = tlsCertFilePath + params.rt.CertificateKeyFile = tlsPrivateKeyFilePath + params.rt.CertificateRefresh = params.tlsCertRefresh + params.rt.CertPoolFile = tlsCACertFilePath + + if tlsCACertFilePath != "" { + pool, err := loadCertPool(tlsCACertFilePath) + if err != nil { + return nil, err + } + params.rt.CertPool = pool + } + + params.rt.Authentication = authenticationSchemes[params.authentication.String()] + params.rt.Authorization = authorizationScheme[params.authorization.String()] + params.rt.MinTLSVersion = minTLSVersions[params.minTLSVersion.String()] + params.rt.Certificate = cert + + timestampFormat := params.logTimestampFormat + if timestampFormat == "" { + timestampFormat = os.Getenv("OPA_LOG_TIMESTAMP_FORMAT") + } + params.rt.Logging = runtime.LoggingConfig{ + Level: params.logLevel.String(), + Format: params.logFormat.String(), + TimestampFormat: timestampFormat, + } + params.rt.Paths = args + params.rt.Filter = ignored(params.ignore).Apply + params.rt.EnableVersionCheck = !params.disableTelemetry + + // For backwards compatibility, check if `--skip-version-check` flag set. + if params.skipVersionCheck { + params.rt.EnableVersionCheck = false + } + + params.rt.SkipBundleVerification = params.skipBundleVerify + + bvc, err := buildVerificationConfig(params.pubKey, params.pubKeyID, params.algorithm, params.scope, params.excludeVerifyFiles) + if err != nil { + return nil, err + } + params.rt.BundleVerificationConfig = bvc + + if params.rt.BundleVerificationConfig != nil && !params.rt.BundleMode { + return nil, errors.New("enable bundle mode (ie. --bundle) to verify bundle files or directories") + } + + params.rt.SkipKnownSchemaCheck = params.skipKnownSchemaCheck + + if len(params.cipherSuites) > 0 { + cipherSuites, err := verifyCipherSuites(params.cipherSuites) + if err != nil { + return nil, err + } + + params.rt.CipherSuites = cipherSuites + } + + rt, err := runtime.NewRuntime(ctx, params.rt) + if err != nil { + return nil, err + } + + rt.SetDistributedTracingLogging() + rt.Params.AddrSetByUser = addrSetByUser + + if !addrSetByUser && rt.Params.V0Compatible { + rt.Params.Addrs = &[]string{defaultAddr} + } + + return rt, nil +} + +func startRuntime(ctx context.Context, rt *runtime.Runtime, serverMode bool) error { + if serverMode { + return rt.Serve(ctx) + } + return rt.StartREPL(ctx) +} + +func verifyCipherSuites(cipherSuites []string) (*[]uint16, error) { + cipherSuitesMap := map[string]*tls.CipherSuite{} + + for _, c := range tls.CipherSuites() { + cipherSuitesMap[c.Name] = c + } + + for _, c := range tls.InsecureCipherSuites() { + cipherSuitesMap[c.Name] = c + } + + cipherSuitesIDs := []uint16{} + for _, c := range cipherSuites { + val, ok := cipherSuitesMap[c] + if !ok { + return nil, fmt.Errorf("invalid cipher suite %v", c) + } + + // verify no TLS 1.3 cipher suites as they are not configurable + if slices.Contains(val.SupportedVersions, tls.VersionTLS13) { + return nil, fmt.Errorf("TLS 1.3 cipher suite \"%v\" is not configurable", c) + } + + cipherSuitesIDs = append(cipherSuitesIDs, val.ID) + } + + return &cipherSuitesIDs, nil +} + +func historyPath(brand string) string { + b := strings.ToLower(brand) + historyFile := strings.Replace(defaultHistoryFile, "opa", b, 1) + + home := os.Getenv("HOME") + if len(home) == 0 { + return historyFile + } + return path.Join(home, historyFile) +} + +func loadCertificate(tlsCertFile, tlsPrivateKeyFile string) (*tls.Certificate, error) { + if tlsCertFile != "" && tlsPrivateKeyFile != "" { + cert, err := tls.LoadX509KeyPair(tlsCertFile, tlsPrivateKeyFile) + if err != nil { + return nil, err + } + return &cert, nil + } else if tlsCertFile != "" || tlsPrivateKeyFile != "" { + return nil, errors.New("--tls-cert-file and --tls-private-key-file must be specified together") + } + + return nil, nil +} + +func loadCertPool(tlsCACertFile string) (*x509.CertPool, error) { + caCertPEM, err := os.ReadFile(tlsCACertFile) + if err != nil { + return nil, fmt.Errorf("read CA cert file: %v", err) + } + pool := x509.NewCertPool() + if ok := pool.AppendCertsFromPEM(caCertPEM); !ok { + return nil, fmt.Errorf("failed to parse CA cert %q", tlsCACertFile) + } + return pool, nil +} diff --git a/third_party/opa/cmd/run_test.go b/third_party/opa/cmd/run_test.go new file mode 100644 index 000000000000..5dab60ca61b7 --- /dev/null +++ b/third_party/opa/cmd/run_test.go @@ -0,0 +1,464 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "fmt" + "path/filepath" + "slices" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/test/e2e" + "github.com/open-policy-agent/opa/v1/util/test" + "github.com/spf13/cobra" +) + +func TestRunServerBase(t *testing.T) { + params := newTestRunParams() + ctx, cancel := context.WithCancel(context.Background()) + + rt, err := initRuntime(ctx, params, nil, false) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + testRuntime := e2e.WrapRuntime(ctx, cancel, rt) + + done := make(chan bool) + go func() { + err := rt.Serve(ctx) + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + done <- true + }() + + err = testRuntime.WaitForServer() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + validateBasicServe(t, testRuntime) + + cancel() + <-done +} + +func TestRunServerBaseListenOnLocalhost(t *testing.T) { + params := newTestRunParams() + params.rt.V1Compatible = true + + ctx, cancel := context.WithCancel(context.Background()) + + rt, err := initRuntime(ctx, params, nil, true) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + testRuntime := e2e.WrapRuntime(ctx, cancel, rt) + + done := make(chan bool) + go func() { + err := rt.Serve(ctx) + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + done <- true + }() + + err = testRuntime.WaitForServer() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + validateBasicServe(t, testRuntime) + + if len(rt.Addrs()) != 1 { + t.Fatalf("Expected 1 listening address but got %v", len(rt.Addrs())) + } + + expected := "127.0.0.1:8181" + if rt.Addrs()[0] != expected { + t.Fatalf("Expected listening address %v but got %v", expected, rt.Addrs()[0]) + } + + cancel() + <-done +} + +func TestRunServerWithDiagnosticAddr(t *testing.T) { + params := newTestRunParams() + params.rt.DiagnosticAddrs = &[]string{"localhost:0"} + ctx, cancel := context.WithCancel(context.Background()) + + rt, err := initRuntime(ctx, params, nil, false) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + testRuntime := e2e.WrapRuntime(ctx, cancel, rt) + + done := make(chan bool) + go func() { + err := rt.Serve(ctx) + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + done <- true + }() + + err = testRuntime.WaitForServer() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + validateBasicServe(t, testRuntime) + + diagURL, err := testRuntime.AddrToURL(rt.DiagnosticAddrs()[0]) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + if err := testRuntime.HealthCheck(diagURL); err != nil { + t.Error(err) + } + + cancel() + <-done +} + +func TestInitRuntimeVerifyNonBundle(t *testing.T) { + + params := newTestRunParams() + params.pubKey = "secret" + params.serverMode = false + + _, err := initRuntime(context.Background(), params, nil, false) + if err == nil { + t.Fatal("Expected error but got nil") + } + + exp := "enable bundle mode (ie. --bundle) to verify bundle files or directories" + if err.Error() != exp { + t.Fatalf("expected error message %v but got %v", exp, err.Error()) + } +} + +func TestInitRuntimeCipherSuites(t *testing.T) { + testCases := []struct { + name string + cipherSuites []string + expErr bool + expCipherSuites []uint16 + }{ + {"no cipher suites", []string{}, false, []uint16{}}, + {"secure and insecure cipher suites", []string{"TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_RC4_128_SHA"}, false, []uint16{tls.TLS_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_RSA_WITH_RC4_128_SHA}}, + {"invalid cipher suites", []string{"foo"}, true, []uint16{}}, + {"tls 1.3 cipher suite", []string{"TLS_AES_128_GCM_SHA256"}, true, []uint16{}}, + {"tls 1.2-1.3 cipher suite", []string{"TLS_RSA_WITH_AES_128_GCM_SHA256", "TLS_AES_128_GCM_SHA256"}, true, []uint16{}}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + params := newTestRunParams() + + if len(tc.cipherSuites) != 0 { + params.cipherSuites = tc.cipherSuites + } + + rt, err := initRuntime(context.Background(), params, nil, false) + fmt.Println(err) + + if !tc.expErr && err != nil { + t.Fatal("Unexpected error occurred:", err) + } else if tc.expErr && err == nil { + t.Fatal("Expected error but got nil") + } else if err == nil { + if len(tc.expCipherSuites) > 0 { + if !slices.Equal(*rt.Params.CipherSuites, tc.expCipherSuites) { + t.Fatalf("expected cipher suites %v but got %v", tc.expCipherSuites, *rt.Params.CipherSuites) + } + } else { + if rt.Params.CipherSuites != nil { + t.Fatal("expected no value defined for cipher suites") + } + } + } + }) + } +} + +func TestInitRuntimeSkipKnownSchemaCheck(t *testing.T) { + + fs := map[string]string{ + "test/authz.rego": `package system.authz + import rego.v1 + + default allow := false + + allow if { + input.identty = "foo" # this is a typo + }`, + } + + test.WithTempFS(fs, func(rootDir string) { + rootDir = filepath.Join(rootDir, "test") + + params := newTestRunParams() + err := params.authorization.Set("basic") + if err != nil { + t.Fatal(err) + } + + _, err = initRuntime(context.Background(), params, []string{rootDir}, false) + if err == nil { + t.Fatal("Expected error but got nil") + } + + if !strings.Contains(err.Error(), "undefined ref: input.identty") { + t.Errorf("Expected error \"%v\" not found", "undefined ref: input.identty") + } + + // skip type checking for known input schemas + params.skipKnownSchemaCheck = true + _, err = initRuntime(context.Background(), params, []string{rootDir}, false) + if err != nil { + t.Fatal(err) + } + }) +} + +func TestRunServerUploadPolicy(t *testing.T) { + v0Policy := `package test + p { q["a"] } + q[x] { + x = "a" + }` + + v1Policy := `package test + p if { q["a"] } + q contains x if { + x = "a" + }` + + tests := []struct { + note string + v0Compatible bool + module string + expErr bool + }{ + { + note: "v0-compatible, v0 policy", + v0Compatible: true, + module: v0Policy, + }, + { + note: "v0-compatible, v1 policy", + v0Compatible: true, + module: v1Policy, + expErr: true, + }, + { + note: "v1, v0 policy", + v0Compatible: false, + module: v0Policy, + expErr: true, + }, + { + note: "v1, v1 policy", + v0Compatible: false, + module: v1Policy, + }, + } + + for i, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + + params := newTestRunParams() + params.rt.V0Compatible = tc.v0Compatible + + rt, err := initRuntime(ctx, params, nil, false) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + testRuntime := e2e.WrapRuntime(ctx, cancel, rt) + + done := make(chan bool) + go func() { + err := rt.Serve(ctx) + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + done <- true + }() + + err = testRuntime.WaitForServer() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + // upload policy + err = testRuntime.UploadPolicy(fmt.Sprintf("mod%d", i), bytes.NewBufferString(tc.module)) + + if tc.expErr { + if err == nil { + t.Fatalf("Expected error but got nil") + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + } + + cancel() + <-done + }) + } +} + +func TestRunServerCheckLogTimestampFormat(t *testing.T) { + for _, format := range []string{time.Kitchen, time.RFC3339Nano} { + t.Run(format, func(t *testing.T) { + t.Run("parameter", func(t *testing.T) { + params := newTestRunParams() + params.logTimestampFormat = format + checkLogTimeStampFormat(t, params, format) + }) + t.Run("environment variable", func(t *testing.T) { + t.Setenv("OPA_LOG_TIMESTAMP_FORMAT", format) + params := newTestRunParams() + checkLogTimeStampFormat(t, params, format) + }) + }) + } +} + +func checkLogTimeStampFormat(t *testing.T, params runCmdParams, format string) { + ctx, cancel := context.WithCancel(context.Background()) + + rt, err := initRuntime(ctx, params, nil, false) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + var buf bytes.Buffer + logger := rt.Manager.Logger().(*logging.StandardLogger) + logger.SetOutput(&buf) + testRuntime := e2e.WrapRuntime(ctx, cancel, rt) + + done := make(chan bool) + go func() { + err := rt.Serve(ctx) + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + done <- true + }() + + err = testRuntime.WaitForServer() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + validateBasicServe(t, testRuntime) + + cancel() + <-done + + for _, line := range strings.Split(buf.String(), "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + var rec struct { + Time string `json:"time"` + } + if err := json.Unmarshal([]byte(line), &rec); err != nil { + t.Fatalf("incorrect log message %s: %v", line, err) + } + if rec.Time == "" { + t.Fatalf("the time field is empty in log message: %s", line) + } + if _, err := time.Parse(format, rec.Time); err != nil { + t.Fatalf("incorrect timestamp format %q: %v", rec.Time, err) + } + } +} + +func TestInitRuntimeAddrSetByUser(t *testing.T) { + testCases := []struct { + name string + addrValue string + addrFlagSet bool + }{ + {"AddrSetByUser_True", "localhost:8181", true}, + {"AddrSetByUser_False", "", false}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cmd := &cobra.Command{} + cmd.Flags().String("addr", "", "set address") + if tc.addrFlagSet { + if err := cmd.Flags().Set("addr", tc.addrValue); err != nil { + t.Fatalf("Failed to set addr flag: %v", err) + } + } + + params := newTestRunParams() + params.rt.Addrs = &[]string{"localhost:0"} + ctx, cancel := context.WithCancel(context.Background()) + + rt, err := initRuntime(ctx, params, []string{}, cmd.Flags().Changed("addr")) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if rt.Params.AddrSetByUser != tc.addrFlagSet { + t.Errorf("Expected AddrSetByUser to be %v, but got %v", tc.addrFlagSet, rt.Params.AddrSetByUser) + } + + cancel() + }) + } +} + +func newTestRunParams() runCmdParams { + params := newRunParams() + params.rt.GracefulShutdownPeriod = 1 + params.rt.Addrs = &[]string{"localhost:8181"} + params.rt.DiagnosticAddrs = &[]string{} + params.serverMode = true + return params +} + +func validateBasicServe(t *testing.T, runtime *e2e.TestRuntime) { + t.Helper() + + err := runtime.UploadData(bytes.NewBufferString(`{"x": 1}`)) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + resp := struct { + Result int `json:"result"` + }{} + err = runtime.GetDataWithInputTyped("x", nil, &resp) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if resp.Result != 1 { + t.Fatalf("Expected x to be 1, got %v", resp) + } +} diff --git a/third_party/opa/cmd/sign.go b/third_party/opa/cmd/sign.go new file mode 100644 index 000000000000..90e85b98a92d --- /dev/null +++ b/third_party/opa/cmd/sign.go @@ -0,0 +1,293 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/internal/env" + initload "github.com/open-policy-agent/opa/internal/runtime/init" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/util" +) + +type signCmdParams struct { + algorithm string + key string + claimsFile string + outputFilePath string + bundleMode bool + plugin string +} + +const ( + defaultTokenSigningAlg = "RS256" + defaultHashingAlg = "SHA-256" + signaturesFile = ".signatures.json" +) + +var errSigningConfigIncomplete = errors.New("specify the secret (HMAC) or path of the PEM file containing the private key (RSA and ECDSA)") + +func newSignCmdParams() signCmdParams { + return signCmdParams{} +} + +func initSign(root *cobra.Command, brand string) { + executable := root.Name() + cmdParams := newSignCmdParams() + + var signCommand = &cobra.Command{ + Use: "sign [ [...]]", + Short: `Generate an ` + brand + ` bundle signature`, + Long: `Generate an ` + brand + ` bundle signature. + +The 'sign' command generates a digital signature for policy bundles. It generates a +".signatures.json" file that dictates which files should be included in the bundle, +what their SHA hashes are, and is cryptographically secure. + +The signatures file is a JSON file with an array containing a single JSON Web Token (JWT) +that encapsulates the signature for the bundle. + +The --signing-alg flag can be used to specify the algorithm to sign the token. The 'sign' +command uses RS256 (by default) as the signing algorithm. +See https://www.openpolicyagent.org/docs/latest/configuration/#keys +for a list of supported signing algorithms. + +The key to be used for signing the JWT MUST be provided using the --signing-key flag. +For example, for RSA family of algorithms, the command expects a PEM file containing +the private key. +For HMAC family of algorithms (eg. HS256), the secret can be provided using +the --signing-key flag. + +` + brand + ` 'sign' can ONLY be used with the --bundle flag to load paths that refer to +existing bundle files or directories following the bundle structure. + + $ ` + executable + ` sign --signing-key /path/to/private_key.pem --bundle foo + +Where foo has the following structure: + + foo/ + | + +-- bar/ + | | + | +-- data.json + | + +-- policy.rego + | + +-- .manifest + +This will create a ".signatures.json" file in the current directory. +The --output-file-path flag can be used to specify a different location for +the ".signatures.json" file. + +The content of the ".signatures.json" file is shown below: + + { + "signatures": [ + "eyJhbGciOiJSUzI1NiJ9.eyJmaWxlcyI6W3sibmFtZSI6Ii5tYW5pZmVzdCIsImhhc2giOiIxODc0NWRlNzJjMDFlODBjZDlmNTIwZjQxOGMwMDlhYzRkMmMzZDAyYjE3YTUwZTJkMDQyMTU4YmMzNTJhMzJkIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9LHsibmFtZSI6ImJhci9kYXRhLmpzb24iLCJoYXNoIjoiOTNhMjM5NzFhOTE0ZTVlYWNiZjBhOGQyNTE1NGNkYTMwOWMzYzFjNzJmYmI5OTE0ZDQ3YzYwZjNjYjY4MTU4OCIsImFsZ29yaXRobSI6IlNIQS0yNTYifSx7Im5hbWUiOiJwb2xpY3kucmVnbyIsImhhc2giOiJkMGYyNDJhYWUzNGRiNTRlZjU2NmJlYTRkNDVmY2YxOTcwMGM1ZDhmODdhOWRiOTMyZGZhZDZkMWYwZjI5MWFjIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9XX0.lNsmRqrmT1JI4Z_zpY6IzHRZQAU306PyOjZ6osquixPuTtdSBxgbsdKDcp7Civw3B77BgygVsvx4k3fYr8XCDKChm0uYKScrpFr9_yS6g5mVTQws3KZncZXCQHdupRFoqMS8vXAVgJr52C83AinYWABwH2RYq_B0ZPf_GDzaMgzpep9RlDNecGs57_4zlyxmP2ESU8kjfX8jAA6rYFKeGXJHMD-j4SassoYIzYRv9YkHx8F8Y2ae5Kd5M24Ql0kkvqc_4eO_T9s4nbQ4q5qGHGE-91ND1KVn2avcUyVVPc0-XCR7EH8HnHgCl0v1c7gX1RL7ET7NJbPzfmzQAzk0ZW0dEHI4KZnXSpqy8m-3zAc8kIARm2QwoNEWpy3MWiooPeZVSa9d5iw1aLrbyumfjBP0vCQEPes-Aa6PrARwd5jR9SacO5By0-4emzskvJYRZqbfJ9tXSXDMcAFOAm6kqRPJaj8AO4CyajTC_Lt32_0OLeXqYgNpt3HDqLqGjrb-8fVeQc-hKh0aES8XehQqXj4jMwfsTyj5alsXZm08LwzcFlfQZ7s1kUtmr0_BBNJYcdZUdlu6Qio3LFSRYXNuu6edAO1VH5GKqZISvE1uvDZb2E0Z-rtH-oPp1iSpfvsX47jKJ42LVpI6OahEBri44dzHOIwwm3CIuV8gFzOwR0k" + ] + } + +And the decoded JWT payload has the following form: + + { + "files": [ + { + "name": ".manifest", + "hash": "18745de72c01e80cd9f520f418c009ac4d2c3d02b17a50e2d042158bc352a32d", + "algorithm": "SHA-256" + }, + { + "name": "policy.rego", + "hash": "d0f242aae34db54ef566bea4d45fcf19700c5d8f87a9db932dfad6d1f0f291ac", + "algorithm": "SHA-256" + }, + { + "name": "bar/data.json", + "hash": "93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588", + "algorithm": "SHA-256" + } + ] + } + +The "files" field is generated from the files under the directory path(s) +provided to the 'sign' command. During bundle signature verification, ` + brand + ` will check +each file name (ex. "foo/bar/data.json") in the "files" field +exists in the actual bundle. The file content is hashed using SHA256. + +To include additional claims in the payload use the --claims-file flag to provide +a JSON file containing optional claims. + +For more information on the format of the ".signatures.json" file see +https://www.openpolicyagent.org/docs/latest/management-bundles/#signature-format. +`, + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := validateSignParams(args, cmdParams); err != nil { + return err + } + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + if err := doSign(args, cmdParams); err != nil { + fmt.Println("error:", err) + return err + } + return nil + }, + } + + addBundleModeFlag(signCommand.Flags(), &cmdParams.bundleMode, false) + + // bundle signing config + addSigningKeyFlag(signCommand.Flags(), &cmdParams.key) + addClaimsFileFlag(signCommand.Flags(), &cmdParams.claimsFile) + addSigningAlgFlag(signCommand.Flags(), &cmdParams.algorithm, defaultTokenSigningAlg) + addSigningPluginFlag(signCommand.Flags(), &cmdParams.plugin) + + signCommand.Flags().StringVarP(&cmdParams.outputFilePath, "output-file-path", "o", ".", "set the location for the .signatures.json file") + + root.AddCommand(signCommand) +} + +func doSign(args []string, params signCmdParams) error { + load, err := initload.WalkPaths(args, nil, params.bundleMode) + if err != nil { + return err + } + + hash, err := bundle.NewSignatureHasher(bundle.HashingAlgorithm(defaultHashingAlg)) + if err != nil { + return err + } + + files, err := readBundleFiles(load.BundlesLoader, hash) + if err != nil { + return err + } + + signingConfig, err := buildSigningConfig(params.key, params.algorithm, params.claimsFile, params.plugin) + if err != nil { + return err + } + + token, err := bundle.GenerateSignedToken(files, signingConfig, "") + if err != nil { + return err + } + + return writeTokenToFile(token, params.outputFilePath) +} + +func readBundleFiles(loaders []initload.BundleLoader, h bundle.SignatureHasher) ([]bundle.FileInfo, error) { + files := []bundle.FileInfo{} + + for _, bl := range loaders { + for { + f, err := bl.DirectoryLoader.NextFile() + if err == io.EOF { + break + } + + if err != nil { + return files, fmt.Errorf("bundle read failed: %w", err) + } + + // skip existing signatures file + if strings.HasSuffix(f.Path(), bundle.SignaturesFile) { + continue + } + + var buf bytes.Buffer + n, err := f.Read(&buf, bundle.DefaultSizeLimitBytes+1) + f.Close() + + if err != nil && err != io.EOF { + return files, err + } else if err == nil && n >= bundle.DefaultSizeLimitBytes { + return files, fmt.Errorf("bundle file exceeded max size (%v bytes)", bundle.DefaultSizeLimitBytes) + } + + path := f.Path() + if bl.IsDir { + path = f.URL() + } + + // hash the file content + fi, err := hashFileContent(h, buf.Bytes(), path) + if err != nil { + return files, err + } + files = append(files, fi) + } + } + return files, nil +} + +func hashFileContent(h bundle.SignatureHasher, data []byte, path string) (bundle.FileInfo, error) { + + var fileInfo bundle.FileInfo + var value any + + if bundle.IsStructuredDoc(path) { + err := util.Unmarshal(data, &value) + if err != nil { + return fileInfo, err + } + } else { + value = data + } + + bytes, err := h.HashFile(value) + if err != nil { + return fileInfo, err + } + + return bundle.NewFile(strings.TrimPrefix(path, "/"), hex.EncodeToString(bytes), defaultHashingAlg), nil +} + +func writeTokenToFile(token, fileLoc string) error { + content := make(map[string]any) + content["signatures"] = []string{token} + + bs, err := json.MarshalIndent(content, "", " ") + if err != nil { + return err + } + + path := signaturesFile + if fileLoc != "" { + path = filepath.Join(fileLoc, path) + } + return os.WriteFile(path, bs, 0644) +} + +func validateSignParams(args []string, params signCmdParams) error { + if len(args) == 0 { + return errors.New("specify atleast one path containing policy and/or data files") + } + + if params.key == "" { + return errSigningConfigIncomplete + } + + if !params.bundleMode { + return errors.New("enable bundle mode (ie. --bundle) to sign bundle files or directories") + } + return nil +} diff --git a/third_party/opa/cmd/sign_test.go b/third_party/opa/cmd/sign_test.go new file mode 100644 index 000000000000..38025350cba6 --- /dev/null +++ b/third_party/opa/cmd/sign_test.go @@ -0,0 +1,184 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. +package cmd + +import ( + "bytes" + "encoding/json" + "errors" + "os" + "path/filepath" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestWriteTokenToFile(t *testing.T) { + + token := `eyJhbGciOiJSUzI1NiJ9.eyJmaWxlcyI6W3sibmFtZSI6ImJ1bmRsZS8ubWFuaWZlc3QiLCJoYXNoIjoiZWUwZWRiZGZkMjgzNTBjNDk2ZjA4ODI3Y2E1Y2VhYjgwMzA2NzI0YjYyZGY1ZjY0MDRlNzBjYjc2NjYxNWQ5ZCIsImFsZ29yaXRobSI6IlNIQTI1NiJ9LHsibmFtZSI6ImJ1bmRsZS9odHRwL2V4YW1wbGUvYXV0aHovYXV0aHoucmVnbyIsImhhc2giOiI2MDJiZTcwMWIyYmE4ZTc3YTljNTNmOWIzM2QwZTkwM2MzNGMwMGMzMDkzM2Y2NDZiYmU3NGI3YzE2NGY2OGM2IiwiYWxnb3JpdGhtIjoiU0hBMjU2In0seyJuYW1lIjoiYnVuZGxlL3JvbGVzL2JpbmRpbmcvZGF0YS5qc29uIiwiaGFzaCI6ImIxODg1NTViZjczMGVlNDdkZjBiY2Y4MzVlYTNmNTQ1MjlmMzc4N2Y0ODQxZjFhZGE2MDM5M2RhYWZhZmJkYzciLCJhbGdvcml0aG0iOiJTSEEyNTYifV0sImtleWlkIjoiZm9vIiwic2NvcGUiOiJyZWFkIn0.YojuPnGWutdlDL7lwFGBXqPfDtxOG2BuZmShN5zm-G9zfMprI1AMqKDoPoNv4tuCGIBNXwoNsYHYiK538CHfJEfY1v4iDX3JFEWQlwx_CfJWDonwqT9SY9tHUW7PUUrI_WgJXZ5zei8RAMYMymKSb9hpSAtfGg_PU0kZr52WzjbPUj4SRiB19Swi61r0CFXYjbfx3GDJdjrGTNBSWrUCMrdhHYLEWqJPfSQ-fYfRrgQVhq3BJLwJJe66dgBEGnHEgA7XMuxkNIOv7mj3Y_EChbv2tjrD9NJPekDcYH1zCEc4BycHjNCcsGiQXDE6sFtoNZiCXLB2D0sLqUnBx4TCw27wTPfcOuL2KauLPahZitnH5mYvQD8NI76Pm4NSyJfevwdWjSsrT7vf0DCLS-dU6r9dJ79xM_hJU7136CT8ARcmSrk-EvCqfkrH2c4WwZyAzdyyyFumMZh4CYc2vcC7ap0NANHJT193fTud1i23mx1PBslwXdsIqXvBGlTbR7nb9o661m-B_mxbHMkG4nIeoGpZoaBJw8RVaA6-4D55gtk8aaMyLJIlIIlV2_AKOLk3nPG3ACHiLSndasLDOIRIYkCluIEaM2FLEEPEtJfKNR6e1K-EK2TvNKMDAEUtJW71ggOuGQ3b5otYOoVVENJLwm-PsO7qb2Tq6PyAquI3ExU` + expected := make(map[string]any) + expected["signatures"] = []string{token} + + files := map[string]string{} + + test.WithTempFS(files, func(rootDir string) { + err := writeTokenToFile(token, rootDir) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + bs, err := os.ReadFile(filepath.Join(rootDir, ".signatures.json")) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + expectedBytes, err := json.MarshalIndent(expected, "", " ") + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(expectedBytes, bs) { + t.Fatal("Unexpected content in \".signatures.json\" file") + } + }) +} + +func TestDoSign(t *testing.T) { + files := map[string]string{ + "foo/bar/data.json": `{"y": 2}`, + "/example/example.rego": `package example`, + "/.signatures.json": `{"signatures": []}`, + } + test.WithTempFS(files, func(rootDir string) { + params := signCmdParams{ + algorithm: "HS256", + key: "mysecret", + outputFilePath: rootDir, + bundleMode: true, + } + + err := doSign([]string{rootDir}, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) +} + +func TestBundleSignVerification(t *testing.T) { + + // files to be included in the bundle + files := map[string]string{ + "/.manifest": `{"revision": "quickbrownfaux"}`, + "/a/b/c/data.json": "[1,2,3]", + "/a/b/d/data.json": "true", + "/a/b/y/data.yaml": `foo: 1`, + "/example/example.rego": `package example`, + "/policy.wasm": `modules-compiled-as-wasm-binary`, + "/data.json": `{"x": {"y": true}, "a": {"b": {"z": true}}}`, + } + + test.WithTempFS(files, func(rootDir string) { + params := signCmdParams{ + algorithm: "HS256", + key: "mysecret", + outputFilePath: rootDir, + bundleMode: true, + } + + err := doSign([]string{rootDir}, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // create gzipped tarball + var filesInBundle [][2]string + err = filepath.Walk(rootDir, func(path string, info os.FileInfo, _ error) error { + if !info.IsDir() { + bs, err := os.ReadFile(path) + if err != nil { + return err + } + filesInBundle = append(filesInBundle, [2]string{path, string(bs)}) + } + return nil + }) + if err != nil { + t.Fatal(err) + } + + buf := archive.MustWriteTarGz(filesInBundle) + + // bundle verification config + kc := keys.Config{ + Key: "mysecret", + Algorithm: "HS256", + } + + bvc := bundle.NewVerificationConfig(map[string]*keys.Config{"foo": &kc}, "foo", "", nil) + reader := bundle.NewReader(buf).WithBundleVerificationConfig(bvc).WithBaseDir(rootDir) + + _, err = reader.Read() + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) +} + +func TestValidateSignParams(t *testing.T) { + + tests := map[string]struct { + args []string + params signCmdParams + wantErr bool + err error + }{ + "no_args": { + []string{}, + newSignCmdParams(), + true, errors.New("specify atleast one path containing policy and/or data files"), + }, + "no_signing_key": { + []string{"foo"}, + newSignCmdParams(), + true, errors.New("specify the secret (HMAC) or path of the PEM file containing the private key (RSA and ECDSA)"), + }, + "empty_signing_key": { + []string{"foo"}, + signCmdParams{key: "", bundleMode: true}, + true, errors.New("specify the secret (HMAC) or path of the PEM file containing the private key (RSA and ECDSA)"), + }, + "non_bundle_mode": { + []string{"foo"}, + signCmdParams{key: "foo"}, + true, errors.New("enable bundle mode (ie. --bundle) to sign bundle files or directories"), + }, + "no_error": { + []string{"foo"}, + signCmdParams{key: "foo", bundleMode: true}, + false, nil, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + err := validateSignParams(tc.args, tc.params) + + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + } +} diff --git a/third_party/opa/cmd/test.go b/third_party/opa/cmd/test.go new file mode 100644 index 000000000000..a60fd5edf115 --- /dev/null +++ b/third_party/opa/cmd/test.go @@ -0,0 +1,586 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "context" + "errors" + "fmt" + "io" + "os" + "os/signal" + goRuntime "runtime" + "strings" + "syscall" + "testing" + "time" + + "github.com/fsnotify/fsnotify" + "github.com/open-policy-agent/opa/internal/pathwatcher" + initload "github.com/open-policy-agent/opa/internal/runtime/init" + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/formats" + "github.com/open-policy-agent/opa/cmd/internal/env" + "github.com/open-policy-agent/opa/internal/runtime" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/compile" + "github.com/open-policy-agent/opa/v1/cover" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/tester" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/lineage" + "github.com/open-policy-agent/opa/v1/util" +) + +type testCommandParams struct { + verbose bool + explain *util.EnumFlag + errLimit int + outputFormat *util.EnumFlag + coverage bool + threshold float64 + timeout time.Duration + ignore []string + bundleMode bool + benchmark bool + benchMem bool + runRegex string + count int + target *util.EnumFlag + skipExitZero bool + capabilities *capabilitiesFlag + schema *schemaFlags + watch bool + stopChan chan os.Signal + output io.Writer + errOutput io.Writer + v0Compatible bool + v1Compatible bool + varValues bool + parallel int +} + +func newTestCommandParams() testCommandParams { + return testCommandParams{ + outputFormat: formats.Flag(formats.Pretty, formats.JSON, formats.GoBench), + explain: newExplainFlag([]string{explainModeFails, explainModeFull, explainModeNotes, explainModeDebug}), + target: util.NewEnumFlag(compile.TargetRego, []string{compile.TargetRego, compile.TargetWasm}), + capabilities: newCapabilitiesFlag(), + schema: &schemaFlags{}, + output: os.Stdout, + errOutput: os.Stderr, + stopChan: make(chan os.Signal, 1), + parallel: goRuntime.NumCPU(), + } +} + +func (p *testCommandParams) RegoVersion() ast.RegoVersion { + // v0 takes precedence over v1 + if p.v0Compatible { + return ast.RegoV0 + } + if p.v1Compatible { + return ast.RegoV1 + } + return ast.DefaultRegoVersion +} + +func opaTest(args []string, testParams testCommandParams) int { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + if testParams.outputFormat.String() == formats.GoBench && !testParams.benchmark { + errMsg := "cannot use output format %s without running benchmarks (--bench)\n" + _, _ = fmt.Fprintf(testParams.errOutput, errMsg, formats.GoBench) + return 0 + } + + if !isThresholdValid(testParams.threshold) { + _, _ = fmt.Fprintln(testParams.errOutput, "Code coverage threshold must be between 0 and 100") + return 1 + } + + var modules map[string]*ast.Module + var bundles map[string]*bundle.Bundle + var store storage.Store + + popts := ast.ParserOptions{ + RegoVersion: testParams.RegoVersion(), + Capabilities: testParams.capabilities.C, + ProcessAnnotation: true, + } + + var err error + if testParams.bundleMode { + bundles, store, err = tester.LoadBundlesWithParserOptions(args, ignored(testParams.ignore).Apply, popts) + } else { + modules, store, err = tester.LoadWithParserOptions(args, ignored(testParams.ignore).Apply, popts) + } + if err != nil { + _, _ = fmt.Fprintln(testParams.errOutput, err) + return 1 + } + + txn, err := store.NewTransaction(ctx, storage.WriteParams) + if err != nil { + _, _ = fmt.Fprintln(testParams.errOutput, err) + return 1 + } + + runner, reporter, err := compileAndSetupTests(ctx, testParams, store, txn, modules, bundles) + if err != nil { + store.Abort(ctx, txn) + _, _ = fmt.Fprintln(testParams.errOutput, err) + return 1 + } + + success := true + for range testParams.count { + exitCode, _ := runTests(ctx, txn, runner, reporter, testParams) + if exitCode != 0 { + success = false + store.Abort(ctx, txn) + if testParams.watch { + break + } + return exitCode + } + } + + if success { + store.Abort(ctx, txn) + } + + if !testParams.watch { + return 0 + } + + done := make(chan struct{}) + go func() { + var store storage.Store + + if bundle.BundleExtStore != nil { + store = bundle.BundleExtStore() + } else { + store = inmem.NewWithOpts(inmem.OptRoundTripOnWrite(false)) + } + + startWatcher(ctx, testParams, args, store, done) + }() + + signal.Notify(testParams.stopChan, syscall.SIGINT, syscall.SIGTERM) + + <-testParams.stopChan + done <- struct{}{} + return 0 +} + +func runTests(ctx context.Context, txn storage.Transaction, runner *tester.Runner, reporter tester.Reporter, testParams testCommandParams) (int, error) { + var err error + var ch chan *tester.Result + if testParams.benchmark { + // Initialize testing package for benchmarking. This is needed to set default values for some flags that may + // otherwise be dereferenced on some code paths causing panics, as reported in: + // https://github.com/open-policy-agent/opa/issues/7205 + testing.Init() + + benchOpts := tester.BenchmarkOptions{ + ReportAllocations: testParams.benchMem, + } + ch, err = runner.RunBenchmarks(ctx, txn, benchOpts) + } else { + ch, err = runner.RunTests(ctx, txn) + } + + if err != nil { + _, _ = fmt.Fprintln(testParams.errOutput, err) + return 1, err + } + + exitCode := 0 + dup := make(chan *tester.Result) + + go func() { + defer close(dup) + for tr := range ch { + if !tr.Pass() { + if !(tr.Skip && testParams.skipExitZero) { + exitCode = 2 + } + } + tr.Trace = filterTrace(&testParams, tr.Trace) + dup <- tr + } + }() + + if err := reporter.Report(dup); err != nil { + _, _ = fmt.Fprintln(testParams.errOutput, err) + if !testParams.benchmark { + var coverageThresholdError *cover.CoverageThresholdError + if errors.As(err, &coverageThresholdError) { + return 2, err + } + } + return 1, err + } + + return exitCode, err +} + +func filterTrace(params *testCommandParams, trace []*topdown.Event) []*topdown.Event { + // If an explain mode was specified, filter based + // on the mode. If no explain mode was specified, + // default to show both notes and fail events + showDefault := !params.explain.IsSet() && params.verbose + if showDefault { + return lineage.Filter(trace, func(event *topdown.Event) bool { + return event.Op == topdown.NoteOp || event.Op == topdown.FailOp + }) + } + + mode := params.explain.String() + switch mode { + case explainModeNotes: + return lineage.Notes(trace) + case explainModeFull: + return lineage.Full(trace) + case explainModeFails: + return lineage.Fails(trace) + case explainModeDebug: + return lineage.Debug(trace) + default: + return nil + } +} + +func isThresholdValid(t float64) bool { + return 0 <= t && t <= 100 +} + +func startWatcher(ctx context.Context, testParams testCommandParams, paths []string, store storage.Store, done chan struct{}) { + watcher, err := pathwatcher.CreatePathWatcher(paths) + if err != nil { + _, _ = fmt.Fprintln(testParams.errOutput, "Error creating path watcher: ", err) + os.Exit(1) + } + readWatcher(ctx, testParams, watcher, paths, store, done) +} + +func readWatcher(ctx context.Context, testParams testCommandParams, watcher *fsnotify.Watcher, paths []string, store storage.Store, done chan struct{}) { + for { + _, _ = fmt.Fprintln(testParams.output, strings.Repeat("*", 80)) + _, _ = fmt.Fprintln(testParams.output, "Watching for changes ...") + select { + case evt := <-watcher.Events: + removalMask := fsnotify.Remove | fsnotify.Rename + mask := fsnotify.Create | fsnotify.Write | removalMask + if (evt.Op & mask) != 0 { + removed := "" + if (evt.Op & removalMask) != 0 { + removed = evt.Name + } + processWatcherUpdate(ctx, testParams, paths, removed, store) + } + case <-done: + _ = watcher.Close() + return + } + } +} + +func processWatcherUpdate(ctx context.Context, testParams testCommandParams, paths []string, removed string, store storage.Store) { + filter := ignored(testParams.ignore).Apply + + var loadResult *initload.LoadPathsResult + + err := pathwatcher.ProcessWatcherUpdateForRegoVersion(ctx, testParams.RegoVersion(), paths, removed, store, filter, testParams.bundleMode, false, + func(ctx context.Context, txn storage.Transaction, loaded *initload.LoadPathsResult) error { + if len(loaded.Files.Documents) > 0 || removed != "" { + if err := store.Write(ctx, txn, storage.AddOp, storage.Path{}, loaded.Files.Documents); err != nil { + return fmt.Errorf("storage error: %w", err) + } + } + + loadResult = loaded + + return nil + }) + + if err != nil { + _, _ = fmt.Fprintln(testParams.output, err) + return + } + + modules := map[string]*ast.Module{} + for id, module := range loadResult.Files.Modules { + modules[id] = module.Parsed + } + + err = storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + runner, reporter, err := compileAndSetupTests(ctx, testParams, store, txn, modules, loadResult.Bundles) + if err != nil { + return err + } + + for range testParams.count { + exitCode, err := runTests(ctx, txn, runner, reporter, testParams) + if exitCode != 0 { + return err + } + } + return nil + }) + + if err != nil { + _, _ = fmt.Fprintln(testParams.output, err) + } +} + +func compileAndSetupTests(ctx context.Context, testParams testCommandParams, store storage.Store, txn storage.Transaction, modules map[string]*ast.Module, bundles map[string]*bundle.Bundle) (*tester.Runner, tester.Reporter, error) { + + var capabilities *ast.Capabilities + // if capabilities are not provided as a cmd flag, + // then ast.CapabilitiesForThisVersion must be called + // within checkModules to ensure custom builtins are properly captured + if testParams.capabilities.C != nil { + capabilities = testParams.capabilities.C + } else { + capabilities = ast.CapabilitiesForThisVersion() + } + + // -s {file} (one input schema file) + // -s {directory} (one schema directory with input and data schema files) + schemaSet, err := loader.Schemas(testParams.schema.path) + if err != nil { + return nil, nil, err + } + + compiler := ast.NewCompiler(). + SetErrorLimit(testParams.errLimit). + WithPathConflictsCheck(storage.NonEmpty(ctx, store, txn)). + WithEnablePrintStatements(!testParams.benchmark). + WithCapabilities(capabilities). + WithSchemas(schemaSet). + WithUseTypeCheckAnnotations(true). + WithRewriteTestRules(testParams.varValues) + + info, err := runtime.Term(runtime.Params{}) + if err != nil { + return nil, nil, err + } + + if testParams.threshold > 0 && !testParams.coverage { + testParams.coverage = true + } + + var cov *cover.Cover + var coverTracer topdown.QueryTracer + + if testParams.coverage { + if testParams.benchmark { + errMsg := "coverage reporting is not supported when benchmarking tests" + _, _ = fmt.Fprintln(testParams.errOutput, errMsg) + return nil, nil, errors.New(errMsg) + } + cov = cover.New() + coverTracer = cov + } + + timeout := testParams.timeout + if timeout == 0 { // unset + timeout = 5 * time.Second + if testParams.benchmark { + timeout = 30 * time.Second + } + } + + runner := tester.NewRunner(). + SetCompiler(compiler). + SetStore(store). + CapturePrintOutput(true). + EnableTracing(testParams.verbose || testParams.varValues). + SetCoverageQueryTracer(coverTracer). + SetRuntime(info). + SetModules(modules). + SetBundles(bundles). + SetTimeout(timeout). + Filter(testParams.runRegex). + SetParallel(testParams.parallel) + + if testParams.target.IsSet() { + runner = runner.Target(testParams.target.String()) + } + + var reporter tester.Reporter + + goBench := false + + if !testParams.coverage { + switch testParams.outputFormat.String() { + case formats.JSON: + reporter = tester.JSONReporter{ + Output: testParams.output, + } + case formats.GoBench: + goBench = true + fallthrough + default: + reporter = tester.PrettyReporter{ + Verbose: testParams.verbose, + Output: testParams.output, + BenchmarkResults: testParams.benchmark, + BenchMarkShowAllocations: testParams.benchMem, + BenchMarkGoBenchFormat: goBench, + FailureLine: testParams.varValues, + LocalVars: testParams.varValues, + } + } + } else { + reporter = tester.JSONCoverageReporter{ + Cover: cov, + Modules: modules, + Output: testParams.output, + Threshold: testParams.threshold, + Verbose: testParams.verbose, + } + } + + return runner, reporter, nil +} + +func initTest(root *cobra.Command, brand string) { + executable := root.Name() + + var testParams = newTestCommandParams() + + var testCommand = &cobra.Command{ + Use: "test [path [...]]", + Short: "Execute Rego test cases", + Long: `Execute Rego test cases. + +The 'test' command takes a file or directory path as input and executes all +test cases discovered in matching files. Test cases are rules whose names have the prefix "test_". + +If the '--bundle' option is specified the paths will be treated as policy bundles +and loaded following standard bundle conventions. The path can be a compressed archive +file or a directory which will be treated as a bundle. Without the '--bundle' flag OPA +will recursively load ALL *.rego, *.json, and *.yaml files for evaluating the test cases. + +Test cases under development may be prefixed "todo_" in order to skip their execution, +while still getting marked as skipped in the test results. + +Example policy (example/authz.rego): + + package authz + + allow if { + input.path == ["users"] + input.method == "POST" + } + + allow if { + input.path == ["users", input.user_id] + input.method == "GET" + } + +Example test (example/authz_test.rego): + + package authz_test + + import data.authz.allow + + test_post_allowed if { + allow with input as {"path": ["users"], "method": "POST"} + } + + test_get_denied if { + not allow with input as {"path": ["users"], "method": "GET"} + } + + test_get_user_allowed if { + allow with input as {"path": ["users", "bob"], "method": "GET", "user_id": "bob"} + } + + test_get_another_user_denied if { + not allow with input as {"path": ["users", "bob"], "method": "GET", "user_id": "alice"} + } + + todo_test_user_allowed_http_client_data if { + false # Remember to test this later! + } + +Example test run: + + $ ` + executable + ` test ./example/ + +If used with the '--bench' option then tests will be benchmarked. + +Example benchmark run: + + $ ` + executable + ` test --bench ./example/ + +The optional "gobench" output format conforms to the Go Benchmark Data Format. + +The --watch flag can be used to monitor policy and data file-system changes. When a change is detected, ` + brand + ` reloads +the policy and data and then re-runs the tests. Watching individual files (rather than directories) is generally not +recommended as some updates might cause them to be dropped by OPA. +`, + PreRunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("specify at least one file") + } + + // If an --explain flag was set, turn on verbose output + if testParams.explain.IsSet() { + testParams.verbose = true + } + + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + + exit := opaTest(args, testParams) + if exit != 0 { + return newExitError(exit) + } + return nil + }, + } + + // Test specific flags + testCommand.Flags().BoolVarP(&testParams.skipExitZero, "exit-zero-on-skipped", "z", false, "skipped tests return status 0") + testCommand.Flags().BoolVarP(&testParams.verbose, "verbose", "v", false, "set verbose reporting mode") + testCommand.Flags().DurationVar(&testParams.timeout, "timeout", 0, "set test timeout (default 5s, 30s when benchmarking)") + testCommand.Flags().BoolVarP(&testParams.coverage, "coverage", "c", false, "report coverage (overrides debug tracing)") + testCommand.Flags().Float64VarP(&testParams.threshold, "threshold", "", 0, "set coverage threshold and exit with non-zero status if coverage is less than threshold %") + testCommand.Flags().BoolVar(&testParams.benchmark, "bench", false, "benchmark the unit tests") + testCommand.Flags().StringVarP(&testParams.runRegex, "run", "r", "", "run only test cases matching the regular expression") + testCommand.Flags().BoolVarP(&testParams.watch, "watch", "w", false, "watch command line files for changes") + testCommand.Flags().BoolVar(&testParams.varValues, "var-values", false, "show local variable values in test output") + testCommand.Flags().IntVarP(&testParams.parallel, "parallel", "p", goRuntime.NumCPU(), "the number of tests that can run in parallel, defaulting to the number of CPUs (explicitly set with 0). Benchmarks are always run sequentially.") + + // Shared flags + addOutputFormat(testCommand.Flags(), testParams.outputFormat) + addBundleModeFlag(testCommand.Flags(), &testParams.bundleMode, false) + addBenchmemFlag(testCommand.Flags(), &testParams.benchMem, true) + addCountFlag(testCommand.Flags(), &testParams.count, "test") + addMaxErrorsFlag(testCommand.Flags(), &testParams.errLimit) + addIgnoreFlag(testCommand.Flags(), &testParams.ignore) + setExplainFlag(testCommand.Flags(), testParams.explain) + addTargetFlag(testCommand.Flags(), testParams.target) + addCapabilitiesFlag(testCommand.Flags(), testParams.capabilities) + addSchemaFlags(testCommand.Flags(), testParams.schema) + addV0CompatibleFlag(testCommand.Flags(), &testParams.v0Compatible, false) + addV1CompatibleFlag(testCommand.Flags(), &testParams.v1Compatible, false) + + root.AddCommand(testCommand) +} diff --git a/third_party/opa/cmd/test_test.go b/third_party/opa/cmd/test_test.go new file mode 100644 index 000000000000..8ebfa942d33e --- /dev/null +++ b/third_party/opa/cmd/test_test.go @@ -0,0 +1,3688 @@ +package cmd + +import ( + "bytes" + "context" + "fmt" + "io" + "maps" + "os" + "path" + "path/filepath" + "regexp" + "strings" + "syscall" + "testing" + "time" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/ir" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/repl" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestFilterTraceDefault(t *testing.T) { + p := newTestCommandParams() + p.verbose = false + expected := `Enter data.testing.test_p = _ +| Enter data.testing.test_p +| | Enter data.testing.p +| | | Enter data.testing.q +| | | | Enter data.testing.r +| | | | | Fail x = data.x +| | | | Fail data.testing.r[x] +| | | Fail data.testing.q.foo +| | Fail data.testing.p with data.x as "bar" +| Fail data.testing.test_p = _ +` + verifyFilteredTrace(t, &p, expected) +} + +func TestFilterTraceVerbose(t *testing.T) { + p := newTestCommandParams() + p.verbose = true + expected := `Enter data.testing.test_p = _ +| Enter data.testing.test_p +| | Enter data.testing.p +| | | Note "test test" +| | | Enter data.testing.q +| | | | Note "got this far" +| | | | Enter data.testing.r +| | | | | Note "got this far2" +| | | | | Fail x = data.x +| | | | Fail data.testing.r[x] +| | | Fail data.testing.q.foo +| | Fail data.testing.p with data.x as "bar" +| Fail data.testing.test_p = _ +` + verifyFilteredTrace(t, &p, expected) +} + +func TestFilterTraceExplainFails(t *testing.T) { + p := newTestCommandParams() + err := p.explain.Set(explainModeFails) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + expected := `Enter data.testing.test_p = _ +| Enter data.testing.test_p +| | Enter data.testing.p +| | | Enter data.testing.q +| | | | Enter data.testing.r +| | | | | Fail x = data.x +| | | | Fail data.testing.r[x] +| | | Fail data.testing.q.foo +| | Fail data.testing.p with data.x as "bar" +| Fail data.testing.test_p = _ +` + verifyFilteredTrace(t, &p, expected) +} + +func TestFilterTraceExplainNotes(t *testing.T) { + p := newTestCommandParams() + err := p.explain.Set(explainModeNotes) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + expected := `Enter data.testing.test_p = _ +| Enter data.testing.test_p +| | Enter data.testing.p +| | | Note "test test" +| | | Enter data.testing.q +| | | | Note "got this far" +| | | | Enter data.testing.r +| | | | | Note "got this far2" +` + verifyFilteredTrace(t, &p, expected) +} + +func TestFilterTraceExplainFull(t *testing.T) { + p := newTestCommandParams() + err := p.explain.Set(explainModeFull) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + expected := `Enter data.testing.test_p = _ +| Eval data.testing.test_p = _ +| Index data.testing.test_p (matched 1 rule, early exit) +| Enter data.testing.test_p +| | Eval data.testing.p with data.x as "bar" +| | Index data.testing.p (matched 1 rule, early exit) +| | Enter data.testing.p +| | | Eval data.testing.x +| | | Index data.testing.x (matched 1 rule, early exit) +| | | Enter data.testing.x +| | | | Eval data.testing.y +| | | | Index data.testing.y (matched 1 rule, early exit) +| | | | Enter data.testing.y +| | | | | Eval true +| | | | | Exit data.testing.y early +| | | | Exit data.testing.x early +| | | Eval trace("test test") +| | | Note "test test" +| | | Eval data.testing.q.foo +| | | Index data.testing.q (matched 1 rule) +| | | Enter data.testing.q +| | | | Eval trace("got this far") +| | | | Note "got this far" +| | | | Eval data.testing.r[x] +| | | | Index data.testing.r (matched 1 rule) +| | | | Enter data.testing.r +| | | | | Eval trace("got this far2") +| | | | | Note "got this far2" +| | | | | Eval x = data.x +| | | | | Fail x = data.x +| | | | | Redo trace("got this far2") +| | | | Fail data.testing.r[x] +| | | | Redo trace("got this far") +| | | Fail data.testing.q.foo +| | | Redo trace("test test") +| | | Redo data.testing.x +| | | Redo data.testing.x +| | | | Redo data.testing.y +| | | | | Redo true +| | Fail data.testing.p with data.x as "bar" +| Fail data.testing.test_p = _ +` + verifyFilteredTrace(t, &p, expected) +} + +func TestThresholdRange(t *testing.T) { + thresholds := []float64{-1, 101} + for _, threshold := range thresholds { + if isThresholdValid(threshold) { + t.Fatalf("invalid threshold %2f shoul be reported", threshold) + } + } +} + +func verifyFilteredTrace(t *testing.T, params *testCommandParams, expected string) { + filtered := filterTrace(params, failTrace(t)) + + var buff bytes.Buffer + topdown.PrettyTrace(&buff, filtered) + actual := buff.String() + + if actual != expected { + t.Fatalf("Expected:\n\n%s\n\nGot:\n\n%s\n\n", expected, actual) + } +} + +func failTrace(t *testing.T) []*topdown.Event { + t.Helper() + mod := ` + package testing + + p if { + x # Always true + trace("test test") + q["foo"] + } + + x if { + y + } + + y if { + true + } + + q contains x if { + some x + trace("got this far") + r[x] + trace("got this far1") + } + + r contains x if { + trace("got this far2") + x := data.x + } + + test_p if { + p with data.x as "bar" + } + ` + + tracer := topdown.NewBufferTracer() + + _, err := rego.New( + rego.Module("test.rego", mod), + rego.Trace(true), + rego.QueryTracer(tracer), + rego.Query("data.testing.test_p"), + ).Eval(context.Background()) + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + return *tracer +} + +func TestPrettyTraceWithLocalVars(t *testing.T) { + tests := []struct { + note string + includeVars bool + files map[string]string + expected string + }{ + { + note: "without vars", + includeVars: false, + files: map[string]string{ + "test.rego": `package test + +test_p if { + x := 1 + y := 2 + z := 3 + x == z + y +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_p: FAIL (%.*%) + + query:1 %.*% Enter data.test.test_p = _ + query:1 %.*% | Eval data.test.test_p = _ + query:1 %.*% | Index data.test.test_p (matched 1 rule, early exit) + %.*%/test.rego:3 | Enter data.test.test_p + %.*%/test.rego:4 | | Eval x = 1 + %.*%/test.rego:5 | | Eval y = 2 + %.*%/test.rego:6 | | Eval z = 3 + %.*%/test.rego:7 | | Eval plus(z, y, __local3__) + %.*%/test.rego:7 | | Eval x = __local3__ + %.*%/test.rego:7 | | Fail x = __local3__ + %.*%/test.rego:7 | | Redo plus(z, y, __local3__) + %.*%/test.rego:6 | | Redo z = 3 + %.*%/test.rego:5 | | Redo y = 2 + %.*%/test.rego:4 | | Redo x = 1 + query:1 %.*% | Fail data.test.test_p = _ + +SUMMARY +-------------------------------------------------------------------------------- +%.*%/test.rego: +data.test.test_p: FAIL (%.*%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "with vars", + includeVars: true, + files: map[string]string{ + "test.rego": `package test + +test_p if { + x := 1 + y := 2 + z := 3 + x == z + y +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_p: FAIL (%.*%) + + query:1 %.*% Enter data.test.test_p = _ {} + query:1 %.*% | Eval data.test.test_p = _ {} + query:1 %.*% | Index data.test.test_p (matched 1 rule, early exit) {} + %.*%/test.rego:3 | Enter data.test.test_p {} + %.*%/test.rego:4 | | Eval x = 1 {} + %.*%/test.rego:5 | | Eval y = 2 {} + %.*%/test.rego:6 | | Eval z = 3 {} + %.*%/test.rego:7 | | Eval plus(z, y, __local3__) {y: 2, z: 3} + %.*%/test.rego:7 | | Eval x = __local3__ {__local3__: 5, x: 1} + %.*%/test.rego:7 | | Fail x = __local3__ {__local3__: 5, x: 1} + %.*%/test.rego:7 | | Redo plus(z, y, __local3__) {__local3__: 5, y: 2, z: 3} + %.*%/test.rego:6 | | Redo z = 3 {z: 3} + %.*%/test.rego:5 | | Redo y = 2 {y: 2} + %.*%/test.rego:4 | | Redo x = 1 {x: 1} + query:1 %.*% | Fail data.test.test_p = _ {} + + %.*%/test.rego:7: + x == z + y + | | | + | | 2 + | z + y: 5 + | z: 3 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%.*%/test.rego: +data.test.test_p: FAIL (%.*%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(tc.files, func(root string) { + buf := new(bytes.Buffer) + testParams := newTestCommandParams() + testParams.count = 1 + testParams.output = buf + testParams.errOutput = io.Discard + testParams.bundleMode = true + testParams.verbose = true + testParams.varValues = tc.includeVars + _ = testParams.explain.Set(explainModeFull) + + errorCode := opaTest([]string{root}, testParams) + if errorCode != 2 { + t.Fatalf("Unexpected error code: %d", errorCode) + } + + actual := buf.String() + if !stringsMatch(t, tc.expected, actual) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", tc.expected, actual) + } + }) + }) + } +} + +func TestFailVarValues(t *testing.T) { + tests := []struct { + note string + files map[string]string + expected string + }{ + { + note: "simple", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + x := 1 + y := 2 + z := 3 + x == y + z +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:7: + x == y + z + | | | + | | 3 + | y + z: 5 + | y: 2 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "simple (not)", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + x := 5 + y := 2 + z := 3 + not x == y + z +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:7: + not x == y + z + | | | + | | 3 + | y + z: 5 + | y: 2 + 5 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "array", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + x := 1 + y := [1, 2, 3] + z := 3 + x == y[2] + z +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:7: + x == y[2] + z + | | | + | | 3 + | y[2] + z: 6 + | y[2]: 3 + | y: [1, 2, 3] + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "array, var key", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + x := 1 + y := [1, 2, 3] + z := 3 + i := 2 + x == y[i] + z +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:8: + x == y[i] + z + | | | | + | | | 3 + | | 2 + | y[i] + z: 6 + | y[i]: 3 + | y: [1, 2, 3] + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "array containing vars", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + x := 1 + y := 2 + z := 3 + [x, y, z] == [4, 5, 6] +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:7: + [x, y, z] == [4, 5, 6] + | | | + | | 3 + | 2 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "array containing refs", + files: map[string]string{ + "/test.rego": `package test + +a := 1 + +b := 2 + +test_foo if { + [a, data.test.b, data.c] == [4, 5, 6] +} +`, + "data.json": `{"c": 3}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:8: + [a, data.test.b, data.c] == [4, 5, 6] + | | | + | | 3 + | 2 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "array containing refs, undefined", + files: map[string]string{ + "/test.rego": `package test + +a := 1 + +b := data.b + +test_foo if { + [a, b, data.c] == [4, 5, 6] +} +`, + "data.json": `{"c": 3}`, + }, + // Note: each dynamic array element is broken out into a separate "co-expression" by the compiler. + // Since we failed on the 2nd element (b), we don't have value for the 3rd element (data.c). + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:8: + [a, b, data.c] == [4, 5, 6] + | | + | undefined + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "nested collections containing vars", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + x := 1 + y := 2 + z := 3 + [x, {y, {"a": z}}] == [4, {5, {"a": 6}}] +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:7: + [x, {y, {"a": z}}] == [4, {5, {"a": 6}}] + | | | + | | 3 + | 2 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "single line expression containing tabs", + files: map[string]string{ + "/test.rego": `package test + + test_foo if { + x := 1 + y := 2 + z := 3 + x == y + z + } +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:7: + x == y + z + | | | + | | 3 + | y + z: 5 + | y: 2 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "single line expression containing tabs #2", + files: map[string]string{ + "/test.rego": `package test + + test_foo if { + x := 1 + y := 2 + z := 3 + x == y + z + } +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:7: + x == y + z + | | | + | | 3 + | y + z: 5 + | y: 2 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "multi-line expression containing tabs", + files: map[string]string{ + "/test.rego": `package test + + test_foo if { + x := 1 + y := 2 + z := 3 + obj := { + "foo_": 1, + "bar__": 42, + "baz": 3, + } + obj == { + "foo_": x, + "bar__": y, + "baz": z, + } + } +`, + }, + // We can't deal with tabs in a consistent manner when they occur on multiple lines + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:12: + obj == { + "foo_": x, + "bar__": y, + "baz": z, + } + + Where: + + obj: {"bar__": 42, "baz": 3, "foo_": 1} + x: 1 + y: 2 + z: 3 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "composite rule", + files: map[string]string{ + "/test.rego": `package test + +p contains v if { + some v in numbers.range(1, 3) +} + +test_p if { + p == {4, 5, 6} +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_p: FAIL (%TIME%) + + %ROOT%/test.rego:8: + p == {4, 5, 6} + | + {1, 2, 3} + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "composite rule with ref-head", + files: map[string]string{ + "/test.rego": `package test + +p.q contains v if { + some v in numbers.range(1, 3) +} + +test_p if { + p.q == {4, 5, 6} +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_p: FAIL (%TIME%) + + %ROOT%/test.rego:8: + p.q == {4, 5, 6} + | + {1, 2, 3} + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "composite rule with ref-head, partial ref", + files: map[string]string{ + "/test.rego": `package test + +p.q contains v if { + some v in numbers.range(1, 3) +} + +test_p if { + p == { + "q": {4, 5, 6} + } +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_p: FAIL (%TIME%) + + %ROOT%/test.rego:8: + p == { + "q": {4, 5, 6} + } + | + {"q": {1, 2, 3}} + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "composite rules with ref-head, composite value", + files: map[string]string{ + "/test.rego": `package test + +p.q contains v if { + some v in numbers.range(1, 3) +} + +p.r := "foo" + +test_p if { + p == { + "q": {4, 5, 6}, + "r": "bar" + } +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_p: FAIL (%TIME%) + + %ROOT%/test.rego:10: + p == { + "q": {4, 5, 6}, + "r": "bar" + } + | + {"q": {1, 2, 3}, "r": "foo"} + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "refs in different compiled sub-expressions", + files: map[string]string{ + "/test.rego": `package test + +a := 1 +b := 2 +c := 3 + +test_p if { + # This expression is split into multiple final expressions by the compiler, each containing a rule ref + a == b + c +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_p: FAIL (%TIME%) + + %ROOT%/test.rego:9: + a == b + c + | | | + | | 3 + | b + c: 5 + | b: 2 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "rule not defined", + files: map[string]string{ + "/test.rego": `package test + +p if { + input.x == 1 +} + +test_p if { + p with input.x as 2 +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_p: FAIL (%TIME%) + + %ROOT%/test.rego:8: + p with input.x as 2 + | + undefined + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "rule defined (not)", + files: map[string]string{ + "/test.rego": `package test + +p if { + input.x == 1 +} + +test_p if { + not p with input.x as 1 +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_p: FAIL (%TIME%) + + %ROOT%/test.rego:8: + not p with input.x as 1 + | + true + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "data ref", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + y := 1 + data.x == y +} +`, + "data.json": `{"x": 2}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:5: + data.x == y + | | + | 1 + 2 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "data + virtual extent ref", + files: map[string]string{ + "/test.rego": `package test + +foo.x := 1 + +test_foo if { + y := {"x": 1, "y": 42} + foo == y +} +`, + "data.json": `{"test": {"foo": {"y": 2}}}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:7: + foo == y + | | + | {"x": 1, "y": 42} + {"x": 1, "y": 2} + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "in (array)", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + l := ["a", "b", "c"] + x := "q" + x in l +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:6: + x in l + | | + | ["a", "b", "c"] + "q" + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "in (set)", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + l := {"a", "b", "c"} + x := "q" + x in l +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:6: + x in l + | | + | {"a", "b", "c"} + "q" + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "comprehension (array)", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + l := ["a", "b", "c"] + [x | x := l[_]] == ["d", "e", "f"] +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:5: + [x | x := l[_]] == ["d", "e", "f"] + | + ["a", "b", "c"] + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "comprehension (set)", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + l := ["a"] + {x | x := l[_]} == {"b"} +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:5: + {x | x := l[_]} == {"b"} + | + {"a"} + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "comprehension (object)", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + l := ["a", "b", "c"] + {k: x | x := l[k]} == {3: "d", 4: "e", 5: "f"} +} +`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:5: + {k: x | x := l[k]} == {3: "d", 4: "e", 5: "f"} + | + {0: "a", 1: "b", 2: "c"} + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "every", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + l := [1, 2, 3] + every x in l { + x == 1 + } +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:6: + x == 1 + | + 2 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "comprehension inside every", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + l := [1, 2, 3] + every x in l { + [v | v := x] == [42] + } +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:6: + [v | v := x] == [42] + | + [1] + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "nested every", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + l := [[1, 2], [3, 4], [5, 6]] + every x in l { + every y in x { + y < 4 + } + } +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:7: + y < 4 + | + 4 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "nested every with comprehension", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + l := [[1, 2], [3, 4], [5, 6]] + every x in l { + every y in x { + [v | v := y] == [42] + } + } +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:7: + [v | v := y] == [42] + | + [1] + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "ref equality", + files: map[string]string{ + "/test.rego": `package test + +a := 1 +b := 2 + +test_foo if { + a == b +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:7: + a == b + | | + | 2 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "ref equality (data)", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + data.a == data.b +}`, + "data.json": `{"a": 1, "b": 2}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:4: + data.a == data.b + | | + | 2 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "with, containing local vars", + files: map[string]string{ + "/test.rego": `package test + +p := input.x + +test_p if { + a := 1 + p == 2 with input.x as a +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_p: FAIL (%TIME%) + + %ROOT%/test.rego:7: + p == 2 with input.x as a + | | + | 1 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "with, containing ref", + files: map[string]string{ + "/test.rego": `package test + +p := input.x + +testInput := {"x": 1} + +test_p if { + p == 2 with input as testInput +}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_p: FAIL (%TIME%) + + %ROOT%/test.rego:8: + p == 2 with input as testInput + | | + | {"x": 1} + 1 + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "negated rule ref", + files: map[string]string{ + "/test.rego": `package test + +a if {true} + +test_foo if { + not a +}`, + "data.json": `{"a": true}`, + }, + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:6: + not a + | + true + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + { + note: "negated data ref", + files: map[string]string{ + "/test.rego": `package test + +test_foo if { + not data.a +}`, + "data.json": `{"a": true}`, + }, + // Because of the negated expr, the compiler will have opted out of rewriting the expression to + // capture the value of data.a in a local variable, and since data.a isn't in the local bindings + // or in the virtual cache, we don't know if it's undefined or unknown, and therefore can't report + // on a value. + expected: `FAILURES +-------------------------------------------------------------------------------- +data.test.test_foo: FAIL (%TIME%) + + %ROOT%/test.rego:4: + not data.a + +SUMMARY +-------------------------------------------------------------------------------- +%ROOT%/test.rego: +data.test.test_foo: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +`, + }, + } + + r := regexp.MustCompile(`FAIL \(.*s\)`) + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(tc.files, func(root string) { + buf := new(bytes.Buffer) + testParams := newTestCommandParams() + testParams.count = 1 + testParams.output = buf + testParams.errOutput = io.Discard + testParams.bundleMode = true + testParams.varValues = true + _ = testParams.explain.Set(explainModeFull) + + exitCode := opaTest([]string{root}, testParams) + if exitCode != 2 { + t.Fatalf("Unexpected error code: %d", exitCode) + } + + actual := r.ReplaceAllString(buf.String(), "FAIL (%TIME%)") + expected := strings.ReplaceAll(tc.expected, "%ROOT%", root) + + if !stringsMatch(t, expected, actual) { + t.Fatalf("Expected output to be:\n\n%s\n\ngot:\n\n%s", expected, actual) + } + }) + }) + } +} + +// Assert that ignore flag is correctly used when the bundle flag is activated +func TestIgnoreFlag(t *testing.T) { + files := map[string]string{ + "/test.rego": `package test + +p := input.foo == 42 +test_p if { + p with input.foo as 42 +}`, + "/broken.rego": "package foo\n bar {", + } + + var exitCode int + test.WithTempFS(files, func(root string) { + testParams := newTestCommandParams() + testParams.count = 1 + testParams.errOutput = io.Discard + testParams.bundleMode = false + testParams.ignore = []string{"broken.rego"} + + exitCode = opaTest([]string{root}, testParams) + }) + + if exitCode > 0 { + t.Fatalf("unexpected exit code: %d", exitCode) + } +} + +// Assert that ignore flag is correctly used when the bundle flag is activated +func TestIgnoreFlagWithBundleFlag(t *testing.T) { + files := map[string]string{ + "/test.rego": `package test + +p := input.foo == 42 +test_p if { + p with input.foo as 42 +}`, + "/broken.rego": "package foo\n bar {", + } + + var exitCode int + test.WithTempFS(files, func(root string) { + testParams := newTestCommandParams() + testParams.count = 1 + testParams.errOutput = io.Discard + testParams.bundleMode = true + testParams.ignore = []string{"broken.rego"} + exitCode = opaTest([]string{root}, testParams) + }) + + if exitCode > 0 { + t.Fatalf("unexpected exit code: %d", exitCode) + } +} + +func testSchemasAnnotation(rego string) (int, []byte) { + + files := map[string]string{ + "test.rego": rego, + } + + var exitCode int + var buf bytes.Buffer + test.WithTempFS(files, func(path string) { + regoFilePath := filepath.Join(path, "test.rego") + + testParams := newTestCommandParams() + testParams.count = 1 + testParams.errOutput = &buf + + exitCode = opaTest([]string{regoFilePath}, testParams) + }) + return exitCode, buf.Bytes() +} + +// Assert that 'schemas' annotations with schema ref are ignored, but not inlined schemas +func TestSchemasAnnotation(t *testing.T) { + policyWithSchemaRef := ` +package test + +# METADATA +# schemas: +# - input: schema["input"] +p if { + rego.metadata.rule() # presence of rego.metadata.* calls must not trigger unwanted schema evaluation + input.foo == 42 # type mismatch with schema that should be ignored +} + +test_p if { + p with input.foo as 42 +}` + + exitCode, _ := testSchemasAnnotation(policyWithSchemaRef) + if exitCode > 0 { + t.Fatalf("unexpected error when schema ref is present") + } +} +func TestSchemasAnnotationInline(t *testing.T) { + policyWithInlinedSchema := ` +package test + +# METADATA +# schemas: +# - input.foo: {"type": "boolean"} +p if { + input.foo == 42 # type mismatch with schema that should NOT be ignored since it is an inlined schema format +} + +test_p if { + p with input.foo as 42 +}` + + exitCode, errOutput := testSchemasAnnotation(policyWithInlinedSchema) + // We expect an error here, as inlined schemas are always used for type checking + + if exitCode == 0 { + t.Fatalf("didn't get expected error when inlined schema is present") + } + + if !strings.Contains(string(errOutput), "rego_type_error: match error") { + t.Fatalf("didn't get expected %s error when inlined schema is present; got: %v", ast.TypeErr, string(errOutput)) + } +} + +func testSchemasAnnotationWithJSONFile(rego string, schema string) (int, []byte) { + + files := map[string]string{ + "test.rego": rego, + "demo_schema.json": schema, + } + + var exitCode int + var buf bytes.Buffer + test.WithTempFS(files, func(path string) { + regoFilePath := filepath.Join(path, "test.rego") + + testParams := newTestCommandParams() + testParams.count = 1 + testParams.schema.path = path + testParams.errOutput = &buf + + exitCode = opaTest([]string{regoFilePath}, testParams) + }) + + return exitCode, buf.Bytes() +} +func TestJSONSchemaSuccess(t *testing.T) { + + regoContents := `package test + +# METADATA +# schemas: +# - input: schema.demo_schema +p if { + input.foo == 42 +} + +test_p if { + p with input.foo as 42 +}` + + schema := `{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "schema", + "type": "object", + "description": "The root schema comprises the entire JSON document.", + "required": [ + "foo" + ], + "properties": { + "foo": { + "$id": "#/properties/foo", + "type": "number", + "description": "foo" + } + }, + "additionalProperties": false + }` + + errorCode, _ := testSchemasAnnotationWithJSONFile(regoContents, schema) + if errorCode != 0 { + t.Fatalf("unexpected error code: %d", errorCode) + } +} + +func TestJSONSchemaFail(t *testing.T) { + + regoContents := `package test + +# METADATA +# schemas: +# - input: schema.demo_schema +p if { + input.foo == 42 +} + +test_p if { + p with input.foo as 42 +}` + + schema := `{ +"$schema": "http://json-schema.org/draft-07/schema", +"$id": "schema", +"type": "object", +"description": "The root schema comprises the entire JSON document.", +"required": [ + "foo" +], +"properties": { + "foo": { + "$id": "#/properties/foo", + "type": "boolean", + "description": "foo" + } +}, +"additionalProperties": false +}` + + exitCode, errOutput := testSchemasAnnotationWithJSONFile(regoContents, schema) + if exitCode == 0 { + t.Fatalf("didn't get expected error when schema is present and is defining a different type than being used.") + } + + if !strings.Contains(string(errOutput), "rego_type_error: match error") { + t.Fatalf("didn't get expected %s error when schema is defining a different type than being used; got: %v", ast.TypeErr, string(errOutput)) + } +} + +func TestWatchMode(t *testing.T) { + + files := map[string]string{ + "/policy.rego": `package foo +p := 1`, + "/policy_test.rego": `package foo + +test_p if { + p == 1 +}`, + } + + test.WithTempFS(files, func(root string) { + buf := test.BlockingWriter{} + + testParams := newTestCommandParams() + testParams.output = &buf + testParams.watch = true + testParams.count = 1 + + done := make(chan struct{}) + go func() { + _ = opaTest([]string{root}, testParams) + <-done + }() + + expected := "Watching for changes ..." + if !test.Eventually(t, 2*time.Second, func() bool { + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + // update the test + f, _ := os.OpenFile(path.Join(root, "policy_test.rego"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + _, err := f.WriteString("package foo\n test_p if { p == 2 }") + if err != nil { + t.Fatal(err) + } + err = f.Close() + if err != nil { + t.Fatal(err) + } + + r := regexp.MustCompile(`FAIL \(.*s\)`) + expected = `%ROOT%/policy_test.rego: +data.foo.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +******************************************************************************** +Watching for changes ... +` + if !test.Eventually(t, 2*time.Second, func() bool { + actual := r.ReplaceAllString(buf.String(), "FAIL (%TIME%)") + expected := strings.ReplaceAll(expected, "%ROOT%", root) + return strings.Contains(actual, expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + // update policy so test passes + f, _ = os.OpenFile(path.Join(root, "policy.rego"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + _, err = f.WriteString("package foo\n p := 2") + if err != nil { + t.Fatal(err) + } + + err = f.Close() + if err != nil { + t.Fatal(err) + } + + expected = `PASS: 1/1 +******************************************************************************** +Watching for changes ... +` + if !test.Eventually(t, 2*time.Second, func() bool { + expected := strings.ReplaceAll(expected, "%ROOT%", root) + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + // add new policy and test + if err := os.WriteFile(path.Join(root, "policy2.rego"), []byte("package bar\n q := \"hello\""), 0644); err != nil { + t.Fatal(err) + } + + if err := os.WriteFile(path.Join(root, "policy2_test.rego"), []byte("package bar\n test_q if { q == \"hello\" }"), 0644); err != nil { + t.Fatal(err) + } + + expected = `PASS: 2/2 +******************************************************************************** +Watching for changes ... +` + if !test.Eventually(t, 2*time.Second, func() bool { + expected := strings.ReplaceAll(expected, "%ROOT%", root) + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + testParams.stopChan <- syscall.SIGINT + done <- struct{}{} + }) +} + +func TestWatchMode_v0(t *testing.T) { + + files := map[string]string{ + "/policy.rego": `package foo +p := 1`, + "/policy_test.rego": `package foo + +test_p { + p == 1 +}`, + } + + test.WithTempFS(files, func(root string) { + buf := test.BlockingWriter{} + + testParams := newTestCommandParams() + testParams.output = &buf + testParams.watch = true + testParams.count = 1 + testParams.v0Compatible = true + + done := make(chan struct{}) + go func() { + _ = opaTest([]string{root}, testParams) + <-done + }() + + expected := "Watching for changes ..." + if !test.Eventually(t, 2*time.Second, func() bool { + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + // update the test + f, _ := os.OpenFile(path.Join(root, "policy_test.rego"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + _, err := f.WriteString("package foo\n test_p { p == 2 }") + if err != nil { + t.Fatal(err) + } + err = f.Close() + if err != nil { + t.Fatal(err) + } + + r := regexp.MustCompile(`FAIL \(.*s\)`) + expected = `%ROOT%/policy_test.rego: +data.foo.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +******************************************************************************** +Watching for changes ... +` + if !test.Eventually(t, 2*time.Second, func() bool { + actual := r.ReplaceAllString(buf.String(), "FAIL (%TIME%)") + expected := strings.ReplaceAll(expected, "%ROOT%", root) + return strings.Contains(actual, expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + // update policy so test passes + f, _ = os.OpenFile(path.Join(root, "policy.rego"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + _, err = f.WriteString("package foo\n p := 2") + if err != nil { + t.Fatal(err) + } + + err = f.Close() + if err != nil { + t.Fatal(err) + } + + expected = `PASS: 1/1 +******************************************************************************** +Watching for changes ... +` + if !test.Eventually(t, 2*time.Second, func() bool { + expected := strings.ReplaceAll(expected, "%ROOT%", root) + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + // add new policy and test + if err := os.WriteFile(path.Join(root, "policy2.rego"), []byte("package bar\n q := \"hello\""), 0644); err != nil { + t.Fatal(err) + } + + if err := os.WriteFile(path.Join(root, "policy2_test.rego"), []byte("package bar\n test_q { q == \"hello\" }"), 0644); err != nil { + t.Fatal(err) + } + + expected = `PASS: 2/2 +******************************************************************************** +Watching for changes ... +` + if !test.Eventually(t, 2*time.Second, func() bool { + expected := strings.ReplaceAll(expected, "%ROOT%", root) + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + testParams.stopChan <- syscall.SIGINT + done <- struct{}{} + }) +} + +func TestWatchModeWithDataFile(t *testing.T) { + + files := map[string]string{ + "/policy.rego": `package foo + +test_p if { + data.y == 1 +}`, + "/data.json": `{"y": 1}`, + } + + test.WithTempFS(files, func(root string) { + buf := test.BlockingWriter{} + + testParams := newTestCommandParams() + testParams.output = &buf + testParams.watch = true + testParams.count = 1 + + done := make(chan struct{}) + go func() { + _ = opaTest([]string{root}, testParams) + <-done + }() + + expected := "Watching for changes ..." + if !test.Eventually(t, 2*time.Second, func() bool { + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + // update the data + f, _ := os.OpenFile(path.Join(root, "data.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + _, err := f.WriteString(`{"y": 2}`) + if err != nil { + t.Fatal(err) + } + err = f.Close() + if err != nil { + t.Fatal(err) + } + + r := regexp.MustCompile(`FAIL \(.*s\)`) + expected = `%ROOT%/policy.rego: +data.foo.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +******************************************************************************** +Watching for changes ... +` + if !test.Eventually(t, 2*time.Second, func() bool { + actual := r.ReplaceAllString(buf.String(), "FAIL (%TIME%)") + expected := strings.ReplaceAll(expected, "%ROOT%", root) + return strings.Contains(actual, expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + // update policy so test passes + f, _ = os.OpenFile(path.Join(root, "policy.rego"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + _, err = f.WriteString("package foo\n test_p if { data.y == 2 }") + if err != nil { + t.Fatal(err) + } + + err = f.Close() + if err != nil { + t.Fatal(err) + } + + expected = `PASS: 1/1 +******************************************************************************** +Watching for changes ... +` + + if !test.Eventually(t, 2*time.Second, func() bool { + expected := strings.ReplaceAll(expected, "%ROOT%", root) + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + testParams.stopChan <- syscall.SIGINT + done <- struct{}{} + }) +} + +func TestWatchModeWhenDataFileRemoved(t *testing.T) { + files := map[string]string{ + "/policy.rego": `package foo + +test_p if { + data.y == 1 +}`, + "/data.json": `{"y": 1}`, + } + + test.WithTempFS(files, func(root string) { + buf := test.BlockingWriter{} + + testParams := newTestCommandParams() + testParams.output = &buf + testParams.watch = true + testParams.count = 1 + + done := make(chan struct{}) + go func() { + _ = opaTest([]string{root}, testParams) + <-done + }() + + expected := "Watching for changes ..." + if !test.Eventually(t, 2*time.Second, func() bool { + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + // update the data + f, _ := os.OpenFile(path.Join(root, "data.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + _, err := f.WriteString(`{"y": 2}`) + if err != nil { + t.Fatal(err) + } + err = f.Close() + if err != nil { + t.Fatal(err) + } + + r := regexp.MustCompile(`FAIL \(.*s\)`) + expected = `%ROOT%/policy.rego: +data.foo.test_p: FAIL (%TIME%) +-------------------------------------------------------------------------------- +FAIL: 1/1 +******************************************************************************** +Watching for changes ... +` + if !test.Eventually(t, 2*time.Second, func() bool { + actual := r.ReplaceAllString(buf.String(), "FAIL (%TIME%)") + expected := strings.ReplaceAll(expected, "%ROOT%", root) + return strings.Contains(actual, expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + // update the data back to the original state, so the opa test passes + f, _ = os.OpenFile(path.Join(root, "data.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + _, err = f.WriteString(`{"y": 1}`) + if err != nil { + t.Fatal(err) + } + err = f.Close() + if err != nil { + t.Fatal(err) + } + + expected = `PASS: 1/1 +******************************************************************************** +Watching for changes ... +` + + if !test.Eventually(t, 2*time.Second, func() bool { + expected := strings.ReplaceAll(expected, "%ROOT%", root) + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + // remove the data file, check that test fails afterward + err = os.Remove(path.Join(root, "data.json")) + if err != nil { + t.Fatal(err) + } + + time.Sleep(500 * time.Millisecond) + + testParams.stopChan <- syscall.SIGINT + done <- struct{}{} + }) +} + +func TestWatchModeBrokenFileRecovery(t *testing.T) { + + tests := []struct { + note string + fileName string + brokenFile string + fixedFile string + expectedOutput string + }{ + { + note: "empty data file (EOF read by watcher)", + fileName: "data.json", + fixedFile: `{"foo": "bar"}`, + expectedOutput: `1 error occurred during loading: %ROOT%/data.json: EOF +******************************************************************************** +Watching for changes ...`, + }, + { + note: "broken policy", + fileName: "broken_policy.rego", + brokenFile: "package foo\n bar {", + fixedFile: "package foo\n bar if {true}", + expectedOutput: `1 error occurred during loading: %ROOT%/broken_policy.rego:2: rego_parse_error: unexpected eof token + bar { + ^ +******************************************************************************** +Watching for changes ...`, + }, + } + + files := map[string]string{ + "/policy.rego": `package foo +p := 1`, + "/policy_test.rego": `package foo + +test_p if { + p == 1 +}`, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(files, func(root string) { + buf := test.BlockingWriter{} + + testParams := newTestCommandParams() + testParams.output = &buf + testParams.watch = true + testParams.count = 1 + testParams.errOutput = io.Discard + + done := make(chan struct{}) + go func() { + _ = opaTest([]string{root}, testParams) + <-done + }() + + expected := "Watching for changes ..." + if !test.Eventually(t, 2*time.Second, func() bool { + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + // create broken (possibly empty) file + f, _ := os.OpenFile(path.Join(root, tc.fileName), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + if len(tc.brokenFile) > 0 { + _, err := f.WriteString(tc.brokenFile) + if err != nil { + t.Fatal(err) + } + } + err := f.Close() + if err != nil { + t.Fatal(err) + } + + if !test.Eventually(t, 2*time.Second, func() bool { + expected := strings.ReplaceAll(tc.expectedOutput, "%ROOT%", root) + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", tc.expectedOutput, buf.String()) + } + buf.Reset() + + // write data to empty file + f, _ = os.OpenFile(path.Join(root, tc.fileName), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + _, err = f.WriteString(tc.fixedFile) + if err != nil { + t.Fatal(err) + } + err = f.Close() + if err != nil { + t.Fatal(err) + } + + expected = "Watching for changes ..." + if !test.Eventually(t, 2*time.Second, func() bool { + expected := strings.ReplaceAll(expected, "%ROOT%", root) + return strings.Contains(buf.String(), expected) + }) { + t.Fatalf("expected:\n\n%q\n\ngot:\n\n%q", expected, buf.String()) + } + buf.Reset() + + testParams.stopChan <- syscall.SIGINT + done <- struct{}{} + }) + }) + } +} + +func testExitCode(rego string, skipExitZero bool) int { + files := map[string]string{ + "test.rego": rego, + } + + var exitCode int + test.WithTempFS(files, func(path string) { + regoFilePath := filepath.Join(path, "test.rego") + + testParams := newTestCommandParams() + testParams.count = 1 + testParams.skipExitZero = skipExitZero + testParams.errOutput = io.Discard + testParams.output = io.Discard + + exitCode = opaTest([]string{regoFilePath}, testParams) + }) + return exitCode +} + +func TestExitCode(t *testing.T) { + testCases := map[string]struct { + Test string + ExitZeroOnSkipped bool + ExpectedExitCode int + }{ + "pass when no failed or skipped tests": { + Test: `package foo + + test_pass if { true } + `, + ExitZeroOnSkipped: false, + ExpectedExitCode: 0, + }, + "fail when failed tests": { + Test: `package foo + + test_pass if { true } + test_fail if { false } + `, + ExitZeroOnSkipped: false, + ExpectedExitCode: 2, + }, + "fail when skipped tests": { + Test: `package foo + + test_pass if { true } + todo_test_skip if { true } + `, + ExitZeroOnSkipped: false, + ExpectedExitCode: 2, + }, + "fail when failed tests and skipped tests": { + Test: `package foo + + test_pass if { true } + test_fail if { false } + todo_test_skip if { true } + `, + ExitZeroOnSkipped: false, + ExpectedExitCode: 2, + }, + "pass when skipped tests and exit zero on skipped": { + Test: `package foo + + test_pass if { true } + todo_test_skip if { true } + `, + ExitZeroOnSkipped: true, + ExpectedExitCode: 0, + }, + "fail when failed tests and exit zero on skipped": { + Test: `package foo + + test_pass if { true } + test_fail if { false } + `, + ExitZeroOnSkipped: true, + ExpectedExitCode: 2, + }, + "fail when failed tests, skipped tests and exit zero on skipped": { + Test: `package foo + + test_pass if { true } + test_fail if { false } + todo_test_skip if { true } + `, + ExitZeroOnSkipped: true, + ExpectedExitCode: 2, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + exitCode := testExitCode(tc.Test, tc.ExitZeroOnSkipped) + + if exitCode != tc.ExpectedExitCode { + t.Errorf("Expected exit code to be %d but got %d", tc.ExpectedExitCode, exitCode) + } + }) + } +} + +func TestCoverageThreshold(t *testing.T) { + testCases := []struct { + note string + modules map[string]string + threshold float64 + verbose bool + expectedErrOutput string + expectedExitCode int + }{ + { + note: "coverage threshold met", + modules: map[string]string{ + "test.rego": `package test + + p := 1 + test_p if { p == 1 }`, + }, + expectedExitCode: 0, + }, + { + note: "coverage threshold not met", + modules: map[string]string{ + "test.rego": `package test + + p := 1 if { + 1 == 1 + } + q := 2 + r := 3 + test_q if { q == 2 }`, + }, + threshold: 100, + expectedExitCode: 2, + expectedErrOutput: "Code coverage threshold not met: got 40.00 instead of 100.00\n", + }, + { + note: "coverage threshold not met (verbose)", + modules: map[string]string{ + "test.rego": `package test + + p := 1 if { + 1 == 1 + } + q := 2 + r := 3 + test_q if { q == 2 }`, + }, + threshold: 100, + expectedExitCode: 2, + verbose: true, + expectedErrOutput: `Code coverage threshold not met: got 40.00 instead of 100.00 +Lines not covered: + %ROOT%/test.rego:3-4 + %ROOT%/test.rego:7 +`, + }, + { + note: "coverage threshold not met (verbose, multiple files)", + modules: map[string]string{ + "policy1.rego": `package test + + p := 1 if { + 1 == 1 + } + q := 2 + r := 3`, + "policy2.rego": `package test + + s := 4 if { + 1 == 1 + 2 == 2 + } + t := 5 + u := 6 + v := 7`, + "test.rego": `package test + + test_q if { q == 2 } + test_t if { t == 5 }`, + }, + threshold: 100, + expectedExitCode: 2, + verbose: true, + expectedErrOutput: `Code coverage threshold not met: got 33.33 instead of 100.00 +Lines not covered: + %ROOT%/policy1.rego:3-4 + %ROOT%/policy1.rego:7 + %ROOT%/policy2.rego:3-5 + %ROOT%/policy2.rego:8-9 +`, + }, + } + + for _, tc := range testCases { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(tc.modules, func(root string) { + var buf bytes.Buffer + + testParams := newTestCommandParams() + testParams.threshold = tc.threshold + testParams.verbose = tc.verbose + testParams.count = 1 + testParams.errOutput = &buf + + exitCode := opaTest([]string{root}, testParams) + if exitCode != tc.expectedExitCode { + t.Fatalf("unexpected exit code: %d", exitCode) + } + + if len(tc.expectedErrOutput) == 0 && buf.Len() > 0 { + t.Fatalf("expected no error output but got:\n\n%q", buf.String()) + } + + expectedErrOutput := strings.ReplaceAll(tc.expectedErrOutput, "%ROOT%", root) + if buf.String() != expectedErrOutput { + t.Fatalf("expected error output to contain:\n\n%q\n\nbut got:\n\n%q", expectedErrOutput, buf.String()) + } + }) + }) + } +} + +type loadType int + +const ( + loadFile loadType = iota + loadBundle + loadTarball +) + +func (t loadType) String() string { + return [...]string{"file", "bundle", "bundle tarball"}[t] +} + +func TestRun_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + files map[string]string + expErrs []string + }{ + { + note: "v0 module", + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +} + +test_l { + l1 == l2 +}`, + }, + expErrs: []string{ + "test.rego:4: rego_parse_error: `if` keyword is required before rule body", + "test.rego:4: rego_parse_error: `contains` keyword is required for partial set rules", + "test.rego:8: rego_parse_error: `if` keyword is required before rule body", + }, + }, + { + note: "v1 module", + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + } + + loadTypes := []loadType{loadFile, loadBundle, loadTarball} + + for _, tc := range tests { + for _, loadType := range loadTypes { + t.Run(fmt.Sprintf("%s (%s)", tc.note, loadType), func(t *testing.T) { + var files map[string]string + if loadType != loadTarball { + files = tc.files + } + test.WithTempFS(files, func(root string) { + if loadType == loadTarball { + f, err := os.Create(filepath.Join(root, "bundle.tar.gz")) + if err != nil { + t.Fatal(err) + } + + testBundle := bundle.Bundle{ + Data: map[string]any{}, + } + for k, v := range tc.files { + testBundle.Modules = append(testBundle.Modules, bundle.ModuleFile{ + Path: k, + Raw: []byte(v), + }) + } + + if err := bundle.Write(f, testBundle); err != nil { + t.Fatal(err) + } + } + + var buf bytes.Buffer + var errBuf bytes.Buffer + + testParams := newTestCommandParams() + testParams.bundleMode = loadType == loadBundle + testParams.count = 1 + testParams.output = &buf + testParams.errOutput = &errBuf + + var paths []string + if loadType == loadTarball { + paths = []string{filepath.Join(root, "bundle.tar.gz")} + } else { + paths = []string{root} + } + + exitCode := opaTest(paths, testParams) + if len(tc.expErrs) > 0 { + if exitCode == 0 { + t.Fatalf("expected non-zero exit code") + } + + for _, expErr := range tc.expErrs { + if actual := errBuf.String(); !strings.Contains(actual, expErr) { + t.Fatalf("expected error output to contain:\n\n%q\n\nbut got:\n\n%q", expErr, actual) + } + } + } else { + if exitCode != 0 { + t.Fatalf("unexpected exit code: %d", exitCode) + } + + if errBuf.Len() > 0 { + t.Fatalf("expected no error output but got:\n\n%q", buf.String()) + } + + expected := "PASS: 1/1" + if actual := buf.String(); !strings.Contains(actual, expected) { + t.Fatalf("expected output to contain:\n\n%s\n\nbut got:\n\n%q", expected, actual) + } + } + }) + }) + } + } +} + +func TestRunWithRegoV1Capability(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + capabilities *ast.Capabilities + files map[string]string + expErrs []string + }{ + { + note: "v0 module, v0-compatible, no capabilities", + v0Compatible: true, + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +} + +test_l { + l1 == l2 +}`, + }, + }, + { + note: "v0 module, v0-compatible, v0 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +} + +test_l { + l1 == l2 +}`, + }, + }, + { + note: "v0 module, v0-compatible, v1 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +} + +test_l { + l1 == l2 +}`, + }, + }, + + { + note: "v0 module, not v0-compatible, no capabilities", + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +} + +test_l { + l1 == l2 +}`, + }, + expErrs: []string{ + "test.rego:4: rego_parse_error: `if` keyword is required before rule body", + "test.rego:4: rego_parse_error: `contains` keyword is required for partial set rules", + "test.rego:8: rego_parse_error: `if` keyword is required before rule body", + }, + }, + { + note: "v0 module, not v0-compatible, v0 capabilities", + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +} + +test_l { + l1 == l2 +}`, + }, + expErrs: []string{ + "test.rego:4: rego_parse_error: `if` keyword is required before rule body", + "test.rego:4: rego_parse_error: `contains` keyword is required for partial set rules", + "test.rego:8: rego_parse_error: `if` keyword is required before rule body", + }, + }, + { + note: "v0 module, not v0-compatible, v0 capabilities without rego_v1 feature", + capabilities: capsWithoutFeat(ast.RegoV0, ast.FeatureRegoV1), + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +} + +test_l { + l1 == l2 +}`, + }, + expErrs: []string{ + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "v0 module, not v0-compatible, v1 capabilities", + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +} + +test_l { + l1 == l2 +}`, + }, + expErrs: []string{ + "test.rego:4: rego_parse_error: `if` keyword is required before rule body", + "test.rego:4: rego_parse_error: `contains` keyword is required for partial set rules", + "test.rego:8: rego_parse_error: `if` keyword is required before rule body", + }, + }, + + { + note: "v1 module, v0-compatible, no capabilities", + v0Compatible: true, + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + expErrs: []string{ + "test.rego:4: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v1 module, v0-compatible, v0 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + expErrs: []string{ + "test.rego:4: rego_parse_error: var cannot be used for rule name", + }, + }, + { + note: "v1 module, v0-compatible, v1 capabilities", + v0Compatible: true, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + expErrs: []string{ + "test.rego:4: rego_parse_error: var cannot be used for rule name", + }, + }, + + { + note: "v1 module, not v0-compatible, no capabilities", + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v1 module, not v0-compatible, v0 capabilities", + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v1 module, not v0-compatible, v0 capabilities without rego_v1 feature", + capabilities: capsWithoutFeat(ast.RegoV0, ast.FeatureRegoV1), + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + expErrs: []string{ + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "v1 module, not v0-compatible, v1 capabilities", + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + } + + loadTypes := []loadType{loadFile, loadBundle, loadTarball} + + for _, tc := range tests { + for _, loadType := range loadTypes { + t.Run(fmt.Sprintf("%s (%s)", tc.note, loadType), func(t *testing.T) { + var files map[string]string + if loadType != loadTarball { + files = tc.files + } + test.WithTempFS(files, func(root string) { + if loadType == loadTarball { + f, err := os.Create(filepath.Join(root, "bundle.tar.gz")) + if err != nil { + t.Fatal(err) + } + + testBundle := bundle.Bundle{ + Data: map[string]any{}, + } + for k, v := range tc.files { + testBundle.Modules = append(testBundle.Modules, bundle.ModuleFile{ + Path: k, + Raw: []byte(v), + }) + } + + if err := bundle.Write(f, testBundle); err != nil { + t.Fatal(err) + } + } + + var buf bytes.Buffer + var errBuf bytes.Buffer + + testParams := newTestCommandParams() + testParams.bundleMode = loadType == loadBundle + testParams.count = 1 + testParams.output = &buf + testParams.errOutput = &errBuf + testParams.v0Compatible = tc.v0Compatible + testParams.capabilities.C = tc.capabilities + + var paths []string + if loadType == loadTarball { + paths = []string{filepath.Join(root, "bundle.tar.gz")} + } else { + paths = []string{root} + } + + exitCode := opaTest(paths, testParams) + if len(tc.expErrs) > 0 { + if exitCode == 0 { + t.Fatalf("expected non-zero exit code") + } + + for _, expErr := range tc.expErrs { + if actual := errBuf.String(); !strings.Contains(actual, expErr) { + t.Fatalf("expected error output to contain:\n\n%q\n\nbut got:\n\n%q", expErr, actual) + } + } + } else { + if exitCode != 0 { + t.Fatalf("unexpected exit code: %d", exitCode) + } + + if errBuf.Len() > 0 { + t.Fatalf("expected no error output but got:\n\n%q", buf.String()) + } + + expected := "PASS: 1/1" + if actual := buf.String(); !strings.Contains(actual, expected) { + t.Fatalf("expected output to contain:\n\n%s\n\nbut got:\n\n%q", expected, actual) + } + } + }) + }) + } + } +} + +func TestRun_CompatibleFlags(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + v1Compatible bool + files map[string]string + expErr string + }{ + { + note: "v0 module, no imports", + v0Compatible: true, + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + expErr: "rego_parse_error", + }, + { + note: "v0 module, rego.v1 imported", + v0Compatible: true, + files: map[string]string{ + "/test.rego": `package test + +import rego.v1 + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v0 module, future.keywords imported", + v0Compatible: true, + files: map[string]string{ + "/test.rego": `package test + +import future.keywords + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + + { + note: "v1 compatible module, no imports", + v1Compatible: true, + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v1 compatible module, rego.v1 imported", + v1Compatible: true, + files: map[string]string{ + "/test.rego": `package test + +import rego.v1 + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v1 compatible module, future.keywords imported", + v1Compatible: true, + files: map[string]string{ + "/test.rego": `package test + +import future.keywords + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + + // v0 takes precedence over v1 + { + note: "v0+v1 module, no imports", + v0Compatible: true, + v1Compatible: true, + files: map[string]string{ + "/test.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + expErr: "rego_parse_error", + }, + { + note: "v0+v1 module, rego.v1 imported", + v0Compatible: true, + v1Compatible: true, + files: map[string]string{ + "/test.rego": `package test + +import rego.v1 + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v0+v1 module, future.keywords imported", + v0Compatible: true, + v1Compatible: true, + files: map[string]string{ + "/test.rego": `package test + +import future.keywords + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + } + + loadTypes := []loadType{loadFile, loadBundle, loadTarball} + + for _, tc := range tests { + for _, loadType := range loadTypes { + t.Run(fmt.Sprintf("%s (%s)", tc.note, loadType), func(t *testing.T) { + var files map[string]string + if loadType != loadTarball { + files = tc.files + } + test.WithTempFS(files, func(root string) { + if loadType == loadTarball { + f, err := os.Create(filepath.Join(root, "bundle.tar.gz")) + if err != nil { + t.Fatal(err) + } + + testBundle := bundle.Bundle{ + Data: map[string]any{}, + } + for k, v := range tc.files { + testBundle.Modules = append(testBundle.Modules, bundle.ModuleFile{ + Path: k, + Raw: []byte(v), + }) + } + + if err := bundle.Write(f, testBundle); err != nil { + t.Fatal(err) + } + } + + var buf bytes.Buffer + var errBuf bytes.Buffer + + testParams := newTestCommandParams() + testParams.v0Compatible = tc.v0Compatible + testParams.v1Compatible = tc.v1Compatible + testParams.bundleMode = loadType == loadBundle + testParams.count = 1 + testParams.output = &buf + testParams.errOutput = &errBuf + + var paths []string + if loadType == loadTarball { + paths = []string{filepath.Join(root, "bundle.tar.gz")} + } else { + paths = []string{root} + } + + exitCode := opaTest(paths, testParams) + if tc.expErr != "" { + if exitCode == 0 { + t.Fatalf("expected non-zero exit code") + } + + if actual := errBuf.String(); !strings.Contains(actual, tc.expErr) { + t.Fatalf("expected error output to contain:\n\n%q\n\nbut got:\n\n%q", tc.expErr, actual) + } + } else { + if exitCode != 0 { + t.Fatalf("unexpected exit code: %d", exitCode) + } + + if errBuf.Len() > 0 { + t.Fatalf("expected no error output but got:\n\n%q", buf.String()) + } + + expected := "PASS: 1/1" + if actual := buf.String(); !strings.Contains(actual, expected) { + t.Fatalf("expected output to contain:\n\n%s\n\nbut got:\n\n%q", expected, actual) + } + } + }) + }) + } + } +} + +func TestWithBundleRegoVersion(t *testing.T) { + tests := []struct { + note string + files map[string]string + expErr string + }{ + { + note: "v0.x bundle, no imports", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + expErr: "rego_parse_error", + }, + { + note: "v0.x bundle, rego.v1 imported", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test + +import rego.v1 + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v0.x bundle, future.keywords imported", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test + +import future.keywords + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v0 bundle, v1 per-file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } +}`, + "policy1.rego": `package test +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +}`, + "policy2.rego": `package test +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v0 bundle, v1 per-file override (glob)", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/policy2.rego": 1 + } +}`, + "policy1.rego": `package test +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +}`, + "policy2.rego": `package test +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v0 bundle, v1 per-file override, incompatible", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } +}`, + "policy1.rego": `package test +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +}`, + "policy2.rego": `package test +test_l { + l1 == l2 +}`, + }, + expErr: "rego_parse_error", + }, + + { + note: "v1.0 bundle, no imports", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v1.0 bundle, rego.v1 imported", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test + +import rego.v1 + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v1.0 bundle, future.keywords imported", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test + +import future.keywords + +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +} + +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v1 bundle, v0 per-file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } +}`, + "policy1.rego": `package test +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +}`, + "policy2.rego": `package test +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v1 bundle, v0 per-file override (glob)", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "*/policy1.rego": 0 + } +}`, + "policy1.rego": `package test +l1 := {1, 3, 5} +l2[v] { + v := l1[_] +}`, + "policy2.rego": `package test +test_l if { + l1 == l2 +}`, + }, + }, + { + note: "v1 bundle, v0 per-file override, incompatible", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } +}`, + "policy1.rego": `package test +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +}`, + "policy2.rego": `package test +test_l if { + l1 == l2 +}`, + }, + expErr: "rego_parse_error", + }, + } + + bundleTypeCases := []struct { + note string + tar bool + }{ + { + "bundle dir", false, + }, + { + "bundle tar", true, + }, + } + + v1CompatibleFlagCases := []struct { + note string + used bool + }{ + { + "no --v1-compatible", false, + }, + { + "--v1-compatible", true, + }, + } + + for _, bundleType := range bundleTypeCases { + for _, v1CompatibleFlag := range v1CompatibleFlagCases { + for _, tc := range tests { + + t.Run(fmt.Sprintf("%s, %s, %s", bundleType.note, v1CompatibleFlag.note, tc.note), func(t *testing.T) { + files := map[string]string{} + if bundleType.tar { + files["bundle.tar.gz"] = "" + } else { + maps.Copy(files, tc.files) + } + + test.WithTempFS(files, func(root string) { + p := root + if bundleType.tar { + p = filepath.Join(root, "bundle.tar.gz") + files := make([][2]string, 0, len(tc.files)) + for k, v := range tc.files { + files = append(files, [2]string{k, v}) + } + buf := archive.MustWriteTarGz(files) + bf, err := os.Create(p) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + var buf bytes.Buffer + var errBuf bytes.Buffer + + testParams := newTestCommandParams() + testParams.v1Compatible = v1CompatibleFlag.used + testParams.bundleMode = true + testParams.count = 1 + testParams.output = &buf + testParams.errOutput = &errBuf + + exitCode := opaTest([]string{p}, testParams) + if tc.expErr != "" { + if exitCode == 0 { + t.Fatalf("expected non-zero exit code") + } + + if actual := errBuf.String(); !strings.Contains(actual, tc.expErr) { + t.Fatalf("expected error output to contain:\n\n%q\n\nbut got:\n\n%q", tc.expErr, actual) + } + } else { + if exitCode != 0 { + t.Fatalf("unexpected exit code: %d", exitCode) + } + + if errBuf.Len() > 0 { + t.Fatalf("expected no error output but got:\n\n%q", buf.String()) + } + + expected := "PASS: 1/1" + if actual := buf.String(); !strings.Contains(actual, expected) { + t.Fatalf("expected output to contain:\n\n%s\n\nbut got:\n\n%q", expected, actual) + } + } + }) + }) + } + } + } +} + +// Assert that a failing test doesn't cause a panic. +// https://github.com/open-policy-agent/opa/issues/7205 +func TestTestBenchFailingTest(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + test_fail if false`, + } + + test.WithTempFS(files, func(path string) { + fp := filepath.Join(path, "test.rego") + tp := newTestCommandParams() + tp.benchmark = true + tp.count = 1 + + exitCode := opaTest([]string{fp}, tp) + if exitCode == 0 { + t.Fatalf("Expected exit code != 0, got %d", exitCode) + } + }) +} + +func TestTestRunParallel(t *testing.T) { + tests := []struct { + note string + parallel int + }{ + { + note: "default workers", + parallel: 0, + }, + { + note: "1 workers", + parallel: 1, + }, + { + note: "2 workers", + parallel: 2, + }, + { + note: "100 workers", + parallel: 100, + }, + } + + for _, tc := range tests { + testParams := newTestCommandParams() + testParams.parallel = tc.parallel + + files := map[string]string{ + "policy1.rego": `package test +l1 := {1, 3, 5} +l2 contains v if { + v := l1[_] +}`, + "policy2.rego": `package test +test_l if { + l1 == l2 +}`} + + var exitCode int + test.WithTempFS(files, func(root string) { + exitCode = opaTest([]string{root}, testParams) + }) + + if exitCode > 0 { + t.Fatalf("unexpected exit code: %d", exitCode) + } + } +} + +func TestWithDefaultRegoPlugin(t *testing.T) { + // We're injecting a default rego plugin that always returns true. + // If it's picked as a default (as intended), the tests run here will also + // yield true. If it wasn't picked, we'd use topdown, and would see a failing + // test. + tp := &testPlugin{} + rego.RegisterPlugin(targetPlugin, tp) + t.Cleanup(func() { tp.target = targetPlugin }) + + t.Run("test", func(t *testing.T) { + test.WithTempFS(map[string]string{"test.rego": "package test\ntest_true if false"}, func(path string) { + fp := filepath.Join(path, "test.rego") + tp := newTestCommandParams() + tp.output = io.Discard + tp.count = 1 + + exitCode := opaTest([]string{fp}, tp) + if exitCode != 0 { + t.Fatalf("Expected exit code 0, got %d", exitCode) + } + }) + }) + t.Run("eval", func(t *testing.T) { + params := newEvalCommandParams() + params.fail = true + query := "2+2 = 5" // unification will fail ("2+2 == 5" would be false, but defined) + + defined, err := eval([]string{query}, params, io.Discard) + if err != nil { + t.Fatal("unexpected error", err) + } + if !defined { + t.Errorf("expected defined result") + } + }) + + t.Run("repl", func(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := repl.New(store, "", &buffer, "", 0, "") + if err := repl.OneShot(ctx, "2+2==5"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := buffer.String() + if result != "true\n" { + t.Errorf("Expected result to be false but got: %v", result) + } + }) +} + +type testPlugin struct { + target string +} + +func (t *testPlugin) IsTarget(tgt string) bool { + return tgt == t.target // t == "" makes it the global default +} + +func (*testPlugin) PrepareForEval(context.Context, *ir.Policy, ...rego.PrepareOption) (rego.TargetPluginEval, error) { + return &testPlugin{}, nil +} + +func (*testPlugin) Eval(context.Context, *rego.EvalContext, ast.Value) (ast.Value, error) { + return ast.NewSet(ast.NewTerm(ast.NewObject([2]*ast.Term{ast.StringTerm("^term1"), ast.BooleanTerm(true)}))), nil +} + +const targetPlugin = "rego_test_default_plugin" diff --git a/third_party/opa/cmd/utils.go b/third_party/opa/cmd/utils.go new file mode 100644 index 000000000000..179767f3c74a --- /dev/null +++ b/third_party/opa/cmd/utils.go @@ -0,0 +1,30 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" +) + +type ExitError struct { + Exit int + wrapped error +} + +func newExitError(exit int) error { + return &ExitError{Exit: exit} +} + +func newExitErrorWrap(exit int, err error) error { + return &ExitError{Exit: exit, wrapped: err} +} + +func (c *ExitError) Error() string { + return fmt.Sprintf("exit %d", c.Exit) +} + +func (c *ExitError) Unwrap() error { + return c.wrapped +} diff --git a/third_party/opa/cmd/version.go b/third_party/opa/cmd/version.go new file mode 100644 index 000000000000..d3f4f50257ab --- /dev/null +++ b/third_party/opa/cmd/version.go @@ -0,0 +1,85 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "context" + "fmt" + "io" + "os" + + "github.com/open-policy-agent/opa/v1/ast" + version2 "github.com/open-policy-agent/opa/v1/version" + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/cmd/internal/env" + "github.com/open-policy-agent/opa/internal/report" +) + +func initVersion(root *cobra.Command, brand string) { + var check bool + var versionCommand = &cobra.Command{ + Use: "version", + Short: `Print the version of ` + brand, + Long: `Show version and build information for ` + brand + `.`, + PreRunE: func(cmd *cobra.Command, args []string) error { + return env.CmdFlags.CheckEnvironmentVariables(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceErrors = true + cmd.SilenceUsage = true + return generateCmdOutput(os.Stdout, check) + }, + } + + // The version command can also be used to check for the latest released OPA version. + // Some tools could use this for feature flagging purposes and hence this option is OFF by-default. + versionCommand.Flags().BoolVarP(&check, "check", "c", false, "check for latest "+brand+" release") + root.AddCommand(versionCommand) +} + +func generateCmdOutput(out io.Writer, check bool) error { + fmt.Fprintln(out, "Version: "+version2.Version) + fmt.Fprintln(out, "Build Commit: "+version2.Vcs) + fmt.Fprintln(out, "Build Timestamp: "+version2.Timestamp) + fmt.Fprintln(out, "Build Hostname: "+version2.Hostname) + fmt.Fprintln(out, "Go Version: "+version2.GoVersion) + fmt.Fprintln(out, "Platform: "+version2.Platform) + fmt.Fprintln(out, "Rego Version: "+ast.DefaultRegoVersion.String()) + + var wasmAvailable string + + if version2.WasmRuntimeAvailable() { + wasmAvailable = "available" + } else { + wasmAvailable = "unavailable" + } + + fmt.Fprintln(out, "WebAssembly: "+wasmAvailable) + + if check { + err := checkOPAUpdate(out) + if err != nil { + fmt.Fprintf(out, "Error: %v\n", err) + return err + } + } + return nil +} + +func checkOPAUpdate(out io.Writer) error { + reporter, err := report.New(report.Options{}) + if err != nil { + return err + } + + resp, err := reporter.SendReport(context.Background()) + if err != nil { + return err + } + + fmt.Fprintln(out, resp.Pretty()) + return nil +} diff --git a/third_party/opa/cmd/version_test.go b/third_party/opa/cmd/version_test.go new file mode 100644 index 000000000000..1ad665037fdc --- /dev/null +++ b/third_party/opa/cmd/version_test.go @@ -0,0 +1,131 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cmd + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "runtime" + "sort" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/report" +) + +func TestGenerateCmdOutputDisableCheckFlag(t *testing.T) { + var stdout bytes.Buffer + + if err := generateCmdOutput(&stdout, false); err != nil { + t.Fatal(err) + } + + expectOutputKeys(t, stdout.String(), []string{ + "Version", + "Build Commit", + "Build Timestamp", + "Build Hostname", + "Go Version", + "Platform", + "WebAssembly", + "Rego Version", + }) +} + +func TestGenerateCmdOutputWithCheckFlagNoError(t *testing.T) { + // to support testing on all supported platforms + downloadLink := fmt.Sprintf("https://openpolicyagent.org/downloads/v100.0.0/opa_%v_%v", + runtime.GOOS, runtime.GOARCH) + + if runtime.GOARCH == "arm64" { + downloadLink = fmt.Sprintf("%v_static", downloadLink) + } + + if strings.HasPrefix(runtime.GOOS, "win") { + downloadLink = fmt.Sprintf("%v.exe", downloadLink) + } + + resp := &report.GHResponse{ + TagName: "v100.0.0", + Download: downloadLink, + ReleaseNotes: "https://github.com/open-policy-agent/opa/releases/tag/v100.0.0", + } + + // test server + baseURL, teardown := getTestServer(resp, http.StatusOK) + defer teardown() + + t.Setenv("OPA_TELEMETRY_SERVICE_URL", baseURL) + + var stdout bytes.Buffer + + if err := generateCmdOutput(&stdout, true); err != nil { + t.Fatal(err) + } + + expectOutputKeys(t, stdout.String(), []string{ + "Version", + "Build Commit", + "Build Timestamp", + "Build Hostname", + "Go Version", + "Platform", + "WebAssembly", + "Latest Upstream Version", + "Release Notes", + "Download", + "Rego Version", + }) +} + +func TestCheckOPAUpdateBadURL(t *testing.T) { + url := "http://foo:8112" + t.Setenv("OPA_TELEMETRY_SERVICE_URL", url) + + err := checkOPAUpdate(nil) + if err == nil { + t.Fatal("Expected error but got nil") + } +} + +func expectOutputKeys(t *testing.T, stdout string, expectedKeys []string) { + t.Helper() + + lines := strings.Split(strings.Trim(stdout, "\n"), "\n") + gotKeys := make([]string, 0, len(lines)) + + for _, line := range lines { + gotKeys = append(gotKeys, strings.Split(line, ":")[0]) + } + + sort.Strings(expectedKeys) + sort.Strings(gotKeys) + + if len(expectedKeys) != len(gotKeys) { + t.Fatalf("expected %v but got %v", expectedKeys, gotKeys) + } + + for i, got := range gotKeys { + if expectedKeys[i] != got { + t.Fatalf("expected %v but got %v", expectedKeys, gotKeys) + } + } +} + +func getTestServer(update any, statusCode int) (baseURL string, teardownFn func()) { + mux := http.NewServeMux() + ts := httptest.NewServer(mux) + + mux.HandleFunc("/repos/open-policy-agent/opa/releases/latest", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(statusCode) + bs, _ := json.Marshal(update) + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(bs) + }) + return ts.URL, ts.Close +} diff --git a/third_party/opa/compile/compile.go b/third_party/opa/compile/compile.go new file mode 100644 index 000000000000..decae108eff9 --- /dev/null +++ b/third_party/opa/compile/compile.go @@ -0,0 +1,37 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package compile implements bundles compilation and linking. +package compile + +import ( + "github.com/open-policy-agent/opa/ast" + v1 "github.com/open-policy-agent/opa/v1/compile" +) + +const ( + // TargetRego is the default target. The source rego is copied (potentially + // rewritten for optimization purpsoes) into the bundle. The target supports + // base documents. + TargetRego = v1.TargetRego + + // TargetWasm is an alternative target that compiles the policy into a wasm + // module instead of Rego. The target supports base documents. + TargetWasm = v1.TargetWasm + + // TargetPlan is an altertive target that compiles the policy into an + // imperative query plan that can be further transpiled or interpreted. + TargetPlan = v1.TargetPlan +) + +// Targets contains the list of targets supported by the compiler. +var Targets = v1.Targets + +// Compiler implements bundle compilation and linking. +type Compiler = v1.Compiler + +// New returns a new compiler instance that can be invoked. +func New() *Compiler { + return v1.New().WithRegoVersion(ast.DefaultRegoVersion) +} diff --git a/third_party/opa/compile/compile_test.go b/third_party/opa/compile/compile_test.go new file mode 100644 index 000000000000..e197ed75d04c --- /dev/null +++ b/third_party/opa/compile/compile_test.go @@ -0,0 +1,774 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package compile + +import ( + "context" + "fmt" + "io/fs" + "maps" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/bundle" + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/loader" + "github.com/open-policy-agent/opa/util/test" +) + +func TestCompilerDefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expRegoVersion ast.RegoVersion + expErrs []string + }{ + { + note: "v0", // Default rego-version + module: ` + package test + + p[x] { + x = "a" + }`, + expRegoVersion: ast.RegoV0, + }, + { + note: "import rego.v1", + module: ` + package test + import rego.v1 + + p contains x if { + x = "a" + }`, + expRegoVersion: ast.RegoV0, + }, + { + note: "v1", // NOT default rego-version + module: ` + package test + + p contains x if { + x = "a" + }`, + expRegoVersion: ast.RegoV1, + expErrs: []string{ + "test.rego:4: rego_parse_error: var cannot be used for rule name", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.module, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root) + + err := compiler.Build(context.Background()) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatal("expected error, got none") + } + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected error to contain:\n\n%s\n\ngot:\n\n%v", expErr, err) + } + } + } else { + if err != nil { + t.Fatal(err) + } + + // Verify result is just bundle load. + exp, err := loader.NewFileLoader().WithFS(fsys).AsBundle(root) + if err != nil { + panic(err) + } + + err = exp.FormatModules(false) + if err != nil { + t.Fatal(err) + } + + if !compiler.Bundle().Equal(*exp) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", compiler.Bundle(), exp) + } + } + }) + } + }) + } +} + +func TestCompilerLoadAsBundleWithBundleRegoVersion(t *testing.T) { + tests := []struct { + note string + files map[string]string + expErrs []string + }{ + { + note: "No bundle rego version (default version)", + files: map[string]string{ + ".manifest": `{}`, + "test.rego": `package test +import rego.v1 +p[1] if { + input.x == 2 +}`, + }, + }, + { + note: "v0 bundle rego version", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "test.rego": `package test +p[1] { + input.x == 2 +}`, + }, + }, + { + note: "v0 bundle rego version, missing keyword imports", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "test.rego": `package test +p contains 1 if { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v1 bundle rego version", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "test.rego": `package test +p contains 1 if { + input.x == 2 +}`, + }, + }, + { + note: "v1 bundle rego version, no keywords", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "test.rego": `package test +p[1] { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1 bundle rego version, duplicate imports", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "test.rego": `package test +import data.foo +import data.foo + +p contains 1 if { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + // file overrides + { + note: "v0 bundle rego version, v1 file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "test1.rego": `package test +p["A"] { + input.x == 1 +}`, + "test2.rego": `package test +p contains "B" if { + input.x == 2 +}`, + }, + }, + { + note: "v0 bundle rego version, v1 file override, missing file", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "test1.rego": `package test +p["A"] { + input.x == 1 +}`, + }, + }, + { + note: "v0 bundle rego version, v1 file override, no keywords", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "test1.rego": `package test +p["A"] { + input.x == 1 +}`, + "test2.rego": `package test +p["B"] { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0 bundle rego version, v1 file override, duplicate imports", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "test1.rego": `package test +p["A"] { + input.x == 1 +}`, + "test2.rego": `package test +import data.foo +import data.foo + +p contains "B" if { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v1 bundle rego version, v0 file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "*/test1.rego": 0 + } +}`, + "test1.rego": `package test +p["A"] { + input.x == 1 +}`, + "test2.rego": `package test +p contains "B" if { + input.x == 2 +}`, + }, + }, + { + note: "v1 bundle rego version, v0 file override, no import", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "*/test1.rego": 0 + } +}`, + "test1.rego": `package test +p contains "A" if { + input.x == 1 +}`, + "test2.rego": `package test +p contains "B" if { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: string cannot be used for rule name", + }, + }, + } + + bundleTypeCases := []struct { + note string + tar bool + }{ + { + "bundle dir", false, + }, + { + "bundle tar", true, + }, + } + + for _, bundleType := range bundleTypeCases { + for _, tc := range tests { + ctx := context.Background() + t.Run(fmt.Sprintf("%s, %s", bundleType.note, tc.note), func(t *testing.T) { + files := map[string]string{} + if bundleType.tar { + files["bundle.tar"] = "" + } else { + maps.Copy(files, tc.files) + } + + test.WithTestFS(tc.files, false, func(root string, fsys fs.FS) { + var path string + if bundleType.tar { + path = filepath.Join(root, "bundle.tar.gz") + files := make([][2]string, 0, len(tc.files)) + for k, v := range tc.files { + files = append(files, [2]string{k, v}) + } + buf := archive.MustWriteTarGz(files) + + bf, err := os.Create(path) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } else { + path = root + } + + compiler := New(). + WithFS(fsys). + WithPaths(path). + WithAsBundle(true) + + err := compiler.Build(ctx) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatal("expected error, got none") + } + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected error to contain:\n\n%s\n\ngot:\n\n%v", expErr, err) + } + } + } else if err != nil { + t.Fatal(err) + } + }) + }) + } + } +} + +func TestCompilerBundleMergeWithBundleRegoVersion(t *testing.T) { + regoV0 := ast.RegoV0.Int() + regoV1 := ast.RegoV1.Int() + regoDef := ast.RegoV0.Int() + + tests := []struct { + note string + bundles []*bundle.Bundle + regoVersion ast.RegoVersion + expGlobalRegoVersion *int + expFileRegoVersions map[string]int + }{ + { + note: "single bundle, no bundle rego version (default version)", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{}, + }, + }, + expGlobalRegoVersion: ®oDef, + expFileRegoVersions: map[string]int{}, + }, + { + note: "single bundle, global rego version", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + RegoVersion: ®oV1, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{}, + }, + }, + expGlobalRegoVersion: ®oV1, + expFileRegoVersions: map[string]int{}, + }, + { + note: "no global rego versions", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{}, + }, + { + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{}, + }, + }, + regoVersion: ast.RegoV1, + expGlobalRegoVersion: ®oV1, + }, + { + note: "global rego versions, v1 bundles, v0 provided", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + RegoVersion: ®oV1, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "a/test1.rego", + URL: "a/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package a"), + }, + }, + }, + { + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + RegoVersion: ®oV1, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "b/test1.rego", + URL: "b/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package b"), + }, + }, + }, + }, + regoVersion: ast.RegoV0, + // global rego-version in bundles are dropped in favor of the provided rego-version + expGlobalRegoVersion: ®oV0, + expFileRegoVersions: map[string]int{ + "/a/test1.rego": 1, + "/b/test1.rego": 1, + }, + }, + { + note: "global rego versions, v0 bundles, v1 provided", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + RegoVersion: ®oV0, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "a/test1.rego", + URL: "a/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package a"), + }, + }, + }, + { + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + RegoVersion: ®oV0, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "b/test1.rego", + URL: "b/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package b"), + }, + }, + }, + }, + regoVersion: ast.RegoV1, + // global rego-version in bundles are dropped in favor of the provided rego-version + expGlobalRegoVersion: ®oV1, + expFileRegoVersions: map[string]int{ + "/a/test1.rego": 0, + "/b/test1.rego": 0, + }, + }, + { + note: "different global rego versions", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + RegoVersion: ®oV0, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "a/test1.rego", + URL: "a/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package a"), + }, + }, + }, + { + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + RegoVersion: ®oV1, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "b/test1.rego", + URL: "b/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package b"), + }, + }, + }, + }, + regoVersion: ast.RegoV0, + // global rego-version in bundles are dropped in favor of the provided rego-version + expGlobalRegoVersion: ®oV0, + expFileRegoVersions: map[string]int{ + "/b/test1.rego": 1, + }, + }, + { + note: "different global rego versions, per-file overrides", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + RegoVersion: ®oV1, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "a/test1.rego", + URL: "a/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package a"), + }, + { + Path: "a/test2.rego", + URL: "a/test2.rego", + RelativePath: "/test2.rego", + Raw: []byte("package a"), + }, + }, + }, + { + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + RegoVersion: ®oV1, + FileRegoVersions: map[string]int{ + "/test1.rego": 0, + }, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + // we don't expect this file to get an individual rego-version in the result, as + // it has the same rego-version as the global rego-version + Path: "b/test1.rego", + URL: "b/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package b"), + }, + { + Path: "b/test2.rego", + URL: "b/test2.rego", + RelativePath: "/test2.rego", + Raw: []byte("package b"), + }, + }, + }, + { + Manifest: bundle.Manifest{ + RegoVersion: ®oV0, + Roots: &[]string{"c"}, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + // we don't expect these files to get individual rego-versions in the result, + // as they have the same rego-version as the global rego-version + { + Path: "c/test1.rego", + URL: "c/test1.rego", + RelativePath: "test1.rego", + Raw: []byte("package c"), + }, + { + Path: "c/test2.rego", + URL: "c/test2.rego", + RelativePath: "test2.rego", + Raw: []byte("package c"), + }, + }, + }, + }, + regoVersion: ast.RegoV0, + // global rego-version in bundles are dropped in favor of the provided rego-version + expGlobalRegoVersion: ®oV0, + // rego-versions is expected for all modules with different rego-version than the global rego-version + expFileRegoVersions: map[string]int{ + "/a/test1.rego": 1, + "/a/test2.rego": 1, + "/b/test2.rego": 1, + }, + }, + { + note: "glob per-file overrides", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + RegoVersion: ®oV0, + FileRegoVersions: map[string]int{ + "a/*": 1, + }, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "a/foo/test.rego", + URL: "a/foo/test.rego", + Raw: []byte("package a"), + }, + { + Path: "a/bar/test.rego", + URL: "a/bar/test.rego", + Raw: []byte("package a"), + }, + { + Path: "a/baz/test.rego", + URL: "a/baz/test.rego", + Raw: []byte("package a"), + }, + }, + }, + { + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + RegoVersion: ®oV1, + FileRegoVersions: map[string]int{ + // glob should not affect files with matching path in the other bundle + "*/bar/*": 0, + }, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "b/foo/test.rego", + URL: "b/foo/test.rego", + Raw: []byte("package b"), + }, + { + Path: "b/bar/test.rego", + URL: "b/bar/test.rego", + Raw: []byte("package b"), + }, + { + Path: "b/baz/test.rego", + URL: "b/baz/test.rego", + Raw: []byte("package b"), + }, + }, + }, + }, + regoVersion: ast.RegoV0, + expGlobalRegoVersion: ®oV0, + expFileRegoVersions: map[string]int{ + "/a/foo/test.rego": 1, + "/a/bar/test.rego": 1, + "/a/baz/test.rego": 1, + "/b/foo/test.rego": 1, + "/b/baz/test.rego": 1, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + for _, b := range tc.bundles { + b.Manifest.Init() + for i, m := range b.Modules { + b.Modules[i].Parsed = ast.MustParseModule(string(m.Raw)) + } + } + + result, err := bundle.MergeWithRegoVersion(tc.bundles, tc.regoVersion, false) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + compareRegoVersions(t, tc.expGlobalRegoVersion, result.Manifest.RegoVersion) + + if !maps.Equal(tc.expFileRegoVersions, result.Manifest.FileRegoVersions) { + t.Fatalf("expected file rego versions to be:\n\n%v\n\nbut got:\n\n%v", tc.expFileRegoVersions, result.Manifest.FileRegoVersions) + } + }) + } +} + +func compareRegoVersions(t *testing.T, exp, act *int) { + t.Helper() + if exp == nil { + if act != nil { + t.Errorf("expected no rego version, but got %v", *act) + } + } else { + if act == nil { + t.Errorf("expected rego version to be %v, but got none", *exp) + } else if *act != *exp { + t.Errorf("expected rego version to be %v, but got %v", *exp, *act) + } + } +} diff --git a/third_party/opa/compile/doc.go b/third_party/opa/compile/doc.go new file mode 100644 index 000000000000..bfe5c97dc2cf --- /dev/null +++ b/third_party/opa/compile/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package compile diff --git a/third_party/opa/config/config.go b/third_party/opa/config/config.go new file mode 100644 index 000000000000..e612df0a002b --- /dev/null +++ b/third_party/opa/config/config.go @@ -0,0 +1,19 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package config implements OPA configuration file parsing and validation. +package config + +import ( + v1 "github.com/open-policy-agent/opa/v1/config" +) + +// Config represents the configuration file that OPA can be started with. +type Config = v1.Config + +// ParseConfig returns a valid Config object with defaults injected. The id +// and version parameters will be set in the labels map. +func ParseConfig(raw []byte, id string) (*Config, error) { + return v1.ParseConfig(raw, id) +} diff --git a/third_party/opa/config/doc.go b/third_party/opa/config/doc.go new file mode 100644 index 000000000000..c6dd968dc62b --- /dev/null +++ b/third_party/opa/config/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package config diff --git a/third_party/opa/cover/cover.go b/third_party/opa/cover/cover.go new file mode 100644 index 000000000000..892a3a5968ca --- /dev/null +++ b/third_party/opa/cover/cover.go @@ -0,0 +1,37 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package cover reports coverage on modules. +package cover + +import ( + v1 "github.com/open-policy-agent/opa/v1/cover" +) + +// Cover computes and reports on coverage. +type Cover = v1.Cover + +// New returns a new Cover object. +func New() *Cover { + return v1.New() +} + +// Position represents a file location. +type Position = v1.Position + +// PositionSlice is a collection of position that can be sorted. +type PositionSlice = v1.PositionSlice + +// Range represents a range of positions in a file. +type Range = v1.Range + +// FileReport represents a coverage report for a single file. +type FileReport = v1.FileReport + +// Report represents a coverage report for a set of files. +type Report = v1.Report + +// CoverageThresholdError represents an error raised when the global +// code coverage percentage is lower than the specified threshold. +type CoverageThresholdError = v1.CoverageThresholdError diff --git a/third_party/opa/cover/doc.go b/third_party/opa/cover/doc.go new file mode 100644 index 000000000000..4dce6e538a53 --- /dev/null +++ b/third_party/opa/cover/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package cover diff --git a/third_party/opa/debug/breakpoint.go b/third_party/opa/debug/breakpoint.go new file mode 100644 index 000000000000..66f3144c32be --- /dev/null +++ b/third_party/opa/debug/breakpoint.go @@ -0,0 +1,13 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import ( + v1 "github.com/open-policy-agent/opa/v1/debug" +) + +type BreakpointID = v1.BreakpointID + +type Breakpoint = v1.Breakpoint diff --git a/third_party/opa/debug/debugger.go b/third_party/opa/debug/debugger.go new file mode 100644 index 000000000000..33f46fb67742 --- /dev/null +++ b/third_party/opa/debug/debugger.go @@ -0,0 +1,52 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package debug +// EXPERIMENTAL: This package is under active development and is subject to change. +package debug + +import ( + "github.com/open-policy-agent/opa/logging" + "github.com/open-policy-agent/opa/rego" + v1 "github.com/open-policy-agent/opa/v1/debug" +) + +// Debugger is the interface for launching OPA debugger Session(s). +// This implementation is similar in structure to the Debug Adapter Protocol (DAP) +// to make such integrations easier, but is not intended to be a direct implementation. +// See: https://microsoft.github.io/debug-adapter-protocol/specification +// +// EXPERIMENTAL: These interfaces are under active development and is subject to change. +type Debugger = v1.Debugger + +type Session = v1.Session + +type DebuggerOption = v1.DebuggerOption + +func NewDebugger(options ...DebuggerOption) Debugger { + return v1.NewDebugger(options...) +} + +func SetLogger(logger logging.Logger) DebuggerOption { + return v1.SetLogger(logger) +} + +func SetEventHandler(handler EventHandler) DebuggerOption { + return v1.SetEventHandler(handler) +} + +type LaunchEvalProperties = v1.LaunchEvalProperties + +type LaunchTestProperties = v1.LaunchTestProperties + +type LaunchProperties = v1.LaunchProperties + +type LaunchOption = v1.LaunchOption + +// RegoOption adds a rego option to the internal Rego instance. +// Options may be overridden by the debugger, and it is recommended to +// use LaunchEvalProperties for commonly used options. +func RegoOption(opt func(*rego.Rego)) LaunchOption { + return v1.RegoOption(opt) +} diff --git a/third_party/opa/debug/doc.go b/third_party/opa/debug/doc.go new file mode 100644 index 000000000000..08931e60279b --- /dev/null +++ b/third_party/opa/debug/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package debug diff --git a/third_party/opa/debug/event.go b/third_party/opa/debug/event.go new file mode 100644 index 000000000000..50d6bc2904e1 --- /dev/null +++ b/third_party/opa/debug/event.go @@ -0,0 +1,23 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import ( + v1 "github.com/open-policy-agent/opa/v1/debug" +) + +type EventType = v1.EventType + +const ( + ExceptionEventType = v1.ExceptionEventType + StdoutEventType = v1.StdoutEventType + StoppedEventType = v1.StoppedEventType + TerminatedEventType = v1.TerminatedEventType + ThreadEventType = v1.ThreadEventType +) + +type Event = v1.Event + +type EventHandler = v1.EventHandler diff --git a/third_party/opa/debug/frame.go b/third_party/opa/debug/frame.go new file mode 100644 index 000000000000..b260acad9233 --- /dev/null +++ b/third_party/opa/debug/frame.go @@ -0,0 +1,16 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import ( + v1 "github.com/open-policy-agent/opa/v1/debug" +) + +type FrameID = v1.FrameID + +type StackFrame = v1.StackFrame + +// StackTrace represents a StackFrame stack. +type StackTrace = v1.StackTrace diff --git a/third_party/opa/debug/thread.go b/third_party/opa/debug/thread.go new file mode 100644 index 000000000000..bf7a01b297ec --- /dev/null +++ b/third_party/opa/debug/thread.go @@ -0,0 +1,17 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import ( + v1 "github.com/open-policy-agent/opa/v1/debug" +) + +type ThreadID = v1.ThreadID + +// Thread represents a single thread of execution. +type Thread = v1.Thread + +// Scope represents the variable state of a StackFrame. +type Scope = v1.Scope diff --git a/third_party/opa/debug/variable.go b/third_party/opa/debug/variable.go new file mode 100644 index 000000000000..585d615625b5 --- /dev/null +++ b/third_party/opa/debug/variable.go @@ -0,0 +1,13 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import ( + v1 "github.com/open-policy-agent/opa/v1/debug" +) + +type Variable = v1.Variable + +type VarRef = v1.VarRef diff --git a/third_party/opa/dependencies/deps.go b/third_party/opa/dependencies/deps.go new file mode 100644 index 000000000000..804bd883d659 --- /dev/null +++ b/third_party/opa/dependencies/deps.go @@ -0,0 +1,42 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package dependencies + +import ( + "github.com/open-policy-agent/opa/ast" + v1 "github.com/open-policy-agent/opa/v1/dependencies" +) + +// All returns the list of data ast.Refs that the given AST element depends on. +func All(x any) (resolved []ast.Ref, err error) { + return v1.All(x) +} + +// Minimal returns the list of data ast.Refs that the given AST element depends on. +// If an AST element depends on a ast.Ref that is a prefix of another dependency, the +// ast.Ref that is the prefix of the other will be the only one in the returned list. +// +// As an example, if an element depends on data.x and data.x.y, only data.x will +// be in the returned list. +func Minimal(x any) (resolved []ast.Ref, err error) { + return v1.Minimal(x) +} + +// Base returns the list of base data documents that the given AST element depends on. +// +// The returned refs are always constant and are truncated at any point where they become +// dynamic. That is, a ref like data.a.b[x] will be truncated to data.a.b. +func Base(compiler *ast.Compiler, x any) ([]ast.Ref, error) { + return v1.Base(compiler, x) +} + +// Virtual returns the list of virtual data documents that the given AST element depends +// on. +// +// The returned refs are always constant and are truncated at any point where they become +// dynamic. That is, a ref like data.a.b[x] will be truncated to data.a.b. +func Virtual(compiler *ast.Compiler, x any) ([]ast.Ref, error) { + return v1.Virtual(compiler, x) +} diff --git a/third_party/opa/dependencies/doc.go b/third_party/opa/dependencies/doc.go new file mode 100644 index 000000000000..f2a4ea5cf0a7 --- /dev/null +++ b/third_party/opa/dependencies/doc.go @@ -0,0 +1,11 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package dependencies provides functions for determining the set of ast.Refs that AST +// elements depend on. +// +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package dependencies diff --git a/third_party/opa/docs/.gitignore b/third_party/opa/docs/.gitignore new file mode 100644 index 000000000000..e0e7820f25e6 --- /dev/null +++ b/third_party/opa/docs/.gitignore @@ -0,0 +1,7 @@ +.docusaurus +build +node_modules* +package-lock.json + +# this is generated by the build process +static/data/versions.json diff --git a/third_party/opa/docs/Makefile b/third_party/opa/docs/Makefile new file mode 100644 index 000000000000..99ae4e6f46eb --- /dev/null +++ b/third_party/opa/docs/Makefile @@ -0,0 +1,27 @@ +.PHONY: install +install: + npm install + +.PHONY: ci +ci: + npm ci + +.PHONY: dev +dev: + npx docusaurus start + +.PHONY: build +build: + npx docusaurus build + +.PHONY: clean +clean: + rm -rf build .docusaurus + +.PHONY: generate-cli-docs +generate-cli-docs: + $(CURDIR)/../build/gen-cli-docs.sh > $(CURDIR)/src/data/cli.json + +.PHONY: smoke-test +smoke-test: + ./bin/smoke-test.sh diff --git a/third_party/opa/docs/README.md b/third_party/opa/docs/README.md new file mode 100644 index 000000000000..3a47a39d6a8a --- /dev/null +++ b/third_party/opa/docs/README.md @@ -0,0 +1,6 @@ +# Documentation and Website Development + +Please see the +[contributing documentation](http://openpolicyagent.org/docs/contrib-docs) +for information about how to get started contributing to the OPA documentation +and website. diff --git a/third_party/opa/docs/devel/DEVELOPMENT.md b/third_party/opa/docs/devel/DEVELOPMENT.md new file mode 100644 index 000000000000..141956a32f76 --- /dev/null +++ b/third_party/opa/docs/devel/DEVELOPMENT.md @@ -0,0 +1,5 @@ +## Development + +The development guide has become part of the contributing documentation +and can be found [here](https://www.openpolicyagent.org/docs/contrib-development/). + diff --git a/third_party/opa/docs/devel/RELEASE.md b/third_party/opa/docs/devel/RELEASE.md new file mode 100644 index 000000000000..a6ea2b15690a --- /dev/null +++ b/third_party/opa/docs/devel/RELEASE.md @@ -0,0 +1,232 @@ +# Release Process + +## Overview + +The release process consists of two phases: versioning and publishing the release. + +Versioning involves maintaining the following files: + +- **CHANGELOG.md** - this file contains a list of all the important changes in each release. +- **Makefile** - the Makefile contains a VERSION variable that defines the version of the project. + +The steps below explain how to update these files. In addition, the repository +should be tagged with the semantic version identifying the release. + +Publishing involves creating a new *Release* on GitHub with the relevant +CHANGELOG.md snippet and uploading the binaries from the build phase. + +> Note: This release process is subject to change without notice. + +## Release Cadence + +There are two version tracks for the OPA project: + +1. Release Candidate (vX.Y.Z-rc.A) +2. Stable (vX.Y.Z) + +A new version of OPA is scheduled to release on the last Friday of every month. At the beginning of that week, +we will create a release candidate branch (`release-.-rc.0`) from the main branch and create a release +candidate tag (`v..0-rc.0`) based on the release candidate branch for pre-release. Once the pre-release +is published, users are encouraged to try out the features, bug fixes in the release candidate. If regressions or bugs +are detected, they need to get fixed before cutting the stable release. We do not recommend using OPA release +candidates in a production environment. The stable release that comes out after the release candidate may be identical +to the release candidate if no other features or bug fixes are introduced to the main branch in between. + +See the next section for details on cutting an individual release. + +## Versioning + +The steps below assume an OPA development environment has configured for the +standard GitHub fork workflow. See [OPA Dev Instructions](DEVELOPMENT.md) + +1. The following steps assume a remote named `upstream` exists that references the OPA source + repository. As needed, add an `upstream` remote for the repository: + + ``` + git remote add upstream git@github.com:open-policy-agent/opa.git + git fetch --tags upstream + ``` + + Note: This stage can fail if you have not registered an [SSH key](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account) + on your Github account. + +1. Create a release branch off of `main`, to ensure you don't mangle your + fork while creating the release: + + ``` + git checkout -b release-v origin/main + ``` + +1. Create a [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) + for GitHub with the 'read:org' scope. Export it to the `GITHUB_TOKEN` environment variable. + +1. Execute the release-patch target to generate boilerplate patch. Give the semantic version of the release: + + ``` + make release-patch VERSION=0.12.8 > ~/release.patch + ``` + +1. Apply the release patch to the working copy and preview the changes: + + ``` + patch -p1 < ~/release.patch + git diff + ``` + + > Amend the changes as necessary, e.g., many of the Fixes and Miscellaneous + > changes may not be user facing (so remove them). Also, if there have been + > any significant API changes, call them out in their own sections. + +1. Commit the changes and push to remote repository fork. + + ``` + git add . + git commit -s -m "Prepare v release" + git push origin release-v + ``` + +1. Create a Pull Request for the release preparation commit. + +1. Once the Pull Request has merged fetch the latest changes and tag the commit to prepare for publishing: + + ``` + git fetch upstream + git tag v upstream/main + ``` + + > Note: Ensure that tag is pointing to the correct commit ID! It must be the merged release preparation commit. + +1. Create a new branch for the dev-patch work: + + ``` + git checkout -b dev-v origin/main + ``` + +1. Execute the dev-patch target to generate boilerplate patch. Give the semantic version of the next release: + + ``` + make dev-patch VERSION=0.12.9 > ~/dev.patch + ``` + + > The semantic version of the next release typically increments the point version by one. + +1. Apply the patch to the working copy and preview the changes: + + ``` + patch -p1 < ~/dev.patch + git diff + ``` + +1. Commit the changes and push to remote repository fork. + + ``` + git commit -a -s -m "Prepare v development" + git push origin dev-v + ``` + +1. Create a Pull Request for the development preparation commit. + +## Publishing + +1. Push the release tag to remote source repository. + + ``` + git push upstream v + ``` + + > Note: Only OPA maintainers will have permissions to perform this step. + +1. Open browser and go to [https://github.com/open-policy-agent/opa/releases](https://github.com/open-policy-agent/opa/releases) + +1. Update the draft release (may take up to 20 min for the draft to become + available, track its process under + [https://github.com/open-policy-agent/opa/actions](https://github.com/open-policy-agent/opa/actions)). + Ensure everything looks OK and publish when ready. + +## Notes + +- The openpolicyagent/opa Docker image is automatically built and published to + Docker Hub as part of the Travis-CI pipeline. There are no manual steps + involved here. +- The docs and website should update and be published automatically. If they are not you can + trigger one by a couple of methods: + - Login to Netlify (requires permission for the project) and manually trigger a build. + - Post to the build webhook via: + ```bash + curl -X POST -d {} https://api.netlify.com/build_hooks/612e8941ffe30d2902bcce80 + ``` +- A versioned release is created automatically at + `vX-Y-Z--opa-docs.netlify.app`, automation is configured in + `github.com/open-policy-agent/opa-docs-machinery/actions`. + +# Bugfix Release Process + +The following steps assume a remote named `upstream` exists that references the OPA source +repository. As needed, add an `upstream` remote for the repository: + +``` +git remote add upstream git@github.com:open-policy-agent/opa.git +git fetch --tags upstream +``` + +If this is the first bugfix for the release, create the release branch from the +release tag and push to the source repository. + +```bash +git checkout -b release-0.14 v0.14.0 +git push upstream release-0.14 +``` + +Otherwise, checkout the release branch and sync with `upstream` (as needed): + +```bash +git fetch upstream +git checkout release-0.14 +git reset --hard upstream/release-0.14 +``` + +Cherry pick the changes from main or other branches onto the bugfix branch: + +```bash +git cherry-pick -x +``` + +> Using `-x` helps to keep track of where the commit came from originally + +Update the `VERSION` variable in the Makefile and CHANGELOG, same workflow as a normal release. + +```bash +make release-patch VERSION=0.14.1 > ~/release.patch +``` + +Apply the patch to the working copy and preview the changes: + +```bash +patch -p1 < ~/release.patch +git diff +``` + +> The generated CHANGELOG will likely need some manual adjustments for bugfix releases! + +Commit this change and push to fork: + +```bash +git commit -s -a -m 'Prepare v0.14.1 release' +git push origin release-0.14 +``` + +Open a Pull Request against the upstream release branch. Be careful to open the +Pull Request against the correct upstream release branch. **DO NOT** open/merge +the Pull Request into main or other release branches. + +When merging the Pull Request, make sure to do a rebase merge to retain all +cherry-picked commits (**do not squash**). + +Once the Pull Request has merged fetch the latest changes and tag the commit to +prepare for publishing. Use the same instructions as defined above in normal +release [publishing](#publishing) guide (being careful to tag the appropriate commit). + +Last step is to copy the CHANGELOG snippet and generated files +(builtin_metadata.json and capabilities.json) for the version to `main`. Create +a new PR with the version information added below the `Unreleased` section. +Remove any `Unreleased` notes if they were included in the bugfix release. diff --git a/third_party/opa/docs/docs/assets/OverviewDiagram.jsx b/third_party/opa/docs/docs/assets/OverviewDiagram.jsx new file mode 100644 index 000000000000..ab304cc4b582 --- /dev/null +++ b/third_party/opa/docs/docs/assets/OverviewDiagram.jsx @@ -0,0 +1,16 @@ +import Mermaid from "@theme/Mermaid"; + +const logoPath = require("./logo.png").default; + +const diagram = ` +graph TD; + Client -->|Request/Event| Service; + Service -->|"Query
(any JSON Value)"| OPA[""]; + OPA -->|"Decision
(any JSON Value)"| Service; + Policy["Policy (Rego)"] --> OPA; + Data["Data (JSON)"] --> OPA; +`; + +const OverviewDiagram = () => ; + +export default OverviewDiagram; diff --git a/third_party/opa/docs/docs/assets/logo.png b/third_party/opa/docs/docs/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..56427178c446bc2196ce0f83f76b89b1439a75c4 GIT binary patch literal 38210 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 literal 0 HcmV?d00001 diff --git a/third_party/opa/docs/docs/aws-cloudformation-hooks.md b/third_party/opa/docs/docs/aws-cloudformation-hooks.md new file mode 100644 index 000000000000..85e66a1fa132 --- /dev/null +++ b/third_party/opa/docs/docs/aws-cloudformation-hooks.md @@ -0,0 +1,504 @@ +--- +title: AWS CloudFormation Hooks +--- + +[AWS CloudFormation Hooks](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/hooks.html) allows users to +verify AWS infrastructure components defined in AWS CloudFormation +[templates](https://aws.amazon.com/cloudformation/resources/templates/), like S3 Buckets or EC2 instances, prior to +deployment. This is done via **hooks**. Hooks are composed of custom code running in an AWS Lambda function, which is +invoked before a resource is created, updated or deleted. + +AWS currently supports hooks written in either Java or Python, and provides a +[sample repository](https://github.com/aws-cloudformation/aws-cloudformation-samples), which includes example hooks +written in both languages. Since we'd rather use OPA for this purpose, we'd need some code to process the requests +handled by the hook and send them forward to OPA for policy decisions via its +[REST API](https://www.openpolicyagent.org/docs/latest/rest-api/) using +the [OPA AWS CloudFormation Hook](https://github.com/StyraInc/opa-aws-cloudformation-hook). + +## Goals + +This tutorial shows how to deploy an AWS CloudFormation Hook that forwards requests to OPA for policy decisions, +allowing us to use policy to determine whether a request to create, update or delete a resource should be +allowed or denied. We'll learn how to author policies that take the input structure of CloudFormation Templates into +account, and some special considerations to be aware of in this environment. + +In addition, this tutorial shows how we can leverage dynamic policy composition to group and structure our policies in a +way that follows the domain to which they apply. + +## Prerequisites + +In order to complete this tutorial, the following prerequisites needs to be met: + +- An AWS account, with permissions to deploy resources via AWS CloudFormation, and valid credentials available to the CLI commands +- The [AWS CLI](https://aws.amazon.com/cli/) (`aws`) tool +- The [CloudFormation CLI](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/what-is-cloudformation-cli.html) (`cfn`) tool +- Docker +- OPA server running at an endpoint reachable by the AWS Lambda function, either within the same AWS environment, or + elsewhere. While developing your CloudFormation policies, a good option is to run OPA locally, but exposed to the + public via a service like [tunnelmole](https://tunnelmole.com/docs), an open source tunneling tool or [ngrok](https://ngrok.com/), + a popular closed source tunneling tool. + +## Steps + +### 1. Install the CloudFormation Hook + +To start out, clone the OPA AWS CloudFormation Hook repository: + +```shell +git clone https://github.com/StyraInc/opa-aws-cloudformation-hook.git +cd opa-aws-cloudformation-hook +``` + +To install (but not activate) the hook provided in this repository into your AWS account, cd into the `hooks` directory +and run: + +```shell +cd hooks +cfn submit --set-default +``` + +When the command above is finished (this may take several minutes), you should see output similar to this: + +``` +Successfully submitted type. Waiting for registration with token '16697881-de36-45b8-8bc4-d9744431fa82' to complete. +Registration complete. +{ + 'ProgressStatus': 'COMPLETE', + 'Description': 'Deployment is currently in DEPLOY_STAGE of status COMPLETED', + 'TypeArn': 'arn:aws:cloudformation:eu-north-1:687803501377:type/hook/Styra-OPA-Hook', + ... +} +``` + +### 2. Configure the OPA AWS CloudFormation Hook + +The hook is now installed but needs to be configured for your environment. First, copy the value of the `TypeArn` +attribute from the JSON output of the above command, and store it in an environment variable: + +```shell +export HOOK_TYPE_ARN="arn:aws:cloudformation:eu-north-1:687803501377:type/hook/Styra-OPA-Hook" +``` + +Next, set the AWS region and the URL to use for calling OPA: + +```shell +export AWS_REGION="eu-north-1" +export OPA_URL="https://cfn-opa.example.com" +``` + +**(OPTIONAL):** If you want to use a bearer token to authenticate against OPA, provide an ARN pointing to the AWS Secret +containing the token: + +```shell +export OPA_AUTH_TOKEN_SECRET="arn:aws:secretsmanager:eu-north-1:687803501377:secret:opa-cfn-token-l26bHK" +``` + +With the configuration variables set, push the configuration to AWS (remove `opaAuthTokenSecret` if you don't intend to +use it): + +```shell +aws cloudformation --region "$AWS_REGION" set-type-configuration \ + --configuration "{\"CloudFormationConfiguration\":{\"HookConfiguration\":{\"TargetStacks\":\"ALL\",\"FailureMode\":\"FAIL\",\"Properties\":{\"opaUrl\": \"$OPA_URL\",\"opaAuthTokenSecret\":\"$OPA_AUTH_TOKEN_SECRET\"}}}}" \ + --type-arn $HOOK_TYPE_ARN +``` + +The hook is now installed, configured and activated! + +### 3. Learn the Domain + +Before we proceed to write our first policy, let's take a closer look at the data we'll be working with. + +#### AWS CloudFormation Templates + +A template file is commonly a YAML or JSON file, describing a set of AWS resources. While a template may describe +multiple resources, the hook will send each resource for validation separately. Important to note here is that the +resource presented to the hook will be shown **exactly** as provided in the template file. The hook does not perform any +type preprocessing, such as adding default values where missing, or providing auto-generated names. Policy authors must +hence take into account that even "obvious" attributes like name might not be present in the resource provided for +evaluation. As an example, a template to deploy an S3 Bucket with default attributes may be as minimal as this: + +```yaml +Resources: + ExampleS3Bucket: + Type: AWS::S3::Bucket +``` + +For more information on templates, see the +[AWS User Guide](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-guide.html) on that topic. + +#### Input and Response Format + +The OPA configured to receive requests from the CFN hook will have its input provided in this format: + +```json +{ + "action": "CREATE", + "hook": "Styra::OPA::Hook", + "resource": { + "id": "MyS3Bucket", + "name": "AWS::S3::Bucket", + "type": "AWS::S3::Bucket", + "properties": { + "Tags": [{ "Key": "Owner", "Value": "Platform Team" }], + "BucketName": "platform-bucket-1" + } + } +} +``` + +- The `action` is either `CREATE`, `UPDATE` or `DELETE` +- The `id` is the key of the resource, as provided in the template +- The `type` is divided by "resource domain" and the specific type, so e.g. the S3 domain may contain `Bucket`, + `BucketPolicy`, and so on. + +The hook expects the response to contain a boolean `allow` attribute, and a list of (potential) `violations`: + +```json +{ + "allow": false, + "violations": [ + "bucket must not be public", + "bucket name must follow naming standard" + ] +} +``` + +Any request denied will be logged in [AWS CloudWatch](https://aws.amazon.com/cloudwatch/) for the same account. + +### 4. Write a CloudFormation Hook Policy + +With knowledge of the domain and the data model, we're ready to write our first CloudFormation Hook policy. Since we'll +have a single OPA endpoint servicing requests for all types of resources, we'll use the +[default decision](./configuration/#miscellaneous) policy, which by default queries the `system.main` rule. Let's add a +simple policy to block an S3 Bucket unless it has an `AccessControl` attribute set to `Private`: + +```rego +package system + +main := { + "allow": count(deny) == 0, + "violations": deny, +} + +deny contains msg if { + bucket_create_or_update + not bucket_is_private + + msg := sprintf("S3 Bucket %s 'AccessControl' attribute value must be 'Private'", [input.resource.id]) +} + +bucket_create_or_update if { + input.resource.type == "AWS::S3::Bucket" + input.action in {"CREATE", "UPDATE"} +} + +bucket_is_private if { + input.resource.properties.AccessControl == "Private" +} +``` + +Since we know that CloudFormation Templates may contain only the bare minimum of information, we can't assume that there +will be an `AccessControl` attribute present in the input at all. Using negation of boolean rules inside of our `deny` +rules help alleviate the problem of values potentially being undefined. Compare to the following deny rule, which might +look correct at a first glance: + +```rego +deny contains msg if { + bucket_create_or_update + + input.resource.properties.AccessControl != "Private" + + msg := sprintf("S3 Bucket %s 'AccessControl' attribute value must be 'Private'", [input.resource.id]) +} +``` + +This rule would work fine as long as there _is_ an `AccessControl` attribute present in the input resource, but would +fail (i.e. not evaluate) as soon as the property was missing, leading to the resource being allowed! Using helper rules +and negation is a good way to work with data that might or might not be present, and results in more readable policies, +too. + +:::danger +Surprisingly, boolean values from CloudFormation Templates are provided to the hook in the form of **strings** (i.e. +"true" and "false"). Policy authors must take this into account, and explicitly check for the value of these +attributes. An example S3 bucket policy might for example want to check that public ACLs are blocked: + +```rego +# Wrong: will allow both "true" and "false" values as both are considered "truthy" +block_public_acls if { + input.resource.properties.PublicAccessBlockConfiguration.BlockPublicAcls +} +``` + +```rego +# Correct: will allow only when property set to "true" +block_public_acls if { + input.resource.properties.PublicAccessBlockConfiguration.BlockPublicAcls == "true" +} +``` + +::: + +### 5. Policy Enforcement Testing + +With the above policy loaded into OPA, we may proceed to try it out. Let's deploy the minimal S3 Bucket from the +previous template example. Save the below minimal template to a file called `s3bucket.yaml`: + +```yaml +Resources: + ExampleS3Bucket: + Type: AWS::S3::Bucket +``` + +Since our S3 bucket doesn't have an `AccessControl` attribute, it should be denied by the hook. We +deploy a template by creating a **stack**: + +```shell +aws cloudformation create-stack --stack-name cfn-s3 --template-body file://s3bucket.yaml +``` + +The output of the above command will simply be a confirmation that the stack was deployed. It won't tell us whether the +deployment was successful or not. In order to know that, we'll need to check the stack events: + +```shell +aws cloudformation describe-stack-events --stack-name cfn-s3 +``` + +The output of the above command will be a list of all events associated with the `cfn-s3` stack. Among the events, you +should now find an item describing that the hook denied the request, and its reason for doing so: + +```json +{ + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:eu-north-1:55523455647:stack/cfn-s3/4f605f70-b1ca-12ec-b4d8-0a63e869dfee", + "EventId": "ExampleS3Bucket-c243efd6-bfe7-3f10-8304-a0e40fe5f6f4", + "StackName": "cfn-s3", + "LogicalResourceId": "ExampleS3Bucket", + "PhysicalResourceId": "", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": "2022-03-31T08:41:15.946000+00:00", + "ResourceStatus": "CREATE_IN_PROGRESS", + "HookType": "Styra::OPA::Hook", + "HookStatus": "HOOK_COMPLETE_FAILED", + "HookStatusReason": "Hook failed with message: S3 Bucket ExampleS3Bucket 'AccessControl' attribute value must be 'Private'", + "HookInvocationPoint": "PRE_PROVISION", + "HookFailureMode": "FAIL" + } + ] +} +``` + +Congratulations! You've just successfully enforced your first CloudFormation Hook policy using OPA. Let's update the +template so that it passes our policy requirement: + +**s3bucket.yaml** + +```yaml +Resources: + ExampleS3Bucket: + Type: AWS::S3::Bucket + Properties: + AccessControl: Private +``` + +Even though our stack did not create an S3 bucket (as the change got rolled back), the **stack** still exists. +In order to try again, we'll first need to delete the existing stack: + +```shell +aws cloudformation delete-stack --stack-name cfn-s3 +``` + +Now, let's try again: + +```shell +aws cloudformation create-stack --stack-name cfn-s3 --template-body file://s3bucket.yaml +``` + +Checking the output of `aws cloudformation describe-stack-events --stack-name cfn-s3` once more will now show that the +resource was created. Do note that this could take up to a minute, so if you don't see it immediately, rerun the command +a bit later. + +```json +{ + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:eu-north-1:55523455647:stack/cfn-s3/4f605f70-b1ca-12ec-b4d8-0a63e869dfee", + "EventId": "e20fdfa0-b0d0-11ec-b669-0e70f1f560a6", + "StackName": "cfn-s3", + "LogicalResourceId": "cfn-s3", + "PhysicalResourceId": "arn:aws:cloudformation:eu-north-1:55523455647:stack/cfn-s3/cf418141-b0d9-11bc-b421-0a1244c68dd1", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": "2022-03-31T08:59:33.392000+00:00", + "ResourceStatus": "CREATE_COMPLETE" + } + ] +} +``` + +Note: once our stack is successfully deployed, we can use the `update-stack` command after we've made changes to our +templates: + +```shell +aws cloudformation update-stack --stack-name cfn-s3 --template-body file://s3bucket.yaml` +``` + +## Further Improvements + +### Dynamic Policy Composition + +Having a single policy file for all rules will quickly become unwieldy. Could we improve this somehow? One way of doing +that would be to use dynamic policy composition, where a single main policy acts as a "router", and forwards queries to +other packages based on attributes from the input. A natural attribute to use for CloudFormation templates might for +example be the resource type, allowing us to group our policies by the resource type they're meant to act on. Let's +take a look at what such a main policy might look like: + +```rego title="main.rego" +# METADATA +# description: | +# Dynamic routing to policy based in input.resource.type, +# aggregating the deny rules found in all policies with a +# matching package name +# +package system + +main := { + "allow": count(violations) == 0, + "violations": violations, +} + +# METADATA +# description: | +# Main routing logic, simply converting input.resource.type, e.g. +# AWS::S3::Bucket to data.aws.s3.bucket and returning that document. +# +# By default, only input.action == "CREATE" | "UPDATE" will be routed +# to the data.aws.s3.bucket document. If handling "DELETE" actions is +# desirable, one may create a special policy for that by simply appending +# "delete" to the package name, e.g. data.aws.s3.bucket.delete +# +route := document(lower(component), lower(type)) if { + ["AWS", component, type] = split(input.resource.type, "::") +} + +violations contains msg if { + # Aggregate all deny rules found in routed document + some msg in route.deny +} + +# +# Basic input validation to avoid having to do this in each resource policy +# + +violations contains "Missing input.resource" if { + not input.resource +} + +violations contains "Missing input.resource.type" if { + not input.resource.type +} + +violations contains "Missing input.resource.id" if { + not input.resource.id +} + +violations contains "Missing input.action" if { + not input.action +} + +# +# Helpers +# + +document(component, type) := data.aws[component][type] if { + input.action != "DELETE" +} + +document(component, type) := data.aws[component][type].delete if { + input.action == "DELETE" +} +``` + +The above policy will invoke the `route` rule to determine which package should be evaluated based on the +`input.resource.type`, transforming a value such as `AWS::S3::Bucket` into a call to the `data.aws.s3.bucket` package, +where each rule named `deny` will be evaluated, and the result aggregated into the final decision. + +Since most of our policies will only deal with `CREATE` or `UPDATE` actions, we'd rather want to avoid having to check +for this in all of our rules. Instead, we'll have the router append `.delete` to the package name for `DELETE` +operations, so that a request to delete e.g. an S3 bucket would invoke the `data.aws.s3.bucket.delete` package (if it +exists). + +Additionally, we'll also do some simple input validation at this stage, so that we may avoid doing so in our resource +specific policies. + +We can now modify our original policy to verify S3 bucket resources only: + +```rego +package aws.s3.bucket + +deny contains sprintf("S3 Bucket %s 'AccessControl' attribute value must be 'Private'", [input.resource.id]) if { + not bucket_is_private +} + +bucket_is_private if { + input.resource.properties.AccessControl == "Private" +} +``` + +Note how we no longer need the `bucket_create_or_update` rule, as that is already asserted by the main policy. +Quite an improvement in terms of readability, and a good foundation for further policy authoring. If you'd like to see +more examples of policy utilizing this pattern, check out the +[policy directory](https://github.com/StyraInc/opa-aws-cloudformation-hook/tree/main/examples/policy) in the OPA AWS +CloudFormation Hook repo. + +### OPA Authentication via AWS Secrets + +#### OPA Configuration + +Since the OPA server does not run inside the AWS Lambda, it is a good idea to require authentication to access its REST +API, as described in the OPA [documentation](https://www.openpolicyagent.org/docs/latest/security/#authentication-and-authorization). + +A simple authz policy for checking the bearer token might look something like this: + +```rego title="authz.rego" +package system.authz + +default allow := false + +allow if { + input.identity == "my_secret_token" +} +``` + +Once created, remember to pass the appropriate flags to `opa run` to enable authentication / authorization: + +```shell +opa run --server --authentication=token --authorization=basic . +``` + +#### OPA AWS CloudFormation Hook Configuration + +If configured to use a bearer token for authenticating against OPA (by setting the `OPA_AUTH_TOKEN_SECRET` environment +variable as described in the section on configuring the hook), the hook will try to fetch the token from the +secret provided in the `opaAuthTokenSecret` (ARN) configuration attribute. Note that the token should be provided +as a plain string in the secret (i.e. the `SecretString`) and not wrapped in a JSON object. + +In order to fetch the token, the hook will need to be permitted to perform the `secretsmanager:GetSecretValue` +operation. Note that the hook will **only** read the secret provided by `opaAuthTokenSecret`, but it's recommended +to limit the `HookTypePolicy` on the IAM role to the specific secret accessed, i.e. the same ARN provided in +`opaAuthTokenSecret`. Example `HookTypePolicy` to allow the hook access to a specific secret: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "VisualEditor0", + "Effect": "Allow", + "Action": "secretsmanager:GetSecretValue", + "Resource": "arn:aws:secretsmanager:eu-north-1:673240551671:secret:opa-cfn-token-l26bHK" + } + ] +} +``` + +If you aren't planning to use bearer tokens for authentication, you may remove the permission entirely. diff --git a/third_party/opa/docs/docs/cicd.md b/third_party/opa/docs/docs/cicd.md new file mode 100644 index 000000000000..3b3eafc30c46 --- /dev/null +++ b/third_party/opa/docs/docs/cicd.md @@ -0,0 +1,94 @@ +--- +sidebar_label: CI/CD +--- + +# Using OPA in CI/CD Pipelines + +OPA is a great tool for implementing policy-as-code guardrails in +CI/CD +pipelines. With OPA, you can automatically verify configurations, validate +outputs, and enforce organizational policies before code reaches production. OPA +serves as a powerful 'swiss army knife' for implementing custom checks required +by your organization that might be difficult to implement in a script or in +another tool. + +For users looking to parse and validate configuration files or Infrastructure as +Code (IaC) committed to git, [Conftest](https://www.conftest.dev) is typically +the better choice as it supports many file formats (HCL, Jsonnet etc.). +However, OPA's `eval` command excels at connecting other tools and making checks +against runtime data, as it can only parse JSON and YAML formats. + +OPA as a CLI tool provides powerful capabilities for testing and validating +various types of data in your continuous integration workflows: + +- **Repository governance** - Use OPA to call GitHub APIs to validate commit + message formats and pull request metadata compliance. +- **Software supply chain validation** - Check package manager configurations + (package.json, requirements.txt, go.mod) to ensure dependencies meet security + and licensing requirements. +- **Test selection** - Determine which tests to run based on the + files that changed, optimizing CI runtime by only executing relevant test + suites based on changed files. +- **Checking JSON test coverage reports** - Ensure test coverage meets minimum + thresholds or that benchmarks results are within acceptable limits. +- **Defining dependencies between jobs** - Validate that deployment pipelines + follow proper sequencing and dependency requirements. An example of this can + be found in the OPA Repo's [own PR checks](https://github.com/open-policy-agent/opa/blob/aee10e4a8deef80f3110237426a64fa5d4e229de/.github/workflows/pull-request.yaml#L476-L521). +- **Test coverage enforcement** - Check that test files are added when code + files are created (e.g., ensuring each `foo.js` has a corresponding + `foo_test.js` in the appropriate directory). + +The [`opa eval`](./cli#eval) command provides +several flags that are particularly useful for CI/CD scenarios: + +- `--fail` and `--fail-defined` - Set the exit code to 1 based on query results + (`--fail` when undefined or false, `--fail-defined` when defined), making it + easy to fail CI jobs when policies are violated +- `--stdin-input` - Reads input data from stdin, allowing you to pipe output + from other commands directly into OPA for evaluation +- `-d` - load in JSON or YAML data files for evaluation. + +These flags help ensure your CI/CD pipelines respond appropriately to policy evaluation results and integrate smoothly with other tools in your pipeline. + +## GitHub Actions Integration + +For GitHub users, the easiest way to get started is using the official OPA setup +action. This will make the `opa` command available in your workflow, allowing +you to run OPA policies against your codebase. + +```yaml title="OPA installation step" +- name: Download OPA + uses: open-policy-agent/setup-opa + with: + version: latest # install the latest version +``` + +```yaml title="Example workflow checking test coverage" +name: OPA Checks +on: [pull_request] + +jobs: + validate-configs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Download OPA + uses: open-policy-agent/setup-opa + with: + version: edge + + - name: Check tests results coverage remains above 70% + run: | + my test command | + opa eval --fail-defined \ + --stdin-input \ + 'input.results[_].coverage < 0.7' +``` + +## Other CI/CD Platforms + +For users of other CI/CD platforms (GitLab CI, Jenkins, Azure DevOps, etc.), you +can download OPA directly from the [official installation page](../docs?current-os=linux#1-download-opa). +This provides installation instructions for various operating systems and +package managers. diff --git a/third_party/opa/docs/docs/cli.md b/third_party/opa/docs/docs/cli.md new file mode 100644 index 000000000000..cced609c9b4c --- /dev/null +++ b/third_party/opa/docs/docs/cli.md @@ -0,0 +1,22 @@ +--- +title: CLI Reference +--- + +The commands exposed in the `opa` executable are listed here in alphabetical +order. + +:::tip +Note that command line arguments may either be provided as traditional flags, or +as environment variables. The expected format of environment variables used for +this purpose follows the pattern `OPA__` where COMMAND is the +command name in uppercase (like EVAL) and FLAG is the flag name in uppercase +(like STRICT), i.e. `OPA_EVAL_STRICT` would be equivalent to passing the +--strict flag to the eval command. +::: + +import commands from "@generated/cli-data/default/cli.json"; +import CommandList from "@site/src/components/CommandList"; + + + +export const toc = commands.map(command => ({ value: command.id, id: command.id, level: 2 })); diff --git a/third_party/opa/docs/docs/comparison-to-other-systems.md b/third_party/opa/docs/docs/comparison-to-other-systems.md new file mode 100644 index 000000000000..034326d4c050 --- /dev/null +++ b/third_party/opa/docs/docs/comparison-to-other-systems.md @@ -0,0 +1,478 @@ +--- +title: Comparison to Other Systems +--- + +Often the easiest way to understand a new language is by comparing +it to languages you already know. Here we show how policies from +several existing policy systems can be implemented with the Open +Policy Agent. + +## Role-based access control (RBAC) + +Role-based access control (RBAC) is pervasive today for authorization. +To use RBAC for authorization, you write down two different kinds of +information. + +- Which users have which roles +- Which roles have which permissions + +Once you provide RBAC with both those assignments, RBAC tells you +how to make an authorization decision. A user is authorized for +all those permissions assigned to any of the roles she is assigned to. + +For example, we might have the following user/role assignments: + +| User | Role | +| ------- | ------------- | +| `alice` | `engineering` | +| `alice` | `webdev` | +| `bob` | `hr` | + +And the following role/permission assignments: + +| Role | Permission | Resource | +| ------------- | ---------- | ------------- | +| `engineering` | `read` | `server123` | +| `webdev` | `write` | `server123` | +| `webdev` | `read` | `server123` | +| `hr` | `read` | `database456` | + +In this example, RBAC makes the following authorization decisions: + +| User | Operation | Resource | Decision | +| ------- | --------- | ------------- | -------------------------------------------------------- | +| `alice` | `read` | `server123` | `allow` because `alice` is in `engineering` | +| `alice` | `write` | `server123` | `allow` because `alice` is in `webdev` | +| `bob` | `read` | `database456` | `allow` because `bob` is in `hr` | +| `bob` | `read` | `server123` | `deny` because `bob` is not in `engineering` or `webdev` | + +With OPA, you can write the following snippets to implement the +example RBAC policy shown above. + +```rego +package rbac.authz + +# user-role assignments +user_roles := { + "alice": ["engineering", "webdev"], + "bob": ["hr"], +} + +# role-permissions assignments +role_permissions := { + "engineering": [{"action": "read", "object": "server123"}], + "webdev": [{"action": "read", "object": "server123"}, + {"action": "write", "object": "server123"}], + "hr": [{"action": "read", "object": "database456"}], +} + +# logic that implements RBAC. +default allow := false + +allow if { + # lookup the list of roles for the user + roles := user_roles[input.user] + # for each role in that list + r := roles[_] + # lookup the permissions list for role r + permissions := role_permissions[r] + # for each permission + p := permissions[_] + # check if the permission granted to r matches the user's request + p == {"action": input.action, "object": input.object} +} +``` + + + +```json +{ + "user": "bob", + "action": "read", + "object": "server123" +} +``` + + + +### RBAC Separation of duty (SOD) + +Separation of duty (SOD) refers to the idea that there are certain +combinations of permissions that no one should have at the same time. +For example, no one should be able to both create payments and approve payments. + +In RBAC, that means there are some pairs of roles that no one should be +assigned simultaneously. For example, any user assigned both of the roles +in each pair below would violate SOD. + +- create-payment and approve-payment +- create-vendor and pay-vendor + +OPA's API does not yet let you enforce SOD by rejecting improper role-assignments, +but it does let you express SOD constraints and ask for all SOD violations, +as shown below. (Here we assume the statements below are added to the RBAC +statements above.) + +```rego +# Pairs of roles that no user can be assigned to simultaneously +sod_roles := [ + ["create-payment", "approve-payment"], + ["create-vendor", "pay-vendor"], +] + +# Find all users violating SOD +sod_violation contains user if { + some user + # grab one role for a user + role1 := user_roles[user][_] + # grab another role for that same user + role2 := user_roles[user][_] + # check if those roles are forbidden by SOD + sod_roles[_] == [role1, role2] +} +``` + +(For those familiar with SOD, this is the static version since SOD violations +happen whenever a user is assigned two conflicting roles. The dynamic version of SOD allows +a single user to be assigned two conflicting roles but requires that the same user not +utilize those roles on the same transaction, which is out of scope for this document.) + +## Attribute-based access control (ABAC) + +With attribute-based access control, you make policy decisions using the +attributes of the users, objects, and actions involved in the request. +It has three main components: + +- Attributes for users +- Attributes for objects +- Logic dictating which attribute combinations are authorized + +For example, we might know the following attributes for our users + +- alice + - joined the company 15 years ago + - is a trader +- bob + - joined the company 5 years ago + - is an analyst + +We would also have attributes for the objects, in this case stock ticker symbols. + +- MSFT + - is sold on NASDAQ + - sells at $59.20 per share +- AMZN + - is sold on NASDAQ + - sells at $813.64 per share + +An example ABAC policy in natural language might be: + +- Traders may purchase NASDAQ stocks for under $2M +- Traders with 10+ years experience may purchase NASDAQ stocks for under $5M + +OPA supports ABAC policies as shown below. + +```rego +package abac + +# User attributes +user_attributes := { + "alice": {"tenure": 15, "title": "trader"}, + "bob": {"tenure": 5, "title": "analyst"}, +} + +# Stock attributes +ticker_attributes := { + "MSFT": {"exchange": "NASDAQ", "price": 59.20}, + "AMZN": {"exchange": "NASDAQ", "price": 813.64}, +} + +default allow := false + +# all traders may buy NASDAQ under $2M +allow if { + # lookup the user's attributes + user := user_attributes[input.user] + # check that the user is a trader + user.title == "trader" + # check that the stock being purchased is sold on the NASDAQ + ticker_attributes[input.ticker].exchange == "NASDAQ" + # check that the purchase amount is under $2M + input.amount <= 2000000 +} + +# traders with 10+ years experience may buy NASDAQ under $5M +allow if { + # lookup the user's attributes + user := user_attributes[input.user] + # check that the user is a trader + user.title == "trader" + # check that the stock being purchased is sold on the NASDAQ + ticker_attributes[input.ticker].exchange == "NASDAQ" + # check that the user has at least 10 years of experience + user.tenure > 10 + # check that the purchase amount is under $5M + input.amount <= 5000000 +} +``` + + + +```json +{ + "user": "alice", + "ticker": "MSFT", + "action": "buy", + "amount": 1000000 +} +``` + + + +In OPA, there's nothing special about users and objects. You can attach +attributes to anything. And the attributes can themselves be structured JSON objects +and have attributes on attributes on attributes, etc. Because OPA was designed to work +with arbitrarily nested JSON data, it supports incredibly rich ABAC policies. + +## Amazon Web Services IAM + +Amazon Web Services (AWS) lets you create policies that can be attached to users, roles, groups, +and selected resources. You write `allow` and `deny` statements to enforce which users/roles can/can't +execute which API calls on which resources under certain conditions. +By default all API access requests are implicitly denied (i.e., not allowed). Policy statements +can explicitly allow or deny API requests. If a request is both allowed and denied, it is always denied. +Let's assume that the following [customer managed policy](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#customer-managed-policies) is defined in AWS: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "FirstStatement", + "Effect": "Allow", + "Action": ["iam:ChangePassword"], + "Resource": "*" + }, + { + "Sid": "SecondStatement", + "Effect": "Allow", + "Action": "s3:ListAllMyBuckets", + "Resource": "*" + }, + { + "Sid": "ThirdStatement", + "Effect": "Allow", + "Action": [ + "s3:List*", + "s3:Get*" + ], + "Resource": [ + "arn:aws:s3:::confidential-data", + "arn:aws:s3:::confidential-data/*" + ] + } + ] +} +``` + +And the above policy is attached to principal alice in AWS using +[attach-user-policy](https://docs.aws.amazon.com/cli/latest/reference/iam/attach-user-policy.html) API. +In OPA, you write each of the AWS `allow` statements as a separate statement, and you +expect the input to have `principal`, `action`, and `resource` fields. + +```rego +package aws + +default allow := false + +# FirstStatement +allow if { + principals_match + input.action == "iam:ChangePassword" +} + +# SecondStatement +allow if { + principals_match + input.action == "s3:ListAllMyBuckets" +} + +# ThirdStatement +# Use helpers to handle implicit OR in the AWS policy. +# Below all of the 'principals_match', 'actions_match' and 'resources_match' must be true. +allow if { + principals_match + actions_match + resources_match +} + +# principals_match is true if input.principal matches +principals_match if { + input.principal == "alice" +} + +# actions_match is true if input.action matches one in the list +actions_match if { + # iterate over the actions in the list + actions := ["s3:List.*", "s3:Get.*"] + action := actions[_] + # check if input.action matches an action + regex.globs_match(input.action, action) +} + +# resources_match is true if input.resource matches one in the list +resources_match if { + # iterate over the resources in the list + resources := ["arn:aws:s3:::confidential-data", "arn:aws:s3:::confidential-data/.*"] + resource := resources[_] + # check if input.resource matches a resource + regex.globs_match(input.resource, resource) +} +``` + + + +```json +{ + "principal": "alice", + "action": "ec2:StartInstance", + "resource": "arn:aws:ec2:::instance/i78999879" +} +``` + + + +## XACML + +eXtensible Access Control Markup Language (XACML) was designed to express security policies: allow/deny decisions using attributes of users, resources, actions, and the environment. +The following policy says that users from the organization Curtiss or Packard who are US or Great Britain nationals and who work on DetailedDesign or Simulation are permitted access to documents about NavigationSystems. + +```xml + + Policy for Business Authorization category TAA-1.1 + + + + + + + NavigationSystem + + + + + + + + Packard + + + + + + Curtiss + + + + + + + + GB + + + + + + US + + + + + + + + DetailedDesign + + + + + + Simulation + + + + + + + +``` + +The same statement is shown below in OPA. Here the inputs are assumed to be +roughly the same as for XACML: attributes of users, actions, and resources. + +```rego +package xacml + +# METADATA +# title: urn:curtiss:ba:taa:taa-1.1 +# description: Policy for Business Authorization category TAA-1.1 +default permit := false +permit if { + # Check that resource has a "NavigationSystem" entry + input.resource["NavigationSystem"] + + # Check that organization is one of the options + input.user.organization in ["Packard", "Curtiss"] + + # Check that nationality is one of the options + input.user.nationality in ["GB", "US"] + + # Check that work_effort is one of the options + input.user.work_effort in ["DetailedDesign", "Simulation"] +} +``` + + + +```json +{ + "user": { + "name": "alice", + "organization": "Packard", + "nationality": "GB", + "work_effort": "DetailedDesign" + }, + "resource": { + "NavigationSystem": true + }, + "action": { + "name": "read" + } +} +``` + + diff --git a/third_party/opa/docs/docs/configuration.md b/third_party/opa/docs/docs/configuration.md new file mode 100644 index 000000000000..be8cca0e67e2 --- /dev/null +++ b/third_party/opa/docs/docs/configuration.md @@ -0,0 +1,1197 @@ +--- +title: Configuration +--- + +This page defines the format of OPA configuration files. Fields marked as +required must be specified if the parent is defined. For example, when the +configuration contains a `status` key, the `status.service` field must be +defined. + +:::info +OPA accepts any name for the configuration file. Some tooling may however benefit from knowing what name to associate +with OPA's configuration file (for auto-completion of attributes, linting, etc.). The following names could be +considered idiomatic for that purpose: + +- `opa-config.yaml` (or `.json`) +- `opa-conf.yaml` (or `.json`) + +::: + +The configuration file path is specified with the `-c` or `--config-file` +command line argument: + +```bash +opa run -s -c opa-config.yaml +``` + +The file can be either JSON or YAML format. The following is an example +configuration file sets fields in many of the subcomponents inside of OPA. + +```yaml +services: + acmecorp: + url: https://example.com/control-plane-api/v1 + response_header_timeout_seconds: 5 + credentials: + bearer: + token: "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm" + +labels: + app: myapp + region: west + environment: production + +bundles: + authz: + service: acmecorp + resource: bundles/http/example/authz.tar.gz + persist: true + polling: + min_delay_seconds: 60 + max_delay_seconds: 120 + signing: + keyid: global_key + scope: write + +decision_logs: + service: acmecorp + reporting: + min_delay_seconds: 300 + max_delay_seconds: 600 + +status: + service: acmecorp + +default_decision: /http/example/authz/allow + +persistence_directory: /var/opa + +keys: + global_key: + algorithm: RS256 + key: + scope: read + +caching: + inter_query_builtin_cache: + max_size_bytes: 10000000 + forced_eviction_threshold_percentage: 70 + stale_entry_eviction_period_seconds: 3600 + +distributed_tracing: + type: grpc + address: localhost:4317 + service_name: opa + sample_percentage: 50 + encryption: "off" + resource: + service_namespace: "my-namespace" + service_version: "1.1" + service_instance_id: "1" + deployment_environment: "prod" + +server: + decoding: + max_length: 134217728 + gzip: + max_length: 268435456 + encoding: + gzip: + min_length: 1024 + compression_level: 9 +``` + +## Services + +Services represent endpoints that implement one or more control plane APIs +such as the Bundle or Status APIs. OPA configuration files may contain +multiple services. + +| Field | Type | Required | Description | +| --------------------------------------------- | -------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `services[_].name` | `string` | Yes | Unique name for the service. Referred to by plugins. | +| `services[_].url` | `string` | Yes | Base URL to contact the service with. | +| `services[_].response_header_timeout_seconds` | `int64` | No (default: 10) | Amount of time to wait for a server's response headers after fully writing the request. This time does not include the time to read the response body. | +| `services[_].headers` | `object` | No | HTTP headers to include in requests to the service. | +| `services[_].tls.ca_cert` | `string` | No | The path to the root CA certificate. If not provided, this defaults to TLS using the host's root CA set. | +| `services[_].tls.system_ca_required` | `bool` | No (default: `false`) | Require system certificate appended with root CA certificate. | +| `services[_].allow_insecure_tls` | `bool` | No | Allow insecure TLS. | +| `services[_].type` | `string` | No (default: empty) | Optional parameter that allows to use an "OCI" service type. This will allow bundle and discovery plugins to download bundles from an OCI registry. | + +Services can be defined as an array or object. When defined as an object, the +object keys override the `services[_].name` fields. For example: + +> ```yaml +> services: +> s1: +> url: https://s1/example/ +> s2: +> url: https://s2/ +> ``` +> +> Is equivalent to +> +> ```yaml +> services: +> - name: s1 +> url: https://s1/example/ +> - name: s2 +> url: https://s2/ +> ``` + +Each service may optionally specify a credential mechanism by which OPA will authenticate +itself to the service. + +### Bearer Token + +OPA will authenticate using the specified bearer token and schema; to enable bearer token +authentication, either the token or the path to the token must be specified. If the latter is provided, on each request OPA will re-read the token from the file and use that token for authentication. + +The `scheme` attribute is optional, and will default to `Bearer` if unspecified. + +| Field | Type | Required | Description | +| ------------------------------------------- | -------- | -------- | -------------------------------------------------------------------------------------------------- | +| `services[_].credentials.bearer.token` | `string` | Yes | Enables token-based authentication and supplies the bearer token to authenticate with. | +| `services[_].credentials.bearer.token_path` | `string` | Yes | Enables token-based authentication and supplies the path to the bearer token to authenticate with. | +| `services[_].credentials.bearer.scheme` | `string` | No | Bearer token scheme to specify. | + +### Client TLS Certificate + +OPA will present the specified TLS certificate to authenticate. The paths to the client certificate +and the private key are required; the passphrase for the private key is only required if the +private key is encrypted. + +| Field | Type | Required | Description | +| ----------------------------------------------------------- | -------- | -------- | -------------------------------------------------------- | +| `services[_].credentials.client_tls.cert` | `string` | Yes | The path to the client certificate to authenticate with. | +| `services[_].credentials.client_tls.private_key` | `string` | Yes | The path to the private key of the client certificate. | +| `services[_].credentials.client_tls.private_key_passphrase` | `string` | No | The passphrase to use for the private key. | + +### OAuth2 Client Credentials + +OPA will authenticate using a bearer token obtained through the OAuth2 [client credentials](https://tools.ietf.org/html/rfc6749#section-4.4) flow. +Following successful authentication at the token endpoint the returned token will be cached for subsequent requests for the duration of its lifetime. Note that as per the [OAuth2 standard](https://tools.ietf.org/html/rfc6749#section-2.3.1), only the HTTPS scheme is supported for the token endpoint URL. + +| Field | Type | Required | Description | +| ------------------------------------------------------ | ---------- | -------- | ------------------------------------------------------------------------------------------- | +| `services[_].credentials.oauth2.token_url` | `string` | Yes | URL pointing to the token endpoint at the OAuth2 authorization server. | +| `services[_].credentials.oauth2.client_id` | `string` | Yes | The client ID to use for authentication. | +| `services[_].credentials.oauth2.client_secret` | `string` | Yes | The client secret to use for authentication. | +| `services[_].credentials.oauth2.scopes` | `[]string` | No | Optional list of scopes to request for the token. | +| `services[_].credentials.oauth2.additional_headers` | `map` | No | Map of additional headers to send to token endpoint at the OAuth2 authorization server | +| `services[_].credentials.oauth2.additional_parameters` | `map` | No | Map of additional body parameters to send token endpoint at the OAuth2 authorization server | + +### OAuth2 Client Credentials JWT authentication + +OPA will authenticate using a bearer token obtained through the OAuth2 [client credentials](https://tools.ietf.org/html/rfc6749#section-4.4) flow. +Rather than providing a client secret along with the request for an access token, the client [asserts](https://tools.ietf.org/html/rfc7521#section-4.2) its identity in the form of a signed JWT. +Following successful authentication at the token endpoint the returned token will be cached for subsequent requests for the duration of its lifetime. Note that as per the [OAuth2 standard](https://tools.ietf.org/html/rfc6749#section-2.3.1), only the HTTPS scheme is supported for the token endpoint URL. + +| Field | Type | Required | Description | +| --------------------------------------------------------------------- | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `services[_].credentials.oauth2.token_url` | `string` | Yes | URL pointing to the token endpoint at the OAuth2 authorization server. | +| `services[_].credentials.oauth2.grant_type` | `string` | No | Defaults to `client_credentials`. | +| `services[_].credentials.oauth2.client_id` | `string` | No | The client ID to use for authentication. | +| `services[_].credentials.oauth2.signing_key` | `string` | No | Reference to private key used for signing the JWT. Required if `aws_kms` is not provided | +| `services[_].credentials.oauth2.thumbprint` | `string` | No | Certificate thumbprint to use for x5t header generation. | +| `services[_].credentials.oauth2.additional_claims` | `map` | No | Map of claims to include in the JWT (see notes below) | +| `services[_].credentials.oauth2.include_jti_claim` | `bool` | No | Include a uniquely generated `jti` claim in any issued JWT | +| `services[_].credentials.oauth2.scopes` | `[]string` | No | Optional list of scopes to request for the token. | +| `services[_].credentials.oauth2.aws_kms.name` | `string` | No | To specify a KMS key, use its key ID, key ARN, alias name, or alias ARN. Required only for signing with AWS KMS. | +| `services[_].credentials.oauth2.aws_kms.algorithm` | `string` | No | Specifies the signing algorithm used by the key `aws_kms.name` `(ECDSA_SHA_256, ECDSA_SHA_384 or ECDSA_SHA_512)`. Required only for signing with AWS KMS. | +| `services[_].credentials.oauth2.aws_signing` | `{}` | No | AWS credentials for signing requests. Required if `aws_kms` is provided. | +| `services[_].credentials.oauth2.azure_keyvault.key` | `string` | No | Specify what key name should be used for signing. | +| `services[_].credentials.oauth2.azure_keyvault.key_version` | `string` | No | Key version that should be used for signing. Will used latest if not specified | +| `services[_].credentials.oauth2.azure_keyvault.key_algorithm` | `string` | No | Specifies the signing algorithm used by the key `azure_keyvault.key`. `ES256, ES256K, PS256, RS256, ES384, PS384, RS384, ES512, PS512 or RS512)` | +| `services[_].credentials.oauth2.azure_keyvault.vault` | `string` | No | The name of the azure keyvault. used for interpolation of URL. | +| `services[_].credentials.oauth2.azure_keyvault.api_version` | `string` | No | The version of the [azure keyvault sign api](https://learn.microsoft.com/en-us/rest/api/keyvault/keys/sign/sign). Defaults to "7.4" | +| `services[_].credentials.oauth2.azure_signing.service` | `string` | No | What azure service to use for signing. only valid service currently is "keyvault". | +| `services[_].credentials.oauth2.azure_signing.azure_managed_identity` | `{}` | No | What managed identity OPA will try to use for auth in azure. Identity has to have signing rights to the key in `azure_keyvault.key`. see [managed-identity](#azure-managed-identities-token) for more info. | +| `services[_].credentials.oauth2.client_assertion_path` | `string` | No | To specify a path to find a client assertion file. Used for Azure Workload Identity. | +| `services[_].credentials.oauth2.client_assertion` | `string` | No | To specify a client assertion. Used for Azure Workload Identity. | + +Two claims will always be included in the issued JWT: `iat` and `exp`. Any other claims will be populated from the `additional_claims` map. + +:::info +For using `services[_].credentials.oauth2.aws_kms`, a method for setting the AWS credentials has to be specified in the `services[_].credentials.oauth2.aws_signing`. +The value of `services[_].credentials.oauth2.aws_signing.service` should be `kms`. Several methods of obtaining the necessary credentials are available; exactly one must be specified, +see description for `services[_].credentials.s3_signing`. +::: + +The following is an example of using the client credentials grant type with JWT +client authentication replacing client secret as the credential used at the +token endpoint. + +```yaml +services: + remote: + url: ${BUNDLE_SERVICE_URL} + credentials: + oauth2: + token_url: ${TOKEN_URL} + grant_type: client_credentials + client_id: opa-client + signing_key: jwt_signing_key # references the key in `keys` below + include_jti_claim: true + scopes: + - read + - write + additional_claims: + sub: opa-client + iss: opa-${POD_NAME} + +bundles: + authz: + service: remote + resource: bundles/http/example/authz.tar.gz + +keys: + jwt_signing_key: + algorithm: ES512 + private_key: ${BUNDLE_SERVICE_SIGNING_KEY} +``` + +The following is an example of using the client credentials grant type with JWT +client authentication & AWS KMS signing of client assertions. + +```yaml +services: + remote: + url: ${BUNDLE_SERVICE_URL} + credentials: + oauth2: + token_url: ${TOKEN_URL} + grant_type: client_credentials + client_id: opa-client + aws_kms: + name: ${AWS_KMS_KEYID} + algorithm: ECDSA_SHA_256 + aws_signing: # similar to s3_signing + service: kms + environment_credentials: + aws_default_region: eu-west-1 + include_jti_claim: true + scopes: + - read + - write + additional_claims: + sub: opa-client + iss: opa-${POD_NAME} + +bundles: + authz: + service: remote + resource: bundles/http/example/authz.tar.gz +``` + +The following is an example of using the client credentials grant type with JWT +client authentication & Azure Keyvault signing of client assertions. +```yaml +services: + remote: + url: ${BUNDLE_SERVICE_URL} + credentials: + oauth2: + token_url: ${TOKEN_URL} + grant_type: client_credentials + client_id: opa-client + azure_keyvault: + key: ${AZURE_KEY_NAME} + key_algorithm: ES256 + vault: ${AZURE_VAULT_NAME} + azure_signing: + service: keyvault + azure_managed_identity: {} + include_jti_claim: true + scopes: + - read + - write + additional_claims: + sub: opa-client + iss: opa-${POD_NAME} + +bundles: + authz: + service: remote + resource: bundles/http/example/authz.tar.gz +``` + +The following is an example of using the client credentials grant type with JWT client authentication via Azure Workload Identity access to the storage account hosting the policies. All referenced environment variables are automatically populated by Azure when deployed via AKS. Note the similarity to [managed identity](#azure-managed-identities-token). + +```yaml +services: + azure_storage_account: + url: https://YOUR_STORAGE_ACCOUNT.blob.core.windows.net/ + headers: + x-ms-version: 2017-11-09 + response_header_timeout_seconds: 5 + credentials: + oauth2: + grant_type: client_credentials + client_id: "${AZURE_CLIENT_ID}" + client_assertion_path: "${AZURE_FEDERATED_TOKEN_FILE}" + token_url: "${AZURE_AUTHORITY_HOST}/${AZURE_TENANT_ID}/oauth2/v2.0/token" + scopes: + - https://storage.azure.com/.default + +bundles: + authz: + service: azure_storage_account + resource: YOUR_CONTAINER/YOUR_POLICY_BUNDLE.tar.gz + persist: true + polling: + min_delay_seconds: 60 + max_delay_seconds: 120 +``` + +### OAuth2 JWT Bearer Grant Type + +OPA will authenticate using a bearer token obtained through the OAuth2 [JWT authorization grant](https://tools.ietf.org/html/rfc7523#section-2.1) flow. +Rather than providing a client secret along with the request for an access token, the client [asserts](https://tools.ietf.org/html/rfc7521#section-4.1) its identity in the form of a signed JWT. +Following successful authentication at the token endpoint the returned token will be cached for subsequent requests for the duration of its lifetime. Note that as per the [OAuth2 standard](https://tools.ietf.org/html/rfc6749#section-2.3.1), only the HTTPS scheme is supported for the token endpoint URL. + +| Field | Type | Required | Description | +| -------------------------------------------------- | ---------- | -------- | ---------------------------------------------------------------------------------------- | +| `services[_].credentials.oauth2.token_url` | `string` | Yes | URL pointing to the token endpoint at the OAuth2 authorization server. | +| `services[_].credentials.oauth2.grant_type` | `string` | No | Must be set to `jwt_bearer` for JWT bearer grant type. Defaults to `client_credentials`. | +| `services[_].credentials.oauth2.signing_key` | `string` | Yes | Reference to private key used for signing the JWT. | +| `services[_].credentials.oauth2.additional_claims` | `map` | No | Map of claims to include in the JWT (see notes below) | +| `services[_].credentials.oauth2.include_jti_claim` | `bool` | No | Include a uniquely generated `jti` claim in any issued JWT | +| `services[_].credentials.oauth2.scopes` | `[]string` | No | Optional list of scopes to request for the token. | + +Two claims will always be included in the issued JWT: `iat` and `exp`. Any other claims will be populated from the `additional_claims` map. + +The following is an example of using a [Google Cloud Storage](https://cloud.google.com/storage/) bucket as a bundle service backend +from outside the cloud account (for access from inside the account, see the [GCP Metadata Token](#gcp-metadata-token) section). + +```yaml +services: + gcp: + url: https://storage.googleapis.com/storage/v1/b/${BUCKET_NAME}/o + credentials: + oauth2: + token_url: https://oauth2.googleapis.com/token + grant_type: jwt_bearer + signing_key: jwt_signing_key # references the key in `keys` below + scopes: + - https://www.googleapis.com/auth/devstorage.read_only + additional_claims: + aud: https://oauth2.googleapis.com/token + iss: opa-client@my-account.iam.gserviceaccount.com + +bundles: + authz: + service: gcp + resource: "bundles%2fhttp%2fexample%2fauthz.tar.gz?alt=media" + +keys: + jwt_signing_key: + algorithm: RS256 + private_key: ${BUNDLE_SERVICE_SIGNING_KEY} +``` + +:::danger +OPA masks services authentication secrets which make use of the `credentials` field, in order to prevent the exposure of sensitive tokens. +It is important to note that the [/v1/config API](./rest-api/#config-api) allows clients to read the runtime configuration of OPA. As such, any credentials used by +custom configurations not utilizing the credentials field will be exposed to the caller. +Consider requiring authentication in order to prevent unauthorized read access to OPA's runtime configuration. +::: + +### AWS Signature + +OPA will authenticate with an [AWS Version 4](https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html) or version 4A signature. While version 4 is the default, version 4A must be used when making requests that might be handled by more than one region, such as an [S3 Multi-Region Access Point](https://docs.aws.amazon.com/AmazonS3/latest/userguide/MultiRegionAccessPoints.html). You must use version 4A for this or requests will fail when routed to a different region than the one indicated in a version 4 signature. Furthermore, using version 4a also requires that temporary credentials are retrieved from a [regional AWS STS endpoint](https://docs.aws.amazon.com/sdkref/latest/guide/feature-sts-regionalized-endpoints.html), rather than the global STS endpoint. + +Several methods of obtaining the necessary credentials are available; exactly one must be specified to use the AWS signature authentication method. + +The AWS service for which to sign the request can be specified in the `service` field. If omitted, the default is `s3`. + +The AWS signature version to sign the request with can be specified in the `signature_version` field. If omitted, the default is `4`. The only other valid value is `4a`. + +| Field | Type | Required | Description | +| ------------------------------------------------------ | -------- | -------- | ------------------------------------------------------------------------------ | +| `services[_].credentials.s3_signing.service` | `string` | No | The AWS service to sign requests with, e.g. `execute-api` or `s3`. Default: `s3` | +| `services[_].credentials.s3_signing.signature_version` | `string` | No | The AWS signature version to sign requests with, e.g. `4` or `4a`. Default: `4` | + +#### Using Static Environment Credentials + +If specifying `environment_credentials`, OPA will expect to find environment variables +for `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION`, in accordance with the +convention used by the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html). + +Please note that if you are using temporary IAM credentials (e.g. assumed IAM role credentials) you have to provide additional `AWS_SESSION_TOKEN` or `AWS_SECURITY_TOKEN` environment variable. + +| Field | Type | Required | Description | +| ------------------------------------------------------------ | ---- | -------- | ------------------------------------------------------------------------------------------- | +| `services[_].credentials.s3_signing.environment_credentials` | `{}` | Yes | Enables AWS signing using environment variables to source the configuration and credentials | + +#### Using Named Profile Credentials + +If specifying `profile_credentials`, OPA will expect to find the `access key id`, `secret access key` and +`session token` from the [named profiles](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) +stored in the [credentials](https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html) file on disk. On each +request OPA will re-read the credentials from the file and use them for authentication. + +| Field | Type | Required | Description | +| ------------------------------------------------------------------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `services[_].credentials.s3_signing.profile_credentials.path` | `string` | No | The path to the shared credentials file. If empty, OPA will look for the `AWS_SHARED_CREDENTIALS_FILE` env variable. If the variable is not set, the path defaults to the current user's home directory. `~/.aws/credentials` (Linux & Mac) or `%USERPROFILE%\.aws\credentials` (Windows) | +| `services[_].credentials.s3_signing.profile_credentials.profile` | `string` | No | AWS Profile to extract credentials from the credentials file. If empty, OPA will look for the `AWS_PROFILE` env variable. If the variable is not set, the `default` profile will be used | +| `services[_].credentials.s3_signing.profile_credentials.aws_region` | `string` | No | The AWS region to use for the AWS signing service credential method. If unset, the `AWS_REGION` environment variable must be set | + +#### Using SSO Profile Credentials + +If specifying `sso_credentials`, OPA will expect to find an sso profile configured as explained in [SSO Profiles](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html) and stored in the [config](https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html) file on disk. +On each request, Opa will try to use cached token acquired credentials using the SSO credentials. In case the current token has expired, OPA will try to refresh the token using the SSO refresh token, assuming the SSO session is still valid. New token will be cached in memory. + + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `services[_].credentials.s3_signing.sso_credentials.path` | `string` | No | The path to the shared config file. If empty, OPA will look for the `AWS_CONFIG_FILE` env variable. If the variable is not set, the path defaults to the current user's home directory. `~/.aws/config` (Linux & Mac) or `%USERPROFILE%\.aws\config` (Windows) | +| `services[_].credentials.s3_signing.sso_credentials.profile` | `string` | No | AWS Profile to extract sso session from the config file. If empty, OPA will look for the `AWS_PROFILE` env variable. If the variable is not set, the `default` profile will be used | +| `services[_].credentials.s3_signing.sso_credentials.aws_region` | `string` | No | The AWS region to use for the AWS signing service credential method. If unset, the `AWS_REGION` environment variable must be set | + +#### Using EC2 Metadata Credentials + +If specifying `metadata_credentials`, OPA will use the AWS metadata services for [EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html) +or [ECS](https://docs.aws.amazon.com/AmazonECS/latest/userguide/task-iam-roles.html) +to obtain the necessary credentials when running within a supported virtual machine/container. + +To use the EC2 metadata service, the IAM role to use and the AWS region for the resource must both +be specified as `iam_role` and `aws_region` respectively. + +To use the ECS metadata service, specify only the AWS region for the resource as `aws_region`. ECS +containers have at most one associated IAM role. As per the [AWS documentation](https://docs.aws.amazon.com/sdkref/latest/guide/feature-container-credentials.html), credentials are +sourced from the `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` metadata environment variable or the +`AWS_CONTAINER_CREDENTIALS_FULL_URI` metadata environment variable in order. + +> Providing a value for `iam_role` will cause OPA to use the EC2 metadata service even +> if running inside an ECS container. This may result in unexpected problems if, for example, +> there is no route to the EC2 metadata service from inside the container or if the IAM role is only available within the container and not from the hosting EC2 instance. + +| Field | Type | Required | Description | +| -------------------------------------------------------------------- | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `services[_].credentials.s3_signing.metadata_credentials.aws_region` | `string` | No | The AWS region to use for the AWS signing service credential method. If unset, the `AWS_REGION` environment variable must be set | +| `services[_].credentials.s3_signing.metadata_credentials.iam_role` | `string` | No | The IAM role to use for the AWS signing service credential method | + +#### Using AWS Security Token Service (AWS STS) via AssumeRole + +If specifying `assume_role_credentials`, OPA will use [AWS STS](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html) +to obtain temporary security credentials for accessing AWS resources. In order to retrieve temporary security credentials from STS +via [AssumeRole](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html) valid AWS security credentials are required. + +:::info +For using `services[_].credentials.s3_signing.assume_role_credentials`, a method for setting the AWS credentials has to be specified in the `services[_].credentials.s3_signing.assume_role_credentials.aws_signing`. +The value of `services[_].credentials.s3_signing.assume_role_credentials.aws_signing.service` is set to `sts`. Several methods of obtaining the necessary credentials are available; exactly one must be specified, +see description for `services[_].credentials.s3_signing`. Currently supported methods are `services[_].credentials.s3_signing.environment_credentials`, `services[_].credentials.s3_signing.profile_credentials` and +`services[_].credentials.s3_signing.metadata_credentials`. OPA will follow this _internally defined_ order of precedence when multiple credential providers are specified. +::: + +| Field | Type | Required | Description | +| ------------------------------------------------------------------------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `services[_].credentials.s3_signing.assume_role_credentials.aws_region` | `string` | Yes | The AWS region to use for the sts regional endpoint. Uses the global endpoint by default | +| `services[_].credentials.s3_signing.assume_role_credentials.iam_role_arn` | `string` | Yes | The IAM Role ARN to be assumed. Can also be set via the `AWS_ROLE_ARN` environment variable (config takes precedence) | +| `services[_].credentials.s3_signing.assume_role_credentials.aws_signing` | `{}` | Yes | AWS credentials for signing requests. | +| `services[_].credentials.s3_signing.assume_role_credentials.session_name` | `string` | No | The session name used to identify the assumed role session. Default: `open-policy-agent` | +| `services[_].credentials.s3_signing.assume_role_credentials.aws_domain` | `string` | No | The AWS domain name to use. Default: `amazonaws.com`. Can also be set via the `AWS_DOMAIN` environment variable (config takes precedence) | + +The following is an example using Assume Role Credentials type with EC2 Metadata Credentials signing plugin: + +```yaml +services: + remote: + url: ${BUNDLE_SERVICE_URL} + credentials: + assume_role_credentials: + aws_region: us-east-1 + iam_role_arn: arn:aws::iam::123456789012:role/demo + session_name: demo + aws_signing: # similar to s3_signing + metadata_credentials: + aws_region: us-east-1 + iam_role: s3access + +bundles: + authz: + service: remote + resource: bundles/http/example/authz.tar.gz +``` + +#### Using EKS IAM Roles for Service Account (Web Identity) Credentials + +If specifying `web_identity_credentials`, OPA will expect to find environment variables for `AWS_ROLE_ARN` and `AWS_WEB_IDENTITY_TOKEN_FILE`, in accordance with the convention used by the [AWS EKS IAM Roles for Service Accounts](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). + +| Field | Type | Required | Description | +| -------------------------------------------------------------------------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `services[_].credentials.s3_signing.web_identity_credentials.aws_region` | `string` | Yes | The AWS region to use for the sts regional endpoint. Uses the global endpoint by default | +| `services[_].credentials.s3_signing.web_identity_credentials.session_name` | `string` | No | The session name used to identify the assumed role session. Default: `open-policy-agent` | +| `services[_].credentials.s3_signing.web_identity_credentials.aws_domain` | `string` | No | The AWS domain name to use. Default: `amazonaws.com`. Can also be set via the `AWS_DOMAIN` environment variable (config takes precedence) | + +### GCP Metadata Token + +OPA will authenticate with a GCP [access token](https://cloud.google.com/run/docs/securing/service-identity#access_tokens) or [identity token](https://cloud.google.com/run/docs/securing/service-identity) fetched from the [Compute Metadata Server](https://cloud.google.com/compute/docs/storing-retrieving-metadata). When one or more `scopes` is provided an access token is fetched. When a non-empty `audience` is provided an identity token is fetched. An audience or `scopes` array is required. + +When authenticating to native GCP services such as [Google Cloud Storage](https://cloud.google.com/storage) an access token should be used with the appropriate set of scopes required by the target resource. When authenticating to a third party application such as an application hosted on Google Cloud Run an identity token should be used. + +| Field | Type | Required | Description | +| -------------------------------------------------------- | -------- | -------- | ---------------------------------------------------- | +| `services[_].credentials.gcp_metadata.audience` | `string` | No | The audience to use when fetching identity tokens. | +| `services[_].credentials.gcp_metadata.endpoint` | `string` | No | The metadata endpoint to use. | +| `services[_].credentials.gcp_metadata.scopes` | `array` | No | The set of scopes to use when fetching access token. | +| `services[_].credentials.gcp_metadata.access_token_path` | `string` | No | The access token metadata path to use. | +| `services[_].credentials.gcp_metadata.id_token_path` | `string` | No | The identity token metadata path to use. | + +The following is an example using a [Cloud Run](https://cloud.google.com/run) service as a bundle service backend. + +```yaml +services: + cloudrun: + url: ${BUNDLE_SERVICE_URL} + response_header_timeout_seconds: 5 + credentials: + gcp_metadata: + audience: ${BUNDLE_SERVICE_URL} + +bundles: + authz: + service: cloudrun + resource: bundles/http/example/authz.tar.gz + persist: true + polling: + min_delay_seconds: 60 + max_delay_seconds: 120 +``` + +Using [Google Cloud Storage](https://cloud.google.com/storage) as a bundle service backend. + +```yaml +services: + gcs: + url: https://storage.googleapis.com/storage/v1/b/${BUCKET_NAME}/o + response_header_timeout_seconds: 5 + credentials: + gcp_metadata: + scopes: + - "https://www.googleapis.com/auth/devstorage.read_only" + +bundles: + authz: + service: gcs + resource: "bundles%2fhttp%2fexample%2fbundle.tar.gz?alt=media" + persist: true + polling: + min_delay_seconds: 60 + max_delay_seconds: 120 +``` + +When the given resource (the object in the GCS bucket) contains slashes (/) or other special characters, these need to be url-encoded here. + +### Azure Managed Identities Token + +OPA will authenticate with an [Azure managed identities](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview) token. +The [token request](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http) +can be configured via the plugin to customize the base URL, API version, and resource. Specific managed identity IDs can be optionally provided as well. +(The token request for [Azure App Service](https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=portal%2Chttp#connect-to-azure-services-in-app-code) or +[Azure Container Apps](https://learn.microsoft.com/en-us/azure/container-apps/managed-identity?tabs=bicep%2Chttp#connect-to-azure-services-in-app-code) is similar to above interface, +but the endpoint and the header are different. Please see the individual documents for more details.) + +| Field | Type | Required | Description | +| ------------------------------------------------------------ | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `services[_].credentials.azure_managed_identity.endpoint` | `string` | No | Request endpoint. (Detect endpoint from IDENTITY_ENDPOINT environment variable when you use managed identity on Azure App Service or Container Apps. Otherwise set default: `http://169.254.169.254/metadata/identity/oauth2/token`, the Azure Instance Metadata Service endpoint (recommended)) | +| `services[_].credentials.azure_managed_identity.api_version` | `string` | No | API version to use. (default: `2019-08-01` when you use `IDENTITY_ENDPONT` endpoint, otherwise `2018-02-01`, the minimum version) | +| `services[_].credentials.azure_managed_identity.resource` | `string` | No | App ID URI of the target resource. (default: `https://storage.azure.com/`) | +| `services[_].credentials.azure_managed_identity.object_id` | `string` | No | Optional object ID of the managed identity you would like the token for. Required, if your VM has multiple user-assigned managed identities. | +| `services[_].credentials.azure_managed_identity.client_id` | `string` | No | Optional client ID of the managed identity you would like the token for. Required, if your VM has multiple user-assigned managed identities. | +| `services[_].credentials.azure_managed_identity.mi_res_id` | `string` | No | Optional Azure Resource ID of the managed identity you would like the token for. Required, if your VM has multiple user-assigned managed identities. | + +The following is an example of how to use an [Azure storage account](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-overview) +as a bundle service backend. + +> Note that the `x-ms-version` header must be specified for the storage account +> service, and a minimum version of `2017-11-09` must be provided as per [Azure documentation](https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-azure-active-directory#call-storage-operations-with-oauth-tokens). + +```yaml +services: + azure_storage_account: + url: ${STORAGE_ACCOUNT_URL} + headers: + x-ms-version: 2017-11-09 + response_header_timeout_seconds: 5 + credentials: + azure_managed_identity: {} + +bundles: + authz: + service: azure_storage_account + resource: bundles/http/example/authz.tar.gz + persist: true + polling: + min_delay_seconds: 60 + max_delay_seconds: 120 +``` + +### OCI Repositories + +When using a private image from an OCI registry you need to specify an authentication method. Supported authentication methods are listed in the [Services](#services) section. The Azure managed identity plugin is not supported at this point in time. + +Examples of setting credentials for pulling private images: +_AWS ECR_ private images usually require at least basic authentication. The credentials to authenticate can be obtained using the AWS CLI command `aws ecr get-login` and those can be passed to the service configuration as basic bearer credentials as follows: + +```yaml +credentials: + bearer: + scheme: "Basic" + token: ":" +``` + +Other AWS authentication methods also work: + +```yaml +credentials: + s3_signing: + service: "ecr" + metadata_credentials: + aws_region: us-east-1 +``` + +Note, that the authentication method `s3_signing` does work for +signing requests to other AWS services. + +A special case is that bearer authentication works differently to normal service authentication. The OCI downloader base64-encodes the credentials for you so that they need to be supplied in plain text. + +For _GHCR_ (Github Container Registry) you can use a developer PAT (personal access token) when downloading a private image. These can be supplied as: + +```yaml +credentials: + bearer: + scheme: "Bearer" + token: "" +``` + +The following is a complete example using an OCI service type to download a bundle from an OCI repository. + +```yaml +services: + ghcr-registry: + url: https://ghcr.io + type: oci + +bundles: + authz: + service: ghcr-registry + resource: ghcr.io/${ORGANIZATION}/${REPOSITORY}:${TAG} + persist: true + polling: + min_delay_seconds: 60 + max_delay_seconds: 120 + +persistence_directory: ${PERSISTENCE_PATH} +``` + +When using an OCI service type the downloader uses the persistence path to store +the layers of the downloaded repository. This storage path should be maintained +by the user. If persistence is not configured the OCI downloader will store the +layers in the system's temporary directory to allow automatic cleanup on system +restart. + +### Custom Plugin + +If none of the existing credential options work for a service, OPA can authenticate using a custom plugin, enabling support for any authentication scheme. + +| Field | Type | Required | Description | +| -------------------------------- | -------- | -------- | ------------------------------------------------ | +| `services[_].credentials.plugin` | `string` | No | The name of the plugin to use for authentication | + +The following is an example of using a custom plugin for service credentials: + +```yaml +services: + my_service: + url: https://example.com/v1 + credentials: + plugin: my_custom_auth +plugins: + my_custom_auth: + foo: bar +``` + +```go +package plugins + +import ( + "github.com/open-policy-agent/opa/plugins" + "github.com/open-policy-agent/opa/plugins/rest" + "github.com/open-policy-agent/opa/runtime" + "github.com/open-policy-agent/opa/util" +) + +type Config struct { + Foo string `json:"foo"` +} + +type PluginFactory struct{} + +type Plugin struct { + manager *plugins.Manager + config Config + stop chan chan struct{} + reconfig chan interface{} +} + +func (p *PluginFactory) Validate(manager *plugins.Manager, config []byte) (interface{}, error) { + var parsedConfig Config + if err := util.Unmarshal(config, &parsedConfig); err != nil { + return nil, err + } + return &parsedConfig, nil +} + +func (p *PluginFactory) New(manager *plugins.Manager, config interface{}) plugins.Plugin { + return &Plugin{ + config: *config.(*Config), + manager: manager, + stop: make(chan chan struct{}), + reconfig: make(chan interface{}), + } +} + +func (p *Plugin) Start(ctx context.Context) error { + p.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateOK}) + return nil +} + +func (p *Plugin) Stop(ctx context.Context) { + done := make(chan struct{}) + p.stop <- done + <-done + p.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateNotReady}) + return +} + +func (p *Plugin) Reconfigure(ctx context.Context, config interface{}) { + p.reconfig <- config + return +} + +func (p *Plugin) NewClient(c rest.Config) (*http.Client, error) { + t, err := rest.DefaultTLSConfig(c) + if err != nil { + return nil, err + } + return rest.DefaultRoundTripperClient(t, *c.ResponseHeaderTimeoutSeconds), nil +} + +func (p *Plugin) Prepare(req *http.Request) error { + req.Header.Add("X-Custom-Auth-Protocol", "knock knock") + return nil +} + +func init() { + runtime.RegisterPlugin("my_custom_auth", &PluginFactory{}) +} +``` + +## Bundles + +Bundles are defined with a key that is the `name` of the bundle. This `name` is used in the status API, decision logs, +server provenance, etc. + +Each bundle can be configured to verify a bundle signature using the `keyid` and `scope` fields. The `keyid` is the name of +one of the keys listed under the [keys](#keys) entry. + +Signature verification fails if the `bundles[_].signing` field is configured on a bundle but no `.signatures.json` file is +included in the actual bundle gzipped tarball. + +| Field | Type | Required | Description | +| ------------------------------------------------- | ------------------------------ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `bundles[_].resource` | `string` | No (default: `bundles/`) | Resource path to use to download bundle from configured service. | +| `bundles[_].service` | `string` | Yes | Name of service to use to contact remote server. | +| `bundles[_].polling.min_delay_seconds` | `int64` | No (default: `60`) | Minimum amount of time to wait between bundle downloads. | +| `bundles[_].polling.max_delay_seconds` | `int64` | No (default: `120`) | Maximum amount of time to wait between bundle downloads. | +| `bundles[_].trigger` | `string` (default: `periodic`) | No | Controls how bundle is downloaded from the remote server. Allowed values are `periodic` and `manual` ([`manual` triggers](./integration/#manually-triggering-bundle-reloads) are only possible when running OPA as a SDK instance from the Go package). | +| `bundles[_].polling.long_polling_timeout_seconds` | `int64` | No | Maximum amount of time the server should wait before issuing a timeout if there's no update available. | +| `bundles[_].persist` | `bool` | No | Persist activated bundles to disk. | +| `bundles[_].signing.keyid` | `string` | No | Name of the key to use for bundle signature verification. | +| `bundles[_].signing.scope` | `string` | No | Scope to use for bundle signature verification. | +| `bundles[_].signing.exclude_files` | `array` | No | Files in the bundle to exclude during verification. | +| `bundles[_].size_limit_bytes` | `int64` | No (default: `1073741824`) | Size limit for individual files contained in the bundle. | + +## Status + +| Field | Type | Required | Description | +| ------------------------------------------------------------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `status.service` | `string` | Yes | Name of service to use to contact remote server. | +| `status.partition_name` | `string` | No | Path segment to include in status updates. | +| `status.console` | `boolean` | No (default: `false`) | Log the status updates locally to the console. When enabled alongside a remote status update API the `service` must be configured, the default `service` selection will be disabled. | +| `status.prometheus` | `boolean` | No (default: `false`) | Export the status (bundle and plugin) metrics to prometheus (see [the monitoring documentation](./monitoring/#prometheus)). When enabled alongside a remote status update API the `service` must be configured, the default `service` selection will be disabled. | +| `status.prometheus_config.collectors.bundle_loading_duration_ns.buckets` | `[]float64` | No, (Only use when status.prometheus true, default: [1000, 2000, 4000, 8000, 16_000, 32_000, 64_000, 128_000, 256_000, 512_000, 1_024_000, 2_048_000, 4_096_000, 8_192_000, 16_384_000, 32_768_000, 65_536_000, 131_072_000, 262_144_000, 524_288_000]) | Specifies the buckets for the `bundle_loading_duration_ns` metric. Each value is a float, it is expressed in nanoseconds. | +| `status.plugin` | `string` | No | Use the named plugin for status updates. If this field exists, the other configuration fields are not required. | +| `status.trigger` | `string` | No (default: `periodic`) | Controls how status updates are reported to the remote server. Allowed values are `periodic` and `manual` (`manual` triggers are only possible when using OPA as a Go package). | + +## Decision Logs + +| Field | Type | Required | Description | +|----------------------------------------------------|-----------|-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `decision_logs.service` | `string` | No | Name of the service to use to contact remote server. If no `plugin` is specified, and `console` logging is disabled, this will default to the first `service` name defined in the Services configuration. | +| `decision_logs.partition_name` | `string` | No | Deprecated: Use `resource` instead. Path segment to include in status updates. | +| `decision_logs.resource` | `string` | No (default: `/logs`) | Full path to use for sending decision logs to a remote server. | +| `decision_logs.reporting.buffer_type` | `string` | No (default: `size`) | Toggles the type of buffer to use. The two available options are "size" or "event". Refer to the [Decision Log Plugin README](https://github.com/open-policy-agent/opa/tree/main/v1/plugins/logs/README.md) for for a detailed comparison. | +| `decision_logs.reporting.buffer_size_limit_events` | `int64` | No (default: `10000`) | Decision log buffer size limit by events. OPA will drop old events from the log if this limit is exceeded. By default, 100 events are held. This number has to be greater than zero. Only works with "event" buffer type. | +| `decision_logs.reporting.buffer_size_limit_bytes` | `int64` | No (default: `unlimited`) | Decision log buffer size limit in bytes. OPA will drop old events from the log if this limit is exceeded. By default, no limit is set. Only one of `buffer_size_limit_bytes`, `max_decisions_per_second` may be set. Only works with "size" buffer type. | +| `decision_logs.reporting.max_decisions_per_second` | `float64` | No | Maximum number of decision log events to buffer per second. OPA will drop events if the rate limit is exceeded. Only one of `buffer_size_limit_bytes`, `max_decisions_per_second` may be set. | +| `decision_logs.reporting.upload_size_limit_bytes` | `int64` | No (default: `32768`) | Decision log upload size limit in bytes. This limit enforces the maximum size of a gzip compressed payload of events within the message body. | +| `decision_logs.reporting.min_delay_seconds` | `int64` | No (default: `300`) | Minimum amount of time to wait between uploads. | +| `decision_logs.reporting.max_delay_seconds` | `int64` | No (default: `600`) | Maximum amount of time to wait between uploads. | +| `decision_logs.reporting.trigger` | `string` | No (default: `periodic`) | Controls how decision logs are reported to the remote server. Allowed values are `periodic` and `manual` (`manual` triggers are only possible when using OPA as a Go package). | +| `decision_logs.mask_decision` | `string` | No (default: `/system/log/mask`) | Set path of masking decision. | +| `decision_logs.drop_decision` | `string` | No (default: `/system/log/drop`) | Set path of drop decision. | +| `decision_logs.plugin` | `string` | No | Use the named plugin for decision logging. If this field exists, the other configuration fields are not required. | +| `decision_logs.console` | `boolean` | No (default: `false`) | Log the decisions locally to the console. When enabled alongside a remote decision logging API the `service` must be configured, the default `service` selection will be disabled. | +| `decision_logs.request_context.http.headers` | `array` | No | List of HTTP headers to include in the decision log. OPA will include the values for these headers in the decision log if they exist in the incoming HTTP request. | + +## Discovery + +| Field | Type | Required | Description | +| ------------------------------------------------ | ------------------------------ | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `discovery.resource` | `string` | Yes | Resource path to use to download bundle from configured service. | +| `discovery.service` | `string` | No | Name of the service to use to contact remote server. If omitted, the configuration must contain exactly one service. Discovery will default to this service. | +| `discovery.decision` | `string` | No | The path of the decision to evaluate in the discovery bundle. By default, OPA will evaluate `data` in the discovery bundle to produce the configuration. | +| `discovery.polling.min_delay_seconds` | `int64` | No (default: `60`) | Minimum amount of time to wait between configuration downloads. | +| `discovery.polling.max_delay_seconds` | `int64` | No (default: `120`) | Maximum amount of time to wait between configuration downloads. | +| `discovery.trigger` | `string` (default: `periodic`) | No | Controls how bundle is downloaded from the remote server. Allowed values are `periodic` and `manual` (`manual` triggers are only possible when using OPA as a Go package). | +| `discovery.polling.long_polling_timeout_seconds` | `int64` | No | Maximum amount of time the server should wait before issuing a timeout if there's no update available. | +| `discovery.signing.keyid` | `string` | No | Name of the key to use for bundle signature verification. | +| `discovery.signing.scope` | `string` | No | Scope to use for bundle signature verification. | +| `discovery.signing.exclude_files` | `array` | No | Files in the bundle to exclude during verification. | +| `discovery.persist` | `bool` | No | Persist activated discovery bundle to disk. | + +> ⚠️ The plugin trigger mode configured on the discovery plugin will be inherited by the bundle, decision log +> and status plugins. For example, if the discovery plugin is configured to use the manual trigger mode, all other +> plugins will use manual triggering as well. If any of the plugins explicitly specify a different mode (for ex. periodic), +> OPA will generate a configuration error. + +The following `discovery` configuration fields are supported but deprecated: + +| Field | Type | Required | Description | +| ------------------ | -------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `discovery.prefix` | `string` | No (default: `bundles`) | Deprecated: Use `resource` instead. Path prefix to use to download configuration from remote server. | +| `discovery.name` | `string` | No | Deprecated: Use `resource` instead. Name of the discovery configuration to download. If `discovery.name` is specified and `discovery.resource` is unset, the `discovery.decision` field will default to the `discovery.name` value. | + +## Keys + +Keys is a dictionary mapping the key name to the actual key and optionally the algorithm and scope. + +| Field | Type | Required | Description | +| --------------------- | -------- | ----------------------------------- | --------------------------------------------------------- | +| `keys[_].key` | `string` | Yes (unless `private_key` provided) | PEM encoded public key to use for signature verification. | +| `keys[_].private_key` | `string` | Yes (unless `key` provided`) | PEM encoded private key to use for signing. | +| `keys[_].algorithm` | `string` | No (default: `RS256`) | Name of the signing algorithm. | +| `keys[_].scope` | `string` | No | Scope to use for bundle signature verification. | + +> Note: If the `scope` is provided in a bundle's `signing` configuration (ie. `bundles[_].signing.scope`), +> it takes precedence over `keys[_].scope`. + +The following signing algorithms are supported: + +| Name | Description | +| ------- | --------------------------------------- | +| `ES256` | ECDSA using P-256 and SHA-256 | +| `ES384` | ECDSA using P-384 and SHA-384 | +| `ES512` | ECDSA using P-521 and SHA-512 | +| `HS256` | HMAC using SHA-256 | +| `HS384` | HMAC using SHA-384 | +| `HS512` | HMAC using SHA-512 | +| `PS256` | RSASSA-PSS using SHA256 and MGF1-SHA256 | +| `PS384` | RSASSA-PSS using SHA384 and MGF1-SHA384 | +| `PS512` | RSASSA-PSS using SHA512 and MGF1-SHA512 | +| `RS256` | RSASSA-PKCS-v1.5 using SHA-256 | +| `RS384` | RSASSA-PKCS-v1.5 using SHA-384 | +| `RS512` | RSASSA-PKCS-v1.5 using SHA-512 | + +## Caching + +Caching represents the configuration of the inter-query cache that built-in functions can utilize. Of the built-in +functions provided by OPA, `http.send` is currently the only one to utilize the inter-query cache. See the documentation +on the [http.send built-in function](./policy-reference/#http) for information about the available caching options. + +It also represents the configuration of the inter-query _value_ cache that built-in functions can utilize. Currently, +this cache is utilized by the `regex` and `glob` built-in functions for compiled regex and glob match patterns +respectively, the `json.schema_match` built-in function for compiled JSON schemas, and any `graphql` built-in function +that requires GraphQL schemas. + +| Field | Type | Required | Description | +| ------------------------------------------------------------------------ | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `caching.inter_query_builtin_cache.max_size_bytes` | `int64` | No | Inter-query cache size limit in bytes. OPA will drop old items from the cache if this limit is exceeded. By default, no limit is set. | +| `caching.inter_query_builtin_cache.forced_eviction_threshold_percentage` | `int64` | No | Threshold limit configured as percentage of `caching.inter_query_builtin_cache.max_size_bytes`, when exceeded OPA will start dropping old items prematurely. By default, set to `100`. | +| `caching.inter_query_builtin_cache.stale_entry_eviction_period_seconds` | `int64` | No | Stale entry eviction period in seconds. OPA will drop expired items from the cache every `stale_entry_eviction_period_seconds`. By default, set to `0` indicating stale entry eviction is disabled. | +| `caching.inter_query_builtin_value_cache.max_num_entries` | `int` | No | Maximum number of entries in the Inter-query value cache. OPA will drop random items from the cache if this limit is exceeded. By default, set to `0` indicating unlimited size. | +| `caching.inter_query_builtin_value_cache.named.io_jwt.max_num_entries` | `int` | No | Maximum number of entries in the `io_jwt` cache, used by the [`io.jwt` token verification](./policy-reference/#tokens) built-in functions. OPA will drop random items from the cache if this limit is exceeded. By default, this cache is disabled. | +| `caching.inter_query_builtin_value_cache.named.graphql.max_num_entries` | `int` | No | Maximum number of entries in the `graphql` cache, used by the [`graphql` builtins](./policy-reference/#graphql) built-in functions to cache parsed schemas. OPA will drop random items from the cache if this limit is exceeded. By default, this cache is set to a maximum of 10 entries. | + +## Distributed tracing + +Distributed tracing represents the configuration of the OpenTelemetry Tracing. + +| Field | Type | Required | Description | +| ------------------------------------------------------------------------ | --------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | +| `distributed_tracing.type` | `string` | No | Setting this to "grpc" enables distributed tracing with an gRPC endpoint; or "http" with an HTTP endpoint. | +| `distributed_tracing.address` | `string` | No (default: `localhost:4317` if `type` is `grpc` or `localhost:4318` if `type` is `http`) | Address of the OpenTelemetry Collector gRPC or HTTP endpoint. | +| `distributed_tracing.service_name` | `string` | No (default: `opa`) | Logical name of the service. | +| `distributed_tracing.sample_percentage` | `float64` | No (default: `100`) | Percentage of traces that are sampled and exported. | +| `distributed_tracing.encryption` | `string` | No (default: `off`) | Configures TLS. | +| `distributed_tracing.allow_insecure_tls` | `bool` | No (default: `false`) | Allow insecure TLS. | +| `distributed_tracing.tls_ca_cert_file` | `string` | No | The path to the root CA certificate. | +| `distributed_tracing.tls_cert_file` | `string` | No (unless `encryption` equals `mtls`) | The path to the client certificate to authenticate with. | +| `distributed_tracing.tls_private_key_file` | `string` | No (unless `tls_cert_file` provided) | The path to the private key of the client certificate. | +| `distributed_tracing.resource.service_version` | `string` | No | Service version | +| `distributed_tracing.resource.service_instance_id` | `string` | No | Service instance id | +| `distributed_tracing.resource.service_namespace` | `string` | No | Service namespace | +| `distributed_tracing.resource.deployment_environment` | `string` | No | Deployment environment name | +| `distributed_tracing.batch_span_processor_options.blocking` | `bool` | No (default: `false`) | Wait for span batch enqueue operations to succeed instead of dropping data when the queue is full. | +| `distributed_tracing.batch_span_processor_options.batch_timeout_ms` | `int` | No (default: `5000`) | The maximum duration for constructing a batch in milliseconds. | +| `distributed_tracing.batch_span_processor_options.export_timeout_ms` | `int` | No (default: `30000`) | The maximum duration for exporting spans in milliseconds. | +| `distributed_tracing.batch_span_processor_options.max_export_batch_size` | `int` | No (default: `512`) | The maximum number of spans to process in a single batch. | +| `distributed_tracing.batch_span_processor_options.max_queue_size` | `int` | No (default: `2048`) | The maximum queue size to buffer spans for delayed processing. | + +The following encryption methods are supported: + +| Name | Description | +| ------ | ----------------- | +| `off` | Disable TLS | +| `tls` | Enable TLS | +| `mtls` | Enable mutual TLS | + +## Disk Storage + +The `storage` configuration key allows for enabling, and configuring, the +persistent on-disk storage of an OPA instance. + +If `disk` is set to something, the server will enable the on-disk store +with data put into the configured `directory`. + +| Field | Type | Required | Description | +| -------------------------- | --------------- | --------------------- | ------------------------------------------------------------------------------ | +| `storage.disk.directory` | `string` | Yes | This is the directory to use for storing the persistent database. | +| `storage.disk.auto_create` | `bool` | No (default: `false`) | If set to true, the configured directory will be created if it does not exist. | +| `storage.disk.partitions` | `array[string]` | No | Non-overlapping `data` prefixes used for partitioning the data on disk. | +| `storage.disk.badger` | `string` | No (default: empty) | "Superflags" passed to Badger allowing to modify advanced options. | + +See [the docs on disk storage](./storage/) for details about the settings. + +## Server + +The `server` configuration sets: + +- for all incoming requests: + - maximum allowed request size + - maximum decompressed gzip payload size +- the gzip compression settings for responses from the `/v0/data`, `/v1/data` and `/v1/compile` HTTP `POST` endpoints + The gzip compression settings are used when the client sends `Accept-Encoding: gzip` +- buckets for `http_request_duration_seconds` histogram + +| Field | Type | Required | Description | +| ----------------------------------------------------------- | ----------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `server.decoding.max_length` | `int` | No, (default: 268435456) | Specifies the maximum allowed number of bytes to read from a request body. | +| `server.decoding.gzip.max_length` | `int` | No, (default: 536870912) | Specifies the maximum allowed number of bytes to read from the gzip decompressor for gzip-encoded requests. | +| `server.encoding.gzip.min_length` | `int` | No, (default: 1024) | Specifies the minimum length of the response to compress. | +| `server.encoding.gzip.compression_level` | `int` | No, (default: 9) | Specifies the compression level. Accepted values: a value of either 0 (no compression), 1 (best speed, lowest compression) or 9 (slowest, best compression). See https://pkg.go.dev/compress/flate#pkg-constants | +| `server.metrics.prom.http_request_duration_seconds.buckets` | `[]float64` | No, (default: [1e-6, 5e-6, 1e-5, 5e-5, 1e-4, 5e-4, 1e-3, 0.01, 0.1, 1 ]) | Specifies the buckets for the `http_request_duration_seconds` metric. Each value is a float, it is expressed in seconds and subdivisions of it. E.g `1e-6` is 1 microsecond, `1e-3` 1 millisecond, `0.01` 10 milliseconds | + +## Miscellaneous + +| Field | Type | Required | Description | +| -------------------------------- | --------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `labels` | `object` | Yes | Set of key-value pairs that uniquely identify the OPA instance. Labels are included when OPA uploads decision logs and status information. | +| `default_decision` | `string` | No (default: `/system/main`) | Set path of default policy decision used to serve queries against OPA's base URL. | +| `default_authorization_decision` | `string` | No (default: `/system/authz/allow`) | Set path of default authorization decision for OPA's API. | +| `persistence_directory` | `string` | No (default `$PWD/.opa`) | Set directory to use for persistence with options like `bundles[_].persist`. | +| `plugins` | `object` | No (default: `{}`) | Location for custom plugin configuration. | +| `nd_builtin_cache` | `boolean` | No (default: `false`) | Enable the non-deterministic builtins caching system during policy evaluation, and include the contents of the cache in decision logs. Note that decision logs that are larger than `upload_size_limit_bytes` will drop the `nd_builtin_cache` key from the log entry before uploading. | + +## Using Environment Variables in Configuration + +> Only supported with the OPA runtime (`opa run`). + +Environment variables referenced with the `${...}` notation within the configuration +will be replaced with the value of the environment variable. + +Example using `BASE_URL` and `BEARER_TOKEN` environment variables: + +```yaml +services: + acmecorp: + url: "${BASE_URL}" + credentials: + bearer: + token: "${BEARER_TOKEN}" + +discovery: + resource: /configuration/example/discovery + decision: example +``` + +The environment variables `BASE_URL` and `BEARER_TOKEN` will be substituted in when the config +file is loaded by the OPA runtime. + +> If the variable is undefined then an empty string (`""`) is substituted. It will **not** +> raise an error. + +## Setting Configuration via CLI Arguments + +> Only supported with the OPA runtime (`opa run`). + +Using `opa run` there are CLI options to explicitly set config values. These will override +any values set in the config file. + +There are two options to use: `--set` and `--set-file` + +Both options take in a key=value format where the key is a selector for the yaml +config structure, for example: `decision_logs.reporting.min_delay_seconds=300` is equivalent +to JSON `{"decision_logs": {"reporting": {"min_delay_seconds": 300}}}`. Multiple values can be +specified with comma separators (`key1=value,key2=value2,..`). Or with additional `--set` +parameters. + +Example using several different options: + +```shell +opa run \ + --set "default_decision=/http/example/authz/allow" \ + --set "services.acmecorp.url=https://test-env/control-plane-api/v1" \ + --set "services.acmecorp.credentials.bearer.token=\${TOKEN}" + --set "labels.app=myapp,labels.region=west" +``` + +This is equivalent to a YAML config file that looks like: + +```yaml +services: + acmecorp: + url: https://test-env/control-plane-api/v1 + credentials: + bearer: + token: ${TOKEN} + +labels: + app: myapp + region: west + +default_decision: /http/example/authz/allow +``` + +The `--set-file` option is expecting a file path for the value. This allows keeping secrets in +files and loading them into the config at run time. For Example: + +With a file `/var/run/secrets/bearer_token.txt` that has contents: + +``` +bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm +``` + +Then using the `--set-file` flag for OPA + +```bash +opa run --set-file "services.acmecorp.credentials.bearer.token=/var/run/secrets/bearer_token.txt" +``` + +It will read the contents of the file and set the config value with the token. + +### Override Limitations with Lists + +If using arrays/lists in the configuration the `--set` and `--set-file` overrides will not be able to +patch sub-objects of the list. They will overwrite the entire index with the new object. + +For example, a `opa-config.yaml` file with contents: + +```yaml +services: +- name: acmecorp + url: https://test-env/control-plane-api/v1 + credentials: + bearer: + token: "" +``` + +Used with overrides: + +```shell +opa run \ + --config-file opa-config.yaml + --set-file "services[0].credentials.bearer.token=/var/run/secrets/bearer_token.txt" +``` + +Will result in configuration like: + +```yaml +services: +- credentials: + bearer: + token: bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm +``` + +Because the entire `0` index was overwritten. + +It is highly recommended to use objects/maps instead of lists for configuration for this reason. + +### Remote Bundles Override Shorthand + +When running the server to quickly try a remote public bundle — such as those published from the +[Rego Playground](https://play.openpolicyagent.org), you may find it convenient to provide the URL of the +bundle directly, rather than via repeated `--set` flags: + +```shell +opa run -s https://example.com/bundles/bundle.tar.gz +``` + +The above shorthand command is identical to: + +```shell +opa run -s --set "services.cli1.url=https://example.com" \ + --set "bundles.cli1.service=cli1" \ + --set "bundles.cli1.resource=/bundles/bundle.tar.gz" \ + --set "bundles.cli1.persist=true" +``` + +### Empty Objects + +If you need to set an empty object with the CLI overrides, for example with plugin configuration like: + +```yaml +decision_logs: + plugin: my_plugin + +plugins: + my_plugin: +# empty +``` + +You can do this by setting the value with `null`. For example: + +```shell +opa run --set "decision_logs.plugin=my_plugin" --set "plugins.my_plugin=null" +``` + +### Keys with Special Characters + +If you have a key which contains a special character (`=`, `[`, `,`, `.`), like `opa.example.com`, and want to use +the `--set` or `--set-file` options you will need to escape the character with a backslash (`\`). + +For example a config section like: + +```yaml +services: + opa.example.com: + url: https://opa.example.com +``` + +Could be specified with something like: + +`--set services.opa\.example\.com.url=https://opa.example.com` + +Note that when using it in a shell you may need to put it in quotes or escape the `\` +character too. For example: + +`--set services."opa\.example\.com".url=https://opa.example.com` + +_or_ + +`--set services.opa\\.example\\.com.url=https://opa.example.com` + +Where the end result passed into OPA still has the `\.` preserved. diff --git a/third_party/opa/docs/docs/contrib-adding-builtin-functions.md b/third_party/opa/docs/docs/contrib-adding-builtin-functions.md new file mode 100644 index 000000000000..8260aad3a057 --- /dev/null +++ b/third_party/opa/docs/docs/contrib-adding-builtin-functions.md @@ -0,0 +1,187 @@ +--- +title: Adding Built-in Functions +--- + +[Built-in Functions](./policy-reference/#built-in-functions) +can be added inside the `topdown` package. + +Built-in functions may be upstreamed if they are generally useful and provide functionality that would be +impractical to implement natively in Rego (e.g., CIDR arithmetic). Implementations should avoid third-party +dependencies. If absolutely necessary, consider importing the code manually into the `internal` package. + +:::info +Read more about extending OPA with custom built-in functions in go [here](./extensions#custom-built-in-functions-in-go). +::: + +Adding a new built-in function involves the following steps: + +1. [Declare and register](#declare-and-register) the function +2. [Implement](#implement) the function +3. [Test](#test) the function +4. [Document](#document) the function +5. [Add a capability](#add-a-capability) for the function + +## Example + +The following example adds a simple built-in function, `repeat(string, int)`, that returns a given string repeated a given number of times. + +### Declare and Register + +In `ast/builtins.go`, we declare the structure of our built-in function with a `Builtin` struct instance: + +```go +var Repeat = &Builtin{ + Name: "repeat", // The name of the function + Description: "Returns, as a string, the given string repeated the given number of times.", + Decl: types.NewFunction( + types.Args( // The built-in takes two arguments, where .. + types.Named("str", types.S).Description("string to repeat"), // named string argument + types.Named("count", types.N).Description("how often to repeat `str`"), // named number argument + ), + types.Named("output", types.S).Description("the repetitions"), // The return type is a string. + ), + Categories: category("strings"), // the category the built-in belongs to +} +``` + +To register the new built-in function, we locate the `DefaultBuiltins` array in `ast/builtins.go`, and add the `Builtin` instance to it: + +```go +var DefaultBuiltins = [...]*Builtin{ + ... + Repeat, + ... +} +``` + +### Implement + +In the `topdown` package, we locate a suitable source file for our new built-in function, or add a new file, as appropriate. + +In this example, we introduce a new source file, `topdown/repeat.go`: + +```go +package topdown + +import ( + "fmt" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +// implements topdown.BuiltinFunc +func builtinRepeat(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + // Get the first argument as a string, returning an error if it's not the correct type. + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Get the second argument as an int, returning an error if it's not the correct type or not a positive value. + count, err := builtins.IntOperand(operands[1].Value, 2) + if err != nil { + return err + } else if count < 0 { + // Defensive check, strings.Repeat(...) will panic for count<0 + return fmt.Errorf("count must be a positive integer") + } + + // Return a string by invoking the given iterator function + return iter(ast.StringTerm(strings.Repeat(string(str), count))) +} + +func init() { + RegisterBuiltinFunc(ast.Repeat.Name, builtinRepeat) +} +``` + +In the above code, `builtinRepeat` implements the `topdown.BuiltinFunc` function type. +The call to `RegisterBuiltinFunc(...)` in `init()` adds the built-in function to the evaluation engine; binding the implementation to `ast.Repeat` that was registered in [an earlier step](#declare-and-register). + +### Test + +All built-in function implementations must include a test suite. +Test cases for built-in functions are written in YAML and located under `test/cases/testdata/v1`. + +We create two new test cases (one positive, expecting a string output; and one negative, expecting an error) for our built-in function: + +```yaml +cases: +- note: repeat/positive + query: data.test.p = x + modules: + - | + package test + + p := repeated if { + repeated := repeat(input.str, input.count) + } + input: { "str": "Foo", "count": 3 } + want_result: + - x: FooFooFoo +- note: repeat/negative + query: data.test.p = x + modules: + - | + package test + + p := repeated if { + repeated := repeat(input.str, input.count) + } + input: { "str": "Foo", "count": -3 } + strict_error: true + want_error_code: eval_builtin_error + want_error: "repeat: count must be a positive integer" +``` + +The above test cases can be run separate from all other tests through: `go test ./topdown -v -run 'TestRego/v1/repeat'` + +See [test/cases/testdata/helloworld](https://github.com/open-policy-agent/opa/tree/main/v1/test/cases/testdata/v0/helloworld) +for a more detailed example of how to implement tests for your built-in functions. + +:::info +Note: We can manually test our new built-in function by [building](./contrib-development#getting-started) +and running the `eval` command. E.g.: `$./opa__ eval 'repeat("Foo", 3)'` +::: + +### Document + +All built-in functions will automatically be documented in `docs/content/policy-reference.md` under an appropriate subsection. + +For this example, we'll get an entry for our new function under the `Strings` section. + +### Add a capability + +:::info +Read more about extending the default capabilities list for built-ins [here](./deployments/#built-ins). +::: + +One of the security features of OPA is [capabilities](./deployments/#capabilities) checks on policies, allowing users to restrict which built-in functions will be available to policies at runtime. +To ensure that our new `repeat` function will be available to callers, we'll need to add it to the `capabilities.json` file at the root of the repo. +We can have this entry auto-generated for us by running `make generate`. + +After running `make generate` we should see a new JSON object entry in the list under the `"builtins"` key: + +```json +... +{ + "name": "repeat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } +}, +... +``` diff --git a/third_party/opa/docs/docs/contrib-code.md b/third_party/opa/docs/docs/contrib-code.md new file mode 100644 index 000000000000..27b6b6e8076b --- /dev/null +++ b/third_party/opa/docs/docs/contrib-code.md @@ -0,0 +1,188 @@ +--- +title: Contributing Code +--- + +We are thrilled that you're interested in contributing to OPA! This document +outlines some of the important guidelines when getting started as a new +contributor. For developer environment setup, please refer to the +[Contributing Development](./contrib-development/) page. + +When contributing please consider the following pointers: + +- Most changes should be accompanied with tests. +- All commits must be signed off (see next section). +- Related commits must be squashed before they are merged (this can be done in + the PR UI on GitHub). +- All tests must pass and there must be no warnings from the `make check` target. + +When you implement new features in OPA, consider whether the +types/functions you are adding need to be exported. Prefer +unexported types and functions as much as possible. + +If you need to share logic across multiple OPA packages, consider +implementing it inside of the +`github.com/open-policy-agent/opa/internal` package. The `internal` +package is not visible outside of OPA. + +Avoid adding third-party dependencies (vendoring). OPA is designed to be minimal, +lightweight, and easily embedded. Vendoring may make features _easier_ to +implement however they come with their own cost for both OPA developers and +OPA users (e.g., vendoring conflicts, security, debugging, etc.) + +### Commit Messages + +Commit messages should explain _why_ the changes were made and should probably +look like this: + +``` +Description of the change in 50 characters or less + +More detail on what was changed. Provide some background on the issue +and describe how the changes address the issue. Feel free to use multiple +paragraphs but please keep each line under 72 characters or so. +``` + +If your changes are related to an open issue (bug or feature), please include +the following line at the end of your commit message: + +``` +Fixes # +``` + +If the changes are isolated to a specific OPA package or directory please +include a prefix on the first line of the commit message with the following +format: + +``` +: +``` + +For example, a change to the `ast` package: + +``` +ast: Fix X when Y happens + + + +Fixes: #123 +Signed-off-by: Random J Developer +``` + +or a change in the OPA website content (found in `./docs/content`): + +``` +docs/website: Add X to homepage for Y + + + +Fixes: #456 +Signed-off-by: Random J Developer +``` + +### Developer Certificate Of Origin + +The OPA project requires that contributors sign off on changes submitted to OPA +repositories. +The [Developer Certificate of Origin (DCO)](https://developercertificate.org/) +is a simple way to certify that you wrote or have the right to submit the code +you are contributing to the project. + +The DCO is a standard requirement for Linux Foundation and CNCF projects. + +You sign-off by adding the following to your commit messages: + +``` +This is my commit message + +Signed-off-by: Random J Developer +``` + +Git has a `-s` command line option to do this automatically. + +```sh +git commit -s -m 'This is my commit message' +``` + +You can find the full text of the DCO here: https://developercertificate.org/ + +:::info +**Note:** If using AI or machine learning tools to assist in the authoring +of OPA patches, you must ensure the code you produce is compliant with the +DCO requirements, and OPA's license. All commits in your patch _must_ be signed +off by a human author. + +The OPA maintainers reserve the right to request additional information about +patches and reject PRs where code origin cannot be verified. +::: + +### Code Review + +Before a Pull Request is merged, it will undergo code review from other members +of the OPA community. In order to streamline the code review process, when +amending your Pull Request in response to a review, do not squash your changes +into relevant commits until it has been approved for merge. This allows the +reviewer to see what changes are new and removes the need to wade through code +that has not been modified to search for a small change. + +When adding temporary patches in response to review comments, consider +formatting the message subject like one of the following: + +- `Fixup into commit (squash before merge)` +- `Fixed changes requested by @username (squash before merge)` +- `Amended (squash before merge)` + +The purpose of these formats is to provide some context into the reason the +temporary commit exists, and to label it as needing squashed before a merge +is performed. + +It is worth noting that not all changes need be squashed before a merge is +performed. Some changes made as a result of review stand well on their own, +independent of other commits in the series. Such changes should be made into +their own commit and added to the PR. + +If your Pull Request is small though, it is acceptable to squash changes during +the review process. Use your judgement about what constitutes a small Pull +Request. If you aren't sure, send a message to the OPA slack or post a comment +on the Pull Request. + +### Vulnerability scanning + +On each Pull Request, a series of tests will be run to ensure that the code +is up to standard. Part of this process is also to run vulnerability scanning +on the code and on the generated container image. + +[Trivy](https://aquasecurity.github.io/trivy/) is used to run the aforementioned +vulnerability scanning. To install, follow the [installation instructions](https://aquasecurity.github.io/trivy/v0.29.2/getting-started/installation/). + +To run the vulnerability scanning, on the code-base, run the following command: + +```bash +$ trivy fs . +``` + +To run the vulnerability scanning on the container image, run the following command: + +```bash +$ trivy image +``` + +If the tool catches any false positives, it's recommended to appropriately document them +in the `.trivyignore` file. + +## Contribution process + +Small bug fixes (or other small improvements) can be submitted directly via a +[Pull Request](https://github.com/open-policy-agent/opa/pulls) on GitHub. +You can expect at least one of the OPA maintainers to respond quickly. + +Before submitting large changes, please open an issue on GitHub outlining: + +- The use case that your changes are applicable to. +- Steps to reproduce the issue(s) if applicable. +- Detailed description of what your changes would entail. +- Alternative solutions or approaches if applicable. + +Use your judgement about what constitutes a large change. If you aren't sure, +send a message in +[#contributors](https://openpolicyagent.slack.com/archives/C02L1TLPN59) on Slack +or submit [an issue on GitHub](https://github.com/open-policy-agent/opa/issues). diff --git a/third_party/opa/docs/docs/contrib-development.md b/third_party/opa/docs/docs/contrib-development.md new file mode 100644 index 000000000000..9a1b2075cd2d --- /dev/null +++ b/third_party/opa/docs/docs/contrib-development.md @@ -0,0 +1,197 @@ +--- +title: Development +--- + +This page details the process for getting up and running locally for OPA +development. If you're a first time contributor, we recommend you read through +the [Contributing to OPA](./contrib-code) page first. + +OPA is written in the [Go](https://golang.org) programming language. +If you are new to Go, consider reading +[Effective Go](https://golang.org/doc/effective_go.html), +[Go Code Review Comments](https://go.dev/wiki/CodeReviewComments) or +[How to Write Go Code](https://go.dev/doc/code) +for guidance on writing idiomatic Go code. + +Requirements: + +- Git +- GitHub account (if you are contributing) +- Go (please see the project's [go.mod](https://github.com/open-policy-agent/opa/blob/main/go.mod) file for the current version in use) +- GNU Make +- Python3, pip, yamllint (if linting YAML files manually) + +## Getting Started + +After forking the repository and creating a [clone from your fork](https://docs.github.com/en/get-started/quickstart/contributing-to-projects), +just run `make`. This will: + +- Build the OPA binary. +- Run all of the tests. +- Run all of the static analysis checks. + +If the build was successful, a binary will be produced in the top directory (`opa__`). + +Verify the build was successful with `./opa__ run`. + +You can re-build the project with `make build`, execute all of the tests +with `make test`, and execute all of the performance benchmarks with `make perf`. + +For quicker development-test iteration, you may use `make test-short` during development, +and only run `make test` before submitting your changed. This avoids running the slowest +tests and normally completes under a minute (compared to the several minutes required to run +the full test suite). + +The static analysis checks (e.g., `go fmt`, `golint`, `go vet`) can be run +with `make check`. + +> To correct any imports or style errors run `make fmt`. + +## Workflow + +### Fork, clone, create a branch + +Go to [https://github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) and fork the repository +into your account by clicking the "Fork" button. + +Clone the fork to your local machine: + +```bash +git clone git@github.com//opa.git opa +cd opa +git remote add upstream https://github.com/open-policy-agent/opa.git +``` + +Create a branch for your changes. + +```bash +git checkout -b somefeature +``` + +### Developing your change + +Develop your changes and regularly update your local branch against upstream, +for example by rebasing: + +```bash +git fetch upstream +git rebase upstream/main +``` + +> Be sure to run `make check` before submitting your pull request. You +> may need to run `go fmt` on your code to make it comply with standard Go +> style. +> For YAML files, you may need to run the `yamllint` tool on the +> `test/cases/testdata` folder to make sure any new tests are well-formatted. +> If you have Docker available, you can run `make check-yaml-tests` to +> run `yamllint` on the tests without installing any Python dependencies. + +### Submission + +Commit changes and push to your fork. + +```bash +git commit -s +git push origin somefeature +``` + +> Make sure to use a [good commit message](./contrib-code/#commit-messages). + +Now, submit a Pull Request from your fork. +See the official [GitHub Documentation](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork) +for instructions to create the request. + +> Hint: You should be prompted to with a "Compare and Pull Request" button +> that mentions your new branch on [https://github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) + +Once your Pull Request has been reviewed and signed off please squash your +commits. If you have a specific reason to leave multiple commits in the +Pull Request, please mention it in the discussion. + +> If you are not familiar with squashing commits, see [the following blog post for a good overview](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html). + +## Benchmarks + +Several packages in this repository implement benchmark tests. To execute the +benchmarks you can run `make perf` in the top-level directory. We use the Go +benchmarking framework for all benchmarks. + +## Dependencies + +OPA is a Go module [https://github.com/golang/go/wiki/Modules](https://github.com/golang/go/wiki/Modules) +and dependencies are tracked with the standard [go.mod](https://github.com/open-policy-agent/opa/blob/main/go.mod) file. + +We also keep a full copy of the dependencies in the [vendor](https://github.com/open-policy-agent/opa/tree/main/vendor) +directory. All `go` commands from the [Makefile](https://github.com/open-policy-agent/opa/blob/main/Makefile) will enable +module mode by setting `GO111MODULE=on GOFLAGS=-mod=vendor` which will also +force using the `vendor` directory. + +To update a dependency ensure that `GO111MODULE` is either on, or the repository +qualifies for `auto` to enable module mode. Then simply use `go get ..` to get +the version desired. This should update the [go.mod](https://github.com/open-policy-agent/opa/blob/main/go.mod) and (potentially) +[go.sum](https://github.com/open-policy-agent/opa/blob/main/go.sum) files. After this you _MUST_ run `go mod vendor` to ensure +that the `vendor` directory is in sync. + +Example workflow for updating a dependency: + +```bash +go get -u github.com/sirupsen/logrus@v1.4.2 # Get the specified version of the package. +go mod tidy # (Somewhat optional) Prunes removed dependencies. +go mod vendor # Ensure the vendor directory is up to date. +``` + +If dependencies have been removed ensure to run `go mod tidy` to clean them up. + +### Go + +If you need to update the version of Go used to build OPA you must update these +files in the root of this repository: + +- `.go-version`- which is used by the Makefile and CI tooling. Put the exact go + version that OPA should use. + +## Refactoring and Style Fixes + +If you've found some code that you think would benefit from a refactoring — either by making it more readable or more +performant, that's great! Some things should however be considered before you submit such a change: + +- Avoid mixing bug fixes and feature PRs with refactorings or style fixes. These PRs are generally difficult to review. + Instead, split your work up in multiple, separate PRs. If a refactoring is "needed" for a feature, at least ensure to + split the two into separate commits. +- If you intend to work on a larger refactoring project, make sure to first create an issue for discussion. Sometimes + things are the way they are for a reason, even when it's not immediately obvious. +- Ensure that there are tests covering the code subject to change. + +## CI Configuration + +OPA uses Github Actions defined in the [.github/workflows](https://github.com/open-policy-agent/opa/tree/main/.github/workflows) +directory. + +### Github Action Secrets + +The following secrets are used by the Github Action workflows: + +| Name | Description | +| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| S3_RELEASE_BUCKET | AWS S3 Bucket name to upload `edge` release binaries to. Optional -- If not provided the release upload steps are skipped. | +| AWS_ACCESS_KEY_ID | AWS credentials required to upload to the configured `S3_RELEASE_BUCKET`. Optional -- If not provided the release upload steps are skipped. | +| AWS_SECRET_ACCESS_KEY | AWS credentials required to upload to the configured `S3_RELEASE_BUCKET`. Optional -- If not provided the release upload steps are skipped. | +| DOCKER_IMAGE | Full docker image name (with org) to tag and publish OPA images. Optional -- If not provided the image defaults to `openpolicyagent/opa`. | +| DOCKER_WASM_BUILDER_IMAGE | Full docker image name (with org) to tag and publish WASM builder images. Optional -- If not provided the image defaults to `openpolicyagent/opa-wasm-builder`. | +| DOCKER_USER | Docker username for uploading release images. Will be used with `docker login`. Optional -- If not provided the image push steps are skipped. | +| DOCKER_PASSWORD | Docker password or API token for the configured `DOCKER_USER`. Will be used with `docker login`. Optional -- If not provided the image push steps are skipped. | +| SLACK_NOTIFICATION_WEBHOOK | Slack webhook for sending notifications. Optional -- If not provided the notification steps are skipped. | +| TELEMETRY_URL | URL to inject at build-time for OPA version reporting. Optional -- If not provided the default value in OPA's source is used. | +| NETLIFY_BUILD_HOOK_URL | URL to trigger Netlify (openpolicyagent.org) deploys after release. Optional -- If not provided the Netlify steps are skipped. | + +### Periodic Workflows + +Some of the Github Action workflows are triggered on a schedule, and not included in the +post-merge, pull-request, etc actions. These are reserved for time consuming or potentially +non-deterministic jobs (race detection tests, fuzzing, etc). + +Below is a list of workflows and links to their status: + +| Workflow | Description | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | +| [![Nightly](https://github.com/open-policy-agent/opa/workflows/Nightly/badge.svg?branch=main)](https://github.com/open-policy-agent/opa/actions?query=workflow%3A"Nightly") | Runs once per day at 8:00 UTC. | diff --git a/third_party/opa/docs/docs/contrib-docs.md b/third_party/opa/docs/docs/contrib-docs.md new file mode 100644 index 000000000000..57a3566aae6b --- /dev/null +++ b/third_party/opa/docs/docs/contrib-docs.md @@ -0,0 +1,154 @@ +--- +title: Contributing Documentation +--- + +Contributing to our documentation is one of the best ways to get started +contributing to the OPA project. The OPA documentation is often the first place +people go for help and so any improvements can be very impactful. +**Thank you in advance for contributing to our documentation!** + +## Local Development + +Those contributing to the OPA website and documentation must first install the +Node packages to run it. The following commands are run relative to the +[`docs/` directory](https://github.com/open-policy-agent/opa/tree/main/docs). + +```bash +cd docs +make install +``` + +Once these have been installed, a live updating development server can be +started by running: + +```bash +make dev +``` + +:::tip +**Note** the Docusaurus server will not restart when you change the +`docusaurus.config.js`. + +If you are working on the website code (not only the documentation content) +you might find the following command useful to reload the site when changing the +configuration files too using [entr](https://github.com/eradman/entr): + +```bash +find . -name docusaurus.config.js -o -name sidebars.js | entr -c -r make dev +``` + +::: + +You can run a local build of the website using the `build`. This will also show +a summary of any broken links or anchors. + +```bash +make build +``` + +This will build a version of the site as it is deployed to the Netlify CDN. + +## `docs` Directory Structure + +- `docs/` contains the main documentation content. +- `src/` is where custom pages and components are defined. In addition, some + dynamically processed content such as that for the `/ecosystem/*` pages is + managed here. + - `pages` contains the designs and assets used for custom pages not under + `/docs/`. + - `lib` contains shared Javascript functions and sidebar configuration. + - `theme` contains customizations to the Docusaurus theme components. + - `data/cli.json` is automatically updated from the make task + `generate-cli-docs`. +- `functions/` is used loaded by Netlify to run a number of edge functions for + interactive purposes or complex redirects. + +## Documentation File Format + +Documentation is written primarily as Markdown. However, the use of React +components is available too. A number of components are used to provide more +advanced features such as: + +- Interactive Rego examples +- Two column layouts +- Linking to relevant pages in the `/ecosystem` section +- Mermaid diagrams containing asset references + +Generally, this is not required, but feel free to ask in PRs or in Slack if you +have questions. `src/theme/MDXComponents.js` contains a list of components that +are available for use in any Markdown file. Additional components can be +imported if needed on a one off basis. + +## Creating a Local Branch + +To get started, fork the OPA repository and create a local branch for your Docs changes. +Check out the [Development Guide](./contrib-development/#fork-clone-create-a-branch) +if you need some help setting this up. + +## Updating Existing Documentation + +Navigate to the +[docs](https://github.com/open-policy-agent/opa/blob/main/docs/docs) +folder in your local environment. Each top level item in the documentation nav +will have an associated markdown file in the documentation folder. Locate the +file you wish to update and confirm the title in the YAML frontmatter matches. Once +you've located the correct page, edit the markdown page as necessary. + +## Adding New Pages + +In the case where you want to add a topic that doesn't fit nicely into any of +the existing pages, it may make sense to add a new page. Create a markdown file +in the content folder and add the appropriate YAML frontmatter heading. Aside +from the title, you may wish to set `sidebar_position`. + +You may also wish to update `src/lib/sidebars.js` to place your new page in the +correct location. + +## Testing your changes + +Once you have made your updates, the next step is to test that they look as +expected. To test your changes, you can run `make dev`. Note, broken links will +only be checked when Docusaurus builds the site, so you will need to run make +build to check for broken links if needed. This will be done when generating the +preview site anyway. + +## Submitting a Pull Request + +Once you've tested your changes and you're happy with how they look, commit them +to your branch and open a pull request. If this is your first time opening a +pull request with the OPA repository, check out the +[Contributing Guide](./contributing). +Once your PR has been received a Netlify preview will be automatically created, +check the PR for a unique link. + +## Having trouble? + +Reach out in the +[#contributors](https://openpolicyagent.slack.com/archives/C02L1TLPN59) +channel on the [OPA Slack](https://slack.openpolicyagent.org/) to ask for help. + +## OPA Ecosystem Additions + +The [OPA Ecosystem](/ecosystem/) is a showcase of projects that are built with +or integrated with OPA. If you have a project that you would like to showcase, +please open a PR with two files: + +- A markdown file in [docs/src/data/ecosystem/entries](https://github.com/open-policy-agent/opa/blob/main/docs/src/data/ecosystem/entries) +- An icon file in [docs/static/img/ecosystem-entry-logos](https://github.com/open-policy-agent/opa/blob/main/docs/static/img/ecosystem-entry-logos) + +Both files should have the same 'id', e.g. if your project is called `foobar`, +then the markdown file should be named `foobar.md` and the icon file +is named `foobar.{png|svg}`. + +You will need to restart the Docusaurus dev server to see the changes if +previewing locally. + +## Sub-project Documentation + +Documentation for the OPA sub-projects each have their own home. Check out their +documentation sites to see how to contribute. + +- [Gatekeeper](https://open-policy-agent.github.io/gatekeeper/website/docs/) + docs for Kubernetes Admission Control +- [Conftest](https://www.conftest.dev/) + docs for validating your structured configuration data, like YAML and HCL files diff --git a/third_party/opa/docs/docs/contributing.md b/third_party/opa/docs/docs/contributing.md new file mode 100644 index 000000000000..44d398722473 --- /dev/null +++ b/third_party/opa/docs/docs/contributing.md @@ -0,0 +1,75 @@ +--- +title: Contributing to OPA +sidebar_label: Introduction +--- + +Thanks for your interest in contributing to the Open Policy Agent project! +There are all sorts of ways to get involved, whether you are a user of OPA or +interested in contributing. + +:::info +Most of the discussions about OPA take place on Slack, if you haven't already, +you can [sign up here](https://slack.openpolicyagent.org/). +::: + +## I'd like to help OPA users + +Most users ask in the [#help](https://openpolicyagent.slack.com/archives/CBR63TK2A) +channel in Slack, hang out in there and see if there are any questions you +can help with. + +You might also want to keep an eye on the +[OPA Discussion Board](https://github.com/orgs/open-policy-agent/discussions) or +on the [`#rego`](https://stackoverflow.com/questions/tagged/rego) tag on Stack +Overflow. + +## I'd like to contribute code + +If you have found a bug and would like to work on a fix, **we always encourage you +file a [GitHub Issue](https://github.com/open-policy-agent/opa/issues)** to talk +about the problem and the solution you have in mind. This allows you to get +feedback from maintainers before committing time to something that might already +have a solution or might not be the right approach. + +If you have an idea for a new feature, we also **request that you file an +issue** to discuss it first. This again allows you to get feedback from +the maintainer team and the community before you start working on it. + +If you want to chat to the maintainers before opening an issue or about anything +else, head over to +[#contributors](https://openpolicyagent.slack.com/archives/C02L1TLPN59) in +Slack. + +If you are ready to start contributing code, please see our +[Contributing Code](./contrib-code/) guide for pointers on how to get +started. Please note we have some restrictions around the use of AI tooling +which are documented here. + +## I'd like to help improve the documentation + +Great! Please see our [Contributing Documentation](./contrib-docs) guide for +more details. + +## I have an OPA project or talk I'd like to share + +Awesome! For OPA-based projects, we have our [Ecosystem page](/ecosystem/). +This is a great place to showcase your project and how it uses OPA. + +You can create a markdown file in: +[`docs/src/data/ecosystem/entries`](https://github.com/open-policy-agent/opa/tree/main/docs/src/data/ecosystem/entries) +and add an icon in +[`/docs/static/img/ecosystem-entry-logos`](https://github.com/open-policy-agent/opa/tree/main/docs/static/img/ecosystem-entry-logos) +to have your project listed on the ecosystem page. + +If you have a talk or blog you'd like to share please feel free to post in: + +- [#ecosystem](https://openpolicyagent.slack.com/archives/C02J6LBL6GH) in Slack. +- [Ecosystem Discussions](https://github.com/orgs/open-policy-agent/discussions/categories/ecosystem) on GitHub. + +## I'm interested in something else... + +Sounds interesting, we'd love to hear all about it, +[sign up for our Slack](https://slack.openpolicyagent.org/) and +drop a message in the +[#contributors](https://openpolicyagent.slack.com/archives/C02L1TLPN59) +channel. diff --git a/third_party/opa/docs/docs/debugging/debugging-dap.gif b/third_party/opa/docs/docs/debugging/debugging-dap.gif new file mode 100644 index 0000000000000000000000000000000000000000..d89126898b6ec441b39a92e5008125c56b8d255e GIT binary patch literal 823913 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

^$J?H$4$OBx42qGA zQHxwjKJeg6QAEbi+V=?jxfUG1bz>~TGa7mLaT1s@qTl>J(7L$A>)AR;ad=VR<%np))pYykD||4`8z&n8@RTZ=Odo_52ft53K{cufB?HzI1*%Lc^Ex+DqTP z7kVRn@R{h@$V)DNjIpa|@QZ_x}3L)87i-&pfg(OudSBl@OchXL~V6d!Ili3QBPQj2izKf%SS!+fL9MrTl<2jMH+^%0GOQ)uV8W z=r;wX%OCgKW1v!LI_Bm|)e@qv+O|@NDTlC6YH~^F-5HD5q84KU!g*UmGW;)dKYx8h z_V8F~56Rh{SMSJ;whw3t10ePl0uM|6HC;bh6eXEo^_W3yW}ZQ>Q3cay2ei98N{ZHk zHPT;=pXlzqi2fRWF#YvRrs37lctib^+30Qh0Ct;^FUD>XrHX~0zVDZsiY+qrHPDxbarf>%`gGTBpOQPu^FXL~LF= z`Ed0(_P^P&%wJs&&u$m}Sj9%B$GiXhWj4-WigNc{i{1>`xiU2RvC)W2#W~q~rAYYe z`!7t^9pt?z*`gcGB4%3*sqJ2fbPFBxWQ$=s`#rntGgz)$YY=Wh&dyAc2ddz0Egbp( ztmZ#}5nJ?~0Dk6W9yWmp%#OuY(s$3u5e2r$U==>Fs}#E&;#WG4c+Z7;eh24s;J?Yp z2rkT@jQ&Hy_;XQz$j|~hmWo4mhWMT1z>BHy-_(R+4z!;Oi{QZR?x3P$AxFPMjl4+Upjz1l9H7T}{3yqjZ{GlV#xv)OIy8{sw8V@Vx zz^`*r`5b8ROYHG^L^Kci&5JT03g3!_=fz@Aj=;tM*u#9LQeJK}4*8aoQ%u30A|c;% zVJoF-=0~yQQa}$6EAl(Dd^u>K6nW7>@I29O=0em(M856|?0H^PE;YY(S$u$E|9mL_ z`f~nFL_vq@Zb7GG!EK}HJEzbY35H?ww6|Q*GCp{+8C!E3^<B~74@kC`3HYyn+TDoV9R-c832UL#UM!V6^dK=FSiwP7_$`{O+oeHAfDc+ zZ7#~6o{-0dZQ>yB*^-K%(0zOXJ$7Ce6|L%2R*XY7069fLH_WWV;s~( zh>Czg=o%mWn}qJ8=kjroK0Z1EfL|x7Ji^n6d*9NMyrP+10g@h<_opivl%`YBkMHHJ2i4{xp*z z73n*4mQ`$7IxAxygA$|a02UGUm%0Tni;i(CIwRX~xAEoTGwpo~3iAQ^DRy%82k z!Y&J7Bj?1?}P

{0^H!q$Jwadl$jEY!XD> z`ZDHDt5uFNB#zF|@KlMUQ%Ogy!KaV~xI!5v_!E4H`B%C6S*6`sb?{m3hqLUxj z?PvTz6HCD^7k*-FuN`$w%)H&t#6c&HeBx1~pTY9i`-y@kQQ;JGDuE*R%e|Set@fwL zI!)%jxZ`sBy+Y8g$LSL|67?#Igk>^@RqnANQ8nVP;ig#;vD%~~{qB}|sZEpb#qr&3 z%TLSQK@IxmJL6@h+20~qEq1NzeRgI$-dX&%{T6hw8M|sUrxqM>F)@`=TdEddw6t*d z9M0Krw^+_?GTG|D`Fp17bG1M1dqX0`!)}E~{Hy5iEh16E`^-Z325an3wK-{Thr8kLK`y-ztzX9sW+N z`@1D!LFmr=$*lN0(iN1t6Ryvs^@Tp1Q}qgci7A&JFyGR8ec*{e8USMoAFEvdp?!UT z(0fNh_2ANN?c1?t?j7;V&Bkao>Wj7^4HY6-##jRyRGjL5lqNu=)<*oyL)<%mb@M~8 zZ+LG<;CpwcSl(_|QBJm{-mW_V`&QU0ffCB^@+)3#lu5z(ZWlP)#h|d2mxAH#8{hROEwy}V#jNr^CrO#I?o#q4vzfovFTL2i ze)qGDY{mTnrWLZskCZ#0`sq{}pj)0H1AS_jWEwCjAaaOJ7Rp~c|9f1;sOeAlD>$nM zEJDHlmB1AhH(H`o|)2H0X1HEH_=c$9BNF1+Gk%*nEE?^M zs$LtGeCIspmRofs?Hr9=o~kGqIodJwwM|<03Ne4XzU(_&zK%*x)mh^!FyT(9z?sV> z&OGkp=y2$IDwb;T12U}n8ceGe(5i~hjFI2mb@3k^U$)FM!Ff^hoOhK@pdmv0shlJj1lcuS zBoL#}LB){}?j*wvy5P~0pvwvY8*t12kOXmt0&DfVQA8>md=_neRyaDto?Zi%0Vmm` z6v}7`@0HeKob)U)NuB33E)1uy9gIMN9ay9R)~@w~q#~(22;w@4PkgH%n$fs#jNTF4Orb1xx@a$nct8z#W{)?3+(5L~rac{!)b5~YEk z4r_82*t0|vVp0;$e#0`-q8tCpK~Ni6k0}`t!~hc3N$HV<&+Lo*-oXmjfp!v94Gh=rs=OusaBip#FI8g85D`$ z=XIT=&}e%d(pc)l7D{%78&||iEX-9iNS>%1L6+ebS|3}?00(G_3I2=>HuxBvf&tKD zP@(P!5@227jV~~bX6xlvkaz-w{_-gqi{Cu0QC+yO_z>TSfdhTWA%krFJj^7^m@Lju zhc6UOAt4;XLM-bTylP`GR9K7*7ipJqF4M57%tl0HVYNX5VF{sz@k6jF9is_vq(ah?u`V%t%sE*YagM!smvIu$VHnudZZog{ ze23Xwa6&!MkK6{GOkvK1gFSGMW*A?5lpwi2USIj@?y@Ec%{D0mttXkd6F-Rr8ds3N<`p@Rv9F>dd)}6Q<^+v6wDd#a>T#AdfB9Mz7Q=n4KjNl z#i3>l2??Imb^h3TQ|?Y%sJ=Ib%oH8U?>NZ!I2{<|oNCS28S!05u#L?*0VT_p3MDzW zzj9(AT)dt!_hdprMv5BZs|z5wE(pPv(5adR3fj~q*KS5; z@yg`ffqv26oHJQ6{ulxx*Yj!D2T4)iJkK$~vUV7Kc&bhJ!7!iu{mp<6Kke{V*|E8n z+4RVwS8g#HRDl>y4>_W~g|!C&(WG#LMaGko&kjq@gvanJGB>pQzVOw&>YHDb#3~ea zB^R3UIgd0t%hkN_%lr(v%dSB+_}78O#Sh||bMsTp3D31EV=y@GY07S_I~%)X+j=>f zHJ{sobIJw^s!)_>n}7$s0E8FAOupPwnWw$whf?E2VehpANa_YL5U;0X4CpKdM+`<< z#ke{45(Fdr&iLr|&qX&d7b1@7D=neqmB?xdLKwN^{5~->=@ka=!W041c|dBCdPv>W zDi}e9Gxj3gbd-)=GX zkaVUSgRA2}V&dD9(dt1`DCji)y0O+Z+V?sXtC`gU%RrJ_we3RX#u#f)$io1n#+L0G zNIYO-8RQ`9??njXmnnd^yF$}BSDn+xrdQ>O32kx=F3NHJoLz!ex141d#d7Pf#kO=6 zEOY1d9EOle6Iuo>(V>Ovz^iDcg})#8hfjKslPpU^>M;g$4WUUdZcNG?GZ=ku9SQWV z1w-D`hTRjK7vk&=qe$k$@(wR@g2r9EBCox3quZdl-dPcl!b{E<^MGyHmXzn&C{&ry zLat^O%0ey`b$z_`itv@iGCY6!;tb6a0@GwI6BH9UUm`>(cKJUKcY52}uh6 zy?#jiN_(u@wBBXhDFuG;-GKn&)UQr+vs~y-P3=U)`?$!SWW$F&pM*~3zo?twm)k#6 zLUcZWz`dufw7Kx={$)-^@24+Z?eDO?#DjzYACnimb%b5wo?RZ#5sAT3G%v_$pMkYK z!8c!Uuxq5HhNLlk&1*T~Ye;1dz~dZ^Q%bk$`_ZA)7_iHOH_lcOl*5$QCMglQ#rz8~ zh+JCQ44LmRaXvxtW1*D0m~Z7S`}-}~^Vu!LD4UdF6Vwz7oi zD~R>bjqyu`xO;L0*3voFM?c4FHK1bxF^~@v^oD4X`Nft7H!9H<+PU>Nn(I7Ecq*au zSiw_f-Ntv?G3{M-?LsE8GQBY^v;d5dq9omo-N!rMzh)XTc`+6HeK*mRzK($=QbvG3P*-i{TP0-+?s)=t`u z4qHvyJ9iLTe=C~RTp>^W$Yo+Wp7M5i#W3Yj&~#$RuP}ygOi(vZCN~V^Mea@C1Z+S| z*Yk|7pzyA|lgMDlsNiLYFH?6Y9n7ulW8p%mgI@yJ@Jx}i5@*~&Ggc|Z+dk1JZHm2 zs_4Cr>B-X8%N)L$5p&bY5|Rss+(DC?!y@cmUllsy3UhmlDtn9DdrO9UKQH%w&d|>W zfE7+cf(zAijH7UR*Pog7RXO&(%1-Vcj7~Md*O}pivicf^X;NR%uxWJFz}^IALET{V z>~$Ru=@cs4eVvh%wSna98ZoJQl#E>cHB2$xV#(K;f ziu?l^$vusIt=kCpM94^^0V$n<{}mlpl5&iszj=6IW_e)taA58V_&ZN`dw$qJ{{*Uo ziPk>B+0R2%-TKzTzps}tEg1|nZX49c;GgTJJ@=z@U>WcO7}cKe*_`9d{GvPtDOf-C z)ui?nz#`(F#@J1A)Q3}85OAB;hL6g?$)~vIWzfq&+;bE&Fb@ihMJ2b~+M>kOc;W}o zabAL?SI7^vMEq#Q9O?WfV-Gxo=~`6bYFOhbQF_R-#8#%uqizx$KH@F$~_ z36sxHro5lbRVK_GKj~*^^bz44nPG~9VFe1|_9u`HnO{6NeklpvK5#T1U8bls?*&;i>86*^r)9o`~vD-N`TgY{gE|n{xEv`V+`n|B&vRVLehh$rBt( zkd|ICsUWh#2NNYhPbxNokd=hYFJQl+A+>;;bp$i@>|u+D5z9AsEgd6-CG*)Q-rt%3 zkz98p0D$|soDk#+nkS1;8i_vZr*KOhadH}U_A!?$4lPIkItGzDT|jC05)07KJL{nZ zC=gY)xvZ$M-q%qV*|BFg#=M(SgI>{~W1~?c6v~9KTI&i}VDt+?GoO*MpcM;mzTsLl z)aU|g(iE+F7=7O_$_r+Ju);2cjJ=E)k9;$(JcA>eltp~26{LPe+T>|m$P^KgIi5g2 zk;rF-`buh`Fj1QoaRp?(Cn&F7Myh=Rz3DmOhOuf&uxhFuuPGyS8B9p6n#k-BEcC>p z5cg^=Cz9??<~iN7ioNDl9Bq2H6M(AI-ls{5#xr&6|74(r9x^` zahZSa#jhlq2u{7Ep!@AHS(i6e?~_M+VFB@>bYd9@j7+{|g)?CuS6QZT7o*(C9T1VV ztc!#u@8Okhus1Yd%iMv`DczeaA$yY9>_mWQk~l8VFxmwCv`?sZ}dU1^RSmUi063# zD&sDg5!vWG)#^O;(L8*Xg2NSULApR|JO9(i0>b~`n)3pKoSkZInAc~lS)Ku7^@Ec? z;lbStkZ;MHsTQ@UAFK?E#{=P{rC|lNKTO{wgES`-~! z6kA;se{bjTXHk-2Ns50-T5jpO&Jt1P{qX+XB{^RQ)Wn?p+a<;PC8g>mWh{AV$CB#m zQg2eY-TD%WVfk*)a`SK0tWu5#I2C_u##cM}^!_8_(lG#3k*;{0eM#KJS zQPqC63cLEjWmCD8J31?7<|}udSIm7^EU+t^;z^iUkuNa@x4O8892Ien0I{yeVp0mc3<*c1SRlANd;&rv&qgYxMX>Wo~(oDE086914)&!7xeJa5le~t}A}*CC+-> zP4;jl^6@55?4Sm)pBNLG^)1wL9CYPT5;CLdKSl6GZW7xzzKTyUhd=~in^%#GDhhPF z_J8w`s)WeLSt@d4uYcq+ih%34Y zC;$x>Fb4CU{P?!gR*+>H?Yu3Uv-*SD%6TwMeK0J`94se@?GjtA9jA%F?u@_PnaJOn z#ID?DaE<&PAKcwcoITo^gY3>T>@M){F3RmL>Fh3>@2)uSuKMn-VRzTx?r!8iUg1xs zaiUS5S#4#q9Hv~Y!;?yPhdD|9D7sud(D{96{`-&f?<3#e$JpN|Z-1ZW|NdM3`>gZ# z`RMP9m%l?Exg26$Z2?$2B!NVp0J=#4TM$TH2$1IlD2@P2A&?ai$ZH4`T?EQ80@WIU z>;n@a*)*X0exqN684PC+Bem*=;;MHTp6@Z@_LvHsutzJ?SofcA$J-+f98HHC6kY|d zM_ikE$}F(YEx*qb|A^(s^7GEUHL<~Gg&X|DOxELQkI3sHYmW{q_s@&5;$I-*zrDm6 zp9v8&>G_`#pZRfU0YlbEMahR=lbyhQ0GTbxBBek zweumIt|v+GI7os3mGT7L_yTmUC1tg&qxXx@;{?hOaJq$|Ufo#XNN^BrQ9YjNx`0pQ zABeMLxDNsA9tpi`hjSmq8AM|2EP>>LI7(Dl&^iD#4suQiqZtP%ct%j^hA2e>R0ly6 zf|NGLI0a>&n7vKc=jM;Hpc1vAK`>+P@vxvsZ15nKDm9F59H21{u!fP+5kl!Brv)*< z;H+>Mo-|l@Pc;i5K_HIt6N5pa^o(CX)*gHhqs;~1_;s8$)!=nGJ5 z5SqO97m@A%N@Jjz*z6h!Z8oxuJc7);rE^B?J*3*}BH+AIr$av5v^FcMOmp-T)z!() zDNGV-U46a>-;m?KSYtaJf+zp5xESrS8-0E;{@f0eaxsaUGpV_l9=jmUtX%{bT+Bi* z=NT^-QljSsE|+dzE?Zo#xLgLwUasOU*HbQ=uooLOms?$z+AlA+*DkF-U+zMQ1V-YX z0C8WQcu+9>`zG-Z=gmJZ#A6q&V;u3cfcW>h-DwT+e2jPjpWT5*29rSlAHZLp?gZ|e zNuqB1yuHc77Udor`+WVW(k{PB+z$9}K2dnSzrArFFqox@qZL!SQ}8oaKSlb{=AqDV zp=p8XXZJtCqb2uhR+l&bh>VxncO{8EJQAI(bRR2!v~?slUF)@mUC}ufpKbI%-rw0e zz5-2!La&K?oJcMb+oKq-Ki+2As_9A+xKrwJD!tmDF8^$I`}F#HzOL45@kf8X+kX~b z2@?Cgs;%*I7iFyBncVJl{quv}oilmDe5*)6h3dJ&{&H{1^(WK{3w#Zz>` zZ6hvR@IfT|yn;GLadX{FhB%lLtL?j^z?;Z+_YQ9owkMVMwPBHV)@!E?#yp=}rZ4#3 zGSjE@9%EkEclGFVnFFkwDPCEvRKu~ zy#TT53HJuEni>ByvD$?wW%0Tx?ssF-ls6##(!I53;*EQ~$`Va~CIckCoo+TrG++EZ zlfVNhR3uwSS*IneAn!$fDpGxR5N)Tqr6Scqe?L&Fllgajnf0l0I8`@SG@F;q&qxpH z?#b|y{chrTa>jo5&$8|E9wNGJzh7*t@%j(dv-9hN8WgHBKX0%G$^6n2X{!9m)|VHL z`sAJ)uf76Xj#V=caEKp~e{4CZ2DJYSpD>>~_@i!?a%Vt^&pxPd=xGC=kJgI8sgaz` z8oLpHjHH8++(@`r|KumZ10%)8c6<3*-|Mt`DkFZh`a;Pi_j<*)93A2Xu!1>1BuYx! zVg!DAi^*@k`1v_Oe#GpW;_Q*+P@$@}aajxG>q!m{_~4{1nl!C_$%tO?q)l*9(Yp*t zS)PRsA!Ya5j|Pd1I~dW^cqafQd_2nF;-BW=g;45+k*kbCnHpjFQpCAKzBLqDME`9j1>&msf0WVpIJ5~u2& zMaDzu+~^l*t;yZun)Jhox+jnaGlfEX6mH6#o8TxLm)I@|I8kYIBxSNab4vhDy%OE% z>Y>GYhzcW>PQX4=@FczTy9xc3Kq3cV^oNF3Kt_gWe6GG*E^=ir!KyK_wK@31R zfCP10U@W@fG$$akE08NMH-enk7)jcitOYs$8fv%rIq7?UpZrH)`x93!<()w+@)scH zxi$&YnXwLUlm?mBwgy9^WH^#CG3E*3DK&Xum$EGlxxe2c{h7i|UjMx~^;{sMLvFir z@`Ffwne8-4)2;dLv3}N09@eu#YsI2!IF|Z1*1WG%>2YD*zy=TNU#2-V7XP->4hPN` z#p#;xBGX22Da+rdLbKMJN-5QJ`BGIv`w7SJ^!nTCid7r>VZy`Nv{i*Vs=}tPi-z;0 zt3KTh5;n^b9{FTiRb<{IY+g|`^4Y7Z*!Du$vQ2pOYf@E-v#Q9wp`y{Ma$?oz$3Y_2 zOTuGyzp6@on?!66ipHAutG>Lv5J6x4m0D=4uYkXz_ScHXJEW_>#s!Hw@`z0Im{ym+ zZ4!09UOX}2RbBD^Ley1DWb$WHb!EP)nERdL$&vEvs?s1a4+oK{iC@*#)lFiLpA}Ed z>{r)(yAbmX6PaG1t*Px)7592wJiQ`aQ};bc+&hPr$wIg03ivDTTTwi-<5klzdm--E zCNjI1RMWVsD&ap=Jo~4-rfD}wB5+A$?(|p9x1%PBmj}gj7yC8MmlqNkkmx+{S}h); zCK)PaYOKIp+d?g;2jw{DSJbU-b zhKa`B>4g9Y2J}=OPudnXG*!F&Am6W3wuhRFpmom9LVjFsQSbmw9V@0i7qRh%# z>6>dIIW_$WEc!Xa`ENU=vz~6=DfS;JeJNY=FW~P`{b=mT$G%<4mW7FJ zKiFbn=|#$w8^*>uUT>KA{!*?oM{EZz0b;@n>J&f7Ob~)OBBFGOIH^{#-KVb`rdQSE z>xVw?`cyQ`?7oz5ToU{JVyI#E=wHC!&%c8X8s;u90azJl|_yvB#Df+49mJo# zjSgIutMMCnQhJbi(72{Xe9`mlu~#BR(;BD5-wwtk0IiJm;(2NW`xTVV&zPNKzgB6q zqSU9jFKlbS5IWWr=i^tXwSDi0zv^a7eASnWEjJ_Oi8=A-RR>F(&%WFo`~C9x%fZsl zzGcH$564Nx!=_zlf%8$CFDJzo7uz__vqe_oNyi5tZlNx?qVkEYz9rSueD<}GgD==vp_^1M9i!x&QH+KlaAWXvLnx` zkQY|-7ipaHA(`|nNTzx;Q&cv2_1ySZc19L1ifSZv9GY5g9_){%W?2AnBhUWKAa=7j zAY7bB3)5npSHys`2oADUByH;qo9P0LZ?;PNgBd*}&+P@$$vLn;*El;ji4u}OYMyq} zma8pkCg2?gCCS-QUF0sraBgd=t9%^E`>?N=eer1y^vzx#1%&S6sa0Cfcd}Fj z*~wH|+quFdEAc$r0O5X3<`bZ_8Iru-Zs!j%Q`uKbr5Y`NGQUI5trSQ?cLF3IT@V}P zq4R7J?I+>f2B`FAvL}ZOi*b>?424xL)DV=BlN;8!;fxFgZBnzdb7`}D)VVtsOQP)#^0_lczM0U`oRJ$d#kNHSrgE#6d?35DT-MmnY)(1n3)02? zi8)F8T<+C5Ew2``s66GjK;iz}$xlQrJGnfSKoUk&ivSWNeZC@EyI?+aUYlXmz0 zovi+5>U>0+PXeFX=PP>wUC1BEtMCdBJ~+s9^0m#i^J;NRpYu-QXM|}Hg7U4T-%qkH zau&6zg`!9e@Zduit)j&dFE61w%KwQ%)Q`apVW|-bfWKF7Da`yCy>CH z003=(ls02vNZY4O76pe}ML@d37FZ~|v<`Bfi%0qdsJ6N06$OA1TEw-prhvS3 zl_9W`7Qs-iGue-JYML^;#3gEPq)L_+v)M9rf~EjBNY6_HlRqP#+yd-J>HFqRPgF!Ts*8FNnqMS zZ{G8#U2c#vILsw}GAqwg*$yQy1PIY>ii>6&3k%a1J&?z&id`uyC0ER;ssvm9OTs~z;5eUb}O|gijknDUWq434iVhuQy0ve(r5v1JR0_w^H*iXPd7CiLV9O7C5{Yany0mSqTd7eRh``Va^X1`BGfAcFFax|OOqebb zK*zi070Pd1n%O*AIQgi^=HiXOe~$g{3P~jjt7Ba$ClWu}7Ez6- zEX4DmSJX<;#SGlj7fWy77l|by(?&tkg#b?2Jh&gg$@CC`=C?c&`4$RLYPZ|p`$S&m zs+zk&SqW(2Ku>bDQAgq_nF?wAMP1!RI?{`civV=nBuWMta?7??`a-Oeysf^#mcKyV zq}H*#d>A-n{M*B>qfGL7;pqB}i9OKs+h|fY06+m?xy!F8XEP=zB8_lqwOxK4itqom z%^gVGq0C*?i~_vdZ3T;>uy0EypXRV&9MlT|jFelPzG8HqB6Qy3Ldh*#t)j}B?#`Dp zPJe-P2sFia5|o$5a1k$w^9FUH#<&^@T_pP!keOvw;Uw7fwWcWXPpY_OXbT`s4x?!VBx&)-bfhbc! z5hNBcx&#D8rIb_=5fG(2R8j#EDUnhKA|fEW_xs-Y+{b-9f55fv*mhjob)MJf^#-IQ z{vw7MugF-r)xCNA_?CI#P%TDa8k8)4@!Ah!C~+&>yx#X9V%gLGbnYw5^bduUM5ZBm zKvl&0RYIC)uSu3TXCea+6;RD2%Tvuz{eyT6l?8BotNzd{9;wvC)Qc}9K<{)nwB6UO z?)LFC;oHIJHpt5=D7!bWNqh7vaZ0Ot({P1_00polya@xe$1vl&fhi2K`r*t$%GL1| z!w>2LA72cb49?G6f|J5N{;uJtuG;-*knvlRuB?l>%u{ePXq1eA61H9X0FDd z?0-s7cf*_{;R-!Uym+nz;|*xH1u>COiycwAVkWFY_x6^FgUD@`*-?Bz&GAe5RvDoi`0;f@;|dd(uPrDUd#wjPEH`PgW8iVwEtsUA4nVqi0DKJi`>q^w z!o{@7GJR9oVy15t&CqMY5!^}@BBsxwg=aVu0~tpezCR6uJXjMSTQi!hw|;kC>+q*m zA-3=7hOyffz8qj0`?bldgpcLKk6YCu>Jn7r?(aRuGn#>gS*o}ITxdC{`02*S+6{@w zsI1AIz1mO#-v@Ge=j}seJzoD{`vriLcPzebXelYfKZJFOHyIlB5Da@XwF32oujuc6 zE*mjX+eULLR6U9h>f55d&@(cI;7WDV_OiA_3u*~)g`a8kx6t$@?!~Iqx+EvpMGX1ble7}n z*mjv2tq}Gjk+Iy^m&8E5{v(ZTPxBW+$m7q&SUjimOCk7kG;QTv52)A<_l;dz#3*7q zzEU$?uK1nG+q;{hb=X+qGm&aS&EuudfbZhoYkBc&Y^5Q5(W+LLrQEM}g9S0r4as@J zea{y-`Pkx3M2vAHa!y=28z`Z3x8hS7iK_)8pdeGwOl9*RWk1~0 z-Fl%VQ8#~9N~mSE=sGqGx{>TGy9-^K-@5t*@IpgU;h|wrVL4H@R~tvHI%}~!6Gh@b z#6l7;36wmZU;bfaUZdRc1^#T)B?0(@80fV6*~o6UX}tjtjJW@+nVf|ZlRgTV|7~KUL}UXgYqVgB z`D<$|Hs@z9+l=h6L=uL8^(9x;^-jgcAIMUde^gQY=0i32<9b-eb|ftCdmjIm`6Oj{ z@M!+sPU$^*@YA^Eqw)Q_#u1p9R~T_^P^?O)$+}&hnyg?8|Fn+SM-m!+?OWDPpU&p| zp4?ORV_&&f`tV*E<*09lS~h`xeK8eDpfl9u?@C}_U1-fW;RX?x?N-VT_?=24I}X`| z;bD1NU?fr8@hADC&)Yj#=4f=Zr%BIG<36!oabDc_%da$WejnKNo*Dmw*D-{Z|5%Vc z1hZdyn6<}d)MIY=Gn?&cMBo}1P|+yiP@IZ&NER!YKVnM(j=WDaksx_~d*TUG+X`9f zX^ZBl=n)r8gmhibh@ks18$fs$AQJO=Q(6}z7hU^nkfiwOg@nJ?7IWc}c>OI|<&C*r zk<>z(!b%Lj%U67n^>q=98om9(B>PqV!HWC!%M9$_plM&Y6NIRRdb&#ndLM)egexlx z>-Dm(vU-v#nbi)3wLkVOwy3T38m*{sJYj13V?8fql!4~K)Hji?{(61hrdi8UxZ3yA zp(gWED!Xd$_A4g!z!z;w0koHY66EU=*Fl3ofiR1R(F zVWv4e>>ub8H+HI*K*aRBLg^!=#FYylv(8=O&6AtDL=m~@P+C3LT9E`tX1m~JUywNU ziIA_|t1Tw1Mo}9hnCAn*gVIKySw5J2gE2MQqp*4N{uiWVW#qUG%$k`WG%$v>+Rg@7 z(Kf^j<#u*}qu>1bQiAKF*1lc!^J zIg_z0&gF4~D&~D#q&CZ;mgtoa8&Hu{FsNgcFmKMNuAiS(=F6^&v(klP8jvkI^k- zdzjP1-9VVC^do!GpcAA&bjG&nmwuCB>{_SBv4nl8`fIDVSx~PcF^A4p&a(8r))P~G zqeBN!nnk36^AyI{1yrasWtzmijD9pEqcNSMChjsa3n}3Cyk%OC5xR?Y;n>cwD&_M` zEYI{H%~~7jgMJN|+L44&y_8HIq6MWthv$6~rGu+j%5hV_~TGf%D4B%Z$m$!tv0$22-SDIsNXPudEKn>r_JDIdN?5MiA^8jwR#~%@-Ls17i>a)84ErmgBd1H_TRAWL3&e-dSF;!6|~5|t_y9I-hcNcTht<8v+e_>Dh^-It4UDNpWvxn5^9BMnJ1 z>r=KdF7tJ0Q%PS_z@vI(mjI6IMhB4t_!==b`mPGk{5cso*EnCo22ql;-QMuG2`mr2 z!LHR0CQ0`#F+aL)T+C?~e-QhnLsdqD^T%k?Zw%`VZ(i)HafHw>2{G)3d(nejWP+4_ zHj}z5LUi$~;${9Jz*n`0f?Ig<9=4o&b+vyFeI?0hwag*y&{*MeZt$hZRe?|9w>jF7 zNjlTw#yfsBybq}9yn-o$=|d)ljXBIW*YY`2Te+jDIq{bRXaOuA^UT->o{@KpSL4W! zbr_XYb2G+D^?yVQ3XY7Y`nXJA$&tLXGOlvX()jD?fcr4=9g64@M){O-Jb82Rx0_i2K<9J0SU@GRWb<>u4*36Ag6rI*gi6P<$PULgxAs6iZ5ZMn&N zh9h2)1X<62uWTn6o73E-!Uc0{tK(yaePx^m{UOFuL1V;o>i3g6bql0)H>qSkv(g>m z*ArB_x3yI|d0aU6@Vp19#MZitfk;h~?SC47TEFG|x!CLTkHVJrs0+Bbd!Gi`v|9VW zD&jNc7kbP-ln-mvU*p*F-ZZ>X#a}@AxubLb+lSs89hrz5LIQfb?04R*YDC-|J^u9T zDNp-RJ5cnUB z=0AN8I9;RR{l7FC__^o*zZy+r zpQ|mOTFw9LdsO-MrU`v=oKtDUv7WZ|Z-4RhY5gFcQP}+S^Xuqe|LJ=ST%3Wry*778 z`=v~5sDj20x4QdMrP(?>GUt}Mc%HF!#C#Wd@g9_L{~v7T+5e*6PzR0k ze2QvstnV#1eggTw7Pfms*Y1XHUx3C7X!m>?kMD%Oi&Yxl0N-ZJ@AxOul+%|_r#tC= zkH5dp*IDTQ`FXeX;M0Ac{B{NO^&?n|EJ)TjP=Q!fCcTsh=Eg1I0c?`zLnViyXCvO5 zP0tw}1_!(PEecjICktNRD05q2sr-zUh-n$S@xUxn-1Gd2Nt#UM278IwJlBDkb@s^% zd-b&ix<-Q$1}_NKYnZH>9N%maN`t9{I%GqB13%}{f)0^tcI%`;q0=Q3guyY3x|rGV z9IvEttM_y!=Hi1*df%h*y7hH#uBno1KdoFI=1I+|AvG#O^Mgu+8K<%_)yUe7a=y=v z?BvT%j!1PYUAHll&d-lOnaYHPjpuWk^US8))lNkkl)GTZi|CmMDYFn&T1;*kC`pFi z_bAsS%C7(36N=1XZxX-$__C>N>WE~Gt;(e7?2gb5hSQ3JW8&r6r*yh=p|oWya6wkr z7VDt=B&M)5XW^|yj(R)(cnLGh)u&i#d+Wj@%%r!RgWvwrF2WE>;J8%xJzu=InUj%3 zr#lg4T>N}HgmJEZjk<1j;QdMODnuFYPb0#x1r%$QoFQdysK5N_#^B-HfIO%;FNJ?9 z|N2fh%eVSp?`Ycl^rT7yJ*mR1f$CR2ud(;OB#gUPvs3gS7AEc83X{0DL01i3;cX$ zmamJOy{#~QuKVEK__@y}o$Ok(mG3j3tkdaEw?bS{Yr_QBr}I>v_U+v2qK}>Bmc!Kf zW3Vxs%+1xrVS!R051gHpT6u{!)NhmmU`a-<5|>~3nf9n*S&3j?PC-kRXqCH6s#LO! z?eh8kb(HInF&FS=nL>c00co6?tQKj_b{=lRJF>#1b5hFmAMNh)8t<7g%im!|XU=KY zCf}@CR4m2y$$9RkDWx(iS+-W`>ei`K4|YYI!D}KCD}4!^jX5f}$}8AI|8rW(FkO?7x@l?Q)|laoXF|fW@~ds7=D8_P!c0ln+Kfl^z3LtjvsHm_Lyweh6x(?3?Fs&b zKSoom#sc_%6%E-wR=x8#>)Lqc=zunre8o!ktaL&Ano;;MsA` zw&AJuY!CEhL(19ufWRv=glGxj3{i77b7ZQ=NspJmP+dqukcC2s3-`rLiV=et$w2Wn@nMa@;uViKwD@V^{nAUmU$BVS!wNywZuv|1tX*O`|5WX!z`&jgedCwLN-c$UmR4uKc{#VoM)thXXRe1y_e>aDX5>K= zbt=mcGv*RCwQ(frlZkZQh3g``+OlglXa}0=Khg2YN&v zM3abdpBz8A9X4Pga$%G(KP2-^n`>>dQLP``IYXVJ1p`ny30ma;6sV7eu5PfPx+i6&6MJ%K99etJkT z%4hjBuj(eM6Vi|LTS92FluhObf6df-8~UdD#^8Q5iLX*D(|M>AKG_A!y|$e#PhDc_ z7d6)RON8k|gTjHEnhS!+zS@|2o*qr7py>U$cv7B$*3R zSN9jG;q6vjnb;%>oGAUAy6?5$qLb316b3b;Vih)P-*BCEaDUgl&ZzEczbtqxXx+_zUcy*{G_MIktDGbf3P)gx5%Sb zV`Y3!vd^0+5eIv!vMmFJGvlw2!}}xo%YU{%#BK>)iCjsx>o?R)BzZaLGVw3oU>ey?Khp<-eEM|vUL#|N2$x=9J9Z(2H^^XAMp7ukW&YG}hU=SvPJtAI9$-G%8X6;+;Y|Y$E=q^wMl?f5T zq>qGr<$*5HuWuYcr!OHt{g|Tg%-TpuJ~h$-c7`$8@7y24a|hm=CI}5ts5S#^ya5o3 zytVIOBLGB$y0M@MO|&+09}_1~9;z*a(p`7n%|YZWfo@rZ?Z5Q23ic9S_v$dib-1!w z54mv-xWUf2>?VVKsGt#sB-pyw6RD)IaxdLomZvjz=%qp0sUy^Gn zY%NOknhcUaMf`F=UeG|MHj}RK81WY1N0A<;9w`j#h`IwQU!b2aF)EEVo8oxKE8{RF zf5!};b#vk#)P4>z)&jWeTv z^KKp@K3j6PY-ED1tm&M}7~14{!+#ym_BRK~m&>k`%Xu!Bn=f}6Vgq@V8%tmn+q+dA znX~J_@hy?_%*{L*S&pxzxq@?fH&L0-^I27OZmZM#9yf1m(~AE`-$QRR4|9{}Y@iYL zT)sJ@Br-DJ`c=Mgk3KEY-j?3?NELR_DR8@4@Tw=zgWmV3;qFe645jxytk~JqQJxH_OU`$$6yJ&A<07Id&_Kj|0h1iycI>&L@*X8vk=UhYay<{C{hg_{EGDukEJ+Nv{&;P6M!S#|d`G562&IyM6z5jpo zJ&GS}%^3nhd6r*2pz=N3OMNh{^Y9?FNLUp4`{u(F9cvM^#6P~$a>;u@&(dns66oDh zIdZAZGLh-1^fJ2#Qn$>l=^KSJtFmsc+3_Goc{eEhiEN zPZWh))}mU^vA2(dd_y%QbSdm=k3f45rNXpe1fb7d?sK}Rc}tkI9NdWkO2b3_L?PgA z!(e`HZHFq5KXY6|6%EZ4=O@W<4ii1X9n?L4pTNZ-D;;R)f^L^ zz$>J``0^YijMh+*_G9YUzcnXQgm3wyLiP|2i z|Ht1Tgk@ZeK~B->O1TE6bYvN3UC^`dMS=ZV*tzUA=Go2-LW+)Hs#W>>?LTVF&c@ zn!=9Dftr^(bCv=W>Y5a+8{=BpCDCGShfJ1ev?g!*U(-gni-v0&6vI)}3R0abQr5p* z$f8yvl-WR}T7nC8#(}32)(B&WMJ_Tt3;u-qi)jWq7Vvko&7#>-@3mH0BkADLurtX7 z{DeQZbvzOAwhImcrhvp$5~+xOh8wa1EmC5yq@OdQ2yI{j9i&0s6-Cw`Bc2GLo_Qf( z-s8H9eto^H?Op@p-JI7yy1+nD2H0MW%0|ARJf>e8xwt2V(O`e2$NuE_H822Gx55H*4=i`fiY+4G5^)N8!~Y;hkB`WIEdiY*jmAYZDYx0nLkFn+-~P7 zd(DYQUh4q|prt{mc8>1XN1r%5?sakGzJ_HW3m9HYEx&I3gc42!Sc6`-*D*q;dUgq#p(M}fmip|biKloP~z(XJd@BZ69D#H=FF~FFf0sAMckqP{7is&sR)VN zZPssUX`)OZ6;`1Bkf7LI0eV|1r;3IIzwc^4tB+I^MXDttE5?Qsa{vgHQCb5C_?&6- z1hGC2`Fw)t76n?Iv`y1LZBZocqz^16`vf=&^auYz_Iy?8xoa934 z{brfeshDvKxuA#`j;ZXWY7otY%=Ye$yV8 zG0b|5@b;sWys1?sz6V`_dSxQlMXP9j(_nvARby6KDf9U!jI+18+8M@x-C*giUW*c# z@DkveEwV8~IBTn>!l9M#;>$j7)MMuU!@7^4@ILTA1UrNB?QsV3(ns(zaHyV+?gDAv zud776+CR0WDIl{%-xAQ$4oe?^DonEq$myI9j+Y?aIe=z7QVlr+QUOhk!>R?qRcKc0 zF=UnkvS1w0@eiSfnPE}sqvb51mWSGqEZ7F7mq!R|Q6>s4dCpW&Ql4L_#DVs&^3>QiUhE;)$3z+uzeYZe<<&IV$=eLOvU* z=ew^=+e0Sk{d-CU0iL9I>BMm`JzHXe?aua!y-Li1pHntwVEoBh)`IGmzcVtSD2RF< zOe!tPym^d}v`N39@f|mUybwittCx0%ZZ6s&{+7D?B_J0AY69)kWAy4`Hyoe-J!rro z<7f!q0Ax@$DwZzPzCV1a?Q!85r6^3W7_qcC|99WI(8rhE*sM(}x^gkk8&Z~jWm{U^ zyyQ_9d%Ura(Fixs6;g;*di~EsF~S>^e>ySZNDP~fk7|DI;hXWrOKtS0{&uPsvPwMOg(_*Ku)`g1aJI1u_JUbH9{cBl%EBBkGD&1TJ^Sf*~l$&J=o85KU zbb8G4k5zwWUxJOO3I3P=KQx+nh=5!8Ks4W+ELn19eXt7qJ@bEQG-k7QfqLz`8j6k;!GQ&5Gv$-h+pZ?6yTIb`d%eOm}K9}c((e+owt{ueh{8@WpZ}i_a8mT6Sww;?* zwEAdnE~=0zYm&N?mGq+~CmY$LWv8^Ei6&?JmH=B(eIqUz0VjJi4cAI`>`GO5mTU^v z^}?el2@ddx>}}8I#~0vwENwVUW-2D&VN$bbNYP(2wF?#fnxDeI zie5oo>V#-{Um28gSK=Kt(>fpZF}UUYjW0=Bi;sc|T6|*Z7iB(if4;W(CeR?-egwKk z<3~c6Yx^hhgtYooM3Oz~apI4)S6sMn<;W&HebTy0kmi)hD^UgW1!dct&q>BDwtc-5 zCrmD0&A;%VI50NIP$uNx!Kid^hT3BtT$$_Z$zpjIP6VVaB(tslhDhB}KMdjO9BY~}q0XZ}@=Jgj0 z_!olj=D3s~apGA8Hjc6liKDX`r0|mcoTj+k6$hy%NDEzzC!th`sI{_Rd3emAN=AO)Bgk!E=c4v{>k=m8a|RYIfs}w7@pig2unKlpVHVAu`t>cka(} z_SHTK>{SVf1!dZ50OE>7IZhLMp>LLw z4>tx}>}%T={f@a0FBmlU15YV^)O`OSl8F|3OOr z(a+wq3_LRQ&KiATj&!wFQq+#)ZhL+SD3tD>%}=d&)~ z39NS!8gkqIe4+C1&3an&4Tb8h3p7Fw$$3=0wHvm_>V@bN=;9u_D{_iYp@C*6sss8Z zd~#A9Z;4(U(B}MtWh3ed-u>b+pz>~+HMV8pt1{*0Sg_%>|7bLWH{V5dTzl|m;hPaG zW<1F-vb=XeK;gz;t^9XR7l0M~Q!z2q#bQyl_Mf+02ajf6K8Si6@p=0^?D%89@b$** z&pW{u$8$3et~WpX{QVN;_|sS6Kfx}6d|T(&C)yn|z1GUP6JWQNAV$$WJd0F{#jO>t z&gcQH#ocVmtu-m5nOA5o0g?YRF>Oo!cAdxjl2o-xg z)v2vuBWPbI4ZS*xct}4i|_4rvZ+b3wG)7UII&R0C*h0JmVy9zb+#N z%qhrBnipU;+xgx(iQORyum*M=2ia98qmjU)Kx0jYF0VJ(Nr_~0Jdmdv>U~1;b|CW% zC3$QZYdV;z)o$O&Ucb`E5SWY5(FBl*fc{zL%x zEQn_92exSi@}j$K(FVA^cHYSL_s(GU4FJ1k((Op1=Ng!834HdAu~UYbivj^XP72%r zu<`br%MjR+L~|K4X(TWNNo2V6hS{w zNVU-|$+4cXj#V2Qo*@R;Lr_H|Amk9hh9{tw$m|$n_Cu1=8Us6qWF!i()Pox2*s|e) zJ~9NZr9|D7p+C+L^HMOj!4f+JR=^Q;{Rk)quyqQVThhRe47C>=rg>XhDQplt9g>_P z0Tz-b#exQ|yZ|8#DG&)%^q}xKKs05}mXiU>bUhmn(DWOasDrRG^b2E1!c>Yz(xVVcxq#0wygC5}wft?WdjA%Nm6h0715&_x_!F>qaCu&PdGPXXEuF{nlZv}P%S zc;a7gn1P4GF1X*4^DJ*QfJ+_B5ky!BBD={L`08Nkt}uE{zm z4-VYzg1NBpTDWzX&OvBy5Tg8`Q7%b#OGy!ey|njrW2#(0X*kJyDAAlAute4hFTH0? z9EH?Fg^EnrL`i~6U@cKXnCL{*5QHZO{Ieb+=^=Vr*PrJNYvVKvZ?gF-I3+yGpy~0J z@%J?RK32dWnLUTzA|c3Nh~|d?%@m5TAITgKP#h;#-Rrkiw^_*tJN{cB5LAF`E*B*lGq#~#`URYR@D zhuP_Bh73T!(oXg}z{w% zRxb`4btyG@4;MrN)Eg**T}1iMiL2lNRCT|HYf`~J5!FRx9S4}>haIP#PFIus-;xcM z2$qNAXi?yIV}MDQ<)T98g)RN1TkWj%PIjUoJ1X|1$=D9t z1$UTZ%*fy_*YKf7zdaqi&0f58I{|m$UBRnCUg0GcE|QjQmp-)`Ogx+++PTgReK(N_ z0=>|;`8jRvL*EIkhAIyc&np5Vy54zvSh++3IPpLiQ_zn0px_t)7uUxjN#yJTa0Y=T z{K!`MNx0xMkMt_JD31XxeFJ-sZigJO{rFq!F>nU?^J3(g{bShE_Qij3 z?J{d$XkUB3)pZ)7KkhKM8+8FC1v|P~+P#LroEhNH8DA`^L=%omp-frnE*%hy<4*$ShM>OWWT;<&;8ZmOt7`)*Rzk; ziioeAp}}s#!R|`IE`?v+9(;9w`qks{SMTQF^8;V4l53)hbgX<}epx!!ngJ50u&amA zA`bc3YipK<+M$!LLm#h)HLqVBSP%cOerbLE^1*rp^xOZT(OigJ=j0H8^GS$ZZCED~ zQK8Q8OH0rgrIcTC@R$$Z613MdFzazYQf?;aCWgRCAK;g?De>Gm5(Y=ngx`7$r||{i z_-^AKMQ_mkird=j!7@aL&w1}DG{F)w{5ae)p1Hv-R4ee64w6I*Oo91v=e$#*|C>*0 z{jn2WMDoIYUr#A{M#j!EkCN^@Eu0!jj@C|gnxcf^A-=~+inDUD*C6MpR0(B@Xs|Zaeds4GOonc+B*%_I<8naiDU@(aYG5m5 zDlKWmFIkbdEE*4SJA|pHBzgHkrLZh#8+}IEOo8(aq^+1^<&hcuktspc+Z0T+>#Bnu{p&lZ2 z>R&+ahe-yl+hK{@%Ln);F}lGiuwd=*%3>pLP-`F_?~VqabD;XdAtrto16s9if2ta^ zhA37)^_8JtFOz)7VF5UzZ^|`}FEE2>U&&b#yVh>B%#A6z0^vWvuOC2)7?5%bWn&L$ zFqFJx49a{<)=eQdt6qsM1uG*#>K-tf3Z5i~PtwH@)l~VIDUXQ@~gxm`?PVw<=(!fOiLz zWM=IRD5Nbp2v!uRE_$Sk1R1nKv}b{-*)W}I650>1kVBF~vNZDlKzr;8cS58sLDG20 zUDhNWy5}=asxBm}V@Mg<6t7ug!nIW4_lnLuqk1kR zoA@Pr&4Q9|K!THQ#Rewpj+ce|B{RZeLUNKU$0=TZWcsm~FCeTR`D)Q6EIKLb=L_GZ zBo8E5I|p)*38A4QioK%4;4tZKBFLN$1L1&Hblk@|`Nc;HU5L^t1#8Rfay+{sti`3A z0#TR(B_KiPW@Fv`;uK5i1{5Utkf2_4>h*;fc$lP{1JJ<-GHh;0#coun>tD2=&QgS=dgYRCBx&63v zCya*{oGvYIt?5RuvdTjSA3Dd))xv96`+YP+ys}&u!v3Ste81i=Rqd@4Og!b2dt{G% zM(?h&pk7F)6s-DYIDNloE9)k!GsuPt3EsEgwZO-w}KMJchZgpP5Ik(5wCX6S_uourp8BXny?DJN(}o z&4-##fiC^b{PtS+hEh+nkNzbbiFc>C&3`eQesX(2d3(X)z83Od<9or!bU5rxz#(W* zI3uMyU12d}t>5UYchfs%&m9T4V2o+GJxAkyP_~iQ_f@j=k&E-$^w>iVGnr6iuQeBL zuG*fzzK>+4kxO3AHsEm$M9BLb-cGi_@mgf?2!3^;a1$kmeH0>|td0oNavg}b#k2|= zvv?Rs%KIHy`N-x9u0!SJJpCHoEJWHYDc?}I@tZaOQP5hYaOmNNy@n{4d#2V1u6s;p zYm+P6cemd8tuPIJbu_-Eh?p$4ogX-){Qa|difMflx7C>k4n3AS_kQr$=+j(Yywe?o zJ6TJ%9QN8sKF1_6OZ4Gtwsp1-f29lOnX;#<#`~xXD!n<`8x0)b8@zXAWNEOV@;@5Q z*SoPwZJV`vPi<^O0`u5vTaAu3TID|?HOFH66=b(vgz-C5 zPOsclDl8P{hVJAj6nxIJl6|H-P$Sk-S(qtWQ>w_Q78+EZE!Mcvnijlx$^Ge_(V}jbDdhpj3`raR;xxE{Qwlid?%Z4RNMSy&7wOje}%g|pdUW^ zEz{SbiW89mlkd9okWDC1XQLMpl{JA07{|*UfXdvBuVe)7$a`*j4%S>Y&WH{wcWDhY zQ~2Ny_nDp$jtYWGJWbBEB!@W1H&smOx}@Dh8_VaS1uMk;;qL_{*cUxuXcjTJ{n*Z# zN2Maa;~k&)pvAf0v?g8A{LSp+z3TNjR!SjIc9eE*va!$&z|v6d*X!3L!MNsnX8ptVX86_nM|fc%Jx2mQ>4l#r7US&JRkjo)J6EuR;tEiS*Du1oB`ia236=g zo|f~>w0%%a157c@G*%h@lXakLG(T}B%4Tmz9dm1^pGZb2Tb0u%Zm%%ipCO3;` z=f7ro{H>koJo^1C`Rqj%{6xCXbma5&eh9ixclv*6G|UfH;|g9FJQ?|&GOQGkcU`&s zEprD;bV=B9k4}HefZfg7x&N)vc#Sri@?BIIP{}LfYHz*T^1n2ikIgKmAv(OX3!6@K zjVAcxdAr!p-xs;_r_vOExBiz#^D)NNzvO~m-RJ%CKTW0Ksf0HbpLZ(hMh)xwt@qb_ ztRNI}-=VS!J&2rM(%gDrfF^RB+`m+^D)bXa)!Eq1x^zP7xiOba-gI1k4R7v2HE)w! z#8#LrNIf(bLkm^ESQPW8;6ASFyLz5G0elgM{cIt-DQ5bsPg*?&l{HmzxV(j+ltU2} zsA{^`*sA0=sbQAK6ms#xn&^cK0{soLgiVG5x!mCX3^;-4x~Y1!u{| z`0}JWxk*;u%w@CjY#iUYYUb64{CX`Oo3S#h`^KNH(I84P!=}M(Pqj-0*AuG)mhM=c z9MP~cOTd@O)N-N2Mv8P+Uy5!DlxINA&L(UPK{ZR`>;31ROXcna3RPGud^J-Jj|)v4 zd?Yo388tZ|Bs=B*6e$OP!LD{^G#{GqJ$G>0`#^byo0~TwHAncKvKJY!+~JE#2Y(ES zL>)&DWma-skE~R?Vm}mc>1CQ*L)k%CvI|L!_xX(yMnPT|Cgy^V0cMhTLi#Ccs%s)% zce=PP^E!Ej370}tXhNmR4~dI(;f$3R9j9?{N%m(;gv{2HEG=@}lYN<_GI2mkX{s3O z_$-h5qFDNw=w)OpvR5aK0L9@=YWLwuJa2N6Z&Zr0x<~g5XHcQm-a!E;y6@_Mdi;8hZU|KFZ2Cx!qMi&B-A){blDe`wkRUbzksDEf#FKA1a5iWn-2hHx|DMxNUunyjgJ|J;0{}|`L&h==M1`Gh zL0s`)v-@DXrO20HwK0NoAIRVmKw@WDxs~62g9K{BN_G)EkskWUp{eP!GNs*y`jf)o zG@h{umKeBTX_`R_R>qPjtiVRFVo{pLo>>D6ru1|3n#x9F4au}r&*`)5Or~cz`>^?- zR8C9&A%ekJk`XVFXKnJ2{)o5=kvj;8S|FmDkb*c<&FF4GH;>dbL3kRwo#Ga+;ywgO z_6s73Mfd0~n9HDn68R%f^LsCq3K|FkQ{MMs$e;_0jJ`AFD605DJmjSXXIF;&VQ-@; z_6c>8`#X%qcEM!9i-~*4EkCL4vuSH8;4+z@l@H=P>ea2c;uV!JC&S09&q^MWN<{k& zn^Hs%N!)#KQ0l7Tf8*-Dznbd8HUUFI2`xFHhMt7bYv>>?30-;zL8%J9f>cF{kkEVR zT|!Y1QA3fUp!5z>6%`c_6crT&r3hsBW@fEfYyN_>*ZFNf`+lz5AD1mOP}WUBCi;*E z2~LqDzAajU-ZEFdkeTo@gTN?W&NE53j#S-CBXPYy}o`4m%=;sbusgMkPAmh>G{BLQtRvk96 z&e?EaB#e&G-jr8eJtrTTArBR|z3TLAA@Ls~8vz0JIN*9S&te25-hvAX^cEfzHy#9t zcbOzoF|GE9z%!F zCAWx(MEY`uA(3s6P_SL)F`d8yPKuI**i2A9u;kk6!v?R21ommijO%TKpqiHWRT@aP z6ECv@d?J&TH(`-#&d)cAT&4Zh5mqLb@lrY&J}aPalcJJ5#_9`1j1*X?0(z||?>mA` z#UWX;pnsK`eyJ%za$(-{H~~q4iT6TPbN~xihL9QVT}>F)047(q#>;azGFwvK)hIr; z=IqT&y3@_(=E`4Z6mGHdB-`pz$h*z^<6}Sk=%9_QpjfRD_Hy(FWm{^Ii-;>1?iY=&?*14 zKh;Xd5WTu|`9B9kEtf2lrFZ}MNn_Z4Zx_&ZB>B?bCE&fG#9p4_;kqKCKXjB75}qyf zUhvkd`<26ahWw`Qw-San$8f}Xy(Ws%eQ8QNw;B(ZQkppsNJQyaTKq9HD&g{+v{qT< zP;^Yh?#yf8zP&JLOzF(lQnOdJkXkml>b!lQr8-}Pic%#`+){h@Alup5fk0!ZoY??y zmJZ6dmLonS4cY?AL+&X{lA2lE#*?Om;RP&}*HT>YKWwFk!Fr{Y?2~LKRbiOFpK~lO zD=OssgU@*nmB2Z_hdSDw6`t@B3;%mcdess2{q|5}Um!Q*MViIc4`qt)-KMMgF8T5) zI2uu1860!LTE}25BzGrp^*(*VZ_Ni!xS5N!E<7=A8bmv*UVJrT*(7JnjMXs-Y2sC+ z1J`;sjb=B61pa4JDmQl3I@kb+7balc39Ux|jZe!>y=t2B&Ka97Zu=nj@^912Q@I=N z?8=AvH-5;?hy>aOfiFn^m~&#j5Rg#tW68~kkj7D3Apa)GBk+d7kBoqng|`7YeT%P? zndh&{-%@f>l_MzAIV(#I4BX(K8~#zr`6i(UT#)3lfallMe4{Q@nq=#uc{HGPET31o zv?%Z>`_GS+M@z4dT*$(uxf>-pet~tGL9b5(YT!HbCwM>bfOfS8Vrfp;pW41ZIjYopB#4!K`5t?{Ys- z2g3+WrVdw2*691ptmInc};!s`TNsXUbo)UR!85DYa1qLS1;>9OpFZNm@aS| z1v=bgQ4}x13u{_JySS-di6+90ceJlsNP}(iCQpY!;yTO$&#%+H2wCVMNcj^HhJCV$ zwL%9XN`|<1W{z)GLD0?`^e1IpKMigE)Q6rAD~cpXzcAY^XeGjPt!qgZqD zgZzf_3Vyhkxa48@JBf%G?K^lfDk{(?n}n<&c(NxWeMy1G z{nwO9a+=AK4hbSaLWx1W?4G$)FTIMUH)bjMN>PI3CK1(1($lnV0&GgDS;{)d&2G>( zB110tuC(`+kjXS;Ctj|O)-T_Q4>Qdv%RY;H{k;(Y)-}W{yh;=If2UVaxB_)j?e^s0 z9(Z~WTvny_(_}G!(2#v^-!HIyPWC*C*1(J`;JT4G7@b>VuW^^Df|c zgwjxnIE-hKkY^w7Sr^4O0)noLw-aBq(tEQ?Nmj@#34({le?3klhCO2x0_atiSME7L z3`a@&o_($Yzp#;{+SY!eW~IO6>wSaF_nUp%qvQ89kA6^~^@vCeBKe8~__|2K+0U(i z41L%Q6*~sIb?>K9lP_%&WQ0hMY*O@T3AVp*hcje33G0?0M3A2K-U`X)t!$3uxm&Rj zH!m7qROv6Jv-5kINzHF|OazT|HA?GD(8CFGqd@A-E5?Q->6y3wrg-UXk}`0Q9>8~-I>i@N;1aihmb>c5@9S88uSVn%;)-g64>HikqjX50<3*bChW@t1_ zSaqLzUJlR+Sn1UDTbhDe_%6_l&+bkpVUu62ET6VW_Jl|WrOtvCe?D}TSIBw4nCP*T zu)3tL*|SpQnEKu^0gJf)X-R!6A+eIsy}YEK_zCrIAj?R1>72`}UjqqU?GYIs=`RSH zdOJ%U;H2ggx@4b(v`YO^(g59Fx^AF-;DEB++1)HKB5moM96`}=Aki^U)iXiekVbI9 z5mYnZFBm0Q8L_^O0w=lZeHU<5UP=D)C}`uJOIn}PM?9F3|BbQ~_{)tTWvL>|MF*dN zQmd{dW!au>lXo55uTMT3?M%p3K4KnxioJ-SIW522aZXXDBmClg9mXL)XMYWBd%ey? zn`Bu0=R?ZA^Dkn>i}2MHc4F@SIp05@#|$jlPBbe+=!9BtY%vGXwtwl<@|g{MWjhou zw8t^SB^+F+X9zb8Wn7?{>ur?5%`c*QaJ|+zTSQC!SToeeBnz#Qd~M0r*EE%>m3|;1 zgYQw9dYq$R+RQtnDt#gok9L8-ZLLYJgyYkK0Q-9DYU9H3{BI5qoSQ*E(%NYC8rK(7 z>AB8lB2*sJ+AljkD+>I78qKqtJ0fyl3Kc(g$I&zM>XZa~4Wz&&Z#@06(lwmjfxU5H z8R9oljJ@(4;VS48_*UXJ(?gpJZT@+1W2Q!J)OSjghjr$|TS{ARS~G(FWk%h<8aPqF z=-e*%7WaW!dfw@I#jUWX@1}e59|eAV_UYl*!`hoMRr+yX5GKjc51PNe(&UrZPvSPKO?o~+?e6ip^Iijrec81Yx=y5;X8PGu zWK2q3{97csoYrnN)JNJ8YYTv9N0NEE42OP|SI2P~r9N}%ArE>uY2N}WFCo&HjUSgt zOQ3}2wKtoDa%`E5jonqx0;dnsBzev8ZL<%0{+h!H55~Pd^FD6uGD1mTcV(8ZiIS-= zbI?HzTcq5Pv$N9dK_yFg-fm_ozI3~5qgbVb-C+g?o*KSm!!S$WH9G@TMH9xQ>+2$e z?xT4y?#=Z`j#@INz~|M@j%XbI%|KNg+VP%wbR*Ub8HL2$ zQ_?OPy<;d!!uRkxMi5kY-H7PB`?c5Dy$18OBLl7o-(senJ(6Fu71n$gfshzQahwbe z{)vRRI@u(Fk8FPGf8Boty+=?`)J9hQfr3dTdMJSyJp!T^$(_M zwl4qC^Bj%ndOqA+ttbFiizx`Bq#2TJYML=+7p1CGcw8?Y z>TvCaJ`xj%i~qVIbe=tJz2YLin)%QVGHoJJ#Cx8OYFn$U z@=ai9scF%4HZd7+5@QcL7~-wLq{{S~Do)OT6qHdALL!RGc!d$>9s_ac)JMG~pn-F;luB?9*|-wf!obL;-135 zDcBqn?qzmLpCto9vj(Il&Ibc*mmP4N9iEBvw>?=Sj}kcDlnkNfsFW%-FVWm>eEAH; zb!dYE&FxImJH4B8iLM9|?U}^ae=|eAgd1AdLa)#=lp9L*ZfuVz=|h~cm#T?WH(bR0>@d5M zCB+-6Ia?ZqK5I=SA)14289uG1;iqA@C!RdOzk+Azj-q)t00zrjUO6rcC?36OL%HBS zC+mg>E-k%A5w5GOZB40&N1Dc}Omb(DEWBuwr(uJ6^M*@g1GLm8S$b_JW4Elo$^qV> z_t#58*a*!LW(rQ7ZXD88!oz$%JktBS$>OY33)NX;)NMcSc{VfQ|GqSk_HBjLAk3K; zbu?1L-<*Q^(bZe*zL&tbnibJtm$qTx(7dvjqqAcMJR%#(9JOueIf}`z^lv{5`GLJg z>#h~1^%>tdZqA82m~(}^d2)kVO^%6_)c$esN9*>rUfxJgR(PF1pDr9-Q#B6p8JD$Skp6ZA5s<_6e$3}uJ1^$d=ky?3Y?23*l*yZKpVDH> z?Oxw2$dEBJk}(F^2BmKQyxF&}i>VGBd(T+&2w`^B{;~{Yz-=w}+Ikw_gNxgDsCUHf zUB9zi=?wMRR4Gdjb};zy$^X`?+Ojh&Uj2JW&wo}`N52Pe{}GOW_|(94sM-+3%#1aD z^xLgfSk*yXTFg!%nCtRNn)M50va)C1)phHtqs5NrUvd(8udi$#z286G?-RYflT&rP zp`q_36Lb5|V+f{Wq0(_|)4`SK zXRYb^sr151?s-4y#m_>WRFM2O2&)8E3kGXZ!MfXEoDzdkFoPMD!E&3yR*8}37|iHO zW%Sr)^ipE-3uX$SG6ilkg(xvc1T){FGRJN+6O|xI!H_g6Bx@UztHe?m%u-5asRURE zD4Ekwq90aR9&WQ%k$9-VY+d9~bqZUb68jf#=pdDSYMZ@Jkt3J_KJ{Xq-R4+Tf^Gyu zzfz$;wxK&pus^}DLn`cK8%B=>7()PODipELZ)z%5<8r6 zN}}#7tXH92x^QN-Ym8S{xXgB#-cz~D2Lz4bJV}ZNE;~G4SYE#n-hgJ_z#ZNYEZ-Yu z5rD*3-_Hlbi(p86#9370?7wU*Egg=|+fiOv6BiI@sJR;AqwAc@-0}wVi3;( zz4W(LL>CruNH0u*3yjR7=4_?D;RG^B0xx%v-@JrV)(}%z1edqS2EB-=x5ztNbpMX% zaEOQ~NtAI;s%3}2Iz;rYx8N)a`94SFlP!v!QzQ|~zu_hP!WPN#TlAn$m_HPa;Y1-? z#1v}~t6rEaZ&BAdL~OtCM>y)It$1uda=w}0>$hlVD7w5KlS2}?`5UGF8@UJ<<;#6g&gefbRD! zx)#m}rVcKa?O5&YQ}GHlEW+ThkJ+ zVV4LK+}M%kMWOD5fGE8JGF-w>y@addxz$xrZY!cXD$+A~+J$1$D#~cpQ2wcA#<^X= z{ARg~5Nz6S$w4^lmVtC<2=nC?q)i_zu||eZS$KAp|6MQ2UR_I13dH}lgK zWV?zcF@%qV)`EF6yCGGa?PX|2^C(gt+GQ8X#HDD^iliya<=_QwuJTvSOY@LmXKj9O z<8`$1#dy|qN^=D+crruL23%-Jssq{$3E^FlQdicMCz+eI$T=D?K>H2hAO;ya=@&a( z+#uFhVaosJrKY(IS2U zWg$p&Xit<}&EC$%y0Rxa$&Gf4XA^J`X|_jwBc9Kg7m~NvNZJw9;MC-CM3{tH(zsDY z^HOS0(3{*SWS*2`ya|`LbaSf_5zn#RCN)d6eaS6ruqS%pseQ$ZIh+f1JAzN$&LkG? z@?!z|Hyn*<5vASvBYN4B*_2-7CmcIui;+e1CoG~?>@>ulI8TMZXK;+%5r!OUr~^-C zt3qR#x4R?7QVhh(OH}=2kIwbtb&Alv!sC(S&4;!}PQcLx-27W@glFMQbVX?OHftVr z51N+;jKqDz(^9ce>nXjk=^k2c*I8^$Fjp02(5{H$LY0L}ql=^^S_PHo&lftV$oE4# zSH$cc(1YQ8Gkwq+ZZt22aWYpzgBu}>wEZ;eD+;)6kjtTZs4xZ8VhY5TA*3xKoLE$Z~^jf^;;XOt9-FM^|?{$rai{`c~tn6HU!{fNo zXZVnK(<5B<;VORukFa3`+DScJ3=I+THopJFpd9U@(kj|M=YdnT75s~WI10z)iaXk) zDHIMSb<|~?1CAH{yf7HXckv0Y6lH}gdDp)ygkFSr21%pL3MIUDL*Qs(kg9G%uQ^dI zj!pR@Z9$h&6=lqep83P&fQ(JR3Sx^f_j%n^G|tJc>A+Q7c+_Kf(2$uwIFAAW+Q_V- zSs&44A5HgHzJm{C#Up%xGVofpe(lKds+Gv*BKLkL$&xz`USnO2_f6VI-Jth+qiQAE zubE|^%Ep-p8$dEUMWEJXWc#m9aPo8Wp)!kzS9&k;^rt=B6CFeZn#`lL+f+r-xBi5t z2$R?hDWLJ!Xh20pephgxJ5v%L)xj-yn46y9Eg1dRRjfoROapbqt?->UG-ubJ3$6Sm zQf7Sa7Q7}o<+bAmT*PTfdW6%E)|@K_3Qh`vAJfVIvqdR5+kPxYE8>L}_Ts1bPSW7FqhYwjQL#e@kLV^OAUf!DCIkon4DG zd-Ygb(b5op%kbKN^O7tMr2)#AlU&VmTjC|R1bIghZoB#sIChraaKhGAnk1Uci9Hu3 zw%?rUtb(%R6pqgo)qCTZWSboLs^D%8>F#jKTli57&jiPjwPa3VyiwMA++&p~m% zK=HS$ev8PRCy3TrA^tFhe{RW=PK_J+e5i84F9JD%H;@}%)*eUKq}N0ev{jQ@oKc{b zK0EPzJ5<+^vKYuezzxaVl5xwfesPtb?Uk5r=>3R8qzGpt7n+_433){kJt8WVA6;H` zG<7~l_KKDg=TzHmm5_94q5th*lzgK#SVHgR9_yVxjdI=@x|+0tpu#V zB?r@_5Z<@B4v*o&m!8Sy??k^TmxVj2hp8e_bBPaj!n=rQ zLg?r!@p*MfVXeE|r6X*Fa>I|Pu_^7b2QiaF-IHU-lT$iVvoTYP-BWLlr{3#KuO3fD zC{OL~^FR3~X|?~5qf_8<^XM}j+{51!xuEWjM6K#P(UFQm`tGLua>PjPj4XOWcCBL3 ztv(EJh_IzoX;=Qgnk73OjZW?iv!p}*Kh1LU>dFfe6PJwpD7AVZ1rRf@Fm0|G%0Q@G zT^?<&9mzow(uK@8F|~1$=ZjRiu?|uR)#lQwcU$gFCEoSFSa7Sg{$-Ux0_#a<(0lz_ zLcWX#>sb!>EM_0H1fX>_>HQXBe5fQRpK+~wIjmJ>G~UU(AxZaDVc z-{LooP$EaB1XXDM1>T-s#G;eBTKxtpd-0f$Zz-iC$g%J^r)6U-^6{E(h7=*>-;_Y;)_{jPrkj%(X}5s&;R2~VjBPV_Jh!lUmtrD*hH<|z0?w3c+-5GvbuIZ zk7Qj+7~OJv^50C!r3+3RW&7Wuk4FX`le4yXlidJ1AjSXQ|X8X9`x{%`9VN zjl65PPTu`OQQN8Et4_Uxc#qe6Ds7w09i>Tz-fS;@Z@hC1PUWYWs$h}?*UZNCla`A& zgFnXP&y>I26tkg#{AiyR^aqbW72{i>1p$xh|9<-q`<&^)ncKH^)Vli^U!LaNK_#>IGD>(I?bW&3a17`ZL#j$hJg(L&?sq(*ch3FEQZ_)@ve&F}t@8 zgC1GD<~}pZJ%e-)Rfl4})+&EyHT=pAihW@7+~JsOKwpF|!?%LF2%N^QETsy~2L8&9 z>mRo5TIn6SNLf8I>ze`8zsoG-7^nyqlpFF%B~DGr?PpT>Oa#`qZQR^<00jbJAw!Z? ze#u7hFxwZUuzR_UgdcZtpA!e0%}>5yWjrBMIAIQKwY+zF3|})|k~<5p`cIKx(5Um< zMXa~b881_JXqg?j*{!YFhcSE?mp=$$dnTpRgetbF)r0lb5?lnHHHC4sI^FXy;A7$)3mvVoBiB+0WsgL|tXlN(4i9 zVCsy1{5Y<1)!~r39sa>f^#=B%N}rqj+JgCe$8CDpL0LXa1mve)5u4>Jy?2f!?X?T? zGH4%|I&fcc{36_L-ccoBtN{xf>wTieN<8WCgaAkdFY3F1;H(e~2dnExe5fZN1Q`@@ z^iADhvP+6(uVdm0NHSXY2Qci30iLQ5dK!`Rfql>qe81B4_(?mlg5(*9zl6`j4zLJc z@w*jo+-Jbko!ss1`o}xZU=qm~cW9PN1BUp6B8QVSw-K|!cEEpY9Gy?9FGE8H@sqPp z64$SxTHM47J0of^0)uQ!V|3+_&1b7u>zE)RQxV2F`oFnYblyJ`1QRD)0s>S*GxZ>} z9gs~e@==BdHH3C5Ahtw-g3UhZOZ9dp_O7C$Tv7!YKqeW$G^!pL9Xy?-We6UQ;+ZeN z=ubmNkE9L5dHQGWviGwxdKw^mmR^;_?c)I> zWe|!WJ8I8}?;#%CZ8Q)VJ(r~djKT?I)-j?GB6R-rr1pl+!Dn3iTR_)>hoi|R^*b+T ze&)=>n+(r{8w4Dy3#Buv-nkXwxvA|J8>o3K&4~tRCrQhTTOd8D{uieKfcQ4?yjJ5^ z6QHKGq;CZErgvDE!!9edf}0BN68WV%@%Dw`0_s<)vud7gRw|8mz6b_0tm@kW)z~h7!qe5k2-)Vvh<8omakc)6L?1pPSB(!@ee24l9Z9sOKvI#jC{n7r{`4Bknf723jeI~h%pTrU5IpAJwwWRLA^TjGU%7)aO zxTcJMqwYHDdbAkQd@u94vGQQgeF-ycQ8kCZe{DzA4bxJdyhaaN?duN^2U%j-VC~>^9DD{0v-e;4MsZ=kEpCwzVuy=>_I zOfkDPdLcWkOm^_C5bWtp$|H2jz_~TJsOdm)cKE$>x?fL{pU&MCiMXMti*nX5eN`V6 zQD>d~&E(h7GhM>Pni1FAm#zO5G!o_+guzp2ml%B&bl?a_JsZL`6Gq?sg3e2;fZ=dUUlFg1-?^{7#Xp2+J?QZ2ckOw7$Tl07j}CO zfNkD>Ivd3lV$HV%2pn)h7&!^sFrNsxzutbTc2)Nq}_`L*BL`d^j0+xEg zo@6U88*s`TGG~>A9A-xFRgG2!WIeT(H2@Ax&^{WlTLRV%Sr!r)tQvsa1Auu`@LxO( zL`*%yz-p0z%w2$R7AxI{5s~M?*aTQE?Aa2)IhxwLlgcv7koX#K^XIxtabknhnGDn#E6_&2lI> zUji&pfxgc8(GsB6;QX#Z)`un8A0u2S7sLi4T_%@vr`QL=m;27pk!AoZ@zD=zbL0qx zMy)36`vKo+*sCD8GyDD9=BwJ(bf%)SNzzp2qnVWS=3MY9lr1EeMkejkLD*~|dO)g^ z5)&8&zH4XB4Uc7o2MP0Yv#Y{8b|fq#RGV&adHBOuBBPG&;p71)CR70jk!c9Y764=l zqu5+{IAd^mV%0Fe4n$XdvA-?L=N02sCBR$@wrZolV8gQO39F?t6HS#r*|5wj#eLle zIG2cC{AH=mF_#XK1Xu09t`@T3%#+9BQMU2@BxRasWZho;fl|y~yz;#NZS_sqeZF{( z64;}bn3^}Pl*fC@b1z$tH_v<0Pz`G zZ$%Q3?PVtb2?m>zIH-qF!(Hh0uK_9zahzV2tG6OQ*kpilbc|$EdriM3K$Er|E$f|p z=~flwTfnq5(B%numQrN%0>L#^amY#_syd!t1@<%wGnH@XO3TEHA?1i)HXAyFB^NNSqc<2wgho7UvH#~c(Q#x3eF)~QfnMlcd!0(YggO> zSz03zMkmE#lwRlHcDHIIvU3(L@RTEBA13tbu9LvM8Lletam0|zy@2|vv|@{^SL=36 zC_J{8cMR13QLFu<>RD&YFrczXZMe2SV1-L_sRx3UN@CNAapp*4GH^^Ym5nzeLw9O8 z>2KQ8Rkr)zy>bMTa)rg&b{|ENpeRxp`JfjRqD+Av8 zz@zSilQ4jNusYZ_Vf3boxPRS7B&SlZp8O6hPl?2Wby5t;y+>y0&AVTf9!y|>Z&1p3 zY&nd9ro>!u4z7^}XFR6$fUjE4!GO_h2Ds=Mu9Y4`u!#e~GdSS%wD=S@dR!0XoV)tN z0KNz8vz4Q(@EuXed1Kn6HAjD6{ZIq9m!RWZ`Ml_`u>%z6s38>J@fk*Ld3O**h#7ee4$bN$^ zr#XqW8D!rGBx}QiJ)6L|i+_6`)dWPpdO{D%rJq=VNss|U&lE2*=GgJl^&`&R7;6{=xZXLb(g1yrNx zIsVzOHwCv{jt*J!zi~u`$$YKk-Hb0eO8rQd7P%@*10;T}aF`x;DwSo-h;amk6C(e? z>bO}Vjh}M%c~Q%QA7tD-d7byq=2`2rXWj3fJ^%NtSFpSPT=(FG?&0X}(eiGm8umZy zzK(NNaNDl&b3Kl3jItncjfH$*Hb3Ni&xe0KD>T99Yv+ajvefgUARMUTPtTqM<84gX zY%u=Lh94j-I_3dL{+Z65<=JKR(yXaQyiFg z=mdQha-T48D25!*O7zXee|m%akhh@-@(676Y~& z0Bqa+KO3)?QklH+!`w$lUiFF-V=8EyWn9FVr|&4DL@;x}Xn{ra_YT;O(oN0~mN$~f z!&~>AlDHRv*trM)fkJug+$6Q&L+2k8|R}lSZD+Wf)+Kvv$rD0&zbPa1 zBR<+Wrinc^^1-ag$()pTH4Zh0pb?etu+D4hBK6L3!gQ@OG=L)kNsw1$ha4yG_6x1b z1vPA@Xthz)DmS%>`|<(+Nn+gXfWA>88F};y#I~Aq=xQWB_xLJ9FGc?yISi`qhin7+ zz2_Bw7zvW;Lpa(&%{7-QrC6i@jg2{Ih(E1&7#=bQTg@0P19Oqk$dEboP5@R^GyWHBK z25p0==EbH9qnY^%4K(59*ctY>EA1nJi2qKSpQXfn{_>K&I(oU-;(iCr_WdlUODRoh z`Qy~vWrGhJy7L1jbl?1e6IVo6`LkEYu(z?Vr4h?tLqDE*sz0{hp7d~o?{dyoluutP zo9%|JQByIsc`*-flX_+-Gb^>gGALJJIewH2ieRu=6|a7(=Q@R&o?9@3eO`gg2g5+N zGxB{I5|XY2I2mwkkrj_mKIt{T;;5wue}H!t!}fs8r_nJEcVMRmY^m`q`)B&? z$&7^_Ak`hePF?r8$i;^M!Ki1;u25tJurl`HQ1auyZT8xDNYqdEHYM1hC#*;3b98(^ zjl$sM$@r2M{F(F4X9AH9e0Mpb2cG<8zF({B-mTliBOB8cwrR?jpGvTC$N%-jQ%S_S*kCu5CNexqRv}sXS7G&Ch&IU18r>f>~^SvVit5 z*+S}pRsZMT^ytTyY!rn1^igA)R*zA=S-o4$y6W5e+` z{)1Q_Q$Ze}_9$k&9<~(>Yg>WtZ?k*+glhU#P9n8e6(z+1Y1f6{=x^n{{5=6%#y(--~o z{P&j@i}?-tXLc}M&8k1{*EPke;_55l-<>~sT>9&T?*jL+aG>1p|MxctCZt>mAe;KY zxorIXKPN2yUVpK#VG9k7XSsG(X&wc&q~AY#{HZ8RjLjdPm3fx{|a-XG=8*Ue;&;JVC>}- z?fTvP3!FL>$PYS`MdQFjGMGEhV&eK(V#}_Rsp0!7?!TUqT91K0zuXS|f&7!|18OcE z@%jgSA_Ft8VWdqpE}2D8Ashi;8%*I5wmT}u`qaclpQ_Cx{(sFfk>CEaMVVUQhr#>U zvoPAf(^<9vSi?lDo^#7Y*W5)rODPTUq_UQ;rk3UR(%S@hzAx5$(ER>1I&Qg?5Rd%1 zzhrH>wFka`ex_LlSv*l0yRXapqw2*C@gMS*hYFuA6@EJP$T|x5Z76c|k$D9`*Et3)W@Xawk^9^^X_NY z05x=4IbX*1Cq~@-_qaaYxp3(M3^G&c+#ubofx!M}Wmz%L%Y#@G;hXDN%rz?RYg)i* z^^YfU;iFQbx#{?{N=^33QFeY8$o+&uz24qBIaTwO3}eG|l$amg>dK3_JJR<#vfkRD z5x1b*LAGyEgfmGZ=2zlv!JdLfB0!E!r%`bQP6?-gB+`S@HnsX=eG)=trDE(Mc zRGiifySIb)UQ3TV2%Bc7%bia3Re>BZ*JdWl;%NMWDX&JUxo3DoDg)*3%>`2x84MtL zz|G=)RsG!es`ExspAGWtq+YjtLf)>+Ap0&^V6Gk^Rcj^H%0}S1+|#B7wv!x1Huw~Gy&sppS;#2;LGBu%z`dwRF>nt_RK#NvOtK1h$0YwcfR(zA}Lrc3X? zyi(l6{?(zY_S{`f_RgtP0H%C&AQ}K6#;0d8v8S2mDvcV=sle?Y?HCy5(ogl7Ty;NF zM1%T1#BFh09yF0Tl}xq0m)jRyPC|AJ@K0WU@gomZ>4m$ny^)iqZmVaTDP;q=8Hvka zSxH$k;ufX`f1HZ*XDnt&e1j`d49PiFCq6VJ&OXia)Jv}5Si%0qcY^cGXU$ylmZ<2# zh^#W6N$j}RQF@F}bV^Ke5?=uqn_8LTF`VD2)~Y@;Zf3t7IFAE-(-cdGxsK@`XBuuw z)BR%>l`3v0XRcaZ|4$TtAH^@*uxds2C-4my!POW*VJ2b=szSA>tWhZj3Hl36|68+! zt0^3th%zKYek~7(AQ@Iop=S6JL;o2TC6v87pw z{U6OTdtHb&ldQkj47=*GLBOochfJl4XeW{a$% zvFpHMl#)No5fEOObC_--?`E4h%n?y5VDV;`qd=!sd@ohlD)=bJ z&NoqTi9D=XN1sK3E;ekDbG=fZI@F$m1-B014%J8Y4WKb4>!EMKNU$KdKqf6k_j z;~e+(3Y&a~WE=*@p$)8y_i>i8>cb&6A=HE>FH1A$MFEGz%M7Nb^i9U`4qBb*bxnau zvgar$C#2e|`+hv%Xzuj?7J8LmwPX)9xtzx@gp8^+8?ZKBbMcxqy~Y3tnHe^o&Sv+G zFFbi1cp2?HXWuYm+^)}`W{W&(t+=z^F0hkg9Q^j(5%+}X$R871%y{0>)?SCi8xzCW zc1yI{ge20JGuK?%l7b5D>PZ^236|lw=Q0N8d_}%*`>G>p^?I_a7>?~N z*8^LqY^&U}H;(V^Fsr#_NdMT)VU>plO|677hH;wdQ0aiX+}r9^Op%ls>nWB)B~A(-z4Rhfb5DcB{I+&^(8Y+hySjl_U)FUz^6!6n zJlh9hELJxzZM$LQof-Vkcd7H9l0_srm&d+ z8H{0G<`Gnz>k}sTLun1k34W(snY09}sfMmw*GZ%@Kyw+Gb{o_^(=7Q;RDMp6oinsz z&pOr-^RQG8=042vaY2Y9eOs-)ZTZ|g8tvQ04x1Nuv(dBBtHvhLZxt3=P@yHE`#!bZLVB_a_~5%^dytn|jii(N3RAyB!<@TvZ;UGJx(uGW7k*Yh~I zELbQMg-V=SF%>q&#Zv8$J8A6`bcekt&-idP}As4 zy?s*P$&d$ObSZBpZ_27$u!yrOy7M!H?-o)_>ypH%*EUtvR0jzb9O9_%IErRRIg98* z>LXC%#RLAg>0Oyp9D+6_v8MOxcb^THCQHOK1aHcxa-}_wDiTjCE&3{(6crx&o& zVuDPHK|YRa7>bihiResfea7@)fZ1P+`gF6{Fx1e{aM(A7r7A!{AuItte(w!SUok{9 zH#I*KQjt%ml;<*-NoEx9EU=ob@(F(Ic${*H`ZUf{+gyyr>`XiYz!eAdy34sX30<*N z`kP-%3k(?T=+g4GdoEKd3`4d3R7VpWST^RE728m|cB389kAhUSs+JP)Ko`xFOw4Pb zpQXYU1z0|4m-QTs#?6r8T$r-RWtpA~ij=fSaB;;bv6PT@#g|1c@^StWbF}+Iyn9+w z7#EjSYrb*Z41XQpze22(xr`UFq{{CaW4rDnrlxIO06lA+HHNml|BgLi( znehoFn!{zOq|8*aio~?@+$^Dq&E>cB1^t;i7E&}ZNj@v2OqY>Nsg#O$DG|Mj6zq5( z#^P>p#-F!f{-~!eVyT)seXp0mKiOD@HNnF`ngpcYkUU1) zmL&UnGhXT?WSMp@bF;X2CS7kX)XbbNj>L6jGI#_e8-f&RTx|uBxDTo?Rh#hDveu=M zqlNh#v6_$MKOtZ2rVNXrK<5Y_x!HCqa(&uhTda@P%kDu6tn#HMb`573hSGv3q@y(P zzrX`|!84#C_NegI*n)l-_ayzqW76cqDx17DWi>|eS}1o?X13gZ8D9z>|7p|b zo0Pv9rioCS70gn<%bpggX`}N@UakNS8=6Il&!LUdR2H-f0lb*soJ2&9$Rg`o8!Gty z`9g$zg=yWJHWsOfIfXxS`TmwlC5(vwSi-_#6ku*1$w-5ValSi)5t8OL3t0jX^Kn3O zCXh)DQ@Z?s(6v3V(3JMlEIkZ0PX8!wp`17Jm^sX_zr#8%7qBWA}xDGDx&dh$B@Y-RAm#lXd>!dXaN zW}0?q+?^F`6wVDq9`e#NDBxeu@NJLQPR@gASEI(8l@#fBn4@d7`dD8 z{T$B^&v88WasLnJbzSH6KHul-jebAc(AlcPW}d*5IGr+Xc5hB8K2JVbc_cejPIY2T z?VN&OD#IUdf{r_kl1VS17EgP+H=B3|sS}GK7*C!OSrzEF$i~KaaNLtMpGc)ea4^kh z>I|lPO%yp&COXAue@&&06QQEcEVj}YQAoRLm@l$eNWElx`HEU22a+jB&-ikz_~Sxl zDp%FaLiI^2;xFY-EcK-bRo%uy!<`?GWFI!?brJt)47e}D#Y73$_|aDUC{0%WgARs| z?R)#ik4C2aTp2&Ikqyx5;Ch%saR+WB#D#HNkgC88Ck z)G|eqYcP+UYcaM}wecG9pNfVg>W@0v6z>r~52L2MhypC%O9xt}DSuBi`I>Tw{(fRZ za8F%mO(eQVe4OI>AeogUwN5_%E71A+=jO+StMEsu`m?Fg6ikk@(@Yo?E#;E3%@*0y z;#UhTdCE^@T0hrOlsQov=|_$;CLc!p!pBo#8fuPHTeZRUCuoDUY`gdyIH3H zZMi=~g(1mLYLd6;`bgpQWMkf#p=3o`aD@p#mHm@%dk16DBn5dSR`t>j=2xfGzt5 zf7irucU@oc9(Qz0-VudP>Mj0w#drylxj1RJ^YrdJQ58*jW9AK~i#8L@jTzVQkUIH= z^#9ssfUH1_h;oidBHBU-WGPZxO^w;L!7%oI4OtF*)%(cCFQXl3^Xe)J2*g03n64>O zio<9c*fY*Q%jr<^Co;9K)D57W&&KB!l4OXVe2r$Gqd+peGb{TRCzGiPbfz&ZasW!@ z#BHXtlQ`31e6F*BE*l}Cn?aUbKV{Rjn6}Ujw{XkVB-Ro14aFVJEt>EK0uT9m^fR{m zH_S9e>CR4*<<7^*)w2ql_s@1(({|c!Y#7SS+WTJ!U2e91-RV5t z`SSZmD*0}ATDh0dZtwTPL7m;c&b)N$+k$oJPurzloeG)zIn?h)ZM`-6d-vO^TMzQ< z81vqQ(B7ot-jwd%wB_E+v%T+rd$W;yb7_0?C3_3adq4X37QgTP{Jlq5I^Fw4zQ4@8 zzaq51s<^+VyT5MfK4rVM@%ttHg5ze|{-2Wl?Z~~)AKmBa9xHs?-#>MqYNmHt3Z=bv z{}K87<3F@{7g_JVS(TJW)_9~t)AJy;C>PjDqOAuu=iuy{R{x+Aj*GG(O?0=0Def6s zKN?kzKj~r-b!jOp`H&tpy_q_EaXaNE0Dfdx{zE!!W`~ z?tMR^ob<$wPilBjdwvDV26$#l_+Wwm)KSxJ-+|7GzVRmH@G(jvLe!XnUW%1fyw&mF zfZ89DPg3S~GM69z2f~Ou(TYZ4#EGo9AJ7JisBqez#C3%|if>2)8gb`ssCr;Hh9DZt z`&cMjl1gMAo6l2!HjRNe2N$&#sFT;hmsDHwhnAS9c(0VM*z_`Tzggg!B@ekd>I|6C{T>c6Sr-(ghaQpLDa5VV|d4G)gW`0rFf1Ijq%A0nq!L%Q{ zWVc*xStaT2U;m*0CpukbR@z?0T)R9NlVQl!VR#}wS7$$PGopJcG2$~UM&dKH`=KSo zRHA{;ilu;8#pUepUXY@@I^R8|t50{pMZXBR7#1JLNS(smSZ%iZxrXI(zeh8lrp3Ov z)y5>Wg?Tt-6KB7eJQS>{doW(tJ(%S@RTH=KOEw|AXL^6n`@_LaOLI!xs~I`>R&RojpE1-kxpr*gBBw)+cN6Z1lV;z(mOt zHeMVMv=HG;=uG4SV@kLn%?1F9*hoo9LRC)uUPjSa&n>;A>0Tax$>Ezf@RrQ!aEyw; z*lQv{xA=-TmboOcH zG!G<12H``DuQ%Kd937n0C#=dBbqPPNeBLRN-!$HRY(!^{LR+<;3uSMARDsx&U$YA5 z(gZSLmDcepiAr7@DkAw2hKyUVU!3QunUSiQrFjX4s-nfI)`8^)>u(zWlrUOGiLU$~ zQ7}U}%K-kFPfhY8Cw$7Zw%-~T+W?qZ*wd+CotA86;kqB&u=PaX<;PirffqW?9)CoaU-A+`Oz0(-2HZOyklj@+ax+jxQhPR0t*O;i(}7ZA`v9(Q zOydbw+LVHShx}!SML!h1q$98ezgdG*Lk7u(NmCzzIY_T%6I%(3jy{Ox?Pa9YKr4}H zKPfN7bxbU1S{7cRz&G^BoIAxx%2Lf&j+*XuPxX{t?6nCcA?0UczqYZ$8cdR5UAqFn zwrk}YczifEL3(1+^G%gbK!4;8p_`ZzfL-=OS}Xhwr_+iS$IS!UWSijxYtLI->3UZf zLU28s*RgW`^WH6L~jicO}VY+#2-Tr zyMU^bnN+A~X$#g}=hFIHb;c3gOe^4?cTOQ}8H$bqZ;@%Rjl;B+Z_SUJ6l7lTxc-gi z5Sv=4&rku~DI*Tte85AH0}3ZOXSZaVBCWQ5AVtDZGqfu_H&;8|j>E*0GZ)df$ z@(rMH_00cW>Q$XOVY4<4cvdR}_Q@xsGw&do+Rjm8gBJSXz%8B?tbk>xW#V82Tydc? z2O+T1O9=0BhGIj%iujbvnOiz0f{!*J!iqIkVK1VRY3!wz3aVOs4|!?<|Q&YqIW$i?q~gND$y(!HSt-P%G*CK z)xPuG6r*bH?!Idv(?@H@;a3yLCV(J7xn#Yu7L!g*l}`lqodFi~^*v6pPS*ogUk$&1 z3@6M$yv>h&mi7Z19BGu5Fq-0UcSJitjQ*1Nm{ z>p*ubajGIx_F{u7ofB+3!G;Y_uO(a%;@KnY>l`jRDK7Ub7iK2=4TbC_-J&e|lq*xL9}-wWSR5GBFFM~NMlhQ6j< z@;=kn5_gAdJ7QbSdzk=3HSmh=SpRejzUdvH6q`NsF&&O!0_grwpeOd(;GHjkA>CJF z7YPAXPSCCMD2;chrzmitKp)XN_~@fVwQhkFAa}Aa`>cO zM5)s@a!$qfL4l@7nMX4g>iYudWRl8Du|Md`))V_gUx0&8Q#MlxwGgreXS?q^oWa%O z5WiSVHGVPhD5l{~3ab~?=(|r4rI{(u6H|AHEG~xH+c^sNFo#j}7*?~>7`|A>UQRXn zuqu$|kRCay9z!Yb`>9MJIa7m(bFknk=VLwjcHUwUx_1;wKZ$sB#{m;dF*`VL4JSX- ztE&Oc<;2IBXyO)b#=nTNrmc`;0d6Z+gTVqd@IQ7T3x^xMFk$)x;S&{2QVhYzYSrEX zW!}WgWOE9yP;-xLGRRr3*@nM^@0d*m$(|;jA{^K3E>)0<%DRd>^udYBjrPL*vcMuX z`WRG`EaQ_SK4gVZRuYXz!^^8EfvWQaV?1g&a*%2?6A#*vAz7xCJ*}o_NOT7|_DvMa zSWmaQN285fRr;lejTXVdv!6C4V~pfoePh`rJ)sLC+(pF*kWj~hJ~5?F=xzjCeq z!6eS}-}Lr<3YNkj1F62$U{6y?4w-=hdu zS;jk%Nh#e~dxp<&eGt|oosz$TLSxrKt9tovCtr4yfK+tY>-m43XT<6=uy>|3Hy54w zel5+v$+d5^C0kBMLNt$^ijTv=4*L^d-Z~-2mclx?1%I+!opI5>jfvyA{GD)UB#>>l z_~zkGaK_dCgOC1A$eW|hFJE+!5{K`uR)>wBUFt5A1jDD$VO#=Cy>c#AgBr+{uwm}U z2})g$_0ZZ&^E@{;%MUwj^tMyDV zvom#&J=l&OEzi6OJEU|Gl>BD-^?VY$P7L=Tah?U57fFtON%+;=F&Av|Q!D++a}zXx z8Nf)7V`Sq@wSZs$j6rvh(pvsR8b@l>Ex?FSdH7|mWs)&6cKEVwCGiRwUj`X7?k0y) zuZ)4~6od#M*5Bpnqy;>XLBvCKa{zp#ycu?2#DN5IP~}&lc`r9^oUfka-ml}4x9th z>#+A&!=E}2kyq%GJLuAU-AfHz-5sP%WC7yRd%1}VC$~U4jYC|~#_~>Q1LzCkt+(^Psz^5NLe1w$7J7id%>#sbzoOz& zM#q|l)O&qn+;h*lTGn-V9P;s%gTpSrj(dOE9W+uKjrh)6hkvT$CSW+tt#2t{&~Bi+ zz%1=%Z%`v(WItfgEMVNfZr)L3{JrSEyM3M$>;LTw+$#ZWp9tJsuNwgcjp&Q@SOrbE zf7zJxA*=EbtUa+RfcB5b3UibmGkVkJm%fmoTi7EBnbZvfv|N#4o(#e(y^nh_A3>C- zWh?fD#P({H~MkvY9-&&~E+X z&OgMdF9FPNaS*Qw5>r~yK9$f%H`fw?DnC1 zpn00lOd|k?(&^q}Vw82ftycyrZB4NaM@tf6Ovi3R~Cl4fVNS$taiS8mr zks>=#&Y^#SGh7BIDl*z`0T0t-^5q z!m4VKzbY=~KvCTQQu)MhM)ZX8!07a7lJaCtS6z{E!y*B=iD=3bQF4-!+u}^KFMq$k z7Jjn!{+*~n4CQwIQe2WKzP$Y2Uy-c8MXsL_E}fz|k<3ywTTwS(x%0B+ww~yj_fn>@ zu5lsWzkQ?_(EBuLVV~)F&cgTb86C1yv~ZP<@5CBBdtINMgg+GLgF~peIjS$ePrn#0 z%SAs_D1YlTXvF@wuws0B7Z?8oP1uDupY$X-mLjg^xYa47>PZ5VIBw4lYqkp#sQsKB zeSepjv;S^2p zcpIBG-QyP`TWm#S!cs#d=(b3P0BBHPcS2WJXHAS-GfspVExf)7cXUb71n3u34yb-L zP!%+kSS!;!xTP6F>PEv?Lad=VHcE6Mx)6$Q3>lscks==E5 z)7-oijs~x-eewfm3wO)IghOtRv2)?SQqo%|l3B9S$GWx0cMlS6g-)JGpFXQSb(cQ# zt37)wjgPFwzmq;st3A(^z9^}^sFA*GuD$G(zUr^N`X+t-z4m%Z`sR1-OzI0mI zQvXFQQSE@_9707#(r`JP-6HS2Arv}Js^qv!1j2lG>@@~ICXX9@p>P|>RY&oGj8q;F z5($7meiwv9YKuso1@2Qc4hH4(fZ~T>P2vhxUEyUP>gbVs0$bTq2jkz#*qdyrThjhL zK?1G_q-2gb|635@^73kh^Rad|G>}Zgav$v6pCO6p(aAp<_E5~*XL_Ogjb={^{rG+H zaJ-BlJV3`X+=DGbPlU#WbxOAahj{tgaXqm1NN$ev>~@K*r}m;AT1tU4;AI5K@i|06 z=U{xL1Bz~_j10CjcmvZ1k6uN&&|}ZD(^|xwgOH3}4nR&QxsO*KP+}o><*vuX_*>*4 z;ve7sg{6MF1@5G5Dy&(~%KWE(v^ypDi>j9Z+X6Y0?V|hBAgQTHspDR)Ep0Y|BZa?S zI-y?frU0wI>!sZP0_TVkS1;irVN{32@bx`7`p}yOK(#*jF}+{Qq>4=kEqm6(#7Jk#St%%Ny@AHfmB z2 zE9#;$30MBaOy&7X&vB5+FETMK>5huL@v@)J)+?AV}`AEUwoP-a>>L`sC`Hq(9LMBI*1 zrw^;hG7L~IA5I_Apk#4qIcZ%jU&M>T(_3;Zd=RBRP3pec?0(>Q!e!PjP{zExJugI&)`+DJ?o%{s`L^Rb?ePKE71CqmihX@Evvub#X=9N&#$0E8H| zcq0HWdGIh+f6vR7Xf0fjxRO8WEW%mIkI)*dhWFvY#j_C5CYKWePiw~!PG zJ2$|c#rXSUpXw*cqcJSFc0p_Wp%`4u6{u z2&n|^O<+N%$m3p@p1oHC_^Ab!RkrwSHme^ZiW*x{{}4(k?eICjQnia>jyCV!_R`)9 z|4XG09Oqi2J;GIZ*k}(jJD?PV)Q|}0f`##z;{qy4<=(Wva!`tIjE-Ex$2;I(p9N&d z<0@EMlXg^IQsA?)&cjdvy5oMDpH#|~&=Io)#WxH{wz6(mo;I5&aRUQVX@TjdS}vMxat(M1Bs`&99$B2B_{LSgV- zBIQrY$t-}nv7UOL*KaQ`VCvd#YXR_-ZvMIe1Q?&Eh2!(ZYfY=e^AI05)#0%2F$RBc zEwrMNQrJP4)FGTFYqNnr ztNNbNgu?Q8062Lxuq-g@LAxKrB_bQ&B|oU=a5k)_RLcVRJliV;#(m?#g{^)5+M*uh z(>5Ix7{&u&F%AV@Ay(vZ-ok_cBVQ$+Y-Qz^F4*oHve8gu;kN0PvqA!5kw7XIeMN&0 z`^|G+GH@9J0?a+t-1>4ABkrFw`wjMfrleizeV8@o^ZA+4Kc>Eps7oViEahCOt`8kX0pTZ$zh_Wv)YANSIv7R<7*pN{(Hp;)q6Y-<6Ak3g5EfHiN3JE>)kuAP4->C z0(pYCtgQE7;DkJy0299Vy`Gr~LfZHEg)U!}M$($hH@ApIip-7v zkhcW2EftBDr05y+)$v*{ZXAJqNBn-OTS3& zexyW`hXpXa!lsi;G@2PdXNTwYg3wcwBLjTFcx+=LKo-RleIAac+=`y^ySO*ynC9Co_iUqS(M$Air!+j zbq~ZHAR^X>5UcB6HSG)?-E1XnOZ>VZ58pCOkwp0Iv;#!#5Owa&Pm@1b{ET3NAcNJV zRKo$x8W5_Nz5~2&Lz~_v+c73od`g9m8-iLYp>V4{F0k^JaLtHei0?m9mu^=S1#!-U zQQ<7&$Ijv!ro`UQaM^FO*CBdMgHk{QRkdsX^Ar6^{>1KLZtL|Ini)^EwTqIeY4@Rm z;%QVfEPNA4SAAjAUa4sLFZ20MLR`qcxg|5%El~=aKU0P*r~0*Vx1pk2CImfd>pF0c zr(F=k>v;7vdVZ?t@~)7CIeS_Yb3b{#-?>Dh3ivk!1T0 zokReJ-zYY+VbEqi@G{`{5`j=kAUP8+C5G4aJON;gCbD^LK&^Sp%d%n49k zJt&W!*j`93l>(&&|3~Hgo$rCKKncei?WiXf8`?Ob=|m?hQ@2F;6y-%-rALHV^Sjjg z3w=M$xAg{UpSP1I(M-JWFP(#r4RflyU)7b?Q~gfKY)iTD5f~}7bK(6<-+L0-OhH+E zJWf#+O7F;D)1-ef-8(y)ukCD;Gek;NXId)|3P>0!q`5j=HkdUqHu1VacOB0 z`zPi8kpwiONvZ51ijmoN_;Jj3CdU72=(Wsg*4d#idb>~3YTAy#SXk{FYv46zl>U$V zs{p=JOEyN4u+~01=iH64NcrxYnu)XgSjII~6Y{~gI{!QsXJbJ7Hk856LJ%O)Vj~?W z?>9BoO7X~+n9gcCOWoJTX&}QaN~SfFjc9|}kSX6QxHfzlH=q7Uu|)iX3ChO(=ST6= zBWbH?SA|!hbHb0WFi1-SNxMM@yFK(v?dungA(K`;ZUE^gd)rJxLW0Dkz#bO!J2SY7 zlR-gEV}G{R4av*b;)nWi-jWzG*yVZGoV92;#eXhX&$ba#HW#e|36}tu%&DwX-%|Kz zSL<`p>EfH?^8a$4o7Y;;R6jeZw8`0q<+JyqPaO+E1$6v(ADwgszcVv98NO|>SY3p23{vv)>Pw!sbaT0TC1fpbD4w-V_(FQKH=+{ViJ-+H1M z#Gj1LZP%hk0PkKJKU{5oxio08G}_h(w*|n1qzonIx2DSOS4si@%j3ipto7sH?>v1G zHo~Vj-O+xF*a@w)ZDX&_ZwOC0T*{^CX#2ORUHJF8rJUGt#$?R($k(cclY`~nPlPJ_ z+8F%DwW5>oG#YEivzCV!$_IWw6)*5QfzoqI1dCJB4d!Ko4QNxG|0Mp5L4&IK<5P87 z-7Sd1u*BLDZ1iJMPAEY}#O}Y#V3E~mlttN(x12T8u@TJA^zDt70{)E!OMj0xm)n^O zz()M&9^f+?c5KOUB(dO+S6s7|pRiNaM6PK~?oO?znENr!d$o7_a=HT~dl%Cz_{_hm z;;#y6b>`9Kzcckk@83S}qP?TC(Bc`}Fgh$ho%Z4={+^c`xmcOKI$a=GWgVwapBt&T zEn+ipNHQwxr}_L(ae9T2kp#gr^G|7Br1hL+!xf^UtgNJ>!lq+!HRY~)AAxG8N^PQj zySnwaYO+MpsdYt77rFRu9gg|MZvCK8kEUhKob&y1wn5APc6e>(d9LfU>$v5DsuX3 zez&$5q}bC$L|0wNggm^?46CJE+?LTz2natMh_%6*dXq+YN2GY-nED?r#{Eke(VUYT z)B1hIFaS%>J_3mV=KtLjk>*)%fN7%Kj-TF`AL;7S{89y}5v*h*nWR|$;W#wQBCMB5PNkTlch~%`VpNX&x)woc}4~*HQh&Ep+@Y zs-#rva<^&L{c^8$v*vQ&t)8D&!BA}I@(}ms*zsskxc2JrsM5jJzi~b3>*MJM9@i&x z&b8O4i~a}KXUiedukq{eJ#NmoN^5T}c3Tc^E?>#6@?9OzdJwMho3(_StFr?FfruuL zh(f=Mg#9oSXq-pFQYHkGJq!cKCys|GC`~?4Prs*GrT{Hs zoKJ;i;h|verxuUPr*Xz}u}mID_6<+K`lEWe`68nK8`|p&dXH67ZllUlEa_Xb!K#mL#P>sK{~G8XsJL(BQ5d5fB

<-U4l z{>#4Wn5a>K`R3RDFXu3>RDIK6EO6mpE`GFB^K6JP_O^tMgUM_|;I2-Hl+o=1lA%>V^4uet-&t{7^bVeP-TO)U z>pTv#cnr4ZtROdfe5+77o?0QCR_FI<*>3qR!Pk*yT@(DMuV94|I47V>}93#Lmmg1CAt4llU2_wa@L*|jz5CynmSQh)O>3pA(I3Y zw7@|)HYF~gWJ4N{^(bgOuV)|^>i^RZKgtZ~ZwjB|RcV9=T9^&bMv+Y_d+edUeVO2G zGofV+qK&G&cEj_sSGB?NKjy7*_TY0SJw7;_VBPw3 zFL#{uU$w-i?*Tlid^F67IglW8Ft0rHNV8KC@X*!3db1?T{c9 z-3MI;hQqqL5iT6K$;~BWKC_EosOjlJ(s~kT_En-rdXP**G>M*!E>*#`11niK9I+OPE{MuIMOjkzHpZBRr*Pyx#*+bA1!VO)&WY;Zp@^bU?X0s1s}&5&#g^Y_){f zXbBFHsjmy()oOJbx)Q!5zvq7am(V_4diSK_(e*YZrRrmC^aQEV1LR0FdR)TPi|! z7C-JNCgDw&jpRA8*gj7p2(v5((wMZ7+7nk%-PF5xF?p|v3ZwLS`@vYC(f;R?x`fZW z&i5`C1{z&dT0ZalhcH?Ey*cX|h|zq)bG6Drzz^QLad`)E+(@oH|CZ2sTzc<%`w`({ zZmjdPW$b!CWFNg$yF`%c{iR-$Ot{**_vK=C?B?_n;rcM)%hl#R!o?554Swv)&Dj{? z=JNIyW(_5|)j_R7$pb=x>7i7up)_-$bofv(YZ$$97^77fb3hntdKi0a80TCVH$Du? z8qTL2E?^Zd6c8?w9xm1zE-@D_g%6iuMae0n6s%A~e`yCCP#pEN1;V`JL<*{9;sOk4 zq!k)DD5C44pqq#u7%#gX>4F>zy!nk7NdcL#U}u+Zz#;{+kYeo<~oa&JX=Ee_ektfC{M6)p0) zgiR^TPDlxa<)A1LZWSw79wTPil!@-2xH}PK{S+j#jQ*tLF~mT-mq?2xR-9Las3O7R zGRA3hoR#>P2Ibf$tJvm%*j5*Yu9VmgePy?CNKLe;;5abhG{kq6L<$P#GzmtR={)s$5{i4~K84ESy$prekMo)`F49};s&(p@H&aR~Y% z65><}zHVu7ACpbW6%GTCTa@JE?s+KD7uEVVKL zlguTXtl*mbel@wv7fYR=d_F+kQHNFYO7XQ%(H}}NDo-((PciwEVtSrp_Bz!vBh{iU z)qFnn{&}j^P^xWt>Jzp!CxbKx(X?k7Y0l+o56cq=OgOw$QVB3&?KW(i2~$A%d!M1V zOD1#yeEebOAk@Qj65jMEgLI5*Iw3SYzC1l)DE*yEMxsGR(!<->vW(2O^lY|_9M_Bw z85#L)8MzNL^B!gr>gaYt(WEQszo6iXj7*Ylk&yY!(D~R_N{xDKmgHc32Zp_OLaXyU zD^NC@t!o!>dl>bn`B!me*%hWaXkLEExx^O&RCe(- z`_RK&lq_beEq8Z5cmF*1knO`?l@HgXz&5=P_>2!14+Xz~VGok2}a{6Id?&a$| zMf+@Edmha~-uXi))pj0zOzcvd#&_9#)z$<7Qwo=HKrj$u4~rJ$jj_AU;5rg8oq@~` zc|T~OAlMc}%ToC1^X2DJ@P%ckw&b9@sWq%(ea1PuDLEAvm_{jOSgq+tJ)cq70jXaX zz2s$Se(PFMYF6`u3tUl)WrmwG3@t78zI;omm#sUKU4KD0_ENmdIY7 zq*{JlRPxU}!OFZeC6Hbv@1A^nxzGx^cV78V@(NnhVm7amlJ<(KOvkDDin;~*TDHoj z3TB9PC6`@Z>u|__b(NhLmD@E`&ruSaoU&|niC?>q!?)0y83%y zH33$A2nEfHfqn#5&rpH3$E%kss;8|%q}?D=Q_${k&FVtU-UVojo%PTbyvWXY7NdG@ z2rH7Q-Jz=4wXWV411}m@?}&kSRl!>@@QG*bjacpF^V<1g5VczE_HebyAtkjDc=e*z z)3lb8xpVp)Uvf8f?BO;WsT$umY<1rWc3Yq zAdPZHEI;dNx2T#{!kS2YYndu*1U`X39X3Jon_lrY(bhMz*nJcrzO{OSpgWCT*!%ni zbXG5z>P(t(2&(JQW?=qDZ+Ii=1c}T9NJ5qx=t5CO1ld)kRdgQtcXM8mI}-7Cm@#Spm7pI0@f4_%M{F2g8{pFQELUc5!g4S{I1A;c~e8iU_oo*G&p8h)@%&YdQi!ACAFV>QV96}kqQ3)NW82s z@+!lC4D9BEhdh;${|6FpLy8VcSuAq#T|DY_6Jbbea19$^N4_R{gwPZ zC+u9>!KGHS^Xm?Zz$4xH2LEq;B1>2+<9*jbr2o@-($-h@GO*wp@+b(F!{K ztH(^ar;?apE+h}GbbhO1@`c&pSwAVL)Xt!uRTktr+F%diN8S#NQ$ zA_?{%o#LG5nw-)P&qfPbLN-6Rfi%C$WR&~tYD|f9e)k!}D>Z zDo1_98M&ZKO`G4YseG?gW+DlR>A$gb0*qfq+jtPln_9((9M&q@DlnPWBs6Mkg9)Tj zS_6Lw>f=9-k!xqoR1xB)Ew3KL1n9+n!R&I6DCB?#>A8N+=7!}4Cv}4*vc)=@oCtYjbLTfjsl?xuuJ%0)PW%dEvQ7s_v-%3VYpTsVn+)8fpH#GwU3X2(0}^t{tFRIXUD}jEW#&0B zl`7cktBc+y9l0plnAn0UBOWFNr3Kxru?}nNGvOxU9If63)4FPVcD)DxggY|ZCq*I& z4OpHSFeJ)TTl;UCBg6%ckp+eW1sC~apB&!h4HVHKXHBAF1>S~VL88f^+B95L>lCi6 z(ez{_4bO{UPzp96F_}GS7#KuGz(+8U7nDcij(CEQ(U6jVAPM9uPe9Qa2(U_-X0ra6 z#(Dj24SwL*sjBY8!ysHTPay~QK-s?4zpZ|F2XK^H~U3u?J6S)=UUdcqb5Wgnn4QD0T z^@pu{L*wJ}V7lOLm+1UwKm#x<1qo>wnuIJWx=YKrPCraQj#pRTp`31r5U%1jkwr2D zKv*93M!ZYv}%!?+Z z;XDY+0y-%w;0nccPG@>PWZg=<+nfX6eCW2^#I%vUty2}P0gdc%zDnnYDfEHzCd@4{ zUZG=|hlB}uwsi*Lz{x$ib560b_z%<;b2kV$fue1OCMv8$ptZ z`+na&vLqQzHh*Z(##Hr%Iqm8UaR6bwq9*21g<2xL%aQVfmT%@90{d`q&>SiM_CxxI zi;aGI$U%ObaNJt~0LKhoOh|z5r$fO#c)zTWbvtIn@0iRu24%7VUAzm5hO*f%w&(FmIB1 zLx#Kv0Bh(m&mC5Mf%Wo4g_}aG4mFCo^E@G1%n`unwF;976-&3QDeFoJVyj2cQ7i!%g}voCfkW zavboV!77h26VBG#WW{h8{(`qhz=73^W<=ClyT&D4kxLNl0ypD*g(qfb#bWY{x+!&= zhvweW=to3{%`jn>bgPhp(!u&wk#aSuz84AiCEgn@f1A-_Hw`uMrso@f=|sj*K(09| ziJYWU&}Z$QjdXP2h%KDCAm@AM#uPZ#nW-O9C3YYJSu`8Mzd}4YlF!Qd%QFE^?_Q*c zf65xi`pYUx&iE_=AbQ2KXQkIi=1rF`$j@OwxKI8!)O6~J1V1>8NVZe;1);z%DV^;yqc;*R|GNPmDc91!Dm2f+kX#USZ`%nf$l zn|0IN@MfSqNiU&&t!f$5=!0kwu4zxkJ!X_09Z2Y9 zhMqrSD8m#N2_6kiR0o{#@gx^QourM_JEPJ!tbQSn3mT2CJB?<<(DZ_Hal zy!L9xcJkh^SS>@vY=xfTnNY;dk9U@gRvt+L^xrE3^M26R?Ca4QxVIg(OryN?Wl& zELl7#lfw?u9$Ec+3+r4F{>O%lrE)AXCD>2fUlS@3+d*OPJ@*kG_ z^I$VJ$OSZoV3KO20yTR@eb0br{5PcPdrB}ig{qrI&j4a(5#Mn5_Ms^F^KnQc<9iy@ z6soZIfXOudRZ91dkO877?Q+mG!jNPIG*^^rw-T}_pNa>A4(dS1hjgc*{~e2fPxC;% z=5)v78IKG=oe~|n+zcb2Xk0j5LOpaPglprvCah1Iih&>Fy6pG z%o~#d&Swd91@&aG5H?Xqd8thK*@8z&y2QG#7=w)`?&7AwqoQD(EvOfe@_Z_j#w&-7 zIQO=_)qDV>#S=YWn+L0a)5_Td`+c9&bmtOj1G>PV?Q~O5A_LR^MbW*-Gxh&*03WuQ z>o)hhVGOy<{gTTt%>6DQF?Y!=_mXYKM&vHHG?!dTg(OLC%{2*0L_$Kke37JF_Var@ z_BfCId(L@0&f}cV=ly!VTKEk@9V$EE#(l*XJGuVi;1@yojR8Y*E1)V{?3k5r&a-0X z>PpspHkv*NujqF_ySX6SDQE0{+FM9~5fQM@ zc@}QMkOOqBu{4aZ3{6Udt(3x6VYMzv#lf1aoAXuH%%LyyC41X!S`^^_`Z(j=x%uZ| z``av?+iAh?_H!x`riGOMg0%sZzhT(o97Uf7 zp^72!4!+x$^x?pLQ9s?&+h!-6&-H|{p2F_OA?JJA>c$xitF;VlVsom65h3g*(0_Tk z$*u7`W3WBiE!%Rr>Zrz}-%uw%b#)T>ld17BTjil+Bu5$7J`Nh02NUemF|QLepn>R* z`SAdj2V<)9+brmMLv0LaVl1w0o7t!yJ2C2kVpm54)ER*+G>zg-);q#ZL4#5}+P^qn zE&Gd3X*x}nyQ8_(qrm8Y&~!((lPGYlBU?GU(HmdT0U8Q1;b4J7h|7?2&vJ;=y)s!4 z!?zR;2i3ULG;B#0VDMktHDG=~qcX7=S(r2u#)1a$?SL_CaG8H_;a6#Mlda5BTmf09 zQ7SC7$?TNcV3)GzWVE&($iM`L^!UvK#Qd1WP1v-(;A^*Fx@Tah!>iYsifEI*j6UW! zlQ3sAdy+Qn3qqTj*!0U6L|lHv@a5pRZ}s&r)Z-8uC)^sw!>(CTH187oY)9~mqoiI2 z+-3BxU6OyVa6YgL`#cMlWC7J@DQZUBG{@^WMOEh$n4?8;+E=QL2ZRJmU{T=U{4>?# zz7T}VvzuLl!~fvrhmvcoMd{m6rv+H%DQdnA{WZy2_bR?_&7~j<)_MabTn>E;05g^$ zcnurajSg#40aq)==qy}?B3k0h-gyWG!T>_mZTltF?9ZMeyG(6c*v?!{us92wTJz7J z_h2Kk)H0-bk!K#pj<~W!d$O#6#IJHJKZi%n!9tM_rI@-o^IKmm2o@t?zxvF6VXWOm zJD8_9C;UsTK0l%WATv>5uKnKS+On5>y+Plg8DF7gY%h8$kcTL^LfV0{Hwwh5I>VMBzKDY*}MB)bo1ZU%D;i*UxCNxLR}TJ==hp`d!&Q_m~xQrVwg?yvMxKBlXge~>r)7f~EzP#w`dXf=IC^UKROEcB5nj-hCb zFk?}F%8`~q;PA|bX_mpb<}*_`hb!?T$5X(ov>`DtyquG#pb7dxVGw3AR?iNvVjHnl zeBt?|+i7TwnN`MbFpsg_10jxf5J&x@br`hzeyQT~WyJ%wrK#C4+`V%-N#kwVHy3DJ zr_GA1XxR8GSZ~ao*WE?;?uDD)dcBk_r=%|WcnZ?rma=g48l5!3B6VkTae_fe0}hz2 zgp`idy!zQ(uyvGPeeF%~CR0-y*YVJsrlbj+FVH%+hWYknrwJ$f2G?W_`*s^8yPEw; zpXw*f2>b2AI0UQt<51z_ss5?p5wo(DL+(1hA;j%z1Nkz=7q&4aTpv#1j@rbZj7GRt zPNa_EsPg5U!|u06$8Rw?aKn#+jJvnKj~jVO4aR4}iZh38#b+L!*Zu3n^_V=xW`dpk zbLOA#JI4z;>+(Ei9|fBU{M7)i)zNXD{ITRG@I+CbVQ*U15j17-oyZ4RDY!T(i840| z^H~NxC6veBE_g8ZPSX6I+q1D5m(2L-V2bq|WTnHfUKs{|@0tbkL{KlHY+0hi9z5fI z1*lK@e5?}$h2+- z=u95lJORi$TT(J}j-g-{aP6hoXtLqiEeyiQ!!e07n(;b8|XZ7tBn z%T6q6LaoFemVEz9MX*r14!maq24`kOu_P@sjf}&3X)4SvTzY++1~BNWd((cN#d>{V z#@pv#m9z0pUQlU*xu;p3X<_cg82Hnd+4b&j+O)Fg8-l`;_-~BD3fZ$UzD`jP)^&SV z6ButasObVcbaF5zwnuOWEG_}Z`)aYlSy-mL*!rVNU|>AM#}1OudAL2enf-WO!{SGa z`4tZJOLC6Wc{b1Zz}cszuid*Cu>#EIXPr~YAeN*A1Ta2&})|ES=i+}Fv zGnf;%9~^qEr1Bbgk0`e%xbtg2|G!bSO4rmeiIvU$7Vdn&e7Hl%-<|20XUi!J!eMh| z%tangW28Xkfxt@HP{A#S+f&xY2N%BX7eU|A3;*7doQb-Fr1bthtz*MZ|CKWt!>0bL zx%2N{uGrRr@A1l32VKZJiF5d#U`G)+dj(q9`!A%J>tjUZJH$bEq{3Du=ZmVv#ACtK zXHe5$d~V1NE;ZJ}xRNf$V#^Kp0lr~FB31J^-3_j2UPhg|pl z?o}2ZfrdbQ5s)=IKWLIUwoSsHlL+n=QU$F1_X$G+aY;_ZS!F$O4+H+*C`!1E!i&!vWr&^M4Hz>MwJ$!%U;2>X#o*_i z;}9|NW_dddD-y~9H}fTFp(xSIM}A1X)PPJx6MTJSxzfDx`-YC^h?GssJqIxW|fje-uDX1}Q zFB8eUA)P6m#<|uQ1l+LQiC|!VM4a!>;F5OIu(v|p)s)eUd^`NwTIWlZ?Y(fE6M~4UbWqAKXPS2GAG7im#Bl!JTqOt3RpNCUd>+kEc7|k z7<|3~4xZK&1_+AG)=1E*E$>`^uM|(iLCc2`*Vyp~cjCqL=V|ibn85Trid}C?pj4v7WUFt+(j3&Z4uv(YeGPf#e zomc-n;`x8t`XC)ee3w*6vG@qJk+I`h4dpBcLV#c_H9E}*wjqf)`AfK+=wUOLL3D&1 zg4|)riY(ohs28zsWz8R7&Xt-@kOth3CY4>=oJUDgg_AQ8z!*L1>9a2HucSV##*b$d z>X1lFY-uDRss%m&i~S0v$0vWwEjjnJ_-+Jszf4O@;xcLA*$di$)rroX7PhX?dnwim zn}3PrtOv=5k&kU>NQqjb$Ya6X3*-5FzRii3Yktxb`h0_O$zU95CTiTPU6;lbNOdJdyv8~g@+>0Rpk zH@&dS{2_AyLk|p_Y5)N26GdP%P$T(tSA+XJ`XRpCH~GVfl=kj->^>{$@N5G7gGWEu z7Ld)CjbO3cw&ZuDO~p^Wy?eYfDttVvm6F)z$4Z?2B;@=yt+_B@<7%iQRQ+j|{7cDX z%z&s#TP{4D2`vAFAeA>s^ymAm*GubDr8|-W68$*fz97!_zL%2CeUkIr6vHimqfFGM zkWbVgq@BjfktogVIfCKdcgl%jkE>jn!Cg!mQtlP#a2aL;aTDM}pX*E&Q0uv6!GeH`1`#U+?&exCVKfkWeK|L-&X?sWy2#`Yh6#xUIJxOw z(zu<*>=WJx7GCaGRrhs1ABBO|)B4o93Q30FwK{(Rzk903sbfM-l=kF5rGUOmM<$wa zJe{$l2FAitg(qZwTPMt>vTjPuf7z<^8WUvWB773sAh@!JB`IFG4dR zq_=l&I@R(Ih-eE1_+mhD_a4=Rxqn0U3c~(9g4Hx3|GiJdT{=dwAj5X zhT&;zxpTvEBaZkKn3K%HJll?@xpU1jX>#q|n~WoTwGnHOSUPY6PCj;s@m1J1X!OXzuBCx7K7W3XR6L*v!(QL+R%k!y(&h9bnjM~~y zo~D<)kBzpfWR+q-Wl$5ooVS(#$jteDS1XGI@;}p=S=&Mc=c}iftV;1Lk6HjKKKV=- z4}_!vnpsnRlB3@8I{B(9PC+vPrYKkUDf{VZR%bwjAj3dP@*=6gf@3I`n7wqn*T^}< z>S=pjtr%A5?zdigTx#n8xYaT2f;=Bnpa%w)-vZdQ^d*Xjr7_D>0njBABEO^8zrBD# z_T~wroT7hH+Ud)(1j}csmA*0T&w%$2D8LbrG9IF#Mogntq!Jqb+$fw*t`Gi4nZ{GE zG%~xx&XVRb5&#)ED%ff>9p3^xPXl0`!O8OJhTAPEAs`b%#ub=G*xwA{MXfYizeqM! zxLr$ZJd5a&fogl9m_@~%p)M6tRT1RWP9PaqLEh1&hL8%?gsdmeB=I~&tuk5oB2M4n zG&)JdJd(x%D>?)`$HJzs#+EXg5eLX)q(rHtBET}bU`Jt<)W#iWsY(#sz!1-U7AmqL?~@vxqU1O;S!>i)K&!u zA12LY#!PUk-Fh>P2*^m?2FK`9D+nC_UV(d7?|T~5cM&+4KMdBMHSbz6dpjUVmahvZ znEw+Uz=u}kz0LgalG?YxqD;Lrb4YONFbffwXxJc`5|SgYs`P;Ep_;b8hLfV?RTw0N%6ip9QtNA(%N59Jnl@gM zs{CCc?pnSZCpi@`Ohm9UE|N!R&Rvy-pfDVS8Iak)qV1|qFL^ecI>)J_Hjt4AzM2Of zI?qZR@63K>St$Zh1`cHN6*BdcqmC$Ej_)*Y<`iSM*#^0yQ!W$^)6k)p;@q?=7?2slIqG{anT>LV4`J)wV%#pd1a6u zKbaBL#%gjv3hp9!y_-@6E>$6sZ8ExwqyYrvNe3g*0L6JZq~jM%0|@ z@!`OLp6a;Y2C59HDh=qAtDqiUJ6mJJW6u}`uJh3WlA z(3tPjuOXsA9=z_DoCi|WvXyjyg0A-bqtY)Qx(3y5et9~KtEvX%HBhTq&ro}hSqX`|%s8gR9;dTVQR`)5vB?rP|#5Xtv-X8zbwCEn&# zhGD&)ydw%RTgbE81Hsp%z(amHMdI23_#F9e$>jrZjJylb^PKD5#f(Xtl!NIEA%pY0 zDHZX~5&gwX`{)l?BAE zV=cqu^ao1lq&P`@N$sRm&wFNg_?-*)vpWBt+3Sm2&zq>^^sMEnR9uE(^h@G)IwP^H z>6tKz?L84fvFU*LYg288C=W+2q-vvhOw(#gleN53=BKL&ygbG7=Q2LX8A(P)3+Em+7I`sh4h2?K;=2 zM>f(t0{k=9(j}-N57#Is6zlah2K6B%MUI+zLb-IZW~)N|=?$?C3Ao=6u!dbAHInZd zq$i<+T~11Umcf^OFUCuhAJ~A@)NR#yUJ^U)&KN+D5)e~I&x}rCoR7I}L9)@WM!b<5|9OS5NISl7`<_&q%@O$%eCa|bSqrQm#NyX^j-?sa(g8Cm|}`Km3i&0 zn?;x;AaVz+0tb{lZAgdFCKJ9SCj_@{B2kEIs+y{DKygE-sU%KP zm&k~gbkayDai7R&K4CSIhs>Q`j233nHj%hZ4EBKF&{UO>G_{aRBoA_`FNswvEzOsC zW0`@-$PM#eG2(Azhc)H;x_A-lCroyicb{8yswL9PrY`~{~QTXYXLO*T%xwTYAv$o z(al(4>LD$Q3vLspsDSCEwe(W3>|}c8-enLJO!A1#!mjVAK$hh|AW6uFY^sxPx)Yje zgkzQU0KGP%3Lj7`9e0DZsg`KSr&&wuAB;A6LrF8KBSsLeI!sp(5c!bs2SyNQU23HqNE!8w1R)Xoc0PzvUFOKaKO^kq ze??_{BQ68fg{a?rAX>fuY5(Cn{`ntC9vmGKsHY<`Zb7~OWr?_rD+-`rX+!v>33X%y zxJdrYh@WZEW?7(-VDbU(hK&qX<^zbf@{352&4j6y;37!-t1x`^@bR2m4E_sthVvL-~P_^kOx z0rOE*g@zpg>XmfD9ni?8&N8mChG0h{x8Al9+NyVx{HamvN zgkahbMXJ?J1D!xlj9Z%sxv4^hZWG`PRyXudPx(-?t_=wXmcx;34gi9(A(OW#g8aagJ0%kXRXh)-kWqB#UDq~Fr@Uz)HEmV?<**wzdi%Ih! zPrTV`nI%uCl6^fAYz1~z2ALAJ?2~mUR~VV!(~J&4cQTR>O&0K?>`Vrb zTe&^)(wr%MQki|ZJ#WG(bDP(+f03eMGOlmBH3s}h4JvY=x=dcb+OSFPS##(KLRtQ` zFakC1pQPM8x+J$blKUgOWIqT+4YpVxF^L=`e|N3iV z!VCGBYy8%Bneo+sdp~%aBA1^R9F~{N0G;2;-Di7%f49Y3V{7N5ob>=VSu>v*8hZ1> znP?@ePCxOV{oIY&J2-ecTEzBL7#wk= zySp}aOXE&w1k!M5|NAjVah|A7{9MbiD2nUDpVaB0V;1Oxv#0B(!&gLaiHqulf1hR) zUQ9(i{mEACXQP^Pmhi_8NQ(!JQF5++#}($qugWIU;+HPJMJ+oC(clnl^rWaF!kR8uDgD=99+!P$_OXhAP7X4?Wmnv29=cUF z>1+od+oTm_iG-O=mHk_<_}BM!{dTE!dy!$tw3&Q^&1>t;2OZbT@85g=FYDW*F6Oh% zEra{k-&?!nkT|n(?$`;;JfEQ4lXsq1l{3asKjo_x-XW7uADD$^C$$h)sLRqj~h zF#n6&(5ZU9$dJ&p3hn-{&jwn@pvyQZFiNE->ZPY%s&=uh=uH{@XE9iWovF^WF}qj7 z=VmH|5_9fiN%vAPOziP@aGAY}FEeMo|9z{8pK~| zD=wHrG0iD6QDx0(b5u(EbSV33xLL-Li+zWiSBlnbR=?&{XV#8hp1Kp(v%qM^Nj=Y5 zD_<#JDP$OHJI3C?hN_S14$rTx)CT@At`m~^sxSVku3-+*KObpEtTliM)>v0B8FT0) zM46pUJ&v12nJ2{KHbM&ZHmh$K{lj``SY8mRsrN+V3K}^S5TTXT!m3^;EMWPQfVn&# zMjgpP9qFgYz387tel&PEv*VzVd-5wy{!O&VH;moK{JcFW<2n-WgJ z$=kh*MnBFv5h(UGrZ@W|E6wd{nbJHT)RTHoj$ewNIiegwLiN|9UUf1Z- zw(|iZMt!!X6BtvmqtE=#I9DFMcy%84Ey?$v%jD;%^z%7L-?Lq=WqIvev1R@FIbSrIkzqgKvs1tprJe5uYV<%uS6uB2humFY*JWSJO7 z-hjdP9o2}hR_rYKMC0w9J_eEQD1yYY)ZwU<0$2g@YM3>LkCv*@mrIC6=FxU(;>%33 zO$OU}-h0bmx-%SWIcB8HjeMnZzIwM+EFH<|G94J-*_%dA*t*F_PovLxNJ-`OO`dI> zK6}7LFCGxZ2_5-Tg0t}IEITiaONr`QN{^FySIpWQ2RIF1Pa`daAROg*W>HN)gxrB8 zyY%ODz9Llz_6ppu4?DRQ1ut_~3>2(g?oEFVm_`L!Z{YBLHe-K54vz?Tj6R&6#tDm1 zXCFm#Uuv32-J_=L-ZG>z>G*VBPTvrL=wgI&>kd}aWSGodmZGcbDtY~7VWX_~{oZA? zD@*lyR3C-MWN+3n?2ud+6udtxzJHd*U0<_kdHuxihIL2~pO6?;dF*d)^+}otk`std zW}}AkHNzNr{zg7il%zEo0Zx}8>~_9FUt7dgkR1+rrwGz>@)9JlLod|4%5(2EP*U#h znHU0OVSajF_w(AQT;Oh#;rwq@DyUgT1kZu>%@bghT9}5cKt40?OAZ}hHVt3NPE|T5 zJ^n*lS}x{-moIbI>yJNi-G^Sbj(;TAUO$XXTewjAE?)0yi0ZY^&r276BA>3iS}&nD z1P9v89@q9!!rboH60=DnCId8rsPwWF$R9xq8Id2C8cwNm6T>Ke!7x`=bI#-aj>RGU zd-{2y*{fEJ&n@Pz_lxqJG=(Jp6B{{W#L*P&ktp3|gX?8(_8eI`=;{9PzCSUU_jv1E zT=(6&@Gtk*WYe0m2F@&uEI-M8x=VIA&RQ9gO99e_8V|m%>;A3c(nC3}-PLKNBYk$rV@6xeEeUH+6k^8jW5bl-F?lS7 zxPaT{=vnT>2g2|E3eA6%UbocD$*yPUQc^BChhQGqA+q87#fM z?qO9v2lGI!z0hrK#+`oEwLQxxe+yw9>|`wn+!LbjMX>YwT-&bZy*)Y_pH(5y(VZyp z7u{4y?NV0D*R?f!Ow?sL>I;TfBk6()ke@G%^kJCn-;&+(OUBcRJSJo!%E0%B@j-At zQZ?fn_;6Ug3&WKnrjD#D9Z}(t&hmrprj5fukQ)yV26^*(^!5H(X~yrq4B0mgH!wTP zsMv#^(F_!`rl#Fog+b8a9n*+W%9B?bFeR}oat`lfL|Hs%>9|UykM2CPY+L%zKu)~5 z+L%%Ic4K0!A6jNqg6$$f$6z2!A_#3(nKg9q)rIka>(NsQuDiAhAXE18kIbSeD=gY= z`~F;Wx(+=vQr!ZL0!bUGK%cyyl|zT zjO%XljsUM~eKg`^YCsO8?J$WR=NKQ^8DW=2#c1UZ#z0k4BJr7wy+O!?Xyb45UgUr- za?Y5*ylDrF8}}&@;x7R*BeDl{h0GC-X1X+iGo?!oz9m-jc!!XWetav&bK;SDj$cZ} zu8N=fUpGyvtwiE$P^3{8!=d-xj*(pU`+5$Qznw=;hwTAncQL&8=*}*a<3S;uk7a?2 zD|Au)q58QTm$e$4m{Yak+vadC*^)5@ylw2dbu+jmK@?kj>Z}$AI2>({11F9#{)2qh3aj+r3PUPBT$pvZY1=5< zLs7vdnO<>QQ+1?u{TgOcP^GT#{70;&vwtr@RI=ukcuIlwdYyel`R7bBBNNMid(DbX z)*i=QKeL4OUb9z`b#b2V;3Au?SK4h(*~Ri%iBPKL?>dvnXZC<`Q6E*TfIT^MhGU>h zm8|EO+u5Q;-$D9DAh0rJT5Vkxq?o}*B+F~Ql%X;k0zPA4GuSo! z#d=wP?VytuZyaKRz53Gj44dmy0Kx;oTheTLjoXGSOgzM(zF8x^mG1D86cdj2K=aabR|4vb*e) zQ89L_+T>+Gfc;vZQQz$ow(RjYrkM8Veu{9*X&xJbn*b#d3vv{ z#ZNABuoXQ_eIU_3LyU92%GPu6AkEhg9Jkyrj?AwVxHtc2$!L$4+Cs{~| zhZD^#^~_tI?<)o}P@mXeZ|kSWX~KmLwY8<>@!x3#xSDPEOC#wrNZ@~ePjPz1x{q09 z8TT6XEga5BX^v`Ph1G^! z-VI=8n3;jTgJM(Pg)hdfF)+C0Hv+a~WTFXDX_C*A9tVz#gTb?C{w~b@m+53M z;sbVkDSdZo>owM;)4s?)M2H~yjazl+C;7sM&s?+0Z^FOWlJlH|-TLd8kaBbg5?dul zAD+7{5J13Ti8@g~#*YJbP9@%(K&ONIL@?*${c%6vh`Mb*+&xdzC=Sg|D}`-uO%Jc~ z?yqVvchkB7WikAzT5qG&%s>QG~)&XdhKyo?F$%ZWgdbtqI?V z-$CpAn%pvzyG8apNmDZB)i|qhS}G5l3HaUhdLc-|Uw|T1p{je!4QJGZQS?Kxlls{eRiXzsPd96-Zy9J)BUy!6S~mF4}G#@9Miy(gwt z?NW8SuF$-EV_M0E&tucK@2>2Hn!2W`*7q-~IQ;d%STwkDpUpC+hGWiOv3lvQ$F+-z zy<$4oM?N+?PxP`$D@5J#H&vQ3#)98`iLL+AiY;2lIs%A8^^R8yhvj(6n_u$bGvKPf z>!rfX905))zjcy!Ymd#80iw`rzm;kpx!V+Iu%#=wvapXYhFmXSEdE`}*= zH{hkd?uK`Qi&pLjpI|R;#uMc4-y2)mzLMH_R0sZ+LX=8}|68=5aYq1eWPn3Qphkr6 z#g3pzhIVj5JSeCmn9>oFs~!6ET;MJ3Fw0Atz{_DzJGx07mxi@H*~`M;$2Z2@+(`au zyhb?DGB;Szj%v8O*ot8tmwn$7AB9S=HX005e0t?DSa18Hd_ZU*OlO>)&ks> zl?#{vf3v=h_}o9MIfM~zg+woMVxi7eQSW?p8wG0rIR(F~@Bi4Vvpl7nY14>&32iBi zKf?cb7ZaIf(P_!t^iY;g((ogq!w1dI1KBwh zD7lHCpg5eZZf3)Q^)s>R(=^|G-RxKI^sz5>UnFLKI*^?YG;tv3eD2IWS8!Gbo6FG6 zzr&cx6VxmC^;=U`udpJ%&^YO;lTwjGlDD{C@kPCoV7)TyXC(>GWUD)GQJ$6N{E-_GcckbyZ7R z8bN_6lKQneUA5&*BWRRb#)}r=GzqTSuLXD>Zlh5h=8;lXw%=yb|PqikYh70$3Y1i;YBX6 z+XmQ@%GMijAg-)9XSYdt)e%}=)}$N2EzqA_A81$*Teec9dh9g58B`olPT!x+naf2ij{l^m{=6w<}? zqe2cBuxadwS>&QaM@&|5LzG$U{4e@`Y0^d8CaxKi>rbA%E~Fb_qyFi>_^N90t+wL4 zoxD=THtRY)-Te=y4iGmMbU-10Tr=F)!HT{8WYulV8-Z&40)X3cVAcpbxy!v$Ahw5Q zA$7}Hl8i-I05K2I8pEe1oo$I;=v5ZJg0L;lUmnT!#MQe#D+qj5M5i9h>|grS3n}nk zpLtdo>~P1J23UR8n%$IZP+LOFX8lH7~Rp0EZ7bF(f!5+rX@UQpU*B>iL-`R2@ zO|iTCd$#zSM1msSSS-+0Iw^cVj?eJ_habc79nS9Usrqz>*{gMcKAgKdlR7?wH^@M% z_dmUqwAwa2cQ@aWVPO6@K{#X~r_`yz{Y81)rT35gQ0Cuw7W7DSCsVIRavCpZeSk1G zG(KNGOQthchF*wxJ%7wd{N_`2^yxdP?^jd%O-Iqll95z<%TDi#$f|XJ|JbU5I-BjK z+YPsj?_5tg{IjzmXzQ=yFE_#dzVC#b@G*1q2sEwmv zu>St+*l-!L3zOgT98-}=J?TeDUULk{ALKhuavBoys)G-SUSKpw5AmJ*4wvL2A|Z4{ zrqc9?OuntNRUJfAor64*88fQT)I2?QrghxehPE5BcE{i(^c4T>I#M53<51+aRa0>O zHLm@_+r?hHX<>Te?9RB$8^gay#fql1oE;}D|8LZD z{^*L9H+FhIX5KoOyl#;h#09=`ZFW3vm~lPl_It+BFUM{6eCUIBvtH4!-QHhF`tk1l zMIx8`oL{E$?3{nTo%?)X+2z^!;OZRr51~yDWA+n$A++y^o^83Xr znabdgacao?j|nR|=a!PbYH2@8-Z6jBeC@CJpQU%qKh7-^*%H>|7#s*Y0-E~`5_|o;nqDgcgMLC zxQeWU_5Ns3F_HVs~*9iID~w zp?Y$@T-uN>2LYSFvg+8YS8i6Er{*NqB0^65C=KliZO;+^C9>E4=*3y5h2Ppg80H}h z9IF#^|ho$|g$k}190^p*dyWJIY+tcleOz~!QQh?LbtNvp23Gj_ZXJSfq5vYdYWxY%Z_+T zgc^zS$e+cKL)c2fqwmmB)@!(wMMRycyQI|u&!m~9t}WkxR}3w?G?KxphVLMSzu|== zNA@c%PyM3+CB}i72@lGYqp1b;wH7CzNQK|Oz-C~sSi`L|m=-{9K>XbGv7T%x;6vnF z%g`X~$?Ai}i7Ik?@F{1bc&uW^C2eDB&4fn{7yGg;>hFlB^?pmA8Z(=qN~oAA@DFsr zFM#Er0tK2LlfV5}P3j8PS!?cnAwS<*4#o4Od^g%cZE=v*A2!Gif-JU8KCJoZ5r#Y zWZG&D@K*I%$%uN|DM)>(R{9!Z60G?f|2`k9jSY&wQu)O zI_P2ETU&KN#&WtT|2=k6CxMqS0%BrNs4!a4nt*uInz3B1qmG!_T|TK5W4?ap;_U_{ zc3DJkOmwm5k*lmFmjcep*)a?4+6X%DZK9rULUMglA9weg!;mM;P?IEVwP|8GPsn>Q zQH%&1gs>HqwD zdS2UX@e^xh2)pKcmonX5l!_xzAe~0FXvVPKz0%JiUUOdC6z z!kKSGLfhP|xIDaJ0tY(vfcW*G^V8>)b}{g7#KftF>HOAc$XHJ6GH(C3qYOialidyL zq)`uH4!R``XNvLTSY^&&Ur|TC@C#_q^EM14BSJdEp4~Jd*^YFuR(kx0un`0e(Mfgo zQ6y>H_O0QXHO(hcp(eLi)BDF^QW<@t^0$uyCb#IX6^M}*zBErmT5}^XG%i=8;Kmn$ zfETP5xCbwym%pI84}{lw&u(8F=^-)x8#(#= zu2LLvQP!w9Kvx+SkhxDGoy_jVsA1V957g+}&yGyjK0XSXHElY<7}ckK+mtrfG(%DS zILhgw+)ycQN7=bY2-AKwc9PIcjmU|r|4k;rM!d|##;tBYas0|Pqs%znG_|FW#FGAk zpc~Qw)u{|?m}x9ZMgT4ANF^E$Yt*8z$`Ux?p&SxsUm}k`eRV?I!(d9JxCq-9V?sFw0NmgGxHs>k zFepAeM5+e%i&GxODV5*izztsowW8z`t8d0evBu%l_T_n70IM*3JcM~AJWtx0q5>I! zHQOmqBgm%-#h*7&Cm;^IDQuFa!k&o`$>h<^k!~ajwLdzY_T*Mz=GY3kBENdcpqiaC zUplWr%wjbPHxRR0t!!kk1we)ZiEyfv>I90%1GPkda7~@G5ra>~WFk-T^{vfI3?nOn zN;oWsEqw{?mTVezWWwDE=Ju)yT_)Mu8;a>{sIq8>#O>sOVg?mG||7<|urkX$V zt~yKe%`yoZGfOqrS{n=h6utiZtOMpd}Kg-SENEZg}p8JJ*19Y(mxkz!jJe zk9Ld|N1Q3F#Tiw^TJF4ikew?HYdWJ|ql#NAEFuc4)QC}6l3{ou#w#X4=1t8~B3TZ5 zs83S7=a=WWE>yB2$diYd7;Z1E21C_li&oTAoA@tUUKuly5>XIUl1}v`8Jk`a@7W^Q z{xrHak!t(3@EVWIjGe532{?G2;z|-sW|A?WAhsSU$FWsI%upXl#|R$T98;O6>}WJz z>D;O`q0kC9prj1cv%ww3OXsW9A~{G3DUZ)Ssw;+(N~M80rs)Q1%-FuSvirjZFCH8I zwSxLh%ARa1b8-q6tlwPJF#fF}+fgg{=a(^45fY(}VQWA8_oXgJY{IgJ3{2A`L0U_E z?{5!|Bv)iQ?SdlIn##=3@P4Oq3{GV4+^U_bCK9Wo8NulMkCDBavYRK*N7CnDCfibw zh$572`#AwuWdjodr41kEMjg~wtoWmO08x5>nZ3kB2m7)Yh!XZN<-b76sr*l}o=?h_ zjyf{=O9Pe455NFKc#NX{OFo%>04!>iKWk5$>{>1o3pM?U($CQIicJ@B;`XTKe}$La zBW&P#W$+uslSf|i(ju7Gyj0rFd%>aYzNV#DOmxg)15UD=#8K}W!=JJeK|6vUOqIJf z1w^KlPlyOMX@Ll)+S#K2G56MQZTQ`qE(8k@h~OUFU5h5c-L-}lFNKy;w76SvDWwFr zQmjSWq9wQ&X`wh2DNcb>a`?Xc-LvPKIp@sGZ}T@Kx$b}aTXEag zhtNmrRGfewZVAIbkugk?9FFKM$B;g?)=a)oDQEma4Ne@ACRNGmX>3?m-YEqVa97-A>0gX6TFUb-2AI=ZB}U)>`+bYvitz2z~(nK zYf_W8LeFBIRm8bB#i{K311teLmH=pomO@Ms<*~RGJtvczC9mR$ilP*sRtclp`yg{3 zkeh_81;FYB%F8w@_&JBu({8FZND8g9mSUumBq~T81!*#~lxpHme9nz)qT^G9g{?U~ zH3j5QN6_V~KD+F3vuWnu`^|Q4i@jGA{d-w7ttvnnVDIQ&0q<-yKdH{_F%&x`42p6!yqxMT{AjS}4mof4D0Q9~2s@yInp_gIcu`L11OuiQqGhMJ=DX}!5$GoJgR{ka2}H>(j+`O`MAg3wjn7* z`4~Q4o4ROCg9m}PVIc=&XA;*_x(Zh!`&Sjb%geOVr%0S9=FxRnP|J%T&G)*JMYq#U zMxWS0Eg2k6&SJC*mKdVk*&;OiI;{~0mss8P`hs-{k8R`a84VaaAEt1$-zwNS?6=3d zSrK)9Iq6lJtm&#q9wIBF`I%prjETRY6@49lIMnGCyJ!_k;Z9sYMw|f==yr>T>D7n&*)mRS#x>rPg)5rZ3D>2NUXHqO_HoJX~3)tFxQd2a~U zUVgaG^yP7fCHsSfL zhXAo325{m4c266=R&&rP&Yd4{P?EOI0^sb235^QLB;d4s7i=CH9%*sxC<_Q+L$nG` zD`7jcmFWw#Cyafa#cTgTq0=w>wRBZE&3qfY>NT9Nljo+5g`9(i89}QP6J|5<>U**@(z8q2v)s^S=$3Puz`6D?A>DA8kmC9V0Y?l8;vBz_Dj<=>YRlSTc}tN& z1HKgZ@@F8*PZH3OtGp8o7`VYW2vkyc-`jDm7Y43JUb|j2Jm}`mgYVqkl~t;Dzr$z+ zZx``lJDl3H=7_4dyHksQdwGlw7mvRW#BP67B=JD_U$f(vGXJt%jUy`M_Q9t`HSiUr zOLZ%A2fH9F&F4u~IPC#)o65WwD{O6D;9opUpJGKJw+DL0XrR0+|^J= zW9dk0l}6#~M@Cs$!Ni%c(`bpBxq8PTooRboj)^9ZZ*9TkvbEpChKOyFNPgM2hFDP9 zm|AVU>69hZrKt&n2z2of*>y0aa`j70YXK3#RC9_zI?y4kDDjCO><(prT3`_UDiDiX_U3pq}zOLhneW2b}$v9_w(w zOZdnj5J&l-b@TD^&?}V`WltH8VH|_Ili@3w>WN;4rmy<5D))ci%-GwZZ&yF;(J}<@ zPrxFn6eSZXXzsO_H@Y)d#5^&2rsp2b^BnTF+R@1m2%_dhHwi$V>o7-?#DlZQ} z7mTN{DM&0@WYDEJS((hWB+Wp_wKVI28?XEhhzqPpJEMv&$And(6=7Wt0;*+YgSV?V zaaZ6Pwp2GrWd-fJn|AA_&UV$S)!Pfq1neifh@HbhceMd!E7svYck?^h&J`~dpMxIUgR5ic<+9QlQJB%+kj z_2em|K-Dtro=8_yt<8bz@%Mz<%95R-gI4Ob05Ofdyy~}_4TBLPNK%}s=NGVGQ@tuU zVsNZ4w0q4J%hlg*RL_4RIEGy+5_ndQ86B*vBA*yG94IO1fKK8?c+|HwWRagC)B~%e zy52JqHnbBSTnVX9pfYO8D`XO$H12=>$$REdB<;dtnE?)PLP@_%~WKjj9Zx832 zWP}?Vh%3c1 zJv;bG6r3W_MC!#ZtA+hGO(H-;UqYcGNxP>uL!sj1V3R)e8tFnSnIH{f7x0VRlr#TK zT>FdtF`oI^5?x~lJ0ZjTt2e|LSqNXJ{Y!!lreAt9DG?XQD2oW1>ro5hc43fh5eMtL zP2Z-7PB*%fK|4LIXpj}s4&d9Kg0jI&aeu7O!uc)gP{!|0eWa1YF~I74j#h~g*Y*x> zQ}VUJU=F>7j5NER>!O_H{`kW@g{|OtRmJ8oMKfqM_!|`~Hpc>@GM*TwKo4Z=w5ZpD zsZnsD22R2;=jDm!KaaeaEAI52*S-RMZ+cJeQ>}+PA%}(r92X z*{G%oc1bU;kA69#IBiGh)9Zc~O+Haa&pLR}GK!O_=z|r9L$6 zTgbS$sgLjhrBUDhwMBTyqTza-@R!$FPVv^*9-7`aq^@CP*Dgt#p?Ek3J;yc0*3NN5 zMUJAe2xoO1d!qKQgqRaR7lP6af+ylgthT#I8Q$4%&XQ&`kpAYvhUa|<-#CUz&GFc* z`J?kf5{Bl^EGNZNs_^c2@zXmbs&r)+YJ@uOfAi0iK|4f)Ap7n;5vWu&2(x*Tr8h=6?3WX z%XEdsT2@kGXx!_wY69-LJ5F=Os{L?7(;As1)3A8Zn0r~C5vSdLToLZ(*c2+pP&m~F zJ8E)PO9X+*ewEfQ#Ha@mt?Kf`w_symF&2@=9z9^kScy|@ktpoh63aZdq<@x$jRkU6 zTQULVDes}PMbbIkT1XT3HP%?FA@zhS;Up~g&_uDeMVY~y_b2cLKy;A>`QPycRv%Sa zi<#fY@(Bk%se&+qp&Y`jSl%YT#i;!8vJDi`(6OehQJ>)J=}fH-r*^avr6UPw<|lFn z@+nt?luY5&g3~(?(&PDy?M+=YZ7Ooc`cSnV%tDJp3LGk_;~a>MTnI4ggknz0K+Mlqd@kIFxNeRoN*8v;7*?3D%< zzNoW%aB$a~@qre)YcG|>@j30|PO*0nDaL4l`8IDPW8W8S>(mgx;1uBNX=)i?k@;vw zU#xe6`V~K+WGJ{zbabgQnf)vRVOOZ6>@~T9yNkthrWqux=VDV!9*^WZwiK(x`{DI= z$Tbr=y>C&)@};^rvd-wf0d;gTL9T&s!x39vRzH9i54@oS>nGE8hV0S$KnO2uX+B#8 zza^fJjKss=Np|=eS0(Dttjx|^Q+IClygTw}$1*gL|7#qX)t{MG@0V7Q>r}F0_9NAw zS~ywQxnI5w+sSPc2BK;n*jR^q9q$v|+LVRgo`-Gw!w-W96|cN&d+F{Ge&CH|lNuf0 z@ju)p_Ls8@1A9Y_Y|%NAzzPLrq0zuYCz4B6Q-+1`71cQGsBD&9Si(9ypXQW_=99>0 z{y_$MHg+trKZDvh(R43SyODKhFNv%;PQR8@7}TZH@dDBg7YI<-+c{BqT5?}8!YEWt zW$l@jA&E0Q29i!B*0&~nqe@nUG9V*>9svY6ybK-?5^;2z9*Yr#QV07kBb`l!&)4nF zQ9dvT9(gL>h^EBQ3_q_0gtJk3I8(N}1R{x64r(%f=jim%NB(|HHapyZv$d}7==QMHctXA)v|Tv3mll2?mN zm#XL~m6S*%1aCdbf?l|zTXL657cGE+?{K_X5 z7vT?uH0`Y&1Rt7;?KMVDb3B$Ika)HhnZhamfnMSu$7aAvmAKyn^DLO6OS!fYIrcP_ zAdD=xF>JCiKEg+n)@c;U+#~9W4G-4{KUkH-mIRSbfK4kOlT7$gOc1$41T@#=chk`n zQZTW`dnCq2&vR*se`W}gN*cw%!nAYlOo4^?VfRWBF;qDOxA#b%q0#)g6q31_47u5^ zx%Q1|IRm-5^SN(+=3+?miuv<0NppGFldp4Rst4rg;uOt$@^}d(**SBJocVT)gj)tg zmgk}(868Q2Td0Kg>gOJVhd|244=#yjuC_NN|heH6oG<p7 z2~l)aXRxVqs@$kp=e5rJoo=^TVu^O*Bx~t+cR$*&=>A)AK-A_jzwmDpqNnL2Tu$rz zTgAb@Oo&buFBqCWl7-+i4Y$;<^`kj;%Z45|JWBc@LbXSf`Al<6j_6Jwyw3W83m{uO zffV#^bGFW5s9?W=e!u=oX!OMQmB(hn=fK~?r=s5w_qs^lv@t}$-ByCO>>>=5YNJ95 zs%A@`N|GXHAA>D%Q#VaNR9_5tB=L@Y3fB4NLUWn1`Tafe&#!bw%bXFpzt?!geo(SI z9na$_O|Kn)xXo02$O}c}zA#e6$#uU`f;jno6$O|h!=foC%ER<)_z-!BF`7uWSY>0X z5uc6WMzrjyco$xRoIzBtcK>xx1l1iFb&dN=Pl!9Ke&BB_NBgqHEQdJ3uu`IuVT+f1m2DMMg5eg+bySG$8Iw`o#K$8jTxzk5h!Imah#*nfpu{g(&~-HI9%`1UCu*J|3S8>&fn)>NVpD z>rOX+XMS&NhBJLWWB6kKY!3XNHeNhztu!hk#Um{O_9s>C#~vrQx|)xmz(+NedbehAcgUO z*V&#|KP$>>uXbvhe_id?_5YU%@y16wPgkw&$Dh+$1-(nqlk20V&FE|EKQxZ2)IO6#@`ZvXls#7lqzW5myOLG7b)Nsy_cp|R@s29~j}F7fN2KA6$+i100{2Dzq2 zIH0lz2VQ-rgNGQU*eB0-n-1eci6zuDfJf@gWE_eBw{-|@V!X=mqg2N02-Z?^Ta<*d9Iybaf>jG%K8OPjP|#v-O^B1~&kn}zP^at~qGT=)oFZ8mNE^O{G>Ipn z8SrjRi2d>lN6CJ}#jL-v{mNGp5`BeW5}XzmbwijGtyjX~9FA3&PvbcUu5$8fYiD-! zu!{Q>BleCcc`T083qhrPnsHD|vo5)VP&+r^(>o?@u#5sVAPwO6pv|fXNt7Vj4f8R< z-78p1A$x>I{7Q=6&2f%XN0+)w+^`eR1nZ?sq-43PRsdlN>**Y~bn~Sqe?W=NcToB7 zI#<29$Hw{FHEV7;-k?WKrgwk-rwMUNB(3A$Cd8|$;N6RIM5$&|*eYKZu|6p=)u53` z_&W?lf02npXCmI_w1|FWTXd*mB4_ShF`S}OOY@4JIqkIM2{Md1KOFB7=Csty*w%D4 zHB>jzk$gO&(!e3z5D4ro!+HHQh!H4z1|zIc(-#O0@FNY}<}4&ViZ;&FnfYWpRl;D+ zsY$e&!fJ6=-F+LbQAwEB<(VryNz}3#Bcp!TaQ1e8zFTcY$Feo{9f#OOKZHIUfb@7@ z8>*im$ZDTbMRD#y`12J)jCSse{CPb(sm764cYe_3ydh_##z}T)epH9Js(|9H%g4=; ziM+4;?n-anFsol@+RphD{oi_EBsjuw<4-t=^4@w%O2jSvqGb<g-3J&(XyMfb*Y&Af+2(EPH?uGAk+OgQ3kP|0+%$WOv0O8hH6}Dv zK-VAsrS_^D`<$^MEN*O|$ry)N)ryn9n)r#~BVifRm4R|tzJQ5jBykR0_oANwc4(VO zSOgBE*AkywStMKQWi=QPbu^Ud3Q-JW6gaIzqRQ-J=}~ar)G!#CIK`Eps-5tcIBAMi z(BLW(ZsDhK=PG@z2p-B4DI7-$K3i5I>4z$JH zfz!{+Sz(5v28l!O>aD-J;KW5`E*_}#hCPxAWZ(z|{(4;wSXKISITlKJdnZkQ8^vTJ z!i!a>2H$s~U~Z;OWNm_+zxckR5imw^5kr21jL^m9Qa<!HJ~|ECiB7szr&Bbf zC&}LjPJFC=C8Ix#c#NNFt-9c;12+jx@#@=d<|5A~m_#FP#Bw6_vnq@M=2qDh>&9>EInJmHtizx39Q z;3X~Z1j6MW{O#dTc5NIjFjY)qKZ)uaTx&zlq2LG%(r)E53vEJhc>!McJ5l2*dV5KV z?iae-ZzJsLhw)97J$U=J2(`Pp72%p;e#_L>od7KZ91vOi>zw|6(Os?horDg!tis{Z?>_}cbdj)#0Yef>ivt#0iKzeev- zU02f9vlN!^z(O2Q{b}if0*>vpEBe=?H!nR-$1b$4`xS}haADIA;lHz0{{DUvaQqDu1^^8 zCZw1b0{01r%s~#%ogU2CaHTSdxr7rK8=OvrLSn-uD^vl_;qr44cP=6nfst(M5lE{@ zRlZ0CzetUih_pknXjv$wow`Q7ZfU3kOHNh_BCe%oWk!ax zB@+t^7cT+N5k?sEc(p=f2vZ}C_!z)`)JOy$tUiiqY!y|g? z`yA0ooLDSL7rOwGOk%|+Zdj*-!hAbZY`)DKc{0ggkE6}TY2 zKy=TI!f3-{OX*FTtRPB+l=qjz@2S%rdBwlqmO`J<`iiN)#DLjo5)Gaun1!hh+nQ|H z#yQ%>byg(mJ$uI4mEe^MgkquD#YtcJR7dfA43Xp?@E)q$B@?WGo|ZjYaZx(QixA+9n&o=a+Gw~0aY2y*( zW?B2P6HN*QZ>zF*7b`OILy4Qrca4xYp-B|ENP3PGpdDiGiw03W9|b!lmN%UR3C_m# zLNDMN+&O9erb(Pf_mt!G(59&-#`Zuv&+{DiOUPr@R(ZV)B{K0uGIojqAp7IPj2ed& zR|I5+7lP1+bs$s|@eP9tGiy$m99tn^SIAv4K{||&2=ex-7R6)~bT~Ha(S$P&LUg>M z!N*S(2#+Qgr;tMf*Tl&5$FuZPEg8b18M?AbjtJVtAOYxj;+445I?Ak^>;P@SP&foX z!;@J2WefosAkBslpX_p4Aq6ik#}P6QQEat~z#;P9xbllQz!n zcI$$j)&f&@jVpA)seRO*zxAyQ@wAA95cY;Ez(N;O$bV&i zFWollYmxSQ6A7DQnRxkeH1qA~zv4o%@>MaG+MbiF1Qeyp8UWGIE;(^b*I6hrVC3r~ zcw^{h)O4rB^nWrT-p6WkQwW;~?FB9TmTg~u7&8WPM_Ek*_ttgd^ z^W=_|K9F0#1vU}zRwC7~YXDM!cSq`q%0QdS6`sA+rmw$D?`~ZToQB#h8zR!mhK-2$ z3W<(%Si~1#=7#`9JQCbE{>N#Ed0j0-coA^d=H_QfvowsTtqx1|8g^^c3#_MqRnOR7 z&-|^P^-n#VsewbGf$MGqPhbPztA>8AQh{#`*)+8xx_2xX8)*av;*SjrnJFNChS#L8$IG&6SOkrEx;cAw&=kVV5`DkS{$jl2+BV-WP!I z)B(Um1@?6i* zJLACkWq?7QEAdPVZB+}EF#s?@81G9h8rt;0vFTnsp*9lmd6iIU98%}bk5p?tldr0t zAR{`u6`i%yb`h}SyPEG30iYeV?JbX1I@DL5#apyjiFMH6Q~_#uumW;u;oZuGZ_uBD zkou^P`o!GJyCu8s)bH_JTbDb|m_DZ#f8M!U(}5=MFLc2%bvhGt0z5j2^g0QGI>{0{ z$@4ow%v})0F7UlB%AhWa{4SVB7o%PmeMcADUKfjEr#QYLaJV|ggp$9bn*h@-Jlsu1 z(9_D%BXXNB1og<|_edu8DAe>wfA2wD_n`E8)P{RhJ$k8&1^+O@223aoYPt>fKCPiB zL8g5$&OWRBzO07cmlJPonEBGMpq)X;Jt4@(pFXsAEqm&FehiUI7+niqlS>d}mI>x? zk0!Rd`FWl8vkoE8U1;EUNb0x#J@NOHVeR1)?;~*?LXSGI$@k2iud!*00~z-QvOqOO zfdh5k{ZuOyEuq-FJ-Yb!ut!4!ZBlo_IDuP&4bzN+8pvjKXsiD2;LCsHkzbnfhsvo2 z+G-LLj>zo^hWEb>&3J=46J-^|2YZE>v%b6;x*i^99+}KHtM4R_&lj8mF?~4_e2zEd zTkoVlg^e5}qbV7F(v?;*rNdzNmO783aF6OfKdf5uj@}27@Z}MQ9ir)7y`iiTdwc8+ z^ElwGr+K3+ffADC{=twcY#+{cVRO1C%x8PppQAZFa3 z&|CS!MFH2v)ZJ?$d3;scK}+uyIQhJ5LI5W*DG@v+H&Si+YD#hOUy1{kX_V5m+Wl#b z;AySb(>fog^%kcM{!SaQ%$O+6nBAYT2%fRhr%leCF)@CBZv^InfT1sE1SSR@gTK1G z{!0A357711?eEtGyFo9d*~>7Bna&a)3hJ+4hWz!ZqTp)5O4Qe(5Uav3VM?%{q2LO; zuf!{en7^=dylf&2`CnU#GOCedT?OkKtz9{JZp@Te`SWqF49sFu8bjAo+N z&USR9q+P5Y5VUANh?>G3f;QYIZdD*o8{aIlDBkYP(bP`?yS~MYApc_G)@_+Pj79QT z7Olv@ldnCdDNK*O7N=Ld_}RfsILsm!K6vRhWce@T%m};|z9c2S1m^(n=rirTh8!iq zHb$Vok|1aIX}aqOQtAkpFq8m%@PR%}ir6yk%Hm1VkIb}XPP=7D_cBfDGNb_72!S?w zEwPEOh#CK&WQCF+l4deMjq1oR-$Ls`Vb@}Fa0JAtkQf)b%kKcML<*ov$&`|#LfeJwRL1zgFwmRj zAM_Z2z{Cc1{VEWLzv=CbM~2xZN5u5F4-KycVB}K;}fK;GqX}D~ql#Ar_t+B+#Gt#0UhI zcL5lHlP2`=aEI9L7tK)~LD(Um-4O({f4sk#(7E|t1R~tIAzHA_fLZc2TK9z?kYOmJ zZ>UIcDJxviRX02Uy9lJjmUWZ$2!O=rz&o(?l|pq40pf_{X*_$>H)L4Tjjr z=uj5AT4K9#X1hm;ITb%zQyzn8ih!>ngj9!{4-FO>l?kW{AsIQp0QhG~A`spec;pBO zv&!vPfS?5NiwtuH2wSpMURr&9Olb_B)jt7=&*{}t0$Dd-;MS)efbo*owhuS>*r;(g zkZYqQVfZ#)GG)O_$nxtmf{9JW+N0DTzsFa8gP=cZo?H?gt<6!c#Jq!6h(c=EeiyN> zy(+j&D}d-2EY_$j$^H7%Ecy$F*c0v~vV6CReRp}Q%2mc)aEkv})BhU@26O&cH<)Mr zv7mpl@cHjI1IWUU?N0~PhlP{?&(nj~Ck#)HHbgJgUqY=Gda&2OS(G@gkRIcPUlj&I?!hV z4H>zq54EC?gIKR~O}CAT)RSn}=TEjx$|ziUr01USO_!O!eb5&#Roz+0(C$gZWFe%U zL!`Ufn`^Y#8$vY&<90DEkgt;Xne~3qckjWc;J-IS%vOiC-7NQS-o9u#w66(ZG4L6- zazWs+UlJ1iOK8i72f_;nbo}q9y~>1TiM9=nLxd;=o`%w)Y%zzyeC@`vkymohc7m)% z;7u8TlYM5LrizJTt#pC=gH1f+W&e@(R?So!VeA$M-&fDRv+dy!)`o0Lyu~%d-5>h! z_FJ=8JFV)UC)R`I-X}Z%2yK5P4CaXc(QT<)>51*RnGT?RvieWjTd^aKjYhX=_crZq=Kc$wb)9c{w!6e~X7TvmC!*s9rR#@BYrW}A z{nFQT2V3LCx+;FU7SDftdh7oA(?^Bh2PI*KI5xvi9hcD&ByS#$eNsAgQ(7E)WBB>g z-#-^8yFbSMo6we!)@3;i$WyT#4wBhkj(}+Jtwd5=xU59cJ5{ViGy86@#K5EZR%5xc zTvp@w$|_dlrzUz<6U4sottE~jsgh)*HY?VW70u5Dv*MEezmFsDGGXDr| zHTX9&%q(0tGOe5{H?r>e{@lp6jppCXam;ev%>6H+t=AX+t$d%au3NADHY>LZg3f<# zy$K~2*e;Bub=$_o@>FdXCCcn<7o#-<{t?=`{VdIKs=5{0`tJNJFN_x0sVK>E+o`N5 ztJK6!M(a(TKBzrEKl`b!?4WmUgNli z;C|Dzh5P>dIj8FV58r)v_nVia1%I`yXSx4s-7c#ZDZaX1TW$0E)NY7GbupDI-AZmr zDYCEEQa(PLWDkR|BH%_MQWYd#uY7_I$GBe8AA(tYgh1u4?%PHQ1P^V!!ih=d9gcF9 z;9e*ZK|K3#eo7awZmhdWcrPR6`mwIU$K{h0y7+5B2#azL$VmZ5e;lEr+R{KNHu8Nn zg)aPJjYIN;On6gnE(ad{*~{<9kZN42OY5UWcB?iw12zrJXc-Ds=ixy#XwTcbnLwym znx^8pYnpK3;nWoKO0tVqZX6M$IO@i5IRBjfMXsqLBD@Qy)(Jpmf^J%FRY4^U08%{r zapG4o{@TKXA3GqBifj>XQ9{>oa*+_`k_cq0S$-#1?L+M^+`2;30lObfU3>Ze@~9yI zgr%vGuE6omz$4Sz1~Sx`;mvOt8o$~+6!>%#E|-djcq$wOUa9tAvooAzw#+B zp>TheQcfX06p-r)ABp2Xjc}TOEh`A*?R_G{z#c&4CM=K59`%o3<6XH z!Nq9xaxfmnYUvfhva!9Kzlx*v6N{+JV@rZN$X}9{6wx+g)mf1B2`WYXe>=6lNWl0K zoQ5aprs#`#8;+8^5{v(*wD%|_6jTBy(Z-?}kI}KWX)oOXRzvHX5l!iWw=!2#lX;mtK;lW zP?=ba_OLzUNe))8Od@+=*h&5*cRaC7s$6@-&E_O;Zn#XQd0@md;N;aZs9dgJd-Q4E zN&dE8x%}+FsBhcJ>%;#{drt~3hW|->|Bk)9I(dUfQGp=Q84qVXEd=UUpy&q2qvcOA zkfaJV-v3N{D>P&WCz1nB|0C_ynM}(&E#cFz)Uh0#%xXI=6-%nrbJm&4TR1I~8>uvS zF*sFlby|*~s4|Mtx!tWftI*P~GRYpCE|Wj2G)k&6E7zH+vN@}=8mY2q9-OHSIIFg$ zsJ80Y`Pz_oR^y^yZ8JOg^+Vg)Td$<*ds{lQZ3}0$ek0WnehO7=qC3~k zcwQH)Ut>>4KFPONL!U^okp!|&4vCFm-arFE!aEdCsnwWgvC*;}(D}){^QH>@w{Dh0 zHdWZ-_jNN4N&vvWi14rej~9;=?-!u!f0qc9n7{4kPjUy`2NC~c|949SJ-@i+hS&eq z|J@Sd3Jv6HzliY0;A7j_?p&k$kI|3qzYmss68?h-C)-oi zw?z2(=l4{qnm0!s^Ytwe=$MQCJ0e`5VPW*QL>Nm-9aEXUmNZ0u5W6Kpx&FnN6wWIG zeaHD)k}{erD|S8tq;!%(<8U1j8z;8(0W7mBoql^gCn+s9K5SdkJ`wRj1ML5YdVNeq zb;rTc(*k7Xh&C!HEAjgSx=c~gWOW3iEHrM35c;u^`~Ny32-}`ICOBr#`VM6(S=~^9 zvmD4SN}7?AYhbA~+si@08?SFaXuJxUxLZCKhN37Lw(AWQ&x!BNWZjsp;I#sH6=xJ~ z9J?b_e((v2o9#%L^r#%@)zq6To1{gk?0N`2H9ik*4<~52F#UxUI#@Sk;d7in9==nl zOGL!O&41b*@3jruvQ&ckDU+!g@ z0+{vbQ#-qWKl}EcQ8M@W%^4^kG?~J*aqAI9$`0*IyEAyIrX*Reh#o9!cu<4}%M&32 zE$OPrr$bE76b;8(2XXPl$dOr03A`o}s>9f#$fm;;>T#mO7tY)`!gK>S47lvHk8Tk= z2fO5tQF|H@Gd@TqeH3fQ5RL6+_41C@a}0;RPoTTR0}3hO)v$z*_pl}(r5Nq8!wzx^ z;7i^Kcl7EQ5+RYytB<&G-eOieWR{fH{qKWoerYw#MX=&b!s>$@e?@nrSdY&!y*v`e zaW@qj(rq$mFLrOMwN4G%=88U%yU^gZV{JCU~q?fEd~ysKy|x6-o@m=-d}(XJ;Ho zh>!vB@3Io6e{7nME2i%^7@5s2rym=||}CqpXn#c|S&N`Mi|Z-F#0YwJn= z4&pU|{2W2nr@z|YGiSzK7~IJcPd-Ydktq?!aik{3z9CpCmm9hXH1#jfAVXJRx+ zC3w~-)?@jiXm*~u2{|19FNr{7PS^hbMg(iBXoQ4}Y14x$kmoHC#u~a>|Nn_F^#9fW z1)Mh)-u8c*x(okBgoUqd=kM#1-g-Feep|pToPTH;dF%ON=-blOd2=U4tyhfh_f^K< zEm-~9r`bc_H|2k~jwjXnl#he9$Ner@VkAP;+HxzZDBzm^hYM4%-L0vfA@G`N&MHeKw zE|OPoh3xJ{H~nZ`wCwN-W#B~*oU%SvQ*Raa>Y|s=pg!Jm*dD=X2j{a5z|8$1Z4v{MsJ;i=@P4D(2$lryw0O=Ibf9BkWsF;A#nF{&}y_Ht9f`s zB=B;`ma;LYUvE?5)#b2@L1W(R@TN@rW#Ncd@`--~yK`oK@hICsuitj!mhyk!|6Ps8 z8Z;HrjcjWuTuq>pn@V`~f9l-5n#>t(Dw7@gX%KidRY>{1LQ{XonNavGglsFxO6laLpNjx1mwy!V}fA&D??44O< zh$8=$5KWVVaa<(_PZYgqfkDDgd*I z(y@6|ZlBKIJ!^8wy_3B;eA$E;#^qEl2?a`SjuUvl`}Ad4(siyOnB3=WyHwLyM!Mj` z(IAQA&GF)djA`$Am0h}bNQVzKfgbPjq*ZZuztnV~_I^;=Xo`Nkh4$gv&>QeGSB>3G zaW@O6T!k0sJHpSbJl65~79_AVBv7eYyXJy$0RI$7azN??D(OE_e*28u*)S96_u(sY zYyr_uKHJ?WvsJk8;zY|8t$Y~v+Y;w9a#=^FgJgD+G#zeDcn*v5=OxaL5u7WiLNc2#{r$!nD97%(zY&HTEj2hn>S(G5&{I2{&89wzl` zhRhg)PG-w{nl^lO7zoBdy8vKP~zNutFFXcQ(SO?ZY7=gbR6J!BcI z(PcC4Nrz)JI2ZBpJwV)w8u2u#ni@;$pZ@Wuv-ks#2qMiTfFOg%UUpKlznCB{Y~blB@dtOVFuE@D_= z9I8^K@aHjgcH}r5v^^$LH&)abgJAXox^Pk6W%Qm^qgUf3;Xh#8%UN0oYDyjFgJ{1u z9uqI=N_+wpx(}_gLupZ^PGV9xR{!+3$-p$jjDB*61Q@7El%dHLdzl^vtvLXGr18&pQ0pCd z8ZncXy8pYP)QS>1Xx;O2Mv1$`jA0)nMIP`bI-$es`Hqug<{aGfT(?vjx=PX>!*~Qk zxzuECBT*T@1GZ&dEh)w*(%2RU$t9yTmC=|QbuyTEaSEyzqj{n#O4QH+?PX()Igl9cADeDd2D*5Q&mDBOV=tbB&vzyNc-T$8Y05bLmP*W0iBGKr zUflhw>DV-ru+`_laIYcpq6i;k%eBV5fv86;vA_3FBBURh%S>*zVRotMiIZ`BhX|*K z^1l^e97Hpyti1nvn8~R+(_j|fTncf;>iQoZPXsl}QP$eTXi&2NY4oB*m9zSqKh^cquk$IG&T zyQ0gW`^yHhx0^h1KoP&A-s(g9AF&xU z*EqWFi$W0}iZuSCG+iu~qX3e+pKk;pogU`h7{>$#)UvQ^{CaA+=5Satqyv??6Auf);!T~aGv_TZtr!LKgyn++(S2VzdSukApD%HS(oTngKfK7 z_&Im$D^DL>ox%c%;YBwSE7yi=vj+Alxiz})DDjAV4Q%LNc~Q)gITlpzkH2G#>p3Yr zxV`^2mT)&QPbeR!xnr3^`?dPhSzXA-z59lL=4M}>bHhIFXpjAw`19a2-|%mh^t<#b zD%=J3iQZcF!NsN|?sBv_@DyYC_vhWuS96~~of8$b1BSSQ zfGLaSuRKgq7C22ci)4X=0k-cgw;dTj?&c2;XVYy+;0>A(6Vk7zIe;75V7y4@sL69e z70xLsbG#VJLaB2g5GykfU)bFVYKFN`rW^M}aTk{K0Y%)(mjig=`3}G3F&7#P=-f$u>KKxJ6JHu?RKT zbq;FI12lJmTFd$7oP#sRWpR)=GV%CBo`=^QlYnEbRZ zfxwQ44;lFfE?(*c&h;Vwhz6rMA)U7)0G;uf6?ED_g7*=!lNFHH7u4-og3q?MYZdob zeWAQlP{qP{hFM#HH@@;AVL2K+zn#ctr(yz4p|%4yOsjm+gl*43wR3z}p(Hp65CElg z;DhL#f}QcWobiZ@55eKKL=J@%Ip>kmgm%+~E`Swql3dcUIN(wm+Ig4)L8h^+AYtm( zF~m3>YwO9Ul4f*d-9tUiMBZ9?7{CvdNzDk`}9 zjg-LtMqvERGN8-!A^5}=kSqqqi4j9qfKdE&(iQLOxfIqo`hr47Zo4!e;N` zdY4m#f8e@t-rOH;89XYWeF$p=KJ=32vXqb%-?AqmBq5of-@o_VbI-Zw{&Szt^M1cx zmWvS6U3kV~j&Fv^I~2Skv7l`i#`}pe4GwYbNgjF>%$k^vw|EihIEoR2;%TgpnC5c z;T{h3+%dehhQlx$-l~M?LBiyvav54cba|{EOPma~2vd6yu@-*W3U29fxlUAb#{Fhl z7y^U3m6wk^(7+u|l3cBmaYroeyYM_P>B6HwY6cblP>v?R?i#Ty|m zZ(8}jg|Th`kYZKsv&URviqOqRymo`l=fM-KBLUUavz~HsL>9{l+{=W03CfYIUmvlg z9>^z^Zd%iy1q!&|g!QR&vJ9DXqKwi34afh(E zJm=}~yvzruqoL?A^iTc`qc$8vmsbv*EB9(E3=usHvr&+3l?8ZxEfCk2iBxEhWhf}eMr z?t~!gP;eleZsP<-Jbya^E4xg5FwKPPYF<)WUcK1g_?X|Y&n1}(^R3>zTv8(Ne+F%) zT3qvVQXk-~EQheW%}*JAmjraRIsPT)a0kVNQ2Fvs1=&wQfW{eB>X7v(3d z+WoJ_n;y4^{zxaO+)4cds|)UUJS_Dd{`mG^qTopf(F}23h|fBLZHuz9X?3Q|cdCt8 zew}Q)+Z($y-RAK_R(hjTdD!*aWY@#cr{f`Y2_Mrx)OOjnbaI9~t1{zFwr2aWs5TtT z60Meo?rS}<7rlc->_30*oyoe^O!+~0-aUpqy#i9*1!!gfba$D1Wzg^So&toQdxx~4 zgkSC)!2S51_WwXENk#G^H1=pEAk7k%x)(1~msrnG%xcYS1xqjs6y$8O&Q&;#+nO;p zRDR5g0xx9=IpN)xL3|)2Du$Hb5bhQTp;qQ89G4Lus5t!H=&Vi(V=6U+ms($LrbmsE z&H+AnE5@rCCWbbw;J{b6=-r&H08$vBu3P?9H%RLycc3QxaiujwCLrs(Qs3v>94!WT z8Tdh78#_I~e^LV9ETbK)gtKSQcEUbCgk9|cJVRCz7y$`!%=oU*8wK#(4EQrlBSQ%2 zk`!F&1jZiIZWi-Wj1CaYnQe@=2*WKhxzoMA-pg^7j!~}xo}`Jm+{~t$FWmGTdC`HfH69pD2&+w! zg~u_Qt|2~&F?;5j3=@FlW`{3y=*z*c0oHF+p4In^^ouO%U&SD!tpEiG*!V7B4D-a1 z*%lt6q`PZgV$cs=f^x&fpHA99^iNx-{ESCPfy6_Iv!HFtc?qC#(e&!}|ak z$NZ=k-n~@?P}EMhN9;AgU1Z@*^<~grju!#%#A@2M>Zmj~dd^8*Nx0U&n#wl(Xs!Qu6+2N*`}N%~>}cyJQ<(+!A0fk_c>t+22rI4HUYpl!uy4+m*1LjPK| z*a=&!=~)XN;5tO^vs=TPOl=uj|6F32i4BZ)n zKX#dAdRQ(@0?!C3XN%{P9dI^zCSjbZ%;oc03FvYLbX}XvTJbU@dw`aq^-z(~o;;6I zM0&tEam}DlR!mDaqnxEIv8|*dW%dhpOil>!tOJ{)70A_!sqPS4zRLR0n#q*bDz9Du z_q~kxhsFF{1gXSa`+#En)%{W!+qK+=?6G1U)UzJX(7KWH^&NswU$Kj!g&_qFD=uM) zKDV+~>Jx&zfej zl5Ej7(kr@F%8N}zj$;_`D98m2gMby2gW&h~iu1dB)K2NmolGdPnY}(}QN9;R@`hzL zGsqCY?s~vsz0I00f?sa%JSbp~e)S`62`b+U8M?m(c**l{eIujlcDVdj7NVLrkViRu zG>;RJaXZ5>iz}T=<*(kinxk)%7CZy@x7Oc6a^Lb~|MwzB(R3(9qK@4=lzG) zK$v=Jx?kG6Wxd1nnuJ`k+S^SiW!wMsVBbbjb`8TA2^grRJz$kLc$t+U-_2iKBL;5N zgW8K5Jnv<`+qfSjyw7BLK;x;7i7OH%JzA>8ZD(sOO}Qz3OsUv+yoD(yq#oRk+ZB1Y z%U5a?-NTwD1s}s*y>?wm;t9{!VyVeb76}ogx66;zSM?Y4a+iw_?U#Nt!48=ezi_l1 zMt$wX=p3c?{`n?v5Vwl{yx0y9#u6mt1u*jG!lG zN&4_dNw}Tkx}V1-w|2z#vg&_2@W~s2A{| z`TSpCCFU~X6k2%U>m$KZvzFk}uR_uZ!I@*|yui^>UwHL&lBfmxHq*xbLJ*oMRONnKVB-l@HByFXsn zvse-;ekt@mCtr8hR_O}{^Dxgc|KUQpub)9v%c|3`} zj6G8^Cu*2ncYcO|-m6*52r-x&5%U*^`w~#QMWe~>dcHnuHBahy6*fPS9ARrb+|43Eq)}~UFS&g zj&e&xkvjxv){jWh|shPj{E1I#)3NfRC$t9XTs`(pt zhf5<5-~W8f&Wnb;An%r22(h}HRu|auCm9Y>XP00+*ZTUJjfEabD|8gvg@MpU_#4#M zdD)pzR&wO2qN!AM${Rr$)xc!osLy;<@#rV}b@Jb2>(WbMk>>R}GU0vcrs`j_rA7u~ zl28wd)kaE4-l!8X!DQ`I3h$3a(KaiCdVUoe`p2noYP~E>ZboJ$EnY<&GBFM1psTcok8rFCz&$r<<_(Jtf&N7898})S_ zpZ~^vUfisnzu*47UMnr?ZSGXO)~%O+&a7`V#-&Z($lDdny45ezzaHG#;nlqt_Tk~K zP!QU*sMK{o5nOgf+!uf0ttO4JaFljTE_}~r{IX66IRDK$JKFU3ZxZliXM3jpgm-r! zcsj9S&;ls+GNAkY&w=@L%Q`&yR9L&Y3-K@7kmq{Lhlo{B^4Z?=#FG!gNHw>OFO>Gq zS1~o1SbSlfbEE$Ud&60Zpgqm~>*c>@5D>;aU#4=VF8`EVA`aCL55xd zv{LMX*yu;uiSUG2f;)>F?PwVJ1kUKT;g!tl#1auUDC=)(Yb%LjpyPeYDsm#qJtzrP ze6>7Ak^C;C()s4f!LRNeOa*|3hvQhR--cFtbLJc|Vr3MQNH(6KtD=6mTqT8M%>lt; zPbPh@Z}2EEIEP5ZvLds{7ti`RX`O}!#G~{K(>8}Q0#N$zZhHRP1EHD8O0)0+&V6ej zJ<>5rrxV+pA6%(o3>HJlY4$q!8I3Y@meKqDZ$r{(Z!xUh5XUzgWIXq@#12)Fzf#-Jj49*AU0ud44ai5J|$qgKKhYsv?h7e;`SA zZm|hu!Vb6IGG9PuihKu%_KJnfVejUc0BcCvv?~XQ#7O^P1~O(Mu=DG)xIINcA+{74SW|?wBpo^RS!bq^ySpU#P5#Y z&f0yX*Bn0V-ZdKAwijD*WnG<;IO+jKe7Rp4u40XUh)MhPX|ia;m|`M&PIC|MOxE>x zN|6P=XZJ`M!M#a&uiNbd^XNP$R^lx+w-B%lOgt}#zs8g&M-x|s?e?u|3)9w8N*iY_ z8)69|b5@$53$xXY6NfAvJplV4(H9I)4Ec86jzz!zPciUY$BNiHc*?Hqg>4_aq-mUa zv>;Z!e=wEP7DkS4r$jcZK`~CFvPSy)?gg4unWS4I zVAxDgH#F~7Nxp}MN_PO$4>w9k4_NS^R}k1I#My`1>Sd{bMo89&==Hq|AmdAbrc~hN zUPgQ&7GBmT)7B^3&?h&5m7nR82lgv)8YqbNV;Zn#bifo1VbuW$O(aFX*$G}m2fevV z*#dM8aimjwVy$9+XK<~IQ(4PY@=;+u;&#+x}`A_TWHWMgO{Y|wR`H~b* zM+G^wURZC#S0)^F^4z^dwTgjShf#Gn2WnjfOErx@1QJ%aBSnU%-nh!OZBya}j z-H%Gd<~M~@5Vs4I2fgpUC$?1Al+b1N1iz` zo^hu~Lf{U{6h%TCM@a4gI&R*Xn!m`;aYV_jFw8>%?Mo7oZd4CHV16XHAd>8qP1G_l zBsVL|l#q3-7*r)2V#21mMMXLbv1_Rj<)O$)%D>0)4lgJw5 zxg&4f-VQdlRM3R&s0oQj8l##V(A#kHNXJOh?^U=m4DJSWAf&V!RQR>I1utA z4C<{n;abEP9mcWJC+)#0dzB6xG=lr2Qs`j?UJIlu6I7y0n6GP?AZwGGb}7#sTMIKx z$RlRtJc}d(Vftc1{-^@oMHy%b`8bsN^?;5%M`tyrP@rcCd|;9k_@*(8`hWxxbCa|_ zVl?iI_pJf)=7DKR<5N`5L0ZwfqAi>0B&wbpME;1;Mh`=s1W^_z$$rzhDdWnWV>;nf zTPw@w<|cVZ5=ntWxf&C9fu>mZi3WDr<~kftBkvK3dfisNBHJWtt;tK5;wC+)P@n+WA&AOE!2E&@z6MPbZp|A{sZ>~kbk{|#)%^#0+2zWwVJ|lp+$6|R@{GuI z*)(D0CSN~CqiKeEtKM8UdJ9$?^}!KfNt~z$^~wpOQbyb+i7MI&gy9K5ItHa4g8vNt zL$Tq!=4o392?ZhDEt#e|k|>Mu8I5DHlH*kT=iA=Y=zo-ByLjVZ$kn+qsvp2H60E!% zKQhz*kUzfkWvx9j{+AczISUDR6C$)5-&aH)Ii=u!$5S*wB%63_9$ELlp{ziNUQLsW z`9ypryE}hlK3{^XpChHRfG&*s>IRi@tT`4!f=7?rV;FzHrG;LK@jrmZ|ZP zk%7~Z{}QP7dM+$tUnWx_Ni*}gDT87?3?}xMjmGN=%4$76gJW?EmHxxIHT@miuoI+W z@r}nPkcuSI7faDu`-A?>qQv=Bs&A^{q1Tt9!RvV`iB4_TD<=8EMzWwD#2x3aFr|q^$(#F@rwSKE^H5BUv3;JrJl;uMkZJdLk64L?ugDv%(3Blk1hdH@i(r`u z&%lzDQFl*uZcmxwWpxWr6|#qV#0q)WL;1^!l27}j;;KFw908}0FBci9d6FHm5sPHq z2rvDe6_X%OouC!<`>T2ptCnn!RL_=UGr+B7%R1uTQe?7KB*+1hz%sqCPEeutwA{_` zCfBT8btog(^u;!NQ#RJFuX~ewu-C-bWmVQGdEOB{j4A`Hr5Yq_I4}$4fpX0klV_=R zQl5{3j{i+-&j254_8c#=r#bgLJ$~QV+34TWevgoT4XUvkED(3pN)Cq z^oryi@OLhRe_GnNEZ0o;lZ`tL%xPz7S@PFQJs{7P1O}h^JXlig8%c3IQojF0jkMM1 zaNz|ZNUpxK@_C)X&A%lPlAsW`HvK8xYBu~+5>rZvaY%s= zzRCDa$p?JGjhcXjA6euVNgZrAk}@7+G9N#;@K2u0lDW%y#Z#qI?K(932U#VcCk^si zaO)L!psTz7-iz;LZzhJDvqbJwB|=kBev|5BX&pp}HeK&CBR{9O#H4?L|9_3i@`imgqkqf04SmItS7<@{K7lvAI4i7!7gqJ4K#_1ju-AI10@1IojE!NM-ZI>d& zu+9sT$<<4I!Rbft1jtD;UZ)Evz*l*&^A;ND@}E{kx~a$pvp0zhfeI+GC;01grHP|VpHsZ3yqXCy@b1`7uns)o9L$ka zlYVd?YDK3E>X1CzO@Oshjv}ZC6yJrK3JM`E!6Ytlj?JJaL9XDIX`Ez2Npe6&Fi7e7 zX>sN97m9dr-W|er8Jd@EUc`<-Un`7kHk}>UyCxW;0GyGjg*#%y943pEh{ixoY z!kIQbc_`44UR{$L!_?W&zjxOIOG6UTP#TH4nGygAkgQs7W{82v<7*g_jxPUSJuN{{{Cc6p#xoSrx4pU^GbT|!G*Q_%*lEHC2wly%^gDWe0>b#lq8_} zCN*G}ao-09&ef$i_xW8Jy?#QhGNKa*BLX9Cu}uj zv=<6_dBV-n)MjK*m0y}aL1N-2)uVX_bd41nTSxMTrZPw*H}8Cabf@`HW%~>LTNyFa zn-j>m#xzLv=*y=1`g|&d<^fSUkYOcde+Mdc{g>E%J6G)AhU2r7q==sQ=MA@(tH8=7 zWCz}aD@QPV9%CLFl z-S`pO@)+E4hr}Ug+h%ELjJ(o-jebUtIP>Gj@9-^nOui^j)>K7kmujFR-S zP6#L^f9A&k>b<>vL|ezZudibd;Gd_fcOUo1{`?}Aop(#SK#HTH>tO8Pvo&R; zlJLtr%pB;7Z+oKSdGOzeg-C1hw+~LVZN@updtsy1ZnMh;97s-8(6j|SRu81g@ut-_ zjRC)o z&ef}qBNEU{JLRV_=iL(PcdfU6o?SXU+P6G=zLsdLNnPt_*DbPxh92WQXH+q9`=*9A z=Y7)lB6h1zJ?tP4r7*jH%@##p6)RCn) z2E+gjrg2qV#s3L%`YCO-`a|)#epJDRY*+cvw2y1f{OUwTL$)GqF+@Cs zR&L5M{KBrZFlbzLK1TWNRsTH-uKJU$yn~i?!6YwKj-P`9-8M*NiL+APRnd@*w2^cu zS}Gq>6qsO3bFZz+k->)qa?VO@Lj{RYZ>hwD3*HYOTuLOMZAXs>rY%}&VSnDCXlXm~39uNBqM=fLS`Q5#s9N*z z{PrEw1G$?1YI7+m@TWD0PO~)thuM>{;ALH7T z$)S~H)()^*yGFZ-0Mh^sJO-7hkt|@tT!X&_3ify%8KVu&15ysqS`!))~MB}tfBsw?1hN_=9pmZlai^) zFm^<%cC*}iBAz{5qo z;L3jLPhprQGctUI5}Sd3*upO+Q+NeGcUq2r6C_876%SpXhtuTXmC#L#RDy|p+nLikOuu4xPL(0+h<6UShEk&t00 zj)UP7eena;thZR>i{OcC=$&Aednz|XZ#ElXnPzlQdrDm)0N~pWJ5mzmzC#os+mKO$3h~(}qNRqsorY-h;RQ^&mf+ zEg{PD>?}Z>w%9;_NoFLNt?)g#5yMa;p^w+#@ObeCXChnJI-IuU@ix}) z8CvGZ*xi%q0Z+GQobabWtPLj1&WBJb)JR2~Ew$vCD|@4FgM#~(l5=qon63t%=j=U; zlb5mIC-$wXw4{YK@uyIkqb-G}IH(WGFheq5v}j|mm0Tj$&~n2Ji)NZ8HK5ot8^ur9 zv125-Fr#YCsKw~V*ReJ3h~CR1=%bzvcQTGsM?3M{^kcj(2+bjgVTk`lvMg25fsA_1 zf+Z`jJRI;8;^}?j2)9Y)v@@_$FuAqXFxC~1tSqKUyb-X92rbz0Zwej${V2w%?{z+0 zwf9%titx>b6`25o2bEkb7F#Orc`9I)XNu{D!$-@FEOXLYZ`|;+HUGi>Z7EA1Ln7~- zN|~Lz#9YswqOR4iDXPOppvObz99ipSa|aRMK1>v@d1Q;Fe%B`33RcEy5>i z->9coMai&#R^jzWp7_A*E8oqKeY>B`ggWf6tD0$L|B?)zk2+VY;Ln?1LbUC+^~fx?Mj7N@MZ;H6F5 z*HPIyBI}=e7AQpG4|qPW)Gz&8`Bt z*!yp%Uivh2y219~ty6Dbq)Wx<^EEubc+jtK|BuG%M@?aHX^Y21AbotylUcgKbhWeN zX&PSS4RQsktK%XK6tOolbqXA&efTP!BS_E3hi!J|i)7jpC;{j!-W~XK0%VQQ7X-SZ zz#7-HtBj0|fjfZFK-xyvYBdm%HMMW(A6Q^S_fd;5Yk$;&W!` z>KsdZrVO&gW+Ug77-`1K3C5mB&o{<=N(11nu4297`9*>JyQ9M&77?3s5wh2?HKSZg z&WvPIU7nsfGFdkd>uzGkvooiNBDsvX@$Ki2CgAwVIG`x5#kxlqGxPIhX=<%S;hwvG zqLas6Vnf2fu#?^(t`uZ9z`%fx`;XyH+-!;_+6KXli~LU)>0&(qfZ3qqoEV?N7j### z>l4KiIuFFe2R>GyIf{$b8y2N@KImJ4j|S0kU8QnEF7i8Qg~Kw1Gc*QVj$so~+Ny!WeXT(XsI7>2OzU&<_UkR$z@> zyKC@Rz4(I)I$?QHLvS(2a$!8e%P^?ozx+jA$#~K6UVmzDZ%Bp7=SOy12I65R*y0Ma z&n`;4gSwF`X0sL3-s7-rJ;Y~G%fkxGvx;a8*EGGD_3XSLx0sE`JnLCJO42}+>SklK zRw*}$Gp)3D6?5>ZbO;i=5>a{OzL+Dq(lJfU>0zZ)v6ypJrSlW9tL>FnUx{5Cs=W4A z%w@LH<%^i>dZp`**!9E8>t|wa;3_vZad+-@cw(8mxcH6J1YHd;a~$>@F5y*dEK@V> z07+(g<%R4E0a%wvT8tJVX<79n9h;0P3B!vnX*bd9g!eNVyVK5?$O{z=Nw_}>WI+vZLx|AsDIvZ%&vm0#`+;}7 z+~zoA`_9?uN5D~iORk11SS0;f*O(uBg6!^Sicgt!bC3P0Z~Q1OB{V^td(ig`pd(J| zZ%MQ1>2(sNT{e$d)T|V;&U@e>6@Q0&F!(GUu}TRa*Kc)PC%8_Vl=M(G$>9(&^T;ce zDIZNbOLg!JrY(~B+=GeIgBd#;L6QcJ2>Pb+CA&xi)owp~*`oOTJkhN;kq8eQQvXSe zn@A|P4Y0-@MbAA@V>W1lozV;I_QyN=nsd&^iIa+L2ASZ!WurJV;(~6rSLFAa6S)WLhHy8*W`dtU^P~+WmeQdNKTd8B z^O8*y%#HF9T+0yYPPoI=n_1loE^}FQ&3JENOCMaLsMQ`9wha6>A>Bw!VD($lbre@P zr8&>_kPy2J`98$1hgng359}*!3St(T25Y^~`kQoaV0&hq4*c*(BiTc^-naBjy97j_ zjMhErCUKH1N?$v>A5vlCSV_1jw+PRh78LBa6ENmPjo`nt;=E%L7@xLGS}6v4%t_d^^@p}IideseWrA1GPr5LHFC=e{Ee|})GC0i( z`Z7e>%!*f!jNjdq*Wmu9J|1Kf>B(kaY(8=wziPNO=$f|(zy>S)aeZ?b(WD!raD3)& z-5~#qSh;zBlwCGn#V_7l^cL4%&zL?W~Cg4yLhT=+0%OJQxO= zfuIW3(9K|H_NU{Ck!wcN5wQtmy9>I40pDr{CoAYL8f?iu`H96qbDN=$F-+CXOidW( zj%Mc97?$DYdGCJ6STpNa4BNM6wx7F}No7*|yIw&qO>`{*K|T(rGz{XS*!<|HVECXJ z$yP4uGP{Il2tz+tr?0tJJa?PC&9$30sFB#WcAN@E%Are~6dY%#`=~gu4=wll#2;IM z<{L;9?n&dx7Px|B2v`FOOMllzHJCT_1WEz;AGgS<>5H5s^8~k;YuGojCkW9Dyj(#d z)S}OGPt4u+Og7d{G~AQ;_T|ZbhO&TeaaHFfqJ8ZygP=}@9%9!Xzhl~6^tVO0<@ z-XqF3gy6$WB%5z`kcg;mxi5#_9fUdS&Jg@A!yaFVIX|sG`>f_Qd0;WrFpptn>vYqa zS|VFQ*Exg}I0Jn38C#0*;pD5k*a{x5sJD?-QI-wk;iLcYW`JQQ#DX&n5A?@pZ(<6P zj2s4r&uh@ux2>%LCQ@4B)5K99t!ELNtSdsJ}6P$ruSf|6aNb{07IL^Vcym*y>^+o&)0?RJS$s|1tBz@*2d5H0!(t5}#u??qX6EaySq zjY#uF^<>>6U48?0R4@KiKls+TZgcP15JRd0zXF~Gy2>CMiiOS%5@-g<;GX!g_Hi*_ zu_qs+)fKRt{l4%u^K0SQjYz`{dMM`|tt+@9w=irZi%9cRs05qlhiW5J!}T&{N!As9UwZ&VRuDe50kopzW%2n9N0UQY2F-wr!rxJXo)g zGSFUkC(Kbv_s`YWR}JMjyHQ?8g)2CbV50O$`(s+Xv?PN>UT5KSbjg^SL`CB3@7*Cy zcka%sz3-0+V{A4J1y`ZqqQjN z76&ob*E%PMfl2Q!Spd}+jQL(yzL|QgY(5hC>_SvY(?4AtR&rJC9&AnPi}q(8E`_6) zPa5xhp#k27XvD+Mnpy-|@7gY~29lC#C>mGCHTByYC z_X8DnZ^nI%ExB^^PY9s5yZk%dO`kX%%6ckcRjN;c2^(c=WO^7T^Cxn^kjslZd+Q%w z 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

^*n**PQ_g?2B4|E7vJafzaJ;U+FbR_}9riZZ)- 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

>pYX3So0vz=dZ)=?J>_B5k1u_XJKmavJR5zq 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�|he-H2|Gw>tmh-Dvg$*D@q($Qxr;)xV_3@D9ot<5cko>g<(;dcM^uLsQxA% ztMO`*{6!c2Y{j))KVx(feImMo`Zo-mP>aEjj@L^}5F6l?G(n=6cLavFq}or*|4&q< zVE`yC1f+ymX0|RDdsl-I5SPJ<2v1h#_RC}(_FBl(%N#u{Q0s+kRmI7turVCZ;^4wt3VbC+TL<-*mX+;nTYH$U4|laa9-#w0@(HA($yuSd3` zar+`+`{ddaL%7x_HojF>RF~Pf6me(y_s=C?HV|_7G?lYAdu6HedufsZnFk+mi{2NF z_2B$|+C}f{P0}M?cV8-RAez+(%bF}m5pLp=Lie6NS*|siJ*^+s^(>m&B| zx%!78uptCwP_%qB<$WtQ5ZEvLT0Z#%eAzYuB^0oM5m@aCz(}%|#7};*BA-6#Ts01R z+{#7HY%8^em7ExPABmQfYRPn0T~zLWcxwKWzJz~+K`rdn!vUekkIj;$xs3YTmhLF^ z!i55m24Vx>Q=yAN-p+N5wn3xACC1F9EZs?W%47xW{S~-Mv0{dTu^`xkEjBKZEWRhh zHv=%3@)iS(RO}{($3wX+*yaFm8sz0e{{-#2j;g!(8~3?TujWMN+vUM0T)4}&h%%p! z)3ddPjN@WD8bQGfvij~Kh3`(U?FXz`)~`iBfsK#VMsJSFV}8%UOH{ao9&5R6Ocx{# zV^RMDt4Bx24txXnzhm{d-Tuc$(zDw7pBqUJ8M}XPB#GP8|K3PmTmJV(^0eFV-;Jbd z>S7UNYwgtIe{UqSrwZM?x!Po(Huz7}y1i+8@}b?~4uelL*FQFr|G?@ky-ol!37K~^ z{tK%&8{F1U{1;Y_wvlYGFEIblM)EIK@3H&a=QONd(R%j-ix+Ld9TB&O@YUbie$cRb z1I%xNeod)0CDS&NFL#=KXIf4p2Td$?zs^2|-gIwNwtF^L;YsaL%W}IGPA$3o7pv#u zzj%Att!kw6_|L)X>5Iiy`PS}bG4H*cl?cugm@_pY%N5~R?%Bv41`W>&8wTC$qxgf_D|28E-+ zO!F8J!?g(7Mlw})XHeqJn(vVGqq+i~M9kxzHwpu?yCW(yLA!4?KG*HO)A_ZvOWR1w z?TuofkNn55Qcw2A&DFo{O<0@A{iJOqgMUsrU3&6!+V$$UpEDk*x*|xgJDFT9evh8a zHH!6>?|;az`J%LN#WBUIDeAM^>_Y56uzJQTEE;x8H&$6Rfi^%EwN92SvrfG2!2O+wHSHfPDXC>%NYiuxwtr;&QZS?!fMgp0T50^_+s==vpi;$Xlz;#m5tM zEAKVv*$g-QIQero=e6%|fiXCBvWVLE>-gvw4XgLF`C_1KOJP9Fvi$WzBwa&UX7rIa zOA-&^fD4PxqF5&W-km=iOU&HA*2nb6PEf6F8y@nKL9C^`i2roq?Fba}wIl6w6sL}y z$Qbby>Y&I^%SHsxI3;l3|Gq6%vI`M@Od)`}#_v-^iE$lr$=F^#fPz$}zqD`6*90$) zb~U=kF`?<6=SbLBFd4bT?F@eo{wF7jM%cFsnUhRiKr-yMSM{4fI}UNdhGvLCMT6MY zg$vxA#8h2*UzT;!^7NiG!e7=h0lzS$fQRNV$YTg9G{na(0P{~~5I+sJXF-4=uOrO( z=+PW#0YWEgT$XPPbqZaEHn33w$6QDs_cpkE?!o9g%a>ce;7A`+9EY;#GP@tuSQ;Ig z4-5y-;Z*`_Mgk7sXH1WLn(h}zgxnmz4co3#XHc?yNQl(gn3U}4zGI%$l2^QPX5pYt z9T6c?UrcTMT+v)g6~7(;EXZzpget8;T<6NVk)3l1^^GA1H@~a+0n4r4Qp&Q!tKzw- z!lTVZ|32&1kFYhEqxScM!psx#8#~uc^o(mWLl1g{L-IbMc)hGb!n}oll$fZsp|kwA zR{3pWH+~M^(Z%~23V$BN>l?>r-FUewI7c>K<|(rAFnTBYB3~Gd7@n%n&E}uw>E(+k zWe<)4mfn5(;AcyCmSr89^Q$yeWlw?NTxZC?xkE#KOIdiw8WvGNXN2%LC=MeoltwYq zksfyPH%=1Z_-U*ncF!t8;N2rUB}ErR)8rm`A^Ne&y7Uh3YR4ZxrViFRI%+>~tt(}} z+6PskG-XCm2MYveOQ7ogIF^}cG5)V%^n#Lrmq7_i{L)jks0hpppZap9GXEa^f}Ctm z>w3XaV!!&LXr`~dqVU|hvF5%g%jK{np~cK<-5X;m-b62GPa{+#AH`@(U}&fU^8^gE zJMVm(>X;)82x^sP2bFlTT!BHAz0_f$9Y0&Y^7PWg9BJBXV%*z={tE*=4kTk$;R6y> zxBn?iaIZHHnFqr8g6O5Y1!N&(`^4uxGHytev0SZ&NPk!G4ka%mvky|#w-iB~Nvo`B z1gNrDj+dgK^6PDbNBKa__0y?v{?SG#m)0`8Nocjz%hy<*ga(G~H3=qdV6?SFL0UIF zXbqnr7AmTg znn^*c@hzy)HnpA^@)hOa9)SJV`X`+!eiHG_$UgmLPr~s!{;RlGmY+X<5Kq z{5lv&ca1W0q(Y4~H^=h$b}JT1#@gQnv<*5`XF%V3MI@V8+bzXRl4FA9VNKCty*(U( z7oi%jP|)V`K2{|CarS&XM{kZTW985XL6+J@QE!3BaFU!E01!XuJ_ zEZHQL)w2#PhD%*M;5HeDobx{$su|*haB((14@*tECcd)i$f!+sF*{7IT=kZe$KOrO z4n*h)sRxSIP9TCRvyhp_A z?&RF`!}bWT1?&Cp|1%!7v1+^aVc^^@+QP=`spuNY*l0{IpF?zFG9J{#GUyPn-v}?_ zfd59tUGmjs$LetoT1_Xp@>D0lrf&R3#k~7#7MIPk!H zwXv*hjXe`?X*+E}-b>~Y)yPJrh?cUG6C$g_6r8XQ0UOp%gQ4pF&b-QO;|^?JJeZsF zC1IlH&J&Z|XT?4z@cs#d{_bTI+m>ubN!BSNTEulD0b=K=dw52iDXVB&Jdl0FGltp- zqms--MH%PBppjTa_@HQtp@lr>X&%)J42A|(2XL#(sEKISB?mp3A!(i|A)dPPGm5)K4rlHX{OkIM!`?QSkG3*pJ@c2AXt8c zfq5GNm#wqT{$%|?u|1)%3W~r^Q1JPZ_&*!TeiN}SE$|#NU2h{48OH2IVobCKJBUD8 z8|j-%z%+1x7m>xq0~kT3jlIBc#9*u>SRxexs6#NQj)G`@j4(Q&e&h+potAhe$DDdk<*^C4bmd=jf znsF2bn4*K831iY61#yfrDxmU8)aXaq7~08Ti$;JA860HFE$9JjZv-Tw0L~sjn-cgf zl|u7K!$UFyD+(||R!bxTUlp?OdH_i%0A>_$76Cz$!K_3^88S#=odHRAmxIWFB!l=V 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 +Here are some projects in the OPA ecosystem that can help with debugging Rego policies. + + +# Debugging OPA Instances in Distributed Systems + +Debugging problems in distributed systems poses a number of challenges. Since OPA is commonly deployed in a distributed +fashion, as part of a larger platform, it is helpful to understand the various tools and techniques available for +debugging OPA in these environments. + +## OPA Logs + +OPA logs are a great place to start when debugging issues. The logs can be used to understand what OPA is doing +at any given time. Common issues such as failing to load in policy or data bundles will be shown here. + +You can also enable debug logging to get more detailed information about what OPA is doing with `--log-level debug`. +This is documented in the [CLI documentation](./cli/#options-10) for `opa run`. + +### Decision Logging + +When OPA responds to a query, it is making a decision based on the policy and data that it has loaded. With the default +logging configuration, these are not logged in detail to the OPA logs. However, it is possible to enable console decision +logging by setting the following in OPA's config file: + +```yaml +decision_logs: + console: true +``` + +It might be preferable to send these logs to an HTTP endpoint or other system, to learn more about decision logging, +take a look at the [Decision Logging documentation](./management-decision-logs). + +## Metrics, Health and Status APIs + +Like other cloud-native tools, OPA exposes `/metrics` and `/health` endpoints that can be used to understand the +state of an OPA instance at any given time. + +- `/metrics` - exposes Prometheus metrics about the OPA instance's memory use, bundle loading and HTTP requests. + Read more in the [Metrics documentation](./monitoring). +- `/health` - shows information about the instance's readiness to serve requests, there are options available to also + show information about the loading of bundles and other plugins. Read more about the endpoint in the + [Health API documentation](./rest-api/#health-api). +- `/status` - is a JSON formatted endpoint that shows both health and metrics information. Read more in + [Status API documentation](./rest-api/#status-api). + +## Manually Querying OPA + +In distributed systems, it's common that an OPA instance is being invoked by another service, sometimes it can be helpful +to isolate the OPA instance and query it directly. This can be done using the [REST API](./rest-api). + +For example, to get a snapshot of the data that OPA has loaded, you can use the following command: + +```shell +curl --silent https://$OPA_HOSTNAME/v1/data +``` + +Or to manually evaluate a policy rule with some input: + +```shell +curl -X POST https://$OPA_HOSTNAME/v0/data/example_package/example_rule -d '{"foo": "bar"}' +``` + +## Load a Production Bundle Locally + +Sometimes there are too many moving parts in a distributed system to debug an issue effectively on a live system. +In these cases, it can be helpful to load a bundle into a local OPA instance and debug the issue there. + +You can quickly start an OPA instance with a remote bundle using the following command: + +```shell +opa run -s https://example.com/bundles/bundle.tar.gz +``` + +If you need to configure the OPA instance with other options, you can use a config file to +make more detailed configurations. Read more in the [Configuration documentation](./configuration) documentation. diff --git a/third_party/opa/docs/docs/deployments.md b/third_party/opa/docs/docs/deployments.md new file mode 100644 index 000000000000..652c7ac83f62 --- /dev/null +++ b/third_party/opa/docs/docs/deployments.md @@ -0,0 +1,553 @@ +--- +title: Deployment +--- + +This document helps you get OPA up and running in different deployment +environments. You should read this document if you are planning to deploy OPA. + +## Docker + +Docker makes OPA easy to deploy in different types of environments. + +This section explains how to use the official OPA Docker images. If this is your +first time deploying OPA and you plan to use one of the Docker images, we +recommend you review this section to familiarize yourself with the basics. + +OPA releases are available as images on Docker Hub. + +- [openpolicyagent/opa](https://hub.docker.com/r/openpolicyagent/opa/) + +### Running with Docker + +If you start OPA outside of Docker without any arguments, it prints a list of +available commands. By default, the official OPA Docker image executes the `run` +command which starts an instance of OPA as an interactive shell. This is nice +for development, however, for deployments, we want to run OPA as a server. + +The `run` command accepts a `--server` (or `-s`) flag that starts OPA as a +server. See `--help` for more information on other arguments. The most important +command line arguments for OPA's server mode are: + +- `--addr` to set the listening address (default: `localhost:8181`). +- `--log-level` (or `-l`) to set the log level (default: `"info"`). +- `--log-format` to set the log format (default: `"json"`). + +By default, OPA listens for normal HTTP connections on `localhost:8181`. To make +OPA listen for HTTPS connections, see [Security](./security). + +We can run OPA as a server using Docker: + +```bash +docker run -p 8181:8181 openpolicyagent/opa \ + run --server --log-level debug --addr=0.0.0.0:8181 +``` + +:::info +We have to use `--addr` here to bind to all interfaces to ensure OPA is +accessible from outside the container. This is not necessary when running OPA +in other environments. + +More information can be found in the +[security documentation](./security/#interface-binding). +::: + +Test that OPA is available: + +``` +curl -i localhost:8181/ +``` + +#### Logging + +OPA logs to stderr and the level can be set with `--log-level/-l`. The default log level is `info` which causes OPA to log request/response information. + +``` +{"client_addr":"[::1]:64427","level":"debug","msg":"Received request.","req_body":"","req_id":1,"req_method":"GET","req_params":{},"req_path":"/v1/data","time":"20.7.13-11T18:22:18-08:00"} +{"client_addr":"[::1]:64427","level":"debug","msg":"Sent response.","req_id":1,"req_method":"GET","req_path":"/v1/data","resp_bytes":13,"resp_duration":0.392554,"resp_status":200,"time":"20.7.13-11T18:22:18-08:00"} +``` + +If the log level is set to `debug` the request and response message bodies will be logged. This is useful for development however it can be expensive in production. + +``` +{"addrs":[":8181"],"insecure_addr":"","level":"info","msg":"First line of log stream.","time":"2019-05-08T17:25:26-07:00"} +{"level":"info","msg":"Starting decision log uploader.","plugin":"decision_logs","time":"2019-05-08T17:25:26-07:00"} +{"client_addr":"[::1]:63902","level":"info","msg":"Received request.","req_body":"","req_id":1,"req_method":"GET","req_params":{},"req_path":"/v1/data","time":"2019-05-08T17:25:41-07:00"} +{"client_addr":"[::1]:63902","level":"info","msg":"Sent response.","req_id":1,"req_method":"GET","req_path":"/v1/data","resp_body":"{\"decision_id\":\"f4b41501-2408-4a14-8269-1c1085abeda4\",\"result\":{}}","resp_bytes":66,"resp_duration":2.545972,"resp_status":200,"time":"2019-05-08T17:25:41-07:00"} +``` + +The default log format is json and intended for production use. For more human readable +formats use "json-pretty" or "text". + +> **Note:** The `text` log format is not performance optimized or intended for production use. + +#### Volume Mounts + +By default, OPA does not include any data or policies. + +The simplest way to load data and policies into OPA is to provide them via the +file system as command line arguments. When running inside Docker, you can +provide files via volume mounts. + +```bash +docker run -v $PWD:/example openpolicyagent/opa eval -d /example 'data.example.greeting' +``` + +```rego title="policy.rego" +package example + +greeting := msg if { + info := opa.runtime() + + # Docker sets the HOSTNAME environment variable. + hostname := info.env["HOSTNAME"] + + msg := sprintf("hello from container %q!", [hostname]) +} +``` + +#### More Information + +For more information on OPA's command line, see `--help`: + +``` +docker run openpolicyagent/opa run --help +``` + +### Tagging + +The Docker Hub repository contains tags for every release of OPA. For more +information on each release see the [GitHub Releases](https://github.com/open-policy-agent/opa/releases) page. + +The "latest" tag refers to the most recent release. The latest tag is convenient +if you want to quickly try out OPA however for production deployments, we +recommend using an explicit version tag. + +Development builds are also available on Docker Hub. For each version the +`{version}-dev` tag refers the most recent development build for that version. + +The `edge` tag refers to the current `main` branch of OPA. Useful for testing +unreleased features. It is not recommended to use `edge` for production deployments. + +The version information is contained in the OPA executable itself. You can check +the version with the following command: + +```bash +docker run openpolicyagent/opa version +``` + +## Kubernetes + +### Kicking the Tires + +This section shows how to quickly deploy OPA on top of Kubernetes to try it out. + +> If you are interested in using OPA to enforce admission control policies in +> Kubernetes, see the [Kubernetes Admission Control Tutorial](./kubernetes/tutorial). + +> These steps assume Kubernetes is deployed with +> [minikube](https://github.com/kubernetes/minikube). If you are using a different +> Kubernetes provider, the steps should be similar. You may need to use a +> different Service configuration at the end. + +First, create a ConfigMap containing a test policy. + +In this case, the policy file does not contain sensitive information so it's +fine to store as a ConfigMap. If the file contained sensitive information, then +we recommend you store it as a Secret. + +```rego title="example.rego" +package example + +greeting := msg if { + info := opa.runtime() + + # Kubernetes sets the HOSTNAME environment variable. + hostname := info.env["HOSTNAME"] + + msg := sprintf("hello from pod %q!", [hostname]) +} +``` + +```bash +kubectl create configmap example-policy --from-file example.rego +``` + +Next, create a Deployment to run OPA. The ConfigMap containing the policy is +volume mounted into the container. This allows OPA to load the policy from +the file system. + + +```yaml title="deployment-opa.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: opa + labels: + app: opa +spec: + replicas: 1 + selector: + matchLabels: + app: opa + template: + metadata: + labels: + app: opa + name: opa + spec: + containers: + - name: opa + image: openpolicyagent/opa:{{ current_version_docker }} + ports: + - name: http + containerPort: 8181 + args: + - "run" + - "--ignore=.*" # exclude hidden dirs created by Kubernetes + - "--server" + - "/policies" + volumeMounts: + - readOnly: true + mountPath: /policies + name: example-policy + volumes: + - name: example-policy + configMap: + name: example-policy +``` + + +```bash +kubectl create -f deployment-opa.yaml +``` + +At this point OPA is up and running. Create a Service to expose the OPA API so +that you can query it: + +```yaml title="service-opa.yaml" +kind: Service +apiVersion: v1 +metadata: + name: opa + labels: + app: opa +spec: + type: NodePort + selector: + app: opa + ports: + - name: http + protocol: TCP + port: 8181 + targetPort: 8181 +``` + +```bash +kubectl create -f service-opa.yaml +``` + +Get the URL of OPA using `minikube`: + +```bash +OPA_URL=$(minikube service opa --url) +``` + +Now you can query OPA's API: + +```bash +curl $OPA_URL/v1/data +``` + +OPA will respond with the greeting from the policy (the pod hostname will differ): + +```json +{ + "result": { + "example": { + "greeting": "hello from pod \"opa-78ccdfddd-xplxr\"!" + } + } +} +``` + +### Readiness and Liveness Probes + +OPA exposes a `/health` API endpoint that you can configure Kubernetes +[Readiness and Liveness Probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/) +to call. For example: + + +```yaml +containers: +- name: opa + image: openpolicyagent/opa:{{ current_version_docker }} + ports: + - name: http + containerPort: 8181 + args: + - "run" + - "--ignore=.*" # exclude hidden dirs created by Kubernetes + - "--server" + - "/policies" + volumeMounts: + - readOnly: true + mountPath: /policies + name: example-policy + livenessProbe: + httpGet: + path: /health + scheme: HTTP # assumes OPA listens on localhost:8181 + port: 8181 + initialDelaySeconds: 5 # tune these periods for your environment + periodSeconds: 5 + readinessProbe: + httpGet: + path: /health?bundle=true # Include bundle activation in readiness + scheme: HTTP + port: 8181 + initialDelaySeconds: 5 + periodSeconds: 5 +``` + + +See the [Health API](./rest-api#health-api) documentation for more detail on the `/health` API endpoint. + +## HTTP Proxies + +OPA uses the standard Go [net/http](https://golang.org/pkg/net/http/) package +for outbound HTTP requests that download bundles, upload decision logs, etc. In +environments where an HTTP proxy is required, you can configure OPA using the +pseudo-standard `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment +variables. + +## CPU and Memory Requirements + +For more information see the [Resource Utilization section on the Policy Performance page](./policy-performance#resource-utilization). + +## Operational Readiness and Failure Modes + +Depending on how you deploy OPA, it may or may not have policies available as soon as it starts up. If OPA starts making decisions without any policies, it will return `undefined` as an answer to all policy queries. This can be problematic because even though OPA returns a response, it has not actually returned the decision dictated by policy. + +For example, without loading any policies into OPA whatsoever, a policy query will return the answer `undefined`, which via the HTTP API is represented as an empty JSON object `{}`. + +``` +$ opa run -s +$ curl localhost:8181/v1/data/foo/bar +{} +``` + +In contrast, when policies are loaded, OPA is operationally ready for policy queries, and the answer is defined, the answer is a JSON object of the form `{"result": ...}` + +``` +$ opa run foo.rego -s +$ curl localhost:8181/v1/data/foo/bar +{"result": 7} +``` + +However, it is possible that even though policies have been loaded the policy response is still `undefined` because the policy makes no decision for the given inputs. + +``` +$ opa run foo.rego -s +$ curl localhost:8181/v1/data/baz +{} +``` + +Just because OPA has returned an answer for a policy query, that does not indicate that it was operationally ready for that query. Moreover, the operational readiness of OPA cannot be ascertained from the query response, as illustrated above. Two issues must therefore be addressed: how to know when OPA is operationally ready for policy queries and how to make a decision before OPA is ready. + +### Ensuring Operational Readiness + +The relevance of the discussion above depends on how you have chosen to deploy policies into OPA. + +If you deploy policies to OPA on disk (e.g. volume mounting into the OPA container on Kubernetes), then OPA will only start answering policy queries once all the policies are successfully loaded. In this case, it is impossible for OPA to answer policy queries before it has loaded policy, so the discussion above is a non-issue. + +On the other hand, if you use the [Bundle service](./management-bundles) OPA will start up without any policies and immediately start downloading a bundle. But even before the bundle has successfully downloaded, OPA will answer policy queries if asked (which is in every case except the bootstrap case the right thing to do). For this reason, OPA provides a `/health` [API](./rest-api/#health-api) that verifies that the server is operational and optionally that a bundle has been successfully activated. As long as no policy queries are routed to OPA until the `/health` API verifies that OPA is operational. The recommendation is to ensure the `/health` API indicates that OPA is operational before routing policy queries to it. + +Finally, you might choose to push policies into OPA via its [REST API](./rest-api/#create-or-update-a-policy). In this case, there is no way for OPA to know whether it has a complete policy set, and so the decision as to when to route policy queries to OPA must be handled by whatever software is pushing policies into OPA. + +### Making Decisions before OPA is Ready + +The mechanisms discussed above ensure that OPA is not asked to answer policy queries before it is ready to do so. But from the perspective of the software needing decisions, until OPA is operational, the software must make a decision on its own. Typically there are two choices: + +- fail-open: if OPA does not provide a decision, then treat the decision as allowed. +- fail-closed: if OPA does not provide a decision, then treat the decision as denied. + +The choices are more varied if the policy is not making an allow/deny decision, but often there is some analog to fail-open and fail-closed. The key observation is that this logic is entirely the responsibility of the software asking OPA for a policy decision. Despite the fact that what to do when OPA is unavailable is technically a policy question, it is one that we cannot rely on OPA to answer. The right logic can depend on many factors including the likelihood of OPA not making a decision and the cost of allowing or denying a request incorrectly. + +In Kubernetes admission control, for example, the Kubernetes admin can choose whether to fail-open or fail-closed, leaving the decision up to the user. And often this is the correct way to build an integration because it is unlikely that there is a universal solution. For example, running an OPA-integration in a development environment might require fail-open, but running exactly the same integration in a production environment might require fail-closed. + +## Capabilities + +OPA now supports a _capabilities_ check on policies. The check allows callers to restrict the [built-in](./policy-reference/#built-in-functions) functions that policies may depend on. If the policies passed to OPA require built-ins not listed in the capabilities structure, an error is returned. The capabilities check is currently supported by the `check` and `build` sub-commands and can be accessed programmatically on the `ast.Compiler` structure. The OPA repository includes a set of capabilities files for previous versions of OPA in the [capabilities](https://github.com/open-policy-agent/opa/tree/main/capabilities) folder. + +For example, given the following policy: + +```rego +package example + +deny contains "missing semantic version" if { + not valid_semantic_version_tag +} + +valid_semantic_version_tag if { + semver.is_valid(input.version) +} +``` + +We can check whether it is compatible with different versions of OPA: + +```bash +# OK! +$ opa build ./policies/example.rego --capabilities ./capabilities/v0.22.0.json + +# ERROR! +$ opa build ./policies/example.rego --capabilities ./capabilities/v0.21.1.json +``` + +### Built-ins + +The 'build' command can validate policies against a configurable set of OPA capabilities. The capabilities define the built-in functions and other language features that policies may depend on. For example, the following capabilities file only permits the policy to depend on the "plus" built-in function ('+'): + +```json +{ + "builtins": [ + { + "name": "plus", + "infix": "+", + "decl": { + "type": "function", + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + } + } + } + ] +} +``` + +The following command builds a directory of policies ('./policies') and validates them against `capability-built-in-plus.json`: + +```bash +opa build ./policies --capabilities ./capability-built-in-plus.json +``` + +### Network + +When passing a capabilities definition file via `--capabilities`, one can restrict which hosts remote schema definitions can be retrieved from. For example, a `capabilities.json` containing the json below would disallow fetching remote schemas from any host but "kubernetesjsonschema.dev". Setting `allow_net` to an empty array would prohibit fetching any remote schemas. + +```json title="capabilities.json" +{ + "builtins": [ ... ], + "allow_net": [ "kubernetesjsonschema.dev" ] +} +``` + +Not providing a capabilities file, or providing a file without an `allow_net` key, will permit fetching remote schemas from any host. + +Note that the metaschemas [http://json-schema.org/draft-04/schema](http://json-schema.org/draft-04/schema), [http://json-schema.org/draft-06/schema](http://json-schema.org/draft-06/schema), and [http://json-schema.org/draft-07/schema](http://json-schema.org/draft-07/schema), are always available, even without network access. + +Similarly, the `allow_net` capability restricts what hosts the `http.send` built-in function may send requests to, and what hosts the `net.lookup_ip_addr` built-in function may resolve IP addresses for. + +### Features + +Some features of OPA can be toggled on and off through the `features` list: + +```json +{ + "features": [ + "rule_head_ref_string_prefixes", + "rule_head_refs", + "rego_v1_import" + ] +} +``` + +Features present in the list are enabled, while features not present are disabled. The following features are available: + +- `rule_head_ref_string_prefixes`: Enables the use of a [reference in place of name](./policy-language/#rule-heads-containing-references) in the head of rules. This is a subset of `rule_head_refs`, and only covers references where all terms are primitive types, or where only the last element of the ref (the key in the generated object or set) is allowed to be a variable. +- `rule_head_refs`: Enables general support for [references in rule heads](./policy-language/#rule-heads-containing-references), including [variables at arbitrary locations](./policy-language/#variables-in-rule-head-references). This feature also covers the functionality of `rule_head_ref_string_prefixes`. +- `rego_v1_import`: enables use of the `rego.v1` import. + +### Future keywords + +:::info +It is recommended to use the `rego.v1` import instead of `future.keywords` imports, as this will ensure that your policy is compatible with the future release of [OPA v1.0](./v0-upgrade/) +If the `rego.v1` import is present in a module, then `future.keywords` and `future.keywords.*` import is implied, and not allowed. +::: + +The availability of future keywords in an OPA version can also be controlled using the capabilities file: + +```json +{ + "future_keywords": ["in"] +} +``` + +With these capabilities, the future import `future.keywords.in` would be available. See [the documentation +of the membership and iteration operator for details](./policy-language/#membership-and-iteration-in). + +### Wasm ABI compatibility + +A specific OPA version's capabilities file shows which Wasm ABI versions it is capable of evaluating: + +```json +{ + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ] +} +``` + +This snippet would allow for evaluating bundles containing Wasm modules of the ABI version 1.1 and 1.2. +See [the ABI version docs](./wasm/#abi-versions) for details. + +### Building your own capabilities JSON + +Use the following JSON structure to build more complex capability checks. + +```json +{ + "builtins": [ + { + "name": "name", // REQUIRED: Unique name of built-in function, e.g., (arg1,arg2,...,argN) + + "infix": "+", // OPTIONAL: Unique name of infix operator. Default should be unset. + + "decl": { // REQUIRED: Built-in function type declaration. + "type": "function", // REQUIRED: states this is a function + + "args": [ // REQUIRED: List of types to be passed in as an argument: any, number, string, boolean, object, array, set. + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { // REQUIRED: The expected result type. + "type": "number" + } + } + } + ], + "allow_net": [ // OPTIONAL: allow_net is an array of hostnames or IP addresses, that an OPA instance is allowed to connect to. + "mycompany.com", + "database.safe" + ], + "future_keywords": ["in"] +} +``` diff --git a/third_party/opa/docs/docs/docker-authorization.md b/third_party/opa/docs/docs/docker-authorization.md new file mode 100644 index 000000000000..d1a99da51126 --- /dev/null +++ b/third_party/opa/docs/docs/docker-authorization.md @@ -0,0 +1,445 @@ +--- +title: Docker +--- + +Docker’s out-of-the-box authorization model is all or nothing. But many users +require finer-grained access control and Docker’s plugin infrastructure allows +us to do so. + +This is an excellent opportunity to see how to policy enable an existing +service. + +## Goals + +This tutorial helps you get started with OPA and introduces you to core concepts +in OPA. + +> Policy enabling an application decouples the policy implementation from the +> business logic so that administrators can define policy without changing the +> application while still keeping up with the size, complexity, and dynamic +> nature of modern applications. + +For the purpose of this tutorial, we want to use OPA to enforce a policy that +prevents users from running insecure containers. + +This tutorial illustrates two key concepts: + +1. OPA policy definition is decoupled from the implementation of the service + (in this case Docker). The administrator is empowered to define and manage + policies without requiring changes to any of the apps. + +2. Both the data relevant to policy and the policy definitions themselves can + change rapidly. + +## Prerequisites + +This tutorial requires: + +- Docker Engine 18.06.0-ce or newer +- Docker API version 1.38 or newer +- `root` or `sudo` access +- Nginx, or any capable [bundle](https://www.openpolicyagent.org/docs/latest/management-bundles/) server + +The tutorial has been tested on the following platforms: + +- Ubuntu 20.04 (64-bit) + +If you are using a different distro, OS, or architecture, the steps will be the +same. However, there may be slight differences in the commands you need to run. + +## Steps + +Several of the steps below require `root` or `sudo` access. When you are +modifying files under `/etc/docker` or signalling the Docker daemon to +restart, you will need root access. + +### 1. Create an empty policy definition that will allow all requests. + +**authz.rego**: + +```rego +package docker.authz + +allow := true +``` + +This policy defines a single rule named `allow` that always produces the +decision `true`. Once all the components are running, we will come back to +the policy. + +### 2. Create policy bundle and OPA configuration. + +For the purpose of this example, we are going to use [Nginx](https://www.openpolicyagent.org/docs/latest/management-bundles/#nginx) +to serve bundles from the same machine Docker is running on. + +With nginx running, simply build the policy bundle placed into the nginx web root directory. + +```shell +opa build --bundle --output /var/www/html/bundle.tar.gz . +``` + +Next, create an OPA configuration file pointing to the bundle. + +```yaml +services: + authz: + url: http://localhost + +bundles: + authz: + service: authz + resource: bundle.tar.gz + +# Optional - Print decisions in the Docker logs. Configure a remote service for production use cases. +decision_logs: + console: true +``` + +Save the above file as `opa-config.yaml`. We'll need to place this somewhere where the plugin can find it. +The `/etc/docker` directory will be mounted as `/opa` in the container running the plugin, so let's create a +sub-directory for our configuration file there. + +```shell +sudo mkdir -p /etc/docker/config +sudo mv opa-config.yaml /etc/docker/config/ +``` + +### 3. Install the opa-docker-authz plugin. + +Install the `opa-docker-authz` plugin and point it to the config file just created. + +```shell +docker plugin install --alias opa-docker-authz ghcr.io/open-policy-agent/opa-docker-authz:v0.10 opa-args="-config-file /opa/config/opa-config.yaml" +``` + +You need to configure the Docker daemon to use the plugin for authorization. + +```shell +cat > /etc/docker/daemon.json < + +Again, rebuild the bundle and save it in the Nginx document root directory. + +```shell +opa build --bundle --output /var/www/html/bundle.tar.gz . +``` + +The plugin queries the `allow` rule to authorize requests to Docker. The `input` +document is set to the attributes passed from Docker. + +```rego +package example + +result := data.docker.authz.allow +``` + + + +

+ +Click to expand the input document + +Look for `SecurityOpt` and try changing it to see the result. + +```json title="input.json" +{ + "AuthMethod": "", + "Body": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": null, + "Env": [], + "HostConfig": { + "AutoRemove": false, + "Binds": null, + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 0, + "BlkioWeightDevice": [], + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 0, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "Devices": [], + "DiskQuota": 0, + "Dns": [], + "DnsOptions": [], + "DnsSearch": [], + "ExtraHosts": null, + "GroupAdd": null, + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "", + "Isolation": "", + "KernelMemory": 0, + "Links": null, + "LogConfig": { + "Config": {}, + "Type": "" + }, + "MaskedPaths": null, + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": -1, + "NanoCpus": 0, + "NetworkMode": "default", + "OomKillDisable": false, + "OomScoreAdj": 0, + "PidMode": "", + "PidsLimit": 0, + "PortBindings": {}, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": null, + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "no" + }, + "SecurityOpt": ["seccomp:unconfined"], + "ShmSize": 0, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "Hostname": "", + "Image": "hello-world", + "Labels": {}, + "NetworkingConfig": { + "EndpointsConfig": {} + }, + "OnBuild": null, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "", + "Volumes": {}, + "WorkingDir": "" + }, + "Headers": { + "Content-Length": "1470", + "Content-Type": "application/json", + "User-Agent": "Docker-Client/18.06.1-ce (linux)" + }, + "Method": "POST", + "Path": "/v1.38/containers/create", + "User": "" +} +``` + + + +
+ +### 7. Test the policy is working by running a simple container: + +```shell +docker run hello-world +``` + +Now try running the same container but disable seccomp (which should be +prevented by the policy): + +```shell +docker run --security-opt seccomp:unconfined hello-world +``` + +Congratulations! You have successfully prevented containers from running without +seccomp! + +The rest of the tutorial shows how you can grant fine-grained access to specific +clients. + +### 8. Identify the user in Docker requests. + +> Back up your existing Docker configuration, just in case. You can replace your +> original configuration after you are done with the tutorial. + +```shell +mkdir -p ~/.docker +cp ~/.docker/config.json ~/.docker/config.json~ +``` + +To identify the user, include an HTTP header in all of the requests sent to the +Docker daemon: + +```shell +cat >~/.docker/config.json < Docker does not currently provide a way to authenticate clients. But in Docker +> 1.12, clients can be authenticated using TLS and there are plans to include +> other means of authentication. For the purpose of this tutorial, we assume that +> an authentication system is place. + +### 9. Update the policy to include basic user access controls. + +```rego +package docker.authz + +default allow := false + +# allow if the user is granted read/write access. +allow if { + user_id := input.Headers["Authz-User"] + user := users[user_id] + not user.readOnly +} + +# allow if the user is granted read-only access and the request is a GET. +allow if { + user_id := input.Headers["Authz-User"] + users[user_id].readOnly + input.Method == "GET" +} + +# users defines permissions for the user. In this case, we define a single +# attribute 'readOnly' that controls the kinds of commands the user can run. +users := { + "bob": {"readOnly": true}, + "alice": {"readOnly": false}, +} +``` + +### 10. Attempt to run a container. + +Because the configured user is `"bob"`, the request is rejected: + +```shell +docker run hello-world +``` + +### 11. Change the user to "alice" and re-run the container. + +```shell +cat > ~/.docker/config.json <>+Envoy: Request (HTTP) + Envoy->>+OPA: External Authz Request (gRPC) + OPA->>+Envoy: External Authz Response (gRPC) + Envoy->>+Service: Request (HTTP) + Service->>+Envoy: Response (HTTP) + Envoy->>+Client: Response (HTTP) +``` + +> 💡 The OPA-Envoy plugin is frequently deployed in Kubernetes environments as a sidecar container however it can also +> be used in other environments as a standalone process running next to Envoy. + +## Configuration + +The OPA-Envoy plugin supports the following configuration fields: + +| Field | Required | Description | +| --------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `plugins["envoy_ext_authz_grpc"].addr` | No | Set listening address of Envoy External Authorization gRPC server. This must match the value configured in the Envoy config. Default: `:9191`. | +| `plugins["envoy_ext_authz_grpc"].path` | No | Specifies the hierarchical policy decision path. The policy decision can either be a `boolean` or an `object`. If boolean, `true` indicates the request should be allowed and `false` indicates the request should be denied. If the policy decision is an object, it **must** contain the `allowed` key set to either `true` or `false` to indicate if the request is allowed or not respectively. It can optionally contain a `headers` field to send custom headers to the downstream client or upstream. An optional `body` field can be included in the policy decision to send a response body data to the downstream client. Also an optional `http_status` field can be included to send a HTTP response status code to the downstream client other than `403 (Forbidden)`. Default: `envoy/authz/allow`. | +| `plugins["envoy_ext_authz_grpc"].dry-run` | No | Configures the Envoy External Authorization gRPC server to unconditionally return an `ext_authz.CheckResponse.Status` of `google_rpc.Status{Code: google_rpc.OK}`. Default: `false`. | +| `plugins["envoy_ext_authz_grpc"].enable-reflection` | No | Enables gRPC server reflection on the Envoy External Authorization gRPC server. Default: `false`. | +| `plugins["envoy_ext_authz_grpc"].proto-descriptor` | No | Set the path to a pb that enables the capability to decode the raw body to the parsed body. Default: turns this capability off. | +| `plugins["envoy_ext_authz_grpc"].grpc-max-recv-msg-size` | No | Set the max message size in bytes the gRPC server can receive. Defaults to 4MB. | +| `plugins["envoy_ext_authz_grpc"].grpc-max-send-msg-size` | No | Set the max message size in bytes the gRPC server can send. Defaults to 2048MB. | +| `plugins["envoy_ext_authz_grpc"].skip-request-body-parse` | No | Specifies if the plugin should skip parsing the input request body. Default: `false`. | + +If the configuration does not specify the `path` field, `envoy/authz/allow` will be considered as the default policy +decision path. `data.envoy.authz.allow` will be the name of the policy decision to query in the default case. + +The `dry-run` parameter is provided to enable you to test out new policies. You can set `dry-run: true` which will +unconditionally allow requests. Decision logs can be monitored to see what "would" have happened. This is especially +useful for initial integration of OPA or when policies undergo large refactoring. + +The `enable-reflection` parameter registers the Envoy External Authorization gRPC server with reflection. After enabling +server reflection, a command line tool such as [grpcurl](https://github.com/fullstorydev/grpcurl) can be used to invoke +RPC methods on the gRPC server. See [Interacting with the gRPC server](./envoy/debugging#interacting-with-the-grpc-server) +section for more details. + +Providing a file containing a protobuf descriptor set allows the plugin to decode gRPC message payloads. +So far, only unary methods using uncompressed protobuf-encoded payloads are supported. +The protoset can be generated using `protoc`, e.g. `protoc --descriptor_set_out=protoset.pb --include_imports`. + +## Additional Resources + +See the following pages on [envoyproxy.io](https://www.envoyproxy.io/) for more +information on external authorization: + +- [External Authorization](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ext_authz_filter.html) + to learn about the External Authorization filter. +- [Network](https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/network_filters/ext_authz_filter#config-network-filters-ext-authz) + and [HTTP](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter#config-http-filters-ext-authz) + for details on configuring the External Authorization filter. + +## Ecosystem Projects + + +Here are some projects relating to Envoy and OPA from the OPA ecosystem. + diff --git a/third_party/opa/docs/docs/envoy/performance.md b/third_party/opa/docs/docs/envoy/performance.md new file mode 100644 index 000000000000..193e7f4c675e --- /dev/null +++ b/third_party/opa/docs/docs/envoy/performance.md @@ -0,0 +1,260 @@ +--- +title: Performance +sidebar_position: 6 +--- + +This page provides some guidance and best practices around benchmarking the performance of the OPA-Envoy plugin in order +to give users an idea of the overhead of using the plugin. It describes an example setup to perform the benchmarks, different +benchmarking scenarios and important metrics that should be captured to understand the impact of the OPA-Envoy plugin. + +### Benchmark Setup + +#### Sample App + +The first component of the setup features a simple Go app which provides information about employees in a company. It +exposes a `/people` endpoint to `get` and `create` employees. The app's source code can be found [here](https://github.com/ashutosh-narkar/go-test-server). + +#### Envoy + +Next, is the Envoy proxy that runs alongside the example application. The Envoy configuration below defines an external authorization +filter `envoy.ext_authz` for a gRPC authorization server. The config uses Envoy’s in-built gRPC client which +is a minimal custom implementation of gRPC to make the external gRPC call. + +```yaml +static_resources: + listeners: + - address: + socket_address: + address: 0.0.0.0 + port_value: 8000 + filter_chains: + - filters: + - name: envoy.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + codec_type: auto + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: backend + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: service + http_filters: + - name: envoy.ext_authz + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz + transport_api_version: V3 + with_request_body: + max_request_bytes: 8192 + allow_partial_message: true + failure_mode_allow: false + grpc_service: + envoy_grpc: + cluster_name: opa-envoy + timeout: 0.5s + - name: envoy.filters.http.router + clusters: + - name: service + connect_timeout: 0.25s + type: strict_dns + lb_policy: round_robin + load_assignment: + cluster_name: service + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 8080 + - name: opa-envoy + connect_timeout: 1.25s + type: strict_dns + lb_policy: round_robin + http2_protocol_options: {} + load_assignment: + cluster_name: opa-envoy + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 9191 +admin: + access_log_path: "/dev/null" + address: + socket_address: + address: 0.0.0.0 + port_value: 8001 +layered_runtime: + layers: + - name: static_layer_0 + static_layer: + envoy: + resource_limits: + listener: + example_listener_name: + connection_limit: 10000 + overload: + global_downstream_max_connections: 50000 +``` + +#### OPA-Envoy Plugin + +Now let's deploy OPA as an External Authorization server. Below is a sample configuration for the OPA-Envoy container: + + +```yaml +containers: +- image: openpolicyagent/opa:{{ current_version_docker_envoy }} + imagePullPolicy: IfNotPresent + name: opa + resources: + requests: + memory: "64Mi" + cpu: "1m" + limits: + memory: "128Mi" + cpu: "2m" + args: + - "run" + - "--server" + - "--addr=localhost:8181" + - "--diagnostic-addr=0.0.0.0:8282" + - "--set=plugins.envoy_ext_authz_grpc.addr=:9191" + - "--set=plugins.envoy_ext_authz_grpc.path=envoy/authz/allow" + - "--ignore=.*" + - "/policy/policy.rego" + livenessProbe: + httpGet: + path: /health?plugins + port: 8282 + readinessProbe: + httpGet: + path: /health?plugins + port: 8282 +``` + + +> 💡 Consider specifying CPU and memory resource requests and limits for the OPA and other containers to prevent +> deployments from resource starvation. +> You can also start OPA with the [`GOMAXPROCS`](https://golang.org/pkg/runtime)environment variable to limit the number of +> cores that OPA can consume. +> +> 💡 The OPA-Envoy plugin can be configured to listen on a UNIX Domain Socket. A complete example of such a setup +> can be found [here](https://github.com/open-policy-agent/opa-envoy-plugin/tree/main/examples/envoy-uds). + +### Load Generator And Measurement Tool + +Consider using a load generator and measurement tool that measures latency from the end user’s perspective and reports +latency as the percentiles of a distribution, e.g. `p50` (median), `p99`, `p999` etc. As example +implementation of such a tool can be found [here](https://github.com/ashutosh-narkar/stress-opa-envoy). + +### Benchmark Scenarios + +Following are some scenarios to perform benchmarks on. The results could be used to compare OPA-Envoy plugin's +latency and resource consumption with the baseline (no-opa) case for instance. + +- **App Only** + +In this case, requests are sent directly to the application ie. no Envoy and OPA in the request path. + +- **App and Envoy** + +In this case, OPA is not included in the request path but Envoy is (ie. [Envoy External Authorization API](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ext_authz_filter.html) disabled). + +- **App, Envoy and OPA (NOP policy)** + +In this case, performance measurements are observed with [Envoy External Authorization API](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ext_authz_filter.html) enabled. This means +Envoy will make a call to OPA on every incoming request with the below NOP policy loaded into OPA. + +```rego +package envoy.authz + +default allow := true +``` + +- **App, Envoy and OPA (RBAC policy)** + +In this case, performance measurements are observed with [Envoy External Authorization API](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ext_authz_filter.html) enabled and +a sample real-world RBAC policy as shown below loaded into OPA. + +```rego +package envoy.authz + +import input.attributes.request.http as http_request + +default allow := false + +allow { + roles_for_user[r] + required_roles[r] +} + +roles_for_user[r] { + r := user_roles[user_name][_] +} + +required_roles[r] { + perm := role_perms[r][_] + perm.method == http_request.method + perm.path == http_request.path +} + +user_name := parsed { + [_, encoded] := split(http_request.headers.authorization, " ") + [parsed, _] := split(base64url.decode(encoded), ":") +} + +user_roles := { + "alice": ["guest"], + "bob": ["admin"] +} + +role_perms := { + "guest": [ + {"method": "GET", "path": "/people"}, + ], + "admin": [ + {"method": "GET", "path": "/people"}, + {"method": "POST", "path": "/people"}, + ], +} +``` + +- **App, Envoy and OPA (Header Injection policy)** + +This scenario is similar to the previous one expect the policy decision is an object which contains optional +response headers. An example of such a policy can be found [here](../envoy/primer#example-policy-with-object-response). + +### Measurements + +This section describes some metrics that should help to measure the cost of the OPA-Envoy plugin in terms of +CPU and memory consumed as well as latency added. + +- `End-to-end Latency` is the latency measured from the end user’s perspective. This includes time spent on the network, + in the application, in OPA and so on. The sample [load tester tool](https://github.com/ashutosh-narkar/stress-opa-envoy) + shows how to measure this metric. + +- `OPA Evaluation` is the time taken to evaluate the policy. + +- `gRPC Server Handler` is the total time taken to prepare the input for the policy, evaluate the policy (`OPA Evaluation`) + and prepare the result. Basically this is time spent by the OPA-Envoy plugin to process the request. OPA's [metrics](https://pkg.go.dev/github.com/open-policy-agent/opa/metrics) + package provides helpers to measure both `gRPC Server Handler` and `OPA Evaluation` time. + +- `Resource utilization` refers to the CPU and memory usage of the OPA-Envoy container. `kubectl top` utility can be + leveraged to measure this. + +### Features + +The sample OPA-Envoy deployment described [previously](#opa-envoy-plugin), does not utilize OPA's [decision logs](https://www.openpolicyagent.org/docs/latest/management-decision-logs/) +management API that enables periodic reporting of decision logs to remote HTTP servers or local console. Decision logging +can be enabled by updating the OPA-Envoy configuration, and the guidance provided on this page can be used to +gather benchmark results. diff --git a/third_party/opa/docs/docs/envoy/primer.md b/third_party/opa/docs/docs/envoy/primer.md new file mode 100644 index 000000000000..2f93cee8de0f --- /dev/null +++ b/third_party/opa/docs/docs/envoy/primer.md @@ -0,0 +1,532 @@ +--- +title: Policy Primer via Examples +sidebar_position: 1 +--- + +This page covers how to write policies for the content of the requests that are passed to OPA by Envoy's +[External Authorization filter](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ext_authz_filter.html). + +## Writing Policies + +Let's start with an example policy that restricts access to an endpoint based on a user's role and permissions. + +```rego +package envoy.authz + +import input.attributes.request.http + +default allow := false + +allow if { + is_token_valid + action_allowed +} + +is_token_valid if { + token.valid + now := time.now_ns() / 1000000000 + token.payload.nbf <= now + now < token.payload.exp +} + +action_allowed if { + http.method == "GET" + token.payload.role == "guest" + glob.match("/people/*", ["/"], http.path) +} + +action_allowed if { + http.method == "GET" + token.payload.role == "admin" + glob.match("/people/*", ["/"], http.path) +} + +action_allowed if { + http.method == "POST" + token.payload.role == "admin" + glob.match("/people", ["/"], http.path) + lower(input.parsed_body.firstname) != base64url.decode(token.payload.sub) +} + +token := {"valid": valid, "payload": payload} if { + [_, encoded] := split(http.headers.authorization, " ") + [valid, _, payload] := io.jwt.decode_verify(encoded, {"secret": "secret"}) +} +``` + + + +The first line `package envoy.authz` declaration gives the (hierarchical) name `envoy.authz` to the rules in the +remainder of the policy. If the OPA-Envoy [configuration](../#configuration) does not specify the `path` +field, `envoy/authz/allow` will be considered as the default policy decision path. `data.envoy.authz.allow` will be the +name of the policy decision to query in the default case. + +The above policy uses the `io.jwt.decode_verify` builtin function to parse and verify the JWT containing +information about the user making the request. It uses other builtins like `glob.match`, `lower`, `base64url.decode` etc. +OPA has 150+ builtins detailed in the [policy reference](../policy-reference). + +The dot notation seen in multiple places in the policy for ex. `input.parsed_body.firstname` simply descends through +the hierarchy to access the requested value. The dot (.) operator never throws any errors; if the path does not exist +the value of the expression is `undefined`. + +```rego +package example + +result := data.envoy.authz.allow +``` + + + +Sample input received by the policy is shown below: + +```json +{ + "attributes": { + "request": { + "http": { + "method": "GET", + "path": "/people/", + "headers": { + "authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiZ3Vlc3QiLCJzdWIiOiJZV3hwWTJVPSIsIm5iZiI6MTUxNDg1MTEzOSwiZXhwIjoyMDYyODQwNDY0fQ.1KwYoZe7DdhAAQ1H2J5pI9HiSKbHATIQlDTYvT-e29M" + } + } + } + } +} +``` + + + +## Example Policy with Additional Controls + +The `allow` variable in the above policy returns a `boolean` decision to indicate whether a request should be allowed or not. +If you want, you can also control the HTTP status sent to the upstream or downstream client, along with the response body, and the response headers. Response metadata can also be set for consumption by the next Envoy filter. To do that, you can write rules like the ones below to fill in values for variables with the following types: + +- `headers` is an object whose keys are strings and values are strings. In case the request is denied, the object represents the HTTP response headers to be sent to the downstream client. If the request is allowed, the object represents additional request headers to be sent to the upstream. +- `response_headers_to_add` is an object whose keys are strings and values are strings. It defines the HTTP response headers to be sent to the downstream client when a request is allowed. +- `request_headers_to_remove` is an array of strings which describes the HTTP headers to remove from the original request before dispatching it to the upstream when a request is allowed. +- `body` is a string which represents the response body data sent to the downstream client when a request is denied. +- `status_code` is a number which represents the HTTP response status code sent to the downstream client when a request is denied. +- `dynamic_metadata` is an object whose keys are strings and values can be booleans, strings, numbers, arrays, or objects. It will set the `DynamicMetadata` in the `CheckResponse` returned by the `opa-envoy-plugin` and can be consumed elsewhere in the envoy filter chain. +- `query_parameters_to_set` is an object whose keys are strings and values can be strings or arrays of strings. It defines the query parameters to be added or modified in the request before dispatching it to the upstream when a request is allowed. When a value is an array, it represents multiple values for the same parameter key. + +```rego +package envoy.authz + +import input.attributes.request.http + +default allow := false + +allow if { + is_token_valid + action_allowed +} + +headers["x-ext-auth-allow"] := "yes" +headers["x-validated-by"] := "security-checkpoint" + +request_headers_to_remove := ["one-auth-header", "another-auth-header"] + +response_headers_to_add["x-foo"] := "bar" + +query_parameters_to_set = { + "user-role": token.payload.role, + "tags": ["main-flow", "auth-enabled"] +} + +status_code := 200 if { + allow +} else := 401 if { + not is_token_valid +} else := 403 + +body := "Authentication Failed" if status_code == 401 +body := "Unauthorized Request" if status_code == 403 + +dynamic_metadata := {"foo": "bar"} + +is_token_valid if { + token.valid + now := time.now_ns() / 1000000000 + token.payload.nbf <= now + now < token.payload.exp +} + +action_allowed if { + http.method == "GET" + token.payload.role == "guest" + glob.match("/people/*", ["/"], http.path) +} + +action_allowed if { + http.method == "GET" + token.payload.role == "admin" + glob.match("/people/*", ["/"], http.path) +} + +action_allowed if { + http.method == "POST" + token.payload.role == "admin" + glob.match("/people", ["/"], http.path) + lower(input.parsed_body.firstname) != base64url.decode(token.payload.sub) +} + +token := {"valid": valid, "payload": payload} if { + [_, encoded] := split(http.headers.authorization, " ") + [valid, _, payload] := io.jwt.decode_verify(encoded, {"secret": "secret"}) +} +``` + + + +Sample input received by the policy is shown below: + +```json +{ + "attributes": { + "request": { + "http": { + "method": "GET", + "path": "/people", + "headers": { + "authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiZ3Vlc3QiLCJzdWIiOiJZV3hwWTJVPSIsIm5iZiI6MTUxNDg1MTEzOSwiZXhwIjoyMDYyODQwNjEwfQ.vIX2U9Mp9L5c6DS4JkqtSUrUkhxrI9zMFOr0Xs1cWPo" + } + } + } + } +} +``` + + + +## Output Document + +When Envoy receives a policy decision, it expects a JSON object with the following fields: + +- `allowed` (required): a boolean deciding whether or not the request is allowed +- `headers` (optional): an object mapping a string header name to a string header value (e.g. key "x-ext-auth-allow" has value "yes") +- `response_headers_to_add` (optional): an object mapping a string header name to a string header value +- `request_headers_to_remove` (optional): is an array of string header names +- `http_status` (optional): a number representing the HTTP status code +- `body` (optional): the response body +- `dynamic_metadata` (optional): an object representing dynamic metadata to be consumed by the next Envoy filter. +- `query_parameters_to_remove` (optional): is an array containing the names of string query parameters to be removed. +- `query_parameters_to_set` (optional): an object mapping parameter names to values (string) or arrays of values (for multiple values with the same key) + +To construct that output object using the policies demonstrated in the last section, you can use the following Rego snippet. Notice that we are using partial object rules so that any variables with undefined values simply have no key in the `result` object. + +```rego +result["allowed"] := allow +result["headers"] := headers +result["response_headers_to_add"] := response_headers_to_add +result["request_headers_to_remove"] := request_headers_to_remove +result["body"] := body +result["http_status"] := status_code +result["dynamic_metadata"] := dynamic_metadata +result["query_parameters_to_remove"] := query_parameters_to_remove +result["query_parameters_to_set"] = query_parameters_to_set +``` + +For a single user, including this snippet in your normal policy is fine, but when you have multiple teams writing policies, you will typically pull this bit of boilerplate into a wrapper package, so your teams can focus on writing the policies shown in the previous sections. + +## Input Document + +In OPA, `input` is a reserved, global variable whose value is the request sent by the Envoy External Authorization filter +to OPA. The OPA-Envoy plugin supports both [v2](https://github.com/envoyproxy/data-plane-api/blob/main/envoy/service/auth/v2/external_auth.proto) +and [v3](https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto#service-auth-v3-checkrequest) +versions of the `CheckRequest` which is used to pass the request to OPA. + +For v3 requests, the [specified JSON mapping for protobuf](https://developers.google.com/protocol-buffers/docs/proto3#json) +is used for making the incoming `envoy.service.auth.v3.CheckRequest` available in `input`. +It differs from the encoding used for v2 requests. In v3, all keys are lower camelcase. +Also, needless nesting of `oneof` values is removed. + +For example, source address data that looks like this in v2, + +```json +"source": { + "address": { + "Address": { + "SocketAddress": { + "PortSpecifier": { + "PortValue": 59052 + }, + "address": "127.0.0.1" + } + } + } +} +``` + +Becomes, this in v3, + +```json +"source": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 59052 + } + } +} +``` + +The following table shows the rego code for common data, in v2 and v3: + +| information | rego v2 | rego v3 | +| --------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------- | +| `source address` | `input.attributes.source.address.Address.SocketAddress.address` | `input.attributes.source.address.socketAddress.address` | +| `source port` | `input.attributes.source.address.Address.SocketAddress.PortSpecifier.PortValue` | `input.attributes.source.address.socketAddress.portValue` | +| `destination address` | `input.attributes.destination.address.Address.SocketAddress.address` | `input.attributes.destination.address.socketAddress.address` | +| `destination port` | `input.attributes.destination.address.Address.SocketAddress.PortSpecifier.PortValue` | `input.attributes.destination.address.socketAddress.portValue` | +| `dynamic metadata` | `input.attributes.metadata_context.filter_metadata` | `input.attributes.metadataContext.filterMetadata` | + +Due to those differences, it's important to know which version is used when writing policies. +Thus, this information is passed into the OPA evaluation under `input.version`, where you'll either +find, for v2, + +```rego +input.version == { "ext_authz": "v2", "encoding": "encoding/json" } +``` + +or, for v3, + +```rego +input.version == { "ext_authz": "v3", "encoding": "protojson" } +``` + +To have Envoy use the v3 version of the service, the `http_filters` entry in the Envoy configuration should look +like below (minimal version): + +```yaml +http_filters: +- name: envoy.ext_authz + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz + transport_api_version: V3 + grpc_service: + google_grpc: # or envoy_grpc + target_uri: "127.0.0.1:9191" +``` + +### Example Input + +
+ Example v3 Input + +```json +{ + "attributes": { + "source": { + "address": { + "socketAddress": { + "address": "172.17.0.1", + "portValue": 61402 + } + } + }, + "destination": { + "address": { + "socketAddress": { + "address": "172.17.06", + "portValue": 8000 + } + } + }, + "request": { + "time": "2020-11-20T09:47:47.722473Z", + "http": { + "id": "13519049518330544501", + "method": "POST", + "headers": { + ":authority": "192.168.99.206:30164", + ":method": "POST", + ":path": "/people?lang=en", + "accept": "*/*", + "authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJzdWIiOiJZbTlpIiwibmJmIjoxNTE0ODUxMTM5LCJleHAiOjE2NDEwODE1Mzl9.WCxNAveAVAdRCmkpIObOTaSd0AJRECY2Ch2Qdic3kU8", + "content-length": "41", + "content-type": "application/json", + "user-agent": "curl/7.54.0", + "x-forwarded-proto": "http", + "x-request-id": "7bca5c86-bf55-432c-b212-8c0f1dc999ec" + }, + "host": "192.168.99.206:30164", + "path": "/people?lang=en", + "protocol": "HTTP/1.1", + "body": "{\"firstname\":\"Charlie\", \"lastname\":\"Opa\"}", + "size": 41 + } + }, + "metadataContext": {} + }, + "parsed_body": { "firstname": "Charlie", "lastname": "Opa" }, + "parsed_path": ["people"], + "parsed_query": { "lang": ["en"] }, + "truncated_body": false, + "version": { + "encoding": "protojson", + "ext_authz": "v3" + } +} +``` + +
+ +
+ Example v2 Input + +```json +{ + "attributes": { + "source": { + "address": { + "Address": { + "SocketAddress": { + "PortSpecifier": { + "PortValue": 61402 + }, + "address": "172.17.0.1" + } + } + } + }, + "destination": { + "address": { + "Address": { + "SocketAddress": { + "PortSpecifier": { + "PortValue": 8000 + }, + "address": "172.17.0.6" + } + } + } + }, + "request": { + "http": { + "id": "13519049518330544501", + "method": "POST", + "headers": { + ":authority": "192.168.99.206:30164", + ":method": "POST", + ":path": "/people?lang=en", + "accept": "*/*", + "authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJzdWIiOiJZbTlpIiwibmJmIjoxNTE0ODUxMTM5LCJleHAiOjE2NDEwODE1Mzl9.WCxNAveAVAdRCmkpIObOTaSd0AJRECY2Ch2Qdic3kU8", + "content-length": "41", + "content-type": "application/json", + "user-agent": "curl/7.54.0", + "x-forwarded-proto": "http", + "x-request-id": "7bca5c86-bf55-432c-b212-8c0f1dc999ec" + }, + "host": "192.168.99.206:30164", + "path": "/people?lang=en", + "protocol": "HTTP/1.1", + "body": "{\"firstname\":\"Charlie\", \"lastname\":\"Opa\"}", + "size": 41 + } + } + }, + "parsed_body": { "firstname": "Charlie", "lastname": "Opa" }, + "parsed_path": ["people"], + "parsed_query": { "lang": ["en"] }, + "truncated_body": false, + "version": { + "encoding": "encoding/json", + "ext_authz": "v2" + } +} +``` + +
+ +The `parsed_path` field in the input is generated from the `path` field in the HTTP request which is included in the +Envoy External Authorization `CheckRequest` message type. This field provides the request path as a string array which +can help policy authors perform pattern matching on the HTTP request path. The below sample policy allows anyone to +access the path `/people`. + +```rego +package envoy.authz + +default allow := false + +allow if input.parsed_path == ["people"] +``` + +The `parsed_query` field in the input is also generated from the `path` field in the HTTP request. This field provides +the HTTP URL query as a map of string array. The below sample policy allows anyone to access the path +`/people?lang=en&id=1&id=2`. + +```rego +package envoy.authz + +default allow := false + +allow if { + input.parsed_path == ["people"] + input.parsed_query.lang == ["en"] + input.parsed_query.id == ["1", "2"] +} +``` + +The `parsed_body` field in the input is generated from the `body` field in the HTTP request which is included in the +Envoy External Authorization `CheckRequest` message type. This field contains the deserialized JSON request body which +can then be used in a policy as shown below. + +```rego +package envoy.authz + +default allow := false + +allow if { + input.parsed_body.firstname == "Charlie" + input.parsed_body.lastname == "Opa" +} +``` + +The `truncated_body` field in the input represents if the HTTP request body is truncated. The body is considered to be +truncated, if the value of the `Content-Length` header exceeds the size of the request body. + +If `skip-request-body-parse: true` is specified in the OPA-Envoy [configuration](../#configuration), then +the `parsed_body` and `truncated_body` fields will be omitted from the input. + +## Example with JWT payload passed from Envoy + +Envoy can be configured to pass validated JWT payload data into the `ext_authz` filter with `metadata_context_namespaces` +and `payload_in_metadata`. + +### Example Envoy Configuration + +```yaml +http_filters: +- name: envoy.filters.http.jwt_authn + typed_config: + "@type": type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication + providers: + example: + payload_in_metadata: verified_jwt + <...> +- name: envoy.ext_authz + config: + metadata_context_namespaces: + - envoy.filters.http.jwt_authn + <...> +``` + +### Example OPA Input + +This will result in something like the following dictionary being added to `input.attributes` (some common fields have +been excluded for brevity): + +```json +"metadata_context": { + "filter_metadata": { + "envoy.filters.http.jwt_authn": { + "verified_jwt": { + "email": "alice@example.com", + "exp": 1569026124, + "name": "Alice" + } + } + } +} +``` diff --git a/third_party/opa/docs/docs/envoy/tutorial-gloo-edge.md b/third_party/opa/docs/docs/envoy/tutorial-gloo-edge.md new file mode 100644 index 000000000000..338d5c1c10c5 --- /dev/null +++ b/third_party/opa/docs/docs/envoy/tutorial-gloo-edge.md @@ -0,0 +1,339 @@ +--- +title: "Tutorial: Gloo Edge" +sidebar_position: 4 +--- + +[Gloo Edge](https://docs.solo.io/gloo-edge/latest/) is an Envoy based API Gateway that provides a Kubernetes CRD to manage Envoy configuration for performing traffic management and routing. + +Gloo Edge allows creation of a [Custom External Auth Service](https://docs.solo.io/gloo-edge/master/guides/security/auth/custom_auth/) that implements the Envoy spec for an [External Authorization Server](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ext_authz_filter.html). + +The purpose of this tutorial is to show how OPA could be used with Gloo Edge to apply security policies for upstream services. + +## Prerequisites + +This tutorial requires Kubernetes 1.14 or later. To run the tutorial locally, we recommend using [minikube](https://minikube.sigs.k8s.io/docs/start/) in version v1.0+ with Kubernetes 1.14+. + +The tutorial also requires [Helm](https://helm.sh/docs/intro/install/) to install Gloo Edge on a Kubernetes cluster. + +## Steps + +### 1. Start Minikube + +```bash +minikube start +``` + +### 2. Setup and Configure Gloo Edge + +```bash +helm repo add gloo https://storage.googleapis.com/solo-public-helm +helm upgrade --install --namespace gloo-system --create-namespace gloo gloo/gloo +kubectl config set-context $(kubectl config current-context) --namespace=gloo-system +``` + +Ensure all the pods are running using `kubectl get pod` command. + +### 3. Create Virtual Service and Upstream + +[Virtual Services](https://docs.solo.io/gloo-edge/latest/introduction/architecture/concepts/#virtual-services) define a set of route rules, security configuration, rate limiting, transformations, and other core routing capabilities supported by Gloo Edge. + +[Upstreams](https://docs.solo.io/gloo-edge/latest/introduction/architecture/concepts/#upstreams) define destinations for routes. + +Save the configuration as **vs.yaml**. + +```yaml +apiVersion: gloo.solo.io/v1 +kind: Upstream +metadata: + name: httpbin +spec: + static: + hosts: + - addr: httpbin.org + port: 80 +--- +apiVersion: gateway.solo.io/v1 +kind: VirtualService +metadata: + name: httpbin +spec: + virtualHost: + domains: + - "*" + routes: + - matchers: + - prefix: / + routeAction: + single: + upstream: + name: httpbin + namespace: gloo-system + options: + autoHostRewrite: true +``` + +```bash +kubectl apply -f vs.yaml +``` + +### 4. Test Gloo + +For simplification port-forwarding will be used. Open another terminal and execute. + +```bash +kubectl port-forward deployment/gateway-proxy 8080:8080 +``` + +The `VirtualService` created in the previous step forwards requests to http://httpbin.org + +Let's test that Gloo works properly by running the below command in the first terminal. + +```bash +curl -XGET -Is localhost:8080/get | head -n 1 +HTTP/1.1 200 OK + +curl http -XPOST -Is localhost:8080/post | head -n1 +HTTP/1.1 200 OK +``` + +### 5. Define an OPA Policy + +The following OPA policy will work as follows: + +- Alice is granted a **guest** role and can perform `GET` requests. +- Bob is granted an **admin** role and can perform `GET` and `POST` requests. + +```rego title="policy.rego" +package envoy.authz + +import input.attributes.request.http as http_request + +default allow := false + +allow if { + is_token_valid + action_allowed +} + +is_token_valid if { + token.valid + now := time.now_ns() / 1000000000 + token.payload.nbf <= now + now < token.payload.exp +} + +action_allowed if { + http_request.method == "GET" + token.payload.role == "guest" +} + +action_allowed if { + http_request.method == "GET" + token.payload.role == "admin" +} + +action_allowed if { + http_request.method == "POST" + token.payload.role == "admin" +} + +token := {"valid": valid, "payload": payload} if { + [_, encoded] := split(http_request.headers.authorization, " ") + [valid, _, payload] := io.jwt.decode_verify(encoded, {"secret": "secret"}) +} +``` + + + +The sample input can be seen below using Alice's token, Alice should be able to `GET` but not `POST` + +```json title="input.json" +{ + "attributes": { + "request": { + "http": { + "method": "GET", + "headers": { + "authorization": "Bearer eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJleHAiOiAyMjQxMDgxNTM5LCAibmJmIjogMTUxNDg1MTEzOSwgInJvbGUiOiAiZ3Vlc3QiLCAic3ViIjogIllXeHBZMlU9In0.Uk5hgUqMuUfDLvBLnlXMD0-X53aM_Hlziqg3vhOsCc8" + } + } + } + } +} +``` + + + +Next we build an OPA bundle. + +```bash +opa build policy.rego +``` + +And now we serve the OPA bundle created above using Nginx. + +```bash +docker run --rm --name bundle-server -d -p 8888:80 -v ${PWD}:/usr/share/nginx/html:ro nginx:latest +``` + +### 6. Setup OPA-Envoy + +Create a deployment as shown below: + + +```yaml title="deployments.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: opa + labels: + app: opa +spec: + replicas: 1 + selector: + matchLabels: + app: opa + template: + metadata: + labels: + app: opa + spec: + containers: + - name: opa + image: openpolicyagent/opa:{{ current_version_docker_envoy }} + volumeMounts: + - readOnly: true + mountPath: /policy + name: opa-policy + args: + - "run" + - "--server" + - "--addr=0.0.0.0:8181" + - "--set=services.default.url=http://host.minikube.internal:8888" + - "--set=bundles.default.resource=bundle.tar.gz" + - "--set=plugins.envoy_ext_authz_grpc.addr=0.0.0.0:9191" + - "--set=plugins.envoy_ext_authz_grpc.path=envoy/authz/allow" + - "--set=decision_logs.console=true" + - "--set=status.console=true" + - "--ignore=.*" + volumes: + - name: opa-policy +``` + + +```bash +kubectl apply -f deployments.yaml +``` + +Ensure all pods are running using `kubectl get pod` command. + +Next, define a Kubernetes `service` for OPA-Envoy. This is required to create a DNS record and thereby create a Gloo `Upstream` object. + +**service.yaml** + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: opa +spec: + selector: + app: opa + ports: + - name: grpc + protocol: TCP + port: 9191 + targetPort: 9191 +``` + +**Note**: Since the name of the service port is `grpc`, `Gloo` will understand that traffic should be routed using HTTP2 protocol. + +`kubectl apply -f service.yaml` + +### 7. Configure Gloo Edge to use OPA + +To use OPA as a custom auth server, we need to add the `extauth` attribute as described below: + +**gloo.yaml** + +```yaml +global: + extensions: + extAuth: + extauthzServerRef: + name: gloo-system-opa-9191 + namespace: gloo-system +``` + +To apply it, run the following command: + +```bash +helm upgrade --install --namespace gloo-system --create-namespace -f gloo.yaml gloo gloo/gloo +``` + +Configure Gloo Edge routes to perform authorization via configured extauth before regular processing. + +**vs-patch.yaml** + +```yaml +spec: + virtualHost: + options: + extauth: + customAuth: {} +``` + +Then apply the patch to our `VirtualService` as shown below: + +```bash +kubectl patch vs httpbin --type=merge --patch "$(cat vs-patch.yaml)" +``` + +### 8. Exercise the OPA Policy + +Before we exercise the policy, for convenience sake, we will want to store Alice and Bob's tokens in environment variables as such: + +```bash +export ALICE_TOKEN="eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJleHAiOiAyMjQxMDgxNTM5LCAibmJmIjogMTUxNDg1MTEzOSwgInJvbGUiOiAiZ3Vlc3QiLCAic3ViIjogIllXeHBZMlU9In0.Uk5hgUqMuUfDLvBLnlXMD0-X53aM_Hlziqg3vhOsCc8" +export BOB_TOKEN="eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJleHAiOiAyMjQxMDgxNTM5LCAibmJmIjogMTUxNDg1MTEzOSwgInJvbGUiOiAiYWRtaW4iLCAic3ViIjogIlltOWkifQ.5qsm7rRTvqFHAgiB6evX0a_hWnGbWquZC0HImVQPQo8" +``` + +Now let's verify that OPA only allows **Alice** to perform `GET` requests. + +```bash +curl -XGET -Is -H "Authorization: Bearer $ALICE_TOKEN" localhost:8080/get +HTTP/1.1 200 OK +``` + +And with a `POST` request, we get: + +```bash +curl http -XPOST -Is -H "Authorization: Bearer $ALICE_TOKEN" localhost:8080/post +HTTP/1.1 403 Forbidden +``` + +And for **Bob**, we should be able to `GET` and `POST`: + +```bash +curl -XGET -Is -H "Authorization: Bearer $BOB_TOKEN" localhost:8080/get +HTTP/1.1 200 OK +``` + +And the `POST`: + +```bash +curl http -XPOST -Is -H "Authorization: Bearer $BOB_TOKEN" localhost:8080/post +HTTP/1.1 200 OK +``` + +Check OPA's decision logs to view the inputs received by OPA from Gloo Edge and the results generated by OPA. + +```bash +kubectl logs deployment/opa -n gloo-system +``` + +## Wrap Up + +Congratulations for finishing the tutorial! + +This tutorial showed how you can use OPA with [Gloo Edge](https://docs.solo.io/gloo-edge/latest/) to apply security policies for upstream services and how to create and test a policy that would allow `GET` or `POST` requests based on your user role. diff --git a/third_party/opa/docs/docs/envoy/tutorial-istio.md b/third_party/opa/docs/docs/envoy/tutorial-istio.md new file mode 100644 index 000000000000..11af13252241 --- /dev/null +++ b/third_party/opa/docs/docs/envoy/tutorial-istio.md @@ -0,0 +1,220 @@ +--- +title: "Tutorial: Istio" +sidebar_position: 3 +--- + +[Istio](https://istio.io/latest/) is an open source service mesh for managing the different microservices that make +up a cloud-native application. Istio provides a mechanism to use a service as an external authorizer with the +[AuthorizationPolicy API](https://istio.io/latest/docs/tasks/security/authorization/authz-custom/). + +This tutorial shows how Istio's AuthorizationPolicy can be configured to delegate authorization decisions to OPA. + +## Prerequisites + +This tutorial requires Kubernetes 1.20 or later. To run the tutorial locally ensure you start a cluster with Kubernetes +version 1.20+, we recommend using [minikube](https://kubernetes.io/docs/getting-started-guides/minikube) or +[KIND](https://kind.sigs.k8s.io/). + +The tutorial also requires Istio v1.19.0 or later. It assumes you have Istio deployed on top of Kubernetes. +See Istio's [Helm Install](https://istio.io/latest/docs/setup/install/helm/) page to get started. + +If you are using an earlier version of Istio (1.9+), you will have to customize the `AuthorizationPolicy` in the +`quick_start.yaml` file to use the `security.istio.io/v1beta1` API version instead of `security.istio.io/v1`. + +## Steps + +### 1. Install OPA-Envoy + +```bash +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/opa-envoy-plugin/main/examples/istio/quick_start.yaml +``` + +The `quick_start.yaml` manifest defines the following resources: + +- AuthorizationPolicy to direct authorization checks to the OPA-Envoy sidecar. See `kubectl -n {$NAMESPACE} get authorizationpolicy ext-authz` for details. + +- ServiceEntry to allow Istio to find the OPA-Envoy sidecars. See `kubectl -n {$NAMESPACE} get serviceentry opa-ext-authz-grpc-local` for details. + +- Kubernetes namespace (`opa-istio`) for OPA-Envoy control plane components. + +- Kubernetes admission controller in the `opa-istio` namespace that automatically injects the OPA-Envoy sidecar into pods in namespaces labelled with `opa-istio-injection=enabled`. + +- OPA configuration file and an OPA policy into ConfigMaps in the namespace where the app will be deployed, e.g., `default`. + The following is the example OPA policy: + + - alice is granted a **guest** role and can perform a `GET` request to `/productpage`. + - bob is granted an **admin** role and can perform a `GET` to `/productpage` and `/api/v1/products`. + + ```rego title="authz.rego" + package istio.authz + + default allow := false + + allow if { + input.parsed_path[0] == "health" + input.attributes.request.method == "GET" + } + + allow if { + some user_role in _user_roles[_user_name] + some permission in _role_permissions[user_role] + + permission.method == input.attributes.request.http.method + permission.path == input.attributes.request.http.path + } + + # Underscore prefix used only to signal that rules and functions are + # intended to be referenced only within the same policy, i.e. "private". + # It has no special meaning to OPA. + + _user_name := parsed if { + [_, encoded] := split(input.attributes.request.http.headers.authorization, " ") + [parsed, _] := split(base64url.decode(encoded), ":") + } + + _user_roles := { + "alice": ["guest"], + "bob": ["admin"], + } + + _role_permissions := { + "guest": [{"method": "GET", "path": "/productpage"}], + "admin": [ + {"method": "GET", "path": "/productpage"}, + {"method": "GET", "path": "/api/v1/products"}, + ], + } + ``` + + + + OPA is configured to query for the `data.istio.authz.allow` + decision. If the response is `true` the operation is allowed, otherwise the + operation is denied. Sample input received by OPA is shown below: + + ```json title="input.json" + { + "attributes": { + "request": { + "http": { + "method": "GET", + "path": "/productpage", + "headers": { + "authorization": "Basic YWxpY2U6cGFzc3dvcmQ=" + } + } + } + } + } + ``` + + + + ```rego + package example + + result := data.istio.authz.allow + ``` + + + + An example of the complete input received by OPA can be seen [here](https://github.com/open-policy-agent/opa-envoy-plugin/tree/main/examples/istio#example-input). + + > In typical deployments the policy would either be built into the OPA container + > image or it would be fetched dynamically via the [Bundle API](../management-bundles/). ConfigMaps are + > used in this tutorial for test purposes. + +### 2. Configure the mesh to define the external authorizer + +Edit the mesh configmap with `kubectl edit configmap -n istio-system istio` and define the external provider: + +```yaml +data: + mesh: |- + # Add the following lines to define the ServiceEntry previously created as an external authorizer: + extensionProviders: + - name: opa-ext-authz-grpc + envoyExtAuthzGrpc: + service: opa-ext-authz-grpc.local + port: 9191 +``` + +See [the Istio Docs for AuthorizationPolicy](https://istio.io/latest/docs/tasks/security/authorization/authz-custom/#define-the-external-authorizer) for +more details. + +The format of the service value is `[/]`. The specification +of `` is required only when it is insufficient to unambiguously resolve +a service in the service registry. See also the [configuration documentation](https://istio.io/latest/docs/reference/config/istio.mesh.v1alpha1/#MeshConfig-ExtensionProvider-EnvoyExternalAuthorizationGrpcProvider). +Example: `opa-ext-authz-grpc.foo.svc.cluster.local` or +`bar/opa-ext-authz-grpc.local`. + +### 3. Enable automatic injection of the Istio Proxy and OPA-Envoy sidecars in the namespace where the app will be deployed, e.g., `default` + +```bash +kubectl label namespace default opa-istio-injection="enabled" +kubectl label namespace default istio-injection="enabled" +``` + +### 4. Deploy the BookInfo application and make it accessible outside the cluster + +```bash +kubectl apply -f https://raw.githubusercontent.com/istio/istio/master/samples/bookinfo/platform/kube/bookinfo.yaml +``` + +```bash +kubectl apply -f https://raw.githubusercontent.com/istio/istio/master/samples/bookinfo/networking/bookinfo-gateway.yaml +``` + +### 5. Set the `SERVICE_HOST` environment variable in your shell to the public IP/port of the Istio Ingress gateway + +Run this command in a new terminal window to start a Minikube tunnel that sends traffic to your Istio Ingress Gateway: + +``` +minikube tunnel +``` + +Check that the Service shows an `EXTERNAL-IP`: + +```bash +kubectl -n istio-system get service istio-ingressgateway + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +istio-ingressgateway LoadBalancer 10.98.42.178 127.0.0.1 15021:32290/TCP,80:30283/TCP,443:32497/TCP,31400:30216/TCP,15443:30690/TCP 5s +``` + +**minikube:** + +```bash +export SERVICE_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` + +For other platforms see the [Istio documentation on determining ingress IP and ports.](https://istio.io/docs/tasks/traffic-management/ingress/#determining-the-ingress-ip-and-ports) + +### 6. Exercise the OPA policy + +Check that **alice** can access `/productpage` **BUT NOT** `/api/v1/products`. + +```bash +curl --user alice:password -i http://$SERVICE_HOST/productpage +curl --user alice:password -i http://$SERVICE_HOST/api/v1/products +``` + +Check that **bob** can access `/productpage` **AND** `/api/v1/products`. + +```bash +curl --user bob:password -i http://$SERVICE_HOST/productpage +curl --user bob:password -i http://$SERVICE_HOST/api/v1/products +``` + +## Wrap Up + +Congratulations for finishing the tutorial ! + +This tutorial showed how Istio's [AuthorizationPolicy API](https://istio.io/latest/docs/tasks/security/authorization/authz-custom/) +can be configured to use OPA as an External authorization service. + +This tutorial also showed a sample OPA policy that returns a `boolean` decision +to indicate whether a request should be allowed or not. + +More details about the tutorial can be seen +[here](https://github.com/open-policy-agent/opa-envoy-plugin/tree/main/examples/istio). diff --git a/third_party/opa/docs/docs/envoy/tutorial-standalone-envoy.md b/third_party/opa/docs/docs/envoy/tutorial-standalone-envoy.md new file mode 100644 index 000000000000..705abc727ba4 --- /dev/null +++ b/third_party/opa/docs/docs/envoy/tutorial-standalone-envoy.md @@ -0,0 +1,561 @@ +--- +title: "Tutorial: Standalone Envoy" +sidebar_position: 2 +--- + +The tutorial shows how Envoy’s External +[authorization filter](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ext_authz_filter.html) +can be used with OPA as an authorization service to enforce security policies over API requests +received by Envoy. The tutorial also covers examples of authoring custom +policies over the HTTP request body. + +## Overview + +In this tutorial we'll see how to use OPA as an External +Authorization service for the Envoy proxy. We'll do this by: + +- Running a local Kubernetes cluster +- Creating a simple authorization policy in Rego and serving it via the Bundle API +- Deploying a sample application with Envoy and OPA sidecars +- Run some sample requests to see the policy in action + +Note that other than the HTTP client and bundle server, all components +are co-located in the same pod. + +## Running a local Kubernetes cluster + +To start a local Kubernetes cluster to run our demo, we'll be using +[kind](https://kind.sigs.k8s.io/). + +:::info +If you haven't used `kind` before, you can find installation instructions +in the [project documentation](https://kind.sigs.k8s.io/#installation-and-usage). +::: + +Create a cluster with the following command: + +```shell +$ kind create cluster --name opa-envoy --image kindest/node:v1.27.3 +Creating cluster "opa-envoy" ... + ✓ Ensuring node image (kindest/node:v1.27.3) 🖼 + ✓ Preparing nodes 📦 + ✓ Writing configuration 📜 + ✓ Starting control-plane 🕹️ + ✓ Installing CNI 🔌 + ✓ Installing StorageClass 💾 +... +``` + +Once the cluster is created, make sure your `kubectl` context is set to connect +to the new cluster: + +```shell +$ kubectl cluster-info --context kind-opa-envoy +Kubernetes control plane is running at ... +CoreDNS is running at ... +... +``` + +Listing the cluster nodes, should show something like this: + +```shell +$ kubectl get nodes +NAME STATUS ROLES AGE VERSION +opa-envoy-control-plane Ready control-plane 2m35s v1.27.3 +``` + +## Creating & Serving our Policy Bundle + +This tutorial assumes you have some Rego knowledge, in summary the policy below does the following: + +- Checks that the JWT token is valid +- Checks that the action is allowed based on the token payload `role` and the request path +- Guests have read-only access to the `/people` endpoint, admins can create users too as long as the + name is not the same as the admin's name. + +```rego +# policy.rego +package envoy.authz + +import input.attributes.request.http as http_request + +default allow := false + +allow if { + is_token_valid + action_allowed +} + +is_token_valid if { + token.valid + now := time.now_ns() / 1000000000 + token.payload.nbf <= now + now < token.payload.exp +} + +action_allowed if { + http_request.method == "GET" + token.payload.role == "guest" + glob.match("/people", ["/"], http_request.path) +} + +action_allowed if { + http_request.method == "GET" + token.payload.role == "admin" + glob.match("/people", ["/"], http_request.path) +} + +action_allowed if { + http_request.method == "POST" + token.payload.role == "admin" + glob.match("/people", ["/"], http_request.path) + lower(input.parsed_body.firstname) != base64url.decode(token.payload.sub) +} + +token := {"valid": valid, "payload": payload} if { + [_, encoded] := split(http_request.headers.authorization, " ") + [valid, _, payload] := io.jwt.decode_verify(encoded, {"secret": "secret"}) +} +``` + +Create a file called `policy.rego` with the above content and store it in a ConfigMap: + +```shell +kubectl create configmap authz-policy --from-file policy.rego +``` + +Now that the policy is stored in a ConfigMap, we can spin up an HTTP server to make it +available as a Bundle to OPA when it's making decisions for our application: + +```yaml +# bundle-server.yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bundle-server + labels: + app: bundle-server +spec: + replicas: 1 + selector: + matchLabels: + app: bundle-server + template: + metadata: + labels: + app: bundle-server + spec: + initContainers: + - name: opa-builder + image: openpolicyagent/opa:latest + args: + - "build" + - "--bundle" + - "/opt/policy/" + - "--output" + - "/opt/output/bundle.tar.gz" + volumeMounts: + - name: index + mountPath: /opt/output/ + - name: policy + mountPath: /opt/policy/ + containers: + - name: bundle-server + image: nginx:1.25 + ports: + - containerPort: 80 + name: http + volumeMounts: + - name: index + mountPath: /usr/share/nginx/html + volumes: + - name: index + emptyDir: {} + - name: policy + configMap: + name: authz-policy +--- +apiVersion: v1 +kind: Service +metadata: + name: bundle-server +spec: + selector: + app: bundle-server + ports: + - protocol: TCP + port: 80 + targetPort: http +``` + +Create a file called `bundle-server.yaml` with the above content and apply it to the cluster: + +```shell +kubectl apply -f bundle-server.yaml +``` + +Once the deployment is running, we can check that the bundle is available by running: + +```shell +kubectl port-forward service/bundle-server 8080:80 +``` + +Before checking that the bundle has been generated correctly and is available to download: + +```shell +$ curl -I localhost:8080/bundle.tar.gz +HTTP/1.1 200 OK +... +``` + +You may now exit the port-forwarding session, the bundle server will only be accessed +from inside the cluster from now on. + +## Deploying an application with Envoy and OPA sidecars + +In this tutorial, we are manually configuring the Envoy proxy sidecar to intermediate +HTTP traffic from clients and our application. Envoy will consult OPA to +make authorization decisions for each request by sending `CheckRequest` messages over +a gRPC connection. + +We will use the following Envoy configuration to achieve this. In summary, this +configures Envoy to: + +- Listen on port `8000` for HTTP traffic +- Consult OPA for authorization decisions at 127.0.0.1:9191 & deny failing requests +- Forward requests to the application at 127.0.0.1:8080 if ok. + +```yaml +# envoy.yaml +static_resources: + listeners: + - address: + socket_address: + address: 0.0.0.0 + port_value: 8000 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + codec_type: auto + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: backend + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: service + http_filters: + - name: envoy.ext_authz + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz + transport_api_version: V3 + with_request_body: + max_request_bytes: 8192 + allow_partial_message: true + failure_mode_allow: false + grpc_service: + google_grpc: + target_uri: 127.0.0.1:9191 + stat_prefix: ext_authz + timeout: 0.5s + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: service + connect_timeout: 0.25s + type: strict_dns + lb_policy: round_robin + load_assignment: + cluster_name: service + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 8080 +admin: + access_log_path: "/dev/null" + address: + socket_address: + address: 0.0.0.0 + port_value: 8001 +layered_runtime: + layers: + - name: static_layer_0 + static_layer: + envoy: + resource_limits: + listener: + example_listener_name: + connection_limit: 10000 + overload: + global_downstream_max_connections: 50000 +``` + +Create a `ConfigMap` containing the above configuration by running: + +```shell +kubectl create configmap proxy-config --from-file envoy.yaml +``` + +Our application will be configured using a `Deployment` and `Service`. +There are a few things to note: + +- the pods have an `initContainer` that configures the `iptables` rules to + redirect traffic to the Envoy proxy. +- the `demo-test-server` container is a simple user store using in-memory state. +- the `envoy` container is configured to use the `proxy-config` `ConfigMap` we + created earlier. +- The OPA container is configured to download policy bundles from + the in-cluster bundle server (`bundle-server.default.svc.cluster.local`). + +```yaml +# app.yaml +kind: Deployment +apiVersion: apps/v1 +metadata: + name: example-app + labels: + app: example-app +spec: + replicas: 1 + selector: + matchLabels: + app: example-app + template: + metadata: + labels: + app: example-app + spec: + initContainers: + - name: proxy-init + image: openpolicyagent/proxy_init:v8 + # Configure the iptables bootstrap script to redirect traffic to the + # Envoy proxy on port 8000. Envoy will be running as 1111, and port + # 8282 will be excluded to support OPA health checks. + args: ["-p", "8000", "-u", "1111", "-w", "8282"] + securityContext: + capabilities: + add: + - NET_ADMIN + runAsNonRoot: false + runAsUser: 0 + containers: + - name: app + image: openpolicyagent/demo-test-server:v1 + ports: + - containerPort: 8080 + - name: envoy + image: envoyproxy/envoy:v1.26.3 + volumeMounts: + - readOnly: true + mountPath: /config + name: proxy-config + args: + - "envoy" + - "--config-path" + - "/config/envoy.yaml" + env: + - name: ENVOY_UID + value: "1111" + - name: opa + image: openpolicyagent/opa:latest-envoy + args: + - "run" + - "--server" + - "--addr=localhost:8181" + - "--diagnostic-addr=0.0.0.0:8282" + - "--set=services.default.url=http://bundle-server" + - "--set=bundles.default.resource=bundle.tar.gz" + - "--set=plugins.envoy_ext_authz_grpc.addr=:9191" + - "--set=plugins.envoy_ext_authz_grpc.path=envoy/authz/allow" + - "--set=decision_logs.console=true" + - "--set=status.console=true" + - "--ignore=.*" + livenessProbe: + httpGet: + path: /health?plugins + scheme: HTTP + port: 8282 + initialDelaySeconds: 5 + periodSeconds: 5 + readinessProbe: + httpGet: + path: /health?plugins + scheme: HTTP + port: 8282 + initialDelaySeconds: 1 + periodSeconds: 3 + volumes: + - name: proxy-config + configMap: + name: proxy-config +--- +apiVersion: v1 +kind: Service +metadata: + name: example-app +spec: + selector: + app: example-app + ports: + - protocol: TCP + port: 80 + targetPort: 8080 +``` + +Deploy the application and Kubernetes Service to the cluster with: + +```shell +kubectl apply -f app.yaml +``` + +Check that everything is working by listing the pod (make sure that +all three pods are running ok). + +```shell +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +bundle-server-5d7bfffdb6-bgn86 1/1 Running 0 1m +example-app-74b4bc88-5d4wh 3/3 Running 0 1m +``` + +## See the Policy in Action + +Run a shell inside the cluster to use for testing. We will use this in-cluster +shell for the rest of the tutorial. + +```shell +kubectl run curl --restart=Never -it --rm --image curlimages/curl:8.1.2 -- sh +``` + +Set two tokens for two users, Alice and Bob with different permissions. +As defined by our policy: + +```shell +export ALICE_TOKEN="eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJleHAiOiAyMjQxMDgxNTM5LCAibmJmIjogMTUxNDg1MTEzOSwgInJvbGUiOiAiZ3Vlc3QiLCAic3ViIjogIllXeHBZMlU9In0.Uk5hgUqMuUfDLvBLnlXMD0-X53aM_Hlziqg3vhOsCc8" +export BOB_TOKEN="eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJleHAiOiAyMjQxMDgxNTM5LCAibmJmIjogMTUxNDg1MTEzOSwgInJvbGUiOiAiYWRtaW4iLCAic3ViIjogIlltOWkifQ.5qsm7rRTvqFHAgiB6evX0a_hWnGbWquZC0HImVQPQo8" +``` + +### Listing People + +Send a request to list people. This should succeed for both Alice and Bob. + +```shell +curl -i -H "Authorization: Bearer $ALICE_TOKEN" http://example-app/people +``` + +``` +HTTP/1.1 200 OK +content-type: application/json +date: Tue, 18 Jul 2023 15:22:25 GMT +content-length: 96 +x-envoy-upstream-service-time: 14 +server: envoy + +[{"id":"1","firstname":"John","lastname":"Doe"},{"id":"2","firstname":"Jane","lastname":"Doe"}] +``` + +And for Bob: + +```shell +curl -i -H "Authorization: Bearer $BOB_TOKEN" http://example-app/people +``` + +``` +HTTP/1.1 200 OK +...omitted... +``` + +### Creating People + +Send a request to create a new user. This should fail for Alice but not Bob: + +```shell +curl -i -H "Authorization: Bearer $ALICE_TOKEN" \ + -d '{"firstname":"Foo", "lastname":"Bar"}' -H "Content-Type: application/json" \ + -X POST http://example-app/people +``` + +``` +HTTP/1.1 403 Forbidden +date: Tue, 18 Jul 2023 15:25:28 GMT +server: envoy +content-length: 0 +``` + +And for Bob, the request is permitted and the user is saved with an ID + +```shell +curl -i -H "Authorization: Bearer $BOB_TOKEN" \ + -d '{"firstname":"Foo", "lastname":"Bar"}' -H "Content-Type: application/json" \ + -X POST http://example-app/people +``` + +``` +HTTP/1.1 200 OK +content-type: application/json +date: Tue, 18 Jul 2023 15:28:20 GMT +content-length: 51 +x-envoy-upstream-service-time: 11 +server: envoy + +{"id":"498081","firstname":"Foo","lastname":"Bar"} +``` + +### Creating People: Conflict + +Our policy also blocks users from creating users with the same name, test that +functionality with this request: + +```shell +curl -i -H "Authorization: Bearer $BOB_TOKEN" \ + -d '{"firstname":"Bob", "lastname":"Bar"}' -H "Content-Type: application/json" \ + -X POST http://example-app/people +``` + +``` +HTTP/1.1 403 Forbidden +date: Tue, 18 Jul 2023 15:31:48 GMT +server: envoy +content-length: 0 +``` + +## Shutting Down + +Exit the in-cluster shell by typing `exit`. + +Delete the cluster by running: + +```shell +$ kind delete cluster --name opa-envoy +Deleting cluster "opa-envoy" ... +Deleted nodes: ["opa-envoy-control-plane"] +``` + +## Wrap Up + +Congratulations on finishing the tutorial ! + +This tutorial showed how to use OPA as an External authorization service to +enforce custom policies by leveraging Envoy’s External authorization filter. + +This tutorial also showed a sample OPA policy that returns a `boolean` decision +to indicate whether a request should be allowed or not. + +Envoy's external authorization filter allows optional response headers and body +to be sent to the downstream client or upstream. An example of a rule that +returns an object that not only indicates if a request is allowed or not but +also provides optional response headers, body and HTTP status that can be sent +to the downstream client or upstream can be seen +[here](https://github.com/open-policy-agent/opa-envoy-plugin#example-policy-with-object-response). diff --git a/third_party/opa/docs/docs/extensions.md b/third_party/opa/docs/docs/extensions.md new file mode 100644 index 000000000000..b79b2634ce46 --- /dev/null +++ b/third_party/opa/docs/docs/extensions.md @@ -0,0 +1,538 @@ +--- +title: Extending OPA +sidebar_position: 8 +--- + +OPA can be extended with custom built-in functions and plugins that +implement functionality like support for new protocols. This page explains how +to customize and extend OPA in different ways. + +## Custom Built-in Functions in Go + +Read this section if you want to extend OPA with custom built-in functions. + +:::info +This section assumes you are embedding OPA as a library and executing policies +via the `github.com/open-policy-agent/opa/rego` package. If you are NOT embedding OPA +as a library and instead want to customize the OPA runtime, read this section +anyway because it provides useful information on implementing built-in functions. +For a complete example that shows how to add custom built-in functions to the +OPA runtime, see the [Adding Built-in Functions to the OPA Runtime](#adding-built-in-functions-to-the-opa-runtime) appendix. +::: + +OPA supports built-in functions for simple operations like string manipulation +and arithmetic as well as more complex operations like JWT verification and +executing HTTP requests. If you need to to extend OPA with custom built-in +functions for use cases or integrations that are not supported out-of-the-box +you can supply the function definitions when you prepare queries. + +Using custom built-in functions involves providing a declaration and +implementation. The declaration tells OPA the function's type signature and the +implementation provides the callback that OPA can execute during query +evaluation. + +To get started you need to import three packages: + +``` +import "github.com/open-policy-agent/opa/ast" +import "github.com/open-policy-agent/opa/types" +import "github.com/open-policy-agent/opa/rego" +``` + +The `ast` and `types` packages contain the types for declarations and runtime +objects passed to your implementation. Here is a trivial example that shows the +process: + +```golang +r := rego.New( + rego.Query(`x = hello("bob")`), + rego.Function1( + ®o.Function{ + Name: "hello", + Decl: types.NewFunction(types.Args(types.S), types.S), + }, + func(_ rego.BuiltinContext, a *ast.Term) (*ast.Term, error) { + if str, ok := a.Value.(ast.String); ok { + return ast.StringTerm("hello, " + string(str)), nil + } + return nil, nil + }), +) + +query, err := r.PrepareForEval(ctx) +if err != nil { + // handle error. +} +``` + +At this point you can execute the `query`: + +```golang +rs, err := query.Eval(ctx) +if err != nil { + // handle error. +} + +// Do something with result. +fmt.Println(rs[0].Bindings["x"]) +``` + +If you executed this code you the output would be: + +```rego +"hello, bob" +``` + +The example above highlights a few important points. + +- The `rego` package includes variants of `rego.Function1` for accepting + different numbers of operands (e.g., `rego.Function2`, `rego.Function3`, etc.) +- The `rego.Function#Name` struct field specifies the operator that queries can + refer to. +- The `rego.Function#Decl` struct field specifies the function's type signature. + In the example above the function accepts a string and returns a string. +- The function indicates it's undefined by returning `nil` for the first return + argument. + +Let's look at another example. Imagine you want to expose GitHub repository +metadata to your policies. One option is to implement a custom built-in +function to fetch the data for specific repositories on-the-fly. + +```golang +r := rego.New( + rego.Query(`github.repo("open-policy-agent", "opa")`), + rego.Function2( + ®o.Function{ + Name: "github.repo", + Decl: types.NewFunction(types.Args(types.S, types.S), types.A), + Memoize: true, + Nondeterministic: true, + }, + func(bctx rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error) { + // see implementation below. + }, + ), +) +``` + +Built-in function names can include `.` characters. Consider namespacing your +built-in functions to avoid collisions. This declaration indicates the function +accepts two strings and returns a value of type `any`. The `any` type is the +union of all types in Rego. + +:::info +`types.S` and `types.A` are shortcuts for constructing Rego types. If you need +to define use-case specific types (e.g., a list of objects that have fields +`foo`, `bar`, and `baz`, you will need to construct them using the `types` +packages APIs.) +::: + +The declaration also sets `rego.Function#Memoize` to true to enable memoization +across multiple calls in the same query. If your built-in function performs I/O, +you should enable memoization as it ensures function evaluation is +deterministic. + +Since this built-in could have non-deterministic results, depending on network +conditions, the declaration also sets `rego.Function#Nondeterministic` to true. +This provides basic safety information to the runtime, so that the function +isn't accidentally run during bundle builds or partial evaluation. If your +builtin can have non-deterministic results, you should mark it appropriately +to avoid surprises. + +The implementation wraps the Go standard library to perform HTTP requests to +GitHub's API: + +```golang +func(bctx rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error) { + var org, repo string + + if err := ast.As(a.Value, &org); err != nil { + return nil, err + } else if err := ast.As(b.Value, &repo); err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", fmt.Sprintf("https://api.github.com/repos/%v/%v", org, repo), nil) + if err != nil { + return nil, err + } + + resp, err := http.DefaultClient.Do(req.WithContext(bctx.Context)) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf(resp.Status) + } + + v, err := ast.ValueFromReader(resp.Body) + if err != nil { + return nil, err + } + + return ast.NewTerm(v), nil +} +``` + +The implementation is careful to use the context passed to the built-in function +when executing the HTTP request. See the appendix at the end of this page for +the complete example. + +:::danger +Custom built-in functions **must not** be used for effecting changes in +external systems as OPA does not guarantee that the statement will be executed due +to automatic performance optimizations that are applied during policy evaluation. +::: + +## Custom Plugins for OPA Runtime + +Read this section if you want to customize or extend the OPA runtime/executable +with custom behaviour. + +OPA defines a plugin interface that allows you to customize certain behaviour +like decision logging or add new behaviour like different query APIs. To +implement a custom plugin you must implement two interfaces: + +- [`Factory`](https://pkg.go.dev/github.com/open-policy-agent/opa/plugins#Factory) + to instantiate your plugin. +- [`Plugin`](https://pkg.go.dev/github.com/open-policy-agent/opa/plugins#Plugin) + to provide your plugin behavior. + +You can register your factory with OPA by calling +[`RegisterPlugin`](https://pkg.go.dev/github.com/open-policy-agent/opa/runtime#RegisterPlugin) +inside your main function. + +### Plugin Status + +The plugin may (optionally) report its current status to the plugin Manager via the `plugins.Manager#UpdatePluginStatus` +API. + +:::info +If no status is provided the plugin is assumed to be working OK. +::: + +Typically the plugin should report `StatusNotReady` at creation time and update to `StatusOK` (or `StatusErr`) when +appropriate. + +### Putting It Together + +The example below shows how you can implement a custom [Decision Logger](./management-decision-logs) +that writes events to a stream (e.g., stdout/stderr). + +```golang +import ( + "encoding/json" + + "github.com/open-policy-agent/opa/plugins/logs" +) + +const PluginName = "println_decision_logger" + +type Config struct { + Stderr bool `json:"stderr"` // false => stdout, true => stderr +} + +type PrintlnLogger struct { + manager *plugins.Manager + mtx sync.Mutex + config Config +} + +func (p *PrintlnLogger) Start(ctx context.Context) error { + p.manager.UpdatePluginStatus(PluginName, &plugins.Status{State: plugins.StateOK}) + return nil +} + +func (p *PrintlnLogger) Stop(ctx context.Context) { + p.manager.UpdatePluginStatus(PluginName, &plugins.Status{State: plugins.StateNotReady}) +} + +func (p *PrintlnLogger) Reconfigure(ctx context.Context, config interface{}) { + p.mtx.Lock() + defer p.mtx.Unlock() + p.config = config.(Config) +} + + +// Log is called by the decision logger when a record (event) should be emitted. The logs.EventV1 fields +// map 1:1 to those described in https://www.openpolicyagent.org/docs/latest/management-decision-logs +func (p *PrintlnLogger) Log(ctx context.Context, event logs.EventV1) error { + p.mtx.Lock() + defer p.mtx.Unlock() + w := os.Stdout + if p.config.Stderr { + w = os.Stderr + } + bs, err := json.Marshal(event) + if err != nil { + p.manager.UpdatePluginStatus(PluginName, &plugins.Status{State: plugins.StateErr}) + return nil + } + _, err = fmt.Fprintln(w, string(bs)) + if err != nil { + p.manager.UpdatePluginStatus(PluginName, &plugins.Status{State: plugins.StateErr}) + } + return nil +} +``` + +Next, implement a factory function that instantiates your plugin: + +```golang +import ( + "github.com/open-policy-agent/opa/plugins" + "github.com/open-policy-agent/opa/util" +) + +type Factory struct{} + +func (Factory) New(m *plugins.Manager, config interface{}) plugins.Plugin { + + m.UpdatePluginStatus(PluginName, &plugins.Status{State: plugins.StateNotReady}) + + return &PrintlnLogger{ + manager: m, + config: config.(Config), + } +} + +func (Factory) Validate(_ *plugins.Manager, config []byte) (interface{}, error) { + parsedConfig := Config{} + return parsedConfig, util.Unmarshal(config, &parsedConfig) +} +``` + +Finally, register your factory with OPA and call `cmd.RootCommand.Execute`. The +latter starts OPA and does not return. + +```golang +import ( + "github.com/open-policy-agent/opa/cmd" + "github.com/open-policy-agent/opa/runtime" +) + +func main() { + runtime.RegisterPlugin(PluginName, Factory{}) + + if err := cmd.RootCommand.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} +``` + +At this point you can build an OPA executable including your plugin. + +``` +go build -o opa++ +``` + +Define an OPA configuration file that will use your plugin: + +**opa-config.yaml**: + +```yaml +decision_logs: + plugin: println_decision_logger +plugins: + println_decision_logger: + stderr: false +``` + +Start OPA with the configuration file: + +```bash +./opa++ run --server --config-file opa-config.yaml +``` + +Exercise the plugin via the OPA API: + +``` +curl localhost:8181/v1/data +``` + +If everything worked you will see the Go struct representation of the decision +log event written to stdout. + +The source code for this example can be found +[here](https://github.com/open-policy-agent/contrib/tree/main/decision_logger_plugin_example). + +:::info +If there is a mask policy set (see [Decision Logger](./management-decision-logs) +for details) the `Event` received by the demo plugin will potentially be different +than the example documented. +::: + +## Setting the OPA Runtime Version + +The OPA runtime version is set statically at build-time. The following global variables +are exported by the `github.com/open-policy-agent/opa/version` package and can be +set at build-time: + +| Name | Description | +| ----------- | ------------------------------------------------------- | +| `Version` | Human-readable/semantic version of the OPA runtime. | +| `Vcs` | Git SHA that the OPA runtime was built from. | +| `Timestamp` | Date/time when the OPA runtime was built. | +| `Hostname` | Hostname of the system where the OPA runtime was built. | + +These values can be set on the command-line when building OPA from source: + +``` +go build \ + -ldflags=" \ + -X github.com/open-policy-agent/opa/v1/version.Version=MY_VERSION\ + -X github.com/open-policy-agent/opa/v1/version.Vcs=MY_COMMIT_HASH \ + -X github.com/open-policy-agent/opa/v1/version.Hostname=MY_HOSTNAME \ + -X github.com/open-policy-agent/opa/v1/version.Timestamp=MY_TIMESTAMP" \ + -o opa++ +``` + +## Appendix + +### Custom Built-in Function in Go + +```golang +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + "net/http" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/rego" + "github.com/open-policy-agent/opa/types" +) + +func main() { + + r := rego.New( + rego.Query(`github.repo("open-policy-agent", "opa")`), + rego.Function2( + ®o.Function{ + Name: "github.repo", + Decl: types.NewFunction(types.Args(types.S, types.S), types.A), + Memoize: true, + Nondeterministic: true, + }, + func(bctx rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error) { + + var org, repo string + + if err := ast.As(a.Value, &org); err != nil { + return nil, err + } else if err := ast.As(b.Value, &repo); err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", fmt.Sprintf("https://api.github.com/repos/%v/%v", org, repo), nil) + if err != nil { + return nil, err + } + + resp, err := http.DefaultClient.Do(req.WithContext(bctx.Context)) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf(resp.Status) + } + + v, err := ast.ValueFromReader(resp.Body) + if err != nil { + return nil, err + } + + return ast.NewTerm(v), nil + }, + ), + ) + + rs, err := r.Eval(context.Background()) + if err != nil { + log.Fatal(err) + } else if len(rs) == 0 { + fmt.Println("undefined") + } else { + bs, _ := json.MarshalIndent(rs[0].Expressions[0].Value, "", " ") + fmt.Println(string(bs)) + } +} +``` + +### Adding Built-in Functions to the OPA Runtime + +```golang +package main + +import ( + "fmt" + "net/http" + "os" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/cmd" + "github.com/open-policy-agent/opa/rego" + "github.com/open-policy-agent/opa/types" + +) + +func main() { + + rego.RegisterBuiltin2( + ®o.Function{ + Name: "github.repo", + Decl: types.NewFunction(types.Args(types.S, types.S), types.A), + Memoize: true, + Nondeterministic: true, + }, + func(bctx rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error) { + + var org, repo string + + if err := ast.As(a.Value, &org); err != nil { + return nil, err + } else if err := ast.As(b.Value, &repo); err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", fmt.Sprintf("https://api.github.com/repos/%v/%v", org, repo), nil) + if err != nil { + return nil, err + } + + resp, err := http.DefaultClient.Do(req.WithContext(bctx.Context)) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf(resp.Status) + } + + v, err := ast.ValueFromReader(resp.Body) + if err != nil { + return nil, err + } + + return ast.NewTerm(v), nil + }, + ) + + if err := cmd.RootCommand.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} +``` diff --git a/third_party/opa/docs/docs/external-data/index.md b/third_party/opa/docs/docs/external-data/index.md new file mode 100644 index 000000000000..0edeafa82b2b --- /dev/null +++ b/third_party/opa/docs/docs/external-data/index.md @@ -0,0 +1,262 @@ +--- +title: External Data +sidebar_position: 6 +--- + +OPA was designed to let you make context-aware authorization and policy decisions by injecting external data that describes what is happening in the world and then writing policy using that data. OPA has a cache or replica of that data, just as OPA has a cache/replica of policy; OPA is not designed to be the source of truth for either. + +This document describes options for replicating data into OPA. The content of the data does not matter, but the size, frequency of update, and consistency constraints all do impact which kind of data replication to employ. You should prefer earlier options in the list to later options, but in the end the right choice depends on your situation. + +## Option 1: JWT Tokens + +[JSON Web Tokens (JWTs)](https://tools.ietf.org/html/rfc7519) allow you to securely transmit JSON data between software systems and are usually produced during the authentication process. You can set up authentication so that when the user logs in you create a JWT with that user's attributes (or any other data as far as OPA is concerned). Then you hand that JWT to OPA and use OPA's specialized support for JWTs to extract the information you need to make a policy decision. + +### Flow + +The following diagram shows this process in more detail. + +1. User logs in to an authentication system, e.g. LDAP/AD/etc. +1. The user is given a JWT token encoding group membership and other user attributes stored in LDAP/AD +1. The user provides that JWT token to an OPA-enabled software system for authentication +1. The OPA-enabled software system includes that token as part of the usual `input` to OPA. +1. OPA decodes the JWT token and uses the contents to make policy decisions. + +```mermaid +sequenceDiagram + box Authn + participant User + participant IDP + end + box OPA Enabled Software + participant OES as OPA Enabled Software + participant OPA + end + User->>+IDP: "1. username/password" + IDP->>+User: 2. JWT + User->>+OES: 3. JWT + OES->>+OPA: 4. JWT & Context + OPA->>+OPA: 5. decode JWT and
evaluated in policy + OPA->>+OES: Policy Response + OES->>+User: Application Response +``` + +### Updates + +The JWT only gets refreshed when the user authenticates; how often that happens is up to the TTL included in the token. When user-attribute information changes, those changes will not be seen by OPA until the user authenticates and gets a new JWT. + +### Size Limitations + +JWTs have a limited size in practice, so if your organization has too many user attributes you may not be able to fit all the required information into a JWT. + +### Security + +- OPA includes primitives to verify the signature of JWT tokens. +- OPA let's you check the TTL. +- OPA has support for making HTTP requests during evaluation, which could be used to check if a JWT has been revoked. Though if you're connecting to a remote system on every policy decision anyway, you should think about whether connecting to the authentication system directly is more appropriate (see below). + +## Option 2: Overload `input` + +Often policies require external data that's not available to the authentication system, ruling out JWTs. The calling system can include external data as part of `input` (necessitating of course that the policy is written accordingly). + +For example, suppose your policy says that only a file's owner may delete it. The authentication system does not track resource-ownership, but the system responsible for files certainly does. + +The file-ownership system may be the one that is asking for an authorization decision from OPA. It already knows which file is being operated on and who the owner is, so it can hand OPA the file-owner as part of OPA's `input`. This can be dangerous in that it ties the integration of OPA to the policy, but often it's sufficient to have the file-ownership system hand over all the file's metadata. + +### Flow + +1. OPA-enabled software gathers relevant metadata (and caches it for subsequent requests) +1. OPA-enabled software sends `input` to OPA including the external data +1. Policy makes decisions based on external data included in `input` + +```mermaid +sequenceDiagram + participant OES as OPA Enabled Software + participant OPA + OES->>+OES: 1. Data Loaded + OES->>+OPA: 2. Runtime and Relevant Preloaded Data + OPA->>+OPA: 3. Policy decision
made using data + OPA->>+OES: Policy Response +``` + +### Updates + +External data gets updated as frequently as the OPA-enabled software updates it. Often some of that data is local to the OPA-enabled software, and sometimes it is remote. The remote data is usually cached for performance and hence is as updated as the caching strategy allows. + +### Size Limitations + +Size limitations are rarely a problem for OPA in this approach because it only sees the metadata for 1 request at a time. However, the cache of remote data that the OPA-enabled service creates will have a limit that the developer controls. + +### Security + +This approach is as secure as the connection between the OPA-enabled service and OPA itself, under the assumption that the OPA-enabled service gathers the appropriate metadata securely. That is, using external data with this approach is as secure as using OPA in the first place. + +### Recommended usage: Local, Dynamic data + +This approach is valuable when the data changes fairly frequently and/or when the cost of making decisions using stale data is high. It works especially well when the external data is local to the system asking for authorization decisions. It can work in the case of remote data as well, but there is more coupling of the system to OPA because the system is hardcoded to fetch the data needed by the policy (and only that data). + +## Option 3: Bundle API + +When external data changes infrequently and can reasonably be stored in memory all at once, you can replicate that data in bulk via OPA's bundle feature. The bundle feature periodically downloads policy bundles from a centralized server, which can include data as well as policy. Every time OPA gets updated policies, it gets updated data too. You must implement the bundle server and integrate your external data into the bundle server--OPA does NOT help with that--but once it is done, OPA will happily pull the data (and policies) out of your bundle server. + +### Flow + +Three things happen independently with this kind of data integration. + +- A. OPA-enabled software system asks OPA for policy decisions +- B. OPA downloads new policy bundles including external data +- C. Bundle server replicates data from source of truth + +```mermaid +graph LR + OES[OPA
Enabled Software] + OPA + DS[Data
Sources] + BS[Bundle
Server] + + OES <-->|A. Req/Resp
Policy Decision| OPA + OPA <-->|B. Bundles Loaded Periodically| BS + BS <-->|C. Data Loaded &
Bundled| DS +``` + +### Updates + +The lag between a data update and OPA having the update is the sum of the lag for an update between data replication and the central bundle server and the lag for an update between the central bundle server and OPA. So if data replication happens every 5 minutes, and OPA pulls a new bundle every 2 minutes, then the total maximum lag is 7 minutes. + +### Size limitations + +OPA stores the entire datasource at once in memory. Obviously this can be a problem with large external data sets. Because the centralized server handles both policy and data it can prune data to just that which is needed for the policies. + + + +### Recommended usage: Static, Medium-sized data + +This approach is more flexible than the JWT and `input` cases above because you can include an entirely new data source at the bundle server without changing the authentication service or the OPA-enabled service. You are also guaranteed that the policy and its corresponding data always arrive at the same time, making the policy-data consistency perfect. + +The drawback is that the consistency of the data with the source of truth is worse than the `input` case and could be better or worse than the consistency for the JWT case (because JWTs only get updated on login). One feature currently under design is a delta-based bundle protocol, which could improve the data consistency model significantly by lowering the cost of frequent updates. But as it stands this approach is ideal when the data is relatively static and the data fits into memory. + +### Ecosystem Projects + + +Loading policy and data via Bundles is an important part of the OPA API. A number of +ecosystem projects make use of this functionality to share code and keep data +up-to-date. + + +## Option 4: Push Data + +Another way to replicate external data in its entirety into OPA is to use OPA's API for injecting arbitrary JSON data. You can build a replicator that pulls information out of the external data source and pushes that information in OPA through its API. This approach is similar in most respects to the bundle API, except it lets you optimize for update latency and network traffic. + +### Flow + +Three things happen independently with this kind of data replication. + +- A. OPA-enabled software system asks OPA for policy decisions +- B. Data replicator pushes data into OPA +- C. Data replicator replicates data from source of truth + +Depending on the replication scheme, B and C could be tied together so that every update the data replicator gets from the source of truth it pushes into OPA, but in general those could be decoupled depending on the desired network load, the changes in the data, and so on. + +```mermaid +graph LR + OES[OPA
Enabled Software] + OPA + DS[Data
Sources] + R[Replicator] + + OES <-->|A. Req/Resp
Policy Decision| OPA + R -->|B. Data Push|OPA + R <-->|C. Data replicated from SoT| DS +``` + +### Updates + +The total lag between the external data source being updated and OPA being updated is the sum of the lag for an update between the data source and the synchronizer plus the lag for an update between the synchronizer and OPA. + +### Size limitations + +The entirety of the external data source is stored in memory, which can obviously be a problem with large external data sources. But unlike the bundle API, this approach does allow updates to data. + + + +### Recommended usage: Dynamic, Medium-sized data + +This approach is very similar to the bundle approach except it updates the data stored in OPA with deltas instead of an entire snapshot at a time. Because the data is updated as deltas, this approach is well-suited for data that changes frequently. It assumes the data can fit entirely in memory and so is well-suited to small and medium-sized data sets. + +### Ecosystem Projects + + +Some OPA Ecosystem projects support pushing data into OPA. + + +## Option 5: Pull Data during Evaluation + +OPA includes functionality for reaching out to external servers during evaluation. This functionality handles those cases where there is too much data to synchronize into OPA, JWTs are ineffective, or policy requires information that must be as up to date as possible. + +That functionality is implemented using built-in functions such as [`http.send`](https://www.openpolicyagent.org/docs/latest/policy-reference/#http). Check the docs for the latest instructions. + +### Current limitations + +- Credentials needed for the external service can either be hardcoded into policy or pulled from the environment. +- The built-in functions do not implement any retry logic. + +### Flow + +The key difference here is that every decision requires contacting the external data source. If that service or the network connection is slow or unavailable, OPA may not be able to return a decision. + +1. OPA-enabled service asks OPA for a decision +1. During evaluation OPA asks the external data source for additional information + +```mermaid +graph LR + OES[OPA
Enabled Software] + OPA + DS["Data
Sources(s)"] + + OES <-->|1 Req/Resp
Policy Decision| OPA + OPA <-->|2 OPA Loads data at runtime| DS +``` + +### Updates + +External data is perfectly fresh. There is no lag between an update to the external data and when OPA sees that update. + +### Size limitations + +Only the data actually needed by the policy is pulled from the external data source. There is no need for a replicator to figure out what data the policy will need before execution. + +### Performance and Availability + +Latency and availability of decision-making are dependent on the network. This approach may still be superior to running OPA on a remote server entirely because a local OPA can make some decisions without going over the network--those decisions that do not require information from the remote data server. + +### Recommended usage: Highly Dynamic or Large-sized data + +If the data is too large to fit into memory, or it changes too frequently to cache it inside of OPA, the only real option is to fetch the data on demand. The `input` approach fetches data on demand as well, but puts the burden on the OPA-enabled service to fetch the necessary data (and to know what data is necessary). + +The downside to pulling data on demand is reduced performance and availability because of the network, which can be mitigated via caching. In the `input` case, caching is under the control of the OPA-enabled service and can therefore be tailored to fit the properties of the data. In the `http.send` case, caching is largely under the control of the remote service that sets HTTP response headers to indicate how long the response can be cached for. It is crucial in this approach for the OPA-enabled service to handle the case when OPA returns no decision. + +### Ecosystem Projects + + +Loading data at evaluation time has been an area of focus for some projects in the OPA community.\ + + +## Summary + +| Approach | Perf/Avail | Limitations | Recommended Data | +| --------------- | -------------------- | ------------------------------------------------------- | ---------------- | +| JWT | High | Updates only when user logs back in | User attributes | +| Input | High | Coupling between service and OPA | Local, dynamic | +| Bundle | High | Updates to policy/data at the same time. Size an issue. | Static, medium | +| Push | High | Control data refresh rate. Size an issue. | Dynamic, medium | +| Evaluation Pull | Dependent on network | Perfectly up to date. No size limit. | Dynamic or large | + +## Ecosystem Projects + + +Here are some projects that integrate with OPA to provide external data. + diff --git a/third_party/opa/docs/docs/faq.md b/third_party/opa/docs/docs/faq.md new file mode 100644 index 000000000000..957c721fa03b --- /dev/null +++ b/third_party/opa/docs/docs/faq.md @@ -0,0 +1,439 @@ +--- +title: Frequently Asked Questions +--- + +## How do I make user attributes stored in LDAP/AD available to OPA for making decisions? + +[This best-practice guide](./external-data) explains three options: JSON Web Tokens, synchronization with LDAP/AD, and calling into LDAP/AD during policy evaluation. + +## How does OPA do conflict resolution? {#conflict-resolution} + +In Rego (OPA's policy language), you can write statements that both allow and +deny a request, such as + +```rego +package foo + +allow { input.name == "alice" } +deny { input.name == "alice" } +``` + +Neither `allow` nor `deny` are keywords in Rego so if you want to treat them +as contradictory, you control which one takes precedence explicitly. When you ask for +a policy decision from OPA, you specify both the policy name (`foo`) and the +virtual document that names the decision within foo. Typically in this scenario, +you create a virtual document called `authz` and define it so that `allow` +overrides `deny` or vice versa. Then when asking for a policy decision, you +ask for `foo/authz`. + +```rego +# deny everything by default +default authz := false + +# deny overrides allow +authz { + allow + not deny +} +``` + +If instead you want to resolve conflicts using a first-match strategy (where +the first statement applicable makes the decision), see the FAQ entry on +[statement order](#statement-order). + +## Does Statement Order Matter? {#statement-order} + +The order in which statements occur does not matter in Rego. Reorder any two statements +and the policy means exactly the same thing. For example, the following two statements +mean the same thing whichever order you write them in. + +```rego +package unordered + +ratelimit := 4 if input.name == "alice" +ratelimit := 5 if input.name == "bob" +``` + + + +```json title="input.json" +{ + "name": "bob" +} +``` + + + +Sometimes, though, you want the statement order to matter. For example, you might put more specific statements before more general statements so that the more specific statements take precedence (e.g. for [conflict resolution](#conflict-resolution)). Rego lets you do that using the `else` keyword. For example, if you want to make the first statement above take precedence, you would write the following Rego. + +```rego +package ordered + +ratelimit := 4 if { + input.department == "engineering" +} else := 3 if { + input.name == "alice" +} +``` + + + +```json title="input.json" +{ + "name": "alice", + "department": "engineering" +} +``` + + + +## Which Equality Operator Should I Use? + +Rego supports three kinds of equality: assignment (`:=`), comparison (`==`), and unification `=`. We recommend using assignment (`:=`) and comparison (`==`) whenever possible for policies that are easier to read and write. + +```rego +# Assignment: declare local variable x and give it value 7 +# If x appears before this statement in the rule, compiler throws error. +x := 7 +y := {"a", "b", "c"} + +# Comparison: check if two values are the same. +# Do not assign variables--variables must be "safe". +x == 7 +x == y +y == [1, 2, [3]] + +# Unification: assign variables to values that make the +# equality true +x = 7 # causes x to be assigned 7 +[x, 2] = [3, y] # x is assigned 3 and y is assigned 2 +``` + +## Collaboration Using Import + +OPA lets multiple teams contribute independent policies that you can then combine to make an overall decision. Each team writes their policy in a separate `package`, then you write one more policy that imports all the teams policies and makes a decision. + +For example, suppose there is a network team, a storage team, and a compute team. Suppose they each write their own policy: + +```rego +package compute +allow { ... } +``` + +```rego +package network +allow { ... } +``` + +```rego +package storage +allow { ... } +``` + +Now the cloud team, who is in charge of the overall decision, writes another policy that combines the decisions for each of the team policies. In the example below, all 3 teams must allow for the overall decision to be allowed. + +```rego +package main +import data.compute +import data.storage +import data.network + +# allow if all 3 teams allow +allow { + compute.allow + storage.allow + network.allow +} +``` + +The cloud team could have a more sophisticated scheme for combining policies, e.g. using just the compute policy for compute-only resources or requiring the compute policy to allow the compute-relevant portions of resource. Remember that `allow` is not special--it is just another boolean that the policy author can use to make decisions. + +## Functions Versus Rules + +Rego lets you factor out common logic in 2 different and complementary ways. + +One is the _function_, which is conceptually identical to functions from most programming languages. It takes any input and returns any output. Importantly, a function can take infinitely many inputs, e.g. any string. + +```rego +package functions + +trim_and_split(s) := result if { + t := trim(s, " ") + result := split(t, ".") +} + +result := trim_and_split(" hello.world ") +``` + + + +The other way to factor out common logic is with a _rule_. Rules differ in that (i) they support automatic iteration and (ii) they are only defined for finitely many inputs. (Those obviously go hand-in-hand.) For example, you could define a rule that maps an application to the hostnames that app is running on: + +```rego +package rules + +app_to_hostnames[app_name] := hostnames if { + app := apps[_] + app_name := app.name + hostnames := [hostname | name := app.servers[_] + s := sites[_].servers[_] + s.name == name + hostname := s.hostname] +} + +apps := [ + { + "name": "web", + "servers": ["s1", "s2"], + }, + { + "name": "mysql", + "servers": ["s3"], + }, + { + "name": "mongodb", + "servers": ["s4"], + }, +] + +sites := [ + { + "servers": [ + { + "name": "s1", + "hostname": "hydrogen", + }, + { + "name": "s3", + "hostname": "helium", + }, + { + "name": "s4", + "hostname": "nitrogen", + }, + ], + }, + { + "servers": [ + { + "name": "s2", + "hostname": "carbon", + }, + ], + }, +] +``` + + + +And then we can iterate over all the key/value pairs of that app-to-hostname mapping (just like we could iterate over all key/value pairs of a hardcoded JSON object). You can also iterate over just the keys or just the values or you can look up the value for a key or lookup all the keys for a single value. + +```rego +package example + +import data.rules.app_to_hostnames + +# iterate over all key/value pairs +result.all contains [k, v] if { + some k, v in app_to_hostnames +} + +# iterate over all values +result.values := {e| some e in app_to_hostnames } + +# iterate over all keys +result.keys contains x if { + some x + app_to_hostnames[x] +} + +# lookup the value for key "web" +result.web := app_to_hostnames["web"] + +# lookup keys where value includes "carbon" +result.where contains k if { + some k + app_to_hostnames[k][_] == "carbon" +} +``` + + + +Obviously with the `trim_and_split` function we cannot ask for all the inputs/outputs since there are infinitely many. We can't provide 1 input and ask for all the other inputs that make the function return true, again, because there could be infinitely many. The only thing we can do with a function is provide it all the inputs and ask for the output. + +Functions allow you to factor out common logic that has infinitely-many input/output pairs; rules allow you to factor out common logic with finitely many input/outputs and allow you to iterate over them in the same way as native JSON objects. + +To achieve automatic iteration, there is an additional syntactic requirement on a rule that is NOT present for a function: `safety`. See the FAQ entry on safety for technical details. Every rule must be `safe`, which guarantees that OPA can figure out a finite list of possible values for every variable in the body and head of a rule. + +We recommend using rules where possible and using functions when rules do not work. + +## Safety + +The compiler will sometimes throw errors that say a rule is not `safe`. The goal of safety is to ensure that every rule has finitely many inputs/outputs. Safety ensures that every variable has finitely many possible values, so that OPA can iterate over them to find those values that make the rule true. Technically: + +``` +Safety: every variable appearing in the head or in a builtin or inside a negation must appear in a non-negated, non-builtin expression in the body of the rule. +``` + +Examples: + +```rego +# Unsafe: x in head does not appear in body. +# There are infinitely many values that make p true +p[x] { some y; q[y]; r[y] } + +# Safe. q and r are both rules +# Both q and r are finite; therefore p is also finite. +p[x] := y { some x, y; q[x]; r[y] } + +# Unsafe: y appears inside a builtin (+) but not in the body. +# y has infinitely many possible values; so too does x. +p[x] { some y; x := y + 7 } + +# Safe: the only values for y are those in q. +# Since q is a rule and finite so is p finite. +p[x] { some y; x := y + 7; q[y]} + +# Unsafe: x appears inside a negation +# If q is finite, all the x's not in q are infinite. +p[x] { some x; not q[x] } + +# Safe: x appears inside of r so p is no larger than r +# Since r is finite, so too is p +p[x] { some x; not q[x]; r[x] } +``` + +Safety has one implication about negation: you don't iterate over values NOT in a rule like `q`. Instead, you iterate over values in another rule like `r` and then use negation to CHECK whether if that value is NOT in `q`. + +Embedded terms like `not p[q[_]]` sometimes produce difficult to decipher error messages. We recommend pulling the embedded terms out into the rule--the meaning is the same and often creates easier to read error messages: + +```rego +x := q[_] +not p[x] +``` + +## JSON Web Tokens (JWTs) + +[JSON Web Tokens (JWTs)](https://jwt.io/) are an industry standard for exchanging information between services. Often they are used to represent information about the users logged into a system. OPA has special-purpose code for dealing with JWTs. + +All JWTs with OPA come in as strings. That string is a JSON Web Token encoded with JWS Compact Serialization. JWE and JWS JSON Serialization are not supported. + +You can verify tokens are properly signed. + +```rego +# RS256 signature +io.jwt.verify_rs256(string, certificate) + +# PS256 signature +io.jwt.verify_ps256(string, certificate) + +# ES256 signature +io.jwt.verify_es256(string, certificate) + +# HS256 signature +io.jwt.verify_hs256(string, certificate) +``` + +You can decode JWTs and use the contents of the JWT to make policy decisions. + +```json +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM" +} +``` + + + +```rego +package jwt_decode + +result := io.jwt.decode(input.token) +``` + + + +:::info +If nested signing was used, the header, payload and signature will represent the most deeply nested token. +::: + +You can decode **and** verify using `io.jwt.decode_verify`. + +```rego +package jwt_decode + +result := io.jwt.decode_verify(input.token, { + "secret": "secret", + "alg": "HS256", +}) +``` + + + +See the [Policy Reference](./policy-reference#tokens) for additional verification constraints. + +To get certificates into the policy, you can either hardcode them or provide them as environmental variables to OPA and then use the `opa.runtime` builtin to retrieve those variables. + +```rego +# all runtime information +runtime := opa.runtime() + +# environment variables provided when OPA started +runtime.env + +# the env variable PROD_CERTIFICATE +runtime.env.PROD_CERTIFICATE +``` + +## How do I Write Policies Securely? + +Depending on the use case and the integration with OPA that you are using, the style of policy you choose can impact your overall security posture. Below we show three styles of authoring policy and compare them. + +**Default allow**. This style of policy allows every request by default. The rules you write dictate which requests should be rejected. + +```rego +package example + +# entry point is 'deny' +default deny := false + +deny if { ... } +deny if { ... } +``` + +If you assume all of the rules you write are correct, then you know that every rejection the policy produces should truly be rejected. However, there could be requests that are allowed that you may not truly want allowed, but you simply neglected to write the rule for. For operations, this is often a useful style of policy authoring because it allows you to incrementally tighten the controls for a system from wherever that system starts. For security, this style is less appropriate because it allows unknown bad actions to occur. + +**Default deny**. This style of policy rejects every request by default. The rules you write dictate which requests should be allowed. + +```rego +package example + +# entry point is 'allow' +default allow := false + +allow if { ... } +allow if { ... } +``` + +If you assume your rules are correct, the only requests that are accepted are known to be safe. Any statements you leave out reject requests that in actuality are safe but which you did not know were safe. For operations, these policies are less suitable for incrementally improving the policy posture of a system because the initial policy must explicitly allow all of the behaviors that are necessary for the system to operate correctly. For security, these policies ensure that any request that is allowed is known to be safe (because there is a rule saying it is safe). + +**Default allow with deny override**. This style of policy rejects every request by default. You write rules that dictate which requests should be allowed, and optionally you write other rules that dictate which of those allowed requests should be rejected. + +```rego +package example + +# entry point is 'authz' +default authz := false + +authz if { + allow + not deny +} + +allow if { ... } + +deny if { ... } +``` + +This hybrid approach to policy authoring combines the two previous styles. These policies allow relatively coarse grained parts of the request space and then carve out of each part what should actually be denied. Any deny statements that you forget lead to security problems; any allow statements you forget lead to operational problems. But since this approach allows you to implement either of the other two, it is a common pattern across use cases. + +**Non-boolean policies**. The examples above focus on policies with boolean decisions. Policies that make non-boolean decisions typically have similar tradeoffs. Are you enumerating the conditions under which requests are permitted (e.g. the list of clusters to which an app SHOULD be deployed) or are you enumerating the conditions under which requests are prohibited (e.g. the list of clusters to which an app SHOULD NOT be deployed). While the details differ, the concepts are often similar. diff --git a/third_party/opa/docs/docs/graphql-api-authorization.md b/third_party/opa/docs/docs/graphql-api-authorization.md new file mode 100644 index 000000000000..d30645b7ddf5 --- /dev/null +++ b/third_party/opa/docs/docs/graphql-api-authorization.md @@ -0,0 +1,520 @@ +--- +title: "GraphQL APIs" +--- + +GraphQL APIs have become a popular way for clients to query the information +they require from a range of data sources. +Generally, services providing a [GraphQL](https://graphql.org/) API must +authorize calls to control data access and mutations. +OPA makes it easy to write fine-grained, context-aware policies to implement +GraphQL query authorization. + +In this tutorial, you'll use a simple GraphQL server that accepts any GraphQL query that you issue, and echoes the OPA decision back as text. +OPA will fetch policy bundles from a simple bundle server. +OPA, the bundle server, and the GraphQL server will run as separate containers. + +For this tutorial, we have the following example scenario: + +- Staff can see their own salaries (`query user($id: ) { salary }` is permitted for ``) +- A manager can see their direct reports' salaries (`query user($id: ) { salary }` is permitted for ``'s manager) + +This tutorial requires [Docker Compose](https://docs.docker.com/compose/install/) to run a demo web server along with OPA. + +:::info +Using GraphQL in Rego via the +[GraphQL built-in functions](./policy-reference#graphql) +can involve some cumbersome code-sharing, for example when sharing custom +`@directive` definitions between schemas used in different rules. +We recommend you evaluate the range of available functions and review your +GraphQL feature use to form a plan before embarking on major migrations. +::: + +## Steps + +### 1. Define our GraphQL schema. + +Most modern GraphQL frameworks encourage starting with a schema, so we'll follow suit, and begin by defining the schema for this example. + +```graphql title="schema.gql" +type Employee { + id: String! + salary: Int! +} + +schema { + query: Query +} + +type Query { + employeeByID(id: String!): Employee +} +``` + +Every GraphQL service has a `query` type, and may or may not have a `mutation` type. +These types are special because they define the entry points of _every_ GraphQL query for the API covered by that schema. + +For our example above, we've defined exactly one query entry point, the parameterized query `employeeByID(id: String!)`. + +### 2. Create a policy bundle. + +GraphQL APIs allow surprising flexibility in how queries can be constructed, which makes writing policies for them a bit more challenging than for a REST API, which usually has a more fixed structure. + +To protect a particular endpoint or field, we need to see if they are referenced in the incoming GraphQL query. +By using `graphql.parse`, we can extract an [abstract syntax tree][wikipedia-ast] (AST) from the incoming query, and then walk down the tree to its leaves to see if our endpoint is the target of the query. + +We can then use separate rules to enforce conditions around the `salary` field, and who is allowed to access it. + +The policy below does all of the above in parts: + +- Obtains the query AST (and validates it against our schema with `graphql.parse`). +- Recursive traversal with `walk()` to obtain chunks of the AST with queries of interest present. + - Selection of nodes of interest by name and structure. +- Salary field selected. +- Every query of interest found has to pass one of the `allowed_query` rules, or the entire query is rejected. + - Constant/variable cases for both employees and their managers. + +```rego title="example.rego" +package graphqlapi.authz + +subordinates := {"alice": [], "charlie": [], "bob": ["alice"], "betty": ["charlie"]} + +query_ast := graphql.parse(input.query, input.schema)[0] # If validation fails, the rules depending on this will be undefined. + +default allow := false + +allow if { + employeeByIDQueries != {} + every query in employeeByIDQueries { + allowed_query(query) + } +} + +# Allow users to see the salaries of their subordinates. (variable case) +allowed_query(q) if { + selected_salary(q) + varname := variable_arg(q, "id") + input.variables[varname] in subordinates[input.user] # Do value lookup from the 'variables' object. +} + +# Allow users to see the salaries of their subordinates. (constant value case) +allowed_query(q) if { + selected_salary(q) + username := constant_string_arg(q, "id") + username in subordinates[input.user] +} + +# Helper rules. + +# Allow users to get their own salaries. (variable case) +allowed_query(q) if { + selected_salary(q) + varname := variable_arg(q, "id") + input.user == input.variables[varname] # Do value lookup from the 'variables' object. +} + +# Allow users to get their own salaries. (constant value case) +allowed_query(q) if { + selected_salary(q) + username := constant_string_arg(q, "id") + input.user == username +} + +# Helper functions. + +# Build up an object with all queries of interest as values. +employeeByIDQueries contains value if { + some value + walk(query_ast, [_, value]) + value.Name == "employeeByID" + count(value.SelectionSet) > 0 # Ensure we latch onto an employeeByID query. +} + +# Extract the string value of a constant value argument. +constant_string_arg(value, argname) := arg.Value.Raw if { + some arg in value.Arguments + arg.Name == argname + arg.Value.Kind == 3 +} + +# Extract the variable name for a variable argument. +variable_arg(value, argname) := arg.Value.Raw if { + some arg in value.Arguments + arg.Name == argname + arg.Value.Kind == 0 +} + +# Ensure we're dealing with a selection set that includes the "salary" field. +selected_salary(value) := value.SelectionSet[_].Name == "salary" +``` + + + +Then, build a bundle. + +```shell +mkdir bundles +opa build example.rego +mv bundle.tar.gz ./bundles +``` + +You should now see a policy bundle (`bundle.tar.gz`) in your working directory. + +### 3. Bootstrap the tutorial environment using Docker Compose. + +Next, create a `docker-compose.yaml` file that runs OPA, a bundle server and the demo GraphQL server. + + +```yaml title="docker-compose.yaml" +services: + opa: + image: openpolicyagent/opa:{{ current_version_docker }} + ports: + - "8181:8181" + command: + - "run" + - "--server" + - "--log-format=json-pretty" + - "--set=decision_logs.console=true" + - "--set=services.nginx.url=http://bundle_server" + - "--set=bundles.nginx.service=nginx" + - "--set=bundles.nginx.resource=bundle.tar.gz" + - "--set=bundles.nginx.polling.min_delay_seconds=10" + - "--set=bundles.nginx.polling.max_delay_seconds=30" + depends_on: + - bundle_server + api_server: + image: openpolicyagent/demo-graphql-api:0.1 + ports: + - "6000:5000" + environment: + - OPA_ADDR=http://opa:8181 + - POLICY_PATH=/v1/data/graphqlapi/authz + depends_on: + - opa + bundle_server: + image: nginx:1.20.0-alpine + ports: + - 8888:80 + volumes: + - ./bundles/:/usr/share/nginx/html/ +``` + + +Then run `docker-compose` to pull and run the containers. + +:::info +If running "Docker Desktop" (Mac or Windows) you may instead use the `docker compose` command. +::: + +```shell +docker-compose -f docker-compose.yaml up +``` + +Every time the demo GraphQL server receives an HTTP request, it asks OPA to decide whether an GraphQL query is authorized or not using a single RESTful API call. +An example codebase is [here][graphql-example-repo], but the crux of the (JavaScript, Apollo framework) code is shown below. + +[graphql-example-repo]: https://github.com/StyraInc/graphql-apollo-example + +```javascript +// we assume user is passed in as part of the request context. +var user = req.user; + +// we feed in the query and schema strings, as well as the variables object. +var input = { + input: { + schema: schema, // GraphQL schema text. + query: query, // GraphQL query text. + user: user, + variables: variables, // GraphQL variable bindings. + }, +}; + +await axios + // ask OPA for a policy decision. + // (in reality OPA URL would be constructed from environment) + .post("http://127.0.0.1:8181/v1/data/graphqlapi/authz", input) + .then(res => { + // GraphQL query allowed. + }) + .catch(error => { + // GraphQL query denied. + }); +``` + +### 4. Check that `alice` can see her own salary. + +We'll define a quick shell function to make the following examples cleaner on the command line: + +```shell +gql-query() { + curl --user "$1" -H "Content-Type: application/json" "$2" --data-ascii "$3" +} +``` + +The following command will succeed. + +```shell +gql-query alice:password "localhost:6000/" '{"query":"query { employeeByID(id: \"alice\") { salary }}"}' +``` + +The GraphQL server queries OPA to authorize the request. +In the query, the server includes JSON data describing the incoming request. + +```json +{ + "schema": "type Employee {\n id: ...", + "query": "query { employeeByID(id: \"alice\") { salary }}", + "user": "alice", + "variables": {} +} +``` + + + +When the GraphQL server queries OPA it asks for a specific policy decision. +In this case, the integration is hardcoded to ask for `/v1/data/graphqlapi/authz`. +OPA translates this URL path into a query: + +```rego +package example + +result := data.graphqlapi.authz +``` + + + +### 4. Check that `bob` can see `alice`'s salary (because `bob` is `alice`'s manager.) + +```shell +gql-query bob:password "localhost:6000/" '{"query":"query { employeeByID(id: \"alice\") { salary }}"}' +``` + +### 5. Check that `bob` CANNOT see `charlie`'s salary. + +`bob` is not `charlie`'s manager, so the following command will fail. + +```shell +gql-query bob:password "localhost:6000/" '{"query":"query { employeeByID(id: \"charlie\") { salary }}"}' +``` + +### 6. Change the policy. + +Suppose the organization now includes an HR department. +The organization wants members of HR to be able to see any salary. +Let's extend the policy to handle this. + +```rego title="example-hr.rego" +package graphqlapi.authz + +# Allow HR members to get anyone's salary. +allowed_query(q) if { + selected_salary(q) + input.user == hr[_] +} + +# David is the only member of HR. +hr := ["david"] +``` + +Build a new bundle with the new policy included. + +```shell +opa build example.rego example-hr.rego +mv bundle.tar.gz ./bundles +``` + +The updated bundle will automatically be served by the bundle server, but note that it might take up to the configured `max_delay_seconds` for the new bundle to be downloaded by OPA. +If you plan to make frequent policy changes you might want to adjust this value in `docker-compose.yaml` accordingly. + +For the sake of the tutorial we included `manager_of` and `hr` data directly inside the policies. +In real-world scenarios that information would be imported from external data sources. + +### 7. Check that the new policy works. + +Check that `david` can see anyone's salary. + +```shell +gql-query david:password "localhost:6000/" '{"query":"query { employeeByID(id: \"alice\") { salary }}"}' +gql-query david:password "localhost:6000/" '{"query":"query { employeeByID(id: \"bob\") { salary }}"}' +gql-query david:password "localhost:6000/" '{"query":"query { employeeByID(id: \"charlie\") { salary }}"}' +gql-query david:password "localhost:6000/" '{"query":"query { employeeByID(id: \"david\") { salary }}"}' +``` + +### 8. (Optional) Use JSON Web Tokens to communicate policy data. + +OPA supports the parsing of JSON Web Tokens via the builtin function `io.jwt.decode`. +To get a sense of one way the subordinate and HR data might be communicated in the real world, let's try a similar exercise utilizing the JWT utilities of OPA. + +```rego title="example-jwt.rego" +package graphqlapi.authz + +query_ast := graphql.parse(input.query, input.schema)[0] # If validation fails, the rules depending on this will be undefined. + +# Helper rules. + +# Allow users to see the salaries of their subordinates. (variable case) +allowed_query(q) if { + selected_salary(q) + varname := variable_arg(q, "id") + input.variables[varname] in token.payload.subordinates # Do value lookup from the 'variables' object. +} + +# Allow users to see the salaries of their subordinates. (constant value case) +allowed_query(q) if { + selected_salary(q) + username := constant_string_arg(q, "id") + username in token.payload.subordinates +} + +# Allow users to get their own salaries. (variable case) +allowed_query(q) if { + selected_salary(q) + varname := variable_arg(q, "id") + token.payload.user == input.variables[varname] # Do value lookup from the 'variables' object. +} + +# Allow users to get their own salaries. (constant value case) +allowed_query(q) if { + selected_salary(q) + username := constant_string_arg(q, "id") + token.payload.user == username +} + +# Allow HR members to get anyone's salary. +allowed_query(q) if { + selected_salary(q) + token.payload.hr == true +} + +# Helper functions. + +# Build up a set with all queries of interest as values. +employeeByIDQueries contains value if { + some value + walk(query_ast, [_, value]) + value.Name == "employeeByID" + count(value.SelectionSet) > 0 # Ensure we latch onto an employeeByID query. +} + +# Extract the string value of a constant value argument. +constant_string_arg(value, argname) := arg.Value.Raw if { + some arg in value.Arguments + arg.Name == argname + arg.Value.Kind == 3 +} + +# Extract the variable name for a variable argument. +variable_arg(value, argname) := arg.Value.Raw if { + some arg in value.Arguments + arg.Name == argname + arg.Value.Kind == 0 +} + +# Ensure we're dealing with a selection set that includes the "salary" field. +selected_salary(value) := value.SelectionSet[_].Name == "salary" +``` + +```rego +default allow := false + +allow if { + employeeByIDQueries != {} + user_owns_token # Ensure we validate the JWT token. + every query in employeeByIDQueries { + allowed_query(query) + } +} + +# Helper rules ... (Same as example.rego) + +# Helper functions ... (Same as example.rego) + +# ------------------------------------------------------------- +# JWT Token Support + +# Ensure that the token was issued to the user supplying it. +user_owns_token if input.user == token.payload.azp + +# Helper to get the token payload. +token := {"payload": payload} if { + [_, payload, _] := io.jwt.decode(input.token) +} +``` + +```json +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM", + "schema": "type Employee {\n id: ...", + "query": "query { employeeByID(id: \"alice\") { salary }}", + "user": "alice", + "variables": {} +} +``` + +Build a new bundle for the new policy. + +```shell +opa build example-jwt.rego example-hr.rego +mv bundle.tar.gz ./bundles +``` + +For convenience, we'll want to store user tokens in environment variables (they're really long). + +```shell +export ALICE_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM" +export BOB_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYm9iIiwiYXpwIjoiYm9iIiwic3Vib3JkaW5hdGVzIjpbImFsaWNlIl0sImhyIjpmYWxzZX0.n_lXN4H8UXGA_fXTbgWRx8b40GXpAGQHWluiYVI9qf0" +export CHARLIE_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiY2hhcmxpZSIsImF6cCI6ImNoYXJsaWUiLCJzdWJvcmRpbmF0ZXMiOltdLCJociI6ZmFsc2V9.EZd_y_RHUnrCRMuauY7y5a1yiwdUHKRjm9xhVtjNALo" +export BETTY_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYmV0dHkiLCJhenAiOiJiZXR0eSIsInN1Ym9yZGluYXRlcyI6WyJjaGFybGllIl0sImhyIjpmYWxzZX0.TGCS6pTzjrs3nmALSOS7yiLO9Bh9fxzDXEDiq1LIYtE" +export DAVID_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZGF2aWQiLCJhenAiOiJkYXZpZCIsInN1Ym9yZGluYXRlcyI6W10sImhyIjp0cnVlfQ.Q6EiWzU1wx1g6sdWQ1r4bxT1JgSHUpVXpINMqMaUDMU" +``` + +These tokens encode the same information as the policies we did before (`bob` is `alice`'s manager, `betty` is `charlie`'s, `david` is the only HR member, etc). +If you want to inspect their contents, start up the OPA REPL and execute `io.jwt.decode(, [header, payload, signature])` or open the example above in the Playground. + +Let's try a few queries (note: you may need to escape the `?` characters in the queries for your shell): + +Check that `charlie` can't see `bob`'s salary. + +```shell +gql-query charlie:password "localhost:5000/?token=$CHARLIE_TOKEN" '{"query":"query { employeeByID(id: \"bob\") { salary }}"}' +``` + +Check that `charlie` can't pretend to be `bob` to see `alice`'s salary. + +```shell +gql-query charlie:password "localhost:5000/?token=$BOB_TOKEN" '{"query":"query { employeeByID(id: \"alice\") { salary }}"}' +``` + +Check that `david` can see `betty`'s salary. + +```shell +gql-query david:password "localhost:5000/?token=$DAVID_TOKEN" '{"query":"query { employeeByID(id: \"betty\") { salary }}"}' +``` + +Check that `bob` can see `alice`'s salary. + +```shell +gql-query bob:password "localhost:5000/?token=$BOB_TOKEN" '{"query":"query { employeeByID(id: \"alice\") { salary }}"}' +``` + +Check that `alice` can see her own salary. + +```shell +gql-query alice:password "localhost:5000/?token=$ALICE_TOKEN" '{"query":"query { employeeByID(id: \"alice\") { salary }}"}' +``` + +## Wrap Up + +Congratulations for finishing the tutorial! + +You learned a number of things about API authorization with OPA: + +- OPA gives you fine-grained policy control over GraphQL APIs once you set up the server to ask OPA for authorization. +- You write allow/deny policies to control which endpoints and fields can be accessed by whom. +- You can import external data into OPA and write policies that depend on that data. +- You can use OPA data structures to define abstractions over your data. +- You can use a remote bundle server for distributing policy and data. + +The code for this tutorial can be found in the +[StyraInc/graphql-apollo-example][graphql-example-repo] +repository. diff --git a/third_party/opa/docs/docs/http-api-authorization.md b/third_party/opa/docs/docs/http-api-authorization.md new file mode 100644 index 000000000000..932a915673ee --- /dev/null +++ b/third_party/opa/docs/docs/http-api-authorization.md @@ -0,0 +1,373 @@ +--- +title: "HTTP APIs" +--- + +Anything that exposes an HTTP API (whether an individual microservice or an application as a whole) needs to control who can run those APIs and when. OPA makes it easy to write fine-grained, context-aware policies to implement API authorization. + +## Goals + +In this tutorial, you'll use a simple HTTP web server that accepts any HTTP GET +request that you issue and echoes the OPA decision back as text. OPA will fetch +policy bundles from a simple bundle server. Both OPA, the bundle server and the +web server will be run as containers. + +For this tutorial, our desired policy is: + +- People can see their own salaries (`GET /finance/salary/{user}` is permitted for `{user}`) +- A manager can see their direct reports' salaries (`GET /finance/salary/{user}` is permitted for `{user}`'s manager) + +## Prerequisites + +This tutorial requires [Docker Compose](https://docs.docker.com/compose/install/) to run a demo web server along with OPA. + +## Steps + +### 1. Create a policy bundle. + +Create a policy that allows users to request their own salary as well as the salary of their direct subordinates. + +**First** create a directory named `bundles` and cd into it. + +```shell +mkdir bundles +cd bundles +``` + +```rego title=example.rego" +package httpapi.authz + +# bob is alice's manager, and betty is charlie's. +subordinates := {"alice": [], "charlie": [], "bob": ["alice"], "betty": ["charlie"]} + +default allow := false + +# Allow users to get their own salaries. +allow if { + input.method == "GET" + input.path == ["finance", "salary", input.user] +} + +# Allow managers to get their subordinates' salaries. +allow if { + some username + input.method == "GET" + input.path = ["finance", "salary", username] + subordinates[input.user][_] == username +} +``` + + + +**Then**, build a bundle. + +```shell +opa build example.rego +cd .. +``` + +You should now see a policy bundle (`bundle.tar.gz`) in your working directory (`./bundles/bundle.tar.gz`). + +### 2. Bootstrap the tutorial environment using Docker Compose. + +Next, create a `docker-compose.yaml` file that runs OPA, a bundle server and the demo web server. + + +```yaml title="docker-compose.yaml" +version: "2" +services: + opa: + image: openpolicyagent/opa:{{ current_version_docker }} + ports: + - 8181:8181 + # WARNING: OPA is NOT running with an authorization policy configured. This + # means that clients can read and write policies in OPA. If you are + # deploying OPA in an insecure environment, be sure to configure + # authentication and authorization on the daemon. See the Security page for + # details: https://www.openpolicyagent.org/docs/security.html. + command: + - "run" + - "--server" + - "--addr=0.0.0.0:8181" + - "--log-format=json-pretty" + - "--set=decision_logs.console=true" + - "--set=services.nginx.url=http://bundle_server" + - "--set=bundles.nginx.service=nginx" + - "--set=bundles.nginx.resource=bundles/bundle.tar.gz" + depends_on: + - bundle_server + api_server: + image: openpolicyagent/demo-restful-api:0.3 + ports: + - 5000:5000 + environment: + - OPA_ADDR=http://opa:8181 + - POLICY_PATH=/v1/data/httpapi/authz + depends_on: + - opa + bundle_server: + image: nginx:1.20.0-alpine + ports: + - 8888:80 + volumes: + - ./bundles:/usr/share/nginx/html/bundles +``` + + +Then run `docker-compose` to pull and run the containers. + +**NOTE:** if running "Docker Desktop" (Mac or Windows) you may instead use the `docker compose` command. + +```shell +docker-compose -f docker-compose.yaml up +``` + +:::info +This example shows conceptually a 'manual' REST API integration with OPA. +You might find it easier to build your OPA integration using one of the +[language SDKs](/ecosystem/#languages) than working with the REST API directly. +::: + +Every time the demo web server receives an HTTP request, it +asks OPA to decide whether an HTTP API is authorized or not +using a single RESTful API call. An example code is [here](https://github.com/open-policy-agent/contrib/blob/main/api_authz/docker/echo_server.py), +but the crux of the (Python) code is shown below. + +```python +# Grab basic information. We assume user is passed on a form. +http_api_user = request.form['user'] + +# Get the path as a list (removing leading and trailing /) +# Example: "/finance/salary/" will become ["finance", "salary"] +http_api_path_list = request.path.strip("/").split("/") + +input_dict = { # create input to hand to OPA + "input": { + "user": http_api_user, + "path": http_api_path_list, # Ex: ["finance", "salary", "alice"] + "method": request.method # HTTP verb, e.g. GET, POST, PUT, ... + } +} +# ask OPA for a policy decision +# (in reality OPA URL would be constructed from environment) +rsp = requests.post("http://127.0.0.1:8181/v1/data/httpapi/authz", json=input_dict) +if rsp.json()["allow"]: + # HTTP API allowed +else: + # HTTP API denied +``` + +### 3. Check that `alice` can see her own salary. + +The following command will succeed. + +```shell +curl --user alice:password localhost:5000/finance/salary/alice +``` + +The webserver queries OPA to authorize the request. In the query, the webserver +includes JSON data describing the incoming request. + +```json title="input.json" +{ + "method": "GET", + "path": ["finance", "salary", "alice"], + "user": "alice" +} +``` + + + +When the webserver queries OPA it asks for a specific policy decision. In this +case, the integration is hardcoded to ask for `/v1/data/httpapi/authz`. OPA +translates this URL path into a query: + +```rego +package example + +result := data.httpapi.authz +``` + + + +### 4. Check that `bob` can see `alice`'s salary (because `bob` is `alice`'s manager.) + +```shell +curl --user bob:password localhost:5000/finance/salary/alice +``` + +### 5. Check that `bob` CANNOT see `charlie`'s salary. + +`bob` is not `charlie`'s manager, so the following command will fail. + +```shell +curl --user bob:password localhost:5000/finance/salary/charlie +``` + +### 6. Change the policy. + +Suppose the organization now includes an HR department. The organization wants +members of HR to be able to see any salary. Let's extend the policy to handle +this. + +```rego title="example-hr.rego" +package httpapi.authz + +# Allow HR members to get anyone's salary. +allow if { + input.method == "GET" + input.path = ["finance", "salary", _] + input.user == hr[_] +} + +# David is the only member of HR. +hr := ["david"] +``` + +Build a new bundle with the new policy included. + +```shell +opa build example.rego example-hr.rego +``` + +The updated bundle will automatically be served by the bundle server, but note that it might take up to the +configured `max_delay_seconds` for the new bundle to be downloaded by OPA. If you plan to make frequent policy +changes you might want to adjust this value in `docker-compose.yaml` accordingly. + +For the sake of the tutorial we included `manager_of` and `hr` data directly +inside the policies. In real-world scenarios that information would be imported +from external data sources. + +### 7. Check that the new policy works. + +Check that `david` can see anyone's salary. + +```shell +curl --user david:password localhost:5000/finance/salary/alice +curl --user david:password localhost:5000/finance/salary/bob +curl --user david:password localhost:5000/finance/salary/charlie +curl --user david:password localhost:5000/finance/salary/david +``` + +### 8. (Optional) Use JSON Web Tokens to communicate policy data. + +OPA supports the parsing of JSON Web Tokens via the builtin function `io.jwt.decode`. +To get a sense of one way the subordinate and HR data might be communicated in the +real world, let's try a similar exercise utilizing the JWT utilities of OPA. + +```rego title="example-jwt.rego" +package httpapi.authz + +default allow := false + +# Allow users to get their own salaries. +allow if { + some username + input.method == "GET" + input.path = ["finance", "salary", username] + token.payload.user == username + user_owns_token +} + +# Allow managers to get their subordinate' salaries. +allow if { + some username + input.method == "GET" + input.path = ["finance", "salary", username] + token.payload.subordinates[_] == username + user_owns_token +} + +# Allow HR members to get anyone's salary. +allow if { + input.method == "GET" + input.path = ["finance", "salary", _] + token.payload.hr == true + user_owns_token +} + +# Ensure that the token was issued to the user supplying it. +user_owns_token if input.user == token.payload.azp + +# Helper to get the token payload. +token := {"payload": payload} if { + [header, payload, signature] := io.jwt.decode(input.token) +} +``` + +```json title="input.json" +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM", + "method": "GET", + "path": ["finance", "salary", "alice"], + "user": "alice" +} +``` + +Build a new bundle for the new policy. + +```shell +opa build example-jwt.rego +``` + +For convenience, we'll want to store user tokens in environment variables (they're really long). + +```shell +export ALICE_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM" +export BOB_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYm9iIiwiYXpwIjoiYm9iIiwic3Vib3JkaW5hdGVzIjpbImFsaWNlIl0sImhyIjpmYWxzZX0.n_lXN4H8UXGA_fXTbgWRx8b40GXpAGQHWluiYVI9qf0" +export CHARLIE_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiY2hhcmxpZSIsImF6cCI6ImNoYXJsaWUiLCJzdWJvcmRpbmF0ZXMiOltdLCJociI6ZmFsc2V9.EZd_y_RHUnrCRMuauY7y5a1yiwdUHKRjm9xhVtjNALo" +export BETTY_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYmV0dHkiLCJhenAiOiJiZXR0eSIsInN1Ym9yZGluYXRlcyI6WyJjaGFybGllIl0sImhyIjpmYWxzZX0.TGCS6pTzjrs3nmALSOS7yiLO9Bh9fxzDXEDiq1LIYtE" +export DAVID_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZGF2aWQiLCJhenAiOiJkYXZpZCIsInN1Ym9yZGluYXRlcyI6W10sImhyIjp0cnVlfQ.Q6EiWzU1wx1g6sdWQ1r4bxT1JgSHUpVXpINMqMaUDMU" +``` + +These tokens encode the same information as the policies we did before (`bob` is `alice`'s manager, `betty` is `charlie`'s, `david` is the only HR member, etc). +If you want to inspect their contents, start up the OPA REPL and execute `io.jwt.decode(, [header, payload, signature])` or open the example above in the Playground. + +Let's try a few queries (note: you may need to escape the `?` characters in the queries for your shell): + +Check that `charlie` can't see `bob`'s salary. + +```shell +curl --user charlie:password "localhost:5000/finance/salary/bob?token=$CHARLIE_TOKEN" +``` + +Check that `charlie` can't pretend to be `bob` to see `alice`'s salary. + +```shell +curl --user charlie:password "localhost:5000/finance/salary/alice?token=$BOB_TOKEN" +``` + +Check that `david` can see `betty`'s salary. + +```shell +curl --user david:password "localhost:5000/finance/salary/betty?token=$DAVID_TOKEN" +``` + +Check that `bob` can see `alice`'s salary. + +```shell +curl --user bob:password "localhost:5000/finance/salary/alice?token=$BOB_TOKEN" +``` + +Check that `alice` can see her own salary. + +```shell +curl --user alice:password "localhost:5000/finance/salary/alice?token=$ALICE_TOKEN" +``` + +## Wrap Up + +Congratulations for finishing the tutorial! + +You learned a number of things about API authorization with OPA: + +- OPA gives you fine-grained policy control over APIs once you set up the + server to ask OPA for authorization. +- You write allow/deny policies to control which APIs can be executed by whom. +- You can import external data into OPA and write policies that depend on + that data. +- You can use OPA data structures to define abstractions over your data. +- You can use a remote bundle server for distributing policy and data. + +The code for this tutorial can be found in the +[open-policy-agent/contrib](https://github.com/open-policy-agent/contrib) +repository. diff --git a/third_party/opa/docs/docs/index.md b/third_party/opa/docs/docs/index.md new file mode 100644 index 000000000000..5c4b99991458 --- /dev/null +++ b/third_party/opa/docs/docs/index.md @@ -0,0 +1,1281 @@ +--- +title: "Introduction" +--- + +The Open Policy Agent (OPA, pronounced "oh-pa") is an open source, +general-purpose policy engine that unifies policy enforcement across the stack. +OPA provides a high-level declarative language that lets you specify policy as +code and simple APIs to offload policy decision-making from your software. You +can use OPA to enforce policies in microservices, Kubernetes, CI/CD pipelines, +API gateways, and more. + +OPA was originally created by [Styra](https://www.styra.com) and is proud to be +a graduated project in the [Cloud Native Computing Foundation +(CNCF)](https://www.cncf.io/) landscape. For details read the CNCF +[announcement](https://www.cncf.io/announcements/2021/02/04/cloud-native-computing-foundation-announces-open-policy-agent-graduation/). + +Read this page to learn about the core concepts in OPA's policy language +([Rego](./docs/policy-language)) as well as how to download, run, and integrate OPA. + +## What is OPA? + +OPA [decouples](./docs/philosophy#policy-decoupling) policy decision-making from policy +enforcement. When your software needs to make policy decisions it **queries** +OPA and supplies structured data (e.g., JSON) as input. OPA accepts arbitrary +structured data as input. + +import OverviewDiagram from './assets/OverviewDiagram'; + + + +OPA generates policy decisions by evaluating the query input against +policies and data. OPA and Rego are domain-agnostic so you can describe almost +any kind of invariant in your policies. For example: + +- Which users can access which resources. +- Which subnets egress traffic is allowed to. +- Which clusters a workload must be deployed to. +- Which registries binaries can be downloaded from. +- Which OS capabilities a container can execute with. +- Which times of day the system can be accessed at. + +Policy decisions are not limited to simple yes/no or allow/deny answers. Like +query inputs, your policies can generate arbitrary structured data as output. + +Let's look at an example. Imagine you work for an organization with the following network infrastructure: + +```mermaid +graph + nX["Public Network X"] --> Internet + nY["Private Network Y"] + sX["Server X"] --"Port X"--> nX + sY["Server Y"] --"Port X"--> nX + sY --"Port Y"--> nY + sZ["Server Z"] --"Port Z"--> nY +``` + +There are three kinds of components in the system: + +- Servers expose zero or more protocols (e.g., `http`, `ssh`, etc.) +- Networks connect servers and can be public or private. Public networks are connected to the Internet. +- Ports attach servers to networks. + +All the servers, networks, and ports are provisioned using infrastructure as +code. The infrastructure configuration is specified in this JSON representation: + +```json +{ + "servers": [ + { "id": "app", "protocols": ["https", "ssh"], "ports": ["p1", "p2", "p3"] }, + { "id": "db", "protocols": ["mysql"], "ports": ["p3"] }, + { "id": "cache", "protocols": ["memcache"], "ports": ["p3"] }, + { "id": "ci", "protocols": ["http"], "ports": ["p1", "p2"] }, + { "id": "busybox", "protocols": ["telnet"], "ports": ["p1"] } + ], + "networks": [ + { "id": "net1", "public": false }, + { "id": "net2", "public": false }, + { "id": "net3", "public": true }, + { "id": "net4", "public": true } + ], + "ports": [ + { "id": "p1", "network": "net1" }, + { "id": "p2", "network": "net3" }, + { "id": "p3", "network": "net2" } + ] +} +``` + + + +Your organization has established the following security policy that must be implemented: + +> 1. Servers reachable from the Internet must not expose the insecure 'http' protocol. +> 2. Servers are not allowed to expose the 'telnet' protocol. + +The policy needs to be enforced when servers, networks, and ports are +provisioned and the compliance team wants to periodically audit the system to +find servers that violate the policy. + +Let's explore how OPA can help implement this policy. + +## Writing Policy with Rego + +OPA policies are expressed in a high-level declarative language called Rego. +Rego (pronounced "ray-go") is purpose-built for expressing policies over complex +hierarchical data structures. For detailed information on Rego see the +[Policy Language](./docs/policy-language) documentation. + +:::tip +The examples below are interactive! If you edit the input data above +containing servers, networks, and ports, the output will change below. +Similarly, if you edit the queries or rules in the examples below the output +will change. As you read through this section, try changing the input, queries, +and rules and observe the difference in output. + +They can also be run locally on your machine using the +[`opa eval` command, here are setup instructions.](#running-opa) + +Note that the examples in this section try to represent the best practices. +As such, they make use of keywords that are meant to become standard keywords +at some point in time, but have been introduced gradually. +[See the docs on _future keywords_](./docs/policy-language/#future-keywords) +for more information. +::: + +:::note +This section covers the building blocks of writing policies in Rego. You can see +how these concepts come together to solve our network security policy in the +[Complete Example](#complete-example). +::: + +### Basic Syntax + +To implement our security policy, we first need to access and examine the +infrastructure data. When OPA evaluates policies, it binds data provided in the +query to a global variable called `input`. You can refer to specific parts of +the input data using the `.` (dot) operator. + +```rego +package servers + +output := input.servers +``` + + + +To refer to array elements you can use the familiar square-bracket syntax: + +```rego +package servers + +output := input.servers[0].protocols[0] +``` + + + +:::tip +You can use the same square bracket syntax if keys contain other than +`[a-zA-Z0-9_]`. E.g., `input["foo~bar"]`. +::: + +If you refer to a value that does not exist, OPA returns _undefined_. Undefined +means that OPA was not able to find any results. + +```rego +package servers + +output := input.foobar +``` + + + +The most simple policy decisions are made by writing expressions that perform +logical operations on the input data. For example, we can check if a server has +a specific ID using an equality check with `==`. + +```rego +package servers + +output := input.servers[0].id == "app" +``` + + + +OPA includes a set of [built-in functions](./docs/policy-reference/builtins) you +can use to perform common operations like string manipulation, regular +expression matching, arithmetic, aggregation, and more. + +```rego +package servers + +output := count(input.servers[0].protocols) >= 1 +``` + + + +For queries to produce results, all of the expressions in the query must be true +or defined. You can separate expressions across multiple lines (or optionally +join them with `;` - meaning AND, on a single line): + +```rego +package servers + +output if { + input.servers[0].id == "app" + input.servers[0].protocols[0] == "https" +} +``` + + + +If any of the expressions in the query are not true (or defined) the result is +undefined. In the example below, the second expression is false: + +```rego +package servers + +output if { + input.servers[0].id == "app" + // highlight-next-line + input.servers[0].protocols[0] == "telnet" +} +``` + + + +:::note +Expressions are joined together with AND only when they are in the same rule +body. In this example, the checks against `input.servers` are OR'd since they +are in different rule bodies. See [Logical OR](#logical-or) in the Rules section +below for more detail. + +```rego +output if { + input.servers[0].id == "app" +} + +output if { + input.servers[0].protocols[0] == "telnet" +} +``` + +::: + +You can store values in intermediate variables using the `:=` (assignment) +operator to help make more complex rules easier to read. Variables can be +referenced just like `input` and are, like `input`, immutable. + +```rego +package servers + +output if { + s := input.servers[0] + s.id == "app" + p := s.protocols[0] + p == "https" +} +``` + + + +When OPA evaluates expressions, it finds values for the variables that make all +of the expressions true. If there are no variable assignments that make all of +the expressions true, the result is undefined. + +```rego +package servers + +output if { + s := input.servers[0] + s.id == "app" + s.protocols[1] == "telnet" +} +``` + + + +Like other declarative languages (e.g., SQL), iteration in Rego happens +implicitly when you inject variables into expressions. + +There are explicit iteration constructs to express _FOR ALL_ and _FOR SOME_, [see below](#for-some-and-for-all). + +To understand how iteration works in Rego, imagine you need to check if any +networks are public. Recall that the networks are supplied inside an array: +`[{"id": "net1", "public": false}, {"id": "net2", "public": false}, ...]` + +One option would be to test each network in the input (which is undefined since +networks 1 and 2 are not public). Incremental definitions of a rule are +[OR'd together](#logical-or) so if any are true, the result of the whole rule is +true. + +```rego +package servers + +exists_public_network if input.networks[0].public == true +# or +exists_public_network if input.networks[1].public == true +# or +exists_public_network if input.networks[2].public == true +# or +exists_public_network if input.networks[3].public == true +# ... +``` + + + +**This approach is problematic**. There may be too many networks to list +statically, or more importantly, the number of networks may not be known in +advance. In Rego, the solution is to substitute the array index with a variable. + +```rego +package servers + +exists_public_network if { + some i + input.networks[i].public == true +} +``` + + + +Now the query asks for values of `i` that make the overall expression true. When +you substitute variables in references, OPA automatically finds variable +assignments that satisfy all of the expressions in the query. Just like +intermediate variables, OPA returns the values of the variables. + +You can substitute as many variables as you want. For example, to find out if +any servers expose the insecure `"http"` protocol you could write: + +```rego +package servers + +http_server if { + some i, j + input.servers[i].protocols[j] == "http" +} +``` + + + +If variables appear multiple times the assignments satisfy all of the +expressions. For example, to find the ids of ports connected to public networks, +you could write: + +```rego +package servers + +exposed_ports contains port_id if { + some i, j + port_id := input.ports[i].id + input.ports[i].network == input.networks[j].id + input.networks[j].public +} +``` + + + +Just like references that refer to non-existent fields or expressions that fail +to match, if OPA is unable to find any variable assignments that satisfy all of +the expressions, the result is undefined. + +```rego +package servers + +ssh_server if { + some i + # there is no assignment of i that satisfies the expression + input.servers[i].protocols[i] == "ssh" +} +``` + + + +#### FOR SOME and FOR ALL + +While plain iteration serves as a powerful building block, Rego also features ways +to express _FOR SOME_ and _FOR ALL_ more explicitly. + +##### FOR SOME (`some`) + +`some ... in ...` is used to iterate over the collection (its last argument), +and will bind its variables (key, value position) to the collection items. +It introduces new bindings to the evaluation of the rest of the rule body. + +Using `some`, we can express the rules introduced above in different ways: + +```rego +package servers + +public_network contains net.id if { + some net in input.networks # some network exists and.. + net.public # it is public. +} + +shell_accessible contains server.id if { + some server in input.servers + "telnet" in server.protocols +} + +shell_accessible contains server.id if { + some server in input.servers + "ssh" in server.protocols +} +``` + + + +For details on `some ... in ...`, see +[the documentation of the `in` operator](./docs/policy-language/#membership-and-iteration-in). + +##### FOR ALL (`every`) + +Expanding on the examples above, `every` allows us to succinctly express that +a condition holds for all elements of a domain. + +```json title="Edit the input to add a 'telnet' protocol to a server" +{ + "servers": [ + { + "id": "busybox", + "protocols": ["http", "ftp"] + }, + { + "id": "db", + "protocols": ["mysql", "ssh"] + }, + { + "id": "web", + "protocols": ["https"] + } + ] +} +``` + + + +```rego +package servers + +no_telnet_exposed if { + every server in input.servers { + not "telnet" in server.protocols + } +} +``` + + + +Learn more about the [Every Keyword](./docs/policy-language/#every-keyword). + +### Policy Rules + +Rego lets you encapsulate and re-use logic with rules. Rules are just if-then +logic statements. Rules can either be "complete" or "partial". + +#### Complete Rules + +Complete rules are if-then statements that assign a single value to a variable. +Every rule consists of a _head_ and a _body_. In Rego we say the rule head +is true _if_ the rule body is true for some set of variable assignments. + +```rego +package rules + +# head +exists_public_network := true if { + # body + some net in input.networks # some network exists and.. + net.public # it is public. +} +``` + + + +You can query for the value generated by rules just like any other value (such as `input` or your own variables): + +```rego +package rules + +exists_public_network := true if { + some net in input.networks # some network exists and.. + net.public # it is public. +} + +another_rule := { + "public_networks": exists_public_network, +} +``` + + + +All values generated by rules can be queried via the global `data` variable from +other packages loaded into OPA. + +```rego +package another_package + +yet_another_rule := { + "public_networks": data.rules.exists_public_network, +} +``` + + + +:::tip +You can query the value of any rule loaded into OPA by referring to it with an +absolute path. The path of a rule is always: +`data..`. +::: + +If you omit the `= ` part of the rule head the value defaults to `true`. +You could rewrite the example above as follows without changing the meaning: + +```rego +exists_public_network if { + some net in input.networks + net.public +} +``` + +To define constants, omit the rule body. When you omit the rule body it defaults +to `true`. Since the rule body is true, the rule head is always true/defined. + +```rego +package servers + +max_allowed_protocols := 5 +``` + + + +Constants defined like this can be queried just like any other values: + +```rego +count(input.servers[0].protocols) < max_allowed_protocols +``` + +If OPA cannot find variable assignments that satisfy the rule body, we say that +the rule is undefined. For example, if the `input` provided to OPA does not +include a public network then `exists_public_network` will be undefined (which is +not the same as false.) Below, OPA is given a different set of input networks +(none of which are public): + +```json +{ + "networks": [ + { "id": "n1", "public": false }, + { "id": "n2", "public": false } + ] +} +``` + + + +```rego +package rules + +exists_public_network if { + some net in input.networks + net.public +} +``` + + + +#### Partial Rules + +Partial rules are if-then statements that generate a set of values and +assign that set to a variable. In the example below `public_network contains net.id` is the rule head and +`some net in input.networks; net.public` is the rule body. You can query for the entire +set of values just like any other value. + +```rego +package example + +# head +public_network contains net.id if { + # body + some net in input.networks # some network exists and.. + net.public # it is public. +} +``` + + + +Using the `in` keyword we can use this list to test if some other value is in +the set defined by `public_network`: + +```rego +package example + +allow if "net3" in public_network +``` + + + +You can also iterate over the set of values by referencing the set elements with a +variable: + +```rego +package example + +allow if { + some net in public_network + net == "net3" +} +``` + + + +In addition to partially defining sets, You can also partially define key/value +pairs (aka objects). See +[Rules](https://www.openpolicyagent.org/docs/latest/policy-language/#rules) in +the language guide for more information. + +#### Logical OR + +When you join multiple expressions together in a query you are expressing +logical AND. To express logical OR in Rego you define multiple rules with the +same name. Let's look at an example. + +Imagine you wanted to know if any servers expose protocols that give clients +shell access. To determine this you could define a complete rule that declares +`shell_accessible` to be `true` if any servers expose the `"telnet"` or `"ssh"` +protocols: + +```json title="Input with telnet and ssh" +{ + "servers": [ + { + "id": "busybox", + "protocols": ["http", "telnet"] + }, + { + "id": "db", + "protocols": ["mysql", "ssh"] + }, + { + "id": "web", + "protocols": ["https"] + } + ] +} +``` + + + +```rego +package example.logical_or + +default shell_accessible := false + +shell_accessible if { + input.servers[_].protocols[_] == "telnet" +} + +shell_accessible if { + input.servers[_].protocols[_] == "ssh" +} +``` + + + +:::tip +The `default` keyword tells OPA to assign a value to the variable if all of +the other rules with the same name are undefined. +::: + +When you use logical OR with partial rules, each rule definition contributes +to the set of values assigned to the variable. For example, the example above +could be modified to generate a set of servers that expose `"telnet"` or +`"ssh"`. + +```rego +package example.logical_or + +shell_accessible contains server.id if { + server := input.servers[_] + server.protocols[_] == "telnet" +} + +shell_accessible contains server.id if { + server := input.servers[_] + server.protocols[_] == "ssh" +} +``` + + + +:::tip +Check out this +[blog post](https://www.styra.com/blog/how-to-express-or-in-rego/) +that goes into much more detail on this topic showing different methods to +express OR in idiomatic Rego for different use cases. +::: + + + +### Complete Example + +The sections above explain the core concepts in Rego. To put it all together +let's review the desired policy in natural language: + +> 1. Servers reachable from the Internet must not expose the insecure 'http' protocol. +> 2. Servers are not allowed to expose the 'telnet' protocol. + +At a high-level the policy needs to identify servers that violate some +conditions. To implement this policy we could define rules called `violation` +that generate a set of servers that are in violation. For example: + +```rego +package example + +violation contains message if { # a server is in the violation set if... + some server in public_servers # it exists in the 'public_servers' set and... + "http" in server.protocols # it contains the insecure "http" protocol. + message := sprintf("server %s exposes http", [server.id]) +} + +violation contains message if { # a server is in the violation set if... + some server in input.servers # it exists in the input.servers collection and... + "telnet" in server.protocols # it contains the "telnet" protocol. + message := sprintf("server %s exposes telnet", [server.id]) +} + +public_servers contains server if { # a server exists in the public_servers set if... + some server in input.servers # it exists in the input.servers collection and... + + some port in server.ports # it references a port in the input.ports collection and... + some input_port in input.ports + port == input_port.id + + # the port references a network in the input.networks collection and... + some input_network in input.networks + input_port.network == input_network.id + + # the network is public. + input_network.public +} +``` + + + +This example demonstrates how we can use Rego to create a clear list of policy +violations that can be handed back to the infrastructure as code system to +present to the user, making it easy for them to see what's gone wrong. + +## Running OPA + +This section explains how you can query OPA directly and interact with it on +your own machine. If you just want to quickly get a feel for the language +without installing anything, check out the +[OPA Playground](https://play.openpolicyagent.org/). + +### 1. Download OPA + + + + OPA binaries can be installed on macOS using Homebrew. The formula can be + reviewed on [brew.sh](https://formulae.brew.sh/formula/opa). This method + supports both ARM64 and AMD64 architectures. + ```shell + brew install opa + ``` + +It's also possible to download the OPA binary directly: + + + + ```shell + curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_darwin_arm64_static + ``` + + + ```shell + curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_darwin_amd64 + ``` + + + +After downloading the OPA binary, you must ensure it's executable: + +```shell +chmod 755 ./opa +``` + +It's also recommended to move the OPA binary into a directory in your +`PATH` so you can run OPA commands in different directories. + +You can verify the installation by running: + +```shell +opa version +``` + + + + +There are a number of packages repos that provide OPA binaries for Linux/Unix. +For example: + +- [Arch](https://archlinux.org/packages/extra/x86_64/open-policy-agent/) +- [nixpkgs](https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/op/open-policy-agent/package.nix) +- [Wolfi](https://github.com/wolfi-dev/os/blob/main/opa.yaml) +- [FreeBSD](https://cgit.freebsd.org/ports/tree/sysutils/opa) +- [NetBSD](https://pkgsrc.se/devel/opa) + +In order to manually install the OPA binary from the GitHub release assets, +please run the following: + + + + ```shell + curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_arm64_static + ``` + + + ```shell + curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64 + ``` + + +After downloading the OPA binary, you must ensure it's executable: +```shell +chmod 755 ./opa +``` +It's also recommended to move the OPA binary into a directory in your +`PATH` so you can run OPA commands in any directory. + +You can verify the installation by running: + +```shell +opa version +``` + + + + +Download the Windows binary using PowerShell: + +```powershell +Invoke-WebRequest -Uri "https://openpolicyagent.org/downloads/latest/opa_windows_amd64.exe" -OutFile "opa.exe" +``` + +Or using curl (if available): + +```cmd +curl -L -o opa.exe https://openpolicyagent.org/downloads/latest/opa_windows_amd64.exe +``` + +Add the OPA binary to your PATH by creating a Tools directory for it: + +```cmd +mkdir C:\Tools\OPA +move opa.exe C:\Tools\OPA\ +``` + +Now we can add this to our `PATH`: + +Control Panel → System → Advanced system settings → Environment Variables + +Edit the Path variable → Add: `C:\Tools\OPA` + +Alternatively, run: + +```powershell +[Environment]::SetEnvironmentVariable("Path", "$env:Path;C:\Tools\OPA", "User") +``` + +You can verify the installation by running: + +```cmd +opa version +``` + + + +You can also download and run OPA via Docker. The latest stable image tag is +`openpolicyagent/opa:latest`. + +You can verify the installation by running: + +```shell +docker run --rm -it openpolicyagent/opa:latest version +``` + + + + +See all available binaries on the +[GitHub releases](https://github.com/open-policy-agent/opa/releases). +Checksums for all binaries are available in the download path by appending +`.sha256` to the binary filename. + +For example, verify the macOS arm64 binary checksum: + +```shell +BINARY_NAME=opa_darwin_arm64_static +curl -L -o opa_darwin_amd64 https://openpolicyagent.org/downloads/latest/$BINARY_NAME +curl -L -o opa_darwin_amd64.sha256 https://openpolicyagent.org/downloads/latest/$BINARY_NAME.sha256 +shasum -c $BINARY_NAME.sha256 +``` + +### 2. Try `opa eval` + +The simplest way to interact with OPA is via the command-line using the [`opa eval` sub-command](./docs/cli/#opa-eval). +It is a swiss-army knife that you can use to evaluate arbitrary Rego expressions and policies. +`opa eval` supports a large number of options for controlling evaluation. +Commonly used flags include: + +| Flag | Short | Description | +| ---------------- | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `--bundle` | `-b` | Load a [bundle file](./docs/management-bundles/#bundle-file-format) or directory into OPA. This flag can be repeated. | +| `--data` | `-d` | Load policy or data files into OPA. This flag can be repeated. | +| `--input` | `-i` | Load a data file and use it as `input`. This flag cannot be repeated. | +| `--format` | `-f` | Set the output format to use. The default is `json` and is intended for programmatic use. The `pretty` format emits more human-readable output. | +| `--fail` | n/a | Exit with a non-zero exit code if the query is undefined. | +| `--fail-defined` | n/a | Exit with a non-zero exit code if the query is not undefined. | + +For example: + +```json title="input.json" +{ + "servers": [ + { "id": "app", "protocols": ["https", "ssh"], "ports": ["p1", "p2", "p3"] }, + { "id": "db", "protocols": ["mysql"], "ports": ["p3"] }, + { "id": "cache", "protocols": ["memcache"], "ports": ["p3"] }, + { "id": "ci", "protocols": ["http"], "ports": ["p1", "p2"] }, + { "id": "busybox", "protocols": ["telnet"], "ports": ["p1"] } + ], + "networks": [ + { "id": "net1", "public": false }, + { "id": "net2", "public": false }, + { "id": "net3", "public": true }, + { "id": "net4", "public": true } + ], + "ports": [ + { "id": "p1", "network": "net1" }, + { "id": "p2", "network": "net3" }, + { "id": "p3", "network": "net2" } + ] +} +``` + +```rego title="example.rego" +package example + +default allow := false # unless otherwise defined, allow is false + +allow if { # allow is true if... + count(violation) == 0 # there are zero violations. +} + +violation contains server.id if { # a server is in the violation set if... + some server + public_servers[server] # it exists in the 'public_servers' set and... + server.protocols[_] == "http" # it contains the insecure "http" protocol. +} + +violation contains server.id if { # a server is in the violation set if... + server := input.servers[_] # it exists in the input.servers collection and... + server.protocols[_] == "telnet" # it contains the "telnet" protocol. +} + +public_servers contains server if { # a server exists in the 'public_servers' set if... + some i, j + server := input.servers[_] # it exists in the input.servers collection and... + server.ports[_] == input.ports[i].id # it references a port in the input.ports collection and... + input.ports[i].network == input.networks[j].id # the port references a network in the input.networks collection and... + input.networks[j].public # the network is public. +} +``` + +```bash +# Evaluate a trivial expression. +./opa eval "1*2+3" + +# Evaluate a policy on the command line. +./opa eval -i input.json -d example.rego "data.example.violation[x]" + +# Evaluate a policy on the command line and use the exit code. +./opa eval --fail-defined -i input.json -d example.rego "data.example.violation[x]" +echo $? +``` + +### 3. Try `opa run` (interactive) + +OPA includes an interactive shell or REPL (Read-Eval-Print-Loop) accessible via +the [`opa run` sub-command](./docs/cli/#opa-run). +You can use the REPL to experiment with policies and prototype new ones. + +To start the REPL just: + +```bash +./opa run +``` + +When you enter statements in the REPL, OPA evaluates them and prints the result. + +```rego +> true +true +> 3.14 +3.14 +> ["hello", "world"] +[ + "hello", + "world" +] +``` + +Most REPLs let you define variables that you can reference later on. OPA allows +you to do something similar. For example, you can define a `pi` constant as +follows: + +```rego +> pi := 3.14 +``` + +Once `pi` is defined, you query for the value and write expressions in terms of +it: + +```rego +> pi +3.14 +> pi > 3 +true +``` + +Quit out of the REPL by pressing Control-D or typing `exit`: + +```rego +> exit +``` + +You can load policy and data files into the REPL by passing them on the command +line. By default, JSON and YAML files are rooted under `data`. + +```shell +opa run input.json +``` + +Run a few queries to poke around the data: + +```rego +> data.servers[0].protocols[1] +``` + +```rego +> data.servers[i].protocols[j] +``` + +```rego +> net := data.networks[_]; net.public +``` + +To set a data file as the `input` document in the REPL prefix the file path: + +```shell +opa run example.rego repl.input:input.json +``` + +```rego +> data.example.public_servers[s] +``` + +:::info +Prefixing file paths with a reference controls where file is loaded under +`data`. By convention, the REPL sets the `input` document that queries see by +reading `data.repl.input` each time a statement is evaluated. See `help input` +for details in the REPL. +::: + +Quit out of the REPL by pressing Control-D or typing `exit`: + +```rego +> exit +``` + +### 4. Try `opa run` (server) + +To integrate with OPA you can run it as a server and execute queries over HTTP. +You can start OPA as a server with `-s` or `--server`: + +```bash +./opa run --server ./example.rego +``` + +By default OPA listens for HTTP connections on `localhost:8181`. See `opa run --help` for a list of options to change the listening address, enable TLS, and +more. + +Inside of another terminal use `curl` (or a similar tool) to access OPA's HTTP +API. When you query the `/v1/data` HTTP API you must wrap input data inside of a +JSON object: + +```json +{ + "input": +} +``` + +Create a copy the input file for sending via `curl`: + +``` +cat < v1-data-input.json +{ + "input": $(cat input.json) +} +EOF +``` + +Execute a few `curl` requests and inspect the output: + +```bash +curl localhost:8181/v1/data/example/violation -d @v1-data-input.json -H 'Content-Type: application/json' +curl localhost:8181/v1/data/example/allow -d @v1-data-input.json -H 'Content-Type: application/json' +``` + +By default `data.system.main` is used to serve policy queries without a path. +When you execute queries without providing a path, you do not have to wrap the +input. If the `data.system.main` decision is undefined it is treated as an +error: + +```bash +curl localhost:8181 -i -d @input.json -H 'Content-Type: application/json' +``` + +You can restart OPA and configure to use any decision as the default decision: + +``` +./opa run --server --set=default_decision=example/allow ./example.rego +``` + +Re-run the last `curl` command from above: + +```bash +curl localhost:8181 -i -d @input.json -H 'Content-Type: application/json' +``` + +### 5. Try OPA as a Go library + +OPA can be embedded inside Go programs as a library. The simplest way to embed +OPA as a library is to import the `github.com/open-policy-agent/opa/rego` +package. + +```go +import "github.com/open-policy-agent/opa/rego" +``` + +Call the `rego.New` function to create an object that can be prepared or +evaluated: + +```go +r := rego.New( + rego.Query("x = data.example.allow"), + rego.Load([]string{"./example.rego"}, nil)) +``` + +The `rego.Rego` supports several options that let you customize evaluation. See +the [GoDoc](https://godoc.org/github.com/open-policy-agent/opa/rego) page for +details. After constructing a new `rego.Rego` object you can call +`PrepareForEval()` to obtain an executable query. If `PrepareForEval()` fails it +indicates one of the options passed to the `rego.New()` call was invalid (e.g., +parse error, compile error, etc.) + +```go +ctx := context.Background() +query, err := r.PrepareForEval(ctx) +if err != nil { + // handle error +} +``` + +The prepared query object can be cached in-memory, shared across multiple +goroutines, and invoked repeatedly with different inputs. Call `Eval()` to +execute the prepared query. + +```go +bs, err := ioutil.ReadFile("./input.json") +if err != nil { + // handle error +} + +var input interface{} + +if err := json.Unmarshal(bs, &input); err != nil { + // handle error +} + +rs, err := query.Eval(ctx, rego.EvalInput(input)) +if err != nil { + // handle error +} +``` + +The policy decision is contained in the results returned by the `Eval()` call. +You can inspect the decision and handle it accordingly: + +```go +// In this example we expect a single result (stored in the variable 'x'). +fmt.Println("Result:", rs[0].Bindings["x"]) +``` + +You can combine the steps above into a simple command-line program that +evaluates policies and outputs the result: + +```go title="main.go" +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + "os" + + "github.com/open-policy-agent/opa/v1/rego" +) + +func main() { + ctx := context.Background() + + // Construct a Rego object that can be prepared or evaluated. + r := rego.New( + rego.Query(os.Args[2]), + rego.Load([]string{os.Args[1]}, nil)) + + // Create a prepared query that can be evaluated. + query, err := r.PrepareForEval(ctx) + if err != nil { + log.Fatal(err) + } + + // Load the input document from stdin. + var input interface{} + dec := json.NewDecoder(os.Stdin) + dec.UseNumber() + if err := dec.Decode(&input); err != nil { + log.Fatal(err) + } + + // Execute the prepared query. + rs, err := query.Eval(ctx, rego.EvalInput(input)) + if err != nil { + log.Fatal(err) + } + + // Do something with the result. + fmt.Println(rs) +} +``` + +Run the code above as follows: + +```shell +go run main.go example.rego 'data.example.violation' < input.json +``` + +## Next Steps + +Congratulations on completing the introduction to OPA. You have learned the core +concepts behind OPA's policy language as well as how to get OPA and run it on +your own. + +If you have more questions about how to write policies in Rego check out: + +- The [Policy Reference](./docs/policy-reference) page for reference documentation on built-in functions. +- The [Policy Language](./docs/policy-language) page for complete descriptions of all language features. + +If you want to try OPA for a specific use case check out: + +- The [Ecosystem](./ecosystem) page which showcases various of OPA integrations. + +Some popular tutorials include: + +- The [Kubernetes](./docs/kubernetes) page for how to use OPA as an admission controller in Kubernetes. +- The [Envoy](./docs/envoy) page for how to use OPA as an external authorizer with Envoy. +- The [Terraform](./docs/terraform) page for how to use OPA to validate Terraform plans. + +Don't forget to install the OPA (Rego) Plugin for your favorite +[IDE or Text Editor](./docs/editor-and-ide-support). diff --git a/third_party/opa/docs/docs/integration.md b/third_party/opa/docs/docs/integration.md new file mode 100644 index 000000000000..5d65ccf4d621 --- /dev/null +++ b/third_party/opa/docs/docs/integration.md @@ -0,0 +1,484 @@ +--- +title: Integrating OPA +sidebar_position: 7 +--- + +OPA exposes domain-agnostic APIs that your service can call to manage and +enforce policies. Read this page if you want to integrate an application, +service, or tool with OPA. + +When integrating with OPA there are two interfaces to consider: + +- **Evaluation**: OPA's interface for asking for policy decisions. Integrating OPA is primarily focused on integrating an application, service, or tool with OPA's policy evaluation interface. This integration results in policy decisions being decoupled from that application, service, or tool. +- **Management**: OPA's interface for deploying policies, understanding status, uploading logs, and so on. This integration is typically the same across all OPA instances, regardless what software the evaluation interface is integrated with. Distributing policy, retrieving status, and storing logs in the same way across all OPAs provides a unified management plane for policy across many different software systems. + +This page focuses predominantly on different ways to integrate with OPA's policy evaluation interface and how they compare. For more information about the management interface: + +- See the [Bundle API](./management-bundles) for distributing policy and data to OPA. +- See the [Status API](./management-status) for collecting status reports on bundle activation and agent health. +- See the [Decision Log API](./management-decision-logs) for collecting a log of policy decisions made by agents. +- See the [Health API](./rest-api#health-api) for checking agent deployment readiness and health. +- See the [Prometheus API endpoint](./monitoring/#prometheus) to obtain insight into performance and errors. + +## Evaluating Policies + +OPA supports different ways to evaluate policies. + +- The [REST API](./rest-api) returns decisions as JSON over HTTP. + - Also see the [Language SDKs](/ecosystem/#languages) for working with the REST API in different languages. +- The [Go API (GoDoc)](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/rego) returns + decisions as simple Go types (`bool`, `string`, `map[string]interface{}`, + etc.) +- [WebAssembly](./wasm) compiles Rego policies into Wasm instructions so they can be embedded and evaluated by any WebAssembly runtime +- Custom compilers and evaluators may be written to parse evaluation plans in the low-level + [Intermediate Representation](./ir) format, which can be emitted by the `opa build` command +- The [SDK](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/sdk) provides high-level APIs for obtaining the output + of query evaluation as simple Go types (`bool`, `string`, `map[string]interface{}`, etc.) + +### Integrating with the REST API + +To integrate with OPA outside of Go, we recommend you deploy OPA as a host-level +daemon or sidecar container. Running OPA locally on the same host as your +application or service helps ensure policy decisions are fast and highly-available. + +When your application or service needs to make policy decisions it can query OPA +locally via HTTP. While it's possible to call OPA's [REST API](./rest-api) directly, +you can also find a number of [native language REST SDKs](/ecosystem/#languages) +which make the integration easier. + +#### Named Policy Decisions + +Use the [Data API](./rest-api#data-api) to query OPA for _named_ policy decisions: + +```http +POST /v1/data/ +Content-Type: application/json +``` + +```json +{ + "input": +} +``` + +The `` in the HTTP request identifies the policy decision to ask for. In +OPA, every rule generates a policy decision. In the example below there are two +decisions: `example/authz/allow` and `example/authz/is_admin`. + +```rego +package example.authz + +default allow := false + +allow if { + input.method == "GET" + input.path == ["salary", input.subject.user] +} + +allow if is_admin + +is_admin if "admin" in input.subject.groups +``` + +You can request specific decisions by querying for `/`. +For example to request the `allow` decision execute the following HTTP request: + +```http +POST /v1/data/example/authz/allow +Content-Type: application/json +``` + +```json +{ + "input": +} +``` + +The body of the request specifies the value of the `input` document to use +during policy evaluation. For example: + +```http +POST /v1/data/example/authz/allow +Content-Type: application/json +``` + +```json +{ + "input": { + "method": "GET", + "path": ["salary", "bob"], + "subject": { + "user": "bob" + } + } +} +``` + +OPA returns an HTTP 200 response code if the policy was evaluated successfully. +Non-HTTP 200 response codes indicate configuration or runtime errors. The policy +decision is contained in the `"result"` key of the response message body. For +example, the above request returns the following response: + +```http +200 OK +Content-Type: application/json +``` + +```json +{ + "result": true +} +``` + +If the requested policy decision is _undefined_ OPA returns an HTTP 200 response +without the `"result"` key. For example, the following request for `is_admin` is +undefined because there is no default value for `is_admin` and the input does +not satisfy the `is_admin` rule body: + +```http +POST /v1/data/example/authz/is_admin +Content-Type: application/json +``` + +```json +{ + "input": { + "subject": { + "user": "bob", + "groups": ["sales", "marketing"] + } + } +} +``` + +The response: + +```http +200 OK +Content-Type: application/json +``` + +```json +{} +``` + +For another example of how to integrate with OPA via HTTP see the [HTTP API Authorization](./http-api-authorization) tutorial. +The [reference documentation](./rest-api) is also a good place to start. + +#### Ecosystem Projects + +The REST API is a common way to build integrations with OPA in distributed systems. +Browse the OPA Ecosystem for +examples on REST API integrations for inspiration. + +### Integrating with the Go SDK + +:::info +This section documents the v1 SDK package. +Please see [v0 Backwards Compatibility](./v0-compatibility) for notes on using +the v0 SDK package. +::: + +The [SDK](https://pkg.go.dev/github.com/open-policy-agent/opa/sdk) package contains high-level APIs for embedding OPA +inside of Go programs and obtaining the output of query evaluation. To get started, import the `sdk` package: + +```go +import "github.com/open-policy-agent/opa/v1/sdk" +``` + +A typical workflow when using the `sdk` package would involve first creating a new `sdk.OPA` object by calling +`sdk.New` and then invoking its `Decision` method to fetch the policy decision. The `sdk.New` call takes the +`sdk.Options` object as an input which allows specifying the OPA configuration, console logger, plugins, etc. + +Here is an example that shows this process: + +```go +package main + +import ( + "bytes" + "context" + "fmt" + + "github.com/open-policy-agent/opa/v1/sdk" + sdktest "github.com/open-policy-agent/opa/v1/sdk/test" +) + +func main() { + ctx := context.Background() + + // create a mock HTTP bundle server + server, err := sdktest.NewServer(sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "example.rego": ` + package authz + + default allow := false + + allow if input.open == "sesame" + `, + })) + if err != nil { + // handle error. + } + + defer server.Stop() + + // provide the OPA configuration which specifies + // fetching policy bundles from the mock server + // and logging decisions locally to the console + config := []byte(fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL())) + + // create an instance of the OPA object + opa, err := sdk.New(ctx, sdk.Options{ + ID: "opa-test-1", + Config: bytes.NewReader(config), + }) + if err != nil { + // handle error. + } + + defer opa.Stop(ctx) + + // get the named policy decision for the specified input + if result, err := opa.Decision(ctx, sdk.DecisionOptions{Path: "/authz/allow", Input: map[string]interface{}{"open": "sesame"}}); err != nil { + // handle error. + } else if decision, ok := result.Result.(bool); !ok || !decision { + // handle error. + } +} +``` + +If you executed this code, the output (i.e. [Decision Log](https://www.openpolicyagent.org/docs/latest/management-decision-logs/) event) +would be logged to the console by default. + +Setting an `ID` in `sdk.Options` is optional, but recommended. If you do not set an `ID`, a random one will be created +for the system. While this is fine for testing, it makes it difficult to monitor the system over time, as a new ID will +be created each time the SDK is initialized, such as when the process is restarted. + +#### Manually Triggering Bundle Reloads + +Users of the SDK can +[manually trigger](./configuration/#bundles) +the SDK's Bundle plugin to load new bundles immediately based on external +events. When doing so, it's recommended to set `bundles[_].trigger` to `manual` +if you want to disable periodic bundle polling. + +In this short example, the `bundle` plugin is loaded from the SDK instance and +triggered to check for new bundles. Do this sparingly, it is not intended to be +used as a replacement for periodic bundle polling. For best performance, only +trigger the bundle plugin when you know that new bundles are available. + +```go +options := sdk.Options{ + Config: bytes.NewReader(config), + Logger: logger, + Ready: make(chan struct{}), // <-- needed or else sdk.New will block +} + +opa, err := sdk.New(ctx, options) +if err != nil { + log.Fatal(err) +} +defer opa.Stop(ctx) + +bundle, ok := opa.Plugin("bundle").(*bundle.Plugin) +if !ok { + log.Fatal("bundle plugin not found") +} + +err = bundle.Trigger(ctx) +if err != nil { + log.Fatal(err) +} +``` + +### Integrating with the Go API + +Use the low-level +[github.com/open-policy-agent/opa/v1/rego](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/rego) +package to embed OPA as a library inside services written in Go, when only policy **evaluation** — and +no other capabilities of OPA, like the management features — are desired. If you're unsure which one to +use, the SDK is probably the better option. + +To get started import the `rego` package: + +```go +import "github.com/open-policy-agent/opa/v1/rego" +``` + +The `rego` package exposes different options for customizing how policies are +evaluated. Through the `rego` package you can supply policies and data, enable +metrics and tracing, toggle optimizations, etc. In most cases you will: + +1. Use the `rego` package to construct a prepared query. +2. Execute the prepared query to produce policy decisions. +3. Interpret and enforce the policy decisions. + +Preparing queries in advance avoids parsing and compiling the policies on each +query and improves performance considerably. Prepared queries are safe to share +across multiple Go routines. + +To prepare a query create a new `rego.Rego` object by calling `rego.New(...)` +and then invoke `rego.Rego#PrepareForEval`. The `rego.New(...)` call can be +parameterized with different options like the query, policy module(s), data +store, etc. + +```go +module := ` +package example.authz + +default allow := false + +allow if { + input.method == "GET" + input.path == ["salary", input.subject.user] +} + +allow if is_admin + +is_admin if "admin" in input.subject.groups +` + +ctx := context.TODO() + +query, err := rego.New( + rego.Query("x = data.example.authz.allow"), + rego.Module("example.rego", module), + ).PrepareForEval(ctx) + +if err != nil { + // Handle error. +} +``` + +Using the `query` returned by `rego.Rego#PrepareForEval` call the `Eval` +function to evaluate the policy: + +```go +input := map[string]interface{}{ + "method": "GET", + "path": []interface{}{"salary", "bob"}, + "subject": map[string]interface{}{ + "user": "bob", + "groups": []interface{}{"sales", "marketing"}, + }, +} + +results, err := query.Eval(ctx, rego.EvalInput(input)) +``` + +The `rego.PreparedEvalQuery#Eval` function returns a _result set_ that contains +the query results. If the result set is empty it indicates the query could not +be satisfied. Each element in the result set contains a set of _variable +bindings_ and a set of expression values. The query from above includes a single +variable `x` so we can lookup the value and interpret it to enforce the policy +decision. + +```go +if err != nil { + // Handle evaluation error. +} else if len(results) == 0 { + // Handle undefined result. +} else if result, ok := results[0].Bindings["x"].(bool); !ok { + // Handle unexpected result type. +} else { + // Handle result/decision. + // fmt.Printf("%+v", results) => [{Expressions:[true] Bindings:map[x:true]}] +} +``` + +For the common case of policies evaluating to a single boolean value, there's +a helper method: With `results.Allowed()`, the previous snippet can be shortened +to + +```go +results, err := query.Eval(ctx, rego.EvalInput(input)) +if err != nil { + // handle error +} +if !results.Allowed() { + // handle result +} +``` + +For more examples of embedding OPA as a library see the +[`rego`](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/rego#pkg-examples) +package in the Go documentation. + +:::info +This section documents the v1 Rego package. +Please see [v0 Backwards Compatibility](./v0-compatibility) for notes on using +the v0 Rego package. +::: + +#### Ecosystem Projects + +The Go API is made available to allow other projects to build policy functionality into their +applications. Browse Go +integrations in the OPA Ecosystem for inspiration. + +### WebAssembly (Wasm) + +Policies can be evaluated as compiled Wasm binaries. See [OPA Wasm docs](./wasm) for more details. + +There are a number of projects already built on OPA's Wasm support. Take a look +in Wasm +integrations in the OPA Ecosystem for more details. + +### Intermediate Representation (IR) + +Policies may be compiled into evaluation plans using an intermediate representation format, suitable for custom +compilers and evaluators. + +See [OPA IR docs](./ir) for more details. + +## Comparison + +A comparison of the different integration choices are summarized below. + +| Dimension | REST API | Go Lib | Wasm | +| ---------- | --------------- | -------------------------- | -------------------------- | +| Evaluation | Fast | Faster | Fastest | +| Language | Any | Only Go | Any with Wasm | +| Operations | Update just OPA | Update entire service | Update service rarely | +| Security | Must secure API | Enable only what is needed | Enable only what is needed | + +Integrating OPA via the REST API is the most common, at the time of writing. OPA +is most often deployed either as a sidecar or less commonly as an external +service. Operationally this makes it easy to upgrade OPA and to configure it to +use its management services (bundles, status, decision logs, etc.). Because it +is a separate process it requires monitoring and logging (though this happens +automatically for any sidecar-aware environment like Kubernetes). OPA's +configuration and APIs must be secured according to the [security guide](./security). + +Integrating OPA via the Go API only works for Go software. Updates to OPA +require re-vendoring and re-deploying the software. Evaluation has less overhead +than the REST API because all the communication happens in the same +operating-system process. All of the management functionality (bundles, decision +logs, etc.) must be either enabled or implemented. Security concerns are limited +to those management features that are enabled or implemented. + +Wasm policies are embeddable in any programming language that has a Wasm +runtime. Evaluation has less overhead than the REST API (because it is evaluated +in the same operating-system process) and should outperform the Go API (because +the policies have been compiled to a lower-level instruction set). Each +programming language will need its own SDKs that implement the management +functionality and the evaluation interface. Typically new OPA language features +will not require updating the service since neither the Wasm runtime nor the +SDKs will be impacted. Updating the SDKs will require re-deploying the service. +Security is analogous to the Go API integration: it is mainly the management +functionality that presents security risks. diff --git a/third_party/opa/docs/docs/ir.md b/third_party/opa/docs/docs/ir.md new file mode 100644 index 000000000000..65504114bb80 --- /dev/null +++ b/third_party/opa/docs/docs/ir.md @@ -0,0 +1,461 @@ +--- +title: Intermediate Representation (IR) +sidebar_position: 12 +--- + +# Overview + +OPA can compile policy queries into planned evaluation paths suitable for +further compilation or interpretation. This document explains the structure and +semantics of the intermediate representation (IR) used to represent these +planned evaluation paths. Read this document if you want to write a compiler or +interpreter for Rego. + +# Structure + +This section explains the structure of policies compiled into the IR. + +## Policy + +The root object emitted by the compiler is a `Policy` and contains the following +top-level keys: + +- `static` is an object containing static data used by the compiled plans and + functions. +- `plans` is an object containing entrypoints to compiled evaluation paths. +- `funcs` is an object containing functions supporting the compiled evaluation + paths. + +## Static + +The `Static` object contains static data required by the plans and functions. +The static object also contains metadata that does not affect the semantics of +the policy. The static object contains the following top-level keys: + +- `strings` is an array of string constants referenced by compiled statements in + the plans and functions. +- `builtin_funcs` is an array of function declarations representing built-in + functions required by the compiled statements. +- `files` is used for debugging purposes only. It is an array of filenames that + were used during compilation. + +### Strings + +The `Strings` array is a collection of string objects referenced by compiled +statements in the policy. Strings are referenced by their index in the +collection. Each string object contains the following fields: + +- `value` is the string constant value. The string may be any valid JSON string. + +### Built-in Functions + +The `Built-in Functions` array is a collection of built-in function +declarations. Each declaration represents a function that must be provided by +the environment where the policy is eventually executed. Each built-in function +contains the following fields: + +- `name` is the name of the function that must be provided. +- `decl` is the type definition of the function. + +### Files + +The `Files` array is a collection of static strings representing names of source +files used during compilation. Filenames are referred to by their index in the +files array. + +## Plans + +The `Plans` object contains a collection of planned evaluation paths +representing entrypoints to the policy. When users compile policies they supply +the queries to expose as entrypoints. Each plan contains the following fields: + +- `name` is the entrypoint identifier, typically set to the path of the policy + decision (e.g., `authz/allow`). +- `blocks` is a collection of [`Block`](#blocks) objects representing the + compiled statements that define the entrypoint. + +## Functions + +The `Functions` object contains a collection of function definitions that +represent functions supporting the plans. Functions can be invoked by name +inside of plans and other functions. Each function contains the following +fields: + +- `name` is the function identifier referenced by call statements. +- `path` is the function identifier referenced by dynamic call statements. +- `params` is an ordered list of local variable identifiers representing + function parameters. The parameters can be referenced inside of the blocks + that define the function. +- `return` is the local variable containing the return value of the function. +- `blocks` is collection of [`Block`](#blocks) objects representing the compiled + statements that define the function. + +## Blocks + +The `Block` object contains a sequence of [Statements](#statements) that must be +executed in order until a statement terminating block execution is encountered +or the end of the block is reached. Each block contains the following fields: + +- `stmts` is an array of `Statement` objects. + +## Statements + +The `Statement` object represents an operation performed by the policy (e.g., +function invocation, lookup, iteration, comparison, etc.) The structure is +specific to each statement type but every statement contains the following +fields: + +- `type` is a string value that identifies the type of the statement. +- `stmt` is an object containing statement-specific fields. +- `file` is the index of source filename where this statement originated. +- `row` is the row in the source file where this statement originated. +- `col` is the column in the source file where this statement originated. + +See the [Statement Definitions](#statement-definitions) section for an +explanation of the supported statement types. + +# Execution + +This section explains the execution model for compiled policies. + +## Plan Execution + +Compiled policies consist of one or more plans. Any plan can be invoked by name. +If no name is supplied, the first plan in the policy should be executed. Plans +consist of one or more [Blocks](#blocks) that are executed in-order. Statements +inside the blocks of a plan have implicit access to two local variables +representing the `input` and `data` documents (`0` and `1` respectively.) The +final statement in every block inside of a plan is a `ResultSetAddStmt` +statement that adds an object to an implicit result set. The object contains the +key-value bindings representing the values of variables in the original query. +If no `ResultSetAddStmt` statements are executed, the implicit result set is +empty. + +## Function Execution + +Compiled policies may contain zero or more functions. Any function can be +invoked by name via the `CallStmt` statement or dynamically via the +`CallDynamicStmt` statement. All functions are defined with two or more +positional arguments. The first positional argument is a local variable +representing the `input` document. The second positional argument is a local +variable representing the `data` document. Function execution terminates when a +`ReturnLocalStmt` statement is encountered. All functions include a final block +that includes a `ReturnLocalStmt`. + +## Block Execution + +Blocks are sequences of statements that are executed in order. Statements can be +executed if all of the input parameters are defined. If any input parameter is +undefined then the statement is undefined. The [Statement Definitions](#statement-definitions) section below indicates when a statement +may be undefined. When a statement is undefined execution breaks to the end of +the current block and resumes execution at the statement immediately following +the block (which may be the beginning of another block.) When a statement is +defined, all output parameters are defined. Execution halts if a statement +raises an exception. + +# Statement Definitions + +This section defines the statements that can be contained in plans and functions +and explains the input and output parameters that each statement accepts. The +set of valid parameter types are: + +- `local` is a 32-bit integer representing a local variable. +- `int32` is a 32-bit integer. +- `int64` is a 64-bit integer. +- `uint32` is a 32-bit unsigned integer. +- `string` is an arbitrary-length unicode string. +- `array[...]` represents a sequence of `...` values. + +In addition, parameters may be of type `operand`. The `operand` type represents +a tagged union that can refer to a local variable, boolean constant, or string +constant index: + +``` +{ + "type": "local" | "bool" | "string_index" + "value": number | boolean | number +} +``` + +Local variables refer to values. The value types are any JSON value (i.e., `null`, +`true`, `false`, `number`, `string`, `array`, and `object`) as well as sets +(which are unordered value collections.) + +## `ArrayAppendStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | --------------------------------- | +| `array` | `input` | `local` | The array to append a value to. | +| `value` | `input` | `operand` | The value to append to the array. | + +## `AssignIntStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ------- | -------------------------------------------- | +| `value` | `input` | `int64` | The integer value to assign to the target. | +| `target` | `output` | `local` | The local variable to assign the integer to. | + +## `AssignVarOnceStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | -------------------------------------------- | +| `source` | `input` | `operand` | The value to assign to the target. | +| `target` | `output` | `local` | The local variable to assign the operand to. | + +:::danger +This statement raises an exception if the `target` operand is already assigned. +::: + +## `AssignVarStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | -------------------------------------------- | +| `source` | `input` | `operand` | The value to assign to the target. | +| `target` | `output` | `local` | The local variable to assign the operand to. | + +## `BlockStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ----------------- | ----------------------------- | +| `blocks` | `input` | [Blocks](#blocks) | The nested blocks to execute. | + +## `BreakStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `index` | `input` | `uint32` | The index of the block to jump out of starting with zero representing the current block and incrementing by one for each outer block. | + +## `CallDynamicStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ---------------- | ---------------------------------------------------------- | +| `path` | `input` | `array[operand]` | The path of the function to invoke. | +| `args` | `input` | `array[local]` | The positional arguments to pass to the function. | +| `result` | `output` | `local` | The local variable to assign the function return value to. | + +## `CallStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | -------------- | ---------------------------------------------------------- | +| `func` | `input` | `string` | The name of the function to invoke. | +| `args` | `input` | `array[local]` | The positional arguments to pass to the function. | +| `result` | `output` | `local` | The local variable to assign the function return value to. | + +## `DotStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | ------------------------------------------- | +| `source` | `input` | `operand` | The value to perform a lookup operation on. | +| `key` | `input` | `operand` | The key to lookup in the source. | +| `target` | `output` | `local` | The local variable to assign the result to. | + +This statement is **undefined** if the `key` does not exist in the `source` value. + +## `EqualStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | ---------------------------- | +| `a` | `input` | `operand` | The first value to compare. | +| `b` | `input` | `operand` | The second value to compare. | + +This statement is **undefined** if `a` does not equal `b`. + +## `IsArrayStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | ------------------- | +| `source` | `input` | `operand` | The value to check. | + +This statement is **undefined** if `source` is not an array. + +## `IsDefinedStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | ------------------- | +| `source` | `input` | `operand` | The value to check. | + +This statement is **undefined** if `source` is undefined. + +## `IsObjectStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | ------------------- | +| `source` | `input` | `operand` | The value to check. | + +This statement is **undefined** if `source` is not an object. + +## `IsSetStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | ------------------- | +| `source` | `input` | `operand` | The value to check. | + +This statement is **undefined** if `source` is not a set. + +## `IsUndefinedStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | ------------------- | +| `source` | `input` | `operand` | The value to check. | + +This statement is **undefined** if `source` is not undefined. + +## `LenStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | ------------------------------------------- | +| `source` | `input` | `operand` | The value to compute the length for. | +| `target` | `output` | `local` | The local variable to assign the length to. | + +## `MakeArrayStmt` + +| Parameter | Input/Output | Type | Description | +| ---------- | ------------ | ------- | ------------------------------------------------ | +| `capacity` | `input` | `int32` | The initial size of the array to pre-allocate. | +| `target` | `output` | `local` | The local variable to assign the array value to. | + +## `MakeNullStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ------- | ----------------------------------------------- | +| `target` | `output` | `local` | The local variable to assign the null value to. | + +## `MakeNumberIntStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ------- | ------------------------------------------------ | +| `value` | `input` | `int64` | The integer value to initialize the target with. | +| `target` | `output` | `local` | The local variable to assign the number to. | + +## `MakeNumberRefStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ------- | -------------------------------------------------------------- | +| `index` | `input` | `int32` | The index of the string constant to construct the number with. | +| `target` | `output` | `local` | The local variable to assign the number to. | + +## `MakeObjectStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ------- | ------------------------------------------- | +| `target` | `output` | `local` | The local variable to assign the object to. | + +## `MakeSetStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ------- | ---------------------------------------- | +| `target` | `output` | `local` | The local variable to assign the set to. | + +## `NopStmt` + +This statement is only used for debugging purposes. + +## `NotEqualStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | ---------------------------- | +| `a` | `input` | `operand` | The first value to compare. | +| `b` | `input` | `operand` | The second value to compare. | + +This statement is **undefined** if `a` is equal to `b`. + +## `NotStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ---------------- | --------------------------------- | +| `block` | `input` | [Block](#blocks) | The negated statement to execute. | + +This statement is **undefined** if the contained block is not undefined. + +## `ObjectInsertOnceStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | --------------------------------------------- | +| `key` | `input` | `operand` | The key to insert into the object. | +| `value` | `input` | `operand` | The value to insert into the object. | +| `object` | `input` | `local` | The object to insert the key-value pair into. | + +:::danger +This statement raises an exception if the `object` contains an existing `key` with a different `value`. +::: + +## `ObjectInsertStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | --------------------------------------------- | +| `key` | `input` | `operand` | The key to insert into the object. | +| `value` | `input` | `operand` | The value to insert into the object. | +| `object` | `input` | `local` | The object to insert the key-value pair into. | + +## `ObjectMergeStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ------- | -------------------------------------------------- | +| `a` | `input` | `local` | The object to merge into. | +| `b` | `input` | `local` | The object to merge from. | +| `target` | `output` | `local` | The local variable to assign the merged object to. | + +## `ResetLocalStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ------- | ---------------------------- | +| `target` | `output` | `local` | The local variable to reset. | + +## `ResultSetAddStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ------- | ----------------------------------- | +| `value` | `input` | `local` | The value to add to the result set. | + +## `ReturnLocalStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ------- | -------------------------------------- | +| `source` | `input` | `local` | The value to return from the function. | + +## `ScanStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ---------------- | -------------------------------------------------------------------------- | +| `source` | `input` | `local` | The value to scan. | +| `key` | `output` | `local` | The local variable to assign keys to before executing the nested block. | +| `value` | `output` | `local` | The local variable to assign values to before executing the nested block. | +| `block` | `input` | [Block](#blocks) | The nested block to execute repeatedly for each element in the collection. | + +This statement is **undefined** if `source` is a scalar value or empty collection. + +## `SetAddStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | --------- | --------------------------------- | +| `value` | `input` | `operand` | The value to insert into the set. | +| `set` | `input` | `local` | The set to insert the value into. | + +## `WithStmt` + +| Parameter | Input/Output | Type | Description | +| --------- | ------------ | ---------------- | --------------------------------------------------------------------------------------------------------------- | +| `local` | `input` | `local` | The value to mutate in the context of the nested block. | +| `path` | `input` | `array[int32]` | The path of the nested document to replace with the `value` represented as an array of string constant indices. | +| `value` | `input` | `operand` | The value to upsert. | +| `block` | `input` | [Block](#blocks) | The nested block to execute in the context of the mutation. | + +# Test Suite + +The OPA repository contains a [test suite](https://github.com/open-policy-agent/opa/tree/main/v1/test/cases/testdata/v1) +that is used internally to validate both the Go interpreter and the Wasm +compiler. If you are implementing your own compiler or interpreter we highly +recommend integrating the test suite into your own development environment so +that your implementation can be verified to conform with OPA's. + +The test suite consists of a set of YAML files that each contain a set of test +cases. Each test cases specifies a query, set of modules, data values, and +expected outputs or expected error conditions. + +To get started with the test suite, see the [Hello World](https://github.com/open-policy-agent/opa/blob/main/v1/test/cases/testdata/v0/helloworld/test-helloworld-1.yaml) +example. + +The following examples show how the test suite is used internally: + +- [`github.com/open-policy-agent/opa/topdown#TestRego`](https://github.com/open-policy-agent/opa/blob/main/v1/topdown/exported_test.go) +- [`github.com/open-policy-agent/opa/internal/wasm/sdk/test/e2e/external_test`](https://github.com/open-policy-agent/opa/blob/main/internal/wasm/sdk/test/e2e/external_test.go) diff --git a/third_party/opa/docs/docs/kafka-authorization.md b/third_party/opa/docs/docs/kafka-authorization.md new file mode 100644 index 000000000000..627bf6afec23 --- /dev/null +++ b/third_party/opa/docs/docs/kafka-authorization.md @@ -0,0 +1,526 @@ +--- +title: Kafka +--- + +[Apache Kafka](https://kafka.apache.org/) is a high-performance distributed +streaming platform deployed by thousands of companies. In many deployments, +administrators require fine-grained access control over Kafka topics to +enforce important requirements around confidentiality and integrity. + +## Goals + +This tutorial shows how to enforce fine-grained access control over Kafka +topics. In this tutorial you will use OPA to define and enforce an +authorization policy stating: + +- Consumers of topics containing Personally Identifiable Information (PII) must be on allow list. +- Producers to topics with _high fanout_ must be on allow list. + +In addition, this tutorial shows how to break up a policy with small helper +rules to reuse logic and improve overall readability. + +## Prerequisites + +This tutorial requires [Docker Compose](https://docs.docker.com/compose/install/) to run Kafka, ZooKeeper, and OPA. + +Additionally, we'll use Nginx for serving policy and data bundles to OPA. This component is however easily replaceable +by any other bundle server [implementation](./management-bundles/#implementations). + +## Steps + +### 1. Bootstrap the tutorial environment using Docker Compose. + +First, let's create some directories. We'll create one for our policy files, a second one for built bundles, and a third +one or the OPA authorizer plugin. + +```bash +mkdir policies bundles plugin +``` + +Next, create an OPA policy that allows all requests. You will update this policy later in the tutorial. + +**policies/tutorial.rego**: + +```rego +package kafka.authz + +allow := true +``` + +With the policy in place, build a bundle from the contents of the `policies` directory and place it in the `bundles` +directory. The `bundles` directory will later be mounted into the Nginx container in order to distribute policy updates +to OPA. + +```shell +opa build --bundle policies/ --output bundles/bundle.tar.gz +``` + +#### Kafka Authorizer JAR File + +Next, download the latest version of the [Open Policy Agent plugin for Kafka authorization](https://github.com/StyraInc/opa-kafka-plugin) +plugin from the projects [release pages](https://github.com/StyraInc/opa-kafka-plugin/releases). + +Store the plugin in the `plugin` directory (replace `${version}` with the version number of the plugin just downloaded): + +```bash +mv opa-authorizer-${version}-all.jar plugin/ +``` + +For more information on how to configure the OPA plugin for Kafka, see the plugin [repository](https://github.com/StyraInc/opa-kafka-plugin). + +Next, create a `docker-compose.yaml` file that runs OPA, Nginx, ZooKeeper, and Kafka. + + + +```yaml title="docker-compose.yaml" +services: + nginx: + image: nginx:1.21.4 + volumes: + - "./bundles:/usr/share/nginx/html" + ports: + - "80:80" + opa: + image: openpolicyagent/opa:{{ current_version_docker }} + ports: + - "8181:8181" + command: + - "run" + - "--server" + - "--set=decision_logs.console=true" + - "--set=services.authz.url=http://nginx" + - "--set=bundles.authz.service=authz" + - "--set=bundles.authz.resource=bundle.tar.gz" + depends_on: + - nginx + zookeeper: + image: confluentinc/cp-zookeeper:6.2.1 + ports: + - "2181:2181" + environment: + - ALLOW_ANONYMOUS_LOGIN=yes + - ZOOKEEPER_CLIENT_PORT=2181 + broker: + image: confluentinc/cp-kafka:6.2.1 + ports: + - "9093:9093" + environment: + # Set cache expiry to low value for development in order to see decisions + KAFKA_OPA_AUTHORIZER_CACHE_EXPIRE_AFTER_SECONDS: 10 + KAFKA_OPA_AUTHORIZER_URL: http://opa:8181/v1/data/kafka/authz/allow + KAFKA_AUTHORIZER_CLASS_NAME: org.openpolicyagent.kafka.OpaAuthorizer + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181" + KAFKA_ADVERTISED_LISTENERS: SSL://localhost:9093 + KAFKA_SECURITY_INTER_BROKER_PROTOCOL: SSL + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 + KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true" + KAFKA_SSL_KEYSTORE_FILENAME: server.keystore + KAFKA_SSL_KEYSTORE_CREDENTIALS: credentials.txt + KAFKA_SSL_KEY_CREDENTIALS: credentials.txt + KAFKA_SSL_TRUSTSTORE_FILENAME: server.truststore + KAFKA_SSL_TRUSTSTORE_CREDENTIALS: credentials.txt + KAFKA_SSL_CLIENT_AUTH: required + CLASSPATH: "/plugin/*" + volumes: + - "./plugin:/plugin" + - "./cert/server:/etc/kafka/secrets" + depends_on: + - opa + - zookeeper +``` + + + +#### Authentication + +The Docker Compose file defined above requires **SSL client authentication** +for clients that connect to the broker. Enabling SSL client authentication +allows for service identities to be provided as input to your policy. The +example below shows the input structure. + +```json +{ + "action": { + "logIfAllowed": true, + "logIfDenied": true, + "operation": "WRITE", + "resourcePattern": { + "name": "credit-scores", + "patternType": "LITERAL", + "resourceType": "TOPIC", + "unknown": false + }, + "resourceReferenceCount": 1 + }, + "requestContext": { + "clientAddress": "/172.22.0.1", + "clientInformation": { + "softwareName": "apache-kafka-java", + "softwareVersion": "2.8.1" + }, + "connectionId": "172.22.0.5:9093-172.22.0.1:62744-2", + "header": { + "headerVersion": 2, + "name": { + "clientId": "consumer-console-producer-63933-1", + "correlationId": 10, + "requestApiKey": 1, + "requestApiVersion": 12 + } + }, + "listenerName": "SSL", + "principal": { + "name": "CN=anon_producer,OU=Developers", + "principalType": "User" + }, + "securityProtocol": "SSL" + } +} +``` + +The client identity is extracted from the SSL certificates that clients +present when they connect to the broker. The user identity information is +encoded in the `input.requestContext.principal.name` field. +This field can be used inside the policy. + +A detailed rundown of generating SSL certificates and JKS files required +for SSL client authentication is outside the scope of this tutorial, but the plugin +repository provides an [example script](https://github.com/StyraInc/opa-kafka-plugin/tree/main/example/opa_tutorial/create_cert.sh) +that demonstrates the creation of client certificates for the four different +users used in this tutorial: + +- `anon_producer` +- `anon_consumer` +- `pii_consumer` +- `fanout_producer` + +Lets' download the script and run it: + +```shell +curl -O https://raw.githubusercontent.com/StyraInc/opa-kafka-plugin/main/example/opa_tutorial/create_cert.sh +chmod +x create_cert.sh +./create_cert.sh +``` + +We should now find a new `cert` directory created by the script, +containing the server and client certificates we'll need for TLS +authentication. + +Note: Do not rely on these SSL certificates in real-world scenarios. +They are only provided for convenience/test purposes. + +If you'd rather set up these users by other means, like one +of the available SASL mechanisms Kafka provides, that should work +just as well. Just make sure to update the Docker compose file +accordingly. + +Once you have created the files needed for authentication, +you may launch the containers for this tutorial. + +```bash +docker-compose --project-name opa-kafka-tutorial up +``` + +Now that the tutorial environment is running, we can define an authorization policy using OPA and test it. + +### 2. Define a policy to restrict consumer access to topics containing Personally Identifiable Information (PII). + +Update the `policies/tutorial.rego` with the following content. + +```rego +#----------------------------------------------------------------------------- +# High level policy for controlling access to Kafka. +# +# * Deny operations by default. +# * Allow operations if no explicit denial. +# +# The kafka-authorizer-opa plugin will query OPA for decisions at +# /kafka/authz/allow. If the policy decision is _true_ the request is allowed. +# If the policy decision is _false_ the request is denied. +#----------------------------------------------------------------------------- +package kafka.authz + +default allow := false + +allow if { + not deny +} + +deny if { + is_read_operation + topic_contains_pii + not consumer_is_allowlisted_for_pii +} + +#----------------------------------------------------------------------------- +# Data structures for controlling access to topics. In real-world deployments, +# these data structures could be loaded into OPA as raw JSON data. The JSON +# data could be pulled from external sources like AD, Git, etc. +#----------------------------------------------------------------------------- + +consumer_allowlist := {"pii": {"pii_consumer"}} + +topic_metadata := {"credit-scores": {"tags": ["pii"]}} + +#----------------------------------- +# Helpers for checking topic access. +#----------------------------------- + +topic_contains_pii if { + "pii" in topic_metadata[topic_name].tags +} + +consumer_is_allowlisted_for_pii if { + principal.name in consumer_allowlist.pii +} + +#----------------------------------------------------------------------------- +# Helpers for processing Kafka operation input. This logic could be split out +# into a separate file and shared. For conciseness, we have kept it all in one +# place. +#----------------------------------------------------------------------------- + +is_write_operation if { + input.action.operation == "WRITE" +} + +is_read_operation if { + input.action.operation == "READ" +} + +is_topic_resource if { + input.action.resourcePattern.resourceType == "TOPIC" +} + +topic_name := input.action.resourcePattern.name if { + is_topic_resource +} + +principal := {"fqn": parsed.CN, "name": cn_parts[0]} if { + parsed := parse_user(input.requestContext.principal.name) + cn_parts := split(parsed.CN, ".") +} + +# If client certificates aren't used for authentication +else := {"fqn": "", "name": input.requestContext.principal.name} + +parse_user(user) := {key: value | + parts := split(user, ",") + [key, value] := split(parts[_], "=") +} +``` + + + +The Kafka authorization plugin is configured to query for the +`data.kafka.authz.allow` decision. If the response is `true` the operation is +allowed, otherwise the operation is denied. When the integration queries OPA it +supplies a JSON representation of the operation, resource, client, and principal. + +```json title="input.json" +{ + "action": { + "logIfAllowed": true, + "logIfDenied": true, + "operation": "READ", + "resourcePattern": { + "name": "credit-scores", + "patternType": "LITERAL", + "resourceType": "TOPIC", + "unknown": false + }, + "resourceReferenceCount": 1 + }, + "requestContext": { + "clientAddress": "/172.22.0.1", + "clientInformation": { + "softwareName": "apache-kafka-java", + "softwareVersion": "2.8.1" + }, + "connectionId": "172.22.0.5:9093-172.22.0.1:62744-2", + "header": { + "headerVersion": 2, + "name": { + "clientId": "consumer-console-producer-63933-1", + "correlationId": 10, + "requestApiKey": 1, + "requestApiVersion": 12 + } + }, + "listenerName": "SSL", + "principal": { + "name": "CN=pii_consumer,OU=developers", + "principalType": "User" + }, + "securityProtocol": "SSL" + } +} +``` + + + +The `./bundles` directory is mounted into the Docker container running Nginx. +When the bundle under this directory change, OPA is notified via the bundle API, +and the policies are automatically reloaded. + +You can update the bundle at any time by rebuilding it. + +```shell +opa build --bundle policies/ --output bundles/bundle.tar.gz +``` + +At this point, you can exercise the policy. + +### 3. Exercise the policy that restricts consumer access to topics containing PII. + +This step shows how you can grant fine-grained access to services using +Kafka. In this scenario, some services are allowed to read PII data while +others are not. + +First, run `kafka-console-producer` to generate some data on the +`credit-scores` topic. + +> This tutorial uses the `kafka-console-producer` and `kafka-console-consumer` scripts provided by Kafka to generate and display Kafka messages. These scripts read from STDIN and write to STDOUT and are frequently used to send and receive data via Kafka over the command line. If you are not familiar with these scripts you can learn more in Kafka's [Quick Start](https://kafka.apache.org/documentation/#quickstart) documentation. + +```bash +docker run -v $(pwd)/cert/client:/tmp/client --rm --network opa-kafka-tutorial_default \ + confluentinc/cp-kafka:6.2.1 \ + bash -c 'for i in {1..10}; do echo "{\"user\": \"bob\", \"score\": $i}"; done | kafka-console-producer --topic credit-scores --broker-list broker:9093 -producer.config /tmp/client/anon_producer.properties' +``` + +This command will send 10 messages to the `credit-scores` topic. Bob's credit +score seems to be improving. + +Next, run `kafka-console-consumer` and try to read data off the topic. Use +the `pii_consumer` credentials to simulate a service that is allowed to read +PII data. + +```bash +docker run -v $(pwd)/cert/client:/tmp/client --rm --network opa-kafka-tutorial_default \ + confluentinc/cp-kafka:6.2.1 \ + kafka-console-consumer --bootstrap-server broker:9093 --topic credit-scores --from-beginning --consumer.config /tmp/client/pii_consumer.properties +``` + +This command will output the 10 messages sent to the topic in the first part +of this step. Once the 10 messages have been printed, exit out of the script +(^C). + +Finally, run `kafka-console-consumer` again but this time try to use the +`anon_consumer` credentials. The `anon_consumer` credentials simulate a +service that has **not** been explicitly granted access to PII data. + +```bash +docker run -v $(pwd)/cert/client:/tmp/client --rm --network opa-kafka-tutorial_default \ + confluentinc/cp-kafka:6.2.1 \ + kafka-console-consumer --bootstrap-server broker:9093 --topic credit-scores --from-beginning --consumer.config /tmp/client/anon_consumer.properties +``` + +Because the `anon_consumer` is not allowed to read PII data, the request will +be denied and the consumer will output an error message. + +``` +Not authorized to read from topic credit-scores. +... +Processed a total of 0 messages +``` + +### 4. Extend the policy to prevent services from accidentally writing to topics with large fanout. + +First, add the following content to the policy file (`./policies/tutorial.rego`): + +```rego +deny if { + is_write_operation + topic_has_large_fanout + not producer_is_allowlisted_for_large_fanout +} + +producer_allowlist := { + "large-fanout": { + "fanout_producer", + } +} + +topic_has_large_fanout if { + topic_metadata[topic_name].tags[_] == "large-fanout" +} + +producer_is_allowlisted_for_large_fanout if { + producer_allowlist["large-fanout"][_] == principal.name +} +``` + +Next, update the `topic_metadata` data structure in the same file to indicate +that the `click-stream` topic has a high fanout. + +```rego +topic_metadata := { + "click-stream": { + "tags": ["large-fanout"], + }, + "credit-scores": { + "tags": ["pii"], + } +} +``` + +Last, build a bundle from the updated policy. + +```shell +opa build --bundle policies/ --output bundles/bundle.tar.gz +``` + +### 5. Exercise the policy that restricts producer access to topics with high fanout. + +First, run `kafka-console-producer` and simulate a service with access to the +`click-stream` topic. + +```bash +docker run -v $(pwd)/cert/client:/tmp/client --rm --network opa-kafka-tutorial_default \ + confluentinc/cp-kafka:6.2.1 \ + bash -c 'for i in {1..10}; do echo "{\"user\": \"alice\", \"button\": $i}"; done | kafka-console-producer --topic click-stream --broker-list broker:9093 -producer.config /tmp/client/fanout_producer.properties' +``` + +Next, run the `kafka-console-consumer` to confirm that the messages were published. + +```bash +docker run -v $(pwd)/cert/client:/tmp/client --rm --network opa-kafka-tutorial_default \ + confluentinc/cp-kafka:6.2.1 \ + kafka-console-consumer --bootstrap-server broker:9093 --topic click-stream --from-beginning --consumer.config /tmp/client/anon_consumer.properties +``` + +Once you see the 10 messages produced by the first part of this step, exit the console consumer (^C). + +Lastly, run `kafka-console-producer` to simulate a service that should **not** +have access to _high fanout_ topics. + +```bash +docker run -v $(pwd)/cert/client:/tmp/client --rm --network opa-kafka-tutorial_default \ + confluentinc/cp-kafka:6.2.1 \ + bash -c 'echo "{\"user\": \"alice\", \"button\": \"bogus\"}" | kafka-console-producer --topic click-stream --broker-list broker:9093 -producer.config /tmp/client/anon_producer.properties' +``` + +Because `anon_producer` is not authorized to write to high fanout topics, the +request will be denied and the producer will output an error message. + +``` +Not authorized to access topics: [click-stream] +``` + +## Wrap Up + +Congratulations on finishing the tutorial! + +At this point you have learned how to enforce fine-grained access control +over Kafka topics. In addition, you have seen how to break down policies into +smaller rules that can be reused and improve the overall readability over the +policy. + +If you want to use the Kafka Authorizer plugin that integrates Kafka with +OPA, see the build and install instructions in the +[opa-kafka-plugin](https://github.com/StyraInc/opa-kafka-plugin) +repository. diff --git a/third_party/opa/docs/docs/kubernetes/_category_.yaml b/third_party/opa/docs/docs/kubernetes/_category_.yaml new file mode 100644 index 000000000000..fe9ad0bee325 --- /dev/null +++ b/third_party/opa/docs/docs/kubernetes/_category_.yaml @@ -0,0 +1,4 @@ +position: 15 +label: "Kubernetes" +collapsible: true +collapsed: true diff --git a/third_party/opa/docs/docs/kubernetes/debugging.md b/third_party/opa/docs/docs/kubernetes/debugging.md new file mode 100644 index 000000000000..cfd297edb920 --- /dev/null +++ b/third_party/opa/docs/docs/kubernetes/debugging.md @@ -0,0 +1,173 @@ +--- +title: Debugging Tips +--- + +If you run into problems getting OPA to enforce admission control policies in +Kubernetes there are a few things you can check to make sure everything is +configured correctly. If none of these tips work, feel free to join +[our slack](https://slack.openpolicyagent.org) and ask for help. + +The tips below cover the OPA-Kubernetes integration that uses kube-mgmt. +The [OPA Gatekeeper version](https://open-policy-agent.github.io/gatekeeper) +has its own docs. + +### Check for the `openpolicyagent.org/kube-mgmt-status` annotation on ConfigMaps containing policies + +If you are loading policies into OPA via +[kube-mgmt](https://github.com/open-policy-agent/kube-mgmt) you can check the +`openpolicyagent.org/kube-mgmt-status` annotation on ConfigMaps that contain your +policies. The annotation should be set to `{"status":"ok"}` if the policy was loaded +successfully. If errors occurred during loading (e.g., because the policy +contained a syntax error) the cause will be reported here. + +If the annotation is +missing entirely, check the `kube-mgmt` container logs for connection errors +between the container and the Kubernetes API server. + +### Check the `kube-mgmt` container logs for error messages + +When `kube-mgmt` is healthy, the container logs will be quiet/empty. If you are +trying to enforce policies based on Kubernetes context (e.g., to check for +ingress conflicts) then you need to make sure that `kube-mgmt` can replicate +Kubernetes objects into OPA. If `kube-mgmt` is unable to list/watch resources in +the Kubernetes API server, they will not be replicated into OPA and the policy +will not get enforced. + +### Check the `opa` container logs for TLS errors + +Communication between the Kubernetes API server and OPA is secured with TLS. If +the CA bundle specified in the webhook configuration is out-of-sync with the +server certificate that OPA is configured with, OPA will log errors indicating a +TLS issue. Verify that the CA bundle specified in the validating or mutating +webhook configurations matches the server certificate you configured OPA to use. + +### Check for POST requests in the `opa` container logs + +When the Kubernetes API server queries OPA for admission control decisions, it +sends HTTP `POST` requests. If there are no `POST` requests contained in the +`opa` container logs, it indicates that the webhook configuration is wrong or +there is a network connectivity problem between the Kubernetes API server and +OPA. + +- If you have access to the Kubernetes API server logs, review them to see if + they indicate the cause. +- If you are running on AWS EKS make sure your security group settings allow + traffic from Kubernetes "master" nodes to the node(s) where OPA is running. + +### Ensure the webhook is configured for the proper namespaces + +When you create the webhook according to the installation instructions, +it includes a namespaceSelector so that you +can decide which namespaces to ignore. + +```yaml +namespaceSelector: + matchExpressions: + - key: openpolicyagent.org/webhook + operator: NotIn + values: + - ignore +``` + +If OPA seems to not be making the decisions you expect, check if the namespace +is using the label `openpolicyagent.org/webhook: ignore`. + +If OPA is making decision on namespaces (like `kube-system`) that you would +prefer OPA would ignore, assign the namespace the label +`openpolicyagent.org/webhook: ignore`. + +### Ensure mutating policies construct JSON Patches correctly + +If you are using OPA to enforce mutating admission policies you must ensure the +JSON Patch objects you generate escape "/" characters in the JSON Pointer. For +example, if you are generating a JSON Patch that sets annotations like +`acmecorp.com/myannotation` you need to escape the "/" character in the +annotation name using `~1` (per [RFC 6901](https://tools.ietf.org/html/rfc6901#section-3)). + + + + +```json title="Correct" +{ + "op": "add", + "path": "/metadata/annotations/acmecorp.com~1myannotation", + "value": "somevalue" +} +``` + + + + +```json title="Incorrect" +{ + "op": "add", + "path": "/metadata/annotations/acmecorp.com/myannotation", + "value": "somevalue" +} +``` + + + +In addition, when your policy generates the response for the Kubernetes API +server, you must use the `base64.encode` built-in function to encode the JSON +Patch objects. DO NOT use the `base64url.encode` function because the Kubernetes +API server will not process it: + + + +```rego title="Correct" +package system + +main := { +"apiVersion": "admission.k8s.io/v1", +"kind": "AdmissionReview", +"response": response, +} + +response := { +"allowed": true, +"patchType": "JSONPatch", +"patch": base64.encode(json.marshal(patches)) # <-- GOOD: uses base64.encode +} + +patches := [ +{ +"op": "add", +"path": "/metadata/annotations/acmecorp.com~1myannotation", +"value": "somevalue" +} +] + +```` + + +```rego title="Incorrect" +package system + +main := { + "apiVersion": "admission.k8s.io/v1", + "kind": "AdmissionReview", + "response": response, +} + +response := { + "allowed": true, + "patchType": "JSONPatch", + "patch": base64url.encode(json.marshal(patches)) # <-- BAD: uses base64url.encode +} + +patches := [ + { + "op": "add", + "path": "/metadata/annotations/acmecorp.com~1myannotation", + "value": "somevalue" + } +] +```` + + + + +Also, for more examples of how to construct mutating policies and integrating +them with validating policies, see [these examples](https://github.com/open-policy-agent/library/tree/master/kubernetes/mutating-admission) +in https://github.com/open-policy-agent/library. diff --git a/third_party/opa/docs/docs/kubernetes/index.md b/third_party/opa/docs/docs/kubernetes/index.md new file mode 100644 index 000000000000..3c0e96b3367a --- /dev/null +++ b/third_party/opa/docs/docs/kubernetes/index.md @@ -0,0 +1,291 @@ +--- +title: "Overview & Architecture" +sidebar_position: 1 +--- + +In Kubernetes, [Admission Controllers](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/) +enforce policies on objects during create, update, and delete operations. Admission +control is fundamental to policy enforcement in Kubernetes. + +For example, by deploying OPA as an admission controller you can: + +- Require specific labels on all resources. +- Require container images come from the corporate image registry. +- Require all Pods specify resource requests and limits. +- Prevent conflicting Ingress objects from being created. + +Admission controllers can also mutate incoming objects. By deploying OPA as a +mutating admission controller you can: + +- Inject sidecar containers into Pods. +- Set specific annotations on all resources. +- Rewrite container images to point at the corporate image registry. +- Include node and pod (anti-)affinity selectors on Deployments. + +These are just examples of policies you can enforce with admission controllers +and OPA. There are dozens of other policies you will want to enforce in your +Kubernetes clusters for security, cost, and availability reasons. + +## What is OPA Gatekeeper? + +[OPA Gatekeeper](https://open-policy-agent.github.io/gatekeeper) is a specialized +project providing first-class integration between OPA and Kubernetes. For +background information see this [blog post](https://kubernetes.io/blog/2019/08/06/opa-gatekeeper-policy-and-governance-for-kubernetes) +on kubernetes.io. + +OPA Gatekeeper adds the following on top of plain OPA: + +- An extensible, parameterized policy library. +- Native Kubernetes CRDs for instantiating the policy library (aka "constraints"). +- Native Kubernetes CRDs for extending the policy library (aka "constraint templates"). +- Audit functionality. + +If you want to kick the tires: + +- See the [Installation Instructions](https://open-policy-agent.github.io/gatekeeper/website/docs/install/) + in the README. +- See the + [demo/basic](https://github.com/open-policy-agent/gatekeeper/tree/master/demo/basic) + and + [demo/agilebank](https://github.com/open-policy-agent/gatekeeper/tree/master/demo/agilebank) + directories for examples policies and setup scripts. + +**Recommendation**: OPA Gatekeeper is **the go-to project** for using OPA for +Kubernetes admission control. Plain OPA and Kube-mgmt (see below) are alternatives +that can be reached for if you want to use the management features of OPA, such as +status logs, decision logs, and bundles. + +## How Does It Work With Plain OPA and Kube-mgmt? + +The Kubernetes API Server is configured to query OPA for admission control +decisions when objects (e.g., Pods, Services, etc.) are created, updated, or +deleted. + +```mermaid +graph TD + kubectl + cicd[CI/CD Pipelines] + controllers[Controllers] + api[API Server] + OPA + + kubectl --> api + cicd --> api + controllers --> api + + api -->|AdmissionReview
Request| OPA + OPA -->|AdmissionReview
Response| api +``` + +The API Server sends the entire Kubernetes object in the webhook request to OPA. +OPA evaluates the policies it has loaded using the admission review as `input`. +For example, the following policy denies objects that include container images +referring to illegal registries: + +```rego +package kubernetes.admission + +deny contains reason if { + some container + input_containers[container] + not startswith(container.image, "hooli.com/") + reason := "container image refers to illegal registry (must be hooli.com)" +} + +input_containers contains container if { + container := input.request.object.spec.containers[_] +} + +input_containers contains container if { + container := input.request.object.spec.template.spec.containers[_] +} +``` + + + +The `input` document contains the following fields: + +- `input.request.kind` specifies the type of the object (e.g., `Pod`, `Service`, + etc.) +- `input.request.operation` specifies the type of the operation, i.e., `CREATE`, + `UPDATE`, `DELETE`, `CONNECT`. +- `input.request.userInfo` specifies the identity of the caller. +- `input.request.object` contains the entire Kubernetes object. +- `input.request.oldObject` specifies the previous version of the Kubernetes + object on `UPDATE` and `DELETE`. + +Here is an example of a Pod being created: + +```json title="input.json" +{ + "kind": "AdmissionReview", + "apiVersion": "admission.k8s.io/v1", + "request": { + "kind": { + "group": "", + "version": "v1", + "kind": "Pod" + }, + "resource": { + "group": "", + "version": "v1", + "resource": "pods" + }, + "namespace": "opa-test", + "operation": "CREATE", + "userInfo": { + "username": "system:serviceaccount:kube-system:replicaset-controller", + "uid": "439dea65-3e4e-4fa8-b5f8-8fdc4bc7cf53", + "groups": [ + "system:serviceaccounts", + "system:serviceaccounts:kube-system", + "system:authenticated" + ] + }, + "object": { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "creationTimestamp": "2019-08-13T16:01:54Z", + "generateName": "nginx-7bb7cd8db5-", + "labels": { + "pod-template-hash": "7bb7cd8db5", + "run": "nginx" + }, + "name": "nginx-7bb7cd8db5-dbplk", + "namespace": "opa-test", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "ReplicaSet", + "name": "nginx-7bb7cd8db5", + "uid": "7b6a307f-d9b4-4b65-a916-5d0b96305e87" + } + ], + "uid": "266d2c8b-e43e-42d9-a19c-690bb6103900" + }, + "spec": { + "containers": [ + { + "image": "nginx", + "imagePullPolicy": "Always", + "name": "nginx", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "default-token-6h4dn", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": {}, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "default-token-6h4dn", + "secret": { + "secretName": "default-token-6h4dn" + } + } + ] + }, + "status": { + "phase": "Pending", + "qosClass": "BestEffort" + } + }, + "oldObject": null + } +} +``` + + + +The policies you give to OPA ultimately generate an admission review response +that is sent back to the API Server. Here is an example of the policy decision +sent back to the API Server. + +```json title="response" +{ + "kind": "AdmissionReview", + "apiVersion": "admission.k8s.io/v1", + "response": { + "allowed": false, + "status": { + "message": "container image refers to illegal registry (must be hooli.com)" + } + } +} +``` + +> The API Server implements a "deny overrides" conflict resolution strategy. If +> any admission controller denies the request, the request is denied (even if +> one of the later admission controllers were to allow the request.) + +Policies can be loaded into OPA dynamically via ConfigMap objects using the +[kube-mgmt](https://github.com/open-policy-agent/kube-mgmt) sidecar container. +The kube-mgmt sidecar container can also load any other Kubernetes object into +OPA as JSON under `data`. This lets you enforce policies that rely on an +eventually consistent snapshot of the Kubernetes cluster as context. + +```mermaid +graph LR + policies["Rego Policies
(Config Map)"] + api[API Server] + kubemgmt[kube-mgmt] + OPA + + policies --> api + api -->|ConfigMap| kubemgmt + api -->|Namespaces| kubemgmt + api -->|Ingresses| kubemgmt + api -->|...| kubemgmt + kubemgmt -->|Structured
Resource Data| OPA +``` + +See the [Policy Authoring](./kubernetes/primer) and [Tutorial: Ingress Validation](./kubernetes/tutorial) pages for more details. + +## Additional Resources + +See the following pages on [kubernetes.io](https://kubernetes.io) for more +information on admission control: + +- [A Guide to Kubernetes Admission + Controllers](https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/) + for a quick primer on admission controllers. +- [Dynamic Admission Control](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/) + for details on configuring external admission controllers. + +## Ecosystem Projects + + +Kubernetes is a popular integration with OPA and there are number of projects already in this space +which might work for your use case. + diff --git a/third_party/opa/docs/docs/kubernetes/primer.md b/third_party/opa/docs/docs/kubernetes/primer.md new file mode 100644 index 000000000000..23536a0eda96 --- /dev/null +++ b/third_party/opa/docs/docs/kubernetes/primer.md @@ -0,0 +1,563 @@ +--- +title: Policy Primer via Examples +--- + +Read this page if you are new to Kubernetes admission control with OPA and want +to learn how to write policies for Kubernetes. It covers the version +that uses kube-mgmt. The [OPA Gatekeeper version](https://open-policy-agent.github.io/gatekeeper) +has its own docs. + +## Writing Policies + +To get started, let's look at a common policy: ensure all images come from a +trusted registry. + +```rego showLineNumbers=true +package kubernetes.admission # line 1 + +deny contains msg if { # line 2 + input.request.kind.kind == "Pod" # line 3 + image := input.request.object.spec.containers[_].image # line 4 + not startswith(image, "hooli.com/") # line 5 + msg := sprintf("image '%v' comes from untrusted registry", [image]) # line 6 +} +``` + + + +### Packages + +In line 1 the `package kubernetes.admission` declaration gives the (hierarchical) name `kubernetes.admission` to the rules in the remainder of the policy. The default installation of OPA as an admission controller assumes your rules are in the package `kubernetes.admission`. + +### Deny Rules + +For admission control, you write `deny` statements. Order does not matter. (OPA is far more flexible than this, but we recommend writing just `deny` statements to start.) In line 2, the _head_ of the rule `deny contains msg if` says that the admission control request should be rejected and the user handed the error message `msg` if the conditions in the _body_ (the statements between the `{}`) are true. + +`deny` is the _set_ of error messages that should be returned to the user. Each rule you write adds to that set of error messages. + +For example, suppose you tried to create the Pod below with nginx and mysql images. + +```yaml +kind: Pod +apiVersion: v1 +metadata: + name: myapp +spec: + containers: + - image: nginx + name: nginx-frontend + - image: mysql + name: mysql-backend +``` + +The admission review request to be sent to OPA would look like this: + +```json title="input.json" +{ + "kind": "AdmissionReview", + "request": { + "kind": { + "kind": "Pod", + "version": "v1" + }, + "object": { + "metadata": { + "name": "myapp" + }, + "spec": { + "containers": [ + { + "image": "nginx", + "name": "nginx-frontend" + }, + { + "image": "mysql", + "name": "mysql-backend" + } + ] + } + } + } +} +``` + + + +When the `deny` rule is evaluated with the input above: + +```rego +package example + +import data.kubernetes.admission + +result := admission.deny +``` + + + +### Input Document + +In OPA, `input` is a reserved, global variable whose value is the Kubernetes AdmissionReview object that the API server hands to any admission control webhook. + +AdmissionReview objects have many fields. The rule above uses `input.request.kind`, which includes the usual group/version/kind information. The rule also uses `input.request.object`, which is the YAML that the user provided to `kubectl` (augmented with defaults, timestamps, etc.). The full `input` object is 50+ lines of YAML, so below we show just the relevant parts. + +```yaml +apiVersion: admission.k8s.io/v1 +kind: AdmissionReview +request: + kind: + group: + kind: Pod + version: v1 + object: + metadata: + name: myapp + spec: + containers: + - image: nginx + name: nginx-frontend + - image: mysql + name: mysql-backend +``` + +### Dot Notation + +In line 3 `input.request.kind.kind == "Pod"`, the expression `input.request.kind.kind` does the obvious thing: it descends through the YAML hierarchy. The dot (.) operator never throws any errors; if the path does not exist the value of the expression is `undefined`. + +```rego +package example + +result := input.request.kind +``` + + + +```rego +package example + +result := input.request.kind.kind +``` + + + +```rego +package example + +result := input.request.object.spec.containers +``` + + + +### Equality + +Lines 3, 4, 6 all use a form of equality. There are 3 forms of equality in OPA. + +- `x := 7` declares a local variable `x` and assigns it a value of 7. The compiler throws an error if `x` already has a value. +- `x == 7` returns true if `x` has a value of 7. The compiler throws an error if `x` has no value. +- `x = 7` either assigns the value 7 to `x` if `x` has no value or compares `x`'s value to 7 if it has a value. The compiler never throws an error. + +The recommendation for rule-writing is to use `:=` and `==` wherever possible. Rules written with `:=` and `==` are easier to write and to read. `=` is invaluable in more advanced use cases, and outside of rules is the only supported form of equality. + +### Arrays + +Lines 4-5 find images in the Pod that don't come from the trusted registry. To do that, they use the `[]` operator, which does what you expect: index into the array. + +Continuing the example from earlier: + +```rego +package example + +result := input.request.object.spec.containers[0] +``` + + + +```rego +package example + +result := input.request.object.spec.containers[0].image +``` + + + +The `[]` operators let you use variables to index into the array as well. + +```rego +package example + +result contains c if { + i := 0 + c := input.request.object.spec.containers[i] +} +``` + + + +### Iteration + +The containers array has an unknown number of elements, so to implement an image registry check you need to iterate over them. Iteration in OPA requires no new syntax. In fact, OPA is always iterating--it's always searching for all variable assignments that make the conditions in the rule true. It's just that sometimes the search is so easy people don't think of it as iteration/search. + +To iterate over the indexes in the `input.request.object.spec.containers` array, you just put a variable that has no value in for the index. OPA will do what it always does: find values for that variable that make the conditions true. + +OPA detects when there will be multiple answers and displays all the results in a table. + +```rego +package example + +result contains c if { + some j + c := input.request.object.spec.containers[j] +} +``` + + + +Often you don't want to invent new variable names for iteration. OPA provides the special anonymous variable `_` for exactly that reason. So in line (4) `image := input.request.object.spec.containers[_].image` finds all the images in the containers array and assigns each to the `image` variable one at a time. + +### Builtins + +On line 5 the _builtin_ `startswith` checks if one string is a prefix of the other. The builtin `sprintf` on line 6 formats a string with arguments. OPA has 150+ builtins detailed in [the Policy Reference](../policy-reference/#built-in-functions). +Builtins let you analyze and manipulate: + +- Numbers, Strings, Regexs, Networks +- Aggregates, Arrays, Sets +- Types +- Encodings (base64, YAML, JSON, URL, JWT) +- Time + +## Testing Policies + +When you write policies, you should use the OPA unit-test framework _before_ sending the policies out into the OPA that is running on your cluster. The debugging process will be much quicker and effective. Here's an example test for the policy from the last section. + +```rego +package kubernetes.test_admission # line 1 + +import data.kubernetes.admission # line 2 + +test_image_safety if { # line 3 + unsafe_image := { # line 4 + "request": { + "kind": {"kind": "Pod"}, + "object": { + "spec": { + "containers": [ + {"image": "hooli.com/nginx"}, + {"image": "busybox"} + ] + } + } + } + } + expected := "image 'busybox' comes from untrusted registry" + admission.deny[expected] with input as unsafe_image # line 5 +} +``` + +**Different Package**. On line 1 the `package` directive puts these tests in a different package than admission control policy itself. This is the recommended best practice. + +**Import**. On line 2 `import data.kubernetes.admission` allows us to reference the admission control policy using the name `admission` everywhere in the test package. `import` is not strictly necessary--it simply sets up an alias; you could instead reference `data.kubernetes.admission` inside the rules. + +**Unit Test**. On line 3 `test_image_safety` defines a unittest. If the rule evaluates to true the test passes; otherwise it fails. When you use the OPA test runner, anything in any package starting with `test` is treated as a test. + +**Assignment**. On line 4 `unsafe_image` is the input we want to use for the test. Ideally this would be a real AdmissionReview object, though those are so long that in this example we hand-rolled a partial input. + +**Dot for packages**. On line 5 we use the Dot operator on a package. `admission.deny[expected]` runs the `deny` rule(s) in package `admission` and checks if the message is contained in the set defined by `deny`. + +**Test Input**. Also on line 5 the stanza `with input as unsafe_image` sets the value of `input` to be `unsafe_image` while evaluating `admission.deny[expected]`. + +**Running Tests**. If you've created the files _image-safety.rego_ and _test-image-safety.rego_ in the current directory then you run the tests by naming the files explicitly as shown below or by handing the `opa test` command the directory (and subdirectories) of files to load: `opa test .` + +``` +$ opa test image-safety.rego test-image-safety.rego +PASS: 1/1 +``` + +## Using Context in Policies + +The image-repository example shows an example where you can make a policy decision using just the one JSON/YAML file describing the resource in question. But sometimes you need to know what other resources exist in the cluster to make an allow/deny decision. + +For example, it’s possible to accidentally configure two Kubernetes ingresses so that one steals traffic from the other. The policy that prevents conflicting ingresses needs to compare the ingress that’s being created/updated with all of the existing ingresses. Just knowing the new/updated ingress isn't enough information to make an allow/deny decision. + +Below is a partial example of the input OPA sees when someone creates an ingress. To avoid conflicts, we want to prevent two ingresses from having the same `request.object.spec.rules.host`. If OPA has only this one ingress configuration it doesn't have enough information to make an allow/deny decision; it also needs the configurations for all of the existing ingresses. + +```yaml +apiVersion: admission.k8s.io/v1 +kind: AdmissionReview +request: + kind: + group: networking.k8s.io + kind: Ingress + version: v1 + object: + metadata: + name: prod + spec: + rules: + - host: initech.com + http: + paths: + - path: /finance + pathType: Prefix + backend: + service: + name: banking + port: + number: 443 +``` + +To avoid conflicting ingresses, you write a policy like the one that follows. + +```rego +package kubernetes.admission + +deny contains msg if { + some namespace, name + input.request.kind.kind == "Ingress" # line 1 + newhost := input.request.object.spec.rules[_].host # line 2 + oldhost := data.kubernetes.ingresses[namespace][name].spec.rules[_].host # line 3 + newhost == oldhost # line 4 + input.request.object.metadata.namespace != namespace # line 5 + input.request.object.metadata.name != name # line 6 + msg := sprintf("ingress host conflicts with ingress %v/%v", [namespace, name]) # line 7 +} +``` + +The first part of the rule you already understand: + +- Line (1) checks if the `input` is an Ingress +- Line (2) iterates over all the rules in the `input` ingress and looks up the `host` field for each of its rules. + +**Existing K8s Resources** Line (3) iterates over ingresses that already exist in Kubernetes. `data` is a global variable where (among other things) OPA has a record of the current resources inside Kubernetes. The line `oldhost := data.kubernetes.ingresses[namespace][name].spec.rules[_].host` finds all ingresses in all namespaces, iterates over all the `rules` inside each of those and assigns the `host` field to the variable `oldhost`. Whenever `newhost == oldhost`, there's a conflict, and the OPA rule includes an appropriate error message into the `deny` set. + +In this case the rule uses explicit variable names `namespace` and `name` for iteration so that it can use those variables again when constructing the error message in line (7). + +**Schema Differences**. Both `input` and `data.kubernetes.ingresses[namespace][name]` represent ingresses, but they do it differently. + +- `input` is a Kubernetes AdmissionReview object. It includes several fields in addition to the Kubernetes Ingress object itself. +- `data.kubernetes.ingresses[namespace][name]` is a native Kubernetes Ingress object as returned by the API. + +Here are two examples. + + + +```yaml title="data.kubernetes.ingresses[namespace][name]" +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: prod +spec: + rules: + - host: initech.com + http: + paths: + - path: /finance + pathType: Prefix + backend: + service: + name: banking + port: + number: 443 +``` + + +```yaml title="admission_review.yaml" +apiVersion: admission.k8s.io/v1 +kind: AdmissionReview +request: + kind: + group: networking.k8s.io + kind: Ingress + version: v1 + operation: CREATE + userInfo: + groups: + username: alice + object: + metadata: + name: prod + spec: + rules: + - host: initech.com + http: + paths: + - path: /finance + pathType: Prefix + backend: + service: + name: banking + port: + number: 443 +``` + + + +## Detailed Admission Control Flow + +This section provides a detailed explanation of the admission control flow +introduced in the [Introduction](..) page. + +It starts with someone (or something) running `kubectl` (or sending a request to +the API server.) For example, a user might run `kubectl create -f pod.yaml`: + +```yaml title="pod.yaml" +kind: Pod +apiVersion: v1 +metadata: + name: nginx + labels: + app: nginx +spec: + containers: + - image: nginx + name: nginx +``` + +When the request reaches the API server it's authenticated and authorized and +processed by the admission controllers. When the API server's Webhook admission +controller executes, the API server sends a webhook request to OPA containing an +**AdmissionReview** object. + +```yaml title="admission_review.yaml" +apiVersion: admission.k8s.io/v1 +kind: AdmissionReview +request: + kind: + group: "" + kind: Pod + version: v1 + namespace: opa + object: + metadata: + creationTimestamp: "2018-10-27T02:12:20Z" + labels: + app: nginx + name: nginx + namespace: opa + uid: bbfee96d-d98d-11e8-b280-080027868e77 + spec: + containers: + - image: nginx + imagePullPolicy: Always + name: nginx + resources: {} + terminationMessagePath: "/dev/termination-log" + terminationMessagePolicy: File + volumeMounts: + - mountPath: "/var/run/secrets/kubernetes.io/serviceaccount" + name: default-token-tm9v8 + readOnly: true + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + serviceAccount: default + serviceAccountName: default + terminationGracePeriodSeconds: 30 + tolerations: + - effect: NoExecute + key: node.kubernetes.io/not-ready + operator: Exists + tolerationSeconds: 300 + - effect: NoExecute + key: node.kubernetes.io/unreachable + operator: Exists + tolerationSeconds: 300 + volumes: + - name: default-token-tm9v8 + secret: + secretName: default-token-tm9v8 + status: + phase: Pending + qosClass: BestEffort + oldObject: + operation: CREATE + resource: + group: "" + resource: pods + version: v1 + uid: 8d836dfd-e0c0-4490-93ba-85ed4a04261e + userInfo: + groups: + - system:masters + - system:authenticated + username: minikube-user +``` + +Typically the API server is configured (via `ValidatingWebhookConfiguration` or +`MutatingWebhookConfiguration` objects) to query OPA without providing the name +of a decision. For example: + +```http +POST / HTTP/1.1 +Content-Type: application/json +``` + +```json +{ + "apiVersion": "admission.k8s.io/v1", + "kind": "AdmissionReview", + "request": ... +} +``` + +When OPA receives the webhook request, it binds the payload to the `input` +document and generates the default decision: `system.main`. The `system.main` +decision is defined by a rule that evaluates all of the admission control +policies that have been loaded into OPA. + +As the administrator responsible for deploying OPA, you have full control over +the `system.main` decision (i.e., it is just another Rego policy.) A basic +implementation of the `system.main` policy simply evaluates all deny rules that +have been loaded into OPA and unions the results: + +```rego +package system + +import data.kubernetes.admission + +main := { + "apiVersion": "admission.k8s.io/v1", + "kind": "AdmissionReview", + "response": response, +} + +default uid := "" + +uid := input.request.uid + +response := { + "allowed": false, + "uid": uid, + "status": {"message": reason}, +} if { + reason := concat(", ", admission.deny) + reason != "" +} + +else := {"allowed": true, "uid": uid} +``` + +The `system.main` policy MUST generate an **AdmissionReview** object containing +a response that the API server can interpret. If the request should be allowed, +the `response.allowed` field should be true. Otherwise, the `response.allowed` +field should be set to `false` and the `response.status.message` field should be +set to include an error message that indicates why the request is being +rejected. The error message will be returned to the API server caller (e.g., the +user running `kubectl`). Often the error message is the concatenation of all the +messages in the `deny` set defined above. + +For example, with the input and Image Registry Safety examples above, the +response from OPA would be: + +```yaml +apiVersion: admission.k8s.io/v1 +kind: AdmissionReview +response: + uid: 8d836dfd-e0c0-4490-93ba-85ed4a04261e + allowed: false + status: + message: "image fails to come from trusted registry: nginx" +``` + +For more detail on how Kubernetes Admission Control works, see [this blog post](https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/) +on kubernetes.io. diff --git a/third_party/opa/docs/docs/kubernetes/tutorial.md b/third_party/opa/docs/docs/kubernetes/tutorial.md new file mode 100644 index 000000000000..c2474c750e59 --- /dev/null +++ b/third_party/opa/docs/docs/kubernetes/tutorial.md @@ -0,0 +1,564 @@ +--- +title: "Tutorial: Ingress Validation" +--- + +This tutorial shows how to deploy OPA as an admission controller from scratch. +It covers the OPA-kubernetes version that uses kube-mgmt. +The [OPA Gatekeeper version](https://open-policy-agent.github.io/gatekeeper) has its own docs. +For the purpose of the tutorial we will deploy two policies that ensure: + +- Ingress hostnames must be on allowlist on the Namespace containing the Ingress. +- Two ingresses in different namespaces must not have the same hostname. + +> 💡 Kubernetes does not guarantee consistency across resources. If two +> ingresses are created in parallel, there is no guarantee that OPA (or any +> other admission controller) will observe the creation of one ingress before +> the other. This means that it's not possible to enforce these policies during +> admission control 100% of the time. There will be a small window of time +> (usually on the order of milliseconds) when the eventually consistent cache +> inside of OPA (or any other admission controller) is out-of-date. To catch +> these violations we recommend you periodically audit the state of the cluster +> against your policies. Offline auditing is one of the features provided by the +> [OPA Gatekeeper](https://github.com/open-policy-agent/gatekeeper) project. + +## Prerequisites + +This tutorial requires Kubernetes 1.20 or later. To run the tutorial locally ensure you start a cluster with Kubernetes +version 1.20+, we recommend using [minikube](https://kubernetes.io/docs/getting-started-guides/minikube) or +[KIND](https://kind.sigs.k8s.io/). + +## Steps + +### 1. Enable recommended Kubernetes Admission Controllers + +To implement admission control rules that validate Kubernetes resources during +create, update, and delete operations, you must enable the +[ValidatingAdmissionWebhook](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#validatingadmissionwebhook) +when the Kubernetes API server is started. The ValidatingAdmissionWebhook +admission controller is included in the +[recommended set of admission controllers](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#is-there-a-recommended-set-of-admission-controllers-to-use) +to enable. + +Start minikube: + +```bash +minikube start +``` + +Make sure that the minikube ingress addon is enabled: + +```bash +minikube addons enable ingress +``` + +### 2. Create a new Namespace to deploy OPA into + +```bash +kubectl create namespace opa +``` + +Configure `kubectl` to use this namespace: + +```bash +kubectl config set-context opa-tutorial --user minikube --cluster minikube --namespace opa +kubectl config use-context opa-tutorial +``` + +### 3. Create TLS credentials for OPA + +Communication between Kubernetes and OPA must be secured using TLS. To configure TLS, use `openssl` to create a +certificate authority (CA) and certificate/key pair for OPA: + +```bash +openssl genrsa -out ca.key 2048 +openssl req -x509 -new -nodes -sha256 -key ca.key -days 100000 -out ca.crt -subj "/CN=admission_ca" +``` + +Generate the TLS key and certificate for OPA: + +```bash +cat >server.conf < Note: the Common Name value and Subject Alternative Name you give to openssl MUST match the name of the OPA service created below. + +Create a Secret to store the TLS credentials for OPA: + +```bash +kubectl create secret tls opa-server --cert=server.crt --key=server.key --namespace opa +``` + +### 4. Define OPA policy + +Let's define a couple of policies to test admission control. First create a new folder to store our policies: + +```bash +mkdir policies && cd policies +``` + +#### Policy 1: Restrict Hostnames + +Create a policy that restricts the hostnames that an ingress can use. Only hostnames matching the specified regular +expressions will be allowed. + +```rego title="ingress-allowlist.rego" +package kubernetes.admission + +import data.kubernetes.namespaces + +operations := {"CREATE", "UPDATE"} + +deny contains msg if { + input.request.kind.kind == "Ingress" + operations[input.request.operation] + host := input.request.object.spec.rules[_].host + not fqdn_matches_any(host, valid_ingress_hosts) + msg := sprintf("invalid ingress host %q", [host]) +} + +valid_ingress_hosts := {host | + allowlist := namespaces[input.request.namespace].metadata.annotations["ingress-allowlist"] + hosts := split(allowlist, ",") + host := hosts[_] +} + +fqdn_matches_any(str, patterns) if { + fqdn_matches(str, patterns[_]) +} + +fqdn_matches(str, pattern) if { + pattern_parts := split(pattern, ".") + pattern_parts[0] == "*" + suffix := trim(pattern, "*.") + endswith(str, suffix) +} + +fqdn_matches(str, pattern) if { + not contains(pattern, "*") + str == pattern +} +``` + +#### Policy 2: Prohibit Hostname Conflicts + +Now let's define another policy to test admission control. The following policy prevents Ingress objects in different +namespaces from sharing the same hostname. + +**ingress-conflicts.rego**: + +```rego title="ingress-conflicts.rego" +package kubernetes.admission + +import data.kubernetes.ingresses + +deny contains msg if { + some other_ns, other_ingress + input.request.kind.kind == "Ingress" + input.request.operation == "CREATE" + host := input.request.object.spec.rules[_].host + ingress := ingresses[other_ns][other_ingress] + other_ns != input.request.namespace + ingress.spec.rules[_].host == host + msg := sprintf("invalid ingress host %q (conflicts with %v/%v)", [host, other_ns, other_ingress]) +} +``` + +#### Combine Policies + +Let's define a main policy that imports the [Restrict Hostnames](#policy-1-restrict-hostnames) and +[Prohibit Hostname Conflicts](#policy-2-prohibit-hostname-conflicts) policies and provides an overall policy decision. + +```rego title="main.rego" +package system + +import data.kubernetes.admission + +main := { + "apiVersion": "admission.k8s.io/v1", + "kind": "AdmissionReview", + "response": response, +} + +default uid := "" + +uid := input.request.uid + +response := { + "allowed": false, + "uid": uid, + "status": {"message": reason}, +} if { + reason = concat(", ", admission.deny) + reason != "" +} + +else := {"allowed": true, "uid": uid} +``` + +> ⚠️ When OPA receives a request, it executes a query against the document defined `data.system.main` by default. + +### 5. Build and Publish OPA Bundle + +Build an OPA bundle containing policies defined in the previous step. In our setup, OPA will download policies from the +bundle service and the `kube-mgmt` container will load Kubernetes resources into OPA. Since we load policy and data into +OPA from multiple sources, we need to scope the bundle to a subset of OPA’s policy and data cache by defining a manifest. +More information about this can be found [here](../management-bundles#multiple-sources-of-policy-and-data). Run the +following commands in the `policies` folder created in the previous step. + +```bash +cat > .manifest < +``` +# Grant OPA/kube-mgmt read-only access to resources. This lets kube-mgmt +# replicate resources into OPA so they can be used in policies. +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: opa-viewer +roleRef: + kind: ClusterRole + name: view + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: Group + name: system:serviceaccounts:opa + apiGroup: rbac.authorization.k8s.io +--- +# Define role for OPA/kube-mgmt to update configmaps with policy status. +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + namespace: opa + name: configmap-modifier +rules: +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["update", "patch"] +--- +# Grant OPA/kube-mgmt role defined above. +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + namespace: opa + name: opa-configmap-modifier +roleRef: + kind: Role + name: configmap-modifier + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: Group + name: system:serviceaccounts:opa + apiGroup: rbac.authorization.k8s.io +--- +kind: Service +apiVersion: v1 +metadata: + name: opa + namespace: opa +spec: + selector: + app: opa + ports: + - name: https + protocol: TCP + port: 443 + targetPort: 8443 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: opa + namespace: opa + name: opa +spec: + replicas: 1 + selector: + matchLabels: + app: opa + template: + metadata: + labels: + app: opa + name: opa + spec: + containers: + # WARNING: OPA is NOT running with an authorization policy configured. This + # means that clients can read and write policies in OPA. If you are + # deploying OPA in an insecure environment, be sure to configure + # authentication and authorization on the daemon. See the Security page for + # details: https://www.openpolicyagent.org/docs/security.html. + - name: opa + image: openpolicyagent/opa:{{ current_version_docker }} + args: + - "run" + - "--server" + - "--tls-cert-file=/certs/tls.crt" + - "--tls-private-key-file=/certs/tls.key" + - "--addr=0.0.0.0:8443" + - "--addr=http://127.0.0.1:8181" + - "--set=services.default.url=http://host.minikube.internal:8888" + - "--set=bundles.default.resource=bundle.tar.gz" + - "--log-format=json-pretty" + - "--set=status.console=true" + - "--set=decision_logs.console=true" + volumeMounts: + - readOnly: true + mountPath: /certs + name: opa-server + readinessProbe: + httpGet: + path: /health?plugins&bundle + scheme: HTTPS + port: 8443 + initialDelaySeconds: 3 + periodSeconds: 5 + livenessProbe: + httpGet: + path: /health + scheme: HTTPS + port: 8443 + initialDelaySeconds: 3 + periodSeconds: 5 + - name: kube-mgmt + image: openpolicyagent/kube-mgmt:{{ current_version_kube_mgmt }} + args: + - "--replicate-cluster=v1/namespaces" + - "--replicate=networking.k8s.io/v1/ingresses" + volumes: + - name: opa-server + secret: + secretName: opa-server +``` + + +> ⚠️ If using `kind` to run a local Kubernetes cluster, the bundle service URL should be `http://host.docker.internal:8888`. + +```bash +kubectl apply -f admission-controller.yaml +``` + +When OPA starts, the `kube-mgmt` container will load Kubernetes Namespace and Ingress objects into OPA. You can +configure the sidecar to load any kind of Kubernetes object into OPA. The sidecar establishes watches on the +Kubernetes API server so that OPA has access to an eventually consistent cache of Kubernetes objects. + +Next, generate the manifest that will be used to register OPA as an admission controller. This webhook will ignore +any namespace with the label `openpolicyagent.org/webhook=ignore`. + +```bash +cat > webhook-configuration.yaml <Gg83?r)cE#*n9T2at^yq1 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

hIDdHG;(?osz!h*=I`k#XC-)=3rhWR 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

x~BvDIdytbOP^>`^R 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 literal 0 HcmV?d00001 diff --git a/third_party/opa/docs/docs/management-bundles/index.md b/third_party/opa/docs/docs/management-bundles/index.md new file mode 100644 index 000000000000..86f9274efc32 --- /dev/null +++ b/third_party/opa/docs/docs/management-bundles/index.md @@ -0,0 +1,1470 @@ +--- +title: "Bundles" +--- + +Many use cases require that OPA reload policy and related data while serving +requests. OPA is commonly deployed as a supporting role to applications and +other callers, so it's often desirable to be able to update policies quicker +than would be possible with a full redeployment. +Frequent data updates are another driver for this functionality. + +Updated policies and data are loaded on the fly without requiring a restart of +OPA. Once the policies and data have been loaded, they are enforced immediately. +Policies and data loaded from bundles are accessible via the standard OPA +[REST API](./rest-api) to callers. + +Bundles provide an alternative to pushing policies into OPA via the REST APIs. +By configuring OPA to download bundles from a remote HTTP server, you can +ensure that OPA has an up-to-date copy of policies and data required for +enforcement at all times in an eventually consistent manner. + +By default, the OPA REST APIs will prevent you from modifying policy and data +loaded via bundles. If you need to load policy and data from multiple sources, +see the section below. + +See the [Configuration Reference](./configuration) for configuration details. + +### Bundle build + +The CLI command [`opa build`](./cli/#opa-build) gives you the capability to build your own bundles. + +Here is a basic example on how to build a bundle from a folder called `foo`. The bundle will be named by default `bundle.tar.gz`. + +```console +$ ls foo/ +example.rego + +$ opa build -b foo/ +``` + +More, you can optimize the bundle by specifying the `--optimize` or `-O` flag. + +```console +opa build -b foo/ --optimize=1 +``` + +Finally, you can also sign your bundle with `opa build`. + +```console +opa build --verification-key /path/to/public_key.pem --signing-key /path/to/private_key.pem --bundle foo/ +``` + +For more information, see the [`opa build` command documentation.](./cli/#opa-build) + +### Bundle Service API + +OPA expects the service to expose an API endpoint that serves bundles. The +bundle API should allow clients to download bundles at an arbitrary URL. In +combination with a service's `url` path. + +```http +GET // HTTP/1.1 +``` + +If the bundle exists, the server should respond with an HTTP 200 OK status +followed by a gzipped tarball in the message body. + +```http +HTTP/1.1 200 OK +Content-Type: application/gzip +``` + +Enable bundle downloading via configuration. For example: + +```yaml +services: +- name: acmecorp + url: https://example.com/service/v1 + credentials: + bearer: + token: "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm" + +bundles: + authz: + service: acmecorp + resource: somedir/bundle.tar.gz + persist: true + polling: + min_delay_seconds: 10 + max_delay_seconds: 20 + signing: + keyid: my_global_key + scope: read +``` + +Using this configuration, OPA will fetch bundles from +`https://example.com/service/v1/somedir/bundle.tar.gz`. + +The URL is constructed as follows: + +``` +https://example.com/service/v1/somedir/bundle.tar.gz +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +services[0].url resource +``` + +If the `bundles[_].resource` field is not defined, the value defaults to +`bundles/` where the `name` is the key value in the configuration. For the +example above this is `authz` and would default to `bundles/authz`. + +Bundle names can have any valid YAML characters in them, including `/`. This can +be useful when relying on default `resource` behavior with a name like +`authz/bundle.tar.gz` which results in a `resource` of +`bundles/authz/bundle.tar.gz`. + +OPA can optionally persist activated bundles to disk for recovery purposes. To enable +persistence, set the `bundles[_].persist` field to `true`. When bundle +persistence is enabled, OPA will attempt to read the bundle from disk on startup. This +allows OPA to start with the most recently activated bundle in case OPA cannot communicate +with the bundle server. OPA will try to load and activate persisted bundles on a best-effort basis. Any errors +encountered during the process will be surfaced in the bundle's status update. When communication between OPA and +the bundle server is restored, the latest bundle is downloaded, activated, and persisted. + +:::info +By default, bundles are persisted under the current working directory of the OPA process (e.g., `./.opa/bundles//bundle.tar.gz`). +::: + +The optional `bundles[_].signing` field can be used to specify the `keyid` and `scope` that should be used +for verifying the signature of the bundle. See [this](#signing) section for details. + +See the following section for details on the bundle file format. + +#### Caching + +Services implementing the Bundle Service API should set the HTTP `Etag` header +in bundle responses to identify the revision of the bundle. OPA will include the +`Etag` value in the `If-None-Match` header of bundle requests. Services can +check the `If-None-Match` header and reply with HTTP `304 Not Modified` if the +bundle has not changed since the last update. + +#### HTTP Long Polling + +With the periodic bundle downloading (ie. `short polling`) technique, OPA sends regular requests to the remote HTTP +server to pull any available bundle. If there is no new bundle, the server responds with a `304 Not Modified` response. +The polling frequency depends on the latency that the client can tolerate in +retrieving updated information from the server. A drawback of this +method is that if the acceptable latency is low, then the polling frequency could add unnecessary +burden on the server and/or network. + +[HTTP Long Polling](https://datatracker.ietf.org/doc/html/rfc6202#section-2) helps to minimize server/network resource +usage and also reduces the delay in delivery of updates to the client. When OPA sends a long poll request to the server, +it defers its response until an update is available or timeout has occurred. In case of a timeout, the server responds +with a `304 Not Modified` response. + +The below configuration shows how to enable bundle downloading via `long polling`: + +```yaml +services: +- name: acmecorp + url: https://example.com/service/v1 + credentials: + bearer: + token: "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm" + +bundles: + authz: + service: acmecorp + resource: somedir/bundle.tar.gz + persist: true + polling: + long_polling_timeout_seconds: 10 + signing: + keyid: my_global_key + scope: read +``` + +With the above configuration, OPA sends a long poll request to the server with a timeout set to `10` seconds. If the server +supports `long polling`, OPA expects the server to set the `Content-Type` header to `application/vnd.openpolicyagent.bundles`. +If the server does not support `long polling`, OPA will fallback to the regular periodic polling. + +### Bundle File Format + +Bundle files are gzipped tarballs (`.tar.gz`) that contain policies and/or +data. + +Policy files are Rego source files with the `.rego` extension and will be +available within Rego modules based on their package's path, +e.g. `package example.authz` is available at `data.example.authz` in the +[`data` Document](./philosophy/#the-opa-document-model). + +The data files within the bundle can be organized hierarchically into +directories inside the tarball. The hierarchical organization indicates to OPA +where to load the data files into the `data` Document - similar to how Rego +files can control their location using the package path. This functionality +can be useful when: + +- Deploying larger bundles with policy for different callers and use cases. +- Creating bundles where different teams manage different parts of the bundle. + For example, some shared policy is to be loaded alongside some application + specific policy and data. +- When some parts of the data are to be updated more frequently than others, + e.g. using [Delta Bundles](#delta-bundles). + +You can list the content of a bundle with `tar`: + +```bash +$ tar tzf bundle.tar.gz +.manifest +roles +roles/bindings +roles/bindings/data.json +roles/permissions +roles/permissions/data.json +http +http/example +http/example/authz +http/example/authz/authz.rego +``` + +In this example, the bundle contains one policy file (`authz.rego`) and two +data files (`roles/bindings/data.json` and `roles/permissions/data.json`). +A data file in the root of the bundle will be loaded into the `data` Document at +the root. For example, here we can see that the `foo` key is inserted at the +root of the `data` document: + +```sh +$ tree bundle +bundle +└── data.json + +1 directory, 1 file +$ cat bundle/data.json +{ "foo": true } +$ opa eval -b bundle/ data.foo --format=raw +true +``` + +The bundle may also contain an optional Wasm binary file (`policy.wasm`). +OPA stores the WebAssembly compiled version of all the Rego policy files within +the bundle in `policy.wasm` when building with `-t wasm`: + +```sh +$ cat -p bundle/http/example/authz/authz.rego +package http.example.authz + +allow := true +$ opa build -t wasm -e http/example/authz/allow bundle +$ tar tzf bundle.tar.gz +/data.json +/bundle/http/example/authz/authz.rego +/policy.wasm +/.manifest +$ opa eval -b bundle.tar.gz data --format=raw +{"http":{"example":{"authz":{"allow":true}}}} +``` + +Bundle files may contain an optional `.manifest` file that stores bundle +metadata. The file should contain a JSON serialized object, with the following +fields: + +- `revision` - If the bundle service is capable of serving different revisions of the same + bundle, the service should include a top-level `revision` field containing a + `string` value that identifies the bundle revision. + +- `rego_version` - An optional field that specifies the rego-version of the Rego source files + in the bundle. The value of this field is an `integer`; where `0` corresponds to v0 Rego ([OPA v0.x](./v0-compatibility/) syntax), + and `1` corresponds to v1 Rego (current OPA v1.x syntax). + If the field is not included in the manifest, OPA will enforce v1 syntax, or v0 if executed with + the `--v0-compatible` flag. + An existing bundle `rego_version` field takes precedence to the `--v0-compatible` flag. + +- `file_rego_versions` - An optional field that specifies per-file rego-version overrides to the + `rego_version` field. The value of this field is a `map` where the keys are file paths relative to the + bundle root directory (paths are absolute and start with `/`) and the values are `integer` rego-versions. + Glob patterns are accepted, to allow for a single entry to apply to multiple files. The behaviour is undefined + for overlapping patterns. If a file is not matched by any pattern, the `rego_version` field is used. + Existing bundle `rego_version` and `file_rego_versions` fields takes precedence to the `--v0-compatible` flag. + +- `roots` - If you expect to load additional data into OPA from outside the + bundle (e.g., via OPA's HTTP API) you should include a top-level + `roots` field containing of path prefixes that declare the scope of + the bundle. See the section below on managing data from multiple + sources. If the `roots` field is not included in the manifest it + defaults to `[""]` which means that ALL data and policy must come + from the bundle. + +- `wasm` - A list of OPA WebAssembly (Wasm) module files in the bundle along with + metadata for how they should be evaluated. The following keys are supported: + - `entrypoint` - A string path defining what query path the wasm module is + built to evaluate. Once loaded any usage of this path in a query will use + the Wasm module to compute the value. + - `module` - A string path to the Wasm module relative to the root of the bundle. + +- `metadata` - An optional key that contains arbitrary metadata to accompany the + bundle. This metadata is available for querying using `data.system`, along with the + rest of the manifest. + +For example, this manifest specifies a revision (which happens to be a Git +commit hash) and a set of roots for the bundle contents. In this case, the +manifest declares that it owns the roots `data.roles` and +`data.http.example.authz`. + +```json +{ + "revision": "7864d60dd78d748dbce54b569e939f5b0dc07486", + "roots": ["roles", "http/example/authz"] +} +``` + +Another example, this time showing a Wasm module configured for +`data.http.example.authz.allow`: + +```json +{ + "revision": "7864d60dd78d748dbce54b569e939f5b0dc07486", + "roots": ["roles", "http/example/authz"], + "wasm": [ + { + "entrypoint": "http/example/authz/allow", + "module": "path/to/policy.wasm" + } + ] +} +``` + +For example, the manifest below specifies the global Rego version for the bundle using the `rego_version` field and +uses the `file_rego_versions` field for overrides. This manifest describes a bundle that follows the OPA v1.0 syntax +expect for policy files `/policy1.rego` and those under the folder `foo`. + +```json +{ + "revision": "7864d60dd78d748dbce54b569e939f5b0dc07486", + "rego_version": 1, + "file_rego_versions": { + "/foo/*.rego": 0, + "/policy1.rego": 0 + } +} +``` + +Some important details for bundle files: + +- OPA will only load data files named `data.json` or `data.yaml` (which contain + JSON or YAML respectively). Other JSON and YAML files will be ignored. +- The `*.rego` policy files must be valid [Modules](./policy-language/#modules). +- OPA will only load Wasm modules named `policy.wasm`. Other WebAssembly binary + files will be ignored. + +:::info +YAML data loaded into OPA is converted to JSON. Since JSON is a subset of +YAML, you are not allowed to use binary or null keys in objects and boolean +and number keys are converted to strings. +[YAML `!!binary` tags](https://ref.coddy.tech/yaml/yaml-binary-data) are not +supported. +::: + +### Multiple Sources of Policy and Data + +By default, when OPA is configured to download policy and data from a +bundle service, the entire content of OPA's policy and data cache is +defined by the bundle. However, if you need to load OPA with policy +and data from multiple sources, you can implement your bundle service +to generate bundles that are scoped to a subset of OPA's policy and +data cache. + +:::danger +We recommend that whenever possible, you implement policy and data +aggregation centrally, however, in some cases that's not possible +(e.g., due to latency requirements.). +When using multiple sources there are **no** ordering guarantees for which bundle loads first and +takes over some root. If multiple bundles conflict, but are loaded at different +times, OPA may go into an error state. It is highly recommended to use +the health check and include bundle state: [Monitoring OPA](./monitoring#health-checks) +::: + +To scope bundles to a subset of OPA's policy and data cache, include +a top-level `roots` key in the bundle that defines the roots of the +`data` namespace that are owned by the bundle. + +For example, the following manifest would declare two roots +(`acmecorp/policy` and `acmecorp/oncall`): + +``` +{ + "roots": ["acmecorp/policy", "acmecorp/oncall"] +} +``` + +If OPA was loaded with a bundle containing this manifest it would only +erase and overwrite policy and data under these roots. Policy and data +loaded under other roots is left intact. + +When OPA loads scoped bundles, it validates that: + +- The roots are not overlapping (e.g., `a/b/c` and `a/b` are + overlapped and will result in an error.) Note: This is _not_ + enforced across multiple bundles. Only within the same bundle + manifest. + +- The policies in the bundle are contained under the roots. This is + determined by inspecting the `package` statement in each of the + policy files. For example, given the manifest above, it would be an + error to include a policy file containing `package acmecorp.other` + because `acmecorp.other` is not contained in either of the roots. + +- The data in the bundle is contained under the roots. + +If bundle validation fails, OPA will report the validation error via +the Status API. + +### Debugging Your Bundles + +When you run OPA, you can provide bundle files over the command line. This +allows you to manually check that your bundles include all of the files that +you intended and that they are structured correctly. For example: + +```bash +opa run bundle.tar.gz +``` + +### Signing + +To ensure the integrity of policies (ie. the policies are coming from a trusted source), policy bundles may be +digitally signed so that industry-standard cryptographic primitives can verify their authenticity. + +OPA supports digital signatures for policy bundles. Specifically, a signed bundle is a normal OPA bundle that includes +a file named `.signatures.json` that dictates which files should be included in the bundle, what their SHA hashes are, +and of course is cryptographically secure. + +When OPA receives a new bundle, it checks that it has been properly signed using a (public) key that OPA has been +configured with out-of-band. Only if that verification succeeds does OPA activate the new bundle; otherwise, OPA +continues using its existing bundle and reports an activation failure via the status API and error logging. + +:::warning +⚠️ `opa run` performs bundle signature verification only when the `-b`/`--bundle` flag is given +or when Bundle downloading is enabled. Sub-commands primarily used in development and debug environments +(such as `opa eval`, `opa test`, etc.) DO NOT verify bundle signatures at this point in time. +::: + +#### Signature Format + +Recall that a [policy bundle](#bundle-file-format) is a gzipped tarball that contains policies and data. A signed bundle +differs from a normal bundle in that it has a `.signatures.json` file as well. + +```bash +$ tar tzf bundle.tar.gz +.manifest +.signatures.json +roles +roles/bindings +roles/bindings/data.json +``` + +The signatures file is a JSON file with an array of JSON Web Tokens (JWTs) that encapsulate the signatures for the bundle. +Currently, you will be limited to one signature, as shown below. In the future, we may add support to include multiple +signatures to sign different files within the bundle. + +```json +{ + "signatures": [ + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaWxlcyI6W3sibmFtZSI6Ii5tYW5pZmVzdCIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoiU0hBMjU2In0seyJuYW1lIjoicm9sZXMvYmluZGluZ3MvZGF0YS5qc29uIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsImtleWlkIjoibXlQdWJsaWNLZXkiLCJzY29wZSI6IndyaXRlIn0.ZjtUgXC6USwmhv4XP9gFH6MzZwpZrGpAL_2sTK1P-mg" + ] +} +``` + +The JWT has the standard headers `alg` (for algorithm), `typ` (always JWT), and `kid` (for key id). It has a JSON payload of the +following form: + +```json +{ + "files": [ + { + "name": ".manifest", + "hash": "c2131544c716a25a5e31f504300f5240e8235cadb9a57f0bd1b6f4bd74b26612", + "algorithm": "SHA-256" + }, + { + "name": "roles/bindings/data.json", + "hash": "42cfe6768b57bb5f7503c165c28dd07ac5b813554ebc850f2cc35843e7137b1d", + "algorithm": "SHA-256" + } + ] +} +``` + +| Field | Type | Required | Description | +| -------------------- | -------- | -------- | ----------------------------------------------------------------------------------- | +| `files[_].name` | `string` | Yes | Path of a file in the bundle. | +| `files[_].hash` | `string` | Yes | Output of the hashing algorithm applied to the file. | +| `files[_].algorithm` | `string` | Yes | Name of the hashing algorithm. | +| `scope` | `string` | No | Represents the fragment of signings. | +| `iat` | `string` | No | Time of signature creation since epoch in seconds. For informational purposes only. | +| `iss` | `string` | No | Identifies the issuer of the JWT. For informational purposes only. | + +:::info +OPA will first look for the `keyid` on the command-line. If the `keyid` is empty, OPA will look for it in it's +configuration. If `keyid` is still empty, OPA will finally look for `kid` in the JWT header. + +To include additional claims in the JWT payload such as `scope`, `iat`, `iss` use the `--claims-file` flag +in the `opa build` or `opa sign` commands to provide a JSON file containing optional claims. See `opa build --help` +or `opa sign --help` for more details. +::: + +The following hashing algorithms are supported: + + MD5 + SHA-1 + SHA-224 + SHA-256 + SHA-384 + SHA-512 + SHA-512-224 + SHA-512-256 + +To calculate the digest for unstructured files (ie. all files except JSON or YAML files), apply the hash +function to the byte stream of the file. + +For structured files, read the byte stream and parse into a JSON structure; then recursively order the fields of all +objects alphabetically and then apply the hash function to the result to compute the hash. This ensures +that the digital signature is independent of whitespace and other non-semantic JSON features. + +To generate a `.signatures.json` file for policy and data files that will be part of a bundle, see the `opa sign` command. + +#### Signature Verification + +When OPA receives a policy bundle that doesn't include the `.signatures.json` file and the bundle is not configured to +use a signature, OPA does not perform signature verification and activates the bundle just as it always has. + +If the actual bundle contains the `.signatures.json` file but the bundle is not configured to use a signature, verification fails. + +| `.signatures.json` exists | bundle configured to verify signature | verification performed | result | +| ------------------------- | ------------------------------------- | ---------------------- | --------------------------------------------------- | +| `no` | `no` | `no` | `NA` | +| `no` | `yes` | `yes` | `fail` | +| `yes` | `no` | `yes` | `fail` | +| `yes` | `yes` | `yes` | `depends on the verification steps described below` | + +When OPA receives a signed bundle it opens the `.signatures.json` file, grabs the JWT and performs the following steps: + +- Verify the JWT signature with the appropriate public key + +- Verify that the JWT payload and target directory specify the same set of files + +- Verify the content of each file by checking the hash recorded in the JWT payload is the same as the hash generated + for that file + +OPA activates the new bundle only if all the verification steps succeed; otherwise, it continues using its existing bundle +and reports an activation failure via the status API and error logging. + +The signature verification process uses each of the fields in the JWT header and payload as follows: + +- `files`: This list of files in the payload must match exactly the files in the bundle, and for each file the hash of the file must match + +- `kid`: If supplied in the header, dictates which key (and algorithm) to use for verification. The actual key is supplied via + OPA out-of-band + +- `scope`: If supplied in the payload, must match exactly the value provided out-of-band to OPA + +- `iat`: unused for verification even if present in payload + +- `iss`: unused for verification even if present in payload + +#### Signature Plugin + +OPA supports the option to implement your own bundle signing and verification logic. This will be unnecessary +for most and is intended for advanced use cases, such as leveraging key-related services from cloud providers. +To implement your own signing and verification logic, you'll need to [extend OPA](./extensions). Here is +[an example](https://github.com/open-policy-agent/contrib/tree/main/custom_bundle_signing) to get you started. + +When registering custom signing and verification plugins, you will need to register the Signer and the Verifier +under the same plugin key, because the plugin key is stored in the signed bundle and informs OPA which Verifier +is capable of verifying the bundle, e.g. + +```go +bundle.RegisterSigner("custom", &CustomSigner{}) +bundle.RegisterVerifier("custom", &CustomVerifier{}) +``` + +### Delta Bundles + +A regular _snapshot_ bundle represents the entirety of OPA’s policy and data cache. When a new _snapshot_ bundle is +downloaded, OPA will erase and overwrite all the policy and data in its cache before activating the new bundle. We can +optionally scope the bundle to a subset of OPA’s policy and data cache by defining the `roots` in the bundle's `.manifest` file. + +Although OPA [caches](#caching) snapshot bundles to avoid unnecessary retransmission, +servers must still retransmit the entire snapshot when any change occurs. If you need +to propagate small changes to bundles without waiting for polling delays, consider +using _delta_ bundles in conjunction with [HTTP Long Polling](#http-long-polling). + +_Delta_ bundles provide a more efficient way to make data changes by containing patches to data instead of complete snapshots. +_Delta_ bundles are structured differently from _snapshot_ bundles. A _delta_ bundle contains a +single `patch.json` file at the root of the bundle which includes a [JSON Patch](https://datatracker.ietf.org/doc/html/rfc6902) +(i.e., an array of one or more JSON objects). The operations in the JSON Patch will be applied to OPA's in-memory store in order. + +:::info +_Delta_ bundles currently support updates to data only and not policies. +::: + +#### Delta Bundle File Format + +OPA expects a _delta_ bundle to contain an optional `.manifest` file and a required `patch.json` file that specifies a list of one or more +patch operations on the data. OPA will generate an error if a _delta_ bundle contains any policy, data or wasm binary files. +If the `.manifest` file specifies any `roots`, any data patch outside the bundle's roots will cause an error. + +```bash +$ tar tzf bundle.tar.gz +.manifest +patch.json +``` + +Below is an example of the `patch.json` file: + +```json +{ + "data": [ + { "op": "upsert", "path": "/a/b", "value": ["hello", "world"] }, + { "op": "remove", "path": "/a/c" } + ] +} +``` + +If OPA has previously activated a _snapshot_ bundle that did not contain a .manifest file, then the _delta_ bundle +must not contain a `.manifest` file. + +If OPA has a previously activated _snapshot_ bundle that did contain a `.manifest` file, then the _delta_ bundle may +contain a `.manifest` file. Specifically if a previously activated _snapshot_ bundle contains a `.manifest` file that +declares `roots` or `wasm` fields, a _delta_ bundle update MUST have the same values for the manifest +`roots` and `wasm` fields from the original _snapshot_ bundle. This means a _delta_ bundle cannot be used to change +the scope of the original bundle or update Wasm resolvers. A _delta_ bundle can however contain different +values for the bundle's `revision` and `metadata`. + +:::danger +An empty list of operations in a _delta_ bundle `patch.json` will remove all the data from OPA's in-memory store. I.e., the following are equivalent: + +```json +{ + "data": [] +} +``` + +```json +{ + "data": [ + { "op": "replace", "path": "/", "value": {} } + ] +} +``` + +If there are no operations to apply to the data, the bundle server should return the same [`Etag`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) value as the last update. OPA will send the last `Etag` value in the [`If-None-Match`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match) Header. +::: + +#### Delta Bundle Patch Operations + +Each patch operation defined in the `patch.json` file must have exactly one `op` member which indicates the +operation to perform. Valid options include: + +| op | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `"remove"` | The `"path"` specified will be removed from OPA's in-memory store. The `"value"` field is ignored for `"remove"` operations. The target path must exist for the operation to be successful. | +| `"replace"` | The value at the specified `"path"` will be replaced by the new value defined by the `"value"` field. The target path must exist for the operation to be successful. | +| `"upsert"` | The `"value"` will be set at the specified `"path"`. If the `"path"` specifies an array index, the `"value"` is inserted into the array at the specified index. If the `"path"` specifies an object member that does not already exist, a new member is added to the object. If the object member exists, its value is replaced. If the `"path"` does not exist, OPA will create and add it to its in-memory store. | + +:::info +The `upsert` operation in not part of the [JSON Patch](https://datatracker.ietf.org/doc/html/rfc6902) standard. +::: + +The `"path"` field defines a JSON pointer path to the location to perform the operation on. + +The `"value"` field defines the value to be added or replaced. Only required for `"upsert"` and `"replace"` operations. + +#### Current Limitations + +- _Delta_ bundles only support updates to data. Policies cannot be updated using _delta_ bundles. +- _Delta_ bundles do not support bundle signing. +- Unlike _snapshot_ bundles, activated _delta_ bundles are not persisted to disk when the `bundles[_].persist` field is `true`. + +#### Delta Bundle FAQ + +This section discusses some _delta_ bundle usage, edge cases and failure scenarios. + +- What happens if OPA cannot apply a data patch ? + +Bundle activation will fail in this scenario. In the next attempt to download the bundle, OPA will set the value +of the `If-None-Match` header of the bundle request to the last successful activation Etag value. This should help the +Bundle Service to send the correct revision of the bundle to OPA. + +- What happens if OPA cannot reach the Bundle Service (for example. network failure) or is unable to download a bundle ? + +OPA always includes the last successful activation Etag value in the bundle request. When OPA eventually reconnects +with the server, the value of the `If-None-Match` header of bundle request could be empty indicating that OPA was not +able to activate the first revision of the bundle itself. This helps the server to re-transmit the correct bundle revision. + +In case OPA has already activated a revision of the bundle, and reaches out to the server with the last +successful activation Etag value, the server now knows to send the next bundle revision. This could either be a snapshot +or delta bundle. One possible approach on the server-side, would be to first send a snapshot bundle and then send delta bundles +to perform data patch operations. The server could maintain the order in which the bundles should go out for example, +assigning an Etag value to each bundle revision. Hence, it can figure out the right bundle to send by looking up the +`If-None-Match` header of bundle request and then lining-up the next bundle in the queue. + +- Does a _delta_ bundle always need to be preceded by a _snapshot_ bundle ? + +No. OPA will activate a _delta_ bundle if all the patch operations in it were successfully applied. Note that a _snapshot_ +bundle would erase and overwrite policy and data under the manifest `roots`. + +## Implementations + +The Bundle API is simple. Most HTTP servers capable of serving static files will do. While not strictly required in all deployments, it is also good if the implementation supports: + +- HTTP caching using the [ETag header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag). This keeps OPA from having to download a bundle unless the bundle's content have changes. +- Authentication. When exposing a bundle at a remote endpoint, it is often desirable to protect the data by requiring all requests to the endpoint to be authenticated. + +This document lists some of the more common HTTP servers suitable as bundle servers, along with instructions for how to set them up as such. + +### Amazon S3 + +#### OPA Bundle Support + +| Feature | Supported | +| ---------------------- | ----------------------------------------------------------------------------------------- | +| Caching headers | Yes | +| Authentication methods | [AWS Signature](https://www.openpolicyagent.org/docs/latest/configuration/#aws-signature) | + +#### Setup Instructions + +1. Search for "S3" and on the "Buckets" page, click "Create bucket". +2. Fill in the form according to your preferences (name, region, etc). +3. Either choose "Block all public access" for internal systems, or unmark the checkbox for that to allow external (authenticated) requests. +4. You can now upload your bundle to the bucket. If you try to download it right away you'll notice that by default you're unauthorized to do so. +5. To allow anyone to read the bundle, click on it and select "Make public" from the "Object actions" dropdown menu. If not, proceed to configure authentication. + +#### Authentication + +Authentication can be configured to either use the credentials of a service account stored in the environment, or to use credentials fetched from the AWS metadata API. The latter is only available from services running inside of AWS (on EC2 or ECS). + +Both methods are going to need a policy for either the service account or the IAM role, so when that is mentioned in the steps for either method you may refer to the example below. + +**Example IAM policy** + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "s3:ListBucket" + ], + "Resource": [ + "arn:aws:s3:::my-example-opa-bucket" + ] + }, + { + "Effect": "Allow", + "Action": [ + "s3:PutObject", + "s3:GetObject" + ], + "Resource": [ + "arn:aws:s3:::my-example-opa-bucket/*" + ] + } + ] +} +``` + +**NOTE:** The above policy permits both uploads and downloads, which is good for testing. The OPA client however needs only the `s3:GetObject` permission for downloads and should be the only permission granted for production use cases. + +##### Environment Credentials + +1. Go to the "IAM" section of the AWS console. Choose "Users" and "Create new user". Select a name for the user, and the "Programmatic access" option. +2. On the following "Permissions" page, choose "Attach existing policies directly" and then press "Create policy". Select the JSON tab and paste a policy like the example shown above, replacing `my-example-opa-bucket` with the name of your bucket. +3. Once the policy has been created, it can be assigned to the user. With the user having been created, make sure to note down the AWS access key ID and the AWS secret access key, as they will be the credentials used for authentication. + +##### Metadata Credentials + +1. Go to the "IAM" section of the AWS console. Choose "Roles" and "Create role". For type, select "AWS service" and for use case, choose EC2, or wherever you'll be running OPA. +2. On the following "Permissions" page, choose "Create policy". Select the JSON tab and paste a policy like the example shown above, replacing `my-example-opa-bucket` with the name of your bucket. +3. Once the policy has been created, it can be assigned to the role. +4. With the role created, go to the EC2 instance view. Select an instance where OPA will run and select "Actions" -> "Security" -> "Modify IAM role". Select the role created in previous steps. + +##### Web Identity Credentials + +Using EKS IAM Roles for Service Account (Web Identity) Credential. + +Below are steps to use OpenID connect provider and kubernetes. + +1. Go to the "IAM" section of the AWS console. +2. Click Add provider and select OpenID connect. +3. For Provider URL enter the one belonging to your chosen kubernetes cluster. +4. Click on Get thumbprint +5. For the audience enter: sts.amazonaws.com +6. Add the provider. +7. Once the provider is added, copy the ARN for the identity provider. Here's an example ARN: `arn:aws:iam:::oidc-provider/oidc.eks.ap-northeast-1.amazonaws.com/id/DFGHJKKJHGF34HFDFGHY44TRFDE4RGDF` +8. Create an IAM role (eg: app_dev_role) with the policy created above and assign it to the kubernetes service account. +9. Go to Trust relationships inside the created role and click Edit trust relationship and enter the following policy document. + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + ":sub": "system:serviceaccount::" + } + } + } + ] +} +``` + +10. Create the kubernetes service account. + ```yaml + apiVersion: v1 + kind: ServiceAccount + metadata: + annotations: + eks.amazonaws.com/role-arn: :role/app_dev_role> + name: + namespace: + automountServiceAccountToken: false + ``` +11. Configure your kubernetes resources to use this service account. + ```yaml + apiVersion: apps/v1 + kind: Deployment + metadata: + ****** + spec: + ****** + template: + ******* + spec: + serviceAccountName: app-dev-service-account # <--- like this + automountServiceAccountToken: true + containers: + ****** + ``` + +You should now be able to access AWS services from your kubernetes cluster. + +The above steps should add the following variable to the pod. + +```bash +AWS_ROLE_ARN= +AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token +``` + +Please read [IAM roles for service accounts](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) for more details. + +##### Testing Authentication + +Use the [AWS CLI tools](https://aws.amazon.com/cli/) (see ["Upload Bundle"](#upload-bundle) below). + +#### Upload Bundle + +Bundle uploads to S3 are easily facilitated using the `aws` command in the [AWS CLI tools](https://aws.amazon.com/cli/). + +```shell +aws --profile=opa-service-account s3 cp bundle.tar.gz s3://my-example-opa-bucket/ +``` + +#### Example OPA Configuration + +##### Environment Credentials + +With the environment variables `AWS_REGION`, `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` set, the following configuration will extract the credentials from the [environment](https://www.openpolicyagent.org/docs/latest/configuration/#using-static-environment-credentials). + +```yaml +services: + s3: + url: https://my-example-opa-bucket.s3.eu-north-1.amazonaws.com + credentials: + s3_signing: + environment_credentials: {} + +bundles: + authz: + service: s3 + resource: bundle.tar.gz +``` + +**NOTE:** the S3 `url` is the bucket's regional endpoint. + +##### Metadata Credentials + +In order for this to work it is required that the permissions you created in the "Authentication" steps above are embedded in an IAM Role, which is then assigned to the EC2 instance hosting OPA. + +```yaml +services: + s3: + url: https://my-example-opa-bucket.s3.eu-north-1.amazonaws.com + credentials: + s3_signing: + metadata_credentials: + aws_region: eu-north-1 + iam_role: my-opa-bucket-access-role + +bundles: + authz: + service: s3 + resource: bundle.tar.gz +``` + +**NOTE:** the S3 `url` is the bucket's regional endpoint. + +##### Assume Role Credentials + +```yaml +services: + s3: + url: https://my-example-opa-bucket.s3.us-east-1.amazonaws.com + credentials: + s3_signing: + assume_role_credentials: + aws_region: us-east-1 + iam_role_arn: arn:aws::iam::123456789012:role/demo + session_name: my-open-policy-agent # Optional. Default: open-policy-agent + aws_signing: # similar to s3_signing + metadata_credentials: + aws_region: us-east-1 + iam_role: s3access + +bundles: + authz: + service: s3 + resource: bundle.tar.gz +``` + +**NOTE:** the S3 `url` is the bucket's regional endpoint. + +##### Web Identity Credentials + +```yaml +services: + s3: + url: https://my-example-opa-bucket.s3.eu-north-1.amazonaws.com + credentials: + s3_signing: + web_identity_credentials: + aws_region: eu-north-1 + session_name: my-open-policy-agent # Optional. Default: open-policy-agent + +bundles: + authz: + service: s3 + resource: bundle.tar.gz +``` + +**NOTE:** the S3 `url` is the bucket's regional endpoint. + +##### Credential Provider Chaining + +Multiple AWS credential providers can be configured. OPA will follow an _internally defined_ order to try each of the credential provider given in the configuration till success. Following order of precedence is followed when multiple credential provider is given in the configuration + +1. Environment Credential +1. Assume Role Credential +1. Web Identity Credential +1. Profile Credential +1. Metadata Credential + +```yaml +services: + s3: + url: https://my-example-opa-bucket.s3.eu-north-1.amazonaws.com + credentials: + s3_signing: + metadata_credentials: + aws_region: eu-north-1 + iam_role: my-opa-bucket-access-role + environment_credentials: {} + +bundles: + authz: + service: s3 + resource: bundle.tar.gz +``` + +**NOTE:** In this example, OPA will look for AWS credentials in the environment first before trying metadata endpoint. S3 signing will fail if none of the providers are successful. + +### Google Cloud Storage + +#### OPA Bundle Support + +| Feature | Supported | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Caching headers | Yes | +| Authentication methods | [GCP Metadata Token](https://www.openpolicyagent.org/docs/latest/configuration/#gcp-metadata-token)
[OAuth2 JWT Bearer Grant Type](https://www.openpolicyagent.org/docs/latest/configuration/#oauth2-jwt-bearer-grant-type) | + +#### Setup Instructions + +1. In the left pane menu, choose "Cloud Storage". Click "New bucket". +2. Fill in the form according to your preferences (name, region, availability, etc). +3. Once the bucket is created, you can press "Upload" to upload a test bundle. Clicking this will provide a link to the bundle which you can use in your OPA configuration. +4. At this stage you can either choose to make the bucket public (by clicking "Permissions") or to configure a service account for authenticated access. + +#### Authentication + +##### GCP Metadata Token Authentication + +If your instance of OPA runs inside GCP, you'll be able to authenticate using GCP metadata tokens. These tokens by default carry all the permissions granted to the default service account, so you might still want to create a dedicated service account for this purpose (see [JWT Bearer Grant Type](#jwt-bearer-grant-type) below). + +##### JWT Bearer Grant Type + +Use this for [authenticating](https://cloud.google.com/storage/docs/authentication) _external_ clients, i.e. OPAs running outside the GCP environment. + +1. Search for "credentials" in the top search box and choose "Credentials - APIs and Services". +2. Click "Create Credentials" followed by "Service Account." +3. Fill in a name for the account and proceed to select roles. +4. Choose "Storage Object Viewer" for read access and "Storage Object Creator" for write access (if scripted uploads is desired). +5. Click the newly created service account and then the "Keys" tab. Press "Add Key" and either "Create new" or upload an existing one. +6. If creating new, choose to download the private key in JSON format (not P12). +7. Open the JSON file just downloaded and copy the PEM encoded value of the `private_key` attribute. This is the key you'll use for your OPA configuration. + +##### Testing Authentication + +The easiest way of testing GCP metadata token or JWT bearer grant type authentication is simply to set up OPA with config for these and run the server. + +#### Upload Bundle + +Uploading a bundle is trivial with the `gsutil` command included with the [Google Cloud SDK](https://cloud.google.com/sdk/docs/quickstart). + +```shell +gsutil cp bundle.tar.gz gs:/// +``` + +#### Example OPA Configuration + +##### GCP Metadata Token Authentication + +```yaml +services: + gcs: + url: https://storage.googleapis.com/storage/v1/b/${BUCKET_NAME}/o + credentials: + gcp_metadata: + scopes: + - https://www.googleapis.com/auth/devstorage.read_only + +bundles: + authz: + service: gcs + # NOTE ?alt=media is required + resource: "bundle.tar.gz?alt=media" +``` + +If the resource (the object in the gcs bucket) contains slashes (/) or other special characters, these need to be url-encoded here, e.g. +`bundles/bundle.tar.gz?alt=media` should be entered as `bundles%2fbundle.tar.gz?alt=media`. Please refer to the [official documentation](https://cloud.google.com/storage/docs/request-endpoints#encoding) for more information. + +##### Google Cloud Storage Bundle and JWT Bearer Authentication + +```yaml +services: + gcp: + url: https://storage.googleapis.com/storage/v1/b/${BUCKET_NAME}/o + credentials: + oauth2: + grant_type: jwt_bearer + token_url: https://oauth2.googleapis.com/token + signing_key: jwt_signing_key # references the key in `keys` below + scopes: + - https://www.googleapis.com/auth/devstorage.read_only + additional_claims: + aud: https://oauth2.googleapis.com/token + iss: opa-client@my-account.iam.gserviceaccount.com + +bundles: + authz: + service: gcp + # NOTE ?alt=media is required + resource: "bundle.tar.gz?alt=media" + +keys: + jwt_signing_key: + algorithm: RS256 + private_key: ${BUNDLE_SERVICE_SIGNING_KEY} +``` + +### Azure Blob Storage + +#### OPA Bundle Support + +| Feature | Supported | +| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Caching headers | Yes | +| Authentication methods | [OAuth2 Client Credentials](https://www.openpolicyagent.org/docs/latest/configuration/#oauth2-client-credentials),
[OAuth2 Client Credentials JWT authentication](https://www.openpolicyagent.org/docs/latest/configuration/#oauth2-client-credentials-jwt-authentication) | + +Note that for the time being, the [Shared Key or Shared Access Signature (SAS)](https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-requests-to-azure-storage) options are [not supported](https://github.com/open-policy-agent/opa/issues/2964). + +#### Setup Instructions + +1. Any type of storage in Azure is grouped in Storage Accounts. If you have one already, skip to step 3. +2. From the Azure console, select "Storage Accounts" followed by "New". Fill in the form (name, region, etc) according to your preferences. One thing to note when selecting "account kind", make sure to pick the Storage V2 (general purpose v2) option and not the legacy BlobStorage kind. +3. With the storage account deployed, press "Go to resource" to create a new storage resource. +4. Select "Containers" and press the plus sign to create a new storage container. +5. Name your container and select access level. Choose "Private" to require authentication, or "Blob" to allow unauthenticated read access. +6. Press "upload" and select the bundle from your local filesystem. +7. Clicking the filename should bring up a properties window where the public URL to the bundle is included. + +#### Authentication + +1. Go to Azure Active Directory. +2. In the left menu, click "App Registrations" followed by "New Registration". Name your app (client) amd leave the other options be. Click "Register". +3. Click "Certificates and Secrets". Either create a secret to be used for [OAuth2 Client Credentials](https://www.openpolicyagent.org/docs/latest/configuration/#oauth2-client-credentials) or upload a certificate for [OAuth2 Client Credentials JWT authentication](https://www.openpolicyagent.org/docs/latest/configuration/#oauth2-client-credentials-jwt-authentication). +4. In the menu to the left, click "API permissions". Click "Add a permission". Choose "Azure Storage" and check the "user_impersonation" checkbox. +5. Click "Add admin consent for Default Directory". Answer Yes on the followup question. +6. Navigate back to your storage account. Click "Access Control (IAM)". Click "Add role assignments". +7. Select the "Storage Blob Data Contributor" role. Leave "Assign access to" as "User, group or service principal". Search and select the name of the app created in step 2. +8. Configuration is now complete. Go back to "App Registrations" in the Active Directory view to check details like tenant ID, application ID and endpoints. You'll need those when configuring OPA (see [Example Configuration](#example-opa-configuration) below). + +##### Testing Authentication + +Use Curl to test client authentication with a secret. + +```shell +curl --silent \ + --data "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=https://storage.azure.com/.default" \ + "https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" +``` + +#### Upload Bundle + +Uploading bundles to Azure Blob storage is easily done using the [azcopy](https://docs.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10) tool. Make sure to first properly [authorize](https://docs.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-authorize-azure-active-directory) the user to be able to upload to Blob storage. + +By now you should be able to login interactively using `azcopy login --tenant-id `. Since you'll most likely will want to log in from scripts (to upload bundles programmatically), you should however create an Azure AD application, and a [service principal](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal) to do so. Good news! If you've followed the Authentication steps above, you already have one. + +**Uploading bundle using client secret authentication** + +```shell +AZCOPY_SPA_CLIENT_SECRET='' azcopy login \ + --service-principal \ + --tenant-id \ + --application-id + +azcopy copy bundle.tar.gz https://.blob.core.windows.net//bundle.tar.gz +``` + +**Uploading bundle using client certificate authentication** + +```shell +AZCOPY_SPA_CERT_PASSWORD='' azcopy login \ + --service-principal \ + --tenant-id \ + --certificate-path --tenant-id + +azcopy copy bundle.tar.gz https://.blob.core.windows.net//bundle.tar.gz +``` + +**Uploading bundle using Curl** + +```shell +token=$(curl --silent \ + --data "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=https://storage.azure.com/.default" \ + "https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" | jq -r .access_token) + +curl --silent \ + -X PUT \ + --data-binary "@bundle.tar.gz" -H "X-Ms-Version: 2020-04-08" -H "Authorization: Bearer $token" \ + https://styra.blob.core.windows.net/opa/bundle.tar.gz +``` + +#### Example OPA Configuration + +##### Azure Blob Storage Bundle and Client Credentials Authentication + +```yaml +services: + blob: + url: https://my-storage-account.blob.core.windows.net + headers: + # This header _must_ be present in all authenticated requests + x-ms-version: "2020-04-08" + credentials: + oauth2: + token_url: "https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token" + client_id: "${CLIENT_ID}" + client_secret: "${CLIENT_SECRET}" + scopes: + - https://storage.azure.com/.default + +bundles: + authz: + service: blob + resource: my-container/bundle.tar.gz +``` + +Note that the `$CLIENT_ID` is what is referred to as the "Application ID" inside your Azure account. + +##### Azure Blob Storage Bundle and Client Credentials JWT Authentication + +```yaml +keys: + blob_key: + algorithm: RS256 + private_key: "${PRIVATE_KEY_PEM}" + +services: + blob: + url: https://my-storage-account.blob.core.windows.net + headers: + # This header _must_ be present in all authenticated requests + x-ms-version: "2020-04-08" + credentials: + oauth2: + token_url: "https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token" + signing_key: blob_key + thumbprint: "8F1BDDDE9982299E62749C20EDDBAAC57F619D04" + include_jti_claim: true + scopes: + - https://storage.azure.com/.default + additional_claims: + aud: "https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token" + iss: "${CLIENT_ID}" + sub: "${CLIENT_ID}" + +bundles: + authz: + service: blob + resource: opa/bundle.tar.gz +``` + +Note that the `$CLIENT_ID` is what is referred to as the "Application ID" inside your Azure account. +Also note in particular how the `thumbprint` property is required for Azure. The value expected here can be found under "Certificates and Secrets" in your application's configuration. + +![Certificate thumbprint](./assets/thumbprint.png) + +### Nginx + +Nginx offers a simple but competent bundle server for those who prefer to host their own. A great choice for local testing. + +| Feature | Supported | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Caching headers | Yes | +| Authentication methods | [Bearer Token](https://www.openpolicyagent.org/docs/latest/configuration/#bearer-token) 1
[OAuth2 Client Credentials JWT authentication](https://www.openpolicyagent.org/docs/latest/configuration/#oauth2-client-credentials-jwt-authentication) 2 | + +1Nginx does not support bearer token authentication, but it does support [basic auth](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/). This can be achieved by setting `services[_].credentials.bearer.scheme` to `Basic` in the OPA configuration, and simply provide the base64 encoded credentials as the token.
+2Only available with Nginx Plus. + +#### Upload Bundle + +Either use the [nginx-upload-module](https://www.nginx.com/resources/wiki/modules/upload/) or upload bundles out-of-band with SSH or similar. + +#### Example OPA Configuration + +```yaml +services: + nginx: + url: https://my-nginx.example.com + credentials: + bearer: + token: dGVzdGluZzp0ZXN0aW5n + scheme: Basic + +bundles: + authz: + service: nginx + resource: /bundle.tar.gz +``` + +### OCI Registry + +OPA is able to interact with [OCI](https://opencontainers.org/) compatible registries to be able to download and use policies stored as containers. +To configure OPA to use an OCI repository see the [service configuration section](./configuration/#services) + +**Structure** +The bundle container is composed of 3 layers: + +- the manifest layer - contains the information about the tarball layer of the container(the digest, size, mediatype and annotations) and the config layer +- the bundle tarball layer - the actual bundle tarball +- the configuration layer - currently empty + +For OCI compatible registries an _**oci**_ folder is created in the [persistence directory](./configuration/#miscellaneous). If this value is not set, because the OCI downloader plugin requires a storage path, the system's temporary folder location will be used instead. This folder should be maintained by the user. We recommend backing-up or cleaning up this folder periodically as this acts as a local cache for the OCI downloader. + +**Current Limitations** +The OCI Downloader plugin used by OPA has a couple of limitation: + +- it accepts only **one** layer per image that contains the bundle tarball +- it can download only the following application media types: + - `application/vnd.oci.image.layer.v1.tar+gzip` + - `application/vnd.oci.image.manifest.v1+json` + - `application/vnd.oci.image.config.v1+json` + +#### Building and Publishing Policy Containers + +There are multiple ways to build an image from a policy code base using different tools. + +##### Using OPA and ORAS CLIs + +To build and push a policy bundle to a remote OCI registry with the +[OPA CLI](./cli/) +and [ORAS CLI](https://oras.land/docs/installation/) you can use the following +commands: + +- `opa build ` will allow you to build a bundle tarball from your OPA policy and data files + +Now that we have the tarball we will need to provide a config manifest to the ORAS CLI and the tarball itself: + +- `oras push //: --manifest-config :application/vnd.oci.image.config.v1+json :application/vnd.oci.image.layer.v1.tar+gzip` + +Using an empty(`{}`) `manifest-config` json file should be sufficient to be able to push and allow the OCI downloader to use the remote policy image. + +#### Maintaining a policy-as-code repository + +One of the easiest method of managing your policy bundles is to store your code base in a hosted repository service like Github or Gitlab and set up an automated way to build and publish your code as a container to the desired registry using a CI(ex. Github Action). + +#### Example + +In this example we are using the [ghcr.io](https://ghcr.io) OCI registry as the upstream repository and the OPA and ORAS CLI as our build and publishing tool. + +###### Starting from scratch + +Let's set up a basic policy example structured as: + +``` +└── src + ├── data.json + ├── .manifest + └── policies + └── hello.rego +``` + +Here our _hello.rego_ file contains a very simple example: + +```rego +package policies.play + +default hello = false + +hello { + m := input.message + m == "world" +} +``` + +The _.manifest_ file specifies the root only as: + +```json +{ + "roots": ["policies"], + "metadata": { + "required_builtins": { + "builtin1": [] + } + } +} +``` + +And the _data.json_ file is empty json: + +``` +{} +``` + +###### Building your policy + +To build my bundle tarball I'm going to use the OPA CLI and run the following command: + +```bash +opa build .src/ +``` + +###### Pushing the container to a remote registry + +I'll prepare an empty config.json file that contains: + +``` +{} +``` + +To push the build image to an upstream registry we first need to login using: + +```bash +oras login ghcr.io +``` + +And now we can push our policy using: + +```bash +oras push ghcr.io/someorg/policy-hello:1.0.0 --config config.json:application/vnd.oci.image.config.v1+json bundle.tar.gz:application/vnd.oci.image.layer.v1.tar+gzip +``` + +###### Spin up the policy with OPA CLI + +Now that our image is pushed we prepare the OPA configuration. + +In this example the configuration.yaml looks like this as the pushed image is private we need credentials for OPA to download it: + +```yaml +services: + ghcr-registry: + url: https://ghcr.io + type: oci + credentials: + bearer: + scheme: "Bearer" + token: "" + +bundles: + authz: + service: ghcr-registry + resource: ghcr.io/someorg/policy-hello:1.0.0 + persist: true + polling: + min_delay_seconds: 30 + max_delay_seconds: 120 +``` + +In the above configuration we pinned the configuration to use the 1.0.0 tag of the image. OPA will identify this image by the tag and the descriptor SHA. If the SHA of the image is changed upstream, OPA will redownload and activate the changes. + +If we run the _opa CLI_ with this configuration using the command it will open an interactive terminal (REPL) where we can see the loaded bundle: + +```bash +opa run -c configuration.yaml +``` + +The terminal should show that the bundle has been loaded and activated: + +``` +> {"level":"info","msg":"Bundle loaded and activated successfully.","name":"authz","plugin":"bundle","time":"2022-06-15T16:50:53+03:00"} +> data +{ + "policies": { + "play": { + "hello": false + } + } +} +> exit +``` + +We can now start OPA as a server using: + +```bash +opa run --server --set default_decision=policies -c configuration.yaml +``` + +To interact with the server you can do a simple **curl** to verify if it works as intended: + +```bash +curl localhost:8181 -i -d '{ "message":"world"}' -H 'Content-Type:application/json' + +HTTP/1.1 200 OK +Content-Type: application/json +Date: Wed, 15 Jun 2022 13:55:19 GMT +Content-Length: 23 + +{"play":{"hello":true}} +``` + +```bash +curl localhost:8181 -i -d '{ "message":"other"}' -H 'Content-Type:application/json' +HTTP/1.1 200 OK +Content-Type: application/json +Date: Wed, 15 Jun 2022 13:56:13 GMT +Content-Length: 24 + +{"play":{"hello":false}} +``` + +## Ecosystem Projects + + +The Bundle API is great way to manage your policies and data. The following +projects all make use of this API if you're looking for inspiration or examples +of how to use it. + diff --git a/third_party/opa/docs/docs/management-decision-logs.md b/third_party/opa/docs/docs/management-decision-logs.md new file mode 100644 index 000000000000..696dc1368e56 --- /dev/null +++ b/third_party/opa/docs/docs/management-decision-logs.md @@ -0,0 +1,314 @@ +--- +title: "Decision Logs" +--- + +OPA can periodically report decision logs to remote HTTP servers, using custom +plugins, or to the console output; or any combination thereof. +The decision logs contain events that describe policy queries. Each event includes +the policy that was queried, the input to the query, bundle metadata, and other +information that enables auditing and offline debugging of policy decisions. + +When decision logging is enabled the OPA server will include a `decision_id` +field in API calls that return policy decisions. + +See the [Configuration Reference](./configuration) for configuration details. + +### Decision Log Service API + +OPA expects the service to expose an API endpoint that will receive decision logs. + +```http +POST /[] HTTP/1.1 +Content-Encoding: gzip +Content-Type: application/json +``` + +The resource field is an optional configuration that can be used to route logs +to a specific endpoint in the service by defining the full path. If the resource path is not configured on the agent, +updates will be sent to `/logs`. + +The message body contains a gzip compressed JSON array. Each array element (event) +represents a policy decision returned by OPA. + + +```json +[ + { + "labels": { + "app": "my-example-app", + "id": "1780d507-aea2-45cc-ae50-fa153c8e4a5a", + "version": "{{ current_version }}" + }, + "decision_id": "4ca636c1-55e4-417a-b1d8-4aceb67960d1", + "bundles": { + "authz": { + "revision": "W3sibCI6InN5cy9jYXRhbG9nIiwicyI6NDA3MX1d" + } + }, + "path": "http/example/authz/allow", + "input": { + "method": "GET", + "path": "/salary/bob" + }, + "result": "true", + "requested_by": "[::1]:59943", + "timestamp": "2018-01-01T00:00:00.000000Z" + } +] +``` + + +Decision log updates contain the following fields: + +| Field | Type | Description | +| ---------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[_].labels` | `object` | Set of key-value pairs that uniquely identify the OPA instance. | +| `[_].decision_id` | `string` | Unique identifier generated for each decision for traceability. | +| `[_].trace_id` | `string` | Unique identifier of a trace generated for each incoming request for traceability. This is a hex string representation compliant with the W3C trace-context specification. See more at https://www.w3.org/TR/trace-context/#trace-id. | +| `[_].span_id` | `string` | Unique identifier of a span in a trace to assist traceability. This is a hex string representation compliant with the W3C trace-context specification. See more at https://www.w3.org/TR/trace-context/#parent-id. | +| `[_].bundles` | `object` | Set of key-value pairs describing the bundles which contained policy used to produce the decision. | +| `[_].bundles[_].revision` | `string` | Revision of the bundle at the time of evaluation. | +| `[_].path` | `string` | Hierarchical policy decision path, e.g., `/http/example/authz/allow`. Receivers should tolerate slash-prefixed paths. | +| `[_].query` | `string` | Ad-hoc Rego query received by Query API. | +| `[_].input` | `any` | Input data provided in the policy query. | +| `[_].result` | `any` | Policy decision returned to the client, e.g., `true` or `false`. | +| `[_].requested_by` | `string` | Identifier for client that executed policy query, e.g., the client address. | +| `[_].request_context.http.headers` | `object` | Set of key-value pairs describing HTTP headers and their corresponding values. The header keys in this object are specified by the user as part of the decision log configuration. The values in this object represent a list of values associated with the given header key. | +| `[_].timestamp` | `string` | RFC3999 timestamp of policy decision. | +| `[_].metrics` | `object` | Key-value pairs of [performance metrics](./rest-api#performance-metrics). | +| `[_].erased` | `array[string]` | Set of JSON Pointers specifying fields in the event that were erased. | +| `[_].masked` | `array[string]` | Set of JSON Pointers specifying fields in the event that were masked. | +| `[_].nd_builtin_cache` | `object` | Key-value pairs of non-deterministic builtin names, paired with objects specifying the input/output mappings for each unique invocation of that builtin during policy evaluation. Intended for use in debugging and decision replay. Receivers will need to decode the JSON using Rego's JSON decoders. | +| `[_].req_id` | `number` | Incremental request identifier, and unique only to the OPA instance, for the request that started the policy query. The attribute value is the same as the value present in others logs (request, response, and print) and could be used to correlate them all. This attribute will be included just when OPA runtime is initialized in server mode and the log level is equal to or greater than info. | + +If the decision log was successfully uploaded to the remote service, it should respond with an HTTP 2xx status. If the +service responds with a non-2xx status, OPA will requeue the last chunk containing decision log events and upload it +during the next upload event. OPA also performs an exponential backoff to calculate the delay in uploading the next chunk +when the remote service responds with a non-2xx status. + +OPA periodically uploads decision logs to the remote service. In order to conserve network and memory resources, OPA +attempts to fill up each message body with as many events as possible while respecting the user-specified +`upload_size_limit_bytes` config option. Each message body is a gzip compressed JSON array and the `upload_size_limit_bytes` +config option represents the gzip compressed size, it can be referred to as the compressed limit. To avoid compressing +each incoming event to get its compressed size to see if the compressed limit is reached, OPA tries to make an educated +guess what the uncompressed limit could be. It does so by using an adaptive limit, referred to as the uncompressed limit, +that gets adjusted by measuring incoming events. This does mean that initially the chunk sizes will most likely be smaller +than the compressed limit, but as OPA consumes more decision events it will adjust the adaptive uncompressed limit to +optimize the messages. The algorithm to adjust the uncompressed limit uses the following criteria: + +`Scale Up`: If the current chunk size is below 90% of the user-configured compressed limit, exponentially increase the +uncompressed limit. The exponential function is 2^x where x has a minimum value of 1 + +`Scale Down`: If the current chunk size exceeds the compressed limit, decrease the uncompressed limit and re-encode the +decisions in the last chunk. + +`Equilibrium`: If the chunk size is between 90% and 100% of the user-configured limit, maintain uncompressed limit value. + +When an event containing `nd_builtin_cache` cannot fit into a chunk smaller than `upload_size_limit_bytes`, OPA will +drop the `nd_builtin_cache` key from the event, and will retry encoding the chunk without the non-deterministic +builtins cache information. This best-effort approach ensures that OPA reports decision log events as much as possible, +and bounds how large decision log events can get. This size-bounding is necessary, because some non-deterministic builtins +(such as `http.send`) can increase the decision log event size by a potentially unbounded amount. + +### Local Decision Logs + +Local console logging of decisions can be enabled via the `console` config option. +This does not require any remote server. Example of minimal config to enable: + +```yaml +decision_logs: + console: true +``` + +This will dump all decisions to the console. See +[Configuration Reference](./configuration) for more details. + +### Masking Sensitive Data + +Policy queries may contain sensitive information in the `input` document that +must be removed or modified before decision logs are uploaded to the remote API +(e.g., usernames, passwords, etc.) Similarly, parts of the policy decision itself may +be considered sensitive. + +By default, OPA queries the `data.system.log.mask` path prior to encoding and +uploading decision logs or calling custom decision log plugins. + +OPA provides the decision log event as input to the policy query and expects +the query to return a set of JSON Pointers that refer to fields in the decision +log event to either **erase** or **modify**. + +For example, assume OPA is queried with the following `input` document: + +```json +{ + "resource": "user", + "name": "bob", + "password": "passw0rd" +} +``` + +To **remove** the `password` field from decision log events related to "user" +resources, supply the following policy to OPA: + +```ruby +package system.log + +mask contains "/input/password" if { + # OPA provides the entire decision log event as input to the masking policy. + # Refer to the original input document under input.input. + input.input.resource == "user" +} + +# To mask certain fields unconditionally, omit the rule body. +mask contains "/input/ssn" +``` + +When the masking policy generates one or more JSON Pointers, they will be erased +from the decision log event. The erased paths are recorded on the event itself: + +```json +{ + "decision_id": "b4638167-7fcb-4bc7-9e80-31f5f87cb738", + "erased": [ + "/input/password", + "/input/ssn" + ], + "input": { + "name": "bob", + "resource": "user" + }, +------------------------- 8< ------------------------- + "path": "system/main", + "requested_by": "127.0.0.1:36412", + "result": true, + "timestamp": "2019-06-03T20:07:16.939402185Z" +} +``` + +There are a few restrictions on the JSON Pointers that OPA will erase: + +- Pointers must be prefixed with `/input`, `/result`, or `/nd_builtin_cache`. +- Pointers may point to undefined data. For example `/input/name/first` in the + example above would be undefined. Masking operations on undefined pointers are + ignored. +- Pointers can also refer to arrays both as part of the path and as the last + element in the path. For example, both `/input/users/0/name` and + `/input/users/0` would be valid. + +In order to **modify** the contents of an input field, the **mask** rule may utilize the following format. + +- `"op"` -- The operation to apply when masking. All operations are done at the + path specified. Valid options include: + +| op | Description | +| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `"remove"` | The `"path"` specified will be removed from the resulting log message. The `"value"` mask field is ignored for `"remove"` operations. | +| `"upsert"` | The `"value"` will be set at the specified `"path"`. If the field exists it is overwritten, if it does not exist it will be added to the resulting log message. | + +- `"path"` -- A JSON pointer path to the field to perform the operation on. + +Optional Fields: + +- `"value"` -- Only required for `"upsert"` operations. + +> This is processed for every decision being logged, so be mindful of +> performance when performing complex operations in the mask body, e.g. crypto +> operations + +```ruby +package system.log + +mask contains {"op": "upsert", "path": "/input/password", "value": "**REDACTED**"} if { + # conditionally upsert password if it existed in the original event + input.input.password +} +``` + +To always **upsert** a value, even if it didn't exist in the original event, +the following rule format can be used. + +```ruby +package system.log + +# always upsert, no conditions in rule body +mask contains {"op": "upsert", "path": "/input/password", "value": "**REDACTED**"} +``` + +The result of this mask operation on the decision log event produces +the following output. Notice that the **mask** event field exists +to track **remove** vs **upsert** mask operations. + +```json +{ + "decision_id": "b4638167-7fcb-4bc7-9e80-31f5f87cb738", + "erased": [ + "/input/ssn" + ], + "masked": [ + "/input/password" + ], + "input": { + "name": "bob", + "resource": "user", + "password": "**REDACTED**" + }, +------------------------- 8< ------------------------- + "path": "system/main", + "requested_by": "127.0.0.1:36412", + "result": true, + "timestamp": "2019-06-03T20:07:16.939402185Z" +} +``` + +### Drop Decision Logs + +Drop rules filters all decisions from logging where the rule evaluates to `true`. + +This rule will drop all requests to the _allow_ rule in the _kafka_ package, that returned _true_: + +```rego +package system.log + +drop if { + input.path == "kafka/allow" + input.result == true +} +``` + +Log only requests for _delete_ and _alter_ operations +(Kafka with the [opa-kafka-plugin](https://github.com/StyraInc/opa-kafka-plugin)): + +```rego +package system.log + +drop if { + input.path == "kafka/allow" + not input.input.action.operation in {"DELETE", "ALTER"} +} +``` + +The name of the drop rules by default is `drop` in the package `system.log`. It can be changed with the configuration +property `decision_logs.drop_decision`. + +```yaml +decision_logs: + drop_decision: /system/log/drop +``` + +### Rate Limiting Decision Logs + +There are scenarios where OPA may be uploading decisions faster than what the remote service is able to consume. Although +OPA provides a user-specified buffer size limit in bytes, it may be difficult to determine the ideal buffer size that will +allow the service to consume logs without being overwhelmed. The `max_decisions_per_second` config option allows users +to set the maximum number of decision log events to buffer per second. OPA will drop events if the rate limit is exceeded. +This option provides users more control over how OPA buffers log events and is an effective mechanism to make sure the +service can successfully process incoming log events. + +## Ecosystem Projects + +Decision Logging is an important feature of OPA which supports, in particular, auditing and debugging. The following OPA +ecosystem projects implement functionality related to Decision Logging: + + +These projects implement decision logging functionality. + diff --git a/third_party/opa/docs/docs/management-discovery.md b/third_party/opa/docs/docs/management-discovery.md new file mode 100644 index 000000000000..6ee67bc07fe6 --- /dev/null +++ b/third_party/opa/docs/docs/management-discovery.md @@ -0,0 +1,314 @@ +--- +title: "Discovery" +--- + +OPA can be configured to download bundles of policy and data, report status, and +upload decision logs to remote endpoints. The discovery feature helps you +centrally manage the OPA configuration for these features. You should use the +discovery feature if you want to avoid managing OPA configuration updates in +a number of different locations. + +When the discovery feature is enabled, OPA will periodically download a +_discovery bundle_. Like regular bundles, the discovery bundle may contain JSON +and Rego files. OPA will evaluate the data and policies contained in the +discovery bundle to generate the rest of the configuration. There are two main +ways to structure the discovery bundle: + +1. Include static JSON configuration files that define the OPA configuration. +2. Include Rego files that can be evaluated to produce the OPA configuration. + +> If you need OPA to select which policy to download dynamically (e.g., based on +> environment variables like the region where OPA is running), use the second +> option. + +If discovery is enabled, other features like bundle downloading and status +reporting **can** be configured manually. In case of conflicts, the bootstrap configuration +for plugins would override the discovered configuration. **In general, the bootstrap configuration +overrides the discovered configuration.** + +See the [Configuration Reference](./configuration) for configuration details. + +### Discovery Service API + +OPA expects the service to expose an API endpoint that serves bundles. + +```http +GET // HTTP/1.1 +``` + +If the bundle exists, the server should respond with an HTTP 200 OK status +followed by a gzipped tarball in the message body. + +```http +HTTP/1.1 200 OK +Content-Type: application/gzip +``` + +You can enable discovery with an OPA configuration file similar to the example +below. In some places in the documentation, the initial configuration provided +to OPA is referred to as the "boot configuration". + +```yaml +services: + acmecorp: + url: https://example.com/control-plane-api/v1 + credentials: + bearer: + token: "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm" + +discovery: + service: acmecorp + resource: /configuration/example/discovery.tar.gz +``` + +Using the boot configuration above, OPA will fetch discovery bundles from: + +``` +https://example.com/control-plane-api/v1/configuration/example/discovery.tar.gz +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +services[discovery.service].url discovery.resource +``` + +> The `discovery.resource` field defaults to `bundles/`. The default +> is convenient if you want to serve discovery bundles and normal bundles from the same API +> endpoint. If only one service is defined, there is no need to set `discovery.service`. + +> The optional `discovery.signing` field can be used to specify the `keyid` and `scope` that should be used +> for verifying the signature of the discovery bundle. See [this](#discovery-bundle-signature) section for details. + +OPA generates it's subsequent configuration by querying the Rego and JSON files +contained inside the discovery bundle. The default query is `data` however this +can be overridden by specifying the `discovery.decision`. + +```yaml +services: +- name: acmecorp + url: https://example.com/control-plane-api/v1 + credentials: + bearer: + token: "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm" +discovery: + name: example + resource: /configuration/example/discovery.tar.gz + decision: example/discovery +``` + +OPA executes the following query: + +``` +data.example.discovery +``` + +If the discovery bundle contained the following Rego file: + +```ruby +package example + +discovery := { + "bundles": { + "main": { + "service": "acmecorp", + "resource": bundle_name + }, + }, + "default_decision": "acmecorp/httpauthz/allow" +} + +bundle_name := "acmecorp/httpauthz" +``` + +The subsequent configuration would be: + +```json +{ + "bundles": { + "main": { + "service": "acmecorp", + "resource": "acmecorp/httpauthz" + } + }, + "default_decision": "acmecorp/httpauthz/allow" +} +``` + +The discovery bundle contents above are essentially static. The same result +could be achieved by constructing the discovery bundle with a static JSON file: + +```json +{ + "example": { + "discovery": { + "bundles": { + "main": { + "service": "acmecorp", + "resource": "acmecorp/httpauthz" + } + }, + "default_decision": "acmecorp/httpauthz/allow" + } + } +} +``` + +> For an example of how to configure OPA dynamically see the [Example](#example) +> section below. + +The subsequent configuration does not have to specify `services` or include a +reference to a service in the `bundle`, `status,` or `decision_log` sections. If +the either the `services` or references to services are missing, OPA will +default them to the value from the boot configuration. + +### Example + +Let's see an example of how the discovery feature can be used to dynamically +configure an OPA to download one of two bundles based on a label in the boot +configuration. Let's say the label `region` indicates the region in which the +OPA is running and it's value will decide the bundle to download. + +Below is a policy file which generates an OPA configuration. + +**example.rego** + +```ruby +package discovery + +config := { + "bundles": { + "main": { + "service": "acmecorp", + "resource": bundle_name # line 7 + } + } +} + +rt := opa.runtime() +region := rt.config.labels.region +bundle_name := region_bundle[region] + +# region-bundle information +region_bundle := { + "US": "example/test1/p", + "UK": "example/test2/p" +} +``` + +The `bundle_name` variable in `line 7` of the above policy will be dynamically +selected based on the value of the label `region`. So if an OPA was started +with `region: "US"`, then the `bundle_name` will be `example/test1/p`. + +Start an OPA with a boot configuration as shown below: + +**opa-config.yaml** + +```yaml +services: +- name: acmecorp + url: https://example.com/control-plane-api/v1 + credentials: + bearer: + token: "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm" +discovery: + resource: bundles/discovery.tar.gz + decision: discovery/config +labels: + region: "US" +``` + +Run OPA: + +```bash +opa run -s -c opa-config.yaml +``` + +You should see a log like below, which shows the bundle being downloaded. In +this case, the bundle name is `example/test1/p` as `region` is `US`. + +```raw +INFO Bundle downloaded and activated successfully. name=example/test1/p plugin=bundle +``` + +Now start another OPA with a boot configuration as shown below. Notice the +`region` is `UK`: + +**opa-config.yaml** + +```yaml +services: +- name: acmecorp + url: https://example.com/control-plane-api/v1 + credentials: + bearer: + token: "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm" +discovery: + resource: bundles/discovery.tar.gz + decision: discovery/config +labels: + region: "UK" +``` + +Run OPA: + +```bash +opa run -s -c opa-config.yaml +``` + +In this case, the bundle being downloaded is `example/test2/p` as `region` is +`UK`. + +```raw +INFO Bundle downloaded and activated successfully. name=example/test2/p plugin=bundle +``` + +This shows how the discovery feature can help in centrally managing the bundle +to be downloaded by an OPA based on a configuration label. You can use the same +strategy to dynamically configure other plugins based on the running OPA's +configuration labels or environment variables. + +### Limitations + +In practice, discovery services do not change frequently. These configuration sections are treated as +immutable to avoid accidental configuration errors rendering OPA unable to discover a new configuration. +If the discovered configuration changes the `discovery` section, +those changes are ignored. If the discovered configuration changes the discovery service, +an error will be logged. +If the discovered configuration changes the `labels` section, only labels that are additional compared to the bootstrap configuration are used, all other changes are ignored. If the discovery document changes its `labels` section over time, the effective set of labels is always the bootstrap configuration plus added labels from the latest discovery document. + +### Discovery Bundle Signature + +Like regular bundles, if the discovery bundle contains a `.signatures.json` file, OPA will verify the discovery +bundle before activating it. The format of the `.signatures.json` file and the verification steps are same as that for +regular bundles. Since the discovered configuration ignores changes to the `discovery` section, any key used for +signature verification of a discovery bundle **CANNOT** be modified via discovery. + +> 🚨 We recommend that if you are using discovery you should be signing the discovery bundles because those bundles +> include the keys used to verify the non-discovery bundles. However, OPA does not enforce that recommendation. You may use +> unsigned discovery bundles that themselves require non-discovery bundles to be signed. + +### Discovery Bundle Persistence + +OPA can optionally persist the activated discovery bundle to disk for recovery purposes. To enable +persistence, set the `discovery.persist` field to `true`. When bundle +persistence is enabled, OPA will attempt to read the discovery bundle from disk on startup. This +allows OPA to start with the most recently activated bundle in case OPA cannot communicate +with the bundle server. OPA will try to load and activate the persisted discovery bundle on a best-effort basis. Any errors +encountered during the process will be surfaced in the bundle's status update. When communication between OPA and +the bundle server is restored, the latest bundle is downloaded, activated, and persisted. Like regular bundles, only +the discovery bundle itself is persisted. The discovered configuration that is generated by evaluating the data and +policies contained in the discovery bundle will **NOT** be persisted. + +:::info +The discovery bundle is persisted at +`/bundles//bundle.tar.gz`. By default +`persistence_directory` is `.opa` in the working directory of the OPA process. +If `persistence_directory` is changed through discovery this will not affect +where the discovery plugin will store the discovery bundles, the boot +configuration will always be used. +::: + +## Ecosystem Projects + +Configuring OPA using Discovery Bundles is a powerful production feature. + + +Wasm is a great way to integrate OPA into applications where the Go SDK is unavailable. + diff --git a/third_party/opa/docs/docs/management-introduction/assets/ControlPlaneDiagram.jsx b/third_party/opa/docs/docs/management-introduction/assets/ControlPlaneDiagram.jsx new file mode 100644 index 000000000000..b1e852c44dd6 --- /dev/null +++ b/third_party/opa/docs/docs/management-introduction/assets/ControlPlaneDiagram.jsx @@ -0,0 +1,26 @@ +import Mermaid from "@theme/Mermaid"; + +const logoPath = require("./logo.png").default; + +const diagram = ` +graph LR + subgraph CP[Control Plane] + Monitoring + Logging + Config + Bundles + end + + OPA["
OPA"]; + Service["Service"] --- OPA + + OPA -->|Status| Monitoring + OPA -->|Decisions| Logging + Bundles -->|Bundles| OPA + Config -->|Discovery
Bundles| OPA + +`; + +const ControlPlaneDiagram = () => ; + +export default ControlPlaneDiagram; diff --git a/third_party/opa/docs/docs/management-introduction/assets/DistributedDiagram.jsx b/third_party/opa/docs/docs/management-introduction/assets/DistributedDiagram.jsx new file mode 100644 index 000000000000..8b202270428f --- /dev/null +++ b/third_party/opa/docs/docs/management-introduction/assets/DistributedDiagram.jsx @@ -0,0 +1,40 @@ +import Mermaid from "@theme/Mermaid"; + +const logoPath = require("./logo.png").default; + +const diagram = ` +graph TD; + subgraph SB[Service B] + style SB fill:none,stroke:none; + direction LR + subgraph SBNP[Node/Pod] + style SBNP fill:none,stroke-dasharray: 7 5 + direction LR + subgraph "Local OPA Instance" + B_OPA["
OPA"]; + end + subgraph "App Instance" + B_Service["Service Logic"] -->|HTTP Call| B_OPA + end + end + end + + subgraph SA[Service A] + style SA fill:none,stroke:none; + direction LR + subgraph SANP[Node/Pod] + style SANP fill:none,stroke-dasharray: 7 5 + direction LR + subgraph "Local OPA Instance" + A_OPA["
OPA"]; + end + subgraph "App Instance" + A_Service["Service Logic"] -->|HTTP Call| A_OPA + end + end + end +`; + +const DistributedDiagram = () => ; + +export default DistributedDiagram; diff --git a/third_party/opa/docs/docs/management-introduction/assets/HostLocalDiagram.jsx b/third_party/opa/docs/docs/management-introduction/assets/HostLocalDiagram.jsx new file mode 100644 index 000000000000..de5e30745013 --- /dev/null +++ b/third_party/opa/docs/docs/management-introduction/assets/HostLocalDiagram.jsx @@ -0,0 +1,36 @@ +import Mermaid from "@theme/Mermaid"; + +const logoPath = require("./logo.png").default; + +const diagram = ` +graph TD; + subgraph LM[Library Model] + style LM fill:none,stroke:none; + direction LR + subgraph SI[Service Instance] + style SI fill:none,stroke-dasharray: 7 5 + B_Service["Service Logic"] <-->|Function Call| B_OPA["
OPA"]; + end + B_Policy["Policy & Data"] --> B_OPA; + end + + subgraph AM[Agent Model] + style AM fill:none,stroke:none; + direction LR + subgraph NP[Node/Pod] + style NP fill:none,stroke-dasharray: 7 5 + direction LR + subgraph "OPA Instance" + A_OPA["
OPA"]; + end + subgraph "App Instance" + A_Service["Service Logic"] <-->|HTTP Call| A_OPA + end + end + A_Policy["Policy & Data"] --> A_OPA; + end +`; + +const HostLocalDiagram = () => ; + +export default HostLocalDiagram; diff --git a/third_party/opa/docs/docs/management-introduction/assets/logo.png b/third_party/opa/docs/docs/management-introduction/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..56427178c446bc2196ce0f83f76b89b1439a75c4 GIT binary patch literal 38210 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 literal 0 HcmV?d00001 diff --git a/third_party/opa/docs/docs/management-introduction/index.md b/third_party/opa/docs/docs/management-introduction/index.md new file mode 100644 index 000000000000..3ff67896feb0 --- /dev/null +++ b/third_party/opa/docs/docs/management-introduction/index.md @@ -0,0 +1,42 @@ +--- +title: "Overview & Architecture" +--- + +OPA exposes a set of APIs that enable unified, logically centralized policy +management. Read this page if you are interested in how to build a control plane +around OPA that enables policy distribution and collection of important +telemetry data like decision logs. + +OPA enables low-latency, highly-available policy enforcement by providing a +lightweight engine for distributed architectures. By default, all of the policy +and data that OPA uses to make decisions is kept in-memory: + +import HostLocalDiagram from './assets/HostLocalDiagram'; + + + +OPA is designed to enable _distributed_ policy enforcement. You can run OPA next +to each and every service that needs to offload policy decision-making. By +colocating OPA with the services that require decision-making, you ensure that +policy decisions are rendered as fast as possible and in a highly-available +manner. + +import DistributedDiagram from './assets/DistributedDiagram'; + + + +To control and observe a set of OPAs, each OPA can be configured to connect to +management APIs that enable: + +- Policy distribution ([Bundles](./management-bundles)) +- Decision telemetry ([Decision Logs](./management-decision-logs)) +- Agent telemetry ([Status](./management-status)) +- Dynamic agent configuration ([Discovery](./management-discovery)) + +By configuring and implementing these management APIs you can unify control and +visibility over OPAs in your environments. OPA does not provide a control plane +service out-of-the-box. + +import ControlPlaneDiagram from './assets/ControlPlaneDiagram'; + + diff --git a/third_party/opa/docs/docs/management-status.md b/third_party/opa/docs/docs/management-status.md new file mode 100644 index 000000000000..5fcaf590d1c6 --- /dev/null +++ b/third_party/opa/docs/docs/management-status.md @@ -0,0 +1,313 @@ +--- +title: "Status" +--- + +OPA can periodically report status updates to remote HTTP servers. The +updates contain status information for OPA itself as well as the +[Bundles](./management-bundles) that have been downloaded and activated. + +OPA sends status reports whenever one of the following happens: + +- Bundles are downloaded and activated -- If the bundle download or activation fails for any reason, the status update + will include error information describing the failure. This includes Discovery bundles. +- A plugin state has changed -- All plugin status is reported, and an update to any plugin will + trigger a Status API report which contains the latest state. + +The status updates will include a set of labels that uniquely identify the +OPA instance. OPA automatically includes an `id` value in the label set that +provides a globally unique identifier or the running OPA instance and a +`version` value that provides the version of OPA. + +See the [Configuration Reference](./configuration) for configuration details. + +## Status Service API + +OPA expects the service to expose an API endpoint that will receive status +updates. + +```http +POST /status[/] HTTP/1.1 +Content-Type: application/json +``` + +The partition name is an optional path segment that can be used to route +status updates to different backends. If the partition name is not configured +on the agent, updates will be sent to `/status`. + + +```json +{ + "labels": { + "app": "my-example-app", + "id": "1780d507-aea2-45cc-ae50-fa153c8e4a5a", + "version": "{{ current_version }}" + }, + "bundles": { + "http/example/authz": { + "active_revision": "ABC", + "last_request": "2018-01-01T00:00:00.000Z", + "last_successful_request": "2018-01-01T00:00:00.000Z", + "last_successful_download": "2018-01-01T00:00:00.000Z", + "last_successful_activation": "2018-01-01T00:00:00.000Z", + "metrics": { + "timer_rego_data_parse_ns": 12345, + "timer_rego_module_compile_ns": 12345, + "timer_rego_module_parse_ns": 12345 + } + "name": "http/example/authz", + "size": 1048576, + "type": "snapshot", + } + }, + "decision_logs": { + "code": "decision_log_error", + "message": "Upload Failed", + "http_code": "400", + "metrics": { + "counter_decision_logs_dropped": "2", + "decision_logs_nd_builtin_cache_dropped": "1" + } + }, + "plugins": { + "bundle": { + "state": "OK" + }, + "discovery": { + "state": "OK" + }, + "status": { + "state": "OK" + } + }, + "metrics": { + "prometheus": { + "go_gc_cycles_automatic_gc_cycles_total": { + "name": "go_gc_cycles_automatic_gc_cycles_total", + "help": "Count of completed GC cycles generated by the Go runtime.", + "type": "COUNTER", + "metric": [ + { + "counter": { + "value": 1 + } + } + ] + }, + "go_gc_cycles_forced_gc_cycles_total": { + "name": "go_gc_cycles_forced_gc_cycles_total", + "help": "Count of completed GC cycles forced by the application.", + "type": "COUNTER", + "metric": [ + { + "counter": { + "value": 0 + } + } + ] + }, + "go_gc_cycles_total_gc_cycles_total": { + "name": "go_gc_cycles_total_gc_cycles_total", + "help": "Count of all completed GC cycles.", + "type": "COUNTER", + "metric": [ + { + "counter": { + "value": 1 + } + } + ] + }, + "go_gc_duration_seconds": { + "name": "go_gc_duration_seconds", + "help": "A summary of the pause duration of garbage collection cycles.", + "type": "SUMMARY", + "metric": [ + { + "summary": { + "sampleCount": "1", + "sampleSum": 4.1765e-05, + "quantile": [ + { + "quantile": 0, + "value": 4.1765e-05 + }, + { + "quantile": 0.25, + "value": 4.1765e-05 + }, + { + "quantile": 0.5, + "value": 4.1765e-05 + }, + { + "quantile": 0.75, + "value": 4.1765e-05 + }, + { + "quantile": 1, + "value": 4.1765e-05 + } + ] + } + } + ] + }, +------------------------------8< SNIP 8<------------------------------ + "http_request_duration_seconds": { + "name": "http_request_duration_seconds", + "help": "A histogram of duration for requests.", + "type": "HISTOGRAM", + "metric": [ + { + "label": [ + { + "name": "code", + "value": "200" + }, + { + "name": "handler", + "value": "v1/data" + }, + { + "name": "method", + "value": "get" + } + ], + "histogram": { + "sampleCount": "2", + "sampleSum": 0.00060022, + "bucket": [ + { + "cumulativeCount": "0", + "upperBound": 1e-06 + }, + { + "cumulativeCount": "0", + "upperBound": 5e-06 + }, + { + "cumulativeCount": "0", + "upperBound": 1e-05 + }, + { + "cumulativeCount": "0", + "upperBound": 5e-05 + }, + { + "cumulativeCount": "0", + "upperBound": 0.0001 + }, + { + "cumulativeCount": "2", + "upperBound": 0.0005 + }, + { + "cumulativeCount": "2", + "upperBound": 0.001 + }, + { + "cumulativeCount": "2", + "upperBound": 0.01 + }, + { + "cumulativeCount": "2", + "upperBound": 0.1 + }, + { + "cumulativeCount": "2", + "upperBound": 1 + } + ] + } + } + ] + } + } + } +} +``` + + +Status updates contain the following fields: + +| Field | Type | Description | +| --------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `labels` | `object` | Set of key-value pairs that uniquely identify the OPA instance. | +| `bundles` | `object` | Set of objects describing the status for each bundle configured with OPA. | +| `bundles[_].name` | `string` | Name of bundle that the OPA instance is configured to download. | +| `bundles[_].active_revision` | `string` | Opaque revision identifier of the last successful activation. | +| `bundles[_].last_request` | `string` | RFC3339 timestamp of last bundle request. This timestamp should be >= to the successful request timestamp in normal operation. | +| `bundles[_].last_successful_request` | `string` | RFC3339 timestamp of last successful bundle request. This timestamp should be >= to the successful download timestamp in normal operation. | +| `bundles[_].last_successful_download` | `string` | RFC3339 timestamp of last successful bundle download. | +| `bundles[_].last_successful_activation` | `string` | RFC3339 timestamp of last successful bundle activation. | +| `bundles[_].metrics` | `object` | Metrics from the last update of the bundle. | +| `bundles[_].code` | `string` | If present, indicates error(s) occurred activating this bundle. | +| `bundles[_].message` | `string` | Human readable messages describing the error(s). | +| `bundles[_].http_code` | `number` | If present, indicates an erroneous HTTP status code that OPA received downloading this bundle. | +| `bundles[_].errors` | `array` | Collection of detailed parse or compile errors that occurred during activation of this bundle. | +| `bundles[_].size` | `number` | Bundle size, in bytes | +| `bundles[_].type` | `string` | Bundle type, either `snapshot` or `delta` | +| `discovery.name` | `string` | Name of discovery bundle that the OPA instance is configured to download. | +| `discovery.active_revision` | `string` | Opaque revision identifier of the last successful discovery activation. | +| `discovery.last_request` | `string` | RFC3339 timestamp of last discovery bundle request. This timestamp should be >= to the successful request timestamp in normal operation. | +| `discovery.last_successful_request` | `string` | RFC3339 timestamp of last successful discovery bundle request. This timestamp should be >= to the successful download timestamp in normal operation. | +| `discovery.last_successful_download` | `string` | RFC3339 timestamp of last successful discovery bundle download. | +| `discovery.last_successful_activation` | `string` | RFC3339 timestamp of last successful discovery bundle activation. | +| `decision_logs.code` | `string` | If present, indicates error(s) occurred during decision log upload event. | +| `decision_logs.message` | `string` | Human readable messages describing the error(s). | +| `decision_logs.http_code` | `number` | If present, indicates an erroneous HTTP status code that OPA received during a decision log upload event. | +| `decision_logs.metrics` | `object` | Metrics from the last decision log upload event. | +| `plugins` | `object` | A set of objects describing the state of configured plugins in OPA's runtime. | +| `plugins[_].state` | `string` | The state of each plugin. | +| `metrics.prometheus` | `object` | Global performance metrics for the OPA instance. | + +If the discovery bundle download or activation failed, the status update will contain +the following additional fields. + +| Field | Type | Description | +| ------------------- | -------- | ------------------------------------------------------------------------------- | +| `discovery.code` | `string` | If present, indicates error(s) occurred. | +| `discovery.message` | `string` | Human readable messages describing the error(s). | +| `discovery.errors` | `array` | Collection of detailed parse or compile errors that occurred during activation. | + +Services should reply with a `2xx` HTTP status if the status update is +processed successfully. + +## Local Status Logs + +Local console logging of status updates can be enabled via the `console` config option. +This does not require any remote server. Example of minimal config to enable: + +```yaml +status: + console: true +``` + +This will dump all status updates to the console. See +[Configuration Reference](./configuration) for more details. + +> Warning: Status update messages are somewhat infrequent but can be very verbose! The +> `metrics.prometheus` portion of the status update in particular can create a considerable +> amount of log text at info level. + +## Prometheus Status Metrics + +Prometheus status metrics can be enabled via the `prometheus` config option. (see [the configuration documentation](./configuration/#status)) +Example of minimal config to enable: + +```yaml +status: + prometheus: true + prometheus_config: + collectors: + bundle_loading_duration_ns: + buckets: [1, 1000, 10_000, 1e8] +``` + +When enabled the OPA instance's Prometheus endpoint exposes the metrics described on [the monitoring documentation](./monitoring/#status-metrics). + +## Ecosystem Projects + + +Here are some projects that use the OPA Status API to report status updates. + diff --git a/third_party/opa/docs/docs/monitoring.md b/third_party/opa/docs/docs/monitoring.md new file mode 100644 index 000000000000..a7a1d2eb574b --- /dev/null +++ b/third_party/opa/docs/docs/monitoring.md @@ -0,0 +1,97 @@ +--- +title: Monitoring +--- + +## OpenTelemetry + +When run as a server and configured accordingly, OPA will emit spans to an +[OpenTelemetry](https://opentelemetry.io/) collector via gRPC. + +Each [REST API](./rest-api/) request sent to the server will start a span. +If processing the request involves policy evaluation, and that in turn uses +[`http.send`](./policy-reference/#http), those HTTP clients will emit descendant spans. + +Furthermore, spans exported for policy evaluation requests will contain an +attribute `opa.decision_id` of the evaluation's decision ID _if_ the server +has decision logging enabled. + +See [the configuration documentation](./configuration/#distributed-tracing) +for all OpenTelemetry-related configurables. + +## Prometheus + +OPA exposes an HTTP endpoint that can be used to collect performance metrics +for all API calls. The Prometheus endpoint is enabled by default when you run +OPA as a server. + +You can enable metric collection from OPA with the following `prometheus.yml` config: + +```yaml +global: + scrape_interval: 15s +scrape_configs: +- job_name: "opa" + metrics_path: "/metrics" + static_configs: + - targets: + - "localhost:8181" +``` + +The Prometheus endpoint exports Go runtime metrics as well as HTTP request latency metrics for all handlers (e.g., `v1/data`). + +| Metric name | Metric type | Description | Status | +| -------------------------------- | ----------- | ------------------------------------------------------------------ | ------ | +| go_gc_duration_seconds | summary | A summary of the GC invocation durations. | STABLE | +| go_goroutines | gauge | Number of goroutines that currently exist. | STABLE | +| go_info | gauge | Information about the Go environment. | STABLE | +| go_memstats_alloc_bytes | gauge | Number of bytes allocated and still in use. | STABLE | +| go_memstats_alloc_bytes_total | counter | Total number of bytes allocated, even if freed. | STABLE | +| go_memstats_buck_hash_sys_bytes | gauge | Number of bytes used by the profiling bucket hash table. | STABLE | +| go_memstats_frees_total | counter | Total number of frees. | STABLE | +| go_memstats_gc_sys_bytes | gauge | Number of bytes used for garbage collection system metadata. | STABLE | +| go_memstats_heap_alloc_bytes | gauge | Number of heap bytes allocated and still in use. | STABLE | +| go_memstats_heap_idle_bytes | gauge | Number of heap bytes waiting to be used. | STABLE | +| go_memstats_heap_inuse_bytes | gauge | Number of heap bytes that are in use. | STABLE | +| go_memstats_heap_objects | gauge | Number of allocated objects. | STABLE | +| go_memstats_heap_released_bytes | gauge | Number of heap bytes released to OS. | STABLE | +| go_memstats_heap_sys_bytes | gauge | Number of heap bytes obtained from system. | STABLE | +| go_memstats_last_gc_time_seconds | gauge | Number of seconds since 1970 of last garbage collection. | STABLE | +| go_memstats_lookups_total | counter | Total number of pointer lookups. | STABLE | +| go_memstats_mallocs_total | counter | Total number of mallocs. | STABLE | +| go_memstats_mcache_inuse_bytes | gauge | Number of bytes in use by mcache structures. | STABLE | +| go_memstats_mcache_sys_bytes | gauge | Number of bytes used for mcache structures obtained from system. | STABLE | +| go_memstats_mspan_inuse_bytes | gauge | Number of bytes in use by mspan structures. | STABLE | +| go_memstats_mspan_sys_bytes | gauge | Number of bytes used for mspan structures obtained from system. | STABLE | +| go_memstats_next_gc_bytes | gauge | Number of heap bytes when next garbage collection will take place. | STABLE | +| go_memstats_other_sys_bytes | gauge | Number of bytes used for other system allocations. | STABLE | +| go_memstats_stack_inuse_bytes | gauge | Number of bytes in use by the stack allocator. | STABLE | +| go_memstats_stack_sys_bytes | gauge | Number of bytes obtained from system for stack allocator. | STABLE | +| go_memstats_sys_bytes | gauge | Number of bytes obtained from system. | STABLE | +| go_threads | gauge | Number of OS threads created. | STABLE | +| http_request_duration_seconds | histogram | A histogram of duration for requests. | STABLE | + +### Status Metrics + +When Prometheus is enabled in the status plugin (see [Configuration](./configuration/#status)), the OPA instance's Prometheus endpoint also exposes these metrics: + +| Metric name | Metric type | Description | Status | +| ------------------------------ | ----------- | ------------------------------------------------------ | ------ | +| opa_info | gauge | Information about the OPA environment. | STABLE | +| plugin_status_gauge | gauge | Number of plugins by name and status. | STABLE | +| bundle_loaded_counter | counter | Number of bundles loaded with success. | STABLE | +| bundle_failed_load_counter | counter | Number of bundles that failed to load. | STABLE | +| last_bundle_request | gauge | Last bundle request in UNIX nanoseconds. | STABLE | +| last_success_bundle_activation | gauge | Last successful bundle activation in UNIX nanoseconds. | STABLE | +| last_success_bundle_download | gauge | Last successful bundle download in UNIX nanoseconds. | STABLE | +| last_success_bundle_request | gauge | Last successful bundle request in UNIX nanoseconds. | STABLE | +| bundle_loading_duration_ns | histogram | A histogram of duration for bundle loading. | STABLE | + +## Health Checks + +OPA exposes a `/health` API endpoint that can be used to perform health checks. +See [Health API](./rest-api#health-api) for details. + +## Status API + +OPA provides a plugin which can push status to a remote service. +See [Status API](./management-status) for details. diff --git a/third_party/opa/docs/docs/oauth-oidc.md b/third_party/opa/docs/docs/oauth-oidc.md new file mode 100644 index 000000000000..9c8d81ff41ba --- /dev/null +++ b/third_party/opa/docs/docs/oauth-oidc.md @@ -0,0 +1,109 @@ +--- +title: OAuth2 and OIDC Samples +--- + +OAuth2 and OpenID Connect are both pervasive technologies in modern identity systems. While verification of JSON web tokens issued by these systems is documented in the [policy reference](https://www.openpolicyagent.org/docs/latest/policy-reference/#token-verification), the policy examples below aim to cover some other common use cases. + +## Metadata discovery + +Rather than storing endpoints and other metadata as part of policy data, the authorization server metadata endpoint may be queried for this data. + +```rego +package oidc + +issuers := {"https://issuer1.example.com", "https://issuer2.example.com"} + +metadata_discovery(issuer) := http.send({ + "url": concat("", [issuers[issuer], "/.well-known/openid-configuration"]), + "method": "GET", + "force_cache": true, + "force_cache_duration_seconds": 86400 # Cache response for 24 hours +}).body + +claims := jwt.decode(input.token)[1] +metadata := metadata_discovery(claims.iss) + +jwks_endpoint := metadata.jwks_uri +token_endpoint := metadata.token_endpoint +``` + +## Token verification using JWKS endpoint + +Below example uses the keys published at the JWKS endpoint of the authorization server for token verification. + +```rego +package oidc + +jwks_request(url) := http.send({ + "url": url, + "method": "GET", + "force_cache": true, + "force_cache_duration_seconds": 3600 # Cache response for an hour +}) + +jwks := jwks_request("https://authorization-server.example.com/jwks").raw_body + +verified := io.jwt.verify_rs256(input.token, jwks) +``` + +### Key rotation + +Use the keys published at the JWKS endpoint of the authorization server for token verification, with [key rotation](https://openid.net/specs/openid-connect-core-1_0.html#RotateSigKeys) taken into account. + +```rego +package oidc + +jwks_request(url) := http.send({ + "url": url, + "method": "GET", + "force_cache": true, + "force_cache_duration_seconds": 3600 +}) + +jwt_unverified := io.jwt.decode(input.token) +jwt_header := jwt_unverified[0] + +# Use the key ID (kid) from the token as a cache key - if a new kid is encountered +# we obtain a fresh JWKS object as the keys have likely been rotated. +jwks_url := concat("?", [ + "https://authorization-server.example.com/jwks", + urlquery.encode_object({"kid": jwt_header.kid}), +]) +jwks := jwks_request(jwks_url).raw_body + +jwt_verified := jwt_unverified { + io.jwt.verify_rs256(input.token, jwks) +} + +claims_verified := jwt_verified[1] +``` + +## Token retrieval + +Programmatically obtain an OAuth2 access token following the client credentials or resource owner password credential flow. + +```rego +package oauth2 + +token := t { + response := http.send({ + "url": "https://authorization-server.example.com/token", + "method": "POST", + "headers": { + "Content-Type": "application/x-www-form-urlencoded", + "Authorization": concat(" ", [ + "Basic", + base64.encode(sprintf("%v:%v", [client_id, client_secret])) + ]), + }, + # To use the resource owner password credentials flow, change grant_type + # to "password" and add username and password parameters to the body + "raw_body": "grant_type=client_credentials", + "force_cache": true, + "force_cache_duration_seconds": 3595, # Given an `expires_in` value of 3600 + }) + response.status_code == 200 + + t := response.body.access_token +} +``` diff --git a/third_party/opa/docs/docs/philosophy/index.md b/third_party/opa/docs/docs/philosophy/index.md new file mode 100644 index 000000000000..cea5f33f20d9 --- /dev/null +++ b/third_party/opa/docs/docs/philosophy/index.md @@ -0,0 +1,209 @@ +--- +title: Philosophy +sidebar_position: 2 +--- + +A [**policy**](#policy) is a set of rules that governs the behavior of a +software service. That policy could describe rate-limits, names of trusted +servers, the clusters an application should be deployed to, permitted network +routes, or accounts a user can withdraw money from. + +Authorization is a special kind of policy that often dictates which people or +machines can run which actions on which resources. Authorization is sometimes +confused with Authentication: how people or machines prove they are who they say +they are. Authorization and more generally policy often utilize the results of +authentication (the username, user attributes, groups, claims), but makes +decisions based on far more information than just who the user is. Generalizing +away from authorization back to policy makes the distinction even clearer +because some policy decisions have nothing to do with users, e.g. policy simply +describes invariants that must hold in a software system (e.g. all binaries must +come from a trusted source). + +Today policy is often a hard-coded feature of the software service it actually +governs. Open Policy Agent lets you [**decouple policy**](#policy-decoupling) +from that software service so that the people responsible for policy can read, +write, analyze, version, distribute, and in general manage policy separate from +the service itself. OPA also gives you a unified toolset to decouple policy from +any software service you like, and to write context-aware policies using any +context that you like. In short, OPA helps you decouple any policy using any +context from any software system. + +## What is Policy? {#policy} + +All organizations have policies. Policies are essential to the long-term success +of organizations because they encode important knowledge about how to comply +with legal requirements, work within technical constraints, avoid repeating +mistakes, and so on. + +In their simplest form, policies can be applied manually based on rules that are +written down or conventions that are unspoken but permeate an organization’s +culture. Policies may also be enforced with application logic or statically +configured at deploy time. + +## What is Policy Decoupling? {#policy-decoupling} + +Software services should allow policies to be specified declaratively, updated +at any time without recompiling or redeploying, and enforced automatically +(which is especially valuable when decisions need to be made faster than humanly +possible). + +Decoupling policy helps you build such software services at scale, makes them +adaptable to changing business requirements, improves the ability to discover +violations and conflicts, increases the consistency of policy compliance, and +mitigates the risk of human error. The policies you write can adapt more easily +to the external environment--to factors that the developer could never have +imagined at the time the software service was designed. + +For example, a cloud computing service could answer questions such as: + +- Can I add compute capacity? +- In what regions can I add compute capacity? +- Which instances are currently running in the wrong region? + +## What is OPA? + +OPA is a lightweight general-purpose policy engine that can be co-located with +your service. You can integrate OPA as a sidecar, host-level daemon, or library. + +Services offload policy decisions to OPA by executing _queries_. OPA evaluates +policies and data to produce query results (which are sent back to the client). +Policies are written in a high-level declarative language and can be loaded +dynamically into OPA remotely via APIs or through the local filesystem. + +## Why use OPA? + +OPA is a full-featured policy engine that offloads policy decisions from your +software. You can think of it as a concierge for your software who can answer +detailed questions on behalf of your users to meet their specific needs. +OPA provides the building blocks for enabling better control and visibility over +policy in your systems. + +Without OPA, you need to implement policy management for your software from scratch. +Required components such as the policy language (syntax _and_ semantics) and the +evaluation engine need to be carefully designed, implemented, tested, documented, +and then maintained to ensure correct behaviour and a positive user experience +for your customers. On top of that you must carefully consider security, tooling, +management, and more. That's a lot of work. + +## How Does OPA Work? + +See the [Introduction](..) for an overview of how OPA works and how to get started. + +## The OPA Document Model + +OPA policies (written in Rego) make decisions based on hierarchical structured data. +Sometimes we refer to this data as a document, set of attributes, piece of context, +or even just "JSON" [1]. Importantly, OPA policies can make decisions based on _arbitrary_ +structured data. OPA itself is not tied to any particular domain model. Similarly, +OPA policies can represent decisions as arbitrary structured data (e.g., booleans, +strings, maps, maps of lists of maps, etc.) + +Data can be loaded into OPA from outside world using push or pull interfaces that operate +synchronously or asynchronously with respect to policy evaluation. We refer to all data +loaded into OPA from the outside world as **base documents** [2]. These base documents +almost always contribute to your policy decision-making logic. However, your policies can +also make decisions based on each other. Policies almost always consist of multiple rules +that refer to other rules (possibly authored by different groups). In OPA, we refer +to the values generated by rules (a.k.a., decisions) as **virtual documents**. The term +"virtual" in this case just means the document is _computed_ by the policy, i.e., +it's not loaded into OPA from the outside world. + +Base and virtual documents can represent the exact same kind of information, e.g., numbers, +strings, lists, maps, and so on. Moreover, with Rego, you can refer to both base and virtual +documents using the exact same dot/bracket-style reference syntax. Consistency across the +types of values that can be represented and the way those values are referenced means that +_policy authors only need to learn one way of modeling and referring to information +that drives policy decision-making_. Additionally, since there is no conceptual difference +in the types of values or the way you refer to those values in base and virtual documents, +Rego lets you refer to _both_ base and virtual documents through a global variable +called `data`. Similarly, OPA lets you query for both base and virtual documents via the +`/v1/data` HTTP API [3]. This is why queries for just `data` (or `data.foo` or `data.foo.bar`, etc.) +return the combination of base and virtual documents located under that path. + +Since base documents come from outside of OPA, their location under `data` is controlled +by the software doing the loading. On the other hand, the location of virtual +documents under `data` is controlled by policies themselves using the `package` directive +in the language. + +Base documents can be pushed or pulled into OPA _asynchronously_ by replicating data +into OPA when the state of the world changes. This can happen periodically or when some +event (like a database change notification) occurs. Base documents loaded asynchronously +are always accessed under the `data` global variable. On the other hand, base documents can +also be pushed or pulled into OPA _synchronously_ when your software queries OPA for policy +decisions. We refer to base documents pushed synchronously as "input". Policies can +access these inputs under the `input` global variable. To pull base documents during +policy evaluation, OPA exposes (and can be extended with custom) built-in functions like +`http.send`. Built-in function return values can be assigned to local variables and +surfaced in virtual documents. Data loaded synchronously is kept outside of `data` to +avoid naming conflicts. + +The following table summarizes the different models for loading base documents into OPA, +how they can be referenced inside of policies, and the actual mechanism(s) for loading. + +| Model | How to access in Rego | How to integrate with OPA | +| ----------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------- | +| Asynchronous Push | The `data` global variable | Invoke OPA's API(s), e.g., `PUT /v1/data` | +| Asynchronous Pull | The `data` global variable | Configure OPA's [Bundle](./management-bundles) feature | +| Synchronous Push | The `input` global variable | Provide data in policy query, e.g., inside the body of `POST /v1/data` | +| Synchronous Pull | The [built-in functions](./policy-reference), e.g., `http.send` | N/A | + +Data loaded asynchronously into OPA is cached in-memory so that it can be read efficiently +during policy evaluation. Similarly, policies are also cached in-memory to ensure +high-performance and high-availability. Data _pulled_ synchronously can also be +cached in-memory. For more information on loading external data into OPA, including tradeoffs, +see the [External Data](./external-data) page. + +The following diagram illustrates the base and virtual document model described above for a +hypothetical policy that renders authorization decisions (named `data.acme.allow`) based on: + +- API request information pushed synchronously located under `input`. +- Entitlements data pulled asynchronously and located under `data.entitlements`. +- Resource data pulled synchronously during policy evaluation using the `http.send` built-in function. + +The entitlements and resource information is _abstracted_ by rules that generate +virtual documents named `data.iam.user_has_role` and `data.acme.user_is_assigned` respectively. + +```mermaid +flowchart TD + allow["data.acme.allow"] + user_has_role["data.acme.user_has_role"] + user_is_assigned["data.acme.user_is_assigned"] + input_request["input.request
sync/push"] + input_subject["input.subject
sync/push"] + data_entitlements["data.entitlements
async/pull"] + http_send["http.send(request)
sync/pull"] + + input_request --> user_is_assigned + input_request --> allow + user_is_assigned --> allow + user_has_role --> allow + + data_entitlements --> user_has_role + http_send --> user_is_assigned + + subgraph Legend + b["Base Document"] + v["Virtual Document"] + b -->|Contributes to| v + end + + style allow fill:#fff1c1 + style user_has_role fill:#fff1c1 + style user_is_assigned fill:#fff1c1 + style v fill:#fff1c1 +``` + +> [1] OPA has excellent support for loading JSON and YAML because they are prevalent +> in modern systems; however, OPA is not tied to any particular data format. OPA +> uses its own internal representation for structures like maps and lists (a.k.a., +> objects and arrays in JSON.) + +> [2] The term "document" comes from the document-oriented database world. Document +> is just a generic term to refer to data or information encoded in some standard +> format like JSON, YAML, XML, etc. Document-oriented data does not have to adhere +> to a strict schema like data in the relational world. Documents are often deeply +> nested, hierarchical data structures containing several levels of embedded +> maps and lists. + +> [3] Internally, HTTP requests like `GET /v1/data` or `GET /v1/data/foo/bar` are turned +> into Rego queries that are almost identical to the HTTP path (e.g., `data` or `data.foo.bar`) diff --git a/third_party/opa/docs/docs/policy-language.md b/third_party/opa/docs/docs/policy-language.md new file mode 100644 index 000000000000..519b89f41962 --- /dev/null +++ b/third_party/opa/docs/docs/policy-language.md @@ -0,0 +1,3555 @@ +--- +title: Policy Language +sidebar_position: 3 +--- + +OPA is purpose built for reasoning about information represented in structured +documents. The data that your service and its users publish can be inspected and +transformed using OPA’s native query language Rego. + +## What is Rego? + +Rego was inspired by [Datalog](https://en.wikipedia.org/wiki/Datalog), which is +a well understood, decades old query language. Rego extends Datalog to support +structured document models such as JSON. + +Rego queries are assertions on data stored in OPA. These queries can be used to +define policies that enumerate instances of data that violate the expected state +of the system. + +## Why use Rego? + +Use Rego for defining policy that is easy to read and write. + +Rego focuses on providing powerful support for referencing nested documents and +ensuring that queries are correct and unambiguous. + +Rego is declarative so policy authors can focus on what queries should return +rather than how queries should be executed. These queries are simpler and more +concise than the equivalent in an imperative language. + +Like other applications which support declarative query languages, OPA is able +to optimize queries to improve performance. + +## Learning Rego + +While reviewing the examples below, you might find it helpful to follow along +using the online [OPA playground](http://play.openpolicyagent.org). The +playground also allows sharing of examples via URL which can be helpful when +asking questions on the [OPA Slack](https://slack.openpolicyagent.org). +In addition to these official resources, you may also be interested to check +out the +community learning materials and +tools. + +## The Basics + +This section introduces the main aspects of Rego. + +The simplest rule is a single expression and is defined in terms of a +[Scalar Value](#scalar-values). This `example` package defines a rule +called `pi` that contains the value of pi: + +```rego +package example + +pi := 3.14159 +``` + + + +Rules can also be defined in terms of [Composite Values](#composite-values): + +```rego +package example + +rect := {"width": 2, "height": 4} +``` + + + +You can compare two scalar or composite values, and when you do so you are +checking if the two values are the same JSON value. + +```rego +package example + +result := rect == {"width": 2, "height": 4} +``` + + + +You can define a new concept using a rule. For example, `v` below is true if the +equality expression is true. +If we evaluate `v`, the result is `undefined` because the body of the rule never +evaluates to `true`. As a result, the document generated by the rule is not +defined. + +```rego +package example + +v if "hello" == "world" +``` + + + +Expressions that refer to undefined values are also undefined. This includes comparisons such as `!=`. + +```rego +package example + +v if "hello" == "world" + +# also undefined +w if v != true +``` + + + +We can define rules in terms of [variables](#variables) as well: + +```rego +package example + +t if { + x := 42 + y := 41 + x > y +} +``` + + + +When evaluating rule bodies, OPA searches for variable bindings that make all of +the expressions true. There may be multiple sets of bindings that make the rule +body true. The rule body can be understood intuitively as: + +``` +expression-1 AND expression-2 AND ... AND expression-N +``` + +The rule itself can be understood intuitively as: + +``` +rule-name IS value IF body +``` + +If the **value** is not specified, it defaults to the boolean value of **true**. + +Rego [references](#references) help you refer to nested documents. +The rule `prod_exists` asserts that there exists (at least) one document +within `sites` where the `name` attribute equals `"prod"`. + +```rego +package sites + +sites := [{"name": "prod"}, {"name": "smoke1"}, {"name": "dev"}] + +prod_exists if { + some site in sites + site.name == "prod" +} +``` + + + +We can generalize the example above with a rule that defines a set document +instead of a boolean value. Here `site_names` is a set of all the site's name +values. + +```rego +package sites + +site_names contains name if { + some site in sites + name := site.name +} +``` + + + +This section introduced the main aspects of Rego. The rest of this document +walks those new to Rego through other important aspects of the language. +Please review the [Policy Reference](./policy-reference) for more detailed +information about the Rego language. + +## Scalar Values + +Scalar values are the simplest type of term in Rego. Scalar values can be [strings](#strings), numbers, booleans, or null. + +Documents can be defined solely in terms of scalar values. This is useful for defining constants that are referenced in multiple places. For example: + +```rego +package scalars + +greeting := "Hello" +max_height := 42 +pi := 3.14159 +allowed := true +location := null +``` + + + +## Strings + +Rego supports two different types of syntax for declaring strings. The first is likely to be the most familiar: characters surrounded by double quotes. +In such strings, certain characters must be escaped to appear in the string, such as double quotes themselves, backslashes, etc. See the [Policy Reference](./policy-reference/#grammar) for a formal definition. + +The other type of string declaration is a raw string declaration. These are made of characters surrounded by backticks (`` ` ``), with the exception +that raw strings may not contain backticks themselves. Raw strings are what they sound like: escape sequences are not interpreted, but instead taken +as the literal text inside the backticks. For example, the raw string `` `hello\there` `` will be the text "hello\there", not "hello" and "here" +separated by a tab. Raw strings are particularly useful when constructing regular expressions for matching, as it eliminates the need to double +escape special characters. + +A simple example is a regex to match a valid Rego variable. With a regular string, the regex is `"[a-zA-Z_]\\w*"`, but with raw strings, it becomes `` `[a-zA-Z_]\w*` ``. + +## Composite Values + +Composite values define collections. In simple cases, composite values can be treated as constants like [Scalar Values](#scalar-values): + +```rego +package composite + +cuboid := {"width": 3, "height": 4, "depth": 5} +``` + + + +Composite values can also be defined in terms of [variables](#variables) or [references](#references). For example: + +```rego +package composite_variables + +a := 42 +b := false +c := null +d := {"a": a, "x": [b, c]} +``` + + + +By defining composite values in terms of variables and references, rules can define abstractions over raw data and other rules. + +### Arrays + +Arrays are ordered collections of values. Arrays in Rego are zero-indexed, and may contain any value, including +variable references. + +```rego +package arrays + +pi := 3.14 +arr := [1, "two", pi*2] +last := arr[2] +``` + + + +Use arrays when order matters or when duplicate values are required. + +### Objects + +Objects are unordered key-value collections. In Rego, any value type can be +used as an object key. For example, the following assignment maps port **numbers** +to a list of IP addresses (represented as strings). + +```rego +package objects + +ips_by_port := { + 80: ["10.0.0.1", "10.10.10.1"], + 443: ["10.1.1.1"], +} + +result := ips_by_port[80] +``` + + + +When Rego values are converted to JSON non-string object keys are marshalled +as strings (because JSON does not support non-string object keys). + +```rego +package objects + +# when queried, this will be converted to JSON +json := ips_by_port +``` + + + +### Sets + +In addition to arrays and objects, Rego supports set values. Sets are unordered +collections of unique values. Just like other composite values, sets can be +defined in terms of scalars, variables, references, and other composite values. +For example: + +```rego +package sets + +s1 := {1,2,3} +s2 := {3,2,1} + +sets_equal := s1 == s2 +``` + + + +:::warning +Set documents are collections of values without keys or order. OPA represents +sets as arrays when serializing to JSON or other formats that do not support a +set data type. The important distinction between sets and arrays or objects is +that sets are unkeyed while arrays and objects are keyed, i.e., you cannot refer +to the index of an element within a set. +::: + +Sets share their curly-brace syntax with objects, and an empty object is +defined with `{}`, an empty set has to be constructed with a different syntax: + +```rego +package sets + +empty := count(set()) +not_empty := count({1, 2, 3}) +empty_object := count({}) +not_equal := {} == {e| some e in []} +``` + + + +:::danger +`count({})` will still return `0` because `{}` is an empty object. However, +since `{}` is not a set, it will not equal `set()` or something that evaluates +to an empty set. +::: + +## Variables + +Variables are another kind of term in Rego. They appear in both the head and body of rules. + +Variables appearing in the head of a rule can be thought of as input and output of the rule. Unlike many programming languages, where a variable is either an input or an output, in Rego a variable is simultaneously an input and an output. If a query supplies a value for a variable, that variable is an input, and if the query does not supply a value for a variable, that variable is an output. + +For example: + +```rego +package variables + +sites := [ + {"name": "prod"}, + {"name": "smoke1"}, + {"name": "dev"} +] + +# name is a var in the head and body +q contains name if { + # site is a var only used in the body + some site in sites + name := site.name +} +``` + + + +In this case, we evaluate `q` with a variable `x` (which is not bound to a value). As a result, the query returns all of the values for `x` and all of the values for `q[x]`, which are always the same because `q` is a set. + +```rego +package variables + +result := { x | q[x] } +``` + + + +On the other hand, if we evaluate `q` with an input value for `name` we can determine whether `name` exists in the document defined by `q`: + +```rego +package variables + +result := q["dev"] +``` + + + +Variables appearing in the head of a rule must also appear in a non-negated equality expression within the same rule. This property ensures that if the rule is evaluated and all of the expressions evaluate to true for some set of variable bindings, the variable in the head of the rule will be defined. + +## References + +References are used to access nested documents. + +

+ +The examples that follow use some data defined in `data.example.*` here + +```rego +package example + +sites := [ + { + "region": "east", + "name": "prod", + "servers": [ + { + "name": "web-0", + "hostname": "hydrogen" + }, + { + "name": "web-1", + "hostname": "helium" + }, + { + "name": "db-0", + "hostname": "lithium" + } + ] + }, + { + "region": "west", + "name": "smoke", + "servers": [ + { + "name": "web-1000", + "hostname": "beryllium" + }, + { + "name": "web-1001", + "hostname": "boron" + }, + { + "name": "db-1000", + "hostname": "carbon" + } + ] + }, + { + "region": "west", + "name": "dev", + "servers": [ + { + "name": "web-dev", + "hostname": "nitrogen" + }, + { + "name": "db-dev", + "hostname": "oxygen" + } + ] + } +] + +apps := [ + { + "name": "web", + "servers": ["web-0", "web-1", "web-1000", "web-1001", "web-dev"] + }, + { + "name": "mysql", + "servers": ["db-0", "db-1000"] + }, + { + "name": "mongodb", + "servers": ["db-dev"] + } +] + +containers := [ + { + "image": "redis", + "ipaddress": "10.0.0.1", + "name": "big_stallman" + }, + { + "image": "nginx", + "ipaddress": "10.0.0.2", + "name": "cranky_euclid" + } +] +``` + + + +
+ +The simplest reference contains no variables. For example, the following reference returns the hostname of the second server in the first site document from our example data: + +```rego +package references + +import data.example.sites + +result := sites[0].servers[1].hostname +``` + + + +References are typically written using the “dot-access” style. The canonical form does away with `.` and closely resembles dictionary lookup in a language such as Python: + +```rego +package references + +import data.example.sites + +result := sites[0]["servers"][1]["hostname"] +``` + + + +Both forms are valid, however, the dot-access style is typically more readable. Note that there are four cases where brackets must be used: + +1. String keys containing characters other than `[a-z]`, `[A-Z]`, `[0-9]`, or `_` (underscore). +2. Non-string keys such as numbers, booleans, and null. +3. Variable keys which are described later. +4. Composite keys which are described later. + +The prefix of a reference identifies the root document for that reference. In +the example above this is `sites`. The root document may be: + +- a local variable inside a rule. +- a rule inside the same package. +- a document stored in OPA. +- a documented temporarily provided to OPA as part of a transaction. +- an array, object or set, e.g. `[1, 2, 3][0]`. +- a function call, e.g. `split("a.b.c", ".")[1]`. +- a [comprehension](#comprehensions). + +### Variable Keys + +References can include variables as keys. References written this way are used to select a value from every element in a collection. + +The following reference will select the hostnames of all the servers in our +example data: + +```rego +package references + +import data.example.sites + +result := {h| h := sites[i].servers[j].hostname } +``` + + + +Conceptually, this is the same as the following imperative code: + +```python +def hostnames(sites): + result = [] + + for site in sites: + for server in site.servers: + result.append(server.hostname) + + return result +``` + +In the reference above, we effectively used variables named `i` and `j` to iterate the collections. If the variables are unused outside the reference, we prefer to replace them with an underscore (`_`) character. The reference above can be rewritten as: + +```rego +sites[_].servers[_].hostname +``` + +The underscore is special because it cannot be referred to by other parts of the rule, e.g., the other side of the expression, another expression, etc. The underscore can be thought of as a special iterator. Each time an underscore is specified, a new iterator is instantiated. + +:::info +Under the hood, OPA translates the `_` character to a unique variable name that does not conflict with variables and rules that are in scope. +::: + +### Composite Keys + +References can include [composite values](#composite-values) as keys if the key is being used to refer into a set. Composite keys may not be used in refs +for base data documents, they are only valid for references into virtual documents. + +This is useful for checking for the presence of composite values within a set, or extracting all values within a set matching some pattern. +For example: + +```rego +package composite_key + +s := {[1, 2], [1, 4], [2, 6]} + +result := { + "exists": {e| e:= s[[1, 2]] }, + "matching": {e| e:= s[[1, _]] } +} +``` + + + +### Multiple Expressions + +Rules are often written in terms of multiple expressions that contain references to documents. In the following example, the rule defines a set of arrays where each array contains an application name and a hostname of a server where the application is deployed. + +```rego +package multiple_exprs + +import data.example.apps +import data.example.sites + +apps_and_hostnames contains [name, hostname] if { + some i, j, k + name := apps[i].name + server := apps[i].servers[_] + sites[j].servers[k].name == server + hostname := sites[j].servers[k].hostname +} +``` + + + +Don't worry about understanding everything in this example right now. There are just two important points: + +1. Several variables appear more than once in the body. When a variable is used in multiple locations, OPA will only produce documents for the rule with the variable bound to the same value in all expressions. +2. The rule is joining the `apps` and `sites` documents implicitly. In Rego (and other languages based on Datalog), joins are implicit. + +### Self-Joins + +Using a different key on the same array or object provides the equivalent of self-join in SQL. For example, the following rule defines a document containing apps deployed on the same site as `"mysql"`: + +```rego +package multiple_exprs + +import data.example.apps +import data.example.sites + +same_site contains apps[k].name if { + some i, j, k + apps[i].name == "mysql" + + server := apps[i].servers[_] + server == sites[j].servers[_].name + + other_server := sites[j].servers[_].name + server != other_server + + other_server == apps[k].servers[_] +} +``` + + + +## Comprehensions + +Comprehensions provide a concise way of building [Composite Values](#composite-values) from sub-queries. + +Like [Rules](#rules), comprehensions consist of a head and a body. The body of a comprehension can be understood in exactly the same way as the body of a rule, that is, one or more expressions that must all be true in order for the overall body to be true. When the body evaluates to true, the head of the comprehension is evaluated to produce an element in the result. + +The body of a comprehension is able to refer to variables defined in the outer body. For example: + +```rego +package comprehensions + +import data.example.apps +import data.example.sites + +region := "west" +names := [name | sites[i].region == region; name := sites[i].name] +``` + + + +In the above query, the second expression contains an [Array Comprehension](#array-comprehensions) that refers to the `region` variable. The region variable will be bound in the outer body. + +> When a comprehension refers to a variable in an outer body, OPA will reorder expressions in the outer body so that variables referred to in the comprehension are bound by the time the comprehension is evaluated. + +Comprehensions are similar to the same constructs found in other languages like Python. For example, we could write the above comprehension in Python as follows: + +```python +# Python equivalent of Rego comprehension shown above. +names = [site.name for site in sites if site.region == "west"] +``` + +Comprehensions are often used to group elements by some key. A common use case for comprehensions is to assist in computing aggregate values (e.g., the number of containers running on a host). + +### Array Comprehensions + +Array Comprehensions build array values out of sub-queries. Array Comprehensions have the form: + +``` +[ | ] +``` + +For example, the following rule defines an object where the keys are application names and the values are hostnames of servers where the application is deployed. The hostnames of servers are represented as an array. + +```rego +package comprehensions + +import data.example.apps +import data.example.sites + +app_to_hostnames[app_name] := hostnames if { + app := apps[_] + app_name := app.name + hostnames := [hostname | name := app.servers[_] + s := sites[_].servers[_] + s.name == name + hostname := s.hostname] +} +``` + + + +### Object Comprehensions + +Object Comprehensions build object values out of sub-queries. Object Comprehensions have the form: + +``` +{ : | } +``` + +We can use Object Comprehensions to write the rule from above as a comprehension instead: + +```rego +package comprehensions + +import data.example.apps +import data.example.sites + +app_to_hostnames := {app.name: hostnames | + app := apps[_] + hostnames := [hostname | + name := app.servers[_] + s := sites[_].servers[_] + s.name == name + hostname := s.hostname] +} +``` + + + +Object comprehensions are not allowed to have conflicting entries, similar to rules: + +```rego +package comprehensions + +conflicting := { "foo": i | + some i in [1, 2] +} +``` + + + +### Set Comprehensions + +Set comprehensions build a set values out of sub-queries. Set comprehensions have +the following form, where terms are selected from the body to be set members: + +``` +{ | } +``` + +For example, to construct a set from an array, we can use `e` where `e` is an +element in the array: + +```rego +package comprehensions + +my_array := [1, 1, 2, 2, 3, 3] +my_set := {e | some e in my_array} +``` + + + +## Rules + +Rules define the content of [Virtual Documents](./philosophy#how-does-opa-work) in +OPA. When OPA evaluates a rule, we say OPA _generates_ the content of the +document that is defined by the rule. + +The sample code in this section make use of the data defined in [Examples](#example-data). + +### Generating Sets + +The following rule defines a set containing the hostnames of all servers in the +example data: + +```rego +package sets + +import data.example.sites + +hostnames contains name if { + name := sites[_].servers[_].hostname +} +``` + + + +When we query for the content of our new `hostnames` rule we see the same data +as we would if we queried using the `sites[_].servers[_].hostname` reference +directly. + +This example introduces a few important aspects of Rego. + +First, the rule defines a set document where the contents are defined by the +variable `name`. We know this rule defines a set document because the head only +includes a key. All rules have the following form (where key, value, and body +are all optional): + +``` + ? ? ? +``` + +:::tip +If the value had been set, this would create an object instead. + +For a more formal definition of the rule syntax, see the [Policy Reference](./policy-reference/#grammar) document. +::: + +Second, the `sites[_].servers[_].hostname` fragment selects the `hostname` +attribute from all the objects in the `servers` collection. From reading the +fragment in isolation we cannot tell whether the fragment refers to arrays or +objects. We only know that it refers to a collections of values. + +Third, the `name := sites[_].servers[_].hostname` expression binds the value of the `hostname` attribute to the variable `name`, which is also declared in the head of the rule. + +### Generating Objects + +Rules that define objects are very similar to rules that define sets. Note that +object rules have a key and a value in the head of the rule. + +```rego +package objects + +import data.example.apps +import data.example.sites + +apps_by_hostname[hostname] := app if { + some i + server := sites[_].servers[_] + hostname := server.hostname + apps[i].servers[_] == server.name + app := apps[i].name +} +``` + + + +The rule above defines an object that maps hostnames to app names. The main difference between this rule and one which defines a set is the rule head: in addition to declaring a key, the rule head also declares a value for the document. + +### Incremental Definitions + +A rule may be defined multiple times with the same name. When a rule is defined +this way, we refer to the rule definition as _incremental_ because each +definition is additive. The document produced by incrementally defined rules is +the union of the documents produced by each individual rule. + +An incrementally defined rule can be intuitively understood as ` OR OR ... OR `. + +For example, we can write a rule that abstracts over our `servers` and +`containers` data as `instances`: + +```rego +package incremental + +import data.example.sites +import data.example.containers + +instances contains instance if { + server := sites[_].servers[_] + instance := {"address": server.hostname, "name": server.name} +} + +instances contains instance if { + some container in containers + instance := {"address": container.ipaddress, "name": container.name} +} +``` + + + +### Complete Definitions + +In addition to rules that _partially_ define sets and objects, Rego also +supports so-called _complete_ definitions of any type of document. Rules provide +a complete definition by omitting the key in the head. Complete definitions are +commonly used for constants: + +```rego +pi := 3.14159 +``` + +:::info +Rego allows authors to omit the body of rules. If the body is omitted, it defaults to true. +::: + +Documents produced by rules with complete definitions can only have one value at +a time. If evaluation produces multiple values for the same document, an error +will be returned. + +For example: + +```rego showLineNumbers=true +package complete + +# Define user "bob" for test input. +user := "bob" + +# Define two sets of users: power users and restricted users. Accidentally +# include "bob" in both. +power_users := {"alice", "bob", "fred"} +restricted_users := {"bob", "kim"} + +# Power users get 32GB memory. +max_memory := 32 if power_users[user] + +# Restricted users get 4GB memory. +max_memory := 4 if restricted_users[user] +``` + + + +OPA returns an error in this case because the rule definitions are in _conflict_. +The value produced by max_memory cannot be 32 and 4 **at the same time**. + +The documents produced by rules with complete definitions may still be undefined: + +```rego +package undefined + +import data.complete.max_memory + +result := m if { + m := max_memory with data.complete.user as "johnson" +} +``` + + + +In some cases, having an undefined result for a document is not desirable. In +those cases, policies can use the [`default` keyword](#default-keyword) to +provide a fallback value. + +### Rule Heads containing References + +As a shorthand for defining nested rule structures, it's valid to use references as rule heads. +This module defines _two complete rules_, `data.example.fruit.apple.seeds` and `data.example.fruit.orange.color`: + +```rego +package rule_refs + +fruit.apple.seeds := 12 + +fruit.orange.color := "orange" +``` + + + +#### Variables in Rule Head References + +Any term, except the very first, in a rule head's reference can be a variable. +These variables can be assigned within the rule, just as for any other partial +rule, to dynamically construct a nested collection of objects. + +```json title="input.json" +{ + "users": [ + { + "id": "alice", + "role": "employee", + "country": "USA" + }, + { + "id": "bob", + "role": "customer", + "country": "USA" + }, + { + "id": "dora", + "role": "admin", + "country": "Sweden" + } + ], + "admins": [ + { + "id": "charlie" + } + ] +} +``` + + + +```rego +package roles + +# A partial object rule that converts a list of users to a mapping by "role" and then "id". +users_by_role[role][id] := user if { + some user in input.users + id := user.id + role := user.role +} + +# Partial rule with an explicit "admin" key override +users_by_role.admin[id] := user if { + some user in input.admins + id := user.id +} + +# Leaf entries can be partial sets +users_by_country[country] contains user.id if { + some user in input.users + country := user.country +} +``` + + + +##### Conflicts + +The first variable declared in a rule head's reference divides the reference in +a leading constant portion and a trailing dynamic portion. Other rules are +allowed to overlap with the dynamic portion (dynamic extent) without causing a +compile-time conflict. + +```rego showLineNumbers=true +package example + +# R1 +p[x].r := y if { + x := "q" + y := 1 +} + +# R2 +p.q.r := 2 +``` + + + +In the above example, rule `R2` overlaps with the dynamic portion of rule `R1`'s +reference (`[x].r`), which is allowed at compile-time, as these rules aren't +guaranteed to produce conflicting output. +However, as `R1` defines `x` as `"q"` and `y` as `1`, a conflict will be +reported at evaluation-time. + +Conflicts are detected at compile-time, where possible, between rules even if +they are within the dynamic extent of another rule. + +```rego showLineNumbers=true +package example + +# R1 +p[x].r := y if { + x := "foo" + y := 1 +} + +# R2 +p.q.r := 2 + +# R3 +p.q.r.s := 3 +``` + + + +Above, `R2` and `R3` are within the dynamic extent of `R1`, but are in conflict +with each other, which is detected at compile-time (note the `rego_type_error`, +rather than `eval_conflict_error` seen above). + +Rules are also not allowed to overlap with object values of other rules: + +```rego showLineNumbers=true +package example + +# R1 +p.q.r := {"s": 1} + +# R2 +p[x].r.t := 2 if { + x := "q" +} +``` + + + +In the above example, `R1` is within the dynamic extent of `R2` and a conflict +cannot be detected at compile-time. However, at evaluation-time `R2` will +attempt to inject a value under key `t` in an object value defined by `R1`. This +is a conflict, as rules are not allowed to modify or replace values defined by +other rules. +We won't get a conflict if we update the policy to the following: + +```rego +package example + +# R1 +p.q.r.s := 1 + +# R2 +p[x].r.t := 2 if { + x := "q" +} +``` + + + +As `R1` is now instead defining a value within the dynamic extent of `R2`'s reference, which is allowed: + +### Functions + +Rego supports user-defined functions that can be called with the same semantics as [built-in functions](#built-in-functions). They have access to both the [the data document](./philosophy/#the-opa-document-model) and [the input document](./philosophy/#the-opa-document-model). + +For example, the following function will return the result of trimming the spaces from a string and then splitting it by periods. + +```rego +package functions + +trim_and_split(s) := x if { + t := trim(s, " ") + x := split(t, ".") +} + +result := trim_and_split(" foo.bar ") +``` + + + +Functions may have an arbitrary number of inputs, but exactly one output. Function arguments may be any kind of term. For example, suppose we have the following function: + +```rego +package functions + +foo([x, {"bar": y}]) := z if { + z := {x: y} +} +``` + +The following calls would produce the logical mappings given: + +| Call | `x` | `y` | +| ----------------------------------------------------- | ------ | --------------------------- | +| `z := foo(a)` | `a[0]` | `a[1].bar` | +| `z := foo(["5", {"bar": "hello"}])` | `"5"` | `"hello"` | +| `z := foo(["5", {"bar": [1, 2, 3, ["foo", "bar"]]}])` | `"5"` | `[1, 2, 3, ["foo", "bar"]]` | + +If you need multiple outputs, write your functions so that the output is an array, object or set +containing your results. If the output term is omitted, it is equivalent to having the output term +be the literal `true`. Furthermore, `if` can be used to write shorter definitions. That is, the +function declarations below are equivalent: + +```rego +package functions + +f(x) if { x == "foo" } +f(x) if x == "foo" + +f(x) := true if { x == "foo" } +f(x) := true if x == "foo" +``` + +The outputs of user functions have some additional limitations, namely that they must resolve to a single value. If you write a function that has multiple possible bindings for an output variable, you will get a conflict error: + +```rego showLineNumbers=true +package functions + +p(x) := y if { + y := x[_] +} + +result := p([1, 2, 3]) +``` + + + +It is possible in Rego to define a function more than once, to achieve a conditional selection of which function to execute: + +Functions can be defined incrementally. + +```rego +package incremental + +q("single", x) := y if { + y := x +} + +q("double", x) := y if { + y := x*2 +} +``` + + + +```rego +package incremental + +result := q("single", 2) +``` + + + +```rego +package incremental + +result := q("double", 2) +``` + + + +A given function call will execute all functions that match the signature given. If a call matches multiple functions, they must produce the same output, or else a conflict error will occur: + +```rego showLineNumbers=true +package incremental + +r(1, x) := y if { + y := x +} + +r(x, 2) := y if { + y := x*4 +} + +result := r(1, 2) +``` + + + +On the other hand, if a call matches no functions, then the result is undefined. + +```rego +package imcremental + +s(x, 2) := y if { + y := x * 4 +} + +result := s(5, 3) +``` + + + +#### Function overloading + +Rego does not support the overloading of functions by the number of +parameters. If two function definitions are given with the same function name +but different numbers of parameters, a compile-time type error is generated. + +```rego showLineNumbers=true +package function_overloading_error + +r(x) := result if { + result := 2*x +} + +r(x, y) := result if { + result := 2*x + 3*y +} +``` + + + +In the unusual case that it is critical to use the same name, the function could +be made to take the list of parameters as a single array. However, this approach +is not generally recommended because it sacrifices some helpful compile-time +checking and can be quite error-prone. + +```rego +package function_overloading_array + +r(params) := result if { + count(params) == 1 + result := 2*params[0] +} + +r(params) := result if { + count(params) == 2 + result := 2*params[0] + 3*params[1] +} + +result := [r([10]), r([10, 1])] +``` + + + +## Negation + +To generate the content of a [Virtual Document](./philosophy#how-does-opa-work), OPA attempts to bind variables in the body of the rule such that all expressions in the rule evaluate to True. + +This generates the correct result when the expressions represent assertions about what states should exist in the data stored in OPA. In some cases, you want to express that certain states _should not_ exist in the data stored in OPA. In these cases, negation must be used. + +For safety, a variable appearing in a negated expression must also appear in another non-negated equality expression in the rule. + +> OPA will reorder expressions to ensure that negated expressions are evaluated after other non-negated expressions with the same variables. OPA will reject rules containing negated expressions that do not meet the safety criteria described above. + +The simplest use of negation involves only scalar values or variables and is equivalent to complementing the operator: + +```rego +package negation + +t if { + greeting := "hello" + not greeting == "goodbye" +} +``` + + + +Negation is required to check whether some value _does not_ exist in a collection: `not p["foo"]`. That is not the same as complementing the `==` operator in an expression `p[_] == "foo"` which yields `p[_] != "foo"` +which means for any item in `p`, return true if the item is not `"foo"`. See more details [here](https://docs.styra.com/regal/rules/bugs/not-equals-in-loop). + +For example, we can write a rule that defines a document containing names of +apps not deployed on the `"prod"` site: + +```rego +package negation + +import data.example.apps +import data.example.sites + +prod_servers contains name if { + some site in sites + site.name == "prod" + some server in site.servers + name := server.name +} + +apps_in_prod contains name if { + some site in sites + some app in apps + name := app.name + some server in app.servers + prod_servers[server] +} + +# Click evaluate to see the result +apps_not_in_prod contains name if { + some app in apps + name := app.name + not apps_in_prod[name] +} +``` + + + +## Universal Quantification (FOR ALL) + +Rego allows for several ways to express universal quantification. + +For example, imagine you want to express a policy that says in natural language: + +``` +There must be no apps named "bitcoin-miner". +``` + +The most expressive way to state this in Rego is using the `every` keyword: + +```rego +no_bitcoin_miners_using_every if { + every app in apps { + app.name != "bitcoin-miner" + } +} +``` + +Variables in Rego are _existentially quantified_ by default: when you write + +```rego +array := ["one", "two", "three"] +array[i] == "three" +``` + +The query will be satisfied **if there is an `i`** such that the query's +expressions are simultaneously satisfied. + +Therefore, there are other ways to express the desired policy. + +For this policy, you can also define a rule that finds if there exists a bitcoin-mining +app (which is easy using the `some` keyword). And then you use negation to check +that there is NO bitcoin-mining app. Technically, you're using 2 negations and +an existential quantifier, which is logically the same as a universal +quantifier. + +For example: + +```rego +package negation + +import data.example.apps + +no_bitcoin_miners_using_negation if not any_bitcoin_miners + +any_bitcoin_miners if { + some app in apps + app.name == "bitcoin-miner" +} +``` + + + +```rego +package negation + +result := true if { + no_bitcoin_miners_using_negation + with data.example.apps as [{"name": "web"}] +} +``` + + + +```rego +package negation + +result := true if { + no_bitcoin_miners_using_negation + with data.example.apps as [{"name": "bitcoin-miner"}, {"name": "web"}] +} +``` + + + +:::info +The `undefined` result above is expected because we did not define a default +value for `no_bitcoin_miners_using_negation`. Since the body of the rule fails +to match, there is no value generated. +::: + +A common mistake is to try encoding the policy with a rule named `no_bitcoin_miners` +like so: + +```rego +no_bitcoin_miners if { + app := apps[_] + app.name != "bitcoin-miner" # THIS IS NOT CORRECT. +} +``` + +It becomes clear that this is incorrect when you use the [`some`](#some-keyword) +keyword, because the rule is true whenever there is SOME app that is not a +bitcoin-miner: + +```rego +no_bitcoin_miners if { + some app in apps + app.name != "bitcoin-miner" # THIS IS NOT CORRECT. +} +``` + +The reason the rule is incorrect is that variables in Rego are _existentially +quantified_. This means that rule bodies and queries express FOR ANY and not FOR +ALL. To express FOR ALL in Rego complement the logic in the rule body (e.g., +`!=` becomes `==`) and then complement the check using negation (e.g., +`no_bitcoin_miners` becomes `not any_bitcoin_miners`). + +Alternatively, we can implement the same kind of logic inside a single rule +using [Comprehensions](#comprehensions). + +```rego +no_bitcoin_miners_using_comprehension if { + bitcoin_miners := {app | some app in apps; app.name == "bitcoin-miner"} + count(bitcoin_miners) == 0 +} +``` + +:::info +Whether you use negation, comprehensions, or `every` to express FOR ALL is up to you. +The `every` keyword should lend itself nicely to a rule formulation that closely +follows how requirements are stated, and thus enhances your policy's readability. + +The comprehension version is more concise than the negation variant, and does not +require a helper rule while the negation version is more verbose but a bit simpler +and allows for more complex ORs. +::: + +## Modules + +In Rego, policies are defined inside _modules_. Modules consist of: + +- Exactly one [Package](#packages) declaration. +- Zero or more [Import](#imports) statements. +- Zero or more [Rule](#rules) definitions. + +Modules are typically represented in Unicode text and encoded in UTF-8. + +### Comments + +Comments begin with the `#` character and continue until the end of the line. + +### Packages + +Packages group the rules defined in one or more modules into a particular namespace. Because rules are namespaced they can be safely shared across projects. + +Modules contributing to the same package do not have to be located in the same directory. + +The rules defined in a module are automatically exported. That is, they can be queried under OPA’s [Data API](./rest-api#data-api) provided the appropriate package is given. For example, given the following module: + +```rego +package opa.examples + +pi := 3.14159 +``` + +The `pi` document can be queried via the Data API: + +```http +GET https://example.com/v1/data/opa/examples/pi HTTP/1.1 +``` + +Valid package names are variables or references that only contain string operands. For example, these are all valid package names: + +```rego +package foo +package foo.bar +package foo.bar.baz +package foo["bar.baz"].qux +``` + +These are invalid package names: + +```rego +package 1foo # not a variable +package foo[1].bar # contains non-string operand +``` + +For more details see the language [Grammar](./policy-reference/#grammar). + +### Imports + +Import statements declare dependencies that modules have on documents defined outside the package. By importing a +document, the identifiers exported by that document can be referenced within the current module. + +All modules contain implicit statements which import the `data` and `input` documents. + +Modules use the same syntax to declare dependencies on [Base and Virtual Documents](./philosophy#how-does-opa-work). + +```rego +package opa.examples + +import data.example.servers + +http_servers contains server if { + some server in servers + "http" in server.protocols +} +``` + +Similarly, modules can declare dependencies on query arguments by specifying an import path that starts with `input`. + +```rego +package examples + +import input.user +import input.method + +# allow alice to perform any operation. +allow if user == "alice" + +# allow bob to perform read-only operations. +allow if { + user == "bob" + method == "GET" +} + +# allows users assigned a "dev" role to perform read-only operations. +allow if { + method == "GET" + input.user in data.roles["dev"] +} + +# allows user catherine access on Saturday and Sunday +allow if { + user == "catherine" + day := time.weekday(time.now_ns()) + day in ["Saturday", "Sunday"] +} +``` + + + +Imports can include an optional `as` keyword to resolve namespacing conflicts: + +```rego +package opa.examples + +import data.example.servers as my_servers + +http_servers contains server if { + some server in my_servers + "http" in server.protocols +} +``` + +## In Keyword + +More expressive membership and existential quantification keyword: + +```rego +deny { + some x in input.roles # iteration + x == "denylisted-role" +} + +deny { + "denylisted-role" in input.roles # membership check +} +``` + +See [the keywords docs](#membership-and-iteration-in) for details. + +## If Keyword + +This keyword allows more expressive rule heads: + +```rego +deny if input.token != "secret" +``` + +## Contains Keyword + +This keyword allows more expressive rule heads for partial set rules: + +```rego +deny contains msg { msg := "forbidden" } +``` + +## Some Keyword + +The `some` keyword allows queries to explicitly declare local variables. Use the +`some` keyword in rules that contain unification statements or references with +variable operands **if** variables contained in those statements are not +declared using `:=` . + +| Statement | Example | Variables | +| -------------------------------- | -------------------------------- | ----------- | +| Unification | `input.a = [["b", x], [y, "c"]]` | `x` and `y` | +| Reference with variable operands | `data.foo[i].bar[j]` | `i` and `j` | + +For example, the following rule generates tuples of array indices for servers in +the "west" region that contain "db" in their name. The first element in the +tuple is the site index and the second element is the server index. + +```rego +package tuples + +import data.example.sites + +tuples contains [i, j] if { + some i, j + sites[i].region == "west" + server := sites[i].servers[j] # note: 'server' is local because it's declared with := + contains(server.name, "db") +} +``` + + + +If we query for the tuples we get two results. +Since we have declared `i`, `j`, and `server` to be local, we can introduce +rules in the same package without affecting the result above: + +```rego +# Define a rule called 'i', has no impact on the tubples rule +i := 1 +``` + +If we had not declared `i` with the `some` keyword, introducing the `i` rule +above would have changed the result of `tuples` because the `i` symbol in the +body would capture the global value. Try removing `some i, j` and see what happens! + +The `some` keyword is not required but it's recommended to avoid situations like +the one above where introduction of a rule inside a package could change +behaviour of other rules. + +For using the `some` keyword with iteration, see +[the documentation of the `in` operator](#membership-and-iteration-in). + +## Every Keyword + +```rego +package example + +import data.example.sites + +names_with_dev if { + some site in sites + site.name == "dev" + + every server in site.servers { + endswith(server.name, "-dev") + } +} +``` + + + +The `every` keyword takes an (optional) key argument, a value argument, a domain, and a +block of further queries, its "body". + +The keyword is used to explicitly assert that its body is true for _any element in the domain_. +It will iterate over the domain, bind its variables, and check that the body holds +for those bindings. +If one of the bindings does not yield a successful evaluation of the body, the overall +statement is undefined. + +If the domain is empty, the overall statement is true. + +Evaluating `every` does **not** introduce new bindings into the rule evaluation. + +Used with a key argument, the index, or property name (for objects), comes into the +scope of the body evaluation: + +```rego +package example + +array_domain if { + every i, x in [1, 2, 3] { x-i == 1 } # array domain +} + +object_domain if { + every k, v in {"foo": "bar", "fox": "baz" } { # object domain + startswith(k, "f") + startswith(v, "b") + } +} + +set_domain if { + every x in {1, 2, 3} { x != 4 } # set domain +} +``` + + + +Negating `every` is forbidden. If you need to express `not every x in xs { p(x) }` +please use `some x in xs; not p(x)` instead. + +## With Keyword + +The `with` keyword allows queries to programmatically specify values nested +under the [input Document](./philosophy/#the-opa-document-model) or the +[data Document](./philosophy/#the-opa-document-model), or built-in functions. + +For example, given the simple authorization policy in the [Imports](#imports) +section, we can write a query that checks whether a particular request would be +allowed: + +```rego +package authz + +import data.examples.allow + +result := true if { + allow with input as {"user": "alice", "method": "POST"} +} +``` + + + +```rego +package authz + +import data.examples.allow + +result := true if { + allow with input as {"user": "bob", "method": "GET"} +} +``` + + + +```rego +package authz + +import data.examples.allow + +result := true if { + not allow with input as {"user": "bob", "method": "DELETE"} +} +``` + + + +It's also possible to use `with` multiple times in the same query. `dev` role +allows `GET`, even for an unknown user in our policy. + +```rego +package authz + +import data.examples.allow + +result := true if { + allow with input as {"user": "charlie", "method": "GET"} + with data.roles as {"dev": ["charlie"]} +} +``` + + + +catherine is only allowed access at weekends. The following query uses `with` to +test this functionality: + +```rego +package authz + +import data.examples.allow + +result := true if { + allow with input as {"user": "catherine", "method": "GET"} + with data.roles as {"dev": ["bob"]} + with time.weekday as "Sunday" +} +``` + + + +The `with` keyword acts as a modifier on expressions. A single expression is +allowed to have zero or more `with` modifiers. The `with` keyword has the +following syntax: + +``` + with as [with as [...]] +``` + +The ``s must be references to values in the input document (or the input +document itself) or data document, or references to functions (built-in or not). + +:::info +When applied to the `data` document, the `` must not attempt to +partially define virtual documents. For example, given a virtual document at +path `data.foo.bar`, the compiler will generate an error if the policy +attempts to replace `data.foo.bar.baz`. +::: + +The `with` keyword only affects the attached expression. Subsequent expressions +will see the unmodified value. The exception to this rule is when multiple +`with` keywords are in-scope like below: + +```rego +inner := [x, y] if { + x := input.foo + y := input.bar +} + +middle := [a, b] if { + a := inner with input.foo as 100 + b := input +} + +outer := result if { + result := middle with input as {"foo": 200, "bar": 300} +} +``` + +When `` is a reference to a function, like `http.send`, then +its `` can be any of the following: + +1. a value: `with http.send as {"body": {"success": true }}` +2. a reference to another function: `with http.send as mock_http_send` +3. a reference to another (possibly custom) built-in function: `with custom_builtin as less_strict_custom_builtin` +4. a reference to a rule that will be used as the _value_. + +When the replacement value is a function, its arity needs to match the replaced +function's arity; and the types must be compatible. + +Replacement functions can call the function they're replacing **without causing +recursion**. +See the following example: + +```rego +package mock + +f(x) := count(x) + +mock_count(x) := 0 if "x" in x +mock_count(x) := count(x) if not "x" in x + +result := v if { + v := f(["x", 2, 3]) with count as mock_count +} +``` + + + +Each replacement function evaluation will start a new scope: it's valid to use +`with as ...` in the body of the replacement function -- for example: + +```rego +package mocks + +f(x) := count(x) if { + rule_using_concat with concat as "foo,bar" +} +``` + +Note that function replacement via `with` does not affect the evaluation of the +function arguments: if running `f(input.x), and`input.x`is undefined, the replacement of`concat` does not change the result of the evaluation. + +## Default Keyword + +The `default` keyword allows policies to define a default value for documents +produced by rules with [Complete Definitions](#complete-definitions). The +default value is used when all the rules sharing the same name are undefined. + +For example: + +```rego +package example + +default allow := false + +allow if { + input.user == "bob" + input.method == "GET" +} +``` + + + +But if we run this with the following input: + +```json +{ + "user": "bob", + "method": "GET" +} +``` + + + +```rego +package example + +default allow := false + +allow if { + input.user == "bob" + input.method == "GET" +} +``` + + + +Without the default definition, the `allow` document would simply be undefined for the same input. + +When the `default` keyword is used, the rule syntax is restricted to: + +```rego +default := +``` + +The term may be any scalar, composite, or comprehension value but it may not be +a variable or reference. If the value is a composite then it may not contain +variables or references. Comprehensions however may, as the result of a +comprehension is never undefined. + +Similar to rules, the `default` keyword can be applied to functions as well. For +example: + +```rego +default clamp_positive(_) := 0 + +clamp_positive(x) := x if { + x > 0 +} +``` + +When `clamp_positive` is queried, the return value will be either the argument provided to the function or `0`. + +The value of a `default` function follows the same conditions as that of a `default` rule. In addition, a `default` +function satisfies the following properties: + +- same arity as other functions with the same name +- arguments should only be plain variables ie. no composite values +- argument names should not be repeated + +:::info +A `default` function will still fail (as in not evaluate, even to the default value) if any of the arguments provided in +the call are **undefined**. The reason for this is that the arguments are evaluated before the function is even called, +and an undefined argument halts evaluation at that point. +::: + +## Else Keyword + +The `else` keyword is a basic control flow construct that gives you control +over rule evaluation order. + +Rules grouped together with the `else` keyword are evaluated until a match is +found. Once a match is found, rule evaluation does not proceed to rules further +in the chain. + +The `else` keyword is useful if you are porting policies into Rego from an +order-sensitive system like IPTables. + +```rego +package else_example + +authorize := "allow" if { + input.user == "superuser" # allow 'superuser' to perform any operation. +} else := "deny" if { + input.path[0] == "admin" # disallow 'admin' operations... + input.source_network == "external" # from external networks. +} # ... more rules +``` + + + +In the example below, evaluation stops immediately after the first rule even +though the input matches the second rule as well. + +```json +{ + "path": [ + "admin", + "exec_shell" + ], + "source_network": "external", + "user": "superuser" +} +``` + + + +```rego +package else_example + +superuser_result := authorize +``` + + + +In the next example, the input matches the second rule (but not the first) so +evaluation continues to the second rule before stopping. + +```json +{ + "path": [ + "admin", + "exec_shell" + ], + "source_network": "external", + "user": "alice" +} +``` + + + +```rego +package else_example + +alice_result := authorize +``` + + + +The `else` keyword may be used repeatedly on the same rule and there is no +limit imposed on the number of `else` clauses on a rule. However, it is +recommended that policy authors use the `else` keyword sparingly to avoid +tightly coupled rules. + +## Operators + +### Membership and iteration: `in` + +The membership operator `in` lets you check if an element is part of a collection (array, set, or object). It always evaluates to `true` or `false`: + +```rego +package example + +result := { + "array": 3 in [1, 2, 3], + "set": 3 in {1, 2, 3}, + "object": 3 in {"foo": 1, "bar": 3}, + "object_key": "foo" in {"foo": 1, "bar": 3}, # false, see below +} +``` + + + +When providing two arguments on the left-hand side of the `in` operator, +and an object or an array on the right-hand side, the first argument is +taken to be the key (object) or index (array), respectively: + +```rego +package example + +result.object := "foo", "bar" in {"foo": "bar"} # key, val with object +result.array := 2, "baz" in ["foo", "bar", "baz"] # key, val with array +``` + + + +**Note** that in list contexts, like set or array definitions and function +arguments, parentheses are required to use the form with two left-hand side +arguments -- compare: + +```rego +package list_in + +p := x if { + x := [ 0, 2 in [2] ] +} +q := x if { + x := [ (0, 2 in [2]) ] +} +w := x if { + x := g((0, 2 in [2])) +} +z := x if { + x := f(0, 2 in [2]) +} + +f(x, y) := sprintf("two function arguments: %v, %v", [x, y]) +g(x) := sprintf("one function argument: %v", [x]) +``` + + + +Combined with `not`, the operator can be handy when asserting that an element is _not_ +member of an array: + +```rego +package not_in + +deny if not "admin" in input.user.roles + +# Click evaluate to see the result +test_deny if { + deny with input.user.roles as ["operator", "user"] +} +``` + + + +**Note** that expressions using the `in` operator _always return `true` or `false`_, even +when called in non-collection arguments: + +```rego +package boolean_in + +q := x if { + x := 3 in "three" +} +``` + + + +Using the `some` variant, it can be used to introduce new variables based on a collections' items: + +```rego +package some_in + +p contains x if { + some x in ["a", "r", "r", "a", "y"] +} + +q contains x if { + some x in {"s", "e", "t"} +} + +r contains x if { + some x in {"foo": "bar", "baz": "quz"} +} +``` + + + +Furthermore, passing a second argument allows you to work with _object keys_ and _array indices_: + +```rego +package some_in + +p contains x if { + some x, "r" in ["a", "r", "r", "a", "y"] # key variable, value constant +} + +q[x] := y if { + some x, y in ["a", "r", "r", "a", "y"] # both variables +} + +r[y] := x if { + some x, y in {"foo": "bar", "baz": "quz"} +} +``` + + + +Any argument to the `some` variant can be a composite, non-ground value: + +```rego +package some_in + +p[x] = y if { + some x, {"foo": y} in [{"foo": 100}, {"bar": 200}] +} + +p[x] = y if { + some {"bar": x}, {"foo": y} in {{"bar": "b"}: {"foo": "f"}} +} +``` + + + +### Equality: Assignment, Comparison, and Unification + +Rego supports three kinds of equality: assignment (`:=`), comparison (`==`), and unification `=`. We recommend using assignment (`:=`) and comparison (`==`) whenever possible for policies that are easier to read and write. + +#### Assignment `:=` + +The assignment operator (`:=`) is used to assign values to variables. Variables assigned inside a rule are locally scoped to that rule and shadow global variables. + +```rego +package assignment + +x := 100 + +p if { + x := 1 # declare local variable 'x' and assign value 1 + x != 100 # true because 'x' refers to local variable +} +``` + + + +Assigned variables are not allowed to appear before the assignment in the +query. For example, the following policy will not compile: + +```rego showLineNumbers=true +package assignment + +p if { + x != 100 + x := 1 # error because x appears earlier in the query. +} + +q if { + x := 1 + x := 2 # error because x is assigned twice. +} +``` + + + +A simple form of destructuring can be used to unpack values from arrays and assign them to variables: + +```rego +package assignment + +address := ["3 Abbey Road", "NW8 9AY", "London", "England"] + +in_london if { + [_, _, city, country] := address + city == "London" + country == "England" +} +``` + + + +#### Comparison `==` + +Comparison checks if two values are equal within a rule. If the left or right hand side contains a variable that has not been assigned a value, the compiler throws an error. + +```rego +package comparison + +p if { + x := 100 + x == 100 # true because x refers to the local variable +} + +y := 100 + +q if { + y == 100 # true because y refers to the global variable +} +``` + + + +Values used in comparison must be assigned before the comparison is made. For +example, the following policy will not compile: + +```rego showLineNumbers=true +package comparison + +p if { + z == 100 # error because z is not assigned +} +``` + + + +#### Unification `=` + +Unification (`=`) combines assignment and comparison. Rego will assign variables to values that make the comparison true. Unification lets you ask for values for variables that make an expression true. + +```rego +package unification + +# Find values for x and y that make the equality true +result := [x, y] if { + [x, "world"] = ["hello", y] +} +``` + + + +```rego +package unification + +import data.example.sites +import data.example.apps + +# find all the servers running apps +result contains sites[i].servers[j].name if { + sites[i].servers[j].name = apps[k].servers[m] +} +``` + + + +As opposed to when assignment (`:=`) is used, the order of expressions in a rule does not affect the document’s content. + +```rego +package unification + +s if { + x > y + y = 41 + x = 42 +} +``` + + + +#### Best Practices for Equality + +Here is a comparison of the three forms of equality. + +``` +Equality Applicable Compiler Errors Use Case +-------- ----------- ------------------------- ---------------------- +:= Everywhere Var already assigned Assign variable +== Everywhere Var not assigned Compare values += Everywhere Values cannot be computed Express query +``` + +Best practice is to use assignment `:=` and comparison `==` wherever possible. The additional compiler checks help avoid errors when writing policy, and the additional syntax helps make the intent clearer when reading policy. + +Under the hood `:=` and `==` are syntactic sugar for `=`, local variable creation, and additional compiler checks. + +### Comparison Operators + +The following comparison operators are supported: + +```rego +a == b # `a` is equal to `b`. +a != b # `a` is not equal to `b`. +a < b # `a` is less than `b`. +a <= b # `a` is less than or equal to `b`. +a > b # `a` is greater than `b`. +a >= b # `a` is greater than or equal to `b`. +``` + +None of these operators bind variables contained +in the expression. As a result, if either operand is a variable, the variable +must appear in another expression in the same rule that would cause the +variable to be bound, i.e., an equality expression or the target position of +a built-in function. + +## Built-in Functions + +In some cases, rules must perform simple arithmetic, aggregation, and so on. +Rego provides a number of built-in functions (or “built-ins”) for performing +these tasks. + +Built-ins can be easily recognized by their syntax. All built-ins have the +following form: + +``` +(, , ..., ) +``` + +Built-ins usually take one or more input values and produce one output +value. Unless stated otherwise, all built-ins accept values or variables as +output arguments. + +If a built-in function is invoked with a variable as input, the variable must +be _safe_, i.e., it must be assigned elsewhere in the query. + +Built-ins can include "." characters in the name. This allows them to be +namespaced. If you are adding custom built-ins to OPA, consider namespacing +them to avoid naming conflicts, e.g., `org.example.special_func`. + +See the [Policy Reference](./policy-reference#built-in-functions) document for +details on each built-in function. + +### Errors + +By default, built-in function calls that encounter runtime errors evaluate to +undefined (which can usually be treated as `false`) and do not halt policy +evaluation. This ensures that built-in functions can be called with invalid +inputs without causing the entire policy to stop evaluating. + +In most cases, policies do not have to implement any kind of error handling +logic. If error handling is required, the built-in function call can be negated +to test for undefined. For example: + +```json title="input.json" +{ + "token": "a poorly formatted token" +} +``` + + + +```rego +package errors + +allow if { + io.jwt.verify_hs256(input.token, "secret") + [_, payload, _] := io.jwt.decode(input.token) + payload.role == "admin" +} + +reason contains "invalid JWT supplied as input" if { + not io.jwt.decode(input.token) +} +``` + + + +If you wish to disable this behaviour and instead have built-in function call +errors treated as exceptions that halt policy evaluation enable "strict built-in +errors" in the caller: + +| API | Flag | +| --------------------- | --------------------------------------- | +| `POST v1/data` (HTTP) | `strict-builtin-errors` query parameter | +| `GET v1/data` (HTTP) | `strict-builtin-errors` query parameter | +| `opa eval` (CLI) | `--strict-builtin-errors` | +| `opa run` (REPL) | `> strict-builtin-errors` | +| `rego` Go module | `rego.StrictBuiltinErrors(true)` option | +| Wasm | Not Available | + +## Metadata + +The package and individual rules in a module can be annotated with a rich set of metadata. + +```rego +package metadata + +# METADATA +# title: My rule +# description: A rule that determines if x is allowed. +# authors: +# - John Doe +# entrypoint: true +allow if { + ... +} +``` + +Annotations are grouped within a _metadata block_, and must be specified as YAML within a comment block that **must** start with `# METADATA`. +Also, every line in the comment block containing the annotation **must** start at Column 1 in the module/file, or otherwise, they will be ignored. + +:::danger +OPA will attempt to parse the YAML document in comments following the +initial `# METADATA` comment. If the YAML document cannot be parsed, OPA will +return an error. If you need to include additional comments between the +comment block and the next statement, include a blank line immediately after +the comment block containing the YAML document. This tells OPA that the +comment block containing the YAML document is finished +::: + +### Annotations + +| Name | Type | Description | +| ----------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| scope | string; one of `package`, `rule`, `document`, `subpackages` | The scope for which the metadata applies. Read more [here](./#scope). | +| title | string | A human-readable name for the annotation target. Read more [here](#title). | +| description | string | A description of the annotation target. Read more [here](#description). | +| related_resources | list of URLs | A list of URLs pointing to related resources/documentation. Read more [here](#related-resources). | +| authors | list of strings | A list of authors for the annotation target. Read more [here](#authors). | +| organizations | list of strings | A list of organizations related to the annotation target. Read more [here](#organizations). | +| schemas | list of object | A list of associations between value paths and schema definitions. Read more [here](#schemas). | +| entrypoint | boolean | Whether or not the annotation target is to be used as a policy entrypoint. Read more [here](#entrypoint). | +| custom | mapping of arbitrary data | A custom mapping of named parameters holding arbitrary data. Read more [here](#custom). | + +### Scope + +Annotations can be defined at the rule or package level. The `scope` annotation in +a metadata block determines how that metadata block will be applied. If the +`scope` field is omitted, it defaults to the scope for the statement that +immediately follows the annotation. The `scope` values that are currently +supported are: + +- `rule` - applies to the individual rule statement (within the same file). Default, when metadata block precedes rule. +- `document` - applies to all of the rules with the same name in the same package (across multiple files) +- `package` - applies to all of the rules in the package (across multiple files). Default, when metadata block precedes package. +- `subpackages` - applies to all of the rules in the package and all subpackages (recursively, across multiple files) + +Since the `document` scope annotation applies to all rules with the same name in the same package +and the `package` and `subpackages` scope annotations apply to all packages with a matching path, metadata blocks with +these scopes are applied over all files with applicable package- and rule paths. +As there is no ordering across files in the same package, the `document`, `package`, and `subpackages` scope annotations +can only be specified **once** per path. The `document` scope annotation can be applied to any rule in the set (i.e., +ordering does not matter.) + +An `entrypoint` annotation implies a `scope` of either `package` or `document`. When `entrypoint` is set to `true` on a +rule, the `scope` is automatically set to `document` if not explicitly provided. Setting the `scope` to `rule` will +result in an error, as an entrypoint always applies to the whole document. + +#### Example Policy with Metadata + +```rego +# METADATA +# scope: document +# description: A set of rules that determines if x is allowed. +package metadata + +# METADATA +# title: Allow Ones +allow if { + x == 1 +} + +# METADATA +# title: Allow Twos +allow if { + x == 2 +} + +# METADATA +# entrypoint: true +# description: | +# `scope` annotation automatically set to `document` +# as that is required for entrypoints +message := "welcome!" if allow +``` + +### Metadata `title` + +The `title` annotation is a string value giving a human-readable name to the annotation target. + +```rego +# METADATA +# title: Allow Ones +allow if { + x == 1 +} + +# METADATA +# title: Allow Twos +allow if { + x == 2 +} +``` + +### Metadata `description` + +The `description` annotation is a string value describing the annotation target, such as its purpose. + +```rego +# METADATA +# description: | +# The 'allow' rule... +# Is about allowing things. +# Not denying them. +allow if { + ... +} +``` + +### Metadata `related_resources` + +The `related_resources` annotation is a list of _related-resource_ entries, where each links to some related external resource; such as RFCs and other reading material. +A _related-resource_ entry can either be an object or a short-form string holding a single URL. + +#### Object Related-resource Format + +When a _related-resource_ entry is presented as an object, it has two fields: + +- `ref`: a URL pointing to the resource (required). +- `description`: a text describing the resource. + +#### String Related-resource Format + +When a _related-resource_ entry is presented as a string, it needs to be a valid URL. + +#### Examples + +```rego +# METADATA +# related_resources: +# - ref: https://example.com +# ... +# - ref: https://example.com/foo +# description: A text describing this resource +allow if { + ... +} +``` + +```rego +# METADATA +# related_resources: +# - https://example.com/foo +# ... +# - https://example.com/bar +allow if { + ... +} +``` + +### Metadata `authors` + +The `authors` annotation is a list of author entries, where each entry denotes an _author_. +An _author_ entry can either be an object or a short-form string. + +#### Object Author Format + +When an _author_ entry is presented as an object, it has two fields: + +- `name`: the name of the author +- `email`: the email of the author + +At least one of the above fields are required for a valid `author` entry. + +#### String Author Format + +When an _author_ entry is presented as a string, it has the format `{ name } [ "<" email ">"]`; +where the name of the author is a sequence of whitespace-separated words. +Optionally, the last word may represent an email, if enclosed with `<>`. + +#### Examples + +```rego +# METADATA +# authors: +# - name: John Doe +# ... +# - name: Jane Doe +# email: jane@example.com +allow if { + ... +} +``` + +```rego +# METADATA +# authors: +# - John Doe +# ... +# - Jane Doe +allow if { + ... +} +``` + +### Metadata `organizations` + +The `organizations` annotation is a list of string values representing the organizations associated with the annotation target. + +#### Example + +```rego +# METADATA +# organizations: +# - Acme Corp. +# ... +# - Tyrell Corp. +allow if { + ... +} +``` + +### Metadata `schemas` + +The `schemas` annotation is a list of key value pairs, associating schemas to data values. +In-depth information on this topic can be found [here](#annotations). + +#### Schema Reference Format + +Schema files can be referenced by path, where each path starts with the `schema` namespace, and trailing components specify +the path of the schema file (sans file-ending) relative to the root directory specified by the `--schema` flag on applicable commands. +If the `--schema` flag is not present, referenced schemas are ignored during type checking. + +```rego +# METADATA +# schemas: +# - input: schema.input +# - data.acl: schema["acl-schema"] +allow if { + access := data.acl["alice"] + access[_] == input.operation +} +``` + +#### Inlined Schema Format + +Schema definitions can be inlined by specifying the schema structure as a YAML or JSON map. +Inlined schemas are always used to inform type checking for the `eval`, `check`, and `test` commands; +in contrast to [by-reference schema annotations](#schema-reference-format), which require the `--schema` flag to be present in order to be evaluated. + +```rego +# METADATA +# schemas: +# - input.x: {type: number} +allow if { + input.x == 42 +} +``` + +### Metadata `entrypoint` + +The `entrypoint` annotation is a boolean used to mark rules and packages that should be used as entrypoints for a policy. +This value is false by default, and can only be used at `document` or `package` scope. When used on a rule with no +explicit `scope` set, the presence of an `entrypoint` annotation will automatically set the scope to `document`. + +The `build` and `eval` CLI commands will automatically pick up annotated entrypoints; you do not have to specify them with +[`--entrypoint`](./cli/#options-1). + +:::info +Unless the `--prune-unused` flag is used, any rule transitively referring to a +package or rule declared as an entrypoint will also be enumerated as an entrypoint. +::: + +### Metadata `custom` + +The `custom` annotation is a mapping of user-defined data, mapping string keys to arbitrarily typed values. + +#### Example + +```rego +# METADATA +# custom: +# my_int: 42 +# my_string: Some text +# my_bool: true +# my_list: +# - a +# - b +# my_map: +# a: 1 +# b: 2 +allow if { + ... +} +``` + +### Accessing annotations + +Information in metadata blocks can be accessed in a number of ways. + +#### From Rego Rules + +In the example below, you can see how to access an annotation from within a policy. + +```json title="input.json" +{ + "number": 11 +} +``` + + + +The following policy uses the `rego.metadata.rule()` function to access the metadata +from the rule to show in the output message. + +```rego +package example + +# METADATA +# title: Deny invalid numbers +# description: Numbers may not be higher than 5 +# custom: +# severity: MEDIUM +output := decision if { + input.number > 5 + + annotation := rego.metadata.rule() + decision := { + "severity": annotation.custom.severity, + "message": annotation.description, + } +} +``` + + + +If you'd like more examples and information on this, you can see more here under the [Rego](./policy-reference/#rego) policy reference. + +#### From the `inspect` command + +Annotations can be listed through the `inspect` command by using the `-a` flag: + +```shell +opa inspect -a +``` + +#### From the Go API + +The `ast.AnnotationSet` is a collection of all `ast.Annotations` declared in a set of modules. +An `ast.AnnotationSet` can be created from a slice of compiled modules: + +```go +var modules []*ast.Module +... +as, err := ast.BuildAnnotationSet(modules) +if err != nil { + // Handle error. +} +``` + +or can be retrieved from an `ast.Compiler` instance: + +```go +var modules []*ast.Module +... +compiler := ast.NewCompiler() +compiler.Compile(modules) +as := compiler.GetAnnotationSet() +``` + +The `ast.AnnotationSet` can be flattened into a slice of `ast.AnnotationsRef`, which is a complete, sorted list of all +annotations, grouped by the path and location of their targeted package or -rule. + +```go +flattened := as.Flatten() +for _, entry := range flattened { + fmt.Printf("%v at %v has annotations %v\n", + entry.Path, + entry.Location, + entry.Annotations) +} + +// Output: +// data.foo at foo.rego:5 has annotations {"scope":"subpackages","organizations":["Acme Corp."]} +// data.foo.bar at mod:3 has annotations {"scope":"package","description":"A couple of useful rules"} +// data.foo.bar.p at mod:7 has annotations {"scope":"rule","title":"My Rule P"} +// +// For modules: +// # METADATA +// # scope: subpackages +// # organizations: +// # - Acme Corp. +// package foo +// --- +// # METADATA +// # description: A couple of useful rules +// package foo.bar +// +// # METADATA +// # title: My Rule P +// p := 7 +``` + +Given an `ast.Rule`, the `ast.AnnotationSet` can return the chain of annotations declared for that rule, and its path ancestry. +The returned slice is ordered starting with the annotations for the rule, going outward to the farthest node with declared annotations +in the rule's path ancestry. + +```go +var rule *ast.Rule +... +chain := ast.Chain(rule) +for _, link := range chain { + fmt.Printf("link at %v has annotations %v\n", + link.Path, + link.Annotations) +} + +// Output: +// data.foo.bar.p at mod:7 has annotations {"scope":"rule","title":"My Rule P"} +// data.foo.bar at mod:3 has annotations {"scope":"package","description":"A couple of useful rules"} +// data.foo at foo.rego:5 has annotations {"scope":"subpackages","organizations":["Acme Corp."]} +// +// For modules: +// # METADATA +// # scope: subpackages +// # organizations: +// # - Acme Corp. +// package foo +// --- +// # METADATA +// # description: A couple of useful rules +// package foo.bar +// +// # METADATA +// # title: My Rule P +// p := 7 +``` + +## Schema + +### Using schemas to enhance the Rego type checker + +You can provide one or more input schema files and/or data schema files to `opa eval` to improve static type checking and get more precise error reports as you develop Rego code. + +Schemas can be provided to OPA in two main ways: by supplying external JSON Schema files using the `-s` command-line flag (explained below), or by embedding schema definitions directly within your Rego files using [schema annotations](#schema-annotations) (detailed further down in this document). Both methods help improve static type checking. + +The `-s` flag can be used to upload schemas for input and data documents in JSON Schema format. You can either load a single JSON schema file for the input document or directory of schema files. + +``` +-s, --schema string set schema file path or directory path +``` + +#### Passing a single file with -s + +When a single file is passed, it is a schema file associated with the input document globally. This means that for all rules in all packages, the `input` has a type derived from that schema. There is no constraint on the name of the file, it could be anything. + +Example: + +``` +opa eval data.envoy.authz.allow -i opa-schema-examples/envoy/input.json -d opa-schema-examples/envoy/policy.rego -s opa-schema-examples/envoy/schemas/my-schema.json +``` + +#### Passing a directory with -s + +When a directory path is passed, annotations will be used in the code to indicate what expressions map to what schemas (see below). +Both input schema files and data schema files can be provided in the same directory, with different names. The directory of schemas may have any sub-directories. Notice that when a directory is passed the input document does not have a schema associated with it globally. This must also +be indicated via an annotation. + +Example: + +``` +opa eval data.kubernetes.admission -i opa-schema-examples/kubernetes/input.json -d opa-schema-examples/kubernetes/policy.rego -s opa-schema-examples/kubernetes/schemas +``` + +Schemas can also be provided for policy and data files loaded via `opa eval --bundle` + +Example: + +``` +opa eval data.kubernetes.admission -i opa-schema-examples/kubernetes/input.json -b opa-schema-examples/bundle.tar.gz -s opa-schema-examples/kubernetes/schemas +``` + +Samples provided at: [`github.com/aavarghese/opa-schema-examples`](https://github.com/aavarghese/opa-schema-examples/). + +### Usage scenario with a single schema file + +Consider the following Rego code, which assumes as input a Kubernetes admission review. For resources that are Pods, it checks that the image name +starts with a specific prefix. + +```rego title="pod.rego" +package kubernetes.admission + +deny contains msg if { + input.request.kind.kinds == "Pod" + image := input.request.object.spec.containers[_].image + not startswith(image, "hooli.com/") + msg := sprintf("image '%v' comes from untrusted registry", [image]) +} +``` + +Notice that this code has a typo in it: `input.request.kind.kinds` is undefined and should have been `input.request.kind.kind`. + +Consider the following input document: + +```json title="input.json" +{ + "kind": "AdmissionReview", + "request": { + "kind": { + "kind": "Pod", + "version": "v1" + }, + "object": { + "metadata": { + "name": "myapp" + }, + "spec": { + "containers": [ + { + "image": "nginx", + "name": "nginx-frontend" + }, + { + "image": "mysql", + "name": "mysql-backend" + } + ] + } + } + } +} +``` + +Clearly there are 2 image names that are in violation of the policy. However, when we evaluate the erroneous Rego code against this input we obtain: + +```shell +$ opa eval data.kubernetes.admission --format pretty -i opa-schema-examples/kubernetes/input.json -d opa-schema-examples/kubernetes/policy.rego +[] +``` + +The empty value returned is indistinguishable from a situation where the input did not violate the policy. This error is therefore causing the policy not to catch violating inputs appropriately. + +If we fix the Rego code and change `input.request.kind.kinds` to `input.request.kind.kind`, then we obtain the expected result: + +```json +[ + "image 'nginx' comes from untrusted registry", + "image 'mysql' comes from untrusted registry" +] +``` + +With this feature, it is possible to pass a schema to `opa eval`, written in JSON Schema. Consider the admission review schema provided at +[`schemas/input.json`](https://github.com/aavarghese/opa-schema-examples/blob/main/kubernetes/schemas/input.json). + +We can pass this schema to the evaluator as follows: + +``` +% opa eval data.kubernetes.admission --format pretty -i opa-schema-examples/kubernetes/input.json -d opa-schema-examples/kubernetes/policy.rego -s opa-schema-examples/kubernetes/schemas/input.json +``` + +With the erroneous Rego code, we now obtain the following type error: + +```shell +1 error occurred: ../../aavarghese/opa-schema-examples/kubernetes/policy.rego:5: rego_type_error: undefined ref: input.request.kind.kinds +input.request.kind.kinds + ^ + have: "kinds" + want (one of): ["kind" "version"] +``` + +This indicates the error to the Rego developer right away, without having the need to observe the results of runs on actual data, thereby improving productivity. + +### Schema annotations + +When passing a directory of schemas to `opa eval`, schema annotations become handy to associate a Rego expression with a corresponding schema within a given scope: + +```rego +# METADATA +# schemas: +# - : +# ... +# - : +allow if { + ... +} +``` + +See the [annotations documentation](./policy-language/#annotations) for general information relating to annotations. + +The `schemas` field specifies an array associating schemas to data values. Paths must start with `input` or `data` (i.e., they must be fully-qualified.) + +The type checker derives a Rego Object type for the schema and an appropriate entry is added to the type environment before type checking the rule. This entry is removed upon exit from the rule. + +Example: + +Consider the following Rego code which checks if an operation is allowed by a user, given an ACL data document: + +```rego +package policy + +import data.acl + +default allow := false + +# METADATA +# schemas: +# - input: schema.input +# - data.acl: schema["acl-schema"] +allow if { + access := data.acl.alice + access[_] == input.operation +} + +allow if { + access := data.acl.bob + access[_] == input.operation +} +``` + +Consider a directory named `mySchemasDir` with the following structure, provided via `opa eval --schema opa-schema-examples/mySchemasDir` + +```shell +$ tree mySchemasDir/ +mySchemasDir/ +├── input.json +└── acl-schema.json +``` + +See here for [code samples](https://github.com/aavarghese/opa-schema-examples/tree/main/acl). + +In the first `allow` rule above, the input document has the schema `input.json`, and `data.acl` has the schema `acl-schema.json`. Note that we use the relative path inside the `mySchemasDir` directory to identify a schema, omit the `.json` suffix, and use the global variable `schema` to stand for the top-level of the directory. +Schemas in annotations are proper Rego references. So `schema.input` is also valid, but `schema.acl-schema` is not. + +If we had the expression `data.acl.foo` in this rule, it would result in a type error because the schema contained in `acl-schema.json` only defines object properties `"alice"` and `"bob"` in the ACL data document. + +On the other hand, this annotation does not constrain other paths under `data`. What it says is that we know the type of `data.acl` statically, but not that of other paths. So for example, `data.foo` is not a type error and gets assigned the type `Any`. + +Note that the second `allow` rule doesn't have a METADATA comment block attached to it, and hence will not be type checked with any schemas. + +On a different note, schema annotations can also be added to policy files part of a bundle package loaded via `opa eval --bundle` along with the `--schema` parameter for type checking a set of `*.rego` policy files. + +The _scope_ of the `schema` annotation can be controlled through the [scope](./policy-language/#annotations) annotation + +In case of overlap, schema annotations override each other as follows: + +- `rule` overrides `document` +- `document` overrides `package` +- `package` overrides `subpackages` + +The following sections explain how the different scopes affect `schema` annotation +overriding for type checking. + +#### Rule and Document Scopes + +In the example above, the second rule does not include an annotation so type +checking of the second rule would not take schemas into account. To enable type +checking on the second (or other rules in the same file) we could specify the +annotation multiple times: + +```rego +# METADATA +# scope: rule +# schemas: +# - input: schema.input +# - data.acl: schema["acl-schema"] +allow if { + access := data.acl["alice"] + access[_] == input.operation +} + +# METADATA +# scope: rule +# schemas: +# - input: schema.input +# - data.acl: schema["acl-schema"] +allow if { + access := data.acl["bob"] + access[_] == input.operation +} +``` + +This is obviously redundant and error-prone. To avoid this problem, we can +define the annotation once on a rule with scope `document`: + +```rego +# METADATA +# scope: document +# schemas: +# - input: schema.input +# - data.acl: schema["acl-schema"] +allow if { + access := data.acl["alice"] + access[_] == input.operation +} + +allow if { + access := data.acl["bob"] + access[_] == input.operation +} +``` + +In this example, the annotation with `document` scope has the same affect as the +two `rule` scoped annotations in the previous example. + +#### Package and Subpackage Scopes + +Annotations can be defined at the `package` level and then applied to all rules +within the package: + +```rego +# METADATA +# scope: package +# schemas: +# - input: schema.input +# - data.acl: schema["acl-schema"] +package example + +allow if { + access := data.acl["alice"] + access[_] == input.operation +} + +allow if { + access := data.acl["bob"] + access[_] == input.operation +} +``` + +`package` scoped schema annotations are useful when all rules in the same +package operate on the same input structure. In some cases, when policies are +organized into many sub-packages, it is useful to declare schemas recursively +for them using the `subpackages` scope. For example: + +```rego +# METADTA +# scope: subpackages +# schemas: +# - input: schema.input +package kubernetes.admission +``` + +This snippet would declare the top-level schema for `input` for the +`kubernetes.admission` package as well as all subpackages. If admission control +rules were defined inside packages like `kubernetes.admission.workloads.pods`, +they would be able to pick up that one schema declaration. + +### Overriding + +JSON Schemas are often incomplete specifications of the format of data. For example, a Kubernetes Admission Review resource has a field `object` which can contain any other Kubernetes resource. A schema for Admission Review has a generic type `object` for that field that has no further specification. To allow more precise type checking in such cases, we support overriding existing schemas. + +Consider the following example: + +```rego +package kubernetes.admission + +# METADATA +# scope: rule +# schemas: +# - input: schema.input +# - input.request.object: schema.kubernetes.pod +deny contains msg if { + input.request.kind.kind == "Pod" + image := input.request.object.spec.containers[_].image + not startswith(image, "hooli.com/") + msg := sprintf("image '%v' comes from untrusted registry", [image]) +} +``` + +In this example, the `input` is associated with an Admission Review schema, and furthermore `input.request.object` is set to have the schema of a Kubernetes Pod. In effect, the second schema annotation overrides the first one. Overriding is a schema transformation feature and combines existing schemas. In this case, we are combining the Admission Review schema with that of a Pod. + +Notice that the order of schema annotations matter for overriding to work correctly. + +Given a schema annotation, if a prefix of the path already has a type in the environment, then the annotation has the effect of merging and overriding the existing type with the type derived from the schema. In the example above, the prefix `input` already has a type in the type environment, so the second annotation overrides this existing type. Overriding affects the type of the longest prefix that already has a type. If no such prefix exists, the new path and type are added to the type environment for the scope of the rule. + +In general, consider the existing Rego type: + +``` +object{a: object{b: object{c: C, d: D, e: E}}} +``` + +If we override this type with the following type (derived from a schema annotation of the form `a.b.e: schema-for-E1`): + +``` +object{a: object{b: object{e: E1}}} +``` + +It results in the following type: + +``` +object{a: object{b: object{c: C, d: D, e: E1}}} +``` + +Notice that `b` still has its fields `c` and `d`, so overriding has a merging effect as well. Moreover, the type of expression `a.b.e` is now `E1` instead of `E`. + +We can also use overriding to add new paths to an existing type, so if we override the initial type with the following: + +``` +object{a: object{b: object{f: F}}} +``` + +We obtain the following type: + +``` +object{a: object{b: object{c: C, d: D, e: E, f: F}}} +``` + +We use schemas to enhance the type checking capability of OPA, and not to validate the input and data documents against desired schemas. This burden is still on the user and care must be taken when using overriding to ensure that the input and data provided are sensible and validated against the transformed schemas. + +### Multiple input schemas + +It is sometimes useful to have different input schemas for different rules in the same package. This can be achieved as illustrated by the following example: + +```rego +package policy + +import data.acl + +default allow := false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - data.acl: schema["acl-schema"] +allow if { + access := data.acl[input.user] + access[_] == input.operation +} + +# METADATA for whocan rule +# scope: rule +# schemas: +# - input: schema["whocan-input-schema"] +# - data.acl: schema["acl-schema"] +whocan contains user if { + access := acl[user] + access[_] == input.operation +} +``` + +The directory that is passed to `opa eval` is the following: + +```shell +$ tree mySchemasDir/ +mySchemasDir/ +├── input.json +└── acl-schema.json +└── whocan-input-schema.json +``` + +In this example, we associate the schema `input.json` with the input document in the rule `allow`, and the schema `whocan-input-schema.json` +with the input document for the rule `whocan`. + +### Translating schemas to Rego types and dynamicity + +Rego has a gradual type system meaning that types can be partially known statically. For example, an object could have certain fields whose types are known and others that are unknown statically. OPA type checks what it knows statically and leaves the unknown parts to be type checked at runtime. An OPA object type has two parts: the static part with the type information known statically, and a dynamic part, which can be nil (meaning everything is known statically) or non-nil and indicating what is unknown. + +When we derive a type from a schema, we try to match what is known and unknown in the schema. For example, an `object` that has no specified fields becomes the Rego type `Object{Any: Any}`. However, currently `additionalProperties` and `additionalItems` are ignored. When a schema is fully specified, we derive a type with its dynamic part set to nil, meaning that we take a strict interpretation in order to get the most out of static type checking. This is the case even if `additionalProperties` is set to `true` in the schema. In the future, we will take this feature into account when deriving Rego types. + +When overriding existing types, the dynamicity of the overridden prefix is preserved. + +### Supporting JSON Schema composition keywords + +JSON Schema provides keywords such as `anyOf` and `allOf` to structure a complex schema. For `anyOf`, at least one of the subschemas must be true, and for `allOf`, all subschemas must be true. The type checker is able to identify such keywords and derive a more robust Rego type through more complex schemas. + +#### `anyOf` + +Specifically, `anyOf` acts as an Rego Or type where at least one (can be more than one) of the subschemas is true. Consider the following Rego and schema file containing `anyOf`: + +```rego title="policy-anyOf.rego" +package kubernetes.admission + +# METADATA +# scope: rule +# schemas: +# - input: schema["input-anyOf"] +deny if { + input.request.servers.versions == "Pod" +} +``` + +```json title="input-anyOf.json" +{ + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "properties": { + "kind": { "type": "string" }, + "request": { + "type": "object", + "anyOf": [ + { + "properties": { + "kind": { + "type": "object", + "properties": { + "kind": { "type": "string" }, + "version": { "type": "string" } + } + } + } + }, + { + "properties": { + "server": { + "type": "object", + "properties": { + "accessNum": { "type": "integer" }, + "version": { "type": "string" } + } + } + } + } + ] + } + } +} +``` + +We can see that `request` is an object with two options as indicated by the choices under `anyOf`: + +- contains property `kind`, which has properties `kind` and `version` +- contains property `server`, which has properties `accessNum` and `version` + +The type checker finds the first error in the Rego code, suggesting that `servers` should be either `kind` or `server`. + +``` +input.request.servers.versions + ^ + have: "servers" + want (one of): ["kind" "server"] +``` + +Once this is fixed, the second typo is highlighted, prompting the user to choose between `accessNum` and `version`. + +``` +input.request.server.versions + ^ + have: "versions" + want (one of): ["accessNum" "version"] +``` + +#### `allOf` + +Specifically, `allOf` keyword implies that all conditions under `allOf` within a schema must be met by the given data. `allOf` is implemented through merging the types from all of the JSON subSchemas listed under `allOf` before parsing the result to convert it to a Rego type. Merging of the JSON subSchemas essentially combines the passed in subSchemas based on what types they contain. Consider the following Rego and schema file containing `allOf`: + +```rego title="policy-allOf.rego" +package kubernetes.admission + +# METADATA +# scope: rule +# schemas: +# - input: schema["input-allof"] +deny if { + input.request.servers.versions == "Pod" +} +``` + +```json title="input-allOf.json" +{ + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "properties": { + "kind": { "type": "string" }, + "request": { + "type": "object", + "allOf": [ + { + "properties": { + "kind": { + "type": "object", + "properties": { + "kind": { "type": "string" }, + "version": { "type": "string" } + } + } + } + }, + { + "properties": { + "server": { + "type": "object", + "properties": { + "accessNum": { "type": "integer" }, + "version": { "type": "string" } + } + } + } + } + ] + } + } +} +``` + +We can see that `request` is an object with properties as indicated by the elements listed under `allOf`: + +- contains property `kind`, which has properties `kind` and `version` +- contains property `server`, which has properties `accessNum` and `version` + +The type checker finds the first error in the Rego code, suggesting that `servers` should be `server`. + +``` +input.request.servers.versions + ^ + have: "servers" + want (one of): ["kind" "server"] +``` + +Once this is fixed, the second typo is highlighted, informing the user that `versions` should be one of `accessNum` or `version`. + +``` +input.request.server.versions + ^ + have: "versions" + want (one of): ["accessNum" "version"] +``` + +Because the properties `kind`, `version`, and `accessNum` are all under the `allOf` keyword, the resulting schema that the given data must be validated against will contain the types contained in these properties children (string and integer). + +### Remote references in JSON schemas + +It is valid for JSON schemas to reference other JSON schemas via URLs, like this: + +```json +{ + "description": "Pod is a collection of containers that can run on a host.", + "type": "object", + "properties": { + "metadata": { + "$ref": "https://kubernetesjsonschema.dev/v1.14.0/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + } +} +``` + +OPA's type checker will fetch these remote references by default. +To control the remote hosts schemas will be fetched from, pass a capabilities +file to your `opa eval` or `opa check` call. + +Starting from the capabilities.json of your OPA version (which can be found [in the repository](https://github.com/open-policy-agent/opa/tree/main/capabilities)), add +an `allow_net` key to it: its values are the IP addresses or host names that OPA is +supposed to connect to for retrieving remote schemas. + +```json +{ + "builtins": [ ... ], + "allow_net": [ "kubernetesjsonschema.dev" ] +} +``` + +#### Note + +- To forbid all network access in schema checking, set `allow_net` to `[]` +- Host names are checked against the list as-is, so adding `127.0.0.1` to `allow_net`, + and referencing a schema from `http://localhost/` will _fail_. +- Metaschemas for different JSON Schema draft versions are not subject to this + constraint, as they are already provided by OPA's schema checker without requiring + network access. These are: + + - `http://json-schema.org/draft-04/schema` + - `http://json-schema.org/draft-06/schema` + - `http://json-schema.org/draft-07/schema` + +### Limitations + +Currently this feature admits schemas written in JSON Schema but does not support every feature available in this format. +In particular the following features are not yet supported: + +- additional properties for objects +- pattern properties for objects +- additional items for arrays +- contains for arrays +- oneOf, not +- enum +- if/then/else + +A note of caution: overriding is a powerful capability that must be used carefully. For example, the user is allowed to write: + +``` +# METADATA +# scope: rule +# schema: +# - data: schema["some-schema"] +``` + +In this case, we are overriding the root of all documents to have some schema. Since all Rego code lives under `data` as virtual documents, this in practice renders all of them inaccessible (resulting in type errors). Similarly, assigning a schema to a package name is not a good idea and can cause problems. Care must also be taken when defining overrides so that the transformation of schemas is sensible and data can be validated against the transformed schema. + +### References + +For more examples, please see [here](https://github.com/aavarghese/opa-schema-examples). + +This contains samples for Envoy, Kubernetes, and Terraform including corresponding JSON Schemas. + +See here for the [JSON Schema Reference](https://docs.solo.io/gloo-edge/latest/guides/security/auth/extauth/opa/). + +For a tool that generates JSON Schema from JSON samples, +[please see here](https://app.quicktype.io/#l=schema) +([Other Tools](https://json-schema.org/tools?query=&sortBy=name&sortOrder=ascending&groupBy=toolingTypes&licenses=&languages=&drafts=&toolingTypes=data-to-schema&environments=&showObsolete=false&supportsBowtie=false)). + +## Strict Mode + +The Rego compiler supports `strict mode`, where additional constraints and safety checks are enforced during compilation. +Compiler Strict mode is supported by the `check` command, and can be enabled through the `--strict`/`-S` flag. + +``` +-S, --strict enable compiler strict mode +``` + +### Strict Mode Constraints and Checks + +| Name | Description | +| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | +| Unused local assignments | Unused arguments or [assignments](./policy-reference/#assignment-and-equality) local to a rule, function or comprehension are prohibited | +| Unused imports | Unused [imports](./policy-language/#imports) are prohibited. | + +## Ecosystem Projects + + +Here are some projects that can help you learn Rego: + diff --git a/third_party/opa/docs/docs/policy-performance.md b/third_party/opa/docs/docs/policy-performance.md new file mode 100644 index 000000000000..4025b70e7d42 --- /dev/null +++ b/third_party/opa/docs/docs/policy-performance.md @@ -0,0 +1,980 @@ +--- +title: Policy Performance +sidebar_position: 5 +--- + +## High Performance Policy Decisions + +Some use cases require very low-latency policy decisions. For example, a microservice API authorization decision might +have a budget in the order of 1 millisecond. OPA is a general-purpose policy engine and supports some features and +techniques to address high-performance use cases. + +### Linear fragment + +For such high-performance use cases, there is a fragment of the Rego language which has been engineered to evaluate +in near constant time. Adding more rules to the policy will not significantly increase the evaluation time. + +For example, the following rule has one local variable `user`, and that variable can only be assigned one value. Intuitively, evaluating this rule requires checking each of the conditions in the body, and if there were N of these rules, evaluation would only require walking over each of them as well. + +```rego +package linear + +allow if { + some user + input.method == "GET" + input.path = ["accounts", user] + input.user == user +} +``` + +### Use objects over arrays + + + + +```rego title="Bad" +# Array of objects where each object has a unique identifier +d := [{"id": "a123", "first": "alice", "last": "smith"}, + {"id": "a456", "first": "bob", "last": "jones"}, + {"id": "a789", "first": "clarice", "last": "johnson"} + ] +# search through all elements of the array to find the ID +d[i].id == "a789" +d[i].first ... +``` + +One common mistake people make is using arrays when they could use objects. For +example, below is an array of ID/first-name/last-names where ID is unique, and +you're looking up the first-name/last-name given the ID. + + + + +```rego title="Good" +# Use object whose keys are the IDs for the objects. +# Looking up an object given its ID requires NO search +d := {"a123": {"first": "alice", "last": "smith"}, + "a456": {"first": "bob", "last": "jones"}, + "a789": {"first": "clarice", "last": "johnson"} + } +# no search required +d["a789"].first ... +``` + +Instead, use a dictionary where the key is the ID and the value is the +first-name/last-name. Given the ID, you can look up the name information +directly. + + + + +### Use indexed statements + +The linear-time fragment ensures that the cost of evaluation is no larger than the size of the policy. OPA lets you write non-linear policies, because sometimes you need to, and because sometimes it's convenient. The blog on [partial evaluation](https://blog.openpolicyagent.org/partial-evaluation-162750eaf422) describes one mechanism for converting non-linear policies into linear policies. + +But as the size of the policy grows, the cost of evaluation grows with it. Sometimes the policy can grow large enough that even the linear-fragment fails to meet the performance budget. + +In the linear fragment, OPA includes special algorithms that **index rules efficiently**, sometimes making evaluation constant-time, even as the policy grows. The more effective the indexing is the fewer rules need to be evaluated. + +Here is an example policy from the [rule-indexing blog](https://blog.openpolicyagent.org/optimizing-opa-rule-indexing-59f03f17caf3) giving the details for these algorithms. See the rest of this section for details on indexed statements. + +```rego +package indexed + +default allow := false + +allow if { + some user + input.method == "GET" + input.path = ["accounts", user] + input.user == user +} + +allow if { + input.method == "GET" + input.path == ["accounts", "report"] + roles[input.user][_] == "admin" +} + +allow if { + input.method == "POST" + input.path == ["accounts"] + roles[input.user][_] == "admin" +} + +roles := { + "bob": ["admin", "hr"], + "alice": ["procurement"], +} +``` + + + +```json +{ + "user": "bob", + "path": ["accounts", "bob"], + "method": "GET" +} +``` + + + +#### Equality statements + +For simple equality statements (`=` and `==`) to be indexed one side must be a non-nested reference that does not contain any variables and the other side must be a variable, scalar, or array (which may contain scalars and variables). For example: + +| Expression | Indexed | Reason | +| --------------------------- | ------- | ---------------------------- | +| `input.x == "foo"` | yes | n/a | +| `input.x.y == "bar"` | yes | n/a | +| `input.x == ["foo", i]` | yes | n/a | +| `input.x[i] == "foo"` | no | reference contains variables | +| `input.x[input.y] == "foo"` | no | reference is nested | + +#### Glob statements + +For `glob.match(pattern, delimiter, match)` statements to be indexed the pattern must be recognized by the indexer and the match be a non-nested reference that does not contain any variables. The indexer recognizes patterns containing the normal glob (`*`) operator but not the super glob (`**`) or character pattern matching operators. + +| Expression | Indexed | Reason | +| -------------------------------------------- | ------- | -------------------------- | +| `glob.match("foo:*:bar", [":"], input.x)` | yes | n/a | +| `glob.match("foo:**:bar", [":"], input.x)` | no | pattern contains `**` | +| `glob.match("foo:*:bar", [":"], input.x[i])` | no | match contains variable(s) | + +### Early Exit in Rule Evaluation + +In general, OPA has to iterate all potential variable bindings to determine the outcome +of a query. However, there are conditions under which additional iterations cannot change +the result: + +1. A set of complete document rules that only have one, ground value. +2. A set of function rules that only have one, ground value. + +The most common case for this are a set of `allow` rules: + +```rego +package earlyexit + +allow if { + input.user == "alice" +} + +allow if { + input.user == "bob" +} + +allow if { + input.group == "admins" +} +``` + +since `allow if { ... }` is a shorthand for `allow := true if { ... }`. + +Intuitively, the value can be anything that does not contain a variable: + +```rego +package earlyexit.examples + +# p, q, r and s could be evaluated with early-exit semantics: + +p if { + # ... +} + +q := 123 if { + # ... +} + +r := {"hello": "world"} if { + # ... +} + +s(x) := 12 if { + # ... +} + +# u, v, w, and y could _not_ + +u contains x if { # not a complete document rule, but a partial set + x := 911 +} + +v := x if { # x is a variable, not ground + x := true +} + +w := {"foo": x} if { # a compound term containing a variable + x := "bar" +} + +y(z) := r if { # variable value, not ground + r := z + 1 +} +``` + +When "early exit" is possible for a (set of) rules, iterations inside that rule will be +**cancelled** as soon as one binding matches the rule body: + +```rego +package earlyexit.iteration + +p if { + some p + data.projects[p] == "project-a" +} +``` + +Since there's no possibility that could change the outcome of `data.earlyexit.iteration.p` +once a variable binding is found that satisfies the conditions, no further iteration will +occur. + +The check if "early exit" is applicable for a query happens _after_ the indexing lookup, +so in this contrived example, an evaluation with input `{"user": "alice"}` _would_ exit +early; an evaluation with `{"user": "bob", "group": "admins"}` _would not_: + +```rego +package earlyexit + +allow if { + input.user == "alice" +} + +allow := false if { + input.user == "bob" +} + +allow if { + input.group == "admins" +} +``` + +This is because the index lookup for `{"user": "bob", "group": "admins"}` returns two complete +document rules with _different values_, `true` and `false`, whereas the indexer query for +`{"user": "alice"}` only returns rules with value `true`. + +### Comprehension Indexing + +Rego does not support mutation. As a result, certain operations like "group by" require +use of comprehensions to aggregate values. To avoid O(n^2) runtime complexity in +queries/rules that perform group-by, OPA may compute and memoize the entire collection +produced by comprehensions at once. This ensures that runtime complexity is O(n) where +n is the size of the collection that group-by/aggregation is being performed on. + +For example, suppose the policy must check if the number of ports exposed on an interface +exceeds some threshold (e.g., any interface may expose up to 100 ports.) The policy is given +the port->interface mapping as a JSON array under `input`: + +```json +{ + "exposed": [ + { + "interface": "eth0", + "port": 8080 + }, + { + "interface": "eth0", + "port": 8081 + }, + { + "interface": "eth1", + "port": 443 + }, + { + "interface": "lo1", + "port": 5000 + } + ] +} +``` + +In this case, the policy must count the number of ports exposed on each interface. To do this, +the policy must first aggregate/group the ports by the interface name. Conceptually, +the policy should generate a document like this: + +```json +{ + "exposed_ports_by_interface": { + "eth0": [8080, 8081], + "eth1": [443], + "lo1": [5000] + } +} +``` + +Since multiple ports could be exposed on a single interface, the policy must use a comprehension to +aggregate the port values by the interface names. To implement this logic in Rego, we would write: + +```rego +some i +intf := input.exposed[i].interface +ports := [port | some j; input.exposed[j].interface == intf; port := input.exposed[j].port] +``` + +Without comprehension indexing, this query would be O(n^2) where n is the size of `input.exposed`. +However, with comprehension indexing, the query remains O(n) because OPA only computes the comprehension +_once_. In this case, the comprehension is evaluated and all possible values of `ports` are computed +at once. These values are indexed by the assignments of `intf`. + +To implement the policy above we could write: + +```rego +package example + +deny contains msg if { + some i + count(exposed_ports_by_interface[i]) > 100 + msg := sprintf("interface '%v' exposes too many ports", [i]) +} + +exposed_ports_by_interface := {intf: ports | + some i + intf := input.exposed[i].interface + ports := [port | + some j + input.exposed[j].interface == intf + port := input.exposed[j].port + ] +} +``` + +Indices can be built for comprehensions (nested or not) that generate collections (i.e., arrays, sets, or objects) +based on variables in an outer query. In the example above: + +- `intf` is the variable in the outer query. +- `[port | some j; input.exposed[j].interface == intf; port := input.exposed[j].port]` is the comprehension. +- `ports` is the variable the collection is assigned to. + +In order to be indexed, comprehensions must meet the following conditions: + +1. The comprehension appears in an assignment or unification statement. +1. The expression containing the comprehension does not include a `with` statement. +1. The expression containing the comprehension is not negated. +1. The comprehension body is safe when considered independent of the outer query. +1. The comprehension body closes over at least one variable in the outer query and none of these variables appear as outputs in references or `walk()` calls or inside nested comprehensions. + +The following examples shows rules that are **not** indexed: + +```rego +package example + +not_indexed_because_missing_assignment if { + x := input[_] + [y | some y; x == input[y]] +} + +not_indexed_because_includes_with if { + x := input[_] + ys := [y | some y; x := input[y]] with input as {} +} + +not_indexed_because_negated if { + x := input[_] + not data.arr = [y | some y; x := input[y]] +} + +not_indexed_because_safety if { + obj := input.foo.bar + x := obj[_] + ys := [y | some y; x == obj[y]] +} + +not_indexed_because_no_closure if { + ys := [y | x := input[y]] +} + +not_indexed_because_reference_operand_closure if { + x := input[y].x + ys := [y | x == input[y].z[_]] +} + +not_indexed_because_nested_closure if { + x := 1 + y := 2 + _ = [i | + x == input.foo[i] + _ = [j | y == input.bar[j]] + ] +} +``` + +> The 4th and 5th restrictions may be relaxed in the future. + +### Profiling + +You can also profile your policies using `opa eval`. The profiler is useful if you need to understand +why policy evaluation is slow. + +The `opa eval` command provides the following profiler options: + +| Option | Detail | Default | +| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | +| `--profile` | Enables expression profiling and outputs profiler results. | off | +| `--profile-sort` | Criteria to sort the expression profiling results. This options implies `--profile`. | total_time_ns => num_eval => num_redo => num_gen_expr => file => line | +| `--profile-limit` | Desired number of profiling results sorted on the given criteria. This options implies `--profile`. | 10 | +| `--count` | Desired number of evaluations that profiling metrics are to be captured for. With `--format=pretty`, the output will contain min, max, mean and the 90th and 99th percentile. All collected percentiles can be found in the JSON output. | 1 | + +#### Sort criteria for the profile results + +- `total_time_ns` - Results are displayed is decreasing order of _expression evaluation time_ +- `num_eval` - Results are displayed is decreasing order of _number of times an expression is evaluated_ +- `num_redo` - Results are displayed is decreasing order of _number of times an expression is re-evaluated(redo)_ +- `num_gen_expr` - Results are displayed is decreasing order of _number of generated expressions_ +- `file` - Results are sorted in reverse alphabetical order based on the _rego source filename_ +- `line` - Results are displayed is decreasing order of _expression line number_ in the source file + +When the sort criteria is not provided `total_time_ns` has the highest sort priority +while `line` has the lowest. + +The `num_gen_expr` represents the number of expressions generated for a given statement on a particular line. For example, +let's take the following policy: + +```rego +package test + +p if { + a := 1 + b := 2 + c := 3 + x = a + (b * c) +} +``` + +If we profile the above policy we would get something like the following output: + +``` ++----------+----------+----------+--------------+-------------+ +| TIME | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION | ++----------+----------+----------+--------------+-------------+ +| 20.291µs | 3 | 3 | 3 | test.rego:8 | +| 1µs | 1 | 1 | 1 | test.rego:7 | +| 2.333µs | 1 | 1 | 1 | test.rego:6 | +| 6.333µs | 1 | 1 | 1 | test.rego:5 | +| 84.75µs | 1 | 1 | 1 | data | ++----------+----------+----------+--------------+-------------+ +``` + +The first entry indicates that line `test.rego:8` has a `EVAL/REDO` count of `3`. If we look at the expression on line `test.rego:8` +ie `x = a + b * c` it's not immediately clear why this line has a `EVAL/REDO` count of `3`. But we also notice that there +are `3` generated expressions (ie. `NUM GEN EXPR`) at line `test.rego:8`. This is because the compiler rewrites the above policy to +something like below: + +`p = true if { __local0__ = 1; __local1__ = 2; __local2__ = 3; mul(__local1__, __local2__, __local3__); plus(__local0__, __local3__, __local4__); x = __local4__ }` + +And that line `test.rego:8` is rewritten to `mul(__local1__, __local2__, __local3__); plus(__local0__, __local3__, __local4__); x = __local4__` which +results in a `NUM GEN EXPR` count of `3`. Hence, the `NUM GEN EXPR` count can help to better understand the `EVAL/REDO` counts +for a given expression and also provide more clarity into the profile results and how policy evaluation works. + +#### Example Policy + +The different profiling examples shown later on this page use the below +sample policy. + +```rego +package rbac + +# Example input request + +inp := { + "subject": "bob", + "resource": "foo123", + "action": "write", +} + +# Example RBAC configuration. +bindings := [ + { + "user": "alice", + "roles": ["dev", "test"], + }, + { + "user": "bob", + "roles": ["test"], + }, +] + +roles := [ + { + "name": "dev", + "permissions": [ + {"resource": "foo123", "action": "write"}, + {"resource": "foo123", "action": "read"}, + ], + }, + { + "name": "test", + "permissions": [{"resource": "foo123", "action": "read"}], + }, +] + +# Example RBAC policy implementation. + +default allow := false + +allow if { + some role_name + user_has_role[role_name] + role_has_permission[role_name] +} + +user_has_role contains role_name if { + binding := bindings[_] + binding.user == inp.subject + role_name := binding.roles[_] +} + +role_has_permission contains role_name if { + role := roles[_] + role_name := role.name + perm := role.permissions[_] + perm.resource == inp.resource + perm.action == inp.action +} +``` + +#### Example: Display all profile results with default ordering criteria + +```bash +opa eval --data rbac.rego --profile --format=pretty 'data.rbac.allow' +``` + +**Sample Output** + +```ruby +false ++------------------------------+---------+ +| METRIC | VALUE | ++------------------------------+---------+ +| timer_rego_load_files_ns | 769583 | +| timer_rego_module_compile_ns | 1652125 | +| timer_rego_module_parse_ns | 482417 | +| timer_rego_query_compile_ns | 23042 | +| timer_rego_query_eval_ns | 440542 | +| timer_rego_query_parse_ns | 36250 | ++------------------------------+---------+ ++-----------+----------+----------+--------------+-----------------+ +| TIME | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION | ++-----------+----------+----------+--------------+-----------------+ +| 237.126µs | 1 | 1 | 1 | data.rbac.allow | +| 25.75µs | 1 | 1 | 1 | docs.rego:13 | +| 17.5µs | 1 | 1 | 1 | docs.rego:40 | +| 6.832µs | 2 | 1 | 1 | docs.rego:50 | +| 5.042µs | 1 | 1 | 1 | docs.rego:44 | +| 4.666µs | 1 | 0 | 1 | docs.rego:45 | +| 4.209µs | 1 | 1 | 1 | docs.rego:58 | +| 3.792µs | 1 | 2 | 1 | docs.rego:49 | +| 3.666µs | 1 | 2 | 1 | docs.rego:55 | +| 3.167µs | 1 | 1 | 1 | docs.rego:24 | ++-----------+----------+----------+--------------+-----------------+ +``` + +As seen from the above table, all results are displayed. The profile results are +sorted on the default sort criteria. + +To evaluate the policy multiple times, and aggregate the profiling data over those +runs, pass `--count=NUMBER`: + +```bash +opa eval --data rbac.rego --profile --format=pretty --count=10 'data.rbac.allow' +``` + +**Sample Output** + +```ruby +false ++------------------------------+--------+---------+----------+------------------------+--------------+ +| METRIC | MIN | MAX | MEAN | 90% | 99% | ++------------------------------+--------+---------+----------+------------------------+--------------+ +| timer_rego_load_files_ns | 140167 | 1092875 | 387233.3 | 1.0803291e+06 | 1.092875e+06 | +| timer_rego_module_compile_ns | 447208 | 1178542 | 646295.9 | 1.1565419000000001e+06 | 1.178542e+06 | +| timer_rego_module_parse_ns | 121458 | 1041333 | 349183.2 | 1.022583e+06 | 1.041333e+06 | +| timer_rego_query_compile_ns | 17542 | 47875 | 25758.4 | 47450 | 47875 | +| timer_rego_query_eval_ns | 47666 | 136625 | 68200 | 132762.5 | 136625 | +| timer_rego_query_parse_ns | 14334 | 46917 | 26270.9 | 46842 | 46917 | ++------------------------------+--------+---------+----------+------------------------+--------------+ ++---------+----------+---------+----------+----------+----------+----------+--------------+-----------------+ +| MIN | MAX | MEAN | 90% | 99% | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION | ++---------+----------+---------+----------+----------+----------+----------+--------------+-----------------+ +| 5.208µs | 27µs | 9.008µs | 25.525µs | 27µs | 1 | 1 | 1 | data.rbac.allow | +| 4.126µs | 17µs | 7.196µs | 16.479µs | 17µs | 1 | 1 | 1 | docs.rego:13 | +| 3.958µs | 12.833µs | 6.116µs | 12.583µs | 12.833µs | 1 | 1 | 1 | docs.rego:40 | +| 3.459µs | 10.708µs | 5.354µs | 10.499µs | 10.708µs | 2 | 1 | 1 | docs.rego:50 | +| 3.291µs | 9.209µs | 4.912µs | 9.096µs | 9.209µs | 1 | 1 | 1 | docs.rego:44 | +| 3.209µs | 8.75µs | 4.637µs | 8.62µs | 8.75µs | 1 | 0 | 1 | docs.rego:45 | +| 3.042µs | 8.333µs | 4.491µs | 8.233µs | 8.333µs | 1 | 1 | 1 | docs.rego:51 | +| 3µs | 7.25µs | 4.1µs | 7.112µs | 7.25µs | 1 | 1 | 1 | docs.rego:58 | +| 2.667µs | 5.75µs | 3.783µs | 5.72µs | 5.75µs | 1 | 2 | 1 | docs.rego:49 | +| 2.583µs | 5.708µs | 3.479µs | 5.595µs | 5.708µs | 1 | 1 | 1 | docs.rego:24 | ++---------+----------+---------+----------+----------+----------+----------+--------------+-----------------+ +``` + +##### Example: Display top 5 profile results + +```bash +opa eval --data rbac.rego --profile-limit 5 --format=pretty 'data.rbac.allow' +``` + +**Sample Output** + +```ruby ++----------+----------+----------+--------------+-----------------+ +| TIME | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION | ++----------+----------+----------+--------------+-----------------+ +| 24.624µs | 1 | 1 | 1 | data.rbac.allow | +| 15.251µs | 1 | 1 | 1 | docs.rego:13 | +| 12.167µs | 1 | 1 | 1 | docs.rego:40 | +| 9.625µs | 2 | 1 | 1 | docs.rego:50 | +| 8.751µs | 1 | 1 | 1 | docs.rego:44 | ++----------+----------+----------+--------------+-----------------+ +``` + +The profile results are sorted on the default sort criteria. +Also `--profile` option is implied and does not need to be provided. + +##### Example: Display top 5 profile results based on the 'number of times an expression is evaluated' + +```bash +opa eval --data rbac.rego --profile-limit 5 --profile-sort num_eval --format=pretty 'data.rbac.allow' +``` + +**Sample Profile Output** + +```ruby ++----------+----------+----------+--------------+-----------------+ +| TIME | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION | ++----------+----------+----------+--------------+-----------------+ +| 10.541µs | 2 | 1 | 1 | docs.rego:50 | +| 4.041µs | 2 | 1 | 1 | docs.rego:56 | +| 27.876µs | 1 | 1 | 1 | data.rbac.allow | +| 19.916µs | 1 | 1 | 1 | docs.rego:40 | +| 19.416µs | 1 | 1 | 1 | docs.rego:13 | ++----------+----------+----------+--------------+-----------------+ +``` + +As seen from the above table, the results are arranged first in decreasing +order of number of evaluations and if two expressions have been evaluated +the same number of times, the default criteria is used since no other sort criteria is provided. +In this case, total_time_ns => num_redo => file => line. +Also `--profile` option is implied and does not need to be provided. + +##### Example: Display top 5 profile results based on the 'number of times an expression is evaluated' and 'number of times an expression is re-evaluated' + +```bash +opa eval --data rbac.rego --profile-limit 5 --profile-sort num_eval,num_redo --format=pretty 'data.rbac.allow' +``` + +**Sample Profile Output** + +```ruby ++---------+----------+----------+--------------+-----------------+ +| TIME | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION | ++---------+----------+----------+--------------+-----------------+ +| 9.625µs | 2 | 1 | 1 | docs.rego:50 | +| 3.458µs | 2 | 1 | 1 | docs.rego:56 | +| 5.625µs | 1 | 2 | 1 | docs.rego:49 | +| 5.292µs | 1 | 2 | 1 | docs.rego:55 | +| 18.25µs | 1 | 1 | 1 | data.rbac.allow | ++---------+----------+----------+--------------+-----------------+ +``` + +As seen from the above table, result are first arranged based on _number of evaluations_, +then _number of re-evaluations_ and finally the default criteria is used. +In this case, total_time_ns => file => line. +The `--profile-sort` options accepts repeated or comma-separated values for the criteria. +The order of the criteria on the command line determine their priority. + +Another way to get the same output as above would be the following: + +```bash +opa eval --data rbac.rego --profile-limit 5 --profile-sort num_eval --profile-sort num_redo --format=pretty 'data.rbac.allow' +``` + +## Benchmarking Queries + +OPA provides CLI options to benchmark a single query via the `opa bench` command. This will evaluate similarly to +`opa eval` but it will repeat the evaluation (in its most efficient form) a number of times and report metrics. + +#### Example: Benchmark rbac allow + +Using the same [policy source as shown above](#example-policy): + +```bash +opa bench --data rbac.rego 'data.rbac.allow' +``` + +Will result in an output similar to: + +``` ++-------------------------------------------+------------+ +| samples | 27295 | +| ns/op | 45032 | +| B/op | 20977 | +| allocs/op | 382 | +| histogram_timer_rego_query_eval_ns_stddev | 25568 | +| histogram_timer_rego_query_eval_ns_99.9% | 335906 | +| histogram_timer_rego_query_eval_ns_99.99% | 336493 | +| histogram_timer_rego_query_eval_ns_mean | 40355 | +| histogram_timer_rego_query_eval_ns_median | 35846 | +| histogram_timer_rego_query_eval_ns_99% | 133936 | +| histogram_timer_rego_query_eval_ns_90% | 44780 | +| histogram_timer_rego_query_eval_ns_95% | 50815 | +| histogram_timer_rego_query_eval_ns_min | 31284 | +| histogram_timer_rego_query_eval_ns_max | 336493 | +| histogram_timer_rego_query_eval_ns_75% | 38254 | +| histogram_timer_rego_query_eval_ns_count | 27295 | ++-------------------------------------------+------------+ +``` + +These results capture metrics of `samples` runs, where only the query evaluation is measured. All time spent preparing +to evaluate (loading, parsing, compiling, etc.) is omitted. + +> Note: all `*/op` results are an average over the number of `samples` (or `N` in the JSON format) + +#### Options for `opa bench` + +| Option | Detail | Default | +| ------------------------------------------------------- | ------------------------------------------------- | ------- | +| `--benchmem` | Report memory allocations with benchmark results. | true | +| `--metrics` | Report additional query performance metrics. | true | +| `--count` | Number of times to repeat the benchmark. | 1 | + +### Benchmarking OPA Tests + +There is also a `--bench` option for `opa test` which will perform benchmarking on OPA unit tests. This will evaluate +any loaded tests as benchmarks. There will be additional time for any test-specific actions are included so the timing +will typically be longer than what is seen with `opa bench`. The primary use-case is not for absolute time, but to +track relative time as policies change. + +#### Options for `opa test --bench` + +| Option | Detail | Default | +| ------------------------------------------------------- | ------------------------------------------------- | ------- | +| `--benchmem` | Report memory allocations with benchmark results. | true | +| `--count` | Number of times to repeat the benchmark. | 1 | + +#### Example Tests + +Adding a unit test file for the [policy source as shown above](#example-policy): + +```rego +package rbac + +test_user_has_role_dev if { + user_has_role.dev with input as {"subject": "alice"} +} + +test_user_has_role_negative if { + not user_has_role["super-admin"] with input as {"subject": "alice"} +} +``` + +Which when run normally will output something like: + +``` +$ opa test -v ./rbac.rego ./rbac_test.rego +data.rbac.test_user_has_role_dev: PASS (605.076µs) +data.rbac.test_user_has_role_negative: PASS (318.047µs) +-------------------------------------------------------------------------------- +PASS: 2/2 +``` + +#### Example: Benchmark rbac unit tests + +```bash +opa test -v --bench ./rbac.rego ./rbac_test.rego +``` + +Results in output: + +``` +data.rbac.test_user_has_role_dev 44749 27677 ns/op 23146 timer_rego_query_eval_ns/op 12303 B/op 229 allocs/op +data.rbac.test_user_has_role_negative 44526 26348 ns/op 22033 timer_rego_query_eval_ns/op 12470 B/op 235 allocs/op +-------------------------------------------------------------------------------- +PASS: 2/2 +``` + +#### Example: Benchmark rbac unit tests and compare with `benchstat` + +The benchmark output formats default to `pretty`, but support a `gobench` format which complies with the +[Golang Benchmark Data Format](https://go.googlesource.com/proposal/+/master/design/14313-benchmark-format.md). +This allows for usage of tools like [benchstat](https://godoc.org/golang.org/x/perf/cmd/benchstat) to gain additional +insight into the benchmark results and to diff between benchmark results. + +Example: + +```bash +opa test -v --bench --count 10 --format gobench ./rbac.rego ./rbac_test.rego | tee ./old.txt +``` + +Will result in an `old.txt` and output similar to: + +``` +BenchmarkDataRbacTestUserHasRoleDev 45152 26323 ns/op 22026 timer_rego_query_eval_ns/op 12302 B/op 229 allocs/op +BenchmarkDataRbacTestUserHasRoleNegative 45483 26253 ns/op 21986 timer_rego_query_eval_ns/op 12470 B/op 235 allocs/op +-------------------------------------------------------------------------------- +PASS: 2/2 +``` + +Repeated 10 times (as specified by the `--count` flag). + +This format can then be loaded by `benchstat`: + +```bash +benchstat ./old.txt +``` + +Output: + +``` +name time/op +DataRbacTestUserHasRoleDev 29.8µs ±18% +DataRbacTestUserHasRoleNegative 32.0µs ±35% + +name timer_rego_query_eval_ns/op +DataRbacTestUserHasRoleDev 25.0k ±18% +DataRbacTestUserHasRoleNegative 26.7k ±35% + +name alloc/op +DataRbacTestUserHasRoleDev 12.3kB ± 0% +DataRbacTestUserHasRoleNegative 12.5kB ± 0% + +name allocs/op +DataRbacTestUserHasRoleDev 229 ± 0% +DataRbacTestUserHasRoleNegative 235 ± 0% +``` + +If later on a change was introduced that altered the performance we can run again: + +```bash +opa test -v --bench --count 10 --format gobench ./rbac.rego ./rbac_test.rego | tee ./new.txt +``` + +``` +BenchmarkDataRbacTestUserHasRoleDev 27415 43671 ns/op 39301 timer_rego_query_eval_ns/op 17201 B/op 379 allocs/op +BenchmarkDataRbacTestUserHasRoleNegative 27583 44743 ns/op 40152 timer_rego_query_eval_ns/op 17369 B/op 385 allocs/op +-------------------------------------------------------------------------------- +PASS: 2/2 +``` + +(Repeated 10 times) + +Then we can compare the results via: + +```bash +benchstat ./old.txt ./new.txt +``` + +``` +name old time/op new time/op delta +DataRbacTestUserHasRoleDev 29.8µs ±18% 47.4µs ±15% +59.06% (p=0.000 n=9+10) +DataRbacTestUserHasRoleNegative 32.0µs ±35% 47.1µs ±14% +47.48% (p=0.000 n=10+9) + +name old timer_rego_query_eval_ns/op new timer_rego_query_eval_ns/op delta +DataRbacTestUserHasRoleDev 25.0k ±18% 42.6k ±15% +70.51% (p=0.000 n=9+10) +DataRbacTestUserHasRoleNegative 26.7k ±35% 42.3k ±14% +58.15% (p=0.000 n=10+9) + +name old alloc/op new alloc/op delta +DataRbacTestUserHasRoleDev 12.3kB ± 0% 17.2kB ± 0% +39.81% (p=0.000 n=10+10) +DataRbacTestUserHasRoleNegative 12.5kB ± 0% 17.4kB ± 0% +39.28% (p=0.000 n=10+10) + +name old allocs/op new allocs/op delta +DataRbacTestUserHasRoleDev 229 ± 0% 379 ± 0% +65.50% (p=0.000 n=10+10) +DataRbacTestUserHasRoleNegative 235 ± 0% 385 ± 0% +63.83% (p=0.000 n=10+10) +``` + +This gives clear feedback that the evaluations have slowed down considerably by looking at the `delta` + +> Note that for [benchstat](https://godoc.org/golang.org/x/perf/cmd/benchstat) you will want to run with `--count` to +> repeat the benchmarks a number of times (5-10 is usually enough). The tool requires several data points else the `p` +> value will not show meaningful changes and the `delta` will be `~`. + +## Resource Utilization + +Policy evaluation is typically CPU-bound unless the policies have to pull additional +data on-the-fly using built-in functions like `http.send()` (in which case evaluation +likely becomes I/O-bound.) Policy evaluation is currently single-threaded. If you +are embedding OPA as a library, it is your responsibility to dispatch concurrent queries +to different Goroutines/threads. If you are running the OPA server, it will parallelize +concurrent requests and use as many cores as possible. You can limit the number of +cores that OPA can consume by starting OPA with the [`GOMAXPROCS`](https://golang.org/pkg/runtime) +environment variable. + +Memory usage scales with the size of the policy (i.e., Rego) and data (e.g., JSON) that you +load into OPA. Raw JSON data loaded into OPA uses approximately 20x more memory compared to the +same data stored in a compact, serialized format (e.g., on disk). This increased +memory usage is due to the need to load the JSON data into Go data structures like maps, +slices, and strings so that it can be evaluated. For example, if you load 8MB worth of +JSON data representing 100,000 permission objects specifying subject/action/resource triplets, +OPA would consume approximately 160MB of RAM. + +Memory usage also scales linearly with the number of rules loaded into OPA. For example, +loading 10,000 rules that implement an ACL-style authorization policy consumes approximately +130MB of RAM while 100,000 rules implementing the same policy (but with 10x more tuples to check) +consumes approximately 1.1GB of RAM. + +By default, OPA stores policy and data in-memory. OPA's disk storage feature allows policy and data to be stored on disk. See [this](./storage/#disk) for more details. + +## Optimization Levels + +The `--optimize` (or `-O`) flag on the `opa build` command controls how bundles are optimized. + +> Optimization applies partial evaluation to precompute _known_ values in the policy. The goal of +> partial evaluation is to convert non-linear-time policies into linear-time policies. + +By specifying the `--optimize` flag, users can control how much time and resources are spent +attempting to optimize the bundle. Generally, higher optimization levels require more time +and resources. Currently, OPA supports three optimization levels. The exact optimizations applied +in each level may change over time. + +### -O=0 (default) + +By default optimizations are disabled. + +### -O=1 (recommended) + +Policies are partially evaluated. Rules that DO NOT depend on unknowns (directly or indirectly) are +evaluated and the virtual documents they produce are inlined into call sites. Virtual documents that +are required at evaluation time are not inlined. For example, if a base or virtual document is +targeted by a `with` statement in the policy, the document will not be inlined. + +Rules that depend on unknowns (directly or indirectly) are also partially evaluated however the +virtual documents they produce ARE NOT inlined into call sites. The output policy should be structurally +similar to the input policy. + +The `opa build` automatically marks the `input` document as unknown. In addition to the `input` document, +if `opa build` is invoked with the `-b`/`--bundle` flag, any `data` references NOT prefixed by the +`.manifest` roots are also marked as unknown. + +### -O=2 (aggressive) + +Same as `-O=1` except virtual documents produced by rules that depend on unknowns may be inlined +into call sites. In addition, more aggressive inlining is applied within rules. This includes +[copy propagation](https://en.wikipedia.org/wiki/Copy_propagation) and inlining of certain negated +statements that would otherwise generate support rules. + +## Storage Optimization + +### In-Memory Store Read Optimization + +During normal operation, data values read from storage are converted to an AST representation that is used during policy evaluation. +This conversion can be expensive both in execution time and in memory usage, especially for large data values. +The default in-memory store can be configured to optimize for read speed by precomputing the AST representation of data values during storage write operations. +This removes the time spent converting raw data values to AST during policy evaluation, improving performance. + +The memory footprint of the store will increase, as processed AST values generally take up more space in memory than the corresponding raw data values, but overall memory usage of OPA might remain more stable over time, as pre-converted data is shared across evaluations and isn't recomputed for each evaluation, which can cause spikes in memory usage. +Storage write operations will be slower due to the additional processing required to precompute the AST representation of data values. This can impact startup time and bundle loading/updates, especially for large data values. + +This feature can be enabled for `opa run`, `opa eval`, and `opa bench` by setting the `--optimize-store-for-read-speed` flag. + +Users are recommended to do performance testing to determine the optimal configuration for their use case. + +## Key Takeaways + +For high-performance use cases: + +- Write your policies to minimize iteration and search. + - Use objects instead of arrays when you have a unique identifier for the elements of the array. + - Consider [partial-evaluation](https://blog.openpolicyagent.org/partial-evaluation-162750eaf422) to compile non-linear policies to linear policies. +- Write your policies with indexed statements so that [rule-indexing](https://blog.openpolicyagent.org/optimizing-opa-rule-indexing-59f03f17caf3) is effective. +- Use the profiler to help identify portions of the policy that would benefit the most from improved performance. +- Use the benchmark tools to help get real world timing data and detect policy performance changes. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/config.json b/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/config.json new file mode 100644 index 000000000000..972742914881 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/config.json @@ -0,0 +1,7 @@ +{ + "showInput": true, + "showData": false, + "showTitles": false, + "command": "data.crypto_digest_verification", + "titleSize": 5 +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/input.json b/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/input.json new file mode 100644 index 000000000000..40d1b315d5f3 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/input.json @@ -0,0 +1,8 @@ +{ + "payload": { + "user": "alice", + "action": "read", + "resource": "/api/users" + }, + "expected_digest": "ea99819f665c10c744cbbf8da651c37a" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/intro.md new file mode 100644 index 000000000000..81f56d4ab341 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/intro.md @@ -0,0 +1,6 @@ +This example shows how to use `crypto.md5` to verify payload integrity by computing a digest of the JSON representation and comparing it with an expected value. + +Content verification is helpful where you need to ensure data hasn't been tampered with or missed during transmission. The digest acts as a fingerprint - any change to the payload will result in a different digest. + +Change any value in the `payload` object (like the user name or resource path) and re-run the example. You'll see `digest_valid` becomes `false`, demonstrating how any change is detected. + diff --git a/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/policy.rego new file mode 100644 index 000000000000..a2f5210719ef --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/policy.rego @@ -0,0 +1,7 @@ +package crypto_digest_verification + +# Verify payload integrity using MD5 digest +# (commonly used for content verification) +payload_json := json.marshal(input.payload) +computed_digest := crypto.md5(payload_json) +digest_valid := computed_digest == input.expected_digest diff --git a/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/title.txt new file mode 100644 index 000000000000..7e969321d4d4 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/crypto/digest_verification/title.txt @@ -0,0 +1,2 @@ +Payload Digest Verification + diff --git a/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/config.json b/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/config.json new file mode 100644 index 000000000000..784da7da23fc --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/config.json @@ -0,0 +1,7 @@ +{ + "showInput": true, + "showData": false, + "showTitles": false, + "command": "data.envoy.authz", + "titleSize": 5 +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/input.json b/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/input.json new file mode 100644 index 000000000000..8c57a10a007a --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/input.json @@ -0,0 +1,12 @@ +{ + "attributes": { + "request": { + "http": { + "headers": { + "x-username": "alice", + "x-password": "secret123" + } + } + } + } +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/intro.md new file mode 100644 index 000000000000..7d04ea5b75c1 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/intro.md @@ -0,0 +1,6 @@ +This example shows how `base64.encode` acts as a utility function to bridge communication between client and server when they don't speak the same language. + +Suppose that some legacy client sends credentials in custom headers (`x-username`, `x-password`), but the downstream service expects HTTP Basic Authentication. This example policy uses the base64 function to deliver this transparently to the downstream caller. + +This might be useful in an API gateway where you need to adapt between different authentication schemes without the option of editing clients and downstream servers. + diff --git a/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/policy.rego new file mode 100644 index 000000000000..f220add0083b --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/policy.rego @@ -0,0 +1,26 @@ +package envoy.authz + +# Extract credentials from request header +headers := input.attributes.request.http.headers + +username := headers["x-username"] +password := headers["x-password"] + +# Default deny - only allow if credentials are provided +default allow := false + +allow if { + username != "" + password != "" +} + +# Add Authorization header to downstream request using base64 encoding +response_headers_to_add := { + "Authorization": sprintf( + "Basic %s", [ + base64.encode(sprintf( + "%s:%s", [username, password]) + ) + ] + ) +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/title.txt new file mode 100644 index 000000000000..334b17d0cba5 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/encoding/envoy_header_manipulation/title.txt @@ -0,0 +1 @@ +OPA Envoy Header Manipulation diff --git a/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/config.json b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/config.json new file mode 100644 index 000000000000..7d70c89138c1 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.graph_reachable_example.result" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/intro.md new file mode 100644 index 000000000000..5a2239925f1e --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/intro.md @@ -0,0 +1,4 @@ +A common class of recursive rules can be reduced to a graph reachability +problem, so `graph.reachable` is useful for more than just graph analysis. +This usually requires some pre- and postprocessing. The following example +shows you how to "flatten" a hierarchy of access permissions. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/policy.rego new file mode 100644 index 000000000000..b5a47166b54b --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/policy.rego @@ -0,0 +1,21 @@ +package graph_reachable_example + +org_chart_data := { + "ceo": {}, + "human_resources": {"owner": "ceo", "access": ["salaries", "complaints"]}, + "staffing": {"owner": "human_resources", "access": ["interviews"]}, + "internships": {"owner": "staffing", "access": ["blog"]}, +} + +org_chart_graph[entity_name] := edges if { + org_chart_data[entity_name] + edges := {neighbor | org_chart_data[neighbor].owner == entity_name} +} + +org_chart_permissions[entity_name] := access if { + org_chart_data[entity_name] + reachable := graph.reachable(org_chart_graph, {entity_name}) + access := {item | reachable[k]; item := org_chart_data[k].access[_]} +} + +result contains org_chart_permissions[entity_name] diff --git a/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/title.txt new file mode 100644 index 000000000000..e06e55928bff --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable/title.txt @@ -0,0 +1 @@ +Graph Reachable diff --git a/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/config.json b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/config.json new file mode 100644 index 000000000000..2721545638d6 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.graph_reachable_paths_example.result" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/intro.md new file mode 100644 index 000000000000..fe23dfbea74e --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/intro.md @@ -0,0 +1 @@ +It may be useful to find all reachable paths from a root element. `graph.reachable_paths` can be used for this. Note that cyclical paths will terminate on the repeated node. If an element references a nonexistent element, the path will be terminated, and excludes the nonexistent node. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/policy.rego new file mode 100644 index 000000000000..9009146b99d7 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/policy.rego @@ -0,0 +1,15 @@ +package graph_reachable_paths_example + +path_data := { + "aTop": [], + "cMiddle": ["aTop"], + "bBottom": ["cMiddle"], + "dIgnored": [], +} + +all_paths[root] := paths if { + path_data[root] + paths := graph.reachable_paths(path_data, {root}) +} + +result contains all_paths[entity_name] diff --git a/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/title.txt new file mode 100644 index 000000000000..4ae56a7661b5 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/graphs/reachable_paths/title.txt @@ -0,0 +1 @@ +Graph Reachable Paths diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/config.json b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/config.json new file mode 100644 index 000000000000..b1426891afbb --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.netcidrcontainsmatches.result" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/intro.md new file mode 100644 index 000000000000..49d51619d796 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/intro.md @@ -0,0 +1 @@ +Either (or both) operand(s) may be an array, set, or object. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/policy.rego new file mode 100644 index 000000000000..576b68e0ceef --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/policy.rego @@ -0,0 +1,3 @@ +package netcidrcontainsmatches + +result := net.cidr_contains_matches(["1.1.1.0/24", "1.1.2.0/24"], "1.1.1.128") diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/title.txt new file mode 100644 index 000000000000..e92e77e4ab01 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_array_string/title.txt @@ -0,0 +1 @@ +CIDR Match with Array diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/config.json b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/config.json new file mode 100644 index 000000000000..b1426891afbb --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.netcidrcontainsmatches.result" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/intro.md new file mode 100644 index 000000000000..3db16128a081 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/intro.md @@ -0,0 +1 @@ +The array/set/object elements may be arrays. In that case, the first element must be a valid CIDR/IP. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/policy.rego new file mode 100644 index 000000000000..f99ff105d8ce --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/policy.rego @@ -0,0 +1,6 @@ +package netcidrcontainsmatches + +result := net.cidr_contains_matches( + [["1.1.0.0/16", "foo"], "1.1.2.0/24"], + ["1.1.1.128", ["1.1.254.254", "bar"]] +) diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/title.txt new file mode 100644 index 000000000000..6c5946302dc3 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_arrays/title.txt @@ -0,0 +1 @@ +CIDR Match with Arrays diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/config.json b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/config.json new file mode 100644 index 000000000000..b1426891afbb --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.netcidrcontainsmatches.result" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/intro.md new file mode 100644 index 000000000000..e42ccd08be7d --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/intro.md @@ -0,0 +1 @@ +If the operand is a set, the outputs are matching elements. If the operand is an object, the outputs are matching keys. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/policy.rego new file mode 100644 index 000000000000..7a4bff406752 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/policy.rego @@ -0,0 +1,6 @@ +package netcidrcontainsmatches + +result := net.cidr_contains_matches( + {["1.1.0.0/16", "foo"], "1.1.2.0/24"}, + {"x": "1.1.1.128", "y": ["1.1.254.254", "bar"]} +) diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/title.txt new file mode 100644 index 000000000000..ab31b003a24d --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_objects/title.txt @@ -0,0 +1 @@ +CIDR Match with Objects diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/config.json b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/config.json new file mode 100644 index 000000000000..b1426891afbb --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.netcidrcontainsmatches.result" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/intro.md new file mode 100644 index 000000000000..2b5929a27284 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/intro.md @@ -0,0 +1 @@ +If both operands are string values the function is similar to `net.cidr_contains`. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/policy.rego new file mode 100644 index 000000000000..5021546df6c5 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/policy.rego @@ -0,0 +1,3 @@ +package netcidrcontainsmatches + +result := net.cidr_contains_matches("1.1.1.0/24", "1.1.1.128") diff --git a/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/title.txt new file mode 100644 index 000000000000..de0af2b04335 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/net/cidr_contains_strings/title.txt @@ -0,0 +1 @@ +CIDR Match with String Ranges diff --git a/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/config.json b/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/config.json new file mode 100644 index 000000000000..2743717033b2 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/config.json @@ -0,0 +1,7 @@ +{ + "showInput": true, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.example" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/input.json b/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/input.json new file mode 100644 index 000000000000..4c1810cfc939 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/input.json @@ -0,0 +1,7 @@ +{ + "number": 11, + "subject": { + "name": "John doe", + "role": "customer" + } +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/intro.md new file mode 100644 index 000000000000..be1ec16c55ed --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/intro.md @@ -0,0 +1,4 @@ +The following policy will deny the given input because: + +- the `number` is greater than 5 +- the `subject` does not have the `admin` role diff --git a/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/policy.rego new file mode 100644 index 000000000000..e2e9ad27c43a --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/policy.rego @@ -0,0 +1,21 @@ +package example + +# METADATA +# title: Deny invalid numbers +# description: Numbers may not be higher than 5 +# custom: +# severity: MEDIUM +deny contains format(rego.metadata.rule()) if { + input.number > 5 +} + +# METADATA +# title: Deny non-admin subjects +# description: Subject must have the 'admin' role +# custom: +# severity: HIGH +deny contains format(rego.metadata.rule()) if { + input.subject.role != "admin" +} + +format(meta) := {"severity": meta.custom.severity, "reason": meta.description} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/title.txt new file mode 100644 index 000000000000..2afdf1c2c8c1 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/rego/rule_metadata/title.txt @@ -0,0 +1 @@ +Rule Metadata diff --git a/third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/config.json b/third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/config.json new file mode 100644 index 000000000000..6ac5ff999a65 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.semverisvalid" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/intro.md new file mode 100644 index 000000000000..11fc03d8d90b --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/intro.md @@ -0,0 +1,8 @@ +The `result := semver.is_valid(vsn)` function checks to see if a version +string is of the form: `MAJOR.MINOR.PATCH[-PRERELEASE][+METADATA]`, where +items in square braces are optional elements. + +:::warning +When working with Go-style semantic versions, remember to remove the +leading `v` character, or the semver string will be marked as invalid! +::: diff --git a/third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/policy.rego new file mode 100644 index 000000000000..ba8f8f99aad8 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/policy.rego @@ -0,0 +1,5 @@ +package semverisvalid + +leadingV := semver.is_valid("v1.1.12-rc1+foo") + +valid := semver.is_valid("1.1.12-rc1+foo") diff --git a/third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/title.txt new file mode 100644 index 000000000000..b8e594523253 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/semver/isvalid/title.txt @@ -0,0 +1 @@ +Example of semver.is_valid diff --git a/third_party/opa/docs/docs/policy-reference/_examples/time/time_format/config.json b/third_party/opa/docs/docs/policy-reference/_examples/time/time_format/config.json new file mode 100644 index 000000000000..df34d705bf24 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/time/time_format/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "command": "data.time_format", + "titleSize": 5 +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/time/time_format/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/time/time_format/intro.md new file mode 100644 index 000000000000..48e912f54f9b --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/time/time_format/intro.md @@ -0,0 +1 @@ +In OPA, we can parse a simple YYYY-MM-DD timestamp as follows: diff --git a/third_party/opa/docs/docs/policy-reference/_examples/time/time_format/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/time/time_format/policy.rego new file mode 100644 index 000000000000..cdacc971f843 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/time/time_format/policy.rego @@ -0,0 +1,4 @@ +package time_format + +ts := "1985-10-27" +result := time.parse_ns("2006-01-02", ts) diff --git a/third_party/opa/docs/docs/policy-reference/_examples/time/time_format/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/time/time_format/title.txt new file mode 100644 index 000000000000..0a9a1809c90c --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/time/time_format/title.txt @@ -0,0 +1 @@ +Timestamp Parsing diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/empty_json/config.json b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/empty_json/config.json new file mode 100644 index 000000000000..664417172f0f --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/empty_json/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.jwt.result" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/empty_json/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/empty_json/policy.rego new file mode 100644 index 000000000000..abfd98ff2c27 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/empty_json/policy.rego @@ -0,0 +1,9 @@ +package jwt + +result := io.jwt.encode_sign({ + "typ": "JWT", + "alg": "HS256"}, + {}, { + "kty": "oct", + "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow" +}) diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/empty_json/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/empty_json/title.txt new file mode 100644 index 000000000000..da0cc9ea8bc8 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/empty_json/title.txt @@ -0,0 +1 @@ +Symmetric Key with empty JSON payload diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/hmac/config.json b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/hmac/config.json new file mode 100644 index 000000000000..664417172f0f --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/hmac/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.jwt.result" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/hmac/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/hmac/policy.rego new file mode 100644 index 000000000000..c125fd4ddcf2 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/hmac/policy.rego @@ -0,0 +1,18 @@ +package jwt + +result := io.jwt.encode_sign({ + "typ": "JWT", + "alg": "HS256" +}, { + "iss": "joe", + "exp": 1300819380, + "aud": ["bob", "saul"], + "http://example.com/is_root": true, + "privateParams": { + "private_one": "one", + "private_two": "two" + } +}, { + "kty": "oct", + "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow" +}) diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/hmac/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/hmac/title.txt new file mode 100644 index 000000000000..ed005e78d010 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/hmac/title.txt @@ -0,0 +1 @@ +Symmetric Key (HMAC with SHA-256) diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/rsa/config.json b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/rsa/config.json new file mode 100644 index 000000000000..664417172f0f --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/rsa/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.jwt.result" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/rsa/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/rsa/policy.rego new file mode 100644 index 000000000000..eb280605a6af --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/rsa/policy.rego @@ -0,0 +1,25 @@ +package jwt + +result := io.jwt.encode_sign({ + "alg": "RS256" +}, { + "iss": "joe", + "exp": 1300819380, + "aud": ["bob", "saul"], + "http://example.com/is_root": true, + "privateParams": { + "private_one": "one", + "private_two": "two" + } +}, +{ + "kty": "RSA", + "n": "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", + "e": "AQAB", + "d": "Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ", + "p": "4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc", + "q": "uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc", + "dp": "BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0", + "dq": "h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU", + "qi": "IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U" +}) diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/rsa/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/rsa/title.txt new file mode 100644 index 000000000000..43d8d0a3ac9a --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/rsa/title.txt @@ -0,0 +1 @@ +RSA Key (RSA Signature with SHA-256) diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/config.json b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/config.json new file mode 100644 index 000000000000..664417172f0f --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.jwt.result" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/intro.md new file mode 100644 index 000000000000..a49f6ca577c6 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/intro.md @@ -0,0 +1,3 @@ +If you need to generate the signature for a serialized token you an use the +`io.jwt.encode_sign_raw` built-in function which accepts JSON serialized string +parameters. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/policy.rego new file mode 100644 index 000000000000..fd18f47024f6 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/policy.rego @@ -0,0 +1,7 @@ +package jwt + +result := io.jwt.encode_sign_raw( + `{"typ":"JWT","alg":"HS256"}`, + `{"iss":"joe","exp":1300819380,"http://example.com/is_root":true}`, + `{"kty":"oct","k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"}` +) diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/title.txt new file mode 100644 index 000000000000..8a4f22fa21a6 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/sign/sign_raw/title.txt @@ -0,0 +1 @@ +Raw Token Signing diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/config.json b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/config.json new file mode 100644 index 000000000000..9bd499f9c862 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/config.json @@ -0,0 +1,8 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "showPlayground": false, + "titleSize": 5, + "command": "data.jwt" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/intro.md new file mode 100644 index 000000000000..684820074238 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/intro.md @@ -0,0 +1,3 @@ +This example shows a two-step process to verify the token signature and then decode it for +further checks of the payload content. This approach gives more flexibility in verifying only +the claims that the policy needs to enforce. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/policy.rego new file mode 100644 index 000000000000..d16dbb4cd669 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/policy.rego @@ -0,0 +1,5 @@ +package jwt + +result.verify := io.jwt.verify_es256(es256_token, cert) # Verify the token with the certificate +result.payload := io.jwt.decode(es256_token) # Decode the token +result.check := result.payload[1].iss == "xxx" # Ensure the issuer (`iss`) claim is the expected value diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/title.txt new file mode 100644 index 000000000000..cee3da9d3bd1 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert/title.txt @@ -0,0 +1 @@ +Certificate Verify diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/config.json b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/config.json new file mode 100644 index 000000000000..9bd499f9c862 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/config.json @@ -0,0 +1,8 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "showPlayground": false, + "titleSize": 5, + "command": "data.jwt" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/intro.md new file mode 100644 index 000000000000..cfa596903128 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/intro.md @@ -0,0 +1,4 @@ +This next example shows doing the same token signature verification, decoding, and content checks +but instead with a single call to `io.jwt.decode_verify`. Note that this gives less flexibility +in validating the payload content as **all** claims defined in the JWT spec are verified with the +provided constraints. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/policy.rego new file mode 100644 index 000000000000..b3ff1924fdd1 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/policy.rego @@ -0,0 +1,8 @@ +package jwt + +result := [valid, header, payload] if { + [valid, header, payload] := io.jwt.decode_verify(es256_token, { + "cert": cert, + "iss": "xxx", + }) +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/title.txt new file mode 100644 index 000000000000..1ac5f598524a --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/cert_single/title.txt @@ -0,0 +1 @@ +Certificate Verify Single diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/config.json b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/config.json new file mode 100644 index 000000000000..9bd499f9c862 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/config.json @@ -0,0 +1,8 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "showPlayground": false, + "titleSize": 5, + "command": "data.jwt" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/intro.md new file mode 100644 index 000000000000..684820074238 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/intro.md @@ -0,0 +1,3 @@ +This example shows a two-step process to verify the token signature and then decode it for +further checks of the payload content. This approach gives more flexibility in verifying only +the claims that the policy needs to enforce. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/policy.rego new file mode 100644 index 000000000000..cf12572c3e98 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/policy.rego @@ -0,0 +1,5 @@ +package jwt + +result.verify := io.jwt.verify_es256(es256_token, jwks) # Verify the token with the JWKS +result.payload := io.jwt.decode(es256_token) # Decode the token +result.check := result.payload[1].iss == "xxx" # Ensure the issuer (`iss`) claim is the expected value diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/title.txt new file mode 100644 index 000000000000..ae546bb0e36d --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks/title.txt @@ -0,0 +1 @@ +JWKS Verify diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/config.json b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/config.json new file mode 100644 index 000000000000..9bd499f9c862 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/config.json @@ -0,0 +1,8 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "showPlayground": false, + "titleSize": 5, + "command": "data.jwt" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/data.json b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/data.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/data.json @@ -0,0 +1 @@ +{} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/input.json b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/input.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/input.json @@ -0,0 +1 @@ +{} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/intro.md new file mode 100644 index 000000000000..49b2d55f9889 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/intro.md @@ -0,0 +1,4 @@ +This next example shows doing the token signature verification, decoding, and content checks +all in one call using `io.jwt.decode_verify`. Note that this gives less flexibility in validating +the payload content as **all** claims defined in the JWT spec are verified with the provided +constraints. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/policy.rego new file mode 100644 index 000000000000..48fcb4d0404b --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/policy.rego @@ -0,0 +1,8 @@ +package jwt + +result := [valid, header, payload] if { + [valid, header, payload] := io.jwt.decode_verify(es256_token, { + "cert": jwks, + "iss": "xxx", + }) +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/title.txt new file mode 100644 index 000000000000..e42777726e49 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/jwks_single/title.txt @@ -0,0 +1 @@ +JWKS Single Verify diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/config.json b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/config.json new file mode 100644 index 000000000000..5c3b946f6bc4 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.jwt" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/intro.md new file mode 100644 index 000000000000..4a61fa5a7c4f --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/intro.md @@ -0,0 +1 @@ +This one demonstrates how to encode the and sign the same token contents as in the example above but with `io.jwt.encode_sign` instead of the `raw` variant. diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/policy.rego new file mode 100644 index 000000000000..aa4584f47dff --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/policy.rego @@ -0,0 +1,17 @@ +package jwt + +result_hs256 := io.jwt.encode_sign( + { + "alg":"HS256", + "typ":"JWT" + }, + {}, + { + "kty":"oct", + "k":"Zm9v" + } +) + +# Important! - Use the un-encoded plain text secret to verify and decode +result_parts_hs256 := io.jwt.decode_verify(result_hs256, {"secret": "foo"}) +result_valid_hs256 := io.jwt.verify_hs256(result_hs256, "foo") diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/title.txt new file mode 100644 index 000000000000..a6acae0639d5 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign/title.txt @@ -0,0 +1 @@ +Sign and Verify diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/config.json b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/config.json new file mode 100644 index 000000000000..5c3b946f6bc4 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/config.json @@ -0,0 +1,7 @@ +{ + "showInput": false, + "showData": false, + "showTitles": false, + "titleSize": 5, + "command": "data.jwt" +} diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/intro.md b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/intro.md new file mode 100644 index 000000000000..9ec8a2d6dde7 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/intro.md @@ -0,0 +1 @@ +This exambles demonstrates how to do this using the `io.jwt.encode_sign_raw` built-in: diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/policy.rego b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/policy.rego new file mode 100644 index 000000000000..87f200925dc1 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/policy.rego @@ -0,0 +1,11 @@ +package jwt + +raw_result_hs256 := io.jwt.encode_sign_raw( + `{"alg":"HS256","typ":"JWT"}`, + `{}`, + `{"kty":"oct","k":"Zm9v"}` # "Zm9v" == base64url.encode_no_pad("foo") +) + +# Important! - Use the un-encoded plain text secret to verify and decode +raw_result_valid_hs256 := io.jwt.verify_hs256(raw_result_hs256, "foo") +raw_result_parts_hs256 := io.jwt.decode_verify(raw_result_hs256, {"secret": "foo"}) diff --git a/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/title.txt b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/title.txt new file mode 100644 index 000000000000..a49884e020cf --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/_examples/tokens/verify/sign_raw/title.txt @@ -0,0 +1 @@ +Sign and Verify Raw diff --git a/third_party/opa/docs/docs/policy-reference/builtins/aggregates.mdx b/third_party/opa/docs/docs/policy-reference/builtins/aggregates.mdx new file mode 100644 index 000000000000..37b04d6c5a7c --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/aggregates.mdx @@ -0,0 +1,5 @@ +--- +title: Aggregates +--- + + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/array.mdx b/third_party/opa/docs/docs/policy-reference/builtins/array.mdx new file mode 100644 index 000000000000..5ac1e02a6791 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/array.mdx @@ -0,0 +1,4 @@ +--- +title: Arrays +--- + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/bits.mdx b/third_party/opa/docs/docs/policy-reference/builtins/bits.mdx new file mode 100644 index 000000000000..6e6cab4998f4 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/bits.mdx @@ -0,0 +1,4 @@ +--- +title: Bits +--- + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/comparison.mdx b/third_party/opa/docs/docs/policy-reference/builtins/comparison.mdx new file mode 100644 index 000000000000..108927be5e0a --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/comparison.mdx @@ -0,0 +1,4 @@ +--- +title: Comparisons +--- + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/conversions.mdx b/third_party/opa/docs/docs/policy-reference/builtins/conversions.mdx new file mode 100644 index 000000000000..814d4ae73926 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/conversions.mdx @@ -0,0 +1,4 @@ +--- +title: Type Conversions +--- + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/crypto.mdx b/third_party/opa/docs/docs/policy-reference/builtins/crypto.mdx new file mode 100644 index 000000000000..aaf139249be7 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/crypto.mdx @@ -0,0 +1,8 @@ +--- +title: Cryptography +--- + + +#### Examples + + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/encoding.mdx b/third_party/opa/docs/docs/policy-reference/builtins/encoding.mdx new file mode 100644 index 000000000000..b3963a73a058 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/encoding.mdx @@ -0,0 +1,21 @@ +--- +title: Encoding +--- + + +The `json.marshal_with_options` builtin's `opts` parameter accepts the following properties: + +| Field | Required | Type | Default | Description | +| :------- | :------- | :------- | :------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `pretty` | No | `bool` | `true` if `indent` or `prefix` are declared,
`false` otherwise | Enables multi-line, human-readable JSON output ("pretty-printing").
If this property is `true`, then objects will be marshaled into multi-line JSON with either user-specified or default indent/prefix options. If this property is `false`, `indent`/`prefix` will be ignored and this builtin functions identically to `json.marshal()`. | +| `indent` | No | `string` | `"\t"`
(Horizontal tab, character 0x09) | The string to use when indenting nested keys in the emitted JSON. One or more copies of this string will be included before child elements in every object or array. | +| `prefix` | No | `string` | `""`
(empty) | The string to prefix lines with in the emitted JSON. One copy of this string will be prepended to each line. | + +Default values will be used if: + +- `opts` is an empty object. +- `opts` does not contain the named property. + +#### Examples + + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/glob.mdx b/third_party/opa/docs/docs/policy-reference/builtins/glob.mdx new file mode 100644 index 000000000000..799665e91c25 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/glob.mdx @@ -0,0 +1,29 @@ +--- +title: Glob Matching +--- + +The following table shows examples of how `glob.match` works: + +| `call` | `output` | Description | +| ---------------------------------------------------------------- | -------- | --------------------------------------------- | +| `output := glob.match("*.github.com", [], "api.github.com")` | `true` | A glob with the default `["."]` delimiter. | +| `output := glob.match("*.github.com", [], "api.cdn.github.com")` | `false` | A glob with the default `["."]` delimiter. | +| `output := glob.match("*hub.com", null, "api.cdn.github.com")` | `true` | A glob without delimiter. | +| `output := glob.match("*:github:com", [":"], "api:github:com")` | `true` | A glob with delimiters `[":"]`. | +| `output := glob.match("api.**.com", [], "api.github.com")` | `true` | A super glob. | +| `output := glob.match("api.**.com", [], "api.cdn.github.com")` | `true` | A super glob. | +| `output := glob.match("?at", [], "cat")` | `true` | A glob with a single character wildcard. | +| `output := glob.match("?at", [], "at")` | `false` | A glob with a single character wildcard. | +| `output := glob.match("[abc]at", [], "bat")` | `true` | A glob with character-list matchers. | +| `output := glob.match("[abc]at", [], "cat")` | `true` | A glob with character-list matchers. | +| `output := glob.match("[abc]at", [], "lat")` | `false` | A glob with character-list matchers. | +| `output := glob.match("[!abc]at", [], "cat")` | `false` | A glob with negated character-list matchers. | +| `output := glob.match("[!abc]at", [], "lat")` | `true` | A glob with negated character-list matchers. | +| `output := glob.match("[a-c]at", [], "cat")` | `true` | A glob with character-range matchers. | +| `output := glob.match("[a-c]at", [], "lat")` | `false` | A glob with character-range matchers. | +| `output := glob.match("[!a-c]at", [], "cat")` | `false` | A glob with negated character-range matchers. | +| `output := glob.match("[!a-c]at", [], "lat")` | `true` | A glob with negated character-range matchers. | +| `output := glob.match("{cat,bat,[fr]at}", [], "cat")` | `true` | A glob with pattern-alternatives matchers. | +| `output := glob.match("{cat,bat,[fr]at}", [], "bat")` | `true` | A glob with pattern-alternatives matchers. | +| `output := glob.match("{cat,bat,[fr]at}", [], "rat")` | `true` | A glob with pattern-alternatives matchers. | +| `output := glob.match("{cat,bat,[fr]at}", [], "at")` | `false` | A glob with pattern-alternatives matchers. | diff --git a/third_party/opa/docs/docs/policy-reference/builtins/graph.mdx b/third_party/opa/docs/docs/policy-reference/builtins/graph.mdx new file mode 100644 index 000000000000..6b1dac93da9f --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/graph.mdx @@ -0,0 +1,7 @@ +--- +title: Graphs +--- + + + + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/graphql.mdx b/third_party/opa/docs/docs/policy-reference/builtins/graphql.mdx new file mode 100644 index 000000000000..bb621ae87d09 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/graphql.mdx @@ -0,0 +1,37 @@ +--- +title: GraphQL +--- + +:::info +Custom [GraphQL `@directive`](http://spec.graphql.org/October2021/#sec-Language.Directives) definitions defined by your GraphQL framework will need to be included manually as part of your GraphQL schema string in order for validation to work correctly on GraphQL queries using those directives. + +Directives defined as part of the GraphQL specification (`@skip`, `@include`, `@deprecated`, and `@specifiedBy`) are supported by default, and do not need to be added to your schema manually. +::: + +#### GraphQL Custom `@directive` Example + +New `@directive` definitions can be defined separately from your schema, so long as you `concat` them onto the schema definition before attempting to validate a query/schema using those custom directives. +In the following example, a custom directive is defined, and then used in the schema to annotate an argument on one of the allowed query types. + +```rego +package graphql_custom_directive_example + +custom_directives := ` +directive @customDeprecatedArgs( + reason: String +) on ARGUMENT_DEFINITION +` + +schema := ` +type Query { + foo(name: String! @customDeprecatedArgs(reason: "example reason")): String, + bar: String! +} +` + +query := `query { foo(name: "example") }` + +p { + graphql.is_valid(query, concat("", [custom_directives, schema])) +} +``` diff --git a/third_party/opa/docs/docs/policy-reference/builtins/http.mdx b/third_party/opa/docs/docs/policy-reference/builtins/http.mdx new file mode 100644 index 000000000000..b09cf3f2b9ab --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/http.mdx @@ -0,0 +1,115 @@ +--- +title: HTTP +--- + +:::info +Similar to other built-in functions, multiple calls to the `http.send` built-in function for a given request object +within a single policy evaluation query will always return the same value. +::: + +:::danger +This built-in function **must not** be used for effecting changes in +external systems as OPA does not guarantee that the statement will be executed due +to automatic performance optimizations that are applied during policy evaluation. +::: + +The `request` object parameter may contain the following fields: + +| Field | Required | Type | Description | +| ------------------------------ | -------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `url` | yes | `string` | HTTP URL to specify in the request (e.g., `"https://www.openpolicyagent.org"`). | +| `method` | yes | `string` | HTTP method to specify in request (e.g., `"GET"`, `"POST"`, `"PUT"`, etc.) | +| `body` | no | `any` | HTTP message body to include in request. The value will be serialized to JSON. | +| `raw_body` | no | `string` | HTTP message body to include in request. The value WILL NOT be serialized. Use this for non-JSON messages. | +| `headers` | no | `object` | HTTP headers to include in the request (e.g,. `{"X-Opa": "rules"}`). | +| `enable_redirect` | no | `boolean` | Follow HTTP redirects. Default: `false`. | +| `force_json_decode` | no | `boolean` | Decode the HTTP response message body as JSON even if the `Content-Type` header is missing. Default: `false`. | +| `force_yaml_decode` | no | `boolean` | Decode the HTTP response message body as YAML even if the `Content-Type` header is missing. Default: `false`. | +| `tls_use_system_certs` | no | `boolean` | Use the system certificate pool. Default: `true` when `tls_ca_cert`, `tls_ca_cert_file`, `tls_ca_cert_env_variable` are unset. **Ignored on Windows** due to the system certificate pool not being accessible in the same way as it is for other platforms. | +| `tls_ca_cert` | no | `string` | String containing a root certificate in PEM encoded format. | +| `tls_ca_cert_file` | no | `string` | Path to file containing a root certificate in PEM encoded format. | +| `tls_ca_cert_env_variable` | no | `string` | Environment variable containing a root certificate in PEM encoded format. | +| `tls_client_cert` | no | `string` | String containing a client certificate in PEM encoded format. | +| `tls_client_cert_file` | no | `string` | Path to file containing a client certificate in PEM encoded format. | +| `tls_client_cert_env_variable` | no | `string` | Environment variable containing a client certificate in PEM encoded format. | +| `tls_client_key` | no | `string` | String containing a key in PEM encoded format. | +| `tls_client_key_file` | no | `string` | Path to file containing a key in PEM encoded format. | +| `tls_client_key_env_variable` | no | `string` | Environment variable containing a client key in PEM encoded format. | +| `timeout` | no | `string` or `number` | Timeout for the HTTP request with a default of 5 seconds (`5s`). Numbers provided are in nanoseconds. Strings must be a valid duration string where a duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". A zero timeout means no timeout. | +| `tls_insecure_skip_verify` | no | `bool` | Allows for skipping TLS verification when calling a network endpoint. Not recommended for production. | +| `tls_server_name` | no | `string` | Sets the hostname that is sent in the client Server Name Indication and that be will be used for server certificate validation. If this is not set, the value of the `Host` header (if present) will be used. If neither are set, the host name from the requested URL is used. | +| `cache` | no | `boolean` | Cache HTTP response across OPA queries. Default: `false`. | +| `force_cache` | no | `boolean` | Cache HTTP response across OPA queries and override cache directives defined by the server. Default: `false`. | +| `force_cache_duration_seconds` | no | `number` | If `force_cache` is set, this field specifies the duration in seconds for the freshness of a cached response. | +| `caching_mode` | no | `string` | Controls the format in which items are inserted into the inter-query cache. Allowed modes are `serialized` and `deserialized`. In the `serialized` mode, items will be serialized before inserting into the cache. This mode is helpful if memory conservation is preferred over higher latency during cache lookup. This is the default mode. In the `deserialized` mode, an item will be inserted in the cache without any serialization. This means when items are fetched from the cache, there won't be a need to decode them. This mode helps to make the cache lookup faster at the expense of more memory consumption. If this mode is enabled, the configured `caching.inter_query_builtin_cache.max_size_bytes` value will be ignored. This means an unlimited cache size will be assumed. | +| `cache_ignored_headers` | no | `list` | List of header keys from `headers` parameter that should not considered when interacting with the cache. Default is `nil`, meaning all headers will be considered. **Important:** Note that if a cache entry exists with a subset/superset of headers that are considered in this request, it will lead to a cache miss. | +| `raise_error` | no | `bool` | If `raise_error` is set, `http.send` will return an error that can halt policy evaluation when used in conjunction with the `strict-builtin-errors` option. Default: `true`. | +| `max_retry_attempts` | no | `number` | Number of times to retry a HTTP request when a network error is encountered. If provided, retries are performed with an exponential backoff delay. Default: `0`. | + +If the `Host` header is included in `headers`, its value will be used as the `Host` header of the request. The `url` parameter will continue to specify the server to connect to. + +When sending HTTPS requests with client certificates at least one the following combinations must be included + +- `tls_client_cert` and `tls_client_key` +- `tls_client_cert_file` and `tls_client_key_file` +- `tls_client_cert_env_variable` and `tls_client_key_env_variable` + +:::info +To validate TLS server certificates, the user must also provide trusted root CA certificates through the `tls_ca_cert`, `tls_ca_cert_file` and `tls_ca_cert_env_variable` fields. If the `tls_use_system_certs` field is `true`, the system certificate pool will be used as well as any additional CA certificates. +::: + +The `response` object parameter will contain the following fields: + +| Field | Type | Description | +| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `status` | `string` | HTTP status message (e.g., `"200 OK"`). | +| `status_code` | `number` | HTTP status code (e.g., `200`). If `raise_error` is `false`, this field will be set to `0` if `http.send` encounters an error. | +| `body` | `any` | Any value. If the HTTP response message body was not deserialized from JSON or YAML (by force or via the expected Content-Type headers `application/json`; or `application/yaml` or `application/x-yaml`), this field is set to `null`. | +| `raw_body` | `string` | The entire raw HTTP response message body represented as a string. | +| `headers` | `object` | An object containing the response headers. The values will be an array of strings, repeated headers are grouped under the same keys with all values in the array. | +| `error` | `object` | If `raise_error` is `false`, this field will represent the error encountered while running `http.send`. The `error` object contains a `message` key which holds the actual error message and a `code` key which represents if the error was caused due to a network issue or during policy evaluation. | + +By default, an error returned by `http.send` halts the policy evaluation when used in conjunction with the `strict-builtin-errors` option that can be set when running evaluation. +This behaviour can be altered such that instead of halting evaluation, if `http.send` encounters an error, it can return a `response` object with `status_code` +set to `0` and `error` describing the actual error. This can be activated by setting the `raise_error` field +in the `request` object to `false`. Note that if the `strict-builtin-errors` option is not specified and `raise_error` +field is `true` (which is the default), an error returned by `http.send` will generate an undefined result. + +If the `cache` field in the `request` object is `true`, `http.send` will return a cached response after it checks its +freshness and validity. + +`http.send` uses the `Cache-Control` and `Expires` response headers to check the freshness of the cached response. +Specifically if the [max-age](https://tools.ietf.org/html/rfc7234#section-5.2.2.8) `Cache-Control` directive is set, `http.send` +will use it to determine if the cached response is fresh or not. If `max-age` is not set, the `Expires` header will be used instead. + +If the cached response is stale, `http.send` uses the `Etag` and `Last-Modified` response headers to check with the server if the +cached response is in fact still fresh. If the server responds with a `200` (`OK`) response, `http.send` will update the cache +with the new response. On a `304` (`Not Modified`) server response, `http.send` will update the headers in cached response with +their corresponding values in the `304` response. + +The `force_cache` field can be used to override the cache directives defined by the server. This field is used in +conjunction with the `force_cache_duration_seconds` field. If `force_cache` is `true`, then `force_cache_duration_seconds` +**must** be specified and `http.send` will use this value to check the freshness of the cached response. + +Also, if `force_cache` is `true`, it overrides the `cache` field. + +`http.send` only caches responses with the following HTTP status codes: `200`, `203`, `204`, `206`, `300`, `301`, +`404`, `405`, `410`, `414`, and `501`. This is behavior is as per https://www.rfc-editor.org/rfc/rfc7231#section-6.1 and +is enforced when caching responses within a single query or across queries via the `cache` and `force_cache` request fields. + +:::info +`http.send` uses the `Date` response header to calculate the current age of the response by comparing it with the current time. +This value is used to determine the freshness of the cached response. As per https://tools.ietf.org/html/rfc7231#section-7.1.1.2, +an origin server MUST NOT send a `Date` header field if it does not have a clock capable of providing a reasonable +approximation of the current instance in Coordinated Universal Time. Hence, if `http.send` encounters a scenario where current +age of the response is represented as a negative duration, the cached response will be considered as stale. +::: + +The table below shows examples of calling `http.send`: + +| Example | Comments | +| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Accessing Google using System Cert Pool | `http.send({"method": "get", "url": "https://www.google.com", "tls_use_system_certs": true })` | +| Files containing TLS material | `http.send({"method": "get", "url": "https://127.0.0.1:65331", "tls_ca_cert_file": "testdata/ca.pem", "tls_client_cert_file": "testdata/client-cert.pem", "tls_client_key_file": "testdata/client-key.pem"})` | +| Environment variables containing TLS material | `http.send({"method": "get", "url": "https://127.0.0.1:65360", "tls_ca_cert_env_variable": "CLIENT_CA_ENV", "tls_client_cert_env_variable": "CLIENT_CERT_ENV", "tls_client_key_env_variable": "CLIENT_KEY_ENV"})` | +| Unix Socket URL Format | `http.send({"method": "get", "url": "unix://localhost/?socket=%F2path%F2file.socket"})` | diff --git a/third_party/opa/docs/docs/policy-reference/builtins/index.mdx b/third_party/opa/docs/docs/policy-reference/builtins/index.mdx new file mode 100644 index 000000000000..05c3082d5939 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/index.mdx @@ -0,0 +1,11 @@ +--- +title: Rego Built-ins +sidebar_label: Overview +sidebar_position: 0 +--- + +Rego supports a wide range of built-in functions for different policy use cases. Built-ins available in this version of OPA are listed below. + +import BuiltinSearch from "@site/src/components/BuiltinSearch"; + + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/net.mdx b/third_party/opa/docs/docs/policy-reference/builtins/net.mdx new file mode 100644 index 000000000000..3e630e11ac23 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/net.mdx @@ -0,0 +1,36 @@ +--- +title: Networking +--- + + +#### Notes on Name Resolution (`net.lookup_ip_addr`) + +The lookup mechanism uses either the pure-Go, or the cgo-based resolver, depending on the operating system and availability of cgo. +The latter depends on flags that can be provided when building OPA as a Go library, and can be adjusted at runtime via the GODEBUG environment variable. +See [these docs on the `net` package](https://pkg.go.dev/net@go1.17.3#hdr-Name_Resolution) for details. + +Note that the cgo-based resolver is often **preferable**: It will take advantage of host-based DNS caching in place. +This built-in function only caches DNS lookups within _a single_ policy evaluation. + +#### Examples of `net.cidr_contains_matches` + +The `output := net.cidr_contains_matches(a, b)` function allows callers to supply +strings, arrays, sets, or objects for either `a` or `b`. The `output` value in +all cases is a set of tuples (2-element arrays) that identify matches, i.e., +elements of `b` contained by elements of `a`. The first tuple element refers to +the match in `a` and the second tuple element refers to the match in `b`. + +| Input Type | Output Type | +| ---------- | ------------- | +| `string` | `string` | +| `array` | `array` index | +| `set` | `set` element | +| `object` | `object` key | + + + + + + + + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/numbers.mdx b/third_party/opa/docs/docs/policy-reference/builtins/numbers.mdx new file mode 100644 index 000000000000..cd01f81820cd --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/numbers.mdx @@ -0,0 +1,4 @@ +--- +title: Numbers +--- + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/object.mdx b/third_party/opa/docs/docs/policy-reference/builtins/object.mdx new file mode 100644 index 000000000000..8004fd0dbe59 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/object.mdx @@ -0,0 +1,16 @@ +--- +title: Objects +--- + +- When `keys` are provided as an object only the top level keys on the object will be used, values are ignored. + For example: `object.remove({"a": {"b": {"c": 2}}, "x": 123}, {"a": 1}) == {"x": 123}` regardless of the value + for key `a` in the keys object, the following `keys` object gives the same result + `object.remove({"a": {"b": {"c": 2}}, "x": 123}, {"a": {"b": {"foo": "bar"}}}) == {"x": 123}`. +- The `json` string `paths` may reference into array values by using index numbers. For example with the object + `{"a": ["x", "y", "z"]}` the path `a/1` references `y`. Nested structures are supported as well, for example: + `{"a": ["x", {"y": {"y1": {"y2": ["foo", "bar"]}}}, "z"]}` the path `a/1/y1/y2/0` references `"foo"`. +- The `json` string `paths` support `~0`, or `~1` characters for `~` and `/` characters in key names. + It does not support `-` for last index of an array. For example the path `/foo~1bar~0` will reference `baz` + in `{ "foo/bar~": "baz" }`. +- The `json` string `paths` may be an array of string path segments rather than a `/` separated string. For example + the path `a/b/c` can be passed in as `["a", "b", "c"]`. diff --git a/third_party/opa/docs/docs/policy-reference/builtins/opa.mdx b/third_party/opa/docs/docs/policy-reference/builtins/opa.mdx new file mode 100644 index 000000000000..73f793e4d638 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/opa.mdx @@ -0,0 +1,23 @@ +--- +title: OPA +--- + +:::danger +Policies that depend on the output of `opa.runtime` may return different answers depending on how OPA was started. +If possible, prefer using an explicit `input` or `data` value instead of `opa.runtime`. +::: + +### Debugging + +| Built-in | Description | Details | +| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | +| `print(...)` | `print` is used to output the values of variables for debugging purposes. `print` calls have no effect on the result of queries or rules. All variables passed to `print` must be assigned inside of the query or rule. If any of the `print` arguments are undefined, their values are represented as `` in the output stream. Because policies can be invoked via different interfaces (e.g., CLI, HTTP API, etc.) the exact output format differs. See the table below for details. | SDK-dependent | + +| API | Output | Memo | +| --------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `opa eval` | `stderr` | | +| `opa run` (REPL) | `stderr` | | +| `opa test` | `stdout` | Specify `-v` to see output for passing tests. Output for failing tests is displayed automatically. | +| `opa run -s` (server) | `stderr` | Specify `--log-level=info` (default) or higher. Output is sent to the log stream. Use `--log-format=text` for pretty output. | +| Go (library) | `io.Writer` | [https://pkg.go.dev/github.com/open-policy-agent/opa/rego#example-Rego-Print_statements](https://pkg.go.dev/github.com/open-policy-agent/opa/rego#example-Rego-Print_statements) | + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/providers.aws.mdx b/third_party/opa/docs/docs/policy-reference/builtins/providers.aws.mdx new file mode 100644 index 000000000000..528a5fe1e091 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/providers.aws.mdx @@ -0,0 +1,105 @@ +--- +title: AWS +--- + +The AWS Request Signing builtin in OPA implements the header-based auth, +single-chunk method described in the [AWS SigV4 docs](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html). +It will default to signing the payload when present, configurable via `aws_config`, and will sign most user-provided +headers for the request, to ensure their integrity. + +:::info +Note that the `authorization`, `user-agent`, and `x-amzn-trace-id` headers, +are commonly modified by proxy systems, and as such are ignored by OPA +for signing. +::: + +The `request` object parameter may contain any and all of the same fields as for `http.send`. +The following fields will have effects on the output `Authorization` header signature: + +| Field | Required | Type | Description | +| ---------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ | +| `url` | yes | `string` | HTTP URL to specify in the request. Used in the signature. | +| `method` | yes | `string` | HTTP method to specify in request. Used in the signature. | +| `body` | no | `any` | HTTP message body. The JSON serialized version of this value will be used for the payload portion of the signature if present. | +| `raw_body` | no | `string` | HTTP message body. This will be used for the payload portion of the signature if present. | +| `headers` | no | `object` | HTTP headers to include in the request. These will be added to the list of headers to sign. | + +The `aws_config` object parameter may contain the following fields: + +| Field | Required | Type | Description | +| ------------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `aws_access_key` | yes | `string` | AWS access key. | +| `aws_secret_access_key` | yes | `string` | AWS secret access key. Used in generating the signing key for the request. | +| `aws_service` | yes | `string` | AWS service the request will be valid for. (e.g. `"s3"`) | +| `aws_region` | yes | `string` | AWS region for the request. (e.g. `"us-east-1"`) | +| `aws_session_token` | no | `string` | AWS security token. Used for the `x-amz-security-token` request header. | +| `disable_payload_signing` | no | `boolean` | When `true` an `UNSIGNED-PAYLOAD` value will be used for calculating the `x-amz-content-sha256` header during signing, and will be returned in the response. Applicable only for `s3` and `glacier` service. Default: `false`. | + +#### AWS Request Signing Examples + +##### Basic Request Signing Example + +The example below shows using hard-coded AWS credentials for signing the request +object for `http.send`. + +:::info +For deployments, a common way to provide AWS credentials is via environment +variables, usually by using the results of `opa.runtime().env`. +::: + +```rego +req := {"method": "get", "url": "https://examplebucket.s3.amazonaws.com/data"} +aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", +} + +example_verify_resource { + resp := http.send(providers.aws.sign_req(req, aws_config, time.now_ns())) + # process response from AWS ... +} +``` + +##### Unsigned Payload Request Signing Example + +The [AWS S3 request signing API](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html) +supports unsigned payload signing option. This example below shows s3 request signing with payload signing disabled. + +```rego +req := {"method": "get", "url": "https://examplebucket.s3.amazonaws.com/data"} +aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + "disable_payload_signing": true, +} + +example_verify_resource { + resp := http.send(providers.aws.sign_req(req, aws_config, time.now_ns())) + # process response from AWS ... +} +``` + +##### Pre-Signed Request Example + +The [AWS S3 request signing API](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html) +supports pre-signing requests, so that they will only be valid at a future date. +To do this in OPA, simply adjust the time parameter: + +```rego +env := opa.runtime().env +req := {"method": "get", "url": "https://examplebucket.s3.amazonaws.com/data"} +aws_config := { + "aws_access_key": env["AWS_ACCESS_KEY"], + "aws_secret_access_key": env["AWS_SECRET_ACCESS_KEY"], + "aws_service": "s3", + "aws_region": env["AWS_REGION"], +} +# Request will become valid 2 days from now. +signing_time := time.add_date(time.now_ns(), 0, 0, 2) + +pre_signed_req := providers.aws.sign_req(req, aws_config, signing_time)) +``` diff --git a/third_party/opa/docs/docs/policy-reference/builtins/regex.mdx b/third_party/opa/docs/docs/policy-reference/builtins/regex.mdx new file mode 100644 index 000000000000..f98d35110eee --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/regex.mdx @@ -0,0 +1,4 @@ +--- +title: Regular Expressions +--- + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/rego.mdx b/third_party/opa/docs/docs/policy-reference/builtins/rego.mdx new file mode 100644 index 000000000000..71bc418aacf7 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/rego.mdx @@ -0,0 +1,77 @@ +--- +title: Rego +--- + +#### Example + + + +#### Metadata Merge strategies + +When multiple [annotations](../../policy-language/#annotations) are declared along the path ancestry (chain) for a rule, how any given annotation should be selected, inherited or merged depends on the semantics of the annotation, the context of the rule, and the preferences of the developer. +OPA doesn't presume what merge strategy is appropriate; instead, this lies in the hands of the developer. The following example demonstrates how some string and list type annotations in a metadata chain can be merged into a single metadata object. + +```rego +# METADATA +# title: My Example Package +# description: A set of rules illustrating how metadata annotations can be merged. +# authors: +# - John Doe +# organizations: +# - Acme Corp. +package example + +# METADATA +# scope: document +# description: A rule that merges metadata annotations in various ways. + +# METADATA +# title: My Allow Rule +# authors: +# - Jane Doe +allow if { + meta := merge(rego.metadata.chain()) + meta.title == "My Allow Rule" # 'title' pulled from 'rule' scope + meta.description == "A rule that merges metadata annotations in various ways." # 'description' pulled from 'document' scope + meta.authors == { + {"email": "jane@example.com", "name": "Jane Doe"}, # 'authors' joined from 'package' and 'rule' scopes + {"email": "john@example.com", "name": "John Doe"}, + } + meta.organizations == {"Acme Corp."} # 'organizations' pulled from 'package' scope +} + +allow if { + meta := merge(rego.metadata.chain()) + meta.title == null # No 'title' present in 'rule' or 'document' scopes + meta.description == "A rule that merges metadata annotations in various ways." # 'description' pulled from 'document' scope + meta.authors == { # 'authors' pulled from 'package' scope + {"email": "john@example.com", "name": "John Doe"} + } + meta.organizations == {"Acme Corp."} # 'organizations' pulled from 'package' scope +} + +merge(chain) := meta if { + ruleAndDoc := ["rule", "document"] + meta := { + "title": override_annot(chain, "title", ruleAndDoc), # looks for 'title' in 'rule' scope, then 'document' scope + "description": override_annot(chain, "description", ruleAndDoc), # looks for 'description' in 'rule' scope, then 'document' scope + "related_resources": override_annot(chain, "related_resources", ruleAndDoc), # looks for 'related_resources' in 'rule' scope, then 'document' scope + "authors": merge_annot(chain, "authors"), # merges all 'authors' across all scopes + "organizations": merge_annot(chain, "organizations"), # merges all 'organizations' across all scopes + } +} + +override_annot(chain, name, scopes) := val if { + val := [v | + link := chain[_] + link.annotations.scope in scopes + v := link.annotations[name] + ][0] +} else := null + +merge_annot(chain, name) := val if { + val := {v | + v := chain[_].annotations[name][_] + } +} else := null +``` diff --git a/third_party/opa/docs/docs/policy-reference/builtins/semver.mdx b/third_party/opa/docs/docs/policy-reference/builtins/semver.mdx new file mode 100644 index 000000000000..ba19d202c23a --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/semver.mdx @@ -0,0 +1,5 @@ +--- +title: Semantic Version +--- + + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/sets.mdx b/third_party/opa/docs/docs/policy-reference/builtins/sets.mdx new file mode 100644 index 000000000000..31c600af39a8 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/sets.mdx @@ -0,0 +1,4 @@ +--- +title: Sets +--- + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/strings.mdx b/third_party/opa/docs/docs/policy-reference/builtins/strings.mdx new file mode 100644 index 000000000000..831549d1579d --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/strings.mdx @@ -0,0 +1,9 @@ +--- +title: Strings +--- + +:::info +When using `sprintf`, values are pre-processed and may have an unexpected type. For example, +`%T` evaluates to `string` for both `string` and `boolean` types. In such cases, use `type_name` to +accurately evaluate the underlying type. +::: diff --git a/third_party/opa/docs/docs/policy-reference/builtins/time.mdx b/third_party/opa/docs/docs/policy-reference/builtins/time.mdx new file mode 100644 index 000000000000..33024b0edcbe --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/time.mdx @@ -0,0 +1,47 @@ +--- +title: Time +--- + +:::info +Multiple calls to the `time.now_ns` built-in function within a single policy +evaluation query will always return the same value. +::: + +Timezones can be specified as + +- an [IANA Time Zone](https://www.iana.org/time-zones) string e.g. "America/New_York" +- "UTC" or "", which are equivalent to not passing a timezone (i.e. will return as UTC) +- "Local", which will use the local timezone. + +Note that OPA will use the `time/tzdata` data if none is present on the runtime filesystem (see the +[Go `time.LoadLocation()`](https://pkg.go.dev/time#LoadLocation) documentation for more information). + +#### Timestamp Parsing + +OPA can parse timestamps of nearly arbitrary formats, and currently accepts the same inputs as Go's `time.Parse()` utility. +As a result, either you will pass a supported constant, or you **must** describe the format of your timestamps using the Reference Timestamp that Go's `time` module expects: + + 2006-01-02T15:04:05Z07:00 + +In other date formats, that same value is rendered as: + +- January 2, 15:04:05, 2006, in time zone seven hours west of GMT +- Unix time: `1136239445` +- Unix `date` command output: `Mon Jan 2 15:04:05 MST 2006` +- RFC3339 timestamp: `2006-01-02T15:04:05Z07:00` + +Examples of valid values for each timestamp field: + +- Year: `"2006"` `"06"` +- Month: `"Jan"` `"January"` `"01"` `"1"` +- Day of the week: `"Mon"` `"Monday"` +- Day of the month: `"2"` `"_2"` `"02"` +- Day of the year: `"__2"` `"002"` +- Hour: `"15"` `"3"` `"03"` (PM or AM) +- Minute: `"4"` `"04"` +- Second: `"5"` `"05"` +- AM/PM mark: `"PM"` + +For supported constants, formatting of nanoseconds, time zones, and other fields, see the [Go `time/format` module documentation](https://cs.opensource.google/go/go/+/master:src/time/format.go;l=9-113). + + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/tokens.mdx b/third_party/opa/docs/docs/policy-reference/builtins/tokens.mdx new file mode 100644 index 000000000000..f96fb428c10f --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/tokens.mdx @@ -0,0 +1,102 @@ +--- +title: Token Verification +--- + +:::info +Note that the `io.jwt.verify_XX` built-in methods verify **only** the signature. They **do not** provide any validation for the JWT +payload and any claims specified. The `io.jwt.decode_verify` built-in will verify the payload and **all** standard claims. +::: + +The input `string` is a JSON Web Token encoded with JWS Compact Serialization. JWE and JWS JSON Serialization are not supported. If nested signing was used, the `header`, `payload` and `signature` will represent the most deeply nested token. + +For `io.jwt.decode_verify`, `constraints` is an object with the following members: + +| Name | Meaning | Required | +| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | +| `cert` | A PEM encoded certificate, PEM encoded public key, or a JWK key (set) containing an RSA or ECDSA public key. | See below | +| `secret` | The secret key for HS256, HS384 and HS512 verification. | See below | +| `alg` | The JWA algorithm name to use. If it is absent then any algorithm that is compatible with the key is accepted. | Optional | +| `iss` | The issuer string. If it is present the only tokens with this issuer are accepted. If it is absent then any issuer is accepted. | Optional | +| `time` | The time in nanoseconds to verify the token at. If this is present then the `exp` and `nbf` claims are compared against this value. If it is absent then they are compared against the current time. | Optional | +| `aud` | The audience that the verifier identifies with. If this is present then the `aud` claim is checked against it. **If it is absent then the `aud` claim must be absent too.** | Optional | + +Exactly one of `cert` and `secret` must be present. If there are any +unrecognized constraints then the token is considered invalid. + +#### Token Verification Examples + +The examples below use the following token: + +```rego +package jwt + +es256_token := "eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJFUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiaXNzIjogInh4eCJ9.lArczfN-pIL8oUU-7PU83u-zfXougXBZj6drFeKFsPEoVhy9WAyiZlRshYqjTSXdaw8yw2L-ovt4zTUZb2PWMg" +``` + + + +#### Using JWKS + +This example shows a two-step process to verify the token signature and then decode it for +further checks of the payload content. This approach gives more flexibility in verifying only +the claims that the policy needs to enforce. + +```rego +package jwt + +jwks := `{ + "keys": [{ + "kty":"EC", + "crv":"P-256", + "x":"z8J91ghFy5o6f2xZ4g8LsLH7u2wEpT2ntj8loahnlsE", + "y":"7bdeXLH61KrGWRdh7ilnbcGQACxykaPKfmBccTHIOUo" + }] +}` +``` + + + + + + +#### Using PEM encoded X.509 Certificate + +The following examples will demonstrate verifying tokens using an X.509 Certificate +defined as: + +```rego +package jwt + +cert := `-----BEGIN CERTIFICATE----- +MIIBcDCCARagAwIBAgIJAMZmuGSIfvgzMAoGCCqGSM49BAMCMBMxETAPBgNVBAMM +CHdoYXRldmVyMB4XDTE4MDgxMDE0Mjg1NFoXDTE4MDkwOTE0Mjg1NFowEzERMA8G +A1UEAwwId2hhdGV2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATPwn3WCEXL +mjp/bFniDwuwsfu7bASlPae2PyWhqGeWwe23Xlyx+tSqxlkXYe4pZ23BkAAscpGj +yn5gXHExyDlKo1MwUTAdBgNVHQ4EFgQUElRjSoVgKjUqY5AXz2o74cLzzS8wHwYD +VR0jBBgwFoAUElRjSoVgKjUqY5AXz2o74cLzzS8wDwYDVR0TAQH/BAUwAwEB/zAK +BggqhkjOPQQDAgNIADBFAiEA4yQ/88ZrUX68c6kOe9G11u8NUaUzd8pLOtkKhniN +OHoCIHmNX37JOqTcTzGn2u9+c8NlnvZ0uDvsd1BmKPaUmjmm +-----END CERTIFICATE-----` +``` + + + + + + + +#### Round Trip - Sign and Verify + +These examples show how to encode a token, verify, and decode it with the different options available. + + + + + +:::info +Note that the resulting encoded token is different from the first example using +`io.jwt.encode_sign_raw`. The reason is that the `io.jwt.encode_sign` function +is using canonicalized formatting for the header and payload whereas +`io.jwt.encode_sign_raw` does not change the whitespace of the strings passed +in. The decoded and parsed JSON values are still the same. +::: diff --git a/third_party/opa/docs/docs/policy-reference/builtins/tokensign.mdx b/third_party/opa/docs/docs/policy-reference/builtins/tokensign.mdx new file mode 100644 index 000000000000..f9604df4a15e --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/tokensign.mdx @@ -0,0 +1,50 @@ +--- +title: Token Signing +--- + +OPA provides two builtins that implement JSON Web Signature [RFC7515](https://tools.ietf.org/html/rfc7515) functionality. + +`io.jwt.encode_sign_raw()` takes three JSON Objects (strings) as parameters and returns their JWS Compact Serialization. +This builtin should be used by those that want maximum control over the signing and serialization procedure. It is +important to remember that StringOrURI values are compared as case-sensitive strings with no transformations or +canonicalizations applied. Therefore, line breaks and whitespaces are significant. + +`io.jwt.encode_sign()` takes three Rego Objects as parameters and returns their JWS Compact Serialization. This builtin +should be used by those that want to use rego objects for signing during policy evaluation. + +:::info +Note that with `io.jwt.encode_sign` the Rego objects are serialized to JSON with standard formatting applied +whereas the `io.jwt.encode_sign_raw` built-in will **not** affect whitespace of the strings passed in. +This will mean that the final encoded token may have different string values, but the decoded and parsed +JSON will match. +::: + +The following algorithms are supported: + +- `ES256`: ECDSA using P-256 and SHA-256 +- `ES384`: ECDSA using P-384 and SHA-384 +- `ES512`: ECDSA using P-521 and SHA-512 +- `HS256`: HMAC using SHA-256 +- `HS384`: HMAC using SHA-384 +- `HS512`: HMAC using SHA-512 +- `PS256`: RSASSA-PSS using SHA256 and MGF1-SHA256 +- `PS384`: RSASSA-PSS using SHA384 and MGF1-SHA384 +- `PS512`: RSASSA-PSS using SHA512 and MGF1-SHA512 +- `RS256`: RSASSA-PKCS-v1.5 using SHA-256 +- `RS384`: RSASSA-PKCS-v1.5 using SHA-384 +- `RS512`: RSASSA-PKCS-v1.5 using SHA-512 + +:::info +Note that the key's provided should be base64 URL encoded (without padding) as per the specification ([RFC7517](https://tools.ietf.org/html/rfc7517)). +This differs from the plain text secrets provided with the algorithm specific verify built-ins described below. +::: + +#### Token Signing Examples + + + + + + + + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/tracing.mdx b/third_party/opa/docs/docs/policy-reference/builtins/tracing.mdx new file mode 100644 index 000000000000..cdda77d04848 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/tracing.mdx @@ -0,0 +1,4 @@ +--- +title: Tracing +--- + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/types.mdx b/third_party/opa/docs/docs/policy-reference/builtins/types.mdx new file mode 100644 index 000000000000..a17a7c9a4af2 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/types.mdx @@ -0,0 +1,4 @@ +--- +title: Types +--- + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/units.mdx b/third_party/opa/docs/docs/policy-reference/builtins/units.mdx new file mode 100644 index 000000000000..a89df206b9d7 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/units.mdx @@ -0,0 +1,4 @@ +--- +title: Units +--- + diff --git a/third_party/opa/docs/docs/policy-reference/builtins/uuid.mdx b/third_party/opa/docs/docs/policy-reference/builtins/uuid.mdx new file mode 100644 index 000000000000..a9d3e05c1fa1 --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/builtins/uuid.mdx @@ -0,0 +1,4 @@ +--- +title: UUID +--- + diff --git a/third_party/opa/docs/docs/policy-reference/index.md b/third_party/opa/docs/docs/policy-reference/index.md new file mode 100644 index 000000000000..a3168ddf428c --- /dev/null +++ b/third_party/opa/docs/docs/policy-reference/index.md @@ -0,0 +1,432 @@ +--- +title: Policy Reference +sidebar_position: 3 +--- + +import BuiltinLegacyRedirect from "@site/src/components/BuiltinLegacyRedirect"; + + + +## Assignment and Equality + +```rego +# assign variable x to value of field foo.bar.baz in input +x := input.foo.bar.baz + +# check if variable x has same value as variable y +x == y + +# check if variable x is a set containing "foo" and "bar" +x == {"foo", "bar"} + +# OR + +{"foo", "bar"} == x +``` + +## Lookup + +### Arrays + +```rego +# lookup value at index 0 +val := arr[0] + + # check if value at index 0 is "foo" +"foo" == arr[0] + +# find all indices i that have value "foo" +"foo" == arr[i] + +# lookup last value +val := arr[count(arr)-1] + +# with keywords +some 0, val in arr # lookup value at index 0 +0, "foo" in arr # check if value at index 0 is "foo" +some i, "foo" in arr # find all indices i that have value "foo" +``` + +### Objects + +```rego +# lookup value for key "foo" +val := obj["foo"] + +# check if value for key "foo" is "bar" +"bar" == obj["foo"] + +# OR + +"bar" == obj.foo + +# check if key "foo" exists and is not false +obj.foo + +# check if key assigned to variable k exists +k := "foo" +obj[k] + +# check if path foo.bar.baz exists and is not false +obj.foo.bar.baz + +# check if path foo.bar.baz, foo.bar, or foo does not exist or is false +not obj.foo.bar.baz + +# with keywords +o := {"foo": false} +# check if value exists: the expression will be true +false in o +# check if value for key "foo" is false +"foo", false in o +``` + +### Sets + +```rego +# check if "foo" belongs to the set +a_set["foo"] + +# check if "foo" DOES NOT belong to the set +not a_set["foo"] + +# check if the array ["a", "b", "c"] belongs to the set +a_set[["a", "b", "c"]] + +# find all arrays of the form [x, "b", z] in the set +a_set[[x, "b", z]] + +# with keywords +"foo" in a_set +not "foo" in a_set +some ["a", "b", "c"] in a_set +some [x, "b", z] in a_set +``` + +## Iteration + +### Arrays + +```rego +# iterate over indices i +arr[i] + +# iterate over values +val := arr[_] + +# iterate over index/value pairs +val := arr[i] + +# with keywords +some val in arr # iterate over values +some i, _ in arr # iterate over indices +some i, val in arr # iterate over index/value pairs +``` + +### Objects + +```rego +# iterate over keys +obj[key] + +# iterate over values +val := obj[_] + +# iterate over key/value pairs +val := obj[key] + +# with keywords +some val in obj # iterate over values +some key, _ in obj # iterate over keys +some key, val in obj # key/value pairs +``` + +### Sets + +```rego +# iterate over values +set[val] + +# with keywords +some val in set +``` + +### Advanced + +```rego +# nested: find key k whose bar.baz array index i is 7 +foo[k].bar.baz[i] == 7 + +# simultaneous: find keys in objects foo and bar with same value +foo[k1] == bar[k2] + +# simultaneous self: find 2 keys in object foo with same value +foo[k1] == foo[k2]; k1 != k2 + +# multiple conditions: k has same value in both conditions +foo[k].bar.baz[i] == 7; foo[k].qux > 3 +``` + +## For All + +```rego +# assert no values in set match predicate +count({x | set[x]; f(x)}) == 0 + +# assert all values in set make function f true +count({x | set[x]; f(x)}) == count(set) + +# assert no values in set make function f true (using negation and helper rule) +not any_match + +# assert all values in set make function f true (using negation and helper rule) +not any_not_match +``` + +```rego +# with keywords +any_match if { + some x in set + f(x) +} + +any_not_match if { + some x in set + not f(x) +} +``` + +## Rules + +In the examples below `...` represents one or more conditions. + +### Constants + +```rego +a := {1, 2, 3} +b := {4, 5, 6} +c := a | b +``` + +### Conditionals (Boolean) + +```rego +# p is true if ... +p := true { ... } + +# OR +# with keywords +p if { ... } + +# OR +p { ... } +``` + +### Conditionals + +```rego +# with keywords +default a := 1 +a := 5 if { ... } +a := 100 if { ... } +``` + +### Incremental + +```rego +# a_set will contain values of x and values of y +a_set[x] { ... } +a_set[y] { ... } + +# alternatively, with keywords +a_set contains x if { ... } +a_set contains y if { ... } + +# a_map will contain key->value pairs x->y and w->z +a_map[x] := y if { ... } +a_map[w] := z if { ... } +``` + +### Ordered (Else) + +```rego +# with keywords +default a := 1 +a := 5 if { ... } +else := 10 if { ... } +``` + +### Functions (Boolean) + +```rego +# with keywords +f(x, y) if { + ... +} + +# OR + +f(x, y) := true if { + ... +} +``` + +### Functions (Conditionals) + +```rego +# with keywords +f(x) := "A" if { x >= 90 } +f(x) := "B" if { x >= 80; x < 90 } +f(x) := "C" if { x >= 70; x < 80 } +``` + +### Reference Heads + +```rego +# with keywords +fruit.apple.seeds = 12 if input == "apple" # complete document (single value rule) + +fruit.pineapple.colors contains x if x := "yellow" # multi-value rule + +fruit.banana.phone[x] = "bananular" if x := "cellular" # single value rule +fruit.banana.phone.cellular = "bananular" if true # equivalent single value rule + +fruit.orange.color(x) = true if x == "orange" # function +``` + +For reasons of backwards-compatibility, partial sets need to use `contains` in +their rule heads, i.e. + +```rego +fruit.box contains "apples" if true +``` + +whereas + +```rego +fruit.box[x] if { x := "apples" } +``` + +defines a _complete document rule_ `fruit.box.apples` with value `true`. +The same is the case of rules with brackets that don't contain dots, like + +```rego +box[x] if { x := "apples" } # => {"box": {"apples": true }} +box2[x] { x := "apples" } # => {"box": ["apples"]} +``` + +For backwards-compatibility, rules _without_ if and without _dots_ will be interpreted +as defining partial sets, like `box2`. + +## Tests + +```rego +# define a rule that starts with test_ +test_NAME { ... } + +# override input.foo value using the 'with' keyword +data.foo.bar.deny with input.foo as {"bar": [1,2,3]}} +``` + +## Built-in Functions + +Rego's built-in functions offer policy authors tools for common policy +operations like JWT validation, signature verification, among many others. +The reference documentation for these functions can be found under [Built-in Functions](./policy-reference/builtins). + +## Reserved Names + +The following words are reserved and cannot be used as variable names, rule +names, or dot-access style reference arguments: + +``` +as +contains +data +default +else +every +false +if +in +import +input +package +not +null +some +true +with +``` + +## Grammar + +Rego’s syntax is defined by the following grammar: + +```ebnf +module = package { import } policy +package = "package" ref +import = "import" ref [ "as" var ] +policy = { rule } +rule = [ "default" ] rule-head { rule-body } +rule-head = ( ref | var ) ( rule-head-set | rule-head-obj | rule-head-func | rule-head-comp ) +rule-head-comp = [ assign-operator term ] [ "if" ] +rule-head-obj = "[" term "]" [ assign-operator term ] [ "if" ] +rule-head-func = "(" rule-args ")" [ assign-operator term ] [ "if" ] +rule-head-set = "contains" term [ "if" ] | "[" term "]" +rule-args = term { "," term } +rule-body = [ "else" [ assign-operator term ] [ "if" ] ] ( "{" query "}" ) | literal +query = literal { ( ";" | ( [CR] LF ) ) literal } +literal = ( some-decl | expr | "not" expr ) { with-modifier } +with-modifier = "with" term "as" term +some-decl = "some" term { "," term } { "in" expr } +expr = term | expr-call | expr-infix | expr-every | expr-parens | unary-expr +expr-call = var [ "." var ] "(" [ expr { "," expr } ] ")" +expr-infix = expr infix-operator expr +expr-every = "every" var { "," var } "in" ( term | expr-call | expr-infix ) "{" query "}" +expr-parens = "(" expr ")" +unary-expr = "-" expr +membership = term [ "," term ] "in" term +term = ref | var | scalar | array | object | set | membership | array-compr | object-compr | set-compr +array-compr = "[" term "|" query "]" +set-compr = "{" term "|" query "}" +object-compr = "{" object-item "|" query "}" +infix-operator = assign-operator | bool-operator | arith-operator | bin-operator +bool-operator = "==" | "!=" | "<" | ">" | ">=" | "<=" +arith-operator = "+" | "-" | "*" | "/" | "%" +bin-operator = "&" | "|" +assign-operator = ":=" | "=" +ref = ( var | array | object | set | array-compr | object-compr | set-compr | expr-call ) { ref-arg } +ref-arg = ref-arg-dot | ref-arg-brack +ref-arg-brack = "[" ( scalar | var | array | object | set | "_" ) "]" +ref-arg-dot = "." var +var = ( ALPHA | "_" ) { ALPHA | DIGIT | "_" } +scalar = string | NUMBER | TRUE | FALSE | NULL +string = STRING | raw-string +raw-string = "`" { CHAR-"`" } "`" +array = "[" term { "," term } "]" +object = "{" object-item { "," object-item } "}" +object-item = ( scalar | ref | var ) ":" term +set = empty-set | non-empty-set +non-empty-set = "{" term { "," term } "}" +empty-set = "set(" ")" +``` + +The grammar defined above makes use of the following syntax. See [the Wikipedia page on EBNF](https://en.wikipedia.org/wiki/Extended_Backus–Naur_Form) for more details: + +``` +[] optional (zero or one instances) +{} repetition (zero or more instances) +| alternation (one of the instances) +() grouping (order of expansion) +STRING JSON string +NUMBER JSON number +TRUE JSON true +FALSE JSON false +NULL JSON null +CHAR Unicode character +ALPHA ASCII characters A-Z and a-z +DIGIT ASCII characters 0-9 +CR Carriage Return +LF Line Feed +``` diff --git a/third_party/opa/docs/docs/policy-testing.md b/third_party/opa/docs/docs/policy-testing.md new file mode 100644 index 000000000000..fcc883b0ef28 --- /dev/null +++ b/third_party/opa/docs/docs/policy-testing.md @@ -0,0 +1,715 @@ +--- +title: Policy Testing +sidebar_position: 4 +--- + +OPA gives you a high-level declarative language +([Rego](/docs/policy-language)) to author fine-grained policies that +codify important requirements in your system. + +To help you verify the correctness of your policies, OPA also gives you a +framework that you can use to write _tests_ for your policies. By writing +tests for your policies you can speed up the development process of new rules +and reduce the amount of time it takes to modify rules as requirements evolve. + +:::info +The examples in this section try to represent the best practices. As such, they +make use of keywords that are meant to become standard keywords at some point in +time, but have been introduced gradually. +[See the docs on _future keywords_](./policy-language/#future-keywords) for more information. +::: + +## Getting Started + +Let's use an example to get started. The file below implements a simple +policy that allows new users to be created and users to access their own +profile. + +```rego title="example.rego" +package authz + +allow if { + input.path == ["users"] + input.method == "POST" +} + +allow if { + input.path == ["users", input.user_id] + input.method == "GET" +} +``` + +To test this policy, we will create a separate Rego file that contains test cases. + +```rego title="example_test.rego" +package authz_test + +import data.authz + +test_post_allowed if { + authz.allow with input as {"path": ["users"], "method": "POST"} +} + +test_get_anonymous_denied if { + not authz.allow with input as {"path": ["users"], "method": "GET"} +} + +test_get_user_allowed if { + authz.allow with input as {"path": ["users", "bob"], "method": "GET", "user_id": "bob"} +} + +test_get_another_user_denied if { + not authz.allow with input as {"path": ["users", "bob"], "method": "GET", "user_id": "alice"} +} +``` + +Both of these files are saved in the same directory. + +```console +$ ls +example.rego example_test.rego +``` + +To exercise the policy, run the `opa test` command in the directory containing the files. + +```console +$ opa test . -v +data.authz_test.test_post_allowed: PASS (1.417µs) +data.authz_test.test_get_anonymous_denied: PASS (426ns) +data.authz_test.test_get_user_allowed: PASS (367ns) +data.authz_test.test_get_another_user_denied: PASS (320ns) +-------------------------------------------------------------------------------- +PASS: 4/4 +``` + +The `opa test` output indicates that all of the tests passed. + +Try exercising the tests a bit more by removing the first rule in **example.rego**. + +```console +$ opa test . -v +FAILURES +-------------------------------------------------------------------------------- +data.authz_test.test_post_allowed: FAIL (277.306µs) + + query:1 Enter data.authz_test.test_post_allowed = _ + example_test.rego:3 | Enter data.authz_test.test_post_allowed + example_test.rego:4 | | Fail data.authz_test.allow with input as {"method": "POST", "path": ["users"]} + query:1 | Fail data.authz_test.test_post_allowed = _ + +SUMMARY +-------------------------------------------------------------------------------- +data.authz_test.test_post_allowed: FAIL (277.306µs) +data.authz_test.test_get_anonymous_denied: PASS (124.287µs) +data.authz_test.test_get_user_allowed: PASS (242.2µs) +data.authz_test.test_get_another_user_denied: PASS (131.964µs) +-------------------------------------------------------------------------------- +PASS: 3/4 +FAIL: 1/4 +``` + +## Enriched Test Report With Variable Values + +Sometimes, e.g. when testing rules with complex output, it can be useful to know more about the circumstances that caused a certain expression to fail a test. +The `--var-values` flag can be used to enrich the test report with the exact expression that caused a test rule to fail, including the values of any variables or references used in the expression. + +Consider the following utility module: + +```rego title="authz.rego" +package authz + +allowed_actions(user) := [action | + user in data.actions[action] +] +``` + +with accompanying tests: + +```rego title="authz_test.rego" +package authz_test + +import data.authz + +test_allowed_actions_all_can_read if { + users := ["alice", "bob", "jane"] + r := ["alice", "bob"] + w := ["jane"] + p := {"read": r, "write": w} + + every user in users { + "read" in authz.allowed_actions(user) with data.actions as p + } +} +``` + +Exercising the tests with the `--var-values` flag: + +```console +opa test . --var-values +FAILURES +-------------------------------------------------------------------------------- +data.authz_test.test_allowed_actions_all_can_read: FAIL (904µs) + + util_test.rego:13: + "read" in authz.allowed_actions(user) with data.actions as p + | | | + | | {"read": ["alice", "bob"], "write": ["jane"]} + | "jane" + ["write"] + +SUMMARY +-------------------------------------------------------------------------------- +util_test.rego: +data.authz_test.test_allowed_actions_all_can_read: FAIL (904µs) +-------------------------------------------------------------------------------- +FAIL: 1/1 +``` + +The test failed because it expected users with **write** permission to implicitly also have the **read** permission, an expectation the function under test didn't meet. +By including the failing expression and its local variable assignments in the test report, we make troubleshooting easier for the developer, as it's immediately apparent what assertion and combination of parameters caused the test to fail. + +## Test Format + +Tests are expressed as standard Rego rules with a convention that the rule +name is prefixed with `test_`. It's a good practice for tests to be placed in a package suffixed with `_test`, but not a requirement. + +```rego +package mypackage_test + +import data.mypackage + +test_some_descriptive_name if { + # test logic +} +``` + +## Test Discovery + +The `opa test` subcommand runs all of the tests (i.e., rules prefixed with +`test_`) found in Rego files passed on the command line. If directories are +passed as command line arguments, `opa test` will load their file contents +recursively. + +## Specifying Tests to Run + +The `opa test` subcommand supports a `--run`/`-r` regex option to further +specify which of the discovered tests should be evaluated. The option supports +[re2 syntax](https://github.com/google/re2/wiki/Syntax) + +## Test Results + +If the test rule is undefined or generates a non-`true` value the test result +is reported as `FAIL`. If the test encounters a runtime error (e.g., a divide +by zero condition) the test result is marked as an `ERROR`. Tests prefixed with +`todo_` will be reported as `SKIPPED`. Otherwise, the test result is marked as +`PASS`. + +**pass_fail_error_test.rego**: + +```rego +package example_test + +import data.example + +# This test will pass. +test_ok if true + +# This test will fail. +test_failure if 1 == 2 + +# This test will error. +test_error if 1 / 0 + +# This test will be skipped. +todo_test_missing_implementation if { + example.allow with data.roles as ["not", "implemented"] +} +``` + +By default, `opa test` reports the number of tests executed and displays all +of the tests that failed or errored. + +```console +$ opa test pass_fail_error_test.rego +data.example_test.test_failure: FAIL (253ns) +data.example_test.test_error: ERROR (289ns) + pass_fail_error_test.rego:15: eval_builtin_error: div: divide by zero +-------------------------------------------------------------------------------- +PASS: 1/3 +FAIL: 1/3 +ERROR: 1/3 +``` + +By default, OPA prints the test results in a human-readable format. If you +need to consume the test results programmatically, use the JSON output format. + +```bash +opa test --format=json pass_fail_error_test.rego +``` + +```json +[ + { + "location": { + "file": "pass_fail_error_test.rego", + "row": 4, + "col": 1 + }, + "package": "data.example_test", + "name": "test_ok", + "duration": 618515 + }, + { + "location": { + "file": "pass_fail_error_test.rego", + "row": 9, + "col": 1 + }, + "package": "data.example_test", + "name": "test_failure", + "fail": true, + "duration": 322177 + }, + { + "location": { + "file": "pass_fail_error_test.rego", + "row": 14, + "col": 1 + }, + "package": "data.example_test", + "name": "test_error", + "error": { + "code": "eval_internal_error", + "message": "div: divide by zero", + "location": { + "file": "pass_fail_error_test.rego", + "row": 15, + "col": 5 + } + }, + "duration": 345148 + } +] +``` + +## Parameterized Tests and Data-driven Testing + +A test rule can define multiple test cases for evaluation. +Test cases are declared by adding their name(s) to the rule as variables in its head's reference, and are evaluated through regular enumeration. + +**example_test.rego**: + +```rego +package example_test + +test_concat[note] if { + some note, tc in { + "empty + empty": { + "a": [], + "b": [], + "exp": [], + }, + "empty + filled": { + "a": [], + "b": [1, 2], + "exp": [1, 2], + }, + "filled + filled": { + "a": [1, 2], + "b": [3, 4], + "exp": [1, 2, 3], # Faulty expectation, this test case will fail + }, + } + + act := array.concat(tc.a, tc.b) + act == tc.exp +} +``` + +```console +$ opa test example_test.rego +example_test.rego: +data.example_test.test_concat: FAIL (263.375µs) + empty + empty: PASS + empty + filled: PASS + filled + filled: FAIL +-------------------------------------------------------------------------------- +FAIL: 1/1 +``` + +Just as in regular evaluation, test-case data doesn't need to be declared as inline Rego, but can be loaded from json and yaml data files: + +**file_example_test.rego**: + +```rego +package example_test + +import data.test_cases + +test_concat[note] if { + some note, tc in test_cases + + act := array.concat(tc.a, tc.b) + act == tc.exp +} +``` + +**file_example_test.yaml**: + +```yaml +test_cases: + empty + empty: + a: [] + b: [] + exp: [] + empty + filled: + a: [] + b: [1, 2] + exp: [1, 2] + filled + filled: + a: [1, 2] + b: [3, 4] + exp: [1, 2, 3] # Faulty expectation, this test case will fail +``` + +```console +$ opa test file_example_test.rego file_example_test.yaml +file_example_test.rego: +data.example_test.test_concat: FAIL (280µs) + empty + empty: PASS + empty + filled: PASS + filled + filled: FAIL +-------------------------------------------------------------------------------- +FAIL: 1/1 +``` + +Test cases can be nested by declaring multiple test case name variables in the head reference. +This is useful when e.g. the same set of test cases can be used for asserting the same behaviour across slightly different circumstances: + +**nested_example_test.rego**: + +```rego +package example_test + +test_sign_token[note][alg] if { + some note, tc in { + "claims": { + "claims": {"foo": "bar"}, + }, + "no claims": { + "claims": {}, + }, + } + + some alg in [ + "HS256", + "HS333", # unknown signing algorithm, this test case will fail + "HS512", + ] + + secret := "foobar" + key := base64.encode(secret) + + token := io.jwt.encode_sign({ + "typ": "JWT", + "alg": alg + }, tc.claims, { + "kty": "oct", + "k": key + }) + + [valid, _, payload] := io.jwt.decode_verify(token, {"secret": secret}) + valid + payload = tc.claims +} +``` + +```console +$ opa test nested_example_test.rego +nested_example_test.rego: +data.example_test.test_sign_token: FAIL (1.214541ms) + claims: FAIL + HS256: PASS + HS333: FAIL + HS512: PASS + no claims: FAIL + HS256: PASS + HS333: FAIL + HS512: PASS +-------------------------------------------------------------------------------- +FAIL: 1/1 +``` + +## Data and Function Mocking + +OPA's `with` keyword can be used to replace the data document or called functions with mocks. +Both base and virtual documents can be replaced. + +When replacing functions, built-in or otherwise, the following constraints are in place: + +1. Replacing `internal.*` functions, or `rego.metadata.*`, or `eq`; or relations (`walk`) is not allowed. +2. Replacement and replaced function need to have the same arity. +3. Replaced functions can call the functions they're replacing, and those calls + will call out to the original function, and not cause recursion. + +Below is a simple policy that depends on the data document. + +```rego title="authz.rego" +package authz + +allow if { + some x in data.policies + x.name == "test_policy" + matches_role(input.role) +} + +matches_role(my_role) if input.user in data.roles[my_role] +``` + +Below is the Rego file to test the above policy. + +```rego title="authz_test.rego" +package authz_test + +import data.authz + +policies := [{"name": "test_policy"}] +roles := {"admin": ["alice"]} + +test_allow_with_data if { + authz.allow with input as {"user": "alice", "role": "admin"} + with data.policies as policies + with data.roles as roles +} +``` + +To exercise the policy, run the `opa test` command. + +```console +$ opa test -v authz.rego authz_test.rego +data.authz_test.test_allow_with_data: PASS (697ns) +-------------------------------------------------------------------------------- +PASS: 1/1 +``` + +Below is an example to replace a **rule without arguments**. + +**authz.rego**: + +```rego +package authz + +allow1 if allow2 + +allow2 if 2 == 1 +``` + +**authz_test.rego**: + +```rego +package authz_test + +import data.authz + +test_replace_rule if { + authz.allow1 with authz.allow2 as true +} +``` + +```console +$ opa test -v authz.rego authz_test.rego +data.authz_test.test_replace_rule: PASS (328ns) +-------------------------------------------------------------------------------- +PASS: 1/1 +``` + +Here is an example to replace a rule's **built-in function** with a user-defined function. + +**authz.rego**: + +```rego +package authz + +import data.jwks.cert + +allow if { + [true, _, _] = io.jwt.decode_verify(input.headers["x-token"], {"cert": cert, "iss": "corp.issuer.com"}) +} +``` + +**authz_test.rego**: + +```rego +package authz_test + +import data.authz + +mock_decode_verify("my-jwt", _) := [true, {}, {}] +mock_decode_verify(x, _) := [false, {}, {}] if x != "my-jwt" + +test_allow if { + authz.allow with input.headers["x-token"] as "my-jwt" + with data.jwks.cert as "mock-cert" + with io.jwt.decode_verify as mock_decode_verify +} +``` + +```console +$ opa test -v authz.rego authz_test.rego +data.authz_test.test_allow: PASS (458.752µs) +-------------------------------------------------------------------------------- +PASS: 1/1 +``` + +In simple cases, a function can also be replaced with a value, as in + +```rego +test_allow_value if { + authz.allow + with input.headers["x-token"] as "my-jwt" + with data.jwks.cert as "mock-cert" + with io.jwt.decode_verify as [true, {}, {}] +} +``` + +Every invocation of the function will then return the replacement value, regardless +of the function's arguments. + +Note that it's also possible to replace one built-in function by another; or a non-built-in +function by a built-in function. + +**authz.rego**: + +```rego +package authz + +replace_rule if { + replace(input.label) +} + +replace(label) if { + label == "test_label" +} +``` + +**authz_test.rego**: + +```rego +package authz_test + +import data.authz + +test_replace_rule if { + authz.replace_rule with input.label as "does-not-matter" with replace as true +} +``` + +```console +$ opa test -v authz.rego authz_test.rego +data.authz_test.test_replace_rule: PASS (648.314µs) +-------------------------------------------------------------------------------- +PASS: 1/1 +``` + +## Coverage + +In addition to reporting pass, fail, and error results for tests, `opa test` +can also report _coverage_ for the policies under test. + +The coverage report includes all of the lines evaluated and not evaluated in +the Rego files provided on the command line. When a line is not covered it +indicates one of two things: + +- If the line refers to the head of a rule, the body of the rule was never true. +- If the line refers to an expression in a rule, the expression was never evaluated. + +It is also possible that [rule indexing](./policy-performance/#use-indexed-statements) +has determined some path unnecessary for evaluation, thereby affecting the lines +reported as covered. + +If we run the coverage report on the original **example.rego** file without +`test_get_user_allowed` from **example_test**.rego the report will indicate +that line 8 is not covered. + +```bash +opa test --coverage --format=json example.rego example_test.rego +``` + +```json +{ + "files": { + "example.rego": { + "covered": [ + { + "start": { + "row": 3 + }, + "end": { + "row": 5 + } + }, + { + "start": { + "row": 9 + }, + "end": { + "row": 11 + } + } + ], + "not_covered": [ + { + "start": { + "row": 8 + }, + "end": { + "row": 8 + } + } + ], + "covered_lines": 6, + "not_covered_lines": 1, + "coverage": 85.7 + }, + "example_test.rego": { + "covered": [ + { + "start": { + "row": 3 + }, + "end": { + "row": 4 + } + }, + { + "start": { + "row": 7 + }, + "end": { + "row": 8 + } + }, + { + "start": { + "row": 11 + }, + "end": { + "row": 12 + } + } + ], + "covered_lines": 6, + "coverage": 100 + }, + "covered_lines": 12, + "not_covered_lines": 1, + "coverage": 92.3 + } +} +``` + +## Ecosystem Projects + + +Here are some projects that can help you with policy testing: + diff --git a/third_party/opa/docs/docs/privacy.md b/third_party/opa/docs/docs/privacy.md new file mode 100644 index 000000000000..ad2bd2b6e96c --- /dev/null +++ b/third_party/opa/docs/docs/privacy.md @@ -0,0 +1,79 @@ +--- +title: Privacy +--- + +This document provides details about OPA's anonymous information reporting feature. + +## Overview + +OPA periodically reports its version and specific anonymous runtime statistics to a publicly hosted, external service. +The reports contain the OPA version number (e.g., v0.12.3), a randomly generated UUID and the following runtime statistics: + +- heap usage in bytes + +This feature is only applicable to the `opa run` and `opa version` commands. + +In case of the `opa run` command, this feature is **ON by-default** and can be easily disabled by specifying +the `--disable-telemetry` flag. When OPA is started in either the server or repl mode, OPA calls the external service +on a best-effort basis and shares the version it's running and other statistics such as current memory usage. +The time taken to execute the remote call and process the subsequent response from the external service does not +delay OPA's start-up. + +In case of the `opa version` command, this feature can be enabled by specifying the `--check` or `-c` flag. + +## External Service + +OPA uploads its information by default at [telemetry.openpolicyagent.org](https://telemetry.openpolicyagent.org). +The environment variable `OPA_TELEMETRY_SERVICE_URL` can be used to configure the external service OPA reports to. + +Sample HTTP request from OPA to the external service looks like this: + +```http +POST /v1/version HTTP/1.1 +Host: telemetry.openpolicyagent.org +Content-Type: application/json +User-Agent: "Open Policy Agent/v0.12.3 (darwin, amd64)" +``` + +```json +{ + "id": "08c1d850-6065-478a-b9b5-a8f9f464ad33", + "version": "v0.12.3", + "heap_usage_bytes": "596000" +} +``` + +The _id_ field in the request body above is a version 4 random UUID generated when OPA starts. + +The external service checks the OPA version reported by a remote OPA client and responds with information about the +latest OPA release. This information includes a link to download the latest OPA version, release notes etc. + +Sample response from the external service looks like this: + +```json +{ + "latest": { + "download": "https://openpolicyagent.org/downloads/v0.19.2/opa_darwin_amd64", + "release_notes": "https://github.com/open-policy-agent/opa/releases/tag/v0.19.2", + "latest_release": "v0.19.2" + } +} +``` + +The external service response contains a link to download the latest released OPA binary for client's platform, and a link +to the OPA release notes. + +## Benefits + +- OPA's anonymous version reporting feature provides users with up-to-date information about new OPA versions while + still executing the familiar OPA `run` and `version` commands. It helps users stay abreast of OPA's latest capabilities + and hence empowers them to make informed decisions while upgrading their OPA deployments. + +- OPA maintainers and the [Cloud Native Computing Foundation](https://cncf.io) (CNCF) executive staff can use the version + reports for obtaining more information about OPA usage and engagement. For example, the information can be used in + making better decisions about OPA's deprecation cycle. + +- Reporting a running OPA's memory usage can help to better understand how much memory an OPA instance is consuming and + thereby drive optimization efforts around better resource utilization. Some users have concerns around OPA's memory usage + and hence this information can help OPA maintainers quantify the number of impacted OPA deployments and also guide future + features and priorities for the project. diff --git a/third_party/opa/docs/docs/rest-api.md b/third_party/opa/docs/docs/rest-api.md new file mode 100644 index 000000000000..360efaf771ca --- /dev/null +++ b/third_party/opa/docs/docs/rest-api.md @@ -0,0 +1,2216 @@ +--- +title: REST API Reference +sidebar_position: 10 +--- + +This document is the authoritative specification of the OPA REST API. The API can be broken down into the following +groups: + +- [Policy API](#policy-api) - manage policy loaded into the OPA instance. +- [Data API](#data-api) - evaluate rules and retrieve data. +- [Query API](#query-api) - execute adhoc queries. +- [Compile API](#compile-api) - access Rego's [Partial Evaluation](https://blog.openpolicyagent.org/partial-evaluation-162750eaf422) functionality. +- [Health API](#health-api) - access instance operational health information. +- [Config API](#config-api) - view instance configuration. +- [Status API](#status-api) - view instance [status](./management-status) state. + +The REST API is a common way to integrate with OPA. + +You may also want to review the [integration documentation](./integration) for other options +to build on OPA by embedding functionality directly into your application. + +:::info +Integrating with OPA from a programming language? You might find it easier to build your +OPA integration using one of the [language SDKs](/ecosystem/#languages) than working +with the REST API directly. +::: + +## Common Request Headers + +The following request headers are commonly used in some API endpoints: + +#### Content-Type + +It indicates the request body format. These are some values used in some APIs: + +- `application/json` for JSON encoded content, e.g. a JSON document +- `application/yaml` for YAML encoded content, e.g. a YAML document +- `text/plain` for plain text content, e.g. a policy + +#### Accept-Encoding + +It could have `gzip` value which indicates the server should respond with a gzip encoded body. The server will send the compressed response only if its length is above `server.encoding.gzip.min_length` value. See the [configuration section](./configuration/#server). + +#### Content-Encoding + +It could have `gzip` value which indicates the request body is a gzip encoded object. + +## Policy API + +The Policy API exposes CRUD endpoints for managing policy modules. Policy modules can be added, removed, and modified at any time. + +The identifiers given to policy modules are only used for management purposes. They are not used outside the Policy API. + +### List Policies + +```http +GET /v1/policies HTTP/1.1 +``` + +List policy modules. + +#### Status Codes + +- **200** - no error +- **500** - server error + +#### Example Request + +```http +GET /v1/policies HTTP/1.1 +``` + +#### Example Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "result": [ + { + "id": "example2", + "raw": "package opa.examples\n\nimport data.servers\n\nviolations[server] {\n\tserver = servers[_]\n\tserver.protocols[_] = \"http\"\n\tpublic_servers[server]\n}\n", + "ast": { + "package": { + "path": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "opa" + }, + { + "type": "string", + "value": "examples" + } + ] + }, + "rules": [ + { + "head": { + "name": "violations", + "key": { + "type": "var", + "value": "server" + } + }, + "body": [ + { + "index": 0, + "terms": [ + { + "type": "string", + "value": "eq" + }, + { + "type": "var", + "value": "server" + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "servers" + }, + { + "type": "var", + "value": "$0" + } + ] + } + ] + }, + { + "index": 1, + "terms": [ + { + "type": "string", + "value": "eq" + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "server" + }, + { + "type": "string", + "value": "protocols" + }, + { + "type": "var", + "value": "$1" + } + ] + }, + { + "type": "string", + "value": "http" + } + ] + }, + { + "index": 2, + "terms": { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "opa" + }, + { + "type": "string", + "value": "examples" + }, + { + "type": "string", + "value": "public_servers" + }, + { + "type": "var", + "value": "server" + } + ] + } + } + ] + } + ] + } + }, + { + "id": "example1", + "raw": "package opa.examples\n\nimport data.servers\nimport data.networks\nimport data.ports\n\npublic_servers[server] {\n\tserver = servers[_]\n\tserver.ports[_] = ports[k].id\n\tports[k].networks[_] = networks[m].id\n\tnetworks[m].public = true\n}\n", + "ast": { + "package": { + "path": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "opa" + }, + { + "type": "string", + "value": "examples" + } + ] + }, + "rules": [ + { + "head": { + "name": "public_servers", + "key": { + "type": "var", + "value": "server" + } + }, + "body": [ + { + "index": 0, + "terms": [ + { + "type": "string", + "value": "eq" + }, + { + "type": "var", + "value": "server" + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "servers" + }, + { + "type": "var", + "value": "$0" + } + ] + } + ] + }, + { + "index": 1, + "terms": [ + { + "type": "string", + "value": "eq" + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "server" + }, + { + "type": "string", + "value": "ports" + }, + { + "type": "var", + "value": "$1" + } + ] + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "ports" + }, + { + "type": "var", + "value": "k" + }, + { + "type": "string", + "value": "id" + } + ] + } + ] + }, + { + "index": 2, + "terms": [ + { + "type": "string", + "value": "eq" + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "ports" + }, + { + "type": "var", + "value": "k" + }, + { + "type": "string", + "value": "networks" + }, + { + "type": "var", + "value": "$2" + } + ] + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "networks" + }, + { + "type": "var", + "value": "m" + }, + { + "type": "string", + "value": "id" + } + ] + } + ] + }, + { + "index": 3, + "terms": [ + { + "type": "string", + "value": "eq" + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "networks" + }, + { + "type": "var", + "value": "m" + }, + { + "type": "string", + "value": "public" + } + ] + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + ] + } + } + ] +} +``` + +### Get a Policy + +``` +GET /v1/policies/ +``` + +Get a policy module. + +#### Query Parameters + +- **pretty** - If parameter is `true`, response will be formatted for humans. + +#### Status Codes + +- **200** - no error +- **404** - not found +- **500** - server error + +#### Example Request + +```http +GET /v1/policies/example1 HTTP/1.1 +``` + +#### Example Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "result": { + "id": "example1", + "raw": "package opa.examples\n\nimport data.servers\nimport data.networks\nimport data.ports\n\npublic_servers[server] {\n\tserver = servers[_]\n\tserver.ports[_] = ports[k].id\n\tports[k].networks[_] = networks[m].id\n\tnetworks[m].public = true\n}\n", + "ast": { + "package": { + "path": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "opa" + }, + { + "type": "string", + "value": "examples" + } + ] + }, + "rules": [ + { + "head": { + "name": "public_servers", + "key": { + "type": "var", + "value": "server" + } + }, + "body": [ + { + "index": 0, + "terms": [ + { + "type": "string", + "value": "eq" + }, + { + "type": "var", + "value": "server" + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "servers" + }, + { + "type": "var", + "value": "$0" + } + ] + } + ] + }, + { + "index": 1, + "terms": [ + { + "type": "string", + "value": "eq" + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "server" + }, + { + "type": "string", + "value": "ports" + }, + { + "type": "var", + "value": "$1" + } + ] + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "ports" + }, + { + "type": "var", + "value": "k" + }, + { + "type": "string", + "value": "id" + } + ] + } + ] + }, + { + "index": 2, + "terms": [ + { + "type": "string", + "value": "eq" + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "ports" + }, + { + "type": "var", + "value": "k" + }, + { + "type": "string", + "value": "networks" + }, + { + "type": "var", + "value": "$2" + } + ] + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "networks" + }, + { + "type": "var", + "value": "m" + }, + { + "type": "string", + "value": "id" + } + ] + } + ] + }, + { + "index": 3, + "terms": [ + { + "type": "string", + "value": "eq" + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "networks" + }, + { + "type": "var", + "value": "m" + }, + { + "type": "string", + "value": "public" + } + ] + }, + { + "type": "boolean", + "value": true + } + ] + } + ] + } + ] + } + } +} +``` + +### Create or Update a Policy + +``` +PUT /v1/policies/ +Content-Type: text/plain +``` + +Create or update a policy module. + +If the policy module does not exist, it is created. If the policy module already exists, it is replaced. + +#### Request Headers + +- **[Content-Type](#content-type)**: `text/plain` + +#### Query Parameters + +- **pretty** - If parameter is `true`, response will be formatted for humans. +- **metrics** - Return compiler performance metrics in addition to result. See [Performance Metrics](#performance-metrics) for more detail. + +#### Status Codes + +- **200** - no error +- **400** - bad request +- **500** - server error + +Before accepting the request, the server will parse, compile, and install the policy module. If the policy module is invalid, one of these steps will fail and the server will respond with 400. The error message in the response will be set to indicate the source of the error. + +#### Example Request + +```http +PUT /v1/policies/example1 HTTP/1.1 +Content-Type: text/plain +``` + +```rego +package opa.examples + +import data.networks +import data.ports +import data.servers + +public_servers contains server if { + some k, m + server := servers[_] + server.ports[_] == ports[k].id + ports[k].networks[_] == networks[m].id + networks[m].public == true +} +``` + +> cURL's `-d/--data` flag removes newline characters from input files. Use the `--data-binary` flag instead. + +#### Example Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{} +``` + +### Delete a Policy + +``` +DELETE /v1/policies/ +``` + +Delete a policy module. + +#### Query Parameters + +- **pretty** - If parameter is `true`, response will be formatted for humans. +- **metrics** - Return compiler performance metrics in addition to result. See [Performance Metrics](#performance-metrics) for more detail. + +#### Status Codes + +- **200** - no error +- **400** - bad request +- **404** - not found +- **500** - server error + +If other policy modules in the same package depend on rules in the policy module to be deleted, the server will return 400. + +#### Example Request + +```http +DELETE /v1/policies/example2 HTTP/1.1 +``` + +#### Example Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{} +``` + +## Data API + +The Data API exposes endpoints for reading and writing documents in OPA. For an explanation to the different types of documents in OPA see [How Does OPA Work?](./philosophy#how-does-opa-work) + +### Get a Document + +``` +GET /v1/data/{path:.+} +``` + +Get a document. + +The path separator is used to access values inside object and array documents. The server attempts to convert path segments to integers. If a path element cannot be converted to an integer, the server will use its string representation. + +#### Request Headers + +- **[Accept-Encoding](#accept-encoding)**: `gzip` + +#### Query Parameters + +- **input** - Provide an input document. Format is a JSON value that will be used as the value for the input document. +- **pretty** - If parameter is `true`, response will be formatted for humans. +- **provenance** - If parameter is `true`, response will include build/version info in addition to the result. See [Provenance](#provenance) for more detail. +- **explain** - Return query explanation in addition to result. Values: **notes**, **fails**, **full**, **debug**. +- **metrics** - Return query performance metrics in addition to result. See [Performance Metrics](#performance-metrics) for more detail. +- **instrument** - Instrument query evaluation and return a superset of performance metrics in addition to result. See [Performance Metrics](#performance-metrics) for more detail. +- **strict-builtin-errors** - Treat built-in function call errors as fatal and return an error immediately. + +#### Status Codes + +- **200** - no error +- **400** - bad request +- **500** - server error + +The server returns 400 if the input document is invalid (i.e. malformed JSON). + +The server returns 200 if the path refers to an undefined document. In this +case, the response will not contain a `result` property. + +#### Response Message + +- **result** - The base or virtual document referred to by the URL path. If the + path is undefined, this key will be omitted. +- **metrics** - If query metrics are enabled, this field contains query + performance metrics collected during the parse, compile, and evaluation steps. + +* **decision_id** - If decision logging is enabled, this field contains a string + that uniquely identifies the decision. The identifier will be included in the + decision log event for this decision. Callers can use the identifier for + correlation purposes. + +#### Example Request + +```http +GET /v1/data/opa/examples/public_servers HTTP/1.1 +``` + +#### Example Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "result": [ + { + "id": "s1", + "name": "app", + "ports": [ + "p1", + "p2", + "p3" + ], + "protocols": [ + "https", + "ssh" + ] + }, + { + "id": "s4", + "name": "dev", + "ports": [ + "p1", + "p2" + ], + "protocols": [ + "http" + ] + } + ] +} +``` + +### Get a Document (with Input) + +``` +POST /v1/data/{path:.+} +Content-Type: application/json +``` + +```json +{ + "input": ... +} +``` + +Get a document that requires input. + +The path separator is used to access values inside object and array documents. The server attempts to convert path segments to integers. If a path element cannot be converted to an integer, the server will use its string representation. + +The request body contains an object that specifies a value for [The input Document](./philosophy/#the-opa-document-model). + +#### Request Headers + +- **[Content-Type](#content-type)**: `application/json` or `application/yaml` +- **[Content-Encoding](#content-encoding)**: `gzip` +- **[Accept-Encoding](#accept-encoding)**: `gzip` + +#### Query Parameters + +- **pretty** - If parameter is `true`, response will be formatted for humans. +- **provenance** - If parameter is `true`, response will include build/version info in addition to the result. See [Provenance](#provenance) for more detail. +- **explain** - Return query explanation in addition to result. Values: **notes**, **fails**, **full**, **debug**. +- **metrics** - Return query performance metrics in addition to result. See [Performance Metrics](#performance-metrics) for more detail. +- **instrument** - Instrument query evaluation and return a superset of performance metrics in addition to result. See [Performance Metrics](#performance-metrics) for more detail. +- **strict-builtin-errors** - Treat built-in function call errors as fatal and return an error immediately. + +#### Status Codes + +- **200** - no error +- **400** - bad request +- **500** - server error + +The server returns 400 if the input document is invalid (i.e. malformed JSON). + +The server returns 200 if the path refers to an undefined document. In this +case, the response will not contain a `result` property. + +#### Response Message + +- **result** - The base or virtual document referred to by the URL path. If the + path is undefined, this key will be omitted. +- **metrics** - If query metrics are enabled, this field contains query + performance metrics collected during the parse, compile, and evaluation steps. + +* **decision_id** - If decision logging is enabled, this field contains a string + that uniquely identifies the decision. The identifier will be included in the + decision log event for this decision. Callers can use the identifier for + correlation purposes. + +The examples below assume the following policy: + +```rego +package opa.examples + +import input.example.flag + +allow_request if flag == true +``` + +#### Example Request + +```http +POST /v1/data/opa/examples/allow_request HTTP/1.1 +Content-Type: application/json +``` + +```json +{ + "input": { + "example": { + "flag": true + } + } +} +``` + +#### Example Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "result": true +} +``` + +#### Example Request + +```http +POST /v1/data/opa/examples/allow_request HTTP/1.1 +Content-Type: application/json +``` + +```json +{ + "input": { + "example": { + "flag": false + } + } +} +``` + +#### Example Response + +```http +HTTP/1.1 200 OK +``` + +```json +{} +``` + +### Get a Document (Webhook) + +``` +POST /v0/data/{path:.+} +Content-Type: application/json +``` + +Get a document from a webhook. + +Use this API if you are enforcing policy decisions via webhooks that have pre-defined +request/response formats. Note, the API path prefix is `/v0` instead of `/v1`. + +The request message body defines the content of the [input document](./philosophy/#the-opa-document-model). The request message body +may be empty. The path separator is used to access values inside object and +array documents. + +#### Request Headers + +- **[Content-Type](#content-type)**: `application/json` or `application/yaml` +- **[Content-Encoding](#content-encoding)**: `gzip` +- **[Accept-Encoding](#accept-encoding)**: `gzip` + +#### Query Parameters + +- **pretty** - If parameter is `true`, response will be formatted for humans. + +#### Status Codes + +- **200** - no error +- **400** - bad request +- **404** - not found +- **500** - server error + +If the requested document is missing or undefined, the server will return 404 and the message body will contain an error object. + +The examples below assume the following policy: + +```rego +package opa.examples + +import input.example.flag + +allow_request if flag == true +``` + +#### Example Request + +```http +POST /v0/data/opa/examples/allow_request HTTP/1.1 +Content-Type: application/json +``` + +```json +{ + "example": { + "flag": true + } +} +``` + +#### Example Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +true +``` + +### Create or Overwrite a Document + +``` +PUT /v1/data/{path:.+} +Content-Type: application/json +``` + +Create or overwrite a document. + +If the path does not refer to an existing document, the server will attempt to create all the necessary containing documents. This behavior is similar in principle to the Unix command `mkdir -p`. + +#### Request Headers + +- **[Content-Type](#content-type)**: `application/json` +- **If-None-Match**: `*` - the server will not overwrite an existing document located at the path. + +#### Query Parameters + +- **metrics** - Return performance metrics in addition to result. See [Performance Metrics](#performance-metrics) for more detail. + +#### Status Codes + +- **204** - no content (success) +- **304** - not modified +- **400** - bad request +- **404** - write conflict +- **500** - server error + +If the path refers to a virtual document or a conflicting base document the server will respond with 404. A base document conflict will occur if the parent portion of the path refers to a non-object document. + +#### Example Request To Initialize Document With If-None-Match + +```http +PUT /v1/data/us-west/servers HTTP/1.1 +Content-Type: application/json +If-None-Match: * +``` + +```json +{} +``` + +#### Example Response If Document Already Exists + +```http +HTTP/1.1 304 Not Modified +``` + +#### Example Response If Document Does Not Exist + +```http +HTTP/1.1 204 No Content +``` + +### Patch a Document + +``` +PATCH /v1/data/{path:.+} +Content-Type: application/json-patch+json +``` + +Update a document. + +The server accepts updates encoded as JSON Patch operations. The message body of the request should contain a JSON encoded array containing one or more JSON Patch operations. Each operation specifies the operation type, path, and an optional value. For more information on JSON Patch, see [RFC 6902](https://tools.ietf.org/html/rfc6902). + +The effective path of the JSON Patch operation is obtained by joining the path portion of the URL with the path value from the operation(s) contained in the message body. In all cases, the parent of the effective path MUST refer to an existing document, otherwise the server returns 404. In the case of **remove** and **replace** operations, the effective path MUST refer to an existing document, otherwise the server returns 404. + +#### Request Headers + +- **[Content-Type](#content-type)**: `application/json-patch+json` + +#### Status Codes + +- **204** - no content (success) +- **400** - bad request +- **404** - not found +- **500** - server error + +#### Example Request + +```http +PATCH /v1/data/servers HTTP/1.1 +Content-Type: application/json-patch+json +``` + +```json +[ + { + "op": "add", + "path": "-", + "value": { + "id": "s5", + "name": "job", + "protocols": ["amqp"], + "ports": ["p3"] + } + } +] +``` + +#### Example Response + +```http +HTTP/1.1 204 No Content +``` + +### Delete a Document + +``` +DELETE /v1/data/{path:.+} +``` + +Delete a document. + +The server processes the DELETE method as if the client had sent a PATCH request containing a single remove operation. + +#### Query Parameters + +- **metrics** - Return performance metrics in addition to result. See [Performance Metrics](#performance-metrics) for more detail. + +#### Status Codes + +- **204** - no content (success) +- **404** - not found +- **500** - server error + +If the path refers to a non-existent document, the server returns 404. + +#### Example Request + +```http +DELETE /v1/data/servers HTTP/1.1 +``` + +#### Example Response + +```http +HTTP/1.1 204 No Content +``` + +## Query API + +### Execute a Simple Query + +``` +POST / +Content-Type: application/json +``` + +Execute a simple query. + +OPA serves POST requests without a URL path by querying for the document at +path `/data/system/main` by default. The content of that document defines the response +entirely. This default can be overridden by the `default_decision` configuration. See the [Configuration Reference](./configuration/#miscellaneous) +for more information. + +#### Request Headers + +- **[Content-Type](#content-type)**: `application/json` or `application/yaml` + +#### Query Parameters + +- **pretty** - If parameter is `true`, response will be formatted for humans. + +#### Status Codes + +- **200** - no error +- **400** - bad request +- **404** - not found +- **500** - server error + +If the default decision (defaulting to `/system/main`) is undefined, the server returns 404. + +The policy example below shows how to define a rule that will +produce a value for the `/data/system/main` document. You can configure OPA +to use a different URL path to serve these queries. See the [Configuration Reference](./configuration) +for more information. + +The request message body is mapped to the [Input Document](./philosophy/#the-opa-document-model). + +```http +PUT /v1/policies/example1 HTTP/1.1 +Content-Type: text/plain +``` + +```rego +package system + +main := msg if { + msg := sprintf("hello, %v", [input.user]) +} +``` + +#### Example Request + +```http +POST / +Content-Type: application/json +``` + +```json +{ + "user": ["alice"] +} +``` + +#### Example Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +"hello, alice" +``` + +### Execute an Ad-hoc Query + +Execute an ad-hoc query and return bindings for variables found in the query. + +``` +GET /v1/query +``` + +#### Query Parameters + +- **q** - The ad-hoc query to execute. OPA will parse, compile, and execute the query represented by the parameter value. The value MUST be URL encoded. Only used in GET method. For POST method the query is sent as part of the request body and this parameter is not used. +- **pretty** - If parameter is `true`, response will be formatted for humans. +- **explain** - Return query explanation in addition to result. Values: **notes**, **fails**, **full**, **debug**. +- **metrics** - Return query performance metrics in addition to result. See [Performance Metrics](#performance-metrics) for more detail. + +#### Status Codes + +- **200** - no error +- **400** - bad request +- **500** - server error +- **501** - streaming not implemented + +For queries that have large JSON values it is recommended to use the `POST` method with: + +- the query included as the request body +- [Content-Type](#content-type): `application/json` request header + +``` +POST /v1/query HTTP/1.1 +Content-Type: application/json +``` + +```json +{ + "query": "input.servers[i].ports[_] = \"p2\"; input.servers[i].name = name", + "input": { + "servers": [ ... ], + } +} +``` + +#### Example Request + +``` +GET /v1/query?q=data.servers[i].ports[_] = "p2"; data.servers[i].name = name HTTP/1.1 +``` + +#### Example Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "result": [ + { + "i": 3, + "name": "dev" + }, + { + "i": 0, + "name": "app" + } + ] +} +``` + +## Compile API + +### Partially Evaluate a Query + +```http +POST /v1/compile +Content-Type: application/json +``` + +Partially evaluate a query. + +The [Compile API](#compile-api) allows you to partially evaluate Rego queries +and obtain a simplified version of the policy. This is most useful when building +integrations where policy logic is to be translated and evaluated in another +environment. For example, +[this post](https://blog.openpolicyagent.org/write-policy-in-opa-enforce-policy-in-sql-d9d24db93bf4) +on the OPA blog shows how SQL can be generated based on Compile API output. +For more details on Partial Evaluation in OPA, please refer to +[this blog post](https://blog.openpolicyagent.org/partial-evaluation-162750eaf422). + +Note that nondeterminstic builtins (like `http.send`) are _not evaluated_ during PE. +You can change that by providing `nondeterminsticBuiltins: true` in your payload options. +This would be desirable when using PE for generating filters using extra information +from `http.send`. + +#### Request Body + +Compile API requests contain the following fields: + +| Field | Type | Required | Description | +| ---------- | --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| `query` | `string` | Yes | The query to partially evaluate and compile. | +| `input` | `any` | No | The input document to use during partial evaluation (default: undefined). | +| `options` | `object[string, any]` | No | Additional options to use during partial evaluation: `disableInlining` (default: undefined) and `nondeterminsticBuiltins` (default: false). | +| `unknowns` | `array[string]` | No | The terms to treat as unknown during partial evaluation (default: `["input"]`]). | + +#### Request Headers + +- **[Content-Type](#content-type)**: `application/json` +- **[Content-Encoding](#content-encoding)**: `gzip` +- **[Accept-Encoding](#accept-encoding)**: `gzip` + +#### Query Parameters + +- **pretty** - If parameter is `true`, response will be formatted for humans. +- **explain** - Return query explanation in addition to result. Values: **notes**, **fails**, **full**, **debug**. +- **metrics** - Return query performance metrics in addition to result. See [Performance Metrics](#performance-metrics) for more detail. +- **instrument** - Instrument query evaluation and return a superset of performance metrics in addition to result. See [Performance Metrics](#performance-metrics) for more detail. + +#### Status Codes + +- **200** - no error +- **400** - bad request +- **500** - server error + +The example below assumes that OPA has been given the following policy: + +```rego +package example + +allow if { + input.subject.clearance_level >= data.reports[_].clearance_level +} +``` + +#### Example Request + +```http +POST /v1/compile HTTP/1.1 +Content-Type: application/json +``` + +```json +{ + "query": "data.example.allow == true", + "input": { + "subject": { + "clearance_level": 4 + } + }, + "options": { + "disableInlining": [] + }, + "unknowns": [ + "data.reports" + ] +} +``` + +#### Example Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "result": { + "queries": [ + [ + { + "index": 0, + "terms": [ + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "gte" + } + ] + }, + { + "type": "number", + "value": 4 + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "reports" + }, + { + "type": "var", + "value": "i1" + }, + { + "type": "string", + "value": "clearance_level" + } + ] + } + ] + } + ] + ] + } +} +``` + +#### Unconditional Results from Partial Evaluation + +When you partially evaluate a query with the Compile API, OPA returns a new set of queries and supporting policies. However, in some cases, the result of Partial Evaluation is a conclusive, unconditional answer. + +For example, if you extend the policy above to include a "break glass" condition, the decision may be to allow all requests regardless of clearance level. + +```rego +package example + +allow if { + input.subject.clearance_level >= data.reports[_].clearance_level +} + +allow if { + data.break_glass = true +} +``` + +In this case, if `data.break_glass` is `true` then the query +`data.example.allow == true` will _always_ be true. If the query is +always true, the `"queries"` value in the result will contain an empty +array. The empty array indicates that your query can be satisfied +without any further evaluation. + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "result": { + "queries": [ + [], + [ + { + "index": 0, + "terms": [ + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "gte" + } + ] + }, + { + "type": "number", + "value": 4 + }, + { + "type": "ref", + "value": [ + { + "type": "var", + "value": "data" + }, + { + "type": "string", + "value": "reports" + }, + { + "type": "var", + "value": "$02" + }, + { + "type": "string", + "value": "clearance_level" + } + ] + } + ] + } + ] + ] + } +} +``` + +It is also possible for queries to _never_ be true. For example, the +original policy could be extended to require that users be granted an +exception: + +```rego +package example + +allow if { + input.subject.clearance_level >= data.reports[_].clearance_level + exceptions[input.subject.name] +} + +exceptions contains "bob" +exceptions contains "alice" +``` + +In this case, if we execute query on behalf of a user that does not +have an exception (e.g., `"eve"`), the OPA response will not contain a +`queries` field at all. This indicates there are NO conditions that +could make the query true. + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "result": {} +} +``` + +The following table summarizes the behavior for partial evaluation results. + +| Example Query | Unknowns | Result | Description | +| ------------- | -------------- | ------------------------------------------ | ----------------------------------------------------------------------- | +| `input.x > 0` | `["input"]` | `{"result": {"queries": [[input.x > 0]]}}` | The query is partially evaluated and remaining conditions are returned. | +| `input.x > 0` | Not specified. | `{"result": {"queries": [[input.x > 0]]}}` | If the set of unknowns is not specified, it defaults to `["input"]`. | +| `input.x > 0` | `[]` | `{"result": {}}` | The query is false/undefined because there are no unknowns. | +| `1 > 0` | N/A | `{"result": {"queries": [[]]}}` | The query is always true. | +| `1 < 0` | N/A | `{"result": {}}` | The query is always false. | + +> The partially evaluated queries are represented as strings in the table above. The actual API response contains the JSON AST representation. + +## Health API + +The `/health` API endpoint executes a simple built-in policy query to verify +that the server is operational. Optionally it can account for bundle activation as well +(useful for "ready" checks at startup). + +#### Query Parameters + +- `bundles` - Boolean parameter to account for bundle activation status in response. This includes any discovery bundles or bundles defined in the loaded discovery configuration. +- `plugins` - Boolean parameter to account for plugin status in response. +- `exclude-plugin` - String parameter to exclude a plugin from status checks. Can be added multiple times. Does nothing if `plugins` is not true. This parameter is useful for special use cases where a plugin depends on the server being fully initialized before it can fully initialize itself. + +#### Status Codes + +- **200** - OPA service is healthy. If the `bundles` option is specified then all configured bundles have + been activated. If the `plugins` option is specified then all plugins are in an OK state. +- **500** - OPA service is not healthy. If the `bundles` option is specified this can mean any of the configured + bundles have not yet been activated. If the `plugins` option is specified then at least one + plugin is in a non-OK state. + +:::info +The bundle activation check is only for initial bundle activation. Subsequent +downloads will not affect the health check. The [Status](./management-status) +API should be used for more fine-grained bundle status monitoring. +::: + +#### Example Request + +```http +GET /health HTTP/1.1 +``` + +#### Example Request (bundle activation) + +```http +GET /health?bundles HTTP/1.1 +``` + +#### Example Request (plugin status) + +```http +GET /health?plugins HTTP/1.1 +``` + +#### Example Request (plugin status with exclude) + +```http +GET /health?plugins&exclude-plugin=decision-logs&exclude-plugin=status HTTP/1.1 +``` + +#### Healthy Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{} +``` + +#### Unhealthy Response + +```http +HTTP/1.1 500 Internal Server Error +Content-Type: application/json +``` + +```json +{ + "error": "not all plugins in OK state" +} +``` + +Other error messages include: + +- `"unable to perform evaluation"` +- `"not all configured bundles have been activated"` + +### Custom Health Checks + +The Health API includes support for "all or nothing" checks that verify +configured bundles have activated and plugins are operational. In some cases, +health checks may need to perform fine-grained checks on plugin state or other +internal components. To support these cases, use the policy-based Health API. + +By convention, the `/health/live` and `/health/ready` API endpoints allow you to +use Rego to evaluate the current state of the server and its plugins to +determine "liveness" (when OPA is capable of receiving traffic) and "readiness" +(when OPA is ready to receive traffic). Policy for the `live` and `ready` rules +is defined under package `system.health`. + +> The "liveness" and "readiness" check convention comes from +> [Kubernetes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) +> but they are just conventions. You can implement your own check endpoints +> under the `system.health` package as needed. Any rules implemented inside +> `system.health` will be exposed at `/health/`. + +#### Policy Examples + +Here is a basic health policy for liveness and readiness. In this example, OPA is live once it is +able to process the `live` rule. OPA is ready once all plugins have entered the OK state at least once. + +```rego +package system.health + +# opa is live if it can process this rule +default live := true + +# by default, opa is not ready +default ready := false + +# opa is ready once all plugins have reported OK at least once +ready if { + input.plugins_ready +} +``` + +Note that once `input.plugins_ready` is true, it stays true. If you want to fail the ready check when +specific a plugin leaves the OK state, try this: + +```rego +package system.health + +default live := true + +default ready := false + +# opa is ready once all plugins have reported OK at least once AND +# the bundle plugin is currently in an OK state +ready if { + input.plugins_ready + input.plugin_state.bundle == "OK" +} +``` + +See the following section for all the inputs available to use in health policy. + +#### Policy Inputs + +- `input.plugins_ready`: Will be false until all registered plugins have started + and are reporting an `OK` state, at which point it will be true. Once true, it will stay true + until the process ends. +- `input.plugin_state.`: Shows the current state of a plugin, where `` + is replaced with the name of the plugin, e.g. `bundle`, `status`. + +#### Status Codes + +- **200** - OPA service is healthy. +- **500** - OPA service is not healthy because policy has not evaluated to true, or is missing. + +#### Example Requests + +```http +GET /health/ready HTTP/1.1 +``` + +```http +GET /health/live HTTP/1.1 +``` + +#### Healthy Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{} +``` + +#### Unhealthy Response + +```http +HTTP/1.1 500 Internal Server Error +Content-Type: application/json +``` + +```json +{ + "error": "health policy was not true at data.system.health." +} +``` + +Other error messages include: + +- `"health policy was undefined at data.system.health."` + +## Config API + +The `/config` API endpoint returns OPA's active configuration. When the discovery feature is enabled, this API can be +used to fetch the discovered configuration in the last evaluated discovery bundle. The `credentials` field in the +[Services](./configuration#services) configuration and the `private_key` and `key` fields in the [Keys](./configuration#keys) +configuration will be omitted from the API response. + +### Get Config + +``` +GET /v1/config HTTP/1.1 +``` + +#### Query Parameters + +- **pretty** - If parameter is `true`, response will be formatted for humans. + +#### Status Codes + +- **200** - no error +- **500** - server error + +#### Example Request + +```http +GET /v1/config HTTP/1.1 +``` + +#### Example Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "result": { + "services": { + "acmecorp": { + "url": "https://example.com/control-plane-api/v1" + } + }, + "labels": { + "id": "test-id", + "version": "0.27.0" + }, + "keys": { + "global_key": { + "scope": "read" + } + }, + "decision_logs": { + "service": "acmecorp" + }, + "status": { + "service": "acmecorp" + }, + "bundles": { + "authz": { + "service": "acmecorp" + } + }, + "default_authorization_decision": "/system/authz/allow", + "default_decision": "/system/main" + } +} +``` + +## Status API + +The `/status` endpoint exposes a pull-based API for accessing OPA +[Status](./management-status) information. Normally this information is pushed +by OPA to a remote service via HTTP, console, or custom plugins. However, in +some cases, callers may wish to poll OPA and fetch the information. + +### Get Status + +``` +GET /v1/status HTTP/1.1 +``` + +#### Query Parameters + +- **pretty** - If parameter is `true`, response will be formatted for humans. + +#### Status Codes + +- **200** - no error +- **500** - server error + +#### Example Request + +```http +GET /v1/status HTTP/1.1 +``` + +#### Example Response + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "result": { + "labels": { + "id": "7da62ac6-42e0-4b3c-b6d5-199239ad436e", + "version": "99.9.9-dev" + }, + "bundles": { + "play": { + "name": "play", + "active_revision": "b3BlbnBvbGljeWFnZW50Lm9yZw==", + "last_successful_activation": "2021-12-08T01:36:14.201927Z", + "last_successful_download": "2021-12-08T01:36:14.20038Z", + "last_successful_request": "2021-12-08T01:36:23.131346Z", + "last_request": "2021-12-08T01:36:23.131346Z", + "metrics": { + "timer_bundle_request_ns": 168273779 + } + } + }, + "metrics": { + "prometheus": { +<------------------8<------------------> + } + }, + "plugins": { + "bundle": { + "state": "OK" + }, + "decision_logs": { + "state": "OK" + }, + "discovery": { + "state": "OK" + }, + "status": { + "state": "OK" + } + } + } +} +``` + +## Authentication + +The API is secured via [HTTPS, Authentication, and Authorization](./security). + +### Bearer Tokens + +When OPA is started with the `--authentication=token` command line flag, +clients MUST provide a Bearer token in the HTTP Authorization header: + +```http +GET /v1/data/exempli-gratia HTTP/1.1 +Authorization: Bearer my-secret-token +``` + +Bearer tokens must be represented with a valid HTTP header value character +sequence. + +OPA will extract the Bearer token value (which is set to `my-secret-token` +above) and provide it to the authorization component inside OPA that will (i) +validate the token and (ii) execute the authorization policy configured by the +admin. + +## Errors + +All the API endpoints use standard HTTP status codes to indicate success or +failure of an API call. If an API call fails, the response will contain a JSON +encoded object that provides more detail. The `errors` and `location` fields are +optional: + +``` +{ + "code": "invalid_parameter", + "message": "error(s) occurred while compiling module(s)", + "errors": [ + { + "code": "rego_unsafe_var_error", + "message": "var x is unsafe", + "location": { + "file": "example", + "row": 3, + "col": 1 + } + } + ] +} +``` + +### Method not Allowed + +OPA will respond with a 405 Error (Method Not Allowed) if the method used to +access the URL is not supported. For example, if a client uses the _HEAD_ method +to access any path within `/v1/data/{path:.*}`, a 405 will be returned. + +## Explanations + +OPA supports query explanations that describe (in detail) the steps taken to +produce query results. + +Explanations can be requested for: + +- [Data API](#data-api) GET queries +- [Query API](#query-api) queries + +Explanations are requested by setting the `explain` query parameter to one of +the following values: + +- **off** - do not return any trace. +- **full** - returns a full query trace containing every step in the query evaluation process. +- **debug** - returns a full query trace including debug info. +- **notes** - returns only note events and their context. +- **fails** - returns only fail events and their context. + +By default, explanations are represented in a machine-friendly format. Set the +`pretty` parameter to request a human-friendly format for debugging purposes. + +### Trace Events + +When the `explain` query parameter is set to anything except `off`, the response contains an array of Trace Event objects. + +Trace Event objects contain the following fields: + +- **op** - identifies the kind of Trace Event. Values: **"Enter"**, **"Exit"**, **"Eval"**, **"Fail"**, **"Redo"**. +- **query_id** - uniquely identifies the query that the Trace Event was emitted for. +- **parent_id** - identifies the parent query. +- **type** - indicates the type of the **node** field. Values: **"expr"**, **"rule"**, **"body"**. +- **node** - contains the AST element associated with the evaluation step. +- **locals** - contains the term bindings from the query at the time when the Trace Event was emitted. + +#### Query IDs + +Queries often reference rules or contain comprehensions. In both cases, query +evaluation involves evaluation of one or more other queries, e.g., the body of +the rule or comprehension. + +Trace Events from different queries can be distinguished by the **query_id** +field. + +Trace Events from related queries can be identified by the **parent_id** field. + +For example, if query A references a rule R, Trace Events emitted as part of +evaluating rule R's body will have the **parent_id** field set to query A's +**query_id**. + +#### Types of Events + +Each Trace Event represents a step in the query evaluation process. Trace Events +are emitted at the following points: + +- **enter** - before a body or rule is evaluated. +- **exit** - after a body or rule has evaluated successfully. +- **eval** - before an expression is evaluated. +- **fail** - after an expression has evaluated to false. +- **redo** - before evaluation restarts from a body, rule, or expression. + +By default, OPA searches for all sets of term bindings that make all expressions +in the query evaluate to true. Because there may be multiple answers, the search +can _restart_ when OPA determines the query is true or false. When the search +restarts, a **Redo** Trace Event is emitted. + +#### Example Trace Event + +```json +{ + "op": "eval", + "query_id": 20, + "parent_id": 0, + "type": "expr", + "node": { + "index": 1, + "terms": [ + { + "type": "var", + "value": "eq" + }, + { + "type": "var", + "value": "x" + }, + { + "type": "var", + "value": "y" + } + ] + }, + "locals": [ + { + "key": { + "type": "var", + "value": "x" + }, + "value": { + "type": "string", + "value": "hello" + } + } + ] +} +``` + +## Performance Metrics + +OPA can report detailed performance metrics at runtime. Performance metrics can +be requested on individual API calls and are returned inline with the API +response. To enable performance metric collection on an API call, specify the +`metrics=true` query parameter when executing the API call. Performance metrics +are currently supported for the following APIs: + +- Policy API (PUT and DELETE) +- Data API (GET, POST, PUT, and DELETE) +- Query API (all methods) +- Compile API (POST) + +For example: + +```http +POST /v1/data/example?metrics=true HTTP/1.1 +``` + +Response: + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "metrics": { + "timer_rego_query_compile_ns": 69994, + "timer_rego_query_eval_ns": 48425, + "timer_rego_query_parse_ns": 4096 + }, + "result": { + "some_strings": [ + "hello", + "world" + ] + } +} +``` + +OPA currently supports the following query performance metrics: + +- **timer_rego_input_parse_ns**: time taken (in nanoseconds) to parse the input +- **timer_rego_query_parse_ns**: time taken (in nanoseconds) to parse the query. +- **timer_rego_query_compile_ns**: time taken (in nanoseconds) to compile the query. +- **timer_rego_query_eval_ns**: time taken (in nanoseconds) to evaluate the query. +- **timer_rego_module_parse_ns**: time taken (in nanoseconds) to parse the input policy module. +- **timer_rego_module_compile_ns**: time taken (in nanoseconds) to compile the loaded policy modules. +- **timer_server_handler_ns**: time take (in nanoseconds) to handle the API request. +- **counter_server_query_cache_hit**: number of cache hits for the query. + +The `counter_server_query_cache_hit` counter gives an indication about whether OPA creates a new Rego query +or it uses a pre-processed query which holds some prepared state to serve the API request. A pre-processed query will be +faster to evaluate since OPA will not have to re-parse or compile it. Hence, when the query is served from the cache +`timer_rego_query_parse_ns` and `timer_rego_query_compile_ns` timers will be omitted from the reported performance metrics. + +OPA also supports query instrumentation. To enable query instrumentation, +specify the `instrument=true` query parameter when executing the API call. +Query instrumentation can help diagnose performance problems, however, it can +add significant overhead to query evaluation. We recommend leaving query +instrumentation off unless you are debugging a performance problem. + +When instrumentation is enabled there are several additional performance metrics +for the compilation stages. They follow the format of `timer_compile_stage_*_ns` +and `timer_query_compile_stage_*_ns` for the query and module compilation stages. + +## Provenance + +OPA can report provenance information at runtime. Provenance information can +be requested on individual API calls and are returned inline with the API +response. To obtain provenance information on an API call, specify the +`provenance=true` query parameter when executing the API call. Provenance information +is currently supported for the following APIs: + +- Data API (GET and POST) + +For example: + +```http +POST /v1/data/example?provenance=true HTTP/1.1 +``` + +Response: + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "provenance": { + "build_commit": "1955fc4d", + "build_host": "foo.com", + "build_timestamp": "2019-04-29T23:42:04Z", + "bundles": { + "authz": { + "revision": "ID-b1298a6c-6ad8-11e9-a26f-d38b5ceadad5" + } + }, + "version": "0.10.8-dev" + }, + "result": true +} +``` + +OPA currently supports the following query provenance information: + +- **version**: The version of this OPA instance. +- **build_commit**: The git commit id of this OPA build. +- **build_timestamp**: The timestamp when this instance was built. +- **build_host**: The hostname where this instance was built. +- **revision**: (Deprecated) The _revision_ string included in a .manifest file (if present) within + a bundle. Omitted when `bundles` are configured. +- **bundles**: A set of key-value pairs describing each bundle activated on the server. Includes + the `revision` field which is the _revision_ string included in a .manifest file (if present) + within a bundle + +## Ecosystem Projects + + +OPA's REST API has already been used by many projects in the OPA Ecosystem to support a variety of use cases. + diff --git a/third_party/opa/docs/docs/security.md b/third_party/opa/docs/docs/security.md new file mode 100644 index 000000000000..9ed8c4f03449 --- /dev/null +++ b/third_party/opa/docs/docs/security.md @@ -0,0 +1,654 @@ +--- +title: Security +--- + +This document provides guidelines for deploying OPA inside untrusted +environments. You should read this document if you are deploying OPA as a +service. + +Securing the API involves configuring OPA to use TLS, authentication, and +authorization so that: + +- Traffic between OPA and clients is encrypted. +- Clients verify the OPA API endpoint identity. +- OPA verifies client identities. +- Clients are only granted access to specific APIs or sections of [The `data` Document](./philosophy/#the-opa-document-model). + +## TLS and HTTPS + +HTTPS is configured by specifying TLS credentials via command line flags at +startup: + +- `--tls-cert-file=` specifies the path of the file containing the TLS certificate. +- `--tls-private-key-file=` specifies the path of the file containing the TLS private key. + +OPA will exit immediately with a non-zero status code if only one of these flags +is specified. + +The server can track the certificate and key files' contents, and reload them if necessary: + +- `--tls-cert-refresh-period=` specifies how often OPA should check the TLS certificate and + private key file for changes (defaults to 0s, disabling periodic refresh). This argument accepts + any duration, such as "30s", "5m" or "24h". + +Note that for using TLS-based authentication, a CA cert file can be provided: + +- `--tls-ca-cert-file=` specifies the path of the file containing the CA cert. + +If provided, it will be used to validate clients' TLS certificates when using TLS +authentication (see below). + +By default, OPA ignores insecure HTTP connections when TLS is enabled. To allow +insecure HTTP connections in addition to HTTPS connections, provide another +listening address with `--addr`. For example: + +```bash +opa run --server \ + --log-level debug \ + --tls-cert-file public.crt \ + --tls-private-key-file private.key \ + --addr https://0.0.0.0:8181 \ + --addr http://localhost:8282 +``` + +### 1. Generate the TLS credentials for OPA (Example) + +```bash +openssl genrsa -out private.key 2048 +openssl req -new -x509 -sha256 -key private.key -out public.crt -days 1 +``` + +> We have generated a self-signed certificate for example purposes here. DO NOT +> rely on self-signed certificates outside of development without understanding +> the risks. + +### 2. Start OPA with TLS enabled + +```bash +opa run --server --log-level debug \ + --tls-cert-file public.crt \ + --tls-private-key-file private.key +``` + +### 3. Try to access the API with HTTP + +```bash +curl http://localhost:8181/v1/data +``` + +### 4. Access the API with HTTPS + +```bash +curl -k https://localhost:8181/v1/data +``` + +:::info +We have to use cURL's `-k/--insecure` flag because we are using a self-signed certificate. +::: + +## Interface Binding + +OPA can be configured to listen on specific interfaces using the `--addr` flag. For example: + +```bash +opa run --server \ + --log-level debug \ + --addr 0.0.0.0:8181 \ +``` + +By default, OPA binds to `localhost`, which prevents the OPA server from being exposed to services running outside of the same machine. + +In situations where OPA is not intended to be exposed to remote services, it is recommended to bind OPA to the localhost interface, which only allows connections from the same machine. If it is necessary to expose OPA to remote services, ensure to follow the security recommendations on this page, such as requiring authentication. + +## Authentication and Authorization + +This section shows how to configure OPA to authenticate and authorize client +requests. Client-side authentication of the OPA API endpoint should be handled +with TLS. + +Authentication and authorization allow OPA to: + +- Verify client identities. +- Control client access to APIs and data. + +Both are configured via command line flags: + +- `--authentication=` specifies the authentication scheme to use. +- `--authorization=` specifies the authorization scheme to use. + +By default, OPA does not perform authentication or authorization and these flags +default to `off`. + +For authentication, OPA supports: + +- [Bearer tokens](./rest-api#bearer-tokens): Bearer tokens are enabled by + starting OPA with `--authentication=token`. When the `token` authentication + mode is enabled, OPA will extract the Bearer token from incoming API requests + and provide to the authorization handler. When you use the `token` + authentication, you must configure an authorization policy that checks the + tokens. If the client does not supply a Bearer token, the `input.identity` + value will be undefined when the authorization policy is evaluated. +- Client TLS certificates: Client TLS authentication is enabled by starting + OPA with `--authentication=tls`. When this authentication mode is enabled, + OPA will require all clients to provide a client certificate. It is verified + against the CA certificate(s) provided via `--tls-ca-cert-file`. Upon successful + verification, the `input.identity` value is set to the TLS certificate's + subject. + + Note that TLS authentication does not disable non-HTTPS listeners. To ensure + that all your communication is secured, it should be paired with an + authorization policy (see below) that at least requires the client identity + (`input.identity`) to _be set_. + +For authorization, OPA relies on policy written in Rego. Authorization is +enabled by starting OPA with `--authorization=basic`. + +When the `basic` authorization scheme is enabled, a minimal authorization policy +must be provided on startup. The authorization policy must be structured as follows: + +```rego +# The "system" namespace is reserved for internal use +# by OPA. Authorization policy must be defined under +# system.authz as follows: +package system.authz + +default allow := false # Reject requests by default. + +allow if { + # Logic to authorize request goes here. +} +``` + +When OPA receives a request, it executes a query against the document defined +`data.system.authz.allow`. The implementation of the policy may span multiple +packages however it is recommended that administrators keep the policy under the +`system` namespace. + +If the document produced by the `allow` rule is `true`, the request is +processed normally. If the document is undefined or **not** `true`, the +request is rejected immediately. The count of requests rejected by an OPA instance +are surfaced via the performance metrics in the [Status](./management-status) information. + +OPA provides the following `input` document when executing the authorization +policy. Since the schema for the `input` document is known to OPA, it performs automatic type checking of this document +and reports any errors resulting from the schema check. The `--skip-known-schema-check` flag can be passed to `opa run` +to disable automatic type checking of this `input` document. + + + +```jsonc +{ + # Identity value established by authentication scheme. + # When Bearer tokens are used, the identity is + # set to the Bearer token value. + # When TLS client certificates are used, the identity + # is set to the certificate subject RDNSequence. + # E.g. "OU=opa-client-01,O=Example" + # Note: client certificate data is available in the + # 'client_certificates' key. + "identity": "", + + # Client certificates provided by the client when calling OPA + # over an mTLS connection. Represented in input as a list of + # Go x509.Certificate objects marshalled as JSON. + "client_certificates": [], + + # One of {"GET", "POST", "PUT", "PATCH", "DELETE"}. + "method": "", + + # URL path represented as an array. + # For example: /v1/data/exempli-gratia + # is represented as ["v1", "data", "exampli-gratia"] + "path": [...], + + # URL parameters represented as an object of string arrays. + # For example: metrics&explain=true is represented as + # {"metrics": [""], "explain": ["true"]} + "params": {"...": ...}, + + # Request headers represented as an object of string arrays. + # + # Example Request Headers: + # + # host: acmecorp.com + # x-custom: secretvalue + # + # Example input.headers Value: + # + # {"Host": ["acmecorp.com"], "X-Custom": ["mysecret"]} + # + # Example header check: + # + # input.headers["X-Custom"][_] == "mysecret" + # + # Header keys follow canonical MIME form. The first character and any + # characters following a hyphen are uppercase. The rest are lowercase. + # If the header key contains space or invalid header field bytes, + # no conversion is performed. + "headers": {"...": [...]}, + + # Request message body if present for applicable APIs. + # + # Example Request: + # + # POST v1/data HTTP/1.1 + # Content-Type: application/json + # + # {"input": {"action": "trade", "stock": "ACME"}} + # + # Example input.body Value: + # + # {"input": {"action": "trade", "stock": "ACME"}} + # + # Example body check: + # + # input.body.input.stock == "ACME" + # + # The 'body' field is provided for the following APIs: + # + # * POST v1/data + # * POST v0/data + # * POST / + "body": ..., +} +``` + +At a minimum, the authorization policy should grant access to a special root +identity: + +```rego +package system.authz + +default allow := false # Reject requests by default. + +allow if { # Allow request if... + "secret" == input.identity # Identity is the secret root key. +} +``` + +When OPA is configured with this minimal authorization policy, requests without +authentication are rejected: + +```http +GET /v1/policies HTTP/1.1 +``` + +Response: + +```http +HTTP/1.1 401 Unauthorized +Content-Type: application/json +``` + +```json +{ + "code": "unauthorized", + "message": "request rejected by administrative policy" +} +``` + +However, if Bearer token authentication is enabled and the request includes the +secret from above, the request is allowed: + +```http +GET /v1/policies HTTP/1.1 +Authorization: Bearer secret +``` + +Response: + +```http +HTTP/1.1 200 OK +Content-Type: application/json +``` + +Besides boolean responses, authorization policies can change the message included +in the deny response. Do do that, policy decisions must yield an object response as +follows: + +```rego +package system.authz + +default allow := { + "allowed": false, + "reason": "unauthorized resource access", +} + +allow := {"allowed": true} if { # Allow request if... + "secret" == input.identity # identity is the secret root key. +} + +allow := {"allowed": false, "reason": reason} if { + not input.identity + reason := "no identity provided" +} +``` + +### Token-based Authentication Example + +When Bearer tokens are used for authentication, the policy should at minimum +validate the identity: + +```rego +package system.authz + +# Tokens may defined in policy or pushed into OPA as data. +tokens := { + "my-secret-token-foo": { + "roles": ["admin"] + }, + "my-secret-token-bar": { + "roles": ["service-1"] + }, + "my-secret-token-baz": { + "roles": ["service-2", "service-3"] + } +} + +default allow := false # Reject requests by default. + +allow if { # Allow request if... + input.identity == "secret" # Identity is the secret root key. +} + +allow if { # Allow request if... + tokens[input.identity] # Identity exists in "tokens". +} +``` + +To complete this example, the policy could further restrict tokens to specific +documents: + +```rego +package system.authz + +# Rights may be defined in policy or pushed into OPA as data. +rights := { + "admin": { + "path": "*" + }, + "service-1": { + "path": ["v1", "data", "exempli", "gratia"] + }, + "service-2": { + "path": ["v1", "data", "par", "example"] + } +} + +# Tokens may be defined in policy or pushed into OPA as data. +tokens := { + "my-secret-token-foo": { + "roles": ["admin"] + }, + "my-secret-token-bar": { + "roles": ["service-1"] + }, + "my-secret-token-baz": { + "roles": ["service-2", "service-3"] + } +} + +default allow := false # Reject requests by default. + +allow if { # Allow request if... + some right + identity_rights[right] # Rights for identity exist, and... + right.path == "*" # Right.path is '*'. +} + +allow if { # Allow request if... + some right + identity_rights[right] # Rights for identity exist, and... + right.path == input.path # Right.path matches input.path. +} + +identity_rights contains right if { # Right is in the identity_rights set if... + token := tokens[input.identity] # Token exists for identity, and... + role := token.roles[_] # Token has a role, and... + right := rights[role] # Role has rights defined. +} +``` + +### TLS-based Authentication Example + +To set up authentication based on mutual TLS, we will need three certificates: + +1. the CA cert (self-signed), +2. the server cert (signed by the CA), and +3. the client cert (signed by the CA). + +We use `openssl` to create the example certificates and keys used in this demo. In production, creation of certificates +and keys should be handled by an automated process out of scope for this tutorial. + +Note that we also create an extra client cert (client-2). While this certificate is signed by the same CA, it's identity +is different. We'll use this to show our authorization policy in action. + +```bash +# CA +openssl ecparam -out ca-key.pem -name prime256v1 -genkey +openssl req -x509 -new -nodes -key ca-key.pem -days 30 -out ca.pem -subj "/CN=my-ca" + +# client 1 +cat <req.cnf +[req] +req_extensions = v3_req +distinguished_name = req_distinguished_name + +[req_distinguished_name] + +[v3_req] +basicConstraints = CA:FALSE +subjectAltName = @alt_names + +[alt_names] +URI.1 = spiffe://example.com/client-1 +EOF +openssl ecparam -out client-key-1.pem -name prime256v1 -genkey +openssl req -new -key client-key-1.pem -out csr.pem -subj "/CN=client-1" -config req.cnf +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert-1.pem -days 10 -extensions v3_req -extfile req.cnf -sha256 + +# client 2 +cat <req.cnf +[req] +req_extensions = v3_req +distinguished_name = req_distinguished_name + +[req_distinguished_name] + +[v3_req] +basicConstraints = CA:FALSE +subjectAltName = @alt_names + +[alt_names] +URI.1 = spiffe://example.com/client-2 +EOF +openssl ecparam -out client-key-2.pem -name prime256v1 -genkey +openssl req -new -key client-key-2.pem -out csr.pem -subj "/CN=client-2" -config req.cnf +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert-2.pem -days 10 -extensions v3_req -extfile req.cnf -sha256 + +# create server cert with IP and DNS SANs +cat <req.cnf +[req] +req_extensions = v3_req +distinguished_name = req_distinguished_name + +[req_distinguished_name] + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = opa.example.com +IP.1 = 127.0.0.1 +URI.1 = spiffe://example.com/server +EOF +openssl ecparam -out server-key.pem -name prime256v1 -genkey +openssl req -new -key server-key.pem -out csr.pem -subj "/CN=server" -config req.cnf +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 10 -extensions v3_req -extfile req.cnf -sha256 +``` + +We also create an example authorization policy file, called `check.rego`. This example `system.authz` policy will check +the certificate ID against a list of allowed paths as defined in a simple Access Control List. + +:::danger +When choosing messages to return to unauthorized clients in `system.authz` policies, be careful not to expose sensitive +information such as which paths are allowed. +::: + +```rego +package system.authz + +id_uri := input.client_certificates[0].URIs[0] +id_string := sprintf("%s://%s%s", [id_uri.Scheme, id_uri.Host, id_uri.Path]) + +# client_acl represents an access control list and may defined in policy or pushed into OPA as data changes. +client_acl := { + "spiffe://example.com/client-1": [["v1", "data"]], + "spiffe://example.com/client-2": [], +} + +default allow := {"allowed": false, "reason": "Access denied: unknown caller"} + +allow := {"allowed": true} if { + input.path in client_acl[id_string] +} else := { + "allowed": false, + "reason": sprintf("%s is not allowed to call /%s", [ + id_string, + concat("/", input.path), + ]), +} +``` + +Now, we're ready to starting the server with `-authentication=tls` and the +certificate-related parameters: + +```console +$ opa run -s \ + --tls-cert-file server-cert.pem \ + --tls-private-key-file server-key.pem \ + --tls-ca-cert-file ca.pem \ + --authentication=tls \ + --authorization=basic \ + -a https://127.0.0.1:8181 \ + check.rego +{"addrs":["https://127.0.0.1:8181"],"diagnostic-addrs":[],"level":"info","msg":"Initializing server.","time":"2023-01-04T10:31:12Z"} +``` + +We can use `curl` to validate our TLS-based authentication setup: + +First, we use the client certificate that was signed by the CA, and has a subject +matching our authorization policy: + +```console +$ curl --key client-key-1.pem \ + --cert client-cert-1.pem \ + --cacert ca.pem \ + --resolve opa.example.com:8181:127.0.0.1 \ + https://opa.example.com:8181/v1/data +{"result":{}} +``` + +Note that we're passing the CA cert to curl -- this is done to have curl accept +the server's certificate, which has been signed by our CA cert. + +Since we've set up an IP SAN, we may also `curl https://127.0.0.1:8181/v1/data` +directly. (To keep our examples focused, we'll do that from here on.) + +Using a valid certificate whose subject will be declined by our authorization +policy: + +```console +$ curl --key client-key-2.pem \ + --cert client-cert-2.pem \ + --cacert ca.pem \ + https://127.0.0.1:8181/v1/data +{ + "code": "unauthorized", + "message": "spiffe://example.com/client-2 is not allowed to call /v1/data" +} +``` + +Finally, we'll attempt to query without a client certificate: + +```console +$ curl --cacert ca.pem https://127.0.0.1:8181/v1/data +curl: (56) LibreSSL SSL_read: error:1404C412:SSL routines:ST_OK:sslv3 alert bad certificate, errno 0 +``` + +As you can see, TLS-based authentication disallows these request before even invoking the `system.authz` policy. + +## Secure Health and Monitoring + +Often OPA is deployed locally to the host where the client resides (side-car or +similar model). In these deployments it is ideal to only expose the API via +`localhost` to prevent any remote clients from reaching OPA at all. The downside +to this approach is that it blocks remote monitoring systems that require access +to `/health` or `/metrics`. + +The solution is to configure OPA with a separate diagnostic listener by +providing the `--diagnostic-addr` flag, for example: + +``` +$ opa run \ + -s \ + --addr localhost:8181 \ + --diagnostic-addr :8282 +``` + +The configuration above would expose only `/health` and `/metrics` API's on port +`8282` while keeping the normal REST API bound to `localhost:8181`. + +> When the diagnostic listener is enabled, the `/metrics` and `/health` APIs will +> still be exposed on the normal listener. + +## Hardened Configuration Example + +You can run a hardened OPA deployment with minimal configuration. There are a +few things to keep in mind: + +- Limit API access to host-local clients executing policy queries. +- Configure TLS (for localhost TCP) or a UNIX domain socket. +- Do not pass credentials as command-line arguments. +- Run OPA as a non-root user ideally inside it's own account. + +With OPA configured to fetch policies using the [Bundles](./management-bundles) feature +you can configure OPA with a restrictive authorization policy that only grants +clients access to the default policy decision, i.e., `POST /`: + +```rego +package system.authz + +# Deny access by default. +default allow := false + +# Allow anonymous access to the default policy decision. +allow if { + input.method == "POST" + input.path == [""] +} +``` + +The example below shows flags that tell OPA to: + +- Authorize all API requests (`--authorization=basic`) +- Listen on localhost for HTTPS (not HTTP!) connections (`--addr`, `--tls-cert-file`, `--tls-private-key-file`) +- Download bundles from a remote HTTPS endpoint (`--set` flags and `--set-file` flag) + +```bash +opa run \ + --server \ + --authorization=basic \ + --addr=https://localhost:8181 \ + --tls-cert-file=/var/tmp/server.crt \ + --tls-private-key-file=/var/tmp/server.key \ + --set=bundles.authz.service=default \ + --set=bundles.authz.resource=myapp_authz_bundle \ + --set=services.default.url=https://control.acmecorp.com \ + --set-file=services.default.credentials.bearer.token=/var/tmp/secret-bearer-token +``` + +> The `/var/tmp/secret-bearer-token` will store the credential in plaintext. You +> should make sure that file permission(s) are setup to limit access. diff --git a/third_party/opa/docs/docs/ssh-and-sudo-authorization.md b/third_party/opa/docs/docs/ssh-and-sudo-authorization.md new file mode 100644 index 000000000000..ee178abd72c0 --- /dev/null +++ b/third_party/opa/docs/docs/ssh-and-sudo-authorization.md @@ -0,0 +1,438 @@ +--- +title: SSH and sudo +--- + +Host-level access controls are an important part of every organization's +security strategy. Using [Linux-PAM](http://tldp.org/HOWTO/User-Authentication-HOWTO/x115.html) and OPA +we can extend policy-based access control to SSH and sudo. + +## Goals + +This tutorial shows how you can use OPA and Linux-PAM to enforce fine-grained, +host-level access controls over SSH and sudo. + +Linux-PAM can be configured to delegate authorization decisions to plugins +(shared libraries). In this case, we have created an OPA-based plugin that can +be configured to authorize SSH and sudo access. The OPA-based Linux-PAM plugin +used in this tutorial can be found at [open-policy-agent/contrib](https://github.com/open-policy-agent/contrib/tree/main/pam_opa). + +For this tutorial, our desired policy is: + +- Admins can SSH into any host and run sudo commands. +- Normal users can SSH into hosts that they have _contributed_ to and run sudo commands. + +Furthermore, we'll assume we have the following set of users and hosts: + +- `frontend-dev` is a developer who contributes to the app running on the `frontend` host. +- `backend-dev` is a developer who contributes to the app running on the `backend` host. +- `ops` is an administrator for the organization. + +Authentication (verifying user identity) is outside the scope of OPA's +responsibility so this tutorial relies on identities being statically +defined. In real-world scenarios authentication can be delegated to SSH itself +(authorized_keys) or other identity management systems. + +Let's get started. + +## Prerequisites + +This tutorial requires [Docker Compose](https://docs.docker.com/compose/install/) to run dummy SSH hosts along +with OPA. The dummy SSH hosts are just containers running sshd inside. + +## Steps + +### 1. Bootstrap the tutorial environment using Docker Compose. + +First, create a `tutorial-docker-compose.yaml` file that runs OPA and the containers that +represent our backend and frontend hosts. + + +```yaml title="tutorial-docker-compose.yaml" +version: "2" +services: + opa: + image: openpolicyagent/opa:{{ current_version_docker }} + ports: + - "8181:8181" + # WARNING: OPA is NOT running with an authorization policy configured. This + # means that clients can read and write policies in OPA. If you are + # deploying OPA in an insecure environment, be sure to configure + # authentication and authorization on the daemon. See the Security page for + # details: https://www.openpolicyagent.org/docs/security.html. + command: + - "run" + - "--server" + - "--set=decision_logs.console=true" + - "--set=services.nginx.url=http://bundle_server" + - "--set=bundles.nginx.service=nginx" + - "--set=bundles.nginx.resource=bundles/bundle.tar.gz" + depends_on: + - bundle_server + frontend: + image: openpolicyagent/demo-pam + ports: + - "2222:22" + volumes: + - ./frontend_host_id.json:/etc/host_identity.json + backend: + image: openpolicyagent/demo-pam + ports: + - "2223:22" + volumes: + - ./backend_host_id.json:/etc/host_identity.json + bundle_server: + image: nginx:1.20.0-alpine + ports: + - 8888:80 + volumes: + - ./bundles:/usr/share/nginx/html/bundles +``` + + +The `tutorial-docker-compose.yaml` file requires two other local files: +`frontend_host_id.json` and `backend_host_id.json`. These files are mounted +into the containers representing our hosts. The content of the file provides +_context_ that the PAM module provides as input when executing queries +against OPA. + +Create the extra files required by tutorial-docker-compose.yaml: + +```shell +echo '{"host_id": "frontend"}' > frontend_host_id.json +echo '{"host_id": "backend"}' > backend_host_id.json +``` + +> In real-world scenarios, these files could contain arbitrary information that we want to expose to the policy. + +Finally, run `docker-compose` to pull and run the containers. + +```shell +docker-compose -f tutorial-docker-compose.yaml up +``` + +This tutorial uses a special Docker image named `openpolicyagent/demo-pam` to simulate an SSH server. +This image contains pre-created Linux accounts for our users, and the required PAM module is +pre-configured inside the `sudo` and `sshd` files in `/etc/pam.d/`. + +### 2. Create a Bundle for the policies and data. + +In another terminal, create the policies and data that OPA will use to control access to the hosts. + +First, create folder called bundles and cd into it. + +```bash +mkdir bundles +cd bundles +``` + +Next, create a policy that will tell the PAM module to collect context that is required for authorization. +For more details on what this policy should look like, see [this documentation](https://github.com/open-policy-agent/contrib/tree/main/pam_opa/pam#pull). + +**pull.rego**: + +```rego +package pull + +# Which files should be loaded into the context? +files := ["/etc/host_identity.json"] + +# Which environment variables should be loaded into the context? +env_vars := [] +``` + +Create the policies that will authorize SSH and sudo requests. +The `input` which makes up the authorization context in the policy below will also +include some default values, such as the username making the request. See +[this documentation](https://github.com/open-policy-agent/contrib/tree/main/pam_opa/pam#authz) +to get a better understanding of what the `input` to the authorization policy will look like. + +Unlike the _pull_ policy, we'll create separate _authz_ policies +for SSH and `sudo` for more fine-grained control. +In production, it makes more sense to have this separation for _display_ and _pull_ as well. + +Create the SSH authorization policy. It should allow admins to SSH into all hosts, +and non-admins to only SSH into hosts that they contributed code to. + +**sshd_authz.rego**: + +```rego +package sshd.authz + +import input.pull_responses +import input.sysinfo + +import data.hosts + +# By default, users are not authorized. +default allow := false + +# Allow access to any user that has the "admin" role. +allow if { + data.roles.admin[_] == input.sysinfo.pam_username +} + +# Allow access to any user who contributed to the code running on the host. +# +# This rule gets the "host_id" value from the file "/etc/host_identity.json". +# It is available in the input under "pull_responses" because we +# asked for it in our pull policy above. +# +# It then compares all the contributors for that host against the username +# that is asking for authorization. +allow if { + hosts[pull_responses.files["/etc/host_identity.json"].host_id].contributors[_] == sysinfo.pam_username +} + +# If the user is not authorized, then include an error message in the response. +errors contains "Request denied by administrative policy" if { + not allow +} +``` + +Create the `sudo` authorization policy. It should allow only admins to use `sudo`. + +**sudo_authz.rego**: + +```rego +package sudo.authz + +# By default, users are not authorized. +default allow := false + +# Allow access to any user that has the "admin" role. +allow if { + data.roles.admin[_] == input.sysinfo.pam_username +} + +# If the user is not authorized, then include an error message in the response. +errors contains "Request denied by administrative policy" if { + not allow +} +``` + +Now we need to create the data that represents our roles, hots, and contributors into OPA. + +Create a folder called roles, and the following data file. + +```shell +mkdir roles +cat < roles/data.json +{ + "admin": ["ops"] +} +EOF +``` + +Create a folder called hosts, and the following data file. + +```shell +mkdir hosts +cat < hosts/data.json +{ + "frontend": { + "contributors": [ + "frontend-dev" + ] + }, + "backend": { + "contributors": [ + "backend-dev" + ] + } +} +EOF +``` + +Finally create the bundle for the bundle server to use. + +```bash +opa build -b . +``` + +Now you should have the following file structure setup. + +``` +. +└── tutorial-docker-compose.yaml +├── backend_host_id.json +├── frontend_host_id.json +├── bundles +│   ├── bundle.tar.gz +│   ├── pull.rego +│   ├── sshd_authz.rego +│   ├── sudo_authz.rego +│   ├── hosts +│   │   └── data.json +│   ├── roles +│   │   └── data.json +``` + +### 3. SSH and sudo as a user with the `admin` role. + +First, let's try to access the hosts as the `ops` user. Recall, the `ops` user +has been granted the `admin` role (via the `PUT /data/roles` request above) and +users with the `admin` role can login to any host and perform sudo commands. + +Login to the `frontend` host (which has SSH listening on port 2222) and run a command with sudo as the `ops` user. + +```shell +ssh -p 2222 ops@localhost \ + -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null + +sudo ls / +exit +``` + +You will see a lot of verbose logs from `sudo` as the PAM module goes through the motions. +This is intended so you can study how the PAM module works. +You can disable verbose logging by changing the `log_level` argument in the PAM +configuration. For more details see +[this documentation](https://github.com/open-policy-agent/contrib/tree/main/pam_opa/pam#configuration). + +### 4. SSH as a user without the `admin` role. + +Let's try a user without the admin role. Recall, that a non-admin user can SSH +into any host that they have _contributed to_. + +The `frontend-dev` user contributed code to the `frontend` host so they should be +able to login. + +```shell +ssh -p 2222 frontend-dev@localhost \ + -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null +``` + +Only admins can use `sudo`, so you shouldn't be able to run `sudo ls /`. + +Since `frontend-dev` did not contribute to the code running on the +`backend` host (which has SSH listening on port 2223), they should not be able +to login. + +```shell +ssh -p 2223 frontend-dev@localhost \ + -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null +``` + +### 5. Elevate a user's rights through policy. + +Suppose you have a ticketing system for elevation, where you generate tickets for users +that need elevated rights, send the ticket to the user, and expire those tickets when +their rights should be removed. + +Let's mock the current state of this simple ticketing system's API with some data. + +```shell +mkdir elevate +cat < elevate/data.json +{ + "tickets": { + "frontend-dev": "1234" + } +} +EOF +``` + +This means that for now, if the `frontend-dev` user can provide ticket number `1234`, +they should be able to SSH into all servers. + +Let's write policy to ensure that this happens. + +First, we need to make the PAM module take input from the user. + +**display.rego**: + +```rego +package display + +# What should be prompted to the user? +display_spec := [ + { + "message": "Please enter an elevation ticket if you have one:", + "style": "prompt_echo_on", + "key": "ticket" + } +] +``` + +Then we need to make sure that the authorization takes this input into account. + +**sudo_authz_elevated.rego**: + +```rego +# A package can be defined across multiple files. +package sudo.authz + +import data.elevate +import input.display_responses +import input.sysinfo + +# Allow this user if the elevation ticket they provided matches our mock API +# of an internal elevation system. +allow if { + elevate.tickets[sysinfo.pam_username] == display_responses.ticket +} +``` + +Now we need to build a new bundle for OPA to use. + +```shell +opa build -b . +``` + +Confirm that the user `frontend-dev` can indeed use `sudo`. + +```shell +ssh -p 2222 frontend-dev@localhost \ + -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null + +sudo ls / +``` + +You should be prompted with the message that we defined in our _display_ policy +for both the SSH and `sudo` authorization cycles. +This happens because the _display_ policy is shared by the PAM configurations of SSH and `sudo`. +In production, it is more practical to use separate policy packages for each PAM configuration. + +We have not defined the SSH _authz_ policy to work with elevation, so you can enter any value +into the prompt that comes up for for SSH. + +For `sudo`, enter the ticket number `1234` to get access. + +Lastly, update the mocked elevation API and confirm the user's original rights are restored. + +```shell +cat < elevate/data.json +{ + "tickets": {} +} +EOF +``` + +Once again, build the bundle with this new data + +```bash +opa build -b . +``` + +You will find that running `sudo ls /` as the `frontend-dev` user is disallowed again. + +It is possible to configure the _display_ policy to only make the PAM module prompt for the +elevation ticket when our mock API has a non-empty `tickets` object. So when there are no +elevated users, there will be no prompt for a ticket. This can be done using the Rego +[`count` aggregate](./policy-reference/#aggregates). + +## Wrap Up + +Congratulations for finishing the tutorial! + +You learned a number of things about SSH with OPA: + +- OPA gives you fine-grained access control over SSH, `sudo`, and any other application that uses PAM. + Although this tutorial used the some of the same policies for both + SSH and sudo, you should use separate, fine-grained policies for each application that supports PAM. +- Writing allow/deny policies to control who has access to what using context from the user and host. +- Importing external data into OPA and writing policies that depend on that data. + +The code for the PAM module used in this tutorial can be found in the +[open-policy-agent/contrib](https://github.com/open-policy-agent/contrib) +repository. diff --git a/third_party/opa/docs/docs/storage.md b/third_party/opa/docs/docs/storage.md new file mode 100644 index 000000000000..b2ad1a17bfd0 --- /dev/null +++ b/third_party/opa/docs/docs/storage.md @@ -0,0 +1,182 @@ +--- +title: Storage +--- + +## Disk + +This page outlines configuration options relevant to using the disk storage +feature of OPA. +Configuration options are to be found in [the configuration docs](./configuration/#disk-storage). + +:::info +The persistent disk storage enables OPA to work with data that does not fit +into the memory resources granted to the OPA server. +It is **not** supposed to be used as the primary source of truth for that data. + +The on-disk storage should be considered ephemeral: you need to secure the +means to restore that data. +Backup and restore, or repair procedures for data corruption are not provided +at this time. +::: + +### Partitions + +Partitions determine how the JSON data is split up when stored in the +underlying key-value store. +For example, this table shows how an example document would be stored given +different configured partitions: + +```json +{ + "users": { + "alice": { "roles": ["admin"] }, + "bob": { "roles": ["viewer"] } + } +} +``` + +| Partitions | Keys | Values | +| -------------- | -------------------- | ---------------------------------------------------------------- | +| (1) none | `/users` | `{"alice": {"roles": ["admin"]}, "bob": {"roles": ["viewer"]}}}` | +| --- | --- | --- | +| (2) `/users` | `/users/alice` | `{"roles": ["admin"]}` | +| | `/users/bob` | `{"roles": ["viewer"]}` | +| --- | --- | --- | +| (3) `/users/*` | `/users/alice/roles` | `["admin"]` | +| | `/users/bob/roles` | `["viewer"]` | + +Partitioning has consequences on performance: in the example above, the +number of keys to retrieve from the database (and the amount of data of +its values) varies. + +| Query | Partitions | Number of keys read | +| ------------------ | ---------- | ----------------------------- | +| `data.users` | (1) | 1 | +| | (2) | 2 | +| | (3) | 2 | +| --- | --- | --- | +| `data.users.alice` | (1) | 1 with `bob` data thrown away | +| | (2) | 2 | +| | (3) | 2 | + +For example, retrieving the full extent of `data.users` from the disk store +will require a single key fetch with the partitions of (1). +With (2), the storage engine will fetch two keys and their values. + +Retrieving a single user's data, e.g. `data.users.alice`, will require +reading a single key and all the users data with (1); but throw away most +of it: all the data not belonging to `alice`. + +There is no one-size-fits-all setting for partitions: good settings depend +on the actual usage, and that comes down to the policies that are used with +OPA. +Commonly, you would optimize the partition settings for those queries that +are performance critical. + +To figure out suboptimal partitioning, please have a look at the exposed +metrics. + +OPA stores some internal values (such as bundle metadata) in the data store, +under `/system`. Partitions for that part of the data store are managed by +OPA, and providing any overlapping partitions in the config will raise an +error. + +### Metrics + +Using the [REST API](./rest-api/), you can include the `?metrics` query string +to gain insights into the disk storage access related to a certain OPA query. + +``` +$ curl 'http://localhost:8181/v1/data/tenants/acme1/bindings/user1?metrics' | opa eval -I 'input.metrics' -fpretty +{ + "counter_disk_read_bytes": 339, + "counter_disk_read_keys": 3, + "counter_server_query_cache_hit": 1, + "timer_disk_read_ns": 40736, + "timer_rego_external_resolve_ns": 251, + "timer_rego_input_parse_ns": 656, + "timer_rego_query_eval_ns": 66616, + "timer_server_handler_ns": 117539 +} +``` + +The `timer_disk_*_ns` timers give an indication about how much time +was spent with the different disk operations. + +Available timers are + +- `timer_disk_read_ns` +- `timer_disk_write_ns` +- `timer_disk_commit_ns` + +Also note the `counter_disk_*` counters in the metrics: + +- `counter_disk_read_keys`: number of keys retrieved +- `counter_disk_written_keys`: number of keys written +- `counter_disk_deleted_keys`: number of keys deleted +- `counter_disk_read_bytes`: bytes retrieved + +Suboptimal partition settings can be spotted when the amount of +keys and bytes retrieved for a query is unproportional to the +actual data returned: the query likely had to retrieve a giant +JSON object, and most of it was thrown away. + +### Debug Logging + +Pass `--log-level debug` to `opa run` to see all the underlying storage +engine's logs. + +When debug logging is _enabled_, the service will output some +statistics about the configured disk partitions and their key +sizes. + +``` +[DEBUG] partition /tenants/acme3/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes) +[DEBUG] partition /tenants/acme4/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes) +[DEBUG] partition /tenants/acme8/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes) +[DEBUG] partition /tenants/acme9/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes) +[DEBUG] partition /tenants/acme0/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes) +[DEBUG] partition /tenants/acme2/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes) +[DEBUG] partition /tenants/acme6/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes) +``` + +Note that this process will iterate over all database keys. +It only happens on startup, when debug logging is enabled. + +### Fine-tuning Badger settings (superflags) + +While partitioning should be the first thing to look into to tune the memory usage and +performance of the on-disk storage engine, this configurable gives you the means to +change many internal aspects of how Badger uses memory and disk storage. + +:::danger +To be used with care! + +Any of the Badger settings used by OPA can be overridden using this feature. +There is no validation happening for configurables set using this flag. + +When the embedded Badger version changes, these configurables could change, +too. +::: + +The configurables correspond to Badger options that can be set on [the library's Options struct](https://pkg.go.dev/github.com/dgraph-io/badger/v3#Options). + +The following configurables can _not_ be overridden: + +- `dir` +- `valuedir` +- `detectconflicts` + +Aside from conflict detection, Badger in OPA uses the default options [you can find here](https://github.com/dgraph-io/badger/blob/v3.2103.2/options.go#L128-L187). + +Conflict detection is disabled because the locking scheme used within OPA does not allow +for having multiple concurrent writes. + +#### Example + +```yaml +storage: + disk: + directory: /tmp/disk + badger: nummemtables=1; numgoroutines=2; maxlevels=3 +``` diff --git a/third_party/opa/docs/docs/terraform.md b/third_party/opa/docs/docs/terraform.md new file mode 100644 index 000000000000..d94871703e2a --- /dev/null +++ b/third_party/opa/docs/docs/terraform.md @@ -0,0 +1,914 @@ +--- +title: Terraform +--- + +Terraform lets you describe the infrastructure you want and automatically creates, deletes, and modifies +your existing infrastructure to match. OPA makes it possible to write policies that test the changes +Terraform is about to make before it makes them. Such tests help in different ways: + +- tests help individual developers sanity check their Terraform changes +- tests can auto-approve run-of-the-mill infrastructure changes and reduce the burden of peer-review +- tests can help catch problems that arise when applying Terraform to production after applying it to staging + +Terraform is a popular integration case for OPA and there are already a number +of popular tools for running policy on HCL and plan JSONs. Browse existing + tools using OPA and +Terraform in the OPA Ecosystem. + +## Goals + +In this tutorial, you'll learn how to use OPA to implement unit tests for Terraform plans that create +and delete auto-scaling groups and servers. + +## Prerequisites + +This tutorial requires + +- [Terraform 0.12.6](https://releases.hashicorp.com/terraform/0.12.6/) +- [OPA](https://github.com/open-policy-agent/opa/releases) + +(This tutorial _should_ also work with the +[latest version of Terraform](https://www.terraform.io/downloads.html), but +it is untested. Contributions welcome!) + +# Getting Started + +## Steps + +### 1. Create and save a Terraform plan + +Create a [Terraform](https://www.terraform.io/docs/index.html) file that includes an +auto-scaling group and a server on AWS. +(You will need to modify the `shared_credentials_file` to point to your AWS credentials.) + +```shell +cat >main.tf < tfplan.json +``` + +Here is the expected contents of `tfplan.json`. + +```json +{ + "format_version": "0.1", + "terraform_version": "0.12.6", + "planned_values": { + "root_module": { + "resources": [ + { + "address": "aws_autoscaling_group.my_asg", + "mode": "managed", + "type": "aws_autoscaling_group", + "name": "my_asg", + "provider_name": "aws", + "schema_version": 0, + "values": { + "availability_zones": [ + "us-west-1a" + ], + "desired_capacity": 4, + "enabled_metrics": null, + "force_delete": true, + "health_check_grace_period": 300, + "health_check_type": "ELB", + "initial_lifecycle_hook": [], + "launch_configuration": "my_web_config", + "launch_template": [], + "max_size": 5, + "metrics_granularity": "1Minute", + "min_elb_capacity": null, + "min_size": 1, + "mixed_instances_policy": [], + "name": "my_asg", + "name_prefix": null, + "placement_group": null, + "protect_from_scale_in": false, + "suspended_processes": null, + "tag": [], + "tags": null, + "termination_policies": null, + "timeouts": null, + "wait_for_capacity_timeout": "10m", + "wait_for_elb_capacity": null + } + }, + { + "address": "aws_instance.web", + "mode": "managed", + "type": "aws_instance", + "name": "web", + "provider_name": "aws", + "schema_version": 1, + "values": { + "ami": "ami-09b4b74c", + "credit_specification": [], + "disable_api_termination": null, + "ebs_optimized": null, + "get_password_data": false, + "iam_instance_profile": null, + "instance_initiated_shutdown_behavior": null, + "instance_type": "t2.micro", + "monitoring": null, + "source_dest_check": true, + "tags": null, + "timeouts": null, + "user_data": null, + "user_data_base64": null + } + }, + { + "address": "aws_launch_configuration.my_web_config", + "mode": "managed", + "type": "aws_launch_configuration", + "name": "my_web_config", + "provider_name": "aws", + "schema_version": 0, + "values": { + "associate_public_ip_address": false, + "enable_monitoring": true, + "ephemeral_block_device": [], + "iam_instance_profile": null, + "image_id": "ami-09b4b74c", + "instance_type": "t2.micro", + "name": "my_web_config", + "name_prefix": null, + "placement_tenancy": null, + "security_groups": null, + "spot_price": null, + "user_data": null, + "user_data_base64": null, + "vpc_classic_link_id": null, + "vpc_classic_link_security_groups": null + } + } + ] + } + }, + "resource_changes": [ + { + "address": "aws_autoscaling_group.my_asg", + "mode": "managed", + "type": "aws_autoscaling_group", + "name": "my_asg", + "provider_name": "aws", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "availability_zones": [ + "us-west-1a" + ], + "desired_capacity": 4, + "enabled_metrics": null, + "force_delete": true, + "health_check_grace_period": 300, + "health_check_type": "ELB", + "initial_lifecycle_hook": [], + "launch_configuration": "my_web_config", + "launch_template": [], + "max_size": 5, + "metrics_granularity": "1Minute", + "min_elb_capacity": null, + "min_size": 1, + "mixed_instances_policy": [], + "name": "my_asg", + "name_prefix": null, + "placement_group": null, + "protect_from_scale_in": false, + "suspended_processes": null, + "tag": [], + "tags": null, + "termination_policies": null, + "timeouts": null, + "wait_for_capacity_timeout": "10m", + "wait_for_elb_capacity": null + }, + "after_unknown": { + "arn": true, + "availability_zones": [ + false + ], + "default_cooldown": true, + "id": true, + "initial_lifecycle_hook": [], + "launch_template": [], + "load_balancers": true, + "mixed_instances_policy": [], + "service_linked_role_arn": true, + "tag": [], + "target_group_arns": true, + "vpc_zone_identifier": true + } + } + }, + { + "address": "aws_instance.web", + "mode": "managed", + "type": "aws_instance", + "name": "web", + "provider_name": "aws", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "ami": "ami-09b4b74c", + "credit_specification": [], + "disable_api_termination": null, + "ebs_optimized": null, + "get_password_data": false, + "iam_instance_profile": null, + "instance_initiated_shutdown_behavior": null, + "instance_type": "t2.micro", + "monitoring": null, + "source_dest_check": true, + "tags": null, + "timeouts": null, + "user_data": null, + "user_data_base64": null + }, + "after_unknown": { + "arn": true, + "associate_public_ip_address": true, + "availability_zone": true, + "cpu_core_count": true, + "cpu_threads_per_core": true, + "credit_specification": [], + "ebs_block_device": true, + "ephemeral_block_device": true, + "host_id": true, + "id": true, + "instance_state": true, + "ipv6_address_count": true, + "ipv6_addresses": true, + "key_name": true, + "network_interface": true, + "network_interface_id": true, + "password_data": true, + "placement_group": true, + "primary_network_interface_id": true, + "private_dns": true, + "private_ip": true, + "public_dns": true, + "public_ip": true, + "root_block_device": true, + "security_groups": true, + "subnet_id": true, + "tenancy": true, + "volume_tags": true, + "vpc_security_group_ids": true + } + } + }, + { + "address": "aws_launch_configuration.my_web_config", + "mode": "managed", + "type": "aws_launch_configuration", + "name": "my_web_config", + "provider_name": "aws", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "associate_public_ip_address": false, + "enable_monitoring": true, + "ephemeral_block_device": [], + "iam_instance_profile": null, + "image_id": "ami-09b4b74c", + "instance_type": "t2.micro", + "name": "my_web_config", + "name_prefix": null, + "placement_tenancy": null, + "security_groups": null, + "spot_price": null, + "user_data": null, + "user_data_base64": null, + "vpc_classic_link_id": null, + "vpc_classic_link_security_groups": null + }, + "after_unknown": { + "ebs_block_device": true, + "ebs_optimized": true, + "ephemeral_block_device": [], + "id": true, + "key_name": true, + "root_block_device": true + } + } + } + ], + "configuration": { + "provider_config": { + "aws": { + "name": "aws", + "expressions": { + "region": { + "constant_value": "us-west-1" + } + } + } + }, + "root_module": { + "resources": [ + { + "address": "aws_autoscaling_group.my_asg", + "mode": "managed", + "type": "aws_autoscaling_group", + "name": "my_asg", + "provider_config_key": "aws", + "expressions": { + "availability_zones": { + "constant_value": [ + "us-west-1a" + ] + }, + "desired_capacity": { + "constant_value": 4 + }, + "force_delete": { + "constant_value": true + }, + "health_check_grace_period": { + "constant_value": 300 + }, + "health_check_type": { + "constant_value": "ELB" + }, + "launch_configuration": { + "constant_value": "my_web_config" + }, + "max_size": { + "constant_value": 5 + }, + "min_size": { + "constant_value": 1 + }, + "name": { + "constant_value": "my_asg" + } + }, + "schema_version": 0 + }, + { + "address": "aws_instance.web", + "mode": "managed", + "type": "aws_instance", + "name": "web", + "provider_config_key": "aws", + "expressions": { + "ami": { + "constant_value": "ami-09b4b74c" + }, + "instance_type": { + "constant_value": "t2.micro" + } + }, + "schema_version": 1 + }, + { + "address": "aws_launch_configuration.my_web_config", + "mode": "managed", + "type": "aws_launch_configuration", + "name": "my_web_config", + "provider_config_key": "aws", + "expressions": { + "image_id": { + "constant_value": "ami-09b4b74c" + }, + "instance_type": { + "constant_value": "t2.micro" + }, + "name": { + "constant_value": "my_web_config" + } + }, + "schema_version": 0 + } + ] + } + } +} +``` + +The json plan output produced by terraform contains a lot of information. For this tutorial, we will be interested by: + +- `.resource_changes`: array containing all the actions that terraform will apply on the infrastructure. +- `.resource_changes[].type`: the type of resource (e.g. `aws_instance` , `aws_iam` ...) +- `.resource_changes[].change.actions`: array of actions applied on the resource (`create`, `update`, `delete`...) + +For more information about the json plan representation, please check the [terraform documentation](https://www.terraform.io/docs/internals/json-format.html#plan-representation) + +### 3. Write the OPA policy to check the plan + +The policy computes a score for a Terraform that combines + +- The number of deletions of each resource type +- The number of creations of each resource type +- The number of modifications of each resource type + +The policy authorizes the plan when the score for the plan is below a threshold +and there are no changes made to any IAM resources. +(For simplicity, the threshold in this tutorial is the same for everyone, but in +practice you would vary the threshold depending on the user.) + +**policy/terraform.rego**: + +```rego +package terraform.analysis + +import input as tfplan + +######################## +# Parameters for Policy +######################## + +# acceptable score for automated authorization +blast_radius := 30 + +# weights assigned for each operation on each resource-type +weights := { + "aws_autoscaling_group": {"delete": 100, "create": 10, "modify": 1}, + "aws_instance": {"delete": 10, "create": 1, "modify": 1}, +} + +# Consider exactly these resource types in calculations +resource_types := {"aws_autoscaling_group", "aws_instance", "aws_iam", "aws_launch_configuration"} + +######### +# Policy +######### + +# Authorization holds if score for the plan is acceptable and no changes are made to IAM +default authz := false + +authz if { + score < blast_radius + not touches_iam +} + +# Compute the score for a Terraform plan as the weighted sum of deletions, creations, modifications +score := s if { + all_resources := [x | + some resource_type, crud in weights + + del := crud.delete * num_deletes[resource_type] + new := crud.create * num_creates[resource_type] + mod := crud.modify * num_modifies[resource_type] + x := (del + new) + mod + ] + s := sum(all_resources) +} + +# Whether there is any change to IAM +touches_iam if { + all_resources := resources.aws_iam + count(all_resources) > 0 +} + +#################### +# Terraform Library +#################### + +# list of all resources of a given type +resources[resource_type] := all_resources if { + some resource_type, _ in resource_types + + all_resources := [name | + some name in tfplan.resource_changes + name.type == resource_type + ] +} + +# number of creations of resources of a given type +num_creates[resource_type] := num if { + some resource_type, _ in resource_types + + all_resources := resources[resource_type] + creates := [res | + some res in all_resources + "create" in res.change.actions + ] + num := count(creates) +} + +# number of deletions of resources of a given type +num_deletes[resource_type] := num if { + some resource_type, _ in resource_types + + all_resources := resources[resource_type] + + deletions := [res | + some res in all_resources + "delete" in res.change.actions + ] + num := count(deletions) +} + +# number of modifications to resources of a given type +num_modifies[resource_type] := num if { + some resource_type, _ in resource_types + + all_resources := resources[resource_type] + + modifies := [res | + some res in all_resources + "update" in res.change.actions + ] + num := count(modifies) +} +``` + +### 4. Evaluate the OPA policy on the Terraform plan + +To evaluate the policy against that plan, you hand OPA the policy, the Terraform plan as input, and +ask it to evaluate `terraform/analysis/authz`. + +```shell +opa exec --decision terraform/analysis/authz --bundle policy/ tfplan.json +``` + +```json +{ + "result": [ + { + "path": "tfplan.json", + "result": true + } + ] +} +``` + +If you're curious, you can ask for the score that the policy used to make the authorization decision. +In our example, it is 11 (10 for the creation of the auto-scaling group and 1 for the creation of the server). + +```shell +opa exec --decision terraform/analysis/score --bundle policy/ tfplan.json +``` + +```json +{ + "result": [ + { + "path": "tfplan.json", + "result": 11 + } + ] +} +``` + +If as suggested in the previous step, you want to modify your policy to make an authorization decision +based on both the user and the Terraform plan, the input you would give to OPA would take the form +`{"user": , "plan": }`, and your policy would reference the user with `input.user` and +the plan with `input.plan`. You could even go so far as to provide the Terraform state file and the AWS +EC2 data to OPA and write policy using all of that context. + +### 5. Create a Large Terraform plan and Evaluate it + +Create a Terraform plan that creates enough resources to exceed the blast-radius permitted +by policy. + +```shell +cat >main.tf < tfplan_large.json +``` + +Evaluate the policy to see that it fails the policy tests and check the score. + +```shell +opa exec --decision terraform/analysis/authz --bundle policy/ tfplan_large.json +opa exec --decision terraform/analysis/score --bundle policy/ tfplan_large.json +``` + +### 6. (Optional) Run OPA using a remote policy bundle + +In addition to loading policies from the local filesystem, `opa exec` can fetch policies from remote locations via [Bundles](./management-bundles). To see this in action, first build the policies into a bundle: + +```shell +opa build policy/ +``` + +Next, serve the bundle via nginx: + +```bash +docker run --rm --name bundle_server -d -p 8888:80 -v ${PWD}:/usr/share/nginx/html:ro nginx:latest +``` + +Then run `opa exec` with bundles enabled: + +``` +opa exec --decision terraform/analysis/authz \ + --set services.bundle_server.url=http://localhost:8888 \ + --set bundles.tutorial.resource=bundle.tar.gz \ + tfplan_large.json +``` + +## Wrap Up + +Congratulations for finishing the tutorial! + +You learned a number of things about Terraform Testing with OPA: + +- OPA gives you fine-grained policy control over Terraform plans. +- You can use data other than the plan itself (e.g. the user) when writing authorization policies. + +Keep in mind that it's up to you to decide how to use OPA's Terraform tests and authorization decision. Here are some ideas. + +- Add it as part of your Terraform wrapper to implement unit tests on Terraform plans +- Use it to automatically approve run-of-the-mill Terraform changes to reduce the burden of peer-review +- Embed it into your deployment system to catch problems that arise when applying Terraform to production after applying it to staging + +If you'd like to explore an additional example that uses terraform modules please continue below. + +# Working with Modules + +## Module Steps + +### 1. Create and save Terraform module plan + +Create a new Terraform file that includes a +security group and security group from a module. +(This example uses the module from +[terraform-aws-modules](https://github.com/terraform-aws-modules/terraform-aws-security-group)) + +```shell +cat >main.tf < tfplan2.json +``` + +### 3. Write the OPA policy to collect resources + +The policy evaluates if a security group is valid based on the contents of it's description: + +- Resources can be specified under the root module or in child modules +- We want to evaluate against the combined group of these resources +- This example is scoped to the planned changes section of the json representation + +The policy uses the walk keyword to explore the json structure, and uses conditions to filter for the specific paths where resources would be found. + +**policy/terraform_module.rego**: + +```rego +package terraform.module + +deny contains msg if { + some r + desc := resources[r].values.description + contains(desc, "HTTP") + msg := sprintf("No security groups should be using HTTP. Resource in violation: %v", [r.address]) +} + +resources contains r if { + some path, value + + # Walk over the JSON tree and check if the node we are + # currently on is a module (either root or child) resources + # value. + walk(input.planned_values, [path, value]) + + # Look for resources in the current value based on path + some r in module_resources(path, value) +} + +# Variant to match root_module resources +module_resources(path, value) := value if { + # Expect something like: + # + # { + # "root_module": { + # "resources": [...], + # ... + # } + # ... + # } + # + # Where the path is [..., "root_module", "resources"] + + reverse_index(path, 1) == "resources" + reverse_index(path, 2) == "root_module" +} + +# Variant to match child_modules resources +module_resources(path, value) := value if { + # Expect something like: + # + # { + # ... + # "child_modules": [ + # { + # "resources": [...], + # ... + # }, + # ... + # ] + # ... + # } + # + # Where the path is [..., "child_modules", 0, "resources"] + # Note that there will always be an index int between `child_modules` + # and `resources`. We know that walk will only visit each one once, + # so we shouldn't need to keep track of what the index is. + + reverse_index(path, 1) == "resources" + reverse_index(path, 3) == "child_modules" +} + +reverse_index(path, idx) := path[count(path) - idx] +``` + +### 4. Evaluate the OPA policy on the Terraform module plan + +To evaluate the policy against that plan, you hand OPA the policy, the Terraform plan as input, and +ask it to evaluate `data.terraform.module.deny`. + +```shell +opa exec --decision terraform/module/deny --bundle policy/ tfplan2.json +``` + +This should return one of the two resources. The security group created by the module uses HTTP in its description and therefore fails the evaluation. + +```shell +{ + "result": [ + { + "path": "tfplan2.json", + "result": [ + "No security groups should be using HTTP. Resource in violation: module.http_sg.aws_security_group.this_name_prefix[0]" + ] + } + ] +} +``` + +## Module Wrap Up + +Congratulations on finishing the tutorial! + +You learned OPA can be used to determine if a proposed configuration is authorized. + +Additional use cases might include: + +- Ensuring all resources have tags before they are created +- Making sure naming standards for resources are followed +- Security or operational requirements + +# Ecosystem Projects + + +As further reading, you might be interested to review the Terraform integrations +from the OPA Ecosystem. + diff --git a/third_party/opa/docs/docs/v0-compatibility.md b/third_party/opa/docs/docs/v0-compatibility.md new file mode 100644 index 000000000000..c1b8ffe9e684 --- /dev/null +++ b/third_party/opa/docs/docs/v0-compatibility.md @@ -0,0 +1,188 @@ +--- +title: v0 Backwards Compatibility +sidebar_position: 14 +--- + +## Running OPA in v0.x compatibility mode + +The v1.0 release of OPA comes with functionality to run in a backwards +compatible, v0.x mode. This is used by running OPA with the `--v0-compatible` +flag or using the v0.x compatible options in Go integrations. When enabled, OPA +instances and Go integrations will behave as they do in pre v1.0 releases. + +### When to use v0.x compatibility mode + +**Use of v0.x compatibility mode is not recommended for most users**. This +mode is intended to help users with large volumes of third party Rego stay up to +date while performing a longer term migration to a OPA v1.0 compatible OPA +feature set. Examples of when this applies: + +- You run a service for customers who supply their own Rego. +- You use OPA as part of a managed platform and need to run a mix of v0.x and + v1.0 OPAs based on customer demands. + +Users with control over their Rego and OPA deployments are instead encouraged +to migrate their Rego to be compatible with OPA v1.0 using the below tooling options: + +1. The `rego.v1` import makes OPA apply all restrictions that are enforced by default in OPA v1.0. + If a Rego module imports `rego.v1`, it means applicable `future.keywords` imports are implied. It is illegal to import both `rego.v1` and `future.keywords` in the same module. +2. The `--v0-v1` flag on the `opa fmt` command will rewrite existing modules to use the `rego.v1` import instead of `future.keywords` imports. +3. The `--v0-v1` flag on the `opa check` command will check that either the `rego.v1` import or applicable `future.keywords` imports are present if any of the `in`, `every`, `if` and `contains` keywords are used in a module. + +### v0.x compatibility mode in the OPA binary + +The `--v0-compatible` flag is supported on the following commands in OPA v1.0.x +releases: + +- `bench`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `build`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `deps`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `check`*: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `eval`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `exec`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `fmt`*: formats modules to be compatible with OPA v0.x syntax. See note about + `--v0-v1` flag below. +- `inspect`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `parse`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `run`: supports modules (including discovery bundle) using Rego v0.x syntax, use of `import rego.v1` is optional. Binds server listeners to all interfaces by default, rather than localhost. +- `test`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. + +Note (*): the `check` and `fmt` commands also support the `--v0-v1` flag, +which will check/format Rego modules as if compatible with the Rego syntax of +_both_ the old 0.x OPA version and current OPA v1.0. + +Note (*): Pre v1.0 versions of OPA also support a comparable `--v1-compatible` +flag which can be used to produce and consume Rego v1 bundles. See +[Upgrading to v1.0](./v0-upgrade) for more information on how to use this flag +as part of an upgrade to OPA v1.0. + +### v0.x compatibility mode in Rego package + +There are three ways to enable v0.x compatibility mode in the [Rego package](https://pkg.go.dev/github.com/open-policy-agent/opa/rego): + +1. Set the Rego version on modules +2. Set the Rego version on bundle manifests +3. Use the SetRegoVersion Rego argument + +1 & 2 are preferred as they are more granular and make it easier to run a +mix of v0.x and v1.0 compatible Rego in the same OPA instance and thus better +support a gradual upgrade path. + +The `SetRegoVersion` method on [Module](https://pkg.go.dev/github.com/open-policy-agent/opa/ast#Module.SetRegoVersion?) +can be used like this: + +```go +m := ast.Module{ + Package: regoCode, +} + +m.SetRegoVersion(ast.RegoV0) +``` + +Similarly, the [Bundle Manifest](https://pkg.go.dev/github.com/open-policy-agent/opa/bundle#Manifest.SetRegoVersion) Rego version +can be set like this: + +```go +b := Bundle{ + // ... +} +b.SetRegoVersion(ast.RegoV0) +``` + +If you cannot set the Rego version on modules or bundle manifests, you +can use the [`SetRegoVersion`](https://pkg.go.dev/github.com/open-policy-agent/opa/rego#SetRegoVersion) Rego argument to control the Rego version used when +evaluating policies. + +Users are encouraged to use the more granular options where possible to better +allow them to upgrade Rego used in their system to Rego v1 gradually. + +In the example below, `SetRegoVersion` is used as a Rego argument instructing +the supplied Rego to be handled as v0.x syntax: + +```go +// Only to be used if the above are not suitable. +r := rego.New( + rego.Query("data.foo.bar"), + rego.Module("policy.rego", regoCode), + rego.SetRegoVersion(ast.RegoV1), // <--- +) +``` + +Finally, another option is to import the `v0` package instead. The program + +below imports the v0 package instead: + +```go +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/open-policy-agent/opa/rego" + // rather than the v1 import, which is: + // "github.com/open-policy-agent/opa/v1/rego" +) + +func main() { + module := `package example +messages[msg] { + msg := "foo" +} +` + + r := v0rego.New( + rego.Query("data.example.messages"), + rego.Module("example.rego", module), + ) + + rs, _ = rv0.Eval(context.TODO()) + bs, _ = json.Marshal(rs) + + fmt.Println(string(bs)) +} +``` + +:::danger +**Note**: Using v0 packages and v1 packages in the same program is considered an +anti-pattern and is not recommended or supported. Any interoperability between +the two packages is not guaranteed and should be considered unsupported. +::: + +### v0.x compatibility mode in the OPA Go SDK + +In OPA 1.0, the recommended + +[SDK package](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/sdk) +import for most users is `github.com/open-policy-agent/opa/v1/sdk`. + +Those who need to support v0 bundles should set the Rego version on bundle +manifests as outlined above wherever possible. For users unable to do this, use +of a v0 import of the SDK package is required. For example: + +```go +package main + +import ( + "bytes" + "context" + "fmt" + + "github.com/open-policy-agent/opa/sdk" // <-- import v0 sdk package +) + +func main() { + opa, _ := sdk.New(ctx, sdk.Options{ + ID: "opa-1", + Config: bytes.NewReader(config), + }) + + defer opa.Stop(ctx) + + // ... +} +``` + +Users in this scenario should look to version bundles as soon as possible to +allow them to use a v1 SDK instead. diff --git a/third_party/opa/docs/docs/v0-upgrade/index.md b/third_party/opa/docs/docs/v0-upgrade/index.md new file mode 100644 index 000000000000..f18b00d2ddf8 --- /dev/null +++ b/third_party/opa/docs/docs/v0-upgrade/index.md @@ -0,0 +1,555 @@ +--- +title: Upgrading to v1.0 +sidebar_position: 14 +--- + +All users should plan to upgrade to OPA v1.0 eventually. Some users, with more +control over the Rego loaded into the OPA instances they run will be able to do +so more quickly. Other users with less control or running third party Rego may +wish to upgrade to OPA v1.0 and use the [v0 compatibility](./v0-compatibility) functionality to +upgrade gradually. + +This documentation covers the different upgrade scenarios and the best course of +action for each. The documentation makes use of the following concepts: + +- **Bundle Producer**: A system based on `opa build` that produces a bundle that + is loaded by consumers. +- **Bundle Consumer**: An OPA instance that loads and evaluates policy + from a bundle in the system. +- **Authoring**: The process of writing Rego policies before bundles are + produced and consumed. In managed systems, this might be a user's only contact + point with OPA. + +In some systems, where OPA is used without a bundle, there is no producer. This +simplifies the upgrade process. + +Users are encouraged to upgrade to OPA v1.0 as soon as possible. Using the v0 +compatible functionality until updating Rego is preferred to delaying the +upgrade. The first part of this guide refers to upgrading OPA instances used for +producing and consuming bundles. This is the first step users should take unless +their Rego is already v1.0 compatible. See [Upgrading Rego](#upgrading-rego) +below for information on how to upgrade Rego policies to be v1.0 compatible. + +## General Upgrade Approach: Upgrade Producers, then Consumers + +Users will need to upgrade to OPA v1.0 in their own way, depending on their +release and change management processes, use cases and risk tolerance. The +general advice is to upgrade producers first, then consumers. This is because +the updated producers would be able to set the Rego version on bundle manifests +and as a result it wouldn't be necessary to run consumers with the `--v0-compatible` flag. +Also since it's likely there are much more consumers than producers, upgrading producers +first would lead to a smoother upgrade process. + +Some users may wish to migrate to OPA v1.0 all at once, with adequate testing and validation +this is possible. Not all steps are necessary for all users so a hybrid approach +is also an option depending on your context. + +The rest of this documentation is designed to meet users where they find +themselves and direct them down the smoothest path to upgrade to OPA +v1.0. + +## Detailed Producer & Consumer Version Scenarios + +Tabulated in this section are the different versions of OPA users might be +working with in different parts of their systems. Select the scenario that best +matches your setup to find the recommended upgrade path. + +If you are in doubt, [Scenario 1](#scenario-1) is the most common starting +point and we recommended you start there. + +| | v0.x Consumer | Mix Consumer | v1.0 Consumer | +| ----------------- | ------------------------------------ | ------------------------- | ------------------------------------ | +| **v0.x Producer** | [Scenario 1](#scenario-1) (All v0.x) | [Scenario 4](#scenario-4) | [Scenario 7](#scenario-7) | +| **Mix Producer** | [Scenario 2](#scenario-2) | [Scenario 5](#scenario-5) | [Scenario 8](#scenario-8) | +| **v1.0 Producer** | [Scenario 3](#scenario-3) | [Scenario 6](#scenario-6) | [Scenario 9](#scenario-9) (All v1.0) | + +```mermaid +graph LR + s2["Scenario 2"] + s3["Scenario 3"] + s4["Scenario 4"] + s5["Scenario 5"] + s6["Scenario 6"] + s7["Scenario 7"] + s8["Scenario 8"] + s10["Scenario 10"] + start["Recommended
Start Point"] --> s1 + style start fill:none,stroke:none + s10 --> s1["Scenario 1
All v0.x"] + s1 --> s2 + s2 --> s3 + s3 --> s6 + s6 --> s9 + s4 --> s1 + s4 --> s6 + s5 --> s9 + s7 --> s9 + s1 -->|Single step
upgrade path| s9 + s8 --> s9["Scenario 9
OPA 1.0+"] +``` + +## Upgrade Scenarios + +Listed below are the different upgrade scenarios and the recommended migration +plans for each case. + +### Scenario 1: v0.x Producer, v0.x Consumer + +All OPA runtimes - both bundle consumers and producers - are v0.x. This is the +most common starting point for users upgrading from a v0.x version of OPA. + +#### How to Run + +- Policies are authored to be v0.x compatible. + +#### Next + +Start upgrading producers to v1.0 ([Scenario 2](#scenario-2)) until all producers +are v1.0 ([Scenario 3](#scenario-3)). + +### Scenario 2: Mix Producer, v0.x Consumer + +Some bundle producers are v1.0, while some remain on v0.x. All bundle consumers +are v0.x. This might be the case if you have bundles from different tenants or +users using different OPA versions and you cannot control the versions they use. + +#### Pre-requisites + +Users cannot proceed with upgrade until they have either a single version of bundle +producers or have a means to control the use of `--v0-compatible` on newer +producers. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v0.x producers are run as-is. +- v1.0 producer is run with `--v0-compatible`, or modules have `rego.v1` import. +- v0.x consumers are run as-is. + +#### Next + +Continue migrating producers to v1.0 until all producers have been upgraded +([Scenario 3](#scenario-3)). + +### Scenario 3: v1.0 Producer, v0.x Consumer + +OPA bundles are produced by OPA v1.0 instances, consumers are still on v0.x. This +scenario is common as users upgrade to OPA v1.0 by upgrading their producers +first. + +#### Pre-requisites + +Control of producers to set `--v0-compatible` or use `rego.v1` imports is +required. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v0.x consumers are run as-is. +- v1.0 producer is run with `--v0-compatible`, or modules have `rego.v1` import. +- Since policies will always be consumed by a v0.x OPA, all policies _must_ be v0.x compliant. + +#### Next + +Now that all producers are v1.0, and consumers are still not all v1.0, it's time +to get all the consumers to v1.0, ([Scenario 6](#scenario-6)). + +### Scenario 4: v0.x Producer, Mix Consumer + +Producers are v0.x, consumers are a mix of v0.x and v1.0. This scenario might occur +when users have partially upgraded OPA instances to v1.0, but have not yet +upgraded their consumers. This is not a recommended step if it can be avoided as +it's recommended to upgrade producers first. + +#### Pre-requisites + +OPA v1.0 consumers must be able to run with `--v0-compatible` to accept v0.x +bundles. This upgrade path cannot continue until this is possible. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v0.x producers and v0.x consumers are run as is. + +### Next + +Upgrade producers to v1.0 and continue the upgrade from that point. Generally, it's recommended to +upgrade producers first, however depending on your existing OPAs v1.0 consumers deployments, you may prefer to +upgrade all your producers to v1.0 rather than to downgrade consumers. + +### Scenario 5: Mix Producer, Mix Consumer + +Mixed versions of OPA are being used for both bundle production and consumption. + +#### Pre-requisites + +As users have a mix of bundle producers, they must have control over the runtime +options for the producers to set `--v0-compatible`. Users must also have control +over their v1.0 consumers to set the `--v0-compatible` flag. Both these conditions +must be met for the upgrade to proceed. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v1.0 consumers are run with `--v0-compatible`. +- v1.0 producers are run with `--v0-compatible`. + +### Next + +Please gradually upgrade producers to v1.0 until all producers are v1.0 ([Scenario 6](#scenario-6)). + +### Scenario 6: v1.0 Producer, Mix Consumer + +All consumers can be run without flags, as the bundle will contain +attributes to inform v1.0 OPAs to accept v0.x modules. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v0.x consumers are run as-is. Bundles will contain v0.x policies +- v1.0 consumers are run as-is. Bundles will contain `rego_version` attribute, so v0.x modules are accepted. + +#### Pre-requisites + +If users cannot set their OPA v1.0 producers to use `--v0-compatible` to be +compatible with their v0.x consumers, then this upgrade path is blocked. + +#### Next + +Running exclusively v1.0 producers and consumers, ([Scenario 9](#scenario-9)), is +the next and final step. + +### Scenario 7: v0.x Producer, v1.0 Consumer + +All consumers are v1.0, but producers are v0.x. This scenario might occur when +OPAs used for evaluation are upgraded before the policy bundling system. + +#### Pre-requisites + +If v1.0 consumers cannot be run with `--v0-compatible`, when loading v0.x consumer +generated bundle, the bundles cannot include `rego_version` attribute. This means +the upgrade path is blocked until either the consumers can create bundles with a +Rego version or the `--v0-compatible` flag is available for producers. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v1.0 consumers are run with `--v0-compatible` + +#### Next + +Upgrade producers to v1.0 ([Scenario 8](#scenario-8)) until all producers are v1.0 +([Scenario 9](#scenario-9)). + +### Scenario 8: Mix Producer, v1.0 Consumer + +All consumers are v1.0, but producers are a mix of v0.x and v1.0. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v0.x producers are run as is. +- v1.0 consumers are run with `--v0-compatible` +- v1.0 producers are run with `--v0-compatible` + +#### Pre-requisites + +If using v0.x bundles, it must be possible to use `--v0-compatible` on the bundle +producers in order for them to work in the v1.0 consumers. + +v1.0 consumers will accept v1.0 producer bundles, as these will have the Rego version specified in the manifest; +they won't however accept bundles from v0.x producers unless they have `--v0-compatible` set. + +#### Next + +Upgrade producers to v1.0 ([Scenario 9](#scenario-9)), completing the upgrade. + +### Scenario 9: v1.0 Producer, v1.0 Consumer + +Once you have all consumers and producers running at v1.0 then you have +completed the upgrade to OPA v1.0. If you are using `--v0-compatible` +functionality, the next task is to upgrade the Rego loaded into OPAs to Rego v1. + +Regardless of whether you are now upgrading your Rego, we encourage users to +use `opa check`, `opa check --strict` and to lint their Rego projects if you +have not already done so to identify issues. + +## Changes to Rego in OPA v1.0 + +Once you have upgraded OPA instance to v1.0, or if you are upgrading all at +once, you will need to upgrade your Rego policies to Rego v1.0. This section +outlines the changes in Rego v1.0. + +### The `future.keywords` imports + +The `in`, `every`, `if` and `contains` keywords have been introduced over time, +and Rego v0.x required an opt-in to prevent them from breaking policies that +existed before their introduction. The `future.keywords` imports facilitate this +opt-in mechanism. These keywords help to increase the readability of policies +and provide syntactic sugar for commonly used operations such as iteration, +membership checks, defining multi-value rules, and so on. There is growing +adoption of these keywords and their usage is prevalent in the OPA +documentation, Rego Playground, etc. + +In OPA v1.0 the `in`, `every`, `if` and `contains` keywords are part of the +language by default and the `future.keywords` imports will become a no-op. A +policy that makes use of these keywords, but doesn't import `future.keywords` is +valid in OPA v1.0 but not in older versions of OPA. + +### Enforce use of `if` and `contains` keywords in rule head declarations + +In Rego v0.x, there is semantic ambiguity between rules like `a.b {true}` and +`a.b.c {true}`. Although syntactically similar, the former generates a set with +the entry `b` at path `data.a`, while the latter generates an object with the +attribute `"c": true` at path `data.a.b`. This inconsistency makes it difficult +for new users to understand how Rego works. The `if` keyword is more than just +syntactic sugar. When used in a rule head, that rule doesn't contribute to a +partial set unless the `contains` keyword is also used. E.g. `a.b if {true}` +will generate an object with the attribute `"b": true` at path `data.a`. To make +things simpler, OPA v1.0 requires the usage of `if` and `contains` keywords when +declaring rules. This would mean: + +- All rules are single-value by default. When the value is omitted from the + head, it defaults to `true`. +- To make rules multi-value (i.e. partial set rules), use the `contains` keyword + to convert the value into a set. + +The `contains` keyword is required to disambiguate rules that generate a single +value from rules that generate multiple values. The `if` keyword ensures that +the semantics of rules do not change between v0.x and v1.0 Rego. The table below +illustrates why `if` is required. + +| rule | output in v0.x | output in v1.0 | +| ------------------- | -------------------------- | -------------------------- | +| `p { true }` | `{"p": true}` | `compile error` | +| `p.a { true }` | `{"p": {"a"}}` | `compile error` | +| `p.a.b { true }` | `{"p": {"a": {"b": true}}` | `compile error` | +| `p if { true }` | `{"p": true}` | `{"p": true}` | +| `p.a if { true }` | `{"p":{"a": true}}` | `{"p":{"a": true}}` | +| `p.a.b if { true }` | `{"p": {"a": {"b": true}}` | `{"p": {"a": {"b": true}}` | +| `p contains “a”` | `{"p": {"a"}}` | `{"p": {"a"}}` | + +If the Rego language was changed so that all rules were single-value by default, +unless the `contains` keyword was used to make them multi-value, then the +outcome of a rule like `p.a { true }` would change between v0.x and v1.0 +without generating an error. Generating errors in this case is preferable to +changing the semantics of existing rules. Therefore, use of the `if` keyword is +a requirement in OPA v1.0. + +In OPA v1.0, the `if` keyword is only required for rules with a declared body. +Constants, rules that only consist of a value assignment, do not require `if`. +The following forms therefore remain valid in OPA v1.0: + +| rule | output in v0.x | output in v1.0 | +| ------------ | ------------------------ | ------------------------ | +| `p := 1` | `{"p": 1}` | `{"p": 1}` | +| `p.a := 1` | `{"p": {"a": 1}}` | `{"p": {"a": 1}}` | +| `p.a.b := 1` | `{"p": {"a": {"b": 1}}}` | `{"p": {"a": {"b": 1}}}` | + +Since the `if` keyword can only be used in front of a rule body, rules with no +body and no value assignment, i.e. a solitary reference, are not allowed in +the v1.0 Rego syntax: + +| rule | output in v0.x | output in v1.0 | +| ------- | --------------------------- | --------------- | +| `p` | `compile error` | `compile error` | +| `p.a` | `{"p": {"a"}}` | `compile error` | +| `p.a.b` | `{"p": {"a": {"b": true}}}` | `compile error` | + +The below table gives examples of v0.x valid Rego syntax which are +invalid in OPA v1.0, along with the equivalent valid syntax in OPA v1.0: + +| invalid in v1.0 | v1.0 equivalent | Note | +| ---------------- | ---------------------------- | ------------------------- | +| `p { true }` | `p if { true }` | `Single-value rule` | +| `p.a` | `p contains "a"` | `Multi-value insertion` | +| `p.a { true }` | `p contains "a" if { true }` | `Multi-value rule` | +| `p.a.b` | `p.a.b := true` | `Single-value assignment` | +| `p.a.b { true }` | `p.a.b if { true }` | `Single-value rule` | + +Following is an example of how to define a rule that generates a set: + +```rego +package play + +a contains b if { b := 1 } +``` + +When the above rule is evaluated the output is (sets are serialized into arrays +in JSON): + +```json +{ + "a": [1] +} +``` + +Following is an example of how to define a rule that generates an object: + +```rego +package play + +a[b] if { b := 1} +``` + +When the above rule is evaluated the output is: + +```json +{ + "a": { + "1": true + } +} +``` + +The requirement of `if` and `contains` keywords remove the ambiguity +between single-value and multi-value rule declaration. This makes Rego code +easier to author and read; thereby making it simpler for users to author their +policies. + +### Prohibit duplicate imports + +As part of `strict` mode in OPA 0.x, the Rego compiler prohibits duplicate imports where one import shadows another. +OPA v1.0 enforces this check by default. + +An import shadowing another is most likely an authoring error and probably +unintentional. OPA checking this by default will help to avoid policy +evaluations resulting in error-prone decisions. + +### `input` and `data` keywords are reserved + +The Rego compiler ensures that `input` and `data` are reserved keywords and may +not be used as names for rules and variable assignments. This is part of`strict` mode in OPA 0.x + +The `input` document holds the user-provided input, while the data pushed into +OPA and rule evaluation results are nested under the `data` document. Hence, if +a rule or variable shadows `input` or `data` you have the unintended consequence +of erasing information under these inside the local scope, resulting in incorrect policy decisions. In +OPA v1.0 such scenarios are avoided by default. + +Note, using the [with](./policy-language/#with-keyword) keyword to insert +values into - or to fully replace - the `input` or `data` documents, as in +`my_func(x) with input as {...}` does not constitute shadowing and is therefore +allowed in OPA v1.0. + +### Prohibit use of deprecated builtins + +As part of `strict` mode in OPA 0.x, the Rego compiler prohibits use of deprecated built-in functions. In OPA v1.0, +these built-ins have been removed. + +The following built-in functions are deprecated: `any`, `all`, `re_match`, +`net.cidr_overlap`, `set_diff`, `cast_array`, `cast_set`, `cast_string`, +`cast_boolean`, `cast_null`, `cast_object`. In some cases, new built-in +functions have been added that provide functionality at least similar to a +deprecated built-in. + +### Rego-versioned bundles + +A bundle built with OPA `v0.64.0` or later, contain a `rego_version` attribute +in their [manifest](./management-bundles/#bundle-file-format), which the OPA +consuming that bundle will use when processing the contained modules. A bundle's +internal rego-version takes precedence over the presence of the +`--v1-compatible` flag; therefore, prerequisite knowledge about what Rego syntax +any consumed bundle contains is not needed. The `--v1-compatible` flag (and +`--v0-compatible` in v1.0) on the `opa build` command allows the user to control +the `rego-version` of the built bundle. + +See [Upgrading to v1.0](./v0-upgrade) for more information on how to use +versioned bundles as part of an upgrade to OPA v1.0. + +## Compilation Constraints and Checks + +Below constraints and safety checks are enforced by default in v1.0 during compilation. These checks along with the ones in [v1.0 strict mode](./policy-language/#strict-mode) +were part of the compiler `strict` mode in OPA 0.x. + +| Name | Description | +| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Duplicate imports | Duplicate [imports](./policy-language/#imports), where one import shadows another, are prohibited. | +| `input` and `data` reserved keywords | `input` and `data` are reserved keywords, and may not be used as names for rules and variable assignment. | +| Use of deprecated built-ins | Use of deprecated functions is prohibited, and these will be removed in OPA 1.0. Deprecated built-in functions: `any`, `all`, `re_match`, `net.cidr_overlap`, `set_diff`, `cast_array`, `cast_set`, `cast_string`, `cast_boolean`, `cast_null`, `cast_object` | + +## Upgrading Rego + +Users with v0.x Rego projects are encouraged to follow the below process to +upgrade their Rego code to conform to best practices, and to be compatible with +OPA v1.0. These steps are largely based on the process outlined in this +[detailed blog post](https://www.styra.com/blog/renovating-rego/). + +Before starting the upgrade, users are recommended to ensure they have a local +OPA binary of version 1.0 or later. + +1. `opa check --v0-v1`, this will catch any parse or compilation errors. +2. `opa check --v0-v1 --strict`, this will raise a number of other issues found in code + that might make it incompatible with OPA v1.0 such as the use of deprecated + built-ins or duplicate imports. +3. Automatically reformat your code for OPA v1.0 with `opa fmt --write --v0-v1`. +4. `regal lint`, the [Regal linter](../ecosystem/entry/regal/) has many more rules to + test for issues in Rego code that can lead to errors, poor performance or + unexpected behaviour. + +If you run into any issues while upgrading a Rego project, please drop a message +in the #help channel on the [OPA Slack](https://slack.openpolicyagent.org/). + +## Upgrading OPA Instances + +Prior to OPA 1.0, when running in server mode (`opa run --server/-s`), OPA would +bind to all interfaces by default. In OPA 1.0, +[OPA will bind to `localhost`](https://github.com/open-policy-agent/opa/issues/6286) +by default instead. Though not inherently insecure in a trusted environment, +it's good practice to bind OPA to localhost by default if OPA is not intended to +be exposed to remote services. + +If you need to replicate the v0.x behaviour, you can use the `--addr` flag to +bind to all interfaces. For example: + +```sh +opa run --server --addr 0.0.0.0:8181 +``` + +:::info +When running OPA in a container, binding to all interfaces is required +when the instance needs to be accessed by the host or another container. +::: + +More information can be found in the +[security documentation](../security/#interface-binding). + +## Upgrading for Go Integrations + +Both users of the +[v0 SDK](https://pkg.go.dev/github.com/open-policy-agent/opa/sdk) +and +[v0 Rego](https://pkg.go.dev/github.com/open-policy-agent/opa/rego) packages are +encoraged to upgrade to the new v1 packages instead. These can be found here: + +- [SDK v1](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/sdk) +- [Rego v1](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/rego) + +In order to upgrade to a v1 package, you need to make the following change: + +Before: + +``` +import ( + "github.com/open-policy-agent/opa/rego" +) +``` + +After: + +``` +import ( + "github.com/open-policy-agent/opa/v1/rego" +) +``` + +This will be needed for all OPA packages your application depends on, not just +`rego` and `sdk`, other commonly used packages are: `ast`, `bundle`, `compile`, +`types` & `topdown`. + +As of OPA 1.0, all v0 packages have been deprecated. While they will remain for +the lifetime of OPA 1.0, you are encouraged to upgrade as soon as possible. + +If you need to use v0 functionality, you can still use v1 packages. Please see +the [Backwards Compatibility](./v0-compatibility/) documentation for more +details. diff --git a/third_party/opa/docs/docs/wasm.md b/third_party/opa/docs/docs/wasm.md new file mode 100644 index 000000000000..daba23d775b7 --- /dev/null +++ b/third_party/opa/docs/docs/wasm.md @@ -0,0 +1,382 @@ +--- +title: WebAssembly +sidebar_position: 13 +--- + +# What is WebAssembly (Wasm)? + +As described on [https://webassembly.org/](https://webassembly.org/) + +> WebAssembly (abbreviated Wasm) is a binary instruction format for a +> stack-based virtual machine. Wasm is designed as a portable target for +> compilation of high-level languages like C/C++/Rust, enabling deployment on +> the web for client and server applications. + +## Overview + +OPA is able to compile Rego policies into executable Wasm modules that can be +evaluated with different inputs and external data. This is _not_ running the OPA +server in Wasm, nor is this just cross-compiled Golang code. The compiled Wasm +module is a planned evaluation path for the source policy and query. + +## Current Status + +The core language is supported fully but there are a number of built-in +functions that are not, and probably won't be natively supported in Wasm (e.g., +`http.send`). Built-in functions that are not natively supported can be +implemented in the host environment (e.g., JavaScript). + +## Compiling Policies + +You can compile Rego policies into Wasm modules using the `opa build` subcommand. + +For example, the `opa build` command below compiles the `example.rego` file into a +Wasm module and packages it into an OPA bundle. The `wasm` target requires at least +one entrypoint rule (specified by `-e`, or a metadata `entrypoint` annotation). + +```bash +opa build -t wasm -e example/allow example.rego +``` + +The output of a Wasm module built this way contain the `result` of evaluating the +entrypoint rule. For example: + +```json +[ + { + "result": + } +] +``` + +The output of policy evaluation is a set of variable assignments. The variable +assignments specify values that satisfy the expressions in the policy query +(i.e., if the variables in the query are replaced with the values from the +assignments, all of the expressions in the query would be defined and not +false.) + +When policies are compiled into Wasm, the user provides the path of the policy +decision that should be exposed by the Wasm module. The policy decision is +assigned to a variable named `result`. The policy decision can be ANY JSON value +(boolean, string, object, etc.) but there will be at-most-one assignment. This +means that callers should first check if the set of variable assignments is +empty (indicating an undefined policy decision) otherwise they should select the +`"result"` key out of the variable assignment set. + +> For more information on `opa build` run `opa build --help`. + +### Advanced Compiling Options + +You can also compile Rego policies into Wasm modules from Go using the lower-level +[rego](https://pkg.go.dev/github.com/open-policy-agent/opa/rego#Rego.Compile) API +that produces raw Wasm executables and the higher-level +[compile](https://pkg.go.dev/github.com/open-policy-agent/opa/compile#Compiler.Build) +API that produces OPA bundle files. The compile API is recommended. + +## Using Compiled Policies + +### JavaScript SDK + +There is a JavaScript SDK available that simplifies the process of loading and +evaluating compiled policies. If you want to evaluate Rego policies inside +JavaScript we recommend you use the +[Javascript SDK](https://github.com/open-policy-agent/npm-opa-wasm). +There is also an +[example NodeJS application](https://github.com/open-policy-agent/npm-opa-wasm/tree/master/examples/nodejs-app) +provided for reference. + +### Other Languages + +A number of other languages have OPA Wasm support too via various community SDKs +in the OPA Ecosystem. + +### From Scratch + +If you want to integrate Wasm compiled policies into a language or runtime that +does not have SDK support, read this section. + +#### Instantiating the Wasm Module + +Before you can evaluate Wasm compiled policies you need to instantiate the Wasm +module produced by the compilation process described earlier on this page. + +To load the compiled Wasm module refer the documentation for the Wasm runtime +that you are using. At a high-level you must provide a memory buffer and a set +of import functions. The memory buffer is a contiguous, mutable byte-array that +allows you to pass data to the policy and receive output from the policy. The +import functions are dependencies of the compiled policies. + +#### ABI Versions + +Wasm modules built using OPA 0.27.0 onwards contain a global variable named +`opa_wasm_abi_version` that has a constant i32 value indicating the ABI version +this module requires. Described below you find ABI versions `1.x`. + +There's another i32 constant exported, `opa_wasm_abi_minor_version`, used +to track backwards-compatible changes. + +Using tools like `wasm-objdump` (`wasm-objdump -x policy.wasm`), the ABI +version can be found here: + +``` +Global[3]: + - global[0] i32 mutable=1 - init i32=121904 + - global[1] i32 mutable=0 - init i32=1 + - global[2] i32 mutable=0 - init i32=0 +Export[19]: +[...] + - global[1] -> "opa_wasm_abi_version" + - global[2] -> "opa_wasm_abi_minor_version" +``` + +Note the `i32=1` of `global[1]`, exported by the name of `opa_wasm_abi_version`. + +##### Version notes + +| ABI | Notes | +| --- | --------------------------------------------------------------------------------------------------------------------- | +| 1.0 | Start of ABI versioning. | +| 1.1 | Adds export `memory`. | +| 1.2 | Adds exported function `opa_eval`. | +| 1.3 | Adds exported functions `opa_value_free`, `opa_heap_blocks_stash`, `opa_heap_blocks_restore`, `opa_heap_stash_clear`. | + +#### Exports + +The primary exported functions for interacting with policy modules are listed below. +In the ABI column, you can find the ABI version with which the export was introduced. + +| Function | Description | ABI | +| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- | +| `eval` |
`int32 eval(ctx_addr)`
Evaluates the loaded policy with the provided evaluation context. The return value is reserved for future use.
| 1.0 | +| `builtins` |
`value_addr builtins(void)`
Returns the address of a mapping of built-in function names to numeric identifiers that are required by the policy.
| 1.0 | +| `entrypoints` |
`value_addr entrypoints(void)`
Returns the address of a mapping of entrypoints to numeric identifiers that can be selected when evaluating the policy.
| 1.0 | +| `opa_eval_ctx_new` |
`ctx_addr opa_eval_ctx_new(void)`
Returns the address of a newly allocated evaluation context.
| 1.0 | +| `opa_eval_ctx_set_input` |
`void opa_eval_ctx_set_input(ctx_addr, value_addr)`
Set the input value to use during evaluation. This must be called before each `eval()` call. If the input value is not set before evaluation, references to the `input` document result produce no results (i.e., they are undefined.)
| 1.0 | +| `opa_eval_ctx_set_data` |
`void opa_eval_ctx_set_data(ctx_addr, value_addr)`
Set the data value to use during evaluation. This should be called before each `eval()` call. If the data value is not set before evaluation, references to base `data` documents produce no results (i.e., they are undefined.)
| 1.0 | +| `opa_eval_ctx_set_entrypoint` |
`void opa_eval_ctx_set_entrypoint(ctx_addr, entrypoint_id)`
Set the entrypoint to evaluate. By default, entrypoint with id `0` is evaluated.
| 1.0 | +| `opa_eval_ctx_get_result` |
`value_addr opa_eval_ctx_get_result(ctx_addr)`
Get the result set produced by the evaluation process.
| 1.0 | +| `opa_malloc` |
`addr opa_malloc(int32 size)`
Allocates size bytes in the shared memory and returns the starting address.
| 1.0 | +| `opa_free` |
`void opa_free(addr)`
Free a pointer. Calls `opa_abort` on error.
| 1.0 | +| `opa_json_parse` |
`value_addr opa_json_parse(str_addr, size)`
Parses the JSON serialized value starting at str_addr of size bytes and returns the address of the parsed value. The parsed value may refer to a null, boolean, number, string, array, or object value.
| 1.0 | +| `opa_value_parse` |
`value_addr opa_value_parse(str_addr, size)`
The same as `opa_json_parse` except Rego set literals are supported.
| 1.0 | +| `opa_json_dump` |
`str_addr opa_json_dump(value_addr)`
Dumps the value referred to by `value_addr` to a null-terminated JSON serialized string and returns the address of the start of the string. Rego sets are serialized as JSON arrays. Non-string Rego object keys are serialized as strings.
| 1.0 | +| `opa_value_dump` |
`str_addr opa_value_dump(value_addr)`
The same as `opa_json_dump` except Rego sets are serialized using the literal syntax and non-string Rego object keys are not serialized as strings.
| 1.0 | +| `opa_heap_ptr_set` |
`void opa_heap_ptr_set(addr)`
Set the heap pointer for the next evaluation.
| 1.0 | +| `opa_heap_ptr_get` |
`addr opa_heap_ptr_get(void)`
Get the current heap pointer.
| 1.0 | +| `opa_value_add_path` |
`int32 opa_value_add_path(base_value_addr, path_value_addr, value_addr)`
Add the value at the `value_addr` into the object referenced by `base_value_addr` at the given path. The `path_value_addr` must point to an array value with string keys (e.g.: `["a", "b", "c"]`). Existing values will be updated. On success the value at `value_addr` is no longer owned by the caller, it will be freed with the base value. The path value must be freed by the caller after use by calling `opa_value_free`. (The original path string passed to `opa_json_parse` or `opa_value_parse` to create the value must be freed by calling `opa_free`.) If an error occurs the base value will remain unchanged. Example: base object `{"a": {"b": 123}}`, path `["a", "x", "y"]`, and value `{"foo": "bar"}` will yield `{"a": {"b": 123, "x": {"y": {"foo": "bar"}}}}`. Returns an error code (see below).
| 1.0 | +| `opa_value_remove_path` |
`int32 opa_value_remove_path(base_value_addr, path_value_addr)`
Remove the value from the object referenced by `base_value_addr` at the given path. Values removed will be freed. The path value must be freed by the caller after use by calling `opa_value_free`. (The original path string parsed by `opa_json_parse` or `opa_value_parse` must be released using `opa_free`.) The `path_value_addr` must point to an array value with string keys (e.g.: `["a", "b", "c"]`). Returns an error code (see below).
| 1.0 | +| `opa_value_free` |
`void opa_value_free(value_addr)`
Free a value such as one generated by `opa_value_parse` or `opa_json_parse` reference at `value_addr`
| 1.3 | +| `opa_heap_blocks_stash` |
`void opa_heap_blocks_stash(void)`
Stash free heap blocks in a shadow heap to enable `eval` or `opa_eval` to allocate only blocks that it can subsequently free with a call to `opa_heap_ptr_set`. The caller should subsequently call `opa_heap_ptr_get` and store the value to save before calling `opa_heap_bloks_restore`
| 1.3 | +| `opa_heap_blocks_restore` |
`void opa_heap_blocks_restore(void)`
Restore heap blocks stored by `opa_heap_blocks_stash` to the heap. This should only be called after a `opa_heap_ptr_set` to the a heap pointer recorded by `opa_heap_ptr_get` after the previous call to `opa_heap_blocks_stash`.
| 1.3 | +| `opa_heap_stash_clear` |
`void opa_heap_stash_clear(void)`
Drop all heap blocks saved by `opa_heap_blocks_stash`. This leaks memory in the VM unless the caller subsequently invokes `opa_heap_ptr_set` to a value taken prior to calling `opa_heap_blocks_stash`. (see below)
| 1.3 | +| `opa_eval` |
`str_addr opa_eval(_ addr, entrypoint_id int32, data value_addr, input str_addr, input_len int32, heap_ptr addr, format int32)`
One-off policy evaluation method. Its arguments are everything needed to evaluate: entrypoint, address of data in memory, address and length of input JSON string in memory, heap address to use, and the output format (`0` is JSON, `1` is "value", i.e. serialized Rego values). The first argument is reserved for future use and must be `0`. Returns the address to the serialised result value.
| 1.2 | + +The addresses passed and returned by the policy modules are 32-bit integer +offsets into the shared memory region. The `value_addr` parameters and return +values refer to OPA value data structures: `null`, `boolean`, `number`, +`string`, `array`, `object`, and `set`. + +**Error codes:** + +OPA Wasm Error codes are int32 values defined as: + +| Value | Name | Description | +| ----- | -------------------- | ----------------------------------- | +| 0 | OPA_ERR_OK | No error. | +| 1 | OPA_ERR_INTERNAL | Unrecoverable internal error. | +| 2 | OPA_ERR_INVALID_TYPE | Invalid value type was encountered. | +| 3 | OPA_ERR_INVALID_PATH | Invalid object path reference. | + +#### Imports + +Policy modules require the following function imports at instantiation-time: + +| Namespace | Name | Params | Result | Description | +| --------- | -------------- | ------------------------------------------------------------------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `env` | `opa_abort` | `(addr)` | `void` | Called if an internal error occurs. The `addr` refers to a null-terminated string in the shared memory buffer. | +| `env` | `opa_println` | `(addr)` | `void` | Called to emit a message from the policy evaluation. The `addr` refers to a null-terminated string in the shared memory buffer. | +| `env` | `opa_builtin0` | `(builtin_id, ctx)` | `addr` | Called to dispatch the built-in function identified by the `builtin_id`. The `ctx` parameter reserved for future use. The result `addr` must refer to a value in the shared-memory buffer. The function accepts 0 arguments. | +| `env` | `opa_builtin1` | `(builtin_id, ctx, _1)` | `addr` | Same as previous except the function accepts 1 argument. | +| `env` | `opa_builtin2` | `(builtin_id, ctx, _1, _2)` | `addr` | Same as previous except the function accepts 2 arguments. | +| `env` | `opa_builtin3` | `(builtin_id, ctx, _1, _2, _3)` | `addr` | Same as previous except the function accepts 3 arguments. | +| `env` | `opa_builtin4` | `(builtin_id, ctx, _1, _2, _3, _4)` | `addr` | Same as previous except the function accepts 4 arguments. | + +The policy module also requires a shared memory buffer named `env.memory`. + +#### Memory Buffer + +A shared memory buffer must be provided as an import for the policy module with +the name `env.memory`. The buffer must be large enough to accommodate the input, +provided data, and result of evaluation. + +#### Built-in Functions + +After instantiating the policy module, call the exported `builtins` function to +receive a mapping of built-in functions required during evaluation. The result +maps required built-in function names to the identifiers supplied to the +built-in function callbacks (e.g., `opa_builtin0`, `opa_builtin1`, etc.) + +For example: + +```javascript +const memory = new WebAssembly.Memory({ initial: 5 }); +const policy_module = await WebAssembly.instantiate( + byte_buffer, + /* import object */ +); +const addr = policy_module.instance.exports.builtins(); +const str_addr = policy_module.instance.exports.opa_json_dump(addr); +const builtin_map = deserialize_null_terminated_JSON_string(memory, str_addr); +``` + +The built-in function mapping will contain all of the built-in functions that +may be required during evaluation. For example, the following query refers to +the `http.send` built-in function which is not included in the policy module: + +```rego +result := http.send({"method": "get", "url": "https://example.com/api/lookup/12345"}) +``` + +If this query was compiled to Wasm the built-in map would contain a single +element: + +```json +{ + "http.send": 0 +} +``` + +When the evaluation runs, the `opa_builtin1` callback would invoked with +`builtin_id` set to `0`. + +#### Evaluation + +Once instantiated, the policy module is ready to be evaluated. Use the +`opa_eval_ctx_new` exported function to create an evaluation context. Use the +`opa_eval_ctx_set_input` and `opa_eval_ctx_set_data` exported functions to specify +the values of the `input` and base `data` documents to use during evaluation. + +To evaluate, call to the exported `eval` function with the eval context address +as the only parameter. + +#### Input + +The (optional) `input` document for a policy can be provided by loading a JSON +string into the shared memory buffer. Use the `opa_malloc` exported function to +allocate a buffer the size of the JSON string and copy the contents in at the +returned address. After the raw string is loaded into memory you will need to +call the `opa_json_parse` exported method to get an address to the parsed input +document for use in evaluations. Set the address via the +`opa_eval_ctx_set_input` exported function supplying the evaluation context +address and parsed input document address. + +#### External Data + +External data can be loaded for use in evaluation. Similar to the `input` this +is done by loading a JSON string into the shared memory buffer. Use `opa_malloc` +and `opa_json_parse` followed by `opa_eval_ctx_set_data` to set the address on +the evaluation context. + +Data can be updated by using the `opa_value_add_path` and `opa_value_remove_path` +and providing the same value address as the base. Similarly, use `opa_malloc` and +`opa_json_parse` for the updated value and creating the path. + +After loading the external data use the `opa_heap_ptr_get` exported method to save +the current point in the heap before evaluation. After evaluation this should be +reset by calling `opa_heap_ptr_set` to ensure that evaluation restarts back at the +saved data and re-uses heap space. This is particularly important if re-evaluating many +times with the same data. + +If you want to continue to update data in between query evaluations then the calling +convention is a little more sophisticated due to the way that `eval` and `opa_eval` +release temporary memory between queries. The functions `opa_heap_blocks_stash` and +`opa_heap_blocks_restore` provide a safe way to stash free heap memory during queries +and then restore it for use when adding or removing further external data. Without +using these, the `eval` and `opa_eval` calls will leak all heap blocks below the +heap pointer. The calling convention is as follows: + +- It's always prudent at VM initialization to call `opa_malloc` with a size of 0 + and then `opa_heap_ptr_get` to obtain the initial value of the heap pointer for the + VM. Call this the "initial heap pointer". +- If you never load external data, you can use the "initial heap pointer" as your + "data heap pointer" for calls to `eval` or `opa_eval`. +- On the first load of external data, after calling `opa_heap_ptr_get` also call + `opa_heap_blocks_stash`. This will save free heap memory for reuse later and prevent + calls to `eval` or `opa_eval` from leaking that memory. Call the saved heap pointer + after the initial data document load the "data heap pointer". This "data heap + pointer" is the value that should be used in `eval` or `opa_eval` calls. +- On subsequent calls to modify the data document using `opa_value_add_path` or + `opa_value_remove_path`, do the following: + 1. Call `opa_heap_ptr_set` passing the "data heap pointer" to reset the heap + and clear any memory left from `eval` or `opa_eval` calls. + 2. Call `opa_heap_blocks_restore` to reinstate the heap stashed heap memory. + 3. Call `opa_malloc`/`opa_json_parse`/`opa_free` to create the "path" and + "value" arguments (in WASM value form) as usual. + 4. Call `opa_value_add_path` or `opa_value_remove_path` as usual. + 5. Call `opa_value_free` on the "path" argument to release it as usual. + 6. Call `opa_value_blocks_stash` to stash any free heap blocks to + protect them during `eval` or `opa_eval` calls. + 7. Call `opa_heap_ptr_get` to get a new "data heap pointer". It may + be larger or smaller than the previous value depending upon which + internal memory the calls allocated or released. +- The calling convention for `eval` and `opa_eval` don't change at all. +- If, at any point, you wish to reset the VM to an initial state with regard + to the policy data then do the following: + 1. Call `opa_heap_stash_clear` to drop all stashed heap blocks (if any). + 2. Call `opa_heap_ptr_set` with the "initial heap pointer" to reset the + heap to its initial state. + 3. Use the "initial heap pointer" as your new "data heap pointer" until + the next time you add external data in the VM. + +It might seem counter-intuitive to hide available heap memory from calls +to `eval` or `opa_eval`. But that memory was never truly available for +queries in the first place. The very first call to `opa_heap_ptr_set` +(either before `eval` or which `opa_eval` calls internally) resets the heap +and leaks any free blocks on the heap. In versions of the ABI prior to +1.3 this memory was simply lost. Note, however, that multiple queries +would not continue to leak memory since they would always reset the heap +pointer to the same value. The WASM engine would only leak further +memory if there were subsequent calls to `opa_value_add_path` or +`opa_value_remove_path` followed by more queries. ABI 1.3 introduced the +calling convention using `opa_heap_blocks_stash` and +`opa_heap_blocks_restore` to allow for interleaving query evaluations +with incremental data document modifications. + +#### Entrypoints + +The compiled policy may have one or more entrypoints. If no entrypoint is set +on the evaluation context the default entrypoint (`0`) will be evaluated. SDKs +can call `entrypoints()` after instantiating the module to retrieve the +entrypoint name to entrypoint identifier mapping. SDKs can set the entrypoint to +evaluate by calling `opa_eval_ctx_set_entrypoint` on the evaluation context. If +an invalid entrypoint identifier is passed, the `eval` function will invoke `opa_abort`. + +#### Results + +After evaluation results can be retrieved via the exported +`opa_eval_ctx_get_result` function. Pass in the evaluation context address. The +return value is an address in the shared memory buffer to the structured result. +To access the JSON result use the `opa_json_dump` exported function to retrieve +a pointer in shared memory to a null terminated JSON string. + +The result of evaluation is the set variable bindings that satisfy the +expressions in the query. For example, the query `x = 1; y = 2; y > x` would +produce the following result set: + +```json +[ + { + "x": 1, + "y": 2 + } +] +``` + +Sets are represented as JSON arrays. + +## Ecosystem Projects + + +Wasm is a great way to integrate OPA into applications where the Go SDK is unavailable. + diff --git a/third_party/opa/docs/docusaurus.config.js b/third_party/opa/docs/docusaurus.config.js new file mode 100644 index 000000000000..ef31bdc34bea --- /dev/null +++ b/third_party/opa/docs/docusaurus.config.js @@ -0,0 +1,508 @@ +const { themes } = require("prism-react-renderer"); +const lightCodeTheme = themes.github; +const darkCodeTheme = themes.dracula; +const semver = require("semver"); +import fs from "fs/promises"; +const path = require("path"); + +const { loadPages } = require("./src/lib/ecosystem/loadPages"); + +const baseUrl = "/"; + +// With JSDoc @type annotations, IDEs can provide config autocompletion +/** @type {import("@docusaurus/types").DocusaurusConfig} */ +( + module.exports = { + title: "Open Policy Agent", + tagline: "Policy-based control for cloud native environments", + url: "https://openpolicyagent.org", + baseUrl: baseUrl, + // Build-time options + onBrokenLinks: "throw", + onBrokenMarkdownLinks: "throw", + trailingSlash: false, + presets: [ + [ + "@docusaurus/preset-classic", + /** @type {import("@docusaurus/preset-classic").Options} */ + { + docs: { + path: "docs", + routeBasePath: "/docs/", + breadcrumbs: false, + sidebarPath: require.resolve("./src/lib/sidebars.js"), + }, + blog: false, + theme: { + customCss: require.resolve("./src/css/custom.css"), + }, + gtag: { + trackingID: "G-JNBNV64PDX", + anonymizeIP: true, + }, + }, + ], + ], + + customFields: { + buildVersion: process.env.BUILD_VERSION, + }, + + markdown: { + mermaid: true, + }, + themes: ["@docusaurus/theme-mermaid"], + + themeConfig: { + colorMode: { + defaultMode: "light", + disableSwitch: false, + respectPrefersColorScheme: true, + }, + metadata: [ + { name: "msapplication-TileColor", content: "#2b5797" }, + { name: "theme-color", content: "#ffffff" }, + ], + headTags: [ + { + tagName: "link", + attributes: { + rel: "icon", + type: "image/png", + href: "/favicon-96x96.png", + sizes: "96x96", + }, + }, + { + tagName: "link", + attributes: { + rel: "icon", + type: "image/svg+xml", + href: "/favicon.svg", + }, + }, + { + tagName: "link", + attributes: { + rel: "shortcut icon", + href: "/favicon.ico", + }, + }, + { + tagName: "link", + attributes: { + rel: "apple-touch-icon", + sizes: "180x180", + href: "/apple-touch-icon.png", + }, + }, + { + tagName: "meta", + attributes: { + name: "apple-mobile-web-app-title", + content: "OPA", + }, + }, + { + tagName: "link", + attributes: { + rel: "manifest", + href: "/site.webmanifest", + }, + }, + ], + navbar: { + title: "Open Policy Agent", + logo: { + alt: "OPA Logo", + src: "img/nav/logo.png", + }, + items: [ + { + // Based on implementation outlined here: + // https://github.com/facebook/docusaurus/issues/7227#issue-1212117180 + type: "custom-currentVersionNavbarItem", + position: "left", + }, + { to: "/docs/", label: "Docs", position: "right" }, + { + type: "dropdown", + label: "Resources", + position: "right", + items: [ + { to: "/security", label: "Security" }, + { to: "/support", label: "Support" }, + { to: "/community", label: "Community" }, + { href: "https://blog.openpolicyagent.org/", label: "Blog" }, + ], + }, + { to: "/ecosystem/", label: "Ecosystem", position: "right" }, + { href: "https://play.openpolicyagent.org/", label: "Play", position: "right" }, + { + type: "html", + position: "right", + value: ` +
+ GitHub + GitHub + + `, + }, + { + type: "html", + position: "right", + value: ` + + Slack + Slack + + `, + }, + ], + }, + footer: { + style: "light", + links: [], + copyright: + `Open Policy Agent is a Cloud Native Computing Foundation Graduated project. + +CNCF Logo +CNCF Logo +
+ +© ${new Date().getFullYear()} +Open Policy Agent contributors. +Licensed under the Apache License, Version 2.0. +See the contributing documentation for information about contributing. + +The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page.`, + }, + prism: { + theme: lightCodeTheme, + darkTheme: darkCodeTheme, + additionalLanguages: [ + "rego", + "hcl", + "json", + "java", + "scala", + "gradle", + "javadoc", + "sql", + "http", + "diff", + "typescript", + "ini", + "cypher", + "csharp", + "shell-session", + "go-module", + "docker", + "javastacktrace", + "properties", + "log", + ], + magicComments: [ + { + className: "code-block-terminal-command", + line: "terminal-command", + }, + { + className: "code-block-terminal-command", + line: "cmd", + }, + { + className: "code-block-diff-add-line", + line: "diff-add", + block: { start: "diff-add-start", end: "diff-add-end" }, + }, + { + className: "code-block-diff-remove-line", + line: "diff-remove", + block: { start: "diff-remove-start", end: "diff-remove-end" }, + }, + { + className: "theme-code-block-highlighted-line", + line: "highlight-next-line", + block: { start: "highlight-start", end: "highlight-end" }, + }, + { + className: "code-block-error-line", + line: "error-next-line", + block: { start: "error-start", end: "error-end" }, + }, + ], + }, + mermaid: { + theme: { light: "base", dark: "dark" }, + options: { + themeVariables: { // https://mermaid.js.org/config/theming.html#theme-variables + fontFamily: "sans-serif", + primaryColor: "#76d3ed", + secondaryColor: "#fff", + tertiaryColor: "#fff", + }, + }, + }, + }, + + plugins: [ + [ + require.resolve("@easyops-cn/docusaurus-search-local"), + { + indexPages: true, + }, + ], + () => ({ + name: "raw-loader", + configureWebpack() { + return { + module: { + rules: [ + { test: /\.rego$/, use: "raw-loader" }, + { test: /\.mermaid$/, use: "raw-loader" }, + { test: /\.txt$/, use: "raw-loader" }, + ], + }, + }; + }, + }), + async function ecosystemLanguagePageGen(context, options) { + return { + name: "ecosystem-language-gen", + async loadContent() { + const languages = await loadPages(path.join(context.siteDir, "src/data/ecosystem/languages/*.md")); + return { languages }; + }, + + async contentLoaded({ content, actions }) { + const { pagesByLanguage, languages } = content; + await Promise.all( + Object.keys(languages).map(async (language) => { + const routePath = path.join(baseUrl, `/ecosystem/by-language/${language}`); + return actions.addRoute({ + path: routePath, + component: require.resolve("./src/EcosystemLanguage.js"), + exact: true, + modules: {}, + customData: { language }, + }); + }), + ); + }, + }; + }, + + async function ecosystemFeaturePageGen(context, options) { + return { + name: "ecosystem-feature-gen", + async loadContent() { + const features = await loadPages(path.join(context.siteDir, "src/data/ecosystem/features/*.md")); + + return { features }; + }, + + async contentLoaded({ content, actions }) { + const { features } = content; + await Promise.all( + Object.keys(features).map(async (feature) => { + const routePath = path.join(baseUrl, `/ecosystem/by-feature/${feature}`); + return actions.addRoute({ + path: routePath, + component: require.resolve("./src/EcosystemFeature.js"), + exact: true, + modules: {}, + customData: { feature }, + }); + }), + ); + }, + }; + }, + + async function ecosystemData(context, options) { + return { + name: "ecosystem-data", + + async loadContent() { + const entries = await loadPages(path.join(context.siteDir, "src/data/ecosystem/entries/*.md")); + const languages = await loadPages(path.join(context.siteDir, "src/data/ecosystem/languages/*.md")); + const features = await loadPages(path.join(context.siteDir, "src/data/ecosystem/features/*.md")); + const featureCategories = await loadPages( + path.join(context.siteDir, "src/data/ecosystem/feature-categories/*.md"), + ); + + return { + entries, + languages, + features, + featureCategories, + }; + }, + + async contentLoaded({ content, actions }) { + const { createData } = actions; + const { entries, languages, features, featureCategories } = content; + + await createData("entries.json", JSON.stringify(entries, null, 2)); + await createData("languages.json", JSON.stringify(languages, null, 2)); + await createData("features.json", JSON.stringify(features, null, 2)); + await createData("feature-categories.json", JSON.stringify(featureCategories, null, 2)); + }, + }; + }, + + async function builtinData(context, options) { + return { + name: "builtin-data", + + async loadContent() { + const filePath = "../builtin_metadata.json"; + const fileContent = await fs.readFile(filePath, "utf-8"); + const builtins = JSON.parse(fileContent); + return { builtins }; + }, + + async contentLoaded({ content, actions }) { + const { createData } = actions; + const { builtins } = content; + + await createData("builtins.json", JSON.stringify(builtins, null, 2)); + }, + }; + }, + + async function ecosystemPagesGen(context, options) { + return { + name: "ecosystem-entries-pages-gen", + async loadContent() { + const entries = await loadPages(path.join(context.siteDir, "src/data/ecosystem/entries/*.md")); + return { entries }; + }, + + async contentLoaded({ content, actions }) { + const { entries } = content; + + await Promise.all( + Object.values(entries).map(async (entry) => { + const routePath = path.join(baseUrl, `/ecosystem/entry/${entry.id}`); + return actions.addRoute({ + path: routePath, + component: require.resolve("./src/EcosystemEntry.js"), + exact: true, + modules: {}, + customData: { id: entry.id }, + }); + }), + ); + }, + }; + }, + + async function versionsData(context, options) { + return { + name: "versions-data", + + async loadContent() { + const capabilitiesDir = path.resolve(__dirname, "../capabilities"); + let sortedVersions = []; + + const dirents = await fs.readdir(capabilitiesDir, { withFileTypes: true }); + + const versionStrings = dirents + .filter(dirent => dirent.isFile() && dirent.name.endsWith(".json")) + .map(dirent => dirent.name.replace(".json", "")); + + const validVersions = versionStrings.filter(v => semver.valid(v)); + + sortedVersions = semver.sort(validVersions); + + return { versions: sortedVersions }; + }, + + async contentLoaded({ content, actions }) { + const { createData } = actions; + const { versions } = content; + + await createData("versions.json", JSON.stringify(versions, null, 2)); + + const staticDir = path.join(context.siteDir, "static", "data"); + await fs.mkdir(staticDir, { recursive: true }); + await fs.writeFile( + path.join(staticDir, "versions.json"), + JSON.stringify(versions, null, 2), + ); + }, + }; + }, + + async function cliData(context, options) { + return { + name: "cli-data", + + async loadContent() { + const filePath = path.join(context.siteDir, "src/data/cli.json"); + const cliJson = await fs.readFile(filePath, "utf-8"); + const parsedData = JSON.parse(cliJson); + + return parsedData; + }, + + async contentLoaded({ content, actions }) { + const { createData } = actions; + + await createData("cli.json", JSON.stringify(content, null, 2)); + }, + }; + }, + + async function versionsPageGen(context, options) { + return { + name: "version-page-gen", + async contentLoaded({ content, actions }) { + return actions.addRoute({ + path: path.join(baseUrl, `/docs/archive`), + component: require.resolve("./src/Archive.js"), + exact: true, + modules: {}, + }); + }, + }; + }, + ], + clientModules: [ + require.resolve("./src/lib/playground.js"), + ], + stylesheets: [ + { + href: "https://unpkg.com/@antonz/codapi@0.19.8/dist/snippet.css", + }, + ], + scripts: [ + { + src: "https://unpkg.com/@antonz/codapi@0.19.8/dist/snippet.js", + defer: true, + }, + ], + } +); diff --git a/third_party/opa/docs/functions/badge.ts b/third_party/opa/docs/functions/badge.ts new file mode 100644 index 000000000000..7136cbdc92a0 --- /dev/null +++ b/third_party/opa/docs/functions/badge.ts @@ -0,0 +1,56 @@ +import { Context } from "netlify:edge"; + +const schemaVersion = 1; +const label = "OPA"; +const releases = "https://api.github.com/repos/open-policy-agent/opa/releases/latest"; + +// OPA logo SVG +const logoSvg = + ""; + +export default async (req: Request, context: Context) => { + const url = new URL(req.url); + const host = req.headers.get("host"); + + const pathParts = url.pathname.split("/").filter(Boolean); // remove empty parts + const query = url.searchParams; + + // Handle /badge/config/:tag + if (pathParts.length === 3 && pathParts[0] === "badge" && pathParts[1] === "config") { + const tag = pathParts[2]; + + const latest = await fetch(releases) + .then((response) => response.json()) + .then((data) => data.tag_name) + .catch(() => "unknown"); + + const res = { + schemaVersion, + label, + logoSvg, + message: tag, + color: latest === tag ? "green" : "yellow", + }; + + return context.json(res); + } + + // Handle /badge/:tag + if (pathParts.length === 2 && pathParts[0] === "badge") { + const tag = pathParts[1]; + const style = query.get("style"); + + // Build dynamic badge endpoint URL using current host + const base = `https://${host}/badge/config/${tag}`; + const encodedUrl = encodeURIComponent(base); + + let redirectUrl = `https://img.shields.io/endpoint?url=${encodedUrl}`; + if (style) { + redirectUrl += `&style=${encodeURIComponent(style)}`; + } + + return Response.redirect(redirectUrl, 302); + } + + return new Response("Not Found", { status: 404 }); +}; diff --git a/third_party/opa/docs/functions/version-redirect.ts b/third_party/opa/docs/functions/version-redirect.ts new file mode 100644 index 000000000000..302ee57e4a67 --- /dev/null +++ b/third_party/opa/docs/functions/version-redirect.ts @@ -0,0 +1,37 @@ +// Redirect path versioned requests to archived versions of the OPA +// documentation. +// Context: https://github.com/open-policy-agent/opa/issues/7037 +import { Context } from "@netlify/edge-functions"; + +export default async ( + request: Request, + context: Context, +): Promise => { + const url: URL = new URL(request.url); + const pathname: string = url.pathname; + + // this is the name of the archived site project and forms the base of all + // archived sites. + const siteName: string = "opa-docs"; + + const versionRegex: RegExp = /^\/docs\/v(\d+\.\d+\.\d+)(\/.*)?$/; + const match: RegExpMatchArray | null = pathname.match(versionRegex); + + if (match) { + const version: string = match[1]; // e.g., "0.65.0" + const restOfPath: string = match[2] || "/"; // e.g., "/introduction" or defaults to "/" + + // Format version for the Netlify subdomain: replace dots with dashes + const formattedVersion: string = version.replace(/\./g, "-"); // e.g., "0-65-0" + + // Construct the target archive URL + const targetDomain: string = `v${formattedVersion}--${siteName}.netlify.app`; + const targetUrl: string = `https://${targetDomain}${restOfPath}`; + + console.log(`Edge Function: Redirecting ${pathname} to ${targetUrl}`); + + return Response.redirect(targetUrl, 301); + } + + return undefined; +}; diff --git a/third_party/opa/docs/package.json b/third_party/opa/docs/package.json new file mode 100644 index 000000000000..38fae8dc8328 --- /dev/null +++ b/third_party/opa/docs/package.json @@ -0,0 +1,27 @@ +{ + "name": "opa-website", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@docusaurus/core": "^3.8.1", + "@docusaurus/plugin-google-gtag": "^3.8.1", + "@docusaurus/preset-classic": "^3.8.1", + "@docusaurus/theme-mermaid": "^3.8.1", + "@easyops-cn/docusaurus-search-local": "^0.49.2", + "@iconify/react": "^6.0.0", + "docusaurus-lunr-search": "^3.6.0", + "md-front-matter": "^1.0.4", + "raw-loader": "^4.0.2", + "react-markdown": "^10.1.0" + }, + "engines": { + "node": ">=22.0.0" + } +} diff --git a/third_party/opa/docs/src/Archive.js b/third_party/opa/docs/src/Archive.js new file mode 100644 index 000000000000..1c335cd3e1e4 --- /dev/null +++ b/third_party/opa/docs/src/Archive.js @@ -0,0 +1,164 @@ +import React from "react"; + +import Admonition from "@theme/Admonition"; +import Heading from "@theme/Heading"; +import Layout from "@theme/Layout"; + +const semver = require("semver"); + +import versions from "@generated/versions-data/default/versions.json"; + +const Archive = (props) => { + const title = "OPA Documentation Archive"; + + const lastOldDocsVersion = "v1.4.2"; + const oldDocsVersions = [ + "v0.11.0", + "v0.12.2", + "v0.13.5", + "v0.14.2", + "v0.15.1", + "v0.16.2", + "v0.17.3", + "v0.18.0", + "v0.19.2", + "v0.20.5", + "v0.21.1", + "v0.22.0", + "v0.23.2", + "v0.24.0", + "v0.25.2", + "v0.26.0", + "v0.27.1", + "v0.28.0", + "v0.29.4", + "v0.30.2", + "v0.31.0", + "v0.32.1", + "v0.33.1", + "v0.34.2", + "v0.35.0", + "v0.36.1", + "v0.37.2", + "v0.38.1", + "v0.39.0", + "v0.40.0", + "v0.41.0", + "v0.42.2", + "v0.43.1", + "v0.44.0", + "v0.45.0", + "v0.46.3", + "v0.47.4", + "v0.48.0", + "v0.49.2", + "v0.50.2", + "v0.51.0", + "v0.52.0", + "v0.53.1", + "v0.54.0", + "v0.55.0", + "v0.56.0", + "v0.57.1", + "v0.58.0", + "v0.59.0", + "v0.60.0", + "v0.61.0", + "v0.62.1", + "v0.63.0", + "v0.64.1", + "v0.65.0", + "v0.66.0", + "v0.67.1", + "v0.68.0", + "v0.69.0", + "v0.70.0", + "v1.0.1", + "v1.1.0", + "v1.2.0", + "v1.3.0", + "v1.4.2", + ]; + + const firstDocsVersion = semver.valid("0.17.2"); + + // We only show the latest patch for each minor release + const getLatestPatchVersions = (versions) => { + const versionGroups = {}; + + versions.forEach(version => { + const parsed = semver.parse(version); + if (!parsed) return; + + const majorMinorKey = `${parsed.major}.${parsed.minor}`; + + if (!versionGroups[majorMinorKey] || semver.gt(version, versionGroups[majorMinorKey])) { + versionGroups[majorMinorKey] = version; + } + }); + + return Object.values(versionGroups); + }; + + // A known list of old docs versions are shown, otherwise, any newer version + // is shown. + const filteredVersions = versions.slice().filter(version => { + return semver.gt(version, lastOldDocsVersion) || oldDocsVersions.includes(version); + }); + + // For versions > lastOldDocsVersion, group by minor and get only latest patch + // For versions in oldDocsVersions, keep as is + const newerVersions = filteredVersions.filter(v => semver.gt(v, lastOldDocsVersion)); + const olderVersions = filteredVersions.filter(v => oldDocsVersions.includes(v)); + + const latestNewerVersions = getLatestPatchVersions(newerVersions); + + // Combine all selected versions, and sort them newest to oldest + const descVersions = [...latestNewerVersions, ...olderVersions].sort(semver.rcompare); + + const getArchiveUrl = (version) => { + const urlVersionPart = version.replaceAll(".", "-"); + return `https://${urlVersionPart}--opa-docs.netlify.app/`; + }; + + return ( + +
+ {title} + + + Pre-release documentation is available on the{" "} + edge deployment. + + +

+ Please find links to past versions of the OPA Documentation here. Note that only the latest patch within a + minor release is shown. +

+ +
+ +
+
+
+ ); +}; + +export default Archive; diff --git a/third_party/opa/docs/src/EcosystemEntry.js b/third_party/opa/docs/src/EcosystemEntry.js new file mode 100644 index 000000000000..5afcb879561e --- /dev/null +++ b/third_party/opa/docs/src/EcosystemEntry.js @@ -0,0 +1,212 @@ +import Heading from "@theme/Heading"; +import Layout from "@theme/Layout"; +import React from "react"; +import ReactMarkdown from "react-markdown"; + +import entries from "@generated/ecosystem-data/default/entries.json"; +import getLogoAsset from "./lib/ecosystem/getLogoAsset.js"; + +function humanizeSegment(segment) { + const words = segment.replace(/-/g, " ").split(" "); + + // Filter out words with more than 3 digits, this is a heuristic to avoid URL + // ids and so on. + const filteredWords = words.filter((word) => { + const digitCount = (word.match(/\d/g) || []).length; + return digitCount <= 3; + }); + + return filteredWords + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .map((word) => { + if (word.toLowerCase() === "opa") return "OPA"; + return word.charAt(0).toUpperCase() + word.slice(1); + }) + .join(" "); +} + +function getHumanLabelFromUrl(url) { + try { + const parsed = new URL(url); + const domain = parsed.hostname.replace(/^www\./, ""); + + const parts = parsed.pathname + .split("/") + .filter(Boolean) + .map(humanizeSegment); + + const label = parts.length > 0 ? parts.join(" > ") : domain; + + return { domain, label }; + } catch (err) { + console.error(err); + return { domain: "", label: url }; + } +} + +const HumanLink = ({ href }) => { + const { domain, label } = getHumanLabelFromUrl(href); + + return ( + <> + + {label} + {" "} + ({domain}) + + ); +}; + +const EcosystemEntry = (props) => { + const { id } = props.route.customData; + const page = entries[id]; + + const { + title, + subtitle, + labels, + inventors, + blogs, + code, + videos, + tutorials, + content, + } = page; + + return ( + +
+
+ {`${title} + + + {title} + +
+ + {subtitle &&

{subtitle}

} + + {/* Content (Markdown) */} + {content && ( +
+
+ + {content} + +
+
+ )} + + {/* Inventors */} + {/* Disabled until we have inventor pages */} + {false && inventors?.length > 0 && ( +
+ Inventors: {inventors.join(", ")} +
+ )} + + {/* Blogs */} + {blogs?.length > 0 && ( +
+ Blogs: +
    + {blogs.map((url, idx) => ( +
  • + +
  • + ))} +
+
+ )} + + {/* Code */} + {code?.length > 0 && ( +
+ Code: +
    + {code.map((url, idx) => ( +
  • + +
  • + ))} +
+
+ )} + + {/* Videos */} + {videos?.length > 0 && ( +
+ Videos: + +
+ )} + + {/* Tutorials */} + {tutorials?.length > 0 && ( +
+ Tutorials: +
    + {tutorials.map((url, idx) => ( +
  • + +
  • + ))} +
+
+ )} + + {/* Labels */} + {labels && ( +
+ Category: {labels.category}
+ Layer: {labels.layer} +
+ )} +
+
+ ); +}; + +export default EcosystemEntry; diff --git a/third_party/opa/docs/src/EcosystemFeature.js b/third_party/opa/docs/src/EcosystemFeature.js new file mode 100644 index 000000000000..d9c74de61ac9 --- /dev/null +++ b/third_party/opa/docs/src/EcosystemFeature.js @@ -0,0 +1,78 @@ +import Heading from "@theme/Heading"; +import Layout from "@theme/Layout"; +import React from "react"; +import ReactMarkdown from "react-markdown"; + +import Card from "./components/Card"; +import CardGrid from "./components/CardGrid"; + +import getLogoAsset from "./lib/ecosystem/getLogoAsset.js"; +import sortPagesByRank from "./lib/ecosystem/sortPagesByRank.js"; + +import entries from "@generated/ecosystem-data/default/entries.json"; +import featureCategories from "@generated/ecosystem-data/default/feature-categories.json"; +import features from "@generated/ecosystem-data/default/features.json"; +import languages from "@generated/ecosystem-data/default/languages.json"; + +const EcosystemFeature = (props) => { + const { feature } = props.route.customData; + + const pagesByFeature = {}; + + for (const pageId in entries) { + const page = entries[pageId]; + const features = page.docs_features || {}; + + for (const featureKey of Object.keys(features)) { + if (!pagesByFeature[featureKey]) { + pagesByFeature[featureKey] = []; + } + + pagesByFeature[featureKey].push(page); + } + } + + const pages = pagesByFeature[feature] || []; + + const sortedPages = sortPagesByRank(pages); + + const title = features[feature].title; + const content = features[feature].content; + + return ( + +
+ + {title} + + {content && ( +
+
+ + {content} + +
+
+ )} + + + {sortedPages.map((id) => { + const page = pages[id]; + + const cardData = { + title: page.title, + note: page.docs_features[feature].note, + icon: getLogoAsset(page.id), + link: `/ecosystem/entry/${page.id}`, + link_text: "View Details", + }; + + return ; + })} + +
+
+ ); +}; + +export default EcosystemFeature; diff --git a/third_party/opa/docs/src/EcosystemLanguage.js b/third_party/opa/docs/src/EcosystemLanguage.js new file mode 100644 index 000000000000..c0899a50fd22 --- /dev/null +++ b/third_party/opa/docs/src/EcosystemLanguage.js @@ -0,0 +1,75 @@ +import Heading from "@theme/Heading"; +import Layout from "@theme/Layout"; +import React from "react"; +import ReactMarkdown from "react-markdown"; + +import Card from "./components/Card"; +import CardGrid from "./components/CardGrid"; + +import getLogoAsset from "./lib/ecosystem/getLogoAsset.js"; +import sortPagesByRank from "./lib/ecosystem/sortPagesByRank.js"; + +import entries from "@generated/ecosystem-data/default/entries.json"; +import featureCategories from "@generated/ecosystem-data/default/feature-categories.json"; +import features from "@generated/ecosystem-data/default/features.json"; +import languages from "@generated/ecosystem-data/default/languages.json"; + +const EcosystemFeature = (props) => { + const { language } = props.route.customData; + + const pagesByLanguage = {}; + + for (const pageId in entries) { + const page = entries[pageId]; + const lang = page.for_language; + if (!lang) continue; + if (!pagesByLanguage[lang]) { + pagesByLanguage[lang] = []; + } + pagesByLanguage[lang].push(page); + } + + const pages = pagesByLanguage[language] || []; + + const sortedPages = sortPagesByRank(pages); + + const title = languages[language].title; + const content = languages[language].content; + + return ( + +
+ + {title} + + {content && ( +
+
+ + {content} + +
+
+ )} + + + {sortedPages.map((id) => { + const page = pages[id]; + + const cardData = { + title: page.title, + note: page.subtitle, + icon: getLogoAsset(page.id), + link: `/ecosystem/entry/${page.id}`, + link_text: "View Details", + }; + + return ; + })} + +
+
+ ); +}; + +export default EcosystemFeature; diff --git a/third_party/opa/docs/src/components/BuiltinLegacyRedirect/index.js b/third_party/opa/docs/src/components/BuiltinLegacyRedirect/index.js new file mode 100644 index 000000000000..9f33e75269d2 --- /dev/null +++ b/third_party/opa/docs/src/components/BuiltinLegacyRedirect/index.js @@ -0,0 +1,16 @@ +import { Redirect, useLocation } from "@docusaurus/router"; +import React from "react"; + +// This component redirects the old built-in links to the new category pages, +// https://www.openpolicyagent.org/docs/policy-reference#builtin-comparison-equal leads to +// https://www.openpolicyagent.org/docs/policy-reference/builtins/comparison#builtin-comparison-equal +// Should be removed around version 1.12 or 1.13, only used in docs/policy-reference/index.md +export default function BuiltinLegacyRedirect({}) { + const loc = useLocation(); + const hash = loc.hash.replace("#", "").split("-"); + if (hash.length == 3 && hash[0] == "builtin") { + const newLoc = `/docs/policy-reference/builtins/${hash[1]}${loc.hash}`; + return ; + } + return
; +} diff --git a/third_party/opa/docs/src/components/BuiltinSearch/index.js b/third_party/opa/docs/src/components/BuiltinSearch/index.js new file mode 100644 index 000000000000..2895976da42c --- /dev/null +++ b/third_party/opa/docs/src/components/BuiltinSearch/index.js @@ -0,0 +1,154 @@ +import builtins from "@generated/builtin-data/default/builtins.json"; +import React, { useMemo, useState } from "react"; +import ReactMarkdown from "react-markdown"; + +import styles from "./styles.module.css"; + +export default function BuiltinSearch({ entryLimit, alwaysShow, elementId }) { + const [searchTerm, setSearchTerm] = useState(""); + const [selectedCategory, setSelectedCategory] = useState(""); + const [isWasm, setIsWasm] = useState(false); + const isFiltered = isWasm || searchTerm || selectedCategory || alwaysShow; + const displayLimit = entryLimit; + const allRows = useMemo(() => { + return (Object.keys(builtins._categories).filter((a) => (a != "internal")) + .map((category) => { + return builtins._categories[category] + .filter((builtin) => ((!builtins[builtin].deprecated ?? false) && (builtins[builtin]))) + .map((builtin) => { + const fn = builtins[builtin]; + + const anchor = `/docs/policy-reference/builtins/${category}#builtin-${category}-${ + builtin.replaceAll(".", "") + }`; + + const isInfix = !!fn.infix; + const isRelation = !!fn.relation; + const args = fn.args || []; + const result = fn.result || {}; + + const signature = isInfix + ? `${args[0]?.name || "x"} ${fn.infix} ${args[1]?.name || "y"}` + : isRelation + ? `${builtin}(${args.map((a) => a.name).join(", ")}, ${result.name})` + : `${result.name || "result"} := ${builtin}(${args.map((a) => a.name).join(", ")})`; + return { + name: builtin, + category: category, + wasm: fn.wasm, + introduced: fn.introduced, + infix: isInfix, + signature: signature, + anchor: anchor, + versions: fn.available, + }; + }); + })).reduce((a, b) => a.concat(b), []); // builds and flattens the function list + }); + const filteredRows = allRows.filter((row) => ( + (row.name.toLowerCase().includes(searchTerm.toLowerCase()) + || row.name.replace(".", "").toLowerCase().includes(searchTerm.toLowerCase()) + || (row.signature.includes(searchTerm) && row.infix)) // filter name + && (!selectedCategory || row.category == selectedCategory) // filter category + && (!isWasm || row.wasm) // filter wasm + && isFiltered // ensures that at least one criteria is being filtered on + )); + return ( + <> +
+ + setSearchTerm(e.target.value)} + /> + setIsWasm(!isWasm)}> + {isWasm ? "✓" : "✗"} Wasm Only + +
+ {isFiltered && renderResults(filteredRows, entryLimit)} + + ); +} + +function renderResults(filteredRows, entryLimit) { + if (filteredRows.length == 0) return

No matches

; + if (!entryLimit || filteredRows.length <= entryLimit) return (renderTable(filteredRows)); + return

Currently {filteredRows.length} matches, keep typing to narrow it down

; +} + +function renderTable(filteredRows) { + return ( + + + + + + + + + + + + + + + {filteredRows.map((row) => (renderRow(row)))} + +
CategoryNameMeta
+ ); +} + +function renderRow(row) { + return ( + + + + {row.category} + + + + {row.name} +
+ {row.signature} + + +
+ {row.introduced && row.introduced !== "edge" && row.introduced !== "v0.17.0" && ( + + + {row.introduced} + + + )} + {row.introduced === "edge" && edge} {row.wasm + ? ( + + Wasm + + ) + : ( + + SDK-dependent + + )} +
+ + + ); +} diff --git a/third_party/opa/docs/src/components/BuiltinSearch/styles.module.css b/third_party/opa/docs/src/components/BuiltinSearch/styles.module.css new file mode 100644 index 000000000000..8f9460bf6f91 --- /dev/null +++ b/third_party/opa/docs/src/components/BuiltinSearch/styles.module.css @@ -0,0 +1,111 @@ +.versionTag a:link { + color: inherit; +} + +.versionTag { + background-color: slategrey; + color: white; + font-weight: bold; + font-size: 0.8rem; + padding: 0.1rem 0.2rem; + border-radius: 0.2rem; + margin-bottom: 0.5rem; +} + +.versionTag a:visited { + color: inherit; +} + +.wasmTag { + background-color: seagreen; + color: white; + font-weight: bold; + font-size: 0.8rem; + padding: 0.1rem 0.2rem; + border-radius: 0.2rem; +} + +.sdkTag { + background-color: darkgoldenrod; + color: white; + font-weight: bold; + font-size: 0.8rem; + padding: 0.1rem 0.2rem; + border-radius: 0.2rem; + white-space: nowrap; +} + +.categoryTag { + font-weight: bold; + font-size: 0.8rem; + padding: 0.1rem 0.2rem; + border-radius: 0.2rem; + white-space: nowrap; +} + +.categoryTag a:link { + color: inherit; +} +.categoryTag a:visited { + color: inherit; +} +.searchBar { + display: flex; + font-weight: bold; + font-size: 0.8rem; + padding: 0.1rem 0.2rem; + border-radius: 0.2rem; + margin-bottom: 0.4rem; + white-space: nowrap; +} +.searchBar input { + display: flex; + background-color: var(--ifm-background-color); + color: inherit; + font-weight: bold; + font-size: 0.8rem; + padding: 0.1rem 0.2rem; + border: none; + border-bottom: 0.1rem solid var(--ifm-table-border-color); +} +.searchBar select { + display: flex; + background-color: var(--ifm-background-color); + color: inherit; + font-weight: bold; + font-size: 0.8rem; + padding: 0.1rem 0.2rem; + border: none; + border-bottom: 0.1rem solid var(--ifm-table-border-color); +} + +.wasm_0 { + cursor: pointer; + user-select: none; + background-color: slategrey; + color: white; + font-weight: bold; + font-size: 0.8rem; + padding: 0.1rem 0.2rem; + margin-left: 0.4rem; + border-radius: 0.2rem; +} +.wasm_1 { + cursor: pointer; + user-select: none; + background-color: seagreen; + color: white; + font-weight: bold; + font-size: 0.8rem; + padding: 0.1rem 0.2rem; + margin-left: 0.4rem; + border-radius: 0.2rem; +} + +.metaCell { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + flex-wrap: wrap; +} diff --git a/third_party/opa/docs/src/components/BuiltinTable/index.js b/third_party/opa/docs/src/components/BuiltinTable/index.js new file mode 100644 index 000000000000..2a692e87e346 --- /dev/null +++ b/third_party/opa/docs/src/components/BuiltinTable/index.js @@ -0,0 +1,195 @@ +import React from "react"; +import ReactMarkdown from "react-markdown"; + +import styles from "./styles.module.css"; + +import builtins from "@generated/builtin-data/default/builtins.json"; + +function capitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1); +} + +export default function BuiltinTable({ + category, + id, + title, + children, +}) { + const categoryFns = builtins._categories[category]; + if (!categoryFns) return

No built-ins found for category "{category}".

; + + const htmlID = id || category; + const htmlTitle = title || capitalize(category); + + // This component is used on a page that's so large it takes some time to + // render. This means that something the anhor is not present on the page + // and the browser is unable to find the element and go there itself. + // Ideally, the built ins would have smaller pages but this is here to + // preserve the functionality for now. + React.useEffect(() => { + const path = window.location.hash; + + // Exit early if there's no hash in the URL + if (!path || !path.includes("#")) return; + + const id = path.replace("#", ""); + let attempts = 0; + const maxAttempts = 15; + const interval = 100; + + const tryScroll = () => { + const el = document.getElementById(id); + + if (el) { + const r = el.getBoundingClientRect(); + window.top.scroll({ + top: window.pageYOffset + r.top, + behavior: "auto", // immediate jump + }); + } else if (attempts < maxAttempts) { + attempts += 1; + setTimeout(tryScroll, interval); + } + }; + + tryScroll(); + }, []); + + return ( +
+ + + + + + + + + + + + + + + {categoryFns.map((name) => { + const fn = builtins[name]; + if (!fn) return null; + + // we have links out there in the wild that use the name without a dot + // this needs to be preserved for backwards compatibility. + const anchor = `builtin-${category}-${name.replaceAll(".", "")}`; + const isInfix = !!fn.infix; + const isRelation = !!fn.relation; + + const args = fn.args || []; + const result = fn.result || {}; + + const signature = isInfix + ? `${args[0]?.name || "x"} ${fn.infix} ${args[1]?.name || "y"}` + : isRelation + ? `${name}(${args.map((a) => a.name).join(", ")}, ${result.name})` + : `${result.name || "result"} := ${name}(${args.map((a) => a.name).join(", ")})`; + + return ( + + + + + + ); + })} + +
FunctionDescriptionMeta
+ + + {(isInfix ? signature : name) + .split(/(\.|_)/) + .map((part, index) => + part === "." || part === "_" + ? ( + + {part} + + + ) + : part + )} + + + +

+ {signature} +

+ {fn.description && {fn.description}} + + {args.length > 0 && ( +
+ Arguments: + {args.map((arg, i) => ( +
+
+ {arg.name} ({arg.type}) +
+
+ {arg.description} +
+
+ ))} +
+ )} + + Returns: +
+ {result.name} ({result.type}) +
+ {result.description} +
+
+
+
+ {fn.introduced && fn.introduced !== "edge" && fn.introduced !== "v0.17.0" && ( + + {fn.introduced} + + )} + {fn.introduced === "edge" && edge} {fn.wasm + ? ( + + Wasm + + ) + : ( + + SDK-dependent + + )} +
+
+ + {children} +
+ ); +} diff --git a/third_party/opa/docs/src/components/BuiltinTable/styles.module.css b/third_party/opa/docs/src/components/BuiltinTable/styles.module.css new file mode 100644 index 000000000000..cf5706466a25 --- /dev/null +++ b/third_party/opa/docs/src/components/BuiltinTable/styles.module.css @@ -0,0 +1,7 @@ +.functionCell { + width: 20%; +} + +.functionName { + font-size: 0.8rem; +} diff --git a/third_party/opa/docs/src/components/Card/index.js b/third_party/opa/docs/src/components/Card/index.js new file mode 100644 index 000000000000..9131951c74f8 --- /dev/null +++ b/third_party/opa/docs/src/components/Card/index.js @@ -0,0 +1,28 @@ +import React from "react"; + +import ReactMarkdown from "react-markdown"; + +import Link from "@docusaurus/Link"; + +import styles from "./styles.module.css"; + +export default function Card({ item }) { + return ( +
+
+ {item.icon && } +

{item.title}

+
+ {item.note && ( +
+ {item.note} +
+ )} + {item.link && ( + + {item.link_text || "Learn more"} + + )} +
+ ); +} diff --git a/third_party/opa/docs/src/components/Card/styles.module.css b/third_party/opa/docs/src/components/Card/styles.module.css new file mode 100644 index 000000000000..c4a5e252eb36 --- /dev/null +++ b/third_party/opa/docs/src/components/Card/styles.module.css @@ -0,0 +1,50 @@ +.card { + display: flex; + flex-direction: column; + padding: 1.5rem; + background-color: var(--ifm-card-background-color); + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 0.5rem; + transition: transform 0.2s, box-shadow 0.2s; +} + +.card:hover { + transform: translateY(-0.125rem); + box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.1); +} + +.header { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 1rem; +} + +.icon { + width: 2rem; + height: 2rem; + object-fit: contain; +} + +.title { + margin: 0; + font-size: 1.2rem; + font-weight: 600; +} + +.note { + margin: 0.5rem 0; + color: var(--ifm-color-emphasis-700); + font-size: 0.9rem; +} + +.link { + margin-top: auto; + color: var(--ifm-color-primary); + text-decoration: none; + font-weight: 500; +} + +.link:hover { + text-decoration: underline; +} diff --git a/third_party/opa/docs/src/components/CardGrid/index.js b/third_party/opa/docs/src/components/CardGrid/index.js new file mode 100644 index 000000000000..ddc758392865 --- /dev/null +++ b/third_party/opa/docs/src/components/CardGrid/index.js @@ -0,0 +1,11 @@ +import React from "react"; + +import styles from "./styles.module.css"; + +export default function CardGrid({ children, justifyCenter = true }) { + return ( +
+ {children} +
+ ); +} diff --git a/third_party/opa/docs/src/components/CardGrid/styles.module.css b/third_party/opa/docs/src/components/CardGrid/styles.module.css new file mode 100644 index 000000000000..df7c8708a9f0 --- /dev/null +++ b/third_party/opa/docs/src/components/CardGrid/styles.module.css @@ -0,0 +1,37 @@ +.grid { + display: flex; + flex-wrap: wrap; + gap: 1.5rem; + margin: 2rem 0; + width: 100%; + justify-content: flex-start; +} + +.gridCenter { + justify-content: center; +} + +.grid>* { + flex: 1 1 22rem; + min-width: 0; + max-width: calc(33.333% - 1rem); +} + +@media (max-width: 1024px) { + .grid>* { + max-width: calc(50% - 0.75rem); + } +} + +@media (max-width: 768px) { + .grid { + flex-direction: column; + align-items: center; + } + + .grid>* { + width: 100%; + max-width: 18.75rem; + flex: none; + } +} diff --git a/third_party/opa/docs/src/components/CommandDoc/index.js b/third_party/opa/docs/src/components/CommandDoc/index.js new file mode 100644 index 000000000000..db28126df170 --- /dev/null +++ b/third_party/opa/docs/src/components/CommandDoc/index.js @@ -0,0 +1,129 @@ +import { React, useEffect } from "react"; +import { useLocation } from "react-router-dom"; + +import { useThemeConfig } from "@docusaurus/theme-common"; +import ReactMarkdown from "react-markdown"; +import styles from "./styles.module.css"; + +const capitalize = (str) => { + return str.charAt(0).toUpperCase() + str.slice(1); +}; + +const convertUrlsToMarkdownLinks = (text) => { + if (typeof text !== "string") { + text = String(text); + } + + text = text.replace(/<\/?[^>]+(>|$)/g, ""); + + const urlRegex = /https?:\/\/(?:www\.)?[^\s<>"'()]+[^\s<>"'.),!?]/g; + + return text.replace(urlRegex, (rawUrl) => { + const trailingMatch = rawUrl.match(/[.)]+$/); + const trailing = trailingMatch ? trailingMatch[0] : ""; + const url = trailing ? rawUrl.slice(0, -trailing.length) : rawUrl; + + const displayText = url + .replace(/^https?:\/\//, "") + .replace(/^www\./, "") + .replace(/\/$/, ""); + + return `[${displayText}](${url})${trailing}`; + }); +}; + +const htmlSafe = (str) => { + return String(str) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +}; + +const CommandDoc = ({ command }) => { + const { navbar } = useThemeConfig(); + const location = useLocation(); + + const { + id, + use, + useline, + long, + example, + flags, + } = command; + + return ( +
+
+

+ {id} + + # + +

+
+ + {useline && ( +
+          {useline}
+        
+ )} + + {long && {convertUrlsToMarkdownLinks(long)}} + + {flags.length > 0 && ( + <> +

Flags

+ + + + + + + + + + {flags.map((f, idx) => ( + + + + + + ))} + +
ShortFlagDescription
+ {f.shorthand && {f.shorthand}} + + {f.name} + + {f.description !== "" && ( + + {convertUrlsToMarkdownLinks(capitalize(htmlSafe(f.description)))} + + )} + + {f.type && f.type.startsWith("{") && ( +
+ Accepts:{" "} + + {f.type.replace(/,/g, ",\u200b")} + +
+ )} +
+ + )} + + {example && example.trim() !== "" && ( + <> +

Example

+ {example} + + )} +
+ ); +}; + +export default CommandDoc; diff --git a/third_party/opa/docs/src/components/CommandDoc/styles.module.css b/third_party/opa/docs/src/components/CommandDoc/styles.module.css new file mode 100644 index 000000000000..c952ae67dee4 --- /dev/null +++ b/third_party/opa/docs/src/components/CommandDoc/styles.module.css @@ -0,0 +1,54 @@ +.commandHeader { + margin-bottom: 0.5rem; +} + +.commandTitle { + display: inline; + scroll-margin-top: calc(var(--ifm-navbar-height) + 0.5rem); +} + +.directLink { + margin-left: 0.3rem; +} + +.directLinkSymbol { + color: var(--ifm-link-color); +} + +.useline { + padding: 0.5em; + border-radius: var(--ifm-code-border-radius); +} + +.flagsTable { + font-size: 0.9em; + border-collapse: collapse; + width: 100%; +} + +.flagsTableHeader { + text-align: left; + padding: 0.5em; +} + +.flagsTableCell { + padding: 0.5em; +} + +.flagsTableCellWhitespace { + white-space: nowrap; + padding: 0.5em; +} + +.flagsTableCellWide { + padding: 0.5em; + width: 100%; +} + +.flagTypeContainer { + margin-top: 0.25em; +} + +.flagTypeCode { + word-break: break-word; +} diff --git a/third_party/opa/docs/src/components/CommandList/index.js b/third_party/opa/docs/src/components/CommandList/index.js new file mode 100644 index 000000000000..3b3cfd2c5b88 --- /dev/null +++ b/third_party/opa/docs/src/components/CommandList/index.js @@ -0,0 +1,67 @@ +import React, { useState } from "react"; + +import CommandDoc from "../CommandDoc"; + +import styles from "./styles.module.css"; + +function filterCommandById(command, query) { + const lowerQuery = query.toLowerCase(); + + const matches = (text) => text && text.toLowerCase().includes(lowerQuery); + + const isMatch = matches(command.id); + + // (charlieegan3) we don't have child commands for now and it might need to adjusted when + // we do, but I didn't want to ignore the fact they might exist in future. + let matchedChildren = []; + if (Array.isArray(command.children)) { + matchedChildren = command.children + .map(child => filterCommandById(child, query)) + .filter(Boolean); + } + + if (isMatch || matchedChildren.length > 0) { + return { + ...command, + children: matchedChildren.length > 0 ? matchedChildren : command.children, + }; + } + + return null; +} + +const CommandList = ({ commands }) => { + const [search, setSearch] = useState(""); + + const filtered = commands + .map(cmd => filterCommandById(cmd, search)) + .filter(Boolean); + + const totalCommands = commands.length; + const filteredCommands = filtered.length; + + return ( +
+ setSearch(e.target.value)} + className={styles.searchInput} + /> + + {filteredCommands !== totalCommands && filteredCommands > 0 && ( +

+ Showing {filteredCommands}/{totalCommands} commands + {filtered.length > 1 && "(" + filtered.map(cmd => cmd.id).join(", ") + ")"} +

+ )} + + {filteredCommands === 0 ?

No matching commands found.

: ( + filtered.map((cmd, idx) => ) + )} +
+ ); +}; + +export default CommandList; diff --git a/third_party/opa/docs/src/components/CommandList/styles.module.css b/third_party/opa/docs/src/components/CommandList/styles.module.css new file mode 100644 index 000000000000..638a659482a2 --- /dev/null +++ b/third_party/opa/docs/src/components/CommandList/styles.module.css @@ -0,0 +1,16 @@ +.searchInput { + width: 100%; + padding: 0.5rem; + margin-bottom: 1rem; + font-size: 1rem; + border: 1px solid #ccc; + border-radius: 4px; +} + +.searchResults { + margin-bottom: 1rem; +} + +.noResults { + margin-top: 1rem; +} diff --git a/third_party/opa/docs/src/components/EcosystemEmbed/index.js b/third_party/opa/docs/src/components/EcosystemEmbed/index.js new file mode 100644 index 000000000000..07d7e0f18612 --- /dev/null +++ b/third_party/opa/docs/src/components/EcosystemEmbed/index.js @@ -0,0 +1,57 @@ +import React from "react"; + +import useBaseUrl from "@docusaurus/useBaseUrl"; + +import Card from "../Card"; +import CardGrid from "../CardGrid"; + +import getLogoAsset from "../../lib/ecosystem/getLogoAsset"; +import sortPagesByRank from "../../lib/ecosystem/sortPagesByRank"; + +import entries from "@generated/ecosystem-data/default/entries.json"; + +export default function EcosystemEmbed({ feature, children }) { + const allPages = entries; + + const featurePages = []; + + for (const pageId in allPages) { + const page = allPages[pageId]; + if (page.docs_features && page.docs_features[feature]) { + featurePages.push(page); + } + } + + if (featurePages.length > 5) { + return ( +
+ Browse {featurePages.length} projects related to "{feature}" in the{" "} + OPA Ecosystem. +
+ ); + } + + const sortedPages = sortPagesByRank(featurePages); + + return ( +
+ {children} + + {sortedPages.map((id) => { + const page = featurePages[id]; + if (!page) return null; + + const cardData = { + title: page.title, + note: page.docs_features[feature]?.note ?? "No note available", + icon: getLogoAsset(page.id), + link: useBaseUrl(`/ecosystem/entry/${page.id}`), + link_text: "View Details", + }; + + return ; + })} + +
+ ); +} diff --git a/third_party/opa/docs/src/components/EcosystemFeatureLink/index.js b/third_party/opa/docs/src/components/EcosystemFeatureLink/index.js new file mode 100644 index 000000000000..a032658a9bb2 --- /dev/null +++ b/third_party/opa/docs/src/components/EcosystemFeatureLink/index.js @@ -0,0 +1,24 @@ +import useBaseUrl from "@docusaurus/useBaseUrl"; +import React from "react"; + +import entries from "@generated/ecosystem-data/default/entries.json"; + +export default function EcosystemFeatureLink({ feature, children }) { + const allPages = entries; + + const featurePages = []; + + for (const pageId in allPages) { + const page = allPages[pageId]; + if (page.docs_features && page.docs_features[feature]) { + featurePages.push(page); + } + } + + let message = "1 project"; + if (featurePages.length > 1) { + message = `${featurePages.length} projects`; + } + + return {children} ({message}); +} diff --git a/third_party/opa/docs/src/components/EvergreenCodeBlock/index.js b/third_party/opa/docs/src/components/EvergreenCodeBlock/index.js new file mode 100644 index 000000000000..08cd8440b458 --- /dev/null +++ b/third_party/opa/docs/src/components/EvergreenCodeBlock/index.js @@ -0,0 +1,52 @@ +import React from "react"; +import semver from "semver"; + +import versions from "@generated/versions-data/default/versions.json"; + +// EvergreenCodeBlock is a component that can perform some templating of code blocks +// based on data available in docusaurus but not in mdx files. +function EvergreenCodeBlock({ children }) { + const currentVersion = versions.filter(semver.valid).sort(semver.rcompare)[0]; + + const replacements = { + "current_version": currentVersion, + "current_version_docker": currentVersion.replace("v", ""), + "current_version_docker_envoy": currentVersion.replace("v", "") + "-envoy-4", + // TODO: Automate the updates of this value + "current_version_kube_mgmt": "9.0.1", + }; + + // Function to recursively process React children and replace template variables + const processChildren = (children) => { + return React.Children.map(children, child => { + if (typeof child === "string") { + let processedText = child; + + Object.entries(replacements).forEach(([key, value]) => { + const pattern = new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, "g"); + processedText = processedText.replace(pattern, value); + }); + + return processedText; + } + + if (React.isValidElement(child) && child.props.children) { + return React.cloneElement(child, { + ...child.props, + children: processChildren(child.props.children), + }); + } + + return child; + }); + }; + + return ( +
+ {processChildren(children)} +
+ ); +} + +export default EvergreenCodeBlock; + diff --git a/third_party/opa/docs/src/components/FeedbackForm/index.js b/third_party/opa/docs/src/components/FeedbackForm/index.js new file mode 100644 index 000000000000..4cb48eef80c4 --- /dev/null +++ b/third_party/opa/docs/src/components/FeedbackForm/index.js @@ -0,0 +1,322 @@ +import React, { useEffect, useState, useRef } from "react"; +import { Icon } from "@iconify/react"; + +import BrowserOnly from "@docusaurus/BrowserOnly"; +import Admonition from "@theme/Admonition"; + +import styles from "./styles.module.css"; + +/** + * A feedback form component that is automatically added to the end of documentation pages. + * It can also be manually included in other pages by setting enablePopup to true. + * The form allows users to provide positive or negative feedback with optional comments + * and email for follow-up. Feedback is submitted to Netlify Forms via AJAX. + * + * Note: When adding new fields to this form, remember to update the static/netlify-forms.html + * file so that Netlify knows which fields to permit. If this is not updated, new fields + * in the form will be dropped. + */ +export default function FeedbackForm({ enablePopup = false }) { + // this is used to ensure the response is categorized in netlify forms and that + // local state is remembered. + const formName = "page-feedback"; + + const [feedbackType, setFeedbackType] = useState(""); + const [comment, setComment] = useState(""); + const [url, setUrl] = useState(""); + const [path, setPath] = useState(""); + const [message, setMessage] = useState(""); + + const [showFloatingPopup, setShowFloatingPopup] = useState(false); + const [hasScrolled, setHasScrolled] = useState(false); + const [popupEnabled, setPopupEnabled] = useState(enablePopup); + const feedbackFormRef = useRef(null); + + // The popup will never show before the user has been on the page for 10s + const feedbackPopupTimeoutMs = 10000; + + useEffect(() => { + if (typeof window !== "undefined") { + setUrl(window.location); + setPath(window.location.pathname); + + // Check if popup has been globally dismissed + const popupDismissed = localStorage.getItem("documentation-feedback-popup-dismissed"); + if (popupDismissed) { + setPopupEnabled(false); + } + } + }, []); + + // This effect sets up an intersection observer to detect when the feedback form + // comes into view. When the form is visible, it permanently disables the popup + // to prevent it from showing again. This ensures users who have found the form + // don't get interrupted by the popup. + useEffect(() => { + if (feedbackFormRef.current) { + const observer = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting) { + setShowFloatingPopup(false); + setPopupEnabled(false); + } + }, + { threshold: 0.1 } + ); + + observer.observe(feedbackFormRef.current); + + return () => { + observer.disconnect(); + }; + } + }, [feedbackFormRef.current]); + + useEffect(() => { + if (popupEnabled) { + const handleScroll = () => { + if (window.scrollY > 100) { + setHasScrolled(true); + window.removeEventListener("scroll", handleScroll); + } + }; + + window.addEventListener("scroll", handleScroll); + + const timer = setTimeout(() => { + if (hasScrolled) { + setShowFloatingPopup(true); + } + }, feedbackPopupTimeoutMs); + + return () => { + clearTimeout(timer); + window.removeEventListener("scroll", handleScroll); + }; + } + }, [popupEnabled, hasScrolled]); + + // feedback is stored in local storage to prevent the form / popup from showing again + useEffect(() => { + const feedbackSubmitted = localStorage.getItem(`${formName}-${path}`); + if (feedbackSubmitted) { + setMessage("Thank you for your feedback!"); + setPopupEnabled(false); + } + }, [path]); + + const handleSubmit = (event) => { + event.preventDefault(); + + const formData = new FormData(event.target); + + fetch("/", { + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams(formData).toString(), + }) + .then((response) => { + if (!response.ok) { + throw new Error(`HTTP Status: ${response.status}`); + } + + localStorage.setItem(`${formName}-${path}`, "submitted"); + setMessage("Thank you for your feedback!"); + setShowFloatingPopup(false); + }) + .catch((error) => { + console.error(error); + setMessage("Oh dear, there was an error submitting feedback..."); + }); + }; + + const scrollToFeedback = () => { + if (feedbackFormRef.current) { + feedbackFormRef.current.scrollIntoView({ behavior: "smooth" }); + setShowFloatingPopup(false); + } + }; + + if (message !== "") { + return ( + + {() => ( + } + title="Feedback" + > +

{message}

+ +

+ Got more to say? Questions for the OPA experts? Please come and find us on the{" "} + + OPA Slack. + {" "} + The #help channel is a great place to get stated. +

+
+ )} +
+ ); + } + + return ( + + {() => ( + <> + {popupEnabled && showFloatingPopup && ( + setShowFloatingPopup(false)} + onFeedbackSelect={(type) => { + setFeedbackType(type); + scrollToFeedback(); + }} + /> + )} +
+ } + title="Feedback" + > +

We are always trying to make our documentation the best it can be and welcome your comments.

+
+ +
+ + )} +
+ ); +} + +/** + * A floating popup that appears after scrolling to encourage users to provide feedback. + * The popup shows thumbs up/down buttons that, when clicked, will scroll the user to + * the feedback form and pre-select their feedback type. The popup is automatically + * hidden when the feedback form comes into view. + */ +const FloatingPopup = ({ onClose, onFeedbackSelect }) => { + const handleClose = () => { + // Set global flag to never show popup again + localStorage.setItem("documentation-feedback-popup-dismissed", "true"); + onClose(); + }; + + const handleFeedbackSelect = (type) => { + if (type === "negative") { + localStorage.setItem("documentation-feedback-popup-dismissed", "true"); + } + onFeedbackSelect(type); + }; + + return ( +
+ +
+

How's this page?

+
+ + +
+
+
+ ); +}; + +const Form = ({ + formName, + url, + feedbackType, + setFeedbackType, + comment, + setComment, + handleSubmit, +}) => ( + + + + + +
+ + +
+ + {feedbackType && ( +
+
+ + +
+ + + diff --git a/third_party/opa/docs/static/robots.txt b/third_party/opa/docs/static/robots.txt new file mode 100644 index 000000000000..eb0536286f30 --- /dev/null +++ b/third_party/opa/docs/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/third_party/opa/docs/static/site.webmanifest b/third_party/opa/docs/static/site.webmanifest new file mode 100644 index 000000000000..d36921311385 --- /dev/null +++ b/third_party/opa/docs/static/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "Open Policy Agent", + "short_name": "OPA", + "icons": [ + { + "src": "/web-app-manifest-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/web-app-manifest-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} \ No newline at end of file diff --git a/third_party/opa/docs/static/web-app-manifest-192x192.png b/third_party/opa/docs/static/web-app-manifest-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..ad2fe981a659858a619cddd06220b451686a8484 GIT binary patch literal 6640 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 literal 0 HcmV?d00001 diff --git a/third_party/opa/download/config.go b/third_party/opa/download/config.go new file mode 100644 index 000000000000..0e252b05890b --- /dev/null +++ b/third_party/opa/download/config.go @@ -0,0 +1,15 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package download + +import ( + v1 "github.com/open-policy-agent/opa/v1/download" +) + +// PollingConfig represents polling configuration for the downloader. +type PollingConfig = v1.PollingConfig + +// Config represents the configuration for the downloader. +type Config = v1.Config diff --git a/third_party/opa/download/doc.go b/third_party/opa/download/doc.go new file mode 100644 index 000000000000..ad1a6cd3fa05 --- /dev/null +++ b/third_party/opa/download/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package download diff --git a/third_party/opa/download/download.go b/third_party/opa/download/download.go new file mode 100644 index 000000000000..cfb14c0704f7 --- /dev/null +++ b/third_party/opa/download/download.go @@ -0,0 +1,29 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package download implements low-level OPA bundle downloading. +package download + +import ( + "github.com/open-policy-agent/opa/plugins/rest" + v1 "github.com/open-policy-agent/opa/v1/download" +) + +// Update contains the result of a download. If an error occurred, the Error +// field will be non-nil. If a new bundle is available, the Bundle field will +// be non-nil. +type Update = v1.Update + +// Downloader implements low-level OPA bundle downloading. Downloader can be +// started and stopped. After starting, the downloader will request bundle +// updates from the remote HTTP endpoint that the client is configured to +// connect to. +type Downloader = v1.Downloader + +// New returns a new Downloader that can be started. +func New(config Config, client rest.Client, path string) *Downloader { + return v1.New(config, client, path) +} + +type HTTPError = v1.HTTPError diff --git a/third_party/opa/download/oci_download.go b/third_party/opa/download/oci_download.go new file mode 100644 index 000000000000..6df1246c0c9d --- /dev/null +++ b/third_party/opa/download/oci_download.go @@ -0,0 +1,14 @@ +//go:build !opa_no_oci + +package download + +import ( + v1 "github.com/open-policy-agent/opa/v1/download" + + "github.com/open-policy-agent/opa/plugins/rest" +) + +// NewOCI returns a new Downloader that can be started. +func NewOCI(config Config, client rest.Client, path, storePath string) *OCIDownloader { + return v1.NewOCI(config, client, path, storePath) +} diff --git a/third_party/opa/download/oci_download_unavailable.go b/third_party/opa/download/oci_download_unavailable.go new file mode 100644 index 000000000000..5727804d28b3 --- /dev/null +++ b/third_party/opa/download/oci_download_unavailable.go @@ -0,0 +1,59 @@ +//go:build opa_no_oci + +package download + +import ( + "context" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/bundle" + "github.com/open-policy-agent/opa/plugins/rest" +) + +func NewOCI(Config, rest.Client, string, string) *OCIDownloader { + panic("built without OCI support") +} + +func (d *OCIDownloader) WithCallback(f func(context.Context, Update)) *OCIDownloader { + panic("built without OCI support") +} + +func (d *OCIDownloader) WithLogAttrs(map[string]any) *OCIDownloader { + panic("built without OCI support") +} + +func (d *OCIDownloader) WithBundleVerificationConfig(*bundle.VerificationConfig) *OCIDownloader { + panic("built without OCI support") +} + +func (d *OCIDownloader) WithSizeLimitBytes(int64) *OCIDownloader { + panic("built without OCI support") +} + +func (d *OCIDownloader) WithBundlePersistence(bool) *OCIDownloader { + panic("built without OCI support") +} + +func (d *OCIDownloader) ClearCache() { + panic("built without OCI support") +} + +func (d *OCIDownloader) SetCache(string) { + panic("built without OCI support") +} + +func (d *OCIDownloader) Trigger(context.Context) error { + panic("built without OCI support") +} + +func (d *OCIDownloader) Start(context.Context) { + panic("built without OCI support") +} + +func (d *OCIDownloader) Stop(context.Context) { + panic("built without OCI support") +} + +func (*OCIDownloader) WithBundleParserOpts(ast.ParserOptions) *OCIDownloader { + panic("built without OCI support") +} diff --git a/third_party/opa/download/oci_downloader.go b/third_party/opa/download/oci_downloader.go new file mode 100644 index 000000000000..847c456c5bb3 --- /dev/null +++ b/third_party/opa/download/oci_downloader.go @@ -0,0 +1,7 @@ +package download + +import ( + v1 "github.com/open-policy-agent/opa/v1/download" +) + +type OCIDownloader = v1.OCIDownloader diff --git a/third_party/opa/features/doc.go b/third_party/opa/features/doc.go new file mode 100644 index 000000000000..7f2bdce3d2d8 --- /dev/null +++ b/third_party/opa/features/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package features diff --git a/third_party/opa/features/tracing/doc.go b/third_party/opa/features/tracing/doc.go new file mode 100644 index 000000000000..161a3d0cee07 --- /dev/null +++ b/third_party/opa/features/tracing/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package tracing diff --git a/third_party/opa/features/tracing/tracing.go b/third_party/opa/features/tracing/tracing.go new file mode 100644 index 000000000000..017f79f69bb5 --- /dev/null +++ b/third_party/opa/features/tracing/tracing.go @@ -0,0 +1,10 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package tracing + +import ( + // Importing v1 for side effects + _ "github.com/open-policy-agent/opa/v1/features/tracing" +) diff --git a/third_party/opa/features/wasm/doc.go b/third_party/opa/features/wasm/doc.go new file mode 100644 index 000000000000..165997e4a245 --- /dev/null +++ b/third_party/opa/features/wasm/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package wasm diff --git a/third_party/opa/features/wasm/wasm.go b/third_party/opa/features/wasm/wasm.go new file mode 100644 index 000000000000..a2c6e8f06cf4 --- /dev/null +++ b/third_party/opa/features/wasm/wasm.go @@ -0,0 +1,14 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Import this package to enable evaluation of rego code using the +// built-in wasm engine. +package wasm + +import ( + v1 "github.com/open-policy-agent/opa/v1/features/wasm" +) + +// OPA is an implementation of the OPA SDK. +type OPA = v1.OPA diff --git a/third_party/opa/format/doc.go b/third_party/opa/format/doc.go new file mode 100644 index 000000000000..ba514fffb948 --- /dev/null +++ b/third_party/opa/format/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package format diff --git a/third_party/opa/format/format.go b/third_party/opa/format/format.go new file mode 100644 index 000000000000..5782dd2aa8a2 --- /dev/null +++ b/third_party/opa/format/format.go @@ -0,0 +1,86 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package format implements formatting of Rego source files. +package format + +import ( + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/types" + v1 "github.com/open-policy-agent/opa/v1/format" +) + +// Opts lets you control the code formatting via `AstWithOpts()`. +type Opts = v1.Opts + +// Source formats a Rego source file. The bytes provided must describe a complete +// Rego module. If they don't, Source will return an error resulting from the attempt +// to parse the bytes. +func Source(filename string, src []byte) ([]byte, error) { + return SourceWithOpts(filename, src, Opts{ + RegoVersion: ast.DefaultRegoVersion, + ParserOptions: &ast.ParserOptions{ + RegoVersion: ast.DefaultRegoVersion, + }, + }) +} + +func SourceWithOpts(filename string, src []byte, opts Opts) ([]byte, error) { + if opts.RegoVersion == ast.RegoUndefined { + opts.RegoVersion = ast.DefaultRegoVersion + } + if opts.ParserOptions == nil { + opts.ParserOptions = &ast.ParserOptions{} + } + if opts.ParserOptions.RegoVersion == ast.RegoUndefined { + opts.ParserOptions.RegoVersion = ast.DefaultRegoVersion + } + + return v1.SourceWithOpts(filename, src, opts) +} + +// MustAst is a helper function to format a Rego AST element. If any errors +// occurs this function will panic. This is mostly used for test +func MustAst(x any) []byte { + bs, err := Ast(x) + if err != nil { + panic(err) + } + return bs +} + +// MustAstWithOpts is a helper function to format a Rego AST element. If any errors +// occurs this function will panic. This is mostly used for test +func MustAstWithOpts(x any, opts Opts) []byte { + bs, err := AstWithOpts(x, opts) + if err != nil { + panic(err) + } + return bs +} + +// Ast formats a Rego AST element. If the passed value is not a valid AST +// element, Ast returns nil and an error. If AST nodes are missing locations +// an arbitrary location will be used. +func Ast(x any) ([]byte, error) { + return AstWithOpts(x, Opts{ + RegoVersion: ast.DefaultRegoVersion, + }) +} + +func AstWithOpts(x any, opts Opts) ([]byte, error) { + if opts.RegoVersion == ast.RegoUndefined { + opts.RegoVersion = ast.DefaultRegoVersion + } + + return v1.AstWithOpts(x, opts) +} + +// ArgErrDetail but for `fmt` checks since compiler has not run yet. +type ArityFormatErrDetail = v1.ArityFormatErrDetail + +// arityMismatchError but for `fmt` checks since the compiler has not run yet. +func ArityFormatMismatchError(operands []*ast.Term, operator string, loc *ast.Location, f *types.Function) *ast.Error { + return v1.ArityFormatMismatchError(operands, operator, loc, f) +} diff --git a/third_party/opa/format/format_test.go b/third_party/opa/format/format_test.go new file mode 100644 index 000000000000..669cac017d3c --- /dev/null +++ b/third_party/opa/format/format_test.go @@ -0,0 +1,152 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package format + +import ( + "strings" + "testing" + + "github.com/open-policy-agent/opa/ast" +) + +func TestSource_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expFormatted string + expErrs []string + }{ + { + note: "v0", // from default rego-version + module: `package test + +p[x] { + x = "a" +}`, + expFormatted: `package test + +p[x] { + x = "a" +} +`, + }, + { + note: "v1", + module: `package test + +p contains x if { + x = "a" +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + formatted, err := Source("test.rego", []byte(tc.module)) + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected error:\n\n%q\n\nbut got:\n\n%q", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + formattedStr := string(formatted) + if formattedStr != tc.expFormatted { + t.Fatalf("expected %q but got %q", tc.expFormatted, formattedStr) + } + } + }) + } +} + +func TestSourceWithOpts_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + toRegoVersion ast.RegoVersion + module string + expFormatted string + expErrs []string + }{ + { + note: "v0 -> v0", // from default rego-version + toRegoVersion: ast.RegoV0, + module: `package test + +p[x] { + x = "a" +}`, + expFormatted: `package test + +p[x] { + x = "a" +} +`, + }, + { + note: "v0 -> v1", // from default rego-version + toRegoVersion: ast.RegoV1, + module: `package test + +p[x] { + x = "a" +}`, + expFormatted: `package test + +p contains x if { + x = "a" +} +`, + }, + { + note: "v1 -> v1", // from non-default rego-version + toRegoVersion: ast.RegoV1, + module: `package test + +p contains x if { + x = "a" +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + formatted, err := SourceWithOpts("test.rego", []byte(tc.module), Opts{RegoVersion: tc.toRegoVersion}) + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected error:\n\n%q\n\nbut got:\n\n%q", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + formattedStr := string(formatted) + if formattedStr != tc.expFormatted { + t.Fatalf("expected %q but got %q", tc.expFormatted, formattedStr) + } + } + }) + } +} diff --git a/third_party/opa/go.mod b/third_party/opa/go.mod new file mode 100644 index 000000000000..648a1987fcce --- /dev/null +++ b/third_party/opa/go.mod @@ -0,0 +1,112 @@ +module github.com/open-policy-agent/opa + +go 1.23.8 + +toolchain go1.24.3 + +require ( + github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 + github.com/cespare/xxhash/v2 v2.3.0 + github.com/containerd/containerd/v2 v2.1.3 + github.com/containerd/errdefs v1.0.0 + github.com/dgraph-io/badger/v4 v4.8.0 + github.com/fortytw2/leaktest v1.3.0 + github.com/foxcpp/go-mockdns v1.1.0 + github.com/fsnotify/fsnotify v1.9.0 + github.com/go-ini/ini v1.67.0 + github.com/go-logr/logr v1.4.3 + github.com/gobwas/glob v0.2.3 + github.com/google/go-cmp v0.7.0 + github.com/google/uuid v1.6.0 + github.com/olekukonko/tablewriter v0.0.5 + github.com/opencontainers/go-digest v1.0.0 + github.com/opencontainers/image-spec v1.1.1 + github.com/peterh/liner v1.2.2 + github.com/prometheus/client_golang v1.22.0 + github.com/prometheus/client_model v0.6.2 + github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 + github.com/sergi/go-diff v1.4.0 + github.com/sirupsen/logrus v1.9.3 + github.com/spf13/cobra v1.9.1 + github.com/spf13/pflag v1.0.7 + github.com/spf13/viper v1.20.1 + github.com/tchap/go-patricia/v2 v2.3.3 + github.com/vektah/gqlparser/v2 v2.5.30 + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 + github.com/yashtewari/glob-intersection v0.2.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 + go.opentelemetry.io/otel v1.37.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 + go.opentelemetry.io/otel/sdk v1.37.0 + go.opentelemetry.io/otel/trace v1.37.0 + go.uber.org/automaxprocs v1.6.0 + golang.org/x/net v0.42.0 + golang.org/x/time v0.12.0 + google.golang.org/grpc v1.74.2 + google.golang.org/protobuf v1.36.6 + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c + go.yaml.in/yaml/v3 v3.0.5 + oras.land/oras-go/v2 v2.6.0 + sigs.k8s.io/yaml v1.6.0 +) + +require ( + github.com/agnivade/levenshtein v1.2.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v1.0.0-rc.1 // indirect + github.com/containerd/typeurl/v2 v2.2.3 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect + github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.3.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/flatbuffers v25.2.10+incompatible // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/miekg/dns v1.1.57 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.0 // indirect + golang.org/x/tools v0.34.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect +) + +// retract directive comment below will be displayed as a warning on pkg.go.dev for the old package name. Please retain +// this for future releases. + +// Use the path github.com/open-policy-agent/opa (lower-case), not github.com/open-policy-agent/OPA. +// Before 0.15.0 OPA was not using go modules and the correct import path was not enforced. +retract [v0.1.0-rc1, v0.14.2] diff --git a/third_party/opa/go.sum b/third_party/opa/go.sum new file mode 100644 index 000000000000..5a9a773062a6 --- /dev/null +++ b/third_party/opa/go.sum @@ -0,0 +1,314 @@ +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= +github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= +github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +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/containerd/containerd/v2 v2.1.3 h1:eMD2SLcIQPdMlnlNF6fatlrlRLAeDaiGPGwmRKLZKNs= +github.com/containerd/containerd/v2 v2.1.3/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E= +github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= +github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v4 v4.8.0 h1:JYph1ChBijCw8SLeybvPINizbDKWZ5n/GYbz2yhN/bs= +github.com/dgraph-io/badger/v4 v4.8.0/go.mod h1:U6on6e8k/RTbUWxqKR0MvugJuVmkxSNc79ap4917h4w= +github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= +github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= +github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk= +github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= +github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= +github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +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.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw= +github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= +github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc= +github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= +github.com/vektah/gqlparser/v2 v2.5.30 h1:EqLwGAFLIzt1wpx1IPpY67DwUujF1OfzgEyDsLrN6kE= +github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= +github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= +go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= +go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY= +google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= +google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc= +oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/third_party/opa/hooks/doc.go b/third_party/opa/hooks/doc.go new file mode 100644 index 000000000000..6c5092427482 --- /dev/null +++ b/third_party/opa/hooks/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package hooks diff --git a/third_party/opa/hooks/hooks.go b/third_party/opa/hooks/hooks.go new file mode 100644 index 000000000000..110398873bef --- /dev/null +++ b/third_party/opa/hooks/hooks.go @@ -0,0 +1,46 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package hooks + +import ( + v1 "github.com/open-policy-agent/opa/v1/hooks" +) + +// Hook is a hook to be called in some select places in OPA's operation. +// +// The base Hook interface is any, and wherever a hook can occur, the calling code +// will check if your hook implements an appropriate interface. If so, your hook +// is called. +// +// This allows you to only hook in to behavior you care about, and it allows the +// OPA to add more hooks in the future. +// +// All hook interfaces in this package have Hook in the name. Hooks must be safe +// for concurrent use. It is expected that hooks are fast; if a hook needs to take +// time, then copy what you need and ensure the hook is async. +// +// When multiple instances of a hook are provided, they are all going to be executed +// in an unspecified order (it's a map-range call underneath). If you need hooks to +// be run in order, you can wrap them into another hook, and configure that one. +type Hook = v1.Hook + +// Hooks is the type used for every struct in OPA that can work with hooks. +type Hooks = v1.Hooks + +// New creates a new instance of Hooks. +func New(hs ...Hook) Hooks { + return v1.New(hs...) +} + +// ConfigHook allows inspecting or rewriting the configuration when the plugin +// manager is processing it. +// Note that this hook is not run when the plugin manager is reconfigured. This +// usually only happens when there's a new config from a discovery bundle, and +// for processing _that_, there's `ConfigDiscoveryHook`. +type ConfigHook = v1.ConfigHook + +// ConfigHook allows inspecting or rewriting the discovered configuration when +// the discovery plugin is processing it. +type ConfigDiscoveryHook = v1.ConfigDiscoveryHook diff --git a/third_party/opa/internal/bundle/inspect/inspect.go b/third_party/opa/internal/bundle/inspect/inspect.go new file mode 100644 index 000000000000..0321fcad3aad --- /dev/null +++ b/third_party/opa/internal/bundle/inspect/inspect.go @@ -0,0 +1,246 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package inspect + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + initload "github.com/open-policy-agent/opa/internal/runtime/init" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/util" +) + +// Info represents information about a bundle. +type Info struct { + Manifest *bundle.Manifest `json:"manifest,omitempty"` + Signatures bundle.SignaturesConfig `json:"signatures_config,omitempty"` + WasmModules []map[string]any `json:"wasm_modules,omitempty"` + Namespaces map[string][]string `json:"namespaces,omitempty"` + Annotations []*ast.AnnotationsRef `json:"annotations,omitempty"` + Required *ast.Capabilities `json:"capabilities,omitempty"` +} + +func File(path string, includeAnnotations bool) (*Info, error) { + return FileForRegoVersion(ast.RegoV0, path, includeAnnotations) +} + +func FileForRegoVersion(regoVersion ast.RegoVersion, path string, includeAnnotations bool) (*Info, error) { + if strings.HasSuffix(path, bundle.RegoExt) { + return fileInfoForRegoVersion(regoVersion, path, includeAnnotations) + } + + return bundleOrDirInfoForRegoVersion(regoVersion, path, includeAnnotations) +} + +func bundleOrDirInfoForRegoVersion(regoVersion ast.RegoVersion, path string, includeAnnotations bool) (*Info, error) { + b, err := loader.NewFileLoader(). + WithRegoVersion(regoVersion). + WithSkipBundleVerification(true). + WithBundleLazyLoadingMode(true). // Bundle lazy loading mode skips parsing data files + WithProcessAnnotation(true). // Always process annotations, for enriching namespace listing + AsBundle(path) + if err != nil { + return nil, err + } + + bi := &Info{Manifest: &b.Manifest} + + namespaces := make(map[string][]string, len(b.Modules)) + modules := make([]*ast.Module, 0, len(b.Modules)) + for _, m := range b.Modules { + namespaces[m.Parsed.Package.Path.String()] = append(namespaces[m.Parsed.Package.Path.String()], filepath.Clean(m.Path)) + modules = append(modules, m.Parsed) + } + bi.Namespaces = namespaces + + if includeAnnotations { + as, errs := ast.BuildAnnotationSet(modules) + if len(errs) > 0 { + return nil, errs + } + flattened := as.Flatten() + + for _, wr := range bi.Manifest.WasmResolvers { + if as := wr.Annotations; len(as) > 0 { + path, err := ast.PtrRef(ast.DefaultRootDocument, wr.Entrypoint) + if err != nil { + return nil, fmt.Errorf("failed to parse Wasm entrypoint in manifest: %s", err) + } + for _, a := range as { + ar := ast.NewAnnotationsRef(a) + ar.Path = path + ar.Location = ast.NewLocation(nil, wr.Module, 0, 0) + flattened = flattened.Insert(ar) + } + } + } + + bi.Annotations = flattened + } + + err = bi.getBundleDataWasmAndSignatures(path) + if err != nil { + return nil, err + } + + wasmModules := make([]map[string]any, 0, len(b.WasmModules)) + for _, w := range b.WasmModules { + wasmModule := map[string]any{ + "url": w.URL, + "path": w.Path, + } + + var entrypoints []string + for _, r := range w.Entrypoints { + entrypoints = append(entrypoints, r.String()) + } + wasmModule["entrypoints"] = entrypoints + + wasmModules = append(wasmModules, wasmModule) + } + bi.WasmModules = wasmModules + + moduleMap := make(map[string]*ast.Module, len(b.Modules)) + for _, f := range b.Modules { + moduleMap[f.URL] = f.Parsed + } + + c := ast.NewCompiler(). + WithAllowUndefinedFunctionCalls(true) + c.Compile(moduleMap) + if c.Failed() { + return bi, c.Errors + } + + bi.Required = c.Required + + return bi, nil +} + +func (bi *Info) getBundleDataWasmAndSignatures(name string) error { + + load, err := initload.WalkPaths([]string{name}, nil, true) + if err != nil { + return err + } + + if len(load.BundlesLoader) == 0 || len(load.BundlesLoader) > 1 { + return errors.New("expected information on one bundle only but got none or multiple") + } + + bl := load.BundlesLoader[0] + descriptors := []*bundle.Descriptor{} + + for { + f, err := bl.DirectoryLoader.NextFile() + if err == io.EOF { + break + } + + if err != nil { + return fmt.Errorf("bundle read failed: %w", err) + } + + if strings.HasSuffix(f.Path(), bundle.SignaturesFile) { + var buf bytes.Buffer + n, err := f.Read(&buf, bundle.DefaultSizeLimitBytes+1) + f.Close() + + if err != nil && err != io.EOF { + return err + } else if err == nil && n >= bundle.DefaultSizeLimitBytes { + return fmt.Errorf("bundle file exceeded max size (%v bytes)", bundle.DefaultSizeLimitBytes) + } + + var signatures bundle.SignaturesConfig + if err := util.NewJSONDecoder(&buf).Decode(&signatures); err != nil { + return fmt.Errorf("bundle load failed on signatures decode: %w", err) + } + bi.Signatures = signatures + } + + if filepath.Base(f.Path()) == "data.json" || filepath.Base(f.Path()) == "data.yaml" { + descriptors = append(descriptors, f) + } + } + + for _, f := range descriptors { + path := filepath.Clean(f.Path()) + key := strings.Split(strings.TrimPrefix(path, string(os.PathSeparator)), string(os.PathSeparator)) + + value := path + if bl.IsDir { + value = filepath.Clean(f.URL()) + } + + if len(key) > 1 { + key = key[:len(key)-1] // ignore file name ie. data.json / data.yaml + path := fmt.Sprintf("%v.%v", ast.DefaultRootDocument, strings.Join(key, ".")) + bi.Namespaces[path] = append(bi.Namespaces[path], value) + } else { + bi.Namespaces[ast.DefaultRootDocument.String()] = append(bi.Namespaces[ast.DefaultRootDocument.String()], value) // data file at bundle root + } + } + + for _, item := range bi.Manifest.WasmResolvers { + key := strings.Split(strings.TrimPrefix(item.Entrypoint, "/"), "/") + path := fmt.Sprintf("%v.%v", ast.DefaultRootDocument, strings.Join(key, ".")) + bi.Namespaces[path] = append(bi.Namespaces[path], item.Module) + } + + return nil +} + +func fileInfoForRegoVersion(regoVersion ast.RegoVersion, path string, includeAnnotations bool) (*Info, error) { + res, err := loader.NewFileLoader(). + WithRegoVersion(regoVersion). + WithSkipBundleVerification(true). + WithProcessAnnotation(true). // Always process annotations, for enriching namespace listing + All([]string{path}) + if err != nil { + return nil, err + } + bi := &Info{ + Namespaces: make(map[string][]string, len(res.Modules)), + } + + moduleMap := make(map[string]*ast.Module, len(res.Modules)) + + for _, m := range res.Modules { + bi.Namespaces[m.Parsed.Package.Path.String()] = append( + bi.Namespaces[m.Parsed.Package.Path.String()], + filepath.Clean(m.Name), + ) + moduleMap[m.Name] = m.Parsed + } + + if includeAnnotations { + as, errs := ast.BuildAnnotationSet(util.Values(moduleMap)) + if len(errs) > 0 { + return nil, errs + } + + bi.Annotations = as.Flatten() + } + + c := ast.NewCompiler(). + WithAllowUndefinedFunctionCalls(true) + c.Compile(moduleMap) + if c.Failed() { + return bi, c.Errors + } + + bi.Required = c.Required + + return bi, nil +} diff --git a/third_party/opa/internal/bundle/inspect/inspect_test.go b/third_party/opa/internal/bundle/inspect/inspect_test.go new file mode 100644 index 000000000000..5e17cc382ca9 --- /dev/null +++ b/third_party/opa/internal/bundle/inspect/inspect_test.go @@ -0,0 +1,280 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package inspect + +import ( + "encoding/json" + "fmt" + "os" + "path" + "path/filepath" + "reflect" + "slices" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/ast" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestGenerateBundleInfoWithFileDir(t *testing.T) { + + files := map[string]string{ + "/fuz/data.json": "[1,2,3]", + "/fuz/fuz.rego": "package fuz\np = 1", + "/data.json": `{"a": {"b": {"c": [123]}}}`, + "/foo/policy.rego": "package foo\np = 1", + "/baz/authz.rego": "package foo\nx = 1", + "base.rego": "package bar\nx = 1 { input > 7 }", + "/.manifest": `{"roots": ["foo", "bar", "fuz", "baz", "a"], "revision": "rev"}`, + } + + test.WithTempFS(files, func(rootDir string) { + info, err := File(rootDir, false) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expectedManifest := bundle.Manifest{ + Revision: "rev", + Roots: &[]string{"foo", "bar", "fuz", "baz", "a"}, + WasmResolvers: nil, + Metadata: nil, + } + + if !info.Manifest.Equal(expectedManifest) { + t.Fatalf("expected manifest %v, but got: %v", expectedManifest, info.Manifest) + } + + expectedNamespaces := map[string][]string{ + "data": {filepath.Join(rootDir, "data.json")}, + "data.bar": {filepath.Join(rootDir, "base.rego")}, + "data.foo": {filepath.Join(rootDir, "baz", "authz.rego"), filepath.Join(rootDir, "foo", "policy.rego")}, + "data.fuz": {filepath.Join(rootDir, "fuz", "fuz.rego"), filepath.Join(rootDir, "fuz", "data.json")}, + } + + if !reflect.DeepEqual(info.Namespaces, expectedNamespaces) { + t.Fatalf("expected namespaces %v, but got %v", expectedNamespaces, info.Namespaces) + } + + var builtinNames []string + for _, bi := range info.Required.Builtins { + builtinNames = append(builtinNames, bi.Name) + } + + expBuiltinNames := []string{"eq", "gt"} + + if !slices.Equal(expBuiltinNames, builtinNames) { + t.Fatalf("expected builtin names to be %v but got %v", expBuiltinNames, builtinNames) + } + }) +} + +func TestBundleInfoHasAnnotationLocationDataSet(t *testing.T) { + + files := map[string]string{ + "/fuz/fuz.rego": `# METADATA +# title: My package +package fuz + +p = 1`, + } + + test.WithTempFS(files, func(rootDir string) { + info, err := File(rootDir, true) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if got, exp := len(info.Annotations), 1; got != exp { + t.Fatalf("expected %d annotation, but got: %d", exp, got) + } + + astJSON.SetOptions(astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + AnnotationsRef: true, + }, + }, + }) + defer astJSON.SetOptions(astJSON.Defaults()) + + bs, err := json.Marshal(info.Annotations[0]) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + exp := fmt.Sprintf(`"location":{"file":"%s/fuz/fuz.rego","row":3,"col":1}`, rootDir) + + if got := string(bs); !strings.Contains(got, exp) { + t.Fatalf("expected to find %q in %q", exp, got) + } + }) +} + +func TestGenerateBundleInfoWithFile(t *testing.T) { + files := map[string]string{ + "bundle.tar.gz": "", + } + + mod := "package b.c\np=1" + + test.WithTempFS(files, func(rootDir string) { + bundleFile := filepath.Join(rootDir, "bundle.tar.gz") + + f, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + b := &bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"a", "b/c"}, + Revision: "123", + }, + Data: map[string]any{ + "a": map[string]any{ + "b": []int{4, 5, 6}, + }, + }, + Modules: []bundle.ModuleFile{ + { + URL: path.Join(bundleFile, "policy.rego"), + Path: "/policy.rego", + Raw: []byte(mod), + Parsed: ast.MustParseModule(mod), + }, + }, + } + + err = bundle.Write(f, *b) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + err = f.Close() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + info, err := File(bundleFile, false) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if !info.Manifest.Equal(b.Manifest) { + t.Fatalf("expected manifest %v, but got: %v", b.Manifest, info.Manifest) + } + + expectedNamespaces := map[string][]string{ + "data": {"/data.json"}, + "data.b.c": {"/policy.rego"}, + } + + if !reflect.DeepEqual(info.Namespaces, expectedNamespaces) { + t.Fatalf("expected namespaces %v, but got %v", expectedNamespaces, info.Namespaces) + } + }) +} + +func TestGenerateBundleInfoWithBundleTarGz(t *testing.T) { + signedTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJmaWxlcyI6W3sibmFtZSI6Ii5tYW5pZmVzdCIsImhhc2giOiI1MDdhMmMzOGExNDQxZGI1OGQyY2I4Nzk4MmM0MmFhOTFhNDM0MmVmNDIyYTZiNTQyZWRkZWJlZWY2ZjA0MTJmIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9LHsibmFtZSI6ImEvYi9jL2RhdGEuanNvbiIsImhhc2giOiI0MmNmZTY3NjhiNTdiYjVmNzUwM2MxNjVjMjhkZDA3YWM1YjgxMzU1NGViYzg1MGYyY2MzNTg0M2U3MTM3YjFkIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9LHsibmFtZSI6Imh0dHAvcG9saWN5L3BvbGljeS5yZWdvIiwiaGFzaCI6ImE2MTVlZWFlZTIxZGU1MTc5ZGUwODBkZThjMzA1MmM4ZGE5MDExMzg0MDZiYTcxYzM4YzAzMjg0NWY3ZDU0ZjQiLCJhbGdvcml0aG0iOiJTSEEtMjU2In1dLCJpYXQiOjE1OTIyNDgwMjcsImlzcyI6IkpXVFNlcnZpY2UiLCJrZXlpZCI6ImZvbyIsInNjb3BlIjoid3JpdGUifQ.grzWHYvyVS6LfWy0oiFTEJThKooOAwic8sexYaflzOM` + + files := [][2]string{ + {"/.signatures.json", fmt.Sprintf(`{"signatures": ["%v"]}`, signedTokenHS256)}, + {"/.manifest", `{"revision": "quickbrownfaux", "metadata": {"foo": "bar"}, "wasm": [{"entrypoint": "http/example/authz/allow", "module": "/policy.wasm"}, {"entrypoint": "http/example/foo/allow", "module": "/example/policy.wasm"}]}`}, + {"/a/b/c/data.json", "[1,2,3]"}, + {"/a/b/d/data.json", "true"}, + {"/a/b/y/data.yaml", `foo: 1`}, + {"/a/b/y/foo.rego", `package a.b.y`}, + {"/example/example.rego", `package example`}, + {"/example/policy.wasm", `modules-compiled-as-wasm-binary`}, + {"/policy.wasm", `modules-compiled-as-wasm-binary`}, + {"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`}, + } + + buf := archive.MustWriteTarGz(files) + + test.WithTempFS(nil, func(rootDir string) { + bundleFile := filepath.Join(rootDir, "bundle.tar.gz") + + out, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = out.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + info, err := File(bundleFile, false) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + metadata := map[string]any{"foo": "bar"} + wasmResolvers := []bundle.WasmResolver{{ + Entrypoint: "http/example/authz/allow", + Module: "/policy.wasm", + }, { + Entrypoint: "http/example/foo/allow", + Module: "/example/policy.wasm"}, + } + + expectedManifest := bundle.Manifest{ + Revision: "quickbrownfaux", + WasmResolvers: wasmResolvers, + Metadata: metadata, + } + + if !info.Manifest.Equal(expectedManifest) { + t.Fatalf("expected manifest %v, but got: %v", expectedManifest, info.Manifest) + } + + expectedNamespaces := map[string][]string{ + "data.example": {"/example/example.rego"}, + "data": {"/data.json"}, + "data.a.b.c": {"/a/b/c/data.json"}, + "data.a.b.d": {"/a/b/d/data.json"}, + "data.a.b.y": {"/a/b/y/foo.rego", "/a/b/y/data.yaml"}, + "data.http.example.authz.allow": {"/policy.wasm"}, + "data.http.example.foo.allow": {"/example/policy.wasm"}, + } + + if !reflect.DeepEqual(info.Namespaces, expectedNamespaces) { + t.Fatalf("expected namespaces %v, but got %v", expectedNamespaces, info.Namespaces) + } + + expectedWasmModules := []map[string]any{} + expectedWasmModule1 := map[string]any{ + "path": "/example/policy.wasm", + "url": filepath.Join(bundleFile, "example", "policy.wasm"), + "entrypoints": []string{"data.http.example.foo.allow"}, + } + + expectedWasmModule2 := map[string]any{ + "path": "/policy.wasm", + "url": filepath.Join(bundleFile, "policy.wasm"), + "entrypoints": []string{"data.http.example.authz.allow"}, + } + + expectedWasmModules = append(expectedWasmModules, expectedWasmModule1, expectedWasmModule2) + + if !reflect.DeepEqual(info.WasmModules, expectedWasmModules) { + t.Fatalf("expected wasm modules %v, but got %v", expectedWasmModules, info.WasmModules) + } + + expectedSign := bundle.SignaturesConfig{ + Signatures: []string{signedTokenHS256}, + } + + if !reflect.DeepEqual(info.Signatures, expectedSign) { + t.Fatalf("expected signature config %v, but got %v", expectedSign, info.Signatures) + } + }) +} diff --git a/third_party/opa/internal/bundle/utils.go b/third_party/opa/internal/bundle/utils.go new file mode 100644 index 000000000000..836aa586b934 --- /dev/null +++ b/third_party/opa/internal/bundle/utils.go @@ -0,0 +1,147 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "context" + "errors" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/resolver/wasm" + "github.com/open-policy-agent/opa/v1/storage" +) + +// LoadWasmResolversFromStore will lookup all Wasm modules from the store along with the +// associated bundle manifest configuration and instantiate the respective resolvers. +func LoadWasmResolversFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, otherBundles map[string]*bundle.Bundle) ([]*wasm.Resolver, error) { + bundleNames, err := bundle.ReadBundleNamesFromStore(ctx, store, txn) + if err != nil && !storage.IsNotFound(err) { + return nil, err + } + + var resolversToLoad []*bundle.WasmModuleFile + for _, bundleName := range bundleNames { + var wasmResolverConfigs []bundle.WasmResolver + rawModules := map[string][]byte{} + + // Save round-tripping the bundle that was just activated + if _, ok := otherBundles[bundleName]; ok { + wasmResolverConfigs = otherBundles[bundleName].Manifest.WasmResolvers + for _, wmf := range otherBundles[bundleName].WasmModules { + rawModules[wmf.Path] = wmf.Raw + } + } else { + wasmResolverConfigs, err = bundle.ReadWasmMetadataFromStore(ctx, store, txn, bundleName) + if err != nil && !storage.IsNotFound(err) { + return nil, fmt.Errorf("failed to read wasm module manifest from store: %s", err) + } + rawModules, err = bundle.ReadWasmModulesFromStore(ctx, store, txn, bundleName) + if err != nil && !storage.IsNotFound(err) { + return nil, fmt.Errorf("failed to read wasm modules from store: %s", err) + } + } + + for path, raw := range rawModules { + wmf := &bundle.WasmModuleFile{ + URL: path, + Path: path, + Raw: raw, + } + for _, resolverConf := range wasmResolverConfigs { + if resolverConf.Module == path { + ref, err := ast.PtrRef(ast.DefaultRootDocument, resolverConf.Entrypoint) + if err != nil { + return nil, fmt.Errorf("failed to parse wasm module entrypoint '%s': %s", resolverConf.Entrypoint, err) + } + wmf.Entrypoints = append(wmf.Entrypoints, ref) + } + } + if len(wmf.Entrypoints) > 0 { + resolversToLoad = append(resolversToLoad, wmf) + } + } + } + + var resolvers []*wasm.Resolver + if len(resolversToLoad) > 0 { + // Get a full snapshot of the current data (including any from "outside" the bundles) + data, err := store.Read(ctx, txn, storage.Path{}) + if err != nil { + return nil, fmt.Errorf("failed to initialize wasm runtime: %s", err) + } + + for _, wmf := range resolversToLoad { + resolver, err := wasm.New(wmf.Entrypoints, wmf.Raw, data) + if err != nil { + return nil, fmt.Errorf("failed to initialize wasm module for entrypoints '%s': %s", wmf.Entrypoints, err) + } + resolvers = append(resolvers, resolver) + } + } + return resolvers, nil +} + +// LoadBundleFromDisk loads a previously persisted activated bundle from disk +func LoadBundleFromDisk(path, name string, bvc *bundle.VerificationConfig) (*bundle.Bundle, error) { + return LoadBundleFromDiskForRegoVersion(ast.RegoV0, path, name, bvc) +} + +func LoadBundleFromDiskForRegoVersion(regoVersion ast.RegoVersion, path, name string, bvc *bundle.VerificationConfig) (*bundle.Bundle, error) { + bundlePath := filepath.Join(path, name, "bundle.tar.gz") + + _, err := os.Stat(bundlePath) + if err == nil { + f, err := os.Open(bundlePath) + if err != nil { + return nil, err + } + defer f.Close() + + r := bundle.NewCustomReader(bundle.NewTarballLoaderWithBaseURL(f, "")). + WithRegoVersion(regoVersion) + + if bvc != nil { + r = r.WithBundleVerificationConfig(bvc) + } + + b, err := r.Read() + if err != nil { + return nil, err + } + return &b, nil + } else if os.IsNotExist(err) { + return nil, nil + } + + return nil, err +} + +// SaveBundleToDisk saves the given raw bytes representing the bundle's content to disk +func SaveBundleToDisk(path string, raw io.Reader) (string, error) { + if _, err := os.Stat(path); os.IsNotExist(err) { + err = os.MkdirAll(path, os.ModePerm) + if err != nil { + return "", err + } + } + + if raw == nil { + return "", errors.New("no raw bundle bytes to persist to disk") + } + + dest, err := os.CreateTemp(path, ".bundle.tar.gz.*.tmp") + if err != nil { + return "", err + } + defer dest.Close() + + _, err = io.Copy(dest, raw) + return dest.Name(), err +} diff --git a/third_party/opa/internal/cidr/merge/merge.go b/third_party/opa/internal/cidr/merge/merge.go new file mode 100644 index 000000000000..c2392b677540 --- /dev/null +++ b/third_party/opa/internal/cidr/merge/merge.go @@ -0,0 +1,367 @@ +// Copyright 2017-2020 Authors of Cilium +// +// 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 merge provides helper functions for merging a list of +// IP addresses and subnets into the smallest possible list of CIDRs. +// Original Implementation: https://github.com/cilium/cilium +package merge + +import ( + "bytes" + "encoding/binary" + "math/big" + "net" +) + +const ( + ipv4BitLen = 8 * net.IPv4len + ipv6BitLen = 8 * net.IPv6len +) + +var ( + v4Mappedv6Prefix = []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff} + defaultIPv4 = []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0} + defaultIPv6 = []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} + upperIPv4 = []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 255, 255, 255, 255} + upperIPv6 = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + ipv4LeadingZeroes = []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} +) + +// RangeToCIDRs converts the range of IPs covered by firstIP and lastIP to +// a list of CIDRs that contains all of the IPs covered by the range. +func RangeToCIDRs(firstIP, lastIP net.IP) []*net.IPNet { + // First, create a CIDR that spans both IPs. + spanningCIDR := createSpanningCIDR(&firstIP, &lastIP) + firstIPSpanning, lastIPSpanning := GetAddressRange(spanningCIDR) + + cidrList := []*net.IPNet{} + + // If the first IP of the spanning CIDR passes the lower bound (firstIP), + // we need to split the spanning CIDR and only take the IPs that are + // greater than the value which we split on, as we do not want the lesser + // values since they are less than the lower-bound (firstIP). + if bytes.Compare(firstIPSpanning, firstIP) < 0 { + // Split on the previous IP of the first IP so that the right list of IPs + // of the partition includes the firstIP. + prevFirstRangeIP := GetPreviousIP(firstIP) + var bitLen int + if prevFirstRangeIP.To4() != nil { + bitLen = ipv4BitLen + } else { + bitLen = ipv6BitLen + } + _, _, right := partitionCIDR(spanningCIDR, net.IPNet{IP: prevFirstRangeIP, Mask: net.CIDRMask(bitLen, bitLen)}) + + // Append all CIDRs but the first, as this CIDR includes the upper + // bound of the spanning CIDR, which we still need to partition on. + cidrList = append(cidrList, right...) + spanningCIDR = *right[0] + cidrList = cidrList[1:] + } + + // Conversely, if the last IP of the spanning CIDR passes the upper bound + // (lastIP), we need to split the spanning CIDR and only take the IPs that + // are greater than the value which we split on, as we do not want the greater + // values since they are greater than the upper-bound (lastIP). + if bytes.Compare(lastIPSpanning, lastIP) > 0 { + // Split on the next IP of the last IP so that the left list of IPs + // of the partition include the lastIP. + nextFirstRangeIP := getNextIP(lastIP) + var bitLen int + if nextFirstRangeIP.To4() != nil { + bitLen = ipv4BitLen + } else { + bitLen = ipv6BitLen + } + left, _, _ := partitionCIDR(spanningCIDR, net.IPNet{IP: nextFirstRangeIP, Mask: net.CIDRMask(bitLen, bitLen)}) + cidrList = append(cidrList, left...) + } else { + // Otherwise, there is no need to partition; just use add the spanning + // CIDR to the list of networks. + cidrList = append(cidrList, &spanningCIDR) + } + return cidrList +} + +// GetAddressRange returns the first and last addresses in the given CIDR range. +func GetAddressRange(ipNet net.IPNet) (net.IP, net.IP) { + firstIP := make(net.IP, len(ipNet.IP)) + lastIP := make(net.IP, len(ipNet.IP)) + + copy(firstIP, ipNet.IP) + copy(lastIP, ipNet.IP) + + firstIP = firstIP.Mask(ipNet.Mask) + lastIP = lastIP.Mask(ipNet.Mask) + + if firstIP.To4() != nil { + firstIP = append(v4Mappedv6Prefix, firstIP...) + lastIP = append(v4Mappedv6Prefix, lastIP...) + } + + lastIPMask := make(net.IPMask, len(ipNet.Mask)) + copy(lastIPMask, ipNet.Mask) + for i := range lastIPMask { + lastIPMask[len(lastIPMask)-i-1] = ^lastIPMask[len(lastIPMask)-i-1] + lastIP[net.IPv6len-i-1] |= lastIPMask[len(lastIPMask)-i-1] + } + + return firstIP, lastIP +} + +// GetPreviousIP returns the previous IP from the given IP address. +func GetPreviousIP(ip net.IP) net.IP { + // Cannot go lower than zero! + if ip.Equal(net.IP(defaultIPv4)) || ip.Equal(net.IP(defaultIPv6)) { + return ip + } + + previousIP := make(net.IP, len(ip)) + copy(previousIP, ip) + + var overflow bool + var lowerByteBound int + if ip.To4() != nil { + lowerByteBound = net.IPv6len - net.IPv4len + } else { + lowerByteBound = 0 + } + for i := len(ip) - 1; i >= lowerByteBound; i-- { + if overflow || i == len(ip)-1 { + previousIP[i]-- + } + // Track if we have overflowed and thus need to continue subtracting. + if ip[i] == 0 && previousIP[i] == 255 { + overflow = true + } else { + overflow = false + } + } + return previousIP +} + +// createSpanningCIDR returns a single IP network spanning the +// the lower and upper bound IP addresses. +func createSpanningCIDR(firstIP, lastIP *net.IP) net.IPNet { + // Don't want to modify the values of the provided range, so make copies. + lowest := *firstIP + highest := *lastIP + + var isIPv4 bool + var spanningMaskSize, bitLen, byteLen int + if lowest.To4() != nil { + isIPv4 = true + bitLen = ipv4BitLen + byteLen = net.IPv4len + } else { + bitLen = ipv6BitLen + byteLen = net.IPv6len + } + + if isIPv4 { + spanningMaskSize = ipv4BitLen + } else { + spanningMaskSize = ipv6BitLen + } + + // Convert to big Int so we can easily do bitshifting on the IP addresses, + // since golang only provides up to 64-bit unsigned integers. + lowestBig := big.NewInt(0).SetBytes(lowest) + highestBig := big.NewInt(0).SetBytes(highest) + + // Starting from largest mask / smallest range possible, apply a mask one bit + // larger in each iteration to the upper bound in the range until we have + // masked enough to pass the lower bound in the range. This + // gives us the size of the prefix for the spanning CIDR to return as + // well as the IP for the CIDR prefix of the spanning CIDR. + for spanningMaskSize > 0 && lowestBig.Cmp(highestBig) < 0 { + spanningMaskSize-- + mask := big.NewInt(1) + mask = mask.Lsh(mask, uint(bitLen-spanningMaskSize)) + mask = mask.Mul(mask, big.NewInt(-1)) + highestBig = highestBig.And(highestBig, mask) + } + + // If ipv4, need to append 0s because math.Big gets rid of preceding zeroes. + if isIPv4 { + highest = append(ipv4LeadingZeroes, highestBig.Bytes()...) + } else { + highest = highestBig.Bytes() + } + + // Int does not store leading zeroes. + if len(highest) == 0 { + highest = make([]byte, byteLen) + } + + newNet := net.IPNet{IP: highest, Mask: net.CIDRMask(spanningMaskSize, bitLen)} + return newNet +} + +// partitionCIDR returns a list of IP Networks partitioned upon excludeCIDR. +// The first list contains the networks to the left of the excludeCIDR in the +// partition, the second is a list containing the excludeCIDR itself if it is +// contained within the targetCIDR (nil otherwise), and the +// third is a list containing the networks to the right of the excludeCIDR in +// the partition. +func partitionCIDR(targetCIDR net.IPNet, excludeCIDR net.IPNet) ([]*net.IPNet, []*net.IPNet, []*net.IPNet) { + var targetIsIPv4 bool + if targetCIDR.IP.To4() != nil { + targetIsIPv4 = true + } + + targetFirstIP, targetLastIP := GetAddressRange(targetCIDR) + excludeFirstIP, excludeLastIP := GetAddressRange(excludeCIDR) + + targetMaskSize, _ := targetCIDR.Mask.Size() + excludeMaskSize, _ := excludeCIDR.Mask.Size() + + if bytes.Compare(excludeLastIP, targetFirstIP) < 0 { + return nil, nil, []*net.IPNet{&targetCIDR} + } else if bytes.Compare(targetLastIP, excludeFirstIP) < 0 { + return []*net.IPNet{&targetCIDR}, nil, nil + } + + if targetMaskSize >= excludeMaskSize { + return nil, []*net.IPNet{&targetCIDR}, nil + } + + left := []*net.IPNet{} + right := []*net.IPNet{} + + newPrefixLen := targetMaskSize + 1 + + targetFirstCopy := make(net.IP, len(targetFirstIP)) + copy(targetFirstCopy, targetFirstIP) + + iLowerOld := make(net.IP, len(targetFirstCopy)) + copy(iLowerOld, targetFirstCopy) + + // Since golang only supports up to unsigned 64-bit integers, and we need + // to perform addition on addresses, use math/big library, which allows + // for manipulation of large integers. + + // Used to track the current lower and upper bounds of the ranges to compare + // to excludeCIDR. + iLower := big.NewInt(0) + iUpper := big.NewInt(0) + iLower = iLower.SetBytes(targetFirstCopy) + + var bitLen int + + if targetIsIPv4 { + bitLen = ipv4BitLen + } else { + bitLen = ipv6BitLen + } + shiftAmount := (uint)(bitLen - newPrefixLen) + + targetIPInt := big.NewInt(0) + targetIPInt.SetBytes(targetFirstIP.To16()) + + exp := big.NewInt(0) + + // Use left shift for exponentiation + exp = exp.Lsh(big.NewInt(1), shiftAmount) + iUpper = iUpper.Add(targetIPInt, exp) + + matched := big.NewInt(0) + + for excludeMaskSize >= newPrefixLen { + // Append leading zeros to IPv4 addresses, as math.Big.Int does not + // append them when the IP address is copied from a byte array to + // math.Big.Int. Leading zeroes are required for parsing IPv4 addresses + // for use with net.IP / net.IPNet. + var iUpperBytes, iLowerBytes []byte + if targetIsIPv4 { + iUpperBytes = append(ipv4LeadingZeroes, iUpper.Bytes()...) + iLowerBytes = append(ipv4LeadingZeroes, iLower.Bytes()...) + } else { + iUpperBytesLen := len(iUpper.Bytes()) + // Make sure that the number of bytes in the array matches what net + // package expects, as big package doesn't append leading zeroes. + if iUpperBytesLen != net.IPv6len { + numZeroesToAppend := net.IPv6len - iUpperBytesLen + zeroBytes := make([]byte, numZeroesToAppend) + iUpperBytes = append(zeroBytes, iUpper.Bytes()...) + } else { + iUpperBytes = iUpper.Bytes() + + } + + iLowerBytesLen := len(iLower.Bytes()) + if iLowerBytesLen != net.IPv6len { + numZeroesToAppend := net.IPv6len - iLowerBytesLen + zeroBytes := make([]byte, numZeroesToAppend) + iLowerBytes = append(zeroBytes, iLower.Bytes()...) + } else { + iLowerBytes = iLower.Bytes() + + } + } + // If the IP we are excluding over is of a higher value than the current + // CIDR prefix we are generating, add the CIDR prefix to the set of IPs + // to the left of the exclude CIDR + if bytes.Compare(excludeFirstIP, iUpperBytes) >= 0 { + left = append(left, &net.IPNet{IP: iLowerBytes, Mask: net.CIDRMask(newPrefixLen, bitLen)}) + matched = matched.Set(iUpper) + } else { + // Same as above, but opposite. + right = append(right, &net.IPNet{IP: iUpperBytes, Mask: net.CIDRMask(newPrefixLen, bitLen)}) + matched = matched.Set(iLower) + } + + newPrefixLen++ + + if newPrefixLen > bitLen { + break + } + + iLower = iLower.Set(matched) + iUpper = iUpper.Add(matched, big.NewInt(0).Lsh(big.NewInt(1), uint(bitLen-newPrefixLen))) + + } + excludeList := []*net.IPNet{&excludeCIDR} + + return left, excludeList, right +} + +func getNextIP(ip net.IP) net.IP { + if ip.Equal(upperIPv4) || ip.Equal(upperIPv6) { + return ip + } + + nextIP := make(net.IP, len(ip)) + switch len(ip) { + case net.IPv4len: + ipU32 := binary.BigEndian.Uint32(ip) + ipU32++ + binary.BigEndian.PutUint32(nextIP, ipU32) + return nextIP + case net.IPv6len: + ipU64 := binary.BigEndian.Uint64(ip[net.IPv6len/2:]) + ipU64++ + binary.BigEndian.PutUint64(nextIP[net.IPv6len/2:], ipU64) + if ipU64 == 0 { + ipU64 = binary.BigEndian.Uint64(ip[:net.IPv6len/2]) + ipU64++ + binary.BigEndian.PutUint64(nextIP[:net.IPv6len/2], ipU64) + } else { + copy(nextIP[:net.IPv6len/2], ip[:net.IPv6len/2]) + } + return nextIP + default: + return ip + } +} diff --git a/third_party/opa/internal/cmd/genbuiltinmetadata/main.go b/third_party/opa/internal/cmd/genbuiltinmetadata/main.go new file mode 100644 index 000000000000..4cff1afc620a --- /dev/null +++ b/third_party/opa/internal/cmd/genbuiltinmetadata/main.go @@ -0,0 +1,172 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/json" + "log" + "os" + "strings" + + "github.com/open-policy-agent/opa/internal/compiler/wasm" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/types" +) + +func main() { + f := ast.CapabilitiesForThisVersion() + sorted := sortedCaps() + sorted = append(sorted, versionedCaps{version: "edge", caps: f}) + + mdata := make(map[string]any) + categories := make(map[string][]string) + + for _, bi := range f.Builtins { + latest := getLatest(bi.Name, sorted) + for _, cat := range builtinCategories(latest) { + categories[cat] = append(categories[cat], bi.Name) + } + + argTypes := make([]map[string]any, len(latest.Decl.FuncArgs().Args)) + + for i, typ := range latest.Decl.NamedFuncArgs().Args { + if n, ok := typ.(*types.NamedType); ok { + argTypes[i] = map[string]any{ + "name": n.Name, + "type": n.Type.String(), + } + if n.Descr != "" { + argTypes[i]["description"] = n.Descr + } + } else { + argTypes[i] = map[string]any{ + "type": typ.String(), + } + } + } + res := map[string]any{} + resType := latest.Decl.NamedResult() + if n, ok := resType.(*types.NamedType); ok { + res["name"] = n.Name + if n.Descr != "" { + res["description"] = n.Descr + } + res["type"] = n.Type.String() + } else if resType != nil { + res["type"] = resType.String() + } + versions := getVersions(bi.Name, sorted) + md := map[string]any{ + "introduced": versions[0], + "available": versions, + "wasm": getWasm(bi.Name), + "args": argTypes, + "result": res, + } + if latest.Relation { + md["relation"] = true + } + if latest.Infix != "" { + md["infix"] = latest.Infix + } + if latest.Description != "" { + md["description"] = latest.Description + } + if latest.IsDeprecated() { + md["deprecated"] = true + } + mdata[bi.Name] = md + } + + mdata["_categories"] = categories + + md, err := os.Create(os.Args[1]) // metadata + if err != nil { + panic(err) + } + + enc := json.NewEncoder(md) + enc.SetIndent("", " ") + + if err := enc.Encode(mdata); err != nil { + panic(err) + } + + if err := md.Close(); err != nil { + panic(err) + } +} + +func getVersions(bi string, sorted []versionedCaps) []string { + vers := []string{} + for i := range sorted { + for j := range sorted[i].caps.Builtins { + if sorted[i].caps.Builtins[j].Name == bi { + vers = append(vers, sorted[i].version) + } + } + } + return vers +} + +func getLatest(bi string, sorted []versionedCaps) *ast.Builtin { + for i := len(sorted) - 1; i >= 0; i++ { + for j := range sorted[i].caps.Builtins { + if sorted[i].caps.Builtins[j].Name == bi { + return sorted[i].caps.Builtins[j] + } + } + } + panic("unreachable") +} + +func getWasm(bi string) bool { + return wasm.IsWasmEnabled(bi) +} + +type versionedCaps struct { + version string + caps *ast.Capabilities +} + +func sortedCaps() []versionedCaps { + vers, err := ast.LoadCapabilitiesVersions() + if err != nil { + panic(err) + } + sorted := make([]versionedCaps, len(vers)) + for i, v := range vers { + caps, err := ast.LoadCapabilitiesVersion(v) + if err != nil { + panic(err) + } + sorted[i] = versionedCaps{ + version: v, + caps: caps, + } + } + return sorted +} + +func builtinCategories(b *ast.Builtin) []string { + if b.IsDeprecated() { + return nil + } + if len(b.Categories) > 0 { + return b.Categories + } + if s := strings.Split(b.Name, "."); len(s) > 1 { + return []string{s[0]} + } + + switch b.Name { + case "assign", "eq", "print": + // Do nothing. + default: + log.Printf("WARN: not categorized: %s", b.Name) + } + + return nil +} diff --git a/third_party/opa/internal/cmd/genopacapabilities/main.go b/third_party/opa/internal/cmd/genopacapabilities/main.go new file mode 100644 index 000000000000..c9b06d241e27 --- /dev/null +++ b/third_party/opa/internal/cmd/genopacapabilities/main.go @@ -0,0 +1,37 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/json" + "os" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func main() { + f := ast.CapabilitiesForThisVersion() + + fd, err := os.Create(os.Args[1]) + if err != nil { + panic(err) + } + + enc := json.NewEncoder(fd) + enc.SetIndent("", " ") + + for i, bi := range f.Builtins { + // NOTE(sr): This ensures that there are no type names and descriptions in capabilities.json + f.Builtins[i] = bi.Minimal() + } + + if err := enc.Encode(f); err != nil { + panic(err) + } + + if err := fd.Close(); err != nil { + panic(err) + } +} diff --git a/third_party/opa/internal/cmd/genversionindex/main.go b/third_party/opa/internal/cmd/genversionindex/main.go new file mode 100644 index 000000000000..eb00830819b1 --- /dev/null +++ b/third_party/opa/internal/cmd/genversionindex/main.go @@ -0,0 +1,78 @@ +package main + +import ( + "encoding/json" + "os" + + "github.com/open-policy-agent/opa/internal/semver" + "github.com/open-policy-agent/opa/v1/ast" +) + +func minVersionIndex() ast.VersionIndex { + + index := ast.VersionIndex{ + Builtins: map[string]semver.Version{}, + Features: map[string]semver.Version{}, + Keywords: map[string]semver.Version{}, + } + + versions, err := ast.LoadCapabilitiesVersions() + if err != nil { + panic(err) + } + + for _, v := range versions { + var sv semver.Version + if err := sv.Set(v[1:]); err != nil { + panic(err) + } + + c, err := ast.LoadCapabilitiesVersion(v) + if err != nil { + panic(err) + } + + for _, bi := range c.Builtins { + exist, ok := index.Builtins[bi.Name] + if !ok || exist.Compare(sv) > 0 { + index.Builtins[bi.Name] = sv + } + } + + for _, kw := range c.FutureKeywords { + exist, ok := index.Keywords[kw] + if !ok || exist.Compare(sv) > 0 { + index.Keywords[kw] = sv + } + } + + for _, feat := range c.Features { + exist, ok := index.Features[feat] + if !ok || exist.Compare(sv) > 0 { + index.Features[feat] = sv + } + } + } + + return index +} + +func main() { + fd, err := os.Create(os.Args[1]) + if err != nil { + panic(err) + } + + enc := json.NewEncoder(fd) + enc.SetIndent("", " ") + + vi := minVersionIndex() + + if err := enc.Encode(vi); err != nil { + panic(err) + } + + if err := fd.Close(); err != nil { + panic(err) + } +} diff --git a/third_party/opa/internal/compiler/utils.go b/third_party/opa/internal/compiler/utils.go new file mode 100644 index 000000000000..5d2e778b139a --- /dev/null +++ b/third_party/opa/internal/compiler/utils.go @@ -0,0 +1,95 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package compiler + +import ( + "errors" + "sync" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/schemas" + "github.com/open-policy-agent/opa/v1/util" +) + +type SchemaFile string + +const ( + AuthorizationPolicySchema SchemaFile = "authorizationPolicy.json" +) + +var schemaDefinitions = map[SchemaFile]any{} + +var loadOnce = sync.OnceValue(func() error { + cont, err := schemas.FS.ReadFile(string(AuthorizationPolicySchema)) + if err != nil { + return err + } + + if len(cont) == 0 { + return errors.New("expected authorization policy schema file to be present") + } + + var schema any + if err := util.Unmarshal(cont, &schema); err != nil { + return err + } + + schemaDefinitions[AuthorizationPolicySchema] = schema + + return nil +}) + +// VerifyAuthorizationPolicySchema performs type checking on rules against the schema for the Authorization Policy +// Input document. +// NOTE: The provided compiler should have already run the compilation process on the input modules +func VerifyAuthorizationPolicySchema(compiler *ast.Compiler, ref ast.Ref) error { + if err := loadOnce(); err != nil { + panic(err) + } + + rules := getRulesWithDependencies(compiler, ref) + + if len(rules) == 0 { + return nil + } + + schemaSet := ast.NewSchemaSet() + schemaSet.Put(ast.SchemaRootRef, schemaDefinitions[AuthorizationPolicySchema]) + + errs := ast.NewCompiler(). + WithDefaultRegoVersion(compiler.DefaultRegoVersion()). + WithSchemas(schemaSet). + PassesTypeCheckRules(rules) + + if len(errs) > 0 { + return errs + } + + return nil +} + +// getRulesWithDependencies returns a slice of rules that are referred to by ref along with their dependencies +func getRulesWithDependencies(compiler *ast.Compiler, ref ast.Ref) []*ast.Rule { + allRules := compiler.GetRules(ref) + + deps := map[*ast.Rule]struct{}{} + for _, rule := range allRules { + transitiveDependencies(compiler, rule, deps) + } + + for dep := range deps { + allRules = append(allRules, dep) + } + + return allRules +} + +func transitiveDependencies(compiler *ast.Compiler, rule *ast.Rule, deps map[*ast.Rule]struct{}) { + for x := range compiler.Graph.Dependencies(rule) { + other := x.(*ast.Rule) + deps[other] = struct{}{} + transitiveDependencies(compiler, other, deps) + } +} diff --git a/third_party/opa/internal/compiler/utils_test.go b/third_party/opa/internal/compiler/utils_test.go new file mode 100644 index 000000000000..0bf2a25b7226 --- /dev/null +++ b/third_party/opa/internal/compiler/utils_test.go @@ -0,0 +1,135 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package compiler + +import ( + "fmt" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestVerifyAuthorizationPolicySchema(t *testing.T) { + + module1 := ` + package policy + + default allow := false + + allow if { + input.identity = "foo" + } + + allow if { + input.client_certificates[0] = {"foo": "bar"} + } + + allow if { + input.method = "GET" + } + + allow if { + input.path = ["foo", "bar"] + } + + allow if { + "foo" in input.path + } + + allow if { + input.params = {"foo": "bar"} + } + + allow if { + input.headers = {"foo": "bar"} + } + + allow if { + input.body.input.stock = "ACME" + }` + + module2 := ` + package policy + + default allow := false + + allow if { + input.identty = "foo" + } + + allow if { + input.path = "foo" + }` + + module3 := ` + package policy + + default allow := false + + allow if { + input.path = [1, 2, 3] + }` + + module4 := ` + package policy + + default allow := false + + allow if { + input.client_certificates[0] = "foo" + }` + + tests := []struct { + note string + modules []string + wantErr bool + errs []string + }{ + {note: "no rules", modules: []string{}}, + {note: "no error", modules: []string{module1}}, + {note: "multiple errors", modules: []string{module2}, wantErr: true, errs: []string{"match error", "undefined ref: input.identty"}}, + {note: "wrong item type path", modules: []string{module3}, wantErr: true, errs: []string{"match error"}}, + {note: "wrong item type certs", modules: []string{module4}, wantErr: true, errs: []string{"match error"}}, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + modules := map[string]*ast.Module{} + + for i, module := range tc.modules { + mod, err := ast.ParseModuleWithOpts(fmt.Sprintf("test%d.rego", i+1), module, + ast.ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatal(err) + } + + modules[fmt.Sprintf("test%d.rego", i+1)] = mod + } + + c := ast.NewCompiler() + c.Compile(modules) + if c.Failed() { + t.Fatal("unexpected error:", c.Errors) + } + + err := VerifyAuthorizationPolicySchema(c, ast.MustParseRef("data.policy.allow")) + + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + for _, e := range tc.errs { + if !strings.Contains(err.Error(), e) { + t.Errorf("Expected error %v not found", e) + } + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + } +} diff --git a/third_party/opa/internal/compiler/wasm/opa/callgraph.csv b/third_party/opa/internal/compiler/wasm/opa/callgraph.csv new file mode 100644 index 000000000000..473497abbdad --- /dev/null +++ b/third_party/opa/internal/compiler/wasm/opa/callgraph.csv @@ -0,0 +1,2461 @@ +opa_agg_count,opa_value_type +opa_agg_count,chartorune +opa_agg_count,opa_number_int +opa_agg_sum,opa_value_type +opa_agg_sum,mpd_qnew +opa_agg_sum,mpd_max_ctx +opa_agg_sum,mpd_qset_i32 +opa_agg_sum,opa_abort +opa_agg_sum,mpd_del +opa_agg_sum,opa_number_to_bf +opa_agg_sum,qadd +opa_agg_sum,opa_bf_to_number +opa_agg_product,opa_value_type +opa_agg_product,mpd_qnew +opa_agg_product,mpd_max_ctx +opa_agg_product,mpd_qset_i32 +opa_agg_product,opa_abort +opa_agg_product,mpd_del +opa_agg_product,opa_number_to_bf +opa_agg_product,qmul +opa_agg_product,opa_bf_to_number +opa_agg_max,opa_value_type +opa_agg_max,opa_value_compare +opa_agg_min,opa_value_type +opa_agg_min,opa_value_compare +opa_agg_sort,opa_value_type +opa_agg_sort,opa_array_with_cap +opa_agg_sort,opa_array_append +opa_agg_sort,opa_value_shallow_copy +opa_agg_sort,opa_array_sort +opa_agg_all,opa_value_type +opa_agg_all,opa_boolean +opa_agg_any,opa_value_type +opa_agg_any,opa_boolean +opa_agg_any,opa_set_get +builtin_member,opa_value_iter +builtin_member,opa_value_get +builtin_member,opa_value_compare +builtin_member,opa_boolean +builtin_member3,opa_value_type +builtin_member3,opa_value_get +builtin_member3,opa_value_compare +builtin_member3,opa_boolean +opa_arith_abs,opa_number_to_bf +opa_arith_abs,mpd_qnew +opa_arith_abs,mpd_max_ctx +opa_arith_abs,mpd_qabs +opa_arith_abs,mpd_del +opa_arith_abs,opa_abort +opa_arith_abs,opa_bf_to_number +opa_arith_round,opa_number_to_bf +opa_arith_round,mpd_qnew +opa_arith_round,mpd_max_ctx +opa_arith_round,mpd_qround_to_int +opa_arith_round,mpd_del +opa_arith_round,opa_abort +opa_arith_round,opa_bf_to_number +opa_arith_ceil,opa_number_to_bf +opa_arith_ceil,mpd_qnew +opa_arith_ceil,mpd_max_ctx +opa_arith_ceil,mpd_qceil +opa_arith_ceil,mpd_del +opa_arith_ceil,opa_bf_to_number +opa_arith_floor,opa_number_to_bf +opa_arith_floor,mpd_qnew +opa_arith_floor,mpd_max_ctx +opa_arith_floor,mpd_qfloor +opa_arith_floor,mpd_del +opa_arith_floor,opa_bf_to_number +opa_arith_plus,opa_number_to_bf +opa_arith_plus,opa_mpd_del +opa_arith_plus,mpd_qnew +opa_arith_plus,mpd_max_ctx +opa_arith_plus,mpd_qadd +opa_arith_plus,mpd_del +opa_arith_plus,opa_abort +opa_arith_plus,opa_bf_to_number +opa_arith_minus,opa_number_to_bf +opa_arith_minus,mpd_qnew +opa_arith_minus,mpd_max_ctx +opa_arith_minus,mpd_qsub +opa_arith_minus,mpd_del +opa_arith_minus,opa_abort +opa_arith_minus,opa_bf_to_number +opa_arith_minus,opa_mpd_del +opa_arith_minus,opa_set_diff +opa_arith_multiply,opa_number_to_bf +opa_arith_multiply,opa_mpd_del +opa_arith_multiply,mpd_qnew +opa_arith_multiply,mpd_max_ctx +opa_arith_multiply,mpd_qmul +opa_arith_multiply,mpd_del +opa_arith_multiply,opa_abort +opa_arith_multiply,opa_bf_to_number +opa_arith_divide,opa_number_to_bf +opa_arith_divide,opa_mpd_del +opa_arith_divide,mpd_qnew +opa_arith_divide,mpd_default_ctx +opa_arith_divide,mpd_qdiv +opa_arith_divide,mpd_del +opa_arith_divide,opa_abort +opa_arith_divide,opa_bf_to_number +opa_arith_rem,opa_number_to_bf +opa_arith_rem,mpd_isinteger +opa_arith_rem,opa_mpd_del +opa_arith_rem,mpd_qnew +opa_arith_rem,mpd_max_ctx +opa_arith_rem,mpd_qrem +opa_arith_rem,mpd_del +opa_arith_rem,opa_bf_to_number +opa_array_concat,opa_value_type +opa_array_concat,opa_array_with_cap +opa_array_concat,opa_array_append +opa_array_slice,opa_value_type +opa_array_slice,opa_number_try_int +opa_array_slice,opa_array_with_cap +opa_array_slice,opa_array_append +opa_array_reverse,opa_value_type +opa_array_reverse,opa_array_with_cap +opa_array_reverse,opa_array_append +opa_bits_or,opa_number_to_bf +opa_bits_or,mpd_isinteger +opa_bits_or,opa_mpd_del +opa_bits_or,mpd_sign +opa_bits_or,qabs +opa_bits_or,qsub_one +opa_bits_or,qand +opa_bits_or,qadd_one +opa_bits_or,qneg +opa_bits_or,opa_bf_to_number +opa_bits_or,qor +opa_bits_or,qand_not +opa_bits_and,opa_number_to_bf +opa_bits_and,mpd_isinteger +opa_bits_and,opa_mpd_del +opa_bits_and,mpd_sign +opa_bits_and,qabs +opa_bits_and,qsub_one +opa_bits_and,qor +opa_bits_and,qadd_one +opa_bits_and,qneg +opa_bits_and,opa_bf_to_number +opa_bits_and,qand +opa_bits_and,qand_not +opa_bits_negate,opa_number_to_bf +opa_bits_negate,mpd_isinteger +opa_bits_negate,mpd_sign +opa_bits_negate,qabs +opa_bits_negate,opa_bf_to_bf_bits +opa_bits_negate,opa_bf_bits_to_bf +opa_bits_negate,qsub_one +opa_bits_negate,qadd_one +opa_bits_negate,qneg +opa_bits_negate,opa_bf_to_number +opa_bits_xor,opa_number_to_bf +opa_bits_xor,mpd_isinteger +opa_bits_xor,opa_mpd_del +opa_bits_xor,mpd_sign +opa_bits_xor,qabs +opa_bits_xor,qsub_one +opa_bits_xor,qxor +opa_bits_xor,opa_bf_to_number +opa_bits_xor,qadd_one +opa_bits_xor,qneg +opa_bits_shiftleft,opa_number_to_bf +opa_bits_shiftleft,opa_bf_to_bf_bits +opa_bits_shiftleft,opa_mpd_del +opa_bits_shiftleft,opa_value_type +opa_bits_shiftleft,opa_number_try_int +opa_bits_shiftleft,mpd_qnew +opa_bits_shiftleft,mpd_max_ctx +opa_bits_shiftleft,mpd_qshiftn +opa_bits_shiftleft,mpd_del +opa_bits_shiftleft,opa_abort +opa_bits_shiftleft,opa_bf_bits_to_bf +opa_bits_shiftleft,opa_bf_to_number +opa_bits_shiftright,opa_number_to_bf +opa_bits_shiftright,mpd_isinteger +opa_bits_shiftright,opa_value_type +opa_bits_shiftright,mpd_del +opa_bits_shiftright,opa_number_try_int +opa_bits_shiftright,mpd_sign +opa_bits_shiftright,qabs +opa_bits_shiftright,qsub_one +opa_bits_shiftright,opa_bf_to_bf_bits +opa_bits_shiftright,mpd_qshiftr_inplace +opa_bits_shiftright,opa_bf_bits_to_bf +opa_bits_shiftright,qadd_one +opa_bits_shiftright,qneg +opa_bits_shiftright,opa_bf_to_number +opa_cidr_contains,opa_value_type +opa_cidr_contains,parse_cidr +opa_cidr_contains,parse_ip +opa_cidr_contains,opa_boolean +parse_cidr,parse_ip +parse_cidr,opa_atoi64 +parse_ip,memchr +opa_cidr_intersects,opa_value_type +opa_cidr_intersects,parse_cidr +opa_cidr_intersects,opa_boolean +opa_cmp_eq,opa_value_compare +opa_cmp_eq,opa_boolean +opa_cmp_neq,opa_value_compare +opa_cmp_neq,opa_boolean +opa_cmp_gt,opa_value_compare +opa_cmp_gt,opa_boolean +opa_cmp_gte,opa_value_compare +opa_cmp_gte,opa_boolean +opa_cmp_lt,opa_value_compare +opa_cmp_lt,opa_boolean +opa_cmp_lte,opa_value_compare +opa_cmp_lte,opa_boolean +opa_eval_ctx_new,opa_malloc +opa_eval,opa_abort +opa_eval,opa_heap_ptr_set +opa_eval,opa_value_parse +opa_eval,eval +opa_eval,opa_value_dump +opa_eval,opa_json_dump +__force_import_opa_builtins,opa_builtin0 +__force_import_opa_builtins,opa_builtin1 +__force_import_opa_builtins,opa_builtin2 +__force_import_opa_builtins,opa_builtin3 +__force_import_opa_builtins,opa_builtin4 +opa_to_number,opa_value_type +opa_to_number,opa_number_int +opa_to_number,opa_atof64 +opa_to_number,opa_number_ref +opa_base64_is_valid,opa_value_type +opa_base64_is_valid,opa_boolean +opa_base64_is_valid,base64_gen_decode +opa_base64_is_valid,free +base64_gen_decode,memset +base64_gen_decode,malloc +base64_gen_decode,free +opa_base64_decode,opa_value_type +opa_base64_decode,base64_gen_decode +opa_base64_decode,opa_string_allocated +opa_base64_encode,opa_value_type +opa_base64_encode,base64_gen_encode +opa_base64_encode,opa_string_allocated +base64_gen_encode,malloc +opa_base64_url_decode,opa_value_type +opa_base64_url_decode,base64_gen_decode +opa_base64_url_decode,opa_string_allocated +opa_base64_url_encode,opa_value_type +opa_base64_url_encode,base64_gen_encode +opa_base64_url_encode,opa_string_allocated +opa_json_unmarshal,opa_value_type +opa_json_unmarshal,opa_json_parse +opa_json_marshal,opa_json_dump +opa_json_marshal,strlen +opa_json_marshal,opa_string_allocated +opa_json_is_valid,opa_value_type +opa_json_is_valid,opa_json_parse +opa_json_is_valid,opa_free +opa_json_is_valid,opa_boolean +opa_runtime_error,opa_itoa +opa_runtime_error,opa_strlen +opa_runtime_error,opa_malloc +opa_runtime_error,snprintf_ +opa_runtime_error,opa_abort +builtin_graph_reachable,opa_value_type +builtin_graph_reachable,opa_array +builtin_graph_reachable,opa_array_append +builtin_graph_reachable,opa_set +builtin_graph_reachable,opa_value_get +builtin_graph_reachable,opa_set_get +builtin_graph_reachable,opa_set_add +opa_json_lex_read_number,opa_isdigit +opa_json_lex_read_string,opa_ishex +opa_json_lex_read,opa_strncmp +opa_json_lex_read,opa_isspace +opa_json_lex_read,opa_json_lex_read_string +opa_json_lex_read,opa_isdigit +opa_json_lex_read,opa_json_lex_read_number +opa_json_parse_string,opa_malloc +opa_json_parse_string,opa_string_allocated +opa_json_parse_string,opa_unicode_decode_unit +opa_json_parse_string,opa_unicode_surrogate +opa_json_parse_string,opa_abort +opa_json_parse_string,opa_unicode_decode_utf8 +opa_json_parse_string,opa_unicode_encode_utf8 +opa_json_parse_string,opa_unicode_decode_surrogate +opa_json_parse_token,opa_null +opa_json_parse_token,opa_boolean +opa_json_parse_token,opa_malloc +opa_json_parse_token,opa_number_ref_allocated +opa_json_parse_token,opa_json_parse_string +opa_json_parse_token,opa_array +opa_json_parse_token,opa_json_lex_read +opa_json_parse_token,opa_json_parse_token +opa_json_parse_token,opa_array_append +opa_json_parse_token,opa_object +opa_json_parse_token,opa_json_parse_set +opa_json_parse_token,opa_json_parse_object +opa_json_parse_token,opa_set +opa_json_parse_set,opa_set +opa_json_parse_set,opa_set_add +opa_json_parse_set,opa_json_lex_read +opa_json_parse_set,opa_json_parse_token +opa_json_parse_object,opa_json_lex_read +opa_json_parse_object,opa_json_parse_token +opa_json_parse_object,opa_object +opa_json_parse_object,opa_object_insert +opa_json_parse,opa_json_lex_read +opa_json_parse,opa_json_parse_token +opa_value_parse,opa_json_lex_read +opa_value_parse,opa_json_parse_token +opa_json_writer_emit_boolean,opa_malloc +opa_json_writer_emit_integer,opa_itoa +opa_json_writer_emit_integer,opa_strlen +opa_json_writer_emit_integer,opa_malloc +opa_json_writer_emit_number,opa_json_writer_emit_integer +opa_json_writer_emit_number,opa_malloc +opa_json_writer_emit_number,opa_abort +opa_json_writer_emit_string,opa_malloc +opa_json_writer_emit_string,snprintf_ +opa_json_writer_emit_array_element,opa_value_get +opa_json_writer_emit_array_element,opa_json_writer_emit_value +opa_json_writer_emit_value,opa_value_type +opa_json_writer_emit_value,opa_malloc +opa_json_writer_emit_value,opa_json_writer_emit_boolean +opa_json_writer_emit_value,opa_json_writer_emit_string +opa_json_writer_emit_value,opa_json_writer_emit_number +opa_json_writer_emit_value,opa_json_writer_emit_collection +opa_json_writer_emit_value,opa_json_writer_emit_set_literal +opa_json_writer_emit_collection,opa_malloc +opa_json_writer_emit_collection,opa_value_iter +opa_json_writer_emit_set_element,opa_json_writer_emit_value +opa_json_writer_emit_set_literal,opa_value_length +opa_json_writer_emit_set_literal,opa_malloc +opa_json_writer_emit_set_literal,opa_json_writer_emit_collection +opa_json_writer_emit_object_element,opa_value_type +opa_json_writer_emit_object_element,opa_json_writer_emit_value +opa_json_writer_emit_object_element,opa_json_writer_write +opa_json_writer_emit_object_element,opa_string_terminated +opa_json_writer_emit_object_element,opa_value_free +opa_json_writer_emit_object_element,opa_free +opa_json_writer_emit_object_element,opa_malloc +opa_json_writer_emit_object_element,opa_value_get +opa_json_writer_write,opa_malloc +opa_json_writer_write,opa_json_writer_emit_value +opa_json_writer_write,opa_free +opa_json_dump,opa_json_writer_write +opa_value_dump,opa_json_writer_write +move_freelists,opa_abort +opa_heap_blocks_stash,move_freelists +opa_heap_blocks_restore,move_freelists +opa_malloc,opa_free_bulk_commit +opa_malloc,opa_abort +opa_free_bulk_commit,merge_sort_blocks +opa_realloc,opa_malloc +opa_realloc,memcpy +opa_realloc,opa_free +opa_builtin_cache_get,opa_abort +opa_builtin_cache_set,opa_abort +merge_sort_blocks,merge_sort_blocks +opa_memoize_init,opa_malloc +opa_memoize_init,opa_object +opa_memoize_push,opa_malloc +opa_memoize_push,opa_object +opa_memoize_insert,opa_number_int +opa_memoize_insert,opa_object_insert +opa_memoize_get,opa_number_init_int +opa_memoize_get,opa_object_get +opa_mpd_init,mpd_defaultcontext +opa_mpd_init,mpd_maxcontext +opa_mpd_init,mpd_qnew +opa_mpd_init,mpd_qset_i32 +opa_mpd_init,opa_abort +opa_mpd_del,mpd_del +opa_number_to_bf,opa_value_type +opa_number_to_bf,mpd_qnew +opa_number_to_bf,malloc +opa_number_to_bf,memcpy +opa_number_to_bf,mpd_qset_string +opa_number_to_bf,opa_abort +opa_number_to_bf,free +opa_number_to_bf,mpd_qset_i32 +opa_number_to_bf,snprintf_ +opa_bf_to_number,mpd_qget_i32 +opa_bf_to_number,mpd_del +opa_bf_to_number,opa_number_int +opa_bf_to_number,mpd_to_sci +opa_bf_to_number,opa_strlen +opa_bf_to_number,opa_number_ref +opa_bf_to_number_no_free,mpd_qget_i32 +opa_bf_to_number_no_free,opa_number_int +opa_bf_to_number_no_free,mpd_to_sci +opa_bf_to_number_no_free,opa_strlen +opa_bf_to_number_no_free,opa_number_ref +opa_bf_to_bf_bits,mpd_qnew +opa_bf_to_bf_bits,mpd_qround_to_intx +opa_bf_to_bf_bits,mpd_del +opa_bf_to_bf_bits,mpd_qcmp +opa_bf_to_bf_bits,opa_abort +opa_bf_to_bf_bits,mpd_sign +opa_bf_to_bf_bits,mpd_qabs +opa_bf_to_bf_bits,mpd_sizeinbase +opa_bf_to_bf_bits,malloc +opa_bf_to_bf_bits,mpd_qexport_u16 +opa_bf_to_bf_bits,mpd_qimport_u16 +opa_bf_to_bf_bits,free +opa_bf_bits_to_bf,mpd_sign +opa_bf_bits_to_bf,mpd_qnew +opa_bf_bits_to_bf,mpd_qabs +opa_bf_bits_to_bf,opa_abort +opa_bf_bits_to_bf,mpd_del +opa_bf_bits_to_bf,mpd_sizeinbase +opa_bf_bits_to_bf,malloc +opa_bf_bits_to_bf,mpd_qexport_u16 +opa_bf_bits_to_bf,mpd_qimport_u16 +opa_bf_bits_to_bf,free +qabs,mpd_qnew +qabs,mpd_qabs +qabs,opa_abort +qabs,mpd_del +qadd_one,mpd_qnew +qadd_one,mpd_qadd +qadd_one,opa_abort +qadd_one,mpd_del +qadd,mpd_qnew +qadd,mpd_qadd +qadd,opa_abort +qadd,mpd_del +qsub_one,mpd_qnew +qsub_one,mpd_qsub +qsub_one,opa_abort +qsub_one,mpd_del +qmul,mpd_qnew +qmul,mpd_qmul +qmul,opa_abort +qmul,mpd_del +qand,opa_bf_to_bf_bits +qand,mpd_del +qand,mpd_qnew +qand,mpd_qand +qand,opa_abort +qand,opa_bf_bits_to_bf +qand_not,opa_bf_to_bf_bits +qand_not,mpd_del +qand_not,mpd_sizeinbase +qand_not,malloc +qand_not,mpd_qexport_u16 +qand_not,opa_abort +qand_not,mpd_qnew +qand_not,mpd_qimport_u16 +qand_not,free +qand_not,mpd_qxor +qand_not,mpd_qand +qand_not,opa_bf_bits_to_bf +qor,opa_bf_to_bf_bits +qor,mpd_del +qor,mpd_qnew +qor,mpd_qor +qor,opa_abort +qor,opa_bf_bits_to_bf +qxor,opa_bf_to_bf_bits +qxor,mpd_del +qxor,mpd_qnew +qxor,mpd_qxor +qxor,opa_abort +qxor,opa_bf_bits_to_bf +qneg,opa_bf_to_bf_bits +qneg,mpd_qnew +qneg,mpd_qminus +qneg,mpd_del +qneg,opa_abort +qneg,opa_bf_bits_to_bf +opa_numbers_range,opa_number_to_bf +opa_numbers_range,mpd_isinteger +opa_numbers_range,mpd_qcmp +opa_numbers_range,opa_abort +opa_numbers_range,opa_array +opa_numbers_range,opa_bf_to_number_no_free +opa_numbers_range,opa_array_append +opa_numbers_range,qsub_one +opa_numbers_range,qadd_one +opa_numbers_range,opa_mpd_del +__paths_to_object,opa_object +__paths_to_object,opa_value_get +__paths_to_object,opa_object_insert +__paths_to_object,opa_value_type +__paths_to_object,opa_null +__parse_path,opa_array +__parse_path,opa_value_type +__parse_path,opa_array_append +__parse_path,opa_string_terminated +__parse_path,opa_strings_trim_left +__parse_path,opa_strings_split +__parse_path,opa_strings_replace +__get_json_paths,opa_array +__get_json_paths,opa_value_iter +__get_json_paths,opa_value_type +__get_json_paths,opa_value_get +__get_json_paths,__parse_path +__get_json_paths,opa_array_append +__json_remove,opa_value_type +__json_remove,opa_object +__json_remove,opa_value_iter +__json_remove,opa_value_get +__json_remove,__json_remove +__json_remove,opa_object_insert +__json_remove,opa_set +__json_remove,opa_set_add +__json_remove,opa_array +__json_remove,opa_number_int +__json_remove,opa_strings_format_int +__json_remove,opa_array_append +__json_filter,opa_null +__json_filter,opa_value_compare +__json_filter,opa_value_type +__json_filter,opa_object +__json_filter,opa_value_iter +__json_filter,opa_value_get +__json_filter,__json_filter +__json_filter,opa_object_insert +__json_filter,opa_set +__json_filter,opa_set_add +__json_filter,opa_array +__json_filter,opa_number_int +__json_filter,opa_strings_format_int +__json_filter,opa_array_append +builtin_object_filter,opa_value_type +builtin_object_filter,opa_object +builtin_object_filter,opa_value_iter +builtin_object_filter,opa_value_get +builtin_object_filter,opa_object_get +builtin_object_filter,opa_object_insert +builtin_object_get,opa_value_type +builtin_object_get,opa_value_get +builtin_object_keys,opa_value_type +builtin_object_keys,opa_set_with_cap +builtin_object_keys,opa_set_add +builtin_object_remove,opa_value_type +builtin_object_remove,opa_set +builtin_object_remove,opa_value_iter +builtin_object_remove,opa_value_get +builtin_object_remove,opa_set_add +builtin_object_remove,opa_object +builtin_object_remove,opa_set_get +builtin_object_remove,opa_object_get +builtin_object_remove,opa_object_insert +builtin_object_union,opa_value_type +builtin_object_union,__merge +__merge,opa_object +__merge,opa_value_iter +__merge,opa_object_get +__merge,opa_value_type +__merge,__merge +__merge,opa_object_insert +__merge,opa_value_get +builtin_object_union_n,opa_value_iter +builtin_object_union_n,opa_value_get +builtin_object_union_n,opa_value_type +builtin_object_union_n,__merge +builtin_json_remove,opa_value_type +builtin_json_remove,__get_json_paths +builtin_json_remove,__paths_to_object +builtin_json_remove,__json_remove +builtin_json_filter,opa_value_type +builtin_json_filter,__get_json_paths +builtin_json_filter,__paths_to_object +builtin_json_filter,__json_filter +opa_set_diff,opa_value_type +opa_set_diff,opa_set +opa_set_diff,opa_set_get +opa_set_diff,opa_set_add +opa_set_intersection,opa_value_type +opa_set_intersection,opa_set_with_cap +opa_set_intersection,opa_set_get +opa_set_intersection,opa_set_add +opa_sets_intersection,opa_value_type +opa_sets_intersection,opa_set +opa_sets_intersection,opa_set_union +opa_sets_intersection,opa_set_intersection +opa_sets_intersection,opa_value_free_shallow +opa_set_union,opa_value_type +opa_set_union,opa_set +opa_set_union,opa_set_add +opa_sets_union,opa_value_type +opa_sets_union,opa_set +opa_sets_union,opa_set_add +opa_sets_union,opa_value_free_shallow +opa_strings_any_prefix_match,opa_value_type +opa_strings_any_prefix_match,opa_value_iter +opa_strings_any_prefix_match,opa_value_get +opa_strings_any_prefix_match,opa_strings_any_prefix_match +opa_strings_any_prefix_match,opa_value_free +opa_strings_any_prefix_match,opa_strncmp +opa_strings_any_prefix_match,opa_boolean +opa_strings_any_suffix_match,opa_value_type +opa_strings_any_suffix_match,opa_value_iter +opa_strings_any_suffix_match,opa_value_get +opa_strings_any_suffix_match,opa_strings_any_suffix_match +opa_strings_any_suffix_match,opa_value_free +opa_strings_any_suffix_match,opa_boolean +opa_strings_concat,opa_value_type +opa_strings_concat,opa_malloc +opa_strings_concat,memcpy +opa_strings_concat,opa_string_allocated +opa_strings_contains,opa_value_type +opa_strings_contains,opa_strncmp +opa_strings_contains,opa_boolean +opa_strings_endswith,opa_value_type +opa_strings_endswith,opa_boolean +opa_strings_format_int,opa_value_type +opa_strings_format_int,opa_number_try_int +opa_strings_format_int,opa_number_to_bf +opa_strings_format_int,mpd_qnew +opa_strings_format_int,mpd_max_ctx +opa_strings_format_int,mpd_qtrunc +opa_strings_format_int,opa_abort +opa_strings_format_int,mpd_qget_i32 +opa_strings_format_int,opa_malloc +opa_strings_format_int,snprintf_ +opa_strings_format_int,opa_strlen +opa_strings_format_int,opa_string_allocated +opa_strings_indexof,opa_value_type +opa_strings_indexof,opa_strncmp +opa_strings_indexof,opa_number_int +opa_strings_indexof,opa_unicode_decode_utf8 +opa_strings_indexof,opa_abort +opa_strings_replace,opa_value_type +opa_strings_replace,opa_malloc +opa_strings_replace,opa_strncmp +opa_strings_replace,opa_realloc +opa_strings_replace,memcpy +opa_strings_replace,opa_string_allocated +opa_strings_replace_n,opa_value_type +opa_strings_replace_n,opa_malloc +opa_strings_replace_n,memcpy +opa_strings_replace_n,opa_string_allocated +opa_strings_replace_n,opa_strings_replace +opa_strings_replace_n,opa_value_free +opa_strings_reverse,opa_value_type +opa_strings_reverse,opa_malloc +opa_strings_reverse,opa_unicode_decode_utf8 +opa_strings_reverse,opa_abort +opa_strings_reverse,memcpy +opa_strings_reverse,opa_string_allocated +opa_strings_split,opa_value_type +opa_strings_split,opa_array +opa_strings_split,opa_strncmp +opa_strings_split,opa_malloc +opa_strings_split,memcpy +opa_strings_split,opa_string_allocated +opa_strings_split,opa_array_append +opa_strings_split,opa_unicode_decode_utf8 +opa_strings_split,opa_abort +opa_strings_startswith,opa_value_type +opa_strings_startswith,opa_strncmp +opa_strings_startswith,opa_boolean +opa_strings_substring,opa_value_type +opa_strings_substring,opa_number_try_int +opa_strings_substring,opa_string_terminated +opa_strings_substring,opa_unicode_decode_utf8 +opa_strings_substring,opa_abort +opa_strings_substring,opa_malloc +opa_strings_substring,memcpy +opa_strings_substring,opa_string_allocated +opa_strings_trim,opa_value_type +opa_strings_trim,opa_strings_trim_left +opa_strings_trim,opa_strings_trim_right +opa_strings_trim,opa_value_free +opa_strings_trim_left,opa_value_type +opa_strings_trim_left,opa_unicode_decode_utf8 +opa_strings_trim_left,opa_abort +opa_strings_trim_left,opa_strncmp +opa_strings_trim_left,opa_malloc +opa_strings_trim_left,memcpy +opa_strings_trim_left,opa_string_allocated +opa_strings_trim_right,opa_value_type +opa_strings_trim_right,opa_unicode_last_utf8 +opa_strings_trim_right,opa_abort +opa_strings_trim_right,opa_unicode_decode_utf8 +opa_strings_trim_right,opa_strncmp +opa_strings_trim_right,opa_malloc +opa_strings_trim_right,memcpy +opa_strings_trim_right,opa_string_allocated +opa_strings_trim_prefix,opa_value_type +opa_strings_trim_prefix,opa_strncmp +opa_strings_trim_prefix,opa_malloc +opa_strings_trim_prefix,memcpy +opa_strings_trim_prefix,opa_string_allocated +opa_strings_trim_suffix,opa_value_type +opa_strings_trim_suffix,opa_strncmp +opa_strings_trim_suffix,opa_malloc +opa_strings_trim_suffix,memcpy +opa_strings_trim_suffix,opa_string_allocated +opa_strings_trim_space,opa_value_type +opa_strings_trim_space,trim_space +opa_strings_trim_space,opa_malloc +opa_strings_trim_space,memcpy +opa_strings_trim_space,opa_string_allocated +trim_space,opa_unicode_decode_utf8 +trim_space,opa_abort +trim_space,opa_unicode_is_space +trim_space,opa_unicode_last_utf8 +trim_space,opa_malloc +trim_space,memcpy +trim_space,opa_string_allocated +opa_strings_lower,opa_value_type +opa_strings_lower,opa_malloc +opa_strings_lower,opa_string_allocated +opa_strings_lower,malloc +opa_strings_lower,opa_unicode_decode_utf8 +opa_strings_lower,opa_abort +opa_strings_lower,opa_unicode_to_lower +opa_strings_lower,opa_realloc +opa_strings_lower,opa_unicode_encode_utf8 +opa_strings_upper,opa_value_type +opa_strings_upper,opa_malloc +opa_strings_upper,opa_string_allocated +opa_strings_upper,malloc +opa_strings_upper,opa_unicode_decode_utf8 +opa_strings_upper,opa_abort +opa_strings_upper,opa_unicode_to_upper +opa_strings_upper,opa_realloc +opa_strings_upper,opa_unicode_encode_utf8 +opa_types_is_number,opa_value_type +opa_types_is_number,opa_boolean +opa_types_is_string,opa_value_type +opa_types_is_string,opa_boolean +opa_types_is_boolean,opa_value_type +opa_types_is_boolean,opa_boolean +opa_types_is_array,opa_value_type +opa_types_is_array,opa_boolean +opa_types_is_set,opa_value_type +opa_types_is_set,opa_boolean +opa_types_is_object,opa_value_type +opa_types_is_object,opa_boolean +opa_types_is_null,opa_value_type +opa_types_is_null,opa_boolean +opa_types_name,opa_value_type +opa_types_name,opa_string +opa_value_hash,opa_value_hash +opa_value_hash,opa_number_hash +opa_value_compare,opa_value_compare_number +opa_value_compare,opa_value_compare_string +opa_value_compare,opa_value_compare_array +opa_value_compare,opa_value_compare_object +opa_value_compare,opa_value_compare_set +opa_value_compare,opa_abort +opa_object_get,opa_value_hash +opa_object_get,opa_value_compare +opa_set_get,opa_value_hash +opa_set_get,opa_value_compare +opa_number_try_int,opa_atoi64 +opa_number_try_int,opa_abort +opa_value_get,opa_atoi64 +opa_value_get,opa_value_hash +opa_value_get,opa_value_compare +opa_value_get,opa_abort +opa_value_compare_number,opa_abort +opa_value_compare_number,opa_atoi64 +opa_value_compare_number,opa_number_to_bf +opa_value_compare_number,mpd_qcmp +opa_value_compare_number,mpd_del +opa_value_compare_string,opa_strncmp +opa_value_compare_array,opa_value_compare +opa_value_compare_object,opa_object_keys +opa_value_compare_object,opa_value_compare +opa_value_compare_object,opa_value_hash +opa_value_compare_object,opa_value_compare_number +opa_value_compare_object,opa_strncmp +opa_value_compare_object,opa_value_compare_object +opa_value_compare_object,opa_value_compare_set +opa_value_compare_object,opa_abort +opa_value_compare_object,opa_free +opa_value_compare_set,opa_malloc +opa_value_compare_set,opa_free +opa_value_compare_set,opa_value_compare +opa_value_compare_set,opa_value_compare_number +opa_value_compare_set,opa_strncmp +opa_value_compare_set,opa_value_compare_object +opa_value_compare_set,opa_value_compare_set +opa_value_compare_set,opa_abort +opa_number_hash,opa_atof64 +opa_number_hash,opa_abort +opa_value_iter,opa_atoi64 +opa_value_iter,opa_value_hash +opa_value_iter,opa_value_compare +opa_value_iter,opa_abort +opa_object_keys,opa_malloc +opa_object_keys,opa_free +opa_object_keys,opa_value_compare +opa_object_keys,opa_value_compare_number +opa_object_keys,opa_strncmp +opa_object_keys,opa_value_compare_object +opa_object_keys,opa_abort +opa_object_keys,opa_value_compare_set +opa_array_free,__opa_value_free +opa_array_free,opa_free +opa_array_free,opa_free_bulk +__opa_value_free,opa_free_bulk +__opa_value_free,opa_free +__opa_value_free,opa_array_free +__opa_value_free,__opa_object_buckets_free +__opa_value_free,__opa_set_buckets_free +__opa_object_buckets_free,opa_free +__opa_object_buckets_free,opa_array_free +__opa_object_buckets_free,__opa_object_buckets_free +__opa_object_buckets_free,__opa_set_buckets_free +__opa_object_buckets_free,__opa_value_free +__opa_object_buckets_free,opa_free_bulk +__opa_set_buckets_free,opa_free +__opa_set_buckets_free,opa_array_free +__opa_set_buckets_free,__opa_object_buckets_free +__opa_set_buckets_free,__opa_set_buckets_free +__opa_set_buckets_free,__opa_value_free +__opa_set_buckets_free,opa_free_bulk +opa_value_free,__opa_value_free +opa_value_free_shallow,__opa_value_free +opa_value_merge,opa_malloc +opa_value_merge,opa_value_get +opa_value_merge,__opa_object_insert +opa_value_merge,opa_value_merge +opa_value_merge,opa_atoi64 +opa_value_merge,opa_value_hash +opa_value_merge,opa_value_compare_number +opa_value_merge,opa_strncmp +opa_value_merge,opa_value_compare +opa_value_merge,opa_value_compare_object +opa_value_merge,opa_value_compare_set +opa_value_merge,opa_abort +__opa_object_insert,opa_value_hash +__opa_object_insert,opa_value_compare +__opa_object_insert,__opa_value_free +__opa_object_insert,__opa_object_grow +__opa_object_insert,opa_malloc +opa_object_insert,__opa_object_insert +opa_boolean,opa_malloc +opa_number_ref,opa_malloc +opa_number_int,opa_malloc +opa_string,opa_malloc +opa_value_shallow_copy_object,opa_malloc +opa_value_shallow_copy_object,opa_value_iter +opa_value_shallow_copy_object,opa_value_get +opa_value_shallow_copy_object,__opa_object_insert +opa_value_shallow_copy_set,opa_malloc +opa_value_shallow_copy_set,opa_value_iter +opa_value_shallow_copy_set,opa_set_add +opa_set_add,opa_value_hash +opa_set_add,opa_value_compare +opa_set_add,__opa_set_grow +opa_set_add,opa_malloc +__opa_set_grow,opa_malloc +__opa_set_grow,opa_value_hash +__opa_set_grow,opa_value_compare_number +__opa_set_grow,opa_strncmp +__opa_set_grow,opa_value_compare +__opa_set_grow,opa_value_compare_object +__opa_set_grow,opa_value_compare_set +__opa_set_grow,opa_abort +__opa_set_grow,opa_free +opa_value_shallow_copy,opa_malloc +opa_value_shallow_copy,opa_value_shallow_copy_object +opa_value_shallow_copy,opa_value_shallow_copy_set +opa_value_shallow_copy,opa_abort +opa_value_transitive_closure,opa_malloc +opa_value_transitive_closure,__opa_value_transitive_closure +__opa_value_transitive_closure,opa_malloc +__opa_value_transitive_closure,opa_free +__opa_value_transitive_closure,opa_array_append +__opa_value_transitive_closure,opa_value_iter +__opa_value_transitive_closure,opa_value_get +__opa_value_transitive_closure,__opa_value_transitive_closure +opa_array_append,opa_malloc +opa_array_append,opa_free +opa_null,opa_malloc +opa_number_size,opa_malloc +opa_number_ref_allocated,opa_malloc +opa_string_terminated,opa_malloc +opa_string_terminated,opa_strlen +opa_string_allocated,opa_malloc +opa_array,opa_malloc +opa_array_with_cap,opa_malloc +opa_array_with_cap,opa_free +opa_object,opa_malloc +opa_set,opa_malloc +opa_set_with_cap,opa_malloc +__opa_object_grow,opa_malloc +__opa_object_grow,opa_value_hash +__opa_object_grow,opa_value_compare_number +__opa_object_grow,opa_strncmp +__opa_object_grow,opa_value_compare +__opa_object_grow,opa_value_compare_object +__opa_object_grow,opa_value_compare_set +__opa_object_grow,opa_abort +__opa_object_grow,opa_free +opa_object_remove,opa_value_hash +opa_object_remove,opa_value_compare +opa_object_remove,__opa_value_free +opa_object_remove,opa_free_bulk +opa_object_remove,opa_free +opa_string_copy,opa_malloc +opa_value_add_path,opa_value_get +opa_value_add_path,opa_malloc +opa_value_add_path,__opa_object_insert +opa_value_add_path,__opa_value_free +opa_value_remove_path,opa_value_get +opa_value_remove_path,opa_object_remove +opa_lookup,opa_value_get +opa_lookup,opa_value_iter +opa_lookup,opa_abort +opa_lookup,opa_atoi64 +opa_mapping_init,opa_json_parse +opa_mapping_lookup,opa_lookup +node::re2\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,std::__1::basic_string\2c\20std::__1::allocator\20>::assign\28char\20const*\29 +node::re2\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,node::re2\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +node::re2\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\2c\20unsigned\20long\29 +node::re2\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,operator\20delete\28void*\29 +node::re2\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\29 +node::re2\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,escape\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +node::re2\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,operator\20new\28unsigned\20long\29 +node::re2\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,memcpy +node::re2\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,abort +escape\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,std::__1::basic_string\2c\20std::__1::allocator\20>::push_back\28char\29 +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,operator\20new\28unsigned\20long\29 +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,lexer::lexer\28char\20const*\2c\20unsigned\20long\29 +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,glob_parse\28lexer*\2c\20node**\29 +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::compare\28unsigned\20long\2c\20unsigned\20long\2c\20char\20const*\2c\20unsigned\20long\29\20const +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,lexer::~lexer\28\29 +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,operator\20delete\28void*\29 +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::assign\28char\20const*\29 +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,opa_unicode_decode_utf8 +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,escape\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\2c\20unsigned\20long\29 +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\29 +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,node::re2\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,node::~node\28\29 +lexer::~lexer\28\29,operator\20delete\28void*\29 +lexer::next\28token*\29,strlen +lexer::next\28token*\29,operator\20new\28unsigned\20long\29 +lexer::next\28token*\29,memcpy +lexer::next\28token*\29,operator\20delete\28void*\29 +lexer::next\28token*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::operator=\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +lexer::next\28token*\29,abort +lexer::next\28token*\29,lexer::fetch_item\28\29 +lexer::fetch_item\28\29,opa_unicode_decode_utf8 +lexer::fetch_item\28\29,operator\20new\28unsigned\20long\29 +lexer::fetch_item\28\29,memcpy +lexer::fetch_item\28\29,operator\20delete\28void*\29 +lexer::fetch_item\28\29,lexer::fetch_range\28\29 +lexer::fetch_item\28\29,lexer::fetch_text\28int\20const*\29 +lexer::fetch_item\28\29,abort +lexer::fetch_range\28\29,opa_unicode_decode_utf8 +lexer::fetch_range\28\29,operator\20new\28unsigned\20long\29 +lexer::fetch_range\28\29,memcpy +lexer::fetch_range\28\29,operator\20delete\28void*\29 +lexer::fetch_range\28\29,lexer::fetch_text\28int\20const*\29 +lexer::fetch_range\28\29,abort +lexer::fetch_text\28int\20const*\29,opa_unicode_decode_utf8 +lexer::fetch_text\28int\20const*\29,operator\20new\28unsigned\20long\29 +lexer::fetch_text\28int\20const*\29,memcpy +lexer::fetch_text\28int\20const*\29,operator\20delete\28void*\29 +lexer::fetch_text\28int\20const*\29,opa_malloc +lexer::fetch_text\28int\20const*\29,opa_free +lexer::fetch_text\28int\20const*\29,abort +node::~node\28\29,node::~node\28\29 +node::~node\28\29,operator\20delete\28void*\29 +node::insert\28node*\29,operator\20new\28unsigned\20long\29 +node::insert\28node*\29,memcpy +node::insert\28node*\29,operator\20delete\28void*\29 +node::insert\28node*\29,abort +glob_parse\28lexer*\2c\20node**\29,operator\20new\28unsigned\20long\29 +glob_parse\28lexer*\2c\20node**\29,std::__1::basic_string\2c\20std::__1::allocator\20>::compare\28unsigned\20long\2c\20unsigned\20long\2c\20char\20const*\2c\20unsigned\20long\29\20const +glob_parse\28lexer*\2c\20node**\29,node::~node\28\29 +glob_parse\28lexer*\2c\20node**\29,operator\20delete\28void*\29 +glob_parse\28lexer*\2c\20node**\29,std::__1::basic_string\2c\20std::__1::allocator\20>::basic_string\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +parser_main\28state*\2c\20lexer*\29,lexer::next\28token*\29 +parser_main\28state*\2c\20lexer*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::operator=\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +parser_main\28state*\2c\20lexer*\29,operator\20new\28unsigned\20long\29 +parser_main\28state*\2c\20lexer*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::basic_string\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +parser_main\28state*\2c\20lexer*\29,node::insert\28node*\29 +parser_main\28state*\2c\20lexer*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::assign\28char\20const*\29 +parser_main\28state*\2c\20lexer*\29,operator\20delete\28void*\29 +parser_range\28state*\2c\20lexer*\29,lexer::next\28token*\29 +parser_range\28state*\2c\20lexer*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::assign\28char\20const*\29 +parser_range\28state*\2c\20lexer*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::operator=\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +parser_range\28state*\2c\20lexer*\29,opa_unicode_decode_utf8 +parser_range\28state*\2c\20lexer*\29,memcmp +parser_range\28state*\2c\20lexer*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::compare\28unsigned\20long\2c\20unsigned\20long\2c\20char\20const*\2c\20unsigned\20long\29\20const +parser_range\28state*\2c\20lexer*\29,operator\20new\28unsigned\20long\29 +parser_range\28state*\2c\20lexer*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::basic_string\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +parser_range\28state*\2c\20lexer*\29,node::insert\28node*\29 +parser_range\28state*\2c\20lexer*\29,operator\20delete\28void*\29 +opa_glob_match,opa_value_type +opa_glob_match,opa_value_iter +opa_glob_match,opa_value_get +opa_glob_match,operator\20new\28unsigned\20long\29 +opa_glob_match,memcpy +opa_glob_match,operator\20delete\28void*\29 +opa_glob_match,void\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>::__push_back_slow_path\2c\20std::__1::allocator\20>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>&&\29 +opa_glob_match,opa_builtin_cache_get +opa_glob_match,opa_builtin_cache_set +opa_glob_match,std::__1::basic_string\2c\20std::__1::allocator\20>::basic_string\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +opa_glob_match,std::__1::__hash_iterator\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::find\28cache_key\20const&\29 +opa_glob_match,std::__1::basic_string\2c\20std::__1::allocator\20>::operator=\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +opa_glob_match,glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29 +opa_glob_match,std::__1::unordered_map\2c\20std::__1::allocator\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::erase\28std::__1::__hash_map_iterator\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\20>\29 +opa_glob_match,std::__1::pair\2c\20std::__1::allocator\20>\20>::pair\2c\20std::__1::allocator\20>&\2c\20false>\28cache_key&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>&\29 +opa_glob_match,std::__1::pair\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\20>\20>\28cache_key\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\20>&&\29 +opa_glob_match,opa_string +opa_glob_match,opa_regex_match +opa_glob_match,abort +void\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>::__push_back_slow_path\2c\20std::__1::allocator\20>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>&&\29,operator\20new\28unsigned\20long\29 +void\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>::__push_back_slow_path\2c\20std::__1::allocator\20>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>&&\29,operator\20delete\28void*\29 +void\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>::__push_back_slow_path\2c\20std::__1::allocator\20>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>&&\29,abort +std::__1::__hash_iterator\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::find\28cache_key\20const&\29,std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>::operator\28\29\28cache_key\20const&\29\20const +std::__1::__hash_iterator\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::find\28cache_key\20const&\29,std::__1::equal_to::operator\28\29\28cache_key\20const&\2c\20cache_key\20const&\29\20const +std::__1::unordered_map\2c\20std::__1::allocator\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::erase\28std::__1::__hash_map_iterator\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\20>\29,std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::remove\28std::__1::__hash_const_iterator\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\29 +std::__1::unordered_map\2c\20std::__1::allocator\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::erase\28std::__1::__hash_map_iterator\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\20>\29,std::__1::unique_ptr\2c\20std::__1::allocator\20>\20>\2c\20void*>\2c\20std::__1::__hash_node_destructor\2c\20std::__1::allocator\20>\20>\2c\20void*>\20>\20>\20>::~unique_ptr\28\29 +std::__1::pair\2c\20std::__1::allocator\20>\20>::pair\2c\20std::__1::allocator\20>&\2c\20false>\28cache_key&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>&\29,std::__1::basic_string\2c\20std::__1::allocator\20>::basic_string\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +std::__1::pair\2c\20std::__1::allocator\20>\20>::pair\2c\20std::__1::allocator\20>&\2c\20false>\28cache_key&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>&\29,operator\20new\28unsigned\20long\29 +std::__1::pair\2c\20std::__1::allocator\20>\20>::pair\2c\20std::__1::allocator\20>&\2c\20false>\28cache_key&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>&\29,abort +std::__1::pair\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\20>\20>\28cache_key\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\20>&&\29,std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>::operator\28\29\28cache_key\20const&\29\20const +std::__1::pair\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\20>\20>\28cache_key\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\20>&&\29,std::__1::equal_to::operator\28\29\28cache_key\20const&\2c\20cache_key\20const&\29\20const +std::__1::pair\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\20>\20>\28cache_key\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\20>&&\29,operator\20new\28unsigned\20long\29 +std::__1::pair\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\20>\20>\28cache_key\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\20>&&\29,std::__1::__next_prime\28unsigned\20long\29 +std::__1::pair\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\20>\20>\28cache_key\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\20>&&\29,std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__rehash\28unsigned\20long\29 +std::__1::pair\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\20>\20>\28cache_key\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\20>&&\29,std::__1::unique_ptr\2c\20std::__1::allocator\20>\20>\2c\20void*>\2c\20std::__1::__hash_node_destructor\2c\20std::__1::allocator\20>\20>\2c\20void*>\20>\20>\20>::~unique_ptr\28\29 +std::__1::unique_ptr\2c\20std::__1::allocator\20>\20>\2c\20void*>\2c\20std::__1::__hash_node_destructor\2c\20std::__1::allocator\20>\20>\2c\20void*>\20>\20>\20>::~unique_ptr\28\29,operator\20delete\28void*\29 +std::__1::equal_to::operator\28\29\28cache_key\20const&\2c\20cache_key\20const&\29\20const,memcmp +std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__rehash\28unsigned\20long\29,operator\20new\28unsigned\20long\29 +std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__rehash\28unsigned\20long\29,operator\20delete\28void*\29 +std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__rehash\28unsigned\20long\29,memcmp +std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__rehash\28unsigned\20long\29,abort +opa_regex_is_valid,opa_value_type +opa_regex_is_valid,opa_boolean +opa_regex_is_valid,operator\20new\28unsigned\20long\29 +opa_regex_is_valid,memcpy +opa_regex_is_valid,re2::RE2::RE2\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29 +opa_regex_is_valid,re2::RE2::~RE2\28\29 +opa_regex_is_valid,operator\20delete\28void*\29 +opa_regex_is_valid,abort +opa_regex_match,opa_value_type +opa_regex_match,operator\20new\28unsigned\20long\29 +opa_regex_match,memcpy +opa_regex_match,compile\28char\20const*\29 +opa_regex_match,re2::RE2::PartialMatchN\28re2::StringPiece\20const&\2c\20re2::RE2\20const&\2c\20re2::RE2::Arg\20const*\20const*\2c\20int\29 +opa_regex_match,reuse\28re2::RE2*\29 +opa_regex_match,opa_boolean +opa_regex_match,operator\20delete\28void*\29 +opa_regex_match,abort +compile\28char\20const*\29,opa_builtin_cache_get +compile\28char\20const*\29,operator\20new\28unsigned\20long\29 +compile\28char\20const*\29,opa_builtin_cache_set +compile\28char\20const*\29,strlen +compile\28char\20const*\29,memcpy +compile\28char\20const*\29,std::__1::__hash_iterator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20void*>*>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::find\2c\20std::__1::allocator\20>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +compile\28char\20const*\29,operator\20delete\28void*\29 +compile\28char\20const*\29,re2::RE2::RE2\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29 +compile\28char\20const*\29,re2::RE2::~RE2\28\29 +compile\28char\20const*\29,abort +reuse\28re2::RE2*\29,opa_builtin_cache_get +reuse\28re2::RE2*\29,operator\20new\28unsigned\20long\29 +reuse\28re2::RE2*\29,opa_builtin_cache_set +reuse\28re2::RE2*\29,re2::RE2::~RE2\28\29 +reuse\28re2::RE2*\29,operator\20delete\28void*\29 +reuse\28re2::RE2*\29,std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::remove\28std::__1::__hash_const_iterator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20void*>*>\29 +reuse\28re2::RE2*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::basic_string\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 +reuse\28re2::RE2*\29,std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\2c\20std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>&&\29 +std::__1::__hash_iterator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20void*>*>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::find\2c\20std::__1::allocator\20>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,memcmp +std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\2c\20std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>&&\29,memcmp +std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\2c\20std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>&&\29,operator\20new\28unsigned\20long\29 +std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\2c\20std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>&&\29,std::__1::__next_prime\28unsigned\20long\29 +std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\2c\20std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\2c\20re2::RE2*>&&\29,std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::__rehash\28unsigned\20long\29 +opa_regex_find_all_string_submatch,opa_value_type +opa_regex_find_all_string_submatch,opa_number_try_int +opa_regex_find_all_string_submatch,operator\20new\28unsigned\20long\29 +opa_regex_find_all_string_submatch,memcpy +opa_regex_find_all_string_submatch,compile\28char\20const*\29 +opa_regex_find_all_string_submatch,opa_array +opa_regex_find_all_string_submatch,memset +opa_regex_find_all_string_submatch,re2::RE2::Match\28re2::StringPiece\20const&\2c\20unsigned\20long\2c\20unsigned\20long\2c\20re2::RE2::Anchor\2c\20re2::StringPiece*\2c\20int\29\20const +opa_regex_find_all_string_submatch,fullrune +opa_regex_find_all_string_submatch,chartorune +opa_regex_find_all_string_submatch,opa_array_with_cap +opa_regex_find_all_string_submatch,opa_malloc +opa_regex_find_all_string_submatch,opa_string_allocated +opa_regex_find_all_string_submatch,opa_array_append +opa_regex_find_all_string_submatch,reuse\28re2::RE2*\29 +opa_regex_find_all_string_submatch,operator\20delete\28void*\29 +opa_regex_find_all_string_submatch,abort +std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::__rehash\28unsigned\20long\29,operator\20new\28unsigned\20long\29 +std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::__rehash\28unsigned\20long\29,operator\20delete\28void*\29 +std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::__rehash\28unsigned\20long\29,memcmp +std::__1::__hash_table\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\2c\20std::__1::__hash_value_type\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\2c\20std::__1::equal_to\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20re2::RE2*>\20>\20>::__rehash\28unsigned\20long\29,abort +snprintf_,_vsnprintf +_vsnprintf,_ntoa_format +_vsnprintf,_ftoa +_vsnprintf,_etoa +_ntoa_format,memset +_ftoa,_out_rev +_ftoa,_etoa +_ftoa,memset +_etoa,_ftoa +_etoa,memset +fprintf,opa_abort +fwrite,opa_abort +fputc,opa_abort +abort,opa_abort_ +opa_abort,opa_abort_ +malloc,opa_malloc +free,opa_free +calloc,opa_malloc +calloc,memset +realloc,opa_realloc +strtol,isspace +strtol,isalpha +strtol,isupper +memmove,opa_malloc +memmove,opa_free +_mpd_baseadd,opa_abort +_mpd_shortadd,opa_abort +_mpd_baseincr,opa_abort +_mpd_basesub,opa_abort +_mpd_shortmul,opa_abort +_mpd_basemul,opa_abort +_mpd_basemul,memset +_mpd_shortdiv,opa_abort +_mpd_basedivmod,opa_abort +_mpd_basedivmod,mpd_alloc +_mpd_basedivmod,fprintf +_mpd_basedivmod,fwrite +_mpd_basedivmod,fputc +_mpd_basedivmod,abort +_mpd_baseshiftl,opa_abort +_mpd_baseshiftl,mpd_uint_zero +_mpd_baseshiftr,opa_abort +_mpd_shortmul_c,opa_abort +crt3,opa_abort +fnt_dif2,opa_abort +std_fnt,opa_abort +std_fnt,_mpd_init_fnt_params +std_fnt,fnt_dif2 +std_inv_fnt,opa_abort +std_inv_fnt,_mpd_init_fnt_params +std_inv_fnt,fnt_dif2 +four_step_fnt,opa_abort +four_step_fnt,_mpd_init_w3table +four_step_fnt,_mpd_getkernel +four_step_fnt,six_step_fnt +inv_four_step_fnt,opa_abort +inv_four_step_fnt,inv_six_step_fnt +inv_four_step_fnt,_mpd_getkernel +inv_four_step_fnt,_mpd_init_w3table +mpd_qset_string,mpd_set_flags +mpd_qset_string,mpd_set_negative +mpd_qset_string,mpd_setspecial +mpd_qset_string,strtol +mpd_qset_string,mpd_qresize +mpd_qset_string,mpd_seterror +mpd_qset_string,mpd_setdigits +mpd_qset_string,mpd_qfinalize +mpd_to_sci,_mpd_to_string +_mpd_to_string,mpd_isspecial +_mpd_to_string,mpd_isnan +_mpd_to_string,mpd_alloc +_mpd_to_string,mpd_isnegative +_mpd_to_string,mpd_isqnan +_mpd_to_string,mpd_msword +_mpd_to_string,mpd_word_digits +_mpd_to_string,word_to_string +_mpd_to_string,mpd_isinfinite +_mpd_to_string,abort +_mpd_to_string,opa_abort +_mpd_to_string,mpd_iszero +_mpd_to_string,memset +mpd_switch_to_dyn,opa_abort +mpd_switch_to_dyn,mpd_set_qnan +mpd_switch_to_dyn,mpd_set_positive +mpd_switch_to_dyn,memcpy +mpd_switch_to_dyn,mpd_set_dynamic_data +mpd_realloc_dyn,mpd_set_qnan +mpd_realloc_dyn,mpd_set_positive +mpd_msword,opa_abort +mpd_iszero,opa_abort +mpd_uint_zero,memset +mpd_qresize,opa_abort +mpd_qresize,mpd_switch_to_dyn +mpd_qresize,mpd_realloc_dyn +mpd_setdigits,opa_abort +mpd_zerocoeff,opa_abort +mpd_zerocoeff,mpd_realloc +mpd_qmaxcoeff,opa_abort +mpd_qmaxcoeff,mpd_switch_to_dyn +mpd_qmaxcoeff,mpd_realloc_dyn +mpd_isinteger,opa_abort +_mpd_isint,opa_abort +mpd_setspecial,opa_abort +mpd_setspecial,mpd_realloc +mpd_seterror,opa_abort +mpd_seterror,mpd_realloc +mpd_qsset_ssize,mpd_qfinalize +mpd_qfinalize,_mpd_fix_nan +mpd_qfinalize,_mpd_check_exp +mpd_qfinalize,mpd_qshiftr_inplace +mpd_qfinalize,_mpd_baseincr +mpd_qfinalize,opa_abort +_mpd_fix_nan,opa_abort +_mpd_fix_nan,mpd_realloc +_mpd_fix_nan,mpd_switch_to_dyn +_mpd_fix_nan,mpd_realloc_dyn +_mpd_check_exp,opa_abort +_mpd_check_exp,mpd_realloc +_mpd_check_exp,mpd_qmaxcoeff +_mpd_check_exp,mpd_setspecial +_mpd_check_exp,abort +_mpd_check_exp,mpd_qshiftl +_mpd_check_exp,mpd_qshiftr_inplace +_mpd_check_exp,_mpd_apply_round_excess +_mpd_check_exp,mpd_zerocoeff +mpd_qshiftr_inplace,opa_abort +mpd_qshiftr_inplace,_mpd_get_rnd +mpd_qshiftr_inplace,mpd_realloc +mpd_qshiftr_inplace,_mpd_baseshiftr +mpd_qshiftr_inplace,mpd_switch_to_dyn +mpd_qshiftr_inplace,mpd_realloc_dyn +_settriple,opa_abort +_settriple,mpd_realloc +mpd_qset_i32,opa_abort +mpd_qset_i32,mpd_realloc +mpd_qset_i32,mpd_qsset_ssize +_mpd_qget_uint,opa_abort +_mpd_qget_uint,_mpd_isint +_mpd_qget_uint,mpd_qsshiftr +mpd_qsshiftr,opa_abort +mpd_qsshiftr,memcpy +mpd_qsshiftr,_mpd_get_rnd +mpd_qsshiftr,mpd_realloc +mpd_qsshiftr,_mpd_baseshiftr +mpd_qget_i32,_mpd_qget_uint +mpd_qcopy,opa_abort +mpd_qcopy,mpd_switch_to_dyn +mpd_qcopy,mpd_realloc_dyn +mpd_qcopy,memcpy +mpd_qshiftl,opa_abort +mpd_qshiftl,mpd_qcopy +mpd_qshiftl,mpd_switch_to_dyn +mpd_qshiftl,mpd_realloc_dyn +mpd_qshiftl,_mpd_baseshiftl +_mpd_apply_round_excess,_mpd_baseincr +_mpd_apply_round_excess,opa_abort +_mpd_apply_round_excess,mpd_switch_to_dyn +_mpd_apply_round_excess,mpd_realloc_dyn +mpd_qcmp,_mpd_cmp +_mpd_cmp,opa_abort +_mpd_cmp,_mpd_cmp_same_adjexp +_mpd_cmp_same_adjexp,_mpd_basecmp +mpd_qshiftr,opa_abort +mpd_qshiftr,mpd_qcopy +mpd_qshiftr,_mpd_get_rnd +mpd_qshiftr,mpd_realloc +mpd_qshiftr,_mpd_baseshiftr +mpd_qshiftr,mpd_switch_to_dyn +mpd_qshiftr,mpd_realloc_dyn +mpd_qand,opa_abort +mpd_qand,mpd_realloc +mpd_qand,mpd_switch_to_dyn +mpd_qand,mpd_realloc_dyn +mpd_qand,mpd_setdigits +mpd_qand,_mpd_cap +mpd_qand,mpd_seterror +_mpd_cap,opa_abort +_mpd_cap,mpd_switch_to_dyn +_mpd_cap,mpd_realloc_dyn +_mpd_cap,_settriple +mpd_qor,opa_abort +mpd_qor,mpd_realloc +mpd_qor,mpd_switch_to_dyn +mpd_qor,mpd_realloc_dyn +mpd_qor,mpd_setdigits +mpd_qor,_mpd_cap +mpd_qor,mpd_seterror +_mpd_qaddsub,opa_abort +_mpd_qaddsub,mpd_qshiftl +_mpd_qaddsub,mpd_realloc +_mpd_qaddsub,mpd_switch_to_dyn +_mpd_qaddsub,mpd_realloc_dyn +_mpd_qaddsub,_mpd_baseadd +_mpd_qaddsub,_mpd_basesub +mpd_qshiftn,mpd_qcopy +mpd_qshiftn,_mpd_fix_nan +mpd_qshiftn,mpd_qshiftl +mpd_qshiftn,_mpd_cap +mpd_qshiftn,mpd_qshiftr_inplace +mpd_qshiftn,opa_abort +mpd_qshiftn,mpd_realloc +mpd_qxor,opa_abort +mpd_qxor,mpd_realloc +mpd_qxor,mpd_switch_to_dyn +mpd_qxor,mpd_realloc_dyn +mpd_qxor,mpd_setdigits +mpd_qxor,_mpd_cap +mpd_qxor,mpd_seterror +mpd_qabs,mpd_qcopy +mpd_qabs,_mpd_fix_nan +mpd_qabs,mpd_qminus +mpd_qabs,mpd_qplus +mpd_qminus,mpd_qcopy +mpd_qminus,_mpd_fix_nan +mpd_qminus,opa_abort +mpd_qminus,mpd_qfinalize +mpd_qplus,mpd_qcopy +mpd_qplus,_mpd_fix_nan +mpd_qplus,opa_abort +mpd_qplus,mpd_qfinalize +mpd_qadd,mpd_qcopy +mpd_qadd,_mpd_fix_nan +mpd_qadd,opa_abort +mpd_qadd,mpd_realloc +mpd_qadd,_mpd_qaddsub +mpd_qadd,mpd_qfinalize +mpd_qsub,mpd_qcopy +mpd_qsub,_mpd_fix_nan +mpd_qsub,opa_abort +mpd_qsub,mpd_realloc +mpd_qsub,_mpd_qaddsub +mpd_qsub,mpd_qfinalize +mpd_qdiv,_mpd_qdiv +_mpd_qdiv,mpd_qcopy +_mpd_qdiv,_mpd_fix_nan +_mpd_qdiv,opa_abort +_mpd_qdiv,mpd_realloc +_mpd_qdiv,_settriple +_mpd_qdiv,mpd_qshiftl +_mpd_qdiv,mpd_seterror +_mpd_qdiv,mpd_switch_to_dyn +_mpd_qdiv,mpd_realloc_dyn +_mpd_qdiv,_mpd_shortdiv +_mpd_qdiv,_mpd_basedivmod +_mpd_qdiv,_mpd_base_ndivmod +_mpd_qdiv,mpd_setspecial +_mpd_qdiv,mpd_setdigits +_mpd_qdiv,mpd_trail_zeros +_mpd_qdiv,mpd_qshiftr_inplace +_mpd_qdiv,mpd_qfinalize +_mpd_base_ndivmod,mpd_qnew +_mpd_base_ndivmod,mpd_maxcontext +_mpd_base_ndivmod,opa_abort +_mpd_base_ndivmod,mpd_switch_to_dyn +_mpd_base_ndivmod,mpd_realloc_dyn +_mpd_base_ndivmod,_mpd_shortdiv +_mpd_base_ndivmod,_mpd_qmul_exact +_mpd_base_ndivmod,mpd_qshiftr +_mpd_base_ndivmod,_mpd_qmul +_mpd_base_ndivmod,mpd_qfinalize +_mpd_base_ndivmod,mpd_qsub +_mpd_base_ndivmod,mpd_realloc +_mpd_base_ndivmod,_mpd_qround_to_integral +_mpd_base_ndivmod,fprintf +_mpd_base_ndivmod,fwrite +_mpd_base_ndivmod,fputc +_mpd_base_ndivmod,_mpd_cmp +_mpd_base_ndivmod,mpd_qadd +_mpd_base_ndivmod,mpd_qcopy +_mpd_qdivmod,opa_abort +_mpd_qdivmod,mpd_qcopy +_mpd_qdivmod,_settriple +_mpd_qdivmod,mpd_qshiftl +_mpd_qdivmod,mpd_switch_to_dyn +_mpd_qdivmod,mpd_realloc_dyn +_mpd_qdivmod,_mpd_shortdiv +_mpd_qdivmod,_mpd_basedivmod +_mpd_qdivmod,_mpd_base_ndivmod +_mpd_qdivmod,mpd_setdigits +_mpd_qdivmod,mpd_realloc +_mpd_qmul,mpd_qcopy +_mpd_qmul,_mpd_fix_nan +_mpd_qmul,opa_abort +_mpd_qmul,mpd_realloc +_mpd_qmul,_mpd_mul_2_le2 +_mpd_qmul,memset +_mpd_qmul,_mpd_shortmul +_mpd_qmul,_mpd_basemul +_mpd_qmul,mpd_switch_to_dyn +_mpd_qmul,mpd_realloc_dyn +_mpd_qmul,mpd_calloc +_mpd_qmul,_mpd_kmul +_mpd_qmul,_mpd_fntmul +_mpd_qmul,_mpd_kmul_fnt +_mpd_qmul,mpd_seterror +_mpd_kmul,opa_abort +_mpd_kmul,mpd_calloc +_mpd_kmul,_kmul_worksize +_mpd_kmul,_karatsuba_rec +_mpd_kmul,fprintf +_mpd_kmul,fwrite +_mpd_kmul,fputc +_mpd_kmul,abort +_mpd_fntmul,opa_abort +_mpd_fntmul,fprintf +_mpd_fntmul,fwrite +_mpd_fntmul,fputc +_mpd_fntmul,abort +_mpd_fntmul,mpd_calloc +_mpd_fntmul,memcpy +_mpd_fntmul,fnt_autoconvolute +_mpd_fntmul,fnt_convolute +_mpd_fntmul,memset +_mpd_fntmul,crt3 +_mpd_kmul_fnt,opa_abort +_mpd_kmul_fnt,mpd_calloc +_mpd_kmul_fnt,_kmul_worksize +_mpd_kmul_fnt,fprintf +_mpd_kmul_fnt,fwrite +_mpd_kmul_fnt,fputc +_mpd_kmul_fnt,abort +_mpd_kmul_fnt,_karatsuba_rec_fnt +mpd_qmul,_mpd_qmul +mpd_qmul,mpd_qfinalize +_mpd_qround_to_integral,mpd_qcopy +_mpd_qround_to_integral,_mpd_fix_nan +_mpd_qround_to_integral,opa_abort +_mpd_qround_to_integral,_settriple +_mpd_qround_to_integral,mpd_qshiftr +_mpd_qround_to_integral,_mpd_apply_round_excess +mpd_qrem,mpd_qcopy +mpd_qrem,_mpd_fix_nan +mpd_qrem,opa_abort +mpd_qrem,mpd_realloc +mpd_qrem,abort +mpd_qrem,_mpd_qdivmod +mpd_qrem,mpd_qfinalize +_mpd_qmul_exact,_mpd_qmul +_mpd_qmul_exact,mpd_qfinalize +_mpd_qmul_exact,opa_abort +_mpd_qmul_exact,mpd_realloc +mpd_qround_to_int,_mpd_qround_to_integral +mpd_qround_to_intx,_mpd_qround_to_integral +mpd_qtrunc,_mpd_qround_to_integral +mpd_qfloor,_mpd_qround_to_integral +mpd_qceil,_mpd_qround_to_integral +mpd_sizeinbase,opa_abort +mpd_sizeinbase,log10 +mpd_qexport_u16,opa_abort +mpd_qexport_u16,mpd_sizeinbase +mpd_qexport_u16,mpd_alloc +mpd_qexport_u16,mpd_qshiftl +mpd_qexport_u16,mpd_qshiftr +mpd_qexport_u16,mpd_realloc +mpd_qexport_u16,_mpd_shortdiv +mpd_qimport_u16,opa_abort +mpd_qimport_u16,log10 +mpd_qimport_u16,mpd_realloc +mpd_qimport_u16,mpd_alloc +mpd_qimport_u16,mpd_switch_to_dyn +mpd_qimport_u16,mpd_realloc_dyn +mpd_qimport_u16,_mpd_shortmul_c +mpd_qimport_u16,_mpd_shortadd +mpd_qimport_u16,mpd_qfinalize +_mpd_basecmp,opa_abort +_kmul_worksize,_kmul_worksize +_kmul_worksize,fprintf +_kmul_worksize,fwrite +_kmul_worksize,fputc +_kmul_worksize,abort +_karatsuba_rec,opa_abort +_karatsuba_rec,_mpd_basemul +_karatsuba_rec,memset +_karatsuba_rec,_karatsuba_rec +_karatsuba_rec,_mpd_baseaddto +_karatsuba_rec,memcpy +_karatsuba_rec,_mpd_basesubfrom +_karatsuba_rec_fnt,opa_abort +_karatsuba_rec_fnt,_mpd_basemul +_karatsuba_rec_fnt,_mpd_fntmul +_karatsuba_rec_fnt,memcpy +_karatsuba_rec_fnt,memset +_karatsuba_rec_fnt,_karatsuba_rec_fnt +_karatsuba_rec_fnt,_mpd_baseaddto +_karatsuba_rec_fnt,_mpd_basesubfrom +_mpd_init_fnt_params,opa_abort +_mpd_init_fnt_params,mpd_sh_alloc +six_step_fnt,opa_abort +six_step_fnt,transpose_pow2 +six_step_fnt,_mpd_init_fnt_params +six_step_fnt,fnt_dif2 +six_step_fnt,_mpd_getkernel +inv_six_step_fnt,opa_abort +inv_six_step_fnt,_mpd_init_fnt_params +inv_six_step_fnt,fnt_dif2 +inv_six_step_fnt,_mpd_getkernel +inv_six_step_fnt,transpose_pow2 +transpose_pow2,opa_abort +transpose_pow2,squaretrans_pow2 +transpose_pow2,swap_halfrows_pow2 +transpose_pow2,abort +transpose_pow2,fprintf +transpose_pow2,fwrite +transpose_pow2,fputc +squaretrans_pow2,memcpy +swap_halfrows_pow2,opa_abort +swap_halfrows_pow2,mpd_calloc +swap_halfrows_pow2,memcpy +swap_halfrows_pow2,memmove +swap_halfrows_pow2,fprintf +swap_halfrows_pow2,fwrite +swap_halfrows_pow2,fputc +swap_halfrows_pow2,abort +std::__1::__next_prime\28unsigned\20long\29,abort +operator\20new\28unsigned\20long\29,opa_malloc +operator\20delete\28void*\29,opa_free +operator\20new\5b\5d\28unsigned\20long\29,opa_malloc +operator\20delete\5b\5d\28void*\29,opa_free +__cxa_pure_virtual,opa_abort +std::__1::basic_string\2c\20std::__1::allocator\20>::basic_string\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,opa_malloc +std::__1::basic_string\2c\20std::__1::allocator\20>::basic_string\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,memcpy +std::__1::basic_string\2c\20std::__1::allocator\20>::basic_string\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,abort +std::__1::basic_string\2c\20std::__1::allocator\20>::operator=\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,memcpy +std::__1::basic_string\2c\20std::__1::allocator\20>::operator=\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,opa_malloc +std::__1::basic_string\2c\20std::__1::allocator\20>::operator=\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,opa_free +std::__1::basic_string\2c\20std::__1::allocator\20>::operator=\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29,abort +std::__1::basic_string\2c\20std::__1::allocator\20>::resize\28unsigned\20long\2c\20char\29,std::__1::basic_string\2c\20std::__1::allocator\20>::append\28unsigned\20long\2c\20char\29 +std::__1::basic_string\2c\20std::__1::allocator\20>::append\28unsigned\20long\2c\20char\29,opa_malloc +std::__1::basic_string\2c\20std::__1::allocator\20>::append\28unsigned\20long\2c\20char\29,memcpy +std::__1::basic_string\2c\20std::__1::allocator\20>::append\28unsigned\20long\2c\20char\29,opa_free +std::__1::basic_string\2c\20std::__1::allocator\20>::append\28unsigned\20long\2c\20char\29,memset +std::__1::basic_string\2c\20std::__1::allocator\20>::append\28unsigned\20long\2c\20char\29,abort +std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\2c\20unsigned\20long\29,memcpy +std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\2c\20unsigned\20long\29,opa_malloc +std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\2c\20unsigned\20long\29,opa_free +std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\2c\20unsigned\20long\29,abort +std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\29,strlen +std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\2c\20unsigned\20long\29 +std::__1::basic_string\2c\20std::__1::allocator\20>::push_back\28char\29,opa_malloc +std::__1::basic_string\2c\20std::__1::allocator\20>::push_back\28char\29,memcpy +std::__1::basic_string\2c\20std::__1::allocator\20>::push_back\28char\29,opa_free +std::__1::basic_string\2c\20std::__1::allocator\20>::push_back\28char\29,abort +std::__1::basic_string\2c\20std::__1::allocator\20>::__assign_external\28char\20const*\2c\20unsigned\20long\29,memmove +std::__1::basic_string\2c\20std::__1::allocator\20>::__assign_external\28char\20const*\2c\20unsigned\20long\29,opa_malloc +std::__1::basic_string\2c\20std::__1::allocator\20>::__assign_external\28char\20const*\2c\20unsigned\20long\29,memcpy +std::__1::basic_string\2c\20std::__1::allocator\20>::__assign_external\28char\20const*\2c\20unsigned\20long\29,opa_free +std::__1::basic_string\2c\20std::__1::allocator\20>::__assign_external\28char\20const*\2c\20unsigned\20long\29,abort +std::__1::basic_string\2c\20std::__1::allocator\20>::assign\28char\20const*\2c\20unsigned\20long\29,std::__1::basic_string\2c\20std::__1::allocator\20>::__assign_external\28char\20const*\2c\20unsigned\20long\29 +std::__1::basic_string\2c\20std::__1::allocator\20>::assign\28char\20const*\29,strlen +std::__1::basic_string\2c\20std::__1::allocator\20>::assign\28char\20const*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::__assign_external\28char\20const*\2c\20unsigned\20long\29 +std::__1::basic_string\2c\20std::__1::allocator\20>::compare\28unsigned\20long\2c\20unsigned\20long\2c\20char\20const*\2c\20unsigned\20long\29\20const,memcmp +std::__1::basic_string\2c\20std::__1::allocator\20>::compare\28unsigned\20long\2c\20unsigned\20long\2c\20char\20const*\2c\20unsigned\20long\29\20const,abort +void\20std::__1::__sort&\2c\20int*>\28int*\2c\20int*\2c\20std::__1::__less&\29,unsigned\20int\20std::__1::__sort5&\2c\20int*>\28int*\2c\20int*\2c\20int*\2c\20int*\2c\20int*\2c\20std::__1::__less&\29 +void\20std::__1::__sort&\2c\20int*>\28int*\2c\20int*\2c\20std::__1::__less&\29,bool\20std::__1::__insertion_sort_incomplete&\2c\20int*>\28int*\2c\20int*\2c\20std::__1::__less&\29 +void\20std::__1::__sort&\2c\20int*>\28int*\2c\20int*\2c\20std::__1::__less&\29,void\20std::__1::__sort&\2c\20int*>\28int*\2c\20int*\2c\20std::__1::__less&\29 +bool\20std::__1::__insertion_sort_incomplete&\2c\20int*>\28int*\2c\20int*\2c\20std::__1::__less&\29,unsigned\20int\20std::__1::__sort5&\2c\20int*>\28int*\2c\20int*\2c\20int*\2c\20int*\2c\20int*\2c\20std::__1::__less&\29 +re2::BitState::Push\28int\2c\20char\20const*\29,operator\20new\28unsigned\20long\29 +re2::BitState::Push\28int\2c\20char\20const*\29,memmove +re2::BitState::Push\28int\2c\20char\20const*\29,operator\20delete\28void*\29 +re2::BitState::Push\28int\2c\20char\20const*\29,abort +re2::BitState::TrySearch\28int\2c\20char\20const*\29,re2::BitState::Push\28int\2c\20char\20const*\29 +re2::BitState::TrySearch\28int\2c\20char\20const*\29,opa_abort +re2::BitState::TrySearch\28int\2c\20char\20const*\29,re2::Prog::EmptyFlags\28re2::StringPiece\20const&\2c\20char\20const*\29 +re2::BitState::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,operator\20new\28unsigned\20long\29 +re2::BitState::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,operator\20delete\28void*\29 +re2::BitState::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,memset +re2::BitState::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,re2::BitState::TrySearch\28int\2c\20char\20const*\29 +re2::BitState::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,memchr +re2::BitState::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,re2::Prog::PrefixAccel_FrontAndBack\28void\20const*\2c\20unsigned\20long\29 +re2::BitState::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,abort +re2::Prog::SearchBitState\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20int\29,re2::BitState::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29 +re2::Prog::SearchBitState\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20int\29,operator\20delete\28void*\29 +re2::Compiler::AllocInst\28int\29,operator\20new\28unsigned\20long\29 +re2::Compiler::AllocInst\28int\29,memset +re2::Compiler::AllocInst\28int\29,memmove +re2::Compiler::AllocInst\28int\29,operator\20delete\28void*\29 +re2::Compiler::AllocInst\28int\29,abort +re2::Compiler::~Compiler\28\29,re2::Prog::~Prog\28\29 +re2::Compiler::~Compiler\28\29,operator\20delete\28void*\29 +re2::Compiler::~Compiler\28\29,re2::Regexp::Walker::~Walker\28\29 +re2::Regexp::Walker::~Walker\28\29,operator\20delete\5b\5d\28void*\29 +re2::Regexp::Walker::~Walker\28\29,operator\20delete\28void*\29 +re2::Regexp::Walker::~Walker\28\29,std::__1::__deque_base\2c\20std::__1::allocator\20>\20>::~__deque_base\28\29 +re2::Compiler::~Compiler\28\29.1,re2::Compiler::~Compiler\28\29 +re2::Compiler::~Compiler\28\29.1,operator\20delete\28void*\29 +re2::Compiler::Cat\28re2::Frag\2c\20re2::Frag\29,opa_abort +re2::Compiler::Star\28re2::Frag\2c\20bool\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::Star\28re2::Frag\2c\20bool\29,re2::Prog::Inst::InitAlt\28unsigned\20int\2c\20unsigned\20int\29 +re2::Compiler::Star\28re2::Frag\2c\20bool\29,opa_abort +re2::Compiler::Quest\28re2::Frag\2c\20bool\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::Quest\28re2::Frag\2c\20bool\29,re2::Prog::Inst::InitNop\28unsigned\20int\29 +re2::Compiler::Quest\28re2::Frag\2c\20bool\29,re2::Prog::Inst::InitAlt\28unsigned\20int\2c\20unsigned\20int\29 +re2::Compiler::Nop\28\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::Nop\28\29,re2::Prog::Inst::InitNop\28unsigned\20int\29 +re2::Compiler::ByteRange\28int\2c\20int\2c\20bool\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::ByteRange\28int\2c\20int\2c\20bool\29,re2::Prog::Inst::InitByteRange\28int\2c\20int\2c\20int\2c\20unsigned\20int\29 +re2::Compiler::Match\28int\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::Match\28int\29,re2::Prog::Inst::InitMatch\28int\29 +re2::Compiler::EmptyWidth\28re2::EmptyOp\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::EmptyWidth\28re2::EmptyOp\29,re2::Prog::Inst::InitEmptyWidth\28re2::EmptyOp\2c\20unsigned\20int\29 +re2::Compiler::Capture\28re2::Frag\2c\20int\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::Capture\28re2::Frag\2c\20int\29,re2::Prog::Inst::InitCapture\28int\2c\20unsigned\20int\29 +re2::Compiler::Capture\28re2::Frag\2c\20int\29,opa_abort +re2::Compiler::BeginRange\28\29,operator\20delete\28void*\29 +re2::Compiler::UncachedRuneByteSuffix\28unsigned\20char\2c\20unsigned\20char\2c\20bool\2c\20int\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::UncachedRuneByteSuffix\28unsigned\20char\2c\20unsigned\20char\2c\20bool\2c\20int\29,re2::Prog::Inst::InitByteRange\28int\2c\20int\2c\20int\2c\20unsigned\20int\29 +re2::Compiler::UncachedRuneByteSuffix\28unsigned\20char\2c\20unsigned\20char\2c\20bool\2c\20int\29,opa_abort +std::__1::pair\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\20>\20>::__emplace_unique_key_args\2c\20std::__1::tuple<>\20>\28unsigned\20long\20long\20const&\2c\20std::__1::piecewise_construct_t\20const&\2c\20std::__1::tuple&&\2c\20std::__1::tuple<>&&\29,operator\20new\28unsigned\20long\29 +std::__1::pair\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\20>\20>::__emplace_unique_key_args\2c\20std::__1::tuple<>\20>\28unsigned\20long\20long\20const&\2c\20std::__1::piecewise_construct_t\20const&\2c\20std::__1::tuple&&\2c\20std::__1::tuple<>&&\29,std::__1::__next_prime\28unsigned\20long\29 +std::__1::pair\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\20>\20>::__emplace_unique_key_args\2c\20std::__1::tuple<>\20>\28unsigned\20long\20long\20const&\2c\20std::__1::piecewise_construct_t\20const&\2c\20std::__1::tuple&&\2c\20std::__1::tuple<>&&\29,std::__1::__hash_table\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\20>\20>::__rehash\28unsigned\20long\29 +re2::Compiler::AddSuffixRecursive\28int\2c\20int\29,opa_abort +re2::Compiler::AddSuffixRecursive\28int\2c\20int\29,re2::Compiler::FindByteRange\28int\2c\20int\29 +re2::Compiler::AddSuffixRecursive\28int\2c\20int\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::AddSuffixRecursive\28int\2c\20int\29,re2::Prog::Inst::InitAlt\28unsigned\20int\2c\20unsigned\20int\29 +re2::Compiler::AddSuffixRecursive\28int\2c\20int\29,std::__1::__hash_iterator\2c\20void*>*>\20std::__1::__hash_table\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\20>\20>::find\28unsigned\20long\20long\20const&\29 +re2::Compiler::AddSuffixRecursive\28int\2c\20int\29,re2::Prog::Inst::InitByteRange\28int\2c\20int\2c\20int\2c\20unsigned\20int\29 +re2::Compiler::AddSuffixRecursive\28int\2c\20int\29,re2::Compiler::AddSuffixRecursive\28int\2c\20int\29 +re2::Compiler::FindByteRange\28int\2c\20int\29,opa_abort +re2::Compiler::FindByteRange\28int\2c\20int\29,re2::Compiler::ByteRangeEqual\28int\2c\20int\29 +re2::Compiler::ByteRangeEqual\28int\2c\20int\29,opa_abort +re2::Compiler::AddRuneRange\28int\2c\20int\2c\20bool\29,re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29 +re2::Compiler::AddRuneRange\28int\2c\20int\2c\20bool\29,re2::Compiler::AddRuneRangeLatin1\28int\2c\20int\2c\20bool\29 +re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29,re2::Compiler::Add_80_10ffff\28\29 +re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29,runetochar +re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29,re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29 +re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29,opa_abort +re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29,std::__1::__hash_iterator\2c\20void*>*>\20std::__1::__hash_table\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\20>\20>::find\28unsigned\20long\20long\20const&\29 +re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29,re2::Compiler::UncachedRuneByteSuffix\28unsigned\20char\2c\20unsigned\20char\2c\20bool\2c\20int\29 +re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29,std::__1::pair\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\20>\20>::__emplace_unique_key_args\2c\20std::__1::tuple<>\20>\28unsigned\20long\20long\20const&\2c\20std::__1::piecewise_construct_t\20const&\2c\20std::__1::tuple&&\2c\20std::__1::tuple<>&&\29 +re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29,re2::Compiler::AddSuffixRecursive\28int\2c\20int\29 +re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29,re2::Prog::Inst::InitAlt\28unsigned\20int\2c\20unsigned\20int\29 +re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29,re2::Prog::Inst::InitByteRange\28int\2c\20int\2c\20int\2c\20unsigned\20int\29 +re2::Compiler::AddRuneRangeLatin1\28int\2c\20int\2c\20bool\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::AddRuneRangeLatin1\28int\2c\20int\2c\20bool\29,re2::Prog::Inst::InitByteRange\28int\2c\20int\2c\20int\2c\20unsigned\20int\29 +re2::Compiler::AddRuneRangeLatin1\28int\2c\20int\2c\20bool\29,re2::Compiler::AddSuffixRecursive\28int\2c\20int\29 +re2::Compiler::AddRuneRangeLatin1\28int\2c\20int\2c\20bool\29,re2::Prog::Inst::InitAlt\28unsigned\20int\2c\20unsigned\20int\29 +re2::Compiler::Add_80_10ffff\28\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::Add_80_10ffff\28\29,re2::Prog::Inst::InitByteRange\28int\2c\20int\2c\20int\2c\20unsigned\20int\29 +re2::Compiler::Add_80_10ffff\28\29,re2::Compiler::UncachedRuneByteSuffix\28unsigned\20char\2c\20unsigned\20char\2c\20bool\2c\20int\29 +re2::Compiler::Add_80_10ffff\28\29,re2::Compiler::AddSuffixRecursive\28int\2c\20int\29 +re2::Compiler::Add_80_10ffff\28\29,re2::Prog::Inst::InitAlt\28unsigned\20int\2c\20unsigned\20int\29 +re2::Compiler::Literal\28int\2c\20bool\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::Literal\28int\2c\20bool\29,re2::Prog::Inst::InitByteRange\28int\2c\20int\2c\20int\2c\20unsigned\20int\29 +re2::Compiler::Literal\28int\2c\20bool\29,runetochar +re2::Compiler::Literal\28int\2c\20bool\29,re2::Compiler::Cat\28re2::Frag\2c\20re2::Frag\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::Nop\28\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::Match\28int\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::EmptyWidth\28re2::EmptyOp\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::Cat\28re2::Frag\2c\20re2::Frag\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Prog::Inst::InitAlt\28unsigned\20int\2c\20unsigned\20int\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::Star\28re2::Frag\2c\20bool\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::Quest\28re2::Frag\2c\20bool\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::Literal\28int\2c\20bool\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,opa_abort +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::BeginRange\28\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::AddRuneRange\28int\2c\20int\2c\20bool\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::ByteRange\28int\2c\20int\2c\20bool\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::AddRuneRangeUTF8\28int\2c\20int\2c\20bool\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::AddRuneRangeLatin1\28int\2c\20int\2c\20bool\29 +re2::Compiler::PostVisit\28re2::Regexp*\2c\20re2::Frag\2c\20re2::Frag\2c\20re2::Frag*\2c\20int\29,re2::Compiler::Capture\28re2::Frag\2c\20int\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,operator\20new\28unsigned\20long\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,re2::Prog::Prog\28\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,re2::Prog::Inst::InitFail\28\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,re2::Regexp::Simplify\28\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,re2::IsAnchorStart\28re2::Regexp**\2c\20int\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,re2::IsAnchorEnd\28re2::Regexp**\2c\20int\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20re2::Frag\2c\20bool\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,re2::Regexp::Decref\28\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,memset +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,memmove +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,operator\20delete\28void*\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,re2::Prog::Inst::InitMatch\28int\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,re2::Compiler::Cat\28re2::Frag\2c\20re2::Frag\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,re2::Compiler::DotStar\28\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,re2::Compiler::Finish\28re2::Regexp*\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,re2::Compiler::~Compiler\28\29 +re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29,abort +re2::IsAnchorStart\28re2::Regexp**\2c\20int\29,re2::Regexp::Incref\28\29 +re2::IsAnchorStart\28re2::Regexp**\2c\20int\29,re2::IsAnchorStart\28re2::Regexp**\2c\20int\29 +re2::IsAnchorStart\28re2::Regexp**\2c\20int\29,operator\20new\28unsigned\20long\29 +re2::IsAnchorStart\28re2::Regexp**\2c\20int\29,re2::Regexp::Concat\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::IsAnchorStart\28re2::Regexp**\2c\20int\29,re2::Regexp::Decref\28\29 +re2::IsAnchorStart\28re2::Regexp**\2c\20int\29,operator\20delete\28void*\29 +re2::IsAnchorStart\28re2::Regexp**\2c\20int\29,opa_abort +re2::IsAnchorStart\28re2::Regexp**\2c\20int\29,re2::Regexp::Capture\28re2::Regexp*\2c\20re2::Regexp::ParseFlags\2c\20int\29 +re2::IsAnchorStart\28re2::Regexp**\2c\20int\29,re2::Regexp::LiteralString\28int*\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::IsAnchorEnd\28re2::Regexp**\2c\20int\29,re2::Regexp::Incref\28\29 +re2::IsAnchorEnd\28re2::Regexp**\2c\20int\29,re2::IsAnchorEnd\28re2::Regexp**\2c\20int\29 +re2::IsAnchorEnd\28re2::Regexp**\2c\20int\29,operator\20new\28unsigned\20long\29 +re2::IsAnchorEnd\28re2::Regexp**\2c\20int\29,re2::Regexp::Concat\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::IsAnchorEnd\28re2::Regexp**\2c\20int\29,re2::Regexp::Decref\28\29 +re2::IsAnchorEnd\28re2::Regexp**\2c\20int\29,operator\20delete\28void*\29 +re2::IsAnchorEnd\28re2::Regexp**\2c\20int\29,opa_abort +re2::IsAnchorEnd\28re2::Regexp**\2c\20int\29,re2::Regexp::Capture\28re2::Regexp*\2c\20re2::Regexp::ParseFlags\2c\20int\29 +re2::IsAnchorEnd\28re2::Regexp**\2c\20int\29,re2::Regexp::LiteralString\28int*\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20re2::Frag\2c\20bool\29,operator\20delete\5b\5d\28void*\29 +re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20re2::Frag\2c\20bool\29,operator\20delete\28void*\29 +re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20re2::Frag\2c\20bool\29,std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29 +re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20re2::Frag\2c\20bool\29,operator\20new\5b\5d\28unsigned\20long\29 +re2::Compiler::DotStar\28\29,re2::Compiler::AllocInst\28int\29 +re2::Compiler::DotStar\28\29,re2::Prog::Inst::InitByteRange\28int\2c\20int\2c\20int\2c\20unsigned\20int\29 +re2::Compiler::DotStar\28\29,re2::Prog::Inst::InitAlt\28unsigned\20int\2c\20unsigned\20int\29 +re2::Compiler::DotStar\28\29,opa_abort +re2::Compiler::Finish\28re2::Regexp*\29,operator\20delete\28void*\29 +re2::Compiler::Finish\28re2::Regexp*\29,re2::Prog::Optimize\28\29 +re2::Compiler::Finish\28re2::Regexp*\29,re2::Prog::Flatten\28\29 +re2::Compiler::Finish\28re2::Regexp*\29,re2::Prog::ComputeByteMap\28\29 +re2::Compiler::Finish\28re2::Regexp*\29,re2::Regexp::RequiredPrefixForAccel\28std::__1::basic_string\2c\20std::__1::allocator\20>*\2c\20bool*\29 +re2::Regexp::CompileToProg\28long\20long\29,re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29 +re2::Regexp::CompileToReverseProg\28long\20long\29,re2::Compiler::Compile\28re2::Regexp*\2c\20bool\2c\20long\20long\29 +std::__1::__deque_base\2c\20std::__1::allocator\20>\20>::~__deque_base\28\29,operator\20delete\28void*\29 +std::__1::__hash_table\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\20>\20>::__rehash\28unsigned\20long\29,operator\20new\28unsigned\20long\29 +std::__1::__hash_table\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\20>\20>::__rehash\28unsigned\20long\29,operator\20delete\28void*\29 +std::__1::__hash_table\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\20>\20>::__rehash\28unsigned\20long\29,abort +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,memmove +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,operator\20new\28unsigned\20long\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,operator\20delete\28void*\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,abort +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29,memmove +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29,operator\20delete\28void*\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29,abort +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29,memmove +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29,operator\20delete\28void*\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29,abort +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29,memmove +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29,operator\20delete\28void*\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29,abort +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29,memmove +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29,operator\20delete\28void*\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29,abort +re2::DFA::DFA\28re2::Prog*\2c\20re2::Prog::MatchKind\2c\20long\20long\29,operator\20new\28unsigned\20long\29 +re2::DFA::DFA\28re2::Prog*\2c\20re2::Prog::MatchKind\2c\20long\20long\29,abort +re2::DFA::~DFA\28\29,opa_abort +re2::DFA::~DFA\28\29,operator\20delete\28void*\29 +re2::DFA::WorkqToCachedState\28re2::DFA::Workq*\2c\20re2::DFA::Workq*\2c\20unsigned\20int\29,operator\20new\28unsigned\20long\29 +re2::DFA::WorkqToCachedState\28re2::DFA::Workq*\2c\20re2::DFA::Workq*\2c\20unsigned\20int\29,opa_abort +re2::DFA::WorkqToCachedState\28re2::DFA::Workq*\2c\20re2::DFA::Workq*\2c\20unsigned\20int\29,void\20std::__1::__sort&\2c\20int*>\28int*\2c\20int*\2c\20std::__1::__less&\29 +re2::DFA::WorkqToCachedState\28re2::DFA::Workq*\2c\20re2::DFA::Workq*\2c\20unsigned\20int\29,abort +re2::DFA::WorkqToCachedState\28re2::DFA::Workq*\2c\20re2::DFA::Workq*\2c\20unsigned\20int\29,re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29 +re2::DFA::WorkqToCachedState\28re2::DFA::Workq*\2c\20re2::DFA::Workq*\2c\20unsigned\20int\29,operator\20delete\28void*\29 +re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29,std::__1::__hash_iterator*>\20std::__1::__hash_table\20>::find\28re2::DFA::State*\20const&\29 +re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29,operator\20new\28unsigned\20long\29 +re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29,memset +re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29,memmove +re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29,std::__1::pair*>\2c\20bool>\20std::__1::__hash_table\20>::__emplace_unique_key_args\28re2::DFA::State*\20const&\2c\20re2::DFA::State*\20const&\29 +std::__1::__hash_iterator*>\20std::__1::__hash_table\20>::find\28re2::DFA::State*\20const&\29,opa_abort +std::__1::pair*>\2c\20bool>\20std::__1::__hash_table\20>::__emplace_unique_key_args\28re2::DFA::State*\20const&\2c\20re2::DFA::State*\20const&\29,opa_abort +std::__1::pair*>\2c\20bool>\20std::__1::__hash_table\20>::__emplace_unique_key_args\28re2::DFA::State*\20const&\2c\20re2::DFA::State*\20const&\29,operator\20new\28unsigned\20long\29 +std::__1::pair*>\2c\20bool>\20std::__1::__hash_table\20>::__emplace_unique_key_args\28re2::DFA::State*\20const&\2c\20re2::DFA::State*\20const&\29,std::__1::__next_prime\28unsigned\20long\29 +std::__1::pair*>\2c\20bool>\20std::__1::__hash_table\20>::__emplace_unique_key_args\28re2::DFA::State*\20const&\2c\20re2::DFA::State*\20const&\29,std::__1::__hash_table\20>::__rehash\28unsigned\20long\29 +re2::SparseSetT::InsertInternal\28bool\2c\20int\29,opa_abort +re2::SparseSetT::InsertInternal\28bool\2c\20int\29,re2::SparseSetT::create_index\28int\29 +re2::DFA::AddToQueue\28re2::DFA::Workq*\2c\20int\2c\20unsigned\20int\29,opa_abort +re2::DFA::AddToQueue\28re2::DFA::Workq*\2c\20int\2c\20unsigned\20int\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +re2::DFA::RunWorkqOnByte\28re2::DFA::Workq*\2c\20re2::DFA::Workq*\2c\20int\2c\20unsigned\20int\2c\20bool*\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +re2::DFA::RunWorkqOnByte\28re2::DFA::Workq*\2c\20re2::DFA::Workq*\2c\20int\2c\20unsigned\20int\2c\20bool*\29,re2::DFA::AddToQueue\28re2::DFA::Workq*\2c\20int\2c\20unsigned\20int\29 +re2::DFA::RunWorkqOnByte\28re2::DFA::Workq*\2c\20re2::DFA::Workq*\2c\20int\2c\20unsigned\20int\2c\20bool*\29,opa_abort +re2::DFA::RunStateOnByte\28re2::DFA::State*\2c\20int\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +re2::DFA::RunStateOnByte\28re2::DFA::State*\2c\20int\29,re2::DFA::AddToQueue\28re2::DFA::Workq*\2c\20int\2c\20unsigned\20int\29 +re2::DFA::RunStateOnByte\28re2::DFA::State*\2c\20int\29,re2::DFA::RunWorkqOnByte\28re2::DFA::Workq*\2c\20re2::DFA::Workq*\2c\20int\2c\20unsigned\20int\2c\20bool*\29 +re2::DFA::RunStateOnByte\28re2::DFA::State*\2c\20int\29,re2::DFA::WorkqToCachedState\28re2::DFA::Workq*\2c\20re2::DFA::Workq*\2c\20unsigned\20int\29 +re2::DFA::ResetCache\28re2::DFA::RWLocker*\29,re2::hooks::GetDFAStateCacheResetHook\28\29 +re2::DFA::ResetCache\28re2::DFA::RWLocker*\29,operator\20delete\28void*\29 +re2::DFA::SearchFFF\28re2::DFA::SearchParams*\29,bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::RunStateOnByte\28re2::DFA::State*\2c\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20new\5b\5d\28unsigned\20long\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,memmove +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::ResetCache\28re2::DFA::RWLocker*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20delete\5b\5d\28void*\29 +re2::DFA::SearchFFT\28re2::DFA::SearchParams*\29,bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::RunStateOnByte\28re2::DFA::State*\2c\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20new\5b\5d\28unsigned\20long\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,memmove +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::ResetCache\28re2::DFA::RWLocker*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20delete\5b\5d\28void*\29 +re2::DFA::SearchFTF\28re2::DFA::SearchParams*\29,bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::RunStateOnByte\28re2::DFA::State*\2c\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20new\5b\5d\28unsigned\20long\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,memmove +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::ResetCache\28re2::DFA::RWLocker*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20delete\5b\5d\28void*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +re2::DFA::SearchFTT\28re2::DFA::SearchParams*\29,bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::RunStateOnByte\28re2::DFA::State*\2c\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20new\5b\5d\28unsigned\20long\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,memmove +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::ResetCache\28re2::DFA::RWLocker*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20delete\5b\5d\28void*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +re2::DFA::SearchTFF\28re2::DFA::SearchParams*\29,bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,opa_abort +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,memchr +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::Prog::PrefixAccel_FrontAndBack\28void\20const*\2c\20unsigned\20long\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::RunStateOnByte\28re2::DFA::State*\2c\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20new\5b\5d\28unsigned\20long\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,memmove +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::ResetCache\28re2::DFA::RWLocker*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20delete\5b\5d\28void*\29 +re2::DFA::SearchTFT\28re2::DFA::SearchParams*\29,bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,opa_abort +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,memchr +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::Prog::PrefixAccel_FrontAndBack\28void\20const*\2c\20unsigned\20long\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::RunStateOnByte\28re2::DFA::State*\2c\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20new\5b\5d\28unsigned\20long\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,memmove +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::ResetCache\28re2::DFA::RWLocker*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20delete\5b\5d\28void*\29 +re2::DFA::SearchTTF\28re2::DFA::SearchParams*\29,bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,opa_abort +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,memchr +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::Prog::PrefixAccel_FrontAndBack\28void\20const*\2c\20unsigned\20long\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::RunStateOnByte\28re2::DFA::State*\2c\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20new\5b\5d\28unsigned\20long\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,memmove +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::ResetCache\28re2::DFA::RWLocker*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20delete\5b\5d\28void*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +re2::DFA::SearchTTT\28re2::DFA::SearchParams*\29,bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,opa_abort +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,memchr +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::Prog::PrefixAccel_FrontAndBack\28void\20const*\2c\20unsigned\20long\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::RunStateOnByte\28re2::DFA::State*\2c\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20new\5b\5d\28unsigned\20long\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,memmove +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::ResetCache\28re2::DFA::RWLocker*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::DFA::CachedState\28int*\2c\20int\2c\20unsigned\20int\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,operator\20delete\5b\5d\28void*\29 +bool\20re2::DFA::InlinedSearchLoop\28re2::DFA::SearchParams*\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +re2::DFA::AnalyzeSearch\28re2::DFA::SearchParams*\29,re2::DFA::AddToQueue\28re2::DFA::Workq*\2c\20int\2c\20unsigned\20int\29 +re2::DFA::AnalyzeSearch\28re2::DFA::SearchParams*\29,re2::DFA::WorkqToCachedState\28re2::DFA::Workq*\2c\20re2::DFA::Workq*\2c\20unsigned\20int\29 +re2::DFA::AnalyzeSearch\28re2::DFA::SearchParams*\29,re2::DFA::ResetCache\28re2::DFA::RWLocker*\29 +re2::DFA::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20bool\2c\20bool*\2c\20char\20const**\2c\20re2::SparseSetT*\29,re2::DFA::AnalyzeSearch\28re2::DFA::SearchParams*\29 +re2::Prog::GetDFA\28re2::Prog::MatchKind\29,std::__1::__call_once\28unsigned\20long\20volatile&\2c\20void*\2c\20void\20\28*\29\28void*\29\29 +void\20std::__1::__call_once_proxy\20>\28void*\29,operator\20new\28unsigned\20long\29 +void\20std::__1::__call_once_proxy\20>\28void*\29,re2::DFA::DFA\28re2::Prog*\2c\20re2::Prog::MatchKind\2c\20long\20long\29 +void\20std::__1::__call_once_proxy\20>\28void*\29,operator\20new\28unsigned\20long\29 +void\20std::__1::__call_once_proxy\20>\28void*\29,re2::DFA::DFA\28re2::Prog*\2c\20re2::Prog::MatchKind\2c\20long\20long\29 +void\20std::__1::__call_once_proxy\20>\28void*\29,operator\20new\28unsigned\20long\29 +void\20std::__1::__call_once_proxy\20>\28void*\29,re2::DFA::DFA\28re2::Prog*\2c\20re2::Prog::MatchKind\2c\20long\20long\29 +re2::Prog::DeleteDFA\28re2::DFA*\29,re2::DFA::~DFA\28\29 +re2::Prog::DeleteDFA\28re2::DFA*\29,operator\20delete\28void*\29 +re2::Prog::SearchDFA\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20bool*\2c\20re2::SparseSetT*\29,re2::Prog::GetDFA\28re2::Prog::MatchKind\29 +re2::Prog::SearchDFA\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20bool*\2c\20re2::SparseSetT*\29,re2::DFA::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20bool\2c\20bool*\2c\20char\20const**\2c\20re2::SparseSetT*\29 +re2::Prog::SearchDFA\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20bool*\2c\20re2::SparseSetT*\29,re2::hooks::GetDFASearchFailureHook\28\29 +re2::SparseSetT::create_index\28int\29,opa_abort +std::__1::__hash_table\20>::__rehash\28unsigned\20long\29,operator\20new\28unsigned\20long\29 +std::__1::__hash_table\20>::__rehash\28unsigned\20long\29,operator\20delete\28void*\29 +std::__1::__hash_table\20>::__rehash\28unsigned\20long\29,opa_abort +std::__1::__hash_table\20>::__rehash\28unsigned\20long\29,abort +re2::NFA::NFA\28re2::Prog*\29,memset +re2::NFA::NFA\28re2::Prog*\29,re2::SparseArray::resize\28int\29 +re2::NFA::NFA\28re2::Prog*\29,operator\20new\28unsigned\20long\29 +re2::NFA::NFA\28re2::Prog*\29,operator\20delete\28void*\29 +re2::NFA::NFA\28re2::Prog*\29,abort +re2::SparseArray::resize\28int\29,opa_abort +re2::SparseArray::resize\28int\29,operator\20new\28unsigned\20long\29 +re2::SparseArray::resize\28int\29,memmove +re2::SparseArray::resize\28int\29,operator\20delete\28void*\29 +re2::SparseArray::resize\28int\29,abort +re2::NFA::~NFA\28\29,operator\20delete\5b\5d\28void*\29 +re2::NFA::~NFA\28\29,std::__1::__deque_base\20>::~__deque_base\28\29 +re2::NFA::~NFA\28\29,operator\20delete\28void*\29 +re2::NFA::~NFA\28\29,opa_abort +std::__1::__deque_base\20>::~__deque_base\28\29,operator\20delete\28void*\29 +re2::NFA::AddToThreadq\28re2::SparseArray*\2c\20int\2c\20int\2c\20re2::StringPiece\20const&\2c\20char\20const*\2c\20re2::NFA::Thread*\29,opa_abort +re2::NFA::AddToThreadq\28re2::SparseArray*\2c\20int\2c\20int\2c\20re2::StringPiece\20const&\2c\20char\20const*\2c\20re2::NFA::Thread*\29,std::__1::deque\20>::__add_back_capacity\28\29 +re2::NFA::AddToThreadq\28re2::SparseArray*\2c\20int\2c\20int\2c\20re2::StringPiece\20const&\2c\20char\20const*\2c\20re2::NFA::Thread*\29,operator\20new\5b\5d\28unsigned\20long\29 +re2::NFA::AddToThreadq\28re2::SparseArray*\2c\20int\2c\20int\2c\20re2::StringPiece\20const&\2c\20char\20const*\2c\20re2::NFA::Thread*\29,memmove +re2::NFA::AddToThreadq\28re2::SparseArray*\2c\20int\2c\20int\2c\20re2::StringPiece\20const&\2c\20char\20const*\2c\20re2::NFA::Thread*\29,re2::Prog::EmptyFlags\28re2::StringPiece\20const&\2c\20char\20const*\29 +std::__1::deque\20>::__add_back_capacity\28\29,memmove +std::__1::deque\20>::__add_back_capacity\28\29,operator\20new\28unsigned\20long\29 +std::__1::deque\20>::__add_back_capacity\28\29,operator\20delete\28void*\29 +std::__1::deque\20>::__add_back_capacity\28\29,std::__1::__split_buffer\20>::push_back\28re2::NFA::Thread*&&\29 +std::__1::deque\20>::__add_back_capacity\28\29,std::__1::__split_buffer\20>::push_front\28re2::NFA::Thread*&&\29 +std::__1::deque\20>::__add_back_capacity\28\29,std::__1::__split_buffer&>::push_back\28re2::NFA::Thread*&&\29 +std::__1::deque\20>::__add_back_capacity\28\29,std::__1::__split_buffer&>::push_front\28re2::NFA::Thread*\20const&\29 +std::__1::deque\20>::__add_back_capacity\28\29,abort +re2::NFA::Step\28re2::SparseArray*\2c\20re2::SparseArray*\2c\20int\2c\20re2::StringPiece\20const&\2c\20char\20const*\29,memmove +re2::NFA::Step\28re2::SparseArray*\2c\20re2::SparseArray*\2c\20int\2c\20re2::StringPiece\20const&\2c\20char\20const*\29,opa_abort +re2::NFA::Step\28re2::SparseArray*\2c\20re2::SparseArray*\2c\20int\2c\20re2::StringPiece\20const&\2c\20char\20const*\29,re2::NFA::AddToThreadq\28re2::SparseArray*\2c\20int\2c\20int\2c\20re2::StringPiece\20const&\2c\20char\20const*\2c\20re2::NFA::Thread*\29 +re2::NFA::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,operator\20new\5b\5d\28unsigned\20long\29 +re2::NFA::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,memset +re2::NFA::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,re2::NFA::Step\28re2::SparseArray*\2c\20re2::SparseArray*\2c\20int\2c\20re2::StringPiece\20const&\2c\20char\20const*\29 +re2::NFA::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,opa_abort +re2::NFA::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,re2::Prog::PrefixAccel\28void\20const*\2c\20unsigned\20long\29 +re2::NFA::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,std::__1::deque\20>::__add_back_capacity\28\29 +re2::NFA::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,memmove +re2::NFA::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29,re2::NFA::AddToThreadq\28re2::SparseArray*\2c\20int\2c\20int\2c\20re2::StringPiece\20const&\2c\20char\20const*\2c\20re2::NFA::Thread*\29 +re2::Prog::PrefixAccel\28void\20const*\2c\20unsigned\20long\29,opa_abort +re2::Prog::PrefixAccel\28void\20const*\2c\20unsigned\20long\29,memchr +re2::Prog::PrefixAccel\28void\20const*\2c\20unsigned\20long\29,re2::Prog::PrefixAccel_FrontAndBack\28void\20const*\2c\20unsigned\20long\29 +re2::Prog::SearchNFA\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20int\29,re2::NFA::NFA\28re2::Prog*\29 +re2::Prog::SearchNFA\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20int\29,re2::NFA::Search\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20bool\2c\20bool\2c\20re2::StringPiece*\2c\20int\29 +re2::Prog::SearchNFA\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20int\29,re2::NFA::~NFA\28\29 +re2::SparseArray::SetInternal\28bool\2c\20int\2c\20int\20const&\29,opa_abort +re2::SparseArray::SetInternal\28bool\2c\20int\2c\20int\20const&\29,re2::SparseArray::create_index\28int\29 +re2::SparseArray::SetInternal\28bool\2c\20int\2c\20int\20const&\29,re2::SparseArray::SetExistingInternal\28int\2c\20int\20const&\29 +re2::SparseArray::create_index\28int\29,opa_abort +re2::SparseArray::SetExistingInternal\28int\2c\20int\20const&\29,opa_abort +std::__1::__split_buffer\20>::push_back\28re2::NFA::Thread*&&\29,memmove +std::__1::__split_buffer\20>::push_back\28re2::NFA::Thread*&&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer\20>::push_back\28re2::NFA::Thread*&&\29,operator\20delete\28void*\29 +std::__1::__split_buffer\20>::push_back\28re2::NFA::Thread*&&\29,abort +std::__1::__split_buffer\20>::push_front\28re2::NFA::Thread*&&\29,memmove +std::__1::__split_buffer\20>::push_front\28re2::NFA::Thread*&&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer\20>::push_front\28re2::NFA::Thread*&&\29,operator\20delete\28void*\29 +std::__1::__split_buffer\20>::push_front\28re2::NFA::Thread*&&\29,abort +std::__1::__split_buffer&>::push_back\28re2::NFA::Thread*&&\29,memmove +std::__1::__split_buffer&>::push_back\28re2::NFA::Thread*&&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer&>::push_back\28re2::NFA::Thread*&&\29,operator\20delete\28void*\29 +std::__1::__split_buffer&>::push_back\28re2::NFA::Thread*&&\29,abort +std::__1::__split_buffer&>::push_front\28re2::NFA::Thread*\20const&\29,memmove +std::__1::__split_buffer&>::push_front\28re2::NFA::Thread*\20const&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer&>::push_front\28re2::NFA::Thread*\20const&\29,operator\20delete\28void*\29 +std::__1::__split_buffer&>::push_front\28re2::NFA::Thread*\20const&\29,abort +re2::Prog::SearchOnePass\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20int\29,memset +re2::Prog::SearchOnePass\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20int\29,re2::Prog::EmptyFlags\28re2::StringPiece\20const&\2c\20char\20const*\29 +re2::Prog::SearchOnePass\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20int\29,memcpy +re2::Prog::IsOnePass\28\29,operator\20new\28unsigned\20long\29 +re2::Prog::IsOnePass\28\29,memset +re2::Prog::IsOnePass\28\29,opa_abort +re2::Prog::IsOnePass\28\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +re2::Prog::IsOnePass\28\29,std::__1::vector\20>::insert\28std::__1::__wrap_iter\2c\20unsigned\20long\2c\20unsigned\20char\20const&\29 +re2::Prog::IsOnePass\28\29,operator\20delete\28void*\29 +re2::Prog::IsOnePass\28\29,memmove +re2::Prog::IsOnePass\28\29,abort +std::__1::vector\20>::insert\28std::__1::__wrap_iter\2c\20unsigned\20long\2c\20unsigned\20char\20const&\29,memmove +std::__1::vector\20>::insert\28std::__1::__wrap_iter\2c\20unsigned\20long\2c\20unsigned\20char\20const&\29,operator\20new\28unsigned\20long\29 +std::__1::vector\20>::insert\28std::__1::__wrap_iter\2c\20unsigned\20long\2c\20unsigned\20char\20const&\29,memcpy +std::__1::vector\20>::insert\28std::__1::__wrap_iter\2c\20unsigned\20long\2c\20unsigned\20char\20const&\29,operator\20delete\28void*\29 +std::__1::vector\20>::insert\28std::__1::__wrap_iter\2c\20unsigned\20long\2c\20unsigned\20char\20const&\29,abort +std::__1::__tree\20>::destroy\28std::__1::__tree_node*\29,std::__1::__tree\20>::destroy\28std::__1::__tree_node*\29 +std::__1::__tree\20>::destroy\28std::__1::__tree_node*\29,operator\20delete\28void*\29 +re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29,re2::Regexp::ParseState::MaybeConcatString\28int\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29,re2::CharClassBuilder::RemoveAbove\28int\29 +re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29,re2::Regexp::Decref\28\29 +re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29,operator\20new\28unsigned\20long\29 +re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29,re2::CharClassBuilder::Contains\28int\29 +re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29,re2::Regexp::ComputeSimple\28\29 +re2::Regexp::ParseState::MaybeConcatString\28int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::AddRuneToString\28int\29 +re2::Regexp::ParseState::MaybeConcatString\28int\2c\20re2::Regexp::ParseFlags\29,operator\20delete\5b\5d\28void*\29 +re2::Regexp::ParseState::MaybeConcatString\28int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::Decref\28\29 +re2::Regexp::ParseState::PushLiteral\28int\29,re2::CycleFoldRune\28int\29 +re2::Regexp::ParseState::PushLiteral\28int\29,operator\20new\28unsigned\20long\29 +re2::Regexp::ParseState::PushLiteral\28int\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::PushLiteral\28int\29,re2::CharClassBuilder::CharClassBuilder\28\29 +re2::Regexp::ParseState::PushLiteral\28int\29,re2::CharClassBuilder::AddRange\28int\2c\20int\29 +re2::Regexp::ParseState::PushLiteral\28int\29,re2::Regexp::ParseState::MaybeConcatString\28int\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::PushLiteral\28int\29,re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29 +re2::Regexp::ParseState::PushSimpleOp\28re2::RegexpOp\29,operator\20new\28unsigned\20long\29 +re2::Regexp::ParseState::PushSimpleOp\28re2::RegexpOp\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::PushSimpleOp\28re2::RegexpOp\29,re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29 +re2::Regexp::ParseState::PushDot\28\29,operator\20new\28unsigned\20long\29 +re2::Regexp::ParseState::PushDot\28\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::PushDot\28\29,re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29 +re2::Regexp::ParseState::PushDot\28\29,re2::CharClassBuilder::CharClassBuilder\28\29 +re2::Regexp::ParseState::PushDot\28\29,re2::CharClassBuilder::AddRange\28int\2c\20int\29 +re2::Regexp::ParseState::PushRepeatOp\28re2::RegexpOp\2c\20re2::StringPiece\20const&\2c\20bool\29,operator\20new\28unsigned\20long\29 +re2::Regexp::ParseState::PushRepeatOp\28re2::RegexpOp\2c\20re2::StringPiece\20const&\2c\20bool\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::PushRepeatOp\28re2::RegexpOp\2c\20re2::StringPiece\20const&\2c\20bool\29,re2::CharClassBuilder::GetCharClass\28\29 +re2::Regexp::ParseState::PushRepeatOp\28re2::RegexpOp\2c\20re2::StringPiece\20const&\2c\20bool\29,std::__1::__tree\20>::destroy\28std::__1::__tree_node*\29 +re2::Regexp::ParseState::PushRepeatOp\28re2::RegexpOp\2c\20re2::StringPiece\20const&\2c\20bool\29,operator\20delete\28void*\29 +re2::Regexp::ParseState::PushRepeatOp\28re2::RegexpOp\2c\20re2::StringPiece\20const&\2c\20bool\29,re2::Regexp::ComputeSimple\28\29 +re2::Regexp::ParseState::PushRepetition\28int\2c\20int\2c\20re2::StringPiece\20const&\2c\20bool\29,operator\20new\28unsigned\20long\29 +re2::Regexp::ParseState::PushRepetition\28int\2c\20int\2c\20re2::StringPiece\20const&\2c\20bool\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::PushRepetition\28int\2c\20int\2c\20re2::StringPiece\20const&\2c\20bool\29,re2::CharClassBuilder::GetCharClass\28\29 +re2::Regexp::ParseState::PushRepetition\28int\2c\20int\2c\20re2::StringPiece\20const&\2c\20bool\29,std::__1::__tree\20>::destroy\28std::__1::__tree_node*\29 +re2::Regexp::ParseState::PushRepetition\28int\2c\20int\2c\20re2::StringPiece\20const&\2c\20bool\29,operator\20delete\28void*\29 +re2::Regexp::ParseState::PushRepetition\28int\2c\20int\2c\20re2::StringPiece\20const&\2c\20bool\29,re2::Regexp::ComputeSimple\28\29 +re2::Regexp::ParseState::PushRepetition\28int\2c\20int\2c\20re2::StringPiece\20const&\2c\20bool\29,re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20int\2c\20bool\29 +re2::Regexp::ParseState::PushRepetition\28int\2c\20int\2c\20re2::StringPiece\20const&\2c\20bool\29,re2::Regexp::Walker::~Walker\28\29 +re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20int\2c\20bool\29,operator\20delete\5b\5d\28void*\29 +re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20int\2c\20bool\29,operator\20delete\28void*\29 +re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20int\2c\20bool\29,std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29 +re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20int\2c\20bool\29,operator\20new\5b\5d\28unsigned\20long\29 +re2::Regexp::Walker::~Walker\28\29,operator\20delete\5b\5d\28void*\29 +re2::Regexp::Walker::~Walker\28\29,operator\20delete\28void*\29 +re2::Regexp::Walker::~Walker\28\29,std::__1::__deque_base\2c\20std::__1::allocator\20>\20>::~__deque_base\28\29 +std::__1::__deque_base\2c\20std::__1::allocator\20>\20>::~__deque_base\28\29,operator\20delete\28void*\29 +re2::Regexp::ParseState::DoLeftParen\28re2::StringPiece\20const&\29,operator\20new\28unsigned\20long\29 +re2::Regexp::ParseState::DoLeftParen\28re2::StringPiece\20const&\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::DoLeftParen\28re2::StringPiece\20const&\29,memcpy +re2::Regexp::ParseState::DoLeftParen\28re2::StringPiece\20const&\29,re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29 +re2::Regexp::ParseState::DoLeftParen\28re2::StringPiece\20const&\29,abort +re2::Regexp::ParseState::DoVerticalBar\28\29,re2::Regexp::ParseState::MaybeConcatString\28int\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::DoVerticalBar\28\29,operator\20new\28unsigned\20long\29 +re2::Regexp::ParseState::DoVerticalBar\28\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::DoVerticalBar\28\29,re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29 +re2::Regexp::ParseState::DoVerticalBar\28\29,re2::Regexp::ParseState::DoCollapse\28re2::RegexpOp\29 +re2::Regexp::ParseState::DoVerticalBar\28\29,re2::Regexp::Decref\28\29 +re2::Regexp::ParseState::DoCollapse\28re2::RegexpOp\29,operator\20new\28unsigned\20long\29 +re2::Regexp::ParseState::DoCollapse\28re2::RegexpOp\29,re2::Regexp::Incref\28\29 +re2::Regexp::ParseState::DoCollapse\28re2::RegexpOp\29,re2::Regexp::Decref\28\29 +re2::Regexp::ParseState::DoCollapse\28re2::RegexpOp\29,re2::CharClassBuilder::GetCharClass\28\29 +re2::Regexp::ParseState::DoCollapse\28re2::RegexpOp\29,std::__1::__tree\20>::destroy\28std::__1::__tree_node*\29 +re2::Regexp::ParseState::DoCollapse\28re2::RegexpOp\29,operator\20delete\28void*\29 +re2::Regexp::ParseState::DoCollapse\28re2::RegexpOp\29,re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29 +re2::Regexp::ParseState::DoCollapse\28re2::RegexpOp\29,re2::Regexp::ComputeSimple\28\29 +re2::Regexp::ParseState::DoCollapse\28re2::RegexpOp\29,abort +re2::Regexp::ParseState::DoRightParen\28\29,re2::Regexp::ParseState::DoVerticalBar\28\29 +re2::Regexp::ParseState::DoRightParen\28\29,re2::Regexp::Decref\28\29 +re2::Regexp::ParseState::DoRightParen\28\29,re2::Regexp::ParseState::DoCollapse\28re2::RegexpOp\29 +re2::Regexp::ParseState::DoRightParen\28\29,re2::CharClassBuilder::GetCharClass\28\29 +re2::Regexp::ParseState::DoRightParen\28\29,std::__1::__tree\20>::destroy\28std::__1::__tree_node*\29 +re2::Regexp::ParseState::DoRightParen\28\29,operator\20delete\28void*\29 +re2::Regexp::ParseState::DoRightParen\28\29,re2::Regexp::ComputeSimple\28\29 +re2::Regexp::ParseState::DoRightParen\28\29,re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29 +re2::Regexp::ParseState::DoFinish\28\29,re2::Regexp::ParseState::DoVerticalBar\28\29 +re2::Regexp::ParseState::DoFinish\28\29,re2::Regexp::Decref\28\29 +re2::Regexp::ParseState::DoFinish\28\29,re2::Regexp::ParseState::DoCollapse\28re2::RegexpOp\29 +re2::Regexp::ParseState::DoFinish\28\29,re2::CharClassBuilder::GetCharClass\28\29 +re2::Regexp::ParseState::DoFinish\28\29,std::__1::__tree\20>::destroy\28std::__1::__tree_node*\29 +re2::Regexp::ParseState::DoFinish\28\29,operator\20delete\28void*\29 +re2::Regexp::RemoveLeadingString\28re2::Regexp*\2c\20int\29,operator\20delete\5b\5d\28void*\29 +re2::Regexp::RemoveLeadingString\28re2::Regexp*\2c\20int\29,memmove +re2::Regexp::RemoveLeadingString\28re2::Regexp*\2c\20int\29,re2::Regexp::Decref\28\29 +re2::Regexp::RemoveLeadingString\28re2::Regexp*\2c\20int\29,re2::Regexp::Swap\28re2::Regexp*\29 +re2::Regexp::FactorAlternation\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29,void\20std::__1::vector\20>::__emplace_back_slow_path\28re2::Regexp**&\2c\20int&\29 +re2::Regexp::FactorAlternation\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::AlternateNoFactor\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::FactorAlternation\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::Concat\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::FactorAlternation\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::FactorAlternationImpl::Round2\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29 +re2::Regexp::FactorAlternation\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::FactorAlternationImpl::Round3\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29 +re2::Regexp::FactorAlternation\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29,operator\20delete\28void*\29 +re2::Regexp::FactorAlternation\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::FactorAlternationImpl::Round1\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29 +void\20std::__1::vector\20>::__emplace_back_slow_path\28re2::Regexp**&\2c\20int&\29,operator\20new\28unsigned\20long\29 +void\20std::__1::vector\20>::__emplace_back_slow_path\28re2::Regexp**&\2c\20int&\29,operator\20delete\28void*\29 +void\20std::__1::vector\20>::__emplace_back_slow_path\28re2::Regexp**&\2c\20int&\29,abort +re2::FactorAlternationImpl::Round2\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,re2::Regexp::Equal\28re2::Regexp*\2c\20re2::Regexp*\29 +re2::FactorAlternationImpl::Round2\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,re2::Regexp::Incref\28\29 +re2::FactorAlternationImpl::Round2\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,re2::Regexp::Decref\28\29 +re2::FactorAlternationImpl::Round2\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,memmove +re2::FactorAlternationImpl::Round2\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,operator\20new\28unsigned\20long\29 +re2::FactorAlternationImpl::Round2\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::FactorAlternationImpl::Round2\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,memcpy +re2::FactorAlternationImpl::Round2\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,operator\20delete\28void*\29 +re2::FactorAlternationImpl::Round2\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,abort +re2::FactorAlternationImpl::Round3\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,re2::CharClassBuilder::CharClassBuilder\28\29 +re2::FactorAlternationImpl::Round3\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,re2::CharClassBuilder::AddRange\28int\2c\20int\29 +re2::FactorAlternationImpl::Round3\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,re2::AddFoldedRange\28re2::CharClassBuilder*\2c\20int\2c\20int\2c\20int\29 +re2::FactorAlternationImpl::Round3\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,re2::Regexp::Decref\28\29 +re2::FactorAlternationImpl::Round3\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,re2::CharClassBuilder::GetCharClass\28\29 +re2::FactorAlternationImpl::Round3\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,re2::Regexp::NewCharClass\28re2::CharClass*\2c\20re2::Regexp::ParseFlags\29 +re2::FactorAlternationImpl::Round3\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,operator\20new\28unsigned\20long\29 +re2::FactorAlternationImpl::Round3\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,memcpy +re2::FactorAlternationImpl::Round3\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,operator\20delete\28void*\29 +re2::FactorAlternationImpl::Round3\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,std::__1::__tree\20>::destroy\28std::__1::__tree_node*\29 +re2::FactorAlternationImpl::Round3\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,abort +re2::FactorAlternationImpl::Round1\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,re2::Regexp::LiteralString\28int*\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::FactorAlternationImpl::Round1\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,re2::Regexp::RemoveLeadingString\28re2::Regexp*\2c\20int\29 +re2::FactorAlternationImpl::Round1\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,operator\20new\28unsigned\20long\29 +re2::FactorAlternationImpl::Round1\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,memcpy +re2::FactorAlternationImpl::Round1\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,operator\20delete\28void*\29 +re2::FactorAlternationImpl::Round1\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20std::__1::vector\20>*\29,abort +re2::AddFoldedRange\28re2::CharClassBuilder*\2c\20int\2c\20int\2c\20int\29,re2::CharClassBuilder::AddRange\28int\2c\20int\29 +re2::AddFoldedRange\28re2::CharClassBuilder*\2c\20int\2c\20int\2c\20int\29,re2::AddFoldedRange\28re2::CharClassBuilder*\2c\20int\2c\20int\2c\20int\29 +re2::CharClassBuilder::AddRangeFlags\28int\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::AddFoldedRange\28re2::CharClassBuilder*\2c\20int\2c\20int\2c\20int\29 +re2::CharClassBuilder::AddRangeFlags\28int\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::CharClassBuilder::AddRange\28int\2c\20int\29 +re2::ParseUnicodeGroup\28re2::StringPiece*\2c\20re2::Regexp::ParseFlags\2c\20re2::CharClassBuilder*\2c\20re2::RegexpStatus*\29,fullrune +re2::ParseUnicodeGroup\28re2::StringPiece*\2c\20re2::Regexp::ParseFlags\2c\20re2::CharClassBuilder*\2c\20re2::RegexpStatus*\29,chartorune +re2::ParseUnicodeGroup\28re2::StringPiece*\2c\20re2::Regexp::ParseFlags\2c\20re2::CharClassBuilder*\2c\20re2::RegexpStatus*\29,re2::StringPiece::find\28char\2c\20unsigned\20long\29\20const +re2::ParseUnicodeGroup\28re2::StringPiece*\2c\20re2::Regexp::ParseFlags\2c\20re2::CharClassBuilder*\2c\20re2::RegexpStatus*\29,re2::IsValidUTF8\28re2::StringPiece\20const&\2c\20re2::RegexpStatus*\29 +re2::ParseUnicodeGroup\28re2::StringPiece*\2c\20re2::Regexp::ParseFlags\2c\20re2::CharClassBuilder*\2c\20re2::RegexpStatus*\29,memcmp +re2::ParseUnicodeGroup\28re2::StringPiece*\2c\20re2::Regexp::ParseFlags\2c\20re2::CharClassBuilder*\2c\20re2::RegexpStatus*\29,strlen +re2::ParseUnicodeGroup\28re2::StringPiece*\2c\20re2::Regexp::ParseFlags\2c\20re2::CharClassBuilder*\2c\20re2::RegexpStatus*\29,re2::AddUGroup\28re2::CharClassBuilder*\2c\20re2::UGroup\20const*\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::IsValidUTF8\28re2::StringPiece\20const&\2c\20re2::RegexpStatus*\29,fullrune +re2::IsValidUTF8\28re2::StringPiece\20const&\2c\20re2::RegexpStatus*\29,chartorune +re2::AddUGroup\28re2::CharClassBuilder*\2c\20re2::UGroup\20const*\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::CharClassBuilder::AddRangeFlags\28int\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::AddUGroup\28re2::CharClassBuilder*\2c\20re2::UGroup\20const*\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::CharClassBuilder::CharClassBuilder\28\29 +re2::AddUGroup\28re2::CharClassBuilder*\2c\20re2::UGroup\20const*\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::CharClassBuilder::AddRange\28int\2c\20int\29 +re2::AddUGroup\28re2::CharClassBuilder*\2c\20re2::UGroup\20const*\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::CharClassBuilder::Negate\28\29 +re2::AddUGroup\28re2::CharClassBuilder*\2c\20re2::UGroup\20const*\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::CharClassBuilder::AddCharClass\28re2::CharClassBuilder*\29 +re2::AddUGroup\28re2::CharClassBuilder*\2c\20re2::UGroup\20const*\2c\20int\2c\20re2::Regexp::ParseFlags\29,std::__1::__tree\20>::destroy\28std::__1::__tree_node*\29 +re2::StringPieceToRune\28int*\2c\20re2::StringPiece*\2c\20re2::RegexpStatus*\29,fullrune +re2::StringPieceToRune\28int*\2c\20re2::StringPiece*\2c\20re2::RegexpStatus*\29,chartorune +re2::Regexp::ParseState::ParseCCCharacter\28re2::StringPiece*\2c\20int*\2c\20re2::StringPiece\20const&\2c\20re2::RegexpStatus*\29,re2::ParseEscape\28re2::StringPiece*\2c\20int*\2c\20re2::RegexpStatus*\2c\20int\29 +re2::Regexp::ParseState::ParseCCCharacter\28re2::StringPiece*\2c\20int*\2c\20re2::StringPiece\20const&\2c\20re2::RegexpStatus*\29,fullrune +re2::Regexp::ParseState::ParseCCCharacter\28re2::StringPiece*\2c\20int*\2c\20re2::StringPiece\20const&\2c\20re2::RegexpStatus*\29,chartorune +re2::ParseEscape\28re2::StringPiece*\2c\20int*\2c\20re2::RegexpStatus*\2c\20int\29,fullrune +re2::ParseEscape\28re2::StringPiece*\2c\20int*\2c\20re2::RegexpStatus*\2c\20int\29,chartorune +re2::ParseEscape\28re2::StringPiece*\2c\20int*\2c\20re2::RegexpStatus*\2c\20int\29,isalpha +re2::ParseEscape\28re2::StringPiece*\2c\20int*\2c\20re2::RegexpStatus*\2c\20int\29,re2::StringPieceToRune\28int*\2c\20re2::StringPiece*\2c\20re2::RegexpStatus*\29 +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,operator\20new\28unsigned\20long\29 +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,re2::CharClassBuilder::CharClassBuilder\28\29 +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,re2::CharClassBuilder::AddRange\28int\2c\20int\29 +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,fullrune +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,chartorune +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,re2::ParseUnicodeGroup\28re2::StringPiece*\2c\20re2::Regexp::ParseFlags\2c\20re2::CharClassBuilder*\2c\20re2::RegexpStatus*\29 +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,strlen +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,memcmp +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::ParseCCCharacter\28re2::StringPiece*\2c\20int*\2c\20re2::StringPiece\20const&\2c\20re2::RegexpStatus*\29 +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,re2::CharClassBuilder::AddRangeFlags\28int\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,re2::CharClassBuilder::Negate\28\29 +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,re2::AddUGroup\28re2::CharClassBuilder*\2c\20re2::UGroup\20const*\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29,re2::Regexp::Decref\28\29 +re2::Regexp::ParseState::ParsePerlFlags\28re2::StringPiece*\29,re2::StringPiece::find\28char\2c\20unsigned\20long\29\20const +re2::Regexp::ParseState::ParsePerlFlags\28re2::StringPiece*\29,re2::IsValidUTF8\28re2::StringPiece\20const&\2c\20re2::RegexpStatus*\29 +re2::Regexp::ParseState::ParsePerlFlags\28re2::StringPiece*\29,re2::IsValidCaptureName\28re2::StringPiece\20const&\29 +re2::Regexp::ParseState::ParsePerlFlags\28re2::StringPiece*\29,re2::Regexp::ParseState::DoLeftParen\28re2::StringPiece\20const&\29 +re2::Regexp::ParseState::ParsePerlFlags\28re2::StringPiece*\29,fullrune +re2::Regexp::ParseState::ParsePerlFlags\28re2::StringPiece*\29,chartorune +re2::Regexp::ParseState::ParsePerlFlags\28re2::StringPiece*\29,operator\20new\28unsigned\20long\29 +re2::Regexp::ParseState::ParsePerlFlags\28re2::StringPiece*\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ParseState::ParsePerlFlags\28re2::StringPiece*\29,re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,operator\20new\28unsigned\20long\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,runetochar +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\2c\20unsigned\20long\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,operator\20delete\28void*\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,fullrune +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,chartorune +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::PushLiteral\28int\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::DoFinish\28\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::ParsePerlFlags\28re2::StringPiece*\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::PushRegexp\28re2::Regexp*\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::DoLeftParen\28re2::StringPiece\20const&\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::DoVerticalBar\28\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::DoRightParen\28\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::PushDot\28\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::ParseCharClass\28re2::StringPiece*\2c\20re2::Regexp**\2c\20re2::RegexpStatus*\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::PushRepeatOp\28re2::RegexpOp\2c\20re2::StringPiece\20const&\2c\20bool\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::ParseInteger\28re2::StringPiece*\2c\20int*\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::PushSimpleOp\28re2::RegexpOp\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::CharClassBuilder::CharClassBuilder\28\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::ParseUnicodeGroup\28re2::StringPiece*\2c\20re2::Regexp::ParseFlags\2c\20re2::CharClassBuilder*\2c\20re2::RegexpStatus*\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::Decref\28\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,strlen +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::ParseEscape\28re2::StringPiece*\2c\20int*\2c\20re2::RegexpStatus*\2c\20int\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::AddUGroup\28re2::CharClassBuilder*\2c\20re2::UGroup\20const*\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29,re2::Regexp::ParseState::PushRepetition\28int\2c\20int\2c\20re2::StringPiece\20const&\2c\20bool\29 +re2::RepetitionWalker::~RepetitionWalker\28\29,re2::Regexp::Walker::~Walker\28\29 +re2::RepetitionWalker::~RepetitionWalker\28\29,operator\20delete\28void*\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,memmove +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,operator\20new\28unsigned\20long\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,operator\20delete\28void*\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,abort +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29,memmove +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29,operator\20delete\28void*\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29,abort +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29,memmove +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29,operator\20delete\28void*\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29,abort +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29,memmove +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29,operator\20delete\28void*\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29,abort +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29,memmove +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29,operator\20delete\28void*\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29,abort +re2::Prog::Inst::InitAlt\28unsigned\20int\2c\20unsigned\20int\29,opa_abort +re2::Prog::Inst::InitByteRange\28int\2c\20int\2c\20int\2c\20unsigned\20int\29,opa_abort +re2::Prog::Inst::InitCapture\28int\2c\20unsigned\20int\29,opa_abort +re2::Prog::Inst::InitEmptyWidth\28re2::EmptyOp\2c\20unsigned\20int\29,opa_abort +re2::Prog::Inst::InitMatch\28int\29,opa_abort +re2::Prog::Inst::InitNop\28unsigned\20int\29,opa_abort +re2::Prog::Inst::InitFail\28\29,opa_abort +re2::Prog::~Prog\28\29,re2::Prog::DeleteDFA\28re2::DFA*\29 +re2::Prog::~Prog\28\29,operator\20delete\28void*\29 +re2::Prog::Optimize\28\29,operator\20new\28unsigned\20long\29 +re2::Prog::Optimize\28\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +re2::Prog::Optimize\28\29,opa_abort +re2::Prog::Optimize\28\29,abort +re2::Prog::Optimize\28\29,operator\20delete\28void*\29 +re2::ByteMapBuilder::Mark\28int\2c\20int\29,opa_abort +re2::ByteMapBuilder::Mark\28int\2c\20int\29,operator\20new\28unsigned\20long\29 +re2::ByteMapBuilder::Mark\28int\2c\20int\29,memcpy +re2::ByteMapBuilder::Mark\28int\2c\20int\29,operator\20delete\28void*\29 +re2::ByteMapBuilder::Mark\28int\2c\20int\29,abort +re2::ByteMapBuilder::Merge\28\29,opa_abort +re2::ByteMapBuilder::Merge\28\29,operator\20new\28unsigned\20long\29 +re2::ByteMapBuilder::Merge\28\29,memcpy +re2::ByteMapBuilder::Merge\28\29,operator\20delete\28void*\29 +re2::ByteMapBuilder::Merge\28\29,abort +re2::ByteMapBuilder::Recolor\28int\29,operator\20new\28unsigned\20long\29 +re2::ByteMapBuilder::Recolor\28int\29,memcpy +re2::ByteMapBuilder::Recolor\28int\29,operator\20delete\28void*\29 +re2::ByteMapBuilder::Recolor\28int\29,abort +re2::ByteMapBuilder::Build\28unsigned\20char*\2c\20int*\29,opa_abort +re2::ByteMapBuilder::Build\28unsigned\20char*\2c\20int*\29,re2::ByteMapBuilder::Recolor\28int\29 +re2::ByteMapBuilder::Build\28unsigned\20char*\2c\20int*\29,memset +re2::Prog::ComputeByteMap\28\29,re2::ByteMapBuilder::Mark\28int\2c\20int\29 +re2::Prog::ComputeByteMap\28\29,opa_abort +re2::Prog::ComputeByteMap\28\29,operator\20new\28unsigned\20long\29 +re2::Prog::ComputeByteMap\28\29,memcpy +re2::Prog::ComputeByteMap\28\29,operator\20delete\28void*\29 +re2::Prog::ComputeByteMap\28\29,re2::ByteMapBuilder::Merge\28\29 +re2::Prog::ComputeByteMap\28\29,abort +re2::Prog::ComputeByteMap\28\29,re2::ByteMapBuilder::Build\28unsigned\20char*\2c\20int*\29 +re2::Prog::Flatten\28\29,operator\20new\28unsigned\20long\29 +re2::Prog::Flatten\28\29,re2::Prog::MarkSuccessors\28re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29 +re2::Prog::Flatten\28\29,memmove +re2::Prog::Flatten\28\29,void\20std::__1::__sort::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\2c\20re2::SparseArray::IndexValue*>\28re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20bool\20\28*&\29\28re2::SparseArray::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\29 +re2::Prog::Flatten\28\29,re2::Prog::MarkDominator\28int\2c\20re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29 +re2::Prog::Flatten\28\29,memset +re2::Prog::Flatten\28\29,re2::Prog::EmitList\28int\2c\20re2::SparseArray*\2c\20std::__1::vector\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29 +re2::Prog::Flatten\28\29,re2::Prog::ComputeHints\28std::__1::vector\20>*\2c\20int\2c\20int\29 +re2::Prog::Flatten\28\29,opa_abort +re2::Prog::Flatten\28\29,operator\20delete\28void*\29 +re2::Prog::Flatten\28\29,abort +re2::Prog::MarkSuccessors\28re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,re2::SparseArray::SetInternal\28bool\2c\20int\2c\20int\20const&\29 +re2::Prog::MarkSuccessors\28re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,opa_abort +re2::Prog::MarkSuccessors\28re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,operator\20new\28unsigned\20long\29 +re2::Prog::MarkSuccessors\28re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,operator\20delete\28void*\29 +re2::Prog::MarkSuccessors\28re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +re2::Prog::MarkSuccessors\28re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,memcpy +re2::Prog::MarkSuccessors\28re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,abort +void\20std::__1::__sort::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\2c\20re2::SparseArray::IndexValue*>\28re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20bool\20\28*&\29\28re2::SparseArray::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\29,unsigned\20int\20std::__1::__sort4::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\2c\20re2::SparseArray::IndexValue*>\28re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20bool\20\28*&\29\28re2::SparseArray::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\29 +void\20std::__1::__sort::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\2c\20re2::SparseArray::IndexValue*>\28re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20bool\20\28*&\29\28re2::SparseArray::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\29,void\20std::__1::__insertion_sort_3::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\2c\20re2::SparseArray::IndexValue*>\28re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20bool\20\28*&\29\28re2::SparseArray::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\29 +void\20std::__1::__sort::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\2c\20re2::SparseArray::IndexValue*>\28re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20bool\20\28*&\29\28re2::SparseArray::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\29,bool\20std::__1::__insertion_sort_incomplete::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\2c\20re2::SparseArray::IndexValue*>\28re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20bool\20\28*&\29\28re2::SparseArray::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\29 +void\20std::__1::__sort::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\2c\20re2::SparseArray::IndexValue*>\28re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20bool\20\28*&\29\28re2::SparseArray::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\29,void\20std::__1::__sort::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\2c\20re2::SparseArray::IndexValue*>\28re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20bool\20\28*&\29\28re2::SparseArray::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\29 +re2::Prog::MarkDominator\28int\2c\20re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,operator\20new\28unsigned\20long\29 +re2::Prog::MarkDominator\28int\2c\20re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,operator\20delete\28void*\29 +re2::Prog::MarkDominator\28int\2c\20re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,opa_abort +re2::Prog::MarkDominator\28int\2c\20re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +re2::Prog::MarkDominator\28int\2c\20re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,memcpy +re2::Prog::MarkDominator\28int\2c\20re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,re2::SparseArray::create_index\28int\29 +re2::Prog::MarkDominator\28int\2c\20re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,re2::SparseArray::SetExistingInternal\28int\2c\20int\20const&\29 +re2::Prog::MarkDominator\28int\2c\20re2::SparseArray*\2c\20re2::SparseArray*\2c\20std::__1::vector\20>\2c\20std::__1::allocator\20>\20>\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,abort +re2::Prog::EmitList\28int\2c\20re2::SparseArray*\2c\20std::__1::vector\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,operator\20new\28unsigned\20long\29 +re2::Prog::EmitList\28int\2c\20re2::SparseArray*\2c\20std::__1::vector\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,operator\20delete\28void*\29 +re2::Prog::EmitList\28int\2c\20re2::SparseArray*\2c\20std::__1::vector\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,opa_abort +re2::Prog::EmitList\28int\2c\20re2::SparseArray*\2c\20std::__1::vector\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,re2::SparseSetT::InsertInternal\28bool\2c\20int\29 +re2::Prog::EmitList\28int\2c\20re2::SparseArray*\2c\20std::__1::vector\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,memcpy +re2::Prog::EmitList\28int\2c\20re2::SparseArray*\2c\20std::__1::vector\20>*\2c\20re2::SparseSetT*\2c\20std::__1::vector\20>*\29,abort +re2::Prog::ComputeHints\28std::__1::vector\20>*\2c\20int\2c\20int\29,opa_abort +re2::Prog::ComputeHints\28std::__1::vector\20>*\2c\20int\2c\20int\29,re2::Prog::ComputeHints\28std::__1::vector\20>*\2c\20int\2c\20int\29::$_1::operator\28\29\28int\2c\20int\29\20const +re2::Prog::ComputeHints\28std::__1::vector\20>*\2c\20int\2c\20int\29::$_1::operator\28\29\28int\2c\20int\29\20const,opa_abort +re2::Prog::PrefixAccel_FrontAndBack\28void\20const*\2c\20unsigned\20long\29,opa_abort +re2::Prog::PrefixAccel_FrontAndBack\28void\20const*\2c\20unsigned\20long\29,memchr +bool\20std::__1::__insertion_sort_incomplete::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\2c\20re2::SparseArray::IndexValue*>\28re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20bool\20\28*&\29\28re2::SparseArray::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\29,unsigned\20int\20std::__1::__sort4::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\2c\20re2::SparseArray::IndexValue*>\28re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20re2::SparseArray::IndexValue*\2c\20bool\20\28*&\29\28re2::SparseArray::IndexValue\20const&\2c\20re2::SparseArray::IndexValue\20const&\29\29 +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,std::__1::__call_once\28unsigned\20long\20volatile&\2c\20void*\2c\20void\20\28*\29\28void*\29\29 +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,std::__1::basic_string\2c\20std::__1::allocator\20>::assign\28char\20const*\2c\20unsigned\20long\29 +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,re2::Regexp::Parse\28re2::StringPiece\20const&\2c\20re2::Regexp::ParseFlags\2c\20re2::RegexpStatus*\29 +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,operator\20new\28unsigned\20long\29 +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,re2::RegexpStatus::Text\28\29\20const +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,memcpy +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,operator\20delete\28void*\29 +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,re2::Regexp::RequiredPrefix\28std::__1::basic_string\2c\20std::__1::allocator\20>*\2c\20bool*\2c\20re2::Regexp**\29 +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,re2::Regexp::Incref\28\29 +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,re2::Regexp::CompileToProg\28long\20long\29 +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,re2::Regexp::NumCaptures\28\29 +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,re2::Prog::IsOnePass\28\29 +re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,abort +void\20std::__1::__call_once_proxy\20>\28void*\29,operator\20new\28unsigned\20long\29 +re2::RE2::RE2\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29,re2::RE2::Init\28re2::StringPiece\20const&\2c\20re2::RE2::Options\20const&\29 +re2::RE2::ReverseProg\28\29\20const,std::__1::__call_once\28unsigned\20long\20volatile&\2c\20void*\2c\20void\20\28*\29\28void*\29\29 +void\20std::__1::__call_once_proxy\20>\28void*\29,re2::Regexp::CompileToReverseProg\28long\20long\29 +re2::RE2::~RE2\28\29,re2::Regexp::Decref\28\29 +re2::RE2::~RE2\28\29,re2::Prog::~Prog\28\29 +re2::RE2::~RE2\28\29,operator\20delete\28void*\29 +re2::RE2::~RE2\28\29,std::__1::__tree\2c\20std::__1::allocator\20>\2c\20int>\2c\20std::__1::__map_value_compare\2c\20std::__1::allocator\20>\2c\20std::__1::__value_type\2c\20std::__1::allocator\20>\2c\20int>\2c\20std::__1::less\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20int>\20>\20>::destroy\28std::__1::__tree_node\2c\20std::__1::allocator\20>\2c\20int>\2c\20void*>*\29 +re2::RE2::~RE2\28\29,std::__1::__tree\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__map_value_compare\2c\20std::__1::allocator\20>\20>\2c\20std::__1::less\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::destroy\28std::__1::__tree_node\2c\20std::__1::allocator\20>\20>\2c\20void*>*\29 +std::__1::__tree\2c\20std::__1::allocator\20>\2c\20int>\2c\20std::__1::__map_value_compare\2c\20std::__1::allocator\20>\2c\20std::__1::__value_type\2c\20std::__1::allocator\20>\2c\20int>\2c\20std::__1::less\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20int>\20>\20>::destroy\28std::__1::__tree_node\2c\20std::__1::allocator\20>\2c\20int>\2c\20void*>*\29,std::__1::__tree\2c\20std::__1::allocator\20>\2c\20int>\2c\20std::__1::__map_value_compare\2c\20std::__1::allocator\20>\2c\20std::__1::__value_type\2c\20std::__1::allocator\20>\2c\20int>\2c\20std::__1::less\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20int>\20>\20>::destroy\28std::__1::__tree_node\2c\20std::__1::allocator\20>\2c\20int>\2c\20void*>*\29 +std::__1::__tree\2c\20std::__1::allocator\20>\2c\20int>\2c\20std::__1::__map_value_compare\2c\20std::__1::allocator\20>\2c\20std::__1::__value_type\2c\20std::__1::allocator\20>\2c\20int>\2c\20std::__1::less\2c\20std::__1::allocator\20>\20>\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\2c\20int>\20>\20>::destroy\28std::__1::__tree_node\2c\20std::__1::allocator\20>\2c\20int>\2c\20void*>*\29,operator\20delete\28void*\29 +std::__1::__tree\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__map_value_compare\2c\20std::__1::allocator\20>\20>\2c\20std::__1::less\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::destroy\28std::__1::__tree_node\2c\20std::__1::allocator\20>\20>\2c\20void*>*\29,std::__1::__tree\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__map_value_compare\2c\20std::__1::allocator\20>\20>\2c\20std::__1::less\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::destroy\28std::__1::__tree_node\2c\20std::__1::allocator\20>\20>\2c\20void*>*\29 +std::__1::__tree\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__map_value_compare\2c\20std::__1::allocator\20>\20>\2c\20std::__1::less\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::destroy\28std::__1::__tree_node\2c\20std::__1::allocator\20>\20>\2c\20void*>*\29,operator\20delete\28void*\29 +re2::RE2::DoMatch\28re2::StringPiece\20const&\2c\20re2::RE2::Anchor\2c\20unsigned\20long*\2c\20re2::RE2::Arg\20const*\20const*\2c\20int\29\20const,memset +re2::RE2::DoMatch\28re2::StringPiece\20const&\2c\20re2::RE2::Anchor\2c\20unsigned\20long*\2c\20re2::RE2::Arg\20const*\20const*\2c\20int\29\20const,operator\20new\5b\5d\28unsigned\20long\29 +re2::RE2::DoMatch\28re2::StringPiece\20const&\2c\20re2::RE2::Anchor\2c\20unsigned\20long*\2c\20re2::RE2::Arg\20const*\20const*\2c\20int\29\20const,re2::RE2::Match\28re2::StringPiece\20const&\2c\20unsigned\20long\2c\20unsigned\20long\2c\20re2::RE2::Anchor\2c\20re2::StringPiece*\2c\20int\29\20const +re2::RE2::DoMatch\28re2::StringPiece\20const&\2c\20re2::RE2::Anchor\2c\20unsigned\20long*\2c\20re2::RE2::Arg\20const*\20const*\2c\20int\29\20const,operator\20delete\5b\5d\28void*\29 +re2::RE2::Match\28re2::StringPiece\20const&\2c\20unsigned\20long\2c\20unsigned\20long\2c\20re2::RE2::Anchor\2c\20re2::StringPiece*\2c\20int\29\20const,memcmp +re2::RE2::Match\28re2::StringPiece\20const&\2c\20unsigned\20long\2c\20unsigned\20long\2c\20re2::RE2::Anchor\2c\20re2::StringPiece*\2c\20int\29\20const,re2::RE2::ReverseProg\28\29\20const +re2::RE2::Match\28re2::StringPiece\20const&\2c\20unsigned\20long\2c\20unsigned\20long\2c\20re2::RE2::Anchor\2c\20re2::StringPiece*\2c\20int\29\20const,re2::Prog::SearchDFA\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20bool*\2c\20re2::SparseSetT*\29 +re2::RE2::Match\28re2::StringPiece\20const&\2c\20unsigned\20long\2c\20unsigned\20long\2c\20re2::RE2::Anchor\2c\20re2::StringPiece*\2c\20int\29\20const,re2::Prog::SearchOnePass\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20int\29 +re2::RE2::Match\28re2::StringPiece\20const&\2c\20unsigned\20long\2c\20unsigned\20long\2c\20re2::RE2::Anchor\2c\20re2::StringPiece*\2c\20int\29\20const,re2::Prog::SearchBitState\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20int\29 +re2::RE2::Match\28re2::StringPiece\20const&\2c\20unsigned\20long\2c\20unsigned\20long\2c\20re2::RE2::Anchor\2c\20re2::StringPiece*\2c\20int\29\20const,re2::Prog::SearchNFA\28re2::StringPiece\20const&\2c\20re2::StringPiece\20const&\2c\20re2::Prog::Anchor\2c\20re2::Prog::MatchKind\2c\20re2::StringPiece*\2c\20int\29 +re2::RE2::Match\28re2::StringPiece\20const&\2c\20unsigned\20long\2c\20unsigned\20long\2c\20re2::RE2::Anchor\2c\20re2::StringPiece*\2c\20int\29\20const,memset +re2::RE2::PartialMatchN\28re2::StringPiece\20const&\2c\20re2::RE2\20const&\2c\20re2::RE2::Arg\20const*\20const*\2c\20int\29,re2::RE2::DoMatch\28re2::StringPiece\20const&\2c\20re2::RE2::Anchor\2c\20unsigned\20long*\2c\20re2::RE2::Arg\20const*\20const*\2c\20int\29\20const +re2::Regexp::~Regexp\28\29,operator\20delete\28void*\29 +re2::Regexp::~Regexp\28\29,operator\20delete\5b\5d\28void*\29 +re2::Regexp::~Regexp\28\29,std::__1::__tree\20>::destroy\28std::__1::__tree_node*\29 +re2::Regexp::Incref\28\29,std::__1::__call_once\28unsigned\20long\20volatile&\2c\20void*\2c\20void\20\28*\29\28void*\29\29 +re2::Regexp::Incref\28\29,operator\20new\28unsigned\20long\29 +re2::Regexp::Incref\28\29,void\20std::__1::__tree_balance_after_insert*>\28std::__1::__tree_node_base*\2c\20std::__1::__tree_node_base*\29 +void\20std::__1::__call_once_proxy\20>\28void*\29,operator\20new\28unsigned\20long\29 +re2::Regexp::Decref\28\29,operator\20new\28unsigned\20long\29 +re2::Regexp::Decref\28\29,void\20std::__1::__tree_balance_after_insert*>\28std::__1::__tree_node_base*\2c\20std::__1::__tree_node_base*\29 +re2::Regexp::Decref\28\29,void\20std::__1::__tree_remove*>\28std::__1::__tree_node_base*\2c\20std::__1::__tree_node_base*\29 +re2::Regexp::Decref\28\29,operator\20delete\28void*\29 +re2::Regexp::Decref\28\29,re2::Regexp::Destroy\28\29 +re2::Regexp::Destroy\28\29,re2::Regexp::~Regexp\28\29 +re2::Regexp::Destroy\28\29,operator\20delete\28void*\29 +re2::Regexp::Destroy\28\29,re2::Regexp::Decref\28\29 +re2::Regexp::Destroy\28\29,operator\20delete\5b\5d\28void*\29 +re2::Regexp::AddRuneToString\28int\29,opa_abort +re2::Regexp::AddRuneToString\28int\29,operator\20new\5b\5d\28unsigned\20long\29 +re2::Regexp::AddRuneToString\28int\29,operator\20delete\5b\5d\28void*\29 +re2::Regexp::StarPlusOrQuest\28re2::RegexpOp\2c\20re2::Regexp*\2c\20re2::Regexp::ParseFlags\29,operator\20new\28unsigned\20long\29 +re2::Regexp::StarPlusOrQuest\28re2::RegexpOp\2c\20re2::Regexp*\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::Incref\28\29 +re2::Regexp::StarPlusOrQuest\28re2::RegexpOp\2c\20re2::Regexp*\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::Decref\28\29 +re2::Regexp::Plus\28re2::Regexp*\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::StarPlusOrQuest\28re2::RegexpOp\2c\20re2::Regexp*\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::Star\28re2::Regexp*\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::StarPlusOrQuest\28re2::RegexpOp\2c\20re2::Regexp*\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::Quest\28re2::Regexp*\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::StarPlusOrQuest\28re2::RegexpOp\2c\20re2::Regexp*\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29,operator\20new\28unsigned\20long\29 +re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29,memcpy +re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29,re2::Regexp::FactorAlternation\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29,operator\20new\5b\5d\28unsigned\20long\29 +re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29,re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29 +re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29,opa_abort +re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29,operator\20delete\28void*\29 +re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29,abort +re2::Regexp::Concat\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29 +re2::Regexp::AlternateNoFactor\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29 +re2::Regexp::Capture\28re2::Regexp*\2c\20re2::Regexp::ParseFlags\2c\20int\29,operator\20new\28unsigned\20long\29 +re2::Regexp::Repeat\28re2::Regexp*\2c\20re2::Regexp::ParseFlags\2c\20int\2c\20int\29,operator\20new\28unsigned\20long\29 +re2::Regexp::LiteralString\28int*\2c\20int\2c\20re2::Regexp::ParseFlags\29,operator\20new\28unsigned\20long\29 +re2::Regexp::LiteralString\28int*\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::AddRuneToString\28int\29 +re2::Regexp::NewCharClass\28re2::CharClass*\2c\20re2::Regexp::ParseFlags\29,operator\20new\28unsigned\20long\29 +re2::Regexp::Equal\28re2::Regexp*\2c\20re2::Regexp*\29,re2::TopEqual\28re2::Regexp*\2c\20re2::Regexp*\29 +re2::Regexp::Equal\28re2::Regexp*\2c\20re2::Regexp*\29,operator\20new\28unsigned\20long\29 +re2::Regexp::Equal\28re2::Regexp*\2c\20re2::Regexp*\29,memcpy +re2::Regexp::Equal\28re2::Regexp*\2c\20re2::Regexp*\29,operator\20delete\28void*\29 +re2::Regexp::Equal\28re2::Regexp*\2c\20re2::Regexp*\29,opa_abort +re2::Regexp::Equal\28re2::Regexp*\2c\20re2::Regexp*\29,std::__1::vector\20>::__append\28unsigned\20long\29 +re2::Regexp::Equal\28re2::Regexp*\2c\20re2::Regexp*\29,abort +re2::TopEqual\28re2::Regexp*\2c\20re2::Regexp*\29,memcmp +std::__1::vector\20>::__append\28unsigned\20long\29,memset +std::__1::vector\20>::__append\28unsigned\20long\29,operator\20new\28unsigned\20long\29 +std::__1::vector\20>::__append\28unsigned\20long\29,memcpy +std::__1::vector\20>::__append\28unsigned\20long\29,operator\20delete\28void*\29 +std::__1::vector\20>::__append\28unsigned\20long\29,abort +re2::RegexpStatus::Text\28\29\20const,strlen +re2::RegexpStatus::Text\28\29\20const,operator\20new\28unsigned\20long\29 +re2::RegexpStatus::Text\28\29\20const,memcpy +re2::RegexpStatus::Text\28\29\20const,std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\2c\20unsigned\20long\29 +re2::RegexpStatus::Text\28\29\20const,operator\20delete\28void*\29 +re2::RegexpStatus::Text\28\29\20const,std::__1::basic_string\2c\20std::__1::allocator\20>::append\28char\20const*\29 +re2::RegexpStatus::Text\28\29\20const,abort +re2::Regexp::NumCaptures\28\29,re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20int\2c\20bool\29 +re2::Regexp::NumCaptures\28\29,re2::Regexp::Walker::~Walker\28\29 +re2::ConvertRunesToBytes\28bool\2c\20int*\2c\20int\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,std::__1::basic_string\2c\20std::__1::allocator\20>::resize\28unsigned\20long\2c\20char\29 +re2::ConvertRunesToBytes\28bool\2c\20int*\2c\20int\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,runetochar +re2::ConvertRunesToBytes\28bool\2c\20int*\2c\20int\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,operator\20new\28unsigned\20long\29 +re2::ConvertRunesToBytes\28bool\2c\20int*\2c\20int\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,memcpy +re2::ConvertRunesToBytes\28bool\2c\20int*\2c\20int\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29,operator\20delete\28void*\29 +re2::Regexp::RequiredPrefix\28std::__1::basic_string\2c\20std::__1::allocator\20>*\2c\20bool*\2c\20re2::Regexp**\29,re2::Regexp::Incref\28\29 +re2::Regexp::RequiredPrefix\28std::__1::basic_string\2c\20std::__1::allocator\20>*\2c\20bool*\2c\20re2::Regexp**\29,re2::Regexp::ConcatOrAlternate\28re2::RegexpOp\2c\20re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\2c\20bool\29 +re2::Regexp::RequiredPrefix\28std::__1::basic_string\2c\20std::__1::allocator\20>*\2c\20bool*\2c\20re2::Regexp**\29,operator\20new\28unsigned\20long\29 +re2::Regexp::RequiredPrefix\28std::__1::basic_string\2c\20std::__1::allocator\20>*\2c\20bool*\2c\20re2::Regexp**\29,re2::ConvertRunesToBytes\28bool\2c\20int*\2c\20int\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29 +re2::Regexp::RequiredPrefixForAccel\28std::__1::basic_string\2c\20std::__1::allocator\20>*\2c\20bool*\29,re2::ConvertRunesToBytes\28bool\2c\20int*\2c\20int\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29 +re2::CharClassBuilder::AddRange\28int\2c\20int\29,void\20std::__1::__tree_remove*>\28std::__1::__tree_node_base*\2c\20std::__1::__tree_node_base*\29 +re2::CharClassBuilder::AddRange\28int\2c\20int\29,operator\20delete\28void*\29 +re2::CharClassBuilder::AddRange\28int\2c\20int\29,operator\20new\28unsigned\20long\29 +re2::CharClassBuilder::AddRange\28int\2c\20int\29,void\20std::__1::__tree_balance_after_insert*>\28std::__1::__tree_node_base*\2c\20std::__1::__tree_node_base*\29 +re2::CharClassBuilder::AddCharClass\28re2::CharClassBuilder*\29,re2::CharClassBuilder::AddRange\28int\2c\20int\29 +re2::CharClassBuilder::RemoveAbove\28int\29,void\20std::__1::__tree_remove*>\28std::__1::__tree_node_base*\2c\20std::__1::__tree_node_base*\29 +re2::CharClassBuilder::RemoveAbove\28int\29,operator\20delete\28void*\29 +re2::CharClassBuilder::RemoveAbove\28int\29,operator\20new\28unsigned\20long\29 +re2::CharClassBuilder::RemoveAbove\28int\29,void\20std::__1::__tree_balance_after_insert*>\28std::__1::__tree_node_base*\2c\20std::__1::__tree_node_base*\29 +re2::CharClassBuilder::Negate\28\29,operator\20new\28unsigned\20long\29 +re2::CharClassBuilder::Negate\28\29,memcpy +re2::CharClassBuilder::Negate\28\29,operator\20delete\28void*\29 +re2::CharClassBuilder::Negate\28\29,std::__1::__tree\20>::destroy\28std::__1::__tree_node*\29 +re2::CharClassBuilder::Negate\28\29,void\20std::__1::__tree_balance_after_insert*>\28std::__1::__tree_node_base*\2c\20std::__1::__tree_node_base*\29 +re2::CharClassBuilder::Negate\28\29,abort +re2::CharClassBuilder::GetCharClass\28\29,operator\20new\5b\5d\28unsigned\20long\29 +re2::CharClassBuilder::GetCharClass\28\29,opa_abort +re2::NumCapturesWalker::~NumCapturesWalker\28\29,re2::Regexp::Walker::~Walker\28\29 +re2::NumCapturesWalker::~NumCapturesWalker\28\29,operator\20delete\28void*\29 +re2::Regexp::Simplify\28\29,re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20re2::Regexp*\2c\20bool\29 +re2::Regexp::Simplify\28\29,re2::Regexp::Decref\28\29 +re2::Regexp::Simplify\28\29,re2::Regexp::Walker::~Walker\28\29 +re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20re2::Regexp*\2c\20bool\29,operator\20delete\5b\5d\28void*\29 +re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20re2::Regexp*\2c\20bool\29,operator\20delete\28void*\29 +re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20re2::Regexp*\2c\20bool\29,std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29 +re2::Regexp::Walker::WalkInternal\28re2::Regexp*\2c\20re2::Regexp*\2c\20bool\29,operator\20new\5b\5d\28unsigned\20long\29 +re2::Regexp::Walker::~Walker\28\29,operator\20delete\5b\5d\28void*\29 +re2::Regexp::Walker::~Walker\28\29,operator\20delete\28void*\29 +re2::Regexp::Walker::~Walker\28\29,std::__1::__deque_base\2c\20std::__1::allocator\20>\20>::~__deque_base\28\29 +re2::CoalesceWalker::Copy\28re2::Regexp*\29,re2::Regexp::Incref\28\29 +re2::CoalesceWalker::ShortVisit\28re2::Regexp*\2c\20re2::Regexp*\29,re2::Regexp::Incref\28\29 +re2::CoalesceWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,re2::Regexp::Incref\28\29 +re2::CoalesceWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,re2::CoalesceWalker::CanCoalesce\28re2::Regexp*\2c\20re2::Regexp*\29 +re2::CoalesceWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,re2::Regexp::Decref\28\29 +re2::CoalesceWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,re2::CoalesceWalker::DoCoalesce\28re2::Regexp**\2c\20re2::Regexp**\29 +re2::CoalesceWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,operator\20new\28unsigned\20long\29 +re2::CoalesceWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::CoalesceWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,opa_abort +re2::CoalesceWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,operator\20new\5b\5d\28unsigned\20long\29 +re2::CoalesceWalker::CanCoalesce\28re2::Regexp*\2c\20re2::Regexp*\29,re2::Regexp::Equal\28re2::Regexp*\2c\20re2::Regexp*\29 +re2::CoalesceWalker::DoCoalesce\28re2::Regexp**\2c\20re2::Regexp**\29,re2::Regexp::Incref\28\29 +re2::CoalesceWalker::DoCoalesce\28re2::Regexp**\2c\20re2::Regexp**\29,re2::Regexp::Repeat\28re2::Regexp*\2c\20re2::Regexp::ParseFlags\2c\20int\2c\20int\29 +re2::CoalesceWalker::DoCoalesce\28re2::Regexp**\2c\20re2::Regexp**\29,opa_abort +re2::CoalesceWalker::DoCoalesce\28re2::Regexp**\2c\20re2::Regexp**\29,re2::Regexp::LiteralString\28int*\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::CoalesceWalker::DoCoalesce\28re2::Regexp**\2c\20re2::Regexp**\29,operator\20new\28unsigned\20long\29 +re2::CoalesceWalker::DoCoalesce\28re2::Regexp**\2c\20re2::Regexp**\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::CoalesceWalker::DoCoalesce\28re2::Regexp**\2c\20re2::Regexp**\29,re2::Regexp::Decref\28\29 +re2::SimplifyWalker::Copy\28re2::Regexp*\29,re2::Regexp::Incref\28\29 +re2::SimplifyWalker::ShortVisit\28re2::Regexp*\2c\20re2::Regexp*\29,re2::Regexp::Incref\28\29 +re2::SimplifyWalker::PreVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20bool*\29,re2::Regexp::Incref\28\29 +re2::SimplifyWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,re2::Regexp::Decref\28\29 +re2::SimplifyWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,re2::Regexp::Incref\28\29 +re2::SimplifyWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,re2::SimplifyWalker::SimplifyCharClass\28re2::Regexp*\29 +re2::SimplifyWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,re2::SimplifyWalker::SimplifyRepeat\28re2::Regexp*\2c\20int\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::SimplifyWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,operator\20new\28unsigned\20long\29 +re2::SimplifyWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::SimplifyWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,opa_abort +re2::SimplifyWalker::PostVisit\28re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp*\2c\20re2::Regexp**\2c\20int\29,operator\20new\5b\5d\28unsigned\20long\29 +re2::SimplifyWalker::SimplifyCharClass\28re2::Regexp*\29,opa_abort +re2::SimplifyWalker::SimplifyCharClass\28re2::Regexp*\29,operator\20new\28unsigned\20long\29 +re2::SimplifyWalker::SimplifyCharClass\28re2::Regexp*\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::SimplifyWalker::SimplifyCharClass\28re2::Regexp*\29,re2::Regexp::Incref\28\29 +re2::SimplifyWalker::SimplifyRepeat\28re2::Regexp*\2c\20int\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::Incref\28\29 +re2::SimplifyWalker::SimplifyRepeat\28re2::Regexp*\2c\20int\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::Star\28re2::Regexp*\2c\20re2::Regexp::ParseFlags\29 +re2::SimplifyWalker::SimplifyRepeat\28re2::Regexp*\2c\20int\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::Plus\28re2::Regexp*\2c\20re2::Regexp::ParseFlags\29 +re2::SimplifyWalker::SimplifyRepeat\28re2::Regexp*\2c\20int\2c\20int\2c\20re2::Regexp::ParseFlags\29,operator\20new\28unsigned\20long\29 +re2::SimplifyWalker::SimplifyRepeat\28re2::Regexp*\2c\20int\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::Concat\28re2::Regexp**\2c\20int\2c\20re2::Regexp::ParseFlags\29 +re2::SimplifyWalker::SimplifyRepeat\28re2::Regexp*\2c\20int\2c\20int\2c\20re2::Regexp::ParseFlags\29,operator\20delete\28void*\29 +re2::SimplifyWalker::SimplifyRepeat\28re2::Regexp*\2c\20int\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::Regexp\28re2::RegexpOp\2c\20re2::Regexp::ParseFlags\29 +re2::SimplifyWalker::SimplifyRepeat\28re2::Regexp*\2c\20int\2c\20int\2c\20re2::Regexp::ParseFlags\29,re2::Regexp::Quest\28re2::Regexp*\2c\20re2::Regexp::ParseFlags\29 +re2::SimplifyWalker::SimplifyRepeat\28re2::Regexp*\2c\20int\2c\20int\2c\20re2::Regexp::ParseFlags\29,operator\20new\5b\5d\28unsigned\20long\29 +re2::SimplifyWalker::SimplifyRepeat\28re2::Regexp*\2c\20int\2c\20int\2c\20re2::Regexp::ParseFlags\29,abort +re2::CoalesceWalker::~CoalesceWalker\28\29,re2::Regexp::Walker::~Walker\28\29 +re2::CoalesceWalker::~CoalesceWalker\28\29,operator\20delete\28void*\29 +re2::SimplifyWalker::~SimplifyWalker\28\29,re2::Regexp::Walker::~Walker\28\29 +re2::SimplifyWalker::~SimplifyWalker\28\29,operator\20delete\28void*\29 +std::__1::__deque_base\2c\20std::__1::allocator\20>\20>::~__deque_base\28\29,operator\20delete\28void*\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,memmove +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,operator\20new\28unsigned\20long\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,operator\20delete\28void*\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29 +std::__1::deque\2c\20std::__1::allocator\20>\20>::__add_back_capacity\28\29,abort +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29,memmove +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29,operator\20delete\28void*\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_back\28re2::WalkState*&&\29,abort +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29,memmove +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29,operator\20delete\28void*\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>\20>::push_front\28re2::WalkState*&&\29,abort +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29,memmove +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29,operator\20delete\28void*\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_back\28re2::WalkState*&&\29,abort +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29,memmove +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29,operator\20new\28unsigned\20long\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29,operator\20delete\28void*\29 +std::__1::__split_buffer*\2c\20std::__1::allocator*>&>::push_front\28re2::WalkState*\20const&\29,abort diff --git a/third_party/opa/internal/compiler/wasm/opa/opa.go b/third_party/opa/internal/compiler/wasm/opa/opa.go new file mode 100644 index 000000000000..10b338405bee --- /dev/null +++ b/third_party/opa/internal/compiler/wasm/opa/opa.go @@ -0,0 +1,27 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package opa contains bytecode for the OPA-WASM library. +package opa + +import ( + _ "embed" +) + +//go:embed opa.wasm +var wasmBase []byte + +//go:embed callgraph.csv +var callGraphCSV []byte + +// Bytes returns the OPA-WASM bytecode. +func Bytes() []byte { + return wasmBase +} + +// CallGraphCSV returns a CSV representation of the +// OPA-WASM bytecode's call graph: 'caller,callee' +func CallGraphCSV() []byte { + return callGraphCSV +} diff --git a/third_party/opa/internal/compiler/wasm/opa/opa.wasm b/third_party/opa/internal/compiler/wasm/opa/opa.wasm new file mode 100755 index 0000000000000000000000000000000000000000..667b9cdd4902ae7d186d5007302013358862ffb6 GIT binary patch literal 436729 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%0AG7XjUKdX?B3 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

ZF$B3P&H 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%0Adw^Bb2T(@q% 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

$+|y7z`ya6ox@5>;|J?4>%MKha(}aIjwnDkY}$9 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 literal 0 HcmV?d00001 diff --git a/third_party/opa/internal/compiler/wasm/optimizations.go b/third_party/opa/internal/compiler/wasm/optimizations.go new file mode 100644 index 000000000000..ecdd8d82c1c6 --- /dev/null +++ b/third_party/opa/internal/compiler/wasm/optimizations.go @@ -0,0 +1,271 @@ +package wasm + +import ( + "bytes" + "context" + "encoding/csv" + "fmt" + "os" + "os/exec" + "strconv" + "strings" + "time" + + "github.com/open-policy-agent/opa/internal/compiler/wasm/opa" + "github.com/open-policy-agent/opa/internal/wasm/encoding" + "github.com/open-policy-agent/opa/internal/wasm/instruction" + "github.com/open-policy-agent/opa/internal/wasm/module" +) + +const warning = `--------------------------------------------------------------- +WARNING: Using EXPERIMENTAL, unsupported wasm-opt optimization. + It is not supported, and may go away in the future. +---------------------------------------------------------------` + +// optimizeBinaryen passes the encoded module into wasm-opt, and replaces +// the compiler's module with the decoding of the process' output. +func (c *Compiler) optimizeBinaryen() error { + if os.Getenv("EXPERIMENTAL_WASM_OPT") == "" && os.Getenv("EXPERIMENTAL_WASM_OPT_ARGS") == "" { + c.debug.Printf("not opted in, skipping wasm-opt optimization") + return nil + } + if !woptFound() { + c.debug.Printf("wasm-opt binary not found, skipping optimization") + return nil + } + if os.Getenv("EXPERIMENTAL_WASM_OPT") != "silent" { // for benchmarks + fmt.Fprintln(os.Stderr, warning) + } + args := []string{ // WARNING: flags with typos are ignored! + "-O2", + "--debuginfo", // don't strip name section + } + // allow overriding the options + if env := os.Getenv("EXPERIMENTAL_WASM_OPT_ARGS"); env != "" { + args = strings.Split(env, " ") + } + + args = append(args, "-o", "-") // always output to stdout + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + wopt := exec.CommandContext(ctx, "wasm-opt", args...) + stdin, err := wopt.StdinPipe() + if err != nil { + return fmt.Errorf("get stdin: %w", err) + } + defer stdin.Close() + + var stdout, stderr bytes.Buffer + wopt.Stdout = &stdout + + if err := wopt.Start(); err != nil { + return fmt.Errorf("start wasm-opt: %w", err) + } + if err := encoding.WriteModule(stdin, c.module); err != nil { + return fmt.Errorf("encode module: %w", err) + } + if err := stdin.Close(); err != nil { + return fmt.Errorf("write to wasm-opt: %w", err) + } + if err := wopt.Wait(); err != nil { + return fmt.Errorf("wait for wasm-opt: %w", err) + } + + if d := stderr.String(); d != "" { + c.debug.Printf("wasm-opt debug output: %s", d) + } + mod, err := encoding.ReadModule(&stdout) + if err != nil { + return fmt.Errorf("decode module: %w", err) + } + c.module = mod + return nil +} + +func woptFound() bool { + _, err := exec.LookPath("wasm-opt") + return err == nil +} + +// NOTE(sr): Yes, there are more control instructions than these two, +// but we haven't made use of them yet. So this function only checks +// for the control instructions we're possibly emitting, and which are +// relevant for block nesting. +func withControlInstr(is []instruction.Instruction) bool { + for _, i := range is { + switch i := i.(type) { + case instruction.Br, instruction.BrIf: + return true + case instruction.StructuredInstruction: + // NOTE(sr): We could attempt to further flatten the nested blocks + // here, but I believe we'd then have to correct block labels. + if withControlInstr(i.Instructions()) { + return true + } + } + } + return false +} + +func unquote(s string) (string, error) { + return strconv.Unquote("\"" + strings.ReplaceAll(s, `\`, `\x`) + "\"") +} + +func (c *Compiler) removeUnusedCode() error { + cgCSV := opa.CallGraphCSV() + r := csv.NewReader(bytes.NewReader(cgCSV)) + r.LazyQuotes = true + cg, err := r.ReadAll() + if err != nil { + return fmt.Errorf("csv read: %w", err) + } + + cgIdx := map[uint32][]uint32{} + for i := range cg { + callerName, err := unquote(cg[i][0]) + if err != nil { + return fmt.Errorf("unquote caller name %s: %w", cg[i][0], err) + } + calleeName, err := unquote(cg[i][1]) + if err != nil { + return fmt.Errorf("unquote callee name %s: %w", cg[i][1], err) + } + caller, ok := c.funcs[callerName] + if !ok { + return fmt.Errorf("caller not found: %s (%s)", cg[i][0], callerName) + } + callee, ok := c.funcs[calleeName] + if !ok { + return fmt.Errorf("callee not found: %s (%s)", cg[i][1], calleeName) + } + cgIdx[caller] = append(cgIdx[caller], callee) + } + + // add the calls from planned functions + for _, f := range c.funcsCode { + fidx := c.funcs[f.name] + cgIdx[fidx] = findCallees(f.code.Func.Expr.Instrs) + } + + keepFuncs := map[uint32]struct{}{} + + // we'll keep + // - what's referenced in a table (these could be called indirectly) + // - what's exported or imported + // - what's been compiled by us + // - anything transitively called from those + + for _, imp := range c.module.Import.Imports { + if _, ok := imp.Descriptor.(module.FunctionImport); ok { + reach(cgIdx, keepFuncs, c.funcs[imp.Name]) + } + } + + for _, exp := range c.module.Export.Exports { + if exp.Descriptor.Type == module.FunctionExportType { + reach(cgIdx, keepFuncs, c.funcs[exp.Name]) + } + } + + for _, f := range c.funcsCode { + reach(cgIdx, keepFuncs, c.funcs[f.name]) + } + + // anything referenced in a table + for _, seg := range c.module.Element.Segments { + for _, idx := range seg.Indices { + if c.skipElemRE2(keepFuncs, idx) { + c.debug.Printf("dropping element %d because policy does not depend on re2", idx) + } else { + reach(cgIdx, keepFuncs, idx) + } + } + } + + // remove all that's not needed, update index for remaining ones + funcNames := []module.NameMap{} + for _, nm := range c.module.Names.Functions { + if _, ok := keepFuncs[nm.Index]; ok { + funcNames = append(funcNames, nm) + } + } + c.module.Names.Functions = funcNames + + // For anything that we don't want, replace the function code entries' + // expressions with `unreachable`. + // We do this because it lets the resulting wasm module pass `wasm-validate`, + // empty bodies would not. + nopEntry := module.Function{ + Expr: module.Expr{ + Instrs: []instruction.Instruction{instruction.Unreachable{}}, + }, + } + var buf bytes.Buffer + if err := encoding.WriteCodeEntry(&buf, &module.CodeEntry{Func: nopEntry}); err != nil { + return fmt.Errorf("write code entry: %w", err) + } + for i := range c.module.Code.Segments { + idx := i + c.functionImportCount() + if _, ok := keepFuncs[uint32(idx)]; !ok { + c.module.Code.Segments[i].Code = buf.Bytes() + } + } + return nil +} + +func findCallees(instrs []instruction.Instruction) []uint32 { + var ret []uint32 + for _, expr := range instrs { + switch expr := expr.(type) { + case instruction.Call: + ret = append(ret, expr.Index) + case instruction.StructuredInstruction: + ret = append(ret, findCallees(expr.Instructions())...) + } + } + return ret +} + +func reach(cg map[uint32][]uint32, keep map[uint32]struct{}, node uint32) { + if _, ok := keep[node]; !ok { + keep[node] = struct{}{} + for _, v := range cg[node] { + reach(cg, keep, v) + } + } +} + +// skipElemRE2 determines if a function in the table is really required: +// We'll exclude anything with a prefix of "re2::" if none of the known +// entrypoints into re2 are used. +func (c *Compiler) skipElemRE2(keep map[uint32]struct{}, idx uint32) bool { + if c.usesRE2(keep) { + return false + } + return c.nameContains(idx, "re2::", "lexer::", "std::", "__cxa_pure_virtual", "operator", "parser_") +} + +func (c *Compiler) usesRE2(keep map[uint32]struct{}) bool { + for _, fn := range builtinsUsingRE2 { + if _, ok := keep[c.function(fn)]; ok { + return true + } + } + return false +} + +func (c *Compiler) nameContains(idx uint32, hs ...string) bool { + // TODO(sr): keep reverse mapping (idx -> name) in Compiler struct + for _, nm := range c.module.Names.Functions { + if nm.Index == idx { + for _, h := range hs { + if strings.Contains(nm.Name, h) { + return true + } + } + return false + } + } + return false +} diff --git a/third_party/opa/internal/compiler/wasm/optimizations_test.go b/third_party/opa/internal/compiler/wasm/optimizations_test.go new file mode 100644 index 000000000000..166d53502f08 --- /dev/null +++ b/third_party/opa/internal/compiler/wasm/optimizations_test.go @@ -0,0 +1,56 @@ +package wasm + +import ( + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/planner" + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestRemoveUnusedCode(t *testing.T) { + policy, err := planner.New(). + WithQueries([]planner.QuerySet{ + { + Name: "test", + Queries: []ast.Body{ast.MustParseBody(`input.foo = 1`)}, + }, + }).Plan() + + if err != nil { + t.Fatal(err) + } + + c := New().WithPolicy(policy) + mod, err := c.Compile() + if err != nil { + t.Fatal(err) + } + + // NOTE(sr): our unused code elimination has the following invariant: + // if a function is not used, both its code and its name are removed + // from the code section, and name sections respectively. + idxToName := map[uint32]string{} + for _, nm := range mod.Names.Functions { + idxToName[nm.Index] = nm.Name + } + for i, seg := range mod.Code.Segments { + idx := i + c.functionImportCount() + noop := len(seg.Code) == 3 + name, ok := idxToName[uint32(idx)] + if noop && ok { + t.Errorf("func[%d] has name (%s) and no code", idx, name) + } + if !noop && !ok { + t.Errorf("func[%d] has code but no name", idx) + } + } + + // Having established that, we can check that this simple policy + // has no re2-related code by consulting the name map: + for _, nm := range mod.Names.Functions { + if strings.Contains(nm.Name, "re2") { + t.Errorf("expected no re2-related functions, found %s", nm.Name) + } + } +} diff --git a/third_party/opa/internal/compiler/wasm/wasm.go b/third_party/opa/internal/compiler/wasm/wasm.go new file mode 100644 index 000000000000..25cbc13b4718 --- /dev/null +++ b/third_party/opa/internal/compiler/wasm/wasm.go @@ -0,0 +1,1786 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package wasm contains an IR->WASM compiler backend. +package wasm + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + + "github.com/open-policy-agent/opa/internal/compiler/wasm/opa" + "github.com/open-policy-agent/opa/internal/debug" + "github.com/open-policy-agent/opa/internal/wasm/encoding" + "github.com/open-policy-agent/opa/internal/wasm/instruction" + "github.com/open-policy-agent/opa/internal/wasm/module" + "github.com/open-policy-agent/opa/internal/wasm/types" + "github.com/open-policy-agent/opa/internal/wasm/util" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ir" + opatypes "github.com/open-policy-agent/opa/v1/types" +) + +// Record Wasm ABI version in exported global variable +const ( + opaWasmABIVersionVal = 1 + opaWasmABIVersionVar = "opa_wasm_abi_version" + opaWasmABIMinorVersionVal = 3 + opaWasmABIMinorVersionVar = "opa_wasm_abi_minor_version" +) + +// nolint: deadcode,varcheck +const ( + opaTypeNull int32 = iota + 1 + opaTypeBoolean + opaTypeNumber + opaTypeString + opaTypeArray + opaTypeObject + opaTypeSet + opaTypeStringInterned + opaTypeBooleanInterned +) + +const ( + opaAbort = "opa_abort" + opaRuntimeError = "opa_runtime_error" + opaNull = "opa_null" + opaBoolean = "opa_boolean" + opaNumberInt = "opa_number_int" + opaNumberRef = "opa_number_ref" + opaNumberSize = "opa_number_size" + opaArrayWithCap = "opa_array_with_cap" + opaArrayAppend = "opa_array_append" + opaObject = "opa_object" + opaObjectInsert = "opa_object_insert" + opaSet = "opa_set" + opaSetAdd = "opa_set_add" + opaStringTerminated = "opa_string_terminated" + opaValueNumberSetInt = "opa_value_number_set_int" + opaValueCompare = "opa_value_compare" + opaValueGet = "opa_value_get" + opaValueIter = "opa_value_iter" + opaValueLength = "opa_value_length" + opaValueMerge = "opa_value_merge" + opaValueShallowCopy = "opa_value_shallow_copy" + opaValueType = "opa_value_type" + opaMemoizeInit = "opa_memoize_init" + opaMemoizePush = "opa_memoize_push" + opaMemoizePop = "opa_memoize_pop" + opaMemoizeInsert = "opa_memoize_insert" + opaMemoizeGet = "opa_memoize_get" + opaMappingInit = "opa_mapping_init" + opaMappingLookup = "opa_mapping_lookup" + opaMPDInit = "opa_mpd_init" + opaMallocInit = "opa_malloc_init" +) + +var builtinsFunctions = map[string]string{ + ast.Plus.Name: "opa_arith_plus", + ast.Minus.Name: "opa_arith_minus", + ast.Multiply.Name: "opa_arith_multiply", + ast.Divide.Name: "opa_arith_divide", + ast.Abs.Name: "opa_arith_abs", + ast.Round.Name: "opa_arith_round", + ast.Ceil.Name: "opa_arith_ceil", + ast.Floor.Name: "opa_arith_floor", + ast.Rem.Name: "opa_arith_rem", + ast.ArrayConcat.Name: "opa_array_concat", + ast.ArrayReverse.Name: "opa_array_reverse", + ast.ArraySlice.Name: "opa_array_slice", + ast.SetDiff.Name: "opa_set_diff", + ast.And.Name: "opa_set_intersection", + ast.Or.Name: "opa_set_union", + ast.Intersection.Name: "opa_sets_intersection", + ast.Union.Name: "opa_sets_union", + ast.IsNumber.Name: "opa_types_is_number", + ast.IsString.Name: "opa_types_is_string", + ast.IsBoolean.Name: "opa_types_is_boolean", + ast.IsArray.Name: "opa_types_is_array", + ast.IsSet.Name: "opa_types_is_set", + ast.IsObject.Name: "opa_types_is_object", + ast.IsNull.Name: "opa_types_is_null", + ast.TypeNameBuiltin.Name: "opa_types_name", + ast.BitsOr.Name: "opa_bits_or", + ast.BitsAnd.Name: "opa_bits_and", + ast.BitsNegate.Name: "opa_bits_negate", + ast.BitsXOr.Name: "opa_bits_xor", + ast.BitsShiftLeft.Name: "opa_bits_shiftleft", + ast.BitsShiftRight.Name: "opa_bits_shiftright", + ast.Count.Name: "opa_agg_count", + ast.Sum.Name: "opa_agg_sum", + ast.Product.Name: "opa_agg_product", + ast.Max.Name: "opa_agg_max", + ast.Min.Name: "opa_agg_min", + ast.Sort.Name: "opa_agg_sort", + ast.All.Name: "opa_agg_all", + ast.Any.Name: "opa_agg_any", + ast.Base64IsValid.Name: "opa_base64_is_valid", + ast.Base64Decode.Name: "opa_base64_decode", + ast.Base64Encode.Name: "opa_base64_encode", + ast.Base64UrlEncode.Name: "opa_base64_url_encode", + ast.Base64UrlDecode.Name: "opa_base64_url_decode", + ast.NetCIDRContains.Name: "opa_cidr_contains", + ast.NetCIDROverlap.Name: "opa_cidr_contains", + ast.NetCIDRIntersects.Name: "opa_cidr_intersects", + ast.Equal.Name: "opa_cmp_eq", + ast.GreaterThan.Name: "opa_cmp_gt", + ast.GreaterThanEq.Name: "opa_cmp_gte", + ast.LessThan.Name: "opa_cmp_lt", + ast.LessThanEq.Name: "opa_cmp_lte", + ast.NotEqual.Name: "opa_cmp_neq", + ast.GlobMatch.Name: "opa_glob_match", + ast.JSONMarshal.Name: "opa_json_marshal", + ast.JSONUnmarshal.Name: "opa_json_unmarshal", + ast.JSONIsValid.Name: "opa_json_is_valid", + ast.ObjectFilter.Name: "builtin_object_filter", + ast.ObjectGet.Name: "builtin_object_get", + ast.ObjectKeys.Name: "builtin_object_keys", + ast.ObjectRemove.Name: "builtin_object_remove", + ast.ObjectUnion.Name: "builtin_object_union", + ast.ObjectUnionN.Name: "builtin_object_union_n", + ast.Concat.Name: "opa_strings_concat", + ast.FormatInt.Name: "opa_strings_format_int", + ast.IndexOf.Name: "opa_strings_indexof", + ast.Substring.Name: "opa_strings_substring", + ast.Lower.Name: "opa_strings_lower", + ast.Upper.Name: "opa_strings_upper", + ast.Contains.Name: "opa_strings_contains", + ast.StartsWith.Name: "opa_strings_startswith", + ast.EndsWith.Name: "opa_strings_endswith", + ast.StringReverse.Name: "opa_strings_reverse", + ast.Split.Name: "opa_strings_split", + ast.Replace.Name: "opa_strings_replace", + ast.ReplaceN.Name: "opa_strings_replace_n", + ast.Trim.Name: "opa_strings_trim", + ast.TrimLeft.Name: "opa_strings_trim_left", + ast.TrimPrefix.Name: "opa_strings_trim_prefix", + ast.TrimRight.Name: "opa_strings_trim_right", + ast.TrimSuffix.Name: "opa_strings_trim_suffix", + ast.TrimSpace.Name: "opa_strings_trim_space", + ast.NumbersRange.Name: "opa_numbers_range", + ast.ToNumber.Name: "opa_to_number", + ast.WalkBuiltin.Name: "opa_value_transitive_closure", + ast.ReachableBuiltin.Name: "builtin_graph_reachable", + ast.RegexIsValid.Name: "opa_regex_is_valid", + ast.RegexMatch.Name: "opa_regex_match", + ast.RegexMatchDeprecated.Name: "opa_regex_match", + ast.RegexFindAllStringSubmatch.Name: "opa_regex_find_all_string_submatch", + ast.JSONRemove.Name: "builtin_json_remove", + ast.JSONFilter.Name: "builtin_json_filter", + ast.Member.Name: "builtin_member", + ast.MemberWithKey.Name: "builtin_member3", +} + +// If none of these is called from a policy, the resulting wasm +// module will not contain any RE2-related functions +var builtinsUsingRE2 = [...]string{ + builtinsFunctions[ast.RegexIsValid.Name], + builtinsFunctions[ast.RegexMatch.Name], + builtinsFunctions[ast.RegexMatchDeprecated.Name], + builtinsFunctions[ast.RegexFindAllStringSubmatch.Name], + builtinsFunctions[ast.GlobMatch.Name], +} + +func IsWasmEnabled(bi string) bool { + _, ok := builtinsFunctions[bi] + return ok +} + +type externalFunc struct { + ID int32 + Decl *opatypes.Function +} + +var builtinDispatchers = [...]string{ + "opa_builtin0", + "opa_builtin1", + "opa_builtin2", + "opa_builtin3", + "opa_builtin4", +} + +// Compiler implements an IR->WASM compiler backend. +type Compiler struct { + stages []func() error // compiler stages to execute + errors []error // compilation errors encountered + + policy *ir.Policy // input policy to compile + module *module.Module // output WASM module + code *module.CodeEntry // output WASM code + + funcsCode []funcCode // compile functions' code + + builtinStringAddrs map[int]uint32 // addresses of built-in string constants + externalFuncNameAddrs map[string]int32 // addresses of required built-in function names for listing + externalFuncs map[string]externalFunc // required built-in function ids and types + entrypointNameAddrs map[string]int32 // addresses of available entrypoint names for listing + entrypoints map[string]int32 // available entrypoint ids + stringOffset int32 // null-terminated string data base offset + stringAddrs []uint32 // null-terminated string constant addresses + opaStringAddrs []uint32 // addresses of interned opa_string_t + opaBoolAddrs map[ir.Bool]uint32 // addresses of interned opa_boolean_t + fileAddrs []uint32 // null-terminated string constant addresses, used for file names + funcs map[string]uint32 // maps imported and exported function names to function indices + + nextLocal uint32 + locals map[ir.Local]uint32 + lctx uint32 // local pointing to eval context + lrs uint32 // local pointing to result set + + debug debug.Debug +} + +type funcCode struct { + name string + code *module.CodeEntry +} + +const ( + errVarAssignConflict int = iota + errObjectInsertConflict + errIllegalEntrypoint +) + +var errorMessages = [...]struct { + id int + message string +}{ + {errVarAssignConflict, "var assignment conflict"}, + {errObjectInsertConflict, "object insert conflict"}, + {errIllegalEntrypoint, "internal: illegal entrypoint id"}, +} + +// New returns a new compiler object. +func New() *Compiler { + c := &Compiler{ + debug: debug.Discard(), + } + c.stages = []func() error{ + c.initModule, + c.compileStringsAndBooleans, + c.addImportMemoryDecl, + c.compileExternalFuncDecls, + c.compileEntrypointDecls, + c.compileFuncs, + c.compilePlans, + c.emitABIVersionGlobals, + + // "local" optimizations + c.removeUnusedCode, + + // final emissions + c.emitFuncs, + + // global optimizations + c.optimizeBinaryen, + } + return c +} + +// ABIVersion returns the Wasm ABI version this compiler +// emits. +func (*Compiler) ABIVersion() ast.WasmABIVersion { + return ast.WasmABIVersion{ + Version: opaWasmABIVersionVal, + Minor: opaWasmABIMinorVersionVal, + } +} + +// WithPolicy sets the policy to compile. +func (c *Compiler) WithPolicy(p *ir.Policy) *Compiler { + c.policy = p + return c +} + +// WithDebug sets the sink for debug logs emitted by the compiler. +func (c *Compiler) WithDebug(sink io.Writer) *Compiler { + if sink != nil { + c.debug = debug.New(sink) + } + return c +} + +// Compile returns a compiled WASM module. +func (c *Compiler) Compile() (*module.Module, error) { + + for _, stage := range c.stages { + if err := stage(); err != nil { + return nil, err + } else if len(c.errors) > 0 { + return nil, c.errors[0] // TODO(tsandall) return all errors. + } + } + + return c.module, nil +} + +// initModule instantiates the module from the pre-compiled OPA binary. The +// module is then updated to include declarations for all of the functions that +// are about to be compiled. +func (c *Compiler) initModule() error { + + bs := opa.Bytes() + var err error + c.module, err = encoding.ReadModule(bytes.NewReader(bs)) + if err != nil { + return err + } + + c.funcs = make(map[string]uint32) + for _, fn := range c.module.Names.Functions { + name := fn.Name + // Account for recording duplicate functions -- this only happens + // with the RE2 C++ lib so far. + // NOTE: This isn't good enough for function names used more than + // two times. But let's deal with that when it happens. + if _, ok := c.funcs[name]; ok { // already seen + c.debug.Printf("function name duplicate: %s (%d)", name, fn.Index) + name += ".1" + } + c.funcs[name] = fn.Index + } + + for _, fn := range c.policy.Funcs.Funcs { + + params := make([]types.ValueType, len(fn.Params)) + for i := range params { + params[i] = types.I32 + } + + tpe := module.FunctionType{ + Params: params, + Results: []types.ValueType{types.I32}, + } + + c.emitFunctionDecl(fn.Name, tpe, false) + } + + c.emitFunctionDecl("eval", module.FunctionType{ + Params: []types.ValueType{types.I32}, + Results: []types.ValueType{types.I32}, + }, true) + + c.emitFunctionDecl("builtins", module.FunctionType{ + Params: nil, + Results: []types.ValueType{types.I32}, + }, true) + + c.emitFunctionDecl("entrypoints", module.FunctionType{ + Params: nil, + Results: []types.ValueType{types.I32}, + }, true) + + // NOTE(sr): LLVM needs a section of linear memory to be zero'ed out and reserved, + // for static variables defined in the C code. When using imported memory, it adds + // a data segment to ensure that. When not using imported memory, it would ensure + // that a zero'ed out region is available by adjust the __heap_base address. + // Since we control "imported/not-imported" memory here, we make these adjustments + // here, too: + // + // a. the __heap_base exported variable is read, + // b. the __heap_base variable is removed from exports and globals + // c. a data segment filled with zeros of the proper length is added + var idx uint32 + var del int + for i, exp := range c.module.Export.Exports { + if exp.Name == "__heap_base" { + idx = exp.Descriptor.Index + del = i + } + } + heapBase := c.module.Global.Globals[idx].Init.Instrs[0].(instruction.I32Const).Value + + // (b) remove __heap_base export and global + c.module.Export.Exports = append(c.module.Export.Exports[:del], c.module.Export.Exports[del+1:]...) + c.module.Global.Globals = append(c.module.Global.Globals[:idx], c.module.Global.Globals[idx+1:]...) + + // (c) add data segment with zeros + offset, err := getLowestFreeDataSegmentOffset(c.module) + if err != nil { + return err + } + + c.module.Data.Segments = append(c.module.Data.Segments, module.DataSegment{ + Index: 0, + Offset: module.Expr{ + Instrs: []instruction.Instruction{ + instruction.I32Const{ + Value: offset, + }, + }, + }, + Init: bytes.Repeat([]byte{0}, int(heapBase-offset)), + }) + + return nil +} + +// NOTE(sr): The wasm module we start with, compiled via LLVM, has NO memory +// import or export. Here, we change that: the OPA-generated wasm module will +// +// a. import its memory +// b. re-export that memory +// c. have no "own" memory (in its memory section) +// +// (b) is provided by the LLVM base module, it already has an export of memory[0] +// (a) and (c) are taken care of here. +// +// In the future, we could change that, and here would be the place to do so. +func (c *Compiler) addImportMemoryDecl() error { + offset, err := getLowestFreeDataSegmentOffset(c.module) + if err != nil { + return err + } + + c.module.Import.Imports = append(c.module.Import.Imports, module.Import{ + Module: "env", + Name: "memory", + Descriptor: module.MemoryImport{ + Mem: module.MemType{ + Lim: module.Limit{ + Min: util.Pages(uint32(offset)), + }, + }, + }, + }) + c.module.Memory.Memories = nil + + return nil +} + +// emitABIVersionGLobals adds globals for ABI [minor] version, exports them +func (c *Compiler) emitABIVersionGlobals() error { + abiVersionGlobals := []module.Global{ + { + Type: types.I32, + Mutable: false, + Init: module.Expr{ + Instrs: []instruction.Instruction{ + instruction.I32Const{Value: opaWasmABIVersionVal}, + }, + }, + }, + { + Type: types.I32, + Mutable: false, + Init: module.Expr{ + Instrs: []instruction.Instruction{ + instruction.I32Const{Value: opaWasmABIMinorVersionVal}, + }, + }, + }, + } + abiVersionExports := []module.Export{ + { + Name: opaWasmABIVersionVar, + Descriptor: module.ExportDescriptor{ + Type: module.GlobalExportType, + Index: uint32(len(c.module.Global.Globals)), + }, + }, + { + Name: opaWasmABIMinorVersionVar, + Descriptor: module.ExportDescriptor{ + Type: module.GlobalExportType, + Index: uint32(len(c.module.Global.Globals)) + 1, + }, + }, + } + c.module.Global.Globals = append(c.module.Global.Globals, abiVersionGlobals...) + c.module.Export.Exports = append(c.module.Export.Exports, abiVersionExports...) + return nil +} + +// compileStringsAndBooleans compiles various string constants (strings, file names, +// external function names, entrypoint names, builtin names), and interned opa_value structs +// for strings and booleans into the data section of the module. +// All are indexed for lookups in later stages. +func (c *Compiler) compileStringsAndBooleans() error { + + var err error + c.stringOffset, err = getLowestFreeDataSegmentOffset(c.module) + if err != nil { + return err + } + + var buf bytes.Buffer + + c.writeStrings(&buf) + + if err := c.writeInternedOPAValues(&buf); err != nil { + return err + } + + c.writeFileAddrs(&buf) + c.writeExternalFuncNames(&buf) + c.writeEntrypointNames(&buf) + c.writeBuiltinStrings(&buf) + + c.module.Data.Segments = append(c.module.Data.Segments, module.DataSegment{ + Index: 0, + Offset: module.Expr{ + Instrs: []instruction.Instruction{ + instruction.I32Const{ + Value: c.stringOffset, + }, + }, + }, + Init: buf.Bytes(), + }) + + return nil +} + +func (c *Compiler) writeStrings(buf *bytes.Buffer) { + c.stringAddrs = make([]uint32, len(c.policy.Static.Strings)) + + for i, s := range c.policy.Static.Strings { + addr := uint32(buf.Len()) + uint32(c.stringOffset) + buf.WriteString(s.Value) + buf.WriteByte(0) + c.stringAddrs[i] = addr + } +} + +func (c *Compiler) writeInternedOPAValues(buf *bytes.Buffer) error { + // interned `opa_value*` for these true/false booleans + c.opaBoolAddrs = make(map[ir.Bool]uint32, 2) + for _, val := range []bool{true, false} { + opaBool := ir.Bool(val) + v := byte(0) + if val { + v = 1 + } + c.opaBoolAddrs[opaBool] = uint32(buf.Len()) + uint32(c.stringOffset) + size := 2 + n, err := buf.Write([]byte{byte(opaTypeBooleanInterned), v}) + if err != nil { + return fmt.Errorf("write interned bools: %w", err) + } + if n != size { + return fmt.Errorf("short write: %d (expected %d)", n, size) + } + } + + // interned `opa_value*` for these constant strings + c.opaStringAddrs = make([]uint32, len(c.policy.Static.Strings)) + for i, s := range c.policy.Static.Strings { + c.opaStringAddrs[i] = uint32(buf.Len()) + uint32(c.stringOffset) + size := 12 + b := make([]byte, size) + binary.LittleEndian.PutUint16(b[0:], uint16(opaTypeStringInterned)) + binary.LittleEndian.PutUint32(b[4:], uint32(len(s.Value))) + binary.LittleEndian.PutUint32(b[8:], c.stringAddrs[i]) + n, err := buf.Write(b) + if err != nil { + return fmt.Errorf("write interned strings: %w", err) + } + if n != size { + return fmt.Errorf("short write: %d (expected %d)", n, size) + } + } + return nil +} + +func (c *Compiler) writeFileAddrs(buf *bytes.Buffer) { + // NOTE(sr): All files that have been consulted in planning are recorded, + // regardless of their potential in generating runtime errors. + c.fileAddrs = make([]uint32, len(c.policy.Static.Files)) + + for i, file := range c.policy.Static.Files { + addr := uint32(buf.Len()) + uint32(c.stringOffset) + buf.WriteString(file.Value) + buf.WriteByte(0) + c.fileAddrs[i] = addr + } +} + +func (c *Compiler) writeExternalFuncNames(buf *bytes.Buffer) { + c.externalFuncNameAddrs = make(map[string]int32) + + for _, decl := range c.policy.Static.BuiltinFuncs { + if _, ok := builtinsFunctions[decl.Name]; !ok { + addr := int32(buf.Len()) + c.stringOffset + buf.WriteString(decl.Name) + buf.WriteByte(0) + c.externalFuncNameAddrs[decl.Name] = addr + } + } +} + +func (c *Compiler) writeEntrypointNames(buf *bytes.Buffer) { + c.entrypointNameAddrs = make(map[string]int32) + + for _, plan := range c.policy.Plans.Plans { + addr := int32(buf.Len()) + c.stringOffset + buf.WriteString(plan.Name) + buf.WriteByte(0) + c.entrypointNameAddrs[plan.Name] = addr + } +} + +func (c *Compiler) writeBuiltinStrings(buf *bytes.Buffer) { + c.builtinStringAddrs = make(map[int]uint32, len(errorMessages)) + + for i := range errorMessages { + addr := uint32(buf.Len()) + uint32(c.stringOffset) + buf.WriteString(errorMessages[i].message) + buf.WriteByte(0) + c.builtinStringAddrs[errorMessages[i].id] = addr + } +} + +// compileExternalFuncDecls generates a function that lists the built-ins required by +// the policy. The host environment should invoke this function obtain the list +// of built-in function identifiers (represented as integers) that will be used +// when calling out. +func (c *Compiler) compileExternalFuncDecls() error { + + c.code = &module.CodeEntry{} + c.nextLocal = 0 + c.locals = map[ir.Local]uint32{} + + lobj := c.genLocal() + + c.appendInstr(instruction.Call{Index: c.function(opaObject)}) + c.appendInstr(instruction.SetLocal{Index: lobj}) + c.externalFuncs = make(map[string]externalFunc) + + for index, decl := range c.policy.Static.BuiltinFuncs { + if _, ok := builtinsFunctions[decl.Name]; !ok { + c.appendInstr(instruction.GetLocal{Index: lobj}) + c.appendInstr(instruction.I32Const{Value: c.externalFuncNameAddrs[decl.Name]}) + c.appendInstr(instruction.Call{Index: c.function(opaStringTerminated)}) + c.appendInstr(instruction.I64Const{Value: int64(index)}) + c.appendInstr(instruction.Call{Index: c.function(opaNumberInt)}) + c.appendInstr(instruction.Call{Index: c.function(opaObjectInsert)}) + c.externalFuncs[decl.Name] = externalFunc{ID: int32(index), Decl: decl.Decl} + } + } + + c.appendInstr(instruction.GetLocal{Index: lobj}) + + c.code.Func.Locals = []module.LocalDeclaration{ + { + Count: c.nextLocal, + Type: types.I32, + }, + } + + return c.storeFunc("builtins", c.code) +} + +// compileEntrypointDecls generates a function that lists the entrypoints available +// in the policy. The host environment can pick which entrypoint to invoke by setting +// the entrypoint identifier (represented as an integer) on the evaluation context. +func (c *Compiler) compileEntrypointDecls() error { + + c.code = &module.CodeEntry{} + c.nextLocal = 0 + c.locals = map[ir.Local]uint32{} + + lobj := c.genLocal() + + c.appendInstr(instruction.Call{Index: c.function(opaObject)}) + c.appendInstr(instruction.SetLocal{Index: lobj}) + c.entrypoints = make(map[string]int32) + + for index, plan := range c.policy.Plans.Plans { + c.appendInstr(instruction.GetLocal{Index: lobj}) + c.appendInstr(instruction.I32Const{Value: c.entrypointNameAddrs[plan.Name]}) + c.appendInstr(instruction.Call{Index: c.function(opaStringTerminated)}) + c.appendInstr(instruction.I64Const{Value: int64(index)}) + c.appendInstr(instruction.Call{Index: c.function(opaNumberInt)}) + c.appendInstr(instruction.Call{Index: c.function(opaObjectInsert)}) + c.entrypoints[plan.Name] = int32(index) + } + + c.appendInstr(instruction.GetLocal{Index: lobj}) + + c.code.Func.Locals = []module.LocalDeclaration{ + { + Count: c.nextLocal, + Type: types.I32, + }, + } + + return c.storeFunc("entrypoints", c.code) +} + +// compileFuncs compiles the policy functions and emits them into the module. +func (c *Compiler) compileFuncs() error { + for _, fn := range c.policy.Funcs.Funcs { + if err := c.compileFunc(fn); err != nil { + return fmt.Errorf("func %v: %w", fn.Name, err) + } + } + + if err := c.emitMappingAndStartFunc(); err != nil { + return fmt.Errorf("writing mapping: %w", err) + } + + if err := c.replaceBooleanFunc(); err != nil { + return fmt.Errorf("replacing opa_boolean: %w", err) + } + return nil +} + +// compilePlans compiles the policy plans and emits the resulting function into +// the module. +func (c *Compiler) compilePlans() error { + + c.code = &module.CodeEntry{} + c.nextLocal = 0 + c.locals = map[ir.Local]uint32{} + c.lctx = c.genLocal() + c.lrs = c.genLocal() + + // Initialize memoization. + c.appendInstr(instruction.Call{Index: c.function(opaMemoizeInit)}) + + // Initialize the input and data locals. + c.appendInstr(instruction.GetLocal{Index: c.lctx}) + c.appendInstr(instruction.I32Load{Offset: 0, Align: 2}) + c.appendInstr(instruction.SetLocal{Index: c.local(ir.Input)}) + + c.appendInstr(instruction.GetLocal{Index: c.lctx}) + c.appendInstr(instruction.I32Load{Offset: 4, Align: 2}) + c.appendInstr(instruction.SetLocal{Index: c.local(ir.Data)}) + + // Initialize the result set. + c.appendInstr(instruction.Call{Index: c.function(opaSet)}) + c.appendInstr(instruction.SetLocal{Index: c.lrs}) + c.appendInstr(instruction.GetLocal{Index: c.lctx}) + c.appendInstr(instruction.GetLocal{Index: c.lrs}) + c.appendInstr(instruction.I32Store{Offset: 8, Align: 2}) + + // Initialize the entrypoint id local. + leid := c.genLocal() + c.appendInstr(instruction.GetLocal{Index: c.lctx}) + c.appendInstr(instruction.I32Load{Offset: 12, Align: 2}) + c.appendInstr(instruction.SetLocal{Index: leid}) + + // Add each entrypoint to this block. + main := instruction.Block{} + + for i, plan := range c.policy.Plans.Plans { + + entrypoint := instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.GetLocal{Index: leid}, + instruction.I32Const{Value: int32(i)}, + instruction.I32Ne{}, + instruction.BrIf{Index: 0}, + }, + } + + for j, block := range plan.Blocks { + + instrs, err := c.compileBlock(block) + if err != nil { + return fmt.Errorf("plan %d block %d: %w", i, j, err) + } + + entrypoint.Instrs = append(entrypoint.Instrs, instruction.Block{ + Instrs: instrs, + }) + } + + entrypoint.Instrs = append(entrypoint.Instrs, instruction.Br{Index: 1}) + main.Instrs = append(main.Instrs, entrypoint) + } + + // If none of the entrypoint blocks execute, call opa_abort() as this likely + // indicates inconsistency between the generated entrypoint identifiers in the + // eval() and entrypoint() functions (or the SDK invoked eval() with an invalid + // entrypoint ID which should not be possible.) + main.Instrs = append(main.Instrs, + instruction.I32Const{Value: c.builtinStringAddr(errIllegalEntrypoint)}, + instruction.Call{Index: c.function(opaAbort)}, + instruction.Unreachable{}, + ) + + c.appendInstr(main) + c.appendInstr(instruction.I32Const{Value: int32(0)}) + + c.code.Func.Locals = []module.LocalDeclaration{ + { + Count: c.nextLocal, + Type: types.I32, + }, + } + + return c.storeFunc("eval", c.code) +} + +func (c *Compiler) compileFunc(fn *ir.Func) error { + idx, ok := c.funcs[fn.Name] + if !ok { + return fmt.Errorf("unknown function: %v", fn.Name) + } + + memoize := len(fn.Params) == 2 + + if len(fn.Params) == 0 { + return errors.New("illegal function: zero args") + } + + c.nextLocal = 0 + c.locals = map[ir.Local]uint32{} + + for _, a := range fn.Params { + _ = c.local(a) + } + + _ = c.local(fn.Return) + + c.code = &module.CodeEntry{} + + // memoization: get + if memoize { + c.appendInstr(instruction.I32Const{Value: int32(idx)}) + c.appendInstr(instruction.Call{Index: c.function(opaMemoizeGet)}) + c.appendInstr(instruction.TeeLocal{Index: c.local(fn.Return)}) + c.appendInstr(instruction.If{Instrs: []instruction.Instruction{ + instruction.GetLocal{Index: c.local(fn.Return)}, + instruction.Return{}, + }}) + } + + for i := range fn.Blocks { + instrs, err := c.compileBlock(fn.Blocks[i]) + if err != nil { + return fmt.Errorf("block %d: %w", i, err) + } + if i < len(fn.Blocks)-1 { // not the last block: wrap in `block` instr + if withControlInstr(instrs) { // unless we don't need to + c.appendInstr(instruction.Block{Instrs: instrs}) + } else { + c.appendInstrs(instrs) + } + } else { // last block, no wrapping + // memoization: insert, spliced into the instructions right + // before the return: + for _, instr := range instrs { + if _, ok := instr.(instruction.Return); ok && memoize { + c.appendInstr(instruction.I32Const{Value: int32(idx)}) + c.appendInstr(instruction.GetLocal{Index: c.local(fn.Return)}) + c.appendInstr(instruction.Call{Index: c.function(opaMemoizeInsert)}) + } + c.appendInstr(instr) + } + } + } + + c.code.Func.Locals = []module.LocalDeclaration{ + { + Count: c.nextLocal, + Type: types.I32, + }, + } + + return c.storeFunc(fn.Name, c.code) +} + +func mapFunc(mapping ast.Object, fn *ir.Func, index int) (ast.Object, bool) { + curr := ast.NewObject(ast.Item(ast.StringTerm(fn.Path[len(fn.Path)-1]), ast.IntNumberTerm(index))) + for i := len(fn.Path) - 2; i >= 0; i-- { + curr = ast.NewObject(ast.Item(ast.StringTerm(fn.Path[i]), ast.NewTerm(curr))) + } + return mapping.Merge(curr) +} + +func (c *Compiler) emitMappingAndStartFunc() error { + indices := make([]uint32, 0, len(c.policy.Funcs.Funcs)) + var ok bool + mapping := ast.NewObject() + + // element segment offset for our mapped function entries + elemOffset, err := getLowestFreeElementSegmentOffset(c.module) + if err != nil { + return err + } + + for i, fn := range c.policy.Funcs.Funcs { + indices = append(indices, c.funcs[fn.Name]) + mapping, ok = mapFunc(mapping, fn, i+int(elemOffset)) + if !ok { + return fmt.Errorf("mapping function %v failed", fn.Name) + } + } + + // emit data segment for JSON blob encoding mapping + jsonMap := []byte(mapping.String()) + dataOffset, err := getLowestFreeDataSegmentOffset(c.module) + if err != nil { + return err + } + c.module.Data.Segments = append(c.module.Data.Segments, module.DataSegment{ + Index: 0, + Offset: module.Expr{ + Instrs: []instruction.Instruction{ + instruction.I32Const{ + Value: dataOffset, + }, + }, + }, + Init: jsonMap, + }) + + // write element segments for table entries + c.module.Element.Segments = append(c.module.Element.Segments, module.ElementSegment{ + Index: 0, + Offset: module.Expr{ + Instrs: []instruction.Instruction{ + instruction.I32Const{ + Value: elemOffset, + }, + }, + }, + Indices: indices, + }) + + // adjust table limits + min := c.module.Table.Tables[0].Lim.Min + uint32(len(indices)) + max := *c.module.Table.Tables[0].Lim.Max + uint32(len(indices)) + c.module.Table.Tables[0].Lim.Min = min + c.module.Table.Tables[0].Lim.Max = &max + + heapBase, err := getLowestFreeDataSegmentOffset(c.module) + if err != nil { + return err + } + + // create function that calls `void opa_mapping_initialize(const char *s, const int l)` + // with s being the offset of the data segment just written, and l its length + fName := "_initialize" + c.code = &module.CodeEntry{} + c.appendInstr(instruction.I32Const{Value: heapBase}) + c.appendInstr(instruction.Call{Index: c.function(opaMallocInit)}) + c.appendInstr(instruction.Call{Index: c.function(opaMPDInit)}) + c.appendInstr(instruction.I32Const{Value: dataOffset}) + c.appendInstr(instruction.I32Const{Value: int32(len(jsonMap))}) + c.appendInstr(instruction.Call{Index: c.function(opaMappingInit)}) + c.emitFunctionDecl(fName, module.FunctionType{}, false) + idx := c.function(fName) + c.module.Start.FuncIndex = &idx + return c.storeFunc(fName, c.code) +} + +// replaceBooleanFunc finds the `opa_boolean` code section, and replaces it with +// a simpler function, that's returning one of the interned `opa_boolean_t`s +// instead. +// NOTE(sr): We're doing it in this crude way because LLVM 11 doesn't let us +// differentiate that some unknown symbols (opa_abort, opa_builtin0, etc) should +// be created as imports, and other should be ignored. So, we're having a stub +// implementation in wasm/src/value.c that'll get replaced here. +func (c *Compiler) replaceBooleanFunc() error { + c.code = &module.CodeEntry{} + c.appendInstr(instruction.I32Const{Value: int32(c.opaBoolAddrs[true])}) + c.appendInstr(instruction.I32Const{Value: int32(c.opaBoolAddrs[false])}) + c.appendInstr(instruction.GetLocal{Index: 0}) + c.appendInstr(instruction.Select{}) + + return c.storeFunc(opaBoolean, c.code) +} + +func (c *Compiler) compileBlock(block *ir.Block) ([]instruction.Instruction, error) { + + var instrs []instruction.Instruction + + for _, stmt := range block.Stmts { + switch stmt := stmt.(type) { + case *ir.ResultSetAddStmt: + instrs = append(instrs, + instruction.GetLocal{Index: c.lrs}, + instruction.GetLocal{Index: c.local(stmt.Value)}, + instruction.Call{Index: c.function(opaSetAdd)}, + ) + case *ir.ReturnLocalStmt: + instrs = append(instrs, + instruction.GetLocal{Index: c.local(stmt.Source)}, + instruction.Return{}, + ) + case *ir.BlockStmt: + for i := range stmt.Blocks { + block, err := c.compileBlock(stmt.Blocks[i]) + if err != nil { + return nil, err + } + if withControlInstr(block) { + instrs = append(instrs, instruction.Block{Instrs: block}) + } else { + instrs = append(instrs, block...) + } + } + case *ir.BreakStmt: + instrs = append(instrs, instruction.Br{Index: stmt.Index}) + case *ir.CallStmt: + if err := c.compileCallStmt(stmt, &instrs); err != nil { + return nil, err + } + case *ir.CallDynamicStmt: + if err := c.compileCallDynamicStmt(stmt, &instrs); err != nil { + return nil, err + } + case *ir.WithStmt: + if err := c.compileWithStmt(stmt, &instrs); err != nil { + return instrs, err + } + case *ir.AssignVarStmt: + instrs = append(instrs, + c.instrRead(stmt.Source), + instruction.SetLocal{Index: c.local(stmt.Target)}, + ) + case *ir.AssignVarOnceStmt: + instrs = append(instrs, instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.Block{ + Instrs: append([]instruction.Instruction{ + instruction.GetLocal{Index: c.local(stmt.Target)}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 0}, + instruction.GetLocal{Index: c.local(stmt.Target)}, + c.instrRead(stmt.Source), + instruction.Call{Index: c.function(opaValueCompare)}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 1}, + }, + c.runtimeErrorAbort(stmt.Location, errVarAssignConflict)...), + }, + c.instrRead(stmt.Source), + instruction.SetLocal{Index: c.local(stmt.Target)}, + }, + }) + case *ir.AssignIntStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Target)}) + instrs = append(instrs, instruction.I64Const{Value: stmt.Value}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueNumberSetInt)}) + case *ir.ScanStmt: + if err := c.compileScan(stmt, &instrs); err != nil { + return nil, err + } + case *ir.NopStmt: + instrs = append(instrs, instruction.Nop{}) + case *ir.NotStmt: + if err := c.compileNot(stmt, &instrs); err != nil { + return nil, err + } + case *ir.DotStmt: + if loc, ok := stmt.Source.Value.(ir.Local); ok { + instrs = append(instrs, instruction.GetLocal{Index: c.local(loc)}) + instrs = append(instrs, c.instrRead(stmt.Key)) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueGet)}) + instrs = append(instrs, instruction.TeeLocal{Index: c.local(stmt.Target)}) + instrs = append(instrs, instruction.I32Eqz{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + } else { + // Booleans and string sources would lead to the BrIf (since opa_value_get + // on them returns 0), so let's skip trying that. + instrs = append(instrs, instruction.Br{Index: 0}) + break + } + case *ir.LenStmt: + instrs = append(instrs, c.instrRead(stmt.Source)) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueLength)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaNumberSize)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.EqualStmt: + instrs = append(instrs, c.instrRead(stmt.A)) + instrs = append(instrs, c.instrRead(stmt.B)) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueCompare)}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.NotEqualStmt: + instrs = append(instrs, c.instrRead(stmt.A)) + instrs = append(instrs, c.instrRead(stmt.B)) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueCompare)}) + instrs = append(instrs, instruction.I32Eqz{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.MakeNullStmt: + instrs = append(instrs, instruction.Call{Index: c.function(opaNull)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeNumberIntStmt: + instrs = append(instrs, instruction.I64Const{Value: stmt.Value}) + instrs = append(instrs, instruction.Call{Index: c.function(opaNumberInt)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeNumberRefStmt: + instrs = append(instrs, instruction.I32Const{Value: c.stringAddr(stmt.Index)}) + instrs = append(instrs, instruction.I32Const{Value: int32(len(c.policy.Static.Strings[stmt.Index].Value))}) + instrs = append(instrs, instruction.Call{Index: c.function(opaNumberRef)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeArrayStmt: + instrs = append(instrs, instruction.I32Const{Value: stmt.Capacity}) + instrs = append(instrs, instruction.Call{Index: c.function(opaArrayWithCap)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeObjectStmt: + instrs = append(instrs, instruction.Call{Index: c.function(opaObject)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeSetStmt: + instrs = append(instrs, instruction.Call{Index: c.function(opaSet)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.IsArrayStmt: + 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: opaTypeArray}) + instrs = append(instrs, instruction.I32Ne{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + } else { + instrs = append(instrs, instruction.Br{Index: 0}) + break + } + case *ir.IsObjectStmt: + 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: opaTypeObject}) + instrs = append(instrs, instruction.I32Ne{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + } else { + 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}) + instrs = append(instrs, instruction.I32Ne{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.ResetLocalStmt: + instrs = append(instrs, instruction.I32Const{Value: 0}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.IsDefinedStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)}) + instrs = append(instrs, instruction.I32Eqz{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.ArrayAppendStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Array)}) + instrs = append(instrs, c.instrRead(stmt.Value)) + instrs = append(instrs, instruction.Call{Index: c.function(opaArrayAppend)}) + case *ir.ObjectInsertStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Object)}) + instrs = append(instrs, c.instrRead(stmt.Key)) + instrs = append(instrs, c.instrRead(stmt.Value)) + instrs = append(instrs, instruction.Call{Index: c.function(opaObjectInsert)}) + case *ir.ObjectInsertOnceStmt: + tmp := c.genLocal() + instrs = append(instrs, instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.Block{ + Instrs: append([]instruction.Instruction{ + instruction.GetLocal{Index: c.local(stmt.Object)}, + c.instrRead(stmt.Key), + instruction.Call{Index: c.function(opaValueGet)}, + instruction.TeeLocal{Index: tmp}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 0}, + instruction.GetLocal{Index: tmp}, + c.instrRead(stmt.Value), + instruction.Call{Index: c.function(opaValueCompare)}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 1}, + }, c.runtimeErrorAbort(stmt.Location, errObjectInsertConflict)...), + }, + instruction.GetLocal{Index: c.local(stmt.Object)}, + c.instrRead(stmt.Key), + c.instrRead(stmt.Value), + instruction.Call{Index: c.function(opaObjectInsert)}, + }, + }) + case *ir.ObjectMergeStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.A)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.B)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueMerge)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.SetAddStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Set)}) + instrs = append(instrs, c.instrRead(stmt.Value)) + instrs = append(instrs, instruction.Call{Index: c.function(opaSetAdd)}) + default: + var buf bytes.Buffer + err := ir.Pretty(&buf, stmt) + if err != nil { + return nil, err + } + return instrs, fmt.Errorf("illegal statement: %v", buf.String()) + } + } + + return instrs, nil +} + +func (c *Compiler) compileScan(scan *ir.ScanStmt, result *[]instruction.Instruction) error { + var instrs = *result + instrs = append(instrs, instruction.I32Const{Value: 0}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(scan.Key)}) + body, err := c.compileScanBlock(scan) + if err != nil { + return err + } + instrs = append(instrs, instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.Loop{Instrs: body}, + }, + }) + *result = instrs + return nil +} + +func (c *Compiler) compileScanBlock(scan *ir.ScanStmt) ([]instruction.Instruction, error) { + var instrs []instruction.Instruction + + // Execute iterator. + instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Source)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Key)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueIter)}) + + // Check for emptiness. + instrs = append(instrs, instruction.TeeLocal{Index: c.local(scan.Key)}) + instrs = append(instrs, instruction.I32Eqz{}) + instrs = append(instrs, instruction.BrIf{Index: 1}) + + // Load value. + instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Source)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Key)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueGet)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(scan.Value)}) + + // Loop body. + nested, err := c.compileBlock(scan.Block) + if err != nil { + return nil, err + } + + // Continue. + instrs = append(instrs, nested...) + instrs = append(instrs, instruction.Br{Index: 0}) + + return instrs, nil +} + +func (c *Compiler) compileNot(not *ir.NotStmt, result *[]instruction.Instruction) error { + var instrs = *result + + // generate and initialize condition variable + cond := c.genLocal() + instrs = append(instrs, instruction.I32Const{Value: 1}) + instrs = append(instrs, instruction.SetLocal{Index: cond}) + + nested, err := c.compileBlock(not.Block) + if err != nil { + return err + } + + // unset condition variable if end of block is reached + nested = append(nested, instruction.I32Const{Value: 0}) + nested = append(nested, instruction.SetLocal{Index: cond}) + instrs = append(instrs, instruction.Block{Instrs: nested}) + + // break out of block if condition variable was unset + instrs = append(instrs, instruction.GetLocal{Index: cond}) + instrs = append(instrs, instruction.I32Eqz{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + + *result = instrs + return nil +} + +func (c *Compiler) compileWithStmt(with *ir.WithStmt, result *[]instruction.Instruction) error { + + var instrs = *result + save := c.genLocal() + instrs = append(instrs, instruction.Call{Index: c.function(opaMemoizePush)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(with.Local)}) + instrs = append(instrs, instruction.SetLocal{Index: save}) + + if len(with.Path) == 0 { + instrs = append(instrs, c.instrRead(with.Value)) + instrs = append(instrs, instruction.SetLocal{Index: c.local(with.Local)}) + } else { + instrs = c.compileUpsert(with.Local, with.Path, with.Value, with.Location, instrs) + } + + undefined := c.genLocal() + instrs = append(instrs, instruction.I32Const{Value: 1}) + instrs = append(instrs, instruction.SetLocal{Index: undefined}) + + nested, err := c.compileBlock(with.Block) + if err != nil { + return err + } + + nested = append(nested, instruction.I32Const{Value: 0}) + nested = append(nested, instruction.SetLocal{Index: undefined}) + instrs = append(instrs, instruction.Block{Instrs: nested}) + instrs = append(instrs, instruction.GetLocal{Index: save}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(with.Local)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaMemoizePop)}) + instrs = append(instrs, instruction.GetLocal{Index: undefined}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + + *result = instrs + + return nil +} + +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)}) + instrs = append(instrs, instruction.SetLocal{Index: lcopy}) + + // Shallow copy the local if defined otherwise initialize to an empty object. + instrs = append(instrs, instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.Block{Instrs: []instruction.Instruction{ + instruction.GetLocal{Index: lcopy}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 0}, + instruction.GetLocal{Index: lcopy}, + instruction.Call{Index: c.function(opaValueShallowCopy)}, + instruction.TeeLocal{Index: lcopy}, + instruction.SetLocal{Index: c.local(local)}, + instruction.Br{Index: 1}, + }}, + instruction.Call{Index: c.function(opaObject)}, + instruction.TeeLocal{Index: lcopy}, + instruction.SetLocal{Index: c.local(local)}, + }, + }) + + // Initialize the locals that specify the path of the upsert operation. + lpath := make(map[int]uint32, len(path)) + + for i := range path { + lpath[i] = c.genLocal() + instrs = append(instrs, instruction.I32Const{Value: c.opaStringAddr(path[i])}) + instrs = append(instrs, instruction.SetLocal{Index: lpath[i]}) + } + + // Generate a block that traverses the path of the upsert operation, + // shallowing copying values at each step as needed. Stop before the final + // segment that will only be inserted. + inner := make([]instruction.Instruction, 0, len(path)*21+1) + ltemp := c.genLocal() + + for i := range len(path) - 1 { + + // Lookup the next part of the path. + inner = append(inner, instruction.GetLocal{Index: lcopy}) + inner = append(inner, instruction.GetLocal{Index: lpath[i]}) + inner = append(inner, instruction.Call{Index: c.function(opaValueGet)}) + inner = append(inner, instruction.SetLocal{Index: ltemp}) + + // If the next node is missing, break. + inner = append(inner, instruction.GetLocal{Index: ltemp}) + inner = append(inner, instruction.I32Eqz{}) + inner = append(inner, instruction.BrIf{Index: uint32(i)}) + + // If the next node is not an object, break. + inner = append(inner, instruction.GetLocal{Index: ltemp}) + inner = append(inner, instruction.Call{Index: c.function(opaValueType)}) + inner = append(inner, instruction.I32Const{Value: opaTypeObject}) + inner = append(inner, instruction.I32Ne{}) + inner = append(inner, instruction.BrIf{Index: uint32(i)}) + + // Otherwise, shallow copy the next node node and insert into the copy + // before continuing. + inner = append(inner, instruction.GetLocal{Index: ltemp}) + inner = append(inner, instruction.Call{Index: c.function(opaValueShallowCopy)}) + inner = append(inner, instruction.SetLocal{Index: ltemp}) + inner = append(inner, instruction.GetLocal{Index: lcopy}) + inner = append(inner, instruction.GetLocal{Index: lpath[i]}) + inner = append(inner, instruction.GetLocal{Index: ltemp}) + inner = append(inner, instruction.Call{Index: c.function(opaObjectInsert)}) + inner = append(inner, instruction.GetLocal{Index: ltemp}) + inner = append(inner, instruction.SetLocal{Index: lcopy}) + } + + inner = append(inner, instruction.Br{Index: uint32(len(path) - 1)}) + + // Generate blocks that handle missing nodes during traversal. + block := make([]instruction.Instruction, 0, len(path)*10) + lval := c.genLocal() + + for i := range len(path) - 1 { + block = append(block, instruction.Block{Instrs: inner}) + block = append(block, instruction.Call{Index: c.function(opaObject)}) + block = append(block, instruction.SetLocal{Index: lval}) + block = append(block, instruction.GetLocal{Index: lcopy}) + block = append(block, instruction.GetLocal{Index: lpath[i]}) + block = append(block, instruction.GetLocal{Index: lval}) + block = append(block, instruction.Call{Index: c.function(opaObjectInsert)}) + block = append(block, instruction.GetLocal{Index: lval}) + block = append(block, instruction.SetLocal{Index: lcopy}) + inner = block + block = nil + } + + // Finish by inserting the statement's value into the shallow copied node. + instrs = append(instrs, instruction.Block{Instrs: inner}) + instrs = append(instrs, instruction.GetLocal{Index: lcopy}) + instrs = append(instrs, instruction.GetLocal{Index: lpath[len(path)-1]}) + instrs = append(instrs, c.instrRead(value)) + instrs = append(instrs, instruction.Call{Index: c.function(opaObjectInsert)}) + + return instrs +} + +func (c *Compiler) compileCallDynamicStmt(stmt *ir.CallDynamicStmt, result *[]instruction.Instruction) error { + instrs := []instruction.Instruction{} + larray := c.genLocal() + lidx := c.genLocal() + + // init array: + instrs = append(instrs, + instruction.I32Const{Value: int32(len(stmt.Path))}, + instruction.Call{Index: c.function(opaArrayWithCap)}, + instruction.SetLocal{Index: larray}, + ) + + // append to it: + for _, lv := range stmt.Path { + instrs = append(instrs, + instruction.GetLocal{Index: larray}, + c.instrRead(lv), + instruction.Call{Index: c.function(opaArrayAppend)}, + ) + } + + // prep stack for later call_indirect + for _, arg := range stmt.Args { + instrs = append(instrs, instruction.GetLocal{Index: c.local(arg)}) + } + + tpe := module.FunctionType{ + Params: []types.ValueType{types.I32, types.I32}, // data, input + Results: []types.ValueType{types.I32}, + } + typeIndex := c.emitFunctionType(tpe) + + instrs = append(instrs, + // lookup elem idx via larray path + instruction.GetLocal{Index: larray}, + instruction.Call{Index: c.function(opaMappingLookup)}, // [arg0 arg1 larray] -> [arg0 arg1 tbl_idx] + instruction.TeeLocal{Index: lidx}, + instruction.I32Eqz{}, // mapping not found + instruction.BrIf{Index: 0}, // check data + + instruction.GetLocal{Index: lidx}, + instruction.CallIndirect{Index: typeIndex}, // [arg0 arg1 tbl_idx] -> [res] + instruction.TeeLocal{Index: c.local(stmt.Result)}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 3}, // mapping found, "undefined" result counts + ) + + *result = append(*result, instrs...) + return nil +} + +func (c *Compiler) compileCallStmt(stmt *ir.CallStmt, result *[]instruction.Instruction) error { + + fn := stmt.Func + + if name, ok := builtinsFunctions[stmt.Func]; ok { + fn = name + } + + if index, ok := c.funcs[fn]; ok { + return c.compileInternalCall(stmt, index, result) + } + + if ef, ok := c.externalFuncs[fn]; ok { + return c.compileExternalCall(stmt, ef, result) + } + + c.errors = append(c.errors, fmt.Errorf("undefined function: %q", fn)) + + return nil +} + +func (c *Compiler) compileInternalCall(stmt *ir.CallStmt, index uint32, result *[]instruction.Instruction) error { + + instrs := []instruction.Instruction{} + + // Prepare function args and call. + for _, arg := range stmt.Args { + instrs = append(instrs, c.instrRead(arg)) + } + + instrs = append(instrs, + instruction.Call{Index: index}, + instruction.TeeLocal{Index: c.local(stmt.Result)}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 0}) + + *result = append(*result, instrs...) + + return nil +} + +func (c *Compiler) compileExternalCall(stmt *ir.CallStmt, ef externalFunc, result *[]instruction.Instruction) error { + + if len(stmt.Args) >= len(builtinDispatchers) { + c.errors = append(c.errors, fmt.Errorf("too many built-in call arguments: %q", stmt.Func)) + return nil + } + + instrs := *result + instrs = append(instrs, instruction.I32Const{Value: ef.ID}, instruction.I32Const{Value: 0}) // unused context parameter + + for _, arg := range stmt.Args { + instrs = append(instrs, c.instrRead(arg)) + } + + instrs = append(instrs, instruction.Call{Index: c.function(builtinDispatchers[len(stmt.Args)])}) + + if ef.Decl.Result() != nil { + instrs = append(instrs, + instruction.TeeLocal{Index: c.local(stmt.Result)}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 0}, + ) + } else { + instrs = append(instrs, instruction.Drop{}) + } + + *result = instrs + return nil +} + +func (c *Compiler) emitFunctionDecl(name string, tpe module.FunctionType, export bool) { + + var idx uint32 + if old, ok := c.funcs[name]; ok { + c.debug.Printf("function declaration for %v is being emitted multiple times (overwriting old index %d)", name, old) + idx = old + } else { + typeIndex := c.emitFunctionType(tpe) + c.module.Function.TypeIndices = append(c.module.Function.TypeIndices, typeIndex) + c.module.Code.Segments = append(c.module.Code.Segments, module.RawCodeSegment{}) + idx = uint32((len(c.module.Function.TypeIndices) - 1) + c.functionImportCount()) + c.funcs[name] = idx + } + + if export { + c.module.Export.Exports = append(c.module.Export.Exports, module.Export{ + Name: name, + Descriptor: module.ExportDescriptor{ + Type: module.FunctionExportType, + Index: idx, + }, + }) + } + + // add functions 'name' entry + var found bool + for _, m := range c.module.Names.Functions { + if m.Index == idx { + found = true + } + } + if !found { + c.module.Names.Functions = append(c.module.Names.Functions, module.NameMap{ + Index: idx, + Name: name, + }) + } +} + +func (c *Compiler) emitFunctionType(tpe module.FunctionType) uint32 { + for i, other := range c.module.Type.Functions { + if tpe.Equal(other) { + return uint32(i) + } + } + c.module.Type.Functions = append(c.module.Type.Functions, tpe) + return uint32(len(c.module.Type.Functions) - 1) +} + +func (c *Compiler) emitFunction(name string, entry *module.CodeEntry) error { + var buf bytes.Buffer + if err := encoding.WriteCodeEntry(&buf, entry); err != nil { + return err + } + index := c.function(name) - uint32(c.functionImportCount()) + c.module.Code.Segments[index].Code = buf.Bytes() + return nil +} + +// emitFuncs writes the compiled (and optimized) functions' code into the +// module +func (c *Compiler) emitFuncs() error { + for _, fn := range c.funcsCode { + if err := c.emitFunction(fn.name, fn.code); err != nil { + return fmt.Errorf("write function %s: %w", fn.name, err) + } + } + return nil +} + +func (c *Compiler) functionImportCount() int { + var count int + + for _, imp := range c.module.Import.Imports { + if imp.Descriptor.Kind() == module.FunctionImportType { + count++ + } + } + + return count +} + +func (c *Compiler) stringAddr(index int) int32 { + return int32(c.stringAddrs[index]) +} + +func (c *Compiler) builtinStringAddr(code int) int32 { + return int32(c.builtinStringAddrs[code]) +} + +func (c *Compiler) opaStringAddr(index int) int32 { + return int32(c.opaStringAddrs[index]) +} + +func (c *Compiler) opaBoolAddr(b ir.Bool) int32 { + return int32(c.opaBoolAddrs[b]) +} + +func (c *Compiler) fileAddr(code int) int32 { + return int32(c.fileAddrs[code]) +} + +func (c *Compiler) local(l ir.Local) uint32 { + var u32 uint32 + var exist bool + if u32, exist = c.locals[l]; !exist { + u32 = c.nextLocal + c.locals[l] = u32 + c.nextLocal++ + } + return u32 +} + +func (c *Compiler) genLocal() uint32 { + l := c.nextLocal + c.nextLocal++ + return l +} + +func (c *Compiler) function(name string) uint32 { + fidx, ok := c.funcs[name] + if !ok { + panic("function not found: " + name) + } + return fidx +} + +func (c *Compiler) appendInstr(instr instruction.Instruction) { + c.code.Func.Expr.Instrs = append(c.code.Func.Expr.Instrs, instr) +} + +func (c *Compiler) appendInstrs(instrs []instruction.Instruction) { + for _, instr := range instrs { + c.appendInstr(instr) + } +} + +func getLowestFreeDataSegmentOffset(m *module.Module) (int32, error) { + + var offset int32 + + for i := range m.Data.Segments { + + if len(m.Data.Segments[i].Offset.Instrs) != 1 { + return 0, errors.New("bad data segment offset instructions") + } + + instr, ok := m.Data.Segments[i].Offset.Instrs[0].(instruction.I32Const) + if !ok { + return 0, errors.New("bad data segment offset expr") + } + + // NOTE(tsandall): assume memory up to but not including addr is taken. + addr := instr.Value + int32(len(m.Data.Segments[i].Init)) + if addr > offset { + offset = addr + } + } + + return offset, nil +} + +func getLowestFreeElementSegmentOffset(m *module.Module) (int32, error) { + var offset int32 + + for _, seg := range m.Element.Segments { + if len(seg.Offset.Instrs) != 1 { + return 0, errors.New("bad data segment offset instructions") + } + + instr, ok := seg.Offset.Instrs[0].(instruction.I32Const) + if !ok { + return 0, errors.New("bad data segment offset expr") + } + + addr := instr.Value + int32(len(seg.Indices)) + if addr > offset { + offset = addr + } + } + + return offset, nil +} + +// runtimeErrorAbort uses the passed source location to build the +// arguments for a call to opa_runtime_error(file, row, col, msg). +// It returns the instructions that make up the function call with +// arguments, followed by Unreachable. +func (c *Compiler) runtimeErrorAbort(loc ir.Location, errType int) []instruction.Instruction { + index, row, col := loc.File, loc.Row, loc.Col + return []instruction.Instruction{ + instruction.I32Const{Value: c.fileAddr(index)}, + instruction.I32Const{Value: int32(row)}, + instruction.I32Const{Value: int32(col)}, + instruction.I32Const{Value: c.builtinStringAddr(errType)}, + instruction.Call{Index: c.function(opaRuntimeError)}, + instruction.Unreachable{}, + } +} + +func (c *Compiler) storeFunc(name string, code *module.CodeEntry) error { + for _, fn := range c.funcsCode { + if fn.name == name { + return fmt.Errorf("duplicate function entry %s", name) + } + } + c.funcsCode = append(c.funcsCode, funcCode{name: name, code: code}) + return nil +} + +func (c *Compiler) instrRead(lv ir.Operand) instruction.Instruction { + switch x := lv.Value.(type) { + case ir.Bool: + return instruction.I32Const{Value: c.opaBoolAddr(x)} + case ir.StringIndex: + return instruction.I32Const{Value: c.opaStringAddr(int(x))} + case ir.Local: + return instruction.GetLocal{Index: c.local(x)} + } + panic("unreachable") +} diff --git a/third_party/opa/internal/compiler/wasm/wasm_test.go b/third_party/opa/internal/compiler/wasm/wasm_test.go new file mode 100644 index 000000000000..2d78ab9fe5f4 --- /dev/null +++ b/third_party/opa/internal/compiler/wasm/wasm_test.go @@ -0,0 +1,95 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package wasm + +import ( + "testing" + + "github.com/open-policy-agent/opa/internal/planner" + "github.com/open-policy-agent/opa/internal/wasm/instruction" + "github.com/open-policy-agent/opa/internal/wasm/module" + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestCompilerHelloWorld(t *testing.T) { + + policy, err := planner.New(). + WithQueries([]planner.QuerySet{ + { + Name: "test", + Queries: []ast.Body{ast.MustParseBody(`input.foo = 1`)}, + }, + }).Plan() + + if err != nil { + t.Fatal(err) + } + + c := New().WithPolicy(policy) + _, err = c.Compile() + if err != nil { + t.Fatal(err) + } +} + +func TestCompilerBadDataSegment(t *testing.T) { + + result, err := getLowestFreeDataSegmentOffset(&module.Module{}) + if err != nil || result != 0 { + t.Fatal("expected zero but got:", result, "err:", err) + } + + _, err = getLowestFreeDataSegmentOffset(&module.Module{Data: module.DataSection{ + Segments: []module.DataSegment{ + { + Offset: module.Expr{ + Instrs: []instruction.Instruction{}, + }, + }, + }, + }}) + if err == nil || err.Error() != "bad data segment offset instructions" { + t.Fatal("unexpected err:", err) + } + + _, err = getLowestFreeDataSegmentOffset(&module.Module{Data: module.DataSection{ + Segments: []module.DataSegment{ + { + Offset: module.Expr{ + Instrs: []instruction.Instruction{ + instruction.I64Const{Value: 100}, + }, + }, + }, + }, + }}) + if err == nil || err.Error() != "bad data segment offset expr" { + t.Fatal("unexpected err:", err) + } + + result, err = getLowestFreeDataSegmentOffset(&module.Module{Data: module.DataSection{ + Segments: []module.DataSegment{ + { + Init: []byte("foo"), + Offset: module.Expr{ + Instrs: []instruction.Instruction{ + instruction.I32Const{Value: 106}, + }, + }, + }, + { + Init: []byte("bar"), + Offset: module.Expr{ + Instrs: []instruction.Instruction{ + instruction.I32Const{Value: 100}, + }, + }, + }, + }, + }}) + if err != nil || result != 109 { + t.Fatal("expected 106 but got:", result, "err:", err) + } +} diff --git a/third_party/opa/internal/config/config.go b/third_party/opa/internal/config/config.go new file mode 100644 index 000000000000..7b92a95b7f8b --- /dev/null +++ b/third_party/opa/internal/config/config.go @@ -0,0 +1,175 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package config implements helper functions to parse OPA's configuration. +package config + +import ( + "encoding/json" + "fmt" + "os" + "regexp" + "strings" + + "sigs.k8s.io/yaml" + + "github.com/open-policy-agent/opa/internal/strvals" + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/plugins/rest" + "github.com/open-policy-agent/opa/v1/tracing" + "github.com/open-policy-agent/opa/v1/util" +) + +// ServiceOptions stores the options passed to ParseServicesConfig +type ServiceOptions struct { + Raw json.RawMessage + AuthPlugin rest.AuthPluginLookupFunc + Keys map[string]*keys.Config + Logger logging.Logger + DistributedTacingOpts tracing.Options +} + +// ParseServicesConfig returns a set of named service clients. The service +// clients can be specified either as an array or as a map. Some systems (e.g., +// Helm) do not have proper support for configuration values nested under +// arrays, so just support both here. +func ParseServicesConfig(opts ServiceOptions) (map[string]rest.Client, error) { + + services := map[string]rest.Client{} + + var arr []json.RawMessage + var obj map[string]json.RawMessage + + if err := util.Unmarshal(opts.Raw, &arr); err == nil { + for _, s := range arr { + client, err := rest.New(s, opts.Keys, rest.AuthPluginLookup(opts.AuthPlugin), rest.Logger(opts.Logger), rest.DistributedTracingOpts(opts.DistributedTacingOpts)) + if err != nil { + return nil, err + } + services[client.Service()] = client + } + } else if util.Unmarshal(opts.Raw, &obj) == nil { + for k := range obj { + client, err := rest.New(obj[k], opts.Keys, rest.Name(k), rest.AuthPluginLookup(opts.AuthPlugin), rest.Logger(opts.Logger), rest.DistributedTracingOpts(opts.DistributedTacingOpts)) + if err != nil { + return nil, err + } + services[client.Service()] = client + } + } else { + // Return error from array decode as that is the default format. + return nil, err + } + + return services, nil +} + +// Load implements configuration file loading. The supplied config file will be +// read from disk (if specified) and overrides will be applied. If no config file is +// specified, the overrides can still be applied to an empty config. +func Load(configFile string, overrides []string, overrideFiles []string) ([]byte, error) { + baseConf := map[string]any{} + + // User specified config file + if configFile != "" { + var bytes []byte + var err error + bytes, err = os.ReadFile(configFile) + if err != nil { + return nil, err + } + + processedConf := subEnvVars(string(bytes)) + + if err := yaml.Unmarshal([]byte(processedConf), &baseConf); err != nil { + return nil, fmt.Errorf("failed to parse %s: %s", configFile, err) + } + } + + overrideConf := map[string]any{} + + // User specified a config override via --set + for _, override := range overrides { + processedOverride := subEnvVars(override) + if err := strvals.ParseInto(processedOverride, overrideConf); err != nil { + return nil, fmt.Errorf("failed parsing --set data: %s", err) + } + } + + // User specified a config override value via --set-file + for _, override := range overrideFiles { + reader := func(rs []rune) (any, error) { + bytes, err := os.ReadFile(string(rs)) + value := strings.TrimSpace(string(bytes)) + return value, err + } + if err := strvals.ParseIntoFile(override, overrideConf, reader); err != nil { + return nil, fmt.Errorf("failed parsing --set-file data: %s", err) + } + } + + // Merge together base config file and overrides, prefer the overrides + conf := mergeValues(baseConf, overrideConf) + + // Take the patched config and marshal back to YAML + return yaml.Marshal(conf) +} + +// regex looking for ${...} notation strings +var envRegex = regexp.MustCompile(`(?U:\${.*})`) + +// SubEnvVars will look for any environment variables in the passed in string +// with the syntax of ${VAR_NAME} and replace that string with ENV[VAR_NAME] +func SubEnvVars(s string) string { + return subEnvVars(s) +} + +func subEnvVars(s string) string { + updatedConfig := envRegex.ReplaceAllStringFunc(s, func(s string) string { + // Trim off the '${' and '}' + if len(s) <= 3 { + // This should never happen.. + return "" + } + varName := s[2 : len(s)-1] + + // Lookup the variable in the environment. We do not + // play by bash rules: if its undefined we'll keep it + // as-is, it could be replaced somewhere down the line. + if lu := os.Getenv(varName); lu != "" { + return lu + } + return s + }) + + return updatedConfig +} + +// mergeValues will merge source and destination map, preferring values from the source map +func mergeValues(dest map[string]any, src map[string]any) map[string]any { + for k, v := range src { + // If the key doesn't exist already, then just set the key to that value + if _, exists := dest[k]; !exists { + dest[k] = v + continue + } + nextMap, ok := v.(map[string]any) + // If it isn't another map, overwrite the value + if !ok { + dest[k] = v + continue + } + // Edge case: If the key exists in the destination, but isn't a map + destMap, isMap := dest[k].(map[string]any) + // If the source map has a map for this key, prefer it + if !isMap { + dest[k] = v + continue + } + // If we got to this point, it is a map in both, so merge them + dest[k] = mergeValues(destMap, nextMap) + } + return dest +} diff --git a/third_party/opa/internal/config/config_test.go b/third_party/opa/internal/config/config_test.go new file mode 100644 index 000000000000..e97bb19f755a --- /dev/null +++ b/third_party/opa/internal/config/config_test.go @@ -0,0 +1,486 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: goconst // string duplication is for test readability. +package config + +import ( + "fmt" + "os" + "path/filepath" + "reflect" + "testing" + + "sigs.k8s.io/yaml" + + "github.com/open-policy-agent/opa/v1/util/test" +) + +func setTestEnvVar(t *testing.T, name, value string) string { + envKey := fmt.Sprintf("%s_%s", t.Name(), name) + t.Setenv(envKey, value) + return envKey +} + +func TestSubEnvVarsVarsSubOne(t *testing.T) { + envKey := setTestEnvVar(t, "var1", "foo") + configYaml := fmt.Sprintf("field1: ${%s}", envKey) + + expected := "field1: foo" + + actual := subEnvVars(configYaml) + + if actual != expected { + t.Errorf("Expected: '%s'\nActual: '%s'", expected, actual) + } +} + +func TestSubEnvVarsVarsSubMulti(t *testing.T) { + urlEnvKey := setTestEnvVar(t, "SERVICE_URL", "https://example.com/control-plane-api/v1") + tokenEnvKey := setTestEnvVar(t, "BEARER_TOKEN", "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm") + configYaml := fmt.Sprintf(` + services: + - name: acmecorp + url: ${%s} + credentials: + bearer: + token: "${%s}" + + discovery: + name: /example/discovery + prefix: configuration`, urlEnvKey, tokenEnvKey) + + expected := ` + services: + - name: acmecorp + url: https://example.com/control-plane-api/v1 + credentials: + bearer: + token: "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm" + + discovery: + name: /example/discovery + prefix: configuration` + + actual := subEnvVars(configYaml) + + if actual != expected { + t.Errorf("\nExpected: '%s'\nActual: '%s'", expected, actual) + } +} + +func TestSubEnvVarsVarsNoVars(t *testing.T) { + configYaml := "field1: foo" + expected := "field1: foo" + + actual := subEnvVars(configYaml) + + if actual != expected { + t.Errorf("Expected: '%s'\nActual: '%s'", expected, actual) + } +} + +func TestSubEnvVarsVarsEmptyString(t *testing.T) { + configYaml := "" + expected := "" + + actual := subEnvVars(configYaml) + + if actual != expected { + t.Errorf("Expected: '%s'\nActual: '%s'", expected, actual) + } +} + +func TestSubEnvVarsVarsSubMissingEnvVar(t *testing.T) { + envKey := setTestEnvVar(t, "var1", "foo") + configYaml := fmt.Sprintf("field1: '${%s}'", envKey) + + // Remove the env var and expect the system to sub in "" + os.Unsetenv(envKey) + expected := configYaml // untouched + + actual := subEnvVars(configYaml) + + if actual != expected { + t.Errorf("Expected: '%s'\nActual: '%s'", expected, actual) + } +} + +func TestSubEnvVarsVarsSubEmptyVarName(t *testing.T) { + configYaml := "field1: '${}'" + expected := "field1: ''" + + actual := subEnvVars(configYaml) + + if actual != expected { + t.Errorf("Expected: '%s'\nActual: '%s'", expected, actual) + } +} + +func TestMergeValuesNoOverride(t *testing.T) { + dest := map[string]any{} + src := map[string]any{ + "a": map[string]any{ + "b": "foo", + }, + } + + actual := mergeValues(dest, src) + + expected := map[string]any{ + "a": map[string]any{ + "b": "foo", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("merged map does not match expected:\n\nExpected: %+v\nActual: %+v", expected, actual) + } +} + +func TestMergeValuesOverrideSingle(t *testing.T) { + dest := map[string]any{ + "a": "bar", + } + src := map[string]any{ + "a": "override-value", + } + + actual := mergeValues(dest, src) + + expected := map[string]any{ + "a": "override-value", + } + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("merged map does not match expected:\n\nExpected: %+v\nActual: %+v", expected, actual) + } +} + +func TestMergeValuesOverrideSingleNested(t *testing.T) { + dest := map[string]any{ + "a": map[string]any{ + "b": "foo", + }, + } + src := map[string]any{ + "a": map[string]any{ + "b": "override-value", + }, + } + + actual := mergeValues(dest, src) + + expected := map[string]any{ + "a": map[string]any{ + "b": "override-value", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("merged map does not match expected:\n\nExpected: %+v\nActual: %+v", expected, actual) + } +} + +func TestMergeValuesOverrideMultipleNested(t *testing.T) { + dest := map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "k1": "v1", + "k2": "v2", + "k3": "v3", + "k4": "v4", + }, + }, + } + src := map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "k1": "v1-override", + "k4": "v4-override", + }, + }, + } + + actual := mergeValues(dest, src) + + expected := map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "k1": "v1-override", + "k2": "v2", + "k3": "v3", + "k4": "v4-override", + }, + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("merged map does not match expected:\n\nExpected: %+v\nActual: %+v", expected, actual) + } +} + +func TestMergeValuesOverrideSingleList(t *testing.T) { + dest := map[string]any{ + "a": map[string]any{ + "b": []map[string]any{ + { + "k1": "v1", + "k2": "v2", + }, + }, + }, + } + src := map[string]any{ + "a": map[string]any{ + "b": []map[string]any{ + { + "k3": "v3", + }, + }, + }, + } + + actual := mergeValues(dest, src) + + // The list index 0 should have been replaced instead of merging the sub objects + expected := map[string]any{ + "a": map[string]any{ + "b": []map[string]any{ + { + "k3": "v3", + }, + }, + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("merged map does not match expected:\n\nExpected: %+v\nActual: %+v", expected, actual) + } +} + +func TestMergeValuesNoSrc(t *testing.T) { + dest := map[string]any{ + "a": map[string]any{ + "b": "foo", + }, + } + src := map[string]any{} + + actual := mergeValues(dest, src) + + expected := map[string]any{ + "a": map[string]any{ + "b": "foo", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("merged map does not match expected:\n\nExpected: %+v\nActual: %+v", expected, actual) + } +} + +func TestMergeValuesNoSrcOrDest(t *testing.T) { + dest := map[string]any{} + src := map[string]any{} + + actual := mergeValues(dest, src) + + expected := map[string]any{} + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("merged map does not match expected:\n\nExpected: %+v\nActual: %+v", expected, actual) + } +} + +func TestLoadConfigWithParamOverride(t *testing.T) { + fs := map[string]string{"/some/config.yaml": ` +services: + acmecorp: + url: https://example.com/control-plane-api/v1 + +discovery: + name: /example/discovery + prefix: configuration +`} + + test.WithTempFS(fs, func(rootDir string) { + configFile := filepath.Join(rootDir, "some", "config.yaml") + configOverrides := []string{"services.acmecorp.credentials.bearer.token=bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm"} + + configBytes, err := Load(configFile, configOverrides, nil) + if err != nil { + t.Errorf("unexpected error loading config: %s", err.Error()) + } + + config := map[string]any{} + err = yaml.Unmarshal(configBytes, &config) + if err != nil { + t.Errorf("unexpected error unmarshalling config") + } + + expected := map[string]any{ + "services": map[string]any{ + "acmecorp": map[string]any{ + "url": "https://example.com/control-plane-api/v1", + "credentials": map[string]any{ + "bearer": map[string]any{ + "token": "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm", + }, + }, + }, + }, + "discovery": map[string]any{ + "name": "/example/discovery", + "prefix": "configuration", + }, + } + + if !reflect.DeepEqual(config, expected) { + t.Errorf("config does not match expected:\n\nExpected: %+v\nActual: %+v", expected, config) + } + }) +} + +func TestLoadConfigWithFileOverride(t *testing.T) { + fs := map[string]string{ + "/some/config.yaml": ` +services: + acmecorp: + url: https://example.com/control-plane-api/v1 + credentials: + bearer: + token: "XXXXXXXXXX" + +discovery: + name: /example/discovery + prefix: configuration +`, + "/some/secret.txt": "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm", + } + + test.WithTempFS(fs, func(rootDir string) { + configFile := filepath.Join(rootDir, "some", "config.yaml") + secretFile := filepath.Join(rootDir, "some", "secret.txt") + overrideFiles := []string{"services.acmecorp.credentials.bearer.token=" + secretFile} + + configBytes, err := Load(configFile, nil, overrideFiles) + if err != nil { + t.Errorf("unexpected error loading config: %s", err.Error()) + } + + config := map[string]any{} + err = yaml.Unmarshal(configBytes, &config) + if err != nil { + t.Errorf("unexpected error unmarshalling config") + } + + expected := map[string]any{ + "services": map[string]any{ + "acmecorp": map[string]any{ + "url": "https://example.com/control-plane-api/v1", + "credentials": map[string]any{ + "bearer": map[string]any{ + "token": "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm", + }, + }, + }, + }, + "discovery": map[string]any{ + "name": "/example/discovery", + "prefix": "configuration", + }, + } + + if !reflect.DeepEqual(config, expected) { + t.Errorf("config does not match expected:\n\nExpected: %+v\nActual: %+v", expected, config) + } + }) +} + +func TestLoadConfigWithParamOverrideNoConfigFile(t *testing.T) { + configOverrides := []string{ + "services.acmecorp.url=https://example.com/control-plane-api/v1", + "services.acmecorp.credentials.bearer.token=bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm", + "discovery.name=/example/discovery", + "discovery.prefix=configuration", + } + + configBytes, err := Load("", configOverrides, nil) + if err != nil { + t.Errorf("unexpected error loading config: %s", err.Error()) + } + + config := map[string]any{} + err = yaml.Unmarshal(configBytes, &config) + if err != nil { + t.Errorf("unexpected error unmarshalling config") + } + + expected := map[string]any{ + "services": map[string]any{ + "acmecorp": map[string]any{ + "url": "https://example.com/control-plane-api/v1", + "credentials": map[string]any{ + "bearer": map[string]any{ + "token": "bGFza2RqZmxha3NkamZsa2Fqc2Rsa2ZqYWtsc2RqZmtramRmYWxkc2tm", + }, + }, + }, + }, + "discovery": map[string]any{ + "name": "/example/discovery", + "prefix": "configuration", + }, + } + + if !reflect.DeepEqual(config, expected) { + t.Errorf("config does not match expected:\n\nExpected: %+v\nActual: %+v", expected, config) + } +} + +func TestLoadConfigWithParamOverrideNoConfigFileWithEmptyObject(t *testing.T) { + configOverrides := []string{ + "services.acmecorp.url=https://example.com/control-plane-api/v1", + "services.acmecorp.headers=null", + "services.acmecorp.credentials.s3_signing.environment_credentials=null", + "decision_logs.plugin=my_plugin", + "plugins.my_plugin=null", + } + + configBytes, err := Load("", configOverrides, nil) + if err != nil { + t.Errorf("unexpected error loading config: %s", err.Error()) + } + + config := map[string]any{} + err = yaml.Unmarshal(configBytes, &config) + if err != nil { + t.Errorf("unexpected error unmarshalling config") + } + + expected := map[string]any{ + "services": map[string]any{ + "acmecorp": map[string]any{ + "url": "https://example.com/control-plane-api/v1", + "headers": map[string]any{}, + "credentials": map[string]any{ + "s3_signing": map[string]any{ + "environment_credentials": map[string]any{}, + }, + }, + }, + }, + "decision_logs": map[string]any{ + "plugin": "my_plugin", + }, + "plugins": map[string]any{ + "my_plugin": map[string]any{}, + }, + } + + if !reflect.DeepEqual(config, expected) { + t.Errorf("config does not match expected:\n\nExpected: %+v\nActual: %+v", expected, config) + } +} diff --git a/third_party/opa/internal/debug/debug.go b/third_party/opa/internal/debug/debug.go new file mode 100644 index 000000000000..9448aeb288a1 --- /dev/null +++ b/third_party/opa/internal/debug/debug.go @@ -0,0 +1,35 @@ +package debug + +import ( + "io" + "log" +) + +// Debug allows printing debug messages. +type Debug interface { + // Printf prints, with a short file:line-number prefix + Printf(format string, args ...any) + // Writer returns the writer being written to, which may be + // `io.Discard` if no debug output is requested. + Writer() io.Writer + + // Output allows tweaking the calldepth used for figuring + // out which Go source file location is the interesting one, + // i.e., which is included in the debug message. Useful for + // setting up local helper methods. + Output(calldepth int, s string) error +} + +// New returns a new `Debug` outputting to the passed `sink`. +func New(sink io.Writer) Debug { + flags := log.Lshortfile + return log.New(sink, "", flags) +} + +// Discard returns a new `Debug` that doesn't output anything. +// Note: We're not implementing the methods here with noop stubs +// since doing this way, we can propagate the "discarding" via +// `(Debug).Writer()`. +func Discard() Debug { + return New(io.Discard) +} diff --git a/third_party/opa/internal/deepcopy/deepcopy.go b/third_party/opa/internal/deepcopy/deepcopy.go new file mode 100644 index 000000000000..dc3a231bc14d --- /dev/null +++ b/third_party/opa/internal/deepcopy/deepcopy.go @@ -0,0 +1,31 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package deepcopy + +// DeepCopy performs a recursive deep copy for nested slices/maps and +// returns the copied object. Supports []any +// and map[string]any only +func DeepCopy(val any) any { + switch val := val.(type) { + case []any: + cpy := make([]any, len(val)) + for i := range cpy { + cpy[i] = DeepCopy(val[i]) + } + return cpy + case map[string]any: + return Map(val) + default: + return val + } +} + +func Map(val map[string]any) map[string]any { + cpy := make(map[string]any, len(val)) + for k := range val { + cpy[k] = DeepCopy(val[k]) + } + return cpy +} diff --git a/third_party/opa/internal/deepcopy/deepcopy_test.go b/third_party/opa/internal/deepcopy/deepcopy_test.go new file mode 100644 index 000000000000..0d2847b6d62c --- /dev/null +++ b/third_party/opa/internal/deepcopy/deepcopy_test.go @@ -0,0 +1,31 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package deepcopy + +import ( + "reflect" + "testing" +) + +func TestDeepCopyMapRoot(t *testing.T) { + target := map[string]any{ + "a": map[string]any{ + "b": []any{ + "c", + "d", + }, + "e": "f", + }, + "x": "y", + } + result := DeepCopy(target).(map[string]any) + if !reflect.DeepEqual(target, result) { + t.Fatal("Expected result of DeepCopy to be DeepEqual with original.") + } + result["a"] = "mutated" + if target["a"] == "mutated" { + t.Fatal("Expected target to remain unmutated when the DeepCopy result was mutated") + } +} diff --git a/third_party/opa/internal/distributedtracing/distributedtracing.go b/third_party/opa/internal/distributedtracing/distributedtracing.go new file mode 100644 index 000000000000..22b225ea9ffc --- /dev/null +++ b/third_party/opa/internal/distributedtracing/distributedtracing.go @@ -0,0 +1,411 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package distributedtracing + +import ( + "context" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "os" + "strings" + "time" + + "github.com/go-logr/logr" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + "google.golang.org/grpc/credentials" + + "github.com/open-policy-agent/opa/v1/config" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/util" + + // The import registers opentelemetry with the top-level `tracing` package, + // so the latter can be used from rego/topdown without an explicit build-time + // dependency. + _ "github.com/open-policy-agent/opa/v1/features/tracing" +) + +const ( + // default gRPC port defined in https://opentelemetry.io/docs/specs/otlp/#otlpgrpc-default-port + defaultGRPCAddress = "localhost:4317" + // default HTTP port defined in https://opentelemetry.io/docs/specs/otlp/#otlphttp-default-port + defaultHTTPAddress = "localhost:4318" + + defaultServiceName = "opa" + defaultSampleRatePercentage = float64(100) + defaultEncyrptionScheme = "off" + defaultEncryptionSkipVerify = false + + // the following default values are from the OpenTelemetry specs: + // https://opentelemetry.io/docs/specs/otel/trace/sdk/#batching-processor + defaultBatchSpanProcessorBlocking = false + defaultBatchSpanProcessorBatchTimeoutMs = 5000 + defaultBatchSpanProcessorExportTimeoutMs = 30000 + defaultBatchSpanProcessorMaxExportBatchSize = 512 + defaultBatchSpanProcessorMaxQueueSize = 2048 +) + +var supportedEncryptionScheme = map[string]struct{}{ + "off": {}, "tls": {}, "mtls": {}, +} + +func isSupportedEncryptionScheme(scheme string) bool { + _, ok := supportedEncryptionScheme[scheme] + return ok +} + +func isSupportedSampleRatePercentage(sampleRate float64) bool { + return sampleRate >= 0 && sampleRate <= 100 +} + +type resourceConfig struct { + ServiceVersion string `json:"service_version,omitempty"` + ServiceInstanceID string `json:"service_instance_id,omitempty"` + ServiceNamespace string `json:"service_namespace,omitempty"` + DeploymentEnvironment string `json:"deployment_environment,omitempty"` +} + +type batchSpanProcessorConfig struct { + Blocking *bool `json:"blocking,omitempty"` + BatchTimeoutMs *int `json:"batch_timeout_ms,omitempty"` + ExportTimeoutMs *int `json:"export_timeout_ms,omitempty"` + MaxExportBatchSize *int `json:"max_export_batch_size,omitempty"` + MaxQueueSize *int `json:"max_queue_size,omitempty"` +} + +type distributedTracingConfig struct { + Type string `json:"type,omitempty"` + Address string `json:"address,omitempty"` + ServiceName string `json:"service_name,omitempty"` + SampleRatePercentage *float64 `json:"sample_percentage,omitempty"` + EncryptionScheme string `json:"encryption,omitempty"` + EncryptionSkipVerify *bool `json:"allow_insecure_tls,omitempty"` + TLSCertFile string `json:"tls_cert_file,omitempty"` + TLSCertPrivateKeyFile string `json:"tls_private_key_file,omitempty"` + TLSCACertFile string `json:"tls_ca_cert_file,omitempty"` + Resource resourceConfig `json:"resource,omitempty"` + BatchSpanProcessorOptions batchSpanProcessorConfig `json:"batch_span_processor_options,omitempty"` +} + +func Init(ctx context.Context, raw []byte, id string) (*otlptrace.Exporter, *trace.TracerProvider, *resource.Resource, error) { + parsedConfig, err := config.ParseConfig(raw, id) + if err != nil { + return nil, nil, nil, err + } + + distributedTracingConfig, err := parseDistributedTracingConfig(parsedConfig.DistributedTracing) + if err != nil { + return nil, nil, nil, err + } + + if !strings.EqualFold(distributedTracingConfig.Type, "grpc") && !strings.EqualFold(distributedTracingConfig.Type, "http") { + return nil, nil, nil, nil + } + + certificate, err := loadCertificate(distributedTracingConfig.TLSCertFile, distributedTracingConfig.TLSCertPrivateKeyFile) + if err != nil { + return nil, nil, nil, err + } + + certPool, err := loadCertPool(distributedTracingConfig.TLSCACertFile) + if err != nil { + return nil, nil, nil, err + } + + var traceExporter *otlptrace.Exporter + if strings.EqualFold(distributedTracingConfig.Type, "grpc") { + tlsOption, err := grpcTLSOption(distributedTracingConfig.EncryptionScheme, *distributedTracingConfig.EncryptionSkipVerify, certificate, certPool) + if err != nil { + return nil, nil, nil, err + } + + traceExporter = otlptracegrpc.NewUnstarted( + otlptracegrpc.WithEndpoint(distributedTracingConfig.Address), + tlsOption, + ) + } else if strings.EqualFold(distributedTracingConfig.Type, "http") { + tlsOption, err := httpTLSOption(distributedTracingConfig.EncryptionScheme, *distributedTracingConfig.EncryptionSkipVerify, certificate, certPool) + if err != nil { + return nil, nil, nil, err + } + + traceExporter = otlptracehttp.NewUnstarted( + otlptracehttp.WithEndpoint(distributedTracingConfig.Address), + tlsOption, + ) + } + + var resourceAttributes []attribute.KeyValue + if distributedTracingConfig.Resource.ServiceVersion != "" { + resourceAttributes = append(resourceAttributes, semconv.ServiceVersionKey.String(distributedTracingConfig.Resource.ServiceVersion)) + } + if distributedTracingConfig.Resource.ServiceInstanceID != "" { + resourceAttributes = append(resourceAttributes, semconv.ServiceInstanceIDKey.String(distributedTracingConfig.Resource.ServiceInstanceID)) + } + if distributedTracingConfig.Resource.ServiceNamespace != "" { + resourceAttributes = append(resourceAttributes, semconv.ServiceNamespaceKey.String(distributedTracingConfig.Resource.ServiceNamespace)) + } + + // NOTE: this is currently using the `deployment.environment` setting which is being deprecated + // in favour of `deployment.environment.name` in future versions of the OpenTelemetry schema. + // This will need to be taken into account when upgrading the library version in the future. + if distributedTracingConfig.Resource.DeploymentEnvironment != "" { + resourceAttributes = append(resourceAttributes, semconv.DeploymentEnvironmentKey.String(distributedTracingConfig.Resource.DeploymentEnvironment)) + } + res, err := resource.New(ctx, + resource.WithAttributes( + semconv.ServiceNameKey.String(distributedTracingConfig.ServiceName), + ), + resource.WithAttributes(resourceAttributes...), + ) + if err != nil { + return nil, nil, nil, err + } + + var batchSpanProcessorOptions []trace.BatchSpanProcessorOption + if distributedTracingConfig.BatchSpanProcessorOptions.Blocking != nil && *distributedTracingConfig.BatchSpanProcessorOptions.Blocking { + batchSpanProcessorOptions = append(batchSpanProcessorOptions, trace.WithBlocking()) + } + if distributedTracingConfig.BatchSpanProcessorOptions.BatchTimeoutMs != nil { + batchSpanProcessorOptions = append(batchSpanProcessorOptions, trace.WithBatchTimeout(time.Duration(*distributedTracingConfig.BatchSpanProcessorOptions.BatchTimeoutMs)*time.Millisecond)) + } + if distributedTracingConfig.BatchSpanProcessorOptions.ExportTimeoutMs != nil { + batchSpanProcessorOptions = append(batchSpanProcessorOptions, trace.WithExportTimeout(time.Duration(*distributedTracingConfig.BatchSpanProcessorOptions.ExportTimeoutMs)*time.Millisecond)) + } + if distributedTracingConfig.BatchSpanProcessorOptions.MaxExportBatchSize != nil { + batchSpanProcessorOptions = append(batchSpanProcessorOptions, trace.WithMaxExportBatchSize(*distributedTracingConfig.BatchSpanProcessorOptions.MaxExportBatchSize)) + } + if distributedTracingConfig.BatchSpanProcessorOptions.MaxQueueSize != nil { + batchSpanProcessorOptions = append(batchSpanProcessorOptions, trace.WithMaxQueueSize(*distributedTracingConfig.BatchSpanProcessorOptions.MaxQueueSize)) + } + + traceProvider := trace.NewTracerProvider( + trace.WithResource(res), + trace.WithSampler(trace.ParentBased(trace.TraceIDRatioBased(*distributedTracingConfig.SampleRatePercentage/float64(100)))), + trace.WithSpanProcessor(trace.NewBatchSpanProcessor(traceExporter, batchSpanProcessorOptions...)), + ) + + return traceExporter, traceProvider, res, nil +} + +func SetupLogging(logger logging.Logger) { + otel.SetErrorHandler(&errorHandler{logger: logger}) + otel.SetLogger(logr.New(&sink{logger: logger})) +} + +func parseDistributedTracingConfig(raw []byte) (*distributedTracingConfig, error) { + if raw == nil { + encryptionSkipVerify := new(bool) + sampleRatePercentage := new(float64) + *sampleRatePercentage = defaultSampleRatePercentage + *encryptionSkipVerify = defaultEncryptionSkipVerify + return &distributedTracingConfig{ + Address: defaultGRPCAddress, + ServiceName: defaultServiceName, + SampleRatePercentage: sampleRatePercentage, + EncryptionScheme: defaultEncyrptionScheme, + EncryptionSkipVerify: encryptionSkipVerify, + }, nil + } + var config distributedTracingConfig + + if err := util.Unmarshal(raw, &config); err != nil { + return nil, err + } + if err := config.validateAndInjectDefaults(); err != nil { + return nil, err + } + + return &config, nil +} + +func (c *distributedTracingConfig) validateAndInjectDefaults() error { + switch c.Type { + case "", "grpc", "http": // OK + default: + return fmt.Errorf("unknown distributed_tracing.type '%s', must be \"grpc\", \"http\" or \"\" (unset)", c.Type) + } + + if c.Address == "" { + if c.Type == "grpc" { + c.Address = defaultGRPCAddress + } else if c.Type == "http" { + c.Address = defaultHTTPAddress + } + } + if c.ServiceName == "" { + c.ServiceName = defaultServiceName + } + if c.SampleRatePercentage == nil { + sampleRatePercentage := new(float64) + *sampleRatePercentage = defaultSampleRatePercentage + c.SampleRatePercentage = sampleRatePercentage + } + if c.EncryptionScheme == "" { + c.EncryptionScheme = defaultEncyrptionScheme + } + if c.EncryptionSkipVerify == nil { + encryptionSkipVerify := new(bool) + *encryptionSkipVerify = defaultEncryptionSkipVerify + c.EncryptionSkipVerify = encryptionSkipVerify + } + + if c.BatchSpanProcessorOptions.Blocking == nil { + blocking := new(bool) + *blocking = defaultBatchSpanProcessorBlocking + c.BatchSpanProcessorOptions.Blocking = blocking + } + + if c.BatchSpanProcessorOptions.BatchTimeoutMs == nil { + batchTimeoutMs := new(int) + *batchTimeoutMs = defaultBatchSpanProcessorBatchTimeoutMs + c.BatchSpanProcessorOptions.BatchTimeoutMs = batchTimeoutMs + } + + if c.BatchSpanProcessorOptions.ExportTimeoutMs == nil { + exportTimeoutMs := new(int) + *exportTimeoutMs = defaultBatchSpanProcessorExportTimeoutMs + c.BatchSpanProcessorOptions.ExportTimeoutMs = exportTimeoutMs + } + + if c.BatchSpanProcessorOptions.MaxExportBatchSize == nil { + maxExportBatchSize := new(int) + *maxExportBatchSize = defaultBatchSpanProcessorMaxExportBatchSize + c.BatchSpanProcessorOptions.MaxExportBatchSize = maxExportBatchSize + } + + if c.BatchSpanProcessorOptions.MaxQueueSize == nil { + maxQueueSize := new(int) + *maxQueueSize = defaultBatchSpanProcessorMaxQueueSize + c.BatchSpanProcessorOptions.MaxQueueSize = maxQueueSize + } + + if !isSupportedEncryptionScheme(c.EncryptionScheme) { + return fmt.Errorf("unsupported distributed_tracing.encryption_scheme '%s'", c.EncryptionScheme) + } + + if !isSupportedSampleRatePercentage(*c.SampleRatePercentage) { + return fmt.Errorf("unsupported distributed_tracing.sample_percentage '%v'", *c.SampleRatePercentage) + } + + return nil +} + +func loadCertificate(tlsCertFile, tlsPrivateKeyFile string) (*tls.Certificate, error) { + + if tlsCertFile != "" && tlsPrivateKeyFile != "" { + cert, err := tls.LoadX509KeyPair(tlsCertFile, tlsPrivateKeyFile) + if err != nil { + return nil, err + } + return &cert, nil + } + + if tlsCertFile != "" || tlsPrivateKeyFile != "" { + return nil, errors.New("distributed_tracing.tls_cert_file and distributed_tracing.tls_private_key_file must be specified together") + } + + return nil, nil +} + +func loadCertPool(tlsCACertFile string) (*x509.CertPool, error) { + if tlsCACertFile == "" { + return nil, nil + } + + caCertPEM, err := os.ReadFile(tlsCACertFile) + if err != nil { + return nil, fmt.Errorf("read CA cert file: %v", err) + } + pool := x509.NewCertPool() + if ok := pool.AppendCertsFromPEM(caCertPEM); !ok { + return nil, fmt.Errorf("failed to parse CA cert %q", tlsCACertFile) + } + return pool, nil +} + +func grpcTLSOption(encryptionScheme string, encryptionSkipVerify bool, cert *tls.Certificate, certPool *x509.CertPool) (otlptracegrpc.Option, error) { + if encryptionScheme == "off" { + return otlptracegrpc.WithInsecure(), nil + } + tlsConfig := &tls.Config{ + RootCAs: certPool, + InsecureSkipVerify: encryptionSkipVerify, + } + if encryptionScheme == "mtls" { + if cert == nil { + return nil, errors.New("distributed_tracing.tls_cert_file required but not supplied") + } + tlsConfig.Certificates = []tls.Certificate{*cert} + } + return otlptracegrpc.WithTLSCredentials(credentials.NewTLS(tlsConfig)), nil +} + +func httpTLSOption(encryptionScheme string, encryptionSkipVerify bool, cert *tls.Certificate, certPool *x509.CertPool) (otlptracehttp.Option, error) { + if encryptionScheme == "off" { + return otlptracehttp.WithInsecure(), nil + } + tlsConfig := &tls.Config{ + RootCAs: certPool, + InsecureSkipVerify: encryptionSkipVerify, + } + if encryptionScheme == "mtls" { + if cert == nil { + return nil, errors.New("distributed_tracing.tls_cert_file required but not supplied") + } + tlsConfig.Certificates = []tls.Certificate{*cert} + } + return otlptracehttp.WithTLSClientConfig(tlsConfig), nil +} + +type errorHandler struct { + logger logging.Logger +} + +func (e *errorHandler) Handle(err error) { + e.logger.Warn("Distributed tracing: " + err.Error()) +} + +// NOTE(sr): This adapter code is used to ensure that whatever otel logs, now or +// in the future, will end up in "our" logs, and not go through whatever defaults +// it has set up with its global logger. As such, it's to a full-featured +// implementation fo the logr.LogSink interface, but a rather minimal one. Notably, +// fields are no supported, the initial runtime time info is ignored, and there is +// no support for different verbosity level is "info" logs: they're all printed +// as-is. + +type sink struct { + logger logging.Logger +} + +func (s *sink) Enabled(level int) bool { + return int(s.logger.GetLevel()) >= level +} + +func (*sink) Init(logr.RuntimeInfo) {} // ignored + +func (s *sink) Info(_ int, msg string, _ ...any) { + s.logger.Info(msg) +} + +func (s *sink) Error(err error, msg string, _ ...any) { + s.logger.WithFields(map[string]any{"err": err}).Error(msg) +} + +func (s *sink) WithName(name string) logr.LogSink { + return &sink{s.logger.WithFields(map[string]any{"name": name})} +} + +func (s *sink) WithValues(...any) logr.LogSink { // ignored + return s +} diff --git a/third_party/opa/internal/edittree/bitvector/README.md b/third_party/opa/internal/edittree/bitvector/README.md new file mode 100644 index 000000000000..c98de9ee4356 --- /dev/null +++ b/third_party/opa/internal/edittree/bitvector/README.md @@ -0,0 +1,15 @@ +# godropbox [![GoDoc](https://godoc.org/github.com/dropbox/godropbox?status.svg)](https://godoc.org/github.com/dropbox/godropbox) [![Actions Status](https://github.com/dropbox/godropbox/workflows/Test/badge.svg)](https://github.com/dropbox/godropbox/actions) [![Actions Status](https://github.com/dropbox/godropbox/workflows/Lint/badge.svg)](https://github.com/dropbox/godropbox/actions) + +Common libraries for writing go services/applications on Linux servers. + +### Requirements + * Go 1.13+ + * Linux/x64 + +### Installation +``go get github.com/dropbox/godropbox`` + +### Documentation + +See https://pkg.go.dev/github.com/dropbox/godropbox for modules documentation. + diff --git a/third_party/opa/internal/edittree/bitvector/bitvector.go b/third_party/opa/internal/edittree/bitvector/bitvector.go new file mode 100644 index 000000000000..bfacf3bcea7b --- /dev/null +++ b/third_party/opa/internal/edittree/bitvector/bitvector.go @@ -0,0 +1,206 @@ +// Package bitvector provides the implementation of a variable sized compact vector of bits +// which supports lookups, sets, appends, insertions, and deletions. +package bitvector + +// A BitVector is a variable sized vector of bits. It supports +// lookups, sets, appends, insertions, and deletions. +// +// This class is not thread safe. +type BitVector struct { + data []byte + length int +} + +// NewBitVector creates and initializes a new bit vector with length +// elements, using data as its initial contents. +func NewBitVector(data []byte, length int) *BitVector { + return &BitVector{ + data: data, + length: length, + } +} + +// Bytes returns a slice of the contents of the bit vector. If the caller changes the returned slice, +// the contents of the bit vector may change. +func (vector *BitVector) Bytes() []byte { + return vector.data +} + +// Length returns the current number of elements in the bit vector. +func (vector *BitVector) Length() int { + return vector.length +} + +// This function shifts a byte slice one bit lower (less significant). +// bit (either 1 or 0) contains the bit to put in the most significant +// position of the last byte in the slice. +// This returns the bit that was shifted off of the last byte. +func shiftLower(bit byte, b []byte) byte { + bit <<= 7 + for i := len(b) - 1; i >= 0; i-- { + newByte := b[i] >> 1 + newByte |= bit + bit = (b[i] & 1) << 7 + b[i] = newByte + } + return bit >> 7 +} + +// This function shifts a byte slice one bit higher (more significant). +// bit (either 1 or 0) contains the bit to put in the least significant +// position of the first byte in the slice. +// This returns the bit that was shifted off the last byte. +func shiftHigher(bit byte, b []byte) byte { + for i := range b { + newByte := b[i] << 1 + newByte |= bit + bit = (b[i] & 0x80) >> 7 + b[i] = newByte + } + return bit +} + +// Returns the minimum number of bytes needed for storing the bit vector. +func (vector *BitVector) bytesLength() int { + lastBitIndex := vector.length - 1 + lastByteIndex := lastBitIndex >> 3 + return lastByteIndex + 1 +} + +// Panics if the given index is not within the bounds of the bit vector. +func (vector *BitVector) indexAssert(i int) { + if i < 0 || i >= vector.length { + panic("Attempted to access element outside buffer") + } +} + +// Append adds a bit to the end of a bit vector. +func (vector *BitVector) Append(bit byte) { + index := uint32(vector.length) + vector.length++ + + if vector.bytesLength() > len(vector.data) { + vector.data = append(vector.data, 0) + } + + byteIndex := index >> 3 + byteOffset := index % 8 + oldByte := vector.data[byteIndex] + var newByte byte + if bit == 1 { + newByte = oldByte | 1<> 3 + byteOffset := uint32(i % 8) + b := vector.data[byteIndex] + // Check the offset bit + return (b >> byteOffset) & 1 +} + +// Set changes the bit in the ith index of the bit vector to the value specified in +// bit. +func (vector *BitVector) Set(bit byte, index int) { + vector.indexAssert(index) + byteIndex := uint32(index >> 3) + byteOffset := uint32(index % 8) + + oldByte := vector.data[byteIndex] + + var newByte byte + if bit == 1 { + // turn on the byteOffset'th bit + newByte = oldByte | 1< len(vector.data) { + vector.data = append(vector.data, 0) + } + + byteIndex := uint32(index >> 3) + byteOffset := uint32(index % 8) + var bitToInsert byte + if bit == 1 { + bitToInsert = 1 << byteOffset + } + + oldByte := vector.data[byteIndex] + // This bit will need to be shifted into the next byte + leftoverBit := (oldByte & 0x80) >> 7 + // Make masks to pull off the bits below and above byteOffset + // This mask has the byteOffset lowest bits set. + bottomMask := byte((1 << byteOffset) - 1) + // This mask has the 8 - byteOffset top bits set. + topMask := ^bottomMask + top := (oldByte & topMask) << 1 + newByte := bitToInsert | (oldByte & bottomMask) | top + + vector.data[byteIndex] = newByte + // Shift the rest of the bytes in the slice one higher, append + // the leftoverBit obtained above. + shiftHigher(leftoverBit, vector.data[byteIndex+1:]) +} + +// Delete removes the bit in the supplied index of the bit vector. All +// bits in positions greater than or equal to index before the call will +// be shifted down by one. +func (vector *BitVector) Delete(index int) { + vector.indexAssert(index) + vector.length-- + byteIndex := uint32(index >> 3) + byteOffset := uint32(index % 8) + + oldByte := vector.data[byteIndex] + + // Shift all the bytes above the byte we're modifying, return the + // leftover bit to include in the byte we're modifying. + bit := shiftLower(0, vector.data[byteIndex+1:]) + + // Modify oldByte. + // At a high level, we want to select the bits above byteOffset, + // and shift them down by one, removing the bit at byteOffset. + + // This selects the bottom bits + bottomMask := byte((1 << byteOffset) - 1) + // This selects the top (8 - byteOffset - 1) bits + topMask := byte(^((1 << (byteOffset + 1)) - 1)) + // newTop is the top bits, shifted down one, combined with the leftover bit from shifting + // the other bytes. + newTop := (oldByte&topMask)>>1 | (bit << 7) + // newByte takes the bottom bits and combines with the new top. + newByte := (bottomMask & oldByte) | newTop + vector.data[byteIndex] = newByte + + // The desired length is the byte index of the last element plus one, + // where the byte index of the last element is the bit index of the last + // element divided by 8. + byteLength := vector.bytesLength() + if byteLength < len(vector.data) { + vector.data = vector.data[:byteLength] + } +} diff --git a/third_party/opa/internal/edittree/bitvector/bitvector_test.go b/third_party/opa/internal/edittree/bitvector/bitvector_test.go new file mode 100644 index 000000000000..4e42a27e3dee --- /dev/null +++ b/third_party/opa/internal/edittree/bitvector/bitvector_test.go @@ -0,0 +1,106 @@ +package bitvector + +import ( + "testing" + + "gopkg.in/check.v1" +) + +func Test(t *testing.T) { check.TestingT(t) } + +type BitVectorSuite struct { + vector *BitVector + lVector *BitVector +} + +var _ = check.Suite(&BitVectorSuite{}) + +func (s *BitVectorSuite) SetUpTest(_ *check.C) { + // This sets elements 4-12 + s.vector = NewBitVector([]byte{0xF0, 0x0F}, 12) + + s.lVector = NewBitVector([]byte{0xF0, 0x0F}, 16) +} + +func (s *BitVectorSuite) TestElement(c *check.C) { + for i := range 4 { + c.Assert(s.vector.Element(i), check.Equals, byte(0)) + } + for i := 4; i < 12; i++ { + c.Assert(s.vector.Element(i), check.Equals, byte(1)) + } +} + +func (s *BitVectorSuite) TestInsert(c *check.C) { + for range 4 { + s.vector.Insert(0, 8) + } + c.Assert(s.vector.Bytes(), check.DeepEquals, []byte{0xF0, 0xF0}) +} + +func (s *BitVectorSuite) TestAppend(c *check.C) { + for i := range 4 { + if i%2 == 0 { + s.vector.Append(0) + } else { + s.vector.Append(1) + } + } + c.Assert(s.vector.Bytes(), check.DeepEquals, []byte{0xF0, 0xAF}) +} + +func (s *BitVectorSuite) TestSet(c *check.C) { + for i := 4; i < 8; i++ { + if i%2 == 0 { + s.vector.Set(0, i) + } + } + c.Assert(s.vector.Bytes(), check.DeepEquals, []byte{0xA0, 0x0F}) +} + +func (s *BitVectorSuite) TestSetOneBit(c *check.C) { + for i := 4; i < 8; i++ { + if i%2 == 0 { + s.vector.Set(1, i) + } + } + c.Assert(s.vector.Bytes(), check.DeepEquals, []byte{0xF0, 0x0F}) +} + +func (s *BitVectorSuite) TestDelete(c *check.C) { + for range 4 { + s.vector.Delete(8) + } + c.Assert(s.vector.Bytes(), check.DeepEquals, []byte{0xF0}) +} + +func (s *BitVectorSuite) TestDeleteFirstIndex(c *check.C) { + for range 4 { + s.vector.Delete(0) + } + c.Assert(s.vector.Bytes(), check.DeepEquals, []byte{0xFF}) +} + +func (s *BitVectorSuite) TestDeleteInvalidInput(c *check.C) { + + defer func() { + if r := recover(); r == nil { + c.Errorf("Delete should have panicked") + } + }() + s.vector.Delete(-1) +} + +func (s *BitVectorSuite) TestLength(c *check.C) { + c.Assert(s.vector.Length(), check.Equals, 12) +} + +func (s *BitVectorSuite) TestInsertLongVector(c *check.C) { + s.lVector.Insert(1, 1) + c.Assert(s.lVector.Bytes(), check.DeepEquals, []byte{0xE2, 0x1F, 0x0}) +} + +func (s *BitVectorSuite) TestAppendLongVector(c *check.C) { + s.lVector.Append(1) + c.Assert(s.lVector.Bytes(), check.DeepEquals, []byte{0xF0, 0xF, 0x1}) +} diff --git a/third_party/opa/internal/edittree/bitvector/license.txt b/third_party/opa/internal/edittree/bitvector/license.txt new file mode 100644 index 000000000000..04d42108eeb2 --- /dev/null +++ b/third_party/opa/internal/edittree/bitvector/license.txt @@ -0,0 +1,27 @@ +Copyright (c) 2014 Dropbox, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/opa/internal/edittree/edittree.go b/third_party/opa/internal/edittree/edittree.go new file mode 100644 index 000000000000..1dafc57b0b4e --- /dev/null +++ b/third_party/opa/internal/edittree/edittree.go @@ -0,0 +1,1186 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package EditTree implements a specialized tree data structure that +// allows for cheap edits and modifications of nested Term structures. +// +// # Overview +// +// The EditTree data structure exists to solve an ugly problem in Rego: +// modification/deletion of a Term can be very expensive, because we have +// to rebuild the whole Term, sans the modified/deleted parts. +// +// To work around that problem, the EditTree allows simple add, modify, or +// delete operations on Term structures, and then at the end of a series of +// edits, the caller can pay the cost of generating a new Term from the +// tree of edits relatively efficiently. (Essentially a recursive, DFS +// traversal of the tree.) +// +// The data structure preserves basic type/safety properties, the same as +// working on the real underlying Term values. To do this, recursive +// lookups are used. On average, these are fairly straightforward and +// cheap to do. +// +// Basic Operations: +// - Insert/update EditTree node +// - Delete EditTree node +// - Unfold EditTree nodes along a path +// - Render EditTree node +// +// These operations provide all of the basic utilities required to +// recursively construct the tree. Ref-based convenience functions are also +// provided, to make rendering subtrees at a particular JSON path easier. +// +// Path-Based Convenience Functions: +// - InsertAtPath +// - DeleteAtPath +// - RenderAtPath +// +// Additionally, a few "optional" (but nice to have) functions have been +// added, to allow replacing slower/less-efficient equivalents elsewhere. +// +// Optional Functions: +// - Exists (a more efficient boolean alternative to Unfold) +// - Filter (an alternative to Object.Filter that efficiently renders +// paths out of an EditTree) +// +// # Storing scalar children "inline" +// +// The original design for the EditTree allocated a new EditTree node for +// each Term stored in the tree, but this was found to be inefficient when +// dealing with large arrays and objects. The current design of the +// EditTree separates children based on their types, with scalars stored in +// a hash -> Term map, and composites stored in a hash -> EditTree map. +// +// This results in dramatically fewer heap allocations and faster access +// times for "shallow" Term structures, without penalizing nested Term +// structures noticeably. +// +// # Object operations +// +// Objects are the most straightforward composite type, as their key-value +// structure maps naturally onto trees. Their inserts/deletes are recorded +// directly in the appropriate child maps, with almost no additional +// complexity required. +// +// Object EditTree nodes use the child key and value maps, and will not +// initialize the bit-vectors, since those are only used for Arrays. +// +// # Set operations +// +// Set data types have a major problem: they're *content-addressed*. This +// means that we often have to render/materialize the sub-terms before +// carrying out inserts or deletes, in order to know if the path to the +// destination Term exists. This forces a tree collapse at the Set's +// EditTree node, and is brutally inefficient. +// +// Example: +// +// Source set: {[0], "a"} +// {"op": "add", "path": [[0], 1], "value": 1} -> result value: {[0, 1], "a"} +// {"op": "add", "path": [[0, 1], 3], "value": 3} -> result value: {[0, 1, 3], "a"} +// +// We mitigate this somewhat by only collapsing a Set when a composite +// value is being used for indexing. Scalars imply a shallow access, which +// we can look up directly in the appropriate child map. +// +// Set EditTree nodes use the child key and value maps, and will not +// initialize the bit-vectors, since those are only used for Arrays. +// +// # Array operations +// +// Arrays can have elements inserted/replaced/deleted, and this requires +// some bookkeeping work to keep everything straight. We do this +// bookkeeping work using two bit vectors to track all the +// insertions/deletions. +// +// One bit-vector tracks which indexes are preserved/eliminated from the +// original Array, and the second bit-vector tracks which indexes have +// insertions. We can record inserts and deletes *directly* on the second +// bit-vector, "bleeding through" deletions to the preserved/eliminated bit +// vector when there's not an insert to wipe out first. +// +// For bleed-through deletes, a linear scan is required to find the index +// of which original element will be knocked out. We then mark that bit in +// the preserved/eliminated bit-vector. This is a fair bit of bookkeeping, +// but greatly reduces the cost and complexity of tracking Array state. +// There can only be insertions, or original values present. Any other +// "deletion" is an error. +// +// Insert and Delete operations also imply a linear "index rewriting" pass +// for an Array's child maps, where indexes that occur above the affected +// index of the insertion/deletion must be incremented or decremented +// appropriately. This ensures that when rendered later, the +// original/inserted values will be spliced in at the correct offsets in +// the final Array value. +// +// Due to optimizations discussed later, Array EditTree nodes do not use +// the child key map (leaving it uninitialized), but will initialize and +// use the child value maps normally. Array EditTree nodes are the only +// types of EditTree nodes that should ever be expected to have initialized +// bit-vectors present. +// +// # Scalar operations +// +// Scalars are fairly simple: just a term stored in an EditTree node, or in +// the scalar child map of a composite type's EditTree node. They cannot +// have children, and normally do not exist as independent EditTree nodes, +// except to satisfy certain EditTree APIs. +// +// Scalar EditTree nodes can only be expected to have a valid Term value; +// all other fields will be left uninitialized. +// +// # Optimization: Direct Array Indexing with ints +// +// Arrays are unique in Rego, because the only valid Terms that can index +// into them are integer, numeric values. When processing the key Terms for +// Objects and Sets, we have to identify children by their hash values +// (which hash to integers). Because the only valid key Terms for Arrays +// work as ints as well, we can skip the hashing step entirely, and just +// use the int indexes *directly*. +// +// This provides a substantial CPU savings in benchmarks, because the +// "index rewriting" passes become much cheaper from not having to rehash +// every child's index. +package edittree + +import ( + "errors" + "fmt" + "math/big" + "sort" + "strings" + + "github.com/open-policy-agent/opa/internal/edittree/bitvector" + "github.com/open-policy-agent/opa/v1/ast" +) + +// Deletions are encoded with a nil value pointer. +type EditTree struct { + value *ast.Term + childKeys map[int]*ast.Term + childScalarValues map[int]*ast.Term + childCompositeValues map[int]*EditTree + eliminated *bitvector.BitVector // Which original indexes have been knocked out? + insertions *bitvector.BitVector // Which indexes have a new value inserted at them? (also used for "live" bookkeeping) +} + +// Creates a new EditTree node from term. +func NewEditTree(term *ast.Term) *EditTree { + if term == nil { + return nil + } + + var tree EditTree + switch x := term.Value.(type) { + case ast.Object, ast.Set: + tree = EditTree{ + value: term, + childKeys: map[int]*ast.Term{}, + childScalarValues: map[int]*ast.Term{}, + childCompositeValues: map[int]*EditTree{}, + } + case *ast.Array: + tree = EditTree{ + value: term, + childScalarValues: map[int]*ast.Term{}, + childCompositeValues: map[int]*EditTree{}, + } + bytesLength := ((x.Len() - 1) / 8) + 1 // How many bytes to use for the bit-vectors. + tree.eliminated = bitvector.NewBitVector(make([]byte, bytesLength), x.Len()) + tree.insertions = bitvector.NewBitVector(make([]byte, bytesLength), x.Len()) + default: + tree = EditTree{ + value: term, + } + } + + return &tree +} + +// Returns correct (collision-resolved) hash for this term + whether or not +// it was found in the table already. +func (e *EditTree) getKeyHash(key *ast.Term) (int, bool) { + hash := key.Hash() + // This `equal` utility is duplicated and manually inlined a number of + // time in this file. Inlining it avoids heap allocations, so it makes + // a big performance difference: some operations like lookup become twice + // as slow without it. + var equal func(v ast.Value) bool + + switch x := key.Value.(type) { + case ast.Null, ast.Boolean, ast.String, ast.Var: + equal = func(y ast.Value) bool { return x == y } + case ast.Number: + if xi, ok := x.Int64(); ok { + equal = func(y ast.Value) bool { + if y, ok := y.(ast.Number); ok { + if yi, ok := y.Int64(); ok { + return xi == yi + } + } + + return false + } + break + } + + // We use big.Rat for comparing big numbers. + // It replaces big.Float due to following reason: + // big.Float comes with a default precision of 64, and setting a + // larger precision results in more memory being allocated + // (regardless of the actual number we are parsing with SetString). + // + // Note: If we're so close to zero that big.Float says we are zero, do + // *not* big.Rat).SetString on the original string it'll potentially + // take very long. + var a *big.Rat + fa, ok := new(big.Float).SetString(string(x)) + if !ok { + panic("illegal value") + } + if fa.IsInt() { + if i, _ := fa.Int64(); i == 0 { + a = new(big.Rat).SetInt64(0) + } + } + if a == nil { + a, ok = new(big.Rat).SetString(string(x)) + if !ok { + panic("illegal value") + } + } + + equal = func(b ast.Value) bool { + if bNum, ok := b.(ast.Number); ok { + var b *big.Rat + fb, ok := new(big.Float).SetString(string(bNum)) + if !ok { + panic("illegal value") + } + if fb.IsInt() { + if i, _ := fb.Int64(); i == 0 { + b = new(big.Rat).SetInt64(0) + } + } + if b == nil { + b, ok = new(big.Rat).SetString(string(bNum)) + if !ok { + panic("illegal value") + } + } + + return a.Cmp(b) == 0 + } + return false + } + + default: + equal = func(y ast.Value) bool { return ast.Compare(x, y) == 0 } + } + + // Look through childKeys, looking up the original hash + // value first, and then use linear-probing to iter + // through the keys until we either find the Term we're + // after, or run out of candidates. + for curr, ok := e.childKeys[hash]; ok; { + if equal(curr.Value) { + return hash, true + } + + hash++ + curr, ok = e.childKeys[hash] + } + + // Didn't find any matches in childKeys. Hash will be + // the first open slot after the linear probing loop. + return hash, false +} + +//gcassert:inline +func isComposite(t *ast.Term) bool { + switch t.Value.(type) { + case ast.Object, ast.Set, *ast.Array: + return true + default: + return false + } +} + +//gcassert:inline +func (e *EditTree) setChildKey(hash int, key *ast.Term) { + e.childKeys[hash] = key +} + +//gcassert:inline +func (e *EditTree) setChildScalarValue(hash int, value *ast.Term) { + e.childScalarValues[hash] = value +} + +//gcassert:inline +func (e *EditTree) setChildCompositeValue(hash int, child *EditTree) { + e.childCompositeValues[hash] = child +} + +// We don't have a deleteChildKeys method, because once a key is inserted, +// it can only be replaced with either another value, or a delete node from +// then on. +// +//gcassert:inline +func (e *EditTree) deleteChildValue(hash int) { + delete(e.childScalarValues, hash) + delete(e.childCompositeValues, hash) +} + +// Insert creates a new child of e, and returns the new child EditTree node. +func (e *EditTree) Insert(key, value *ast.Term) (*EditTree, error) { + if e.value == nil { + return nil, errors.New("deleted node encountered during insert operation") + } + if key == nil { + return nil, errors.New("nil key provided for insert operation") + } + if value == nil { + return nil, errors.New("nil value provided for insert operation") + } + + switch x := e.value.Value.(type) { + case ast.Object: + return e.unsafeInsertObject(key, value), nil + case ast.Set: + if !key.Equal(value) { + return nil, fmt.Errorf("set key %v does not equal value to be inserted %v", key, value) + } + // We only collapse this Set-typed node if a composite type is involved. + if isComposite(key) { + // TODO: Investigate re-rendering *only* the immediate composite children. + collapsed := e.Render() + e.value = collapsed + e.childKeys = map[int]*ast.Term{} + e.childScalarValues = map[int]*ast.Term{} + e.childCompositeValues = map[int]*EditTree{} + } + return e.unsafeInsertSet(key, value), nil + case *ast.Array: + idx, err := toIndex(e.insertions.Length(), key) + if err != nil { + return nil, err + } + if idx < 0 || idx > e.insertions.Length() { + return nil, errors.New("index for array insertion out of bounds") + } + return e.unsafeInsertArray(idx, value), nil + default: + // Catch all primitive types. + return nil, fmt.Errorf("expected composite type, found value: %v (type: %T)", x, x) + } +} + +func (e *EditTree) unsafeInsertObject(key, value *ast.Term) *EditTree { + child := NewEditTree(value) + keyHash, found := e.getKeyHash(key) + if found { + e.deleteChildValue(keyHash) + } + e.setChildKey(keyHash, key) + if isComposite(value) { + e.setChildCompositeValue(keyHash, child) + } else { + e.setChildScalarValue(keyHash, value) + } + return child +} + +func (e *EditTree) unsafeInsertSet(key, value *ast.Term) *EditTree { + child := NewEditTree(value) + keyHash, found := e.getKeyHash(key) + if found { + e.deleteChildValue(keyHash) + } + e.setChildKey(keyHash, key) + if isComposite(value) { + e.setChildCompositeValue(keyHash, child) + } else { + e.setChildScalarValue(keyHash, value) + } + return child +} + +func (e *EditTree) unsafeInsertArray(idx int, value *ast.Term) *EditTree { + child := NewEditTree(value) + // Collect insertion indexes above the insertion site for rewriting. + rewritesScalars := []int{} + rewritesComposites := []int{} + for i := idx; i < e.insertions.Length(); i++ { + if e.insertions.Element(i) == 1 { + if _, ok := e.childScalarValues[i]; ok { + rewritesScalars = append(rewritesScalars, i) + continue + } + if _, ok := e.childCompositeValues[i]; ok { + rewritesComposites = append(rewritesComposites, i) + continue + } + panic(fmt.Errorf("invalid index %d during Insert operation", i)) + } + } + // Do rewrites in reverse order to make room for the newly-inserted element. + for i := len(rewritesScalars) - 1; i >= 0; i-- { + originalIdx := rewritesScalars[i] + rewriteIdx := rewritesScalars[i] + 1 + v := e.childScalarValues[originalIdx] + e.deleteChildValue(originalIdx) + e.setChildScalarValue(rewriteIdx, v) + } + for i := len(rewritesComposites) - 1; i >= 0; i-- { + originalIdx := rewritesComposites[i] + rewriteIdx := rewritesComposites[i] + 1 + v := e.childCompositeValues[originalIdx] + e.deleteChildValue(originalIdx) + e.setChildCompositeValue(rewriteIdx, v) + } + // Insert new element in children array, bump bit-vector over by 1. + if idx == e.insertions.Length() { + e.insertions.Append(1) + } else { + e.insertions.Insert(1, idx) + } + if isComposite(value) { + e.setChildCompositeValue(idx, child) + } else { + e.setChildScalarValue(idx, value) + } + return child +} + +// Delete removes a child of e, or else creates a delete node for a term +// already present in e. It then returns the deleted child EditTree node. +func (e *EditTree) Delete(key *ast.Term) (*EditTree, error) { + if e.value == nil { + return nil, errors.New("deleted node encountered during delete operation") + } + if key == nil { + return nil, errors.New("nil key provided for delete operation") + } + + switch e.value.Value.(type) { + case ast.Object: + keyHash, found := e.getKeyHash(key) + // If child found, replace with delete node. If delete node already existed, error. + if found { + if child, ok := e.childScalarValues[keyHash]; ok { + if child == nil { + return nil, fmt.Errorf("cannot delete the already deleted scalar node for key %v", key) + } + e.setChildKey(keyHash, key) + e.setChildScalarValue(keyHash, nil) + return NewEditTree(child), nil + } + if child, ok := e.childCompositeValues[keyHash]; ok { + if child == nil { + return nil, fmt.Errorf("cannot delete the already deleted composite node for key %v", key) + } + e.setChildKey(keyHash, key) + e.setChildCompositeValue(keyHash, nil) + return child, nil + } + // Note(philipc): We panic here, because the only way to reach + // this panic is to have broken the bookkeeping around the key + // and child maps in a way that is not recoverable. + // For example, if we have an Object EditTree node, and mess up + // the bookkeeping elsewhere by deleting just the value from + // the child maps, *without* also deleting the key from the key + // map, we would reach this place, where the data structure + // *expects* a value to exist, but nothing is present. + panic(fmt.Errorf("hash value %d not found in scalar or composite child maps", keyHash)) + } + // No child, lookup the key in e.value, and put in a delete if present. + // Error if key does not exist in e.value. + return e.fallbackDelete(key) + case ast.Set: + // We only collapse this Set-typed node if a composite type is involved. + if isComposite(key) { + // TODO: Investigate re-rendering *only* the immediate composite children. + collapsed := e.Render() + e.value = collapsed + e.childKeys = map[int]*ast.Term{} + e.childScalarValues = map[int]*ast.Term{} + e.childCompositeValues = map[int]*EditTree{} + } else { + keyHash, found := e.getKeyHash(key) + // If child found, replace with delete node. If delete node already existed, error. + if found { + if child, ok := e.childScalarValues[keyHash]; ok { + if child == nil { + return nil, fmt.Errorf("cannot delete the already deleted scalar node for key %v", key) + } + if key.Equal(child) { + e.setChildKey(keyHash, key) + e.setChildScalarValue(keyHash, nil) + return NewEditTree(child), nil + } + } + } + } + // No child, lookup the key in e.value, and put in a delete if present. + // Error if key does not exist in e.value. + return e.fallbackDelete(key) + case *ast.Array: + idx, err := toIndex(e.insertions.Length(), key) + if err != nil { + return nil, err + } + if idx < 0 || idx > e.insertions.Length()-1 { + return nil, errors.New("index for array delete out of bounds") + } + + // Collect insertion indexes above the delete site for rewriting. + rewritesScalars := []int{} + rewritesComposites := []int{} + for i := idx + 1; i < e.insertions.Length(); i++ { + if e.insertions.Element(i) == 1 { + if _, ok := e.childScalarValues[i]; ok { + rewritesScalars = append(rewritesScalars, i) + continue + } + if _, ok := e.childCompositeValues[i]; ok { + rewritesComposites = append(rewritesComposites, i) + continue + } + panic(fmt.Errorf("invalid index %d during Insert operation", i)) + } + } + // Do rewrites to clear out the newly-removed element. + e.deleteChildValue(idx) + for i := range rewritesScalars { + originalIdx := rewritesScalars[i] + rewriteIdx := rewritesScalars[i] - 1 + v := e.childScalarValues[originalIdx] + e.deleteChildValue(originalIdx) + e.setChildScalarValue(rewriteIdx, v) + } + for i := range rewritesComposites { + originalIdx := rewritesComposites[i] + rewriteIdx := rewritesComposites[i] - 1 + v := e.childCompositeValues[originalIdx] + e.deleteChildValue(originalIdx) + e.setChildCompositeValue(rewriteIdx, v) + } + + // "bleed through" to the underlying array if needed. + // To do this, we sum up the zeroes below the current index, and use that value + // to index through the `eliminated` array until we find a surviving index. + if e.insertions.Element(idx) == 0 { + zeroesSeen := 1 + sumZeroesBelowIndex(idx, e.insertions) + // Mark appropriate position in `eliminated` array, or error. + elimIdx, found := findIndexOfNthZero(zeroesSeen, e.eliminated) + if !found { + panic(fmt.Errorf("could not successfully eliminate index %d from array", idx)) + } + e.eliminated.Set(1, elimIdx) + } + // Delete element from insertions array, bump bit-vec over by 1. + e.insertions.Delete(idx) + return e, nil + default: + // Catch all primitive types. + return nil, fmt.Errorf("expected composite type, found value: %v (type: %T)", e.value.Value, e.value.Value) + } +} + +//gcassert:inline +func sumZeroesBelowIndex(index int, bv *bitvector.BitVector) int { + zeroesSeen := 0 + for i := range index { + if bv.Element(i) == 0 { + zeroesSeen++ + } + } + return zeroesSeen +} + +func findIndexOfNthZero(n int, bv *bitvector.BitVector) (int, bool) { + zeroesSeen := 0 + for i := range bv.Length() { + if bv.Element(i) == 0 { + zeroesSeen++ + } + if zeroesSeen == n { + return i, true + } + } + return 0, false +} + +// Helper function for sets/objects when the key isn't present in either +// child map. +func (e *EditTree) fallbackDelete(key *ast.Term) (*EditTree, error) { + value, err := e.value.Value.Find(ast.Ref{key}) + if err != nil { + return nil, fmt.Errorf("cannot delete child key %v that does not exist", key) + } + keyHash, _ := e.getKeyHash(key) + e.setChildKey(keyHash, key) + if isComposite(ast.NewTerm(value)) { + e.setChildCompositeValue(keyHash, nil) + } else { + e.setChildScalarValue(keyHash, nil) + } + return NewEditTree(ast.NewTerm(value)), nil +} + +// Unfurls a chain of EditTree nodes down a given path, or else returns an error. +func (e *EditTree) Unfold(path ast.Ref) (*EditTree, error) { + // 0 path segments base case. (Root hits this.) + if len(path) == 0 { + return e, nil + } + // 1+ path segment case. + if e.value == nil { + return nil, errors.New("nil value encountered where composite value was expected") + } + + // Switch behavior based on types. + key := path[0] + switch x := e.value.Value.(type) { + case ast.Object: + keyHash, found := e.getKeyHash(key) + if found { + if term, ok := e.childScalarValues[keyHash]; ok { + if term == nil { + return nil, fmt.Errorf("cannot unfold the already deleted scalar node for key %v", key) + } + child := NewEditTree(term) + return child.Unfold(path[1:]) + } + if child, ok := e.childCompositeValues[keyHash]; ok { + if child == nil { + return nil, fmt.Errorf("cannot unfold the already deleted composite node for key %v", key) + } + return child.Unfold(path[1:]) + } + // Note(philipc): We panic here, because the only way to reach + // this panic is to have broken the bookkeeping around the key + // and child maps in a way that is not recoverable. + // For example, if we have an Object EditTree node, and mess up + // the bookkeeping elsewhere by deleting just the value from + // the child maps, *without* also deleting the key from the key + // map, we would reach this place, where the data structure + // *expects* a value to exist, but nothing is present. + panic(fmt.Errorf("hash value %d not found in scalar or composite child maps", keyHash)) + } + // Fall back to looking up the key in e.value. + // Extend the tree if key is present. Error otherwise. + if v, err := x.Find(ast.Ref{path[0]}); err == nil { + child, err := e.Insert(path[0], ast.NewTerm(v)) + if err != nil { + return nil, err + } + return child.Unfold(path[1:]) + } + return nil, fmt.Errorf("path %v does not exist in object term %v", ast.Ref{path[0]}, e.value.Value) + case ast.Set: + // Sets' keys *are* their values, so in order to allow accurate + // traversal, we have to collapse the tree beneath this node, + // so that we can accurately unfold it again for an update, + // once we know that the key we care about is present. + if isComposite(key) { + collapsed := e.Render() + e.value = collapsed + e.childKeys = map[int]*ast.Term{} + e.childScalarValues = map[int]*ast.Term{} + e.childCompositeValues = map[int]*EditTree{} + } else { + keyHash, found := e.getKeyHash(key) + if found { + if term, ok := e.childScalarValues[keyHash]; ok { + child := NewEditTree(term) + return child.Unfold(path[1:]) + } + } + } + // Fall back to looking up the key in e.value. + // Extend the tree if key is present. Error otherwise. + if v, err := e.value.Value.Find(ast.Ref{path[0]}); err == nil { + child, err := e.Insert(path[0], ast.NewTerm(v)) + if err != nil { + return nil, err + } + return child.Unfold(path[1:]) + } + return nil, fmt.Errorf("path %v does not exist in set term %v", ast.Ref{path[0]}, e.value.Value) + case *ast.Array: + idx, err := toIndex(e.insertions.Length(), path[0]) + if err != nil { + return nil, err + } + if term, ok := e.childScalarValues[idx]; ok { + child := NewEditTree(term) + return child.Unfold(path[1:]) + } + if child, ok := e.childCompositeValues[idx]; ok { + return child.Unfold(path[1:]) + } + + idxt := ast.InternedTerm(idx) + + // Fall back to looking up the key in e.value. + // Extend the tree if key is present. Error otherwise. + if v, err := x.Find(ast.Ref{idxt}); err == nil { + // TODO: Consider a more efficient "Replace" function that special-cases this for arrays instead? + _, err := e.Delete(idxt) + if err != nil { + return nil, err + } + child, err := e.Insert(idxt, ast.NewTerm(v)) + if err != nil { + return nil, err + } + return child.Unfold(path[1:]) + } + return nil, fmt.Errorf("path %v does not exist in array term %v", ast.Ref{ast.IntNumberTerm(idx)}, e.value.Value) + default: + // Catch all primitive types. + return nil, fmt.Errorf("expected composite type for path %v, found value: %v (type: %T)", ast.Ref{path[0]}, x, x) + } +} + +// Render generates the effective value for the term at e by recursively +// rendering the children of e, and then copying over any leftover keys +// from the original term stored at e. +func (e *EditTree) Render() *ast.Term { + if e.value == nil { + return nil + } + + switch x := e.value.Value.(type) { + case ast.Object: + // Early exit if no modifications. + if len(e.childKeys) == 0 { + return e.value + } + // Build a new Object with modified/deleted keys. + // We do this by adding the modified/deleted keys first, then + // skipping those keys later when iterating over the original base + // term in e.value. + skipKeysList := make([]*ast.Term, 0, len(e.childKeys)) + out := make([][2]*ast.Term, 0, len(e.childKeys)+x.Len()) + for hash, term := range e.childScalarValues { + skipKeysList = append(skipKeysList, e.childKeys[hash]) + if term == nil { + continue // Delete case. + } + // Normal value case. + out = append(out, [2]*ast.Term{e.childKeys[hash], term}) + } + for hash, child := range e.childCompositeValues { + skipKeysList = append(skipKeysList, e.childKeys[hash]) + if child == nil { + continue // Delete case. + } + // Normal value case. + subtreeResult := child.Render() + out = append(out, [2]*ast.Term{e.childKeys[hash], subtreeResult}) + } + skipKeys := ast.NewSet(skipKeysList...) + // Copy over all keys that weren't deleted/modified. + x.Foreach(func(k, v *ast.Term) { + if skipKeys.Contains(k) { + return + } + out = append(out, [2]*ast.Term{k, v}) + }) + return ast.ObjectTerm(out...) + case ast.Set: + // Early exit if no modifications. + if len(e.childKeys) == 0 { + return e.value + } + // Build a new Set. + // Sets only can have deletions/new insertions, because the value + // *is* its own key. + skipKeysList := make([]*ast.Term, 0, len(e.childKeys)) + out := make([]*ast.Term, 0, x.Len()+len(e.childKeys)) + for hash, term := range e.childScalarValues { + skipKeysList = append(skipKeysList, e.childKeys[hash]) + if term == nil { + continue // Delete case. + } + // Normal value case. + out = append(out, e.childScalarValues[hash]) + } + // Only happens when this set hasn't been collapsed yet. + for hash, child := range e.childCompositeValues { + skipKeysList = append(skipKeysList, e.childKeys[hash]) + if child == nil { + continue // Delete case. + } + // Normal value case. + subtreeResult := child.Render() + out = append(out, subtreeResult) + } + skipKeys := ast.NewSet(skipKeysList...) + // Copy over all keys that weren't deleted/modified. + x.Foreach(func(key *ast.Term) { + if skipKeys.Contains(key) { + return + } + out = append(out, key) + }) + return ast.SetTerm(out...) + case *ast.Array: + // No early exit here, because we might have just deletes on the + // original array. We build a new Array with modified/deleted keys. + out := make([]*ast.Term, 0, e.insertions.Length()) + eIdx := 0 + for i := range e.insertions.Length() { + // If the index == 0, that indicates we should look up the next + // surviving original element. + // If the index == 1, that indicates we should look up that + // index in the child maps. + if e.insertions.Element(i) == 0 { + // Scan through the e.eliminated bit-vec, pick the + // first non-zero index. The then use that index to + // look up the element we should append from e.value. + foundIdx := false + for j := eIdx; j < e.eliminated.Length(); j++ { + if e.eliminated.Element(j) == 0 { + foundIdx = true + eIdx = j + break + } + } + if !foundIdx { + panic(fmt.Errorf("too many eliminated indexes in array, expected to find uneliminated index %d", i)) + } + // Append element from original term. + out = append(out, x.Elem(eIdx)) + eIdx++ // Bump the counter, so that we monotonically advance through the array. + } else { + // Append value from rendered child index. + // Since deletions are not possible as children for an Array, + // we don't need to check for nils here. + if t, ok := e.childScalarValues[i]; ok { + out = append(out, t) + } else if child, ok := e.childCompositeValues[i]; ok { + t := child.Render() + out = append(out, t) + } else { + panic(fmt.Errorf("invalid index %d does not exist in array", i)) + } + } + } + return ast.ArrayTerm(out...) + default: + return e.value + } +} + +// InsertAtPath traverses down the tree from e and uses the last path +// segment as the key to insert value into the tree. +// Returns the inserted EditTree node. +func (e *EditTree) InsertAtPath(path ast.Ref, value *ast.Term) (*EditTree, error) { + if value == nil { + return nil, errors.New("cannot insert nil value into EditTree") + } + + if len(path) == 0 { + e.value = value + e.childKeys = map[int]*ast.Term{} + e.childScalarValues = map[int]*ast.Term{} + e.childCompositeValues = map[int]*EditTree{} + if v, ok := value.Value.(*ast.Array); ok { + bytesLength := ((v.Len() - 1) / 8) + 1 // How many bytes to use for the bit-vectors. + e.eliminated = bitvector.NewBitVector(make([]byte, bytesLength), v.Len()) + e.insertions = bitvector.NewBitVector(make([]byte, bytesLength), v.Len()) + } + return e, nil + } + + dest, err := e.Unfold(path[:len(path)-1]) + if err != nil { + return nil, err + } + + return dest.Insert(path[len(path)-1], value) +} + +// DeleteAtPath traverses down the tree from e and uses the last path +// segment as the key to delete a node from the tree. +// Returns the deleted EditTree node. +func (e *EditTree) DeleteAtPath(path ast.Ref) (*EditTree, error) { + // Root document case: + if len(path) == 0 { + if e.value == nil { + return nil, errors.New("deleted node encountered during delete operation") + } + e.value = nil + e.childKeys = nil + e.childScalarValues = nil + e.childCompositeValues = nil + e.eliminated = nil + e.insertions = nil + return e, nil + } + + dest, err := e.Unfold(path[:len(path)-1]) + if err != nil { + return nil, err + } + + return dest.Delete(path[len(path)-1]) +} + +// RenderAtPath traverses down the tree from e and renders the EditTree +// node at the end of path. +func (e *EditTree) RenderAtPath(path ast.Ref) (*ast.Term, error) { + dest, err := e.Unfold(path) + if err != nil { + return nil, err + } + + return dest.Render(), nil +} + +func (e *EditTree) String() string { + if t := e.Render(); t != nil { + return "EditTree[" + t.String() + "]" + } + return "" +} + +func (e *EditTree) Exists(path ast.Ref) bool { + if e.value == nil { + return false + } + + switch { + // 0 path segments base case. (Root hits this.) + case len(path) == 0: + return true + // 1+ path segments case. + case len(path) >= 1: + // Switch behavior based on types. + key := path[0] + switch x := e.value.Value.(type) { + case ast.Object: + keyHash, found := e.getKeyHash(key) + if found { + if term, ok := e.childScalarValues[keyHash]; ok { + if term == nil { + return false + } + return len(path) == 1 + } + if child, ok := e.childCompositeValues[keyHash]; ok { + if child == nil { + return false + } + return child.Exists(path[1:]) + } + // Note(philipc): We panic here, because the only way to reach + // this panic is to have broken the bookkeeping around the key + // and child maps in a way that is not recoverable. + // For example, if we have an Object EditTree node, and mess up + // the bookkeeping elsewhere by deleting just the value from + // the child maps, *without* also deleting the key from the key + // map, we would reach this place, where the data structure + // *expects* a value to exist, but nothing is present. + panic(fmt.Errorf("hash value %d not found in scalar or composite child maps", keyHash)) + } + // Fallback if child lookup failed. + _, err := x.Find(path) + return err == nil + case ast.Set: + // Sets' keys *are* their values, so in order to allow accurate + // traversal, we have to collapse the tree beneath this node, + // so that we can accurately unfold it again for an update, + // once we know that the key we care about is present. + if isComposite(key) { + collapsed := e.Render() + e.value = collapsed + e.childKeys = map[int]*ast.Term{} + e.childScalarValues = map[int]*ast.Term{} + e.childCompositeValues = map[int]*EditTree{} + } else { + keyHash, found := e.getKeyHash(key) + if found { + if _, ok := e.childScalarValues[keyHash]; ok { + return len(path) == 1 + } + } + } + // Fallback if child lookup failed. + _, err := e.value.Value.Find(path) + return err == nil + case *ast.Array: + var idx int + idx, err := toIndex(e.insertions.Length(), path[0]) + if err != nil { + return false + } + if _, ok := e.childScalarValues[idx]; ok { + return len(path) == 1 + } + if child, ok := e.childCompositeValues[idx]; ok { + return child.Exists(path[1:]) + } + // Fallback if child lookup failed. + // We have to ensure that the lookup term is a number here, or Find will fail. + _, err = x.Find(ast.Ref{ast.InternedTerm(idx)}.Concat(path[1:])) + return err == nil + default: + // Catch all primitive types. + return false + } + } + return false +} + +// -------------------------------------------------------------------- +// Utility functions + +// toIndex tries to convert path elements (that may be strings) into indexes +// into an array. +func toIndex(arrayLength int, term *ast.Term) (int, error) { + i := 0 + var ok bool + switch v := term.Value.(type) { + case ast.Number: + if i, ok = v.Int(); !ok { + return 0, errors.New("invalid number type for indexing") + } + case ast.String: + if v == "-" { + return arrayLength, nil + } + num := ast.Number(v) + if i, ok = num.Int(); !ok { + return 0, errors.New("invalid string for indexing") + } + if v != "0" && strings.HasPrefix(string(v), "0") { + return 0, errors.New("leading zeros are not allowed in JSON paths") + } + default: + return 0, errors.New("invalid type for indexing") + } + + return i, nil +} + +// -------------------------------------------------------------------- +// Term-level utility functions + +// Filter pulls out only the values selected by paths. +// This is done recursively by plucking off one level from the paths each time we descend a level. +// Note: Values pulled from arrays will have the same approximate +// ordering in the final term. +func (e *EditTree) Filter(paths []ast.Ref) *ast.Term { + if e.value == nil { + return nil + } + + // Separate out keys for this level. + // In the event of paths like "a", "a/b", "a/b/c", the "a" path will win out. + // Nil keys, such as "" or [], are not permitted. (legacy behavior) + pathMap := make(map[ast.Value][]ast.Ref, len(paths)) + renderNowList := []*ast.Term{} + for i := range paths { + path := paths[i] + switch { + case len(path) == 0: + continue // ignore nil paths, such as "" and []. + case len(path) == 1: + renderNowList = append(renderNowList, path[0]) + default: // len(path) > 1 + if _, ok := pathMap[path[0].Value]; !ok { + pathMap[path[0].Value] = []ast.Ref{} + } + pathMap[path[0].Value] = append(pathMap[path[0].Value], path[1:]) + } + } + renderNow := ast.NewSet(renderNowList...) + // Clear everything out of the pathMap that has a renderNow candidate. + for k := range pathMap { + if renderNow.Contains(ast.NewTerm(k)) { + delete(pathMap, k) + } + } + + // Now that we've reached the target, we can start rendering everything beneath us in the tree. + switch e.value.Value.(type) { + case ast.Object: + out := make([][2]*ast.Term, 0, renderNow.Len()+len(pathMap)) + // Render any finished paths. + renderNow.Foreach(func(k *ast.Term) { + if e.Exists(ast.Ref{k}) { + subtreeResult, _ := e.RenderAtPath(ast.Ref{k}) + out = append(out, [2]*ast.Term{k, subtreeResult}) + } + }) + // Recursively descend remaining paths. + for k, p := range pathMap { + if e.Exists(ast.Ref{ast.NewTerm(k)}) { + child, _ := e.Unfold(ast.Ref{ast.NewTerm(k)}) + subtreeResult := child.Filter(p) + out = append(out, [2]*ast.Term{ast.NewTerm(k), subtreeResult}) + } + } + return ast.ObjectTerm(out...) + case ast.Set: + out := make([]*ast.Term, 0, renderNow.Len()+len(pathMap)) + // Render any finished paths. + renderNow.Foreach(func(k *ast.Term) { + if e.Exists(ast.Ref{k}) { + subtreeResult, _ := e.RenderAtPath(ast.Ref{k}) + out = append(out, subtreeResult) + } + }) + // Recursively descend remaining paths. + for k, p := range pathMap { + if e.Exists(ast.Ref{ast.NewTerm(k)}) { + child, _ := e.Unfold(ast.Ref{ast.NewTerm(k)}) + subtreeResult := child.Filter(p) + out = append(out, subtreeResult) + } + } + return ast.SetTerm(out...) + case *ast.Array: + // No early exit here, because we might have just deletes on the + // original array. We build a new Array with modified/deleted keys. + out := make([]*ast.Term, 0, renderNow.Len()+len(pathMap)) + // Sort array indexes before descending. + idxList := make([]*ast.Term, 0, len(pathMap)) + renderNow.Foreach(func(k *ast.Term) { + idxList = append(idxList, k) + }) + for k := range pathMap { + idxList = append(idxList, ast.NewTerm(k)) + } + sort.Sort(termSlice(idxList)) + // Render child or recursively descend as needed. + for i := range idxList { + k := idxList[i] + if renderNow.Contains(k) { + if e.Exists(ast.Ref{k}) { + subtreeResult, _ := e.RenderAtPath(ast.Ref{k}) + out = append(out, subtreeResult) + } + } else if e.Exists(ast.Ref{k}) { + child, _ := e.Unfold(ast.Ref{k}) + subtreeResult := child.Filter(pathMap[k.Value]) + out = append(out, subtreeResult) + } + } + return ast.ArrayTerm(out...) + default: + return e.value + } +} + +type termSlice []*ast.Term + +func (s termSlice) Less(i, j int) bool { return ast.Compare(s[i].Value, s[j].Value) < 0 } +func (s termSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s termSlice) Len() int { return len(s) } diff --git a/third_party/opa/internal/edittree/edittree_test.go b/third_party/opa/internal/edittree/edittree_test.go new file mode 100644 index 000000000000..c76ca0cbe49d --- /dev/null +++ b/third_party/opa/internal/edittree/edittree_test.go @@ -0,0 +1,951 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package edittree + +import ( + "errors" + "fmt" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +// These test cases require malformed tree nodes or arguments, +// so they're tested separately from normal error conditions. +func TestEditTreeNilValueCases(t *testing.T) { + // Try to create an EditTree with a nil Term pointer. + if tree := NewEditTree(nil); tree != nil { + t.Fatalf("Expected nil pointer, got %v", tree) + } + + // Try to Insert on a nil-valued EditTree node. + badroot := EditTree{value: nil} + if result, err := badroot.Insert(ast.StringTerm("a"), ast.IntNumberTerm(2)); err != nil { + if err.Error() != "deleted node encountered during insert operation" { + t.Fatalf("wrong error: %v", err.Error()) + } + } else { + t.Fatalf("Expected error from Insert on nil-valued tree node, got %v", result) + } + // Try to Delete on a nil-valued EditTree node. + if result, err := badroot.Delete(ast.StringTerm("a")); err != nil { + if err.Error() != "deleted node encountered during delete operation" { + t.Fatalf("wrong error: %v", err.Error()) + } + } else { + t.Fatalf("Expected error from Delete on nil-valued tree node, got %v", result) + } + // Try to Unfold past a nil-valued EditTree node. + if result, err := badroot.Unfold(ast.Ref{ast.StringTerm("a")}); err != nil { + if err.Error() != "nil value encountered where composite value was expected" { + t.Fatalf("wrong error: %v", err.Error()) + } + } else { + t.Fatalf("Expected error from Unfold on nil-valued tree node, got %v", result) + } + // Try Exists with a nil-valued EditTree node. + if ok := badroot.Exists(ast.Ref{ast.StringTerm("a")}); ok { + t.Fatalf("Expected false from Exists on nil-valued tree node, got true instead") + } + // Try Filter with a nil-valued EditTree node. + if result := badroot.Filter([]ast.Ref{{ast.StringTerm("a")}}); result != nil { + t.Fatalf("Expected nil from Filter with nil-valued tree node, got: %v", result) + } + + // Try to Insert with a nil value as the key Term. + simpleTree := NewEditTree(ast.ObjectTerm()) + if result, err := simpleTree.Insert(nil, ast.IntNumberTerm(2)); err != nil { + if err.Error() != "nil key provided for insert operation" { + t.Fatalf("wrong error: %v", err.Error()) + } + } else { + t.Fatalf("Expected error from Insert with nil-valued key Term, got %v", result) + } + // Try to Insert with a nil value as the value Term. + if result, err := simpleTree.Insert(ast.StringTerm("a"), nil); err != nil { + if err.Error() != "nil value provided for insert operation" { + t.Fatalf("wrong error: %v", err.Error()) + } + } else { + t.Fatalf("Expected error from Insert with nil-valued value Term, got %v", result) + } + // Try to Delete with a nil value as the key Term + if result, err := simpleTree.Delete(nil); err != nil { + if err.Error() != "nil key provided for delete operation" { + t.Fatalf("wrong error: %v", err.Error()) + } + } else { + t.Fatalf("Expected error from Delete with nil-valued key Term, got %v", result) + } + // Try to InsertAtPath with a nil value. + if result, err := simpleTree.InsertAtPath(ast.Ref{ast.StringTerm("a")}, nil); err != nil { + if err.Error() != "cannot insert nil value into EditTree" { + t.Fatalf("wrong error: %v", err.Error()) + } + } else { + t.Fatalf("Expected error from InsertAtPath with nil-valued value Term, got %v", result) + } + // Try Filter with a nil-valued path slice. + if result := simpleTree.Filter(nil); ast.Compare(result, ast.ObjectTerm()) != 0 { + t.Fatalf("Expected empty Object from Filter with nil-valued path slice, got: %v", result) + } + if result := simpleTree.Filter([]ast.Ref{{ast.StringTerm("/a")}, nil}); ast.Compare(result, ast.ObjectTerm()) != 0 { + t.Fatalf("Expected empty Object from Filter with nil-valued path slice, got: %v", result) + } +} + +func TestEditTreeString(t *testing.T) { + // A nil-valued EditTree node. + badroot := EditTree{value: nil} + if result := badroot.String(); result != "" { + t.Fatalf("Expected \"\", got %v", result) + } + // A normal EditTree node. + normalTree := NewEditTree(ast.ArrayTerm(ast.IntNumberTerm(0), ast.IntNumberTerm(1), ast.IntNumberTerm(2))) + if result := normalTree.String(); result != "" { + if result != "EditTree[[0, 1, 2]]" { + t.Fatalf("Expected EditTree[[0, 1, 2]], got %v", result) + } + } else { + t.Fatal("Unexpected empty string") + } +} + +func TestEditTreeFilter(t *testing.T) { + cases := []struct { + note string + object string + paths []string + source string + expResult string + expError error + }{ + // Simple scalar. + { + note: "simple scalar", + paths: []string{`[]`}, + source: `2`, + expResult: `2`, + }, + // Top-level keys only. + { + note: "simple top-level key - object", + paths: []string{`"/b"`}, + source: `{"a": 2, "b": 3}`, + expResult: `{"b": 3}`, + }, + { + note: "simple top-level key - set", + paths: []string{`"/b"`}, + source: `{"a", "b", "c"}`, + expResult: `{"b"}`, + }, + { + note: "simple top-level key - array", + paths: []string{`"/1"`}, + source: `["a", "b", "c"]`, + expResult: `["b"]`, + }, + // Nested keys. + { + note: "simple nested key - object", + paths: []string{`"/a/b/c"`}, + source: `{"a": {"b": {"c": 3}, "d": 4}, "e": 5}`, + expResult: `{"a": {"b": {"c": 3}}}`, + }, + { + note: "simple nested key - set", + paths: []string{`[{"b", {"c"}}, {"c"}]`}, + source: `{"a", {"b", {"c"}}}`, + expResult: `{{{"c"}}}`, + }, + { + note: "simple nested key - array", + paths: []string{`"/1/1/0"`}, + source: `["a", ["b", ["c", 3], 4], 5]`, + expResult: `[[["c"]]]`, + }, + // Overlapping paths. + { + note: "overlapping nested keys - object", + paths: []string{`"/a/b/c"`, `"/a"`}, + source: `{"a": {"b": {"c": 3}, "d": 4}, "e": 5}`, + expResult: `{"a": {"b": {"c": 3}, "d": 4}}`, + }, + // Out-of-order array indexes. + { + note: "out-of-order array indexes.", + paths: []string{`"/4"`, `"/0"`, `"/1"`}, + source: `["a", "b", "c", "d", "e"]`, + expResult: `["a", "b", "e"]`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + paths := make([]ast.Ref, 0, len(tc.paths)) + for i := range tc.paths { + path, err := parsePath(ast.MustParseTerm(tc.paths[i])) + if err != nil { + t.Fatal("could not parse path:", tc.paths[i]) + } + paths = append(paths, path) + } + + et := NewEditTree(ast.MustParseTerm(tc.source)) + // If an error occurred, was it the error we expected? If not, then that's an error. + + actual := et.Filter(paths) + // TODO: Nil check here? + // if err != nil { + // t.Fatalf("unexpected error rendering EditTree: %s", err) + // } + + if tc.expResult != "" { + expected := ast.MustParseTerm(tc.expResult) + if expected.Value.Compare(actual.Value) != 0 { + t.Errorf("Wrong result generated from filter list:\n\nExpected:\n\t%v\n\nActual:\n\t%v\n\n", expected, actual) + } + } + }) + } +} + +// Some of the tests in this batch of tests reference the underlying tree +// methods directly. Those tests exist specifically to boost the test +// suite's line coverage. References to scalar/composite are similar in +// purpose: ensuring as many code paths are excercised as possible. +func TestEditTreeApplyPatches(t *testing.T) { + cases := []struct { + note string + object string + patches []string + source string + expResult string + expError error + }{ + // Ops on top-level keys only. + { + note: "simple add", + patches: []string{`{"op": "add", "path": "/b", "value": 3}`}, + source: `{"a": 2}`, + expResult: `{"a": 2, "b": 3}`, + }, + { + note: "simple remove", + patches: []string{`{"op": "remove", "path": "/a"}`}, + source: `{"a": true}`, + expResult: `{}`, + }, + { + note: "simple replace", + patches: []string{`{"op": "replace", "path": "/a", "value": 3}`}, + source: `{"a": true}`, + expResult: `{"a": 3}`, + }, + { + note: "simple move", + patches: []string{`{"op": "move", "path": "/b", "from": "/a"}`}, + source: `{"a": true}`, + expResult: `{"b": true}`, + }, + { + note: "simple copy", + patches: []string{`{"op": "copy", "path": "/b", "from": "/a"}`}, + source: `{"a": true}`, + expResult: `{"a": true, "b": true}`, + }, + { + note: "simple test", + patches: []string{`{"op": "test", "path": "/a", "value": 2}`}, + source: `{"a": 2}`, + expResult: `{"a": 2}`, + }, + // Op chains: + { + note: "simple add-remove chain x3", + patches: []string{ + `{"op": "add", "path": "/a", "value": 3}`, + `{"op": "remove", "path": "/a"}`, + `{"op": "add", "path": "/a", "value": 3}`, + `{"op": "remove", "path": "/a"}`, + `{"op": "add", "path": "/a", "value": 3}`, + `{"op": "remove", "path": "/a"}`, + }, + source: `{"a": true}`, + expResult: `{}`, + }, + { + note: "simple add-move-remove chain x2", + patches: []string{ + `{"op": "add", "path": "/a", "value": 3}`, + `{"op": "move", "path": "/b", "from": "/a"}`, + `{"op": "remove", "path": "/b"}`, + `{"op": "add", "path": "/a", "value": 3}`, + `{"op": "move", "path": "/b", "from": "/a"}`, + `{"op": "remove", "path": "/b"}`, + }, + source: `{"a": true}`, + expResult: `{}`, + }, + { + note: "simple add chain x3", + patches: []string{ + `{"op": "add", "path": "/a", "value": 3}`, + `{"op": "add", "path": "/a", "value": 4}`, + `{"op": "add", "path": "/a", "value": 5}`, + }, + source: `{"a": true}`, + expResult: `{"a": 5}`, + }, + { + note: "simple replace chain x3", + patches: []string{ + `{"op": "replace", "path": "/a", "value": 3}`, + `{"op": "replace", "path": "/a", "value": 4}`, + `{"op": "replace", "path": "/a", "value": 5}`, + }, + source: `{"a": true}`, + expResult: `{"a": 5}`, + }, + { + note: "simple test chain x3", + patches: []string{ + `{"op": "test", "path": "/a", "value": 3}`, + `{"op": "test", "path": "/a", "value": 3}`, + `{"op": "test", "path": "/a", "value": 3}`, + }, + source: `{"a": 3}`, + expResult: `{"a": 3}`, + }, + // Nested operation tests. + { + note: "nested add chain object x3", + patches: []string{ + `{"op": "add", "path": "/a", "value": {"b": {}}}`, + `{"op": "add", "path": "/a/b", "value": {"c": {}}}`, + `{"op": "add", "path": "/a/b/c", "value": {"d": {}}}`, + }, + source: `{"a": {}}`, + expResult: `{"a": {"b": {"c": {"d": {}}}}}`, + }, + { + note: "nested add chain array x3 - composites", + patches: []string{ + `{"op": "add", "path": "/0", "value": []}`, + `{"op": "add", "path": "/0/0", "value": []}`, + `{"op": "add", "path": "/0/0/0", "value": []}`, + }, + source: `[]`, + expResult: `[[[[]]]]`, + }, + { + note: "add chain array x3 - scalars", + patches: []string{ + `{"op": "add", "path": "/0", "value": 1}`, + `{"op": "add", "path": "/0", "value": 2}`, + `{"op": "add", "path": "/0", "value": 3}`, + }, + source: `[0]`, + expResult: `[3, 2, 1, 0]`, + }, + { + note: "remove chain array x3 - scalars", + patches: []string{ + `{"op": "add", "path": "/0", "value": 1}`, + `{"op": "add", "path": "/0", "value": 2}`, + `{"op": "add", "path": "/0", "value": 3}`, + `{"op": "remove", "path": "/0"}`, + `{"op": "remove", "path": "/0"}`, + `{"op": "remove", "path": "/0"}`, + }, + source: `[0]`, + expResult: `[0]`, + }, + { + note: "nested add chain set complex", + patches: []string{ + `{"op": "add", "path": [{1}, 2], "value": 2}`, // {{1, 2}, {2}, {3}} + `{"op": "add", "path": [{2}, 3], "value": 3}`, // {{1, 2}, {2, 3}, {3}} + `{"op": "add", "path": [{3}, {4}], "value": {4}}`, // {{1, 2}, {2, 3}, {3, {4}}} + `{"op": "add", "path": [{3, {4}}, {4}, 5], "value": 5}`, // {{1, 2}, {2, 3}, {3, {4, 5}}} + }, + source: `{{1}, {2}, {3}}`, + expResult: `{{1, 2}, {2, 3}, {3, {4, 5}}}`, + }, + { + note: "nested remove chain object x3", + patches: []string{ + `{"op": "remove", "path": "/a/b/c/d"}`, + `{"op": "remove", "path": "/a/b/c"}`, + `{"op": "remove", "path": "/a/b"}`, + }, + source: `{"a": {"b": {"c": {"d": {}}}}}`, + expResult: `{"a": {}}`, + }, + { + note: "nested remove chain array x3", + patches: []string{ + `{"op": "remove", "path": "/1/2/0"}`, + `{"op": "remove", "path": "/1/1"}`, + `{"op": "remove", "path": "/0"}`, + }, + source: `[1, [2, 3, [4, 5]]]`, + expResult: `[[2, [5]]]`, + }, + // Array operations: + { + note: "array add scalars", + patches: []string{ + `{"op": "add", "path": [0], "value": 1}`, // [1, 0] + `{"op": "add", "path": [0], "value": 2}`, // [2, 1, 0] + `{"op": "add", "path": [0], "value": 3}`, // [3, 2, 1, 0] + }, + source: `[0]`, + expResult: `[3, 2, 1, 0]`, + }, + { + note: "array add composites", + patches: []string{ + `{"op": "add", "path": [0], "value": [1]}`, // [[1], 0] + `{"op": "add", "path": [0], "value": [2]}`, // [[2], [1], 0] + `{"op": "add", "path": [0], "value": [3]}`, // [[3], [2], [1], 0] + }, + source: `[0]`, + expResult: `[[3], [2], [1], 0]`, + }, + { + note: "array append scalar", + patches: []string{ + `{"op": "add", "path": "-", "value": 2}`, // [0, 1] + }, + source: `[0, 1]`, + expResult: `[0, 1, 2]`, + }, + // Object operations: + { + note: "object add/replace bignum scalar", + patches: []string{ + `{"op": "add", "path": [18446744073709551616], "value": 2}`, + `{"op": "add", "path": [18446744073709551616], "value": 3}`, + }, + source: `{}`, + expResult: `{18446744073709551616: 3}`, + }, + // Set operations: + { + note: "set add scalar", + patches: []string{ + `{"op": "add", "path": [2], "value": 2}`, // {"a", 2} + }, + source: `{"a"}`, + expResult: `{"a", 2}`, + }, + { + note: "set remove scalar", + patches: []string{ + `{"op": "remove", "path": [2]}`, // {"a"} + }, + source: `{"a", 2}`, + expResult: `{"a"}`, + }, + { + note: "set remove scalar child", + patches: []string{ + `{"op": "add", "path": ["b"], "value": "b"}`, // {"a", "b"} + `{"op": "add", "path": [2], "value": 2}`, // {"a", "b", 2} + `{"op": "test", "path": [2], "value": 2}`, + `{"op": "remove", "path": ["b"]}`, // {"a", 2} + `{"op": "remove", "path": [2]}`, // {"a"} + }, + source: `{"a"}`, + expResult: `{"a"}`, + }, + { + note: "nested set add composite", + patches: []string{ + `{"op": "add", "path": [{"a"}], "value": {"a"}}`, // {"a", {"a"}} + `{"op": "add", "path": [{"a"}, {"a"}], "value": {"a"}}`, // {"a", {"a", {"a"}}} + `{"op": "add", "path": [{"a", {"a"}}, {"a"}, {"a"}], "value": {"a"}}`, // {"a", {"a", {"a", {"a"}}}} + `{"op": "add", "path": [{"a", {"a", {"a"}}}, {"a", {"a"}}, {"a"}, {"a"}], "value": {"a"}}`, // {"a", {"a", {"a", {"a", {"a"}}}}} + }, + source: `{"a"}`, + expResult: `{"a", {"a", {"a", {"a", {"a"}}}}}`, + }, + { + note: "set remove nested composite", + patches: []string{ + `{"op": "remove", "path": [{"a", {"a", {"a", {"a"}}}}, {"a", {"a", {"a"}}}, {"a", {"a"}}, {"a"}]}`, // {"a", {"a", {"a", {"a"}}}} + `{"op": "remove", "path": [{"a", {"a", {"a"}}}, {"a", {"a"}}, {"a"}]}`, // {"a", {"a", {"a"}}} + `{"op": "remove", "path": [{"a", {"a"}}, {"a"}]}`, // {"a", {"a"}} + `{"op": "remove", "path": [{"a"}]}`, // {"a"} + `{"op": "remove", "path": ["a"]}`, + }, + source: `{"a", {"a", {"a", {"a", {"a"}}}}}`, + expResult: `set()`, + }, + // Operations on the root: + { + note: "remove root", + patches: []string{ + `{"op": "remove", "path": []}`, + }, + source: `{"a"}`, + expResult: ``, + }, + { + note: "add/replace root with scalar", + patches: []string{ + `{"op": "add", "path": [], "value": 3}`, + }, + source: `{"a"}`, + expResult: `3`, + }, + { + note: "add/replace root with composite", + patches: []string{ + `{"op": "add", "path": [], "value": [3]}`, + }, + source: `{"a"}`, + expResult: `[3]`, + }, + + // Error cases. + // Root node cases. + { + note: "remove on empty root errors", + patches: []string{ + `{"op": "remove", "path": []}`, + `{"op": "remove", "path": []}`, + }, + source: `"a"`, + expError: errors.New(`deleted node encountered during delete operation`), + }, + // Primitive/Scalar error cases. + { + note: "nested add on primitive string", + patches: []string{ + `{"op": "add", "path": "/a", "value": "example"}`, + }, + source: `"a"`, + expError: errors.New(`expected composite type, found value: "a" (type: ast.String)`), + }, + { + note: "nested add on primitive number", + patches: []string{ + `{"op": "add", "path": "/1", "value": "example"}`, + }, + source: `2`, + expError: errors.New(`expected composite type, found value: 2 (type: ast.Number)`), + }, + { + note: "nested remove on primitive string", + patches: []string{ + `{"op": "remove", "path": "/a"}`, + }, + source: `"a"`, + expError: errors.New(`expected composite type, found value: "a" (type: ast.String)`), + }, + { + note: "nested remove on primitive number", + patches: []string{ + `{"op": "remove", "path": "/1"}`, + }, + source: `2`, + expError: errors.New(`expected composite type, found value: 2 (type: ast.Number)`), + }, + { + note: "nested remove on nested primitive number", + patches: []string{ + `{"op": "test", "path": "/a/2/b", "value": 3}`, + }, + source: `{"a": 2}`, + expError: errors.New(`expected composite type for path "2", found value: 2 (type: ast.Number)`), + }, + // Object error cases. + { + note: "remove on non-existent Object path", + patches: []string{ + `{"op": "remove", "path": "/b"}`, + }, + source: `{"a": {}}`, + expError: errors.New(`cannot delete child key "b" that does not exist`), + }, + { + note: "add on non-existent nested Object path", + patches: []string{ + `{"op": "add", "path": "/b/c", "value": "example"}`, + }, + source: `{"a": {}}`, + expError: errors.New(`path "b" does not exist in object term {"a": {}}`), + }, + { + note: "remove on non-existent nested Object path", + patches: []string{ + `{"op": "remove", "path": "/b/c"}`, + }, + source: `{"a": {}}`, + expError: errors.New(`path "b" does not exist in object term {"a": {}}`), + }, + { + note: "delete fails on deleted Object path - scalar", + patches: []string{ + `{"op": "remove", "path": "/a"}`, + `{"op": "remove", "path": "/a"}`, + }, + source: `{"a": 2}`, + expError: errors.New(`cannot delete the already deleted scalar node for key "a"`), + }, + { + note: "delete fails on deleted Object path - composite", + patches: []string{ + `{"op": "add", "path": "/a", "value": {2}}`, + `{"op": "remove", "path": "/a"}`, + `{"op": "remove", "path": "/a"}`, + }, + source: `{}`, + expError: errors.New(`cannot delete the already deleted composite node for key "a"`), + }, + { + note: "unfold fails on deleted Object path - scalar", + patches: []string{ + `{"op": "add", "path": "/a", "value": 2}`, + `{"op": "remove", "path": "/a"}`, + `{"op": "test", "path": "/a", "value": 2}`, + }, + source: `{}`, + expError: errors.New(`cannot unfold the already deleted scalar node for key "a"`), + }, + { + note: "unfold fails on deleted Object path - composite", + patches: []string{ + `{"op": "add", "path": "/a", "value": {2}}`, + `{"op": "remove", "path": "/a"}`, + `{"op": "test", "path": "/a", "value": 2}`, + }, + source: `{}`, + expError: errors.New(`cannot unfold the already deleted composite node for key "a"`), + }, + // Array error cases. + { + note: "add on non-existent Array path", + patches: []string{ + `{"op": "add", "path": "/2", "value": "example"}`, + }, + source: `["a"]`, + expError: errors.New(`index for array insertion out of bounds`), + }, + { + note: "remove on non-existent Array path", + patches: []string{ + `{"op": "remove", "path": "/2"}`, + }, + source: `["a"]`, + expError: errors.New(`index for array delete out of bounds`), + }, + { + note: "add on non-existent nested Array path", + patches: []string{ + `{"op": "add", "path": "/0/2", "value": "example"}`, + }, + source: `["a", [1, 2]]`, + expError: errors.New(`expected composite type, found value: "a" (type: ast.String)`), + }, + { + note: "remove on non-existent nested Array path", + patches: []string{ + `{"op": "remove", "path": "/0/1"}`, + }, + source: `["a", [1, 2]]`, + expError: errors.New(`expected composite type, found value: "a" (type: ast.String)`), + }, + { + note: "remove on non-integer number Array path - term array", + patches: []string{ + `{"op": "remove", "path": [1, 4.3]}`, + }, + source: `["a", [1, 2]]`, + expError: errors.New(`invalid number type for indexing`), + }, + { + note: "remove on non-integer number Array path - string", + patches: []string{ + `{"op": "remove", "path": "/1/4.3"}`, + }, + source: `["a", [1, 2]]`, + expError: errors.New(`invalid string for indexing`), + }, + { + note: "remove using number with 0 prefix in Array path", + patches: []string{ + `{"op": "remove", "path": "/1/01"}`, + }, + source: `["a", [1, 2]]`, + expError: errors.New(`leading zeros are not allowed in JSON paths`), + }, + { + note: "add with wrong indexing type in Array path", + patches: []string{ + `{"op": "add", "path": [1, [0]], "value": 4}`, + }, + source: `["a", [1, 2]]`, + expError: errors.New(`invalid type for indexing`), + }, + { + note: "test on non-existent nested Array path", + patches: []string{ + `{"op": "add", "path": "/1", "value": 1}`, + `{"op": "add", "path": "/2", "value": 2}`, + `{"op": "test", "path": "/1/2", "value": "example"}`, + }, + source: `[0]`, + expError: errors.New(`expected composite type for path "2", found value: 1 (type: ast.Number)`), + }, + // The "-" index is always one beyond the end of the array, thus the delete case is an error. + { + note: "array remove scalar using the '-' path errors", + patches: []string{ + `{"op": "remove", "path": "-"}`, + }, + source: `[0, 1, 2]`, + expError: errors.New("index for array delete out of bounds"), // Ref: https://www.rfc-editor.org/rfc/rfc6901, section 4 + }, + // Set error cases. + { + note: "remove on non-existent Set path", + patches: []string{ + `{"op": "remove", "path": "/b"}`, + }, + source: `{"a"}`, + expError: errors.New(`cannot delete child key "b" that does not exist`), + }, + { + note: "add on non-existent nested Set path", + patches: []string{ + `{"op": "add", "path": [{"a", [2, 1]}, [2, 1], 1], "value": {"a"}}`, + }, + source: `{"a"}`, + expError: errors.New(`path {"a", [2, 1]} does not exist in set term {"a"}`), + }, + { + note: "remove on non-existent nested Set path", + patches: []string{ + `{"op": "remove", "path": [{"a", [2, 1]}, [2, 1], 0]}`, + }, + source: `{"a"}`, + expError: errors.New(`path {"a", [2, 1]} does not exist in set term {"a"}`), + }, + { + note: "insert non-matching key value pair into Set", + patches: []string{ + `{"op": "add", "path": ["b"], "value": "c"}`, + }, + source: `{"a"}`, + expError: errors.New(`set key "b" does not equal value to be inserted "c"`), + }, + { + note: "delete fails on deleted Set path - scalar", + patches: []string{ + `{"op": "remove", "path": "/a"}`, + `{"op": "remove", "path": "/a"}`, + }, + source: `{"a"}`, + expError: errors.New(`cannot delete the already deleted scalar node for key "a"`), + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + patches := ast.NewArray() + for i := range tc.patches { + patches = patches.Append(ast.MustParseTerm(tc.patches[i])) + } + + et := NewEditTree(ast.MustParseTerm(tc.source)) + err := patches.Iter(func(term *ast.Term) error { + object, ok := term.Value.(ast.Object) + if !ok { + return errors.New("must be an array of JSON-Patch objects, but at least one element is not an object") + } + patch, err := getPatch(object) + if err != nil { + return err + } + path, err := parsePath(patch.path) + if err != nil { + return err + } + switch patch.op { + case "add": + _, err = et.InsertAtPath(path, patch.value) + if err != nil { + return err + } + case "remove": + _, err = et.DeleteAtPath(path) + if err != nil { + return err + } + case "replace": + _, err = et.DeleteAtPath(path) + if err != nil { + return err + } + _, err = et.InsertAtPath(path, patch.value) + if err != nil { + return err + } + case "move": + from, err := parsePath(patch.from) + if err != nil { + return err + } + chunk, err := et.RenderAtPath(from) + if err != nil { + return err + } + _, err = et.DeleteAtPath(from) + if err != nil { + return err + } + _, err = et.InsertAtPath(path, chunk) + if err != nil { + return err + } + case "copy": + from, err := parsePath(patch.from) + if err != nil { + return err + } + chunk, err := et.RenderAtPath(from) + if err != nil { + return err + } + _, err = et.InsertAtPath(path, chunk) + if err != nil { + return err + } + case "test": + chunk, err := et.RenderAtPath(path) + if err != nil { + return err + } + if !chunk.Equal(patch.value) { + return fmt.Errorf("value from EditTree != patch value.\n\nExpected: %v\n\nFound: %v", patch.value, chunk) + } + } + return nil + }) + // If an error occurred, was it the error we expected? If not, then that's an error. + if err != nil { + if tc.expError != nil { + if tc.expError.Error() != err.Error() { + t.Fatalf("wrong error: %s", err) + } + } else { + t.Fatalf("unexpected error building EditTree: %s", err) + } + } + + actual := et.Render() + // TODO: Nil check here? + // if err != nil { + // t.Fatalf("unexpected error rendering EditTree: %s", err) + // } + + if tc.expResult != "" { + expected := ast.MustParseTerm(tc.expResult) + if expected.Value.Compare(actual.Value) != 0 { + t.Errorf("Wrong result generated from patch list:\n\nExpected:\n\t%v\n\nActual:\n\t%v\n\n", expected, actual) + } + } + }) + } +} + +func parsePath(path *ast.Term) (ast.Ref, error) { + // paths can either be a `/` separated json path or + // an array or set of values + var pathSegments ast.Ref + switch p := path.Value.(type) { + case ast.String: + if p == "" { + return ast.Ref{}, nil + } + parts := strings.Split(strings.TrimLeft(string(p), "/"), "/") + for _, part := range parts { + part = strings.ReplaceAll(strings.ReplaceAll(part, "~1", "/"), "~0", "~") + pathSegments = append(pathSegments, ast.StringTerm(part)) + } + case *ast.Array: + p.Foreach(func(term *ast.Term) { + pathSegments = append(pathSegments, term) + }) + default: + return nil, builtins.NewOperandErr(2, "must be one of {set, array} containing string paths or array of path segments but got %v", ast.ValueName(p)) + } + + return pathSegments, nil +} + +type jsonPatch struct { + op string + path *ast.Term + from *ast.Term + value *ast.Term +} + +func getPatch(o ast.Object) (jsonPatch, error) { + var out jsonPatch + var ok bool + getAttribute := func(attr string) (*ast.Term, error) { + if term := o.Get(ast.StringTerm(attr)); term != nil { + return term, nil + } + + return nil, fmt.Errorf("missing '%s' attribute", attr) + } + + opTerm, err := getAttribute("op") + if err != nil { + return out, err + } + op, ok := opTerm.Value.(ast.String) + if !ok { + return out, errors.New("attribute 'op' must be a string") + } + out.op = string(op) + + pathTerm, err := getAttribute("path") + if err != nil { + return out, err + } + out.path = pathTerm + + // Fetch if present: + fromTerm, err := getAttribute("from") + if err != nil { + switch out.op { + case "move", "copy": + return out, err + } + } else { + out.from = fromTerm + } + + // Fetch if present: + valueTerm, err := getAttribute("value") + if err != nil { + switch out.op { + case "add", "replace", "test": + return out, err + } + } + out.value = valueTerm + + return out, nil +} diff --git a/third_party/opa/internal/file/archive/tarball.go b/third_party/opa/internal/file/archive/tarball.go new file mode 100644 index 000000000000..6b8ba48d159d --- /dev/null +++ b/third_party/opa/internal/file/archive/tarball.go @@ -0,0 +1,42 @@ +package archive + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "strings" +) + +// MustWriteTarGz write the list of file names and content +// into a tarball. +func MustWriteTarGz(files [][2]string) *bytes.Buffer { + var buf bytes.Buffer + gw := gzip.NewWriter(&buf) + defer gw.Close() + tw := tar.NewWriter(gw) + defer tw.Close() + for _, file := range files { + if err := WriteFile(tw, file[0], []byte(file[1])); err != nil { + panic(err) + } + } + return &buf +} + +// WriteFile adds a file header with content to the given tar writer +func WriteFile(tw *tar.Writer, path string, bs []byte) error { + + hdr := &tar.Header{ + Name: "/" + strings.TrimLeft(path, "/"), + Mode: 0600, + Typeflag: tar.TypeReg, + Size: int64(len(bs)), + } + + if err := tw.WriteHeader(hdr); err != nil { + return err + } + + _, err := tw.Write(bs) + return err +} diff --git a/third_party/opa/internal/file/url/url.go b/third_party/opa/internal/file/url/url.go new file mode 100644 index 000000000000..aacc571837c8 --- /dev/null +++ b/third_party/opa/internal/file/url/url.go @@ -0,0 +1,42 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package url contains helpers for dealing with file paths and URLs. +package url + +import ( + "fmt" + "net/url" + "runtime" + "strings" +) + +var goos = runtime.GOOS + +// Clean returns a cleaned file path that may or may not be a URL. +func Clean(path string) (string, error) { + + if strings.Contains(path, "://") { + + url, err := url.Parse(path) + if err != nil { + return "", err + } + + if url.Scheme != "file" { + return "", fmt.Errorf("unsupported URL scheme: %v", path) + } + + path = url.Path + + // Trim leading slash on Windows if present. The url.Path field returned + // by url.Parse has leading slash that causes CreateFile() calls to fail + // on Windows. See https://github.com/golang/go/issues/6027 for details. + if goos == "windows" && len(path) >= 1 && path[0] == '/' { + path = path[1:] + } + } + + return path, nil +} diff --git a/third_party/opa/internal/file/url/url_test.go b/third_party/opa/internal/file/url/url_test.go new file mode 100644 index 000000000000..4b56fd4477b6 --- /dev/null +++ b/third_party/opa/internal/file/url/url_test.go @@ -0,0 +1,50 @@ +package url + +import "testing" + +func TestClean(t *testing.T) { + + cases := []struct { + input string + goos string + exp string + err error + }{ + { + input: "c:/foo", + exp: "c:/foo", + goos: "windows", + }, + { + input: "file:///c:/a/b", + exp: "c:/a/b", + goos: "windows", + }, + { + input: "foo", + exp: "foo", + }, + { + input: "/a/b/c", + exp: "/a/b/c", + }, + { + input: "file:///a/b/c", + exp: "/a/b/c", + }, + } + + for _, tc := range cases { + t.Run(tc.input, func(t *testing.T) { + goos = tc.goos + path, err := Clean(tc.input) + if tc.err != nil { + if err == nil || err == tc.err { + t.Fatalf("Want err: %v but got: %v, err: %v", tc.err, path, err) + } + } else if err != nil || path != tc.exp { + t.Fatalf("Want %v but got: %v, err: %v", tc.exp, path, err) + } + }) + } +} diff --git a/third_party/opa/internal/future/filter_imports.go b/third_party/opa/internal/future/filter_imports.go new file mode 100644 index 000000000000..27ca5559f10a --- /dev/null +++ b/third_party/opa/internal/future/filter_imports.go @@ -0,0 +1,49 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package future + +import "github.com/open-policy-agent/opa/v1/ast" + +// FilterFutureImports filters OUT any future imports from the passed slice of +// `*ast.Import`s. +func FilterFutureImports(imps []*ast.Import) []*ast.Import { + ret := []*ast.Import{} + for _, imp := range imps { + path := imp.Path.Value.(ast.Ref) + if !ast.FutureRootDocument.Equal(path[0]) { + ret = append(ret, imp) + } + } + return ret +} + +// IsAllFutureKeywords returns true if the passed *ast.Import is `future.keywords` +func IsAllFutureKeywords(imp *ast.Import) bool { + path := imp.Path.Value.(ast.Ref) + return len(path) == 2 && + ast.FutureRootDocument.Equal(path[0]) && + path[1].Equal(ast.InternedTerm("keywords")) +} + +// IsFutureKeyword returns true if the passed *ast.Import is `future.keywords.{kw}` +func IsFutureKeyword(imp *ast.Import, kw string) bool { + path := imp.Path.Value.(ast.Ref) + return len(path) == 3 && + ast.FutureRootDocument.Equal(path[0]) && + path[1].Equal(ast.InternedTerm("keywords")) && + path[2].Equal(ast.StringTerm(kw)) +} + +func WhichFutureKeyword(imp *ast.Import) (string, bool) { + path := imp.Path.Value.(ast.Ref) + if len(path) == 3 && + ast.FutureRootDocument.Equal(path[0]) && + path[1].Equal(ast.InternedTerm("keywords")) { + if str, ok := path[2].Value.(ast.String); ok { + return string(str), true + } + } + return "", false +} diff --git a/third_party/opa/internal/future/parser_opts.go b/third_party/opa/internal/future/parser_opts.go new file mode 100644 index 000000000000..eaeb87e29691 --- /dev/null +++ b/third_party/opa/internal/future/parser_opts.go @@ -0,0 +1,43 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package future + +import ( + "errors" + "fmt" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// ParserOptionsFromFutureImports transforms a slice of `ast.Import`s into the +// `ast.ParserOptions` that can be used to parse a statement according to the +// included "future.keywords" and "future.keywords.xyz" imports. +func ParserOptionsFromFutureImports(imports []*ast.Import) (ast.ParserOptions, error) { + popts := ast.ParserOptions{ + FutureKeywords: []string{}, + } + for _, imp := range imports { + path := imp.Path.Value.(ast.Ref) + if !ast.FutureRootDocument.Equal(path[0]) { + continue + } + if len(path) >= 2 { + if string(path[1].Value.(ast.String)) != "keywords" { + return popts, fmt.Errorf("unknown future import: %v", imp) + } + if len(path) == 2 { + // retun, one "future.keywords" import means we can disregard any others + return ast.ParserOptions{AllFutureKeywords: true}, nil + } + } + if len(path) == 3 { + if imp.Alias != "" { + return popts, errors.New("alias not supported") + } + popts.FutureKeywords = append(popts.FutureKeywords, string(path[2].Value.(ast.String))) + } + } + return popts, nil +} diff --git a/third_party/opa/internal/gojsonschema/LICENSE-APACHE-2.0.txt b/third_party/opa/internal/gojsonschema/LICENSE-APACHE-2.0.txt new file mode 100644 index 000000000000..55ede8a42cc9 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/LICENSE-APACHE-2.0.txt @@ -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 2015 xeipuuv + + 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/third_party/opa/internal/gojsonschema/README.md b/third_party/opa/internal/gojsonschema/README.md new file mode 100644 index 000000000000..8308e79dd76c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/README.md @@ -0,0 +1,482 @@ +# gojsonschema (Details of library residing in OPA's internal) + +## Description + +https://github.com/xeipuuv/gojsonschema was duplicated into `internal/gojsonschema` folder and modified to make it possible +to set Rego types in OPA, using schemas returned from `gojsonschema`s `Compile` utility. + +The modifications done are as below: + +1. Some of the private fields in `gojsonschema`'s structs, `schema` and `subSchema` have been exported (publicized) so that OPA's type checking code can access and manipulate the values returned by `gojsonschema`s `Compile` method, + +2. Also, other changes in `gojsonschema`'s code include fixes to satisfy OPA's lint and format checker scripts. Hence, this `internal/gojsonschema` is in conformance with OPA's code style. + +3. Modernize usage of Go in `gojsonschema`, using conventions like type switching and language-built-in map accessing rather than helper methods. + +[![GoDoc](https://godoc.org/github.com/xeipuuv/gojsonschema?status.svg)](https://godoc.org/github.com/xeipuuv/gojsonschema) +[![Build Status](https://travis-ci.org/xeipuuv/gojsonschema.svg)](https://travis-ci.org/xeipuuv/gojsonschema) +[![Go Report Card](https://goreportcard.com/badge/github.com/xeipuuv/gojsonschema)](https://goreportcard.com/report/github.com/xeipuuv/gojsonschema) + +# gojsonschema (Details of original parent repository from README) + +## Description + +An implementation of JSON Schema for the Go programming language. Supports draft-04, draft-06 and draft-07. + +References : + +* http://json-schema.org +* http://json-schema.org/latest/json-schema-core.html +* http://json-schema.org/latest/json-schema-validation.html + +## Installation + +``` +go get github.com/xeipuuv/gojsonschema +``` + +Dependencies : +* [github.com/xeipuuv/gojsonpointer](https://github.com/xeipuuv/gojsonpointer) +* [github.com/xeipuuv/gojsonreference](https://github.com/xeipuuv/gojsonreference) +* [github.com/stretchr/testify/assert](https://github.com/stretchr/testify#assert-package) - (Note, this dependency has + been removed to reduce dependencies in OPA) + +## Usage + +### Example + +```go + +package main + +import ( + "fmt" + "github.com/xeipuuv/gojsonschema" +) + +func main() { + + schemaLoader := gojsonschema.NewReferenceLoader("file:///home/me/schema.json") + documentLoader := gojsonschema.NewReferenceLoader("file:///home/me/document.json") + + result, err := gojsonschema.Validate(schemaLoader, documentLoader) + if err != nil { + panic(err.Error()) + } + + if result.Valid() { + fmt.Printf("The document is valid\n") + } else { + fmt.Printf("The document is not valid. see errors :\n") + for _, desc := range result.Errors() { + fmt.Printf("- %s\n", desc) + } + } +} + + +``` + +#### Loaders + +There are various ways to load your JSON data. +In order to load your schemas and documents, +first declare an appropriate loader : + +* Web / HTTP, using a reference : + +```go +loader := gojsonschema.NewReferenceLoader("http://www.some_host.com/schema.json") +``` + +* Local file, using a reference : + +```go +loader := gojsonschema.NewReferenceLoader("file:///home/me/schema.json") +``` + +References use the URI scheme, the prefix (file://) and a full path to the file are required. + +* JSON strings : + +```go +loader := gojsonschema.NewStringLoader(`{"type": "string"}`) +``` + +* Custom Go types : + +```go +m := map[string]interface{}{"type": "string"} +loader := gojsonschema.NewGoLoader(m) +``` + +And + +```go +type Root struct { + Users []User `json:"users"` +} + +type User struct { + Name string `json:"name"` +} + +... + +data := Root{} +data.Users = append(data.Users, User{"John"}) +data.Users = append(data.Users, User{"Sophia"}) +data.Users = append(data.Users, User{"Bill"}) + +loader := gojsonschema.NewGoLoader(data) +``` + +#### Validation + +Once the loaders are set, validation is easy : + +```go +result, err := gojsonschema.Validate(schemaLoader, documentLoader) +``` + +Alternatively, you might want to load a schema only once and process to multiple validations : + +```go +schema, err := gojsonschema.NewSchema(schemaLoader) +... +result1, err := schema.Validate(documentLoader1) +... +result2, err := schema.Validate(documentLoader2) +... +// etc ... +``` + +To check the result : + +```go + if result.Valid() { + fmt.Printf("The document is valid\n") + } else { + fmt.Printf("The document is not valid. see errors :\n") + for _, err := range result.Errors() { + // Err implements the ResultError interface + fmt.Printf("- %s\n", err) + } + } +``` + + +## Loading local schemas + +By default `file` and `http(s)` references to external schemas are loaded automatically via the file system or via http(s). An external schema can also be loaded using a `SchemaLoader`. + +```go + sl := gojsonschema.NewSchemaLoader() + loader1 := gojsonschema.NewStringLoader(`{ "type" : "string" }`) + err := sl.AddSchema("http://some_host.com/string.json", loader1) +``` + +Alternatively if your schema already has an `$id` you can use the `AddSchemas` function +```go + loader2 := gojsonschema.NewStringLoader(`{ + "$id" : "http://some_host.com/maxlength.json", + "maxLength" : 5 + }`) + err = sl.AddSchemas(loader2) +``` + +The main schema should be passed to the `Compile` function. This main schema can then directly reference the added schemas without needing to download them. +```go + loader3 := gojsonschema.NewStringLoader(`{ + "$id" : "http://some_host.com/main.json", + "allOf" : [ + { "$ref" : "http://some_host.com/string.json" }, + { "$ref" : "http://some_host.com/maxlength.json" } + ] + }`) + + schema, err := sl.Compile(loader3) + + documentLoader := gojsonschema.NewStringLoader(`"hello world"`) + + result, err := schema.Validate(documentLoader) +``` + +It's also possible to pass a `ReferenceLoader` to the `Compile` function that references a loaded schema. + +```go +err = sl.AddSchemas(loader3) +schema, err := sl.Compile(gojsonschema.NewReferenceLoader("http://some_host.com/main.json")) +``` + +Schemas added by `AddSchema` and `AddSchemas` are only validated when the entire schema is compiled, unless meta-schema validation is used. + +## Using a specific draft +By default `gojsonschema` will try to detect the draft of a schema by using the `$schema` keyword and parse it in a strict draft-04, draft-06 or draft-07 mode. If `$schema` is missing, or the draft version is not explicitely set, a hybrid mode is used which merges together functionality of all drafts into one mode. + +Autodectection can be turned off with the `AutoDetect` property. Specific draft versions can be specified with the `Draft` property. + +```go +sl := gojsonschema.NewSchemaLoader() +sl.Draft = gojsonschema.Draft7 +sl.AutoDetect = false +``` + +If autodetection is on (default), a draft-07 schema can savely reference draft-04 schemas and vice-versa, as long as `$schema` is specified in all schemas. + +## Meta-schema validation +Schemas that are added using the `AddSchema`, `AddSchemas` and `Compile` can be validated against their meta-schema by setting the `Validate` property. + +The following example will produce an error as `multipleOf` must be a number. If `Validate` is off (default), this error is only returned at the `Compile` step. + +```go +sl := gojsonschema.NewSchemaLoader() +sl.Validate = true +err := sl.AddSchemas(gojsonschema.NewStringLoader(`{ + "$id" : "http://some_host.com/invalid.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "multipleOf" : true +}`)) + ``` +``` + ``` + +Errors returned by meta-schema validation are more readable and contain more information, which helps significantly if you are developing a schema. + +Meta-schema validation also works with a custom `$schema`. In case `$schema` is missing, or `AutoDetect` is set to `false`, the meta-schema of the used draft is used. + + +## Working with Errors + +The library handles string error codes which you can customize by creating your own gojsonschema.locale and setting it +```go +gojsonschema.Locale = YourCustomLocale{} +``` + +However, each error contains additional contextual information. + +Newer versions of `gojsonschema` may have new additional errors, so code that uses a custom locale will need to be updated when this happens. + +**err.Type()**: *string* Returns the "type" of error that occurred. Note you can also type check. See below + +Note: An error of RequiredType has an err.Type() return value of "required" + + "required": RequiredError + "invalid_type": InvalidTypeError + "number_any_of": NumberAnyOfError + "number_one_of": NumberOneOfError + "number_all_of": NumberAllOfError + "number_not": NumberNotError + "missing_dependency": MissingDependencyError + "internal": InternalError + "const": ConstEror + "enum": EnumError + "array_no_additional_items": ArrayNoAdditionalItemsError + "array_min_items": ArrayMinItemsError + "array_max_items": ArrayMaxItemsError + "unique": ItemsMustBeUniqueError + "contains" : ArrayContainsError + "array_min_properties": ArrayMinPropertiesError + "array_max_properties": ArrayMaxPropertiesError + "additional_property_not_allowed": AdditionalPropertyNotAllowedError + "invalid_property_pattern": InvalidPropertyPatternError + "invalid_property_name": InvalidPropertyNameError + "string_gte": StringLengthGTEError + "string_lte": StringLengthLTEError + "pattern": DoesNotMatchPatternError + "multiple_of": MultipleOfError + "number_gte": NumberGTEError + "number_gt": NumberGTError + "number_lte": NumberLTEError + "number_lt": NumberLTError + "condition_then" : ConditionThenError + "condition_else" : ConditionElseError + +**err.Value()**: *interface{}* Returns the value given + +**err.Context()**: *gojsonschema.JsonContext* Returns the context. This has a String() method that will print something like this: (root).firstName + +**err.Field()**: *string* Returns the fieldname in the format firstName, or for embedded properties, person.firstName. This returns the same as the String() method on *err.Context()* but removes the (root). prefix. + +**err.Description()**: *string* The error description. This is based on the locale you are using. See the beginning of this section for overwriting the locale with a custom implementation. + +**err.DescriptionFormat()**: *string* The error description format. This is relevant if you are adding custom validation errors afterwards to the result. + +**err.Details()**: *gojsonschema.ErrorDetails* Returns a map[string]interface{} of additional error details specific to the error. For example, GTE errors will have a "min" value, LTE will have a "max" value. See errors.go for a full description of all the error details. Every error always contains a "field" key that holds the value of *err.Field()* + +Note in most cases, the err.Details() will be used to generate replacement strings in your locales, and not used directly. These strings follow the text/template format i.e. +``` +{{.field}} must be greater than or equal to {{.min}} +``` + +The library allows you to specify custom template functions, should you require more complex error message handling. +```go +gojsonschema.ErrorTemplateFuncs = map[string]interface{}{ + "allcaps": func(s string) string { + return strings.ToUpper(s) + }, +} +``` + +Given the above definition, you can use the custom function `"allcaps"` in your localization templates: +``` +{{allcaps .field}} must be greater than or equal to {{.min}} +``` + +The above error message would then be rendered with the `field` value in capital letters. For example: +``` +"PASSWORD must be greater than or equal to 8" +``` + +Learn more about what types of template functions you can use in `ErrorTemplateFuncs` by referring to Go's [text/template FuncMap](https://golang.org/pkg/text/template/#FuncMap) type. + +## Formats +JSON Schema allows for optional "format" property to validate instances against well-known formats. gojsonschema ships with all of the formats defined in the spec that you can use like this: + +````json +{"type": "string", "format": "email"} +```` + +Not all formats defined in draft-07 are available. Implemented formats are: + +* `date` +* `time` +* `date-time` +* `hostname`. Subdomains that start with a number are also supported, but this means that it doesn't strictly follow [RFC1034](http://tools.ietf.org/html/rfc1034#section-3.5) and has the implication that ipv4 addresses are also recognized as valid hostnames. +* `email`. Go's email parser deviates slightly from [RFC5322](https://tools.ietf.org/html/rfc5322). Includes unicode support. +* `idn-email`. Same caveat as `email`. +* `ipv4` +* `ipv6` +* `uri`. Includes unicode support. +* `uri-reference`. Includes unicode support. +* `iri` +* `iri-reference` +* `uri-template` +* `uuid` +* `regex`. Go uses the [RE2](https://github.com/google/re2/wiki/Syntax) engine and is not [ECMA262](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) compatible. +* `json-pointer` +* `relative-json-pointer` + +`email`, `uri` and `uri-reference` use the same validation code as their unicode counterparts `idn-email`, `iri` and `iri-reference`. If you rely on unicode support you should use the specific +unicode enabled formats for the sake of interoperability as other implementations might not support unicode in the regular formats. + +The validation code for `uri`, `idn-email` and their relatives use mostly standard library code. + +For repetitive or more complex formats, you can create custom format checkers and add them to gojsonschema like this: + +```go +// Define the format checker +type RoleFormatChecker struct {} + +// Ensure it meets the gojsonschema.FormatChecker interface +func (f RoleFormatChecker) IsFormat(input interface{}) bool { + + asString, ok := input.(string) + if ok == false { + return false + } + + return strings.HasPrefix("ROLE_", asString) +} + +// Add it to the library +gojsonschema.FormatCheckers.Add("role", RoleFormatChecker{}) +```` + +Now to use in your json schema: +````json +{"type": "string", "format": "role"} +```` + +Another example would be to check if the provided integer matches an id on database: + +JSON schema: +```json +{"type": "integer", "format": "ValidUserId"} +``` + +```go +// Define the format checker +type ValidUserIdFormatChecker struct {} + +// Ensure it meets the gojsonschema.FormatChecker interface +func (f ValidUserIdFormatChecker) IsFormat(input interface{}) bool { + + asFloat64, ok := input.(float64) // Numbers are always float64 here + if ok == false { + return false + } + + // XXX + // do the magic on the database looking for the int(asFloat64) + + return true +} + +// Add it to the library +gojsonschema.FormatCheckers.Add("ValidUserId", ValidUserIdFormatChecker{}) +```` + +Formats can also be removed, for example if you want to override one of the formats that is defined by default. + +```go +gojsonschema.FormatCheckers.Remove("hostname") +``` + + +## Additional custom validation +After the validation has run and you have the results, you may add additional +errors using `Result.AddError`. This is useful to maintain the same format within the resultset instead +of having to add special exceptions for your own errors. Below is an example. + +```go +type AnswerInvalidError struct { + gojsonschema.ResultErrorFields +} + +func newAnswerInvalidError(context *gojsonschema.JsonContext, value interface{}, details gojsonschema.ErrorDetails) *AnswerInvalidError { + err := AnswerInvalidError{} + err.SetContext(context) + err.SetType("custom_invalid_error") + // it is important to use SetDescriptionFormat() as this is used to call SetDescription() after it has been parsed + // using the description of err will be overridden by this. + err.SetDescriptionFormat("Answer to the Ultimate Question of Life, the Universe, and Everything is {{.answer}}") + err.SetValue(value) + err.SetDetails(details) + + return &err +} + +func main() { + // ... + schema, err := gojsonschema.NewSchema(schemaLoader) + result, err := gojsonschema.Validate(schemaLoader, documentLoader) + + if true { // some validation + jsonContext := gojsonschema.NewJsonContext("question", nil) + errDetail := gojsonschema.ErrorDetails{ + "answer": 42, + } + result.AddError( + newAnswerInvalidError( + gojsonschema.NewJsonContext("answer", jsonContext), + 52, + errDetail, + ), + errDetail, + ) + } + + return result, err + +} +``` + +This is especially useful if you want to add validation beyond what the +json schema drafts can provide such business specific logic. + +## Uses + +gojsonschema uses the following test suite : + +https://github.com/json-schema/JSON-Schema-Test-Suite diff --git a/third_party/opa/internal/gojsonschema/draft.go b/third_party/opa/internal/gojsonschema/draft.go new file mode 100644 index 000000000000..656804acb734 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/draft.go @@ -0,0 +1,122 @@ +// Copyright 2018 johandorland ( https://github.com/johandorland ) +// +// 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 gojsonschema + +import ( + "errors" + "math" + + "github.com/xeipuuv/gojsonreference" +) + +// Draft is a JSON-schema draft version +type Draft int + +// Supported Draft versions +const ( + Draft4 Draft = 4 + Draft6 Draft = 6 + Draft7 Draft = 7 + Hybrid Draft = math.MaxInt32 +) + +type draftConfig struct { + Version Draft + MetaSchemaURL string + MetaSchema string +} +type draftConfigs []draftConfig + +var drafts draftConfigs + +func init() { + drafts = []draftConfig{ + { + Version: Draft4, + MetaSchemaURL: "http://json-schema.org/draft-04/schema", + MetaSchema: `{"id":"http://json-schema.org/draft-04/schema#","$schema":"http://json-schema.org/draft-04/schema#","description":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"positiveInteger":{"type":"integer","minimum":0},"positiveIntegerDefault0":{"allOf":[{"$ref":"#/definitions/positiveInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"minItems":1,"uniqueItems":true}},"type":"object","properties":{"id":{"type":"string"},"$schema":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"multipleOf":{"type":"number","minimum":0,"exclusiveMinimum":true},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"boolean","default":false},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"boolean","default":false},"maxLength":{"$ref":"#/definitions/positiveInteger"},"minLength":{"$ref":"#/definitions/positiveIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/positiveInteger"},"minItems":{"$ref":"#/definitions/positiveIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"maxProperties":{"$ref":"#/definitions/positiveInteger"},"minProperties":{"$ref":"#/definitions/positiveIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"dependencies":{"exclusiveMaximum":["maximum"],"exclusiveMinimum":["minimum"]},"default":{}}`, + }, + { + Version: Draft6, + MetaSchemaURL: "http://json-schema.org/draft-06/schema", + MetaSchema: `{"$schema":"http://json-schema.org/draft-06/schema#","$id":"http://json-schema.org/draft-06/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"examples":{"type":"array","items":{}},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":{},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":{}}`, + }, + { + Version: Draft7, + MetaSchemaURL: "http://json-schema.org/draft-07/schema", + MetaSchema: `{"$schema":"http://json-schema.org/draft-07/schema#","$id":"http://json-schema.org/draft-07/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"$comment":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":true,"readOnly":{"type":"boolean","default":false},"examples":{"type":"array","items":true},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":true},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":true,"enum":{"type":"array","items":true,"minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"contentMediaType":{"type":"string"},"contentEncoding":{"type":"string"},"if":{"$ref":"#"},"then":{"$ref":"#"},"else":{"$ref":"#"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":true}`, + }, + } +} + +func (dc draftConfigs) GetMetaSchema(url string) string { + for _, config := range dc { + if config.MetaSchemaURL == url { + return config.MetaSchema + } + } + return "" +} +func (dc draftConfigs) GetDraftVersion(url string) *Draft { + for _, config := range dc { + if config.MetaSchemaURL == url { + return &config.Version + } + } + return nil +} +func (dc draftConfigs) GetSchemaURL(draft Draft) string { + for _, config := range dc { + if config.Version == draft { + return config.MetaSchemaURL + } + } + return "" +} + +func parseSchemaURL(documentNode any) (string, *Draft, error) { + if _, ok := documentNode.(bool); ok { + return "", nil, nil + } + + m, ok := documentNode.(map[string]any) + if !ok { + return "", nil, errors.New("schema is invalid") + } + + if v, ok := m[KeySchema]; ok { + s, ok := v.(string) + if !ok { + return "", nil, errors.New(formatErrorDescription( + Locale.MustBeOfType(), + ErrorDetails{ + "key": KeySchema, + "type": TypeString, + })) + } + + schemaReference, err := gojsonreference.NewJsonReference(s) + + if err != nil { + return "", nil, err + } + + schema := schemaReference.String() + + return schema, drafts.GetDraftVersion(schema), nil + } + + return "", nil, nil +} diff --git a/third_party/opa/internal/gojsonschema/errors.go b/third_party/opa/internal/gojsonschema/errors.go new file mode 100644 index 000000000000..4afab261a948 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/errors.go @@ -0,0 +1,331 @@ +// nolint: goconst // String duplication will be handled later by using errors.Is. +package gojsonschema + +import ( + "fmt" + "strings" +) + +type ( + + // FalseError indicates that - + // ErrorDetails: - + FalseError struct { + ResultErrorFields + } + + // RequiredError indicates that a required field is missing + // ErrorDetails: property string + RequiredError struct { + ResultErrorFields + } + + // InvalidTypeError indicates that a field has the incorrect type + // ErrorDetails: expected, given + InvalidTypeError struct { + ResultErrorFields + } + + // NumberAnyOfError is produced in case of a failing "anyOf" validation + // ErrorDetails: - + NumberAnyOfError struct { + ResultErrorFields + } + + // NumberOneOfError is produced in case of a failing "oneOf" validation + // ErrorDetails: - + NumberOneOfError struct { + ResultErrorFields + } + + // NumberAllOfError is produced in case of a failing "allOf" validation + // ErrorDetails: - + NumberAllOfError struct { + ResultErrorFields + } + + // NumberNotError is produced if a "not" validation failed + // ErrorDetails: - + NumberNotError struct { + ResultErrorFields + } + + // MissingDependencyError is produced in case of a "missing dependency" problem + // ErrorDetails: dependency + MissingDependencyError struct { + ResultErrorFields + } + + // InternalError indicates an internal error + // ErrorDetails: error + InternalError struct { + ResultErrorFields + } + + // ConstError indicates a const error + // ErrorDetails: allowed + ConstError struct { + ResultErrorFields + } + + // EnumError indicates an enum error + // ErrorDetails: allowed + EnumError struct { + ResultErrorFields + } + + // ArrayNoAdditionalItemsError is produced if additional items were found, but not allowed + // ErrorDetails: - + ArrayNoAdditionalItemsError struct { + ResultErrorFields + } + + // ArrayMinItemsError is produced if an array contains less items than the allowed minimum + // ErrorDetails: min + ArrayMinItemsError struct { + ResultErrorFields + } + + // ArrayMaxItemsError is produced if an array contains more items than the allowed maximum + // ErrorDetails: max + ArrayMaxItemsError struct { + ResultErrorFields + } + + // ItemsMustBeUniqueError is produced if an array requires unique items, but contains non-unique items + // ErrorDetails: type, i, j + ItemsMustBeUniqueError struct { + ResultErrorFields + } + + // ArrayContainsError is produced if an array contains invalid items + // ErrorDetails: + ArrayContainsError struct { + ResultErrorFields + } + + // ArrayMinPropertiesError is produced if an object contains less properties than the allowed minimum + // ErrorDetails: min + ArrayMinPropertiesError struct { + ResultErrorFields + } + + // ArrayMaxPropertiesError is produced if an object contains more properties than the allowed maximum + // ErrorDetails: max + ArrayMaxPropertiesError struct { + ResultErrorFields + } + + // AdditionalPropertyNotAllowedError is produced if an object has additional properties, but not allowed + // ErrorDetails: property + AdditionalPropertyNotAllowedError struct { + ResultErrorFields + } + + // InvalidPropertyPatternError is produced if an pattern was found + // ErrorDetails: property, pattern + InvalidPropertyPatternError struct { + ResultErrorFields + } + + // InvalidPropertyNameError is produced if an invalid-named property was found + // ErrorDetails: property + InvalidPropertyNameError struct { + ResultErrorFields + } + + // StringLengthGTEError is produced if a string is shorter than the minimum required length + // ErrorDetails: min + StringLengthGTEError struct { + ResultErrorFields + } + + // StringLengthLTEError is produced if a string is longer than the maximum allowed length + // ErrorDetails: max + StringLengthLTEError struct { + ResultErrorFields + } + + // DoesNotMatchPatternError is produced if a string does not match the defined pattern + // ErrorDetails: pattern + DoesNotMatchPatternError struct { + ResultErrorFields + } + + // DoesNotMatchFormatError is produced if a string does not match the defined format + // ErrorDetails: format + DoesNotMatchFormatError struct { + ResultErrorFields + } + + // MultipleOfError is produced if a number is not a multiple of the defined multipleOf + // ErrorDetails: multiple + MultipleOfError struct { + ResultErrorFields + } + + // NumberGTEError is produced if a number is lower than the allowed minimum + // ErrorDetails: min + NumberGTEError struct { + ResultErrorFields + } + + // NumberGTError is produced if a number is lower than, or equal to the specified minimum, and exclusiveMinimum is set + // ErrorDetails: min + NumberGTError struct { + ResultErrorFields + } + + // NumberLTEError is produced if a number is higher than the allowed maximum + // ErrorDetails: max + NumberLTEError struct { + ResultErrorFields + } + + // NumberLTError is produced if a number is higher than, or equal to the specified maximum, and exclusiveMaximum is set + // ErrorDetails: max + NumberLTError struct { + ResultErrorFields + } + + // ConditionThenError is produced if a condition's "then" validation is invalid + // ErrorDetails: - + ConditionThenError struct { + ResultErrorFields + } + + // ConditionElseError is produced if a condition's "else" condition is invalid + // ErrorDetails: - + ConditionElseError struct { + ResultErrorFields + } +) + +// newError takes a ResultError type and sets the type, context, description, details, value, and field +func newError(err ResultError, context *JSONContext, value any, locale locale, details ErrorDetails) { + var t string + var d string + switch err.(type) { + case *FalseError: + t = "false" + d = locale.False() + case *RequiredError: + t = "required" + d = locale.Required() + case *InvalidTypeError: + t = "invalid_type" + d = locale.InvalidType() + case *NumberAnyOfError: + t = "number_any_of" + d = locale.NumberAnyOf() + case *NumberOneOfError: + t = "number_one_of" + d = locale.NumberOneOf() + case *NumberAllOfError: + t = "number_all_of" + d = locale.NumberAllOf() + case *NumberNotError: + t = "number_not" + d = locale.NumberNot() + case *MissingDependencyError: + t = "missing_dependency" + d = locale.MissingDependency() + case *InternalError: + t = "internal" + d = locale.Internal() + case *ConstError: + t = "const" + d = locale.Const() + case *EnumError: + t = "enum" + d = locale.Enum() + case *ArrayNoAdditionalItemsError: + t = "array_no_additional_items" + d = locale.ArrayNoAdditionalItems() + case *ArrayMinItemsError: + t = "array_min_items" + d = locale.ArrayMinItems() + case *ArrayMaxItemsError: + t = "array_max_items" + d = locale.ArrayMaxItems() + case *ItemsMustBeUniqueError: + t = "unique" + d = locale.Unique() + case *ArrayContainsError: + t = "contains" + d = locale.ArrayContains() + case *ArrayMinPropertiesError: + t = "array_min_properties" + d = locale.ArrayMinProperties() + case *ArrayMaxPropertiesError: + t = "array_max_properties" + d = locale.ArrayMaxProperties() + case *AdditionalPropertyNotAllowedError: + t = "additional_property_not_allowed" + d = locale.AdditionalPropertyNotAllowed() + case *InvalidPropertyPatternError: + t = "invalid_property_pattern" + d = locale.InvalidPropertyPattern() + case *InvalidPropertyNameError: + t = "invalid_property_name" + d = locale.InvalidPropertyName() + case *StringLengthGTEError: + t = "string_gte" + d = locale.StringGTE() + case *StringLengthLTEError: + t = "string_lte" + d = locale.StringLTE() + case *DoesNotMatchPatternError: + t = "pattern" + d = locale.DoesNotMatchPattern() + case *DoesNotMatchFormatError: + t = "format" + d = locale.DoesNotMatchFormat() + case *MultipleOfError: + t = "multiple_of" + d = locale.MultipleOf() + case *NumberGTEError: + t = "number_gte" + d = locale.NumberGTE() + case *NumberGTError: + t = "number_gt" + d = locale.NumberGT() + case *NumberLTEError: + t = "number_lte" + d = locale.NumberLTE() + case *NumberLTError: + t = "number_lt" + d = locale.NumberLT() + case *ConditionThenError: + t = "condition_then" + d = locale.ConditionThen() + case *ConditionElseError: + t = "condition_else" + d = locale.ConditionElse() + } + + err.SetType(t) + err.SetContext(context) + err.SetValue(value) + err.SetDetails(details) + err.SetDescriptionFormat(d) + details["field"] = err.Field() + + if _, exists := details["context"]; !exists && context != nil { + details["context"] = context.String() + } + + err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details)) +} + +// formatErrorDescription takes a string in the default text/template +// format and converts it to a string with replacements. The fields come +// from the ErrorDetails struct and vary for each type of error. +func formatErrorDescription(s string, details ErrorDetails) string { + for key, value := range details { + key := fmt.Sprintf("{{.%s}}", key) + value := fmt.Sprintf("%v", value) + s = strings.ReplaceAll(s, key, value) + } + return s +} diff --git a/third_party/opa/internal/gojsonschema/format_checkers.go b/third_party/opa/internal/gojsonschema/format_checkers.go new file mode 100644 index 000000000000..c078e9862f12 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/format_checkers.go @@ -0,0 +1,368 @@ +package gojsonschema + +import ( + "net" + "net/mail" + "net/url" + "regexp" + "strings" + "sync" + "time" +) + +type ( + // FormatChecker is the interface all formatters added to FormatCheckerChain must implement + FormatChecker interface { + // IsFormat checks if input has the correct format + IsFormat(input any) bool + } + + // FormatCheckerChain holds the formatters + FormatCheckerChain struct { + formatters map[string]FormatChecker + } + + // EmailFormatChecker verifies email address formats + EmailFormatChecker struct{} + + // IPV4FormatChecker verifies IP addresses in the IPv4 format + IPV4FormatChecker struct{} + + // IPV6FormatChecker verifies IP addresses in the IPv6 format + IPV6FormatChecker struct{} + + // DateTimeFormatChecker verifies date/time formats per RFC3339 5.6 + // + // Valid formats: + // Partial Time: HH:MM:SS + // Full Date: YYYY-MM-DD + // Full Time: HH:MM:SSZ-07:00 + // Date Time: YYYY-MM-DDTHH:MM:SSZ-0700 + // + // Where + // YYYY = 4DIGIT year + // MM = 2DIGIT month ; 01-12 + // DD = 2DIGIT day-month ; 01-28, 01-29, 01-30, 01-31 based on month/year + // HH = 2DIGIT hour ; 00-23 + // MM = 2DIGIT ; 00-59 + // SS = 2DIGIT ; 00-58, 00-60 based on leap second rules + // T = Literal + // Z = Literal + // + // Note: Nanoseconds are also suported in all formats + // + // http://tools.ietf.org/html/rfc3339#section-5.6 + DateTimeFormatChecker struct{} + + // DateFormatChecker verifies date formats + // + // Valid format: + // Full Date: YYYY-MM-DD + // + // Where + // YYYY = 4DIGIT year + // MM = 2DIGIT month ; 01-12 + // DD = 2DIGIT day-month ; 01-28, 01-29, 01-30, 01-31 based on month/year + DateFormatChecker struct{} + + // TimeFormatChecker verifies time formats + // + // Valid formats: + // Partial Time: HH:MM:SS + // Full Time: HH:MM:SSZ-07:00 + // + // Where + // HH = 2DIGIT hour ; 00-23 + // MM = 2DIGIT ; 00-59 + // SS = 2DIGIT ; 00-58, 00-60 based on leap second rules + // T = Literal + // Z = Literal + TimeFormatChecker struct{} + + // URIFormatChecker validates a URI with a valid Scheme per RFC3986 + URIFormatChecker struct{} + + // URIReferenceFormatChecker validates a URI or relative-reference per RFC3986 + URIReferenceFormatChecker struct{} + + // URITemplateFormatChecker validates a URI template per RFC6570 + URITemplateFormatChecker struct{} + + // HostnameFormatChecker validates a hostname is in the correct format + HostnameFormatChecker struct{} + + // UUIDFormatChecker validates a UUID is in the correct format + UUIDFormatChecker struct{} + + // RegexFormatChecker validates a regex is in the correct format + RegexFormatChecker struct{} + + // JSONPointerFormatChecker validates a JSON Pointer per RFC6901 + JSONPointerFormatChecker struct{} + + // RelativeJSONPointerFormatChecker validates a relative JSON Pointer is in the correct format + RelativeJSONPointerFormatChecker struct{} +) + +var ( + // FormatCheckers holds the valid formatters, and is a public variable + // so library users can add custom formatters + FormatCheckers = FormatCheckerChain{ + formatters: map[string]FormatChecker{ + "date": DateFormatChecker{}, + "time": TimeFormatChecker{}, + "date-time": DateTimeFormatChecker{}, + "hostname": HostnameFormatChecker{}, + "email": EmailFormatChecker{}, + "idn-email": EmailFormatChecker{}, + "ipv4": IPV4FormatChecker{}, + "ipv6": IPV6FormatChecker{}, + "uri": URIFormatChecker{}, + "uri-reference": URIReferenceFormatChecker{}, + "iri": URIFormatChecker{}, + "iri-reference": URIReferenceFormatChecker{}, + "uri-template": URITemplateFormatChecker{}, + "uuid": UUIDFormatChecker{}, + "regex": RegexFormatChecker{}, + "json-pointer": JSONPointerFormatChecker{}, + "relative-json-pointer": RelativeJSONPointerFormatChecker{}, + }, + } + + // Regex credit: https://www.socketloop.com/tutorials/golang-validate-hostname + rxHostname = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`) + + // Use a regex to make sure curly brackets are balanced properly after validating it as a AURI + rxURITemplate = regexp.MustCompile("^([^{]*({[^}]*})?)*$") + + rxUUID = regexp.MustCompile("^(?i)[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$") + + rxJSONPointer = regexp.MustCompile("^(?:/(?:[^~/]|~0|~1)*)*$") + + rxRelJSONPointer = regexp.MustCompile("^(?:0|[1-9][0-9]*)(?:#|(?:/(?:[^~/]|~0|~1)*)*)$") + + lock = new(sync.RWMutex) +) + +// Add adds a FormatChecker to the FormatCheckerChain +// The name used will be the value used for the format key in your json schema +func (c *FormatCheckerChain) Add(name string, f FormatChecker) *FormatCheckerChain { + lock.Lock() + c.formatters[name] = f + lock.Unlock() + + return c +} + +// Remove deletes a FormatChecker from the FormatCheckerChain (if it exists) +func (c *FormatCheckerChain) Remove(name string) *FormatCheckerChain { + lock.Lock() + delete(c.formatters, name) + lock.Unlock() + + return c +} + +// Has checks to see if the FormatCheckerChain holds a FormatChecker with the given name +func (c *FormatCheckerChain) Has(name string) bool { + lock.RLock() + _, ok := c.formatters[name] + lock.RUnlock() + + return ok +} + +// IsFormat will check an input against a FormatChecker with the given name +// to see if it is the correct format +func (c *FormatCheckerChain) IsFormat(name string, input any) bool { + lock.RLock() + f, ok := c.formatters[name] + lock.RUnlock() + + // If a format is unrecognized it should always pass validation + if !ok { + return true + } + + return f.IsFormat(input) +} + +// IsFormat checks if input is a correctly formatted e-mail address +func (f EmailFormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + _, err := mail.ParseAddress(asString) + return err == nil +} + +// IsFormat checks if input is a correctly formatted IPv4-address +func (f IPV4FormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + // Credit: https://github.com/asaskevich/govalidator + ip := net.ParseIP(asString) + return ip != nil && strings.Contains(asString, ".") +} + +// IsFormat checks if input is a correctly formatted IPv6=address +func (f IPV6FormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + // Credit: https://github.com/asaskevich/govalidator + ip := net.ParseIP(asString) + return ip != nil && strings.Contains(asString, ":") +} + +// IsFormat checks if input is a correctly formatted date/time per RFC3339 5.6 +func (f DateTimeFormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + formats := []string{ + "15:04:05", + "15:04:05Z07:00", + "2006-01-02", + time.RFC3339, + time.RFC3339Nano, + } + + for _, format := range formats { + if _, err := time.Parse(format, asString); err == nil { + return true + } + } + + return false +} + +// IsFormat checks if input is a correctly formatted date (YYYY-MM-DD) +func (f DateFormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + _, err := time.Parse("2006-01-02", asString) + return err == nil +} + +// IsFormat checks if input correctly formatted time (HH:MM:SS or HH:MM:SSZ-07:00) +func (f TimeFormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + if _, err := time.Parse("15:04:05Z07:00", asString); err == nil { + return true + } + + _, err := time.Parse("15:04:05", asString) + return err == nil +} + +// IsFormat checks if input is correctly formatted URI with a valid Scheme per RFC3986 +func (f URIFormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + u, err := url.Parse(asString) + + if err != nil || u.Scheme == "" { + return false + } + + return !strings.Contains(asString, `\`) +} + +// IsFormat checks if input is a correctly formatted URI or relative-reference per RFC3986 +func (f URIReferenceFormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + _, err := url.Parse(asString) + return err == nil && !strings.Contains(asString, `\`) +} + +// IsFormat checks if input is a correctly formatted URI template per RFC6570 +func (f URITemplateFormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + u, err := url.Parse(asString) + if err != nil || strings.Contains(asString, `\`) { + return false + } + + return rxURITemplate.MatchString(u.Path) +} + +// IsFormat checks if input is a correctly formatted hostname +func (f HostnameFormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + return rxHostname.MatchString(asString) && len(asString) < 256 +} + +// IsFormat checks if input is a correctly formatted UUID +func (f UUIDFormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + return rxUUID.MatchString(asString) +} + +// IsFormat checks if input is a correctly formatted regular expression +func (f RegexFormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + if asString == "" { + return true + } + _, err := regexp.Compile(asString) + return err == nil +} + +// IsFormat checks if input is a correctly formatted JSON Pointer per RFC6901 +func (f JSONPointerFormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + return rxJSONPointer.MatchString(asString) +} + +// IsFormat checks if input is a correctly formatted relative JSON Pointer +func (f RelativeJSONPointerFormatChecker) IsFormat(input any) bool { + asString, ok := input.(string) + if !ok { + return true + } + + return rxRelJSONPointer.MatchString(asString) +} diff --git a/third_party/opa/internal/gojsonschema/format_checkers_test.go b/third_party/opa/internal/gojsonschema/format_checkers_test.go new file mode 100644 index 000000000000..ed9fb24f2605 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/format_checkers_test.go @@ -0,0 +1,162 @@ +package gojsonschema + +import ( + "encoding/json" + "testing" +) + +func TestUUIDFormatCheckerIsFormat(t *testing.T) { + checker := UUIDFormatChecker{} + + if !checker.IsFormat("01234567-89ab-cdef-0123-456789abcdef") { + t.Error("Expected true, got false") + } + + if !checker.IsFormat("f1234567-89ab-cdef-0123-456789abcdef") { + t.Error("Expected true, got false") + } + + if !checker.IsFormat("01234567-89AB-CDEF-0123-456789ABCDEF") { + t.Error("Expected true, got false") + } + if !checker.IsFormat("F1234567-89AB-CDEF-0123-456789ABCDEF") { + t.Error("Expected true, got false") + } + + if checker.IsFormat("not-a-uuid") { + t.Error("Expected false, got true") + } + + if checker.IsFormat("g1234567-89ab-cdef-0123-456789abcdef") { + t.Error("Expected false, got true") + } +} + +func TestURIReferenceFormatCheckerIsFormat(t *testing.T) { + checker := URIReferenceFormatChecker{} + + if !checker.IsFormat("relative") { + t.Error("Expected true, got false") + } + if !checker.IsFormat("https://dummyhost.com/dummy-path?dummy-qp-name=dummy-qp-value") { + t.Error("Expected true, got false") + } +} + +const formatSchema = `{ + "type": "object", + "properties": { + "arr": {"type": "array", "items": {"type": "string"}, "format": "ArrayChecker"}, + "bool": {"type": "boolean", "format": "BoolChecker"}, + "int": {"format": "IntegerChecker"}, + "name": {"type": "string"}, + "str": {"type": "string", "format": "StringChecker"} + }, + "format": "ObjectChecker", + "required": ["name"] +}` + +type arrayChecker struct{} + +func (c arrayChecker) IsFormat(input any) bool { + arr, ok := input.([]any) + if !ok { + return true + } + for _, v := range arr { + if v == "x" { + return true + } + } + return false +} + +type boolChecker struct{} + +func (c boolChecker) IsFormat(input any) bool { + b, ok := input.(bool) + if !ok { + return true + } + return b +} + +type integerChecker struct{} + +func (c integerChecker) IsFormat(input any) bool { + number, ok := input.(json.Number) + if !ok { + return true + } + f, _ := number.Float64() + return int(f)%2 == 0 +} + +type objectChecker struct{} + +func (c objectChecker) IsFormat(input any) bool { + obj, ok := input.(map[string]any) + if !ok { + return true + } + return obj["name"] == "x" +} + +type stringChecker struct{} + +func (c stringChecker) IsFormat(input any) bool { + str, ok := input.(string) + if !ok { + return true + } + return str == "o" +} + +func TestCustomFormat(t *testing.T) { + FormatCheckers. + Add("ArrayChecker", arrayChecker{}). + Add("BoolChecker", boolChecker{}). + Add("IntegerChecker", integerChecker{}). + Add("ObjectChecker", objectChecker{}). + Add("StringChecker", stringChecker{}) + + sl := NewStringLoader(formatSchema) + validResult, err := Validate(sl, NewGoLoader(map[string]any{ + "arr": []string{"x", "y", "z"}, + "bool": true, + "int": "2", // format not defined for string + "name": "x", + "str": "o", + })) + if err != nil { + t.Error(err) + } + + if !validResult.Valid() { + for _, desc := range validResult.Errors() { + t.Error(desc) + } + } + + invalidResult, err := Validate(sl, NewGoLoader(map[string]any{ + "arr": []string{"a", "b", "c"}, + "bool": false, + "int": 1, + "name": "z", + "str": "a", + })) + if err != nil { + t.Error(err) + } + + if len(invalidResult.Errors()) != 5 { + t.Error("Expected 5 errors, got", len(invalidResult.Errors())) + } + + FormatCheckers. + Remove("ArrayChecker"). + Remove("BoolChecker"). + Remove("IntegerChecker"). + Remove("ObjectChecker"). + Remove("StringChecker") +} diff --git a/third_party/opa/internal/gojsonschema/internalLog.go b/third_party/opa/internal/gojsonschema/internalLog.go new file mode 100644 index 000000000000..bab75112ebaf --- /dev/null +++ b/third_party/opa/internal/gojsonschema/internalLog.go @@ -0,0 +1,37 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Very simple log wrapper. +// Used for debugging/testing purposes. +// +// created 01-01-2015 + +package gojsonschema + +import ( + "log" +) + +const internalLogEnabled = false + +func internalLog(format string, v ...any) { + log.Printf(format, v...) +} diff --git a/third_party/opa/internal/gojsonschema/jsonContext.go b/third_party/opa/internal/gojsonschema/jsonContext.go new file mode 100644 index 000000000000..f131ba585a80 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/jsonContext.go @@ -0,0 +1,73 @@ +// Copyright 2013 MongoDB, Inc. +// +// 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. + +// author tolsen +// author-github https://github.com/tolsen +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Implements a persistent (immutable w/ shared structure) singly-linked list of strings for the purpose of storing a json context +// +// created 04-09-2013 + +package gojsonschema + +import "bytes" + +// JSONContext implements a persistent linked-list of strings +type JSONContext struct { + head string + tail *JSONContext +} + +// NewJSONContext creates a new JSONContext +func NewJSONContext(head string, tail *JSONContext) *JSONContext { + return &JSONContext{head, tail} +} + +// String displays the context in reverse. +// This plays well with the data structure's persistent nature with +// Cons and a json document's tree structure. +func (c *JSONContext) String(del ...string) string { + byteArr := make([]byte, 0, c.stringLen()) + buf := bytes.NewBuffer(byteArr) + c.writeStringToBuffer(buf, del) + + return buf.String() +} + +func (c *JSONContext) stringLen() int { + length := 0 + if c.tail != nil { + length = c.tail.stringLen() + 1 // add 1 for "." + } + + length += len(c.head) + return length +} + +func (c *JSONContext) writeStringToBuffer(buf *bytes.Buffer, del []string) { + if c.tail != nil { + c.tail.writeStringToBuffer(buf, del) + + if len(del) > 0 { + buf.WriteString(del[0]) + } else { + buf.WriteString(".") + } + } + + buf.WriteString(c.head) +} diff --git a/third_party/opa/internal/gojsonschema/jsonLoader.go b/third_party/opa/internal/gojsonschema/jsonLoader.go new file mode 100644 index 000000000000..73f25e3b7fb2 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/jsonLoader.go @@ -0,0 +1,410 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Different strategies to load JSON files. +// Includes References (file and HTTP), JSON strings and Go types. +// +// created 01-02-2015 + +package gojsonschema + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "os" + "path/filepath" + "runtime" + "strings" + "sync" + + "github.com/xeipuuv/gojsonreference" +) + +// NOTE(sr): We need to control from which hosts remote references are +// allowed to be resolved via HTTP requests. It's quite cumbersome to +// add extra parameters to all calls and interfaces involved, so we're +// using a global variable instead: +var allowNet map[string]struct{} +var netMut sync.RWMutex + +func SetAllowNet(hosts []string) { + netMut.Lock() + defer netMut.Unlock() + if hosts == nil { + allowNet = nil // resetting the global + return + } + allowNet = make(map[string]struct{}, len(hosts)) + for _, host := range hosts { + allowNet[host] = struct{}{} + } +} + +func isAllowed(ref *url.URL) bool { + netMut.RLock() + defer netMut.RUnlock() + if allowNet == nil { + return true + } + _, ok := allowNet[ref.Hostname()] + return ok +} + +var osFS = osFileSystem(os.Open) + +// JSONLoader defines the JSON loader interface +type JSONLoader interface { + JSONSource() any + LoadJSON() (any, error) + JSONReference() (gojsonreference.JsonReference, error) + LoaderFactory() JSONLoaderFactory +} + +// JSONLoaderFactory defines the JSON loader factory interface +type JSONLoaderFactory interface { + // New creates a new JSON loader for the given source + New(source string) JSONLoader +} + +// DefaultJSONLoaderFactory is the default JSON loader factory +type DefaultJSONLoaderFactory struct { +} + +// FileSystemJSONLoaderFactory is a JSON loader factory that uses http.FileSystem +type FileSystemJSONLoaderFactory struct { + fs http.FileSystem +} + +// New creates a new JSON loader for the given source +func (d DefaultJSONLoaderFactory) New(source string) JSONLoader { + return &jsonReferenceLoader{ + fs: osFS, + source: source, + } +} + +// New creates a new JSON loader for the given source +func (f FileSystemJSONLoaderFactory) New(source string) JSONLoader { + return &jsonReferenceLoader{ + fs: f.fs, + source: source, + } +} + +// osFileSystem is a functional wrapper for os.Open that implements http.FileSystem. +type osFileSystem func(string) (*os.File, error) + +// Opens a file with the given name +func (o osFileSystem) Open(name string) (http.File, error) { + return o(name) +} + +// JSON Reference loader +// references are used to load JSONs from files and HTTP + +type jsonReferenceLoader struct { + fs http.FileSystem + source string +} + +func (l *jsonReferenceLoader) JSONSource() any { + return l.source +} + +func (l *jsonReferenceLoader) JSONReference() (gojsonreference.JsonReference, error) { + return gojsonreference.NewJsonReference(l.JSONSource().(string)) +} + +func (l *jsonReferenceLoader) LoaderFactory() JSONLoaderFactory { + return &FileSystemJSONLoaderFactory{ + fs: l.fs, + } +} + +// NewReferenceLoader returns a JSON reference loader using the given source and the local OS file system. +func NewReferenceLoader(source string) JSONLoader { + return &jsonReferenceLoader{ + fs: osFS, + source: source, + } +} + +// NewReferenceLoaderFileSystem returns a JSON reference loader using the given source and file system. +func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) JSONLoader { + return &jsonReferenceLoader{ + fs: fs, + source: source, + } +} + +func (l *jsonReferenceLoader) LoadJSON() (any, error) { + + var err error + + reference, err := gojsonreference.NewJsonReference(l.JSONSource().(string)) + if err != nil { + return nil, err + } + + refToURL := reference + refToURL.GetUrl().Fragment = "" + + if reference.HasFileScheme { + + filename := strings.TrimPrefix(refToURL.String(), "file://") + filename, err = url.QueryUnescape(filename) + + if err != nil { + return nil, err + } + + if runtime.GOOS == "windows" { + // on Windows, a file URL may have an extra leading slash, use slashes + // instead of backslashes, and have spaces escaped + filename = strings.TrimPrefix(filename, "/") + filename = filepath.FromSlash(filename) + } + + return l.loadFromFile(filename) + } + + // NOTE(sr): hardcoded metaschema references are not subject to allow_net + // checking; their contents are hardcoded in the library! + // + // returned cached versions for metaschemas for drafts 4, 6 and 7 + // for performance and allow for easier offline use + if metaSchema := drafts.GetMetaSchema(refToURL.String()); metaSchema != "" { + return decodeJSONUsingNumber(strings.NewReader(metaSchema)) + } + + if isAllowed(refToURL.GetUrl()) { + return l.loadFromHTTP(refToURL.String()) + } + + return nil, fmt.Errorf("remote reference loading disabled: %s", reference.String()) +} + +func (l *jsonReferenceLoader) loadFromHTTP(address string) (any, error) { + + resp, err := http.Get(address) + if err != nil { + return nil, err + } + + // must return HTTP Status 200 OK + if resp.StatusCode != http.StatusOK { + return nil, errors.New(formatErrorDescription(Locale.HTTPBadStatus(), ErrorDetails{"status": resp.Status})) + } + + bodyBuff, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return decodeJSONUsingNumber(bytes.NewReader(bodyBuff)) +} + +func (l *jsonReferenceLoader) loadFromFile(path string) (any, error) { + f, err := l.fs.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + bodyBuff, err := io.ReadAll(f) + if err != nil { + return nil, err + } + + return decodeJSONUsingNumber(bytes.NewReader(bodyBuff)) + +} + +// JSON string loader + +type jsonStringLoader struct { + source string +} + +func (l *jsonStringLoader) JSONSource() any { + return l.source +} + +func (l *jsonStringLoader) JSONReference() (gojsonreference.JsonReference, error) { + return gojsonreference.NewJsonReference("#") +} + +func (l *jsonStringLoader) LoaderFactory() JSONLoaderFactory { + return &DefaultJSONLoaderFactory{} +} + +// NewStringLoader creates a new JSONLoader, taking a string as source +func NewStringLoader(source string) JSONLoader { + return &jsonStringLoader{source: source} +} + +func (l *jsonStringLoader) LoadJSON() (any, error) { + + return decodeJSONUsingNumber(strings.NewReader(l.JSONSource().(string))) + +} + +// JSON bytes loader + +type jsonBytesLoader struct { + source []byte +} + +func (l *jsonBytesLoader) JSONSource() any { + return l.source +} + +func (l *jsonBytesLoader) JSONReference() (gojsonreference.JsonReference, error) { + return gojsonreference.NewJsonReference("#") +} + +func (l *jsonBytesLoader) LoaderFactory() JSONLoaderFactory { + return &DefaultJSONLoaderFactory{} +} + +// NewBytesLoader creates a new JSONLoader, taking a `[]byte` as source +func NewBytesLoader(source []byte) JSONLoader { + return &jsonBytesLoader{source: source} +} + +func (l *jsonBytesLoader) LoadJSON() (any, error) { + return decodeJSONUsingNumber(bytes.NewReader(l.JSONSource().([]byte))) +} + +// JSON Go (types) loader +// used to load JSONs from the code as maps, any, structs ... + +type jsonGoLoader struct { + source any +} + +func (l *jsonGoLoader) JSONSource() any { + return l.source +} + +func (l *jsonGoLoader) JSONReference() (gojsonreference.JsonReference, error) { + return gojsonreference.NewJsonReference("#") +} + +func (l *jsonGoLoader) LoaderFactory() JSONLoaderFactory { + return &DefaultJSONLoaderFactory{} +} + +// NewGoLoader creates a new JSONLoader from a given Go struct +func NewGoLoader(source any) JSONLoader { + return &jsonGoLoader{source: source} +} + +func (l *jsonGoLoader) LoadJSON() (any, error) { + + // convert it to a compliant JSON first to avoid types "mismatches" + + jsonBytes, err := json.Marshal(l.JSONSource()) + if err != nil { + return nil, err + } + + return decodeJSONUsingNumber(bytes.NewReader(jsonBytes)) + +} + +type jsonIOLoader struct { + buf *bytes.Buffer +} + +// NewReaderLoader creates a new JSON loader using the provided io.Reader +func NewReaderLoader(source io.Reader) (JSONLoader, io.Reader) { + buf := &bytes.Buffer{} + return &jsonIOLoader{buf: buf}, io.TeeReader(source, buf) +} + +// NewWriterLoader creates a new JSON loader using the provided io.Writer +func NewWriterLoader(source io.Writer) (JSONLoader, io.Writer) { + buf := &bytes.Buffer{} + return &jsonIOLoader{buf: buf}, io.MultiWriter(source, buf) +} + +func (l *jsonIOLoader) JSONSource() any { + return l.buf.String() +} + +func (l *jsonIOLoader) LoadJSON() (any, error) { + return decodeJSONUsingNumber(l.buf) +} + +func (l *jsonIOLoader) JSONReference() (gojsonreference.JsonReference, error) { + return gojsonreference.NewJsonReference("#") +} + +func (l *jsonIOLoader) LoaderFactory() JSONLoaderFactory { + return &DefaultJSONLoaderFactory{} +} + +// JSON raw loader +// In case the JSON is already marshalled to any use this loader +// This is used for testing as otherwise there is no guarantee the JSON is marshalled +// "properly" by using https://golang.org/pkg/encoding/json/#Decoder.UseNumber +type jsonRawLoader struct { + source any +} + +// NewRawLoader creates a new JSON raw loader for the given source +func NewRawLoader(source any) JSONLoader { + return &jsonRawLoader{source: source} +} +func (l *jsonRawLoader) JSONSource() any { + return l.source +} +func (l *jsonRawLoader) LoadJSON() (any, error) { + return l.source, nil +} +func (l *jsonRawLoader) JSONReference() (gojsonreference.JsonReference, error) { + return gojsonreference.NewJsonReference("#") +} +func (l *jsonRawLoader) LoaderFactory() JSONLoaderFactory { + return &DefaultJSONLoaderFactory{} +} + +func decodeJSONUsingNumber(r io.Reader) (any, error) { + + var document any + + decoder := json.NewDecoder(r) + decoder.UseNumber() + + err := decoder.Decode(&document) + if err != nil { + return nil, err + } + + return document, nil + +} diff --git a/third_party/opa/internal/gojsonschema/jsonschema_test.go b/third_party/opa/internal/gojsonschema/jsonschema_test.go new file mode 100644 index 000000000000..54d24ccf7623 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/jsonschema_test.go @@ -0,0 +1,198 @@ +// Copyright 2017 johandorland ( https://github.com/johandorland ) +// +// 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 gojsonschema + +import ( + "encoding/json" + "fmt" + "net/http" + "os" + "path/filepath" + "regexp" + "strings" + "testing" +) + +type jsonSchemaTest struct { + Description string `json:"description"` + // Some tests may not always pass, so some tests are manually edited to include + // an extra attribute whether that specific test should be disabled and skipped + Disabled bool `json:"disabled"` + Schema any `json:"schema"` + Tests []jsonSchemaTestCase `json:"tests"` +} +type jsonSchemaTestCase struct { + Description string `json:"description"` + Data any `json:"data"` + Valid bool `json:"valid"` +} + +// Skip any directories not named appropiately +// filepath.Walk will also visit files in the root of the test directory +var testDirectories = regexp.MustCompile(`(draft\d+)`) +var draftMapping = map[string]Draft{ + "draft4": Draft4, + "draft6": Draft6, + "draft7": Draft7, +} + +func executeTests(t *testing.T, path string) error { + file, err := os.Open(path) + if err != nil { + t.Errorf("Error (%s)\n", err.Error()) + } + fmt.Println(file.Name()) + + var tests []jsonSchemaTest + d := json.NewDecoder(file) + d.UseNumber() + err = d.Decode(&tests) + + if err != nil { + t.Errorf("Error (%s)\n", err.Error()) + } + + draft := Hybrid + if m := testDirectories.FindString(path); m != "" { + draft = draftMapping[m] + } + + for _, test := range tests { + fmt.Println(" " + test.Description) + + if test.Disabled { + continue + } + + testSchemaLoader := NewRawLoader(test.Schema) + sl := NewSchemaLoader() + sl.Draft = draft + sl.Validate = true + testSchema, err := sl.Compile(testSchemaLoader) + + if err != nil { + t.Errorf("Error (%s)\n", err.Error()) + } + + for _, testCase := range test.Tests { + testDataLoader := NewRawLoader(testCase.Data) + result, err := testSchema.Validate(testDataLoader) + + if err != nil { + t.Errorf("Error (%s)\n", err.Error()) + } + + if result.Valid() != testCase.Valid { + schemaString, _ := marshalToJSONString(test.Schema) + testCaseString, _ := marshalToJSONString(testCase.Data) + + t.Errorf("Test failed : %s\n"+ + "%s.\n"+ + "%s.\n"+ + "expects: %t, given %t\n"+ + "Schema: %s\n"+ + "Data: %s\n", + file.Name(), + test.Description, + testCase.Description, + testCase.Valid, + result.Valid(), + *schemaString, + *testCaseString) + } + } + } + return nil +} + +func TestSuite(t *testing.T) { + + wd, err := os.Getwd() + if err != nil { + panic(err.Error()) + } + wd = filepath.Join(wd, "testdata") + + go func() { + err := http.ListenAndServe("localhost:1234", http.FileServer(http.Dir(filepath.Join(wd, "remotes")))) + if err != nil { + + panic(err.Error()) + } + }() + + SetAllowNet(nil) + + err = filepath.Walk(wd, func(path string, fileInfo os.FileInfo, _ error) error { + if fileInfo.IsDir() && path != wd && !testDirectories.MatchString(fileInfo.Name()) { + return filepath.SkipDir + } + if !strings.HasSuffix(fileInfo.Name(), ".json") { + return nil + } + return executeTests(t, path) + }) + if err != nil { + t.Errorf("Error (%s)\n", err.Error()) + } +} + +func TestFormats(t *testing.T) { + wd, err := os.Getwd() + if err != nil { + panic(err.Error()) + } + wd = filepath.Join(wd, "testdata") + + dirs, err := os.ReadDir(wd) + + if err != nil { + panic(err.Error()) + } + + for _, dir := range dirs { + if testDirectories.MatchString(dir.Name()) { + formatJSONFile := filepath.Join(wd, dir.Name(), "optional", "format.json") + if _, err = os.Stat(formatJSONFile); err == nil { + err = executeTests(t, formatJSONFile) + } else { + err = nil + } + + if err != nil { + t.Errorf("Error (%s)\n", err.Error()) + } + + formatsDirectory := filepath.Join(wd, dir.Name(), "optional", "format") + err = filepath.Walk(formatsDirectory, func(path string, fileInfo os.FileInfo, _ error) error { + if fileInfo == nil || !strings.HasSuffix(fileInfo.Name(), ".json") { + return nil + } + return executeTests(t, path) + }) + + if err != nil { + t.Errorf("Error (%s)\n", err.Error()) + } + } + } +} + +func Test_ConcurrentNetAccessModification(_ *testing.T) { + go func() { + SetAllowNet([]string{"something"}) + }() + SetAllowNet(nil) +} diff --git a/third_party/opa/internal/gojsonschema/locales.go b/third_party/opa/internal/gojsonschema/locales.go new file mode 100644 index 000000000000..384fbad29e6b --- /dev/null +++ b/third_party/opa/internal/gojsonschema/locales.go @@ -0,0 +1,472 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Contains const string and messages. +// +// created 01-01-2015 + +package gojsonschema + +type ( + // locale is an interface for defining custom error strings + locale interface { + + // False returns a format-string for "false" schema validation errors + False() string + + // Required returns a format-string for "required" schema validation errors + Required() string + + // InvalidType returns a format-string for "invalid type" schema validation errors + InvalidType() string + + // NumberAnyOf returns a format-string for "anyOf" schema validation errors + NumberAnyOf() string + + // NumberOneOf returns a format-string for "oneOf" schema validation errors + NumberOneOf() string + + // NumberAllOf returns a format-string for "allOf" schema validation errors + NumberAllOf() string + + // NumberNot returns a format-string to format a NumberNotError + NumberNot() string + + // MissingDependency returns a format-string for "missing dependency" schema validation errors + MissingDependency() string + + // Internal returns a format-string for internal errors + Internal() string + + // Const returns a format-string to format a ConstError + Const() string + + // Enum returns a format-string to format an EnumError + Enum() string + + // ArrayNotEnoughItems returns a format-string to format an error for arrays having not enough items to match positional list of schema + ArrayNotEnoughItems() string + + // ArrayNoAdditionalItems returns a format-string to format an ArrayNoAdditionalItemsError + ArrayNoAdditionalItems() string + + // ArrayMinItems returns a format-string to format an ArrayMinItemsError + ArrayMinItems() string + + // ArrayMaxItems returns a format-string to format an ArrayMaxItemsError + ArrayMaxItems() string + + // Unique returns a format-string to format an ItemsMustBeUniqueError + Unique() string + + // ArrayContains returns a format-string to format an ArrayContainsError + ArrayContains() string + + // ArrayMinProperties returns a format-string to format an ArrayMinPropertiesError + ArrayMinProperties() string + + // ArrayMaxProperties returns a format-string to format an ArrayMaxPropertiesError + ArrayMaxProperties() string + + // AdditionalPropertyNotAllowed returns a format-string to format an AdditionalPropertyNotAllowedError + AdditionalPropertyNotAllowed() string + + // InvalidPropertyPattern returns a format-string to format an InvalidPropertyPatternError + InvalidPropertyPattern() string + + // InvalidPropertyName returns a format-string to format an InvalidPropertyNameError + InvalidPropertyName() string + + // StringGTE returns a format-string to format an StringLengthGTEError + StringGTE() string + + // StringLTE returns a format-string to format an StringLengthLTEError + StringLTE() string + + // DoesNotMatchPattern returns a format-string to format an DoesNotMatchPatternError + DoesNotMatchPattern() string + + // DoesNotMatchFormat returns a format-string to format an DoesNotMatchFormatError + DoesNotMatchFormat() string + + // MultipleOf returns a format-string to format an MultipleOfError + MultipleOf() string + + // NumberGTE returns a format-string to format an NumberGTEError + NumberGTE() string + + // NumberGT returns a format-string to format an NumberGTError + NumberGT() string + + // NumberLTE returns a format-string to format an NumberLTEError + NumberLTE() string + + // NumberLT returns a format-string to format an NumberLTError + NumberLT() string + + // Schema validations + + // RegexPattern returns a format-string to format a regex-pattern error + RegexPattern() string + + // GreaterThanZero returns a format-string to format an error where a number must be greater than zero + GreaterThanZero() string + + // MustBeOfA returns a format-string to format an error where a value is of the wrong type + MustBeOfA() string + + // MustBeOfAn returns a format-string to format an error where a value is of the wrong type + MustBeOfAn() string + + // CannotBeUsedWithout returns a format-string to format a "cannot be used without" error + CannotBeUsedWithout() string + + // CannotBeGT returns a format-string to format an error where a value are greater than allowed + CannotBeGT() string + + // MustBeOfType returns a format-string to format an error where a value does not match the required type + MustBeOfType() string + + // MustBeValidRegex returns a format-string to format an error where a regex is invalid + MustBeValidRegex() string + + // MustBeValidFormat returns a format-string to format an error where a value does not match the expected format + MustBeValidFormat() string + + // MustBeGTEZero returns a format-string to format an error where a value must be greater or equal than 0 + MustBeGTEZero() string + + // KeyCannotBeGreaterThan returns a format-string to format an error where a key is greater than the maximum allowed + KeyCannotBeGreaterThan() string + + // KeyItemsMustBeOfType returns a format-string to format an error where a key is of the wrong type + KeyItemsMustBeOfType() string + + // KeyItemsMustBeUnique returns a format-string to format an error where keys are not unique + KeyItemsMustBeUnique() string + + // ReferenceMustBeCanonical returns a format-string to format a "reference must be canonical" error + ReferenceMustBeCanonical() string + + // NotAValidType returns a format-string to format an invalid type error + NotAValidType() string + + // Duplicated returns a format-string to format an error where types are duplicated + Duplicated() string + + // HTTPBadStatus returns a format-string for errors when loading a schema using HTTP + HTTPBadStatus() string + + // ParseError returns a format-string for JSON parsing errors + ParseError() string + + // ConditionThen returns a format-string for ConditionThenError errors + ConditionThen() string + + // ConditionElse returns a format-string for ConditionElseError errors + ConditionElse() string + + // ErrorFormat returns a format string for errors + ErrorFormat() string + } + + // DefaultLocale is the default locale for this package + DefaultLocale struct{} +) + +// False returns a format-string for "false" schema validation errors +func (l DefaultLocale) False() string { + return "False always fails validation" +} + +// Required returns a format-string for "required" schema validation errors +func (l DefaultLocale) Required() string { + return `{{.property}} is required` +} + +// InvalidType returns a format-string for "invalid type" schema validation errors +func (l DefaultLocale) InvalidType() string { + return `Invalid type. Expected: {{.expected}}, given: {{.given}}` +} + +// NumberAnyOf returns a format-string for "anyOf" schema validation errors +func (l DefaultLocale) NumberAnyOf() string { + return `Must validate at least one schema (anyOf)` +} + +// NumberOneOf returns a format-string for "oneOf" schema validation errors +func (l DefaultLocale) NumberOneOf() string { + return `Must validate one and only one schema (oneOf)` +} + +// NumberAllOf returns a format-string for "allOf" schema validation errors +func (l DefaultLocale) NumberAllOf() string { + return `Must validate all the schemas (allOf)` +} + +// NumberNot returns a format-string to format a NumberNotError +func (l DefaultLocale) NumberNot() string { + return `Must not validate the schema (not)` +} + +// MissingDependency returns a format-string for "missing dependency" schema validation errors +func (l DefaultLocale) MissingDependency() string { + return `Has a dependency on {{.dependency}}` +} + +// Internal returns a format-string for internal errors +func (l DefaultLocale) Internal() string { + return `Internal Error {{.error}}` +} + +// Const returns a format-string to format a ConstError +func (l DefaultLocale) Const() string { + return `{{.field}} does not match: {{.allowed}}` +} + +// Enum returns a format-string to format an EnumError +func (l DefaultLocale) Enum() string { + return `{{.field}} must be one of the following: {{.allowed}}` +} + +// ArrayNoAdditionalItems returns a format-string to format an ArrayNoAdditionalItemsError +func (l DefaultLocale) ArrayNoAdditionalItems() string { + return `No additional items allowed on array` +} + +// ArrayNotEnoughItems returns a format-string to format an error for arrays having not enough items to match positional list of schema +func (l DefaultLocale) ArrayNotEnoughItems() string { + return `Not enough items on array to match positional list of schema` +} + +// ArrayMinItems returns a format-string to format an ArrayMinItemsError +func (l DefaultLocale) ArrayMinItems() string { + return `Array must have at least {{.min}} items` +} + +// ArrayMaxItems returns a format-string to format an ArrayMaxItemsError +func (l DefaultLocale) ArrayMaxItems() string { + return `Array must have at most {{.max}} items` +} + +// Unique returns a format-string to format an ItemsMustBeUniqueError +func (l DefaultLocale) Unique() string { + return `{{.type}} items[{{.i}},{{.j}}] must be unique` +} + +// ArrayContains returns a format-string to format an ArrayContainsError +func (l DefaultLocale) ArrayContains() string { + return `At least one of the items must match` +} + +// ArrayMinProperties returns a format-string to format an ArrayMinPropertiesError +func (l DefaultLocale) ArrayMinProperties() string { + return `Must have at least {{.min}} properties` +} + +// ArrayMaxProperties returns a format-string to format an ArrayMaxPropertiesError +func (l DefaultLocale) ArrayMaxProperties() string { + return `Must have at most {{.max}} properties` +} + +// AdditionalPropertyNotAllowed returns a format-string to format an AdditionalPropertyNotAllowedError +func (l DefaultLocale) AdditionalPropertyNotAllowed() string { + return `Additional property {{.property}} is not allowed` +} + +// InvalidPropertyPattern returns a format-string to format an InvalidPropertyPatternError +func (l DefaultLocale) InvalidPropertyPattern() string { + return `Property "{{.property}}" does not match pattern {{.pattern}}` +} + +// InvalidPropertyName returns a format-string to format an InvalidPropertyNameError +func (l DefaultLocale) InvalidPropertyName() string { + return `Property name of "{{.property}}" does not match` +} + +// StringGTE returns a format-string to format an StringLengthGTEError +func (l DefaultLocale) StringGTE() string { + return `String length must be greater than or equal to {{.min}}` +} + +// StringLTE returns a format-string to format an StringLengthLTEError +func (l DefaultLocale) StringLTE() string { + return `String length must be less than or equal to {{.max}}` +} + +// DoesNotMatchPattern returns a format-string to format an DoesNotMatchPatternError +func (l DefaultLocale) DoesNotMatchPattern() string { + return `Does not match pattern '{{.pattern}}'` +} + +// DoesNotMatchFormat returns a format-string to format an DoesNotMatchFormatError +func (l DefaultLocale) DoesNotMatchFormat() string { + return `Does not match format '{{.format}}'` +} + +// MultipleOf returns a format-string to format an MultipleOfError +func (l DefaultLocale) MultipleOf() string { + return `Must be a multiple of {{.multiple}}` +} + +// NumberGTE returns the format string to format a NumberGTEError +func (l DefaultLocale) NumberGTE() string { + return `Must be greater than or equal to {{.min}}` +} + +// NumberGT returns the format string to format a NumberGTError +func (l DefaultLocale) NumberGT() string { + return `Must be greater than {{.min}}` +} + +// NumberLTE returns the format string to format a NumberLTEError +func (l DefaultLocale) NumberLTE() string { + return `Must be less than or equal to {{.max}}` +} + +// NumberLT returns the format string to format a NumberLTError +func (l DefaultLocale) NumberLT() string { + return `Must be less than {{.max}}` +} + +// Schema validators + +// RegexPattern returns a format-string to format a regex-pattern error +func (l DefaultLocale) RegexPattern() string { + return `Invalid regex pattern '{{.pattern}}'` +} + +// GreaterThanZero returns a format-string to format an error where a number must be greater than zero +func (l DefaultLocale) GreaterThanZero() string { + return `{{.number}} must be strictly greater than 0` +} + +// MustBeOfA returns a format-string to format an error where a value is of the wrong type +func (l DefaultLocale) MustBeOfA() string { + return `{{.x}} must be of a {{.y}}` +} + +// MustBeOfAn returns a format-string to format an error where a value is of the wrong type +func (l DefaultLocale) MustBeOfAn() string { + return `{{.x}} must be of an {{.y}}` +} + +// CannotBeUsedWithout returns a format-string to format a "cannot be used without" error +func (l DefaultLocale) CannotBeUsedWithout() string { + return `{{.x}} cannot be used without {{.y}}` +} + +// CannotBeGT returns a format-string to format an error where a value are greater than allowed +func (l DefaultLocale) CannotBeGT() string { + return `{{.x}} cannot be greater than {{.y}}` +} + +// MustBeOfType returns a format-string to format an error where a value does not match the required type +func (l DefaultLocale) MustBeOfType() string { + return `{{.key}} must be of type {{.type}}` +} + +// MustBeValidRegex returns a format-string to format an error where a regex is invalid +func (l DefaultLocale) MustBeValidRegex() string { + return `{{.key}} must be a valid regex` +} + +// MustBeValidFormat returns a format-string to format an error where a value does not match the expected format +func (l DefaultLocale) MustBeValidFormat() string { + return `{{.key}} must be a valid format {{.given}}` +} + +// MustBeGTEZero returns a format-string to format an error where a value must be greater or equal than 0 +func (l DefaultLocale) MustBeGTEZero() string { + return `{{.key}} must be greater than or equal to 0` +} + +// KeyCannotBeGreaterThan returns a format-string to format an error where a value is greater than the maximum allowed +func (l DefaultLocale) KeyCannotBeGreaterThan() string { + return `{{.key}} cannot be greater than {{.y}}` +} + +// KeyItemsMustBeOfType returns a format-string to format an error where a key is of the wrong type +func (l DefaultLocale) KeyItemsMustBeOfType() string { + return `{{.key}} items must be {{.type}}` +} + +// KeyItemsMustBeUnique returns a format-string to format an error where keys are not unique +func (l DefaultLocale) KeyItemsMustBeUnique() string { + return `{{.key}} items must be unique` +} + +// ReferenceMustBeCanonical returns a format-string to format a "reference must be canonical" error +func (l DefaultLocale) ReferenceMustBeCanonical() string { + return `Reference {{.reference}} must be canonical` +} + +// NotAValidType returns a format-string to format an invalid type error +func (l DefaultLocale) NotAValidType() string { + return `has a primitive type that is NOT VALID -- given: {{.given}} Expected valid values are:{{.expected}}` +} + +// Duplicated returns a format-string to format an error where types are duplicated +func (l DefaultLocale) Duplicated() string { + return `{{.type}} type is duplicated` +} + +// HTTPBadStatus returns a format-string for errors when loading a schema using HTTP +func (l DefaultLocale) HTTPBadStatus() string { + return `Could not read schema from HTTP, response status is {{.status}}` +} + +// ErrorFormat returns a format string for errors +// Replacement options: field, description, context, value +func (l DefaultLocale) ErrorFormat() string { + return `{{.field}}: {{.description}}` +} + +// ParseError returns a format-string for JSON parsing errors +func (l DefaultLocale) ParseError() string { + return `Expected: {{.expected}}, given: Invalid JSON` +} + +// ConditionThen returns a format-string for ConditionThenError errors +// If/Else +func (l DefaultLocale) ConditionThen() string { + return `Must validate "then" as "if" was valid` +} + +// ConditionElse returns a format-string for ConditionElseError errors +func (l DefaultLocale) ConditionElse() string { + return `Must validate "else" as "if" was not valid` +} + +// constants +const ( + StringNumber = "Number" + StringArrayOfStrings = "Array Of Strings" + StringArrayOfSchemas = "Array Of Schemas" + StringSchema = "Valid Schema" + StringSchemaOrArrayOfStrings = "Schema Or Array Of Strings" + StringProperties = "Properties" + StringDependency = "Dependency" + StringProperty = "Property" + StringUndefined = "Undefined" + StringContextRoot = "(Root)" + StringRootSchemaProperty = "(Root)" +) diff --git a/third_party/opa/internal/gojsonschema/result.go b/third_party/opa/internal/gojsonschema/result.go new file mode 100644 index 000000000000..0329721c203c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/result.go @@ -0,0 +1,220 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Result and ResultError implementations. +// +// created 01-01-2015 + +package gojsonschema + +import ( + "fmt" + "strings" +) + +type ( + // ErrorDetails is a map of details specific to each error. + // While the values will vary, every error will contain a "field" value + ErrorDetails map[string]any + + // ResultError is the interface that library errors must implement + ResultError interface { + // Field returns the field name without the root context + // i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName + Field() string + // SetType sets the error-type + SetType(string) + // Type returns the error-type + Type() string + // SetContext sets the JSON-context for the error + SetContext(*JSONContext) + // Context returns the JSON-context of the error + Context() *JSONContext + // SetDescription sets a description for the error + SetDescription(string) + // Description returns the description of the error + Description() string + // SetDescriptionFormat sets the format for the description in the default text/template format + SetDescriptionFormat(string) + // DescriptionFormat returns the format for the description in the default text/template format + DescriptionFormat() string + // SetValue sets the value related to the error + SetValue(any) + // Value returns the value related to the error + Value() any + // SetDetails sets the details specific to the error + SetDetails(ErrorDetails) + // Details returns details about the error + Details() ErrorDetails + // String returns a string representation of the error + String() string + } + + // ResultErrorFields holds the fields for each ResultError implementation. + // ResultErrorFields implements the ResultError interface, so custom errors + // can be defined by just embedding this type + ResultErrorFields struct { + errorType string // A string with the type of error (i.e. invalid_type) + context *JSONContext // Tree like notation of the part that failed the validation. ex (root).a.b ... + description string // A human readable error message + descriptionFormat string // A format for human readable error message + value any // Value given by the JSON file that is the source of the error + details ErrorDetails + } + + // Result holds the result of a validation + Result struct { + errors []ResultError + // Scores how well the validation matched. Useful in generating + // better error messages for anyOf and oneOf. + score int + } +) + +// Field returns the field name without the root context +// i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName +func (v *ResultErrorFields) Field() string { + return strings.TrimPrefix(v.context.String(), StringRootSchemaProperty+".") +} + +// SetType sets the error-type +func (v *ResultErrorFields) SetType(errorType string) { + v.errorType = errorType +} + +// Type returns the error-type +func (v *ResultErrorFields) Type() string { + return v.errorType +} + +// SetContext sets the JSON-context for the error +func (v *ResultErrorFields) SetContext(context *JSONContext) { + v.context = context +} + +// Context returns the JSON-context of the error +func (v *ResultErrorFields) Context() *JSONContext { + return v.context +} + +// SetDescription sets a description for the error +func (v *ResultErrorFields) SetDescription(description string) { + v.description = description +} + +// Description returns the description of the error +func (v *ResultErrorFields) Description() string { + return v.description +} + +// SetDescriptionFormat sets the format for the description in the default text/template format +func (v *ResultErrorFields) SetDescriptionFormat(descriptionFormat string) { + v.descriptionFormat = descriptionFormat +} + +// DescriptionFormat returns the format for the description in the default text/template format +func (v *ResultErrorFields) DescriptionFormat() string { + return v.descriptionFormat +} + +// SetValue sets the value related to the error +func (v *ResultErrorFields) SetValue(value any) { + v.value = value +} + +// Value returns the value related to the error +func (v *ResultErrorFields) Value() any { + return v.value +} + +// SetDetails sets the details specific to the error +func (v *ResultErrorFields) SetDetails(details ErrorDetails) { + v.details = details +} + +// Details returns details about the error +func (v *ResultErrorFields) Details() ErrorDetails { + return v.details +} + +// String returns a string representation of the error +func (v ResultErrorFields) String() string { + // as a fallback, the value is displayed go style + valueString := fmt.Sprintf("%v", v.value) + + // marshal the go value value to json + if v.value == nil { + valueString = TypeNull + } else { + if vs, err := marshalToJSONString(v.value); err == nil { + if vs == nil { + valueString = TypeNull + } else { + valueString = *vs + } + } + } + + return formatErrorDescription(Locale.ErrorFormat(), ErrorDetails{ + "context": v.context.String(), + "description": v.description, + "value": valueString, + "field": v.Field(), + }) +} + +// Valid indicates if no errors were found +func (v *Result) Valid() bool { + return len(v.errors) == 0 +} + +// Errors returns the errors that were found +func (v *Result) Errors() []ResultError { + return v.errors +} + +// AddError appends a fully filled error to the error set +// SetDescription() will be called with the result of the parsed err.DescriptionFormat() +func (v *Result) AddError(err ResultError, details ErrorDetails) { + if _, exists := details["context"]; !exists && err.Context() != nil { + details["context"] = err.Context().String() + } + + err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details)) + + v.errors = append(v.errors, err) +} + +func (v *Result) addInternalError(err ResultError, context *JSONContext, value any, details ErrorDetails) { + newError(err, context, value, Locale, details) + v.errors = append(v.errors, err) + v.score -= 2 // results in a net -1 when added to the +1 we get at the end of the validation function +} + +// Used to copy errors from a sub-schema to the main one +func (v *Result) mergeErrors(otherResult *Result) { + v.errors = append(v.errors, otherResult.Errors()...) + v.score += otherResult.score +} + +func (v *Result) incrementScore() { + v.score++ +} diff --git a/third_party/opa/internal/gojsonschema/schema.go b/third_party/opa/internal/gojsonschema/schema.go new file mode 100644 index 000000000000..e8007ee2b634 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/schema.go @@ -0,0 +1,957 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Defines Schema, the main entry to every SubSchema. +// Contains the parsing logic and error checking. +// +// created 26-02-2013 + +package gojsonschema + +import ( + "encoding/json" + "errors" + "math/big" + "regexp" + "text/template" + + "github.com/xeipuuv/gojsonreference" +) + +var ( + // Locale is the default locale to use + // Library users can overwrite with their own implementation + Locale locale = DefaultLocale{} + + // ErrorTemplateFuncs allows you to define custom template funcs for use in localization. + ErrorTemplateFuncs template.FuncMap +) + +// NewSchema instances a schema using the given JSONLoader +func NewSchema(l JSONLoader) (*Schema, error) { + return NewSchemaLoader().Compile(l) +} + +// Schema holds a schema +type Schema struct { + DocumentReference gojsonreference.JsonReference + RootSchema *SubSchema + Pool *schemaPool + ReferencePool *schemaReferencePool +} + +func (d *Schema) parse(document any, draft Draft) error { + d.RootSchema = &SubSchema{Property: StringRootSchemaProperty, Draft: &draft} + return d.parseSchema(document, d.RootSchema) +} + +// SetRootSchemaName sets the root-schema name +func (d *Schema) SetRootSchemaName(name string) { + d.RootSchema.Property = name +} + +// Parses a SubSchema +// +// Pretty long function ( sorry :) )... but pretty straight forward, repetitive and boring +// Not much magic involved here, most of the job is to validate the key names and their values, +// then the values are copied into SubSchema struct +func (d *Schema) parseSchema(documentNode any, currentSchema *SubSchema) error { + + if currentSchema.Draft == nil { + if currentSchema.Parent == nil { + return errors.New("Draft not set") + } + currentSchema.Draft = currentSchema.Parent.Draft + } + + // As of draft 6 "true" is equivalent to an empty schema "{}" and false equals "{"not":{}}" + if *currentSchema.Draft >= Draft6 { + if b, isBool := documentNode.(bool); isBool { + currentSchema.pass = &b + return nil + } + } + + m, isMap := documentNode.(map[string]any) + if !isMap { + return errors.New(formatErrorDescription( + Locale.ParseError(), + ErrorDetails{ + "expected": StringSchema, + }, + )) + } + + if currentSchema.Parent == nil { + currentSchema.Ref = &d.DocumentReference + currentSchema.ID = &d.DocumentReference + } + + if currentSchema.ID == nil && currentSchema.Parent != nil { + currentSchema.ID = currentSchema.Parent.ID + } + + // In draft 6 the id keyword was renamed to $id + // Hybrid mode uses the old id by default + var keyID string + + switch *currentSchema.Draft { + case Draft4: + keyID = KeyID + case Hybrid: + keyID = KeyIDNew + if _, found := m[KeyID]; found { + keyID = KeyID + } + default: + keyID = KeyIDNew + } + + if id, err := getString(m, keyID); err != nil { + return err + } else if id != nil { + jsonReference, err := gojsonreference.NewJsonReference(*id) + if err != nil { + return err + } + if currentSchema == d.RootSchema { + currentSchema.ID = &jsonReference + } else { + ref, err := currentSchema.Parent.ID.Inherits(jsonReference) + if err != nil { + return err + } + currentSchema.ID = ref + } + } + + // definitions + if v, ok := m[KeyDefinitions]; ok { + switch mt := v.(type) { + case map[string]any: + for _, dv := range mt { + switch dv.(type) { + case bool, map[string]any: + newSchema := &SubSchema{Property: KeyDefinitions, Parent: currentSchema} + err := d.parseSchema(dv, newSchema) + if err != nil { + return err + } + default: + return invalidType(StringArrayOfSchemas, KeyDefinitions) + } + } + default: + return invalidType(StringArrayOfSchemas, KeyDefinitions) + } + } + + // title + var err error + currentSchema.title, err = getString(m, KeyTitle) + if err != nil { + return err + } + + // description + currentSchema.description, err = getString(m, KeyDescription) + if err != nil { + return err + } + + // $ref + if ref, err := getString(m, KeyRef); err != nil { + return err + } else if ref != nil { + jsonReference, err := gojsonreference.NewJsonReference(*ref) + if err != nil { + return err + } + + currentSchema.Ref = &jsonReference + + if sch, ok := d.ReferencePool.Get(currentSchema.Ref.String()); ok { + currentSchema.RefSchema = sch + } else { + return d.parseReference(documentNode, currentSchema) + } + } + + // type + if typ, found := m[KeyType]; found { + switch t := typ.(type) { + case string: + err := currentSchema.Types.Add(t) + if err != nil { + return err + } + case []any: + for _, typeInArray := range t { + s, isString := typeInArray.(string) + if !isString { + return invalidType(KeyType, TypeString+"/"+StringArrayOfStrings) + } + if err := currentSchema.Types.Add(s); err != nil { + return err + } + } + default: + return invalidType(KeyType, TypeString+"/"+StringArrayOfStrings) + } + } + + // properties + if properties, found := m[KeyProperties]; found { + err := d.parseProperties(properties, currentSchema) + if err != nil { + return err + } + } + + // additionalProperties + if additionalProperties, found := m[KeyAdditionalProperties]; found { + switch v := additionalProperties.(type) { + case bool: + currentSchema.additionalProperties = v + case map[string]any: + newSchema := &SubSchema{Property: KeyAdditionalProperties, Parent: currentSchema, Ref: currentSchema.Ref} + currentSchema.additionalProperties = newSchema + err := d.parseSchema(v, newSchema) + if err != nil { + return errors.New(err.Error()) + } + default: + return invalidType(TypeBoolean+"/"+StringSchema, KeyAdditionalProperties) + } + } + + // patternProperties + if patternProperties, err := getMap(m, KeyPatternProperties); err != nil { + return err + } else if patternProperties != nil { + if len(patternProperties) > 0 { + currentSchema.patternProperties = make(map[string]*SubSchema) + for k, v := range patternProperties { + _, err := regexp.MatchString(k, "") + if err != nil { + return errors.New(formatErrorDescription( + Locale.RegexPattern(), + ErrorDetails{"pattern": k}, + )) + } + newSchema := &SubSchema{Property: k, Parent: currentSchema, Ref: currentSchema.Ref} + err = d.parseSchema(v, newSchema) + if err != nil { + return errors.New(err.Error()) + } + currentSchema.patternProperties[k] = newSchema + } + } + } + + // propertyNames + if propertyNames, found := m[KeyPropertyNames]; found && *currentSchema.Draft >= Draft6 { + switch propertyNames.(type) { + case bool, map[string]any: + newSchema := &SubSchema{Property: KeyPropertyNames, Parent: currentSchema, Ref: currentSchema.Ref} + currentSchema.propertyNames = newSchema + err := d.parseSchema(propertyNames, newSchema) + if err != nil { + return err + } + default: + return errors.New(formatErrorDescription( + Locale.InvalidType(), + ErrorDetails{ + "expected": StringSchema, + "given": KeyPatternProperties, + }, + )) + } + } + + // dependencies + if dependencies, found := m[KeyDependencies]; found { + err := d.parseDependencies(dependencies, currentSchema) + if err != nil { + return err + } + } + + // items + if items, found := m[KeyItems]; found { + switch i := items.(type) { + case []any: + for _, itemElement := range i { + switch itemElement.(type) { + case map[string]any, bool: + newSchema := &SubSchema{Parent: currentSchema, Property: KeyItems} + newSchema.Ref = currentSchema.Ref + currentSchema.ItemsChildren = append(currentSchema.ItemsChildren, newSchema) + err := d.parseSchema(itemElement, newSchema) + if err != nil { + return err + } + default: + return invalidType(StringSchema+"/"+StringArrayOfSchemas, KeyItems) + } + currentSchema.ItemsChildrenIsSingleSchema = false + } + case map[string]any, bool: + newSchema := &SubSchema{Parent: currentSchema, Property: KeyItems} + newSchema.Ref = currentSchema.Ref + currentSchema.ItemsChildren = append(currentSchema.ItemsChildren, newSchema) + err := d.parseSchema(items, newSchema) + if err != nil { + return err + } + currentSchema.ItemsChildrenIsSingleSchema = true + default: + return invalidType(StringSchema+"/"+StringArrayOfSchemas, KeyItems) + } + } + + // additionalItems + if additionalItems, found := m[KeyAdditionalItems]; found { + switch i := additionalItems.(type) { + case bool: + currentSchema.additionalItems = i + case map[string]any: + newSchema := &SubSchema{Property: KeyAdditionalItems, Parent: currentSchema, Ref: currentSchema.Ref} + currentSchema.additionalItems = newSchema + err := d.parseSchema(additionalItems, newSchema) + if err != nil { + return errors.New(err.Error()) + } + default: + return invalidType(TypeBoolean+"/"+StringSchema, KeyAdditionalItems) + } + } + + // validation : number / integer + if multipleOf, found := m[KeyMultipleOf]; found { + multipleOfValue := mustBeNumber(multipleOf) + if multipleOfValue == nil { + return invalidType(StringNumber, KeyMultipleOf) + } + if multipleOfValue.Cmp(big.NewRat(0, 1)) <= 0 { + return errors.New(formatErrorDescription( + Locale.GreaterThanZero(), + ErrorDetails{"number": KeyMultipleOf}, + )) + } + currentSchema.multipleOf = multipleOfValue + } + + if minimum, found := m[KeyMinimum]; found { + minimumValue := mustBeNumber(minimum) + if minimumValue == nil { + return errors.New(formatErrorDescription( + Locale.MustBeOfA(), + ErrorDetails{"x": KeyMinimum, "y": StringNumber}, + )) + } + currentSchema.minimum = minimumValue + } + + if exclusiveMinimum, found := m[KeyExclusiveMinimum]; found { + switch *currentSchema.Draft { + case Draft4: + boolExclusiveMinimum, isBool := exclusiveMinimum.(bool) + if !isBool { + return invalidType(TypeBoolean, KeyExclusiveMinimum) + } + if currentSchema.minimum == nil { + return errors.New(formatErrorDescription( + Locale.CannotBeUsedWithout(), + ErrorDetails{"x": KeyExclusiveMinimum, "y": KeyMinimum}, + )) + } + if boolExclusiveMinimum { + currentSchema.exclusiveMinimum = currentSchema.minimum + currentSchema.minimum = nil + } + case Hybrid: + switch b := exclusiveMinimum.(type) { + case bool: + if currentSchema.minimum == nil { + return errors.New(formatErrorDescription( + Locale.CannotBeUsedWithout(), + ErrorDetails{"x": KeyExclusiveMinimum, "y": KeyMinimum}, + )) + } + if b { + currentSchema.exclusiveMinimum = currentSchema.minimum + currentSchema.minimum = nil + } + case json.Number: + currentSchema.exclusiveMinimum = mustBeNumber(m[KeyExclusiveMinimum]) + default: + return invalidType(TypeBoolean+"/"+TypeNumber, KeyExclusiveMinimum) + } + default: + if isJSONNumber(exclusiveMinimum) { + currentSchema.exclusiveMinimum = mustBeNumber(exclusiveMinimum) + } else { + return invalidType(TypeNumber, KeyExclusiveMinimum) + } + } + } + + if maximum, found := m[KeyMaximum]; found { + maximumValue := mustBeNumber(maximum) + if maximumValue == nil { + return errors.New(formatErrorDescription( + Locale.MustBeOfA(), + ErrorDetails{"x": KeyMaximum, "y": StringNumber}, + )) + } + currentSchema.maximum = maximumValue + } + + if exclusiveMaximum, found := m[KeyExclusiveMaximum]; found { + switch *currentSchema.Draft { + case Draft4: + boolExclusiveMaximum, isBool := exclusiveMaximum.(bool) + if !isBool { + return invalidType(TypeBoolean, KeyExclusiveMaximum) + } + if currentSchema.maximum == nil { + return errors.New(formatErrorDescription( + Locale.CannotBeUsedWithout(), + ErrorDetails{"x": KeyExclusiveMaximum, "y": KeyMaximum}, + )) + } + if boolExclusiveMaximum { + currentSchema.exclusiveMaximum = currentSchema.maximum + currentSchema.maximum = nil + } + case Hybrid: + switch b := exclusiveMaximum.(type) { + case bool: + if currentSchema.maximum == nil { + return errors.New(formatErrorDescription( + Locale.CannotBeUsedWithout(), + ErrorDetails{"x": KeyExclusiveMaximum, "y": KeyMaximum}, + )) + } + if b { + currentSchema.exclusiveMaximum = currentSchema.maximum + currentSchema.maximum = nil + } + case json.Number: + currentSchema.exclusiveMaximum = mustBeNumber(exclusiveMaximum) + default: + return invalidType(TypeBoolean+"/"+TypeNumber, KeyExclusiveMaximum) + } + default: + if isJSONNumber(exclusiveMaximum) { + currentSchema.exclusiveMaximum = mustBeNumber(exclusiveMaximum) + } else { + return invalidType(TypeNumber, KeyExclusiveMaximum) + } + } + } + + // validation : string + + if minLength, found := m[KeyMinLength]; found { + minLengthIntegerValue := mustBeInteger(minLength) + if minLengthIntegerValue == nil { + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KeyMinLength, "y": TypeInteger}, + )) + } + if *minLengthIntegerValue < 0 { + return errors.New(formatErrorDescription( + Locale.MustBeGTEZero(), + ErrorDetails{"key": KeyMinLength}, + )) + } + currentSchema.minLength = minLengthIntegerValue + } + + if maxLength, found := m[KeyMaxLength]; found { + maxLengthIntegerValue := mustBeInteger(maxLength) + if maxLengthIntegerValue == nil { + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KeyMaxLength, "y": TypeInteger}, + )) + } + if *maxLengthIntegerValue < 0 { + return errors.New(formatErrorDescription( + Locale.MustBeGTEZero(), + ErrorDetails{"key": KeyMaxLength}, + )) + } + currentSchema.maxLength = maxLengthIntegerValue + } + + if currentSchema.minLength != nil && currentSchema.maxLength != nil { + if *currentSchema.minLength > *currentSchema.maxLength { + return errors.New(formatErrorDescription( + Locale.CannotBeGT(), + ErrorDetails{"x": KeyMinLength, "y": KeyMaxLength}, + )) + } + } + + // NOTE: Regex compilation step removed as we don't use "pattern" attribute for + // type checking, and this would cause schemas to fail if they included patterns + // that were valid ECMA regex dialect but not known to Go (i.e. the regexp.Compile + // function), such as patterns with negative lookahead + if _, err := getString(m, KeyPattern); err != nil { + return err + } + + if format, err := getString(m, KeyFormat); err != nil { + return err + } else if format != nil { + currentSchema.format = *format + } + + // validation : object + + if minProperties, found := m[KeyMinProperties]; found { + minPropertiesIntegerValue := mustBeInteger(minProperties) + if minPropertiesIntegerValue == nil { + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KeyMinProperties, "y": TypeInteger}, + )) + } + if *minPropertiesIntegerValue < 0 { + return errors.New(formatErrorDescription( + Locale.MustBeGTEZero(), + ErrorDetails{"key": KeyMinProperties}, + )) + } + currentSchema.minProperties = minPropertiesIntegerValue + } + + if maxProperties, found := m[KeyMaxProperties]; found { + maxPropertiesIntegerValue := mustBeInteger(maxProperties) + if maxPropertiesIntegerValue == nil { + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KeyMaxProperties, "y": TypeInteger}, + )) + } + if *maxPropertiesIntegerValue < 0 { + return errors.New(formatErrorDescription( + Locale.MustBeGTEZero(), + ErrorDetails{"key": KeyMaxProperties}, + )) + } + currentSchema.maxProperties = maxPropertiesIntegerValue + } + + if currentSchema.minProperties != nil && currentSchema.maxProperties != nil { + if *currentSchema.minProperties > *currentSchema.maxProperties { + return errors.New(formatErrorDescription( + Locale.KeyCannotBeGreaterThan(), + ErrorDetails{"key": KeyMinProperties, "y": KeyMaxProperties}, + )) + } + } + + required, err := getSlice(m, KeyRequired) + if err != nil { + return err + } + for _, requiredValue := range required { + s, isString := requiredValue.(string) + if !isString { + return invalidType(TypeString, KeyRequired) + } else if isStringInSlice(currentSchema.required, s) { + return errors.New(formatErrorDescription( + Locale.KeyItemsMustBeUnique(), + ErrorDetails{"key": KeyRequired}, + )) + } + currentSchema.required = append(currentSchema.required, s) + } + + // validation : array + + if minItems, found := m[KeyMinItems]; found { + minItemsIntegerValue := mustBeInteger(minItems) + if minItemsIntegerValue == nil { + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KeyMinItems, "y": TypeInteger}, + )) + } + if *minItemsIntegerValue < 0 { + return errors.New(formatErrorDescription( + Locale.MustBeGTEZero(), + ErrorDetails{"key": KeyMinItems}, + )) + } + currentSchema.minItems = minItemsIntegerValue + } + + if maxItems, found := m[KeyMaxItems]; found { + maxItemsIntegerValue := mustBeInteger(maxItems) + if maxItemsIntegerValue == nil { + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KeyMaxItems, "y": TypeInteger}, + )) + } + if *maxItemsIntegerValue < 0 { + return errors.New(formatErrorDescription( + Locale.MustBeGTEZero(), + ErrorDetails{"key": KeyMaxItems}, + )) + } + currentSchema.maxItems = maxItemsIntegerValue + } + + if uniqueItems, found := m[KeyUniqueItems]; found { + bUniqueItems, isBool := uniqueItems.(bool) + if !isBool { + return errors.New(formatErrorDescription( + Locale.MustBeOfA(), + ErrorDetails{"x": KeyUniqueItems, "y": TypeBoolean}, + )) + } + currentSchema.uniqueItems = bUniqueItems + } + + if contains, found := m[KeyContains]; found && *currentSchema.Draft >= Draft6 { + newSchema := &SubSchema{Property: KeyContains, Parent: currentSchema, Ref: currentSchema.Ref} + currentSchema.contains = newSchema + err := d.parseSchema(contains, newSchema) + if err != nil { + return err + } + } + + // validation : all + if vConst, found := m[KeyConst]; found && *currentSchema.Draft >= Draft6 { + is, err := marshalWithoutNumber(vConst) + if err != nil { + return err + } + currentSchema._const = is + } + + enum, err := getSlice(m, KeyEnum) + if err != nil { + return err + } + for _, v := range enum { + is, err := marshalWithoutNumber(v) + if err != nil { + return err + } + if isStringInSlice(currentSchema.enum, *is) { + return errors.New(formatErrorDescription( + Locale.KeyItemsMustBeUnique(), + ErrorDetails{"key": KeyEnum}, + )) + } + currentSchema.enum = append(currentSchema.enum, *is) + } + + // validation : SubSchema + oneOf, err := getSlice(m, KeyOneOf) + if err != nil { + return err + } + for _, v := range oneOf { + newSchema := &SubSchema{Property: KeyOneOf, Parent: currentSchema, Ref: currentSchema.Ref} + currentSchema.oneOf = append(currentSchema.oneOf, newSchema) + err := d.parseSchema(v, newSchema) + if err != nil { + return err + } + } + + anyOf, err := getSlice(m, KeyAnyOf) + if err != nil { + return err + } + for _, v := range anyOf { + newSchema := &SubSchema{Property: KeyAnyOf, Parent: currentSchema, Ref: currentSchema.Ref} + currentSchema.AnyOf = append(currentSchema.AnyOf, newSchema) + err := d.parseSchema(v, newSchema) + if err != nil { + return err + } + } + + allOf, err := getSlice(m, KeyAllOf) + if err != nil { + return err + } + for _, v := range allOf { + newSchema := &SubSchema{Property: KeyAllOf, Parent: currentSchema, Ref: currentSchema.Ref} + currentSchema.AllOf = append(currentSchema.AllOf, newSchema) + err := d.parseSchema(v, newSchema) + if err != nil { + return err + } + } + + if vNot, found := m[KeyNot]; found { + switch vNot.(type) { + case bool, map[string]any: + newSchema := &SubSchema{Property: KeyNot, Parent: currentSchema, Ref: currentSchema.Ref} + currentSchema.not = newSchema + err := d.parseSchema(vNot, newSchema) + if err != nil { + return err + } + default: + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KeyNot, "y": TypeObject}, + )) + } + } + + if *currentSchema.Draft >= Draft7 { + if vIf, found := m[KeyIf]; found { + switch vIf.(type) { + case bool, map[string]any: + newSchema := &SubSchema{Property: KeyIf, Parent: currentSchema, Ref: currentSchema.Ref} + currentSchema._if = newSchema + err := d.parseSchema(vIf, newSchema) + if err != nil { + return err + } + default: + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KeyIf, "y": TypeObject}, + )) + } + } + + if then, found := m[KeyThen]; found { + switch then.(type) { + case bool, map[string]any: + newSchema := &SubSchema{Property: KeyThen, Parent: currentSchema, Ref: currentSchema.Ref} + currentSchema._then = newSchema + err := d.parseSchema(then, newSchema) + if err != nil { + return err + } + default: + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KeyThen, "y": TypeObject}, + )) + } + } + + if vElse, found := m[KeyElse]; found { + switch vElse.(type) { + case bool, map[string]any: + newSchema := &SubSchema{Property: KeyElse, Parent: currentSchema, Ref: currentSchema.Ref} + currentSchema._else = newSchema + err := d.parseSchema(vElse, newSchema) + if err != nil { + return err + } + default: + return errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": KeyElse, "y": TypeObject}, + )) + } + } + } + + return nil +} + +func (d *Schema) parseReference(_ any, currentSchema *SubSchema) error { + var ( + refdDocumentNode any + dsp *schemaPoolDocument + err error + ) + + newSchema := &SubSchema{Property: KeyRef, Parent: currentSchema, Ref: currentSchema.Ref} + + d.ReferencePool.Add(currentSchema.Ref.String(), newSchema) + + dsp, err = d.Pool.GetDocument(*currentSchema.Ref) + if err != nil { + return err + } + newSchema.ID = currentSchema.Ref + + refdDocumentNode = dsp.Document + newSchema.Draft = dsp.Draft + + switch refdDocumentNode.(type) { + case bool, map[string]any: + // expected + default: + return errors.New(formatErrorDescription( + Locale.MustBeOfType(), + ErrorDetails{"key": StringSchema, "type": TypeObject}, + )) + } + + err = d.parseSchema(refdDocumentNode, newSchema) + if err != nil { + return err + } + + currentSchema.RefSchema = newSchema + + return nil + +} + +func (d *Schema) parseProperties(documentNode any, currentSchema *SubSchema) error { + m, isMap := documentNode.(map[string]any) + if !isMap { + return errors.New(formatErrorDescription( + Locale.MustBeOfType(), + ErrorDetails{"key": StringProperties, "type": TypeObject}, + )) + } + + for k := range m { + schemaProperty := k + newSchema := &SubSchema{Property: schemaProperty, Parent: currentSchema, Ref: currentSchema.Ref} + currentSchema.PropertiesChildren = append(currentSchema.PropertiesChildren, newSchema) + err := d.parseSchema(m[k], newSchema) + if err != nil { + return err + } + } + + return nil +} + +func (d *Schema) parseDependencies(documentNode any, currentSchema *SubSchema) error { + m, isMap := documentNode.(map[string]any) + if !isMap { + return errors.New(formatErrorDescription( + Locale.MustBeOfType(), + ErrorDetails{"key": KeyDependencies, "type": TypeObject}, + )) + } + currentSchema.dependencies = make(map[string]any) + + for k := range m { + switch values := m[k].(type) { + case []any: + var valuesToRegister []string + for _, value := range values { + str, isString := value.(string) + if !isString { + return errors.New(formatErrorDescription( + Locale.MustBeOfType(), + ErrorDetails{ + "key": StringDependency, + "type": StringSchemaOrArrayOfStrings, + }, + )) + } + valuesToRegister = append(valuesToRegister, str) + currentSchema.dependencies[k] = valuesToRegister + } + + case bool, map[string]any: + depSchema := &SubSchema{Property: k, Parent: currentSchema, Ref: currentSchema.Ref} + err := d.parseSchema(m[k], depSchema) + if err != nil { + return err + } + currentSchema.dependencies[k] = depSchema + + default: + return errors.New(formatErrorDescription( + Locale.MustBeOfType(), + ErrorDetails{ + "key": StringDependency, + "type": StringSchemaOrArrayOfStrings, + }, + )) + } + + } + + return nil +} + +func invalidType(expected, given string) error { + return errors.New(formatErrorDescription( + Locale.InvalidType(), + ErrorDetails{ + "expected": expected, + "given": given, + }, + )) +} + +func getString(m map[string]any, key string) (*string, error) { + v, found := m[key] + if !found { + // not found + return nil, nil + } + s, isString := v.(string) + if !isString { + // wrong type + return nil, invalidType(TypeString, key) + } + return &s, nil +} + +func getMap(m map[string]any, key string) (map[string]any, error) { + v, found := m[key] + if !found { + // not found + return nil, nil + } + s, isMap := v.(map[string]any) + if !isMap { + // wrong type + return nil, invalidType(StringSchema, key) + } + return s, nil +} + +func getSlice(m map[string]any, key string) ([]any, error) { + v, found := m[key] + if !found { + return nil, nil + } + s, isArray := v.([]any) + if !isArray { + return nil, errors.New(formatErrorDescription( + Locale.MustBeOfAn(), + ErrorDetails{"x": key, "y": TypeArray}, + )) + } + return s, nil +} diff --git a/third_party/opa/internal/gojsonschema/schemaLoader.go b/third_party/opa/internal/gojsonschema/schemaLoader.go new file mode 100644 index 000000000000..88caa65de2aa --- /dev/null +++ b/third_party/opa/internal/gojsonschema/schemaLoader.go @@ -0,0 +1,206 @@ +// Copyright 2018 johandorland ( https://github.com/johandorland ) +// +// 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 gojsonschema + +import ( + "bytes" + "errors" + + "github.com/xeipuuv/gojsonreference" +) + +// SchemaLoader is used to load schemas +type SchemaLoader struct { + pool *schemaPool + AutoDetect bool + Validate bool + Draft Draft +} + +// NewSchemaLoader creates a new NewSchemaLoader +func NewSchemaLoader() *SchemaLoader { + + ps := &SchemaLoader{ + pool: &schemaPool{ + schemaPoolDocuments: make(map[string]*schemaPoolDocument), + }, + AutoDetect: true, + Validate: false, + Draft: Hybrid, + } + ps.pool.autoDetect = &ps.AutoDetect + + return ps +} + +func (sl *SchemaLoader) validateMetaschema(documentNode any) error { + + var ( + schema string + err error + ) + if sl.AutoDetect { + schema, _, err = parseSchemaURL(documentNode) + if err != nil { + return err + } + } + + // If no explicit "$schema" is used, use the default metaschema associated with the draft used + if schema == "" { + if sl.Draft == Hybrid { + return nil + } + schema = drafts.GetSchemaURL(sl.Draft) + } + + //Disable validation when loading the metaschema to prevent an infinite recursive loop + sl.Validate = false + + metaSchema, err := sl.Compile(NewReferenceLoader(schema)) + + if err != nil { + return err + } + + sl.Validate = true + + result := metaSchema.validateDocument(documentNode) + + if !result.Valid() { + var res bytes.Buffer + for _, err := range result.Errors() { + res.WriteString(err.String()) + res.WriteString("\n") + } + return errors.New(res.String()) + } + + return nil +} + +// AddSchemas adds an arbritrary amount of schemas to the schema cache. As this function does not require +// an explicit URL, every schema should contain an $id, so that it can be referenced by the main schema +func (sl *SchemaLoader) AddSchemas(loaders ...JSONLoader) error { + emptyRef, _ := gojsonreference.NewJsonReference("") + + for _, loader := range loaders { + doc, err := loader.LoadJSON() + + if err != nil { + return err + } + + if sl.Validate { + if err := sl.validateMetaschema(doc); err != nil { + return err + } + } + + // Directly use the Recursive function, so that it get only added to the schema Pool by $id + // and not by the ref of the document as it's empty + if err = sl.pool.parseReferences(doc, emptyRef, false); err != nil { + return err + } + } + + return nil +} + +// AddSchema adds a schema under the provided URL to the schema cache +func (sl *SchemaLoader) AddSchema(url string, loader JSONLoader) error { + + ref, err := gojsonreference.NewJsonReference(url) + + if err != nil { + return err + } + + doc, err := loader.LoadJSON() + + if err != nil { + return err + } + + if sl.Validate { + if err := sl.validateMetaschema(doc); err != nil { + return err + } + } + + return sl.pool.parseReferences(doc, ref, true) +} + +// Compile loads and compiles a schema +func (sl *SchemaLoader) Compile(rootSchema JSONLoader) (*Schema, error) { + + ref, err := rootSchema.JSONReference() + + if err != nil { + return nil, err + } + + d := Schema{} + d.Pool = sl.pool + d.Pool.jsonLoaderFactory = rootSchema.LoaderFactory() + d.DocumentReference = ref + d.ReferencePool = newSchemaReferencePool() + + var doc any + if ref.String() != "" { + // Get document from schema pool + spd, err := d.Pool.GetDocument(d.DocumentReference) + if err != nil { + return nil, err + } + doc = spd.Document + } else { + // Load JSON directly + doc, err = rootSchema.LoadJSON() + if err != nil { + return nil, err + } + // References need only be parsed if loading JSON directly + // as pool.GetDocument already does this for us if loading by reference + err = sl.pool.parseReferences(doc, ref, true) + if err != nil { + return nil, err + } + } + + if sl.Validate { + if err := sl.validateMetaschema(doc); err != nil { + return nil, err + } + } + + draft := sl.Draft + if sl.AutoDetect { + _, detectedDraft, err := parseSchemaURL(doc) + if err != nil { + return nil, err + } + if detectedDraft != nil { + draft = *detectedDraft + } + } + + err = d.parse(doc, draft) + if err != nil { + return nil, err + } + + return &d, nil +} diff --git a/third_party/opa/internal/gojsonschema/schemaLoader_test.go b/third_party/opa/internal/gojsonschema/schemaLoader_test.go new file mode 100644 index 000000000000..c9be1a3c0116 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/schemaLoader_test.go @@ -0,0 +1,223 @@ +// Copyright 2018 johandorland ( https://github.com/johandorland ) +// +// 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 gojsonschema + +import ( + "testing" +) + +func TestSchemaLoaderWithReferenceToAddedSchema(t *testing.T) { + sl := NewSchemaLoader() + err := sl.AddSchemas(NewStringLoader(`{ + "$id" : "http://localhost:1234/test1.json", + "type" : "integer" + }`)) + if err != nil { + t.Errorf("Error adding schema: %v", err) + } + + schema, err := sl.Compile(NewReferenceLoader("http://localhost:1234/test1.json")) + if err != nil { + t.Errorf("Error compiling schema: %v", err) + } + + result, err := schema.Validate(NewStringLoader(`"hello"`)) + if err != nil { + t.Errorf("Error validating schema: %v", err) + } + + if len(result.Errors()) != 1 || result.Errors()[0].Type() != "invalid_type" { + t.Errorf("Expected invalid type erorr, instead got %v", result.Errors()) + } +} + +func TestCrossReference(t *testing.T) { + schema1 := NewStringLoader(`{ + "$ref" : "http://localhost:1234/test3.json", + "definitions" : { + "foo" : { + "type" : "integer" + } + } + }`) + schema2 := NewStringLoader(`{ + "$ref" : "http://localhost:1234/test2.json#/definitions/foo" + }`) + + sl := NewSchemaLoader() + err := sl.AddSchema("http://localhost:1234/test2.json", schema1) + if err != nil { + t.Errorf("Error adding schema: %v", err) + } + + err = sl.AddSchema("http://localhost:1234/test3.json", schema2) + if err != nil { + t.Errorf("Error adding schema: %v", err) + } + + schema, err := sl.Compile(NewStringLoader(`{"$ref" : "http://localhost:1234/test2.json"}`)) + if err != nil { + t.Errorf("Error compiling schema: %v", err) + } + + result, err := schema.Validate(NewStringLoader(`"hello"`)) + if err != nil { + t.Errorf("Error validating schema: %v", err) + } + + if len(result.Errors()) != 1 || result.Errors()[0].Type() != "invalid_type" { + t.Errorf("Expected invalid type erorr, instead got %v", result.Errors()) + } +} + +// Multiple schemas identifying under the same $id should throw an error +func TestDoubleIDReference(t *testing.T) { + sl := NewSchemaLoader() + err := sl.AddSchema("http://localhost:1234/test4.json", NewStringLoader("{}")) + if err != nil { + t.Errorf("Error adding schema: %v", err) + } + + err = sl.AddSchemas(NewStringLoader(`{ "$id" : "http://localhost:1234/test4.json"}`)) + if err == nil { + t.Errorf("Expected error adding schema, got none") + } +} + +func TestCustomMetaSchema(t *testing.T) { + + loader := NewStringLoader(`{ + "$id" : "http://localhost:1234/test5.json", + "properties" : { + "multipleOf" : false + } + }`) + + // Test a custom metaschema in which we disallow the use of the keyword "multipleOf" + sl := NewSchemaLoader() + sl.Validate = true + + err := sl.AddSchemas(loader) + if err != nil { + t.Errorf("Error adding schema: %v", err) + } + + _, err = sl.Compile(NewStringLoader(`{ + "$id" : "http://localhost:1234/test6.json", + "$schema" : "http://localhost:1234/test5.json", + "type" : "string" + }`)) + if err != nil { + t.Errorf("Error compiling schema: %v", err) + } + + sl = NewSchemaLoader() + sl.Validate = true + err = sl.AddSchemas(loader) + if err != nil { + t.Errorf("Error adding schema: %v", err) + } + + _, err = sl.Compile(NewStringLoader(`{ + "$id" : "http://localhost:1234/test7.json", + "$schema" : "http://localhost:1234/test5.json", + "multipleOf" : 5 + }`)) + if err == nil { + t.Errorf("Expected error compiling schema, got none") + } +} + +func TestSchemaDetection(t *testing.T) { + loader := NewStringLoader(`{ + "$schema" : "http://json-schema.org/draft-04/schema#", + "exclusiveMinimum" : 5 + }`) + + // The schema should produce an error in draft-04 mode + _, err := NewSchema(loader) + if err == nil { + t.Errorf("Expected error, got none") + } + + // With schema detection disabled the schema should not produce an error in hybrid mode + sl := NewSchemaLoader() + sl.AutoDetect = false + + _, err = sl.Compile(loader) + if err != nil { + t.Errorf("Error compiling schema: %v", err) + } +} + +func TestDraftCrossReferencing(t *testing.T) { + + // Tests the following cross referencing with any combination + // of autodetection and preset draft version. + + loader1 := NewStringLoader(`{ + "$schema" : "http://json-schema.org/draft-04/schema#", + "id" : "http://localhost:1234/file.json", + "$id" : "http://localhost:1234/file.json", + "exclusiveMinimum" : 5 + }`) + loader2 := NewStringLoader(`{ + "$schema" : "http://json-schema.org/draft-07/schema#", + "id" : "http://localhost:1234/main.json", + "$id" : "http://localhost:1234/main.json", + "$ref" : "file.json" + }`) + + for _, b := range []bool{true, false} { + for _, draft := range []Draft{Draft4, Draft6, Draft7} { + sl := NewSchemaLoader() + sl.Draft = draft + sl.AutoDetect = b + + err := sl.AddSchemas(loader1) + if err != nil { + t.Errorf("Error adding schema: %v", err) + } + + _, err = sl.Compile(loader2) + + // It will always fail with autodetection on as "exclusiveMinimum" : 5 + // is only valid since draft-06. With autodetection off it will pass if + // draft-06 or newer is used + + got := !b && draft >= Draft6 + if (err == nil) != got { + t.Errorf("Expected error: %v, got: %v", !got, err) + } + } + } +} + +const notMapInterface = "not map interface" + +func TestParseSchemaURL_NotMap(t *testing.T) { + //GIVEN + sl := NewGoLoader(notMapInterface) + //WHEN + _, err := NewSchema(sl) + //THEN + if err == nil { + t.Fatalf("Expected error, got none") + } + + if err.Error() != "schema is invalid" { + t.Fatalf("Expected error: %s, got: %s", "schema is invalid", err.Error()) + } +} diff --git a/third_party/opa/internal/gojsonschema/schemaPool.go b/third_party/opa/internal/gojsonschema/schemaPool.go new file mode 100644 index 000000000000..513f8df2cc89 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/schemaPool.go @@ -0,0 +1,230 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Defines resources pooling. +// Eases referencing and avoids downloading the same resource twice. +// +// created 26-02-2013 + +package gojsonschema + +import ( + "errors" + "fmt" + + "github.com/xeipuuv/gojsonreference" +) + +type schemaPoolDocument struct { + Document any + Draft *Draft +} + +type schemaPool struct { + schemaPoolDocuments map[string]*schemaPoolDocument + jsonLoaderFactory JSONLoaderFactory + autoDetect *bool +} + +func (p *schemaPool) parseReferences(document any, ref gojsonreference.JsonReference, pooled bool) error { + + var ( + draft *Draft + err error + reference = ref.String() + ) + // Only the root document should be added to the schema pool if pooled is true + if _, ok := p.schemaPoolDocuments[reference]; pooled && ok { + return fmt.Errorf("Reference already exists: \"%s\"", reference) + } + + if *p.autoDetect { + _, draft, err = parseSchemaURL(document) + if err != nil { + return err + } + } + + err = p.parseReferencesRecursive(document, ref, draft) + + if pooled { + p.schemaPoolDocuments[reference] = &schemaPoolDocument{Document: document, Draft: draft} + } + + return err +} + +func (p *schemaPool) parseReferencesRecursive(document any, ref gojsonreference.JsonReference, draft *Draft) error { + // parseReferencesRecursive parses a JSON document and resolves all $id and $ref references. + // For $ref references it takes into account the $id scope it is in and replaces + // the reference by the absolute resolved reference + + // When encountering errors it fails silently. Error handling is done when the schema + // is syntactically parsed and any error encountered here should also come up there. + switch m := document.(type) { + case []any: + for _, v := range m { + err := p.parseReferencesRecursive(v, ref, draft) + if err != nil { + return err + } + } + case map[string]any: + localRef := &ref + + keyID := KeyIDNew + if _, ok := m[KeyID]; ok { + keyID = KeyID + } + if v, ok := m[keyID]; ok { + if value, isString := v.(string); isString { + jsonReference, err := gojsonreference.NewJsonReference(value) + if err == nil { + localRef, err = ref.Inherits(jsonReference) + if err == nil { + if _, ok := p.schemaPoolDocuments[localRef.String()]; ok { + return fmt.Errorf("Reference already exists: \"%s\"", localRef.String()) + } + p.schemaPoolDocuments[localRef.String()] = &schemaPoolDocument{Document: document, Draft: draft} + } + } + } + } + + if v, ok := m[KeyRef]; ok { + if s, isString := v.(string); isString { + jsonReference, err := gojsonreference.NewJsonReference(s) + if err == nil { + absoluteRef, err := localRef.Inherits(jsonReference) + if err == nil { + m[KeyRef] = absoluteRef.String() + } + } + } + } + + for k, v := range m { + // const and enums should be interpreted literally, so ignore them + if k == KeyConst || k == KeyEnum { + continue + } + // Something like a property or a dependency is not a valid schema, as it might describe properties named "$ref", "$id" or "const", etc + // Therefore don't treat it like a schema. + if k == KeyProperties || k == KeyDependencies || k == KeyPatternProperties { + if child, ok := v.(map[string]any); ok { + for _, v := range child { + err := p.parseReferencesRecursive(v, *localRef, draft) + if err != nil { + return err + } + } + } + } else { + err := p.parseReferencesRecursive(v, *localRef, draft) + if err != nil { + return err + } + } + } + } + return nil +} + +func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*schemaPoolDocument, error) { + + var ( + spd *schemaPoolDocument + draft *Draft + ok bool + err error + ) + + if internalLogEnabled { + internalLog("Get Document ( %s )", reference.String()) + } + + // Create a deep copy, so we can remove the fragment part later on without altering the original + refToURL, _ := gojsonreference.NewJsonReference(reference.String()) + + // First check if the given fragment is a location independent identifier + // http://json-schema.org/latest/json-schema-core.html#rfc.section.8.2.3 + + if spd, ok = p.schemaPoolDocuments[refToURL.String()]; ok { + if internalLogEnabled { + internalLog(" From pool") + } + return spd, nil + } + + // If the given reference is not a location independent identifier, + // strip the fragment and look for a document with it's base URI + + refToURL.GetUrl().Fragment = "" + + if cachedSpd, ok := p.schemaPoolDocuments[refToURL.String()]; ok { + document, _, err := reference.GetPointer().Get(cachedSpd.Document) + + if err != nil { + return nil, err + } + + if internalLogEnabled { + internalLog(" From pool") + } + + spd = &schemaPoolDocument{Document: document, Draft: cachedSpd.Draft} + p.schemaPoolDocuments[reference.String()] = spd + + return spd, nil + } + + // It is not possible to load anything remotely that is not canonical... + if !reference.IsCanonical() { + return nil, errors.New(formatErrorDescription( + Locale.ReferenceMustBeCanonical(), + ErrorDetails{"reference": reference.String()}, + )) + } + + jsonReferenceLoader := p.jsonLoaderFactory.New(reference.String()) + document, err := jsonReferenceLoader.LoadJSON() + + if err != nil { + return nil, err + } + + // add the whole document to the pool for potential re-use + err = p.parseReferences(document, refToURL, true) + if err != nil { + return nil, err + } + + _, draft, _ = parseSchemaURL(document) + + // resolve the potential fragment and also cache it + document, _, err = reference.GetPointer().Get(document) + + if err != nil { + return nil, err + } + + return &schemaPoolDocument{Document: document, Draft: draft}, nil +} diff --git a/third_party/opa/internal/gojsonschema/schemaReferencePool.go b/third_party/opa/internal/gojsonschema/schemaReferencePool.go new file mode 100644 index 000000000000..515702095b25 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/schemaReferencePool.go @@ -0,0 +1,64 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Pool of referenced schemas. +// +// created 25-06-2013 + +package gojsonschema + +type schemaReferencePool struct { + documents map[string]*SubSchema +} + +func newSchemaReferencePool() *schemaReferencePool { + + p := &schemaReferencePool{} + p.documents = make(map[string]*SubSchema) + + return p +} + +func (p *schemaReferencePool) Get(ref string) (r *SubSchema, o bool) { + + if internalLogEnabled { + internalLog("Schema Reference ( %s )", ref) + } + + if sch, ok := p.documents[ref]; ok { + if internalLogEnabled { + internalLog(" From pool") + } + return sch, true + } + + return nil, false +} + +func (p *schemaReferencePool) Add(ref string, sch *SubSchema) { + + if internalLogEnabled { + internalLog("Add Schema Reference %s to pool", ref) + } + if _, ok := p.documents[ref]; !ok { + p.documents[ref] = sch + } +} diff --git a/third_party/opa/internal/gojsonschema/schemaType.go b/third_party/opa/internal/gojsonschema/schemaType.go new file mode 100644 index 000000000000..4abcc6814e88 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/schemaType.go @@ -0,0 +1,78 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Helper structure to handle schema types, and the combination of them. +// +// created 28-02-2013 + +package gojsonschema + +import ( + "errors" + "fmt" + "slices" + "strings" +) + +type jsonSchemaType struct { + types []string +} + +// Is the schema typed ? that is containing at least one type +// When not typed, the schema does not need any type validation +func (t *jsonSchemaType) IsTyped() bool { + return len(t.types) > 0 +} + +func (t *jsonSchemaType) Add(etype string) error { + + if !isStringInSlice(JSONTypes, etype) { + return errors.New(formatErrorDescription(Locale.NotAValidType(), ErrorDetails{"given": "/" + etype + "/", "expected": JSONTypes})) + } + + if t.Contains(etype) { + return errors.New(formatErrorDescription(Locale.Duplicated(), ErrorDetails{"type": etype})) + } + + t.types = append(t.types, etype) + + return nil +} + +func (t *jsonSchemaType) Contains(etype string) bool { + + return slices.Contains(t.types, etype) +} + +func (t *jsonSchemaType) String() string { + + if len(t.types) == 0 { + return StringUndefined // should never happen + } + + // Displayed as a list [type1,type2,...] + if len(t.types) > 1 { + return fmt.Sprintf("[%s]", strings.Join(t.types, ",")) + } + + // Only one type: name only + return t.types[0] +} diff --git a/third_party/opa/internal/gojsonschema/schema_test.go b/third_party/opa/internal/gojsonschema/schema_test.go new file mode 100644 index 000000000000..d7646d42a4d0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/schema_test.go @@ -0,0 +1,413 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description (Unit) Tests for schema validation. +// +// created 16-06-2013 + +// nolint: deadcode // Package in development (2021). +package gojsonschema + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "testing" +) + +const circularReference = `{ + "type": "object", + "properties": { + "games": { + "type": "array", + "items": { + "$ref": "#/definitions/game" + } + } + }, + "definitions": { + "game": { + "type": "object", + "properties": { + "winner": { + "$ref": "#/definitions/player" + }, + "loser": { + "$ref": "#/definitions/player" + } + } + }, + "player": { + "type": "object", + "properties": { + "user": { + "$ref": "#/definitions/user" + }, + "game": { + "$ref": "#/definitions/game" + } + } + }, + "user": { + "type": "object", + "properties": { + "fullName": { + "type": "string" + } + } + } + } +}` + +func TestCircularReference(t *testing.T) { + loader := NewStringLoader(circularReference) + // call the target function + _, err := NewSchema(loader) + if err != nil { + t.Errorf("Got error: %s", err.Error()) + } +} + +// From http://json-schema.org/examples.html +const simpleSchema = `{ + "title": "Example Schema", + "type": "object", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "age": { + "description": "Age in years", + "type": "integer", + "minimum": 0 + } + }, + "required": ["firstName", "lastName"] +}` + +func TestLoaders(t *testing.T) { + // setup reader loader + reader := bytes.NewBufferString(simpleSchema) + readerLoader, wrappedReader := NewReaderLoader(reader) + + // drain reader + by, err := io.ReadAll(wrappedReader) + if err != nil { + t.Error(err) + } + + if simpleSchema != string(by) { + t.Errorf("Expected %s, got %s", simpleSchema, string(by)) + } + + // setup writer loaders + writer := &bytes.Buffer{} + writerLoader, wrappedWriter := NewWriterLoader(writer) + + // fill writer + n, err := io.WriteString(wrappedWriter, simpleSchema) + if err != nil { + t.Error(err) + } + if exp, got := n, len(simpleSchema); exp != got { + t.Errorf("Expected %d, got %d", exp, got) + } + + loaders := []JSONLoader{ + NewStringLoader(simpleSchema), + readerLoader, + writerLoader, + } + + for _, l := range loaders { + _, err := NewSchema(l) + if err != nil { + t.Error(err) + } + } +} + +const invalidPattern = `{ + "title": "Example Pattern", + "type": "object", + "properties": { + "invalid": { + "type": "string", + "pattern": 99999 + } + } +}` + +func TestLoadersWithInvalidPattern(t *testing.T) { + // setup reader loader + reader := bytes.NewBufferString(invalidPattern) + readerLoader, wrappedReader := NewReaderLoader(reader) + + // drain reader + by, err := io.ReadAll(wrappedReader) + if err != nil { + t.Error(err) + } + if invalidPattern != string(by) { + t.Errorf("Expected %s, got %s", invalidPattern, string(by)) + } + + // setup writer loaders + writer := &bytes.Buffer{} + writerLoader, wrappedWriter := NewWriterLoader(writer) + + // fill writer + n, err := io.WriteString(wrappedWriter, invalidPattern) + if err != nil { + t.Error(err) + } + if exp, got := n, len(invalidPattern); exp != got { + t.Errorf("Expected %d, got %d", exp, got) + } + + loaders := []JSONLoader{ + NewStringLoader(invalidPattern), + readerLoader, + writerLoader, + } + + for _, l := range loaders { + _, err := NewSchema(l) + if err == nil { + t.Errorf("Expected error loading invalid pattern: %T", l) + } + } +} + +const refPropertySchema = `{ + "$id" : "http://localhost/schema.json", + "properties" : { + "$id" : { + "$id": "http://localhost/foo.json" + }, + "$ref" : { + "const": { + "$ref" : "hello.world" + } + }, + "const" : { + "$ref" : "#/definitions/$ref" + } + }, + "definitions" : { + "$ref" : { + "const": { + "$ref" : "hello.world" + } + } + }, + "dependencies" : { + "$ref" : [ "const" ], + "const" : [ "$ref" ] + } +}` + +func TestRefProperty(t *testing.T) { + schemaLoader := NewStringLoader(refPropertySchema) + documentLoader := NewStringLoader(`{ + "$ref" : { "$ref" : "hello.world" }, + "const" : { "$ref" : "hello.world" } + }`) + // call the target function + s, err := NewSchema(schemaLoader) + if err != nil { + t.Fatalf("Got error: %s", err.Error()) + } + result, err := s.Validate(documentLoader) + if err != nil { + t.Fatalf("Got error: %s", err.Error()) + } + if !result.Valid() { + for _, err := range result.Errors() { + fmt.Println(err.String()) + } + t.Errorf("Got invalid validation result.") + } +} + +func TestFragmentLoader(t *testing.T) { + wd, err := os.Getwd() + + if err != nil { + panic(err.Error()) + } + + fileName := filepath.Join(wd, "testdata", "extra", "fragment_schema.json") + + schemaLoader := NewReferenceLoader("file://" + filepath.ToSlash(fileName) + "#/definitions/x") + schema, err := NewSchema(schemaLoader) + + if err != nil { + t.Fatalf("Encountered error while loading schema: %s", err.Error()) + } + + validDocument := NewStringLoader(`5`) + invalidDocument := NewStringLoader(`"a"`) + + result, err := schema.Validate(validDocument) + if err != nil { + t.Errorf("Unexpected error while validating document: %T", err) + } + if !result.Valid() { + t.Errorf("Got invalid validation result.") + } + + result, err = schema.Validate(invalidDocument) + if err != nil { + t.Errorf("Unexpected error while validating document: %T", err) + } + if len(result.Errors()) != 1 || result.Errors()[0].Type() != "invalid_type" { + t.Errorf("Got invalid validation result.") + } +} + +func TestFileWithSpace(t *testing.T) { + wd, err := os.Getwd() + + if err != nil { + panic(err.Error()) + } + + fileName := filepath.Join(wd, "testdata", "extra", "file with space.json") + loader := NewReferenceLoader("file://" + filepath.ToSlash(fileName)) + + data, err := loader.LoadJSON() + if err != nil { + t.Errorf("Unexpected error when trying to load a filepath containing a space: %s", err) + } + + jsonBytes, err := json.Marshal(data) + if err != nil { + t.Errorf("Unexpected error when trying to marshal json: %s", err) + } + + if exp, got := `{"foo":true}`, string(jsonBytes); exp != got { + t.Errorf("Contents of the file do not match, expected %s, got %s", exp, got) + } +} + +func TestAdditionalPropertiesErrorMessage(t *testing.T) { + schema := `{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "Device": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } +}` + text := `{ + "Device":{ + "Color" : true + } + }` + loader := NewBytesLoader([]byte(schema)) + result, err := Validate(loader, NewBytesLoader([]byte(text))) + if err != nil { + t.Fatal(err) + } + + if len(result.Errors()) != 1 { + t.Fatal("Expected 1 error but got", len(result.Errors())) + } + + expected := "Device.Color: Invalid type. Expected: string, given: boolean" + actual := result.Errors()[0].String() + if actual != expected { + t.Fatalf("Expected '%s' but got '%s'", expected, actual) + } +} + +// Inspired by http://json-schema.org/latest/json-schema-core.html#rfc.section.8.2.3 +const locationIndependentSchema = `{ + "definitions": { + "A": { + "$id": "#foo" + }, + "B": { + "$id": "http://example.com/other.json", + "definitions": { + "X": { + "$id": "#bar", + "allOf": [false] + }, + "Y": { + "$id": "t/inner.json" + } + } + }, + "C": { + "$id" : "#frag", + "$ref": "http://example.com/other.json#bar" + } + }, + "$ref": "#frag" +}` + +func TestLocationIndependentIdentifier(t *testing.T) { + schemaLoader := NewStringLoader(locationIndependentSchema) + documentLoader := NewStringLoader(`{}`) + + s, err := NewSchema(schemaLoader) + if err != nil { + t.Errorf("Got error: %s", err.Error()) + } + + result, err := s.Validate(documentLoader) + if err != nil { + t.Fatalf("Got error: %s", err.Error()) + } + + if len(result.Errors()) != 2 || result.Errors()[0].Type() != "false" || result.Errors()[1].Type() != "number_all_of" { + t.Errorf("Got invalid validation result.") + } +} + +const incorrectRefSchema = `{ + "$ref" : "#/fail" +}` + +func TestIncorrectRef(t *testing.T) { + + schemaLoader := NewStringLoader(incorrectRefSchema) + s, err := NewSchema(schemaLoader) + + if s != nil { + t.Errorf("Expected nil schema") + } + if err.Error() != "Object has no key 'fail'" { + t.Errorf("Expected error 'Object has no key 'fail'' but got '%s'", err.Error()) + } +} diff --git a/third_party/opa/internal/gojsonschema/subSchema.go b/third_party/opa/internal/gojsonschema/subSchema.go new file mode 100644 index 000000000000..b7ceb3136ed2 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/subSchema.go @@ -0,0 +1,151 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Defines the structure of a sub-SubSchema. +// A sub-SubSchema can contain other sub-schemas. +// +// created 27-02-2013 + +package gojsonschema + +import ( + "math/big" + "regexp" + + "github.com/xeipuuv/gojsonreference" +) + +// Constants +const ( + KeySchema = "$schema" + KeyID = "id" + KeyIDNew = "$id" + KeyRef = "$ref" + KeyTitle = "title" + KeyDescription = "description" + KeyType = "type" + KeyItems = "items" + KeyAdditionalItems = "additionalItems" + KeyProperties = "properties" + KeyPatternProperties = "patternProperties" + KeyAdditionalProperties = "additionalProperties" + KeyPropertyNames = "propertyNames" + KeyDefinitions = "definitions" + KeyMultipleOf = "multipleOf" + KeyMinimum = "minimum" + KeyMaximum = "maximum" + KeyExclusiveMinimum = "exclusiveMinimum" + KeyExclusiveMaximum = "exclusiveMaximum" + KeyMinLength = "minLength" + KeyMaxLength = "maxLength" + KeyPattern = "pattern" + KeyFormat = "format" + KeyMinProperties = "minProperties" + KeyMaxProperties = "maxProperties" + KeyDependencies = "dependencies" + KeyRequired = "required" + KeyMinItems = "minItems" + KeyMaxItems = "maxItems" + KeyUniqueItems = "uniqueItems" + KeyContains = "contains" + KeyConst = "const" + KeyEnum = "enum" + KeyOneOf = "oneOf" + KeyAnyOf = "anyOf" + KeyAllOf = "allOf" + KeyNot = "not" + KeyIf = "if" + KeyThen = "then" + KeyElse = "else" +) + +// SubSchema holds a sub schema +type SubSchema struct { + Draft *Draft + + // basic SubSchema meta properties + ID *gojsonreference.JsonReference + title *string + description *string + + Property string + + // Quick pass/fail for boolean schemas + pass *bool + + // Types associated with the SubSchema + Types jsonSchemaType + + // Reference url + Ref *gojsonreference.JsonReference + // Schema referenced + RefSchema *SubSchema + + // hierarchy + Parent *SubSchema + ItemsChildren []*SubSchema + ItemsChildrenIsSingleSchema bool + PropertiesChildren []*SubSchema + + // validation : number / integer + multipleOf *big.Rat + maximum *big.Rat + exclusiveMaximum *big.Rat + minimum *big.Rat + exclusiveMinimum *big.Rat + + // validation : string + minLength *int + maxLength *int + pattern *regexp.Regexp + format string + + // validation : object + minProperties *int + maxProperties *int + required []string + + dependencies map[string]any + additionalProperties any + patternProperties map[string]*SubSchema + propertyNames *SubSchema + + // validation : array + minItems *int + maxItems *int + uniqueItems bool + contains *SubSchema + + additionalItems any + + // validation : all + _const *string //const is a golang keyword + enum []string + + // validation : SubSchema + oneOf []*SubSchema + AnyOf []*SubSchema + AllOf []*SubSchema + not *SubSchema + _if *SubSchema // if/else are golang keywords + _then *SubSchema + _else *SubSchema +} diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/additionalItems.json b/third_party/opa/internal/gojsonschema/testdata/draft4/additionalItems.json new file mode 100644 index 000000000000..abecc578be3c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/additionalItems.json @@ -0,0 +1,87 @@ +[ + { + "description": "additionalItems as schema", + "schema": { + "items": [{}], + "additionalItems": {"type": "integer"} + }, + "tests": [ + { + "description": "additional items match schema", + "data": [ null, 2, 3, 4 ], + "valid": true + }, + { + "description": "additional items do not match schema", + "data": [ null, 2, 3, "foo" ], + "valid": false + } + ] + }, + { + "description": "items is schema, no additionalItems", + "schema": { + "items": {}, + "additionalItems": false + }, + "tests": [ + { + "description": "all items match schema", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + } + ] + }, + { + "description": "array of items with no additionalItems", + "schema": { + "items": [{}, {}, {}], + "additionalItems": false + }, + "tests": [ + { + "description": "fewer number of items present", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "equal number of items present", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "additional items are not permitted", + "data": [ 1, 2, 3, 4 ], + "valid": false + } + ] + }, + { + "description": "additionalItems as false without items", + "schema": {"additionalItems": false}, + "tests": [ + { + "description": + "items defaults to empty schema so everything is valid", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + } + ] + }, + { + "description": "additionalItems are allowed by default", + "schema": {"items": [{"type": "integer"}]}, + "tests": [ + { + "description": "only the first item is validated", + "data": [1, "foo", false], + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/additionalProperties.json b/third_party/opa/internal/gojsonschema/testdata/draft4/additionalProperties.json new file mode 100644 index 000000000000..90d760734e92 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/additionalProperties.json @@ -0,0 +1,98 @@ +[ + { + "description": + "additionalProperties being false does not allow other properties", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "patternProperties": { "^v": {} }, + "additionalProperties": false + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobarbaz", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + }, + { + "description": "patternProperties are not additional properties", + "data": {"foo":1, "vroom": 2}, + "valid": true + } + ] + }, + { + "description": + "additionalProperties allows a schema which should validate", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional valid property is valid", + "data": {"foo" : 1, "bar" : 2, "quux" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : 12}, + "valid": false + } + ] + }, + { + "description": + "additionalProperties can exist by itself", + "schema": { + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "an additional valid property is valid", + "data": {"foo" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1}, + "valid": false + } + ] + }, + { + "description": "additionalProperties are allowed by default", + "schema": {"properties": {"foo": {}, "bar": {}}}, + "tests": [ + { + "description": "additional properties are allowed", + "data": {"foo": 1, "bar": 2, "quux": true}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/allOf.json b/third_party/opa/internal/gojsonschema/testdata/draft4/allOf.json new file mode 100644 index 000000000000..bbb5f89e4bc5 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/allOf.json @@ -0,0 +1,112 @@ +[ + { + "description": "allOf", + "schema": { + "allOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "allOf", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "mismatch second", + "data": {"foo": "baz"}, + "valid": false + }, + { + "description": "mismatch first", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "wrong type", + "data": {"foo": "baz", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "allOf with base schema", + "schema": { + "properties": {"bar": {"type": "integer"}}, + "required": ["bar"], + "allOf" : [ + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + }, + { + "properties": { + "baz": {"type": "null"} + }, + "required": ["baz"] + } + ] + }, + "tests": [ + { + "description": "valid", + "data": {"foo": "quux", "bar": 2, "baz": null}, + "valid": true + }, + { + "description": "mismatch base schema", + "data": {"foo": "quux", "baz": null}, + "valid": false + }, + { + "description": "mismatch first allOf", + "data": {"bar": 2, "baz": null}, + "valid": false + }, + { + "description": "mismatch second allOf", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "mismatch both", + "data": {"bar": 2}, + "valid": false + } + ] + }, + { + "description": "allOf simple types", + "schema": { + "allOf": [ + {"maximum": 30}, + {"minimum": 20} + ] + }, + "tests": [ + { + "description": "valid", + "data": 25, + "valid": true + }, + { + "description": "mismatch one", + "data": 35, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/anyOf.json b/third_party/opa/internal/gojsonschema/testdata/draft4/anyOf.json new file mode 100644 index 000000000000..6c8b25183724 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/anyOf.json @@ -0,0 +1,109 @@ +[ + { + "description": "anyOf", + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first anyOf valid", + "data": 1, + "valid": true + }, + { + "description": "second anyOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both anyOf valid", + "data": 3, + "valid": true + }, + { + "description": "neither anyOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "anyOf with base schema", + "schema": { + "type": "string", + "anyOf" : [ + { + "maxLength": 2 + }, + { + "minLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one anyOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both anyOf invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf complex types", + "schema": { + "anyOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first anyOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second anyOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both anyOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "neither anyOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/default.json b/third_party/opa/internal/gojsonschema/testdata/draft4/default.json new file mode 100644 index 000000000000..17629779fbea --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/default.json @@ -0,0 +1,49 @@ +[ + { + "description": "invalid type for default", + "schema": { + "properties": { + "foo": { + "type": "integer", + "default": [] + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"foo": 13}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + }, + { + "description": "invalid string value for default", + "schema": { + "properties": { + "bar": { + "type": "string", + "minLength": 4, + "default": "bad" + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"bar": "good"}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/definitions.json b/third_party/opa/internal/gojsonschema/testdata/draft4/definitions.json new file mode 100644 index 000000000000..cf935a321532 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/definitions.json @@ -0,0 +1,32 @@ +[ + { + "description": "valid definition", + "schema": {"$ref": "http://json-schema.org/draft-04/schema#"}, + "tests": [ + { + "description": "valid definition schema", + "data": { + "definitions": { + "foo": {"type": "integer"} + } + }, + "valid": true + } + ] + }, + { + "description": "invalid definition", + "schema": {"$ref": "http://json-schema.org/draft-04/schema#"}, + "tests": [ + { + "description": "invalid definition schema", + "data": { + "definitions": { + "foo": {"type": 1} + } + }, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/dependencies.json b/third_party/opa/internal/gojsonschema/testdata/draft4/dependencies.json new file mode 100644 index 000000000000..38effa1a15f8 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/dependencies.json @@ -0,0 +1,123 @@ +[ + { + "description": "dependencies", + "schema": { + "dependencies": {"bar": ["foo"]} + }, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependant", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "with dependency", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["bar"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "multiple dependencies", + "schema": { + "dependencies": {"quux": ["foo", "bar"]} + }, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependants", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "with dependencies", + "data": {"foo": 1, "bar": 2, "quux": 3}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"foo": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing other dependency", + "data": {"bar": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing both dependencies", + "data": {"quux": 1}, + "valid": false + } + ] + }, + { + "description": "multiple dependencies subschema", + "schema": { + "dependencies": { + "bar": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "integer"} + } + } + } + }, + "tests": [ + { + "description": "valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "no dependency", + "data": {"foo": "quux"}, + "valid": true + }, + { + "description": "wrong type", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "wrong type other", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + }, + { + "description": "wrong type both", + "data": {"foo": "quux", "bar": "quux"}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/enum.json b/third_party/opa/internal/gojsonschema/testdata/draft4/enum.json new file mode 100644 index 000000000000..f124436a7d90 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/enum.json @@ -0,0 +1,72 @@ +[ + { + "description": "simple enum validation", + "schema": {"enum": [1, 2, 3]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": 1, + "valid": true + }, + { + "description": "something else is invalid", + "data": 4, + "valid": false + } + ] + }, + { + "description": "heterogeneous enum validation", + "schema": {"enum": [6, "foo", [], true, {"foo": 12}]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": [], + "valid": true + }, + { + "description": "something else is invalid", + "data": null, + "valid": false + }, + { + "description": "objects are deep compared", + "data": {"foo": false}, + "valid": false + } + ] + }, + { + "description": "enums in properties", + "schema": { + "type":"object", + "properties": { + "foo": {"enum":["foo"]}, + "bar": {"enum":["bar"]} + }, + "required": ["bar"] + }, + "tests": [ + { + "description": "both properties are valid", + "data": {"foo":"foo", "bar":"bar"}, + "valid": true + }, + { + "description": "missing optional property is valid", + "data": {"bar":"bar"}, + "valid": true + }, + { + "description": "missing required property is invalid", + "data": {"foo":"foo"}, + "valid": false + }, + { + "description": "missing all properties is invalid", + "data": {}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/format.json b/third_party/opa/internal/gojsonschema/testdata/draft4/format.json new file mode 100644 index 000000000000..54d8dad6b6e9 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/format.json @@ -0,0 +1,218 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] \ No newline at end of file diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/items.json b/third_party/opa/internal/gojsonschema/testdata/draft4/items.json new file mode 100644 index 000000000000..6a4e648f7381 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/items.json @@ -0,0 +1,78 @@ +[ + { + "description": "a schema given for items", + "schema": { + "items": {"type": "integer"} + }, + "tests": [ + { + "description": "valid items", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "wrong type of items", + "data": [1, "x"], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "length": 1 + }, + "valid": true + } + ] + }, + { + "description": "an array of schemas for items", + "schema": { + "items": [ + {"type": "integer"}, + {"type": "string"} + ] + }, + "tests": [ + { + "description": "correct types", + "data": [ 1, "foo" ], + "valid": true + }, + { + "description": "wrong types", + "data": [ "foo", 1 ], + "valid": false + }, + { + "description": "incomplete array of items", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with additional items", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "1": "valid", + "length": 2 + }, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/maxItems.json b/third_party/opa/internal/gojsonschema/testdata/draft4/maxItems.json new file mode 100644 index 000000000000..3b53a6b371a7 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/maxItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "maxItems validation", + "schema": {"maxItems": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": [1], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "too long is invalid", + "data": [1, 2, 3], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "foobar", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/maxLength.json b/third_party/opa/internal/gojsonschema/testdata/draft4/maxLength.json new file mode 100644 index 000000000000..811d35b253c0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/maxLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "maxLength validation", + "schema": {"maxLength": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": "f", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too long is invalid", + "data": "foo", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + }, + { + "description": "two supplementary Unicode code points is long enough", + "data": "\uD83D\uDCA9\uD83D\uDCA9", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/maxProperties.json b/third_party/opa/internal/gojsonschema/testdata/draft4/maxProperties.json new file mode 100644 index 000000000000..513731e4c883 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/maxProperties.json @@ -0,0 +1,38 @@ +[ + { + "description": "maxProperties validation", + "schema": {"maxProperties": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "too long is invalid", + "data": {"foo": 1, "bar": 2, "baz": 3}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/maximum.json b/third_party/opa/internal/gojsonschema/testdata/draft4/maximum.json new file mode 100644 index 000000000000..82718fb2d5a3 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/maximum.json @@ -0,0 +1,47 @@ +[ + { + "description": "maximum validation", + "schema": {"maximum": 3.0}, + "tests": [ + { + "description": "below the maximum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 3.0, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "exclusiveMaximum validation", + "schema": { + "maximum": 3.0, + "exclusiveMaximum": true + }, + "tests": [ + { + "description": "below the maximum is still valid", + "data": 2.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 3.0, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/minItems.json b/third_party/opa/internal/gojsonschema/testdata/draft4/minItems.json new file mode 100644 index 000000000000..ed5118815ee9 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/minItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "minItems validation", + "schema": {"minItems": 1}, + "tests": [ + { + "description": "longer is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1], + "valid": true + }, + { + "description": "too short is invalid", + "data": [], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/minLength.json b/third_party/opa/internal/gojsonschema/testdata/draft4/minLength.json new file mode 100644 index 000000000000..3f09158deef0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/minLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "minLength validation", + "schema": {"minLength": 2}, + "tests": [ + { + "description": "longer is valid", + "data": "foo", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too short is invalid", + "data": "f", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 1, + "valid": true + }, + { + "description": "one supplementary Unicode code point is not long enough", + "data": "\uD83D\uDCA9", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/minProperties.json b/third_party/opa/internal/gojsonschema/testdata/draft4/minProperties.json new file mode 100644 index 000000000000..49a0726e01ce --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/minProperties.json @@ -0,0 +1,38 @@ +[ + { + "description": "minProperties validation", + "schema": {"minProperties": 1}, + "tests": [ + { + "description": "longer is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "too short is invalid", + "data": {}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/minimum.json b/third_party/opa/internal/gojsonschema/testdata/draft4/minimum.json new file mode 100644 index 000000000000..9af8ed40b028 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/minimum.json @@ -0,0 +1,47 @@ +[ + { + "description": "minimum validation", + "schema": {"minimum": 1.1}, + "tests": [ + { + "description": "above the minimum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 1.1, + "valid": true + }, + { + "description": "below the minimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "exclusiveMinimum validation", + "schema": { + "minimum": 1.1, + "exclusiveMinimum": true + }, + "tests": [ + { + "description": "above the minimum is still valid", + "data": 1.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 1.1, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/multipleOf.json b/third_party/opa/internal/gojsonschema/testdata/draft4/multipleOf.json new file mode 100644 index 000000000000..ca3b7618053f --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/multipleOf.json @@ -0,0 +1,60 @@ +[ + { + "description": "by int", + "schema": {"multipleOf": 2}, + "tests": [ + { + "description": "int by int", + "data": 10, + "valid": true + }, + { + "description": "int by int fail", + "data": 7, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "by number", + "schema": {"multipleOf": 1.5}, + "tests": [ + { + "description": "zero is multiple of anything", + "data": 0, + "valid": true + }, + { + "description": "4.5 is multiple of 1.5", + "data": 4.5, + "valid": true + }, + { + "description": "35 is not multiple of 1.5", + "data": 35, + "valid": false + } + ] + }, + { + "description": "by small number", + "schema": {"multipleOf": 0.0001}, + "tests": [ + { + "description": "0.0075 is multiple of 0.0001", + "data": 0.0075, + "valid": true + }, + { + "description": "0.00751 is not multiple of 0.0001", + "data": 0.00751, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/not.json b/third_party/opa/internal/gojsonschema/testdata/draft4/not.json new file mode 100644 index 000000000000..cbb7f46bf8bc --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/not.json @@ -0,0 +1,96 @@ +[ + { + "description": "not", + "schema": { + "not": {"type": "integer"} + }, + "tests": [ + { + "description": "allowed", + "data": "foo", + "valid": true + }, + { + "description": "disallowed", + "data": 1, + "valid": false + } + ] + }, + { + "description": "not multiple types", + "schema": { + "not": {"type": ["integer", "boolean"]} + }, + "tests": [ + { + "description": "valid", + "data": "foo", + "valid": true + }, + { + "description": "mismatch", + "data": 1, + "valid": false + }, + { + "description": "other mismatch", + "data": true, + "valid": false + } + ] + }, + { + "description": "not more complex schema", + "schema": { + "not": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + }, + "tests": [ + { + "description": "match", + "data": 1, + "valid": true + }, + { + "description": "other match", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "mismatch", + "data": {"foo": "bar"}, + "valid": false + } + ] + }, + { + "description": "forbidden property", + "schema": { + "properties": { + "foo": { + "not": {} + } + } + }, + "tests": [ + { + "description": "property present", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "property absent", + "data": {"bar": 1, "baz": 2}, + "valid": true + } + ] + } + +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/oneOf.json b/third_party/opa/internal/gojsonschema/testdata/draft4/oneOf.json new file mode 100644 index 000000000000..3a03ded926c9 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/oneOf.json @@ -0,0 +1,109 @@ +[ + { + "description": "oneOf", + "schema": { + "oneOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first oneOf valid", + "data": 1, + "valid": true + }, + { + "description": "second oneOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both oneOf valid", + "data": 3, + "valid": false + }, + { + "description": "neither oneOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "oneOf with base schema", + "schema": { + "type": "string", + "oneOf" : [ + { + "minLength": 2 + }, + { + "maxLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one oneOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both oneOf valid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf complex types", + "schema": { + "oneOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second oneOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both oneOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": false + }, + { + "description": "neither oneOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/optional/bignum.json b/third_party/opa/internal/gojsonschema/testdata/draft4/optional/bignum.json new file mode 100644 index 000000000000..ccc7c17fe8d5 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/optional/bignum.json @@ -0,0 +1,107 @@ +[ + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a bignum is an integer", + "data": 12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a bignum is a number", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a negative bignum is an integer", + "data": -12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a negative bignum is a number", + "data": -98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "string", + "schema": {"type": "string"}, + "tests": [ + { + "description": "a bignum is not a string", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"maximum": 18446744073709551615}, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision", + "schema": { + "maximum": 972783798187987123879878123.18878137, + "exclusiveMaximum": true + }, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 972783798187987123879878123.188781371, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"minimum": -18446744073709551615}, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision on negative numbers", + "schema": { + "minimum": -972783798187987123879878123.18878137, + "exclusiveMinimum": true + }, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -972783798187987123879878123.188781371, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/optional/ecmascript-regex.json b/third_party/opa/internal/gojsonschema/testdata/draft4/optional/ecmascript-regex.json new file mode 100644 index 000000000000..08dc9360b870 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/optional/ecmascript-regex.json @@ -0,0 +1,13 @@ +[ + { + "description": "ECMA 262 regex non-compliance", + "schema": { "format": "regex" }, + "tests": [ + { + "description": "ECMA 262 has no support for \\Z anchor from .NET", + "data": "^\\S(|(.|\\n)*\\S)\\Z", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/optional/format.json b/third_party/opa/internal/gojsonschema/testdata/draft4/optional/format.json new file mode 100644 index 000000000000..32db8def7559 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/optional/format.json @@ -0,0 +1,223 @@ +[ + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "a valid date-time string", + "data": "1963-06-19T08:30:06.283185Z", + "valid": true + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963 08:30:06 PST", + "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350T01:01:01", + "valid": false + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "a valid URL with anchor tag", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid URL with anchor tag and parantheses", + "data": "http://foo.com/blah_(wikipedia)_blah#cite-1", + "valid": true + }, + { + "description": "a valid URL with URL-encoded stuff", + "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff", + "valid": true + }, + { + "description": "a valid puny-coded URL ", + "data": "http://xn--nw2a.xn--j6w193g/", + "valid": true + }, + { + "description": "a valid URL with many special characters", + "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "valid": true + }, + { + "description": "a valid URL based on IPv4", + "data": "http://223.255.255.254", + "valid": true + }, + { + "description": "a valid URL with ftp scheme", + "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt", + "valid": true + }, + { + "description": "a valid URL for a simple text file", + "data": "http://www.ietf.org/rfc/rfc2396.txt", + "valid": true + }, + { + "description": "a valid URL ", + "data": "ldap://[2001:db8::7]/c=GB?objectClass?one", + "valid": true + }, + { + "description": "a valid mailto URI", + "data": "mailto:John.Doe@example.com", + "valid": true + }, + { + "description": "a valid newsgroup URI", + "data": "news:comp.infosystems.www.servers.unix", + "valid": true + }, + { + "description": "a valid tel URI", + "data": "tel:+1-816-555-1212", + "valid": true + }, + { + "description": "a valid URN", + "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", + "valid": true + }, + { + "description": "an invalid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": false + }, + { + "description": "an invalid relative URI Reference", + "data": "/abc", + "valid": false + }, + { + "description": "an invalid URI", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "an invalid URI though valid URI reference", + "data": "abc", + "valid": false + }, + { + "description": "an invalid URI with spaces", + "data": "http:// shouldfail.com", + "valid": false + }, + { + "description": "an invalid URI with spaces and missing scheme", + "data": ":// should fail", + "valid": false + } + ] + }, + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "a valid e-mail address", + "data": "joe.bloggs@example.com", + "valid": true + }, + { + "description": "an invalid e-mail address", + "data": "2962", + "valid": false + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "a valid IP address", + "data": "192.168.0.1", + "valid": true + }, + { + "description": "an IP address with too many components", + "data": "127.0.0.0.1", + "valid": false + }, + { + "description": "an IP address with out-of-range values", + "data": "256.256.256.256", + "valid": false + }, + { + "description": "an IP address without 4 components", + "data": "127.0", + "valid": false + }, + { + "description": "an IP address as an integer", + "data": "0x7f000001", + "valid": false + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "a valid IPv6 address", + "data": "::1", + "valid": true + }, + { + "description": "an IPv6 address with out-of-range values", + "data": "12345::", + "valid": false + }, + { + "description": "an IPv6 address with too many components", + "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", + "valid": false + }, + { + "description": "an IPv6 address containing illegal characters", + "data": "::laptop", + "valid": false + } + ] + }, + { + "description": "validation of host names", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "a valid host name", + "data": "www.example.com", + "valid": true + }, + { + "description": "a host name starting with an illegal character", + "data": "-a-host-name-that-starts-with--", + "valid": false + }, + { + "description": "a host name containing illegal characters", + "data": "not_a_valid_host_name", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/optional/zeroTerminatedFloats.json b/third_party/opa/internal/gojsonschema/testdata/draft4/optional/zeroTerminatedFloats.json new file mode 100644 index 000000000000..9b50ea277694 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/optional/zeroTerminatedFloats.json @@ -0,0 +1,15 @@ +[ + { + "description": "some languages do not distinguish between different types of numeric value", + "schema": { + "type": "integer" + }, + "tests": [ + { + "description": "a float is not an integer even without fractional part", + "data": 1.0, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/pattern.json b/third_party/opa/internal/gojsonschema/testdata/draft4/pattern.json new file mode 100644 index 000000000000..fa8bdb945ef1 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/pattern.json @@ -0,0 +1,34 @@ +[ + { + "description": "pattern validation", + "schema": {"pattern": "^a*$"}, + "tests": [ + { + "description": "a matching pattern is valid", + "data": "aaa", + "valid": true + }, + { + "description": "a non-matching pattern is invalid (but ignored)", + "data": "abc", + "valid": true + }, + { + "description": "ignores non-strings", + "data": true, + "valid": true + } + ] + }, + { + "description": "pattern is not anchored", + "schema": {"pattern": "a+"}, + "tests": [ + { + "description": "matches a substring", + "data": "xxaayy", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/patternProperties.json b/third_party/opa/internal/gojsonschema/testdata/draft4/patternProperties.json new file mode 100644 index 000000000000..5f741dfca609 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/patternProperties.json @@ -0,0 +1,120 @@ +[ + { + "description": + "patternProperties validates properties matching a regex", + "schema": { + "patternProperties": { + "f.*o": {"type": "integer"} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "multiple valid matches is valid", + "data": {"foo": 1, "foooooo" : 2}, + "valid": true + }, + { + "description": "a single invalid match is invalid", + "data": {"foo": "bar", "fooooo": 2}, + "valid": false + }, + { + "description": "multiple invalid matches is invalid", + "data": {"foo": "bar", "foooooo" : "baz"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "multiple simultaneous patternProperties are validated", + "schema": { + "patternProperties": { + "a*": {"type": "integer"}, + "aaa*": {"maximum": 20} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"a": 21}, + "valid": true + }, + { + "description": "a simultaneous match is valid", + "data": {"aaaa": 18}, + "valid": true + }, + { + "description": "multiple matches is valid", + "data": {"a": 21, "aaaa": 18}, + "valid": true + }, + { + "description": "an invalid due to one is invalid", + "data": {"a": "bar"}, + "valid": false + }, + { + "description": "an invalid due to the other is invalid", + "data": {"aaaa": 31}, + "valid": false + }, + { + "description": "an invalid due to both is invalid", + "data": {"aaa": "foo", "aaaa": 31}, + "valid": false + } + ] + }, + { + "description": "regexes are not anchored by default and are case sensitive", + "schema": { + "patternProperties": { + "[0-9]{2,}": { "type": "boolean" }, + "X_": { "type": "string" } + } + }, + "tests": [ + { + "description": "non recognized members are ignored", + "data": { "answer 1": "42" }, + "valid": true + }, + { + "description": "recognized members are accounted for", + "data": { "a31b": null }, + "valid": false + }, + { + "description": "regexes are case sensitive", + "data": { "a_x_3": 3 }, + "valid": true + }, + { + "description": "regexes are case sensitive, 2", + "data": { "a_X_3": 3 }, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/properties.json b/third_party/opa/internal/gojsonschema/testdata/draft4/properties.json new file mode 100644 index 000000000000..a830c67e7b36 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/properties.json @@ -0,0 +1,97 @@ +[ + { + "description": "object properties validation", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "string"} + } + }, + "tests": [ + { + "description": "both properties present and valid is valid", + "data": {"foo": 1, "bar": "baz"}, + "valid": true + }, + { + "description": "one property invalid is invalid", + "data": {"foo": 1, "bar": {}}, + "valid": false + }, + { + "description": "both properties invalid is invalid", + "data": {"foo": [], "bar": {}}, + "valid": false + }, + { + "description": "doesn't invalidate other properties", + "data": {"quux": []}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": + "properties, patternProperties, additionalProperties interaction", + "schema": { + "properties": { + "foo": {"type": "array", "maxItems": 3}, + "bar": {"type": "array"} + }, + "patternProperties": {"f.o": {"minItems": 2}}, + "additionalProperties": {"type": "integer"} + }, + "tests": [ + { + "description": "property validates property", + "data": {"foo": [1, 2]}, + "valid": true + }, + { + "description": "property invalidates property", + "data": {"foo": [1, 2, 3, 4]}, + "valid": false + }, + { + "description": "patternProperty invalidates property", + "data": {"foo": []}, + "valid": false + }, + { + "description": "patternProperty validates nonproperty", + "data": {"fxo": [1, 2]}, + "valid": true + }, + { + "description": "patternProperty invalidates nonproperty", + "data": {"fxo": []}, + "valid": false + }, + { + "description": "additionalProperty ignores property", + "data": {"bar": []}, + "valid": true + }, + { + "description": "additionalProperty validates others", + "data": {"quux": 3}, + "valid": true + }, + { + "description": "additionalProperty invalidates others", + "data": {"quux": "foo"}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/ref.json b/third_party/opa/internal/gojsonschema/testdata/draft4/ref.json new file mode 100644 index 000000000000..52cf50a982f4 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/ref.json @@ -0,0 +1,300 @@ +[ + { + "description": "root pointer ref", + "schema": { + "properties": { + "foo": {"$ref": "#"} + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "match", + "data": {"foo": false}, + "valid": true + }, + { + "description": "recursive match", + "data": {"foo": {"foo": false}}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": false}, + "valid": false + }, + { + "description": "recursive mismatch", + "data": {"foo": {"bar": false}}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to object", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"$ref": "#/properties/foo"} + } + }, + "tests": [ + { + "description": "match", + "data": {"bar": 3}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": true}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to array", + "schema": { + "items": [ + {"type": "integer"}, + {"$ref": "#/items/0"} + ] + }, + "tests": [ + { + "description": "match array", + "data": [1, 2], + "valid": true + }, + { + "description": "mismatch array", + "data": [1, "foo"], + "valid": false + } + ] + }, + { + "description": "escaped pointer ref", + "schema": { + "tilda~field": {"type": "integer"}, + "slash/field": {"type": "integer"}, + "percent%field": {"type": "integer"}, + "properties": { + "tilda": {"$ref": "#/tilda~0field"}, + "slash": {"$ref": "#/slash~1field"}, + "percent": {"$ref": "#/percent%25field"} + } + }, + "tests": [ + { + "description": "slash invalid", + "data": {"slash": "aoeu"}, + "valid": false + }, + { + "description": "tilda invalid", + "data": {"tilda": "aoeu"}, + "valid": false + }, + { + "description": "percent invalid", + "data": {"percent": "aoeu"}, + "valid": false + }, + { + "description": "slash valid", + "data": {"slash": 123}, + "valid": true + }, + { + "description": "tilda valid", + "data": {"tilda": 123}, + "valid": true + }, + { + "description": "percent valid", + "data": {"percent": 123}, + "valid": true + } + ] + }, + { + "description": "nested refs", + "schema": { + "definitions": { + "a": {"type": "integer"}, + "b": {"$ref": "#/definitions/a"}, + "c": {"$ref": "#/definitions/b"} + }, + "$ref": "#/definitions/c" + }, + "tests": [ + { + "description": "nested ref valid", + "data": 5, + "valid": true + }, + { + "description": "nested ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref overrides any sibling keywords", + "schema": { + "definitions": { + "reffed": { + "type": "array" + } + }, + "properties": { + "foo": { + "$ref": "#/definitions/reffed", + "maxItems": 2 + } + } + }, + "tests": [ + { + "description": "ref valid", + "data": { "foo": [] }, + "valid": true + }, + { + "description": "ref valid, maxItems ignored", + "data": { "foo": [ 1, 2, 3] }, + "valid": true + }, + { + "description": "ref invalid", + "data": { "foo": "string" }, + "valid": false + } + ] + }, + { + "description": "remote ref, containing refs itself", + "schema": {"$ref": "http://json-schema.org/draft-04/schema#"}, + "tests": [ + { + "description": "remote ref valid", + "data": {"minLength": 1}, + "valid": true + }, + { + "description": "remote ref invalid", + "data": {"minLength": -1}, + "valid": false + } + ] + }, + { + "description": "property named $ref that is not a reference", + "schema": { + "properties": { + "$ref": {"type": "string"} + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "Recursive references between schemas", + "schema": { + "id": "http://localhost:1234/tree", + "description": "tree of nodes", + "type": "object", + "properties": { + "meta": {"type": "string"}, + "nodes": { + "type": "array", + "items": {"$ref": "node"} + } + }, + "required": ["meta", "nodes"], + "definitions": { + "node": { + "id": "http://localhost:1234/node", + "description": "node", + "type": "object", + "properties": { + "value": {"type": "number"}, + "subtree": {"$ref": "tree"} + }, + "required": ["value"] + } + } + }, + "tests": [ + { + "description": "valid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 1.1}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": true + }, + { + "description": "invalid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": "string is invalid"}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/refRemote.json b/third_party/opa/internal/gojsonschema/testdata/draft4/refRemote.json new file mode 100644 index 000000000000..8611fadc01e5 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/refRemote.json @@ -0,0 +1,171 @@ +[ + { + "description": "remote ref", + "schema": {"$ref": "http://localhost:1234/integer.json"}, + "tests": [ + { + "description": "remote ref valid", + "data": 1, + "valid": true + }, + { + "description": "remote ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "fragment within remote ref", + "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"}, + "tests": [ + { + "description": "remote fragment valid", + "data": 1, + "valid": true + }, + { + "description": "remote fragment invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref within remote ref", + "schema": { + "$ref": "http://localhost:1234/subSchemas.json#/refToInteger" + }, + "tests": [ + { + "description": "ref within ref valid", + "data": 1, + "valid": true + }, + { + "description": "ref within ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "base URI change", + "schema": { + "id": "http://localhost:1234/", + "items": { + "id": "folder/", + "items": {"$ref": "folderInteger.json"} + } + }, + "tests": [ + { + "description": "base URI change ref valid", + "data": [[1]], + "valid": true + }, + { + "description": "base URI change ref invalid", + "data": [["a"]], + "valid": false + } + ] + }, + { + "description": "base URI change - change folder", + "schema": { + "id": "http://localhost:1234/scope_change_defs1.json", + "type" : "object", + "properties": { + "list": {"$ref": "#/definitions/baz"} + }, + "definitions": { + "baz": { + "id": "folder/", + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "base URI change - change folder in subschema", + "schema": { + "id": "http://localhost:1234/scope_change_defs2.json", + "type" : "object", + "properties": { + "list": {"$ref": "#/definitions/baz/definitions/bar"} + }, + "definitions": { + "baz": { + "id": "folder/", + "definitions": { + "bar": { + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "root ref in remote ref", + "schema": { + "id": "http://localhost:1234/object", + "type": "object", + "properties": { + "name": {"$ref": "name.json#/definitions/orNull"} + } + }, + "tests": [ + { + "description": "string is valid", + "data": { + "name": "foo" + }, + "valid": true + }, + { + "description": "null is valid", + "data": { + "name": null + }, + "valid": true + }, + { + "description": "object is invalid", + "data": { + "name": { + "name": null + } + }, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/required.json b/third_party/opa/internal/gojsonschema/testdata/draft4/required.json new file mode 100644 index 000000000000..1e2a4f0bddfe --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/required.json @@ -0,0 +1,54 @@ +[ + { + "description": "required validation", + "schema": { + "properties": { + "foo": {}, + "bar": {} + }, + "required": ["foo"] + }, + "tests": [ + { + "description": "present required property is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "non-present required property is invalid", + "data": {"bar": 1}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "required default validation", + "schema": { + "properties": { + "foo": {} + } + }, + "tests": [ + { + "description": "not required by default", + "data": {}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/type.json b/third_party/opa/internal/gojsonschema/testdata/draft4/type.json new file mode 100644 index 000000000000..61293740df70 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/type.json @@ -0,0 +1,345 @@ +[ + { + "description": "integer type matches integers", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "an integer is an integer", + "data": 1, + "valid": true + }, + { + "description": "a float is not an integer", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an integer", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not an integer, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not an integer", + "data": {}, + "valid": false + }, + { + "description": "an array is not an integer", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an integer", + "data": true, + "valid": false + }, + { + "description": "null is not an integer", + "data": null, + "valid": false + } + ] + }, + { + "description": "number type matches numbers", + "schema": {"type": "number"}, + "tests": [ + { + "description": "an integer is a number", + "data": 1, + "valid": true + }, + { + "description": "a float is a number", + "data": 1.1, + "valid": true + }, + { + "description": "a string is not a number", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not a number, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not a number", + "data": {}, + "valid": false + }, + { + "description": "an array is not a number", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a number", + "data": true, + "valid": false + }, + { + "description": "null is not a number", + "data": null, + "valid": false + } + ] + }, + { + "description": "string type matches strings", + "schema": {"type": "string"}, + "tests": [ + { + "description": "1 is not a string", + "data": 1, + "valid": false + }, + { + "description": "a float is not a string", + "data": 1.1, + "valid": false + }, + { + "description": "a string is a string", + "data": "foo", + "valid": true + }, + { + "description": "a string is still a string, even if it looks like a number", + "data": "1", + "valid": true + }, + { + "description": "an object is not a string", + "data": {}, + "valid": false + }, + { + "description": "an array is not a string", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a string", + "data": true, + "valid": false + }, + { + "description": "null is not a string", + "data": null, + "valid": false + } + ] + }, + { + "description": "object type matches objects", + "schema": {"type": "object"}, + "tests": [ + { + "description": "an integer is not an object", + "data": 1, + "valid": false + }, + { + "description": "a float is not an object", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an object", + "data": "foo", + "valid": false + }, + { + "description": "an object is an object", + "data": {}, + "valid": true + }, + { + "description": "an array is not an object", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an object", + "data": true, + "valid": false + }, + { + "description": "null is not an object", + "data": null, + "valid": false + } + ] + }, + { + "description": "array type matches arrays", + "schema": {"type": "array"}, + "tests": [ + { + "description": "an integer is not an array", + "data": 1, + "valid": false + }, + { + "description": "a float is not an array", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an array", + "data": "foo", + "valid": false + }, + { + "description": "an object is not an array", + "data": {}, + "valid": false + }, + { + "description": "an array is an array", + "data": [], + "valid": true + }, + { + "description": "a boolean is not an array", + "data": true, + "valid": false + }, + { + "description": "null is not an array", + "data": null, + "valid": false + } + ] + }, + { + "description": "boolean type matches booleans", + "schema": {"type": "boolean"}, + "tests": [ + { + "description": "an integer is not a boolean", + "data": 1, + "valid": false + }, + { + "description": "a float is not a boolean", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not a boolean", + "data": "foo", + "valid": false + }, + { + "description": "an object is not a boolean", + "data": {}, + "valid": false + }, + { + "description": "an array is not a boolean", + "data": [], + "valid": false + }, + { + "description": "a boolean is a boolean", + "data": true, + "valid": true + }, + { + "description": "null is not a boolean", + "data": null, + "valid": false + } + ] + }, + { + "description": "null type matches only the null object", + "schema": {"type": "null"}, + "tests": [ + { + "description": "an integer is not null", + "data": 1, + "valid": false + }, + { + "description": "a float is not null", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not null", + "data": "foo", + "valid": false + }, + { + "description": "an object is not null", + "data": {}, + "valid": false + }, + { + "description": "an array is not null", + "data": [], + "valid": false + }, + { + "description": "a boolean is not null", + "data": true, + "valid": false + }, + { + "description": "null is null", + "data": null, + "valid": true + } + ] + }, + { + "description": "multiple types can be specified in an array", + "schema": {"type": ["integer", "string"]}, + "tests": [ + { + "description": "an integer is valid", + "data": 1, + "valid": true + }, + { + "description": "a string is valid", + "data": "foo", + "valid": true + }, + { + "description": "a float is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "an object is invalid", + "data": {}, + "valid": false + }, + { + "description": "an array is invalid", + "data": [], + "valid": false + }, + { + "description": "a boolean is invalid", + "data": true, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft4/uniqueItems.json b/third_party/opa/internal/gojsonschema/testdata/draft4/uniqueItems.json new file mode 100644 index 000000000000..c1f4ab99c9a4 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft4/uniqueItems.json @@ -0,0 +1,79 @@ +[ + { + "description": "uniqueItems validation", + "schema": {"uniqueItems": true}, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is invalid", + "data": [1, 1], + "valid": false + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": false + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is invalid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": false + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is invalid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": false + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is invalid", + "data": [["foo"], ["foo"]], + "valid": false + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1], + "valid": true + }, + { + "description": "non-unique heterogeneous types are invalid", + "data": [{}, [1], true, null, {}, 1], + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/additionalItems.json b/third_party/opa/internal/gojsonschema/testdata/draft6/additionalItems.json new file mode 100644 index 000000000000..abecc578be3c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/additionalItems.json @@ -0,0 +1,87 @@ +[ + { + "description": "additionalItems as schema", + "schema": { + "items": [{}], + "additionalItems": {"type": "integer"} + }, + "tests": [ + { + "description": "additional items match schema", + "data": [ null, 2, 3, 4 ], + "valid": true + }, + { + "description": "additional items do not match schema", + "data": [ null, 2, 3, "foo" ], + "valid": false + } + ] + }, + { + "description": "items is schema, no additionalItems", + "schema": { + "items": {}, + "additionalItems": false + }, + "tests": [ + { + "description": "all items match schema", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + } + ] + }, + { + "description": "array of items with no additionalItems", + "schema": { + "items": [{}, {}, {}], + "additionalItems": false + }, + "tests": [ + { + "description": "fewer number of items present", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "equal number of items present", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "additional items are not permitted", + "data": [ 1, 2, 3, 4 ], + "valid": false + } + ] + }, + { + "description": "additionalItems as false without items", + "schema": {"additionalItems": false}, + "tests": [ + { + "description": + "items defaults to empty schema so everything is valid", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + } + ] + }, + { + "description": "additionalItems are allowed by default", + "schema": {"items": [{"type": "integer"}]}, + "tests": [ + { + "description": "only the first item is validated", + "data": [1, "foo", false], + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/additionalProperties.json b/third_party/opa/internal/gojsonschema/testdata/draft6/additionalProperties.json new file mode 100644 index 000000000000..90d760734e92 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/additionalProperties.json @@ -0,0 +1,98 @@ +[ + { + "description": + "additionalProperties being false does not allow other properties", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "patternProperties": { "^v": {} }, + "additionalProperties": false + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobarbaz", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + }, + { + "description": "patternProperties are not additional properties", + "data": {"foo":1, "vroom": 2}, + "valid": true + } + ] + }, + { + "description": + "additionalProperties allows a schema which should validate", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional valid property is valid", + "data": {"foo" : 1, "bar" : 2, "quux" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : 12}, + "valid": false + } + ] + }, + { + "description": + "additionalProperties can exist by itself", + "schema": { + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "an additional valid property is valid", + "data": {"foo" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1}, + "valid": false + } + ] + }, + { + "description": "additionalProperties are allowed by default", + "schema": {"properties": {"foo": {}, "bar": {}}}, + "tests": [ + { + "description": "additional properties are allowed", + "data": {"foo": 1, "bar": 2, "quux": true}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/allOf.json b/third_party/opa/internal/gojsonschema/testdata/draft6/allOf.json new file mode 100644 index 000000000000..00c016cd12a0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/allOf.json @@ -0,0 +1,145 @@ +[ + { + "description": "allOf", + "schema": { + "allOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "allOf", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "mismatch second", + "data": {"foo": "baz"}, + "valid": false + }, + { + "description": "mismatch first", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "wrong type", + "data": {"foo": "baz", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "allOf with base schema", + "schema": { + "properties": {"bar": {"type": "integer"}}, + "required": ["bar"], + "allOf" : [ + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + }, + { + "properties": { + "baz": {"type": "null"} + }, + "required": ["baz"] + } + ] + }, + "tests": [ + { + "description": "valid", + "data": {"foo": "quux", "bar": 2, "baz": null}, + "valid": true + }, + { + "description": "mismatch base schema", + "data": {"foo": "quux", "baz": null}, + "valid": false + }, + { + "description": "mismatch first allOf", + "data": {"bar": 2, "baz": null}, + "valid": false + }, + { + "description": "mismatch second allOf", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "mismatch both", + "data": {"bar": 2}, + "valid": false + } + ] + }, + { + "description": "allOf simple types", + "schema": { + "allOf": [ + {"maximum": 30}, + {"minimum": 20} + ] + }, + "tests": [ + { + "description": "valid", + "data": 25, + "valid": true + }, + { + "description": "mismatch one", + "data": 35, + "valid": false + } + ] + }, + { + "description": "allOf with boolean schemas, all true", + "schema": {"allOf": [true, true]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "allOf with boolean schemas, some false", + "schema": {"allOf": [true, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with boolean schemas, all false", + "schema": {"allOf": [false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/anyOf.json b/third_party/opa/internal/gojsonschema/testdata/draft6/anyOf.json new file mode 100644 index 000000000000..4d05a9e509ec --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/anyOf.json @@ -0,0 +1,142 @@ +[ + { + "description": "anyOf", + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first anyOf valid", + "data": 1, + "valid": true + }, + { + "description": "second anyOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both anyOf valid", + "data": 3, + "valid": true + }, + { + "description": "neither anyOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "anyOf with base schema", + "schema": { + "type": "string", + "anyOf" : [ + { + "maxLength": 2 + }, + { + "minLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one anyOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both anyOf invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf with boolean schemas, all true", + "schema": {"anyOf": [true, true]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "anyOf with boolean schemas, some true", + "schema": {"anyOf": [true, false]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "anyOf with boolean schemas, all false", + "schema": {"anyOf": [false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf complex types", + "schema": { + "anyOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first anyOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second anyOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both anyOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "neither anyOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/boolean_schema.json b/third_party/opa/internal/gojsonschema/testdata/draft6/boolean_schema.json new file mode 100644 index 000000000000..6d40f23f2622 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/boolean_schema.json @@ -0,0 +1,104 @@ +[ + { + "description": "boolean schema 'true'", + "schema": true, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "boolean true is valid", + "data": true, + "valid": true + }, + { + "description": "boolean false is valid", + "data": false, + "valid": true + }, + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": "bar"}, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + }, + { + "description": "array is valid", + "data": ["foo"], + "valid": true + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "boolean schema 'false'", + "schema": false, + "tests": [ + { + "description": "number is invalid", + "data": 1, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + }, + { + "description": "boolean true is invalid", + "data": true, + "valid": false + }, + { + "description": "boolean false is invalid", + "data": false, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + }, + { + "description": "object is invalid", + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "empty object is invalid", + "data": {}, + "valid": false + }, + { + "description": "array is invalid", + "data": ["foo"], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/const.json b/third_party/opa/internal/gojsonschema/testdata/draft6/const.json new file mode 100644 index 000000000000..0fe00f21f389 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/const.json @@ -0,0 +1,86 @@ +[ + { + "description": "const validation", + "schema": {"const": 2}, + "tests": [ + { + "description": "same value is valid", + "data": 2, + "valid": true + }, + { + "description": "another value is invalid", + "data": 5, + "valid": false + }, + { + "description": "another type is invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "const with object", + "schema": {"const": {"foo": "bar", "baz": "bax"}}, + "tests": [ + { + "description": "same object is valid", + "data": {"foo": "bar", "baz": "bax"}, + "valid": true + }, + { + "description": "same object with different property order is valid", + "data": {"baz": "bax", "foo": "bar"}, + "valid": true + }, + { + "description": "another object is invalid", + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "another type is invalid", + "data": [1, 2], + "valid": false + } + ] + }, + { + "description": "const with array", + "schema": {"const": [{ "foo": "bar" }]}, + "tests": [ + { + "description": "same array is valid", + "data": [{"foo": "bar"}], + "valid": true + }, + { + "description": "another array item is invalid", + "data": [2], + "valid": false + }, + { + "description": "array with additional items is invalid", + "data": [1, 2, 3], + "valid": false + } + ] + }, + { + "description": "const with null", + "schema": {"const": null}, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "not null is invalid", + "data": 0, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/contains.json b/third_party/opa/internal/gojsonschema/testdata/draft6/contains.json new file mode 100644 index 000000000000..b7ae5a25fea5 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/contains.json @@ -0,0 +1,95 @@ +[ + { + "description": "contains keyword validation", + "schema": { + "contains": {"minimum": 5} + }, + "tests": [ + { + "description": "array with item matching schema (5) is valid", + "data": [3, 4, 5], + "valid": true + }, + { + "description": "array with item matching schema (6) is valid", + "data": [3, 4, 6], + "valid": true + }, + { + "description": "array with two items matching schema (5, 6) is valid", + "data": [3, 4, 5, 6], + "valid": true + }, + { + "description": "array without items matching schema is invalid", + "data": [2, 3, 4], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "not array is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "contains keyword with const keyword", + "schema": { + "contains": { "const": 5 } + }, + "tests": [ + { + "description": "array with item 5 is valid", + "data": [3, 4, 5], + "valid": true + }, + { + "description": "array with two items 5 is valid", + "data": [3, 4, 5, 5], + "valid": true + }, + { + "description": "array without item 5 is invalid", + "data": [1, 2, 3, 4], + "valid": false + } + ] + }, + { + "description": "contains keyword with boolean schema true", + "schema": {"contains": true}, + "tests": [ + { + "description": "any non-empty array is valid", + "data": ["foo"], + "valid": true + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + }, + { + "description": "contains keyword with boolean schema false", + "schema": {"contains": false}, + "tests": [ + { + "description": "any non-empty array is invalid", + "data": ["foo"], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/default.json b/third_party/opa/internal/gojsonschema/testdata/draft6/default.json new file mode 100644 index 000000000000..17629779fbea --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/default.json @@ -0,0 +1,49 @@ +[ + { + "description": "invalid type for default", + "schema": { + "properties": { + "foo": { + "type": "integer", + "default": [] + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"foo": 13}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + }, + { + "description": "invalid string value for default", + "schema": { + "properties": { + "bar": { + "type": "string", + "minLength": 4, + "default": "bad" + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"bar": "good"}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/definitions.json b/third_party/opa/internal/gojsonschema/testdata/draft6/definitions.json new file mode 100644 index 000000000000..7f3b8997d5cd --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/definitions.json @@ -0,0 +1,32 @@ +[ + { + "description": "valid definition", + "schema": {"$ref": "http://json-schema.org/draft-06/schema#"}, + "tests": [ + { + "description": "valid definition schema", + "data": { + "definitions": { + "foo": {"type": "integer"} + } + }, + "valid": true + } + ] + }, + { + "description": "invalid definition", + "schema": {"$ref": "http://json-schema.org/draft-06/schema#"}, + "tests": [ + { + "description": "invalid definition schema", + "data": { + "definitions": { + "foo": {"type": 1} + } + }, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/dependencies.json b/third_party/opa/internal/gojsonschema/testdata/draft6/dependencies.json new file mode 100644 index 000000000000..80e552f73fc6 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/dependencies.json @@ -0,0 +1,172 @@ +[ + { + "description": "dependencies", + "schema": { + "dependencies": {"bar": ["foo"]} + }, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependant", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "with dependency", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["bar"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "dependencies with empty array", + "schema": { + "dependencies": {"bar": []} + }, + "tests": [ + { + "description": "empty object", + "data": {}, + "valid": true + }, + { + "description": "object with one property", + "data": {"bar": 2}, + "valid": true + } + ] + }, + { + "description": "multiple dependencies", + "schema": { + "dependencies": {"quux": ["foo", "bar"]} + }, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependants", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "with dependencies", + "data": {"foo": 1, "bar": 2, "quux": 3}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"foo": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing other dependency", + "data": {"bar": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing both dependencies", + "data": {"quux": 1}, + "valid": false + } + ] + }, + { + "description": "multiple dependencies subschema", + "schema": { + "dependencies": { + "bar": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "integer"} + } + } + } + }, + "tests": [ + { + "description": "valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "no dependency", + "data": {"foo": "quux"}, + "valid": true + }, + { + "description": "wrong type", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "wrong type other", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + }, + { + "description": "wrong type both", + "data": {"foo": "quux", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "dependencies with boolean subschemas", + "schema": { + "dependencies": { + "foo": true, + "bar": false + } + }, + "tests": [ + { + "description": "object with property having schema true is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property having schema false is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "object with both properties is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/enum.json b/third_party/opa/internal/gojsonschema/testdata/draft6/enum.json new file mode 100644 index 000000000000..f124436a7d90 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/enum.json @@ -0,0 +1,72 @@ +[ + { + "description": "simple enum validation", + "schema": {"enum": [1, 2, 3]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": 1, + "valid": true + }, + { + "description": "something else is invalid", + "data": 4, + "valid": false + } + ] + }, + { + "description": "heterogeneous enum validation", + "schema": {"enum": [6, "foo", [], true, {"foo": 12}]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": [], + "valid": true + }, + { + "description": "something else is invalid", + "data": null, + "valid": false + }, + { + "description": "objects are deep compared", + "data": {"foo": false}, + "valid": false + } + ] + }, + { + "description": "enums in properties", + "schema": { + "type":"object", + "properties": { + "foo": {"enum":["foo"]}, + "bar": {"enum":["bar"]} + }, + "required": ["bar"] + }, + "tests": [ + { + "description": "both properties are valid", + "data": {"foo":"foo", "bar":"bar"}, + "valid": true + }, + { + "description": "missing optional property is valid", + "data": {"bar":"bar"}, + "valid": true + }, + { + "description": "missing required property is invalid", + "data": {"foo":"foo"}, + "valid": false + }, + { + "description": "missing all properties is invalid", + "data": {}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/exclusiveMaximum.json b/third_party/opa/internal/gojsonschema/testdata/draft6/exclusiveMaximum.json new file mode 100644 index 000000000000..dc3cd709d31d --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/exclusiveMaximum.json @@ -0,0 +1,30 @@ +[ + { + "description": "exclusiveMaximum validation", + "schema": { + "exclusiveMaximum": 3.0 + }, + "tests": [ + { + "description": "below the exclusiveMaximum is valid", + "data": 2.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 3.0, + "valid": false + }, + { + "description": "above the exclusiveMaximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/exclusiveMinimum.json b/third_party/opa/internal/gojsonschema/testdata/draft6/exclusiveMinimum.json new file mode 100644 index 000000000000..b38d7ecec630 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/exclusiveMinimum.json @@ -0,0 +1,30 @@ +[ + { + "description": "exclusiveMinimum validation", + "schema": { + "exclusiveMinimum": 1.1 + }, + "tests": [ + { + "description": "above the exclusiveMinimum is valid", + "data": 1.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "below the exclusiveMinimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/format.json b/third_party/opa/internal/gojsonschema/testdata/draft6/format.json new file mode 100644 index 000000000000..87b75ebc63fe --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/format.json @@ -0,0 +1,326 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of JSON pointers", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI references", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI templates", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] \ No newline at end of file diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/items.json b/third_party/opa/internal/gojsonschema/testdata/draft6/items.json new file mode 100644 index 000000000000..13a6a113669f --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/items.json @@ -0,0 +1,133 @@ +[ + { + "description": "a schema given for items", + "schema": { + "items": {"type": "integer"} + }, + "tests": [ + { + "description": "valid items", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "wrong type of items", + "data": [1, "x"], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "length": 1 + }, + "valid": true + } + ] + }, + { + "description": "an array of schemas for items", + "schema": { + "items": [ + {"type": "integer"}, + {"type": "string"} + ] + }, + "tests": [ + { + "description": "correct types", + "data": [ 1, "foo" ], + "valid": true + }, + { + "description": "wrong types", + "data": [ "foo", 1 ], + "valid": false + }, + { + "description": "incomplete array of items", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with additional items", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "1": "valid", + "length": 2 + }, + "valid": true + } + ] + }, + { + "description": "items with boolean schema (true)", + "schema": {"items": true}, + "tests": [ + { + "description": "any array is valid", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items with boolean schema (false)", + "schema": {"items": false}, + "tests": [ + { + "description": "any non-empty array is invalid", + "data": [ 1, "foo", true ], + "valid": false + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items with boolean schemas", + "schema": { + "items": [true, false] + }, + "tests": [ + { + "description": "array with one item is valid", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with two items is invalid", + "data": [ 1, "foo" ], + "valid": false + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/maxItems.json b/third_party/opa/internal/gojsonschema/testdata/draft6/maxItems.json new file mode 100644 index 000000000000..3b53a6b371a7 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/maxItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "maxItems validation", + "schema": {"maxItems": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": [1], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "too long is invalid", + "data": [1, 2, 3], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "foobar", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/maxLength.json b/third_party/opa/internal/gojsonschema/testdata/draft6/maxLength.json new file mode 100644 index 000000000000..811d35b253c0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/maxLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "maxLength validation", + "schema": {"maxLength": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": "f", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too long is invalid", + "data": "foo", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + }, + { + "description": "two supplementary Unicode code points is long enough", + "data": "\uD83D\uDCA9\uD83D\uDCA9", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/maxProperties.json b/third_party/opa/internal/gojsonschema/testdata/draft6/maxProperties.json new file mode 100644 index 000000000000..513731e4c883 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/maxProperties.json @@ -0,0 +1,38 @@ +[ + { + "description": "maxProperties validation", + "schema": {"maxProperties": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "too long is invalid", + "data": {"foo": 1, "bar": 2, "baz": 3}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/maximum.json b/third_party/opa/internal/gojsonschema/testdata/draft6/maximum.json new file mode 100644 index 000000000000..8150984ee573 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/maximum.json @@ -0,0 +1,28 @@ +[ + { + "description": "maximum validation", + "schema": {"maximum": 3.0}, + "tests": [ + { + "description": "below the maximum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 3.0, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/minItems.json b/third_party/opa/internal/gojsonschema/testdata/draft6/minItems.json new file mode 100644 index 000000000000..ed5118815ee9 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/minItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "minItems validation", + "schema": {"minItems": 1}, + "tests": [ + { + "description": "longer is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1], + "valid": true + }, + { + "description": "too short is invalid", + "data": [], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/minLength.json b/third_party/opa/internal/gojsonschema/testdata/draft6/minLength.json new file mode 100644 index 000000000000..3f09158deef0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/minLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "minLength validation", + "schema": {"minLength": 2}, + "tests": [ + { + "description": "longer is valid", + "data": "foo", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too short is invalid", + "data": "f", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 1, + "valid": true + }, + { + "description": "one supplementary Unicode code point is not long enough", + "data": "\uD83D\uDCA9", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/minProperties.json b/third_party/opa/internal/gojsonschema/testdata/draft6/minProperties.json new file mode 100644 index 000000000000..49a0726e01ce --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/minProperties.json @@ -0,0 +1,38 @@ +[ + { + "description": "minProperties validation", + "schema": {"minProperties": 1}, + "tests": [ + { + "description": "longer is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "too short is invalid", + "data": {}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/minimum.json b/third_party/opa/internal/gojsonschema/testdata/draft6/minimum.json new file mode 100644 index 000000000000..bd1e95bc014d --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/minimum.json @@ -0,0 +1,28 @@ +[ + { + "description": "minimum validation", + "schema": {"minimum": 1.1}, + "tests": [ + { + "description": "above the minimum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 1.1, + "valid": true + }, + { + "description": "below the minimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/multipleOf.json b/third_party/opa/internal/gojsonschema/testdata/draft6/multipleOf.json new file mode 100644 index 000000000000..ca3b7618053f --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/multipleOf.json @@ -0,0 +1,60 @@ +[ + { + "description": "by int", + "schema": {"multipleOf": 2}, + "tests": [ + { + "description": "int by int", + "data": 10, + "valid": true + }, + { + "description": "int by int fail", + "data": 7, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "by number", + "schema": {"multipleOf": 1.5}, + "tests": [ + { + "description": "zero is multiple of anything", + "data": 0, + "valid": true + }, + { + "description": "4.5 is multiple of 1.5", + "data": 4.5, + "valid": true + }, + { + "description": "35 is not multiple of 1.5", + "data": 35, + "valid": false + } + ] + }, + { + "description": "by small number", + "schema": {"multipleOf": 0.0001}, + "tests": [ + { + "description": "0.0075 is multiple of 0.0001", + "data": 0.0075, + "valid": true + }, + { + "description": "0.00751 is not multiple of 0.0001", + "data": 0.00751, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/not.json b/third_party/opa/internal/gojsonschema/testdata/draft6/not.json new file mode 100644 index 000000000000..98de0eda8dd0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/not.json @@ -0,0 +1,117 @@ +[ + { + "description": "not", + "schema": { + "not": {"type": "integer"} + }, + "tests": [ + { + "description": "allowed", + "data": "foo", + "valid": true + }, + { + "description": "disallowed", + "data": 1, + "valid": false + } + ] + }, + { + "description": "not multiple types", + "schema": { + "not": {"type": ["integer", "boolean"]} + }, + "tests": [ + { + "description": "valid", + "data": "foo", + "valid": true + }, + { + "description": "mismatch", + "data": 1, + "valid": false + }, + { + "description": "other mismatch", + "data": true, + "valid": false + } + ] + }, + { + "description": "not more complex schema", + "schema": { + "not": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + }, + "tests": [ + { + "description": "match", + "data": 1, + "valid": true + }, + { + "description": "other match", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "mismatch", + "data": {"foo": "bar"}, + "valid": false + } + ] + }, + { + "description": "forbidden property", + "schema": { + "properties": { + "foo": { + "not": {} + } + } + }, + "tests": [ + { + "description": "property present", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "property absent", + "data": {"bar": 1, "baz": 2}, + "valid": true + } + ] + }, + { + "description": "not with boolean schema true", + "schema": {"not": true}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "not with boolean schema false", + "schema": {"not": false}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/oneOf.json b/third_party/opa/internal/gojsonschema/testdata/draft6/oneOf.json new file mode 100644 index 000000000000..bc4295cab4f6 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/oneOf.json @@ -0,0 +1,153 @@ +[ + { + "description": "oneOf", + "schema": { + "oneOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first oneOf valid", + "data": 1, + "valid": true + }, + { + "description": "second oneOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both oneOf valid", + "data": 3, + "valid": false + }, + { + "description": "neither oneOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "oneOf with base schema", + "schema": { + "type": "string", + "oneOf" : [ + { + "minLength": 2 + }, + { + "maxLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one oneOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both oneOf valid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, all true", + "schema": {"oneOf": [true, true, true]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, one true", + "schema": {"oneOf": [true, false, false]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "oneOf with boolean schemas, more than one true", + "schema": {"oneOf": [true, true, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, all false", + "schema": {"oneOf": [false, false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf complex types", + "schema": { + "oneOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second oneOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both oneOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": false + }, + { + "description": "neither oneOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/optional/bignum.json b/third_party/opa/internal/gojsonschema/testdata/draft6/optional/bignum.json new file mode 100644 index 000000000000..fac275e21fe2 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/optional/bignum.json @@ -0,0 +1,105 @@ +[ + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a bignum is an integer", + "data": 12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a bignum is a number", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a negative bignum is an integer", + "data": -12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a negative bignum is a number", + "data": -98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "string", + "schema": {"type": "string"}, + "tests": [ + { + "description": "a bignum is not a string", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"maximum": 18446744073709551615}, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision", + "schema": { + "exclusiveMaximum": 972783798187987123879878123.18878137 + }, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 972783798187987123879878123.188781371, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"minimum": -18446744073709551615}, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision on negative numbers", + "schema": { + "exclusiveMinimum": -972783798187987123879878123.18878137 + }, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -972783798187987123879878123.188781371, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/optional/ecmascript-regex.json b/third_party/opa/internal/gojsonschema/testdata/draft6/optional/ecmascript-regex.json new file mode 100644 index 000000000000..08dc9360b870 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/optional/ecmascript-regex.json @@ -0,0 +1,13 @@ +[ + { + "description": "ECMA 262 regex non-compliance", + "schema": { "format": "regex" }, + "tests": [ + { + "description": "ECMA 262 has no support for \\Z anchor from .NET", + "data": "^\\S(|(.|\\n)*\\S)\\Z", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/optional/format.json b/third_party/opa/internal/gojsonschema/testdata/draft6/optional/format.json new file mode 100644 index 000000000000..67f2fe630d42 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/optional/format.json @@ -0,0 +1,458 @@ +[ + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "a valid date-time string", + "data": "1963-06-19T08:30:06.283185Z", + "valid": true + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963 08:30:06 PST", + "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350T01:01:01", + "valid": false + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "a valid URL with anchor tag", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid URL with anchor tag and parantheses", + "data": "http://foo.com/blah_(wikipedia)_blah#cite-1", + "valid": true + }, + { + "description": "a valid URL with URL-encoded stuff", + "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff", + "valid": true + }, + { + "description": "a valid puny-coded URL ", + "data": "http://xn--nw2a.xn--j6w193g/", + "valid": true + }, + { + "description": "a valid URL with many special characters", + "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "valid": true + }, + { + "description": "a valid URL based on IPv4", + "data": "http://223.255.255.254", + "valid": true + }, + { + "description": "a valid URL with ftp scheme", + "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt", + "valid": true + }, + { + "description": "a valid URL for a simple text file", + "data": "http://www.ietf.org/rfc/rfc2396.txt", + "valid": true + }, + { + "description": "a valid URL ", + "data": "ldap://[2001:db8::7]/c=GB?objectClass?one", + "valid": true + }, + { + "description": "a valid mailto URI", + "data": "mailto:John.Doe@example.com", + "valid": true + }, + { + "description": "a valid newsgroup URI", + "data": "news:comp.infosystems.www.servers.unix", + "valid": true + }, + { + "description": "a valid tel URI", + "data": "tel:+1-816-555-1212", + "valid": true + }, + { + "description": "a valid URN", + "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", + "valid": true + }, + { + "description": "an invalid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": false + }, + { + "description": "an invalid relative URI Reference", + "data": "/abc", + "valid": false + }, + { + "description": "an invalid URI", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "an invalid URI though valid URI reference", + "data": "abc", + "valid": false + }, + { + "description": "an invalid URI with spaces", + "data": "http:// shouldfail.com", + "valid": false + }, + { + "description": "an invalid URI with spaces and missing scheme", + "data": ":// should fail", + "valid": false + } + ] + }, + { + "description": "validation of URI References", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "a valid URI", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid relative URI Reference", + "data": "/abc", + "valid": true + }, + { + "description": "an invalid URI Reference", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "a valid URI Reference", + "data": "abc", + "valid": true + }, + { + "description": "a valid URI fragment", + "data": "#fragment", + "valid": true + }, + { + "description": "an invalid URI fragment", + "data": "#frag\\ment", + "valid": false + } + ] + }, + { + "description": "format: uri-template", + "schema": { + "format": "uri-template" + }, + "tests": [ + { + "description": "a valid uri-template", + "data": "http://example.com/dictionary/{term:1}/{term}", + "valid": true + }, + { + "description": "an invalid uri-template", + "data": "http://example.com/dictionary/{term:1}/{term", + "valid": false + }, + { + "description": "a valid uri-template without variables", + "data": "http://example.com/dictionary", + "valid": true + }, + { + "description": "a valid relative uri-template", + "data": "dictionary/{term:1}/{term}", + "valid": true + } + ] + }, + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "a valid e-mail address", + "data": "joe.bloggs@example.com", + "valid": true + }, + { + "description": "an invalid e-mail address", + "data": "2962", + "valid": false + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "a valid IP address", + "data": "192.168.0.1", + "valid": true + }, + { + "description": "an IP address with too many components", + "data": "127.0.0.0.1", + "valid": false + }, + { + "description": "an IP address with out-of-range values", + "data": "256.256.256.256", + "valid": false + }, + { + "description": "an IP address without 4 components", + "data": "127.0", + "valid": false + }, + { + "description": "an IP address as an integer", + "data": "0x7f000001", + "valid": false + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "a valid IPv6 address", + "data": "::1", + "valid": true + }, + { + "description": "an IPv6 address with out-of-range values", + "data": "12345::", + "valid": false + }, + { + "description": "an IPv6 address with too many components", + "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", + "valid": false + }, + { + "description": "an IPv6 address containing illegal characters", + "data": "::laptop", + "valid": false + } + ] + }, + { + "description": "validation of host names", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "a valid host name", + "data": "www.example.com", + "valid": true + }, + { + "description": "a host name starting with an illegal character", + "data": "-a-host-name-that-starts-with--", + "valid": false + }, + { + "description": "a host name containing illegal characters", + "data": "not_a_valid_host_name", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", + "valid": false + } + ] + }, + { + "description": "validation of JSON-pointers (JSON String Representation)", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "a valid JSON-pointer", + "data": "/foo/bar~0/baz~1/%a", + "valid": true + }, + { + "description": "not a valid JSON-pointer (~ not escaped)", + "data": "/foo/bar~", + "valid": false + }, + { + "description": "valid JSON-pointer with empty segment", + "data": "/foo//bar", + "valid": true + }, + { + "description": "valid JSON-pointer with the last empty segment", + "data": "/foo/bar/", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #1", + "data": "", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #2", + "data": "/foo", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #3", + "data": "/foo/0", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #4", + "data": "/", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #5", + "data": "/a~1b", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #6", + "data": "/c%d", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #7", + "data": "/e^f", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #8", + "data": "/g|h", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #9", + "data": "/i\\j", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #10", + "data": "/k\"l", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #11", + "data": "/ ", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #12", + "data": "/m~0n", + "valid": true + }, + { + "description": "valid JSON-pointer used adding to the last array position", + "data": "/foo/-", + "valid": true + }, + { + "description": "valid JSON-pointer (- used as object member name)", + "data": "/foo/-/bar", + "valid": true + }, + { + "description": "valid JSON-pointer (multiple escaped characters)", + "data": "/~1~0~0~1~1", + "valid": true + }, + { + "description": "valid JSON-pointer (escaped with fraction part) #1", + "data": "/~1.1", + "valid": true + }, + { + "description": "valid JSON-pointer (escaped with fraction part) #2", + "data": "/~0.1", + "valid": true + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #1", + "data": "#", + "valid": false + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #2", + "data": "#/", + "valid": false + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #3", + "data": "#a", + "valid": false + }, + { + "description": "not a valid JSON-pointer (some escaped, but not all) #1", + "data": "/~0~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (some escaped, but not all) #2", + "data": "/~0/~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (wrong escape character) #1", + "data": "/~2", + "valid": false + }, + { + "description": "not a valid JSON-pointer (wrong escape character) #2", + "data": "/~-1", + "valid": false + }, + { + "description": "not a valid JSON-pointer (multiple characters not escaped)", + "data": "/~~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1", + "data": "a", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2", + "data": "0", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3", + "data": "a/a", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/optional/zeroTerminatedFloats.json b/third_party/opa/internal/gojsonschema/testdata/draft6/optional/zeroTerminatedFloats.json new file mode 100644 index 000000000000..1bcdf9603631 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/optional/zeroTerminatedFloats.json @@ -0,0 +1,15 @@ +[ + { + "description": "some languages do not distinguish between different types of numeric value", + "schema": { + "type": "integer" + }, + "tests": [ + { + "description": "a float without fractional part is an integer", + "data": 1.0, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/pattern.json b/third_party/opa/internal/gojsonschema/testdata/draft6/pattern.json new file mode 100644 index 000000000000..fa8bdb945ef1 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/pattern.json @@ -0,0 +1,34 @@ +[ + { + "description": "pattern validation", + "schema": {"pattern": "^a*$"}, + "tests": [ + { + "description": "a matching pattern is valid", + "data": "aaa", + "valid": true + }, + { + "description": "a non-matching pattern is invalid (but ignored)", + "data": "abc", + "valid": true + }, + { + "description": "ignores non-strings", + "data": true, + "valid": true + } + ] + }, + { + "description": "pattern is not anchored", + "schema": {"pattern": "a+"}, + "tests": [ + { + "description": "matches a substring", + "data": "xxaayy", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/patternProperties.json b/third_party/opa/internal/gojsonschema/testdata/draft6/patternProperties.json new file mode 100644 index 000000000000..1d04a1675cab --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/patternProperties.json @@ -0,0 +1,151 @@ +[ + { + "description": + "patternProperties validates properties matching a regex", + "schema": { + "patternProperties": { + "f.*o": {"type": "integer"} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "multiple valid matches is valid", + "data": {"foo": 1, "foooooo" : 2}, + "valid": true + }, + { + "description": "a single invalid match is invalid", + "data": {"foo": "bar", "fooooo": 2}, + "valid": false + }, + { + "description": "multiple invalid matches is invalid", + "data": {"foo": "bar", "foooooo" : "baz"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["foo"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foo", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "multiple simultaneous patternProperties are validated", + "schema": { + "patternProperties": { + "a*": {"type": "integer"}, + "aaa*": {"maximum": 20} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"a": 21}, + "valid": true + }, + { + "description": "a simultaneous match is valid", + "data": {"aaaa": 18}, + "valid": true + }, + { + "description": "multiple matches is valid", + "data": {"a": 21, "aaaa": 18}, + "valid": true + }, + { + "description": "an invalid due to one is invalid", + "data": {"a": "bar"}, + "valid": false + }, + { + "description": "an invalid due to the other is invalid", + "data": {"aaaa": 31}, + "valid": false + }, + { + "description": "an invalid due to both is invalid", + "data": {"aaa": "foo", "aaaa": 31}, + "valid": false + } + ] + }, + { + "description": "regexes are not anchored by default and are case sensitive", + "schema": { + "patternProperties": { + "[0-9]{2,}": { "type": "boolean" }, + "X_": { "type": "string" } + } + }, + "tests": [ + { + "description": "non recognized members are ignored", + "data": { "answer 1": "42" }, + "valid": true + }, + { + "description": "recognized members are accounted for", + "data": { "a31b": null }, + "valid": false + }, + { + "description": "regexes are case sensitive", + "data": { "a_x_3": 3 }, + "valid": true + }, + { + "description": "regexes are case sensitive, 2", + "data": { "a_X_3": 3 }, + "valid": false + } + ] + }, + { + "description": "patternProperties with boolean schemas", + "schema": { + "patternProperties": { + "f.*": true, + "b.*": false + } + }, + "tests": [ + { + "description": "object with property matching schema true is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property matching schema false is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "object with both properties is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/properties.json b/third_party/opa/internal/gojsonschema/testdata/draft6/properties.json new file mode 100644 index 000000000000..c8ad7197ff4c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/properties.json @@ -0,0 +1,128 @@ +[ + { + "description": "object properties validation", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "string"} + } + }, + "tests": [ + { + "description": "both properties present and valid is valid", + "data": {"foo": 1, "bar": "baz"}, + "valid": true + }, + { + "description": "one property invalid is invalid", + "data": {"foo": 1, "bar": {}}, + "valid": false + }, + { + "description": "both properties invalid is invalid", + "data": {"foo": [], "bar": {}}, + "valid": false + }, + { + "description": "doesn't invalidate other properties", + "data": {"quux": []}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": + "properties, patternProperties, additionalProperties interaction", + "schema": { + "properties": { + "foo": {"type": "array", "maxItems": 3}, + "bar": {"type": "array"} + }, + "patternProperties": {"f.o": {"minItems": 2}}, + "additionalProperties": {"type": "integer"} + }, + "tests": [ + { + "description": "property validates property", + "data": {"foo": [1, 2]}, + "valid": true + }, + { + "description": "property invalidates property", + "data": {"foo": [1, 2, 3, 4]}, + "valid": false + }, + { + "description": "patternProperty invalidates property", + "data": {"foo": []}, + "valid": false + }, + { + "description": "patternProperty validates nonproperty", + "data": {"fxo": [1, 2]}, + "valid": true + }, + { + "description": "patternProperty invalidates nonproperty", + "data": {"fxo": []}, + "valid": false + }, + { + "description": "additionalProperty ignores property", + "data": {"bar": []}, + "valid": true + }, + { + "description": "additionalProperty validates others", + "data": {"quux": 3}, + "valid": true + }, + { + "description": "additionalProperty invalidates others", + "data": {"quux": "foo"}, + "valid": false + } + ] + }, + { + "description": "properties with boolean schema", + "schema": { + "properties": { + "foo": true, + "bar": false + } + }, + "tests": [ + { + "description": "no property present is valid", + "data": {}, + "valid": true + }, + { + "description": "only 'true' property present is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "only 'false' property present is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "both properties present is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/propertyNames.json b/third_party/opa/internal/gojsonschema/testdata/draft6/propertyNames.json new file mode 100644 index 000000000000..8423690d9037 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/propertyNames.json @@ -0,0 +1,78 @@ +[ + { + "description": "propertyNames validation", + "schema": { + "propertyNames": {"maxLength": 3} + }, + "tests": [ + { + "description": "all property names valid", + "data": { + "f": {}, + "foo": {} + }, + "valid": true + }, + { + "description": "some property names invalid", + "data": { + "foo": {}, + "foobar": {} + }, + "valid": false + }, + { + "description": "object without properties is valid", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [1, 2, 3, 4], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "propertyNames with boolean schema true", + "schema": {"propertyNames": true}, + "tests": [ + { + "description": "object with any properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "propertyNames with boolean schema false", + "schema": {"propertyNames": false}, + "tests": [ + { + "description": "object with any properties is invalid", + "data": {"foo": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/ref.json b/third_party/opa/internal/gojsonschema/testdata/draft6/ref.json new file mode 100644 index 000000000000..5b5896492304 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/ref.json @@ -0,0 +1,332 @@ +[ + { + "description": "root pointer ref", + "schema": { + "properties": { + "foo": {"$ref": "#"} + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "match", + "data": {"foo": false}, + "valid": true + }, + { + "description": "recursive match", + "data": {"foo": {"foo": false}}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": false}, + "valid": false + }, + { + "description": "recursive mismatch", + "data": {"foo": {"bar": false}}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to object", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"$ref": "#/properties/foo"} + } + }, + "tests": [ + { + "description": "match", + "data": {"bar": 3}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": true}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to array", + "schema": { + "items": [ + {"type": "integer"}, + {"$ref": "#/items/0"} + ] + }, + "tests": [ + { + "description": "match array", + "data": [1, 2], + "valid": true + }, + { + "description": "mismatch array", + "data": [1, "foo"], + "valid": false + } + ] + }, + { + "description": "escaped pointer ref", + "schema": { + "tilda~field": {"type": "integer"}, + "slash/field": {"type": "integer"}, + "percent%field": {"type": "integer"}, + "properties": { + "tilda": {"$ref": "#/tilda~0field"}, + "slash": {"$ref": "#/slash~1field"}, + "percent": {"$ref": "#/percent%25field"} + } + }, + "tests": [ + { + "description": "slash invalid", + "data": {"slash": "aoeu"}, + "valid": false + }, + { + "description": "tilda invalid", + "data": {"tilda": "aoeu"}, + "valid": false + }, + { + "description": "percent invalid", + "data": {"percent": "aoeu"}, + "valid": false + }, + { + "description": "slash valid", + "data": {"slash": 123}, + "valid": true + }, + { + "description": "tilda valid", + "data": {"tilda": 123}, + "valid": true + }, + { + "description": "percent valid", + "data": {"percent": 123}, + "valid": true + } + ] + }, + { + "description": "nested refs", + "schema": { + "definitions": { + "a": {"type": "integer"}, + "b": {"$ref": "#/definitions/a"}, + "c": {"$ref": "#/definitions/b"} + }, + "$ref": "#/definitions/c" + }, + "tests": [ + { + "description": "nested ref valid", + "data": 5, + "valid": true + }, + { + "description": "nested ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref overrides any sibling keywords", + "schema": { + "definitions": { + "reffed": { + "type": "array" + } + }, + "properties": { + "foo": { + "$ref": "#/definitions/reffed", + "maxItems": 2 + } + } + }, + "tests": [ + { + "description": "ref valid", + "data": { "foo": [] }, + "valid": true + }, + { + "description": "ref valid, maxItems ignored", + "data": { "foo": [ 1, 2, 3] }, + "valid": true + }, + { + "description": "ref invalid", + "data": { "foo": "string" }, + "valid": false + } + ] + }, + { + "description": "remote ref, containing refs itself", + "schema": {"$ref": "http://json-schema.org/draft-06/schema#"}, + "tests": [ + { + "description": "remote ref valid", + "data": {"minLength": 1}, + "valid": true + }, + { + "description": "remote ref invalid", + "data": {"minLength": -1}, + "valid": false + } + ] + }, + { + "description": "property named $ref that is not a reference", + "schema": { + "properties": { + "$ref": {"type": "string"} + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "$ref to boolean schema true", + "schema": { + "$ref": "#/definitions/bool", + "definitions": { + "bool": true + } + }, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "$ref to boolean schema false", + "schema": { + "$ref": "#/definitions/bool", + "definitions": { + "bool": false + } + }, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "Recursive references between schemas", + "schema": { + "$id": "http://localhost:1234/tree", + "description": "tree of nodes", + "type": "object", + "properties": { + "meta": {"type": "string"}, + "nodes": { + "type": "array", + "items": {"$ref": "node"} + } + }, + "required": ["meta", "nodes"], + "definitions": { + "node": { + "$id": "http://localhost:1234/node", + "description": "node", + "type": "object", + "properties": { + "value": {"type": "number"}, + "subtree": {"$ref": "tree"} + }, + "required": ["value"] + } + } + }, + "tests": [ + { + "description": "valid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 1.1}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": true + }, + { + "description": "invalid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": "string is invalid"}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/refRemote.json b/third_party/opa/internal/gojsonschema/testdata/draft6/refRemote.json new file mode 100644 index 000000000000..819d32678a40 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/refRemote.json @@ -0,0 +1,171 @@ +[ + { + "description": "remote ref", + "schema": {"$ref": "http://localhost:1234/integer.json"}, + "tests": [ + { + "description": "remote ref valid", + "data": 1, + "valid": true + }, + { + "description": "remote ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "fragment within remote ref", + "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"}, + "tests": [ + { + "description": "remote fragment valid", + "data": 1, + "valid": true + }, + { + "description": "remote fragment invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref within remote ref", + "schema": { + "$ref": "http://localhost:1234/subSchemas.json#/refToInteger" + }, + "tests": [ + { + "description": "ref within ref valid", + "data": 1, + "valid": true + }, + { + "description": "ref within ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "base URI change", + "schema": { + "$id": "http://localhost:1234/", + "items": { + "$id": "folder/", + "items": {"$ref": "folderInteger.json"} + } + }, + "tests": [ + { + "description": "base URI change ref valid", + "data": [[1]], + "valid": true + }, + { + "description": "base URI change ref invalid", + "data": [["a"]], + "valid": false + } + ] + }, + { + "description": "base URI change - change folder", + "schema": { + "$id": "http://localhost:1234/scope_change_defs1.json", + "type" : "object", + "properties": { + "list": {"$ref": "#/definitions/baz"} + }, + "definitions": { + "baz": { + "$id": "folder/", + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "base URI change - change folder in subschema", + "schema": { + "$id": "http://localhost:1234/scope_change_defs2.json", + "type" : "object", + "properties": { + "list": {"$ref": "#/definitions/baz/definitions/bar"} + }, + "definitions": { + "baz": { + "$id": "folder/", + "definitions": { + "bar": { + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "root ref in remote ref", + "schema": { + "$id": "http://localhost:1234/object", + "type": "object", + "properties": { + "name": {"$ref": "name.json#/definitions/orNull"} + } + }, + "tests": [ + { + "description": "string is valid", + "data": { + "name": "foo" + }, + "valid": true + }, + { + "description": "null is valid", + "data": { + "name": null + }, + "valid": true + }, + { + "description": "object is invalid", + "data": { + "name": { + "name": null + } + }, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/required.json b/third_party/opa/internal/gojsonschema/testdata/draft6/required.json new file mode 100644 index 000000000000..bd96907b9f70 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/required.json @@ -0,0 +1,70 @@ +[ + { + "description": "required validation", + "schema": { + "properties": { + "foo": {}, + "bar": {} + }, + "required": ["foo"] + }, + "tests": [ + { + "description": "present required property is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "non-present required property is invalid", + "data": {"bar": 1}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "required default validation", + "schema": { + "properties": { + "foo": {} + } + }, + "tests": [ + { + "description": "not required by default", + "data": {}, + "valid": true + } + ] + }, + { + "description": "required with empty array", + "schema": { + "properties": { + "foo": {} + }, + "required": [] + }, + "tests": [ + { + "description": "property not required", + "data": {}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/type.json b/third_party/opa/internal/gojsonschema/testdata/draft6/type.json new file mode 100644 index 000000000000..61293740df70 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/type.json @@ -0,0 +1,345 @@ +[ + { + "description": "integer type matches integers", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "an integer is an integer", + "data": 1, + "valid": true + }, + { + "description": "a float is not an integer", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an integer", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not an integer, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not an integer", + "data": {}, + "valid": false + }, + { + "description": "an array is not an integer", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an integer", + "data": true, + "valid": false + }, + { + "description": "null is not an integer", + "data": null, + "valid": false + } + ] + }, + { + "description": "number type matches numbers", + "schema": {"type": "number"}, + "tests": [ + { + "description": "an integer is a number", + "data": 1, + "valid": true + }, + { + "description": "a float is a number", + "data": 1.1, + "valid": true + }, + { + "description": "a string is not a number", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not a number, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not a number", + "data": {}, + "valid": false + }, + { + "description": "an array is not a number", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a number", + "data": true, + "valid": false + }, + { + "description": "null is not a number", + "data": null, + "valid": false + } + ] + }, + { + "description": "string type matches strings", + "schema": {"type": "string"}, + "tests": [ + { + "description": "1 is not a string", + "data": 1, + "valid": false + }, + { + "description": "a float is not a string", + "data": 1.1, + "valid": false + }, + { + "description": "a string is a string", + "data": "foo", + "valid": true + }, + { + "description": "a string is still a string, even if it looks like a number", + "data": "1", + "valid": true + }, + { + "description": "an object is not a string", + "data": {}, + "valid": false + }, + { + "description": "an array is not a string", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a string", + "data": true, + "valid": false + }, + { + "description": "null is not a string", + "data": null, + "valid": false + } + ] + }, + { + "description": "object type matches objects", + "schema": {"type": "object"}, + "tests": [ + { + "description": "an integer is not an object", + "data": 1, + "valid": false + }, + { + "description": "a float is not an object", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an object", + "data": "foo", + "valid": false + }, + { + "description": "an object is an object", + "data": {}, + "valid": true + }, + { + "description": "an array is not an object", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an object", + "data": true, + "valid": false + }, + { + "description": "null is not an object", + "data": null, + "valid": false + } + ] + }, + { + "description": "array type matches arrays", + "schema": {"type": "array"}, + "tests": [ + { + "description": "an integer is not an array", + "data": 1, + "valid": false + }, + { + "description": "a float is not an array", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an array", + "data": "foo", + "valid": false + }, + { + "description": "an object is not an array", + "data": {}, + "valid": false + }, + { + "description": "an array is an array", + "data": [], + "valid": true + }, + { + "description": "a boolean is not an array", + "data": true, + "valid": false + }, + { + "description": "null is not an array", + "data": null, + "valid": false + } + ] + }, + { + "description": "boolean type matches booleans", + "schema": {"type": "boolean"}, + "tests": [ + { + "description": "an integer is not a boolean", + "data": 1, + "valid": false + }, + { + "description": "a float is not a boolean", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not a boolean", + "data": "foo", + "valid": false + }, + { + "description": "an object is not a boolean", + "data": {}, + "valid": false + }, + { + "description": "an array is not a boolean", + "data": [], + "valid": false + }, + { + "description": "a boolean is a boolean", + "data": true, + "valid": true + }, + { + "description": "null is not a boolean", + "data": null, + "valid": false + } + ] + }, + { + "description": "null type matches only the null object", + "schema": {"type": "null"}, + "tests": [ + { + "description": "an integer is not null", + "data": 1, + "valid": false + }, + { + "description": "a float is not null", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not null", + "data": "foo", + "valid": false + }, + { + "description": "an object is not null", + "data": {}, + "valid": false + }, + { + "description": "an array is not null", + "data": [], + "valid": false + }, + { + "description": "a boolean is not null", + "data": true, + "valid": false + }, + { + "description": "null is null", + "data": null, + "valid": true + } + ] + }, + { + "description": "multiple types can be specified in an array", + "schema": {"type": ["integer", "string"]}, + "tests": [ + { + "description": "an integer is valid", + "data": 1, + "valid": true + }, + { + "description": "a string is valid", + "data": "foo", + "valid": true + }, + { + "description": "a float is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "an object is invalid", + "data": {}, + "valid": false + }, + { + "description": "an array is invalid", + "data": [], + "valid": false + }, + { + "description": "a boolean is invalid", + "data": true, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft6/uniqueItems.json b/third_party/opa/internal/gojsonschema/testdata/draft6/uniqueItems.json new file mode 100644 index 000000000000..c1f4ab99c9a4 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft6/uniqueItems.json @@ -0,0 +1,79 @@ +[ + { + "description": "uniqueItems validation", + "schema": {"uniqueItems": true}, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is invalid", + "data": [1, 1], + "valid": false + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": false + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is invalid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": false + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is invalid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": false + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is invalid", + "data": [["foo"], ["foo"]], + "valid": false + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1], + "valid": true + }, + { + "description": "non-unique heterogeneous types are invalid", + "data": [{}, [1], true, null, {}, 1], + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/additionalItems.json b/third_party/opa/internal/gojsonschema/testdata/draft7/additionalItems.json new file mode 100644 index 000000000000..abecc578be3c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/additionalItems.json @@ -0,0 +1,87 @@ +[ + { + "description": "additionalItems as schema", + "schema": { + "items": [{}], + "additionalItems": {"type": "integer"} + }, + "tests": [ + { + "description": "additional items match schema", + "data": [ null, 2, 3, 4 ], + "valid": true + }, + { + "description": "additional items do not match schema", + "data": [ null, 2, 3, "foo" ], + "valid": false + } + ] + }, + { + "description": "items is schema, no additionalItems", + "schema": { + "items": {}, + "additionalItems": false + }, + "tests": [ + { + "description": "all items match schema", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + } + ] + }, + { + "description": "array of items with no additionalItems", + "schema": { + "items": [{}, {}, {}], + "additionalItems": false + }, + "tests": [ + { + "description": "fewer number of items present", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "equal number of items present", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "additional items are not permitted", + "data": [ 1, 2, 3, 4 ], + "valid": false + } + ] + }, + { + "description": "additionalItems as false without items", + "schema": {"additionalItems": false}, + "tests": [ + { + "description": + "items defaults to empty schema so everything is valid", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + } + ] + }, + { + "description": "additionalItems are allowed by default", + "schema": {"items": [{"type": "integer"}]}, + "tests": [ + { + "description": "only the first item is validated", + "data": [1, "foo", false], + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/additionalProperties.json b/third_party/opa/internal/gojsonschema/testdata/draft7/additionalProperties.json new file mode 100644 index 000000000000..90d760734e92 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/additionalProperties.json @@ -0,0 +1,98 @@ +[ + { + "description": + "additionalProperties being false does not allow other properties", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "patternProperties": { "^v": {} }, + "additionalProperties": false + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobarbaz", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + }, + { + "description": "patternProperties are not additional properties", + "data": {"foo":1, "vroom": 2}, + "valid": true + } + ] + }, + { + "description": + "additionalProperties allows a schema which should validate", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional valid property is valid", + "data": {"foo" : 1, "bar" : 2, "quux" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : 12}, + "valid": false + } + ] + }, + { + "description": + "additionalProperties can exist by itself", + "schema": { + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "an additional valid property is valid", + "data": {"foo" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1}, + "valid": false + } + ] + }, + { + "description": "additionalProperties are allowed by default", + "schema": {"properties": {"foo": {}, "bar": {}}}, + "tests": [ + { + "description": "additional properties are allowed", + "data": {"foo": 1, "bar": 2, "quux": true}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/allOf.json b/third_party/opa/internal/gojsonschema/testdata/draft7/allOf.json new file mode 100644 index 000000000000..00c016cd12a0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/allOf.json @@ -0,0 +1,145 @@ +[ + { + "description": "allOf", + "schema": { + "allOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "allOf", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "mismatch second", + "data": {"foo": "baz"}, + "valid": false + }, + { + "description": "mismatch first", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "wrong type", + "data": {"foo": "baz", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "allOf with base schema", + "schema": { + "properties": {"bar": {"type": "integer"}}, + "required": ["bar"], + "allOf" : [ + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + }, + { + "properties": { + "baz": {"type": "null"} + }, + "required": ["baz"] + } + ] + }, + "tests": [ + { + "description": "valid", + "data": {"foo": "quux", "bar": 2, "baz": null}, + "valid": true + }, + { + "description": "mismatch base schema", + "data": {"foo": "quux", "baz": null}, + "valid": false + }, + { + "description": "mismatch first allOf", + "data": {"bar": 2, "baz": null}, + "valid": false + }, + { + "description": "mismatch second allOf", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "mismatch both", + "data": {"bar": 2}, + "valid": false + } + ] + }, + { + "description": "allOf simple types", + "schema": { + "allOf": [ + {"maximum": 30}, + {"minimum": 20} + ] + }, + "tests": [ + { + "description": "valid", + "data": 25, + "valid": true + }, + { + "description": "mismatch one", + "data": 35, + "valid": false + } + ] + }, + { + "description": "allOf with boolean schemas, all true", + "schema": {"allOf": [true, true]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "allOf with boolean schemas, some false", + "schema": {"allOf": [true, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with boolean schemas, all false", + "schema": {"allOf": [false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/anyOf.json b/third_party/opa/internal/gojsonschema/testdata/draft7/anyOf.json new file mode 100644 index 000000000000..4d05a9e509ec --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/anyOf.json @@ -0,0 +1,142 @@ +[ + { + "description": "anyOf", + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first anyOf valid", + "data": 1, + "valid": true + }, + { + "description": "second anyOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both anyOf valid", + "data": 3, + "valid": true + }, + { + "description": "neither anyOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "anyOf with base schema", + "schema": { + "type": "string", + "anyOf" : [ + { + "maxLength": 2 + }, + { + "minLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one anyOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both anyOf invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf with boolean schemas, all true", + "schema": {"anyOf": [true, true]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "anyOf with boolean schemas, some true", + "schema": {"anyOf": [true, false]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "anyOf with boolean schemas, all false", + "schema": {"anyOf": [false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf complex types", + "schema": { + "anyOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first anyOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second anyOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both anyOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "neither anyOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/boolean_schema.json b/third_party/opa/internal/gojsonschema/testdata/draft7/boolean_schema.json new file mode 100644 index 000000000000..6d40f23f2622 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/boolean_schema.json @@ -0,0 +1,104 @@ +[ + { + "description": "boolean schema 'true'", + "schema": true, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "boolean true is valid", + "data": true, + "valid": true + }, + { + "description": "boolean false is valid", + "data": false, + "valid": true + }, + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": "bar"}, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + }, + { + "description": "array is valid", + "data": ["foo"], + "valid": true + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "boolean schema 'false'", + "schema": false, + "tests": [ + { + "description": "number is invalid", + "data": 1, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + }, + { + "description": "boolean true is invalid", + "data": true, + "valid": false + }, + { + "description": "boolean false is invalid", + "data": false, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + }, + { + "description": "object is invalid", + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "empty object is invalid", + "data": {}, + "valid": false + }, + { + "description": "array is invalid", + "data": ["foo"], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/const.json b/third_party/opa/internal/gojsonschema/testdata/draft7/const.json new file mode 100644 index 000000000000..0fe00f21f389 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/const.json @@ -0,0 +1,86 @@ +[ + { + "description": "const validation", + "schema": {"const": 2}, + "tests": [ + { + "description": "same value is valid", + "data": 2, + "valid": true + }, + { + "description": "another value is invalid", + "data": 5, + "valid": false + }, + { + "description": "another type is invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "const with object", + "schema": {"const": {"foo": "bar", "baz": "bax"}}, + "tests": [ + { + "description": "same object is valid", + "data": {"foo": "bar", "baz": "bax"}, + "valid": true + }, + { + "description": "same object with different property order is valid", + "data": {"baz": "bax", "foo": "bar"}, + "valid": true + }, + { + "description": "another object is invalid", + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "another type is invalid", + "data": [1, 2], + "valid": false + } + ] + }, + { + "description": "const with array", + "schema": {"const": [{ "foo": "bar" }]}, + "tests": [ + { + "description": "same array is valid", + "data": [{"foo": "bar"}], + "valid": true + }, + { + "description": "another array item is invalid", + "data": [2], + "valid": false + }, + { + "description": "array with additional items is invalid", + "data": [1, 2, 3], + "valid": false + } + ] + }, + { + "description": "const with null", + "schema": {"const": null}, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "not null is invalid", + "data": 0, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/contains.json b/third_party/opa/internal/gojsonschema/testdata/draft7/contains.json new file mode 100644 index 000000000000..b7ae5a25fea5 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/contains.json @@ -0,0 +1,95 @@ +[ + { + "description": "contains keyword validation", + "schema": { + "contains": {"minimum": 5} + }, + "tests": [ + { + "description": "array with item matching schema (5) is valid", + "data": [3, 4, 5], + "valid": true + }, + { + "description": "array with item matching schema (6) is valid", + "data": [3, 4, 6], + "valid": true + }, + { + "description": "array with two items matching schema (5, 6) is valid", + "data": [3, 4, 5, 6], + "valid": true + }, + { + "description": "array without items matching schema is invalid", + "data": [2, 3, 4], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "not array is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "contains keyword with const keyword", + "schema": { + "contains": { "const": 5 } + }, + "tests": [ + { + "description": "array with item 5 is valid", + "data": [3, 4, 5], + "valid": true + }, + { + "description": "array with two items 5 is valid", + "data": [3, 4, 5, 5], + "valid": true + }, + { + "description": "array without item 5 is invalid", + "data": [1, 2, 3, 4], + "valid": false + } + ] + }, + { + "description": "contains keyword with boolean schema true", + "schema": {"contains": true}, + "tests": [ + { + "description": "any non-empty array is valid", + "data": ["foo"], + "valid": true + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + }, + { + "description": "contains keyword with boolean schema false", + "schema": {"contains": false}, + "tests": [ + { + "description": "any non-empty array is invalid", + "data": ["foo"], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/default.json b/third_party/opa/internal/gojsonschema/testdata/draft7/default.json new file mode 100644 index 000000000000..17629779fbea --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/default.json @@ -0,0 +1,49 @@ +[ + { + "description": "invalid type for default", + "schema": { + "properties": { + "foo": { + "type": "integer", + "default": [] + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"foo": 13}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + }, + { + "description": "invalid string value for default", + "schema": { + "properties": { + "bar": { + "type": "string", + "minLength": 4, + "default": "bad" + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"bar": "good"}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/definitions.json b/third_party/opa/internal/gojsonschema/testdata/draft7/definitions.json new file mode 100644 index 000000000000..436040650a37 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/definitions.json @@ -0,0 +1,32 @@ +[ + { + "description": "valid definition", + "schema": {"$ref": "http://json-schema.org/draft-07/schema#"}, + "tests": [ + { + "description": "valid definition schema", + "data": { + "definitions": { + "foo": {"type": "integer"} + } + }, + "valid": true + } + ] + }, + { + "description": "invalid definition", + "schema": {"$ref": "http://json-schema.org/draft-07/schema#"}, + "tests": [ + { + "description": "invalid definition schema", + "data": { + "definitions": { + "foo": {"type": 1} + } + }, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/dependencies.json b/third_party/opa/internal/gojsonschema/testdata/draft7/dependencies.json new file mode 100644 index 000000000000..80e552f73fc6 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/dependencies.json @@ -0,0 +1,172 @@ +[ + { + "description": "dependencies", + "schema": { + "dependencies": {"bar": ["foo"]} + }, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependant", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "with dependency", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["bar"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "dependencies with empty array", + "schema": { + "dependencies": {"bar": []} + }, + "tests": [ + { + "description": "empty object", + "data": {}, + "valid": true + }, + { + "description": "object with one property", + "data": {"bar": 2}, + "valid": true + } + ] + }, + { + "description": "multiple dependencies", + "schema": { + "dependencies": {"quux": ["foo", "bar"]} + }, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependants", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "with dependencies", + "data": {"foo": 1, "bar": 2, "quux": 3}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"foo": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing other dependency", + "data": {"bar": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing both dependencies", + "data": {"quux": 1}, + "valid": false + } + ] + }, + { + "description": "multiple dependencies subschema", + "schema": { + "dependencies": { + "bar": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "integer"} + } + } + } + }, + "tests": [ + { + "description": "valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "no dependency", + "data": {"foo": "quux"}, + "valid": true + }, + { + "description": "wrong type", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "wrong type other", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + }, + { + "description": "wrong type both", + "data": {"foo": "quux", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "dependencies with boolean subschemas", + "schema": { + "dependencies": { + "foo": true, + "bar": false + } + }, + "tests": [ + { + "description": "object with property having schema true is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property having schema false is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "object with both properties is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/enum.json b/third_party/opa/internal/gojsonschema/testdata/draft7/enum.json new file mode 100644 index 000000000000..f124436a7d90 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/enum.json @@ -0,0 +1,72 @@ +[ + { + "description": "simple enum validation", + "schema": {"enum": [1, 2, 3]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": 1, + "valid": true + }, + { + "description": "something else is invalid", + "data": 4, + "valid": false + } + ] + }, + { + "description": "heterogeneous enum validation", + "schema": {"enum": [6, "foo", [], true, {"foo": 12}]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": [], + "valid": true + }, + { + "description": "something else is invalid", + "data": null, + "valid": false + }, + { + "description": "objects are deep compared", + "data": {"foo": false}, + "valid": false + } + ] + }, + { + "description": "enums in properties", + "schema": { + "type":"object", + "properties": { + "foo": {"enum":["foo"]}, + "bar": {"enum":["bar"]} + }, + "required": ["bar"] + }, + "tests": [ + { + "description": "both properties are valid", + "data": {"foo":"foo", "bar":"bar"}, + "valid": true + }, + { + "description": "missing optional property is valid", + "data": {"bar":"bar"}, + "valid": true + }, + { + "description": "missing required property is invalid", + "data": {"foo":"foo"}, + "valid": false + }, + { + "description": "missing all properties is invalid", + "data": {}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/exclusiveMaximum.json b/third_party/opa/internal/gojsonschema/testdata/draft7/exclusiveMaximum.json new file mode 100644 index 000000000000..dc3cd709d31d --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/exclusiveMaximum.json @@ -0,0 +1,30 @@ +[ + { + "description": "exclusiveMaximum validation", + "schema": { + "exclusiveMaximum": 3.0 + }, + "tests": [ + { + "description": "below the exclusiveMaximum is valid", + "data": 2.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 3.0, + "valid": false + }, + { + "description": "above the exclusiveMaximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/exclusiveMinimum.json b/third_party/opa/internal/gojsonschema/testdata/draft7/exclusiveMinimum.json new file mode 100644 index 000000000000..b38d7ecec630 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/exclusiveMinimum.json @@ -0,0 +1,30 @@ +[ + { + "description": "exclusiveMinimum validation", + "schema": { + "exclusiveMinimum": 1.1 + }, + "tests": [ + { + "description": "above the exclusiveMinimum is valid", + "data": 1.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "below the exclusiveMinimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/format.json b/third_party/opa/internal/gojsonschema/testdata/draft7/format.json new file mode 100644 index 000000000000..49cfac12e6ee --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/format.json @@ -0,0 +1,614 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IDN e-mail addresses", + "schema": {"format": "idn-email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of regexes", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IDN hostnames", + "schema": {"format": "idn-hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of JSON pointers", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of relative JSON pointers", + "schema": {"format": "relative-json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IRIs", + "schema": {"format": "iri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IRI references", + "schema": {"format": "iri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI references", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI templates", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] \ No newline at end of file diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/if-then-else.json b/third_party/opa/internal/gojsonschema/testdata/draft7/if-then-else.json new file mode 100644 index 000000000000..18bd1f79ce1a --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/if-then-else.json @@ -0,0 +1,188 @@ +[ + { + "description": "ignore if without then or else", + "schema": { + "if": { + "const": 0 + } + }, + "tests": [ + { + "description": "valid when valid against lone if", + "data": 0, + "valid": true + }, + { + "description": "valid when invailid against lone if", + "data": "hello", + "valid": true + } + ] + }, + { + "description": "ignore then without if", + "schema": { + "then": { + "const": 0 + } + }, + "tests": [ + { + "description": "valid when valid against lone then", + "data": 0, + "valid": true + }, + { + "description": "valid when invailid against lone then", + "data": "hello", + "valid": true + } + ] + }, + { + "description": "ignore else without if", + "schema": { + "else": { + "const": 0 + } + }, + "tests": [ + { + "description": "valid when valid against lone else", + "data": 0, + "valid": true + }, + { + "description": "valid when invailid against lone else", + "data": "hello", + "valid": true + } + ] + }, + { + "description": "if and then without else", + "schema": { + "if": { + "exclusiveMaximum": 0 + }, + "then": { + "minimum": -10 + } + }, + "tests": [ + { + "description": "valid through then", + "data": -1, + "valid": true + }, + { + "description": "invalid through then", + "data": -100, + "valid": false + }, + { + "description": "valid when if test fails", + "data": 3, + "valid": true + } + ] + }, + { + "description": "if and else without then", + "schema": { + "if": { + "exclusiveMaximum": 0 + }, + "else": { + "multipleOf": 2 + } + }, + "tests": [ + { + "description": "valid when if test passes", + "data": -1, + "valid": true + }, + { + "description": "valid through else", + "data": 4, + "valid": true + }, + { + "description": "invalid through else", + "data": 3, + "valid": false + } + ] + }, + { + "description": "validate against correct branch, then vs else", + "schema": { + "if": { + "exclusiveMaximum": 0 + }, + "then": { + "minimum": -10 + }, + "else": { + "multipleOf": 2 + } + }, + "tests": [ + { + "description": "valid through then", + "data": -1, + "valid": true + }, + { + "description": "invalid through then", + "data": -100, + "valid": false + }, + { + "description": "valid through else", + "data": 4, + "valid": true + }, + { + "description": "invalid through else", + "data": 3, + "valid": false + } + ] + }, + { + "description": "non-interference across combined schemas", + "schema": { + "allOf": [ + { + "if": { + "exclusiveMaximum": 0 + } + }, + { + "then": { + "minimum": -10 + } + }, + { + "else": { + "multipleOf": 2 + } + } + ] + }, + "tests": [ + { + "description": "valid, but woud have been invalid through then", + "data": -100, + "valid": true + }, + { + "description": "valid, but would have been invalid through else", + "data": 3, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/items.json b/third_party/opa/internal/gojsonschema/testdata/draft7/items.json new file mode 100644 index 000000000000..13a6a113669f --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/items.json @@ -0,0 +1,133 @@ +[ + { + "description": "a schema given for items", + "schema": { + "items": {"type": "integer"} + }, + "tests": [ + { + "description": "valid items", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "wrong type of items", + "data": [1, "x"], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "length": 1 + }, + "valid": true + } + ] + }, + { + "description": "an array of schemas for items", + "schema": { + "items": [ + {"type": "integer"}, + {"type": "string"} + ] + }, + "tests": [ + { + "description": "correct types", + "data": [ 1, "foo" ], + "valid": true + }, + { + "description": "wrong types", + "data": [ "foo", 1 ], + "valid": false + }, + { + "description": "incomplete array of items", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with additional items", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "1": "valid", + "length": 2 + }, + "valid": true + } + ] + }, + { + "description": "items with boolean schema (true)", + "schema": {"items": true}, + "tests": [ + { + "description": "any array is valid", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items with boolean schema (false)", + "schema": {"items": false}, + "tests": [ + { + "description": "any non-empty array is invalid", + "data": [ 1, "foo", true ], + "valid": false + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items with boolean schemas", + "schema": { + "items": [true, false] + }, + "tests": [ + { + "description": "array with one item is valid", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with two items is invalid", + "data": [ 1, "foo" ], + "valid": false + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/maxItems.json b/third_party/opa/internal/gojsonschema/testdata/draft7/maxItems.json new file mode 100644 index 000000000000..3b53a6b371a7 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/maxItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "maxItems validation", + "schema": {"maxItems": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": [1], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "too long is invalid", + "data": [1, 2, 3], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "foobar", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/maxLength.json b/third_party/opa/internal/gojsonschema/testdata/draft7/maxLength.json new file mode 100644 index 000000000000..811d35b253c0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/maxLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "maxLength validation", + "schema": {"maxLength": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": "f", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too long is invalid", + "data": "foo", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + }, + { + "description": "two supplementary Unicode code points is long enough", + "data": "\uD83D\uDCA9\uD83D\uDCA9", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/maxProperties.json b/third_party/opa/internal/gojsonschema/testdata/draft7/maxProperties.json new file mode 100644 index 000000000000..513731e4c883 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/maxProperties.json @@ -0,0 +1,38 @@ +[ + { + "description": "maxProperties validation", + "schema": {"maxProperties": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "too long is invalid", + "data": {"foo": 1, "bar": 2, "baz": 3}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/maximum.json b/third_party/opa/internal/gojsonschema/testdata/draft7/maximum.json new file mode 100644 index 000000000000..8150984ee573 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/maximum.json @@ -0,0 +1,28 @@ +[ + { + "description": "maximum validation", + "schema": {"maximum": 3.0}, + "tests": [ + { + "description": "below the maximum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 3.0, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/minItems.json b/third_party/opa/internal/gojsonschema/testdata/draft7/minItems.json new file mode 100644 index 000000000000..ed5118815ee9 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/minItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "minItems validation", + "schema": {"minItems": 1}, + "tests": [ + { + "description": "longer is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1], + "valid": true + }, + { + "description": "too short is invalid", + "data": [], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/minLength.json b/third_party/opa/internal/gojsonschema/testdata/draft7/minLength.json new file mode 100644 index 000000000000..3f09158deef0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/minLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "minLength validation", + "schema": {"minLength": 2}, + "tests": [ + { + "description": "longer is valid", + "data": "foo", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too short is invalid", + "data": "f", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 1, + "valid": true + }, + { + "description": "one supplementary Unicode code point is not long enough", + "data": "\uD83D\uDCA9", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/minProperties.json b/third_party/opa/internal/gojsonschema/testdata/draft7/minProperties.json new file mode 100644 index 000000000000..49a0726e01ce --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/minProperties.json @@ -0,0 +1,38 @@ +[ + { + "description": "minProperties validation", + "schema": {"minProperties": 1}, + "tests": [ + { + "description": "longer is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "too short is invalid", + "data": {}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/minimum.json b/third_party/opa/internal/gojsonschema/testdata/draft7/minimum.json new file mode 100644 index 000000000000..bd1e95bc014d --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/minimum.json @@ -0,0 +1,28 @@ +[ + { + "description": "minimum validation", + "schema": {"minimum": 1.1}, + "tests": [ + { + "description": "above the minimum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 1.1, + "valid": true + }, + { + "description": "below the minimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/multipleOf.json b/third_party/opa/internal/gojsonschema/testdata/draft7/multipleOf.json new file mode 100644 index 000000000000..ca3b7618053f --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/multipleOf.json @@ -0,0 +1,60 @@ +[ + { + "description": "by int", + "schema": {"multipleOf": 2}, + "tests": [ + { + "description": "int by int", + "data": 10, + "valid": true + }, + { + "description": "int by int fail", + "data": 7, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "by number", + "schema": {"multipleOf": 1.5}, + "tests": [ + { + "description": "zero is multiple of anything", + "data": 0, + "valid": true + }, + { + "description": "4.5 is multiple of 1.5", + "data": 4.5, + "valid": true + }, + { + "description": "35 is not multiple of 1.5", + "data": 35, + "valid": false + } + ] + }, + { + "description": "by small number", + "schema": {"multipleOf": 0.0001}, + "tests": [ + { + "description": "0.0075 is multiple of 0.0001", + "data": 0.0075, + "valid": true + }, + { + "description": "0.00751 is not multiple of 0.0001", + "data": 0.00751, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/not.json b/third_party/opa/internal/gojsonschema/testdata/draft7/not.json new file mode 100644 index 000000000000..98de0eda8dd0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/not.json @@ -0,0 +1,117 @@ +[ + { + "description": "not", + "schema": { + "not": {"type": "integer"} + }, + "tests": [ + { + "description": "allowed", + "data": "foo", + "valid": true + }, + { + "description": "disallowed", + "data": 1, + "valid": false + } + ] + }, + { + "description": "not multiple types", + "schema": { + "not": {"type": ["integer", "boolean"]} + }, + "tests": [ + { + "description": "valid", + "data": "foo", + "valid": true + }, + { + "description": "mismatch", + "data": 1, + "valid": false + }, + { + "description": "other mismatch", + "data": true, + "valid": false + } + ] + }, + { + "description": "not more complex schema", + "schema": { + "not": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + }, + "tests": [ + { + "description": "match", + "data": 1, + "valid": true + }, + { + "description": "other match", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "mismatch", + "data": {"foo": "bar"}, + "valid": false + } + ] + }, + { + "description": "forbidden property", + "schema": { + "properties": { + "foo": { + "not": {} + } + } + }, + "tests": [ + { + "description": "property present", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "property absent", + "data": {"bar": 1, "baz": 2}, + "valid": true + } + ] + }, + { + "description": "not with boolean schema true", + "schema": {"not": true}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "not with boolean schema false", + "schema": {"not": false}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/oneOf.json b/third_party/opa/internal/gojsonschema/testdata/draft7/oneOf.json new file mode 100644 index 000000000000..bc4295cab4f6 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/oneOf.json @@ -0,0 +1,153 @@ +[ + { + "description": "oneOf", + "schema": { + "oneOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first oneOf valid", + "data": 1, + "valid": true + }, + { + "description": "second oneOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both oneOf valid", + "data": 3, + "valid": false + }, + { + "description": "neither oneOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "oneOf with base schema", + "schema": { + "type": "string", + "oneOf" : [ + { + "minLength": 2 + }, + { + "maxLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one oneOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both oneOf valid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, all true", + "schema": {"oneOf": [true, true, true]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, one true", + "schema": {"oneOf": [true, false, false]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "oneOf with boolean schemas, more than one true", + "schema": {"oneOf": [true, true, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, all false", + "schema": {"oneOf": [false, false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf complex types", + "schema": { + "oneOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second oneOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both oneOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": false + }, + { + "description": "neither oneOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/bignum.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/bignum.json new file mode 100644 index 000000000000..fac275e21fe2 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/bignum.json @@ -0,0 +1,105 @@ +[ + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a bignum is an integer", + "data": 12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a bignum is a number", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a negative bignum is an integer", + "data": -12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a negative bignum is a number", + "data": -98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "string", + "schema": {"type": "string"}, + "tests": [ + { + "description": "a bignum is not a string", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"maximum": 18446744073709551615}, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision", + "schema": { + "exclusiveMaximum": 972783798187987123879878123.18878137 + }, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 972783798187987123879878123.188781371, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"minimum": -18446744073709551615}, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision on negative numbers", + "schema": { + "exclusiveMinimum": -972783798187987123879878123.18878137 + }, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -972783798187987123879878123.188781371, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/content.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/content.json new file mode 100644 index 000000000000..6a98f11af062 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/content.json @@ -0,0 +1,62 @@ +[ + { + "description": "validation of string-encoded content based on media type", + "schema": { + "contentMediaType": "application/json" + }, + "tests": [ + { + "description": "a valid JSON document", + "data": "{\"foo\": \"bar\"}", + "valid": true + }, + { + "description": "an invalid JSON document", + "data": "{:}", + "valid": false + } + ] + }, + { + "description": "validation of binary string-encoding", + "schema": { + "contentEncoding": "base64" + }, + "tests": [ + { + "description": "a valid base64 string", + "data": "eyJmb28iOiAiYmFyIn0K", + "valid": true + }, + { + "description": "an invalid base64 string (% is not a valid character)", + "data": "eyJmb28iOi%iYmFyIn0K", + "valid": false + } + ] + }, + { + "description": "validation of binary-encoded media type documents", + "schema": { + "contentMediaType": "application/json", + "contentEncoding": "base64" + }, + "tests": [ + { + "description": "a valid base64-encoded JSON document", + "data": "eyJmb28iOiAiYmFyIn0K", + "valid": true + }, + { + "description": "a validly-encoded invalid JSON document", + "data": "ezp9Cg==", + "valid": false + }, + { + "description": "an invalid base64 string that is valid JSON", + "data": "{}", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/ecmascript-regex.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/ecmascript-regex.json new file mode 100644 index 000000000000..08dc9360b870 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/ecmascript-regex.json @@ -0,0 +1,13 @@ +[ + { + "description": "ECMA 262 regex non-compliance", + "schema": { "format": "regex" }, + "tests": [ + { + "description": "ECMA 262 has no support for \\Z anchor from .NET", + "data": "^\\S(|(.|\\n)*\\S)\\Z", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/date-time.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/date-time.json new file mode 100644 index 000000000000..b450fe6223a6 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/date-time.json @@ -0,0 +1,23 @@ +[ + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "a valid date-time string", + "data": "1963-06-19T08:30:06.283185Z", + "valid": true + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963 08:30:06 PST", + "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350T01:01:01", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/date.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/date.json new file mode 100644 index 000000000000..cd23baae3abc --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/date.json @@ -0,0 +1,23 @@ +[ + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "a valid date string", + "data": "1963-06-19", + "valid": true + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963", + "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/email.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/email.json new file mode 100644 index 000000000000..c837c84bc157 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/email.json @@ -0,0 +1,18 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "a valid e-mail address", + "data": "joe.bloggs@example.com", + "valid": true + }, + { + "description": "an invalid e-mail address", + "data": "2962", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/hostname.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/hostname.json new file mode 100644 index 000000000000..d22e57db03f4 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/hostname.json @@ -0,0 +1,33 @@ +[ + { + "description": "validation of host names", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "a valid host name", + "data": "www.example.com", + "valid": true + }, + { + "description": "a valid punycoded IDN hostname", + "data": "xn--4gbwdl.xn--wgbh1c", + "valid": true + }, + { + "description": "a host name starting with an illegal character", + "data": "-a-host-name-that-starts-with--", + "valid": false + }, + { + "description": "a host name containing illegal characters", + "data": "not_a_valid_host_name", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/idn-email.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/idn-email.json new file mode 100644 index 000000000000..637409ea8ffd --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/idn-email.json @@ -0,0 +1,18 @@ +[ + { + "description": "validation of an internationalized e-mail addresses", + "schema": {"format": "idn-email"}, + "tests": [ + { + "description": "a valid idn e-mail (example@example.test in Hangul)", + "data": "실례@실례.테스트", + "valid": true + }, + { + "description": "an invalid idn e-mail address", + "data": "2962", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/idn-hostname.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/idn-hostname.json new file mode 100644 index 000000000000..6a228e60bf4c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/idn-hostname.json @@ -0,0 +1,29 @@ +[ + { + "description": "validation of internationalized host names", + "schema": {"format": "idn-hostname"}, + "disabled" : true, + "tests": [ + { + "description": "a valid host name (example.test in Hangul)", + "data": "실례.테스트", + "valid": true + }, + { + "description": "illegal first char U+302E Hangul single dot tone mark", + "data": "〮실례.테스트", + "valid": false + }, + { + "description": "contains illegal char U+302E Hangul single dot tone mark", + "data": "실〮례.테스트", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실례례테스트례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례테스트례례실례.테스트", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/ipv4.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/ipv4.json new file mode 100644 index 000000000000..661148a74d9c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/ipv4.json @@ -0,0 +1,33 @@ +[ + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "a valid IP address", + "data": "192.168.0.1", + "valid": true + }, + { + "description": "an IP address with too many components", + "data": "127.0.0.0.1", + "valid": false + }, + { + "description": "an IP address with out-of-range values", + "data": "256.256.256.256", + "valid": false + }, + { + "description": "an IP address without 4 components", + "data": "127.0", + "valid": false + }, + { + "description": "an IP address as an integer", + "data": "0x7f000001", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/ipv6.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/ipv6.json new file mode 100644 index 000000000000..f67559b35df0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/ipv6.json @@ -0,0 +1,28 @@ +[ + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "a valid IPv6 address", + "data": "::1", + "valid": true + }, + { + "description": "an IPv6 address with out-of-range values", + "data": "12345::", + "valid": false + }, + { + "description": "an IPv6 address with too many components", + "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", + "valid": false + }, + { + "description": "an IPv6 address containing illegal characters", + "data": "::laptop", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/iri-reference.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/iri-reference.json new file mode 100644 index 000000000000..1fd779c23c0b --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/iri-reference.json @@ -0,0 +1,43 @@ +[ + { + "description": "validation of IRI References", + "schema": {"format": "iri-reference"}, + "tests": [ + { + "description": "a valid IRI", + "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx", + "valid": true + }, + { + "description": "a valid protocol-relative IRI Reference", + "data": "//ƒøø.ßår/?∂éœ=πîx#πîüx", + "valid": true + }, + { + "description": "a valid relative IRI Reference", + "data": "/âππ", + "valid": true + }, + { + "description": "an invalid IRI Reference", + "data": "\\\\WINDOWS\\filëßåré", + "valid": false + }, + { + "description": "a valid IRI Reference", + "data": "âππ", + "valid": true + }, + { + "description": "a valid IRI fragment", + "data": "#ƒrägmênt", + "valid": true + }, + { + "description": "an invalid IRI fragment", + "data": "#ƒräg\\mênt", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/iri.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/iri.json new file mode 100644 index 000000000000..f9c87158053d --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/iri.json @@ -0,0 +1,48 @@ +[ + { + "description": "validation of IRIs", + "schema": {"format": "iri"}, + "tests": [ + { + "description": "a valid IRI with anchor tag", + "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx", + "valid": true + }, + { + "description": "a valid IRI with anchor tag and parantheses", + "data": "http://ƒøø.com/blah_(wîkïpédiå)_blah#ßité-1", + "valid": true + }, + { + "description": "a valid IRI with URL-encoded stuff", + "data": "http://ƒøø.ßår/?q=Test%20URL-encoded%20stuff", + "valid": true + }, + { + "description": "a valid IRI with many special characters", + "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "valid": true + }, + { + "description": "a valid IRI based on IPv6", + "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "valid": true + }, + { + "description": "an invalid relative IRI Reference", + "data": "/abc", + "valid": false + }, + { + "description": "an invalid IRI", + "data": "\\\\WINDOWS\\filëßåré", + "valid": false + }, + { + "description": "an invalid IRI though valid IRI reference", + "data": "âππ", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/json-pointer.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/json-pointer.json new file mode 100644 index 000000000000..65c2f064f082 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/json-pointer.json @@ -0,0 +1,168 @@ +[ + { + "description": "validation of JSON-pointers (JSON String Representation)", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "a valid JSON-pointer", + "data": "/foo/bar~0/baz~1/%a", + "valid": true + }, + { + "description": "not a valid JSON-pointer (~ not escaped)", + "data": "/foo/bar~", + "valid": false + }, + { + "description": "valid JSON-pointer with empty segment", + "data": "/foo//bar", + "valid": true + }, + { + "description": "valid JSON-pointer with the last empty segment", + "data": "/foo/bar/", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #1", + "data": "", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #2", + "data": "/foo", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #3", + "data": "/foo/0", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #4", + "data": "/", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #5", + "data": "/a~1b", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #6", + "data": "/c%d", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #7", + "data": "/e^f", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #8", + "data": "/g|h", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #9", + "data": "/i\\j", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #10", + "data": "/k\"l", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #11", + "data": "/ ", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #12", + "data": "/m~0n", + "valid": true + }, + { + "description": "valid JSON-pointer used adding to the last array position", + "data": "/foo/-", + "valid": true + }, + { + "description": "valid JSON-pointer (- used as object member name)", + "data": "/foo/-/bar", + "valid": true + }, + { + "description": "valid JSON-pointer (multiple escaped characters)", + "data": "/~1~0~0~1~1", + "valid": true + }, + { + "description": "valid JSON-pointer (escaped with fraction part) #1", + "data": "/~1.1", + "valid": true + }, + { + "description": "valid JSON-pointer (escaped with fraction part) #2", + "data": "/~0.1", + "valid": true + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #1", + "data": "#", + "valid": false + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #2", + "data": "#/", + "valid": false + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #3", + "data": "#a", + "valid": false + }, + { + "description": "not a valid JSON-pointer (some escaped, but not all) #1", + "data": "/~0~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (some escaped, but not all) #2", + "data": "/~0/~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (wrong escape character) #1", + "data": "/~2", + "valid": false + }, + { + "description": "not a valid JSON-pointer (wrong escape character) #2", + "data": "/~-1", + "valid": false + }, + { + "description": "not a valid JSON-pointer (multiple characters not escaped)", + "data": "/~~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1", + "data": "a", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2", + "data": "0", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3", + "data": "a/a", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/regex.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/regex.json new file mode 100644 index 000000000000..d99d021ec02c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/regex.json @@ -0,0 +1,18 @@ +[ + { + "description": "validation of regular expressions", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "a valid regular expression", + "data": "([abc])+\\s+$", + "valid": true + }, + { + "description": "a regular expression with unclosed parens is invalid", + "data": "^(abc]", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/relative-json-pointer.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/relative-json-pointer.json new file mode 100644 index 000000000000..ceeb743a3212 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/relative-json-pointer.json @@ -0,0 +1,33 @@ +[ + { + "description": "validation of Relative JSON Pointers (RJP)", + "schema": {"format": "relative-json-pointer"}, + "tests": [ + { + "description": "a valid upwards RJP", + "data": "1", + "valid": true + }, + { + "description": "a valid downwards RJP", + "data": "0/foo/bar", + "valid": true + }, + { + "description": "a valid up and then down RJP, with array index", + "data": "2/0/baz/1/zip", + "valid": true + }, + { + "description": "a valid RJP taking the member or index name", + "data": "0#", + "valid": true + }, + { + "description": "an invalid RJP that is a valid JSON Pointer", + "data": "/foo/bar", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/time.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/time.json new file mode 100644 index 000000000000..21f74703d4bc --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/time.json @@ -0,0 +1,23 @@ +[ + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "a valid time string", + "data": "08:30:06.283185Z", + "valid": true + }, + { + "description": "an invalid time string", + "data": "08:30:06 PST", + "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "14:30", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/uri-reference.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/uri-reference.json new file mode 100644 index 000000000000..e4c9eef63cf3 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/uri-reference.json @@ -0,0 +1,43 @@ +[ + { + "description": "validation of URI References", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "a valid URI", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid relative URI Reference", + "data": "/abc", + "valid": true + }, + { + "description": "an invalid URI Reference", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "a valid URI Reference", + "data": "abc", + "valid": true + }, + { + "description": "a valid URI fragment", + "data": "#fragment", + "valid": true + }, + { + "description": "an invalid URI fragment", + "data": "#frag\\ment", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/uri-template.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/uri-template.json new file mode 100644 index 000000000000..d8396a5a79b1 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/uri-template.json @@ -0,0 +1,30 @@ +[ + { + "description": "format: uri-template", + "schema": { + "format": "uri-template" + }, + "tests": [ + { + "description": "a valid uri-template", + "data": "http://example.com/dictionary/{term:1}/{term}", + "valid": true + }, + { + "description": "an invalid uri-template", + "data": "http://example.com/dictionary/{term:1}/{term", + "valid": false + }, + { + "description": "a valid uri-template without variables", + "data": "http://example.com/dictionary", + "valid": true + }, + { + "description": "a valid relative uri-template", + "data": "dictionary/{term:1}/{term}", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/uri.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/uri.json new file mode 100644 index 000000000000..25cc40c80a9a --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/format/uri.json @@ -0,0 +1,103 @@ +[ + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "a valid URL with anchor tag", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid URL with anchor tag and parantheses", + "data": "http://foo.com/blah_(wikipedia)_blah#cite-1", + "valid": true + }, + { + "description": "a valid URL with URL-encoded stuff", + "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff", + "valid": true + }, + { + "description": "a valid puny-coded URL ", + "data": "http://xn--nw2a.xn--j6w193g/", + "valid": true + }, + { + "description": "a valid URL with many special characters", + "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "valid": true + }, + { + "description": "a valid URL based on IPv4", + "data": "http://223.255.255.254", + "valid": true + }, + { + "description": "a valid URL with ftp scheme", + "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt", + "valid": true + }, + { + "description": "a valid URL for a simple text file", + "data": "http://www.ietf.org/rfc/rfc2396.txt", + "valid": true + }, + { + "description": "a valid URL ", + "data": "ldap://[2001:db8::7]/c=GB?objectClass?one", + "valid": true + }, + { + "description": "a valid mailto URI", + "data": "mailto:John.Doe@example.com", + "valid": true + }, + { + "description": "a valid newsgroup URI", + "data": "news:comp.infosystems.www.servers.unix", + "valid": true + }, + { + "description": "a valid tel URI", + "data": "tel:+1-816-555-1212", + "valid": true + }, + { + "description": "a valid URN", + "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", + "valid": true + }, + { + "description": "an invalid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": false + }, + { + "description": "an invalid relative URI Reference", + "data": "/abc", + "valid": false + }, + { + "description": "an invalid URI", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "an invalid URI though valid URI reference", + "data": "abc", + "valid": false + }, + { + "description": "an invalid URI with spaces", + "data": "http:// shouldfail.com", + "valid": false + }, + { + "description": "an invalid URI with spaces and missing scheme", + "data": ":// should fail", + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/optional/zeroTerminatedFloats.json b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/zeroTerminatedFloats.json new file mode 100644 index 000000000000..1bcdf9603631 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/optional/zeroTerminatedFloats.json @@ -0,0 +1,15 @@ +[ + { + "description": "some languages do not distinguish between different types of numeric value", + "schema": { + "type": "integer" + }, + "tests": [ + { + "description": "a float without fractional part is an integer", + "data": 1.0, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/pattern.json b/third_party/opa/internal/gojsonschema/testdata/draft7/pattern.json new file mode 100644 index 000000000000..fa8bdb945ef1 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/pattern.json @@ -0,0 +1,34 @@ +[ + { + "description": "pattern validation", + "schema": {"pattern": "^a*$"}, + "tests": [ + { + "description": "a matching pattern is valid", + "data": "aaa", + "valid": true + }, + { + "description": "a non-matching pattern is invalid (but ignored)", + "data": "abc", + "valid": true + }, + { + "description": "ignores non-strings", + "data": true, + "valid": true + } + ] + }, + { + "description": "pattern is not anchored", + "schema": {"pattern": "a+"}, + "tests": [ + { + "description": "matches a substring", + "data": "xxaayy", + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/patternProperties.json b/third_party/opa/internal/gojsonschema/testdata/draft7/patternProperties.json new file mode 100644 index 000000000000..1d04a1675cab --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/patternProperties.json @@ -0,0 +1,151 @@ +[ + { + "description": + "patternProperties validates properties matching a regex", + "schema": { + "patternProperties": { + "f.*o": {"type": "integer"} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "multiple valid matches is valid", + "data": {"foo": 1, "foooooo" : 2}, + "valid": true + }, + { + "description": "a single invalid match is invalid", + "data": {"foo": "bar", "fooooo": 2}, + "valid": false + }, + { + "description": "multiple invalid matches is invalid", + "data": {"foo": "bar", "foooooo" : "baz"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["foo"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foo", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "multiple simultaneous patternProperties are validated", + "schema": { + "patternProperties": { + "a*": {"type": "integer"}, + "aaa*": {"maximum": 20} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"a": 21}, + "valid": true + }, + { + "description": "a simultaneous match is valid", + "data": {"aaaa": 18}, + "valid": true + }, + { + "description": "multiple matches is valid", + "data": {"a": 21, "aaaa": 18}, + "valid": true + }, + { + "description": "an invalid due to one is invalid", + "data": {"a": "bar"}, + "valid": false + }, + { + "description": "an invalid due to the other is invalid", + "data": {"aaaa": 31}, + "valid": false + }, + { + "description": "an invalid due to both is invalid", + "data": {"aaa": "foo", "aaaa": 31}, + "valid": false + } + ] + }, + { + "description": "regexes are not anchored by default and are case sensitive", + "schema": { + "patternProperties": { + "[0-9]{2,}": { "type": "boolean" }, + "X_": { "type": "string" } + } + }, + "tests": [ + { + "description": "non recognized members are ignored", + "data": { "answer 1": "42" }, + "valid": true + }, + { + "description": "recognized members are accounted for", + "data": { "a31b": null }, + "valid": false + }, + { + "description": "regexes are case sensitive", + "data": { "a_x_3": 3 }, + "valid": true + }, + { + "description": "regexes are case sensitive, 2", + "data": { "a_X_3": 3 }, + "valid": false + } + ] + }, + { + "description": "patternProperties with boolean schemas", + "schema": { + "patternProperties": { + "f.*": true, + "b.*": false + } + }, + "tests": [ + { + "description": "object with property matching schema true is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property matching schema false is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "object with both properties is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/properties.json b/third_party/opa/internal/gojsonschema/testdata/draft7/properties.json new file mode 100644 index 000000000000..c8ad7197ff4c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/properties.json @@ -0,0 +1,128 @@ +[ + { + "description": "object properties validation", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "string"} + } + }, + "tests": [ + { + "description": "both properties present and valid is valid", + "data": {"foo": 1, "bar": "baz"}, + "valid": true + }, + { + "description": "one property invalid is invalid", + "data": {"foo": 1, "bar": {}}, + "valid": false + }, + { + "description": "both properties invalid is invalid", + "data": {"foo": [], "bar": {}}, + "valid": false + }, + { + "description": "doesn't invalidate other properties", + "data": {"quux": []}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": + "properties, patternProperties, additionalProperties interaction", + "schema": { + "properties": { + "foo": {"type": "array", "maxItems": 3}, + "bar": {"type": "array"} + }, + "patternProperties": {"f.o": {"minItems": 2}}, + "additionalProperties": {"type": "integer"} + }, + "tests": [ + { + "description": "property validates property", + "data": {"foo": [1, 2]}, + "valid": true + }, + { + "description": "property invalidates property", + "data": {"foo": [1, 2, 3, 4]}, + "valid": false + }, + { + "description": "patternProperty invalidates property", + "data": {"foo": []}, + "valid": false + }, + { + "description": "patternProperty validates nonproperty", + "data": {"fxo": [1, 2]}, + "valid": true + }, + { + "description": "patternProperty invalidates nonproperty", + "data": {"fxo": []}, + "valid": false + }, + { + "description": "additionalProperty ignores property", + "data": {"bar": []}, + "valid": true + }, + { + "description": "additionalProperty validates others", + "data": {"quux": 3}, + "valid": true + }, + { + "description": "additionalProperty invalidates others", + "data": {"quux": "foo"}, + "valid": false + } + ] + }, + { + "description": "properties with boolean schema", + "schema": { + "properties": { + "foo": true, + "bar": false + } + }, + "tests": [ + { + "description": "no property present is valid", + "data": {}, + "valid": true + }, + { + "description": "only 'true' property present is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "only 'false' property present is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "both properties present is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/propertyNames.json b/third_party/opa/internal/gojsonschema/testdata/draft7/propertyNames.json new file mode 100644 index 000000000000..8423690d9037 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/propertyNames.json @@ -0,0 +1,78 @@ +[ + { + "description": "propertyNames validation", + "schema": { + "propertyNames": {"maxLength": 3} + }, + "tests": [ + { + "description": "all property names valid", + "data": { + "f": {}, + "foo": {} + }, + "valid": true + }, + { + "description": "some property names invalid", + "data": { + "foo": {}, + "foobar": {} + }, + "valid": false + }, + { + "description": "object without properties is valid", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [1, 2, 3, 4], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "propertyNames with boolean schema true", + "schema": {"propertyNames": true}, + "tests": [ + { + "description": "object with any properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "propertyNames with boolean schema false", + "schema": {"propertyNames": false}, + "tests": [ + { + "description": "object with any properties is invalid", + "data": {"foo": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/ref.json b/third_party/opa/internal/gojsonschema/testdata/draft7/ref.json new file mode 100644 index 000000000000..75795076b321 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/ref.json @@ -0,0 +1,332 @@ +[ + { + "description": "root pointer ref", + "schema": { + "properties": { + "foo": {"$ref": "#"} + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "match", + "data": {"foo": false}, + "valid": true + }, + { + "description": "recursive match", + "data": {"foo": {"foo": false}}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": false}, + "valid": false + }, + { + "description": "recursive mismatch", + "data": {"foo": {"bar": false}}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to object", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"$ref": "#/properties/foo"} + } + }, + "tests": [ + { + "description": "match", + "data": {"bar": 3}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": true}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to array", + "schema": { + "items": [ + {"type": "integer"}, + {"$ref": "#/items/0"} + ] + }, + "tests": [ + { + "description": "match array", + "data": [1, 2], + "valid": true + }, + { + "description": "mismatch array", + "data": [1, "foo"], + "valid": false + } + ] + }, + { + "description": "escaped pointer ref", + "schema": { + "tilda~field": {"type": "integer"}, + "slash/field": {"type": "integer"}, + "percent%field": {"type": "integer"}, + "properties": { + "tilda": {"$ref": "#/tilda~0field"}, + "slash": {"$ref": "#/slash~1field"}, + "percent": {"$ref": "#/percent%25field"} + } + }, + "tests": [ + { + "description": "slash invalid", + "data": {"slash": "aoeu"}, + "valid": false + }, + { + "description": "tilda invalid", + "data": {"tilda": "aoeu"}, + "valid": false + }, + { + "description": "percent invalid", + "data": {"percent": "aoeu"}, + "valid": false + }, + { + "description": "slash valid", + "data": {"slash": 123}, + "valid": true + }, + { + "description": "tilda valid", + "data": {"tilda": 123}, + "valid": true + }, + { + "description": "percent valid", + "data": {"percent": 123}, + "valid": true + } + ] + }, + { + "description": "nested refs", + "schema": { + "definitions": { + "a": {"type": "integer"}, + "b": {"$ref": "#/definitions/a"}, + "c": {"$ref": "#/definitions/b"} + }, + "$ref": "#/definitions/c" + }, + "tests": [ + { + "description": "nested ref valid", + "data": 5, + "valid": true + }, + { + "description": "nested ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref overrides any sibling keywords", + "schema": { + "definitions": { + "reffed": { + "type": "array" + } + }, + "properties": { + "foo": { + "$ref": "#/definitions/reffed", + "maxItems": 2 + } + } + }, + "tests": [ + { + "description": "ref valid", + "data": { "foo": [] }, + "valid": true + }, + { + "description": "ref valid, maxItems ignored", + "data": { "foo": [ 1, 2, 3] }, + "valid": true + }, + { + "description": "ref invalid", + "data": { "foo": "string" }, + "valid": false + } + ] + }, + { + "description": "remote ref, containing refs itself", + "schema": {"$ref": "http://json-schema.org/draft-07/schema#"}, + "tests": [ + { + "description": "remote ref valid", + "data": {"minLength": 1}, + "valid": true + }, + { + "description": "remote ref invalid", + "data": {"minLength": -1}, + "valid": false + } + ] + }, + { + "description": "property named $ref that is not a reference", + "schema": { + "properties": { + "$ref": {"type": "string"} + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "$ref to boolean schema true", + "schema": { + "$ref": "#/definitions/bool", + "definitions": { + "bool": true + } + }, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "$ref to boolean schema false", + "schema": { + "$ref": "#/definitions/bool", + "definitions": { + "bool": false + } + }, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "Recursive references between schemas", + "schema": { + "$id": "http://localhost:1234/tree", + "description": "tree of nodes", + "type": "object", + "properties": { + "meta": {"type": "string"}, + "nodes": { + "type": "array", + "items": {"$ref": "node"} + } + }, + "required": ["meta", "nodes"], + "definitions": { + "node": { + "$id": "http://localhost:1234/node", + "description": "node", + "type": "object", + "properties": { + "value": {"type": "number"}, + "subtree": {"$ref": "tree"} + }, + "required": ["value"] + } + } + }, + "tests": [ + { + "description": "valid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 1.1}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": true + }, + { + "description": "invalid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": "string is invalid"}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/refRemote.json b/third_party/opa/internal/gojsonschema/testdata/draft7/refRemote.json new file mode 100644 index 000000000000..819d32678a40 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/refRemote.json @@ -0,0 +1,171 @@ +[ + { + "description": "remote ref", + "schema": {"$ref": "http://localhost:1234/integer.json"}, + "tests": [ + { + "description": "remote ref valid", + "data": 1, + "valid": true + }, + { + "description": "remote ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "fragment within remote ref", + "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"}, + "tests": [ + { + "description": "remote fragment valid", + "data": 1, + "valid": true + }, + { + "description": "remote fragment invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref within remote ref", + "schema": { + "$ref": "http://localhost:1234/subSchemas.json#/refToInteger" + }, + "tests": [ + { + "description": "ref within ref valid", + "data": 1, + "valid": true + }, + { + "description": "ref within ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "base URI change", + "schema": { + "$id": "http://localhost:1234/", + "items": { + "$id": "folder/", + "items": {"$ref": "folderInteger.json"} + } + }, + "tests": [ + { + "description": "base URI change ref valid", + "data": [[1]], + "valid": true + }, + { + "description": "base URI change ref invalid", + "data": [["a"]], + "valid": false + } + ] + }, + { + "description": "base URI change - change folder", + "schema": { + "$id": "http://localhost:1234/scope_change_defs1.json", + "type" : "object", + "properties": { + "list": {"$ref": "#/definitions/baz"} + }, + "definitions": { + "baz": { + "$id": "folder/", + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "base URI change - change folder in subschema", + "schema": { + "$id": "http://localhost:1234/scope_change_defs2.json", + "type" : "object", + "properties": { + "list": {"$ref": "#/definitions/baz/definitions/bar"} + }, + "definitions": { + "baz": { + "$id": "folder/", + "definitions": { + "bar": { + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "root ref in remote ref", + "schema": { + "$id": "http://localhost:1234/object", + "type": "object", + "properties": { + "name": {"$ref": "name.json#/definitions/orNull"} + } + }, + "tests": [ + { + "description": "string is valid", + "data": { + "name": "foo" + }, + "valid": true + }, + { + "description": "null is valid", + "data": { + "name": null + }, + "valid": true + }, + { + "description": "object is invalid", + "data": { + "name": { + "name": null + } + }, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/required.json b/third_party/opa/internal/gojsonschema/testdata/draft7/required.json new file mode 100644 index 000000000000..bd96907b9f70 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/required.json @@ -0,0 +1,70 @@ +[ + { + "description": "required validation", + "schema": { + "properties": { + "foo": {}, + "bar": {} + }, + "required": ["foo"] + }, + "tests": [ + { + "description": "present required property is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "non-present required property is invalid", + "data": {"bar": 1}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "required default validation", + "schema": { + "properties": { + "foo": {} + } + }, + "tests": [ + { + "description": "not required by default", + "data": {}, + "valid": true + } + ] + }, + { + "description": "required with empty array", + "schema": { + "properties": { + "foo": {} + }, + "required": [] + }, + "tests": [ + { + "description": "property not required", + "data": {}, + "valid": true + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/type.json b/third_party/opa/internal/gojsonschema/testdata/draft7/type.json new file mode 100644 index 000000000000..61293740df70 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/type.json @@ -0,0 +1,345 @@ +[ + { + "description": "integer type matches integers", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "an integer is an integer", + "data": 1, + "valid": true + }, + { + "description": "a float is not an integer", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an integer", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not an integer, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not an integer", + "data": {}, + "valid": false + }, + { + "description": "an array is not an integer", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an integer", + "data": true, + "valid": false + }, + { + "description": "null is not an integer", + "data": null, + "valid": false + } + ] + }, + { + "description": "number type matches numbers", + "schema": {"type": "number"}, + "tests": [ + { + "description": "an integer is a number", + "data": 1, + "valid": true + }, + { + "description": "a float is a number", + "data": 1.1, + "valid": true + }, + { + "description": "a string is not a number", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not a number, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not a number", + "data": {}, + "valid": false + }, + { + "description": "an array is not a number", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a number", + "data": true, + "valid": false + }, + { + "description": "null is not a number", + "data": null, + "valid": false + } + ] + }, + { + "description": "string type matches strings", + "schema": {"type": "string"}, + "tests": [ + { + "description": "1 is not a string", + "data": 1, + "valid": false + }, + { + "description": "a float is not a string", + "data": 1.1, + "valid": false + }, + { + "description": "a string is a string", + "data": "foo", + "valid": true + }, + { + "description": "a string is still a string, even if it looks like a number", + "data": "1", + "valid": true + }, + { + "description": "an object is not a string", + "data": {}, + "valid": false + }, + { + "description": "an array is not a string", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a string", + "data": true, + "valid": false + }, + { + "description": "null is not a string", + "data": null, + "valid": false + } + ] + }, + { + "description": "object type matches objects", + "schema": {"type": "object"}, + "tests": [ + { + "description": "an integer is not an object", + "data": 1, + "valid": false + }, + { + "description": "a float is not an object", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an object", + "data": "foo", + "valid": false + }, + { + "description": "an object is an object", + "data": {}, + "valid": true + }, + { + "description": "an array is not an object", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an object", + "data": true, + "valid": false + }, + { + "description": "null is not an object", + "data": null, + "valid": false + } + ] + }, + { + "description": "array type matches arrays", + "schema": {"type": "array"}, + "tests": [ + { + "description": "an integer is not an array", + "data": 1, + "valid": false + }, + { + "description": "a float is not an array", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an array", + "data": "foo", + "valid": false + }, + { + "description": "an object is not an array", + "data": {}, + "valid": false + }, + { + "description": "an array is an array", + "data": [], + "valid": true + }, + { + "description": "a boolean is not an array", + "data": true, + "valid": false + }, + { + "description": "null is not an array", + "data": null, + "valid": false + } + ] + }, + { + "description": "boolean type matches booleans", + "schema": {"type": "boolean"}, + "tests": [ + { + "description": "an integer is not a boolean", + "data": 1, + "valid": false + }, + { + "description": "a float is not a boolean", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not a boolean", + "data": "foo", + "valid": false + }, + { + "description": "an object is not a boolean", + "data": {}, + "valid": false + }, + { + "description": "an array is not a boolean", + "data": [], + "valid": false + }, + { + "description": "a boolean is a boolean", + "data": true, + "valid": true + }, + { + "description": "null is not a boolean", + "data": null, + "valid": false + } + ] + }, + { + "description": "null type matches only the null object", + "schema": {"type": "null"}, + "tests": [ + { + "description": "an integer is not null", + "data": 1, + "valid": false + }, + { + "description": "a float is not null", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not null", + "data": "foo", + "valid": false + }, + { + "description": "an object is not null", + "data": {}, + "valid": false + }, + { + "description": "an array is not null", + "data": [], + "valid": false + }, + { + "description": "a boolean is not null", + "data": true, + "valid": false + }, + { + "description": "null is null", + "data": null, + "valid": true + } + ] + }, + { + "description": "multiple types can be specified in an array", + "schema": {"type": ["integer", "string"]}, + "tests": [ + { + "description": "an integer is valid", + "data": 1, + "valid": true + }, + { + "description": "a string is valid", + "data": "foo", + "valid": true + }, + { + "description": "a float is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "an object is invalid", + "data": {}, + "valid": false + }, + { + "description": "an array is invalid", + "data": [], + "valid": false + }, + { + "description": "a boolean is invalid", + "data": true, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/draft7/uniqueItems.json b/third_party/opa/internal/gojsonschema/testdata/draft7/uniqueItems.json new file mode 100644 index 000000000000..c1f4ab99c9a4 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/draft7/uniqueItems.json @@ -0,0 +1,79 @@ +[ + { + "description": "uniqueItems validation", + "schema": {"uniqueItems": true}, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is invalid", + "data": [1, 1], + "valid": false + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": false + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is invalid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": false + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is invalid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": false + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is invalid", + "data": [["foo"], ["foo"]], + "valid": false + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1], + "valid": true + }, + { + "description": "non-unique heterogeneous types are invalid", + "data": [{}, [1], true, null, {}, 1], + "valid": false + } + ] + } +] diff --git a/third_party/opa/internal/gojsonschema/testdata/extra/file with space.json b/third_party/opa/internal/gojsonschema/testdata/extra/file with space.json new file mode 100644 index 000000000000..de33b4d8378a --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/extra/file with space.json @@ -0,0 +1 @@ +{"foo": true} diff --git a/third_party/opa/internal/gojsonschema/testdata/extra/fragment_schema.json b/third_party/opa/internal/gojsonschema/testdata/extra/fragment_schema.json new file mode 100644 index 000000000000..a91d885949c3 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/extra/fragment_schema.json @@ -0,0 +1 @@ +{"type":"object","additionalProperties":false,"definitions":{"x":{"type":"integer"}}} diff --git a/third_party/opa/internal/gojsonschema/testdata/remotes/folder/folderInteger.json b/third_party/opa/internal/gojsonschema/testdata/remotes/folder/folderInteger.json new file mode 100644 index 000000000000..dbe5c758ee3c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/remotes/folder/folderInteger.json @@ -0,0 +1,3 @@ +{ + "type": "integer" +} \ No newline at end of file diff --git a/third_party/opa/internal/gojsonschema/testdata/remotes/integer.json b/third_party/opa/internal/gojsonschema/testdata/remotes/integer.json new file mode 100644 index 000000000000..dbe5c758ee3c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/remotes/integer.json @@ -0,0 +1,3 @@ +{ + "type": "integer" +} \ No newline at end of file diff --git a/third_party/opa/internal/gojsonschema/testdata/remotes/name.json b/third_party/opa/internal/gojsonschema/testdata/remotes/name.json new file mode 100644 index 000000000000..19ba093552cb --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/remotes/name.json @@ -0,0 +1,11 @@ +{ + "definitions": { + "orNull": { + "anyOf": [ + {"type": "null"}, + {"$ref": "#"} + ] + } + }, + "type": "string" +} diff --git a/third_party/opa/internal/gojsonschema/testdata/remotes/subSchemas.json b/third_party/opa/internal/gojsonschema/testdata/remotes/subSchemas.json new file mode 100644 index 000000000000..8b6d8f842fc0 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/testdata/remotes/subSchemas.json @@ -0,0 +1,8 @@ +{ + "integer": { + "type": "integer" + }, + "refToInteger": { + "$ref": "#/integer" + } +} \ No newline at end of file diff --git a/third_party/opa/internal/gojsonschema/types.go b/third_party/opa/internal/gojsonschema/types.go new file mode 100644 index 000000000000..df2d4b2f017c --- /dev/null +++ b/third_party/opa/internal/gojsonschema/types.go @@ -0,0 +1,62 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Contains const types for schema and JSON. +// +// created 28-02-2013 + +package gojsonschema + +// Type constants +const ( + TypeArray = `array` + TypeBoolean = `boolean` + TypeInteger = `integer` + TypeNumber = `number` + TypeNull = `null` + TypeObject = `object` + TypeString = `string` +) + +// JSONTypes hosts the list of type that are supported in JSON +var JSONTypes []string + +// SchemaTypes Hosts The List Of Type That Are Supported In Schemas +var SchemaTypes []string + +func init() { + JSONTypes = []string{ + TypeArray, + TypeBoolean, + TypeInteger, + TypeNumber, + TypeNull, + TypeObject, + TypeString} + + SchemaTypes = []string{ + TypeArray, + TypeBoolean, + TypeInteger, + TypeNumber, + TypeObject, + TypeString} +} diff --git a/third_party/opa/internal/gojsonschema/utils.go b/third_party/opa/internal/gojsonschema/utils.go new file mode 100644 index 000000000000..ca071930f21b --- /dev/null +++ b/third_party/opa/internal/gojsonschema/utils.go @@ -0,0 +1,161 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Various utility functions. +// +// created 26-02-2013 + +// nolint: deadcode,unused,varcheck // Package in development (2021). +package gojsonschema + +import ( + "encoding/json" + "math/big" + "slices" +) + +func isStringInSlice(s []string, what string) bool { + return slices.Contains(s, what) +} + +func marshalToJSONString(value any) (*string, error) { + + mBytes, err := json.Marshal(value) + if err != nil { + return nil, err + } + + sBytes := string(mBytes) + return &sBytes, nil +} + +func marshalWithoutNumber(value any) (*string, error) { + + // The JSON is decoded using https://golang.org/pkg/encoding/json/#Decoder.UseNumber + // This means the numbers are internally still represented as strings and therefore 1.00 is unequal to 1 + // One way to eliminate these differences is to decode and encode the JSON one more time without Decoder.UseNumber + // so that these differences in representation are removed + + jsonString, err := marshalToJSONString(value) + if err != nil { + return nil, err + } + + var document any + + err = json.Unmarshal([]byte(*jsonString), &document) + if err != nil { + return nil, err + } + + return marshalToJSONString(document) +} + +func isJSONNumber(what any) bool { + + switch what.(type) { + + case json.Number: + return true + } + + return false +} + +func checkJSONInteger(what any) (isInt bool) { + + jsonNumber := what.(json.Number) + + bigFloat, isValidNumber := new(big.Rat).SetString(string(jsonNumber)) + + return isValidNumber && bigFloat.IsInt() + +} + +// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER +const ( + maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1 + minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1 +) + +func mustBeInteger(what any) *int { + number, ok := what.(json.Number) + if !ok { + return nil + } + + isInt := checkJSONInteger(number) + if !isInt { + return nil + } + + int64Value, err := number.Int64() + if err != nil { + return nil + } + + // This doesn't actually convert to an int32 value; it converts to the + // system-specific default integer. Assuming this is a valid int32 could cause + // bugs. + int32Value := int(int64Value) + return &int32Value +} + +func mustBeNumber(what any) *big.Rat { + number, ok := what.(json.Number) + if !ok { + return nil + } + + float64Value, success := new(big.Rat).SetString(string(number)) + if success { + return float64Value + } + return nil +} + +func convertDocumentNode(val any) any { + + if lval, ok := val.([]any); ok { + + res := []any{} + for _, v := range lval { + res = append(res, convertDocumentNode(v)) + } + + return res + + } + + if mval, ok := val.(map[any]any); ok { + + res := map[string]any{} + + for k, v := range mval { + res[k.(string)] = convertDocumentNode(v) + } + + return res + + } + + return val +} diff --git a/third_party/opa/internal/gojsonschema/utils_test.go b/third_party/opa/internal/gojsonschema/utils_test.go new file mode 100644 index 000000000000..78d789077123 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/utils_test.go @@ -0,0 +1,58 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author janmentzel +// author-github https://github.com/janmentzel +// author-mail ? ( forward to xeipuuv@gmail.com ) +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description (Unit) Tests for utils ( Float / Integer conversion ). +// +// created 08-08-2013 + +package gojsonschema + +import ( + "encoding/json" + "testing" +) + +func TestCheckJsonNumber(t *testing.T) { + var testCases = []struct { + isInt bool + value json.Number + }{ + {true, "0"}, + {true, "2147483647"}, + {true, "-2147483648"}, + {true, "9223372036854775807"}, + {true, "-9223372036854775808"}, + {true, "1.0e+2"}, + {true, "1.0e+10"}, + {true, "-1.0e+2"}, + {true, "-1.0e+10"}, + {false, "1.0e-2"}, + {false, "number"}, + {false, "123number"}, + } + + for _, testCase := range testCases { + if exp, got := testCase.isInt, checkJSONInteger(testCase.value); exp != got { + t.Errorf("Expected %v, got %v for %v", exp, got, testCase.value) + } + } + +} diff --git a/third_party/opa/internal/gojsonschema/validation.go b/third_party/opa/internal/gojsonschema/validation.go new file mode 100644 index 000000000000..e33a0f3d2772 --- /dev/null +++ b/third_party/opa/internal/gojsonschema/validation.go @@ -0,0 +1,837 @@ +// Copyright 2015 xeipuuv ( https://github.com/xeipuuv ) +// +// 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. + +// author xeipuuv +// author-github https://github.com/xeipuuv +// author-mail xeipuuv@gmail.com +// +// repository-name gojsonschema +// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language. +// +// description Extends Schema and SubSchema, implements the validation phase. +// +// created 28-02-2013 + +package gojsonschema + +import ( + "encoding/json" + "math/big" + "reflect" + "regexp" + "strconv" + "strings" + "unicode/utf8" +) + +// Validate loads and validates a JSON schema +func Validate(ls JSONLoader, ld JSONLoader) (*Result, error) { + // load schema + schema, err := NewSchema(ls) + if err != nil { + return nil, err + } + return schema.Validate(ld) +} + +// Validate loads and validates a JSON document +func (v *Schema) Validate(l JSONLoader) (*Result, error) { + root, err := l.LoadJSON() + if err != nil { + return nil, err + } + return v.validateDocument(root), nil +} + +func (v *Schema) validateDocument(root any) *Result { + result := &Result{} + context := NewJSONContext(StringContextRoot, nil) + v.RootSchema.validateRecursive(v.RootSchema, root, result, context) + return result +} + +func (v *SubSchema) subValidateWithContext(document any, context *JSONContext) *Result { + result := &Result{} + v.validateRecursive(v, document, result, context) + return result +} + +// Walker function to validate the json recursively against the SubSchema +func (v *SubSchema) validateRecursive(currentSubSchema *SubSchema, currentNode any, result *Result, context *JSONContext) { + + if internalLogEnabled { + internalLog("validateRecursive %s", context.String()) + internalLog(" %v", currentNode) + } + + // Handle true/false schema as early as possible as all other fields will be nil + if currentSubSchema.pass != nil { + if !*currentSubSchema.pass { + result.addInternalError( + new(FalseError), + context, + currentNode, + ErrorDetails{}, + ) + } + return + } + + // Handle referenced schemas, returns directly when a $ref is found + if currentSubSchema.RefSchema != nil { + v.validateRecursive(currentSubSchema.RefSchema, currentNode, result, context) + return + } + + // Check for null value + if currentNode == nil { + if currentSubSchema.Types.IsTyped() && !currentSubSchema.Types.Contains(TypeNull) { + result.addInternalError( + new(InvalidTypeError), + context, + currentNode, + ErrorDetails{ + "expected": currentSubSchema.Types.String(), + "given": TypeNull, + }, + ) + return + } + + currentSubSchema.validateSchema(currentSubSchema, currentNode, result, context) + v.validateCommon(currentSubSchema, currentNode, result, context) + + } else { // Not a null value + + if value, isNumber := currentNode.(json.Number); isNumber { + isInt := checkJSONInteger(value) + + validType := currentSubSchema.Types.Contains(TypeNumber) || (isInt && currentSubSchema.Types.Contains(TypeInteger)) + + if currentSubSchema.Types.IsTyped() && !validType { + + givenType := TypeInteger + if !isInt { + givenType = TypeNumber + } + + result.addInternalError( + new(InvalidTypeError), + context, + currentNode, + ErrorDetails{ + "expected": currentSubSchema.Types.String(), + "given": givenType, + }, + ) + return + } + + currentSubSchema.validateSchema(currentSubSchema, value, result, context) + v.validateNumber(currentSubSchema, value, result, context) + v.validateCommon(currentSubSchema, value, result, context) + v.validateString(currentSubSchema, value, result, context) + + } else { + + rValue := reflect.ValueOf(currentNode) + rKind := rValue.Kind() + + switch rKind { + + // Slice => JSON array + + case reflect.Slice: + + if currentSubSchema.Types.IsTyped() && !currentSubSchema.Types.Contains(TypeArray) { + result.addInternalError( + new(InvalidTypeError), + context, + currentNode, + ErrorDetails{ + "expected": currentSubSchema.Types.String(), + "given": TypeArray, + }, + ) + return + } + + castCurrentNode := currentNode.([]any) + + currentSubSchema.validateSchema(currentSubSchema, castCurrentNode, result, context) + + v.validateArray(currentSubSchema, castCurrentNode, result, context) + v.validateCommon(currentSubSchema, castCurrentNode, result, context) + + // Map => JSON object + + case reflect.Map: + if currentSubSchema.Types.IsTyped() && !currentSubSchema.Types.Contains(TypeObject) { + result.addInternalError( + new(InvalidTypeError), + context, + currentNode, + ErrorDetails{ + "expected": currentSubSchema.Types.String(), + "given": TypeObject, + }, + ) + return + } + + castCurrentNode, ok := currentNode.(map[string]any) + if !ok { + castCurrentNode = convertDocumentNode(currentNode).(map[string]any) + } + + currentSubSchema.validateSchema(currentSubSchema, castCurrentNode, result, context) + + v.validateObject(currentSubSchema, castCurrentNode, result, context) + v.validateCommon(currentSubSchema, castCurrentNode, result, context) + + for _, pSchema := range currentSubSchema.PropertiesChildren { + nextNode, ok := castCurrentNode[pSchema.Property] + if ok { + subContext := NewJSONContext(pSchema.Property, context) + v.validateRecursive(pSchema, nextNode, result, subContext) + } + } + + // Simple JSON values : string, number, boolean + + case reflect.Bool: + + if currentSubSchema.Types.IsTyped() && !currentSubSchema.Types.Contains(TypeBoolean) { + result.addInternalError( + new(InvalidTypeError), + context, + currentNode, + ErrorDetails{ + "expected": currentSubSchema.Types.String(), + "given": TypeBoolean, + }, + ) + return + } + + value := currentNode.(bool) + + currentSubSchema.validateSchema(currentSubSchema, value, result, context) + v.validateNumber(currentSubSchema, value, result, context) + v.validateCommon(currentSubSchema, value, result, context) + v.validateString(currentSubSchema, value, result, context) + + case reflect.String: + + if currentSubSchema.Types.IsTyped() && !currentSubSchema.Types.Contains(TypeString) { + result.addInternalError( + new(InvalidTypeError), + context, + currentNode, + ErrorDetails{ + "expected": currentSubSchema.Types.String(), + "given": TypeString, + }, + ) + return + } + + value := currentNode.(string) + + currentSubSchema.validateSchema(currentSubSchema, value, result, context) + v.validateNumber(currentSubSchema, value, result, context) + v.validateCommon(currentSubSchema, value, result, context) + v.validateString(currentSubSchema, value, result, context) + + } + + } + + } + + result.incrementScore() +} + +// Different kinds of validation there, SubSchema / common / array / object / string... +func (v *SubSchema) validateSchema(currentSubSchema *SubSchema, currentNode any, result *Result, context *JSONContext) { + + if internalLogEnabled { + internalLog("validateSchema %s", context.String()) + internalLog(" %v", currentNode) + } + + if len(currentSubSchema.AnyOf) > 0 { + + validatedAnyOf := false + var bestValidationResult *Result + + for _, anyOfSchema := range currentSubSchema.AnyOf { + if !validatedAnyOf { + validationResult := anyOfSchema.subValidateWithContext(currentNode, context) + validatedAnyOf = validationResult.Valid() + + if !validatedAnyOf && (bestValidationResult == nil || validationResult.score > bestValidationResult.score) { + bestValidationResult = validationResult + } + } + } + if !validatedAnyOf { + + result.addInternalError(new(NumberAnyOfError), context, currentNode, ErrorDetails{}) + + if bestValidationResult != nil { + // add error messages of closest matching SubSchema as + // that's probably the one the user was trying to match + result.mergeErrors(bestValidationResult) + } + } + } + + if len(currentSubSchema.oneOf) > 0 { + + nbValidated := 0 + var bestValidationResult *Result + + for _, oneOfSchema := range currentSubSchema.oneOf { + validationResult := oneOfSchema.subValidateWithContext(currentNode, context) + if validationResult.Valid() { + nbValidated++ + } else if nbValidated == 0 && (bestValidationResult == nil || validationResult.score > bestValidationResult.score) { + bestValidationResult = validationResult + } + } + + if nbValidated != 1 { + + result.addInternalError(new(NumberOneOfError), context, currentNode, ErrorDetails{}) + + if nbValidated == 0 { + // add error messages of closest matching SubSchema as + // that's probably the one the user was trying to match + result.mergeErrors(bestValidationResult) + } + } + + } + + if len(currentSubSchema.AllOf) > 0 { + nbValidated := 0 + + for _, allOfSchema := range currentSubSchema.AllOf { + validationResult := allOfSchema.subValidateWithContext(currentNode, context) + if validationResult.Valid() { + nbValidated++ + } + result.mergeErrors(validationResult) + } + + if nbValidated != len(currentSubSchema.AllOf) { + result.addInternalError(new(NumberAllOfError), context, currentNode, ErrorDetails{}) + } + } + + if currentSubSchema.not != nil { + validationResult := currentSubSchema.not.subValidateWithContext(currentNode, context) + if validationResult.Valid() { + result.addInternalError(new(NumberNotError), context, currentNode, ErrorDetails{}) + } + } + + if len(currentSubSchema.dependencies) > 0 { + if currentNodeMap, ok := currentNode.(map[string]any); ok { + for elementKey := range currentNodeMap { + if dependency, ok := currentSubSchema.dependencies[elementKey]; ok { + switch dependency := dependency.(type) { + + case []string: + for _, dependOnKey := range dependency { + if _, dependencyResolved := currentNode.(map[string]any)[dependOnKey]; !dependencyResolved { + result.addInternalError( + new(MissingDependencyError), + context, + currentNode, + ErrorDetails{"dependency": dependOnKey}, + ) + } + } + + case *SubSchema: + dependency.validateRecursive(dependency, currentNode, result, context) + } + } + } + } + } + + if currentSubSchema._if != nil { + validationResultIf := currentSubSchema._if.subValidateWithContext(currentNode, context) + if currentSubSchema._then != nil && validationResultIf.Valid() { + validationResultThen := currentSubSchema._then.subValidateWithContext(currentNode, context) + if !validationResultThen.Valid() { + result.addInternalError(new(ConditionThenError), context, currentNode, ErrorDetails{}) + result.mergeErrors(validationResultThen) + } + } + if currentSubSchema._else != nil && !validationResultIf.Valid() { + validationResultElse := currentSubSchema._else.subValidateWithContext(currentNode, context) + if !validationResultElse.Valid() { + result.addInternalError(new(ConditionElseError), context, currentNode, ErrorDetails{}) + result.mergeErrors(validationResultElse) + } + } + } + + result.incrementScore() +} + +func (v *SubSchema) validateCommon(currentSubSchema *SubSchema, value any, result *Result, context *JSONContext) { + + if internalLogEnabled { + internalLog("validateCommon %s", context.String()) + internalLog(" %v", value) + } + + // const: + if currentSubSchema._const != nil { + vString, err := marshalWithoutNumber(value) + if err != nil { + result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err}) + } + if *vString != *currentSubSchema._const { + result.addInternalError(new(ConstError), + context, + value, + ErrorDetails{ + "allowed": *currentSubSchema._const, + }, + ) + } + } + + // enum: + if len(currentSubSchema.enum) > 0 { + vString, err := marshalWithoutNumber(value) + if err != nil { + result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err}) + } + if !isStringInSlice(currentSubSchema.enum, *vString) { + result.addInternalError( + new(EnumError), + context, + value, + ErrorDetails{ + "allowed": strings.Join(currentSubSchema.enum, ", "), + }, + ) + } + } + + // format: + if currentSubSchema.format != "" { + if !FormatCheckers.IsFormat(currentSubSchema.format, value) { + result.addInternalError( + new(DoesNotMatchFormatError), + context, + value, + ErrorDetails{"format": currentSubSchema.format}, + ) + } + } + + result.incrementScore() +} + +func (v *SubSchema) validateArray(currentSubSchema *SubSchema, value []any, result *Result, context *JSONContext) { + + if internalLogEnabled { + internalLog("validateArray %s", context.String()) + internalLog(" %v", value) + } + + nbValues := len(value) + + // TODO explain + if currentSubSchema.ItemsChildrenIsSingleSchema { + for i := range value { + subContext := NewJSONContext(strconv.Itoa(i), context) + validationResult := currentSubSchema.ItemsChildren[0].subValidateWithContext(value[i], subContext) + result.mergeErrors(validationResult) + } + } else { + if len(currentSubSchema.ItemsChildren) > 0 { + + nbItems := len(currentSubSchema.ItemsChildren) + + // while we have both schemas and values, check them against each other + for i := 0; i != nbItems && i != nbValues; i++ { + subContext := NewJSONContext(strconv.Itoa(i), context) + validationResult := currentSubSchema.ItemsChildren[i].subValidateWithContext(value[i], subContext) + result.mergeErrors(validationResult) + } + + if nbItems < nbValues { + // we have less schemas than elements in the instance array, + // but that might be ok if "additionalItems" is specified. + + switch currentSubSchema.additionalItems.(type) { + case bool: + if !currentSubSchema.additionalItems.(bool) { + result.addInternalError(new(ArrayNoAdditionalItemsError), context, value, ErrorDetails{}) + } + case *SubSchema: + additionalItemSchema := currentSubSchema.additionalItems.(*SubSchema) + for i := nbItems; i != nbValues; i++ { + subContext := NewJSONContext(strconv.Itoa(i), context) + validationResult := additionalItemSchema.subValidateWithContext(value[i], subContext) + result.mergeErrors(validationResult) + } + } + } + } + } + + // minItems & maxItems + if currentSubSchema.minItems != nil { + if nbValues < *currentSubSchema.minItems { + result.addInternalError( + new(ArrayMinItemsError), + context, + value, + ErrorDetails{"min": *currentSubSchema.minItems}, + ) + } + } + if currentSubSchema.maxItems != nil { + if nbValues > *currentSubSchema.maxItems { + result.addInternalError( + new(ArrayMaxItemsError), + context, + value, + ErrorDetails{"max": *currentSubSchema.maxItems}, + ) + } + } + + // uniqueItems: + if currentSubSchema.uniqueItems { + var stringifiedItems = make(map[string]int) + for j, v := range value { + vString, err := marshalWithoutNumber(v) + if err != nil { + result.addInternalError(new(InternalError), context, value, ErrorDetails{"err": err}) + } + if i, ok := stringifiedItems[*vString]; ok { + result.addInternalError( + new(ItemsMustBeUniqueError), + context, + value, + ErrorDetails{"type": TypeArray, "i": i, "j": j}, + ) + } + stringifiedItems[*vString] = j + } + } + + // contains: + + if currentSubSchema.contains != nil { + validatedOne := false + var bestValidationResult *Result + + for i, v := range value { + subContext := NewJSONContext(strconv.Itoa(i), context) + + validationResult := currentSubSchema.contains.subValidateWithContext(v, subContext) + if validationResult.Valid() { + validatedOne = true + break + } + + if bestValidationResult == nil || validationResult.score > bestValidationResult.score { + bestValidationResult = validationResult + } + } + if !validatedOne { + result.addInternalError( + new(ArrayContainsError), + context, + value, + ErrorDetails{}, + ) + if bestValidationResult != nil { + result.mergeErrors(bestValidationResult) + } + } + } + + result.incrementScore() +} + +func (v *SubSchema) validateObject(currentSubSchema *SubSchema, value map[string]any, result *Result, context *JSONContext) { + + if internalLogEnabled { + internalLog("validateObject %s", context.String()) + internalLog(" %v", value) + } + + // minProperties & maxProperties: + if currentSubSchema.minProperties != nil { + if len(value) < *currentSubSchema.minProperties { + result.addInternalError( + new(ArrayMinPropertiesError), + context, + value, + ErrorDetails{"min": *currentSubSchema.minProperties}, + ) + } + } + if currentSubSchema.maxProperties != nil { + if len(value) > *currentSubSchema.maxProperties { + result.addInternalError( + new(ArrayMaxPropertiesError), + context, + value, + ErrorDetails{"max": *currentSubSchema.maxProperties}, + ) + } + } + + // required: + for _, requiredProperty := range currentSubSchema.required { + _, ok := value[requiredProperty] + if ok { + result.incrementScore() + } else { + result.addInternalError( + new(RequiredError), + context, + value, + ErrorDetails{"property": requiredProperty}, + ) + } + } + + // additionalProperty & patternProperty: + for pk := range value { + + // Check whether this property is described by "properties" + found := false + for _, spValue := range currentSubSchema.PropertiesChildren { + if pk == spValue.Property { + found = true + } + } + + // Check whether this property is described by "patternProperties" + ppMatch := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context) + + // If it is not described by neither "properties" nor "patternProperties" it must pass "additionalProperties" + if !found && !ppMatch { + switch ap := currentSubSchema.additionalProperties.(type) { + case bool: + // Handle the boolean case separately as it's cleaner to return a specific error than failing to pass the false schema + if !ap { + result.addInternalError( + new(AdditionalPropertyNotAllowedError), + context, + value[pk], + ErrorDetails{"property": pk}, + ) + + } + case *SubSchema: + validationResult := ap.subValidateWithContext(value[pk], NewJSONContext(pk, context)) + result.mergeErrors(validationResult) + } + } + } + + // propertyNames: + if currentSubSchema.propertyNames != nil { + for pk := range value { + validationResult := currentSubSchema.propertyNames.subValidateWithContext(pk, context) + if !validationResult.Valid() { + result.addInternalError(new(InvalidPropertyNameError), + context, + value, ErrorDetails{ + "property": pk, + }) + result.mergeErrors(validationResult) + } + } + } + + result.incrementScore() +} + +func (v *SubSchema) validatePatternProperty(currentSubSchema *SubSchema, key string, value any, result *Result, context *JSONContext) bool { + + if internalLogEnabled { + internalLog("validatePatternProperty %s", context.String()) + internalLog(" %s %v", key, value) + } + + validated := false + + for pk, pv := range currentSubSchema.patternProperties { + if matches, _ := regexp.MatchString(pk, key); matches { + validated = true + subContext := NewJSONContext(key, context) + validationResult := pv.subValidateWithContext(value, subContext) + result.mergeErrors(validationResult) + } + } + + if !validated { + return false + } + + result.incrementScore() + return true +} + +func (v *SubSchema) validateString(currentSubSchema *SubSchema, value any, result *Result, context *JSONContext) { + + // Ignore JSON numbers + stringValue, isString := value.(string) + if !isString { + return + } + + if internalLogEnabled { + internalLog("validateString %s", context.String()) + internalLog(" %v", value) + } + + // minLength & maxLength: + if currentSubSchema.minLength != nil { + if utf8.RuneCountInString(stringValue) < *currentSubSchema.minLength { + result.addInternalError( + new(StringLengthGTEError), + context, + value, + ErrorDetails{"min": *currentSubSchema.minLength}, + ) + } + } + if currentSubSchema.maxLength != nil { + if utf8.RuneCountInString(stringValue) > *currentSubSchema.maxLength { + result.addInternalError( + new(StringLengthLTEError), + context, + value, + ErrorDetails{"max": *currentSubSchema.maxLength}, + ) + } + } + + // pattern: + if currentSubSchema.pattern != nil { + if !currentSubSchema.pattern.MatchString(stringValue) { + result.addInternalError( + new(DoesNotMatchPatternError), + context, + value, + ErrorDetails{"pattern": currentSubSchema.pattern}, + ) + + } + } + + result.incrementScore() +} + +func (v *SubSchema) validateNumber(currentSubSchema *SubSchema, value any, result *Result, context *JSONContext) { + + // Ignore non numbers + number, isNumber := value.(json.Number) + if !isNumber { + return + } + + if internalLogEnabled { + internalLog("validateNumber %s", context.String()) + internalLog(" %v", value) + } + + float64Value, _ := new(big.Rat).SetString(string(number)) + + // multipleOf: + if currentSubSchema.multipleOf != nil { + if q := new(big.Rat).Quo(float64Value, currentSubSchema.multipleOf); !q.IsInt() { + result.addInternalError( + new(MultipleOfError), + context, + number, + ErrorDetails{ + "multiple": new(big.Float).SetRat(currentSubSchema.multipleOf), + }, + ) + } + } + + //maximum & exclusiveMaximum: + if currentSubSchema.maximum != nil { + if float64Value.Cmp(currentSubSchema.maximum) == 1 { + result.addInternalError( + new(NumberLTEError), + context, + number, + ErrorDetails{ + "max": new(big.Float).SetRat(currentSubSchema.maximum), + }, + ) + } + } + if currentSubSchema.exclusiveMaximum != nil { + if float64Value.Cmp(currentSubSchema.exclusiveMaximum) >= 0 { + result.addInternalError( + new(NumberLTError), + context, + number, + ErrorDetails{ + "max": new(big.Float).SetRat(currentSubSchema.exclusiveMaximum), + }, + ) + } + } + + //minimum & exclusiveMinimum: + if currentSubSchema.minimum != nil { + if float64Value.Cmp(currentSubSchema.minimum) == -1 { + result.addInternalError( + new(NumberGTEError), + context, + number, + ErrorDetails{ + "min": new(big.Float).SetRat(currentSubSchema.minimum), + }, + ) + } + } + if currentSubSchema.exclusiveMinimum != nil { + if float64Value.Cmp(currentSubSchema.exclusiveMinimum) <= 0 { + result.addInternalError( + new(NumberGTError), + context, + number, + ErrorDetails{ + "min": new(big.Float).SetRat(currentSubSchema.exclusiveMinimum), + }, + ) + } + } + + result.incrementScore() +} diff --git a/third_party/opa/internal/json/patch/patch.go b/third_party/opa/internal/json/patch/patch.go new file mode 100644 index 000000000000..9ddb93506efb --- /dev/null +++ b/third_party/opa/internal/json/patch/patch.go @@ -0,0 +1,45 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package patch + +import ( + "strings" + + "github.com/open-policy-agent/opa/v1/storage" +) + +// ParsePatchPathEscaped returns a new path for the given escaped str. +// This is based on storage.ParsePathEscaped so will do URL unescaping of +// the provided str for backwards compatibility, but also handles the +// specific escape strings defined in RFC 6901 (JSON Pointer) because +// that's what's mandated by RFC 6902 (JSON Patch). +func ParsePatchPathEscaped(str string) (path storage.Path, ok bool) { + path, ok = storage.ParsePathEscaped(str) + if !ok { + return + } + for i := range path { + // RFC 6902 section 4: "[The "path" member's] value is a string containing + // a JSON-Pointer value [RFC6901] that references a location within the + // target document (the "target location") where the operation is performed." + // + // RFC 6901 section 3: "Because the characters '~' (%x7E) and '/' (%x2F) + // have special meanings in JSON Pointer, '~' needs to be encoded as '~0' + // and '/' needs to be encoded as '~1' when these characters appear in a + // reference token." + + // RFC 6901 section 4: "Evaluation of each reference token begins by + // decoding any escaped character sequence. This is performed by first + // transforming any occurrence of the sequence '~1' to '/', and then + // transforming any occurrence of the sequence '~0' to '~'. By performing + // the substitutions in this order, an implementation avoids the error of + // turning '~01' first into '~1' and then into '/', which would be + // incorrect (the string '~01' correctly becomes '~1' after transformation)." + path[i] = strings.ReplaceAll(path[i], "~1", "/") + path[i] = strings.ReplaceAll(path[i], "~0", "~") + } + + return +} diff --git a/third_party/opa/internal/json/patch/patch_test.go b/third_party/opa/internal/json/patch/patch_test.go new file mode 100644 index 000000000000..bba484d86f5e --- /dev/null +++ b/third_party/opa/internal/json/patch/patch_test.go @@ -0,0 +1,87 @@ +package patch + +import ( + "slices" + "testing" + + "github.com/open-policy-agent/opa/v1/storage" +) + +func TestParsePatchPathEscaped(t *testing.T) { + tests := []struct { + note string + path string + expectedPath storage.Path + expectedOK bool + }{ + // success-path tests + { + note: "single-level", + path: "/single-level", + expectedPath: storage.Path{"single-level"}, + expectedOK: true, + }, + { + note: "multi-level", + path: "/a/multi-level/path", + expectedPath: storage.Path{"a", "multi-level", "path"}, + expectedOK: true, + }, + { + note: "end", + path: "/-", + expectedPath: storage.Path{"-"}, + expectedOK: true, + }, + { // not strictly correct but included for backwards compatibility with existing OPA + note: "url-escaped forward slash", + path: "/github.com%2Fopen-policy-agent", + expectedPath: storage.Path{"github.com/open-policy-agent"}, + expectedOK: true, + }, + { + note: "json-pointer-escaped forward slash", + path: "/github.com~1open-policy-agent", + expectedPath: storage.Path{"github.com/open-policy-agent"}, + expectedOK: true, + }, + { + note: "json-pointer-escaped tilde", + path: "/~0opa", + expectedPath: storage.Path{"~opa"}, + expectedOK: true, + }, + { + note: "json-pointer-escape correctness", + path: "/~01", + expectedPath: storage.Path{"~1"}, + expectedOK: true, + }, + + // failure-path tests + { // not possible with existing callers but for completeness... + note: "empty string", + path: "", + expectedOK: false, + }, + { // not possible with existing callers but for completeness... + note: "string that doesn't start with /", + path: "foo", + expectedOK: false, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + actualPath, actualOK := ParsePatchPathEscaped(tc.path) + + if tc.expectedOK != actualOK { + t.Fatalf("Expected ok to be %v but was %v", tc.expectedOK, actualOK) + } + + if !slices.Equal(tc.expectedPath, actualPath) { + t.Fatalf("Expected path to be %v but was %v", tc.expectedPath, actualPath) + } + }) + } +} diff --git a/third_party/opa/internal/jwx/.gitignore b/third_party/opa/internal/jwx/.gitignore new file mode 100644 index 000000000000..3057b6deee9c --- /dev/null +++ b/third_party/opa/internal/jwx/.gitignore @@ -0,0 +1,31 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +# IDE +.idea +.vscode +.DS_Store + +coverage.out diff --git a/third_party/opa/internal/jwx/LICENSE b/third_party/opa/internal/jwx/LICENSE new file mode 100644 index 000000000000..6369f4fcc404 --- /dev/null +++ b/third_party/opa/internal/jwx/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 lestrrat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/opa/internal/jwx/Makefile b/third_party/opa/internal/jwx/Makefile new file mode 100644 index 000000000000..f84b0f94520e --- /dev/null +++ b/third_party/opa/internal/jwx/Makefile @@ -0,0 +1,10 @@ +.PHONY: realclean cover viewcover + +realclean: + rm coverage.out + +cover: + go test -v -coverpkg=./... -coverprofile=coverage.out ./... + +viewcover: + go tool cover -html=coverage.out diff --git a/third_party/opa/internal/jwx/buffer/buffer.go b/third_party/opa/internal/jwx/buffer/buffer.go new file mode 100644 index 000000000000..c383ff3b5498 --- /dev/null +++ b/third_party/opa/internal/jwx/buffer/buffer.go @@ -0,0 +1,112 @@ +// Package buffer provides a very thin wrapper around []byte buffer called +// `Buffer`, to provide functionalities that are often used within the jwx +// related packages +package buffer + +import ( + "encoding/base64" + "encoding/binary" + "encoding/json" + "fmt" +) + +// Buffer wraps `[]byte` and provides functions that are often used in +// the jwx related packages. One notable difference is that while +// encoding/json marshalls `[]byte` using base64.StdEncoding, this +// module uses base64.RawURLEncoding as mandated by the spec +type Buffer []byte + +// FromUint creates a `Buffer` from an unsigned int +func FromUint(v uint64) Buffer { + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, v) + + i := 0 + for ; i < len(data); i++ { + if data[i] != 0x0 { + break + } + } + return Buffer(data[i:]) +} + +// FromBase64 constructs a new Buffer from a base64 encoded data +func FromBase64(v []byte) (Buffer, error) { + b := Buffer{} + if err := b.Base64Decode(v); err != nil { + return Buffer(nil), fmt.Errorf("failed to decode from base64: %w", err) + } + + return b, nil +} + +// FromNData constructs a new Buffer from a "n:data" format +// (I made that name up) +func FromNData(v []byte) (Buffer, error) { + size := binary.BigEndian.Uint32(v) + buf := make([]byte, int(size)) + copy(buf, v[4:4+size]) + return Buffer(buf), nil +} + +// Bytes returns the raw bytes that comprises the Buffer +func (b Buffer) Bytes() []byte { + return []byte(b) +} + +// NData returns Datalen || Data, where Datalen is a 32 bit counter for +// the length of the following data, and Data is the octets that comprise +// the buffer data +func (b Buffer) NData() []byte { + buf := make([]byte, 4+b.Len()) + binary.BigEndian.PutUint32(buf, uint32(b.Len())) + + copy(buf[4:], b.Bytes()) + return buf +} + +// Len returns the number of bytes that the Buffer holds +func (b Buffer) Len() int { + return len(b) +} + +// Base64Encode encodes the contents of the Buffer using base64.RawURLEncoding +func (b Buffer) Base64Encode() ([]byte, error) { + enc := base64.RawURLEncoding + out := make([]byte, enc.EncodedLen(len(b))) + enc.Encode(out, b) + return out, nil +} + +// Base64Decode decodes the contents of the Buffer using base64.RawURLEncoding +func (b *Buffer) Base64Decode(v []byte) error { + enc := base64.RawURLEncoding + out := make([]byte, enc.DecodedLen(len(v))) + n, err := enc.Decode(out, v) + if err != nil { + return fmt.Errorf("failed to decode from base64: %w", err) + } + out = out[:n] + *b = Buffer(out) + return nil +} + +// MarshalJSON marshals the buffer into JSON format after encoding the buffer +// with base64.RawURLEncoding +func (b Buffer) MarshalJSON() ([]byte, error) { + v, err := b.Base64Encode() + if err != nil { + return nil, fmt.Errorf("failed to encode to base64: %w", err) + } + return json.Marshal(string(v)) +} + +// UnmarshalJSON unmarshals from a JSON string into a Buffer, after decoding it +// with base64.RawURLEncoding +func (b *Buffer) UnmarshalJSON(data []byte) error { + var x string + if err := json.Unmarshal(data, &x); err != nil { + return fmt.Errorf("failed to unmarshal JSON: %w", err) + } + return b.Base64Decode([]byte(x)) +} diff --git a/third_party/opa/internal/jwx/buffer/buffer_test.go b/third_party/opa/internal/jwx/buffer/buffer_test.go new file mode 100644 index 000000000000..ca1bfae504a7 --- /dev/null +++ b/third_party/opa/internal/jwx/buffer/buffer_test.go @@ -0,0 +1,89 @@ +package buffer + +import ( + "bytes" + "encoding/json" + "testing" +) + +func TestBuffer_FromUint(t *testing.T) { + b := FromUint(1) + if !bytes.Equal([]byte{1}, b.Bytes()) { + t.Fatal("mismatched buffer values") + } +} + +func TestBuffer_Convert(t *testing.T) { + v1 := []byte{'a', 'b', 'c'} + b := Buffer(v1) + if !bytes.Equal(v1, b.Bytes()) { + t.Fatal("mismatched buffer values") + } + + v2 := "abc" + b = Buffer(v2) + if !bytes.Equal([]byte(v2), b.Bytes()) { + t.Fatal("mismatched buffer values") + } +} + +func TestBuffer_Base64Encode(t *testing.T) { + b := Buffer{'a', 'b', 'c'} + v, err := b.Base64Encode() + if err != nil { + t.Fatal("failed to base64 encode") + } + if !bytes.Equal([]byte{'Y', 'W', 'J', 'j'}, v) { + t.Fatal("mismatched buffer values") + } +} + +func TestJSON(t *testing.T) { + b1 := Buffer{'a', 'b', 'c'} + + jsontxt, err := json.Marshal(b1) + if err != nil { + t.Fatal("failed to marshal buffer") + } + if `"YWJj"` != string(jsontxt) { + t.Fatal("mismatched json values") + } + + var b2 Buffer + err = json.Unmarshal(jsontxt, &b2) + if err != nil { + t.Fatal("failed to marshal buffer") + } + + if !bytes.Equal(b1, b2) { + t.Fatal("mismatched buffer values") + } +} + +func TestFunky(t *testing.T) { + s := `QD4_B3ghg0PNu-c_EAlXn3Xlb0gzAFPJSYQSI1cZZ8sPIxISgPMtNJTzgncC281IaKDXLV1aEnYuH5eH-4u4f383zlyBCGKSKSQWmqKNE7xcIqleFVNsfzOucTL4QRxfbcyHcli_symC_RGWJ6GdocE0VgyYN8t9_0sm_Nq5lcwtYEQs_hNlf1ileCjjdsUfC05zTbbrLpMjgI3IK5_QxOU81FLei4LMx3iQ1kqrIGH5FxxQMKGdx_fDaRQ-YBAA2YVqn7rs3TcwQ7NUjjz8JyDE168NlMV1WxoDC9nwOe0O6K4NzFuWpoGHTh0M-0lT5M3dy9kEBYgPtWoe_u9dogA` + b := Buffer{} + err := b.Base64Decode([]byte(s)) + if err != nil { + t.Fatal("failed to base64 decode") + } + if 257 != b.Len() { + t.Fatal("Mismatched buffer lengths") + } +} + +func TestBuffer_NData(t *testing.T) { + payload := []byte("Alice") + nd := Buffer(payload).NData() + if !bytes.Equal([]byte{0, 0, 0, 5, 65, 108, 105, 99, 101}, nd) { + t.Fatal("mismatched byte buffer values") + } + + b1, err := FromNData(nd) + if err != nil { + t.Fatal("failed to extract data") + } + if !bytes.Equal(payload, b1.Bytes()) { + t.Fatal("mismatched byte values ") + } +} diff --git a/third_party/opa/internal/jwx/jwa/elliptic.go b/third_party/opa/internal/jwx/jwa/elliptic.go new file mode 100644 index 000000000000..b7e35dc707bf --- /dev/null +++ b/third_party/opa/internal/jwx/jwa/elliptic.go @@ -0,0 +1,11 @@ +package jwa + +// EllipticCurveAlgorithm represents the algorithms used for EC keys +type EllipticCurveAlgorithm string + +// Supported values for EllipticCurveAlgorithm +const ( + P256 EllipticCurveAlgorithm = "P-256" + P384 EllipticCurveAlgorithm = "P-384" + P521 EllipticCurveAlgorithm = "P-521" +) diff --git a/third_party/opa/internal/jwx/jwa/key_type.go b/third_party/opa/internal/jwx/jwa/key_type.go new file mode 100644 index 000000000000..61d23844a175 --- /dev/null +++ b/third_party/opa/internal/jwx/jwa/key_type.go @@ -0,0 +1,67 @@ +package jwa + +import ( + "errors" + "fmt" + "strconv" +) + +// KeyType represents the key type ("kty") that are supported +type KeyType string + +var keyTypeAlg = map[string]struct{}{"EC": {}, "oct": {}, "RSA": {}} + +// Supported values for KeyType +const ( + EC KeyType = "EC" // Elliptic Curve + InvalidKeyType KeyType = "" // Invalid KeyType + OctetSeq KeyType = "oct" // Octet sequence (used to represent symmetric keys) + RSA KeyType = "RSA" // RSA +) + +// Accept is used when conversion from values given by +// outside sources (such as JSON payloads) is required +func (keyType *KeyType) Accept(value any) error { + var tmp KeyType + switch x := value.(type) { + case string: + tmp = KeyType(x) + case KeyType: + tmp = x + default: + return fmt.Errorf("invalid type for jwa.KeyType: %T", value) + } + _, ok := keyTypeAlg[tmp.String()] + if !ok { + return errors.New("unknown Key Type algorithm") + } + + *keyType = tmp + return nil +} + +// String returns the string representation of a KeyType +func (keyType KeyType) String() string { + return string(keyType) +} + +// UnmarshalJSON unmarshals and checks data as KeyType Algorithm +func (keyType *KeyType) UnmarshalJSON(data []byte) error { + var quote byte = '"' + var quoted string + if data[0] == quote { + var err error + quoted, err = strconv.Unquote(string(data)) + if err != nil { + return fmt.Errorf("failed to process signature algorithm: %w", err) + } + } else { + quoted = string(data) + } + _, ok := keyTypeAlg[quoted] + if !ok { + return errors.New("unknown signature algorithm") + } + *keyType = KeyType(quoted) + return nil +} diff --git a/third_party/opa/internal/jwx/jwa/parameters.go b/third_party/opa/internal/jwx/jwa/parameters.go new file mode 100644 index 000000000000..2fe72e1dbcc6 --- /dev/null +++ b/third_party/opa/internal/jwx/jwa/parameters.go @@ -0,0 +1,29 @@ +package jwa + +import ( + "crypto/elliptic" + + "github.com/open-policy-agent/opa/internal/jwx/buffer" +) + +// EllipticCurve provides a indirect type to standard elliptic curve such that we can +// use it for unmarshal +type EllipticCurve struct { + elliptic.Curve +} + +// AlgorithmParameters provides a single structure suitable to unmarshaling any JWK +type AlgorithmParameters struct { + N buffer.Buffer `json:"n,omitempty"` + E buffer.Buffer `json:"e,omitempty"` + D buffer.Buffer `json:"d,omitempty"` + P buffer.Buffer `json:"p,omitempty"` + Q buffer.Buffer `json:"q,omitempty"` + Dp buffer.Buffer `json:"dp,omitempty"` + Dq buffer.Buffer `json:"dq,omitempty"` + Qi buffer.Buffer `json:"qi,omitempty"` + Crv EllipticCurveAlgorithm `json:"crv,omitempty"` + X buffer.Buffer `json:"x,omitempty"` + Y buffer.Buffer `json:"y,omitempty"` + K buffer.Buffer `json:"k,omitempty"` +} diff --git a/third_party/opa/internal/jwx/jwa/signature.go b/third_party/opa/internal/jwx/jwa/signature.go new file mode 100644 index 000000000000..c601c46ea9aa --- /dev/null +++ b/third_party/opa/internal/jwx/jwa/signature.go @@ -0,0 +1,78 @@ +package jwa + +import ( + "errors" + "fmt" + "strconv" +) + +// SignatureAlgorithm represents the various signature algorithms as described in https://tools.ietf.org/html/rfc7518#section-3.1 +type SignatureAlgorithm string + +var signatureAlg = map[string]struct{}{"ES256": {}, "ES384": {}, "ES512": {}, "HS256": {}, "HS384": {}, "HS512": {}, "PS256": {}, "PS384": {}, "PS512": {}, "RS256": {}, "RS384": {}, "RS512": {}, "none": {}} + +// Supported values for SignatureAlgorithm +const ( + ES256 SignatureAlgorithm = "ES256" // ECDSA using P-256 and SHA-256 + ES384 SignatureAlgorithm = "ES384" // ECDSA using P-384 and SHA-384 + ES512 SignatureAlgorithm = "ES512" // ECDSA using P-521 and SHA-512 + HS256 SignatureAlgorithm = "HS256" // HMAC using SHA-256 + HS384 SignatureAlgorithm = "HS384" // HMAC using SHA-384 + HS512 SignatureAlgorithm = "HS512" // HMAC using SHA-512 + NoSignature SignatureAlgorithm = "none" + PS256 SignatureAlgorithm = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256 + PS384 SignatureAlgorithm = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384 + PS512 SignatureAlgorithm = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512 + RS256 SignatureAlgorithm = "RS256" // RSASSA-PKCS-v1.5 using SHA-256 + RS384 SignatureAlgorithm = "RS384" // RSASSA-PKCS-v1.5 using SHA-384 + RS512 SignatureAlgorithm = "RS512" // RSASSA-PKCS-v1.5 using SHA-512 + NoValue SignatureAlgorithm = "" // No value is different from none + Unsupported SignatureAlgorithm = "unsupported" +) + +// Accept is used when conversion from values given by +// outside sources (such as JSON payloads) is required +func (signature *SignatureAlgorithm) Accept(value any) error { + var tmp SignatureAlgorithm + switch x := value.(type) { + case string: + tmp = SignatureAlgorithm(x) + case SignatureAlgorithm: + tmp = x + default: + return fmt.Errorf("invalid type for jwa.SignatureAlgorithm: %T", value) + } + _, ok := signatureAlg[tmp.String()] + if !ok { + return errors.New("unknown signature algorithm") + } + *signature = tmp + return nil +} + +// String returns the string representation of a SignatureAlgorithm +func (signature SignatureAlgorithm) String() string { + return string(signature) +} + +// UnmarshalJSON unmarshals and checks data as Signature Algorithm +func (signature *SignatureAlgorithm) UnmarshalJSON(data []byte) error { + var quote byte = '"' + var quoted string + if data[0] == quote { + var err error + quoted, err = strconv.Unquote(string(data)) + if err != nil { + return fmt.Errorf("failed to process signature algorithm: %w", err) + } + } else { + quoted = string(data) + } + _, ok := signatureAlg[quoted] + if !ok { + *signature = Unsupported + return nil + } + *signature = SignatureAlgorithm(quoted) + return nil +} diff --git a/third_party/opa/internal/jwx/jwk/ecdsa.go b/third_party/opa/internal/jwx/jwk/ecdsa.go new file mode 100644 index 000000000000..0677f4dc30ff --- /dev/null +++ b/third_party/opa/internal/jwx/jwk/ecdsa.go @@ -0,0 +1,120 @@ +package jwk + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "errors" + "fmt" + "math/big" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +func newECDSAPublicKey(key *ecdsa.PublicKey) (*ECDSAPublicKey, error) { + + var hdr StandardHeaders + err := hdr.Set(KeyTypeKey, jwa.EC) + if err != nil { + return nil, fmt.Errorf("failed to set Key Type: %w", err) + } + + return &ECDSAPublicKey{ + StandardHeaders: &hdr, + key: key, + }, nil +} + +func newECDSAPrivateKey(key *ecdsa.PrivateKey) (*ECDSAPrivateKey, error) { + + var hdr StandardHeaders + err := hdr.Set(KeyTypeKey, jwa.EC) + if err != nil { + return nil, fmt.Errorf("failed to set Key Type: %w", err) + } + + return &ECDSAPrivateKey{ + StandardHeaders: &hdr, + key: key, + }, nil +} + +// Materialize returns the EC-DSA public key represented by this JWK +func (k ECDSAPublicKey) Materialize() (any, error) { + return k.key, nil +} + +// Materialize returns the EC-DSA private key represented by this JWK +func (k ECDSAPrivateKey) Materialize() (any, error) { + return k.key, nil +} + +// GenerateKey creates a ECDSAPublicKey from JWK format +func (k *ECDSAPublicKey) GenerateKey(keyJSON *RawKeyJSON) error { + + var x, y big.Int + + if keyJSON.X == nil || keyJSON.Y == nil || keyJSON.Crv == "" { + return errors.New("missing mandatory key parameters X, Y or Crv") + } + + x.SetBytes(keyJSON.X.Bytes()) + y.SetBytes(keyJSON.Y.Bytes()) + + var curve elliptic.Curve + switch keyJSON.Crv { + case jwa.P256: + curve = elliptic.P256() + case jwa.P384: + curve = elliptic.P384() + case jwa.P521: + curve = elliptic.P521() + default: + return fmt.Errorf("invalid curve name %s", keyJSON.Crv) + } + + *k = ECDSAPublicKey{ + StandardHeaders: &keyJSON.StandardHeaders, + key: &ecdsa.PublicKey{ + Curve: curve, + X: &x, + Y: &y, + }, + } + return nil +} + +// GenerateKey creates a ECDSAPrivateKey from JWK format +func (k *ECDSAPrivateKey) GenerateKey(keyJSON *RawKeyJSON) error { + + if keyJSON.D == nil { + return errors.New("missing mandatory key parameter D") + } + eCDSAPublicKey := &ECDSAPublicKey{} + err := eCDSAPublicKey.GenerateKey(keyJSON) + if err != nil { + return fmt.Errorf("failed to generate public key: %w", err) + } + dBytes := keyJSON.D.Bytes() + // The length of this octet string MUST be ceiling(log-base-2(n)/8) + // octets (where n is the order of the curve). This is because the private + // key d must be in the interval [1, n-1] so the bitlength of d should be + // no larger than the bitlength of n-1. The easiest way to find the octet + // length is to take bitlength(n-1), add 7 to force a carry, and shift this + // bit sequence right by 3, which is essentially dividing by 8 and adding + // 1 if there is any remainder. Thus, the private key value d should be + // output to (bitlength(n-1)+7)>>3 octets. + n := eCDSAPublicKey.key.Params().N + octetLength := (new(big.Int).Sub(n, big.NewInt(1)).BitLen() + 7) >> 3 + if octetLength-len(dBytes) != 0 { + return errors.New("failed to generate private key. Incorrect D value") + } + privateKey := &ecdsa.PrivateKey{ + PublicKey: *eCDSAPublicKey.key, + D: (&big.Int{}).SetBytes(keyJSON.D.Bytes()), + } + + k.key = privateKey + k.StandardHeaders = &keyJSON.StandardHeaders + + return nil +} diff --git a/third_party/opa/internal/jwx/jwk/ecdsa_test.go b/third_party/opa/internal/jwx/jwk/ecdsa_test.go new file mode 100644 index 000000000000..3de029796301 --- /dev/null +++ b/third_party/opa/internal/jwx/jwk/ecdsa_test.go @@ -0,0 +1,248 @@ +package jwk_test + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/open-policy-agent/opa/internal/jwx/buffer" + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jwk" +) + +func TestECDSA(t *testing.T) { + + t.Run("Key Generation Errors", func(t *testing.T) { + jwkSrc := `{ + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "key_ops": [ + "verify", + "sign" + ], + "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE" + } + ] +}` + + rawKeySetJSON := &jwk.RawKeySetJSON{} + err := json.Unmarshal([]byte(jwkSrc), rawKeySetJSON) + if err != nil { + t.Fatalf("Failed to unmarshal JWK Set: %s", err.Error()) + } + if len(rawKeySetJSON.Keys) != 1 { + t.Fatalf("Failed to parse JWK Set: %v", err) + } + rawKeyJSON := rawKeySetJSON.Keys[0] + curveName := rawKeyJSON.Crv + if curveName != "P-256" { + t.Fatalf("Curve name should be P-256, not: %s ", curveName) + } + rawKeyJSON.Crv = jwa.EllipticCurveAlgorithm("dummy") + _, err = rawKeyJSON.GenerateKey() + if err == nil { + t.Fatal("Key generation should fail") + } + rawKeyJSON.Crv = jwa.P256 + rawKeyJSON.D = buffer.Buffer("1234") + _, err = rawKeyJSON.GenerateKey() + if err == nil { + t.Fatal("Key generation should fail") + } + }) + t.Run("Parse Private Key", func(t *testing.T) { + jwkSrc := `{ + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "key_ops": [ + "verify" + ], + "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE" + } + ] +}` + + var jwkSet *jwk.Set + jwkSet, err := jwk.ParseString(jwkSrc) + if err != nil { + t.Fatalf("Failed to parse key: %s", err.Error()) + } + jwkKey := jwkSet.Keys[0] + privateKey, err := jwkKey.Materialize() + if err != nil { + t.Fatalf("Failed to expose private key: %s", err.Error()) + } + if jwk.GetKeyTypeFromKey(privateKey) != jwa.EC { + t.Fatal("Wrong Key Type") + } + if _, ok := privateKey.(*ecdsa.PrivateKey); !ok { + t.Fatalf("Key type should be of type: %s", fmt.Sprintf("%T", privateKey)) + } + publicKey, err := jwk.GetPublicKey(privateKey) + if err != nil { + t.Fatalf("Failed to expose public key: %s", err.Error()) + } + if _, ok := publicKey.(*ecdsa.PublicKey); !ok { + t.Fatalf("Key type should be of type: %s", fmt.Sprintf("%T", privateKey)) + } + }) + t.Run("Initialization", func(t *testing.T) { + // Generate an ECDSA P-256 test key. + ecPrk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal("failed to generate EC P-256 key") + } + // Test initialization of a private EC JWK. + prk, err := jwk.New(ecPrk) + if err != nil { + t.Fatal("failed to create new private key") + } + err = prk.Set(jwk.KeyIDKey, "MyKey") + if err != nil { + t.Fatalf("Faild to set KeyID: %s", err.Error()) + } + if prk.GetKeyID() != "MyKey" { + t.Fatalf("KeyID should be MyKey, not: %s", prk.GetKeyID()) + } + + if prk.GetKeyType() != jwa.EC { + t.Fatalf("Key type should be %s, not: %s", jwa.EC, prk.GetKeyType()) + } + + // Test initialization of a public EC JWK. + puk, err := jwk.New(&ecPrk.PublicKey) + if err != nil { + t.Fatal("failed to create new public key") + } + + err = puk.Set(jwk.KeyIDKey, "MyKey") + if err != nil { + t.Fatalf("Faild to set KeyID: %s", err.Error()) + } + if puk.GetKeyID() != "MyKey" { + t.Fatalf("KeyID should be MyKey, not: %s", puk.GetKeyID()) + } + + if puk.GetKeyType() != jwa.EC { + t.Fatalf("Key type should be %s, not: %s", jwa.EC, puk.GetKeyType()) + } + }) + t.Run("Marshall Unmarshal Public Key", func(t *testing.T) { + jwkSrc := `{ + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "key_ops": [ + "verify" + ], + "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE" + } + ] +}` + + var jwkSet *jwk.Set + var jwkKey jwk.Key + jwkSet, err := jwk.ParseString(jwkSrc) + if err != nil { + t.Fatalf("Failed to parse key: %s", err.Error()) + } + jwkKey = jwkSet.Keys[0] + privateKey, err := jwkKey.Materialize() + if err != nil { + t.Fatalf("Failed to expose private key: %s", err.Error()) + } + if _, ok := privateKey.(*ecdsa.PrivateKey); !ok { + t.Fatalf("Key type should be of type: %s", fmt.Sprintf("%T", privateKey)) + } + publicKey, err := jwk.GetPublicKey(privateKey) + if err != nil { + t.Fatalf("Failed to expose public key: %s", err.Error()) + } + if jwk.GetKeyTypeFromKey(publicKey) != jwa.EC { + t.Fatal("Wrong Key Type") + } + if jwk.GetKeyTypeFromKey(nil) != jwa.InvalidKeyType { + t.Fatal("Key should be invalid") + } + if _, ok := publicKey.(*ecdsa.PublicKey); !ok { + t.Fatalf("Key type should be of type: %s", fmt.Sprintf("%T", privateKey)) + } + eCDSAPublicKey, err := jwk.New(publicKey) + if err != nil { + t.Fatal("Failed to create ECDSAPublicKey") + } + newPublicKey, err := eCDSAPublicKey.Materialize() + if err != nil { + t.Fatalf("Failed to materialize key: %s", err.Error()) + } + if !reflect.DeepEqual(publicKey, newPublicKey) { + t.Fatal("ECDSA Public Keys do not match") + } + }) + t.Run("Unmarshal Private Key", func(t *testing.T) { + jwkSrc := `{ + "keys": [ + { + "kty": "EC", + "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE", + "crv": "P-256", + "key_ops": [ + "verify" + ], + "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM" + } + ] +}` + + var jwkSet *jwk.Set + var jwkKey jwk.Key + jwkSet, err := jwk.ParseString(jwkSrc) + if err != nil { + t.Fatalf("Failed to parse key: %s", err.Error()) + } + jwkKey = jwkSet.Keys[0] + privateKey, err := jwkKey.Materialize() + if err != nil { + t.Fatalf("Failed to expose private key: %s", err.Error()) + } + if _, ok := privateKey.(*ecdsa.PrivateKey); !ok { + t.Fatalf("Key type should be of type: %s", fmt.Sprintf("%T", privateKey)) + } + if _, ok := jwkKey.(*jwk.ECDSAPrivateKey); !ok { + t.Fatalf("Key type should be of type: %s", fmt.Sprintf("%T", jwkKey)) + } + }) + t.Run("Invalid ECDSA Private Key", func(t *testing.T) { + const jwkSrc = `{ + "kty": "EC", + "crv": "P-256", + "y": "lf0u0pMj4lGAzZix5u4Cm5CMQIgMNpkwy163wtKYVKI", + "d": "0g5vAEKzugrXaRbgKG0Tj2qJ5lMP4Bezds1_sTybkfk" +}` + rawKeyJSON := &jwk.RawKeyJSON{} + err := json.Unmarshal([]byte(jwkSrc), rawKeyJSON) + if err != nil { + t.Fatalf("Failed to unmarshal JWK Set: %s", err.Error()) + } + _, err = rawKeyJSON.GenerateKey() + if err == nil { + t.Fatalf("Key Generation should fail") + } + }) +} diff --git a/third_party/opa/internal/jwx/jwk/headers.go b/third_party/opa/internal/jwx/jwk/headers.go new file mode 100644 index 000000000000..b1a6763dda01 --- /dev/null +++ b/third_party/opa/internal/jwx/jwk/headers.go @@ -0,0 +1,178 @@ +package jwk + +import ( + "fmt" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +// Convenience constants for common JWK parameters +const ( + AlgorithmKey = "alg" + KeyIDKey = "kid" + KeyOpsKey = "key_ops" + KeyTypeKey = "kty" + KeyUsageKey = "use" + PrivateParamsKey = "privateParams" +) + +// Headers provides a common interface to all future possible headers +type Headers interface { + Get(string) (any, bool) + Set(string, any) error + Walk(func(string, any) error) error + GetAlgorithm() jwa.SignatureAlgorithm + GetKeyID() string + GetKeyOps() KeyOperationList + GetKeyType() jwa.KeyType + GetKeyUsage() string + GetPrivateParams() map[string]any +} + +// StandardHeaders stores the common JWK parameters +type StandardHeaders struct { + Algorithm *jwa.SignatureAlgorithm `json:"alg,omitempty"` // https://tools.ietf.org/html/rfc7517#section-4.4 + KeyID string `json:"kid,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.4 + KeyOps KeyOperationList `json:"key_ops,omitempty"` // https://tools.ietf.org/html/rfc7517#section-4.3 + KeyType jwa.KeyType `json:"kty,omitempty"` // https://tools.ietf.org/html/rfc7517#section-4.1 + KeyUsage string `json:"use,omitempty"` // https://tools.ietf.org/html/rfc7517#section-4.2 + PrivateParams map[string]any `json:"privateParams,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.4 +} + +// GetAlgorithm is a convenience function to retrieve the corresponding value stored in the StandardHeaders +func (h *StandardHeaders) GetAlgorithm() jwa.SignatureAlgorithm { + if v := h.Algorithm; v != nil { + return *v + } + return jwa.NoValue +} + +// GetKeyID is a convenience function to retrieve the corresponding value stored in the StandardHeaders +func (h *StandardHeaders) GetKeyID() string { + return h.KeyID +} + +// GetKeyOps is a convenience function to retrieve the corresponding value stored in the StandardHeaders +func (h *StandardHeaders) GetKeyOps() KeyOperationList { + return h.KeyOps +} + +// GetKeyType is a convenience function to retrieve the corresponding value stored in the StandardHeaders +func (h *StandardHeaders) GetKeyType() jwa.KeyType { + return h.KeyType +} + +// GetKeyUsage is a convenience function to retrieve the corresponding value stored in the StandardHeaders +func (h *StandardHeaders) GetKeyUsage() string { + return h.KeyUsage +} + +// GetPrivateParams is a convenience function to retrieve the corresponding value stored in the StandardHeaders +func (h *StandardHeaders) GetPrivateParams() map[string]any { + return h.PrivateParams +} + +// Get is a general getter function for JWK StandardHeaders structure +func (h *StandardHeaders) Get(name string) (any, bool) { + switch name { + case AlgorithmKey: + alg := h.GetAlgorithm() + if alg != jwa.NoValue { + return alg, true + } + return nil, false + case KeyIDKey: + v := h.KeyID + if v == "" { + return nil, false + } + return v, true + case KeyOpsKey: + v := h.KeyOps + if v == nil { + return nil, false + } + return v, true + case KeyTypeKey: + v := h.KeyType + if v == jwa.InvalidKeyType { + return nil, false + } + return v, true + case KeyUsageKey: + v := h.KeyUsage + if v == "" { + return nil, false + } + return v, true + case PrivateParamsKey: + v := h.PrivateParams + if len(v) == 0 { + return nil, false + } + return v, true + default: + return nil, false + } +} + +// Set is a general getter function for JWK StandardHeaders structure +func (h *StandardHeaders) Set(name string, value any) error { + switch name { + case AlgorithmKey: + var acceptor jwa.SignatureAlgorithm + if err := acceptor.Accept(value); err != nil { + return fmt.Errorf("invalid value for %s key: %w", AlgorithmKey, err) + } + h.Algorithm = &acceptor + return nil + case KeyIDKey: + if v, ok := value.(string); ok { + h.KeyID = v + return nil + } + return fmt.Errorf("invalid value for %s key: %T", KeyIDKey, value) + case KeyOpsKey: + if err := h.KeyOps.Accept(value); err != nil { + return fmt.Errorf("invalid value for %s key: %w", KeyOpsKey, err) + } + return nil + case KeyTypeKey: + if err := h.KeyType.Accept(value); err != nil { + return fmt.Errorf("invalid value for %s key: %w", KeyTypeKey, err) + } + return nil + case KeyUsageKey: + if v, ok := value.(string); ok { + h.KeyUsage = v + return nil + } + return fmt.Errorf("invalid value for %s key: %T", KeyUsageKey, value) + case PrivateParamsKey: + if v, ok := value.(map[string]any); ok { + h.PrivateParams = v + return nil + } + return fmt.Errorf("invalid value for %s key: %T", PrivateParamsKey, value) + default: + return fmt.Errorf("invalid key: %s", name) + } +} + +// Walk iterates over all JWK standard headers fields while applying a function to its value. +func (h StandardHeaders) Walk(f func(string, any) error) error { + for _, key := range []string{AlgorithmKey, KeyIDKey, KeyOpsKey, KeyTypeKey, KeyUsageKey, PrivateParamsKey} { + if v, ok := h.Get(key); ok { + if err := f(key, v); err != nil { + return fmt.Errorf("walk function returned error for %s: %w", key, err) + } + } + } + + for k, v := range h.PrivateParams { + if err := f(k, v); err != nil { + return fmt.Errorf("walk function returned error for %s: %w", k, err) + } + } + return nil +} diff --git a/third_party/opa/internal/jwx/jwk/headers_test.go b/third_party/opa/internal/jwx/jwk/headers_test.go new file mode 100644 index 000000000000..a900ac8e6417 --- /dev/null +++ b/third_party/opa/internal/jwx/jwk/headers_test.go @@ -0,0 +1,174 @@ +package jwk_test + +import ( + "reflect" + "testing" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jwk" +) + +func TestHeader(t *testing.T) { + + privateHeaderParams := map[string]any{"one": "1", "two": "11"} + t.Run("RoundTrip", func(t *testing.T) { + values := map[string]any{ + jwk.KeyIDKey: "helloworld01", + jwk.KeyTypeKey: jwa.RSA, + jwk.KeyOpsKey: jwk.KeyOperationList{jwk.KeyOpSign}, + jwk.KeyUsageKey: "sig", + jwk.PrivateParamsKey: privateHeaderParams, + } + + var h jwk.StandardHeaders + for k, v := range values { + err := h.Set(k, v) + if err != nil { + t.Fatalf("failed to set value for: %s", k) + } + + got, ok := h.Get(k) + if !ok { + t.Fatalf("failed to get value for: %s", k) + } + + if !reflect.DeepEqual(v, got) { + t.Fatalf("mismtached values for: %s", k) + } + + err = h.Set(k, v) + if err != nil { + t.Fatalf("failed to set value for: %s", k) + } + } + }) + t.Run("RoundTripError 1", func(t *testing.T) { + + type dummyStruct struct { + dummy1 int + dummy2 float64 + } + dummy := &dummyStruct{1, 3.4} + values := map[string]any{ + jwk.AlgorithmKey: dummy, + jwk.KeyIDKey: dummy, + jwk.KeyTypeKey: dummy, + jwk.KeyUsageKey: dummy, + jwk.KeyOpsKey: dummy, + jwk.PrivateParamsKey: dummy, + "invalid key": "", + } + + var h jwk.StandardHeaders + for k, v := range values { + err := h.Set(k, v) + if err == nil { + t.Fatalf("Setting %s value should have failed", k) + } + } + if h.GetAlgorithm() != jwa.NoValue { + t.Fatalf("Algorithm should be empty string") + } + if h.GetKeyID() != "" { + t.Fatalf("KeyID should be empty string") + } + if h.GetKeyType() != "" { + t.Fatalf("KeyType should be empty string") + } + if h.GetKeyUsage() != "" { + t.Fatalf("KeyUsage should be empty string") + } + if h.GetKeyOps() != nil { + t.Fatalf("KeyOps should be empty string") + } + if h.GetPrivateParams() != nil { + t.Fatalf("Private params should be empty string") + } + }) + t.Run("RoundTripError 2", func(t *testing.T) { + + type dummyStruct struct { + dummy1 int + dummy2 float64 + } + dummy := &dummyStruct{1, 3.4} + values := map[string]any{ + jwk.AlgorithmKey: jwa.SignatureAlgorithm("dummy"), + jwk.KeyIDKey: 1, + jwk.KeyTypeKey: jwa.KeyType("dummy"), + jwk.KeyUsageKey: dummy, + jwk.KeyOpsKey: []string{"unknown", "usage"}, + jwk.PrivateParamsKey: dummy, + "invalid key": "", + } + + var h jwk.StandardHeaders + for k, v := range values { + err := h.Set(k, v) + if err == nil { + t.Fatalf("Setting %s value should have failed", k) + } + } + if h.GetAlgorithm() != jwa.NoValue { + t.Fatalf("Algorithm should be empty string") + } + if h.GetKeyID() != "" { + t.Fatalf("KeyID should be empty string") + } + if h.GetKeyType() != "" { + t.Fatalf("KeyType should be empty string") + } + if h.GetKeyUsage() != "" { + t.Fatalf("KeyUsage should be empty string") + } + if h.GetKeyOps() != nil { + t.Fatalf("KeyOps should be empty string") + } + if h.GetPrivateParams() != nil { + t.Fatalf("Private params should be empty string") + } + }) + + t.Run("Algorithm", func(t *testing.T) { + var h jwk.StandardHeaders + for _, value := range []any{jwa.RS256, jwa.ES256} { + err := h.Set("alg", value) + if err != nil { + t.Fatalf("Failed to set algorithm value: %s", err.Error()) + } + got, ok := h.Get("alg") + if !ok { + t.Fatal("Failed to get algorithm") + } + if value != got { + t.Fatalf("Algorithm values do not match %s:%s", value, got) + } + } + }) + t.Run("KeyType", func(t *testing.T) { + var h jwk.StandardHeaders + for _, value := range []any{jwa.RSA, "RSA"} { + err := h.Set(jwk.KeyTypeKey, value) + if err != nil { + t.Fatalf("failed to set key type: %s", err.Error()) + } + + got, ok := h.Get(jwk.KeyTypeKey) + if !ok { + t.Fatal("failed to get key type") + } + + var s string + switch v := value.(type) { + case jwa.KeyType: + s = v.String() + case string: + s = v + } + + if got != jwa.KeyType(s) { + t.Fatal("expected and realized key types do not match") + } + } + }) +} diff --git a/third_party/opa/internal/jwx/jwk/interface.go b/third_party/opa/internal/jwx/jwk/interface.go new file mode 100644 index 000000000000..9c7846269e08 --- /dev/null +++ b/third_party/opa/internal/jwx/jwk/interface.go @@ -0,0 +1,71 @@ +package jwk + +import ( + "crypto/ecdsa" + "crypto/rsa" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +// Set is a convenience struct to allow generating and parsing +// JWK sets as opposed to single JWKs +type Set struct { + Keys []Key `json:"keys"` +} + +// Key defines the minimal interface for each of the +// key types. Their use and implementation differ significantly +// between each key types, so you should use type assertions +// to perform more specific tasks with each key +type Key interface { + Headers + + // Materialize creates the corresponding key. For example, + // RSA types would create *rsa.PublicKey or *rsa.PrivateKey, + // EC types would create *ecdsa.PublicKey or *ecdsa.PrivateKey, + // and OctetSeq types create a []byte key. + Materialize() (any, error) + GenerateKey(*RawKeyJSON) error +} + +// RawKeyJSON is generic type that represents any kind JWK +type RawKeyJSON struct { + StandardHeaders + jwa.AlgorithmParameters +} + +// RawKeySetJSON is generic type that represents a JWK Set +type RawKeySetJSON struct { + Keys []RawKeyJSON `json:"keys"` +} + +// RSAPublicKey is a type of JWK generated from RSA public keys +type RSAPublicKey struct { + *StandardHeaders + key *rsa.PublicKey +} + +// RSAPrivateKey is a type of JWK generated from RSA private keys +type RSAPrivateKey struct { + *StandardHeaders + *jwa.AlgorithmParameters + key *rsa.PrivateKey +} + +// SymmetricKey is a type of JWK generated from symmetric keys +type SymmetricKey struct { + *StandardHeaders + key []byte +} + +// ECDSAPublicKey is a type of JWK generated from ECDSA public keys +type ECDSAPublicKey struct { + *StandardHeaders + key *ecdsa.PublicKey +} + +// ECDSAPrivateKey is a type of JWK generated from ECDH-ES private keys +type ECDSAPrivateKey struct { + *StandardHeaders + key *ecdsa.PrivateKey +} diff --git a/third_party/opa/internal/jwx/jwk/jwk.go b/third_party/opa/internal/jwx/jwk/jwk.go new file mode 100644 index 000000000000..b13245d17285 --- /dev/null +++ b/third_party/opa/internal/jwx/jwk/jwk.go @@ -0,0 +1,153 @@ +// Package jwk implements JWK as described in https://tools.ietf.org/html/rfc7517 +package jwk + +import ( + "crypto/ecdsa" + "crypto/rsa" + "encoding/json" + "errors" + "fmt" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +// GetPublicKey returns the public key based on the private key type. +// For rsa key types *rsa.PublicKey is returned; for ecdsa key types *ecdsa.PublicKey; +// for byte slice (raw) keys, the key itself is returned. If the corresponding +// public key cannot be deduced, an error is returned +func GetPublicKey(key any) (any, error) { + if key == nil { + return nil, errors.New("jwk.New requires a non-nil key") + } + + switch v := key.(type) { + // Mental note: although Public() is defined in both types, + // you can not coalesce the clauses for rsa.PrivateKey and + // ecdsa.PrivateKey, as then `v` becomes any + // b/c the compiler cannot deduce the exact type. + case *rsa.PrivateKey: + return v.Public(), nil + case *ecdsa.PrivateKey: + return v.Public(), nil + case []byte: + return v, nil + default: + return nil, fmt.Errorf("invalid key type %T", key) + } +} + +// GetKeyTypeFromKey creates a jwk.Key from the given key. +func GetKeyTypeFromKey(key any) jwa.KeyType { + + switch key.(type) { + case *rsa.PrivateKey, *rsa.PublicKey: + return jwa.RSA + case *ecdsa.PrivateKey, *ecdsa.PublicKey: + return jwa.EC + case []byte: + return jwa.OctetSeq + default: + return jwa.InvalidKeyType + } +} + +// New creates a jwk.Key from the given key. +func New(key any) (Key, error) { + if key == nil { + return nil, errors.New("jwk.New requires a non-nil key") + } + + switch v := key.(type) { + case *rsa.PrivateKey: + return newRSAPrivateKey(v) + case *rsa.PublicKey: + return newRSAPublicKey(v) + case *ecdsa.PrivateKey: + return newECDSAPrivateKey(v) + case *ecdsa.PublicKey: + return newECDSAPublicKey(v) + case []byte: + return newSymmetricKey(v) + default: + return nil, fmt.Errorf("invalid key type %T", key) + } +} + +func parse(jwkSrc string) (*Set, error) { + + var jwkKeySet Set + var jwkKey Key + rawKeySetJSON := &RawKeySetJSON{} + err := json.Unmarshal([]byte(jwkSrc), rawKeySetJSON) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal JWK Set: %w", err) + } + if len(rawKeySetJSON.Keys) == 0 { + + // It might be a single key + rawKeyJSON := &RawKeyJSON{} + err := json.Unmarshal([]byte(jwkSrc), rawKeyJSON) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal JWK: %w", err) + } + jwkKey, err = rawKeyJSON.GenerateKey() + if err != nil { + return nil, fmt.Errorf("failed to generate key: %w", err) + } + // Add to set + jwkKeySet.Keys = append(jwkKeySet.Keys, jwkKey) + } else { + for i := range rawKeySetJSON.Keys { + rawKeyJSON := rawKeySetJSON.Keys[i] + if rawKeyJSON.Algorithm != nil && *rawKeyJSON.Algorithm == jwa.Unsupported { + continue + } + jwkKey, err = rawKeyJSON.GenerateKey() + if err != nil { + return nil, fmt.Errorf("failed to generate key: %w", err) + } + jwkKeySet.Keys = append(jwkKeySet.Keys, jwkKey) + } + } + return &jwkKeySet, nil +} + +// ParseBytes parses JWK from the incoming byte buffer. +func ParseBytes(buf []byte) (*Set, error) { + return parse(string(buf)) +} + +// ParseString parses JWK from the incoming string. +func ParseString(s string) (*Set, error) { + return parse(s) +} + +// GenerateKey creates an internal representation of a key from a raw JWK JSON +func (r *RawKeyJSON) GenerateKey() (Key, error) { + + var key Key + + switch r.KeyType { + case jwa.RSA: + if r.D != nil { + key = &RSAPrivateKey{} + } else { + key = &RSAPublicKey{} + } + case jwa.EC: + if r.D != nil { + key = &ECDSAPrivateKey{} + } else { + key = &ECDSAPublicKey{} + } + case jwa.OctetSeq: + key = &SymmetricKey{} + default: + return nil, errors.New("unrecognized key type") + } + err := key.GenerateKey(r) + if err != nil { + return nil, fmt.Errorf("failed to generate key from JWK: %w", err) + } + return key, nil +} diff --git a/third_party/opa/internal/jwx/jwk/jwk_test.go b/third_party/opa/internal/jwx/jwk/jwk_test.go new file mode 100644 index 000000000000..b9504fe1b544 --- /dev/null +++ b/third_party/opa/internal/jwx/jwk/jwk_test.go @@ -0,0 +1,191 @@ +package jwk_test + +import ( + "testing" + + "github.com/open-policy-agent/opa/internal/jwx/jwk" +) + +func TestNew(t *testing.T) { + k, err := jwk.New(nil) + if k != nil { + t.Fatalf("key should be nil: %s", err.Error()) + } + if err == nil { + t.Fatal("nil key should cause an error") + } +} + +func TestGetPublicKeyErrors(t *testing.T) { + + t.Run("Key is nil", func(t *testing.T) { + _, err := jwk.GetPublicKey(nil) + if err == nil { + t.Fatal("GetPublicKey should have failed") + } + }) + t.Run("Key type is invalid", func(t *testing.T) { + _, err := jwk.GetPublicKey("dummy") + if err == nil { + t.Fatal("GetPublicKey should have failed") + } + }) +} + +func TestGetPublicKey(t *testing.T) { + + t.Run("Symmetric Key", func(t *testing.T) { + _, err := jwk.GetPublicKey([]byte("GawgguFyGrWKav7AX4VKUg")) + if err != nil { + t.Fatalf("GetPublicKey failed: %s", err.Error()) + } + }) +} + +func TestJwkNewErrors(t *testing.T) { + + t.Run("Key type is invalid", func(t *testing.T) { + _, err := jwk.New("dummy") + if err == nil { + t.Fatal("JwkNew should have failed") + } + }) +} + +func TestParseErrors(t *testing.T) { + + t.Run("Invalid JSON", func(t *testing.T) { + var jwkSrc = []byte(`{ + "keys" [ + { + "kty": "EC", + "crv": "P-256", + "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "use": "enc", + "kid": "1" + }, + { + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB", + "alg": "RS256", + "kid": "2011-04-29" + } + ] +}`) + + _, err := jwk.ParseBytes(jwkSrc) + if err == nil { + t.Fatalf("JWK Parsing should have failed") + } + }) + t.Run("Invalid JSON Key Set", func(t *testing.T) { + var jwkSrc = []byte(`{ + "keys" :[ + { + "kty": "EC", + "crv": "P-256", + "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "use": "enc", + "kid": "1" + }, + { + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB", + "alg": "RS256", + "kid": "2011-04-29" + } + ] +}`) + + _, err := jwk.ParseBytes(jwkSrc) + if err == nil { + t.Fatalf("JWK Parsing should have failed") + } + }) + + t.Run("Invalid JWK JSON", func(t *testing.T) { + var jwkSrc = []byte(`{ + "kty": "EC", + "crv": "P-256", + "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "use": "enc", + "kid": "1" +}`) + + _, err := jwk.ParseBytes(jwkSrc) + if err == nil { + t.Fatalf("JWK Parsing should have failed") + } + }) + t.Run("Invalid Key Type", func(t *testing.T) { + const jwkSrc = `{ + "e": "AQAB", + "kty": "invalid", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw" +}` + + _, err := jwk.ParseString(jwkSrc) + if err == nil { + t.Fatal("JWK parse should have failed") + } + }) + t.Run("Invalid Key Ops", func(t *testing.T) { + const jwkSrc = `{ + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "key_ops": [ + "invalid", + "sign" + ], + "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE" + } + ] +}` + + _, err := jwk.ParseString(jwkSrc) + if err == nil { + t.Fatal("JWK parse should have failed") + } + }) +} + +func TestAppendix(t *testing.T) { + + t.Run("A1", func(t *testing.T) { + var jwkSrc = []byte(`{ + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "use": "enc", + "kid": "1" + }, + { + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB", + "alg": "RS256", + "kid": "2011-04-29" + } + ] +}`) + + var jwkKeySet *jwk.Set + jwkKeySet, err := jwk.ParseBytes(jwkSrc) + if err != nil { + t.Fatalf("Failed to parse JWK Set: %s", err.Error()) + } + if len(jwkKeySet.Keys) != 2 { + t.Fatalf("Failed to parse JWK Set: %s", err.Error()) + } + }) +} diff --git a/third_party/opa/internal/jwx/jwk/key_ops.go b/third_party/opa/internal/jwx/jwk/key_ops.go new file mode 100644 index 000000000000..628caae4ad53 --- /dev/null +++ b/third_party/opa/internal/jwx/jwk/key_ops.go @@ -0,0 +1,67 @@ +package jwk + +import ( + "encoding/json" + "errors" + "fmt" +) + +// KeyUsageType is used to denote what this key should be used for +type KeyUsageType string + +const ( + // ForSignature is the value used in the headers to indicate that + // this key should be used for signatures + ForSignature KeyUsageType = "sig" + // ForEncryption is the value used in the headers to indicate that + // this key should be used for encryptiong + ForEncryption KeyUsageType = "enc" +) + +// KeyOperation is used to denote the allowed operations for a Key +type KeyOperation string + +// KeyOperationList represents an slice of KeyOperation +type KeyOperationList []KeyOperation + +var keyOps = map[string]struct{}{"sign": {}, "verify": {}, "encrypt": {}, "decrypt": {}, "wrapKey": {}, "unwrapKey": {}, "deriveKey": {}, "deriveBits": {}} + +// KeyOperation constants +const ( + KeyOpSign KeyOperation = "sign" // (compute digital signature or MAC) + KeyOpVerify KeyOperation = "verify" // (verify digital signature or MAC) + KeyOpEncrypt KeyOperation = "encrypt" // (encrypt content) + KeyOpDecrypt KeyOperation = "decrypt" // (decrypt content and validate decryption, if applicable) + KeyOpWrapKey KeyOperation = "wrapKey" // (encrypt key) + KeyOpUnwrapKey KeyOperation = "unwrapKey" // (decrypt key and validate decryption, if applicable) + KeyOpDeriveKey KeyOperation = "deriveKey" // (derive key) + KeyOpDeriveBits KeyOperation = "deriveBits" // (derive bits not to be used as a key) +) + +// Accept determines if Key Operation is valid +func (keyOperationList *KeyOperationList) Accept(v any) error { + switch x := v.(type) { + case KeyOperationList: + *keyOperationList = x + return nil + default: + return fmt.Errorf(`invalid value %T`, v) + } +} + +// UnmarshalJSON unmarshals and checks data as KeyType Algorithm +func (keyOperationList *KeyOperationList) UnmarshalJSON(data []byte) error { + var tempKeyOperationList []string + err := json.Unmarshal(data, &tempKeyOperationList) + if err != nil { + return errors.New("invalid key operation") + } + for _, value := range tempKeyOperationList { + _, ok := keyOps[value] + if !ok { + return errors.New("unknown key operation") + } + *keyOperationList = append(*keyOperationList, KeyOperation(value)) + } + return nil +} diff --git a/third_party/opa/internal/jwx/jwk/rsa.go b/third_party/opa/internal/jwx/jwk/rsa.go new file mode 100644 index 000000000000..d7b5089418fa --- /dev/null +++ b/third_party/opa/internal/jwx/jwk/rsa.go @@ -0,0 +1,133 @@ +package jwk + +import ( + "crypto/rsa" + "encoding/binary" + "errors" + "fmt" + "math/big" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +func newRSAPublicKey(key *rsa.PublicKey) (*RSAPublicKey, error) { + + var hdr StandardHeaders + err := hdr.Set(KeyTypeKey, jwa.RSA) + if err != nil { + return nil, fmt.Errorf("failed to set Key Type: %w", err) + } + return &RSAPublicKey{ + StandardHeaders: &hdr, + key: key, + }, nil +} + +func newRSAPrivateKey(key *rsa.PrivateKey) (*RSAPrivateKey, error) { + + var hdr StandardHeaders + err := hdr.Set(KeyTypeKey, jwa.RSA) + if err != nil { + return nil, fmt.Errorf("failed to set Key Type: %w", err) + } + + var algoParams jwa.AlgorithmParameters + + // it is needed to use raw encoding to omit the "=" paddings at the end + algoParams.D = key.D.Bytes() + algoParams.P = key.Primes[0].Bytes() + algoParams.Q = key.Primes[1].Bytes() + algoParams.Dp = key.Precomputed.Dp.Bytes() + algoParams.Dq = key.Precomputed.Dq.Bytes() + algoParams.Qi = key.Precomputed.Qinv.Bytes() + + // "modulus" (N) from the public key in the private key + algoParams.N = key.PublicKey.N.Bytes() + + // make the E a.k.a "coprime" + // https://en.wikipedia.org/wiki/RSA_(cryptosystem) + coprime := make([]byte, 8) + binary.BigEndian.PutUint64(coprime, uint64(key.PublicKey.E)) + // find the 1st index of non 0x0 paddings from the beginning + i := 0 + for ; i < len(coprime); i++ { + if coprime[i] != 0x0 { + break + } + } + algoParams.E = coprime[i:] + + return &RSAPrivateKey{ + StandardHeaders: &hdr, + AlgorithmParameters: &algoParams, + key: key, + }, nil +} + +// Materialize returns the standard RSA Public Key representation stored in the internal representation +func (k *RSAPublicKey) Materialize() (any, error) { + if k.key == nil { + return nil, errors.New("key has no rsa.PublicKey associated with it") + } + return k.key, nil +} + +// Materialize returns the standard RSA Private Key representation stored in the internal representation +func (k *RSAPrivateKey) Materialize() (any, error) { + if k.key == nil { + return nil, errors.New("key has no rsa.PrivateKey associated with it") + } + return k.key, nil +} + +// GenerateKey creates a RSAPublicKey from a RawKeyJSON +func (k *RSAPublicKey) GenerateKey(keyJSON *RawKeyJSON) error { + + if keyJSON.N == nil || keyJSON.E == nil { + return errors.New("missing mandatory key parameters N or E") + } + rsaPublicKey := &rsa.PublicKey{ + N: (&big.Int{}).SetBytes(keyJSON.N.Bytes()), + E: int((&big.Int{}).SetBytes(keyJSON.E.Bytes()).Int64()), + } + k.key = rsaPublicKey + k.StandardHeaders = &keyJSON.StandardHeaders + return nil +} + +// GenerateKey creates a RSAPublicKey from a RawKeyJSON +func (k *RSAPrivateKey) GenerateKey(keyJSON *RawKeyJSON) error { + + rsaPublicKey := &RSAPublicKey{} + err := rsaPublicKey.GenerateKey(keyJSON) + if err != nil { + return fmt.Errorf("failed to generate public key: %w", err) + } + + if keyJSON.D == nil || keyJSON.P == nil || keyJSON.Q == nil { + return errors.New("missing mandatory key parameters D, P or Q") + } + privateKey := &rsa.PrivateKey{ + PublicKey: *rsaPublicKey.key, + D: (&big.Int{}).SetBytes(keyJSON.D.Bytes()), + Primes: []*big.Int{ + (&big.Int{}).SetBytes(keyJSON.P.Bytes()), + (&big.Int{}).SetBytes(keyJSON.Q.Bytes()), + }, + } + + if keyJSON.Dp.Len() > 0 { + privateKey.Precomputed.Dp = (&big.Int{}).SetBytes(keyJSON.Dp.Bytes()) + } + if keyJSON.Dq.Len() > 0 { + privateKey.Precomputed.Dq = (&big.Int{}).SetBytes(keyJSON.Dq.Bytes()) + } + if keyJSON.Qi.Len() > 0 { + privateKey.Precomputed.Qinv = (&big.Int{}).SetBytes(keyJSON.Qi.Bytes()) + } + + k.key = privateKey + k.StandardHeaders = &keyJSON.StandardHeaders + k.AlgorithmParameters = &keyJSON.AlgorithmParameters + return nil +} diff --git a/third_party/opa/internal/jwx/jwk/rsa_test.go b/third_party/opa/internal/jwx/jwk/rsa_test.go new file mode 100644 index 000000000000..8007c8bb85da --- /dev/null +++ b/third_party/opa/internal/jwx/jwk/rsa_test.go @@ -0,0 +1,243 @@ +package jwk_test + +import ( + "bytes" + "encoding/json" + "fmt" + "testing" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jwk" +) + +func TestRSA(t *testing.T) { + verify := func(t *testing.T, key jwk.Key) { + t.Helper() + + rsaKey, err := key.Materialize() + if err != nil { + t.Fatalf("Materialize() failed: %s", err.Error()) + } + + newKey, err := jwk.New(rsaKey) + if err != nil { + t.Fatalf("jwk.New failed: %s", err.Error()) + } + + err = key.Walk(func(k string, v any) error { + return newKey.Set(k, v) + }) + if err != nil { + t.Fatalf("Failed to walk key: %s", err.Error()) + } + + jsonBuf1, err := json.Marshal(key) + if err != nil { + t.Fatalf("JSON marshal failed: %s", err.Error()) + } + + jsonBuf2, err := json.Marshal(newKey) + if err != nil { + t.Fatalf("JSON marshal failed: %s", err.Error()) + } + + if !bytes.Equal(jsonBuf1, jsonBuf2) { + t.Fatal("JSON marshal buffers do not match") + } + } + t.Run("Public Key", func(t *testing.T) { + const jwkSrc = `{ + "e": "AQAB", + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw" +}` + + var jwkKey jwk.Key + + // It might be a single key + rawKeyJSON := &jwk.RawKeyJSON{} + err := json.Unmarshal([]byte(jwkSrc), rawKeyJSON) + if err != nil { + t.Fatalf("Failed to unmarshal JWK: %s", err.Error()) + } + jwkKey, err = rawKeyJSON.GenerateKey() + if err != nil { + t.Fatalf("Failed to generate key: %v", err) + } + if _, ok := jwkKey.(*jwk.RSAPublicKey); !ok { + t.Fatalf("Key type should be of type: %s", fmt.Sprintf("%T", jwkKey)) + } + rsaKey, err := jwkKey.Materialize() + if err != nil { + t.Fatalf("Failed to materialize symmetric key: %v", err) + } + if jwk.GetKeyTypeFromKey(rsaKey) != jwa.RSA { + t.Fatal("Wrong Key Type") + } + + verify(t, jwkKey) + }) + t.Run("No Key Type Error", func(t *testing.T) { + const jwkSrc = `{ + "e": "AQAB", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw" +}` + + // It might be a single key + rawKeyJSON := &jwk.RawKeyJSON{} + err := json.Unmarshal([]byte(jwkSrc), rawKeyJSON) + if err != nil { + t.Fatalf("Failed to unmarshal JWK: %s", err.Error()) + } + _, err = rawKeyJSON.GenerateKey() + if err == nil { + t.Fatal("Key generation should have failed") + } + + }) + t.Run("Single Private Key Error", func(t *testing.T) { + const jwkSrc = `{ + "e": "AQAB", + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "d": "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q", + "p": "83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "alg": "RS256", + "kid": "2011-04-29" +}` + // It might be a single key + rawKeyJSON := &jwk.RawKeyJSON{} + err := json.Unmarshal([]byte(jwkSrc), rawKeyJSON) + if err != nil { + t.Fatalf("Failed to unmarshal JWK: %s", err.Error()) + } + _, err = rawKeyJSON.GenerateKey() + if err == nil { + t.Fatalf("Key generation should fail") + } + }) + + t.Run("Single Private Key", func(t *testing.T) { + const jwkSrc = `{ + "e": "AQAB", + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "d": "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q", + "p": "83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs", + "q": "3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "alg": "RS256", + "kid": "2011-04-29" +}` + var jwkKey jwk.Key + rawKeySetJSON := &jwk.RawKeySetJSON{} + err := json.Unmarshal([]byte(jwkSrc), rawKeySetJSON) + if err != nil { + t.Fatalf("Failed to unmarshal JWK Set: %s", err.Error()) + } + if len(rawKeySetJSON.Keys) == 0 { + // It might be a single key + rawKeyJSON := &jwk.RawKeyJSON{} + err := json.Unmarshal([]byte(jwkSrc), rawKeyJSON) + if err != nil { + t.Fatalf("Failed to unmarshal JWK: %s", err.Error()) + } + jwkKey, err = rawKeyJSON.GenerateKey() + if err != nil { + t.Fatalf("Failed to generate key: %v", err) + } + if _, ok := jwkKey.(*jwk.RSAPrivateKey); !ok { + t.Fatalf("Key type should be of type: %s", fmt.Sprintf("%T", jwkKey)) + } + rsaKey, err := jwkKey.Materialize() + if err != nil { + t.Fatal("Failed to materialize symmetric key") + } + if jwk.GetKeyTypeFromKey(rsaKey) != jwa.RSA { + t.Fatal("Wrong Key Type") + } + } else { + t.Fatal("Incorrect number of keys") + } + verify(t, jwkKey) + }) + t.Run("JWK Set of one Private Key", func(t *testing.T) { + jwkSrc := `{ + "keys": [ + { + "kty": "RSA", + "alg": "RS256", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB", + "d": "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q", + "p": "83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs", + "q": "3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "kid": "2011-04-29" + } + ] +}` + var jwkKey jwk.Key + rawKeySetJSON := &jwk.RawKeySetJSON{} + err := json.Unmarshal([]byte(jwkSrc), rawKeySetJSON) + if err != nil { + t.Fatalf("Failed to unmarshal JWK Set: %s", err.Error()) + } + if len(rawKeySetJSON.Keys) == 0 { + t.Fatal("Incorrect number of keys") + } else { + rawKeyJSON := rawKeySetJSON.Keys[0] + jwkKey, err = rawKeyJSON.GenerateKey() + if err != nil { + t.Fatalf("Failed to generate key: %v", err) + } + if _, ok := jwkKey.(*jwk.RSAPrivateKey); !ok { + t.Fatalf("Key type should be of type: %s", fmt.Sprintf("%T", jwkKey)) + } + } + verify(t, jwkKey) + }) +} + +func TestRSAMaterializeErrors(t *testing.T) { + t.Run("No Standard Public Key", func(t *testing.T) { + rsaPublicKey := &jwk.RSAPublicKey{} + _, err := rsaPublicKey.Materialize() + if err == nil { + t.Fatal("Materialize key should fail") + } + }) + t.Run("No Standard Private Key", func(t *testing.T) { + rsaPrivateKey := &jwk.RSAPrivateKey{} + _, err := rsaPrivateKey.Materialize() + if err == nil { + t.Fatal("Materialize key should fail") + } + }) +} + +func TestRSAGenerateErrors(t *testing.T) { + t.Run("Raw JWK missing D or E", func(t *testing.T) { + rawKeyJSON := &jwk.RawKeyJSON{} + rsaPublicKey := &jwk.RSAPublicKey{} + err := rsaPublicKey.GenerateKey(rawKeyJSON) + if err == nil { + t.Fatal("Materialize key should fail") + } + }) + t.Run("Raw JWK missing D or E", func(t *testing.T) { + rawKeyJSON := &jwk.RawKeyJSON{} + rsaPrivateKey := &jwk.RSAPrivateKey{} + err := rsaPrivateKey.GenerateKey(rawKeyJSON) + if err == nil { + t.Fatal("Materialize key should fail") + } + }) +} diff --git a/third_party/opa/internal/jwx/jwk/symmetric.go b/third_party/opa/internal/jwx/jwk/symmetric.go new file mode 100644 index 000000000000..e76189f523e7 --- /dev/null +++ b/third_party/opa/internal/jwx/jwk/symmetric.go @@ -0,0 +1,41 @@ +package jwk + +import ( + "fmt" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +func newSymmetricKey(key []byte) (*SymmetricKey, error) { + var hdr StandardHeaders + + err := hdr.Set(KeyTypeKey, jwa.OctetSeq) + if err != nil { + return nil, fmt.Errorf("failed to set Key Type: %w", err) + } + return &SymmetricKey{ + StandardHeaders: &hdr, + key: key, + }, nil +} + +// Materialize returns the octets for this symmetric key. +// Since this is a symmetric key, this just calls Octets +func (s SymmetricKey) Materialize() (any, error) { + return s.Octets(), nil +} + +// Octets returns the octets in the key +func (s SymmetricKey) Octets() []byte { + return s.key +} + +// GenerateKey creates a Symmetric key from a RawKeyJSON +func (s *SymmetricKey) GenerateKey(keyJSON *RawKeyJSON) error { + + *s = SymmetricKey{ + StandardHeaders: &keyJSON.StandardHeaders, + key: keyJSON.K, + } + return nil +} diff --git a/third_party/opa/internal/jwx/jwk/symmetric_test.go b/third_party/opa/internal/jwx/jwk/symmetric_test.go new file mode 100644 index 000000000000..f8ca50b50cbe --- /dev/null +++ b/third_party/opa/internal/jwx/jwk/symmetric_test.go @@ -0,0 +1,125 @@ +package jwk_test + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jwk" +) + +func TestSymmetric(t *testing.T) { + + t.Run("A3", func(t *testing.T) { + const ( + key1 = `5Fn8i7r5cRWZW_yyr9Flkg` + key2 = `AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow` + ) + + buf1, err := base64.RawURLEncoding.DecodeString(key1) + if err != nil { + t.Fatalf("Failed to decode key1: %s", err.Error()) + } + buf2, err := base64.RawURLEncoding.DecodeString(key2) + if err != nil { + t.Fatalf("Failed to decode key2: %s", err.Error()) + } + + var jwkSrc = []byte(`{ + "keys": [ + { + "kty": "oct", + "alg": "HS256", + "k": "5Fn8i7r5cRWZW_yyr9Flkg" + }, + { + "kty": "oct", + "kid": "HMAC key used in JWS spec Appendix A.1 example", + "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow" + } + ] +}`) + var jwkKey jwk.Key + rawKeySetJSON := &jwk.RawKeySetJSON{} + err = json.Unmarshal(jwkSrc, rawKeySetJSON) + if err != nil { + t.Fatalf("Failed to unmarshal JWK Set: %s", err.Error()) + } + rawKeyJSON0 := rawKeySetJSON.Keys[0] + jwkKey0, err := rawKeyJSON0.GenerateKey() + if err != nil { + t.Fatalf("Failed to generate key: %s", err.Error()) + } + if _, ok := jwkKey0.(*jwk.SymmetricKey); !ok { + t.Fatalf("Key type should be of type: %s", fmt.Sprintf("%T", jwkKey)) + } + realizedKey0, err := jwkKey0.Materialize() + if err != nil { + t.Fatalf("Failed to materialize key: %s", err.Error()) + } + if jwk.GetKeyTypeFromKey(realizedKey0) != jwa.OctetSeq { + t.Fatal("Wrong Key Type") + } + if !bytes.Equal(realizedKey0.([]byte), buf1) { + t.Fatalf("Mismatched key values %s:%s", realizedKey0.([]byte), buf1) + } + + rawKeyJSON1 := rawKeySetJSON.Keys[1] + jwkKey1, err := rawKeyJSON1.GenerateKey() + if err != nil { + t.Fatalf("Failed to generate key: %s", err.Error()) + } + if _, ok := jwkKey1.(*jwk.SymmetricKey); !ok { + t.Fatalf("Key type should be of type: %s", fmt.Sprintf("%T", jwkKey)) + } + realizedKey1, err := jwkKey1.Materialize() + if err != nil { + t.Fatalf("Failed to materialize key: %s", err.Error()) + } + if !bytes.Equal(realizedKey1.([]byte), buf2) { + t.Fatalf("Mismatched key values %s:%s", realizedKey1.([]byte), buf1) + } + }) + t.Run("Test New Key", func(t *testing.T) { + + var jwkSrc = []byte(`{ + "keys": [ + { + "kty": "oct", + "kid": "HMAC key used in JWS spec Appendix A.1 example", + "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow" + } + ] +}`) + rawKeySetJSON := &jwk.RawKeySetJSON{} + err := json.Unmarshal(jwkSrc, rawKeySetJSON) + if err != nil { + t.Fatalf("Failed to unmarshal JWK Set: %s", err.Error()) + } + rawKeyJSON0 := rawKeySetJSON.Keys[0] + jwkKey0, err := rawKeyJSON0.GenerateKey() + if err != nil { + t.Fatalf("Failed to generate key: %s", err.Error()) + } + realizedKey0, err := jwkKey0.Materialize() + if err != nil { + t.Fatalf("Failed to materialize key: %s", err.Error()) + } + jwkKey1, err := jwk.New(realizedKey0) + if err != nil { + t.Fatalf("Failed to create new symmetric key: %s", err.Error()) + } + realizedKey1, err := jwkKey1.Materialize() + if err != nil { + t.Fatalf("Failed to materialize key: %s", err.Error()) + } + if !reflect.DeepEqual(realizedKey1, realizedKey0) { + t.Fatalf("Mismatched symmetric keys") + } + }) + +} diff --git a/third_party/opa/internal/jwx/jws/headers.go b/third_party/opa/internal/jwx/jws/headers.go new file mode 100644 index 000000000000..dcadea43e233 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/headers.go @@ -0,0 +1,154 @@ +package jws + +import ( + "fmt" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +// Constants for JWS Common parameters +const ( + AlgorithmKey = "alg" + ContentTypeKey = "cty" + CriticalKey = "crit" + JWKKey = "jwk" + JWKSetURLKey = "jku" + KeyIDKey = "kid" + PrivateParamsKey = "privateParams" + TypeKey = "typ" +) + +// Headers provides a common interface for common header parameters +type Headers interface { + Get(string) (any, bool) + Set(string, any) error + GetAlgorithm() jwa.SignatureAlgorithm +} + +// StandardHeaders contains JWS common parameters. +type StandardHeaders struct { + Algorithm jwa.SignatureAlgorithm `json:"alg,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.1 + ContentType string `json:"cty,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.10 + Critical []string `json:"crit,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.11 + JWK string `json:"jwk,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.3 + JWKSetURL string `json:"jku,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.2 + KeyID string `json:"kid,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.4 + PrivateParams map[string]any `json:"privateParams,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.9 + Type string `json:"typ,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.9 +} + +// GetAlgorithm returns algorithm +func (h *StandardHeaders) GetAlgorithm() jwa.SignatureAlgorithm { + return h.Algorithm +} + +// Get is a general getter function for StandardHeaders structure +func (h *StandardHeaders) Get(name string) (any, bool) { + switch name { + case AlgorithmKey: + v := h.Algorithm + if v == "" { + return nil, false + } + return v, true + case ContentTypeKey: + v := h.ContentType + if v == "" { + return nil, false + } + return v, true + case CriticalKey: + v := h.Critical + if len(v) == 0 { + return nil, false + } + return v, true + case JWKKey: + v := h.JWK + if v == "" { + return nil, false + } + return v, true + case JWKSetURLKey: + v := h.JWKSetURL + if v == "" { + return nil, false + } + return v, true + case KeyIDKey: + v := h.KeyID + if v == "" { + return nil, false + } + return v, true + case PrivateParamsKey: + v := h.PrivateParams + if len(v) == 0 { + return nil, false + } + return v, true + case TypeKey: + v := h.Type + if v == "" { + return nil, false + } + return v, true + default: + return nil, false + } +} + +// Set is a general setter function for StandardHeaders structure +func (h *StandardHeaders) Set(name string, value any) error { + switch name { + case AlgorithmKey: + if err := h.Algorithm.Accept(value); err != nil { + return fmt.Errorf("invalid value for %s key: %w", AlgorithmKey, err) + } + return nil + case ContentTypeKey: + if v, ok := value.(string); ok { + h.ContentType = v + return nil + } + return fmt.Errorf("invalid value for %s key: %T", ContentTypeKey, value) + case CriticalKey: + if v, ok := value.([]string); ok { + h.Critical = v + return nil + } + return fmt.Errorf("invalid value for %s key: %T", CriticalKey, value) + case JWKKey: + if v, ok := value.(string); ok { + h.JWK = v + return nil + } + return fmt.Errorf("invalid value for %s key: %T", JWKKey, value) + case JWKSetURLKey: + if v, ok := value.(string); ok { + h.JWKSetURL = v + return nil + } + return fmt.Errorf("invalid value for %s key: %T", JWKSetURLKey, value) + case KeyIDKey: + if v, ok := value.(string); ok { + h.KeyID = v + return nil + } + return fmt.Errorf("invalid value for %s key: %T", KeyIDKey, value) + case PrivateParamsKey: + if v, ok := value.(map[string]any); ok { + h.PrivateParams = v + return nil + } + return fmt.Errorf("invalid value for %s key: %T", PrivateParamsKey, value) + case TypeKey: + if v, ok := value.(string); ok { + h.Type = v + return nil + } + return fmt.Errorf("invalid value for %s key: %T", TypeKey, value) + default: + return fmt.Errorf("invalid key: %s", name) + } +} diff --git a/third_party/opa/internal/jwx/jws/headers_test.go b/third_party/opa/internal/jwx/jws/headers_test.go new file mode 100644 index 000000000000..8a092b3378ad --- /dev/null +++ b/third_party/opa/internal/jwx/jws/headers_test.go @@ -0,0 +1,122 @@ +package jws_test + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jws" +) + +func TestHeader(t *testing.T) { + jwkSrc := `{ + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB", + "alg": "RS256", + "kid": "2011-04-29" +}` + + privateHeaderParams := map[string]any{"one": "1", "two": "11"} + + values := map[string]any{ + jws.AlgorithmKey: jwa.ES256, + jws.ContentTypeKey: "example", + jws.CriticalKey: []string{"exp"}, + jws.JWKKey: jwkSrc, + jws.JWKSetURLKey: "https://www.jwk.com/key.json", + jws.TypeKey: "JWT", + jws.KeyIDKey: "e9bc097a-ce51-4036-9562-d2ade882db0d", + jws.PrivateParamsKey: privateHeaderParams, + } + t.Run("RoundTrip", func(t *testing.T) { + + var h jws.StandardHeaders + for k, v := range values { + err := h.Set(k, v) + if err != nil { + t.Fatalf("Set failed for %s", k) + } + got, ok := h.Get(k) + if !ok { + t.Fatalf("Set failed for %s", k) + } + if !reflect.DeepEqual(v, got) { + t.Fatalf("Values do not match: (%v, %v)", v, got) + } + } + }) + t.Run("JSON Marshal Unmarshal", func(t *testing.T) { + + var h jws.StandardHeaders + for k, v := range values { + err := h.Set(k, v) + if err != nil { + t.Fatalf("Set failed for %s", k) + } + got, ok := h.Get(k) + if !ok { + t.Fatalf("Set failed for %s", k) + } + if !reflect.DeepEqual(v, got) { + t.Fatalf("Values do not match: (%v, %v)", v, got) + } + } + hByte, err := json.Marshal(h) + if err != nil { + t.Fatal("Failed to JSON marshal") + } + var hNew jws.StandardHeaders + err = json.Unmarshal(hByte, &hNew) + if err != nil { + t.Fatal("Failed to JSON marshal") + } + }) + t.Run("RoundTripError", func(t *testing.T) { + + type dummyStruct struct { + dummy1 int + dummy2 float64 + } + dummy := &dummyStruct{1, 3.4} + + values := map[string]any{ + jws.AlgorithmKey: dummy, + jws.ContentTypeKey: dummy, + jws.CriticalKey: dummy, + jws.JWKKey: dummy, + jws.JWKSetURLKey: dummy, + jws.KeyIDKey: dummy, + jws.TypeKey: dummy, + jws.PrivateParamsKey: dummy, + "invalid key": "", + } + + var h jws.StandardHeaders + for k, v := range values { + err := h.Set(k, v) + if err == nil { + t.Fatalf("Setting %s value should have failed", k) + } + } + for k := range values { + _, ok := h.Get(k) + if ok { + t.Fatalf("Getting %s value should have failed", k) + } + } + }) + t.Run("Unknown alg", func(t *testing.T) { + + headers := `{"typ":"JWT",` + "\r\n" + ` "alg":"dummy"}` + var standardHeaders jws.StandardHeaders + err := json.Unmarshal([]byte(headers), &standardHeaders) + if err != nil { + t.Fatal(err) + } + if standardHeaders.Algorithm != jwa.Unsupported { + t.Errorf("expected unsupported algorithm") + } + }) +} diff --git a/third_party/opa/internal/jwx/jws/interface.go b/third_party/opa/internal/jwx/jws/interface.go new file mode 100644 index 000000000000..e647c8ac9370 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/interface.go @@ -0,0 +1,22 @@ +package jws + +// Message represents a full JWS encoded message. Flattened serialization +// is not supported as a struct, but rather it's represented as a +// Message struct with only one `Signature` element. +// +// Do not expect to use the Message object to verify or construct a +// signed payloads with. You should only use this when you want to actually +// want to programmatically view the contents for the full JWS Payload. +// +// To sign and verify, use the appropriate `SignWithOption()` nad `Verify()` functions +type Message struct { + Payload []byte `json:"payload"` + Signatures []*Signature `json:"signatures,omitempty"` +} + +// Signature represents the headers and signature of a JWS message +type Signature struct { + Headers Headers `json:"header,omitempty"` // Unprotected Headers + Protected Headers `json:"Protected,omitempty"` // Protected Headers + Signature []byte `json:"signature,omitempty"` // GetSignature +} diff --git a/third_party/opa/internal/jwx/jws/jws.go b/third_party/opa/internal/jwx/jws/jws.go new file mode 100644 index 000000000000..b2b224830614 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/jws.go @@ -0,0 +1,220 @@ +// Package jws implements the digital Signature on JSON based data +// structures as described in https://tools.ietf.org/html/rfc7515 +// +// If you do not care about the details, the only things that you +// would need to use are the following functions: +// +// jws.SignWithOption(Payload, algorithm, key) +// jws.Verify(encodedjws, algorithm, key) +// +// To sign, simply use `jws.SignWithOption`. `Payload` is a []byte buffer that +// contains whatever data you want to sign. `alg` is one of the +// jwa.SignatureAlgorithm constants from package jwa. For RSA and +// ECDSA family of algorithms, you will need to prepare a private key. +// For HMAC family, you just need a []byte value. The `jws.SignWithOption` +// function will return the encoded JWS message on success. +// +// To verify, use `jws.Verify`. It will parse the `encodedjws` buffer +// and verify the result using `algorithm` and `key`. Upon successful +// verification, the original Payload is returned, so you can work on it. +package jws + +import ( + "bytes" + "crypto/rand" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "strings" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jwk" + "github.com/open-policy-agent/opa/internal/jwx/jws/sign" + "github.com/open-policy-agent/opa/internal/jwx/jws/verify" +) + +// SignLiteral generates a Signature for the given Payload and Headers, and serializes +// it in compact serialization format. In this format you may NOT use +// multiple signers. +func SignLiteral(payload []byte, alg jwa.SignatureAlgorithm, key any, hdrBuf []byte, rnd io.Reader) ([]byte, error) { + encodedHdr := base64.RawURLEncoding.EncodeToString(hdrBuf) + encodedPayload := base64.RawURLEncoding.EncodeToString(payload) + signingInput := strings.Join( + []string{ + encodedHdr, + encodedPayload, + }, ".", + ) + signer, err := sign.New(alg) + if err != nil { + return nil, fmt.Errorf("failed to create signer: %w", err) + } + + var signature []byte + switch s := signer.(type) { + case *sign.ECDSASigner: + signature, err = s.SignWithRand([]byte(signingInput), key, rnd) + default: + signature, err = signer.Sign([]byte(signingInput), key) + } + if err != nil { + return nil, fmt.Errorf("failed to sign Payload: %w", err) + } + encodedSignature := base64.RawURLEncoding.EncodeToString(signature) + compactSerialization := strings.Join( + []string{ + signingInput, + encodedSignature, + }, ".", + ) + return []byte(compactSerialization), nil +} + +// SignWithOption generates a Signature for the given Payload, and serializes +// it in compact serialization format. In this format you may NOT use +// multiple signers. +// +// If you would like to pass custom Headers, use the WithHeaders option. +func SignWithOption(payload []byte, alg jwa.SignatureAlgorithm, key any) ([]byte, error) { + var headers Headers = &StandardHeaders{} + + err := headers.Set(AlgorithmKey, alg) + if err != nil { + return nil, fmt.Errorf("failed to set alg value: %w", err) + } + + hdrBuf, err := json.Marshal(headers) + if err != nil { + return nil, fmt.Errorf("failed to marshal Headers: %w", err) + } + // NOTE(sr): we don't use SignWithOption -- if we did, this rand.Reader + // should come from the BuiltinContext's Seed, too. + return SignLiteral(payload, alg, key, hdrBuf, rand.Reader) +} + +// Verify checks if the given JWS message is verifiable using `alg` and `key`. +// If the verification is successful, `err` is nil, and the content of the +// Payload that was signed is returned. If you need more fine-grained +// control of the verification process, manually call `Parse`, generate a +// verifier, and call `Verify` on the parsed JWS message object. +func Verify(buf []byte, alg jwa.SignatureAlgorithm, key any) (ret []byte, err error) { + + verifier, err := verify.New(alg) + if err != nil { + return nil, fmt.Errorf("failed to create verifier: %w", err) + } + + buf = bytes.TrimSpace(buf) + if len(buf) == 0 { + return nil, errors.New(`attempt to verify empty buffer`) + } + + parts, err := SplitCompact(string(buf)) + if err != nil { + return nil, fmt.Errorf("failed extract from compact serialization format: %w", err) + } + + signingInput := strings.Join( + []string{ + parts[0], + parts[1], + }, ".", + ) + + decodedSignature, err := base64.RawURLEncoding.DecodeString(parts[2]) + if err != nil { + return nil, fmt.Errorf("failed to decode signature: %w", err) + } + if err := verifier.Verify([]byte(signingInput), decodedSignature, key); err != nil { + return nil, fmt.Errorf("failed to verify message: %w", err) + } + + if decodedPayload, err := base64.RawURLEncoding.DecodeString(parts[1]); err == nil { + return decodedPayload, nil + } + return nil, fmt.Errorf("failed to decode Payload: %w", err) +} + +// VerifyWithJWK verifies the JWS message using the specified JWK +func VerifyWithJWK(buf []byte, key jwk.Key) (payload []byte, err error) { + + keyVal, err := key.Materialize() + if err != nil { + return nil, fmt.Errorf("failed to materialize key: %w", err) + } + return Verify(buf, key.GetAlgorithm(), keyVal) +} + +// VerifyWithJWKSet verifies the JWS message using JWK key set. +// By default it will only pick up keys that have the "use" key +// set to either "sig" or "enc", but you can override it by +// providing a keyaccept function. +func VerifyWithJWKSet(buf []byte, keyset *jwk.Set) (payload []byte, err error) { + + for _, key := range keyset.Keys { + payload, err := VerifyWithJWK(buf, key) + if err == nil { + return payload, nil + } + } + return nil, errors.New("failed to verify with any of the keys") +} + +// ParseByte parses a JWS value serialized via compact serialization and provided as []byte. +func ParseByte(jwsCompact []byte) (m *Message, err error) { + return parseCompact(string(jwsCompact)) +} + +// ParseString parses a JWS value serialized via compact serialization and provided as string. +func ParseString(s string) (*Message, error) { + return parseCompact(s) +} + +// SplitCompact splits a JWT and returns its three parts +// separately: Protected Headers, Payload and Signature. +func SplitCompact(jwsCompact string) ([]string, error) { + + parts := strings.Split(jwsCompact, ".") + if len(parts) < 3 { + return nil, errors.New("failed to split compact serialization") + } + return parts, nil +} + +// parseCompact parses a JWS value serialized via compact serialization. +func parseCompact(str string) (m *Message, err error) { + + var decodedHeader, decodedPayload, decodedSignature []byte + parts, err := SplitCompact(str) + if err != nil { + return nil, fmt.Errorf("invalid compact serialization format: %w", err) + } + + if decodedHeader, err = base64.RawURLEncoding.DecodeString(parts[0]); err != nil { + return nil, fmt.Errorf("failed to decode Headers: %w", err) + } + var hdr StandardHeaders + if err := json.Unmarshal(decodedHeader, &hdr); err != nil { + return nil, fmt.Errorf("failed to parse JOSE Headers: %w", err) + } + + if decodedPayload, err = base64.RawURLEncoding.DecodeString(parts[1]); err != nil { + return nil, fmt.Errorf("failed to decode Payload: %w", err) + } + + if len(parts) > 2 { + if decodedSignature, err = base64.RawURLEncoding.DecodeString(parts[2]); err != nil { + return nil, fmt.Errorf("failed to decode Signature: %w", err) + } + } + + var msg Message + msg.Payload = decodedPayload + msg.Signatures = append(msg.Signatures, &Signature{ + Protected: &hdr, + Signature: decodedSignature, + }) + return &msg, nil +} diff --git a/third_party/opa/internal/jwx/jws/jws_test.go b/third_party/opa/internal/jwx/jws/jws_test.go new file mode 100644 index 000000000000..e6e7cafae8ad --- /dev/null +++ b/third_party/opa/internal/jwx/jws/jws_test.go @@ -0,0 +1,641 @@ +package jws_test + +import ( + "bytes" + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" + "crypto/sha512" + "encoding/base64" + "encoding/json" + "math/big" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jwk" + "github.com/open-policy-agent/opa/internal/jwx/jws" + "github.com/open-policy-agent/opa/internal/jwx/jws/sign" + "github.com/open-policy-agent/opa/internal/jwx/jws/verify" +) + +const examplePayload = `{"iss":"joe",` + "\r\n" + ` "exp":1300819380,` + "\r\n" + ` "http://example.com/is_root":true}` +const exampleCompactSerialization = `eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk` + +func TestParseErrors(t *testing.T) { + + t.Run("Empty bytes.Buffer", func(t *testing.T) { + _, err := jws.ParseString("") + if err == nil { + t.Fatal("Parsing an empty buffer should result in an error") + } + }) + t.Run("Compact missing parts", func(t *testing.T) { + incoming := strings.Join( + (strings.Split( + exampleCompactSerialization, + ".", + ))[:2], + ".", + ) + _, err := jws.ParseString(incoming) + if err == nil { + t.Fatalf("Parsing compact serialization with less than 3 parts should be an error") + } + }) + const badValue = "%badvalue%" + t.Run("Compact bad header", func(t *testing.T) { + parts := strings.Split(exampleCompactSerialization, ".") + parts[0] = "badValue" + incoming := strings.Join(parts, ".") + + _, err := jws.ParseString(incoming) + if err == nil { + t.Fatal("Parsing compact serialization with bad header should be an error") + } + }) + t.Run("Compact bad Payload", func(t *testing.T) { + parts := strings.Split(exampleCompactSerialization, ".") + parts[1] = badValue + incoming := strings.Join(parts, ".") + + _, err := jws.ParseString(incoming) + if err == nil { + t.Fatal("Parsing compact serialization with bad Payload should be an error") + } + }) + t.Run("Compact bad Signature", func(t *testing.T) { + parts := strings.Split(exampleCompactSerialization, ".") + parts[2] = badValue + incoming := strings.Join(parts, ".") + + _, err := jws.ParseString(incoming) + if err == nil { + t.Fatal("Parsing compact serialization with bad Signature should be an error") + } + }) +} + +func TestAlgError(t *testing.T) { + + t.Run("Unknown Algorithm", func(t *testing.T) { + const hdr = `{"typ":"JWT",` + "\r\n" + ` "alg":"unknown"}` + var standardHeaders jws.StandardHeaders + err := json.Unmarshal([]byte(hdr), &standardHeaders) + if err != nil { + t.Fatal(err) + } + if standardHeaders.Algorithm != jwa.Unsupported { + t.Errorf("expected unsupported algorithm") + } + }) +} + +func TestRoundTrip(t *testing.T) { + payload := []byte("Lorem ipsum") + sharedKey := []byte("Avracadabra") + + hmacAlgorithms := []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512} + for _, alg := range hmacAlgorithms { + t.Run("HMAC "+alg.String(), func(t *testing.T) { + signed, err := jws.SignWithOption(payload, alg, sharedKey) + if err != nil { + t.Fatalf("Failed to sign input: %s", err.Error()) + } + verified, err := jws.Verify(signed, alg, sharedKey) + if err != nil { + t.Fatalf("Message verification failed: %s", err.Error()) + } + if !bytes.Equal(payload, verified) { + t.Fatalf("Mismatched payload (%s):(%s)", payload, verified) + } + }) + } +} + +func TestVerifyWithJWKSet(t *testing.T) { + + payload := []byte("Hello, World!") + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("Failed to generate key: %s", err.Error()) + } + jwkKey, err := jwk.New(&key.PublicKey) + if err != nil { + t.Fatalf("Failed to create JWX Private Key: %s", err.Error()) + } + err = jwkKey.Set(jwk.AlgorithmKey, jwa.RS256) + if err != nil { + t.Fatalf("Failed to set alg: %s", err.Error()) + } + signature, err := jws.SignWithOption(payload, jwa.RS256, key) + if err != nil { + t.Fatalf("Failed to sign message: %s", err.Error()) + } + + _, err = jws.VerifyWithJWKSet(signature, &jwk.Set{Keys: []jwk.Key{jwkKey}}) + if err != nil { + t.Fatalf("Failed to verify with JWKSet: %s", err.Error()) + } + + verified, err := jws.VerifyWithJWK(signature, jwkKey) + if err != nil { + t.Fatalf("Failed to verify with JWK: %s", err.Error()) + } + + if !bytes.Equal(payload, verified) { + t.Fatalf("Mismatched payload (%s):(%s)", payload, verified) + } +} + +func TestRoundtrip_RSACompact(t *testing.T) { + payload := []byte("Hello, World!") + for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512} { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("Failed to generate key: %s", err.Error()) + } + + buf, err := jws.SignWithOption(payload, alg, key) + if err != nil { + t.Fatalf("Failed to sign message: %s", err.Error()) + } + + verified, err := jws.Verify(buf, alg, &key.PublicKey) + if err != nil { + t.Fatalf("Failed to verify signature: %s", err.Error()) + } + + if !bytes.Equal(payload, verified) { + t.Fatalf("Mismatched payloads (%s):(%s)", payload, verified) + } + } +} + +func TestEncode(t *testing.T) { + // HS256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.1 works + t.Run("HS256Compact", func(t *testing.T) { + const hdr = `{"typ":"JWT",` + "\r\n" + ` "alg":"HS256"}` + const hmacKey = `AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow` + const expectedCompact = `eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk` + + hmacKeyDecoded, err := base64.RawURLEncoding.DecodeString(hmacKey) + if err != nil { + t.Fatalf("Failed to decode HMAC Key: %s", err.Error()) + } + + hdrBuf := base64.RawURLEncoding.EncodeToString([]byte(hdr)) + payload := base64.RawURLEncoding.EncodeToString([]byte(examplePayload)) + signingInput := strings.Join( + []string{ + hdrBuf, + payload, + }, ".", + ) + + signer, err := sign.New(jwa.HS256) + if err != nil { + t.Fatalf("Failed to create HMAC signer: %s", err.Error()) + } + + signature, err := signer.Sign([]byte(signingInput), hmacKeyDecoded) + if err != nil { + t.Fatalf("Failed to sign input: %s", err.Error()) + } + encSignature := base64.RawURLEncoding.EncodeToString(signature) + realizedCompact := strings.Join( + []string{ + signingInput, + encSignature, + }, ".", + ) + + if expectedCompact != realizedCompact { + t.Fatal("Mismatched compact serializations") + } + + msg, err := jws.ParseString(realizedCompact) + if err != nil { + t.Fatalf("Failed to parse realized serialization: %s", err.Error()) + } + + signatures := msg.GetSignatures() + if len(signatures) != 1 { + t.Fatalf("Invalid number of signatures: %d", len(signatures)) + } + + algorithm := signatures[0].ProtectedHeaders().GetAlgorithm() + if algorithm != jwa.HS256 { + t.Fatal("Algorithm in header does not match") + } + + v, err := verify.New(jwa.HS256) + if err != nil { + t.Fatalf("Failed to create verifier: %s", err.Error()) + } + + err = v.Verify([]byte(signingInput), signature, hmacKeyDecoded) + if err != nil { + t.Fatalf("Message verification failed: %s", err.Error()) + } + }) + t.Run("HS256CompactLiteral", func(t *testing.T) { + const hdr = `{"typ":"JWT",` + "\r\n" + ` "alg":"HS256"}` + const jwkSrc = `{ +"kty":"oct", +"k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow" +}` + + hdrBytes := []byte(hdr) + + standardHeaders := &jws.StandardHeaders{} + err := json.Unmarshal(hdrBytes, standardHeaders) + if err != nil { + t.Fatal("Failed to parse Protected header") + } + alg := standardHeaders.GetAlgorithm() + + keys, err := jwk.ParseString(jwkSrc) + if err != nil { + t.Fatalf("Failed to parse JWK: %s", err.Error()) + } + key, err := keys.Keys[0].Materialize() + if err != nil { + t.Fatal("Failed to parse key") + } + var jwsCompact []byte + jwsCompact, err = jws.SignLiteral([]byte(examplePayload), alg, key, hdrBytes, rand.Reader) + if err != nil { + t.Fatal("Failed to sign message") + } + + msg, err := jws.ParseByte(jwsCompact) + if err != nil { + t.Fatalf("Failed to parse compact serialization: %s", err.Error()) + } + signatures := msg.GetSignatures() + + algorithm := signatures[0].ProtectedHeaders().GetAlgorithm() + if algorithm != alg { + t.Fatal("Algorithm in header does not match") + } + + v, err := verify.New(alg) + if err != nil { + t.Fatalf("Failed to create verifier: %s", err.Error()) + } + hdrBuf := base64.RawURLEncoding.EncodeToString([]byte(hdr)) + payload := base64.RawURLEncoding.EncodeToString([]byte(examplePayload)) + + signingInput := strings.Join( + []string{ + hdrBuf, + payload, + }, + ".", + ) + + err = v.Verify([]byte(signingInput), signatures[0].GetSignature(), key) + if err != nil { + return + } + }) + t.Run("ES512Compact", func(t *testing.T) { + // ES256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.3 works + hdr := []byte{123, 34, 97, 108, 103, 34, 58, 34, 69, 83, 53, 49, 50, 34, 125} + const jwkSrc = `{ +"kty":"EC", +"crv":"P-521", +"x":"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk", +"y":"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2", +"d":"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C" +}` + + // "GetPayload" + jwsPayload := []byte{80, 97, 121, 108, 111, 97, 100} + + standardHeaders := &jws.StandardHeaders{} + err := json.Unmarshal(hdr, standardHeaders) + if err != nil { + t.Fatal("Failed to parse header") + } + alg := standardHeaders.GetAlgorithm() + + keys, err := jwk.ParseString(jwkSrc) + if err != nil { + t.Fatalf("Failed to parse JWK: %s", err.Error()) + } + key, err := keys.Keys[0].Materialize() + if err != nil { + t.Fatal("Failed to create private key") + } + var jwsCompact []byte + jwsCompact, err = jws.SignWithOption(jwsPayload, alg, key) + if err != nil { + t.Fatal("Failed to sign message") + } + + // Verify with standard ecdsa library + parts, err := jws.SplitCompact(string(jwsCompact)) + if err != nil { + t.Fatal("Failed to split compact JWT") + } + decodedJwsSignature, err := base64.RawURLEncoding.DecodeString(parts[2]) + if err != nil { + t.Fatal("Failed to sign message") + } + r, s := &big.Int{}, &big.Int{} + n := len(decodedJwsSignature) / 2 + r.SetBytes(decodedJwsSignature[:n]) + s.SetBytes(decodedJwsSignature[n:]) + signingHdr := base64.RawURLEncoding.EncodeToString(hdr) + signingPayload := base64.RawURLEncoding.EncodeToString(jwsPayload) + jwsSigningInput := strings.Join( + []string{ + signingHdr, + signingPayload, + }, ".", + ) + hashed512 := sha512.Sum512([]byte(jwsSigningInput)) + ecdsaPrivateKey := key.(*ecdsa.PrivateKey) + verified := ecdsa.Verify(&ecdsaPrivateKey.PublicKey, hashed512[:], r, s) + if !verified { + t.Fatal("Failed to verify message") + } + + // Verify with API library + + publicKey, err := jwk.GetPublicKey(key) + if err != nil { + t.Fatal("Failed to get public from private key") + } + verifiedPayload, err := jws.Verify(jwsCompact, alg, publicKey) + if err != nil || string(verifiedPayload) != string(jwsPayload) { + t.Fatal("Failed to verify message") + } + }) + t.Run("RS256Compact", func(t *testing.T) { + // RS256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.2 works + const hdr = `{"alg":"RS256"}` + const expected = `eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw` + const jwkSrc = `{ + "kty":"RSA", + "n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", + "e":"AQAB", + "d":"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ", + "p":"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc", + "q":"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc", + "dp":"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0", + "dq":"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU", + "qi":"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U" + }` + + var jwkKeySet *jwk.Set + jwkKeySet, err := jwk.ParseString(jwkSrc) + if err != nil { + t.Fatalf("Failed to parse JWK: %s", err.Error()) + } + signer, err := sign.New(jwa.RS256) + if err != nil { + t.Fatalf("Failed to create signer: %s", err.Error()) + } + + hdrStr := base64.RawURLEncoding.EncodeToString([]byte(hdr)) + payload := base64.RawURLEncoding.EncodeToString([]byte(examplePayload)) + + signingInput := strings.Join( + []string{ + hdrStr, + payload, + }, ".", + ) + privateKey, err := jwkKeySet.Keys[0].Materialize() + if err != nil { + t.Fatalf("Failed to materialize key: %s", err.Error()) + } + signature, err := signer.Sign([]byte(signingInput), privateKey) + if err != nil { + t.Fatalf("Failed to sign message: %s", err.Error()) + } + encSignature := base64.RawURLEncoding.EncodeToString(signature) + + encoded := strings.Join( + []string{ + signingInput, + encSignature, + }, ".", + ) + + if expected != encoded { + t.Fatal("Mismatched compact serialization") + } + + msg, err := jws.ParseString(encoded) + if err != nil { + t.Fatalf("Failed to parse JWS: %s", err.Error()) + } + + signatures := msg.GetSignatures() + + algorithm := signatures[0].ProtectedHeaders().GetAlgorithm() + if algorithm != jwa.RS256 { + t.Fatal("Algorithm in header does not match") + } + + v, err := verify.New(jwa.RS256) + if err != nil { + t.Fatalf("Failed to create verifier: %s", err.Error()) + } + publicKey, err := jwk.GetPublicKey(privateKey) + if err != nil { + t.Fatalf("Failed to get public key: %s", err.Error()) + } + + err = v.Verify([]byte(signingInput), signature, publicKey) + if err != nil { + t.Fatalf("Message verification failed: %s", err.Error()) + } + }) + t.Run("ES256Compact", func(t *testing.T) { + // ES256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.3 works + const hdr = `{"alg":"ES256"}` + const jwkSrc = `{ + "kty":"EC", + "crv":"P-256", + "x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", + "d":"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI" + }` + + var jwkKeySet *jwk.Set + jwkKeySet, err := jwk.ParseString(jwkSrc) + if err != nil { + t.Fatalf("Failed to parse JWK: %s", err.Error()) + } + + signer, err := sign.New(jwa.ES256) + if err != nil { + t.Fatalf("Failed to create signer: %s", err.Error()) + } + + hdrStr := base64.RawURLEncoding.EncodeToString([]byte(hdr)) + payload := base64.RawURLEncoding.EncodeToString([]byte(examplePayload)) + + signingInput := strings.Join( + []string{ + hdrStr, + payload, + }, ".", + ) + + privateKey, err := jwkKeySet.Keys[0].Materialize() + if err != nil { + t.Fatalf("Failed to materialize key: %s", err.Error()) + } + signature, err := signer.Sign([]byte(signingInput), privateKey) + if err != nil { + t.Fatalf("Failed to sign message: %s", err.Error()) + } + encSignature := base64.RawURLEncoding.EncodeToString(signature) + + encoded := strings.Join( + []string{ + signingInput, + encSignature, + }, ".", + ) + + // The Signature contains random factor, so unfortunately we can't match + // the output against a fixed expected outcome. We'll wave doing an + // exact match, and just try to verify using the Signature + + msg, err := jws.ParseString(encoded) + if err != nil { + t.Fatalf("Failed to parse JWS: %s", err.Error()) + } + + signatures := msg.GetSignatures() + + algorithm := signatures[0].ProtectedHeaders().GetAlgorithm() + if algorithm != jwa.ES256 { + t.Fatal("Algorithm in header does not match") + } + + v, err := verify.New(jwa.ES256) + if err != nil { + t.Fatalf("Failed to create verifier: %s", err.Error()) + } + publicKey, err := jwk.GetPublicKey(privateKey) + if err != nil { + t.Fatalf("Failed to get public key: %s", err.Error()) + } + err = v.Verify([]byte(signingInput), signature, publicKey) + if err != nil { + t.Fatalf("Message verification failed: %s", err.Error()) + } + }) +} + +func TestDecode_ES384Compact_NoSigTrim(t *testing.T) { + incoming := "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCIsImtpZCI6IjE5MzFmZTQ0YmFhMWNhZTkyZWUzNzYzOTQ0MDU1OGMwODdlMTRlNjk5ZWU5NjVhM2Q1OGU1MmU2NGY4MDE0NWIifQ.eyJpc3MiOiJicmt0LWNsaS0xLjAuN3ByZTEiLCJpYXQiOjE0ODQ2OTU1MjAsImp0aSI6IjgxYjczY2Y3In0.DdFi0KmPHSv4PfIMGcWGMSRLmZsfRPQ3muLFW6Ly2HpiLFFQWZ0VEanyrFV263wjlp3udfedgw_vrBLz3XC8CkbvCo_xeHMzaTr_yfhjoheSj8gWRLwB-22rOnUX_M0A" + const jwkSrc = `{ + "kty":"EC", + "crv":"P-384", + "x":"YHVZ4gc1RDoqxKm4NzaN_Y1r7R7h3RM3JMteC478apSKUiLVb4UNytqWaLoE6ygH", + "y":"CRKSqP-aYTIsqJfg_wZEEYUayUR5JhZaS2m4NLk2t1DfXZgfApAJ2lBO0vWKnUMp" + }` + + var jwkKeySet *jwk.Set + jwkKeySet, err := jwk.ParseString(jwkSrc) + if err != nil { + t.Fatalf("Failed to parse JWK: %s", err.Error()) + } + v, err := verify.New(jwa.ES384) + if err != nil { + t.Fatalf("Failed to create verifier: %s", err.Error()) + } + + parts, err := jws.SplitCompact(incoming) + if err != nil { + t.Fatalf("Failed to spli compact serialization: %s", err.Error()) + } + + decodedSignature, err := base64.RawURLEncoding.DecodeString(parts[2]) + if err != nil { + t.Fatalf("Failed to decode signature: %s", err.Error()) + } + publicKey, err := jwkKeySet.Keys[0].Materialize() + if err != nil { + t.Fatalf("Failed to materialize keys: %v", err) + } + signingInput := strings.Join( + []string{ + parts[0], + parts[1], + }, ".", + ) + + err = v.Verify([]byte(signingInput), decodedSignature, publicKey) + if err != nil { + t.Fatalf("Message verification failed: %s", err.Error()) + } +} + +func TestSignErrors(t *testing.T) { + + t.Run("Bad algorithm", func(t *testing.T) { + _, err := jws.SignWithOption([]byte(nil), jwa.SignatureAlgorithm("FooBar"), nil) + if err == nil { + t.Fatal("Unknown algorithm should return error") + } + }) + t.Run("No private key", func(t *testing.T) { + _, err := jws.SignWithOption([]byte{'a', 'b', 'c'}, jwa.RS256, nil) + if err == nil { + t.Fatal("SignWithOption with no private key should return error") + } + }) + t.Run("RSA verify with no public key", func(t *testing.T) { + _, err := jws.Verify([]byte(nil), jwa.RS256, nil) + if err == nil { + t.Fatal("Verify with no private key should return error") + } + }) + t.Run("Invalid signature algorithm", func(t *testing.T) { + _, err := jws.SignLiteral([]byte("payload"), jwa.SignatureAlgorithm("dummy"), nil, []byte("header"), rand.Reader) + if err == nil { + t.Fatal("JWS signing should have failed") + } + }) + t.Run("Invalid signature algorithm", func(t *testing.T) { + _, err := jws.SignLiteral([]byte("payload"), jwa.SignatureAlgorithm("dummy"), nil, []byte("header"), rand.Reader) + if err == nil { + t.Fatal("JWS signing should have failed") + } + }) +} + +func TestVerifyErrors(t *testing.T) { + + t.Run("Invalid compact serialization", func(t *testing.T) { + message := []byte("some.message") + _, err := jws.Verify(message, jwa.ES256, nil) + if err == nil { + t.Fatal("JWS verification should have failed") + } + }) + t.Run("Invalid signature encoding", func(t *testing.T) { + message := []byte("some.message.c29tZSBtZXNz?Wdl") + _, err := jws.Verify(message, jwa.ES256, nil) + if err == nil { + t.Fatal("JWS verification should have failed") + } + }) + t.Run("Invalid Key", func(t *testing.T) { + rsaPublicKey := &jwk.RSAPublicKey{} + _, err := jws.VerifyWithJWK([]byte("some.message.signed"), rsaPublicKey) + if err == nil { + t.Fatal("JWS verification should have failed") + } + }) +} diff --git a/third_party/opa/internal/jwx/jws/message.go b/third_party/opa/internal/jwx/jws/message.go new file mode 100644 index 000000000000..1366a3d7be9f --- /dev/null +++ b/third_party/opa/internal/jwx/jws/message.go @@ -0,0 +1,26 @@ +package jws + +// PublicHeaders returns the public headers in a JWS +func (s Signature) PublicHeaders() Headers { + return s.Headers +} + +// ProtectedHeaders returns the protected headers in a JWS +func (s Signature) ProtectedHeaders() Headers { + return s.Protected +} + +// GetSignature returns the signature in a JWS +func (s Signature) GetSignature() []byte { + return s.Signature +} + +// GetPayload returns the payload in a JWS +func (m Message) GetPayload() []byte { + return m.Payload +} + +// GetSignatures returns the all signatures in a JWS +func (m Message) GetSignatures() []*Signature { + return m.Signatures +} diff --git a/third_party/opa/internal/jwx/jws/sign/ecdsa.go b/third_party/opa/internal/jwx/jws/sign/ecdsa.go new file mode 100644 index 000000000000..5f3e8accad55 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/sign/ecdsa.go @@ -0,0 +1,90 @@ +package sign + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rand" + "errors" + "fmt" + "io" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +var ecdsaSignFuncs = map[jwa.SignatureAlgorithm]ecdsaSignFunc{} + +func init() { + algs := map[jwa.SignatureAlgorithm]crypto.Hash{ + jwa.ES256: crypto.SHA256, + jwa.ES384: crypto.SHA384, + jwa.ES512: crypto.SHA512, + } + + for alg, h := range algs { + ecdsaSignFuncs[alg] = makeECDSASignFunc(h) + } +} + +func makeECDSASignFunc(hash crypto.Hash) ecdsaSignFunc { + return ecdsaSignFunc(func(payload []byte, key *ecdsa.PrivateKey, rnd io.Reader) ([]byte, error) { + curveBits := key.Curve.Params().BitSize + keyBytes := curveBits / 8 + // Curve bits do not need to be a multiple of 8. + if curveBits%8 > 0 { + keyBytes++ + } + h := hash.New() + h.Write(payload) + r, s, err := ecdsa.Sign(rnd, key, h.Sum(nil)) + if err != nil { + return nil, fmt.Errorf("failed to sign payload using ecdsa: %w", err) + } + + rBytes := r.Bytes() + rBytesPadded := make([]byte, keyBytes) + copy(rBytesPadded[keyBytes-len(rBytes):], rBytes) + + sBytes := s.Bytes() + sBytesPadded := make([]byte, keyBytes) + copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) + + out := append(rBytesPadded, sBytesPadded...) + return out, nil + }) +} + +func newECDSA(alg jwa.SignatureAlgorithm) (*ECDSASigner, error) { + signfn, ok := ecdsaSignFuncs[alg] + if !ok { + return nil, fmt.Errorf("unsupported algorithm while trying to create ECDSA signer: %s", alg) + } + + return &ECDSASigner{ + alg: alg, + sign: signfn, + }, nil +} + +// Algorithm returns the signer algorithm +func (s ECDSASigner) Algorithm() jwa.SignatureAlgorithm { + return s.alg +} + +// SignWithRand signs payload with a ECDSA private key and a provided randomness +// source (such as `rand.Reader`). +func (s ECDSASigner) SignWithRand(payload []byte, key any, r io.Reader) ([]byte, error) { + if key == nil { + return nil, errors.New("missing private key while signing payload") + } + + privateKey, ok := key.(*ecdsa.PrivateKey) + if !ok { + return nil, fmt.Errorf("invalid key type %T. *ecdsa.PrivateKey is required", key) + } + return s.sign(payload, privateKey, r) +} + +// Sign signs payload with a ECDSA private key +func (s ECDSASigner) Sign(payload []byte, key any) ([]byte, error) { + return s.SignWithRand(payload, key, rand.Reader) +} diff --git a/third_party/opa/internal/jwx/jws/sign/ecdsa_test.go b/third_party/opa/internal/jwx/jws/sign/ecdsa_test.go new file mode 100644 index 000000000000..d7e3e1d804f4 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/sign/ecdsa_test.go @@ -0,0 +1,35 @@ +package sign + +import ( + "testing" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +func TestECDSASign(t *testing.T) { + type dummyStruct struct { + dummy1 int + dummy2 float64 + } + dummy := &dummyStruct{1, 3.4} + t.Run("ECDSA Creation Error", func(t *testing.T) { + _, err := newECDSA(jwa.HS256) + if err == nil { + t.Fatal("ECDSA Object creation should fail") + } + }) + t.Run("ECDSA Sign Error", func(t *testing.T) { + signer, err := newECDSA(jwa.ES512) + if err != nil { + t.Fatalf("Signer creation failure: %v", jwa.ES512) + } + _, err = signer.Sign([]byte("payload"), dummy) + if err == nil { + t.Fatal("HMAC Object creation should fail") + } + _, err = signer.Sign([]byte("payload"), []byte("")) + if err == nil { + t.Fatal("HMAC Object creation should fail") + } + }) +} diff --git a/third_party/opa/internal/jwx/jws/sign/hmac.go b/third_party/opa/internal/jwx/jws/sign/hmac.go new file mode 100644 index 000000000000..de541755ef6b --- /dev/null +++ b/third_party/opa/internal/jwx/jws/sign/hmac.go @@ -0,0 +1,66 @@ +package sign + +import ( + "crypto/hmac" + "crypto/sha256" + "crypto/sha512" + "errors" + "fmt" + "hash" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +var hmacSignFuncs = map[jwa.SignatureAlgorithm]hmacSignFunc{} + +func init() { + algs := map[jwa.SignatureAlgorithm]func() hash.Hash{ + jwa.HS256: sha256.New, + jwa.HS384: sha512.New384, + jwa.HS512: sha512.New, + } + + for alg, h := range algs { + hmacSignFuncs[alg] = makeHMACSignFunc(h) + + } +} + +func newHMAC(alg jwa.SignatureAlgorithm) (*HMACSigner, error) { + signer, ok := hmacSignFuncs[alg] + if !ok { + return nil, fmt.Errorf(`unsupported algorithm while trying to create HMAC signer: %s`, alg) + } + + return &HMACSigner{ + alg: alg, + sign: signer, + }, nil +} + +func makeHMACSignFunc(hfunc func() hash.Hash) hmacSignFunc { + return hmacSignFunc(func(payload []byte, key []byte) ([]byte, error) { + h := hmac.New(hfunc, key) + h.Write(payload) + return h.Sum(nil), nil + }) +} + +// Algorithm returns the signer algorithm +func (s HMACSigner) Algorithm() jwa.SignatureAlgorithm { + return s.alg +} + +// Sign signs payload with a Symmetric key +func (s HMACSigner) Sign(payload []byte, key any) ([]byte, error) { + hmackey, ok := key.([]byte) + if !ok { + return nil, fmt.Errorf(`invalid key type %T. []byte is required`, key) + } + + if len(hmackey) == 0 { + return nil, errors.New(`missing key while signing payload`) + } + + return s.sign(payload, hmackey) +} diff --git a/third_party/opa/internal/jwx/jws/sign/hmac_test.go b/third_party/opa/internal/jwx/jws/sign/hmac_test.go new file mode 100644 index 000000000000..cd30cf93b130 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/sign/hmac_test.go @@ -0,0 +1,35 @@ +package sign + +import ( + "testing" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +func TestHMACSign(t *testing.T) { + type dummyStruct struct { + dummy1 int + dummy2 float64 + } + dummy := &dummyStruct{1, 3.4} + t.Run("HMAC Creation Error", func(t *testing.T) { + _, err := newHMAC(jwa.ES256) + if err == nil { + t.Fatal("HMAC Object creation should fail") + } + }) + t.Run("HMAC Sign Error", func(t *testing.T) { + signer, err := newHMAC(jwa.HS512) + if err != nil { + t.Fatalf("Signer creation failure: %v", jwa.HS512) + } + _, err = signer.Sign([]byte("payload"), dummy) + if err == nil { + t.Fatal("HMAC Object creation should fail") + } + _, err = signer.Sign([]byte("payload"), []byte("")) + if err == nil { + t.Fatal("HMAC Object creation should fail") + } + }) +} diff --git a/third_party/opa/internal/jwx/jws/sign/interface.go b/third_party/opa/internal/jwx/jws/sign/interface.go new file mode 100644 index 000000000000..25b592ed4e42 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/sign/interface.go @@ -0,0 +1,46 @@ +package sign + +import ( + "crypto/ecdsa" + "crypto/rsa" + "io" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +// Signer provides a common interface for supported alg signing methods +type Signer interface { + // Sign creates a signature for the given `payload`. + // `key` is the key used for signing the payload, and is usually + // the private key type associated with the signature method. For example, + // for `jwa.RSXXX` and `jwa.PSXXX` types, you need to pass the + // `*"crypto/rsa".PrivateKey` type. + // Check the documentation for each signer for details + Sign(payload []byte, key any) ([]byte, error) + + Algorithm() jwa.SignatureAlgorithm +} + +type rsaSignFunc func([]byte, *rsa.PrivateKey) ([]byte, error) + +// RSASigner uses crypto/rsa to sign the payloads. +type RSASigner struct { + alg jwa.SignatureAlgorithm + sign rsaSignFunc +} + +type ecdsaSignFunc func([]byte, *ecdsa.PrivateKey, io.Reader) ([]byte, error) + +// ECDSASigner uses crypto/ecdsa to sign the payloads. +type ECDSASigner struct { + alg jwa.SignatureAlgorithm + sign ecdsaSignFunc +} + +type hmacSignFunc func([]byte, []byte) ([]byte, error) + +// HMACSigner uses crypto/hmac to sign the payloads. +type HMACSigner struct { + alg jwa.SignatureAlgorithm + sign hmacSignFunc +} diff --git a/third_party/opa/internal/jwx/jws/sign/rsa.go b/third_party/opa/internal/jwx/jws/sign/rsa.go new file mode 100644 index 000000000000..a671b7318ad1 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/sign/rsa.go @@ -0,0 +1,97 @@ +package sign + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "errors" + "fmt" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +var rsaSignFuncs = map[jwa.SignatureAlgorithm]rsaSignFunc{} + +func init() { + algs := map[jwa.SignatureAlgorithm]struct { + Hash crypto.Hash + SignFunc func(crypto.Hash) rsaSignFunc + }{ + jwa.RS256: { + Hash: crypto.SHA256, + SignFunc: makeSignPKCS1v15, + }, + jwa.RS384: { + Hash: crypto.SHA384, + SignFunc: makeSignPKCS1v15, + }, + jwa.RS512: { + Hash: crypto.SHA512, + SignFunc: makeSignPKCS1v15, + }, + jwa.PS256: { + Hash: crypto.SHA256, + SignFunc: makeSignPSS, + }, + jwa.PS384: { + Hash: crypto.SHA384, + SignFunc: makeSignPSS, + }, + jwa.PS512: { + Hash: crypto.SHA512, + SignFunc: makeSignPSS, + }, + } + + for alg, item := range algs { + rsaSignFuncs[alg] = item.SignFunc(item.Hash) + } +} + +func makeSignPKCS1v15(hash crypto.Hash) rsaSignFunc { + return rsaSignFunc(func(payload []byte, key *rsa.PrivateKey) ([]byte, error) { + h := hash.New() + h.Write(payload) + return rsa.SignPKCS1v15(rand.Reader, key, hash, h.Sum(nil)) + }) +} + +func makeSignPSS(hash crypto.Hash) rsaSignFunc { + return rsaSignFunc(func(payload []byte, key *rsa.PrivateKey) ([]byte, error) { + h := hash.New() + h.Write(payload) + return rsa.SignPSS(rand.Reader, key, hash, h.Sum(nil), &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + }) + }) +} + +func newRSA(alg jwa.SignatureAlgorithm) (*RSASigner, error) { + signfn, ok := rsaSignFuncs[alg] + if !ok { + return nil, fmt.Errorf(`unsupported algorithm while trying to create RSA signer: %s`, alg) + } + return &RSASigner{ + alg: alg, + sign: signfn, + }, nil +} + +// Algorithm returns the signer algorithm +func (s RSASigner) Algorithm() jwa.SignatureAlgorithm { + return s.alg +} + +// Sign creates a signature using crypto/rsa. key must be a non-nil instance of +// `*"crypto/rsa".PrivateKey`. +func (s RSASigner) Sign(payload []byte, key any) ([]byte, error) { + if key == nil { + return nil, errors.New(`missing private key while signing payload`) + } + rsakey, ok := key.(*rsa.PrivateKey) + if !ok { + return nil, fmt.Errorf(`invalid key type %T. *rsa.PrivateKey is required`, key) + } + + return s.sign(payload, rsakey) +} diff --git a/third_party/opa/internal/jwx/jws/sign/sign.go b/third_party/opa/internal/jwx/jws/sign/sign.go new file mode 100644 index 000000000000..c1432236fb76 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/sign/sign.go @@ -0,0 +1,66 @@ +package sign + +import ( + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +// New creates a signer that signs payloads using the given signature algorithm. +func New(alg jwa.SignatureAlgorithm) (Signer, error) { + switch alg { + case jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512: + return newRSA(alg) + case jwa.ES256, jwa.ES384, jwa.ES512: + return newECDSA(alg) + case jwa.HS256, jwa.HS384, jwa.HS512: + return newHMAC(alg) + default: + return nil, fmt.Errorf(`unsupported signature algorithm %s`, alg) + } +} + +// GetSigningKey returns a *rsa.PrivateKey or *ecdsa.PrivateKey typically encoded in PEM blocks of type "RSA PRIVATE KEY" +// or "EC PRIVATE KEY" for RSA and ECDSA family of algorithms. +// For HMAC family, it return a []byte value +func GetSigningKey(key string, alg jwa.SignatureAlgorithm) (any, error) { + switch alg { + case jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512: + block, _ := pem.Decode([]byte(key)) + if block == nil { + return nil, errors.New("failed to parse PEM block containing the key") + } + + priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + pkcs8priv, err2 := x509.ParsePKCS8PrivateKey(block.Bytes) + if err2 != nil { + return nil, fmt.Errorf("error parsing private key (%v), (%v)", err, err2) + } + return pkcs8priv, nil + } + return priv, nil + case jwa.ES256, jwa.ES384, jwa.ES512: + block, _ := pem.Decode([]byte(key)) + if block == nil { + return nil, errors.New("failed to parse PEM block containing the key") + } + + priv, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + pkcs8priv, err2 := x509.ParsePKCS8PrivateKey(block.Bytes) + if err2 != nil { + return nil, fmt.Errorf("error parsing private key (%v), (%v)", err, err2) + } + return pkcs8priv, nil + } + return priv, nil + case jwa.HS256, jwa.HS384, jwa.HS512: + return []byte(key), nil + default: + return nil, fmt.Errorf("unsupported signature algorithm: %s", alg) + } +} diff --git a/third_party/opa/internal/jwx/jws/verify/ecdsa.go b/third_party/opa/internal/jwx/jws/verify/ecdsa.go new file mode 100644 index 000000000000..ba32078ac9e6 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/verify/ecdsa.go @@ -0,0 +1,67 @@ +package verify + +import ( + "crypto" + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +var ecdsaVerifyFuncs = map[jwa.SignatureAlgorithm]ecdsaVerifyFunc{} + +func init() { + algs := map[jwa.SignatureAlgorithm]crypto.Hash{ + jwa.ES256: crypto.SHA256, + jwa.ES384: crypto.SHA384, + jwa.ES512: crypto.SHA512, + } + + for alg, h := range algs { + ecdsaVerifyFuncs[alg] = makeECDSAVerifyFunc(h) + } +} + +func makeECDSAVerifyFunc(hash crypto.Hash) ecdsaVerifyFunc { + return ecdsaVerifyFunc(func(payload []byte, signature []byte, key *ecdsa.PublicKey) error { + + r, s := &big.Int{}, &big.Int{} + n := len(signature) / 2 + r.SetBytes(signature[:n]) + s.SetBytes(signature[n:]) + + h := hash.New() + h.Write(payload) + + if !ecdsa.Verify(key, h.Sum(nil), r, s) { + return errors.New(`failed to verify signature using ecdsa`) + } + return nil + }) +} + +func newECDSA(alg jwa.SignatureAlgorithm) (*ECDSAVerifier, error) { + verifyfn, ok := ecdsaVerifyFuncs[alg] + if !ok { + return nil, fmt.Errorf(`unsupported algorithm while trying to create ECDSA verifier: %s`, alg) + } + + return &ECDSAVerifier{ + verify: verifyfn, + }, nil +} + +// Verify checks whether the signature for a given input and key is correct +func (v ECDSAVerifier) Verify(payload []byte, signature []byte, key any) error { + if key == nil { + return errors.New(`missing public key while verifying payload`) + } + ecdsakey, ok := key.(*ecdsa.PublicKey) + if !ok { + return fmt.Errorf(`invalid key type %T. *ecdsa.PublicKey is required`, key) + } + + return v.verify(payload, signature, ecdsakey) +} diff --git a/third_party/opa/internal/jwx/jws/verify/ecdsa_test.go b/third_party/opa/internal/jwx/jws/verify/ecdsa_test.go new file mode 100644 index 000000000000..4526d6e37670 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/verify/ecdsa_test.go @@ -0,0 +1,35 @@ +package verify + +import ( + "testing" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +func TestECDSAVerify(t *testing.T) { + type dummyStruct struct { + dummy1 int + dummy2 float64 + } + dummy := &dummyStruct{1, 3.4} + t.Run("ECDSA Verifier Creation Error", func(t *testing.T) { + _, err := newECDSA(jwa.HS256) + if err == nil { + t.Fatal("ECDSA Verifier Object creation should fail") + } + }) + t.Run("ECDSA Verifier Sign Error", func(t *testing.T) { + pVerifier, err := newECDSA(jwa.ES512) + if err != nil { + t.Fatalf("Signer creation failure: %v", jwa.ES512) + } + err = pVerifier.Verify([]byte("payload"), []byte("signature"), dummy) + if err == nil { + t.Fatal("ECDSA Verification should fail") + } + err = pVerifier.Verify([]byte("payload"), []byte("signature"), nil) + if err == nil { + t.Fatal("ECDSA Verification should fail") + } + }) +} diff --git a/third_party/opa/internal/jwx/jws/verify/hmac.go b/third_party/opa/internal/jwx/jws/verify/hmac.go new file mode 100644 index 000000000000..25651a0f8d2b --- /dev/null +++ b/third_party/opa/internal/jwx/jws/verify/hmac.go @@ -0,0 +1,33 @@ +package verify + +import ( + "crypto/hmac" + "errors" + "fmt" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jws/sign" +) + +func newHMAC(alg jwa.SignatureAlgorithm) (*HMACVerifier, error) { + + s, err := sign.New(alg) + if err != nil { + return nil, fmt.Errorf("failed to generate HMAC signer: %w", err) + } + return &HMACVerifier{signer: s}, nil +} + +// Verify checks whether the signature for a given input and key is correct +func (v HMACVerifier) Verify(signingInput, signature []byte, key any) (err error) { + + expected, err := v.signer.Sign(signingInput, key) + if err != nil { + return fmt.Errorf("failed to generated signature: %w", err) + } + + if !hmac.Equal(signature, expected) { + return errors.New("failed to match hmac signature") + } + return nil +} diff --git a/third_party/opa/internal/jwx/jws/verify/hmac_test.go b/third_party/opa/internal/jwx/jws/verify/hmac_test.go new file mode 100644 index 000000000000..685585912bec --- /dev/null +++ b/third_party/opa/internal/jwx/jws/verify/hmac_test.go @@ -0,0 +1,32 @@ +package verify + +import ( + "testing" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +func TestHMACVerify(t *testing.T) { + type dummyStruct struct { + dummy1 int + dummy2 float64 + } + dummy := &dummyStruct{1, 3.4} + t.Run("HMAC Verifier Creation Error", func(t *testing.T) { + _, err := newHMAC(jwa.NoValue) + if err == nil { + t.Fatal("HMAC Verifier Object creation should fail") + } + }) + t.Run("HMAC Verifier Sign Error", func(t *testing.T) { + pVerifier, err := newHMAC(jwa.HS512) + if err != nil { + t.Fatalf("Signer creation failure: %v", jwa.HS512) + } + err = pVerifier.Verify([]byte("payload"), []byte("signature"), dummy) + if err == nil { + t.Fatal("HMAC Verification should fail") + } + + }) +} diff --git a/third_party/opa/internal/jwx/jws/verify/interface.go b/third_party/opa/internal/jwx/jws/verify/interface.go new file mode 100644 index 000000000000..e72c3ed7f760 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/verify/interface.go @@ -0,0 +1,39 @@ +package verify + +import ( + "crypto/ecdsa" + "crypto/rsa" + + "github.com/open-policy-agent/opa/internal/jwx/jws/sign" +) + +// Verifier provides a common interface for supported alg verification methods +type Verifier interface { + // Verify checks whether the payload and signature are valid for + // the given key. + // `key` is the key used for verifying the payload, and is usually + // the public key associated with the signature method. For example, + // for `jwa.RSXXX` and `jwa.PSXXX` types, you need to pass the + // `*"crypto/rsa".PublicKey` type. + // Check the documentation for each verifier for details + Verify(payload []byte, signature []byte, key any) error +} + +type rsaVerifyFunc func([]byte, []byte, *rsa.PublicKey) error + +// RSAVerifier implements the Verifier interface +type RSAVerifier struct { + verify rsaVerifyFunc +} + +type ecdsaVerifyFunc func([]byte, []byte, *ecdsa.PublicKey) error + +// ECDSAVerifier implements the Verifier interface +type ECDSAVerifier struct { + verify ecdsaVerifyFunc +} + +// HMACVerifier implements the Verifier interface +type HMACVerifier struct { + signer sign.Signer +} diff --git a/third_party/opa/internal/jwx/jws/verify/rsa.go b/third_party/opa/internal/jwx/jws/verify/rsa.go new file mode 100644 index 000000000000..163ff84bcf11 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/verify/rsa.go @@ -0,0 +1,88 @@ +package verify + +import ( + "crypto" + "crypto/rsa" + "errors" + "fmt" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +var rsaVerifyFuncs = map[jwa.SignatureAlgorithm]rsaVerifyFunc{} + +func init() { + algs := map[jwa.SignatureAlgorithm]struct { + Hash crypto.Hash + VerifyFunc func(crypto.Hash) rsaVerifyFunc + }{ + jwa.RS256: { + Hash: crypto.SHA256, + VerifyFunc: makeVerifyPKCS1v15, + }, + jwa.RS384: { + Hash: crypto.SHA384, + VerifyFunc: makeVerifyPKCS1v15, + }, + jwa.RS512: { + Hash: crypto.SHA512, + VerifyFunc: makeVerifyPKCS1v15, + }, + jwa.PS256: { + Hash: crypto.SHA256, + VerifyFunc: makeVerifyPSS, + }, + jwa.PS384: { + Hash: crypto.SHA384, + VerifyFunc: makeVerifyPSS, + }, + jwa.PS512: { + Hash: crypto.SHA512, + VerifyFunc: makeVerifyPSS, + }, + } + + for alg, item := range algs { + rsaVerifyFuncs[alg] = item.VerifyFunc(item.Hash) + } +} + +func makeVerifyPKCS1v15(hash crypto.Hash) rsaVerifyFunc { + return rsaVerifyFunc(func(payload, signature []byte, key *rsa.PublicKey) error { + h := hash.New() + h.Write(payload) + return rsa.VerifyPKCS1v15(key, hash, h.Sum(nil), signature) + }) +} + +func makeVerifyPSS(hash crypto.Hash) rsaVerifyFunc { + return rsaVerifyFunc(func(payload, signature []byte, key *rsa.PublicKey) error { + h := hash.New() + h.Write(payload) + return rsa.VerifyPSS(key, hash, h.Sum(nil), signature, nil) + }) +} + +func newRSA(alg jwa.SignatureAlgorithm) (*RSAVerifier, error) { + verifyfn, ok := rsaVerifyFuncs[alg] + if !ok { + return nil, fmt.Errorf(`unsupported algorithm while trying to create RSA verifier: %s`, alg) + } + + return &RSAVerifier{ + verify: verifyfn, + }, nil +} + +// Verify checks if a JWS is valid. +func (v RSAVerifier) Verify(payload, signature []byte, key any) error { + if key == nil { + return errors.New(`missing public key while verifying payload`) + } + rsaKey, ok := key.(*rsa.PublicKey) + if !ok { + return fmt.Errorf(`invalid key type %T. *rsa.PublicKey is required`, key) + } + + return v.verify(payload, signature, rsaKey) +} diff --git a/third_party/opa/internal/jwx/jws/verify/rsa_test.go b/third_party/opa/internal/jwx/jws/verify/rsa_test.go new file mode 100644 index 000000000000..6404faa19992 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/verify/rsa_test.go @@ -0,0 +1,35 @@ +package verify + +import ( + "testing" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +func TestRSAVerify(t *testing.T) { + type dummyStruct struct { + dummy1 int + dummy2 float64 + } + dummy := &dummyStruct{1, 3.4} + t.Run("RSA Verifier Creation Error", func(t *testing.T) { + _, err := newRSA(jwa.HS256) + if err == nil { + t.Fatal("ECDSA Verifier Object creation should fail") + } + }) + t.Run("RSA Verifier Sign Error", func(t *testing.T) { + pVerifier, err := newRSA(jwa.PS512) + if err != nil { + t.Fatalf("Signer creation failure: %v", jwa.ES512) + } + err = pVerifier.Verify([]byte("payload"), []byte("signature"), dummy) + if err == nil { + t.Fatal("RSA Verification should fail") + } + err = pVerifier.Verify([]byte("payload"), []byte("signature"), nil) + if err == nil { + t.Fatal("RSA Verification should fail") + } + }) +} diff --git a/third_party/opa/internal/jwx/jws/verify/verify.go b/third_party/opa/internal/jwx/jws/verify/verify.go new file mode 100644 index 000000000000..7370b4a2f1e3 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/verify/verify.go @@ -0,0 +1,56 @@ +package verify + +import ( + "crypto/ecdsa" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" +) + +// New creates a new JWS verifier using the specified algorithm +// and the public key +func New(alg jwa.SignatureAlgorithm) (Verifier, error) { + switch alg { + case jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512: + return newRSA(alg) + case jwa.ES256, jwa.ES384, jwa.ES512: + return newECDSA(alg) + case jwa.HS256, jwa.HS384, jwa.HS512: + return newHMAC(alg) + default: + return nil, fmt.Errorf(`unsupported signature algorithm: %s`, alg) + } +} + +// GetSigningKey returns a *rsa.PublicKey or *ecdsa.PublicKey typically encoded in PEM blocks of type "PUBLIC KEY", +// for RSA and ECDSA family of algorithms. +// For HMAC family, it return a []byte value +func GetSigningKey(key string, alg jwa.SignatureAlgorithm) (any, error) { + switch alg { + case jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512, jwa.ES256, jwa.ES384, jwa.ES512: + block, _ := pem.Decode([]byte(key)) + if block == nil { + return nil, errors.New("failed to parse PEM block containing the key") + } + + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, err + } + + switch pub := pub.(type) { + case *rsa.PublicKey, *ecdsa.PublicKey: + return pub, nil + default: + return nil, fmt.Errorf("invalid key type %T", pub) + } + case jwa.HS256, jwa.HS384, jwa.HS512: + return []byte(key), nil + default: + return nil, fmt.Errorf("unsupported signature algorithm: %s", alg) + } +} diff --git a/third_party/opa/internal/jwx/jws/verify/verify_test.go b/third_party/opa/internal/jwx/jws/verify/verify_test.go new file mode 100644 index 000000000000..35c4a6fa2036 --- /dev/null +++ b/third_party/opa/internal/jwx/jws/verify/verify_test.go @@ -0,0 +1,13 @@ +package verify + +import "testing" + +func TestVerifyErrors(t *testing.T) { + + t.Run("Invalid alg", func(t *testing.T) { + _, err := New("dummy") + if err == nil { + t.Fatal("Verifier creation should have failed") + } + }) +} diff --git a/third_party/opa/internal/lcss/README.md b/third_party/opa/internal/lcss/README.md new file mode 100644 index 000000000000..39b8d82c794f --- /dev/null +++ b/third_party/opa/internal/lcss/README.md @@ -0,0 +1,3 @@ +# Longest Common Substring + +Original source https://github.com/vmarkovtsev/go-lcss diff --git a/third_party/opa/internal/lcss/lcss.go b/third_party/opa/internal/lcss/lcss.go new file mode 100644 index 000000000000..8217a34540ad --- /dev/null +++ b/third_party/opa/internal/lcss/lcss.go @@ -0,0 +1,197 @@ +package lcss + +import "bytes" + +// LongestCommonSubstring returns the longest substring which is present in all the given strings. +// https://en.wikipedia.org/wiki/Longest_common_substring_problem +// Not to be confused with the Longest Common Subsequence. +// Complexity: +// * time: sum of `n_i*log(n_i)` where `n_i` is the length of each string. +// * space: sum of `n_i`. +// Returns a byte slice which is never a nil. +// +// ### Algorithm. +// We build suffix arrays for each of the passed string and then follow the same procedure +// as in merge sort: pick the least suffix in the lexicographical order. It is possible +// because the suffix arrays are already sorted. +// We record the last encountered suffixes from each of the strings and measure the longest +// common prefix of those at each "merge sort" step. +// The string comparisons are optimized by maintaining the char-level prefix tree of the "heads" +// of the suffix array sequences. +func LongestCommonSubstring(strs ...[]byte) []byte { + strslen := len(strs) + if strslen == 0 { + return []byte{} + } + if strslen == 1 { + return strs[0] + } + suffixes := make([][]int, strslen) + for i, str := range strs { + suffixes[i] = qsufsort(str) + } + return lcss(strs, suffixes) +} + +func lcss(strs [][]byte, suffixes [][]int) []byte { + strslen := len(strs) + if strslen == 0 { + return []byte{} + } + if strslen == 1 { + return strs[0] + } + minstrlen := len(strs[0]) // minimum length of the strings + for _, str := range strs { + if minstrlen > len(str) { + minstrlen = len(str) + } + } + heads := make([]int, strslen) // position in each suffix array + boilerplate := make([][]byte, strslen) // existing suffixes in the tree + boiling := 0 // indicates how many distinct suffix arrays are presented in `boilerplate` + var root charNode // the character tree built on the strings from `boilerplate` + lcs := []byte{} // our function's return value, `var lcss []byte` does *not* work + for { + mini := -1 + var minSuffixStr []byte + for i, head := range heads { + if head >= len(suffixes[i]) { + // this suffix array has been scanned till the end + continue + } + suffix := strs[i][suffixes[i][head]:] + if minSuffixStr == nil { + // initialize + mini = i + minSuffixStr = suffix + } else if bytes.Compare(minSuffixStr, suffix) > 0 { + // the current suffix is the smallest in the lexicographical order + mini = i + minSuffixStr = suffix + } + } + if mini == -1 { + // all heads exhausted + break + } + if boilerplate[mini] != nil { + // if we already have a suffix from this string, replace it with the new one + root.Remove(boilerplate[mini]) + } else { + // we track the number of distinct strings which have been touched + // when `boiling` becomes strslen we can start measuring the longest common prefix + boiling++ + } + boilerplate[mini] = minSuffixStr + root.Add(minSuffixStr) + heads[mini]++ + if boiling == strslen && root.LongestCommonPrefixLength() > len(lcs) { + // all heads > 0, the current common prefix of the suffixes is the longest + lcs = root.LongestCommonPrefix() + if len(lcs) == minstrlen { + // early exit - we will never find a longer substring + break + } + } + } + return lcs +} + +// charNode builds a tree of individual characters. +// `used` is the counter for collecting garbage: those nodes which have `used`=0 are removed. +// The root charNode always remains intact apart from `children`. +// The tree supports 4 operations: +// 1. Add() a new string. +// 2. Remove() an existing string which was previously Add()-ed. +// 3. LongestCommonPrefixLength(). +// 4. LongestCommonPrefix(). +type charNode struct { + char byte + children []charNode + used int +} + +// Add includes a new string into the tree. We start from the root and +// increment `used` of all the nodes we visit. +func (cn *charNode) Add(str []byte) { + head := cn + for i, char := range str { + found := false + for j, child := range head.children { + if child.char == char { + head.children[j].used++ + head = &head.children[j] // -> child + found = true + break + } + } + if !found { + // add the missing nodes one by one + for _, char = range str[i:] { + head.children = append(head.children, charNode{char: char, children: nil, used: 1}) + head = &head.children[len(head.children)-1] + } + break + } + } +} + +// Remove excludes a node which was previously Add()-ed. +// We start from the root and decrement `used` of all the nodes we visit. +// If there is a node with `used`=0, we erase it from the parent's list of children +// and stop traversing the tree. +func (cn *charNode) Remove(str []byte) { + stop := false + head := cn + for _, char := range str { + for j, child := range head.children { + if child.char != char { + continue + } + head.children[j].used-- + var parent *charNode + head, parent = &head.children[j], head // shift to the child + if head.used == 0 { + parent.children = append(parent.children[:j], parent.children[j+1:]...) + // we can skip deleting the rest of the nodes - they have been already discarded + stop = true + } + break + } + if stop { + break + } + } +} + +// LongestCommonPrefixLength returns the length of the longest common prefix of the strings +// which are stored in the tree. We visit the children recursively starting from the root and +// stop if `used` value decreases or there is more than one child. +func (cn charNode) LongestCommonPrefixLength() int { + var result int + for head := cn; len(head.children) == 1 && head.children[0].used >= head.used; head = head.children[0] { + + result++ + } + return result +} + +// LongestCommonPrefix returns the longest common prefix of the strings +// which are stored in the tree. We compute the length by calling LongestCommonPrefixLength() +// and then record the characters which we visit along the way from the root to the last node. +func (cn charNode) LongestCommonPrefix() []byte { + result := make([]byte, cn.LongestCommonPrefixLength()) + if len(result) == 0 { + return result + } + var i int + for head := cn.children[0]; ; head = head.children[0] { + result[i] = head.char + i++ + if i == len(result) { + break + } + } + return result +} diff --git a/third_party/opa/internal/lcss/lcss_test.go b/third_party/opa/internal/lcss/lcss_test.go new file mode 100644 index 000000000000..54b8cdf80c79 --- /dev/null +++ b/third_party/opa/internal/lcss/lcss_test.go @@ -0,0 +1,242 @@ +package lcss + +import ( + "reflect" + "testing" +) + +func assertEqual(t *testing.T, expected, actual any) { + t.Helper() + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %v got %v", expected, actual) + } +} + +func TestCharNodeAdd(t *testing.T) { + node := &charNode{} + node.Add([]byte("abc")) + assertEqual(t, byte(0), node.char) + assertEqual(t, 0, node.used) + assertEqual(t, 1, len(node.children)) + assertEqual(t, byte('a'), node.children[0].char) + assertEqual(t, 1, node.children[0].used) + assertEqual(t, 1, len(node.children[0].children)) + assertEqual(t, byte('b'), node.children[0].children[0].char) + assertEqual(t, 1, node.children[0].children[0].used) + assertEqual(t, 1, len(node.children[0].children[0].children)) + assertEqual(t, byte('c'), node.children[0].children[0].children[0].char) + assertEqual(t, 1, node.children[0].children[0].children[0].used) + assertEqual(t, 0, len(node.children[0].children[0].children[0].children)) + node.Add([]byte{}) + assertEqual(t, byte(0), node.char) + assertEqual(t, 0, node.used) + assertEqual(t, 1, len(node.children)) + node.Add([]byte("abd")) + assertEqual(t, byte(0), node.char) + assertEqual(t, 0, node.used) + assertEqual(t, 1, len(node.children)) + assertEqual(t, byte('a'), node.children[0].char) + assertEqual(t, 2, node.children[0].used) + assertEqual(t, 1, len(node.children[0].children)) + assertEqual(t, byte('b'), node.children[0].children[0].char) + assertEqual(t, 2, node.children[0].children[0].used) + assertEqual(t, 2, len(node.children[0].children[0].children)) + assertEqual(t, byte('c'), node.children[0].children[0].children[0].char) + assertEqual(t, 1, node.children[0].children[0].children[0].used) + assertEqual(t, 0, len(node.children[0].children[0].children[0].children)) + assertEqual(t, byte('d'), node.children[0].children[0].children[1].char) + assertEqual(t, 1, node.children[0].children[0].children[1].used) + assertEqual(t, 0, len(node.children[0].children[0].children[1].children)) + node.Add([]byte("abc")) + assertEqual(t, byte(0), node.char) + assertEqual(t, 0, node.used) + assertEqual(t, 1, len(node.children)) + assertEqual(t, byte('a'), node.children[0].char) + assertEqual(t, 3, node.children[0].used) + assertEqual(t, 1, len(node.children[0].children)) + assertEqual(t, byte('b'), node.children[0].children[0].char) + assertEqual(t, 3, node.children[0].children[0].used) + assertEqual(t, 2, len(node.children[0].children[0].children)) + assertEqual(t, byte('c'), node.children[0].children[0].children[0].char) + assertEqual(t, 2, node.children[0].children[0].children[0].used) + assertEqual(t, 0, len(node.children[0].children[0].children[0].children)) + assertEqual(t, byte('d'), node.children[0].children[0].children[1].char) + assertEqual(t, 1, node.children[0].children[0].children[1].used) + assertEqual(t, 0, len(node.children[0].children[0].children[1].children)) +} + +func TestCharNodeRemove(t *testing.T) { + node := &charNode{} + node.Add([]byte("abc")) + node.Remove([]byte("abc")) + assertEqual(t, byte(0), node.char) + assertEqual(t, 0, node.used) + assertEqual(t, 0, len(node.children)) + node.Add([]byte("abc")) + node.Add([]byte("abd")) + node.Remove([]byte("abc")) + assertEqual(t, byte(0), node.char) + assertEqual(t, 0, node.used) + assertEqual(t, 1, len(node.children)) + assertEqual(t, byte('a'), node.children[0].char) + assertEqual(t, 1, node.children[0].used) + assertEqual(t, 1, len(node.children[0].children)) + assertEqual(t, byte('b'), node.children[0].children[0].char) + assertEqual(t, 1, node.children[0].children[0].used) + assertEqual(t, 1, len(node.children[0].children[0].children)) + assertEqual(t, byte('d'), node.children[0].children[0].children[0].char) + assertEqual(t, 1, node.children[0].children[0].children[0].used) + assertEqual(t, 0, len(node.children[0].children[0].children[0].children)) + node.Remove([]byte{}) + assertEqual(t, byte(0), node.char) + assertEqual(t, 0, node.used) + assertEqual(t, 1, len(node.children)) + assertEqual(t, byte('a'), node.children[0].char) + assertEqual(t, 1, node.children[0].used) + assertEqual(t, 1, len(node.children[0].children)) + assertEqual(t, byte('b'), node.children[0].children[0].char) + assertEqual(t, 1, node.children[0].children[0].used) + assertEqual(t, 1, len(node.children[0].children[0].children)) + assertEqual(t, byte('d'), node.children[0].children[0].children[0].char) + assertEqual(t, 1, node.children[0].children[0].children[0].used) + assertEqual(t, 0, len(node.children[0].children[0].children[0].children)) + node.Add([]byte("ab")) + node.Remove([]byte("ab")) + assertEqual(t, byte(0), node.char) + assertEqual(t, 0, node.used) + assertEqual(t, 1, len(node.children)) + assertEqual(t, byte('a'), node.children[0].char) + assertEqual(t, 1, node.children[0].used) + assertEqual(t, 1, len(node.children[0].children)) + assertEqual(t, byte('b'), node.children[0].children[0].char) + assertEqual(t, 1, node.children[0].children[0].used) + assertEqual(t, 1, len(node.children[0].children[0].children)) + assertEqual(t, byte('d'), node.children[0].children[0].children[0].char) + assertEqual(t, 1, node.children[0].children[0].children[0].used) + assertEqual(t, 0, len(node.children[0].children[0].children[0].children)) + node.Add([]byte("ab")) + node.Remove([]byte("abd")) + assertEqual(t, byte(0), node.char) + assertEqual(t, 0, node.used) + assertEqual(t, 1, len(node.children)) + assertEqual(t, byte('a'), node.children[0].char) + assertEqual(t, 1, node.children[0].used) + assertEqual(t, 1, len(node.children[0].children)) + assertEqual(t, byte('b'), node.children[0].children[0].char) + assertEqual(t, 1, node.children[0].children[0].used) + assertEqual(t, 0, len(node.children[0].children[0].children)) +} + +func TestCharNodeLongestCommonPrefixLength(t *testing.T) { + node := &charNode{} + assertEqual(t, 0, node.LongestCommonPrefixLength()) + node.Add([]byte("abc")) + assertEqual(t, 3, node.LongestCommonPrefixLength()) + node.Add([]byte("abd")) + assertEqual(t, 2, node.LongestCommonPrefixLength()) + node.Remove([]byte("abd")) + assertEqual(t, 3, node.LongestCommonPrefixLength()) + node.Add([]byte("ab")) + assertEqual(t, 2, node.LongestCommonPrefixLength()) +} + +func TestCharNodeLongestCommonPrefix(t *testing.T) { + node := &charNode{} + assertEqual(t, []byte{}, node.LongestCommonPrefix()) + node.Add([]byte("abc")) + assertEqual(t, []byte("abc"), node.LongestCommonPrefix()) + node.Add([]byte("abd")) + assertEqual(t, []byte("ab"), node.LongestCommonPrefix()) + node.Remove([]byte("abd")) + assertEqual(t, []byte("abc"), node.LongestCommonPrefix()) + node.Add([]byte("ab")) + assertEqual(t, []byte("ab"), node.LongestCommonPrefix()) +} + +func TestCharNodeBug1(t *testing.T) { + node := &charNode{} + node.Add([]byte("a")) + node.Add([]byte("a")) + node.Remove([]byte("a")) + node.Add([]byte("abbara")) + node.Add([]byte("abr")) + + assertEqual(t, byte(0), node.char) + assertEqual(t, 0, node.used) + assertEqual(t, 1, len(node.children)) + assertEqual(t, byte('a'), node.children[0].char) + assertEqual(t, 3, node.children[0].used) + assertEqual(t, 1, len(node.children[0].children)) + assertEqual(t, byte('b'), node.children[0].children[0].char) + assertEqual(t, 2, node.children[0].children[0].used) + assertEqual(t, 2, len(node.children[0].children[0].children)) + assertEqual(t, byte('b'), node.children[0].children[0].children[0].char) + assertEqual(t, 1, node.children[0].children[0].children[0].used) + assertEqual(t, 1, len(node.children[0].children[0].children[0].children)) + assertEqual(t, byte('r'), node.children[0].children[0].children[1].char) + assertEqual(t, 1, node.children[0].children[0].children[1].used) + assertEqual(t, 0, len(node.children[0].children[0].children[1].children)) + assertEqual(t, 1, node.LongestCommonPrefixLength()) + assertEqual(t, []byte("a"), node.LongestCommonPrefix()) +} + +func TestCharNodeBug2(t *testing.T) { + node := &charNode{} + node.Add([]byte("habrahabr")) + node.Add([]byte("bbara")) + node.Add([]byte("mraja")) + node.Remove([]byte("habrahabr")) + node.Add([]byte("r")) + node.Remove([]byte("bbara")) + node.Add([]byte("ra")) + node.Remove([]byte("r")) + node.Add([]byte("rahabr")) + node.Remove([]byte("bbara")) + node.Add([]byte("ra")) + node.Remove([]byte("mraja")) + node.Add([]byte("raja")) + + assertEqual(t, byte(0), node.char) + assertEqual(t, 0, node.used) + assertEqual(t, 1, len(node.children)) + assertEqual(t, byte('r'), node.children[0].char) + assertEqual(t, 3, node.children[0].used) + assertEqual(t, 1, len(node.children[0].children)) + assertEqual(t, byte('a'), node.children[0].children[0].char) + assertEqual(t, 3, node.children[0].children[0].used) + assertEqual(t, 2, len(node.children[0].children[0].children)) + assertEqual(t, byte('h'), node.children[0].children[0].children[0].char) + assertEqual(t, 1, node.children[0].children[0].children[0].used) + assertEqual(t, 1, len(node.children[0].children[0].children[0].children)) + assertEqual(t, byte('j'), node.children[0].children[0].children[1].char) + assertEqual(t, 1, node.children[0].children[0].children[1].used) + assertEqual(t, 1, len(node.children[0].children[0].children[1].children)) + assertEqual(t, []byte("ra"), node.LongestCommonPrefix()) +} + +func TestLongestCommonSubstring(t *testing.T) { + assertEqual(t, []byte{}, LongestCommonSubstring()) + assertEqual(t, []byte("abc"), LongestCommonSubstring([]byte("abc"))) + assertEqual(t, []byte{}, LongestCommonSubstring([]byte("abc"), []byte{})) + assertEqual(t, []byte("ab"), LongestCommonSubstring([]byte("abc"), []byte("abd"))) + assertEqual(t, []byte("bc"), LongestCommonSubstring([]byte("abc"), []byte("dbc"))) + assertEqual(t, []byte("ab"), LongestCommonSubstring([]byte("ab"), []byte("abd"))) + assertEqual(t, []byte("ABC"), LongestCommonSubstring( + []byte("ABABC"), []byte("BABCA"), []byte("ABCBA"))) + assertEqual(t, []byte("ra"), LongestCommonSubstring( + []byte("habrahabr"), + []byte("abbara"), + []byte("humraja"))) + assertEqual(t, []byte("abcdez"), LongestCommonSubstring( + []byte("zxabcdezy"), + []byte("yzabcdezx"), + []byte("abcdez"), + []byte("zyzxabcdez"))) +} + +func TestLongestCommonSubstringWithSuffixArrays(t *testing.T) { + assertEqual(t, []byte{}, lcss(nil, nil)) + assertEqual(t, []byte("abc"), lcss( + [][]byte{[]byte("abc")}, [][]int{{1, 2, 3}})) +} diff --git a/third_party/opa/internal/lcss/qsufsort.go b/third_party/opa/internal/lcss/qsufsort.go new file mode 100644 index 000000000000..61c519688698 --- /dev/null +++ b/third_party/opa/internal/lcss/qsufsort.go @@ -0,0 +1,169 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This algorithm is based on "Faster Suffix Sorting" +// by N. Jesper Larsson and Kunihiko Sadakane +// paper: http://www.larsson.dogma.net/ssrev-tr.pdf +// code: http://www.larsson.dogma.net/qsufsort.c + +// This algorithm computes the suffix array sa by computing its inverse. +// Consecutive groups of suffixes in sa are labeled as sorted groups or +// unsorted groups. For a given pass of the sorter, all suffixes are ordered +// up to their first h characters, and sa is h-ordered. Suffixes in their +// final positions and unambiguously sorted in h-order are in a sorted group. +// Consecutive groups of suffixes with identical first h characters are an +// unsorted group. In each pass of the algorithm, unsorted groups are sorted +// according to the group number of their following suffix. + +// In the implementation, if sa[i] is negative, it indicates that i is +// the first element of a sorted group of length -sa[i], and can be skipped. +// An unsorted group sa[i:k] is given the group number of the index of its +// last element, k-1. The group numbers are stored in the inverse slice (inv), +// and when all groups are sorted, this slice is the inverse suffix array. + +package lcss + +import "sort" + +// qsufsort constructs the suffix array for a given string. +func qsufsort(data []byte) []int { + // initial sorting by first byte of suffix + sa := sortedByFirstByte(data) + if len(sa) < 2 { + return sa + } + // initialize the group lookup table + // this becomes the inverse of the suffix array when all groups are sorted + inv := initGroups(sa, data) + + // the index starts 1-ordered + sufSortable := &suffixSortable{sa: sa, inv: inv, h: 1} + + for sa[0] > -len(sa) { // until all suffixes are one big sorted group + // The suffixes are h-ordered, make them 2*h-ordered + pi := 0 // pi is first position of first group + sl := 0 // sl is negated length of sorted groups + for pi < len(sa) { + if s := sa[pi]; s < 0 { // if pi starts sorted group + pi -= s // skip over sorted group + sl += s // add negated length to sl + } else { // if pi starts unsorted group + if sl != 0 { + sa[pi+sl] = sl // combine sorted groups before pi + sl = 0 + } + pk := inv[s] + 1 // pk-1 is last position of unsorted group + sufSortable.sa = sa[pi:pk] + sort.Sort(sufSortable) + sufSortable.updateGroups(pi) + pi = pk // next group + } + } + if sl != 0 { // if the array ends with a sorted group + sa[pi+sl] = sl // combine sorted groups at end of sa + } + + sufSortable.h *= 2 // double sorted depth + } + + for i := range sa { // reconstruct suffix array from inverse + sa[inv[i]] = i + } + return sa +} + +func sortedByFirstByte(data []byte) []int { + // total byte counts + var count [256]int + for _, b := range data { + count[b]++ + } + // make count[b] equal index of first occurrence of b in sorted array + sum := 0 + for b := range count { + count[b], sum = sum, count[b]+sum + } + // iterate through bytes, placing index into the correct spot in sa + sa := make([]int, len(data)) + for i, b := range data { + sa[count[b]] = i + count[b]++ + } + return sa +} + +func initGroups(sa []int, data []byte) []int { + // label contiguous same-letter groups with the same group number + inv := make([]int, len(data)) + prevGroup := len(sa) - 1 + groupByte := data[sa[prevGroup]] + for i := len(sa) - 1; i >= 0; i-- { + if b := data[sa[i]]; b < groupByte { + if prevGroup == i+1 { + sa[i+1] = -1 + } + groupByte = b + prevGroup = i + } + inv[sa[i]] = prevGroup + if prevGroup == 0 { + sa[0] = -1 + } + } + // Separate out the final suffix to the start of its group. + // This is necessary to ensure the suffix "a" is before "aba" + // when using a potentially unstable sort. + lastByte := data[len(data)-1] + s := -1 + for i := range sa { + if sa[i] >= 0 { + if data[sa[i]] == lastByte && s == -1 { + s = i + } + if sa[i] == len(sa)-1 { + sa[i], sa[s] = sa[s], sa[i] + inv[sa[s]] = s + sa[s] = -1 // mark it as an isolated sorted group + break + } + } + } + return inv +} + +type suffixSortable struct { + sa []int + inv []int + h int + buf []int // common scratch space +} + +func (x *suffixSortable) Len() int { return len(x.sa) } +func (x *suffixSortable) Less(i, j int) bool { return x.inv[x.sa[i]+x.h] < x.inv[x.sa[j]+x.h] } +func (x *suffixSortable) Swap(i, j int) { x.sa[i], x.sa[j] = x.sa[j], x.sa[i] } + +func (x *suffixSortable) updateGroups(offset int) { + bounds := x.buf[0:0] + group := x.inv[x.sa[0]+x.h] + for i := 1; i < len(x.sa); i++ { + if g := x.inv[x.sa[i]+x.h]; g > group { + bounds = append(bounds, i) + group = g + } + } + bounds = append(bounds, len(x.sa)) + x.buf = bounds + + // update the group numberings after all new groups are determined + prev := 0 + for _, b := range bounds { + for i := prev; i < b; i++ { + x.inv[x.sa[i]] = offset + b - 1 + } + if b-prev == 1 { + x.sa[prev] = -1 + } + prev = b + } +} diff --git a/third_party/opa/internal/leb128/leb128.go b/third_party/opa/internal/leb128/leb128.go new file mode 100644 index 000000000000..24ddc9095139 --- /dev/null +++ b/third_party/opa/internal/leb128/leb128.go @@ -0,0 +1,170 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package leb128 implements LEB128 integer encoding. +package leb128 + +import ( + "io" +) + +// MustReadVarInt32 returns an int32 from r or panics. +func MustReadVarInt32(r io.Reader) int32 { + i32, err := ReadVarInt32(r) + if err != nil { + panic(err) + } + return i32 +} + +// MustReadVarInt64 returns an int64 from r or panics. +func MustReadVarInt64(r io.Reader) int64 { + i64, err := ReadVarInt64(r) + if err != nil { + panic(err) + } + return i64 +} + +// MustReadVarUint32 returns an uint32 from r or panics. +func MustReadVarUint32(r io.Reader) uint32 { + u32, err := ReadVarUint32(r) + if err != nil { + panic(err) + } + return u32 +} + +// MustReadVarUint64 returns an uint64 from r or panics. +func MustReadVarUint64(r io.Reader) uint64 { + u64, err := ReadVarUint64(r) + if err != nil { + panic(err) + } + return u64 +} + +// Copied rom http://dwarfstd.org/doc/Dwarf3.pdf. + +// ReadVarUint32 tries to read a uint32 from r. +func ReadVarUint32(r io.Reader) (uint32, error) { + u64, err := ReadVarUint64(r) + if err != nil { + return 0, err + } + return uint32(u64), nil +} + +// ReadVarUint64 tries to read a uint64 from r. +func ReadVarUint64(r io.Reader) (uint64, error) { + var result uint64 + var shift uint64 + buf := make([]byte, 1) + for { + if _, err := r.Read(buf); err != nil { + return 0, err + } + v := uint64(buf[0]) + result |= (v & 0x7F) << shift + if v&0x80 == 0 { + return result, nil + } + shift += 7 + } + +} + +// ReadVarInt32 tries to read a int32 from r. +func ReadVarInt32(r io.Reader) (int32, error) { + i64, err := ReadVarInt64(r) + if err != nil { + return 0, err + } + return int32(i64), nil +} + +// ReadVarInt64 tries to read a int64 from r. +func ReadVarInt64(r io.Reader) (int64, error) { + var result int64 + var shift uint64 + size := uint64(32) + buf := make([]byte, 1) + for { + if _, err := r.Read(buf); err != nil { + return 0, err + } + v := int64(buf[0]) + result |= (v & 0x7F) << shift + shift += 7 + if v&0x80 == 0 { + if (shift < size) && (v&0x40 != 0) { + result |= (^0 << shift) + } + return result, nil + } + } +} + +// WriteVarUint32 writes u to w. +func WriteVarUint32(w io.Writer, u uint32) error { + var b []byte + _, err := w.Write(appendUleb128(b, uint64(u))) + return err +} + +// WriteVarUint64 writes u to w. +func WriteVarUint64(w io.Writer, u uint64) error { + var b []byte + _, err := w.Write(appendUleb128(b, u)) + return err +} + +// WriteVarInt32 writes u to w. +func WriteVarInt32(w io.Writer, i int32) error { + var b []byte + _, err := w.Write(appendSleb128(b, int64(i))) + return err +} + +// WriteVarInt64 writes u to w. +func WriteVarInt64(w io.Writer, i int64) error { + var b []byte + _, err := w.Write(appendSleb128(b, i)) + return err +} + +// Copied from https://github.com/golang/go/blob/master/src/cmd/internal/dwarf/dwarf.go. + +// appendUleb128 appends v to b using DWARF's unsigned LEB128 encoding. +func appendUleb128(b []byte, v uint64) []byte { + for { + c := uint8(v & 0x7f) + v >>= 7 + if v != 0 { + c |= 0x80 + } + b = append(b, c) + if c&0x80 == 0 { + break + } + } + return b +} + +// appendSleb128 appends v to b using DWARF's signed LEB128 encoding. +func appendSleb128(b []byte, v int64) []byte { + for { + c := uint8(v & 0x7f) + s := uint8(v & 0x40) + v >>= 7 + if (v != -1 || s == 0) && (v != 0 || s != 0) { + c |= 0x80 + } + b = append(b, c) + if c&0x80 == 0 { + break + } + } + return b +} diff --git a/third_party/opa/internal/leb128/leb128_test.go b/third_party/opa/internal/leb128/leb128_test.go new file mode 100644 index 000000000000..34b496e3ea5e --- /dev/null +++ b/third_party/opa/internal/leb128/leb128_test.go @@ -0,0 +1,207 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package leb128 + +import ( + "bytes" + "testing" +) + +// Test cases copied from http://dwarfstd.org/doc/Dwarf3.pdf. + +func TestReadVarUint64(t *testing.T) { + + tests := []struct { + bs []byte + exp uint64 + }{ + { + bs: []byte("\x02"), + exp: 2, + }, + { + bs: []byte("\x7F"), + exp: 127, + }, + { + bs: []byte("\x80\x01"), + exp: 128, + }, + { + bs: []byte("\x81\x01"), + exp: 129, + }, + { + bs: []byte("\x82\x01"), + exp: 130, + }, + { + bs: []byte("\xB9\x64"), + exp: 12857, + }, + } + + for i, tc := range tests { + r := bytes.NewReader(tc.bs) + result, err := ReadVarUint64(r) + if err != nil { + t.Fatalf("Case %d, err: %v", i, err) + } else if result != tc.exp { + t.Fatalf("Case %d, expected %v, but got %v", i, tc.exp, result) + } + } + +} + +func TestReadVarInt64(t *testing.T) { + + tests := []struct { + bs []byte + exp int64 + }{ + { + bs: []byte("\x02"), + exp: 2, + }, + { + bs: []byte("\x7E"), + exp: -2, + }, + { + bs: []byte("\xFF\x00"), + exp: 127, + }, + { + bs: []byte("\x81\x7F"), + exp: -127, + }, + { + bs: []byte("\x80\x01"), + exp: 128, + }, + { + bs: []byte("\x80\x7F"), + exp: -128, + }, + { + bs: []byte("\x81\x01"), + exp: 129, + }, + { + bs: []byte("\xFF\x7E"), + exp: -129, + }, + } + + for i, tc := range tests { + r := bytes.NewReader(tc.bs) + result, err := ReadVarInt64(r) + if err != nil { + t.Fatalf("Case %d, err: %v", i, err) + } else if result != tc.exp { + t.Fatalf("Case %d, expected %v, but got %v", i, tc.exp, result) + } + } +} + +func TestWriteVarUint64(t *testing.T) { + + tests := []struct { + bs []byte + input uint64 + }{ + { + bs: []byte("\x02"), + input: 2, + }, + { + bs: []byte("\x7F"), + input: 127, + }, + { + bs: []byte("\x80\x01"), + input: 128, + }, + { + bs: []byte("\x81\x01"), + input: 129, + }, + { + bs: []byte("\x82\x01"), + input: 130, + }, + { + bs: []byte("\xB9\x64"), + input: 12857, + }, + } + + for i, tc := range tests { + var buf bytes.Buffer + + if err := WriteVarUint64(&buf, tc.input); err != nil { + t.Fatalf("Case %d, err: %v", i, err) + } + + if !bytes.Equal(buf.Bytes(), tc.bs) { + t.Fatalf("Case %d, expected %v, but got %v", i, tc.bs, buf.Bytes()) + } + } + +} + +func TestWriteVarInt64(t *testing.T) { + + tests := []struct { + bs []byte + input int64 + }{ + { + bs: []byte("\x02"), + input: 2, + }, + { + bs: []byte("\x7E"), + input: -2, + }, + { + bs: []byte("\xFF\x00"), + input: 127, + }, + { + bs: []byte("\x81\x7F"), + input: -127, + }, + { + bs: []byte("\x80\x01"), + input: 128, + }, + { + bs: []byte("\x80\x7F"), + input: -128, + }, + { + bs: []byte("\x81\x01"), + input: 129, + }, + { + bs: []byte("\xFF\x7E"), + input: -129, + }, + } + + for i, tc := range tests { + + var buf bytes.Buffer + + if err := WriteVarInt64(&buf, tc.input); err != nil { + t.Fatalf("Case %d, err: %v", i, err) + } + + if !bytes.Equal(buf.Bytes(), tc.bs) { + t.Fatalf("Case %d, expected %v, but got %v", i, tc.bs, buf.Bytes()) + } + } +} diff --git a/third_party/opa/internal/logging/logging.go b/third_party/opa/internal/logging/logging.go new file mode 100644 index 000000000000..acd44f8cad5d --- /dev/null +++ b/third_party/opa/internal/logging/logging.go @@ -0,0 +1,105 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package logging + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + + "github.com/sirupsen/logrus" + + "github.com/open-policy-agent/opa/v1/logging" +) + +func GetLevel(level string) (logging.Level, error) { + switch strings.ToLower(level) { + case "debug": + return logging.Debug, nil + case "", "info": + return logging.Info, nil + case "warn": + return logging.Warn, nil + case "error": + return logging.Error, nil + default: + return logging.Debug, fmt.Errorf("invalid log level: %v", level) + } +} + +func GetFormatter(format, timestampFormat string) logrus.Formatter { + switch format { + case "text": + return &prettyFormatter{} + case "json-pretty": + return &logrus.JSONFormatter{PrettyPrint: true, TimestampFormat: timestampFormat} + default: + return &logrus.JSONFormatter{TimestampFormat: timestampFormat} + } +} + +// prettyFormatter implements the Logrus Formatter interface +// and provides a more simple, but easier to read, text formatter +// option than the default logrus.TextFormatter. +type prettyFormatter struct{} + +func spaces(num int) string { + return strings.Repeat(" ", num) +} + +func (*prettyFormatter) Format(e *logrus.Entry) ([]byte, error) { + b := new(bytes.Buffer) + + level := strings.ToUpper(e.Level.String()) + b.WriteString(fmt.Sprintf("[%s] %s\n", level, e.Message)) + + // Format each key for optimal ease of human reading + fieldIndent := 2 + multiLineIndent := 6 + for k, v := range e.Data { + // Special case for multi-line strings, keep them as-is + // but indent them. Everything else gets json'd + stringVal, ok := v.(string) + if ok && strings.Contains(stringVal, "\n") { + sb := strings.Builder{} + for i, line := range strings.Split(stringVal, "\n") { + // match the json indent helper by not indenting the first value + if i != 0 { + sb.WriteString(spaces(multiLineIndent)) + } + sb.WriteString(line) + sb.WriteByte('\n') + stringVal = sb.String() + } + } else if ok && json.Valid([]byte(stringVal)) { + var tmp bytes.Buffer + err := json.Indent(&tmp, []byte(stringVal), spaces(multiLineIndent), spaces(2)) + if err != nil { + return nil, err + } + stringVal = tmp.String() + } else { + jsonVal, err := json.MarshalIndent(v, spaces(multiLineIndent), spaces(2)) + if err != nil { + return nil, err + } + stringVal = string(jsonVal) + } + + b.WriteString(spaces(fieldIndent)) + b.WriteString(k) + if strings.Contains(stringVal, "\n") { + b.WriteString(" = |\n") + b.WriteString(spaces(multiLineIndent)) + } else { + b.WriteString(" = ") + } + b.WriteString(stringVal) + b.WriteString("\n") + } + b.WriteByte('\n') + return b.Bytes(), nil +} diff --git a/third_party/opa/internal/logging/logging_test.go b/third_party/opa/internal/logging/logging_test.go new file mode 100644 index 000000000000..e3cc8a498257 --- /dev/null +++ b/third_party/opa/internal/logging/logging_test.go @@ -0,0 +1,190 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package logging + +import ( + "encoding/json" + "errors" + "strings" + "testing" + + "github.com/sirupsen/logrus" +) + +func TestPrettyFormatterNoFields(t *testing.T) { + fmtr := prettyFormatter{} + + e := logrus.NewEntry(logrus.StandardLogger()) + e.Message = "test" + e.Level = logrus.InfoLevel + + out, err := fmtr.Format(e) + if err != nil { + t.Fatalf("Unexpected error formatting log entry: %s", err.Error()) + } + + actualStr := string(out) + + expectedLvl := strings.ToUpper(e.Level.String()) + if !strings.Contains(actualStr, expectedLvl) { + t.Errorf("Expected log message to have level %s:\n%s", expectedLvl, actualStr) + } + + if !strings.Contains(actualStr, "test") { + t.Errorf("Expected log message to have the entry message '%s':\n%s", "test", actualStr) + } +} + +func TestPrettyFormatterBasicFields(t *testing.T) { + fmtr := prettyFormatter{} + + e := logrus.WithFields(logrus.Fields{ + "number": 5, + "string": "field_string", + "nil": nil, + "error": errors.New("field_error").Error(), + }) + + e.Message = "test" + e.Level = logrus.InfoLevel + + out, err := fmtr.Format(e) + if err != nil { + t.Fatalf("Unexpected error formatting log entry: %s", err.Error()) + } + + actualStr := string(out) + + expectedLvl := strings.ToUpper(e.Level.String()) + if !strings.Contains(actualStr, expectedLvl) { + t.Errorf("Expected log message to have level %s:\n%s", expectedLvl, actualStr) + } + + if !strings.Contains(actualStr, "test\n") { + t.Errorf("Expected log message to have the entry message '%s':\n%s", "test", actualStr) + } + + if !strings.Contains(actualStr, "number = 5\n") { + t.Errorf("Expected to have the number field in message") + } + + if !strings.Contains(actualStr, "string = \"field_string\"\n") { + t.Errorf("Expected to have the string field in message") + } + + if !strings.Contains(actualStr, "nil = null\n") { + t.Errorf("Expected to have the nil field in message") + } + + if !strings.Contains(actualStr, "error = \"field_error\"\n") { + t.Errorf("Expected to have the nil field in message") + } + + expectedLines := 7 // one for the message, 4 fields (one line each), and two trailing \n + actualLines := len(strings.Split(actualStr, "\n")) + if actualLines != expectedLines { + t.Errorf("Expected %d lines in output, found %d\n Output: \n%s\n", expectedLines, actualLines, actualStr) + } +} + +func TestPrettyFormatterMultilineStringFields(t *testing.T) { + fmtr := prettyFormatter{} + + mlStr := ` +package opa.examples + +import data.servers +import data.networks +import data.ports + +public_servers[server] { + server := servers[_] + server.ports[_] == ports[k].id + ports[k].networks[_] == networks[m].id + networks[m].public == true +} +` + + e := logrus.WithFields(logrus.Fields{ + "multi_line": mlStr, + }) + + e.Message = "test" + e.Level = logrus.InfoLevel + + out, err := fmtr.Format(e) + if err != nil { + t.Fatalf("Unexpected error formatting log entry: %s", err.Error()) + } + + actualStr := string(out) + + expectedLvl := strings.ToUpper(e.Level.String()) + if !strings.Contains(actualStr, expectedLvl) { + t.Errorf("Expected log message to have level %s:\n%s", expectedLvl, actualStr) + } + + if !strings.Contains(actualStr, "test") { + t.Errorf("Expected log message to have the entry message '%s':\n%s", "test", actualStr) + } + + for _, line := range strings.Split(mlStr, "\n") { + // The lines will get prefixed with some padding but should always + // still have their real newlines, and not be encoded. + expectedStr := line + "\n" + if !strings.Contains(actualStr, expectedStr) { + t.Errorf("Expected to find line in message:\n\n%s\n\nactual:\n\n%s\n", expectedStr, actualStr) + } + } +} + +func TestPrettyFormatterMultilineJSONFields(t *testing.T) { + fmtr := prettyFormatter{} + + obj := map[string]any{ + "a": 123, + "b": nil, + "d": "abc", + "e": map[string]any{ + "test": []string{ + "aa", + "bb", + "cc", + }, + }, + } + + e := logrus.WithFields(logrus.Fields{ + "json_string": obj, + }) + + e.Message = "test" + e.Level = logrus.InfoLevel + + out, err := fmtr.Format(e) + if err != nil { + t.Fatalf("Unexpected error formatting log entry: %s", err.Error()) + } + + actualStr := string(out) + + expectedLvl := strings.ToUpper(e.Level.String()) + if !strings.Contains(actualStr, expectedLvl) { + t.Errorf("Expected log message to have level %s:\n%s", expectedLvl, actualStr) + } + + if !strings.Contains(actualStr, "test") { + t.Errorf("Expected log message to have the entry message 'test':\n%s", actualStr) + } + + expectedJSON, err := json.MarshalIndent(&obj, " ", " ") + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if !strings.Contains(actualStr, string(expectedJSON)) { + t.Errorf("Expected JSON to be formatted and included in message:\n\nExpected:\n%s\n\nActual:\n%s\n\n", string(expectedJSON), actualStr) + } +} diff --git a/third_party/opa/internal/merge/merge.go b/third_party/opa/internal/merge/merge.go new file mode 100644 index 000000000000..ba1a09c3298d --- /dev/null +++ b/third_party/opa/internal/merge/merge.go @@ -0,0 +1,64 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package merge contains helpers to merge data structures +// frequently encountered in OPA. +package merge + +// InterfaceMaps returns the result of merging a and b. If a and b cannot be +// merged because of conflicting key-value pairs, ok is false. +func InterfaceMaps(a map[string]any, b map[string]any) (map[string]any, bool) { + + if a == nil { + return b, true + } + + if hasConflicts(a, b) { + return nil, false + } + + return merge(a, b), true +} + +func merge(a, b map[string]any) map[string]any { + + for k := range b { + + add := b[k] + exist, ok := a[k] + if !ok { + a[k] = add + continue + } + + existObj := exist.(map[string]any) + addObj := add.(map[string]any) + + a[k] = merge(existObj, addObj) + } + + return a +} + +func hasConflicts(a, b map[string]any) bool { + for k := range b { + + add := b[k] + exist, ok := a[k] + if !ok { + continue + } + + existObj, existOk := exist.(map[string]any) + addObj, addOk := add.(map[string]any) + if !existOk || !addOk { + return true + } + + if hasConflicts(existObj, addObj) { + return true + } + } + return false +} diff --git a/third_party/opa/internal/merge/merge_test.go b/third_party/opa/internal/merge/merge_test.go new file mode 100644 index 000000000000..94c04fe26dab --- /dev/null +++ b/third_party/opa/internal/merge/merge_test.go @@ -0,0 +1,72 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package merge + +import ( + "reflect" + "testing" + + "github.com/open-policy-agent/opa/v1/util" +) + +func TestMergeDocs(t *testing.T) { + + tests := []struct { + a string + b string + c string + ok bool + }{ + {`{"x": 1, "y": 2}`, `{"z": 3}`, `{"x": 1, "y": 2, "z": 3}`, true}, + {`{"x": {"y": 2}}`, `{"z": 3, "x": {"q": 4}}`, `{"x": {"y": 2, "q": 4}, "z": 3}`, true}, + {`{"x": 1}`, `{"x": 1}`, "", false}, + {`{"x": {"y": [{"z": 2}]}}`, `{"x": {"y": [{"z": 3}]}}`, "", false}, + } + + for _, tc := range tests { + a := map[string]any{} + if err := util.UnmarshalJSON([]byte(tc.a), &a); err != nil { + panic(err) + } + aInitial := map[string]any{} + if err := util.UnmarshalJSON([]byte(tc.a), &aInitial); err != nil { + panic(err) + } + + b := map[string]any{} + if err := util.UnmarshalJSON([]byte(tc.b), &b); err != nil { + panic(err) + } + + if len(tc.c) == 0 { + + c, ok := InterfaceMaps(a, b) + if ok { + t.Errorf("Expected merge(%v,%v) == false but got: %v", a, b, c) + } + + if !reflect.DeepEqual(a, aInitial) { + t.Errorf("Expected conflicting merge to not mutate a (%v) but got a: %v", aInitial, a) + } + + } else { + + expected := map[string]any{} + if err := util.UnmarshalJSON([]byte(tc.c), &expected); err != nil { + panic(err) + } + + c, ok := InterfaceMaps(a, b) + if !ok || !reflect.DeepEqual(c, expected) { + t.Errorf("Expected merge(%v, %v) == %v but got: %v (ok: %v)", a, b, expected, c, ok) + } + + if reflect.DeepEqual(a, aInitial) || !reflect.DeepEqual(a, c) { + t.Errorf("Expected merge to mutate a (%v) but got %v", aInitial, a) + } + + } + } +} diff --git a/third_party/opa/internal/pathwatcher/utils.go b/third_party/opa/internal/pathwatcher/utils.go new file mode 100644 index 000000000000..8c9252476037 --- /dev/null +++ b/third_party/opa/internal/pathwatcher/utils.go @@ -0,0 +1,126 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package pathwatcher provides helper functions for creating file and directory watchers +package pathwatcher + +import ( + "context" + "os" + "path/filepath" + + "github.com/fsnotify/fsnotify" + initload "github.com/open-policy-agent/opa/internal/runtime/init" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/util" +) + +// CreatePathWatcher creates watchers to monitor for path changes +func CreatePathWatcher(rootPaths []string) (*fsnotify.Watcher, error) { + watchPaths, err := getWatchPaths(rootPaths) + if err != nil { + return nil, err + } + + watcher, err := fsnotify.NewWatcher() + if err != nil { + return nil, err + } + + for _, path := range watchPaths { + if err := watcher.Add(path); err != nil { + return nil, err + } + } + + return watcher, nil +} + +// ProcessWatcherUpdate handles an occurrence of a watcher event +func ProcessWatcherUpdate(ctx context.Context, paths []string, removed string, store storage.Store, filter loader.Filter, asBundle bool, bundleLazyLoadingMode bool, + f func(context.Context, storage.Transaction, *initload.LoadPathsResult) error) error { + return ProcessWatcherUpdateForRegoVersion(ctx, ast.DefaultRegoVersion, paths, removed, store, filter, asBundle, bundleLazyLoadingMode, f) +} + +func ProcessWatcherUpdateForRegoVersion(ctx context.Context, regoVersion ast.RegoVersion, paths []string, removed string, store storage.Store, filter loader.Filter, asBundle bool, bundleLazyLoadingMode bool, + f func(context.Context, storage.Transaction, *initload.LoadPathsResult) error) error { + loaded, err := initload.LoadPathsForRegoVersion(regoVersion, paths, filter, asBundle, nil, true, bundleLazyLoadingMode, false, false, nil, nil) + if err != nil { + return err + } + + removed = loader.CleanPath(removed) + + return storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + if !asBundle { + ids, err := store.ListPolicies(ctx, txn) + if err != nil { + return err + } + for _, id := range ids { + if id == removed { + if err := store.DeletePolicy(ctx, txn, id); err != nil { + return err + } + } else if _, exists := loaded.Files.Modules[id]; !exists { + // This branch get hit in two cases. + // 1. Another piece of code has access to the store and inserts + // a policy out-of-band. + // 2. In between FS notification and loader.Filtered() call above, a + // policy is removed from disk. + bs, err := store.GetPolicy(ctx, txn, id) + if err != nil { + return err + } + module, err := ast.ParseModuleWithOpts(id, string(bs), ast.ParserOptions{RegoVersion: regoVersion}) + if err != nil { + return err + } + loaded.Files.Modules[id] = &loader.RegoFile{ + Name: id, + Raw: bs, + Parsed: module, + } + } + } + } + + return f(ctx, txn, loaded) + }) +} + +func getWatchPaths(rootPaths []string) ([]string, error) { + paths := []string{} + + for _, path := range rootPaths { + + _, path = loader.SplitPrefix(path) + result, err := loader.Paths(path, true) + if err != nil { + return nil, err + } + + unique := map[string]struct{}{} + + for _, r := range result { + fi, err := os.Lstat(r) + if err != nil { + return nil, err + } + + if fi.IsDir() { + unique[r] = struct{}{} + } else { + dir := filepath.Dir(r) + unique[dir] = struct{}{} + } + } + + paths = append(paths, util.KeysSorted(unique)...) + } + + return paths, nil +} diff --git a/third_party/opa/internal/pathwatcher/utils_test.go b/third_party/opa/internal/pathwatcher/utils_test.go new file mode 100644 index 000000000000..62b0c40610d0 --- /dev/null +++ b/third_party/opa/internal/pathwatcher/utils_test.go @@ -0,0 +1,41 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package pathwatcher + +import ( + "path/filepath" + "slices" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestWatchPaths(t *testing.T) { + + fs := map[string]string{ + "/foo/bar/baz.json": "true", + "/foo/faz/baz.json": "true", + "/foo/baz.json": "true", + } + + expected := []string{ + "/foo", "/foo/bar", "/foo/faz", + } + + test.WithTempFS(fs, func(rootDir string) { + paths, err := getWatchPaths([]string{"prefix:" + rootDir + "/foo"}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := []string{} + for _, p := range paths { + result = append(result, filepath.Clean(strings.TrimPrefix(p, rootDir))) + } + if !slices.Equal(expected, result) { + t.Fatalf("Expected %q but got: %q", expected, result) + } + }) +} diff --git a/third_party/opa/internal/planner/planner.go b/third_party/opa/internal/planner/planner.go new file mode 100644 index 000000000000..8d59158717a4 --- /dev/null +++ b/third_party/opa/internal/planner/planner.go @@ -0,0 +1,2600 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package planner contains a query planner for Rego queries. +package planner + +import ( + "errors" + "fmt" + "io" + "sort" + + "github.com/open-policy-agent/opa/internal/debug" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ast/location" + "github.com/open-policy-agent/opa/v1/ir" +) + +// QuerySet represents the input to the planner. +type QuerySet struct { + Name string + Queries []ast.Body + RewrittenVars map[ast.Var]ast.Var +} + +type planiter func() error +type planLocalIter func(ir.Local) error +type stmtFactory func(ir.Local) ir.Stmt + +// Planner implements a query planner for Rego queries. +type Planner struct { + policy *ir.Policy // result of planning + queries []QuerySet // input queries to plan + modules []*ast.Module // input modules to support queries + strings map[string]int // global string constant indices + files map[string]int // global file constant indices + externs map[string]*ast.Builtin // built-in functions that are required in execution environment + decls map[string]*ast.Builtin // built-in functions that may be provided in execution environment + rules *ruletrie // rules that may be planned + mocks *functionMocksStack // replacements for built-in functions + funcs *funcstack // functions that have been planned + plan *ir.Plan // in-progress query plan + curr *ir.Block // in-progress query block + vars *varstack // in-scope variables + ltarget ir.Operand // target variable or constant of last planned statement + lnext ir.Local // next variable to use + loc *location.Location // location currently "being planned" + debug debug.Debug // debug information produced during planning +} + +// debugf prepends the planner location. We're passing callstack depth 2 because +// it should still log the file location of p.debugf. +func (p *Planner) debugf(format string, args ...any) { + var msg string + if p.loc != nil { + msg = fmt.Sprintf("%s: "+format, append([]any{p.loc}, args...)...) + } else { + msg = fmt.Sprintf(format, args...) + } + _ = p.debug.Output(2, msg) // ignore error +} + +// New returns a new Planner object. +func New() *Planner { + return &Planner{ + policy: &ir.Policy{ + Static: &ir.Static{}, + Plans: &ir.Plans{}, + Funcs: &ir.Funcs{}, + }, + strings: map[string]int{}, + files: map[string]int{}, + externs: map[string]*ast.Builtin{}, + lnext: ir.Unused, + vars: newVarstack(map[ast.Var]ir.Local{ + ast.InputRootDocument.Value.(ast.Var): ir.Input, + ast.DefaultRootDocument.Value.(ast.Var): ir.Data, + }), + rules: newRuletrie(), + funcs: newFuncstack(), + mocks: newFunctionMocksStack(), + debug: debug.Discard(), + } +} + +// WithBuiltinDecls tells the planner what built-in function may be available +// inside the execution environment. +func (p *Planner) WithBuiltinDecls(decls map[string]*ast.Builtin) *Planner { + p.decls = decls + return p +} + +// WithQueries sets the query sets to generate a plan for. The rewritten collection provides +// a mapping of rewritten query vars for each query set. The planner uses rewritten variables +// but the result set key will be the original variable name. +func (p *Planner) WithQueries(queries []QuerySet) *Planner { + p.queries = queries + return p +} + +// WithModules sets the module set that contains query dependencies. +func (p *Planner) WithModules(modules []*ast.Module) *Planner { + p.modules = modules + return p +} + +// WithDebug sets where debug messages are written to. +func (p *Planner) WithDebug(sink io.Writer) *Planner { + if sink != nil { + p.debug = debug.New(sink) + } + return p +} + +// Plan returns a IR plan for the policy query. +func (p *Planner) Plan() (*ir.Policy, error) { + + if err := p.buildFunctrie(); err != nil { + return nil, err + } + + if err := p.planQueries(); err != nil { + return nil, err + } + + if err := p.planExterns(); err != nil { + return nil, err + } + + return p.policy, nil +} + +func (p *Planner) buildFunctrie() error { + + for _, module := range p.modules { + + // Create functrie node for empty packages so that extent queries return + // empty objects. For example: + // + // package x.y + // + // Query: data.x + // + // Expected result: {"y": {}} + if len(module.Rules) == 0 { + _ = p.rules.LookupOrInsert(module.Package.Path) + continue + } + + for _, rule := range module.Rules { + r := rule.Ref().StringPrefix() + val := p.rules.LookupOrInsert(r) + + val.rules = val.DescendantRules() + val.rules = append(val.rules, rule) + val.children = nil + } + } + return nil +} + +func (p *Planner) planRules(rules []*ast.Rule) (string, error) { + // We know the rules with closer to the root (shorter static path) are ordered first. + pathRef := rules[0].Ref() + + // figure out what our rules' collective name/path is: + // if we're planning both p.q.r and p.q[s], we'll name + // the function p.q (for the mapping table) + pieces := len(pathRef) + for i := range rules { + r := rules[i].Ref() + for j, t := range r { + if _, ok := t.Value.(ast.String); !ok && j > 0 && j < pieces { + pieces = j + } + } + } + // control if p.a = 1 is to return 1 directly; or insert 1 under key "a" into an object + buildObject := pieces != len(pathRef) + + var pathPieces []string + for i := 1; /* skip `data` */ i < pieces; i++ { + switch q := pathRef[i].Value.(type) { + case ast.String: + pathPieces = append(pathPieces, string(q)) + default: + panic("impossible") + } + } + + path := pathRef[:pieces].String() + if funcName, ok := p.funcs.Get(path); ok { + return funcName, nil + } + + // Save current state of planner. + // + // TODO(tsandall): perhaps we would be better off using stacks here or + // splitting block planner into separate struct that could be instantiated + // for rule and comprehension bodies. + pvars := p.vars + pcurr := p.curr + pltarget := p.ltarget + plnext := p.lnext + ploc := p.loc + + // Reset the variable counter for the function plan. + p.lnext = ir.Input + + // Set the location to the rule head. + p.loc = rules[0].Head.Loc() + + pcount := p.funcs.argVars() + params := make([]ir.Local, 0, pcount+len(rules[0].Head.Args)) + for range pcount { + params = append(params, p.newLocal()) + } + // Create function definition for rules. + fn := &ir.Func{ + Name: fmt.Sprintf("g%d.%s", p.funcs.gen(), path), + Params: params, + Return: p.newLocal(), + Path: append([]string{fmt.Sprintf("g%d", p.funcs.gen())}, pathPieces...), + } + + // Initialize parameters for functions. + for range len(rules[0].Head.Args) { + fn.Params = append(fn.Params, p.newLocal()) + } + + // only those added as formal parameters: + // f(x, y) is planned as f(data, input, x, y) + // pcount > 2 means there are vars passed along through with replacements by variables + params = fn.Params[pcount:] + + // Initialize return value for partial set/object rules. Complete document + // rules assign directly to `fn.Return`. + switch rules[0].Head.RuleKind() { + case ast.SingleValue: + if buildObject { + fn.Blocks = append(fn.Blocks, p.blockWithStmt(&ir.MakeObjectStmt{Target: fn.Return})) + } + case ast.MultiValue: + if buildObject { + fn.Blocks = append(fn.Blocks, p.blockWithStmt(&ir.MakeObjectStmt{Target: fn.Return})) + } else { + fn.Blocks = append(fn.Blocks, p.blockWithStmt(&ir.MakeSetStmt{Target: fn.Return})) + } + } + + // For complete document rules, allocate one local variable for output + // of the rule body + else branches. + // It is used to let ordered rules (else blocks) check if the previous + // rule body returned a value. + lresult := p.newLocal() + + // At this point the locals for the params and return value have been + // allocated. This will be the first local that can be used in each block. + lnext := p.lnext + + var defaultRule *ast.Rule + var ruleLoc *location.Location + + // We sort rules by ref length, to ensure that when merged, we can detect conflicts when one + // rule attempts to override values (deep and shallow) defined by another rule. + sort.Slice(rules, func(i, j int) bool { + return len(rules[i].Ref()) > len(rules[j].Ref()) + }) + + // Generate function blocks for rules. + for i := range rules { + + // Save location of first encountered rule for the ReturnLocalStmt below + if i == 0 { + ruleLoc = p.loc + } + + // Save default rule for the end. + if rules[i].Default { + defaultRule = rules[i] + continue + } + + // Ordered rules are nested inside an additional block so that execution + // can short-circuit. For unordered rules, blocks can be added directly + // to the function. + var blocks *[]*ir.Block + + if rules[i].Else == nil { + blocks = &fn.Blocks + } else { + stmt := &ir.BlockStmt{} + block := &ir.Block{Stmts: []ir.Stmt{stmt}} + fn.Blocks = append(fn.Blocks, block) + blocks = &stmt.Blocks + } + + var prev *ast.Rule + + // Unordered rules are treated as a special case of ordered rules. + for rule := rules[i]; rule != nil; prev, rule = rule, rule.Else { + + // Update the location for each ordered rule. + p.loc = rule.Head.Loc() + + // Setup planner for block. + p.lnext = lnext + vs := make(map[ast.Var]ir.Local, p.funcs.argVars()) + for i, v := range p.funcs.vars() { + vs[v] = fn.Params[i] + } + p.vars = newVarstack(vs) + + curr := &ir.Block{} + *blocks = append(*blocks, curr) + p.curr = curr + + if prev != nil { + // Ordered rules are handled by short circuiting execution. The + // plan will jump out to the extra block that was planned above. + p.appendStmt(&ir.IsUndefinedStmt{Source: lresult}) + } else { + // The first rule body resets the local, so it can be reused. + // TODO(sr): I don't think we need this anymore. Double-check? Perhaps multi-value rules need it. + p.appendStmt(&ir.ResetLocalStmt{Target: lresult}) + } + + // Complete and partial rules are treated as special cases of + // functions. If there are no args, the first step is a no-op. + err := p.planFuncParams(params, rule.Head.Args, 0, func() error { + + // Run planner on the rule body. + return p.planQuery(rule.Body, 0, func() error { + + // Run planner on the result. + switch rule.Head.RuleKind() { + case ast.SingleValue: + if buildObject { + ref := rule.Ref() + return p.planTerm(rule.Head.Value, func() error { + value := p.ltarget + return p.planNestedObjects(fn.Return, ref[pieces:len(ref)-1], func(obj ir.Local) error { + return p.planTerm(ref[len(ref)-1], func() error { + key := p.ltarget + p.appendStmt(&ir.ObjectInsertOnceStmt{ + Object: obj, + Key: key, + Value: value, + }) + return nil + }) + }) + }) + } + return p.planTerm(rule.Head.Value, func() error { + p.appendStmt(&ir.AssignVarOnceStmt{ + Target: lresult, + Source: p.ltarget, + }) + return nil + }) + case ast.MultiValue: + if buildObject { + ref := rule.Ref() + // we drop the trailing set key from the ref + return p.planNestedObjects(fn.Return, ref[pieces:len(ref)-1], func(obj ir.Local) error { + // Last term on rule ref is the key an which the set is assigned in the deepest nested object + return p.planTerm(ref[len(ref)-1], func() error { + key := p.ltarget + return p.planTerm(rule.Head.Key, func() error { + value := p.ltarget + factory := func(v ir.Local) ir.Stmt { return &ir.MakeSetStmt{Target: v} } + return p.planDotOr(obj, key, factory, func(set ir.Local) error { + p.appendStmt(&ir.SetAddStmt{ + Set: set, + Value: value, + }) + p.appendStmt(&ir.ObjectInsertStmt{Key: key, Value: op(set), Object: obj}) + return nil + }) + }) + }) + }) + } + return p.planTerm(rule.Head.Key, func() error { + p.appendStmt(&ir.SetAddStmt{ + Set: fn.Return, + Value: p.ltarget, + }) + return nil + }) + default: + return errors.New("illegal rule kind") + } + }) + }) + + if err != nil { + return "", err + } + } + + // rule[i] and its else-rule(s), if present, are done + if rules[i].Head.RuleKind() == ast.SingleValue && !buildObject { + end := &ir.Block{} + p.appendStmtToBlock(&ir.IsDefinedStmt{Source: lresult}, end) + p.appendStmtToBlock( + &ir.AssignVarOnceStmt{ + Target: fn.Return, + Source: op(lresult), + }, + end) + *blocks = append(*blocks, end) + } + } + + // Default rules execute if the return is undefined. + if defaultRule != nil { + + // Set the location for the default rule head. + p.loc = defaultRule.Head.Loc() + // NOTE(sr) for `default p = 1`, + // defaultRule.Loc() is `default`, + // defaultRule.Head.Loc() is `p = 1`. + + fn.Blocks = append(fn.Blocks, p.blockWithStmt(&ir.IsUndefinedStmt{Source: fn.Return})) + + p.curr = fn.Blocks[len(fn.Blocks)-1] + + err := p.planQuery(defaultRule.Body, 0, func() error { + p.loc = defaultRule.Head.Loc() + return p.planTerm(defaultRule.Head.Value, func() error { + p.appendStmt(&ir.AssignVarOnceStmt{ + Target: fn.Return, + Source: p.ltarget, + }) + return nil + }) + }) + + if err != nil { + return "", err + } + } + + p.loc = ruleLoc + + // All rules return a value. + fn.Blocks = append(fn.Blocks, p.blockWithStmt(&ir.ReturnLocalStmt{Source: fn.Return})) + + p.appendFunc(fn) + p.funcs.Add(path, fn.Name) + + // Restore the state of the planner. + p.lnext = plnext + p.ltarget = pltarget + p.vars = pvars + p.curr = pcurr + p.loc = ploc + + return fn.Name, nil +} + +func (p *Planner) planDotOr(obj ir.Local, key ir.Operand, or stmtFactory, iter planLocalIter) error { + // We're constructing the following plan: + // + // | block a + // | | block b + // | | | dot &{Source:Local Key:{Value:Local} Target:Local} + // | | | break 1 + // | | or &{Target:Local} + // | iter &{Target:Local} # may update Local. + // | *ir.ObjectInsertStmt &{Key:{Value:Local} Value:{Value:Local} Object:Local} + + prev := p.curr + dotBlock := &ir.Block{} + p.curr = dotBlock + + val := p.newLocal() + p.appendStmt(&ir.DotStmt{ + Source: op(obj), + Key: key, + Target: val, + }) + p.appendStmt(&ir.BreakStmt{Index: 1}) + + outerBlock := &ir.Block{ + Stmts: []ir.Stmt{ + &ir.BlockStmt{Blocks: []*ir.Block{dotBlock}}, // FIXME: Set Location + or(val), + }, + } + + p.curr = prev + p.appendStmt(&ir.BlockStmt{Blocks: []*ir.Block{outerBlock}}) + if err := iter(val); err != nil { + return err + } + p.appendStmt(&ir.ObjectInsertStmt{Key: key, Value: op(val), Object: obj}) + return nil +} + +func (p *Planner) planNestedObjects(obj ir.Local, ref ast.Ref, iter planLocalIter) error { + if len(ref) == 0 { + return iter(obj) + } + + t := ref[0] + + return p.planTerm(t, func() error { + key := p.ltarget + + factory := func(v ir.Local) ir.Stmt { return &ir.MakeObjectStmt{Target: v} } + return p.planDotOr(obj, key, factory, func(childObj ir.Local) error { + return p.planNestedObjects(childObj, ref[1:], iter) + }) + }) +} + +func (p *Planner) planFuncParams(params []ir.Local, args ast.Args, idx int, iter planiter) error { + if idx >= len(args) { + return iter() + } + return p.planUnifyLocal(op(params[idx]), args[idx], func() error { + return p.planFuncParams(params, args, idx+1, iter) + }) +} + +func (p *Planner) planQueries() error { + + for _, qs := range p.queries { + + // Initialize the plan with a block that prepares the query result. + p.plan = &ir.Plan{Name: qs.Name} + p.policy.Plans.Plans = append(p.policy.Plans.Plans, p.plan) + p.curr = &ir.Block{} + + // Build a set of variables appearing in the query and allocate strings for + // each one. The strings will be used in the result set objects. + qvs := ast.NewVarSet() + + for _, q := range qs.Queries { + vs := q.Vars(ast.VarVisitorParams{SkipRefCallHead: true, SkipClosures: true}).Diff(ast.ReservedVars) + qvs.Update(vs) + } + + lvarnames := make(map[ast.Var]ir.StringIndex, len(qvs)) + + for _, qv := range qvs.Sorted() { + qv = rewrittenVar(qs.RewrittenVars, qv) + if !qv.IsGenerated() && !qv.IsWildcard() { + lvarnames[qv] = ir.StringIndex(p.getStringConst(string(qv))) + } + } + + if len(p.curr.Stmts) > 0 { + p.appendBlock(p.curr) + } + + lnext := p.lnext + + for _, q := range qs.Queries { + p.loc = q.Loc() + p.lnext = lnext + p.vars.Push(map[ast.Var]ir.Local{}) + p.curr = &ir.Block{} + defined := false + qvs := q.Vars(ast.VarVisitorParams{SkipRefCallHead: true, SkipClosures: true}).Diff(ast.ReservedVars).Sorted() + + if err := p.planQuery(q, 0, func() error { + + // Add an object containing variable bindings into the result set. + lr := p.newLocal() + + p.appendStmt(&ir.MakeObjectStmt{ + Target: lr, + }) + + for _, qv := range qvs { + rw := rewrittenVar(qs.RewrittenVars, qv) + if !rw.IsGenerated() && !rw.IsWildcard() { + p.appendStmt(&ir.ObjectInsertStmt{ + Object: lr, + Key: op(lvarnames[rw]), + Value: p.vars.GetOpOrEmpty(qv), + }) + } + } + + p.appendStmt(&ir.ResultSetAddStmt{ + Value: lr, + }) + + defined = true + return nil + }); err != nil { + return err + } + + p.vars.Pop() + + if defined { + p.appendBlock(p.curr) + } + } + + } + + return nil +} + +func (p *Planner) planQuery(q ast.Body, index int, iter planiter) error { + + if index >= len(q) { + return iter() + } + + old := p.loc + p.loc = q[index].Loc() + + err := p.planExpr(q[index], func() error { + return p.planQuery(q, index+1, func() error { + curr := p.loc + p.loc = old + err := iter() + p.loc = curr + return err + }) + }) + + p.loc = old + return err +} + +// TODO(tsandall): improve errors to include location information. +func (p *Planner) planExpr(e *ast.Expr, iter planiter) error { + + switch { + case e.Negated: + return p.planNot(e, iter) + + case len(e.With) > 0: + return p.planWith(e, iter) + + case e.IsCall(): + return p.planExprCall(e, iter) + + case e.IsEvery(): + return p.planExprEvery(e, iter) + } + + return p.planExprTerm(e, iter) +} + +func (p *Planner) planNot(e *ast.Expr, iter planiter) error { + not := &ir.NotStmt{ + Block: &ir.Block{}, + } + + prev := p.curr + p.curr = not.Block + + if err := p.planExpr(e.Complement(), func() error { return nil }); err != nil { + return err + } + + p.curr = prev + p.appendStmt(not) + + return iter() +} + +func (p *Planner) planWith(e *ast.Expr, iter planiter) error { + + // Plan the values that will be applied by the `with` modifiers. All values + // must be defined for the overall expression to evaluate. + values := make([]*ast.Term, 0, len(e.With)) // NOTE(sr): we could be overallocating if there are builtin replacements + targets := make([]ast.Ref, 0, len(e.With)) + + vars := []ast.Var{} + mocks := frame{} + + for _, w := range e.With { + v := w.Target.Value.(ast.Ref) + + switch { + case p.isFunctionOrBuiltin(v): // track var values + if wvar, ok := w.Value.Value.(ast.Var); ok { + vars = append(vars, wvar) + } + + case ast.DefaultRootDocument.Equal(v[0]) || + ast.InputRootDocument.Equal(v[0]): + + values = append(values, w.Value) + targets = append(targets, w.Target.Value.(ast.Ref)) + + continue // not a mock + } + + mocks[w.Target.String()] = w.Value + } + + return p.planTermSlice(values, func(locals []ir.Operand) error { + + p.mocks.PushFrame(mocks) + + paths := make([][]int, len(targets)) + saveVars := ast.NewVarSet() + dataRefs := []ast.Ref{} + + for i, target := range targets { + paths[i] = make([]int, len(target)-1) + + for j := 1; j < len(target); j++ { + if s, ok := target[j].Value.(ast.String); ok { + paths[i][j-1] = p.getStringConst(string(s)) + } else { + return errors.New("invalid with target") + } + } + + head := target[0].Value.(ast.Var) + saveVars.Add(head) + + if head.Equal(ast.DefaultRootDocument.Value) { + dataRefs = append(dataRefs, target) + } + } + + restore := make([][2]ir.Local, len(saveVars)) + + for i, v := range saveVars.Sorted() { + lorig := p.vars.GetOrEmpty(v) + lsave := p.newLocal() + p.appendStmt(&ir.AssignVarStmt{Source: op(lorig), Target: lsave}) + restore[i] = [2]ir.Local{lorig, lsave} + } + + // If any of the `with` statements targeted the data document, overwriting + // parts of the ruletrie; or if a function has been mocked, we shadow the + // existing planned functions during expression planning. + // This causes the planner to re-plan any rules that may be required during + // planning of this expression (transitively). + shadowing := p.dataRefsShadowRuletrie(dataRefs) || len(mocks) > 0 + if shadowing { + p.funcs.Push(map[string]string{}, vars) + for _, ref := range dataRefs { + p.rules.Push(ref) + } + } + + err := p.planWithRec(e, paths, locals, 0, func() error { + p.mocks.PopFrame() + if shadowing { + p.funcs.Pop() + for i := len(dataRefs) - 1; i >= 0; i-- { + p.rules.Pop(dataRefs[i]) + } + } + + err := p.planWithUndoRec(restore, 0, func() error { + + err := iter() + + p.mocks.PushFrame(mocks) + if shadowing { + p.funcs.Push(map[string]string{}, vars) + for _, ref := range dataRefs { + p.rules.Push(ref) + } + } + return err + }) + + return err + }) + + p.mocks.PopFrame() + if shadowing { + p.funcs.Pop() + for i := len(dataRefs) - 1; i >= 0; i-- { + p.rules.Pop(dataRefs[i]) + } + } + return err + + }) +} + +func (p *Planner) planWithRec(e *ast.Expr, targets [][]int, values []ir.Operand, index int, iter planiter) error { + if index >= len(targets) { + return p.planExpr(e.NoWith(), iter) + } + + prev := p.curr + p.curr = &ir.Block{} + + err := p.planWithRec(e, targets, values, index+1, iter) + if err != nil { + return err + } + + block := p.curr + p.curr = prev + target := e.With[index].Target.Value.(ast.Ref) + head := target[0].Value.(ast.Var) + + p.appendStmt(&ir.WithStmt{ + Local: p.vars.GetOrEmpty(head), + Path: targets[index], + Value: values[index], + Block: block, + }) + + return nil +} + +func (p *Planner) planWithUndoRec(restore [][2]ir.Local, index int, iter planiter) error { + + if index >= len(restore) { + return iter() + } + + prev := p.curr + p.curr = &ir.Block{} + + if err := p.planWithUndoRec(restore, index+1, iter); err != nil { + return err + } + + block := p.curr + p.curr = prev + lorig := restore[index][0] + lsave := restore[index][1] + + p.appendStmt(&ir.WithStmt{ + Local: lorig, + Value: op(lsave), + Block: block, + }) + + return nil +} + +func (p *Planner) dataRefsShadowRuletrie(refs []ast.Ref) bool { + for _, ref := range refs { + if p.rules.Lookup(ref) != nil { + return true + } + } + return false +} + +func (p *Planner) planExprTerm(e *ast.Expr, iter planiter) error { + // NOTE(sr): There are only three cases to deal with when we see a naked term + // in a rule body: + // 1. it's `false` -- so we can stop, emit a break stmt + // 2. it's a var or a ref, like `input` or `data.foo.bar`, where we need to + // check what it ends up being (at run time) to determine if it's not false + // 3. it's any other term -- `true`, a string, a number, whatever. We can skip + // that, since it's true-ish enough for evaluating the rule body. + switch t := e.Terms.(*ast.Term).Value.(type) { + case ast.Boolean: + if !bool(t) { // We know this cannot hold, break unconditionally + p.appendStmt(&ir.BreakStmt{}) + return iter() + } + case ast.Ref, ast.Var: // We don't know these at plan-time + return p.planTerm(e.Terms.(*ast.Term), func() error { + p.appendStmt(&ir.NotEqualStmt{ + A: p.ltarget, + B: op(ir.Bool(false)), + }) + return iter() + }) + } + return iter() +} + +func (p *Planner) planExprEvery(e *ast.Expr, iter planiter) error { + every := e.Terms.(*ast.Every) + + cond0 := p.newLocal() // outer not + cond1 := p.newLocal() // inner not + + // We're using condition variables together with IsDefinedStmt to encode + // this: + // every x, y in xs { p(x,y) } + // ~> p(x1, y1) AND p(x2, y2) AND ... AND p(xn, yn) + // ~> NOT (NOT p(x1, y1) OR NOT p(x2, y2) OR ... OR NOT p(xn, yn)) + // + // cond1 is initialized to 0, and set to TRUE if p(xi, yi) succeeds for + // a binding of (xi, yi). We then use IsUndefined to check that this has NOT + // happened (NOT p(xi, yi)). + // cond0 is initialized to 0, and set to TRUE if cond1 happens to not + // be set: it's encoding the NOT ( ... OR ... OR ... ) part of this. + + p.appendStmt(&ir.ResetLocalStmt{ + Target: cond0, + }) + + 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, + }) + nested := &ir.BlockStmt{Blocks: []*ir.Block{{}}} + + prev := p.curr + p.curr = nested.Blocks[0] + + lval := p.ltarget + err := p.planUnifyLocal(lval, every.Value, func() error { + return p.planQuery(every.Body, 0, func() error { + p.appendStmt(&ir.AssignVarStmt{ + Source: op(ir.Bool(true)), + Target: cond1, + }) + return nil + }) + }) + if err != nil { + return err + } + + p.curr = prev + p.appendStmt(nested) + p.appendStmt(&ir.IsUndefinedStmt{ + Source: cond1, + }) + p.appendStmt(&ir.AssignVarStmt{ + Source: op(ir.Bool(true)), + Target: cond0, + }) + return nil + }) + }) + if err != nil { + return err + } + + p.appendStmt(&ir.IsUndefinedStmt{ + Source: cond0, + }) + return iter() +} + +func (p *Planner) planExprCall(e *ast.Expr, iter planiter) error { + operator := e.Operator().String() + switch operator { + case ast.Equality.Name: + return p.planUnify(e.Operand(0), e.Operand(1), iter) + + default: + + var relation bool + var name string + var arity int + var void bool + var args []ir.Operand + var err error + + operands := e.Operands() + op := e.Operator() + + if replacement := p.mocks.Lookup(operator); replacement != nil { + if _, ok := replacement.Value.(ast.Var); ok { + var arity int + if node := p.rules.Lookup(op); node != nil { + arity = node.Arity() // NB(sr): We don't need to plan what isn't called, only lookup arity + } else if bi, ok := p.decls[operator]; ok { + arity = bi.Decl.Arity() + } + return p.planExprCallValue(replacement, arity, operands, iter) + } + if r, ok := replacement.Value.(ast.Ref); ok { + if !r.HasPrefix(ast.DefaultRootRef) && !r.HasPrefix(ast.InputRootRef) { + // replacement is builtin + operator = r.String() + bi := p.decls[operator] + p.externs[operator] = bi + + // void functions and relations are forbidden; arity validation happened in compiler + return p.planExprCallFunc(operator, len(bi.Decl.FuncArgs().Args), void, operands, args, iter) + } + + // replacement is a function (rule) + if node := p.rules.Lookup(r); node != nil { + if node.Arity() > 0 { + p.mocks.Push() // new scope + name, err = p.planRules(node.Rules()) + if err != nil { + return err + } + p.mocks.Pop() + return p.planExprCallFunc(name, node.Arity(), void, operands, p.defaultOperands(), iter) + } + // arity==0 => replacement is a ref to a rule to be used as value (fallthrough) + } + } + + // replacement is a value, or ref + if bi, ok := p.decls[operator]; ok { + return p.planExprCallValue(replacement, bi.Decl.Arity(), operands, iter) + } + if node := p.rules.Lookup(op); node != nil { + return p.planExprCallValue(replacement, node.Arity(), operands, iter) + } + return fmt.Errorf("illegal replacement of operator %q by %v", operator, replacement) // should be unreachable + } + + if node := p.rules.Lookup(op); node != nil { + name, err = p.planRules(node.Rules()) + if err != nil { + return err + } + arity = node.Arity() + args = p.defaultOperands() + } else if decl, ok := p.decls[operator]; ok { + relation = decl.Relation + arity = decl.Decl.Arity() + void = decl.Decl.Result() == nil + name = operator + p.externs[operator] = decl + } else { + return fmt.Errorf("illegal call: unknown operator %q", operator) + } + + if len(operands) < arity || len(operands) > arity+1 { + return fmt.Errorf("illegal call: wrong number of operands: got %v, want %v)", len(operands), arity) + } + + if relation { + return p.planExprCallRelation(name, arity, operands, args, iter) + } + + return p.planExprCallFunc(name, arity, void, operands, args, iter) + } +} + +func (p *Planner) planExprCallRelation(name string, arity int, operands []*ast.Term, args []ir.Operand, iter planiter) error { + + if len(operands) == arity { + return p.planCallArgs(operands, 0, args, func(args []ir.Operand) error { + p.ltarget = p.newOperand() + ltarget := p.ltarget.Value.(ir.Local) + p.appendStmt(&ir.CallStmt{ + Func: name, + Args: args, + Result: ltarget, + }) + + lsize := p.newLocal() + + p.appendStmt(&ir.LenStmt{ + Source: op(ltarget), + Target: lsize, + }) + + lzero := p.newLocal() + + p.appendStmt(&ir.MakeNumberIntStmt{ + Value: 0, + Target: lzero, + }) + + p.appendStmt(&ir.NotEqualStmt{ + A: op(lsize), + B: op(lzero), + }) + + return iter() + }) + } + + return p.planCallArgs(operands[:len(operands)-1], 0, args, func(args []ir.Operand) error { + + p.ltarget = p.newOperand() + + p.appendStmt(&ir.CallStmt{ + Func: name, + Args: args, + Result: p.ltarget.Value.(ir.Local), + }) + + return p.planScanValues(operands[len(operands)-1], func(ir.Local) error { + return iter() + }) + }) +} + +func (p *Planner) planExprCallFunc(name string, arity int, void bool, operands []*ast.Term, args []ir.Operand, iter planiter) error { + + switch { + case len(operands) == arity: + // definition: f(x) = y { ... } + // call: f(x) # result not captured + return p.planCallArgs(operands, 0, args, func(args []ir.Operand) error { + p.ltarget = p.newOperand() + ltarget := p.ltarget.Value.(ir.Local) + p.appendStmt(&ir.CallStmt{ + Func: name, + Args: args, + Result: ltarget, + }) + + if !void { + p.appendStmt(&ir.NotEqualStmt{ + A: op(ltarget), + B: op(ir.Bool(false)), + }) + } + + return iter() + }) + + case len(operands) == arity+1: + // definition: f(x) = y { ... } + // call: f(x, 1) # caller captures result + return p.planCallArgs(operands[:len(operands)-1], 0, args, func(args []ir.Operand) error { + result := p.newLocal() + p.appendStmt(&ir.CallStmt{ + Func: name, + Args: args, + Result: result, + }) + return p.planUnifyLocal(op(result), operands[len(operands)-1], iter) + }) + + default: + return errors.New("impossible replacement, arity mismatch") + } +} + +func (p *Planner) planExprCallValue(value *ast.Term, arity int, operands []*ast.Term, iter planiter) error { + switch { + case len(operands) == arity: // call: f(x) # result not captured + return p.planCallArgs(operands, 0, nil, func([]ir.Operand) error { + p.ltarget = p.newOperand() + return p.planTerm(value, func() error { + p.appendStmt(&ir.NotEqualStmt{ + A: p.ltarget, + B: op(ir.Bool(false)), + }) + return iter() + }) + }) + + case len(operands) == arity+1: // call: f(x, 1) # caller captures result + return p.planCallArgs(operands[:len(operands)-1], 0, nil, func([]ir.Operand) error { + p.ltarget = p.newOperand() + return p.planTerm(value, func() error { + return p.planUnifyLocal(p.ltarget, operands[len(operands)-1], iter) + }) + }) + default: + return errors.New("impossible replacement, arity mismatch") + } +} + +func (p *Planner) planCallArgs(terms []*ast.Term, idx int, args []ir.Operand, iter func([]ir.Operand) error) error { + if idx >= len(terms) { + return iter(args) + } + return p.planTerm(terms[idx], func() error { + args = append(args, p.ltarget) + return p.planCallArgs(terms, idx+1, args, iter) + }) +} + +func (p *Planner) planUnify(a, b *ast.Term, iter planiter) error { + + switch va := a.Value.(type) { + case ast.Null, ast.Boolean, ast.Number, ast.String, ast.Ref, ast.Set, *ast.SetComprehension, *ast.ArrayComprehension, *ast.ObjectComprehension: + return p.planTerm(a, func() error { + return p.planUnifyLocal(p.ltarget, b, iter) + }) + case ast.Var: + return p.planUnifyVar(va, b, iter) + case *ast.Array: + switch vb := b.Value.(type) { + case ast.Var: + return p.planUnifyVar(vb, a, iter) + case ast.Ref: + return p.planTerm(b, func() error { + return p.planUnifyLocalArray(p.ltarget, va, iter) + }) + case *ast.ArrayComprehension: + return p.planTerm(b, func() error { + return p.planUnifyLocalArray(p.ltarget, va, iter) + }) + case *ast.Array: + if va.Len() == vb.Len() { + return p.planUnifyArraysRec(va, vb, 0, iter) + } + return nil + } + case ast.Object: + switch vb := b.Value.(type) { + case ast.Var: + return p.planUnifyVar(vb, a, iter) + case ast.Ref: + return p.planTerm(b, func() error { + return p.planUnifyLocalObject(p.ltarget, va, iter) + }) + case ast.Object: + return p.planUnifyObjects(va, vb, iter) + } + } + + return fmt.Errorf("not implemented: unify(%v, %v)", a, b) +} + +func (p *Planner) planUnifyVar(a ast.Var, b *ast.Term, iter planiter) error { + + if la, ok := p.vars.GetOp(a); ok { + return p.planUnifyLocal(la, b, iter) + } + + return p.planTerm(b, func() error { + // `a` may have become known while planning b, like in `a = input.x[a]` + la, ok := p.vars.GetOp(a) + if ok { + p.appendStmt(&ir.EqualStmt{ + A: la, + B: p.ltarget, + }) + } else { + target := p.newLocal() + p.vars.Put(a, target) + p.appendStmt(&ir.AssignVarStmt{ + Source: p.ltarget, + Target: target, + }) + } + return iter() + }) +} + +func (p *Planner) planUnifyLocal(a ir.Operand, b *ast.Term, iter planiter) error { + // special cases: when a is StringIndex or Bool, and b is a string, or a bool, we can shortcut + switch va := a.Value.(type) { + case ir.StringIndex: + if vb, ok := b.Value.(ast.String); ok { + if va != ir.StringIndex(p.getStringConst(string(vb))) { + p.appendStmt(&ir.BreakStmt{}) + } + return iter() // Don't plan EqualStmt{A: "foo", B: "foo"} + } + case ir.Bool: + if vb, ok := b.Value.(ast.Boolean); ok { + if va != ir.Bool(vb) { + p.appendStmt(&ir.BreakStmt{}) + } + return iter() // Don't plan EqualStmt{A: true, B: true} + } + } + + switch vb := b.Value.(type) { + case ast.Null, ast.Boolean, ast.Number, ast.String, ast.Ref, ast.Set, *ast.SetComprehension, *ast.ArrayComprehension, *ast.ObjectComprehension: + return p.planTerm(b, func() error { + p.appendStmt(&ir.EqualStmt{ + A: a, + B: p.ltarget, + }) + return iter() + }) + case ast.Var: + if lv, ok := p.vars.GetOp(vb); ok { + p.appendStmt(&ir.EqualStmt{ + A: a, + B: lv, + }) + return iter() + } + lv := p.newLocal() + p.vars.Put(vb, lv) + p.appendStmt(&ir.AssignVarStmt{ + Source: a, + Target: lv, + }) + return iter() + case *ast.Array: + return p.planUnifyLocalArray(a, vb, iter) + case ast.Object: + return p.planUnifyLocalObject(a, vb, iter) + } + + return fmt.Errorf("not implemented: unifyLocal(%v, %v)", a, b) +} + +func (p *Planner) planUnifyLocalArray(a ir.Operand, b *ast.Array, iter planiter) error { + p.appendStmt(&ir.IsArrayStmt{ + Source: a, + }) + + blen := p.newLocal() + alen := p.newLocal() + + p.appendStmt(&ir.LenStmt{ + Source: a, + Target: alen, + }) + + p.appendStmt(&ir.MakeNumberIntStmt{ + Value: int64(b.Len()), + Target: blen, + }) + + p.appendStmt(&ir.EqualStmt{ + A: op(alen), + B: op(blen), + }) + + lkey := p.newLocal() + + p.appendStmt(&ir.MakeNumberIntStmt{ + Target: lkey, + }) + + lval := p.newLocal() + + return p.planUnifyLocalArrayRec(a, 0, b, lkey, lval, iter) +} + +func (p *Planner) planUnifyLocalArrayRec(a ir.Operand, index int, b *ast.Array, lkey, lval ir.Local, iter planiter) error { + if b.Len() == index { + return iter() + } + + p.appendStmt(&ir.AssignIntStmt{ + Value: int64(index), + Target: lkey, + }) + + p.appendStmt(&ir.DotStmt{ + Source: a, + Key: op(lkey), + Target: lval, + }) + + return p.planUnifyLocal(op(lval), b.Elem(index), func() error { + return p.planUnifyLocalArrayRec(a, index+1, b, lkey, lval, iter) + }) +} + +func (p *Planner) planUnifyObjects(a, b ast.Object, iter planiter) error { + if a.Len() != b.Len() { + return nil + } + + aKeys := ast.NewSet(a.Keys()...) + bKeys := ast.NewSet(b.Keys()...) + unifyKeys := aKeys.Diff(bKeys) + + // planUnifyObjectsRec will actually set variables where possible; + // planUnifyObjectLocals only asserts equality -- it won't assign + // to any local + return p.planUnifyObjectsRec(a, b, aKeys.Intersect(bKeys).Slice(), 0, func() error { + if unifyKeys.Len() == 0 { + return iter() + } + return p.planObject(a, func() error { + la := p.ltarget + return p.planObject(b, func() error { + return p.planUnifyObjectLocals(la, p.ltarget, unifyKeys.Slice(), 0, p.newLocal(), p.newLocal(), iter) + }) + }) + }) +} + +func (p *Planner) planUnifyObjectLocals(a, b ir.Operand, keys []*ast.Term, index int, l0, l1 ir.Local, iter planiter) error { + if index == len(keys) { + return iter() + } + + return p.planTerm(keys[index], func() error { + p.appendStmt(&ir.DotStmt{ + Source: a, + Key: p.ltarget, + Target: l0, + }) + p.appendStmt(&ir.DotStmt{ + Source: b, + Key: p.ltarget, + Target: l1, + }) + p.appendStmt(&ir.EqualStmt{ + A: op(l0), + B: op(l1), + }) + + return p.planUnifyObjectLocals(a, b, keys, index+1, l0, l1, iter) + }) +} + +func (p *Planner) planUnifyLocalObject(a ir.Operand, b ast.Object, iter planiter) error { + p.appendStmt(&ir.IsObjectStmt{ + Source: a, + }) + + blen := p.newLocal() + alen := p.newLocal() + + p.appendStmt(&ir.LenStmt{ + Source: a, + Target: alen, + }) + + p.appendStmt(&ir.MakeNumberIntStmt{ + Value: int64(b.Len()), + Target: blen, + }) + + p.appendStmt(&ir.EqualStmt{ + A: op(alen), + B: op(blen), + }) + + lval := p.newLocal() + bkeys := b.Keys() + + return p.planUnifyLocalObjectRec(a, 0, bkeys, b, lval, iter) +} + +func (p *Planner) planUnifyLocalObjectRec(a ir.Operand, index int, keys []*ast.Term, b ast.Object, lval ir.Local, iter planiter) error { + + if index == len(keys) { + return iter() + } + + return p.planTerm(keys[index], func() error { + p.appendStmt(&ir.DotStmt{ + Source: a, + Key: p.ltarget, + Target: lval, + }) + return p.planUnifyLocal(op(lval), b.Get(keys[index]), func() error { + return p.planUnifyLocalObjectRec(a, index+1, keys, b, lval, iter) + }) + }) +} + +func (p *Planner) planUnifyArraysRec(a, b *ast.Array, index int, iter planiter) error { + if index == a.Len() { + return iter() + } + return p.planUnify(a.Elem(index), b.Elem(index), func() error { + return p.planUnifyArraysRec(a, b, index+1, iter) + }) +} + +func (p *Planner) planUnifyObjectsRec(a, b ast.Object, keys []*ast.Term, index int, iter planiter) error { + if index == len(keys) { + return iter() + } + + aval := a.Get(keys[index]) + bval := b.Get(keys[index]) + if aval == nil || bval == nil { + return nil + } + + return p.planUnify(aval, bval, func() error { + return p.planUnifyObjectsRec(a, b, keys, index+1, iter) + }) +} + +func (p *Planner) planTerm(t *ast.Term, iter planiter) error { + return p.planValue(t.Value, t.Loc(), iter) +} + +func (p *Planner) planValue(t ast.Value, loc *ast.Location, iter planiter) error { + switch v := t.(type) { + case ast.Null: + return p.planNull(v, iter) + case ast.Boolean: + return p.planBoolean(v, iter) + case ast.Number: + return p.planNumber(v, iter) + case ast.String: + return p.planString(v, iter) + case ast.Var: + return p.planVar(v, iter) + case ast.Ref: + return p.planRef(v, iter) + case *ast.Array: + return p.planArray(v, iter) + case ast.Object: + return p.planObject(v, iter) + case ast.Set: + return p.planSet(v, iter) + case *ast.SetComprehension: + p.loc = loc + return p.planSetComprehension(v, iter) + case *ast.ArrayComprehension: + p.loc = loc + return p.planArrayComprehension(v, iter) + case *ast.ObjectComprehension: + p.loc = loc + return p.planObjectComprehension(v, iter) + default: + return fmt.Errorf("%v term not implemented", ast.ValueName(v)) + } +} + +func (p *Planner) planNull(_ ast.Null, iter planiter) error { + + target := p.newLocal() + + p.appendStmt(&ir.MakeNullStmt{ + Target: target, + }) + + p.ltarget = op(target) + + return iter() +} + +func (p *Planner) planBoolean(b ast.Boolean, iter planiter) error { + + p.ltarget = op(ir.Bool(b)) + return iter() +} + +func (p *Planner) planNumber(num ast.Number, iter planiter) error { + + index := p.getStringConst(string(num)) + target := p.newLocal() + + p.appendStmt(&ir.MakeNumberRefStmt{ + Index: index, + Target: target, + }) + + p.ltarget = op(target) + return iter() +} + +func (p *Planner) planString(str ast.String, iter planiter) error { + + p.ltarget = op(ir.StringIndex(p.getStringConst(string(str)))) + + return iter() +} + +func (p *Planner) planVar(v ast.Var, iter planiter) error { + p.ltarget = op(p.vars.GetOrElse(v, p.newLocal)) + return iter() +} + +func (p *Planner) planArray(arr *ast.Array, iter planiter) error { + + larr := p.newLocal() + + p.appendStmt(&ir.MakeArrayStmt{ + Capacity: int32(arr.Len()), + Target: larr, + }) + + return p.planArrayRec(arr, 0, larr, iter) +} + +func (p *Planner) planArrayRec(arr *ast.Array, index int, larr ir.Local, iter planiter) error { + if index == arr.Len() { + p.ltarget = op(larr) + return iter() + } + + return p.planTerm(arr.Elem(index), func() error { + + p.appendStmt(&ir.ArrayAppendStmt{ + Value: p.ltarget, + Array: larr, + }) + + return p.planArrayRec(arr, index+1, larr, iter) + }) +} + +func (p *Planner) planObject(obj ast.Object, iter planiter) error { + + lobj := p.newLocal() + + p.appendStmt(&ir.MakeObjectStmt{ + Target: lobj, + }) + + return p.planObjectRec(obj, 0, obj.Keys(), lobj, iter) +} + +func (p *Planner) planObjectRec(obj ast.Object, index int, keys []*ast.Term, lobj ir.Local, iter planiter) error { + if index == len(keys) { + p.ltarget = op(lobj) + return iter() + } + + return p.planTerm(keys[index], func() error { + lkey := p.ltarget + + return p.planTerm(obj.Get(keys[index]), func() error { + lval := p.ltarget + p.appendStmt(&ir.ObjectInsertStmt{ + Key: lkey, + Value: lval, + Object: lobj, + }) + + return p.planObjectRec(obj, index+1, keys, lobj, iter) + }) + }) +} + +func (p *Planner) planSet(set ast.Set, iter planiter) error { + lset := p.newLocal() + + p.appendStmt(&ir.MakeSetStmt{ + Target: lset, + }) + + return p.planSetRec(set, 0, set.Slice(), lset, iter) +} + +func (p *Planner) planSetRec(set ast.Set, index int, elems []*ast.Term, lset ir.Local, iter planiter) error { + if index == len(elems) { + p.ltarget = op(lset) + return iter() + } + + return p.planTerm(elems[index], func() error { + p.appendStmt(&ir.SetAddStmt{ + Value: p.ltarget, + Set: lset, + }) + return p.planSetRec(set, index+1, elems, lset, iter) + }) +} + +func (p *Planner) planSetComprehension(sc *ast.SetComprehension, iter planiter) error { + + lset := p.newLocal() + + p.appendStmt(&ir.MakeSetStmt{ + Target: lset, + }) + + return p.planComprehension(sc.Body, func() error { + return p.planTerm(sc.Term, func() error { + p.appendStmt(&ir.SetAddStmt{ + Value: p.ltarget, + Set: lset, + }) + return nil + }) + }, lset, iter) +} + +func (p *Planner) planArrayComprehension(ac *ast.ArrayComprehension, iter planiter) error { + + larr := p.newLocal() + + p.appendStmt(&ir.MakeArrayStmt{ + Target: larr, + }) + + return p.planComprehension(ac.Body, func() error { + return p.planTerm(ac.Term, func() error { + p.appendStmt(&ir.ArrayAppendStmt{ + Value: p.ltarget, + Array: larr, + }) + return nil + }) + }, larr, iter) +} + +func (p *Planner) planObjectComprehension(oc *ast.ObjectComprehension, iter planiter) error { + + lobj := p.newLocal() + + p.appendStmt(&ir.MakeObjectStmt{ + Target: lobj, + }) + return p.planComprehension(oc.Body, func() error { + return p.planTerm(oc.Key, func() error { + lkey := p.ltarget + return p.planTerm(oc.Value, func() error { + p.appendStmt(&ir.ObjectInsertOnceStmt{ + Key: lkey, + Value: p.ltarget, + Object: lobj, + }) + return nil + }) + }) + }, lobj, iter) +} + +func (p *Planner) planComprehension(body ast.Body, closureIter planiter, target ir.Local, iter planiter) error { + + // Variables that have been introduced in this comprehension have + // no effect on other parts of the policy, so they'll be dropped + // below. + p.vars.Push(map[ast.Var]ir.Local{}) + prev := p.curr + block := &ir.Block{} + p.curr = block + ploc := p.loc + + if err := p.planQuery(body, 0, closureIter); err != nil { + return err + } + + p.curr = prev + p.loc = ploc + p.vars.Pop() + + p.appendStmt(&ir.BlockStmt{ + Blocks: []*ir.Block{ + block, + }, + }) + + p.ltarget = op(target) + return iter() +} + +func (p *Planner) planRef(ref ast.Ref, iter planiter) error { + + head, ok := ref[0].Value.(ast.Var) + if !ok { + return errors.New("illegal ref: non-var head") + } + + if head.Compare(ast.DefaultRootDocument.Value) == 0 { + virtual := p.rules.Get(ref[0].Value) + base := &baseptr{local: p.vars.GetOrEmpty(ast.DefaultRootDocument.Value.(ast.Var))} + return p.planRefData(virtual, base, ref, 1, iter) + } + + if ref.Equal(ast.InputRootRef) { + p.appendStmt(&ir.IsDefinedStmt{ + Source: p.vars.GetOrEmpty(ast.InputRootDocument.Value.(ast.Var)), + }) + } + + p.ltarget, ok = p.vars.GetOp(head) + if !ok { + return errors.New("illegal ref: unsafe head") + } + + return p.planRefRec(ref, 1, iter) +} + +func (p *Planner) planRefRec(ref ast.Ref, index int, iter planiter) error { + + if len(ref) == index { + return iter() + } + + if !p.unseenVars(ref[index]) { + return p.planDot(ref[index], func() error { + return p.planRefRec(ref, index+1, iter) + }) + } + + return p.planScan(ref[index], func(ir.Local) error { + return p.planRefRec(ref, index+1, iter) + }) +} + +type baseptr struct { + local ir.Local + path ast.Ref +} + +// planRefData implements the virtual document model by generating the value of +// the ref parameter and invoking the iterator with the planner target set to +// the virtual document and all variables in the reference assigned. +func (p *Planner) planRefData(virtual *ruletrie, base *baseptr, ref ast.Ref, index int, iter planiter) error { + + // Early-exit if the end of the reference has been reached. In this case the + // plan has to materialize the full extent of the referenced value. + if index >= len(ref) { + return p.planRefDataExtent(virtual, base, iter) + } + + // On the first iteration, we check if this can be optimized using a + // CallDynamicStatement + // NOTE(sr): we do it on the first index because later on, the recursion + // on subtrees of virtual already lost parts of the path we've taken. + if index == 1 && virtual != nil { + rulesets, path, index, optimize := p.optimizeLookup(virtual, ref) + if optimize { + // If there are no rulesets in a situation that otherwise would + // allow for a call_indirect optimization, then there's nothing + // to do for this ref, except scanning the base document. + if len(rulesets) == 0 { + return p.planRefData(nil, base, ref, 1, iter) // ignore index returned by optimizeLookup + } + // plan rules + for _, rules := range rulesets { + if _, err := p.planRules(rules); err != nil { + return err + } + } + + // We're planning a structure like this: + // + // block res + // block a + // block b + // block c1 + // opa_mapping_lookup || br c1 + // call_indirect || br res + // br b + // end + // block c2 + // dot i || br c2 + // dot i+1 || br c2 + // br b + // end + // br a + // end + // dot i+2 || br res + // dot i+3 || br res + // end; a + // [add_to_result_set] + // end; res + // + // We have to do it like this because the dot IR stmts + // are compiled to `br 0`, the innermost block, if they + // fail. + // The "c2" block will construct the reference from `data` + // only, in case the mapping lookup doesn't yield a func + // to call_dynamic. + + ltarget := p.newLocal() + p.ltarget = op(ltarget) + prev := p.curr + + callDynBlock := &ir.Block{} // "c1" in the sketch + p.curr = callDynBlock + p.appendStmt(&ir.CallDynamicStmt{ + Args: []ir.Local{ + p.vars.GetOrEmpty(ast.InputRootDocument.Value.(ast.Var)), + p.vars.GetOrEmpty(ast.DefaultRootDocument.Value.(ast.Var)), + }, + Path: path, + Result: ltarget, + }) + p.appendStmt(&ir.BreakStmt{Index: 1}) + + dotBlock := &ir.Block{} // "c2" in the sketch above + p.curr = dotBlock + p.ltarget = p.vars.GetOpOrEmpty(ast.DefaultRootDocument.Value.(ast.Var)) + + return p.planRefRec(ref[:index+1], 1, func() error { + p.appendStmt(&ir.AssignVarStmt{ + Source: p.ltarget, + Target: ltarget, + }) + p.appendStmt(&ir.BreakStmt{Index: 1}) + p.ltarget = op(ltarget) + + outerBlock := &ir.Block{Stmts: []ir.Stmt{ + &ir.BlockStmt{Blocks: []*ir.Block{ + { // block "b" in the sketch above + Stmts: []ir.Stmt{ + &ir.BlockStmt{Blocks: []*ir.Block{callDynBlock, dotBlock}}, + &ir.BreakStmt{Index: 2}}, + }, + }}, + }} + p.curr = outerBlock + if err := p.planRefRec(ref, index+1, iter); err != nil { // rest of the ref + return err + } + p.curr = prev + p.appendStmt(&ir.BlockStmt{Blocks: []*ir.Block{outerBlock}}) + return nil + }) + } + } + + // If the reference operand is ground then either continue to the next + // operand or invoke the function for the rule referred to by this operand. + if ref[index].IsGround() { + + var vchild *ruletrie + var rules []*ast.Rule + + if virtual != nil { + + vchild = virtual.Get(ref[index].Value) + rules = vchild.Rules() // hit or miss + } + + if len(rules) > 0 { + p.ltarget = p.newOperand() + + funcName, err := p.planRules(rules) + if err != nil { + return err + } + call := ir.CallStmt{ + Func: funcName, + Args: make([]ir.Operand, 0, p.funcs.argVars()), + Result: p.ltarget.Value.(ir.Local), + } + for _, v := range p.funcs.vars() { + call.Args = append(call.Args, p.vars.GetOpOrEmpty(v)) + } + p.appendStmt(&call) + + return p.planRefRec(ref, index+1, iter) + } + + bchild := *base + bchild.path = append(bchild.path, ref[index]) + return p.planRefData(vchild, &bchild, ref, index+1, iter) + } + + exclude := ast.NewSet() + + // The planner does not support dynamic dispatch so generate blocks to + // evaluate each of the rulesets on the child nodes. + if virtual != nil { + + stmt := &ir.BlockStmt{} + + for _, child := range virtual.Children() { + + block := &ir.Block{} + prev := p.curr + p.curr = block + key := ast.NewTerm(child) + exclude.Add(key) + + // Assignments in each block due to local unification must be undone + // so create a new frame that will be popped after this key is + // processed. + p.vars.Push(map[ast.Var]ir.Local{}) + + if err := p.planTerm(key, func() error { + return p.planUnifyLocal(p.ltarget, ref[index], func() error { + // Create a copy of the reference with this operand plugged. + // This will result in evaluation of the rulesets on the + // child node. + cpy := ref.Copy() + cpy[index] = key + return p.planRefData(virtual, base, cpy, index, iter) + }) + }); err != nil { + return err + } + + p.vars.Pop() + p.curr = prev + stmt.Blocks = append(stmt.Blocks, block) + } + + p.appendStmt(stmt) + } + + // If the virtual tree was enumerated then we do not want to enumerate base + // trees that are rooted at the same key as any of the virtual sub trees. To + // prevent this we build a set of keys that are to be excluded and check + // below during the base scan. + var lexclude *ir.Operand + + if exclude.Len() > 0 { + if err := p.planSet(exclude, func() error { + v := p.ltarget + lexclude = &v + return nil + }); err != nil { + return err + } + + // Perform a scan of the base documents starting from the location referred + // to by the 'path' data pointer. Use the `lexclude` set to avoid revisiting + // sub trees. + p.ltarget = op(base.local) + return p.planRefRec(base.path, 0, func() error { + return p.planScan(ref[index], func(lkey ir.Local) error { + if lexclude != nil { + lignore := p.newLocal() + p.appendStmt(&ir.NotStmt{ + Block: p.blockWithStmt(&ir.DotStmt{ + Source: *lexclude, + Key: op(lkey), + Target: lignore, + })}) + } + + // Assume that virtual sub trees have been visited already so + // recurse without the virtual node. + return p.planRefData(nil, &baseptr{local: p.ltarget.Value.(ir.Local)}, ref, index+1, iter) + }) + }) + } + + // There is nothing to exclude, so we do the same thing done above, but + // use planRefRec to avoid the scan if ref[index] is ground or seen. + p.ltarget = op(base.local) + base.path = append(base.path, ref[index]) + return p.planRefRec(base.path, 0, func() error { + return p.planRefData(nil, &baseptr{local: p.ltarget.Value.(ir.Local)}, ref, index+1, iter) + }) +} + +// planRefDataExtent generates the full extent (combined) of the base and +// virtual nodes and then invokes the iterator with the planner target set to +// the full extent. +func (p *Planner) planRefDataExtent(virtual *ruletrie, base *baseptr, iter planiter) error { + + vtarget := p.newLocal() + + // Generate the virtual document out of rules contained under the virtual + // node (recursively). This document will _ONLY_ contain values generated by + // rules. No base document values will be included. + if virtual != nil { + + p.appendStmt(&ir.MakeObjectStmt{ + Target: vtarget, + }) + + anyKeyNonGround := false + for _, key := range virtual.Children() { + if !key.IsGround() { + anyKeyNonGround = true + break + } + } + if anyKeyNonGround { + var rules []*ast.Rule + for _, key := range virtual.Children() { + // TODO(sr): skip functions + rules = append(rules, virtual.Get(key).Rules()...) + } + + funcName, err := p.planRules(rules) + if err != nil { + return err + } + + // Add leaf to object if defined. + b := &ir.Block{} + p.appendStmtToBlock(&ir.CallStmt{ + Func: funcName, + Args: p.defaultOperands(), + Result: vtarget, + }, b) + p.appendStmt(&ir.BlockStmt{Blocks: []*ir.Block{b}}) + } else { + for _, key := range virtual.Children() { + child := virtual.Get(key) + + // Skip functions. + if child.Arity() > 0 { + continue + } + + rules := child.Rules() + err := p.planValue(key, nil, func() error { + lkey := p.ltarget + + // Build object hierarchy depth-first. + if len(rules) == 0 { + return p.planRefDataExtent(child, nil, func() error { + p.appendStmt(&ir.ObjectInsertStmt{ + Object: vtarget, + Key: lkey, + Value: p.ltarget, + }) + return nil + }) + } + + // Generate virtual document for leaf. + lvalue := p.newLocal() + + funcName, err := p.planRules(rules) + if err != nil { + return err + } + + // Add leaf to object if defined. + b := &ir.Block{} + p.appendStmtToBlock(&ir.CallStmt{ + Func: funcName, + Args: p.defaultOperands(), + Result: lvalue, + }, b) + p.appendStmtToBlock(&ir.ObjectInsertStmt{ + Object: vtarget, + Key: lkey, + Value: op(lvalue), + }, b) + p.appendStmt(&ir.BlockStmt{Blocks: []*ir.Block{b}}) + return nil + }) + if err != nil { + return err + } + } + } + + // At this point vtarget refers to the full extent of the virtual + // document at ref. If the base pointer is unset, no further processing + // is required. + if base == nil { + p.ltarget = op(vtarget) + return iter() + } + } + + // Obtain the base document value and merge (recursively) with the virtual + // document value above if needed. + prev := p.curr + p.curr = &ir.Block{} + p.ltarget = op(base.local) + target := p.newLocal() + + err := p.planRefRec(base.path, 0, func() error { + + if virtual == nil { + target = p.ltarget.Value.(ir.Local) + } else { + stmt := &ir.ObjectMergeStmt{ + A: p.ltarget.Value.(ir.Local), + B: vtarget, + Target: target, + } + p.appendStmt(stmt) + } + + p.appendStmt(&ir.BreakStmt{Index: 1}) + return nil + }) + + if err != nil { + return err + } + + inner := p.curr + + // Fallback to virtual document value if base document is undefined. + // Otherwise, this block is undefined. + p.curr = &ir.Block{} + p.appendStmt(&ir.BlockStmt{Blocks: []*ir.Block{inner}}) + + if virtual != nil { + p.appendStmt(&ir.AssignVarStmt{ + Source: op(vtarget), + Target: target, + }) + } else { + p.appendStmt(&ir.BreakStmt{Index: 1}) + } + + outer := p.curr + p.curr = prev + p.appendStmt(&ir.BlockStmt{Blocks: []*ir.Block{outer}}) + + // At this point, target refers to either the full extent of the base and + // virtual documents at ref or just the base document at ref. + p.ltarget = op(target) + + return iter() +} + +func (p *Planner) planDot(key *ast.Term, iter planiter) error { + + source := p.ltarget + + return p.planTerm(key, func() error { + + target := p.newLocal() + + p.appendStmt(&ir.DotStmt{ + Source: source, + Key: p.ltarget, + Target: target, + }) + + p.ltarget = op(target) + + return iter() + }) +} + +type scaniter func(ir.Local) error + +func (p *Planner) planScan(key *ast.Term, iter scaniter) error { + + scan := &ir.ScanStmt{ + Source: p.ltarget.Value.(ir.Local), + Key: p.newLocal(), + Value: p.newLocal(), + Block: &ir.Block{}, + } + + prev := p.curr + p.curr = scan.Block + + if err := p.planUnifyLocal(op(scan.Key), key, func() error { + p.ltarget = op(scan.Value) + return iter(scan.Key) + }); err != nil { + return err + } + + p.curr = prev + p.appendStmt(scan) + + return nil + +} + +func (p *Planner) planScanValues(val *ast.Term, iter scaniter) error { + + scan := &ir.ScanStmt{ + Source: p.ltarget.Value.(ir.Local), + Key: p.newLocal(), + Value: p.newLocal(), + Block: &ir.Block{}, + } + + prev := p.curr + p.curr = scan.Block + + if err := p.planUnifyLocal(op(scan.Value), val, func() error { + p.ltarget = op(scan.Value) + return iter(scan.Value) + }); err != nil { + return err + } + + p.curr = prev + p.appendStmt(scan) + + return nil +} + +type termsliceiter func([]ir.Operand) error + +func (p *Planner) planTermSlice(terms []*ast.Term, iter termsliceiter) error { + return p.planTermSliceRec(terms, make([]ir.Operand, len(terms)), 0, iter) +} + +func (p *Planner) planTermSliceRec(terms []*ast.Term, locals []ir.Operand, index int, iter termsliceiter) error { + if index >= len(terms) { + return iter(locals) + } + + return p.planTerm(terms[index], func() error { + locals[index] = p.ltarget + return p.planTermSliceRec(terms, locals, index+1, iter) + }) +} + +func (p *Planner) planExterns() error { + + p.policy.Static.BuiltinFuncs = make([]*ir.BuiltinFunc, 0, len(p.externs)) + + for name, decl := range p.externs { + p.policy.Static.BuiltinFuncs = append(p.policy.Static.BuiltinFuncs, &ir.BuiltinFunc{Name: name, Decl: decl.Decl}) + } + + sort.Slice(p.policy.Static.BuiltinFuncs, func(i, j int) bool { + return p.policy.Static.BuiltinFuncs[i].Name < p.policy.Static.BuiltinFuncs[j].Name + }) + + return nil +} + +func (p *Planner) getStringConst(s string) int { + index, ok := p.strings[s] + if !ok { + index = len(p.policy.Static.Strings) + p.policy.Static.Strings = append(p.policy.Static.Strings, &ir.StringConst{ + Value: s, + }) + p.strings[s] = index + } + return index +} + +func (p *Planner) getFileConst(s string) int { + index, ok := p.files[s] + if !ok { + index = len(p.policy.Static.Files) + p.policy.Static.Files = append(p.policy.Static.Files, &ir.StringConst{ + Value: s, + }) + p.files[s] = index + } + return index +} + +func (p *Planner) appendStmt(s ir.Stmt) { + p.appendStmtToBlock(s, p.curr) +} + +func (p *Planner) appendStmtToBlock(s ir.Stmt, b *ir.Block) { + if p.loc != nil { + str := p.loc.File + if str == "" { + str = `` + } + s.SetLocation(p.getFileConst(str), p.loc.Row, p.loc.Col, str, string(p.loc.Text)) + } + b.Stmts = append(b.Stmts, s) +} + +func (p *Planner) blockWithStmt(s ir.Stmt) *ir.Block { + b := &ir.Block{} + p.appendStmtToBlock(s, b) + return b +} + +func (p *Planner) appendBlock(b *ir.Block) { + p.plan.Blocks = append(p.plan.Blocks, b) +} + +func (p *Planner) appendFunc(f *ir.Func) { + p.policy.Funcs.Funcs = append(p.policy.Funcs.Funcs, f) +} + +func (p *Planner) newLocal() ir.Local { + x := p.lnext + p.lnext++ + return x +} + +func (p *Planner) newOperand() ir.Operand { + return op(p.newLocal()) +} + +func rewrittenVar(vars map[ast.Var]ast.Var, k ast.Var) ast.Var { + rw, ok := vars[k] + if !ok { + return k + } + return rw +} + +func dont() ([][]*ast.Rule, []ir.Operand, int, bool) { + return nil, nil, 0, false +} + +// optimizeLookup returns a set of rulesets and required statements planning +// the locals (strings) needed with the used local variables, and the index +// into ref's parth that is still to be planned; if the passed ref's vars +// allow for optimization using CallDynamicStmt. +// +// It's possible if all of these conditions hold: +// - all vars in ref have been seen +// - all ground terms (strings) match some child key on their respective +// layer of the ruletrie +// - there are no child trees left (only rulesets) if we're done checking +// ref +// +// The last condition is necessary because we don't deal with _which key a +// var actually matched_ -- so we don't know which subtree to evaluate +// with the results. +func (p *Planner) optimizeLookup(t *ruletrie, ref ast.Ref) ([][]*ast.Rule, []ir.Operand, int, bool) { + if t == nil { + p.debugf("no optimization of %s: trie is nil", ref) + return dont() + } + + nodes := []*ruletrie{t} + opt := false + var index int + + // ref[0] is data, ignore +outer: + for i := 1; i < len(ref); i++ { + index = i + r := ref[i] + var nextNodes []*ruletrie + + switch r := r.Value.(type) { + case ast.Var: + // check if it's been "seen" before + _, ok := p.vars.Get(r) + if !ok { + p.debugf("no optimization of %s: ref[%d] is unseen var: %v", ref, i, r) + return dont() + } + opt = true + // take all children, they might match + for _, node := range nodes { + if nr := node.Rules(); len(nr) > 0 { + p.debugf("no optimization of %s: node with rules (%v)", ref, refsOfRules(nr)) + return dont() + } + for _, child := range node.Children() { + if node := node.Get(child); node != nil { + nextNodes = append(nextNodes, node) + } + } + } + case ast.String: + // take all children that either match or have a var key // TODO(sr): Where's the code for the second part, having a var key? + for _, node := range nodes { + if nr := node.Rules(); len(nr) > 0 { + p.debugf("no optimization of %s: node with rules (%v)", ref, refsOfRules(nr)) + return dont() + } + if node := node.Get(r); node != nil { + nextNodes = append(nextNodes, node) + } + } + default: + p.debugf("no optimization of %s: ref[%d] is type %T", ref, i, r) // TODO(sr): think more about this + return dont() + } + + nodes = nextNodes + + // if all nodes have rules() > 0, abort ref check and optimize + // NOTE(sr): for a.b[c] = ... and a.b.d = ..., we stop at a.b, as its rules() + // will collect the children rules + // We keep the "all nodes have 0 children" check since it's cheaper and might + // let us break, too. + all := 0 + for _, node := range nodes { + if i < len(ref)-1 { + // Look ahead one term to only count those children relevant to your planned ref. + switch ref[i+1].Value.(type) { + case ast.Var: + all += node.ChildrenCount() + default: + if relChildren := node.Get(ref[i+1].Value); relChildren != nil { + all++ + } + } + } + } + if all == 0 { + p.debugf("ref %s: all nodes have 0 relevant children, break", ref[0:index+1]) + break + } + + // NOTE(sr): we only need this check for the penultimate part: + // When planning the ref data.pkg.a[input.x][input.y], + // We want to capture this situation: + // a.b[c] := "x" if c := "c" + // a.b.d := "y" + // + // Not this: + // a.b[c] := "x" if c := "c" + // a.d := "y" + // since the length doesn't add up. Even if input.x was "d", the second + // rule (a.d) wouldn't contribute anything to the result, since we cannot + // "dot it". + if index == len(ref)-2 { + for _, node := range nodes { + anyNonGround := false + for _, r := range node.Rules() { + anyNonGround = anyNonGround || !r.Ref().IsGround() + } + if anyNonGround { + p.debugf("ref %s: at least one node has 1+ non-ground ref rules, break", ref[0:index+1]) + break outer + } + } + } + } + + var res [][]*ast.Rule + + // if there hasn't been any var, we're not making things better by + // introducing CallDynamicStmt + if !opt { + p.debugf("no optimization of %s: no vars seen before trie descend encountered no children", ref) + return dont() + } + + for _, node := range nodes { + // we're done with ref, check if there's only ruleset leaves; collect rules + if index == len(ref)-1 { + if len(node.Rules()) == 0 && node.ChildrenCount() > 0 { + p.debugf("no optimization of %s: unbalanced ruletrie", ref) + return dont() + } + } + if rules := node.Rules(); len(rules) > 0 { + res = append(res, rules) + } + } + if len(res) == 0 { + p.debugf("ref %s: nothing to plan, no rule leaves", ref[0:index+1]) + return nil, nil, index, true + } + + var path []ir.Operand + + // plan generation + path = append(path, op(ir.StringIndex(p.getStringConst(fmt.Sprintf("g%d", p.funcs.gen()))))) + + for i := 1; i <= index; i++ { + switch r := ref[i].Value.(type) { + case ast.Var: + lv, ok := p.vars.GetOp(r) + if !ok { + p.debugf("no optimization of %s: ref[%d] not a seen var: %v", ref, i, ref[i]) + return dont() + } + path = append(path, lv) + case ast.String: + path = append(path, op(ir.StringIndex(p.getStringConst(string(r))))) + } + } + + return res, path, index, true +} + +func (p *Planner) unseenVars(t *ast.Term) bool { + unseen := false // any var unseen? + ast.WalkVars(t, func(v ast.Var) bool { + if !unseen { + _, exists := p.vars.Get(v) + if !exists { + unseen = true + } + } + return unseen + }) + return unseen +} + +func (p *Planner) defaultOperands() []ir.Operand { + pcount := p.funcs.argVars() + operands := make([]ir.Operand, pcount) + for i, v := range p.funcs.vars() { + operands[i] = p.vars.GetOpOrEmpty(v) + } + return operands +} + +func (p *Planner) isFunctionOrBuiltin(r ast.Ref) bool { + if node := p.rules.Lookup(r); node != nil { + return node.Arity() > 0 + } + _, ok := p.decls[r.String()] + return ok +} + +func op(v ir.Val) ir.Operand { + return ir.Operand{Value: v} +} + +func refsOfRules(rs []*ast.Rule) []string { + refs := make([]string, len(rs)) + for i := range rs { + refs[i] = rs[i].Head.Ref().String() + } + return refs +} diff --git a/third_party/opa/internal/planner/planner_test.go b/third_party/opa/internal/planner/planner_test.go new file mode 100644 index 000000000000..1bd92705bab8 --- /dev/null +++ b/third_party/opa/internal/planner/planner_test.go @@ -0,0 +1,1310 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package planner + +import ( + "errors" + "fmt" + "os" + "reflect" + "slices" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ir" +) + +func TestPlannerHelloWorld(t *testing.T) { + // NOTE(tsandall): These tests are not meant to give comprehensive coverage + // of the planner. Currently we have a suite of end-to-end tests in the + // test/wasm/ directory that are specified in YAML, compiled into Wasm, and + // executed inside of a node program. For the time being, the planner is + // simple enough that exhaustive unit testing is not as valuable as + // end-to-end testing. These tests provide a quick consistency check that the + // planner is not failing on simple inputs. + tests := []struct { + note string + queries []string + modules []string + }{ + { + note: "empty", + queries: []string{}, + }, + { + note: "hello world", + queries: []string{"input.a = 1"}, + }, + { + note: "conjunction", + queries: []string{"1 = 1; 2 = 2"}, + }, + { + note: "disjunction", + queries: []string{"input.a = 1", "input.b = 2"}, + }, + { + note: "iteration", + queries: []string{"input.a[i] = 1; input.b = 2"}, + }, + { + note: "iteration: compare key", + queries: []string{"input.a[i] = 1; input.b = i"}, + }, + { + note: "iteration: nested", + queries: []string{"input.a[i] = 1; input.b[j] = 2"}, + }, + { + note: "iteration: chained", + queries: []string{"input.a[i][j] = 1"}, + }, + { + note: "negation", + queries: []string{"not input.x.y = 1"}, + }, + { + note: "not and known vars", // https://github.com/open-policy-agent/opa/issues/3279 + queries: []string{`x = "foo"; not data.tenants[x]`}, + }, + { + note: "array ref pattern match", + queries: []string{"input.x = [1, [y]]"}, + }, + { + note: "arrays pattern match", + queries: []string{"[x, 3, [2]] = [1, 3, [y]]"}, + }, + { + note: "sets", + queries: []string{"x = {1,2,3}; x[y]"}, + }, + { + note: "vars", + queries: []string{"x = 1"}, + }, + { + note: "complete rules", + queries: []string{"data.test.p = x"}, + modules: []string{` + package test + p = x if { x = 1 } + p = y if { y = 2 } + `}, + }, + { + note: "complete rule reference", + queries: []string{"data.test.p = 10"}, + modules: []string{` + package test + p = x if { x = 10 } + `}, + }, + { + note: "functions", + queries: []string{"data.test.f([1,x])"}, + modules: []string{` + package test + f([a, b]) if { + a = b + } + `}, + }, + { + note: "else", + queries: []string{"data.test.p = 1"}, + modules: []string{` + package test + p = 0 if { + false + } else = 1 if { + true + } + `}, + }, + { + note: "partial set", + queries: []string{"data.test.p = {1,2}"}, + modules: []string{` + package test + p contains 1 + p contains 2 + `}, + }, + { + note: "partial object", + queries: []string{`data.test.p = {"a": 1, "b": 2}`}, + modules: []string{` + package test + p["a"] = 1 + p["b"] = 2 + `}, + }, + { // NOTE(sr): these are handled differently with ref-heads + note: "partial object with var", + queries: []string{`data.test.p = x`}, + modules: []string{` + package test + p["a"] = 1 + p[v] = 2 if { v := "b" } + `}, + }, + { + note: "partial object (ref-head) with var", + queries: []string{`data.test.p.q = x`}, + modules: []string{` + package test + p.q.r["a"] = 1 + p.q[v] = 2 if { v := "b" } + `}, + }, + { + note: "partial object (ref-head) with var (shallow query)", + queries: []string{`data.test.p = x`}, + modules: []string{` + package test + p.q["a"] = 1 + p.q[v] = 2 if { v := "b" } + p.r["c"] = 3 + p.r[v] = 4 if { v := "d" } + `}, + }, + { + note: "partial object (ref-head) with var (multiple)", + queries: []string{`data.test.p.q = x`}, + modules: []string{` + package test + p.q["a"] = 1 + p.q[v] = x if { l1 := ["b", "c", "d"]; l2 := ["foo", "bar"]; l3 := [2, 3]; v := l1[_]; x := l2[_]; z := l3[_] } + `}, + }, + { + note: "partial object (general ref-head) with var", + queries: []string{`data.test.p.q = x`}, + modules: []string{` + package test + p.q["a"] = 1 + p.q.b.s.baz = 2 + p.q.b.s.foo.c = 3 + p.q[r].s[t].u = v if { x := ["foo", "bar"]; r := "b"; t := x[v]} + `}, + }, + { + note: "every", + queries: []string{`data.test.p`}, + modules: []string{` + package test + p if { xs = [1]; every k, v in xs { k < v } } + `}, + }, + { + note: "virtual extent", + queries: []string{`data`}, + modules: []string{` + package test + + p = 1 + q = 2 if { false } + `}, + }, + { + note: "comprehension", + queries: []string{`{x | input[_] = x}`}, + }, + { + note: "object comprehension in policy", + queries: []string{`data.test.a = x`}, + modules: []string{` + package test + + a = { "a": "b" | 1 > 0 } + `}, + }, + { + note: "closure", + queries: []string{`a = [1]; {x | a[_] = x}`}, + }, + { + note: "iteration: packages and rules", + queries: []string{"data.test[x][y] = 3"}, + modules: []string{ + ` + package test.a + + p = 1 + q = 2 if { false } + r = 3 + `, + ` + package test.z + + s = 3 + t = 4 + `, + }, + }, + { + note: "variables in query", + queries: []string{"x = 1", "y = 2", "x = 1; y = 2"}, + }, + { + note: "with keyword", + queries: []string{ + `input[i] = 1 with input as [1]; i > 1`, + }, + }, + { + note: "with keyword data", + queries: []string{`data = x with data.foo as 1 with data.bar.r as 3`}, + modules: []string{ + `package foo + + p = 1`, + `package bar + + q = 2`, + }, + }, + { + note: "with keyword - virtual doc iteration", + queries: []string{`x = data[i][j] with data.bar as 1; y = "a"`}, + modules: []string{ + `package foo + + p = 0 + q = 1 + r = 2`, + }, + }, + { + note: "relation non-empty", + queries: []string{`walk(input)`}, + }, + { + note: "relation unify", + queries: []string{`walk(input, [["foo", y], x])`}, + }, + { + note: "else conflict-1", + queries: []string{`data.p.q`}, + modules: []string{ + `package p + + q if { + false + } + else = true if { + true + } + q = false + `, + }, + }, + { + note: "else conflict-2", + queries: []string{`data.p.q`}, + modules: []string{ + `package p + + q if { + false + } + else = false if { + true + } + q if { + false + } + else = true if { + true + }`, + }, + }, + { + note: "multiple function outputs (single)", + queries: []string{`data.p.r`}, + modules: []string{ + `package p + + p(a) = y if { + y = a[_] + } + + r = y if { + data.p.p([1, 2, 3], y) + } + `, + }, + }, + { + note: "multiple function outputs (multiple)", + queries: []string{`data.p.r`}, + modules: []string{ + `package p + + p(1, a) = y if { + y = a + } + p(x, y) = z if { + z = x + } + + r = y if { + data.p.p(1, 0, y) + } + `, + }, + }, + { + note: "cross product with non-ground refs to packages (simplified)", + queries: []string{`x := "aaa"; y := "bbb"; z := "q"; w := data.foo[x].bar[y].baz[z]`}, + modules: []string{`package foo.aaa.bar.bbb.baz +p = 1 +q = 2`, + `package foo.ccc.bar.bbb.baz +p = 10 +q = 20`, + }, + }, + { + note: `non-ground refs to packages (including "with")`, + // NOTE(sr): data.foo.bbb does not change the outcomes, but triggers + // a gen++ in p.funcs, leading to g1, g2 function being planned and + // referenced + queries: []string{`x := "aaa"; y := "bbb"; z := "q"; w := data.foo[x].bar[y].baz[z] with data.foo.bbb as true`}, + modules: []string{`package foo.aaa.bar.bbb.baz +p = 1 +q = 2`, + }, + }, + { + note: "cross product with non-ground refs to packages, 'no rules leaves' case", + queries: []string{`x := "aaa"; y := data.foo[x].bar.baz`}, + modules: []string{`package foo.aaa.bar`, `package foo.bbb.bar`}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + queries := make([]ast.Body, len(tc.queries)) + for i := range queries { + queries[i] = ast.MustParseBody(tc.queries[i]) + } + modules := make([]*ast.Module, len(tc.modules)) + for i := range modules { + file := fmt.Sprintf("module-%d.rego", i) + opts := ast.ParserOptions{AllFutureKeywords: true} + m, err := ast.ParseModuleWithOpts(file, tc.modules[i], opts) + if err != nil { + t.Fatal(err) + } + modules[i] = m + } + planner := New().WithQueries([]QuerySet{ + { + Name: "test", + Queries: queries, + }, + }).WithModules(modules).WithBuiltinDecls(ast.BuiltinMap) + + if testing.Verbose() { + planner = planner.WithDebug(os.Stderr) + } + policy, err := planner.Plan() + if err != nil { + t.Fatal(err) + } + if testing.Verbose() { + err = ir.Pretty(os.Stderr, policy) + if err != nil { + t.Fatal(err) + } + } + }) + } +} + +type cmpWalker struct { + needle any + loc string + found bool // stop comparing after first found needle +} + +func (*cmpWalker) Before(any) {} +func (*cmpWalker) After(any) {} + +// Visit takes, for example, +// +// *ir.MakeNullStmt{Location: ir.Location{Index:0, Col:1, Row:1}}, +// +// and for the first MakeNullStmt it finds, extracts its location, +// and compares it to the one passed was needle. Other fields of the +// struct, such as Target for ir.MakeNullStmt, are ignored. +// +// Caveat: If NO value of the desired type is found, there's no error +// returned. This trap can be avoided by starting with a failing test, +// and proceeding with caution. ;) +func (f *cmpWalker) Visit(x any) (ir.Visitor, error) { + if !f.found && reflect.TypeOf(f.needle) == reflect.TypeOf(x) { + f.found = true + expLoc := f.loc + actLoc := getLocation(x) + if expLoc != actLoc { + return f, fmt.Errorf("unexpected location for %T:\nwant: %s\ngot: %s", x, expLoc, actLoc) + } + } + return f, nil +} + +func getLocation(x any) string { + v := reflect.ValueOf(x).Elem().FieldByName("Location") + li := v.Interface() + file := v.FieldByName("file").String() + text := v.FieldByName("text").String() + if loc, ok := li.(ir.Location); ok { + return fmt.Sprintf("%s:%d:%d: %s", file, loc.Row, loc.Col, text) + } + return "unknown" +} + +func findInPolicy(needle any, loc string, p any) error { + return ir.Walk(&cmpWalker{needle: needle, loc: loc}, p) +} + +// Assert some selected statements' location mappings. Note that for debugging, +// it's worthwhile to not use tabs in the multi-line strings, as they may be +// counted differently in the editor vs. in code. +func TestPlannerLocations(t *testing.T) { + + funcs := func(p *ir.Policy) any { + return p.Funcs + } + + tests := []struct { + note string + queries []string + modules []string + exps map[ir.Stmt]string // stmt -> expected location "file:row:col: text" + where func(*ir.Policy) any // where to start walking search for `exps` + }{ + { + note: "complete rule reference", + queries: []string{"data.test.p = 10"}, + modules: []string{` +package test +p = x if { + 1 > 0 + x = 10 + true +} +`}, + exps: map[ir.Stmt]string{ + &ir.CallStmt{}: ":1:1: data.test.p = 10", + &ir.AssignVarStmt{}: "module-0.rego:5:3: x = 10", + &ir.AssignVarOnceStmt{}: "module-0.rego:3:1: p = x", + &ir.ReturnLocalStmt{}: "module-0.rego:3:1: p = x", + }, + }, + { + note: "partial set", + queries: []string{"data.test.p = {1,2}"}, + modules: []string{` +package test +p contains 1 +p contains 2 + `}, + exps: map[ir.Stmt]string{ + &ir.MakeSetStmt{}: "module-0.rego:3:1: p contains 1", + &ir.ReturnLocalStmt{}: "module-0.rego:3:1: p contains 1", + }, + where: funcs, + }, + { + note: "partial set with rule body", + queries: []string{"data.test.p = {1,2}"}, + modules: []string{` +package test +p contains 1 if { + 1 > 2 +} + `}, + exps: map[ir.Stmt]string{ + &ir.CallStmt{}: "module-0.rego:4:3: 1 > 2", + &ir.SetAddStmt{}: "module-0.rego:3:1: p contains 1", + }, + where: funcs, + }, + { + note: "partial object", + queries: []string{`data.test.p = {"a": 1, "b": 2}`}, + modules: []string{` +package test +p["a"] = 1 if { + false +} + `}, + exps: map[ir.Stmt]string{ + &ir.MakeObjectStmt{}: `module-0.rego:3:1: p["a"] = 1`, + &ir.ObjectInsertOnceStmt{}: `module-0.rego:3:1: p["a"] = 1`, + }, + where: funcs, + }, + { + note: "default rule", + queries: []string{`data.test.p = x`}, + modules: []string{` +package test +default p = {"foo": "bar"} +p = x if { + x := {"baz": "quz"} +} + `}, + exps: map[ir.Stmt]string{ + &ir.IsUndefinedStmt{}: `module-0.rego:3:9: p = {"foo": "bar"}`, + &ir.MakeObjectStmt{}: `module-0.rego:3:9: p = {"foo": "bar"}`, + &ir.AssignVarOnceStmt{}: `module-0.rego:3:9: p = {"foo": "bar"}`, + }, + where: func(p *ir.Policy) any { + return p.Funcs.Funcs[0].Blocks[2] // default rule block + }, + }, + { + note: "object comprehension in policy", + queries: []string{`data.test.a = x`}, + modules: []string{ + `package test +a = { "a": "b" | + 1 > 0 +}`}, + exps: map[ir.Stmt]string{ + &ir.CallStmt{}: "module-0.rego:3:3: 1 > 0", + &ir.ObjectInsertOnceStmt{}: "module-0.rego:2:5: { \"a\": \"b\" |\n 1 > 0\n}", + }, + where: funcs, + }, + { + note: "array comprehension in policy", + queries: []string{`data.test.a = x`}, + modules: []string{ + `package test +a = [ "a" | + 1 > 0 +]`}, + exps: map[ir.Stmt]string{ + &ir.CallStmt{}: "module-0.rego:3:3: 1 > 0", + &ir.ArrayAppendStmt{}: "module-0.rego:2:5: [ \"a\" |\n 1 > 0\n]", + }, + where: funcs, + }, + { + note: "set comprehension in policy", + queries: []string{`data.test.a = x`}, + modules: []string{ + `package test +a = { "a" | + 1 > 0 +}`}, + exps: map[ir.Stmt]string{ + &ir.CallStmt{}: "module-0.rego:3:3: 1 > 0", + &ir.SetAddStmt{}: "module-0.rego:2:5: { \"a\" |\n 1 > 0\n}", + }, + where: funcs, + }, + { + note: "set in policy", + queries: []string{`data.test.a = x`}, + modules: []string{`package test +a = { "a", 10 }`}, + exps: map[ir.Stmt]string{ + &ir.SetAddStmt{}: "module-0.rego:2:1: a = { \"a\", 10 }", + }, + }, + { + note: "virtual extent", + queries: []string{`data`}, + modules: []string{`package test +p = 1 +q = 2 if { + false +}`}, + exps: map[ir.Stmt]string{ + &ir.CallStmt{}: ":1:1: data", + &ir.ObjectInsertStmt{}: ":1:1: data", + }, + where: func(p *ir.Policy) any { + return p.Plans.Plans[0].Blocks[0].Stmts[4] + }, + }, + { + note: "non-ground ref in query", + queries: []string{`data[y].a = x`}, + modules: []string{`package test +a = true`}, + exps: map[ir.Stmt]string{ + &ir.CallStmt{}: ":1:1: data[y].a = x", + &ir.MakeObjectStmt{}: ":1:1: data[y].a = x", + &ir.ObjectInsertStmt{}: ":1:1: data[y].a = x", + &ir.ResultSetAddStmt{}: ":1:1: data[y].a = x", + &ir.DotStmt{}: ":1:1: data[y].a = x", + }, + where: func(p *ir.Policy) any { + return p.Plans.Plans[0] + }, + }, + { + note: "non-ground ref in policy", + queries: []string{`data.test.a = x`}, + modules: []string{`package test +a if { + data.test1[_].y = "z" +}`}, + exps: map[ir.Stmt]string{ + &ir.CallStmt{}: ":1:1: data.test.a = x", + &ir.MakeObjectStmt{}: ":1:1: data.test.a = x", + &ir.ObjectInsertStmt{}: ":1:1: data.test.a = x", + &ir.ResultSetAddStmt{}: ":1:1: data.test.a = x", + &ir.ScanStmt{}: `module-0.rego:3:3: data.test1[_].y = "z"`, + }, + }, + { + note: "CallDynamicStmt optimization", + queries: []string{`x := "a"; data.test[x] = y`}, + modules: []string{`package test +a if { + true +}`}, + exps: map[ir.Stmt]string{ + &ir.CallDynamicStmt{}: ":1:11: data.test[x] = y", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + queries := make([]ast.Body, len(tc.queries)) + for i := range queries { + queries[i] = ast.MustParseBody(tc.queries[i]) + } + modules := make([]*ast.Module, len(tc.modules)) + for i := range modules { + file := fmt.Sprintf("module-%d.rego", i) + m, err := ast.ParseModuleWithOpts(file, tc.modules[i], ast.ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatal(err) + } + modules[i] = m + } + planner := New().WithQueries([]QuerySet{ + { + Name: "test", + Queries: queries, + }, + }).WithModules(modules).WithBuiltinDecls(ast.BuiltinMap) + policy, err := planner.Plan() + if err != nil { + t.Fatal(err) + } + if testing.Verbose() { + err = ir.Pretty(os.Stderr, policy) + if err != nil { + t.Fatal(err) + } + } + start := any(policy) + if tc.where != nil { + start = tc.where(policy) + } + for exp, loc := range tc.exps { + if err := findInPolicy(exp, loc, start); err != nil { + t.Error(err) + } + } + }) + } +} + +func TestMultipleNamedQueries(t *testing.T) { + + q1 := []ast.Body{ + ast.MustParseBody(`a=1`), + } + + q2 := []ast.Body{ + ast.MustParseBody(`a=2`), + } + + planner := New().WithQueries([]QuerySet{ + { + Name: "q1", + Queries: q1, + }, + { + Name: "q2", + Queries: q2, + }, + }) + + policy, err := planner.Plan() + if err != nil { + t.Fatal(err) + } + + if testing.Verbose() { + err = ir.Pretty(os.Stderr, policy) + if err != nil { + t.Fatal(err) + } + } + + // Consistency check to make sure two expected plans are emitted. + if len(policy.Plans.Plans) != 2 { + t.Fatal("expected two plans") + } else if policy.Plans.Plans[0].Name != "q1" || policy.Plans.Plans[1].Name != "q2" { + t.Fatal("expected to find plans for 'q1' and 'q2'") + } +} + +func ref(r string) ast.Ref { + return ast.MustParseRef("data." + r)[1:] +} + +func TestOptimizeLookup(t *testing.T) { + r0, r1, r2 := ast.MustParseRule("p = 0 { true }"), ast.MustParseRule("p = 1 { true }"), ast.MustParseRule("p = 2 { true }") + planner := func() *Planner { + if testing.Verbose() { + return New().WithDebug(os.Stderr) + } + return New() + } + + t.Run("seen variable (last), one ruleset", func(t *testing.T) { + r := newRuletrie() + val := r.LookupOrInsert(ref("foo.bar")) + val.rules = append(val.rules, r0, r1, r2) + + p := planner() + l := p.newLocal() + p.vars.Put(ast.Var("x"), l) + + rulesets, path, index, opt := p.optimizeLookup(r, ast.MustParseRef("data.foo[x]")) + if exp, act := true, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + if exp, act := 2, index; exp != act { + t.Errorf("expected 'index' %d, got %d\n", exp, act) + } + if exp, act := 1, len(rulesets); exp != act { + t.Fatalf("expected %d rulesets, got %d\n", exp, act) + } + if exp, act := 3, len(rulesets[0]); exp != act { + t.Errorf("expected %d rules, got %d\n", exp, act) + } + // 3 = g0 + foo + x + if exp, act := 3, len(path); exp != act { + t.Fatalf("expected path len %d, got %d\n", exp, act) + } + last, ok := path[len(path)-1].Value.(ir.Local) + if exp, act := true, ok; exp != act { + t.Fatalf("expected last path pieces to be local, got %T\n", last) + } + if exp, act := l, last; exp != act { + t.Errorf("expected last local to be %v, got %v\n", exp, act) + } + }) + + t.Run("ref shorter than ruletrie depth", func(t *testing.T) { + r := newRuletrie() + val := r.LookupOrInsert(ref("foo.bar.baz")) + val.rules = append(val.rules, r0, r1, r2) + + p := planner().WithDebug(os.Stderr) + l := p.newLocal() + p.vars.Put(ast.Var("x"), l) + + _, _, _, opt := p.optimizeLookup(r, ast.MustParseRef("data.foo[x]")) + if exp, act := false, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + }) + + t.Run("seen variable (last), multiple rulesets", func(t *testing.T) { + r := newRuletrie() + val := r.LookupOrInsert(ref("foo.bar")) + val.rules = append(val.rules, r0, r1) + val = r.LookupOrInsert(ref("foo.baz")) + val.rules = append(val.rules, r2) + + p := planner() + p.vars.Put(ast.Var("x"), p.newLocal()) + + rulesets, _, _, opt := p.optimizeLookup(r, ast.MustParseRef("data.foo[x]")) + if exp, act := true, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + if exp, act := 2, len(rulesets); exp != act { + t.Fatalf("expected %d rulesets, got %d\n", exp, act) + } + if exp, act := 2, len(rulesets[0]); exp != act { + t.Errorf("expected %d rules in ruleset 0, got %d\n", exp, act) + } + if exp, act := 1, len(rulesets[1]); exp != act { + t.Errorf("expected %d rules in ruleset 1, got %d\n", exp, act) + } + }) + + t.Run("unseen variable (last)", func(t *testing.T) { + r := newRuletrie() + val := r.LookupOrInsert(ref("foo.bar")) + val.rules = append(val.rules, r0, r1, r2) + + p := planner() + + _, _, _, opt := p.optimizeLookup(r, ast.MustParseRef("data.foo[x]")) + if exp, act := false, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + }) + + t.Run("all ground refs", func(t *testing.T) { + r := newRuletrie() + val := r.LookupOrInsert(ref("foo.bar.baz")) + val.rules = append(val.rules, r0, r1, r2) + + p := planner() + + _, _, _, opt := p.optimizeLookup(r, ast.MustParseRef("data.foo.bar.baz")) + if exp, act := false, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + }) + + t.Run("multiple seen vars, one rule set", func(t *testing.T) { + r := newRuletrie() + val := r.LookupOrInsert(ref("foo.aaa.bar.bbb.q")) + val.rules = append(val.rules, r0, r1, r2) + + p := planner() + lx, ly := p.newLocal(), p.newLocal() + p.vars.Put(ast.Var("x"), lx) + p.vars.Put(ast.Var("y"), ly) + + rulesets, path, index, opt := p.optimizeLookup(r, ast.MustParseRef("data.foo[x].bar[y].q")) + if exp, act := true, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + if exp, act := 5, index; exp != act { + t.Errorf("expected 'index' %d, got %d\n", exp, act) + } + if exp, act := 1, len(rulesets); exp != act { + t.Fatalf("expected %d rulesets, got %d\n", exp, act) + } + if exp, act := 3, len(rulesets[0]); exp != act { + t.Errorf("expected %d rules in ruleset 0, got %d\n", exp, act) + } + // 6 = g0 + foo + x + bar + y + q + if exp, act := 6, len(path); exp != act { + t.Fatalf("expected path len %d, got %d\n", exp, act) + } + }) + + t.Run("one seen var, one unseen, one rule set", func(t *testing.T) { + r := newRuletrie() + val := r.LookupOrInsert(ref("foo.aaa.bar.bbb.q")) + val.rules = append(val.rules, r0, r1, r2) + + p := planner() + p.vars.Put(ast.Var("x"), p.newLocal()) + + _, _, _, opt := p.optimizeLookup(r, ast.MustParseRef("data.foo[x].bar[y].q")) + if exp, act := false, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + }) + + t.Run("one seen var, one rule set and children left", func(t *testing.T) { + r := newRuletrie() + val := r.LookupOrInsert(ref("foo.aaa.bar.bbb.q")) + val.rules = append(val.rules, r0) + val = r.LookupOrInsert(ref("foo.ccc.bar")) + val.rules = append(val.rules, r1, r2) + + p := planner() + p.vars.Put(ast.Var("x"), p.newLocal()) + + _, _, _, opt := p.optimizeLookup(r, ast.MustParseRef("data.foo[x].bar")) + if exp, act := false, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + }) + + t.Run("ref goes into the rules' result", func(t *testing.T) { + r := newRuletrie() + val := r.LookupOrInsert(ref("foo.aaa.bar.q")) + val.rules = append(val.rules, r0, r1, r2) + + p := planner() + p.vars.Put(ast.Var("x"), p.newLocal()) + + rulesets, path, index, opt := p.optimizeLookup(r, ast.MustParseRef("data.foo[x].bar.q.p.r")) + if exp, act := true, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + if exp, act := 4, index; exp != act { + t.Errorf("expected 'index' %d, got %d\n", exp, act) + } + if exp, act := 1, len(rulesets); exp != act { + t.Fatalf("expected %d rulesets, got %d\n", exp, act) + } + if exp, act := 3, len(rulesets[0]); exp != act { + t.Errorf("expected %d rules in ruleset 0, got %d\n", exp, act) + } + // 5 = g0 + foo + x + bar + q + if exp, act := 5, len(path); exp != act { + t.Fatalf("expected path len %d, got %d\n", exp, act) + } + }) + + t.Run("one leaf without rules", func(t *testing.T) { + r := newRuletrie() + val := r.LookupOrInsert(ref("foo.aaa.bar.q")) + val.rules = append(val.rules, r0, r1) + r.LookupOrInsert(ref("foo.bbb.bar.q")) + + p := planner() + p.vars.Put(ast.Var("x"), p.newLocal()) + + rulesets, _, index, opt := p.optimizeLookup(r, ast.MustParseRef("data.foo[x].bar.q")) + if exp, act := true, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + if exp, act := 4, index; exp != act { + t.Errorf("expected 'index' %d, got %d\n", exp, act) + } + if exp, act := 1, len(rulesets); exp != act { + t.Fatalf("expected %d rulesets, got %d\n", exp, act) + } + if exp, act := 2, len(rulesets[0]); exp != act { + t.Errorf("expected %d rules in ruleset 0, got %d\n", exp, act) + } + }) + + t.Run("all leaves without rules", func(t *testing.T) { + r := newRuletrie() + r.LookupOrInsert(ref("foo.aaa.bar.q")) + r.LookupOrInsert(ref("foo.bbb.bar.q")) + + p := planner() + p.vars.Put(ast.Var("x"), p.newLocal()) + + rulesets, _, _, opt := p.optimizeLookup(r, ast.MustParseRef("data.foo[x].bar.q")) + if exp, act := true, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + if exp, act := 0, len(rulesets); exp != act { + t.Fatalf("expected %d rulesets, got %d\n", exp, act) + } + }) + + t.Run("ref heads, mixed case: string and var last term", func(t *testing.T) { + r0, r1 := ast.MustParseRule("b.q.s = 1 { true }"), ast.MustParseRule(`b.q[x] = 2 { x = "t" }`) + r := newRuletrie() + val := r.LookupOrInsert(ref("a.b.q.s")) // b.q.s = 1 (package a) + val.rules = append(val.rules, r0) + val = r.LookupOrInsert(ref("a.b.q")) // b.q[x] = 2 + val.rules = append(val.rules, r1) + + rules := r.Lookup(ref("a.b.q")).Rules() + if exp, act := 2, len(rules); exp != act { + t.Fatalf("ruletrie: expected %d rules, got %d", exp, act) + } + if testing.Verbose() { + t.Logf("rules: %v", r) + } + + p := planner() + p.vars.Put(ast.Var("x"), p.newLocal()) + rulesets, _, _, opt := p.optimizeLookup(r, ast.MustParseRef("data.a[x].q")) + + if exp, act := true, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + if exp, act := 1, len(rulesets); exp != act { + t.Fatalf("expected %d rulesets, got %d\n", exp, act) + } + + if exp, act := 2, len(rulesets[0]); exp != act { + t.Fatalf("expected %d rules in ruleset[0], got %d\n", exp, act) + } + }) + + t.Run("ref heads, mixed case: string and number last term", func(t *testing.T) { + r0, r1 := ast.MustParseRule("b.q[1] = 1 { true }"), ast.MustParseRule(`b.q[x] = 2 { x = "t" }`) + r := newRuletrie() + val := r.LookupOrInsert(ref("a.b.q")) // b.q[1] = 1 (package a) + val.rules = append(val.rules, r0) + val = r.LookupOrInsert(ref("a.b.q")) // b.q[x] = 2 + val.rules = append(val.rules, r1) + + rules := r.Lookup(ref("a.b.q")).Rules() + if exp, act := 2, len(rules); exp != act { + t.Fatalf("ruletrie: expected %d rules, got %d", exp, act) + } + if testing.Verbose() { + t.Logf("rules: %v", r) + } + + p := planner() + p.vars.Put(ast.Var("x"), p.newLocal()) + rulesets, _, _, opt := p.optimizeLookup(r, ast.MustParseRef("data.a[x].q")) + + if exp, act := true, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + if exp, act := 1, len(rulesets); exp != act { + t.Fatalf("expected %d rulesets, got %d\n", exp, act) + } + + if exp, act := 2, len(rulesets[0]); exp != act { + t.Fatalf("expected %d rules in ruleset[0], got %d\n", exp, act) + } + }) + + t.Run("ref heads, unrelated rule in ruletrie", func(t *testing.T) { + r0 := ast.MustParseRule("allow[x].something { x := \"show\" }") + r1 := ast.MustParseRule("allow.see.something_else { true }") + r2 := ast.MustParseRule(`allow.other.stuff { true }`) + r := newRuletrie() + val := r.LookupOrInsert(ref("primary.allow")) + val.rules = append(val.rules, r0, r1) + val = r.LookupOrInsert(ref("unrelated.allow.other.stuff")) + val.rules = append(val.rules, r2) + + if testing.Verbose() { + t.Logf("rules: %v", r) + } + + p := planner() + p.vars.Put(ast.Var("x"), p.newLocal()) + rulesets, _, _, opt := p.optimizeLookup(r, ast.MustParseRef("data[x].allow.see.something_else")) + + if exp, act := true, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + if exp, act := 1, len(rulesets); exp != act { + t.Fatalf("expected %d rulesets, got %d\n", exp, act) + } + + if exp, act := 2, len(rulesets[0]); exp != act { + t.Fatalf("expected %d rules in ruleset[0], got %d\n", exp, act) + } + }) + + t.Run("ref heads, mixed-length rules in ruletrie", func(t *testing.T) { + r0 := ast.MustParseRule("allow[x].something { x := \"show\" }") + r1 := ast.MustParseRule("allow.see.something_else { true }") + r := newRuletrie() + val := r.LookupOrInsert(ref("primary.allow")) + val.rules = append(val.rules, r0) + val = r.LookupOrInsert(ref("secondary.allow.see.something_else")) + val.rules = append(val.rules, r1) + + if testing.Verbose() { + t.Logf("rules: %v", r) + } + + p := planner() + p.vars.Put(ast.Var("x"), p.newLocal()) + _, _, _, opt := p.optimizeLookup(r, ast.MustParseRef("data[x].allow.see.something_else")) + + if exp, act := false, opt; exp != act { + t.Errorf("expected 'optimize' %v, got %v\n", exp, act) + } + }) +} + +func TestPlannerCallDynamic(t *testing.T) { + tests := []struct { + note string + queries []string + modules []string + path []any // path expected on irCallDynamicStmt, string => string const, int => local + where func(*ir.Policy) any // where to start walking search for `exps` + extras []func(any) error + }{ + { + note: "CallDynamicStmt optimization", + queries: []string{`x := "a"; data.test[x] = y`}, + modules: []string{`package test +a if { true }`}, + path: []any{"g0", "test", 2}, + extras: []func(any) error{ + findFunc("g0.data.test.a", "g0.test.a"), + }, + }, + { + note: "simple single-val ref head", + queries: []string{`x := "a"; data.test.a[x].c = y`}, + modules: []string{`package test +a.b.c = 1 if { true }`}, + path: []any{"g0", "test", "a", 2, "c"}, + extras: []func(any) error{ + findFunc("g0.data.test.a.b.c", "g0.test.a.b.c"), + }, + }, + { + note: "two single-val ref heads, string+var", + queries: []string{`x := "a"; data.test.a[x] = y`}, + modules: []string{`package test +a.b.c = 1 if { true } +a.b[t] = 2 if { t := input }`}, + path: []any{"g0", "test", "a", 2}, + extras: []func(any) error{ + findFunc("g0.data.test.a.b", "g0.test.a.b"), + }, + }, + { + note: "two single-val ref heads, number+var", + queries: []string{`x := "a"; data.test.a[x] = y`}, + modules: []string{`package test +a.b[1] = 1 if { true } +a.b[t] = 2 if { t := input }`}, + path: []any{"g0", "test", "a", 2}, + extras: []func(any) error{ + findFunc("g0.data.test.a.b", "g0.test.a.b"), + }, + }, + { + note: "one single-val ref head, number", + queries: []string{`x := "a"; data.test.a[x] = y`}, + modules: []string{`package test +a.b[1] = 1 if { true }`}, + path: []any{"g0", "test", "a", 2}, + extras: []func(any) error{ + findFunc("g0.data.test.a.b", "g0.test.a.b"), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + queries := make([]ast.Body, len(tc.queries)) + for i := range queries { + queries[i] = ast.MustParseBody(tc.queries[i]) + } + modules := make([]*ast.Module, len(tc.modules)) + for i := range modules { + file := fmt.Sprintf("module-%d.rego", i) + m, err := ast.ParseModuleWithOpts(file, tc.modules[i], ast.ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatal(err) + } + modules[i] = m + } + planner := New().WithQueries([]QuerySet{ + { + Name: "test", + Queries: queries, + }, + }).WithModules(modules).WithBuiltinDecls(ast.BuiltinMap) + if testing.Verbose() { + planner = planner.WithDebug(os.Stderr) + } + policy, err := planner.Plan() + if err != nil { + t.Fatal(err) + } + if testing.Verbose() { + err = ir.Pretty(os.Stderr, policy) + if err != nil { + t.Fatal(err) + } + } + start := any(policy) + if tc.where != nil { + start = tc.where(policy) + } + + if tc.path != nil { + exp := make([]ir.Operand, len(tc.path)) + for i := range tc.path { + switch x := tc.path[i].(type) { + case string: + exp[i] = op(ir.StringIndex(planner.getStringConst(x))) + case int: + exp[i] = op(ir.Local(x)) + } + } + if err := findCallDynamic(exp, start); err != nil { + t.Error(err) + } + } + + if tc.extras == nil { + return + } + for _, e := range tc.extras { + if err := e(start); err != nil { + t.Error(err) + } + } + }) + } +} + +type stmtCmpWalker struct { + stmt any + found bool // stop comparing after first found needle +} + +func (*stmtCmpWalker) Before(any) {} +func (*stmtCmpWalker) After(any) {} +func (w *stmtCmpWalker) Visit(x any) (ir.Visitor, error) { + if !w.found { + switch s := w.stmt.(type) { + case *ir.CallDynamicStmt: + c, ok := x.(*ir.CallDynamicStmt) + if ok { + w.found = true + if !reflect.DeepEqual(s.Path, c.Path) { + return nil, fmt.Errorf("call dynamic %v: expected path %v, got %v", c, s.Path, c.Path) + } + } + case *ir.Func: + f, ok := x.(*ir.Func) + if ok && s.Name == f.Name { + w.found = true + if !slices.Equal(s.Path, f.Path) { + return nil, fmt.Errorf("func %v: expected path %v, got %v", f, s.Path, f.Path) + } + } + } + } + return w, nil +} + +func findCallDynamic(path []ir.Operand, p any) error { + w := &stmtCmpWalker{stmt: &ir.CallDynamicStmt{Path: path}} + if err := ir.Walk(w, p); err != nil { + return err + } + if !w.found { + return errors.New("not found") + } + return nil +} + +func findFunc(name, path string) func(any) error { + return func(p any) error { + w := &stmtCmpWalker{stmt: &ir.Func{Name: name, Path: strings.Split(path, ".")}} + if err := ir.Walk(w, p); err != nil { + return err + } + if !w.found { + return errors.New("not found") + } + return nil + } +} diff --git a/third_party/opa/internal/planner/rules.go b/third_party/opa/internal/planner/rules.go new file mode 100644 index 000000000000..9f3d11529398 --- /dev/null +++ b/third_party/opa/internal/planner/rules.go @@ -0,0 +1,337 @@ +package planner + +import ( + "fmt" + "sort" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// funcstack implements a simple map structure used to keep track of virtual +// document => planned function names. The structure supports Push and Pop +// operations so that the planner can shadow planned functions when 'with' +// statements are found. +// The "gen" numbers indicate the "generations"; whenever a 'with' statement +// is planned (a new map is `Push()`ed), it will jump to a previously unused +// number. +type funcstack struct { + stack []taggedPairs + next int +} + +type taggedPairs struct { + pairs map[string]string + vars []ast.Var + vcount int + gen int +} + +func newFuncstack() *funcstack { + return &funcstack{ + stack: []taggedPairs{ + { + pairs: map[string]string{}, + gen: 0, + vars: []ast.Var{ + ast.InputRootDocument.Value.(ast.Var), + ast.DefaultRootDocument.Value.(ast.Var), + }, + vcount: 2, + }, + }, + next: 1} +} + +func (p funcstack) last() taggedPairs { + return p.stack[len(p.stack)-1] +} + +func (p funcstack) argVars() int { + return p.last().vcount +} + +func (p funcstack) vars() []ast.Var { + ret := make([]ast.Var, 0, p.last().vcount) + for i := range p.stack { + ret = append(ret, p.stack[i].vars...) + } + return ret +} + +func (p funcstack) Add(key, value string) { + p.last().pairs[key] = value +} + +func (p funcstack) Get(key string) (string, bool) { + value, ok := p.last().pairs[key] + return value, ok +} + +func (p *funcstack) Push(funcs map[string]string, vars []ast.Var) { + p.stack = append(p.stack, taggedPairs{ + pairs: funcs, + gen: p.next, + vars: vars, + vcount: p.last().vcount + len(vars), + }) + p.next++ +} + +func (p *funcstack) Pop() map[string]string { + last := p.last() + p.stack = p.stack[:len(p.stack)-1] + return last.pairs +} + +func (p funcstack) gen() int { + return p.last().gen +} + +// ruletrie implements a simple trie structure for organizing rules that may be +// planned. The trie nodes are keyed by the rule path. The ruletrie supports +// Push and Pop operations that allow the planner to shadow subtrees when 'with' +// statements are found. +type ruletrie struct { + children map[ast.Value][]*ruletrie + rules []*ast.Rule +} + +func newRuletrie() *ruletrie { + return &ruletrie{ + children: map[ast.Value][]*ruletrie{}, + } +} + +func (t *ruletrie) Arity() int { + rules := t.Rules() + if len(rules) > 0 { + return len(rules[0].Head.Args) + } + return 0 +} + +func (t *ruletrie) Rules() []*ast.Rule { + if t != nil { + if t.rules == nil { + return nil + } + rules := make([]*ast.Rule, len(t.rules), len(t.rules)+len(t.children)) // could be too little + copy(rules, t.rules) + + // NOTE(sr): We pull in one layer of children: the compiler ensures + // that these are the only possible, relevant rule sources for a given + // ref: If the trie is what we get for + // + // a.b.c = 1 { ... } + // a.b[x] = 2 { ... } + // + // and we're retrieving a.b, we want Rules() to include the rule body + // of a.b.c. + // FIXME: We need to go deeper than just immediate children (?) + for _, rs := range t.children { + if r := rs[len(rs)-1].rules; r != nil { + rules = append(rules, r...) + } + } + return rules + } + return nil +} + +func (t *ruletrie) Push(key ast.Ref) { + node := t + for i := range len(key) - 1 { + node = node.Get(key[i].Value) + if node == nil { + return + } + } + elem := key[len(key)-1] + node.children[elem.Value] = append(node.children[elem.Value], nil) +} + +func (t *ruletrie) Pop(key ast.Ref) { + node := t + for i := range len(key) - 1 { + node = node.Get(key[i].Value) + if node == nil { + return + } + } + elem := key[len(key)-1] + sl := node.children[elem.Value] + node.children[elem.Value] = sl[:len(sl)-1] +} + +func (t *ruletrie) Insert(key ast.Ref) *ruletrie { + node := t + for _, elem := range key { + child := node.Get(elem.Value) + if child == nil { + child = newRuletrie() + node.children[elem.Value] = append(node.children[elem.Value], child) + } + node = child + } + return node +} + +func (t *ruletrie) Lookup(key ast.Ref) *ruletrie { + node := t + for _, elem := range key { + node = node.Get(elem.Value) + if node == nil { + return nil + } + } + return node +} + +func (t *ruletrie) LookupShallowest(key ast.Ref) *ruletrie { + node := t + for _, elem := range key { + node = node.Get(elem.Value) + if node == nil { + return nil + } + if len(node.rules) > 0 { + return node + } + } + return node +} + +// TODO: Collapse rules with overlapping extent to same node(?) +func (t *ruletrie) LookupOrInsert(key ast.Ref) *ruletrie { + if val := t.LookupShallowest(key); val != nil { + + return val + } + return t.Insert(key) +} + +func (t *ruletrie) DescendantRules() []*ast.Rule { + if len(t.children) == 0 { + return t.rules + } + + rules := make([]*ast.Rule, len(t.rules), len(t.rules)+len(t.children)) // could be too little + copy(rules, t.rules) + + for _, cs := range t.children { + for _, c := range cs { + rules = append(rules, c.DescendantRules()...) + } + } + + return rules +} + +func (t *ruletrie) ChildrenCount() int { + return len(t.children) +} + +func (t *ruletrie) Children() []ast.Value { + if t == nil { + return nil + } + sorted := make([]ast.Value, 0, len(t.children)) + for key := range t.children { + if t.Get(key) != nil { + sorted = append(sorted, key) + } + } + sort.Slice(sorted, func(i, j int) bool { + return sorted[i].Compare(sorted[j]) < 0 + }) + return sorted +} + +func (t *ruletrie) Get(k ast.Value) *ruletrie { + if t == nil { + return nil + } + nodes := t.children[k] + if len(nodes) == 0 { + return nil + } + return nodes[len(nodes)-1] +} + +func (t *ruletrie) DepthFirst(f func(*ruletrie) bool) { + if f(t) { + return + } + for _, rules := range t.children { + for i := range rules { + rules[i].DepthFirst(f) + } + } +} + +func (t *ruletrie) Depth() int { + if len(t.Children()) == 0 { + return 0 + } + c := make([]int, 0, len(t.Children())) + for _, nodes := range t.children { + c = append(c, nodes[len(nodes)-1].Depth()) + } + max := 0 + for i := range c { + if max < c[i] { + max = c[i] + } + } + return max + 1 +} + +func (t *ruletrie) String() string { + return fmt.Sprintf("", t.rules, t.children) +} + +type functionMocksStack struct { + stack []*functionMocksElem +} + +type functionMocksElem []frame + +type frame map[string]*ast.Term + +func newFunctionMocksStack() *functionMocksStack { + stack := &functionMocksStack{} + stack.Push() + return stack +} + +func newFunctionMocksElem() *functionMocksElem { + return &functionMocksElem{} +} + +func (s *functionMocksStack) Push() { + s.stack = append(s.stack, newFunctionMocksElem()) +} + +func (s *functionMocksStack) Pop() { + s.stack = s.stack[:len(s.stack)-1] +} + +func (s *functionMocksStack) PushFrame(f frame) { + current := s.stack[len(s.stack)-1] + *current = append(*current, f) +} + +func (s *functionMocksStack) PopFrame() { + current := s.stack[len(s.stack)-1] + *current = (*current)[:len(*current)-1] +} + +func (s *functionMocksStack) Lookup(f string) *ast.Term { + current := *s.stack[len(s.stack)-1] + for i := len(current) - 1; i >= 0; i-- { + if t, ok := current[i][f]; ok { + return t + } + } + return nil +} diff --git a/third_party/opa/internal/planner/rules_test.go b/third_party/opa/internal/planner/rules_test.go new file mode 100644 index 000000000000..775fbdda4c6f --- /dev/null +++ b/third_party/opa/internal/planner/rules_test.go @@ -0,0 +1,178 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package planner + +import ( + "slices" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestFuncstack(t *testing.T) { + fs := newFuncstack() + + fs.Add("data.foo.bar", "g0.data.foo.bar") + if exp, act := 2, fs.argVars(); exp != act { + t.Errorf("expected fs argVars to be %d, got %d", exp, act) + } + if exp, act := []ast.Var{ast.InputRootDocument.Value.(ast.Var), ast.DefaultRootDocument.Value.(ast.Var)}, + fs.vars(); !slices.Equal(exp, act) { + t.Errorf("expected fs vars to match, got exp=%v, act=%v", exp, act) + } + + v0 := ast.Var("v0") + fs.Push(map[string]string{}, []ast.Var{v0}) // g0 -> g1 + fs.Add("data.foo.bar", "g1.data.foo.bar") + f, ok := fs.Get("data.foo.bar") + if exp, act := true, ok; exp != act { + t.Fatal("expected func to be found") + } + if exp, act := "g1.data.foo.bar", f; exp != act { + t.Errorf("expected func to be %v, got %v", exp, act) + } + if exp, act := 1, fs.gen(); exp != act { + t.Errorf("expected fs gen to be %d, got %d", exp, act) + } + if exp, act := 3, fs.argVars(); exp != act { + t.Errorf("expected fs argVars to be %d, got %d", exp, act) + } + if exp, act := []ast.Var{ast.InputRootDocument.Value.(ast.Var), ast.DefaultRootDocument.Value.(ast.Var), v0}, + fs.vars(); !slices.Equal(exp, act) { + t.Errorf("expected fs vars to match, got exp=%v, act=%v", exp, act) + } + + g1 := fs.Pop() // g1 -> g0 + if exp, act := 1, len(g1); exp != act { + t.Errorf("expected g1 func map to have length %d, got %d", exp, act) + } + if exp, act := 0, fs.gen(); exp != act { + t.Errorf("expected fs gen to be %d, got %d", exp, act) + } + if exp, act := 2, fs.argVars(); exp != act { + t.Errorf("expected fs argVars to be %d, got %d", exp, act) + } + if exp, act := []ast.Var{ast.InputRootDocument.Value.(ast.Var), ast.DefaultRootDocument.Value.(ast.Var)}, + fs.vars(); !slices.Equal(exp, act) { + t.Errorf("expected fs vars to match, got exp=%v, act=%v", exp, act) + } + + f, ok = fs.Get("data.foo.bar") + if exp, act := true, ok; exp != act { + t.Fatalf("expected func to be found") + } + if exp, act := "g0.data.foo.bar", f; exp != act { + t.Errorf("expected func to be %v, got %v", exp, act) + } + + v1 := ast.Var("v1") + fs.Push(map[string]string{}, []ast.Var{v1}) // g0 -> g2 + fs.Add("data.foo.bar", "g2.data.foo.bar") + f, ok = fs.Get("data.foo.bar") + if exp, act := true, ok; exp != act { + t.Fatal("expected func to be found") + } + if exp, act := "g2.data.foo.bar", f; exp != act { + t.Errorf("expected func to be %v, got %v", exp, act) + } + if exp, act := 2, fs.gen(); exp != act { + t.Errorf("expected fs gen to be %d, got %d", exp, act) + } + if exp, act := 3, fs.argVars(); exp != act { + t.Errorf("expected fs argVars to be %d, got %d", exp, act) + } + if exp, act := []ast.Var{ast.InputRootDocument.Value.(ast.Var), ast.DefaultRootDocument.Value.(ast.Var), v1}, + fs.vars(); !slices.Equal(exp, act) { + t.Errorf("expected fs vars to match, got exp=%v, act=%v", exp, act) + } + + fs.Push(map[string]string{}, []ast.Var{v0}) // g2 -> g3 + fs.Add("data.foo.bar", "g3.data.foo.bar") + f, ok = fs.Get("data.foo.bar") + if exp, act := true, ok; exp != act { + t.Fatal("expected func to be found") + } + if exp, act := "g3.data.foo.bar", f; exp != act { + t.Errorf("expected func to be %v, got %v", exp, act) + } + if exp, act := 4, fs.argVars(); exp != act { + t.Errorf("expected fs argVars to be %d, got %d", exp, act) + } + if exp, act := []ast.Var{ast.InputRootDocument.Value.(ast.Var), ast.DefaultRootDocument.Value.(ast.Var), v1, v0}, + fs.vars(); !slices.Equal(exp, act) { + t.Errorf("expected fs vars to match, got exp=%v, act=%v", exp, act) + } + _ = fs.Pop() // g3 -> g2 + if exp, act := 3, fs.argVars(); exp != act { + t.Errorf("expected fs argVars to be %d, got %d", exp, act) + } + if exp, act := []ast.Var{ast.InputRootDocument.Value.(ast.Var), ast.DefaultRootDocument.Value.(ast.Var), v1}, + fs.vars(); !slices.Equal(exp, act) { + t.Errorf("expected fs vars to match, got exp=%v, act=%v", exp, act) + } + _ = fs.Pop() // g2 -> g0 + if exp, act := 0, fs.gen(); exp != act { + t.Errorf("expected fs gen to be %d, got %d", exp, act) + } + if exp, act := 2, fs.argVars(); exp != act { + t.Errorf("expected fs argVars to be %d, got %d", exp, act) + } + if exp, act := []ast.Var{ast.InputRootDocument.Value.(ast.Var), ast.DefaultRootDocument.Value.(ast.Var)}, + fs.vars(); !slices.Equal(exp, act) { + t.Errorf("expected fs vars to match, got exp=%v, act=%v", exp, act) + } + + fs.Push(map[string]string{}, nil) // g0 -> g4 + if exp, act := 4, fs.gen(); exp != act { + t.Errorf("expected fs gen to be %d, got %d", exp, act) + } +} + +func TestDataRefsShadowRuletrie(t *testing.T) { + p := New() + rt := p.rules + rt.Insert(ast.MustParseRef(("data.foo.bar"))) + rt.Insert(ast.MustParseRef(("data.foo.baz"))) + rt.Insert(ast.MustParseRef(("data.foo.bar.quz"))) + + tests := []struct { + note string + refs []ast.Ref + exp bool + }{ + { + note: "no refs", + refs: nil, + exp: false, + }, + { + note: "data root node", + refs: []ast.Ref{ast.MustParseRef("data")}, + exp: true, + }, + { + note: "one ref only, mismatch in first level", + refs: []ast.Ref{ast.MustParseRef("data.quz")}, + exp: false, + }, + { + note: "two refs, matching 2nd", + refs: []ast.Ref{ + ast.MustParseRef("data.quz"), + ast.MustParseRef("data.foo"), + }, + exp: true, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + act := p.dataRefsShadowRuletrie(tc.refs) + if tc.exp != act { + t.Errorf("expected %v, got %v", tc.exp, act) + } + }) + } +} diff --git a/third_party/opa/internal/planner/varstack.go b/third_party/opa/internal/planner/varstack.go new file mode 100644 index 000000000000..0df6bcd8b2c2 --- /dev/null +++ b/third_party/opa/internal/planner/varstack.go @@ -0,0 +1,71 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package planner + +import ( + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ir" +) + +type varstack []map[ast.Var]ir.Local + +func newVarstack(frames ...map[ast.Var]ir.Local) *varstack { + vs := &varstack{} + for _, f := range frames { + vs.Push(f) + } + return vs +} + +func (vs varstack) GetOrElse(k ast.Var, orElse func() ir.Local) ir.Local { + l, ok := vs.Get(k) + if !ok { + l = orElse() + vs.Put(k, l) + } + return l +} + +func (vs varstack) GetOrEmpty(k ast.Var) ir.Local { + l, _ := vs.Get(k) + return l +} + +func (vs varstack) Get(k ast.Var) (ir.Local, bool) { + for i := len(vs) - 1; i >= 0; i-- { + if l, ok := vs[i][k]; ok { + return l, true + } + } + return 0, false +} + +func (vs varstack) GetOpOrEmpty(k ast.Var) ir.Operand { + l := vs.GetOrEmpty(k) + return ir.Operand{Value: l} +} + +func (vs varstack) GetOp(k ast.Var) (ir.Operand, bool) { + l, ok := vs.Get(k) + if !ok { + return ir.Operand{}, false + } + return ir.Operand{Value: l}, true +} + +func (vs varstack) Put(k ast.Var, v ir.Local) { + vs[len(vs)-1][k] = v +} + +func (vs *varstack) Push(frame map[ast.Var]ir.Local) { + *vs = append(*vs, frame) +} + +func (vs *varstack) Pop() map[ast.Var]ir.Local { + sl := *vs + last := sl[len(sl)-1] + *vs = sl[:len(sl)-1] + return last +} diff --git a/third_party/opa/internal/planner/varstack_test.go b/third_party/opa/internal/planner/varstack_test.go new file mode 100644 index 000000000000..9d44a25354ab --- /dev/null +++ b/third_party/opa/internal/planner/varstack_test.go @@ -0,0 +1,44 @@ +package planner + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ir" +) + +func TestVarStackPushPop(t *testing.T) { + p := New() + vs := newVarstack() + vs.Push(map[ast.Var]ir.Local{}) + loc0, loc1 := p.newLocal(), p.newLocal() + vs.Put(ast.Var("x"), loc0) + vs.Push(map[ast.Var]ir.Local{}) + loc, ok := vs.Get(ast.Var("x")) + if exp, act := true, ok; exp != act { + t.Errorf("Get(x): expected %v, got %v", exp, act) + } + if exp, act := loc0, loc; exp != act { + t.Errorf("Get(x) expected %v, got %v", exp, act) + } + vs.Put(ast.Var("y"), loc1) + pop := vs.Pop() + if exp, act := 1, len(pop); exp != act { + t.Errorf("Pop(): expected len %v, got %v", exp, act) + } + + // x still there + loc, ok = vs.Get(ast.Var("x")) + if exp, act := true, ok; exp != act { + t.Errorf("Get(x): expected %v, got %v", exp, act) + } + if exp, act := loc0, loc; exp != act { + t.Errorf("Get(x) expected %v, got %v", exp, act) + } + + // y not there + _, ok = vs.Get(ast.Var("y")) + if exp, act := false, ok; exp != act { + t.Errorf("Get(y): expected %v, got %v", exp, act) + } +} diff --git a/third_party/opa/internal/presentation/presentation.go b/third_party/opa/internal/presentation/presentation.go new file mode 100644 index 000000000000..4329a3dcddc4 --- /dev/null +++ b/third_party/opa/internal/presentation/presentation.go @@ -0,0 +1,751 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package presentation prints results of an expression evaluation in +// json and tabular formats. +package presentation + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "os" + "sort" + "strconv" + "strings" + "time" + + "github.com/olekukonko/tablewriter" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/cover" + "github.com/open-policy-agent/opa/v1/format" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/profiler" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/topdown" +) + +// DefaultProfileSortOrder is the default ordering unless something is specified in the CLI +var DefaultProfileSortOrder = []string{"total_time_ns", "num_eval", "num_redo", "file", "line"} + +// DepAnalysisOutput contains the result of dependency analysis to be presented. +type DepAnalysisOutput struct { + Base []ast.Ref `json:"base,omitempty"` + Virtual []ast.Ref `json:"virtual,omitempty"` +} + +// JSON outputs o to w as JSON. +func (o DepAnalysisOutput) JSON(w io.Writer) error { + o.sort() + return JSON(w, o) +} + +// Pretty outputs o to w in a human-readable format. +func (o DepAnalysisOutput) Pretty(w io.Writer) error { + + var headers []string + var rows [][]string + + // Fill two columns if results have base and virtual docs. Else fill one column. + if len(o.Base) > 0 && len(o.Virtual) > 0 { + maxLen := max(len(o.Virtual), len(o.Base)) + headers = []string{"Base Documents", "Virtual Documents"} + rows = make([][]string, maxLen) + for i := range rows { + rows[i] = make([]string, 2) + if i < len(o.Base) { + rows[i][0] = o.Base[i].String() + } + if i < len(o.Virtual) { + rows[i][1] = o.Virtual[i].String() + } + } + } else if len(o.Base) > 0 { + headers = []string{"Base Documents"} + rows = make([][]string, len(o.Base)) + for i := range rows { + rows[i] = []string{o.Base[i].String()} + } + } else if len(o.Virtual) > 0 { + headers = []string{"Virtual Documents"} + rows = make([][]string, len(o.Virtual)) + for i := range rows { + rows[i] = []string{o.Virtual[i].String()} + } + } + + if len(rows) == 0 { + return nil + } + + table := tablewriter.NewWriter(w) + table.SetHeader(headers) + table.SetAutoWrapText(false) + for i := range rows { + table.Append(rows[i]) + } + + table.Render() + + return nil +} + +func (o DepAnalysisOutput) sort() { + sort.Slice(o.Base, func(i, j int) bool { + return o.Base[i].Compare(o.Base[j]) < 0 + }) + + sort.Slice(o.Virtual, func(i, j int) bool { + return o.Virtual[i].Compare(o.Virtual[j]) < 0 + }) +} + +// Output contains the result of evaluation to be presented. +type Output struct { + Errors OutputErrors `json:"errors,omitempty"` + Result rego.ResultSet `json:"result,omitempty"` + Partial *rego.PartialQueries `json:"partial,omitempty"` + Metrics metrics.Metrics `json:"metrics,omitempty"` + AggregatedMetrics map[string]any `json:"aggregated_metrics,omitempty"` + Explanation []*topdown.Event `json:"explanation,omitempty"` + Profile []profiler.ExprStats `json:"profile,omitempty"` + AggregatedProfile []profiler.ExprStatsAggregated `json:"aggregated_profile,omitempty"` + Coverage *cover.Report `json:"coverage,omitempty"` + limit int +} + +// WithLimit sets the output limit to set on stringified values. +func (e Output) WithLimit(n int) Output { + e.limit = n + return e +} + +func (e Output) undefined() bool { + return len(e.Result) == 0 && (e.Partial == nil || len(e.Partial.Queries) == 0) +} + +// NewOutputErrors creates a new slice of OutputError's based +// on the type of error passed in. Known structured types will +// be translated as appropriate, while unknown errors are +// placed into a structured format with their string value. +func NewOutputErrors(err error) []OutputError { + var errs []OutputError + if err != nil { + // Handle known structured errors + + switch typedErr := err.(type) { + case *ast.Error: + errs = []OutputError{{ + Code: typedErr.Code, + Message: typedErr.Message, + Details: typedErr.Details, + Location: typedErr.Location, + err: typedErr, + }} + + case *topdown.Error: + errs = []OutputError{{ + Code: typedErr.Code, + Message: typedErr.Message, + Location: typedErr.Location, + err: typedErr, + }} + case *storage.Error: + errs = []OutputError{{ + Code: typedErr.Code, + Message: typedErr.Message, + err: typedErr, + }} + + // The cases below are wrappers for other errors, format errors + // recursively on them. + case ast.Errors: + for _, e := range typedErr { + if e != nil { + errs = append(errs, NewOutputErrors(e)...) + } + } + case rego.Errors: + for _, e := range typedErr { + if e != nil { + errs = append(errs, NewOutputErrors(e)...) + } + } + case loader.Errors: + for _, e := range typedErr { + if e != nil { + errs = append(errs, NewOutputErrors(e)...) + } + } + default: + // Any errors which don't have a structure we know about + // are converted to their string representation only. + errs = []OutputError{{ + Message: err.Error(), + err: typedErr, + }} + if d, ok := err.(rego.ErrorDetails); ok { + details := strings.Join(d.Lines(), "\n") + errs[0].Details = details + } + } + } + return errs +} + +// OutputErrors is a list of errors encountered +// which are to presented. +type OutputErrors []OutputError + +func (e OutputErrors) Error() string { + if len(e) == 0 { + return "no error(s)" + } + + var prefix string + if len(e) == 1 { + prefix = "1 error occurred: " + } else { + prefix = fmt.Sprintf("%d errors occurred:\n", len(e)) + } + + // We preallocate for at least the minimum number of strings. + s := make([]string, 0, len(e)) + for _, err := range e { + s = append(s, err.Error()) + if l, ok := err.Details.(string); ok { + s = append(s, l) + } + } + + return prefix + strings.Join(s, "\n") +} + +// OutputError provides a common structure for all OPA +// library errors so that the JSON output given by the +// presentation package is consistent and parsable. +type OutputError struct { + Message string `json:"message"` + Code string `json:"code,omitempty"` + Location *ast.Location `json:"location,omitempty"` + Details any `json:"details,omitempty"` + err error +} + +func (j OutputError) Error() string { + return j.err.Error() +} + +// JSON writes x to w with indentation. +func JSON(w io.Writer, x any) error { + encoder := json.NewEncoder(w) + encoder.SetIndent("", " ") + return encoder.Encode(x) +} + +// Bindings prints the bindings from r to w. +func Bindings(w io.Writer, r Output) error { + if r.Errors != nil { + return prettyError(w, r.Errors) + } + for _, rs := range r.Result { + if err := JSON(w, rs.Bindings); err != nil { + return err + } + } + return nil +} + +// Values prints the values from r to w. +func Values(w io.Writer, r Output) error { + if r.Errors != nil { + return prettyError(w, r.Errors) + } + for _, rs := range r.Result { + line := make([]any, len(rs.Expressions)) + for i := range line { + line[i] = rs.Expressions[i].Value + } + if err := JSON(os.Stdout, line); err != nil { + return err + } + } + return nil +} + +// Pretty prints all of r to w in a human-readable format. +func Pretty(w io.Writer, r Output) error { + return PrettyWithOptions(w, r, PrettyOptions{ + TraceOpts: topdown.PrettyTraceOptions{ + Locations: true, + }, + }) +} + +type PrettyOptions struct { + TraceOpts topdown.PrettyTraceOptions +} + +// PrettyWithOptions prints all of r to w in a human-readable format. +func PrettyWithOptions(w io.Writer, r Output, opts PrettyOptions) error { + if len(r.Explanation) > 0 { + if err := prettyExplanation(w, r.Explanation, opts.TraceOpts); err != nil { + return err + } + } + if r.Errors != nil { + if err := prettyError(w, r.Errors); err != nil { + return err + } + } else if r.undefined() { + fmt.Fprintln(w, "undefined") + } else if r.Result != nil { + if err := prettyResult(w, r.Result, r.limit); err != nil { + return err + } + } else if r.Partial != nil { + if err := prettyPartial(w, r.Partial); err != nil { + return err + } + } + if r.Metrics != nil { + if err := prettyMetrics(w, r.Metrics, r.limit); err != nil { + return err + } + } + if len(r.Profile) > 0 { + if err := prettyProfile(w, r.Profile); err != nil { + return err + } + } + if len(r.AggregatedMetrics) > 0 { + if err := prettyAggregatedMetrics(w, r.AggregatedMetrics, r.limit); err != nil { + return err + } + } + if len(r.AggregatedProfile) > 0 { + if err := prettyAggregatedProfile(w, r.AggregatedProfile); err != nil { + return err + } + } + if r.Coverage != nil { + if err := prettyCoverage(w, r.Coverage); err != nil { + return err + } + } + return nil +} + +// Source prints partial evaluation results in r to w in a source file friendly +// format. +func Source(w io.Writer, r Output) error { + + if r.Errors != nil { + return prettyError(w, r.Errors) + } + + for i := range r.Partial.Queries { + fmt.Fprintf(w, "# Query %d\n", i+1) + bs, err := format.AstWithOpts(r.Partial.Queries[i], format.Opts{IgnoreLocations: true}) + if err != nil { + return err + } + fmt.Fprintln(w, string(bs)) + } + + for i := range r.Partial.Support { + fmt.Fprintf(w, "# Module %d\n", i+1) + bs, err := format.AstWithOpts(r.Partial.Support[i], format.Opts{IgnoreLocations: true, RegoVersion: r.Partial.Support[i].RegoVersion()}) + if err != nil { + return err + } + fmt.Fprint(w, string(bs)) + } + + return nil +} + +// Raw prints the values from r to w. Each result is written on a separate +// line, and the expressions are separated by spaces. If the values are +// strings, they are written directly rather than formatted as compact +// JSON strings. This output format makes OPA useful in a scripting context. +func Raw(w io.Writer, r Output) error { + if r.Errors != nil { + return prettyError(w, r.Errors) + } + + for _, rs := range r.Result { + for i, expr := range rs.Expressions { + if str, ok := expr.Value.(string); ok { + fmt.Fprint(w, str) + } else { + bytes, err := json.Marshal(expr.Value) + if err != nil { + return err + } + + fmt.Fprint(w, string(bytes)) + } + + if i+1 >= len(rs.Expressions) { + fmt.Fprintln(w, "") + } else { + fmt.Fprint(w, " ") + } + } + } + + return nil +} + +func Discard(w io.Writer, x any) error { + encoder := json.NewEncoder(w) + encoder.SetIndent("", " ") + field, ok := x.(Output) + if !ok { + return errors.New("error in converting interface to type Output") + } + bs, err := json.Marshal(field) + if err != nil { + return err + } + var rawData map[string]any + err = json.Unmarshal(bs, &rawData) + if err != nil { + return err + } + if rawData["result"] != nil { + rawData["result"] = "discarded" + } + return encoder.Encode(rawData) +} + +func prettyError(w io.Writer, errs OutputErrors) error { + _, err := fmt.Fprintln(w, errs) + return err +} + +func prettyResult(w io.Writer, rs rego.ResultSet, limit int) error { + + if len(rs) == 1 && len(rs[0].Bindings) == 0 { + if len(rs[0].Expressions) == 1 || allBoolean(rs[0].Expressions) { + return JSON(w, rs[0].Expressions[0].Value) + } + } + + keys := generateResultKeys(rs) + tableBindings := generateTableBindings(w, keys, rs, limit) + if tableBindings.NumLines() > 0 { + tableBindings.Render() + } + + return nil +} + +func prettyPartial(w io.Writer, pq *rego.PartialQueries) error { + + table := tablewriter.NewWriter(w) + table.SetRowLine(true) + table.SetAutoWrapText(false) + var maxWidth int + + for i := range pq.Queries { + f, width, err := prettyASTNode(pq.Queries[i], ast.DefaultRegoVersion) + if err != nil { + return err + } + if width > maxWidth { + maxWidth = width + } + table.Append([]string{fmt.Sprintf("Query %d", i+1), f}) + } + + for i, s := range pq.Support { + f, width, err := prettyASTNode(s, s.RegoVersion()) + if err != nil { + return err + } + if width > maxWidth { + maxWidth = width + } + table.Append([]string{fmt.Sprintf("Support %d", i+1), f}) + } + + table.SetColMinWidth(1, maxWidth) + table.Render() + + return nil +} + +// prettyASTNode is used for pretty-printing the result of partial eval +func prettyASTNode(x any, regoVersion ast.RegoVersion) (string, int, error) { + bs, err := format.AstWithOpts(x, format.Opts{IgnoreLocations: true, RegoVersion: regoVersion}) + if err != nil { + return "", 0, fmt.Errorf("format error: %w", err) + } + var maxLineWidth int + s := strings.Trim(strings.ReplaceAll(string(bs), "\t", " "), "\n") + for _, line := range strings.Split(s, "\n") { + width := tablewriter.DisplayWidth(line) + if width > maxLineWidth { + maxLineWidth = width + } + } + return s, maxLineWidth, nil +} + +func prettyMetrics(w io.Writer, m metrics.Metrics, limit int) error { + tableMetrics := generateTableMetrics(w) + populateTableMetrics(m, tableMetrics, limit) + if tableMetrics.NumLines() > 0 { + tableMetrics.Render() + } + return nil +} + +var statKeys = []string{"min", "max", "mean", "90%", "99%"} + +func prettyAggregatedMetrics(w io.Writer, ms map[string]any, limit int) error { + keys := []string{"metric"} + tableMetrics := generateTableWithKeys(w, append(keys, statKeys...)...) + populateTableAggregatedMetrics(ms, tableMetrics, limit) + if tableMetrics.NumLines() > 0 { + tableMetrics.Render() + } + return nil +} + +func prettyProfile(w io.Writer, profile []profiler.ExprStats) error { + tableProfile := generateTableProfile(w) + + for _, rs := range profile { + line := []string{} + timeNs := time.Duration(rs.ExprTimeNs) * time.Nanosecond + timeNsStr := timeNs.String() + numEval := strconv.FormatInt(int64(rs.NumEval), 10) + numRedo := strconv.FormatInt(int64(rs.NumRedo), 10) + numGenExpr := strconv.FormatInt(int64(rs.NumGenExpr), 10) + loc := rs.Location.String() + line = append(line, timeNsStr, numEval, numRedo, numGenExpr, loc) + tableProfile.Append(line) + } + if tableProfile.NumLines() > 0 { + tableProfile.Render() + } + return nil +} + +func prettyAggregatedProfile(w io.Writer, profile []profiler.ExprStatsAggregated) error { + tableProfile := generateTableWithKeys(w, append(statKeys, "num eval", "num redo", "num gen expr", "location")...) + for _, rs := range profile { + line := []string{} + for _, k := range statKeys { + v := rs.ExprTimeNsStats.(map[string]any)[k] + if f, ok := v.(float64); ok { + line = append(line, time.Duration(f).String()) + } else if i, ok := v.(int64); ok { + line = append(line, time.Duration(i).String()) + } + } + numEval := strconv.FormatInt(int64(rs.NumEval), 10) + numRedo := strconv.FormatInt(int64(rs.NumRedo), 10) + numGenExpr := strconv.FormatInt(int64(rs.NumGenExpr), 10) + loc := rs.Location.String() + line = append(line, numEval, numRedo, numGenExpr, loc) + tableProfile.Append(line) + } + if tableProfile.NumLines() > 0 { + tableProfile.Render() + } + return nil +} + +func prettyExplanation(w io.Writer, explanation []*topdown.Event, opts topdown.PrettyTraceOptions) error { + topdown.PrettyTraceWithOpts(w, explanation, opts) + return nil +} + +func prettyCoverage(w io.Writer, report *cover.Report) error { + table := tablewriter.NewWriter(w) + table.Append([]string{"Overall Coverage", fmt.Sprintf("%.02f", report.Coverage)}) + table.Render() + return nil +} + +func checkStrLimit(input string, limit int) string { + if limit > 0 && len(input) > limit { + input = input[:limit] + "..." + return input + } + return input +} + +func generateTableBindings(writer io.Writer, keys []resultKey, rs rego.ResultSet, prettyLimit int) *tablewriter.Table { + table := tablewriter.NewWriter(writer) + table.SetAlignment(tablewriter.ALIGN_CENTER) + table.SetAutoFormatHeaders(false) + header := make([]string, len(keys)) + for i := range header { + header[i] = keys[i].string() + } + table.SetHeader(header) + alignment := make([]int, len(keys)) + for i := range header { + alignment[i] = tablewriter.ALIGN_LEFT + } + table.SetColumnAlignment(alignment) + + for _, row := range rs { + printPrettyRow(table, keys, row, prettyLimit) + } + return table +} + +func printPrettyRow(table *tablewriter.Table, keys []resultKey, result rego.Result, prettyLimit int) { + buf := []string{} + for _, k := range keys { + v := k.selectVarValue(result) + js, err := json.Marshal(v) + if err != nil { + buf = append(buf, err.Error()) + } else { + s := checkStrLimit(string(js), prettyLimit) + buf = append(buf, s) + } + } + table.Append(buf) +} + +func generateTableMetrics(writer io.Writer) *tablewriter.Table { + return generateTableWithKeys(writer, "Metric", "Value") +} + +func generateTableWithKeys(writer io.Writer, keys ...string) *tablewriter.Table { + table := tablewriter.NewWriter(writer) + aligns := make([]int, 0, len(keys)) + hdrs := make([]string, 0, len(keys)) + for _, k := range keys { + hdrs = append(hdrs, strings.Title(k)) //nolint:staticcheck // SA1019, no unicode here + aligns = append(aligns, tablewriter.ALIGN_LEFT) + } + table.SetHeader(hdrs) + table.SetAlignment(tablewriter.ALIGN_CENTER) + table.SetColumnAlignment(aligns) + return table +} + +func generateTableProfile(writer io.Writer) *tablewriter.Table { + return generateTableWithKeys(writer, "Time", "Num Eval", "Num Redo", "Num Gen Expr", "Location") +} + +func populateTableMetrics(m metrics.Metrics, table *tablewriter.Table, prettyLimit int) { + lines := [][]string{} + for varName, varValueInterface := range m.All() { + val, ok := varValueInterface.(map[string]any) + if !ok { + line := []string{} + varValue := checkStrLimit(fmt.Sprintf("%v", varValueInterface), prettyLimit) + line = append(line, varName, varValue) + lines = append(lines, line) + } else { + for k, v := range val { + line := []string{} + newVarName := fmt.Sprintf("%v_%v", varName, k) + value := checkStrLimit(fmt.Sprintf("%v", v), prettyLimit) + line = append(line, newVarName, value) + lines = append(lines, line) + } + } + } + sortMetricRows(lines) + table.AppendBulk(lines) +} + +func populateTableAggregatedMetrics(ms map[string]any, table *tablewriter.Table, prettyLimit int) { + lines := [][]string{} + for name, vals := range ms { + line := []string{name} + vs := vals.(map[string]any) + for _, k := range statKeys { + line = append(line, checkStrLimit(fmt.Sprintf("%v", vs[k]), prettyLimit)) + } + lines = append(lines, line) + } + sortMetricRows(lines) + table.AppendBulk(lines) +} + +func sortMetricRows(data [][]string) { + sort.Slice(data, func(i, j int) bool { + return data[i][0] < data[j][0] + }) +} + +type resultKey struct { + varName string + exprIndex int + exprText string +} + +func resultKeyLess(a, b resultKey) bool { + if a.varName != "" { + if b.varName == "" { + return true + } + return a.varName < b.varName + } + return a.exprIndex < b.exprIndex +} + +func (rk resultKey) string() string { + if rk.varName != "" { + return rk.varName + } + return rk.exprText +} + +func (rk resultKey) selectVarValue(result rego.Result) any { + if rk.varName != "" { + return result.Bindings[rk.varName] + } + return result.Expressions[rk.exprIndex].Value +} + +func generateResultKeys(rs rego.ResultSet) []resultKey { + keys := []resultKey{} + if len(rs) != 0 { + for k := range rs[0].Bindings { + keys = append(keys, resultKey{ + varName: k, + }) + } + + for i, expr := range rs[0].Expressions { + if _, ok := expr.Value.(bool); !ok || len(rs[0].Bindings) == 0 { + keys = append(keys, resultKey{ + exprIndex: i, + exprText: expr.Text, + }) + } + } + + sort.Slice(keys, func(i, j int) bool { + return resultKeyLess(keys[i], keys[j]) + }) + } + return keys +} + +func allBoolean(ev []*rego.ExpressionValue) bool { + for i := range ev { + if _, ok := ev[i].Value.(bool); !ok { + return false + } + } + return true +} diff --git a/third_party/opa/internal/presentation/presentation_test.go b/third_party/opa/internal/presentation/presentation_test.go new file mode 100644 index 000000000000..974ad38868a4 --- /dev/null +++ b/third_party/opa/internal/presentation/presentation_test.go @@ -0,0 +1,579 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package presentation + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +type testErrorWithMarshaller struct { + msg string +} + +func (t *testErrorWithMarshaller) Error() string { + return t.msg +} + +func (t *testErrorWithMarshaller) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Text string `json:"text"` + }{ + Text: t.msg, + }) +} + +type testErrorWithDetails struct{} + +func (*testErrorWithDetails) Error() string { return "something went wrong" } +func (*testErrorWithDetails) Lines() []string { return []string{"oh", "so", "wrong"} } + +func validateJSONOutput(t *testing.T, testErr error, expected string) { + t.Helper() + output := Output{Errors: NewOutputErrors(testErr)} + var buf bytes.Buffer + err := JSON(&buf, output) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + result := util.MustUnmarshalJSON(buf.Bytes()) + exp := util.MustUnmarshalJSON([]byte(expected)) + + if !reflect.DeepEqual(result, exp) { + t.Fatal("expected:", exp, "got:", result) + } +} + +func TestOutputJSONErrorUnstructured(t *testing.T) { + err := errors.New("some text") + expected := `{ + "errors": [ + { + "message": "some text" + } + ] +} +` + + validateJSONOutput(t, err, expected) +} + +func TestOutputJSONErrorCustomMarshaller(t *testing.T) { + err := &testErrorWithMarshaller{ + msg: "custom message", + } + expected := `{ + "errors": [ + { + "message": "custom message" + } + ] +} +` + + validateJSONOutput(t, err, expected) +} + +func TestOutputJSONErrorWithDetails(t *testing.T) { + err := &testErrorWithDetails{} + expected := `{ + "errors": [ + { + "message": "something went wrong", + "details": "oh\nso\nwrong" + } + ] +} +` + + validateJSONOutput(t, err, expected) +} + +func TestOutputJSONErrorStructuredASTErr(t *testing.T) { + err := &ast.Error{ + Code: "1", + Message: "error message", + } + expected := `{ + "errors": [ + { + "message": "error message", + "code": "1" + } + ] +} +` + + validateJSONOutput(t, err, expected) +} + +func TestOutputJSONErrorStructuredStorageErr(t *testing.T) { + store := inmem.New() + txn := storage.NewTransactionOrDie(context.Background(), store) + err := store.Write(context.Background(), txn, storage.AddOp, storage.Path{}, map[string]any{"foo": 1}) + expected := `{ + "errors": [ + { + "message": "data write during read transaction", + "code": "storage_invalid_txn_error" + } + ] +} +` + + validateJSONOutput(t, err, expected) +} + +func TestOutputJSONErrorStructuredTopdownErr(t *testing.T) { + mod := ` + package test + import rego.v1 + + p(x) = y if { + y = x[_] + } + + z := p([1, 2, 3]) + ` + + _, err := rego.New( + rego.Module("test.rego", mod), + rego.Query("data.test.z"), + ).Eval(context.Background()) + + expected := `{ + "errors": [ + { + "message": "functions must not produce multiple outputs for same inputs", + "code": "eval_conflict_error", + "location": { + "file": "test.rego", + "row": 5, + "col": 3 + } + } + ] +} +` + + validateJSONOutput(t, err, expected) +} + +func TestOutputJSONErrorStructuredAstErr(t *testing.T) { + _, err := rego.New(rego.Query("count(0)")).Eval(context.Background()) + expected := `{ + "errors": [ + { + "message": "count: invalid argument(s)", + "code": "rego_type_error", + "location": { + "file": "", + "row": 1, + "col": 1 + }, + "details": { + "have": [ + { + "type": "number" + }, + null + ], + "want": { + "args": [ + { + "description": "the set/array/object/string to be counted", + "name": "collection", + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "description": "the count of elements, key/val pairs, or characters, respectively.", + "name": "n", + "type": "number" + } + ] + } + } + } + ] + }` + + validateJSONOutput(t, err, expected) +} + +func TestOutputJSONErrorStructuredAstParseErr(t *testing.T) { + _, err := rego.New( + rego.Module("parse-err.rego", "!!!"), + rego.Query("!!!"), + ).Eval(context.Background()) + + expected := `{ + "errors": [ + { + "message": "illegal ! character", + "code": "rego_parse_error", + "location": { + "file": "parse-err.rego", + "row": 1, + "col": 1 + }, + "details": { + "line": "!!!", + "idx": 0 + } + } + ] +} +` + + validateJSONOutput(t, err, expected) +} + +func TestOutputJSONErrorStructuredASTErrList(t *testing.T) { + c := ast.NewCompiler() + c.Compile(map[string]*ast.Module{ + "error.rego": ast.MustParseModule(` +package test +import rego.v1 + +q if { + bad[reference] +} +`)}) + c.Errors.Sort() + err := c.Errors + + expected := `{ + "errors": [ + { + "message": "var bad is unsafe", + "code": "rego_unsafe_var_error", + "location": { + "file": "", + "row": 6, + "col": 2 + } + }, + { + "message": "var reference is unsafe", + "code": "rego_unsafe_var_error", + "location": { + "file": "", + "row": 6, + "col": 2 + } + } + ] +} +` + + validateJSONOutput(t, err, expected) +} + +func TestOutputJSONErrorStructuredLoaderErrList(t *testing.T) { + files := map[string]string{ + // bundle a + "a/data.json": "{{{", + "b/data.json": "...", + } + + var err error + var tmpPath string + test.WithTempFS(files, func(path string) { + tmpPath = path + _, err = loader.NewFileLoader().All([]string{path}) + }) + + expected := fmt.Sprintf(`{ + "errors": [ + { + "message": "%s/a/data.json: invalid character '{' looking for beginning of object key string" + }, + { + "message": "%s/b/data.json: invalid character '.' looking for beginning of value" + } + ] +} +`, tmpPath, tmpPath) + + validateJSONOutput(t, err, expected) +} + +func TestOutputJSONErrorStructuredRegoErrList(t *testing.T) { + mod := ` +package test +import rego.v1 + +p if { + bad_func1() +} + +q if { + bad_func2() +} +` + _, err := rego.New( + rego.Module("error.rego", mod), + rego.Query("data"), + ).PrepareForEval(context.Background()) + + expected := `{ + "errors": [ + { + "message": "undefined function bad_func1", + "code": "rego_type_error", + "location": { + "file": "error.rego", + "row": 6, + "col": 2 + } + }, + { + "message": "undefined function bad_func2", + "code": "rego_type_error", + "location": { + "file": "error.rego", + "row": 10, + "col": 2 + } + } + ] +} +` + + validateJSONOutput(t, err, expected) +} + +func TestSource(t *testing.T) { + + buf := new(bytes.Buffer) + + err := Source(buf, Output{ + Partial: ®o.PartialQueries{ + Queries: []ast.Body{ + ast.MustParseBody("a = 1; b = 2"), + }, + Support: []*ast.Module{ + ast.MustParseModule(` + package test + p := 1 + `), + }, + }, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + exp := `# Query 1 +a = 1 +b = 2 + +# Module 1 +package test + +p := 1 +` + + if buf.String() != exp { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, buf.String()) + } + +} + +func TestRaw(t *testing.T) { + tests := []struct { + note string + output Output + want string + }{ + { + note: "simple single string", + output: Output{ + Result: []rego.Result{ + { + Expressions: []*rego.ExpressionValue{ + {Value: "Hello world"}, + }, + }, + }, + }, + want: "Hello world\n", + }, + { + note: "table format", + output: Output{ + Result: []rego.Result{ + { + Expressions: []*rego.ExpressionValue{ + {Value: "one"}, + {Value: 1}, + }, + }, + { + Expressions: []*rego.ExpressionValue{ + {Value: "two"}, + {Value: 2}, + }, + }, + }, + }, + want: "one 1\ntwo 2\n", + }, + { + note: "compound values", + output: Output{ + Result: []rego.Result{ + { + Expressions: []*rego.ExpressionValue{ + {Value: []any{"one"}}, + {Value: map[string]any{ + "key": []any{}, + }}, + }, + }, + }, + }, + want: "[\"one\"] {\"key\":[]}\n", + }, + { + note: "error", + output: Output{ + Errors: NewOutputErrors(errors.New("boom")), + }, + want: "1 error occurred: boom\n", + }, + { + // NOTE(sr): The presentation package outputs whatever Error() on + // the errors it is given yields. So even though NewOutputErrors + // will pick up the error details, they won't be output, as they + // are not included in the error's Error() string. + note: "error with details", + output: Output{ + Errors: NewOutputErrors(&testErrorWithDetails{}), + }, + want: "1 error occurred: something went wrong\noh\nso\nwrong\n", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + buf := new(bytes.Buffer) + err := Raw(buf, tc.output) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + if buf.String() != tc.want { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", tc.want, buf.String()) + } + }) + } +} + +func TestDepsAnalysisPrettyOutput(t *testing.T) { + tests := []struct { + note string + output DepAnalysisOutput + want []string + }{ + { + note: "base document", + output: DepAnalysisOutput{Base: []ast.Ref{ast.InputRootRef}}, + want: []string{ + "+----------------+", + "| BASE DOCUMENTS |", + "+----------------+", + "| input |", + "+----------------+", + }, + }, + { + note: "virtual document", + output: DepAnalysisOutput{ + Virtual: []ast.Ref{[]*ast.Term{ + ast.VarTerm("data"), ast.StringTerm("policy"), ast.StringTerm("allow")}, + }, + }, + want: []string{ + "+-------------------+", + "| VIRTUAL DOCUMENTS |", + "+-------------------+", + "| data.policy.allow |", + "+-------------------+", + }, + }, + { + note: "base document and virtual document", + output: DepAnalysisOutput{ + Base: []ast.Ref{ast.InputRootRef}, + Virtual: []ast.Ref{[]*ast.Term{ + ast.VarTerm("data"), ast.StringTerm("policy"), ast.StringTerm("allow")}, + }, + }, + want: []string{ + "+----------------+-------------------+", + "| BASE DOCUMENTS | VIRTUAL DOCUMENTS |", + "+----------------+-------------------+", + "| input | data.policy.allow |", + "+----------------+-------------------+", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + buf := new(bytes.Buffer) + if err := tc.output.Pretty(buf); err != nil { + t.Fatalf("unexpected error %v", err) + } + expected := strings.Join(tc.want, "\n") + "\n" + if buf.String() != expected { + t.Errorf("expected %v, got %v", expected, buf.String()) + } + }) + } +} diff --git a/third_party/opa/internal/prometheus/prometheus.go b/third_party/opa/internal/prometheus/prometheus.go new file mode 100644 index 000000000000..9f75e7ba6718 --- /dev/null +++ b/third_party/opa/internal/prometheus/prometheus.go @@ -0,0 +1,223 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package prometheus + +import ( + "bufio" + "encoding/json" + "fmt" + "math" + "net" + "net/http" + "runtime" + "strconv" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + + "github.com/open-policy-agent/opa/v1/metrics" +) + +// Provider wraps a metrics.Metrics provider with a Prometheus registry that can +// instrument the HTTP server's handlers. +type Provider struct { + registry *prometheus.Registry + durationHistogram *prometheus.HistogramVec + cancellationCounters *prometheus.CounterVec + inner metrics.Metrics + logger loggerFunc +} + +type loggerFunc func(attrs map[string]any, f string, a ...any) + +// New returns a new Provider object. +func New(inner metrics.Metrics, logger loggerFunc, httpRequestBuckets []float64) *Provider { + registry := prometheus.NewRegistry() + registry.MustRegister(collector()) + durationHistogram := prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "http_request_duration_seconds", + Help: "A histogram of duration for requests.", + Buckets: httpRequestBuckets, + }, + []string{"code", "handler", "method"}, + ) + registry.MustRegister(durationHistogram) + + cancellationCounters := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "http_request_cancellations", + Help: "A count of cancelled requests.", + }, + []string{"code", "handler", "method"}, + ) + + registry.MustRegister(cancellationCounters) + return &Provider{ + registry: registry, + durationHistogram: durationHistogram, + cancellationCounters: cancellationCounters, + inner: inner, + logger: logger, + } +} + +// RegisterEndpoints registers `/metrics` endpoint +func (p *Provider) RegisterEndpoints(registrar func(path, method string, handler http.Handler)) { + registrar("/metrics/alloc_bytes", http.MethodGet, http.HandlerFunc(allocHandler)) + registrar("/metrics", http.MethodGet, promhttp.HandlerFor(p.registry, promhttp.HandlerOpts{})) +} + +// InstrumentHandler returned wrapped HTTP handler with added prometheus instrumentation +func (p *Provider) InstrumentHandler(handler http.Handler, label string) http.Handler { + durationCollector := p.durationHistogram.MustCurryWith(prometheus.Labels{"handler": label}) + cancellationsCollector := p.cancellationCounters.MustCurryWith(prometheus.Labels{"handler": label}) + return promhttp.InstrumentHandlerDuration(durationCollector, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + csrw := &captureStatusResponseWriter{ResponseWriter: w, status: http.StatusOK} + var rw http.ResponseWriter + if h, ok := w.(http.Hijacker); ok { + rw = &hijacker{ResponseWriter: csrw, hijacker: h} + } else { + rw = csrw + } + handler.ServeHTTP(rw, r) + if r.Context().Err() != nil { + cancellationsCollector.With(prometheus.Labels{"code": strconv.Itoa(csrw.status), "method": r.Method}).Inc() + } + })) +} + +// Info returns attributes that describe the metric provider. +func (*Provider) Info() metrics.Info { + return metrics.Info{ + Name: "prometheus", + } +} + +// All returns the union of the inner metric provider and the underlying +// prometheus registry. +func (p *Provider) All() map[string]any { + + all := p.inner.All() + + families, err := p.registry.Gather() + if err != nil && p.logger != nil { + p.logger(map[string]any{ + "err": err, + }, "Failed to gather metrics from Prometheus registry.") + } + + for _, f := range families { + all[f.GetName()] = wrap{family: f} + } + + return all +} + +type wrap struct{ family proto.Message } + +func (w wrap) MarshalJSON() ([]byte, error) { + return protojson.Marshal(w.family) +} + +// MarshalJSON returns a JSON representation of the unioned metrics. +func (p *Provider) MarshalJSON() ([]byte, error) { + return json.Marshal(p.All()) +} + +// Timer returns a named timer. +func (p *Provider) Timer(name string) metrics.Timer { + return p.inner.Timer(name) +} + +// Counter returns a named counter. +func (p *Provider) Counter(name string) metrics.Counter { + return p.inner.Counter(name) +} + +// Histogram returns a named histogram. +func (p *Provider) Histogram(name string) metrics.Histogram { + return p.inner.Histogram(name) +} + +// Clear resets the inner metric provider. The Prometheus registry does not +// expose an interface to clear the metrics so this call has no affect on +// metrics tracked by Prometheus. +func (p *Provider) Clear() { + p.inner.Clear() +} + +// Register register the collectors on OPA prometheus registry +func (p *Provider) Register(c prometheus.Collector) error { + return p.registry.Register(c) +} + +// MustRegister register the collectors on OPA prometheus registry and panics when an error occurs +func (p *Provider) MustRegister(cs ...prometheus.Collector) { + p.registry.MustRegister(cs...) +} + +// Unregister unregister the collectors on OPA prometheus registry +func (p *Provider) Unregister(c prometheus.Collector) bool { + return p.registry.Unregister(c) +} + +type captureStatusResponseWriter struct { + http.ResponseWriter + status int +} + +type hijacker struct { + http.ResponseWriter + hijacker http.Hijacker +} + +func (h *hijacker) Hijack() (net.Conn, *bufio.ReadWriter, error) { + return h.hijacker.Hijack() +} + +func (c *captureStatusResponseWriter) WriteHeader(statusCode int) { + c.ResponseWriter.WriteHeader(statusCode) + c.status = statusCode +} + +var _ http.Flusher = (*captureStatusResponseWriter)(nil) + +func (c *captureStatusResponseWriter) Flush() { + if h, ok := c.ResponseWriter.(http.Flusher); ok { + h.Flush() + } +} + +func prettyByteSize(b uint64) string { + bf := float64(b) + for _, unit := range []string{"", "K", "M", "G", "T", "P", "E", "Z"} { + if math.Abs(bf) < 1000.0 { + return fmt.Sprintf("%3.1f%sB", bf, unit) + } + bf /= 1000.0 + } + return fmt.Sprintf("%.1fYiB", bf) +} + +func allocHandler(rsp http.ResponseWriter, req *http.Request) { + var m runtime.MemStats + runtime.ReadMemStats(&m) + + total := m.HeapInuse + m.StackInuse + m.MCacheInuse + m.MSpanInuse + + var alloc string + if req.URL.RawQuery != "" && req.URL.Query().Get("pretty") == "true" { + alloc = prettyByteSize(total) + } else { + alloc = strconv.FormatUint(total, 10) + } + + rsp.WriteHeader(200) + _, _ = fmt.Fprintln(rsp, alloc) +} diff --git a/third_party/opa/internal/prometheus/prometheus_go1.17.go b/third_party/opa/internal/prometheus/prometheus_go1.17.go new file mode 100644 index 000000000000..2bc0bea91b31 --- /dev/null +++ b/third_party/opa/internal/prometheus/prometheus_go1.17.go @@ -0,0 +1,18 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package prometheus + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" +) + +func collector() prometheus.Collector { + return collectors.NewGoCollector( + collectors.WithGoCollectorRuntimeMetrics( + collectors.MetricsAll, + ), + ) +} diff --git a/third_party/opa/internal/prometheus/prometheus_test.go b/third_party/opa/internal/prometheus/prometheus_test.go new file mode 100644 index 000000000000..7d1b92908a2b --- /dev/null +++ b/third_party/opa/internal/prometheus/prometheus_test.go @@ -0,0 +1,204 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. +// +// NOTE: Different go runtime metrics in pretty much +// every Go version. Let's only test these on latest. +//go:build go1.24 + +package prometheus + +import ( + "encoding/json" + "testing" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" +) + +func TestJSONSerialization(t *testing.T) { + inner := metrics.New() + logger := func(logger logging.Logger) loggerFunc { + return func(attrs map[string]any, f string, a ...any) { + logger.WithFields(attrs).Error(f, a...) + } + }(logging.NewNoOpLogger()) + + prom := New(inner, logger, []float64{1e-6, 5e-6, 1e-5, 5e-5, 1e-4, 5e-4, 1e-3, 0.01, 0.1, 1}) + + m := prom.All() + bs, err := json.Marshal(m) + if err != nil { + t.Fatal(err) + } + + act := make(map[string]map[string]any, len(m)) + err = json.Unmarshal(bs, &act) + if err != nil { + t.Fatal(err) + } + + // NOTE(sr): "http_request_duration_seconds" only shows up after there has been a request + exp := map[string][]string{ + "GAUGE": { + "go_gc_heap_goal_bytes", + "go_gc_heap_objects_objects", + "go_gc_stack_starting_size_bytes", + "go_gc_limiter_last_enabled_gc_cycle", + "go_goroutines", + "go_info", + "go_memory_classes_heap_free_bytes", + "go_memory_classes_heap_objects_bytes", + "go_memory_classes_heap_released_bytes", + "go_memory_classes_heap_stacks_bytes", + "go_memory_classes_heap_unused_bytes", + "go_memory_classes_metadata_mcache_free_bytes", + "go_memory_classes_metadata_mcache_inuse_bytes", + "go_memory_classes_metadata_mspan_free_bytes", + "go_memory_classes_metadata_mspan_inuse_bytes", + "go_memory_classes_metadata_other_bytes", + "go_memory_classes_os_stacks_bytes", + "go_memory_classes_other_bytes", + "go_memory_classes_profiling_buckets_bytes", + "go_memory_classes_total_bytes", + "go_memstats_alloc_bytes", + "go_memstats_buck_hash_sys_bytes", + // "go_memstats_gc_cpu_fraction", // removed: https://github.com/prometheus/client_golang/issues/842#issuecomment-861812034 + "go_memstats_gc_sys_bytes", + "go_memstats_heap_alloc_bytes", + "go_memstats_heap_idle_bytes", + "go_memstats_heap_inuse_bytes", + "go_memstats_heap_objects", + "go_memstats_heap_released_bytes", + "go_memstats_heap_sys_bytes", + "go_memstats_last_gc_time_seconds", + "go_memstats_mcache_inuse_bytes", + "go_memstats_mcache_sys_bytes", + "go_memstats_mspan_inuse_bytes", + "go_memstats_mspan_sys_bytes", + "go_memstats_next_gc_bytes", + "go_memstats_other_sys_bytes", + "go_memstats_stack_inuse_bytes", + "go_memstats_stack_sys_bytes", + "go_memstats_sys_bytes", + "go_sched_goroutines_goroutines", + "go_sched_gomaxprocs_threads", + "go_threads", + "go_gc_gomemlimit_bytes", // BEGIN added in 1.21 + "go_gc_heap_live_bytes", + "go_gc_gogc_percent", + "go_gc_scan_globals_bytes", + "go_gc_scan_heap_bytes", + "go_gc_scan_stack_bytes", + "go_gc_scan_total_bytes", + }, + "COUNTER": { + "go_gc_cycles_automatic_gc_cycles_total", + "go_gc_cycles_forced_gc_cycles_total", + "go_gc_cycles_total_gc_cycles_total", + "go_gc_heap_allocs_bytes_total", + "go_gc_heap_allocs_objects_total", + "go_gc_heap_tiny_allocs_objects_total", + "go_gc_heap_frees_bytes_total", + "go_gc_heap_frees_objects_total", + "go_cgo_go_to_c_calls_calls_total", + "go_memstats_alloc_bytes_total", + "go_memstats_mallocs_total", + "go_memstats_frees_total", + "go_cpu_classes_idle_cpu_seconds_total", + "go_cpu_classes_gc_mark_dedicated_cpu_seconds_total", + "go_cpu_classes_scavenge_background_cpu_seconds_total", + "go_cpu_classes_user_cpu_seconds_total", + "go_cpu_classes_scavenge_assist_cpu_seconds_total", + "go_cpu_classes_gc_mark_idle_cpu_seconds_total", + "go_cpu_classes_scavenge_total_cpu_seconds_total", + "go_cpu_classes_gc_mark_assist_cpu_seconds_total", + "go_cpu_classes_total_cpu_seconds_total", + "go_cpu_classes_gc_total_cpu_seconds_total", + "go_sync_mutex_wait_total_seconds_total", + "go_cpu_classes_gc_pause_cpu_seconds_total", + "go_godebug_non_default_behavior_execerrdot_events_total", // BEGIN added in 1.21 + "go_godebug_non_default_behavior_gocachehash_events_total", + "go_godebug_non_default_behavior_gocachetest_events_total", + "go_godebug_non_default_behavior_gocacheverify_events_total", + "go_godebug_non_default_behavior_http2client_events_total", + "go_godebug_non_default_behavior_http2server_events_total", + "go_godebug_non_default_behavior_installgoroot_events_total", + // "go_godebug_non_default_behavior_jstmpllitinterp_events_total", // this one was removed in 1.23 + "go_godebug_non_default_behavior_panicnil_events_total", + "go_godebug_non_default_behavior_randautoseed_events_total", + "go_godebug_non_default_behavior_tarinsecurepath_events_total", + "go_godebug_non_default_behavior_multipartmaxheaders_events_total", + "go_godebug_non_default_behavior_multipartmaxparts_events_total", + "go_godebug_non_default_behavior_multipathtcp_events_total", + // "go_godebug_non_default_behavior_x509sha1_events_total", // removed in 1.24 + "go_godebug_non_default_behavior_x509usefallbackroots_events_total", + "go_godebug_non_default_behavior_zipinsecurepath_events_total", + "go_godebug_non_default_behavior_tlsmaxrsasize_events_total", + "go_godebug_non_default_behavior_gotypesalias_events_total", // BEGIN added in 1.22 + "go_godebug_non_default_behavior_tlsunsafeekm_events_total", + "go_godebug_non_default_behavior_httplaxcontentlength_events_total", + "go_godebug_non_default_behavior_x509usepolicies_events_total", + "go_godebug_non_default_behavior_tls10server_events_total", + "go_godebug_non_default_behavior_httpmuxgo121_events_total", + "go_godebug_non_default_behavior_tlsrsakex_events_total", + "go_godebug_non_default_behavior_netedns0_events_total", // added in 1.22.5 + "go_godebug_non_default_behavior_x509negativeserial_events_total", // added in 1.23.1 (or 1.23) + "go_godebug_non_default_behavior_winsymlink_events_total", + "go_godebug_non_default_behavior_x509keypairleaf_events_total", + "go_godebug_non_default_behavior_winreadlinkvolume_events_total", + "go_godebug_non_default_behavior_asynctimerchan_events_total", + "go_godebug_non_default_behavior_httpservecontentkeepheaders_events_total", + "go_godebug_non_default_behavior_tls3des_events_total", + + "go_godebug_non_default_behavior_randseednop_events_total", + "go_godebug_non_default_behavior_x509rsacrt_events_total", + "go_godebug_non_default_behavior_gotestjsonbuildtext_events_total", + "go_godebug_non_default_behavior_rsa1024min_events_total", + }, + "SUMMARY": { + "go_gc_duration_seconds", + }, + "HISTOGRAM": { + "go_gc_pauses_seconds", // was: "go_gc_pauses_seconds_total" + "go_gc_heap_allocs_by_size_bytes", // was: "go_gc_heap_allocs_by_size_bytes_total" + "go_gc_heap_frees_by_size_bytes", // was: "go_gc_heap_frees_by_size_bytes_total" + "go_sched_latencies_seconds", + "go_sched_pauses_stopping_other_seconds", // BEGIN added in 1.22 + "go_sched_pauses_stopping_gc_seconds", + "go_sched_pauses_total_gc_seconds", + "go_sched_pauses_total_other_seconds", + }, + } + found := 0 + for typ, es := range exp { + for _, e := range es { + a, ok := act[e] + if !ok { + t.Errorf("%v: metric missing", e) + continue + } + if act, ok := a["type"].(string); !ok || act != typ { + t.Errorf("%v: unexpected type: %v (expected %v)", e, act, typ) + continue + } + found++ + } + } + if len(act) != found { + t.Errorf("unexpected extra metrics, expected %d, got %d", found, len(act)) + for a, ty := range act { + found := false + for _, es := range exp { + for _, e := range es { + if a == e { + found = true + } + } + } + if !found { + t.Errorf("unexpected metric: %v (type: %v)", a, ty) + } + } + } +} diff --git a/third_party/opa/internal/providers/aws/NOTICE.txt b/third_party/opa/internal/providers/aws/NOTICE.txt new file mode 100644 index 000000000000..5f14d1162ed4 --- /dev/null +++ b/third_party/opa/internal/providers/aws/NOTICE.txt @@ -0,0 +1,3 @@ +AWS SDK for Go +Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Copyright 2014-2015 Stripe, Inc. diff --git a/third_party/opa/internal/providers/aws/crypto/compare.go b/third_party/opa/internal/providers/aws/crypto/compare.go new file mode 100644 index 000000000000..e2514423b79a --- /dev/null +++ b/third_party/opa/internal/providers/aws/crypto/compare.go @@ -0,0 +1,30 @@ +package crypto + +import "errors" + +// ConstantTimeByteCompare is a constant-time byte comparison of x and y. This function performs an absolute comparison +// if the two byte slices assuming they represent a big-endian number. +// +// error if len(x) != len(y) +// -1 if x < y +// 0 if x == y +// +1 if x > y +func ConstantTimeByteCompare(x, y []byte) (int, error) { + if len(x) != len(y) { + return 0, errors.New("slice lengths do not match") + } + + xLarger, yLarger := 0, 0 + + for i := range x { + xByte, yByte := int(x[i]), int(y[i]) + + x := ((yByte - xByte) >> 8) & 1 + y := ((xByte - yByte) >> 8) & 1 + + xLarger |= x &^ yLarger + yLarger |= y &^ xLarger + } + + return xLarger - yLarger, nil +} diff --git a/third_party/opa/internal/providers/aws/crypto/compare_test.go b/third_party/opa/internal/providers/aws/crypto/compare_test.go new file mode 100644 index 000000000000..98c34eafbf45 --- /dev/null +++ b/third_party/opa/internal/providers/aws/crypto/compare_test.go @@ -0,0 +1,52 @@ +package crypto + +import ( + "bytes" + "math/big" + "testing" +) + +func TestConstantTimeByteCompare(t *testing.T) { + cases := []struct { + x, y []byte + r int + expectErr bool + }{ + {x: []byte{}, y: []byte{}, r: 0}, + {x: []byte{40}, y: []byte{30}, r: 1}, + {x: []byte{30}, y: []byte{40}, r: -1}, + {x: []byte{60, 40, 30, 10, 20}, y: []byte{50, 30, 20, 0, 10}, r: 1}, + {x: []byte{50, 30, 20, 0, 10}, y: []byte{60, 40, 30, 10, 20}, r: -1}, + {x: nil, y: []byte{}, r: 0}, + {x: []byte{}, y: nil, r: 0}, + {x: []byte{}, y: []byte{10}, expectErr: true}, + {x: []byte{10}, y: []byte{}, expectErr: true}, + {x: []byte{10, 20}, y: []byte{10}, expectErr: true}, + } + + for _, tt := range cases { + compare, err := ConstantTimeByteCompare(tt.x, tt.y) + if (err != nil) != tt.expectErr { + t.Fatalf("expectErr=%v, got %v", tt.expectErr, err) + } + if e, a := tt.r, compare; e != a { + t.Errorf("expect %v, got %v", e, a) + } + } +} + +func BenchmarkConstantTimeCompare(b *testing.B) { + x, y := big.NewInt(1023), big.NewInt(1024) + b.ResetTimer() + for range b.N { + _, _ = ConstantTimeByteCompare(x.Bytes(), y.Bytes()) + } +} + +func BenchmarkCompare(b *testing.B) { + x, y := big.NewInt(1023).Bytes(), big.NewInt(1024).Bytes() + b.ResetTimer() + for range b.N { + bytes.Compare(x, y) + } +} diff --git a/third_party/opa/internal/providers/aws/crypto/ecc.go b/third_party/opa/internal/providers/aws/crypto/ecc.go new file mode 100644 index 000000000000..12679a15be3b --- /dev/null +++ b/third_party/opa/internal/providers/aws/crypto/ecc.go @@ -0,0 +1,114 @@ +package crypto + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/hmac" + "encoding/asn1" + "encoding/binary" + "errors" + "fmt" + "hash" + "math" + "math/big" +) + +type ecdsaSignature struct { + R, S *big.Int +} + +// ECDSAKey takes the given elliptic curve, and private key (d) byte slice +// and returns the private ECDSA key. +func ECDSAKey(curve elliptic.Curve, d []byte) *ecdsa.PrivateKey { + return ECDSAKeyFromPoint(curve, (&big.Int{}).SetBytes(d)) +} + +// ECDSAKeyFromPoint takes the given elliptic curve and point and returns the +// private and public keypair +func ECDSAKeyFromPoint(curve elliptic.Curve, d *big.Int) *ecdsa.PrivateKey { + pX, pY := curve.ScalarBaseMult(d.Bytes()) + + privKey := &ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: curve, + X: pX, + Y: pY, + }, + D: d, + } + + return privKey +} + +// ECDSAPublicKey takes the provide curve and (x, y) coordinates and returns +// *ecdsa.PublicKey. Returns an error if the given points are not on the curve. +func ECDSAPublicKey(curve elliptic.Curve, x, y []byte) (*ecdsa.PublicKey, error) { + xPoint := (&big.Int{}).SetBytes(x) + yPoint := (&big.Int{}).SetBytes(y) + + if !curve.IsOnCurve(xPoint, yPoint) { + return nil, fmt.Errorf("point(%v, %v) is not on the given curve", xPoint.String(), yPoint.String()) + } + + return &ecdsa.PublicKey{ + Curve: curve, + X: xPoint, + Y: yPoint, + }, nil +} + +// VerifySignature takes the provided public key, hash, and asn1 encoded signature and returns +// whether the given signature is valid. +func VerifySignature(key *ecdsa.PublicKey, hash []byte, signature []byte) (bool, error) { + var ecdsaSignature ecdsaSignature + + _, err := asn1.Unmarshal(signature, &ecdsaSignature) + if err != nil { + return false, err + } + + return ecdsa.Verify(key, hash, ecdsaSignature.R, ecdsaSignature.S), nil +} + +// HMACKeyDerivation provides an implementation of a NIST-800-108 of a KDF (Key Derivation Function) in Counter Mode. +// For the purposes of this implantation HMAC is used as the PRF (Pseudorandom function), where the value of +// `r` is defined as a 4 byte counter. +func HMACKeyDerivation(hash func() hash.Hash, bitLen int, key []byte, label, context []byte) ([]byte, error) { + // verify that we won't overflow the counter + n := int64(math.Ceil((float64(bitLen) / 8) / float64(hash().Size()))) + if n > 0x7FFFFFFF { + return nil, fmt.Errorf("unable to derive key of size %d using 32-bit counter", bitLen) + } + + // verify the requested bit length is not larger then the length encoding size + if int64(bitLen) > 0x7FFFFFFF { + return nil, errors.New("bitLen is greater than 32-bits") + } + + fixedInput := bytes.NewBuffer(nil) + fixedInput.Write(label) + fixedInput.WriteByte(0x00) + fixedInput.Write(context) + if err := binary.Write(fixedInput, binary.BigEndian, int32(bitLen)); err != nil { + return nil, fmt.Errorf("failed to write bit length to fixed input string: %v", err) + } + + var output []byte + + h := hmac.New(hash, key) + + for i := int64(1); i <= n; i++ { + h.Reset() + if err := binary.Write(h, binary.BigEndian, int32(i)); err != nil { + return nil, err + } + _, err := h.Write(fixedInput.Bytes()) + if err != nil { + return nil, err + } + output = append(output, h.Sum(nil)...) + } + + return output[:bitLen/8], nil +} diff --git a/third_party/opa/internal/providers/aws/crypto/ecc_test.go b/third_party/opa/internal/providers/aws/crypto/ecc_test.go new file mode 100644 index 000000000000..fb3e97165cb4 --- /dev/null +++ b/third_party/opa/internal/providers/aws/crypto/ecc_test.go @@ -0,0 +1,277 @@ +package crypto + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "io" + "testing" +) + +func TestECDSAPublicKeyDerivation_P256(t *testing.T) { + d := []byte{ + 0xc9, 0x80, 0x68, 0x98, 0xa0, 0x33, 0x49, 0x16, 0xc8, 0x60, 0x74, 0x88, 0x80, 0xa5, 0x41, 0xf0, + 0x93, 0xb5, 0x79, 0xa9, 0xb1, 0xf3, 0x29, 0x34, 0xd8, 0x6c, 0x36, 0x3c, 0x39, 0x80, 0x03, 0x57, + } + + x := []byte{ + 0xd0, 0x72, 0x0d, 0xc6, 0x91, 0xaa, 0x80, 0x09, 0x6b, 0xa3, 0x2f, 0xed, 0x1c, 0xb9, 0x7c, 0x2b, + 0x62, 0x06, 0x90, 0xd0, 0x6d, 0xe0, 0x31, 0x7b, 0x86, 0x18, 0xd5, 0xce, 0x65, 0xeb, 0x72, 0x8f, + } + + y := []byte{ + 0x96, 0x81, 0xb5, 0x17, 0xb1, 0xcd, 0xa1, 0x7d, 0x0d, 0x83, 0xd3, 0x35, 0xd9, 0xc4, 0xa8, 0xa9, + 0xa9, 0xb0, 0xb1, 0xb3, 0xc7, 0x10, 0x6d, 0x8f, 0x3c, 0x72, 0xbc, 0x50, 0x93, 0xdc, 0x27, 0x5f, + } + + testKeyDerivation(t, elliptic.P256(), d, x, y) +} + +func TestECDSAPublicKeyDerivation_P384(t *testing.T) { + d := []byte{ + 0x53, 0x94, 0xf7, 0x97, 0x3e, 0xa8, 0x68, 0xc5, 0x2b, 0xf3, 0xff, 0x8d, 0x8c, 0xee, 0xb4, 0xdb, + 0x90, 0xa6, 0x83, 0x65, 0x3b, 0x12, 0x48, 0x5d, 0x5f, 0x62, 0x7c, 0x3c, 0xe5, 0xab, 0xd8, 0x97, + 0x8f, 0xc9, 0x67, 0x3d, 0x14, 0xa7, 0x1d, 0x92, 0x57, 0x47, 0x93, 0x16, 0x62, 0x49, 0x3c, 0x37, + } + + x := []byte{ + 0xfd, 0x3c, 0x84, 0xe5, 0x68, 0x9b, 0xed, 0x27, 0x0e, 0x60, 0x1b, 0x3d, 0x80, 0xf9, 0x0d, 0x67, + 0xa9, 0xae, 0x45, 0x1c, 0xce, 0x89, 0x0f, 0x53, 0xe5, 0x83, 0x22, 0x9a, 0xd0, 0xe2, 0xee, 0x64, + 0x56, 0x11, 0xfa, 0x99, 0x36, 0xdf, 0xa4, 0x53, 0x06, 0xec, 0x18, 0x06, 0x67, 0x74, 0xaa, 0x24, + } + + y := []byte{ + 0xb8, 0x3c, 0xa4, 0x12, 0x6c, 0xfc, 0x4c, 0x4d, 0x1d, 0x18, 0xa4, 0xb6, 0xc2, 0x1c, 0x7f, 0x69, + 0x9d, 0x51, 0x23, 0xdd, 0x9c, 0x24, 0xf6, 0x6f, 0x83, 0x38, 0x46, 0xee, 0xb5, 0x82, 0x96, 0x19, + 0x6b, 0x42, 0xec, 0x06, 0x42, 0x5d, 0xb5, 0xb7, 0x0a, 0x4b, 0x81, 0xb7, 0xfc, 0xf7, 0x05, 0xa0, + } + + testKeyDerivation(t, elliptic.P384(), d, x, y) +} + +func TestECDSAKnownSigningValue_P256(t *testing.T) { + d := []byte{ + 0x51, 0x9b, 0x42, 0x3d, 0x71, 0x5f, 0x8b, 0x58, 0x1f, 0x4f, 0xa8, 0xee, 0x59, 0xf4, 0x77, 0x1a, + 0x5b, 0x44, 0xc8, 0x13, 0x0b, 0x4e, 0x3e, 0xac, 0xca, 0x54, 0xa5, 0x6d, 0xda, 0x72, 0xb4, 0x64, + } + + testKnownSigningValue(t, elliptic.P256(), d) +} + +func TestECDSAKnownSigningValue_P384(t *testing.T) { + d := []byte{ + 0x53, 0x94, 0xf7, 0x97, 0x3e, 0xa8, 0x68, 0xc5, 0x2b, 0xf3, 0xff, 0x8d, 0x8c, 0xee, 0xb4, 0xdb, + 0x90, 0xa6, 0x83, 0x65, 0x3b, 0x12, 0x48, 0x5d, 0x5f, 0x62, 0x7c, 0x3c, 0xe5, 0xab, 0xd8, 0x97, + 0x8f, 0xc9, 0x67, 0x3d, 0x14, 0xa7, 0x1d, 0x92, 0x57, 0x47, 0x93, 0x16, 0x62, 0x49, 0x3c, 0x37, + } + + testKnownSigningValue(t, elliptic.P384(), d) +} + +func testKeyDerivation(t *testing.T, curve elliptic.Curve, d, expectedX, expectedY []byte) { + privKey := ECDSAKey(curve, d) + + if e, a := d, privKey.D.Bytes(); !bytes.Equal(e, a) { + t.Errorf("expected % x, got % x", e, a) + } + + if e, a := expectedX, privKey.X.Bytes(); !bytes.Equal(e, a) { + t.Errorf("expected % x, got % x", e, a) + } + + if e, a := expectedY, privKey.Y.Bytes(); !bytes.Equal(e, a) { + t.Errorf("expected % x, got % x", e, a) + } +} + +func testKnownSigningValue(t *testing.T, curve elliptic.Curve, d []byte) { + signingKey := ECDSAKey(curve, d) + + message := []byte{ + 0x59, 0x05, 0x23, 0x88, 0x77, 0xc7, 0x74, 0x21, 0xf7, 0x3e, 0x43, 0xee, 0x3d, 0xa6, 0xf2, 0xd9, + 0xe2, 0xcc, 0xad, 0x5f, 0xc9, 0x42, 0xdc, 0xec, 0x0c, 0xbd, 0x25, 0x48, 0x29, 0x35, 0xfa, 0xaf, + 0x41, 0x69, 0x83, 0xfe, 0x16, 0x5b, 0x1a, 0x04, 0x5e, 0xe2, 0xbc, 0xd2, 0xe6, 0xdc, 0xa3, 0xbd, + 0xf4, 0x6c, 0x43, 0x10, 0xa7, 0x46, 0x1f, 0x9a, 0x37, 0x96, 0x0c, 0xa6, 0x72, 0xd3, 0xfe, 0xb5, + 0x47, 0x3e, 0x25, 0x36, 0x05, 0xfb, 0x1d, 0xdf, 0xd2, 0x80, 0x65, 0xb5, 0x3c, 0xb5, 0x85, 0x8a, + 0x8a, 0xd2, 0x81, 0x75, 0xbf, 0x9b, 0xd3, 0x86, 0xa5, 0xe4, 0x71, 0xea, 0x7a, 0x65, 0xc1, 0x7c, + 0xc9, 0x34, 0xa9, 0xd7, 0x91, 0xe9, 0x14, 0x91, 0xeb, 0x37, 0x54, 0xd0, 0x37, 0x99, 0x79, 0x0f, + 0xe2, 0xd3, 0x08, 0xd1, 0x61, 0x46, 0xd5, 0xc9, 0xb0, 0xd0, 0xde, 0xbd, 0x97, 0xd7, 0x9c, 0xe8, + } + + sha256Hash := sha256.New() + _, err := io.Copy(sha256Hash, bytes.NewReader(message)) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + msgHash := sha256Hash.Sum(nil) + msgSignature, err := signingKey.Sign(rand.Reader, msgHash, crypto.SHA256) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + verified, err := VerifySignature(&signingKey.PublicKey, msgHash, msgSignature) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if !verified { + t.Fatalf("failed to verify message msgSignature") + } +} + +func TestECDSAInvalidSignature_P256(t *testing.T) { + testInvalidSignature(t, elliptic.P256()) +} + +func TestECDSAInvalidSignature_P384(t *testing.T) { + testInvalidSignature(t, elliptic.P384()) +} + +func TestECDSAGenKeySignature_P256(t *testing.T) { + testGenKeySignature(t, elliptic.P256()) +} + +func TestECDSAGenKeySignature_P384(t *testing.T) { + testGenKeySignature(t, elliptic.P384()) +} + +func testInvalidSignature(t *testing.T, curve elliptic.Curve) { + privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + t.Fatalf("failed to generate key: %v", err) + } + + message := []byte{ + 0x59, 0x05, 0x23, 0x88, 0x77, 0xc7, 0x74, 0x21, 0xf7, 0x3e, 0x43, 0xee, 0x3d, 0xa6, 0xf2, 0xd9, + 0xe2, 0xcc, 0xad, 0x5f, 0xc9, 0x42, 0xdc, 0xec, 0x0c, 0xbd, 0x25, 0x48, 0x29, 0x35, 0xfa, 0xaf, + 0x41, 0x69, 0x83, 0xfe, 0x16, 0x5b, 0x1a, 0x04, 0x5e, 0xe2, 0xbc, 0xd2, 0xe6, 0xdc, 0xa3, 0xbd, + 0xf4, 0x6c, 0x43, 0x10, 0xa7, 0x46, 0x1f, 0x9a, 0x37, 0x96, 0x0c, 0xa6, 0x72, 0xd3, 0xfe, 0xb5, + 0x47, 0x3e, 0x25, 0x36, 0x05, 0xfb, 0x1d, 0xdf, 0xd2, 0x80, 0x65, 0xb5, 0x3c, 0xb5, 0x85, 0x8a, + 0x8a, 0xd2, 0x81, 0x75, 0xbf, 0x9b, 0xd3, 0x86, 0xa5, 0xe4, 0x71, 0xea, 0x7a, 0x65, 0xc1, 0x7c, + 0xc9, 0x34, 0xa9, 0xd7, 0x91, 0xe9, 0x14, 0x91, 0xeb, 0x37, 0x54, 0xd0, 0x37, 0x99, 0x79, 0x0f, + 0xe2, 0xd3, 0x08, 0xd1, 0x61, 0x46, 0xd5, 0xc9, 0xb0, 0xd0, 0xde, 0xbd, 0x97, 0xd7, 0x9c, 0xe8, + } + + sha256Hash := sha256.New() + _, err = io.Copy(sha256Hash, bytes.NewReader(message)) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + msgHash := sha256Hash.Sum(nil) + msgSignature, err := privateKey.Sign(rand.Reader, msgHash, crypto.SHA256) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + byteToFlip := 15 + switch msgSignature[byteToFlip] { + case 0: + msgSignature[byteToFlip] = 0x0a + default: + msgSignature[byteToFlip] &^= msgSignature[byteToFlip] + } + + verified, err := VerifySignature(&privateKey.PublicKey, msgHash, msgSignature) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if verified { + t.Fatalf("expected message verification to fail") + } +} + +func testGenKeySignature(t *testing.T, curve elliptic.Curve) { + privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + t.Fatalf("failed to generate key: %v", err) + } + + message := []byte{ + 0x59, 0x05, 0x23, 0x88, 0x77, 0xc7, 0x74, 0x21, 0xf7, 0x3e, 0x43, 0xee, 0x3d, 0xa6, 0xf2, 0xd9, + 0xe2, 0xcc, 0xad, 0x5f, 0xc9, 0x42, 0xdc, 0xec, 0x0c, 0xbd, 0x25, 0x48, 0x29, 0x35, 0xfa, 0xaf, + 0x41, 0x69, 0x83, 0xfe, 0x16, 0x5b, 0x1a, 0x04, 0x5e, 0xe2, 0xbc, 0xd2, 0xe6, 0xdc, 0xa3, 0xbd, + 0xf4, 0x6c, 0x43, 0x10, 0xa7, 0x46, 0x1f, 0x9a, 0x37, 0x96, 0x0c, 0xa6, 0x72, 0xd3, 0xfe, 0xb5, + 0x47, 0x3e, 0x25, 0x36, 0x05, 0xfb, 0x1d, 0xdf, 0xd2, 0x80, 0x65, 0xb5, 0x3c, 0xb5, 0x85, 0x8a, + 0x8a, 0xd2, 0x81, 0x75, 0xbf, 0x9b, 0xd3, 0x86, 0xa5, 0xe4, 0x71, 0xea, 0x7a, 0x65, 0xc1, 0x7c, + 0xc9, 0x34, 0xa9, 0xd7, 0x91, 0xe9, 0x14, 0x91, 0xeb, 0x37, 0x54, 0xd0, 0x37, 0x99, 0x79, 0x0f, + 0xe2, 0xd3, 0x08, 0xd1, 0x61, 0x46, 0xd5, 0xc9, 0xb0, 0xd0, 0xde, 0xbd, 0x97, 0xd7, 0x9c, 0xe8, + } + + sha256Hash := sha256.New() + _, err = io.Copy(sha256Hash, bytes.NewReader(message)) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + msgHash := sha256Hash.Sum(nil) + msgSignature, err := privateKey.Sign(rand.Reader, msgHash, crypto.SHA256) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + verified, err := VerifySignature(&privateKey.PublicKey, msgHash, msgSignature) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if !verified { + t.Fatalf("expected message verification to fail") + } +} + +func TestECDSASignatureFormat(t *testing.T) { + asn1Signature := []byte{ + 0x30, 0x45, 0x02, 0x21, 0x00, 0xd7, 0xc5, 0xb9, 0x9e, 0x0b, 0xb1, 0x1a, 0x1f, 0x32, 0xda, 0x66, 0xe0, 0xff, + 0x59, 0xb7, 0x8a, 0x5e, 0xb3, 0x94, 0x9c, 0x23, 0xb3, 0xfc, 0x1f, 0x18, 0xcc, 0xf6, 0x61, 0x67, 0x8b, 0xf1, + 0xc1, 0x02, 0x20, 0x26, 0x4d, 0x8b, 0x7c, 0xaa, 0x52, 0x4c, 0xc0, 0x2e, 0x5f, 0xf6, 0x7e, 0x24, 0x82, 0xe5, + 0xfb, 0xcb, 0xc7, 0x9b, 0x83, 0x0d, 0x19, 0x7e, 0x7a, 0x40, 0x37, 0x87, 0xdd, 0x1c, 0x93, 0x13, 0xc4, + } + + x := []byte{ + 0x1c, 0xcb, 0xe9, 0x1c, 0x07, 0x5f, 0xc7, 0xf4, 0xf0, 0x33, 0xbf, 0xa2, 0x48, 0xdb, 0x8f, 0xcc, + 0xd3, 0x56, 0x5d, 0xe9, 0x4b, 0xbf, 0xb1, 0x2f, 0x3c, 0x59, 0xff, 0x46, 0xc2, 0x71, 0xbf, 0x83, + } + + y := []byte{ + 0xce, 0x40, 0x14, 0xc6, 0x88, 0x11, 0xf9, 0xa2, 0x1a, 0x1f, 0xdb, 0x2c, 0x0e, 0x61, 0x13, 0xe0, + 0x6d, 0xb7, 0xca, 0x93, 0xb7, 0x40, 0x4e, 0x78, 0xdc, 0x7c, 0xcd, 0x5c, 0xa8, 0x9a, 0x4c, 0xa9, + } + + publicKey, err := ECDSAPublicKey(elliptic.P256(), x, y) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + message := []byte{ + 0x59, 0x05, 0x23, 0x88, 0x77, 0xc7, 0x74, 0x21, 0xf7, 0x3e, 0x43, 0xee, 0x3d, 0xa6, 0xf2, 0xd9, + 0xe2, 0xcc, 0xad, 0x5f, 0xc9, 0x42, 0xdc, 0xec, 0x0c, 0xbd, 0x25, 0x48, 0x29, 0x35, 0xfa, 0xaf, + 0x41, 0x69, 0x83, 0xfe, 0x16, 0x5b, 0x1a, 0x04, 0x5e, 0xe2, 0xbc, 0xd2, 0xe6, 0xdc, 0xa3, 0xbd, + 0xf4, 0x6c, 0x43, 0x10, 0xa7, 0x46, 0x1f, 0x9a, 0x37, 0x96, 0x0c, 0xa6, 0x72, 0xd3, 0xfe, 0xb5, + 0x47, 0x3e, 0x25, 0x36, 0x05, 0xfb, 0x1d, 0xdf, 0xd2, 0x80, 0x65, 0xb5, 0x3c, 0xb5, 0x85, 0x8a, + 0x8a, 0xd2, 0x81, 0x75, 0xbf, 0x9b, 0xd3, 0x86, 0xa5, 0xe4, 0x71, 0xea, 0x7a, 0x65, 0xc1, 0x7c, + 0xc9, 0x34, 0xa9, 0xd7, 0x91, 0xe9, 0x14, 0x91, 0xeb, 0x37, 0x54, 0xd0, 0x37, 0x99, 0x79, 0x0f, + 0xe2, 0xd3, 0x08, 0xd1, 0x61, 0x46, 0xd5, 0xc9, 0xb0, 0xd0, 0xde, 0xbd, 0x97, 0xd7, 0x9c, 0xe8, + } + + hash := sha256.New() + _, err = io.Copy(hash, bytes.NewReader(message)) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + msgHash := hash.Sum(nil) + + verifySignature, err := VerifySignature(publicKey, msgHash, asn1Signature) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if !verifySignature { + t.Fatalf("failed to verify signature") + } +} diff --git a/third_party/opa/internal/providers/aws/ecr.go b/third_party/opa/internal/providers/aws/ecr.go new file mode 100644 index 000000000000..55e587e9f536 --- /dev/null +++ b/third_party/opa/internal/providers/aws/ecr.go @@ -0,0 +1,148 @@ +package aws + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "math/big" + "net/http" + "strings" + "time" + + "github.com/open-policy-agent/opa/internal/version" + "github.com/open-policy-agent/opa/v1/logging" +) + +// Values taken from +// https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_GetAuthorizationToken.html +const ( + ecrGetAuthorizationTokenTarget = "AmazonEC2ContainerRegistry_V20150921.GetAuthorizationToken" + ecrEndpointFmt = "https://ecr.%s.amazonaws.com/" +) + +// ECR is used to request tokens from Elastic Container Registry. +type ECR struct { + // endpoint returns the region-specifc ECR endpoint. + // It can be overridden by tests. + endpoint func(region string) string + + // client is used to send authorization tokens requests. + client *http.Client + + logger logging.Logger +} + +func NewECR(logger logging.Logger) *ECR { + return &ECR{ + endpoint: func(region string) string { + return fmt.Sprintf(ecrEndpointFmt, region) + }, + client: &http.Client{}, + logger: logger, + } +} + +// GetAuthorizationToken requests a token that can be used to authenticate image pull requests. +func (e *ECR) GetAuthorizationToken(ctx context.Context, creds Credentials, signatureVersion string) (ECRAuthorizationToken, error) { + endpoint := e.endpoint(creds.RegionName) + body := strings.NewReader("{}") + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, body) + if err != nil { + return ECRAuthorizationToken{}, fmt.Errorf("failed to create request: %w", err) + } + + req.Header.Set("X-Amz-Target", ecrGetAuthorizationTokenTarget) + req.Header.Set("Accept-Encoding", "identity") + req.Header.Set("Content-Type", "application/x-amz-json-1.1") + req.Header.Set("User-Agent", version.UserAgent) + + e.logger.Debug("Signing ECR authorization token request") + + if err := SignRequest(req, "ecr", creds, time.Now(), signatureVersion); err != nil { + return ECRAuthorizationToken{}, fmt.Errorf("failed to sign request: %w", err) + } + + resp, err := DoRequestWithClient(req, e.client, "ecr get authorization token", e.logger) + if err != nil { + return ECRAuthorizationToken{}, err + } + + var data struct { + AuthorizationData []struct { + AuthorizationToken string `json:"authorizationToken"` + ExpiresAt json.Number `json:"expiresAt"` + } `json:"authorizationData"` + } + if err := json.Unmarshal(resp, &data); err != nil { + return ECRAuthorizationToken{}, fmt.Errorf("failed to unmarshal response: %w", err) + } + + if len(data.AuthorizationData) < 1 { + return ECRAuthorizationToken{}, errors.New("empty authorization data") + } + + // The GetAuthorizationToken request returns a list of tokens for + // backwards compatibility reasons. We should only ever get one token back + // because we don't define any registryIDs in the request. + // See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_GetAuthorizationToken.html#API_GetAuthorizationToken_ResponseSyntax + resultToken := data.AuthorizationData[0] + + expiresAt, err := parseTimestamp(resultToken.ExpiresAt) + if err != nil { + return ECRAuthorizationToken{}, fmt.Errorf("failed to parse expiresAt: %w", err) + } + + return ECRAuthorizationToken{ + AuthorizationToken: resultToken.AuthorizationToken, + ExpiresAt: expiresAt, + }, nil +} + +// ECRAuthorizationToken can sign requests to AWS ECR. +// +// It corresponds to data returned by the AWS GetAuthorizationToken API. +// See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_AuthorizationData.html +type ECRAuthorizationToken struct { + AuthorizationToken string + ExpiresAt time.Time +} + +// IsValid returns true if the token is set and not expired. +// It respects a margin of error for time handling and will mark it as expired early. +func (t *ECRAuthorizationToken) IsValid() bool { + const tokenExpirationMargin = 5 * time.Minute + + expired := time.Now().Add(tokenExpirationMargin).After(t.ExpiresAt) + return t.AuthorizationToken != "" && !expired +} + +var millisecondsFloat = new(big.Float).SetInt64(1e3) + +// parseTimestamp parses the AWS format for timestamps. +// The time precision is in milliseconds. +// +// The logic is taken from +// https://github.com/aws/aws-sdk-go/blob/41717ba2c04d3fd03f94d09ea984a10899574935/private/protocol/json/jsonutil/unmarshal.go#L294-L302 +func parseTimestamp(raw json.Number) (time.Time, error) { + s := raw.String() + + float, ok := new(big.Float).SetString(s) + if !ok { + return time.Time{}, fmt.Errorf("not a float: %q", raw) + } + + // The float is expected to be in second resolution with millisecond + // decimal places. + // Multiply by millisecondsFloat to obtain an integer in millisecond + // resolution + ms, _ := float.Mul(float, millisecondsFloat).Int64() + + // Multiply again to obtain nanosecond resolution for time.Unix + ns := ms * 1e6 + + t := time.Unix(0, ns).UTC() + + return t, nil +} diff --git a/third_party/opa/internal/providers/aws/ecr_test.go b/third_party/opa/internal/providers/aws/ecr_test.go new file mode 100644 index 000000000000..b80e292bee53 --- /dev/null +++ b/third_party/opa/internal/providers/aws/ecr_test.go @@ -0,0 +1,118 @@ +package aws + +import ( + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/logging" +) + +func TestECR(t *testing.T) { + payload := `{ + "authorizationData": [ + { + "authorizationToken": "secret", + "expiresAt": 1.676258918209E9 + } + ] + }` + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + if _, err := io.WriteString(w, payload); err != nil { + t.Fatalf("io.WriteString(w, payload) = %v", err) + } + })) + defer server.Close() + + logger := logging.New() + logger.SetLevel(logging.Debug) + + ecr := ECR{ + endpoint: func(string) string { return server.URL }, + client: server.Client(), + logger: logger, + } + + creds := Credentials{} + token, err := ecr.GetAuthorizationToken(context.Background(), creds, "v4") + if err != nil { + t.Errorf("ecrServer.getAuthorizationToken = %v", err) + } + + if token.AuthorizationToken != "secret" { + t.Errorf("token.AuthorizationToken = %q, want = %q", token.AuthorizationToken, "secret") + } + + got := token.ExpiresAt + want := time.Date(2023, 02, 13, 03, 28, 38, 209*1000*1000, time.UTC) + if !got.Equal(want) { + t.Errorf("token.ExpiresAt = %v, want = %v", got, want) + } + +} + +func TestParseAWSTimestamp(t *testing.T) { + type testCase struct { + name string + raw json.Number + wantParsed time.Time + wantErr bool + } + + run := func(t *testing.T, tc testCase) { + got, err := parseTimestamp(tc.raw) + if err != nil && tc.wantErr == false { + t.Fatalf("expected no error, got: %s", err) + } + + if err == nil && tc.wantErr { + t.Fatal("expected error") + } + + if !tc.wantParsed.Equal(got) { + t.Fatalf("expected %s, got %s", tc.wantParsed, got) + } + } + + testCases := []testCase{ + { + name: "empty raw value", + wantErr: true, + }, + { + name: "no number", + raw: "no-number", + wantErr: true, + }, + { + name: "float in e notation", + raw: "1.676258918209E9", // actual timestamp returned from the API + wantParsed: time.Date(2023, 02, 13, 03, 28, 38, 209*1000*1000, time.UTC), + }, + { + name: "raw float", + raw: "1676258918.209", + wantParsed: time.Date(2023, 02, 13, 03, 28, 38, 209*1000*1000, time.UTC), + }, + { + name: "cuts down to ms resolution", + raw: "1676258918.209123", + wantParsed: time.Date(2023, 02, 13, 03, 28, 38, 209*1000*1000, time.UTC), + }, + { + name: "integer without ms", + raw: "1676258918", + wantParsed: time.Date(2023, 02, 13, 03, 28, 38, 0, time.UTC), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + run(t, tc) + }) + } +} diff --git a/third_party/opa/internal/providers/aws/kms.go b/third_party/opa/internal/providers/aws/kms.go new file mode 100644 index 000000000000..6dfb06a49616 --- /dev/null +++ b/third_party/opa/internal/providers/aws/kms.go @@ -0,0 +1,106 @@ +package aws + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/open-policy-agent/opa/internal/version" + "github.com/open-policy-agent/opa/v1/logging" +) + +// Values taken from +// https://docs.aws.amazon.com/kms/latest/APIReference/Welcome.html +// https://docs.aws.amazon.com/general/latest/gr/kms.html +const ( + kmsSignTarget = "TrentService.Sign" + kmsEndpointFmt = "https://kms.%s.amazonaws.com/" +) + +// KMS is used to sign payloads using AWS Key Management Service. +type KMS struct { + // endpoint returns the region-specifc KMS endpoint. + // It can be overridden by tests. + endpoint func(region string) string + + // client is used to send authorization tokens requests. + client *http.Client + + logger logging.Logger +} + +func NewKMS(logger logging.Logger) *KMS { + return &KMS{ + endpoint: func(region string) string { + return fmt.Sprintf(kmsEndpointFmt, region) + }, + client: &http.Client{}, + logger: logger, + } +} + +func NewKMSWithURLClient(url string, client *http.Client, logger logging.Logger) *KMS { + return &KMS{ + endpoint: func(string) string { return url }, + client: client, + logger: logger, + } +} + +type KMSSignRequest struct { + KeyID string `json:"KeyId"` + Message string `json:"Message"` + MessageType string `json:"MessageType"` + SigningAlgorithm string `json:"SigningAlgorithm"` +} +type KMSSignResponse struct { + KeyID string `json:"KeyId"` + Signature string `json:"Signature"` + SigningAlgorithm string `json:"SigningAlgorithm"` +} + +// SignDigest signs a digest using KMS. +func (k *KMS) SignDigest(ctx context.Context, digest []byte, keyID string, signingAlgorithm string, creds Credentials, signatureVersion string) (string, error) { + endpoint := k.endpoint(creds.RegionName) + + kmsRequest := KMSSignRequest{ + KeyID: keyID, + Message: base64.StdEncoding.EncodeToString(digest), + MessageType: "DIGEST", + SigningAlgorithm: signingAlgorithm, + } + requestJSONBytes, err := json.Marshal(kmsRequest) + if err != nil { + return "", fmt.Errorf("failed to marshall request: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewBuffer(requestJSONBytes)) + if err != nil { + return "", fmt.Errorf("failed to create request: %w", err) + } + + req.Header.Set("X-Amz-Target", kmsSignTarget) + req.Header.Set("Accept-Encoding", "identity") + req.Header.Set("Content-Type", "application/x-amz-json-1.1") + req.Header.Set("User-Agent", version.UserAgent) + + if err := SignRequest(req, "kms", creds, time.Now(), signatureVersion); err != nil { + return "", fmt.Errorf("failed to sign request: %w", err) + } + + resp, err := DoRequestWithClient(req, k.client, "kms sign digest", k.logger) + if err != nil { + return "", err + } + + var data KMSSignResponse + if err := json.Unmarshal(resp, &data); err != nil { + return "", fmt.Errorf("failed to unmarshal response: %w", err) + } + + return data.Signature, nil +} diff --git a/third_party/opa/internal/providers/aws/kms_test.go b/third_party/opa/internal/providers/aws/kms_test.go new file mode 100644 index 000000000000..5736ac6e422c --- /dev/null +++ b/third_party/opa/internal/providers/aws/kms_test.go @@ -0,0 +1,96 @@ +package aws + +import ( + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/open-policy-agent/opa/v1/logging" +) + +func mockPayload(request KMSSignRequest) string { + responseFmt := `{"KeyId": "%s", "Signature": "%s", "SigningAlgorithm": "%s"}` + return fmt.Sprintf(responseFmt, request.KeyID, request.Message, request.SigningAlgorithm) +} + +func TestKMS_SignDigest(t *testing.T) { + type testCase struct { + name string + request KMSSignRequest + responsePayload string + responseStatus int + wantSignature string + wantErr bool + } + + run := func(t *testing.T, tc testCase) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + if tc.responseStatus != 200 { + w.WriteHeader(tc.responseStatus) + } + if _, err := io.WriteString(w, tc.responsePayload); err != nil { + t.Fatalf("io.WriteString(w, payload) = %v", err) + } + + })) + defer server.Close() + + logger := logging.New() + logger.SetLevel(logging.Debug) + + kms := NewKMSWithURLClient(server.URL, server.Client(), logger) + + creds := Credentials{} + signature, err := kms.SignDigest(context.Background(), []byte(tc.request.Message), tc.request.KeyID, tc.request.SigningAlgorithm, creds, "v4") + if err != nil && tc.wantErr == false { + t.Fatalf("expected no error, got: %s", err) + } + + if err == nil && tc.wantErr { + t.Fatal("expected error") + } + + if err == nil && tc.wantSignature != signature { + t.Fatalf("expected %s, got %s", tc.wantSignature, signature) + } + + } + validRequest1 := KMSSignRequest{ + KeyID: "Keyid1", + Message: "sample", + SigningAlgorithm: "ECDSA_SHA_256", + } + testCases := []testCase{ + { + name: "valid response", + request: validRequest1, + responsePayload: mockPayload(validRequest1), + responseStatus: 200, + wantSignature: validRequest1.Message, + wantErr: false, + }, + { + name: "error response", + request: validRequest1, + responsePayload: "Backend error", + responseStatus: 500, + wantErr: true, + }, + { + name: "valid error response", + request: validRequest1, + responsePayload: `{ "__type" :"SerializationException" }`, + responseStatus: 400, + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + run(t, tc) + }) + } +} diff --git a/third_party/opa/internal/providers/aws/signing_v4.go b/third_party/opa/internal/providers/aws/signing_v4.go new file mode 100644 index 000000000000..07aa568fa2f3 --- /dev/null +++ b/third_party/opa/internal/providers/aws/signing_v4.go @@ -0,0 +1,204 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package aws + +import ( + "bytes" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + v4 "github.com/open-policy-agent/opa/internal/providers/aws/v4" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" +) + +func stringFromTerm(t *ast.Term) string { + if v, ok := t.Value.(ast.String); ok { + return string(v) + } + return "" +} + +// Headers that may be mutated before reaching an aws service (eg by a proxy) should be added here to omit them from +// the sigv4 canonical request +// ref. https://github.com/aws/aws-sdk-go/blob/master/aws/signer/v4/v4.go#L92 +var awsSigv4IgnoredHeaders = map[string]struct{}{ + "authorization": {}, + "user-agent": {}, + "x-amzn-trace-id": {}, +} + +type Credentials struct { + AccessKey string + SecretKey string + RegionName string + SessionToken string +} + +func CredentialsFromObject(v ast.Object) Credentials { + var creds Credentials + awsAccessKey := v.Get(ast.StringTerm("aws_access_key")) + awsSecretKey := v.Get(ast.StringTerm("aws_secret_access_key")) + awsRegion := v.Get(ast.StringTerm("aws_region")) + awsSessionToken := v.Get(ast.StringTerm("aws_session_token")) + + creds.AccessKey = stringFromTerm(awsAccessKey) + creds.SecretKey = stringFromTerm(awsSecretKey) + creds.RegionName = stringFromTerm(awsRegion) + if awsSessionToken != nil { + creds.SessionToken = stringFromTerm(awsSessionToken) + } + return creds +} + +func sha256MAC(message string, key []byte) []byte { + mac := hmac.New(sha256.New, key) + mac.Write([]byte(message)) + return mac.Sum(nil) +} + +// SignRequest modifies an http.Request to include an AWS V4 signature based on the provided credentials. +func SignRequest(req *http.Request, service string, creds Credentials, theTime time.Time, sigVersion string) error { + // General ref. https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html + // S3 ref. https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html + // APIGateway ref. https://docs.aws.amazon.com/apigateway/api-reference/signing-requests/ + + var body []byte + if req.Body == nil { + body = []byte("") + } else { + var err error + body, err = io.ReadAll(req.Body) + if err != nil { + return errors.New("error getting request body: " + err.Error()) + } + // Since ReadAll consumed the body ReadCloser, we must create a new ReadCloser for the request so that the + // subsequent read starts from the beginning + req.Body = io.NopCloser(bytes.NewReader(body)) + } + + now := theTime.UTC() + + if sigVersion == "4a" { + signedHeaders := SignV4a(req.Header, req.Method, req.URL, body, service, creds, now) + req.Header = signedHeaders + } else { + authHeader, awsHeaders := SignV4(req.Header, req.Method, req.URL, body, service, creds, now, false) + req.Header.Set("Authorization", authHeader) + for k, v := range awsHeaders { + req.Header.Add(k, v) + } + } + + return nil +} + +// SignV4 modifies a map[string][]string of headers to generate an AWS V4 signature + headers based on the config/credentials provided. +func SignV4(headers map[string][]string, method string, theURL *url.URL, body []byte, service string, + awsCreds Credentials, theTime time.Time, disablePayloadSigning bool) (string, map[string]string) { + // General ref. https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html + // S3 ref. https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html + // APIGateway ref. https://docs.aws.amazon.com/apigateway/api-reference/signing-requests/ + + now := theTime.UTC() + + contentSha256 := getContentHash(disablePayloadSigning, body) + + // V4 signing has specific ideas of how it wants to see dates/times encoded + dateNow := now.Format("20060102") + iso8601Now := now.Format("20060102T150405Z") + + awsHeaders := map[string]string{ + "host": theURL.Host, + "x-amz-date": iso8601Now, + } + + // s3 and glacier require the extra x-amz-content-sha256 header. other services do not. + if service == "s3" || service == "glacier" { + awsHeaders[amzContentSha256Key] = contentSha256 + } + + // the security token header is necessary for ephemeral credentials, e.g. from + // the EC2 metadata service + if awsCreds.SessionToken != "" { + awsHeaders["x-amz-security-token"] = awsCreds.SessionToken + } + + headersToSign := map[string][]string{} + // sign all of the aws headers. + for k, v := range awsHeaders { + headersToSign[k] = []string{v} + } + + // sign all of the request's headers, except for those in the ignore list + for k, v := range headers { + lowercaseHeader := strings.ToLower(k) + if _, ok := awsSigv4IgnoredHeaders[lowercaseHeader]; !ok { + headersToSign[lowercaseHeader] = v + } + } + + // the "canonical request" is the normalized version of the AWS service access + // that we're attempting to perform + canonicalReq := method + "\n" // HTTP method + canonicalReq += theURL.EscapedPath() + "\n" // URI-escaped path + canonicalReq += theURL.RawQuery + "\n" // RAW Query String + + // include the values for the signed headers + orderedKeys := util.KeysSorted(headersToSign) + for _, k := range orderedKeys { + canonicalReq += k + ":" + strings.Join(headersToSign[k], ",") + "\n" + } + canonicalReq += "\n" // linefeed to terminate headers + + // include the list of the signed headers + headerList := strings.Join(orderedKeys, ";") + canonicalReq += headerList + "\n" + canonicalReq += contentSha256 + + // the "string to sign" is a time-bounded, scoped request token which + // is linked to the "canonical request" by inclusion of its SHA-256 hash + strToSign := "AWS4-HMAC-SHA256\n" // V4 signing with SHA-256 HMAC + strToSign += iso8601Now + "\n" // ISO 8601 time + strToSign += dateNow + "/" + awsCreds.RegionName + "/" + service + "/aws4_request\n" // scoping for signature + strToSign += fmt.Sprintf("%x", sha256.Sum256([]byte(canonicalReq))) // SHA-256 of canonical request + + // the "signing key" is generated by repeated HMAC-SHA256 based on the same + // scoping that's included in the "string to sign"; but including the secret key + // to allow AWS to validate it + signingKey := sha256MAC(dateNow, []byte("AWS4"+awsCreds.SecretKey)) + signingKey = sha256MAC(awsCreds.RegionName, signingKey) + signingKey = sha256MAC(service, signingKey) + signingKey = sha256MAC("aws4_request", signingKey) + + // the "signature" is finally the "string to sign" signed by the "signing key" + signature := sha256MAC(strToSign, signingKey) + + // required format of Authorization header; n.b. the access key corresponding to + // the secret key is included here + authHeader := "AWS4-HMAC-SHA256 Credential=" + awsCreds.AccessKey + "/" + dateNow + authHeader += "/" + awsCreds.RegionName + "/" + service + "/aws4_request," + authHeader += "SignedHeaders=" + headerList + "," + authHeader += "Signature=" + hex.EncodeToString(signature) + + return authHeader, awsHeaders +} + +// getContentHash returns UNSIGNED-PAYLOAD if payload signing is disabled else will compute sha256 from body +func getContentHash(disablePayloadSigning bool, body []byte) string { + if disablePayloadSigning { + return v4.UnsignedPayload + } + return fmt.Sprintf("%x", sha256.Sum256(body)) +} diff --git a/third_party/opa/internal/providers/aws/signing_v4a.go b/third_party/opa/internal/providers/aws/signing_v4a.go new file mode 100644 index 000000000000..8f6d760e82a1 --- /dev/null +++ b/third_party/opa/internal/providers/aws/signing_v4a.go @@ -0,0 +1,410 @@ +// modified from github.com/aws/aws-sdk-go-v2/internal/v4a@7a32d707af +package aws + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "errors" + "hash" + "io" + "math/big" + "net/http" + "net/url" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + signerCrypto "github.com/open-policy-agent/opa/internal/providers/aws/crypto" + v4Internal "github.com/open-policy-agent/opa/internal/providers/aws/v4" +) + +const ( + // AmzRegionSetKey represents the region set header used for sigv4a + AmzRegionSetKey = "X-Amz-Region-Set" + amzSecurityTokenKey = v4Internal.AmzSecurityTokenKey + amzDateKey = v4Internal.AmzDateKey + authorizationHeader = "Authorization" + amzContentSha256Key = "x-amz-content-sha256" + + signingAlgorithm = "AWS4-ECDSA-P256-SHA256" + + timeFormat = "20060102T150405Z" + shortTimeFormat = "20060102" +) + +var ( + p256 elliptic.Curve + nMinusTwoP256 *big.Int + + one = new(big.Int).SetInt64(1) + + cache = credsCache{} + + randomSource = rand.Reader +) + +func init() { + // Ensure the elliptic curve parameters are initialized on package import rather then on first usage + p256 = elliptic.P256() + + nMinusTwoP256 = new(big.Int).SetBytes(p256.Params().N.Bytes()) + nMinusTwoP256 = nMinusTwoP256.Sub(nMinusTwoP256, new(big.Int).SetInt64(2)) +} + +type credsCache struct { + asymmetric atomic.Value + m sync.Mutex +} + +// SetRandomSource used for testing to override rand so tests can expect stable output +func SetRandomSource(reader io.Reader) { + randomSource = reader +} + +// deriveKeyFromAccessKeyPair derives a NIST P-256 PrivateKey from the given +// IAM AccessKey and SecretKey pair. +// +// Based on FIPS.186-4 Appendix B.4.2 +func deriveKeyFromAccessKeyPair(accessKey, secretKey string) (*ecdsa.PrivateKey, error) { + params := p256.Params() + bitLen := params.BitSize // Testing random candidates does not require an additional 64 bits + counter := 0x01 + + buffer := make([]byte, 1+len(accessKey)) // 1 byte counter + len(accessKey) + kdfContext := bytes.NewBuffer(buffer) + + inputKey := append([]byte("AWS4A"), []byte(secretKey)...) + + d := new(big.Int) + for { + kdfContext.Reset() + kdfContext.WriteString(accessKey) + kdfContext.WriteByte(byte(counter)) + + key, err := signerCrypto.HMACKeyDerivation(sha256.New, bitLen, inputKey, []byte(signingAlgorithm), kdfContext.Bytes()) + if err != nil { + return nil, err + } + + // Check key first before calling SetBytes if key is in fact a valid candidate. + // This ensures the byte slice is the correct length (32-bytes) to compare in constant-time + cmp, err := signerCrypto.ConstantTimeByteCompare(key, nMinusTwoP256.Bytes()) + if err != nil { + return nil, err + } + if cmp == -1 { + d.SetBytes(key) + break + } + + counter++ + if counter > 0xFF { + return nil, errors.New("exhausted single byte external counter") + } + } + d = d.Add(d, one) + + priv := new(ecdsa.PrivateKey) + priv.PublicKey.Curve = p256 + priv.D = d + priv.PublicKey.X, priv.PublicKey.Y = p256.ScalarBaseMult(d.Bytes()) + + return priv, nil +} + +// v4aCredentials is Context, ECDSA, and Optional Session Token that can be used +// to sign requests using SigV4a +type v4aCredentials struct { + Context string + PrivateKey *ecdsa.PrivateKey + SessionToken string +} + +// retrievePrivateKey returns credentials suitable for SigV4a signing +func retrievePrivateKey(symmetric Credentials) (v4aCredentials, error) { + cache.m.Lock() + defer cache.m.Unlock() + + // try to get creds from cache + v := cache.asymmetric.Load() + if v != nil { + c := v.(*v4aCredentials) + // if the cached Context matches the symmetric AccessKey ID, then use cached value. Otherwise, creds have + // changed and we need to derive new asymmetric creds + if c != nil && c.Context == symmetric.AccessKey { + return *c, nil + } + } + + privateKey, err := deriveKeyFromAccessKeyPair(symmetric.AccessKey, symmetric.SecretKey) + if err != nil { + return v4aCredentials{}, errors.New("failed to derive asymmetric key from credentials") + } + + creds := v4aCredentials{ + Context: symmetric.AccessKey, + PrivateKey: privateKey, + SessionToken: symmetric.SessionToken, + } + + // cache derived asymmetric creds so we don't derive new ones until symmetric creds change + cache.asymmetric.Store(&creds) + + return creds, nil +} + +type httpSigner struct { + Request *http.Request + ServiceName string + RegionSet []string + Time time.Time + Credentials v4aCredentials + + // PayloadHash is the hex encoded SHA-256 hash of the request payload + // If len(PayloadHash) == 0 the signer will attempt to send the request + // as an unsigned payload. Note: Unsigned payloads only work for a subset of services. + PayloadHash string +} + +func (s *httpSigner) setRequiredSigningFields(headers http.Header, _ url.Values) { + amzDate := s.Time.Format(timeFormat) + + headers.Set(AmzRegionSetKey, strings.Join(s.RegionSet, ",")) + headers.Set(amzDateKey, amzDate) + if len(s.Credentials.SessionToken) > 0 { + headers.Set(amzSecurityTokenKey, s.Credentials.SessionToken) + } +} + +// Build modifies the Request attribute of the httpSigner, adding an Authorization header +func (s *httpSigner) Build() (signedRequest, error) { + req := s.Request + + query := req.URL.Query() + headers := req.Header + + // seemingly required by S3/MRAP -- 403 Forbidden otherwise + headers.Set("host", req.URL.Host) + headers.Set(amzContentSha256Key, s.PayloadHash) + + s.setRequiredSigningFields(headers, query) + + // Sort Each Query Key's Values + for key := range query { + sort.Strings(query[key]) + } + + v4Internal.SanitizeHostForHeader(req) + + credentialScope := s.buildCredentialScope() + credentialStr := s.Credentials.Context + "/" + credentialScope + + unsignedHeaders := headers + + host := req.URL.Host + if len(req.Host) > 0 { + host = req.Host + } + + signedHeaders, signedHeadersStr, canonicalHeaderStr := s.buildCanonicalHeaders(host, v4Internal.IgnoredHeaders, unsignedHeaders, s.Request.ContentLength) + + rawQuery := strings.ReplaceAll(query.Encode(), "+", "%20") + + canonicalURI := v4Internal.GetURIPath(req.URL) + + canonicalString := s.buildCanonicalString( + req.Method, + canonicalURI, + rawQuery, + signedHeadersStr, + canonicalHeaderStr, + ) + + strToSign := s.buildStringToSign(credentialScope, canonicalString) + signingSignature, err := s.buildSignature(strToSign) + if err != nil { + return signedRequest{}, err + } + + headers[authorizationHeader] = append(headers[authorizationHeader][:0], buildAuthorizationHeader(credentialStr, signedHeadersStr, signingSignature)) + + req.URL.RawQuery = rawQuery + + return signedRequest{ + Request: req, + SignedHeaders: signedHeaders, + CanonicalString: canonicalString, + StringToSign: strToSign, + }, nil +} + +func (s *httpSigner) buildCredentialScope() string { + return strings.Join([]string{ + s.Time.Format(shortTimeFormat), + s.ServiceName, + "aws4_request", + }, "/") + +} + +func buildAuthorizationHeader(credentialStr, signedHeadersStr, signingSignature string) string { + const credential = "Credential=" + const signedHeaders = "SignedHeaders=" + const signature = "Signature=" + const commaSpace = ", " + + var parts strings.Builder + parts.Grow(len(signingAlgorithm) + 1 + + len(credential) + len(credentialStr) + len(commaSpace) + + len(signedHeaders) + len(signedHeadersStr) + len(commaSpace) + + len(signature) + len(signingSignature), + ) + parts.WriteString(signingAlgorithm) + parts.WriteRune(' ') + parts.WriteString(credential) + parts.WriteString(credentialStr) + parts.WriteString(commaSpace) + parts.WriteString(signedHeaders) + parts.WriteString(signedHeadersStr) + parts.WriteString(commaSpace) + parts.WriteString(signature) + parts.WriteString(signingSignature) + return parts.String() +} + +func (*httpSigner) buildCanonicalHeaders(host string, rule v4Internal.Rule, header http.Header, length int64) (signed http.Header, signedHeaders, canonicalHeadersStr string) { + signed = make(http.Header) + + const hostHeader = "host" + headers := make([]string, 0) + + if length > 0 { + const contentLengthHeader = "content-length" + headers = append(headers, contentLengthHeader) + signed[contentLengthHeader] = append(signed[contentLengthHeader], strconv.FormatInt(length, 10)) + } + + for k, v := range header { + if !rule.IsValid(k) { + continue // ignored header + } + + lowerCaseKey := strings.ToLower(k) + if _, ok := signed[lowerCaseKey]; ok { + // include additional values + signed[lowerCaseKey] = append(signed[lowerCaseKey], v...) + continue + } + + headers = append(headers, lowerCaseKey) + signed[lowerCaseKey] = v + } + sort.Strings(headers) + + signedHeaders = strings.Join(headers, ";") + + var canonicalHeaders strings.Builder + n := len(headers) + const colon = ':' + for i := range n { + if headers[i] == hostHeader { + canonicalHeaders.WriteString(hostHeader) + canonicalHeaders.WriteRune(colon) + canonicalHeaders.WriteString(v4Internal.StripExcessSpaces(host)) + } else { + canonicalHeaders.WriteString(headers[i]) + canonicalHeaders.WriteRune(colon) + // Trim out leading, trailing, and dedup inner spaces from signed header values. + values := signed[headers[i]] + for j, v := range values { + cleanedValue := strings.TrimSpace(v4Internal.StripExcessSpaces(v)) + canonicalHeaders.WriteString(cleanedValue) + if j < len(values)-1 { + canonicalHeaders.WriteRune(',') + } + } + } + canonicalHeaders.WriteRune('\n') + } + canonicalHeadersStr = canonicalHeaders.String() + + return signed, signedHeaders, canonicalHeadersStr +} + +func (s *httpSigner) buildCanonicalString(method, uri, query, signedHeaders, canonicalHeaders string) string { + return strings.Join([]string{ + method, + uri, + query, + canonicalHeaders, + signedHeaders, + s.PayloadHash, + }, "\n") +} + +func (s *httpSigner) buildStringToSign(credentialScope, canonicalRequestString string) string { + return strings.Join([]string{ + signingAlgorithm, + s.Time.Format(timeFormat), + credentialScope, + hex.EncodeToString(makeHash(sha256.New(), []byte(canonicalRequestString))), + }, "\n") +} + +func makeHash(hash hash.Hash, b []byte) []byte { + hash.Reset() + hash.Write(b) + return hash.Sum(nil) +} + +func (s *httpSigner) buildSignature(strToSign string) (string, error) { + sig, err := s.Credentials.PrivateKey.Sign(randomSource, makeHash(sha256.New(), []byte(strToSign)), crypto.SHA256) + if err != nil { + return "", err + } + return hex.EncodeToString(sig), nil +} + +type signedRequest struct { + Request *http.Request + SignedHeaders http.Header + CanonicalString string + StringToSign string +} + +// SignV4a returns a map[string][]string of headers, including an added AWS V4a signature based on the config/credentials provided. +func SignV4a(headers map[string][]string, method string, theURL *url.URL, body []byte, service string, awsCreds Credentials, theTime time.Time) map[string][]string { + contentSha256 := getContentHash(false, body) + key, err := retrievePrivateKey(awsCreds) + if err != nil { + return map[string][]string{} + } + + bodyReader := bytes.NewReader(body) + req, _ := http.NewRequest(method, theURL.String(), bodyReader) + req.Header = headers + + signer := &httpSigner{ + Request: req, + PayloadHash: contentSha256, + ServiceName: service, + RegionSet: []string{"*"}, + Credentials: key, + Time: theTime, + } + + _, err = signer.Build() + if err != nil { + return map[string][]string{} + } + + return req.Header +} diff --git a/third_party/opa/internal/providers/aws/util.go b/third_party/opa/internal/providers/aws/util.go new file mode 100644 index 000000000000..d43339c96194 --- /dev/null +++ b/third_party/opa/internal/providers/aws/util.go @@ -0,0 +1,39 @@ +package aws + +import ( + "errors" + "io" + "net/http" + + "github.com/open-policy-agent/opa/v1/logging" +) + +// 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) + if err != nil { + // some kind of catastrophe talking to the service + return nil, errors.New(desc + " HTTP request failed: " + err.Error()) + } + defer resp.Body.Close() + + logger.WithFields(map[string]any{ + "url": req.URL.String(), + "status": resp.Status, + "headers": resp.Header, + }).Debug("Received response from " + desc + " service.") + + 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/third_party/opa/internal/providers/aws/v4/const.go b/third_party/opa/internal/providers/aws/v4/const.go new file mode 100644 index 000000000000..89a76e2eaab4 --- /dev/null +++ b/third_party/opa/internal/providers/aws/v4/const.go @@ -0,0 +1,36 @@ +package v4 + +const ( + // EmptyStringSHA256 is the hex encoded sha256 value of an empty string + EmptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` + + // UnsignedPayload indicates that the request payload body is unsigned + UnsignedPayload = "UNSIGNED-PAYLOAD" + + // AmzAlgorithmKey indicates the signing algorithm + AmzAlgorithmKey = "X-Amz-Algorithm" + + // AmzSecurityTokenKey indicates the security token to be used with temporary credentials + AmzSecurityTokenKey = "X-Amz-Security-Token" + + // AmzDateKey is the UTC timestamp for the request in the format YYYYMMDD'T'HHMMSS'Z' + AmzDateKey = "X-Amz-Date" + + // AmzCredentialKey is the access key ID and credential scope + AmzCredentialKey = "X-Amz-Credential" + + // AmzSignedHeadersKey is the set of headers signed for the request + AmzSignedHeadersKey = "X-Amz-SignedHeaders" + + // AmzSignatureKey is the query parameter to store the SigV4 signature + AmzSignatureKey = "X-Amz-Signature" + + // TimeFormat is the time format to be used in the X-Amz-Date header or query parameter + TimeFormat = "20060102T150405Z" + + // ShortTimeFormat is the shorten time format used in the credential scope + ShortTimeFormat = "20060102" + + // ContentSHAKey is the SHA256 of request body + ContentSHAKey = "X-Amz-Content-Sha256" +) diff --git a/third_party/opa/internal/providers/aws/v4/header_rules.go b/third_party/opa/internal/providers/aws/v4/header_rules.go new file mode 100644 index 000000000000..baf6d12cc65f --- /dev/null +++ b/third_party/opa/internal/providers/aws/v4/header_rules.go @@ -0,0 +1,87 @@ +package v4 + +import ( + "strings" +) + +// Rules houses a set of Rule needed for validation of a +// string value +type Rules []Rule + +// Rule interface allows for more flexible rules and just simply +// checks whether or not a value adheres to that Rule +type Rule interface { + IsValid(value string) bool +} + +// IsValid will iterate through all rules and see if any rules +// apply to the value and supports nested rules +func (r Rules) IsValid(value string) bool { + for _, rule := range r { + if rule.IsValid(value) { + return true + } + } + return false +} + +// MapRule generic Rule for maps +type MapRule map[string]struct{} + +// IsValid for the map Rule satisfies whether it exists in the map +func (m MapRule) IsValid(value string) bool { + _, ok := m[value] + return ok +} + +// AllowList is a generic Rule for whitelisting +type AllowList struct { + Rule +} + +// IsValid for AllowList checks if the value is within the AllowList +func (w AllowList) IsValid(value string) bool { + return w.Rule.IsValid(value) +} + +// DenyList is a generic Rule for blacklisting +type DenyList struct { + Rule +} + +// IsValid for AllowList checks if the value is within the AllowList +func (b DenyList) IsValid(value string) bool { + return !b.Rule.IsValid(value) +} + +// Patterns is a list of strings to match against +type Patterns []string + +// FORK: copied from aws-sdk-go-v2/internal/strings +func hasPrefixFold(s, prefix string) bool { + return len(s) >= len(prefix) && strings.EqualFold(s[0:len(prefix)], prefix) +} + +// IsValid for Patterns checks each pattern and returns if a match has +// been found +func (p Patterns) IsValid(value string) bool { + for _, pattern := range p { + if hasPrefixFold(value, pattern) { + return true + } + } + return false +} + +// InclusiveRules rules allow for rules to depend on one another +type InclusiveRules []Rule + +// IsValid will return true if all rules are true +func (r InclusiveRules) IsValid(value string) bool { + for _, rule := range r { + if !rule.IsValid(value) { + return false + } + } + return true +} diff --git a/third_party/opa/internal/providers/aws/v4/headers.go b/third_party/opa/internal/providers/aws/v4/headers.go new file mode 100644 index 000000000000..3487dc3352d3 --- /dev/null +++ b/third_party/opa/internal/providers/aws/v4/headers.go @@ -0,0 +1,67 @@ +package v4 + +// IgnoredHeaders is a list of headers that are ignored during signing +var IgnoredHeaders = Rules{ + DenyList{ + MapRule{ + "Authorization": struct{}{}, + "User-Agent": struct{}{}, + "X-Amzn-Trace-Id": struct{}{}, + }, + }, +} + +// RequiredSignedHeaders is a whitelist for Build canonical headers. +var RequiredSignedHeaders = Rules{ + AllowList{ + MapRule{ + "Cache-Control": struct{}{}, + "Content-Disposition": struct{}{}, + "Content-Encoding": struct{}{}, + "Content-Language": struct{}{}, + "Content-Md5": struct{}{}, + "Content-Type": struct{}{}, + "Expires": struct{}{}, + "If-Match": struct{}{}, + "If-Modified-Since": struct{}{}, + "If-None-Match": struct{}{}, + "If-Unmodified-Since": struct{}{}, + "Range": struct{}{}, + "X-Amz-Acl": struct{}{}, + "X-Amz-Copy-Source": struct{}{}, + "X-Amz-Copy-Source-If-Match": struct{}{}, + "X-Amz-Copy-Source-If-Modified-Since": struct{}{}, + "X-Amz-Copy-Source-If-None-Match": struct{}{}, + "X-Amz-Copy-Source-If-Unmodified-Since": struct{}{}, + "X-Amz-Copy-Source-Range": struct{}{}, + "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": struct{}{}, + "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": struct{}{}, + "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": struct{}{}, + "X-Amz-Grant-Full-control": struct{}{}, + "X-Amz-Grant-Read": struct{}{}, + "X-Amz-Grant-Read-Acp": struct{}{}, + "X-Amz-Grant-Write": struct{}{}, + "X-Amz-Grant-Write-Acp": struct{}{}, + "X-Amz-Metadata-Directive": struct{}{}, + "X-Amz-Mfa": struct{}{}, + "X-Amz-Request-Payer": struct{}{}, + "X-Amz-Server-Side-Encryption": struct{}{}, + "X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": struct{}{}, + "X-Amz-Server-Side-Encryption-Customer-Algorithm": struct{}{}, + "X-Amz-Server-Side-Encryption-Customer-Key": struct{}{}, + "X-Amz-Server-Side-Encryption-Customer-Key-Md5": struct{}{}, + "X-Amz-Storage-Class": struct{}{}, + "X-Amz-Website-Redirect-Location": struct{}{}, + "X-Amz-Content-Sha256": struct{}{}, + "X-Amz-Tagging": struct{}{}, + }, + }, + Patterns{"X-Amz-Meta-"}, +} + +// AllowedQueryHoisting is a whitelist for Build query headers. The boolean value +// represents whether or not it is a pattern. +var AllowedQueryHoisting = InclusiveRules{ + DenyList{RequiredSignedHeaders}, + Patterns{"X-Amz-"}, +} diff --git a/third_party/opa/internal/providers/aws/v4/host.go b/third_party/opa/internal/providers/aws/v4/host.go new file mode 100644 index 000000000000..bf93659a43f3 --- /dev/null +++ b/third_party/opa/internal/providers/aws/v4/host.go @@ -0,0 +1,75 @@ +package v4 + +import ( + "net/http" + "strings" +) + +// SanitizeHostForHeader removes default port from host and updates request.Host +func SanitizeHostForHeader(r *http.Request) { + host := getHost(r) + port := portOnly(host) + if port != "" && isDefaultPort(r.URL.Scheme, port) { + r.Host = stripPort(host) + } +} + +// Returns host from request +func getHost(r *http.Request) string { + if r.Host != "" { + return r.Host + } + + return r.URL.Host +} + +// Hostname returns u.Host, without any port number. +// +// If Host is an IPv6 literal with a port number, Hostname returns the +// IPv6 literal without the square brackets. IPv6 literals may include +// a zone identifier. +// +// Copied from the Go 1.8 standard library (net/url) +func stripPort(hostport string) string { + colon := strings.IndexByte(hostport, ':') + if colon == -1 { + return hostport + } + if i := strings.IndexByte(hostport, ']'); i != -1 { + return strings.TrimPrefix(hostport[:i], "[") + } + return hostport[:colon] +} + +// Port returns the port part of u.Host, without the leading colon. +// If u.Host doesn't contain a port, Port returns an empty string. +// +// Copied from the Go 1.8 standard library (net/url) +func portOnly(hostport string) string { + colon := strings.IndexByte(hostport, ':') + if colon == -1 { + return "" + } + if i := strings.Index(hostport, "]:"); i != -1 { + return hostport[i+len("]:"):] + } + if strings.Contains(hostport, "]") { + return "" + } + return hostport[colon+len(":"):] +} + +// Returns true if the specified URI is using the standard port +// (i.e. port 80 for HTTP URIs or 443 for HTTPS URIs) +func isDefaultPort(scheme, port string) bool { + if port == "" { + return true + } + + lowerCaseScheme := strings.ToLower(scheme) + if (lowerCaseScheme == "http" && port == "80") || (lowerCaseScheme == "https" && port == "443") { + return true + } + + return false +} diff --git a/third_party/opa/internal/providers/aws/v4/util.go b/third_party/opa/internal/providers/aws/v4/util.go new file mode 100644 index 000000000000..c8e7fb1bab0a --- /dev/null +++ b/third_party/opa/internal/providers/aws/v4/util.go @@ -0,0 +1,59 @@ +package v4 + +import ( + "net/url" + "strings" +) + +const doubleSpace = " " + +// StripExcessSpaces will rewrite the passed in slice's string values to not +// contain multiple side-by-side spaces. +func StripExcessSpaces(str string) string { + var j, k, l, m, spaces int + + // Trim leading and trailing spaces + str = strings.Trim(str, " ") + + // Strip multiple spaces. + j = strings.Index(str, doubleSpace) + if j < 0 { + return str + } + + buf := []byte(str) + for k, m, l = j, j, len(buf); k < l; k++ { + if buf[k] == ' ' { + if spaces == 0 { + // First space. + buf[m] = buf[k] + m++ + } + spaces++ + } else { + // End of multiple spaces. + spaces = 0 + buf[m] = buf[k] + m++ + } + } + + return string(buf[:m]) +} + +// GetURIPath returns the escaped URI component from the provided URL +func GetURIPath(u *url.URL) string { + var uri string + + if len(u.Opaque) > 0 { + uri = "/" + strings.Join(strings.Split(u.Opaque, "/")[3:], "/") + } else { + uri = u.EscapedPath() + } + + if len(uri) == 0 { + uri = "/" + } + + return uri +} diff --git a/third_party/opa/internal/providers/aws/v4/util_test.go b/third_party/opa/internal/providers/aws/v4/util_test.go new file mode 100644 index 000000000000..7a07932af038 --- /dev/null +++ b/third_party/opa/internal/providers/aws/v4/util_test.go @@ -0,0 +1,75 @@ +package v4 + +import ( + "testing" +) + +func TestStripExcessHeaders(t *testing.T) { + vals := []string{ + "", + "123", + "1 2 3", + "1 2 3 ", + " 1 2 3", + "1 2 3", + "1 23", + "1 2 3", + "1 2 ", + " 1 2 ", + "12 3", + "12 3 1", + "12 3 1", + "12 3 1abc123", + } + + expected := []string{ + "", + "123", + "1 2 3", + "1 2 3", + "1 2 3", + "1 2 3", + "1 23", + "1 2 3", + "1 2", + "1 2", + "12 3", + "12 3 1", + "12 3 1", + "12 3 1abc123", + } + + for i := range vals { + r := StripExcessSpaces(vals[i]) + if e, a := expected[i], r; e != a { + t.Errorf("%d, expect %v, got %v", i, e, a) + } + } +} + +var stripExcessSpaceCases = []string{ + `AWS4-HMAC-SHA256 Credential=AKIDFAKEIDFAKEID/20160628/us-west-2/s3/aws4_request, SignedHeaders=host;x-amz-date, Signature=1234567890abcdef1234567890abcdef1234567890abcdef`, + `123 321 123 321`, + ` 123 321 123 321 `, + ` 123 321 123 321 `, + "123", + "1 2 3", + " 1 2 3", + "1 2 3", + "1 23", + "1 2 3", + "1 2 ", + " 1 2 ", + "12 3", + "12 3 1", + "12 3 1", + "12 3 1abc123", +} + +func BenchmarkStripExcessSpaces(b *testing.B) { + for range b.N { + for _, v := range stripExcessSpaceCases { + StripExcessSpaces(v) + } + } +} diff --git a/third_party/opa/internal/ref/ref.go b/third_party/opa/internal/ref/ref.go new file mode 100644 index 000000000000..653794b0a909 --- /dev/null +++ b/third_party/opa/internal/ref/ref.go @@ -0,0 +1,36 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package ref implements internal helpers for references +package ref + +import ( + "errors" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" +) + +// ParseDataPath returns a ref from the slash separated path s rooted at data. +// All path segments are treated as identifier strings. +func ParseDataPath(s string) (ast.Ref, error) { + path, ok := storage.ParsePath("/" + strings.TrimPrefix(s, "/")) + if !ok { + return nil, errors.New("invalid path") + } + + return path.Ref(ast.DefaultRootDocument), nil +} + +// ArrayPath will take an ast.Array and build an ast.Ref using the ast.Terms in the Array +func ArrayPath(a *ast.Array) ast.Ref { + ref := make(ast.Ref, 0, a.Len()) + + a.Foreach(func(term *ast.Term) { + ref = append(ref, term) + }) + + return ref +} diff --git a/third_party/opa/internal/rego/opa/engine.go b/third_party/opa/internal/rego/opa/engine.go new file mode 100644 index 000000000000..7defdf788c8d --- /dev/null +++ b/third_party/opa/internal/rego/opa/engine.go @@ -0,0 +1,65 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package opa + +import ( + "context" +) + +// ErrEngineNotFound is returned by LookupEngine if no wasm engine was +// registered by that name. +var ErrEngineNotFound error = &errEngineNotFound{} + +type errEngineNotFound struct{} + +func (*errEngineNotFound) Error() string { return "engine not found" } +func (*errEngineNotFound) Lines() []string { + return []string{ + `WebAssembly runtime not supported in this build.`, + `----------------------------------------------------------------------------------`, + `Please download an OPA binary with Wasm enabled from`, + `https://www.openpolicyagent.org/docs/latest/#running-opa`, + `or build it yourself (with Wasm enabled).`, + `----------------------------------------------------------------------------------`, + } +} + +// Engine repesents a factory for instances of EvalEngine implementations +type Engine interface { + New() EvalEngine +} + +// EvalEngine is the interface implemented by an engine used to eval a policy +type EvalEngine interface { + Init() (EvalEngine, error) + Entrypoints(context.Context) (map[string]int32, error) + WithPolicyBytes([]byte) EvalEngine + WithDataJSON(any) EvalEngine + Eval(context.Context, EvalOpts) (*Result, error) + SetData(context.Context, any) error + SetDataPath(context.Context, []string, any) error + RemoveDataPath(context.Context, []string) error + Close() +} + +var engines = map[string]Engine{} + +// RegisterEngine registers an evaluation engine by its target name. +// Note that the "rego" target is always available. +func RegisterEngine(name string, e Engine) { + if engines[name] != nil { + panic("duplicate engine registration") + } + engines[name] = e +} + +// LookupEngine allows retrieving an engine registered by name +func LookupEngine(name string) (Engine, error) { + e, ok := engines[name] + if !ok { + return nil, ErrEngineNotFound + } + return e, nil +} diff --git a/third_party/opa/internal/rego/opa/options.go b/third_party/opa/internal/rego/opa/options.go new file mode 100644 index 000000000000..97aa41bf0ef8 --- /dev/null +++ b/third_party/opa/internal/rego/opa/options.go @@ -0,0 +1,31 @@ +package opa + +import ( + "io" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/print" +) + +// Result holds the evaluation result. +type Result struct { + Result []byte +} + +// EvalOpts define options for performing an evaluation. +type EvalOpts struct { + Input *any + Metrics metrics.Metrics + Entrypoint int32 + Time time.Time + Seed io.Reader + InterQueryBuiltinCache cache.InterQueryCache + InterQueryBuiltinValueCache cache.InterQueryValueCache + NDBuiltinCache builtins.NDBCache + PrintHook print.Hook + Capabilities *ast.Capabilities +} diff --git a/third_party/opa/internal/report/report.go b/third_party/opa/internal/report/report.go new file mode 100644 index 000000000000..73d3a6e39849 --- /dev/null +++ b/third_party/opa/internal/report/report.go @@ -0,0 +1,216 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package report provides functions to report OPA's version information to an external service and process the response. +package report + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "runtime" + "strconv" + "strings" + "time" + + "github.com/open-policy-agent/opa/internal/semver" + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/version" + + "github.com/open-policy-agent/opa/v1/plugins/rest" + "github.com/open-policy-agent/opa/v1/util" +) + +// ExternalServiceURL is the base HTTP URL for a telemetry service. +// If not otherwise specified, it will use the hard-coded default. +// +// Override at build time via: +// +// -ldflags "-X github.com/open-policy-agent/opa/internal/report.ExternalServiceURL=" +// +// This will be overridden if the OPA_TELEMETRY_SERVICE_URL environment variable +// is provided. +var ExternalServiceURL = "https://api.github.com" + +// Reporter reports information such as the version, heap usage about the running OPA instance to an external service +type Reporter interface { + SendReport(ctx context.Context) (*DataResponse, error) + RegisterGatherer(key string, f Gatherer) +} + +// Gatherer represents a mechanism to inject additional data in the telemetry report +type Gatherer func(ctx context.Context) (any, error) + +// DataResponse represents the data returned by the external service +type DataResponse struct { + Latest ReleaseDetails `json:"latest,omitempty"` +} + +// ReleaseDetails holds information about the latest OPA release +type ReleaseDetails struct { + Download string `json:"download,omitempty"` // link to download the OPA release + ReleaseNotes string `json:"release_notes,omitempty"` // link to the OPA release notes + LatestRelease string `json:"latest_release,omitempty"` // latest OPA released version + OPAUpToDate bool `json:"opa_up_to_date,omitempty"` // is running OPA version greater than or equal to the latest released +} + +// Options supplies parameters to the reporter. +type Options struct { + Logger logging.Logger +} + +type GHVersionCollector struct { + client rest.Client +} + +type GHResponse struct { + TagName string `json:"tag_name,omitempty"` // latest OPA release tag + ReleaseNotes string `json:"html_url,omitempty"` // link to the OPA release notes + Download string `json:"assets_url,omitempty"` // link to download the OPA release +} + +// New returns an instance of the Reporter +func New(opts Options) (Reporter, error) { + r := GHVersionCollector{} + + url := os.Getenv("OPA_TELEMETRY_SERVICE_URL") + if url == "" { + url = ExternalServiceURL + } + + restConfig := fmt.Appendf(nil, `{ + "url": %q, + }`, url) + + client, err := rest.New(restConfig, map[string]*keys.Config{}, rest.Logger(opts.Logger)) + if err != nil { + return nil, err + } + r.client = client + + // heap_usage_bytes is always present, so register it unconditionally + r.RegisterGatherer("heap_usage_bytes", readRuntimeMemStats) + + return &r, nil +} + +// SendReport sends the telemetry report which includes information such as the OPA version, current memory usage to +// the external service +func (r *GHVersionCollector) SendReport(ctx context.Context) (*DataResponse, error) { + rCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + resp, err := r.client.Do(rCtx, "GET", "/repos/open-policy-agent/opa/releases/latest") + if err != nil { + return nil, err + } + + defer util.Close(resp) + + switch resp.StatusCode { + case http.StatusOK: + if resp.Body != nil { + var result GHResponse + err := json.NewDecoder(resp.Body).Decode(&result) + if err != nil { + return nil, err + } + return createDataResponse(result) + } + return nil, nil + default: + return nil, fmt.Errorf("server replied with HTTP %v", resp.StatusCode) + } +} + +func createDataResponse(ghResp GHResponse) (*DataResponse, error) { + if ghResp.TagName == "" { + return nil, errors.New("server response does not contain tag_name") + } + + v := strings.TrimPrefix(version.Version, "v") + sv, err := semver.NewVersion(v) + if err != nil { + return nil, fmt.Errorf("failed to parse current version %q: %w", v, err) + } + + latestV := strings.TrimPrefix(ghResp.TagName, "v") + latestSV, err := semver.NewVersion(latestV) + if err != nil { + return nil, fmt.Errorf("failed to parse latest version %q: %w", latestV, err) + } + + isLatest := sv.Compare(*latestSV) >= 0 + + // Note: alternatively, we could look through the assets in the GH API response to find a matching asset, + // and use its URL. However, this is not guaranteed to be more robust, and wouldn't use the 'openpolicyagent.org' domain. + downloadLink := fmt.Sprintf("https://openpolicyagent.org/downloads/%v/opa_%v_%v", + ghResp.TagName, runtime.GOOS, runtime.GOARCH) + + if runtime.GOARCH == "arm64" { + downloadLink = fmt.Sprintf("%v_static", downloadLink) + } + + if strings.HasPrefix(runtime.GOOS, "win") { + downloadLink = fmt.Sprintf("%v.exe", downloadLink) + } + + return &DataResponse{ + Latest: ReleaseDetails{ + Download: downloadLink, + ReleaseNotes: ghResp.ReleaseNotes, + LatestRelease: ghResp.TagName, + OPAUpToDate: isLatest, + }, + }, nil +} + +func (*GHVersionCollector) RegisterGatherer(_ string, _ Gatherer) { + // no-op for this implementation +} + +// IsSet returns true if dr is populated. +func (dr *DataResponse) IsSet() bool { + return dr != nil && dr.Latest.LatestRelease != "" && dr.Latest.Download != "" && dr.Latest.ReleaseNotes != "" +} + +// Slice returns the dr as a slice of key-value string pairs. If dr is nil, this function returns an empty slice. +func (dr *DataResponse) Slice() [][2]string { + + if !dr.IsSet() { + return nil + } + + return [][2]string{ + {"Latest Upstream Version", strings.TrimPrefix(dr.Latest.LatestRelease, "v")}, + {"Download", dr.Latest.Download}, + {"Release Notes", dr.Latest.ReleaseNotes}, + } +} + +// Pretty returns OPA release information in a human-readable format. +func (dr *DataResponse) Pretty() string { + if !dr.IsSet() { + return "" + } + + pairs := dr.Slice() + lines := make([]string, 0, len(pairs)) + + for _, pair := range pairs { + lines = append(lines, fmt.Sprintf("%v: %v", pair[0], pair[1])) + } + + return strings.Join(lines, "\n") +} + +func readRuntimeMemStats(_ context.Context) (any, error) { + var m runtime.MemStats + runtime.ReadMemStats(&m) + return strconv.FormatUint(m.Alloc, 10), nil +} diff --git a/third_party/opa/internal/report/report_test.go b/third_party/opa/internal/report/report_test.go new file mode 100644 index 000000000000..bf76b88160bb --- /dev/null +++ b/third_party/opa/internal/report/report_test.go @@ -0,0 +1,199 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package report + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "reflect" + "runtime" + "strings" + "testing" +) + +func TestNewReportDefaultURL(t *testing.T) { + + reporter, err := New(Options{}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + actual := reporter.(*GHVersionCollector).client.Config().URL + if actual != ExternalServiceURL { + t.Fatalf("Expected server URL %v but got %v", ExternalServiceURL, actual) + } +} + +func TestSendReportBadRespStatus(t *testing.T) { + + // test server + baseURL, teardown := getTestServer(nil, http.StatusBadRequest) + defer teardown() + + t.Setenv("OPA_TELEMETRY_SERVICE_URL", baseURL) + + reporter, err := New(Options{}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, err = reporter.SendReport(context.Background()) + + if err == nil { + t.Fatal("Expected error but got nil") + } + + expectedErrMsg := "server replied with HTTP 400" + if expectedErrMsg != err.Error() { + t.Fatalf("Expected error: %v but got: %v", expectedErrMsg, err.Error()) + } +} + +func TestSendReportDecodeError(t *testing.T) { + + // test server + baseURL, teardown := getTestServer("foo", http.StatusOK) + defer teardown() + + t.Setenv("OPA_TELEMETRY_SERVICE_URL", baseURL) + + reporter, err := New(Options{}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, err = reporter.SendReport(context.Background()) + + if err == nil { + t.Fatal("Expected error but got nil") + } +} + +func TestSendReportWithOPAUpdate(t *testing.T) { + // to support testing on all supported platforms + downloadLink := fmt.Sprintf("https://openpolicyagent.org/downloads/v100.0.0/opa_%v_%v", + runtime.GOOS, runtime.GOARCH) + + if runtime.GOARCH == "arm64" { + downloadLink = fmt.Sprintf("%v_static", downloadLink) + } + + if strings.HasPrefix(runtime.GOOS, "win") { + downloadLink = fmt.Sprintf("%v.exe", downloadLink) + } + + srvResp := &GHResponse{ + TagName: "v100.0.0", + Download: downloadLink, + ReleaseNotes: "https://github.com/open-policy-agent/opa/releases/tag/v100.0.0", + } + + // test server + baseURL, teardown := getTestServer(srvResp, http.StatusOK) + defer teardown() + + t.Setenv("OPA_TELEMETRY_SERVICE_URL", baseURL) + + reporter, err := New(Options{}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + resp, err := reporter.SendReport(context.Background()) + + if err != nil { + t.Fatalf("Expected no error but got %v", err) + } + + exp := &DataResponse{Latest: ReleaseDetails{ + Download: downloadLink, + ReleaseNotes: "https://github.com/open-policy-agent/opa/releases/tag/v100.0.0", + LatestRelease: "v100.0.0", + OPAUpToDate: false, + }} + + if !reflect.DeepEqual(resp, exp) { + t.Fatalf("Expected response: %+v but got: %+v", exp, resp) + } +} + +func TestPretty(t *testing.T) { + dr := DataResponse{} + resp := dr.Pretty() + + if resp != "" { + t.Fatalf("Expected empty response but got %v", resp) + } + + dr.Latest.Download = "https://openpolicyagent.org/downloads/v100.0.0/opa_darwin_amd64" + resp = dr.Pretty() + + if resp != "" { + t.Fatalf("Expected empty response but got %v", resp) + } + + dr.Latest.ReleaseNotes = "https://github.com/open-policy-agent/opa/releases/tag/v100.0.0" + resp = dr.Pretty() + + if resp != "" { + t.Fatalf("Expected empty response but got %v", resp) + } + + dr.Latest.LatestRelease = "v100.0.0" + resp = dr.Pretty() + + exp := "Latest Upstream Version: 100.0.0\n" + + "Download: https://openpolicyagent.org/downloads/v100.0.0/opa_darwin_amd64\n" + + "Release Notes: https://github.com/open-policy-agent/opa/releases/tag/v100.0.0" + + if resp != exp { + t.Fatalf("Expected response:\n\n%v\n\nGot:\n\n%v\n\n", exp, resp) + } +} + +func TestSlice(t *testing.T) { + + var dr *DataResponse + + if len(dr.Slice()) != 0 { + t.Fatal("expected empty slice") + } + + dr = &DataResponse{} + + if len(dr.Slice()) != 0 { + t.Fatal("expected empty slice since fields are unset") + } + + dr.Latest.Download = "https://example.com" + dr.Latest.LatestRelease = "v0.100.0" + dr.Latest.ReleaseNotes = "https://example2.com" + + exp := [][2]string{ + {"Latest Upstream Version", "0.100.0"}, + {"Download", "https://example.com"}, + {"Release Notes", "https://example2.com"}, + } + + if !reflect.DeepEqual(exp, dr.Slice()) { + t.Fatalf("expected %v but got %v", exp, dr.Slice()) + } +} + +func getTestServer(update any, statusCode int) (baseURL string, teardownFn func()) { + mux := http.NewServeMux() + ts := httptest.NewServer(mux) + + mux.HandleFunc("/repos/open-policy-agent/opa/releases/latest", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(statusCode) + bs, _ := json.Marshal(update) + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(bs) + }) + return ts.URL, ts.Close +} diff --git a/third_party/opa/internal/runtime/init/init.go b/third_party/opa/internal/runtime/init/init.go new file mode 100644 index 000000000000..4f93a4f127da --- /dev/null +++ b/third_party/opa/internal/runtime/init/init.go @@ -0,0 +1,261 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package init is an internal package with helpers for data and policy loading during initialization. +package init + +import ( + "context" + "fmt" + "io/fs" + "path/filepath" + "strings" + + storedversion "github.com/open-policy-agent/opa/internal/version" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" +) + +// InsertAndCompileOptions contains the input for the operation. +type InsertAndCompileOptions struct { + Store storage.Store + Txn storage.Transaction + Files loader.Result + Bundles map[string]*bundle.Bundle + MaxErrors int + EnablePrintStatements bool + ParserOptions ast.ParserOptions + BundleActivatorPlugin string +} + +// InsertAndCompileResult contains the output of the operation. +type InsertAndCompileResult struct { + Compiler *ast.Compiler + Metrics metrics.Metrics +} + +// InsertAndCompile writes data and policy into the store and returns a compiler for the +// store contents. +func InsertAndCompile(ctx context.Context, opts InsertAndCompileOptions) (*InsertAndCompileResult, error) { + if len(opts.Files.Documents) > 0 { + if err := opts.Store.Write(ctx, opts.Txn, storage.AddOp, storage.Path{}, opts.Files.Documents); err != nil { + return nil, fmt.Errorf("storage error: %w", err) + } + } + + policies := make(map[string]*ast.Module, len(opts.Files.Modules)) + + for id, parsed := range opts.Files.Modules { + policies[id] = parsed.Parsed + } + + compiler := ast.NewCompiler(). + WithDefaultRegoVersion(opts.ParserOptions.RegoVersion). + SetErrorLimit(opts.MaxErrors). + WithPathConflictsCheck(storage.NonEmpty(ctx, opts.Store, opts.Txn)). + WithEnablePrintStatements(opts.EnablePrintStatements) + m := metrics.New() + + activation := &bundle.ActivateOpts{ + Ctx: ctx, + Store: opts.Store, + Txn: opts.Txn, + Compiler: compiler, + Metrics: m, + Bundles: opts.Bundles, + ExtraModules: policies, + ParserOptions: opts.ParserOptions, + Plugin: opts.BundleActivatorPlugin, + } + + err := bundle.Activate(activation) + if err != nil { + return nil, err + } + + // Policies in bundles will have already been added to the store, but + // modules loaded outside of bundles will need to be added manually. + for id, parsed := range opts.Files.Modules { + if err := opts.Store.UpsertPolicy(ctx, opts.Txn, id, parsed.Raw); err != nil { + return nil, fmt.Errorf("storage error: %w", err) + } + } + + // Set the version in the store last to prevent data files from overwriting. + if err := storedversion.Write(ctx, opts.Store, opts.Txn); err != nil { + return nil, fmt.Errorf("storage error: %w", err) + } + + return &InsertAndCompileResult{Compiler: compiler, Metrics: m}, nil +} + +// LoadPathsResult contains the output loading a set of paths. +type LoadPathsResult struct { + Bundles map[string]*bundle.Bundle + Files loader.Result +} + +// WalkPathsResult contains the output loading a set of paths. +type WalkPathsResult struct { + BundlesLoader []BundleLoader + FileDescriptors []*Descriptor +} + +// BundleLoader contains information about files in a bundle +type BundleLoader struct { + DirectoryLoader bundle.DirectoryLoader + IsDir bool +} + +// Descriptor contains information about a file +type Descriptor struct { + Root string + Path string +} + +// LoadPaths reads data and policy from the given paths and returns a set of bundles or +// raw loader file results. +func LoadPaths(paths []string, + filter loader.Filter, + asBundle bool, + bvc *bundle.VerificationConfig, + skipVerify bool, + bundleLazyLoading bool, + processAnnotations bool, + caps *ast.Capabilities, + fsys fs.FS) (*LoadPathsResult, error) { + return LoadPathsForRegoVersion(ast.RegoV0, paths, filter, asBundle, bvc, skipVerify, bundleLazyLoading, processAnnotations, false, caps, fsys) +} + +func LoadPathsForRegoVersion(regoVersion ast.RegoVersion, + paths []string, + filter loader.Filter, + asBundle bool, + bvc *bundle.VerificationConfig, + skipVerify bool, + bundleLazyLoading bool, + processAnnotations bool, + followSymlinks bool, + caps *ast.Capabilities, + fsys fs.FS) (*LoadPathsResult, error) { + + if caps == nil { + caps = ast.CapabilitiesForThisVersion() + } + + // tar.gz files are automatically loaded as bundles + var likelyBundles, nonBundlePaths []string + if !asBundle { + likelyBundles, nonBundlePaths = splitByTarGzExt(paths) + paths = likelyBundles + } + + var result LoadPathsResult + var err error + if asBundle || len(likelyBundles) > 0 { + result.Bundles = make(map[string]*bundle.Bundle, len(paths)) + for _, path := range paths { + result.Bundles[path], err = loader.NewFileLoader(). + WithFS(fsys). + WithBundleVerificationConfig(bvc). + WithSkipBundleVerification(skipVerify). + WithBundleLazyLoadingMode(bundleLazyLoading). + WithFilter(filter). + WithProcessAnnotation(processAnnotations). + WithCapabilities(caps). + WithRegoVersion(regoVersion). + WithFollowSymlinks(followSymlinks). + AsBundle(path) + if err != nil { + return nil, err + } + } + } + + if len(nonBundlePaths) == 0 { + return &result, nil + } + + files, err := loader.NewFileLoader(). + WithFS(fsys). + WithBundleLazyLoadingMode(bundleLazyLoading). + WithProcessAnnotation(processAnnotations). + WithCapabilities(caps). + WithRegoVersion(regoVersion). + Filtered(nonBundlePaths, filter) + + if err != nil { + return nil, err + } + + result.Files = *files + + return &result, nil +} + +// splitByTarGzExt splits the paths in 2 groups. Ones with .tar.gz and another with +// non .tar.gz extensions. +func splitByTarGzExt(paths []string) (targzs []string, nonTargzs []string) { + for _, path := range paths { + if strings.HasSuffix(path, ".tar.gz") { + targzs = append(targzs, path) + } else { + nonTargzs = append(nonTargzs, path) + } + } + return +} + +// WalkPaths reads data and policy from the given paths and returns a set of bundle directory loaders +// or descriptors that contain information about files. +func WalkPaths(paths []string, filter loader.Filter, asBundle bool) (*WalkPathsResult, error) { + + var result WalkPathsResult + + if asBundle { + result.BundlesLoader = make([]BundleLoader, len(paths)) + for i, path := range paths { + bundleLoader, isDir, err := loader.GetBundleDirectoryLoader(path) + if err != nil { + return nil, err + } + + result.BundlesLoader[i] = BundleLoader{ + DirectoryLoader: bundleLoader, + IsDir: isDir, + } + } + return &result, nil + } + + result.FileDescriptors = []*Descriptor{} + for _, path := range paths { + filePaths, err := loader.FilteredPaths([]string{path}, filter) + if err != nil { + return nil, err + } + + for _, fp := range filePaths { + // Trim off the root directory and return path as if chrooted + cleanedPath := strings.TrimPrefix(fp, path) + if path == "." && filepath.Base(fp) == bundle.ManifestExt { + cleanedPath = fp + } + + if !strings.HasPrefix(cleanedPath, "/") { + cleanedPath = "/" + cleanedPath + } + + result.FileDescriptors = append(result.FileDescriptors, &Descriptor{ + Root: path, + Path: cleanedPath, + }) + } + } + + return &result, nil +} diff --git a/third_party/opa/internal/runtime/init/init_test.go b/third_party/opa/internal/runtime/init/init_test.go new file mode 100644 index 000000000000..d0ebbd4af1f6 --- /dev/null +++ b/third_party/opa/internal/runtime/init/init_test.go @@ -0,0 +1,528 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package init + +import ( + "bytes" + "context" + "encoding/json" + "io" + "io/fs" + "os" + "path" + "path/filepath" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" + "github.com/open-policy-agent/opa/v1/version" +) + +func TestInit(t *testing.T) { + + mod1 := `package a.b.c + +import data.a.foo + +p = true { foo = "bar" } +p = true { 1 = 2 }` + + mod2 := `package b.c.d + +import data.b.foo + +p = true { foo = "bar" } +p = true { 1 = 2 }` + + tests := []struct { + note string + fs map[string]string + loadParams []string + expectedData map[string]string + expectedMods []string + asBundle bool + }{ + { + note: "load files", + fs: map[string]string{ + "datafile": `{"foo": "bar", "x": {"y": {"z": [1]}}}`, + "policyFile": mod1, + }, + loadParams: []string{"datafile", "policyFile"}, + expectedData: map[string]string{ + "/foo": "bar", + }, + expectedMods: []string{mod1}, + asBundle: false, + }, + { + note: "load bundle", + fs: map[string]string{ + "datafile": `{"foo": "bar", "x": {"y": {"z": [1]}}}`, // Should be ignored + "data.json": `{"foo": "not-bar"}`, + "policy.rego": mod1, + }, + loadParams: []string{"/"}, + expectedData: map[string]string{ + "/foo": "not-bar", + }, + expectedMods: []string{mod1}, + asBundle: true, + }, + { + note: "load multiple bundles", + fs: map[string]string{ + "/bundle1/a/data.json": `{"foo": "bar1", "x": {"y": {"z": [1]}}}`, // Should be ignored + "/bundle1/a/policy.rego": mod1, + "/bundle1/a/.manifest": `{"roots": ["a"]}`, + "/bundle2/b/data.json": `{"foo": "bar2"}`, + "/bundle2/b/policy.rego": mod2, + "/bundle2/b/.manifest": `{"roots": ["b"]}`, + }, + loadParams: []string{"bundle1", "bundle2"}, + expectedData: map[string]string{ + "/a/foo": "bar1", + "/b/foo": "bar2", + }, + expectedMods: []string{mod1, mod2}, + asBundle: true, + }, + { + note: "preserve OPA version", + fs: map[string]string{ + "/root/system/version/data.json": `{"version": "XYZ"}`, // Should be overwritten + }, + loadParams: []string{"root"}, + expectedData: map[string]string{ + "/system/version/version": version.Version, + }, + asBundle: true, + }, + } + + ctx := context.Background() + + for _, useMemoryFS := range []bool{false, true} { + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTestFS(tc.fs, useMemoryFS, func(rootDir string, fsys fs.FS) { + paths := []string{} + + for _, fileName := range tc.loadParams { + paths = append(paths, filepath.Join(rootDir, fileName)) + } + // Create a new store and perform a file load/insert sequence. + store := inmem.New() + + err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + loaded, err := LoadPaths(paths, nil, tc.asBundle, nil, false, true, false, nil, fsys) + if err != nil { + return err + } + + _, err = InsertAndCompile(ctx, InsertAndCompileOptions{ + Store: store, + Txn: txn, + Files: loaded.Files, + Bundles: loaded.Bundles, + MaxErrors: -1, + }) + + return err + }) + + if err != nil { + t.Fatal(err) + } + + // Verify the loading was successful as expected. + txn := storage.NewTransactionOrDie(ctx, store) + + for storePath, expected := range tc.expectedData { + node, err := store.Read(ctx, txn, storage.MustParsePath(storePath)) + if util.Compare(node, expected) != 0 || err != nil { + t.Fatalf("Expected %v but got %v (err: %v)", expected, node, err) + } + } + + ids, err := store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } + + if len(tc.expectedMods) != len(ids) { + t.Fatalf("Expected %d modules, got %d", len(tc.expectedMods), len(ids)) + } + + actualMods := map[string]struct{}{} + for _, id := range ids { + result, err := store.GetPolicy(ctx, txn, id) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + actualMods[string(result)] = struct{}{} + } + + for _, expectedMod := range tc.expectedMods { + if _, found := actualMods[expectedMod]; !found { + t.Fatalf("Expected %v but got: %v", expectedMod, actualMods) + } + } + + _, err = store.Read(ctx, txn, storage.MustParsePath("/system/version")) + if err != nil { + t.Fatal(err) + } + }) + }) + } + } +} + +func TestLoadTarGzsInBundleAndNonBundleMode(t *testing.T) { + + type bundleInfo struct { + fileName string + files [][2]string + expBundle bundle.Bundle + } + + bundle1TarGz := bundleInfo{ + fileName: "bundle1.tar.gz", + files: [][2]string{ + {"/a/data.json", `{"foo": "bar1", "x": {"y": {"z": [1]}}}`}, + {"/a/.manifest", `{"roots": ["a"]}`}, + }, + expBundle: bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + }, + Data: map[string]any{ + "a": map[string]any{ + "foo": "bar1", + "x": map[string]any{ + "y": map[string]any{ + "z": []any{json.Number("1")}, + }, + }, + }, + }, + }, + } + + bundle2TarGz := bundleInfo{ + fileName: "bundle2.tar.gz", + files: [][2]string{ + {"/b/data.json", `{"foo": "bar2", "x": {"y": {"z": [1]}}}`}, + {"/b/.manifest", `{"roots": ["b"]}`}, + }, + expBundle: bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + }, + Data: map[string]any{ + "b": map[string]any{ + "foo": "bar2", + "x": map[string]any{ + "y": map[string]any{ + "z": []any{json.Number("1")}, + }, + }, + }, + }, + }, + } + + bundle1Folder := map[string]string{ + "/bundle1/a/data.json": `{"foo1": "bar2", "x": {"y": {"z": [2]}}}`, + "/bundle1/a/.manifest": `{"roots": ["a"]}`, + "/bundle1/a/foo.rego": `package a.b.y`, + } + + modulePath := "/bundle1/a/foo.rego" + module := `package a.b.y` + + bundle1FolderInfo := bundleInfo{ + fileName: "bundle1", + expBundle: bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + }, + Data: map[string]any{ + "a": map[string]any{ + "foo1": "bar2", + "x": map[string]any{ + "y": map[string]any{ + "z": []any{json.Number("2")}, + }, + }, + }, + }, + Modules: []bundle.ModuleFile{ + { + URL: modulePath, + Path: modulePath, + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + }, + } + + tests := []struct { + note string + bundleInfoTC []bundleInfo + folderContent map[string]string + expectedBundles int + expectedModules int + asBundle bool + }{ + { + note: "load multiple bundles. one tar.gz and one folder. Bundle mode is true", + folderContent: bundle1Folder, + bundleInfoTC: []bundleInfo{ + bundle1TarGz, + bundle1FolderInfo, + }, + expectedBundles: 2, + expectedModules: 0, + asBundle: true, + }, + { + note: "load multiple bundles. one tar.gz and one folder. Bundle mode is false", + folderContent: bundle1Folder, + bundleInfoTC: []bundleInfo{ + bundle1TarGz, + bundle1FolderInfo, + }, + expectedBundles: 1, + expectedModules: 1, + asBundle: false, + }, + { + note: "load multiple bundles. two tar.gz and one folder. Bundle mode is true", + folderContent: bundle1Folder, + bundleInfoTC: []bundleInfo{ + bundle1TarGz, + bundle2TarGz, + bundle1FolderInfo, + }, + expectedBundles: 3, + expectedModules: 0, + asBundle: true, + }, + { + note: "load multiple bundles. two tar.gz and one folder. Bundle mode is false", + folderContent: bundle1Folder, + bundleInfoTC: []bundleInfo{ + bundle1TarGz, + bundle2TarGz, + bundle1FolderInfo, + }, + expectedBundles: 2, + expectedModules: 1, + asBundle: false, + }, + { + note: "load just one folder. Bundle mode is true", + folderContent: bundle1Folder, + bundleInfoTC: []bundleInfo{ + bundle1FolderInfo, + }, + expectedBundles: 1, + expectedModules: 0, + asBundle: true, + }, + { + note: "load just one folder. Bundle mode is false", + folderContent: bundle1Folder, + bundleInfoTC: []bundleInfo{ + bundle1FolderInfo, + }, + expectedBundles: 0, + expectedModules: 1, + asBundle: false, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + + test.WithTempFS(tc.folderContent, func(rootDir string) { + paths := []string{} + for _, bdlInfo := range tc.bundleInfoTC { + if strings.HasSuffix(bdlInfo.fileName, ".tar.gz") { + + // Create the tar gz files temporarily + buf := archive.MustWriteTarGz(bdlInfo.files) + bundleFile := filepath.Join(rootDir, bdlInfo.fileName) + out, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + _, err = out.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + paths = append(paths, filepath.Join(rootDir, bdlInfo.fileName)) + } + + loaded, err := LoadPaths(paths, nil, tc.asBundle, nil, true, false, false, nil, nil) + if err != nil { + t.Fatal("Failed LoadPaths ", err) + } + if tc.expectedBundles != len(loaded.Bundles) { + t.Fatalf("Expected %d bundles, got %d", tc.expectedBundles, len(loaded.Bundles)) + } + if tc.expectedModules != len(loaded.Files.Modules) { + t.Fatalf("Expected %d modules, got %d", tc.expectedModules, len(loaded.Files.Modules)) + } + + // Testing the content + for path, actual := range loaded.Bundles { + for _, bdlInfo := range tc.bundleInfoTC { + if strings.HasSuffix(path, bdlInfo.fileName) { + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).Write(bdlInfo.expBundle); err != nil { + t.Fatal(err) + } + + expected, err := bundle.NewReader(&buf).Read() + if err != nil { + t.Fatal(err) + } + + // adjusting the URL and Path due to /tmp/ path + if len(bdlInfo.expBundle.Modules) > 0 { + expected.Modules[0].URL = rootDir + expected.Modules[0].URL + expected.Modules[0].Path = rootDir + expected.Modules[0].Path + } + + if !expected.Equal(*actual) { + t.Fatalf("\nExpected: %+v\nGot: %+v", expected, actual) + } + } + } + } + }) + }) + } + +} + +func TestWalkPaths(t *testing.T) { + files := map[string]string{ + "/bundle1/a/data.json": `{"foo": "bar1", "x": {"y": {"z": [1]}}}`, + "/bundle1/a/policy.rego": `package example.foo`, + "/bundle1/a/.manifest": `{"roots": ["a"]}`, + "/bundle2/b/data.json": `{"foo": "bar2"}`, + "/bundle2/b/policy.rego": `package authz`, + "/bundle2/b/.manifest": `{"roots": ["b"]}`, + } + + test.WithTempFS(files, func(rootDir string) { + + paths := []string{} + paths = append(paths, filepath.Join(rootDir, "bundle1"), filepath.Join(rootDir, "bundle2")) + + // bundle mode + loaded, err := WalkPaths(paths, nil, true) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if len(loaded.BundlesLoader) != len(paths) { + t.Fatalf("Expected %v bundle loaders but got %v", len(paths), len(loaded.BundlesLoader)) + } + + // check files + result := []string{} + for _, bl := range loaded.BundlesLoader { + for { + f, err := bl.DirectoryLoader.NextFile() + if err == io.EOF { + break + } + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + result = append(result, f.Path()) + + if _, ok := files[strings.TrimPrefix(f.URL(), rootDir)]; !ok { + t.Fatalf("unexpected file %v", f.Path()) + } + } + } + + if len(result) != len(files) { + t.Fatalf("Expected %v files across bundles but got %v", len(files), len(result)) + } + + // non-bundle mode + loaded, err = WalkPaths(paths, nil, false) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if len(loaded.FileDescriptors) != len(files) { + t.Fatalf("Expected %v files across directories but got %v", len(files), len(loaded.FileDescriptors)) + } + + for _, d := range loaded.FileDescriptors { + path := path.Join(d.Root, d.Path) + path = strings.TrimPrefix(path, rootDir) + if _, ok := files[path]; !ok { + t.Fatalf("unexpected file %v", path) + } + } + }) +} + +func TestLoadPathsBundleModeWithFilter(t *testing.T) { + files := map[string]string{ + "a/data.json": `{"foo": "not-bar"}`, + "policy.rego": "package foo\n p = 1", + "policy_test.rego": "package foo\n test_p { p }", + "a/.manifest": `{"roots": ["a", "foo"]}`, + } + + test.WithTempFS(files, func(rootDir string) { + + paths := []string{rootDir} + + // bundle mode + loaded, err := LoadPaths(paths, func(abspath string, info os.FileInfo, depth int) bool { + return loader.GlobExcludeName("*_test.rego", 1)(abspath, info, depth) + }, true, nil, true, false, false, nil, nil) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if len(loaded.Bundles) != len(paths) { + t.Fatalf("Expected %v bundle loaders but got %v", len(paths), len(loaded.Bundles)) + } + + b, ok := loaded.Bundles[rootDir] + if !ok { + t.Fatalf("expected bundle %v", rootDir) + } + + expected := 1 + if len(b.Modules) != expected { + t.Fatalf("expected %v module but got %v", expected, len(b.Modules)) + } + }) +} diff --git a/third_party/opa/internal/runtime/runtime.go b/third_party/opa/internal/runtime/runtime.go new file mode 100644 index 000000000000..07e30c87fda5 --- /dev/null +++ b/third_party/opa/internal/runtime/runtime.go @@ -0,0 +1,62 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package runtime contains utilities to return runtime information on the OPA instance. +package runtime + +import ( + "os" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/version" +) + +// Params controls the types of runtime information to return. +type Params struct { + Config []byte + IsAuthorizationEnabled bool + SkipKnownSchemaCheck bool +} + +// Term returns the runtime information as an ast.Term object. +func Term(params Params) (*ast.Term, error) { + + obj := ast.NewObject() + + if params.Config != nil { + + var x any + if err := util.Unmarshal(params.Config, &x); err != nil { + return nil, err + } + + v, err := ast.InterfaceToValue(x) + if err != nil { + return nil, err + } + + obj.Insert(ast.InternedTerm("config"), ast.NewTerm(v)) + } + + env := ast.NewObject() + + for _, s := range os.Environ() { + parts := strings.SplitN(s, "=", 2) + if len(parts) == 1 { + env.Insert(ast.StringTerm(parts[0]), ast.InternedNullTerm) + } else if len(parts) > 1 { + env.Insert(ast.StringTerm(parts[0]), ast.StringTerm(parts[1])) + } + } + + obj.Insert(ast.InternedTerm("env"), ast.NewTerm(env)) + obj.Insert(ast.InternedTerm("version"), ast.StringTerm(version.Version)) + obj.Insert(ast.InternedTerm("commit"), ast.StringTerm(version.Vcs)) + obj.Insert(ast.InternedTerm("authorization_enabled"), ast.InternedTerm(params.IsAuthorizationEnabled)) + obj.Insert(ast.InternedTerm("skip_known_schema_check"), ast.InternedTerm(params.SkipKnownSchemaCheck)) + + return ast.NewTerm(obj), nil +} diff --git a/third_party/opa/internal/semver/LICENSE b/third_party/opa/internal/semver/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/third_party/opa/internal/semver/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/third_party/opa/internal/semver/semver.go b/third_party/opa/internal/semver/semver.go new file mode 100644 index 000000000000..389eeccc18b9 --- /dev/null +++ b/third_party/opa/internal/semver/semver.go @@ -0,0 +1,235 @@ +// Copyright 2013-2015 CoreOS, Inc. +// +// 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. + +// Semantic Versions http://semver.org + +// Package semver has been vendored from: +// https://github.com/coreos/go-semver/tree/e214231b295a8ea9479f11b70b35d5acf3556d9b/semver +// A number of the original functions of the package have been removed since +// they are not required for our built-ins. +package semver + +import ( + "bytes" + "fmt" + "regexp" + "strconv" + "strings" +) + +// Version represents a parsed SemVer +type Version struct { + Major int64 + Minor int64 + Patch int64 + PreRelease PreRelease + Metadata string +} + +// PreRelease represents a pre-release suffix string +type PreRelease string + +func splitOff(input *string, delim string) (val string) { + parts := strings.SplitN(*input, delim, 2) + + if len(parts) == 2 { + *input = parts[0] + val = parts[1] + } + + return val +} + +// NewVersion constructs new SemVers from strings +func NewVersion(version string) (*Version, error) { + v := Version{} + + if err := v.Set(version); err != nil { + return nil, err + } + + return &v, nil +} + +// Set parses and updates v from the given version string. Implements flag.Value +func (v *Version) Set(version string) error { + metadata := splitOff(&version, "+") + preRelease := PreRelease(splitOff(&version, "-")) + dotParts := strings.SplitN(version, ".", 3) + + if len(dotParts) != 3 { + return fmt.Errorf("%s is not in dotted-tri format", version) + } + + if err := validateIdentifier(string(preRelease)); err != nil { + return fmt.Errorf("failed to validate pre-release: %v", err) + } + + if err := validateIdentifier(metadata); err != nil { + return fmt.Errorf("failed to validate metadata: %v", err) + } + + parsed := make([]int64, 3) + + for i, v := range dotParts[:3] { + val, err := strconv.ParseInt(v, 10, 64) + parsed[i] = val + if err != nil { + return err + } + } + + v.Metadata = metadata + v.PreRelease = preRelease + v.Major = parsed[0] + v.Minor = parsed[1] + v.Patch = parsed[2] + return nil +} + +func (v Version) String() string { + var buffer bytes.Buffer + + fmt.Fprintf(&buffer, "%d.%d.%d", v.Major, v.Minor, v.Patch) + + if v.PreRelease != "" { + fmt.Fprintf(&buffer, "-%s", v.PreRelease) + } + + if v.Metadata != "" { + fmt.Fprintf(&buffer, "+%s", v.Metadata) + } + + return buffer.String() +} + +// Compare tests if v is less than, equal to, or greater than versionB, +// returning -1, 0, or +1 respectively. +func (v Version) Compare(versionB Version) int { + if cmp := recursiveCompare(v.Slice(), versionB.Slice()); cmp != 0 { + return cmp + } + return preReleaseCompare(v, versionB) +} + +// Slice converts the comparable parts of the semver into a slice of integers. +func (v Version) Slice() []int64 { + return []int64{v.Major, v.Minor, v.Patch} +} + +// Slice splits the pre-release suffix string +func (p PreRelease) Slice() []string { + preRelease := string(p) + return strings.Split(preRelease, ".") +} + +func preReleaseCompare(versionA Version, versionB Version) int { + a := versionA.PreRelease + b := versionB.PreRelease + + /* Handle the case where if two versions are otherwise equal it is the + * one without a PreRelease that is greater */ + if len(a) == 0 && (len(b) > 0) { + return 1 + } else if len(b) == 0 && (len(a) > 0) { + return -1 + } + + // If there is a prerelease, check and compare each part. + return recursivePreReleaseCompare(a.Slice(), b.Slice()) +} + +func recursiveCompare(versionA []int64, versionB []int64) int { + if len(versionA) == 0 { + return 0 + } + + a := versionA[0] + b := versionB[0] + + if a > b { + return 1 + } else if a < b { + return -1 + } + + return recursiveCompare(versionA[1:], versionB[1:]) +} + +func recursivePreReleaseCompare(versionA []string, versionB []string) int { + // A larger set of pre-release fields has a higher precedence than a smaller set, + // if all of the preceding identifiers are equal. + if len(versionA) == 0 { + if len(versionB) > 0 { + return -1 + } + return 0 + } else if len(versionB) == 0 { + // We're longer than versionB so return 1. + return 1 + } + + a := versionA[0] + b := versionB[0] + + aInt := false + bInt := false + + aI, err := strconv.Atoi(versionA[0]) + if err == nil { + aInt = true + } + + bI, err := strconv.Atoi(versionB[0]) + if err == nil { + bInt = true + } + + // Numeric identifiers always have lower precedence than non-numeric identifiers. + if aInt && !bInt { + return -1 + } else if !aInt && bInt { + return 1 + } + + // Handle Integer Comparison + if aInt && bInt { + if aI > bI { + return 1 + } else if aI < bI { + return -1 + } + } + + // Handle String Comparison + if a > b { + return 1 + } else if a < b { + return -1 + } + + return recursivePreReleaseCompare(versionA[1:], versionB[1:]) +} + +// validateIdentifier makes sure the provided identifier satisfies semver spec +func validateIdentifier(id string) error { + if id != "" && !reIdentifier.MatchString(id) { + return fmt.Errorf("%s is not a valid semver identifier", id) + } + return nil +} + +// reIdentifier is a regular expression used to check that pre-release and metadata +// identifiers satisfy the spec requirements +var reIdentifier = regexp.MustCompile(`^[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*$`) diff --git a/third_party/opa/internal/semver/semver_test.go b/third_party/opa/internal/semver/semver_test.go new file mode 100644 index 000000000000..d4b7780e0d09 --- /dev/null +++ b/third_party/opa/internal/semver/semver_test.go @@ -0,0 +1,105 @@ +// Copyright 2013-2015 CoreOS, Inc. +// +// 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. + +// vendored from: https://github.com/coreos/go-semver/tree/e214231b295a8ea9479f11b70b35d5acf3556d9b/semver +package semver + +import ( + "testing" +) + +type fixture struct { + GreaterVersion string + LesserVersion string +} + +var fixtures = []fixture{ + {"0.0.0", "0.0.0-foo"}, + {"0.0.1", "0.0.0"}, + {"1.0.0", "0.9.9"}, + {"0.10.0", "0.9.0"}, + {"0.99.0", "0.10.0"}, + {"2.0.0", "1.2.3"}, + {"0.0.0", "0.0.0-foo"}, + {"0.0.1", "0.0.0"}, + {"1.0.0", "0.9.9"}, + {"0.10.0", "0.9.0"}, + {"0.99.0", "0.10.0"}, + {"2.0.0", "1.2.3"}, + {"0.0.0", "0.0.0-foo"}, + {"0.0.1", "0.0.0"}, + {"1.0.0", "0.9.9"}, + {"0.10.0", "0.9.0"}, + {"0.99.0", "0.10.0"}, + {"2.0.0", "1.2.3"}, + {"1.2.3", "1.2.3-asdf"}, + {"1.2.3", "1.2.3-4"}, + {"1.2.3", "1.2.3-4-foo"}, + {"1.2.3-5-foo", "1.2.3-5"}, + {"1.2.3-5", "1.2.3-4"}, + {"1.2.3-5-foo", "1.2.3-5-Foo"}, + {"3.0.0", "2.7.2+asdf"}, + {"3.0.0+foobar", "2.7.2"}, + {"1.2.3-a.10", "1.2.3-a.5"}, + {"1.2.3-a.b", "1.2.3-a.5"}, + {"1.2.3-a.b", "1.2.3-a"}, + {"1.2.3-a.b.c.10.d.5", "1.2.3-a.b.c.5.d.100"}, + {"1.0.0", "1.0.0-rc.1"}, + {"1.0.0-rc.2", "1.0.0-rc.1"}, + {"1.0.0-rc.1", "1.0.0-beta.11"}, + {"1.0.0-beta.11", "1.0.0-beta.2"}, + {"1.0.0-beta.2", "1.0.0-beta"}, + {"1.0.0-beta", "1.0.0-alpha.beta"}, + {"1.0.0-alpha.beta", "1.0.0-alpha.1"}, + {"1.0.0-alpha.1", "1.0.0-alpha"}, + {"1.2.3-rc.1-1-1hash", "1.2.3-rc.2"}, +} + +func TestCompare(t *testing.T) { + for _, v := range fixtures { + gt, err := NewVersion(v.GreaterVersion) + if err != nil { + t.Error(err) + } + + lt, err := NewVersion(v.LesserVersion) + if err != nil { + t.Error(err) + } + + if gt.Compare(*lt) <= 0 { + t.Errorf("%s should be greater than %s", gt, lt) + } + if lt.Compare(*gt) > 0 { + t.Errorf("%s should not be greater than %s", lt, gt) + } + } +} + +func TestBadInput(t *testing.T) { + bad := []string{ + "1.2", + "1.2.3x", + "0x1.3.4", + "-1.2.3", + "1.2.3.4", + "0.88.0-11_e4e5dcabb", + "0.88.0+11_e4e5dcabb", + } + for _, b := range bad { + if _, err := NewVersion(b); err == nil { + t.Error("Improperly accepted value: ", b) + } + } +} diff --git a/third_party/opa/internal/storage/mock/mock.go b/third_party/opa/internal/storage/mock/mock.go new file mode 100644 index 000000000000..3c23ad586de8 --- /dev/null +++ b/third_party/opa/internal/storage/mock/mock.go @@ -0,0 +1,260 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package mock defines a fake storage implementation for use in testing. +package mock + +import ( + "context" + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" +) + +// Transaction is a mock storage.Transaction implementation for use in testing. +// It uses an internal storage.Transaction pointer with some added functionality. +type Transaction struct { + txn storage.Transaction + Committed int + Aborted int +} + +// ID returns the underlying transaction ID +func (t *Transaction) ID() uint64 { + return t.txn.ID() +} + +// Validate returns an error if the transaction is in an invalid state +func (t *Transaction) Validate() error { + if t.Committed > 1 { + return fmt.Errorf("transaction %d has too many commits (%d)", t.ID(), t.Committed) + } + if t.Aborted > 1 { + return fmt.Errorf("transaction %d has too many aborts (%d)", t.ID(), t.Committed) + } + return nil +} + +func (t *Transaction) safeToUse() bool { + return t.Committed == 0 && t.Aborted == 0 +} + +// Store is a mock storage.Store implementation for use in testing. +type Store struct { + inmem storage.Store + storeOpts []inmem.Opt + baseData map[string]any + Transactions []*Transaction + Reads []*ReadCall + Writes []*WriteCall +} + +// ReadCall captures the parameters for a Read call +type ReadCall struct { + Transaction *Transaction + Path storage.Path + Error error + Safe bool +} + +// WriteCall captures the parameters for a write call +type WriteCall struct { + Transaction *Transaction + Op storage.PatchOp + Path storage.Path + Error error + Safe bool +} + +// New creates a new mock Store +func New(opt ...inmem.Opt) *Store { + s := &Store{ + storeOpts: opt, + } + s.Reset() + return s +} + +// NewWithData creates a store with some initial data +func NewWithData(data map[string]any, opt ...inmem.Opt) *Store { + s := &Store{ + baseData: data, + storeOpts: opt, + } + s.Reset() + return s +} + +// Reset the store +func (s *Store) Reset() { + s.Transactions = []*Transaction{} + s.Reads = []*ReadCall{} + s.Writes = []*WriteCall{} + if s.baseData != nil { + s.inmem = inmem.NewFromObjectWithOpts(s.baseData, s.storeOpts...) + } else { + s.inmem = inmem.NewWithOpts(s.storeOpts...) + } +} + +// GetTransaction will a transaction with a specific ID +// that was associated with this Store. +func (s *Store) GetTransaction(id uint64) *Transaction { + for _, txn := range s.Transactions { + if txn.ID() == id { + return txn + } + } + return nil +} + +// Errors returns a list of errors for each invalid state found. +// If any Transactions are invalid or reads/writes were +// unsafe an error will be returned for each problem. +func (s *Store) Errors() []error { + var errs []error + for _, txn := range s.Transactions { + err := txn.Validate() + if err != nil { + errs = append(errs, err) + } + } + + for _, read := range s.Reads { + if !read.Safe { + errs = append(errs, fmt.Errorf("unsafe Read call %+v", *read)) + } + } + + for _, write := range s.Writes { + if !write.Safe { + errs = append(errs, fmt.Errorf("unsafe Write call %+v", *write)) + } + } + + return errs +} + +// AssertValid will raise an error with the provided testing.T if +// there are any errors on the store. +func (s *Store) AssertValid(t *testing.T) { + t.Helper() + for _, err := range s.Errors() { + t.Errorf("Error detected on store: %s", err) + } +} + +// storage.Store interface implementation + +// Register just shims the call to the underlying inmem store +func (s *Store) Register(ctx context.Context, txn storage.Transaction, config storage.TriggerConfig) (storage.TriggerHandle, error) { + return s.inmem.Register(ctx, getRealTxn(txn), config) +} + +// ListPolicies just shims the call to the underlying inmem store +func (s *Store) ListPolicies(ctx context.Context, txn storage.Transaction) ([]string, error) { + return s.inmem.ListPolicies(ctx, getRealTxn(txn)) +} + +// GetPolicy just shims the call to the underlying inmem store +func (s *Store) GetPolicy(ctx context.Context, txn storage.Transaction, name string) ([]byte, error) { + return s.inmem.GetPolicy(ctx, getRealTxn(txn), name) +} + +// UpsertPolicy just shims the call to the underlying inmem store +func (s *Store) UpsertPolicy(ctx context.Context, txn storage.Transaction, name string, policy []byte) error { + return s.inmem.UpsertPolicy(ctx, getRealTxn(txn), name, policy) +} + +// DeletePolicy just shims the call to the underlying inmem store +func (s *Store) DeletePolicy(ctx context.Context, txn storage.Transaction, name string) error { + return s.inmem.DeletePolicy(ctx, getRealTxn(txn), name) +} + +// NewTransaction will create a new transaction on the underlying inmem store +// but wraps it with a mock Transaction. These are then tracked on the store. +func (s *Store) NewTransaction(ctx context.Context, params ...storage.TransactionParams) (storage.Transaction, error) { + realTxn, err := s.inmem.NewTransaction(ctx, params...) + if err != nil { + return nil, err + } + txn := &Transaction{ + txn: realTxn, + Committed: 0, + Aborted: 0, + } + s.Transactions = append(s.Transactions, txn) + return txn, nil +} + +// Read will make a read from the underlying inmem store and +// add a new entry to the mock store Reads list. If there +// is an error are the read is unsafe it will be noted in +// the ReadCall. +func (s *Store) Read(ctx context.Context, txn storage.Transaction, path storage.Path) (any, error) { + mockTxn := txn.(*Transaction) + + data, err := s.inmem.Read(ctx, mockTxn.txn, path) + + s.Reads = append(s.Reads, &ReadCall{ + Transaction: mockTxn, + Path: path, + Error: err, + Safe: mockTxn.safeToUse(), + }) + + return data, err +} + +// Write will make a read from the underlying inmem store and +// add a new entry to the mock store Writes list. If there +// is an error are the write is unsafe it will be noted in +// the WriteCall. +func (s *Store) Write(ctx context.Context, txn storage.Transaction, op storage.PatchOp, path storage.Path, value any) error { + mockTxn := txn.(*Transaction) + + err := s.inmem.Write(ctx, mockTxn.txn, op, path, value) + + s.Writes = append(s.Writes, &WriteCall{ + Transaction: mockTxn, + Op: op, + Path: path, + Error: err, + Safe: mockTxn.safeToUse(), + }) + + return nil +} + +// Commit will commit the underlying transaction while +// also updating the mock Transaction +func (s *Store) Commit(ctx context.Context, txn storage.Transaction) error { + mockTxn := txn.(*Transaction) + + err := s.inmem.Commit(ctx, mockTxn.txn) + if err != nil { + return err + } + + mockTxn.Committed++ + return nil +} + +// Abort will abort the underlying transaction while +// also updating the mock Transaction +func (s *Store) Abort(ctx context.Context, txn storage.Transaction) { + mockTxn := txn.(*Transaction) + s.inmem.Abort(ctx, mockTxn.txn) + mockTxn.Aborted++ +} + +func (s *Store) Truncate(ctx context.Context, txn storage.Transaction, params storage.TransactionParams, it storage.Iterator) error { + return s.inmem.Truncate(ctx, getRealTxn(txn), params, it) +} + +func getRealTxn(txn storage.Transaction) storage.Transaction { + return txn.(*Transaction).txn +} diff --git a/third_party/opa/internal/strings/strings.go b/third_party/opa/internal/strings/strings.go new file mode 100644 index 000000000000..f2838ac36aa8 --- /dev/null +++ b/third_party/opa/internal/strings/strings.go @@ -0,0 +1,82 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package strings contains helpers to perform string manipulation +package strings + +import ( + "path/filepath" + "strings" + + "github.com/open-policy-agent/opa/internal/lcss" +) + +// TruncateFilePaths truncates the given file paths to conform to the given +// "ideal" width and returns the shortened paths by replacing the middle parts of paths +// with "...", ex: bundle1/.../a/b/policy.rego +func TruncateFilePaths(maxIdealWidth, maxWidth int, path ...string) (map[string]string, int) { + canShorten := make([][]byte, 0, len(path)) + + for _, p := range path { + canShorten = append(canShorten, []byte(getPathFromFirstSeparator(p))) + } + + // Find the longest common path segment + var lcs string + if len(canShorten) > 1 { + lcs = string(lcss.LongestCommonSubstring(canShorten...)) + } else { + lcs = string(canShorten[0]) + } + + // Don't just swap in the full LCSS, trim it down to be the least amount of + // characters to reach our "ideal" width boundary giving as much + // detail as possible without going too long. + diff := maxIdealWidth - (maxWidth - len(lcs) + 3) + if diff > 0 { + if diff > len(lcs) { + lcs = "" + } else { + // Favor data on the right hand side of the path + lcs = lcs[:len(lcs)-diff] + } + } + + result := map[string]string{} + for _, p := range path { + result[p] = p + } + + longestLocation := maxWidth + + // Swap in "..." for the longest common path, but if it makes things better + if len(lcs) > 3 { + for path := range result { + result[path] = strings.Replace(path, lcs, "...", 1) + } + + // Drop the overall length down to match our substitution + longestLocation -= (len(lcs) - 3) + } + + 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)) + firstSlash := strings.IndexRune(s, filepath.Separator) + if firstSlash > 0 { + return s[firstSlash+1:] + } + return s +} diff --git a/third_party/opa/internal/strvals/doc.go b/third_party/opa/internal/strvals/doc.go new file mode 100644 index 000000000000..019dc87bb9b4 --- /dev/null +++ b/third_party/opa/internal/strvals/doc.go @@ -0,0 +1,33 @@ +/* +Copyright The Helm 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 strvals provides tools for working with strval lines. + +OPA runtime config supports a compressed format for YAML settings which we call strvals. +The format is roughly like this: + + name=value,topname.subname=value + +The above is equivalent to the YAML document + + name: value + topname: + subname: value + +This package provides a parser and utilities for converting the strvals format +to other formats. +*/ +package strvals diff --git a/third_party/opa/internal/strvals/parser.go b/third_party/opa/internal/strvals/parser.go new file mode 100644 index 000000000000..6d867262f5bd --- /dev/null +++ b/third_party/opa/internal/strvals/parser.go @@ -0,0 +1,429 @@ +/* +Copyright The Helm 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 strvals + +import ( + "bytes" + "errors" + "fmt" + "io" + "strconv" + "strings" + + "sigs.k8s.io/yaml" +) + +// ErrNotList indicates that a non-list was treated as a list. +var ErrNotList = errors.New("not a list") + +// MaxIndex is the maximum index that will be allowed by setIndex. +// The default value 65536 = 1024 * 64 +const MaxIndex = 65536 + +// ToYAML takes a string of arguments and converts to a YAML document. +func ToYAML(s string) (string, error) { + m, err := Parse(s) + if err != nil { + return "", err + } + d, err := yaml.Marshal(m) + return string(d), err +} + +// Parse parses a set line. +// +// A set line is of the form name1=value1,name2=value2 +func Parse(s string) (map[string]any, error) { + vals := map[string]any{} + scanner := bytes.NewBufferString(s) + t := newParser(scanner, vals, false) + err := t.parse() + return vals, err +} + +// ParseString parses a set line and forces a string value. +// +// A set line is of the form name1=value1,name2=value2 +func ParseString(s string) (map[string]any, error) { + vals := map[string]any{} + scanner := bytes.NewBufferString(s) + t := newParser(scanner, vals, true) + err := t.parse() + return vals, err +} + +// ParseInto parses a strvals line and merges the result into dest. +// +// If the strval string has a key that exists in dest, it overwrites the +// dest version. +func ParseInto(s string, dest map[string]any) error { + scanner := bytes.NewBufferString(s) + t := newParser(scanner, dest, false) + return t.parse() +} + +// ParseIntoFile parses a filevals line and merges the result into dest. +// +// This method always returns a string as the value. +func ParseIntoFile(s string, dest map[string]any, runesToVal runesToVal) error { + scanner := bytes.NewBufferString(s) + t := newFileParser(scanner, dest, runesToVal) + return t.parse() +} + +// ParseIntoString parses a strvals line and merges the result into dest. +// +// This method always returns a string as the value. +func ParseIntoString(s string, dest map[string]any) error { + scanner := bytes.NewBufferString(s) + t := newParser(scanner, dest, true) + return t.parse() +} + +// parser is a simple parser that takes a strvals line and parses it into a +// map representation. +// +// where sc is the source of the original data being parsed +// where data is the final parsed data from the parses with correct types +// where st is a boolean to figure out if we're forcing it to parse values as string +type parser struct { + sc *bytes.Buffer + data map[string]any + runesToVal runesToVal +} + +type runesToVal func([]rune) (any, error) + +func newParser(sc *bytes.Buffer, data map[string]any, stringBool bool) *parser { + rs2v := func(rs []rune) (any, error) { + return typedVal(rs, stringBool), nil + } + return &parser{sc: sc, data: data, runesToVal: rs2v} +} + +func newFileParser(sc *bytes.Buffer, data map[string]any, runesToVal runesToVal) *parser { + return &parser{sc: sc, data: data, runesToVal: runesToVal} +} + +func (t *parser) parse() error { + for { + err := t.key(t.data) + if err == nil { + continue + } + if err == io.EOF { + return nil + } + return err + } +} + +func runeSet(r []rune) map[rune]bool { + s := make(map[rune]bool, len(r)) + for _, rr := range r { + s[rr] = true + } + return s +} + +func (t *parser) key(data map[string]any) error { + stop := runeSet([]rune{'=', '[', ',', '.'}) + for { + switch k, last, err := runesUntil(t.sc, stop); { + case err != nil: + if len(k) == 0 { + return err + } + return fmt.Errorf("key %q has no value", string(k)) + case last == '[': + // We are in a list index context, so we need to set an index. + i, err := t.keyIndex() + if err != nil { + return fmt.Errorf("error parsing index: %s", err) + } + kk := string(k) + // Find or create target list + list := []any{} + if _, ok := data[kk]; ok { + list = data[kk].([]any) + } + + // Now we need to get the value after the ]. + list, err = t.listItem(list, i) + set(data, kk, list) + return err + case last == '=': + // End of key. Consume =, Get value. + // FIXME: Get value list first + vl, e := t.valList() + switch e { + case nil: + set(data, string(k), vl) + return nil + case io.EOF: + set(data, string(k), "") + return e + case ErrNotList: + rs, e := t.val() + if e != nil && e != io.EOF { + return e + } + v, e := t.runesToVal(rs) + set(data, string(k), v) + return e + default: + return e + } + + case last == ',': + // No value given. Set the value to empty string. Return error. + set(data, string(k), "") + return fmt.Errorf("key %q has no value (cannot end with ,)", string(k)) + case last == '.': + // First, create or find the target map. + inner := map[string]any{} + if _, ok := data[string(k)]; ok { + inner = data[string(k)].(map[string]any) + } + + // Recurse + e := t.key(inner) + if len(inner) == 0 { + return fmt.Errorf("key map %q has no value", string(k)) + } + set(data, string(k), inner) + return e + } + } +} + +func set(data map[string]any, key string, val any) { + // If key is empty, don't set it. + if len(key) == 0 { + return + } + data[key] = val +} + +func setIndex(list []any, index int, val any) (l2 []any, err error) { + // There are possible index values that are out of range on a target system + // causing a panic. This will catch the panic and return an error instead. + // The value of the index that causes a panic varies from system to system. + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("error processing index %d: %s", index, r) + } + }() + + if index < 0 { + return list, fmt.Errorf("negative %d index not allowed", index) + } + if index > MaxIndex { + return list, fmt.Errorf("index of %d is greater than maximum supported index of %d", index, MaxIndex) + } + if len(list) <= index { + newlist := make([]any, index+1) + copy(newlist, list) + list = newlist + } + list[index] = val + return list, nil +} + +func (t *parser) keyIndex() (int, error) { + // First, get the key. + stop := runeSet([]rune{']'}) + v, _, err := runesUntil(t.sc, stop) + if err != nil { + return 0, err + } + // v should be the index + return strconv.Atoi(string(v)) + +} +func (t *parser) listItem(list []any, i int) ([]any, error) { + if i < 0 { + return list, fmt.Errorf("negative %d index not allowed", i) + } + stop := runeSet([]rune{'[', '.', '='}) + switch k, last, err := runesUntil(t.sc, stop); { + case len(k) > 0: + return list, fmt.Errorf("unexpected data at end of array index: %q", k) + case err != nil: + return list, err + case last == '=': + vl, e := t.valList() + switch e { + case nil: + return setIndex(list, i, vl) + case io.EOF: + return setIndex(list, i, "") + case ErrNotList: + rs, e := t.val() + if e != nil && e != io.EOF { + return list, e + } + v, e := t.runesToVal(rs) + if e != nil { + return nil, e + } + return setIndex(list, i, v) + default: + return list, e + } + case last == '[': + // now we have a nested list. Read the index and handle. + i, err := t.keyIndex() + if err != nil { + return list, fmt.Errorf("error parsing index: %s", err) + } + // Now we need to get the value after the ]. + list2, err := t.listItem(list, i) + if err != nil { + return nil, err + } + return setIndex(list, i, list2) + case last == '.': + // We have a nested object. Send to t.key + inner := map[string]any{} + if len(list) > i { + var ok bool + inner, ok = list[i].(map[string]any) + if !ok { + // We have indices out of order. Initialize empty value. + list[i] = map[string]any{} + inner = list[i].(map[string]any) + } + } + + // Recurse + e := t.key(inner) + if e != nil { + return list, e + } + return setIndex(list, i, inner) + default: + return nil, fmt.Errorf("parse error: unexpected token %v", last) + } +} + +func (t *parser) val() ([]rune, error) { + stop := runeSet([]rune{','}) + v, _, err := runesUntil(t.sc, stop) + return v, err +} + +func (t *parser) valList() ([]any, error) { + r, _, e := t.sc.ReadRune() + if e != nil { + return []any{}, e + } + + if r != '{' { + e = t.sc.UnreadRune() + if e != nil { + return []any{}, e + } + return []any{}, ErrNotList + } + + list := []any{} + stop := runeSet([]rune{',', '}'}) + for { + switch rs, last, err := runesUntil(t.sc, stop); { + case err != nil: + if err == io.EOF { + err = errors.New("list must terminate with '}'") + } + return list, err + case last == '}': + // If this is followed by ',', consume it. + if r, _, e := t.sc.ReadRune(); e == nil && r != ',' { + e = t.sc.UnreadRune() + if e != nil { + return []any{}, e + } + } + v, e := t.runesToVal(rs) + list = append(list, v) + return list, e + case last == ',': + v, e := t.runesToVal(rs) + if e != nil { + return list, e + } + list = append(list, v) + } + } +} + +func runesUntil(in io.RuneReader, stop map[rune]bool) ([]rune, rune, error) { + var v []rune + for { + switch r, _, e := in.ReadRune(); { + case e != nil: + return v, r, e + case inMap(r, stop): + return v, r, nil + case r == '\\': + next, _, e := in.ReadRune() + if e != nil { + return v, next, e + } + v = append(v, next) + default: + v = append(v, r) + } + } +} + +func inMap(k rune, m map[rune]bool) bool { + _, ok := m[k] + return ok +} + +func typedVal(v []rune, st bool) any { + val := string(v) + + if st { + return val + } + + if strings.EqualFold(val, "true") { + return true + } + + if strings.EqualFold(val, "false") { + return false + } + + if strings.EqualFold(val, "null") { + return struct{}{} + } + + if strings.EqualFold(val, "0") { + return int64(0) + } + + // If this value does not start with zero, try parsing it to an int + if len(val) != 0 && val[0] != '0' { + if iv, err := strconv.ParseInt(val, 10, 64); err == nil { + return iv + } + } + + return val +} diff --git a/third_party/opa/internal/strvals/parser_test.go b/third_party/opa/internal/strvals/parser_test.go new file mode 100644 index 000000000000..937400c7a7af --- /dev/null +++ b/third_party/opa/internal/strvals/parser_test.go @@ -0,0 +1,529 @@ +/* +Copyright The Helm 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 strvals + +import ( + "bytes" + "testing" + + "sigs.k8s.io/yaml" +) + +func TestSetIndex(t *testing.T) { + tests := []struct { + name string + initial []any + expect []any + add int + val int + err bool + }{ + { + name: "short", + initial: []any{0, 1}, + expect: []any{0, 1, 2}, + add: 2, + val: 2, + }, + { + name: "equal", + initial: []any{0, 1}, + expect: []any{0, 2}, + add: 1, + val: 2, + }, + { + name: "long", + initial: []any{0, 1, 2, 3, 4, 5}, + expect: []any{0, 1, 2, 4, 4, 5}, + add: 3, + val: 4, + }, + { + name: "negative", + initial: []any{0, 1, 2, 3, 4, 5}, + expect: []any{0, 1, 2, 3, 4, 5}, + add: -1, + val: 4, + err: true, + }, + { + name: "large", + initial: []any{0, 1, 2, 3, 4, 5}, + expect: []any{0, 1, 2, 3, 4, 5}, + add: MaxIndex + 1, + val: 4, + err: true, + }, + } + + for _, tt := range tests { + got, err := setIndex(tt.initial, tt.add, tt.val) + + if err != nil && tt.err == false { + t.Fatalf("%s: Expected no error but error returned", tt.name) + } else if err == nil && tt.err == true { + t.Fatalf("%s: Expected error but no error returned", tt.name) + } + + if len(got) != len(tt.expect) { + t.Fatalf("%s: Expected length %d, got %d", tt.name, len(tt.expect), len(got)) + } + + if !tt.err { + if gg := got[tt.add].(int); gg != tt.val { + t.Errorf("%s, Expected value %d, got %d", tt.name, tt.val, gg) + } + } + + for k, v := range got { + if v != tt.expect[k] { + t.Errorf("%s, Expected value %d, got %d", tt.name, tt.expect[k], v) + } + } + } +} + +func TestParseSet(t *testing.T) { + testsString := []struct { + str string + expect map[string]any + err bool + }{ + { + str: "long_int_string=1234567890", + expect: map[string]any{"long_int_string": "1234567890"}, + err: false, + }, + { + str: "boolean=true", + expect: map[string]any{"boolean": "true"}, + err: false, + }, + { + str: "is_null=null", + expect: map[string]any{"is_null": "null"}, + err: false, + }, + { + str: "zero=0", + expect: map[string]any{"zero": "0"}, + err: false, + }, + } + tests := []struct { + str string + expect map[string]any + err bool + }{ + { + "name1=null,f=false,t=true", + map[string]any{"name1": map[string]any{}, "f": false, "t": true}, + false, + }, + { + "name1=value1", + map[string]any{"name1": "value1"}, + false, + }, + { + "name1=value1,name2=value2", + map[string]any{"name1": "value1", "name2": "value2"}, + false, + }, + { + "name1=value1,name2=value2,", + map[string]any{"name1": "value1", "name2": "value2"}, + false, + }, + { + str: "name1=value1,,,,name2=value2,", + err: true, + }, + { + str: "name1=,name2=value2", + expect: map[string]any{"name1": "", "name2": "value2"}, + }, + { + str: "leading_zeros=00009", + expect: map[string]any{"leading_zeros": "00009"}, + }, + { + str: "zero_int=0", + expect: map[string]any{"zero_int": 0}, + }, + { + str: "long_int=1234567890", + expect: map[string]any{"long_int": 1234567890}, + }, + { + str: "boolean=true", + expect: map[string]any{"boolean": true}, + }, + { + str: "is_null=null", + expect: map[string]any{"is_null": map[string]any{}}, + err: false, + }, + { + str: "name1,name2=", + err: true, + }, + { + str: "name1,name2=value2", + err: true, + }, + { + str: "name1,name2=value2\\", + err: true, + }, + { + str: "name1,name2", + err: true, + }, + { + "name1=one\\,two,name2=three\\,four", + map[string]any{"name1": "one,two", "name2": "three,four"}, + false, + }, + { + "name1=one\\=two,name2=three\\=four", + map[string]any{"name1": "one=two", "name2": "three=four"}, + false, + }, + { + "name1=one two three,name2=three two one", + map[string]any{"name1": "one two three", "name2": "three two one"}, + false, + }, + { + "outer.inner=value", + map[string]any{"outer": map[string]any{"inner": "value"}}, + false, + }, + { + "outer.middle.inner=value", + map[string]any{"outer": map[string]any{"middle": map[string]any{"inner": "value"}}}, + false, + }, + { + "outer.inner1=value,outer.inner2=value2", + map[string]any{"outer": map[string]any{"inner1": "value", "inner2": "value2"}}, + false, + }, + { + "outer.inner1=value,outer.middle.inner=value", + map[string]any{ + "outer": map[string]any{ + "inner1": "value", + "middle": map[string]any{ + "inner": "value", + }, + }, + }, + false, + }, + { + str: "name1.name2", + err: true, + }, + { + str: "name1.name2,name1.name3", + err: true, + }, + { + str: "name1.name2=", + expect: map[string]any{"name1": map[string]any{"name2": ""}}, + }, + { + str: "name1.=name2", + err: true, + }, + { + str: "name1.,name2", + err: true, + }, + { + "name1={value1,value2}", + map[string]any{"name1": []string{"value1", "value2"}}, + false, + }, + { + "name1={value1,value2},name2={value1,value2}", + map[string]any{ + "name1": []string{"value1", "value2"}, + "name2": []string{"value1", "value2"}, + }, + false, + }, + { + "name1={1021,902}", + map[string]any{"name1": []int{1021, 902}}, + false, + }, + { + "name1.name2={value1,value2}", + map[string]any{"name1": map[string]any{"name2": []string{"value1", "value2"}}}, + false, + }, + { + str: "name1={1021,902", + err: true, + }, + // List support + { + str: "list[0]=foo", + expect: map[string]any{"list": []string{"foo"}}, + }, + { + str: "list[0].foo=bar", + expect: map[string]any{ + "list": []any{ + map[string]any{"foo": "bar"}, + }, + }, + }, + { + str: "list[0].foo=bar,list[0].hello=world", + expect: map[string]any{ + "list": []any{ + map[string]any{"foo": "bar", "hello": "world"}, + }, + }, + }, + { + str: "list[0]=foo,list[1]=bar", + expect: map[string]any{"list": []string{"foo", "bar"}}, + }, + { + str: "list[0]=foo,list[1]=bar,", + expect: map[string]any{"list": []string{"foo", "bar"}}, + }, + { + str: "list[0]=foo,list[3]=bar", + expect: map[string]any{"list": []any{"foo", nil, nil, "bar"}}, + }, + { + str: "illegal[0]name.foo=bar", + err: true, + }, + { + str: "noval[0]", + expect: map[string]any{"noval": []any{}}, + }, + { + str: "noval[0]=", + expect: map[string]any{"noval": []any{""}}, + }, + { + str: "nested[0][0]=1", + expect: map[string]any{"nested": []any{[]any{1}}}, + }, + { + str: "nested[1][1]=1", + expect: map[string]any{"nested": []any{nil, []any{nil, 1}}}, + }, + { + str: "name1.name2[0].foo=bar,name1.name2[1].foo=bar", + expect: map[string]any{ + "name1": map[string]any{ + "name2": []map[string]any{{"foo": "bar"}, {"foo": "bar"}}, + }, + }, + }, + { + str: "name1.name2[1].foo=bar,name1.name2[0].foo=bar", + expect: map[string]any{ + "name1": map[string]any{ + "name2": []map[string]any{{"foo": "bar"}, {"foo": "bar"}}, + }, + }, + }, + { + str: "name1.name2[1].foo=bar", + expect: map[string]any{ + "name1": map[string]any{ + "name2": []map[string]any{nil, {"foo": "bar"}}, + }, + }, + }, + } + + for _, tt := range tests { + got, err := Parse(tt.str) + if err != nil { + if tt.err { + continue + } + t.Fatalf("%s: %s", tt.str, err) + } + if tt.err { + t.Errorf("%s: Expected error. Got nil", tt.str) + } + + y1, err := yaml.Marshal(tt.expect) + if err != nil { + t.Fatal(err) + } + y2, err := yaml.Marshal(got) + if err != nil { + t.Fatalf("Error serializing parsed value: %s", err) + } + + if !bytes.Equal(y1, y2) { + t.Errorf("%s: Expected:\n%s\nGot:\n%s", tt.str, y1, y2) + } + } + for _, tt := range testsString { + got, err := ParseString(tt.str) + if err != nil { + if tt.err { + continue + } + t.Fatalf("%s: %s", tt.str, err) + } + if tt.err { + t.Errorf("%s: Expected error. Got nil", tt.str) + } + + y1, err := yaml.Marshal(tt.expect) + if err != nil { + t.Fatal(err) + } + y2, err := yaml.Marshal(got) + if err != nil { + t.Fatalf("Error serializing parsed value: %s", err) + } + + if !bytes.Equal(y1, y2) { + t.Errorf("%s: Expected:\n%s\nGot:\n%s", tt.str, y1, y2) + } + } +} + +func TestParseInto(t *testing.T) { + got := map[string]any{ + "outer": map[string]any{ + "inner1": "overwrite", + "inner2": "value2", + }, + } + input := "outer.inner1=value1,outer.inner3=value3,outer.inner4=4" + expect := map[string]any{ + "outer": map[string]any{ + "inner1": "value1", + "inner2": "value2", + "inner3": "value3", + "inner4": 4, + }, + } + + if err := ParseInto(input, got); err != nil { + t.Fatal(err) + } + + y1, err := yaml.Marshal(expect) + if err != nil { + t.Fatal(err) + } + y2, err := yaml.Marshal(got) + if err != nil { + t.Fatalf("Error serializing parsed value: %s", err) + } + + if !bytes.Equal(y1, y2) { + t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2) + } +} +func TestParseIntoString(t *testing.T) { + got := map[string]any{ + "outer": map[string]any{ + "inner1": "overwrite", + "inner2": "value2", + }, + } + input := "outer.inner1=1,outer.inner3=3" + expect := map[string]any{ + "outer": map[string]any{ + "inner1": "1", + "inner2": "value2", + "inner3": "3", + }, + } + + if err := ParseIntoString(input, got); err != nil { + t.Fatal(err) + } + + y1, err := yaml.Marshal(expect) + if err != nil { + t.Fatal(err) + } + y2, err := yaml.Marshal(got) + if err != nil { + t.Fatalf("Error serializing parsed value: %s", err) + } + + if !bytes.Equal(y1, y2) { + t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2) + } +} + +func TestParseIntoFile(t *testing.T) { + got := map[string]any{} + input := "name1=path1" + expect := map[string]any{ + "name1": "value1", + } + rs2v := func(rs []rune) (any, error) { + v := string(rs) + if v != "path1" { + t.Errorf("%s: runesToVal: Expected value path1, got %s", input, v) + return "", nil + } + return "value1", nil + } + + if err := ParseIntoFile(input, got, rs2v); err != nil { + t.Fatal(err) + } + + y1, err := yaml.Marshal(expect) + if err != nil { + t.Fatal(err) + } + y2, err := yaml.Marshal(got) + if err != nil { + t.Fatalf("Error serializing parsed value: %s", err) + } + + if !bytes.Equal(y1, y2) { + t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2) + } +} + +func TestToYAML(t *testing.T) { + // The TestParse does the hard part. We just verify that YAML formatting is + // happening. + o, err := ToYAML("name=value") + if err != nil { + t.Fatal(err) + } + expect := "name: value\n" + if o != expect { + t.Errorf("Expected %q, got %q", expect, o) + } +} diff --git a/third_party/opa/internal/uuid/uuid.go b/third_party/opa/internal/uuid/uuid.go new file mode 100644 index 000000000000..a18f024a2527 --- /dev/null +++ b/third_party/opa/internal/uuid/uuid.go @@ -0,0 +1,115 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package uuid + +import ( + "fmt" + "io" + "strings" + + "github.com/google/uuid" +) + +const ( + BILLION = 1000000000 +) + +// New Create a version 4 random UUID +func New(r io.Reader) (string, error) { + bs := make([]byte, 16) + n, err := io.ReadFull(r, bs) + if n != len(bs) || err != nil { + return "", err + } + bs[8] = bs[8]&^0xc0 | 0x80 + bs[6] = bs[6]&^0xf0 | 0x40 + return fmt.Sprintf("%x-%x-%x-%x-%x", bs[0:4], bs[4:6], bs[6:8], bs[8:10], bs[10:]), nil +} + +// Parse will use the google/uuid library to parse the string into a uuid +// if parsing fails, it will return an empty map. It will fill the map +// with some decoded values with fillMap +// ref: https://datatracker.ietf.org/doc/html/rfc4122 +func Parse(s string) (map[string]any, error) { + uuid, err := uuid.Parse(s) + if err != nil { + return nil, err + } + out := make(map[string]any, getVersionLen(int(uuid.Version()))) + fillMap(out, uuid) + return out, nil +} + +// Fills the map with values from the uuid. Version and variant for every version. +// Version 1-2 has decodable values that could be of use, version 4 is random, +// and version 3,5 is not feasible to extract data. Generated with either MD5 or SHA1 hash +// ref: https://datatracker.ietf.org/doc/html/rfc4122 about creation of UUIDs +func fillMap(m map[string]any, u uuid.UUID) { + m["version"] = int(u.Version()) + m["variant"] = u.Variant().String() + switch version := m["version"]; version { + case 1, 2: + m["time"] = nanoUnix(u.Time()) + m["nodeid"] = byteDecimalToHexMAC(u.NodeID(), "-") + m["macvariables"] = macVars(u.NodeID()[0]) + m["clocksequence"] = u.ClockSequence() + if version == 2 { + m["id"] = int(u.ID()) + m["domain"] = u.Domain().String() + } + } +} + +// macVars will take the first byte of a MAC-address and check for the +// local/global bit and check for the unicast/multicast bit of the byte, +// and return a string with this info. +// ref: https://datatracker.ietf.org/doc/html/rfc7042#section-2.1 +func macVars(inpb byte) string { + switch { + case inpb&byte(0b11) == byte(0b11): + return "local:multicast" + case inpb&byte(0b01) == byte(0b01): + return "global:multicast" + case inpb&byte(0b10) == byte(0b10): + return "local:unicast" + } + return "global:unicast" +} + +// loops through the byte array to convert all bytes to hexes. +// It will also put the separator between every other to make it human-readable +func byteDecimalToHexMAC(bytes []byte, sep string) string { + hexs := strings.Builder{} + l := len(bytes) + hexs.Grow((l * 3) - 1) // 1 byte -> 2 hexes + 1 separator (if one char) + + for i, b := range bytes { + hexs.WriteString(fmt.Sprintf("%02x", b)) + if i < l-1 { + hexs.WriteString(sep) + } + } + + return hexs.String() +} + +// nanoUnix Converts the uuids encoded time into unix represented time in nanoseconds +func nanoUnix(t uuid.Time) int64 { + unixsec, unixnsec := t.UnixTime() + return unixsec*BILLION + unixnsec +} + +// Helper function to make map with length based on version of uuid +// Most are 2 in length (version, variant), but version 1 and 2 have more. +func getVersionLen(version int) int { + switch version { + case 1: + return 5 + case 2: + return 7 + default: + return 2 + } +} diff --git a/third_party/opa/internal/uuid/uuid_test.go b/third_party/opa/internal/uuid/uuid_test.go new file mode 100644 index 000000000000..71686d3c4199 --- /dev/null +++ b/third_party/opa/internal/uuid/uuid_test.go @@ -0,0 +1,149 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. +package uuid + +import ( + "bytes" + "fmt" + "reflect" + "testing" +) + +func TestUUID4(t *testing.T) { + uuid, err := New(bytes.NewReader(make([]byte, 16))) + if err != nil { + t.Fatal(err) + } + expect := "00000000-0000-4000-8000-000000000000" + if uuid != expect { + t.Errorf("Expected %q, got %q", expect, uuid) + } +} + +func TestParseTrue(t *testing.T) { + var tests = []struct { + name string + input string + ans map[string]any + }{ + { + "Test uuid 1", + "c2fc67c2-47f2-11ee-b67a-9f3619c7493f", + map[string]any{ + "version": 1, + "variant": "RFC4122", + "nodeid": "9f-36-19-c7-49-3f", + "macvariables": "local:multicast", + "time": int64(1693481847404333000), + "clocksequence": 13946}, + }, + { + "Test uuid 2", + "000003e8-48b9-21ee-b200-325096b39f47", + map[string]any{ + "version": 2, + "variant": "RFC4122", + "nodeid": "32-50-96-b3-9f-47", + "macvariables": "local:unicast", + "time": int64(1693566990121469600), + "clocksequence": 12800, + "domain": "Person", + "id": 1000, + }, + }, + { + "Test uuid 3", + "6bea8ef2-d3d3-3cd1-84e0-9bab06a52ece", + map[string]any{ + "version": 3, + "variant": "RFC4122", + }, + }, + { + "Test uuid 4", + "00000000-0000-4000-8000-000000000000", + map[string]any{"version": 4, "variant": "RFC4122"}, + }, + { + "Test uuid 5", + "00000000-0000-5cd1-84e0-9bab06a52ece", + map[string]any{ + "version": 5, + "variant": "RFC4122", + }, + }, + { + "Test future version and variant", + "00000000-0000-fcd1-f4e0-9bab06a52ece", + map[string]any{ + "version": 15, + "variant": "Future", + }, + }, + { + "Test urn format", + "urn:uuid:c2fc67c2-47f2-11ee-b67a-9f3619c7493f", + map[string]any{ + "version": 1, + "variant": "RFC4122", + "nodeid": "9f-36-19-c7-49-3f", + "macvariables": "local:multicast", + "time": int64(1693481847404333000), + "clocksequence": 13946}, + }, + { + "Test uuid with brackets", + "{000003e8-48b9-21ee-b200-325096b39f47}", + map[string]any{ + "version": 2, + "variant": "RFC4122", + "nodeid": "32-50-96-b3-9f-47", + "macvariables": "local:unicast", + "time": int64(1693566990121469600), + "clocksequence": 12800, + "domain": "Person", + "id": 1000, + }, + }, + { + "Test uuid without dashes", + "00000000000040008000000000000000", + map[string]any{"version": 4, "variant": "RFC4122"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _ := Parse(tt.input) + exp := tt.ans + if !reflect.DeepEqual(got, exp) { + t.Errorf("got: %v, expected: %v", got, exp) + } + }) + } +} + +func TestParseNegative(t *testing.T) { + length, runes := "00000000000-123-222-222", "HijVnnS1-GGGG-1234-12345678" + _, err := Parse(length) + if err == nil { + t.Error("got no error, should fail since length of uuid is too long") + } + _, err = Parse(runes) + if err == nil { + t.Error("got no error, should fail since string contains other characters than hexadecimals") + } +} + +func TestMACVars(t *testing.T) { + var inp = []byte{byte(0b11111111), byte(0b11111101), byte(0b11111110), byte(0b11111100)} + var expected = []string{"local:multicast", "global:multicast", "local:unicast", "global:unicast"} + for i, b := range inp { + t.Run(fmt.Sprint("Test", i+1), func(t *testing.T) { + got := macVars(b) + if got != expected[i] { + t.Errorf("got %s, expected %s", got, expected[i]) + } + }) + } +} diff --git a/third_party/opa/internal/version/version.go b/third_party/opa/internal/version/version.go new file mode 100644 index 000000000000..1264278e44bf --- /dev/null +++ b/third_party/opa/internal/version/version.go @@ -0,0 +1,36 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package version implements helper functions for the stored version. +package version + +import ( + "context" + "fmt" + "runtime" + + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/version" +) + +var versionPath = storage.MustParsePath("/system/version") + +// Write the build version information into storage. This makes the +// version information available to the REPL and the HTTP server. +func Write(ctx context.Context, store storage.Store, txn storage.Transaction) error { + + if err := storage.MakeDir(ctx, store, txn, versionPath); err != nil { + return err + } + + return store.Write(ctx, txn, storage.AddOp, versionPath, map[string]any{ + "version": version.Version, + "build_commit": version.Vcs, + "build_timestamp": version.Timestamp, + "build_hostname": version.Hostname, + }) +} + +// UserAgent defines the current OPA instances User-Agent default header value. +var UserAgent = fmt.Sprintf("Open Policy Agent/%s (%s, %s)", version.Version, runtime.GOOS, runtime.GOARCH) diff --git a/third_party/opa/internal/wasm/constant/constant.go b/third_party/opa/internal/wasm/constant/constant.go new file mode 100644 index 000000000000..878979fb6ec7 --- /dev/null +++ b/third_party/opa/internal/wasm/constant/constant.go @@ -0,0 +1,77 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package constant contains WASM constant definitions. +package constant + +// Magic bytes at the beginning of every WASM file ("\0asm"). +const Magic = uint32(0x6D736100) + +// Version defines the WASM version. +const Version = uint32(1) + +// WASM module section IDs. +const ( + CustomSectionID uint8 = iota + TypeSectionID + ImportSectionID + FunctionSectionID + TableSectionID + MemorySectionID + GlobalSectionID + ExportSectionID + StartSectionID + ElementSectionID + CodeSectionID + DataSectionID +) + +// FunctionTypeID indicates the start of a function type definition. +const FunctionTypeID = byte(0x60) + +// ValueType represents an intrinsic value type in WASM. +const ( + ValueTypeF64 byte = iota + 0x7C + ValueTypeF32 + ValueTypeI64 + ValueTypeI32 +) + +// WASM import descriptor types. +const ( + ImportDescType byte = iota + ImportDescTable + ImportDescMem + ImportDescGlobal +) + +// WASM export descriptor types. +const ( + ExportDescType byte = iota + ExportDescTable + ExportDescMem + ExportDescGlobal +) + +// ElementTypeAnyFunc indicates the type of a table import. +const ElementTypeAnyFunc byte = 0x70 + +// BlockTypeEmpty represents a block type. +const BlockTypeEmpty byte = 0x40 + +// WASM global varialbe mutability flag. +const ( + Const byte = iota + Mutable +) + +// NameSectionCustomID is the ID of the "Name" section Custom Section +const NameSectionCustomID = "name" + +// Subtypes of the 'name' custom section +const ( + NameSectionModuleType byte = iota + NameSectionFunctionsType + NameSectionLocalsType +) diff --git a/third_party/opa/internal/wasm/encoding/doc.go b/third_party/opa/internal/wasm/encoding/doc.go new file mode 100644 index 000000000000..b2523696855f --- /dev/null +++ b/third_party/opa/internal/wasm/encoding/doc.go @@ -0,0 +1,6 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package encoding implements WASM module reading and writing. +package encoding diff --git a/third_party/opa/internal/wasm/encoding/encoding_test.go b/third_party/opa/internal/wasm/encoding/encoding_test.go new file mode 100644 index 000000000000..d922979c00e8 --- /dev/null +++ b/third_party/opa/internal/wasm/encoding/encoding_test.go @@ -0,0 +1,113 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package encoding + +import ( + "bytes" + "os" + "path/filepath" + "reflect" + "testing" + + "github.com/open-policy-agent/opa/internal/compiler/wasm/opa" + "github.com/open-policy-agent/opa/internal/wasm/module" +) + +func TestRoundtrip(t *testing.T) { + + bs, err := os.ReadFile(filepath.Join("testdata", "test1.wasm")) + if err != nil { + t.Fatal(err) + } + + module, err := ReadModule(bytes.NewBuffer(bs)) + if err != nil { + t.Fatal(err) + } + + entries, err := CodeEntries(module) + if err != nil { + t.Fatal(err) + } + + for i, e := range entries { + + var buf3 bytes.Buffer + + if err := WriteCodeEntry(&buf3, e); err != nil { + t.Fatal(err) + } + + module.Code.Segments[i].Code = buf3.Bytes() + } + + var buf2 bytes.Buffer + + if err := WriteModule(&buf2, module); err != nil { + t.Fatal(err) + } + + module2, err := ReadModule(&buf2) + if err != nil { + t.Fatal(err) + } + + // TODO(tsandall): how to make this more debuggable + if !reflect.DeepEqual(module, module2) { + t.Fatal("modules are not equal") + } + +} + +func TestRoundtripOPA(t *testing.T) { + + bs := opa.Bytes() + module1, err := ReadModule(bytes.NewBuffer(bs)) + if err != nil { + t.Fatal(err) + } + // When using a WASM module with or without debug, the custom sections differ. + // Both variants have 'producers'. + customSections := map[string]int{} + for _, s := range module1.Customs { + customSections[s.Name]++ + } + if expected, actual := 1, customSections["producers"]; expected != actual { + t.Errorf("expected %d 'producers' custom sections, found %d", expected, actual) + } + if len(module1.Names.Functions) == 0 { + t.Errorf("expected non-zero function names in 'name' custom sections") + } + + // Note(sr): We don't have this set by any other means, so manually set it, and + // check the write->read roundtrip at least. + module1.Names.Module = "foo" + module1.Names.Locals = []module.LocalNameMap{{FuncIndex: 35, NameMap: module.NameMap{Index: 0, Name: "data"}}} + // Note(sr): This isn't a great choice, but it has the right signature: type[13] () -> nil + var start uint32 = 40 // func[40] sig=13 <__force_import_opa_builtins> + module1.Start.FuncIndex = &start + + // TODO(tsandall): when all instructions are handled by reader, add logic to + // check code section contents. + + var buf2 bytes.Buffer + if err := WriteModule(&buf2, module1); err != nil { + t.Fatal(err) + } + + module2, err := ReadModule(&buf2) + if err != nil { + t.Fatal(err) + } + + if expected, actual := len(module1.Names.Functions), len(module2.Names.Functions); expected != actual { + t.Errorf("expected %d function names in 'name' custom sections, found %d", expected, actual) + } + + // TODO(tsandall): how to make this more debuggable + if !reflect.DeepEqual(module1, module2) { + t.Fatal("modules are not equal") + } +} diff --git a/third_party/opa/internal/wasm/encoding/reader.go b/third_party/opa/internal/wasm/encoding/reader.go new file mode 100644 index 000000000000..0695ce94fe72 --- /dev/null +++ b/third_party/opa/internal/wasm/encoding/reader.go @@ -0,0 +1,965 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package encoding + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + + "github.com/open-policy-agent/opa/internal/leb128" + "github.com/open-policy-agent/opa/internal/wasm/constant" + "github.com/open-policy-agent/opa/internal/wasm/instruction" + "github.com/open-policy-agent/opa/internal/wasm/module" + "github.com/open-policy-agent/opa/internal/wasm/opcode" + "github.com/open-policy-agent/opa/internal/wasm/types" +) + +// ReadModule reads a binary-encoded WASM module from r. +func ReadModule(r io.Reader) (*module.Module, error) { + + wr := &reader{r: r, n: 0} + module, err := readModule(wr) + if err != nil { + return nil, fmt.Errorf("offset 0x%x: %w", wr.n, err) + } + + return module, nil +} + +// ReadCodeEntry reads a binary-encoded WASM code entry from r. +func ReadCodeEntry(r io.Reader) (*module.CodeEntry, error) { + + wr := &reader{r: r, n: 0} + entry, err := readCodeEntry(wr) + if err != nil { + return nil, fmt.Errorf("offset 0x%x: %w", wr.n, err) + } + + return entry, nil +} + +// CodeEntries returns the WASM code entries contained in r. +func CodeEntries(m *module.Module) ([]*module.CodeEntry, error) { + + entries := make([]*module.CodeEntry, len(m.Code.Segments)) + + for i, s := range m.Code.Segments { + buf := bytes.NewBuffer(s.Code) + entry, err := ReadCodeEntry(buf) + if err != nil { + return nil, err + } + entries[i] = entry + } + + return entries, nil +} + +type reader struct { + r io.Reader + n int +} + +func (r *reader) Read(bs []byte) (int, error) { + n, err := r.r.Read(bs) + r.n += n + return n, err +} + +func readModule(r io.Reader) (*module.Module, error) { + + if err := readMagic(r); err != nil { + return nil, err + } + + if err := readVersion(r); err != nil { + return nil, err + } + + var m module.Module + + if err := readSections(r, &m); err != nil && err != io.EOF { + return nil, err + } + + return &m, nil +} + +func readCodeEntry(r io.Reader) (*module.CodeEntry, error) { + + var entry module.CodeEntry + + if err := readLocals(r, &entry.Func.Locals); err != nil { + return nil, fmt.Errorf("local declarations: %w", err) + } + + return &entry, readExpr(r, &entry.Func.Expr) +} + +func readMagic(r io.Reader) error { + var v uint32 + if err := binary.Read(r, binary.LittleEndian, &v); err != nil { + return err + } else if v != constant.Magic { + return errors.New("illegal magic value") + } + return nil +} + +func readVersion(r io.Reader) error { + var v uint32 + if err := binary.Read(r, binary.LittleEndian, &v); err != nil { + return err + } else if v != constant.Version { + return errors.New("illegal wasm version") + } + return nil +} + +func readSections(r io.Reader, m *module.Module) error { + for { + id, err := readByte(r) + if err != nil { + return err + } + + size, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + buf := make([]byte, size) + if _, err := io.ReadFull(r, buf); err != nil { + return err + } + + bufr := bytes.NewReader(buf) + + switch id { + case constant.StartSectionID: + if err := readStartSection(bufr, &m.Start); err != nil { + return fmt.Errorf("start section: %w", err) + } + case constant.CustomSectionID: + var name string + if err := readByteVectorString(bufr, &name); err != nil { + return fmt.Errorf("read custom section type: %w", err) + } + if name == "name" { + if err := readCustomNameSections(bufr, &m.Names); err != nil { + return fmt.Errorf("custom 'name' section: %w", err) + } + } else { + if err := readCustomSection(bufr, name, &m.Customs); err != nil { + return fmt.Errorf("custom section: %w", err) + } + } + case constant.TypeSectionID: + if err := readTypeSection(bufr, &m.Type); err != nil { + return fmt.Errorf("type section: %w", err) + } + case constant.ImportSectionID: + if err := readImportSection(bufr, &m.Import); err != nil { + return fmt.Errorf("import section: %w", err) + } + case constant.TableSectionID: + if err := readTableSection(bufr, &m.Table); err != nil { + return fmt.Errorf("table section: %w", err) + } + case constant.MemorySectionID: + if err := readMemorySection(bufr, &m.Memory); err != nil { + return fmt.Errorf("memory section: %w", err) + } + case constant.GlobalSectionID: + if err := readGlobalSection(bufr, &m.Global); err != nil { + return fmt.Errorf("global section: %w", err) + } + case constant.FunctionSectionID: + if err := readFunctionSection(bufr, &m.Function); err != nil { + return fmt.Errorf("function section: %w", err) + } + case constant.ExportSectionID: + if err := readExportSection(bufr, &m.Export); err != nil { + return fmt.Errorf("export section: %w", err) + } + case constant.ElementSectionID: + if err := readElementSection(bufr, &m.Element); err != nil { + return fmt.Errorf("element section: %w", err) + } + case constant.DataSectionID: + if err := readDataSection(bufr, &m.Data); err != nil { + return fmt.Errorf("data section: %w", err) + } + case constant.CodeSectionID: + if err := readRawCodeSection(bufr, &m.Code); err != nil { + return fmt.Errorf("code section: %w", err) + } + default: + return errors.New("illegal section id") + } + } +} + +func readCustomSection(r io.Reader, name string, s *[]module.CustomSection) error { + buf, err := io.ReadAll(r) + if err != nil { + return err + } + + *s = append(*s, module.CustomSection{ + Name: name, + Data: buf, + }) + return nil +} + +func readCustomNameSections(r io.Reader, s *module.NameSection) error { + for { + id, err := readByte(r) + if err != nil { + if err == io.EOF { + break + } + return err + } + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + buf := make([]byte, n) + if _, err := io.ReadFull(r, buf); err != nil { + return err + } + bufr := bytes.NewReader(buf) + switch id { + case constant.NameSectionModuleType: + err = readNameSectionModule(bufr, s) + case constant.NameSectionFunctionsType: + err = readNameSectionFunctions(bufr, s) + case constant.NameSectionLocalsType: + err = readNameSectionLocals(bufr, s) + } + if err != nil { + return err + } + } + return nil +} + +func readNameSectionModule(r io.Reader, s *module.NameSection) error { + return readByteVectorString(r, &s.Module) +} + +func readNameSectionFunctions(r io.Reader, s *module.NameSection) error { + nm, err := readNameMap(r) + if err != nil { + return err + } + s.Functions = nm + return nil +} + +func readNameMap(r io.Reader) ([]module.NameMap, error) { + n, err := leb128.ReadVarUint32(r) + if err != nil { + return nil, err + } + nm := make([]module.NameMap, n) + for i := range n { + var name string + id, err := leb128.ReadVarUint32(r) + if err != nil { + return nil, err + } + + if err := readByteVectorString(r, &name); err != nil { + return nil, err + } + nm[i] = module.NameMap{Index: id, Name: name} + } + return nm, nil +} + +func readNameSectionLocals(r io.Reader, s *module.NameSection) error { + n, err := leb128.ReadVarUint32(r) // length of vec(indirectnameassoc) + if err != nil { + return err + } + for range n { + id, err := leb128.ReadVarUint32(r) // func index + if err != nil { + return err + } + nm, err := readNameMap(r) + if err != nil { + return err + } + for _, m := range nm { + s.Locals = append(s.Locals, module.LocalNameMap{ + FuncIndex: id, + NameMap: module.NameMap{ + Index: m.Index, + Name: m.Name, + }}) + } + } + return nil +} + +func readStartSection(r io.Reader, s *module.StartSection) error { + idx, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + s.FuncIndex = &idx + return nil +} + +func readTypeSection(r io.Reader, s *module.TypeSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for range n { + + var ftype module.FunctionType + if err := readFunctionType(r, &ftype); err != nil { + return err + } + + s.Functions = append(s.Functions, ftype) + } + + return nil +} + +func readImportSection(r io.Reader, s *module.ImportSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for range n { + + var imp module.Import + + if err := readImport(r, &imp); err != nil { + return err + } + + s.Imports = append(s.Imports, imp) + } + + return nil +} + +func readTableSection(r io.Reader, s *module.TableSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for range n { + + var table module.Table + + if elem, err := readByte(r); err != nil { + return err + } else if elem != constant.ElementTypeAnyFunc { + return errors.New("illegal element type") + } + + table.Type = types.Anyfunc + + if err := readLimits(r, &table.Lim); err != nil { + return err + } + + s.Tables = append(s.Tables, table) + } + + return nil +} + +func readMemorySection(r io.Reader, s *module.MemorySection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for range n { + + var mem module.Memory + + if err := readLimits(r, &mem.Lim); err != nil { + return err + } + + s.Memories = append(s.Memories, mem) + } + + return nil +} + +func readGlobalSection(r io.Reader, s *module.GlobalSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for range n { + + var global module.Global + + if err := readGlobal(r, &global); err != nil { + return err + } + + s.Globals = append(s.Globals, global) + } + + return nil +} + +func readFunctionSection(r io.Reader, s *module.FunctionSection) error { + return readVarUint32Vector(r, &s.TypeIndices) +} + +func readExportSection(r io.Reader, s *module.ExportSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for range n { + + var exp module.Export + + if err := readExport(r, &exp); err != nil { + return err + } + + s.Exports = append(s.Exports, exp) + } + + return nil +} + +func readElementSection(r io.Reader, s *module.ElementSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for range n { + + var seg module.ElementSegment + + if err := readElementSegment(r, &seg); err != nil { + return err + } + + s.Segments = append(s.Segments, seg) + } + + return nil +} + +func readDataSection(r io.Reader, s *module.DataSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for range n { + + var seg module.DataSegment + + if err := readDataSegment(r, &seg); err != nil { + return err + } + + s.Segments = append(s.Segments, seg) + } + + return nil +} + +func readRawCodeSection(r io.Reader, s *module.RawCodeSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for range n { + var seg module.RawCodeSegment + + if err := readRawCodeSegment(r, &seg); err != nil { + return err + } + + s.Segments = append(s.Segments, seg) + } + + return nil +} + +func readFunctionType(r io.Reader, ftype *module.FunctionType) error { + + if b, err := readByte(r); err != nil { + return err + } else if b != constant.FunctionTypeID { + return fmt.Errorf("illegal function type id 0x%x", b) + } + + if err := readValueTypeVector(r, &ftype.Params); err != nil { + return err + } + + return readValueTypeVector(r, &ftype.Results) +} + +func readGlobal(r io.Reader, global *module.Global) error { + + if err := readValueType(r, &global.Type); err != nil { + return err + } + + b, err := readByte(r) + if err != nil { + return err + } + + if b == 1 { + global.Mutable = true + } else if b != 0 { + return errors.New("illegal mutability flag") + } + + return readConstantExpr(r, &global.Init) +} + +func readImport(r io.Reader, imp *module.Import) error { + + if err := readByteVectorString(r, &imp.Module); err != nil { + return err + } + + if err := readByteVectorString(r, &imp.Name); err != nil { + return err + } + + b, err := readByte(r) + if err != nil { + return err + + } + + if b == constant.ImportDescType { + index, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + imp.Descriptor = module.FunctionImport{ + Func: index, + } + return nil + } + + if b == constant.ImportDescTable { + if elem, err := readByte(r); err != nil { + return err + } else if elem != constant.ElementTypeAnyFunc { + return errors.New("illegal element type") + } + desc := module.TableImport{ + Type: types.Anyfunc, + } + if err := readLimits(r, &desc.Lim); err != nil { + return err + } + imp.Descriptor = desc + return nil + } + + if b == constant.ImportDescMem { + desc := module.MemoryImport{} + if err := readLimits(r, &desc.Mem.Lim); err != nil { + return err + } + imp.Descriptor = desc + return nil + } + + if b == constant.ImportDescGlobal { + desc := module.GlobalImport{} + if err := readValueType(r, &desc.Type); err != nil { + return err + } + b, err := readByte(r) + if err != nil { + return err + } + if b == 1 { + desc.Mutable = true + } else if b != 0 { + return errors.New("illegal mutability flag") + } + return nil + } + + return errors.New("illegal import descriptor type") +} + +func readExport(r io.Reader, exp *module.Export) error { + + if err := readByteVectorString(r, &exp.Name); err != nil { + return err + } + + b, err := readByte(r) + if err != nil { + return err + } + + switch b { + case constant.ExportDescType: + exp.Descriptor.Type = module.FunctionExportType + case constant.ExportDescTable: + exp.Descriptor.Type = module.TableExportType + case constant.ExportDescMem: + exp.Descriptor.Type = module.MemoryExportType + case constant.ExportDescGlobal: + exp.Descriptor.Type = module.GlobalExportType + default: + return errors.New("illegal export descriptor type") + } + + exp.Descriptor.Index, err = leb128.ReadVarUint32(r) + if err != nil { + return err + } + + return nil +} + +func readElementSegment(r io.Reader, seg *module.ElementSegment) error { + + if err := readVarUint32(r, &seg.Index); err != nil { + return err + } + + if err := readConstantExpr(r, &seg.Offset); err != nil { + return err + } + + return readVarUint32Vector(r, &seg.Indices) +} + +func readDataSegment(r io.Reader, seg *module.DataSegment) error { + + if err := readVarUint32(r, &seg.Index); err != nil { + return err + } + + if err := readConstantExpr(r, &seg.Offset); err != nil { + return err + } + + return readByteVector(r, &seg.Init) +} + +func readRawCodeSegment(r io.Reader, seg *module.RawCodeSegment) error { + return readByteVector(r, &seg.Code) +} + +func readConstantExpr(r io.Reader, expr *module.Expr) error { + + instrs := make([]instruction.Instruction, 0) + + for { + b, err := readByte(r) + if err != nil { + return err + } + + switch opcode.Opcode(b) { + case opcode.I32Const: + i32, err := leb128.ReadVarInt32(r) + if err != nil { + return err + } + instrs = append(instrs, instruction.I32Const{Value: i32}) + case opcode.I64Const: + i64, err := leb128.ReadVarInt64(r) + if err != nil { + return err + } + instrs = append(instrs, instruction.I64Const{Value: i64}) + case opcode.End: + expr.Instrs = instrs + return nil + default: + return fmt.Errorf("illegal constant expr opcode 0x%x", b) + } + } +} + +func readExpr(r io.Reader, expr *module.Expr) (err error) { + + defer func() { + if r := recover(); r != nil { + switch r := r.(type) { + case error: + err = r + default: + err = errors.New("unknown panic") + } + } + }() + + return readInstructions(r, &expr.Instrs) +} + +func readInstructions(r io.Reader, instrs *[]instruction.Instruction) error { + + ret := make([]instruction.Instruction, 0) + + for { + b, err := readByte(r) + if err != nil { + return err + } + + switch opcode.Opcode(b) { + case opcode.I32Const: + ret = append(ret, instruction.I32Const{Value: leb128.MustReadVarInt32(r)}) + case opcode.I64Const: + ret = append(ret, instruction.I64Const{Value: leb128.MustReadVarInt64(r)}) + case opcode.I32Eqz: + ret = append(ret, instruction.I32Eqz{}) + case opcode.GetLocal: + ret = append(ret, instruction.GetLocal{Index: leb128.MustReadVarUint32(r)}) + case opcode.SetLocal: + ret = append(ret, instruction.SetLocal{Index: leb128.MustReadVarUint32(r)}) + case opcode.Call: + ret = append(ret, instruction.Call{Index: leb128.MustReadVarUint32(r)}) + case opcode.CallIndirect: + ret = append(ret, instruction.CallIndirect{ + Index: leb128.MustReadVarUint32(r), + Reserved: mustReadByte(r), + }) + case opcode.BrIf: + ret = append(ret, instruction.BrIf{Index: leb128.MustReadVarUint32(r)}) + case opcode.Return: + ret = append(ret, instruction.Return{}) + case opcode.Block: + block := instruction.Block{} + if err := readBlockValueType(r, block.Type); err != nil { + return err + } + if err := readInstructions(r, &block.Instrs); err != nil { + return err + } + ret = append(ret, block) + case opcode.Loop: + loop := instruction.Loop{} + if err := readBlockValueType(r, loop.Type); err != nil { + return err + } + if err := readInstructions(r, &loop.Instrs); err != nil { + return err + } + ret = append(ret, loop) + case opcode.End: + *instrs = ret + return nil + default: + return fmt.Errorf("illegal opcode 0x%x", b) + } + } +} + +func mustReadByte(r io.Reader) byte { + b, err := readByte(r) + if err != nil { + panic(err) + } + return b +} + +func readLimits(r io.Reader, l *module.Limit) error { + + b, err := readByte(r) + if err != nil { + return err + } + + minLim, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + l.Min = minLim + + if b == 1 { + maxLim, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + l.Max = &maxLim + } else if b != 0 { + return errors.New("illegal limit flag") + } + + return nil +} + +func readLocals(r io.Reader, locals *[]module.LocalDeclaration) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + ret := make([]module.LocalDeclaration, n) + + for i := range n { + if err := readVarUint32(r, &ret[i].Count); err != nil { + return err + } + if err := readValueType(r, &ret[i].Type); err != nil { + return err + } + } + + *locals = ret + return nil +} + +func readByteVector(r io.Reader, v *[]byte) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + buf := make([]byte, n) + if _, err := io.ReadFull(r, buf); err != nil { + return err + } + + *v = buf + return nil +} + +func readByteVectorString(r io.Reader, v *string) error { + + var buf []byte + + if err := readByteVector(r, &buf); err != nil { + return err + } + + *v = string(buf) + return nil +} + +func readVarUint32Vector(r io.Reader, v *[]uint32) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + ret := make([]uint32, n) + + for i := range n { + if err := readVarUint32(r, &ret[i]); err != nil { + return err + } + } + + *v = ret + return nil +} + +func readValueTypeVector(r io.Reader, v *[]types.ValueType) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + ret := make([]types.ValueType, n) + + for i := range n { + if err := readValueType(r, &ret[i]); err != nil { + return err + } + } + + *v = ret + return nil +} + +func readVarUint32(r io.Reader, v *uint32) error { + var err error + *v, err = leb128.ReadVarUint32(r) + return err +} + +func readValueType(r io.Reader, v *types.ValueType) error { + if b, err := readByte(r); err != nil { + return err + } else if b == constant.ValueTypeI32 { + *v = types.I32 + } else if b == constant.ValueTypeI64 { + *v = types.I64 + } else if b == constant.ValueTypeF32 { + *v = types.F32 + } else if b == constant.ValueTypeF64 { + *v = types.F64 + } else { + return fmt.Errorf("illegal value type: 0x%x", b) + } + return nil +} + +func readBlockValueType(r io.Reader, v *types.ValueType) error { + if b, err := readByte(r); err != nil { + return err + } else if b == constant.ValueTypeI32 { + *v = types.I32 + } else if b == constant.ValueTypeI64 { + *v = types.I64 + } else if b == constant.ValueTypeF32 { + *v = types.F32 + } else if b == constant.ValueTypeF64 { + *v = types.F64 + } else if b != constant.BlockTypeEmpty { + return fmt.Errorf("illegal value type: 0x%x", b) + } + return nil +} + +func readByte(r io.Reader) (byte, error) { + buf := make([]byte, 1) + _, err := io.ReadFull(r, buf) + return buf[0], err +} diff --git a/third_party/opa/internal/wasm/encoding/testdata/test1.wasm b/third_party/opa/internal/wasm/encoding/testdata/test1.wasm new file mode 100644 index 0000000000000000000000000000000000000000..b66da80e17f4a317d5644c344a99a99be61d3c08 GIT binary patch literal 409 zcmYL_zfQw25XL{-{YRsM5n@1MNZzDIsu)3{h%t5BqfM3AC3cFK(v^vg_u`Fkno1e& zr2E}>-<=KZ3I>2lvEX&h>IG{P_#Yb!yVsW?(w(y=-Rsg3(7aOygcG z8^d|`7pOaYqo{7R$6e$+c1BW_}qPv3p)~@k5o1=2!=A~WY`RQej b(3a7?tCl-jc?Z1?9|pbhnKk$ (" + strings.Join(results, ", ") + ")" +} + +// Equal returns true if tpe equals other. +func (tpe FunctionType) Equal(other FunctionType) bool { + + if len(tpe.Params) != len(other.Params) || len(tpe.Results) != len(other.Results) { + return false + } + + for i := range tpe.Params { + if tpe.Params[i] != other.Params[i] { + return false + } + } + + for i := range tpe.Results { + if tpe.Results[i] != other.Results[i] { + return false + } + } + + return true +} + +func (imp Import) String() string { + return fmt.Sprintf("%v %v.%v", imp.Descriptor.String(), imp.Module, imp.Name) +} + +func (exp Export) String() string { + return fmt.Sprintf("%v[%v] %v", exp.Descriptor.Type, exp.Descriptor.Index, exp.Name) +} + +func (seg RawCodeSegment) String() string { + return fmt.Sprintf("", len(seg.Code)) +} + +func (seg DataSegment) String() string { + return fmt.Sprintf("", seg.Index, seg.Offset, len(seg.Init)) +} + +func (e Expr) String() string { + return fmt.Sprintf("%d instr(s)", len(e.Instrs)) +} + +func (lim Limit) String() string { + if lim.Max == nil { + return fmt.Sprintf("min=%v", lim.Min) + } + return fmt.Sprintf("min=%v max=%v", lim.Min, lim.Max) +} diff --git a/third_party/opa/internal/wasm/module/pretty.go b/third_party/opa/internal/wasm/module/pretty.go new file mode 100644 index 000000000000..2b28ad85b395 --- /dev/null +++ b/third_party/opa/internal/wasm/module/pretty.go @@ -0,0 +1,84 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package module + +import ( + "encoding/hex" + "fmt" + "io" +) + +// PrettyOption defines options for controlling pretty printing. +type PrettyOption struct { + Contents bool // show raw byte content of data+code sections. +} + +// Pretty writes a human-readable representation of m to w. +func Pretty(w io.Writer, m *Module, opts ...PrettyOption) { + fmt.Fprintln(w, "version:", m.Version) + fmt.Fprintln(w, "types:") + for _, fn := range m.Type.Functions { + fmt.Fprintln(w, " -", fn) + } + fmt.Fprintln(w, "imports:") + for i, imp := range m.Import.Imports { + if imp.Descriptor.Kind() == FunctionImportType { + fmt.Printf(" - [%d] %v\n", i, imp) + } else { + fmt.Fprintln(w, " -", imp) + } + } + fmt.Fprintln(w, "functions:") + for _, fn := range m.Function.TypeIndices { + if fn >= uint32(len(m.Type.Functions)) { + fmt.Fprintln(w, " -", "???") + } else { + fmt.Fprintln(w, " -", m.Type.Functions[fn]) + } + } + fmt.Fprintln(w, "exports:") + for _, exp := range m.Export.Exports { + fmt.Fprintln(w, " -", exp) + } + fmt.Fprintln(w, "code:") + for _, seg := range m.Code.Segments { + fmt.Fprintln(w, " -", seg) + } + fmt.Fprintln(w, "data:") + for _, seg := range m.Data.Segments { + fmt.Fprintln(w, " -", seg) + } + if len(opts) == 0 { + return + } + fmt.Fprintln(w) + for _, opt := range opts { + if opt.Contents { + newline := false + if len(m.Data.Segments) > 0 { + fmt.Fprintln(w, "data section:") + for _, seg := range m.Data.Segments { + if newline { + fmt.Fprintln(w) + } + fmt.Fprintln(w, hex.Dump(seg.Init)) + newline = true + } + newline = false + } + if len(m.Code.Segments) > 0 { + fmt.Fprintln(w, "code section:") + for _, seg := range m.Code.Segments { + if newline { + fmt.Fprintln(w) + } + fmt.Fprintln(w, hex.Dump(seg.Code)) + newline = true + } + newline = false + } + } + } +} diff --git a/third_party/opa/internal/wasm/opcode/opcode.go b/third_party/opa/internal/wasm/opcode/opcode.go new file mode 100644 index 000000000000..7d35a3012c74 --- /dev/null +++ b/third_party/opa/internal/wasm/opcode/opcode.go @@ -0,0 +1,218 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package opcode contains constants and utilities for working with WASM opcodes. +package opcode + +// Opcode represents a WASM instruction opcode. +type Opcode byte + +// Control instructions. +const ( + Unreachable Opcode = iota + Nop + Block + Loop + If + Else +) + +const ( + // End defines the special end WASM opcode. + End Opcode = 0x0B +) + +// Extended control instructions. +const ( + Br Opcode = iota + 0x0C + BrIf + BrTable + Return + Call + CallIndirect +) + +// Parameter instructions. +const ( + Drop Opcode = iota + 0x1A + Select +) + +// Variable instructions. +const ( + GetLocal Opcode = iota + 0x20 + SetLocal + TeeLocal + GetGlobal + SetGlobal +) + +// Memory instructions. +const ( + I32Load Opcode = iota + 0x28 + I64Load + F32Load + F64Load + I32Load8S + I32Load8U + I32Load16S + I32Load16U + I64Load8S + I64Load8U + I64Load16S + I64Load16U + I64Load32S + I64Load32U + I32Store + I64Store + F32Store + F64Store + I32Store8 + I32Store16 + I64Store8 + I64Store16 + I64Store32 + MemorySize + MemoryGrow +) + +// Numeric instructions. +const ( + I32Const Opcode = iota + 0x41 + I64Const + F32Const + F64Const + + I32Eqz + I32Eq + I32Ne + I32LtS + I32LtU + I32GtS + I32GtU + I32LeS + I32LeU + I32GeS + I32GeU + + I64Eqz + I64Eq + I64Ne + I64LtS + I64LtU + I64GtS + I64GtU + I64LeS + I64LeU + I64GeS + I64GeU + + F32Eq + F32Ne + F32Lt + F32Gt + F32Le + F32Ge + + F64Eq + F64Ne + F64Lt + F64Gt + F64Le + F64Ge + + I32Clz + I32Ctz + I32Popcnt + I32Add + I32Sub + I32Mul + I32DivS + I32DivU + I32RemS + I32RemU + I32And + I32Or + I32Xor + I32Shl + I32ShrS + I32ShrU + I32Rotl + I32Rotr + + I64Clz + I64Ctz + I64Popcnt + I64Add + I64Sub + I64Mul + I64DivS + I64DivU + I64RemS + I64RemU + I64And + I64Or + I64Xor + I64Shl + I64ShrS + I64ShrU + I64Rotl + I64Rotr + + F32Abs + F32Neg + F32Ceil + F32Floor + F32Trunc + F32Nearest + F32Sqrt + F32Add + F32Sub + F32Mul + F32Div + F32Min + F32Max + F32Copysign + + F64Abs + F64Neg + F64Ceil + F64Floor + F64Trunc + F64Nearest + F64Sqrt + F64Add + F64Sub + F64Mul + F64Div + F64Min + F64Max + F64Copysign + + I32WrapI64 + I32TruncSF32 + I32TruncUF32 + I32TruncSF64 + I32TruncUF64 + I64ExtendSI32 + I64ExtendUI32 + I64TruncSF32 + I64TruncUF32 + I64TruncSF64 + I64TruncUF64 + F32ConvertSI32 + F32ConvertUI32 + F32ConvertSI64 + F32ConvertUI64 + F32DemoteF64 + F64ConvertSI32 + F64ConvertUI32 + F64ConvertSI64 + F64ConvertUI64 + F64PromoteF32 + I32ReinterpretF32 + I64ReinterpretF64 + F32ReinterpretI32 + F64ReinterpretI64 +) diff --git a/third_party/opa/internal/wasm/sdk/README.md b/third_party/opa/internal/wasm/sdk/README.md new file mode 100644 index 000000000000..af407db98413 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/README.md @@ -0,0 +1,7 @@ +**Work in Progress -- Contributions welcome!** + +# Open Policy Agent WebAssemby Go SDK +This is the source for the Open Policy Agent WebAssembly Go SDK which +is a small go library for using WebAssembly (wasm) compiled [Open +Policy Agent](https://www.openpolicyagent.org/) Rego policies. + diff --git a/third_party/opa/internal/wasm/sdk/examples/basic/.gitignore b/third_party/opa/internal/wasm/sdk/examples/basic/.gitignore new file mode 100644 index 000000000000..84e293ff00a5 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/examples/basic/.gitignore @@ -0,0 +1,2 @@ +bundle.tar.gz +*.wasm \ No newline at end of file diff --git a/third_party/opa/internal/wasm/sdk/examples/basic/README.md b/third_party/opa/internal/wasm/sdk/examples/basic/README.md new file mode 100644 index 000000000000..8025984ba73b --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/examples/basic/README.md @@ -0,0 +1,44 @@ +Basic Wasm Module Evaluation Example +==================================== + +The [main.go](main.go) example demonstrates the loading and executing of OPA +produced wasm policy binary. + +## Setup + +The example directory includes some Rego source files. The first step is to +compile them into Wasm modules. Run: + +```shell +opa build -t wasm -e example/allow ./example-1.rego +``` + +This will generate a `bundle.tar.gz` in your current directory which has the Wasm module included. Extract the module with: + +```shell +tar -zxvf bundle.tar.gz /policy.wasm +mv policy.wasm example-1.wasm +``` + +Repeat the process for the second example Wasm binary: +```shell +opa build -t wasm -e example/allow ./example-2.rego +tar -zxvf bundle.tar.gz /policy.wasm +mv policy.wasm example-2.wasm +``` + +The final result should be two Wasm modules: + +``` +example-1.wasm +example-2.wasm +``` + +## Running the Example + +After the Wasm binaries are available run the example with +```shell +go run main.go . +``` +> This should be run from the same directory as `main.go`, alternatively provide + a path to the directory which contains the Wasm binaries. \ No newline at end of file diff --git a/third_party/opa/internal/wasm/sdk/examples/basic/example-1.rego b/third_party/opa/internal/wasm/sdk/examples/basic/example-1.rego new file mode 100644 index 000000000000..cd8c2a63e711 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/examples/basic/example-1.rego @@ -0,0 +1,3 @@ +package example + +allow := input.foo diff --git a/third_party/opa/internal/wasm/sdk/examples/basic/example-2.rego b/third_party/opa/internal/wasm/sdk/examples/basic/example-2.rego new file mode 100644 index 000000000000..b14ea3b23243 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/examples/basic/example-2.rego @@ -0,0 +1,3 @@ +package example + +allow := input.bar diff --git a/third_party/opa/internal/wasm/sdk/examples/basic/main.go b/third_party/opa/internal/wasm/sdk/examples/basic/main.go new file mode 100644 index 000000000000..62c9ce69f8f5 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/examples/basic/main.go @@ -0,0 +1,106 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. +package main + +import ( + "context" + "fmt" + "os" + "path" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa" +) + +// main demonstrates the loading and executing of OPA produced wasm +// policy binary. To execute run 'go run main.go .' in the directory +// of the main.go. +func main() { + if len(os.Args) < 2 { + fmt.Printf("%s: first argument must a path to a directory with example-1.wasm and example-2.wasm.\n", os.Args[0]) + return + } + + directory := os.Args[1] + + // Setup the SDK + + policy, err := os.ReadFile(path.Join(directory, "example-1.wasm")) + if err != nil { + fmt.Printf("error: %v\n", err) + return + } + + rego, err := opa.New().WithPolicyBytes(policy).Init() + if err != nil { + fmt.Printf("error: %v\n", err) + return + } + + defer rego.Close() + + // Evaluate the policy once. + + var input any = map[string]any{ + "foo": true, + "bar": false, + } + + ctx := context.Background() + + eps, err := rego.Entrypoints(ctx) + if err != nil { + fmt.Printf("error: %v\n", err) + return + } + + entrypointID, ok := eps["example/allow"] + if !ok { + fmt.Println("error: Unable to find entrypoint 'example/allow'") + return + } + + result, err := rego.Eval(ctx, opa.EvalOpts{Entrypoint: entrypointID, Input: &input}) + if err != nil { + fmt.Printf("error: %v\n", err) + return + } + + fmt.Printf("Policy 1 result: %v\n", result) + + // Update the policy on the fly. + + policy, err = os.ReadFile(path.Join(directory, "example-2.wasm")) + if err != nil { + fmt.Printf("error: %v\n", err) + return + } + + // Get an updated entrypoint ID, they may have changed! + eps, err = rego.Entrypoints(ctx) + if err != nil { + fmt.Printf("error: %v\n", err) + return + } + + entrypointID, ok = eps["example/allow"] + if !ok { + fmt.Println("error: Unable to find entrypoint 'example/allow'") + return + } + + // Evaluate the new policy. + + if err := rego.SetPolicy(ctx, policy); err != nil { + fmt.Printf("error: %v\n", err) + return + } + + result, err = rego.Eval(ctx, opa.EvalOpts{Entrypoint: entrypointID, Input: &input}) + if err != nil { + fmt.Printf("error: %v\n", err) + return + } + + fmt.Printf("Policy 2 result: %v\n", result) +} diff --git a/third_party/opa/internal/wasm/sdk/examples/loaders/.gitignore b/third_party/opa/internal/wasm/sdk/examples/loaders/.gitignore new file mode 100644 index 000000000000..84e293ff00a5 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/examples/loaders/.gitignore @@ -0,0 +1,2 @@ +bundle.tar.gz +*.wasm \ No newline at end of file diff --git a/third_party/opa/internal/wasm/sdk/examples/loaders/README.md b/third_party/opa/internal/wasm/sdk/examples/loaders/README.md new file mode 100644 index 000000000000..deb74e64b604 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/examples/loaders/README.md @@ -0,0 +1,25 @@ +Loader Example +============== + +[main.go](main.go) loads a bundle either from a file or HTTP server. + +## Setup + +The example directory includes some Rego source files. The first step is to +compile them into Wasm modules. Run: + +```shell +opa build -t wasm -e example/allow ./example.rego +``` + +## Running the Example + +In the directory of the main.go, execute: +``` +go run main.go bundle.tar.gz +``` +To load the accompanied bundle file. Similarly, execute: +``` +go run main.go http://url/to/bundle.tar.gz +``` +to test downloading the bundle from a HTTP server. diff --git a/third_party/opa/internal/wasm/sdk/examples/loaders/example.rego b/third_party/opa/internal/wasm/sdk/examples/loaders/example.rego new file mode 100644 index 000000000000..cd8c2a63e711 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/examples/loaders/example.rego @@ -0,0 +1,3 @@ +package example + +allow := input.foo diff --git a/third_party/opa/internal/wasm/sdk/examples/loaders/main.go b/third_party/opa/internal/wasm/sdk/examples/loaders/main.go new file mode 100644 index 000000000000..008706fc1288 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/examples/loaders/main.go @@ -0,0 +1,124 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. +package main + +import ( + "context" + "fmt" + gohttp "net/http" + "net/url" + "os" + "time" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa" + opaLoader "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/loader" + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/loader/file" + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/loader/http" +) + +var ( + loader opaLoader.Loader + rego *opa.OPA +) + +func main() { + if len(os.Args) < 2 { + fmt.Printf("provide URL or file\n") + return + } + + url := os.Args[1] + token := "" + if len(os.Args) >= 3 { + token = os.Args[2] + } + + // Setup the SDK, either with HTTP bundle loader or file bundle loader. + + if err := setup(url, token); err != nil { + fmt.Printf("error: %v\n", err) + return + } + + defer cleanup() + + // Evaluate the policy. + + var input any = map[string]any{ + "foo": true, + } + + ctx := context.Background() + + eps, err := rego.Entrypoints(ctx) + if err != nil { + fmt.Printf("error: %v\n", err) + return + } + + entrypointID, ok := eps["example/allow"] + if !ok { + fmt.Println("error: Unable to find entrypoint 'example/allow'") + return + } + + result, err := rego.Eval(ctx, opa.EvalOpts{Entrypoint: entrypointID, Input: &input}) + if err != nil { + fmt.Printf("error: %v\n", err) + return + } + + fmt.Printf("Policy result: %v\n", result) +} + +func setup(u string, token string) error { + var err error + rego, err = opa.New().Init() + if err != nil { + return err + } + + url, err := url.Parse(u) + if err != nil { + return err + } + + switch url.Scheme { + case "http", "https": + loader, err = http.New(rego). + WithURL(url.String()). + WithPrepareRequest(func(req *gohttp.Request) error { + if token != "" { + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token)) + } + return nil + }). + WithInterval(30*time.Second, 60*time.Second). + Init() + case "file", "": + loader, err = file.New(rego). + WithFile(url.String()). + WithInterval(10 * time.Second). + Init() + } + + if err != nil { + return err + } + + if err := loader.Start(context.Background()); err != nil { + return err + } + + return nil +} + +func cleanup() { + if loader != nil { + loader.Close() + } + if rego != nil { + rego.Close() + } +} diff --git a/third_party/opa/internal/wasm/sdk/internal/wasm/bindings.go b/third_party/opa/internal/wasm/sdk/internal/wasm/bindings.go new file mode 100644 index 000000000000..33ee7e451a97 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/internal/wasm/bindings.go @@ -0,0 +1,281 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package wasm + +import ( + "bytes" + "context" + "crypto/rand" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "strconv" + "time" + + wasmtime "github.com/bytecodealliance/wasmtime-go/v3" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/print" +) + +func opaFunctions(dispatcher *builtinDispatcher, store *wasmtime.Store) map[string]wasmtime.AsExtern { + + i32 := wasmtime.NewValType(wasmtime.KindI32) + + externs := map[string]wasmtime.AsExtern{ + "opa_abort": wasmtime.NewFunc(store, wasmtime.NewFuncType([]*wasmtime.ValType{i32}, nil), opaAbort), + "opa_builtin0": wasmtime.NewFunc(store, wasmtime.NewFuncType([]*wasmtime.ValType{i32, i32}, []*wasmtime.ValType{i32}), dispatcher.Call), + "opa_builtin1": wasmtime.NewFunc(store, wasmtime.NewFuncType([]*wasmtime.ValType{i32, i32, i32}, []*wasmtime.ValType{i32}), dispatcher.Call), + "opa_builtin2": wasmtime.NewFunc(store, wasmtime.NewFuncType([]*wasmtime.ValType{i32, i32, i32, i32}, []*wasmtime.ValType{i32}), dispatcher.Call), + "opa_builtin3": wasmtime.NewFunc(store, wasmtime.NewFuncType([]*wasmtime.ValType{i32, i32, i32, i32, i32}, []*wasmtime.ValType{i32}), dispatcher.Call), + "opa_builtin4": wasmtime.NewFunc(store, wasmtime.NewFuncType([]*wasmtime.ValType{i32, i32, i32, i32, i32, i32}, []*wasmtime.ValType{i32}), dispatcher.Call), + "opa_println": wasmtime.NewFunc(store, wasmtime.NewFuncType([]*wasmtime.ValType{i32}, nil), opaPrintln), + } + + return externs +} + +func opaAbort(caller *wasmtime.Caller, args []wasmtime.Val) ([]wasmtime.Val, *wasmtime.Trap) { + + data := caller.GetExport("memory").Memory().UnsafeData(caller)[args[0].I32():] + + n := bytes.IndexByte(data, 0) + if n == -1 { + panic("invalid abort argument") + } + + panic(abortError{message: string(data[:n])}) +} + +func opaPrintln(caller *wasmtime.Caller, args []wasmtime.Val) ([]wasmtime.Val, *wasmtime.Trap) { + data := caller.GetExport("memory").Memory().UnsafeData(caller)[args[0].I32():] + + n := bytes.IndexByte(data, 0) + if n == -1 { + panic("invalid opa_println argument") + } + + fmt.Fprintln(os.Stderr, string(data[:n])) + return nil, nil +} + +type builtinDispatcher struct { + ctx *topdown.BuiltinContext + builtins map[int32]topdown.BuiltinFunc +} + +func newBuiltinDispatcher() *builtinDispatcher { + return &builtinDispatcher{} +} + +func (d *builtinDispatcher) SetMap(m map[int32]topdown.BuiltinFunc) { + d.builtins = m +} + +// Reset is called in Eval before using the builtinDispatcher. +func (d *builtinDispatcher) Reset(ctx context.Context, + seed io.Reader, + ns time.Time, + iqbCache cache.InterQueryCache, + ndbCache builtins.NDBCache, + ph print.Hook, + capabilities *ast.Capabilities) { + if ns.IsZero() { + ns = time.Now() + } + if seed == nil { + seed = rand.Reader + } + d.ctx = &topdown.BuiltinContext{ + Context: ctx, + Metrics: metrics.New(), + Seed: seed, + Time: ast.NumberTerm(json.Number(strconv.FormatInt(ns.UnixNano(), 10))), + Cancel: topdown.NewCancel(), + Runtime: nil, + Cache: make(builtins.Cache), + Location: nil, + Tracers: nil, + QueryTracers: nil, + QueryID: 0, + ParentID: 0, + InterQueryBuiltinCache: iqbCache, + NDBuiltinCache: ndbCache, + PrintHook: ph, + Capabilities: capabilities, + } + +} + +func (d *builtinDispatcher) Call(caller *wasmtime.Caller, args []wasmtime.Val) (result []wasmtime.Val, trap *wasmtime.Trap) { + + if d.ctx == nil { + panic("unreachable: uninitialized built-in dispatcher context") + } + + if d.builtins == nil { + panic("unreachable: uninitialized built-in dispatcher index") + } + + // Bridge ctx <-> topdown.Cancel + // + // If the ctx is cancelled (deadline expired, or manually cancelled), this will + // cause all topdown-builtins (host functions in wasm terms) to be aborted; if + // they check for this. That check occurrs in certain potentially-long-running + // builtins, currently only net.cidr_expand. + // Other potentially-long-running builtins use the passed context, forwarding + // it into stdlib functions: http.send + // The context-scenario should work out-of-the-box; the topdown.Cancel scenario + // is wired up via the go routine below. + done := make(chan struct{}) + defer close(done) + go func() { + select { + case <-done: + case <-d.ctx.Context.Done(): + d.ctx.Cancel.Cancel() + } + }() + + // We don't care for ctx cancellation in the exports called here: they are + // wasm module exports that the host function can make use of. + // If the ctx is cancelled, and we're evaluation this call stack: + // + // wasm func + // \---> host func [(*builtinDispatcher).Call] + // \---> wasm func [exports] + // + // then the ctx <-> interrupt bridging done in internal/wasm/vm.g will + // already have taken care of signalling the interrupt to the wasm + // instance. The instances checks for interrupts that may have happened + // at the head of every loop, and in the prologue of every function. + // + // See https://docs.wasmtime.dev/api/wasmtime/struct.Store.html#when-are-interrupts-delivered + + exports := getExports(caller) + + var convertedArgs []*ast.Term + + // first two args are the built-in identifier and context structure + for i := 2; i < len(args); i++ { + + x, err := fromWasmValue(caller, exports, args[i].I32()) + if err != nil { + panic(builtinError{err: err}) + } + + convertedArgs = append(convertedArgs, x) + } + + var output *ast.Term + + err := d.builtins[args[0].I32()](*d.ctx, convertedArgs, func(t *ast.Term) error { + output = t + return nil + }) + if err != nil { + if errors.As(err, &topdown.Halt{}) { + var e *topdown.Error + if errors.As(err, &e) && e.Code == topdown.CancelErr { + panic(cancelledError{message: e.Message}) + } + panic(builtinError{err: err}) + } + // non-halt errors are treated as undefined ("non-strict eval" is the only + // mode in wasm), the `output == nil` case below will return NULL + } + + // if output is undefined, return NULL + if output == nil { + return []wasmtime.Val{wasmtime.ValI32(0)}, nil + } + + addr, err := toWasmValue(caller, exports, output) + if err != nil { + panic(builtinError{err: err}) + } + + return []wasmtime.Val{wasmtime.ValI32(addr)}, nil +} + +type exports struct { + Memory *wasmtime.Memory + mallocFn *wasmtime.Func + valueDumpFn *wasmtime.Func + valueParseFn *wasmtime.Func +} + +func getExports(c *wasmtime.Caller) exports { + var e exports + e.Memory = c.GetExport("memory").Memory() + e.mallocFn = c.GetExport("opa_malloc").Func() + e.valueDumpFn = c.GetExport("opa_value_dump").Func() + e.valueParseFn = c.GetExport("opa_value_parse").Func() + return e +} + +func (e exports) Malloc(caller *wasmtime.Caller, length int32) (int32, error) { + ptr, err := e.mallocFn.Call(caller, length) + if err != nil { + return 0, err + } + return ptr.(int32), nil +} + +func (e exports) ValueDump(caller *wasmtime.Caller, addr int32) (int32, error) { + result, err := e.valueDumpFn.Call(caller, addr) + if err != nil { + return 0, err + } + return result.(int32), nil +} + +func (e exports) ValueParse(caller *wasmtime.Caller, addr int32, length int32) (int32, error) { + result, err := e.valueParseFn.Call(caller, addr, length) + if err != nil { + return 0, err + } + return result.(int32), nil +} + +func fromWasmValue(caller *wasmtime.Caller, e exports, addr int32) (*ast.Term, error) { + + serialized, err := e.ValueDump(caller, addr) + if err != nil { + return nil, err + } + + data := e.Memory.UnsafeData(caller)[serialized:] + n := bytes.IndexByte(data, 0) + if n < 0 { + return nil, errors.New("invalid serialized value address") + } + + return ast.ParseTerm(string(data[0:n])) +} + +func toWasmValue(caller *wasmtime.Caller, e exports, term *ast.Term) (int32, error) { + + raw := []byte(term.String()) + n := int32(len(raw)) + p, err := e.Malloc(caller, n) + if err != nil { + return 0, err + } + + copy(e.Memory.UnsafeData(caller)[p:p+n], raw) + addr, err := e.ValueParse(caller, p, n) + if err != nil { + return 0, err + } + + return addr, nil +} diff --git a/third_party/opa/internal/wasm/sdk/internal/wasm/pool.go b/third_party/opa/internal/wasm/sdk/internal/wasm/pool.go new file mode 100644 index 000000000000..6bc16c9b1fd1 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/internal/wasm/pool.go @@ -0,0 +1,368 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package wasm + +import ( + "bytes" + "context" + "sync" + + wasmtime "github.com/bytecodealliance/wasmtime-go/v3" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/errors" + "github.com/open-policy-agent/opa/internal/wasm/util" + "github.com/open-policy-agent/opa/v1/metrics" +) + +var errNotReady = errors.New(errors.NotReadyErr, "") + +// Pool maintains a pool of WebAssemly VM instances. +type Pool struct { + engine *wasmtime.Engine + available chan struct{} + mutex sync.Mutex + dataMtx sync.Mutex + initialized bool + closed bool + policy []byte + parsedData []byte // Parsed parsedData memory segment, used to seed new VM's + parsedDataAddr int32 // Address for parsedData value root, used to seed new VM's + memoryMinPages uint32 + memoryMaxPages uint32 + vms []*VM // All current VM instances, acquired or not. + acquired []bool + pendingReinit *VM + blockedReinit chan struct{} +} + +// NewPool constructs a new pool with the pool and VM configuration provided. +func NewPool(poolSize, memoryMinPages, memoryMaxPages uint32) *Pool { + + cfg := wasmtime.NewConfig() + cfg.SetEpochInterruption(true) + + available := make(chan struct{}, poolSize) + for range poolSize { + available <- struct{}{} + } + + return &Pool{ + engine: wasmtime.NewEngineWithConfig(cfg), + memoryMinPages: memoryMinPages, + memoryMaxPages: memoryMaxPages, + available: available, + vms: make([]*VM, 0), + acquired: make([]bool, 0), + } +} + +// ParsedData returns a reference to the pools parsed external data used to +// initialize new VM's. +func (p *Pool) ParsedData() (int32, []byte) { + p.mutex.Lock() + defer p.mutex.Unlock() + return p.parsedDataAddr, p.parsedData +} + +// Policy returns the raw policy Wasm module used by VM's in the pool +func (p *Pool) Policy() []byte { + p.mutex.Lock() + defer p.mutex.Unlock() + return p.policy +} + +// Size returns the current number of VM's in the pool +func (p *Pool) Size() int { + return len(p.vms) +} + +// Acquire obtains a VM from the pool, waiting if all VMms are in use +// and building one as necessary. Returns either ErrNotReady or +// ErrInternal if an error. +func (p *Pool) Acquire(ctx context.Context, metrics metrics.Metrics) (*VM, error) { + metrics.Timer("wasm_pool_acquire").Start() + defer metrics.Timer("wasm_pool_acquire").Stop() + + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-p.available: + } + + p.mutex.Lock() + defer p.mutex.Unlock() + + if !p.initialized || p.closed { + return nil, errNotReady + } + + for i, vm := range p.vms { + if !p.acquired[i] { + p.acquired[i] = true + return vm, nil + } + } + + policy, parsedData, parsedDataAddr := p.policy, p.parsedData, p.parsedDataAddr + + p.mutex.Unlock() + vm, err := newVM(vmOpts{ + policy: policy, + data: nil, + parsedData: parsedData, + parsedDataAddr: parsedDataAddr, + memoryMin: p.memoryMinPages, + memoryMax: p.memoryMaxPages, + }, p.engine) + p.mutex.Lock() + + if err != nil { + p.available <- struct{}{} + return nil, errors.New(errors.InternalErr, err.Error()) + } + + p.acquired = append(p.acquired, true) + p.vms = append(p.vms, vm) + return vm, nil +} + +// Release releases the VM back to the pool. +func (p *Pool) Release(vm *VM, metrics metrics.Metrics) { + metrics.Timer("wasm_pool_release").Start() + defer metrics.Timer("wasm_pool_release").Stop() + + p.mutex.Lock() + + // If the policy data setting is waiting for this one, don't release it back to the general consumption. + // Note the reinit is responsible for pushing to available channel once done with the VM. + if vm == p.pendingReinit { + p.mutex.Unlock() + p.blockedReinit <- struct{}{} + return + } + + for i := range p.vms { + if p.vms[i] == vm { + p.acquired[i] = false + p.mutex.Unlock() + p.available <- struct{}{} + return + } + } + + // VM instance not found anymore, hence pool reconfigured and can release the VM. + + p.mutex.Unlock() + p.available <- struct{}{} +} + +// SetPolicyData re-initializes the vms within the pool with the new policy +// and data. The re-initialization takes place atomically: all new vms +// are constructed in advance before touching the pool. Returns +// either ErrNotReady, ErrInvalidPolicy or ErrInternal if an error +// occurs. +func (p *Pool) SetPolicyData(ctx context.Context, policy []byte, data []byte) error { + p.dataMtx.Lock() + defer p.dataMtx.Unlock() + + p.mutex.Lock() + + if !p.initialized { + vm, err := newVM(vmOpts{ + policy: policy, + data: data, + parsedData: nil, + parsedDataAddr: 0, + memoryMin: p.memoryMinPages, + memoryMax: p.memoryMaxPages, + }, p.engine) + + if err == nil { + parsedDataAddr, parsedData := vm.cloneDataSegment() + p.memoryMinPages = util.Pages(uint32(vm.memory.DataSize(vm.store))) + p.vms = append(p.vms, vm) + p.acquired = append(p.acquired, false) + p.initialized = true + p.policy, p.parsedData, p.parsedDataAddr = policy, parsedData, parsedDataAddr + } else { + err = errors.New(errors.InvalidPolicyOrDataErr, err.Error()) + } + + p.mutex.Unlock() + return err + } + + if p.closed { + p.mutex.Unlock() + return errNotReady + } + + currentPolicy, currentData := p.policy, p.parsedData + p.mutex.Unlock() + + if bytes.Equal(policy, currentPolicy) && bytes.Equal(data, currentData) { + return nil + } + + err := p.setPolicyData(ctx, policy, data) + if err != nil { + return errors.New(errors.InternalErr, err.Error()) + } + + return nil +} + +// SetDataPath will update the current data on the VMs by setting the value at the +// specified path. If an error occurs the instance is still in a valid state, however +// the data will not have been modified. +func (p *Pool) SetDataPath(ctx context.Context, path []string, value any) error { + p.dataMtx.Lock() + defer p.dataMtx.Unlock() + return p.updateVMs(func(vm *VM, _ vmOpts) error { + return vm.SetDataPath(ctx, path, value) + }) +} + +// RemoveDataPath will update the current data on the VMs by removing the value at the +// specified path. If an error occurs the instance is still in a valid state, however +// the data will not have been modified. +func (p *Pool) RemoveDataPath(ctx context.Context, path []string) error { + p.dataMtx.Lock() + defer p.dataMtx.Unlock() + return p.updateVMs(func(vm *VM, _ vmOpts) error { + return vm.RemoveDataPath(ctx, path) + }) +} + +// setPolicyData reinitializes the VMs one at a time. +func (p *Pool) setPolicyData(ctx context.Context, policy []byte, data []byte) error { + return p.updateVMs(func(vm *VM, opts vmOpts) error { + opts.policy = policy + opts.data = data + return vm.SetPolicyData(ctx, opts) + }) +} + +// updateVMs Iterates over each VM, waiting for each to safely acquire them, +// and applies the update function. If the first update succeeds any subsequent +// failures will remove the VM and continue through the pool. Otherwise an error +// will be returned. +func (p *Pool) updateVMs(update func(vm *VM, opts vmOpts) error) error { + var policy []byte + var parsedData []byte + var parsedDataAddr int32 + seedMemorySize := p.memoryMinPages + activated := false + i := 0 + for { + vm := p.Wait(i) + if vm == nil { + // All have been updated or removed. + return nil + } + + err := update(vm, vmOpts{ + policy: policy, + parsedData: parsedData, + parsedDataAddr: parsedDataAddr, + memoryMin: seedMemorySize, + memoryMax: p.memoryMaxPages, // The max pages cannot be changed while updating. + }) + + if err != nil { + // No guarantee about the VM state after an error; hence, remove. + p.remove(i) + p.Release(vm, metrics.New()) + + // After the first successful activation, proceed through all the VMs, ignoring the remaining errors. + if !activated { + return err + } + // Note: Do not increment i when it has been removed! That index is + // replaced by the last VM in the list so we must re-run with the + // same index. + } else { + if !activated { + // Activate the policy and data, now that a single VM has been reset without errors. + activated = true + policy = vm.policy + parsedDataAddr, parsedData = vm.cloneDataSegment() + seedMemorySize = util.Pages(uint32(vm.memory.DataSize(vm.store))) + p.activate(policy, parsedData, parsedDataAddr, seedMemorySize) + } + + p.Release(vm, metrics.New()) + + // Only increment on success + i++ + } + } +} + +// Close waits for all the evaluations to finish and then releases the VMs. +func (p *Pool) Close() { + for range p.vms { + <-p.available + } + + p.mutex.Lock() + defer p.mutex.Unlock() + + p.closed = true + p.vms = nil +} + +// Wait steals the i'th VM instance. The VM has to be released afterwards. +func (p *Pool) Wait(i int) *VM { + p.mutex.Lock() + defer p.mutex.Unlock() + + if i == len(p.vms) { + return nil + } + + vm := p.vms[i] + isActive := p.acquired[i] + p.acquired[i] = true + + if isActive { + p.blockedReinit = make(chan struct{}, 1) + p.pendingReinit = vm + } + + p.mutex.Unlock() + + if isActive { + <-p.blockedReinit + } else { + <-p.available + } + + p.mutex.Lock() + p.pendingReinit = nil + return vm +} + +// remove removes the i'th vm. +func (p *Pool) remove(i int) { + p.mutex.Lock() + defer p.mutex.Unlock() + + n := len(p.vms) + if n > 1 { + p.vms[i] = p.vms[n-1] + p.acquired[i] = p.acquired[n-1] + } + + p.vms = p.vms[0 : n-1] + p.acquired = p.acquired[0 : n-1] +} + +func (p *Pool) activate(policy []byte, data []byte, dataAddr int32, minMemoryPages uint32) { + p.mutex.Lock() + defer p.mutex.Unlock() + + p.policy, p.parsedData, p.parsedDataAddr, p.memoryMinPages = policy, data, dataAddr, minMemoryPages +} diff --git a/third_party/opa/internal/wasm/sdk/internal/wasm/pool_test.go b/third_party/opa/internal/wasm/sdk/internal/wasm/pool_test.go new file mode 100644 index 000000000000..526b87639bfc --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/internal/wasm/pool_test.go @@ -0,0 +1,244 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build opa_wasm +// +build opa_wasm + +package wasm_test + +import ( + "context" + "math/rand" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/internal/wasm" + wasm_util "github.com/open-policy-agent/opa/internal/wasm/util" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/compile" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestOpaEvalGrowMemoryForLargeInput(t *testing.T) { + ctx := context.Background() + module := `package test + p = true + ` + data := []byte(`{}`) + s := strings.Repeat("a", 16*wasm_util.PageSize) + input := any([]byte(s)) + + poolSize := 1 + testPool := initPoolWithData(t, uint32(poolSize), module, "test/p", data) + expected := `{{"result":true}}` + ensurePoolResults(t, ctx, testPool, poolSize, &input, expected) +} + +func TestPoolCopyParsedDataOnInit(t *testing.T) { + ctx := context.Background() + module := `package test + + p = data.a + ` + data := []byte(`{ + "a": { + "b": [ + 1, + 2, + 3, + { + "c": 4, + "d": { + "e": { + "f": 123 + } + } + } + ] + } +}`) + + poolSize := 4 + testPool := initPoolWithData(t, uint32(poolSize), module, "test/p", data) + expected := `{{"result":{"b":[1,2,3,{"d":{"e":{"f":123}},"c":4}]}}}` + ensurePoolResults(t, ctx, testPool, poolSize, nil, expected) +} + +func TestPoolCopyParsedDataUpdateFull(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + module := `package test + + p = data.a + ` + data := []byte(`{"a": 123}`) + + poolSize := 4 + testPool := initPoolWithData(t, uint32(poolSize), module, "test/p", data) + + updated := []byte(`{"a": {"x": 123, "y": "bar"}}`) + err := testPool.SetPolicyData(ctx, testPool.Policy(), updated) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + expected := `{{"result":{"y":"bar","x":123}}}` + ensurePoolResults(t, ctx, testPool, poolSize, nil, expected) + + // Change it one more time, now that all VM's in the pool have been + // initialized and exercised at least once. + updated = []byte(`{"a": [1, 2, 3]}`) + err = testPool.SetPolicyData(ctx, testPool.Policy(), updated) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + expected = `{{"result":[1,2,3]}}` + ensurePoolResults(t, ctx, testPool, poolSize, nil, expected) +} + +func TestPoolCopyParsedDataUpdatePartial(t *testing.T) { + module := `package test + + p = data.a + ` + data := []byte(`{}`) + poolSize := 4 + testPool := initPoolWithData(t, uint32(poolSize), module, "test/p", data) + + // Each case is applied in order to the original dataset + cases := []struct { + note string + update any + path []string + remove bool + expected string + }{ + { + note: "add object", + update: util.MustUnmarshalJSON([]byte(`{"foo": 123}`)), + path: []string{"a"}, + expected: `{{"result":{"foo":123}}}`, + }, + { + note: "remove path", + path: []string{"a", "foo"}, + remove: true, + expected: `{{"result":{}}}`, + }, + { + note: "add set", + update: ast.MustParseTerm(`{"x": {"y": {"z"}}}`), + path: []string{"a", "b", "c"}, + expected: `{{"result":{"b":{"c":{"x":{"y":{"z"}}}}}}}`, + }, + { + note: "remove set", + path: []string{"a", "b", "c", "x", "y"}, + remove: true, + expected: `{{"result":{"b":{"c":{"x":{}}}}}}`, + }, + } + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + var err error + if tc.remove { + err = testPool.RemoveDataPath(ctx, tc.path) + } else { + err = testPool.SetDataPath(ctx, tc.path, tc.update) + } + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + ensurePoolResults(t, ctx, testPool, poolSize, nil, tc.expected) + }) + } +} + +func ensurePoolResults(t *testing.T, ctx context.Context, testPool *wasm.Pool, poolSize int, input *any, expected string) { + t.Helper() + var toRelease []*wasm.VM + for i := 0; i < poolSize; i++ { + vm, err := testPool.Acquire(ctx, metrics.New()) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + toRelease = append(toRelease, vm) + + cfg, _ := cache.ParseCachingConfig(nil) + result, err := vm.Eval(ctx, 0, input, metrics.New(), rand.New(rand.NewSource(0)), time.Now(), cache.NewInterQueryCache(cfg), builtins.NDBCache{}, nil, nil) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + if string(result) != expected { + t.Fatalf("Incorrect result for VM %d:\nExpected: %s\nGot: %s", i, expected, string(result)) + } + } + for _, vm := range toRelease { + testPool.Release(vm, metrics.New()) + } +} + +func initPoolWithData(t *testing.T, size uint32, module string, entrypoint string, data []byte) *wasm.Pool { + t.Helper() + + ctx := context.Background() + + compiler := compile.New(). + WithTarget(compile.TargetWasm). + WithEntrypoints(entrypoint). + WithBundle(&bundle.Bundle{ + Modules: []bundle.ModuleFile{ + { + Path: "policy.rego", + URL: "policy.rego", + Raw: []byte(module), + Parsed: ast.MustParseModule(module), + }, + }, + }) + + err := compiler.Build(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + testPool := wasm.NewPool(size, 16, 100) + + err = testPool.SetPolicyData(ctx, compiler.Bundle().WasmModules[0].Raw, data) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if testPool.Size() != 1 { + t.Fatalf("Expected a single vm to be initialized with data") + } + + parsedDataAddr, parsedData := testPool.ParsedData() + if parsedDataAddr == 0 { + t.Fatalf("Expected parsedDataAddr to be non-nil") + } + + if len(parsedData) == 0 { + t.Fatalf("Expected parsedData to be non-nil") + } + + vm := testPool.Wait(0) + if vm == nil { + t.Fatalf("Expected non-nil initial vm") + } + + testPool.Release(vm, metrics.New()) + return testPool +} diff --git a/third_party/opa/internal/wasm/sdk/internal/wasm/vm.go b/third_party/opa/internal/wasm/sdk/internal/wasm/vm.go new file mode 100644 index 000000000000..396b54269397 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/internal/wasm/vm.go @@ -0,0 +1,825 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package wasm + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "strings" + "time" + + wasmtime "github.com/bytecodealliance/wasmtime-go/v3" + + sdk_errors "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/errors" + "github.com/open-policy-agent/opa/internal/wasm/util" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/print" +) + +// VM is a wrapper around a Wasm VM instance +type VM struct { + dispatcher *builtinDispatcher + engine *wasmtime.Engine + store *wasmtime.Store + instance *wasmtime.Instance // Pointer to avoid unintented destruction (triggering finalizers within). + policy []byte + abiMajorVersion int32 + abiMinorVersion int32 + memory *wasmtime.Memory + memoryMin uint32 + memoryMax uint32 + entrypointIDs map[string]int32 + baseHeapPtr int32 + dataAddr int32 + evalHeapPtr int32 + evalOneOff func(context.Context, int32, int32, int32, int32, int32) (int32, error) + eval func(context.Context, int32) error + evalCtxGetResult func(context.Context, int32) (int32, error) + evalCtxNew func(context.Context) (int32, error) + evalCtxSetData func(context.Context, int32, int32) error + evalCtxSetInput func(context.Context, int32, int32) error + evalCtxSetEntrypoint func(context.Context, int32, int32) error + heapPtrGet func(context.Context) (int32, error) + heapPtrSet func(context.Context, int32) error + jsonDump func(context.Context, int32) (int32, error) + jsonParse func(context.Context, int32, int32) (int32, error) + valueDump func(context.Context, int32) (int32, error) + valueParse func(context.Context, int32, int32) (int32, error) + malloc func(context.Context, int32) (int32, error) + free func(context.Context, int32) error + valueAddPath func(context.Context, int32, int32, int32) (int32, error) + valueRemovePath func(context.Context, int32, int32) (int32, error) + valueFree func(context.Context, int32) error + heapBlocksStash func(context.Context) error + heapBlocksRestore func(context.Context) error + heapStashClear func(context.Context) error +} + +type vmOpts struct { + policy []byte + data []byte + parsedData []byte + parsedDataAddr int32 + memoryMin uint32 + memoryMax uint32 +} + +func newVM(opts vmOpts, engine *wasmtime.Engine) (*VM, error) { + ctx := context.Background() + v := &VM{engine: engine} + store := wasmtime.NewStore(engine) + store.SetEpochDeadline(1) + memorytype := wasmtime.NewMemoryType(opts.memoryMin, true, opts.memoryMax) + memory, err := wasmtime.NewMemory(store, memorytype) + if err != nil { + return nil, err + } + + module, err := wasmtime.NewModule(store.Engine, opts.policy) + if err != nil { + return nil, err + } + + linker := wasmtime.NewLinker(store.Engine) + v.dispatcher = newBuiltinDispatcher() + externs := opaFunctions(v.dispatcher, store) + for name, extern := range externs { + if err := linker.Define("env", name, extern); err != nil { + return nil, fmt.Errorf("linker: env.%s: %w", name, err) + } + } + if err := linker.Define("env", "memory", memory); err != nil { + return nil, fmt.Errorf("linker: env.memory: %w", err) + } + + i, err := linker.Instantiate(store, module) + if err != nil { + return nil, err + } + + v.abiMajorVersion, v.abiMinorVersion, err = getABIVersion(i, store) + if err != nil { + return nil, fmt.Errorf("invalid module: %w", err) + } + if v.abiMajorVersion != int32(1) || (v.abiMinorVersion != int32(1) && v.abiMinorVersion != int32(3)) { + return nil, fmt.Errorf("invalid module: unsupported ABI version: %d.%d", v.abiMajorVersion, v.abiMinorVersion) + } + + // re-exported import, or just plain export if memory wasn't imported + memory = i.GetExport(store, "memory").Memory() + + v.store = store + v.instance = i + v.policy = opts.policy + v.memory = memory + v.memoryMin = opts.memoryMin + v.memoryMax = opts.memoryMax + v.entrypointIDs = make(map[string]int32) + v.dataAddr = 0 + v.eval = func(ctx context.Context, a int32) error { return callVoid(ctx, v, "eval", a) } + v.evalCtxGetResult = func(ctx context.Context, a int32) (int32, error) { return call(ctx, v, "opa_eval_ctx_get_result", a) } + v.evalCtxNew = func(ctx context.Context) (int32, error) { return call(ctx, v, "opa_eval_ctx_new") } + v.evalCtxSetData = func(ctx context.Context, a int32, b int32) error { + return callVoid(ctx, v, "opa_eval_ctx_set_data", a, b) + } + v.evalCtxSetInput = func(ctx context.Context, a int32, b int32) error { + return callVoid(ctx, v, "opa_eval_ctx_set_input", a, b) + } + v.evalOneOff = func(ctx context.Context, ep, dataAddr, inputAddr, inputLen, heapAddr int32) (int32, error) { + return call(ctx, v, "opa_eval", 0 /* reserved */, ep, dataAddr, inputAddr, inputLen, heapAddr, 1 /* value output */) + } + v.evalCtxSetEntrypoint = func(ctx context.Context, a int32, b int32) error { + return callVoid(ctx, v, "opa_eval_ctx_set_entrypoint", a, b) + } + v.free = func(ctx context.Context, a int32) error { return callVoid(ctx, v, "opa_free", a) } + v.heapPtrGet = func(ctx context.Context) (int32, error) { return call(ctx, v, "opa_heap_ptr_get") } + v.heapPtrSet = func(ctx context.Context, a int32) error { return callVoid(ctx, v, "opa_heap_ptr_set", a) } + v.jsonDump = func(ctx context.Context, a int32) (int32, error) { return call(ctx, v, "opa_json_dump", a) } + v.jsonParse = func(ctx context.Context, a int32, b int32) (int32, error) { + return call(ctx, v, "opa_json_parse", a, b) + } + v.valueDump = func(ctx context.Context, a int32) (int32, error) { return call(ctx, v, "opa_value_dump", a) } + v.valueParse = func(ctx context.Context, a int32, b int32) (int32, error) { + return call(ctx, v, "opa_value_parse", a, b) + } + v.malloc = func(ctx context.Context, a int32) (int32, error) { return call(ctx, v, "opa_malloc", a) } + v.valueAddPath = func(ctx context.Context, a int32, b int32, c int32) (int32, error) { + return call(ctx, v, "opa_value_add_path", a, b, c) + } + v.valueRemovePath = func(ctx context.Context, a int32, b int32) (int32, error) { + return call(ctx, v, "opa_value_remove_path", a, b) + } + v.valueFree = func(ctx context.Context, a int32) error { + return callVoid(ctx, v, "opa_value_free", a) + } + v.heapBlocksStash = func(ctx context.Context) error { + return callVoid(ctx, v, "opa_heap_blocks_stash") + } + v.heapBlocksRestore = func(ctx context.Context) error { + return callVoid(ctx, v, "opa_heap_blocks_restore") + } + v.heapStashClear = func(ctx context.Context) error { + return callVoid(ctx, v, "opa_heap_stash_clear") + } + + // Initialize the heap. + + if _, err := v.malloc(ctx, 0); err != nil { + return nil, err + } + + if v.baseHeapPtr, err = v.getHeapState(ctx); err != nil { + return nil, err + } + + // Optimization for cloning a vm, if provided a parsed data memory buffer + // insert it directly into the new vm's buffer and set pointers accordingly. + // This only works because the placement is deterministic (eg, for a given policy + // the base heap pointer and parsed data layout will always be the same). + if opts.parsedData != nil { + if uint32(memory.DataSize(store))-uint32(v.baseHeapPtr) < uint32(len(opts.parsedData)) { + delta := uint32(len(opts.parsedData)) - (uint32(memory.DataSize(store)) - uint32(v.baseHeapPtr)) + _, err = memory.Grow(store, uint64(util.Pages(delta))) + if err != nil { + return nil, err + } + } + mem := memory.UnsafeData(store) + for src, dest := 0, v.baseHeapPtr; src < len(opts.parsedData); src, dest = src+1, dest+1 { + mem[dest] = opts.parsedData[src] + } + v.dataAddr = opts.parsedDataAddr + v.evalHeapPtr = v.baseHeapPtr + int32(len(opts.parsedData)) + err := v.setHeapState(ctx, v.evalHeapPtr) + if err != nil { + return nil, err + } + } else if opts.data != nil { + if v.dataAddr, err = v.toRegoJSON(ctx, opts.data, true); err != nil { + return nil, err + } + } + + if v.evalHeapPtr, err = v.getHeapState(ctx); err != nil { + return nil, err + } + + // Construct the builtin id to name mappings. + + val, err := i.GetFunc(store, "builtins").Call(store) + if err != nil { + return nil, err + } + + builtins, err := v.fromRegoJSON(ctx, val.(int32), true) + if err != nil { + return nil, err + } + + builtinMap := map[int32]topdown.BuiltinFunc{} + + for name, id := range builtins.(map[string]any) { + f := topdown.GetBuiltin(name) + if f == nil { + return nil, fmt.Errorf("builtin '%s' not found", name) + } + + n, err := id.(json.Number).Int64() + if err != nil { + panic(err) + } + + builtinMap[int32(n)] = f + } + + v.dispatcher.SetMap(builtinMap) + + // Extract the entrypoint ID's + val, err = i.GetFunc(store, "entrypoints").Call(store) + if err != nil { + return nil, err + } + + epMap, err := v.fromRegoJSON(ctx, val.(int32), true) + if err != nil { + return nil, err + } + + for ep, value := range epMap.(map[string]any) { + id, err := value.(json.Number).Int64() + if err != nil { + return nil, err + } + v.entrypointIDs[ep] = int32(id) + } + + return v, nil +} + +func getABIVersion(i *wasmtime.Instance, store wasmtime.Storelike) (int32, int32, error) { + major := i.GetExport(store, "opa_wasm_abi_version").Global() + minor := i.GetExport(store, "opa_wasm_abi_minor_version").Global() + if major != nil && minor != nil { + majorVal := major.Get(store) + minorVal := minor.Get(store) + if majorVal.Kind() == wasmtime.KindI32 && minorVal.Kind() == wasmtime.KindI32 { + return majorVal.I32(), minorVal.I32(), nil + } + } + return 0, 0, errors.New("failed to read ABI version") +} + +// Eval performs an evaluation of the specified entrypoint, with any provided +// input, and returns the resulting value dumped to a string. +func (i *VM) Eval(ctx context.Context, + entrypoint int32, + input *any, + metrics metrics.Metrics, + seed io.Reader, + ns time.Time, + iqbCache cache.InterQueryCache, + ndbCache builtins.NDBCache, + ph print.Hook, + capabilities *ast.Capabilities) ([]byte, error) { + if i.abiMinorVersion < int32(2) { + return i.evalCompat(ctx, entrypoint, input, metrics, seed, ns, iqbCache, ndbCache, ph, capabilities) + } + + metrics.Timer("wasm_vm_eval").Start() + defer metrics.Timer("wasm_vm_eval").Stop() + + inputAddr, inputLen := int32(0), int32(0) + + // NOTE: we'll never free the memory used for the input string during + // the one evaluation, but we'll overwrite it on the next evaluation. + heapPtr := i.evalHeapPtr + + if input != nil { + metrics.Timer("wasm_vm_eval_prepare_input").Start() + var raw []byte + switch v := (*input).(type) { + case []byte: + raw = v + case *ast.Term: + raw = []byte(v.String()) + case ast.Value: + raw = []byte(v.String()) + default: + var err error + raw, err = json.Marshal(v) + if err != nil { + return nil, err + } + } + inputLen = int32(len(raw)) + inputAddr = i.evalHeapPtr + + rest := inputAddr + inputLen - int32(i.memory.DataSize(i.store)) + if rest > 0 { // need to grow memory + _, err := i.memory.Grow(i.store, uint64(util.Pages(uint32(rest)))) + if err != nil { + return nil, fmt.Errorf("input: %w (max pages %d)", err, i.memoryMax) + } + } + mem := i.memory.UnsafeData(i.store) + + heapPtr += inputLen + copy(mem[inputAddr:inputAddr+inputLen], raw) + + metrics.Timer("wasm_vm_eval_prepare_input").Stop() + } + + // Setting the ctx here ensures that it'll be available to builtins that + // make use of it (e.g. `http.send`); and it will spawn a go routine + // cancelling the builtins that use topdown.Cancel, when the context is + // cancelled. + i.dispatcher.Reset(ctx, seed, ns, iqbCache, ndbCache, ph, capabilities) + + metrics.Timer("wasm_vm_eval_call").Start() + resultAddr, err := i.evalOneOff(ctx, entrypoint, i.dataAddr, inputAddr, inputLen, heapPtr) + if err != nil { + return nil, err + } + metrics.Timer("wasm_vm_eval_call").Stop() + + data := i.memory.UnsafeData(i.store)[resultAddr:] + n := max(bytes.IndexByte(data, 0), 0) + + // Skip free'ing input and result JSON as the heap will be reset next round anyway. + return data[:n], nil +} + +// evalCompat evaluates a policy using multiple calls into the VM to set the stage. +// It's been superceded with ABI version 1.2, but still here for compatibility with +// Wasm modules lacking the needed export (i.e., ABI 1.1). +func (i *VM) evalCompat(ctx context.Context, + entrypoint int32, + input *any, + metrics metrics.Metrics, + seed io.Reader, + ns time.Time, + iqbCache cache.InterQueryCache, + ndbCache builtins.NDBCache, + ph print.Hook, + capabilities *ast.Capabilities) ([]byte, error) { + metrics.Timer("wasm_vm_eval").Start() + defer metrics.Timer("wasm_vm_eval").Stop() + + metrics.Timer("wasm_vm_eval_prepare_input").Start() + + // Setting the ctx here ensures that it'll be available to builtins that + // make use of it (e.g. `http.send`); and it will spawn a go routine + // cancelling the builtins that use topdown.Cancel, when the context is + // cancelled. + i.dispatcher.Reset(ctx, seed, ns, iqbCache, ndbCache, ph, capabilities) + + err := i.setHeapState(ctx, i.evalHeapPtr) + if err != nil { + return nil, err + } + + // Parse the input JSON and activate it with the data. + ctxAddr, err := i.evalCtxNew(ctx) + if err != nil { + return nil, err + } + + if i.dataAddr != 0 { + if err := i.evalCtxSetData(ctx, ctxAddr, i.dataAddr); err != nil { + return nil, err + } + } + + if err := i.evalCtxSetEntrypoint(ctx, ctxAddr, entrypoint); err != nil { + return nil, err + } + + if input != nil { + inputAddr, err := i.toRegoJSON(ctx, *input, false) + if err != nil { + return nil, err + } + + if err := i.evalCtxSetInput(ctx, ctxAddr, inputAddr); err != nil { + return nil, err + } + } + metrics.Timer("wasm_vm_eval_prepare_input").Stop() + + // Evaluate the policy. + metrics.Timer("wasm_vm_eval_execute").Start() + err = i.eval(ctx, ctxAddr) + metrics.Timer("wasm_vm_eval_execute").Stop() + if err != nil { + return nil, err + } + + metrics.Timer("wasm_vm_eval_prepare_result").Start() + resultAddr, err := i.evalCtxGetResult(ctx, ctxAddr) + if err != nil { + return nil, err + } + + serialized, err := i.valueDump(ctx, resultAddr) + if err != nil { + return nil, err + } + + data := i.memory.UnsafeData(i.store)[serialized:] + n := max(bytes.IndexByte(data, 0), 0) + + metrics.Timer("wasm_vm_eval_prepare_result").Stop() + + // Skip free'ing input and result JSON as the heap will be reset next round anyway. + + return data[0:n], nil +} + +// SetPolicyData Will either update the VM's data or, if the policy changed, +// re-initialize the VM. +func (i *VM) SetPolicyData(ctx context.Context, opts vmOpts) error { + + if !bytes.Equal(opts.policy, i.policy) { + // Swap the instance to a new one, with new policy. + n, err := newVM(opts, i.engine) + if err != nil { + return err + } + + *i = *n + return nil + } + + i.dataAddr = 0 + + // Release any stashed heap blocks since they will be above the base heap pointer + if err := i.heapStashClear(ctx); err != nil { + return err + } + + if err := i.setHeapState(ctx, i.baseHeapPtr); err != nil { + return err + } + + var err error + if opts.parsedData != nil { + if uint32(i.memory.DataSize(i.store))-uint32(i.baseHeapPtr) < uint32(len(opts.parsedData)) { + delta := uint32(len(opts.parsedData)) - (uint32(i.memory.DataSize(i.store)) - uint32(i.baseHeapPtr)) + _, err := i.memory.Grow(i.store, uint64(util.Pages(delta))) + if err != nil { + return err + } + } + mem := i.memory.UnsafeData(i.store) + length := int32(len(opts.parsedData)) + copy(mem[i.baseHeapPtr:i.baseHeapPtr+length], opts.parsedData) + i.dataAddr = opts.parsedDataAddr + + i.evalHeapPtr = i.baseHeapPtr + length + err := i.setHeapState(ctx, i.evalHeapPtr) + if err != nil { + return err + } + } else if opts.data != nil { + if i.dataAddr, err = i.toRegoJSON(ctx, opts.data, true); err != nil { + return err + } + } + + // Stash any free blocks so that eval()/setHeapState() won't leak them + if err := i.heapBlocksStash(ctx); err != nil { + return err + } + + i.evalHeapPtr, err = i.getHeapState(ctx) + if err != nil { + return err + } + + return nil +} + +type abortError struct { + message string +} + +type cancelledError struct { + message string +} + +// Println is invoked if the policy WASM code calls opa_println(). +func (i *VM) Println(arg int32) { + data := i.memory.UnsafeData(i.store)[arg:] + n := bytes.IndexByte(data, 0) + if n == -1 { + panic("invalid opa_println argument") + } + + fmt.Printf("opa_println(): %s\n", string(data[:n])) +} + +type builtinError struct { + err error +} + +// Entrypoints returns a mapping of entrypoint name to ID for use by Eval(). +func (i *VM) Entrypoints() map[string]int32 { + return i.entrypointIDs +} + +// SetDataPath will update the current data on the VM by setting the value at the +// specified path. If an error occurs the instance is still in a valid state, however +// the data will not have been modified. +func (i *VM) SetDataPath(ctx context.Context, path []string, value any) error { + // Reset the heap ptr before patching the vm to try and keep any + // new allocations safe from subsequent heap resets on eval. + if err := i.setHeapState(ctx, i.evalHeapPtr); err != nil { + return err + } + + // Restore saved blocks protected from leaking in eval()/setHeapState() + if err := i.heapBlocksRestore(ctx); err != nil { + return err + } + + valueAddr, err := i.toRegoJSON(ctx, value, true) + if err != nil { + return err + } + + pathAddr, err := i.toRegoJSON(ctx, path, true) + if err != nil { + return err + } + + result, err := i.valueAddPath(ctx, i.dataAddr, pathAddr, valueAddr) + if err != nil { + return err + } + + // We don't need to free the value, assume it is "owned" as part of the + // overall data object now. + // We do need to free the path + if err := i.valueFree(ctx, pathAddr); err != nil { + return err + } + + // Stash free blocks so eval() calls don't leak them when calling setHeapState() + if err := i.heapBlocksStash(ctx); err != nil { + return err + } + + // Update the eval heap pointer to accommodate for any new allocations done + // while patching. + i.evalHeapPtr, err = i.getHeapState(ctx) + if err != nil { + return err + } + + errc := result + if errc != 0 { + return fmt.Errorf("unable to set data value for path %v, err=%d", path, errc) + } + + return nil +} + +// RemoveDataPath will update the current data on the VM by removing the value at the +// specified path. If an error occurs the instance is still in a valid state, however +// the data will not have been modified. +func (i *VM) RemoveDataPath(ctx context.Context, path []string) error { + // Reset the heap ptr before patching the vm to try and keep any + // new allocations safe from subsequent heap resets on eval. + err := i.setHeapState(ctx, i.evalHeapPtr) + if err != nil { + return err + } + + // Restore saved blocks protected from leaking in eval()/setHeapState() + if err := i.heapBlocksRestore(ctx); err != nil { + return err + } + + pathAddr, err := i.toRegoJSON(ctx, path, true) + if err != nil { + return err + } + + errc, err := i.valueRemovePath(ctx, i.dataAddr, pathAddr) + if err != nil { + return err + } + + if err := i.valueFree(ctx, pathAddr); err != nil { + return err + } + + // Stash free blocks so eval() calls don't leak them when calling setHeapState() + if err = i.heapBlocksStash(ctx); err != nil { + return err + } + + // Update the eval heap pointer to accommodate for any newly available memory + if i.evalHeapPtr, err = i.getHeapState(ctx); err != nil { + return err + } + + if errc != 0 { + return fmt.Errorf("unable to set data value for path %v, err=%d", path, errc) + } + + return nil +} + +// fromRegoJSON parses serialized JSON from the Wasm memory buffer into +// native go types. +func (i *VM) fromRegoJSON(ctx context.Context, addr int32, free bool) (any, error) { + serialized, err := i.jsonDump(ctx, addr) + if err != nil { + return nil, err + } + + data := i.memory.UnsafeData(i.store)[serialized:] + n := max(bytes.IndexByte(data, 0), 0) + + // Parse the result into go types. + + decoder := json.NewDecoder(bytes.NewReader(data[0:n])) + decoder.UseNumber() + + var result any + if err := decoder.Decode(&result); err != nil { + return nil, err + } + + if free { + if err := i.free(ctx, serialized); err != nil { + return nil, err + } + } + + return result, nil +} + +// toRegoJSON converts go native JSON to Rego JSON. If the value is +// an AST type it will be dumped using its stringer. +func (i *VM) toRegoJSON(ctx context.Context, v any, free bool) (int32, error) { + var raw []byte + switch v := v.(type) { + case []byte: + raw = v + case *ast.Term: + raw = []byte(v.String()) + case ast.Value: + raw = []byte(v.String()) + default: + var err error + raw, err = json.Marshal(v) + if err != nil { + return 0, err + } + } + + n := int32(len(raw)) + p, err := i.malloc(ctx, n) + if err != nil { + return 0, err + } + + copy(i.memory.UnsafeData(i.store)[p:p+n], raw) + + addr, err := i.valueParse(ctx, p, n) + if err != nil { + return 0, err + } + + if free { + if err := i.free(ctx, p); err != nil { + return 0, err + } + } + + return addr, nil +} + +func (i *VM) getHeapState(ctx context.Context) (int32, error) { + return i.heapPtrGet(ctx) +} + +func (i *VM) setHeapState(ctx context.Context, ptr int32) error { + return i.heapPtrSet(ctx, ptr) +} + +func (i *VM) cloneDataSegment() (int32, []byte) { + // The parsed data values sit between the base heap address and end + // at the eval heap pointer address. + srcData := i.memory.UnsafeData(i.store)[i.baseHeapPtr:i.evalHeapPtr] + patchedData := make([]byte, len(srcData)) + copy(patchedData, srcData) + return i.dataAddr, patchedData +} + +func call(ctx context.Context, vm *VM, name string, args ...int32) (int32, error) { + res, err := callOrCancel(ctx, vm, name, args...) + if err != nil { + return 0, err + } + return res.(int32), nil +} + +func callVoid(ctx context.Context, vm *VM, name string, args ...int32) error { + _, err := callOrCancel(ctx, vm, name, args...) + return err +} + +func callOrCancel(ctx context.Context, vm *VM, name string, args ...int32) (any, error) { + sl := make([]any, len(args)) + for i := range sl { + sl[i] = args[i] + } + + // `done` is closed when the eval is done; + // `ctxdone` is used to ensure that this goroutine is not running rogue; + // it may interact badly with other calls into this VM because of async + // execution. Concretely, there's no guarantee which branch of done or + // ctx.Done() is selected when they're both good to go. Hence, this may + // interrupt the VM long after _this_ functions is done. By tying them + // together (`<-ctxdone` at the end of callOrCancel, `close(ctxdone)` + // here), we can avoid that. + done := make(chan struct{}) + ctxdone := make(chan struct{}) + go func() { + select { + case <-ctx.Done(): + vm.store.Engine.IncrementEpoch() + case <-done: + } + close(ctxdone) + }() + + f := vm.instance.GetFunc(vm.store, name) + // If this call into the VM ends up calling host functions (builtins not + // implemented in Wasm), and those panic, wasmtime will re-throw them, + // and this is where we deal with that: + res, err := func() (res any, err error) { + defer close(done) + defer func() { + if e := recover(); e != nil { + switch e := e.(type) { + case abortError: + err = sdk_errors.New(sdk_errors.InternalErr, e.message) + case cancelledError: + err = sdk_errors.New(sdk_errors.CancelledErr, e.message) + case builtinError: + err = sdk_errors.New(sdk_errors.InternalErr, e.err.Error()) + default: + panic(e) + } + } + }() + res, err = f.Call(vm.store, sl...) + return + }() + if err != nil { + // if last err was trap, extract information + var t *wasmtime.Trap + if errors.As(err, &t) { + if strings.Contains(t.Message(), "wasm trap: interrupt") { + return 0, sdk_errors.New(sdk_errors.CancelledErr, "interrupted") + } + return 0, sdk_errors.New(sdk_errors.InternalErr, getStack(t.Frames(), "trapped")) + } + return 0, err + } + <-ctxdone // wait for the goroutine that's checking ctx + return res, nil +} + +func getStack(fs []*wasmtime.Frame, desc string) string { + var b strings.Builder + b.WriteString(desc) + if len(fs) > 1 { + b.WriteString(" at ") + for i := len(fs) - 1; i >= 0; i-- { // backwards + fr := fs[i] + if fun := fr.FuncName(); fun != nil { + if i != len(fs)-1 { + b.WriteRune('/') + } + b.WriteString(*fun) + + } + } + } + return b.String() +} diff --git a/third_party/opa/internal/wasm/sdk/opa/capabilities/capabilities.go b/third_party/opa/internal/wasm/sdk/opa/capabilities/capabilities.go new file mode 100644 index 000000000000..9f939284e79e --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/capabilities/capabilities.go @@ -0,0 +1,12 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// +build opa_wasm generate + +package capabilities + +// ABIVersions returns the ABI versions that this SDK supports +func ABIVersions() [][2]int { + return [][2]int{{1, 1}, {1, 2}} +} diff --git a/third_party/opa/internal/wasm/sdk/opa/capabilities/capabilities_nowasm.go b/third_party/opa/internal/wasm/sdk/opa/capabilities/capabilities_nowasm.go new file mode 100644 index 000000000000..6b17984bb825 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/capabilities/capabilities_nowasm.go @@ -0,0 +1,14 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build !opa_wasm && !generate +// +build !opa_wasm,!generate + +package capabilities + +// ABIVersions returns the supported Wasm ABI versions for this +// build: none +func ABIVersions() [][2]int { + return nil +} diff --git a/third_party/opa/internal/wasm/sdk/opa/config.go b/third_party/opa/internal/wasm/sdk/opa/config.go new file mode 100644 index 000000000000..8c9859cd172a --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/config.go @@ -0,0 +1,102 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package opa + +import ( + "encoding/json" + "os" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/errors" + "github.com/open-policy-agent/opa/internal/wasm/util" +) + +// WithPolicyFile configures a policy file to load. +func (o *OPA) WithPolicyFile(fileName string) *OPA { + policy, err := os.ReadFile(fileName) + if err != nil { + o.configErr = errors.New(errors.InvalidConfigErr, err.Error()) + return o + } + + o.policy = policy + return o +} + +// WithPolicyBytes configures the compiled policy to load. +func (o *OPA) WithPolicyBytes(policy []byte) *OPA { + o.policy = policy + return o +} + +// WithDataFile configures the JSON data file to load. +func (o *OPA) WithDataFile(fileName string) *OPA { + data, err := os.ReadFile(fileName) + if err != nil { + o.configErr = errors.New(errors.InvalidConfigErr, err.Error()) + return o + } + + o.data = data + return o +} + +// WithDataBytes configures the JSON data to load. +func (o *OPA) WithDataBytes(data []byte) *OPA { + o.data = data + return o +} + +// WithDataJSON configures the JSON data to load. +func (o *OPA) WithDataJSON(data any) *OPA { + v, err := json.Marshal(data) + if err != nil { + o.configErr = errors.New(errors.InvalidConfigErr, err.Error()) + return o + } + + o.data = v + return o +} + +// WithMemoryLimits configures the memory limits (in bytes) for a single policy +// evaluation. +func (o *OPA) WithMemoryLimits(min, max uint32) *OPA { + if min < 2*util.PageSize { + o.configErr = errors.New(errors.InvalidConfigErr, "too low minimum memory limit") + return o + } + + if max == 0 { + max = 0xffffffff + } + + if min > max { + o.configErr = errors.New(errors.InvalidConfigErr, "too low maximum memory limit") + return o + } + + o.memoryMinPages, o.memoryMaxPages = util.Pages(min), util.Pages(max) + return o +} + +// WithPoolSize configures the maximum number of simultaneous policy +// evaluations, i.e., the maximum number of underlying WASM instances +// active at any time. The default is the number of logical CPUs +// usable for the process as per runtime.NumCPU(). +func (o *OPA) WithPoolSize(size uint32) *OPA { + if size == 0 { + o.configErr = errors.New(errors.InvalidConfigErr, "pool size") + return o + } + + o.poolSize = size + return o +} + +// WithErrorLogger configures an error logger invoked with all the errors. +func (o *OPA) WithErrorLogger(logger func(error)) *OPA { + o.logError = logger + return o +} diff --git a/third_party/opa/internal/wasm/sdk/opa/errors/errors.go b/third_party/opa/internal/wasm/sdk/opa/errors/errors.go new file mode 100644 index 000000000000..12c24c090c90 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/errors/errors.go @@ -0,0 +1,77 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package errors + +import ( + "errors" + "fmt" +) + +const ( + // InvalidConfigErr is the error code returned if the OPA initialization fails due to an invalid config. + InvalidConfigErr string = "invalid_config" + + // InvalidPolicyOrDataErr is the error code returned if either policy or data is invalid. + InvalidPolicyOrDataErr string = "invalid_policy_or_data" + + // InvalidBundleErr is the error code returned if the bundle loaded is corrupted. + InvalidBundleErr string = "invalid_bundle" + + // NotReadyErr is the error code returned if the OPA instance is not initialized. + NotReadyErr string = "not_ready" + + // InternalErr is the error code returned if the evaluation fails due to an internal error. + InternalErr string = "internal_error" + + // CancelledErr is the error code returned if the evaluation is cancelled. + CancelledErr string = "cancelled" +) + +// Error is the error code type returned by the SDK functions when an error occurs. +type Error struct { + Code string `json:"code"` + Message string `json:"message,omitempty"` +} + +// New returns a new error with the passed code +func New(code, msg string) error { + switch code { + case InvalidConfigErr, InvalidPolicyOrDataErr, InvalidBundleErr, NotReadyErr, InternalErr, CancelledErr: + return &Error{Code: code, Message: msg} + default: + panic("unknown error code: " + code) + } +} + +// IsError returns true if the err is an Error. +func IsError(err error) bool { + return errorHasCode(err, "") +} + +func errorHasCode(err error, code string) bool { + return errors.Is(err, &Error{Code: code}) +} + +// IsCancel returns true if err was caused by cancellation. +func IsCancel(err error) bool { + return errorHasCode(err, CancelledErr) +} + +// Is allows matching error types using errors.Is (see IsCancel). +func (e *Error) Is(target error) bool { + var t *Error + if errors.As(target, &t) { + return (t.Code == "" || e.Code == t.Code) && + (t.Message == "" || e.Message == t.Message) + } + return false +} + +func (e *Error) Error() string { + if e.Message == "" { + return e.Code + } + return fmt.Sprintf("%v: %v", e.Code, e.Message) +} diff --git a/third_party/opa/internal/wasm/sdk/opa/loader/file/config.go b/third_party/opa/internal/wasm/sdk/opa/loader/file/config.go new file mode 100644 index 000000000000..9322b03e0740 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/loader/file/config.go @@ -0,0 +1,34 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package file + +import ( + "time" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/errors" +) + +// WithFile configures the file to load the bundle from. +func (l *Loader) WithFile(filename string) *Loader { + l.filename = filename + return l +} + +// WithInterval configures the delay between bundle file reloading. +func (l *Loader) WithInterval(interval time.Duration) *Loader { + l.interval = interval + return l +} + +// WithErrorLogger configures an error logger invoked with all the errors. +func (l *Loader) WithErrorLogger(logger func(error)) *Loader { + if logger == nil { + l.configErr = errors.New(errors.InvalidConfigErr, "missing logger") + return l + } + + l.logError = logger + return l +} diff --git a/third_party/opa/internal/wasm/sdk/opa/loader/file/loader.go b/third_party/opa/internal/wasm/sdk/opa/loader/file/loader.go new file mode 100644 index 000000000000..bc53abdae636 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/loader/file/loader.go @@ -0,0 +1,168 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package file + +import ( + "context" + "os" + "sync" + "time" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/errors" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/util" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa" +) + +const ( + // DefaultInterval for re-loading the bundle file. + DefaultInterval = time.Minute +) + +var errNotReady = errors.New(errors.NotReadyErr, "") + +// Loader loads a bundle from a file. If started, it loads the bundle +// periodically until closed. +type Loader struct { + configErr error // Delayed configuration error, if any. + initialized bool + pd policyData + filename string + interval time.Duration + closing chan struct{} // Signal the request to stop the poller. + closed chan struct{} // Signals the successful stopping of the poller. + logError func(error) + mutex sync.Mutex +} + +// policyData captures the functions used in setting the policy and data. +type policyData interface { + SetPolicyData(ctx context.Context, policy []byte, data *any) error +} + +// New constructs a new file loader periodically reloading the bundle +// from a file. +func New(opa *opa.OPA) *Loader { + return newLoader(opa) +} + +// newLoader constructs a newLoader file loader. This is for tests. +func newLoader(pd policyData) *Loader { + return &Loader{ + pd: pd, + interval: DefaultInterval, + logError: func(error) {}, + } +} + +// Init initializes the loader after its construction and +// configuration. If invalid config, will return ErrInvalidConfig. +func (l *Loader) Init() (*Loader, error) { + if l.configErr != nil { + return nil, l.configErr + } + + if l.filename == "" { + return nil, errors.New(errors.InvalidConfigErr, "missing filename") + } + + l.initialized = true + return l, nil +} + +// Start starts the periodic loading byt calling Load, failing if the +// bundle loading fails. +func (l *Loader) Start(ctx context.Context) error { + if !l.initialized { + return errNotReady + } + + if err := l.Load(ctx); err != nil { + return err + } + + l.closing = make(chan struct{}) + l.closed = make(chan struct{}) + + go l.poller() + + return nil +} + +// Close stops the loading, releasing all resources. +func (l *Loader) Close() { + if !l.initialized { + return + } + + if l.closing == nil { + return + } + + close(l.closing) + <-l.closed + + l.closing = nil + l.closed = nil +} + +// Load loads the bundle from a file and installs it. The possible +// returned errors are ErrInvalidBundle (in case of an error in +// loading or opening the bundle) and the ones SetPolicyData of OPA +// returns. +func (l *Loader) Load(ctx context.Context) error { + if !l.initialized { + return errNotReady + } + + l.mutex.Lock() + defer l.mutex.Unlock() + + f, err := os.Open(l.filename) + if err != nil { + return errors.New(errors.InvalidBundleErr, err.Error()) + } + + defer f.Close() + + // TODO: Cut the dependency to the OPA bundle package. + + b, err := bundle.NewReader(f).Read() + if err != nil { + return errors.New(errors.InvalidBundleErr, err.Error()) + } + + if len(b.WasmModules) == 0 { + return errors.New(errors.InvalidBundleErr, "missing wasm") + } + + var data *any + if b.Data != nil { + var v any = b.Data + data = &v + } + + return l.pd.SetPolicyData(ctx, b.WasmModules[0].Raw, data) +} + +// poller periodically downloads the bundle. +func (l *Loader) poller() { + defer close(l.closed) + + for { + if err := l.Load(context.Background()); err != nil { + l.logError(err) + } + + timer, timerCancel := util.TimerWithCancel(l.interval) + select { + case <-timer.C: + case <-l.closing: + timerCancel() // explicitly cancel the timer. + return + } + } +} diff --git a/third_party/opa/internal/wasm/sdk/opa/loader/file/loader_test.go b/third_party/opa/internal/wasm/sdk/opa/loader/file/loader_test.go new file mode 100644 index 000000000000..22f567fcf560 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/loader/file/loader_test.go @@ -0,0 +1,130 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build opa_wasm +// +build opa_wasm + +package file + +import ( + "bytes" + "context" + "os" + "reflect" + "sync" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/bundle" +) + +func TestFileLoader(t *testing.T) { + // Assign a temp file. + + f, err := os.CreateTemp("", "test-file-loader") + if err != nil { + panic(err) + } + + defer os.Remove(f.Name()) + + // Start loader, without having a file in place. + + var pd testPolicyData + loader, err := newLoader(&pd).WithFile(f.Name()).WithInterval(10 * time.Millisecond).Init() + if err != nil { + t.Fatal(err.Error()) + } + + ctx := context.Background() + if err := loader.Start(ctx); err == nil { + t.Fatal("missing file not resulting in an error") + } + + policy := "wasm-policy" + var data any = map[string]any{ + "foo": "bar", + } + + // Start loader, with the file in place. + + writeBundle(f.Name(), policy, data) + + if err := loader.Start(ctx); err != nil { + t.Fatalf("unable to start loader: %v", err) + } + + pd.CheckEqual(t, policy, &data) + + // Reload with updated contents. + + policy = "wasm-policy-modified" + data = map[string]any{ + "bar": "foo", + } + + writeBundle(f.Name(), policy, data) + + pd.WaitUpdate() + pd.CheckEqual(t, policy, &data) + + loader.Close() +} + +type testPolicyData struct { + sync.Mutex + policy []byte + data *any + updated chan struct{} +} + +func (pd *testPolicyData) SetPolicyData(_ context.Context, policy []byte, data *any) error { + pd.Lock() + defer pd.Unlock() + + pd.policy = policy + pd.data = data + if pd.updated != nil { + close(pd.updated) + } + + return nil +} + +func (pd *testPolicyData) CheckEqual(t *testing.T, policy string, data *any) { + pd.Lock() + defer pd.Unlock() + + if !bytes.Equal([]byte(policy), pd.policy) && reflect.DeepEqual(data, pd.data) { + t.Fatal("policy/data mismatch.") + } +} + +func (pd *testPolicyData) WaitUpdate() { + pd.Lock() + pd.updated = make(chan struct{}) + pd.Unlock() + + <-pd.updated + + pd.Lock() + pd.updated = nil + pd.Unlock() +} + +func writeBundle(name string, policy string, data any) { + b := bundle.Bundle{ + Data: data.(map[string]any), + Wasm: []byte(policy), + } + + var buf bytes.Buffer + if err := bundle.Write(&buf, b); err != nil { + panic(err) + } + + if err := os.WriteFile(name, buf.Bytes(), 0644); err != nil { + panic(err) + } +} diff --git a/third_party/opa/internal/wasm/sdk/opa/loader/http/config.go b/third_party/opa/internal/wasm/sdk/opa/loader/http/config.go new file mode 100644 index 000000000000..1f4995200d7e --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/loader/http/config.go @@ -0,0 +1,65 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package http + +import ( + "net/http" + "time" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/errors" +) + +// WithURL configures the URL to download the bundle from. +func (l *Loader) WithURL(url string) *Loader { + l.url = url + return l +} + +// WithClient configures the HTTP client to use. If not configured, +// http.DefaultClient is used. +func (l *Loader) WithClient(client *http.Client) *Loader { + if client == nil { + l.configErr = errors.New(errors.InvalidConfigErr, "client") + return l + } + + l.client = client + return l +} + +// WithInterval configures the minimum and maximum delay between bundle downloads. +func (l *Loader) WithInterval(min, max time.Duration) *Loader { + if min > max { + l.configErr = errors.New(errors.InvalidConfigErr, "interval min > max") + return l + } + + l.minDelay = min + l.maxDelay = max + return l +} + +// WithPrepareRequest configures a handler to customize the HTTP requests before their sending. The +// HTTP request is not modified after the handle invocation. +func (l *Loader) WithPrepareRequest(prepare func(*http.Request) error) *Loader { + if prepare == nil { + l.configErr = errors.New(errors.InvalidConfigErr, "missing prepare") + return l + } + + l.prepareRequest = prepare + return l +} + +// WithErrorLogger configures an error logger invoked with all the errors. +func (l *Loader) WithErrorLogger(logger func(error)) *Loader { + if logger == nil { + l.configErr = errors.New(errors.InvalidConfigErr, "missing logger") + return l + } + + l.logError = logger + return l +} diff --git a/third_party/opa/internal/wasm/sdk/opa/loader/http/loader.go b/third_party/opa/internal/wasm/sdk/opa/loader/http/loader.go new file mode 100644 index 000000000000..42fa818b28c9 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/loader/http/loader.go @@ -0,0 +1,264 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package http + +import ( + "context" + "errors" + "fmt" + "io" + "math/rand" + "net/http" + "sync" + "time" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa" + werrors "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/errors" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + // MinRetryDelay determines the minimum retry interval in case + // of an error. + MinRetryDelay = 100 * time.Millisecond + + // DefaultMinDelay is the default minimum re-downloading + // interval in case of a previously successful download. + DefaultMinDelay = 60 * time.Second + + // DefaultMaxDelay is the default maximum re-downloading + // interval in case of a previously successful download. + DefaultMaxDelay = 120 * time.Second +) + +// Loader downloads a bundle over HTTP. If started, it downloads the +// bundle periodically until closed. +type Loader struct { + configErr error // Delayed configuration error, if any. + initialized bool + pd policyData + client *http.Client + url string + tag string + minDelay time.Duration + maxDelay time.Duration + closing chan struct{} // Signal the request to stop the poller. + closed chan struct{} // Signals the successful stopping of the poller. + logError func(error) + prepareRequest func(*http.Request) error + mutex sync.Mutex +} + +// policyData captures the functions used in setting the policy and data. +type policyData interface { + SetPolicyData(ctx context.Context, policy []byte, data *any) error +} + +// New constructs a new HTTP loader periodically downloading a bundle +// over HTTP. +func New(o *opa.OPA) *Loader { + return newLoader(o) +} + +// newLoader constructs a new HTTP loader. This is for tests. +func newLoader(pd policyData) *Loader { + return &Loader{ + pd: pd, + client: http.DefaultClient, + minDelay: DefaultMinDelay, + maxDelay: DefaultMaxDelay, + logError: func(error) {}, + prepareRequest: func(*http.Request) error { return nil }, + } +} + +// Init initializes the loader after its construction and +// configuration. If invalid config, will return ErrInvalidConfig. +func (l *Loader) Init() (*Loader, error) { + if l.configErr != nil { + return nil, l.configErr + } + + if l.url == "" { + return nil, werrors.New(werrors.InvalidConfigErr, "missing url") + } + + l.initialized = true + return l, nil +} + +// Start starts the periodic downloads, blocking until the first +// successful download. If cancelled, will return context.Cancelled. +func (l *Loader) Start(ctx context.Context) error { + if !l.initialized { + return werrors.New(werrors.NotReadyErr, "") + } + + if err := l.download(ctx); err != nil { + return err + } + + l.closing = make(chan struct{}) + l.closed = make(chan struct{}) + + go l.poller() + + return nil +} + +// Close stops the downloading, releasing all resources. +func (l *Loader) Close() { + if !l.initialized { + return + } + + if l.closing == nil { + return + } + + close(l.closing) + <-l.closed + + l.closing = nil + l.closed = nil +} + +// poller periodically downloads the bundle. +func (l *Loader) poller() { + defer close(l.closed) + + ctx, cancel := context.WithCancel(context.Background()) + go func() { + <-l.closing + cancel() + }() + + for { + if err := l.download(ctx); err != nil { + break + } + + delay := time.Duration(float64((l.maxDelay-l.minDelay))*rand.Float64()) + l.minDelay + timer, timerCancel := util.TimerWithCancel(delay) + select { + case <-timer.C: + case <-ctx.Done(): + timerCancel() // explicitly cancel the timer. + return + } + } +} + +// download blocks until a bundle has been download successfully or +// the context is cancelled. No other error besides context.Canceled +// is ever returned. +func (l *Loader) download(ctx context.Context) error { + for retry := 0; true; retry++ { + if err := l.Load(ctx); err == context.Canceled { + return err + } else if err != nil { + l.logError(err) + } else { + break + } + + delay := defaultBackoff(float64(MinRetryDelay), float64(l.maxDelay), retry) + timer, timerCancel := util.TimerWithCancel(delay) + select { + case <-timer.C: + case <-ctx.Done(): + timerCancel() // explicitly cancel the timer. + return context.Canceled + } + } + + return nil +} + +// Load downloads the bundle from a remote location and installs +// it. The possible returned errors are ErrInvalidBundle (in case of +// an error in downloading or opening the bundle) and the ones +// SetPolicyData of OPA returns. +func (l *Loader) Load(ctx context.Context) error { + if !l.initialized { + return werrors.New(werrors.NotReadyErr, "") + } + + l.mutex.Lock() + defer l.mutex.Unlock() + + bundle, err := l.get(ctx, "") + if err != nil { + return werrors.New(werrors.InvalidBundleErr, err.Error()) + } + + if len(bundle.WasmModules) == 0 { + return werrors.New(werrors.InvalidBundleErr, "missing wasm") + } + + var data *any + if bundle.Data != nil { + var v any = bundle.Data + data = &v + } + + return l.pd.SetPolicyData(ctx, bundle.WasmModules[0].Raw, data) +} + +// get executes HTTP GET. +func (l *Loader) get(ctx context.Context, tag string) (*bundle.Bundle, error) { + req, err := http.NewRequest(http.MethodGet, l.url, nil) + if err != nil { + return nil, err + } + + if tag != "" { + req.Header.Add("If-None-Match", tag) + } + + req = req.WithContext(ctx) + if err := l.prepareRequest(req); err != nil { + return nil, err + } + + resp, err := l.client.Do(req) + if err != nil { + return nil, err + } + + defer l.close(resp) + + switch resp.StatusCode { + case http.StatusOK: + // TODO: Cut the dependency to the OPA bundle package. + + b, err := bundle.NewReader(resp.Body).Read() + if err != nil { + return nil, err + } + + l.tag = resp.Header.Get("ETag") + return &b, nil + + case http.StatusNotModified: + return nil, nil + case http.StatusUnauthorized: + return nil, errors.New("not authorized (401)") + case http.StatusForbidden: + return nil, errors.New("forbidden (403)") + case http.StatusNotFound: + return nil, errors.New("not found (404)") + default: + return nil, fmt.Errorf("unknown HTTP status %v", resp.StatusCode) + } +} + +// close closes the HTTP response gracefully, first draining it, to +// avoid resource leaks. +func (*Loader) close(resp *http.Response) { + _, _ = io.Copy(io.Discard, resp.Body) // Ignore errors. + _ = resp.Body.Close() +} diff --git a/third_party/opa/internal/wasm/sdk/opa/loader/http/loader_test.go b/third_party/opa/internal/wasm/sdk/opa/loader/http/loader_test.go new file mode 100644 index 000000000000..279dce155400 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/loader/http/loader_test.go @@ -0,0 +1,129 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build opa_wasm +// +build opa_wasm + +package http + +import ( + "bytes" + "context" + "net/http" + "net/http/httptest" + "reflect" + "sync" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/bundle" +) + +func TestHTTPLoader(t *testing.T) { + // Start loader, without having the HTTP content in place. + + var pd testPolicyData + loader, err := newLoader(&pd).WithURL("http://localhost:0").WithInterval(10*time.Millisecond, 20*time.Millisecond).Init() + if err != nil { + t.Fatal(err.Error()) + } + + ctx, cancel := context.WithCancel(context.Background()) + go func() { + time.Sleep(10 * time.Millisecond) + cancel() + }() + + if err := loader.Start(ctx); err != context.Canceled { + t.Fatalf("missing file not resulting in a correct error: %v", err) + } + + // Start again, with the HTTP content in place. + + var mutex sync.Mutex + policy := "wasm-policy" + var data any = map[string]any{ + "foo": "bar", + } + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + mutex.Lock() + defer mutex.Unlock() + + if err := bundle.Write(w, bundle.Bundle{ + Data: data.(map[string]any), + Wasm: []byte(policy), + }); err != nil { + panic(err) + } + })) + defer ts.Close() + + loader, err = newLoader(&pd).WithURL(ts.URL).WithInterval(10*time.Millisecond, 20*time.Millisecond).Init() + if err != nil { + t.Fatal(err.Error()) + } + + ctx = context.Background() + if err := loader.Start(ctx); err != nil { + t.Fatalf("unable to start loader: %v", err) + } + + pd.CheckEqual(t, policy, &data) + + // Reload with updated contents. + + mutex.Lock() + policy = "wasm-policy-modified" + data = map[string]any{ + "bar": "foo", + } + mutex.Unlock() + + pd.WaitUpdate() + pd.CheckEqual(t, policy, &data) + + loader.Close() +} + +type testPolicyData struct { + sync.Mutex + policy []byte + data *any + updated chan struct{} +} + +func (pd *testPolicyData) SetPolicyData(_ context.Context, policy []byte, data *any) error { + pd.Lock() + defer pd.Unlock() + + pd.policy = policy + pd.data = data + if pd.updated != nil { + close(pd.updated) + } + + return nil +} + +func (pd *testPolicyData) CheckEqual(t *testing.T, policy string, data *any) { + pd.Lock() + defer pd.Unlock() + + if !bytes.Equal([]byte(policy), pd.policy) && reflect.DeepEqual(data, pd.data) { + t.Fatal("policy/data mismatch.") + } +} + +func (pd *testPolicyData) WaitUpdate() { + pd.Lock() + pd.updated = make(chan struct{}) + pd.Unlock() + + <-pd.updated + + pd.Lock() + pd.updated = nil + pd.Unlock() +} diff --git a/third_party/opa/internal/wasm/sdk/opa/loader/http/util.go b/third_party/opa/internal/wasm/sdk/opa/loader/http/util.go new file mode 100644 index 000000000000..fc47c30e150b --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/loader/http/util.go @@ -0,0 +1,43 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package http + +import ( + "math/rand" + "time" +) + +// defaultBackoff returns a delay with an exponential backoff based on the +// number of retries. +func defaultBackoff(base, max float64, retries int) time.Duration { + return backoff(base, max, .2, 1.6, retries) +} + +// backoff returns a delay with an exponential backoff based on the number of +// retries. Same algorithm used in gRPC. +func backoff(base, max, jitter, factor float64, retries int) time.Duration { + if retries == 0 { + return 0 + } + + //nolint:unconvert + backoff, max := float64(base), float64(max) + for backoff < max && retries > 0 { + backoff *= factor + retries-- + } + if backoff > max { + backoff = max + } + + // Randomize backoff delays so that if a cluster of requests start at + // the same time, they won't operate in lockstep. + backoff *= 1 + jitter*(rand.Float64()*2-1) + if backoff < 0 { + return 0 + } + + return time.Duration(backoff) +} diff --git a/third_party/opa/internal/wasm/sdk/opa/loader/loader.go b/third_party/opa/internal/wasm/sdk/opa/loader/loader.go new file mode 100644 index 000000000000..657629bbfae5 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/loader/loader.go @@ -0,0 +1,21 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package loader + +import ( + "context" +) + +// Loader is the interface all bundle loaders implement. +type Loader interface { + // Load loads a bundle. This can be invoked without starting the polling. + Load(ctx context.Context) error + + // Start starts the bundle polling. + Start(ctx context.Context) error + + // Close stops the polling. + Close() +} diff --git a/third_party/opa/internal/wasm/sdk/opa/opa.go b/third_party/opa/internal/wasm/sdk/opa/opa.go new file mode 100644 index 000000000000..a404dbd1582d --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/opa.go @@ -0,0 +1,226 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package opa + +import ( + "context" + "encoding/json" + "io" + "runtime" + "sync" + "time" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/internal/wasm" + sdk_errors "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/errors" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/print" +) + +var errNotReady = sdk_errors.New(sdk_errors.NotReadyErr, "") + +// OPA executes WebAssembly compiled Rego policies. +type OPA struct { + configErr error // Delayed configuration error, if any. + memoryMinPages uint32 + memoryMaxPages uint32 // 0 means no limit. + poolSize uint32 + pool *wasm.Pool + mutex sync.Mutex // To serialize access to SetPolicy, SetData and Close. + policy []byte // Current policy. + data []byte // Current data. + logError func(error) +} + +// Result holds the evaluation result. +type Result struct { + Result []byte +} + +// New constructs a new OPA SDK instance, ready to be configured with +// With functions. If no policy is provided as a part of +// configuration, policy (and data) needs to be set before invoking +// Eval. Once constructed and configured, the instance needs to be +// initialized before invoking the Eval. +func New() *OPA { + opa := &OPA{ + memoryMinPages: 16, + memoryMaxPages: 0x10000, // 4GB + poolSize: uint32(runtime.GOMAXPROCS(0)), + logError: func(error) {}, + } + + return opa +} + +// Init initializes the SDK instance after the construction and +// configuration. If the configuration is invalid, it returns +// ErrInvalidConfig. +func (o *OPA) Init() (*OPA, error) { + ctx := context.Background() + if o.configErr != nil { + return nil, o.configErr + } + + o.pool = wasm.NewPool(o.poolSize, o.memoryMinPages, o.memoryMaxPages) + + if len(o.policy) != 0 { + if err := o.pool.SetPolicyData(ctx, o.policy, o.data); err != nil { + return nil, err + } + } + + return o, nil +} + +// SetData updates the data for the subsequent Eval calls. Returns +// either ErrNotReady, ErrInvalidPolicyOrData, or ErrInternal if an +// error occurs. +func (o *OPA) SetData(ctx context.Context, v any) error { + if o.pool == nil { + return errNotReady + } + + raw, err := json.Marshal(v) + if err != nil { + return sdk_errors.New(sdk_errors.InvalidPolicyOrDataErr, err.Error()) + } + + o.mutex.Lock() + defer o.mutex.Unlock() + + return o.setPolicyData(ctx, o.policy, raw) +} + +// SetDataPath will update the current data on the VMs by setting the value at the +// specified path. If an error occurs the instance is still in a valid state, however +// the data will not have been modified. +func (o *OPA) SetDataPath(ctx context.Context, path []string, value any) error { + return o.pool.SetDataPath(ctx, path, value) +} + +// RemoveDataPath will update the current data on the VMs by removing the value at the +// specified path. If an error occurs the instance is still in a valid state, however +// the data will not have been modified. +func (o *OPA) RemoveDataPath(ctx context.Context, path []string) error { + return o.pool.RemoveDataPath(ctx, path) +} + +// SetPolicy updates the policy for the subsequent Eval calls. +// Returns either ErrNotReady, ErrInvalidPolicy or ErrInternal if an +// error occurs. +func (o *OPA) SetPolicy(ctx context.Context, p []byte) error { + if o.pool == nil { + return errNotReady + } + + o.mutex.Lock() + defer o.mutex.Unlock() + + return o.setPolicyData(ctx, p, o.data) +} + +// SetPolicyData updates both the policy and data for the subsequent +// Eval calls. Returns either ErrNotReady, ErrInvalidPolicyOrData, or +// ErrInternal if an error occurs. +func (o *OPA) SetPolicyData(ctx context.Context, policy []byte, data *any) error { + if o.pool == nil { + return errNotReady + } + + var raw []byte + if data != nil { + var err error + raw, err = json.Marshal(*data) + if err != nil { + return sdk_errors.New(sdk_errors.InvalidPolicyOrDataErr, err.Error()) + } + } + + o.mutex.Lock() + defer o.mutex.Unlock() + + return o.setPolicyData(ctx, policy, raw) +} + +func (o *OPA) setPolicyData(ctx context.Context, policy []byte, data []byte) error { + if err := o.pool.SetPolicyData(ctx, policy, data); err != nil { + return err + } + + o.policy = policy + o.data = data + return nil +} + +// EvalOpts define options for performing an evaluation +type EvalOpts struct { + Entrypoint int32 + Input *any + Metrics metrics.Metrics + Time time.Time + Seed io.Reader + InterQueryBuiltinCache cache.InterQueryCache + NDBuiltinCache builtins.NDBCache + PrintHook print.Hook + Capabilities *ast.Capabilities +} + +// Eval evaluates the policy with the given input, returning the +// evaluation results. If no policy was configured at construction +// time nor set after, the function returns ErrNotReady. It returns +// ErrInternal if any other error occurs. +func (o *OPA) Eval(ctx context.Context, opts EvalOpts) (*Result, error) { + if o.pool == nil { + return nil, errNotReady + } + + m := opts.Metrics + if m == nil { + m = metrics.New() + } + + instance, err := o.pool.Acquire(ctx, m) + if err != nil { + return nil, err + } + + defer o.pool.Release(instance, m) + + result, err := instance.Eval(ctx, opts.Entrypoint, opts.Input, m, opts.Seed, opts.Time, opts.InterQueryBuiltinCache, opts.NDBuiltinCache, opts.PrintHook, opts.Capabilities) + if err != nil { + return nil, err + } + + return &Result{Result: result}, nil +} + +// Close waits until all the pending evaluations complete and then +// releases all the resources allocated. Eval will return ErrClosed +// afterwards. +func (o *OPA) Close() { + if o.pool == nil { + return + } + + o.mutex.Lock() + defer o.mutex.Unlock() + + o.pool.Close() +} + +// Entrypoints returns a mapping of entrypoint name to ID for use by Eval() and EvalBool(). +func (o *OPA) Entrypoints(ctx context.Context) (map[string]int32, error) { + instance, err := o.pool.Acquire(ctx, metrics.New()) + if err != nil { + return nil, err + } + + defer o.pool.Release(instance, metrics.New()) + + return instance.Entrypoints(), nil +} diff --git a/third_party/opa/internal/wasm/sdk/opa/opa_bench_test.go b/third_party/opa/internal/wasm/sdk/opa/opa_bench_test.go new file mode 100644 index 000000000000..85d313a142a5 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/opa_bench_test.go @@ -0,0 +1,206 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build opa_wasm +// +build opa_wasm + +package opa_test + +import ( + "context" + "fmt" + "testing" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa" + "github.com/open-policy-agent/opa/internal/wasm/util" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func BenchmarkWasmRego(b *testing.B) { + policy := compileRegoToWasm("a = true", "data.p.a = x", false) + instance, _ := opa.New(). + WithPolicyBytes(policy). + WithPoolSize(1). + Init() + + b.ReportAllocs() + b.ResetTimer() + + ctx := context.Background() + var input any = make(map[string]any) + + for i := 0; i < b.N; i++ { + if _, err := instance.Eval(ctx, opa.EvalOpts{Input: &input}); err != nil { + panic(err) + } + } +} + +func BenchmarkGoRego(b *testing.B) { + pq := compileRego(`package p + +a = true`, "data.p.a = x") + + b.ReportAllocs() + b.ResetTimer() + + ctx := context.Background() + input := make(map[string]any) + + for i := 0; i < b.N; i++ { + if _, err := pq.Eval(ctx, rego.EvalInput(input)); err != nil { + panic(err) + } + } +} + +func BenchmarkWasmCompilation(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = compileRegoToWasm("a = true", "data.p.a = x", false) + } +} + +func BenchmarkWASMArrayIteration(b *testing.B) { + sizes := []int{10, 100, 1000, 10000} + for _, n := range sizes { + b.Run(fmt.Sprint(n), func(b *testing.B) { + benchmarkIteration(b, test.ArrayIterationBenchmarkModule(n)) + }) + } +} + +func BenchmarkWASMSetIteration(b *testing.B) { + sizes := []int{10, 100, 1000, 10000} + for _, n := range sizes { + b.Run(fmt.Sprint(n), func(b *testing.B) { + benchmarkIteration(b, test.SetIterationBenchmarkModule(n)) + }) + } +} + +func BenchmarkWASMObjectIteration(b *testing.B) { + sizes := []int{10, 100, 1000, 10000} + for _, n := range sizes { + b.Run(fmt.Sprint(n), func(b *testing.B) { + benchmarkIteration(b, test.ObjectIterationBenchmarkModule(n)) + }) + } +} + +var r *opa.Result + +func benchmarkIteration(b *testing.B, module string) { + query := "data.test.main = x" + policy := compileRegoToWasm(module, query, false) + + instance, err := opa.New(). + WithPolicyBytes(policy). + WithMemoryLimits(2*util.PageSize, 47*util.PageSize). + WithPoolSize(1). + Init() + if err != nil { + b.Fatalf("init sdk: %v", err) + } + + b.ResetTimer() + ctx := context.Background() + var input any = make(map[string]any) + + for i := 0; i < b.N; i++ { + r, err = instance.Eval(ctx, opa.EvalOpts{Input: &input}) + if err != nil { + b.Fatalf("Unexpected query error: %v", err) + } + if string(r.Result) != `{{"x":true}}` { + b.Errorf("unexpected result: %s", string(r.Result)) + } + } +} + +func BenchmarkWASMLargeJSON(b *testing.B) { + for _, kv := range []struct{ key, val int }{ + {10, 10}, + {10, 100}, + {10, 1000}, + {10, 10000}, + {100, 100}, + {100, 1000}, + } { + b.Run(fmt.Sprintf("%dx%d", kv.key, kv.val), func(b *testing.B) { + ctx := context.Background() + data := test.GenerateJSONBenchmarkData(kv.key, kv.val) + + // Read data.values N times inside query. + query := "data.keys[_] = x; data.values = y" + policy := compileRegoToWasm("", query, false) + + instance, err := opa.New(). + WithPolicyBytes(policy). + WithDataJSON(data). + WithMemoryLimits(200*util.PageSize, 600*util.PageSize). // This is rather much + WithPoolSize(1). + Init() + if err != nil { + b.Fatalf("init sdk: %v", err) + } + + b.ResetTimer() + var input any = make(map[string]any) + + for i := 0; i < b.N; i++ { + r, err = instance.Eval(ctx, opa.EvalOpts{Input: &input}) + if err != nil { + b.Fatalf("Unexpected query error: %v", err) + } + } + }) + } +} + +func BenchmarkWASMVirtualDocs(b *testing.B) { + for _, kv := range []struct{ total, hit int }{ + {1, 1}, + {10, 1}, + {100, 1}, + {1000, 1}, + {10, 10}, + {100, 10}, + {1000, 10}, + {100, 100}, + {1000, 100}, + {1000, 1000}, + } { + b.Run(fmt.Sprintf("total=%d/hit=%d", kv.total, kv.hit), func(b *testing.B) { + runVirtualDocsBenchmark(b, kv.total, kv.hit) + }) + } +} + +func runVirtualDocsBenchmark(b *testing.B, numTotalRules, numHitRules int) { + ctx := context.Background() + module, input := test.GenerateVirtualDocsBenchmarkData(numTotalRules, numHitRules) + query := "data.a.b.c.allow = x" + + policy := compileRegoToWasm(module, query, false) + + instance, err := opa.New(). + WithPolicyBytes(policy). + WithMemoryLimits(8*util.PageSize, 8*util.PageSize). + WithPoolSize(1). + Init() + if err != nil { + b.Fatalf("init sdk: %v", err) + } + + b.ResetTimer() + var inp any = input + + for i := 0; i < b.N; i++ { + r, err = instance.Eval(ctx, opa.EvalOpts{Input: &inp}) + if err != nil { + b.Fatalf("Unexpected query error: %v", err) + } + } +} diff --git a/third_party/opa/internal/wasm/sdk/opa/opa_test.go b/third_party/opa/internal/wasm/sdk/opa/opa_test.go new file mode 100644 index 000000000000..8dbc261ece3e --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/opa/opa_test.go @@ -0,0 +1,448 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build opa_wasm +// +build opa_wasm + +package opa_test + +import ( + "context" + "fmt" + "os" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa" + wasm_util "github.com/open-policy-agent/opa/internal/wasm/util" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/compile" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/util" +) + +// control dumping in this file +const dump = false + +func TestOPA(t *testing.T) { + type Eval struct { + NewPolicy string + NewData string + Input string + Result string + } + + largeInput := `"` + strings.Repeat("a", 2*wasm_util.PageSize) + `"` + + tests := []struct { + Description string + Policy string + Query string + Data string + Evals []Eval + WantErr string // "" (or unset) means no error expected + Memory []uint32 // min, max; in pages + }{ + { + Description: "No input, no data, static policy", + Policy: `a = true`, + Query: "data.p.a = x", + Evals: []Eval{ + {Result: `{{"x": true}}`}, + {Result: `{{"x": true}}`}, + }, + }, + { + Description: "Only input changing", + Policy: `a = input`, + Query: "data.p.a = x", + Evals: []Eval{ + {Input: "false", Result: `{{"x": false}}`}, + {Input: "true", Result: `{{"x": true}}`}, + }, + }, + { + Description: "Only data changing", + Policy: `a = data.q`, + Query: "data.p.a = x", + Data: `{"q": false}`, + Evals: []Eval{ + {Result: `{{"x": false}}`}, + {NewData: `{"q": true}`, Result: `{{"x": true}}`}, + }, + }, + { + Description: "Only policy changing", + Policy: `a = data.q`, + Query: "data.p.a = x", + Data: `{"q": false, "r": true}`, + Evals: []Eval{ + {Result: `{{"x": false}}`}, + {NewPolicy: `a = data.r`, Result: `{{"x": true}}`}, + }, + }, + { + Description: "Policy and data changing", + Policy: `a = data.q`, + Query: "data.p.a = x", + Data: `{"q": 0, "r": 1}`, + Evals: []Eval{ + {Result: `{{"x": 0}}`}, + {NewPolicy: `a = data.r`, NewData: `{"q": 2, "r": 3}`, Result: `{{"x": 3}}`}, + }, + }, + { + Description: "Builtins", + Policy: `a = count(data.q) + sum(data.q)`, // builtin not implemented in wasm. + Query: "data.p.a = x", + Evals: []Eval{ + {NewData: `{"q": []}`, Result: `{{"x": 0}}`}, + {NewData: `{"q": [1, 2]}`, Result: `{{"x": 5}}`}, + }, + }, + { + Description: "Undefined decision", + Policy: `a = true`, + Query: "data.p.b = x", + Evals: []Eval{ + {Result: `set()`}, + }, + }, + { + Description: "Runtime error/object insert conflict", + Policy: `a = { "a": y | y := [1, 2][_] }`, + Query: "data.p.a.a = x", + Evals: []Eval{{}}, + WantErr: "internal_error: module.rego:2:5: object insert conflict", + }, + { + Description: "Runtime error/var assignment conflict", + Policy: `a = "b" if { input > 1 } +a = "c" if { input > 2 }`, + Query: "data.p.a = x", + Evals: []Eval{ + {Input: "3"}, + }, + WantErr: "internal_error: module.rego:3:1: var assignment conflict", + }, + { + Description: "Runtime error/else conflict-1", + Query: `data.p.q`, + Policy: ` + q if { + false + } + else = true if { + true + } + q = false`, + Evals: []Eval{{}}, + WantErr: "internal_error: module.rego:9:5: var assignment conflict", + }, + { + Description: "Runtime error/else conflict-2", + Query: `data.p.q`, + Policy: ` + q if { + false + } + else = false if { + true + } + q if { + false + } + else = true if { + true + }`, + Evals: []Eval{{}}, + WantErr: "internal_error: module.rego:12:5: var assignment conflict", + }, + // NOTE(sr): The next two test cases were used to replicate issue + // https://github.com/open-policy-agent/opa/issues/2962 -- their raison d'être + // is thus questionable, but it might be good to keep them around a bit. + { + Description: "Only input changing, regex.match", + Policy: ` + default hello = false + hello if { + regex.match("^world$", input.message) + }`, + Query: "data.p.hello = x", + Evals: []Eval{ + {Input: `{"message": "xxxxxxx"}`, Result: `{{"x": false}}`}, + {Input: `{"message": "world"}`, Result: `{{"x": true}}`}, + }, + }, + { + Description: "Only input changing, glob.match", + Policy: ` + default hello = false + hello if { + glob.match("world", [":"], input.message) + }`, + Query: "data.p.hello = x", + Evals: []Eval{ + {Input: `{"message": "xxxxxxx"}`, Result: `{{"x": false}}`}, + {Input: `{"message": "world"}`, Result: `{{"x": true}}`}, + }, + }, + { + Description: "regex.match with pattern from input", + Query: `x = regex.match(input.re, "foo")`, + Evals: []Eval{ + {Input: `{"re": "^foo$"}`, Result: `{{"x": true}}`}, + }, + }, + { + Description: "regex.find_all_string_submatch_n with pattern from input", + Query: `x = regex.find_all_string_submatch_n(input.re, "-axxxbyc-", -1)`, + Evals: []Eval{ + {Input: `{"re": "a(x*)b(y|z)c"}`, Result: `{{"x":[["axxxbyc","xxx","y"]]}}`}, + }, + }, + { + Description: "simplified", + Query: `x := "q"; y := data.p[x]`, + Policy: `p = 1 + q = 2`, + Evals: []Eval{ + {Result: `{{"y": 2, "x": "q"}}`}, + }, + }, + { + Description: "mpd init problem (#3110)", + Query: `data.p.main = x`, + Policy: `main if { numbers.range(1, 2)[_] == 2 }`, + Evals: []Eval{ + {Result: `{{"x": true}}`}, + {Result: `{{"x": true}}`}, + }, + }, + { + Description: "Virtual extent, undefined data", + Policy: `package a.b + c = 3`, + Query: `data == {"a": {"b": {"c": 3 }}}`, + Evals: []Eval{ + {Result: `{{}}`}, + }, + }, + { + Description: "input exceeds available memory, host fails to grow it", + Policy: `package a.b + p = true`, + Query: `data.a.b.p`, + Memory: []uint32{2, 3}, + Evals: []Eval{ + {Input: largeInput}, + }, + WantErr: "input: failed to grow memory by `2` (max pages 3)", + }, + { + Description: "input exceeds available memory, parsing it hits maximum", + Policy: `package a.b + p = true`, + Query: `data.a.b.p`, + Memory: []uint32{2, 4}, + Evals: []Eval{ + {Input: largeInput}, + }, + WantErr: "internal_error: opa_malloc: failed", + }, + { + Description: "input exceeds available memory, grows successfully", + Policy: `package a.b + p = true`, + Query: `data.a.b.p = x`, + Memory: []uint32{2, 8}, + Evals: []Eval{ + {Input: largeInput, Result: `{{"x":true}}`}, + }, + }, + } + + for _, test := range tests { + t.Run(test.Description, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + policy := compileRegoToWasm(test.Policy, test.Query, dump) + data := []byte(test.Data) + if len(data) == 0 { + data = nil + } + o := opa.New(). + WithPolicyBytes(policy). + WithDataBytes(data). + WithPoolSize(1) // Minimal pool size to test pooling. + if len(test.Memory) == 2 { + o.WithMemoryLimits(test.Memory[0]*wasm_util.PageSize, test.Memory[1]*wasm_util.PageSize) + } + + instance, err := o.Init() + if err != nil { + t.Fatal(err) + } + + // Execute each requested policy evaluation, with their inputs and updating data if requested. + + for _, eval := range test.Evals { + switch { + case eval.NewPolicy != "" && eval.NewData != "": + policy := compileRegoToWasm(eval.NewPolicy, test.Query, dump) + data := parseJSON(eval.NewData) + if err := instance.SetPolicyData(ctx, policy, data); err != nil { + t.Errorf(err.Error()) + } + + case eval.NewPolicy != "": + policy := compileRegoToWasm(eval.NewPolicy, test.Query, dump) + if err := instance.SetPolicy(ctx, policy); err != nil { + t.Errorf(err.Error()) + } + + case eval.NewData != "": + data := parseJSON(eval.NewData) + if err := instance.SetData(ctx, *data); err != nil { + t.Errorf(err.Error()) + } + } + + r, err := instance.Eval(ctx, opa.EvalOpts{Input: parseJSON(eval.Input)}) + if err != nil { + if test.WantErr == "" { // no error desired + t.Fatal(err.Error()) + } + if expected, actual := test.WantErr, err.Error(); expected != actual { + t.Fatalf("expected error %q, got %q", expected, actual) + } + return + } + if test.WantErr != "" { + t.Fatalf("expected error %q, got nil", test.WantErr) + } + + expected := ast.MustParseTerm(eval.Result) + if !ast.MustParseTerm(string(r.Result)).Equal(expected) { + t.Errorf("\nExpected: %v\nGot: %v\n", expected, string(r.Result)) + } + } + + instance.Close() + }) + } +} + +func TestNamedEntrypoint(t *testing.T) { + module := `package test + + a = 7 + b = a + ` + + ctx := context.Background() + + compiler := compile.New(). + WithTarget(compile.TargetWasm). + WithEntrypoints("test/a", "test/b"). + WithBundle(&bundle.Bundle{ + Modules: []bundle.ModuleFile{ + { + Path: "policy.rego", + URL: "policy.rego", + Raw: []byte(module), + Parsed: ast.MustParseModule(module), + }, + }, + }) + + err := compiler.Build(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + instance, err := opa.New(). + WithPolicyBytes(compiler.Bundle().WasmModules[0].Raw). + WithPoolSize(1). + Init() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + eps, err := instance.Entrypoints(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if len(eps) != 2 { + t.Fatalf("Expected 2 entrypoints, got: %+v", eps) + } + + a, err := instance.Eval(ctx, opa.EvalOpts{Entrypoint: eps["test/a"]}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + exp := ast.MustParseTerm(`{{"result":7}}`) + actual := ast.MustParseTerm(string(a.Result)) + if !actual.Equal(exp) { + t.Fatalf("Expected result for 'test/a' to be %s, got: %s", exp, actual) + } + + b, err := instance.Eval(ctx, opa.EvalOpts{Entrypoint: eps["test/b"]}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + actual = ast.MustParseTerm(string(b.Result)) + if !actual.Equal(exp) { + t.Fatalf("Expected result for 'test/b' to be %s, got: %s", exp, actual) + } +} + +// compileRegoToWasm is shared with the benchmarking functions in opa_bench_test.go; +// those function use helpers shared with topdown_bench_test.go, and they all use +// `package test` -- whereas the callers in this file don't provide the package at +// all and assume it'll be `p`. +func compileRegoToWasm(module string, query string, dump bool) []byte { + if !strings.HasPrefix(module, "package") { + module = fmt.Sprintf("package p\n%s", module) + } + opts := []func(*rego.Rego){ + rego.Query(query), + rego.Module("module.rego", module), + } + if dump { + opts = append(opts, rego.Dump(os.Stderr)) + } + cr, err := rego.New(opts...).Compile(context.Background(), rego.CompilePartial(false)) + if err != nil { + panic(err) + } + + return cr.Bytes +} + +func compileRego(module string, query string) rego.PreparedEvalQuery { + rego := rego.New( + rego.Query(query), + rego.Module("module.rego", module), + ) + pq, err := rego.PrepareForEval(context.Background()) + if err != nil { + panic(err) + } + + return pq +} + +func parseJSON(s string) *any { + if s == "" { + return nil + } + + v := util.MustUnmarshalJSON([]byte(s)) + return &v +} diff --git a/third_party/opa/internal/wasm/sdk/test/e2e/exceptions.yaml b/third_party/opa/internal/wasm/sdk/test/e2e/exceptions.yaml new file mode 100644 index 000000000000..d8eb5d5283d6 --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/test/e2e/exceptions.yaml @@ -0,0 +1,3 @@ +# Exception Format is : +"data/toplevel integer": "https://github.com/open-policy-agent/opa/issues/3711" +"data/nested integer": "https://github.com/open-policy-agent/opa/issues/3711" diff --git a/third_party/opa/internal/wasm/sdk/test/e2e/external_test.go b/third_party/opa/internal/wasm/sdk/test/e2e/external_test.go new file mode 100644 index 000000000000..00d6e203474c --- /dev/null +++ b/third_party/opa/internal/wasm/sdk/test/e2e/external_test.go @@ -0,0 +1,274 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build wasm_sdk_e2e +// +build wasm_sdk_e2e + +package e2e + +import ( + "bytes" + "context" + "encoding/json" + "flag" + "fmt" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/test/cases" + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +const opaRootDir = "../../../../../" + +var caseDir = flag.String("case-dir", filepath.Join(opaRootDir, "v1/test/cases/testdata/"), "set directory to load test cases from") +var exceptionsFile = flag.String("exceptions", "./exceptions.yaml", "set file to load a list of test names to exclude") + +var exceptions map[string]string + +func TestMain(m *testing.M) { + exceptions = map[string]string{} + + bs, err := os.ReadFile(*exceptionsFile) + if err != nil { + fmt.Println("Unable to load exceptions file: " + err.Error()) + os.Exit(1) + } + err = util.Unmarshal(bs, &exceptions) + if err != nil { + fmt.Println("Unable to parse exceptions file: " + err.Error()) + os.Exit(1) + } + + addTestSleepBuiltin() + + os.Exit(m.Run()) +} + +func TestWasmE2E(t *testing.T) { + + ctx := context.Background() + + regoVersions := map[string]ast.RegoVersion{ + "v0": ast.RegoV0, + "v1": ast.RegoV1, + } + for versionName, regoVersion := range regoVersions { + for _, tc := range cases.MustLoad(filepath.Join(*caseDir, versionName)).Sorted().Cases { + name := fmt.Sprintf("%s/%s", strings.TrimPrefix(tc.Filename, opaRootDir), tc.Note) + t.Run(name, func(t *testing.T) { + + if shouldSkip(t, tc) { + t.SkipNow() + } + + for k, v := range tc.Env { + t.Setenv(k, v) + } + + opts := []func(*rego.Rego){ + rego.Query(tc.Query), + rego.SetRegoVersion(regoVersion), + } + for i := range tc.Modules { + opts = append(opts, rego.Module(fmt.Sprintf("module-%d.rego", i), tc.Modules[i])) + } + if testing.Verbose() { + opts = append(opts, rego.Dump(os.Stderr)) + } + cr, err := rego.New(opts...).Compile(ctx) + if err != nil { + t.Fatal(err) + } + o := opa.New().WithPolicyBytes(cr.Bytes) + if tc.Data != nil { + o = o.WithDataJSON(tc.Data) + } + o, err = o.Init() + if err != nil { + t.Fatal(err) + } + + var input *any + + if tc.InputTerm != nil { + var x any = ast.MustParseTerm(*tc.InputTerm) + input = &x + } else if tc.Input != nil { + input = tc.Input + } + + result, err := o.Eval(ctx, opa.EvalOpts{Input: input}) + assert(t, tc, result, err) + }) + } + } +} + +func shouldSkip(t *testing.T, tc cases.TestCase) bool { + if reason, ok := exceptions[tc.Note]; ok { + t.Log("Skipping test case: " + reason) + return true + } + + return false +} + +func assert(t *testing.T, tc cases.TestCase, result *opa.Result, err error) { + t.Helper() + if tc.WantDefined != nil { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + assertDefined(t, defined(*tc.WantDefined), result) + } else if tc.WantResult != nil { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + assertResultSet(t, *tc.WantResult, tc.SortBindings, result) + } else if tc.WantErrorCode != nil || tc.WantError != nil { + // The WASM compiler does not support strict errors so if the error + // condition is only visible when strict errors are enabled, expect + // an empty/undefined result from evaluation + if tc.StrictError { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + assertEmptyResultSet(t, result) + return + } + if err == nil { + if result != nil { + t.Fatalf("expected error, got result %s", result.Result) + } + t.Fatal("expected error") + } + + assertErrorCode(t, *tc.WantErrorCode, err) + } +} + +type defined bool + +func (x defined) String() string { + if x { + return "defined" + } + return "undefined" +} + +func assertDefined(t *testing.T, want defined, result *opa.Result) { + t.Helper() + var rs []any + if err := util.NewJSONDecoder(bytes.NewReader(result.Result)).Decode(&rs); err != nil { + t.Fatal(err) + } + got := defined(len(rs) > 0) + if got != want { + t.Fatalf("expected %v but got %v", want, got) + } +} + +func assertEmptyResultSet(t *testing.T, result *opa.Result) { + if result == nil { + t.Fatal("unexpected nil result") + } + assertResultSet(t, []map[string]any{}, false, result) +} + +func assertResultSet(t *testing.T, want []map[string]any, sortBindings bool, result *opa.Result) { + t.Helper() + + exp := ast.NewSet() + for _, b := range want { + obj := ast.NewObject() + for k, v := range b { + astValue := ast.MustInterfaceToValue(v) + obj.Insert(ast.StringTerm(k), ast.NewTerm(astValue)) + } + exp.Add(ast.NewTerm(obj)) + } + + // Round trip the wasm result through JSON to convert sets into array + b := roundTripAstToJSON(result.Result, sortBindings) + got := ast.NewSet() + + b.Value.(*ast.Array).Foreach(func(x *ast.Term) { + obj := ast.NewObject() + x.Value.(ast.Object).Foreach(func(k, v *ast.Term) { + var val ast.Value + if a, ok := v.Value.(*ast.Array); ok && sortBindings { + val = a.Sorted() + } else { + val = v.Value + } + obj.Insert(k, ast.NewTerm(val)) + }) + got.Add(ast.NewTerm(obj)) + }) + + if exp.Compare(got) != 0 { + t.Fatalf("expected %v but got %v", exp, got) + } +} + +func assertErrorCode(t *testing.T, expected string, actual error) { + t.Helper() + switch expected { + case "eval_conflict_error": + exps := []string{"var assignment conflict", "object insert conflict"} + found := false + for _, exp := range exps { + if strings.Contains(actual.Error(), exp) { + found = true + break + } + } + if !found { + t.Errorf("expected %q to contain one of %v", actual, exps) + } + default: + t.Errorf("unmatched error: %v (expected %s)", actual, expected) + } +} + +func toAST(a any) *ast.Term { + + if bs, ok := a.([]byte); ok { + return ast.MustParseTerm(string(bs)) + } + + buf := bytes.NewBuffer(nil) + if err := json.NewEncoder(buf).Encode(a); err != nil { + panic(err) + } + + return ast.MustParseTerm(buf.String()) +} + +func roundTripAstToJSON(b []byte, sortBindings bool) *ast.Term { + j, err := ast.JSONWithOpt(ast.MustParseTerm(string(b)).Value, ast.JSONOpt{SortSets: sortBindings}) + if err != nil { + panic(err) + } + return toAST(j) +} + +func addTestSleepBuiltin() { + rego.RegisterBuiltin1(®o.Function{ + Name: "test.sleep", + Decl: types.NewFunction(types.Args(types.S), types.NewNull()), + }, func(_ rego.BuiltinContext, op *ast.Term) (*ast.Term, error) { + d, _ := time.ParseDuration(string(op.Value.(ast.String))) + time.Sleep(d) + return ast.NullTerm(), nil + }) +} diff --git a/third_party/opa/internal/wasm/types/types.go b/third_party/opa/internal/wasm/types/types.go new file mode 100644 index 000000000000..4e2b776220ca --- /dev/null +++ b/third_party/opa/internal/wasm/types/types.go @@ -0,0 +1,36 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package types defines the WASM value type constants. +package types + +// ValueType represents an intrinsic value in WASM. +type ValueType int + +// Defines the intrinsic value types. +const ( + I32 ValueType = iota + I64 + F32 + F64 +) + +func (tpe ValueType) String() string { + if tpe == I32 { + return "i32" + } else if tpe == I64 { + return "i64" + } else if tpe == F32 { + return "f32" + } + return "f64" +} + +// ElementType defines the type of table elements. +type ElementType int + +const ( + // Anyfunc is the union of all table types. + Anyfunc ElementType = iota +) diff --git a/third_party/opa/internal/wasm/util/util.go b/third_party/opa/internal/wasm/util/util.go new file mode 100644 index 000000000000..42b81256ebe4 --- /dev/null +++ b/third_party/opa/internal/wasm/util/util.go @@ -0,0 +1,18 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +// PageSize represents the WASM page size in bytes. +const PageSize = 65535 + +// Pages converts a byte size to Pages, rounding up as necessary. +func Pages(n uint32) uint32 { + pages := n / PageSize + if pages*PageSize == n { + return pages + } + + return pages + 1 +} diff --git a/third_party/opa/ir/doc.go b/third_party/opa/ir/doc.go new file mode 100644 index 000000000000..6839297f2a9b --- /dev/null +++ b/third_party/opa/ir/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package ir diff --git a/third_party/opa/ir/encoding/doc.go b/third_party/opa/ir/encoding/doc.go new file mode 100644 index 000000000000..bf2818e5247f --- /dev/null +++ b/third_party/opa/ir/encoding/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package encoding diff --git a/third_party/opa/ir/encoding/encoding_test.go b/third_party/opa/ir/encoding/encoding_test.go new file mode 100644 index 000000000000..b4c88d493c79 --- /dev/null +++ b/third_party/opa/ir/encoding/encoding_test.go @@ -0,0 +1,72 @@ +package encoding + +import ( + "bytes" + "encoding/json" + "testing" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/internal/planner" + "github.com/open-policy-agent/opa/ir" +) + +func TestRoundTrip(t *testing.T) { + + // Note: v0 module + c, err := ast.CompileModules(map[string]string{ + "test.rego": ` + package test + + p { + input.foo == 7 + } + `, + }) + + if err != nil { + t.Fatal(err) + } + + modules := []*ast.Module{} + + for _, m := range c.Modules { + modules = append(modules, m) + } + + planner := planner.New(). + WithQueries([]planner.QuerySet{ + { + Name: "main", + Queries: []ast.Body{ + ast.MustParseBody("data.test.p = true"), + }, + }, + }). + WithModules(modules). + WithBuiltinDecls(ast.BuiltinMap) + + plan, err := planner.Plan() + if err != nil { + t.Fatal(err) + } + + bs, err := json.MarshalIndent(plan, "", " ") + if err != nil { + t.Fatal(err) + } + + var cpy ir.Policy + err = json.Unmarshal(bs, &cpy) + if err != nil { + t.Fatal(err) + } + + bs2, err := json.MarshalIndent(plan, "", " ") + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(bs, bs2) { + t.Fatal("expected bytes to be equal") + } +} diff --git a/third_party/opa/ir/ir.go b/third_party/opa/ir/ir.go new file mode 100644 index 000000000000..d43fc56e900e --- /dev/null +++ b/third_party/opa/ir/ir.go @@ -0,0 +1,217 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package ir defines an intermediate representation (IR) for Rego. +// +// The IR specifies an imperative execution model for Rego policies similar to a +// query plan in traditional databases. +package ir + +import ( + v1 "github.com/open-policy-agent/opa/v1/ir" +) + +type ( + // Policy represents a planned policy query. + Policy = v1.Policy + + // Static represents a static data segment that is indexed into by the policy. + Static = v1.Static + + // BuiltinFunc represents a built-in function that may be required by the + // policy. + BuiltinFunc = v1.BuiltinFunc + + // Plans represents a collection of named query plans to expose in the policy. + Plans = v1.Plans + + // Funcs represents a collection of planned functions to include in the + // policy. + Funcs = v1.Funcs + + // Func represents a named plan (function) that can be invoked. Functions + // accept one or more parameters and return a value. By convention, the + // input document and data documents are always passed as the first and + // second arguments (respectively). + Func = v1.Func + + // Plan represents an ordered series of blocks to execute. Plan execution + // stops when a return statement is reached. Blocks are executed in-order. + Plan = v1.Plan + + // Block represents an ordered sequence of statements to execute. Blocks are + // executed until a return statement is encountered, a statement is undefined, + // or there are no more statements. If all statements are defined but no return + // statement is encountered, the block is undefined. + Block = v1.Block + + // Stmt represents an operation (e.g., comparison, loop, dot, etc.) to execute. + Stmt = v1.Stmt + + // Local represents a plan-scoped variable. + // + // TODO(tsandall): should this be int32 for safety? + Local = v1.Local + + // StringConst represents a string value. + StringConst = v1.StringConst +) + +const ( + // Input is the local variable that refers to the global input document. + Input = v1.Input + + // Data is the local variable that refers to the global data document. + Data = v1.Data + + // Unused is the free local variable that can be allocated in a plan. + Unused = v1.Unused +) + +// Operand represents a value that a statement operates on. +type Operand = v1.Operand + +// Val represents an abstract value that statements operate on. There are currently +// 3 types of values: +// +// 1. Local - a local variable that can refer to any type. +// 2. StringIndex - a string constant that refers to a compiled string. +// 3. Bool - a boolean constant. +type Val = v1.Val + +// StringIndex represents the index into the plan's list of constant strings +// of a constant string. +type StringIndex = v1.StringIndex + +// Bool represents a constant boolean. +type Bool = v1.Bool + +// ReturnLocalStmt represents a return statement that yields a local value. +type ReturnLocalStmt = v1.ReturnLocalStmt + +// CallStmt represents a named function call. The result should be stored in the +// result local. +type CallStmt = v1.CallStmt + +// CallDynamicStmt represents an indirect (data) function call. The result should +// be stored in the result local. +type CallDynamicStmt = v1.CallDynamicStmt + +// BlockStmt represents a nested block. Nested blocks and break statements can +// be used to short-circuit execution. +type BlockStmt = v1.BlockStmt + +// BreakStmt represents a jump out of the current block. The index specifies how +// many blocks to jump starting from zero (the current block). Execution will +// continue from the end of the block that is jumped to. +type BreakStmt = v1.BreakStmt + +// DotStmt represents a lookup operation on a value (e.g., array, object, etc.) +// The source of a DotStmt may be a scalar value in which case the statement +// will be undefined. +type DotStmt = v1.DotStmt + +// LenStmt represents a length() operation on a local variable. The +// result is stored in the target local variable. +type LenStmt = v1.LenStmt + +// ScanStmt represents a linear scan over a composite value. The +// source may be a scalar in which case the block will never execute. +type ScanStmt = v1.ScanStmt + +// NotStmt represents a negated statement. +type NotStmt = v1.NotStmt + +// AssignIntStmt represents an assignment of an integer value to a +// local variable. +type AssignIntStmt = v1.AssignIntStmt + +// AssignVarStmt represents an assignment of one local variable to another. +type AssignVarStmt = v1.AssignVarStmt + +// AssignVarOnceStmt represents an assignment of one local variable to another. +// If the target is defined, execution aborts with a conflict error. +// +// TODO(tsandall): is there a better name for this? +type AssignVarOnceStmt = v1.AssignVarOnceStmt + +// ResetLocalStmt resets a local variable to 0. +type ResetLocalStmt = v1.ResetLocalStmt + +// MakeNullStmt constructs a local variable that refers to a null value. +type MakeNullStmt = v1.MakeNullStmt + +// MakeNumberIntStmt constructs a local variable that refers to an integer value. +type MakeNumberIntStmt = v1.MakeNumberIntStmt + +// MakeNumberRefStmt constructs a local variable that refers to a number stored as a string. +type MakeNumberRefStmt = v1.MakeNumberRefStmt + +// MakeArrayStmt constructs a local variable that refers to an array value. +type MakeArrayStmt = v1.MakeArrayStmt + +// MakeObjectStmt constructs a local variable that refers to an object value. +type MakeObjectStmt = v1.MakeObjectStmt + +// MakeSetStmt constructs a local variable that refers to a set value. +type MakeSetStmt = v1.MakeSetStmt + +// EqualStmt represents an value-equality check of two local variables. +type EqualStmt = v1.EqualStmt + +// NotEqualStmt represents a != check of two local variables. +type NotEqualStmt = v1.NotEqualStmt + +// IsArrayStmt represents a dynamic type check on a local variable. +type IsArrayStmt = v1.IsArrayStmt + +// IsObjectStmt represents a dynamic type check on a local variable. +type IsObjectStmt = v1.IsObjectStmt + +// IsSetStmt represents a dynamic type check on a local variable. +type IsSetStmt = v1.IsSetStmt + +// IsDefinedStmt represents a check of whether a local variable is defined. +type IsDefinedStmt = v1.IsDefinedStmt + +// IsUndefinedStmt represents a check of whether local variable is undefined. +type IsUndefinedStmt = v1.IsUndefinedStmt + +// ArrayAppendStmt represents a dynamic append operation of a value +// onto an array. +type ArrayAppendStmt = v1.ArrayAppendStmt + +// ObjectInsertStmt represents a dynamic insert operation of a +// key/value pair into an object. +type ObjectInsertStmt = v1.ObjectInsertStmt + +// ObjectInsertOnceStmt represents a dynamic insert operation of a key/value +// pair into an object. If the key already exists and the value differs, +// execution aborts with a conflict error. +type ObjectInsertOnceStmt = v1.ObjectInsertOnceStmt + +// ObjectMergeStmt performs a recursive merge of two object values. If either of +// the locals refer to non-object values this operation will abort with a +// conflict error. Overlapping object keys are merged recursively. +type ObjectMergeStmt = v1.ObjectMergeStmt + +// SetAddStmt represents a dynamic add operation of an element into a set. +type SetAddStmt = v1.SetAddStmt + +// WithStmt replaces the Local or a portion of the document referred to by the +// Local with the Value and executes the contained block. If the Path is +// non-empty, the Value is upserted into the Local. If the intermediate nodes in +// the Local referred to by the Path do not exist, they will be created. When +// the WithStmt finishes the Local is reset to it's original value. +type WithStmt = v1.WithStmt + +// NopStmt adds a nop instruction. Useful during development and debugging only. +type NopStmt = v1.NopStmt + +// ResultSetAddStmt adds a value into the result set returned by the query plan. +type ResultSetAddStmt = v1.ResultSetAddStmt + +// Location records the filen index, and the row and column inside that file +// that a statement can be connected to. +type Location = v1.Location diff --git a/third_party/opa/ir/pretty.go b/third_party/opa/ir/pretty.go new file mode 100644 index 000000000000..59be4a332089 --- /dev/null +++ b/third_party/opa/ir/pretty.go @@ -0,0 +1,16 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ir + +import ( + "io" + + v1 "github.com/open-policy-agent/opa/v1/ir" +) + +// Pretty writes a human-readable representation of an IR object to w. +func Pretty(w io.Writer, x any) error { + return v1.Pretty(w, x) +} diff --git a/third_party/opa/ir/walk.go b/third_party/opa/ir/walk.go new file mode 100644 index 000000000000..5bd74c7a5fb5 --- /dev/null +++ b/third_party/opa/ir/walk.go @@ -0,0 +1,15 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ir + +import v1 "github.com/open-policy-agent/opa/v1/ir" + +// Visitor defines the interface for visiting IR nodes. +type Visitor = v1.Visitor + +// Walk invokes the visitor for nodes under x. +func Walk(vis Visitor, x any) error { + return v1.Walk(vis, x) +} diff --git a/third_party/opa/keys/doc.go b/third_party/opa/keys/doc.go new file mode 100644 index 000000000000..ffcc0f7ca0a4 --- /dev/null +++ b/third_party/opa/keys/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package keys diff --git a/third_party/opa/keys/keys.go b/third_party/opa/keys/keys.go new file mode 100644 index 000000000000..9eca2effcba4 --- /dev/null +++ b/third_party/opa/keys/keys.go @@ -0,0 +1,25 @@ +package keys + +import ( + "encoding/json" + + v1 "github.com/open-policy-agent/opa/v1/keys" +) + +// IsSupportedAlgorithm true if provided alg is supported +func IsSupportedAlgorithm(alg string) bool { + return v1.IsSupportedAlgorithm(alg) +} + +// Config holds the keys used to sign or verify bundles and tokens +type Config = v1.Config + +// NewKeyConfig return a new Config +func NewKeyConfig(key, alg, scope string) (*Config, error) { + return v1.NewKeyConfig(key, alg, scope) +} + +// ParseKeysConfig returns a map containing the key and the signing algorithm +func ParseKeysConfig(raw json.RawMessage) (map[string]*Config, error) { + return v1.ParseKeysConfig(raw) +} diff --git a/third_party/opa/loader/doc.go b/third_party/opa/loader/doc.go new file mode 100644 index 000000000000..9f60920d9553 --- /dev/null +++ b/third_party/opa/loader/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package loader diff --git a/third_party/opa/loader/errors.go b/third_party/opa/loader/errors.go new file mode 100644 index 000000000000..8dc70b867383 --- /dev/null +++ b/third_party/opa/loader/errors.go @@ -0,0 +1,12 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package loader + +import ( + v1 "github.com/open-policy-agent/opa/v1/loader" +) + +// Errors is a wrapper for multiple loader errors. +type Errors = v1.Errors diff --git a/third_party/opa/loader/extension/doc.go b/third_party/opa/loader/extension/doc.go new file mode 100644 index 000000000000..cf457092d2d9 --- /dev/null +++ b/third_party/opa/loader/extension/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package extension diff --git a/third_party/opa/loader/extension/extension.go b/third_party/opa/loader/extension/extension.go new file mode 100644 index 000000000000..2cae4febcb6a --- /dev/null +++ b/third_party/opa/loader/extension/extension.go @@ -0,0 +1,29 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package extension + +import ( + v1 "github.com/open-policy-agent/opa/v1/loader/extension" +) + +// Handler is used to unmarshal a byte slice of a registered extension +// EXPERIMENTAL: Please don't rely on this functionality, it may go +// away or change in the future. +type Handler = v1.Handler + +// RegisterExtension registers a Handler for a certain file extension, including +// the dot: ".json", not "json". +// EXPERIMENTAL: Please don't rely on this functionality, it may go +// away or change in the future. +func RegisterExtension(name string, handler Handler) { + v1.RegisterExtension(name, handler) +} + +// FindExtension ios used to look up a registered extension Handler +// EXPERIMENTAL: Please don't rely on this functionality, it may go +// away or change in the future. +func FindExtension(ext string) Handler { + return v1.FindExtension(ext) +} diff --git a/third_party/opa/loader/filter/doc.go b/third_party/opa/loader/filter/doc.go new file mode 100644 index 000000000000..a6138df6a2d4 --- /dev/null +++ b/third_party/opa/loader/filter/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package filter diff --git a/third_party/opa/loader/filter/filter.go b/third_party/opa/loader/filter/filter.go new file mode 100644 index 000000000000..cfd03b33a8d4 --- /dev/null +++ b/third_party/opa/loader/filter/filter.go @@ -0,0 +1,5 @@ +package filter + +import v1 "github.com/open-policy-agent/opa/v1/loader/filter" + +type LoaderFilter = v1.LoaderFilter diff --git a/third_party/opa/loader/loader.go b/third_party/opa/loader/loader.go new file mode 100644 index 000000000000..9b2f91d4e9f4 --- /dev/null +++ b/third_party/opa/loader/loader.go @@ -0,0 +1,145 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package loader contains utilities for loading files into OPA. +package loader + +import ( + "io/fs" + "os" + "strings" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/bundle" + v1 "github.com/open-policy-agent/opa/v1/loader" +) + +// Result represents the result of successfully loading zero or more files. +type Result = v1.Result + +// RegoFile represents the result of loading a single Rego source file. +type RegoFile = v1.RegoFile + +// Filter defines the interface for filtering files during loading. If the +// filter returns true, the file should be excluded from the result. +type Filter = v1.Filter + +// 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 v1.GlobExcludeName(pattern, minDepth) +} + +// FileLoader defines an interface for loading OPA data files +// and Rego policies. +type FileLoader = v1.FileLoader + +// NewFileLoader returns a new FileLoader instance. +func NewFileLoader() FileLoader { + return v1.NewFileLoader().WithRegoVersion(ast.DefaultRegoVersion) +} + +// GetBundleDirectoryLoader returns a bundle directory loader which can be used to load +// files in the directory +func GetBundleDirectoryLoader(path string) (bundle.DirectoryLoader, bool, error) { + return v1.GetBundleDirectoryLoader(path) +} + +// GetBundleDirectoryLoaderWithFilter returns a bundle directory loader which can be used to load +// files in the directory after applying the given filter. +func GetBundleDirectoryLoaderWithFilter(path string, filter Filter) (bundle.DirectoryLoader, bool, error) { + return v1.GetBundleDirectoryLoaderWithFilter(path, filter) +} + +// GetBundleDirectoryLoaderFS returns a bundle directory loader which can be used to load +// files in the directory. +func GetBundleDirectoryLoaderFS(fsys fs.FS, path string, filter Filter) (bundle.DirectoryLoader, bool, error) { + return v1.GetBundleDirectoryLoaderFS(fsys, path, filter) +} + +// FilteredPaths is the same as FilterPathsFS using the current diretory file +// system +func FilteredPaths(paths []string, filter Filter) ([]string, error) { + return v1.FilteredPaths(paths, filter) +} + +// FilteredPathsFS return a list of files from the specified +// paths while applying the given filters. If any filter returns true, the +// file/directory is excluded. +func FilteredPathsFS(fsys fs.FS, paths []string, filter Filter) ([]string, error) { + return v1.FilteredPathsFS(fsys, paths, filter) +} + +// Schemas loads a schema set from the specified file path. +func Schemas(schemaPath string) (*ast.SchemaSet, error) { + return v1.Schemas(schemaPath) +} + +// All returns a Result object loaded (recursively) from the specified paths. +// Deprecated: Use FileLoader.Filtered() instead. +func All(paths []string) (*Result, error) { + return NewFileLoader().Filtered(paths, nil) +} + +// Filtered returns a Result object loaded (recursively) from the specified +// paths while applying the given filters. If any filter returns true, the +// file/directory is excluded. +// Deprecated: Use FileLoader.Filtered() instead. +func Filtered(paths []string, filter Filter) (*Result, error) { + return NewFileLoader().Filtered(paths, filter) +} + +// AsBundle loads a path as a bundle. If it is a single file +// it will be treated as a normal tarball bundle. If a directory +// is supplied it will be loaded as an unzipped bundle tree. +// Deprecated: Use FileLoader.AsBundle() instead. +func AsBundle(path string) (*bundle.Bundle, error) { + return NewFileLoader().AsBundle(path) +} + +// 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, _ int) bool { + return !info.IsDir() && !strings.HasSuffix(info.Name(), bundle.RegoExt) + }) +} + +// Rego is deprecated. Use RegoWithOpts instead. +func Rego(path string) (*RegoFile, error) { + return RegoWithOpts(path, ast.ParserOptions{}) +} + +// RegoWithOpts returns a RegoFile object loaded from the given path. +func RegoWithOpts(path string, opts ast.ParserOptions) (*RegoFile, error) { + if opts.RegoVersion == ast.RegoUndefined { + opts.RegoVersion = ast.DefaultRegoVersion + } + + return v1.RegoWithOpts(path, opts) +} + +// CleanPath returns the normalized version of a path that can be used as an identifier. +func CleanPath(path string) string { + return v1.CleanPath(path) +} + +// Paths returns a sorted list of files contained at path. If recurse is true +// and path is a directory, then Paths will walk the directory structure +// recursively and list files at each level. +func Paths(path string, recurse bool) (paths []string, err error) { + return v1.Paths(path, recurse) +} + +// Dirs resolves filepaths to directories. It will return a list of unique +// directories. +func Dirs(paths []string) []string { + return v1.Dirs(paths) +} + +// SplitPrefix returns a tuple specifying the document prefix and the file +// path. +func SplitPrefix(path string) ([]string, string) { + return v1.SplitPrefix(path) +} diff --git a/third_party/opa/loader/loader_test.go b/third_party/opa/loader/loader_test.go new file mode 100644 index 000000000000..d9070e2562f7 --- /dev/null +++ b/third_party/opa/loader/loader_test.go @@ -0,0 +1,364 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package loader + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/util/test" +) + +func TestAll_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0", // v0 is the default rego-version + module: `package test + +p[x] { + x := "a" +}`, + }, + { + note: "rego.v1 import", + module: `package test +import rego.v1 + +p contains x if { + x := "a" +}`, + }, + { + note: "v1", + module: `package test + +p contains x if { + x := "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: var cannot be used for rule name", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "/test.rego": tc.module} + + test.WithTempFS(files, func(rootDir string) { + moduleFile := filepath.Join(rootDir, "test.rego") + loaded, err := All([]string{moduleFile}) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := ast.MustParseModule(files["/test.rego"]) + if !expected.Equal(loaded.Modules[CleanPath(moduleFile)].Parsed) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, loaded.Modules[moduleFile]) + } + } + }) + }) + } +} + +func TestFiltered_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0", // v0 is the default rego-version + module: `package test + +p[x] { + x := "a" +}`, + }, + { + note: "rego.v1 import", + module: `package test +import rego.v1 + +p contains x if { + x := "a" +}`, + }, + { + note: "v1", + module: `package test + +p contains x if { + x := "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: var cannot be used for rule name", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "/test.rego": tc.module} + + test.WithTempFS(files, func(rootDir string) { + moduleFile := filepath.Join(rootDir, "test.rego") + filter := func(string, os.FileInfo, int) bool { + return false + } + + loaded, err := Filtered([]string{moduleFile}, filter) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := ast.MustParseModule(files["/test.rego"]) + if !expected.Equal(loaded.Modules[CleanPath(moduleFile)].Parsed) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, loaded.Modules[moduleFile]) + } + } + }) + }) + } +} + +func TestRego_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0", // v0 is the default rego-version + module: `package test + +p[x] { + x := "a" +}`, + }, + { + note: "rego.v1 import", + module: `package test +import rego.v1 + +p contains x if { + x := "a" +}`, + }, + { + note: "v1", + module: `package test + +p contains x if { + x := "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: var cannot be used for rule name", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "/test.rego": tc.module} + + test.WithTempFS(files, func(rootDir string) { + moduleFile := filepath.Join(rootDir, "test.rego") + loaded, err := Rego(moduleFile) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := ast.MustParseModule(files["/test.rego"]) + if !expected.Equal(loaded.Parsed) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, loaded.Parsed) + } + } + }) + }) + } +} + +func TestAllRegos_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0", // v0 is the default rego-version + module: `package test + +p[x] { + x := "a" +}`, + }, + { + note: "rego.v1 import", + module: `package test +import rego.v1 + +p contains x if { + x := "a" +}`, + }, + { + note: "v1", + module: `package test + +p contains x if { + x := "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: var cannot be used for rule name", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "/test.rego": tc.module} + + test.WithTempFS(files, func(rootDir string) { + moduleFile := filepath.Join(rootDir, "test.rego") + loaded, err := AllRegos([]string{moduleFile}) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := ast.MustParseModule(files["/test.rego"]) + if !expected.Equal(loaded.Modules[CleanPath(moduleFile)].Parsed) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, loaded.Modules[moduleFile]) + } + } + }) + }) + } +} + +func TestLoadRego_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0", // v0 is the default rego-version + module: `package test + +p[x] { + x := "a" +}`, + }, + { + note: "rego.v1 import", + module: `package test +import rego.v1 + +p contains x if { + x := "a" +}`, + }, + { + note: "v1", + module: `package test + +p contains x if { + x := "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: var cannot be used for rule name", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "/test.rego": tc.module} + + test.WithTempFS(files, func(rootDir string) { + moduleFile := filepath.Join(rootDir, "test.rego") + loaded, err := NewFileLoader().All([]string{moduleFile}) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := ast.MustParseModule(files["/test.rego"]) + if !expected.Equal(loaded.Modules[CleanPath(moduleFile)].Parsed) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, loaded.Modules[moduleFile]) + } + } + }) + }) + } +} diff --git a/third_party/opa/logging/doc.go b/third_party/opa/logging/doc.go new file mode 100644 index 000000000000..665cb369a615 --- /dev/null +++ b/third_party/opa/logging/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package logging diff --git a/third_party/opa/logging/logging.go b/third_party/opa/logging/logging.go new file mode 100644 index 000000000000..f84deb86bdae --- /dev/null +++ b/third_party/opa/logging/logging.go @@ -0,0 +1,79 @@ +package logging + +import ( + "context" + + v1 "github.com/open-policy-agent/opa/v1/logging" +) + +// Level log level for Logger +type Level = v1.Level + +const ( + // Error error log level + Error = v1.Error + // Warn warn log level + Warn = v1.Warn + // Info info log level + Info = v1.Info + // Debug debug log level + Debug = v1.Debug +) + +// Logger provides interface for OPA logger implementations +type Logger = v1.Logger + +// StandardLogger is the default OPA logger implementation. +type StandardLogger = v1.StandardLogger + +// New returns a new standard logger. +func New() *StandardLogger { + return v1.New() +} + +// Get returns the standard logger used throughout OPA. +// +// Deprecated. Do not rely on the global logger. +func Get() *StandardLogger { + return v1.Get() +} + +// NoOpLogger logging implementation that does nothing +type NoOpLogger = v1.NoOpLogger + +// NewNoOpLogger instantiates new NoOpLogger +func NewNoOpLogger() *NoOpLogger { + return v1.NewNoOpLogger() +} + +// RequestContext represents the request context used to store data +// related to the request that could be used on logs. +type RequestContext = v1.RequestContext + +type HTTPRequestContext = v1.HTTPRequestContext + +// NewContext returns a copy of parent with an associated RequestContext. +func NewContext(parent context.Context, val *RequestContext) context.Context { + return v1.NewContext(parent, val) +} + +// FromContext returns the RequestContext associated with ctx, if any. +func FromContext(ctx context.Context) (*RequestContext, bool) { + return v1.FromContext(ctx) +} + +func WithHTTPRequestContext(parent context.Context, val *HTTPRequestContext) context.Context { + return v1.WithHTTPRequestContext(parent, val) +} + +func HTTPRequestContextFromContext(ctx context.Context) (*HTTPRequestContext, bool) { + return v1.HTTPRequestContextFromContext(ctx) +} + +func WithDecisionID(parent context.Context, id string) context.Context { + return v1.WithDecisionID(parent, id) +} + +func DecisionIDFromContext(ctx context.Context) (string, bool) { + return v1.DecisionIDFromContext(ctx) +} diff --git a/third_party/opa/logging/test/doc.go b/third_party/opa/logging/test/doc.go new file mode 100644 index 000000000000..dffe11ff7c70 --- /dev/null +++ b/third_party/opa/logging/test/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package test diff --git a/third_party/opa/logging/test/test.go b/third_party/opa/logging/test/test.go new file mode 100644 index 000000000000..1379c1eea3f7 --- /dev/null +++ b/third_party/opa/logging/test/test.go @@ -0,0 +1,16 @@ +package test + +import ( + v1 "github.com/open-policy-agent/opa/v1/logging/test" +) + +// LogEntry represents a log message. +type LogEntry = v1.LogEntry + +// Logger implementation that buffers messages for test purposes. +type Logger = v1.Logger + +// New instantiates new Logger. +func New() *Logger { + return v1.New() +} diff --git a/third_party/opa/logo/logo-144x144.png b/third_party/opa/logo/logo-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..959fe586d43f38d1708e6f0b4980beec65699795 GIT binary patch literal 4589 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 literal 0 HcmV?d00001 diff --git a/third_party/opa/logo/logo.ico b/third_party/opa/logo/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..4f92c9b036228b0de1a5e2cc97614d2df5ff5bf8 GIT binary patch literal 8380 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 literal 0 HcmV?d00001 diff --git a/third_party/opa/logo/logo.png b/third_party/opa/logo/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..56427178c446bc2196ce0f83f76b89b1439a75c4 GIT binary patch literal 38210 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 literal 0 HcmV?d00001 diff --git a/third_party/opa/logo/logo.svg b/third_party/opa/logo/logo.svg new file mode 100644 index 000000000000..73f873b41e39 --- /dev/null +++ b/third_party/opa/logo/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/third_party/opa/main.go b/third_party/opa/main.go new file mode 100644 index 000000000000..eaa60b3a3719 --- /dev/null +++ b/third_party/opa/main.go @@ -0,0 +1,34 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package main + +import ( + "errors" + "os" + + "github.com/open-policy-agent/opa/cmd" +) + +func main() { + var exit int + defer func() { + if exit != 0 { + os.Exit(exit) + } + }() // orderly shutdown, run all defer routines + + if err := cmd.RootCommand.Execute(); err != nil { + var e *cmd.ExitError + if errors.As(err, &e) { + exit = e.Exit + } else { + exit = 1 + } + } +} + +//go:generate build/gen-run-go.sh internal/cmd/genopacapabilities/main.go capabilities.json +//go:generate build/gen-run-go.sh internal/cmd/genbuiltinmetadata/main.go builtin_metadata.json +//go:generate build/gen-run-go.sh internal/cmd/genversionindex/main.go v1/ast/version_index.json diff --git a/third_party/opa/main_windows.go b/third_party/opa/main_windows.go new file mode 100644 index 000000000000..4cdc9ff211d2 --- /dev/null +++ b/third_party/opa/main_windows.go @@ -0,0 +1,5 @@ +//go:build windows + +//go:generate build/gen-windows-versioninfo.sh $GOARCH + +package main diff --git a/third_party/opa/metrics/doc.go b/third_party/opa/metrics/doc.go new file mode 100644 index 000000000000..6a306991e799 --- /dev/null +++ b/third_party/opa/metrics/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package metrics diff --git a/third_party/opa/metrics/metrics.go b/third_party/opa/metrics/metrics.go new file mode 100644 index 000000000000..8f2f6c443552 --- /dev/null +++ b/third_party/opa/metrics/metrics.go @@ -0,0 +1,57 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package metrics contains helpers for performance metric management inside the policy engine. +package metrics + +import ( + v1 "github.com/open-policy-agent/opa/v1/metrics" +) + +// Well-known metric names. +const ( + BundleRequest = v1.BundleRequest + ServerHandler = v1.ServerHandler + ServerQueryCacheHit = v1.ServerQueryCacheHit + SDKDecisionEval = v1.SDKDecisionEval + RegoQueryCompile = v1.RegoQueryCompile + RegoQueryEval = v1.RegoQueryEval + RegoQueryParse = v1.RegoQueryParse + RegoModuleParse = v1.RegoModuleParse + RegoDataParse = v1.RegoDataParse + RegoModuleCompile = v1.RegoModuleCompile + RegoPartialEval = v1.RegoPartialEval + RegoInputParse = v1.RegoInputParse + RegoLoadFiles = v1.RegoLoadFiles + RegoLoadBundles = v1.RegoLoadBundles + RegoExternalResolve = v1.RegoExternalResolve +) + +// Info contains attributes describing the underlying metrics provider. +type Info = v1.Info + +// Metrics defines the interface for a collection of performance metrics in the +// policy engine. +type Metrics = v1.Metrics + +type TimerMetrics = v1.TimerMetrics + +// New returns a new Metrics object. +func New() Metrics { + return v1.New() +} + +// Timer defines the interface for a restartable timer that accumulates elapsed +// time. +type Timer = v1.Timer + +// Histogram defines the interface for a histogram with hardcoded percentiles. +type Histogram = v1.Histogram + +// Counter defines the interface for a monotonic increasing counter. +type Counter = v1.Counter + +func Statistics(num ...int64) any { + return v1.Statistics(num...) +} diff --git a/third_party/opa/misc/syntax/sublime/rego.sublime-syntax b/third_party/opa/misc/syntax/sublime/rego.sublime-syntax new file mode 100644 index 000000000000..2dc8ede45699 --- /dev/null +++ b/third_party/opa/misc/syntax/sublime/rego.sublime-syntax @@ -0,0 +1,97 @@ +%YAML 1.2 +--- +# http://www.sublimetext.com/docs/3/syntax.html +name: Rego +file_extensions: + - rego +scope: source.rego +contexts: + main: + - include: comment + - include: keyword + - include: operator + - include: head + - include: term + comment: + - match: (#).*$\n? + scope: comment.line.number-sign.rego + captures: + 1: punctuation.definition.comment.rego + call: + - match: '([a-zA-Z_][a-zA-Z0-9_]*)\(' + scope: meta.function-call.rego + captures: + 1: support.function.any-method.rego + constant: + - match: \b(?:true|false|null)\b + scope: constant.language.rego + head: + - match: "^([[:alpha:]_][[:alnum:]_]*)" + captures: + 1: entity.name.function.declaration + push: + - meta_scope: meta.function.rego + - match: '(=|{|\n)' + pop: true + - include: term + keyword: + - match: (^|\s+)(?:(default|not|package|import|as|with|else|some))\s+ + scope: keyword.other.rego + number: + - match: |- + (?x: # turn on extended mode + -? # an optional minus + (?: + 0 # a zero + | # ...or... + [1-9] # a 1-9 character + \d* # followed by zero or more digits + ) + (?: + (?: + \. # a period + \d+ # followed by one or more digits + )? + (?: + [eE] # an e character + [+-]? # followed by an option +/- + \d+ # followed by one or more digits + )? # make exponent optional + )? # make decimal portion optional + ) + scope: constant.numeric.rego + operator: + - match: \=|\!\=|>|<|<\=|>\=|\+|-|\*|%|/|\||&|:\= + scope: keyword.operator.comparison.rego + string: + - match: '"' + captures: + 0: punctuation.definition.string.begin.rego + push: + - meta_scope: string.quoted.double.rego + - match: '"' + captures: + 0: punctuation.definition.string.end.rego + pop: true + - match: |- + (?x: # turn on extended mode + \\ # a literal backslash + (?: # ...followed by... + ["\\/bfnrt] # one of these characters + | # ...or... + u # a u + [0-9a-fA-F]{4} # and four hex digits + ) + ) + scope: constant.character.escape.rego + - match: \\. + scope: invalid.illegal.unrecognized-string-escape.rego + term: + - include: constant + - include: string + - include: number + - include: call + - include: variable + variable: + - match: '\b[[:alpha:]_][[:alnum:]_]*\b' + scope: meta.identifier.rego diff --git a/third_party/opa/misc/syntax/textmate/Rego.tmLanguage b/third_party/opa/misc/syntax/textmate/Rego.tmLanguage new file mode 100644 index 000000000000..8b0771a5dfee --- /dev/null +++ b/third_party/opa/misc/syntax/textmate/Rego.tmLanguage @@ -0,0 +1,229 @@ + + + + + fileTypes + + Rego + + name + Rego + patterns + + + include + #comment + + + include + #keyword + + + include + #operator + + + include + #head + + + include + #term + + + repository + + call + + captures + + 1 + + name + support.function.any-method.rego + + + match + ([a-zA-Z_][a-zA-Z0-9_]*)\( + name + meta.function-call.rego + + comment + + captures + + 1 + + name + punctuation.definition.comment.rego + + + match + (#).*$\n? + name + comment.line.number-sign.rego + + constant + + match + \b(?:true|false|null)\b + name + constant.language.rego + + head + + begin + ^([[:alpha:]_][[:alnum:]_]*) + beginCaptures + + 1 + + name + entity.name.function.declaration + + + end + (=|{|\n) + name + meta.function.rego + patterns + + + include + #term + + + + keyword + + match + (^|\s+)(?:(default|not|package|import|as|with|else))\s+ + name + keyword.other.rego + + number + + match + (?x: # turn on extended mode + -? # an optional minus + (?: + 0 # a zero + | # ...or... + [1-9] # a 1-9 character + \d* # followed by zero or more digits + ) + (?: + (?: + \. # a period + \d+ # followed by one or more digits + )? + (?: + [eE] # an e character + [+-]? # followed by an option +/- + \d+ # followed by one or more digits + )? # make exponent optional + )? # make decimal portion optional + ) + name + constant.numeric.rego + + operator + + patterns + + + match + \=|\!\=|>|<|<\=|>\=|\+|-|\*|%|/|\||&|:\= + name + keyword.operator.comparison.rego + + + + string + + begin + " + beginCaptures + + 0 + + name + punctuation.definition.string.begin.rego + + + end + " + endCaptures + + 0 + + name + punctuation.definition.string.end.rego + + + name + string.quoted.double.rego + patterns + + + match + (?x: # turn on extended mode + \\ # a literal backslash + (?: # ...followed by... + ["\\/bfnrt] # one of these characters + | # ...or... + u # a u + [0-9a-fA-F]{4} # and four hex digits + ) + ) + name + constant.character.escape.rego + + + match + \\. + name + invalid.illegal.unrecognized-string-escape.rego + + + + term + + patterns + + + include + #constant + + + include + #string + + + include + #number + + + include + #call + + + include + #variable + + + + variable + + match + \b[[:alpha:]_][[:alnum:]_]*\b + name + meta.identifier.rego + + + scopeName + source.rego + uuid + 165D3571-322B-4C57-ABAD-9EB3922FB004 + + diff --git a/third_party/opa/netlify.toml b/third_party/opa/netlify.toml new file mode 100644 index 000000000000..333a02152e59 --- /dev/null +++ b/third_party/opa/netlify.toml @@ -0,0 +1,31 @@ +[build] +publish = "docs/build" +command = "make netlify" +edge_functions = "docs/functions" + +[build.environment] +NODE_VERSION = "22.15.0" +# some examples in v1/test/cases/testdata/v0/cryptox509* flag the netlify +# secret scan +SECRETS_SCAN_OMIT_PATHS = "v1/test/cases,v1/topdown/crypto_test.go" + +[[edge_functions]] +# this path should not be changed as various external sites depend on it for OPA +# badges. +path = "/badge/*" +function = "badge" + +# Redirect all path based versioned requests to their new archived sites. +# https://github.com/open-policy-agent/opa/issues/7037 +[[edge_functions]] +path = "/docs/*" +function = "version-redirect" + +# /data/versions.json is used by versioned OPA deployments to determine +# if what the latest release is, how outdated they are. +[[headers]] +for = "/data/versions.json" +[headers.values] +Access-Control-Allow-Origin = "*" +Access-Control-Allow-Methods = "GET, OPTIONS" +Access-Control-Allow-Headers = "Content-Type" diff --git a/third_party/opa/plugins/bundle/config.go b/third_party/opa/plugins/bundle/config.go new file mode 100644 index 000000000000..fe103e357551 --- /dev/null +++ b/third_party/opa/plugins/bundle/config.go @@ -0,0 +1,42 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + v1 "github.com/open-policy-agent/opa/v1/plugins/bundle" +) + +// ParseConfig validates the config and injects default values. This is +// for the legacy single bundle configuration. This will add the bundle +// to the `Bundles` map to provide compatibility with newer clients. +// Deprecated: Use `ParseBundlesConfig` with `bundles` OPA config option instead +func ParseConfig(config []byte, services []string) (*Config, error) { + return v1.ParseConfig(config, services) +} + +// ParseBundlesConfig validates the config and injects default values for +// the defined `bundles`. This expects a map of bundle names to resource +// configurations. +func ParseBundlesConfig(config []byte, services []string) (*Config, error) { + return v1.ParseBundlesConfig(config, services) +} + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the bundle config +func NewConfigBuilder() *ConfigBuilder { + return v1.NewConfigBuilder() +} + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder = v1.ConfigBuilder + +// Config represents the configuration of the plugin. +// The Config can define a single bundle source or a map of +// `Source` objects defining where/how to download bundles. The +// older single bundle configuration is deprecated and will be +// removed in the future in favor of the `Bundles` map. +type Config = v1.Config + +// Source is a configured bundle source to download bundles from +type Source = v1.Source diff --git a/third_party/opa/plugins/bundle/doc.go b/third_party/opa/plugins/bundle/doc.go new file mode 100644 index 000000000000..7ec7c9b3328f --- /dev/null +++ b/third_party/opa/plugins/bundle/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package bundle diff --git a/third_party/opa/plugins/bundle/errors.go b/third_party/opa/plugins/bundle/errors.go new file mode 100644 index 000000000000..965c704d3384 --- /dev/null +++ b/third_party/opa/plugins/bundle/errors.go @@ -0,0 +1,14 @@ +package bundle + +import ( + v1 "github.com/open-policy-agent/opa/v1/plugins/bundle" +) + +// Errors represents a list of errors that occurred during a bundle load enriched by the bundle name. +type Errors = v1.Errors + +type Error = v1.Error + +func NewBundleError(bundleName string, cause error) Error { + return v1.NewBundleError(bundleName, cause) +} diff --git a/third_party/opa/plugins/bundle/plugin.go b/third_party/opa/plugins/bundle/plugin.go new file mode 100644 index 000000000000..dffe3a322b54 --- /dev/null +++ b/third_party/opa/plugins/bundle/plugin.go @@ -0,0 +1,31 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package bundle implements bundle loading. +package bundle + +import ( + "github.com/open-policy-agent/opa/plugins" + v1 "github.com/open-policy-agent/opa/v1/plugins/bundle" +) + +// Loader defines the interface that the bundle plugin uses to control bundle +// loading via HTTP, disk, etc. +type Loader = v1.Loader + +// Plugin implements bundle activation. +type Plugin = v1.Plugin + +// New returns a new Plugin with the given config. +func New(parsedConfig *Config, manager *plugins.Manager) *Plugin { + return v1.New(parsedConfig, manager) +} + +// Name identifies the plugin on manager. +const Name = v1.Name + +// Lookup returns the bundle plugin registered with the manager. +func Lookup(manager *plugins.Manager) *Plugin { + return v1.Lookup(manager) +} diff --git a/third_party/opa/plugins/bundle/status.go b/third_party/opa/plugins/bundle/status.go new file mode 100644 index 000000000000..c49f7f4a5b4d --- /dev/null +++ b/third_party/opa/plugins/bundle/status.go @@ -0,0 +1,12 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + v1 "github.com/open-policy-agent/opa/v1/plugins/bundle" +) + +// Status represents the status of processing a bundle. +type Status = v1.Status diff --git a/third_party/opa/plugins/discovery/config.go b/third_party/opa/plugins/discovery/config.go new file mode 100644 index 000000000000..23034d18b7cd --- /dev/null +++ b/third_party/opa/plugins/discovery/config.go @@ -0,0 +1,25 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package discovery + +import ( + v1 "github.com/open-policy-agent/opa/v1/plugins/discovery" +) + +// Config represents the configuration for the discovery feature. +type Config = v1.Config + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder = v1.ConfigBuilder + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the discovery config +func NewConfigBuilder() *ConfigBuilder { + return v1.NewConfigBuilder() +} + +// ParseConfig returns a valid Config object with defaults injected. +func ParseConfig(bs []byte, services []string) (*Config, error) { + return v1.ParseConfig(bs, services) +} diff --git a/third_party/opa/plugins/discovery/discovery.go b/third_party/opa/plugins/discovery/discovery.go new file mode 100644 index 000000000000..696fed91ee80 --- /dev/null +++ b/third_party/opa/plugins/discovery/discovery.go @@ -0,0 +1,52 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package discovery implements configuration discovery. +package discovery + +import ( + "github.com/open-policy-agent/opa/plugins" + "github.com/open-policy-agent/opa/v1/hooks" + "github.com/open-policy-agent/opa/v1/metrics" + v1 "github.com/open-policy-agent/opa/v1/plugins/discovery" +) + +const ( + // Name is the discovery plugin name that will be registered with the plugin manager. + Name = v1.Name +) + +// Discovery implements configuration discovery for OPA. When discovery is +// started it will periodically download a configuration bundle and try to +// reconfigure the OPA. +type Discovery = v1.Discovery + +// Factories provides a set of factory functions to use for +// instantiating custom plugins. +func Factories(fs map[string]plugins.Factory) func(*Discovery) { + return v1.Factories(fs) +} + +// Metrics provides a metrics provider to pass to plugins. +func Metrics(m metrics.Metrics) func(*Discovery) { + return v1.Metrics(m) +} + +func Hooks(hs hooks.Hooks) func(*Discovery) { + return v1.Hooks(hs) +} + +func BootConfig(bootConfig map[string]any) func(*Discovery) { + return v1.BootConfig(bootConfig) +} + +// New returns a new discovery plugin. +func New(manager *plugins.Manager, opts ...func(*Discovery)) (*Discovery, error) { + return v1.New(manager, opts...) +} + +// Lookup returns the discovery plugin registered with the manager. +func Lookup(manager *plugins.Manager) *Discovery { + return v1.Lookup(manager) +} diff --git a/third_party/opa/plugins/discovery/doc.go b/third_party/opa/plugins/discovery/doc.go new file mode 100644 index 000000000000..cebfda6ed531 --- /dev/null +++ b/third_party/opa/plugins/discovery/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package discovery diff --git a/third_party/opa/plugins/doc.go b/third_party/opa/plugins/doc.go new file mode 100644 index 000000000000..77dade9b79a6 --- /dev/null +++ b/third_party/opa/plugins/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package plugins diff --git a/third_party/opa/plugins/logs/doc.go b/third_party/opa/plugins/logs/doc.go new file mode 100644 index 000000000000..947df360135e --- /dev/null +++ b/third_party/opa/plugins/logs/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package logs diff --git a/third_party/opa/plugins/logs/plugin.go b/third_party/opa/plugins/logs/plugin.go new file mode 100644 index 000000000000..206dc0c8cdc4 --- /dev/null +++ b/third_party/opa/plugins/logs/plugin.go @@ -0,0 +1,65 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package logs implements decision log buffering and uploading. +package logs + +import ( + "github.com/open-policy-agent/opa/plugins" + v1 "github.com/open-policy-agent/opa/v1/plugins/logs" +) + +// Logger defines the interface for decision logging plugins. +type Logger = v1.Logger + +// EventV1 represents a decision log event. +// WARNING: The AST() function for EventV1 must be kept in sync with +// the struct. Any changes here MUST be reflected in the AST() +// implementation below. +type EventV1 = v1.EventV1 + +// BundleInfoV1 describes a bundle associated with a decision log event. +type BundleInfoV1 = v1.BundleInfoV1 + +type RequestContext = v1.RequestContext + +type HTTPRequestContext = v1.HTTPRequestContext + +// ReportingConfig represents configuration for the plugin's reporting behaviour. +type ReportingConfig = v1.ReportingConfig + +type RequestContextConfig = v1.RequestContextConfig + +type HTTPRequestContextConfig = v1.HTTPRequestContextConfig + +// Config represents the plugin configuration. +type Config = v1.Config + +// Plugin implements decision log buffering and uploading. +type Plugin = v1.Plugin + +func ParseConfig(config []byte, services []string, pluginList []string) (*Config, error) { + return v1.ParseConfig(config, services, pluginList) +} + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder = v1.ConfigBuilder + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the plugin config. +func NewConfigBuilder() *ConfigBuilder { + return v1.NewConfigBuilder() +} + +// New returns a new Plugin with the given config. +func New(parsedConfig *Config, manager *plugins.Manager) *Plugin { + return v1.New(parsedConfig, manager) +} + +// Name identifies the plugin on manager. +const Name = v1.Name + +// Lookup returns the decision logs plugin registered with the manager. +func Lookup(manager *plugins.Manager) *Plugin { + return v1.Lookup(manager) +} diff --git a/third_party/opa/plugins/logs/status/doc.go b/third_party/opa/plugins/logs/status/doc.go new file mode 100644 index 000000000000..083f744abcc2 --- /dev/null +++ b/third_party/opa/plugins/logs/status/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package status diff --git a/third_party/opa/plugins/logs/status/status.go b/third_party/opa/plugins/logs/status/status.go new file mode 100644 index 000000000000..b23579c78011 --- /dev/null +++ b/third_party/opa/plugins/logs/status/status.go @@ -0,0 +1,14 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package status + +import ( + v1 "github.com/open-policy-agent/opa/v1/plugins/logs/status" +) + +// Status represents the status of processing a decision log. +type Status = v1.Status + +type HTTPError = v1.HTTPError diff --git a/third_party/opa/plugins/plugins.go b/third_party/opa/plugins/plugins.go new file mode 100644 index 000000000000..28c7e860f7f1 --- /dev/null +++ b/third_party/opa/plugins/plugins.go @@ -0,0 +1,268 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package plugins implements plugin management for the policy engine. +package plugins + +import ( + "net/http" + + "github.com/open-policy-agent/opa/internal/report" + "github.com/prometheus/client_golang/prometheus" + "go.opentelemetry.io/otel/sdk/trace" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/bundle" + "github.com/open-policy-agent/opa/hooks" + "github.com/open-policy-agent/opa/loader" + "github.com/open-policy-agent/opa/logging" + "github.com/open-policy-agent/opa/resolver/wasm" + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/topdown/print" + "github.com/open-policy-agent/opa/tracing" + v1 "github.com/open-policy-agent/opa/v1/plugins" +) + +// Factory defines the interface OPA uses to instantiate your plugin. +// +// When OPA processes it's configuration it looks for factories that +// have been registered by calling runtime.RegisterPlugin. Factories +// are registered to a name which is used to key into the +// configuration blob. If your plugin has not been configured, your +// factory will not be invoked. +// +// plugins: +// my_plugin1: +// some_key: foo +// # my_plugin2: +// # some_key2: bar +// +// If OPA was started with the configuration above and received two +// calls to runtime.RegisterPlugins (one with NAME "my_plugin1" and +// one with NAME "my_plugin2"), it would only invoke the factory for +// for my_plugin1. +// +// OPA instantiates and reconfigures plugins in two steps. First, OPA +// will call Validate to check the configuration. Assuming the +// configuration is valid, your factory should return a configuration +// value that can be used to construct your plugin. Second, OPA will +// call New to instantiate your plugin providing the configuration +// value returned from the Validate call. +// +// Validate receives a slice of bytes representing plugin +// configuration and returns a configuration value that can be used to +// instantiate your plugin. The manager is provided to give access to +// the OPA's compiler, storage layer, and global configuration. Your +// Validate function will typically: +// +// 1. Deserialize the raw config bytes +// 2. Validate the deserialized config for semantic errors +// 3. Inject default values +// 4. Return a deserialized/parsed config +// +// New receives a valid configuration for your plugin and returns a +// plugin object. Your New function will typically: +// +// 1. Cast the config value to it's own type +// 2. Instantiate a plugin object +// 3. Return the plugin object +// 4. Update status via `plugins.Manager#UpdatePluginStatus` +// +// After a plugin has been created subsequent status updates can be +// send anytime the plugin enters a ready or error state. +type Factory = v1.Factory + +// Plugin defines the interface OPA uses to manage your plugin. +// +// When OPA starts it will start all of the plugins it was configured +// to instantiate. Each time a new plugin is configured (via +// discovery), OPA will start it. You can use the Start call to spawn +// additional goroutines or perform initialization tasks. +// +// Currently OPA will not call Stop on plugins. +// +// When OPA receives new configuration for your plugin via discovery +// it will first Validate the configuration using your factory and +// then call Reconfigure. +type Plugin = v1.Plugin + +// Triggerable defines the interface plugins use for manual plugin triggers. +type Triggerable = v1.Triggerable + +// State defines the state that a Plugin instance is currently +// in with pre-defined states. +type State = v1.State + +const ( + // StateNotReady indicates that the Plugin is not in an error state, but isn't + // ready for normal operation yet. This should only happen at + // initialization time. + StateNotReady = v1.StateNotReady + + // StateOK signifies that the Plugin is operating normally. + StateOK = v1.StateOK + + // StateErr indicates that the Plugin is in an error state and should not + // be considered as functional. + StateErr = v1.StateErr + + // StateWarn indicates the Plugin is operating, but in a potentially dangerous or + // degraded state. It may be used to indicate manual remediation is needed, or to + // alert admins of some other noteworthy state. + StateWarn = v1.StateWarn +) + +// TriggerMode defines the trigger mode utilized by a Plugin for bundle download, +// log upload etc. +type TriggerMode = v1.TriggerMode + +const ( + // TriggerPeriodic represents periodic polling mechanism + TriggerPeriodic = v1.TriggerPeriodic + + // TriggerManual represents manual triggering mechanism + TriggerManual = v1.TriggerManual + + // DefaultTriggerMode represents default trigger mechanism + DefaultTriggerMode = v1.DefaultTriggerMode +) + +// Status has a Plugin's current status plus an optional Message. +type Status = v1.Status + +// StatusListener defines a handler to register for status updates. +type StatusListener v1.StatusListener + +// Manager implements lifecycle management of plugins and gives plugins access +// to engine-wide components like storage. +type Manager = v1.Manager + +// SetCompilerOnContext puts the compiler into the storage context. Calling this +// function before committing updated policies to storage allows the manager to +// skip parsing and compiling of modules. Instead, the manager will use the +// compiler that was stored on the context. +func SetCompilerOnContext(context *storage.Context, compiler *ast.Compiler) { + v1.SetCompilerOnContext(context, compiler) +} + +// GetCompilerOnContext gets the compiler cached on the storage context. +func GetCompilerOnContext(context *storage.Context) *ast.Compiler { + return v1.GetCompilerOnContext(context) +} + +// SetWasmResolversOnContext puts a set of Wasm Resolvers into the storage +// context. Calling this function before committing updated wasm modules to +// storage allows the manager to skip initializing modules before using them. +// Instead, the manager will use the compiler that was stored on the context. +func SetWasmResolversOnContext(context *storage.Context, rs []*wasm.Resolver) { + v1.SetWasmResolversOnContext(context, rs) +} + +// ValidateAndInjectDefaultsForTriggerMode validates the trigger mode and injects default values +func ValidateAndInjectDefaultsForTriggerMode(a, b *TriggerMode) (*TriggerMode, error) { + return v1.ValidateAndInjectDefaultsForTriggerMode(a, b) +} + +// Info sets the runtime information on the manager. The runtime information is +// propagated to opa.runtime() built-in function calls. +func Info(term *ast.Term) func(*Manager) { + return v1.Info(term) +} + +// InitBundles provides the initial set of bundles to load. +func InitBundles(b map[string]*bundle.Bundle) func(*Manager) { + return v1.InitBundles(b) +} + +// InitFiles provides the initial set of other data/policy files to load. +func InitFiles(f loader.Result) func(*Manager) { + return v1.InitFiles(f) +} + +// MaxErrors sets the error limit for the manager's shared compiler. +func MaxErrors(n int) func(*Manager) { + return v1.MaxErrors(n) +} + +// GracefulShutdownPeriod passes the configured graceful shutdown period to plugins +func GracefulShutdownPeriod(gracefulShutdownPeriod int) func(*Manager) { + return v1.GracefulShutdownPeriod(gracefulShutdownPeriod) +} + +// Logger configures the passed logger on the plugin manager (useful to +// configure default fields) +func Logger(logger logging.Logger) func(*Manager) { + return v1.Logger(logger) +} + +// ConsoleLogger sets the passed logger to be used by plugins that are +// configured with console logging enabled. +func ConsoleLogger(logger logging.Logger) func(*Manager) { + return v1.ConsoleLogger(logger) +} + +func EnablePrintStatements(yes bool) func(*Manager) { + return v1.EnablePrintStatements(yes) +} + +func PrintHook(h print.Hook) func(*Manager) { + return v1.PrintHook(h) +} + +func WithRouter(r *http.ServeMux) func(*Manager) { + return v1.WithRouter(r) +} + +// WithPrometheusRegister sets the passed prometheus.Registerer to be used by plugins +func WithPrometheusRegister(prometheusRegister prometheus.Registerer) func(*Manager) { + return v1.WithPrometheusRegister(prometheusRegister) +} + +// WithTracerProvider sets the passed *trace.TracerProvider to be used by plugins +func WithTracerProvider(tracerProvider *trace.TracerProvider) func(*Manager) { + return v1.WithTracerProvider(tracerProvider) +} + +// WithDistributedTracingOpts sets the options to be used by distributed tracing. +func WithDistributedTracingOpts(tr tracing.Options) func(*Manager) { + return v1.WithDistributedTracingOpts(tr) +} + +// WithHooks allows passing hooks to the plugin manager. +func WithHooks(hs hooks.Hooks) func(*Manager) { + return v1.WithHooks(hs) +} + +// WithParserOptions sets the parser options to be used by the plugin manager. +func WithParserOptions(opts ast.ParserOptions) func(*Manager) { + return v1.WithParserOptions(opts) +} + +// WithEnableTelemetry controls whether OPA will send telemetry reports to an external service. +func WithEnableTelemetry(enableTelemetry bool) func(*Manager) { + return v1.WithEnableTelemetry(enableTelemetry) +} + +// WithTelemetryGatherers allows registration of telemetry gatherers which enable injection of additional data in the +// telemetry report +func WithTelemetryGatherers(gs map[string]report.Gatherer) func(*Manager) { + return v1.WithTelemetryGatherers(gs) +} + +// New creates a new Manager using config. +func New(raw []byte, id string, store storage.Store, opts ...func(*Manager)) (*Manager, error) { + options := make([]func(*Manager), 0, len(opts)+1) + options = append(options, opts...) + + // Add option to apply default Rego version if not set. Must be last in list of options. + options = append(options, func(m *Manager) { + if m.ParserOptions().RegoVersion == ast.RegoUndefined { + cpy := m.ParserOptions() + cpy.RegoVersion = ast.DefaultRegoVersion + WithParserOptions(cpy)(m) + } + }) + + return v1.New(raw, id, store, options...) +} diff --git a/third_party/opa/plugins/plugins_test.go b/third_party/opa/plugins/plugins_test.go new file mode 100644 index 000000000000..c453549de50a --- /dev/null +++ b/third_party/opa/plugins/plugins_test.go @@ -0,0 +1,47 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package plugins + +import ( + "testing" + + "github.com/open-policy-agent/opa/storage/inmem" + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestNew_DefaultRegoVersion(t *testing.T) { + popts := ast.ParserOptions{ + Capabilities: &ast.Capabilities{ + Features: []string{ + "my_custom_feature", + }, + }, + ProcessAnnotation: true, + AllFutureKeywords: true, + FutureKeywords: []string{"foo", "bar"}, + } + m, err := New([]byte(`{"plugins": {"someplugin": {}}}`), "test", inmem.New(), + WithParserOptions(popts)) + if err != nil { + t.Fatal(err) + } + + if exp, act := ast.RegoV0, m.ParserOptions().RegoVersion; exp != act { + t.Fatalf("Expected default Rego version to be %v but got %v", exp, act) + } + + // Check a couple of other options to make sure they haven't changed + if exp, act := popts.ProcessAnnotation, m.ParserOptions().ProcessAnnotation; exp != act { + t.Fatalf("Expected ProcessAnnotation to be %v but got %v", exp, act) + } + + if exp, act := popts.AllFutureKeywords, m.ParserOptions().AllFutureKeywords; exp != act { + t.Fatalf("Expected AllFutureKeywords to be %v but got %v", exp, act) + } + + if exp, act := popts.Capabilities, m.ParserOptions().Capabilities; exp != act { + t.Fatalf("Expected Capabilities to be %v but got %v", exp, act) + } +} diff --git a/third_party/opa/plugins/rest/auth.go b/third_party/opa/plugins/rest/auth.go new file mode 100644 index 000000000000..adf467bf5f20 --- /dev/null +++ b/third_party/opa/plugins/rest/auth.go @@ -0,0 +1,22 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package rest + +import ( + "crypto/tls" + "net/http" + + v1 "github.com/open-policy-agent/opa/v1/plugins/rest" +) + +// DefaultTLSConfig defines standard TLS configurations based on the Config +func DefaultTLSConfig(c Config) (*tls.Config, error) { + return v1.DefaultTLSConfig(c) +} + +// DefaultRoundTripperClient is a reasonable set of defaults for HTTP auth plugins +func DefaultRoundTripperClient(t *tls.Config, timeout int64) *http.Client { + return v1.DefaultRoundTripperClient(t, timeout) +} diff --git a/third_party/opa/plugins/rest/doc.go b/third_party/opa/plugins/rest/doc.go new file mode 100644 index 000000000000..7f953965d923 --- /dev/null +++ b/third_party/opa/plugins/rest/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package rest diff --git a/third_party/opa/plugins/rest/gcp.go b/third_party/opa/plugins/rest/gcp.go new file mode 100644 index 000000000000..d728eda76ef1 --- /dev/null +++ b/third_party/opa/plugins/rest/gcp.go @@ -0,0 +1,12 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package rest + +import ( + v1 "github.com/open-policy-agent/opa/v1/plugins/rest" +) + +// AccessToken holds a GCP access token. +type AccessToken = v1.AccessToken diff --git a/third_party/opa/plugins/rest/rest.go b/third_party/opa/plugins/rest/rest.go new file mode 100644 index 000000000000..b9ca3ae5cd30 --- /dev/null +++ b/third_party/opa/plugins/rest/rest.go @@ -0,0 +1,54 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package rest implements a REST client for communicating with remote services. +package rest + +import ( + "github.com/open-policy-agent/opa/logging" + "github.com/open-policy-agent/opa/v1/keys" + v1 "github.com/open-policy-agent/opa/v1/plugins/rest" + "github.com/open-policy-agent/opa/v1/tracing" +) + +// An HTTPAuthPlugin represents a mechanism to construct and configure HTTP authentication for a REST service +type HTTPAuthPlugin = v1.HTTPAuthPlugin + +// Config represents configuration for a REST client. +type Config = v1.Config + +// An AuthPluginLookupFunc can lookup auth plugins by their name. +type AuthPluginLookupFunc = v1.AuthPluginLookupFunc + +// Client implements an HTTP/REST client for communicating with remote +// services. +type Client = v1.Client + +// Name returns an option that overrides the service name on the client. +func Name(s string) func(*Client) { + return v1.Name(s) +} + +// AuthPluginLookup assigns a function to lookup an HTTPAuthPlugin to a new Client. +// It's intended to be used when creating a Client using New(). Usually this is passed +// the plugins.AuthPlugin func, which retrieves a registered HTTPAuthPlugin from the +// plugin manager. +func AuthPluginLookup(l AuthPluginLookupFunc) func(*Client) { + return v1.AuthPluginLookup(l) +} + +// Logger assigns a logger to the client +func Logger(l logging.Logger) func(*Client) { + return v1.Logger(l) +} + +// DistributedTracingOpts sets the options to be used by distributed tracing. +func DistributedTracingOpts(tr tracing.Options) func(*Client) { + return v1.DistributedTracingOpts(tr) +} + +// New returns a new Client for config. +func New(config []byte, keys map[string]*keys.Config, opts ...func(*Client)) (Client, error) { + return v1.New(config, keys, opts...) +} diff --git a/third_party/opa/plugins/server/decoding/config.go b/third_party/opa/plugins/server/decoding/config.go new file mode 100644 index 000000000000..3a28a49f5805 --- /dev/null +++ b/third_party/opa/plugins/server/decoding/config.go @@ -0,0 +1,35 @@ +// Package decoding implements the configuration side of the upgraded gzip +// decompression framework. The original work only enabled gzip decoding for +// a few endpoints-- here we enable if for all of OPA. Additionally, we provide +// some new defensive configuration options: max_length, and gzip.max_length. +// These allow rejecting requests that indicate their contents are larger than +// the size limits. +// +// The request handling pipeline now looks roughly like this: +// +// Request -> MaxBytesReader(Config.MaxLength) -> ir.CopyN(dest, req, Gzip.MaxLength) +// +// The intent behind this design is to improve how OPA handles large and/or +// malicious requests, compressed or otherwise. The benefit of being a little +// more strict in what we allow is that we can now use "riskier", but +// dramatically more performant techniques, like preallocating content buffers +// for gzipped data. This also should help OPAs in limited memory situations. +package decoding + +import ( + v1 "github.com/open-policy-agent/opa/v1/plugins/server/decoding" +) + +// Config represents the configuration for the Server.Decoding settings +type Config = v1.Config + +// Gzip represents the configuration for the Server.Decoding.Gzip settings +type Gzip = v1.Gzip + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder = v1.ConfigBuilder + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the server config +func NewConfigBuilder() *ConfigBuilder { + return v1.NewConfigBuilder() +} diff --git a/third_party/opa/plugins/server/decoding/doc.go b/third_party/opa/plugins/server/decoding/doc.go new file mode 100644 index 000000000000..cbba4144e5ee --- /dev/null +++ b/third_party/opa/plugins/server/decoding/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package decoding diff --git a/third_party/opa/plugins/server/doc.go b/third_party/opa/plugins/server/doc.go new file mode 100644 index 000000000000..93588bae7112 --- /dev/null +++ b/third_party/opa/plugins/server/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package server diff --git a/third_party/opa/plugins/server/encoding/config.go b/third_party/opa/plugins/server/encoding/config.go new file mode 100644 index 000000000000..68faf0aac96f --- /dev/null +++ b/third_party/opa/plugins/server/encoding/config.go @@ -0,0 +1,19 @@ +package encoding + +import ( + v1 "github.com/open-policy-agent/opa/v1/plugins/server/encoding" +) + +// Config represents the configuration for the Server.Encoding settings +type Config = v1.Config + +// Gzip represents the configuration for the Server.Encoding.Gzip settings +type Gzip = v1.Gzip + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder = v1.ConfigBuilder + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the server config +func NewConfigBuilder() *ConfigBuilder { + return v1.NewConfigBuilder() +} diff --git a/third_party/opa/plugins/server/encoding/doc.go b/third_party/opa/plugins/server/encoding/doc.go new file mode 100644 index 000000000000..bf2818e5247f --- /dev/null +++ b/third_party/opa/plugins/server/encoding/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package encoding diff --git a/third_party/opa/plugins/server/metrics/config.go b/third_party/opa/plugins/server/metrics/config.go new file mode 100644 index 000000000000..f77ac7f34880 --- /dev/null +++ b/third_party/opa/plugins/server/metrics/config.go @@ -0,0 +1,22 @@ +package metrics + +import ( + v1 "github.com/open-policy-agent/opa/v1/plugins/server/metrics" +) + +// Config represents the configuration for the Server.Metrics settings +type Config = v1.Config + +// Prom represents the configuration for the Server.Metrics.Prom settings +type Prom = v1.Prom + +// HTTPRequestDurationSeconds represents the configuration for the Server.Metrics.Prom.HTTPRequestDurationSeconds settings +type HTTPRequestDurationSeconds = v1.HTTPRequestDurationSeconds + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder = v1.ConfigBuilder + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the server config +func NewConfigBuilder() *ConfigBuilder { + return v1.NewConfigBuilder() +} diff --git a/third_party/opa/plugins/server/metrics/doc.go b/third_party/opa/plugins/server/metrics/doc.go new file mode 100644 index 000000000000..6a306991e799 --- /dev/null +++ b/third_party/opa/plugins/server/metrics/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package metrics diff --git a/third_party/opa/plugins/status/doc.go b/third_party/opa/plugins/status/doc.go new file mode 100644 index 000000000000..083f744abcc2 --- /dev/null +++ b/third_party/opa/plugins/status/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package status diff --git a/third_party/opa/plugins/status/metrics.go b/third_party/opa/plugins/status/metrics.go new file mode 100644 index 000000000000..1b8763ccdf47 --- /dev/null +++ b/third_party/opa/plugins/status/metrics.go @@ -0,0 +1,9 @@ +package status + +import ( + v1 "github.com/open-policy-agent/opa/v1/plugins/status" +) + +type PrometheusConfig = v1.PrometheusConfig + +type Collectors = v1.Collectors diff --git a/third_party/opa/plugins/status/plugin.go b/third_party/opa/plugins/status/plugin.go new file mode 100644 index 000000000000..54d9927cc7df --- /dev/null +++ b/third_party/opa/plugins/status/plugin.go @@ -0,0 +1,53 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package status implements status reporting. +package status + +import ( + "github.com/open-policy-agent/opa/plugins" + v1 "github.com/open-policy-agent/opa/v1/plugins/status" +) + +// Logger defines the interface for status plugins. +type Logger = v1.Logger + +// UpdateRequestV1 represents the status update message that OPA sends to +// remote HTTP endpoints. +type UpdateRequestV1 = v1.UpdateRequestV1 + +// Plugin implements status reporting. Updates can be triggered by the caller. +type Plugin = v1.Plugin + +// Config contains configuration for the plugin. +type Config = v1.Config + +// BundleLoadDurationNanoseconds represents the configuration for the status.prometheus_config.bundle_loading_duration_ns settings +type BundleLoadDurationNanoseconds = v1.BundleLoadDurationNanoseconds + +// ParseConfig validates the config and injects default values. +func ParseConfig(config []byte, services []string, pluginsList []string) (*Config, error) { + return v1.ParseConfig(config, services, pluginsList) +} + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder = v1.ConfigBuilder + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the plugin config. +func NewConfigBuilder() *ConfigBuilder { + return v1.NewConfigBuilder() +} + +// New returns a new Plugin with the given config. +func New(parsedConfig *Config, manager *plugins.Manager) *Plugin { + return v1.New(parsedConfig, manager) +} + +// Name identifies the plugin on manager. +const Name = v1.Name + +// Lookup returns the status plugin registered with the manager. +func Lookup(manager *plugins.Manager) *Plugin { + return v1.Lookup(manager) +} diff --git a/third_party/opa/profiler/doc.go b/third_party/opa/profiler/doc.go new file mode 100644 index 000000000000..c04a317028b6 --- /dev/null +++ b/third_party/opa/profiler/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package profiler diff --git a/third_party/opa/profiler/profiler.go b/third_party/opa/profiler/profiler.go new file mode 100644 index 000000000000..65085cd9b68b --- /dev/null +++ b/third_party/opa/profiler/profiler.go @@ -0,0 +1,35 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package profiler computes and reports on the time spent on expressions. +package profiler + +import ( + v1 "github.com/open-policy-agent/opa/v1/profiler" +) + +// Profiler computes and reports on the time spent on expressions. +type Profiler = v1.Profiler + +// New returns a new Profiler object. +func New() *Profiler { + return v1.New() +} + +// ExprStats represents the result of profiling an expression. +type ExprStats = v1.ExprStats + +// ExprStatsAggregated represents the result of profiling an expression +// by aggregating `n` profiles. +type ExprStatsAggregated = v1.ExprStatsAggregated + +func AggregateProfiles(profiles ...[]ExprStats) []ExprStatsAggregated { + return v1.AggregateProfiles(profiles...) +} + +// Report represents the profiler report for a set of files. +type Report = v1.Report + +// FileReport represents a profiler report for a single file. +type FileReport = v1.FileReport diff --git a/third_party/opa/proposals/attic/REGO_V2_PROPOSAL.md b/third_party/opa/proposals/attic/REGO_V2_PROPOSAL.md new file mode 100644 index 000000000000..3d2227edbfea --- /dev/null +++ b/third_party/opa/proposals/attic/REGO_V2_PROPOSAL.md @@ -0,0 +1,756 @@ +Rego v2 - Proposal + +Authors: Tristan Swadell, Tim Hinrichs, Torin Sandall +Last-Modified: 2018-02-22 + +# Goals + +This document serves to facilitate collaborative development of the design of +a general-purpose policy language. _General-purpose_ means that the language +should be applicable to any domain, layer of the stack, or enforcement point. +Different implementations of the language runtime may be better suited to +different applications. + +# Concepts + +The user-experience for policy enforcement depends heavily on the policy +language and what concepts the user must understand to use that language. The +proposed concepts thus far are: + +* **Rule** - An identifier with optional parameters that conditionally produces + a decision. + * May refer to other rules, constants, and functions. + * Declared within modules. + * May be overloaded. + +* **Context** - Data provided at evaluation time or through calls to external + data-sources. + * Rules may declare a signature with the expected context. + * External datasources may be a file, database, or API. + +The proposed language has the following properties: + +* **Side-effect free** and non-Turing complete. +* Rules, functions, and constants are declared within modules. +* Rules are evaluated by name or by module. +* Decisions are qualified by the module name where they are declared. +* Conflicts must be decided by a decision resolver or by the actor calling the + engine. + +# Rules + +The rule declaration provides a named entry point for evaluation and +composition. The signature of the rule declares the context to be provided +upon evaluation, and the body is a collection of conditions and decisions. + +Please note that the definition of `expr` and `literal` are taken from +the [Common Expression Language](http://github.com/google/cel-spec) (CEL): + +``` +rule_decl + := 'rule' id assign_expr (if_expr else_expr*)? + | 'rule' function_signature '{' statement+ '}' + ; +const_decl + := decorator? id assign_expr + ; +assign_expr + := '=' (expr | comprehension_expr) + ; +function_decl + := decorator? 'function' function_signature ('{' statement+ '}')? + ; +function_signature + : id '(' arg_list? ')' + ; +arg_list + := id (',' id)* + ; +decorator: + : '@' id + ; +statement + := condition_expr + | const_decl + | return_expr + | comprehension_expr + ; +condition_expr + := if_expr '{' statement+ '}' + ; +if_expr + := 'if' expr + ; +else_expr + := 'else' (expr | if_expr) + ; +return_expr + : 'return' (expr | comprehension_expr) + ; +comprehension_expr + := iter_expr + | '{' (expr '|')? iter_expr '}' + | '[' (expr '|')? iter_expr ']' + ; +iter_expr + := 'for' id (',' id)? 'in' expr if_expr? statement? + | 'for' id (',' id)? 'in' expr if_expr? ('{' statement+ '}')? + ; +``` + +Rules represents a decision and are declared distinctly from constants and +functions. Rules may be constant-like or function-like. Constant-like rules +yield decisions derived from module or system provided context. Function-like +rules declare a function signature that indicates the context required from the +caller. The body of the rule may contain any number of conditions, decisions, +and local declarations. + +``` +rule userSalary(resource, user) { + match_result = resource.match('users/{target_user}/salary'); + return user == match_result.group.target_user; +} +``` + +Rules may be imported and leveraged within rule decisions. Note the inclusion +of the rule within a package and how this affects function identifier +resolution. + +``` +package acme; +import acme.hr; + +rule readUserSalary(request, resource, user) { + match_result = resource.match('users/{target_user}/salary'); + if (match_result.matches() && request.method == 'get') { + target_user = match_result.groups.target_user + return (hr.isManager(user, target_user) + || hr.isAdmin(user) + || user == target_user) + } + return false; +} + +// outputs -> {'rule': 'acme.readUserSalary', 'result': bool} +``` + +Rules may have multiple return statements in order to enforce allow / deny +semantics (in the case of binary rules), or simply different variations on +an affirmative rule decision, such as whether to return a list of honey-pot +servers versus a valid list of servers. + +``` +rule readUserSalary(request, resource, user) { + match_result = resource.match('users/{target_user}/salary') + if (match_result.matches() && request.method == 'get') { + target_user = match_result.groups.target_user + // Deny requests outside of business hours. + if (request.time.hour() < 9 || request.time.hour() > 17) { + return false + } + return (hr.isManager(user, target_user) + || hr.isAdmin(user) + || user == target_user) + } + return false +} +``` + +Rules may be composed from other rules and functions. The example below +groups reading and listing salaries into a single decision. + +``` +// Rules may be composed. +rule viewSalary(request, resource, user) { + return queryDepartmentSalaries(request, resource, user) + || readUserSalary(request, resource, user); +} + +rule queryDepartmentSalaries(request, resource, user) { + match_result = resource.match('departments/{department}/salaries') + if match_result.matches() && request.method == 'list' { + department = match_result.groups.department; + return !('user' in request.params.fields) + && hr.department(department).manager == user; + } + return false; +} + +// Evaluating of the 'acme' module produces: +// [{'rule': 'acme.viewSalary', 'result': bool}, +// {'rule': 'acme.viewUserSalary', 'result': bool}, +// {'rule': 'acme.queryDepartmentSalaries', 'result': bool}] +// +// Evaluation of just acme.viewSalary would yield only the first decision. +``` + +Rules may also be written in a constant style where overloading occurs on +the name. Developers may choose to write logically ORed statements together +or provide overloads as a means of augmenting the decision set that can be +attached to a rule. + +``` +// Constant-like rule which supports named evaluation. +// +// The authorized value is optionally assigned to the identifier 'allow' +// depending on the request.auth condition. +// +// Note, if the condition evaluates false, authenticated is not assigned and +// not included in the decision set. +rule authorized = allow if request.auth != null + +// Overloads the authorized grant to also permit access to public resources. +// +// When the request is both authenticated and against a public resource, the +// decision set will include two authorized results. +// +// A conflict resolution strategy of 'anyAllow' would ensure a single result +// for the authorized decision. The default conflict resolution behavior could +// be to aggregate overloaded decisions if the type supports aggregation: +// e.g. sets, lists, boolean +// Conflict resolution requires further discussion. +rule authorized = allow if resource.name.contains('/public/') + +// Rules may also be as a conditional assignment with if-else conditions rather +// than as overloads if the developer would like to control the overload +// behavior. +rule rateLimit = 100 if 'admin' in request.auth.claims + else 50 if 'owner' in request.auth.claims + else 10 +``` + +## Conditions + +Conditions are [Common Expression Language](https://github.com/google/cel-spec) +(CEL) expressions and evaluate to a boolean outcome. A condition may be used +to select applicable rules or to make an effect or trigger conditional. +Conditions may also be used to filter list and map entries within a `for-in` +expression. + +Declarations within a condition are within its block scope and may be shadowed +by declarations in nested conditions. Once a declaration has been assigned, it +cannot be reassigned. + +## Decisions + +A rule represents a conditional decision which may depend on other rule +decisions. The types of overloaded rule declarations *should* agree. In the +case of multiple `rule`s being evaluated where the output types do not agree, +the decision is dynamically typed. Rule decisions are _maybe_ values, in the +sense that the result may be a valued type or undefined. This is a crucial +feature in support of rule overloading and evaluation with partial state. + +The decision semantics when multiple `rule`s are evaluated depends on the +conflict resolution algorithm declared within the policy or on the caller. + +Note: conflict resolution across `rule`s are as yet undefined, but will be +addressed in a future update to the proposal. + +### Triggers + +Triggers are not a separate concept, but rather a core consideration of how +the `rule` declarations are designed. Since each rule emits at most one +decision, this makes it feasible to write rules which go beyond request and +config validation, and apply to the conditional execution of additional +compute coordinated by the policy engine. + +Conceptually, triggers align with the concepts of obligations and advice +introduced within [XACML](xacml.org). The difference between whether something +is an obligation or advice typically boils down to whether the action to +perform is synchronous or asynchronous. Synchronous obligations may include +preconditions, whereas async obligations would be considered promises. Advice, +on the other hand, should always be considered asynchronous and best-effort. + +The following is an example of a rule that acts as a trigger to log request +behavior and check quota. + +``` +// This rule conditionally produces metadata indicating that a quota +// check should be performed. The evaluation engine will support the +// registration of decision handlers whose behavior will affect the +// overall request handling. +rule hasQuota(request) { + if authenticated == allow { + // Should return a payload flagged as an obligation, that can be + // processed by a decision handler registered with the evaluator + // for the .hasQuota decision. + return quota.check('perMethodPerUser', + [{name: 'method', value: request.method}, + {name: 'user', value: request.auth.principal}]) + } +} + +// The payload for a log statement could be as simple as a string. +rule log = logger.log(request.auth.principal + + " denied request on " + + resource.name) + if authenticated != allow + +// Module-based evaluation of these rules would output the following +// decisions: +// [{'rule': 'hasQuota', +// 'result': { +// 'type': 'obligation', +// 'handler': 'quota.check', +// 'metadata' : { 'metric': 'perMethodPerUser', args:[ ... ]}}}, +// {'rule': 'log', +// 'result': { +// 'type': 'promise', +// 'handler': 'logger.log', +// 'metadata': {'message': ...}}}] +``` + +# Tests + +Being able to verify the correctness policy-related logic is of paramount +importance. As is the ability to pose ad hoc queries with partial state. To this +end we include `@test` as supported decorator and introduce the `with-as` clause +to assist with partial state bindings required for both adhoc queries and for +function mocking. + +Note: the following is under review and not yet reflected in the grammar. + +``` +rule user_owned_action(auth, resource) { + result = resource.matches('documents/{owner}/**') + return (result.owner == auth.principal + || resource.owner in user_groups(auth.principal)); +} + +@extern function user_groups(user); + +function mock_user_groups(user) { … } + +@test function group_check() { + with user_groups as mock_get_group_users { + assertTrue(user_owned_action({principal: 'me'}, 'documents/my-group')) + assertFalse(user_owned_action({principal: 'me'}, 'documents/their-group')) + } +} +``` + +# Context + +Context is either supplied through arguments provided to the function, through +constant declarations within the module, or through external functions invoked +at evaluation time. + +The following are all examples of how context may be provided: + +``` +package acme; +syntax = 'rego.v2'; + +// Functions bound at evaluation time. +@extern function resource(); +@extern function query(document_name); + +// Constant declaration based on external function calls. +ctx = {resource: resource().name, + resource_owner: resource().owner}; + +// Allow user-owned reads, with user and request provided as a rule argument. +rule allow_user_reads(user, request) { + if (request.method in ['get', 'list']) { + return (request.auth.claims.email == user + || ctx.resource_owner == user + || query("/documents/" + ctx.resource).created_by == user); + } + return false; +} +``` + +It will likely be common practice to provide @extern functions within their own +module like so: + +``` +package acme.db; +syntax = 'rego.v2'; +@extern function resource(); +@extern function query(document_name); +``` + +``` +package acme; +import acme.db; +syntax = 'rego.v2'; + +// Constant declaration based on external function calls. +ctx = {'resource': db.resource().name, + 'resource_owner': db.resource().owner}; + +// Allow user-owned reads, with user and request provided as a rule argument. +rule allow_user_reads(user, request) { + if (request.method in ['get', 'list']) { + return (request.auth.claims.email == user + || ctx.resource_owner == user + || db.query("/documents/" + ctx.resource).created_by == user); + } + return false; +} +``` + +The example below shows how `acme.db` provides a library of context and +functions for use with rules. The `@extern` decorator is equivalent to a +forward declaration, both to serve as documentation for what exists, but +also to be consumed during type-checking to ensure the system context and +function hooks are being used correctly within rules. + +``` +package acme.db; +syntax = 'rego.v2'; +@extern request; +@extern function resource(); +@extern function query(document_name); + +input = { + 'method': request.method, + 'user': request.auth.claims.email, + 'resource': resource() +} + +permission = { + 'read': input.method in ['get', 'list']; + 'write': input.method in ['create', 'update', 'delete']; +} + +function resourceMatch(pattern) { + return resource().name.match(pattern).groups; +} +``` + +``` +package acme; +import acme.db; +syntax = 'rego.v2'; + +// Allow user-owned reads, with context information provided by functions and +// constants provided by acme.db in the form of module or extern declarations. +rule authorized() { + target_user = db.resourceMatch('users/{target_user}/salary').target_user; + return db.permission.read + && (db.input.user == target_user + || db.input.resource.owner == target_user + || db.query("/documents/" + input.resource.name).created_by == target_user); +} +``` + +# Language + +Rego v2 is a series of extensions to the Common Expression Language (CEL) with +the aim of clarifying the flow of execution from Rego v1 while preserving its +features and incorporating the non-Turing complete, partial evaluation +semantics of CEL. + +## Grammar + +``` +module + := package? import* decls + ; +package + := 'package' qualified_id ';' + ; +import + := 'import' qualified_id ('as' id)? ';' + ; +decls + := (rule_decl | function_decl | const_decl)* + ; +rule_decl + := 'rule' id assign_expr (if_expr else_expr*)? + | 'rule' function_signature '{' statement+ '}' + ; +const_decl + := id assign_expr + | decorator id + ; +assign_expr + := '=' (expr | comprehension_expr) + ; +function_decl + := decorator? 'function' function_signature ('{' statement+ '}')? + ; +function_signature + : id '(' arg_list? ')' + ; +arg_list + := id (',' id)* + ; +decorator: + : '@' id + ; +statement + := condition_expr + | const_decl + | return_expr + | comprehension_expr + | with_block + ; +condition_expr + := if_expr '{' statement+ '}' + ; +if_expr + := 'if' expr + ; +else_expr + := 'else' (expr | if_expr) + ; +return_expr + : 'return' (expr | comprehension_expr) + ; +comprehension_expr + := iter_expr + | '{' (expr '|')? iter_expr '}' + | '[' (expr '|')? iter_expr ']' + ; +iter_expr + := 'for' id (',' id)? 'in' expr if_expr? statement? + | 'for' id (',' id)? 'in' expr if_expr? ('{' statement+ '}')? + ; +with_block + : 'with' qualified_id 'as' id '{' statement+ '}' + ; +expr + := or_expr ('?' or_expr ':' expr)? + ; +or_expr + := and_expr ('||' and_expr)* + ; +and_expr + := relation_expr ('&&' relation_expr)* + ; +relation_expr + := calc_expr + | relation_expr ('<'|'<='|'>='|'>'|'=='|'!='|'in') relation_expr + ; +calc_expr + := unary_expr + | calc_expr ('*'|'/'|'%') calc_expr + | calc_expr ('+'|'-') calc_expr + ; +unary_expr + := member_expr + | '!'+ member_expr + | '-'+ member_expr + ; +member_expr + := primary_expr + | member_expr '.' id + | member_expr '.' id '(' expr_list? ')' + | member_expr '[' expr ']' + | qualified_id '{' field_inits? '}' + ; +primary_expr + := '.'? id + | '.'? id '(' expr_list? ')' + | '(' expr ')' + | '[' expr_list? ']' + | '{' map_inits? '}' + | literal + ; +expr_list + := expr (',' expr)* + ; +field_inits + := id ':' expr (',' id ':' expr)* + ; +map_inits + := expr ':' expr (',' expr : 'expr')* + ; +qualified_id + := '.'? id ('.' id)* + ; +``` + +Note: CEL and Rego are gradually typed languages. Although the proposal does +not describe developer-defined type designations on constants and functions, +this may be introduced in the future. + +## Constants + +Constants are identifiers associated with an expression. They are useful for +clarifying the logical relationship between components of a rule decision. +Constants referenced within expression will evaluate in the same manner as +though they were written inline into the statement. + +## Functions + +Functions are simply collections of logical statements. Functions decorated +with `@extern` must only declare a signature as the implementation is provided +by the calling environment. Functions decorated with `@test` may incorporate +`with` blocks and cannot be directly or indirectly referenced by `rule` +declarations. + +Functions are idempotent. Given the same input, the functions must return +the same output. Idempotency must hold for both local and extern functions. +At present functions do not support overloads, though this may change in +future iterations of this proposal. + +The general form of a function is as follows: + +``` +function_decl + := decorator? 'function' id '(' arg_list? ')' ('{' statement+ '}')? + ; +decorator + := '@' id + ; +``` + +The set of supported decorators is limited to `@extern` and `@test`. When a +function is annotated with `@extern`, it must be supplied at runtime as part of +the rule evaluation context. For example: + +``` +// Environment must bind a handler for 'db.get' function calls from the policy. +package db; +@extern function get(document_name); +``` + +## Comprehensions + +The one key difference between a Rego v2 expression and a CEL expression is +that Rego v2 has an explicit syntax for list comprehensions rather than use the +optional CEL macros. This syntax simplifies quantifiers as well as mapping and +filtering while permitting the introduction of specialized reducing functions. +It is recommended, but not required that comprehensions be performed within +local functions rather inline within a rule, but this is not a requirement. + +The general form of a comprehension is as follows: + +``` +comprehension_expr + := iter_expr + | '{' (expr '|')? iter_expr '}' // set, map comprehension + | '[' (expr '|')? iter_expr ']' // list comprehension + ; +iter_expr + := 'for' id (',' id)? 'in' expr iter_op_expr? statement? + | 'for' id (',' id)? 'in' expr iter_op_expr? ('{' statement+ '}')? + ; +iter_op_expr + := if_expr + | 'all' expr + | 'any' expr + ; +filter_expr + := 'if' expr + ; +``` + +A set comprehension differs from a list comprehension only in the sense that +the entries within the set are unique. Set comprehensions are also used for +constructing maps and complex objects where the uniqueness constraint generally +holds true. Within maps, colliding keys are overwritten. Within a +comprehension, colliding keys values are merged if the value is declared as a +list. + +### Filter + +The `for-in` expression may be filtered using a condition. The result of the +filtering, absent of other syntax, will be the same as the range type of the +`for-in` expression. If the for-in range is a list, the result type will be a +list. The same is true for maps, structs, and messages. + +``` +for u in users if u.clearance in ['secret', 'top secret'] // list of users +``` + +Filters may be chained as well. + +``` +{ user: [doc] | +// Iteration variables may be referenced within the transform statement +for user in users if user.clearance in ['secret', 'top_secret'] +// The statement after a for-in is implicitly within its block scope. +// Curly braces may be used to explicitly denote the block. +for doc in documents if doc.clearance <= user.clearance } +``` + +### Quantify + +The quantifiers rely on filtering and simply test the `size()` of the filter +result. The result type of the for-in is determined by the surrounding braces. + +``` +// exists one +[ for e in list if e < 10 ].size() == 1 + +// exists at least two unique values, note the `{}` surrounding the `for-in` +// indicates this is the construction of a set from a list. +{ for e in list if e.startsWith('t') }.size() > 2 +``` + +There are also two special operators which can be used to make qualitative +inferences about the elements within an aggregate type, `all` and `any`. The +primary difference between these operators and the filter expressions (or +even standard comprehensions) is that they are accumulator functions with +the same semantics as the logical `&&` and `||`, meaning these operators +can absorb errors. + +``` +// Any element exists in this list +for e in list any e != 'bad_candidate' + +// All elements in this list must be good candidates +for e in list all e == 'good_candidate' +``` + +The `any` and `all` yield boolean outcomes and are thus specialized reducing +functions capable of absorbing errors in the same manner as hand-rolled code. + +### Map + +It is very common for developers to massage data from a variety of sources in +order to compose the desired format best suited for the task at hand. + +``` +// Set of unique image names +{ image.name | for release_name, image in release_images } + +// Map of image name to release names +// Collisions on the image.name will overwrite the existing value. +// Note: Consider adding syntactic sugar to deal with object construction +// during list comprehensions. +{ image.name : release_name | for release_name, image in release_images } + +// List (of pairs) comprehension. +// There is an implicit single-statement block after a for-in if no +// curly braces are present. +[ [user, file] | + for file in files + for user in file.viewers + [file.owner] + if user.matches("@{domain}.{tld}$").groups.domain in ["styra", "google"] +] + +// Equivalent to the above, but with a set of unique user, file tuples and +// explicit block syntax. +{ [user, file] | + for file in files { + for user in file.viewers + [file.owner] + if user.matches("@{domain}.{tld}$").groups.domain in ["styra", "google"] ] + } +} +``` + +### Reduce + +Rather than introduce a special syntax for reduction, Rego v2 provides the +helper functions: `sum()`, `flatten()`, and `join()`. Additional functions +will be added over time, but these helpers represent the most common use +cases of reduction. + +## Modules + +A module represents a collection of functions and constants. Modules are in +the root package unless otherwise indicated by a package declaration. Multiple +modules may share the same package. Functions in modules within the same +package are accessible within other modules without specifying the qualified +function name, e.g. `module.func()`. + +Importing a module makes all symbols within that module accessible by either +the fully qualified module name, or the simple module name (the last fragment +of a qualified package identifier). + + diff --git a/third_party/opa/race.txt b/third_party/opa/race.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/third_party/opa/refactor/doc.go b/third_party/opa/refactor/doc.go new file mode 100644 index 000000000000..37461206218d --- /dev/null +++ b/third_party/opa/refactor/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package refactor diff --git a/third_party/opa/refactor/refactor.go b/third_party/opa/refactor/refactor.go new file mode 100644 index 000000000000..e110ec101f48 --- /dev/null +++ b/third_party/opa/refactor/refactor.go @@ -0,0 +1,30 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package refactor implements different refactoring operations over Rego modules. +package refactor + +import ( + v1 "github.com/open-policy-agent/opa/v1/refactor" +) + +// Error defines the structure of errors returned by refactor. +type Error = v1.Error + +// Refactor implements different refactoring operations over Rego modules eg. renaming packages. +type Refactor = v1.Refactor + +// New returns a new Refactor object. +func New() *Refactor { + return v1.New() +} + +// MoveQuery holds the set of Rego modules whose package paths and other references are to be rewritten +// as per the mapping defined in SrcDstMapping. +// If validate is true, the moved modules will be compiled to ensure they are valid. +type MoveQuery = v1.MoveQuery + +// MoveQueryResult defines the output of a move query and holds the rewritten modules with updated packages paths +// and references. +type MoveQueryResult = v1.MoveQueryResult diff --git a/third_party/opa/rego/doc.go b/third_party/opa/rego/doc.go new file mode 100644 index 000000000000..febe75696c59 --- /dev/null +++ b/third_party/opa/rego/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package rego diff --git a/third_party/opa/rego/errors.go b/third_party/opa/rego/errors.go new file mode 100644 index 000000000000..bcbd2efeddfe --- /dev/null +++ b/third_party/opa/rego/errors.go @@ -0,0 +1,17 @@ +package rego + +import v1 "github.com/open-policy-agent/opa/v1/rego" + +// HaltError is an error type to return from a custom function implementation +// that will abort the evaluation process (analogous to topdown.Halt). +type HaltError = v1.HaltError + +// NewHaltError wraps an error such that the evaluation process will stop +// when it occurs. +func NewHaltError(err error) error { + return v1.NewHaltError(err) +} + +// ErrorDetails interface is satisfied by an error that provides further +// details. +type ErrorDetails = v1.ErrorDetails diff --git a/third_party/opa/rego/plugins.go b/third_party/opa/rego/plugins.go new file mode 100644 index 000000000000..38ef84416fb2 --- /dev/null +++ b/third_party/opa/rego/plugins.go @@ -0,0 +1,17 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package rego + +import ( + v1 "github.com/open-policy-agent/opa/v1/rego" +) + +type TargetPlugin = v1.TargetPlugin + +type TargetPluginEval = v1.TargetPluginEval + +func RegisterPlugin(name string, p TargetPlugin) { + v1.RegisterPlugin(name, p) +} diff --git a/third_party/opa/rego/rego.go b/third_party/opa/rego/rego.go new file mode 100644 index 000000000000..bdcf6c291a91 --- /dev/null +++ b/third_party/opa/rego/rego.go @@ -0,0 +1,628 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package rego exposes high level APIs for evaluating Rego policies. +package rego + +import ( + "io" + "time" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/bundle" + "github.com/open-policy-agent/opa/loader" + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/v1/metrics" + v1 "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/resolver" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/print" + "github.com/open-policy-agent/opa/v1/tracing" +) + +// CompileResult represents the result of compiling a Rego query, zero or more +// Rego modules, and arbitrary contextual data into an executable. +type CompileResult = v1.CompileResult + +// PartialQueries contains the queries and support modules produced by partial +// evaluation. +type PartialQueries = v1.PartialQueries + +// PartialResult represents the result of partial evaluation. The result can be +// used to generate a new query that can be run when inputs are known. +type PartialResult = v1.PartialResult + +// EvalContext defines the set of options allowed to be set at evaluation +// time. Any other options will need to be set on a new Rego object. +type EvalContext = v1.EvalContext + +// EvalOption defines a function to set an option on an EvalConfig +type EvalOption = v1.EvalOption + +// EvalInput configures the input for a Prepared Query's evaluation +func EvalInput(input any) EvalOption { + return v1.EvalInput(input) +} + +// EvalParsedInput configures the input for a Prepared Query's evaluation +func EvalParsedInput(input ast.Value) EvalOption { + return v1.EvalParsedInput(input) +} + +// EvalMetrics configures the metrics for a Prepared Query's evaluation +func EvalMetrics(metric metrics.Metrics) EvalOption { + return v1.EvalMetrics(metric) +} + +// EvalTransaction configures the Transaction for a Prepared Query's evaluation +func EvalTransaction(txn storage.Transaction) EvalOption { + return v1.EvalTransaction(txn) +} + +// EvalInstrument enables or disables instrumenting for a Prepared Query's evaluation +func EvalInstrument(instrument bool) EvalOption { + return v1.EvalInstrument(instrument) +} + +// EvalTracer configures a tracer for a Prepared Query's evaluation +// Deprecated: Use EvalQueryTracer instead. +func EvalTracer(tracer topdown.Tracer) EvalOption { + return v1.EvalTracer(tracer) +} + +// EvalQueryTracer configures a tracer for a Prepared Query's evaluation +func EvalQueryTracer(tracer topdown.QueryTracer) EvalOption { + return v1.EvalQueryTracer(tracer) +} + +// EvalPartialNamespace returns an argument that sets the namespace to use for +// partial evaluation results. The namespace must be a valid package path +// component. +func EvalPartialNamespace(ns string) EvalOption { + return v1.EvalPartialNamespace(ns) +} + +// EvalUnknowns returns an argument that sets the values to treat as +// unknown during partial evaluation. +func EvalUnknowns(unknowns []string) EvalOption { + return v1.EvalUnknowns(unknowns) +} + +// EvalDisableInlining returns an argument that adds a set of paths to exclude from +// partial evaluation inlining. +func EvalDisableInlining(paths []ast.Ref) EvalOption { + return v1.EvalDisableInlining(paths) +} + +// EvalParsedUnknowns returns an argument that sets the values to treat +// as unknown during partial evaluation. +func EvalParsedUnknowns(unknowns []*ast.Term) EvalOption { + return v1.EvalParsedUnknowns(unknowns) +} + +// EvalRuleIndexing will disable indexing optimizations for the +// evaluation. This should only be used when tracing in debug mode. +func EvalRuleIndexing(enabled bool) EvalOption { + return v1.EvalRuleIndexing(enabled) +} + +// EvalEarlyExit will disable 'early exit' optimizations for the +// evaluation. This should only be used when tracing in debug mode. +func EvalEarlyExit(enabled bool) EvalOption { + return v1.EvalEarlyExit(enabled) +} + +// EvalTime sets the wall clock time to use during policy evaluation. +// time.now_ns() calls will return this value. +func EvalTime(x time.Time) EvalOption { + return v1.EvalTime(x) +} + +// EvalSeed sets a reader that will seed randomization required by built-in functions. +// If a seed is not provided crypto/rand.Reader is used. +func EvalSeed(r io.Reader) EvalOption { + return v1.EvalSeed(r) +} + +// EvalInterQueryBuiltinCache sets the inter-query cache that built-in functions can utilize +// during evaluation. +func EvalInterQueryBuiltinCache(c cache.InterQueryCache) EvalOption { + return v1.EvalInterQueryBuiltinCache(c) +} + +// EvalInterQueryBuiltinValueCache sets the inter-query value cache that built-in functions can utilize +// during evaluation. +func EvalInterQueryBuiltinValueCache(c cache.InterQueryValueCache) EvalOption { + return v1.EvalInterQueryBuiltinValueCache(c) +} + +// EvalNDBuiltinCache sets the non-deterministic builtin cache that built-in functions can +// use during evaluation. +func EvalNDBuiltinCache(c builtins.NDBCache) EvalOption { + return v1.EvalNDBuiltinCache(c) +} + +// EvalResolver sets a Resolver for a specified ref path for this evaluation. +func EvalResolver(ref ast.Ref, r resolver.Resolver) EvalOption { + return v1.EvalResolver(ref, r) +} + +// EvalSortSets causes the evaluator to sort sets before returning them as JSON arrays. +func EvalSortSets(yes bool) EvalOption { + return v1.EvalSortSets(yes) +} + +// EvalCopyMaps causes the evaluator to copy `map[string]any`s before returning them. +func EvalCopyMaps(yes bool) EvalOption { + return v1.EvalCopyMaps(yes) +} + +// EvalPrintHook sets the object to use for handling print statement outputs. +func EvalPrintHook(ph print.Hook) EvalOption { + return v1.EvalPrintHook(ph) +} + +// EvalVirtualCache sets the topdown.VirtualCache to use for evaluation. This is +// optional, and if not set, the default cache is used. +func EvalVirtualCache(vc topdown.VirtualCache) EvalOption { + return v1.EvalVirtualCache(vc) +} + +// PreparedEvalQuery holds the prepared Rego state that has been pre-processed +// for subsequent evaluations. +type PreparedEvalQuery = v1.PreparedEvalQuery + +// PreparedPartialQuery holds the prepared Rego state that has been pre-processed +// for partial evaluations. +type PreparedPartialQuery = v1.PreparedPartialQuery + +// Errors represents a collection of errors returned when evaluating Rego. +type Errors = v1.Errors + +// IsPartialEvaluationNotEffectiveErr returns true if err is an error returned by +// this package to indicate that partial evaluation was ineffective. +func IsPartialEvaluationNotEffectiveErr(err error) bool { + return v1.IsPartialEvaluationNotEffectiveErr(err) +} + +// Rego constructs a query and can be evaluated to obtain results. +type Rego = v1.Rego + +// Function represents a built-in function that is callable in Rego. +type Function = v1.Function + +// BuiltinContext contains additional attributes from the evaluator that +// built-in functions can use, e.g., the request context.Context, caches, etc. +type BuiltinContext = v1.BuiltinContext + +type ( + // Builtin1 defines a built-in function that accepts 1 argument. + Builtin1 = v1.Builtin1 + + // Builtin2 defines a built-in function that accepts 2 arguments. + Builtin2 = v1.Builtin2 + + // Builtin3 defines a built-in function that accepts 3 argument. + Builtin3 = v1.Builtin3 + + // Builtin4 defines a built-in function that accepts 4 argument. + Builtin4 = v1.Builtin4 + + // BuiltinDyn defines a built-in function that accepts a list of arguments. + BuiltinDyn = v1.BuiltinDyn +) + +// RegisterBuiltin1 adds a built-in function globally inside the OPA runtime. +func RegisterBuiltin1(decl *Function, impl Builtin1) { + v1.RegisterBuiltin1(decl, impl) +} + +// RegisterBuiltin2 adds a built-in function globally inside the OPA runtime. +func RegisterBuiltin2(decl *Function, impl Builtin2) { + v1.RegisterBuiltin2(decl, impl) +} + +// RegisterBuiltin3 adds a built-in function globally inside the OPA runtime. +func RegisterBuiltin3(decl *Function, impl Builtin3) { + v1.RegisterBuiltin3(decl, impl) +} + +// RegisterBuiltin4 adds a built-in function globally inside the OPA runtime. +func RegisterBuiltin4(decl *Function, impl Builtin4) { + v1.RegisterBuiltin4(decl, impl) +} + +// RegisterBuiltinDyn adds a built-in function globally inside the OPA runtime. +func RegisterBuiltinDyn(decl *Function, impl BuiltinDyn) { + v1.RegisterBuiltinDyn(decl, impl) +} + +// Function1 returns an option that adds a built-in function to the Rego object. +func Function1(decl *Function, f Builtin1) func(*Rego) { + return v1.Function1(decl, f) +} + +// Function2 returns an option that adds a built-in function to the Rego object. +func Function2(decl *Function, f Builtin2) func(*Rego) { + return v1.Function2(decl, f) +} + +// Function3 returns an option that adds a built-in function to the Rego object. +func Function3(decl *Function, f Builtin3) func(*Rego) { + return v1.Function3(decl, f) +} + +// Function4 returns an option that adds a built-in function to the Rego object. +func Function4(decl *Function, f Builtin4) func(*Rego) { + return v1.Function4(decl, f) +} + +// FunctionDyn returns an option that adds a built-in function to the Rego object. +func FunctionDyn(decl *Function, f BuiltinDyn) func(*Rego) { + return v1.FunctionDyn(decl, f) +} + +// FunctionDecl returns an option that adds a custom-built-in function +// __declaration__. NO implementation is provided. This is used for +// non-interpreter execution envs (e.g., Wasm). +func FunctionDecl(decl *Function) func(*Rego) { + return v1.FunctionDecl(decl) +} + +// Dump returns an argument that sets the writer to dump debugging information to. +func Dump(w io.Writer) func(r *Rego) { + return v1.Dump(w) +} + +// Query returns an argument that sets the Rego query. +func Query(q string) func(r *Rego) { + return v1.Query(q) +} + +// ParsedQuery returns an argument that sets the Rego query. +func ParsedQuery(q ast.Body) func(r *Rego) { + return v1.ParsedQuery(q) +} + +// Package returns an argument that sets the Rego package on the query's +// context. +func Package(p string) func(r *Rego) { + return v1.Package(p) +} + +// ParsedPackage returns an argument that sets the Rego package on the query's +// context. +func ParsedPackage(pkg *ast.Package) func(r *Rego) { + return v1.ParsedPackage(pkg) +} + +// Imports returns an argument that adds a Rego import to the query's context. +func Imports(p []string) func(r *Rego) { + return v1.Imports(p) +} + +// ParsedImports returns an argument that adds Rego imports to the query's +// context. +func ParsedImports(imp []*ast.Import) func(r *Rego) { + return v1.ParsedImports(imp) +} + +// Input returns an argument that sets the Rego input document. Input should be +// a native Go value representing the input document. +func Input(x any) func(r *Rego) { + return v1.Input(x) +} + +// ParsedInput returns an argument that sets the Rego input document. +func ParsedInput(x ast.Value) func(r *Rego) { + return v1.ParsedInput(x) +} + +// Unknowns returns an argument that sets the values to treat as unknown during +// partial evaluation. +func Unknowns(unknowns []string) func(r *Rego) { + return v1.Unknowns(unknowns) +} + +// ParsedUnknowns returns an argument that sets the values to treat as unknown +// during partial evaluation. +func ParsedUnknowns(unknowns []*ast.Term) func(r *Rego) { + return v1.ParsedUnknowns(unknowns) +} + +// DisableInlining adds a set of paths to exclude from partial evaluation inlining. +func DisableInlining(paths []string) func(r *Rego) { + return v1.DisableInlining(paths) +} + +// ShallowInlining prevents rules that depend on unknown values from being inlined. +// Rules that only depend on known values are inlined. +func ShallowInlining(yes bool) func(r *Rego) { + return v1.ShallowInlining(yes) +} + +// SkipPartialNamespace disables namespacing of partial evalution results for support +// rules generated from policy. Synthetic support rules are still namespaced. +func SkipPartialNamespace(yes bool) func(r *Rego) { + return v1.SkipPartialNamespace(yes) +} + +// PartialNamespace returns an argument that sets the namespace to use for +// partial evaluation results. The namespace must be a valid package path +// component. +func PartialNamespace(ns string) func(r *Rego) { + return v1.PartialNamespace(ns) +} + +// Module returns an argument that adds a Rego module. +func Module(filename, input string) func(r *Rego) { + return v1.Module(filename, input) +} + +// ParsedModule returns an argument that adds a parsed Rego module. If a string +// module with the same filename name is added, it will override the parsed +// module. +func ParsedModule(module *ast.Module) func(*Rego) { + return v1.ParsedModule(module) +} + +// Load returns an argument that adds a filesystem path to load data +// and Rego modules from. Any file with a *.rego, *.yaml, or *.json +// extension will be loaded. The path can be either a directory or file, +// directories are loaded recursively. The optional ignore string patterns +// can be used to filter which files are used. +// The Load option can only be used once. +// Note: Loading files will require a write transaction on the store. +func Load(paths []string, filter loader.Filter) func(r *Rego) { + return v1.Load(paths, filter) +} + +// LoadBundle returns an argument that adds a filesystem path to load +// a bundle from. The path can be a compressed bundle file or a directory +// to be loaded as a bundle. +// Note: Loading bundles will require a write transaction on the store. +func LoadBundle(path string) func(r *Rego) { + return v1.LoadBundle(path) +} + +// ParsedBundle returns an argument that adds a bundle to be loaded. +func ParsedBundle(name string, b *bundle.Bundle) func(r *Rego) { + return v1.ParsedBundle(name, b) +} + +// Compiler returns an argument that sets the Rego compiler. +func Compiler(c *ast.Compiler) func(r *Rego) { + return v1.Compiler(c) +} + +// Store returns an argument that sets the policy engine's data storage layer. +// +// If using the Load, LoadBundle, or ParsedBundle options then a transaction +// must also be provided via the Transaction() option. After loading files +// or bundles the transaction should be aborted or committed. +func Store(s storage.Store) func(r *Rego) { + return v1.Store(s) +} + +// StoreReadAST returns an argument that sets whether the store should eagerly convert data to AST values. +// +// Only applicable when no store has been set on the Rego object through the Store option. +func StoreReadAST(enabled bool) func(r *Rego) { + return v1.StoreReadAST(enabled) +} + +// Transaction returns an argument that sets the transaction to use for storage +// layer operations. +// +// Requires the store associated with the transaction to be provided via the +// Store() option. If using Load(), LoadBundle(), or ParsedBundle() options +// the transaction will likely require write params. +func Transaction(txn storage.Transaction) func(r *Rego) { + return v1.Transaction(txn) +} + +// Metrics returns an argument that sets the metrics collection. +func Metrics(m metrics.Metrics) func(r *Rego) { + return v1.Metrics(m) +} + +// Instrument returns an argument that enables instrumentation for diagnosing +// performance issues. +func Instrument(yes bool) func(r *Rego) { + return v1.Instrument(yes) +} + +// Trace returns an argument that enables tracing on r. +func Trace(yes bool) func(r *Rego) { + return v1.Trace(yes) +} + +// Tracer returns an argument that adds a query tracer to r. +// Deprecated: Use QueryTracer instead. +func Tracer(t topdown.Tracer) func(r *Rego) { + return v1.Tracer(t) +} + +// QueryTracer returns an argument that adds a query tracer to r. +func QueryTracer(t topdown.QueryTracer) func(r *Rego) { + return v1.QueryTracer(t) +} + +// Runtime returns an argument that sets the runtime data to provide to the +// evaluation engine. +func Runtime(term *ast.Term) func(r *Rego) { + return v1.Runtime(term) +} + +// Time sets the wall clock time to use during policy evaluation. Prepared queries +// do not inherit this parameter. Use EvalTime to set the wall clock time when +// executing a prepared query. +func Time(x time.Time) func(r *Rego) { + return v1.Time(x) +} + +// Seed sets a reader that will seed randomization required by built-in functions. +// If a seed is not provided crypto/rand.Reader is used. +func Seed(r io.Reader) func(*Rego) { + return v1.Seed(r) +} + +// PrintTrace is a helper function to write a human-readable version of the +// trace to the writer w. +func PrintTrace(w io.Writer, r *Rego) { + v1.PrintTrace(w, r) +} + +// PrintTraceWithLocation is a helper function to write a human-readable version of the +// trace to the writer w. +func PrintTraceWithLocation(w io.Writer, r *Rego) { + v1.PrintTraceWithLocation(w, r) +} + +// UnsafeBuiltins sets the built-in functions to treat as unsafe and not allow. +// This option is ignored for module compilation if the caller supplies the +// compiler. This option is always honored for query compilation. Provide an +// empty (non-nil) map to disable checks on queries. +func UnsafeBuiltins(unsafeBuiltins map[string]struct{}) func(r *Rego) { + return v1.UnsafeBuiltins(unsafeBuiltins) +} + +// SkipBundleVerification skips verification of a signed bundle. +func SkipBundleVerification(yes bool) func(r *Rego) { + return v1.SkipBundleVerification(yes) +} + +// InterQueryBuiltinCache sets the inter-query cache that built-in functions can utilize +// during evaluation. +func InterQueryBuiltinCache(c cache.InterQueryCache) func(r *Rego) { + return v1.InterQueryBuiltinCache(c) +} + +// InterQueryBuiltinValueCache sets the inter-query value cache that built-in functions can utilize +// during evaluation. +func InterQueryBuiltinValueCache(c cache.InterQueryValueCache) func(r *Rego) { + return v1.InterQueryBuiltinValueCache(c) +} + +// NDBuiltinCache sets the non-deterministic builtins cache. +func NDBuiltinCache(c builtins.NDBCache) func(r *Rego) { + return v1.NDBuiltinCache(c) +} + +// StrictBuiltinErrors tells the evaluator to treat all built-in function errors as fatal errors. +func StrictBuiltinErrors(yes bool) func(r *Rego) { + return v1.StrictBuiltinErrors(yes) +} + +// BuiltinErrorList supplies an error slice to store built-in function errors. +func BuiltinErrorList(list *[]topdown.Error) func(r *Rego) { + return v1.BuiltinErrorList(list) +} + +// Resolver sets a Resolver for a specified ref path. +func Resolver(ref ast.Ref, r resolver.Resolver) func(r *Rego) { + return v1.Resolver(ref, r) +} + +// Schemas sets the schemaSet +func Schemas(x *ast.SchemaSet) func(r *Rego) { + return v1.Schemas(x) +} + +// Capabilities configures the underlying compiler's capabilities. +// This option is ignored for module compilation if the caller supplies the +// compiler. +func Capabilities(c *ast.Capabilities) func(r *Rego) { + return v1.Capabilities(c) +} + +// Target sets the runtime to exercise. +func Target(t string) func(r *Rego) { + return v1.Target(t) +} + +// GenerateJSON sets the AST to JSON converter for the results. +func GenerateJSON(f func(*ast.Term, *EvalContext) (any, error)) func(r *Rego) { + return v1.GenerateJSON(f) +} + +// PrintHook sets the object to use for handling print statement outputs. +func PrintHook(h print.Hook) func(r *Rego) { + return v1.PrintHook(h) +} + +// DistributedTracingOpts sets the options to be used by distributed tracing. +func DistributedTracingOpts(tr tracing.Options) func(r *Rego) { + return v1.DistributedTracingOpts(tr) +} + +// EnablePrintStatements enables print() calls. If this option is not provided, +// print() calls will be erased from the policy. This option only applies to +// queries and policies that passed as raw strings, i.e., this function will not +// have any affect if the caller supplies the ast.Compiler instance. +func EnablePrintStatements(yes bool) func(r *Rego) { + return v1.EnablePrintStatements(yes) +} + +// Strict enables or disables strict-mode in the compiler +func Strict(yes bool) func(r *Rego) { + return v1.Strict(yes) +} + +func SetRegoVersion(version ast.RegoVersion) func(r *Rego) { + return v1.SetRegoVersion(version) +} + +// New returns a new Rego object. +func New(options ...func(r *Rego)) *Rego { + opts := make([]func(r *Rego), 0, len(options)+1) + opts = append(opts, options...) + opts = append(opts, func(r *Rego) { + if r.RegoVersion() == ast.RegoUndefined { + SetRegoVersion(ast.DefaultRegoVersion)(r) + } + }) + + return v1.New(opts...) +} + +// CompileOption defines a function to set options on Compile calls. +type CompileOption = v1.CompileOption + +// CompileContext contains options for Compile calls. +type CompileContext = v1.CompileContext + +// CompilePartial defines an option to control whether partial evaluation is run +// before the query is planned and compiled. +func CompilePartial(yes bool) CompileOption { + return v1.CompilePartial(yes) +} + +// PrepareOption defines a function to set an option to control +// the behavior of the Prepare call. +type PrepareOption = v1.PrepareOption + +// PrepareConfig holds settings to control the behavior of the +// Prepare call. +type PrepareConfig = v1.PrepareConfig + +// WithPartialEval configures an option for PrepareForEval +// which will have it perform partial evaluation while preparing +// the query (similar to rego.Rego#PartialResult) +func WithPartialEval() PrepareOption { + return v1.WithPartialEval() +} + +// WithNoInline adds a set of paths to exclude from partial evaluation inlining. +func WithNoInline(paths []string) PrepareOption { + return v1.WithNoInline(paths) +} + +// WithBuiltinFuncs carries the rego.Function{1,2,3} per-query function definitions +// to the target plugins. +func WithBuiltinFuncs(bis map[string]*topdown.Builtin) PrepareOption { + return v1.WithBuiltinFuncs(bis) +} diff --git a/third_party/opa/rego/rego_test.go b/third_party/opa/rego/rego_test.go new file mode 100644 index 000000000000..12c95af99964 --- /dev/null +++ b/third_party/opa/rego/rego_test.go @@ -0,0 +1,126 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package rego + +import ( + "context" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestRegoEval_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expResult any + expErrs []string + }{ + { + note: "v0", // v0 is the default version + module: `package test + +p[x] { + x = ["a", "b", "c"][_] +}`, + expResult: []string{"a", "b", "c"}, + }, + { + note: "v0, v1 compile-time violations", + module: `package test +import data.foo +import data.bar as foo + +p[x] { + x = ["a", "b", "c"][_] +}`, + expResult: []string{"a", "b", "c"}, + }, + { + note: "import rego.v1", + module: `package test +import rego.v1 + +p contains x if { + some x in ["a", "b", "c"] +}`, + expResult: []string{"a", "b", "c"}, + }, + { + note: "v0 import rego.v1, v1 compile-time violations", + module: `package test +import rego.v1 + +import data.foo +import data.bar as foo + +p contains x if { + some x in ["a", "b", "c"] +}`, + expErrs: []string{ + "test.rego:5: rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v1", // v1 is NOT the default version + module: `package test + +p contains x if { + some x in ["a", "b", "c"] +}`, + expErrs: []string{ + "test.rego:4: rego_parse_error: unexpected identifier token", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.module, + } + + test.WithTempFS(files, func(root string) { + ctx := context.Background() + + pq, err := New( + Load([]string{root}, nil), + Query("data.test.p"), + ).PrepareForEval(ctx) + + if tc.expErrs != nil { + if err == nil { + t.Fatalf("Expected error but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain %q but got: %v", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + rs, err := pq.Eval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(rs) != 1 { + t.Fatalf("Expected exactly one result but got: %v", rs) + } + + if reflect.DeepEqual(rs[0].Expressions[0].Value, tc.expResult) { + t.Fatalf("Expected %v but got: %v", tc.expResult, rs[0].Expressions[0].Value) + } + } + }) + }) + } +} diff --git a/third_party/opa/rego/resultset.go b/third_party/opa/rego/resultset.go new file mode 100644 index 000000000000..5c03360dfaa6 --- /dev/null +++ b/third_party/opa/rego/resultset.go @@ -0,0 +1,22 @@ +package rego + +import ( + v1 "github.com/open-policy-agent/opa/v1/rego" +) + +// ResultSet represents a collection of output from Rego evaluation. An empty +// result set represents an undefined query. +type ResultSet = v1.ResultSet + +// Vars represents a collection of variable bindings. The keys are the variable +// names and the values are the binding values. +type Vars = v1.Vars + +// Result defines the output of Rego evaluation. +type Result = v1.Result + +// Location defines a position in a Rego query or module. +type Location = v1.Location + +// ExpressionValue defines the value of an expression in a Rego query. +type ExpressionValue = v1.ExpressionValue diff --git a/third_party/opa/repl/doc.go b/third_party/opa/repl/doc.go new file mode 100644 index 000000000000..fa92ef10ee57 --- /dev/null +++ b/third_party/opa/repl/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package repl diff --git a/third_party/opa/repl/errors.go b/third_party/opa/repl/errors.go new file mode 100644 index 000000000000..2be89dca20b9 --- /dev/null +++ b/third_party/opa/repl/errors.go @@ -0,0 +1,16 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package repl + +import v1 "github.com/open-policy-agent/opa/v1/repl" + +// Error is the error type returned by the REPL. +type Error = v1.Error + +const ( + // BadArgsErr indicates bad arguments were provided to a built-in REPL + // command. + BadArgsErr string = v1.BadArgsErr +) diff --git a/third_party/opa/repl/repl.go b/third_party/opa/repl/repl.go new file mode 100644 index 000000000000..d88742d7af2c --- /dev/null +++ b/third_party/opa/repl/repl.go @@ -0,0 +1,26 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package repl implements a Read-Eval-Print-Loop (REPL) for interacting with the policy engine. +// +// The REPL is typically used from the command line, however, it can also be used as a library. +// nolint: goconst // String reuse here doesn't make sense to deduplicate. +package repl + +import ( + "io" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/storage" + v1 "github.com/open-policy-agent/opa/v1/repl" +) + +// REPL represents an instance of the interactive shell. +type REPL = v1.REPL + +// New returns a new instance of the REPL. +func New(store storage.Store, historyPath string, output io.Writer, outputFormat string, errLimit int, banner string) *REPL { + return v1.New(store, historyPath, output, outputFormat, errLimit, banner). + WithRegoVersion(ast.DefaultRegoVersion) +} diff --git a/third_party/opa/repl/repl_test.go b/third_party/opa/repl/repl_test.go new file mode 100644 index 000000000000..824b23c17409 --- /dev/null +++ b/third_party/opa/repl/repl_test.go @@ -0,0 +1,161 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package repl + +import ( + "bytes" + "context" + "strings" + "testing" + + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/storage/inmem" + "github.com/open-policy-agent/opa/util" +) + +func TestOneShot_DefaultRegoVersion(t *testing.T) { + type action struct { + line string + expOutput string + expErrs []string + } + + tests := []struct { + note string + actions []action + }{ + { + note: "v0 rule, v1 compile-time violation", + actions: []action{ + { + line: "b { data := 1; data == 1 }", + expOutput: "Rule 'b' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v1 keywords used", + actions: []action{ + { + line: "a contains 2 if { true }", + expErrs: []string{ + "rego_unsafe_var_error: var a is unsafe", + }, + }, + }, + }, + { + note: "v1 keywords not used", + actions: []action{ + { + line: "a[2] { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v1 keywords imported", + actions: []action{ + { + line: "import future.keywords", + }, + { + line: "a contains 2 if { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "rego.v1 imported", + actions: []action{ + { + line: "import rego.v1", + }, + { + line: "a contains 2 if { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v1 keywords", + actions: []action{ + { + line: "a contains 2 if { true }", + expErrs: []string{ + "rego_unsafe_var_error: var a is unsafe", + }, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + for _, action := range tc.actions { + err := repl.OneShot(ctx, action.line) + + if len(action.expErrs) != 0 { + if err == nil { + t.Fatalf("Expected error but got: %s", buffer.String()) + } + + for _, e := range action.expErrs { + if !strings.Contains(err.Error(), e) { + t.Fatalf("Expected error to contain:\n\n%q\n\nbut got:\n\n%v", e, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), action.expOutput) + } + } + }) + } +} + +func expectOutput(t *testing.T, output string, expected string) { + t.Helper() + if output != expected { + t.Errorf("Repl output: expected %#v but got %#v", expected, output) + } +} + +func newRepl(store storage.Store, buffer *bytes.Buffer) *REPL { + repl := New(store, "", buffer, "", 0, "") + return repl +} + +func newTestStore() storage.Store { + input := ` + { + "a": [ + { + "b": { + "c": [true,2,false] + } + }, + { + "b": { + "c": [false,true,1] + } + } + ] + } + ` + var data map[string]any + err := util.UnmarshalJSON([]byte(input), &data) + if err != nil { + panic(err) + } + return inmem.NewFromObject(data) +} diff --git a/third_party/opa/resolver/doc.go b/third_party/opa/resolver/doc.go new file mode 100644 index 000000000000..5d6675dffba4 --- /dev/null +++ b/third_party/opa/resolver/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package resolver diff --git a/third_party/opa/resolver/interface.go b/third_party/opa/resolver/interface.go new file mode 100644 index 000000000000..12b8a6aed4f5 --- /dev/null +++ b/third_party/opa/resolver/interface.go @@ -0,0 +1,18 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package resolver + +import ( + v1 "github.com/open-policy-agent/opa/v1/resolver" +) + +// Resolver defines an external value resolver for OPA evaluations. +type Resolver = v1.Resolver + +// Input as provided to a Resolver instance when evaluating. +type Input = v1.Input + +// Result of resolving a ref. +type Result = v1.Result diff --git a/third_party/opa/resolver/wasm/doc.go b/third_party/opa/resolver/wasm/doc.go new file mode 100644 index 000000000000..165997e4a245 --- /dev/null +++ b/third_party/opa/resolver/wasm/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package wasm diff --git a/third_party/opa/resolver/wasm/wasm.go b/third_party/opa/resolver/wasm/wasm.go new file mode 100644 index 000000000000..65311077f7a8 --- /dev/null +++ b/third_party/opa/resolver/wasm/wasm.go @@ -0,0 +1,20 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package wasm + +import ( + "github.com/open-policy-agent/opa/ast" + v1 "github.com/open-policy-agent/opa/v1/resolver/wasm" +) + +// New creates a new Resolver instance which is using the Wasm module +// policy for the given entrypoint ref. +func New(entrypoints []ast.Ref, policy []byte, data any) (*Resolver, error) { + return v1.New(entrypoints, policy, data) +} + +// Resolver implements the resolver.Resolver interface +// using Wasm modules to perform an evaluation. +type Resolver = v1.Resolver diff --git a/third_party/opa/runtime/doc.go b/third_party/opa/runtime/doc.go new file mode 100644 index 000000000000..7d72c66a7f31 --- /dev/null +++ b/third_party/opa/runtime/doc.go @@ -0,0 +1,10 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package runtime contains the entry point to the policy engine. +// +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package runtime diff --git a/third_party/opa/runtime/logging.go b/third_party/opa/runtime/logging.go new file mode 100644 index 000000000000..5b84ea6a511b --- /dev/null +++ b/third_party/opa/runtime/logging.go @@ -0,0 +1,21 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package runtime + +import ( + "net/http" + + "github.com/open-policy-agent/opa/logging" + v1 "github.com/open-policy-agent/opa/v1/runtime" +) + +// LoggingHandler returns an http.Handler that will print log messages +// containing the request information as well as response status and latency. +type LoggingHandler = v1.LoggingHandler + +// NewLoggingHandler returns a new http.Handler. +func NewLoggingHandler(logger logging.Logger, inner http.Handler) http.Handler { + return v1.NewLoggingHandler(logger, inner) +} diff --git a/third_party/opa/runtime/runtime.go b/third_party/opa/runtime/runtime.go new file mode 100644 index 000000000000..42b4bda2cf63 --- /dev/null +++ b/third_party/opa/runtime/runtime.go @@ -0,0 +1,40 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package runtime + +import ( + "context" + + "github.com/open-policy-agent/opa/plugins" + v1 "github.com/open-policy-agent/opa/v1/runtime" +) + +// RegisterPlugin registers a plugin factory with the runtime +// package. When the runtime is created, the factories are used to parse +// plugin configuration and instantiate plugins. If no configuration is +// provided, plugins are not instantiated. This function is idempotent. +func RegisterPlugin(name string, factory plugins.Factory) { + v1.RegisterPlugin(name, factory) +} + +// Params stores the configuration for an OPA instance. +type Params = v1.Params + +// LoggingConfig stores the configuration for OPA's logging behaviour. +type LoggingConfig = v1.LoggingConfig + +// NewParams returns a new Params object. +func NewParams() Params { + return v1.NewParams() +} + +// Runtime represents a single OPA instance. +type Runtime = v1.Runtime + +// NewRuntime returns a new Runtime object initialized with params. Clients must +// call StartServer() or StartREPL() to start the runtime in either mode. +func NewRuntime(ctx context.Context, params Params) (*Runtime, error) { + return v1.NewRuntime(ctx, params) +} diff --git a/third_party/opa/schemas/doc.go b/third_party/opa/schemas/doc.go new file mode 100644 index 000000000000..968bdea04351 --- /dev/null +++ b/third_party/opa/schemas/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package schemas diff --git a/third_party/opa/schemas/schemas.go b/third_party/opa/schemas/schemas.go new file mode 100644 index 000000000000..42fa9db9ba15 --- /dev/null +++ b/third_party/opa/schemas/schemas.go @@ -0,0 +1,13 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package schemas + +import ( + v1 "github.com/open-policy-agent/opa/v1/schemas" +) + +// FS contains the known schemas for OPA's Authorization Policy etc. +// "authorizationPolicy.json" contains the input schema for OPA's Authorization Policy +var FS = v1.FS diff --git a/third_party/opa/sdk/doc.go b/third_party/opa/sdk/doc.go new file mode 100644 index 000000000000..4386bae7626f --- /dev/null +++ b/third_party/opa/sdk/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package sdk diff --git a/third_party/opa/sdk/opa.go b/third_party/opa/sdk/opa.go new file mode 100644 index 000000000000..2c92d635caeb --- /dev/null +++ b/third_party/opa/sdk/opa.go @@ -0,0 +1,41 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package sdk + +import ( + "context" + + "github.com/open-policy-agent/opa/ast" + v1 "github.com/open-policy-agent/opa/v1/sdk" +) + +type OPA = v1.OPA + +type Options = v1.Options + +type DecisionOptions = v1.DecisionOptions + +type DecisionResult = v1.DecisionResult + +type PartialQueryMapper = v1.PartialQueryMapper + +type PartialOptions = v1.PartialOptions + +type PartialResult = v1.PartialResult + +type Error = v1.Error + +type RawMapper = v1.RawMapper + +func New(ctx context.Context, opts Options) (*OPA, error) { + if opts.RegoVersion == ast.RegoUndefined { + opts.RegoVersion = ast.DefaultRegoVersion + } + return v1.New(ctx, opts) +} + +func IsUndefinedErr(err error) bool { + return v1.IsUndefinedErr(err) +} diff --git a/third_party/opa/sdk/opa_test.go b/third_party/opa/sdk/opa_test.go new file mode 100644 index 000000000000..be2fc91ef463 --- /dev/null +++ b/third_party/opa/sdk/opa_test.go @@ -0,0 +1,87 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package sdk_test + +import ( + "context" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/sdk" + sdktest "github.com/open-policy-agent/opa/v1/sdk/test" +) + +func TestDefaultRegoVersion(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.RawBundles(true), + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + // v0 module + "main.rego": ` +package system + +main { + p[_] == "a" +} + +p[x] { + x = "a" +} + +str = "foo" + +loopback = input +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{}); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(bool); !ok || !decision { + t.Fatal("expected true but got:", decision, ok) + } + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{Path: "/system/str"}); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(string); !ok || decision != "foo" { + t.Fatal(`expected "foo" but got:`, decision) + } + + exp := map[string]any{"foo": "bar"} + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{Path: "/system/loopback", Input: map[string]any{"foo": "bar"}}); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(result.Result, exp) { + t.Fatalf("expected %v but got %v", exp, result.Result) + } +} diff --git a/third_party/opa/sdk/test/doc.go b/third_party/opa/sdk/test/doc.go new file mode 100644 index 000000000000..dffe11ff7c70 --- /dev/null +++ b/third_party/opa/sdk/test/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package test diff --git a/third_party/opa/sdk/test/test.go b/third_party/opa/sdk/test/test.go new file mode 100644 index 000000000000..85e2e2a2003b --- /dev/null +++ b/third_party/opa/sdk/test/test.go @@ -0,0 +1,65 @@ +package test + +import ( + "github.com/open-policy-agent/opa/ast" + v1 "github.com/open-policy-agent/opa/v1/sdk/test" +) + +// MockBundle sets a bundle named file on the test server containing the given +// policies. +func MockBundle(file string, policies map[string]string) func(*Server) error { + return v1.MockBundle(file, policies) +} + +// MockOCIBundle prepares the server to allow serving "/v2" OCI responses from the supplied policies +// Ref parameter must be in the form of //: that will be used in detecting future calls +func MockOCIBundle(ref string, policies map[string]string) func(*Server) error { + return v1.MockOCIBundle(ref, policies) +} + +// Ready provides a channel that the server will use to gate readiness. The +// caller can provide this channel to prevent the server from becoming ready. +// The server will response with HTTP 500 responses until ready. The caller +// should close the channel to indicate readiness. +func Ready(ch chan struct{}) func(*Server) error { + return v1.Ready(ch) +} + +// Server provides a mock HTTP server for testing the SDK and integrations. +type Server = v1.Server + +// MustNewServer returns a new Server for test purposes or panics if an error occurs. +func MustNewServer(opts ...func(*Server) error) *Server { + return v1.MustNewServer(setRegoVersion(opts)...) +} + +// NewServer returns a new Server for test purposes. +func NewServer(opts ...func(*Server) error) (*Server, error) { + return v1.NewServer(setRegoVersion(opts)...) +} + +func RawBundles(raw bool) func(*Server) error { + return v1.RawBundles(raw) +} + +// ParserOptions sets the ast.ParserOptions to use when parsing modules when preparing bundles. +func ParserOptions(popts ast.ParserOptions) func(*Server) error { + return v1.ParserOptions(popts) +} + +func setRegoVersion(opts []func(*Server) error) []func(*v1.Server) error { + cpy := make([]func(*v1.Server) error, 0, len(opts)+1) + cpy = append(cpy, opts...) + + // Sets rego-version to default (v0) if not set. + // Must be last in list of options. + cpy = append(cpy, func(s *v1.Server) error { + if popts := s.ParserOptions(); popts.RegoVersion == ast.RegoUndefined { + popts.RegoVersion = ast.DefaultRegoVersion + return ParserOptions(popts)(s) + } + return nil + }) + + return cpy +} diff --git a/third_party/opa/server/authorizer/authorizer.go b/third_party/opa/server/authorizer/authorizer.go new file mode 100644 index 000000000000..c0aed22856cb --- /dev/null +++ b/third_party/opa/server/authorizer/authorizer.go @@ -0,0 +1,71 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package authorizer provides authorization handlers to the server. +package authorizer + +import ( + "context" + "net/http" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/topdown/cache" + "github.com/open-policy-agent/opa/topdown/print" + v1 "github.com/open-policy-agent/opa/v1/server/authorizer" +) + +// Basic provides policy-based authorization over incoming requests. +type Basic = v1.Basic + +// Runtime returns an argument that sets the runtime on the authorizer. +func Runtime(term *ast.Term) func(*Basic) { + return v1.Runtime(term) +} + +// Decision returns an argument that sets the path of the authorization decision +// to query. +func Decision(ref func() ast.Ref) func(*Basic) { + return v1.Decision(ref) +} + +// PrintHook sets the object to use for handling print statement outputs. +func PrintHook(printHook print.Hook) func(*Basic) { + return v1.PrintHook(printHook) +} + +// EnablePrintStatements enables print() calls. If this option is not provided, +// print() calls will be erased from the policy. This option only applies to +// queries and policies that passed as raw strings, i.e., this function will not +// have any affect if the caller supplies the ast.Compiler instance. +func EnablePrintStatements(yes bool) func(r *Basic) { + return v1.EnablePrintStatements(yes) +} + +// InterQueryCache enables the inter-query cache on the authorizer +func InterQueryCache(interQueryCache cache.InterQueryCache) func(*Basic) { + return v1.InterQueryCache(interQueryCache) +} + +// InterQueryValueCache enables the inter-query value cache on the authorizer +func InterQueryValueCache(interQueryValueCache cache.InterQueryValueCache) func(*Basic) { + return v1.InterQueryValueCache(interQueryValueCache) +} + +// NewBasic returns a new Basic object. +func NewBasic(inner http.Handler, compiler func() *ast.Compiler, store storage.Store, opts ...func(*Basic)) http.Handler { + return v1.NewBasic(inner, compiler, store, opts...) +} + +// SetBodyOnContext adds the parsed input value to the context. This function is only +// exposed for test purposes. +func SetBodyOnContext(ctx context.Context, x any) context.Context { + return v1.SetBodyOnContext(ctx, x) +} + +// GetBodyOnContext returns the parsed input from the request context if it exists. +// The authorizer saves the parsed input on the context when it runs. +func GetBodyOnContext(ctx context.Context) (any, bool) { + return v1.GetBodyOnContext(ctx) +} diff --git a/third_party/opa/server/authorizer/doc.go b/third_party/opa/server/authorizer/doc.go new file mode 100644 index 000000000000..3abace276899 --- /dev/null +++ b/third_party/opa/server/authorizer/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package authorizer diff --git a/third_party/opa/server/buffer.go b/third_party/opa/server/buffer.go new file mode 100644 index 000000000000..be44f3a9cdea --- /dev/null +++ b/third_party/opa/server/buffer.go @@ -0,0 +1,15 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package server + +import ( + v1 "github.com/open-policy-agent/opa/v1/server" +) + +// Info contains information describing a policy decision. +type Info = v1.Info + +// BundleInfo contains information describing a bundle. +type BundleInfo = v1.BundleInfo diff --git a/third_party/opa/server/doc.go b/third_party/opa/server/doc.go new file mode 100644 index 000000000000..4eb7efad2a1e --- /dev/null +++ b/third_party/opa/server/doc.go @@ -0,0 +1,10 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package server contains the policy engine's server handlers. +// +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package server diff --git a/third_party/opa/server/features.go b/third_party/opa/server/features.go new file mode 100644 index 000000000000..3b0153722087 --- /dev/null +++ b/third_party/opa/server/features.go @@ -0,0 +1,10 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build opa_wasm +// +build opa_wasm + +package server + +import _ "github.com/open-policy-agent/opa/v1/features/wasm" diff --git a/third_party/opa/server/handlers/compress.go b/third_party/opa/server/handlers/compress.go new file mode 100644 index 000000000000..08c73ac1ae30 --- /dev/null +++ b/third_party/opa/server/handlers/compress.go @@ -0,0 +1,20 @@ +package handlers + +import ( + "net/http" + + v1 "github.com/open-policy-agent/opa/v1/server/handlers" +) + +// This handler applies only for data and compile endpoints, for selected HTTP methods +// +// If the client asked for a gzip response, this handler will buffer the response and +// wait until it reached a certain threshold. If the threshold is not hit, the uncompressed response is sent +// +// If a gzip response is not asked by the client, it'll send the uncompressed response +// +// The threshold and the gzip compression level can be modified from server's configuration + +func CompressHandler(handler http.Handler, gzipMinLength int, gzipCompressionLevel int) http.Handler { + return v1.CompressHandler(handler, gzipMinLength, gzipCompressionLevel) +} diff --git a/third_party/opa/server/handlers/decoding.go b/third_party/opa/server/handlers/decoding.go new file mode 100644 index 000000000000..a276972ab924 --- /dev/null +++ b/third_party/opa/server/handlers/decoding.go @@ -0,0 +1,19 @@ +package handlers + +import ( + "net/http" + + v1 "github.com/open-policy-agent/opa/v1/server/handlers" +) + +// This handler provides hard limits on the size of the request body, for both +// the raw body content, and also for the decompressed size when gzip +// compression is used. +// +// The Content-Length restriction happens here in the handler, but the +// decompressed size limit is enforced later, in `util.ReadMaybeCompressedBody`. +// The handler passes the gzip size limits down to that function through the +// request context whenever gzip encoding is present. +func DecodingLimitsHandler(handler http.Handler, maxLength, gzipMaxLength int64) http.Handler { + return v1.DecodingLimitsHandler(handler, maxLength, gzipMaxLength) +} diff --git a/third_party/opa/server/handlers/doc.go b/third_party/opa/server/handlers/doc.go new file mode 100644 index 000000000000..9ec8997a0b21 --- /dev/null +++ b/third_party/opa/server/handlers/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package handlers diff --git a/third_party/opa/server/identifier/certs.go b/third_party/opa/server/identifier/certs.go new file mode 100644 index 000000000000..3a224fa194f7 --- /dev/null +++ b/third_party/opa/server/identifier/certs.go @@ -0,0 +1,18 @@ +package identifier + +import ( + "crypto/x509" + "net/http" + + v1 "github.com/open-policy-agent/opa/v1/server/identifier" +) + +// ClientCertificates returns the ClientCertificates of the caller associated with ctx. +func ClientCertificates(r *http.Request) ([]*x509.Certificate, bool) { + return v1.ClientCertificates(r) +} + +// SetClientCertificates returns a new http.Request with the ClientCertificates set to v. +func SetClientCertificates(r *http.Request, v []*x509.Certificate) *http.Request { + return v1.SetClientCertificates(r, v) +} diff --git a/third_party/opa/server/identifier/doc.go b/third_party/opa/server/identifier/doc.go new file mode 100644 index 000000000000..59bda00fd079 --- /dev/null +++ b/third_party/opa/server/identifier/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package identifier diff --git a/third_party/opa/server/identifier/identifier.go b/third_party/opa/server/identifier/identifier.go new file mode 100644 index 000000000000..908fb62c3375 --- /dev/null +++ b/third_party/opa/server/identifier/identifier.go @@ -0,0 +1,22 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package identifier provides handlers for associating identity information with incoming requests. +package identifier + +import ( + "net/http" + + v1 "github.com/open-policy-agent/opa/v1/server/identifier" +) + +// Identity returns the identity of the caller associated with ctx. +func Identity(r *http.Request) (string, bool) { + return v1.Identity(r) +} + +// SetIdentity returns a new http.Request with the identity set to v. +func SetIdentity(r *http.Request, v string) *http.Request { + return v1.SetIdentity(r, v) +} diff --git a/third_party/opa/server/identifier/tls.go b/third_party/opa/server/identifier/tls.go new file mode 100644 index 000000000000..6fe432290c58 --- /dev/null +++ b/third_party/opa/server/identifier/tls.go @@ -0,0 +1,19 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package identifier + +import ( + "net/http" + + v1 "github.com/open-policy-agent/opa/v1/server/identifier" +) + +// TLSBased extracts the CN of the client's TLS ceritificate +type TLSBased = v1.TLSBased + +// NewTLSBased returns a new TLSBased object. +func NewTLSBased(inner http.Handler) *TLSBased { + return v1.NewTLSBased(inner) +} diff --git a/third_party/opa/server/identifier/token.go b/third_party/opa/server/identifier/token.go new file mode 100644 index 000000000000..0126e3f809ab --- /dev/null +++ b/third_party/opa/server/identifier/token.go @@ -0,0 +1,15 @@ +package identifier + +import ( + "net/http" + + v1 "github.com/open-policy-agent/opa/v1/server/identifier" +) + +// TokenBased extracts Bearer tokens from the request. +type TokenBased = v1.TokenBased + +// NewTokenBased returns a new TokenBased object. +func NewTokenBased(inner http.Handler) *TokenBased { + return v1.NewTokenBased(inner) +} diff --git a/third_party/opa/server/server.go b/third_party/opa/server/server.go new file mode 100644 index 000000000000..fb19a9bc0e79 --- /dev/null +++ b/third_party/opa/server/server.go @@ -0,0 +1,65 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package server + +import ( + v1 "github.com/open-policy-agent/opa/v1/server" +) + +// AuthenticationScheme enumerates the supported authentication schemes. The +// authentication scheme determines how client identities are established. +type AuthenticationScheme = v1.AuthenticationScheme + +// Set of supported authentication schemes. +const ( + AuthenticationOff = v1.AuthenticationOff + AuthenticationToken = v1.AuthenticationToken + AuthenticationTLS = v1.AuthenticationTLS +) + +// AuthorizationScheme enumerates the supported authorization schemes. The authorization +// scheme determines how access to OPA is controlled. +type AuthorizationScheme = v1.AuthorizationScheme + +// Set of supported authorization schemes. +const ( + AuthorizationOff = v1.AuthorizationOff + AuthorizationBasic = v1.AuthorizationBasic +) + +// Set of handlers for use in the "handler" dimension of the duration metric. +const ( + PromHandlerV0Data = v1.PromHandlerV0Data + PromHandlerV1Data = v1.PromHandlerV1Data + PromHandlerV1Query = v1.PromHandlerV1Query + PromHandlerV1Policies = v1.PromHandlerV1Policies + PromHandlerV1Compile = v1.PromHandlerV1Compile + PromHandlerV1Config = v1.PromHandlerV1Config + PromHandlerV1Status = v1.PromHandlerV1Status + PromHandlerIndex = v1.PromHandlerIndex + PromHandlerCatch = v1.PromHandlerCatch + PromHandlerHealth = v1.PromHandlerHealth + PromHandlerAPIAuthz = v1.PromHandlerAPIAuthz +) + +// Server represents an instance of OPA running in server mode. +type Server = v1.Server + +// Metrics defines the interface that the server requires for recording HTTP +// handler metrics. +type Metrics = v1.Metrics + +// TLSConfig represents the TLS configuration for the server. +// This configuration is used to configure file watchers to reload each file as it +// changes on disk. +type TLSConfig = v1.TLSConfig + +// Loop will contain all the calls from the server that we'll be listening on. +type Loop = v1.Loop + +// New returns a new Server. +func New() *Server { + return v1.New() +} diff --git a/third_party/opa/server/types/doc.go b/third_party/opa/server/types/doc.go new file mode 100644 index 000000000000..d616c7aa56e4 --- /dev/null +++ b/third_party/opa/server/types/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package types diff --git a/third_party/opa/server/types/types.go b/third_party/opa/server/types/types.go new file mode 100644 index 000000000000..c8224b13fcd7 --- /dev/null +++ b/third_party/opa/server/types/types.go @@ -0,0 +1,245 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package types contains request/response types and codes for the server. +package types + +import ( + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown" + v1 "github.com/open-policy-agent/opa/v1/server/types" +) + +// Error codes returned by OPA's REST API. +const ( + CodeInternal = v1.CodeInternal + CodeEvaluation = v1.CodeEvaluation + CodeUnauthorized = v1.CodeUnauthorized + CodeInvalidParameter = v1.CodeInvalidParameter + CodeInvalidOperation = v1.CodeInvalidOperation + CodeResourceNotFound = v1.CodeResourceNotFound + CodeResourceConflict = v1.CodeResourceConflict + CodeUndefinedDocument = v1.CodeUndefinedDocument +) + +// ErrorV1 models an error response sent to the client. +type ErrorV1 = v1.ErrorV1 + +// NewErrorV1 returns a new ErrorV1 object. +func NewErrorV1(code, f string, a ...any) *ErrorV1 { + return v1.NewErrorV1(code, f, a...) +} + +// Messages included in error responses. +const ( + MsgCompileModuleError = v1.MsgCompileModuleError + MsgParseQueryError = v1.MsgParseQueryError + MsgCompileQueryError = v1.MsgCompileQueryError + MsgEvaluationError = v1.MsgEvaluationError + MsgUnauthorizedUndefinedError = v1.MsgUnauthorizedUndefinedError + MsgUnauthorizedError = v1.MsgUnauthorizedError + MsgUndefinedError = v1.MsgUndefinedError + MsgMissingError = v1.MsgMissingError + MsgFoundUndefinedError = v1.MsgFoundUndefinedError + MsgPluginConfigError = v1.MsgPluginConfigError + MsgDecodingLimitError = v1.MsgDecodingLimitError + MsgDecodingGzipLimitError = v1.MsgDecodingGzipLimitError +) + +// PatchV1 models a single patch operation against a document. +type PatchV1 = v1.PatchV1 + +// PolicyListResponseV1 models the response message for the Policy API list operation. +type PolicyListResponseV1 = v1.PolicyListResponseV1 + +// PolicyGetResponseV1 models the response message for the Policy API get operation. +type PolicyGetResponseV1 = v1.PolicyGetResponseV1 + +// PolicyPutResponseV1 models the response message for the Policy API put operation. +type PolicyPutResponseV1 = v1.PolicyPutResponseV1 + +// PolicyDeleteResponseV1 models the response message for the Policy API delete operation. +type PolicyDeleteResponseV1 = v1.PolicyDeleteResponseV1 + +// PolicyV1 models a policy module in OPA. +type PolicyV1 = v1.PolicyV1 + +// ProvenanceV1 models a collection of build/version information. +type ProvenanceV1 = v1.ProvenanceV1 + +// ProvenanceBundleV1 models a bundle at some point in time +type ProvenanceBundleV1 = v1.ProvenanceBundleV1 + +// DataRequestV1 models the request message for Data API POST operations. +type DataRequestV1 = v1.DataRequestV1 + +// DataResponseV1 models the response message for Data API read operations. +type DataResponseV1 = v1.DataResponseV1 + +// Warning models DataResponse warnings +type Warning = v1.Warning + +// Warning Codes +const CodeAPIUsageWarn = v1.CodeAPIUsageWarn + +// Warning Messages +const MsgInputKeyMissing = v1.MsgInputKeyMissing + +// NewWarning returns a new Warning object +func NewWarning(code, message string) *Warning { + return v1.NewWarning(code, message) +} + +// MetricsV1 models a collection of performance metrics. +type MetricsV1 = v1.MetricsV1 + +// QueryResponseV1 models the response message for Query API operations. +type QueryResponseV1 = v1.QueryResponseV1 + +// AdhocQueryResultSetV1 models the result of a Query API query. +type AdhocQueryResultSetV1 = v1.AdhocQueryResultSetV1 + +// ExplainModeV1 defines supported values for the "explain" query parameter. +type ExplainModeV1 = v1.ExplainModeV1 + +// Explanation mode enumeration. +const ( + ExplainOffV1 ExplainModeV1 = v1.ExplainOffV1 + ExplainFullV1 ExplainModeV1 = v1.ExplainFullV1 + ExplainNotesV1 ExplainModeV1 = v1.ExplainNotesV1 + ExplainFailsV1 ExplainModeV1 = v1.ExplainFailsV1 + ExplainDebugV1 ExplainModeV1 = v1.ExplainDebugV1 +) + +// TraceV1 models the trace result returned for queries that include the +// "explain" parameter. +type TraceV1 = v1.TraceV1 + +// TraceV1Raw models the trace result returned for queries that include the +// "explain" parameter. The trace is modelled as series of trace events that +// identify the expression, local term bindings, query hierarchy, etc. +type TraceV1Raw = v1.TraceV1Raw + +// TraceV1Pretty models the trace result returned for queries that include the "explain" +// parameter. The trace is modelled as a human readable array of strings representing the +// evaluation of the query. +type TraceV1Pretty = v1.TraceV1Pretty + +// NewTraceV1 returns a new TraceV1 object. +func NewTraceV1(trace []*topdown.Event, pretty bool) (result TraceV1, err error) { + return v1.NewTraceV1(trace, pretty) +} + +// TraceEventV1 represents a step in the query evaluation process. +type TraceEventV1 = v1.TraceEventV1 + +// BindingsV1 represents a set of term bindings. +type BindingsV1 = v1.BindingsV1 + +// BindingV1 represents a single term binding. +type BindingV1 = v1.BindingV1 + +// NewBindingsV1 returns a new BindingsV1 object. +func NewBindingsV1(locals *ast.ValueMap) (result []*BindingV1) { + return v1.NewBindingsV1(locals) +} + +// CompileRequestV1 models the request message for Compile API operations. +type CompileRequestV1 = v1.CompileRequestV1 + +// CompileResponseV1 models the response message for Compile API operations. +type CompileResponseV1 = v1.CompileResponseV1 + +// PartialEvaluationResultV1 represents the output of partial evaluation and is +// included in Compile API responses. +type PartialEvaluationResultV1 = v1.PartialEvaluationResultV1 + +// QueryRequestV1 models the request message for Query API operations. +type QueryRequestV1 = v1.QueryRequestV1 + +// ConfigResponseV1 models the response message for Config API operations. +type ConfigResponseV1 = v1.ConfigResponseV1 + +// StatusResponseV1 models the response message for Status API (pull) operations. +type StatusResponseV1 = v1.StatusResponseV1 + +// HealthResponseV1 models the response message for Health API operations. +type HealthResponseV1 = v1.HealthResponseV1 + +const ( + // ParamQueryV1 defines the name of the HTTP URL parameter that specifies + // values for the request query. + ParamQueryV1 = v1.ParamQueryV1 + + // ParamInputV1 defines the name of the HTTP URL parameter that specifies + // values for the "input" document. + ParamInputV1 = v1.ParamInputV1 + + // ParamPrettyV1 defines the name of the HTTP URL parameter that indicates + // the client wants to receive a pretty-printed version of the response. + ParamPrettyV1 = v1.ParamPrettyV1 + + // ParamExplainV1 defines the name of the HTTP URL parameter that indicates the + // client wants to receive explanations in addition to the result. + ParamExplainV1 = v1.ParamExplainV1 + + // ParamMetricsV1 defines the name of the HTTP URL parameter that indicates + // the client wants to receive performance metrics in addition to the + // result. + ParamMetricsV1 = v1.ParamMetricsV1 + + // ParamInstrumentV1 defines the name of the HTTP URL parameter that + // indicates the client wants to receive instrumentation data for + // diagnosing performance issues. + ParamInstrumentV1 = v1.ParamInstrumentV1 + + // ParamProvenanceV1 defines the name of the HTTP URL parameter that indicates + // the client wants build and version information in addition to the result. + ParamProvenanceV1 = v1.ParamProvenanceV1 + + // ParamBundleActivationV1 defines the name of the HTTP URL parameter that + // indicates the client wants to include bundle activation in the results + // of the health API. + // Deprecated: Use ParamBundlesActivationV1 instead. + ParamBundleActivationV1 = v1.ParamBundleActivationV1 + + // ParamBundlesActivationV1 defines the name of the HTTP URL parameter that + // indicates the client wants to include bundle activation in the results + // of the health API. + ParamBundlesActivationV1 = v1.ParamBundlesActivationV1 + + // ParamPluginsV1 defines the name of the HTTP URL parameter that + // indicates the client wants to include bundle status in the results + // of the health API. + ParamPluginsV1 = v1.ParamPluginsV1 + + // ParamExcludePluginV1 defines the name of the HTTP URL parameter that + // indicates the client wants to exclude plugin status in the results + // of the health API for the specified plugin(s) + ParamExcludePluginV1 = v1.ParamExcludePluginV1 + + // ParamStrictBuiltinErrors names the HTTP URL parameter that indicates the client + // wants built-in function errors to be treated as fatal. + ParamStrictBuiltinErrors = v1.ParamStrictBuiltinErrors +) + +// BadRequestErr represents an error condition raised if the caller passes +// invalid parameters. +type BadRequestErr = v1.BadRequestErr + +// BadPatchOperationErr returns BadRequestErr indicating the patch operation was +// invalid. +func BadPatchOperationErr(op string) error { + return v1.BadPatchOperationErr(op) +} + +// BadPatchPathErr returns BadRequestErr indicating the patch path was invalid. +func BadPatchPathErr(path string) error { + return v1.BadPatchPathErr(path) +} + +// IsBadRequest returns true if err is a BadRequestErr. +func IsBadRequest(err error) bool { + return v1.IsBadRequest(err) +} diff --git a/third_party/opa/server/writer/doc.go b/third_party/opa/server/writer/doc.go new file mode 100644 index 000000000000..245c404fe258 --- /dev/null +++ b/third_party/opa/server/writer/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package writer diff --git a/third_party/opa/server/writer/writer.go b/third_party/opa/server/writer/writer.go new file mode 100644 index 000000000000..2dc464ec47eb --- /dev/null +++ b/third_party/opa/server/writer/writer.go @@ -0,0 +1,56 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package writer contains utilities for writing responses in the server. +package writer + +import ( + "net/http" + + "github.com/open-policy-agent/opa/v1/server/types" + v1 "github.com/open-policy-agent/opa/v1/server/writer" +) + +// HTTPStatus is used to set a specific status code +// Adapted from https://stackoverflow.com/questions/27711154/what-response-code-to-return-on-a-non-supported-http-method-on-rest +func HTTPStatus(code int) http.HandlerFunc { + return v1.HTTPStatus(code) +} + +// ErrorAuto writes a response with status and code set automatically based on +// the type of err. +func ErrorAuto(w http.ResponseWriter, err error) { + v1.ErrorAuto(w, err) +} + +// ErrorString writes a response with specified status, code, and message set to +// the err's string representation. +func ErrorString(w http.ResponseWriter, status int, code string, err error) { + v1.ErrorString(w, status, code, err) +} + +// Error writes a response with specified status and error response. +func Error(w http.ResponseWriter, status int, err *types.ErrorV1) { + v1.Error(w, status, err) +} + +// JSON writes a response with the specified status code and object. The object +// will be JSON serialized. +// Deprecated: This method is problematic when using a non-200 status `code`: if +// encoding the payload fails, it'll print "superfluous call to WriteHeader()" +// logs. +func JSON(w http.ResponseWriter, code int, v any, pretty bool) { + v1.JSON(w, code, v, pretty) +} + +// JSONOK is a helper for status "200 OK" responses +func JSONOK(w http.ResponseWriter, v any, pretty bool) { + v1.JSONOK(w, v, pretty) +} + +// Bytes writes a response with the specified status code and bytes. +// Deprecated: Unused in OPA, will be removed in the future. +func Bytes(w http.ResponseWriter, code int, bs []byte) { + v1.Bytes(w, code, bs) +} diff --git a/third_party/opa/storage/disk/config.go b/third_party/opa/storage/disk/config.go new file mode 100644 index 000000000000..94a32c097121 --- /dev/null +++ b/third_party/opa/storage/disk/config.go @@ -0,0 +1,17 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk + +import ( + v1 "github.com/open-policy-agent/opa/v1/storage/disk" +) + +var ErrInvalidPartitionPath = v1.ErrInvalidPartitionPath + +// OptionsFromConfig parses the passed config, extracts the disk storage +// settings, validates it, and returns a *Options struct pointer on success. +func OptionsFromConfig(raw []byte, id string) (*Options, error) { + return v1.OptionsFromConfig(raw, id) +} diff --git a/third_party/opa/storage/disk/disk.go b/third_party/opa/storage/disk/disk.go new file mode 100644 index 000000000000..ed71938068aa --- /dev/null +++ b/third_party/opa/storage/disk/disk.go @@ -0,0 +1,77 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package disk provides disk-based implementation of the storage.Store +// interface. +// +// The disk.Store implementation uses an embedded key-value store to persist +// policies and data. Policy modules are stored as raw byte strings with one +// module per key. Data is mapped to the underlying key-value store with the +// assistance of caller-supplied "partitions". Partitions allow the caller to +// control the portions of the /data namespace that are mapped to individual +// keys. Operations that span multiple keys (e.g., a read against the entirety +// of /data) are more expensive than reads that target a specific key because +// the storage layer has to reconstruct the object from individual key-value +// pairs and page all of the data into memory. By supplying partitions that +// align with lookups in the policies, callers can optimize policy evaluation. +// +// Partitions are specified as a set of storage paths (e.g., {/foo/bar} declares +// a single partition at /foo/bar). Each partition tells the store that values +// under the partition path should be mapped to individual keys. Values that +// fall outside of the partitions are stored at adjacent keys without further +// splitting. For example, given the partition set {/foo/bar}, /foo/bar/abcd and +// /foo/bar/efgh are be written to separate keys. All other values under /foo +// are not split any further (e.g., all values under /foo/baz would be written +// to a single key). Similarly, values that fall outside of partitions are +// stored under individual keys at the root (e.g., the full extent of the value +// at /qux would be stored under one key.) +// There is support for wildcards in partitions: {/foo/*} will cause /foo/bar/abc +// and /foo/buz/def to be written to separate keys. Multiple wildcards are +// supported (/tenants/*/users/*/bindings), and they can also appear at the end +// of a partition (/users/*). +// +// All keys written by the disk.Store implementation are prefixed as follows: +// +// /// +// +// The value represents the version of the schema understood by +// this version of OPA. Currently this is always set to 1. The +// value represents the version of the partition layout +// supplied by the caller. Currently this is always set to 1. Currently, the +// disk.Store implementation only supports _additive_ changes to the +// partitioning layout, i.e., new partitions can be added as long as they do not +// overlap with existing unpartitioned data. The value is either "data" +// or "policies" depending on the value being stored. +// +// The disk.Store implementation attempts to be compatible with the inmem.store +// implementation however there are some minor differences: +// +// * Writes that add partitioned values implicitly create an object hierarchy +// containing the value (e.g., `add /foo/bar/abcd` implicitly creates the +// structure `{"foo": {"bar": {"abcd": ...}}}`). This is unavoidable because of +// how nested /data values are mapped to key-value pairs. +// +// * Trigger events do not include a set of changed paths because the underlying +// key-value store does not make them available. +package disk + +import ( + "context" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/open-policy-agent/opa/logging" + v1 "github.com/open-policy-agent/opa/v1/storage/disk" +) + +// Options contains parameters that configure the disk-based store. +type Options = v1.Options + +// Store provides a disk-based implementation of the storage.Store interface. +type Store = v1.Store + +// New returns a new disk-based store based on the provided options. +func New(ctx context.Context, logger logging.Logger, prom prometheus.Registerer, opts Options) (*Store, error) { + return v1.New(ctx, logger, prom, opts) +} diff --git a/third_party/opa/storage/disk/doc.go b/third_party/opa/storage/disk/doc.go new file mode 100644 index 000000000000..89774340f75a --- /dev/null +++ b/third_party/opa/storage/disk/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package disk diff --git a/third_party/opa/storage/doc.go b/third_party/opa/storage/doc.go new file mode 100644 index 000000000000..c33db689edbe --- /dev/null +++ b/third_party/opa/storage/doc.go @@ -0,0 +1,10 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package storage exposes the policy engine's storage layer. +// +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package storage diff --git a/third_party/opa/storage/errors.go b/third_party/opa/storage/errors.go new file mode 100644 index 000000000000..1403b3a98871 --- /dev/null +++ b/third_party/opa/storage/errors.go @@ -0,0 +1,73 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + v1 "github.com/open-policy-agent/opa/v1/storage" +) + +const ( + // InternalErr indicates an unknown, internal error has occurred. + InternalErr = v1.InternalErr + + // NotFoundErr indicates the path used in the storage operation does not + // locate a document. + NotFoundErr = v1.NotFoundErr + + // WriteConflictErr indicates a write on the path enocuntered a conflicting + // value inside the transaction. + WriteConflictErr = v1.WriteConflictErr + + // InvalidPatchErr indicates an invalid patch/write was issued. The patch + // was rejected. + InvalidPatchErr = v1.InvalidPatchErr + + // InvalidTransactionErr indicates an invalid operation was performed + // inside of the transaction. + InvalidTransactionErr = v1.InvalidTransactionErr + + // TriggersNotSupportedErr indicates the caller attempted to register a + // trigger against a store that does not support them. + TriggersNotSupportedErr = v1.TriggersNotSupportedErr + + // WritesNotSupportedErr indicate the caller attempted to perform a write + // against a store that does not support them. + WritesNotSupportedErr = v1.WritesNotSupportedErr + + // PolicyNotSupportedErr indicate the caller attempted to perform a policy + // management operation against a store that does not support them. + PolicyNotSupportedErr = v1.PolicyNotSupportedErr +) + +// Error is the error type returned by the storage layer. +type Error = v1.Error + +// IsNotFound returns true if this error is a NotFoundErr. +func IsNotFound(err error) bool { + return v1.IsNotFound(err) +} + +// IsWriteConflictError returns true if this error a WriteConflictErr. +func IsWriteConflictError(err error) bool { + return v1.IsWriteConflictError(err) +} + +// IsInvalidPatch returns true if this error is a InvalidPatchErr. +func IsInvalidPatch(err error) bool { + return v1.IsInvalidPatch(err) +} + +// IsInvalidTransaction returns true if this error is a InvalidTransactionErr. +func IsInvalidTransaction(err error) bool { + return v1.IsInvalidTransaction(err) +} + +// IsIndexingNotSupported is a stub for backwards-compatibility. +// +// Deprecated: We no longer return IndexingNotSupported errors, so it is +// unnecessary to check for them. +func IsIndexingNotSupported(err error) bool { + return v1.IsIndexingNotSupported(err) +} diff --git a/third_party/opa/storage/inmem/doc.go b/third_party/opa/storage/inmem/doc.go new file mode 100644 index 000000000000..5f536b66ddd9 --- /dev/null +++ b/third_party/opa/storage/inmem/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package inmem diff --git a/third_party/opa/storage/inmem/inmem.go b/third_party/opa/storage/inmem/inmem.go new file mode 100644 index 000000000000..dabedd4ef804 --- /dev/null +++ b/third_party/opa/storage/inmem/inmem.go @@ -0,0 +1,56 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package inmem implements an in-memory version of the policy engine's storage +// layer. +// +// The in-memory store is used as the default storage layer implementation. The +// in-memory store supports multi-reader/single-writer concurrency with +// rollback. +// +// Callers should assume the in-memory store does not make copies of written +// data. Once data is written to the in-memory store, it should not be modified +// (outside of calling Store.Write). Furthermore, data read from the in-memory +// store should be treated as read-only. +package inmem + +import ( + "io" + + "github.com/open-policy-agent/opa/storage" + v1 "github.com/open-policy-agent/opa/v1/storage/inmem" +) + +// New returns an empty in-memory store. +func New() storage.Store { + return v1.New() +} + +// NewWithOpts returns an empty in-memory store, with extra options passed. +func NewWithOpts(opts ...Opt) storage.Store { + return v1.NewWithOpts(opts...) +} + +// NewFromObject returns a new in-memory store from the supplied data object. +func NewFromObject(data map[string]any) storage.Store { + return v1.NewFromObject(data) +} + +// NewFromObjectWithOpts returns a new in-memory store from the supplied data object, with the +// options passed. +func NewFromObjectWithOpts(data map[string]any, opts ...Opt) storage.Store { + return v1.NewFromObjectWithOpts(data, opts...) +} + +// NewFromReader returns a new in-memory store from a reader that produces a +// JSON serialized object. This function is for test purposes. +func NewFromReader(r io.Reader) storage.Store { + return v1.NewFromReader(r) +} + +// NewFromReader returns a new in-memory store from a reader that produces a +// JSON serialized object, with extra options. This function is for test purposes. +func NewFromReaderWithOpts(r io.Reader, opts ...Opt) storage.Store { + return v1.NewFromReaderWithOpts(r, opts...) +} diff --git a/third_party/opa/storage/inmem/opts.go b/third_party/opa/storage/inmem/opts.go new file mode 100644 index 000000000000..43f03ef27b9c --- /dev/null +++ b/third_party/opa/storage/inmem/opts.go @@ -0,0 +1,35 @@ +package inmem + +import v1 "github.com/open-policy-agent/opa/v1/storage/inmem" + +// An Opt modifies store at instantiation. +type Opt = v1.Opt + +// OptRoundTripOnWrite sets whether incoming objects written to store are +// round-tripped through JSON to ensure they are serializable to JSON. +// +// Callers should disable this if they can guarantee all objects passed to +// Write() are serializable to JSON. Failing to do so may result in undefined +// behavior, including panics. +// +// Usually, when only storing objects in the inmem store that have been read +// via encoding/json, this is safe to disable, and comes with an improvement +// in performance and memory use. +// +// If setting to false, callers should deep-copy any objects passed to Write() +// unless they can guarantee the objects will not be mutated after being written, +// and that mutations happening to the objects after they have been passed into +// Write() don't affect their logic. +func OptRoundTripOnWrite(enabled bool) Opt { + return v1.OptRoundTripOnWrite(enabled) +} + +// OptReturnASTValuesOnRead sets whether data values added to the store should be +// eagerly converted to AST values, which are then returned on read. +// +// When enabled, this feature does not sanity check data before converting it to AST values, +// which may result in panics if the data is not valid. Callers should ensure that passed data +// can be serialized to AST values; otherwise, it's recommended to also enable OptRoundTripOnWrite. +func OptReturnASTValuesOnRead(enabled bool) Opt { + return v1.OptReturnASTValuesOnRead(enabled) +} diff --git a/third_party/opa/storage/inmem/test/doc.go b/third_party/opa/storage/inmem/test/doc.go new file mode 100644 index 000000000000..4e2f40c4786c --- /dev/null +++ b/third_party/opa/storage/inmem/test/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package test diff --git a/third_party/opa/storage/inmem/test/testutil.go b/third_party/opa/storage/inmem/test/testutil.go new file mode 100644 index 000000000000..14cd901428b5 --- /dev/null +++ b/third_party/opa/storage/inmem/test/testutil.go @@ -0,0 +1,22 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package test + +import ( + "github.com/open-policy-agent/opa/storage" + v1 "github.com/open-policy-agent/opa/v1/storage/inmem/test" +) + +// New returns an inmem store with some common options set: opt-out of write +// roundtripping. +func New() storage.Store { + return v1.New() +} + +// NewFromObject returns an inmem store from the passed object, with some +// common options set: opt-out of write roundtripping. +func NewFromObject(x map[string]any) storage.Store { + return v1.NewFromObject(x) +} diff --git a/third_party/opa/storage/interface.go b/third_party/opa/storage/interface.go new file mode 100644 index 000000000000..a21b5575e9a5 --- /dev/null +++ b/third_party/opa/storage/interface.go @@ -0,0 +1,89 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + v1 "github.com/open-policy-agent/opa/v1/storage" +) + +// Transaction defines the interface that identifies a consistent snapshot over +// the policy engine's storage layer. +type Transaction = v1.Transaction + +// Store defines the interface for the storage layer's backend. +type Store = v1.Store + +// MakeDirer defines the interface a Store could realize to override the +// generic MakeDir functionality in storage.MakeDir +type MakeDirer = v1.MakeDirer + +// NonEmptyer allows a store implemention to override NonEmpty()) +type NonEmptyer = v1.NonEmptyer + +// TransactionParams describes a new transaction. +type TransactionParams = v1.TransactionParams + +// Context is a simple container for key/value pairs. +type Context = v1.Context + +// NewContext returns a new context object. +func NewContext() *Context { + return v1.NewContext() +} + +// WriteParams specifies the TransactionParams for a write transaction. +var WriteParams = v1.WriteParams + +// PatchOp is the enumeration of supposed modifications. +type PatchOp = v1.PatchOp + +// Patch supports add, remove, and replace operations. +const ( + AddOp = v1.AddOp + RemoveOp = v1.RemoveOp + ReplaceOp = v1.ReplaceOp +) + +// WritesNotSupported provides a default implementation of the write +// interface which may be used if the backend does not support writes. +type WritesNotSupported = v1.WritesNotSupported + +// Policy defines the interface for policy module storage. +type Policy = v1.Policy + +// PolicyNotSupported provides a default implementation of the policy interface +// which may be used if the backend does not support policy storage. +type PolicyNotSupported = v1.PolicyNotSupported + +// PolicyEvent describes a change to a policy. +type PolicyEvent = v1.PolicyEvent + +// DataEvent describes a change to a base data document. +type DataEvent = v1.DataEvent + +// TriggerEvent describes the changes that caused the trigger to be invoked. +type TriggerEvent = v1.TriggerEvent + +// TriggerConfig contains the trigger registration configuration. +type TriggerConfig = v1.TriggerConfig + +// Trigger defines the interface that stores implement to register for change +// notifications when the store is changed. +type Trigger = v1.Trigger + +// TriggersNotSupported provides default implementations of the Trigger +// interface which may be used if the backend does not support triggers. +type TriggersNotSupported = v1.TriggersNotSupported + +// TriggerHandle defines the interface that can be used to unregister triggers that have +// been registered on a Store. +type TriggerHandle = v1.TriggerHandle + +// Iterator defines the interface that can be used to read files from a directory starting with +// files at the base of the directory, then sub-directories etc. +type Iterator = v1.Iterator + +// Update contains information about a file +type Update = v1.Update diff --git a/third_party/opa/storage/path.go b/third_party/opa/storage/path.go new file mode 100644 index 000000000000..91d4f34f2bc6 --- /dev/null +++ b/third_party/opa/storage/path.go @@ -0,0 +1,34 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + "github.com/open-policy-agent/opa/ast" + v1 "github.com/open-policy-agent/opa/v1/storage" +) + +// Path refers to a document in storage. +type Path = v1.Path + +// ParsePath returns a new path for the given str. +func ParsePath(str string) (path Path, ok bool) { + return v1.ParsePath(str) +} + +// ParsePathEscaped returns a new path for the given escaped str. +func ParsePathEscaped(str string) (path Path, ok bool) { + return v1.ParsePathEscaped(str) +} + +// NewPathForRef returns a new path for the given ref. +func NewPathForRef(ref ast.Ref) (path Path, err error) { + return v1.NewPathForRef(ref) +} + +// MustParsePath returns a new Path for s. If s cannot be parsed, this function +// will panic. This is mostly for test purposes. +func MustParsePath(s string) Path { + return v1.MustParsePath(s) +} diff --git a/third_party/opa/storage/storage.go b/third_party/opa/storage/storage.go new file mode 100644 index 000000000000..d1abc1046d2a --- /dev/null +++ b/third_party/opa/storage/storage.go @@ -0,0 +1,53 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + "context" + + v1 "github.com/open-policy-agent/opa/v1/storage" +) + +// NewTransactionOrDie is a helper function to create a new transaction. If the +// storage layer cannot create a new transaction, this function will panic. This +// function should only be used for tests. +func NewTransactionOrDie(ctx context.Context, store Store, params ...TransactionParams) Transaction { + return v1.NewTransactionOrDie(ctx, store, params...) +} + +// ReadOne is a convenience function to read a single value from the provided Store. It +// will create a new Transaction to perform the read with, and clean up after itself +// should an error occur. +func ReadOne(ctx context.Context, store Store, path Path) (any, error) { + return v1.ReadOne(ctx, store, path) +} + +// WriteOne is a convenience function to write a single value to the provided Store. It +// will create a new Transaction to perform the write with, and clean up after itself +// should an error occur. +func WriteOne(ctx context.Context, store Store, op PatchOp, path Path, value any) error { + return v1.WriteOne(ctx, store, op, path, value) +} + +// MakeDir inserts an empty object at path. If the parent path does not exist, +// MakeDir will create it recursively. +func MakeDir(ctx context.Context, store Store, txn Transaction, path Path) error { + return v1.MakeDir(ctx, store, txn, path) +} + +// Txn is a convenience function that executes f inside a new transaction +// opened on the store. If the function returns an error, the transaction is +// aborted and the error is returned. Otherwise, the transaction is committed +// and the result of the commit is returned. +func Txn(ctx context.Context, store Store, params TransactionParams, f func(Transaction) error) error { + return v1.Txn(ctx, store, params, f) +} + +// NonEmpty returns a function that tests if a path is non-empty. A +// path is non-empty if a Read on the path returns a value or a Read +// on any of the path prefixes returns a non-object value. +func NonEmpty(ctx context.Context, store Store, txn Transaction) func([]string) (bool, error) { + return v1.NonEmpty(ctx, store, txn) +} diff --git a/third_party/opa/test/authz/doc.go b/third_party/opa/test/authz/doc.go new file mode 100644 index 000000000000..80389caf4f44 --- /dev/null +++ b/third_party/opa/test/authz/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package authz diff --git a/third_party/opa/test/authz/testing.go b/third_party/opa/test/authz/testing.go new file mode 100644 index 000000000000..e8ffaf9d81c8 --- /dev/null +++ b/third_party/opa/test/authz/testing.go @@ -0,0 +1,43 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package authz contains unit and benchmark tests for authz use-cases +// The public (non-test) APIs are meant to be used as helpers for +// other tests to build off of. +package authz + +import ( + v1 "github.com/open-policy-agent/opa/v1/test/authz" +) + +// Policy is a test rego policy for a token based authz system +const Policy = v1.Policy + +// AllowQuery is the test query that goes with the Policy +// defined in this package +const AllowQuery = v1.AllowQuery + +// DataSetProfile defines how the test data should be generated +type DataSetProfile = v1.DataSetProfile + +// InputMode defines what type of inputs to generate for testings +type InputMode = v1.InputMode + +// InputMode types supported by GenerateInput +const ( + ForbidIdentity = v1.ForbidIdentity + ForbidPath = v1.ForbidPath + ForbidMethod = v1.ForbidMethod + Allow = v1.Allow +) + +// GenerateInput will use a dataset profile and desired InputMode to generate inputs for testing +func GenerateInput(profile DataSetProfile, mode InputMode) (any, any) { + return v1.GenerateInput(profile, mode) +} + +// GenerateDataset will generate a dataset for the given DatasetProfile +func GenerateDataset(profile DataSetProfile) map[string]any { + return v1.GenerateDataset(profile) +} diff --git a/third_party/opa/test/cases/cases.go b/third_party/opa/test/cases/cases.go new file mode 100644 index 000000000000..516db66e8665 --- /dev/null +++ b/third_party/opa/test/cases/cases.go @@ -0,0 +1,26 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package cases contains utilities for evaluation test cases. +package cases + +import ( + v1 "github.com/open-policy-agent/opa/v1/test/cases" +) + +// Set represents a collection of test cases. +type Set = v1.Set + +// TestCase represents a single test case. +type TestCase = v1.TestCase + +// Load returns a set of built-in test cases. +func Load(path string) (Set, error) { + return v1.Load(path) +} + +// MustLoad returns a set of built-in test cases or panics if an error occurs. +func MustLoad(path string) Set { + return v1.MustLoad(path) +} diff --git a/third_party/opa/test/cases/doc.go b/third_party/opa/test/cases/doc.go new file mode 100644 index 000000000000..170d8ecaf35c --- /dev/null +++ b/third_party/opa/test/cases/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package cases diff --git a/third_party/opa/test/e2e/doc.go b/third_party/opa/test/e2e/doc.go new file mode 100644 index 000000000000..3d936695acb6 --- /dev/null +++ b/third_party/opa/test/e2e/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package e2e diff --git a/third_party/opa/test/e2e/logs/doc.go b/third_party/opa/test/e2e/logs/doc.go new file mode 100644 index 000000000000..947df360135e --- /dev/null +++ b/third_party/opa/test/e2e/logs/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package logs diff --git a/third_party/opa/test/e2e/logs/utils.go b/third_party/opa/test/e2e/logs/utils.go new file mode 100644 index 000000000000..8f28a5e14097 --- /dev/null +++ b/third_party/opa/test/e2e/logs/utils.go @@ -0,0 +1,16 @@ +package logs + +import ( + v1 "github.com/open-policy-agent/opa/v1/test/e2e/logs" +) + +// GeneratePolicy generates a policy for use in Decision Log e2e tests. The +// `ruleCounts` determine how many total rules to generate, and the `ruleHits` +// are the number of them that will be evaluated. This is keyed off of +// the `input.hit` boolean value. +func GeneratePolicy(ruleCounts int, ruleHits int) string { + return v1.GeneratePolicy(ruleCounts, ruleHits) +} + +// TestLogServer implements the decision log endpoint for e2e testing. +type TestLogServer = v1.TestLogServer diff --git a/third_party/opa/test/e2e/testing.go b/third_party/opa/test/e2e/testing.go new file mode 100644 index 000000000000..913208a3c891 --- /dev/null +++ b/third_party/opa/test/e2e/testing.go @@ -0,0 +1,49 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package e2e + +import ( + "context" + "testing" + + "github.com/open-policy-agent/opa/v1/runtime" + v1 "github.com/open-policy-agent/opa/v1/test/e2e" +) + +// NewAPIServerTestParams creates a new set of runtime.Params with enough +// default values filled in to start the server. Options can/should +// be customized for the test case. +func NewAPIServerTestParams() runtime.Params { + return v1.NewAPIServerTestParams() +} + +// TestRuntime holds metadata and provides helper methods +// to interact with the runtime being tested. +type TestRuntime = v1.TestRuntime + +// NewTestRuntime returns a new TestRuntime. +func NewTestRuntime(params runtime.Params) (*TestRuntime, error) { + return v1.NewTestRuntime(params) +} + +// NewTestRuntimeWithOpts returns a new TestRuntime. +func NewTestRuntimeWithOpts(opts TestRuntimeOpts, params runtime.Params) (*TestRuntime, error) { + return v1.NewTestRuntimeWithOpts(opts, params) +} + +// WrapRuntime creates a new TestRuntime by wrapping an existing runtime +func WrapRuntime(ctx context.Context, cancel context.CancelFunc, rt *runtime.Runtime) *TestRuntime { + return v1.WrapRuntime(ctx, cancel, rt) +} + +// TestRuntimeOpts contains parameters for the test runtime. +type TestRuntimeOpts = v1.TestRuntimeOpts + +// WithRuntime invokes f with a new TestRuntime after waiting for server +// readiness. This function can be called inside of each test that requires a +// runtime as opposed to RunTests which can only be called once. +func WithRuntime(t *testing.T, opts TestRuntimeOpts, params runtime.Params, f func(rt *TestRuntime)) { + v1.WithRuntime(t, opts, params, f) +} diff --git a/third_party/opa/tester/doc.go b/third_party/opa/tester/doc.go new file mode 100644 index 000000000000..f7abf25d1e15 --- /dev/null +++ b/third_party/opa/tester/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package tester diff --git a/third_party/opa/tester/reporter.go b/third_party/opa/tester/reporter.go new file mode 100644 index 000000000000..3779fc9b2d14 --- /dev/null +++ b/third_party/opa/tester/reporter.go @@ -0,0 +1,21 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package tester + +import ( + v1 "github.com/open-policy-agent/opa/v1/tester" +) + +// Reporter defines the interface for reporting test results. +type Reporter = v1.Reporter + +// PrettyReporter reports test results in a simple human readable format. +type PrettyReporter = v1.PrettyReporter + +// JSONReporter reports test results as array of JSON objects. +type JSONReporter = v1.JSONReporter + +// JSONCoverageReporter reports coverage as a JSON structure. +type JSONCoverageReporter = v1.JSONCoverageReporter diff --git a/third_party/opa/tester/runner.go b/third_party/opa/tester/runner.go new file mode 100644 index 000000000000..d84681bd22a1 --- /dev/null +++ b/third_party/opa/tester/runner.go @@ -0,0 +1,71 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package tester contains utilities for executing Rego tests. +package tester + +import ( + "context" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/bundle" + "github.com/open-policy-agent/opa/loader" + "github.com/open-policy-agent/opa/storage" + v1 "github.com/open-policy-agent/opa/v1/tester" +) + +// TestPrefix declares the prefix for all test rules. +const TestPrefix = v1.TestPrefix + +// SkipTestPrefix declares the prefix for tests that should be skipped. +const SkipTestPrefix = v1.SkipTestPrefix + +// Run executes all test cases found under files in path. +func Run(ctx context.Context, paths ...string) ([]*Result, error) { + return v1.Run(ctx, paths...) +} + +// RunWithFilter executes all test cases found under files in path. The filter +// will be applied to exclude files that should not be included. +func RunWithFilter(ctx context.Context, _ loader.Filter, paths ...string) ([]*Result, error) { + return v1.Run(ctx, paths...) +} + +// Result represents a single test case result. +type Result = v1.Result + +// BenchmarkOptions defines options specific to benchmarking tests +type BenchmarkOptions = v1.BenchmarkOptions + +// Runner implements simple test discovery and execution. +type Runner = v1.Runner + +// NewRunner returns a new runner. +func NewRunner() *Runner { + return v1.NewRunner().SetDefaultRegoVersion(ast.DefaultRegoVersion) +} + +type Builtin = v1.Builtin + +// Load returns modules and an in-memory store for running tests. +func Load(args []string, filter loader.Filter) (map[string]*ast.Module, storage.Store, error) { + return LoadWithRegoVersion(args, filter, ast.DefaultRegoVersion) +} + +// LoadWithRegoVersion returns modules and an in-memory store for running tests. +// Modules are parsed in accordance with the given RegoVersion. +func LoadWithRegoVersion(args []string, filter loader.Filter, regoVersion ast.RegoVersion) (map[string]*ast.Module, storage.Store, error) { + return v1.LoadWithRegoVersion(args, filter, regoVersion) +} + +// LoadBundles will load the given args as bundles, either tarball or directory is OK. +func LoadBundles(args []string, filter loader.Filter) (map[string]*bundle.Bundle, error) { + return LoadBundlesWithRegoVersion(args, filter, ast.DefaultRegoVersion) +} + +// LoadBundlesWithRegoVersion will load the given args as bundles, either tarball or directory is OK. +// Bundles are parsed in accordance with the given RegoVersion. +func LoadBundlesWithRegoVersion(args []string, filter loader.Filter, regoVersion ast.RegoVersion) (map[string]*bundle.Bundle, error) { + return v1.LoadBundlesWithRegoVersion(args, filter, regoVersion) +} diff --git a/third_party/opa/tester/runner_test.go b/third_party/opa/tester/runner_test.go new file mode 100644 index 000000000000..0cdb58f8ce7e --- /dev/null +++ b/third_party/opa/tester/runner_test.go @@ -0,0 +1,171 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package tester + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/storage/inmem" + "github.com/open-policy-agent/opa/util/test" +) + +func TestLoad_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0 module", // default rego-version + module: `package test + +p[x] { + x = "a" +} + +test_p { + p["a"] +}`, + }, + { + note: "import rego.v1", + module: `package test +import rego.v1 + +p contains x if { + x := "a" +} + +test_p if { + "a" in p +}`, + }, + { + note: "v1 module", // NOT default rego-version + module: `package test + +p contains x if { + x := "a" +} + +test_p if { + "a" in p +}`, + expErrs: []string{ + "test.rego:8: rego_parse_error: unexpected identifier token", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.module, + } + + test.WithTempFS(files, func(root string) { + modules, store, err := Load([]string{root}, nil) + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%q\n\nbut got:\n\n%v", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if modules == nil { + t.Fatalf("Expected modules to be non-nil") + } + + if store == nil { + t.Fatalf("Expected store to be non-nil") + } + } + }) + }) + } +} + +// TestRun_DefaultRegoVersion asserts that the internal compiler instantiated by the runner has the correct default rego-version. +func TestRun_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module ast.Module + }{ + { + note: "no v1 violations", + module: ast.Module{ + Package: ast.MustParsePackage(`package test`), + Rules: []*ast.Rule{ + ast.MustParseRule(`p[x] { x = "a" }`), + ast.MustParseRule(`test_p { p["a"] }`), + }, + }, + }, + { + note: "v1 violations", + module: ast.Module{ + Package: ast.MustParsePackage(`package test`), + Imports: ast.MustParseImports(` + import data.foo + import data.bar as foo + `), + Rules: []*ast.Rule{ + ast.MustParseRule(`p[x] { x = "a" }`), + ast.MustParseRule(`test_p { p["a"] }`), + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + + modules := map[string]*ast.Module{ + "test": &tc.module, + } + + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + runner := NewRunner(). + SetStore(store). + SetModules(modules). + SetTimeout(10 * time.Second) + + ch, err := runner.RunTests(ctx, txn) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + var rs []*Result + for r := range ch { + rs = append(rs, r) + } + + if len(rs) != 1 { + t.Fatalf("Expected exactly one result but got: %v", rs) + } + + if rs[0].Fail { + t.Fatalf("Expected test to pass but it failed") + } + }) + } +} diff --git a/third_party/opa/topdown/builtins.go b/third_party/opa/topdown/builtins.go new file mode 100644 index 000000000000..f28c6c795d11 --- /dev/null +++ b/third_party/opa/topdown/builtins.go @@ -0,0 +1,67 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + v1 "github.com/open-policy-agent/opa/v1/topdown" +) + +type ( + // Deprecated: Functional-style builtins are deprecated. Use BuiltinFunc instead. + FunctionalBuiltin1 = v1.FunctionalBuiltin1 //nolint:staticcheck // SA1019: Intentional use of deprecated type. + + // Deprecated: Functional-style builtins are deprecated. Use BuiltinFunc instead. + FunctionalBuiltin2 = v1.FunctionalBuiltin2 //nolint:staticcheck // SA1019: Intentional use of deprecated type. + + // Deprecated: Functional-style builtins are deprecated. Use BuiltinFunc instead. + FunctionalBuiltin3 = v1.FunctionalBuiltin3 //nolint:staticcheck // SA1019: Intentional use of deprecated type. + + // Deprecated: Functional-style builtins are deprecated. Use BuiltinFunc instead. + FunctionalBuiltin4 = v1.FunctionalBuiltin4 //nolint:staticcheck // SA1019: Intentional use of deprecated type. + + // BuiltinContext contains context from the evaluator that may be used by + // built-in functions. + BuiltinContext = v1.BuiltinContext + + // BuiltinFunc defines an interface for implementing built-in functions. + // The built-in function is called with the plugged operands from the call + // (including the output operands.) The implementation should evaluate the + // operands and invoke the iterator for each successful/defined output + // value. + BuiltinFunc = v1.BuiltinFunc +) + +// RegisterBuiltinFunc adds a new built-in function to the evaluation engine. +func RegisterBuiltinFunc(name string, f BuiltinFunc) { + v1.RegisterBuiltinFunc(name, f) +} + +// Deprecated: Functional-style builtins are deprecated. Use RegisterBuiltinFunc instead. +func RegisterFunctionalBuiltin1(name string, fun FunctionalBuiltin1) { + v1.RegisterFunctionalBuiltin1(name, fun) +} + +// Deprecated: Functional-style builtins are deprecated. Use RegisterBuiltinFunc instead. +func RegisterFunctionalBuiltin2(name string, fun FunctionalBuiltin2) { + v1.RegisterFunctionalBuiltin2(name, fun) +} + +// Deprecated: Functional-style builtins are deprecated. Use RegisterBuiltinFunc instead. +func RegisterFunctionalBuiltin3(name string, fun FunctionalBuiltin3) { + v1.RegisterFunctionalBuiltin3(name, fun) +} + +// Deprecated: Functional-style builtins are deprecated. Use RegisterBuiltinFunc instead. +func RegisterFunctionalBuiltin4(name string, fun FunctionalBuiltin4) { + v1.RegisterFunctionalBuiltin4(name, fun) +} + +// GetBuiltin returns a built-in function implementation, nil if no built-in found. +func GetBuiltin(name string) BuiltinFunc { + return v1.GetBuiltin(name) +} + +// Deprecated: The BuiltinEmpty type is no longer needed. Use nil return values instead. +type BuiltinEmpty = v1.Builtin diff --git a/third_party/opa/topdown/builtins/builtins.go b/third_party/opa/topdown/builtins/builtins.go new file mode 100644 index 000000000000..5f605a1722b6 --- /dev/null +++ b/third_party/opa/topdown/builtins/builtins.go @@ -0,0 +1,123 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package builtins contains utilities for implementing built-in functions. +package builtins + +import ( + "math/big" + + "github.com/open-policy-agent/opa/ast" + v1 "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +// Cache defines the built-in cache used by the top-down evaluation. The keys +// must be comparable and should not be of type string. +type Cache = v1.Cache + +// We use an ast.Object for the cached keys/values because a naive +// map[ast.Value]ast.Value will not correctly detect value equality of +// the member keys. +type NDBCache = v1.NDBCache + +// ErrOperand represents an invalid operand has been passed to a built-in +// function. Built-ins should return ErrOperand to indicate a type error has +// occurred. +type ErrOperand = v1.ErrOperand + +// NewOperandErr returns a generic operand error. +func NewOperandErr(pos int, f string, a ...any) error { + return v1.NewOperandErr(pos, f, a...) +} + +// NewOperandTypeErr returns an operand error indicating the operand's type was wrong. +func NewOperandTypeErr(pos int, got ast.Value, expected ...string) error { + return v1.NewOperandTypeErr(pos, got, expected...) +} + +// NewOperandElementErr returns an operand error indicating an element in the +// composite operand was wrong. +func NewOperandElementErr(pos int, composite ast.Value, got ast.Value, expected ...string) error { + return v1.NewOperandElementErr(pos, composite, got, expected...) +} + +// NewOperandEnumErr returns an operand error indicating a value was wrong. +func NewOperandEnumErr(pos int, expected ...string) error { + return v1.NewOperandEnumErr(pos, expected...) +} + +// IntOperand converts x to an int. If the cast fails, a descriptive error is +// returned. +func IntOperand(x ast.Value, pos int) (int, error) { + return v1.IntOperand(x, pos) +} + +// BigIntOperand converts x to a big int. If the cast fails, a descriptive error +// is returned. +func BigIntOperand(x ast.Value, pos int) (*big.Int, error) { + return v1.BigIntOperand(x, pos) +} + +// NumberOperand converts x to a number. If the cast fails, a descriptive error is +// returned. +func NumberOperand(x ast.Value, pos int) (ast.Number, error) { + return v1.NumberOperand(x, pos) +} + +// SetOperand converts x to a set. If the cast fails, a descriptive error is +// returned. +func SetOperand(x ast.Value, pos int) (ast.Set, error) { + return v1.SetOperand(x, pos) +} + +// StringOperand converts x to a string. If the cast fails, a descriptive error is +// returned. +func StringOperand(x ast.Value, pos int) (ast.String, error) { + return v1.StringOperand(x, pos) +} + +// ObjectOperand converts x to an object. If the cast fails, a descriptive +// error is returned. +func ObjectOperand(x ast.Value, pos int) (ast.Object, error) { + return v1.ObjectOperand(x, pos) +} + +// ArrayOperand converts x to an array. If the cast fails, a descriptive +// error is returned. +func ArrayOperand(x ast.Value, pos int) (*ast.Array, error) { + return v1.ArrayOperand(x, pos) +} + +// NumberToFloat converts n to a big float. +func NumberToFloat(n ast.Number) *big.Float { + return v1.NumberToFloat(n) +} + +// FloatToNumber converts f to a number. +func FloatToNumber(f *big.Float) ast.Number { + return v1.FloatToNumber(f) +} + +// NumberToInt converts n to a big int. +// If n cannot be converted to an big int, an error is returned. +func NumberToInt(n ast.Number) (*big.Int, error) { + return v1.NumberToInt(n) +} + +// IntToNumber converts i to a number. +func IntToNumber(i *big.Int) ast.Number { + return v1.IntToNumber(i) +} + +// StringSliceOperand converts x to a []string. If the cast fails, a descriptive error is +// returned. +func StringSliceOperand(a ast.Value, pos int) ([]string, error) { + return v1.StringSliceOperand(a, pos) +} + +// RuneSliceOperand converts x to a []rune. If the cast fails, a descriptive error is +// returned. +func RuneSliceOperand(x ast.Value, pos int) ([]rune, error) { + return v1.RuneSliceOperand(x, pos) +} diff --git a/third_party/opa/topdown/builtins/doc.go b/third_party/opa/topdown/builtins/doc.go new file mode 100644 index 000000000000..1eec53694944 --- /dev/null +++ b/third_party/opa/topdown/builtins/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package builtins diff --git a/third_party/opa/topdown/cache.go b/third_party/opa/topdown/cache.go new file mode 100644 index 000000000000..bb39df03e0cb --- /dev/null +++ b/third_party/opa/topdown/cache.go @@ -0,0 +1,19 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + v1 "github.com/open-policy-agent/opa/v1/topdown" +) + +// VirtualCache defines the interface for a cache that stores the results of +// evaluated virtual documents (rules). +// The cache is a stack of frames, where each frame is a mapping from references +// to values. +type VirtualCache = v1.VirtualCache + +func NewVirtualCache() VirtualCache { + return v1.NewVirtualCache() +} diff --git a/third_party/opa/topdown/cache/cache.go b/third_party/opa/topdown/cache/cache.go new file mode 100644 index 000000000000..e95617b22864 --- /dev/null +++ b/third_party/opa/topdown/cache/cache.go @@ -0,0 +1,64 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package cache defines the inter-query cache interface that can cache data across queries +package cache + +import ( + "context" + + v1 "github.com/open-policy-agent/opa/v1/topdown/cache" +) + +// Config represents the configuration for the inter-query builtin cache. +type Config = v1.Config + +// InterQueryBuiltinValueCacheConfig represents the configuration of the inter-query value cache that built-in functions can utilize. +// MaxNumEntries - max number of cache entries +type InterQueryBuiltinValueCacheConfig = v1.InterQueryBuiltinValueCacheConfig + +// InterQueryBuiltinCacheConfig represents the configuration of the inter-query cache that built-in functions can utilize. +// MaxSizeBytes - max capacity of cache in bytes +// ForcedEvictionThresholdPercentage - capacity usage in percentage after which forced FIFO eviction starts +// StaleEntryEvictionPeriodSeconds - time period between end of previous and start of new stale entry eviction routine +type InterQueryBuiltinCacheConfig = v1.InterQueryBuiltinCacheConfig + +// ParseCachingConfig returns the config for the inter-query cache. +func ParseCachingConfig(raw []byte) (*Config, error) { + return v1.ParseCachingConfig(raw) +} + +// InterQueryCacheValue defines the interface for the data that the inter-query cache holds. +type InterQueryCacheValue = v1.InterQueryCacheValue + +// InterQueryCache defines the interface for the inter-query cache. +type InterQueryCache = v1.InterQueryCache + +// NewInterQueryCache returns a new inter-query cache. +// The cache uses a FIFO eviction policy when it reaches the forced eviction threshold. +// Parameters: +// +// config - to configure the InterQueryCache +func NewInterQueryCache(config *Config) InterQueryCache { + return v1.NewInterQueryCache(config) +} + +// NewInterQueryCacheWithContext returns a new inter-query cache with context. +// The cache uses a combination of FIFO eviction policy when it reaches the forced eviction threshold +// and a periodic cleanup routine to remove stale entries that exceed their expiration time, if specified. +// If configured with a zero stale_entry_eviction_period_seconds value, the stale entry cleanup routine is disabled. +// +// Parameters: +// +// ctx - used to control lifecycle of the stale entry cleanup routine +// config - to configure the InterQueryCache +func NewInterQueryCacheWithContext(ctx context.Context, config *Config) InterQueryCache { + return v1.NewInterQueryCacheWithContext(ctx, config) +} + +type InterQueryValueCache = v1.InterQueryValueCache + +func NewInterQueryValueCache(ctx context.Context, config *Config) InterQueryValueCache { + return v1.NewInterQueryValueCache(ctx, config) +} diff --git a/third_party/opa/topdown/cache/doc.go b/third_party/opa/topdown/cache/doc.go new file mode 100644 index 000000000000..640530c08189 --- /dev/null +++ b/third_party/opa/topdown/cache/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package cache diff --git a/third_party/opa/topdown/cancel.go b/third_party/opa/topdown/cancel.go new file mode 100644 index 000000000000..395a14a80d6e --- /dev/null +++ b/third_party/opa/topdown/cancel.go @@ -0,0 +1,18 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + v1 "github.com/open-policy-agent/opa/v1/topdown" +) + +// Cancel defines the interface for cancelling topdown queries. Cancel +// operations are thread-safe and idempotent. +type Cancel = v1.Cancel + +// NewCancel returns a new Cancel object. +func NewCancel() Cancel { + return v1.NewCancel() +} diff --git a/third_party/opa/topdown/copypropagation/copypropagation.go b/third_party/opa/topdown/copypropagation/copypropagation.go new file mode 100644 index 000000000000..0f26ded0d463 --- /dev/null +++ b/third_party/opa/topdown/copypropagation/copypropagation.go @@ -0,0 +1,34 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package copypropagation + +import ( + "github.com/open-policy-agent/opa/ast" + v1 "github.com/open-policy-agent/opa/v1/topdown/copypropagation" +) + +// CopyPropagator implements a simple copy propagation optimization to remove +// intermediate variables in partial evaluation results. +// +// For example, given the query: input.x > 1 where 'input' is unknown, the +// compiled query would become input.x = a; a > 1 which would remain in the +// partial evaluation result. The CopyPropagator will remove the variable +// assignment so that partial evaluation simply outputs input.x > 1. +// +// In many cases, copy propagation can remove all variables from the result of +// partial evaluation which simplifies evaluation for non-OPA consumers. +// +// In some cases, copy propagation cannot remove all variables. If the output of +// a built-in call is subsequently used as a ref head, the output variable must +// be kept. For example. sort(input, x); x[0] == 1. In this case, copy +// propagation cannot replace x[0] == 1 with sort(input, x)[0] == 1 as this is +// not legal. +type CopyPropagator = v1.CopyPropagator + +// New returns a new CopyPropagator that optimizes queries while preserving vars +// in the livevars set. +func New(livevars ast.VarSet) *CopyPropagator { + return v1.New(livevars) +} diff --git a/third_party/opa/topdown/copypropagation/doc.go b/third_party/opa/topdown/copypropagation/doc.go new file mode 100644 index 000000000000..238ced31ac1d --- /dev/null +++ b/third_party/opa/topdown/copypropagation/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package copypropagation diff --git a/third_party/opa/topdown/doc.go b/third_party/opa/topdown/doc.go new file mode 100644 index 000000000000..a303ef7886eb --- /dev/null +++ b/third_party/opa/topdown/doc.go @@ -0,0 +1,14 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package topdown provides low-level query evaluation support. +// +// The topdown implementation is a modified version of the standard top-down +// evaluation algorithm used in Datalog. References and comprehensions are +// evaluated eagerly while all other terms are evaluated lazily. +// +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package topdown diff --git a/third_party/opa/topdown/errors.go b/third_party/opa/topdown/errors.go new file mode 100644 index 000000000000..47853ec6d108 --- /dev/null +++ b/third_party/opa/topdown/errors.go @@ -0,0 +1,54 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + v1 "github.com/open-policy-agent/opa/v1/topdown" +) + +// Halt is a special error type that built-in function implementations return to indicate +// that policy evaluation should stop immediately. +type Halt = v1.Halt + +// Error is the error type returned by the Eval and Query functions when +// an evaluation error occurs. +type Error = v1.Error + +const ( + + // InternalErr represents an unknown evaluation error. + InternalErr = v1.InternalErr + + // CancelErr indicates the evaluation process was cancelled. + CancelErr = v1.CancelErr + + // ConflictErr indicates a conflict was encountered during evaluation. For + // instance, a conflict occurs if a rule produces multiple, differing values + // for the same key in an object. Conflict errors indicate the policy does + // not account for the data loaded into the policy engine. + ConflictErr = v1.ConflictErr + + // TypeErr indicates evaluation stopped because an expression was applied to + // a value of an inappropriate type. + TypeErr = v1.TypeErr + + // BuiltinErr indicates a built-in function received a semantically invalid + // input or encountered some kind of runtime error, e.g., connection + // timeout, connection refused, etc. + BuiltinErr = v1.BuiltinErr + + // WithMergeErr indicates that the real and replacement data could not be merged. + WithMergeErr = v1.WithMergeErr +) + +// IsError returns true if the err is an Error. +func IsError(err error) bool { + return v1.IsError(err) +} + +// IsCancel returns true if err was caused by cancellation. +func IsCancel(err error) bool { + return v1.IsCancel(err) +} diff --git a/third_party/opa/topdown/graphql.go b/third_party/opa/topdown/graphql.go new file mode 100644 index 000000000000..3729b14daab1 --- /dev/null +++ b/third_party/opa/topdown/graphql.go @@ -0,0 +1,485 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "encoding/json" + "fmt" + "strings" + + gqlast "github.com/vektah/gqlparser/v2/ast" + gqlparser "github.com/vektah/gqlparser/v2/parser" + gqlvalidator "github.com/vektah/gqlparser/v2/validator" + + // Side-effecting import. Triggers GraphQL library's validation rule init() functions. + _ "github.com/vektah/gqlparser/v2/validator/rules" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +// Parses a GraphQL schema, and returns the GraphQL AST for the schema. +func parseSchema(schema string) (*gqlast.SchemaDocument, error) { + // NOTE(philipc): We don't include the "built-in schema defs" from the + // underlying graphql parsing library here, because those definitions + // generate enormous AST blobs. In the future, if there is demand for + // a "full-spec" version of schema ASTs, we may need to provide a + // version of this function that includes the built-in schema + // definitions. + schemaAST, err := gqlparser.ParseSchema(&gqlast.Source{Input: schema}) + if err != nil { + errorParts := strings.SplitN(err.Error(), ":", 4) + msg := strings.TrimLeft(errorParts[3], " ") + return nil, fmt.Errorf("%s in GraphQL string at location %s:%s", msg, errorParts[1], errorParts[2]) + } + return schemaAST, nil +} + +// Parses a GraphQL query, and returns the GraphQL AST for the query. +func parseQuery(query string) (*gqlast.QueryDocument, error) { + queryAST, err := gqlparser.ParseQuery(&gqlast.Source{Input: query}) + if err != nil { + errorParts := strings.SplitN(err.Error(), ":", 4) + msg := strings.TrimLeft(errorParts[3], " ") + return nil, fmt.Errorf("%s in GraphQL string at location %s:%s", msg, errorParts[1], errorParts[2]) + } + return queryAST, nil +} + +// Validates a GraphQL query against a schema, and returns an error. +// In this case, we get a wrappered error list type, and pluck out +// just the first error message in the list. +func validateQuery(schema *gqlast.Schema, query *gqlast.QueryDocument) error { + // Validate the query against the schema, erroring if there's an issue. + err := gqlvalidator.Validate(schema, query) + if err != nil { + // We use strings.TrimSuffix to remove the '.' characters that the library + // authors include on most of their validation errors. This should be safe, + // since variable names in their error messages are usually quoted, and + // this affects only the last character(s) in the string. + // NOTE(philipc): We know the error location will be in the query string, + // because schema validation always happens before this function is called. + errorParts := strings.SplitN(err.Error(), ":", 4) + msg := strings.TrimSuffix(strings.TrimLeft(errorParts[3], " "), ".\n") + return fmt.Errorf("%s in GraphQL query string at location %s:%s", msg, errorParts[1], errorParts[2]) + } + return nil +} + +func getBuiltinSchema() *gqlast.SchemaDocument { + schema, err := gqlparser.ParseSchema(gqlvalidator.Prelude) + if err != nil { + panic(fmt.Errorf("Error in gqlparser Prelude (should be impossible): %w", err)) + } + return schema +} + +// NOTE(philipc): This function expects *validated* schema documents, and will break +// if it is fed arbitrary structures. +func mergeSchemaDocuments(docA *gqlast.SchemaDocument, docB *gqlast.SchemaDocument) *gqlast.SchemaDocument { + ast := &gqlast.SchemaDocument{} + ast.Merge(docA) + ast.Merge(docB) + return ast +} + +// Converts a SchemaDocument into a gqlast.Schema object that can be used for validation. +// It merges in the builtin schema typedefs exactly as gqltop.LoadSchema did internally. +func convertSchema(schemaDoc *gqlast.SchemaDocument) (*gqlast.Schema, error) { + // Merge builtin schema + schema we were provided. + builtinsSchemaDoc := getBuiltinSchema() + mergedSchemaDoc := mergeSchemaDocuments(builtinsSchemaDoc, schemaDoc) + schema, err := gqlvalidator.ValidateSchemaDocument(mergedSchemaDoc) + if err != nil { + return nil, fmt.Errorf("Error in gqlparser SchemaDocument to Schema conversion: %w", err) + } + return schema, nil +} + +// Converts an ast.Object into a gqlast.QueryDocument object. +func objectToQueryDocument(value ast.Object) (*gqlast.QueryDocument, error) { + // Convert ast.Term to any for JSON encoding below. + asJSON, err := ast.JSON(value) + if err != nil { + return nil, err + } + // Marshal to JSON. + bs, err := json.Marshal(asJSON) + if err != nil { + return nil, err + } + // Unmarshal from JSON -> gqlast.QueryDocument. + var result gqlast.QueryDocument + err = json.Unmarshal(bs, &result) + if err != nil { + return nil, err + } + return &result, nil +} + +// Converts an ast.Object into a gqlast.SchemaDocument object. +func objectToSchemaDocument(value ast.Object) (*gqlast.SchemaDocument, error) { + // Convert ast.Term to any for JSON encoding below. + asJSON, err := ast.JSON(value) + if err != nil { + return nil, err + } + // Marshal to JSON. + bs, err := json.Marshal(asJSON) + if err != nil { + return nil, err + } + // Unmarshal from JSON -> gqlast.SchemaDocument. + var result gqlast.SchemaDocument + err = json.Unmarshal(bs, &result) + if err != nil { + return nil, err + } + return &result, nil +} + +// Recursively traverses an AST that has been run through InterfaceToValue, +// and prunes away the fields with null or empty values, and all `Position` +// structs. +// NOTE(philipc): We currently prune away null values to reduce the level +// of clutter in the returned AST objects. In the future, if there is demand +// for ASTs that have a more regular/fixed structure, we may need to provide +// a "raw" version of the AST, where we still prune away the `Position` +// structs, but leave in the null fields. +func pruneIrrelevantGraphQLASTNodes(value ast.Value) ast.Value { + // We iterate over the Value we've been provided, and recurse down + // in the case of complex types, such as Arrays/Objects. + // We are guaranteed to only have to deal with standard JSON types, + // so this is much less ugly than what we'd need for supporting every + // extant ast type! + switch x := value.(type) { + case *ast.Array: + result := ast.NewArray() + // Iterate over the array's elements, and do the following: + // - Drop any Nulls + // - Drop any any empty object/array value (after running the pruner) + for i := range x.Len() { + vTerm := x.Elem(i) + switch v := vTerm.Value.(type) { + case ast.Null: + continue + case *ast.Array: + // Safe, because we knew the type before going to prune it. + va := pruneIrrelevantGraphQLASTNodes(v).(*ast.Array) + if va.Len() > 0 { + result = result.Append(ast.NewTerm(va)) + } + case ast.Object: + // Safe, because we knew the type before going to prune it. + vo := pruneIrrelevantGraphQLASTNodes(v).(ast.Object) + if len(vo.Keys()) > 0 { + result = result.Append(ast.NewTerm(vo)) + } + default: + result = result.Append(vTerm) + } + } + return result + case ast.Object: + result := ast.NewObject() + // Iterate over our object's keys, and do the following: + // - Drop "Position". + // - Drop any key with a Null value. + // - Drop any key with an empty object/array value (after running the pruner) + keys := x.Keys() + for _, k := range keys { + // We drop the "Position" objects because we don't need the + // source-backref/location info they provide for policy rules. + // Note that keys are ast.Strings. + if ast.String("Position").Equal(k.Value) { + continue + } + vTerm := x.Get(k) + switch v := vTerm.Value.(type) { + case ast.Null: + continue + case *ast.Array: + // Safe, because we knew the type before going to prune it. + va := pruneIrrelevantGraphQLASTNodes(v).(*ast.Array) + if va.Len() > 0 { + result.Insert(k, ast.NewTerm(va)) + } + case ast.Object: + // Safe, because we knew the type before going to prune it. + vo := pruneIrrelevantGraphQLASTNodes(v).(ast.Object) + if len(vo.Keys()) > 0 { + result.Insert(k, ast.NewTerm(vo)) + } + default: + result.Insert(k, vTerm) + } + } + return result + default: + return x + } +} + +// Reports errors from parsing/validation. +func builtinGraphQLParse(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + var queryDoc *gqlast.QueryDocument + var schemaDoc *gqlast.SchemaDocument + var err error + + // Parse/translate query if it's a string/object. + switch x := operands[0].Value.(type) { + case ast.String: + queryDoc, err = parseQuery(string(x)) + case ast.Object: + queryDoc, err = objectToQueryDocument(x) + default: + // Error if wrong type. + return builtins.NewOperandTypeErr(0, x, "string", "object") + } + if err != nil { + return err + } + + // Parse/translate schema if it's a string/object. + switch x := operands[1].Value.(type) { + case ast.String: + schemaDoc, err = parseSchema(string(x)) + case ast.Object: + schemaDoc, err = objectToSchemaDocument(x) + default: + // Error if wrong type. + return builtins.NewOperandTypeErr(1, x, "string", "object") + } + if err != nil { + return err + } + + // Transform the ASTs into Objects. + queryASTValue, err := ast.InterfaceToValue(queryDoc) + if err != nil { + return err + } + schemaASTValue, err := ast.InterfaceToValue(schemaDoc) + if err != nil { + return err + } + + // Validate the query against the schema, erroring if there's an issue. + schema, err := convertSchema(schemaDoc) + if err != nil { + return err + } + if err := validateQuery(schema, queryDoc); err != nil { + return err + } + + // Recursively remove irrelevant AST structures. + queryResult := pruneIrrelevantGraphQLASTNodes(queryASTValue.(ast.Object)) + querySchema := pruneIrrelevantGraphQLASTNodes(schemaASTValue.(ast.Object)) + + // Construct return value. + verified := ast.ArrayTerm( + ast.NewTerm(queryResult), + ast.NewTerm(querySchema), + ) + + return iter(verified) +} + +// Returns default value when errors occur. +func builtinGraphQLParseAndVerify(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + var queryDoc *gqlast.QueryDocument + var schemaDoc *gqlast.SchemaDocument + var err error + + unverified := ast.ArrayTerm( + ast.InternedTerm(false), + ast.NewTerm(ast.NewObject()), + ast.NewTerm(ast.NewObject()), + ) + + // Parse/translate query if it's a string/object. + switch x := operands[0].Value.(type) { + case ast.String: + queryDoc, err = parseQuery(string(x)) + case ast.Object: + queryDoc, err = objectToQueryDocument(x) + default: + // Error if wrong type. + return iter(unverified) + } + if err != nil { + return iter(unverified) + } + + // Parse/translate schema if it's a string/object. + switch x := operands[1].Value.(type) { + case ast.String: + schemaDoc, err = parseSchema(string(x)) + case ast.Object: + schemaDoc, err = objectToSchemaDocument(x) + default: + // Error if wrong type. + return iter(unverified) + } + if err != nil { + return iter(unverified) + } + + // Transform the ASTs into Objects. + queryASTValue, err := ast.InterfaceToValue(queryDoc) + if err != nil { + return iter(unverified) + } + schemaASTValue, err := ast.InterfaceToValue(schemaDoc) + if err != nil { + return iter(unverified) + } + + // Validate the query against the schema, erroring if there's an issue. + schema, err := convertSchema(schemaDoc) + if err != nil { + return iter(unverified) + } + if err := validateQuery(schema, queryDoc); err != nil { + return iter(unverified) + } + + // Recursively remove irrelevant AST structures. + queryResult := pruneIrrelevantGraphQLASTNodes(queryASTValue.(ast.Object)) + querySchema := pruneIrrelevantGraphQLASTNodes(schemaASTValue.(ast.Object)) + + // Construct return value. + verified := ast.ArrayTerm( + ast.InternedTerm(true), + ast.NewTerm(queryResult), + ast.NewTerm(querySchema), + ) + + return iter(verified) +} + +func builtinGraphQLParseQuery(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + raw, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Get the highly-nested AST struct, along with any errors generated. + query, err := parseQuery(string(raw)) + if err != nil { + return err + } + + // Transform the AST into an Object. + value, err := ast.InterfaceToValue(query) + if err != nil { + return err + } + + // Recursively remove irrelevant AST structures. + result := pruneIrrelevantGraphQLASTNodes(value.(ast.Object)) + + return iter(ast.NewTerm(result)) +} + +func builtinGraphQLParseSchema(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + raw, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Get the highly-nested AST struct, along with any errors generated. + schema, err := parseSchema(string(raw)) + if err != nil { + return err + } + + // Transform the AST into an Object. + value, err := ast.InterfaceToValue(schema) + if err != nil { + return err + } + + // Recursively remove irrelevant AST structures. + result := pruneIrrelevantGraphQLASTNodes(value.(ast.Object)) + + return iter(ast.NewTerm(result)) +} + +func builtinGraphQLIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + var queryDoc *gqlast.QueryDocument + var schemaDoc *gqlast.SchemaDocument + var err error + + switch x := operands[0].Value.(type) { + case ast.String: + queryDoc, err = parseQuery(string(x)) + case ast.Object: + queryDoc, err = objectToQueryDocument(x) + default: + // Error if wrong type. + return iter(ast.InternedTerm(false)) + } + if err != nil { + return iter(ast.InternedTerm(false)) + } + + switch x := operands[1].Value.(type) { + case ast.String: + schemaDoc, err = parseSchema(string(x)) + case ast.Object: + schemaDoc, err = objectToSchemaDocument(x) + default: + // Error if wrong type. + return iter(ast.InternedTerm(false)) + } + if err != nil { + return iter(ast.InternedTerm(false)) + } + + // Validate the query against the schema, erroring if there's an issue. + schema, err := convertSchema(schemaDoc) + if err != nil { + return iter(ast.InternedTerm(false)) + } + if err := validateQuery(schema, queryDoc); err != nil { + return iter(ast.InternedTerm(false)) + } + + // If we got this far, the GraphQL query passed validation. + return iter(ast.InternedTerm(true)) +} + +func builtinGraphQLSchemaIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + var schemaDoc *gqlast.SchemaDocument + var err error + + switch x := operands[0].Value.(type) { + case ast.String: + schemaDoc, err = parseSchema(string(x)) + case ast.Object: + schemaDoc, err = objectToSchemaDocument(x) + default: + // Error if wrong type. + return iter(ast.InternedTerm(false)) + } + if err != nil { + return iter(ast.InternedTerm(false)) + } + + // Validate the schema, this determines the result + _, err = convertSchema(schemaDoc) + return iter(ast.InternedTerm(err == nil)) +} + +func init() { + RegisterBuiltinFunc(ast.GraphQLParse.Name, builtinGraphQLParse) + RegisterBuiltinFunc(ast.GraphQLParseAndVerify.Name, builtinGraphQLParseAndVerify) + RegisterBuiltinFunc(ast.GraphQLParseQuery.Name, builtinGraphQLParseQuery) + RegisterBuiltinFunc(ast.GraphQLParseSchema.Name, builtinGraphQLParseSchema) + RegisterBuiltinFunc(ast.GraphQLIsValid.Name, builtinGraphQLIsValid) + RegisterBuiltinFunc(ast.GraphQLSchemaIsValid.Name, builtinGraphQLSchemaIsValid) +} diff --git a/third_party/opa/topdown/http.go b/third_party/opa/topdown/http.go new file mode 100644 index 000000000000..693ea4048c41 --- /dev/null +++ b/third_party/opa/topdown/http.go @@ -0,0 +1,17 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + v1 "github.com/open-policy-agent/opa/v1/topdown" +) + +const ( + // HTTPSendInternalErr represents a runtime evaluation error. + HTTPSendInternalErr = v1.HTTPSendInternalErr + + // HTTPSendNetworkErr represents a network error. + HTTPSendNetworkErr = v1.HTTPSendNetworkErr +) diff --git a/third_party/opa/topdown/instrumentation.go b/third_party/opa/topdown/instrumentation.go new file mode 100644 index 000000000000..845f8da612d8 --- /dev/null +++ b/third_party/opa/topdown/instrumentation.go @@ -0,0 +1,21 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/v1/metrics" + v1 "github.com/open-policy-agent/opa/v1/topdown" +) + +// Instrumentation implements helper functions to instrument query evaluation +// to diagnose performance issues. Instrumentation may be expensive in some +// cases, so it is disabled by default. +type Instrumentation = v1.Instrumentation + +// NewInstrumentation returns a new Instrumentation object. Performance +// diagnostics recorded on this Instrumentation object will stored in m. +func NewInstrumentation(m metrics.Metrics) *Instrumentation { + return v1.NewInstrumentation(m) +} diff --git a/third_party/opa/topdown/lineage/doc.go b/third_party/opa/topdown/lineage/doc.go new file mode 100644 index 000000000000..5a463b697da1 --- /dev/null +++ b/third_party/opa/topdown/lineage/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package lineage diff --git a/third_party/opa/topdown/lineage/lineage.go b/third_party/opa/topdown/lineage/lineage.go new file mode 100644 index 000000000000..160c6a3aff49 --- /dev/null +++ b/third_party/opa/topdown/lineage/lineage.go @@ -0,0 +1,39 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package lineage + +import ( + "github.com/open-policy-agent/opa/topdown" + v1 "github.com/open-policy-agent/opa/v1/topdown/lineage" +) + +// Debug contains everything in the log. +func Debug(trace []*topdown.Event) []*topdown.Event { + return v1.Debug(trace) +} + +// Full returns a filtered trace that contains everything except Unify ops +func Full(trace []*topdown.Event) (result []*topdown.Event) { + return v1.Full(trace) +} + +// Notes returns a filtered trace that contains Note events and context to +// understand where the Note was emitted. +func Notes(trace []*topdown.Event) []*topdown.Event { + return v1.Notes(trace) +} + +// Fails returns a filtered trace that contains Fail events and context to +// understand where the Fail occurred. +func Fails(trace []*topdown.Event) []*topdown.Event { + return v1.Fails(trace) +} + +// Filter will filter a given trace using the specified filter function. The +// filtering function should return true for events that should be kept, false +// for events that should be filtered out. +func Filter(trace []*topdown.Event, filter func(*topdown.Event) bool) (result []*topdown.Event) { + return v1.Filter(trace, filter) +} diff --git a/third_party/opa/topdown/print.go b/third_party/opa/topdown/print.go new file mode 100644 index 000000000000..5eacd180d99c --- /dev/null +++ b/third_party/opa/topdown/print.go @@ -0,0 +1,16 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "io" + + "github.com/open-policy-agent/opa/topdown/print" + v1 "github.com/open-policy-agent/opa/v1/topdown" +) + +func NewPrintHook(w io.Writer) print.Hook { + return v1.NewPrintHook(w) +} diff --git a/third_party/opa/topdown/print/doc.go b/third_party/opa/topdown/print/doc.go new file mode 100644 index 000000000000..c2ee0eca7fd4 --- /dev/null +++ b/third_party/opa/topdown/print/doc.go @@ -0,0 +1,8 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package print diff --git a/third_party/opa/topdown/print/print.go b/third_party/opa/topdown/print/print.go new file mode 100644 index 000000000000..66ffbb176f4e --- /dev/null +++ b/third_party/opa/topdown/print/print.go @@ -0,0 +1,14 @@ +package print + +import ( + v1 "github.com/open-policy-agent/opa/v1/topdown/print" +) + +// Context provides the Hook implementation context about the print() call. +type Context = v1.Context + +// Hook defines the interface that callers can implement to receive print +// statement outputs. If the hook returns an error, it will be surfaced if +// strict builtin error checking is enabled (otherwise, it will not halt +// execution.) +type Hook = v1.Hook diff --git a/third_party/opa/topdown/query.go b/third_party/opa/topdown/query.go new file mode 100644 index 000000000000..d24060991f01 --- /dev/null +++ b/third_party/opa/topdown/query.go @@ -0,0 +1,24 @@ +package topdown + +import ( + "github.com/open-policy-agent/opa/v1/ast" + v1 "github.com/open-policy-agent/opa/v1/topdown" +) + +// QueryResultSet represents a collection of results returned by a query. +type QueryResultSet = v1.QueryResultSet + +// QueryResult represents a single result returned by a query. The result +// contains bindings for all variables that appear in the query. +type QueryResult = v1.QueryResult + +// Query provides a configurable interface for performing query evaluation. +type Query = v1.Query + +// Builtin represents a built-in function that queries can call. +type Builtin = v1.Builtin + +// NewQuery returns a new Query object that can be run. +func NewQuery(query ast.Body) *Query { + return v1.NewQuery(query) +} diff --git a/third_party/opa/topdown/trace.go b/third_party/opa/topdown/trace.go new file mode 100644 index 000000000000..4d4cc295e268 --- /dev/null +++ b/third_party/opa/topdown/trace.go @@ -0,0 +1,112 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "io" + + v1 "github.com/open-policy-agent/opa/v1/topdown" +) + +// Op defines the types of tracing events. +type Op = v1.Op + +const ( + // EnterOp is emitted when a new query is about to be evaluated. + EnterOp = v1.EnterOp + + // ExitOp is emitted when a query has evaluated to true. + ExitOp = v1.ExitOp + + // EvalOp is emitted when an expression is about to be evaluated. + EvalOp = v1.EvalOp + + // RedoOp is emitted when an expression, rule, or query is being re-evaluated. + RedoOp = v1.RedoOp + + // SaveOp is emitted when an expression is saved instead of evaluated + // during partial evaluation. + SaveOp = v1.SaveOp + + // FailOp is emitted when an expression evaluates to false. + FailOp = v1.FailOp + + // DuplicateOp is emitted when a query has produced a duplicate value. The search + // will stop at the point where the duplicate was emitted and backtrack. + DuplicateOp = v1.DuplicateOp + + // NoteOp is emitted when an expression invokes a tracing built-in function. + NoteOp = v1.NoteOp + + // IndexOp is emitted during an expression evaluation to represent lookup + // matches. + IndexOp = v1.IndexOp + + // WasmOp is emitted when resolving a ref using an external + // Resolver. + WasmOp = v1.WasmOp + + // 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 = v1.UnifyOp + FailedAssertionOp = v1.FailedAssertionOp +) + +// VarMetadata provides some user facing information about +// a variable in some policy. +type VarMetadata = v1.VarMetadata + +// Event contains state associated with a tracing event. +type Event = v1.Event + +// Tracer defines the interface for tracing in the top-down evaluation engine. +// Deprecated: Use QueryTracer instead. +type Tracer = v1.Tracer + +// QueryTracer defines the interface for tracing in the top-down evaluation engine. +// The implementation can provide additional configuration to modify the tracing +// behavior for query evaluations. +type QueryTracer = v1.QueryTracer + +// TraceConfig defines some common configuration for Tracer implementations +type TraceConfig = v1.TraceConfig + +// WrapLegacyTracer will create a new QueryTracer which wraps an +// older Tracer instance. +func WrapLegacyTracer(tracer Tracer) QueryTracer { + return v1.WrapLegacyTracer(tracer) +} + +// BufferTracer implements the Tracer and QueryTracer interface by +// simply buffering all events received. +type BufferTracer = v1.BufferTracer + +// NewBufferTracer returns a new BufferTracer. +func NewBufferTracer() *BufferTracer { + return v1.NewBufferTracer() +} + +// PrettyTrace pretty prints the trace to the writer. +func PrettyTrace(w io.Writer, trace []*Event) { + v1.PrettyTrace(w, trace) +} + +// PrettyTraceWithLocation prints the trace to the writer and includes location information +func PrettyTraceWithLocation(w io.Writer, trace []*Event) { + v1.PrettyTraceWithLocation(w, trace) +} + +type PrettyTraceOptions = v1.PrettyTraceOptions + +func PrettyTraceWithOpts(w io.Writer, trace []*Event, opts PrettyTraceOptions) { + v1.PrettyTraceWithOpts(w, trace, opts) +} + +type PrettyEventOpts = v1.PrettyEventOpts + +func PrettyEvent(w io.Writer, e *Event, opts PrettyEventOpts) error { + return v1.PrettyEvent(w, e, opts) +} diff --git a/third_party/opa/tracing/doc.go b/third_party/opa/tracing/doc.go new file mode 100644 index 000000000000..161a3d0cee07 --- /dev/null +++ b/third_party/opa/tracing/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package tracing diff --git a/third_party/opa/tracing/tracing.go b/third_party/opa/tracing/tracing.go new file mode 100644 index 000000000000..d43635431f8e --- /dev/null +++ b/third_party/opa/tracing/tracing.go @@ -0,0 +1,45 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package tracing enables dependency-injection at runtime. When used +// together with an underscore-import of `github.com/open-policy-agent/opa/features/tracing`, +// the server and its runtime will emit OpenTelemetry spans to the +// configured sink. +package tracing + +import ( + "net/http" + + v1 "github.com/open-policy-agent/opa/v1/tracing" +) + +// Options are options for the HTTPTracingService, passed along as-is. +type Options = v1.Options + +// NewOptions is a helper method for constructing `tracing.Options` +func NewOptions(opts ...any) Options { + return v1.NewOptions(opts...) +} + +// HTTPTracingService defines how distributed tracing comes in, server- and client-side +type HTTPTracingService = v1.HTTPTracingService + +// RegisterHTTPTracing enables a HTTPTracingService for further use. +func RegisterHTTPTracing(ht HTTPTracingService) { + v1.RegisterHTTPTracing(ht) +} + +// NewTransport returns another http.RoundTripper, instrumented to emit tracing +// spans according to Options. Provided by the HTTPTracingService registered with +// this package via RegisterHTTPTracing. +func NewTransport(tr http.RoundTripper, opts Options) http.RoundTripper { + return v1.NewTransport(tr, opts) +} + +// NewHandler returns another http.Handler, instrumented to emit tracing spans +// according to Options. Provided by the HTTPTracingService registered with +// this package via RegisterHTTPTracing. +func NewHandler(f http.Handler, label string, opts Options) http.Handler { + return v1.NewHandler(f, label, opts) +} diff --git a/third_party/opa/types/decode.go b/third_party/opa/types/decode.go new file mode 100644 index 000000000000..ae04b38ff4e4 --- /dev/null +++ b/third_party/opa/types/decode.go @@ -0,0 +1,14 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package types + +import ( + v1 "github.com/open-policy-agent/opa/v1/types" +) + +// Unmarshal deserializes bs and returns the resulting type. +func Unmarshal(bs []byte) (result Type, err error) { + return v1.Unmarshal(bs) +} diff --git a/third_party/opa/types/doc.go b/third_party/opa/types/doc.go new file mode 100644 index 000000000000..bfa068e66b61 --- /dev/null +++ b/third_party/opa/types/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package types diff --git a/third_party/opa/types/types.go b/third_party/opa/types/types.go new file mode 100644 index 000000000000..0dd428de7fa7 --- /dev/null +++ b/third_party/opa/types/types.go @@ -0,0 +1,200 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package types declares data types for Rego values and helper functions to +// operate on these types. +package types + +import ( + v1 "github.com/open-policy-agent/opa/v1/types" +) + +// Sprint returns the string representation of the type. +func Sprint(x Type) string { + return v1.Sprint(x) +} + +// Type represents a type of a term in the language. +type Type = v1.Type + +// Null represents the null type. +type Null = v1.Null + +// NewNull returns a new Null type. +func NewNull() Null { + return v1.NewNull() +} + +// NamedType represents a type alias with an arbitrary name and description. +// This is useful for generating documentation for built-in functions. +type NamedType = v1.NamedType + +// Named returns the passed type as a named type. +// Named types are only valid at the top level of built-in functions. +// Note that nested named types cause panic. +func Named(name string, t Type) *NamedType { + return v1.Named(name, t) +} + +// Boolean represents the boolean type. +type Boolean = v1.Boolean + +// B represents an instance of the boolean type. +var B = NewBoolean() + +// NewBoolean returns a new Boolean type. +func NewBoolean() Boolean { + return v1.NewBoolean() +} + +// String represents the string type. +type String = v1.String + +// S represents an instance of the string type. +var S = NewString() + +// NewString returns a new String type. +func NewString() String { + return v1.NewString() +} + +// Number represents the number type. +type Number = v1.Number + +// N represents an instance of the number type. +var N = NewNumber() + +// NewNumber returns a new Number type. +func NewNumber() Number { + return v1.NewNumber() +} + +// Array represents the array type. +type Array = v1.Array + +// NewArray returns a new Array type. +func NewArray(static []Type, dynamic Type) *Array { + return v1.NewArray(static, dynamic) +} + +// Set represents the set type. +type Set = v1.Set + +// NewSet returns a new Set type. +func NewSet(of Type) *Set { + return v1.NewSet(of) +} + +// StaticProperty represents a static object property. +type StaticProperty = v1.StaticProperty + +// NewStaticProperty returns a new StaticProperty object. +func NewStaticProperty(key any, value Type) *StaticProperty { + return v1.NewStaticProperty(key, value) +} + +// DynamicProperty represents a dynamic object property. +type DynamicProperty = v1.DynamicProperty + +// NewDynamicProperty returns a new DynamicProperty object. +func NewDynamicProperty(key, value Type) *DynamicProperty { + return v1.NewDynamicProperty(key, value) +} + +// Object represents the object type. +type Object = v1.Object + +// NewObject returns a new Object type. +func NewObject(static []*StaticProperty, dynamic *DynamicProperty) *Object { + return v1.NewObject(static, dynamic) +} + +// Any represents a dynamic type. +type Any = v1.Any + +// A represents the superset of all types. +var A = NewAny() + +// NewAny returns a new Any type. +func NewAny(of ...Type) Any { + return v1.NewAny(of...) +} + +// Function represents a function type. +type Function = v1.Function + +// Args returns an argument list. +func Args(x ...Type) []Type { + return v1.Args(x...) +} + +// Void returns true if the function has no return value. This function returns +// false if x is not a function. +func Void(x Type) bool { + return v1.Void(x) +} + +// Arity returns the number of arguments in the function signature or zero if x +// is not a function. If the type is unknown, this function returns -1. +func Arity(x Type) int { + return v1.Arity(x) +} + +// NewFunction returns a new Function object of the given argument and result types. +func NewFunction(args []Type, result Type) *Function { + return v1.NewFunction(args, result) +} + +// NewVariadicFunction returns a new Function object. This function sets the +// variadic bit on the signature. Non-void variadic functions are not currently +// supported. +func NewVariadicFunction(args []Type, varargs Type, result Type) *Function { + return v1.NewVariadicFunction(args, varargs, result) +} + +// FuncArgs represents the arguments that can be passed to a function. +type FuncArgs = v1.FuncArgs + +// Compare returns -1, 0, 1 based on comparison between a and b. +func Compare(a, b Type) int { + return v1.Compare(a, b) +} + +// Contains returns true if a is a superset or equal to b. +func Contains(a, b Type) bool { + return v1.Contains(a, b) +} + +// Or returns a type that represents the union of a and b. If one type is a +// superset of the other, the superset is returned unchanged. +func Or(a, b Type) Type { + return v1.Or(a, b) +} + +// Select returns a property or item of a. +func Select(a Type, x any) Type { + return v1.Select(a, x) +} + +// Keys returns the type of keys that can be enumerated for a. For arrays, the +// keys are always number types, for objects the keys are always string types, +// and for sets the keys are always the type of the set element. +func Keys(a Type) Type { + return v1.Keys(a) +} + +// Values returns the type of values that can be enumerated for a. +func Values(a Type) Type { + return v1.Values(a) +} + +// Nil returns true if a's type is unknown. +func Nil(a Type) bool { + return v1.Nil(a) +} + +// TypeOf returns the type of the Golang native value. +func TypeOf(x any) Type { + return v1.TypeOf(x) +} diff --git a/third_party/opa/util/backoff.go b/third_party/opa/util/backoff.go new file mode 100644 index 000000000000..11da67d926da --- /dev/null +++ b/third_party/opa/util/backoff.go @@ -0,0 +1,23 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "time" + + v1 "github.com/open-policy-agent/opa/v1/util" +) + +// DefaultBackoff returns a delay with an exponential backoff based on the +// number of retries. +func DefaultBackoff(base, maxNS float64, retries int) time.Duration { + return v1.DefaultBackoff(base, maxNS, retries) +} + +// Backoff returns a delay with an exponential backoff based on the number of +// retries. Same algorithm used in gRPC. +func Backoff(base, maxNS, jitter, factor float64, retries int) time.Duration { + return v1.Backoff(base, maxNS, jitter, factor, retries) +} diff --git a/third_party/opa/util/close.go b/third_party/opa/util/close.go new file mode 100644 index 000000000000..7f14cf0700c7 --- /dev/null +++ b/third_party/opa/util/close.go @@ -0,0 +1,18 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "net/http" + + v1 "github.com/open-policy-agent/opa/v1/util" +) + +// Close reads the remaining bytes from the response and then closes it to +// ensure that the connection is freed. If the body is not read and closed, a +// leak can occur. +func Close(resp *http.Response) { + v1.Close(resp) +} diff --git a/third_party/opa/util/compare.go b/third_party/opa/util/compare.go new file mode 100644 index 000000000000..e3ce2475fc1d --- /dev/null +++ b/third_party/opa/util/compare.go @@ -0,0 +1,19 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + v1 "github.com/open-policy-agent/opa/v1/util" +) + +// Compare returns 0 if a equals b, -1 if a is less than b, and 1 if b is than a. +// +// For comparison between values of different types, the following ordering is used: +// nil < bool < int, float64 < string < []any < map[string]any. Slices and maps +// are compared recursively. If one slice or map is a subset of the other slice or map +// it is considered "less than". Nil is always equal to nil. +func Compare(a, b any) int { + return v1.Compare(a, b) +} diff --git a/third_party/opa/util/decoding/context.go b/third_party/opa/util/decoding/context.go new file mode 100644 index 000000000000..3aef0e01eb1b --- /dev/null +++ b/third_party/opa/util/decoding/context.go @@ -0,0 +1,24 @@ +package decoding + +import ( + "context" + + v1 "github.com/open-policy-agent/opa/v1/util/decoding" +) + +func AddServerDecodingMaxLen(ctx context.Context, maxLen int64) context.Context { + return v1.AddServerDecodingMaxLen(ctx, maxLen) +} + +func AddServerDecodingGzipMaxLen(ctx context.Context, maxLen int64) context.Context { + return v1.AddServerDecodingGzipMaxLen(ctx, maxLen) +} + +// Used for enforcing max body content limits when dealing with chunked requests. +func GetServerDecodingMaxLen(ctx context.Context) (int64, bool) { + return v1.GetServerDecodingMaxLen(ctx) +} + +func GetServerDecodingGzipMaxLen(ctx context.Context) (int64, bool) { + return v1.GetServerDecodingGzipMaxLen(ctx) +} diff --git a/third_party/opa/util/decoding/doc.go b/third_party/opa/util/decoding/doc.go new file mode 100644 index 000000000000..456d22616fb0 --- /dev/null +++ b/third_party/opa/util/decoding/doc.go @@ -0,0 +1,8 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package decoding diff --git a/third_party/opa/util/doc.go b/third_party/opa/util/doc.go new file mode 100644 index 000000000000..25f5f53bd32a --- /dev/null +++ b/third_party/opa/util/doc.go @@ -0,0 +1,10 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package util provides generic utilities used throughout the policy engine. +// +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package util diff --git a/third_party/opa/util/enumflag.go b/third_party/opa/util/enumflag.go new file mode 100644 index 000000000000..6c28d4df4015 --- /dev/null +++ b/third_party/opa/util/enumflag.go @@ -0,0 +1,19 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + v1 "github.com/open-policy-agent/opa/v1/util" +) + +// EnumFlag implements the pflag.Value interface to provide enumerated command +// line parameter values. +type EnumFlag = v1.EnumFlag + +// NewEnumFlag returns a new EnumFlag that has a defaultValue and vs enumerated +// values. +func NewEnumFlag(defaultValue string, vs []string) *EnumFlag { + return v1.NewEnumFlag(defaultValue, vs) +} diff --git a/third_party/opa/util/graph.go b/third_party/opa/util/graph.go new file mode 100644 index 000000000000..edf59da912c6 --- /dev/null +++ b/third_party/opa/util/graph.go @@ -0,0 +1,34 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import v1 "github.com/open-policy-agent/opa/v1/util" + +// Traversal defines a basic interface to perform traversals. +type Traversal = v1.Traversal + +// Equals should return true if node "u" equals node "v". +type Equals = v1.Equals + +// Iter should return true to indicate stop. +type Iter = v1.Iter + +// DFS performs a depth first traversal calling f for each node starting from u. +// If f returns true, traversal stops and DFS returns true. +func DFS(t Traversal, f Iter, u T) bool { + return v1.DFS(t, f, u) +} + +// BFS performs a breadth first traversal calling f for each node starting from +// u. If f returns true, traversal stops and BFS returns true. +func BFS(t Traversal, f Iter, u T) bool { + return v1.BFS(t, f, u) +} + +// DFSPath returns a path from node a to node z found by performing +// a depth first traversal. If no path is found, an empty slice is returned. +func DFSPath(t Traversal, eq Equals, a, z T) []T { + return v1.DFSPath(t, eq, a, z) +} diff --git a/third_party/opa/util/hashmap.go b/third_party/opa/util/hashmap.go new file mode 100644 index 000000000000..5f030c77b595 --- /dev/null +++ b/third_party/opa/util/hashmap.go @@ -0,0 +1,20 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + v1 "github.com/open-policy-agent/opa/v1/util" +) + +// T is a concise way to refer to T. +type T = v1.T + +// HashMap represents a key/value map. +type HashMap = v1.HashMap + +// NewHashMap returns a new empty HashMap. +func NewHashMap(eq func(T, T) bool, hash func(T) int) *HashMap { + return v1.NewHashMap(eq, hash) +} diff --git a/third_party/opa/util/json.go b/third_party/opa/util/json.go new file mode 100644 index 000000000000..0a9197089980 --- /dev/null +++ b/third_party/opa/util/json.go @@ -0,0 +1,68 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "encoding/json" + "io" + + v1 "github.com/open-policy-agent/opa/v1/util" +) + +// UnmarshalJSON parses the JSON encoded data and stores the result in the value +// pointed to by x. +// +// This function is intended to be used in place of the standard json.Marshal +// function when json.Number is required. +func UnmarshalJSON(bs []byte, x any) error { + return v1.UnmarshalJSON(bs, x) +} + +// NewJSONDecoder returns a new decoder that reads from r. +// +// This function is intended to be used in place of the standard json.NewDecoder +// when json.Number is required. +func NewJSONDecoder(r io.Reader) *json.Decoder { + return v1.NewJSONDecoder(r) +} + +// MustUnmarshalJSON parse the JSON encoded data and returns the result. +// +// If the data cannot be decoded, this function will panic. This function is for +// test purposes. +func MustUnmarshalJSON(bs []byte) any { + return v1.MustUnmarshalJSON(bs) +} + +// MustMarshalJSON returns the JSON encoding of x +// +// If the data cannot be encoded, this function will panic. This function is for +// test purposes. +func MustMarshalJSON(x any) []byte { + return v1.MustMarshalJSON(x) +} + +// RoundTrip encodes to JSON, and decodes the result again. +// +// Thereby, it is converting its argument to the representation expected by +// rego.Input and inmem's Write operations. Works with both references and +// values. +func RoundTrip(x *any) error { + return v1.RoundTrip(x) +} + +// Reference returns a pointer to its argument unless the argument already is +// a pointer. If the argument is **t, or ***t, etc, it will return *t. +// +// Used for preparing Go types (including pointers to structs) into values to be +// put through util.RoundTrip(). +func Reference(x any) *any { + return v1.Reference(x) +} + +// Unmarshal decodes a YAML, JSON or JSON extension value into the specified type. +func Unmarshal(bs []byte, v any) error { + return v1.Unmarshal(bs, v) +} diff --git a/third_party/opa/util/maps.go b/third_party/opa/util/maps.go new file mode 100644 index 000000000000..1a9c71f28db2 --- /dev/null +++ b/third_party/opa/util/maps.go @@ -0,0 +1,8 @@ +package util + +import v1 "github.com/open-policy-agent/opa/v1/util" + +// Values returns a slice of values from any map. Copied from golang.org/x/exp/maps. +func Values[M ~map[K]V, K comparable, V any](m M) []V { + return v1.Values(m) +} diff --git a/third_party/opa/util/queue.go b/third_party/opa/util/queue.go new file mode 100644 index 000000000000..d130a97abeb7 --- /dev/null +++ b/third_party/opa/util/queue.go @@ -0,0 +1,25 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import v1 "github.com/open-policy-agent/opa/v1/util" + +// LIFO represents a simple LIFO queue. +type LIFO = v1.LIFO + +// NewLIFO returns a new LIFO queue containing elements ts starting with the +// left-most argument at the bottom. +func NewLIFO(ts ...T) *LIFO { + return v1.NewLIFO(ts...) +} + +// FIFO represents a simple FIFO queue. +type FIFO = v1.FIFO + +// NewFIFO returns a new FIFO queue containing elements ts starting with the +// left-most argument at the front. +func NewFIFO(ts ...T) *FIFO { + return v1.NewFIFO(ts...) +} diff --git a/third_party/opa/util/read_gzip_body.go b/third_party/opa/util/read_gzip_body.go new file mode 100644 index 000000000000..70b615a4e33b --- /dev/null +++ b/third_party/opa/util/read_gzip_body.go @@ -0,0 +1,17 @@ +package util + +import ( + "net/http" + + v1 "github.com/open-policy-agent/opa/v1/util" +) + +// Note(philipc): Originally taken from server/server.go +// The DecodingLimitHandler handles validating that the gzip payload is within the +// allowed max size limit. Thus, in the event of a forged payload size trailer, +// the worst that can happen is that we waste memory up to the allowed max gzip +// payload size, but not an unbounded amount of memory, as was potentially +// possible before. +func ReadMaybeCompressedBody(r *http.Request) ([]byte, error) { + return v1.ReadMaybeCompressedBody(r) +} diff --git a/third_party/opa/util/test/benchmark.go b/third_party/opa/util/test/benchmark.go new file mode 100644 index 000000000000..18f1a85690cd --- /dev/null +++ b/third_party/opa/util/test/benchmark.go @@ -0,0 +1,58 @@ +package test + +// This file collects some helpers for generating data used in +// benchmarks, +// - topdown/topdown_bench_test.go + +import ( + v1 "github.com/open-policy-agent/opa/v1/util/test" +) + +// PartialObjectBenchmarkCrossModule returns a module with n "bench_test_" prefixed rules +// that each refer to another "cond_bench_" prefixed rule +func PartialObjectBenchmarkCrossModule(n int) []string { + return v1.PartialObjectBenchmarkCrossModule(n) +} + +// ArrayIterationBenchmarkModule returns a module that iterates an array +// with `n` elements +func ArrayIterationBenchmarkModule(n int) string { + return v1.ArrayIterationBenchmarkModule(n) +} + +// SetIterationBenchmarkModule returns a module that iterates a set +// with `n` elements +func SetIterationBenchmarkModule(n int) string { + return v1.SetIterationBenchmarkModule(n) +} + +// ObjectIterationBenchmarkModule returns a module that iterates an object +// with `n` key/val pairs +func ObjectIterationBenchmarkModule(n int) string { + return v1.ObjectIterationBenchmarkModule(n) +} + +// GenerateLargeJSONBenchmarkData returns a map of 100 keys and 100.000 key/value +// pairs. +func GenerateLargeJSONBenchmarkData() map[string]any { + return v1.GenerateLargeJSONBenchmarkData() +} + +// GenerateJSONBenchmarkData returns a map of `k` keys and `v` key/value pairs. +func GenerateJSONBenchmarkData(k, v int) map[string]any { + return v1.GenerateJSONBenchmarkData(k, v) +} + +// GenerateConcurrencyBenchmarkData returns a module and data; the module +// checks some input parameters against that data in a simple API authz +// scheme. +func GenerateConcurrencyBenchmarkData() (string, map[string]any) { + return v1.GenerateConcurrencyBenchmarkData() +} + +// GenerateVirtualDocsBenchmarkData generates a module and input; the +// numTotalRules and numHitRules create as many rules in the module to +// match/miss the returned input. +func GenerateVirtualDocsBenchmarkData(numTotalRules, numHitRules int) (string, map[string]any) { + return v1.GenerateVirtualDocsBenchmarkData(numTotalRules, numHitRules) +} diff --git a/third_party/opa/util/test/ci_skip.go b/third_party/opa/util/test/ci_skip.go new file mode 100644 index 000000000000..3380c1592536 --- /dev/null +++ b/third_party/opa/util/test/ci_skip.go @@ -0,0 +1,10 @@ +//go:build !darwin +// +build !darwin + +package test + +import "testing" + +// Skip will skip this test on pull request CI runs. +// Used for slow test runners on GHA's darwin machines. +func Skip(*testing.T) {} diff --git a/third_party/opa/util/test/ci_skip_darwin.go b/third_party/opa/util/test/ci_skip_darwin.go new file mode 100644 index 000000000000..ffdf489007ee --- /dev/null +++ b/third_party/opa/util/test/ci_skip_darwin.go @@ -0,0 +1,13 @@ +package test + +import ( + "testing" + + v1 "github.com/open-policy-agent/opa/v1/util/test" +) + +// Skip will skip this test on pull request CI runs. +// Used for slow test runners on GHA's darwin machines. +func Skip(t *testing.T) { + v1.Skip(t) +} diff --git a/third_party/opa/util/test/doc.go b/third_party/opa/util/test/doc.go new file mode 100644 index 000000000000..9b965f3f19dd --- /dev/null +++ b/third_party/opa/util/test/doc.go @@ -0,0 +1,10 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package test contains utilities used in the policy engine's test suite. +// +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package test diff --git a/third_party/opa/util/test/tempfs.go b/third_party/opa/util/test/tempfs.go new file mode 100644 index 000000000000..c54a4b461304 --- /dev/null +++ b/third_party/opa/util/test/tempfs.go @@ -0,0 +1,31 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package test + +import ( + "io/fs" + + v1 "github.com/open-policy-agent/opa/v1/util/test" +) + +// WithTempFS creates a temporary directory structure and invokes f with the +// root directory path. +func WithTempFS(files map[string]string, f func(string)) { + v1.WithTempFS(files, f) +} + +// MakeTempFS creates a temporary directory structure for test purposes rooted at root. +// If root is empty, the dir is created in the default system temp location. +// If the creation fails, cleanup is nil and the caller does not have to invoke it. If +// creation succeeds, the caller should invoke cleanup when they are done. +func MakeTempFS(root, prefix string, files map[string]string) (rootDir string, cleanup func(), err error) { + return v1.MakeTempFS(root, prefix, files) +} + +// WithTestFS creates a temporary file system of `files` in memory +// if `inMemoryFS` is true and invokes `f“ with that filesystem +func WithTestFS(files map[string]string, inMemoryFS bool, f func(string, fs.FS)) { + v1.WithTestFS(files, inMemoryFS, f) +} diff --git a/third_party/opa/util/test/tempus.go b/third_party/opa/util/test/tempus.go new file mode 100644 index 000000000000..e557667cb8bf --- /dev/null +++ b/third_party/opa/util/test/tempus.go @@ -0,0 +1,24 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package test + +import ( + "testing" + "time" + + v1 "github.com/open-policy-agent/opa/v1/util/test" +) + +func Eventually(t *testing.T, timeout time.Duration, f func() bool) bool { + t.Helper() + return v1.Eventually(t, timeout, f) +} + +func EventuallyOrFatal(t *testing.T, timeout time.Duration, f func() bool) { + t.Helper() + v1.EventuallyOrFatal(t, timeout, f) +} + +type BlockingWriter = v1.BlockingWriter diff --git a/third_party/opa/util/time.go b/third_party/opa/util/time.go new file mode 100644 index 000000000000..3641974705f8 --- /dev/null +++ b/third_party/opa/util/time.go @@ -0,0 +1,38 @@ +package util + +import ( + "time" + + v1 "github.com/open-policy-agent/opa/v1/util" +) + +// TimerWithCancel exists because of memory leaks when using +// time.After in select statements. Instead, we now manually create timers, +// wait on them, and manually free them. +// +// See this for more details: +// https://www.arangodb.com/2020/09/a-story-of-a-memory-leak-in-go-how-to-properly-use-time-after/ +// +// Note: This issue is fixed in Go 1.23, but this fix helps us until then. +// +// Warning: the cancel cannot be done concurrent to reading, everything should +// work in the same goroutine. +// +// Example: +// +// for retries := 0; true; retries++ { +// +// ...main logic... +// +// timer, cancel := utils.TimerWithCancel(utils.Backoff(retries)) +// select { +// case <-ctx.Done(): +// cancel() +// return ctx.Err() +// case <-timer.C: +// continue +// } +// } +func TimerWithCancel(delay time.Duration) (*time.Timer, func()) { + return v1.TimerWithCancel(delay) +} diff --git a/third_party/opa/util/wait.go b/third_party/opa/util/wait.go new file mode 100644 index 000000000000..235f84a0e1f2 --- /dev/null +++ b/third_party/opa/util/wait.go @@ -0,0 +1,19 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "time" + + v1 "github.com/open-policy-agent/opa/v1/util" +) + +// WaitFunc will call passed function at an interval and return nil +// as soon this function returns true. +// If timeout is reached before the passed in function returns true +// an error is returned. +func WaitFunc(fun func() bool, interval, timeout time.Duration) error { + return v1.WaitFunc(fun, interval, timeout) +} diff --git a/third_party/opa/v1/ast/annotations.go b/third_party/opa/v1/ast/annotations.go new file mode 100644 index 000000000000..3465f0808f7a --- /dev/null +++ b/third_party/opa/v1/ast/annotations.go @@ -0,0 +1,984 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "encoding/json" + "fmt" + "net/url" + "slices" + "strings" + + "github.com/open-policy-agent/opa/internal/deepcopy" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + annotationScopePackage = "package" + annotationScopeRule = "rule" + annotationScopeDocument = "document" + annotationScopeSubpackages = "subpackages" +) + +type ( + // Annotations represents metadata attached to other AST nodes such as rules. + Annotations struct { + Scope string `json:"scope"` + Title string `json:"title,omitempty"` + Entrypoint bool `json:"entrypoint,omitempty"` + Description string `json:"description,omitempty"` + Organizations []string `json:"organizations,omitempty"` + RelatedResources []*RelatedResourceAnnotation `json:"related_resources,omitempty"` + Authors []*AuthorAnnotation `json:"authors,omitempty"` + Schemas []*SchemaAnnotation `json:"schemas,omitempty"` + Custom map[string]any `json:"custom,omitempty"` + Location *Location `json:"location,omitempty"` + + comments []*Comment + node Node + } + + // SchemaAnnotation contains a schema declaration for the document identified by the path. + SchemaAnnotation struct { + Path Ref `json:"path"` + Schema Ref `json:"schema,omitempty"` + Definition *any `json:"definition,omitempty"` + } + + AuthorAnnotation struct { + Name string `json:"name"` + Email string `json:"email,omitempty"` + } + + RelatedResourceAnnotation struct { + Ref url.URL `json:"ref"` + Description string `json:"description,omitempty"` + } + + AnnotationSet struct { + byRule map[*Rule][]*Annotations + byPackage map[int]*Annotations + byPath *annotationTreeNode + modules []*Module // Modules this set was constructed from + } + + annotationTreeNode struct { + Value *Annotations + Children map[Value]*annotationTreeNode // we assume key elements are hashable (vars and strings only!) + } + + AnnotationsRef struct { + Path Ref `json:"path"` // The path of the node the annotations are applied to + Annotations *Annotations `json:"annotations,omitempty"` + Location *Location `json:"location,omitempty"` // The location of the node the annotations are applied to + + node Node // The node the annotations are applied to + } + + AnnotationsRefSet []*AnnotationsRef + + FlatAnnotationsRefSet AnnotationsRefSet +) + +func (a *Annotations) String() string { + bs, _ := a.MarshalJSON() + return string(bs) +} + +// Loc returns the location of this annotation. +func (a *Annotations) Loc() *Location { + return a.Location +} + +// SetLoc updates the location of this annotation. +func (a *Annotations) SetLoc(l *Location) { + a.Location = l +} + +// EndLoc returns the location of this annotation's last comment line. +func (a *Annotations) EndLoc() *Location { + count := len(a.comments) + if count == 0 { + return a.Location + } + return a.comments[count-1].Location +} + +// Compare returns an integer indicating if a is less than, equal to, or greater +// than other. +func (a *Annotations) Compare(other *Annotations) int { + + if a == nil && other == nil { + return 0 + } + + if a == nil { + return -1 + } + + if other == nil { + return 1 + } + + if cmp := scopeCompare(a.Scope, other.Scope); cmp != 0 { + return cmp + } + + if cmp := strings.Compare(a.Title, other.Title); cmp != 0 { + return cmp + } + + if cmp := strings.Compare(a.Description, other.Description); cmp != 0 { + return cmp + } + + if cmp := compareStringLists(a.Organizations, other.Organizations); cmp != 0 { + return cmp + } + + if cmp := compareRelatedResources(a.RelatedResources, other.RelatedResources); cmp != 0 { + return cmp + } + + if cmp := compareAuthors(a.Authors, other.Authors); cmp != 0 { + return cmp + } + + if cmp := compareSchemas(a.Schemas, other.Schemas); cmp != 0 { + return cmp + } + + if a.Entrypoint != other.Entrypoint { + if a.Entrypoint { + return 1 + } + return -1 + } + + if cmp := util.Compare(a.Custom, other.Custom); cmp != 0 { + return cmp + } + + return 0 +} + +// GetTargetPath returns the path of the node these Annotations are applied to (the target) +func (a *Annotations) GetTargetPath() Ref { + switch n := a.node.(type) { + case *Package: + return n.Path + case *Rule: + return n.Ref().GroundPrefix() + default: + return nil + } +} + +func (a *Annotations) MarshalJSON() ([]byte, error) { + if a == nil { + return []byte(`{"scope":""}`), nil + } + + data := map[string]any{ + "scope": a.Scope, + } + + if a.Title != "" { + data["title"] = a.Title + } + + if a.Description != "" { + data["description"] = a.Description + } + + if a.Entrypoint { + data["entrypoint"] = a.Entrypoint + } + + if len(a.Organizations) > 0 { + data["organizations"] = a.Organizations + } + + if len(a.RelatedResources) > 0 { + data["related_resources"] = a.RelatedResources + } + + if len(a.Authors) > 0 { + data["authors"] = a.Authors + } + + if len(a.Schemas) > 0 { + data["schemas"] = a.Schemas + } + + if len(a.Custom) > 0 { + data["custom"] = a.Custom + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Annotations { + if a.Location != nil { + data["location"] = a.Location + } + } + + return json.Marshal(data) +} + +func NewAnnotationsRef(a *Annotations) *AnnotationsRef { + var loc *Location + if a.node != nil { + loc = a.node.Loc() + } + + return &AnnotationsRef{ + Location: loc, + Path: a.GetTargetPath(), + Annotations: a, + node: a.node, + } +} + +func (ar *AnnotationsRef) GetPackage() *Package { + switch n := ar.node.(type) { + case *Package: + return n + case *Rule: + return n.Module.Package + default: + return nil + } +} + +func (ar *AnnotationsRef) GetRule() *Rule { + switch n := ar.node.(type) { + case *Rule: + return n + default: + return nil + } +} + +func (ar *AnnotationsRef) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "path": ar.Path, + } + + if ar.Annotations != nil { + data["annotations"] = ar.Annotations + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.AnnotationsRef { + if ar.Location != nil { + data["location"] = ar.Location + } + + // The location set for the schema ref terms is wrong (always set to + // row 1) and not really useful anyway.. so strip it out before marshalling + for _, schema := range ar.Annotations.Schemas { + if schema.Path != nil { + for _, term := range schema.Path { + term.Location = nil + } + } + } + } + + return json.Marshal(data) +} + +func scopeCompare(s1, s2 string) int { + o1 := scopeOrder(s1) + o2 := scopeOrder(s2) + + if o2 < o1 { + return 1 + } else if o2 > o1 { + return -1 + } + + if s1 < s2 { + return -1 + } else if s2 < s1 { + return 1 + } + + return 0 +} + +func scopeOrder(s string) int { + if s == annotationScopeRule { + return 1 + } + return 0 +} + +func compareAuthors(a, b []*AuthorAnnotation) int { + if len(a) > len(b) { + return 1 + } else if len(a) < len(b) { + return -1 + } + + for i := range a { + if cmp := a[i].Compare(b[i]); cmp != 0 { + return cmp + } + } + + return 0 +} + +func compareRelatedResources(a, b []*RelatedResourceAnnotation) int { + if len(a) > len(b) { + return 1 + } else if len(a) < len(b) { + return -1 + } + + for i := range a { + if cmp := a[i].Compare(b[i]); cmp != 0 { + return cmp + } + } + + return 0 +} + +func compareSchemas(a, b []*SchemaAnnotation) int { + maxLen := min(len(b), len(a)) + + for i := range maxLen { + if cmp := a[i].Compare(b[i]); cmp != 0 { + return cmp + } + } + + if len(a) > len(b) { + return 1 + } else if len(a) < len(b) { + return -1 + } + + return 0 +} + +func compareStringLists(a, b []string) int { + if len(a) > len(b) { + return 1 + } else if len(a) < len(b) { + return -1 + } + + for i := range a { + if cmp := strings.Compare(a[i], b[i]); cmp != 0 { + return cmp + } + } + + return 0 +} + +// Copy returns a deep copy of s. +func (a *Annotations) Copy(node Node) *Annotations { + cpy := *a + + cpy.Organizations = make([]string, len(a.Organizations)) + copy(cpy.Organizations, a.Organizations) + + cpy.RelatedResources = make([]*RelatedResourceAnnotation, len(a.RelatedResources)) + for i := range a.RelatedResources { + cpy.RelatedResources[i] = a.RelatedResources[i].Copy() + } + + cpy.Authors = make([]*AuthorAnnotation, len(a.Authors)) + for i := range a.Authors { + cpy.Authors[i] = a.Authors[i].Copy() + } + + cpy.Schemas = make([]*SchemaAnnotation, len(a.Schemas)) + for i := range a.Schemas { + cpy.Schemas[i] = a.Schemas[i].Copy() + } + + if a.Custom != nil { + cpy.Custom = deepcopy.Map(a.Custom) + } + + cpy.node = node + + return &cpy +} + +// toObject constructs an AST Object from the annotation. +func (a *Annotations) toObject() (*Object, *Error) { + obj := NewObject() + + if a == nil { + return &obj, nil + } + + if len(a.Scope) > 0 { + switch a.Scope { + case annotationScopeDocument: + obj.Insert(InternedTerm("scope"), InternedTerm("document")) + case annotationScopePackage: + obj.Insert(InternedTerm("scope"), InternedTerm("package")) + case annotationScopeRule: + obj.Insert(InternedTerm("scope"), InternedTerm("rule")) + case annotationScopeSubpackages: + obj.Insert(InternedTerm("scope"), InternedTerm("subpackages")) + default: + obj.Insert(InternedTerm("scope"), StringTerm(a.Scope)) + } + } + + if len(a.Title) > 0 { + obj.Insert(InternedTerm("title"), StringTerm(a.Title)) + } + + if a.Entrypoint { + obj.Insert(InternedTerm("entrypoint"), InternedTerm(true)) + } + + if len(a.Description) > 0 { + obj.Insert(InternedTerm("description"), StringTerm(a.Description)) + } + + if len(a.Organizations) > 0 { + orgs := make([]*Term, 0, len(a.Organizations)) + for _, org := range a.Organizations { + orgs = append(orgs, StringTerm(org)) + } + obj.Insert(InternedTerm("organizations"), ArrayTerm(orgs...)) + } + + if len(a.RelatedResources) > 0 { + rrs := make([]*Term, 0, len(a.RelatedResources)) + for _, rr := range a.RelatedResources { + rrObj := NewObject(Item(InternedTerm("ref"), StringTerm(rr.Ref.String()))) + if len(rr.Description) > 0 { + rrObj.Insert(InternedTerm("description"), StringTerm(rr.Description)) + } + rrs = append(rrs, NewTerm(rrObj)) + } + obj.Insert(InternedTerm("related_resources"), ArrayTerm(rrs...)) + } + + if len(a.Authors) > 0 { + as := make([]*Term, 0, len(a.Authors)) + for _, author := range a.Authors { + aObj := NewObject() + if len(author.Name) > 0 { + aObj.Insert(InternedTerm("name"), StringTerm(author.Name)) + } + if len(author.Email) > 0 { + aObj.Insert(InternedTerm("email"), StringTerm(author.Email)) + } + as = append(as, NewTerm(aObj)) + } + obj.Insert(InternedTerm("authors"), ArrayTerm(as...)) + } + + if len(a.Schemas) > 0 { + ss := make([]*Term, 0, len(a.Schemas)) + for _, s := range a.Schemas { + sObj := NewObject() + if len(s.Path) > 0 { + sObj.Insert(InternedTerm("path"), NewTerm(s.Path.toArray())) + } + if len(s.Schema) > 0 { + sObj.Insert(InternedTerm("schema"), NewTerm(s.Schema.toArray())) + } + if s.Definition != nil { + def, err := InterfaceToValue(s.Definition) + if err != nil { + return nil, NewError(CompileErr, a.Location, "invalid definition in schema annotation: %s", err.Error()) + } + sObj.Insert(InternedTerm("definition"), NewTerm(def)) + } + ss = append(ss, NewTerm(sObj)) + } + obj.Insert(InternedTerm("schemas"), ArrayTerm(ss...)) + } + + if len(a.Custom) > 0 { + c, err := InterfaceToValue(a.Custom) + if err != nil { + return nil, NewError(CompileErr, a.Location, "invalid custom annotation %s", err.Error()) + } + obj.Insert(InternedTerm("custom"), NewTerm(c)) + } + + 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().GroundPrefix().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 = slices.Delete(cpy, j, j+1) + } + } +} + +func attachAnnotationsNodes(mod *Module) Errors { + var errs Errors + + // Find first non-annotation statement following each annotation and attach + // the annotation to that statement. + for _, a := range mod.Annotations { + for _, stmt := range mod.stmts { + _, ok := stmt.(*Annotations) + if !ok { + if stmt.Loc().Row > a.Location.Row { + a.node = stmt + break + } + } + } + + if a.Scope == "" { + switch a.node.(type) { + case *Rule: + if a.Entrypoint { + a.Scope = annotationScopeDocument + } else { + a.Scope = annotationScopeRule + } + case *Package: + a.Scope = annotationScopePackage + case *Import: + // Note that this isn't a valid scope, but set here so that the + // validate function called below can print an error message with + // a context that makes sense ("invalid scope: 'import'" instead of + // "invalid scope: '') + a.Scope = "import" + } + } + + if err := validateAnnotationScopeAttachment(a); err != nil { + errs = append(errs, err) + } + + if err := validateAnnotationEntrypointAttachment(a); err != nil { + errs = append(errs, err) + } + } + + return errs +} + +func validateAnnotationScopeAttachment(a *Annotations) *Error { + + switch a.Scope { + case annotationScopeRule, annotationScopeDocument: + if _, ok := a.node.(*Rule); ok { + return nil + } + return newScopeAttachmentErr(a, "rule") + case annotationScopePackage, annotationScopeSubpackages: + if _, ok := a.node.(*Package); ok { + return nil + } + return newScopeAttachmentErr(a, "package") + } + + return NewError(ParseErr, a.Loc(), "invalid annotation scope '%v'. Use one of '%s', '%s', '%s', or '%s'", + a.Scope, annotationScopeRule, annotationScopeDocument, annotationScopePackage, annotationScopeSubpackages) +} + +func validateAnnotationEntrypointAttachment(a *Annotations) *Error { + if a.Entrypoint && !(a.Scope == annotationScopeDocument || a.Scope == annotationScopePackage) { + return NewError( + ParseErr, a.Loc(), "annotation entrypoint applied to non-document or package scope '%v'", a.Scope) + } + return nil +} + +// Copy returns a deep copy of a. +func (a *AuthorAnnotation) Copy() *AuthorAnnotation { + cpy := *a + return &cpy +} + +// Compare returns an integer indicating if s is less than, equal to, or greater +// than other. +func (a *AuthorAnnotation) Compare(other *AuthorAnnotation) int { + if cmp := strings.Compare(a.Name, other.Name); cmp != 0 { + return cmp + } + + if cmp := strings.Compare(a.Email, other.Email); cmp != 0 { + return cmp + } + + return 0 +} + +func (a *AuthorAnnotation) String() string { + if len(a.Email) == 0 { + return a.Name + } else if len(a.Name) == 0 { + return fmt.Sprintf("<%s>", a.Email) + } + return fmt.Sprintf("%s <%s>", a.Name, a.Email) +} + +// Copy returns a deep copy of rr. +func (rr *RelatedResourceAnnotation) Copy() *RelatedResourceAnnotation { + cpy := *rr + return &cpy +} + +// Compare returns an integer indicating if s is less than, equal to, or greater +// than other. +func (rr *RelatedResourceAnnotation) Compare(other *RelatedResourceAnnotation) int { + if cmp := strings.Compare(rr.Description, other.Description); cmp != 0 { + return cmp + } + + if cmp := strings.Compare(rr.Ref.String(), other.Ref.String()); cmp != 0 { + return cmp + } + + return 0 +} + +func (rr *RelatedResourceAnnotation) String() string { + bs, _ := json.Marshal(rr) + return string(bs) +} + +func (rr *RelatedResourceAnnotation) MarshalJSON() ([]byte, error) { + d := map[string]any{ + "ref": rr.Ref.String(), + } + + if len(rr.Description) > 0 { + d["description"] = rr.Description + } + + return json.Marshal(d) +} + +// Copy returns a deep copy of s. +func (s *SchemaAnnotation) Copy() *SchemaAnnotation { + cpy := *s + return &cpy +} + +// Compare returns an integer indicating if s is less than, equal to, or greater +// than other. +func (s *SchemaAnnotation) Compare(other *SchemaAnnotation) int { + if cmp := s.Path.Compare(other.Path); cmp != 0 { + return cmp + } + + if cmp := s.Schema.Compare(other.Schema); cmp != 0 { + return cmp + } + + if s.Definition != nil && other.Definition == nil { + return -1 + } else if s.Definition == nil && other.Definition != nil { + return 1 + } else if s.Definition != nil && other.Definition != nil { + return util.Compare(*s.Definition, *other.Definition) + } + + return 0 +} + +func (s *SchemaAnnotation) String() string { + bs, _ := json.Marshal(s) + return string(bs) +} + +func newAnnotationSet() *AnnotationSet { + return &AnnotationSet{ + byRule: map[*Rule][]*Annotations{}, + byPackage: map[int]*Annotations{}, + byPath: newAnnotationTree(), + } +} + +func BuildAnnotationSet(modules []*Module) (*AnnotationSet, Errors) { + as := newAnnotationSet() + var errs Errors + for _, m := range modules { + for _, a := range m.Annotations { + if err := as.add(a); err != nil { + errs = append(errs, err) + } + } + } + if len(errs) > 0 { + return nil, errs + } + as.modules = modules + return as, nil +} + +// NOTE(philipc): During copy propagation, the underlying Nodes can be +// stripped away from the annotations, leading to nil deref panics. We +// silently ignore these cases for now, as a workaround. +func (as *AnnotationSet) add(a *Annotations) *Error { + switch a.Scope { + case annotationScopeRule: + if rule, ok := a.node.(*Rule); ok { + as.byRule[rule] = append(as.byRule[rule], a) + } + case annotationScopePackage: + if pkg, ok := a.node.(*Package); ok { + hash := pkg.Path.Hash() + if exist, ok := as.byPackage[hash]; ok { + return errAnnotationRedeclared(a, exist.Location) + } + as.byPackage[hash] = a + } + case annotationScopeDocument: + if rule, ok := a.node.(*Rule); ok { + path := rule.Ref().GroundPrefix() + x := as.byPath.get(path) + if x != nil { + return errAnnotationRedeclared(a, x.Value.Location) + } + as.byPath.insert(path, a) + } + case annotationScopeSubpackages: + if pkg, ok := a.node.(*Package); ok { + x := as.byPath.get(pkg.Path) + if x != nil && x.Value != nil { + return errAnnotationRedeclared(a, x.Value.Location) + } + as.byPath.insert(pkg.Path, a) + } + } + return nil +} + +func (as *AnnotationSet) GetRuleScope(r *Rule) []*Annotations { + if as == nil { + return nil + } + return as.byRule[r] +} + +func (as *AnnotationSet) GetSubpackagesScope(path Ref) []*Annotations { + if as == nil { + return nil + } + return as.byPath.ancestors(path) +} + +func (as *AnnotationSet) GetDocumentScope(path Ref) *Annotations { + if as == nil { + return nil + } + if node := as.byPath.get(path); node != nil { + return node.Value + } + return nil +} + +func (as *AnnotationSet) GetPackageScope(pkg *Package) *Annotations { + if as == nil { + return nil + } + return as.byPackage[pkg.Path.Hash()] +} + +// Flatten returns a flattened list view of this AnnotationSet. +// The returned slice is sorted, first by the annotations' target path, then by their target location +func (as *AnnotationSet) Flatten() FlatAnnotationsRefSet { + // This preallocation often won't be optimal, but it's superior to starting with a nil slice. + refs := make([]*AnnotationsRef, 0, len(as.byPath.Children)+len(as.byRule)+len(as.byPackage)) + + refs = as.byPath.flatten(refs) + + for _, a := range as.byPackage { + refs = append(refs, NewAnnotationsRef(a)) + } + + for _, as := range as.byRule { + for _, a := range as { + refs = append(refs, NewAnnotationsRef(a)) + } + } + + // Sort by path, then annotation location, for stable output + slices.SortStableFunc(refs, (*AnnotationsRef).Compare) + + return refs +} + +// Chain returns the chain of annotations leading up to the given rule. +// The returned slice is ordered as follows +// 0. Entries for the given rule, ordered from the METADATA block declared immediately above the rule, to the block declared farthest away (always at least one entry) +// 1. The 'document' scope entry, if any +// 2. The 'package' scope entry, if any +// 3. Entries for the 'subpackages' scope, if any; ordered from the closest package path to the fartest. E.g.: 'do.re.mi', 'do.re', 'do' +// The returned slice is guaranteed to always contain at least one entry, corresponding to the given rule. +func (as *AnnotationSet) Chain(rule *Rule) AnnotationsRefSet { + var refs []*AnnotationsRef + + ruleAnnots := as.GetRuleScope(rule) + + if len(ruleAnnots) >= 1 { + for _, a := range ruleAnnots { + refs = append(refs, NewAnnotationsRef(a)) + } + } else { + // Make sure there is always a leading entry representing the passed rule, even if it has no annotations + refs = append(refs, &AnnotationsRef{ + Location: rule.Location, + Path: rule.Ref().GroundPrefix(), + node: rule, + }) + } + + if len(refs) > 1 { + // Sort by annotation location; chain must start with annotations declared closest to rule, then going outward + slices.SortStableFunc(refs, func(a, b *AnnotationsRef) int { + return -a.Annotations.Location.Compare(b.Annotations.Location) + }) + } + + docAnnots := as.GetDocumentScope(rule.Ref().GroundPrefix()) + if docAnnots != nil { + refs = append(refs, NewAnnotationsRef(docAnnots)) + } + + pkg := rule.Module.Package + pkgAnnots := as.GetPackageScope(pkg) + if pkgAnnots != nil { + refs = append(refs, NewAnnotationsRef(pkgAnnots)) + } + + subPkgAnnots := as.GetSubpackagesScope(pkg.Path) + // We need to reverse the order, as subPkgAnnots ordering will start at the root, + // whereas we want to end at the root. + for i := len(subPkgAnnots) - 1; i >= 0; i-- { + refs = append(refs, NewAnnotationsRef(subPkgAnnots[i])) + } + + return refs +} + +func (ars FlatAnnotationsRefSet) Insert(ar *AnnotationsRef) FlatAnnotationsRefSet { + result := make(FlatAnnotationsRefSet, 0, len(ars)+1) + + // insertion sort, first by path, then location + for i, current := range ars { + if ar.Compare(current) < 0 { + result = append(result, ar) + result = append(result, ars[i:]...) + break + } + result = append(result, current) + } + + if len(result) < len(ars)+1 { + result = append(result, ar) + } + + return result +} + +func newAnnotationTree() *annotationTreeNode { + return &annotationTreeNode{ + Value: nil, + Children: map[Value]*annotationTreeNode{}, + } +} + +func (t *annotationTreeNode) insert(path Ref, value *Annotations) { + node := t + for _, k := range path { + child, ok := node.Children[k.Value] + if !ok { + child = newAnnotationTree() + node.Children[k.Value] = child + } + node = child + } + node.Value = value +} + +func (t *annotationTreeNode) get(path Ref) *annotationTreeNode { + node := t + for _, k := range path { + if node == nil { + return nil + } + child, ok := node.Children[k.Value] + if !ok { + return nil + } + node = child + } + return node +} + +// ancestors returns a slice of annotations in ascending order, starting with the root of ref; e.g.: 'root', 'root.foo', 'root.foo.bar'. +func (t *annotationTreeNode) ancestors(path Ref) (result []*Annotations) { + node := t + for _, k := range path { + if node == nil { + return result + } + child, ok := node.Children[k.Value] + if !ok { + return result + } + if child.Value != nil { + result = append(result, child.Value) + } + node = child + } + return result +} + +func (t *annotationTreeNode) flatten(refs []*AnnotationsRef) []*AnnotationsRef { + if a := t.Value; a != nil { + refs = append(refs, NewAnnotationsRef(a)) + } + for _, c := range t.Children { + refs = c.flatten(refs) + } + return refs +} + +func (ar *AnnotationsRef) Compare(other *AnnotationsRef) int { + if c := ar.Path.Compare(other.Path); c != 0 { + return c + } + + if c := ar.Annotations.Location.Compare(other.Annotations.Location); c != 0 { + return c + } + + return ar.Annotations.Compare(other.Annotations) +} diff --git a/third_party/opa/v1/ast/annotations_test.go b/third_party/opa/v1/ast/annotations_test.go new file mode 100644 index 000000000000..cf8c5a9fd64b --- /dev/null +++ b/third_party/opa/v1/ast/annotations_test.go @@ -0,0 +1,1197 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "encoding/json" + "fmt" + "testing" +) + +func TestEntrypointAnnotationScopeRequirements(t *testing.T) { + tests := []struct { + note string + module string + expectError bool + expectScope string + }{ + { + note: "package scope explicit", + module: `# METADATA +# entrypoint: true +# scope: package +package foo`, + expectError: false, + expectScope: "package", + }, + { + note: "package scope implied", + module: `# METADATA +# entrypoint: true +package foo`, + expectError: false, + expectScope: "package", + }, + { + note: "subpackages scope explicit", + module: `# METADATA +# entrypoint: true +# scope: subpackages +package foo`, + expectError: true, + }, + { + note: "document scope explicit", + module: `package foo +# METADATA +# entrypoint: true +# scope: document +foo := true`, + expectError: false, + expectScope: "document", + }, + { + note: "document scope implied", + module: `package foo +# METADATA +# entrypoint: true +foo := true`, + expectError: false, + expectScope: "document", + }, + { + note: "rule scope explicit", + module: `package foo +# METADATA +# entrypoint: true +# scope: rule +foo := true`, + expectError: true, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + module, err := ParseModuleWithOpts("test.rego", tc.module, ParserOptions{ProcessAnnotation: true}) + if err != nil { + if !tc.expectError { + t.Errorf("unexpected error: %v", err) + } + return + } + if tc.expectError { + t.Fatalf("expected error") + } + if tc.expectScope != module.Annotations[0].Scope { + t.Fatalf("expected scope %q, got %q", tc.expectScope, module.Annotations[0].Scope) + } + }) + } + +} + +// Test of example code in docs/content/annotations.md +func ExampleAnnotationSet_Flatten() { + modules := [][]string{ + { + "foo.rego", `# METADATA +# scope: subpackages +# organizations: +# - Acme Corp. +package foo`}, + { + "mod", `# METADATA +# description: A couple of useful rules +package foo.bar + +# METADATA +# title: My Rule P +p := 7`}, + } + + parsed := make([]*Module, 0, len(modules)) + for _, entry := range modules { + pm, err := ParseModuleWithOpts(entry[0], entry[1], ParserOptions{ProcessAnnotation: true}) + if err != nil { + panic(err) + } + parsed = append(parsed, pm) + } + + as, err := BuildAnnotationSet(parsed) + if err != nil { + panic(err) + } + + flattened := as.Flatten() + for _, entry := range flattened { + fmt.Printf("%v at %v has annotations %v\n", + entry.Path, + entry.Location, + entry.Annotations) + } + + // Output: + // data.foo at foo.rego:5 has annotations {"organizations":["Acme Corp."],"scope":"subpackages"} + // data.foo.bar at mod:3 has annotations {"description":"A couple of useful rules","scope":"package"} + // data.foo.bar.p at mod:7 has annotations {"scope":"rule","title":"My Rule P"} +} + +// Test of example code in docs/content/annotations.md +func ExampleAnnotationSet_Chain() { + modules := [][]string{ + { + "foo.rego", `# METADATA +# scope: subpackages +# organizations: +# - Acme Corp. +package foo`}, + { + "mod", `# METADATA +# description: A couple of useful rules +package foo.bar + +# METADATA +# title: My Rule P +p := 7`}, + } + + parsed := make([]*Module, 0, len(modules)) + for _, entry := range modules { + pm, err := ParseModuleWithOpts(entry[0], entry[1], ParserOptions{ProcessAnnotation: true}) + if err != nil { + panic(err) + } + parsed = append(parsed, pm) + } + + as, err := BuildAnnotationSet(parsed) + if err != nil { + panic(err) + } + + rule := parsed[1].Rules[0] + + flattened := as.Chain(rule) + for _, entry := range flattened { + fmt.Printf("%v at %v has annotations %v\n", + entry.Path, + entry.Location, + entry.Annotations) + } + + // Output: + // data.foo.bar.p at mod:7 has annotations {"scope":"rule","title":"My Rule P"} + // data.foo.bar at mod:3 has annotations {"description":"A couple of useful rules","scope":"package"} + // data.foo at foo.rego:5 has annotations {"organizations":["Acme Corp."],"scope":"subpackages"} +} + +func TestAnnotationSet_Flatten(t *testing.T) { + tests := []struct { + note string + modules map[string]string + expected []AnnotationsRef + }{ + { + note: "no modules", + modules: map[string]string{}, + expected: []AnnotationsRef{}, + }, + { + note: "simple module (all annotation types)", + modules: map[string]string{ + "module": `# METADATA +# title: pkg +# description: pkg +# organizations: +# - pkg +# related_resources: +# - https://pkg +# authors: +# - pkg +# schemas: +# - input: {"type": "boolean"} +# custom: +# pkg: pkg +package test + +# METADATA +# scope: document +# title: doc +# description: doc +# organizations: +# - doc +# related_resources: +# - https://doc +# authors: +# - doc +# schemas: +# - input: {"type": "integer"} +# custom: +# doc: doc + +# METADATA +# title: rule +# description: rule +# organizations: +# - rule +# related_resources: +# - https://rule +# authors: +# - rule +# schemas: +# - input: {"type": "string"} +# custom: +# rule: rule +p = 1`, + }, + expected: []AnnotationsRef{ + { + Path: MustParseRef("data.test"), + Location: &Location{File: "module", Row: 14}, + Annotations: &Annotations{ + Scope: "package", + Title: "pkg", + Description: "pkg", + Organizations: []string{"pkg"}, + RelatedResources: []*RelatedResourceAnnotation{ + { + Ref: mustParseURL("https://pkg"), + }, + }, + Authors: []*AuthorAnnotation{ + { + Name: "pkg", + }, + }, + Schemas: []*SchemaAnnotation{ + schemaAnnotationFromMap("input", map[string]any{ + "type": "boolean", + }), + }, + Custom: map[string]any{ + "pkg": "pkg", + }, + }, + }, + { + Path: MustParseRef("data.test.p"), + Location: &Location{File: "module", Row: 44}, + Annotations: &Annotations{ + Scope: "document", + Title: "doc", + Description: "doc", + Organizations: []string{"doc"}, + RelatedResources: []*RelatedResourceAnnotation{ + { + Ref: mustParseURL("https://doc"), + }, + }, + Authors: []*AuthorAnnotation{ + { + Name: "doc", + }, + }, + Schemas: []*SchemaAnnotation{ + schemaAnnotationFromMap("input", map[string]any{ + "type": "integer", + }), + }, + Custom: map[string]any{ + "doc": "doc", + }, + }, + }, + { + Path: MustParseRef("data.test.p"), + Location: &Location{File: "module", Row: 44}, + Annotations: &Annotations{ + Scope: "rule", + Title: "rule", + Description: "rule", + Organizations: []string{"rule"}, + RelatedResources: []*RelatedResourceAnnotation{ + { + Ref: mustParseURL("https://rule"), + }, + }, + Authors: []*AuthorAnnotation{ + { + Name: "rule", + }, + }, + Schemas: []*SchemaAnnotation{ + schemaAnnotationFromMap("input", map[string]any{ + "type": "string", + }), + }, + Custom: map[string]any{ + "rule": "rule", + }, + }, + }, + }, + }, + { + note: "multiple subpackages", + modules: map[string]string{ + "root": `# METADATA +# scope: subpackages +# title: ROOT +package root`, + "root.foo": `# METADATA +# title: FOO +# scope: subpackages +package root.foo`, + "root.foo.baz": `# METADATA +# title: BAZ +package root.foo.baz`, + "root.bar": `# METADATA +# title: BAR +# scope: subpackages +package root.bar`, + "root.bar.baz": `# METADATA +# title: BAZ +package root.bar.baz`, + "root2": `# METADATA +# scope: subpackages +# title: ROOT2 +package root2`, + }, + expected: []AnnotationsRef{ + { + Path: MustParseRef("data.root"), + Location: &Location{File: "root", Row: 4}, + Annotations: &Annotations{ + Scope: "subpackages", + Title: "ROOT", + }, + }, + { + Path: MustParseRef("data.root.bar"), + Location: &Location{File: "root.bar", Row: 4}, + Annotations: &Annotations{ + Scope: "subpackages", + Title: "BAR", + }, + }, + { + Path: MustParseRef("data.root.bar.baz"), + Location: &Location{File: "root.bar.baz", Row: 3}, + Annotations: &Annotations{ + Scope: "package", + Title: "BAZ", + }, + }, + { + Path: MustParseRef("data.root.foo"), + Location: &Location{File: "root.foo", Row: 4}, + Annotations: &Annotations{ + Scope: "subpackages", + Title: "FOO", + }, + }, + { + Path: MustParseRef("data.root.foo.baz"), + Location: &Location{File: "root.foo.baz", Row: 3}, + Annotations: &Annotations{ + Scope: "package", + Title: "BAZ", + }, + }, + { + Path: MustParseRef("data.root2"), + Location: &Location{File: "root2", Row: 4}, + Annotations: &Annotations{ + Scope: "subpackages", + Title: "ROOT2", + }, + }, + }, + }, + { + note: "overlapping rule paths (same module)", + modules: map[string]string{ + "mod": `package test +import rego.v1 + +# METADATA +# title: P1 +p contains v if {v = 1} + +# METADATA +# title: P2 +p contains v if {v = 2}`, + }, + expected: []AnnotationsRef{ + { + Path: MustParseRef("data.test.p"), + Location: &Location{File: "mod", Row: 6}, + Annotations: &Annotations{ + Scope: "rule", + Title: "P1", + }, + }, + { + Path: MustParseRef("data.test.p"), + Location: &Location{File: "mod", Row: 10}, + Annotations: &Annotations{ + Scope: "rule", + Title: "P2", + }, + }, + }, + }, + { + note: "overlapping rule paths (different modules)", + modules: map[string]string{ + "mod1": `package test +import rego.v1 + +# METADATA +# title: P1 +p contains v if {v = 1}`, + "mod2": `package test +import rego.v1 + +# METADATA +# title: P2 +p contains v if {v = 2}`, + }, + expected: []AnnotationsRef{ + { + Path: MustParseRef("data.test.p"), + Location: &Location{File: "mod1", Row: 6}, + Annotations: &Annotations{ + Scope: "rule", + Title: "P1", + }, + }, + { + Path: MustParseRef("data.test.p"), + Location: &Location{File: "mod2", Row: 6}, + Annotations: &Annotations{ + Scope: "rule", + Title: "P2", + }, + }, + }, + }, + { + note: "overlapping rule paths (different modules, rule head refs)", + modules: map[string]string{ + "mod1": `package test.a +import rego.v1 + +# METADATA +# title: P1 +b.c.p[v] if {v = 1}`, + "mod2": `package test +import rego.v1 + +# METADATA +# title: P2 +a.b.c.p[v] if {v = 2}`, + }, + expected: []AnnotationsRef{ + { + Path: MustParseRef("data.test.a.b.c.p"), + Location: &Location{File: "mod1", Row: 6}, + Annotations: &Annotations{ + Scope: "rule", + Title: "P1", + }, + }, + { + Path: MustParseRef("data.test.a.b.c.p"), + Location: &Location{File: "mod2", Row: 6}, + Annotations: &Annotations{ + Scope: "rule", + Title: "P2", + }, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + compiler := MustCompileModulesWithOpts(tc.modules, + CompileOpts{ParserOptions: ParserOptions{ProcessAnnotation: true}}) + + as := compiler.GetAnnotationSet() + if as == nil { + t.Fatalf("Expected compiled AnnotationSet, got nil") + } + + flattened := as.Flatten() + + if len(flattened) != len(tc.expected) { + t.Fatalf("flattened AnnotationSet\n%v\ndoesn't match expected\n%v", + toJSON(flattened), toJSON(tc.expected)) + } + + for i, expected := range tc.expected { + a := flattened[i] + if !expected.Path.Equal(a.Path) { + t.Fatalf("path of AnnotationRef at %d '%v' doesn't match expected '%v'", + i, a.Path, expected.Path) + } + if expected.Location.File != a.Location.File || expected.Location.Row != a.Location.Row { + t.Fatalf("location of AnnotationRef at %d '%v' doesn't match expected '%v'", + i, a.Location, expected.Location) + } + if expected.Annotations.Compare(a.Annotations) != 0 { + t.Fatalf("annotations of AnnotationRef at %d\n%v\ndoesn't match expected\n%v", + i, a.Annotations, expected.Annotations) + } + } + }) + } +} + +func TestAnnotationSet_Chain(t *testing.T) { + tests := []struct { + note string + modules map[string]string + moduleToAnalyze string + ruleOnLineToAnalyze int + expected []AnnotationsRef + }{ + { + note: "simple module (all annotation types)", + modules: map[string]string{ + "module": `# METADATA +# title: pkg +# description: pkg +# organizations: +# - pkg +# related_resources: +# - https://pkg +# authors: +# - pkg +# schemas: +# - input.foo: {"type": "boolean"} +# custom: +# pkg: pkg +package test + +# METADATA +# scope: document +# title: doc +# description: doc +# organizations: +# - doc +# related_resources: +# - https://doc +# authors: +# - doc +# schemas: +# - input.bar: {"type": "integer"} +# custom: +# doc: doc + +# METADATA +# title: rule +# description: rule +# organizations: +# - rule +# related_resources: +# - https://rule +# authors: +# - rule +# schemas: +# - input.baz: {"type": "string"} +# custom: +# rule: rule +p = 1`, + }, + moduleToAnalyze: "module", + ruleOnLineToAnalyze: 44, + expected: []AnnotationsRef{ + { // Rule annotation is always first + Path: MustParseRef("data.test.p"), + Location: &Location{File: "module", Row: 44}, + Annotations: &Annotations{ + Scope: "rule", + Title: "rule", + Description: "rule", + Organizations: []string{"rule"}, + RelatedResources: []*RelatedResourceAnnotation{ + { + Ref: mustParseURL("https://rule"), + }, + }, + Authors: []*AuthorAnnotation{ + { + Name: "rule", + }, + }, + Schemas: []*SchemaAnnotation{ + schemaAnnotationFromMap("input.baz", map[string]any{ + "type": "string", + }), + }, + Custom: map[string]any{ + "rule": "rule", + }, + }, + }, + { + Path: MustParseRef("data.test.p"), + Location: &Location{File: "module", Row: 44}, + Annotations: &Annotations{ + Scope: "document", + Title: "doc", + Description: "doc", + Organizations: []string{"doc"}, + RelatedResources: []*RelatedResourceAnnotation{ + { + Ref: mustParseURL("https://doc"), + }, + }, + Authors: []*AuthorAnnotation{ + { + Name: "doc", + }, + }, + Schemas: []*SchemaAnnotation{ + schemaAnnotationFromMap("input.bar", map[string]any{ + "type": "integer", + }), + }, + Custom: map[string]any{ + "doc": "doc", + }, + }, + }, + { + Path: MustParseRef("data.test"), + Location: &Location{File: "module", Row: 14}, + Annotations: &Annotations{ + Scope: "package", + Title: "pkg", + Description: "pkg", + Organizations: []string{"pkg"}, + RelatedResources: []*RelatedResourceAnnotation{ + { + Ref: mustParseURL("https://pkg"), + }, + }, + Authors: []*AuthorAnnotation{ + { + Name: "pkg", + }, + }, + Schemas: []*SchemaAnnotation{ + schemaAnnotationFromMap("input.foo", map[string]any{ + "type": "boolean", + }), + }, + Custom: map[string]any{ + "pkg": "pkg", + }, + }, + }, + }, + }, + { + note: "no annotations on rule", + modules: map[string]string{ + "module": `# METADATA +# title: pkg +# description: pkg +package test + +# METADATA +# scope: document +# title: doc +# description: doc + +p = 1`, + }, + moduleToAnalyze: "module", + ruleOnLineToAnalyze: 11, + expected: []AnnotationsRef{ + { // Rule entry is always first, even if no annotations are present + Path: MustParseRef("data.test.p"), + Location: &Location{File: "module", Row: 11}, + Annotations: nil, + }, + { + Path: MustParseRef("data.test.p"), + Location: &Location{File: "module", Row: 11}, + Annotations: &Annotations{ + Scope: "document", + Title: "doc", + Description: "doc", + }, + }, + + { + Path: MustParseRef("data.test"), + Location: &Location{File: "module", Row: 4}, + Annotations: &Annotations{ + Scope: "package", + Title: "pkg", + Description: "pkg", + }, + }, + }, + }, + { + note: "multiple subpackages", + modules: map[string]string{ + "root": `# METADATA +# scope: subpackages +# title: ROOT +package root`, + "root.foo": `# METADATA +# title: FOO +# scope: subpackages +package root.foo`, + "root.foo.bar": `# METADATA +# scope: subpackages +# description: subpackages scope applied to rule in other module +# title: BAR-sub + +# METADATA +# title: BAR-other +# description: This metadata is on the path of the queried rule, and should show up in the result even though it's in a different module. +package root.foo.bar + +# METADATA +# scope: document +# description: document scope applied to rule in other module +# title: P-doc +p = 1`, + "rule": `package root.foo.bar + +# METADATA +# title: P +p = 1`, + }, + moduleToAnalyze: "rule", + ruleOnLineToAnalyze: 5, + expected: []AnnotationsRef{ + { + Path: MustParseRef("data.root.foo.bar.p"), + Location: &Location{File: "rule", Row: 5}, + Annotations: &Annotations{ + Scope: "rule", + Title: "P", + }, + }, + { + Path: MustParseRef("data.root.foo.bar.p"), + Location: &Location{File: "root.foo.bar", Row: 15}, + Annotations: &Annotations{ + Scope: "document", + Title: "P-doc", + Description: "document scope applied to rule in other module", + }, + }, + { + Path: MustParseRef("data.root.foo.bar"), + Location: &Location{File: "root.foo.bar", Row: 9}, + Annotations: &Annotations{ + Scope: "package", + Title: "BAR-other", + Description: "This metadata is on the path of the queried rule, and should show up in the result even though it's in a different module.", + }, + }, + { + Path: MustParseRef("data.root.foo.bar"), + Location: &Location{File: "root.foo.bar", Row: 9}, + Annotations: &Annotations{ + Scope: "subpackages", + Title: "BAR-sub", + Description: "subpackages scope applied to rule in other module", + }, + }, + { + Path: MustParseRef("data.root.foo"), + Location: &Location{File: "root.foo", Row: 4}, + Annotations: &Annotations{ + Scope: "subpackages", + Title: "FOO", + }, + }, + { + Path: MustParseRef("data.root"), + Location: &Location{File: "root", Row: 4}, + Annotations: &Annotations{ + Scope: "subpackages", + Title: "ROOT", + }, + }, + }, + }, + { + note: "multiple subpackages, refs in rule heads", // NOTE(sr): same as above, but last module's rule is `foo.bar.p` in package `root` + modules: map[string]string{ + "root": `# METADATA +# scope: subpackages +# title: ROOT +package root`, + "root.foo": `# METADATA +# title: FOO +# scope: subpackages +package root.foo`, + "root.foo.bar": `# METADATA +# scope: subpackages +# description: subpackages scope applied to rule in other module +# title: BAR-sub + +# METADATA +# title: BAR-other +# description: This metadata is on the path of the queried rule, but shouldn't show up in the result as it's in a different module. +package root.foo.bar + +# METADATA +# scope: document +# description: document scope applied to rule in other module +# title: P-doc +p = 1`, + "rule": `# METADATA +# title: BAR +package root + +# METADATA +# title: P +foo.bar.p = 1`, + }, + moduleToAnalyze: "rule", + ruleOnLineToAnalyze: 7, + expected: []AnnotationsRef{ + { + Path: MustParseRef("data.root.foo.bar.p"), + Location: &Location{File: "rule", Row: 7}, + Annotations: &Annotations{ + Scope: "rule", + Title: "P", + }, + }, + { + Path: MustParseRef("data.root.foo.bar.p"), + Location: &Location{File: "root.foo.bar", Row: 15}, + Annotations: &Annotations{ + Scope: "document", + Title: "P-doc", + Description: "document scope applied to rule in other module", + }, + }, + { + Path: MustParseRef("data.root"), + Location: &Location{File: "rule", Row: 3}, + Annotations: &Annotations{ + Scope: "package", + Title: "BAR", + }, + }, + { + Path: MustParseRef("data.root"), + Location: &Location{File: "root", Row: 4}, + Annotations: &Annotations{ + Scope: "subpackages", + Title: "ROOT", + }, + }, + }, + }, + { + note: "multiple metadata blocks for single rule (order)", + modules: map[string]string{ + "module": `package test + +# METADATA +# title: One + +# METADATA +# title: Two + +# METADATA +# title: Three + +# METADATA +# title: Four +p = true`, + }, + moduleToAnalyze: "module", + ruleOnLineToAnalyze: 14, + expected: []AnnotationsRef{ // Rule annotations order is expected to start closest to the rule, moving out + { + Path: MustParseRef("data.test.p"), + Location: &Location{File: "module", Row: 14}, + Annotations: &Annotations{ + Scope: "rule", + Title: "Four", + }, + }, + { + Path: MustParseRef("data.test.p"), + Location: &Location{File: "module", Row: 14}, + Annotations: &Annotations{ + Scope: "rule", + Title: "Three", + }, + }, + { + Path: MustParseRef("data.test.p"), + Location: &Location{File: "module", Row: 14}, + Annotations: &Annotations{ + Scope: "rule", + Title: "Two", + }, + }, + { + Path: MustParseRef("data.test.p"), + Location: &Location{File: "module", Row: 14}, + Annotations: &Annotations{ + Scope: "rule", + Title: "One", + }, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + compiler := MustCompileModulesWithOpts(tc.modules, + CompileOpts{ParserOptions: ParserOptions{ProcessAnnotation: true}}) + + as := compiler.GetAnnotationSet() + if as == nil { + t.Fatalf("Expected compiled AnnotationSet, got nil") + } + + m := compiler.Modules[tc.moduleToAnalyze] + if m == nil { + t.Fatalf("no such module: %s", tc.moduleToAnalyze) + } + + var rule *Rule + for _, r := range m.Rules { + if r.Location.Row == tc.ruleOnLineToAnalyze { + rule = r + break + } + } + if rule == nil { + t.Fatalf("no rule found on line %d in module '%s'", + tc.ruleOnLineToAnalyze, tc.moduleToAnalyze) + } + + chain := as.Chain(rule) + + if len(chain) != len(tc.expected) { + t.Errorf("expected %d elements, got %d:", len(tc.expected), len(chain)) + t.Fatalf("chained AnnotationSet\n%v\n\ndoesn't match expected\n\n%v", + toJSON(chain), toJSON(tc.expected)) + } + + for i, expected := range tc.expected { + a := chain[i] + if !expected.Path.Equal(a.Path) { + t.Fatalf("path of AnnotationRef at %d '%v' doesn't match expected '%v'", + i, a.Path, expected.Path) + } + if expected.Location.File != a.Location.File || expected.Location.Row != a.Location.Row { + t.Fatalf("location of AnnotationRef at %d '%v' doesn't match expected '%v'", + i, a.Location, expected.Location) + } + if expected.Annotations.Compare(a.Annotations) != 0 { + t.Fatalf("annotations of AnnotationRef at %d\n%v\n\ndoesn't match expected\n\n%v", + i, a.Annotations, expected.Annotations) + } + } + }) + } +} + +func TestAnnotations_toObject(t *testing.T) { + annotations := Annotations{ + Scope: annotationScopeRule, + Title: "A title", + Description: "A description", + Organizations: []string{ + "Acme Corp.", + "Tyrell Corp.", + }, + RelatedResources: []*RelatedResourceAnnotation{ + { + Ref: mustParseURL("https://example.com"), + Description: "An example", + }, + { + Ref: mustParseURL("https://another.example.com"), + }, + }, + Authors: []*AuthorAnnotation{ + { + Name: "John Doe", + Email: "john@example.com", + }, + { + Name: "Jane Doe", + }, + { + Email: "jeff@example.com", + }, + }, + Schemas: []*SchemaAnnotation{ + { + Path: MustParseRef("input.foo"), + Schema: MustParseRef("schema.a"), + }, + schemaAnnotationFromMap("input.bar", map[string]any{ + "type": "boolean", + }), + }, + Custom: map[string]any{ + "number": 42, + "float": 2.2, + "string": "foo bar baz", + "bool": true, + "list": []any{ + "a", "b", + }, + "list_of_lists": []any{ + []any{ + "a", "b", + }, + []any{ + "b", "c", + }, + }, + "list_of_maps": []any{ + map[string]any{ + "one": 1, + "two": 2, + }, + map[string]any{ + "two": 2, + "three": 3, + }, + }, + "map": map[string]any{ + "nested_number": 1, + "nested_map": map[string]any{ + "do": "re", + "mi": "fa", + }, + "nested_list": []any{ + 1, 2, 3, + }, + }, + }, + } + + expected := NewObject( + Item(StringTerm("scope"), StringTerm(annotationScopeRule)), + Item(StringTerm("title"), StringTerm("A title")), + Item(StringTerm("description"), StringTerm("A description")), + Item(StringTerm("organizations"), ArrayTerm( + StringTerm("Acme Corp."), + StringTerm("Tyrell Corp."), + )), + Item(StringTerm("related_resources"), ArrayTerm( + ObjectTerm( + Item(StringTerm("ref"), StringTerm("https://example.com")), + Item(StringTerm("description"), StringTerm("An example")), + ), + ObjectTerm( + Item(StringTerm("ref"), StringTerm("https://another.example.com")), + ), + )), + Item(StringTerm("authors"), ArrayTerm( + ObjectTerm( + Item(StringTerm("name"), StringTerm("John Doe")), + Item(StringTerm("email"), StringTerm("john@example.com")), + ), + ObjectTerm( + Item(StringTerm("name"), StringTerm("Jane Doe")), + ), + ObjectTerm( + Item(StringTerm("email"), StringTerm("jeff@example.com")), + ), + )), + Item(StringTerm("schemas"), ArrayTerm( + ObjectTerm( + Item(StringTerm("path"), ArrayTerm(StringTerm("input"), StringTerm("foo"))), + Item(StringTerm("schema"), ArrayTerm(StringTerm("schema"), StringTerm("a"))), + ), + ObjectTerm( + Item(StringTerm("path"), ArrayTerm(StringTerm("input"), StringTerm("bar"))), + Item(StringTerm("definition"), ObjectTerm( + Item(StringTerm("type"), StringTerm("boolean")), + )), + ), + )), + Item(StringTerm("custom"), ObjectTerm( + Item(StringTerm("number"), NumberTerm("42")), + Item(StringTerm("float"), NumberTerm("2.2")), + Item(StringTerm("string"), StringTerm("foo bar baz")), + Item(StringTerm("bool"), BooleanTerm(true)), + Item(StringTerm("list"), ArrayTerm( + StringTerm("a"), + StringTerm("b"), + )), + Item(StringTerm("list_of_lists"), ArrayTerm( + ArrayTerm( + StringTerm("a"), + StringTerm("b"), + ), + ArrayTerm( + StringTerm("b"), + StringTerm("c"), + ), + )), + Item(StringTerm("list_of_maps"), ArrayTerm( + ObjectTerm( + Item(StringTerm("one"), NumberTerm("1")), + Item(StringTerm("two"), NumberTerm("2")), + ), + ObjectTerm( + Item(StringTerm("two"), NumberTerm("2")), + Item(StringTerm("three"), NumberTerm("3")), + ), + )), + Item(StringTerm("map"), ObjectTerm( + Item(StringTerm("nested_number"), NumberTerm("1")), + Item(StringTerm("nested_map"), ObjectTerm( + Item(StringTerm("do"), StringTerm("re")), + Item(StringTerm("mi"), StringTerm("fa")), + )), + Item(StringTerm("nested_list"), ArrayTerm( + NumberTerm("1"), + NumberTerm("2"), + NumberTerm("3"), + )), + )), + )), + ) + + obj, err := annotations.toObject() + if err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + + if Compare(*obj, expected) != 0 { + t.Fatalf("object generated from annotations\n\n%v\n\ndoesn't match expected\n\n%v", + *obj, expected) + } +} + +func toJSON(v any) string { + b, _ := json.MarshalIndent(v, "", " ") + return string(b) +} + +func schemaAnnotationFromMap(path string, def map[string]any) *SchemaAnnotation { + var p any = def + return &SchemaAnnotation{Path: MustParseRef(path), Definition: &p} +} diff --git a/third_party/opa/v1/ast/builtins.go b/third_party/opa/v1/ast/builtins.go new file mode 100644 index 000000000000..7552c145bd37 --- /dev/null +++ b/third_party/opa/v1/ast/builtins.go @@ -0,0 +1,3621 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "strings" + + "github.com/open-policy-agent/opa/v1/types" +) + +// Builtins is the registry of built-in functions supported by OPA. +// Call RegisterBuiltin to add a new built-in. +var Builtins []*Builtin + +// RegisterBuiltin adds a new built-in function to the registry. +// NOTE: The underlying map storing built-ins is **not** thread-safe, +// and it's recommended to call this only during initialization, and never +// later. Registering built-ins after that point is unsupported and will +// likely lead to concurrent map read/write panics. +func RegisterBuiltin(b *Builtin) { + Builtins = append(Builtins, b) + BuiltinMap[b.Name] = b + if len(b.Infix) > 0 { + BuiltinMap[b.Infix] = b + + InternStringTerm(b.Infix) + } + + InternStringTerm(b.Name) + if strings.Contains(b.Name, ".") { + InternStringTerm(strings.Split(b.Name, ".")...) + } +} + +// DefaultBuiltins is the registry of built-in functions supported in OPA +// by default. When adding a new built-in function to OPA, update this +// list. +var DefaultBuiltins = [...]*Builtin{ + // Unification/equality ("=") + Equality, + + // Assignment (":=") + Assign, + + // Membership, infix "in": `x in xs` + Member, + MemberWithKey, + + // Comparisons + GreaterThan, + GreaterThanEq, + LessThan, + LessThanEq, + NotEqual, + Equal, + + // Arithmetic + Plus, + Minus, + Multiply, + Divide, + Ceil, + Floor, + Round, + Abs, + Rem, + + // Bitwise Arithmetic + BitsOr, + BitsAnd, + BitsNegate, + BitsXOr, + BitsShiftLeft, + BitsShiftRight, + + // Binary + And, + Or, + + // Aggregates + Count, + Sum, + Product, + Max, + Min, + Any, + All, + + // Arrays + ArrayConcat, + ArraySlice, + ArrayReverse, + + // Conversions + ToNumber, + + // Casts (DEPRECATED) + CastObject, + CastNull, + CastBoolean, + CastString, + CastSet, + CastArray, + + // Regular Expressions + RegexIsValid, + RegexMatch, + RegexMatchDeprecated, + RegexSplit, + GlobsMatch, + RegexTemplateMatch, + RegexFind, + RegexFindAllStringSubmatch, + RegexReplace, + + // Sets + SetDiff, + Intersection, + Union, + + // Strings + AnyPrefixMatch, + AnySuffixMatch, + Concat, + FormatInt, + IndexOf, + IndexOfN, + Substring, + Lower, + Upper, + Contains, + StringCount, + StartsWith, + EndsWith, + Split, + Replace, + ReplaceN, + Trim, + TrimLeft, + TrimPrefix, + TrimRight, + TrimSuffix, + TrimSpace, + Sprintf, + StringReverse, + RenderTemplate, + + // Numbers + NumbersRange, + NumbersRangeStep, + RandIntn, + + // Encoding + JSONMarshal, + JSONMarshalWithOptions, + JSONUnmarshal, + JSONIsValid, + Base64Encode, + Base64Decode, + Base64IsValid, + Base64UrlEncode, + Base64UrlEncodeNoPad, + Base64UrlDecode, + URLQueryDecode, + URLQueryEncode, + URLQueryEncodeObject, + URLQueryDecodeObject, + YAMLMarshal, + YAMLUnmarshal, + YAMLIsValid, + HexEncode, + HexDecode, + + // Object Manipulation + ObjectUnion, + ObjectUnionN, + ObjectRemove, + ObjectFilter, + ObjectGet, + ObjectKeys, + ObjectSubset, + + // JSON Object Manipulation + JSONFilter, + JSONRemove, + JSONPatch, + + // Tokens + JWTDecode, + JWTVerifyRS256, + JWTVerifyRS384, + JWTVerifyRS512, + JWTVerifyPS256, + JWTVerifyPS384, + JWTVerifyPS512, + JWTVerifyES256, + JWTVerifyES384, + JWTVerifyES512, + JWTVerifyHS256, + JWTVerifyHS384, + JWTVerifyHS512, + JWTDecodeVerify, + JWTEncodeSignRaw, + JWTEncodeSign, + + // Time + NowNanos, + ParseNanos, + ParseRFC3339Nanos, + ParseDurationNanos, + Format, + Date, + Clock, + Weekday, + AddDate, + Diff, + + // Crypto + CryptoX509ParseCertificates, + CryptoX509ParseAndVerifyCertificates, + CryptoX509ParseAndVerifyCertificatesWithOptions, + CryptoMd5, + CryptoSha1, + CryptoSha256, + CryptoX509ParseCertificateRequest, + CryptoX509ParseRSAPrivateKey, + CryptoX509ParseKeyPair, + CryptoParsePrivateKeys, + CryptoHmacMd5, + CryptoHmacSha1, + CryptoHmacSha256, + CryptoHmacSha512, + CryptoHmacEqual, + + // Graphs + WalkBuiltin, + ReachableBuiltin, + ReachablePathsBuiltin, + + // Sort + Sort, + + // Types + IsNumber, + IsString, + IsBoolean, + IsArray, + IsSet, + IsObject, + IsNull, + TypeNameBuiltin, + + // HTTP + HTTPSend, + + // GraphQL + GraphQLParse, + GraphQLParseAndVerify, + GraphQLParseQuery, + GraphQLParseSchema, + GraphQLIsValid, + GraphQLSchemaIsValid, + + // JSON Schema + JSONSchemaVerify, + JSONMatchSchema, + + // Cloud Provider Helpers + ProvidersAWSSignReqObj, + + // Rego + RegoParseModule, + RegoMetadataChain, + RegoMetadataRule, + + // OPA + OPARuntime, + + // Tracing + Trace, + + // Networking + NetCIDROverlap, + NetCIDRIntersects, + NetCIDRContains, + NetCIDRContainsMatches, + NetCIDRExpand, + NetCIDRMerge, + NetLookupIPAddr, + NetCIDRIsValid, + + // Glob + GlobMatch, + GlobQuoteMeta, + + // Units + UnitsParse, + UnitsParseBytes, + + // UUIDs + UUIDRFC4122, + UUIDParse, + + // SemVers + SemVerIsValid, + SemVerCompare, + + // Printing + Print, + InternalPrint, + + // Testing + InternalTestCase, +} + +// BuiltinMap provides a convenient mapping of built-in names to +// built-in definitions. +var BuiltinMap map[string]*Builtin + +// Deprecated: Builtins can now be directly annotated with the +// Nondeterministic property, and when set to true, will be ignored +// for partial evaluation. +var IgnoreDuringPartialEval = []*Builtin{ + RandIntn, + UUIDRFC4122, + JWTDecodeVerify, + JWTEncodeSignRaw, + JWTEncodeSign, + NowNanos, + HTTPSend, + OPARuntime, + NetLookupIPAddr, +} + +/** + * Unification + */ + +// Equality represents the "=" operator. +var Equality = &Builtin{ + Name: "eq", + Infix: "=", + Decl: types.NewFunction( + types.Args(types.A, types.A), + types.B, + ), + canSkipBctx: true, +} + +/** + * Assignment + */ + +// Assign represents the assignment (":=") operator. +var Assign = &Builtin{ + Name: "assign", + Infix: ":=", + Decl: types.NewFunction( + types.Args(types.A, types.A), + types.B, + ), + canSkipBctx: true, +} + +// Member represents the `in` (infix) operator. +var Member = &Builtin{ + Name: "internal.member_2", + Infix: "in", + Decl: types.NewFunction( + types.Args( + types.A, + types.A, + ), + types.B, + ), + canSkipBctx: true, +} + +// MemberWithKey represents the `in` (infix) operator when used +// with two terms on the lhs, i.e., `k, v in obj`. +var MemberWithKey = &Builtin{ + Name: "internal.member_3", + Infix: "in", + Decl: types.NewFunction( + types.Args( + types.A, + types.A, + types.A, + ), + types.B, + ), + canSkipBctx: true, +} + +/** + * Comparisons + */ +var comparison = category("comparison") + +var GreaterThan = &Builtin{ + Name: "gt", + Infix: ">", + Categories: comparison, + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A), + types.Named("y", types.A), + ), + types.Named("result", types.B).Description("true if `x` is greater than `y`; false otherwise"), + ), + canSkipBctx: true, +} + +var GreaterThanEq = &Builtin{ + Name: "gte", + Infix: ">=", + Categories: comparison, + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A), + types.Named("y", types.A), + ), + types.Named("result", types.B).Description("true if `x` is greater or equal to `y`; false otherwise"), + ), + canSkipBctx: true, +} + +// LessThan represents the "<" comparison operator. +var LessThan = &Builtin{ + Name: "lt", + Infix: "<", + Categories: comparison, + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A), + types.Named("y", types.A), + ), + types.Named("result", types.B).Description("true if `x` is less than `y`; false otherwise"), + ), + canSkipBctx: true, +} + +var LessThanEq = &Builtin{ + Name: "lte", + Infix: "<=", + Categories: comparison, + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A), + types.Named("y", types.A), + ), + types.Named("result", types.B).Description("true if `x` is less than or equal to `y`; false otherwise"), + ), + canSkipBctx: true, +} + +var NotEqual = &Builtin{ + Name: "neq", + Infix: "!=", + Categories: comparison, + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A), + types.Named("y", types.A), + ), + types.Named("result", types.B).Description("true if `x` is not equal to `y`; false otherwise"), + ), + canSkipBctx: true, +} + +// Equal represents the "==" comparison operator. +var Equal = &Builtin{ + Name: "equal", + Infix: "==", + Categories: comparison, + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A), + types.Named("y", types.A), + ), + types.Named("result", types.B).Description("true if `x` is equal to `y`; false otherwise"), + ), + canSkipBctx: true, +} + +/** + * Arithmetic + */ +var number = category("numbers") + +var Plus = &Builtin{ + Name: "plus", + Infix: "+", + Description: "Plus adds two numbers together.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N), + types.Named("y", types.N), + ), + types.Named("z", types.N).Description("the sum of `x` and `y`"), + ), + Categories: number, + canSkipBctx: true, +} + +var Minus = &Builtin{ + Name: "minus", + Infix: "-", + Description: "Minus subtracts the second number from the first number or computes the difference between two sets.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.NewAny(types.N, types.SetOfAny)), + types.Named("y", types.NewAny(types.N, types.SetOfAny)), + ), + types.Named("z", types.NewAny(types.N, types.SetOfAny)).Description("the difference of `x` and `y`"), + ), + Categories: category("sets", "numbers"), + canSkipBctx: true, +} + +var Multiply = &Builtin{ + Name: "mul", + Infix: "*", + Description: "Multiplies two numbers.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N), + types.Named("y", types.N), + ), + types.Named("z", types.N).Description("the product of `x` and `y`"), + ), + Categories: number, + canSkipBctx: true, +} + +var Divide = &Builtin{ + Name: "div", + Infix: "/", + Description: "Divides the first number by the second number.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N).Description("the dividend"), + types.Named("y", types.N).Description("the divisor"), + ), + types.Named("z", types.N).Description("the result of `x` divided by `y`"), + ), + Categories: number, + canSkipBctx: true, +} + +var Round = &Builtin{ + Name: "round", + Description: "Rounds the number to the nearest integer.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N).Description("the number to round"), + ), + types.Named("y", types.N).Description("the result of rounding `x`"), + ), + Categories: number, + canSkipBctx: true, +} + +var Ceil = &Builtin{ + Name: "ceil", + Description: "Rounds the number _up_ to the nearest integer.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N).Description("the number to round"), + ), + types.Named("y", types.N).Description("the result of rounding `x` _up_"), + ), + Categories: number, + canSkipBctx: true, +} + +var Floor = &Builtin{ + Name: "floor", + Description: "Rounds the number _down_ to the nearest integer.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N).Description("the number to round"), + ), + types.Named("y", types.N).Description("the result of rounding `x` _down_"), + ), + Categories: number, + canSkipBctx: true, +} + +var Abs = &Builtin{ + Name: "abs", + Description: "Returns the number without its sign.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N).Description("the number to take the absolute value of"), + ), + types.Named("y", types.N).Description("the absolute value of `x`"), + ), + Categories: number, + canSkipBctx: true, +} + +var Rem = &Builtin{ + Name: "rem", + Infix: "%", + Description: "Returns the remainder for of `x` divided by `y`, for `y != 0`.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N), + types.Named("y", types.N), + ), + types.Named("z", types.N).Description("the remainder"), + ), + Categories: number, + canSkipBctx: true, +} + +/** + * Bitwise + */ + +var BitsOr = &Builtin{ + Name: "bits.or", + Description: "Returns the bitwise \"OR\" of two integers.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N).Description("the first integer"), + types.Named("y", types.N).Description("the second integer"), + ), + types.Named("z", types.N).Description("the bitwise OR of `x` and `y`"), + ), + canSkipBctx: true, +} + +var BitsAnd = &Builtin{ + Name: "bits.and", + Description: "Returns the bitwise \"AND\" of two integers.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N).Description("the first integer"), + types.Named("y", types.N).Description("the second integer"), + ), + types.Named("z", types.N).Description("the bitwise AND of `x` and `y`"), + ), + canSkipBctx: true, +} + +var BitsNegate = &Builtin{ + Name: "bits.negate", + Description: "Returns the bitwise negation (flip) of an integer.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N).Description("the integer to negate"), + ), + types.Named("z", types.N).Description("the bitwise negation of `x`"), + ), + canSkipBctx: true, +} + +var BitsXOr = &Builtin{ + Name: "bits.xor", + Description: "Returns the bitwise \"XOR\" (exclusive-or) of two integers.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N).Description("the first integer"), + types.Named("y", types.N).Description("the second integer"), + ), + types.Named("z", types.N).Description("the bitwise XOR of `x` and `y`"), + ), + canSkipBctx: true, +} + +var BitsShiftLeft = &Builtin{ + Name: "bits.lsh", + Description: "Returns a new integer with its bits shifted `s` bits to the left.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N).Description("the integer to shift"), + types.Named("s", types.N).Description("the number of bits to shift"), + ), + types.Named("z", types.N).Description("the result of shifting `x` `s` bits to the left"), + ), + canSkipBctx: true, +} + +var BitsShiftRight = &Builtin{ + Name: "bits.rsh", + Description: "Returns a new integer with its bits shifted `s` bits to the right.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.N).Description("the integer to shift"), + types.Named("s", types.N).Description("the number of bits to shift"), + ), + types.Named("z", types.N).Description("the result of shifting `x` `s` bits to the right"), + ), + canSkipBctx: true, +} + +/** + * Sets + */ + +var sets = category("sets") + +var And = &Builtin{ + Name: "and", + Infix: "&", + Description: "Returns the intersection of two sets.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.SetOfAny).Description("the first set"), + types.Named("y", types.SetOfAny).Description("the second set"), + ), + types.Named("z", types.SetOfAny).Description("the intersection of `x` and `y`"), + ), + Categories: sets, + canSkipBctx: true, +} + +// Or performs a union operation on sets. +var Or = &Builtin{ + Name: "or", + Infix: "|", + Description: "Returns the union of two sets.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.SetOfAny), + types.Named("y", types.SetOfAny), + ), + types.Named("z", types.SetOfAny).Description("the union of `x` and `y`"), + ), + Categories: sets, + canSkipBctx: true, +} + +var Intersection = &Builtin{ + Name: "intersection", + Description: "Returns the intersection of the given input sets.", + Decl: types.NewFunction( + types.Args( + types.Named("xs", types.NewSet(types.SetOfAny)).Description("set of sets to intersect"), + ), + types.Named("y", types.SetOfAny).Description("the intersection of all `xs` sets"), + ), + Categories: sets, + canSkipBctx: true, +} + +var Union = &Builtin{ + Name: "union", + Description: "Returns the union of the given input sets.", + Decl: types.NewFunction( + types.Args( + types.Named("xs", types.NewSet(types.SetOfAny)).Description("set of sets to merge"), + ), + types.Named("y", types.SetOfAny).Description("the union of all `xs` sets"), + ), + Categories: sets, + canSkipBctx: true, +} + +/** + * Aggregates + */ + +var aggregates = category("aggregates") + +var Count = &Builtin{ + Name: "count", + Description: " Count takes a collection or string and returns the number of elements (or characters) in it.", + Decl: types.NewFunction( + types.Args( + types.Named("collection", types.NewAny( + types.SetOfAny, + types.NewArray(nil, types.A), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + types.S, + )).Description("the set/array/object/string to be counted"), + ), + types.Named("n", types.N).Description("the count of elements, key/val pairs, or characters, respectively."), + ), + Categories: aggregates, + canSkipBctx: true, +} + +var Sum = &Builtin{ + Name: "sum", + Description: "Sums elements of an array or set of numbers.", + Decl: types.NewFunction( + types.Args( + types.Named("collection", types.NewAny( + types.SetOfNum, + types.NewArray(nil, types.N), + )).Description("the set or array of numbers to sum"), + ), + types.Named("n", types.N).Description("the sum of all elements"), + ), + Categories: aggregates, + canSkipBctx: true, +} + +var Product = &Builtin{ + Name: "product", + Description: "Multiplies elements of an array or set of numbers", + Decl: types.NewFunction( + types.Args( + types.Named("collection", types.NewAny( + types.SetOfNum, + types.NewArray(nil, types.N), + )).Description("the set or array of numbers to multiply"), + ), + types.Named("n", types.N).Description("the product of all elements"), + ), + Categories: aggregates, + canSkipBctx: true, +} + +var Max = &Builtin{ + Name: "max", + Description: "Returns the maximum value in a collection.", + Decl: types.NewFunction( + types.Args( + types.Named("collection", types.NewAny( + types.SetOfAny, + types.NewArray(nil, types.A), + )).Description("the set or array to be searched"), + ), + types.Named("n", types.A).Description("the maximum of all elements"), + ), + Categories: aggregates, + canSkipBctx: true, +} + +var Min = &Builtin{ + Name: "min", + Description: "Returns the minimum value in a collection.", + Decl: types.NewFunction( + types.Args( + types.Named("collection", types.NewAny( + types.SetOfAny, + types.NewArray(nil, types.A), + )).Description("the set or array to be searched"), + ), + types.Named("n", types.A).Description("the minimum of all elements"), + ), + Categories: aggregates, + canSkipBctx: true, +} + +/** + * Sorting + */ + +var Sort = &Builtin{ + Name: "sort", + Description: "Returns a sorted array.", + Decl: types.NewFunction( + types.Args( + types.Named("collection", types.NewAny( + types.NewArray(nil, types.A), + types.SetOfAny, + )).Description("the array or set to be sorted"), + ), + types.Named("n", types.NewArray(nil, types.A)).Description("the sorted array"), + ), + Categories: aggregates, + canSkipBctx: true, +} + +/** + * Arrays + */ + +var ArrayConcat = &Builtin{ + Name: "array.concat", + Description: "Concatenates two arrays.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.NewArray(nil, types.A)).Description("the first array"), + types.Named("y", types.NewArray(nil, types.A)).Description("the second array"), + ), + types.Named("z", types.NewArray(nil, types.A)).Description("the concatenation of `x` and `y`"), + ), + canSkipBctx: true, +} + +var ArraySlice = &Builtin{ + Name: "array.slice", + Description: "Returns a slice of a given array. If `start` is greater or equal than `stop`, `slice` is `[]`.", + Decl: types.NewFunction( + types.Args( + types.Named("arr", types.NewArray(nil, types.A)).Description("the array to be sliced"), + types.Named("start", types.N).Description("the start index of the returned slice; if less than zero, it's clamped to 0"), + types.Named("stop", types.N).Description("the stop index of the returned slice; if larger than `count(arr)`, it's clamped to `count(arr)`"), + ), + types.Named("slice", types.NewArray(nil, types.A)).Description("the subslice of `array`, from `start` to `end`, including `arr[start]`, but excluding `arr[end]`"), + ), + canSkipBctx: true, +} // NOTE(sr): this function really needs examples + +var ArrayReverse = &Builtin{ + Name: "array.reverse", + Description: "Returns the reverse of a given array.", + Decl: types.NewFunction( + types.Args( + types.Named("arr", types.NewArray(nil, types.A)).Description("the array to be reversed"), + ), + types.Named("rev", types.NewArray(nil, types.A)).Description("an array containing the elements of `arr` in reverse order"), + ), + canSkipBctx: true, +} + +/** + * Conversions + */ +var conversions = category("conversions") + +var ToNumber = &Builtin{ + Name: "to_number", + Description: "Converts a string, bool, or number value to a number: Strings are converted to numbers using `strconv.Atoi`, Boolean `false` is converted to 0 and `true` is converted to 1.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.NewAny( + types.N, + types.S, + types.B, + types.Nl, + )).Description("value to convert"), + ), + types.Named("num", types.N).Description("the numeric representation of `x`"), + ), + Categories: conversions, + canSkipBctx: true, +} + +/** + * Regular Expressions + */ + +var RegexMatch = &Builtin{ + Name: "regex.match", + Description: "Matches a string against a regular expression.", + Decl: types.NewFunction( + types.Args( + types.Named("pattern", types.S).Description("regular expression"), + types.Named("value", types.S).Description("value to match against `pattern`"), + ), + types.Named("result", types.B).Description("true if `value` matches `pattern`"), + ), +} + +var RegexIsValid = &Builtin{ + Name: "regex.is_valid", + Description: "Checks if a string is a valid regular expression: the detailed syntax for patterns is defined by https://github.com/google/re2/wiki/Syntax.", + Decl: types.NewFunction( + types.Args( + types.Named("pattern", types.S).Description("regular expression"), + ), + types.Named("result", types.B).Description("true if `pattern` is a valid regular expression"), + ), + canSkipBctx: true, +} + +var RegexFindAllStringSubmatch = &Builtin{ + Name: "regex.find_all_string_submatch_n", + Description: "Returns all successive matches of the expression.", + Decl: types.NewFunction( + types.Args( + types.Named("pattern", types.S).Description("regular expression"), + types.Named("value", types.S).Description("string to match"), + types.Named("number", types.N).Description("number of matches to return; `-1` means all matches"), + ), + types.Named("output", types.NewArray(nil, types.NewArray(nil, types.S))).Description("array of all matches"), + ), + canSkipBctx: false, +} + +var RegexTemplateMatch = &Builtin{ + Name: "regex.template_match", + Description: "Matches a string against a pattern, where there pattern may be glob-like", + Decl: types.NewFunction( + types.Args( + types.Named("template", types.S).Description("template expression containing `0..n` regular expressions"), + types.Named("value", types.S).Description("string to match"), + types.Named("delimiter_start", types.S).Description("start delimiter of the regular expression in `template`"), + types.Named("delimiter_end", types.S).Description("end delimiter of the regular expression in `template`"), + ), + types.Named("result", types.B).Description("true if `value` matches the `template`"), + ), + canSkipBctx: true, +} // TODO(sr): example:`regex.template_match("urn:foo:{.*}", "urn:foo:bar:baz", "{", "}")`` returns ``true``. + +var RegexSplit = &Builtin{ + Name: "regex.split", + Description: "Splits the input string by the occurrences of the given pattern.", + Decl: types.NewFunction( + types.Args( + types.Named("pattern", types.S).Description("regular expression"), + types.Named("value", types.S).Description("string to match"), + ), + types.Named("output", types.NewArray(nil, types.S)).Description("the parts obtained by splitting `value`"), + ), + canSkipBctx: false, +} + +// RegexFind takes two strings and a number, the pattern, the value and number of match values to +// return, -1 means all match values. +var RegexFind = &Builtin{ + Name: "regex.find_n", + Description: "Returns the specified number of matches when matching the input against the pattern.", + Decl: types.NewFunction( + types.Args( + types.Named("pattern", types.S).Description("regular expression"), + types.Named("value", types.S).Description("string to match"), + types.Named("number", types.N).Description("number of matches to return, if `-1`, returns all matches"), + ), + types.Named("output", types.NewArray(nil, types.S)).Description("collected matches"), + ), + canSkipBctx: false, +} + +// GlobsMatch takes two strings regexp-style strings and evaluates to true if their +// intersection matches a non-empty set of non-empty strings. +// Examples: +// - "a.a." and ".b.b" -> true. +// - "[a-z]*" and [0-9]+" -> not true. +var GlobsMatch = &Builtin{ + Name: "regex.globs_match", + Description: `Checks if the intersection of two glob-style regular expressions matches a non-empty set of non-empty strings. +The set of regex symbols is limited for this builtin: only ` + "`.`, `*`, `+`, `[`, `-`, `]` and `\\` are treated as special symbols.", + Decl: types.NewFunction( + types.Args( + types.Named("glob1", types.S).Description("first glob-style regular expression"), + types.Named("glob2", types.S).Description("second glob-style regular expression"), + ), + types.Named("result", types.B).Description("true if the intersection of `glob1` and `glob2` matches a non-empty set of non-empty strings"), + ), + canSkipBctx: true, +} + +/** + * Strings + */ +var stringsCat = category("strings") + +var AnyPrefixMatch = &Builtin{ + Name: "strings.any_prefix_match", + Description: "Returns true if any of the search strings begins with any of the base strings.", + Decl: types.NewFunction( + types.Args( + types.Named("search", types.NewAny( + types.S, + types.SetOfStr, + types.NewArray(nil, types.S), + )).Description("search string(s)"), + types.Named("base", types.NewAny( + types.S, + types.SetOfStr, + types.NewArray(nil, types.S), + )).Description("base string(s)"), + ), + types.Named("result", types.B).Description("result of the prefix check"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var AnySuffixMatch = &Builtin{ + Name: "strings.any_suffix_match", + Description: "Returns true if any of the search strings ends with any of the base strings.", + Decl: types.NewFunction( + types.Args( + types.Named("search", types.NewAny( + types.S, + types.SetOfStr, + types.NewArray(nil, types.S), + )).Description("search string(s)"), + types.Named("base", types.NewAny( + types.S, + types.SetOfStr, + types.NewArray(nil, types.S), + )).Description("base string(s)"), + ), + types.Named("result", types.B).Description("result of the suffix check"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var Concat = &Builtin{ + Name: "concat", + Description: "Joins a set or array of strings with a delimiter.", + Decl: types.NewFunction( + types.Args( + types.Named("delimiter", types.S).Description("string to use as a delimiter"), + types.Named("collection", types.NewAny( + types.SetOfStr, + types.NewArray(nil, types.S), + )).Description("strings to join"), + ), + types.Named("output", types.S).Description("the joined string"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var FormatInt = &Builtin{ + Name: "format_int", + Description: "Returns the string representation of the number in the given base after rounding it down to an integer value.", + Decl: types.NewFunction( + types.Args( + types.Named("number", types.N).Description("number to format"), + types.Named("base", types.N).Description("base of number representation to use"), + ), + types.Named("output", types.S).Description("formatted number"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var IndexOf = &Builtin{ + Name: "indexof", + Description: "Returns the index of a substring contained inside a string.", + Decl: types.NewFunction( + types.Args( + types.Named("haystack", types.S).Description("string to search in"), + types.Named("needle", types.S).Description("substring to look for"), + ), + types.Named("output", types.N).Description("index of first occurrence, `-1` if not found"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var IndexOfN = &Builtin{ + Name: "indexof_n", + Description: "Returns a list of all the indexes of a substring contained inside a string.", + Decl: types.NewFunction( + types.Args( + types.Named("haystack", types.S).Description("string to search in"), + types.Named("needle", types.S).Description("substring to look for"), + ), + types.Named("output", types.NewArray(nil, types.N)).Description("all indices at which `needle` occurs in `haystack`, may be empty"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var Substring = &Builtin{ + Name: "substring", + Description: "Returns the portion of a string for a given `offset` and a `length`. If `length < 0`, `output` is the remainder of the string.", + Decl: types.NewFunction( + types.Args( + types.Named("value", types.S).Description("string to extract substring from"), + types.Named("offset", types.N).Description("offset, must be positive"), + types.Named("length", types.N).Description("length of the substring starting from `offset`"), + ), + types.Named("output", types.S).Description("substring of `value` from `offset`, of length `length`"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var Contains = &Builtin{ + Name: "contains", + Description: "Returns `true` if the search string is included in the base string", + Decl: types.NewFunction( + types.Args( + types.Named("haystack", types.S).Description("string to search in"), + types.Named("needle", types.S).Description("substring to look for"), + ), + types.Named("result", types.B).Description("result of the containment check"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var StringCount = &Builtin{ + Name: "strings.count", + Description: "Returns the number of non-overlapping instances of a substring in a string.", + Decl: types.NewFunction( + types.Args( + types.Named("search", types.S).Description("string to search in"), + types.Named("substring", types.S).Description("substring to look for"), + ), + types.Named("output", types.N).Description("count of occurrences, `0` if not found"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var StartsWith = &Builtin{ + Name: "startswith", + Description: "Returns true if the search string begins with the base string.", + Decl: types.NewFunction( + types.Args( + types.Named("search", types.S).Description("search string"), + types.Named("base", types.S).Description("base string"), + ), + types.Named("result", types.B).Description("result of the prefix check"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var EndsWith = &Builtin{ + Name: "endswith", + Description: "Returns true if the search string ends with the base string.", + Decl: types.NewFunction( + types.Args( + types.Named("search", types.S).Description("search string"), + types.Named("base", types.S).Description("base string"), + ), + types.Named("result", types.B).Description("result of the suffix check"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var Lower = &Builtin{ + Name: "lower", + Description: "Returns the input string but with all characters in lower-case.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string that is converted to lower-case"), + ), + types.Named("y", types.S).Description("lower-case of x"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var Upper = &Builtin{ + Name: "upper", + Description: "Returns the input string but with all characters in upper-case.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string that is converted to upper-case"), + ), + types.Named("y", types.S).Description("upper-case of x"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var Split = &Builtin{ + Name: "split", + Description: "Split returns an array containing elements of the input string split on a delimiter.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string that is split"), + types.Named("delimiter", types.S).Description("delimiter used for splitting"), + ), + types.Named("ys", types.NewArray(nil, types.S)).Description("split parts"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var Replace = &Builtin{ + Name: "replace", + Description: "Replace replaces all instances of a sub-string.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string being processed"), + types.Named("old", types.S).Description("substring to replace"), + types.Named("new", types.S).Description("string to replace `old` with"), + ), + types.Named("y", types.S).Description("string with replaced substrings"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var ReplaceN = &Builtin{ + Name: "strings.replace_n", + Description: `Replaces a string from a list of old, new string pairs. +Replacements are performed in the order they appear in the target string, without overlapping matches. +The old string comparisons are done in argument order.`, + Decl: types.NewFunction( + types.Args( + types.Named("patterns", types.NewObject( + nil, + types.NewDynamicProperty( + types.S, + types.S)), + ).Description("replacement pairs"), + types.Named("value", types.S).Description("string to replace substring matches in"), + ), + types.Named("output", types.S).Description("string with replaced substrings"), + ), + canSkipBctx: true, +} + +var RegexReplace = &Builtin{ + Name: "regex.replace", + Description: `Find and replaces the text using the regular expression pattern.`, + Decl: types.NewFunction( + types.Args( + types.Named("s", types.S).Description("string being processed"), + types.Named("pattern", types.S).Description("regex pattern to be applied"), + types.Named("value", types.S).Description("regex value"), + ), + types.Named("output", types.S).Description("string with replaced substrings"), + ), + canSkipBctx: false, +} + +var Trim = &Builtin{ + Name: "trim", + Description: "Returns `value` with all leading or trailing instances of the `cutset` characters removed.", + Decl: types.NewFunction( + types.Args( + types.Named("value", types.S).Description("string to trim"), + types.Named("cutset", types.S).Description("string of characters that are cut off"), + ), + types.Named("output", types.S).Description("string trimmed of `cutset` characters"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var TrimLeft = &Builtin{ + Name: "trim_left", + Description: "Returns `value` with all leading instances of the `cutset` characters removed.", + Decl: types.NewFunction( + types.Args( + types.Named("value", types.S).Description("string to trim"), + types.Named("cutset", types.S).Description("string of characters that are cut off on the left"), + ), + types.Named("output", types.S).Description("string left-trimmed of `cutset` characters"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var TrimPrefix = &Builtin{ + Name: "trim_prefix", + Description: "Returns `value` without the prefix. If `value` doesn't start with `prefix`, it is returned unchanged.", + Decl: types.NewFunction( + types.Args( + types.Named("value", types.S).Description("string to trim"), + types.Named("prefix", types.S).Description("prefix to cut off"), + ), + types.Named("output", types.S).Description("string with `prefix` cut off"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var TrimRight = &Builtin{ + Name: "trim_right", + Description: "Returns `value` with all trailing instances of the `cutset` characters removed.", + Decl: types.NewFunction( + types.Args( + types.Named("value", types.S).Description("string to trim"), + types.Named("cutset", types.S).Description("string of characters that are cut off on the right"), + ), + types.Named("output", types.S).Description("string right-trimmed of `cutset` characters"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var TrimSuffix = &Builtin{ + Name: "trim_suffix", + Description: "Returns `value` without the suffix. If `value` doesn't end with `suffix`, it is returned unchanged.", + Decl: types.NewFunction( + types.Args( + types.Named("value", types.S).Description("string to trim"), + types.Named("suffix", types.S).Description("suffix to cut off"), + ), + types.Named("output", types.S).Description("string with `suffix` cut off"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var TrimSpace = &Builtin{ + Name: "trim_space", + Description: "Return the given string with all leading and trailing white space removed.", + Decl: types.NewFunction( + types.Args( + types.Named("value", types.S).Description("string to trim"), + ), + types.Named("output", types.S).Description("string leading and trailing white space cut off"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var Sprintf = &Builtin{ + Name: "sprintf", + Description: "Returns the given string, formatted.", + Decl: types.NewFunction( + types.Args( + types.Named("format", types.S).Description("string with formatting verbs"), + types.Named("values", types.NewArray(nil, types.A)).Description("arguments to format into formatting verbs"), + ), + types.Named("output", types.S).Description("`format` formatted by the values in `values`"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var StringReverse = &Builtin{ + Name: "strings.reverse", + Description: "Reverses a given string.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string to reverse"), + ), + types.Named("y", types.S).Description("reversed string"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +var RenderTemplate = &Builtin{ + Name: "strings.render_template", + Description: `Renders a templated string with given template variables injected. For a given templated string and key/value mapping, values will be injected into the template where they are referenced by key. + For examples of templating syntax, see https://pkg.go.dev/text/template`, + Decl: types.NewFunction( + types.Args( + types.Named("value", types.S).Description("a templated string"), + types.Named("vars", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))).Description("a mapping of template variable keys to values"), + ), + types.Named("result", types.S).Description("rendered template with template variables injected"), + ), + Categories: stringsCat, + canSkipBctx: true, +} + +/** + * Numbers + */ + +// RandIntn returns a random number 0 - n +// Marked non-deterministic because it relies on RNG internally. +var RandIntn = &Builtin{ + Name: "rand.intn", + Description: "Returns a random integer between `0` and `n` (`n` exclusive). If `n` is `0`, then `y` is always `0`. For any given argument pair (`str`, `n`), the output will be consistent throughout a query evaluation.", + Decl: types.NewFunction( + types.Args( + types.Named("str", types.S).Description("seed string for the random number"), + types.Named("n", types.N).Description("upper bound of the random number (exclusive)"), + ), + types.Named("y", types.N).Description("random integer in the range `[0, abs(n))`"), + ), + Categories: number, + Nondeterministic: true, + canSkipBctx: false, +} + +var NumbersRange = &Builtin{ + Name: "numbers.range", + Description: "Returns an array of numbers in the given (inclusive) range. If `a==b`, then `range == [a]`; if `a > b`, then `range` is in descending order.", + Decl: types.NewFunction( + types.Args( + types.Named("a", types.N).Description("the start of the range"), + types.Named("b", types.N).Description("the end of the range (inclusive)"), + ), + types.Named("range", types.NewArray(nil, types.N)).Description("the range between `a` and `b`"), + ), + canSkipBctx: false, // needed for context timeout check +} + +var NumbersRangeStep = &Builtin{ + Name: "numbers.range_step", + Description: `Returns an array of numbers in the given (inclusive) range incremented by a positive step. + If "a==b", then "range == [a]"; if "a > b", then "range" is in descending order. + If the provided "step" is less then 1, an error will be thrown. + If "b" is not in the range of the provided "step", "b" won't be included in the result. + `, + Decl: types.NewFunction( + types.Args( + types.Named("a", types.N).Description("the start of the range"), + types.Named("b", types.N).Description("the end of the range (inclusive)"), + types.Named("step", types.N).Description("the step between numbers in the range"), + ), + types.Named("range", types.NewArray(nil, types.N)).Description("the range between `a` and `b` in `step` increments"), + ), + canSkipBctx: false, // needed for context timeout check +} + +/** + * Units + */ + +var UnitsParse = &Builtin{ + Name: "units.parse", + Description: `Converts strings like "10G", "5K", "4M", "1500m", and the like into a number. +This number can be a non-integer, such as 1.5, 0.22, etc. Scientific notation is supported, +allowing values such as "1e-3K" (1) or "2.5e6M" (2.5 million M). + +Supports standard metric decimal and binary SI units (e.g., K, Ki, M, Mi, G, Gi, etc.) where +m, K, M, G, T, P, and E are treated as decimal units and Ki, Mi, Gi, Ti, Pi, and Ei are treated as +binary units. + +Note that 'm' and 'M' are case-sensitive to allow distinguishing between "milli" and "mega" units +respectively. Other units are case-insensitive.`, + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("the unit to parse"), + ), + types.Named("y", types.N).Description("the parsed number"), + ), + canSkipBctx: true, +} + +var UnitsParseBytes = &Builtin{ + Name: "units.parse_bytes", + Description: `Converts strings like "10GB", "5K", "4mb", or "1e6KB" into an integer number of bytes. + +Supports standard byte units (e.g., KB, KiB, etc.) where KB, MB, GB, and TB are treated as decimal +units, and KiB, MiB, GiB, and TiB are treated as binary units. Scientific notation is supported, +enabling values like "1.5e3MB" (1500MB) or "2e6GiB" (2 million GiB). + +The bytes symbol (b/B) in the unit is optional; omitting it will yield the same result (e.g., "Mi" +and "MiB" are equivalent).`, + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("the byte unit to parse"), + ), + types.Named("y", types.N).Description("the parsed number"), + ), + canSkipBctx: true, +} + +// +/** + * Type + */ + +// UUIDRFC4122 returns a version 4 UUID string. +// Marked non-deterministic because it relies on RNG internally. +var UUIDRFC4122 = &Builtin{ + Name: "uuid.rfc4122", + Description: "Returns a new UUIDv4.", + Decl: types.NewFunction( + types.Args( + types.Named("k", types.S).Description("seed string"), + ), + types.Named("output", types.S).Description("a version 4 UUID; for any given `k`, the output will be consistent throughout a query evaluation"), + ), + Nondeterministic: true, + canSkipBctx: false, +} + +var UUIDParse = &Builtin{ + Name: "uuid.parse", + Description: "Parses the string value as an UUID and returns an object with the well-defined fields of the UUID if valid.", + Categories: nil, + Decl: types.NewFunction( + types.Args( + types.Named("uuid", types.S).Description("UUID string to parse"), + ), + types.Named("result", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))).Description("Properties of UUID if valid (version, variant, etc). Undefined otherwise."), + ), + Relation: false, + canSkipBctx: true, +} + +/** + * JSON + */ + +var objectCat = category("object") + +var JSONFilter = &Builtin{ + Name: "json.filter", + Description: "Filters the object. " + + "For example: `json.filter({\"a\": {\"b\": \"x\", \"c\": \"y\"}}, [\"a/b\"])` will result in `{\"a\": {\"b\": \"x\"}}`). " + + "Paths are not filtered in-order and are deduplicated before being evaluated.", + Decl: types.NewFunction( + types.Args( + types.Named("object", types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + )).Description("object to filter"), + types.Named("paths", types.NewAny( + types.NewArray( + nil, + types.NewAny( + types.S, + types.NewArray( + nil, + types.A, + ), + ), + ), + types.NewSet( + types.NewAny( + types.S, + types.NewArray( + nil, + types.A, + ), + ), + ), + )).Description("JSON string paths"), + ), + types.Named("filtered", types.A).Description("remaining data from `object` with only keys specified in `paths`"), + ), + Categories: objectCat, + canSkipBctx: true, +} + +var JSONRemove = &Builtin{ + Name: "json.remove", + Description: "Removes paths from an object. " + + "For example: `json.remove({\"a\": {\"b\": \"x\", \"c\": \"y\"}}, [\"a/b\"])` will result in `{\"a\": {\"c\": \"y\"}}`. " + + "Paths are not removed in-order and are deduplicated before being evaluated.", + Decl: types.NewFunction( + types.Args( + types.Named("object", types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + )).Description("object to remove paths from"), + types.Named("paths", types.NewAny( + types.NewArray( + nil, + types.NewAny( + types.S, + types.NewArray( + nil, + types.A, + ), + ), + ), + types.NewSet( + types.NewAny( + types.S, + types.NewArray( + nil, + types.A, + ), + ), + ), + )).Description("JSON string paths"), + ), + types.Named("output", types.A).Description("result of removing all keys specified in `paths`"), + ), + Categories: objectCat, + canSkipBctx: true, +} + +var JSONPatch = &Builtin{ + Name: "json.patch", + Description: "Patches an object according to RFC6902. " + + "For example: `json.patch({\"a\": {\"foo\": 1}}, [{\"op\": \"add\", \"path\": \"/a/bar\", \"value\": 2}])` results in `{\"a\": {\"foo\": 1, \"bar\": 2}`. " + + "The patches are applied atomically: if any of them fails, the result will be undefined. " + + "Additionally works on sets, where a value contained in the set is considered to be its path.", + Decl: types.NewFunction( + types.Args( + types.Named("object", types.A).Description("the object to patch"), // TODO(sr): types.A? + types.Named("patches", types.NewArray( + nil, + types.NewObject( + []*types.StaticProperty{ + {Key: "op", Value: types.S}, + {Key: "path", Value: types.A}, + }, + types.NewDynamicProperty(types.A, types.A), + ), + )).Description("the JSON patches to apply"), + ), + types.Named("output", types.A).Description("result obtained after consecutively applying all patch operations in `patches`"), + ), + Categories: objectCat, + canSkipBctx: true, +} + +var ObjectSubset = &Builtin{ + Name: "object.subset", + Description: "Determines if an object `sub` is a subset of another object `super`." + + "Object `sub` is a subset of object `super` if and only if every key in `sub` is also in `super`, " + + "**and** for all keys which `sub` and `super` share, they have the same value. " + + "This function works with objects, sets, arrays and a set of array and set." + + "If both arguments are objects, then the operation is recursive, e.g. " + + "`{\"c\": {\"x\": {10, 15, 20}}` is a subset of `{\"a\": \"b\", \"c\": {\"x\": {10, 15, 20, 25}, \"y\": \"z\"}`. " + + "If both arguments are sets, then this function checks if every element of `sub` is a member of `super`, " + + "but does not attempt to recurse. If both arguments are arrays, " + + "then this function checks if `sub` appears contiguously in order within `super`, " + + "and also does not attempt to recurse. If `super` is array and `sub` is set, " + + "then this function checks if `super` contains every element of `sub` with no consideration of ordering, " + + "and also does not attempt to recurse.", + Decl: types.NewFunction( + types.Args( + types.Named("super", types.NewAny(types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + ), types.SetOfAny, + types.NewArray(nil, types.A), + )).Description("object to test if sub is a subset of"), + types.Named("sub", types.NewAny(types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + ), types.SetOfAny, + types.NewArray(nil, types.A), + )).Description("object to test if super is a superset of"), + ), + types.Named("result", types.A).Description("`true` if `sub` is a subset of `super`"), + ), + canSkipBctx: true, +} + +var ObjectUnion = &Builtin{ + Name: "object.union", + Description: "Creates a new object of the asymmetric union of two objects. " + + "For example: `object.union({\"a\": 1, \"b\": 2, \"c\": {\"d\": 3}}, {\"a\": 7, \"c\": {\"d\": 4, \"e\": 5}})` will result in `{\"a\": 7, \"b\": 2, \"c\": {\"d\": 4, \"e\": 5}}`.", + Decl: types.NewFunction( + types.Args( + types.Named("a", types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + )).Description("left-hand object"), + types.Named("b", types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + )).Description("right-hand object"), + ), + types.Named("output", types.A).Description("a new object which is the result of an asymmetric recursive union of two objects where conflicts are resolved by choosing the key from the right-hand object `b`"), + ), // TODO(sr): types.A? ^^^^^^^ (also below) + canSkipBctx: true, +} + +var ObjectUnionN = &Builtin{ + Name: "object.union_n", + Description: "Creates a new object that is the asymmetric union of all objects merged from left to right. " + + "For example: `object.union_n([{\"a\": 1}, {\"b\": 2}, {\"a\": 3}])` will result in `{\"b\": 2, \"a\": 3}`.", + Decl: types.NewFunction( + types.Args( + types.Named("objects", types.NewArray( + nil, + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + )).Description("list of objects to merge"), + ), + types.Named("output", types.A).Description("asymmetric recursive union of all objects in `objects`, merged from left to right, where conflicts are resolved by choosing the key from the right-hand object"), + ), + canSkipBctx: true, +} + +var ObjectRemove = &Builtin{ + Name: "object.remove", + Description: "Removes specified keys from an object.", + Decl: types.NewFunction( + types.Args( + types.Named("object", types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + )).Description("object to remove keys from"), + types.Named("keys", types.NewAny( + types.NewArray(nil, types.A), + types.SetOfAny, + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + )).Description("keys to remove from x"), + ), + types.Named("output", types.A).Description("result of removing the specified `keys` from `object`"), + ), + canSkipBctx: true, +} + +var ObjectFilter = &Builtin{ + Name: "object.filter", + Description: "Filters the object by keeping only specified keys. " + + "For example: `object.filter({\"a\": {\"b\": \"x\", \"c\": \"y\"}, \"d\": \"z\"}, [\"a\"])` will result in `{\"a\": {\"b\": \"x\", \"c\": \"y\"}}`).", + Decl: types.NewFunction( + types.Args( + types.Named("object", types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + )).Description("object to filter keys"), + types.Named("keys", types.NewAny( + types.NewArray(nil, types.A), + types.SetOfAny, + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + )).Description("keys to keep in `object`"), + ), + types.Named("filtered", types.A).Description("remaining data from `object` with only keys specified in `keys`"), + ), + canSkipBctx: true, +} + +var ObjectGet = &Builtin{ + Name: "object.get", + Description: "Returns value of an object's key if present, otherwise a default. " + + "If the supplied `key` is an `array`, then `object.get` will search through a nested object or array using each key in turn. " + + "For example: `object.get({\"a\": [{ \"b\": true }]}, [\"a\", 0, \"b\"], false)` results in `true`.", + Decl: types.NewFunction( + types.Args( + types.Named("object", types.NewObject(nil, types.NewDynamicProperty(types.A, types.A))).Description("object to get `key` from"), + types.Named("key", types.A).Description("key to lookup in `object`"), + types.Named("default", types.A).Description("default to use when lookup fails"), + ), + types.Named("value", types.A).Description("`object[key]` if present, otherwise `default`"), + ), + canSkipBctx: true, +} + +var ObjectKeys = &Builtin{ + Name: "object.keys", + Description: "Returns a set of an object's keys. " + + "For example: `object.keys({\"a\": 1, \"b\": true, \"c\": \"d\")` results in `{\"a\", \"b\", \"c\"}`.", + Decl: types.NewFunction( + types.Args( + types.Named("object", types.NewObject(nil, types.NewDynamicProperty(types.A, types.A))).Description("object to get keys from"), + ), + types.Named("value", types.SetOfAny).Description("set of `object`'s keys"), + ), + canSkipBctx: true, +} + +/* + * Encoding + */ +var encoding = category("encoding") + +var JSONMarshal = &Builtin{ + Name: "json.marshal", + Description: "Serializes the input term to JSON.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A).Description("the term to serialize"), + ), + types.Named("y", types.S).Description("the JSON string representation of `x`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var JSONMarshalWithOptions = &Builtin{ + Name: "json.marshal_with_options", + Description: "Serializes the input term JSON, with additional formatting options via the `opts` parameter. " + + "`opts` accepts keys `pretty` (enable multi-line/formatted JSON), `prefix` (string to prefix lines with, default empty string) and `indent` (string to indent with, default `\\t`).", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A).Description("the term to serialize"), + types.Named("opts", types.NewObject( + []*types.StaticProperty{ + types.NewStaticProperty("pretty", types.B), + types.NewStaticProperty("indent", types.S), + types.NewStaticProperty("prefix", types.S), + }, + types.NewDynamicProperty(types.S, types.A), + )).Description("encoding options"), + ), + types.Named("y", types.S).Description("the JSON string representation of `x`, with configured prefix/indent string(s) as appropriate"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var JSONUnmarshal = &Builtin{ + Name: "json.unmarshal", + Description: "Deserializes the input string.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("a JSON string"), + ), + types.Named("y", types.A).Description("the term deserialized from `x`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var JSONIsValid = &Builtin{ + Name: "json.is_valid", + Description: "Verifies the input string is a valid JSON document.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("a JSON string"), + ), + types.Named("result", types.B).Description("`true` if `x` is valid JSON, `false` otherwise"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var Base64Encode = &Builtin{ + Name: "base64.encode", + Description: "Serializes the input string into base64 encoding.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string to encode"), + ), + types.Named("y", types.S).Description("base64 serialization of `x`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var Base64Decode = &Builtin{ + Name: "base64.decode", + Description: "Deserializes the base64 encoded input string.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string to decode"), + ), + types.Named("y", types.S).Description("base64 deserialization of `x`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var Base64IsValid = &Builtin{ + Name: "base64.is_valid", + Description: "Verifies the input string is base64 encoded.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string to check"), + ), + types.Named("result", types.B).Description("`true` if `x` is valid base64 encoded value, `false` otherwise"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var Base64UrlEncode = &Builtin{ + Name: "base64url.encode", + Description: "Serializes the input string into base64url encoding.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string to encode"), + ), + types.Named("y", types.S).Description("base64url serialization of `x`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var Base64UrlEncodeNoPad = &Builtin{ + Name: "base64url.encode_no_pad", + Description: "Serializes the input string into base64url encoding without padding.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string to encode"), + ), + types.Named("y", types.S).Description("base64url serialization of `x`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var Base64UrlDecode = &Builtin{ + Name: "base64url.decode", + Description: "Deserializes the base64url encoded input string.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string to decode"), + ), + types.Named("y", types.S).Description("base64url deserialization of `x`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var URLQueryDecode = &Builtin{ + Name: "urlquery.decode", + Description: "Decodes a URL-encoded input string.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("the URL-encoded string"), + ), + types.Named("y", types.S).Description("URL-encoding deserialization of `x`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var URLQueryEncode = &Builtin{ + Name: "urlquery.encode", + Description: "Encodes the input string into a URL-encoded string.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("the string to encode"), + ), + types.Named("y", types.S).Description("URL-encoding serialization of `x`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var URLQueryEncodeObject = &Builtin{ + Name: "urlquery.encode_object", + Description: "Encodes the given object into a URL encoded query string.", + Decl: types.NewFunction( + types.Args( + types.Named("object", types.NewObject( + nil, + types.NewDynamicProperty( + types.S, + types.NewAny( + types.S, + types.NewArray(nil, types.S), + types.SetOfStr, + ), + ), + ), + ).Description("the object to encode"), + ), + types.Named("y", types.S).Description("the URL-encoded serialization of `object`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var URLQueryDecodeObject = &Builtin{ + Name: "urlquery.decode_object", + Description: "Decodes the given URL query string into an object.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("the query string"), + ), + types.Named("object", types.NewObject(nil, types.NewDynamicProperty( + types.S, + types.NewArray(nil, types.S)))).Description("the resulting object"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var YAMLMarshal = &Builtin{ + Name: "yaml.marshal", + Description: "Serializes the input term to YAML.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A).Description("the term to serialize"), + ), + types.Named("y", types.S).Description("the YAML string representation of `x`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var YAMLUnmarshal = &Builtin{ + Name: "yaml.unmarshal", + Description: "Deserializes the input string.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("a YAML string"), + ), + types.Named("y", types.A).Description("the term deserialized from `x`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +// YAMLIsValid verifies the input string is a valid YAML document. +var YAMLIsValid = &Builtin{ + Name: "yaml.is_valid", + Description: "Verifies the input string is a valid YAML document.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("a YAML string"), + ), + types.Named("result", types.B).Description("`true` if `x` is valid YAML, `false` otherwise"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var HexEncode = &Builtin{ + Name: "hex.encode", + Description: "Serializes the input string using hex-encoding.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string to encode"), + ), + types.Named("y", types.S).Description("serialization of `x` using hex-encoding"), + ), + Categories: encoding, + canSkipBctx: true, +} + +var HexDecode = &Builtin{ + Name: "hex.decode", + Description: "Deserializes the hex-encoded input string.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("a hex-encoded string"), + ), + types.Named("y", types.S).Description("deserialized from `x`"), + ), + Categories: encoding, + canSkipBctx: true, +} + +/** + * Tokens + */ +var tokensCat = category("tokens") + +var JWTDecode = &Builtin{ + Name: "io.jwt.decode", + Description: "Decodes a JSON Web Token and outputs it as an object.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token to decode"), + ), + types.Named("output", types.NewArray([]types.Type{ + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + types.S, + }, nil)).Description("`[header, payload, sig]`, where `header` and `payload` are objects; `sig` is the hexadecimal representation of the signature on the token."), + ), + Categories: tokensCat, + canSkipBctx: true, +} + +var JWTVerifyRS256 = &Builtin{ + Name: "io.jwt.verify_rs256", + Description: "Verifies if a RS256 JWT signature is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified"), + types.Named("certificate", types.S).Description("PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature"), + ), + types.Named("result", types.B).Description("`true` if the signature is valid, `false` otherwise"), + ), + Categories: tokensCat, + canSkipBctx: false, +} + +var JWTVerifyRS384 = &Builtin{ + Name: "io.jwt.verify_rs384", + Description: "Verifies if a RS384 JWT signature is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified"), + types.Named("certificate", types.S).Description("PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature"), + ), + types.Named("result", types.B).Description("`true` if the signature is valid, `false` otherwise"), + ), + Categories: tokensCat, + canSkipBctx: false, +} + +var JWTVerifyRS512 = &Builtin{ + Name: "io.jwt.verify_rs512", + Description: "Verifies if a RS512 JWT signature is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified"), + types.Named("certificate", types.S).Description("PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature"), + ), + types.Named("result", types.B).Description("`true` if the signature is valid, `false` otherwise"), + ), + Categories: tokensCat, + canSkipBctx: false, +} + +var JWTVerifyPS256 = &Builtin{ + Name: "io.jwt.verify_ps256", + Description: "Verifies if a PS256 JWT signature is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified"), + types.Named("certificate", types.S).Description("PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature"), + ), + types.Named("result", types.B).Description("`true` if the signature is valid, `false` otherwise"), + ), + Categories: tokensCat, + canSkipBctx: false, +} + +var JWTVerifyPS384 = &Builtin{ + Name: "io.jwt.verify_ps384", + Description: "Verifies if a PS384 JWT signature is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified"), + types.Named("certificate", types.S).Description("PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature"), + ), + types.Named("result", types.B).Description("`true` if the signature is valid, `false` otherwise"), + ), + Categories: tokensCat, + canSkipBctx: false, +} + +var JWTVerifyPS512 = &Builtin{ + Name: "io.jwt.verify_ps512", + Description: "Verifies if a PS512 JWT signature is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified"), + types.Named("certificate", types.S).Description("PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature"), + ), + types.Named("result", types.B).Description("`true` if the signature is valid, `false` otherwise"), + ), + Categories: tokensCat, + canSkipBctx: false, +} + +var JWTVerifyES256 = &Builtin{ + Name: "io.jwt.verify_es256", + Description: "Verifies if a ES256 JWT signature is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified"), + types.Named("certificate", types.S).Description("PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature"), + ), + types.Named("result", types.B).Description("`true` if the signature is valid, `false` otherwise"), + ), + Categories: tokensCat, + canSkipBctx: false, +} + +var JWTVerifyES384 = &Builtin{ + Name: "io.jwt.verify_es384", + Description: "Verifies if a ES384 JWT signature is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified"), + types.Named("certificate", types.S).Description("PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature"), + ), + types.Named("result", types.B).Description("`true` if the signature is valid, `false` otherwise"), + ), + Categories: tokensCat, + canSkipBctx: false, +} + +var JWTVerifyES512 = &Builtin{ + Name: "io.jwt.verify_es512", + Description: "Verifies if a ES512 JWT signature is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified"), + types.Named("certificate", types.S).Description("PEM encoded certificate, PEM encoded public key, or the JWK key (set) used to verify the signature"), + ), + types.Named("result", types.B).Description("`true` if the signature is valid, `false` otherwise"), + ), + Categories: tokensCat, + canSkipBctx: false, +} + +var JWTVerifyHS256 = &Builtin{ + Name: "io.jwt.verify_hs256", + Description: "Verifies if a HS256 (secret) JWT signature is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified"), + types.Named("secret", types.S).Description("plain text secret used to verify the signature"), + ), + types.Named("result", types.B).Description("`true` if the signature is valid, `false` otherwise"), + ), + Categories: tokensCat, + canSkipBctx: false, +} + +var JWTVerifyHS384 = &Builtin{ + Name: "io.jwt.verify_hs384", + Description: "Verifies if a HS384 (secret) JWT signature is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified"), + types.Named("secret", types.S).Description("plain text secret used to verify the signature"), + ), + types.Named("result", types.B).Description("`true` if the signature is valid, `false` otherwise"), + ), + Categories: tokensCat, + canSkipBctx: false, +} + +var JWTVerifyHS512 = &Builtin{ + Name: "io.jwt.verify_hs512", + Description: "Verifies if a HS512 (secret) JWT signature is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified"), + types.Named("secret", types.S).Description("plain text secret used to verify the signature"), + ), + types.Named("result", types.B).Description("`true` if the signature is valid, `false` otherwise"), + ), + Categories: tokensCat, + canSkipBctx: false, +} + +// Marked non-deterministic because it relies on time internally. +var JWTDecodeVerify = &Builtin{ + Name: "io.jwt.decode_verify", + Description: `Verifies a JWT signature under parameterized constraints and decodes the claims if it is valid. +Supports the following algorithms: HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512, PS256, PS384 and PS512.`, + Decl: types.NewFunction( + types.Args( + types.Named("jwt", types.S).Description("JWT token whose signature is to be verified and whose claims are to be checked"), + types.Named("constraints", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))).Description("claim verification constraints"), + ), + types.Named("output", types.NewArray([]types.Type{ + types.B, + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + }, nil)).Description("`[valid, header, payload]`: if the input token is verified and meets the requirements of `constraints` then `valid` is `true`; `header` and `payload` are objects containing the JOSE header and the JWT claim set; otherwise, `valid` is `false`, `header` and `payload` are `{}`"), + ), + Categories: tokensCat, + Nondeterministic: true, + canSkipBctx: false, +} + +var tokenSign = category("tokensign") + +// Marked non-deterministic because it relies on RNG internally. +var JWTEncodeSignRaw = &Builtin{ + Name: "io.jwt.encode_sign_raw", + Description: "Encodes and optionally signs a JSON Web Token.", + Decl: types.NewFunction( + types.Args( + types.Named("headers", types.S).Description("JWS Protected Header"), + types.Named("payload", types.S).Description("JWS Payload"), + types.Named("key", types.S).Description("JSON Web Key (RFC7517)"), + ), + types.Named("output", types.S).Description("signed JWT"), + ), + Categories: tokenSign, + Nondeterministic: true, + canSkipBctx: false, +} + +// Marked non-deterministic because it relies on RNG internally. +var JWTEncodeSign = &Builtin{ + Name: "io.jwt.encode_sign", + Description: "Encodes and optionally signs a JSON Web Token. Inputs are taken as objects, not encoded strings (see `io.jwt.encode_sign_raw`).", + Decl: types.NewFunction( + types.Args( + types.Named("headers", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))).Description("JWS Protected Header"), + types.Named("payload", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))).Description("JWS Payload"), + types.Named("key", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))).Description("JSON Web Key (RFC7517)"), + ), + types.Named("output", types.S).Description("signed JWT"), + ), + Categories: tokenSign, + Nondeterministic: true, + canSkipBctx: false, +} + +/** + * Time + */ + +// Marked non-deterministic because it relies on time directly. +var NowNanos = &Builtin{ + Name: "time.now_ns", + Description: "Returns the current time since epoch in nanoseconds.", + Decl: types.NewFunction( + nil, + types.Named("now", types.N).Description("nanoseconds since epoch"), + ), + Nondeterministic: true, + canSkipBctx: false, +} + +var ParseNanos = &Builtin{ + Name: "time.parse_ns", + Description: "Returns the time in nanoseconds parsed from the string in the given format. `undefined` if the result would be outside the valid time range that can fit within an `int64`.", + Decl: types.NewFunction( + types.Args( + types.Named("layout", types.S).Description("format used for parsing, see the [Go `time` package documentation](https://golang.org/pkg/time/#Parse) for more details"), + types.Named("value", types.S).Description("input to parse according to `layout`"), + ), + types.Named("ns", types.N).Description("`value` in nanoseconds since epoch"), + ), + canSkipBctx: true, +} + +var ParseRFC3339Nanos = &Builtin{ + Name: "time.parse_rfc3339_ns", + Description: "Returns the time in nanoseconds parsed from the string in RFC3339 format. `undefined` if the result would be outside the valid time range that can fit within an `int64`.", + Decl: types.NewFunction( + types.Args( + types.Named("value", types.S).Description("input string to parse in RFC3339 format"), + ), + types.Named("ns", types.N).Description("`value` in nanoseconds since epoch"), + ), + canSkipBctx: true, +} + +var ParseDurationNanos = &Builtin{ + Name: "time.parse_duration_ns", + Description: "Returns the duration in nanoseconds represented by a string.", + Decl: types.NewFunction( + types.Args( + types.Named("duration", types.S).Description("a duration like \"3m\"; see the [Go `time` package documentation](https://golang.org/pkg/time/#ParseDuration) for more details"), + ), + types.Named("ns", types.N).Description("the `duration` in nanoseconds"), + ), + canSkipBctx: true, +} + +var Format = &Builtin{ + Name: "time.format", + Description: "Returns the formatted timestamp for the nanoseconds since epoch.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.NewAny( + types.N, + types.NewArray([]types.Type{types.N, types.S}, nil), + types.NewArray([]types.Type{types.N, types.S, types.S}, nil), + )).Description("a number representing the nanoseconds since the epoch (UTC); or a two-element array of the nanoseconds, and a timezone string; or a three-element array of ns, timezone string and a layout string or golang defined formatting constant (see golang supported time formats)"), + ), + types.Named("formatted timestamp", types.S).Description("the formatted timestamp represented for the nanoseconds since the epoch in the supplied timezone (or UTC)"), + ), + canSkipBctx: true, +} + +var Date = &Builtin{ + Name: "time.date", + Description: "Returns the `[year, month, day]` for the nanoseconds since epoch.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.NewAny( + types.N, + types.NewArray([]types.Type{types.N, types.S}, nil), + )).Description("a number representing the nanoseconds since the epoch (UTC); or a two-element array of the nanoseconds, and a timezone string"), + ), + types.Named("date", types.NewArray([]types.Type{types.N, types.N, types.N}, nil)).Description("an array of `year`, `month` (1-12), and `day` (1-31)"), + ), + canSkipBctx: true, +} + +var Clock = &Builtin{ + Name: "time.clock", + Description: "Returns the `[hour, minute, second]` of the day for the nanoseconds since epoch.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.NewAny( + types.N, + types.NewArray([]types.Type{types.N, types.S}, nil), + )).Description("a number representing the nanoseconds since the epoch (UTC); or a two-element array of the nanoseconds, and a timezone string"), + ), + types.Named("output", types.NewArray([]types.Type{types.N, types.N, types.N}, nil)). + Description("the `hour`, `minute` (0-59), and `second` (0-59) representing the time of day for the nanoseconds since epoch in the supplied timezone (or UTC)"), + ), + canSkipBctx: true, +} + +var Weekday = &Builtin{ + Name: "time.weekday", + Description: "Returns the day of the week (Monday, Tuesday, ...) for the nanoseconds since epoch.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.NewAny( + types.N, + types.NewArray([]types.Type{types.N, types.S}, nil), + )).Description("a number representing the nanoseconds since the epoch (UTC); or a two-element array of the nanoseconds, and a timezone string"), + ), + types.Named("day", types.S).Description("the weekday represented by `ns` nanoseconds since the epoch in the supplied timezone (or UTC)"), + ), + canSkipBctx: true, +} + +var AddDate = &Builtin{ + Name: "time.add_date", + Description: "Returns the nanoseconds since epoch after adding years, months and days to nanoseconds. Month & day values outside their usual ranges after the operation and will be normalized - for example, October 32 would become November 1. `undefined` if the result would be outside the valid time range that can fit within an `int64`.", + Decl: types.NewFunction( + types.Args( + types.Named("ns", types.N).Description("nanoseconds since the epoch"), + types.Named("years", types.N).Description("number of years to add"), + types.Named("months", types.N).Description("number of months to add"), + types.Named("days", types.N).Description("number of days to add"), + ), + types.Named("output", types.N).Description("nanoseconds since the epoch representing the input time, with years, months and days added"), + ), + canSkipBctx: true, +} + +var Diff = &Builtin{ + Name: "time.diff", + Description: "Returns the difference between two unix timestamps in nanoseconds (with optional timezone strings).", + Decl: types.NewFunction( + types.Args( + types.Named("ns1", types.NewAny( + types.N, + types.NewArray([]types.Type{types.N, types.S}, nil), + )).Description("nanoseconds since the epoch; or a two-element array of the nanoseconds, and a timezone string"), + types.Named("ns2", types.NewAny( + types.N, + types.NewArray([]types.Type{types.N, types.S}, nil), + )).Description("nanoseconds since the epoch; or a two-element array of the nanoseconds, and a timezone string"), + ), + types.Named("output", types.NewArray([]types.Type{types.N, types.N, types.N, types.N, types.N, types.N}, nil)).Description("difference between `ns1` and `ns2` (in their supplied timezones, if supplied, or UTC) as array of numbers: `[years, months, days, hours, minutes, seconds]`"), + ), + canSkipBctx: true, +} + +/** + * Crypto. + */ + +var CryptoX509ParseCertificates = &Builtin{ + Name: "crypto.x509.parse_certificates", + Description: `Returns zero or more certificates from the given encoded string containing +DER certificate data. + +If the input is empty, the function will return null. The input string should be a list of one or more +concatenated PEM blocks. The whole input of concatenated PEM blocks can optionally be Base64 encoded.`, + Decl: types.NewFunction( + types.Args( + types.Named("certs", types.S).Description("base64 encoded DER or PEM data containing one or more certificates or a PEM string of one or more certificates"), + ), + types.Named("output", types.NewArray(nil, types.NewObject(nil, types.NewDynamicProperty(types.S, types.A)))).Description("parsed X.509 certificates represented as objects"), + ), + canSkipBctx: true, +} + +var CryptoX509ParseAndVerifyCertificates = &Builtin{ + Name: "crypto.x509.parse_and_verify_certificates", + Description: `Returns one or more certificates from the given string containing PEM +or base64 encoded DER certificates after verifying the supplied certificates form a complete +certificate chain back to a trusted root. + +The first certificate is treated as the root and the last is treated as the leaf, +with all others being treated as intermediates.`, + Decl: types.NewFunction( + types.Args( + types.Named("certs", types.S).Description("base64 encoded DER or PEM data containing two or more certificates where the first is a root CA, the last is a leaf certificate, and all others are intermediate CAs"), + ), + types.Named("output", types.NewArray([]types.Type{ + types.B, + types.NewArray(nil, types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))), + }, nil)).Description("array of `[valid, certs]`: if the input certificate chain could be verified then `valid` is `true` and `certs` is an array of X.509 certificates represented as objects; if the input certificate chain could not be verified then `valid` is `false` and `certs` is `[]`"), + ), + canSkipBctx: true, +} + +var CryptoX509ParseAndVerifyCertificatesWithOptions = &Builtin{ + Name: "crypto.x509.parse_and_verify_certificates_with_options", + Description: `Returns one or more certificates from the given string containing PEM +or base64 encoded DER certificates after verifying the supplied certificates form a complete +certificate chain back to a trusted root. A config option passed as the second argument can +be used to configure the validation options used. + +The first certificate is treated as the root and the last is treated as the leaf, +with all others being treated as intermediates.`, + Decl: types.NewFunction( + types.Args( + types.Named("certs", types.S).Description("base64 encoded DER or PEM data containing two or more certificates where the first is a root CA, the last is a leaf certificate, and all others are intermediate CAs"), + types.Named("options", types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.A), + )).Description("object containing extra configs to verify the validity of certificates. `options` object supports four fields which maps to same fields in [x509.VerifyOptions struct](https://pkg.go.dev/crypto/x509#VerifyOptions). `DNSName`, `CurrentTime`: Nanoseconds since the Unix Epoch as a number, `MaxConstraintComparisons` and `KeyUsages`. `KeyUsages` is list and can have possible values as in: `\"KeyUsageAny\"`, `\"KeyUsageServerAuth\"`, `\"KeyUsageClientAuth\"`, `\"KeyUsageCodeSigning\"`, `\"KeyUsageEmailProtection\"`, `\"KeyUsageIPSECEndSystem\"`, `\"KeyUsageIPSECTunnel\"`, `\"KeyUsageIPSECUser\"`, `\"KeyUsageTimeStamping\"`, `\"KeyUsageOCSPSigning\"`, `\"KeyUsageMicrosoftServerGatedCrypto\"`, `\"KeyUsageNetscapeServerGatedCrypto\"`, `\"KeyUsageMicrosoftCommercialCodeSigning\"`, `\"KeyUsageMicrosoftKernelCodeSigning\"` "), + ), + types.Named("output", types.NewArray([]types.Type{ + types.B, + types.NewArray(nil, types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))), + }, nil)).Description("array of `[valid, certs]`: if the input certificate chain could be verified then `valid` is `true` and `certs` is an array of X.509 certificates represented as objects; if the input certificate chain could not be verified then `valid` is `false` and `certs` is `[]`"), + ), + canSkipBctx: true, +} + +var CryptoX509ParseCertificateRequest = &Builtin{ + Name: "crypto.x509.parse_certificate_request", + Description: "Returns a PKCS #10 certificate signing request from the given PEM-encoded PKCS#10 certificate signing request.", + Decl: types.NewFunction( + types.Args( + types.Named("csr", types.S).Description("base64 string containing either a PEM encoded or DER CSR or a string containing a PEM CSR"), + ), + types.Named("output", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))).Description("X.509 CSR represented as an object"), + ), + canSkipBctx: true, +} + +var CryptoX509ParseKeyPair = &Builtin{ + Name: "crypto.x509.parse_keypair", + Description: "Returns a valid key pair", + Decl: types.NewFunction( + types.Args( + types.Named("cert", types.S).Description("string containing PEM or base64 encoded DER certificates"), + types.Named("pem", types.S).Description("string containing PEM or base64 encoded DER keys"), + ), + types.Named("output", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))).Description("if key pair is valid, returns the tls.certificate(https://pkg.go.dev/crypto/tls#Certificate) as an object. If the key pair is invalid, nil and an error are returned."), + ), + canSkipBctx: true, +} +var CryptoX509ParseRSAPrivateKey = &Builtin{ + Name: "crypto.x509.parse_rsa_private_key", + Description: "Returns a JWK for signing a JWT from the given PEM-encoded RSA private key.", + Decl: types.NewFunction( + types.Args( + types.Named("pem", types.S).Description("base64 string containing a PEM encoded RSA private key"), + ), + types.Named("output", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))).Description("JWK as an object"), + ), + canSkipBctx: true, +} + +var CryptoParsePrivateKeys = &Builtin{ + Name: "crypto.parse_private_keys", + Description: `Returns zero or more private keys from the given encoded string containing DER certificate data. + +If the input is empty, the function will return null. The input string should be a list of one or more concatenated PEM blocks. The whole input of concatenated PEM blocks can optionally be Base64 encoded.`, + Decl: types.NewFunction( + types.Args( + types.Named("keys", types.S).Description("PEM encoded data containing one or more private keys as concatenated blocks. Optionally Base64 encoded."), + ), + types.Named("output", types.NewArray(nil, types.NewObject(nil, types.NewDynamicProperty(types.S, types.A)))).Description("parsed private keys represented as objects"), + ), + canSkipBctx: true, +} + +var CryptoMd5 = &Builtin{ + Name: "crypto.md5", + Description: "Returns a string representing the input string hashed with the MD5 function", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("input string"), + ), + types.Named("y", types.S).Description("MD5-hash of `x`"), + ), + canSkipBctx: true, +} + +var CryptoSha1 = &Builtin{ + Name: "crypto.sha1", + Description: "Returns a string representing the input string hashed with the SHA1 function", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("input string"), + ), + types.Named("y", types.S).Description("SHA1-hash of `x`"), + ), + canSkipBctx: true, +} + +var CryptoSha256 = &Builtin{ + Name: "crypto.sha256", + Description: "Returns a string representing the input string hashed with the SHA256 function", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("input string"), + ), + types.Named("y", types.S).Description("SHA256-hash of `x`"), + ), + canSkipBctx: true, +} + +var CryptoHmacMd5 = &Builtin{ + Name: "crypto.hmac.md5", + Description: "Returns a string representing the MD5 HMAC of the input message using the input key.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("input string"), + types.Named("key", types.S).Description("key to use"), + ), + types.Named("y", types.S).Description("MD5-HMAC of `x`"), + ), + canSkipBctx: true, +} + +var CryptoHmacSha1 = &Builtin{ + Name: "crypto.hmac.sha1", + Description: "Returns a string representing the SHA1 HMAC of the input message using the input key.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("input string"), + types.Named("key", types.S).Description("key to use"), + ), + types.Named("y", types.S).Description("SHA1-HMAC of `x`"), + ), + canSkipBctx: true, +} + +var CryptoHmacSha256 = &Builtin{ + Name: "crypto.hmac.sha256", + Description: "Returns a string representing the SHA256 HMAC of the input message using the input key.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("input string"), + types.Named("key", types.S).Description("key to use"), + ), + types.Named("y", types.S).Description("SHA256-HMAC of `x`"), + ), + canSkipBctx: true, +} + +var CryptoHmacSha512 = &Builtin{ + Name: "crypto.hmac.sha512", + Description: "Returns a string representing the SHA512 HMAC of the input message using the input key.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("input string"), + types.Named("key", types.S).Description("key to use"), + ), + types.Named("y", types.S).Description("SHA512-HMAC of `x`"), + ), + canSkipBctx: true, +} + +var CryptoHmacEqual = &Builtin{ + Name: "crypto.hmac.equal", + Description: "Returns a boolean representing the result of comparing two MACs for equality without leaking timing information.", + Decl: types.NewFunction( + types.Args( + types.Named("mac1", types.S).Description("mac1 to compare"), + types.Named("mac2", types.S).Description("mac2 to compare"), + ), + types.Named("result", types.B).Description("`true` if the MACs are equals, `false` otherwise"), + ), + canSkipBctx: true, +} + +/** + * Graphs. + */ +var graphs = category("graph") + +var WalkBuiltin = &Builtin{ + Name: "walk", + Relation: true, + Description: "Generates `[path, value]` tuples for all nested documents of `x` (recursively). Queries can use `walk` to traverse documents nested under `x`.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A).Description("value to walk"), + ), + types.Named("output", types.NewArray( + []types.Type{ + types.NewArray(nil, types.A), + types.A, + }, + nil, + )).Description("pairs of `path` and `value`: `path` is an array representing the pointer to `value` in `x`. If `path` is assigned a wildcard (`_`), the `walk` function will skip path creation entirely for faster evaluation."), + ), + Categories: graphs, + canSkipBctx: true, +} + +var ReachableBuiltin = &Builtin{ + Name: "graph.reachable", + Description: "Computes the set of reachable nodes in the graph from a set of starting nodes.", + Decl: types.NewFunction( + types.Args( + types.Named("graph", types.NewObject( + nil, + types.NewDynamicProperty( + types.A, + types.NewAny( + types.SetOfAny, + types.NewArray(nil, types.A)), + )), + ).Description("object containing a set or array of neighboring vertices"), + types.Named("initial", types.NewAny(types.SetOfAny, types.NewArray(nil, types.A))).Description("set or array of root vertices"), + ), + types.Named("output", types.SetOfAny).Description("set of vertices reachable from the `initial` vertices in the directed `graph`"), + ), + canSkipBctx: true, +} + +var ReachablePathsBuiltin = &Builtin{ + Name: "graph.reachable_paths", + Description: "Computes the set of reachable paths in the graph from a set of starting nodes.", + Decl: types.NewFunction( + types.Args( + types.Named("graph", types.NewObject( + nil, + types.NewDynamicProperty( + types.A, + types.NewAny( + types.SetOfAny, + types.NewArray(nil, types.A)), + )), + ).Description("object containing a set or array of root vertices"), + types.Named("initial", types.NewAny(types.SetOfAny, types.NewArray(nil, types.A))).Description("initial paths"), // TODO(sr): copied. is that correct? + ), + types.Named("output", types.NewSet(types.NewArray(nil, types.A))).Description("paths reachable from the `initial` vertices in the directed `graph`"), + ), + canSkipBctx: true, +} + +/** + * Type + */ +var typesCat = category("types") + +var IsNumber = &Builtin{ + Name: "is_number", + Description: "Returns `true` if the input value is a number.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A).Description("input value"), + ), + types.Named("result", types.B).Description("`true` if `x` is a number, `false` otherwise."), + ), + Categories: typesCat, + canSkipBctx: true, +} + +var IsString = &Builtin{ + Name: "is_string", + Description: "Returns `true` if the input value is a string.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A).Description("input value"), + ), + types.Named("result", types.B).Description("`true` if `x` is a string, `false` otherwise."), + ), + Categories: typesCat, + canSkipBctx: true, +} + +var IsBoolean = &Builtin{ + Name: "is_boolean", + Description: "Returns `true` if the input value is a boolean.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A).Description("input value"), + ), + types.Named("result", types.B).Description("`true` if `x` is an boolean, `false` otherwise."), + ), + Categories: typesCat, + canSkipBctx: true, +} + +var IsArray = &Builtin{ + Name: "is_array", + Description: "Returns `true` if the input value is an array.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A).Description("input value"), + ), + types.Named("result", types.B).Description("`true` if `x` is an array, `false` otherwise."), + ), + Categories: typesCat, + canSkipBctx: true, +} + +var IsSet = &Builtin{ + Name: "is_set", + Description: "Returns `true` if the input value is a set.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A).Description("input value"), + ), + types.Named("result", types.B).Description("`true` if `x` is a set, `false` otherwise."), + ), + Categories: typesCat, + canSkipBctx: true, +} + +var IsObject = &Builtin{ + Name: "is_object", + Description: "Returns true if the input value is an object", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A).Description("input value"), + ), + types.Named("result", types.B).Description("`true` if `x` is an object, `false` otherwise."), + ), + Categories: typesCat, + canSkipBctx: true, +} + +var IsNull = &Builtin{ + Name: "is_null", + Description: "Returns `true` if the input value is null.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A).Description("input value"), + ), + types.Named("result", types.B).Description("`true` if `x` is null, `false` otherwise."), + ), + Categories: typesCat, + canSkipBctx: true, +} + +/** + * Type Name + */ + +// TypeNameBuiltin returns the type of the input. +var TypeNameBuiltin = &Builtin{ + Name: "type_name", + Description: "Returns the type of its input value.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.A).Description("input value"), + ), + types.Named("type", types.S).Description(`one of "null", "boolean", "number", "string", "array", "object", "set"`), + ), + Categories: typesCat, + canSkipBctx: true, +} + +/** + * HTTP Request + */ + +// Marked non-deterministic because HTTP request results can be non-deterministic. +var HTTPSend = &Builtin{ + Name: "http.send", + Description: "Returns a HTTP response to the given HTTP request.", + Decl: types.NewFunction( + types.Args( + types.Named("request", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))). + Description("the HTTP request object"), + ), + types.Named("response", types.NewObject(nil, types.NewDynamicProperty(types.A, types.A))). + Description("the HTTP response object"), + ), + Nondeterministic: true, + canSkipBctx: false, +} + +/** + * GraphQL + */ + +// GraphQLParse returns a pair of AST objects from parsing/validation. +var GraphQLParse = &Builtin{ + Name: "graphql.parse", + Description: "Returns AST objects for a given GraphQL query and schema after validating the query against the schema. Returns undefined if errors were encountered during parsing or validation. The query and/or schema can be either GraphQL strings or AST objects from the other GraphQL builtin functions.", + Decl: types.NewFunction( + types.Args( + types.Named("query", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("the GraphQL query"), + types.Named("schema", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("the GraphQL schema"), + ), + types.Named("output", types.NewArray([]types.Type{ + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + }, nil)).Description("`output` is of the form `[query_ast, schema_ast]`. If the GraphQL query is valid given the provided schema, then `query_ast` and `schema_ast` are objects describing the ASTs for the query and schema."), + ), + canSkipBctx: false, +} + +// GraphQLParseAndVerify returns a boolean and a pair of AST object from parsing/validation. +var GraphQLParseAndVerify = &Builtin{ + Name: "graphql.parse_and_verify", + Description: "Returns a boolean indicating success or failure alongside the parsed ASTs for a given GraphQL query and schema after validating the query against the schema. The query and/or schema can be either GraphQL strings or AST objects from the other GraphQL builtin functions.", + Decl: types.NewFunction( + types.Args( + types.Named("query", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("the GraphQL query"), + types.Named("schema", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("the GraphQL schema"), + ), + types.Named("output", types.NewArray([]types.Type{ + types.B, + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + }, nil)).Description(" `output` is of the form `[valid, query_ast, schema_ast]`. If the query is valid given the provided schema, then `valid` is `true`, and `query_ast` and `schema_ast` are objects describing the ASTs for the GraphQL query and schema. Otherwise, `valid` is `false` and `query_ast` and `schema_ast` are `{}`."), + ), + canSkipBctx: false, +} + +// GraphQLParseQuery parses the input GraphQL query and returns a JSON +// representation of its AST. +var GraphQLParseQuery = &Builtin{ + Name: "graphql.parse_query", + Description: "Returns an AST object for a GraphQL query.", + Decl: types.NewFunction( + types.Args( + types.Named("query", types.S).Description("GraphQL query string"), + ), + types.Named("output", types.NewObject(nil, types.NewDynamicProperty(types.A, types.A))).Description("AST object for the GraphQL query."), + ), + canSkipBctx: true, +} + +// GraphQLParseSchema parses the input GraphQL schema and returns a JSON +// representation of its AST. +var GraphQLParseSchema = &Builtin{ + Name: "graphql.parse_schema", + Description: "Returns an AST object for a GraphQL schema.", + Decl: types.NewFunction( + types.Args( + types.Named("schema", types.S).Description("GraphQL schema string"), + ), + types.Named("output", types.NewObject(nil, types.NewDynamicProperty(types.A, types.A))).Description("AST object for the GraphQL schema."), + ), + canSkipBctx: false, +} + +// GraphQLIsValid returns true if a GraphQL query is valid with a given +// schema, and returns false for all other inputs. +var GraphQLIsValid = &Builtin{ + Name: "graphql.is_valid", + Description: "Checks that a GraphQL query is valid against a given schema. The query and/or schema can be either GraphQL strings or AST objects from the other GraphQL builtin functions.", + Decl: types.NewFunction( + types.Args( + types.Named("query", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("the GraphQL query"), + types.Named("schema", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("the GraphQL schema"), + ), + types.Named("output", types.B).Description("`true` if the query is valid under the given schema. `false` otherwise."), + ), + canSkipBctx: false, +} + +// GraphQLSchemaIsValid returns true if the input is valid GraphQL schema, +// and returns false for all other inputs. +var GraphQLSchemaIsValid = &Builtin{ + Name: "graphql.schema_is_valid", + Description: "Checks that the input is a valid GraphQL schema. The schema can be either a GraphQL string or an AST object from the other GraphQL builtin functions.", + Decl: types.NewFunction( + types.Args( + types.Named("schema", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("the schema to verify"), + ), + types.Named("output", types.B).Description("`true` if the schema is a valid GraphQL schema. `false` otherwise."), + ), + canSkipBctx: false, +} + +/** + * JSON Schema + */ + +// JSONSchemaVerify returns empty string if the input is valid JSON schema +// and returns error string for all other inputs. +var JSONSchemaVerify = &Builtin{ + Name: "json.verify_schema", + Description: "Checks that the input is a valid JSON schema object. The schema can be either a JSON string or an JSON object.", + Decl: types.NewFunction( + types.Args( + types.Named("schema", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("the schema to verify"), + ), + types.Named("output", types.NewArray([]types.Type{ + types.B, + types.NewAny(types.S, types.Null{}), + }, nil)). + Description("`output` is of the form `[valid, error]`. If the schema is valid, then `valid` is `true`, and `error` is `null`. Otherwise, `valid` is `false` and `error` is a string describing the error."), + ), + Categories: objectCat, + canSkipBctx: true, +} + +// JSONMatchSchema returns empty array if the document matches the JSON schema, +// and returns non-empty array with error objects otherwise. +var JSONMatchSchema = &Builtin{ + Name: "json.match_schema", + Description: "Checks that the document matches the JSON schema.", + Decl: types.NewFunction( + types.Args( + types.Named("document", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("document to verify by schema"), + types.Named("schema", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("schema to verify document by"), + ), + types.Named("output", types.NewArray([]types.Type{ + types.B, + types.NewArray( + nil, types.NewObject( + []*types.StaticProperty{ + {Key: "error", Value: types.S}, + {Key: "type", Value: types.S}, + {Key: "field", Value: types.S}, + {Key: "desc", Value: types.S}, + }, + nil, + ), + ), + }, nil)). + Description("`output` is of the form `[match, errors]`. If the document is valid given the schema, then `match` is `true`, and `errors` is an empty array. Otherwise, `match` is `false` and `errors` is an array of objects describing the error(s)."), + ), + Categories: objectCat, + canSkipBctx: false, +} + +/** + * Cloud Provider Helper Functions + */ +var providersAWSCat = category("providers.aws") + +var ProvidersAWSSignReqObj = &Builtin{ + Name: "providers.aws.sign_req", + Description: "Signs an HTTP request object for Amazon Web Services. Currently implements [AWS Signature Version 4 request signing](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html) by the `Authorization` header method.", + Decl: types.NewFunction( + types.Args( + types.Named("request", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))). + Description("HTTP request object"), + types.Named("aws_config", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))). + Description("AWS configuration object"), + types.Named("time_ns", types.N).Description("nanoseconds since the epoch"), + ), + types.Named("signed_request", types.NewObject(nil, types.NewDynamicProperty(types.A, types.A))). + Description("HTTP request object with `Authorization` header"), + ), + Categories: providersAWSCat, + canSkipBctx: true, +} + +/** + * Rego + */ + +var RegoParseModule = &Builtin{ + Name: "rego.parse_module", + Description: "Parses the input Rego string and returns an object representation of the AST.", + Decl: types.NewFunction( + types.Args( + types.Named("filename", types.S).Description("file name to attach to AST nodes' locations"), + types.Named("rego", types.S).Description("Rego module"), + ), + types.Named("output", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))). + Description("AST object for the Rego module"), + ), + canSkipBctx: true, +} + +var RegoMetadataChain = &Builtin{ + Name: "rego.metadata.chain", + Description: `Returns the chain of metadata for the active rule. +Ordered starting at the active rule, going outward to the most distant node in its package ancestry. +A chain entry is a JSON document with two members: "path", an array representing the path of the node; and "annotations", a JSON document containing the annotations declared for the node. +The first entry in the chain always points to the active rule, even if it has no declared annotations (in which case the "annotations" member is not present).`, + Decl: types.NewFunction( + types.Args(), + types.Named("chain", types.NewArray(nil, types.A)).Description("each array entry represents a node in the path ancestry (chain) of the active rule that also has declared annotations"), + ), + canSkipBctx: true, +} + +// RegoMetadataRule returns the metadata for the active rule +var RegoMetadataRule = &Builtin{ + Name: "rego.metadata.rule", + Description: "Returns annotations declared for the active rule and using the _rule_ scope.", + Decl: types.NewFunction( + types.Args(), + types.Named("output", types.A).Description("\"rule\" scope annotations for this rule; empty object if no annotations exist"), + ), + canSkipBctx: true, +} + +/** + * OPA + */ + +// Marked non-deterministic because of unpredictable config/environment-dependent results. +var OPARuntime = &Builtin{ + Name: "opa.runtime", + Description: "Returns an object that describes the runtime environment where OPA is deployed.", + Decl: types.NewFunction( + nil, + types.Named("output", types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))). + Description("includes a `config` key if OPA was started with a configuration file; an `env` key containing the environment variables that the OPA process was started with; includes `version` and `commit` keys containing the version and build commit of OPA."), + ), + Nondeterministic: true, + canSkipBctx: false, +} + +/** + * Trace + */ +var tracing = category("tracing") + +var Trace = &Builtin{ + Name: "trace", + Description: "Emits `note` as a `Note` event in the query explanation. Query explanations show the exact expressions evaluated by OPA during policy execution. For example, `trace(\"Hello There!\")` includes `Note \"Hello There!\"` in the query explanation. To include variables in the message, use `sprintf`. For example, `person := \"Bob\"; trace(sprintf(\"Hello There! %v\", [person]))` will emit `Note \"Hello There! Bob\"` inside of the explanation.", + Decl: types.NewFunction( + types.Args( + types.Named("note", types.S).Description("the note to include"), + ), + types.Named("result", types.B).Description("always `true`"), + ), + Categories: tracing, + canSkipBctx: false, +} + +/** + * Glob + */ + +var GlobMatch = &Builtin{ + Name: "glob.match", + Description: "Parses and matches strings against the glob notation. Not to be confused with `regex.globs_match`.", + Decl: types.NewFunction( + types.Args( + types.Named("pattern", types.S).Description("glob pattern"), + types.Named("delimiters", types.NewAny( + types.NewArray(nil, types.S), + types.Nl, + )).Description("glob pattern delimiters, e.g. `[\".\", \":\"]`, defaults to `[\".\"]` if unset. If `delimiters` is `null`, glob match without delimiter."), + types.Named("match", types.S).Description("string to match against `pattern`"), + ), + types.Named("result", types.B).Description("true if `match` can be found in `pattern` which is separated by `delimiters`"), + ), + canSkipBctx: false, +} + +var GlobQuoteMeta = &Builtin{ + Name: "glob.quote_meta", + Description: "Returns a string which represents a version of the pattern where all asterisks have been escaped.", + Decl: types.NewFunction( + types.Args( + types.Named("pattern", types.S).Description("glob pattern"), + ), + types.Named("output", types.S).Description("the escaped string of `pattern`"), + ), + canSkipBctx: true, + // TODO(sr): example for this was: Calling ``glob.quote_meta("*.github.com", output)`` returns ``\\*.github.com`` as ``output``. +} + +/** + * Networking + */ + +var NetCIDRIntersects = &Builtin{ + Name: "net.cidr_intersects", + Description: "Checks if a CIDR intersects with another CIDR (e.g. `192.168.0.0/16` overlaps with `192.168.1.0/24`). Supports both IPv4 and IPv6 notations.", + Decl: types.NewFunction( + types.Args( + types.Named("cidr1", types.S).Description("first CIDR"), + types.Named("cidr2", types.S).Description("second CIDR"), + ), + types.Named("result", types.B).Description("`true` if `cidr1` intersects with `cidr2`"), + ), + canSkipBctx: true, +} + +var NetCIDRExpand = &Builtin{ + Name: "net.cidr_expand", + Description: "Expands CIDR to set of hosts (e.g., `net.cidr_expand(\"192.168.0.0/30\")` generates 4 hosts: `{\"192.168.0.0\", \"192.168.0.1\", \"192.168.0.2\", \"192.168.0.3\"}`).", + Decl: types.NewFunction( + types.Args( + types.Named("cidr", types.S).Description("CIDR to expand"), + ), + types.Named("hosts", types.SetOfStr).Description("set of IP addresses the CIDR `cidr` expands to"), + ), + canSkipBctx: false, +} + +var NetCIDRContains = &Builtin{ + Name: "net.cidr_contains", + Description: "Checks if a CIDR or IP is contained within another CIDR. `output` is `true` if `cidr_or_ip` (e.g. `127.0.0.64/26` or `127.0.0.1`) is contained within `cidr` (e.g. `127.0.0.1/24`) and `false` otherwise. Supports both IPv4 and IPv6 notations.", + Decl: types.NewFunction( + types.Args( + types.Named("cidr", types.S).Description("CIDR to check against"), + types.Named("cidr_or_ip", types.S).Description("CIDR or IP to check"), + ), + types.Named("result", types.B).Description("`true` if `cidr_or_ip` is contained within `cidr`"), + ), + canSkipBctx: true, +} + +var NetCIDRContainsMatches = &Builtin{ + Name: "net.cidr_contains_matches", + Description: "Checks if collections of cidrs or ips are contained within another collection of cidrs and returns matches. " + + "This function is similar to `net.cidr_contains` except it allows callers to pass collections of CIDRs or IPs as arguments and returns the matches (as opposed to a boolean result indicating a match between two CIDRs/IPs).", + Decl: types.NewFunction( + types.Args( + types.Named("cidrs", netCidrContainsMatchesOperandType).Description("CIDRs to check against"), + types.Named("cidrs_or_ips", netCidrContainsMatchesOperandType).Description("CIDRs or IPs to check"), + ), + types.Named("output", types.NewSet(types.NewArray([]types.Type{types.A, types.A}, nil))).Description("tuples identifying matches where `cidrs_or_ips` are contained within `cidrs`"), + ), + canSkipBctx: true, +} + +var NetCIDRMerge = &Builtin{ + Name: "net.cidr_merge", + Description: "Merges IP addresses and subnets into the smallest possible list of CIDRs (e.g., `net.cidr_merge([\"192.0.128.0/24\", \"192.0.129.0/24\"])` generates `{\"192.0.128.0/23\"}`." + + `This function merges adjacent subnets where possible, those contained within others and also removes any duplicates. +Supports both IPv4 and IPv6 notations. IPv6 inputs need a prefix length (e.g. "/128").`, + Decl: types.NewFunction( + types.Args( + types.Named("addrs", types.NewAny( + types.NewArray(nil, types.NewAny(types.S)), + types.SetOfStr, + )).Description("CIDRs or IP addresses"), + ), + types.Named("output", types.SetOfStr).Description("smallest possible set of CIDRs obtained after merging the provided list of IP addresses and subnets in `addrs`"), + ), + canSkipBctx: true, +} + +var NetCIDRIsValid = &Builtin{ + Name: "net.cidr_is_valid", + Description: "Parses an IPv4/IPv6 CIDR and returns a boolean indicating if the provided CIDR is valid.", + Decl: types.NewFunction( + types.Args( + types.Named("cidr", types.S).Description("CIDR to validate"), + ), + types.Named("result", types.B).Description("`true` if `cidr` is a valid CIDR"), + ), + canSkipBctx: true, +} + +var netCidrContainsMatchesOperandType = types.NewAny( + types.S, + types.NewArray(nil, types.NewAny( + types.S, + types.NewArray(nil, types.A), + )), + types.NewSet(types.NewAny( + types.S, + types.NewArray(nil, types.A), + )), + types.NewObject(nil, types.NewDynamicProperty( + types.S, + types.NewAny( + types.S, + types.NewArray(nil, types.A), + ), + )), +) + +// Marked non-deterministic because DNS resolution results can be non-deterministic. +var NetLookupIPAddr = &Builtin{ + Name: "net.lookup_ip_addr", + Description: "Returns the set of IP addresses (both v4 and v6) that the passed-in `name` resolves to using the standard name resolution mechanisms available.", + Decl: types.NewFunction( + types.Args( + types.Named("name", types.S).Description("domain name to resolve"), + ), + types.Named("addrs", types.SetOfStr).Description("IP addresses (v4 and v6) that `name` resolves to"), + ), + Nondeterministic: true, + canSkipBctx: false, +} + +/** + * Semantic Versions + */ + +var SemVerIsValid = &Builtin{ + Name: "semver.is_valid", + Description: "Validates that the input is a valid SemVer string.", + Decl: types.NewFunction( + types.Args( + types.Named("vsn", types.A).Description("input to validate"), + ), + types.Named("result", types.B).Description("`true` if `vsn` is a valid SemVer; `false` otherwise"), + ), + canSkipBctx: true, +} + +var SemVerCompare = &Builtin{ + Name: "semver.compare", + Description: "Compares valid SemVer formatted version strings.", + Decl: types.NewFunction( + types.Args( + types.Named("a", types.S).Description("first version string"), + types.Named("b", types.S).Description("second version string"), + ), + types.Named("result", types.N).Description("`-1` if `a < b`; `1` if `a > b`; `0` if `a == b`"), + ), + canSkipBctx: true, +} + +/** + * Printing + */ + +// Print is a special built-in function that writes zero or more operands +// to a message buffer. The caller controls how the buffer is displayed. The +// operands may be of any type. Furthermore, unlike other built-in functions, +// undefined operands DO NOT cause the print() function to fail during +// evaluation. +var Print = &Builtin{ + Name: "print", + Decl: types.NewVariadicFunction(nil, types.A, nil), +} + +// InternalPrint represents the internal implementation of the print() function. +// The compiler rewrites print() calls to refer to the internal implementation. +var InternalPrint = &Builtin{ + Name: "internal.print", + Decl: types.NewFunction([]types.Type{types.NewArray(nil, types.SetOfAny)}, nil), +} + +var InternalTestCase = &Builtin{ + Name: "internal.test_case", + Decl: types.NewFunction([]types.Type{types.NewArray(nil, types.A)}, nil), +} + +/** + * Deprecated built-ins. + */ + +// SetDiff has been replaced by the minus built-in. +var SetDiff = &Builtin{ + Name: "set_diff", + Decl: types.NewFunction( + types.Args( + types.SetOfAny, + types.SetOfAny, + ), + types.SetOfAny, + ), + deprecated: true, + canSkipBctx: true, +} + +// NetCIDROverlap has been replaced by the `net.cidr_contains` built-in. +var NetCIDROverlap = &Builtin{ + Name: "net.cidr_overlap", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), + deprecated: true, + canSkipBctx: true, +} + +// CastArray checks the underlying type of the input. If it is array or set, an array +// containing the values is returned. If it is not an array, an error is thrown. +var CastArray = &Builtin{ + Name: "cast_array", + Decl: types.NewFunction( + types.Args(types.A), + types.NewArray(nil, types.A), + ), + deprecated: true, + canSkipBctx: true, +} + +// CastSet checks the underlying type of the input. +// If it is a set, the set is returned. +// If it is an array, the array is returned in set form (all duplicates removed) +// If neither, an error is thrown +var CastSet = &Builtin{ + Name: "cast_set", + Decl: types.NewFunction( + types.Args(types.A), + types.SetOfAny, + ), + deprecated: true, + canSkipBctx: true, +} + +// CastString returns input if it is a string; if not returns error. +// For formatting variables, see sprintf +var CastString = &Builtin{ + Name: "cast_string", + Decl: types.NewFunction( + types.Args(types.A), + types.S, + ), + deprecated: true, + canSkipBctx: true, +} + +// CastBoolean returns input if it is a boolean; if not returns error. +var CastBoolean = &Builtin{ + Name: "cast_boolean", + Decl: types.NewFunction( + types.Args(types.A), + types.B, + ), + deprecated: true, + canSkipBctx: true, +} + +// CastNull returns null if input is null; if not returns error. +var CastNull = &Builtin{ + Name: "cast_null", + Decl: types.NewFunction( + types.Args(types.A), + types.Nl, + ), + deprecated: true, + canSkipBctx: true, +} + +// CastObject returns the given object if it is null; throws an error otherwise +var CastObject = &Builtin{ + Name: "cast_object", + Decl: types.NewFunction( + types.Args(types.A), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + ), + deprecated: true, + canSkipBctx: true, +} + +// RegexMatchDeprecated declares `re_match` which has been deprecated. Use `regex.match` instead. +var RegexMatchDeprecated = &Builtin{ + Name: "re_match", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), + deprecated: true, + canSkipBctx: false, +} + +// All takes a list and returns true if all of the items +// are true. A collection of length 0 returns true. +var All = &Builtin{ + Name: "all", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.SetOfAny, + types.NewArray(nil, types.A), + ), + ), + types.B, + ), + deprecated: true, + canSkipBctx: true, +} + +// Any takes a collection and returns true if any of the items +// is true. A collection of length 0 returns false. +var Any = &Builtin{ + Name: "any", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.SetOfAny, + types.NewArray(nil, types.A), + ), + ), + types.B, + ), + deprecated: true, + canSkipBctx: true, +} + +// Builtin represents a built-in function supported by OPA. Every built-in +// function is uniquely identified by a name. +type Builtin struct { + Name string `json:"name"` // Unique name of built-in function, e.g., (arg1,arg2,...,argN) + Description string `json:"description,omitempty"` // Description of what the built-in function does. + + // Categories of the built-in function. Omitted for namespaced + // built-ins, i.e. "array.concat" is taken to be of the "array" category. + // "minus" for example, is part of two categories: numbers and sets. (NOTE(sr): aspirational) + Categories []string `json:"categories,omitempty"` + + Decl *types.Function `json:"decl"` // Built-in function type declaration. + Infix string `json:"infix,omitempty"` // Unique name of infix operator. Default should be unset. + Relation bool `json:"relation,omitempty"` // Indicates if the built-in acts as a relation. + deprecated bool // Indicates if the built-in has been deprecated. + canSkipBctx bool // Built-in needs no data from the built-in context. + Nondeterministic bool `json:"nondeterministic,omitempty"` // Indicates if the built-in returns non-deterministic results. +} + +// category is a helper for specifying a Builtin's Categories +func category(cs ...string) []string { + return cs +} + +// Minimal returns a shallow copy of b with the descriptions and categories and +// named arguments stripped out. +func (b *Builtin) Minimal() *Builtin { + cpy := *b + fargs := b.Decl.FuncArgs() + if fargs.Variadic != nil { + cpy.Decl = types.NewVariadicFunction(fargs.Args, fargs.Variadic, b.Decl.Result()) + } else { + cpy.Decl = types.NewFunction(fargs.Args, b.Decl.Result()) + } + cpy.Categories = nil + cpy.Description = "" + return &cpy +} + +// IsDeprecated returns true if the Builtin function is deprecated and will be removed in a future release. +func (b *Builtin) IsDeprecated() bool { + return b.deprecated +} + +// IsDeterministic returns true if the Builtin function returns non-deterministic results. +func (b *Builtin) IsNondeterministic() bool { + return b.Nondeterministic +} + +// Expr creates a new expression for the built-in with the given operands. +func (b *Builtin) Expr(operands ...*Term) *Expr { + ts := make([]*Term, len(operands)+1) + ts[0] = NewTerm(b.Ref()) + for i := range operands { + ts[i+1] = operands[i] + } + return &Expr{ + Terms: ts, + } +} + +// Call creates a new term for the built-in with the given operands. +func (b *Builtin) Call(operands ...*Term) *Term { + call := make(Call, len(operands)+1) + call[0] = NewTerm(b.Ref()) + for i := range operands { + call[i+1] = operands[i] + } + return NewTerm(call) +} + +// Ref returns a Ref that refers to the built-in function. +func (b *Builtin) Ref() Ref { + parts := strings.Split(b.Name, ".") + ref := make(Ref, len(parts)) + ref[0] = VarTerm(parts[0]) + for i := 1; i < len(parts); i++ { + ref[i] = InternedTerm(parts[i]) + } + return ref +} + +// IsTargetPos returns true if a variable in the i-th position will be bound by +// evaluating the call expression. +func (b *Builtin) IsTargetPos(i int) bool { + return b.Decl.Arity() == i +} + +// NeedsBuiltInContext returns true if the built-in depends on the built-in context. +func (b *Builtin) NeedsBuiltInContext() bool { + // Negated, so built-ins we don't know about (and who don't know about this option) + // will get a built-in context provided to them. + return !b.canSkipBctx +} + +func init() { + BuiltinMap = map[string]*Builtin{} + for _, b := range &DefaultBuiltins { + RegisterBuiltin(b) + } +} diff --git a/third_party/opa/v1/ast/builtins_test.go b/third_party/opa/v1/ast/builtins_test.go new file mode 100644 index 000000000000..7f9ef999d6f6 --- /dev/null +++ b/third_party/opa/v1/ast/builtins_test.go @@ -0,0 +1,62 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/types" +) + +func TestBuiltinDeclRoundtrip(t *testing.T) { + bs, err := json.Marshal(Plus) + if err != nil { + t.Fatal(err) + } + + var cpy Builtin + + if err := json.Unmarshal(bs, &cpy); err != nil { + t.Fatal(err) + } + + if types.Compare(cpy.Decl, Plus.Decl) != 0 || cpy.Name != Plus.Name || cpy.Infix != Plus.Infix || cpy.Relation != Plus.Relation { + t.Fatal("expected:", Plus, "got:", cpy) + } +} + +func TestAllBuiltinsHaveDescribedArguments(t *testing.T) { + for _, b := range Builtins { + if b.deprecated || b.Infix != "" || b.Name == "print" || b.Name == "internal.print" || b.Name == "internal.test_case" { + continue + } + + t.Run(b.Name, func(t *testing.T) { + namedAndDescribed(t, "arg", b.Decl.NamedFuncArgs().Args...) + namedAndDescribed(t, "res", b.Decl.NamedResult()) + }) + } +} + +func namedAndDescribed(t *testing.T, typ string, args ...types.Type) { + t.Helper() + + for i, arg := range args { + t.Run(fmt.Sprintf("%s=%d", typ, i), func(t *testing.T) { + typ, ok := arg.(*types.NamedType) + if !ok { + t.Fatalf("expected arg to be %T, got %T", typ, arg) + } + if typ.Name == "" { + t.Error("empty name") + } + if typ.Descr == "" { + t.Error("empty description") + } + }) + } +} diff --git a/third_party/opa/v1/ast/capabilities.go b/third_party/opa/v1/ast/capabilities.go new file mode 100644 index 000000000000..d9accab8e68e --- /dev/null +++ b/third_party/opa/v1/ast/capabilities.go @@ -0,0 +1,285 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "bytes" + _ "embed" + "encoding/json" + "fmt" + "io" + "os" + "slices" + "sort" + "strings" + + caps "github.com/open-policy-agent/opa/capabilities" + "github.com/open-policy-agent/opa/internal/semver" + "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/capabilities" + "github.com/open-policy-agent/opa/v1/util" +) + +// VersonIndex contains an index from built-in function name, language feature, +// and future rego keyword to version number. During the build, this is used to +// create an index of the minimum version required for the built-in/feature/kw. +type VersionIndex struct { + Builtins map[string]semver.Version `json:"builtins"` + Features map[string]semver.Version `json:"features"` + Keywords map[string]semver.Version `json:"keywords"` +} + +// NOTE(tsandall): this file is generated by internal/cmd/genversionindex/main.go +// and run as part of go:generate. We generate the version index as part of the +// build process because it's relatively expensive to build (it takes ~500ms on +// my machine) and never changes. +// +//go:embed version_index.json +var versionIndexBs []byte + +var minVersionIndex = func() VersionIndex { + var vi VersionIndex + err := json.Unmarshal(versionIndexBs, &vi) + if err != nil { + panic(err) + } + return vi +}() + +// In the compiler, we used this to check that we're OK working with ref heads. +// If this isn't present, we'll fail. This is to ensure that older versions of +// OPA can work with policies that we're compiling -- if they don't know ref +// heads, they wouldn't be able to parse them. +const FeatureRefHeadStringPrefixes = "rule_head_ref_string_prefixes" +const FeatureRefHeads = "rule_head_refs" +const FeatureRegoV1 = "rego_v1" +const FeatureRegoV1Import = "rego_v1_import" +const FeatureKeywordsInRefs = "keywords_in_refs" + +// Features carries the default features supported by this version of OPA. +// Use RegisterFeatures to add to them. +var Features = []string{ + FeatureRegoV1, + FeatureKeywordsInRefs, +} + +// RegisterFeatures lets applications wrapping OPA register features, to be +// included in `ast.CapabilitiesForThisVersion()`. +func RegisterFeatures(fs ...string) { + for i := range fs { + if slices.Contains(Features, fs[i]) { + continue + } + Features = append(Features, fs[i]) + } +} + +// Capabilities defines a structure containing data that describes the capabilities +// or features supported by a particular version of OPA. +type Capabilities struct { + Builtins []*Builtin `json:"builtins,omitempty"` + FutureKeywords []string `json:"future_keywords,omitempty"` + WasmABIVersions []WasmABIVersion `json:"wasm_abi_versions,omitempty"` + + // Features is a bit of a mixed bag for checking that an older version of OPA + // is able to do what needs to be done. + // TODO(sr): find better words ^^ + Features []string `json:"features,omitempty"` + + // allow_net is an array of hostnames or IP addresses, that an OPA instance is + // allowed to connect to. + // If omitted, ANY host can be connected to. If empty, NO host can be connected to. + // As of now, this only controls fetching remote refs for using JSON Schemas in + // the type checker. + // TODO(sr): support ports to further restrict connection peers + // TODO(sr): support restricting `http.send` using the same mechanism (see https://github.com/open-policy-agent/opa/issues/3665) + AllowNet []string `json:"allow_net,omitempty"` +} + +// WasmABIVersion captures the Wasm ABI version. Its `Minor` version is indicating +// backwards-compatible changes. +type WasmABIVersion struct { + Version int `json:"version"` + Minor int `json:"minor_version"` +} + +type CapabilitiesOptions struct { + regoVersion RegoVersion +} + +func newCapabilitiesOptions(opts []CapabilitiesOption) CapabilitiesOptions { + co := CapabilitiesOptions{} + for _, opt := range opts { + opt(&co) + } + return co +} + +type CapabilitiesOption func(*CapabilitiesOptions) + +func CapabilitiesRegoVersion(regoVersion RegoVersion) CapabilitiesOption { + return func(o *CapabilitiesOptions) { + o.regoVersion = regoVersion + } +} + +// CapabilitiesForThisVersion returns the capabilities of this version of OPA. +func CapabilitiesForThisVersion(opts ...CapabilitiesOption) *Capabilities { + co := newCapabilitiesOptions(opts) + + f := &Capabilities{} + + for _, vers := range capabilities.ABIVersions() { + f.WasmABIVersions = append(f.WasmABIVersions, WasmABIVersion{Version: vers[0], Minor: vers[1]}) + } + + f.Builtins = make([]*Builtin, len(Builtins)) + copy(f.Builtins, Builtins) + + slices.SortFunc(f.Builtins, func(a, b *Builtin) int { + return strings.Compare(a.Name, b.Name) + }) + + switch co.regoVersion { + case RegoV0, RegoV0CompatV1: + for kw := range allFutureKeywords { + f.FutureKeywords = append(f.FutureKeywords, kw) + } + + f.Features = []string{ + FeatureRefHeadStringPrefixes, + FeatureRefHeads, + FeatureRegoV1Import, + FeatureRegoV1, // Included in v0 capabilities to allow v1 bundles in --v0-compatible mode + FeatureKeywordsInRefs, + } + default: + for kw := range futureKeywords { + f.FutureKeywords = append(f.FutureKeywords, kw) + } + + f.Features = make([]string, len(Features)) + copy(f.Features, Features) + } + + sort.Strings(f.FutureKeywords) + sort.Strings(f.Features) + + return f +} + +// LoadCapabilitiesJSON loads a JSON serialized capabilities structure from the reader r. +func LoadCapabilitiesJSON(r io.Reader) (*Capabilities, error) { + d := util.NewJSONDecoder(r) + var c Capabilities + return &c, d.Decode(&c) +} + +// LoadCapabilitiesVersion loads a JSON serialized capabilities structure from the specific version. +func LoadCapabilitiesVersion(version string) (*Capabilities, error) { + cvs, err := LoadCapabilitiesVersions() + if err != nil { + return nil, err + } + + for _, cv := range cvs { + if cv == version { + cont, err := caps.FS.ReadFile(cv + ".json") + if err != nil { + return nil, err + } + + return LoadCapabilitiesJSON(bytes.NewReader(cont)) + } + + } + return nil, fmt.Errorf("no capabilities version found %v", version) +} + +// LoadCapabilitiesFile loads a JSON serialized capabilities structure from a file. +func LoadCapabilitiesFile(file string) (*Capabilities, error) { + fd, err := os.Open(file) + if err != nil { + return nil, err + } + defer fd.Close() + return LoadCapabilitiesJSON(fd) +} + +// LoadCapabilitiesVersions loads all capabilities versions +func LoadCapabilitiesVersions() ([]string, error) { + ents, err := caps.FS.ReadDir(".") + if err != nil { + return nil, err + } + + capabilitiesVersions := make([]string, 0, len(ents)) + for _, ent := range ents { + capabilitiesVersions = append(capabilitiesVersions, strings.Replace(ent.Name(), ".json", "", 1)) + } + return capabilitiesVersions, nil +} + +// MinimumCompatibleVersion returns the minimum compatible OPA version based on +// the built-ins, features, and keywords in c. +func (c *Capabilities) MinimumCompatibleVersion() (string, bool) { + + var maxVersion semver.Version + + // this is the oldest OPA release that includes capabilities + if err := maxVersion.Set("0.17.0"); err != nil { + panic("unreachable") + } + + for _, bi := range c.Builtins { + v, ok := minVersionIndex.Builtins[bi.Name] + if !ok { + return "", false + } + if v.Compare(maxVersion) > 0 { + maxVersion = v + } + } + + for _, kw := range c.FutureKeywords { + v, ok := minVersionIndex.Keywords[kw] + if !ok { + return "", false + } + if v.Compare(maxVersion) > 0 { + maxVersion = v + } + } + + for _, feat := range c.Features { + v, ok := minVersionIndex.Features[feat] + if !ok { + return "", false + } + if v.Compare(maxVersion) > 0 { + maxVersion = v + } + } + + return maxVersion.String(), true +} + +func (c *Capabilities) ContainsFeature(feature string) bool { + return slices.Contains(c.Features, feature) +} + +// addBuiltinSorted inserts a built-in into c in sorted order. An existing built-in with the same name +// will be overwritten. +func (c *Capabilities) addBuiltinSorted(bi *Builtin) { + i := sort.Search(len(c.Builtins), func(x int) bool { + return c.Builtins[x].Name >= bi.Name + }) + if i < len(c.Builtins) && bi.Name == c.Builtins[i].Name { + c.Builtins[i] = bi + return + } + c.Builtins = append(c.Builtins, nil) + copy(c.Builtins[i+1:], c.Builtins[i:]) + c.Builtins[i] = bi +} diff --git a/third_party/opa/v1/ast/capabilities_test.go b/third_party/opa/v1/ast/capabilities_test.go new file mode 100644 index 000000000000..d29bae65ce46 --- /dev/null +++ b/third_party/opa/v1/ast/capabilities_test.go @@ -0,0 +1,311 @@ +package ast + +import ( + "path" + "testing" + + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestParserCatchesIllegalCapabilities(t *testing.T) { + tests := []struct { + note string + regoVersion RegoVersion + capabilities Capabilities + expErr string + }{ + { + note: "v0, bad future keyword", + regoVersion: RegoV0, + capabilities: Capabilities{ + FutureKeywords: []string{"deadbeef"}, + }, + expErr: "illegal capabilities: unknown keyword: deadbeef", + }, + { + note: "v1, bad future keyword", + regoVersion: RegoV1, + capabilities: Capabilities{ + Features: []string{FeatureRegoV1}, + FutureKeywords: []string{"deadbeef"}, + }, + expErr: "illegal capabilities: unknown keyword: deadbeef", + }, + { + note: "v1, no rego_v1 feature", + regoVersion: RegoV1, + capabilities: Capabilities{}, + expErr: "illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var opts ParserOptions + opts.Capabilities = &tc.capabilities + + opts.RegoVersion = tc.regoVersion + + _, _, err := ParseStatementsWithOpts("test.rego", "true", opts) + if err == nil { + t.Fatal("expected error") + } else if errs, ok := err.(Errors); !ok || len(errs) != 1 { + t.Fatal("expected exactly one error but got:", err) + } else if errs[0].Code != ParseErr || errs[0].Message != tc.expErr { + t.Fatal("unexpected error:", err) + } + }) + } +} + +func TestParserCatchesIllegalFutureKeywordsBasedOnCapabilities(t *testing.T) { + tests := []struct { + note string + regoVersion RegoVersion + }{ + { + note: "v0", + regoVersion: RegoV0, + }, + { + note: "v1", + regoVersion: RegoV1, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var opts ParserOptions + opts.Capabilities = CapabilitiesForThisVersion() + opts.FutureKeywords = []string{"deadbeef"} + + opts.RegoVersion = tc.regoVersion + + _, _, err := ParseStatementsWithOpts("test.rego", "true", opts) + if err == nil { + t.Fatal("expected error") + } else if errs, ok := err.(Errors); !ok || len(errs) != 1 { + t.Fatal("expected exactly one error but got:", err) + } else if errs[0].Code != ParseErr || errs[0].Message != "unknown future keyword: deadbeef" { + t.Fatal("unexpected error:", err) + } + }) + } +} + +func TestParserCapabilitiesWithSpecificOptInAndOlderOPA(t *testing.T) { + + src := ` + package test + + import future.keywords.in + + p { + 1 in [3,2,1] + } + ` + + opts := ParserOptions{ + Capabilities: &Capabilities{}, + RegoVersion: RegoV0, + } + + _, err := ParseModuleWithOpts("test.rego", src, opts) + if err == nil { + t.Fatal("expected error") + } else if errs, ok := err.(Errors); !ok || len(errs) != 1 { + t.Fatal("expected exactly one error but got:", err) + } else if errs[0].Code != ParseErr || errs[0].Location.Row != 4 || errs[0].Message != "unexpected keyword, must be one of []" { + t.Fatal("unexpected error:", err) + } +} + +func TestParserCapabilitiesWithWildcardOptInAndOlderOPA(t *testing.T) { + + src := ` + package test + + import future.keywords + + p { + 1 in [3,2,1] + } + ` + opts := ParserOptions{ + Capabilities: &Capabilities{}, + RegoVersion: RegoV0, + } + + _, err := ParseModuleWithOpts("test.rego", src, opts) + if err == nil { + t.Fatal("expected error") + } else if errs, ok := err.(Errors); !ok || len(errs) != 1 { + t.Fatal("expected exactly one error but got:", err) + } else if errs[0].Code != ParseErr || errs[0].Location.Row != 7 || errs[0].Message != "unexpected identifier token: expected \\n or ; or }" { + t.Fatal("unexpected error:", err) + } +} + +func TestLoadCapabilitiesVersion(t *testing.T) { + + capabilitiesVersions, err := LoadCapabilitiesVersions() + if err != nil { + t.Fatal("expected success", err) + } + + if len(capabilitiesVersions) == 0 { + t.Fatal("expected a non-empty array of capabilities versions") + } + for _, cv := range capabilitiesVersions { + if _, err := LoadCapabilitiesVersion(cv); err != nil { + t.Fatal("expected success", err) + } + } +} + +func TestLoadCapabilitiesFile(t *testing.T) { + + files := map[string]string{ + "test-capabilities.json": ` + { + "builtins": [] + } + `, + } + + test.WithTempFS(files, func(root string) { + _, err := LoadCapabilitiesFile(path.Join(root, "test-capabilities.json")) + if err != nil { + t.Fatal("expected success", err) + } + }) + +} + +func TestCapabilitiesAddBuiltinSorted(t *testing.T) { + + c := CapabilitiesForThisVersion() + + indexOfEq := findBuiltinIndex(c, "eq") + if indexOfEq < 0 { + panic("expected to find eq") + } + + c.addBuiltinSorted(&Builtin{Name: "eq"}) + + if c.Builtins[indexOfEq].Decl != nil { + t.Fatal("expected builtin to get overwritten") + } + + c.addBuiltinSorted(&Builtin{Name: "~foo"}) // non-existent but always sorts to the end + + if findBuiltinIndex(c, "~foo") != len(c.Builtins)-1 { + t.Fatal("expected builtin to be last in slice") + } + + c.addBuiltinSorted(&Builtin{Name: " foo"}) // non-existent but always sorts to start + + if findBuiltinIndex(c, " foo") != 0 { + t.Fatal("expected builtin to be first in slice") + } + + c.addBuiltinSorted(&Builtin{Name: "plus1"}) // non-existent but always after plus in middle + + if findBuiltinIndex(c, "plus1") != findBuiltinIndex(c, "plus")+1 { + t.Fatal("expected builtin to be immediately after plus") + } +} + +func TestCapabilitiesMinimumCompatibleVersion(t *testing.T) { + + tests := []struct { + note string + module string + version string + }{ + { + note: "builtins", + module: ` + package x + p { array.reverse([1,2,3]) } + `, + version: "0.36.0", + }, + { + note: "keywords", + module: ` + package x + import future.keywords.every + `, + version: "0.38.0", + }, + { + note: "features (string prefix ref)", + module: ` + package x + import future.keywords.if + p.a.b.c.d if { true } + `, + version: "0.46.0", + }, + { + note: "features (general ref)", + module: ` + package x + import future.keywords.if + p.a.b[c].d if { c := "foo" } + `, + version: "0.59.0", + }, + { + note: "features (general ref + string prefix ref)", + module: ` + package x + import future.keywords.if + p.a.b.c.d if { true } + p.a.b[c].d if { c := "foo" } + `, + version: "0.59.0", + }, + { + note: "rego.v1 import", + module: ` + package x + import rego.v1`, + version: "0.59.0", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := MustCompileModulesWithOpts(map[string]string{"test.rego": tc.module}, CompileOpts{ + ParserOptions: ParserOptions{ + RegoVersion: RegoV0, + }, + }) + minVersion, found := c.Required.MinimumCompatibleVersion() + if !found || minVersion != tc.version { + t.Fatal("expected", tc.version, "but got", minVersion) + } + }) + } +} + +func BenchmarkCapabilitiesCurrentVersion(b *testing.B) { + var caps *Capabilities + for range b.N { + caps = CapabilitiesForThisVersion() + } + if caps == nil { + b.Fatal("expected capabilities to be non-nil") + } +} + +func findBuiltinIndex(c *Capabilities, name string) int { + for i, bi := range c.Builtins { + if bi.Name == name { + return i + } + } + return -1 +} diff --git a/third_party/opa/v1/ast/check.go b/third_party/opa/v1/ast/check.go new file mode 100644 index 000000000000..e3d2051a2696 --- /dev/null +++ b/third_party/opa/v1/ast/check.go @@ -0,0 +1,1329 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "slices" + "sort" + "strings" + + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +type varRewriter func(Ref) Ref + +// exprChecker defines the interface for executing type checking on a single +// expression. The exprChecker must update the provided TypeEnv with inferred +// types of vars. +type exprChecker func(*TypeEnv, *Expr) *Error + +// typeChecker implements type checking on queries and rules. Errors are +// accumulated on the typeChecker so that a single run can report multiple +// issues. +type typeChecker struct { + builtins map[string]*Builtin + required *Capabilities + errs Errors + exprCheckers map[string]exprChecker + varRewriter varRewriter + ss *SchemaSet + allowNet []string + input types.Type + allowUndefinedFuncs bool + schemaTypes map[string]types.Type +} + +// newTypeChecker returns a new typeChecker object that has no errors. +func newTypeChecker() *typeChecker { + return &typeChecker{ + exprCheckers: map[string]exprChecker{ + "eq": checkExprEq, + }, + } +} + +func (tc *typeChecker) newEnv(exist *TypeEnv) *TypeEnv { + if exist != nil { + return exist.wrap() + } + env := newTypeEnv(tc.copy) + if tc.input != nil { + env.tree.Put(InputRootRef, tc.input) + } + return env +} + +func (tc *typeChecker) copy() *typeChecker { + return newTypeChecker(). + WithVarRewriter(tc.varRewriter). + WithSchemaSet(tc.ss). + WithSchemaTypes(tc.schemaTypes). + WithAllowNet(tc.allowNet). + WithInputType(tc.input). + WithAllowUndefinedFunctionCalls(tc.allowUndefinedFuncs). + WithBuiltins(tc.builtins). + WithRequiredCapabilities(tc.required) +} + +func (tc *typeChecker) WithRequiredCapabilities(c *Capabilities) *typeChecker { + tc.required = c + return tc +} + +func (tc *typeChecker) WithBuiltins(builtins map[string]*Builtin) *typeChecker { + tc.builtins = builtins + return tc +} + +func (tc *typeChecker) WithSchemaSet(ss *SchemaSet) *typeChecker { + tc.ss = ss + return tc +} + +func (tc *typeChecker) WithSchemaTypes(schemaTypes map[string]types.Type) *typeChecker { + tc.schemaTypes = schemaTypes + return tc +} + +func (tc *typeChecker) WithAllowNet(hosts []string) *typeChecker { + tc.allowNet = hosts + return tc +} + +func (tc *typeChecker) WithVarRewriter(f varRewriter) *typeChecker { + tc.varRewriter = f + return tc +} + +func (tc *typeChecker) WithInputType(tpe types.Type) *typeChecker { + tc.input = tpe + return tc +} + +// WithAllowUndefinedFunctionCalls sets the type checker to allow references to undefined functions. +// Additionally, the 'CheckUndefinedFuncs' and 'CheckSafetyRuleBodies' compiler stages are skipped. +func (tc *typeChecker) WithAllowUndefinedFunctionCalls(allow bool) *typeChecker { + tc.allowUndefinedFuncs = allow + return tc +} + +// Env returns a type environment for the specified built-ins with any other +// global types configured on the checker. In practice, this is the default +// environment that other statements will be checked against. +func (tc *typeChecker) Env(builtins map[string]*Builtin) *TypeEnv { + env := tc.newEnv(nil) + for _, bi := range builtins { + env.tree.Put(bi.Ref(), bi.Decl) + } + return env +} + +// CheckBody runs type checking on the body and returns a TypeEnv if no errors +// are found. The resulting TypeEnv wraps the provided one. The resulting +// TypeEnv will be able to resolve types of vars contained in the body. +func (tc *typeChecker) CheckBody(env *TypeEnv, body Body) (*TypeEnv, Errors) { + + errors := []*Error{} + env = tc.newEnv(env) + vis := newRefChecker(env, tc.varRewriter) + + WalkExprs(body, func(expr *Expr) bool { + + closureErrs := tc.checkClosures(env, expr) + for _, err := range closureErrs { + errors = append(errors, err) + } + + hasClosureErrors := len(closureErrs) > 0 + + // reset errors from previous iteration + vis.errs = nil + NewGenericVisitor(vis.Visit).Walk(expr) + for _, err := range vis.errs { + errors = append(errors, err) + } + + hasRefErrors := len(vis.errs) > 0 + + if err := tc.checkExpr(env, expr); err != nil { + // Suppress this error if a more actionable one has occurred. In + // this case, if an error occurred in a ref or closure contained in + // this expression, and the error is due to a nil type, then it's + // likely to be the result of the more specific error. + skip := (hasClosureErrors || hasRefErrors) && causedByNilType(err) + if !skip { + errors = append(errors, err) + } + } + return true + }) + + tc.err(errors) + return env, errors +} + +// CheckTypes runs type checking on the rules returns a TypeEnv if no errors +// are found. The resulting TypeEnv wraps the provided one. The resulting +// TypeEnv will be able to resolve types of refs that refer to rules. +func (tc *typeChecker) CheckTypes(env *TypeEnv, sorted []util.T, as *AnnotationSet) (*TypeEnv, Errors) { + env = tc.newEnv(env) + for _, s := range sorted { + tc.checkRule(env, as, s.(*Rule)) + } + tc.errs.Sort() + return env, tc.errs +} + +func (tc *typeChecker) checkClosures(env *TypeEnv, expr *Expr) Errors { + var result Errors + WalkClosures(expr, func(x any) bool { + switch x := x.(type) { + case *ArrayComprehension: + _, errs := tc.copy().CheckBody(env, x.Body) + if len(errs) > 0 { + result = errs + return true + } + case *SetComprehension: + _, errs := tc.copy().CheckBody(env, x.Body) + if len(errs) > 0 { + result = errs + return true + } + case *ObjectComprehension: + _, errs := tc.copy().CheckBody(env, x.Body) + if len(errs) > 0 { + result = errs + return true + } + } + return false + }) + return result +} + +func (tc *typeChecker) getSchemaType(schemaAnnot *SchemaAnnotation, rule *Rule) (types.Type, *Error) { + if tc.schemaTypes == nil { + tc.schemaTypes = make(map[string]types.Type) + } + + if len(schemaAnnot.Schema) > 0 { + if refType, exists := tc.schemaTypes[schemaAnnot.Schema.String()]; exists { + return refType, nil + } + } + + refType, err := processAnnotation(tc.ss, schemaAnnot, rule, tc.allowNet) + if err != nil { + return nil, err + } + + if refType == nil { + return nil, nil + } + + // Only add to cache if schema is read from file + if len(schemaAnnot.Schema) > 0 { + tc.schemaTypes[schemaAnnot.Schema.String()] = refType + } + + return refType, nil + +} + +func (tc *typeChecker) checkRule(env *TypeEnv, as *AnnotationSet, rule *Rule) { + + env = env.wrap() + + schemaAnnots := getRuleAnnotation(as, rule) + for _, schemaAnnot := range schemaAnnots { + refType, err := tc.getSchemaType(schemaAnnot, rule) + if err != nil { + tc.err([]*Error{err}) + continue + } + + ref := schemaAnnot.Path + // if we do not have a ref or a reftype, we should not evaluate this rule. + if ref == nil || refType == nil { + continue + } + + prefixRef, t := getPrefix(env, ref) + if t == nil || len(prefixRef) == len(ref) { + env.tree.Put(ref, refType) + } else { + newType, err := override(ref[len(prefixRef):], t, refType, rule) + if err != nil { + tc.err([]*Error{err}) + continue + } + env.tree.Put(prefixRef, newType) + } + } + + cpy, err := tc.CheckBody(env, rule.Body) + env = env.next + path := rule.Ref() + + if len(err) > 0 { + // if the rule/function contains an error, add it to the type env so + // that expressions that refer to this rule/function do not encounter + // type errors. + env.tree.Put(path, types.A) + return + } + + var tpe types.Type + + if len(rule.Head.Args) > 0 { + // If args are not referred to in body, infer as any. + WalkVars(rule.Head.Args, func(v Var) bool { + if cpy.GetByValue(v) == nil { + cpy.tree.PutOne(v, types.A) + } + return false + }) + + // Construct function type. + args := make([]types.Type, len(rule.Head.Args)) + for i := range len(rule.Head.Args) { + args[i] = cpy.GetByValue(rule.Head.Args[i].Value) + } + + f := types.NewFunction(args, cpy.Get(rule.Head.Value)) + + tpe = f + } else { + switch rule.Head.RuleKind() { + case SingleValue: + typeV := cpy.GetByValue(rule.Head.Value.Value) + if !path.IsGround() { + // e.g. store object[string: whatever] at data.p.q.r, not data.p.q.r[x] or data.p.q.r[x].y[z] + objPath := path.DynamicSuffix() + path = path.GroundPrefix() + + var err error + tpe, err = nestedObject(cpy, objPath, typeV) + if err != nil { + tc.err([]*Error{NewError(TypeErr, rule.Head.Location, err.Error())}) //nolint:govet + tpe = nil + } + } else if typeV != nil { + tpe = typeV + } + case MultiValue: + typeK := cpy.GetByValue(rule.Head.Key.Value) + if typeK != nil { + tpe = types.NewSet(typeK) + } + } + } + + if tpe != nil { + env.tree.Insert(path, tpe, env) + } +} + +// nestedObject creates a nested structure of object types, where each term on path corresponds to a level in the +// nesting. Each term in the path only contributes to the dynamic portion of its corresponding object. +func nestedObject(env *TypeEnv, path Ref, tpe types.Type) (types.Type, error) { + if len(path) == 0 { + return tpe, nil + } + + k := path[0] + typeV, err := nestedObject(env, path[1:], tpe) + if err != nil { + return nil, err + } + if typeV == nil { + return nil, nil + } + + var dynamicProperty *types.DynamicProperty + typeK := env.GetByValue(k.Value) + if typeK == nil { + return nil, nil + } + dynamicProperty = types.NewDynamicProperty(typeK, typeV) + + return types.NewObject(nil, dynamicProperty), nil +} + +func (tc *typeChecker) checkExpr(env *TypeEnv, expr *Expr) *Error { + if err := tc.checkExprWith(env, expr, 0); err != nil { + return err + } + if !expr.IsCall() { + return nil + } + + operator := expr.Operator().String() + + // If the type checker wasn't provided with a required capabilities + // structure then just skip. In some cases, type checking might be run + // without the need to record what builtins are required. + if tc.required != nil && tc.builtins != nil { + if bi, ok := tc.builtins[operator]; ok { + tc.required.addBuiltinSorted(bi) + } + } + + checker := tc.exprCheckers[operator] + if checker != nil { + return checker(env, expr) + } + + return tc.checkExprBuiltin(env, expr) +} + +func (tc *typeChecker) checkExprBuiltin(env *TypeEnv, expr *Expr) *Error { + + args := expr.Operands() + pre := getArgTypes(env, args) + + // NOTE(tsandall): undefined functions will have been caught earlier in the + // compiler. We check for undefined functions before the safety check so + // that references to non-existent functions result in undefined function + // errors as opposed to unsafe var errors. + // + // We cannot run type checking before the safety check because part of the + // type checker relies on reordering (in particular for references to local + // vars). + name := expr.Operator() + tpe := env.GetByRef(name) + + if tpe == nil { + if tc.allowUndefinedFuncs { + return nil + } + return NewError(TypeErr, expr.Location, "undefined function %v", name) + } + + // check if the expression refers to a function that contains an error + _, ok := tpe.(types.Any) + if ok { + return nil + } + + ftpe, ok := tpe.(*types.Function) + if !ok { + return NewError(TypeErr, expr.Location, "undefined function %v", name) + } + + fargs := ftpe.FuncArgs() + namedFargs := ftpe.NamedFuncArgs() + + if ftpe.Result() != nil { + fargs.Args = append(fargs.Args, ftpe.Result()) + namedFargs.Args = append(namedFargs.Args, ftpe.NamedResult()) + } + + if len(args) > len(fargs.Args) && fargs.Variadic == nil { + return newArgError(expr.Location, name, "too many arguments", pre, namedFargs) + } + + if len(args) < len(ftpe.FuncArgs().Args) { + return newArgError(expr.Location, name, "too few arguments", pre, namedFargs) + } + + for i := range args { + if !unify1(env, args[i], fargs.Arg(i), false) { + post := make([]types.Type, len(args)) + for i := range args { + post[i] = env.GetByValue(args[i].Value) + } + return newArgError(expr.Location, name, "invalid argument(s)", post, namedFargs) + } + } + + return nil +} + +func checkExprEq(env *TypeEnv, expr *Expr) *Error { + + pre := getArgTypes(env, expr.Operands()) + + if len(pre) < Equality.Decl.Arity() { + return newArgError(expr.Location, expr.Operator(), "too few arguments", pre, Equality.Decl.FuncArgs()) + } + + if Equality.Decl.Arity() < len(pre) { + return newArgError(expr.Location, expr.Operator(), "too many arguments", pre, Equality.Decl.FuncArgs()) + } + + a, b := expr.Operand(0), expr.Operand(1) + typeA, typeB := env.GetByValue(a.Value), env.GetByValue(b.Value) + + if !unify2(env, a, typeA, b, typeB) { + err := NewError(TypeErr, expr.Location, "match error") + err.Details = &UnificationErrDetail{ + Left: typeA, + Right: typeB, + } + return err + } + + return nil +} + +func (tc *typeChecker) checkExprWith(env *TypeEnv, expr *Expr, i int) *Error { + if i == len(expr.With) { + return nil + } + + target, value := expr.With[i].Target, expr.With[i].Value + targetType, valueType := env.GetByValue(target.Value), env.GetByValue(value.Value) + + if t, ok := targetType.(*types.Function); ok { // built-in function replacement + switch v := valueType.(type) { + case *types.Function: // ...by function + if !unifies(targetType, valueType) { + return newArgError(expr.With[i].Loc(), target.Value.(Ref), "arity mismatch", v.FuncArgs().Args, t.NamedFuncArgs()) + } + default: // ... by value, nothing to check + } + } + + return tc.checkExprWith(env, expr, i+1) +} + +func unify2(env *TypeEnv, a *Term, typeA types.Type, b *Term, typeB types.Type) bool { + + nilA := types.Nil(typeA) + nilB := types.Nil(typeB) + + if nilA && !nilB { + return unify1(env, a, typeB, false) + } else if nilB && !nilA { + return unify1(env, b, typeA, false) + } else if !nilA && !nilB { + return unifies(typeA, typeB) + } + + switch a.Value.(type) { + case *Array: + return unify2Array(env, a, b) + case *object: + return unify2Object(env, a, b) + case Var: + switch b.Value.(type) { + case Var: + return unify1(env, a, types.A, false) && unify1(env, b, env.GetByValue(a.Value), false) + case *Array: + return unify2Array(env, b, a) + case *object: + return unify2Object(env, b, a) + } + } + + return false +} + +func unify2Array(env *TypeEnv, a *Term, b *Term) bool { + arr := a.Value.(*Array) + switch bv := b.Value.(type) { + case *Array: + if arr.Len() == bv.Len() { + for i := range arr.Len() { + if !unify2(env, arr.Elem(i), env.GetByValue(arr.Elem(i).Value), bv.Elem(i), env.GetByValue(bv.Elem(i).Value)) { + return false + } + } + return true + } + case Var: + return unify1(env, a, types.A, false) && unify1(env, b, env.GetByValue(a.Value), false) + } + return false +} + +func unify2Object(env *TypeEnv, a *Term, b *Term) bool { + obj := a.Value.(Object) + switch bv := b.Value.(type) { + case *object: + cv := obj.Intersect(bv) + if obj.Len() == bv.Len() && bv.Len() == len(cv) { + for i := range cv { + if !unify2(env, cv[i][1], env.GetByValue(cv[i][1].Value), cv[i][2], env.GetByValue(cv[i][2].Value)) { + return false + } + } + return true + } + case Var: + return unify1(env, a, types.A, false) && unify1(env, b, env.GetByValue(a.Value), false) + } + return false +} + +func unify1(env *TypeEnv, term *Term, tpe types.Type, union bool) bool { + switch v := term.Value.(type) { + case *Array: + switch tpe := tpe.(type) { + case *types.Array: + return unify1Array(env, v, tpe, union) + case types.Any: + if types.Compare(tpe, types.A) == 0 { + for i := range v.Len() { + unify1(env, v.Elem(i), types.A, true) + } + return true + } + unifies := false + for i := range tpe { + unifies = unify1(env, term, tpe[i], true) || unifies + } + return unifies + } + return false + case *object: + switch tpe := tpe.(type) { + case *types.Object: + return unify1Object(env, v, tpe, union) + case types.Any: + if types.Compare(tpe, types.A) == 0 { + v.Foreach(func(key, value *Term) { + unify1(env, key, types.A, true) + unify1(env, value, types.A, true) + }) + return true + } + unifies := false + for i := range tpe { + unifies = unify1(env, term, tpe[i], true) || unifies + } + return unifies + } + return false + case Set: + switch tpe := tpe.(type) { + case *types.Set: + return unify1Set(env, v, tpe, union) + case types.Any: + if types.Compare(tpe, types.A) == 0 { + v.Foreach(func(elem *Term) { + unify1(env, elem, types.A, true) + }) + return true + } + unifies := false + for i := range tpe { + unifies = unify1(env, term, tpe[i], true) || unifies + } + return unifies + } + return false + case Ref, *ArrayComprehension, *ObjectComprehension, *SetComprehension: + return unifies(env.GetByValue(v), tpe) + case Var: + if !union { + if exist := env.GetByValue(v); exist != nil { + return unifies(exist, tpe) + } + env.tree.PutOne(term.Value, tpe) + } else { + env.tree.PutOne(term.Value, types.Or(env.GetByValue(v), tpe)) + } + return true + default: + if !IsConstant(v) { + panic("unreachable") + } + return unifies(env.GetByValue(term.Value), tpe) + } +} + +func unify1Array(env *TypeEnv, val *Array, tpe *types.Array, union bool) bool { + if val.Len() != tpe.Len() && tpe.Dynamic() == nil { + return false + } + for i := range val.Len() { + if !unify1(env, val.Elem(i), tpe.Select(i), union) { + return false + } + } + return true +} + +func unify1Object(env *TypeEnv, val Object, tpe *types.Object, union bool) bool { + if val.Len() != len(tpe.Keys()) && tpe.DynamicValue() == nil { + return false + } + stop := val.Until(func(k, v *Term) bool { + if IsConstant(k.Value) { + if child := selectConstant(tpe, k); child != nil { + if !unify1(env, v, child, union) { + return true + } + } else { + return true + } + } else { + // Inferring type of value under dynamic key would involve unioning + // with all property values of tpe whose keys unify. For now, type + // these values as Any. We can investigate stricter inference in + // the future. + unify1(env, v, types.A, union) + } + return false + }) + return !stop +} + +func unify1Set(env *TypeEnv, val Set, tpe *types.Set, union bool) bool { + of := types.Values(tpe) + return !val.Until(func(elem *Term) bool { + return !unify1(env, elem, of, union) + }) +} + +func (tc *typeChecker) err(errors []*Error) { + tc.errs = append(tc.errs, errors...) +} + +type refChecker struct { + env *TypeEnv + errs Errors + varRewriter varRewriter +} + +func rewriteVarsNop(node Ref) Ref { + return node +} + +func newRefChecker(env *TypeEnv, f varRewriter) *refChecker { + if f == nil { + f = rewriteVarsNop + } + + return &refChecker{ + env: env, + errs: nil, + varRewriter: f, + } +} + +func (rc *refChecker) Visit(x any) bool { + switch x := x.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension: + return true + case *Expr: + switch terms := x.Terms.(type) { + case []*Term: + for i := 1; i < len(terms); i++ { + NewGenericVisitor(rc.Visit).Walk(terms[i]) + } + return true + case *Term: + NewGenericVisitor(rc.Visit).Walk(terms) + return true + } + case Ref: + if err := rc.checkApply(rc.env, x); err != nil { + rc.errs = append(rc.errs, err) + return true + } + if err := rc.checkRef(rc.env, rc.env.tree, x, 0); err != nil { + rc.errs = append(rc.errs, err) + } + } + return false +} + +func (rc *refChecker) checkApply(curr *TypeEnv, ref Ref) *Error { + if tpe, ok := curr.GetByRef(ref).(*types.Function); ok { + // NOTE(sr): We don't support first-class functions, except for `with`. + return newRefErrUnsupported(ref[0].Location, rc.varRewriter(ref), len(ref)-1, tpe) + } + + return nil +} + +func (rc *refChecker) checkRef(curr *TypeEnv, node *typeTreeNode, ref Ref, idx int) *Error { + + if idx == len(ref) { + return nil + } + + head := ref[idx] + + // NOTE(sr): as long as package statements are required, this isn't possible: + // the shortest possible rule ref is data.a.b (b is idx 2), idx 1 and 2 need to + // be strings or vars. + if idx == 1 || idx == 2 { + switch head.Value.(type) { + case Var, String: // OK + default: + have := rc.env.GetByValue(head.Value) + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, have, types.S, getOneOfForNode(node)) + } + } + + if _, ok := head.Value.(Var); ok && idx != 0 { + tpe := types.Keys(rc.env.getRefRecExtent(node)) + if exist := rc.env.GetByValue(head.Value); exist != nil { + if !unifies(tpe, exist) { + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, exist, tpe, getOneOfForNode(node)) + } + } else { + rc.env.tree.PutOne(head.Value, tpe) + } + } + + child := node.Child(head.Value) + if child == nil { + // NOTE(sr): idx is reset on purpose: we start over + switch { + case curr.next != nil: + next := curr.next + return rc.checkRef(next, next.tree, ref, 0) + + case RootDocumentNames.Contains(ref[0]): + if idx != 0 { + node.Children().Iter(func(_ Value, child *typeTreeNode) bool { + _ = rc.checkRef(curr, child, ref, idx+1) // ignore error + return false + }) + return nil + } + return rc.checkRefLeaf(types.A, ref, 1) + + default: + return rc.checkRefLeaf(types.A, ref, 0) + } + } + + if child.Leaf() { + return rc.checkRefLeaf(child.Value(), ref, idx+1) + } + + return rc.checkRef(curr, child, ref, idx+1) +} + +func (rc *refChecker) checkRefLeaf(tpe types.Type, ref Ref, idx int) *Error { + + if idx == len(ref) { + return nil + } + + head := ref[idx] + + keys := types.Keys(tpe) + if keys == nil { + return newRefErrUnsupported(ref[0].Location, rc.varRewriter(ref), idx-1, tpe) + } + + switch value := head.Value.(type) { + + case Var: + if exist := rc.env.GetByValue(value); exist != nil { + if !unifies(exist, keys) { + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, exist, keys, getOneOfForType(tpe)) + } + } else { + rc.env.tree.PutOne(value, types.Keys(tpe)) + } + + case Ref: + if exist := rc.env.Get(value); exist != nil { + if !unifies(exist, keys) { + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, exist, keys, getOneOfForType(tpe)) + } + } + + case *Array, Object, Set: + if !unify1(rc.env, head, keys, false) { + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, rc.env.Get(head), keys, nil) + } + + default: + child := selectConstant(tpe, head) + if child == nil { + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, nil, types.Keys(tpe), getOneOfForType(tpe)) + } + return rc.checkRefLeaf(child, ref, idx+1) + } + + return rc.checkRefLeaf(types.Values(tpe), ref, idx+1) +} + +func unifies(a, b types.Type) bool { + + if a == nil || b == nil { + return false + } + + anyA, ok1 := a.(types.Any) + if ok1 { + if unifiesAny(anyA, b) { + return true + } + } + + anyB, ok2 := b.(types.Any) + if ok2 { + if unifiesAny(anyB, a) { + return true + } + } + + if ok1 || ok2 { + return false + } + + switch a := a.(type) { + case types.Null: + _, ok := b.(types.Null) + return ok + case types.Boolean: + _, ok := b.(types.Boolean) + return ok + case types.Number: + _, ok := b.(types.Number) + return ok + case types.String: + _, ok := b.(types.String) + return ok + case *types.Array: + b, ok := b.(*types.Array) + if !ok { + return false + } + return unifiesArrays(a, b) + case *types.Object: + b, ok := b.(*types.Object) + if !ok { + return false + } + return unifiesObjects(a, b) + case *types.Set: + b, ok := b.(*types.Set) + if !ok { + return false + } + return unifies(types.Values(a), types.Values(b)) + case *types.Function: + // NOTE(sr): variadic functions can only be internal ones, and we've forbidden + // their replacement via `with`; so we disregard variadic here + if types.Arity(a) == types.Arity(b) { + b := b.(*types.Function) + for i := range a.FuncArgs().Args { + if !unifies(a.FuncArgs().Arg(i), b.FuncArgs().Arg(i)) { + return false + } + } + return true + } + return false + default: + panic("unreachable") + } +} + +func unifiesAny(a types.Any, b types.Type) bool { + if _, ok := b.(*types.Function); ok { + return false + } + for i := range a { + if unifies(a[i], b) { + return true + } + } + return len(a) == 0 +} + +func unifiesArrays(a, b *types.Array) bool { + + if !unifiesArraysStatic(a, b) { + return false + } + + if !unifiesArraysStatic(b, a) { + return false + } + + return a.Dynamic() == nil || b.Dynamic() == nil || unifies(a.Dynamic(), b.Dynamic()) +} + +func unifiesArraysStatic(a, b *types.Array) bool { + if a.Len() != 0 { + for i := range a.Len() { + if !unifies(a.Select(i), b.Select(i)) { + return false + } + } + } + return true +} + +func unifiesObjects(a, b *types.Object) bool { + if !unifiesObjectsStatic(a, b) { + return false + } + + if !unifiesObjectsStatic(b, a) { + return false + } + + return a.DynamicValue() == nil || b.DynamicValue() == nil || unifies(a.DynamicValue(), b.DynamicValue()) +} + +func unifiesObjectsStatic(a, b *types.Object) bool { + for _, k := range a.Keys() { + if !unifies(a.Select(k), b.Select(k)) { + return false + } + } + return true +} + +// typeErrorCause defines an interface to determine the reason for a type +// error. The type error details implement this interface so that type checking +// can report more actionable errors. +type typeErrorCause interface { + nilType() bool +} + +func causedByNilType(err *Error) bool { + cause, ok := err.Details.(typeErrorCause) + if !ok { + return false + } + return cause.nilType() +} + +// ArgErrDetail represents a generic argument error. +type ArgErrDetail struct { + Have []types.Type `json:"have"` + Want types.FuncArgs `json:"want"` +} + +// Lines returns the string representation of the detail. +func (d *ArgErrDetail) Lines() []string { + lines := make([]string, 2) + lines[0] = "have: " + formatArgs(d.Have) + lines[1] = "want: " + d.Want.String() + return lines +} + +func (d *ArgErrDetail) nilType() bool { + return slices.ContainsFunc(d.Have, types.Nil) +} + +// UnificationErrDetail describes a type mismatch error when two values are +// unified (e.g., x = [1,2,y]). +type UnificationErrDetail struct { + Left types.Type `json:"a"` + Right types.Type `json:"b"` +} + +func (a *UnificationErrDetail) nilType() bool { + return types.Nil(a.Left) || types.Nil(a.Right) +} + +// Lines returns the string representation of the detail. +func (a *UnificationErrDetail) Lines() []string { + lines := make([]string, 2) + lines[0] = fmt.Sprint("left : ", types.Sprint(a.Left)) + lines[1] = fmt.Sprint("right : ", types.Sprint(a.Right)) + return lines +} + +// RefErrUnsupportedDetail describes an undefined reference error where the +// referenced value does not support dereferencing (e.g., scalars). +type RefErrUnsupportedDetail struct { + Ref Ref `json:"ref"` // invalid ref + Pos int `json:"pos"` // invalid element + Have types.Type `json:"have"` // referenced type +} + +// Lines returns the string representation of the detail. +func (r *RefErrUnsupportedDetail) Lines() []string { + lines := []string{ + r.Ref.String(), + strings.Repeat("^", len(r.Ref[:r.Pos+1].String())), + fmt.Sprintf("have: %v", r.Have), + } + return lines +} + +// RefErrInvalidDetail describes an undefined reference error where the referenced +// value does not support the reference operand (e.g., missing object key, +// invalid key type, etc.) +type RefErrInvalidDetail struct { + Ref Ref `json:"ref"` // invalid ref + Pos int `json:"pos"` // invalid element + Have types.Type `json:"have,omitempty"` // type of invalid element (for var/ref elements) + Want types.Type `json:"want"` // allowed type (for non-object values) + OneOf []Value `json:"oneOf"` // allowed values (e.g., for object keys) +} + +// Lines returns the string representation of the detail. +func (r *RefErrInvalidDetail) Lines() []string { + lines := []string{r.Ref.String()} + offset := len(r.Ref[:r.Pos].String()) + 1 + pad := strings.Repeat(" ", offset) + lines = append(lines, pad+"^") + if r.Have != nil { + lines = append(lines, fmt.Sprintf("%shave (type): %v", pad, r.Have)) + } else { + lines = append(lines, fmt.Sprintf("%shave: %v", pad, r.Ref[r.Pos])) + } + if len(r.OneOf) > 0 { + lines = append(lines, fmt.Sprintf("%swant (one of): %v", pad, r.OneOf)) + } else { + lines = append(lines, fmt.Sprintf("%swant (type): %v", pad, r.Want)) + } + return lines +} + +func formatArgs(args []types.Type) string { + buf := make([]string, len(args)) + for i := range args { + buf[i] = types.Sprint(args[i]) + } + return "(" + strings.Join(buf, ", ") + ")" +} + +func newRefErrInvalid(loc *Location, ref Ref, idx int, have, want types.Type, oneOf []Value) *Error { + err := newRefError(loc, ref) + err.Details = &RefErrInvalidDetail{ + Ref: ref, + Pos: idx, + Have: have, + Want: want, + OneOf: oneOf, + } + return err +} + +func newRefErrUnsupported(loc *Location, ref Ref, idx int, have types.Type) *Error { + err := newRefError(loc, ref) + err.Details = &RefErrUnsupportedDetail{ + Ref: ref, + Pos: idx, + Have: have, + } + return err +} + +func newRefError(loc *Location, ref Ref) *Error { + return NewError(TypeErr, loc, "undefined ref: %v", ref) +} + +func newArgError(loc *Location, builtinName Ref, msg string, have []types.Type, want types.FuncArgs) *Error { + err := NewError(TypeErr, loc, "%v: %v", builtinName, msg) + err.Details = &ArgErrDetail{ + Have: have, + Want: want, + } + return err +} + +func getOneOfForNode(node *typeTreeNode) (result []Value) { + node.Children().Iter(func(k Value, _ *typeTreeNode) bool { + result = append(result, k) + return false + }) + + sortValueSlice(result) + return result +} + +func getOneOfForType(tpe types.Type) (result []Value) { + switch tpe := tpe.(type) { + case *types.Object: + for _, k := range tpe.Keys() { + v, err := InterfaceToValue(k) + if err != nil { + panic(err) + } + result = append(result, v) + } + + case types.Any: + for _, object := range tpe { + objRes := getOneOfForType(object) + result = append(result, objRes...) + } + } + + result = removeDuplicate(result) + sortValueSlice(result) + return result +} + +func sortValueSlice(sl []Value) { + sort.Slice(sl, func(i, j int) bool { + return sl[i].Compare(sl[j]) < 0 + }) +} + +func removeDuplicate(list []Value) []Value { + seen := make(map[Value]bool) + var newResult []Value + for _, item := range list { + if !seen[item] { + newResult = append(newResult, item) + seen[item] = true + } + } + return newResult +} + +func getArgTypes(env *TypeEnv, args []*Term) []types.Type { + pre := make([]types.Type, len(args)) + for i := range args { + pre[i] = env.Get(args[i]) + } + return pre +} + +// getPrefix returns the shortest prefix of ref that exists in env +func getPrefix(env *TypeEnv, ref Ref) (Ref, types.Type) { + if len(ref) == 1 { + t := env.Get(ref) + if t != nil { + return ref, t + } + } + for i := 1; i < len(ref); i++ { + t := env.Get(ref[:i]) + if t != nil { + return ref[:i], t + } + } + return nil, nil +} + +// override takes a type t and returns a type obtained from t where the path represented by ref within it has type o (overriding the original type of that path) +func override(ref Ref, t types.Type, o types.Type, rule *Rule) (types.Type, *Error) { + var newStaticProps []*types.StaticProperty + obj, ok := t.(*types.Object) + if !ok { + newType, err := getObjectType(ref, o, rule, types.NewDynamicProperty(types.A, types.A)) + if err != nil { + return nil, err + } + return newType, nil + } + found := false + if ok { + staticProps := obj.StaticProperties() + for _, prop := range staticProps { + valueCopy := prop.Value + key, err := InterfaceToValue(prop.Key) + if err != nil { + return nil, NewError(TypeErr, rule.Location, "unexpected error in override: %s", err.Error()) + } + if len(ref) > 0 && ref[0].Value.Compare(key) == 0 { + found = true + if len(ref) == 1 { + valueCopy = o + } else { + newVal, err := override(ref[1:], valueCopy, o, rule) + if err != nil { + return nil, err + } + valueCopy = newVal + } + } + newStaticProps = append(newStaticProps, types.NewStaticProperty(prop.Key, valueCopy)) + } + } + + // ref[0] is not a top-level key in staticProps, so it must be added + if !found { + newType, err := getObjectType(ref, o, rule, obj.DynamicProperties()) + if err != nil { + return nil, err + } + newStaticProps = append(newStaticProps, newType.StaticProperties()...) + } + return types.NewObject(newStaticProps, obj.DynamicProperties()), nil +} + +func getKeys(ref Ref, rule *Rule) ([]any, *Error) { + keys := []any{} + for _, refElem := range ref { + key, err := JSON(refElem.Value) + if err != nil { + return nil, NewError(TypeErr, rule.Location, "error getting key from value: %s", err.Error()) + } + keys = append(keys, key) + } + return keys, nil +} + +func getObjectTypeRec(keys []any, o types.Type, d *types.DynamicProperty) *types.Object { + if len(keys) == 1 { + staticProps := []*types.StaticProperty{types.NewStaticProperty(keys[0], o)} + return types.NewObject(staticProps, d) + } + + staticProps := []*types.StaticProperty{types.NewStaticProperty(keys[0], getObjectTypeRec(keys[1:], o, d))} + return types.NewObject(staticProps, d) +} + +func getObjectType(ref Ref, o types.Type, rule *Rule, d *types.DynamicProperty) (*types.Object, *Error) { + keys, err := getKeys(ref, rule) + if err != nil { + return nil, err + } + return getObjectTypeRec(keys, o, d), nil +} + +func getRuleAnnotation(as *AnnotationSet, rule *Rule) (result []*SchemaAnnotation) { + + for _, x := range as.GetSubpackagesScope(rule.Module.Package.Path) { + result = append(result, x.Schemas...) + } + + if x := as.GetPackageScope(rule.Module.Package); x != nil { + result = append(result, x.Schemas...) + } + + if x := as.GetDocumentScope(rule.Ref().GroundPrefix()); x != nil { + result = append(result, x.Schemas...) + } + + for _, x := range as.GetRuleScope(rule) { + result = append(result, x.Schemas...) + } + + return result +} + +func processAnnotation(ss *SchemaSet, annot *SchemaAnnotation, rule *Rule, allowNet []string) (types.Type, *Error) { + + var schema any + + if annot.Schema != nil { + if ss == nil { + return nil, nil + } + schema = ss.Get(annot.Schema) + if schema == nil { + return nil, NewError(TypeErr, rule.Location, "undefined schema: %v", annot.Schema) + } + } else if annot.Definition != nil { + schema = *annot.Definition + } + + tpe, err := loadSchema(schema, allowNet) + if err != nil { + return nil, NewError(TypeErr, rule.Location, err.Error()) //nolint:govet + } + + return tpe, nil +} + +func errAnnotationRedeclared(a *Annotations, other *Location) *Error { + return NewError(TypeErr, a.Location, "%v annotation redeclared: %v", a.Scope, other) +} diff --git a/third_party/opa/v1/ast/check_test.go b/third_party/opa/v1/ast/check_test.go new file mode 100644 index 000000000000..2a115d76ac7d --- /dev/null +++ b/third_party/opa/v1/ast/check_test.go @@ -0,0 +1,2586 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "reflect" + "slices" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestCheckInference(t *testing.T) { + + // fake_builtin_1([str1,str2]) + RegisterBuiltin(&Builtin{ + Name: "fake_builtin_1", + Decl: types.NewFunction( + nil, + types.NewArray( + []types.Type{types.S, types.S}, nil, + ), + ), + }) + + // fake_builtin_2({"a":str1,"b":str2}) + RegisterBuiltin(&Builtin{ + Name: "fake_builtin_2", + Decl: types.NewFunction( + nil, + types.NewObject( + []*types.StaticProperty{ + {Key: "a", Value: types.S}, + {Key: "b", Value: types.S}, + }, nil, + ), + ), + }) + + // fake_builtin_3({str1,str2,...}) + RegisterBuiltin(&Builtin{ + Name: "fake_builtin_3", + Decl: types.NewFunction( + nil, + types.NewSet(types.S), + ), + }) + + tests := []struct { + note string + query string + expected map[Var]types.Type + }{ + {"trivial", `x = 1`, map[Var]types.Type{ + Var("x"): types.N, + }}, + {"one-level", "y = 1; x = y", map[Var]types.Type{ + Var("x"): types.N, + Var("y"): types.N, + }}, + {"two-level", "z = 1; y = z; x = y", map[Var]types.Type{ + Var("x"): types.N, + Var("y"): types.N, + Var("z"): types.N, + }}, + {"array-nested", "[x, 1] = [true, y]", map[Var]types.Type{ + Var("x"): types.B, + Var("y"): types.N, + }}, + {"array-transitive", "y = [[2], 1]; [[x], 1] = y", map[Var]types.Type{ + Var("x"): types.N, + Var("y"): types.NewArray( + []types.Type{ + types.NewArray([]types.Type{types.N}, nil), + types.N, + }, nil), + }}, + {"array-embedded", `[1, "2", x] = data.foo`, map[Var]types.Type{ + Var("x"): types.A, + }}, + {"object-nested", `{"a": "foo", "b": {"c": x}} = {"a": y, "b": {"c": 2}}`, map[Var]types.Type{ + Var("x"): types.N, + Var("y"): types.S, + }}, + {"object-transitive", `y = {"a": "foo", "b": 2}; {"a": z, "b": x} = y`, map[Var]types.Type{ + Var("x"): types.N, + Var("z"): types.S, + }}, + {"object-embedded", `{"1": "2", "2": x} = data.foo`, map[Var]types.Type{ + Var("x"): types.A, + }}, + {"object-numeric-key", `x = {1: 2}; y = 1; x[y]`, map[Var]types.Type{ + Var("x"): types.NewObject([]*types.StaticProperty{{Key: json.Number("1"), Value: types.N}}, nil), + Var("y"): types.N, + }}, + {"object-object-key", `x = {{{}: 1}: 1}`, map[Var]types.Type{ + Var("x"): types.NewObject( + []*types.StaticProperty{types.NewStaticProperty( + map[string]any{ + "{}": json.Number("1"), + }, + types.N, + )}, + nil, + ), + }}, + {"object-composite-ref-operand", `x = {{}: 1}; x[{}] = y`, map[Var]types.Type{ + Var("x"): types.NewObject( + []*types.StaticProperty{types.NewStaticProperty( + map[string]any{}, + types.N, + )}, + nil, + ), + Var("y"): types.N, + }}, + {"sets", `x = {1, 2}; y = {{"foo", 1}, x}`, map[Var]types.Type{ + Var("x"): types.NewSet(types.N), + Var("y"): types.NewSet( + types.NewAny( + types.NewSet( + types.NewAny(types.N, types.S), + ), + types.NewSet( + types.N, + ), + ), + ), + }}, + {"sets-nested", `{"a", 1, 2} = {1,2,3}`, nil}, + {"sets-composite-ref-operand", `s = {[1, 2], [3, 4]}; s[[x, y]]`, map[Var]types.Type{ + Var("x"): types.N, + Var("y"): types.N, + Var("s"): types.NewSet(types.NewArray([]types.Type{types.N, types.N}, nil)), + }}, + {"empty-composites", ` + obj = {}; + arr = []; + set = set(); + obj[i] = v1; + arr[j] = v2; + set[v3]; + obj = {"foo": "bar"}; + arr = [1]; + set = {1,2,3} + `, map[Var]types.Type{ + Var("obj"): types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + Var("i"): types.A, + Var("v1"): types.A, + Var("arr"): types.NewArray(nil, types.A), + Var("j"): types.N, + Var("v2"): types.A, + Var("set"): types.NewSet(types.A), + Var("v3"): types.A, + }}, + {"empty-composite-property", ` + obj = {}; + obj.foo = x; + obj[i].foo = y + `, map[Var]types.Type{ + Var("x"): types.A, + Var("y"): types.A, + }}, + {"local-reference", ` + a = [ + 1, + { + "foo": [ + {"bar": null}, + -1, + {"bar": true} + ] + }, + 3]; + + x = a[1].foo[_].bar`, map[Var]types.Type{ + Var("x"): types.NewAny(types.Nl, types.B), + }}, + {"local-reference-var", ` + + a = [ + { + "a": null, + "b": { + "foo": { + "c": {1,}, + }, + "bar": { + "c": {"hello",}, + }, + }, + }, + { + "a": null, + "b": { + "foo": { + "c": {1,}, + }, + "bar": { + "c": {true,}, + }, + }, + }, + ]; + x = a[i].b[j].c[k] + `, map[Var]types.Type{ + Var("i"): types.N, + Var("j"): types.S, + Var("k"): types.NewAny(types.S, types.N, types.B), + Var("x"): types.NewAny(types.S, types.N, types.B), + }}, + {"local-reference-var-any", ` + a = [[], {}]; + a[_][i] + `, map[Var]types.Type{ + Var("i"): types.A, + }}, + {"local-reference-nested", ` + a = [["foo"], 0, {"bar": "baz"}, 2]; + b = [0,1,2,3]; + a[b[_]][k] = v + `, map[Var]types.Type{ + Var("k"): types.NewAny(types.S, types.N), + }}, + {"simple-built-in", "plus(1,2,x)", map[Var]types.Type{ + Var("x"): types.N, + }}, + {"simple-built-in-exists", "plus(1,2,x); plus(x,2,y)", map[Var]types.Type{ + Var("x"): types.N, + Var("y"): types.N, + }}, + {"array-builtin", `fake_builtin_1([x,"foo"])`, map[Var]types.Type{ + Var("x"): types.S, + }}, + {"object-builtin", `fake_builtin_2({"a": "foo", "b": x})`, map[Var]types.Type{ + Var("x"): types.S, + }}, + {"set-builtin", `fake_builtin_3({"foo", x})`, map[Var]types.Type{ + Var("x"): types.S, + }}, + {"array-comprehension-ref-closure", `a = [1,"foo",3]; x = [ i | a[_] = i ]`, map[Var]types.Type{ + Var("x"): types.NewArray(nil, types.NewAny(types.N, types.S)), + }}, + {"array-comprehension-var-closure", `x = 1; y = [ i | x = i ]`, map[Var]types.Type{ + Var("y"): types.NewArray(nil, types.N), + }}, + {"dynamic-object-value", `q = {"a": "b", "c": "d"}; {k: [v]} = {k: [q[k]]}`, map[Var]types.Type{ + Var("k"): types.S, + Var("v"): types.A, + }}, + { + note: "type unioning: arrays", + query: `x = [[1], ["foo"]]; x[_] = [y]`, + expected: map[Var]types.Type{ + Var("y"): types.NewAny( + types.N, types.S, + ), + }, + }, + { + note: "type unioning: sets", + query: `x = {[1], ["foo"]}; x[[y]]`, + expected: map[Var]types.Type{ + Var("y"): types.NewAny( + types.N, types.S, + ), + }, + }, + { + note: "type unioning: object values", + query: `x = {"a": [1], "b": ["foo"]}; x[_] = [y]`, + expected: map[Var]types.Type{ + Var("y"): types.NewAny( + types.N, types.S, + ), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + body := MustParseBody(tc.query) + checker := newTypeChecker() + env := checker.Env(BuiltinMap) + env, err := checker.CheckBody(env, body) + if len(err) != 0 { + t.Fatalf("Unexpected error: %v", err) + } + for k, tpe := range tc.expected { + result := env.Get(k) + if tpe == nil { + if result != nil { + t.Errorf("Expected %v type to be unset but got: %v", k, result) + } + } else { + if result == nil { + t.Errorf("Expected to infer %v => %v but got nil", k, tpe) + } else if types.Compare(tpe, result) != 0 { + t.Errorf("Expected to infer %v => %v but got %v", k, tpe, result) + } + } + } + }) + } +} + +func TestCheckInferenceRules(t *testing.T) { + + // Rules must have refs resolved, safe ordering, etc. Each pair is a + // (package path, rule) tuple. The test constructs the Rule objects to + // run the inference on from these inputs. + ruleset1 := [][2]string{ + {`a`, `trivial = true { true }`}, + {`a`, `complete = [{"foo": x}] { x = 1 }`}, + {`a`, `partialset[{"foo": x}] { y = "bar"; x = y }`}, + {`a`, `partialobj[x] = {"foo": y} { y = "bar"; x = y }`}, + {`b`, `trivial_ref = x { x = data.a.trivial }`}, + {`b`, `transitive_ref = [x] { y = data.b.trivial_ref; x = y }`}, + {`c`, `else_kw = null { false } else = 100 { true } else = "foo" { true }`}, + {`iteration`, `arr = [[1], ["two"], {"x": true}, ["four"]] { true }`}, + {`iteration`, `values[x] { data.iteration.arr[_][_] = x } `}, + {`iteration`, `keys[i] { data.iteration.arr[_][i] = _ } `}, + {`disjunction`, `partialset[1] { true }`}, + {`disjunction`, `partialset[x] { x = "foo" }`}, + {`disjunction`, `partialset[3] { true }`}, + {`disjunction`, `partialobj[x] = y { y = "bar"; x = "foo" }`}, + {`disjunction`, `partialobj[x] = y { y = 100; x = "foo" }`}, + {`disjunction`, `complete = 1 { true }`}, + {`disjunction`, `complete = x { x = "foo" }`}, + {`prefix.a.b.c`, `d = true { true }`}, + {`prefix.i.j.k`, `p = 1 { true }`}, + {`prefix.i.j.k`, `p = "foo" { true }`}, + {`default_rule`, `default x = 1`}, + {`default_rule`, `x = "foo" { true }`}, + {`unknown_type`, `p = [x] { x = data.deadbeef }`}, + {`nested_ref`, `inner = {"a": 0, "b": "1"} { true }`}, + {`nested_ref`, `middle = [[1, true], ["foo", false]] { true }`}, + {`nested_ref`, `p = x { data.nested_ref.middle[data.nested_ref.inner.a][0] = x }`}, + {`number_key`, `q[x] = y { a = ["a", "b"]; y = a[x] }`}, + {`non_leaf`, `p[x] { data.prefix.i[x][_] }`}, + } + ruleset2 := [][2]string{ + {`ref_rule_single`, `p.q.r { true }`}, + {`ref_rule_single_with_number_key`, `p.q[3] { true }`}, + {`ref_regression_array_key`, + `walker[[p, v]] = o { l = input; walk(l, k); [p, v] = k; o = {} }`}, + {`overlap`, `p.q[r] = y { x = ["a", "b"]; y = x[r] }`}, + {`overlap`, `p.q.r = false { true }`}, + {`overlap`, `p.q.r = "false" { true }`}, + {`overlap`, `p.q[42] = 1337 { true }`}, + {`overlap`, `p.q2.a = input.a { true }`}, + {`overlap`, `p.q2[56] = input.a { true }`}, + } + ruleset3 := [][2]string{ + {`simple`, `p.q[r][s] = 42 { x = ["a", "b"]; r = x[s] }`}, + {`mixed`, `p.q[r].s[t] = 42 { x = ["a", "b"]; r = x[t] }`}, + {`overrides`, `p.q[r] = "foo" { x = ["a", "b"]; r = x[_] }`}, + {`overrides`, `p.q.r[s] = 42 { x = ["a", "b"]; x[s] }`}, + {`overrides`, `p.q[r].s = true { x = [true, false]; r = x[_] }`}, + {`overrides_static`, `p.q[r].a = "foo" { r = "bar"; s = "baz" }`}, + {`overrides_static`, `p.q[r].b = 42 { r = "bar" }`}, + {`overrides_static`, `p.q[r].c = true { r = "bar" }`}, + } + + tests := []struct { + note string + rules [][2]string + ref string + expected types.Type + }{ + {"trivial", ruleset1, `data.a.trivial`, types.B}, + + {"complete-doc", ruleset1, `data.a.complete`, types.NewArray( + []types.Type{types.NewObject( + []*types.StaticProperty{{ + Key: "foo", Value: types.N, + }}, + nil, + )}, + nil, + )}, + + {"complete-doc-suffix", ruleset1, `data.a.complete[0].foo`, types.N}, + + {"else-kw", ruleset1, "data.c.else_kw", types.NewAny(types.Nl, types.N, types.S)}, + + {"partial-set-doc", ruleset1, `data.a.partialset`, types.NewSet( + types.NewObject( + []*types.StaticProperty{{ + Key: "foo", Value: types.S, + }}, + nil, + ), + )}, + + {"partial-object-doc", ruleset1, "data.a.partialobj", types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.NewObject( + []*types.StaticProperty{{ + Key: "foo", Value: types.S, + }}, + nil, + )), + )}, + + {"partial-object-doc-suffix", ruleset1, `data.a.partialobj.somekey.foo`, types.S}, + + {"partial-object-doc-number-suffix", ruleset1, "data.number_key.q[1]", types.S}, + + {"iteration", ruleset1, "data.iteration.values", types.NewSet( + types.NewAny( + types.S, + types.N, + types.B), + )}, + + {"iteration-keys", ruleset1, "data.iteration.keys", types.NewSet( + types.NewAny( + types.S, + types.N, + ), + )}, + + {"disj-complete-doc", ruleset1, "data.disjunction.complete", types.NewAny( + types.S, + types.N, + )}, + + {"disj-partial-set-doc", ruleset1, "data.disjunction.partialset", types.NewSet( + types.NewAny( + types.S, + types.N), + )}, + + {"disj-partial-obj-doc", ruleset1, "data.disjunction.partialobj", types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.NewAny(types.S, types.N)), + )}, + + {"ref", ruleset1, "data.b.trivial_ref", types.B}, + + {"ref-transitive", ruleset1, "data.b.transitive_ref", types.NewArray( + []types.Type{ + types.B, + }, + nil, + )}, + + {"prefix", ruleset1, `data.prefix.a.b`, types.NewObject( + []*types.StaticProperty{{ + Key: "c", Value: types.NewObject( + []*types.StaticProperty{{Key: "d", Value: types.B}}, + types.NewDynamicProperty(types.S, types.A), + ), + }}, + types.NewDynamicProperty(types.S, types.A), + )}, + + // Check that prefixes that iterate fallback to any. + {"prefix-iter", ruleset1, `data.prefix.i.j[k]`, types.A}, + + // Check that iteration targeting a rule (but nonetheless prefixed) falls back to any. + {"prefix-iter-2", ruleset1, `data.prefix.i.j[k].p`, types.A}, + + {"default-rule", ruleset1, "data.default_rule.x", types.NewAny( + types.S, + types.N, + )}, + + {"unknown-type", ruleset1, "data.unknown_type.p", types.NewArray( + []types.Type{ + types.A, + }, + nil, + )}, + + {"nested-ref", ruleset1, "data.nested_ref.p", types.NewAny( + types.S, + types.N, + )}, + + {"non-leaf", ruleset1, "data.non_leaf.p", types.NewSet( + types.S, + )}, + + {"ref-rules single value, full ref", ruleset2, "data.ref_rule_single.p.q.r", types.B}, + {"ref-rules single value, prefix", ruleset2, "data.ref_rule_single.p", + types.NewObject( + []*types.StaticProperty{{ + Key: "q", Value: types.NewObject( + []*types.StaticProperty{{Key: "r", Value: types.B}}, + types.NewDynamicProperty(types.S, types.A), + ), + }}, + types.NewDynamicProperty(types.S, types.A), + )}, + + {"ref-rules single value, number key, full ref", ruleset2, "data.ref_rule_single_with_number_key.p.q[3]", types.B}, + {"ref-rules single value, number key, prefix", ruleset2, "data.ref_rule_single_with_number_key.p", + types.NewObject( + []*types.StaticProperty{{ + Key: "q", Value: types.NewObject( + []*types.StaticProperty{{Key: json.Number("3"), Value: types.B}}, + types.NewDynamicProperty(types.S, types.A), + ), + }}, + types.NewDynamicProperty(types.S, types.A), + )}, + + {"ref_regression_array_key", ruleset2, "data.ref_regression_array_key.walker", + types.NewObject( + nil, + types.NewDynamicProperty(types.NewArray([]types.Type{types.NewArray(types.NewAny(), types.A), types.A}, nil), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A))), + )}, + { + note: "ref-rules single value, full ref to known leaf", + rules: ruleset2, + ref: "data.overlap.p.q.r", + expected: types.NewAny(types.B, types.S), + }, + { + note: "ref-rules single value, full ref to known leaf (same key type as dynamic, different value type)", + rules: ruleset2, + ref: "data.overlap.p.q[42]", + expected: types.N, + }, + { + note: "ref-rules single value, full ref to known leaf (any type)", + rules: ruleset2, + ref: "data.overlap.p.q2.a", + expected: types.A, + }, + { + note: "ref-rules single value, full ref to known leaf (same key type as dynamic, any type)", + rules: ruleset2, + ref: "data.overlap.p.q2[56]", + expected: types.A, + }, + { + note: "ref-rules single value, full ref to dynamic leaf", + rules: ruleset2, + ref: "data.overlap.p.q[1]", + expected: types.Any{types.B, types.N, types.S}, // key type cannot be tied to specific dynamic value type, so we get all of them + }, + { + note: "ref-rules single value, prefix ref to partial object root", + rules: ruleset2, + ref: "data.overlap.p.q", + expected: types.NewObject( + nil, + types.NewDynamicProperty(types.Any{types.N, types.S}, types.Any{types.B, types.N, types.S}), + ), + }, + { + note: "general ref-rules, only vars in obj-path, complete obj access", + rules: ruleset3, + ref: "data.simple.p.q", + expected: types.NewObject( + []*types.StaticProperty{}, + types.NewDynamicProperty(types.S, + types.NewObject( + []*types.StaticProperty{}, + types.NewDynamicProperty(types.N, types.N), + ), + ), + ), + }, + { + note: "general ref-rules, only vars in obj-path, intermediate obj access", + rules: ruleset3, + ref: "data.simple.p.q.b", + expected: types.NewObject( + []*types.StaticProperty{}, + types.NewDynamicProperty(types.N, types.N), + ), + }, + { + note: "general ref-rules, only vars in obj-path, leaf access", + rules: ruleset3, + ref: "data.simple.p.q.b[1]", + expected: types.N, + }, + { + note: "general ref-rules, vars and constants in obj-path, complete obj access", + rules: ruleset3, + ref: "data.mixed.p.q", + expected: types.NewObject( + []*types.StaticProperty{}, + types.NewDynamicProperty(types.S, + types.NewObject(nil, + types.NewDynamicProperty(types.S, types.NewObject(nil, + types.NewDynamicProperty(types.N, types.N))), + ), + ), + ), + }, + { + note: "general ref-rules, key overrides, complete obj access", + rules: ruleset3, + ref: "data.overrides.p.q", + expected: types.NewObject(nil, types.NewDynamicProperty( + types.Or(types.B, types.S), + types.Any{ + types.S, + types.NewObject(nil, types.NewDynamicProperty( + types.Any{types.N, types.S}, + types.Any{types.B, types.N})), + }, + ), + ), + }, + { + note: "general ref-rules, multiple static key overrides, complete obj access", + rules: ruleset3, + ref: "data.overrides_static.p.q", + expected: types.NewObject( + []*types.StaticProperty{}, + types.NewDynamicProperty(types.S, + types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.Any{types.B, types.N, types.S}), + ), + ), + ), + }, + { + note: "general ref-rules, multiple static key overrides, intermediate obj access", + rules: ruleset3, + ref: "data.overrides_static.p.q.foo", + expected: types.NewObject(nil, + types.NewDynamicProperty(types.S, types.Any{types.B, types.N, types.S}), + ), + }, + { + note: "general ref-rules, multiple static key overrides, leaf access (a)", + rules: ruleset3, + ref: "data.overrides_static.p.q.foo.a", + expected: types.Any{types.B, types.N, types.S}, // Dynamically build object types don't have static properties, so even though we "know" the 'a' key has a string value, we've lost this information. + }, + { + note: "general ref-rules, multiple static key overrides, leaf access (b)", + rules: ruleset3, + ref: "data.overrides_static.p.q.bar.b", + expected: types.Any{types.B, types.N, types.S}, + }, + { + note: "general ref-rules, multiple static key overrides, leaf access (c)", + rules: ruleset3, + ref: "data.overrides_static.p.q.baz.c", + expected: types.Any{types.B, types.N, types.S}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var elems []util.T + + // Convert test rules into rule slice for call. + for i := range tc.rules { + pkg := MustParsePackage(`package ` + tc.rules[i][0]) + rule := MustParseRule(tc.rules[i][1]) + module := &Module{ + Package: pkg, + Rules: []*Rule{rule}, + } + rule.Module = module + elems = append(elems, rule) + for next := rule.Else; next != nil; next = next.Else { + next.Module = module + elems = append(elems, next) + } + } + + ref := MustParseRef(tc.ref) + checker := newTypeChecker() + env, err := checker.CheckTypes(newTypeChecker().Env(map[string]*Builtin{"walk": BuiltinMap["walk"]}), elems, nil) + + if err != nil { + t.Fatalf("Unexpected error %v:", err) + } + + result := env.Get(ref) + if tc.expected == nil { + if result != nil { + t.Errorf("Expected %v type to be unset but got: %v", ref, result) + } + } else { + if result == nil { + t.Errorf("Expected to infer %v => %v but got nil", ref, tc.expected) + } else if types.Compare(tc.expected, result) != 0 { + t.Errorf("Expected to infer %v => %v but got %v", ref, tc.expected, result) + } + } + }) + } + +} + +func TestCheckInferenceOverlapWithRules(t *testing.T) { + ruleset1 := [][2]string{ + {`prefix.i.j.k`, `p = 1 { true }`}, + {`prefix.i.j.k`, `p = "foo" { true }`}, + } + tests := []struct { + note string + rules [][2]string + ref string + expected types.Type // ref's type + query string + extra map[Var]types.Type + }{ + { + note: "non-leaf, extra vars", + rules: ruleset1, + ref: "data.prefix.i.j[k]", + expected: types.A, + query: "data.prefix.i.j[k][b]", + extra: map[Var]types.Type{ + Var("k"): types.S, + Var("b"): types.S, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var elems []util.T + + // Convert test rules into rule slice for "warmup" call. + for i := range tc.rules { + pkg := MustParsePackage(`package ` + tc.rules[i][0]) + rule := MustParseRule(tc.rules[i][1]) + module := &Module{ + Package: pkg, + Rules: []*Rule{rule}, + } + rule.Module = module + elems = append(elems, rule) + for next := rule.Else; next != nil; next = next.Else { + next.Module = module + elems = append(elems, next) + } + } + + ref := MustParseRef(tc.ref) + checker := newTypeChecker() + env, err := checker.CheckTypes(nil, elems, nil) + if err != nil { + t.Fatalf("Unexpected error %v:", err) + } + + result := env.Get(ref) + if tc.expected == nil { + if result != nil { + t.Errorf("Expected %v type to be unset but got: %v", ref, result) + } + } else { + if result == nil { + t.Errorf("Expected to infer %v => %v but got nil", ref, tc.expected) + } else if types.Compare(tc.expected, result) != 0 { + t.Errorf("Expected to infer %v => %v but got %v", ref, tc.expected, result) + } + } + + body := MustParseBody(tc.query) + env, err = checker.CheckBody(env, body) + if len(err) != 0 { + t.Fatalf("Unexpected error: %v", err) + } + for ex, exp := range tc.extra { + act := env.Get(ex) + if types.Compare(act, exp) != 0 { + t.Errorf("Expected to infer extra %v => %v but got %v", ex, exp, act) + } + } + }) + } +} + +func TestCheckErrorSuppression(t *testing.T) { + + query := `arr = [1,2,3]; arr[0].deadbeef = 1` + + _, errs := newTypeChecker().CheckBody(nil, MustParseBody(query)) + if len(errs) != 1 { + t.Fatalf("Expected exactly one error but got: %v", errs) + } + + _, ok := errs[0].Details.(*RefErrUnsupportedDetail) + if !ok { + t.Fatalf("Expected ref error but got: %v", errs) + } + + query = `_ = [true | count(1)]` + + _, errs = newTypeChecker().CheckBody(newTypeChecker().Env(BuiltinMap), MustParseBody(query)) + if len(errs) != 1 { + t.Fatalf("Expected exactly one error but got: %v", errs) + } + + _, ok = errs[0].Details.(*ArgErrDetail) + if !ok { + t.Fatalf("Expected arg error but got: %v", errs) + } + +} + +func TestCheckBadCardinality(t *testing.T) { + tests := []struct { + body string + exp []types.Type + }{ + { + body: "plus(1)", + exp: []types.Type{types.N}, + }, + { + body: "plus(1, 2, 3, 4)", + exp: []types.Type{types.N, types.N, types.N, types.N}, + }, + } + for _, test := range tests { + body := MustParseBody(test.body) + tc := newTypeChecker() + env := tc.Env(BuiltinMap) + _, err := tc.CheckBody(env, body) + if len(err) != 1 || err[0].Code != TypeErr { + t.Fatalf("Expected 1 type error from %v but got: %v", body, err) + } + detail, ok := err[0].Details.(*ArgErrDetail) + if !ok { + t.Fatalf("Expected argument error details but got: %v", err) + } + if len(test.exp) != len(detail.Have) { + t.Fatalf("Expected arg types %v but got: %v", test.exp, detail.Have) + } + for i := range test.exp { + if types.Compare(test.exp[i], detail.Have[i]) != 0 { + t.Fatalf("Expected types for %v to be %v but got: %v", body[0], test.exp, detail.Have) + } + } + } +} + +func TestCheckMatchErrors(t *testing.T) { + tests := []struct { + note string + query string + }{ + {"null", "null = true"}, + {"boolean", "true = null"}, + {"number", "1 = null"}, + {"string", `"hello" = null`}, + {"array", "[1,2,3] = null"}, + {"array-nested", `[1,2,3] = [1,2,"3"]`}, + {"array-nested-2", `[1,2] = [1,2,3]`}, + {"array-dynamic", `[ true | true ] = [x | a = [1, "foo"]; x = a[_]]`}, + {"object", `{"a": 1, "b": 2} = null`}, + {"object-nested", `{"a": 1, "b": "2"} = {"a": 1, "b": 2}`}, + {"object-nested-2", `{"a": 1} = {"a": 1, "b": "2"}`}, + {"set", "{1,2,3} = null"}, + {"any", `x = ["str", 1]; x[_] = null`}, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + body := MustParseBody(tc.query) + checker := newTypeChecker() + _, err := checker.CheckBody(nil, body) + if len(err) != 1 { + t.Fatalf("Expected exactly one error from %v, but got:\n%v", body, err) + } + }) + } +} + +func TestCheckBuiltinErrors(t *testing.T) { + + RegisterBuiltin(&Builtin{ + Name: "fake_builtin_2", + Decl: types.NewFunction( + types.Args( + types.NewAny(types.NewObject( + []*types.StaticProperty{ + {Key: "a", Value: types.S}, + {Key: "b", Value: types.S}, + }, nil), + ), + ), + types.NewObject( + []*types.StaticProperty{ + {Key: "b", Value: types.S}, + {Key: "c", Value: types.S}, + }, nil, + ), + ), + }) + + tests := []struct { + note string + query string + }{ + {"trivial", "plus(true, 1, x)"}, + {"refs", "x = [null]; plus(x[0], 1, y)"}, + {"array comprehensions", `sum([null | true], x)`}, + {"arrays-any", `sum([1,2,"3",4], x)`}, + {"arrays-bad-input", `contains([1,2,3], "x")`}, + {"objects-any", `fake_builtin_2({"a": a, "c": c})`}, + {"objects-bad-input", `sum({"a": 1, "b": 2}, x)`}, + {"sets-any", `sum({1,2,"3",4}, x)`}, + {"virtual-ref", `plus(data.test.p, data.coffee, 0)`}, + } + + env := newTestEnv([]string{ + `p = "foo" { true }`, + `f(x) = x { true }`, + }) + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + body := MustParseBody(tc.query) + checker := newTypeChecker() + _, err := checker.CheckBody(env, body) + if len(err) != 1 { + t.Fatalf("Expected exactly one error from %v but got:\n%v", body, err) + } + }) + } +} + +func TestVoidBuiltins(t *testing.T) { + + // Void builtins are used in test cases. + RegisterBuiltin(&Builtin{ + Name: "fake_void_builtin", + Decl: types.NewFunction( + types.Args(types.N), + nil, + ), + }) + + tests := []struct { + query string + wantErr bool + }{ + {"fake_void_builtin(1)", false}, + {"fake_void_builtin()", true}, + {"fake_void_builtin(1,2)", true}, + {"fake_void_builtin(true)", true}, + } + + for _, tc := range tests { + body := MustParseBody(tc.query) + checker := newTypeChecker() + _, errs := checker.CheckBody(newTestEnv(nil), body) + if len(errs) != 0 && !tc.wantErr { + t.Fatal(errs) + } else if len(errs) == 0 && tc.wantErr { + t.Fatal("Expected error") + } + } +} + +func TestVariadicBuiltins(t *testing.T) { + + // Ensure type checking allows variadic arguments. + env := newTypeChecker().Env(map[string]*Builtin{ + "println": { + Name: "println", + Decl: types.NewVariadicFunction([]types.Type{}, types.A, nil), + }, + }) + + _, errs := newTypeChecker().CheckBody(env, MustParseBody(`println("hello", "world")`)) + if len(errs) != 0 { + t.Fatal(errs) + } + + // Test error checking on positional arguments on a variadic function. + env = newTypeChecker().Env(map[string]*Builtin{ + "println": { + Name: "println", + Decl: types.NewVariadicFunction([]types.Type{types.N}, types.A, nil), + }, + }) + + _, errs = newTypeChecker().CheckBody(env, MustParseBody(`println("hello", 7)`)) + if len(errs) != 1 { + t.Fatal("expected one error but got:", errs) + } + + detail := errs[0].Details.(*ArgErrDetail) + + if len(detail.Have) != 2 || len(detail.Want.Args) != 1 || types.Compare(detail.Want.Args[0], types.N) != 0 { + t.Fatal("unexpected detail:", detail) + } + + // Test error checking on variadic arguments. + env = newTypeChecker().Env(map[string]*Builtin{ + "println": { + Name: "println", + Decl: types.NewVariadicFunction([]types.Type{types.N}, types.N, nil), + }, + }) + + _, errs = newTypeChecker().CheckBody(env, MustParseBody(`println(7, "world")`)) + if len(errs) != 1 { + t.Fatal("expected one error but got:", errs) + } + + detail = errs[0].Details.(*ArgErrDetail) + + if len(detail.Have) != 2 || len(detail.Want.Args) != 1 || types.Compare(detail.Want.Args[0], types.N) != 0 || types.Compare(detail.Want.Variadic, types.N) != 0 { + t.Fatal("unexpected detail:", detail) + } + +} + +func TestCheckRefErrUnsupported(t *testing.T) { + + query := `arr = [[1,2],[3,4]]; arr[1][0].deadbeef` + + _, errs := newTypeChecker().CheckBody(nil, MustParseBody(query)) + if len(errs) != 1 { + t.Fatalf("Expected exactly one error but got: %v", errs) + } + + details, ok := errs[0].Details.(*RefErrUnsupportedDetail) + if !ok { + t.Fatalf("Expected ref err unsupported but got: %v", errs) + } + + wantRef := MustParseRef(`arr[1][0].deadbeef`) + wantPos := 2 + wantHave := types.N + + if !wantRef.Equal(details.Ref) || + wantPos != details.Pos || + types.Compare(wantHave, details.Have) != 0 { + t.Fatalf("Expected (%v, %v, %v) but got: (%v, %v, %v)", wantRef, wantPos, wantHave, details.Ref, details.Pos, details.Have) + } + +} + +func TestCheckRefErrInvalid(t *testing.T) { + + env := newTestEnv([]string{ + `p { true }`, + `q = {"foo": 1, "bar": 2} { true }`, + `a.b.c[3] = x { x = {"x": {"y": 2}} }`, + }) + + tests := []struct { + note string + query string + ref string + pos int + have types.Type + want types.Type + oneOf []Value + }{ + { + note: "bad non-leaf var", + query: `x = 1; data.test[x]`, + ref: `data.test[x]`, + pos: 2, + have: types.N, + want: types.S, + oneOf: []Value{String("a"), String("p"), String("q")}, + }, + { + note: "bad non-leaf ref", + query: `arr = [1]; data.test[arr[0]]`, + ref: `data.test[arr[0]]`, + pos: 2, + have: types.N, + want: types.S, + oneOf: []Value{String("a"), String("p"), String("q")}, + }, + { + note: "bad leaf ref", + query: `arr = [1]; data.test.q[arr[0]]`, + ref: `data.test.q[arr[0]]`, + pos: 3, + have: types.N, + want: types.S, + oneOf: []Value{String("bar"), String("foo")}, + }, + { + note: "bad ref hitting last term", + query: `x = true; data.test.a.b.c[x][_]`, + ref: `data.test.a.b.c[x][_]`, + pos: 5, + have: types.B, + want: types.Any{types.N, types.S}, + oneOf: []Value{Number("3")}, + }, + { + note: "bad ref hitting dynamic part", + query: `s = true; data.test.a.b.c[3].x[s][_] = _`, + ref: `data.test.a.b.c[3].x[s][_]`, + pos: 7, + have: types.B, + want: types.S, + oneOf: []Value{String("y")}, + }, + { + note: "bad leaf var", + query: `x = 1; data.test.q[x]`, + ref: `data.test.q[x]`, + pos: 3, + have: types.N, + want: types.S, + oneOf: []Value{String("bar"), String("foo")}, + }, + { + note: "bad array index value", + query: "arr = [[1,2],[3],[4]]; arr[0].dead.beef = x", + ref: "arr[0].dead.beef", + pos: 2, + want: types.N, + }, + { + note: "bad set element value", + query: `s = {{1,2},{3,4}}; x = {1,2}; s[x].deadbeef`, + ref: "s[x].deadbeef", + pos: 2, + want: types.N, + }, + { + note: "bad object key value", + query: `arr = [{"a": 1, "c": 3}, {"b": 2}]; arr[0].b`, + ref: "arr[0].b", + pos: 2, + want: types.S, + oneOf: []Value{String("a"), String("c")}, + }, + { + // NOTE(sr): Thins one and the next are special: it cannot work with ref heads, either, since we need at + // least ONE string term after data.test: a module needs a package line, and the shortest head ref + // possible is thus data.x.y. + note: "bad non-leaf value", + query: `data.test[1]`, + ref: "data.test[1]", + pos: 2, + have: types.N, + want: types.S, + oneOf: []Value{String("a"), String("p"), String("q")}, + }, + { + note: "bad non-leaf value (package)", // See note above ^^ + query: `data[1]`, + ref: "data[1]", + pos: 1, + have: types.N, + want: types.S, + oneOf: []Value{String("test")}, + }, + { + note: "composite ref operand", + query: `data.test.q[[1, 2]]`, + ref: "data.test.q[[1, 2]]", + pos: 3, + have: types.NewArray([]types.Type{types.N, types.N}, nil), + want: types.S, + }, + { + note: "composite ref type error 1", + query: `a = {[1], [2], [3]}; a[["foo"]]`, + ref: `a[["foo"]]`, + pos: 1, + have: types.NewArray([]types.Type{types.S}, nil), + want: types.NewArray([]types.Type{types.N}, nil), + }, + { + note: "composite ref type error 2", + query: `a = {{"a": 2}}; a[{"a": "foo"}]`, + ref: `a[{"a": "foo"}]`, + pos: 1, + have: types.NewObject([]*types.StaticProperty{types.NewStaticProperty("a", types.S)}, nil), + want: types.NewObject([]*types.StaticProperty{types.NewStaticProperty("a", types.N)}, nil), + }, + { + note: "composite ref type error 3 - array", + query: `a = [1,2,3]; a[{}] = b`, + ref: `a[{}]`, + pos: 1, + have: types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + want: types.N, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + + _, errs := newTypeChecker().CheckBody(env, MustParseBody(tc.query)) + if len(errs) != 1 { + t.Fatalf("Expected exactly one error but got: %v", errs) + } + + details, ok := errs[0].Details.(*RefErrInvalidDetail) + if !ok { + t.Fatalf("Expected ref error invalid but got: %v", errs) + } + + wantRef := MustParseRef(tc.ref) + + if details.Pos != tc.pos || + !details.Ref.Equal(wantRef) || + types.Compare(details.Want, tc.want) != 0 || + types.Compare(details.Have, tc.have) != 0 || + !reflect.DeepEqual(details.OneOf, tc.oneOf) { + t.Fatalf("Expected (%v, %v, %v, %v, %v) but got: (%v, %v, %v, %v, %v)", wantRef, tc.pos, tc.have, tc.want, tc.oneOf, details.Ref, details.Pos, details.Have, details.Want, details.OneOf) + } + }) + } +} + +func TestFunctionsTypeInference(t *testing.T) { + functions := []string{ + `foo([a, b]) = y if { split(a, b, y) }`, + `bar(x) = y if { count(x, y) }`, + `baz([x, y]) = z if { sprintf("%s%s", [x, y], z) }`, + `qux({"bar": x, "foo": y}) = {a: b} if { upper(y, a); json.unmarshal(x, b) }`, + `corge(x) = y if { qux({"bar": x, "foo": x}, a); baz([a["{5: true}"], "BUZ"], y) }`, + } + body := strings.Join(functions, "\n") + base := "package base\n" + body + + popts := ParserOptions{AllFutureKeywords: true} + + c := NewCompiler() + if c.Compile(map[string]*Module{"base": MustParseModuleWithOpts(base, popts)}); c.Failed() { + t.Fatalf("Failed to compile base module: %v", c.Errors) + } + + tests := []struct { + body string + wantErr bool + }{ + { + `fn(_) = y if { data.base.foo(["hello", 5], y) }`, + true, + }, + { + `fn(_) = y if { data.base.foo(["hello", "ll"], y) }`, + false, + }, + { + `fn(_) = y if { data.base.baz(["hello", "ll"], y) }`, + false, + }, + { + `fn(_) = y if { data.base.baz([5, ["foo", "bar", true]], y) }`, + false, + }, + { + `fn(_) = y if { data.base.baz(["hello", {"a": "b", "c": 3}], y) }`, + false, + }, + { + `fn(_) = y if { data.base.corge("this is not json", y) }`, + false, + }, + { + `fn(x) = y if { data.non_existent(x, a); y = a[0] }`, + true, + }, + { + `fn(x) = y if { y = [x] }`, + false, + }, + { + `f(x) = y if { [x] = y }`, + false, + }, + { + `fn(x) = y if { y = {"k": x} }`, + false, + }, + { + `f(x) = y if { {"k": x} = y }`, + false, + }, + { + `p if { [data.base.foo] }`, + true, + }, + { + `p if { x = data.base.foo }`, + true, + }, + { + `p if { data.base.foo(data.base.bar) }`, + true, + }, + } + + for n, test := range tests { + t.Run(fmt.Sprintf("Test Case %d", n), func(t *testing.T) { + mod := MustParseModuleWithOpts("package test\n"+test.body, popts) + c := NewCompiler() + c.Compile(map[string]*Module{"base": MustParseModuleWithOpts(base, popts), "mod": mod}) + if test.wantErr && !c.Failed() { + t.Errorf("Expected error but got success") + } else if !test.wantErr && c.Failed() { + t.Errorf("Expected success but got error: %v", c.Errors) + } + }) + } +} + +func TestFunctionTypeInferenceUnappliedWithObjectVarKey(t *testing.T) { + + // Run type inference on a function that constructs an object with a key + // from args in the head. + module := MustParseModule(` + package test + import rego.v1 + + f(x) := y if { y = {x: 1} } + `) + + elems := []util.T{ + module.Rules[0], + } + + env, err := newTypeChecker().CheckTypes(newTypeChecker().Env(BuiltinMap), elems, nil) + + if len(err) > 0 { + t.Fatal(err) + } + + // Check inferred type for reference to function. + tpe := env.Get(MustParseRef("data.test.f")) + exp := types.NewFunction([]types.Type{types.A}, types.NewObject(nil, types.NewDynamicProperty(types.A, types.N))) + + if types.Compare(tpe, exp) != 0 { + t.Fatalf("Expected %v but got %v", exp, tpe) + } +} + +func TestCheckValidErrors(t *testing.T) { + + module := MustParseModule(` + package test + import rego.v1 + + p if { + concat("", 1) # type error + } + + q if { + r(1) + } + + r(x) = x`) + + module2 := MustParseModule(` + package test + import rego.v1 + + b if { + a(1) # call erroneous function + } + + a(x) if { + max("foo") # max requires an array + } + + m if { + 1 / "foo" # type error + } + + n if { + m # call erroneous rule + }`) + + module3 := MustParseModule(` + package test + import rego.v1 + + x := {"a" : 1} + + y if { + z + } + + z if { + x[1] == 1 # undefined reference error + }`) + + tests := map[string]struct { + module *Module + numErr int + query []string + }{ + "single_type_error": {module: module, numErr: 1, query: []string{`data.test.p`}}, + "multiple_type_error": {module: module2, numErr: 2, query: []string{`data.test.a`, `data.test.m`}}, + "undefined_reference_error": {module: module3, numErr: 1, query: []string{`data.test.z`}}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + c := NewCompiler() + c.Compile(map[string]*Module{"test": tc.module}) + + if !c.Failed() { + t.Errorf("Expected error but got success") + } + + if len(c.Errors) != tc.numErr { + t.Fatalf("Expected %v error(s) but got: %v", tc.numErr, c.Errors) + } + + // check type of the rule/function that contains an error + for _, q := range tc.query { + tpe := c.TypeEnv.Get(MustParseRef(q)) + + if types.Compare(tpe, types.NewAny()) != 0 { + t.Fatalf("Expected Any type but got %v", tpe) + } + } + }) + } +} + +func TestCheckErrorDetails(t *testing.T) { + + tests := []struct { + detail ErrorDetails + expected []string + }{ + { + detail: &RefErrUnsupportedDetail{ + Ref: MustParseRef("data.foo[x]"), + Pos: 1, + Have: types.N, + }, + expected: []string{ + "data.foo[x]", + "^^^^^^^^", + "have: number", + }, + }, + { + detail: &RefErrInvalidDetail{ + Ref: MustParseRef("data.foo[x]"), + Pos: 2, + Have: types.N, + Want: types.S, + }, + expected: []string{ + "data.foo[x]", + " ^", + " have (type): number", + " want (type): string", + }, + }, + { + detail: &RefErrInvalidDetail{ + Ref: MustParseRef("data.foo[100]"), + Pos: 2, + Want: types.S, + OneOf: []Value{ + String("a"), + String("b"), + }, + }, + expected: []string{ + "data.foo[100]", + " ^", + " have: 100", + ` want (one of): ["a" "b"]`, + }, + }, + { + detail: &ArgErrDetail{ + Have: []types.Type{ + types.N, + types.S, + }, + Want: types.FuncArgs{ + Args: []types.Type{ + types.S, + types.S, + }, + }, + }, + expected: []string{ + "have: (number, string)", + "want: (string, string)", + }, + }, + } + + for _, tc := range tests { + if !slices.Equal(tc.detail.Lines(), tc.expected) { + t.Errorf("Expected %v for %v but got: %v", tc.expected, tc.detail, tc.detail.Lines()) + } + } +} + +func TestCheckErrorOrdering(t *testing.T) { + + mod := MustParseModule(` + package test + import rego.v1 + + q = true + + p if { data.test.q = 1 } # type error: bool = number + p if { data.test.q = 2 } # type error: bool = number + `) + + input := make([]util.T, len(mod.Rules)) + inputReversed := make([]util.T, len(mod.Rules)) + + for i := range input { + input[i] = mod.Rules[i] + inputReversed[i] = mod.Rules[i] + } + + inputReversed[1], inputReversed[2] = inputReversed[2], inputReversed[1] + + _, errs1 := newTypeChecker().CheckTypes(nil, input, nil) + _, errs2 := newTypeChecker().CheckTypes(nil, inputReversed, nil) + + if errs1.Error() != errs2.Error() { + t.Fatalf("Expected error slices to be equal. errs1:\n\n%v\n\nerrs2:\n\n%v\n\n", errs1, errs2) + } +} + +func TestRewrittenVarsInErrors(t *testing.T) { + + _, errs := newTypeChecker().WithVarRewriter(rewriteVarsInRef(map[Var]Var{ + "__local0__": "foo", + "__local1__": "bar", + })).CheckBody(nil, MustParseBody(`__local0__ = [[1]]; __local1__ = "bar"; __local0__[0][__local1__]`)) + + if len(errs) != 1 { + t.Fatal("expected exactly one error but got:", len(errs)) + } + + detail, ok := errs[0].Details.(*RefErrInvalidDetail) + if !ok { + t.Fatal("expected invalid ref detail but got:", errs[0].Details) + } + + if !detail.Ref.Equal(MustParseRef("foo[0][bar]")) { + t.Fatal("expected ref to be foo[0][bar] but got:", detail.Ref) + } + +} + +func newTestEnv(rs []string) *TypeEnv { + module := MustParseModule(` + package test + `) + + // We preallocate enough for at least the base rules. + // Else cases will cause reallocs, but that's okay. + elems := make([]util.T, 0, len(rs)) + + for i := range rs { + rule := MustParseRule(rs[i]) + rule.Module = module + elems = append(elems, rule) + for next := rule.Else; next != nil; next = next.Else { + next.Module = module + elems = append(elems, next) + } + } + + env, err := newTypeChecker().CheckTypes(newTypeChecker().Env(BuiltinMap), elems, nil) + if len(err) > 0 { + panic(err) + } + + return env +} + +const inputSchema = `{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/example.json", + "type": "object", + "title": "The root schema", + "description": "The root schema comprises the entire JSON document.", + "default": {}, + "examples": [ + { + "user": "alice", + "operation": "write" + } + ], + "required": [ + "user", + "operation" + ], + "properties": { + "user": { + "$id": "#/properties/user", + "type": "string", + "title": "The user schema", + "description": "An explanation about the purpose of this instance.", + "default": "", + "examples": [ + "alice" + ] + }, + "operation": { + "$id": "#/properties/operation", + "type": "string", + "title": "The operation schema", + "description": "An explanation about the purpose of this instance.", + "default": "", + "examples": [ + "write" + ] + } + }, + "additionalProperties": true +}` + +const inputSchema2 = `{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/example.json", + "type": "object", + "title": "The root schema", + "description": "The root schema comprises the entire JSON document.", + "default": {}, + "examples": [ + { + "operation": "read" + } + ], + "required": [ + "operation" + ], + "properties": { + "operation": { + "$id": "#/properties/operation", + "type": "string", + "title": "The operation schema", + "description": "An explanation about the purpose of this instance.", + "default": "", + "examples": [ + "read" + ] + } + }, + "additionalProperties": true +}` + +const dataSchema = `{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/example.json", + "type": "object", + "title": "The root schema", + "description": "The root schema comprises the entire JSON document.", + "default": {}, + "examples": [ + { + "alice": [ + "read", + "write" + ], + "bob": [ + "read" + ] + } + ], + "required": [ + "alice", + "bob" + ], + "properties": { + "alice": { + "$id": "#/properties/alice", + "type": "array", + "title": "The alice schema", + "description": "An explanation about the purpose of this instance.", + "default": [], + "examples": [ + [ + "read", + "write" + ] + ], + "additionalItems": false, + "items": { + "$id": "#/properties/alice/items", + "type": "string", + "title": "The items schema", + "description": "An explanation about the purpose of this instance.", + "default": "", + "examples": [ + [ + "read", + "write" + ] + ] + } + }, + "bob": { + "$id": "#/properties/bob", + "type": "array", + "title": "The bob schema", + "description": "An explanation about the purpose of this instance.", + "default": [], + "examples": [ + [ + "read" + ] + ], + "additionalItems": false, + "items": { + "$id": "#/properties/bob/items", + "type": "string", + "title": "The items schema", + "description": "An explanation about the purpose of this instance.", + "default": "", + "examples": [ + [ + "read" + ] + ] + } + } + }, + "additionalProperties": true +}` + +func TestCheckAnnotationRules(t *testing.T) { + + ischema := util.MustUnmarshalJSON([]byte(inputSchema)) + ischema2 := util.MustUnmarshalJSON([]byte(inputSchema2)) + dschema := util.MustUnmarshalJSON([]byte(dataSchema)) + numberSchema := util.MustUnmarshalJSON([]byte(`{"type": "number"}`)) + stringSchema := util.MustUnmarshalJSON([]byte(`{"type": "string"}`)) + + module1 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - data.acl: schema["acl-schema"] +allow { + access = data.acl[input.user] + access[_] == input.operation +}` + + module2 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - input: schema["whocan-input-schema"] +# - data.acl: schema["acl-schema"] +whocan[user] { + access = acl[user] + access[_] == input.operation +}` + + module3 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - input: schema["whocan-input-schema"] +# - data.acl: schema["acl-schema"] +allow { + access = data.acl[input.user] + access[_] == input.operation +}` + + module4 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema.missing +whocan[user] { + access = acl[user] + access[_] == input.operation +}` + + module8 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - data.acl: schema["acl-schema"] +# - input.apple.orange: schema["input"] +allow { + access = data.acl[input.user] + access[_] == input.operation + input.apple.banana +}` + + module9 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - data.acl: schema["acl-schema"] +# - input.apple.orange: schema["input"] +# - input.apple.orange.banana: schema["acl-schema"] +allow { + access = data.acl[input.user] + access[_] == input.operation + input.apple.orange.banana +}` + + module10 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - input.apple.orange: schema["input"] +# - input.apple.orange.banana: schema["acl-schema"] +allow { + access = data.acl[input.user] + access[_] == input.operation + input.apple.orange.banana.fruit +}` + + module11 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - input.apple.orange: schema["input"] +# - input.apple.orange: schema["acl-schema"] +allow { + access = data.acl[input.user] + access[_] == input.operation + input.apple.orange.bob + input.apple.orange.user +}` + + module12 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - input: schema["acl-schema"] +allow { + access = data.acl[input.user] +}` + + module13 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - input.apple["orange"]: schema["input"] +allow { + access = data.acl[input.user] + input.apple.orange.fruit +}` + + module14 := ` +package policy + +import data.acl +import input + +# METADATA +# scope: rule +# schemas: +# - input.request.object: schema["acl-schema"] +deny[msg] { + input.request.kind.kind == "Pod" + image := input.request.object.spec.typo.containers[_].image + not startswith(image, "hooli.com/") +}` + + module15 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - data.acl: schema["acl-schema"] +allow { + access = data.acl[input.user] + access[_] == input.operation + input.apple.orange.banana +}` + + module16 := ` +package policy + +import data.acl +import input + +# METADATA +# scope: rule +# schemas: +# - data.acl: schema["acl-schema"] +deny[msg] { + input.request.kind.kinds == "Pod" + image := input.request.object.spec.containers[_].image + not startswith(image, "hooli.com/") + data.blah +}` + + module17 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["acl-schema"] +allow { + input.alice +} + +deny[msg] { + input.foo +}` + + module18 := ` +package policy + +import data.acl +import input + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - input.apple.banana: schema["input"] +deny[msg] { + input.apple.banana +} + +deny1[msg] { + input.apple.banana.foo +}` + + module19 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - data.acl: schema["acl-schema"] +# - data.acl.foo: schema["input"] +allow { + access = data.acl[input.user] + access[_] == input.operation + input.apple.orange.banana + data.acl.foo.blah +}` + + module20 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - data.acl: schema["acl-schema"] +allow { + access = data.acl[input.user] + access[_] == input.operation + data.acl.foo +}` + + module21 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - data.acl: schema["acl-schema"] +allow { + access = data.acl[input.user] + access[_] == input.operation +} + +# METADATA for whocan rule +# scope: rule +# schemas: +# - input: schema["whocan-input-schema"] +# - data.acl: schema["acl-schema"] +whocan[user] { + access = acl[user] + access[_] == input.operation +}` + + module22 := ` +package policy + +import data.acl +import input + +default allow = false + +# METADATA +# scope: rule +# schemas: +# - input: schema["input"] +# - data.acl: schema["acl-schema"] +allow { + access = data.acl[input.user] + access[_] == input.operation + data.foo + data.acl.foo +} + +# METADATA for whocan rule +# scope: rule +# schemas: +# - input: schema["whocan-input-schema"] +# - data.acl: schema["acl-schema"] +whocan[user] { + access = acl[user] + access[_] == input.operation.foo + +}` + + schemaSet := NewSchemaSet() + schemaSet.Put(MustParseRef("schema.number"), numberSchema) + schemaSet.Put(MustParseRef("schema.string"), stringSchema) + schemaSet.Put(MustParseRef("schema.input"), ischema) + schemaSet.Put(MustParseRef(`schema["whocan-input-schema"]`), ischema2) + schemaSet.Put(MustParseRef(`schema["acl-schema"]`), dschema) + + tests := []struct { + note string + modules []string + err string + }{ + {note: "data and input annotations", modules: []string{module1}}, + {note: "correct data override", modules: []string{module2}}, + {note: "incorrect data override", modules: []string{module3}, err: "undefined ref: input.user"}, + {note: "missing schema", modules: []string{module4}, err: "undefined schema: schema.missing"}, + {note: "overriding ref with length greater than one and not existing", modules: []string{module8}, err: "undefined ref: input.apple.banana"}, + {note: "overriding ref with length greater than one and existing prefix", modules: []string{module9}}, + {note: "overriding ref with length greater than one and existing prefix with type error", modules: []string{module10}, err: "undefined ref: input.apple.orange.banana.fruit"}, + {note: "overriding ref with length greater than one and existing ref", modules: []string{module11}, err: "undefined ref: input.apple.orange.user"}, + {note: "overriding ref of size one", modules: []string{module12}, err: "undefined ref: input.user"}, + {note: "overriding annotation written with brackets", modules: []string{module13}, err: "undefined ref: input.apple.orange.fruit"}, + {note: "overriding strict", modules: []string{module14}, err: "undefined ref: input.request.object.spec.typo"}, + {note: "data annotation but no input schema", modules: []string{module15}}, + {note: "data schema annotation does not overly restrict data expression", modules: []string{module16}}, + {note: "correct defer annotation on another rule has no effect base case", modules: []string{module17}}, + {note: "correct defer annotation on another rule has no effect", modules: []string{module18}}, + {note: "overriding ref with data prefix", modules: []string{module19}, err: "data.acl.foo.blah"}, + {note: "data annotation type error", modules: []string{module20}, err: "data.acl.foo"}, + {note: "more than one rule with metadata", modules: []string{module21}}, + {note: "more than one rule with metadata with type error", modules: []string{module22}, err: "undefined ref"}, + {note: "document scope", err: "test1.rego:8: rego_type_error: match error", modules: []string{`package test +# METADATA +# scope: document +# schemas: +# - input.foo: schema.number +p { input.foo = 7 } + +p { input.foo = [] }`}}, + + {note: "rule scope overrides document scope", modules: []string{`package test + +# METADATA +# scope: document +# schemas: +# - input.foo: schema.number +p { input.foo = 7 } + +# METADATA +# scope: rule +# schemas: +# - input.foo: schema.string +p { input.foo = "str" }`}}, + + {note: "rule scope merges with document scope", err: "test1.rego:15: rego_type_error: match error", modules: []string{`package test + +# METADATA +# scope: document +# schemas: +# - input.bar: schema.number +p { input.bar = 7 } + +# METADATA +# scope: rule +# schemas: +# - input.foo: schema.string +p { + input.foo = "str" + input.bar = "str" +}`}}, + + {note: "document scope conflict", err: "test1.rego:9: rego_type_error: document annotation redeclared: test1.rego:3", modules: []string{`package test + +# METADATA +# scope: document +# schemas: +# - input.foo: schema.number +p { input.foo = 7 } + +# METADATA +# scope: document +# schemas: +# - input.foo: schema.string +p { input.foo = "str" }`}}, + + {note: "package scope in other module", modules: []string{`# METADATA +# scope: package +# schemas: +# - input.foo: schema.number +package test`, `package test + +p { input.foo = 7 }`}}, + + {note: "package scope in other module type conflict", err: "test2.rego:3: rego_type_error: match error", modules: []string{`# METADATA +# scope: package +# schemas: +# - input.foo: schema.string +package test`, `package test + +p { input.foo = 7 }`}}, + + {note: "package scope conflict", err: "test2.rego:1: rego_type_error: package annotation redeclared: test1.rego:1", modules: []string{`# METADATA +# scope: package +# schemas: +# - input.foo: schema.string +package test`, `# METADATA +# scope: package +# schemas: +# - input.foo: schema.number +package test + +p { input.foo = 7 }`}}, + + {note: "subpackages scope", err: "test1.rego:7: rego_type_error: match error", modules: []string{`# METADATA +# scope: subpackages +# schemas: +# - input: schema.number +package test + +p { input = "str" }`}}, + + {note: "document scope overrides subpackages scope", modules: []string{`# METADATA +# scope: subpackages +# schemas: +# - input: schema.number +package test + +# METADATA +# scope: document +# schemas: +# - input: schema.string +p { input = "str" }`}}, + + {note: "document scope overrides subpackages scope and finds error", err: "test1.rego:11: rego_type_error: match error", modules: []string{`# METADATA +# scope: subpackages +# schemas: +# - input: schema.string +package test + +# METADATA +# scope: rule +# schemas: +# - input: schema.number +p { input = "str" }`}}, + + {note: "package scope", err: "test1.rego:7: rego_type_error: match error", modules: []string{`# METADATA +# scope: package +# schemas: +# - input: schema.string +package test + +p { input = 7 }`}}, + + {note: "rule scope overrides package scope", modules: []string{`# METADATA +# scope: package +# schemas: +# - input: schema.string +package test + +# METADATA +# scope: rule +# schemas: +# - input: schema.number +p { input = 7 }`}}, + + {note: "inline definition", err: "test1.rego:7: rego_type_error: match error", modules: []string{`package test + +# METADATA +# scope: rule +# schemas: +# - input: {"type": "string"} +p { input = 7 }`}}, + {note: "document scope is unordered", err: "test1.rego:3: rego_type_error: match error", modules: []string{`package test + +p { input = 7 } + +# METADATA +# scope: document +# schemas: +# - input: schema.string +p { input = "foo" }`}}, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var modules []*Module + var elems []util.T + + for i, module := range tc.modules { + mod, err := ParseModuleWithOpts(fmt.Sprintf("test%d.rego", i+1), module, ParserOptions{ + ProcessAnnotation: true, + RegoVersion: RegoV0, + }) + if err != nil { + t.Fatal(err) + } + modules = append(modules, mod) + + for _, rule := range mod.Rules { + elems = append(elems, rule) + for next := rule.Else; next != nil; next = next.Else { + elems = append(elems, next) + } + } + } + + oldTypeEnv := newTypeChecker().WithSchemaSet(schemaSet).Env(BuiltinMap) + as, errors := BuildAnnotationSet(modules) + typeenv, checkErrors := newTypeChecker().WithSchemaSet(schemaSet).CheckTypes(oldTypeEnv, elems, as) + errors = append(errors, checkErrors...) + if len(errors) > 0 { + for _, e := range errors { + if tc.err == "" || !strings.Contains(e.Error(), tc.err) { + t.Fatalf("Unexpected check rule error when processing annotations: %v", e) + } + } + return + } else if tc.err != "" { + t.Fatalf("Expected error %q but got success", tc.err) + } + + if oldTypeEnv.tree.children != nil && typeenv.next.tree.children != nil && (typeenv.next.tree.children.Len() != oldTypeEnv.tree.children.Len()) { + t.Fatalf("Unexpected type env") + } + + }) + } +} + +func TestCheckAnnotationInference(t *testing.T) { + + tests := []struct { + note string + modules map[string]string + schemas map[string]string + exp map[string]types.Type + }{ + { + note: "rule scope", + modules: map[string]string{ + "test.rego": ` +package test +import rego.v1 + +# METADATA +# scope: rule +# schemas: +# - input: schema.foo +p = x if { input = x } + +q = p`, + }, + schemas: map[string]string{ + "schema.foo": `{"type": "number"}`, + }, + exp: map[string]types.Type{ + "data.test.p": types.N, + "data.test.q": types.N, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + + modules := map[string]*Module{} + for k, v := range tc.modules { + var err error + modules[k], err = ParseModuleWithOpts(k, v, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + + ss := NewSchemaSet() + for k, v := range tc.schemas { + + ref := MustParseRef(k) + var schema any + err = util.Unmarshal([]byte(v), &schema) + if err != nil { + t.Fatal(err) + } + + ss.Put(ref, schema) + } + + compiler := NewCompiler(). + WithSchemas(ss). + WithUseTypeCheckAnnotations(true) + compiler.Compile(modules) + if compiler.Failed() { + t.Fatal("unexpected error:", compiler.Errors) + } + + for k, v := range tc.exp { + ref := MustParseRef(k) + result := compiler.TypeEnv.Get(ref) + if types.Compare(result, v) != 0 { + t.Errorf("expected %v => %v but got %v", ref, v, result) + } + } + } + + }) + } + +} + +// TestSchemaCache is a regression test for https://github.com/open-policy-agent/opa/issues/7679 +func TestInlinedSchemaAnnotationIgnoredByCache(t *testing.T) { + policy := ` +package test + +# METADATA +# schemas: +# - input.x: {"type": "boolean"} +p if { + input.x == 42 +} + +# METADATA +# schemas: +# - input.x: {"type": "object"} +q if { + input.x == "foo" +}` + + m, err := ParseModuleWithOpts("policy.rego", policy, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatalf("failed to parse module: %v", err) + } + + ms := map[string]*Module{"policy.rego": m} + compiler := NewCompiler().WithUseTypeCheckAnnotations(true) + compiler.Compile(ms) + + if !compiler.Failed() { + t.Fatal("compiler failures expected, got none") + } + + expErrs := []string{ + `policy.rego:8: rego_type_error: match error + left : boolean + right : number`, + `policy.rego:15: rego_type_error: match error + left : object[any: any] + right : string`, + } + + for _, expErr := range expErrs { + found := false + for _, err := range compiler.Errors { + if strings.Contains(err.Error(), expErr) { + found = true + continue + } + } + if !found { + t.Errorf("expected error:\n\n%s\n\ngot:\n\n%s", expErr, compiler.Errors.Error()) + } + } +} + +func TestRemoteSchema(t *testing.T) { + schema := `{"type": "boolean"}` + + schemaCalled := false + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + schemaCalled = true + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(schema)) + })) + defer server.Close() + + policy := fmt.Sprintf(` +package test +import rego.v1 + +# METADATA +# schemas: +# - input: {$ref: "%s"} +p if { + input == 42 +}`, server.URL) + + module, err := ParseModuleWithOpts("policy.rego", policy, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + modules := map[string]*Module{"policy.rego": module} + + compiler := NewCompiler(). + WithUseTypeCheckAnnotations(true) + compiler.Compile(modules) + + if !compiler.Failed() { + t.Fatal("expected error, got none") + } + + expectedTypeError := "rego_type_error: match error" + if !strings.Contains(compiler.Errors.Error(), expectedTypeError) { + t.Fatalf("expected error:\n\n%s\n\ngot:\n\n%s", + expectedTypeError, compiler.Errors.Error()) + } + + if !schemaCalled { + t.Fatal("expected schema server to be called, was not") + } +} + +func TestRemoteSchemaHostNotAllowed(t *testing.T) { + capabilities := CapabilitiesForThisVersion() + capabilities.AllowNet = []string{} + schema := `{"type": "boolean"}` + + schemaCalled := false + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + schemaCalled = true + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(schema)) + })) + defer server.Close() + + policy := fmt.Sprintf(` +package test +import rego.v1 + +# METADATA +# schemas: +# - input: {$ref: "%s"} +p if { + input == 42 +}`, server.URL) + + module, err := ParseModuleWithOpts("policy.rego", policy, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + modules := map[string]*Module{"policy.rego": module} + + compiler := NewCompiler(). + WithUseTypeCheckAnnotations(true). + WithCapabilities(capabilities) + compiler.Compile(modules) + + if !compiler.Failed() { + t.Fatal("expected error, got none") + } + + expectedTypeError := "rego_type_error: unable to compile the schema: remote reference loading disabled" + if !strings.Contains(compiler.Errors.Error(), expectedTypeError) { + t.Fatalf("expected error:\n\n%s\n\ngot:\n\n%s", + expectedTypeError, compiler.Errors.Error()) + } + + if schemaCalled { + t.Fatal("expected schema server to not be called, was") + } +} diff --git a/third_party/opa/v1/ast/compare.go b/third_party/opa/v1/ast/compare.go new file mode 100644 index 000000000000..c4754341de47 --- /dev/null +++ b/third_party/opa/v1/ast/compare.go @@ -0,0 +1,429 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "encoding/json" + "fmt" + "math/big" +) + +// Compare returns an integer indicating whether two AST values are less than, +// equal to, or greater than each other. +// +// If a is less than b, the return value is negative. If a is greater than b, +// the return value is positive. If a is equal to b, the return value is zero. +// +// Different types are never equal to each other. For comparison purposes, types +// are sorted as follows: +// +// nil < Null < Boolean < Number < String < Var < Ref < Array < Object < Set < +// ArrayComprehension < ObjectComprehension < SetComprehension < Expr < SomeDecl +// < With < Body < Rule < Import < Package < Module. +// +// Arrays and Refs are equal if and only if both a and b have the same length +// and all corresponding elements are equal. If one element is not equal, the +// return value is the same as for the first differing element. If all elements +// are equal but a and b have different lengths, the shorter is considered less +// than the other. +// +// Objects are considered equal if and only if both a and b have the same sorted +// (key, value) pairs and are of the same length. Other comparisons are +// consistent but not defined. +// +// Sets are considered equal if and only if the symmetric difference of a and b +// is empty. +// Other comparisons are consistent but not defined. +func Compare(a, b any) int { + + if t, ok := a.(*Term); ok { + if t == nil { + a = nil + } else { + a = t.Value + } + } + + if t, ok := b.(*Term); ok { + if t == nil { + b = nil + } else { + b = t.Value + } + } + + if a == nil { + if b == nil { + return 0 + } + return -1 + } + if b == nil { + return 1 + } + + sortA := sortOrder(a) + sortB := sortOrder(b) + + if sortA < sortB { + return -1 + } else if sortB < sortA { + return 1 + } + + switch a := a.(type) { + case Null: + return 0 + case Boolean: + b := b.(Boolean) + if a.Equal(b) { + return 0 + } + if !a { + return -1 + } + return 1 + case Number: + if ai, err := json.Number(a).Int64(); err == nil { + if bi, err := json.Number(b.(Number)).Int64(); err == nil { + if ai == bi { + return 0 + } + if ai < bi { + return -1 + } + return 1 + } + } + + // We use big.Rat for comparing big numbers. + // It replaces big.Float due to following reason: + // big.Float comes with a default precision of 64, and setting a + // larger precision results in more memory being allocated + // (regardless of the actual number we are parsing with SetString). + // + // Note: If we're so close to zero that big.Float says we are zero, do + // *not* big.Rat).SetString on the original string it'll potentially + // take very long. + var bigA, bigB *big.Rat + fa, ok := new(big.Float).SetString(string(a)) + if !ok { + panic("illegal value") + } + if fa.IsInt() { + if i, _ := fa.Int64(); i == 0 { + bigA = new(big.Rat).SetInt64(0) + } + } + if bigA == nil { + bigA, ok = new(big.Rat).SetString(string(a)) + if !ok { + panic("illegal value") + } + } + + fb, ok := new(big.Float).SetString(string(b.(Number))) + if !ok { + panic("illegal value") + } + if fb.IsInt() { + if i, _ := fb.Int64(); i == 0 { + bigB = new(big.Rat).SetInt64(0) + } + } + if bigB == nil { + bigB, ok = new(big.Rat).SetString(string(b.(Number))) + if !ok { + panic("illegal value") + } + } + + return bigA.Cmp(bigB) + case String: + b := b.(String) + if a.Equal(b) { + return 0 + } + if a < b { + return -1 + } + return 1 + case Var: + return VarCompare(a, b.(Var)) + case Ref: + b := b.(Ref) + return termSliceCompare(a, b) + case *Array: + b := b.(*Array) + return termSliceCompare(a.elems, b.elems) + case *lazyObj: + return Compare(a.force(), b) + case *object: + if x, ok := b.(*lazyObj); ok { + b = x.force() + } + b := b.(*object) + return a.Compare(b) + case Set: + b := b.(Set) + return a.Compare(b) + case *ArrayComprehension: + b := b.(*ArrayComprehension) + if cmp := Compare(a.Term, b.Term); cmp != 0 { + return cmp + } + return a.Body.Compare(b.Body) + case *ObjectComprehension: + b := b.(*ObjectComprehension) + if cmp := Compare(a.Key, b.Key); cmp != 0 { + return cmp + } + if cmp := Compare(a.Value, b.Value); cmp != 0 { + return cmp + } + return a.Body.Compare(b.Body) + case *SetComprehension: + b := b.(*SetComprehension) + if cmp := Compare(a.Term, b.Term); cmp != 0 { + return cmp + } + return a.Body.Compare(b.Body) + case Call: + b := b.(Call) + return termSliceCompare(a, b) + case *Expr: + b := b.(*Expr) + return a.Compare(b) + case *SomeDecl: + b := b.(*SomeDecl) + return a.Compare(b) + case *Every: + b := b.(*Every) + return a.Compare(b) + case *With: + b := b.(*With) + return a.Compare(b) + case Body: + b := b.(Body) + return a.Compare(b) + case *Head: + b := b.(*Head) + return a.Compare(b) + case *Rule: + b := b.(*Rule) + return a.Compare(b) + case Args: + b := b.(Args) + return termSliceCompare(a, b) + case *Import: + b := b.(*Import) + return a.Compare(b) + case *Package: + b := b.(*Package) + return a.Compare(b) + case *Annotations: + b := b.(*Annotations) + return a.Compare(b) + case *Module: + b := b.(*Module) + return a.Compare(b) + } + panic(fmt.Sprintf("illegal value: %T", a)) +} + +type termSlice []*Term + +func (s termSlice) Less(i, j int) bool { return Compare(s[i].Value, s[j].Value) < 0 } +func (s termSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s termSlice) Len() int { return len(s) } + +func sortOrder(x any) int { + switch x.(type) { + case Null: + return 0 + case Boolean: + return 1 + case Number: + return 2 + case String: + return 3 + case Var: + return 4 + case Ref: + return 5 + case *Array: + return 6 + case Object: + return 7 + case Set: + return 8 + case *ArrayComprehension: + return 9 + case *ObjectComprehension: + return 10 + case *SetComprehension: + return 11 + case Call: + return 12 + case Args: + return 13 + case *Expr: + return 100 + case *SomeDecl: + return 101 + case *Every: + return 102 + case *With: + return 110 + case *Head: + return 120 + case Body: + return 200 + case *Rule: + return 1000 + case *Import: + return 1001 + case *Package: + return 1002 + case *Annotations: + return 1003 + case *Module: + return 10000 + } + panic(fmt.Sprintf("illegal value: %T", x)) +} + +func importsCompare(a, b []*Import) int { + minLen := min(len(b), len(a)) + for i := range minLen { + if cmp := a[i].Compare(b[i]); cmp != 0 { + return cmp + } + } + if len(a) < len(b) { + return -1 + } + if len(b) < len(a) { + return 1 + } + return 0 +} + +func annotationsCompare(a, b []*Annotations) int { + minLen := min(len(b), len(a)) + for i := range minLen { + if cmp := a[i].Compare(b[i]); cmp != 0 { + return cmp + } + } + if len(a) < len(b) { + return -1 + } + if len(b) < len(a) { + return 1 + } + return 0 +} + +func rulesCompare(a, b []*Rule) int { + minLen := min(len(b), len(a)) + for i := range minLen { + if cmp := a[i].Compare(b[i]); cmp != 0 { + return cmp + } + } + if len(a) < len(b) { + return -1 + } + if len(b) < len(a) { + return 1 + } + return 0 +} + +func termSliceCompare(a, b []*Term) int { + minLen := min(len(b), len(a)) + for i := range minLen { + if cmp := Compare(a[i], b[i]); cmp != 0 { + return cmp + } + } + if len(a) < len(b) { + return -1 + } else if len(b) < len(a) { + return 1 + } + return 0 +} + +func withSliceCompare(a, b []*With) int { + minLen := min(len(b), len(a)) + for i := range minLen { + if cmp := Compare(a[i], b[i]); cmp != 0 { + return cmp + } + } + if len(a) < len(b) { + return -1 + } else if len(b) < len(a) { + return 1 + } + return 0 +} + +func VarCompare(a, b Var) int { + if a == b { + return 0 + } + if a < b { + return -1 + } + return 1 +} + +func TermValueCompare(a, b *Term) int { + return a.Value.Compare(b.Value) +} + +func TermValueEqual(a, b *Term) bool { + return ValueEqual(a.Value, b.Value) +} + +func ValueEqual(a, b Value) bool { + // TODO(ae): why doesn't this work the same? + // + // case interface{ Equal(Value) bool }: + // return v.Equal(b) + // + // When put on top, golangci-lint even flags the other cases as unreachable.. + // but TestTopdownVirtualCache will have failing test cases when we replace + // the other cases with the above one.. 🤔 + switch v := a.(type) { + case Null: + return v.Equal(b) + case Boolean: + return v.Equal(b) + case Number: + return v.Equal(b) + case String: + return v.Equal(b) + case Var: + return v.Equal(b) + case Ref: + return v.Equal(b) + case *Array: + return v.Equal(b) + } + + return a.Compare(b) == 0 +} + +func RefCompare(a, b Ref) int { + return termSliceCompare(a, b) +} + +func RefEqual(a, b Ref) bool { + return termSliceEqual(a, b) +} diff --git a/third_party/opa/v1/ast/compare_test.go b/third_party/opa/v1/ast/compare_test.go new file mode 100644 index 000000000000..ee5812be011a --- /dev/null +++ b/third_party/opa/v1/ast/compare_test.go @@ -0,0 +1,787 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "testing" +) + +func TestCompare(t *testing.T) { + + // Many of the comparison cases are covered by existing equality tests. Here + // we cover edge cases. + tests := []struct { + a string + b string + expected int + }{ + // Comparisons to Go nil. Everything is greater than nil and nil is equal to nil + {"null", "", 1}, + {"", "null", -1}, + {"", "", 0}, + + // Booleans + {"false", "true", -1}, + {"true", "false", 1}, + + // Numbers + {"0", "1", -1}, + {"1", "0", 1}, + {"0", "0", 0}, + {"0", "1.5", -1}, + {"1.5", "0", 1}, + {"123456789123456789123", "123456789123456789123", 0}, + {"123456789123456789123", "123456789123456789122", 1}, + {"123456789123456789122", "123456789123456789123", -1}, + {"123456789123456789123.5", "123456789123456789123.5", 0}, + {"123456789123456789123.5", "123456789123456789122.5", 1}, + {"123456789123456789122.5", "123456789123456789123.5", -1}, + {"630E-840354372", "0", 0}, + + // Object comparisons are consistent + {`{1: 2, 3: 4}`, `{4: 3, 1: 2}`, -1}, + {`{1: 2, 3: 4}`, `{1: 2, 4: 3}`, -1}, + {`{1: 2, 3: 4}`, `{1: 2, 3: 5}`, -1}, + {`{1: 2, 3: 4}`, `{1: 2, 3: 4, 5: 6}`, -1}, + {`{1: 2, 3: 4, 5: 6}`, `{1: 2, 3: 4}`, 1}, + + // Comprehensions + {`[null | true]`, `[false | null]`, -1}, + {`{null | true}`, `{false | null}`, -1}, + {`{"abc": null | true}`, `{"cba": false | null}`, -1}, + + // Expressions + {`a = b`, `b = a`, -1}, + {`b = a`, `not a = b`, -1}, + {`a = b`, `x`, 1}, + {`a = b`, `a = b with input.foo as bar`, -1}, + {`a = b with input.foo as bar`, `a = b`, 1}, + {`a = b with input.foo as bar`, `a = b with input.foo.bar.baz as qux`, -1}, + {`a = b with input.foo as bar`, `a = b with input.foo as bar with input.baz as qux`, -1}, + + // Body + {`a = b`, `a = b; b = a`, -1}, + {`a = b; b = a`, `a = b`, 1}, + } + for _, tc := range tests { + var a, b any + if len(tc.a) > 0 { + a = MustParseStatement(tc.a) + } + if len(tc.b) > 0 { + b = MustParseStatement(tc.b) + } + result := Compare(a, b) + if tc.expected != result { + t.Errorf("Expected %v.Compare(%v) == %v but got %v", a, b, tc.expected, result) + } + } +} + +func TestCompareModule(t *testing.T) { + a := MustParseModule(`package a.b.c`) + b := MustParseModule(`package a.b.d`) + result := Compare(a, b) + + if result != -1 { + t.Errorf("Expected %v to be less than %v but got: %v", a, b, result) + } + + a = MustParseModule(`package a.b.c + +import input.x.y`) + b = MustParseModule(`package a.b.c + +import input.x.z`) + result = Compare(a, b) + + if result != -1 { + t.Errorf("Expected %v to be less than %v but got: %v", a, b, result) + } + + var err error + + a, err = ParseModuleWithOpts("test.rego", `package a + +# METADATA +# scope: rule +# schemas: +# - input: schema.a +p := 7`, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + + b, err = ParseModuleWithOpts("test.rego", `package a + +# METADATA +# scope: rule +# schemas: +# - input: schema.b +p := 7`, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + + result = Compare(a, b) + + if result != -1 { + t.Errorf("Expected %v to be less than %v but got: %v", a, b, result) + } +} + +func TestCompareAnnotations(t *testing.T) { + + tests := []struct { + note string + a string + b string + exp int + }{ + { + note: "same", + a: ` +# METADATA +# scope: a`, + b: ` +# METADATA +# scope: a`, + exp: 0, + }, + { + note: "unknown scope", + a: ` +# METADATA +# scope: rule`, + b: ` +# METADATA +# scope: a`, + exp: 1, + }, + { + note: "unknown scope - less than", + a: ` +# METADATA +# scope: a`, + b: ` +# METADATA +# scope: rule`, + exp: -1, + }, + { + note: "unknown scope - greater than - lexigraphical", + a: ` +# METADATA +# scope: b`, + b: ` +# METADATA +# scope: a`, + exp: 1, + }, + { + note: "unknown scope - less than - lexigraphical", + a: ` +# METADATA +# scope: b`, + b: ` +# METADATA +# scope: c`, + exp: -1, + }, + { + note: "schema", + a: ` +# METADATA +# scope: rule +# schemas: +# - input: schema`, + b: ` +# METADATA +# scope: rule +# schemas: +# - input: schema`, + exp: 0, + }, + { + note: "schema - less than", + a: ` +# METADATA +# scope: rule +# schemas: +# - input.a: schema`, + b: ` +# METADATA +# scope: rule +# schemas: +# - input.b: schema`, + exp: -1, + }, + { + note: "schema - greater than", + a: ` +# METADATA +# scope: rule +# schemas: +# - input.b: schema`, + b: ` +# METADATA +# scope: rule +# schemas: +# - input.a: schema`, + exp: 1, + }, + { + note: "schema - less than (fewer)", + a: ` +# METADATA +# scope: rule +# schemas: +# - input.a: schema`, + b: ` +# METADATA +# scope: rule +# schemas: +# - input.a: schema +# - input.b: schema`, + exp: -1, + }, + { + note: "schema - greater than (more)", + a: ` +# METADATA +# scope: rule +# schemas: +# - input.a: schema +# - input.b: schema`, + b: ` +# METADATA +# scope: rule +# schemas: +# - input.a: schema`, + exp: 1, + }, + { + note: "schema - less than - lexigraphical", + a: ` +# METADATA +# scope: rule +# schemas: +# - input: schema.a`, + b: ` +# METADATA +# scope: rule +# schemas: +# - input: schema.b`, + exp: -1, + }, + { + note: "schema - greater than - lexigraphical", + a: ` +# METADATA +# scope: rule +# schemas: +# - input: schema.c`, + b: ` +# METADATA +# scope: rule +# schemas: +# - input: schema.b`, + exp: 1, + }, + { + note: "definition", + a: ` +# METADATA +# schemas: +# - input: {"type": "string"}`, + b: ` +# METADATA +# schemas: +# - input: {"type": "string"}`, + }, + { + note: "definition - less than schema", + a: ` +# METADATA +# schemas: +# - input: {"type": "string"}`, + b: ` +# METADATA +# schemas: +# - input: schema.a`, + exp: -1, + }, + { + note: "schema - greater than definition", + a: ` +# METADATA +# schemas: +# - input: schema.a`, + b: ` +# METADATA +# schemas: +# - input: {"type": "string"}`, + exp: 1, + }, + { + note: "title", + a: ` +# METADATA +# title: a`, + b: ` +# METADATA +# title: a`, + exp: 0, + }, + { + note: "title - less than", + a: ` +# METADATA +# title: a`, + b: ` +# METADATA +# title: b`, + exp: -1, + }, + { + note: "title - greater than", + a: ` +# METADATA +# title: b`, + b: ` +# METADATA +# title: a`, + exp: 1, + }, + { + note: "description", + a: ` +# METADATA +# description: a`, + b: ` +# METADATA +# description: a`, + exp: 0, + }, + { + note: "description - less than", + a: ` +# METADATA +# description: a`, + b: ` +# METADATA +# description: b`, + exp: -1, + }, + { + note: "description - greater than", + a: ` +# METADATA +# description: b`, + b: ` +# METADATA +# description: a`, + exp: 1, + }, + { + note: "authors", + a: ` +# METADATA +# authors: +# - John Doe +# - Jane Doe`, + b: ` +# METADATA +# authors: +# - John Doe +# - Jane Doe`, + exp: 0, + }, + { + note: "authors - less than", + a: ` +# METADATA +# authors: +# - Jane Doe +# - John Doe +`, + b: ` +# METADATA +# authors: +# - John Doe +# - Jane Doe`, + exp: -1, + }, + { + note: "authors - greater than", + a: ` +# METADATA +# authors: +# - John Doe +# - Jane Doe`, + b: ` +# METADATA +# authors: +# - Jane Doe +# - John Doe`, + exp: 1, + }, + { + note: "authors - less than (fewer)", + a: ` +# METADATA +# scope: rule +# authors: +# - John Doe`, + b: ` +# METADATA +# scope: rule +# authors: +# - John Doe +# - Jane Doe`, + exp: -1, + }, + { + note: "authors - greater than (more)", + a: ` +# METADATA +# scope: rule +# authors: +# - John Doe +# - Jane Doe`, + b: ` +# METADATA +# scope: rule +# authors: +# - John Doe`, + exp: 1, + }, + { + note: "authors - less than (email)", + a: ` +# METADATA +# authors: +# - John Doe `, + b: ` +# METADATA +# authors: +# - John Doe `, + exp: -1, + }, + { + note: "authors - greater than (email)", + a: ` +# METADATA +# authors: +# - John Doe `, + b: ` +# METADATA +# authors: +# - John Doe `, + exp: 1, + }, + { + note: "organizations", + a: ` +# METADATA +# organizations: +# - a +# - b`, + b: ` +# METADATA +# organizations: +# - a +# - b`, + exp: 0, + }, + { + note: "organizations - less than", + a: ` +# METADATA +# organizations: +# - a +# - b`, + b: ` +# METADATA +# organizations: +# - c +# - d`, + exp: -1, + }, + { + note: "organizations - greater than", + a: ` +# METADATA +# organizations: +# - c +# - d`, + b: ` +# METADATA +# organizations: +# - a +# - b`, + exp: 1, + }, + { + note: "organizations - less than (fewer)", + a: ` +# METADATA +# scope: rule +# organizations: +# - a`, + b: ` +# METADATA +# scope: rule +# organizations: +# - a +# - b`, + exp: -1, + }, + { + note: "organizations - greater than (more)", + a: ` +# METADATA +# scope: rule +# organizations: +# - a +# - b`, + b: ` +# METADATA +# scope: rule +# organizations: +# - a`, + exp: 1, + }, + { + note: "related_resources", + a: ` +# METADATA +# related_resources: +# - https://a.example.com +# - +# ref: https://b.example.com +# description: foo bar`, + b: ` +# METADATA +# related_resources: +# - https://a.example.com +# - +# ref: https://b.example.com +# description: foo bar`, + exp: 0, + }, + { + note: "related_resources - less than", + a: ` +# METADATA +# related_resources: +# - https://a.example.com +# - https://b.example.com`, + b: ` +# METADATA +# related_resources: +# - https://b.example.com +# - https://c.example.com`, + exp: -1, + }, + { + note: "related_resources - greater than", + a: ` +# METADATA +# related_resources: +# - https://b.example.com +# - https://c.example.com`, + b: ` +# METADATA +# related_resources: +# - https://a.example.com +# - https://b.example.com`, + exp: 1, + }, + { + note: "related_resources - less than (fewer)", + a: ` +# METADATA +# scope: rule +# organizations: +# - https://a.example.com`, + b: ` +# METADATA +# scope: rule +# organizations: +# - https://a.example.com +# - https://b.example.com`, + exp: -1, + }, + { + note: "related_resources - greater than (more)", + a: ` +# METADATA +# scope: rule +# organizations: +# - https://a.example.com +# - https://b.example.com`, + b: ` +# METADATA +# scope: rule +# organizations: +# - https://a.example.com`, + exp: 1, + }, + { + note: "related_resources - less than (description)", + a: ` +# METADATA +# related_resources: +# - +# ref: https://example.com +# description: a`, + b: ` +# METADATA +# related_resources: +# - +# ref: https://example.com +# description: b`, + exp: -1, + }, + { + note: "related_resources - greater than (description)", + a: ` +# METADATA +# related_resources: +# - +# ref: https://example.com +# description: b`, + b: ` +# METADATA +# related_resources: +# - +# ref: https://example.com +# description: a`, + exp: 1, + }, + { + note: "custom", + a: ` +# METADATA +# custom: +# a: 1 +# b: true +# c: +# d: +# - 1 +# - 2 +# e: +# i: 1 +# j: 2`, + b: ` +# METADATA +# custom: +# a: 1 +# b: true +# c: +# d: +# - 1 +# - 2 +# e: +# i: 1 +# j: 2`, + exp: 0, + }, + { + note: "custom - less than", + a: ` +# METADATA +# custom: +# a: 1`, + b: ` +# METADATA +# custom: +# b: 1`, + exp: -1, + }, + { + note: "custom - greater than", + a: ` +# METADATA +# custom: +# b: 1`, + b: ` +# METADATA +# custom: +# a: 1`, + exp: 1, + }, + { + note: "custom - less than (value)", + a: ` +# METADATA +# custom: +# a: 1`, + b: ` +# METADATA +# custom: +# a: 2`, + exp: -1, + }, + { + note: "custom - greater than (value)", + a: ` +# METADATA +# custom: +# a: 2`, + b: ` +# METADATA +# custom: +# a: 1`, + exp: 1, + }, + { + note: "custom - less than (fewer)", + a: ` +# METADATA +# custom: +# a: 1`, + b: ` +# METADATA +# custom: +# a: 1 +# b: 2`, + exp: -1, + }, + { + note: "custom - greater than (more)", + a: ` +# METADATA +# custom: +# a: 1 +# b: 2`, + b: ` +# METADATA +# custom: +# a: 1`, + exp: 1, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + stmts, _, err := ParseStatementsWithOpts("test.rego", tc.a, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + a := stmts[0].(*Annotations) + stmts, _, err = ParseStatementsWithOpts("test.rego", tc.b, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + b := stmts[0].(*Annotations) + result := a.Compare(b) + if result != tc.exp { + t.Fatalf("Expected %d but got %v for %v and %v", tc.exp, result, a, b) + } + }) + } +} diff --git a/third_party/opa/v1/ast/compile.go b/third_party/opa/v1/ast/compile.go new file mode 100644 index 000000000000..e2492ecb8030 --- /dev/null +++ b/third_party/opa/v1/ast/compile.go @@ -0,0 +1,6120 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "errors" + "fmt" + "io" + "maps" + "slices" + "sort" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/internal/debug" + "github.com/open-policy-agent/opa/internal/gojsonschema" + "github.com/open-policy-agent/opa/v1/ast/location" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +// CompileErrorLimitDefault is the default number errors a compiler will allow before +// exiting. +const CompileErrorLimitDefault = 10 + +var ( + errLimitReached = NewError(CompileErr, nil, "error limit reached") + + doubleEq = Equal.Ref() +) + +// Compiler contains the state of a compilation process. +type Compiler struct { + + // Errors contains errors that occurred during the compilation process. + // If there are one or more errors, the compilation process is considered + // "failed". + Errors Errors + + // Modules contains the compiled modules. The compiled modules are the + // output of the compilation process. If the compilation process failed, + // there is no guarantee about the state of the modules. + Modules map[string]*Module + + // ModuleTree organizes the modules into a tree where each node is keyed by + // an element in the module's package path. E.g., given modules containing + // the following package directives: "a", "a.b", "a.c", and "a.b", the + // resulting module tree would be: + // + // root + // | + // +--- data (no modules) + // | + // +--- a (1 module) + // | + // +--- b (2 modules) + // | + // +--- c (1 module) + // + ModuleTree *ModuleTreeNode + + // RuleTree organizes rules into a tree where each node is keyed by an + // element in the rule's path. The rule path is the concatenation of the + // containing package and the stringified rule name. E.g., given the + // following module: + // + // package ex + // p[1] { true } + // p[2] { true } + // q = true + // a.b.c = 3 + // + // root + // | + // +--- data (no rules) + // | + // +--- ex (no rules) + // | + // +--- p (2 rules) + // | + // +--- q (1 rule) + // | + // +--- a + // | + // +--- b + // | + // +--- c (1 rule) + // + // Another example with general refs containing vars at arbitrary locations: + // + // package ex + // a.b[x].d { x := "c" } # R1 + // a.b.c[x] { x := "d" } # R2 + // a.b[x][y] { x := "c"; y := "d" } # R3 + // p := true # R4 + // + // root + // | + // +--- data (no rules) + // | + // +--- ex (no rules) + // | + // +--- a + // | | + // | +--- b (R1, R3) + // | | + // | +--- c (R2) + // | + // +--- p (R4) + RuleTree *TreeNode + + // Graph contains dependencies between rules. An edge (u,v) is added to the + // graph if rule 'u' refers to the virtual document defined by 'v'. + Graph *Graph + + // TypeEnv holds type information for values inferred by the compiler. + TypeEnv *TypeEnv + + // RewrittenVars is a mapping of variables that have been rewritten + // with the key being the generated name and value being the original. + RewrittenVars map[Var]Var + + // Capabilities required by the modules that were compiled. + Required *Capabilities + + localvargen *localVarGenerator + moduleLoader ModuleLoader + ruleIndices *util.HasherMap[Ref, RuleIndex] + stages []stage + maxErrs int + sorted []string // list of sorted module names + pathExists func([]string) (bool, error) + pathConflictCheckRoots []string + 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. + defaultRegoVersion RegoVersion +} + +func (c *Compiler) DefaultRegoVersion() RegoVersion { + return c.defaultRegoVersion +} + +// CompilerStage defines the interface for stages in the compiler. +type CompilerStage func(*Compiler) *Error + +// CompilerEvalMode allows toggling certain stages that are only +// needed for certain modes, Concretely, only "topdown" mode will +// have the compiler build comprehension and rule indices. +type CompilerEvalMode int + +const ( + // EvalModeTopdown (default) instructs the compiler to build rule + // and comprehension indices used by topdown evaluation. + EvalModeTopdown CompilerEvalMode = iota + + // EvalModeIR makes the compiler skip the stages for comprehension + // and rule indices. + EvalModeIR +) + +// CompilerStageDefinition defines a compiler stage +type CompilerStageDefinition struct { + Name string + MetricName string + Stage CompilerStage +} + +// RulesOptions defines the options for retrieving rules by Ref from the +// compiler. +type RulesOptions struct { + // IncludeHiddenModules determines if the result contains hidden modules, + // currently only the "system" namespace, i.e. "data.system.*". + IncludeHiddenModules bool +} + +// QueryContext contains contextual information for running an ad-hoc query. +// +// Ad-hoc queries can be run in the context of a package and imports may be +// included to provide concise access to data. +type QueryContext struct { + Package *Package + Imports []*Import +} + +// NewQueryContext returns a new QueryContext object. +func NewQueryContext() *QueryContext { + return &QueryContext{} +} + +// WithPackage sets the pkg on qc. +func (qc *QueryContext) WithPackage(pkg *Package) *QueryContext { + if qc == nil { + qc = NewQueryContext() + } + qc.Package = pkg + return qc +} + +// WithImports sets the imports on qc. +func (qc *QueryContext) WithImports(imports []*Import) *QueryContext { + if qc == nil { + qc = NewQueryContext() + } + qc.Imports = imports + return qc +} + +// Copy returns a deep copy of qc. +func (qc *QueryContext) Copy() *QueryContext { + if qc == nil { + return nil + } + cpy := *qc + if cpy.Package != nil { + cpy.Package = qc.Package.Copy() + } + cpy.Imports = make([]*Import, len(qc.Imports)) + for i := range qc.Imports { + cpy.Imports[i] = qc.Imports[i].Copy() + } + return &cpy +} + +// QueryCompiler defines the interface for compiling ad-hoc queries. +type QueryCompiler interface { + + // Compile should be called to compile ad-hoc queries. The return value is + // the compiled version of the query. + Compile(q Body) (Body, error) + + // TypeEnv returns the type environment built after running type checking + // on the query. + TypeEnv() *TypeEnv + + // WithContext sets the QueryContext on the QueryCompiler. Subsequent calls + // to Compile will take the QueryContext into account. + WithContext(qctx *QueryContext) QueryCompiler + + // WithEnablePrintStatements enables print statements in queries compiled + // with the QueryCompiler. + WithEnablePrintStatements(yes bool) QueryCompiler + + // WithUnsafeBuiltins sets the built-in functions to treat as unsafe and not + // allow inside of queries. By default the query compiler inherits the + // compiler's unsafe built-in functions. This function allows callers to + // override that set. If an empty (non-nil) map is provided, all built-ins + // are allowed. + WithUnsafeBuiltins(unsafe map[string]struct{}) QueryCompiler + + // WithStageAfter registers a stage to run during query compilation after + // the named stage. + WithStageAfter(after string, stage QueryCompilerStageDefinition) QueryCompiler + + // RewrittenVars maps generated vars in the compiled query to vars from the + // parsed query. For example, given the query "input := 1" the rewritten + // query would be "__local0__ = 1". The mapping would then be {__local0__: input}. + RewrittenVars() map[Var]Var + + // ComprehensionIndex returns an index data structure for the given comprehension + // term. If no index is found, returns nil. + ComprehensionIndex(term *Term) *ComprehensionIndex + + // WithStrict enables strict mode for the query compiler. + WithStrict(strict bool) QueryCompiler +} + +// QueryCompilerStage defines the interface for stages in the query compiler. +type QueryCompilerStage func(QueryCompiler, Body) (Body, error) + +// QueryCompilerStageDefinition defines a QueryCompiler stage +type QueryCompilerStageDefinition struct { + Name string + MetricName string + Stage QueryCompilerStage +} + +type stage struct { + name string + metricName string + f func() +} + +// NewCompiler returns a new empty compiler. +func NewCompiler() *Compiler { + + c := &Compiler{ + Modules: map[string]*Module{}, + RewrittenVars: map[Var]Var{}, + Required: &Capabilities{}, + ruleIndices: util.NewHasherMap[Ref, RuleIndex](RefEqual), + maxErrs: CompileErrorLimitDefault, + after: map[string][]CompilerStageDefinition{}, + unsafeBuiltinsMap: map[string]struct{}{}, + deprecatedBuiltinsMap: map[string]struct{}{}, + comprehensionIndices: map[*Term]*ComprehensionIndex{}, + debug: debug.Discard(), + defaultRegoVersion: DefaultRegoVersion, + } + + c.ModuleTree = NewModuleTree(nil) + c.RuleTree = NewRuleTree(c.ModuleTree) + + c.stages = []stage{ + // Reference resolution should run first as it may be used to lazily + // load additional modules. If any stages run before resolution, they + // need to be re-run after resolution. + {"ResolveRefs", "compile_stage_resolve_refs", c.resolveAllRefs}, + // The local variable generator must be initialized after references are + // resolved and the dynamic module loader has run but before subsequent + // stages that need to generate variables. + {"InitLocalVarGen", "compile_stage_init_local_var_gen", c.initLocalVarGen}, + {"RewriteRuleHeadRefs", "compile_stage_rewrite_rule_head_refs", c.rewriteRuleHeadRefs}, + {"CheckKeywordOverrides", "compile_stage_check_keyword_overrides", c.checkKeywordOverrides}, + {"CheckDuplicateImports", "compile_stage_check_imports", c.checkImports}, + {"RemoveImports", "compile_stage_remove_imports", c.removeImports}, + {"SetModuleTree", "compile_stage_set_module_tree", c.setModuleTree}, + {"SetRuleTree", "compile_stage_set_rule_tree", c.setRuleTree}, // depends on RewriteRuleHeadRefs + {"RewriteLocalVars", "compile_stage_rewrite_local_vars", c.rewriteLocalVars}, + {"CheckVoidCalls", "compile_stage_check_void_calls", c.checkVoidCalls}, + {"RewritePrintCalls", "compile_stage_rewrite_print_calls", c.rewritePrintCalls}, + {"RewriteExprTerms", "compile_stage_rewrite_expr_terms", c.rewriteExprTerms}, + {"ParseMetadataBlocks", "compile_stage_parse_metadata_blocks", c.parseMetadataBlocks}, + {"SetAnnotationSet", "compile_stage_set_annotationset", c.setAnnotationSet}, + {"RewriteRegoMetadataCalls", "compile_stage_rewrite_rego_metadata_calls", c.rewriteRegoMetadataCalls}, + {"SetGraph", "compile_stage_set_graph", c.setGraph}, + {"RewriteComprehensionTerms", "compile_stage_rewrite_comprehension_terms", c.rewriteComprehensionTerms}, + {"RewriteRefsInHead", "compile_stage_rewrite_refs_in_head", c.rewriteRefsInHead}, + {"RewriteWithValues", "compile_stage_rewrite_with_values", c.rewriteWithModifiers}, + {"CheckRuleConflicts", "compile_stage_check_rule_conflicts", c.checkRuleConflicts}, + {"CheckUndefinedFuncs", "compile_stage_check_undefined_funcs", c.checkUndefinedFuncs}, + {"CheckSafetyRuleHeads", "compile_stage_check_safety_rule_heads", c.checkSafetyRuleHeads}, + {"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}, + {"CheckDeprecatedBuiltins", "compile_state_check_deprecated_builtins", c.checkDeprecatedBuiltins}, + {"BuildRuleIndices", "compile_stage_rebuild_indices", c.buildRuleIndices}, + {"BuildComprehensionIndices", "compile_stage_rebuild_comprehension_indices", c.buildComprehensionIndices}, + {"BuildRequiredCapabilities", "compile_stage_build_required_capabilities", c.buildRequiredCapabilities}, + } + + return c +} + +// SetErrorLimit sets the number of errors the compiler can encounter before it +// quits. Zero or a negative number indicates no limit. +func (c *Compiler) SetErrorLimit(limit int) *Compiler { + c.maxErrs = limit + return c +} + +// WithEnablePrintStatements enables print statements inside of modules compiled +// by the compiler. If print statements are not enabled, calls to print() are +// erased at compile-time. +func (c *Compiler) WithEnablePrintStatements(yes bool) *Compiler { + c.enablePrintStatements = yes + return c +} + +// WithPathConflictsCheck enables base-virtual document conflict +// detection. The compiler will check that rules don't overlap with +// paths that exist as determined by the provided callable. +func (c *Compiler) WithPathConflictsCheck(fn func([]string) (bool, error)) *Compiler { + c.pathExists = fn + return c +} + +// WithPathConflictsCheckRoots enables checking path conflicts from the specified root instead +// of the top root node. Limiting conflict checks to a known set of roots, such as bundle roots, +// improves performance. Each root has the format of a "/"-delimited string, excluding the "data" +// root document. +func (c *Compiler) WithPathConflictsCheckRoots(rootPaths []string) *Compiler { + c.pathConflictCheckRoots = rootPaths + return c +} + +// WithStageAfter registers a stage to run during compilation after +// the named stage. +func (c *Compiler) WithStageAfter(after string, stage CompilerStageDefinition) *Compiler { + c.after[after] = append(c.after[after], stage) + return c +} + +// WithMetrics will set a metrics.Metrics and be used for profiling +// the Compiler instance. +func (c *Compiler) WithMetrics(metrics metrics.Metrics) *Compiler { + c.metrics = metrics + return c +} + +// WithCapabilities sets capabilities to enable during compilation. Capabilities allow the caller +// to specify the set of built-in functions available to the policy. In the future, capabilities +// may be able to restrict access to other language features. Capabilities allow callers to check +// if policies are compatible with a particular version of OPA. If policies are a compiled for a +// specific version of OPA, there is no guarantee that _this_ version of OPA can evaluate them +// successfully. +func (c *Compiler) WithCapabilities(capabilities *Capabilities) *Compiler { + c.capabilities = capabilities + return c +} + +// Capabilities returns the capabilities enabled during compilation. +func (c *Compiler) Capabilities() *Capabilities { + return c.capabilities +} + +// WithDebug sets where debug messages are written to. Passing `nil` has no +// effect. +func (c *Compiler) WithDebug(sink io.Writer) *Compiler { + if sink != nil { + c.debug = debug.New(sink) + } + return c +} + +// WithBuiltins is deprecated. +// Deprecated: Use WithCapabilities instead. +func (c *Compiler) WithBuiltins(builtins map[string]*Builtin) *Compiler { + c.customBuiltins = maps.Clone(builtins) + return c +} + +// WithUnsafeBuiltins is deprecated. +// Deprecated: Use WithCapabilities instead. +func (c *Compiler) WithUnsafeBuiltins(unsafeBuiltins map[string]struct{}) *Compiler { + maps.Copy(c.unsafeBuiltinsMap, unsafeBuiltins) + return c +} + +// WithStrict toggles strict mode in the compiler. +func (c *Compiler) WithStrict(strict bool) *Compiler { + c.strict = strict + return c +} + +// WithKeepModules enables retaining unprocessed modules in the compiler. +// Note that the modules aren't copied on the way in or out -- so when +// accessing them via ParsedModules(), mutations will occur in the module +// map that was passed into Compile().` +func (c *Compiler) WithKeepModules(y bool) *Compiler { + c.keepModules = y + return c +} + +// WithUseTypeCheckAnnotations use schema annotations during type checking +func (c *Compiler) WithUseTypeCheckAnnotations(enabled bool) *Compiler { + c.useTypeCheckAnnotations = enabled + return c +} + +func (c *Compiler) WithAllowUndefinedFunctionCalls(allow bool) *Compiler { + c.allowUndefinedFuncCalls = allow + return c +} + +// WithEvalMode allows setting the CompilerEvalMode of the compiler +func (c *Compiler) WithEvalMode(e CompilerEvalMode) *Compiler { + c.evalMode = e + 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. +func (c *Compiler) ParsedModules() map[string]*Module { + return c.parsedModules +} + +func (c *Compiler) QueryCompiler() QueryCompiler { + c.init() + c0 := *c + return newQueryCompiler(&c0) +} + +// Compile runs the compilation process on the input modules. The compiled +// version of the modules and associated data structures are stored on the +// compiler. If the compilation process fails for any reason, the compiler will +// contain a slice of errors. +func (c *Compiler) Compile(modules map[string]*Module) { + + c.init() + + c.Modules = make(map[string]*Module, len(modules)) + c.sorted = make([]string, 0, len(modules)) + + if c.keepModules { + c.parsedModules = make(map[string]*Module, len(modules)) + } else { + c.parsedModules = nil + } + + for k, v := range modules { + c.Modules[k] = v.Copy() + c.sorted = append(c.sorted, k) + if c.parsedModules != nil { + c.parsedModules[k] = v + } + } + + sort.Strings(c.sorted) + + c.compile() +} + +// WithSchemas sets a schemaSet to the compiler +func (c *Compiler) WithSchemas(schemas *SchemaSet) *Compiler { + c.schemaSet = schemas + return c +} + +// Failed returns true if a compilation error has been encountered. +func (c *Compiler) Failed() bool { + return len(c.Errors) > 0 +} + +// ComprehensionIndex returns a data structure specifying how to index comprehension +// results so that callers do not have to recompute the comprehension more than once. +// If no index is found, returns nil. +func (c *Compiler) ComprehensionIndex(term *Term) *ComprehensionIndex { + return c.comprehensionIndices[term] +} + +// GetArity returns the number of args a function referred to by ref takes. If +// ref refers to built-in function, the built-in declaration is consulted, +// otherwise, the ref is used to perform a ruleset lookup. +func (c *Compiler) GetArity(ref Ref) int { + if bi := c.builtins[ref.String()]; bi != nil { + return bi.Decl.Arity() + } + rules := c.GetRulesExact(ref) + if len(rules) == 0 { + return -1 + } + return len(rules[0].Head.Args) +} + +// GetRulesExact returns a slice of rules referred to by the reference. +// +// E.g., given the following module: +// +// package a.b.c +// +// p[k] = v { ... } # rule1 +// p[k1] = v1 { ... } # rule2 +// +// The following calls yield the rules on the right. +// +// GetRulesExact("data.a.b.c.p") => [rule1, rule2] +// GetRulesExact("data.a.b.c.p.x") => nil +// GetRulesExact("data.a.b.c") => nil +func (c *Compiler) GetRulesExact(ref Ref) (rules []*Rule) { + node := c.RuleTree + + for _, x := range ref { + if node = node.Child(x.Value); node == nil { + return nil + } + } + + return extractRules(node.Values) +} + +// GetRulesForVirtualDocument returns a slice of rules that produce the virtual +// document referred to by the reference. +// +// E.g., given the following module: +// +// package a.b.c +// +// p[k] = v { ... } # rule1 +// p[k1] = v1 { ... } # rule2 +// +// The following calls yield the rules on the right. +// +// GetRulesForVirtualDocument("data.a.b.c.p") => [rule1, rule2] +// GetRulesForVirtualDocument("data.a.b.c.p.x") => [rule1, rule2] +// GetRulesForVirtualDocument("data.a.b.c") => nil +func (c *Compiler) GetRulesForVirtualDocument(ref Ref) (rules []*Rule) { + + node := c.RuleTree + + for _, x := range ref { + if node = node.Child(x.Value); node == nil { + return nil + } + if len(node.Values) > 0 { + return extractRules(node.Values) + } + } + + return extractRules(node.Values) +} + +// GetRulesWithPrefix returns a slice of rules that share the prefix ref. +// +// E.g., given the following module: +// +// package a.b.c +// +// p[x] = y { ... } # rule1 +// p[k] = v { ... } # rule2 +// q { ... } # rule3 +// +// The following calls yield the rules on the right. +// +// GetRulesWithPrefix("data.a.b.c.p") => [rule1, rule2] +// GetRulesWithPrefix("data.a.b.c.p.a") => nil +// GetRulesWithPrefix("data.a.b.c") => [rule1, rule2, rule3] +func (c *Compiler) GetRulesWithPrefix(ref Ref) (rules []*Rule) { + + node := c.RuleTree + + for _, x := range ref { + if node = node.Child(x.Value); node == nil { + return nil + } + } + + var acc func(node *TreeNode) + + acc = func(node *TreeNode) { + rules = append(rules, extractRules(node.Values)...) + for _, child := range node.Children { + if child.Hide { + continue + } + acc(child) + } + } + + acc(node) + + return rules +} + +func extractRules(s []any) []*Rule { + rules := make([]*Rule, len(s)) + for i := range s { + rules[i] = s[i].(*Rule) + } + return rules +} + +// GetRules returns a slice of rules that are referred to by ref. +// +// E.g., given the following module: +// +// package a.b.c +// +// p[x] = y { q[x] = y; ... } # rule1 +// q[x] = y { ... } # rule2 +// +// The following calls yield the rules on the right. +// +// GetRules("data.a.b.c.p") => [rule1] +// GetRules("data.a.b.c.p.x") => [rule1] +// GetRules("data.a.b.c.q") => [rule2] +// GetRules("data.a.b.c") => [rule1, rule2] +// GetRules("data.a.b.d") => nil +func (c *Compiler) GetRules(ref Ref) (rules []*Rule) { + + set := map[*Rule]struct{}{} + + for _, rule := range c.GetRulesForVirtualDocument(ref) { + set[rule] = struct{}{} + } + + for _, rule := range c.GetRulesWithPrefix(ref) { + set[rule] = struct{}{} + } + + for rule := range set { + rules = append(rules, rule) + } + + return rules +} + +// GetRulesDynamic returns a slice of rules that could be referred to by a ref. +// +// Deprecated: use GetRulesDynamicWithOpts +func (c *Compiler) GetRulesDynamic(ref Ref) []*Rule { + return c.GetRulesDynamicWithOpts(ref, RulesOptions{}) +} + +// GetRulesDynamicWithOpts returns a slice of rules that could be referred to by +// a ref. +// When parts of the ref are statically known, we use that information to narrow +// down which rules the ref could refer to, but in the most general case this +// will be an over-approximation. +// +// E.g., given the following modules: +// +// package a.b.c +// +// r1 = 1 # rule1 +// +// and: +// +// package a.d.c +// +// r2 = 2 # rule2 +// +// The following calls yield the rules on the right. +// +// GetRulesDynamicWithOpts("data.a[x].c[y]", opts) => [rule1, rule2] +// GetRulesDynamicWithOpts("data.a[x].c.r2", opts) => [rule2] +// GetRulesDynamicWithOpts("data.a.b[x][y]", opts) => [rule1] +// +// Using the RulesOptions parameter, the inclusion of hidden modules can be +// controlled: +// +// With +// +// package system.main +// +// r3 = 3 # rule3 +// +// We'd get this result: +// +// GetRulesDynamicWithOpts("data[x]", RulesOptions{IncludeHiddenModules: true}) => [rule1, rule2, rule3] +// +// Without the options, it would be excluded. +func (c *Compiler) GetRulesDynamicWithOpts(ref Ref, opts RulesOptions) []*Rule { + node := c.RuleTree + + set := map[*Rule]struct{}{} + var walk func(node *TreeNode, i int) + walk = func(node *TreeNode, i int) { + switch { + case i >= len(ref): + // We've reached the end of the reference and want to collect everything + // under this "prefix". + node.DepthFirst(func(descendant *TreeNode) bool { + insertRules(set, descendant.Values) + if opts.IncludeHiddenModules { + return false + } + return descendant.Hide + }) + + case i == 0 || IsConstant(ref[i].Value): + // The head of the ref is always grounded. In case another part of the + // ref is also grounded, we can lookup the exact child. If it's not found + // we can immediately return... + if child := node.Child(ref[i].Value); child != nil { + if len(child.Values) > 0 { + // Add any rules at this position + insertRules(set, child.Values) + } + // There might still be "sub-rules" contributing key-value "overrides" for e.g. partial object rules, continue walking + walk(child, i+1) + } else { + return + } + + default: + // This part of the ref is a dynamic term. We can't know what it refers + // to and will just need to try all of the children. + for _, child := range node.Children { + if child.Hide && !opts.IncludeHiddenModules { + continue + } + insertRules(set, child.Values) + walk(child, i+1) + } + } + } + + walk(node, 0) + rules := make([]*Rule, 0, len(set)) + for rule := range set { + rules = append(rules, rule) + } + return rules +} + +// Utility: add all rule values to the set. +func insertRules(set map[*Rule]struct{}, rules []any) { + for _, rule := range rules { + set[rule.(*Rule)] = struct{}{} + } +} + +// RuleIndex returns a RuleIndex built for the rule set referred to by path. +// The path must refer to the rule set exactly, i.e., given a rule set at path +// data.a.b.c.p, refs data.a.b.c.p.x and data.a.b.c would not return a +// RuleIndex built for the rule. +func (c *Compiler) RuleIndex(path Ref) RuleIndex { + r, ok := c.ruleIndices.Get(path) + if !ok { + return nil + } + return r +} + +// PassesTypeCheck determines whether the given body passes type checking +func (c *Compiler) PassesTypeCheck(body Body) bool { + checker := newTypeChecker().WithSchemaSet(c.schemaSet).WithInputType(c.inputType) + env := c.TypeEnv + _, errs := checker.CheckBody(env, body) + return len(errs) == 0 +} + +// PassesTypeCheckRules determines whether the given rules passes type checking +func (c *Compiler) PassesTypeCheckRules(rules []*Rule) Errors { + elems := []util.T{} + + for _, rule := range rules { + elems = append(elems, rule) + } + + // Load the global input schema if one was provided. + if c.schemaSet != nil { + if schema := c.schemaSet.Get(SchemaRootRef); schema != nil { + + var allowNet []string + if c.capabilities != nil { + allowNet = c.capabilities.AllowNet + } + + tpe, err := loadSchema(schema, allowNet) + if err != nil { + return Errors{NewError(TypeErr, nil, err.Error())} //nolint:govet + } + c.inputType = tpe + } + } + + var as *AnnotationSet + if c.useTypeCheckAnnotations { + as = c.annotationSet + } + + checker := newTypeChecker().WithSchemaSet(c.schemaSet).WithInputType(c.inputType) + + if c.TypeEnv == nil { + if c.capabilities == nil { + c.capabilities = CapabilitiesForThisVersion() + } + + c.builtins = make(map[string]*Builtin, len(c.capabilities.Builtins)+len(c.customBuiltins)) + + for _, bi := range c.capabilities.Builtins { + c.builtins[bi.Name] = bi + } + + maps.Copy(c.builtins, c.customBuiltins) + + c.TypeEnv = checker.Env(c.builtins) + } + + _, errs := checker.CheckTypes(c.TypeEnv, elems, as) + return errs +} + +// ModuleLoader defines the interface that callers can implement to enable lazy +// loading of modules during compilation. +type ModuleLoader func(resolved map[string]*Module) (parsed map[string]*Module, err error) + +// WithModuleLoader sets f as the ModuleLoader on the compiler. +// +// The compiler will invoke the ModuleLoader after resolving all references in +// the current set of input modules. The ModuleLoader can return a new +// collection of parsed modules that are to be included in the compilation +// process. This process will repeat until the ModuleLoader returns an empty +// collection or an error. If an error is returned, compilation will stop +// immediately. +func (c *Compiler) WithModuleLoader(f ModuleLoader) *Compiler { + c.moduleLoader = f + return c +} + +// WithDefaultRegoVersion sets the default Rego version to use when a module doesn't specify one; +// such as when it's hand-crafted instead of parsed. +func (c *Compiler) WithDefaultRegoVersion(regoVersion RegoVersion) *Compiler { + c.defaultRegoVersion = regoVersion + return c +} + +func (c *Compiler) counterAdd(name string, n uint64) { + if c.metrics == nil { + return + } + c.metrics.Counter(name).Add(n) +} + +func (c *Compiler) buildRuleIndices() { + + c.RuleTree.DepthFirst(func(node *TreeNode) bool { + if len(node.Values) == 0 { + return false + } + rules := extractRules(node.Values) + hasNonGroundRef := false + for _, r := range rules { + hasNonGroundRef = !r.Head.Ref().IsGround() + } + if hasNonGroundRef { + // Collect children to ensure that all rules within the extent of a rule with a general ref + // are found on the same index. E.g. the following rules should be indexed under data.a.b.c: + // + // package a + // b.c[x].e := 1 { x := input.x } + // b.c.d := 2 + // b.c.d2.e[x] := 3 { x := input.x } + for _, child := range node.Children { + child.DepthFirst(func(c *TreeNode) bool { + rules = append(rules, extractRules(c.Values)...) + return false + }) + } + } + + index := newBaseDocEqIndex(func(ref Ref) bool { + return isVirtual(c.RuleTree, ref.GroundPrefix()) + }) + if index.Build(rules) { + c.ruleIndices.Put(rules[0].Ref().GroundPrefix(), index) + } + return hasNonGroundRef // currently, we don't allow those branches to go deeper + }) + +} + +func (c *Compiler) buildComprehensionIndices() { + for _, name := range c.sorted { + WalkRules(c.Modules[name], func(r *Rule) bool { + candidates := ReservedVars.Copy() + if len(r.Head.Args) > 0 { + candidates.Update(r.Head.Args.Vars()) + } + n := buildComprehensionIndices(c.debug, c.GetArity, candidates, c.RewrittenVars, r.Body, c.comprehensionIndices) + c.counterAdd(compileStageComprehensionIndexBuild, n) + return false + }) + } +} + +var futureKeywordsPrefix = Ref{FutureRootDocument, InternedTerm("keywords")} + +// buildRequiredCapabilities updates the required capabilities on the compiler +// to include any keyword and feature dependencies present in the modules. The +// built-in function dependencies will have already been added by the type +// checker. +func (c *Compiler) buildRequiredCapabilities() { + + features := map[string]struct{}{} + + // extract required keywords from modules + + keywords := map[string]struct{}{} + + for _, name := range c.sorted { + for _, imp := range c.imports[name] { + mod := c.Modules[name] + path := imp.Path.Value.(Ref) + switch { + case path.Equal(RegoV1CompatibleRef): + if !c.moduleIsRegoV1(mod) { + features[FeatureRegoV1Import] = struct{}{} + } + case path.HasPrefix(futureKeywordsPrefix): + if len(path) == 2 { + if c.moduleIsRegoV1(mod) { + for kw := range futureKeywords { + keywords[kw] = struct{}{} + } + } else { + for kw := range allFutureKeywords { + keywords[kw] = struct{}{} + } + } + } else { + kw := string(path[2].Value.(String)) + if c.moduleIsRegoV1(mod) { + for allowedKw := range futureKeywords { + if kw == allowedKw { + keywords[kw] = struct{}{} + break + } + } + } else { + for allowedKw := range allFutureKeywords { + if kw == allowedKw { + keywords[kw] = struct{}{} + break + } + } + } + } + } + } + } + + c.Required.FutureKeywords = util.KeysSorted(keywords) + + // extract required features from modules + + for _, name := range c.sorted { + mod := c.Modules[name] + + if c.moduleIsRegoV1(mod) { + features[FeatureRegoV1] = struct{}{} + } else { + for _, rule := range mod.Rules { + refLen := len(rule.Head.Reference) + if refLen >= 3 { + if refLen > len(rule.Head.Reference.ConstantPrefix()) { + features[FeatureRefHeads] = struct{}{} + } else { + features[FeatureRefHeadStringPrefixes] = struct{}{} + } + } + } + } + } + + c.Required.Features = util.KeysSorted(features) + + for i, bi := range c.Required.Builtins { + c.Required.Builtins[i] = bi.Minimal() + } +} + +// checkRecursion ensures that there are no recursive definitions, i.e., there are +// no cycles in the Graph. +func (c *Compiler) checkRecursion() { + eq := func(a, b util.T) bool { + return a.(*Rule) == b.(*Rule) + } + + c.RuleTree.DepthFirst(func(node *TreeNode) bool { + for _, rule := range node.Values { + for node := rule.(*Rule); node != nil; node = node.Else { + c.checkSelfPath(node.Loc(), eq, node, node) + } + } + return false + }) +} + +func (c *Compiler) checkSelfPath(loc *Location, eq func(a, b util.T) bool, a, b util.T) { + tr := NewGraphTraversal(c.Graph) + if p := util.DFSPath(tr, eq, a, b); len(p) > 0 { + n := make([]string, 0, len(p)) + for _, x := range p { + n = append(n, astNodeToString(x)) + } + c.err(NewError(RecursionErr, loc, "rule %v is recursive: %v", astNodeToString(a), strings.Join(n, " -> "))) + } +} + +func astNodeToString(x any) string { + return x.(*Rule).Ref().String() +} + +// checkRuleConflicts ensures that rules definitions are not in conflict. +func (c *Compiler) checkRuleConflicts() { + rw := rewriteVarsInRef(c.RewrittenVars) + + c.RuleTree.DepthFirst(func(node *TreeNode) bool { + if len(node.Values) == 0 { + return false // go deeper + } + + kinds := make(map[RuleKind]struct{}, len(node.Values)) + completeRules := 0 + partialRules := 0 + arities := make(map[int]struct{}, len(node.Values)) + name := "" + var conflicts []Ref + defaultRules := make([]*Rule, 0) + + for _, rule := range node.Values { + r := rule.(*Rule) + ref := r.Ref() + name = rw(ref.CopyNonGround()).String() // varRewriter operates in-place + kinds[r.Head.RuleKind()] = struct{}{} + arities[len(r.Head.Args)] = struct{}{} + if r.Default { + defaultRules = append(defaultRules, r) + } + + // Single-value rules may not have any other rules in their extent. + // Rules with vars in their ref are allowed to have rules inside their extent. + // Only the ground portion (terms before the first var term) of a rule's ref is considered when determining + // whether it's inside the extent of another (c.RuleTree is organized this way already). + // These pairs are invalid: + // + // data.p.q.r { true } # data.p.q is { "r": true } + // data.p.q.r.s { true } + // + // data.p.q.r { true } + // data.p.q.r[s].t { s = input.key } + // + // But this is allowed: + // + // data.p.q.r { true } + // data.p.q[r].s.t { r = input.key } + // + // data.p[r] := x { r = input.key; x = input.bar } + // data.p.q[r] := x { r = input.key; x = input.bar } + // + // data.p.q[r] { r := input.r } + // data.p.q.r.s { true } + // + // data.p.q[r] = 1 { r := "r" } + // data.p.q.s = 2 + // + // data.p[q][r] { q := input.q; r := input.r } + // data.p.q.r { true } + // + // data.p.q[r] { r := input.r } + // data.p[q].r { q := input.q } + // + // data.p.q[r][s] { r := input.r; s := input.s } + // data.p[q].r.s { q := input.q } + + if ref.IsGround() && len(node.Children) > 0 { + conflicts = node.flattenChildren() + } + + if r.Head.RuleKind() == SingleValue && r.Head.Ref().IsGround() { + completeRules++ + } else { + partialRules++ + } + } + + switch { + case conflicts != nil: + c.err(NewError(TypeErr, node.Values[0].(*Rule).Loc(), "rule %v conflicts with %v", name, conflicts)) + + case len(kinds) > 1 || len(arities) > 1 || (completeRules >= 1 && partialRules >= 1): + c.err(NewError(TypeErr, node.Values[0].(*Rule).Loc(), "conflicting rules %v found", name)) + + case len(defaultRules) > 1: + + defaultRuleLocations := strings.Builder{} + defaultRuleLocations.WriteString(defaultRules[0].Loc().String()) + for i := 1; i < len(defaultRules); i++ { + defaultRuleLocations.WriteString(", ") + defaultRuleLocations.WriteString(defaultRules[i].Loc().String()) + } + + c.err(NewError( + TypeErr, + defaultRules[0].Module.Package.Loc(), + "multiple default rules %s found at %s", + name, defaultRuleLocations.String()), + ) + } + + return false + }) + + if c.pathExists != nil { + for _, err := range CheckPathConflicts(c, c.pathExists) { + c.err(err) + } + } + + // NOTE(sr): depthfirst might better use sorted for stable errs? + c.ModuleTree.DepthFirst(func(node *ModuleTreeNode) bool { + for _, mod := range node.Modules { + for _, rule := range mod.Rules { + ref := rule.Head.Ref().GroundPrefix() + // Rules with a dynamic portion in their ref are exempted, as a conflict within the dynamic portion + // can only be detected at eval-time. + if len(ref) < len(rule.Head.Ref()) { + continue + } + + childNode, tail := node.find(ref) + if childNode != nil && len(tail) == 0 { + for _, childMod := range childNode.Modules { + // Avoid recursively checking a module for equality unless we know it's a possible self-match. + if childMod.Equal(mod) { + continue // don't self-conflict + } + msg := fmt.Sprintf("%v conflicts with rule %v defined at %v", childMod.Package, rule.Head.Ref(), rule.Loc()) + c.err(NewError(TypeErr, mod.Package.Loc(), msg)) //nolint:govet + } + } + } + } + return false + }) +} + +func (c *Compiler) checkUndefinedFuncs() { + for _, name := range c.sorted { + m := c.Modules[name] + for _, err := range checkUndefinedFuncs(c.TypeEnv, m, c.GetArity, c.RewrittenVars) { + c.err(err) + } + } +} + +func checkUndefinedFuncs(env *TypeEnv, x any, arity func(Ref) int, rwVars map[Var]Var) Errors { + + var errs Errors + + WalkExprs(x, func(expr *Expr) bool { + if !expr.IsCall() { + return false + } + ref := expr.Operator() + if arity := arity(ref); arity >= 0 { + operands := len(expr.Operands()) + if expr.Generated { // an output var was added + if !expr.IsEquality() && operands != arity+1 { + ref = rewriteVarsInRef(rwVars)(ref) + errs = append(errs, arityMismatchError(env, ref, expr, arity, operands-1)) + return true + } + } else { // either output var or not + if operands != arity && operands != arity+1 { + ref = rewriteVarsInRef(rwVars)(ref) + errs = append(errs, arityMismatchError(env, ref, expr, arity, operands)) + return true + } + } + return false + } + ref = rewriteVarsInRef(rwVars)(ref) + errs = append(errs, NewError(TypeErr, expr.Loc(), "undefined function %v", ref)) + return true + }) + + return errs +} + +func arityMismatchError(env *TypeEnv, f Ref, expr *Expr, exp, act int) *Error { + if want, ok := env.Get(f).(*types.Function); ok { // generate richer error for built-in functions + have := make([]types.Type, len(expr.Operands())) + for i, op := range expr.Operands() { + have[i] = env.Get(op) + } + return newArgError(expr.Loc(), f, "arity mismatch", have, want.NamedFuncArgs()) + } + if act != 1 { + return NewError(TypeErr, expr.Loc(), "function %v has arity %d, got %d arguments", f, exp, act) + } + return NewError(TypeErr, expr.Loc(), "function %v has arity %d, got %d argument", f, exp, act) +} + +// checkSafetyRuleBodies ensures that variables appearing in negated expressions or non-target +// positions of built-in expressions will be bound when evaluating the rule from left +// to right, re-ordering as necessary. +func (c *Compiler) checkSafetyRuleBodies() { + for _, name := range c.sorted { + m := c.Modules[name] + WalkRules(m, func(r *Rule) bool { + safe := ReservedVars.Copy() + if len(r.Head.Args) > 0 { + safe.Update(r.Head.Args.Vars()) + } + r.Body = c.checkBodySafety(safe, r.Body) + return false + }) + } +} + +func (c *Compiler) checkBodySafety(safe VarSet, b Body) Body { + reordered, unsafe := reorderBodyForSafety(c.builtins, c.GetArity, safe, b) + if errs := safetyErrorSlice(unsafe, c.RewrittenVars); len(errs) > 0 { + for _, err := range errs { + c.err(err) + } + return b + } + return reordered +} + +// SafetyCheckVisitorParams defines the AST visitor parameters to use for collecting +// variables during the safety check. This has to be exported because it's relied on +// by the copy propagation implementation in topdown. +var SafetyCheckVisitorParams = VarVisitorParams{ + SkipRefCallHead: true, + SkipClosures: true, +} + +// checkSafetyRuleHeads ensures that variables appearing in the head of a +// rule also appear in the body. +func (c *Compiler) checkSafetyRuleHeads() { + for _, name := range c.sorted { + WalkRules(c.Modules[name], func(r *Rule) bool { + safe := r.Body.Vars(SafetyCheckVisitorParams) + if len(r.Head.Args) > 0 { + safe.Update(r.Head.Args.Vars()) + } + if headMayHaveVars(r.Head) { + vars := r.Head.Vars() + if vars.DiffCount(safe) > 0 { + unsafe := vars.Diff(safe) + for v := range unsafe { + if w, ok := c.RewrittenVars[v]; ok { + v = w + } + if !v.IsGenerated() { + c.err(NewError(UnsafeVarErr, r.Loc(), "var %v is unsafe", v)) + } + } + } + } + return false + }) + } +} + +func compileSchema(goSchema any, allowNet []string) (*gojsonschema.Schema, error) { + gojsonschema.SetAllowNet(allowNet) + + var refLoader gojsonschema.JSONLoader + sl := gojsonschema.NewSchemaLoader() + + if goSchema != nil { + refLoader = gojsonschema.NewGoLoader(goSchema) + } else { + return nil, errors.New("no schema as input to compile") + } + schemasCompiled, err := sl.Compile(refLoader) + if err != nil { + return nil, fmt.Errorf("unable to compile the schema: %w", err) + } + return schemasCompiled, nil +} + +func mergeSchemas(schemas ...*gojsonschema.SubSchema) (*gojsonschema.SubSchema, error) { + if len(schemas) == 0 { + return nil, nil + } + var result = schemas[0] + + for i := range schemas { + if len(schemas[i].PropertiesChildren) > 0 { + if !schemas[i].Types.Contains("object") { + if err := schemas[i].Types.Add("object"); err != nil { + return nil, errors.New("unable to set the type in schemas") + } + } + } else if len(schemas[i].ItemsChildren) > 0 { + if !schemas[i].Types.Contains("array") { + if err := schemas[i].Types.Add("array"); err != nil { + return nil, errors.New("unable to set the type in schemas") + } + } + } + } + + for i := 1; i < len(schemas); i++ { + if result.Types.String() != schemas[i].Types.String() { + return nil, fmt.Errorf("unable to merge these schemas: type mismatch: %v and %v", result.Types.String(), schemas[i].Types.String()) + } else if result.Types.Contains("object") && len(result.PropertiesChildren) > 0 && schemas[i].Types.Contains("object") && len(schemas[i].PropertiesChildren) > 0 { + result.PropertiesChildren = append(result.PropertiesChildren, schemas[i].PropertiesChildren...) + } else if result.Types.Contains("array") && len(result.ItemsChildren) > 0 && schemas[i].Types.Contains("array") && len(schemas[i].ItemsChildren) > 0 { + for j := range len(schemas[i].ItemsChildren) { + if len(result.ItemsChildren)-1 < j && !(len(schemas[i].ItemsChildren)-1 < j) { + result.ItemsChildren = append(result.ItemsChildren, schemas[i].ItemsChildren[j]) + } + if result.ItemsChildren[j].Types.String() != schemas[i].ItemsChildren[j].Types.String() { + return nil, errors.New("unable to merge these schemas") + } + } + } + } + return result, nil +} + +type schemaParser struct { + definitionCache map[string]*cachedDef +} + +type cachedDef struct { + properties []*types.StaticProperty +} + +func newSchemaParser() *schemaParser { + return &schemaParser{ + definitionCache: map[string]*cachedDef{}, + } +} + +func (parser *schemaParser) parseSchema(schema any) (types.Type, error) { + return parser.parseSchemaWithPropertyKey(schema, "") +} + +func (parser *schemaParser) parseSchemaWithPropertyKey(schema any, propertyKey string) (types.Type, error) { + subSchema, ok := schema.(*gojsonschema.SubSchema) + if !ok { + return nil, fmt.Errorf("unexpected schema type %v", subSchema) + } + + // Handle referenced schemas, returns directly when a $ref is found + if subSchema.RefSchema != nil { + if existing, ok := parser.definitionCache[subSchema.Ref.String()]; ok { + return types.NewObject(existing.properties, nil), nil + } + return parser.parseSchemaWithPropertyKey(subSchema.RefSchema, subSchema.Ref.String()) + } + + // Handle anyOf + if subSchema.AnyOf != nil { + var orType types.Type + + // If there is a core schema, find its type first + if subSchema.Types.IsTyped() { + copySchema := *subSchema + copySchemaRef := ©Schema + copySchemaRef.AnyOf = nil + coreType, err := parser.parseSchema(copySchemaRef) + if err != nil { + return nil, fmt.Errorf("unexpected schema type %v: %w", subSchema, err) + } + + // Only add Object type with static props to orType + if objType, ok := coreType.(*types.Object); ok { + if objType.StaticProperties() != nil && objType.DynamicProperties() == nil { + orType = types.Or(orType, coreType) + } + } + } + + // Iterate through every property of AnyOf and add it to orType + for _, pSchema := range subSchema.AnyOf { + newtype, err := parser.parseSchema(pSchema) + if err != nil { + return nil, fmt.Errorf("unexpected schema type %v: %w", pSchema, err) + } + orType = types.Or(newtype, orType) + } + + return orType, nil + } + + if subSchema.AllOf != nil { + subSchemaArray := subSchema.AllOf + allOfResult, err := mergeSchemas(subSchemaArray...) + if err != nil { + return nil, err + } + + if subSchema.Types.IsTyped() { + if (subSchema.Types.Contains("object") && allOfResult.Types.Contains("object")) || (subSchema.Types.Contains("array") && allOfResult.Types.Contains("array")) { + objectOrArrayResult, err := mergeSchemas(allOfResult, subSchema) + if err != nil { + return nil, err + } + return parser.parseSchema(objectOrArrayResult) + } else if subSchema.Types.String() != allOfResult.Types.String() { + return nil, errors.New("unable to merge these schemas") + } + } + return parser.parseSchema(allOfResult) + } + + if subSchema.Types.IsTyped() { + if subSchema.Types.Contains("boolean") { + return types.B, nil + + } else if subSchema.Types.Contains("string") { + return types.S, nil + + } else if subSchema.Types.Contains("integer") || subSchema.Types.Contains("number") { + return types.N, nil + + } else if subSchema.Types.Contains("object") { + if len(subSchema.PropertiesChildren) > 0 { + def := &cachedDef{ + properties: make([]*types.StaticProperty, 0, len(subSchema.PropertiesChildren)), + } + for _, pSchema := range subSchema.PropertiesChildren { + def.properties = append(def.properties, types.NewStaticProperty(pSchema.Property, nil)) + } + if propertyKey != "" { + parser.definitionCache[propertyKey] = def + } + for _, pSchema := range subSchema.PropertiesChildren { + newtype, err := parser.parseSchema(pSchema) + if err != nil { + return nil, fmt.Errorf("unexpected schema type %v: %w", pSchema, err) + } + for i, prop := range def.properties { + if prop.Key == pSchema.Property { + def.properties[i].Value = newtype + break + } + } + } + return types.NewObject(def.properties, nil), nil + } + return types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), nil + + } else if subSchema.Types.Contains("array") { + if len(subSchema.ItemsChildren) > 0 { + if subSchema.ItemsChildrenIsSingleSchema { + iSchema := subSchema.ItemsChildren[0] + newtype, err := parser.parseSchema(iSchema) + if err != nil { + return nil, fmt.Errorf("unexpected schema type %v", iSchema) + } + return types.NewArray(nil, newtype), nil + } + newTypes := make([]types.Type, 0, len(subSchema.ItemsChildren)) + for i := 0; i != len(subSchema.ItemsChildren); i++ { + iSchema := subSchema.ItemsChildren[i] + newtype, err := parser.parseSchema(iSchema) + if err != nil { + return nil, fmt.Errorf("unexpected schema type %v", iSchema) + } + newTypes = append(newTypes, newtype) + } + return types.NewArray(newTypes, nil), nil + } + return types.NewArray(nil, types.A), nil + } + } + + // Assume types if not specified in schema + if len(subSchema.PropertiesChildren) > 0 { + if err := subSchema.Types.Add("object"); err == nil { + return parser.parseSchema(subSchema) + } + } else if len(subSchema.ItemsChildren) > 0 { + if err := subSchema.Types.Add("array"); err == nil { + return parser.parseSchema(subSchema) + } + } + + return types.A, nil +} + +func (c *Compiler) setAnnotationSet() { + // Sorting modules by name for stable error reporting + sorted := make([]*Module, 0, len(c.Modules)) + for _, mName := range c.sorted { + sorted = append(sorted, c.Modules[mName]) + } + + as, errs := BuildAnnotationSet(sorted) + for _, err := range errs { + c.err(err) + } + c.annotationSet = as +} + +// checkTypes runs the type checker on all rules. The type checker builds a +// TypeEnv that is stored on the compiler. +func (c *Compiler) checkTypes() { + // Recursion is caught in earlier step, so this cannot fail. + sorted, _ := c.Graph.Sort() + checker := newTypeChecker(). + WithAllowNet(c.capabilities.AllowNet). + WithSchemaSet(c.schemaSet). + WithInputType(c.inputType). + WithBuiltins(c.builtins). + WithRequiredCapabilities(c.Required). + WithVarRewriter(rewriteVarsInRef(c.RewrittenVars)). + WithAllowUndefinedFunctionCalls(c.allowUndefinedFuncCalls) + var as *AnnotationSet + if c.useTypeCheckAnnotations { + as = c.annotationSet + } + env, errs := checker.CheckTypes(c.TypeEnv, sorted, as) + for _, err := range errs { + c.err(err) + } + c.TypeEnv = env +} + +func (c *Compiler) checkUnsafeBuiltins() { + if len(c.unsafeBuiltinsMap) == 0 { + return + } + + for _, name := range c.sorted { + errs := checkUnsafeBuiltins(c.unsafeBuiltinsMap, c.Modules[name]) + for _, err := range errs { + c.err(err) + } + } +} + +func (c *Compiler) checkDeprecatedBuiltins() { + checkNeeded := false + for _, b := range c.Required.Builtins { + if _, found := c.deprecatedBuiltinsMap[b.Name]; found { + checkNeeded = true + break + } + } + if !checkNeeded { + return + } + + for _, name := range c.sorted { + mod := c.Modules[name] + if c.strict || mod.regoV1Compatible() { + errs := checkDeprecatedBuiltins(c.deprecatedBuiltinsMap, mod) + for _, err := range errs { + c.err(err) + } + } + } +} + +func (c *Compiler) runStage(metricName string, f func()) { + if c.metrics != nil { + c.metrics.Timer(metricName).Start() + defer c.metrics.Timer(metricName).Stop() + } + f() +} + +func (c *Compiler) runStageAfter(metricName string, s CompilerStage) *Error { + if c.metrics != nil { + c.metrics.Timer(metricName).Start() + defer c.metrics.Timer(metricName).Stop() + } + return s(c) +} + +func (c *Compiler) compile() { + + defer func() { + if r := recover(); r != nil && r != errLimitReached { + panic(r) + } + }() + + for _, s := range c.stages { + if c.evalMode == EvalModeIR { + switch s.name { + case "BuildRuleIndices", "BuildComprehensionIndices": + continue // skip these stages + } + } + + if c.allowUndefinedFuncCalls && (s.name == "CheckUndefinedFuncs" || s.name == "CheckSafetyRuleBodies") { + continue + } + + c.runStage(s.metricName, s.f) + if c.Failed() { + return + } + for _, a := range c.after[s.name] { + if err := c.runStageAfter(a.MetricName, a.Stage); err != nil { + c.err(err) + return + } + } + } +} + +func (c *Compiler) init() { + + if c.initialized { + return + } + + if defaultModuleLoader != nil { + if c.moduleLoader == nil { + c.moduleLoader = defaultModuleLoader + } else { + first := c.moduleLoader + c.moduleLoader = func(res map[string]*Module) (map[string]*Module, error) { + res0, err := first(res) + if err != nil { + return nil, err + } + res1, err := defaultModuleLoader(res) + if err != nil { + return nil, err + } + // merge res1 into res0, based on module "file" names, to avoid clashes + for k, v := range res1 { + if _, ok := res0[k]; !ok { + res0[k] = v + } + } + return res0, nil + } + } + } + + if c.capabilities == nil { + c.capabilities = CapabilitiesForThisVersion() + } + + c.builtins = make(map[string]*Builtin, len(c.capabilities.Builtins)+len(c.customBuiltins)) + + for _, bi := range c.capabilities.Builtins { + c.builtins[bi.Name] = bi + if bi.IsDeprecated() { + c.deprecatedBuiltinsMap[bi.Name] = struct{}{} + } + } + + maps.Copy(c.builtins, c.customBuiltins) + + // Load the global input schema if one was provided. + if c.schemaSet != nil { + if schema := c.schemaSet.Get(SchemaRootRef); schema != nil { + tpe, err := loadSchema(schema, c.capabilities.AllowNet) + if err != nil { + c.err(NewError(TypeErr, nil, err.Error())) //nolint:govet + } else { + c.inputType = tpe + } + } + } + + c.TypeEnv = newTypeChecker(). + WithSchemaSet(c.schemaSet). + WithInputType(c.inputType). + Env(c.builtins) + + c.initialized = true +} + +func (c *Compiler) err(err *Error) { + if c.maxErrs > 0 && len(c.Errors) >= c.maxErrs { + c.Errors = append(c.Errors, errLimitReached) + panic(errLimitReached) + } + c.Errors = append(c.Errors, err) +} + +func (c *Compiler) getExports() *util.HasherMap[Ref, []Ref] { + + rules := util.NewHasherMap[Ref, []Ref](RefEqual) + + for _, name := range c.sorted { + mod := c.Modules[name] + + for _, rule := range mod.Rules { + hashMapAdd(rules, mod.Package.Path, rule.Head.Ref().GroundPrefix()) + } + } + + return rules +} + +func refSliceEqual(a, b []Ref) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if !a[i].Equal(b[i]) { + return false + } + } + return true +} + +func hashMapAdd(rules *util.HasherMap[Ref, []Ref], pkg, rule Ref) { + prev, ok := rules.Get(pkg) + if !ok { + rules.Put(pkg, []Ref{rule}) + return + } + for _, p := range prev { + if p.Equal(rule) { + return + } + } + rules.Put(pkg, append(prev, rule)) +} + +func (c *Compiler) GetAnnotationSet() *AnnotationSet { + return c.annotationSet +} + +func (c *Compiler) checkImports() { + modules := make([]*Module, 0, len(c.Modules)) + + supportsRegoV1Import := c.capabilities.ContainsFeature(FeatureRegoV1Import) || + c.capabilities.ContainsFeature(FeatureRegoV1) + + for _, name := range c.sorted { + mod := c.Modules[name] + + for _, imp := range mod.Imports { + if !supportsRegoV1Import && RegoV1CompatibleRef.Equal(imp.Path.Value) { + c.err(NewError(CompileErr, imp.Loc(), "rego.v1 import is not supported")) + } + } + + if c.strict || c.moduleIsRegoV1Compatible(mod) { + modules = append(modules, mod) + } + } + + errs := checkDuplicateImports(modules) + for _, err := range errs { + c.err(err) + } +} + +func (c *Compiler) checkKeywordOverrides() { + for _, name := range c.sorted { + mod := c.Modules[name] + if c.strict || c.moduleIsRegoV1Compatible(mod) { + errs := checkRootDocumentOverrides(mod) + for _, err := range errs { + c.err(err) + } + } + } +} + +func (c *Compiler) moduleIsRegoV1(mod *Module) bool { + if mod.regoVersion == RegoUndefined { + switch c.defaultRegoVersion { + case RegoUndefined: + c.err(NewError(CompileErr, mod.Package.Loc(), "cannot determine rego version for module")) + return false + case RegoV1: + return true + } + return false + } + return mod.regoVersion == RegoV1 +} + +func (c *Compiler) moduleIsRegoV1Compatible(mod *Module) bool { + if mod.regoVersion == RegoUndefined { + switch c.defaultRegoVersion { + case RegoUndefined: + c.err(NewError(CompileErr, mod.Package.Loc(), "cannot determine rego version for module")) + return false + case RegoV1, RegoV0CompatV1: + return true + } + return false + } + return mod.regoV1Compatible() +} + +// resolveAllRefs resolves references in expressions to their fully qualified values. +// +// For instance, given the following module: +// +// package a.b +// import data.foo.bar +// p[x] { bar[_] = x } +// +// The reference "bar[_]" would be resolved to "data.foo.bar[_]". +// +// Ref rules are resolved, too: +// +// package a.b +// q { c.d.e == 1 } +// c.d[e] := 1 if e := "e" +// +// The reference "c.d.e" would be resolved to "data.a.b.c.d.e". +func (c *Compiler) resolveAllRefs() { + + rules := c.getExports() + + for _, name := range c.sorted { + mod := c.Modules[name] + + var ruleExports []Ref + if x, ok := rules.Get(mod.Package.Path); ok { + ruleExports = x + } + + globals := getGlobals(mod.Package, ruleExports, mod.Imports) + + WalkRules(mod, func(rule *Rule) bool { + err := resolveRefsInRule(globals, rule) + if err != nil { + c.err(NewError(CompileErr, rule.Location, err.Error())) //nolint:govet + } + return false + }) + + if c.strict { // check for unused imports + for _, imp := range mod.Imports { + path := imp.Path.Value.(Ref) + if FutureRootDocument.Equal(path[0]) || RegoRootDocument.Equal(path[0]) { + continue // ignore future and rego imports + } + + for v, u := range globals { + if v.Equal(imp.Name()) && !u.used { + c.err(NewError(CompileErr, imp.Location, "%s unused", imp.String())) + } + } + } + } + } + + if c.moduleLoader != nil { + + parsed, err := c.moduleLoader(c.Modules) + if err != nil { + c.err(NewError(CompileErr, nil, err.Error())) //nolint:govet + return + } + + if len(parsed) == 0 { + return + } + + for id, module := range parsed { + c.Modules[id] = module.Copy() + c.sorted = append(c.sorted, id) + if c.parsedModules != nil { + c.parsedModules[id] = module + } + } + + sort.Strings(c.sorted) + c.resolveAllRefs() + } +} + +func (c *Compiler) removeImports() { + c.imports = make(map[string][]*Import, len(c.Modules)) + for name := range c.Modules { + c.imports[name] = c.Modules[name].Imports + c.Modules[name].Imports = nil + } +} + +func (c *Compiler) initLocalVarGen() { + c.localvargen = newLocalVarGeneratorForModuleSet(c.sorted, c.Modules) +} + +func (c *Compiler) rewriteComprehensionTerms() { + f := newEqualityFactory(c.localvargen) + for _, name := range c.sorted { + mod := c.Modules[name] + _, _ = rewriteComprehensionTerms(f, mod) // ignore error + } +} + +func (c *Compiler) rewriteExprTerms() { + for _, name := range c.sorted { + mod := c.Modules[name] + WalkRules(mod, func(rule *Rule) bool { + rewriteExprTermsInHead(c.localvargen, rule) + rule.Body = rewriteExprTermsInBody(c.localvargen, rule.Body) + return false + }) + } +} + +func (c *Compiler) rewriteRuleHeadRefs() { + f := newEqualityFactory(c.localvargen) + for _, name := range c.sorted { + WalkRules(c.Modules[name], func(rule *Rule) bool { + + ref := rule.Head.Ref() + // NOTE(sr): We're backfilling Refs here -- all parser code paths would have them, but + // it's possible to construct Module{} instances from Golang code, so we need + // to accommodate for that, too. + if len(rule.Head.Reference) == 0 { + rule.Head.Reference = ref + } + + cannotSpeakStringPrefixRefs := true + cannotSpeakGeneralRefs := true + for _, f := range c.capabilities.Features { + switch f { + case FeatureRefHeadStringPrefixes: + cannotSpeakStringPrefixRefs = false + case FeatureRefHeads: + cannotSpeakGeneralRefs = false + case FeatureRegoV1: + cannotSpeakStringPrefixRefs = false + cannotSpeakGeneralRefs = false + } + } + + if cannotSpeakStringPrefixRefs && cannotSpeakGeneralRefs && rule.Head.Name == "" { + c.err(NewError(CompileErr, rule.Loc(), "rule heads with refs are not supported: %v", rule.Head.Reference)) + return true + } + + for i := 1; i < len(ref); i++ { + if cannotSpeakGeneralRefs && (rule.Head.RuleKind() == MultiValue || i != len(ref)-1) { // last + if _, ok := ref[i].Value.(String); !ok { + c.err(NewError(TypeErr, rule.Loc(), "rule heads with general refs (containing variables) are not supported: %v", rule.Head.Reference)) + continue + } + } + + // Rewrite so that any non-scalar elements in the rule's ref are vars: + // p.q.r[y.z] { ... } => p.q.r[__local0__] { __local0__ = y.z } + // p.q[a.b][c.d] { ... } => p.q[__local0__] { __local0__ = a.b; __local1__ = c.d } + // because that's what the RuleTree knows how to deal with. + if _, ok := ref[i].Value.(Var); !ok && !IsScalar(ref[i].Value) { + expr := f.Generate(ref[i]) + if i == len(ref)-1 && rule.Head.Key.Equal(ref[i]) { + rule.Head.Key = expr.Operand(0) + } + rule.Head.Reference[i] = expr.Operand(0) + rule.Body.Append(expr) + } + } + + return true + }) + } +} + +func (c *Compiler) checkVoidCalls() { + for _, name := range c.sorted { + mod := c.Modules[name] + for _, err := range checkVoidCalls(c.TypeEnv, mod) { + c.err(err) + } + } +} + +func (c *Compiler) rewritePrintCalls() { + var modified bool + if !c.enablePrintStatements { + for _, name := range c.sorted { + if erasePrintCalls(c.Modules[name]) { + modified = true + } + } + } else { + for _, name := range c.sorted { + mod := c.Modules[name] + WalkRules(mod, func(r *Rule) bool { + safe := r.Head.Args.Vars() + safe.Update(ReservedVars) + vis := func(b Body) bool { + modrec, errs := rewritePrintCalls(c.localvargen, c.GetArity, safe, b) + if modrec { + modified = true + } + for _, err := range errs { + c.err(err) + } + return false + } + WalkBodies(r.Head, vis) + WalkBodies(r.Body, vis) + return false + }) + } + } + if modified { + c.Required.addBuiltinSorted(Print) + } +} + +// checkVoidCalls returns errors for any expressions that treat void function +// calls as values. The only void functions in Rego are specific built-ins like +// print(). +func checkVoidCalls(env *TypeEnv, x any) Errors { + var errs Errors + WalkTerms(x, func(x *Term) bool { + if call, ok := x.Value.(Call); ok { + if tpe, ok := env.Get(call[0]).(*types.Function); ok && tpe.Result() == nil { + errs = append(errs, NewError(TypeErr, x.Loc(), "%v used as value", call)) + } + } + return false + }) + return errs +} + +// rewritePrintCalls will rewrite the body so that print operands are captured +// in local variables and their evaluation occurs within a comprehension. +// Wrapping the terms inside of a comprehension ensures that undefined values do +// not short-circuit evaluation. +// +// For example, given the following print statement: +// +// print("the value of x is:", input.x) +// +// The expression would be rewritten to: +// +// print({__local0__ | __local0__ = "the value of x is:"}, {__local1__ | __local1__ = input.x}) +func rewritePrintCalls(gen *localVarGenerator, getArity func(Ref) int, globals VarSet, body Body) (bool, Errors) { + + var errs Errors + var modified bool + + // Visit comprehension bodies recursively to ensure print statements inside + // those bodies only close over variables that are safe. + for i := range body { + if ContainsClosures(body[i]) { + safe := outputVarsForBody(body[:i], getArity, globals) + safe.Update(globals) + WalkClosures(body[i], func(x any) bool { + var modrec bool + var errsrec Errors + switch x := x.(type) { + case *SetComprehension: + modrec, errsrec = rewritePrintCalls(gen, getArity, safe, x.Body) + case *ArrayComprehension: + modrec, errsrec = rewritePrintCalls(gen, getArity, safe, x.Body) + case *ObjectComprehension: + modrec, errsrec = rewritePrintCalls(gen, getArity, safe, x.Body) + case *Every: + safe.Update(x.KeyValueVars()) + modrec, errsrec = rewritePrintCalls(gen, getArity, safe, x.Body) + } + if modrec { + modified = true + } + errs = append(errs, errsrec...) + return true + }) + if len(errs) > 0 { + return false, errs + } + } + } + + for i := range body { + + if !isPrintCall(body[i]) { + continue + } + + modified = true + + var errs Errors + safe := outputVarsForBody(body[:i], getArity, globals) + safe.Update(globals) + args := body[i].Operands() + + var vis *VarVisitor + for j := range args { + vis = vis.ClearOrNew().WithParams(SafetyCheckVisitorParams) + vis.Walk(args[j]) + vars := vis.Vars() + if vars.DiffCount(safe) > 0 { + unsafe := vars.Diff(safe) + for _, v := range unsafe.Sorted() { + errs = append(errs, NewError(CompileErr, args[j].Loc(), "var %v is undeclared", v)) + } + } + } + + if len(errs) > 0 { + return false, errs + } + + terms := make([]*Term, 0, len(args)) + + for j := range args { + x := NewTerm(gen.Generate()).SetLocation(args[j].Loc()) + capture := Equality.Expr(x, args[j]).SetLocation(args[j].Loc()) + terms = append(terms, SetComprehensionTerm(x, NewBody(capture)).SetLocation(args[j].Loc())) + } + + body.Set(NewExpr([]*Term{ + NewTerm(InternalPrint.Ref()).SetLocation(body[i].Loc()), + ArrayTerm(terms...).SetLocation(body[i].Loc()), + }).SetLocation(body[i].Loc()), i) + } + + return modified, nil +} + +func erasePrintCalls(node any) bool { + var modified bool + NewGenericVisitor(func(x any) bool { + var modrec bool + switch x := x.(type) { + case *Rule: + modrec, x.Body = erasePrintCallsInBody(x.Body) + case *ArrayComprehension: + modrec, x.Body = erasePrintCallsInBody(x.Body) + case *SetComprehension: + modrec, x.Body = erasePrintCallsInBody(x.Body) + case *ObjectComprehension: + modrec, x.Body = erasePrintCallsInBody(x.Body) + case *Every: + modrec, x.Body = erasePrintCallsInBody(x.Body) + } + if modrec { + modified = true + } + return false + }).Walk(node) + return modified +} + +func erasePrintCallsInBody(x Body) (bool, Body) { + + if !containsPrintCall(x) { + return false, x + } + + var cpy Body + + for i := range x { + + // Recursively visit any comprehensions contained in this expression. + erasePrintCalls(x[i]) + + if !isPrintCall(x[i]) { + cpy.Append(x[i]) + } + } + + if len(cpy) == 0 { + term := BooleanTerm(true).SetLocation(x.Loc()) + expr := NewExpr(term).SetLocation(x.Loc()) + cpy.Append(expr) + } + + return true, cpy +} + +func containsPrintCall(x any) bool { + var found bool + WalkExprs(x, func(expr *Expr) bool { + if !found { + if isPrintCall(expr) { + found = true + } + } + return found + }) + return found +} + +var printRef = Print.Ref() + +func isPrintCall(x *Expr) bool { + return x.IsCall() && x.Operator().Equal(printRef) +} + +// rewriteRefsInHead will rewrite rules so that the head does not contain any +// terms that require evaluation (e.g., refs or comprehensions). If the key or +// value contains one or more of these terms, the key or value will be moved +// into the body and assigned to a new variable. The new variable will replace +// the key or value in the head. +// +// For instance, given the following rule: +// +// p[{"foo": data.foo[i]}] { i < 100 } +// +// The rule would be re-written as: +// +// p[__local0__] { i < 100; __local0__ = {"foo": data.foo[i]} } +func (c *Compiler) rewriteRefsInHead() { + f := newEqualityFactory(c.localvargen) + for _, name := range c.sorted { + mod := c.Modules[name] + WalkRules(mod, func(rule *Rule) bool { + if requiresEval(rule.Head.Key) { + expr := f.Generate(rule.Head.Key) + rule.Head.Key = expr.Operand(0) + rule.Body.Append(expr) + } + if requiresEval(rule.Head.Value) { + expr := f.Generate(rule.Head.Value) + rule.Head.Value = expr.Operand(0) + rule.Body.Append(expr) + } + for i := 0; i < len(rule.Head.Args); i++ { + if requiresEval(rule.Head.Args[i]) { + expr := f.Generate(rule.Head.Args[i]) + rule.Head.Args[i] = expr.Operand(0) + rule.Body.Append(expr) + } + } + return false + }) + } +} + +func (c *Compiler) rewriteEquals() { + modified := false + for _, name := range c.sorted { + modified = rewriteEquals(c.Modules[name]) || modified + } + if modified { + c.Required.addBuiltinSorted(Equal) + } +} + +func (c *Compiler) rewriteDynamicTerms() { + f := newEqualityFactory(c.localvargen) + for _, name := range c.sorted { + WalkRules(c.Modules[name], func(rule *Rule) bool { + rule.Body = rewriteDynamics(f, rule.Body) + return false + }) + } +} + +// 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 + for _, name := range c.sorted { + mod := c.Modules[name] + WalkExprs(mod, func(expr *Expr) bool { + if isRegoMetadataChainCall(expr) || isRegoMetadataRuleCall(expr) { + regoMetadataCalled = true + } + return regoMetadataCalled + }) + + if regoMetadataCalled { + break + } + } + + if regoMetadataCalled { + // NOTE: Possible optimization: only parse annotations for modules on the path of rego.metadata-calling module + for _, name := range c.sorted { + mod := c.Modules[name] + + if len(mod.Annotations) == 0 { + var errs Errors + mod.Annotations, errs = parseAnnotations(mod.Comments) + errs = append(errs, attachAnnotationsNodes(mod)...) + for _, err := range errs { + c.err(err) + } + + attachRuleAnnotations(mod) + } + } + } +} + +func (c *Compiler) rewriteRegoMetadataCalls() { + eqFactory := newEqualityFactory(c.localvargen) + + _, chainFuncAllowed := c.builtins[RegoMetadataChain.Name] + _, ruleFuncAllowed := c.builtins[RegoMetadataRule.Name] + + for _, name := range c.sorted { + mod := c.Modules[name] + + WalkRules(mod, func(rule *Rule) bool { + var firstChainCall *Expr + var firstRuleCall *Expr + + WalkExprs(rule, func(expr *Expr) bool { + if chainFuncAllowed && firstChainCall == nil && isRegoMetadataChainCall(expr) { + firstChainCall = expr + } else if ruleFuncAllowed && firstRuleCall == nil && isRegoMetadataRuleCall(expr) { + firstRuleCall = expr + } + return firstChainCall != nil && firstRuleCall != nil + }) + + chainCalled := firstChainCall != nil + ruleCalled := firstRuleCall != nil + + if chainCalled || ruleCalled { + body := make(Body, 0, len(rule.Body)+2) + + var metadataChainVar Var + if chainCalled { + // Create and inject metadata chain for rule + + chain, err := createMetadataChain(c.annotationSet.Chain(rule)) + if err != nil { + c.err(err) + return false + } + + chain.Location = firstChainCall.Location + eq := eqFactory.Generate(chain) + metadataChainVar = eq.Operands()[0].Value.(Var) + body.Append(eq) + } + + var metadataRuleVar Var + if ruleCalled { + // Create and inject metadata for rule + + var metadataRuleTerm *Term + + a := getPrimaryRuleAnnotations(c.annotationSet, rule) + if a != nil { + annotObj, err := a.toObject() + if err != nil { + c.err(err) + return false + } + metadataRuleTerm = NewTerm(*annotObj) + } else { + // If rule has no annotations, assign an empty object + metadataRuleTerm = ObjectTerm() + } + + metadataRuleTerm.Location = firstRuleCall.Location + eq := eqFactory.Generate(metadataRuleTerm) + metadataRuleVar = eq.Operands()[0].Value.(Var) + body.Append(eq) + } + + for _, expr := range rule.Body { + body.Append(expr) + } + rule.Body = body + + vis := func(b Body) bool { + for _, err := range rewriteRegoMetadataCalls(&metadataChainVar, &metadataRuleVar, b, &c.RewrittenVars) { + c.err(err) + } + return false + } + WalkBodies(rule.Head, vis) + WalkBodies(rule.Body, vis) + } + + return false + }) + } +} + +func getPrimaryRuleAnnotations(as *AnnotationSet, rule *Rule) *Annotations { + annots := as.GetRuleScope(rule) + + if len(annots) == 0 { + return nil + } + + // Sort by annotation location; chain must start with annotations declared closest to rule, then going outward + slices.SortStableFunc(annots, func(a, b *Annotations) int { + return -a.Location.Compare(b.Location) + }) + + return annots[0] +} + +func rewriteRegoMetadataCalls(metadataChainVar *Var, metadataRuleVar *Var, body Body, rewrittenVars *map[Var]Var) Errors { + var errs Errors + + WalkClosures(body, func(x any) bool { + switch x := x.(type) { + case *ArrayComprehension: + errs = rewriteRegoMetadataCalls(metadataChainVar, metadataRuleVar, x.Body, rewrittenVars) + case *SetComprehension: + errs = rewriteRegoMetadataCalls(metadataChainVar, metadataRuleVar, x.Body, rewrittenVars) + case *ObjectComprehension: + errs = rewriteRegoMetadataCalls(metadataChainVar, metadataRuleVar, x.Body, rewrittenVars) + case *Every: + errs = rewriteRegoMetadataCalls(metadataChainVar, metadataRuleVar, x.Body, rewrittenVars) + } + return true + }) + + for i := range body { + expr := body[i] + var metadataVar Var + + if metadataChainVar != nil && isRegoMetadataChainCall(expr) { + metadataVar = *metadataChainVar + } else if metadataRuleVar != nil && isRegoMetadataRuleCall(expr) { + metadataVar = *metadataRuleVar + } else { + continue + } + + // NOTE(johanfylling): An alternative strategy would be to walk the body and replace all operands[0] + // usages with *metadataChainVar + operands := expr.Operands() + var newExpr *Expr + if len(operands) > 0 { // There is an output var to rewrite + rewrittenVar := operands[0] + newExpr = Equality.Expr(rewrittenVar, NewTerm(metadataVar)) + } else { // No output var, just rewrite expr to metadataVar + newExpr = NewExpr(NewTerm(metadataVar)) + } + + newExpr.Generated = true + newExpr.Location = expr.Location + body.Set(newExpr, i) + } + + return errs +} + +var regoMetadataChainRef = RegoMetadataChain.Ref() +var regoMetadataRuleRef = RegoMetadataRule.Ref() + +func isRegoMetadataChainCall(x *Expr) bool { + return x.IsCall() && x.Operator().Equal(regoMetadataChainRef) +} + +func isRegoMetadataRuleCall(x *Expr) bool { + return x.IsCall() && x.Operator().Equal(regoMetadataRuleRef) +} + +func createMetadataChain(chain []*AnnotationsRef) (*Term, *Error) { + + metaArray := NewArray() + for _, link := range chain { + // Dropping leading 'data' element of path + p := link.Path[1:].toArray() + obj := NewObject(Item(InternedTerm("path"), NewTerm(p))) + if link.Annotations != nil { + annotObj, err := link.Annotations.toObject() + if err != nil { + return nil, err + } + obj.Insert(InternedTerm("annotations"), NewTerm(*annotObj)) + } + metaArray = metaArray.Append(NewTerm(obj)) + } + + return NewTerm(metaArray), nil +} + +func (c *Compiler) rewriteLocalVars() { + var assignment bool + + args := NewVarVisitor() + argsStack := newLocalDeclaredVars() + + for _, name := range c.sorted { + mod := c.Modules[name] + gen := c.localvargen + + WalkRules(mod, func(rule *Rule) bool { + args.Clear() + argsStack.Clear() + + if c.strict && len(rule.Head.Args) > 0 { + args.WalkArgs(rule.Head.Args) + } + unusedArgs := args.Vars() + + c.rewriteLocalArgVars(gen, argsStack, rule) + + // Rewrite local vars in each else-branch of the rule. + // Note: this is done instead of a walk so that we can capture any unused function arguments + // across else-branches. + for rule := rule; rule != nil; rule = rule.Else { + stack, errs := c.rewriteLocalVarsInRule(rule, unusedArgs, argsStack, gen) + if stack.assignment { + assignment = true + } + + for arg := range unusedArgs { + if stack.Count(arg) > 1 { + delete(unusedArgs, arg) + } + } + + for _, err := range errs { + c.err(err) + } + } + + if c.strict { + // Report an error for each unused function argument + for arg := range unusedArgs { + if !arg.IsWildcard() { + c.err(NewError(CompileErr, rule.Head.Location, "unused argument %v. (hint: use _ (wildcard variable) instead)", arg)) + } + } + } + + return true + }) + } + + if assignment { + c.Required.addBuiltinSorted(Assign) + } +} + +func (c *Compiler) rewriteLocalVarsInRule(rule *Rule, unusedArgs VarSet, argsStack *localDeclaredVars, gen *localVarGenerator) (*localDeclaredVars, Errors) { + onlyScalars := !headMayHaveVars(rule.Head) + + var used VarSet + + if !onlyScalars { + // Rewrite assignments contained in head of rule. Assignments can + // occur in rule head if they're inside a comprehension. Note, + // assigned vars in comprehensions in the head will be rewritten + // first to preserve scoping rules. For example: + // + // p = [x | x := 1] { x := 2 } becomes p = [__local0__ | __local0__ = 1] { __local1__ = 2 } + // + // This behaviour is consistent scoping inside the body. For example: + // + // p = xs { x := 2; xs = [x | x := 1] } becomes p = xs { __local0__ = 2; xs = [__local1__ | __local1__ = 1] } + nestedXform := &rewriteNestedHeadVarLocalTransform{ + gen: gen, + RewrittenVars: c.RewrittenVars, + strict: c.strict, + } + + NewGenericVisitor(nestedXform.Visit).Walk(rule.Head) + + for _, err := range nestedXform.errs { + c.err(err) + } + + // Rewrite assignments in body. + used = NewVarSet() + + for _, t := range rule.Head.Ref()[1:] { + used.Update(t.Vars()) + } + + if rule.Head.Key != nil { + used.Update(rule.Head.Key.Vars()) + } + + if rule.Head.Value != nil { + valueVars := rule.Head.Value.Vars() + used.Update(valueVars) + for arg := range unusedArgs { + if valueVars.Contains(arg) { + delete(unusedArgs, arg) + } + } + } + } + + stack := argsStack.Copy() + + body, declared, errs := rewriteLocalVars(gen, stack, used, rule.Body, c.strict) + + // For rewritten vars use the collection of all variables that + // were in the stack at some point in time. + maps.Copy(c.RewrittenVars, stack.rewritten) + + rule.Body = body + + if onlyScalars { + return stack, errs + } + + // Rewrite vars in head that refer to locally declared vars in the body. + localXform := rewriteHeadVarLocalTransform{declared: declared} + + for i := range rule.Head.Args { + rule.Head.Args[i], _ = transformTerm(localXform, rule.Head.Args[i]) + } + + for i := 1; i < len(rule.Head.Ref()); i++ { + rule.Head.Reference[i], _ = transformTerm(localXform, rule.Head.Ref()[i]) + } + if rule.Head.Key != nil { + rule.Head.Key, _ = transformTerm(localXform, rule.Head.Key) + } + + if rule.Head.Value != nil { + rule.Head.Value, _ = transformTerm(localXform, rule.Head.Value) + } + return stack, errs +} + +func headMayHaveVars(head *Head) bool { + if head == nil { + return false + } + for i := range head.Args { + if !IsScalar(head.Args[i].Value) { + return true + } + } + if head.Key != nil && !IsScalar(head.Key.Value) { + return true + } + if head.Value != nil && !IsScalar(head.Value.Value) { + return true + } + ref := head.Ref()[1:] + for i := range ref { + if !IsScalar(ref[i].Value) { + return true + } + } + return false +} + +type rewriteNestedHeadVarLocalTransform struct { + gen *localVarGenerator + errs Errors + RewrittenVars map[Var]Var + strict bool +} + +func (xform *rewriteNestedHeadVarLocalTransform) Visit(x any) bool { + if term, ok := x.(*Term); ok { + stop := false + stack := newLocalDeclaredVars() + + switch x := term.Value.(type) { + case *object: + cpy, _ := x.Map(func(k, v *Term) (*Term, *Term, error) { + kcpy := k.Copy() + NewGenericVisitor(xform.Visit).Walk(kcpy) + vcpy := v.Copy() + NewGenericVisitor(xform.Visit).Walk(vcpy) + return kcpy, vcpy, nil + }) + term.Value = cpy + stop = true + case *set: + cpy, _ := x.Map(func(v *Term) (*Term, error) { + vcpy := v.Copy() + NewGenericVisitor(xform.Visit).Walk(vcpy) + return vcpy, nil + }) + term.Value = cpy + stop = true + case *ArrayComprehension: + xform.errs = rewriteDeclaredVarsInArrayComprehension(xform.gen, stack, x, xform.errs, xform.strict) + stop = true + case *SetComprehension: + xform.errs = rewriteDeclaredVarsInSetComprehension(xform.gen, stack, x, xform.errs, xform.strict) + stop = true + case *ObjectComprehension: + xform.errs = rewriteDeclaredVarsInObjectComprehension(xform.gen, stack, x, xform.errs, xform.strict) + stop = true + } + + maps.Copy(xform.RewrittenVars, stack.rewritten) + + return stop + } + + return false +} + +type rewriteHeadVarLocalTransform struct { + declared map[Var]Var +} + +func (xform rewriteHeadVarLocalTransform) Transform(x any) (any, error) { + if v, ok := x.(Var); ok { + if gv, ok := xform.declared[v]; ok { + return gv, nil + } + } + return x, nil +} + +func (c *Compiler) rewriteLocalArgVars(gen *localVarGenerator, stack *localDeclaredVars, rule *Rule) { + + vis := &ruleArgLocalRewriter{ + stack: stack, + gen: gen, + } + + for i := range rule.Head.Args { + Walk(vis, rule.Head.Args[i]) + } + + for i := range vis.errs { + c.err(vis.errs[i]) + } +} + +type ruleArgLocalRewriter struct { + stack *localDeclaredVars + gen *localVarGenerator + errs []*Error +} + +func (vis *ruleArgLocalRewriter) Visit(x any) Visitor { + + t, ok := x.(*Term) + if !ok { + return vis + } + + switch v := t.Value.(type) { + case Var: + gv, ok := vis.stack.Declared(v) + if ok { + vis.stack.Seen(v) + } else { + gv = vis.gen.Generate() + vis.stack.Insert(v, gv, argVar) + } + t.Value = gv + return nil + case *object: + if cpy, err := v.Map(func(k, v *Term) (*Term, *Term, error) { + vcpy := v.Copy() + Walk(vis, vcpy) + return k, vcpy, nil + }); err != nil { + vis.errs = append(vis.errs, NewError(CompileErr, t.Location, err.Error())) //nolint:govet + } else { + t.Value = cpy + } + return nil + case Null, Boolean, Number, String, *ArrayComprehension, *SetComprehension, *ObjectComprehension, Set: + // Scalars are no-ops. Comprehensions are handled above. Sets must not + // contain variables. + return nil + case Call: + vis.errs = append(vis.errs, NewError(CompileErr, t.Location, "rule arguments cannot contain calls")) + return nil + default: + // Recurse on refs and arrays. Any embedded + // variables can be rewritten. + return vis + } +} + +func (c *Compiler) rewriteWithModifiers() { + f := newEqualityFactory(c.localvargen) + for _, name := range c.sorted { + mod := c.Modules[name] + t := NewGenericTransformer(func(x any) (any, error) { + body, ok := x.(Body) + if !ok { + return x, nil + } + body, err := rewriteWithModifiersInBody(c, c.unsafeBuiltinsMap, f, body) + if err != nil { + c.err(err) + } + + return body, nil + }) + _, _ = Transform(t, mod) // ignore error + } +} + +func (c *Compiler) setModuleTree() { + c.ModuleTree = NewModuleTree(c.Modules) +} + +func (c *Compiler) setRuleTree() { + c.RuleTree = NewRuleTree(c.ModuleTree) +} + +func (c *Compiler) setGraph() { + list := func(r Ref) []*Rule { + return c.GetRulesDynamicWithOpts(r, RulesOptions{IncludeHiddenModules: true}) + } + c.Graph = NewGraph(c.Modules, list) +} + +type queryCompiler struct { + compiler *Compiler + qctx *QueryContext + typeEnv *TypeEnv + rewritten map[Var]Var + after map[string][]QueryCompilerStageDefinition + unsafeBuiltins map[string]struct{} + comprehensionIndices map[*Term]*ComprehensionIndex + enablePrintStatements bool +} + +func newQueryCompiler(compiler *Compiler) QueryCompiler { + qc := &queryCompiler{ + compiler: compiler, + qctx: nil, + after: map[string][]QueryCompilerStageDefinition{}, + comprehensionIndices: map[*Term]*ComprehensionIndex{}, + } + return qc +} + +func (qc *queryCompiler) WithStrict(strict bool) QueryCompiler { + qc.compiler.WithStrict(strict) + return qc +} + +func (qc *queryCompiler) WithEnablePrintStatements(yes bool) QueryCompiler { + qc.enablePrintStatements = yes + return qc +} + +func (qc *queryCompiler) WithContext(qctx *QueryContext) QueryCompiler { + qc.qctx = qctx + return qc +} + +func (qc *queryCompiler) WithStageAfter(after string, stage QueryCompilerStageDefinition) QueryCompiler { + qc.after[after] = append(qc.after[after], stage) + return qc +} + +func (qc *queryCompiler) WithUnsafeBuiltins(unsafe map[string]struct{}) QueryCompiler { + qc.unsafeBuiltins = unsafe + return qc +} + +func (qc *queryCompiler) RewrittenVars() map[Var]Var { + return qc.rewritten +} + +func (qc *queryCompiler) ComprehensionIndex(term *Term) *ComprehensionIndex { + if result, ok := qc.comprehensionIndices[term]; ok { + return result + } else if result, ok := qc.compiler.comprehensionIndices[term]; ok { + return result + } + return nil +} + +func (qc *queryCompiler) runStage(metricName string, qctx *QueryContext, query Body, s func(*QueryContext, Body) (Body, error)) (Body, error) { + if qc.compiler.metrics != nil { + qc.compiler.metrics.Timer(metricName).Start() + defer qc.compiler.metrics.Timer(metricName).Stop() + } + return s(qctx, query) +} + +func (qc *queryCompiler) runStageAfter(metricName string, query Body, s QueryCompilerStage) (Body, error) { + if qc.compiler.metrics != nil { + qc.compiler.metrics.Timer(metricName).Start() + defer qc.compiler.metrics.Timer(metricName).Stop() + } + return s(qc, query) +} + +type queryStage = struct { + name string + metricName string + f func(*QueryContext, Body) (Body, error) +} + +func (qc *queryCompiler) Compile(query Body) (Body, error) { + if len(query) == 0 { + return nil, Errors{NewError(CompileErr, nil, "empty query cannot be compiled")} + } + + query = query.Copy() + + stages := []queryStage{ + {"CheckKeywordOverrides", "query_compile_stage_check_keyword_overrides", qc.checkKeywordOverrides}, + {"ResolveRefs", "query_compile_stage_resolve_refs", qc.resolveRefs}, + {"RewriteLocalVars", "query_compile_stage_rewrite_local_vars", qc.rewriteLocalVars}, + {"CheckVoidCalls", "query_compile_stage_check_void_calls", qc.checkVoidCalls}, + {"RewritePrintCalls", "query_compile_stage_rewrite_print_calls", qc.rewritePrintCalls}, + {"RewriteExprTerms", "query_compile_stage_rewrite_expr_terms", qc.rewriteExprTerms}, + {"RewriteComprehensionTerms", "query_compile_stage_rewrite_comprehension_terms", qc.rewriteComprehensionTerms}, + {"RewriteWithValues", "query_compile_stage_rewrite_with_values", qc.rewriteWithModifiers}, + {"CheckUndefinedFuncs", "query_compile_stage_check_undefined_funcs", qc.checkUndefinedFuncs}, + {"CheckSafety", "query_compile_stage_check_safety", qc.checkSafety}, + {"RewriteDynamicTerms", "query_compile_stage_rewrite_dynamic_terms", qc.rewriteDynamicTerms}, + {"CheckTypes", "query_compile_stage_check_types", qc.checkTypes}, + {"CheckUnsafeBuiltins", "query_compile_stage_check_unsafe_builtins", qc.checkUnsafeBuiltins}, + {"CheckDeprecatedBuiltins", "query_compile_stage_check_deprecated_builtins", qc.checkDeprecatedBuiltins}, + } + if qc.compiler.evalMode == EvalModeTopdown { + stages = append(stages, queryStage{"BuildComprehensionIndex", "query_compile_stage_build_comprehension_index", qc.buildComprehensionIndices}) + } + + qctx := qc.qctx.Copy() + + for _, s := range stages { + var err error + query, err = qc.runStage(s.metricName, qctx, query, s.f) + if err != nil { + return nil, qc.applyErrorLimit(err) + } + for _, s := range qc.after[s.name] { + query, err = qc.runStageAfter(s.MetricName, query, s.Stage) + if err != nil { + return nil, qc.applyErrorLimit(err) + } + } + } + + return query, nil +} + +func (qc *queryCompiler) TypeEnv() *TypeEnv { + return qc.typeEnv +} + +func (qc *queryCompiler) applyErrorLimit(err error) error { + var errs Errors + if errors.As(err, &errs) { + if qc.compiler.maxErrs > 0 && len(errs) > qc.compiler.maxErrs { + err = append(errs[:qc.compiler.maxErrs], errLimitReached) + } + } + return err +} + +func (qc *queryCompiler) checkKeywordOverrides(_ *QueryContext, body Body) (Body, error) { + if qc.compiler.strict { + if errs := checkRootDocumentOverrides(body); len(errs) > 0 { + return nil, errs + } + } + return body, nil +} + +func (qc *queryCompiler) resolveRefs(qctx *QueryContext, body Body) (Body, error) { + + var globals map[Var]*usedRef + + if qctx != nil { + pkg := qctx.Package + // Query compiler ought to generate a package if one was not provided and one or more imports were provided. + // The generated package name could even be an empty string to avoid conflicts (it doesn't have to be valid syntactically) + if pkg == nil && len(qctx.Imports) > 0 { + pkg = &Package{Path: RefTerm(VarTerm("")).Value.(Ref)} + } + if pkg != nil { + var ruleExports []Ref + rules := qc.compiler.getExports() + if exist, ok := rules.Get(pkg.Path); ok { + ruleExports = exist + } + + globals = getGlobals(qctx.Package, ruleExports, qctx.Imports) + qctx.Imports = nil + } + } + + ignore := &declaredVarStack{declaredVars(body)} + + return resolveRefsInBody(globals, ignore, body), nil +} + +func (*queryCompiler) rewriteComprehensionTerms(_ *QueryContext, body Body) (Body, error) { + gen := newLocalVarGenerator("q", body) + f := newEqualityFactory(gen) + node, err := rewriteComprehensionTerms(f, body) + if err != nil { + return nil, err + } + return node.(Body), nil +} + +func (*queryCompiler) rewriteDynamicTerms(_ *QueryContext, body Body) (Body, error) { + gen := newLocalVarGenerator("q", body) + f := newEqualityFactory(gen) + return rewriteDynamics(f, body), nil +} + +func (*queryCompiler) rewriteExprTerms(_ *QueryContext, body Body) (Body, error) { + gen := newLocalVarGenerator("q", body) + return rewriteExprTermsInBody(gen, body), nil +} + +func (qc *queryCompiler) rewriteLocalVars(_ *QueryContext, body Body) (Body, error) { + gen := newLocalVarGenerator("q", body) + stack := newLocalDeclaredVars() + body, _, err := rewriteLocalVars(gen, stack, nil, body, qc.compiler.strict) + if len(err) != 0 { + return nil, err + } + + // The vars returned during the rewrite will include all seen vars, + // even if they're not declared with an assignment operation. We don't + // want to include these inside the rewritten set though. + qc.rewritten = maps.Clone(stack.rewritten) + + return body, nil +} + +func (qc *queryCompiler) rewritePrintCalls(_ *QueryContext, body Body) (Body, error) { + if !qc.enablePrintStatements { + _, cpy := erasePrintCallsInBody(body) + return cpy, nil + } + gen := newLocalVarGenerator("q", body) + if _, errs := rewritePrintCalls(gen, qc.compiler.GetArity, ReservedVars, body); len(errs) > 0 { + return nil, errs + } + return body, nil +} + +func (qc *queryCompiler) checkVoidCalls(_ *QueryContext, body Body) (Body, error) { + if errs := checkVoidCalls(qc.compiler.TypeEnv, body); len(errs) > 0 { + return nil, errs + } + return body, nil +} + +func (qc *queryCompiler) checkUndefinedFuncs(_ *QueryContext, body Body) (Body, error) { + if errs := checkUndefinedFuncs(qc.compiler.TypeEnv, body, qc.compiler.GetArity, qc.rewritten); len(errs) > 0 { + return nil, errs + } + return body, nil +} + +func (qc *queryCompiler) checkSafety(_ *QueryContext, body Body) (Body, error) { + safe := ReservedVars.Copy() + reordered, unsafe := reorderBodyForSafety(qc.compiler.builtins, qc.compiler.GetArity, safe, body) + if errs := safetyErrorSlice(unsafe, qc.RewrittenVars()); len(errs) > 0 { + return nil, errs + } + return reordered, nil +} + +func (qc *queryCompiler) checkTypes(_ *QueryContext, body Body) (Body, error) { + var errs Errors + checker := newTypeChecker(). + WithSchemaSet(qc.compiler.schemaSet). + WithInputType(qc.compiler.inputType). + WithVarRewriter(rewriteVarsInRef(qc.rewritten, qc.compiler.RewrittenVars)) + qc.typeEnv, errs = checker.CheckBody(qc.compiler.TypeEnv, body) + if len(errs) > 0 { + return nil, errs + } + + return body, nil +} + +func (qc *queryCompiler) checkUnsafeBuiltins(_ *QueryContext, body Body) (Body, error) { + errs := checkUnsafeBuiltins(qc.unsafeBuiltinsMap(), body) + if len(errs) > 0 { + return nil, errs + } + return body, nil +} + +func (qc *queryCompiler) unsafeBuiltinsMap() map[string]struct{} { + if qc.unsafeBuiltins != nil { + return qc.unsafeBuiltins + } + return qc.compiler.unsafeBuiltinsMap +} + +func (qc *queryCompiler) checkDeprecatedBuiltins(_ *QueryContext, body Body) (Body, error) { + if qc.compiler.strict { + errs := checkDeprecatedBuiltins(qc.compiler.deprecatedBuiltinsMap, body) + if len(errs) > 0 { + return nil, errs + } + } + return body, nil +} + +func (qc *queryCompiler) rewriteWithModifiers(_ *QueryContext, body Body) (Body, error) { + f := newEqualityFactory(newLocalVarGenerator("q", body)) + body, err := rewriteWithModifiersInBody(qc.compiler, qc.unsafeBuiltinsMap(), f, body) + if err != nil { + return nil, Errors{err} + } + return body, nil +} + +func (qc *queryCompiler) buildComprehensionIndices(_ *QueryContext, body Body) (Body, error) { + // NOTE(tsandall): The query compiler does not have a metrics object so we + // cannot record index metrics currently. + _ = buildComprehensionIndices(qc.compiler.debug, qc.compiler.GetArity, ReservedVars, qc.RewrittenVars(), body, qc.comprehensionIndices) + return body, nil +} + +// ComprehensionIndex specifies how the comprehension term can be indexed. The keys +// tell the evaluator what variables to use for indexing. In the future, the index +// could be expanded with more information that would allow the evaluator to index +// a larger fragment of comprehensions (e.g., by closing over variables in the outer +// query.) +type ComprehensionIndex struct { + Term *Term + Keys []*Term +} + +func (ci *ComprehensionIndex) String() string { + if ci == nil { + return "" + } + return fmt.Sprintf("", NewArray(ci.Keys...)) +} + +func buildComprehensionIndices(dbg debug.Debug, arity func(Ref) int, candidates VarSet, rwVars map[Var]Var, node Body, result map[*Term]*ComprehensionIndex) uint64 { + var n uint64 + cpy := candidates.Copy() + WalkBodies(node, func(b Body) bool { + for _, expr := range b { + index := getComprehensionIndex(dbg, arity, cpy, rwVars, expr) + if index != nil { + result[index.Term] = index + n++ + } + // Any variables appearing in the expressions leading up to the comprehension + // are fair-game to be used as index keys. + cpy.Update(expr.Vars(VarVisitorParams{SkipClosures: true, SkipRefCallHead: true})) + } + return false + }) + return n +} + +func getComprehensionIndex(dbg debug.Debug, arity func(Ref) int, candidates VarSet, rwVars map[Var]Var, expr *Expr) *ComprehensionIndex { + + // Ignore everything except = expressions. Extract + // the comprehension term from the expression. + if !expr.IsEquality() || expr.Negated || len(expr.With) > 0 { + // No debug message, these are assumed to be known hinderances + // to comprehension indexing. + return nil + } + + var term *Term + + lhs, rhs := expr.Operand(0), expr.Operand(1) + + if _, ok := lhs.Value.(Var); ok && IsComprehension(rhs.Value) { + term = rhs + } else if _, ok := rhs.Value.(Var); ok && IsComprehension(lhs.Value) { + term = lhs + } + + if term == nil { + // no debug for this, it's the ordinary "nothing to do here" case + return nil + } + + // Ignore comprehensions that contain expressions that close over variables + // in the outer body if those variables are not also output variables in the + // comprehension body. In other words, ignore comprehensions that we cannot + // safely evaluate without bindings from the outer body. For example: + // + // x = [1] + // [true | data.y[z] = x] # safe to evaluate w/o outer body + // [true | data.y[z] = x[0]] # NOT safe to evaluate because 'x' would be unsafe. + // + // By identifying output variables in the body we also know what to index on by + // intersecting with candidate variables from the outer query. + // + // For example: + // + // x = data.foo[_] + // _ = [y | data.bar[y] = x] # index on 'x' + // + // This query goes from O(data.foo*data.bar) to O(data.foo+data.bar). + var body Body + + switch x := term.Value.(type) { + case *ArrayComprehension: + body = x.Body + case *SetComprehension: + body = x.Body + case *ObjectComprehension: + body = x.Body + } + + outputs := outputVarsForBody(body, arity, ReservedVars) + unsafe := body.Vars(SafetyCheckVisitorParams).Diff(outputs).Diff(ReservedVars) + + if len(unsafe) > 0 { + dbg.Printf("%s: comprehension index: unsafe vars: %v", expr.Location, unsafe) + return nil + } + + // Similarly, ignore comprehensions that contain references with output variables + // that intersect with the candidates. Indexing these comprehensions could worsen + // performance. + regressionVis := newComprehensionIndexRegressionCheckVisitor(candidates) + regressionVis.Walk(body) + if regressionVis.worse { + dbg.Printf("%s: comprehension index: output vars intersect candidates", expr.Location) + return nil + } + + // Check if any nested comprehensions close over candidates. If any intersection is found + // the comprehension cannot be cached because it would require closing over the candidates + // which the evaluator does not support today. + nestedVis := newComprehensionIndexNestedCandidateVisitor(candidates) + nestedVis.Walk(body) + if nestedVis.found { + dbg.Printf("%s: comprehension index: nested comprehensions close over candidates", expr.Location) + return nil + } + + // Make a sorted set of variable names that will serve as the index key set. + // Sort to ensure deterministic indexing. In future this could be relaxed + // if we can decide that one ordering is better than another. If the set is + // empty, there is no indexing to do. + indexVars := candidates.Intersect(outputs) + if len(indexVars) == 0 { + dbg.Printf("%s: comprehension index: no index vars", expr.Location) + return nil + } + + result := make([]*Term, 0, len(indexVars)) + + for v := range indexVars { + result = append(result, NewTerm(v)) + } + + slices.SortFunc(result, TermValueCompare) + + debugRes := make([]*Term, len(result)) + for i, r := range result { + if o, ok := rwVars[r.Value.(Var)]; ok { + debugRes[i] = NewTerm(o) + } else { + debugRes[i] = r + } + } + dbg.Printf("%s: comprehension index: built with keys: %v", expr.Location, debugRes) + return &ComprehensionIndex{Term: term, Keys: result} +} + +type comprehensionIndexRegressionCheckVisitor struct { + candidates VarSet + seen VarSet + worse bool +} + +// TODO(tsandall): Improve this so that users can either supply this list explicitly +// or the information is maintained on the built-in function declaration. What we really +// need to know is whether the built-in function allows callers to push down output +// values or not. It's unlikely that anything outside of OPA does this today so this +// solution is fine for now. +var comprehensionIndexBlacklist = map[string]int{ + WalkBuiltin.Name: len(WalkBuiltin.Decl.FuncArgs().Args), +} + +func newComprehensionIndexRegressionCheckVisitor(candidates VarSet) *comprehensionIndexRegressionCheckVisitor { + return &comprehensionIndexRegressionCheckVisitor{ + candidates: candidates, + seen: NewVarSet(), + } +} + +func (vis *comprehensionIndexRegressionCheckVisitor) Walk(x any) { + NewGenericVisitor(vis.visit).Walk(x) +} + +func (vis *comprehensionIndexRegressionCheckVisitor) visit(x any) bool { + if !vis.worse { + switch x := x.(type) { + case *Expr: + operands := x.Operands() + if pos := comprehensionIndexBlacklist[x.Operator().String()]; pos > 0 && pos < len(operands) { + vis.assertEmptyIntersection(operands[pos].Vars()) + } + case Ref: + vis.assertEmptyIntersection(x.OutputVars()) + case Var: + vis.seen.Add(x) + // Always skip comprehensions. We do not have to visit their bodies here. + case *ArrayComprehension, *SetComprehension, *ObjectComprehension: + return true + } + } + return vis.worse +} + +func (vis *comprehensionIndexRegressionCheckVisitor) assertEmptyIntersection(vs VarSet) { + for v := range vs { + if vis.candidates.Contains(v) && !vis.seen.Contains(v) { + vis.worse = true + return + } + } +} + +type comprehensionIndexNestedCandidateVisitor struct { + candidates VarSet + found bool +} + +func newComprehensionIndexNestedCandidateVisitor(candidates VarSet) *comprehensionIndexNestedCandidateVisitor { + return &comprehensionIndexNestedCandidateVisitor{ + candidates: candidates, + } +} + +func (vis *comprehensionIndexNestedCandidateVisitor) Walk(x any) { + NewGenericVisitor(vis.visit).Walk(x) +} + +func (vis *comprehensionIndexNestedCandidateVisitor) visit(x any) bool { + if vis.found { + return true + } + + if v, ok := x.(Value); ok && IsComprehension(v) { + varVis := NewVarVisitor().WithParams(VarVisitorParams{SkipRefHead: true}) + varVis.Walk(v) + vis.found = len(varVis.Vars().Intersect(vis.candidates)) > 0 + return true + } + + return false +} + +// ModuleTreeNode represents a node in the module tree. The module +// tree is keyed by the package path. +type ModuleTreeNode struct { + Key Value + Modules []*Module + Children map[Value]*ModuleTreeNode + Hide bool +} + +func (n *ModuleTreeNode) String() string { + var rules []string + for _, m := range n.Modules { + for _, r := range m.Rules { + rules = append(rules, r.Head.String()) + } + } + return fmt.Sprintf("", n.Key, n.Children, rules, n.Hide) +} + +// NewModuleTree returns a new ModuleTreeNode that represents the root +// of the module tree populated with the given modules. +func NewModuleTree(mods map[string]*Module) *ModuleTreeNode { + root := &ModuleTreeNode{ + Children: map[Value]*ModuleTreeNode{}, + } + for _, name := range util.KeysSorted(mods) { + m := mods[name] + node := root + for i, x := range m.Package.Path { + c, ok := node.Children[x.Value] + if !ok { + var hide bool + if i == 1 && x.Value.Compare(SystemDocumentKey) == 0 { + hide = true + } + c = &ModuleTreeNode{ + Key: x.Value, + Children: map[Value]*ModuleTreeNode{}, + Hide: hide, + } + node.Children[x.Value] = c + } + node = c + } + node.Modules = append(node.Modules, m) + } + return root +} + +// Size returns the number of modules in the tree. +func (n *ModuleTreeNode) Size() int { + s := len(n.Modules) + for _, c := range n.Children { + s += c.Size() + } + return s +} + +// Child returns n's child with key k. +func (n *ModuleTreeNode) child(k Value) *ModuleTreeNode { + switch k.(type) { + case String, Var: + return n.Children[k] + } + return nil +} + +// Find dereferences ref along the tree. ref[0] is converted to a String +// for convenience. +func (n *ModuleTreeNode) find(ref Ref) (*ModuleTreeNode, Ref) { + if v, ok := ref[0].Value.(Var); ok { + ref = Ref{StringTerm(string(v))}.Concat(ref[1:]) + } + node := n + for i, r := range ref { + next := node.child(r.Value) + if next == nil { + tail := make(Ref, len(ref)-i) + tail[0] = VarTerm(string(ref[i].Value.(String))) + copy(tail[1:], ref[i+1:]) + return node, tail + } + node = next + } + return node, nil +} + +// DepthFirst performs a depth-first traversal of the module tree rooted at n. +// If f returns true, traversal will not continue to the children of n. +func (n *ModuleTreeNode) DepthFirst(f func(*ModuleTreeNode) bool) { + if f(n) { + return + } + for _, node := range n.Children { + node.DepthFirst(f) + } +} + +// TreeNode represents a node in the rule tree. The rule tree is keyed by +// rule path. +type TreeNode struct { + Key Value + Values []any + Children map[Value]*TreeNode + Sorted []Value + Hide bool +} + +func (n *TreeNode) String() string { + return fmt.Sprintf("", n.Key, n.Values, n.Sorted, n.Hide) +} + +// NewRuleTree returns a new TreeNode that represents the root +// of the rule tree populated with the given rules. +func NewRuleTree(mtree *ModuleTreeNode) *TreeNode { + root := TreeNode{ + Key: mtree.Key, + } + + mtree.DepthFirst(func(m *ModuleTreeNode) bool { + for _, mod := range m.Modules { + if len(mod.Rules) == 0 { + root.add(mod.Package.Path, nil) + } + for _, rule := range mod.Rules { + root.add(rule.Ref().GroundPrefix(), rule) + } + } + return false + }) + + // ensure that data.system's TreeNode is hidden + node, tail := root.find(DefaultRootRef.Append(NewTerm(SystemDocumentKey))) + if len(tail) == 0 { // found + node.Hide = true + } + + root.DepthFirst(func(x *TreeNode) bool { + x.sort() + return false + }) + + return &root +} + +func (n *TreeNode) add(path Ref, rule *Rule) { + node, tail := n.find(path) + if len(tail) > 0 { + sub := treeNodeFromRef(tail, rule) + if node.Children == nil { + node.Children = make(map[Value]*TreeNode, 1) + } + node.Children[sub.Key] = sub + node.Sorted = append(node.Sorted, sub.Key) + } else if rule != nil { + node.Values = append(node.Values, rule) + } +} + +// Size returns the number of rules in the tree. +func (n *TreeNode) Size() int { + s := len(n.Values) + for _, c := range n.Children { + s += c.Size() + } + return s +} + +// Child returns n's child with key k. +func (n *TreeNode) Child(k Value) *TreeNode { + switch k.(type) { + case Ref, Call: + return nil + default: + return n.Children[k] + } +} + +// Find dereferences ref along the tree +func (n *TreeNode) Find(ref Ref) *TreeNode { + node := n + for _, r := range ref { + node = node.Child(r.Value) + if node == nil { + return nil + } + } + return node +} + +// Iteratively dereferences ref along the node's subtree. +// - If matching fails immediately, the tail will contain the full ref. +// - Partial matching will result in a tail of non-zero length. +// - A complete match will result in a 0 length tail. +func (n *TreeNode) find(ref Ref) (*TreeNode, Ref) { + node := n + for i := range ref { + next := node.Child(ref[i].Value) + if next == nil { + tail := make(Ref, len(ref)-i) + copy(tail, ref[i:]) + return node, tail + } + node = next + } + return node, nil +} + +// DepthFirst performs a depth-first traversal of the rule tree rooted at n. If +// f returns true, traversal will not continue to the children of n. +func (n *TreeNode) DepthFirst(f func(*TreeNode) bool) { + if f(n) { + return + } + for _, node := range n.Children { + node.DepthFirst(f) + } +} + +func (n *TreeNode) sort() { + slices.SortFunc(n.Sorted, Value.Compare) +} + +func treeNodeFromRef(ref Ref, rule *Rule) *TreeNode { + depth := len(ref) - 1 + key := ref[depth].Value + node := &TreeNode{ + Key: key, + Children: nil, + } + if rule != nil { + node.Values = []any{rule} + } + + for i := len(ref) - 2; i >= 0; i-- { + key := ref[i].Value + node = &TreeNode{ + Key: key, + Children: map[Value]*TreeNode{ref[i+1].Value: node}, + Sorted: []Value{ref[i+1].Value}, + } + } + return node +} + +// flattenChildren flattens all children's rule refs into a sorted array. +func (n *TreeNode) flattenChildren() []Ref { + ret := newRefSet() + for _, sub := range n.Children { // we only want the children, so don't use n.DepthFirst() right away + sub.DepthFirst(func(x *TreeNode) bool { + for _, r := range x.Values { + rule := r.(*Rule) + ret.AddPrefix(rule.Ref()) + } + return false + }) + } + + slices.SortFunc(ret.s, RefCompare) + return ret.s +} + +// Graph represents the graph of dependencies between rules. +type Graph struct { + adj map[util.T]map[util.T]struct{} + radj map[util.T]map[util.T]struct{} + nodes map[util.T]struct{} + sorted []util.T +} + +// NewGraph returns a new Graph based on modules. The list function must return +// the rules referred to directly by the ref. +func NewGraph(modules map[string]*Module, list func(Ref) []*Rule) *Graph { + + graph := &Graph{ + adj: map[util.T]map[util.T]struct{}{}, + radj: map[util.T]map[util.T]struct{}{}, + nodes: map[util.T]struct{}{}, + sorted: nil, + } + + // Create visitor to walk a rule AST and add edges to the rule graph for + // each dependency. + vis := func(a *Rule) *GenericVisitor { + stop := false + return NewGenericVisitor(func(x any) bool { + switch x := x.(type) { + case Ref: + for _, b := range list(x) { + for node := b; node != nil; node = node.Else { + graph.addDependency(a, node) + } + } + case *Rule: + if stop { + // Do not recurse into else clauses (which will be handled + // by the outer visitor.) + return true + } + stop = true + } + return false + }) + } + + // Walk over all rules, add them to graph, and build adjacency lists. + for _, module := range modules { + WalkRules(module, func(a *Rule) bool { + graph.addNode(a) + vis(a).Walk(a) + return false + }) + } + + return graph +} + +// Dependencies returns the set of rules that x depends on. +func (g *Graph) Dependencies(x util.T) map[util.T]struct{} { + return g.adj[x] +} + +// Dependents returns the set of rules that depend on x. +func (g *Graph) Dependents(x util.T) map[util.T]struct{} { + return g.radj[x] +} + +// Sort returns a slice of rules sorted by dependencies. If a cycle is found, +// ok is set to false. +func (g *Graph) Sort() (sorted []util.T, ok bool) { + if g.sorted != nil { + return g.sorted, true + } + + sorter := &graphSort{ + sorted: make([]util.T, 0, len(g.nodes)), + deps: g.Dependencies, + marked: map[util.T]struct{}{}, + temp: map[util.T]struct{}{}, + } + + for node := range g.nodes { + if !sorter.Visit(node) { + return nil, false + } + } + + g.sorted = sorter.sorted + return g.sorted, true +} + +func (g *Graph) addDependency(u util.T, v util.T) { + + if _, ok := g.nodes[u]; !ok { + g.addNode(u) + } + + if _, ok := g.nodes[v]; !ok { + g.addNode(v) + } + + edges, ok := g.adj[u] + if !ok { + edges = map[util.T]struct{}{} + g.adj[u] = edges + } + + edges[v] = struct{}{} + + edges, ok = g.radj[v] + if !ok { + edges = map[util.T]struct{}{} + g.radj[v] = edges + } + + edges[u] = struct{}{} +} + +func (g *Graph) addNode(n util.T) { + g.nodes[n] = struct{}{} +} + +type graphSort struct { + sorted []util.T + deps func(util.T) map[util.T]struct{} + marked map[util.T]struct{} + temp map[util.T]struct{} +} + +func (sort *graphSort) Marked(node util.T) bool { + _, marked := sort.marked[node] + return marked +} + +func (sort *graphSort) Visit(node util.T) (ok bool) { + if _, ok := sort.temp[node]; ok { + return false + } + if sort.Marked(node) { + return true + } + sort.temp[node] = struct{}{} + for other := range sort.deps(node) { + if !sort.Visit(other) { + return false + } + } + sort.marked[node] = struct{}{} + delete(sort.temp, node) + sort.sorted = append(sort.sorted, node) + return true +} + +// GraphTraversal is a Traversal that understands the dependency graph +type GraphTraversal struct { + graph *Graph + visited map[util.T]struct{} +} + +// NewGraphTraversal returns a Traversal for the dependency graph +func NewGraphTraversal(graph *Graph) *GraphTraversal { + return &GraphTraversal{ + graph: graph, + visited: map[util.T]struct{}{}, + } +} + +// Edges lists all dependency connections for a given node +func (g *GraphTraversal) Edges(x util.T) []util.T { + r := []util.T{} + for v := range g.graph.Dependencies(x) { + r = append(r, v) + } + return r +} + +// Visited returns whether a node has been visited, setting a node to visited if not +func (g *GraphTraversal) Visited(u util.T) bool { + _, ok := g.visited[u] + g.visited[u] = struct{}{} + return ok +} + +type unsafePair struct { + Expr *Expr + Vars VarSet +} + +type unsafeVarLoc struct { + Var Var + Loc *Location +} + +type unsafeVars map[*Expr]VarSet + +func (vs unsafeVars) Add(e *Expr, v Var) { + if u, ok := vs[e]; ok { + u[v] = struct{}{} + } else { + vs[e] = VarSet{v: struct{}{}} + } +} + +func (vs unsafeVars) Set(e *Expr, s VarSet) { + vs[e] = s +} + +func (vs unsafeVars) Update(o unsafeVars) { + for k, v := range o { + if _, ok := vs[k]; !ok { + vs[k] = VarSet{} + } + vs[k].Update(v) + } +} + +func (vs unsafeVars) Vars() (result []unsafeVarLoc) { + + locs := map[Var]*Location{} + + // If var appears in multiple sets then pick first by location. + for expr, vars := range vs { + for v := range vars { + if locs[v].Compare(expr.Location) > 0 { + locs[v] = expr.Location + } + } + } + + for v, loc := range locs { + result = append(result, unsafeVarLoc{ + Var: v, + Loc: loc, + }) + } + + slices.SortFunc(result, func(a, b unsafeVarLoc) int { + return a.Loc.Compare(b.Loc) + }) + + return result +} + +func (vs unsafeVars) Slice() (result []unsafePair) { + for expr, vs := range vs { + result = append(result, unsafePair{ + Expr: expr, + Vars: vs, + }) + } + return +} + +// reorderBodyForSafety returns a copy of the body ordered such that +// left to right evaluation of the body will not encounter unbound variables +// in input positions or negated expressions. +// +// Expressions are added to the re-ordered body as soon as they are considered +// safe. If multiple expressions become safe in the same pass, they are added +// in their original order. This results in minimal re-ordering of the body. +// +// If the body cannot be reordered to ensure safety, the second return value +// contains a mapping of expressions to unsafe variables in those expressions. +func reorderBodyForSafety(builtins map[string]*Builtin, arity func(Ref) int, globals VarSet, body Body) (Body, unsafeVars) { + vis := varVisitorPool.Get().WithParams(SafetyCheckVisitorParams) + vis.WalkBody(body) + + defer varVisitorPool.Put(vis) + + bodyVars := vis.Vars().Copy() + safe := bodyVars.Intersect(globals) + unsafe := make(unsafeVars, len(bodyVars)-len(safe)) + + for _, e := range body { + vis.Clear().WithParams(SafetyCheckVisitorParams).Walk(e) + for v := range vis.Vars() { + if _, ok := safe[v]; !ok { + unsafe.Add(e, v) + } + } + } + + reordered := make(Body, 0, len(body)) + output := VarSet{} + + for { + n := len(reordered) + + for _, e := range body { + if reordered.Contains(e) { + continue + } + + ovs := outputVarsForExpr(e, arity, safe, output) + + // check closures: is this expression closing over variables that + // haven't been made safe by what's already included in `reordered`? + vs := unsafeVarsInClosures(e) + cv := vs.Intersect(bodyVars).Diff(globals) + ob := outputVarsForBody(reordered, arity, safe) + + if cv.DiffCount(ob) > 0 { + uv := cv.Diff(ob) + if uv.Equal(ovs) { // special case "closure-self" + continue + } + unsafe.Set(e, uv) + } + + for v := range unsafe[e] { + if ovs.Contains(v) || safe.Contains(v) { + delete(unsafe[e], v) + } + } + + if len(unsafe[e]) == 0 { + delete(unsafe, e) + reordered.Append(e) + safe.Update(ovs) // this expression's outputs are safe + } + } + + if len(reordered) == n { // fixed point, could not add any expr of body + break + } + } + + // Recursively visit closures and perform the safety checks on them. + // Update the globals at each expression to include the variables that could + // be closed over. + g := globals.Copy() + xform := &bodySafetyTransformer{ + builtins: builtins, + arity: arity, + } + gvis := &GenericVisitor{} + for i, e := range reordered { + if i > 0 { + vis.Walk(reordered[i-1]) + g.Update(vis.Vars()) + vis.Clear().WithParams(SafetyCheckVisitorParams) + } + xform.current = e + xform.globals = g + xform.unsafe = unsafe + gvis.f = xform.Visit + gvis.Walk(e) + } + + return reordered, unsafe +} + +type bodySafetyTransformer struct { + builtins map[string]*Builtin + arity func(Ref) int + current *Expr + globals VarSet + unsafe unsafeVars +} + +func (xform *bodySafetyTransformer) Visit(x any) bool { + switch term := x.(type) { + case *Term: + switch x := term.Value.(type) { + case *object: + cpy, _ := x.Map(func(k, v *Term) (*Term, *Term, error) { + kcpy := k.Copy() + NewGenericVisitor(xform.Visit).Walk(kcpy) + vcpy := v.Copy() + NewGenericVisitor(xform.Visit).Walk(vcpy) + return kcpy, vcpy, nil + }) + term.Value = cpy + return true + case *set: + cpy, _ := x.Map(func(v *Term) (*Term, error) { + vcpy := v.Copy() + NewGenericVisitor(xform.Visit).Walk(vcpy) + return vcpy, nil + }) + term.Value = cpy + return true + case *ArrayComprehension: + xform.reorderArrayComprehensionSafety(x) + return true + case *ObjectComprehension: + xform.reorderObjectComprehensionSafety(x) + return true + case *SetComprehension: + xform.reorderSetComprehensionSafety(x) + return true + } + case *Expr: + if ev, ok := term.Terms.(*Every); ok { + xform.globals.Update(ev.KeyValueVars()) + ev.Body = xform.reorderComprehensionSafety(NewVarSet(), ev.Body) + return true + } + } + return false +} + +func (xform *bodySafetyTransformer) reorderComprehensionSafety(tv VarSet, body Body) Body { + bv := body.Vars(SafetyCheckVisitorParams) + bv.Update(xform.globals) + + if tv.DiffCount(bv) > 0 { + uv := tv.Diff(bv) + for v := range uv { + xform.unsafe.Add(xform.current, v) + } + } + + r, u := reorderBodyForSafety(xform.builtins, xform.arity, xform.globals, body) + if len(u) == 0 { + return r + } + + xform.unsafe.Update(u) + return body +} + +func (xform *bodySafetyTransformer) reorderArrayComprehensionSafety(ac *ArrayComprehension) { + ac.Body = xform.reorderComprehensionSafety(ac.Term.Vars(), ac.Body) +} + +func (xform *bodySafetyTransformer) reorderObjectComprehensionSafety(oc *ObjectComprehension) { + tv := oc.Key.Vars() + tv.Update(oc.Value.Vars()) + oc.Body = xform.reorderComprehensionSafety(tv, oc.Body) +} + +func (xform *bodySafetyTransformer) reorderSetComprehensionSafety(sc *SetComprehension) { + sc.Body = xform.reorderComprehensionSafety(sc.Term.Vars(), sc.Body) +} + +// unsafeVarsInClosures collects vars that are contained in closures within +// this expression. +func unsafeVarsInClosures(e *Expr) VarSet { + vs := VarSet{} + WalkClosures(e, func(x any) bool { + vis := &VarVisitor{vars: vs} + if ev, ok := x.(*Every); ok { + vis.WalkBody(ev.Body) + return true + } + vis.Walk(x) + return true + }) + return vs +} + +// OutputVarsFromBody returns all variables which are the "output" for +// the given body. For safety checks this means that they would be +// made safe by the body. +func OutputVarsFromBody(c *Compiler, body Body, safe VarSet) VarSet { + return outputVarsForBody(body, c.GetArity, safe) +} + +func outputVarsForBody(body Body, arity func(Ref) int, safe VarSet) VarSet { + o := safe.Copy() + output := VarSet{} + for _, e := range body { + o.Update(outputVarsForExpr(e, arity, o, output)) + } + return o.Diff(safe) +} + +// OutputVarsFromExpr returns all variables which are the "output" for +// the given expression. For safety checks this means that they would be +// made safe by the expr. +func OutputVarsFromExpr(c *Compiler, expr *Expr, safe VarSet) VarSet { + return outputVarsForExpr(expr, c.GetArity, safe, VarSet{}) +} + +func outputVarsForExpr(expr *Expr, arity func(Ref) int, safe VarSet, output VarSet) VarSet { + // Negated expressions must be safe. + if expr.Negated { + return VarSet{} + } + + var vis *VarVisitor + + // With modifier inputs must be safe. + for _, with := range expr.With { + vis = vis.ClearOrNew().WithParams(SafetyCheckVisitorParams) + vis.Walk(with) + if vis.Vars().DiffCount(safe) > 0 { + return VarSet{} + } + } + + switch terms := expr.Terms.(type) { + case *Term: + return outputVarsForTerms(expr, safe) + case []*Term: + if expr.IsEquality() { + return outputVarsForExprEq(expr, safe, output) + } + + operator, ok := terms[0].Value.(Ref) + if !ok { + return VarSet{} + } + + ar := arity(operator) + if ar < 0 { + return VarSet{} + } + + return outputVarsForExprCall(expr, ar, safe, terms, vis, output) + case *Every: + return outputVarsForTerms(terms.Domain, safe) + default: + panic("illegal expression") + } +} + +func outputVarsForExprEq(expr *Expr, safe VarSet, output VarSet) VarSet { + if !validEqAssignArgCount(expr) { + return safe + } + + output.Update(outputVarsForTerms(expr, safe)) + output.Update(safe) + output.Update(Unify(output, expr.Operand(0), expr.Operand(1))) + + diff := output.Diff(safe) + + clear(output) + + return diff +} + +func outputVarsForExprCall(expr *Expr, arity int, safe VarSet, terms []*Term, vis *VarVisitor, output VarSet) VarSet { + clear(output) + + output.Update(outputVarsForTerms(expr, safe)) + + numInputTerms := arity + 1 + if numInputTerms >= len(terms) { + return output + } + + params := VarVisitorParams{ + SkipClosures: true, + SkipSets: true, + SkipObjectKeys: true, + SkipRefHead: true, + } + vis = vis.ClearOrNew().WithParams(params) + vis.WalkArgs(Args(terms[:numInputTerms])) + + unsafe := vis.Vars().Diff(output).DiffCount(safe) + if unsafe > 0 { + return VarSet{} + } + + vis = vis.Clear().WithParams(params) + vis.WalkArgs(Args(terms[numInputTerms:])) + output.Update(vis.vars) + return output +} + +func outputVarsForTerms(expr any, safe VarSet) VarSet { + output := VarSet{} + WalkTerms(expr, func(x *Term) bool { + switch r := x.Value.(type) { + case *SetComprehension, *ArrayComprehension, *ObjectComprehension: + return true + case Ref: + if !isRefSafe(r, safe) { + return true + } + if !r.IsGround() { + // Avoiding r.OutputVars() here as it won't allow reusing the visitor. + vis := varVisitorPool.Get().WithParams(VarVisitorParams{SkipRefHead: true}) + vis.WalkRef(r) + output.Update(vis.Vars()) + varVisitorPool.Put(vis) + } + } + return false + }) + return output +} + +type equalityFactory struct { + gen *localVarGenerator +} + +func newEqualityFactory(gen *localVarGenerator) *equalityFactory { + return &equalityFactory{gen} +} + +func (f *equalityFactory) Generate(other *Term) *Expr { + term := NewTerm(f.gen.Generate()).SetLocation(other.Location) + expr := Equality.Expr(term, other) + expr.Generated = true + expr.Location = other.Location + return expr +} + +// TODO: Move to internal package? +const LocalVarPrefix = "__local" + +type localVarGenerator struct { + exclude VarSet + suffix string + next int +} + +func newLocalVarGeneratorForModuleSet(sorted []string, modules map[string]*Module) *localVarGenerator { + vis := NewVarVisitor() + for _, key := range sorted { + vis.Walk(modules[key]) + } + return &localVarGenerator{exclude: vis.vars, next: 0} +} + +func newLocalVarGenerator(suffix string, node any) *localVarGenerator { + vis := NewVarVisitor() + vis.Walk(node) + return &localVarGenerator{exclude: vis.vars, suffix: suffix, next: 0} +} + +func (l *localVarGenerator) Generate() Var { + for { + result := Var(LocalVarPrefix + l.suffix + strconv.Itoa(l.next) + "__") + l.next++ + if !l.exclude.Contains(result) { + return result + } + } +} + +func getGlobals(pkg *Package, rules []Ref, imports []*Import) map[Var]*usedRef { + globals := make(map[Var]*usedRef, len(rules)+len(imports)) + + for _, ref := range rules { + v := ref[0].Value.(Var) + globals[v] = &usedRef{ref: pkg.Path.Append(StringTerm(string(v)))} + } + + for _, imp := range imports { + path := imp.Path.Value.(Ref) + if FutureRootDocument.Equal(path[0]) || RegoRootDocument.Equal(path[0]) { + continue + } + globals[imp.Name()] = &usedRef{ref: path} + } + + return globals +} + +func requiresEval(x *Term) bool { + if x == nil { + return false + } + return ContainsRefs(x) || ContainsComprehensions(x) +} + +func resolveRef(globals map[Var]*usedRef, ignore *declaredVarStack, ref Ref) Ref { + + r := Ref{} + for i, x := range ref { + switch v := x.Value.(type) { + case Var: + if g, ok := globals[v]; ok && !ignore.Contains(v) { + cpy := g.ref.Copy() + for i := range cpy { + cpy[i].SetLocation(x.Location) + } + if i == 0 { + r = cpy + } else { + r = append(r, NewTerm(cpy).SetLocation(x.Location)) + } + g.used = true + } else { + r = append(r, x) + } + case Ref, *Array, Object, Set, *ArrayComprehension, *SetComprehension, *ObjectComprehension, Call: + r = append(r, resolveRefsInTerm(globals, ignore, x)) + default: + r = append(r, x) + } + } + + return r +} + +type usedRef struct { + ref Ref + used bool +} + +func resolveRefsInRule(globals map[Var]*usedRef, rule *Rule) error { + ignore := &declaredVarStack{} + + vars := NewVarSet() + var vis *GenericVisitor + var err error + + // Walk args to collect vars and transform body so that callers can shadow + // root documents. + vis = NewGenericVisitor(func(x any) bool { + if err != nil { + return true + } + switch x := x.(type) { + case Var: + vars.Add(x) + + // Object keys cannot be pattern matched so only walk values. + case *object: + x.Foreach(func(_, v *Term) { + vis.Walk(v) + }) + + // Skip terms that could contain vars that cannot be pattern matched. + case Set, *ArrayComprehension, *SetComprehension, *ObjectComprehension, Call: + return true + + case *Term: + if _, ok := x.Value.(Ref); ok { + if RootDocumentRefs.Contains(x) { + // We could support args named input, data, etc. however + // this would require rewriting terms in the head and body. + // Preventing root document shadowing is simpler, and + // arguably, will prevent confusing names from being used. + // NOTE: this check is also performed as part of strict-mode in + // checkRootDocumentOverrides. + err = fmt.Errorf("args must not shadow %v (use a different variable name)", x) + return true + } + } + } + return false + }) + + vis.Walk(rule.Head.Args) + + if err != nil { + return err + } + + ignore.Push(vars) + ignore.Push(declaredVars(rule.Body)) + + ref := rule.Head.Ref() + for i := 1; i < len(ref); i++ { + ref[i] = resolveRefsInTerm(globals, ignore, ref[i]) + } + if rule.Head.Key != nil { + rule.Head.Key = resolveRefsInTerm(globals, ignore, rule.Head.Key) + } + + if rule.Head.Value != nil { + rule.Head.Value = resolveRefsInTerm(globals, ignore, rule.Head.Value) + } + + rule.Body = resolveRefsInBody(globals, ignore, rule.Body) + return nil +} + +func resolveRefsInBody(globals map[Var]*usedRef, ignore *declaredVarStack, body Body) Body { + r := make([]*Expr, 0, len(body)) + for _, expr := range body { + r = append(r, resolveRefsInExpr(globals, ignore, expr)) + } + return r +} + +func resolveRefsInExpr(globals map[Var]*usedRef, ignore *declaredVarStack, expr *Expr) *Expr { + cpy := *expr + switch ts := expr.Terms.(type) { + case *Term: + cpy.Terms = resolveRefsInTerm(globals, ignore, ts) + case []*Term: + buf := make([]*Term, len(ts)) + for i := range ts { + buf[i] = resolveRefsInTerm(globals, ignore, ts[i]) + } + cpy.Terms = buf + case *SomeDecl: + if val, ok := ts.Symbols[0].Value.(Call); ok { + cpy.Terms = &SomeDecl{ + Symbols: []*Term{CallTerm(resolveRefsInTermSlice(globals, ignore, val)...)}, + Location: ts.Location, + } + } + case *Every: + locals := NewVarSet() + if ts.Key != nil { + locals.Update(ts.Key.Vars()) + } + locals.Update(ts.Value.Vars()) + ignore.Push(locals) + cpy.Terms = &Every{ + Key: ts.Key.Copy(), // TODO(sr): do more? + Value: ts.Value.Copy(), // TODO(sr): do more? + Domain: resolveRefsInTerm(globals, ignore, ts.Domain), + Body: resolveRefsInBody(globals, ignore, ts.Body), + } + ignore.Pop() + } + for _, w := range cpy.With { + w.Target = resolveRefsInTerm(globals, ignore, w.Target) + w.Value = resolveRefsInTerm(globals, ignore, w.Value) + } + return &cpy +} + +func resolveRefsInTerm(globals map[Var]*usedRef, ignore *declaredVarStack, term *Term) *Term { + switch v := term.Value.(type) { + case Var: + if g, ok := globals[v]; ok && !ignore.Contains(v) { + cpy := g.ref.Copy() + for i := range cpy { + cpy[i].SetLocation(term.Location) + } + g.used = true + return NewTerm(cpy).SetLocation(term.Location) + } + return term + case Ref: + fqn := resolveRef(globals, ignore, v) + cpy := *term + cpy.Value = fqn + return &cpy + case *object: + cpy := *term + cpy.Value, _ = v.Map(func(k, v *Term) (*Term, *Term, error) { + k = resolveRefsInTerm(globals, ignore, k) + v = resolveRefsInTerm(globals, ignore, v) + return k, v, nil + }) + return &cpy + case *Array: + cpy := *term + cpy.Value = NewArray(resolveRefsInTermArray(globals, ignore, v)...) + return &cpy + case Call: + cpy := *term + cpy.Value = Call(resolveRefsInTermSlice(globals, ignore, v)) + return &cpy + case Set: + s, _ := v.Map(func(e *Term) (*Term, error) { + return resolveRefsInTerm(globals, ignore, e), nil + }) + cpy := *term + cpy.Value = s + return &cpy + case *ArrayComprehension: + ac := &ArrayComprehension{} + ignore.Push(declaredVars(v.Body)) + ac.Term = resolveRefsInTerm(globals, ignore, v.Term) + ac.Body = resolveRefsInBody(globals, ignore, v.Body) + cpy := *term + cpy.Value = ac + ignore.Pop() + return &cpy + case *ObjectComprehension: + oc := &ObjectComprehension{} + ignore.Push(declaredVars(v.Body)) + oc.Key = resolveRefsInTerm(globals, ignore, v.Key) + oc.Value = resolveRefsInTerm(globals, ignore, v.Value) + oc.Body = resolveRefsInBody(globals, ignore, v.Body) + cpy := *term + cpy.Value = oc + ignore.Pop() + return &cpy + case *SetComprehension: + sc := &SetComprehension{} + ignore.Push(declaredVars(v.Body)) + sc.Term = resolveRefsInTerm(globals, ignore, v.Term) + sc.Body = resolveRefsInBody(globals, ignore, v.Body) + cpy := *term + cpy.Value = sc + ignore.Pop() + return &cpy + default: + return term + } +} + +func resolveRefsInTermArray(globals map[Var]*usedRef, ignore *declaredVarStack, terms *Array) []*Term { + cpy := make([]*Term, terms.Len()) + for i := range terms.Len() { + cpy[i] = resolveRefsInTerm(globals, ignore, terms.Elem(i)) + } + return cpy +} + +func resolveRefsInTermSlice(globals map[Var]*usedRef, ignore *declaredVarStack, terms []*Term) []*Term { + cpy := make([]*Term, len(terms)) + for i := range terms { + cpy[i] = resolveRefsInTerm(globals, ignore, terms[i]) + } + return cpy +} + +type declaredVarStack []VarSet + +func (s declaredVarStack) Contains(v Var) bool { + for i := len(s) - 1; i >= 0; i-- { + if _, ok := s[i][v]; ok { + return ok + } + } + return false +} + +func (s declaredVarStack) Add(v Var) { + s[len(s)-1].Add(v) +} + +func (s *declaredVarStack) Push(vs VarSet) { + *s = append(*s, vs) +} + +func (s *declaredVarStack) Pop() { + curr := *s + *s = curr[:len(curr)-1] +} + +func declaredVars(x any) VarSet { + vars := NewVarSet() + vis := NewGenericVisitor(func(x any) bool { + switch x := x.(type) { + case *Expr: + if x.IsAssignment() && validEqAssignArgCount(x) { + WalkVars(x.Operand(0), func(v Var) bool { + vars.Add(v) + return false + }) + } else if decl, ok := x.Terms.(*SomeDecl); ok { + for i := range decl.Symbols { + switch val := decl.Symbols[i].Value.(type) { + case Var: + vars.Add(val) + case Call: + args := val[1:] + if len(args) == 3 { // some x, y in xs + WalkVars(args[1], func(v Var) bool { + vars.Add(v) + return false + }) + } + // some x in xs + WalkVars(args[0], func(v Var) bool { + vars.Add(v) + return false + }) + } + } + } + case *ArrayComprehension, *SetComprehension, *ObjectComprehension: + return true + } + return false + }) + vis.Walk(x) + return vars +} + +// rewriteComprehensionTerms will rewrite comprehensions so that the term part +// is bound to a variable in the body. This allows any type of term to be used +// in the term part (even if the term requires evaluation.) +// +// For instance, given the following comprehension: +// +// [x[0] | x = y[_]; y = [1,2,3]] +// +// The comprehension would be rewritten as: +// +// [__local0__ | x = y[_]; y = [1,2,3]; __local0__ = x[0]] +func rewriteComprehensionTerms(f *equalityFactory, node any) (any, error) { + return TransformComprehensions(node, func(x any) (Value, error) { + switch x := x.(type) { + case *ArrayComprehension: + if requiresEval(x.Term) { + expr := f.Generate(x.Term) + x.Term = expr.Operand(0) + x.Body.Append(expr) + } + return x, nil + case *SetComprehension: + if requiresEval(x.Term) { + expr := f.Generate(x.Term) + x.Term = expr.Operand(0) + x.Body.Append(expr) + } + return x, nil + case *ObjectComprehension: + if requiresEval(x.Key) { + expr := f.Generate(x.Key) + x.Key = expr.Operand(0) + x.Body.Append(expr) + } + if requiresEval(x.Value) { + expr := f.Generate(x.Value) + x.Value = expr.Operand(0) + x.Body.Append(expr) + } + return x, nil + } + panic("illegal type") + }) +} + +// rewriteEquals will rewrite exprs under x as unification calls instead of == +// calls. For example: +// +// data.foo == data.bar is rewritten as data.foo = data.bar +// +// This stage should only run the safety check (since == is a built-in with no +// outputs, so the inputs must not be marked as safe.) +// +// This stage is not executed by the query compiler by default because when +// callers specify == instead of = they expect to receive a true/false/undefined +// result back whereas with = the result is only ever true/undefined. For +// partial evaluation cases we do want to rewrite == to = to simplify the +// result. +func rewriteEquals(x any) (modified bool) { + unifyOp := Equality.Ref() + t := NewGenericTransformer(func(x any) (any, error) { + if x, ok := x.(*Expr); ok && x.IsCall() { + operator := x.Operator() + if operator.Equal(doubleEq) && len(x.Operands()) == 2 { + modified = true + x.SetOperator(NewTerm(unifyOp)) + } + } + return x, nil + }) + _, _ = Transform(t, x) // ignore error + 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. +// +// For instance, given the following query: +// +// foo(data.bar) = 1 +// +// The rewritten version will be: +// +// __local0__ = data.bar; foo(__local0__) = 1 +func rewriteDynamics(f *equalityFactory, body Body) Body { + result := make(Body, 0, len(body)) + for _, expr := range body { + switch { + case expr.IsEquality(): + result = rewriteDynamicsEqExpr(f, expr, result) + case expr.IsCall(): + result = rewriteDynamicsCallExpr(f, expr, result) + case expr.IsEvery(): + result = rewriteDynamicsEveryExpr(f, expr, result) + default: + result = rewriteDynamicsTermExpr(f, expr, result) + } + } + return result +} + +func appendExpr(body Body, expr *Expr) Body { + body.Append(expr) + return body +} + +func rewriteDynamicsEqExpr(f *equalityFactory, expr *Expr, result Body) Body { + if !validEqAssignArgCount(expr) { + return appendExpr(result, expr) + } + terms := expr.Terms.([]*Term) + result, terms[1] = rewriteDynamicsInTerm(expr, f, terms[1], result) + result, terms[2] = rewriteDynamicsInTerm(expr, f, terms[2], result) + return appendExpr(result, expr) +} + +func rewriteDynamicsCallExpr(f *equalityFactory, expr *Expr, result Body) Body { + terms := expr.Terms.([]*Term) + for i := 1; i < len(terms); i++ { + result, terms[i] = rewriteDynamicsOne(expr, f, terms[i], result) + } + return appendExpr(result, expr) +} + +func rewriteDynamicsEveryExpr(f *equalityFactory, expr *Expr, result Body) Body { + ev := expr.Terms.(*Every) + result, ev.Domain = rewriteDynamicsOne(expr, f, ev.Domain, result) + ev.Body = rewriteDynamics(f, ev.Body) + return appendExpr(result, expr) +} + +func rewriteDynamicsTermExpr(f *equalityFactory, expr *Expr, result Body) Body { + term := expr.Terms.(*Term) + result, expr.Terms = rewriteDynamicsInTerm(expr, f, term, result) + return appendExpr(result, expr) +} + +func rewriteDynamicsInTerm(original *Expr, f *equalityFactory, term *Term, result Body) (Body, *Term) { + switch v := term.Value.(type) { + case Ref: + for i := 1; i < len(v); i++ { + result, v[i] = rewriteDynamicsOne(original, f, v[i], result) + } + case *ArrayComprehension: + v.Body = rewriteDynamics(f, v.Body) + case *SetComprehension: + v.Body = rewriteDynamics(f, v.Body) + case *ObjectComprehension: + v.Body = rewriteDynamics(f, v.Body) + default: + result, term = rewriteDynamicsOne(original, f, term, result) + } + return result, term +} + +func rewriteDynamicsOne(original *Expr, f *equalityFactory, term *Term, result Body) (Body, *Term) { + switch v := term.Value.(type) { + case Ref: + for i := 1; i < len(v); i++ { + result, v[i] = rewriteDynamicsOne(original, f, v[i], result) + } + 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 := range v.Len() { + var t *Term + result, t = rewriteDynamicsOne(original, f, v.Elem(i), result) + v.set(i, t) + } + return result, term + case *object: + cpy := NewObject() + v.Foreach(func(key, value *Term) { + result, key = rewriteDynamicsOne(original, f, key, result) + result, value = rewriteDynamicsOne(original, f, value, result) + cpy.Insert(key, value) + }) + return result, NewTerm(cpy).SetLocation(term.Location) + case Set: + cpy := NewSet() + for _, term := range v.Slice() { + var rw *Term + result, rw = rewriteDynamicsOne(original, f, term, result) + cpy.Add(rw) + } + return result, NewTerm(cpy).SetLocation(term.Location) + case *ArrayComprehension: + 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 +} + +func rewriteDynamicsComprehensionBody(original *Expr, f *equalityFactory, body Body, term *Term) (Body, *Expr) { + body = rewriteDynamics(f, body) + generated := f.Generate(term) + generated.With = original.With + return body, generated +} + +func rewriteExprTermsInHead(gen *localVarGenerator, rule *Rule) { + for i := range rule.Head.Args { + support, output := expandExprTerm(gen, rule.Head.Args[i]) + for j := range support { + rule.Body.Append(support[j]) + } + rule.Head.Args[i] = output + } + if rule.Head.Key != nil { + support, output := expandExprTerm(gen, rule.Head.Key) + for i := range support { + rule.Body.Append(support[i]) + } + rule.Head.Key = output + } + if rule.Head.Value != nil { + support, output := expandExprTerm(gen, rule.Head.Value) + for i := range support { + rule.Body.Append(support[i]) + } + rule.Head.Value = output + } +} + +func rewriteExprTermsInBody(gen *localVarGenerator, body Body) Body { + cpy := make(Body, 0, len(body)) + for i := range body { + for _, expr := range expandExpr(gen, body[i]) { + cpy.Append(expr) + } + } + return cpy +} + +func expandExpr(gen *localVarGenerator, expr *Expr) (result []*Expr) { + for i := range expr.With { + extras, value := expandExprTerm(gen, expr.With[i].Value) + expr.With[i].Value = value + result = append(result, extras...) + } + switch terms := expr.Terms.(type) { + case *Term: + extras, term := expandExprTerm(gen, terms) + if len(expr.With) > 0 { + for i := range extras { + extras[i].With = expr.With + } + } + result = append(result, extras...) + expr.Terms = term + result = append(result, expr) + case []*Term: + 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 + } + } + result = append(result, extras...) + } + result = append(result, expr) + case *Every: + var extras []*Expr + + 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) + } + 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) { + case Call: + for i := 1; i < len(v); i++ { + var extras []*Expr + extras, v[i] = expandExprTerm(gen, v[i]) + support = append(support, extras...) + } + output = NewTerm(gen.Generate()).SetLocation(term.Location) + expr := v.MakeExpr(output).SetLocation(term.Location) + expr.Generated = true + support = append(support, expr) + case Ref: + support = expandExprRef(gen, v) + case *Array: + support = expandExprTermArray(gen, v) + case *object: + cpy, _ := v.Map(func(k, v *Term) (*Term, *Term, error) { + extras1, expandedKey := expandExprTerm(gen, k) + extras2, expandedValue := expandExprTerm(gen, v) + support = append(support, extras1...) + support = append(support, extras2...) + return expandedKey, expandedValue, nil + }) + output = NewTerm(cpy).SetLocation(term.Location) + case Set: + cpy, _ := v.Map(func(x *Term) (*Term, error) { + extras, expanded := expandExprTerm(gen, x) + support = append(support, extras...) + return expanded, nil + }) + output = NewTerm(cpy).SetLocation(term.Location) + case *ArrayComprehension: + support, term := expandExprTerm(gen, v.Term) + for i := range support { + v.Body.Append(support[i]) + } + v.Term = term + v.Body = rewriteExprTermsInBody(gen, v.Body) + case *SetComprehension: + support, term := expandExprTerm(gen, v.Term) + for i := range support { + v.Body.Append(support[i]) + } + v.Term = term + v.Body = rewriteExprTermsInBody(gen, v.Body) + case *ObjectComprehension: + support, key := expandExprTerm(gen, v.Key) + for i := range support { + v.Body.Append(support[i]) + } + v.Key = key + support, value := expandExprTerm(gen, v.Value) + for i := range support { + v.Body.Append(support[i]) + } + v.Value = value + v.Body = rewriteExprTermsInBody(gen, v.Body) + } + return +} + +func expandExprRef(gen *localVarGenerator, v []*Term) (support []*Expr) { + // Start by calling a normal expandExprTerm on all terms. + support = expandExprTermSlice(gen, v) + + // Rewrite references in order to support indirect references. We rewrite + // e.g. + // + // [1, 2, 3][i] + // + // to + // + // __local_var = [1, 2, 3] + // __local_var[i] + // + // to support these. This only impacts the reference subject, i.e. the + // first item in the slice. + var subject = v[0] + switch subject.Value.(type) { + case *Array, Object, Set, *ArrayComprehension, *SetComprehension, *ObjectComprehension, Call: + f := newEqualityFactory(gen) + assignToLocal := f.Generate(subject) + support = append(support, assignToLocal) + v[0] = assignToLocal.Operand(0) + } + return +} + +func expandExprTermArray(gen *localVarGenerator, arr *Array) (support []*Expr) { + for i := range arr.Len() { + extras, v := expandExprTerm(gen, arr.Elem(i)) + arr.set(i, v) + support = append(support, extras...) + } + return +} + +func expandExprTermSlice(gen *localVarGenerator, v []*Term) (support []*Expr) { + for i := range v { + var extras []*Expr + extras, v[i] = expandExprTerm(gen, v[i]) + support = append(support, extras...) + } + return +} + +type localDeclaredVars struct { + vars []*declaredVarSet + + // rewritten contains a mapping of *all* user-defined variables + // that have been rewritten whereas vars contains the state + // from the current query (not any nested queries, and all vars + // seen). + rewritten map[Var]Var + + // indicates if an assignment (:= operator) has been seen *ever* + assignment bool +} + +type varOccurrence uint8 + +const ( + newVar varOccurrence = iota + argVar + seenVar + assignedVar + declaredVar +) + +type declaredVarSet struct { + vs map[Var]Var + occurrence map[Var]varOccurrence + count map[Var]int +} + +func newDeclaredVarSet() *declaredVarSet { + return &declaredVarSet{ + vs: map[Var]Var{}, + occurrence: map[Var]varOccurrence{}, + count: map[Var]int{}, + } +} + +func (s *declaredVarSet) clear() *declaredVarSet { + clear(s.vs) + clear(s.occurrence) + clear(s.count) + + return s +} + +func newLocalDeclaredVars() *localDeclaredVars { + return &localDeclaredVars{ + vars: []*declaredVarSet{newDeclaredVarSet()}, + rewritten: map[Var]Var{}, + } +} + +func (s *localDeclaredVars) Clear() { + var vs *declaredVarSet + if len(s.vars) > 0 { + vs = s.vars[0] + } + + clear(s.vars) + clear(s.rewritten) + + s.vars = s.vars[:0] + + if vs != nil { + s.vars = append(s.vars, vs.clear()) + } + if s.vars[0] == nil { + s.vars[0] = newDeclaredVarSet() + } + s.assignment = false +} + +func (s *localDeclaredVars) Copy() *localDeclaredVars { + stack := &localDeclaredVars{ + vars: make([]*declaredVarSet, 0, len(s.vars)), + } + + for i := range s.vars { + stack.vars = append(stack.vars, newDeclaredVarSet()) + maps.Copy(stack.vars[0].vs, s.vars[i].vs) + maps.Copy(stack.vars[0].occurrence, s.vars[i].occurrence) + maps.Copy(stack.vars[0].count, s.vars[i].count) + } + + stack.rewritten = maps.Clone(s.rewritten) + + return stack +} + +func (s *localDeclaredVars) Push() { + s.vars = append(s.vars, newDeclaredVarSet()) +} + +func (s *localDeclaredVars) Pop() *declaredVarSet { + sl := s.vars + curr := sl[len(sl)-1] + s.vars = sl[:len(sl)-1] + return curr +} + +func (s localDeclaredVars) Peek() *declaredVarSet { + return s.vars[len(s.vars)-1] +} + +func (s localDeclaredVars) Insert(x, y Var, occurrence varOccurrence) { + elem := s.vars[len(s.vars)-1] + elem.vs[x] = y + elem.occurrence[x] = occurrence + + elem.count[x] = 1 + + // If the variable has been rewritten (where x != y, with y being + // the generated value), store it in the map of rewritten vars. + // Assume that the generated values are unique for the compilation. + if !x.Equal(y) { + s.rewritten[y] = x + } +} + +func (s localDeclaredVars) Declared(x Var) (y Var, ok bool) { + for i := len(s.vars) - 1; i >= 0; i-- { + if y, ok = s.vars[i].vs[x]; ok { + return + } + } + return +} + +// Occurrence returns a flag that indicates whether x has occurred in the +// current scope. +func (s localDeclaredVars) Occurrence(x Var) varOccurrence { + return s.vars[len(s.vars)-1].occurrence[x] +} + +// GlobalOccurrence returns a flag that indicates whether x has occurred in the +// global scope. +func (s localDeclaredVars) GlobalOccurrence(x Var) (varOccurrence, bool) { + for i := len(s.vars) - 1; i >= 0; i-- { + if occ, ok := s.vars[i].occurrence[x]; ok { + return occ, true + } + } + return newVar, false +} + +// Seen marks x as seen by incrementing its counter +func (s localDeclaredVars) Seen(x Var) { + for i := len(s.vars) - 1; i >= 0; i-- { + dvs := s.vars[i] + if c, ok := dvs.count[x]; ok { + dvs.count[x] = c + 1 + return + } + } + + s.vars[len(s.vars)-1].count[x] = 1 +} + +// Count returns how many times x has been seen +func (s localDeclaredVars) Count(x Var) int { + for i := len(s.vars) - 1; i >= 0; i-- { + if c, ok := s.vars[i].count[x]; ok { + return c + } + } + + return 0 +} + +// rewriteLocalVars rewrites bodies to remove assignment/declaration +// expressions. For example: +// +// a := 1; p[a] +// +// Is rewritten to: +// +// __local0__ = 1; p[__local0__] +// +// During rewriting, assignees are validated to prevent use before declaration. +func rewriteLocalVars(g *localVarGenerator, stack *localDeclaredVars, used VarSet, body Body, strict bool) (Body, map[Var]Var, Errors) { + var errs Errors + body, errs = rewriteDeclaredVarsInBody(g, stack, used, body, errs, strict) + return body, stack.Peek().vs, errs +} + +func rewriteDeclaredVarsInBody(g *localVarGenerator, stack *localDeclaredVars, used VarSet, body Body, errs Errors, strict bool) (Body, Errors) { + var cpy Body + + for i := range body { + var expr *Expr + switch { + case body[i].IsAssignment(): + stack.assignment = true + expr, errs = rewriteDeclaredAssignment(g, stack, body[i], errs, strict) + case body[i].IsSome(): + expr, errs = rewriteSomeDeclStatement(g, stack, body[i], errs, strict) + case body[i].IsEvery(): + expr, errs = rewriteEveryStatement(g, stack, body[i], errs, strict) + default: + expr, errs = rewriteDeclaredVarsInExpr(g, stack, body[i], errs, strict) + } + if expr != nil { + cpy.Append(expr) + } + } + + // If the body only contained a var statement it will be empty at this + // point. Append true to the body to ensure that it's non-empty (zero length + // bodies are not supported.) + if len(cpy) == 0 { + cpy.Append(NewExpr(BooleanTerm(true))) + } + + errs = checkUnusedAssignedVars(body, stack, used, errs, strict) + return cpy, checkUnusedDeclaredVars(body, stack, used, cpy, errs) +} + +func checkUnusedAssignedVars(body Body, stack *localDeclaredVars, used VarSet, errs Errors, strict bool) Errors { + if !strict || len(errs) > 0 { + return errs + } + + dvs := stack.Peek() + + hasAssignedVars := false + for _, occ := range dvs.occurrence { + if occ == assignedVar { + hasAssignedVars = true + } + } + if !hasAssignedVars { + return errs + } + + unused := NewVarSet() + + for v, occ := range dvs.occurrence { + // A var that was assigned in this scope must have been seen (used) more than once (the time of assignment) in + // the same, or nested, scope to be counted as used. + if !v.IsWildcard() && stack.Count(v) <= 1 && occ == assignedVar { + unused.Add(dvs.vs[v]) + } + } + + rewrittenUsed := NewVarSet() + for v := range used { + if gv, ok := stack.Declared(v); ok { + rewrittenUsed.Add(gv) + } else { + rewrittenUsed.Add(v) + } + } + + unused = unused.Diff(rewrittenUsed) + if len(unused) == 0 { + return errs + } + + reversed := make(map[Var]Var, len(dvs.vs)) + for k, v := range dvs.vs { + reversed[v] = k + } + + for _, gv := range unused.Sorted() { + found := false + for i := range body { + if body[i].Vars(VarVisitorParams{}).Contains(gv) { + errs = append(errs, NewError(CompileErr, body[i].Loc(), "assigned var %v unused", reversed[gv])) + found = true + break + } + } + if !found { + errs = append(errs, NewError(CompileErr, body[0].Loc(), "assigned var %v unused", reversed[gv])) + } + } + + return errs +} + +func checkUnusedDeclaredVars(body Body, stack *localDeclaredVars, used VarSet, cpy Body, errs Errors) Errors { + + // NOTE(tsandall): Do not generate more errors if there are existing + // declaration errors. + if len(errs) > 0 { + return errs + } + + dvs := stack.Peek() + + hasDeclaredVars := false + for _, occ := range dvs.occurrence { + if occ == declaredVar { + hasDeclaredVars = true + } + } + if !hasDeclaredVars { + return errs + } + + declared := NewVarSet() + + for v, occ := range dvs.occurrence { + if occ == declaredVar { + declared.Add(dvs.vs[v]) + } + } + + bodyvars := cpy.Vars(VarVisitorParams{}) + + for v := range used { + if gv, ok := stack.Declared(v); ok { + bodyvars.Add(gv) + } else { + bodyvars.Add(v) + } + } + + dbv := declared.Diff(bodyvars) + if dbv.DiffCount(used) == 0 { + return errs + } + + reversed := make(map[Var]Var, len(dvs.vs)) + for k, v := range dvs.vs { + reversed[v] = k + } + + for _, gv := range dbv.Diff(used).Sorted() { + rv := reversed[gv] + if !rv.IsGenerated() { + // Scan through body exprs, looking for a match between the + // bad var's original name, and each expr's declared vars. + foundUnusedVarByName := false + for i := range body { + varsDeclaredInExpr := declaredVars(body[i]) + if varsDeclaredInExpr.Contains(rv) { + // TODO(philipc): Clean up the offset logic here when the parser + // reports more accurate locations. + errs = append(errs, NewError(CompileErr, body[i].Loc(), "declared var %v unused", rv)) + foundUnusedVarByName = true + break + } + } + // Default error location returned. + if !foundUnusedVarByName { + errs = append(errs, NewError(CompileErr, body[0].Loc(), "declared var %v unused", rv)) + } + } + } + + return errs +} + +func rewriteEveryStatement(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors, strict bool) (*Expr, Errors) { + e := expr.Copy() + every := e.Terms.(*Every) + + errs = rewriteDeclaredVarsInTermRecursive(g, stack, every.Domain, errs, strict) + + stack.Push() + defer stack.Pop() + + // if the key exists, rewrite + if every.Key != nil { + if v := every.Key.Value.(Var); !v.IsWildcard() { + gv, err := rewriteDeclaredVar(g, stack, v, declaredVar) + if err != nil { + return nil, append(errs, NewError(CompileErr, every.Loc(), err.Error())) //nolint:govet + } + every.Key.Value = gv + } + } else { // if the key doesn't exist, add dummy local + every.Key = NewTerm(g.Generate()) + } + + // value is always present + if v := every.Value.Value.(Var); !v.IsWildcard() { + gv, err := rewriteDeclaredVar(g, stack, v, declaredVar) + if err != nil { + return nil, append(errs, NewError(CompileErr, every.Loc(), err.Error())) //nolint:govet + } + every.Value.Value = gv + } + + used := NewVarSet() + every.Body, errs = rewriteDeclaredVarsInBody(g, stack, used, every.Body, errs, strict) + + return rewriteDeclaredVarsInExpr(g, stack, e, errs, strict) +} + +func rewriteSomeDeclStatement(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors, strict bool) (*Expr, Errors) { + e := expr.Copy() + decl := e.Terms.(*SomeDecl) + for i := range decl.Symbols { + switch v := decl.Symbols[i].Value.(type) { + case Var: + if _, err := rewriteDeclaredVar(g, stack, v, declaredVar); err != nil { + return nil, append(errs, NewError(CompileErr, decl.Loc(), err.Error())) //nolint:govet + } + case Call: + var key, val, container *Term + switch len(v) { + case 4: // member3 + key = v[1] + val = v[2] + container = v[3] + case 3: // member + key = NewTerm(g.Generate()) + val = v[1] + container = v[2] + } + + var rhs *Term + switch c := container.Value.(type) { + case Ref: + rhs = RefTerm(append(c, key)...) + default: + rhs = RefTerm(container, key) + } + e.Terms = []*Term{ + RefTerm(VarTerm(Equality.Name)), val, rhs, + } + + output := VarSet{} + + for _, v0 := range outputVarsForExprEq(e, container.Vars(), output).Sorted() { + if _, err := rewriteDeclaredVar(g, stack, v0, declaredVar); err != nil { + return nil, append(errs, NewError(CompileErr, decl.Loc(), err.Error())) //nolint:govet + } + } + return rewriteDeclaredVarsInExpr(g, stack, e, errs, strict) + } + } + return nil, errs +} + +func rewriteDeclaredVarsInExpr(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors, strict bool) (*Expr, Errors) { + vis := NewGenericVisitor(func(x any) bool { + var stop bool + switch x := x.(type) { + case *Term: + stop, errs = rewriteDeclaredVarsInTerm(g, stack, x, errs, strict) + case *With: + stop, errs = true, rewriteDeclaredVarsInWithRecursive(g, stack, x, errs, strict) + } + return stop + }) + vis.Walk(expr) + return expr, errs +} + +func rewriteDeclaredAssignment(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors, strict bool) (*Expr, Errors) { + + if expr.Negated { + errs = append(errs, NewError(CompileErr, expr.Location, "cannot assign vars inside negated expression")) + return expr, errs + } + + numErrsBefore := len(errs) + + if !validEqAssignArgCount(expr) { + return expr, errs + } + + // Rewrite terms on right hand side capture seen vars and recursively + // process comprehensions before left hand side is processed. Also + // rewrite with modifier. + errs = rewriteDeclaredVarsInTermRecursive(g, stack, expr.Operand(1), errs, strict) + + for _, w := range expr.With { + errs = rewriteDeclaredVarsInTermRecursive(g, stack, w.Value, errs, strict) + } + + // Rewrite vars on left hand side with unique names. Catch redeclaration + // and invalid term types here. + var vis func(t *Term) bool + + vis = func(t *Term) bool { + switch v := t.Value.(type) { + case Var: + if gv, err := rewriteDeclaredVar(g, stack, v, assignedVar); err != nil { + errs = append(errs, NewError(CompileErr, t.Location, err.Error())) //nolint:govet + } else { + t.Value = gv + } + return true + case *Array: + return false + case *object: + v.Foreach(func(_, v *Term) { + WalkTerms(v, vis) + }) + return true + case Ref: + if RootDocumentRefs.Contains(t) { + if gv, err := rewriteDeclaredVar(g, stack, v[0].Value.(Var), assignedVar); err != nil { + errs = append(errs, NewError(CompileErr, t.Location, err.Error())) //nolint:govet + } else { + t.Value = gv + } + return true + } + } + errs = append(errs, NewError(CompileErr, t.Location, "cannot assign to %v", ValueName(t.Value))) + return true + } + + WalkTerms(expr.Operand(0), vis) + + if len(errs) == numErrsBefore { + loc := expr.Operator()[0].Location + expr.SetOperator(RefTerm(VarTerm(Equality.Name).SetLocation(loc)).SetLocation(loc)) + } + + return expr, errs +} + +func rewriteDeclaredVarsInTerm(g *localVarGenerator, stack *localDeclaredVars, term *Term, errs Errors, strict bool) (bool, Errors) { + switch v := term.Value.(type) { + case Var: + if gv, ok := stack.Declared(v); ok { + term.Value = gv + stack.Seen(v) + } else if stack.Occurrence(v) == newVar { + stack.Insert(v, v, seenVar) + } + case Ref: + if RootDocumentRefs.Contains(term) { + x := v[0].Value.(Var) + if occ, ok := stack.GlobalOccurrence(x); ok && occ != seenVar { + gv, _ := stack.Declared(x) + term.Value = gv + } + + return true, errs + } + return false, errs + case Call: + ref := v[0] + WalkVars(ref, func(v Var) bool { + if gv, ok := stack.Declared(v); ok && !gv.Equal(v) { + // We will rewrite the ref of a function call, which is never ok since we don't have first-class functions. + errs = append(errs, NewError(CompileErr, term.Location, "called function %s shadowed", ref)) + return true + } + return false + }) + return false, errs + case *object: + cpy, _ := v.Map(func(k, v *Term) (*Term, *Term, error) { + kcpy := k.Copy() + errs = rewriteDeclaredVarsInTermRecursive(g, stack, kcpy, errs, strict) + errs = rewriteDeclaredVarsInTermRecursive(g, stack, v, errs, strict) + return kcpy, v, nil + }) + term.Value = cpy + case Set: + cpy, _ := v.Map(func(elem *Term) (*Term, error) { + elemcpy := elem.Copy() + errs = rewriteDeclaredVarsInTermRecursive(g, stack, elemcpy, errs, strict) + return elemcpy, nil + }) + term.Value = cpy + case *ArrayComprehension: + errs = rewriteDeclaredVarsInArrayComprehension(g, stack, v, errs, strict) + case *SetComprehension: + errs = rewriteDeclaredVarsInSetComprehension(g, stack, v, errs, strict) + case *ObjectComprehension: + errs = rewriteDeclaredVarsInObjectComprehension(g, stack, v, errs, strict) + default: + return false, errs + } + return true, errs +} + +func rewriteDeclaredVarsInTermRecursive(g *localVarGenerator, stack *localDeclaredVars, term *Term, errs Errors, strict bool) Errors { + WalkTerms(term, func(t *Term) bool { + var stop bool + stop, errs = rewriteDeclaredVarsInTerm(g, stack, t, errs, strict) + return stop + }) + return errs +} + +func rewriteDeclaredVarsInWithRecursive(g *localVarGenerator, stack *localDeclaredVars, w *With, errs Errors, strict bool) Errors { + // NOTE(sr): `with input as` and `with input.a.b.c as` are deliberately skipped here: `input` could + // have been shadowed by a local variable/argument but should NOT be replaced in the `with` target. + // + // We cannot drop `input` from the stack since it's conceivable to do `with input[input] as` where + // the second input is meant to be the local var. It's a terrible idea, but when you're shadowing + // `input` those might be your thing. + errs = rewriteDeclaredVarsInTermRecursive(g, stack, w.Target, errs, strict) + if sdwInput, ok := stack.Declared(InputRootDocument.Value.(Var)); ok { // Was "input" shadowed... + switch value := w.Target.Value.(type) { + case Var: + if sdwInput.Equal(value) { // ...and replaced? If so, fix it + w.Target.Value = InputRootRef + } + case Ref: + if sdwInput.Equal(value[0].Value.(Var)) { + w.Target.Value.(Ref)[0].Value = InputRootDocument.Value + } + } + } + // No special handling of the `with` value + return rewriteDeclaredVarsInTermRecursive(g, stack, w.Value, errs, strict) +} + +func rewriteDeclaredVarsInArrayComprehension(g *localVarGenerator, stack *localDeclaredVars, v *ArrayComprehension, errs Errors, strict bool) Errors { + used := NewVarSet() + used.Update(v.Term.Vars()) + + stack.Push() + v.Body, errs = rewriteDeclaredVarsInBody(g, stack, used, v.Body, errs, strict) + errs = rewriteDeclaredVarsInTermRecursive(g, stack, v.Term, errs, strict) + stack.Pop() + return errs +} + +func rewriteDeclaredVarsInSetComprehension(g *localVarGenerator, stack *localDeclaredVars, v *SetComprehension, errs Errors, strict bool) Errors { + used := NewVarSet() + used.Update(v.Term.Vars()) + + stack.Push() + v.Body, errs = rewriteDeclaredVarsInBody(g, stack, used, v.Body, errs, strict) + errs = rewriteDeclaredVarsInTermRecursive(g, stack, v.Term, errs, strict) + stack.Pop() + return errs +} + +func rewriteDeclaredVarsInObjectComprehension(g *localVarGenerator, stack *localDeclaredVars, v *ObjectComprehension, errs Errors, strict bool) Errors { + used := NewVarSet() + used.Update(v.Key.Vars()) + used.Update(v.Value.Vars()) + + stack.Push() + v.Body, errs = rewriteDeclaredVarsInBody(g, stack, used, v.Body, errs, strict) + errs = rewriteDeclaredVarsInTermRecursive(g, stack, v.Key, errs, strict) + errs = rewriteDeclaredVarsInTermRecursive(g, stack, v.Value, errs, strict) + stack.Pop() + return errs +} + +func rewriteDeclaredVar(g *localVarGenerator, stack *localDeclaredVars, v Var, occ varOccurrence) (gv Var, err error) { + switch stack.Occurrence(v) { + case seenVar: + return gv, fmt.Errorf("var %v referenced above", v) + case assignedVar: + return gv, fmt.Errorf("var %v assigned above", v) + case declaredVar: + return gv, fmt.Errorf("var %v declared above", v) + case argVar: + return gv, fmt.Errorf("arg %v redeclared", v) + } + gv = g.Generate() + stack.Insert(v, gv, occ) + return +} + +// rewriteWithModifiersInBody will rewrite the body so that with modifiers do +// not contain terms that require evaluation as values. If this function +// encounters an invalid with modifier target then it will raise an error. +func rewriteWithModifiersInBody(c *Compiler, unsafeBuiltinsMap map[string]struct{}, f *equalityFactory, body Body) (Body, *Error) { + var result Body + for i := range body { + exprs, err := rewriteWithModifier(c, unsafeBuiltinsMap, f, body[i]) + if err != nil { + return nil, err + } + if len(exprs) > 0 { + for _, expr := range exprs { + result.Append(expr) + } + } else { + result.Append(body[i]) + } + } + return result, nil +} + +func rewriteWithModifier(c *Compiler, unsafeBuiltinsMap map[string]struct{}, f *equalityFactory, expr *Expr) ([]*Expr, *Error) { + + var result []*Expr + for i := range expr.With { + eval, err := validateWith(c, unsafeBuiltinsMap, expr, i) + if err != nil { + return nil, err + } + + if eval { + eq := f.Generate(expr.With[i].Value) + result = append(result, eq) + expr.With[i].Value = eq.Operand(0) + } + } + + return append(result, expr), nil +} + +func validateWith(c *Compiler, unsafeBuiltinsMap map[string]struct{}, expr *Expr, i int) (bool, *Error) { + target, value := expr.With[i].Target, expr.With[i].Value + + // Ensure that values that are built-ins are rewritten to Ref (not Var) + if v, ok := value.Value.(Var); ok { + if _, ok := c.builtins[v.String()]; ok { + value.Value = Ref([]*Term{NewTerm(v)}) + } + } + isBuiltinRefOrVar, err := isBuiltinRefOrVar(c.builtins, unsafeBuiltinsMap, target) + if err != nil { + 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) + targetNode := c.RuleTree + for i := range len(ref) - 1 { + 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)") + } + targetNode = child + } + + 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 := 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 { + return false, err // err may be nil + } + } + } + } + } + + // 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: + + // NOTE(sr): first we ensure that parsed Var builtins (`count`, `concat`, etc) + // are rewritten to their proper Ref convention + if v, ok := target.Value.(Var); ok { + target.Value = Ref([]*Term{NewTerm(v)}) + } + + targetRef := target.Value.(Ref) + bi := c.builtins[targetRef.String()] // safe because isBuiltinRefOrVar checked this + if err := validateWithBuiltinTarget(bi, targetRef, target.Loc()); err != nil { + return false, err + } + + 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) + } + return requiresEval(value), nil +} + +func validateWithBuiltinTarget(bi *Builtin, target Ref, loc *location.Location) *Error { + switch bi.Name { + case Equality.Name, + RegoMetadataChain.Name, + RegoMetadataRule.Name: + return NewError(CompileErr, loc, "with keyword replacing built-in function: replacement of %q invalid", bi.Name) + } + + switch { + case target.HasPrefix(Ref([]*Term{VarTerm("internal")})): + return NewError(CompileErr, loc, "with keyword replacing built-in function: replacement of internal function %q invalid", target) + + case bi.Relation: + return NewError(CompileErr, loc, "with keyword replacing built-in function: target must not be a relation") + + case bi.Decl.Result() == nil: + return NewError(CompileErr, loc, "with keyword replacing built-in function: target must not be a void function") + } + return nil +} + +func validateWithFunctionValue(bs map[string]*Builtin, unsafeMap map[string]struct{}, ruleTree *TreeNode, value *Term) (bool, *Error) { + if v, ok := value.Value.(Ref); ok { + if ruleTree.Find(v) != nil { // ref exists in rule tree + return true, nil + } + } + return isBuiltinRefOrVar(bs, unsafeMap, value) +} + +func isInputRef(term *Term) bool { + if ref, ok := term.Value.(Ref); ok { + if ref.HasPrefix(InputRootRef) { + return true + } + } + return false +} + +func isDataRef(term *Term) bool { + if ref, ok := term.Value.(Ref); ok { + if ref.HasPrefix(DefaultRootRef) { + return true + } + } + return false +} + +func isBuiltinRefOrVar(bs map[string]*Builtin, unsafeBuiltinsMap map[string]struct{}, term *Term) (bool, *Error) { + switch v := term.Value.(type) { + case Ref, Var: + if _, ok := unsafeBuiltinsMap[v.String()]; ok { + return false, NewError(CompileErr, term.Location, "with keyword replacing built-in function: target must not be unsafe: %q", v) + } + _, ok := bs[v.String()] + return ok, nil + } + return false, nil +} + +func isVirtual(node *TreeNode, ref Ref) bool { + for i := range ref { + child := node.Child(ref[i].Value) + if child == nil { + return false + } else if len(child.Values) > 0 { + return true + } + node = child + } + return true +} + +func safetyErrorSlice(unsafe unsafeVars, rewritten map[Var]Var) (result Errors) { + if len(unsafe) == 0 { + return + } + + for _, pair := range unsafe.Vars() { + v := pair.Var + if w, ok := rewritten[v]; ok { + v = w + } + if !v.IsGenerated() { + if _, ok := allFutureKeywords[string(v)]; ok { + result = append(result, NewError(UnsafeVarErr, pair.Loc, + "var %[1]v is unsafe (hint: `import future.keywords.%[1]v` to import a future keyword)", v)) + continue + } + result = append(result, NewError(UnsafeVarErr, pair.Loc, "var %v is unsafe", v)) + } + } + + if len(result) > 0 { + return + } + + // If the expression contains unsafe generated variables, report which + // expressions are unsafe instead of the variables that are unsafe (since + // the latter are not meaningful to the user.) + pairs := unsafe.Slice() + + slices.SortFunc(pairs, func(a, b unsafePair) int { + return a.Expr.Location.Compare(b.Expr.Location) + }) + + // Report at most one error per generated variable. + seen := NewVarSet() + + for _, expr := range pairs { + before := len(seen) + for v := range expr.Vars { + if v.IsGenerated() { + seen.Add(v) + } + } + if len(seen) > before { + result = append(result, NewError(UnsafeVarErr, expr.Expr.Location, "expression is unsafe")) + } + } + + return +} + +func checkUnsafeBuiltins(unsafeBuiltinsMap map[string]struct{}, node any) Errors { + var errs Errors + WalkExprs(node, func(x *Expr) bool { + if x.IsCall() { + operator := x.Operator().String() + if _, ok := unsafeBuiltinsMap[operator]; ok { + errs = append(errs, NewError(TypeErr, x.Loc(), "unsafe built-in function calls in expression: %v", operator)) + } + } + return false + }) + return errs +} + +func rewriteVarsInRef(vars ...map[Var]Var) varRewriter { + return func(node Ref) Ref { + i, _ := TransformVars(node, func(v Var) (Value, error) { + for _, m := range vars { + if u, ok := m[v]; ok { + return u, nil + } + } + return v, nil + }) + return i.(Ref) + } +} + +// NOTE(sr): This is duplicated with compile/compile.go; but moving it into another location +// would cause a circular dependency -- the refSet definition needs ast.Ref. If we make it +// public in the ast package, the compile package could take it from there, but it would also +// increase our public interface. Let's reconsider if we need it in a third place. +type refSet struct { + s []Ref +} + +func newRefSet(x ...Ref) *refSet { + result := &refSet{} + for i := range x { + result.AddPrefix(x[i]) + } + return result +} + +// ContainsPrefix returns true if r is prefixed by any of the existing refs in the set. +func (rs *refSet) ContainsPrefix(r Ref) bool { + return slices.ContainsFunc(rs.s, r.HasPrefix) +} + +// AddPrefix inserts r into the set if r is not prefixed by any existing +// refs in the set. If any existing refs are prefixed by r, those existing +// refs are removed. +func (rs *refSet) AddPrefix(r Ref) { + if rs.ContainsPrefix(r) { + return + } + cpy := []Ref{r} + for i := range rs.s { + if !rs.s[i].HasPrefix(r) { + cpy = append(cpy, rs.s[i]) + } + } + rs.s = cpy +} + +// Sorted returns a sorted slice of terms for refs in the set. +func (rs *refSet) Sorted() []*Term { + terms := make([]*Term, len(rs.s)) + for i := range rs.s { + terms[i] = NewTerm(rs.s[i]) + } + slices.SortFunc(terms, TermValueCompare) + return terms +} diff --git a/third_party/opa/v1/ast/compile_bench_test.go b/third_party/opa/v1/ast/compile_bench_test.go new file mode 100644 index 000000000000..de532b203eb5 --- /dev/null +++ b/third_party/opa/v1/ast/compile_bench_test.go @@ -0,0 +1,47 @@ +package ast + +import ( + "strconv" + "testing" +) + +func BenchmarkRewriteDynamics(b *testing.B) { + + // The choice of query to use is somewhat arbitrary. This query is + // representative of the ones that result from partial evaluation on IAM + // data models (e.g., a triple glob match on subject/action/resource.) + body := MustParseBody(` + glob.match("a:*", [":"], input.abcdef.x12345); + glob.match("a:*", [":"], input.abcdef.y12345); + glob.match("a:*", [":"], input.abcdef.z12345) + `) + sizes := []int{1, 10, 100, 1000, 10000, 100000} + queries := makeQueriesForRewriteDynamicsBenchmark(sizes, body) + + for i := range sizes { + b.Run(strconv.Itoa(sizes[i]), func(b *testing.B) { + factory := newEqualityFactory(newLocalVarGenerator("q", nil)) + b.ResetTimer() + for range b.N { + for _, body := range queries[i] { + rewriteDynamics(factory, body) + } + } + }) + } + +} + +func makeQueriesForRewriteDynamicsBenchmark(sizes []int, body Body) [][]Body { + + queries := make([][]Body, len(sizes)) + + for i := range queries { + queries[i] = make([]Body, sizes[i]) + for j := range sizes[i] { + queries[i][j] = body.Copy() + } + } + + return queries +} diff --git a/third_party/opa/v1/ast/compile_test.go b/third_party/opa/v1/ast/compile_test.go new file mode 100644 index 000000000000..04514957eb05 --- /dev/null +++ b/third_party/opa/v1/ast/compile_test.go @@ -0,0 +1,11562 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "maps" + "reflect" + "slices" + "sort" + "strconv" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestOutputVarsForNode(t *testing.T) { + + tests := []struct { + note string + query string + arities map[string]int + extraSafe string + exp string + }{ + { + note: "single var", + query: "x", + exp: "set()", + }, + { + note: "trivial eq", + query: "x = 1", + exp: "{x}", + }, + { + note: "negation", + query: "not x = 1", + exp: "set()", + }, + { + note: "embedded array", + query: "[x, [1]] = [1, [y]]", + exp: "{x, y}", + }, + { + note: "embedded sets", + query: "{x, [1]} = {1, [y]}", + exp: "set()", + }, + { + note: "embedded object values", + query: `{"foo": x, "bar": {"baz": 1}} = {"foo": 1, "bar": {"baz": y}}`, + exp: "{x, y}", + }, + { + note: "object keys are like sets", + query: `{"foo": x} = {y: 1}`, + exp: `set()`, + }, + { + note: "built-ins", + query: `count([1,2,3], x)`, + exp: "{x}", + arities: map[string]int{"count": 1}, + }, + { + note: "built-ins - input args", + query: `count(x)`, + exp: "set()", + }, + { + note: "functions - no arity", + query: `f(1,x)`, + arities: map[string]int{}, + exp: "set()", + }, + { + note: "functions", + query: `f(1,x)`, + arities: map[string]int{"f": 1}, + exp: "{x}", + }, + { + note: "functions - input args", + query: `f(1,x)`, + arities: map[string]int{"f": 2}, + exp: "set()", + }, + { + note: "functions - embedded refs", + query: `f(data.p[x], y)`, + arities: map[string]int{"f": 1}, + exp: `{x, y}`, + }, + { + note: "functions - skip ref head", + query: `f(x[1])`, + arities: map[string]int{"f": 1}, + exp: `set()`, + }, + { + note: "functions - skip sets", + query: `f(1, {x})`, + arities: map[string]int{"f": 1}, + exp: `set()`, + }, + { + note: "functions - skip object keys", + query: `f(1, {x: 1})`, + arities: map[string]int{"f": 1}, + exp: `set()`, + }, + { + note: "functions - skip closures", + query: `f(1, {x | x = 1})`, + arities: map[string]int{"f": 1}, + exp: `set()`, + }, + { + note: "functions - unsafe input", + query: `f(x, y)`, + arities: map[string]int{"f": 1}, + exp: `set()`, + }, + { + note: "with keyword", + query: "1 with input as y", + exp: "set()", + }, + { + note: "with keyword - unsafe", + query: "x = 1 with input as y", + exp: "set()", + }, + { + note: "with keyword - safe", + query: "x = 1 with input as y", + extraSafe: "{y}", + exp: "{x}", + }, + { + note: "ref operand", + query: "data.p[x]", + exp: "{x}", + }, + { + note: "ref operand - unsafe head", + query: "p[x]", + exp: "set()", + }, + { + note: "ref operand - negation", + query: "not data.p[x]", + exp: "set()", + }, + { + note: "ref operand - nested", + query: "data.p[data.q[x]]", + exp: "{x}", + }, + { + note: "comprehension", + query: "[x | x = 1]", + exp: "set()", + }, + { + note: "comprehension containing safe ref", + query: "[x | data.p[x]]", + exp: "set()", + }, + { + note: "accumulate on exprs", + query: "x = 1; y = x; z = y", + exp: "{x, y, z}", + }, + { + note: "composite head", + query: "{1, 2}[1] = x", + exp: `{x}`, + }, + { + note: "composite head", + query: "x = 1; {x, 2}[1] = y", + exp: `{x, y}`, + }, + { + note: "composite head", + query: "{x, 2}[1] = y", + exp: `set()`, + }, + { + note: "nested function calls", + query: `z = "abc"; x = split(z, "")[y]`, + exp: `{x, y, z}`, + }, + { + note: "unsafe nested function calls", + query: `z = "abc"; x = split(z, a)[y]`, + exp: `{z}`, + }, + { + note: "every: simple: no output vars", + query: `every k, v in [1, 2] { k < v }`, + exp: `set()`, + }, + { + note: "every: output vars in domain", + query: `xs = []; every k, v in xs[i] { k < v }`, + exp: `{xs, i}`, + }, + { + note: "every: output vars in body", + query: `every k, v in [] { k < v; i = 1 }`, + exp: `set()`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + + opts := ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true} + body, err := ParseBodyWithOpts(tc.query, opts) + if err != nil { + t.Fatal(err) + } + arity := func(r Ref) int { + a, ok := tc.arities[r.String()] + if !ok { + return -1 + } + return a + } + + safe := ReservedVars.Copy() + + if tc.extraSafe != "" { + MustParseTerm(tc.extraSafe).Value.(Set).Foreach(func(x *Term) { + safe.Add(x.Value.(Var)) + }) + } + + vs := NewSet() + + for v := range outputVarsForBody(body, arity, safe) { + vs.Add(NewTerm(v)) + } + + exp := MustParseTerm(tc.exp) + + if exp.Value.Compare(vs) != 0 { + t.Fatalf("Expected %v but got %v", exp, vs) + } + }) + } + +} + +func TestModuleTree(t *testing.T) { + + mods := getCompilerTestModules() // 7 modules + mods["system-mod"] = MustParseModule(` + package system.foo + + p = 1 + `) + mods["non-system-mod"] = MustParseModule(` + package user.system + + p = 1 + `) + mods["dots-in-heads"] = MustParseModule(` + package dots + + a.b.c = 12 + d.e.f.g = 34 + `) + tree := NewModuleTree(mods) + expectedSize := 10 + + if tree.Size() != expectedSize { + t.Fatalf("Expected %v but got %v modules", expectedSize, tree.Size()) + } + + if !tree.Children[Var("data")].Children[String("system")].Hide { + t.Fatalf("Expected system node to be hidden") + } + + if tree.Children[Var("data")].Children[String("system")].Children[String("foo")].Hide { + t.Fatalf("Expected system.foo node to be visible") + } + + if tree.Children[Var("data")].Children[String("user")].Children[String("system")].Hide { + t.Fatalf("Expected user.system node to be visible") + } +} + +func TestCompilerGetExports(t *testing.T) { + tests := []struct { + note string + modules []*Module + exports map[string][]string + }{ + { + note: "simple", + modules: modules(`package p + r = 1`), + exports: map[string][]string{"data.p": {"r"}}, + }, + { + note: "simple single-value ref rule", + modules: modules(`package p + q.r.s = 1`), + exports: map[string][]string{"data.p": {"q.r.s"}}, + }, + { + note: "var key single-value ref rule", + modules: modules(`package p + q.r[s] = 1 if { s := "foo" }`), + exports: map[string][]string{"data.p": {"q.r"}}, + }, + { + note: "simple multi-value ref rule", + modules: modules(`package p + q.r.s contains 1 if { true }`), + exports: map[string][]string{"data.p": {"q.r.s"}}, + }, + { + note: "two simple, multiple rules", + modules: modules(`package p + r = 1 + s = 11`, + `package q + x = 2 + y = 22`), + exports: map[string][]string{"data.p": {"r", "s"}, "data.q": {"x", "y"}}, + }, + { + note: "ref head + simple, multiple rules", + modules: modules(`package p.a.b.c + r = 1 + s = 11`, + `package q + a.b.x = 2 + a.b.c.y = 22`), + exports: map[string][]string{ + "data.p.a.b.c": {"r", "s"}, + "data.q": {"a.b.x", "a.b.c.y"}, + }, + }, + { + note: "two ref head, multiple rules", + modules: modules(`package p.a.b.c + r = 1 + s = 11`, + `package p + a.b.x = 2 + a.b.c.y = 22`), + exports: map[string][]string{ + "data.p.a.b.c": {"r", "s"}, + "data.p": {"a.b.x", "a.b.c.y"}, + }, + }, + { + note: "single-value rule with number key", + modules: modules(`package p + q[1] = 1 + q[2] = 2`), + exports: map[string][]string{ + "data.p": {"q[1]", "q[2]"}, // TODO(sr): is this really what we want? + }, + }, + { + note: "single-value (ref) rule with number key", + modules: modules(`package p + a.b.q[1] = 1 + a.b.q[2] = 2`), + exports: map[string][]string{ + "data.p": {"a.b.q[1]", "a.b.q[2]"}, + }, + }, + { + note: "single-value (ref) rule with var key", + modules: modules(`package p + a.b.q[x] = y if { x := 1; y := true } + a.b.q[2] = 2`), + exports: map[string][]string{ + "data.p": {"a.b.q", "a.b.q[2]"}, // TODO(sr): GroundPrefix? right thing here? + }, + }, + { // NOTE(sr): An ast.Module can be constructed in various ways, this is to assert that + // our compilation process doesn't explode here if we're fed a Rule that has no Ref. + note: "synthetic", + modules: func() []*Module { + ms := modules(`package p + r = 1`) + ms[0].Rules[0].Head.Reference = nil + return ms + }(), + exports: map[string][]string{"data.p": {"r"}}, + }, + // TODO(sr): add multi-val rule, and ref-with-var single-value rule. + } + + hashMap := func(ms map[string][]string) *util.HasherMap[Ref, []Ref] { + rules := util.NewHasherMap[Ref, []Ref](RefEqual) + for r, rs := range ms { + refs := make([]Ref, len(rs)) + for i := range rs { + refs[i] = toRef(rs[i]) + } + rules.Put(MustParseRef(r), refs) + } + return rules + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler() + for i, m := range tc.modules { + c.Modules[strconv.Itoa(i)] = m + c.sorted = append(c.sorted, strconv.Itoa(i)) + } + if exp, act := hashMap(tc.exports), c.getExports(); !refMapEqual(exp, act) { + t.Errorf("expected %v, got %v", exp, act) + } + }) + } +} + +func refMapEqual(a, b *util.HasherMap[Ref, []Ref]) bool { + if a.Len() != b.Len() { + return false + } + return !a.Iter(func(k Ref, v []Ref) bool { + v2, ok := b.Get(k) + if !ok { + return true + } + if !refSliceEqual(v, v2) { + return true + } + return false + }) +} + +func toRef(s string) Ref { + switch t := MustParseTerm(s).Value.(type) { + case Var: + return Ref{NewTerm(t)} + case Ref: + return t + default: + panic("unreachable") + } +} + +func TestCompilerCheckRuleHeadRefs(t *testing.T) { + tests := []struct { + note string + modules []*Module + expected *Rule + err string + }{ + { + note: "ref contains var", + modules: modules( + `package x + p.q[i].r = 1 if { i := 10 }`, + ), + }, + { + note: "valid: ref is single-value rule with var key", + modules: modules( + `package x + p.q.r[i] if { i := 10 }`, + ), + }, + { + note: "valid: ref is single-value rule with var key and value", + modules: modules( + `package x + p.q.r[i] = j if { i := 10; j := 11 }`, + ), + }, + { + note: "valid: ref is single-value rule with var key and static value", + modules: modules( + `package x + p.q.r[i] = "ten" if { i := 10 }`, + ), + }, + { + note: "valid: ref is single-value rule with number key", + modules: modules( + `package x + p.q.r[1] if { true }`, + ), + }, + { + note: "valid: ref is single-value rule with boolean key", + modules: modules( + `package x + p.q.r[true] if { true }`, + ), + }, + { + note: "valid: ref is single-value rule with null key", + modules: modules( + `package x + p.q.r[null] if { true }`, + ), + }, + { + note: "valid: ref is single-value rule with set literal key", + modules: modules( + `package x + p.q.r[set()] if { true }`, + ), + }, + { + note: "valid: ref is single-value rule with array literal key", + modules: modules( + `package x + p.q.r[[]] if { true }`, + ), + }, + { + note: "valid: ref is single-value rule with object literal key", + modules: modules( + `package x + p.q.r[{}] if { true }`, + ), + }, + { + note: "valid: ref is single-value rule with ref key", + modules: modules( + `package x + x := [1,2,3] + p.q.r[x[i]] if { i := 0}`, + ), + }, + { + note: "invalid: ref in ref", + modules: modules( + `package x + p.q[arr[0]].r if { i := 10 }`, + ), + }, + { + note: "invalid: non-string in ref (not last position)", + modules: modules( + `package x + p.q[10].r if { true }`, + ), + }, + { + note: "valid: multi-value with var key", + modules: modules( + `package x + p.q.r contains i if i := 10`, + ), + }, + { + note: "rewrite: single-value with non-var key (ref)", + modules: modules( + `package x + p.q.r[y.z] if y := {"z": "a"}`, + ), + expected: MustParseRule(`p.q.r[__local0__] { y := {"z": "a"}; __local0__ = y.z }`), + }, + { + note: "rewrite: single-value with non-var ref term", + modules: modules( + `package x + p.q[y.z].r if y := {"z": "a"}`, + ), + expected: MustParseRule(`p.q[__local0__].r { y := {"z": "a"}; __local0__ = y.z }`), + }, + { + note: "rewrite: single-value with non-var ref term and key", + modules: modules( + `package x + p.q[a.b][c.d] if y := {"z": "a"}`, + ), + expected: MustParseRule(`p.q[__local0__][__local1__] { y := {"z": "a"}; __local0__ = a.b; __local1__ = c.d }`), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + mods := make(map[string]*Module, len(tc.modules)) + for i, m := range tc.modules { + mods[strconv.Itoa(i)] = m + } + c := NewCompiler() + c.Modules = mods + compileStages(c, c.rewriteRuleHeadRefs) + if tc.err != "" { + assertCompilerErrorStrings(t, c, []string{tc.err}) + } else { + if len(c.Errors) > 0 { + t.Fatalf("expected no errors, got %v", c.Errors) + } + if tc.expected != nil { + assertRulesEqual(t, tc.expected, mods["0"].Rules[0]) + } + } + }) + } +} + +func TestRuleTreeWithDotsInHeads(t *testing.T) { + + // TODO(sr): multi-val with var key in ref + tests := []struct { + note string + modules []*Module + size int // expected tree size = number of leaves + depth int // expected tree depth + }{ + { + note: "two modules, same package, one rule each", + modules: modules( + `package x + p.q.r = 1`, + `package x + p.q.w = 2`, + ), + size: 2, + }, + { + note: "two modules, sub-package, one rule each", + modules: modules( + `package x + p.q.r = 1`, + `package x.p + q.w.z = 2`, + ), + size: 2, + }, + { + note: "three modules, sub-package, incl simple rule", + modules: modules( + `package x + p.q.r = 1`, + `package x.p + q.w.z = 2`, + `package x.p.q.w + y = 3`, + ), + size: 3, + }, + { + note: "simple: two modules", + modules: modules( + `package x + p.q.r = 1`, + `package y + p.q.w = 2`, + ), + size: 2, + }, + { + note: "conflict: one module", + modules: modules( + `package q + p[x] = 1 + p = 2`, + ), + size: 2, + }, + { + note: "conflict: two modules", + modules: modules( + `package q + p.r.s[x] = 1`, + `package q.p + r.s = 2 if true`, + ), + size: 2, + }, + { + note: "simple: two modules, one using ref head, one package path", + modules: modules( + `package x + p.q.r = 1 if { input == 1 }`, + `package x.p.q + r = 2 if { input == 2 }`, + ), + size: 2, + }, + { + note: "conflict: two modules, both using ref head, different package paths", + modules: modules( + `package x + p.q.r = 1 if { input == 1 }`, // x.p.q.r = 1 + `package x.p + q.r.s = 2 if { input == 2 }`, // x.p.q.r.s = 2 + ), + size: 2, + }, + { + note: "overlapping: one module, two ref head", + modules: modules( + `package x + p.q.r = 1 + p.q.w.v = 2`, + ), + size: 2, + depth: 6, + }, + { + note: "last ref term != string", + modules: modules( + `package x + p.q.w[1] = 2 + p.q.w[{"foo": "baz"}] = 20 + p.q.x[true] = false + p.q.x[y] = y if { y := "y" }`, + ), + size: 4, + depth: 6, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler() + for i, m := range tc.modules { + c.Modules[strconv.Itoa(i)] = m + c.sorted = append(c.sorted, strconv.Itoa(i)) + } + compileStages(c, c.setRuleTree) + if len(c.Errors) > 0 { + t.Fatal(c.Errors) + } + tree := c.RuleTree + tree.DepthFirst(func(n *TreeNode) bool { + t.Log(n) + if !sort.SliceIsSorted(n.Sorted, func(i, j int) bool { + return n.Sorted[i].Compare(n.Sorted[j]) < 0 + }) { + t.Errorf("expected sorted to be sorted: %v", n.Sorted) + } + return false + }) + if tc.depth > 0 { + if exp, act := tc.depth, depth(tree); exp != act { + t.Errorf("expected tree depth %d, got %d", exp, act) + } + } + if exp, act := tc.size, tree.Size(); exp != act { + t.Errorf("expected tree size %d, got %d", exp, act) + } + }) + } +} + +func TestRuleIndices(t *testing.T) { + tests := []struct { + note string + modules []*Module + exp map[string][]Ref + }{ + { + note: "regression test for #6930 (no if)", + modules: modules( + `package test + + p.q contains "foo" + + p[q] := r if { + q := "bar" + r := "baz" + }`, + ), + exp: map[string][]Ref{ + "data.test.p": { + MustParseRef("p.q"), + MustParseRef("p[__local0__]"), + }, + }, + }, + { + note: "regression test for #6930 (if)", + modules: modules( + `package test + + p.q contains "foo" + + p[q] := r if { + q := "bar" + r := "baz" + }`, + ), + exp: map[string][]Ref{ + "data.test.p": { + MustParseRef("p.q"), + MustParseRef("p[__local0__]"), + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler() + for i, m := range tc.modules { + c.Modules[strconv.Itoa(i)] = m + c.sorted = append(c.sorted, strconv.Itoa(i)) + } + compileStages(c, c.buildRuleIndices) + + for k, expIndex := range tc.exp { + kref := MustParseRef(k) + i, ok := c.ruleIndices.Get(kref) + if i == nil || !ok { + t.Fatalf("expected rule indices for %v", k) + } + index := i.(*baseDocEqIndex) + for _, expRef := range expIndex { + found := false + for _, r := range index.root.rules { + if r.rule.Head.Ref().Equal(expRef) { + found = true + break + } + } + if !found { + t.Errorf("expected rule %v in index for %v", expRef, k) + } + } + } + }) + } +} + +func TestRuleTreeWithVars(t *testing.T) { + opts := ParserOptions{ + RegoVersion: RegoV0, + AllFutureKeywords: true, + unreleasedKeywords: true, + } + + t.Run("simple single-value rule", func(t *testing.T) { + mod0 := `package a.b +c.d.e = 1 if true` + + mods := map[string]*Module{"0.rego": MustParseModuleWithOpts(mod0, opts)} + tree := NewRuleTree(NewModuleTree(mods)) + + node := tree.Find(MustParseRef("data.a.b.c.d.e")) + if node == nil { + t.Fatal("expected non-nil leaf node") + } + if exp, act := 1, len(node.Values); exp != act { + t.Errorf("expected %d values, found %d", exp, act) + } + if exp, act := 0, len(node.Children); exp != act { + t.Errorf("expected %d children, found %d", exp, act) + } + if exp, act := MustParseRef("c.d.e"), node.Values[0].(*Rule).Head.Ref(); !exp.Equal(act) { + t.Errorf("expected rule ref %v, found %v", exp, act) + } + }) + + t.Run("two single-value rules", func(t *testing.T) { + mod0 := `package a.b +c.d.e = 1 if true` + mod1 := `package a.b.c +d.e = 2 if true` + + mods := map[string]*Module{ + "0.rego": MustParseModuleWithOpts(mod0, opts), + "1.rego": MustParseModuleWithOpts(mod1, opts), + } + tree := NewRuleTree(NewModuleTree(mods)) + + node := tree.Find(MustParseRef("data.a.b.c.d.e")) + if node == nil { + t.Fatal("expected non-nil leaf node") + } + if exp, act := 2, len(node.Values); exp != act { + t.Errorf("expected %d values, found %d", exp, act) + } + if exp, act := 0, len(node.Children); exp != act { + t.Errorf("expected %d children, found %d", exp, act) + } + if exp, act := MustParseRef("c.d.e"), node.Values[0].(*Rule).Head.Ref(); !exp.Equal(act) { + t.Errorf("expected rule ref %v, found %v", exp, act) + } + if exp, act := MustParseRef("d.e"), node.Values[1].(*Rule).Head.Ref(); !exp.Equal(act) { + t.Errorf("expected rule ref %v, found %v", exp, act) + } + }) + + t.Run("one multi-value rule, one single-value, with var", func(t *testing.T) { + mod0 := `package a.b +c.d.e.g contains 1 if true` + mod1 := `package a.b.c +d.e.f = 2 if true` + + mods := map[string]*Module{ + "0.rego": MustParseModuleWithOpts(mod0, opts), + "1.rego": MustParseModuleWithOpts(mod1, opts), + } + tree := NewRuleTree(NewModuleTree(mods)) + + // var-key rules should be included in the results + node := tree.Find(MustParseRef("data.a.b.c.d.e.g")) + if node == nil { + t.Fatal("expected non-nil leaf node") + } + if exp, act := 1, len(node.Values); exp != act { + t.Fatalf("expected %d values, found %d", exp, act) + } + if exp, act := 0, len(node.Children); exp != act { + t.Fatalf("expected %d children, found %d", exp, act) + } + node = tree.Find(MustParseRef("data.a.b.c.d.e.f")) + if node == nil { + t.Fatal("expected non-nil leaf node") + } + if exp, act := 1, len(node.Values); exp != act { + t.Fatalf("expected %d values, found %d", exp, act) + } + if exp, act := MustParseRef("d.e.f"), node.Values[0].(*Rule).Head.Ref(); !exp.Equal(act) { + t.Errorf("expected rule ref %v, found %v", exp, act) + } + }) + + t.Run("two multi-value rules, back compat", func(t *testing.T) { + mod0 := `package a +b[c] { c := "foo" }` + mod1 := `package a +b[d] { d := "bar" }` + + mods := map[string]*Module{ + "0.rego": MustParseModuleWithOpts(mod0, opts), + "1.rego": MustParseModuleWithOpts(mod1, opts), + } + tree := NewRuleTree(NewModuleTree(mods)) + + node := tree.Find(MustParseRef("data.a.b")) + if node == nil { + t.Fatal("expected non-nil leaf node") + } + if exp, act := 2, len(node.Values); exp != act { + t.Fatalf("expected %d values, found %d: %v", exp, act, node.Values) + } + if exp, act := 0, len(node.Children); exp != act { + t.Errorf("expected %d children, found %d", exp, act) + } + if exp, act := (Ref{VarTerm("b")}), node.Values[0].(*Rule).Head.Ref(); !exp.Equal(act) { + t.Errorf("expected rule ref %v, found %v", exp, act) + } + if act := node.Values[0].(*Rule).Head.Value; act != nil { + t.Errorf("expected rule value nil, found %v", act) + } + if exp, act := VarTerm("c"), node.Values[0].(*Rule).Head.Key; !exp.Equal(act) { + t.Errorf("expected rule key %v, found %v", exp, act) + } + if exp, act := (Ref{VarTerm("b")}), node.Values[1].(*Rule).Head.Ref(); !exp.Equal(act) { + t.Errorf("expected rule ref %v, found %v", exp, act) + } + if act := node.Values[1].(*Rule).Head.Value; act != nil { + t.Errorf("expected rule value nil, found %v", act) + } + if exp, act := VarTerm("d"), node.Values[1].(*Rule).Head.Key; !exp.Equal(act) { + t.Errorf("expected rule key %v, found %v", exp, act) + } + }) + + t.Run("two multi-value rules, back compat with short style", func(t *testing.T) { + mod0 := `package a +b[1]` + mod1 := `package a +b[2]` + mods := map[string]*Module{ + "0.rego": MustParseModuleWithOpts(mod0, opts), + "1.rego": MustParseModuleWithOpts(mod1, opts), + } + tree := NewRuleTree(NewModuleTree(mods)) + + node := tree.Find(MustParseRef("data.a.b")) + if node == nil { + t.Fatal("expected non-nil leaf node") + } + if exp, act := 2, len(node.Values); exp != act { + t.Fatalf("expected %d values, found %d: %v", exp, act, node.Values) + } + if exp, act := 0, len(node.Children); exp != act { + t.Errorf("expected %d children, found %d", exp, act) + } + if exp, act := (Ref{VarTerm("b")}), node.Values[0].(*Rule).Head.Ref(); !exp.Equal(act) { + t.Errorf("expected rule ref %v, found %v", exp, act) + } + if act := node.Values[0].(*Rule).Head.Value; act != nil { + t.Errorf("expected rule value nil, found %v", act) + } + if exp, act := IntNumberTerm(1), node.Values[0].(*Rule).Head.Key; !exp.Equal(act) { + t.Errorf("expected rule key %v, found %v", exp, act) + } + if exp, act := (Ref{VarTerm("b")}), node.Values[1].(*Rule).Head.Ref(); !exp.Equal(act) { + t.Errorf("expected rule ref %v, found %v", exp, act) + } + if act := node.Values[1].(*Rule).Head.Value; act != nil { + t.Errorf("expected rule value nil, found %v", act) + } + if exp, act := IntNumberTerm(2), node.Values[1].(*Rule).Head.Key; !exp.Equal(act) { + t.Errorf("expected rule key %v, found %v", exp, act) + } + }) + + t.Run("two single-value rules, back compat with short style", func(t *testing.T) { + mod0 := `package a +b[1] = 1` + mod1 := `package a +b[2] = 2` + mods := map[string]*Module{ + "0.rego": MustParseModuleWithOpts(mod0, opts), + "1.rego": MustParseModuleWithOpts(mod1, opts), + } + tree := NewRuleTree(NewModuleTree(mods)) + + // branch point + node := tree.Find(MustParseRef("data.a.b")) + if node == nil { + t.Fatal("expected non-nil leaf node") + } + if exp, act := 0, len(node.Values); exp != act { + t.Fatalf("expected %d values, found %d: %v", exp, act, node.Values) + } + if exp, act := 2, len(node.Children); exp != act { + t.Fatalf("expected %d children, found %d", exp, act) + } + + // branch 1 + node = tree.Find(MustParseRef("data.a.b[1]")) + if node == nil { + t.Fatal("expected non-nil leaf node") + } + if exp, act := 1, len(node.Values); exp != act { + t.Fatalf("expected %d values, found %d: %v", exp, act, node.Values) + } + if exp, act := MustParseRef("b[1]"), node.Values[0].(*Rule).Head.Ref(); !exp.Equal(act) { + t.Errorf("expected rule ref %v, found %v", exp, act) + } + if exp, act := IntNumberTerm(1), node.Values[0].(*Rule).Head.Value; !exp.Equal(act) { + t.Errorf("expected rule value %v, found %v", exp, act) + } + if exp, act := IntNumberTerm(1), node.Values[0].(*Rule).Head.Key; !exp.Equal(act) { + t.Errorf("expected rule key %v, found %v", exp, act) + } + + // branch 2 + node = tree.Find(MustParseRef("data.a.b[2]")) + if node == nil { + t.Fatal("expected non-nil leaf node") + } + if exp, act := 1, len(node.Values); exp != act { + t.Fatalf("expected %d values, found %d: %v", exp, act, node.Values) + } + if exp, act := MustParseRef("b[2]"), node.Values[0].(*Rule).Head.Ref(); !exp.Equal(act) { + t.Errorf("expected rule ref %v, found %v", exp, act) + } + if exp, act := IntNumberTerm(2), node.Values[0].(*Rule).Head.Value; !exp.Equal(act) { + t.Errorf("expected rule value %v, found %v", exp, act) + } + if exp, act := IntNumberTerm(2), node.Values[0].(*Rule).Head.Key; !exp.Equal(act) { + t.Errorf("expected rule key %v, found %v", exp, act) + } + }) + + // NOTE(sr): Now this test seems obvious, but it's a bug that had snuck into the + // NewRuleTree code during development. + t.Run("root node and data node unhidden if there are no system nodes", func(t *testing.T) { + mod0 := `package a +p = 1` + mods := map[string]*Module{ + "0.rego": MustParseModuleWithOpts(mod0, opts), + } + tree := NewRuleTree(NewModuleTree(mods)) + + if exp, act := false, tree.Hide; act != exp { + t.Errorf("expected tree.Hide=%v, got %v", exp, act) + } + dataNode := tree.Child(Var("data")) + if dataNode == nil { + t.Fatal("expected data node") + } + if exp, act := false, dataNode.Hide; act != exp { + t.Errorf("expected dataNode.Hide=%v, got %v", exp, act) + } + }) +} + +func depth(n *TreeNode) int { + d := -1 + for _, m := range n.Children { + if d0 := depth(m); d0 > d { + d = d0 + } + } + return d + 1 +} + +func TestModuleTreeFilenameOrder(t *testing.T) { + // NOTE(sr): It doesn't matter that these are conflicting; but that's where it + // becomes very apparent: before this change, the rule that was reported as + // "conflicting" was that of either one of the input files, randomly. + mods := map[string]*Module{ + "0.rego": MustParseModule(`package p +import rego.v1 +r = 1 if { true }`), + "1.rego": MustParseModule(`package p +import rego.v1 +r = 2 if { true }`), + } + tree := NewModuleTree(mods) + vals := tree.Children[Var("data")].Children[String("p")].Modules + if exp, act := 2, len(vals); exp != act { + t.Fatalf("expected %d rules, found %d", exp, act) + } + mod0 := vals[0] + mod1 := vals[1] + if exp, act := IntNumberTerm(1), mod0.Rules[0].Head.Value; !exp.Equal(act) { + t.Errorf("expected value %v, got %v", exp, act) + } + if exp, act := IntNumberTerm(2), mod1.Rules[0].Head.Value; !exp.Equal(act) { + t.Errorf("expected value %v, got %v", exp, act) + } +} +func TestRuleTree(t *testing.T) { + + mods := getCompilerTestModules() + mods["system-mod"] = MustParseModule(` + package system.foo + + p = 1 + `) + mods["non-system-mod"] = MustParseModule(` + package user.system + + p = 1`) + mods["mod-incr"] = MustParseModule(` + package a.b.c + import rego.v1 + + s contains 1 if { true } + s contains 2 if { true }`, + ) + + mods["dots-in-heads"] = MustParseModule(` + package dots + + a.b.c = 12 + d.e.f.g = 34 + `) + + tree := NewRuleTree(NewModuleTree(mods)) + expectedNumRules := 25 + + if tree.Size() != expectedNumRules { + t.Errorf("Expected %v but got %v rules", expectedNumRules, tree.Size()) + } + + // Check that empty packages are represented as leaves with no rules. + node := tree.Children[Var("data")].Children[String("a")].Children[String("b")].Children[String("empty")] + if node == nil || len(node.Children) != 0 || len(node.Values) != 0 { + t.Fatalf("Unexpected nil value or non-empty leaf of non-leaf node: %v", node) + } + + // Check that root node is not hidden + if exp, act := false, tree.Hide; act != exp { + t.Errorf("expected tree.Hide=%v, got %v", exp, act) + } + + system := tree.Child(Var("data")).Child(String("system")) + if !system.Hide { + t.Fatalf("Expected system node to be hidden: %v", system) + } + + if system.Child(String("foo")).Hide { + t.Fatalf("Expected system.foo node to be visible") + } + + user := tree.Child(Var("data")).Child(String("user")).Child(String("system")) + if user.Hide { + t.Fatalf("Expected user.system node to be visible") + } + + if !isVirtual(tree, MustParseRef("data.a.b.empty")) { + t.Fatal("Expected data.a.b.empty to be virtual") + } + + abc := tree.Children[Var("data")].Children[String("a")].Children[String("b")].Children[String("c")] + exp := []Value{String("p"), String("q"), String("r"), String("s"), String("z")} + + if len(abc.Sorted) != len(exp) { + t.Fatal("expected", exp, "but got", abc) + } + + for i := range exp { + if exp[i].Compare(abc.Sorted[i]) != 0 { + t.Fatal("expected", exp, "but got", abc) + } + } +} + +func TestCompilerEmpty(t *testing.T) { + c := NewCompiler() + c.Compile(nil) + assertNotFailed(t, c) +} + +func TestCompilerExample(t *testing.T) { + c := NewCompiler() + m := MustParseModuleWithOpts(testModule, ParserOptions{AllFutureKeywords: true}) + c.Compile(map[string]*Module{"testMod": m}) + assertNotFailed(t, c) +} + +func TestCompilerWithStageAfter(t *testing.T) { + t.Run("after failing means overall failure", func(t *testing.T) { + c := NewCompiler().WithStageAfter( + "CheckRecursion", + CompilerStageDefinition{"MockStage", "mock_stage", + func(*Compiler) *Error { return NewError(CompileErr, &Location{}, "mock stage error") }}, + ) + m := MustParseModuleWithOpts(testModule, ParserOptions{AllFutureKeywords: true}) + c.Compile(map[string]*Module{"testMod": m}) + + if !c.Failed() { + t.Errorf("Expected compilation error") + } + }) + + t.Run("first 'after' failure inhibits other 'after' stages", func(t *testing.T) { + c := NewCompiler(). + WithStageAfter("CheckRecursion", + CompilerStageDefinition{"MockStage", "mock_stage", + func(*Compiler) *Error { return NewError(CompileErr, &Location{}, "mock stage error") }}). + WithStageAfter("CheckRecursion", + CompilerStageDefinition{"MockStage2", "mock_stage2", + func(*Compiler) *Error { return NewError(CompileErr, &Location{}, "mock stage error two") }}, + ) + m := MustParseModule(`package p +q := true`) + + c.Compile(map[string]*Module{"testMod": m}) + + if !c.Failed() { + t.Errorf("Expected compilation error") + } + if exp, act := 1, len(c.Errors); exp != act { + t.Errorf("expected %d errors, got %d: %v", exp, act, c.Errors) + } + }) + + t.Run("'after' failure inhibits other ordinary stages", func(t *testing.T) { + c := NewCompiler(). + WithStageAfter("CheckRecursion", + CompilerStageDefinition{"MockStage", "mock_stage", + func(*Compiler) *Error { return NewError(CompileErr, &Location{}, "mock stage error") }}) + m := MustParseModule(`package p +import rego.v1 + +q if { + 1 == "a" # would fail "CheckTypes", the next stage +} +`) + c.Compile(map[string]*Module{"testMod": m}) + + if !c.Failed() { + t.Errorf("Expected compilation error") + } + if exp, act := 1, len(c.Errors); exp != act { + t.Errorf("expected %d errors, got %d: %v", exp, act, c.Errors) + } + }) +} + +func TestCompilerFunctions(t *testing.T) { + tests := []struct { + note string + modules []string + wantErr bool + }{ + { + note: "multiple input types", + modules: []string{`package x + + f([x]) = y { + y = x + } + + f({"foo": x}) = y { + y = x + }`}, + }, + { + note: "multiple input types", + modules: []string{`package x + + f([x]) = y { + y = x + } + + f([[x]]) = y { + y = x + }`}, + }, + { + note: "constant input", + modules: []string{`package x + + f(1) = y { + y = "foo" + } + + f(2) = y { + y = "bar" + }`}, + }, + { + note: "constant input", + modules: []string{`package x + + f(1, x) = y { + y = x + } + + f(x, y) = z { + z = x+y + }`}, + }, + { + note: "constant input", + modules: []string{`package x + + f(x, 1) = y { + y = x + } + + f(x, [y]) = z { + z = x+y + }`}, + }, + { + note: "multiple input types (nested)", + modules: []string{`package x + + f({"foo": {"bar": x}}) = y { + y = x + } + + f({"foo": [x]}) = y { + y = x + }`}, + }, + { + note: "multiple output types", + modules: []string{`package x + + f(1) = y { + y = "foo" + } + + f(2) = y { + y = 2 + }`}, + }, + { + note: "namespacing", + modules: []string{ + `package x + + f(x) = y { + data.y.f[x] = y + }`, + `package y + + f[x] = y { + y = "bar" + x = "foo" + }`, + }, + }, + { + note: "implicit value", + modules: []string{ + `package x + + f(x) { + x = "foo" + }`}, + }, + { + note: "resolving", + modules: []string{ + `package x + + f(x) = x { true }`, + + `package y + + import data.x + import data.x.f as g + + p { g(1, a) } + p { x.f(1, b) } + p { data.x.f(1, c) } + `, + }, + }, + { + note: "undefined", + modules: []string{ + `package x + + p { + f(1) + }`, + }, + wantErr: true, + }, + { + note: "must apply", + modules: []string{ + `package x + + f(1) + + p { + f + } + `, + }, + wantErr: true, + }, + { + note: "must apply", + modules: []string{ + `package x + f(1) + p { f.x }`, + }, + wantErr: true, + }, + { + note: "call argument ref output vars", + modules: []string{ + `package x + + f(x) + + p { f(data.foo[i]) }`, + }, + wantErr: false, + }, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var err error + modules := map[string]*Module{} + for i, module := range tc.modules { + name := fmt.Sprintf("mod%d", i) + modules[name], err = ParseModuleWithOpts(name, module, ParserOptions{RegoVersion: RegoV0}) + if err != nil { + panic(err) + } + } + c := NewCompiler() + c.Compile(modules) + if tc.wantErr && !c.Failed() { + t.Errorf("Expected compilation error") + } else if !tc.wantErr && c.Failed() { + t.Errorf("Unexpected compilation error(s): %v", c.Errors) + } + }) + } +} + +func TestCompilerErrorLimit(t *testing.T) { + modules := map[string]*Module{ + "test": MustParseModule(`package test + import rego.v1 + + r = y if { y = true; x = z } + + s[x] = y if { + z = y + x + } + + t contains x if { split(x, y, z) } + `), + } + + c := NewCompiler().SetErrorLimit(2) + c.Compile(modules) + + errs := c.Errors + exp := []string{ + "4:23: rego_unsafe_var_error: var x is unsafe", + "4:23: rego_unsafe_var_error: var z is unsafe", + "rego_compile_error: error limit reached", + } + + result := make([]string, 0, len(errs)) + for _, err := range errs { + result = append(result, err.Error()) + } + + sort.Strings(exp) + sort.Strings(result) + if !slices.Equal(exp, result) { + t.Errorf("Expected errors %v, got %v", exp, result) + } +} + +func TestCompilerCheckSafetyHead(t *testing.T) { + c := NewCompiler() + c.Modules = getCompilerTestModules() + popts := ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true} + c.Modules["newMod"] = MustParseModuleWithOpts(`package a.b + +unboundKey[x1] = y if { q[y] = {"foo": [1, 2, [{"bar": y}]]} } +unboundVal[y] = x2 if { q[y] = {"foo": [1, 2, [{"bar": y}]]} } +unboundCompositeVal[y] = [{"foo": x3, "bar": y}] if { q[y] = {"foo": [1, 2, [{"bar": y}]]} } +unboundCompositeKey contains [{"x": x4}] if { q[y] } +unboundBuiltinOperator = eq if { 4 = 1 } +unboundElse if { false } else = else_var if { true } +c.d.e[x5] if true +f.g.h[y] = x6 if y := "y" +i.j.k contains x7 if true +`, popts) + compileStages(c, c.checkSafetyRuleHeads) + + makeErrMsg := func(v string) string { + return fmt.Sprintf("rego_unsafe_var_error: var %v is unsafe", v) + } + + expected := []string{ + makeErrMsg("x1"), + makeErrMsg("x2"), + makeErrMsg("x3"), + makeErrMsg("x4"), + makeErrMsg("x5"), + makeErrMsg("x6"), + makeErrMsg("x7"), + makeErrMsg("eq"), + makeErrMsg("else_var"), + } + + result := compilerErrsToStringSlice(c.Errors) + sort.Strings(expected) + + if len(result) != len(expected) { + t.Fatalf("Expected %d:\n%v\nBut got %d:\n%v", len(expected), strings.Join(expected, "\n"), len(result), strings.Join(result, "\n")) + } + + for i := range result { + if expected[i] != result[i] { + t.Errorf("Expected %v but got: %v", expected[i], result[i]) + } + } + +} + +func TestCompilerCheckSafetyBodyReordering(t *testing.T) { + tests := []struct { + note string + body string + expected string + }{ + {"noop", `x = 1; x != 0`, `x = 1; x != 0`}, + {"var/ref", `a[i] = x; a = [1, 2, 3, 4]`, `a = [1, 2, 3, 4]; a[i] = x`}, + {"var/ref (nested)", `a = [1, 2, 3, 4]; a[b[i]] = x; b = [0, 0, 0, 0]`, `a = [1, 2, 3, 4]; b = [0, 0, 0, 0]; a[b[i]] = x`}, + {"negation", + `a = [true, false]; b = [true, false]; not a[i]; b[i]`, + `a = [true, false]; b = [true, false]; b[i]; not a[i]`}, + {"built-in", `x != 0; count([1, 2, 3], x)`, `count([1, 2, 3], x); x != 0`}, + {"var/var 1", `x = y; z = 1; y = z`, `z = 1; y = z; x = y`}, + {"var/var 2", `x = y; 1 = z; z = y`, `1 = z; z = y; x = y`}, + {"var/var 3", `x != 0; y = x; y = 1`, `y = 1; y = x; x != 0`}, + {"array compr/var", `x != 0; [y | y = 1] = x`, `[y | y = 1] = x; x != 0`}, + {"array compr/array", `[1] != [x]; [y | y = 1] = [x]`, `[y | y = 1] = [x]; [1] != [x]`}, + {"with", `data.a.b.d.t with input as x; x = 1`, `x = 1; data.a.b.d.t with input as x`}, + {"with-2", `data.a.b.d.t with input.x as x; x = 1`, `x = 1; data.a.b.d.t with input.x as x`}, + {"with-nop", "data.somedoc[x] with input as true", "data.somedoc[x] with input as true"}, + {"ref-head", `s = [["foo"], ["bar"]]; x = y[0]; y = s[_]; contains(x, "oo")`, ` + s = [["foo"], ["bar"]]; + y = s[_]; + x = y[0]; + contains(x, "oo") + `}, + {"userfunc", `split(y, ".", z); data.a.b.funcs.fn("...foo.bar..", y)`, `data.a.b.funcs.fn("...foo.bar..", y); split(y, ".", z)`}, + {"every", `every _ in [] { x != 1 }; x = 1`, `__local4__ = []; x = 1; every __local3__, _ in __local4__ { x != 1}`}, + {"every-domain", `every _ in xs { true }; xs = [1]`, `xs = [1]; __local4__ = xs; every __local3__, _ in __local4__ { true }`}, + } + + for i, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + opts := ParserOptions{ + RegoVersion: RegoV0, + AllFutureKeywords: true, + unreleasedKeywords: true, + } + c := NewCompiler() + c.Modules = getCompilerTestModules() + c.Modules["reordering"] = MustParseModuleWithOpts(fmt.Sprintf( + `package test + p { %s }`, tc.body), opts) + + compileStages(c, c.checkSafetyRuleBodies) + + if c.Failed() { + t.Errorf("%v (#%d): Unexpected compilation error: %v", tc.note, i, c.Errors) + return + } + + expected := MustParseBodyWithOpts(tc.expected, opts) + result := c.Modules["reordering"].Rules[0].Body + + if !expected.Equal(result) { + t.Errorf("%v (#%d): Expected body to be ordered and equal to %v but got: %v", tc.note, i, expected, result) + } + }) + } +} + +func TestCompilerCheckSafetyBodyReorderingClosures(t *testing.T) { + opts := ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true} + + tests := []struct { + note string + mod *Module + exp Body + }{ + { + note: "comprehensions-1", + mod: MustParseModule(`package compr +import rego.v1 +import data.b +import data.c +p = true if { v = [null | true]; xs = [x | a[i] = x; a = [y | y != 1; y = c[j]]]; xs[j] > 0; z = [true | data.a.b.d.t with input as i2; i2 = i]; b[i] = j } +`), + exp: MustParseBody(`v = [null | true]; data.b[i] = j; xs = [x | a = [y | y = data.c[j]; y != 1]; a[i] = x]; xs[j] > 0; z = [true | i2 = i; data.a.b.d.t with input as i2]`), + }, + { + note: "comprehensions-2", + mod: MustParseModule(`package compr +import rego.v1 +import data.b +import data.c +q = true if { _ = [x | x = b[i]]; _ = b[j]; _ = [x | x = true; x != false]; true != false; _ = [x | data.foo[_] = x]; data.foo[_] = _ } +`), + exp: MustParseBody(`_ = [x | x = data.b[i]]; _ = data.b[j]; _ = [x | x = true; x != false]; true != false; _ = [x | data.foo[_] = x]; data.foo[_] = _`), + }, + + { + note: "comprehensions-3", + mod: MustParseModule(`package compr +import rego.v1 +import data.b +import data.c +fn(x) = y if { + trim(x, ".", y) +} +r = true if { a = [x | split(y, ".", z); x = z[i]; fn("...foo.bar..", y)] } +`), + exp: MustParseBody(`a = [x | data.compr.fn("...foo.bar..", y); split(y, ".", z); x = z[i]]`), + }, + { + note: "closure over function output", + mod: MustParseModule(`package test +import rego.v1 + +p if { + object.get(input.subject.roles[_], comp, [""], output) + comp = [ 1 | true ] + every y in [2] { + y in output + } +}`), + exp: MustParseBodyWithOpts(`comp = [1 | true] + __local2__ = [2] + object.get(input.subject.roles[_], comp, [""], output) + every __local0__, __local1__ in __local2__ { internal.member_2(__local1__, output) }`, opts), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler() + c.Modules = map[string]*Module{"mod": tc.mod} + compileStages(c, c.checkSafetyRuleBodies) + assertNotFailed(t, c) + last := len(c.Modules["mod"].Rules) - 1 + actual := c.Modules["mod"].Rules[last].Body + if !actual.Equal(tc.exp) { + t.Errorf("Expected reordered body to be equal to:\n%v\nBut got:\n%v", tc.exp, actual) + } + }) + } +} + +func TestCompilerCheckSafetyBodyErrors(t *testing.T) { + + moduleBegin := ` + package a.b + + import input.aref.b.c as foo + import input.avar as bar + import data.m.n as baz + ` + + tests := []struct { + note string + moduleContent string + expected string + }{ + {"ref-head", `p if { a.b.c = "foo" }`, `{a,}`}, + {"ref-head-2", `p if { {"foo": [{"bar": a.b.c}]} = {"foo": [{"bar": "baz"}]} }`, `{a,}`}, + {"negation", `p if { a = [1, 2, 3, 4]; not a[i] = x }`, `{i, x}`}, + {"negation-head", `p contains x if { a = [1, 2, 3, 4]; not a[i] = x }`, `{i,x}`}, + {"negation-multiple", `p if { a = [1, 2, 3, 4]; b = [1, 2, 3, 4]; not a[i] = x; not b[j] = x }`, `{i, x, j}`}, + {"negation-nested", `p if { a = [{"foo": ["bar", "baz"]}]; not a[0].foo = [a[0].foo[i], a[0].foo[j]] } `, `{i, j}`}, + {"builtin-input", `p if { count([1, 2, x], x) }`, `{x,}`}, + {"builtin-input-name", `p if { count(eq, 1) }`, `{eq,}`}, + {"builtin-multiple", `p if { x > 0; x <= 3; x != 2 }`, `{x,}`}, + {"unordered-object-keys", `p if { x = "a"; [{x: y, z: a}] = [{"a": 1, "b": 2}]}`, `{a,y,z}`}, + {"unordered-sets", `p if { x = "a"; [{x, y}] = [{1, 2}]}`, `{y,}`}, + {"array-compr", `p if { _ = [x | x = data.a[_]; y > 1] }`, `{y,}`}, + {"array-compr-nested", `p if { _ = [x | x = a[_]; a = [y | y = data.a[_]; z > 1]] }`, `{z,}`}, + {"array-compr-closure", `p if { _ = [v | v = [x | x = data.a[_]]; x > 1] }`, `{x,}`}, + {"array-compr-term", `p if { _ = [u | true] }`, `{u,}`}, + {"array-compr-term-nested", `p if { _ = [v | v = [w | w != 0]] }`, `{w,}`}, + {"array-compr-mixed", `p if { _ = [x | y = [a | a = z[i]]] }`, `{a, x, z, i}`}, + {"array-compr-builtin", `p if { [true | eq != 2] }`, `{eq,}`}, + {"closure-self", `p if { x = [x | x = 1] }`, `{x,}`}, + {"closure-transitive", `p if { x = y; x = [y | y = 1] }`, `{x,y}`}, + {"nested", `p if { count(baz[i].attr[bar[dead.beef]], n) }`, `{dead,}`}, + {"negated-import", `p if { not foo; not bar; not baz }`, `set()`}, + {"rewritten", `p contains {"foo": dead[i]} if { true }`, `{dead, i}`}, + {"with-value", `p if { data.a.b.d.t with input as x }`, `{x,}`}, + {"with-value-2", `p if { x = data.a.b.d.t with input as x }`, `{x,}`}, + {"else-kw", "p if { false } else if { count(x, 1) }", `{x,}`}, + {"function", "foo(x) = [y, z] if { split(x, y, z) }", `{y,z}`}, + {"call-vars-input", "p if { f(x, x) } f(x) = x if { true }", `{x,}`}, + {"call-no-output", "p if { f(x) } f(x) = x if { true }", `{x,}`}, + {"call-too-few", "p if { f(1,x) } f(x,y) if { true }", "{x,}"}, + {"object-key-comprehension", "p if { { {p|x}: 0 } }", "{x,}"}, + {"set-value-comprehension", "p if { {1, {p|x}} }", "{x,}"}, + {"every", "p if { every y in [10] { x > y } }", "{x,}"}, + } + + makeErrMsg := func(varName string) string { + return fmt.Sprintf("rego_unsafe_var_error: var %v is unsafe", varName) + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + + // Build slice of expected error messages. + expected := []string{} + + _ = MustParseTerm(tc.expected).Value.(Set).Iter(func(x *Term) error { + expected = append(expected, makeErrMsg(string(x.Value.(Var)))) + return nil + }) // cannot return error + + sort.Strings(expected) + + // Compile test module. + popts := ParserOptions{ + AllFutureKeywords: true, + unreleasedKeywords: true, + } + c := NewCompiler() + c.Modules = map[string]*Module{ + "newMod": MustParseModuleWithOpts(fmt.Sprintf(` + + %v + + %v + + `, moduleBegin, tc.moduleContent), popts), + } + + compileStages(c, c.checkSafetyRuleBodies) + + // Get errors. + result := compilerErrsToStringSlice(c.Errors) + + // Check against expected. + if len(result) != len(expected) { + t.Fatalf("Expected %d:\n%v\nBut got %d:\n%v", len(expected), strings.Join(expected, "\n"), len(result), strings.Join(result, "\n")) + } + + for i := range result { + if expected[i] != result[i] { + t.Errorf("Expected %v but got: %v", expected[i], result[i]) + } + } + + }) + } +} + +func TestCompilerCheckSafetyVarLoc(t *testing.T) { + + _, err := CompileModules(map[string]string{"test.rego": `package test +import rego.v1 + +p if { + not x + x > y +}`}) + + if err == nil { + t.Fatal("expected error") + } + + errs := err.(Errors) + + if !strings.Contains(errs[0].Message, "var x is unsafe") || errs[0].Location.Row != 5 { + t.Fatal("expected error on row 5 but got:", err) + } + + if !strings.Contains(errs[1].Message, "var y is unsafe") || errs[1].Location.Row != 6 { + t.Fatal("expected y is unsafe on row 6 but got:", err) + } +} + +func TestCompilerCheckSafetyFunctionAndContainsKeyword(t *testing.T) { + _, err := CompileModules(map[string]string{"test.rego": `package play + + import future.keywords.contains + + p(id) contains x { + x := id + }`}) + if err == nil { + t.Fatal("expected error") + } + + errs := err.(Errors) + if !strings.Contains(errs[0].Message, "the contains keyword can only be used with multi-value rule definitions (e.g., p contains { ... })") { + t.Fatal("wrong error message:", err) + } + if errs[0].Location.Row != 5 { + t.Fatal("expected error on line 5 but got:", errs[0].Location.Row) + } +} + +func TestCompilerCheckTypes(t *testing.T) { + c := NewCompiler() + modules := getCompilerTestModules() + c.Modules = map[string]*Module{"mod6": modules["mod6"], "mod7": modules["mod7"]} + compileStages(c, c.checkTypes) + assertNotFailed(t, c) +} + +// Regression test for GH issue #6790 +func TestCompilerCheckEveryWithNestedDomainCalls(t *testing.T) { + c := NewCompiler() + c.Modules = map[string]*Module{"test": MustParseModule(`package test +import rego.v1 + +x if { + every p in [1 / 2] { + p == true + } +}`)} + compileStages(c, c.checkTypes) + assertNotFailed(t, c) +} + +func TestCompilerCheckRuleConflicts(t *testing.T) { + + c := getCompilerWithParsedModules(map[string]string{ + "mod1.rego": `package badrules + +p contains x if { x = 1 } +p[x] = y if { x = y; x = "a" } +q contains 1 if { true } +q = {1, 2, 3} if { true } +r[x] = y if { x = y; x = "a" } +r[x] = y if { x = y; x = "a" } +s contains x if { x = "a" } +s contains x if { x = "b" } +t := x if { x = "a"}`, + + // valid extension of r in mod1.rego + "mod2a.rego": `package badrules.r + +q contains 1 if { true }`, + + // invalid override of s in mod1.rego + "mod2b.rego": `package badrules.s + +q contains 1 if { true }`, + + // invalid override of t in mod1.rego + "mod2c.rego": `package badrules.t + +q contains 1 if { true }`, + + "mod3.rego": `package badrules.defkw + +default foo = 1 +default foo = 2 +foo = 3 if { true } + +default p.q.bar = 1 +default p.q.bar = 2 +p.q.bar = 3 if { true } +`, + "mod4.rego": `package badrules.arity + +f(1) if { true } +f if { true } + +g(1) if { true } +g(1,2) if { true } + +p.q.h(1) if { true } +p.q.h if { true } + +p.q.i(1) if { true } +p.q.i(1,2) if { true }`, + "mod5.rego": `package badrules.dataoverlap + +p if { true }`, + "mod6.rego": `package badrules.existserr + +p if { true }`, + + "mod7.rego": `package badrules.foo + +bar.baz contains "quz" if true`, + "mod8.rego": `package badrules.complete_partial +p := 1 +p[r] := 2 if { r := "foo" }`, + }) + + c.WithPathConflictsCheck(func(path []string) (bool, error) { + if slices.Equal(path, []string{"badrules", "dataoverlap", "p"}) { + return true, nil + } else if slices.Equal(path, []string{"badrules", "existserr", "p"}) { + return false, errors.New("unexpected error") + } + return false, nil + }) + + compileStages(c, c.checkRuleConflicts) + + expected := []string{ + "rego_compile_error: conflict check for data path badrules/existserr/p: unexpected error", + "rego_compile_error: conflicting rule for data path badrules/dataoverlap/p found", + "rego_type_error: conflicting rules data.badrules.arity.f found", + "rego_type_error: conflicting rules data.badrules.arity.g found", + "rego_type_error: conflicting rules data.badrules.arity.p.q.h found", + "rego_type_error: conflicting rules data.badrules.arity.p.q.i found", + "rego_type_error: conflicting rules data.badrules.complete_partial.p[r] found", + "rego_type_error: conflicting rules data.badrules.p[x] found", + "rego_type_error: conflicting rules data.badrules.q found", + "rego_type_error: multiple default rules data.badrules.defkw.foo found at mod3.rego:3, mod3.rego:4", + "rego_type_error: multiple default rules data.badrules.defkw.p.q.bar found at mod3.rego:7, mod3.rego:8", + "rego_type_error: package badrules.s conflicts with rule s defined at mod1.rego:10", + "rego_type_error: package badrules.s conflicts with rule s defined at mod1.rego:9", + "rego_type_error: package badrules.t conflicts with rule t defined at mod1.rego:11", + "rego_type_error: rule data.badrules.s conflicts with [data.badrules.s.q]", + "rego_type_error: rule data.badrules.t conflicts with [data.badrules.t.q]", + } + + assertCompilerErrorStrings(t, c, expected) +} + +func TestCompilerCheckRuleConflictsWithRoots(t *testing.T) { + + c := getCompilerWithParsedModules(map[string]string{ + "mod1.rego": `package badrules.dataoverlap +p if { true }`, + "mod2.rego": `package badrules.existserr +p if { true }`, + + // this does not trigger conflict check because + // WithPathConflictsCheckRoots limits the root to "badrules". + "mod3.rego": `package badrules_outside_root.dataoverlap +p if { true }`, + }) + + c.WithPathConflictsCheck(func(path []string) (bool, error) { + if slices.Contains(path, "dataoverlap") { + return true, nil + } else if slices.Equal(path, []string{"badrules", "existserr", "p"}) { + return false, errors.New("unexpected error") + } + return false, nil + }).WithPathConflictsCheckRoots([]string{"badrules"}) + + compileStages(c, c.checkRuleConflicts) + + expected := []string{ + "rego_compile_error: conflict check for data path badrules/existserr/p: unexpected error", + "rego_compile_error: conflicting rule for data path badrules/dataoverlap/p found", + } + + assertCompilerErrorStrings(t, c, expected) +} + +func TestCompilerCheckRuleConflictsDefaultFunction(t *testing.T) { + tests := []struct { + note string + modules []*Module + err string + }{ + { + note: "conflicting rules", + modules: modules( + `package pkg + default f(_) = 100 + f(x, y) = x if { + x == y + }`), + err: "rego_type_error: conflicting rules data.pkg.f found", + }, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + mods := make(map[string]*Module, len(tc.modules)) + for i, m := range tc.modules { + mods[strconv.Itoa(i)] = m + } + c := NewCompiler() + c.Modules = mods + compileStages(c, c.checkRuleConflicts) + if tc.err != "" { + assertCompilerErrorStrings(t, c, []string{tc.err}) + } else { + assertCompilerErrorStrings(t, c, []string{}) + } + }) + } +} + +func TestCompilerCheckRuleConflictsDotsInRuleHeads(t *testing.T) { + tests := []struct { + note string + modules []*Module + err string + }{ + { + note: "arity mismatch, ref and non-ref rule", + modules: modules( + `package pkg + p.q.r if { true }`, + `package pkg.p.q + r(_) = 2`), + err: "rego_type_error: conflicting rules data.pkg.p.q.r found", + }, + { + note: "two default rules, ref and non-ref rule", + modules: modules( + `package pkg + default p.q.r = 3 + p.q.r if { true }`, + `package pkg.p.q + default r = 4 + r = 2`), + err: "rego_type_error: multiple default rules data.pkg.p.q.r found at mod0.rego:2, mod1.rego:2", + }, + { + note: "arity mismatch, ref and ref rule", + modules: modules( + `package pkg.a.b + p.q.r if { true }`, + `package pkg.a + b.p.q.r(_) = 2`), + err: "rego_type_error: conflicting rules data.pkg.a.b.p.q.r found", + }, + { + note: "two default rules, ref and ref rule", + modules: modules( + `package pkg + default p.q.w.r = 3 + p.q.w.r if { true }`, + `package pkg.p + default q.w.r = 4 + q.w.r = 2`), + err: "rego_type_error: multiple default rules data.pkg.p.q.w.r found at mod0.rego:2, mod1.rego:2", + }, + { + note: "multi-value + single-value rules, both with same ref prefix", + modules: modules( + `package pkg + p.q.w[x] = 1 if x := "foo"`, + `package pkg + p.q.w contains "bar"`), + err: "rego_type_error: conflicting rules data.pkg.p.q.w found", + }, + { + note: "two multi-value rules, both with same ref", + modules: modules( + `package pkg + p.q.w contains "baz"`, + `package pkg + p.q.w contains "bar"`), + }, + { + note: "module conflict: non-ref rule", + modules: modules( + `package pkg.q + r if { true }`, + `package pkg.q.r`), + err: "rego_type_error: package pkg.q.r conflicts with rule r defined at mod0.rego:2", + }, + { + note: "module conflict: ref rule", + modules: modules( + `package pkg + p.q.r if { true }`, + `package pkg.p.q.r`), + err: "rego_type_error: package pkg.p.q.r conflicts with rule p.q.r defined at mod0.rego:2", + }, + { + note: "single-value with other rule overlap", + modules: modules( + `package pkg + p.q.r if { true }`, + `package pkg + p.q.r.s if { true }`), + err: "rego_type_error: rule data.pkg.p.q.r conflicts with [data.pkg.p.q.r.s]", + }, + { + note: "single-value with other rule overlap", + modules: modules( + `package pkg + p.q.r if { true } + p.q.r.s if { true } + p.q.r.t if { true }`), + err: "rego_type_error: rule data.pkg.p.q.r conflicts with [data.pkg.p.q.r.s data.pkg.p.q.r.t]", + }, + { + note: "single-value with other partial object (same ref) overlap", + modules: modules( + `package pkg + p.q := 1 + p.q[r] := 2 if { r := "foo" }`), + err: "rego_type_error: conflicting rules data.pkg.p.q[r] foun", + }, + { + note: "single-value with other rule overlap, unknown key", + modules: modules( + `package pkg + p.q[r] = x if { r = input.key; x = input.foo } + p.q.r.s = x if { true } + `), + }, + { + note: "single-value with other rule overlap, unknown ref var and key", + modules: modules( + `package pkg + p.q[r][s] = x if { r = input.key1; s = input.key2; x = input.foo } + p.q.r.s.t = x if { true } + `), + }, + { + note: "single-value partial object with other partial object rule overlap, unknown keys (regression test for #5855; invalidated by multi-var refs)", + modules: modules( + `package pkg + p[r] := x if { r = input.key; x = input.bar } + p.q[r] := x if { r = input.key; x = input.bar } + `), + }, + { + note: "single-value partial object with other partial object (implicit 'true' value) rule overlap, unknown keys", + modules: modules( + `package pkg + p[r] := x if { r = input.key; x = input.bar } + p.q[r] if { r = input.key } + `), + }, + { + note: "single-value partial object with multi-value rule (ref head) overlap, unknown key", + modules: modules( + `package pkg + import future.keywords + p[r] := x if { r = input.key; x = input.bar } + p.q contains r if { r = input.key } + `), + }, + { + note: "single-value partial object with multi-value rule overlap, unknown key", + modules: modules( + `package pkg + p[r] := x if { r = input.key; x = input.bar } + p contains q if { true } + `), + err: "rego_type_error: conflicting rules data.pkg.p found", + }, + { + note: "single-value rule with known and unknown key", + modules: modules( + `package pkg + p.q[r] = x if { r = input.key; x = input.foo } + p.q.s = "x" if { true } + `), + }, + { + note: "multi-value rule with other rule overlap", + modules: modules( + `package pkg + p contains v if { v := ["a", "b"][_] } + p.q := 42 + `), + err: "rego_type_error: rule data.pkg.p conflicts with [data.pkg.p.q]", + }, + { + note: "multi-value rule with other rule (ref) overlap", + modules: modules( + `package pkg + p contains v if { v := ["a", "b"][_] } + p.q.r if { true } + `), + err: "rego_type_error: rule data.pkg.p conflicts with [data.pkg.p.q.r]", + }, + { + note: "multi-value rule (dots in head) with other rule (ref) overlap", + modules: modules( + `package pkg + import future.keywords + p.q contains v if { v := ["a", "b"][_] } + p.q.r if { true } + `), + err: "rule data.pkg.p.q conflicts with [data.pkg.p.q.r]", + }, + { + note: "multi-value rule (dots and var in head) with other rule (ref) overlap", + modules: modules( + `package pkg + import future.keywords + p[q] contains v if { v := ["a", "b"][_] } + p.q.r if { true } + `), + }, + { + note: "function with other rule (ref) overlap", + modules: modules( + `package pkg + p(x) := x + p.q.r if { true } + `), + err: "rego_type_error: rule data.pkg.p conflicts with [data.pkg.p.q.r]", + }, + { + note: "function with other rule (ref) overlap", + modules: modules( + `package pkg + p(x) := x + p.q.r if { true } + `), + err: "rego_type_error: rule data.pkg.p conflicts with [data.pkg.p.q.r]", + }, + { + note: "function (ref) with other rule (ref) overlap", + modules: modules( + `package pkg + p.q(x) := x + p.q.r if { true } + `), + err: "rego_type_error: rule data.pkg.p.q conflicts with [data.pkg.p.q.r]", + }, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + mods := make(map[string]*Module, len(tc.modules)) + for i, m := range tc.modules { + mods[strconv.Itoa(i)] = m + } + c := NewCompiler() + c.Modules = mods + compileStages(c, c.checkRuleConflicts) + if tc.err != "" { + assertCompilerErrorStrings(t, c, []string{tc.err}) + } else { + assertCompilerErrorStrings(t, c, []string{}) + } + }) + } +} + +func TestCompilerCheckRulePkgConflicts(t *testing.T) { + tests := []struct { + note string + modules []*Module + err []string + }{ + { + note: "Package can be declared within dynamic extent of rule (#6387 regression test)", + modules: modules( + `package test + p[x] := y if { x := "a"; y := "b" }`, + `package test.p + q := 1`), + }, + { + note: "Package can be declared deep within dynamic extent of rule (#6387 regression test)", + modules: modules( + `package test + p[x] := y if { x := "a"; y := "b" }`, + `package test.p.q.r.s + t := 1`), + }, + { + note: "Package cannot be declared within extent of single-value rule (ground ref)", + modules: modules( + `package test + p := x if { x := "a" }`, + `package test.p + q := 1`), + err: []string{ + "rego_type_error: package test.p conflicts with rule p defined at mod0.rego:2", + "rego_type_error: rule data.test.p conflicts with [data.test.p.q]", + }, + }, + { + note: "Package cannot be declared within extent of multi-value rule", + modules: modules( + `package test + p contains x if { x := "a" }`, + `package test.p + q := 1`), + err: []string{ + "rego_type_error: package test.p conflicts with rule p defined at mod0.rego:2", + "rego_type_error: rule data.test.p conflicts with [data.test.p.q]", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + mods := make(map[string]*Module, len(tc.modules)) + for i, m := range tc.modules { + mods[strconv.Itoa(i)] = m + } + c := NewCompiler() + c.Modules = mods + compileStages(c, c.checkRuleConflicts) + if len(tc.err) > 0 { + assertCompilerErrorStrings(t, c, tc.err) + } else { + assertCompilerErrorStrings(t, c, []string{}) + } + }) + } +} + +func TestCompilerCheckUndefinedFuncs(t *testing.T) { + + module := ` + package test + import rego.v1 + + undefined_function if { + data.deadbeef(x) + } + + undefined_global if { + deadbeef(x) + } + + # NOTE: all the dynamic dispatch examples here are not supported, + # we're checking assertions about the error returned. + undefined_dynamic_dispatch if { + x = "f"; data.test2[x](1) + } + + undefined_dynamic_dispatch_declared_var if { + y := "f"; data.test2[y](1) + } + + undefined_dynamic_dispatch_declared_var_in_array if { + z := "f"; data.test2[[z]](1) + } + + arity_mismatch_1 if { + data.test2.f(1,2,3) + } + + arity_mismatch_2 if { + data.test2.f() + } + + arity_mismatch_3 if { + x:= data.test2.f() + } + ` + + module2 := ` + package test2 + + f(x) = x + ` + + _, err := CompileModules(map[string]string{ + "test.rego": module, + "test2.rego": module2, + }) + if err == nil { + t.Fatal("expected errors") + } + + result := err.Error() + want := []string{ + "rego_type_error: undefined function data.deadbeef", + "rego_type_error: undefined function deadbeef", + "rego_type_error: undefined function data.test2[x]", + "rego_type_error: undefined function data.test2[y]", + "rego_type_error: undefined function data.test2[[z]]", + "rego_type_error: function data.test2.f has arity 1, got 3 arguments", + "test.rego:32: rego_type_error: function data.test2.f has arity 1, got 0 arguments", + "test.rego:36: rego_type_error: function data.test2.f has arity 1, got 0 arguments", + } + for _, w := range want { + if !strings.Contains(result, w) { + t.Fatalf("Expected %q in result but got: %v", w, result) + } + } +} + +func TestCompilerQueryCompilerCheckUndefinedFuncs(t *testing.T) { + compiler := NewCompiler() + + for _, tc := range []struct { + note, query, err string + }{ + + {note: "undefined function", query: `data.foo(1)`, err: "undefined function data.foo"}, + {note: "undefined global function", query: `foo(1)`, err: "undefined function foo"}, + {note: "var", query: `x = "f"; data[x](1)`, err: "undefined function data[x]"}, + {note: "declared var", query: `x := "f"; data[x](1)`, err: "undefined function data[x]"}, + {note: "declared var in array", query: `x := "f"; data[[x]](1)`, err: "undefined function data[[x]]"}, + } { + t.Run(tc.note, func(t *testing.T) { + _, err := compiler.QueryCompiler().Compile(MustParseBody(tc.query)) + if !strings.Contains(err.Error(), tc.err) { + t.Errorf("Unexpected compilation error: %v (want %s)", err, tc.err) + } + }) + } +} + +func TestCompilerImportsResolved(t *testing.T) { + + modules := map[string]*Module{ + "mod1": MustParseModule(`package ex + +import data +import input +import data.foo +import input.bar +import data.abc as baz +import input.abc as qux`, + ), + } + + c := NewCompiler() + c.Compile(modules) + + assertNotFailed(t, c) + + if len(c.Modules["mod1"].Imports) != 0 { + t.Fatalf("Expected imports to be empty after compile but got: %v", c.Modules) + } + +} + +func TestCompilerExprExpansion(t *testing.T) { + + tests := []struct { + note string + input string + expected []*Expr + }{ + { + note: "identity", + input: "x", + expected: []*Expr{ + MustParseExpr("x"), + }, + }, + { + note: "single", + input: "x+y", + expected: []*Expr{ + MustParseExpr("x+y"), + }, + }, + { + note: "chained", + input: "x+y+z+w", + expected: []*Expr{ + MustParseExpr("plus(x, y, __local0__)"), + MustParseExpr("plus(__local0__, z, __local1__)"), + MustParseExpr("plus(__local1__, w)"), + }, + }, + { + note: "assoc", + input: "x+y*z", + expected: []*Expr{ + MustParseExpr("mul(y, z, __local0__)"), + MustParseExpr("plus(x, __local0__)"), + }, + }, + { + note: "refs", + input: "p[q[f(x)]][g(x)]", + expected: []*Expr{ + MustParseExpr("f(x, __local0__)"), + MustParseExpr("g(x, __local1__)"), + MustParseExpr("p[q[__local0__]][__local1__]"), + }, + }, + { + note: "arrays", + input: "[[f(x)], g(x)]", + expected: []*Expr{ + MustParseExpr("f(x, __local0__)"), + MustParseExpr("g(x, __local1__)"), + MustParseExpr("[[__local0__], __local1__]"), + }, + }, + { + note: "objects", + input: `{f(x): {g(x): h(x)}}`, + expected: []*Expr{ + MustParseExpr("f(x, __local0__)"), + MustParseExpr("g(x, __local1__)"), + MustParseExpr("h(x, __local2__)"), + MustParseExpr("{__local0__: {__local1__: __local2__}}"), + }, + }, + { + note: "sets", + input: `{f(x), {g(x)}}`, + expected: []*Expr{ + MustParseExpr("g(x, __local0__)"), + MustParseExpr("f(x, __local1__)"), + MustParseExpr("{__local1__, {__local0__,}}"), + }, + }, + { + note: "unify", + input: "f(x) = g(x)", + expected: []*Expr{ + MustParseExpr("f(x, __local0__)"), + MustParseExpr("g(x, __local1__)"), + MustParseExpr("__local0__ = __local1__"), + }, + }, + { + note: "unify: composites", + input: "[x, f(x)] = [g(y), y]", + expected: []*Expr{ + MustParseExpr("f(x, __local0__)"), + MustParseExpr("g(y, __local1__)"), + MustParseExpr("[x, __local0__] = [__local1__, y]"), + }, + }, + { + note: "with: term expr", + input: "f[x+1] with input as q", + expected: []*Expr{ + MustParseExpr("plus(x, 1, __local0__) with input as q"), + MustParseExpr("f[__local0__] with input as q"), + }, + }, + { + note: "with: call expr", + input: `f(x) = g(x) with input as p`, + expected: []*Expr{ + MustParseExpr("f(x, __local0__) with input as p"), + MustParseExpr("g(x, __local1__) with input as p"), + MustParseExpr("__local0__ = __local1__ with input as p"), + }, + }, + { + note: "comprehensions", + input: `f(y) = [[plus(x,1) | x = sum(y[z+1])], g(w)]`, + expected: []*Expr{ + MustParseExpr("f(y, __local0__)"), + MustParseExpr("g(w, __local4__)"), + MustParseExpr("__local0__ = [[__local1__ | plus(z,1,__local2__); sum(y[__local2__], __local3__); eq(x, __local3__); plus(x, 1, __local1__)], __local4__]"), + }, + }, + { + note: "indirect references", + input: `[1, 2, 3][i]`, + expected: []*Expr{ + MustParseExpr("__local0__ = [1, 2, 3]"), + MustParseExpr("__local0__[i]"), + }, + }, + { + note: "multiple indirect references", + input: `split(split("foo.bar:qux", ".")[_], ":")[i]`, + expected: []*Expr{ + MustParseExpr(`split("foo.bar:qux", ".", __local0__)`), + MustParseExpr(`split(__local0__[_], ":", __local1__)`), + MustParseExpr(`__local1__[i]`), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + gen := newLocalVarGenerator("", NullTerm()) + expr := MustParseExpr(tc.input) + result := expandExpr(gen, expr.Copy()) + if len(result) != len(tc.expected) { + t.Fatalf("Expected %v exprs but got %v:\n\nExpected:\n\n%v\n\nGot:\n\n%v", len(tc.expected), len(result), Body(tc.expected), Body(result)) + } + for i := range tc.expected { + if !tc.expected[i].Equal(result[i]) { + t.Fatalf("Expected expr %d to be %v but got: %v\n\nExpected:\n\n%v\n\nGot:\n\n%v", i, tc.expected[i], result[i], Body(tc.expected), Body(result)) + } + } + }) + } +} + +func TestCompilerRewriteExprTerms(t *testing.T) { + cases := []struct { + note string + module string + expected any + }{ + { + note: "base", + module: ` + package test + + p { x = a + b * y } + + q[[data.test.f(x)]] { x = 1 } + + r = [data.test.f(x)] { x = 1 } + + f(x) = data.test.g(x) + + pi = 3 + .14 + + with_value { 1 with input as f(1) } + `, + expected: ` + package test + + p { mul(b, y, __local1__); plus(a, __local1__, __local2__); eq(x, __local2__) } + + q[[__local3__]] { x = 1; data.test.f(x, __local3__) } + + r = [__local4__] { x = 1; data.test.f(x, __local4__) } + + f(__local0__) = __local5__ { true; data.test.g(__local0__, __local5__) } + + pi = __local6__ { true; plus(3, 0.14, __local6__) } + + with_value { data.test.f(1, __local7__); 1 with input as __local7__ } + `, + }, + { + note: "builtin calls in head", + module: ` + package test + + f(1+1) = 7 + `, + expected: Errors{&Error{Message: "rule arguments cannot contain calls"}}, + }, + { + note: "builtin calls in head", + module: ` + package test + + f(object.get(x)) { object := {"a": 1}; object.a == x } + `, + expected: Errors{&Error{Message: "rule arguments cannot contain calls"}}, + }, + { + note: "indirect ref in args", + module: ` + package test + + f([1][0]) { true }`, + expected: ` + package test + + f(__local0__[0]) { true; __local0__ = [1] }`, + }, + { + note: "every: domain (array)", + module: ` + package test + + p { every x in [1,2] { x } }`, + expected: ` + package test + + p { __local2__ = [1, 2]; every __local0__, __local1__ in __local2__ { __local1__ } }`, + }, + { + note: "every: domain (call)", + module: ` + package test + + p { every x in numbers.range(1, 3) { x } }`, + expected: ` + package test + + p = true { + numbers.range(1, 3, __local3__) + __local2__ = __local3__ + every __local0__, __local1__ in __local2__ { + __local1__ + } + }`, + }, + { + note: "every: domain (nested calls)", + module: ` + package test + + p { every x in numbers.range(1 + 2, 3 * 4) { x } }`, + expected: ` + package test + + p = true { + plus(1, 2, __local3__) + mul(3, 4, __local4__) + numbers.range(__local3__, __local4__, __local5__) + __local2__ = __local5__ + every __local0__, __local1__ in __local2__ { + __local1__ + } + }`, + }, + // Regression test for GH issue #6790 + { + note: "every: domain (array with call)", + module: ` + package test + + p { every x in [1 / 2, "foo", abs(-1)] { x } }`, + expected: ` + package test + + p = true { + div(1, 2, __local3__) + abs(-1, __local4__) + __local2__ = [__local3__, "foo", __local4__] + every __local0__, __local1__ in __local2__ { + __local1__ + } + }`, + }, + { + note: "every: domain (nested array with call)", + module: ` + package test + + p { every x in [1 / 2, ["foo", abs(-1)]] { x } }`, + expected: ` + package test + + p = true { + div(1, 2, __local3__) + abs(-1, __local4__) + __local2__ = [__local3__, ["foo", __local4__]] + every __local0__, __local1__ in __local2__ { + __local1__ + } + }`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + compiler := NewCompiler() + opts := ParserOptions{ + RegoVersion: RegoV0, + AllFutureKeywords: true, + unreleasedKeywords: true, + } + + compiler.Modules = map[string]*Module{ + "test": MustParseModuleWithOpts(tc.module, opts), + } + compileStages(compiler, compiler.rewriteExprTerms) + + switch exp := tc.expected.(type) { + case string: + assertNotFailed(t, compiler) + + expected := MustParseModuleWithOpts(exp, opts) + + if !expected.Equal(compiler.Modules["test"]) { + t.Fatalf("Expected modules to be equal. Expected:\n\n%v\n\nGot:\n\n%v", expected, compiler.Modules["test"]) + } + case Errors: + assertErrors(t, compiler.Errors, exp, false) + default: + t.Fatalf("Unsupported value type for test case 'expected' field: %v", exp) + } + + }) + } +} + +func TestIllegalFunctionCallRewrite(t *testing.T) { + cases := []struct { + note string + module string + expectedErrors []string + }{ + /*{ + note: "function call override in function value", + module: `package test + foo(x) := x + + p := foo(bar) { + #foo := 1 + bar := 2 + }`, + expectedErrors: []string{ + "undefined function foo", + }, + },*/ + { + note: "function call override in array comprehension value", + module: `package test +p := [foo(bar) | foo := 1; bar := 2]`, + expectedErrors: []string{ + "called function foo shadowed", + }, + }, + { + note: "function call override in set comprehension value", + module: `package test +p := {foo(bar) | foo := 1; bar := 2}`, + expectedErrors: []string{ + "called function foo shadowed", + }, + }, + { + note: "function call override in object comprehension value", + module: `package test +p := {foo(bar): bar(foo) | foo := 1; bar := 2}`, + expectedErrors: []string{ + "called function bar shadowed", + "called function foo shadowed", + }, + }, + { + note: "function call override in array comprehension value", + module: `package test +p := [foo.bar(baz) | foo := 1; bar := 2; baz := 3]`, + expectedErrors: []string{ + "called function foo.bar shadowed", + }, + }, + { + note: "nested function call override in array comprehension value", + module: `package test +p := [baz(foo(bar)) | foo := 1; bar := 2]`, + expectedErrors: []string{ + "called function foo shadowed", + }, + }, + { + note: "function call override of 'input' root document", + module: `package test +p := [input() | input := 1]`, + expectedErrors: []string{ + "called function input shadowed", + }, + }, + { + note: "function call override of 'data' root document", + module: `package test +p := [data() | data := 1]`, + expectedErrors: []string{ + "called function data shadowed", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + compiler := NewCompiler() + opts := ParserOptions{ + RegoVersion: RegoV0, + AllFutureKeywords: true, + unreleasedKeywords: true, + } + + compiler.Modules = map[string]*Module{ + "test": MustParseModuleWithOpts(tc.module, opts), + } + compileStages(compiler, compiler.rewriteLocalVars) + + result := make([]string, 0, len(compiler.Errors)) + for i := range compiler.Errors { + result = append(result, compiler.Errors[i].Message) + } + + sort.Strings(tc.expectedErrors) + sort.Strings(result) + + if len(tc.expectedErrors) != len(result) { + t.Fatalf("Expected %d errors but got %d:\n\n%v\n\nGot:\n\n%v", + len(tc.expectedErrors), len(result), + strings.Join(tc.expectedErrors, "\n"), strings.Join(result, "\n")) + } + + for i := range result { + if result[i] != tc.expectedErrors[i] { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", + strings.Join(tc.expectedErrors, "\n"), strings.Join(result, "\n")) + } + } + }) + } +} + +func TestCompilerCheckUnusedImports(t *testing.T) { + cases := []strictnessTestCase{ + { + note: "simple unused: input ref with same name", + module: `package p + import data.foo.bar as bar + r { + input.bar == 11 + } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("import"), "", 2, 4), + Message: "import data.foo.bar as bar unused", + }, + }, + }, + { + note: "unused import, but imported ref used", + module: `package p + import data.foo # unused + r { data.foo == 10 } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("import"), "", 2, 4), + Message: "import data.foo unused", + }, + }, + }, + { + note: "one of two unused", + module: `package p + import data.foo + import data.x.power #unused + r { foo == 10 } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("import"), "", 3, 4), + Message: "import data.x.power unused", + }, + }, + }, + { + note: "multiple unused: with input ref of same name", + module: `package p + import data.foo + import data.x.power + r { input.foo == 10 } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("import"), "", 2, 4), + Message: "import data.foo unused", + }, + &Error{ + Location: NewLocation([]byte("import"), "", 3, 4), + Message: "import data.x.power unused", + }, + }, + }, + { + note: "ignore unused rego import", + module: `package p + import rego.v1 + r if { 10 == 10 } + `, + }, + { + note: "import used in comparison", + module: `package p + import data.foo.x + r { x == 10 } + `, + }, + { + note: "multiple used imports in one rule", + module: `package p + import data.foo.x + import data.power.ranger + r { ranger == x } + `, + }, + { + note: "multiple used imports in separate rules", + module: `package p + import data.foo.x + import data.power.ranger + r { ranger == 23 } + t { x == 1 } + `, + }, + { + note: "import used as function operand", + module: `package p + import data.foo + r = count(foo) > 1 # only one operand + `, + }, + { + note: "import used as function operand, compount term", + module: `package p + import data.foo + r = sprintf("%v %d", [foo, 0]) + `, + }, + { + note: "import used as plain term", + module: `package p + import data.foo + r { + foo + } + `, + }, + { + note: "import used in 'every' domain", + module: `package p + import future.keywords.every + import data.foo + r { + every x in foo { x > 1 } + } + `, + }, + { + note: "import used in 'every' body", + module: `package p + import future.keywords.every + import data.foo + r { + every x in [1,2,3] { x > foo } + } + `, + }, + { + note: "future import kept even if unused", + module: `package p + import future.keywords + + r { true } + `, + }, + { + note: "shadowed var name in function arg", + module: `package p + import data.foo # unused + + r { f(1) } + f(foo) = foo == 1 + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("import"), "", 2, 4), + Message: "import data.foo unused", + }, + }, + }, + { + note: "shadowed assigned var name", + module: `package p + import data.foo # unused + + r { foo := true; foo } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("import"), "", 2, 4), + Message: "import data.foo unused", + }, + }, + }, + { + note: "used as rule value", + module: `package p + import data.bar # unused + import data.foo + + r = foo { true } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("import"), "", 2, 4), + Message: "import data.bar unused", + }, + }, + }, + { + note: "unused as rule value (but same data ref)", + module: `package p + import data.bar # unused + import data.foo # unused + + r = data.foo { true } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("import"), "", 2, 4), + Message: "import data.bar unused", + }, + &Error{ + Location: NewLocation([]byte("import"), "", 3, 4), + Message: "import data.foo unused", + }, + }, + }, + } + + runStrictnessTestCase(t, cases, true) +} + +func TestCompilerCheckDuplicateImports(t *testing.T) { + cases := []strictnessTestCase{ + { + note: "shadow", + module: `package test + import input.noconflict + import input.foo + import data.foo + import data.bar.foo + + p := noconflict + q := foo + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("import"), "", 4, 5), + Message: "import must not shadow import input.foo", + }, + &Error{ + Location: NewLocation([]byte("import"), "", 5, 5), + Message: "import must not shadow import input.foo", + }, + }, + }, { + note: "alias shadow", + module: `package test + import input.noconflict + import input.foo + import input.bar as foo + + p := noconflict + q := foo + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("import"), "", 4, 5), + Message: "import must not shadow import input.foo", + }, + }, + }, + } + + runStrictnessTestCase(t, cases, true) +} + +func TestCompilerCheckKeywordOverrides(t *testing.T) { + cases := []strictnessTestCase{ + { + note: "rule names", + module: `package test + input { true } + p { true } + data { true } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("input { true }"), "", 2, 5), + Message: "rules must not shadow input (use a different rule name)", + }, + &Error{ + Location: NewLocation([]byte("data { true }"), "", 4, 5), + Message: "rules must not shadow data (use a different rule name)", + }, + }, + }, + { + note: "rule names (set construction)", + module: `package test + input.a { true } + p { true } + data.b { true } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("input.a { true }"), "", 2, 5), + Message: "rules must not shadow input (use a different rule name)", + }, + &Error{ + Location: NewLocation([]byte("data.b { true }"), "", 4, 5), + Message: "rules must not shadow data (use a different rule name)", + }, + }, + }, + { + note: "rule names (object construction)", + module: `package test + input.a := 1 { true } + p { true } + data.b := 2 { true } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("input.a := 1 { true }"), "", 2, 5), + Message: "rules must not shadow input (use a different rule name)", + }, + &Error{ + Location: NewLocation([]byte("data.b := 2 { true }"), "", 4, 5), + Message: "rules must not shadow data (use a different rule name)", + }, + }, + }, + { + note: "leading term in rule refs", + module: `package test + input.a.b { true } + p { true } + data.b.c := "foo" { true } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("input.a.b { true }"), "", 2, 5), + Message: "rules must not shadow input (use a different rule name)", + }, + &Error{ + Location: NewLocation([]byte(`data.b.c := "foo" { true }`), "", 4, 5), + Message: "rules must not shadow data (use a different rule name)", + }, + }, + }, + { + note: "global assignments", + module: `package test + input = 1 + p := 2 + data := 3 + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("input = 1"), "", 2, 5), + Message: "rules must not shadow input (use a different rule name)", + }, + &Error{ + Location: NewLocation([]byte("data := 3"), "", 4, 5), + Message: "rules must not shadow data (use a different rule name)", + }, + }, + }, + { + note: "rule-local assignments", + module: `package test + p { + input := 1 + x := 2 + } else { + data := 3 + } + q { + input := 4 + } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("input := 1"), "", 3, 6), + Message: "variables must not shadow input (use a different variable name)", + }, + &Error{ + Location: NewLocation([]byte("data := 3"), "", 6, 6), + Message: "variables must not shadow data (use a different variable name)", + }, + &Error{ + Location: NewLocation([]byte("input := 4"), "", 9, 6), + Message: "variables must not shadow input (use a different variable name)", + }, + }, + }, + { + note: "array comprehension-local assignments", + module: `package test + p = [ x | + input := 1 + x := 2 + data := 3 + ] + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("input := 1"), "", 3, 6), + Message: "variables must not shadow input (use a different variable name)", + }, + &Error{ + Location: NewLocation([]byte("data := 3"), "", 5, 6), + Message: "variables must not shadow data (use a different variable name)", + }, + }, + }, + { + note: "set comprehension-local assignments", + module: `package test + p = { x | + input := 1 + x := 2 + data := 3 + } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("input := 1"), "", 3, 6), + Message: "variables must not shadow input (use a different variable name)", + }, + &Error{ + Location: NewLocation([]byte("data := 3"), "", 5, 6), + Message: "variables must not shadow data (use a different variable name)", + }, + }, + }, + { + note: "object comprehension-local assignments", + module: `package test + p = { x: 1 | + input := 1 + x := 2 + data := 3 + } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("input := 1"), "", 3, 6), + Message: "variables must not shadow input (use a different variable name)", + }, + &Error{ + Location: NewLocation([]byte("data := 3"), "", 5, 6), + Message: "variables must not shadow data (use a different variable name)", + }, + }, + }, + { + note: "nested override", + module: `package test + p { + [ x | + input := 1 + x := 2 + data := 3 + ] + } + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("input := 1"), "", 4, 7), + Message: "variables must not shadow input (use a different variable name)", + }, + &Error{ + Location: NewLocation([]byte("data := 3"), "", 6, 7), + Message: "variables must not shadow data (use a different variable name)", + }, + }, + }, + } + + runStrictnessTestCase(t, cases, true) +} + +func TestCompilerCheckDeprecatedMethods(t *testing.T) { + cases := []strictnessTestCase{ + { + note: "all() built-in", + module: `package test + p := all([true, false]) + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("all([true, false])"), "", 2, 10), + Message: "deprecated built-in function calls in expression: all", + }, + }, + }, + { + note: "user-defined all()", + module: `package test + import future.keywords.in + all(arr) = {x | some x in arr} == {true} + p := all([true, false]) + `, + }, + { + note: "any() built-in", + module: `package test + p := any([true, false]) + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte("any([true, false])"), "", 2, 10), + Message: "deprecated built-in function calls in expression: any", + }, + }, + }, + { + note: "user-defined any()", + module: `package test + import future.keywords.in + any(arr) := true in arr + p := any([true, false]) + `, + }, + { + note: "re_match built-in", + module: `package test + p := re_match("[a]", "a") + `, + expectedErrors: Errors{ + &Error{ + Location: NewLocation([]byte(`re_match("[a]", "a")`), "", 2, 10), + Message: "deprecated built-in function calls in expression: re_match", + }, + }, + }, + } + + runStrictnessTestCase(t, cases, true) +} + +type strictnessTestCase struct { + note string + module string + expectedErrors Errors +} + +func runStrictnessTestCase(t *testing.T, cases []strictnessTestCase, assertLocation bool) { + t.Helper() + makeTestRunner := func(tc strictnessTestCase, strict bool) func(t *testing.T) { + return func(t *testing.T) { + compiler := NewCompiler().WithStrict(strict) + compiler.Modules = map[string]*Module{ + "test": MustParseModuleWithOpts(tc.module, ParserOptions{RegoVersion: RegoV0}), + } + compileStages(compiler, nil) + + if strict { + assertErrors(t, compiler.Errors, tc.expectedErrors, assertLocation) + } else { + assertNotFailed(t, compiler) + } + } + } + + for _, tc := range cases { + t.Run(tc.note+"_strict", makeTestRunner(tc, true)) + t.Run(tc.note+"_non-strict", makeTestRunner(tc, false)) + } +} + +func assertErrors(t *testing.T, actual Errors, expected Errors, assertLocation bool) { + t.Helper() + if len(expected) != len(actual) { + t.Fatalf("Expected %d errors, got %d:\n\n%s\n", len(expected), len(actual), actual.Error()) + } + incorrectErrs := false + for _, e := range expected { + found := false + for _, actual := range actual { + if e.Message == actual.Message { + if !assertLocation || e.Location.Equal(actual.Location) { + found = true + break + } + } + } + if !found { + incorrectErrs = true + } + } + if incorrectErrs { + t.Fatalf("Expected errors:\n\n%s\n\nGot:\n\n%s\n", expected.Error(), actual.Error()) + } +} + +func TestCompileRegoV1Import(t *testing.T) { + cases := []struct { + note string + modules map[string]string + expectedErrors Errors + }{ + // Duplicate imports + { + note: "duplicate imports", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + import data.foo + import data.bar.foo + p if { + foo == "bar" + }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "import must not shadow import data.foo", + Location: &Location{Text: []byte("import"), File: "policy.rego", Row: 4, Col: 6}, + }, + }, + }, + { + note: "duplicate imports (alias)", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + import data.foo + import data.bar as foo + p if { + foo == "bar" + }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "import must not shadow import data.foo", + Location: &Location{Text: []byte("import"), File: "policy.rego", Row: 4, Col: 6}, + }, + }, + }, + { + note: "duplicate imports (alias, different order)", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + import data.bar as foo + import data.foo + p if { + foo == "bar" + }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "import must not shadow import data.bar as foo", + Location: &Location{Text: []byte("import"), File: "policy.rego", Row: 4, Col: 6}, + }, + }, + }, + { + note: "duplicate imports (repeat)", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + import data.foo + import data.foo + p if { + foo == "bar" + }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "import must not shadow import data.foo", + Location: &Location{Text: []byte("import"), File: "policy.rego", Row: 4, Col: 6}, + }, + }, + }, + { + note: "duplicate imports (multiple modules)", + modules: map[string]string{ + "policy1.rego": `package test + import rego.v1 + import data.foo + import data.bar.foo + p if { + foo == "bar" + }`, + "policy2.rego": `package test + import rego.v1 + import data.foo + import data.bar.foo + q if { + foo == "bar" + }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "import must not shadow import data.foo", + Location: &Location{Text: []byte("import"), File: "policy1.rego", Row: 4, Col: 6}, + }, + &Error{ + Message: "import must not shadow import data.foo", + Location: &Location{Text: []byte("import"), File: "policy2.rego", Row: 4, Col: 6}, + }, + }, + }, + { + note: "duplicate imports (multiple modules, not all strict)", + modules: map[string]string{ + "policy1.rego": `package test + import future.keywords.if + import data.foo + import data.bar.foo + p if { + foo == "bar" + }`, + "policy2.rego": `package test + import rego.v1 + import data.foo + import data.bar.foo + q if { + foo == "bar" + }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "import must not shadow import data.foo", + Location: &Location{Text: []byte("import"), File: "policy2.rego", Row: 4, Col: 6}, + }, + }, + }, + // var shadowing + { + note: "var shadows input", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + p if { + input := 1 + input == 1 + }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "variables must not shadow input (use a different variable name)", + Location: &Location{Text: []byte("input := 1"), File: "policy.rego", Row: 4, Col: 7}, + }, + }, + }, + { + note: "var shadows input (multiple modules)", + modules: map[string]string{ + "policy1.rego": `package test + import rego.v1 + p if { + input := 1 + input == 1 + }`, + "policy2.rego": `package test + import rego.v1 + q if { + input := 1 + input == 1 + }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "variables must not shadow input (use a different variable name)", + Location: &Location{Text: []byte("input := 1"), File: "policy1.rego", Row: 4, Col: 7}, + }, + &Error{ + Message: "variables must not shadow input (use a different variable name)", + Location: &Location{Text: []byte("input := 1"), File: "policy2.rego", Row: 4, Col: 7}, + }, + }, + }, + { + note: "var shadows input (multiple modules, not all strict)", + modules: map[string]string{ + "policy1.rego": `package test + import future.keywords.if + p if { + input := 1 + input == 1 + }`, + "policy2.rego": `package test + import rego.v1 + q if { + input := 1 + input == 1 + }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "variables must not shadow input (use a different variable name)", + Location: &Location{Text: []byte("input := 1"), File: "policy2.rego", Row: 4, Col: 7}, + }, + }, + }, + { + note: "var shadows data", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + p if { + data := 1 + data == 1 + }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "variables must not shadow data (use a different variable name)", + Location: &Location{Text: []byte("data := 1"), File: "policy.rego", Row: 4, Col: 7}, + }, + }, + }, + { + note: "var shadows data (multiple modules)", + modules: map[string]string{ + "policy1.rego": `package test + import rego.v1 + p if { + data := 1 + data == 1 + }`, + "policy2.rego": `package test + import rego.v1 + q if { + data := 1 + data == 1 + }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "variables must not shadow data (use a different variable name)", + Location: &Location{Text: []byte("data := 1"), File: "policy1.rego", Row: 4, Col: 7}, + }, + &Error{ + Message: "variables must not shadow data (use a different variable name)", + Location: &Location{Text: []byte("data := 1"), File: "policy2.rego", Row: 4, Col: 7}, + }, + }, + }, + { + note: "var shadows data (multiple modules, not all strict)", + modules: map[string]string{ + "policy1.rego": `package test + import future.keywords.if + p if { + data := 1 + data == 1 + }`, + "policy2.rego": `package test + import rego.v1 + q if { + data := 1 + data == 1 + }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "variables must not shadow data (use a different variable name)", + Location: &Location{Text: []byte("data := 1"), File: "policy2.rego", Row: 4, Col: 7}, + }, + }, + }, + // rule shadowing + { + note: "rule shadows input", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + input := 1`, + }, + expectedErrors: Errors{ + &Error{ + Message: "rules must not shadow input (use a different rule name)", + Location: &Location{Text: []byte("input := 1"), File: "policy.rego", Row: 3, Col: 6}, + }, + }, + }, + { + note: "rule (object) shadows input", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + input.a := "b" if { true }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "rules must not shadow input (use a different rule name)", + Location: &Location{Text: []byte(`input.a := "b" if { true }`), File: "policy.rego", Row: 3, Col: 6}, + }, + }, + }, + { + note: "rule (set) shadows input", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + input contains "a" if { true }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "rules must not shadow input (use a different rule name)", + Location: &Location{Text: []byte(`input contains "a" if { true }`), File: "policy.rego", Row: 3, Col: 6}, + }, + }, + }, + { + note: "rule ref shadows input", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + input.a.b.c := 1`, + }, + expectedErrors: Errors{ + &Error{ + Message: "rules must not shadow input (use a different rule name)", + Location: &Location{Text: []byte("input.a.b.c := 1"), File: "policy.rego", Row: 3, Col: 6}, + }, + }, + }, + { + note: "rule shadows input (multiple modules)", + modules: map[string]string{ + "policy1.rego": `package test + import rego.v1 + input := 1`, + "policy2.rego": `package test2 + import rego.v1 + input := 2`, + }, + expectedErrors: Errors{ + &Error{ + Message: "rules must not shadow input (use a different rule name)", + Location: &Location{Text: []byte("input := 1"), File: "policy1.rego", Row: 3, Col: 6}, + }, + &Error{ + Message: "rules must not shadow input (use a different rule name)", + Location: &Location{Text: []byte("input := 2"), File: "policy2.rego", Row: 3, Col: 6}, + }, + }, + }, + { + note: "rule shadows input (multiple modules, not all strict)", + modules: map[string]string{ + "policy1.rego": `package test + input := 1`, + "policy2.rego": `package test2 + import rego.v1 + input := 2`, + }, + expectedErrors: Errors{ + &Error{ + Message: "rules must not shadow input (use a different rule name)", + Location: &Location{Text: []byte("input := 2"), File: "policy2.rego", Row: 3, Col: 6}, + }, + }, + }, + { + note: "rule shadows data", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + data := 1`, + }, + expectedErrors: Errors{ + &Error{ + Message: "rules must not shadow data (use a different rule name)", + Location: &Location{Text: []byte("data := 1"), File: "policy.rego", Row: 3, Col: 6}, + }, + }, + }, + { + note: "rule (object) shadows input", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + data.a := "b" if { true }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "rules must not shadow data (use a different rule name)", + Location: &Location{Text: []byte(`data.a := "b" if { true }`), File: "policy.rego", Row: 3, Col: 6}, + }, + }, + }, + { + note: "rule (set) shadows input", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + data contains "a" if { true }`, + }, + expectedErrors: Errors{ + &Error{ + Message: "rules must not shadow data (use a different rule name)", + Location: &Location{Text: []byte(`data contains "a" if { true }`), File: "policy.rego", Row: 3, Col: 6}, + }, + }, + }, + { + note: "rule ref shadows input", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + data.a.b.c := 1`, + }, + expectedErrors: Errors{ + &Error{ + Message: "rules must not shadow data (use a different rule name)", + Location: &Location{Text: []byte("data.a.b.c := 1"), File: "policy.rego", Row: 3, Col: 6}, + }, + }, + }, + { + note: "rule shadows data (multiple modules)", + modules: map[string]string{ + "policy1.rego": `package test + import rego.v1 + data := 1`, + "policy2.rego": `package test2 + import rego.v1 + data := 2`, + }, + expectedErrors: Errors{ + &Error{ + Message: "rules must not shadow data (use a different rule name)", + Location: &Location{Text: []byte("data := 1"), File: "policy1.rego", Row: 3, Col: 6}, + }, + &Error{ + Message: "rules must not shadow data (use a different rule name)", + Location: &Location{Text: []byte("data := 2"), File: "policy2.rego", Row: 3, Col: 6}, + }, + }, + }, + { + note: "rule shadows data (multiple modules, not all strict)", + modules: map[string]string{ + "policy1.rego": `package test + data := 1`, + "policy2.rego": `package test2 + import rego.v1 + data := 2`, + }, + expectedErrors: Errors{ + &Error{ + Message: "rules must not shadow data (use a different rule name)", + Location: &Location{Text: []byte("data := 2"), File: "policy2.rego", Row: 3, Col: 6}, + }, + }, + }, + // deprecated built-ins + { + note: "deprecated built-in", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + p := all([true, false])`, + }, + expectedErrors: Errors{ + &Error{ + Message: "deprecated built-in function calls in expression: all", + Location: &Location{Text: []byte("all([true, false])"), File: "policy.rego", Row: 3, Col: 11}, + }, + }, + }, + { + note: "deprecated built-in (multiple)", + modules: map[string]string{ + "policy.rego": `package test + import rego.v1 + p := all([true, false]) + q := any([true, false])`, + }, + expectedErrors: Errors{ + &Error{ + Message: "deprecated built-in function calls in expression: all", + Location: &Location{Text: []byte("all([true, false])"), File: "policy.rego", Row: 3, Col: 11}, + }, + &Error{ + Message: "deprecated built-in function calls in expression: any", + Location: &Location{Text: []byte("any([true, false])"), File: "policy.rego", Row: 4, Col: 11}, + }, + }, + }, + { + note: "deprecated built-in (multiple modules)", + modules: map[string]string{ + "policy1.rego": `package test + import rego.v1 + p := all([true, false])`, + "policy2.rego": `package test + import rego.v1 + q := all([true, false])`, + }, + expectedErrors: Errors{ + &Error{ + Message: "deprecated built-in function calls in expression: all", + Location: &Location{Text: []byte("all([true, false])"), File: "policy1.rego", Row: 3, Col: 11}, + }, + &Error{ + Message: "deprecated built-in function calls in expression: all", + Location: &Location{Text: []byte("all([true, false])"), File: "policy2.rego", Row: 3, Col: 11}, + }, + }, + }, + { + note: "deprecated built-in (multiple modules, not all strict)", + modules: map[string]string{ + "policy1.rego": `package test + p := all([true, false])`, + "policy2.rego": `package test + import rego.v1 + q := all([true, false])`, + }, + expectedErrors: Errors{ + &Error{ + Message: "deprecated built-in function calls in expression: all", + Location: &Location{Text: []byte("all([true, false])"), File: "policy2.rego", Row: 3, Col: 11}, + }, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + compiler := NewCompiler() + compiler.Modules = map[string]*Module{} + for name, mod := range tc.modules { + if parsed, err := ParseModuleWithOpts(name, mod, ParserOptions{RegoVersion: RegoV0}); err != nil { + t.Fatal(err) + } else { + compiler.Modules[name] = parsed + } + } + compileStages(compiler, nil) + assertErrors(t, compiler.Errors, tc.expectedErrors, true) + }) + } +} + +// NOTE(sr): the tests below this function are unwieldy, let's keep adding new ones to this one +func TestCompilerResolveAllRefsNewTests(t *testing.T) { + tests := []struct { + note string + mod string + exp string + extra string + }{ + { + note: "ref-rules referenced in body", + mod: `package test +a.b.c = 1 +q if a.b.c == 1 +`, + exp: `package test +a.b.c = 1 if { true } +q if data.test.a.b.c = 1 +`, + }, + { + // NOTE(sr): This is a conservative extension of how it worked before: + // we will not automatically extend references to other parts of the rule tree, + // only to ref rules defined on the same level. + note: "ref-rules from other module referenced in body", + mod: `package test +q if a.b.c == 1 +`, + extra: `package test +a.b.c = 1 +`, + exp: `package test +q if data.test.a.b.c = 1 +`, + }, + { + note: "single-value rule in comprehension in call", // NOTE(sr): this is TestRego/partialiter/objects_conflict + mod: `package test +p := count([x | q[x]]) +q[1] = 1 +`, + exp: `package test +p := __local0__ if { true; __local1__ = [x | data.test.q[x]]; count(__local1__, __local0__) } +q[1] = 1 +`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + opts := ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true} + c := NewCompiler() + mod, err := ParseModuleWithOpts("test.rego", tc.mod, opts) + if err != nil { + t.Fatal(err) + } + exp, err := ParseModuleWithOpts("test.rego", tc.exp, opts) + if err != nil { + t.Fatal(err) + } + mods := map[string]*Module{"test": mod} + if tc.extra != "" { + extra, err := ParseModuleWithOpts("test.rego", tc.extra, opts) + if err != nil { + t.Fatal(err) + } + mods["extra"] = extra + } + c.Compile(mods) + if err := c.Errors; len(err) > 0 { + t.Errorf("compile module: %v", err) + } + if act := c.Modules["test"]; !exp.Equal(act) { + t.Errorf("compiled: expected %v, got %v", exp, act) + } + }) + } +} + +func TestCompilerResolveAllRefs(t *testing.T) { + c := NewCompiler() + c.Modules = getCompilerTestModules() + c.Modules["head"] = MustParseModule(`package head + +import rego.v1 +import data.doc1 as bar +import input.x.y.foo +import input.qux as baz + +p[foo[bar[i]]] := {"baz": baz} if { true }`) + + c.Modules["elsekw"] = MustParseModule(`package elsekw + + import rego.v1 + import input.x.y.foo + import data.doc1 as bar + import input.baz + + p if { + false + } else = foo if { + bar + } else = baz if { + true + } + `) + + c.Modules["nestedexprs"] = MustParseModule(`package nestedexprs + import rego.v1 + + x = 1 + + p if { + f(g(x)) + }`) + + c.Modules["assign"] = MustParseModule(`package assign + import rego.v1 + + x = 1 + y = 1 + + p if { + x := y + [true | x := y] + }`) + + c.Modules["someinassign"] = MustParseModule(`package someinassign + import rego.v1 + + x = 1 + y = 1 + + p[x] if { + some x in [1, 2, y] + }`) + + c.Modules["someinassignwithkey"] = MustParseModule(`package someinassignwithkey + import rego.v1 + + x = 1 + y = 1 + + p[x] if { + some k, v in [1, 2, y] + }`) + + c.Modules["donotresolve"] = MustParseModule(`package donotresolve + import rego.v1 + + x = 1 + + f(x) if { + x = 2 + } + `) + + c.Modules["indirectrefs"] = MustParseModule(`package indirectrefs + import rego.v1 + + f(x) = [x] if {true} + + p if { + f(1)[0] + } + `) + + c.Modules["comprehensions"] = MustParseModule(`package comprehensions + import rego.v1 + + nums = [1, 2, 3] + + f(x) = [x] if {true} + + p[[1]] if {true} + + q if { + p[[x | x = nums[_]]] + } + + r = [y | y = f(1)[0]] + `) + + c.Modules["everykw"] = MustParseModule(`package everykw + import rego.v1 + + nums = {1, 2, 3} + f(_) = true + x = 100 + xs = [1, 2, 3] + p if { + every x in xs { + nums[x] + x > 10 + } + }`) + + c.Modules["heads_with_dots"] = MustParseModule(`package heads_with_dots + import rego.v1 + + this_is_not = true + this.is.dotted if { this_is_not } + `) + + compileStages(c, c.resolveAllRefs) + assertNotFailed(t, c) + + // Basic test cases. + mod1 := c.Modules["mod1"] + p := mod1.Rules[0] + expr1 := p.Body[0] + term := expr1.Terms.(*Term) + e := MustParseTerm("data.a.b.c.q[x]") + if !term.Equal(e) { + t.Errorf("Wrong term (global in same module): expected %v but got: %v", e, term) + } + + expr2 := p.Body[1] + term = expr2.Terms.(*Term) + e = MustParseTerm("data.a.b.c.r[x]") + if !term.Equal(e) { + t.Errorf("Wrong term (global in same package/diff module): expected %v but got: %v", e, term) + } + + mod2 := c.Modules["mod2"] + r := mod2.Rules[0] + expr3 := r.Body[1] + term = expr3.Terms.([]*Term)[1] + e = MustParseTerm("data.x.y.p") + if !term.Equal(e) { + t.Errorf("Wrong term (var import): expected %v but got: %v", e, term) + } + + mod3 := c.Modules["mod3"] + expr4 := mod3.Rules[0].Body[0] + term = expr4.Terms.([]*Term)[2] + e = MustParseTerm("{input.x.secret: [{input.x.keyid}]}") + if !term.Equal(e) { + t.Errorf("Wrong term (nested refs): expected %v but got: %v", e, term) + } + + // Array comprehensions. + mod5 := c.Modules["mod5"] + + ac := func(r *Rule) *ArrayComprehension { + return r.Body[0].Terms.(*Term).Value.(*ArrayComprehension) + } + + acTerm1 := ac(mod5.Rules[0]) + assertTermEqual(t, acTerm1.Term, MustParseTerm("input.x.a")) + acTerm2 := ac(mod5.Rules[1]) + assertTermEqual(t, acTerm2.Term, MustParseTerm("data.a.b.c.q.a")) + acTerm3 := ac(mod5.Rules[2]) + assertTermEqual(t, acTerm3.Body[0].Terms.([]*Term)[1], MustParseTerm("input.x.a")) + acTerm4 := ac(mod5.Rules[3]) + assertTermEqual(t, acTerm4.Body[0].Terms.([]*Term)[1], MustParseTerm("data.a.b.c.q[i]")) + acTerm5 := ac(mod5.Rules[4]) + assertTermEqual(t, acTerm5.Body[0].Terms.([]*Term)[2].Value.(*ArrayComprehension).Term, MustParseTerm("input.x.a")) + acTerm6 := ac(mod5.Rules[5]) + assertTermEqual(t, acTerm6.Body[0].Terms.([]*Term)[2].Value.(*ArrayComprehension).Body[0].Terms.([]*Term)[1], MustParseTerm("data.a.b.c.q[i]")) + + // Nested references. + mod6 := c.Modules["mod6"] + nested1 := mod6.Rules[0].Body[0].Terms.(*Term) + assertTermEqual(t, nested1, MustParseTerm("data.x[input.x[i].a[data.z.b[j]]]")) + + nested2 := mod6.Rules[1].Body[1].Terms.(*Term) + assertTermEqual(t, nested2, MustParseTerm("v[input.x[i]]")) + + nested3 := mod6.Rules[3].Body[0].Terms.(*Term) + assertTermEqual(t, nested3, MustParseTerm("data.x[data.a.b.nested.r]")) + + // Refs in head. + mod7 := c.Modules["head"] + assertTermEqual(t, mod7.Rules[0].Head.Key, MustParseTerm("input.x.y.foo[data.doc1[i]]")) + assertTermEqual(t, mod7.Rules[0].Head.Value, MustParseTerm(`{"baz": input.qux}`)) + + // Refs in else. + mod8 := c.Modules["elsekw"] + assertTermEqual(t, mod8.Rules[0].Else.Head.Value, MustParseTerm("input.x.y.foo")) + assertTermEqual(t, mod8.Rules[0].Else.Body[0].Terms.(*Term), MustParseTerm("data.doc1")) + assertTermEqual(t, mod8.Rules[0].Else.Else.Head.Value, MustParseTerm("input.baz")) + + // Refs in calls. + mod9 := c.Modules["nestedexprs"] + assertTermEqual(t, mod9.Rules[1].Body[0].Terms.([]*Term)[1], CallTerm(RefTerm(VarTerm("g")), MustParseTerm("data.nestedexprs.x"))) + + // Ignore assigned vars. + mod10 := c.Modules["assign"] + assertTermEqual(t, mod10.Rules[2].Body[0].Terms.([]*Term)[1], VarTerm("x")) + assertTermEqual(t, mod10.Rules[2].Body[0].Terms.([]*Term)[2], MustParseTerm("data.assign.y")) + assignCompr := mod10.Rules[2].Body[1].Terms.(*Term).Value.(*ArrayComprehension) + assertTermEqual(t, assignCompr.Body[0].Terms.([]*Term)[1], VarTerm("x")) + assertTermEqual(t, assignCompr.Body[0].Terms.([]*Term)[2], MustParseTerm("data.assign.y")) + + // Args + mod11 := c.Modules["donotresolve"] + assertTermEqual(t, mod11.Rules[1].Head.Args[0], VarTerm("x")) + assertExprEqual(t, mod11.Rules[1].Body[0], MustParseExpr("x = 2")) + + // Locations. + parsedLoc := getCompilerTestModules()["mod1"].Rules[0].Body[0].Terms.(*Term).Value.(Ref)[0].Location + compiledLoc := c.Modules["mod1"].Rules[0].Body[0].Terms.(*Term).Value.(Ref)[0].Location + if parsedLoc.Row != compiledLoc.Row { + t.Fatalf("Expected parsed location (%v) and compiled location (%v) to be equal", parsedLoc.Row, compiledLoc.Row) + } + + // Indirect references. + mod12 := c.Modules["indirectrefs"] + assertExprEqual(t, mod12.Rules[1].Body[0], MustParseExpr("data.indirectrefs.f(1)[0]")) + + // Comprehensions + mod13 := c.Modules["comprehensions"] + assertExprEqual(t, mod13.Rules[3].Body[0].Terms.(*Term).Value.(Ref)[3].Value.(*ArrayComprehension).Body[0], MustParseExpr("x = data.comprehensions.nums[_]")) + assertExprEqual(t, mod13.Rules[4].Head.Value.Value.(*ArrayComprehension).Body[0], MustParseExpr("y = data.comprehensions.f(1)[0]")) + + // Ignore vars assigned via `some x in xs`. + mod14 := c.Modules["someinassign"] + someInAssignCall := mod14.Rules[2].Body[0].Terms.(*SomeDecl).Symbols[0].Value.(Call) + assertTermEqual(t, someInAssignCall[1], VarTerm("x")) + collectionLastElem := someInAssignCall[2].Value.(*Array).Get(IntNumberTerm(2)) + assertTermEqual(t, collectionLastElem, MustParseTerm("data.someinassign.y")) + + // Ignore key and val vars assigned via `some k, v in xs`. + mod15 := c.Modules["someinassignwithkey"] + someInAssignCall = mod15.Rules[2].Body[0].Terms.(*SomeDecl).Symbols[0].Value.(Call) + assertTermEqual(t, someInAssignCall[1], VarTerm("k")) + assertTermEqual(t, someInAssignCall[2], VarTerm("v")) + collectionLastElem = someInAssignCall[3].Value.(*Array).Get(IntNumberTerm(2)) + assertTermEqual(t, collectionLastElem, MustParseTerm("data.someinassignwithkey.y")) + + mod16 := c.Modules["everykw"] + everyExpr := mod16.Rules[len(mod16.Rules)-1].Body[0].Terms.(*Every) + assertTermEqual(t, everyExpr.Body[0].Terms.(*Term), MustParseTerm("data.everykw.nums[x]")) + assertTermEqual(t, everyExpr.Domain, MustParseTerm("data.everykw.xs")) + + // 'x' is not resolved + assertTermEqual(t, everyExpr.Value, VarTerm("x")) + gt10 := MustParseExpr("x > 10") + gt10.Index++ // TODO(sr): why? + assertExprEqual(t, everyExpr.Body[1], gt10) + + // head refs are kept as-is, but their bodies are replaced. + mod := c.Modules["heads_with_dots"] + rule := mod.Rules[1] + body := rule.Body[0].Terms.(*Term) + assertTermEqual(t, body, MustParseTerm("data.heads_with_dots.this_is_not")) + if act, exp := rule.Head.Ref(), MustParseRef("this.is.dotted"); act.Compare(exp) != 0 { + t.Errorf("expected %v to match %v", act, exp) + } +} + +func TestCompilerResolveErrors(t *testing.T) { + + c := NewCompiler() + c.Modules = map[string]*Module{ + "shadow-globals": MustParseModule(` + package shadow_globals + import rego.v1 + + f([input]) if { true } + `), + } + + compileStages(c, c.resolveAllRefs) + + expected := []string{ + `args must not shadow input`, + } + + assertCompilerErrorStrings(t, c, expected) +} + +func TestCompilerRewriteTermsInHead(t *testing.T) { + popts := ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true} + + tests := []struct { + note string + mod *Module + exp *Rule + }{ + { + note: "imports", + mod: module(`package head +import data.doc1 as bar +import data.doc2 as corge +import input.x.y.foo +import input.qux as baz + +p[foo[bar[i]]] = {"baz": baz, "corge": corge} if { true } +`), + exp: MustParseRule(`p[__local0__] = __local1__ { true; __local0__ = input.x.y.foo[data.doc1[i]]; __local1__ = {"baz": input.qux, "corge": data.doc2} }`), + }, + { + note: "array comprehension value", + mod: module(`package head +q = [true | true] if { true } +`), + exp: MustParseRule(`q = __local0__ { true; __local0__ = [true | true] }`), + }, + { + note: "array comprehension value in else head", + mod: module(`package head +q if { + false +} else = [true | true] if { + true +} +`), + exp: MustParseRule(`q = true { false } else = __local0__ { true; __local0__ = [true | true] }`), + }, + { + note: "array comprehension value in head (comprehension-local var)", + mod: module(`package head +q = [a | a := true] if { + false +} else = [a | a := true] if { + true +} +`), + exp: MustParseRule(`q = __local2__ { false; __local2__ = [__local0__ | __local0__ = true] } else = __local3__ { true; __local3__ = [__local1__ | __local1__ = true] }`), + }, + { + note: "array comprehension value in function head (comprehension-local var)", + mod: module(`package head +f(x) = [a | a := true] if { + false +} else = [a | a := true] if { + true +} +`), + exp: MustParseRule(`f(__local0__) = __local3__ { false; __local3__ = [__local1__ | __local1__ = true] } else = __local4__ { true; __local4__ = [__local2__ | __local2__ = true] }`), + }, + { + note: "array comprehension value in else-func head (reused arg rewrite)", + mod: module(`package head +f(x, y) = [x | y] if { + false +} else = [x | y] if { + true +} +`), + exp: MustParseRule(`f(__local0__, __local1__) = __local2__ { false; __local2__ = [__local0__ | __local1__] } else = __local3__ { true; __local3__ = [__local0__ | __local1__] }`), + }, + { + note: "object comprehension value", + mod: module(`package head +r = {"true": true | true} if { true } +`), + exp: MustParseRule(`r = __local0__ { true; __local0__ = {"true": true | true} }`), + }, + { + note: "object comprehension value in else head", + mod: module(`package head +q if { + false +} else = {"true": true | true} if { + true +} +`), + exp: MustParseRule(`q = true { false } else = __local0__ { true; __local0__ = {"true": true | true} }`), + }, + { + note: "object comprehension value in head (comprehension-local var)", + mod: module(`package head +q = {"a": a | a := true} if { + false +} else = {"a": a | a := true} if { + true +} +`), + exp: MustParseRule(`q = __local2__ { false; __local2__ = {"a": __local0__ | __local0__ = true} } else = __local3__ { true; __local3__ = {"a": __local1__ | __local1__ = true} }`), + }, + { + note: "object comprehension value in function head (comprehension-local var)", + mod: module(`package head +f(x) = {"a": a | a := true} if { + false +} else = {"a": a | a := true} if { + true +} +`), + exp: MustParseRule(`f(__local0__) = __local3__ { false; __local3__ = {"a": __local1__ | __local1__ = true} } else = __local4__ { true; __local4__ = {"a": __local2__ | __local2__ = true} }`), + }, + { + note: "object comprehension value in else-func head (reused arg rewrite)", + mod: module(`package head +f(x, y) = {x: y | true} if { + false +} else = {x: y | true} if { + true +} +`), + exp: MustParseRule(`f(__local0__, __local1__) = __local2__ { false; __local2__ = {__local0__: __local1__ | true} } else = __local3__ { true; __local3__ = {__local0__: __local1__ | true} }`), + }, + { + note: "set comprehension value", + mod: module(`package head +s = {true | true} if { true } +`), + exp: MustParseRule(`s = __local0__ { true; __local0__ = {true | true} }`), + }, + { + note: "set comprehension value in else head", + mod: module(`package head +q = {false | false} if { + false +} else = {true | true} if { + true +} +`), + exp: MustParseRule(`q = __local0__ { false; __local0__ = {false | false} } else = __local1__ { true; __local1__ = {true | true} }`), + }, + { + note: "set comprehension value in head (comprehension-local var)", + mod: module(`package head +q = {a | a := true} if { + false +} else = {a | a := true} if { + true +} +`), + exp: MustParseRule(`q = __local2__ { false; __local2__ = {__local0__ | __local0__ = true} } else = __local3__ { true; __local3__ = {__local1__ | __local1__ = true} }`), + }, + { + note: "set comprehension value in function head (comprehension-local var)", + mod: module(`package head +f(x) = {a | a := true} if { + false +} else = {a | a := true} if { + true +} +`), + exp: MustParseRule(`f(__local0__) = __local3__ { false; __local3__ = {__local1__ | __local1__ = true} } else = __local4__ { true; __local4__ = {__local2__ | __local2__ = true} }`), + }, + { + note: "set comprehension value in else-func head (reused arg rewrite)", + mod: module(`package head +f(x, y) = {x | y} if { + false +} else = {x | y} if { + true +} +`), + exp: MustParseRule(`f(__local0__, __local1__) = __local2__ { false; __local2__ = {__local0__ | __local1__} } else = __local3__ { true; __local3__ = {__local0__ | __local1__} }`), + }, + { + note: "import in else value", + mod: module(`package head +import input.qux as baz +elsekw if { + false +} else = baz if { + true +} +`), + exp: MustParseRule(`elsekw { false } else = __local0__ { true; __local0__ = input.qux }`), + }, + { + note: "import ref in last ref head term", + mod: module(`package head +import data.doc1 as bar +x.y.z[bar[i]] = true +`), + exp: MustParseRule(`x.y.z[__local0__] = true { true; __local0__ = data.doc1[i] }`), + }, + { + note: "import ref in multi-value ref rule", + mod: module(`package head +import data.doc1 as bar +x.y.w contains bar[i] if true +`), + exp: func() *Rule { + exp, _ := ParseRuleWithOpts(`x.y.w contains __local0__ if {true; __local0__ = data.doc1[i] }`, popts) + return exp + }(), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler() + c.Modules["head"] = tc.mod + compileStages(c, c.rewriteRefsInHead) + assertNotFailed(t, c) + act := c.Modules["head"].Rules[0] + assertRulesEqual(t, act, tc.exp) + }) + } +} + +func TestCompilerRefHeadsNeedCapability(t *testing.T) { + popts := ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true} + for _, tc := range []struct { + note string + mod *Module + err string + }{ + { + note: "one-dot ref, single-value rule, short+compat", + mod: MustParseModule(`package t +p[1] = 2`), + }, + { + note: "function, short", + mod: MustParseModule(`package t +p(1)`), + }, + { + note: "function", + mod: MustParseModuleWithOpts(`package t +p(1) if true`, popts), + }, + { + note: "function with value", + mod: MustParseModuleWithOpts(`package t +p(1) = 2 if true`, popts), + }, + { + note: "function with value", + mod: MustParseModule(`package t +p(1) = 2`), + }, + { + note: "one-dot ref, single-value rule, compat", + mod: MustParseModuleWithOpts(`package t +p[3] = 4 if true`, popts), + }, + { + note: "multi-value non-ref head", + mod: MustParseModuleWithOpts(`package t +p contains 1 if true`, popts), + }, + { // NOTE(sr): this was previously forbidden because we need the `if` for disambiguation + note: "one-dot ref head", + mod: MustParseModuleWithOpts(`package t +p[1] if true`, popts), + err: "rule heads with refs are not supported: p[1]", + }, + { + note: "single-value ref rule", + mod: MustParseModuleWithOpts(`package t +a.b.c[x] if x := input`, popts), + err: "rule heads with refs are not supported: a.b.c[x]", + }, + { + note: "ref head function", + mod: MustParseModuleWithOpts(`package t +a.b.c(x) if x == input`, popts), + err: "rule heads with refs are not supported: a.b.c", + }, + { + note: "multi-value ref rule", + mod: MustParseModuleWithOpts(`package t +a.b.c contains x if x := input`, popts), + err: "rule heads with refs are not supported: a.b.c", + }, + } { + t.Run(tc.note, func(t *testing.T) { + caps, err := LoadCapabilitiesVersion("v0.44.0") + if err != nil { + t.Fatal(err) + } + c := NewCompiler().WithCapabilities(caps) + c.Modules["test"] = tc.mod + compileStages(c, c.rewriteRefsInHead) + if tc.err != "" { + assertErrorWithMessage(t, c.Errors, tc.err) + } else { + assertNotFailed(t, c) + } + }) + } +} + +func TestCompilerRewriteRegoMetadataCalls(t *testing.T) { + tests := []struct { + note string + module string + exp string + }{ + { + note: "rego.metadata called, no metadata", + module: `package test + +p if { + rego.metadata.chain()[0].path == ["test", "p"] + rego.metadata.rule() == {} +}`, + exp: `package test + +p = true if { + __local2__ = [{"path": ["test", "p"]}] + __local3__ = {} + __local0__ = __local2__ + equal(__local0__[0].path, ["test", "p"]) + __local1__ = __local3__ + equal(__local1__, {}) +}`, + }, + { + note: "rego.metadata called, no output var, no metadata", + module: `package test + +p if { + rego.metadata.chain() + rego.metadata.rule() +}`, + exp: `package test + +p = true if { + __local0__ = [{"path": ["test", "p"]}] + __local1__ = {} + __local0__ + __local1__ +}`, + }, + { + note: "rego.metadata called, with metadata", + module: `# METADATA +# description: A test package +package test + +# METADATA +# title: My P Rule +p if { + rego.metadata.chain()[0].title == "My P Rule" + rego.metadata.chain()[1].description == "A test package" +} + +# METADATA +# title: My Other P Rule +p if { + rego.metadata.rule().title == "My Other P Rule" +}`, + exp: `# METADATA +# {"scope":"package","description":"A test package"} +package test + +# METADATA +# {"scope":"rule","title":"My P Rule"} +p = true if { + __local3__ = [ + {"annotations": {"scope": "rule", "title": "My P Rule"}, "path": ["test", "p"]}, + {"annotations": {"description": "A test package", "scope": "package"}, "path": ["test"]} + ] + __local0__ = __local3__ + equal(__local0__[0].title, "My P Rule") + __local1__ = __local3__ + equal(__local1__[1].description, "A test package") +} + +# METADATA +# {"scope":"rule","title":"My Other P Rule"} +p = true if { + __local4__ = {"scope": "rule", "title": "My Other P Rule"} + __local2__ = __local4__ + equal(__local2__.title, "My Other P Rule") +}`, + }, + { + note: "rego.metadata referenced multiple times", + module: `# METADATA +# description: TEST +package test + +p if { + rego.metadata.chain()[0].path == ["test", "p"] + rego.metadata.chain()[1].path == ["test"] +}`, + exp: `# METADATA +# {"scope":"package","description":"TEST"} +package test + +p = true if { + __local2__ = [ + {"path": ["test", "p"]}, + {"annotations": {"description": "TEST", "scope": "package"}, "path": ["test"]} + ] + __local0__ = __local2__ + equal(__local0__[0].path, ["test", "p"]) + __local1__ = __local2__ + equal(__local1__[1].path, ["test"]) }`, + }, + { + note: "rego.metadata return value", + module: `package test + +p := rego.metadata.chain()`, + exp: `package test + +p := __local0__ if { + __local1__ = [{"path": ["test", "p"]}] + true + __local0__ = __local1__ +}`, + }, + { + note: "rego.metadata argument in function call", + module: `package test + +p if { + q(rego.metadata.chain()) +} + +q(s) if { + s == ["test", "p"] +}`, + exp: `package test + +p = true if { + __local2__ = [{"path": ["test", "p"]}] + __local1__ = __local2__ + data.test.q(__local1__) +} + +q(__local0__) = true if { + equal(__local0__, ["test", "p"]) +}`, + }, + { + note: "rego.metadata used in array comprehension", + module: `package test + +p = [x | x := rego.metadata.chain()]`, + exp: `package test + +p = [__local0__ | __local1__ = __local2__; __local0__ = __local1__] if { + __local2__ = [{"path": ["test", "p"]}] + true +}`, + }, + { + note: "rego.metadata used in nested array comprehension", + module: `package test + +p if { + y := [x | x := rego.metadata.chain()] + y[0].path == ["test", "p"] +}`, + exp: `package test + +p = true if { + __local3__ = [{"path": ["test", "p"]}]; + __local1__ = [__local0__ | __local2__ = __local3__; __local0__ = __local2__]; + equal(__local1__[0].path, ["test", "p"]) +}`, + }, + { + note: "rego.metadata used in set comprehension", + module: `package test + +p = {x | x := rego.metadata.chain()}`, + exp: `package test + +p = {__local0__ | __local1__ = __local2__; __local0__ = __local1__} if { + __local2__ = [{"path": ["test", "p"]}] + true +}`, + }, + { + note: "rego.metadata used in nested set comprehension", + module: `package test + +p if { + y := {x | x := rego.metadata.chain()} + y[0].path == ["test", "p"] +}`, + exp: `package test + +p = true if { + __local3__ = [{"path": ["test", "p"]}] + __local1__ = {__local0__ | __local2__ = __local3__; __local0__ = __local2__} + equal(__local1__[0].path, ["test", "p"]) +}`, + }, + { + note: "rego.metadata used in object comprehension", + module: `package test + +p = {i: x | x := rego.metadata.chain()[i]}`, + exp: `package test + +p = {i: __local0__ | __local1__ = __local2__; __local0__ = __local1__[i]} if { + __local2__ = [{"path": ["test", "p"]}] + true +}`, + }, + { + note: "rego.metadata used in nested object comprehension", + module: `package test + +p if { + y := {i: x | x := rego.metadata.chain()[i]} + y[0].path == ["test", "p"] +}`, + exp: `package test + +p = true if { + __local3__ = [{"path": ["test", "p"]}] + __local1__ = {i: __local0__ | __local2__ = __local3__; __local0__ = __local2__[i]} + equal(__local1__[0].path, ["test", "p"]) +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler() + c.Modules = map[string]*Module{ + "test.rego": module(tc.module), + } + compileStages(c, c.rewriteRegoMetadataCalls) + assertNotFailed(t, c) + + result := c.Modules["test.rego"] + exp := MustParseModuleWithOpts(tc.exp, ParserOptions{ + AllFutureKeywords: true, + unreleasedKeywords: true, + ProcessAnnotation: true, + }) + + if result.Compare(exp) != 0 { + t.Fatalf("\nExpected:\n\n%v\n\nGot:\n\n%v", exp, result) + } + }) + } +} + +func TestCompilerOverridingSelfCalls(t *testing.T) { + c := NewCompiler() + c.Modules = map[string]*Module{ + "self.rego": MustParseModule(`package self.metadata + +chain(x) = "foo" +rule := "bar"`), + "test.rego": MustParseModule(`package test +import data.self + +p := self.metadata.chain(42) +q := self.metadata.rule`), + } + + compileStages(c, nil) + assertNotFailed(t, c) +} + +func TestCompilerRewriteLocalAssignments(t *testing.T) { + + tests := []struct { + module string + exp any + expRewrittenMap map[Var]Var + regoVersion RegoVersion + }{ + { + module: ` + package test + body if { a := 1; a > 0 } + `, + exp: ` + package test + body = true if { __local0__ = 1; gt(__local0__, 0) } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("a"), + }, + }, + { + module: ` + package test + head_vars(a) = b if { b := a } + `, + exp: ` + package test + head_vars(__local0__) = __local1__ if { __local1__ = __local0__ } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("a"), + Var("__local1__"): Var("b"), + }, + }, + { + module: ` + package test + head_key contains a if { a := 1 } + `, + exp: ` + package test + head_key contains __local0__ if { __local0__ = 1 } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("a"), + }, + }, + { + module: ` + package test + head_unsafe_var contains a if { some a } + `, + exp: ` + package test + head_unsafe_var contains __local0__ if { true } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("a"), + }, + }, + { + module: ` + package test + p = {1,2,3} + x = 4 + head_nested contains p[x] if { + some x + }`, + exp: ` + package test + p = {1,2,3} + x = 4 + head_nested contains data.test.p[__local0__] + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("x"), + }, + }, + { + module: ` + package test + p = {1,2} + head_closure_nested contains p[x] if { + y = [true | some x; x = 1] + } + `, + exp: ` + package test + p = {1,2} + head_closure_nested contains data.test.p[x] if { + y = [true | __local0__ = 1] + } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("x"), + }, + }, + { + module: ` + package test + nested if { + a := [1,2,3] + x := [true | a[i] > 1] + } + `, + exp: ` + package test + nested = true if { __local0__ = [1, 2, 3]; __local1__ = [true | gt(__local0__[i], 1)] } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("a"), + Var("__local1__"): Var("x"), + }, + }, + { + module: ` + package test + x = 2 + shadow_globals contains x if { x := 1 } + `, + exp: ` + package test + x = 2 if { true } + shadow_globals contains __local0__ if { __local0__ = 1 } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("x"), + }, + }, + { + module: ` + package test + shadow_rule contains shadow_rule if { shadow_rule := 1 } + `, + exp: ` + package test + shadow_rule contains __local0__ if { __local0__ = 1 } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("shadow_rule"), + }, + }, + { + module: ` + package test + shadow_roots_1 { data := 1; input := 2; input > data } + `, + exp: ` + package test + shadow_roots_1 = true { __local0__ = 1; __local1__ = 2; gt(__local1__, __local0__) } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("data"), + Var("__local1__"): Var("input"), + }, + regoVersion: RegoV0, // shadowing only allowed in v0 + }, + { + module: ` + package test + shadow_roots_2 { input := {"a": 1}; input.a > 0 } + `, + exp: ` + package test + shadow_roots_2 = true { __local0__ = {"a": 1}; gt(__local0__.a, 0) } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("input"), + }, + regoVersion: RegoV0, // shadowing only allowed in v0 + }, + { + module: ` + package test + skip_with_target { + a := 1 + input := 2 + data.p with input as a + data.p with input.foo as a + } + `, + exp: ` + package test + skip_with_target = true { __local0__ = 1; __local1__ = 2; data.p with input as __local0__; data.p with input.foo as __local0__ } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("a"), + Var("__local1__"): Var("input"), + }, + regoVersion: RegoV0, // shadowing only allowed in v0 + }, + { + module: ` + package test + shadow_comprehensions if { + a := 1 + [true | a := 2; b := 1] + b := 2 + } + `, + exp: ` + package test + shadow_comprehensions = true if { __local0__ = 1; [true | __local1__ = 2; __local2__ = 1]; __local3__ = 2 } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("a"), + Var("__local1__"): Var("a"), + Var("__local2__"): Var("b"), + Var("__local3__"): Var("b"), + }, + }, + { + module: ` + package test + scoping if { + [true | a := 1] + [true | a := 2] + } + `, + exp: ` + package test + scoping = true if { [true | __local0__ = 1]; [true | __local1__ = 2] } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("a"), + Var("__local1__"): Var("a"), + }, + }, + { + module: ` + package test + object_keys if { + {k: v1, "k2": v2} := {"foo": 1, "k2": 2} + } + `, + exp: ` + package test + object_keys = true if { {"k2": __local0__, k: __local1__} = {"foo": 1, "k2": 2} } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("v2"), + Var("__local1__"): Var("v1"), + }, + }, + { + module: ` + package test + head_array_comprehensions = [[x] | x := 1] + head_set_comprehensions = {[x] | x := 1} + head_object_comprehensions = {k: [x] | k := "foo"; x := 1} + `, + exp: ` + package test + head_array_comprehensions = [[__local0__] | __local0__ = 1] if { true } + head_set_comprehensions = {[__local1__] | __local1__ = 1} if { true } + head_object_comprehensions = {__local2__: [__local3__] | __local2__ = "foo"; __local3__ = 1} if { true } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("x"), + Var("__local1__"): Var("x"), + Var("__local2__"): Var("k"), + Var("__local3__"): Var("x"), + }, + }, + { + module: ` + package test + rewritten_object_key if { + k := "foo" + {k: 1} + } + `, + exp: ` + package test + rewritten_object_key = true if { __local0__ = "foo"; {__local0__: 1} } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("k"), + }, + }, + { + module: ` + package test + rewritten_object_key_head contains [{k: 1}] if { + k := "foo" + } + `, + exp: ` + package test + rewritten_object_key_head contains [{__local0__: 1}] if { __local0__ = "foo" } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("k"), + }, + }, + { + module: ` + package test + rewritten_object_key_head_value = [{k: 1}] if { + k := "foo" + } + `, + exp: ` + package test + rewritten_object_key_head_value = [{__local0__: 1}] if { __local0__ = "foo" } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("k"), + }, + }, + { + module: ` + package test + skip_with_target_in_assignment { + input := 1 + a := [true | true with input as 2; true with input.foo as 3] + } + `, + exp: ` + package test + skip_with_target_in_assignment = true { __local0__ = 1; __local1__ = [true | true with input as 2; true with input.foo as 3] } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("input"), + Var("__local1__"): Var("a"), + }, + regoVersion: RegoV0, // shadowing only allowed in v0 + }, + { + module: ` + package test + rewrite_with_value_in_assignment if { + a := 1 + b := 1 with input as [a] + } + `, + exp: ` + package test + rewrite_with_value_in_assignment = true if { __local0__ = 1; __local1__ = 1 with input as [__local0__] } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("a"), + Var("__local1__"): Var("b"), + }, + }, + { + module: ` + package test + rewrite_with_value_in_expr if { + a := 1 + a > 0 with input as [a] + } + `, + exp: ` + package test + rewrite_with_value_in_expr = true if { __local0__ = 1; gt(__local0__, 0) with input as [__local0__] } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("a"), + }, + }, + { + module: ` + package test + rewrite_nested_with_value_in_expr if { + a := 1 + a > 0 with input as object.union({"a": a}, {"max_a": max([a])}) + } + `, + exp: ` + package test + rewrite_nested_with_value_in_expr = true if { __local0__ = 1; gt(__local0__, 0) with input as object.union({"a": __local0__}, {"max_a": max([__local0__])}) } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("a"), + }, + }, + { + module: ` + package test + global = {} + ref_shadowed if { + global := {"a": 1} + global.a > 0 + } + `, + exp: ` + package test + global = {} if { true } + ref_shadowed = true if { __local0__ = {"a": 1}; gt(__local0__.a, 0) } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("global"), + }, + }, + { + module: ` + package test + f(x) = y if { + x == 1 + y := 2 + } else = y if { + x == 3 + y := 4 + } + `, + // Each "else" rule has a separate rule head and the vars in the + // args will be rewritten. Since we cannot currently redefine the + // args, we must parse the module and then manually update the args. + exp: func() *Module { + module := module(` + package test + + f(__local0__) = __local1__ if { __local0__ == 1; __local1__ = 2 } else = __local2__ if { __local0__ == 3; __local2__ = 4 } + `) + module.Rules[0].Else.Head.Args[0].Value = Var("__local0__") + return module + }, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("x"), + Var("__local1__"): Var("y"), + Var("__local2__"): Var("y"), + }, + }, + { + module: ` + package test + f({"x": [x]}) = y if { x == 1; y := 2 }`, + exp: ` + package test + + f({"x": [__local0__]}) = __local1__ if { __local0__ == 1; __local1__ = 2 }`, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("x"), + Var("__local1__"): Var("y"), + }, + }, + { + module: ` + package test + + f(x, [x]) = x if { x == 1 } + `, + exp: ` + package test + + f(__local0__, [__local0__]) = __local0__ if { __local0__ == 1 } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("x"), + }, + }, + { + module: ` + package test + + f(x) = {x[0]: 1} if { true } + `, + exp: ` + package test + + f(__local0__) = {__local0__[0]: 1} if { true } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("x"), + }, + }, + { + module: ` + package test + + f({{t | t := 0}: 1}) if { + true + } + `, + exp: ` + package test + + f({{__local0__ | __local0__ = 0}: 1}) if { true } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("t"), + }, + }, + { + module: ` + package test + + f({{t | t := 0}}) if { + true + } + `, + exp: ` + package test + + f({{__local0__ | __local0__ = 0}}) if { true } + `, + expRewrittenMap: map[Var]Var{ + Var("__local0__"): Var("t"), + }, + }, + } + + for i, tc := range tests { + t.Run(strconv.Itoa(i), func(t *testing.T) { + setRegoVersion := func(po ParserOptions) ParserOptions { + po.RegoVersion = tc.regoVersion + return po + } + + c := NewCompiler() + c.Modules = map[string]*Module{ + "test.rego": module(tc.module, setRegoVersion), + } + compileStages(c, c.rewriteLocalVars) + assertNotFailed(t, c) + result := c.Modules["test.rego"] + var exp *Module + switch e := tc.exp.(type) { + case string: + exp = module(e, setRegoVersion) + case func() *Module: + exp = e() + default: + panic("expected value must be string or func() *Module") + } + if result.Compare(exp) != 0 { + t.Fatalf("\nExpected:\n\n%v\n\nGot:\n\n%v", exp, result) + } + if !maps.Equal(c.RewrittenVars, tc.expRewrittenMap) { + t.Fatalf("\nExpected Rewritten Vars:\n\n\t%+v\n\nGot:\n\n\t%+v\n\n", tc.expRewrittenMap, c.RewrittenVars) + } + }) + } + +} + +func TestRewriteLocalVarDeclarationErrors(t *testing.T) { + + c := NewCompiler() + + c.Modules["test"] = module(`package test + + redeclaration if { + r1 = 1 + r1 := 2 + r2 := 1 + [b, r2] := [1, 2] + foo.path == 1 + foo := "foo" + _ := [1 | nested := 1; nested := 2] + } + + negation if { + not a := 1 + } + + bad_assign if { + null := x + true := x + 4.5 := x + "foo" := x + [true | true] := [] + {true | true} := set() + {"foo": true | true} := {} + x + 1 := 2 + data.foo := 1 + [z, 1] := [1, 2] + } + + arg_redeclared(arg1) if { + arg1 := 1 + } + + arg_nested_redeclared({{arg_nested| arg_nested := 1; arg_nested := 2}}) if { true } + `) + + compileStages(c, c.rewriteLocalVars) + + expectedErrors := []string{ + "var r1 referenced above", + "var r2 assigned above", + "var foo referenced above", + "var nested assigned above", + "arg arg1 redeclared", + "var arg_nested assigned above", + "cannot assign vars inside negated expression", + "cannot assign to ref", + "cannot assign to arraycomprehension", + "cannot assign to setcomprehension", + "cannot assign to objectcomprehension", + "cannot assign to call", + "cannot assign to number", + "cannot assign to number", + "cannot assign to boolean", + "cannot assign to string", + "cannot assign to null", + } + + sort.Strings(expectedErrors) + + result := []string{} + + for i := range c.Errors { + result = append(result, c.Errors[i].Message) + } + + sort.Strings(result) + + if len(expectedErrors) != len(result) { + t.Fatalf("Expected %d errors but got %d:\n\n%v\n\nGot:\n\n%v", len(expectedErrors), len(result), strings.Join(expectedErrors, "\n"), strings.Join(result, "\n")) + } + + for i := range result { + if result[i] != expectedErrors[i] { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", strings.Join(expectedErrors, "\n"), strings.Join(result, "\n")) + } + } +} + +func TestRewriteDeclaredVarsStage(t *testing.T) { + + // Unlike the following test case, this only executes up to the + // RewriteLocalVars stage. This is done so that later stages like + // RewriteDynamics are not executed. + + tests := []struct { + note string + module string + exp string + }{ + { + note: "object ref key", + module: ` + package test + + p if { + a := {"a": "a"} + {a.a: a.a} + } + `, + exp: ` + package test + + p if { + __local0__ = {"a": "a"} + {__local0__.a: __local0__.a} + } + `, + }, + { + note: "set ref element", + module: ` + package test + + p if { + a := {"a": "a"} + {a.a} + } + `, + exp: ` + package test + + p if { + __local0__ = {"a": "a"} + {__local0__.a} + } + `, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + + c := NewCompiler() + + c.Modules = map[string]*Module{ + "test.rego": module(tc.module), + } + + compileStages(c, c.rewriteLocalVars) + + exp := module(tc.exp) + result := c.Modules["test.rego"] + + if !exp.Equal(result) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, result) + } + }) + } +} + +func TestRewriteDeclaredVars(t *testing.T) { + tests := []struct { + note string + module string + exp string + wantErr error + }{ + { + note: "rewrite unify", + module: ` + package test + x = 1 + y = 2 + p if { some x; input = [x, y] } + `, + exp: ` + package test + x = 1 + y = 2 + p if { __local1__ = data.test.y; input = [__local0__, __local1__] } + `, + }, + { + note: "rewrite call", + module: ` + package test + x = [] + y = {} + p if { some x; walk(y, [x, y]) } + `, + exp: ` + package test + x = [] + y = {} + p if { __local1__ = data.test.y; __local2__ = data.test.y; walk(__local1__, [__local0__, __local2__]) } + `, + }, + { + note: "rewrite term", + module: ` + package test + x = "a" + y = 1 + q contains [2, "b"] + p if { some x; q[[y,x]] } + `, + exp: ` + package test + x = "a" + y = 1 + q contains [2, "b"] + p if { __local1__ = data.test.y; data.test.q[[__local1__, __local0__]] } + `, + }, + { + note: "with: rewrite target", + module: ` + package test + p if { + x := "foo" + true with input[x] as 1 + } + `, + exp: ` + package test + p if { + __local0__ = "foo"; + true with input[__local0__] as 1 + } + `, + }, + { + note: "with: rewrite target in comprehension term", + module: ` + package test + p if { + foo := "bar" + { { 2 | true with input[foo] as 1} | true } + } + `, + exp: ` + package test + p if { + __local0__ = "bar" + {__local1__ | true; __local1__ = { 2 | true with input[__local0__] as 1 }} + } + `, + }, + { + note: "single-value rule with ref head", + module: ` + package test + + p.r.q[s] = t if { + t := 1 + s := input.foo + } + `, + exp: ` + package test + + p.r.q[__local1__] = __local0__ if { + __local0__ = 1 + __local1__ = input.foo + } + `, + }, + { + note: "rewrite some x in xs", + module: ` + package test + import future.keywords.in + xs = ["a", "b", "c"] + p if { some x in xs; x == "a" } + `, + exp: ` + package test + xs = ["a", "b", "c"] + p if { __local2__ = data.test.xs[__local1__]; __local2__ = "a" } + `, + }, + { + note: "rewrite some k, x in xs", + module: ` + package test + import future.keywords.in + xs = ["a", "b", "c"] + p if { some k, x in xs; x == "a"; k == 2 } + `, + exp: ` + package test + xs = ["a", "b", "c"] + p if { __local1__ = data.test.xs[__local0__]; __local1__ = "a"; __local0__ = 2 } + `, + }, + { + note: "rewrite some k, x in xs[i]", + module: ` + package test + import future.keywords.in + xs = [["a", "b", "c"], []] + p if { + some i + some k, x in xs[i] + x == "a" + k == 2 + } + `, + exp: ` + package test + xs = [["a", "b", "c"], []] + p = true if { __local2__ = data.test.xs[__local0__][__local1__]; __local2__ = "a"; __local1__ = 2 } + `, + }, + { + note: "rewrite some k, x in xs[i] with `i` as ref", + module: ` + package test + import future.keywords.in + i = 0 + xs = [["a", "b", "c"], []] + p if { + some k, x in xs[i] + x == "a" + k == 2 + } + `, + exp: ` + package test + i = 0 + xs = [["a", "b", "c"], []] + p = true if { __local2__ = data.test.i; __local1__ = data.test.xs[__local2__][__local0__]; __local1__ = "a"; __local0__ = 2 } + `, + }, + { + note: "rewrite some: with modifier on domain", + module: ` + package test + p if { + some k, x in input with input as [1, 1, 1] + k == 0 + x == 1 + } + `, + exp: ` + package test + p if { + __local1__ = input[__local0__] with input as [1, 1, 1] + __local0__ = 0 + __local1__ = 1 + } + `, + }, + { + note: "rewrite every", + module: ` + package test + # import future.keywords.in + # import future.keywords.every + i = 0 + xs = [1, 2] + k = "foo" + v = "bar" + p if { + every k, v in xs { k + v > i } + } + `, + exp: ` + package test + i = 0 + xs = [1, 2] + k = "foo" + v = "bar" + p = true if { + __local2__ = data.test.xs + every __local0__, __local1__ in __local2__ { + plus(__local0__, __local1__, __local3__) + __local4__ = data.test.i + gt(__local3__, __local4__) + } + } `, + }, + { + note: "rewrite every: unused key var", + module: ` + package test + # import future.keywords.in + # import future.keywords.every + p if { + every k, v in [1] { v >= 1 } + } + `, + wantErr: errors.New("declared var k unused"), + }, + { + // NOTE(sr): this would happen when compiling modules twice: + // the first run rewrites every to include a generated key var, + // the second one bails because it's not used. + // Seen in the wild when using `opa test -b` on a bundle that + // used `every`, https://github.com/open-policy-agent/opa/issues/4420 + note: "rewrite every: unused generated key var", + module: ` + package test + + p if { + every __local0__, v in [1] { v >= 1 } + } + `, + exp: ` + package test + p = true if { + __local3__ = [1] + every __local1__, __local2__ in __local3__ { __local2__ >= 1 } + } + `, + }, + { + note: "rewrite every: unused value var", + module: ` + package test + # import future.keywords.in + # import future.keywords.every + p if { + every v in [1] { true } + } + `, + wantErr: errors.New("declared var v unused"), + }, + { + note: "rewrite every: wildcard value var, used key", + module: ` + package test + # import future.keywords.in + # import future.keywords.every + p if { + every k, _ in [1] { k >= 0 } + } + `, + exp: ` + package test + p = true if { + __local1__ = [1] + every __local0__, _ in __local1__ { gte(__local0__, 0) } + } + `, + }, + { + note: "rewrite every: wildcard key+value var", // NOTE(sr): may be silly, but valid + module: ` + package test + # import future.keywords.in + # import future.keywords.every + p if { + every _, _ in [1] { true } + } + `, + exp: ` + package test + p = true if { __local0__ = [1]; every _, _ in __local0__ { true } } + `, + }, + { + note: "rewrite every: declared vars with different scopes", + module: ` + package test + # import future.keywords.in + # import future.keywords.every + p if { + some x + x = 10 + every x in [1] { x == 1 } + } + `, + exp: ` + package test + p = true if { + __local0__ = 10 + __local3__ = [1] + every __local1__, __local2__ in __local3__ { __local2__ = 1 } + } + `, + }, + { + note: "rewrite every: declared vars used in body", + module: ` + package test + # import future.keywords.in + # import future.keywords.every + p if { + some y + y = 10 + every x in [1] { x == y } + } + `, + exp: ` + package test + p = true if { + __local0__ = 10 + __local3__ = [1] + every __local1__, __local2__ in __local3__ { + __local2__ = __local0__ + } + } + `, + }, + { + note: "rewrite every: pops declared var stack", + module: ` + package test + # import future.keywords.in + # import future.keywords.every + p contains x if { + some x + x = 10 + every _ in [1] { true } + } + `, + exp: ` + package test + p contains __local0__ if { __local0__ = 10; __local2__ = [1]; every __local1__, _ in __local2__ { true } } + `, + }, + { + note: "rewrite every: nested", + module: ` + package test + # import future.keywords.in + # import future.keywords.every + p if { + xs := [[1], [2]] + every v in [1] { + every w in xs[v] { + w == 2 + } + } + } + `, + exp: ` + package test + p = true if { + __local0__ = [[1], [2]] + __local5__ = [1] + every __local1__, __local2__ in __local5__ { + __local6__ = __local0__[__local2__] + every __local3__, __local4__ in __local6__ { + __local4__ = 2 + } + } + } + `, + }, + { + note: "rewrite every: with modifier on domain", + module: ` + package test + # import future.keywords.in + # import future.keywords.every + p if { + every x in input { x == 1 } with input as [1, 1, 1] + } + `, + exp: ` + package test + p if { + __local2__ = input with input as [1, 1, 1] + every __local0__, __local1__ in __local2__ { + __local1__ = 1 + } with input as [1, 1, 1] + } + `, + }, + { + note: "rewrite every: with modifier on domain with declared var", + module: ` + package test + # import future.keywords.in + # import future.keywords.every + p if { + xs := [1, 2] + every x in input { x == 1 } with input as xs + } + `, + exp: ` + package test + p if { + __local0__ = [1, 2] + __local3__ = input with input as __local0__ + every __local1__, __local2__ in __local3__ { + __local2__ = 1 + } with input as __local0__ + } + `, + }, + { + note: "rewrite every: with modifier on body", + module: ` + package test + # import future.keywords.in + # import future.keywords.every + p if { + every x in [2] { x == input } with input as 2 + } + `, + exp: ` + package test + p if { + __local2__ = [2] with input as 2 + every __local0__, __local1__ in __local2__ { + __local1__ = input + } with input as 2 + } + `, + }, + { + note: "rewrite every: with modifier on body, using every's key+value", + module: ` + package test + # import future.keywords.in + # import future.keywords.every + p if { + every x, y in input { true with data.test.q[x][y] as 100 } + } + `, + exp: ` + package test + p if { + __local2__ = input + every __local0__, __local1__ in __local2__ { + true with data.test.q[__local0__][__local1__] as 100 + } + } + `, + }, + { + note: "rewrite closures", + module: ` + package test + x = 1 + y = 2 + p if { + some x, z + z = 3 + [x | x = 2; y = 2; some z; z = 4] + } + `, + exp: ` + package test + x = 1 + y = 2 + p if { + __local1__ = 3 + [__local0__ | __local0__ = 2; data.test.y = 2; __local2__ = 4] + } + `, + }, + { + note: "rewrite head var", + module: ` + package test + x = "a" + y = 1 + z = 2 + p[x] = [y, z] if { + some x, z + x = "b" + z = 4 + }`, + exp: ` + package test + x = "a" + y = 1 + z = 2 + p[__local0__] = __local2__ if { + __local0__ = "b" + __local1__ = 4; + __local3__ = data.test.y + __local2__ = [__local3__, __local1__] + } + `, + }, + { + note: "rewrite call with root document ref as arg", + module: ` + package test + + p if { + f(input, "bar") + } + + f(x, y) if { + x[y] + } + `, + exp: ` + package test + + p = true if { + __local2__ = input; + data.test.f(__local2__, "bar") + } + + f(__local0__, __local1__) = true if { + __local0__[__local1__] + } + `, + }, + { + note: "redeclare err", + module: ` + package test + p if { + some x + some x + } + `, + wantErr: errors.New("test.rego:5: rego_compile_error: var x declared above"), + }, + { + note: "redeclare err, some/in", + module: ` + package test + p if { + some x + some i, x in [] + } + `, + wantErr: errors.New("test.rego:5: rego_compile_error: var x declared above"), + }, + { + note: "redeclare assigned err", + module: ` + package test + p if { + x := 1 + some x + } + `, + wantErr: errors.New("test.rego:5: rego_compile_error: var x assigned above"), + }, + { + note: "redeclare assigned err, some/in", + module: ` + package test + p if { + x := 1 + some i, x in [] + } + `, + wantErr: errors.New("test.rego:5: rego_compile_error: var x assigned above"), + }, + { + note: "redeclare reference err", + module: ` + package test + p if { + data.q[x] + some x + } + `, + wantErr: errors.New("test.rego:5: rego_compile_error: var x referenced above"), + }, + { + note: "redeclare reference err, some/in", + module: ` + package test + p if { + data.q[x] + some i, x in [] + } + `, + wantErr: errors.New("test.rego:5: rego_compile_error: var x referenced above"), + }, + { + note: "declare unused err", + module: ` + package test + p if { + some x + } + `, + wantErr: errors.New("declared var x unused"), + }, + { + note: "declare unsafe err", + module: ` + package test + p contains x if { + some x + x == 1 + } + `, + wantErr: errors.New("var x is unsafe"), + }, + { + note: "declare arg err", + module: ` + package test + + f([a]) if { + some a + a = 1 + } + `, + wantErr: errors.New("arg a redeclared"), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + opts := CompileOpts{ParserOptions: ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true}} + compiler, err := CompileModulesWithOpt(map[string]string{"test.rego": tc.module}, opts) + if tc.wantErr != nil { + if err == nil { + t.Fatal("Expected error but got success") + } + if !strings.Contains(err.Error(), tc.wantErr.Error()) { + t.Fatalf("Expected:\n\n%v\n\nbut got:\n\n%v", tc.wantErr, err) + } + } else if err != nil { + t.Fatal(err) + } else { + exp := MustParseModuleWithOpts(tc.exp, opts.ParserOptions) + result := compiler.Modules["test.rego"] + if exp.Compare(result) != 0 { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, result) + } + } + }) + } +} + +func TestCheckUnusedFunctionArgVars(t *testing.T) { + tests := []strictnessTestCase{ + { + note: "one of the two function args is not used - issue 5602 regression test", + module: `package test + func(x, y) if { + x = 1 + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("func(x, y)"), "", 2, 4), + Message: "unused argument y. (hint: use _ (wildcard variable) instead)", + }, + }, + }, + { + note: "one of the two ref-head function args is not used", + module: `package test + a.b.c.func(x, y) if { + x = 1 + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("a.b.c.func(x, y)"), "", 2, 4), + Message: "unused argument y. (hint: use _ (wildcard variable) instead)", + }, + }, + }, + { + note: "multiple unused argvar in scope - issue 5602 regression test", + module: `package test + func(x, y) if { + input.baz = 1 + input.test == "foo" + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("func(x, y)"), "", 2, 4), + Message: "unused argument x. (hint: use _ (wildcard variable) instead)", + }, + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("func(x, y)"), "", 2, 4), + Message: "unused argument y. (hint: use _ (wildcard variable) instead)", + }, + }, + }, + { + note: "some unused argvar in scope - issue 5602 regression test", + module: `package test + func(x, y) if { + input.test == "foo" + x = 1 + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("func(x, y)"), "", 2, 4), + Message: "unused argument y. (hint: use _ (wildcard variable) instead)", + }, + }, + }, + { + note: "wildcard argvar that's ignored - issue 5602 regression test", + module: `package test + func(x, _) if { + input.test == "foo" + x = 1 + }`, + expectedErrors: Errors{}, + }, + { + note: "wildcard argvar that's ignored - issue 5602 regression test", + module: `package test + func(x, _) if { + input.test == "foo" + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("func(x, _)"), "", 2, 4), + Message: "unused argument x. (hint: use _ (wildcard variable) instead)", + }, + }, + }, + { + note: "argvar not used in body but in head - issue 5602 regression test", + module: `package test + func(x) := x if { + input.test == "foo" + }`, + expectedErrors: Errors{}, + }, + { + note: "argvar not used in body but in head value comprehension", + module: `package test + a := {"foo": 1} + func(x) := { x: v | v := a[x] } if { + input.test == "foo" + }`, + expectedErrors: Errors{}, + }, + { + note: "argvar not used in body but in else-head value comprehension", + module: `package test + a := {"foo": 1} + func(x) if { + input.test == "foo" + } else := { x: v | v := a[x] } if { + input.test == "bar" + }`, + expectedErrors: Errors{}, + }, + { + note: "argvar not used in body and shadowed in head value comprehension", + module: `package test + a := {"foo": 1} + func(x) := { x: v | x := "foo"; v := a[x] } if { + input.test == "foo" + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("func(x) := { x: v | x := \"foo\"; v := a[x] }"), "", 3, 4), + Message: "unused argument x. (hint: use _ (wildcard variable) instead)", + }, + }, + }, + { + note: "argvar used in primary body but not in else body", + module: `package test + func(x) if { + input.test == x + } else := false if { + input.test == "foo" + }`, + expectedErrors: Errors{}, + }, + { + note: "argvar used in primary body but not in else body (with wildcard)", + module: `package test + func(x, _) if { + input.test == x + } else := false if { + input.test == "foo" + }`, + expectedErrors: Errors{}, + }, + { + note: "argvar not used in primary body but in else body", + module: `package test + func(x) if { + input.test == "foo" + } else := false if { + input.test == x + }`, + expectedErrors: Errors{}, + }, + { + note: "argvar not used in primary body but in else body (with wildcard)", + module: `package test + func(x, _) if { + input.test == "foo" + } else := false if { + input.test == x + }`, + expectedErrors: Errors{}, + }, + { + note: "argvar used in primary body but not in implicit else body", + module: `package test + func(x) if { + input.test == x + } else := false`, + expectedErrors: Errors{}, + }, + { + note: "argvars usage spread over multiple bodies", + module: `package test + func(x, y, z) if { + input.test == x + } else if { + input.test == y + } else if { + input.test == z + }`, + expectedErrors: Errors{}, + }, + { + note: "argvars usage spread over multiple bodies, missing in first", + module: `package test + func(x, y, z) if { + input.test == "foo" + } else if { + input.test == y + } else if { + input.test == z + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("func(x, y, z)"), "", 2, 4), + Message: "unused argument x. (hint: use _ (wildcard variable) instead)", + }, + }, + }, + { + note: "argvars usage spread over multiple bodies, missing in second", + module: `package test + func(x, y, z) if { + input.test == x + } else if { + input.test == "bar" + } else if { + input.test == z + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("func(x, y, z)"), "", 2, 4), + Message: "unused argument y. (hint: use _ (wildcard variable) instead)", + }, + }, + }, + { + note: "argvars usage spread over multiple bodies, missing in third", + module: `package test + func(x, y, z) if { + input.test == x + } else if { + input.test == y + } else if { + input.test == "baz" + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("func(x, y, z)"), "", 2, 4), + Message: "unused argument z. (hint: use _ (wildcard variable) instead)", + }, + }, + }, + { + note: "unused default function argvar", + module: `package test + default func(x) := 0`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("func(x) := 0"), "", 2, 12), + Message: "unused argument x. (hint: use _ (wildcard variable) instead)", + }, + }, + }, + } + + t.Helper() + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + compiler := NewCompiler().WithStrict(true) + compiler.Modules = map[string]*Module{ + "test": module(tc.module), + } + compileStages(compiler, nil) + + assertErrors(t, compiler.Errors, tc.expectedErrors, true) + }) + } +} + +func TestCompileUnusedAssignedVarsErrorLocations(t *testing.T) { + tests := []strictnessTestCase{ + { + note: "one of the two function args is not used - issue 5662 regression test", + module: `package test + func(x, y) if { + x = 1 + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("func(x, y)"), "", 2, 4), + Message: "unused argument y. (hint: use _ (wildcard variable) instead)", + }, + }, + }, + { + note: "multiple unused assigned var in scope - issue 5662 regression test", + module: `package test + allow if { + input.message == "world" + input.test == "foo" + input.x == "foo" + input.y == "baz" + a := 1 + b := 2 + x := { + "a": a, + "b": "bar", + } + input.z == "baz" + c := 3 + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("b := 2"), "", 8, 5), + Message: "assigned var b unused", + }, + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("x := {\n\t\t\t\t\t\"a\": a,\n\t\t\t\t\t\"b\": \"bar\",\n\t\t\t\t}"), "", 9, 5), + Message: "assigned var x unused", + }, + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("c := 3"), "", 14, 5), + Message: "assigned var c unused", + }, + }, + }, + } + + t.Helper() + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + compiler := NewCompiler().WithStrict(true) + compiler.Modules = map[string]*Module{ + "test": module(tc.module), + } + compileStages(compiler, nil) + assertErrors(t, compiler.Errors, tc.expectedErrors, true) + }) + } + +} + +func TestCompileUnusedDeclaredVarsErrorLocations(t *testing.T) { + tests := []strictnessTestCase{ + { + note: "simple unused some var - issue 4238 regression test", + module: `package test + + foo if { + print("Hello world") + some i + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("some i"), "", 5, 5), + Message: "declared var i unused", + }, + }, + }, + { + note: "simple unused some vars, 2x rules", + module: `package test + + foo if { + print("Hello world") + some i + } + + bar if { + print("Hello world") + some j + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("some i"), "", 5, 5), + Message: "declared var i unused", + }, + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("some j"), "", 10, 5), + Message: "declared var j unused", + }, + }, + }, + { + note: "multiple unused some vars", + module: `package test + + x := [1, 1, 1] + foo2 if { + print("A") + some a, b, c + some i, j + some k + x[b] == 1 + print("B") + }`, + expectedErrors: Errors{ + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("some a, b, c"), "", 6, 5), + Message: "declared var a unused", + }, + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("some a, b, c"), "", 6, 5), + Message: "declared var c unused", + }, + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("some i, j"), "", 7, 5), + Message: "declared var i unused", + }, + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("some i, j"), "", 7, 5), + Message: "declared var j unused", + }, + &Error{ + Code: CompileErr, + Location: NewLocation([]byte("some k"), "", 8, 5), + Message: "declared var k unused", + }, + }, + }, + } + + // This is similar to the logic for runStrictnessTestCase(), but expects + // unconditional compiler errors. + t.Helper() + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + compiler := NewCompiler().WithStrict(true) + compiler.Modules = map[string]*Module{ + "test": module(tc.module), + } + compileStages(compiler, nil) + + assertErrors(t, compiler.Errors, tc.expectedErrors, true) + }) + } +} + +func TestCompileInvalidEqAssignExpr(t *testing.T) { + tests := []struct { + note string + regoVersion RegoVersion + }{ + { + note: "v0", + regoVersion: RegoV0, + }, + { + note: "v1", + regoVersion: RegoV1, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler() + + c.Modules["error"] = MustParseModuleWithOpts(`package errors + + p if { + # Arity mismatches are caught in the checkUndefinedFuncs check, + # and invalid eq/assign calls are passed along until then. + assign() + assign(1) + eq() + eq(1) + }`, ParserOptions{RegoVersion: tc.regoVersion, AllFutureKeywords: true}) + + var prev func() + checkUndefinedFuncs := reflect.ValueOf(c.checkUndefinedFuncs) + + for _, stage := range c.stages { + if reflect.ValueOf(stage.f).Pointer() == checkUndefinedFuncs.Pointer() { + break + } + prev = stage.f + } + + compileStages(c, prev) + assertNotFailed(t, c) + }) + } +} + +func TestCompilerRewriteComprehensionTerm(t *testing.T) { + + c := NewCompiler() + c.Modules["head"] = MustParseModule(`package head + arr = [[1], [2], [3]] + arr2 = [["a"], ["b"], ["c"]] + arr_comp = [[x[i]] | arr[j] = x] + set_comp = {[x[i]] | arr[j] = x} + obj_comp = {x[i]: x[i] | arr2[j] = x} + `) + + compileStages(c, c.rewriteComprehensionTerms) + assertNotFailed(t, c) + + arrCompRule := c.Modules["head"].Rules[2] + exp1 := MustParseRule(`arr_comp = [__local0__ | data.head.arr[j] = x; __local0__ = [x[i]]] { true }`) + assertRulesEqual(t, arrCompRule, exp1) + + setCompRule := c.Modules["head"].Rules[3] + exp2 := MustParseRule(`set_comp = {__local1__ | data.head.arr[j] = x; __local1__ = [x[i]]} { true }`) + assertRulesEqual(t, setCompRule, exp2) + + objCompRule := c.Modules["head"].Rules[4] + exp3 := MustParseRule(`obj_comp = {__local2__: __local3__ | data.head.arr2[j] = x; __local2__ = x[i]; __local3__ = x[i]} { true }`) + assertRulesEqual(t, objCompRule, exp3) +} + +func TestCompilerRewriteDoubleEq(t *testing.T) { + tests := []struct { + note string + input string + exp string + }{ + { + note: "vars and constants", + input: "p if { x = 1; x == 1; y = [1,2,3]; y == [1,2,3] }", + exp: `x = 1; x = 1; y = [1,2,3]; y = [1,2,3]`, + }, + { + note: "refs", + input: "p if { input.x == data.y }", + exp: `input.x = data.y`, + }, + { + note: "comprehensions", + input: "p if { [1|true] == [2|true] }", + exp: `[1|true] = [2|true]`, + }, + // TODO(tsandall): improve support for calls so that extra unification step is + // not required. This requires more changes to the compiler as the initial + // stages that rewrite term exprs needs to be updated to handle == differently + // and then other stages need to be reviewed to make sure they can deal with + // nested calls. Alternatively, the compiler could keep track of == exprs that + // have been converted into = and then the safety check would need to be updated. + { + note: "calls", + input: "p if { count([1,2]) == 2 }", + exp: `count([1,2], __local0__); __local0__ = 2`, + }, + { + note: "embedded", + input: "p if { x = 1; y = [x == 0] }", + exp: `x = 1; equal(x, 0, __local0__); y = [__local0__]`, + }, + { + note: "embedded in call", + input: `p if { x = 0; neq(true, x == 1) }`, + exp: `x = 0; equal(x, 1, __local0__); neq(true, __local0__)`, + }, + { + note: "comprehension in object key", + input: `p if { {{1 | 0 == 0}: 2} }`, + exp: `{{1 | 0 = 0}: 2}`, + }, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler() + c.Modules["test"] = module("package test\n" + tc.input) + compileStages(c, c.rewriteEquals) + assertNotFailed(t, c) + exp := MustParseBody(tc.exp) + result := c.Modules["test"].Rules[0].Body + if result.Compare(exp) != 0 { + t.Fatalf("\nExp: %v\nGot: %v", exp, result) + } + }) + } +} + +func TestCompilerRewriteDynamicTerms(t *testing.T) { + + fixture := ` + package test + str = "hello" + ` + + tests := []struct { + input string + expected string + }{ + {`arr if { [str] }`, `__local0__ = data.test.str; [__local0__]`}, + {`arr2 if { [[str]] }`, `__local0__ = data.test.str; [[__local0__]]`}, + {`obj if { {"x": str} }`, `__local0__ = data.test.str; {"x": __local0__}`}, + {`obj2 if { {"x": {"y": str}} }`, `__local0__ = data.test.str; {"x": {"y": __local0__}}`}, + {`set if { {str} }`, `__local0__ = data.test.str; {__local0__}`}, + {`set2 if { {{str}} }`, `__local0__ = data.test.str; {{__local0__}}`}, + {`ref if { str[str] }`, `__local0__ = data.test.str; data.test.str[__local0__]`}, + {`ref2 if { str[str[str]] }`, `__local0__ = data.test.str; __local1__ = data.test.str[__local0__]; data.test.str[__local1__]`}, + {`arr_compr if { [1 | [str]] }`, `[1 | __local0__ = data.test.str; [__local0__]]`}, + {`arr_compr2 if { [1 | [1 | [str]]] }`, `[1 | [1 | __local0__ = data.test.str; [__local0__]]]`}, + {`set_compr if { {1 | [str]} }`, `{1 | __local0__ = data.test.str; [__local0__]}`}, + {`set_compr2 if { {1 | {1 | [str]}} }`, `{1 | {1 | __local0__ = data.test.str; [__local0__]}}`}, + {`obj_compr if { {"a": "b" | [str]} }`, `{"a": "b" | __local0__ = data.test.str; [__local0__]}`}, + {`obj_compr2 if { {"a": "b" | {"a": "b" | [str]}} }`, `{"a": "b" | {"a": "b" | __local0__ = data.test.str; [__local0__]}}`}, + {`equality if { str = str }`, `data.test.str = data.test.str`}, + {`equality2 if { [str] = [str] }`, `__local0__ = data.test.str; __local1__ = data.test.str; [__local0__] = [__local1__]`}, + {`call if { startswith(str, "") }`, `__local0__ = data.test.str; startswith(__local0__, "")`}, + {`call2 if { count([str], n) }`, `__local0__ = data.test.str; count([__local0__], n)`}, + {`eq_with if { [str] = [1] with input as 1 }`, `__local0__ = data.test.str with input as 1; [__local0__] = [1] with input as 1`}, + {`term_with if { [[str]] with input as 1 }`, `__local0__ = data.test.str with input as 1; [[__local0__]] with input as 1`}, + {`call_with if { count(str) with input as 1 }`, `__local0__ = data.test.str with input as 1; count(__local0__) with input as 1`}, + {`call_func if { f(input, "foo") } f(x,y) if { x[y] }`, `__local2__ = input; data.test.f(__local2__, "foo")`}, + {`call_func2 if { f(input.foo, "foo") } f(x,y) if { x[y] }`, `__local2__ = input.foo; data.test.f(__local2__, "foo")`}, + {`every_domain if { every _ in str { true } }`, `__local1__ = data.test.str; every __local0__, _ in __local1__ { true }`}, + {`every_domain_array if { every _ in [1, 2, 3] { true } }`, `__local1__ = [1, 2, 3]; every __local0__, _ in __local1__ { true }`}, + {`every_domain_call if { every _ in numbers.range(1, 10) { true } }`, `numbers.range(1, 10, __local2__); __local1__ = __local2__; every __local0__, _ in __local1__ { true }`}, + {`every_domain_array_w_calls if { every _ in [1 / 2, "foo", abs(-1)] { true } }`, `div(1, 2, __local2__); abs(-1, __local3__); __local1__ = [__local2__, "foo", __local3__]; every __local0__, _ in __local1__ { true }`}, + {`every_body if { every _ in [] { [str] } }`, + `__local1__ = []; every __local0__, _ in __local1__ { __local2__ = data.test.str; [__local2__] }`}, + } + + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + c := NewCompiler() + opts := ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true} + c.Modules["test"] = module(fixture + tc.input) + compileStages(c, c.rewriteDynamicTerms) + assertNotFailed(t, c) + expected := MustParseBodyWithOpts(tc.expected, opts) + result := c.Modules["test"].Rules[1].Body + if result.Compare(expected) != 0 { + t.Fatalf("\nExp: %v\nGot: %v", expected, result) + } + }) + } +} + +func TestCompilerRewriteWithValue(t *testing.T) { + fixture := `package test + + arr = ["hello", "goodbye"] + + ` + + tests := []struct { + note string + input string + opts func(*Compiler) *Compiler + expected string + expectedRule *Rule + wantErr error + }{ + { + note: "nop", + input: `p if { true with input as 1 }`, + expected: `p if { true with input as 1 }`, + }, + { + note: "refs", + input: `p if { true with input as arr }`, + expected: `p if { __local0__ = data.test.arr; true with input as __local0__ }`, + }, + { + note: "array comprehension", + input: `p if { true with input as [true | true] }`, + expected: `p if { __local0__ = [true | true]; true with input as __local0__ }`, + }, + { + note: "set comprehension", + input: `p if { true with input as {true | true} }`, + expected: `p if { __local0__ = {true | true}; true with input as __local0__ }`, + }, + { + note: "object comprehension", + input: `p if { true with input as {"k": true | true} }`, + expected: `p if { __local0__ = {"k": true | true}; true with input as __local0__ }`, + }, + { + note: "comprehension nested", + input: `p if { true with input as [true | true with input as arr] }`, + expected: `p if { __local0__ = [true | __local1__ = data.test.arr; true with input as __local1__]; true with input as __local0__ }`, + }, + { + note: "multiple", + input: `p if { true with input.a as arr[0] with input.b as arr[1] }`, + expected: `p if { __local0__ = data.test.arr[0]; __local1__ = data.test.arr[1]; true with input.a as __local0__ with input.b as __local1__ }`, + }, + { + note: "invalid target", + input: `p if { true with foo.q as 1 }`, + wantErr: errors.New("rego_type_error: with keyword target must reference existing input, data, or a function"), + }, + { + note: "built-in function: replaced by (unknown) var", + input: `p if { true with time.now_ns as foo }`, + expected: `p if { true with time.now_ns as foo }`, // `foo` still a Var here + }, + { + note: "built-in function: valid, arity 0", + input: ` + p if { true with time.now_ns as now } + now() = 1 + `, + expected: `p if { true with time.now_ns as data.test.now }`, + }, + { + note: "built-in function: valid func ref, arity 1", + input: ` + p if { true with http.send as mock_http_send } + mock_http_send(_) = { "body": "yay" } + `, + expected: `p if { true with http.send as data.test.mock_http_send }`, + }, + { + note: "built-in function: replaced by value", + input: ` + p if { true with http.send as { "body": "yay" } } + `, + expected: `p if { true with http.send as {"body": "yay"} }`, + }, + { + note: "built-in function: replaced by var", + input: ` + p if { + resp := { "body": "yay" } + true with http.send as resp + } + `, + expected: `p if { __local0__ = {"body": "yay"}; true with http.send as __local0__ }`, + }, + { + note: "non-built-in function: replaced by var", + input: ` + p if { + resp := true + f(true) with f as resp + } + f(false) if { true } + `, + expected: `p if { __local0__ = true; data.test.f(true) with data.test.f as __local0__ }`, + }, + { + note: "built-in function: replaced by comprehension", + input: ` + p if { true with http.send as { x: true | x := ["a", "b"][_] } } + `, + expected: `p if { __local2__ = {__local0__: true | __local1__ = ["a", "b"]; __local0__ = __local1__[_]}; true with http.send as __local2__ }`, + }, + { + note: "built-in function: replaced by ref", + input: ` + p if { true with http.send as resp } + resp := { "body": "yay" } + `, + expected: `p if { true with http.send as data.test.resp }`, + }, + { + note: "built-in function: replaced by another built-in (ref)", + input: ` + p if { true with http.send as object.union_n } + `, + expected: `p if { true with http.send as object.union_n }`, + }, + { + note: "built-in function: replaced by another built-in (simple)", + input: ` + p if { true with http.send as count } + `, + expectedRule: func() *Rule { + r := MustParseRule(`p { true with http.send as count }`) + r.Body[0].With[0].Value.Value = Ref([]*Term{VarTerm("count")}) + return r + }(), + }, + { + note: "built-in function: replaced by another built-in that's marked unsafe", + input: ` + q := is_object({"url": "https://httpbin.org", "method": "GET"}) + p if { q with is_object as http.send } + `, + opts: func(c *Compiler) *Compiler { return c.WithUnsafeBuiltins(map[string]struct{}{"http.send": {}}) }, + wantErr: errors.New("rego_compile_error: with keyword replacing built-in function: target must not be unsafe: \"http.send\""), + }, + { + note: "non-built-in function: replaced by another built-in that's marked unsafe", + input: ` + r(_) = {} + q := r({"url": "https://httpbin.org", "method": "GET"}) + p if { + q with r as http.send + }`, + opts: func(c *Compiler) *Compiler { return c.WithUnsafeBuiltins(map[string]struct{}{"http.send": {}}) }, + wantErr: errors.New("rego_compile_error: with keyword replacing built-in function: target must not be unsafe: \"http.send\""), + }, + { + note: "built-in function: valid, arity 1, non-compound name", + input: ` + p if { concat("/", input) with concat as mock_concat } + mock_concat(_, _) = "foo/bar" + `, + expectedRule: func() *Rule { + r := MustParseRuleWithOpts(`p if { concat("/", input) with concat as data.test.mock_concat }`, + ParserOptions{RegoVersion: RegoV1}) + r.Body[0].With[0].Target.Value = Ref([]*Term{VarTerm("concat")}) + return r + }(), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler() + if tc.opts != nil { + c = tc.opts(c) + } + c.Modules["test"] = module(fixture + tc.input) + compileStages(c, c.rewriteWithModifiers) + if tc.wantErr == nil { + assertNotFailed(t, c) + expected := tc.expectedRule + if expected == nil { + expected = MustParseRuleWithOpts(tc.expected, ParserOptions{RegoVersion: RegoV1}) + } + result := c.Modules["test"].Rules[1] + if result.Compare(expected) != 0 { + t.Fatalf("\nExp: %v\nGot: %v", expected, result) + } + } else { + assertCompilerErrorStrings(t, c, []string{tc.wantErr.Error()}) + } + }) + } +} + +func TestCompilerRewritePrintCallsErasure(t *testing.T) { + + cases := []struct { + note string + module string + exp string + }{ + { + note: "no-op", + module: `package test + p if { true }`, + exp: `package test + p if { true }`, + }, + { + note: "replace empty body with true", + module: `package test + + p if { print(1) } + `, + exp: `package test + + p if { true } `, + }, + { + note: "rule body", + module: `package test + + p if { false; print(1) } + `, + exp: `package test + + p if { false } `, + }, + { + note: "set comprehension body", + module: `package test + + p if { {1 | false; print(1)} } + `, + exp: `package test + + p if { {1 | false} } `, + }, + { + note: "array comprehension body", + module: `package test + + p if { [1 | false; print(1)] } + `, + exp: `package test + + p if { [1 | false] } `, + }, + { + note: "object comprehension body", + module: `package test + + p if { {"x": 1 | false; print(1)} } + `, + exp: `package test + + p if { {"x": 1 | false} } `, + }, + { + note: "every body", + module: `package test + + p if { every _ in [] { false; print(1) } } + `, + exp: `package test + + p = true if { __local1__ = []; every __local0__, _ in __local1__ { false } }`, + }, + { + note: "in head", + module: `package test + + p = {1 | print("x")}`, + exp: `package test + + p = __local0__ if { true; __local0__ = {1 | true} }`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler().WithEnablePrintStatements(false) + c.Compile(map[string]*Module{ + "test.rego": module(tc.module), + }) + if c.Failed() { + t.Fatal(c.Errors) + } + exp := module(tc.exp) + if !exp.Equal(c.Modules["test.rego"]) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, c.Modules["test.rego"]) + } + }) + } +} + +func TestCompilerRewritePrintCallsErrors(t *testing.T) { + cases := []struct { + note string + module string + exp error + }{ + { + note: "non-existent var", + module: `package test + + p if { print(x) }`, + exp: errors.New("var x is undeclared"), + }, + { + note: "declared after print", + module: `package test + + p if { print(x); x = 7 }`, + exp: errors.New("var x is undeclared"), + }, + { + note: "inside comprehension", + module: `package test + p if { {1 | print(x)} = {1 | print(7)} } + `, + exp: errors.New("var x is undeclared"), + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler().WithEnablePrintStatements(true) + c.Compile(map[string]*Module{ + "test.rego": module(tc.module), + }) + if !c.Failed() { + t.Fatal("expected error") + } + if c.Errors[0].Code != CompileErr || c.Errors[0].Message != tc.exp.Error() { + t.Fatal("unexpected error:", c.Errors) + } + }) + } +} + +func TestCompilerRewritePrintCalls(t *testing.T) { + cases := []struct { + note string + module string + exp string + }{ + { + note: "print one", + module: `package test + + p if { print(1) }`, + exp: `package test + + p = true if { __local1__ = {__local0__ | __local0__ = 1}; internal.print([__local1__]) }`, + }, + { + note: "print multiple", + module: `package test + + p if { print(1, 2) }`, + exp: `package test + + p = true if { __local2__ = {__local0__ | __local0__ = 1}; __local3__ = {__local1__ | __local1__ = 2}; internal.print([__local2__, __local3__]) }`, + }, + { + note: "print inside set comprehension", + module: `package test + + p if { x = 1; {2 | print(x)} }`, + exp: `package test + + p = true if { x = 1; {2 | __local1__ = {__local0__ | __local0__ = x}; internal.print([__local1__])} }`, + }, + { + note: "print inside array comprehension", + module: `package test + + p if { x = 1; [2 | print(x)] }`, + exp: `package test + + p = true if { x = 1; [2 | __local1__ = {__local0__ | __local0__ = x}; internal.print([__local1__])] }`, + }, + { + note: "print inside object comprehension", + module: `package test + + p if { x = 1; {"x": 2 | print(x)} }`, + exp: `package test + + p = true if { x = 1; {"x": 2 | __local1__ = {__local0__ | __local0__ = x}; internal.print([__local1__])} }`, + }, + { + note: "print inside every", + module: `package test + + p if { every x in [1,2] { print(x) } }`, + exp: `package test + + p = true if { + __local3__ = [1, 2] + every __local0__, __local1__ in __local3__ { + __local4__ = {__local2__ | __local2__ = __local1__} + internal.print([__local4__]) + } + }`, + }, + { + note: "print output of nested call", + module: `package test + + p if { + x := split("abc", "")[y] + print(x, y) + }`, + exp: `package test + + p = true if { split("abc", "", __local3__); __local0__ = __local3__[y]; __local4__ = {__local1__ | __local1__ = __local0__}; __local5__ = {__local2__ | __local2__ = y}; internal.print([__local4__, __local5__]) }`, + }, + { + note: "print call in head", + module: `package test + + p = {1 | print("x") }`, + exp: `package test + + p = __local1__ if { + true + __local1__ = {1 | __local2__ = { __local0__ | __local0__ = "x"}; internal.print([__local2__])} + }`, + }, + { + note: "print call in head - args treated as safe", + module: `package test + + f(a) = {1 | a[x]; print(x)}`, + exp: `package test + + f(__local0__) = __local2__ if { true; __local2__ = {1 | __local0__[x]; __local3__ = {__local1__ | __local1__ = x}; internal.print([__local3__])} } + `, + }, + { + note: "print call of var in head key", + module: `package test + f(_) = [1, 2, 3] + p contains x if { [_, x, _] := f(true); print(x) }`, + exp: `package test + f(__local0__) = [1, 2, 3] if { true } + p contains __local2__ if { data.test.f(true, __local5__); [__local1__, __local2__, __local3__] = __local5__; __local6__ = {__local4__ | __local4__ = __local2__}; internal.print([__local6__]) } + `, + }, + { + note: "print call of var in head value", + module: `package test + f(_) = [1, 2, 3] + p = x if { [_, x, _] := f(true); print(x) }`, + exp: `package test + f(__local0__) = [1, 2, 3] if { true } + p = __local2__ if { data.test.f(true, __local5__); [__local1__, __local2__, __local3__] = __local5__; __local6__ = {__local4__ | __local4__ = __local2__}; internal.print([__local6__]) } + `, + }, + { + note: "print call of vars in head key and value", + module: `package test + f(_) = [1, 2, 3] + p[x] = y if { [_, x, y] := f(true); print(x) }`, + exp: `package test + f(__local0__) = [1, 2, 3] if { true } + p[__local2__] = __local3__ if { data.test.f(true, __local5__); [__local1__, __local2__, __local3__] = __local5__; __local6__ = {__local4__ | __local4__ = __local2__}; internal.print([__local6__]) } + `, + }, + { + note: "print call of vars altered with 'with' and call", + module: `package test + q = input + p if { + x := q with input as json.unmarshal("{}") + print(x) + }`, + exp: `package test + q = __local3__ if { true; __local3__ = input } + p = true if { + json.unmarshal("{}", __local2__) + __local0__ = data.test.q with input as __local2__ + __local4__ = {__local1__ | __local1__ = __local0__} + internal.print([__local4__]) + }`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler().WithEnablePrintStatements(true) + c.Compile(map[string]*Module{ + "test.rego": module(tc.module), + }) + if c.Failed() { + t.Fatal(c.Errors) + } + exp := module(tc.exp) + if !exp.Equal(c.Modules["test.rego"]) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, c.Modules["test.rego"]) + } + }) + } +} + +func TestRewritePrintCallsWithElseImplicitArgs(t *testing.T) { + + mod := `package test + + f(x, y) if { + x = y + } + + else = false if { + print(x, y) + }` + + c := NewCompiler().WithEnablePrintStatements(true) + c.Compile(map[string]*Module{ + "test.rego": module(mod), + }) + + if c.Failed() { + t.Fatal(c.Errors) + } + + exp := module(`package test + + f(__local0__, __local1__) = true if { __local0__ = __local1__ } + else = false if { __local4__ = {__local2__ | __local2__ = __local0__}; __local5__ = {__local3__ | __local3__ = __local1__}; internal.print([__local4__, __local5__]) } + `) + + // NOTE(tsandall): we have to patch the implicit args on the else rule + // because of how the parser copies the arg names across from the first + // rule. + exp.Rules[0].Else.Head.Args[0] = VarTerm("__local0__") + exp.Rules[0].Else.Head.Args[1] = VarTerm("__local1__") + + if !exp.Equal(c.Modules["test.rego"]) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, c.Modules["test.rego"]) + } +} + +func TestCompilerMockFunction(t *testing.T) { + tests := []struct { + note string + module, extra string + err string + }{ + { + note: "simple valid", + module: `package test + now() = 123 + p if { true with time.now_ns as now } + `, + }, + { + note: "simple valid, simple name", + module: `package test + mock_concat(_, _) = "foo/bar" + p if { concat("/", input) with concat as mock_concat } + `, + }, + { + note: "invalid ref: nonexistant", + module: `package test + p if { true with time.now_ns as now } + `, + err: "rego_unsafe_var_error: var now is unsafe", // we're running all compiler stages here + }, + { + note: "valid ref: not a function, but arity = 0", + module: `package test + now = 1 + p if { true with time.now_ns as now } + `, + }, + { + note: "ref: not a function, arity > 0", + module: `package test + http_send = { "body": "nope" } + p if { true with http.send as http_send } + `, + }, + { + note: "invalid ref: arity mismatch", + module: `package test + http_send(_, _) = { "body": "nope" } + p if { true with http.send as http_send } + `, + err: "rego_type_error: http.send: arity mismatch\n\thave: (any, any)\n\twant: (request: object[string: any])", + }, + { + note: "invalid ref: arity mismatch (in call)", + module: `package test + http_send(_, _) = { "body": "nope" } + p if { http.send({}) with http.send as http_send } + `, + err: "rego_type_error: http.send: arity mismatch\n\thave: (any, any)\n\twant: (request: object[string: any])", + }, + { + note: "invalid ref: value another built-in with different type", + module: `package test + p if { true with http.send as net.lookup_ip_addr } + `, + err: "rego_type_error: http.send: arity mismatch\n\thave: (string)\n\twant: (request: object[string: any])", + }, + { + note: "ref: value another built-in with compatible type", + module: `package test + p if { true with count as object.union_n } + `, + }, + { + note: "valid: package import", + extra: `package mocks + http_send(_) = {} + `, + module: `package test + import data.mocks + p if { true with http.send as mocks.http_send } + `, + }, + { + note: "valid: function import", + extra: `package mocks + http_send(_) = {} + `, + module: `package test + import data.mocks.http_send + p if { true with http.send as http_send } + `, + }, + { + note: "invalid target: relation", + module: `package test + my_walk(_, _) + p if { true with walk as my_walk } + `, + err: "rego_compile_error: with keyword replacing built-in function: target must not be a relation", + }, + { + note: "invalid target: eq", + module: `package test + my_eq(_, _) + p if { true with eq as my_eq } + `, + err: `rego_compile_error: with keyword replacing built-in function: replacement of "eq" invalid`, + }, + { + note: "invalid target: rego.metadata.chain", + module: `package test + p if { true with rego.metadata.chain as [] } + `, + err: `rego_compile_error: with keyword replacing built-in function: replacement of "rego.metadata.chain" invalid`, + }, + { + note: "invalid target: rego.metadata.rule", + module: `package test + p if { true with rego.metadata.rule as {} } + `, + err: `rego_compile_error: with keyword replacing built-in function: replacement of "rego.metadata.rule" invalid`, + }, + { + note: "invalid target: internal.print", + module: `package test + my_print(_, _) + p if { true with internal.print as my_print } + `, + err: `rego_compile_error: with keyword replacing built-in function: replacement of internal function "internal.print" invalid`, + }, + { + note: "mocking custom built-in", + module: `package test + mock(_) + mock_mock(_) + p if { bar(foo.bar("one")) with bar as mock with foo.bar as mock_mock } + `, + }, + { + note: "non-built-in function replaced value", + module: `package test + original(_) + p if { original(true) with original as 123 } + `, + }, + { + note: "non-built-in function replaced by another, arity 0", + module: `package test + original() = 1 + mock() = 2 + p if { original() with original as mock } + `, + err: "rego_type_error: undefined function data.test.original", // TODO(sr): file bug -- this doesn't depend on "with" used or not + }, + { + note: "non-built-in function replaced by another, arity 1", + module: `package test + original(_) + mock(_) + p if { original(true) with original as mock } + `, + }, + { + note: "non-built-in function replaced by built-in", + module: `package test + original(_) + p if { original([1]) with original as count } + `, + }, + { + note: "non-built-in function replaced by another, arity mismatch", + module: `package test + original(_) + mock(_, _) + p if { original([1]) with original as mock } + `, + err: "rego_type_error: data.test.original: arity mismatch\n\thave: (any, any)\n\twant: (any)", + }, + { + note: "non-built-in function replaced by built-in, arity mismatch", + module: `package test + original(_) + p if { original([1]) with original as concat } + `, + err: "rego_type_error: data.test.original: arity mismatch\n\thave: (string, any)\n\twant: (any)", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler().WithBuiltins(map[string]*Builtin{ + "bar": { + Name: "bar", + Decl: types.NewFunction([]types.Type{types.S}, types.A), + }, + "foo.bar": { + Name: "foo.bar", + Decl: types.NewFunction([]types.Type{types.S}, types.A), + }, + }) + if tc.extra != "" { + c.Modules["extra"] = module(tc.extra) + } + c.Modules["test"] = module(tc.module) + + // NOTE(sr): We're running all compiler stages here, since the type checking of + // built-in function replacements happens at the type check stage. + c.Compile(c.Modules) + + if tc.err != "" { + if !strings.Contains(c.Errors.Error(), tc.err) { + t.Errorf("expected error to contain %q, got %q", tc.err, c.Errors.Error()) + } + } else if len(c.Errors) > 0 { + t.Errorf("expected no errors, got %v", c.Errors) + } + }) + } + +} + +func TestCompilerMockVirtualDocumentPartially(t *testing.T) { + c := NewCompiler() + + c.Modules["test"] = module(` + package test + p = {"a": 1} + q = x if { p = x with p.a as 2 } + `) + + compileStages(c, c.rewriteWithModifiers) + assertCompilerErrorStrings(t, c, []string{"rego_compile_error: with keyword cannot partially replace virtual document(s)"}) +} + +func TestCompilerCheckUnusedAssignedVar(t *testing.T) { + type testCase struct { + note string + module string + expectedErrors Errors + } + + cases := []testCase{ + { + note: "global var", + module: `package test + x := 1 + `, + }, + { + note: "simple rule with wildcard", + module: `package test + p if { + _ := 1 + } + `, + }, + { + note: "simple rule", + module: `package test + p if { + x := 1 + y := 2 + z := x + 3 + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + &Error{Message: "assigned var z unused"}, + }, + }, + { + note: "rule with return", + module: `package test + p = x if { + x := 2 + y := 3 + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "rule with function call", + module: `package test + p if { + x := 2 + y := f(x) + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "rule with nested array comprehension", + module: `package test + p if { + x := 2 + y := [z | z := 2 * x] + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "rule with nested array comprehension and shadowing", + module: `package test + p if { + x := 2 + y := [x | x := 2 * x] + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "rule with nested array comprehension and shadowing (unused shadowed var)", + module: `package test + p if { + x := 2 + y := [x | x := 2] + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var x unused"}, + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "rule with nested array comprehension and shadowing (unused shadowing var)", + module: `package test + p if { + x := 2 + x > 1 + [1 | x := 2] + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var x unused"}, + }, + }, + { + note: "rule with nested array comprehension and some declaration", + module: `package test + p if { + some i + _ := [z | z := [1, 2][i]] + } + `, + }, + { + note: "rule with nested set comprehension", + module: `package test + p if { + x := 2 + y := {z | z := 2 * x} + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "rule with nested set comprehension and unused inner var", + module: `package test + p if { + x := 2 + y := {z | z := 2 * x; a := 2} + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var a unused"}, // y isn't reported, as we abort early on errors when moving through the stack + }, + }, + { + note: "rule with nested object comprehension", + module: `package test + p if { + x := 2 + y := {z: x | z := 2 * x} + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "rule with nested closure", + module: `package test + p if { + x := 1 + a := 1 + { y | y := [ z | z:=[1,2,3][a]; z > 1 ][_] } + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var x unused"}, + }, + }, + { + note: "rule with nested closure and unused inner var", + module: `package test + p if { + x := 1 + { y | y := [ z | z:=[1,2,3][x]; z > 1; a := 2 ][_] } + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var a unused"}, + }, + }, + { + note: "simple function", + module: `package test + f() if { + x := 1 + y := 2 + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var x unused"}, + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "simple function with wildcard", + module: `package test + f() if { + x := 1 + _ := 2 + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var x unused"}, + }, + }, + { + note: "function with return", + module: `package test + f() = x if { + x := 1 + y := 2 + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "array comprehension", + module: `package test + comp = [ 1 | + x := [1, 2, 3] + y := 2 + z := x[_] + ] + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + &Error{Message: "assigned var z unused"}, + }, + }, + { + note: "array comprehension nested", + module: `package test + comp := [ 1 | + x := 1 + y := [a | a := x] + ] + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "array comprehension with wildcard", + module: `package test + comp = [ 1 | + x := [1, 2, 3] + _ := 2 + z := x[_] + ] + `, + expectedErrors: Errors{ + &Error{Message: "assigned var z unused"}, + }, + }, + { + note: "array comprehension with return", + module: `package test + comp = [ z | + x := [1, 2, 3] + y := 2 + z := x[_] + ] + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "array comprehension with some", + module: `package test + comp = [ i | + some i + y := 2 + ] + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "set comprehension", + module: `package test + comp = { 1 | + x := [1, 2, 3] + y := 2 + z := x[_] + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + &Error{Message: "assigned var z unused"}, + }, + }, + { + note: "set comprehension nested", + module: `package test + comp := { 1 | + x := 1 + y := [a | a := x] + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "set comprehension with wildcard", + module: `package test + comp = { 1 | + x := [1, 2, 3] + _ := 2 + z := x[_] + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var z unused"}, + }, + }, + { + note: "set comprehension with return", + module: `package test + comp = { z | + x := [1, 2, 3] + y := 2 + z := x[_] + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "set comprehension with some", + module: `package test + comp = { i | + some i + y := 2 + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "object comprehension", + module: `package test + comp = { 1: 2 | + x := [1, 2, 3] + y := 2 + z := x[_] + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + &Error{Message: "assigned var z unused"}, + }, + }, + { + note: "object comprehension nested", + module: `package test + comp := { 1: 1 | + x := 1 + y := {a: x | a := x} + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "object comprehension with wildcard", + module: `package test + comp = { 1: 2 | + x := [1, 2, 3] + _ := 2 + z := x[_] + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var z unused"}, + }, + }, + { + note: "object comprehension with return", + module: `package test + comp = { z: x | + x := [1, 2, 3] + y := 2 + z := x[_] + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "object comprehension with some", + module: `package test + comp = { i | + some i + y := 2 + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "every: unused assigned var in body", + module: `package test + p if { every i in [1] { y := 10; i == 1 } } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var y unused"}, + }, + }, + { + note: "general ref in rule head", + module: `package test + p[q].r[s] := 1 if { + q := "foo" + s := "bar" + t := "baz" + } + `, + expectedErrors: Errors{ + &Error{Message: "assigned var t unused"}, + }, + }, + { + note: "general ref in rule head (no errors)", + module: `package test + p[q].r[s] := 1 if { + q := "foo" + s := "bar" + } + `, + expectedErrors: Errors{}, + }, + } + + makeTestRunner := func(tc testCase, strict bool) func(t *testing.T) { + return func(t *testing.T) { + compiler := NewCompiler().WithStrict(strict) + compiler.Modules = map[string]*Module{ + "test": module(tc.module), + } + compileStages(compiler, compiler.rewriteLocalVars) + + if strict { + assertErrors(t, compiler.Errors, tc.expectedErrors, false) + } else { + assertNotFailed(t, compiler) + } + } + } + + for _, tc := range cases { + t.Run(tc.note+"_strict", makeTestRunner(tc, true)) + t.Run(tc.note+"_non-strict", makeTestRunner(tc, false)) + } +} + +func TestCompilerSetGraph(t *testing.T) { + c := NewCompiler() + c.Modules = getCompilerTestModules() + c.Modules["elsekw"] = module(` + package elsekw + + p if { + false + } else = q if { + false + } else if { + r + } + + q = true + r = true + + s if { t } + t if { false } else if { true } + + `) + compileStages(c, c.setGraph) + + assertNotFailed(t, c) + + mod1 := c.Modules["mod1"] + p := mod1.Rules[0] + q := mod1.Rules[1] + mod2 := c.Modules["mod2"] + r := mod2.Rules[0] + mod5 := c.Modules["mod5"] + + edges := map[util.T]struct{}{ + q: {}, + r: {}, + } + + if !reflect.DeepEqual(edges, c.Graph.Dependencies(p)) { + t.Fatalf("Expected dependencies for p to be q and r but got: %v", c.Graph.Dependencies(p)) + } + + // NOTE(tsandall): this is the correct result but it's chosen arbitrarily for the test. + expDependents := []struct { + x *Rule + want map[util.T]struct{} + }{ + { + x: p, + want: nil, + }, + { + x: q, + want: map[util.T]struct{}{p: {}, mod5.Rules[1]: {}, mod5.Rules[3]: {}, mod5.Rules[5]: {}}, + }, + { + x: r, + want: map[util.T]struct{}{p: {}}, + }, + } + + for _, exp := range expDependents { + if !reflect.DeepEqual(exp.want, c.Graph.Dependents(exp.x)) { + t.Fatalf("Expected dependents for %v to be %v but got: %v", exp.x, exp.want, c.Graph.Dependents(exp.x)) + } + } + + sorted, ok := c.Graph.Sort() + if !ok { + t.Fatalf("Expected sort to succeed.") + } + + numRules := 0 + + for _, module := range c.Modules { + WalkRules(module, func(*Rule) bool { + numRules++ + return false + }) + } + + if len(sorted) != numRules { + t.Fatalf("Expected numRules (%v) to be same as len(sorted) (%v)", numRules, len(sorted)) + } + + // Probe rules with dependencies. Ordering is not stable for ties because + // nodes are stored in a map. + probes := [][2]*Rule{ + {c.Modules["mod1"].Rules[1], c.Modules["mod1"].Rules[0]}, // mod1.q before mod1.p + {c.Modules["mod2"].Rules[0], c.Modules["mod1"].Rules[0]}, // mod2.r before mod1.p + {c.Modules["mod1"].Rules[1], c.Modules["mod5"].Rules[1]}, // mod1.q before mod5.r + {c.Modules["mod1"].Rules[1], c.Modules["mod5"].Rules[3]}, // mod1.q before mod6.t + {c.Modules["mod1"].Rules[1], c.Modules["mod5"].Rules[5]}, // mod1.q before mod6.v + {c.Modules["mod6"].Rules[2], c.Modules["mod6"].Rules[3]}, // mod6.r before mod6.s + {c.Modules["elsekw"].Rules[1], c.Modules["elsekw"].Rules[0].Else}, // elsekw.q before elsekw.p.else + {c.Modules["elsekw"].Rules[2], c.Modules["elsekw"].Rules[0].Else.Else}, // elsekw.r before elsekw.p.else.else + {c.Modules["elsekw"].Rules[4], c.Modules["elsekw"].Rules[3]}, // elsekw.t before elsekw.s + {c.Modules["elsekw"].Rules[4].Else, c.Modules["elsekw"].Rules[3]}, // elsekw.t.else before elsekw.s + } + + getSortedIdx := func(r *Rule) int { + for i := range sorted { + if sorted[i] == r { + return i + } + } + return -1 + } + + for num, probe := range probes { + i := getSortedIdx(probe[0]) + j := getSortedIdx(probe[1]) + if i == -1 || j == -1 { + t.Fatalf("Expected to find probe %d in sorted slice but got: i=%d, j=%d", num+1, i, j) + } + if i >= j { + t.Errorf("Sort order of probe %d (A) %v and (B) %v and is wrong (expected A before B)", num+1, probe[0], probe[1]) + } + } +} + +func TestGraphCycle(t *testing.T) { + mod1 := `package a.b.c + + p if { q } + q if { r } + r if { s } + s if { q }` + + c := NewCompiler() + c.Modules = map[string]*Module{ + "mod1": module(mod1), + } + + compileStages(c, c.setGraph) + assertNotFailed(t, c) + + _, ok := c.Graph.Sort() + if ok { + t.Fatalf("Expected to find cycle in rule graph") + } + + elsekw := `package elsekw + + p if { + false + } else = q if { + true + } + + q if { + false + } else if { + r + } + + r if { s } + + s if { p } + ` + + c = NewCompiler() + c.Modules = map[string]*Module{ + "elsekw": module(elsekw), + } + + compileStages(c, c.setGraph) + assertNotFailed(t, c) + + _, ok = c.Graph.Sort() + if ok { + t.Fatalf("Expected to find cycle in rule graph") + } + +} + +func TestCompilerCheckRecursion(t *testing.T) { + c := NewCompiler() + c.Modules = map[string]*Module{ + "newMod1": module(`package rec + +s = true if { t } +t = true if { s } +a = true if { b } +b = true if { c } +c = true if { d; e } +d = true if { true } +e = true if { a }`), + "newMod2": module(`package rec + +x = true if { s }`, + ), + "newMod3": module(`package rec2 + +import data.rec.x + +y = true if { x }`), + "newMod4": module(`package rec3 + +p[x] = y if { data.rec4[x][y] = z }`, + ), + "newMod5": module(`package rec4 + +import data.rec3.p + +q[x] = y if { p[x] = y }`), + "newMod6": module(`package rec5 + +acp contains x if { acq[x] } +acq contains x if { a = [true | acp[_]]; a[_] = x } +`, + ), + "newMod7": module(`package rec6 + +np[x] = y if { data.a[data.b.c[nq[x]]] = y } +nq[x] = y if { data.d[data.e[x].f[np[y]]] }`, + ), + "newMod8": module(`package rec7 + +prefix = true if { data.rec7 }`, + ), + "newMod9": module(`package rec8 + +dataref = true if { data }`, + ), + "newMod10": module(`package rec9 + + else_self if { false } else if { else_self } + + elsetop if { + false + } else = elsemid if { + true + } + + elsemid if { + false + } else if { + elsebottom + } + + elsebottom if { elsetop } + `), + "fnMod1": module(`package f0 + + fn(x) = y if { + fn(x, y) + }`), + "fnMod2": module(`package f1 + + foo(x) = y if { + bar("buz", x, y) + } + + bar(x, y) = z if { + foo([x, y], z) + }`), + "fnMod3": module(`package f2 + + foo(x) = y if { + bar("buz", x, y) + } + + bar(x, y) = z if { + x = p[y] + z = x + } + + p[x] = y if { + x = "foo.bar" + foo(x, y) + }`), + "everyMod": module(`package everymod + import future.keywords.every + everyp if { + every x in [true, false] { x; everyp } + } + everyq contains 1 if { + every x in everyq { x == 1 } + }`), + } + + compileStages(c, c.checkRecursion) + + makeRuleErrMsg := func(pkg, rule string, loop ...string) string { + l := make([]string, len(loop)) + for i, lo := range loop { + l[i] = "data." + pkg + "." + lo + } + return fmt.Sprintf("rego_recursion_error: rule data.%s.%s is recursive: %v", pkg, rule, strings.Join(l, " -> ")) + } + + expected := []string{ + makeRuleErrMsg("rec", "s", "s", "t", "s"), + makeRuleErrMsg("rec", "t", "t", "s", "t"), + makeRuleErrMsg("rec", "a", "a", "b", "c", "e", "a"), + makeRuleErrMsg("rec", "b", "b", "c", "e", "a", "b"), + makeRuleErrMsg("rec", "c", "c", "e", "a", "b", "c"), + makeRuleErrMsg("rec", "e", "e", "a", "b", "c", "e"), + `rego_recursion_error: rule data.rec3.p[x] is recursive: data.rec3.p[x] -> data.rec4.q[x] -> data.rec3.p[x]`, // NOTE(sr): these two are hardcoded: they are + `rego_recursion_error: rule data.rec4.q[x] is recursive: data.rec4.q[x] -> data.rec3.p[x] -> data.rec4.q[x]`, // the only ones not fitting the pattern. + makeRuleErrMsg("rec5", "acq", "acq", "acp", "acq"), + makeRuleErrMsg("rec5", "acp", "acp", "acq", "acp"), + makeRuleErrMsg("rec6", "np[x]", "np[x]", "nq[x]", "np[x]"), + makeRuleErrMsg("rec6", "nq[x]", "nq[x]", "np[x]", "nq[x]"), + makeRuleErrMsg("rec7", "prefix", "prefix", "prefix"), + makeRuleErrMsg("rec8", "dataref", "dataref", "dataref"), + makeRuleErrMsg("rec9", "else_self", "else_self", "else_self"), + makeRuleErrMsg("rec9", "elsetop", "elsetop", "elsemid", "elsebottom", "elsetop"), + makeRuleErrMsg("rec9", "elsemid", "elsemid", "elsebottom", "elsetop", "elsemid"), + makeRuleErrMsg("rec9", "elsebottom", "elsebottom", "elsetop", "elsemid", "elsebottom"), + makeRuleErrMsg("f0", "fn", "fn", "fn"), + makeRuleErrMsg("f1", "foo", "foo", "bar", "foo"), + makeRuleErrMsg("f1", "bar", "bar", "foo", "bar"), + makeRuleErrMsg("f2", "bar", "bar", "p[x]", "foo", "bar"), + makeRuleErrMsg("f2", "foo", "foo", "bar", "p[x]", "foo"), + makeRuleErrMsg("f2", "p[x]", "p[x]", "foo", "bar", "p[x]"), + makeRuleErrMsg("everymod", "everyp", "everyp", "everyp"), + makeRuleErrMsg("everymod", "everyq", "everyq", "everyq"), + } + + result := compilerErrsToStringSlice(c.Errors) + sort.Strings(expected) + + if len(result) != len(expected) { + t.Fatalf("Expected %d:\n%v\nBut got %d:\n%v", len(expected), strings.Join(expected, "\n"), len(result), strings.Join(result, "\n")) + } + + for i := range result { + if result[i] != expected[i] { + t.Errorf("Expected %v but got: %v", expected[i], result[i]) + } + } +} + +func TestCompilerCheckDynamicRecursion(t *testing.T) { + // This test tries to circumvent the recursion check by using dynamic + // references. For more background info, see + // . + + for _, tc := range []struct { + note, err string + mod *Module + }{ + { + note: "recursion", + mod: module(` +package recursion +pkg = "recursion" +foo contains x if { + data[pkg]["foo"][x] +} +`), + err: "rego_recursion_error: rule data.recursion.foo is recursive: data.recursion.foo -> data.recursion.foo", + }, + {note: "system.main", + mod: module(` +package system.main +foo if { + data[input] +} +`), + err: "rego_recursion_error: rule data.system.main.foo is recursive: data.system.main.foo -> data.system.main.foo", + }, + } { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler() + c.Modules = map[string]*Module{tc.note: tc.mod} + compileStages(c, c.checkRecursion) + + result := compilerErrsToStringSlice(c.Errors) + expected := tc.err + + if len(result) != 1 || result[0] != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } + }) + } +} + +// This is a regression test for a scenario that could make recursion checking miss a recursion scenario in OPA versions older than 0.56.0. +func TestCompilerCheckPartialRuleRecursion(t *testing.T) { + // In the below policy, R2 and R3 has a recursion cycle. In OPA < 0.56.0, R1 hides this cycle from the recursion checker, + // and no error is reported. + policy := `package test + +# R1 +results[id] := 1 if { + id := "bar" +} + +# R2 +results.foo := 2 if { + final_allow +} + +# R3 +final_allow if { + results.foo == 3 +}` + c := NewCompiler() + c.Modules = map[string]*Module{"test": module(policy)} + compileStages(c, c.checkRecursion) + + expected := Errors{ + &Error{Code: "rego_recursion_error", Message: "rule data.test.results.foo is recursive: data.test.results.foo -> data.test.final_allow -> data.test.results.foo"}, + &Error{Code: "rego_recursion_error", Message: "rule data.test.final_allow is recursive: data.test.final_allow -> data.test.results.foo -> data.test.final_allow"}, + } + + assertErrors(t, c.Errors, expected, false) +} + +func TestCompilerCheckVoidCalls(t *testing.T) { + c := NewCompiler().WithCapabilities(&Capabilities{Builtins: []*Builtin{ + { + Name: "test", + Decl: types.NewFunction([]types.Type{types.B}, nil), + }, + }}) + c.Compile(map[string]*Module{ + "test.rego": module(`package test + + p if { + x = test(true) + }`), + }) + if !c.Failed() { + t.Fatal("expected error") + } else if c.Errors[0].Code != TypeErr || c.Errors[0].Message != "test(true) used as value" { + t.Fatal("unexpected error:", c.Errors) + } +} + +func TestCompilerGetRulesExact(t *testing.T) { + mods := getCompilerTestModules() + + // Add incrementally defined rules. + mods["mod-incr"] = module(`package a.b.c + +p contains 1 if { true } +p contains 2 if { true }`, + ) + + c := NewCompiler() + c.Compile(mods) + assertNotFailed(t, c) + + tests := []struct { + note string + ref any + expected []*Rule + }{ + {"exact", "data.a.b.c.p", []*Rule{ + c.Modules["mod-incr"].Rules[0], + c.Modules["mod-incr"].Rules[1], + c.Modules["mod1"].Rules[0], + }}, + {"too short", "data.a", []*Rule{}}, + {"too long/not found", "data.a.b.c.p.q", []*Rule{}}, + {"outside data", "input.a.b.c.p", []*Rule{}}, + {"non-string/var", "data.a.b[data.foo]", []*Rule{}}, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var ref Ref + switch r := tc.ref.(type) { + case string: + ref = MustParseRef(r) + case Ref: + ref = r + } + rules := c.GetRulesExact(ref) + if len(rules) != len(tc.expected) { + t.Fatalf("Expected exactly %v rules but got: %v", len(tc.expected), rules) + } + for i := range rules { + found := slices.ContainsFunc(tc.expected, rules[i].Equal) + if !found { + t.Fatalf("Expected exactly %v but got: %v", tc.expected, rules) + } + } + }) + } +} + +func TestCompilerGetRulesForVirtualDocument(t *testing.T) { + mods := getCompilerTestModules() + + // Add incrementally defined rules. + mods["mod-incr"] = module(`package a.b.c + +p contains 1 if { true } +p contains 2 if { true }`, + ) + + c := NewCompiler() + c.Compile(mods) + assertNotFailed(t, c) + + tests := []struct { + note string + ref any + expected []*Rule + }{ + {"exact", "data.a.b.c.p", []*Rule{ + c.Modules["mod-incr"].Rules[0], + c.Modules["mod-incr"].Rules[1], + c.Modules["mod1"].Rules[0], + }}, + {"deep", "data.a.b.c.p.q", []*Rule{ + c.Modules["mod-incr"].Rules[0], + c.Modules["mod-incr"].Rules[1], + c.Modules["mod1"].Rules[0], + }}, + {"too short", "data.a", []*Rule{}}, + {"non-existent", "data.a.deadbeef", []*Rule{}}, + {"non-string/var", "data.a.b[data.foo]", []*Rule{}}, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var ref Ref + switch r := tc.ref.(type) { + case string: + ref = MustParseRef(r) + case Ref: + ref = r + } + rules := c.GetRulesForVirtualDocument(ref) + if len(rules) != len(tc.expected) { + t.Fatalf("Expected exactly %v rules but got: %v", len(tc.expected), rules) + } + for i := range rules { + found := slices.ContainsFunc(tc.expected, rules[i].Equal) + if !found { + t.Fatalf("Expected exactly %v but got: %v", tc.expected, rules) + } + } + }) + } +} + +func TestCompilerGetRulesWithPrefix(t *testing.T) { + mods := getCompilerTestModules() + + // Add incrementally defined rules. + mods["mod-incr"] = module(`package a.b.c + +p contains 1 if { true } +p contains 2 if { true } +q contains 3 if { true }`, + ) + + c := NewCompiler() + c.Compile(mods) + assertNotFailed(t, c) + + tests := []struct { + note string + ref any + expected []*Rule + }{ + {"exact", "data.a.b.c.p", []*Rule{ + c.Modules["mod-incr"].Rules[0], + c.Modules["mod-incr"].Rules[1], + c.Modules["mod1"].Rules[0], + }}, + {"too deep", "data.a.b.c.p.q", []*Rule{}}, + {"prefix", "data.a.b.c", []*Rule{ + c.Modules["mod1"].Rules[0], + c.Modules["mod1"].Rules[1], + c.Modules["mod1"].Rules[2], + c.Modules["mod2"].Rules[0], + c.Modules["mod-incr"].Rules[0], + c.Modules["mod-incr"].Rules[1], + c.Modules["mod-incr"].Rules[2], + }}, + {"non-existent", "data.a.deadbeef", []*Rule{}}, + {"non-string/var", "data.a.b[data.foo]", []*Rule{}}, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var ref Ref + switch r := tc.ref.(type) { + case string: + ref = MustParseRef(r) + case Ref: + ref = r + } + rules := c.GetRulesWithPrefix(ref) + if len(rules) != len(tc.expected) { + t.Fatalf("Expected exactly %v rules but got: %v", len(tc.expected), rules) + } + for i := range rules { + found := slices.ContainsFunc(tc.expected, rules[i].Equal) + if !found { + t.Fatalf("Expected %v but got: %v", tc.expected, rules) + } + } + }) + } +} + +func TestCompilerGetRules(t *testing.T) { + compiler := getCompilerWithParsedModules(map[string]string{ + "mod1": `package a.b.c + +p[x] = y if { q[x] = y } +q["a"] = 1 if { true } +q["b"] = 2 if { true }`, + }) + + compileStages(compiler, nil) + + rule1 := compiler.Modules["mod1"].Rules[0] + rule2 := compiler.Modules["mod1"].Rules[1] + rule3 := compiler.Modules["mod1"].Rules[2] + + tests := []struct { + input string + expected []*Rule + }{ + {"data.a.b.c.p", []*Rule{rule1}}, + {"data.a.b.c.p.x", []*Rule{rule1}}, + {"data.a.b.c.q", []*Rule{rule2, rule3}}, + {"data.a.b.c", []*Rule{rule1, rule2, rule3}}, + {"data.a.b.d", nil}, + } + + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + result := compiler.GetRules(MustParseRef(tc.input)) + + if len(result) != len(tc.expected) { + t.Fatalf("Expected %v but got: %v", tc.expected, result) + } + + for i := range result { + found := slices.ContainsFunc(tc.expected, result[i].Equal) + if !found { + t.Fatalf("Expected %v but got: %v", tc.expected, result) + } + } + }) + } + +} + +func TestCompilerGetRulesDynamic(t *testing.T) { + compiler := getCompilerWithParsedModules(map[string]string{ + "mod1": `package a.b.c.d +r1 = 1`, + "mod2": `package a.b.c.e +default r2 = false +r2 = 2`, + "mod3": `package a.b +r3 = 3`, + "hidden": `package system.hidden +r4 = 4`, + "mod4": `package b.c +r5[x] = 5 if { x := "foo" } +r5.bar = 6 if { input.x } +r5.baz = 7 if { input.y } +`, + }) + + compileStages(compiler, nil) + + rule1 := compiler.Modules["mod1"].Rules[0] + rule2d := compiler.Modules["mod2"].Rules[0] + rule2 := compiler.Modules["mod2"].Rules[1] + rule3 := compiler.Modules["mod3"].Rules[0] + rule4 := compiler.Modules["hidden"].Rules[0] + rule5 := compiler.Modules["mod4"].Rules[0] + rule5b := compiler.Modules["mod4"].Rules[1] + rule5c := compiler.Modules["mod4"].Rules[2] + + tests := []struct { + input string + expected []*Rule + excludeHidden bool + }{ + {input: "data.a.b.c.d.r1", expected: []*Rule{rule1}}, + {input: "data.a.b[x]", expected: []*Rule{rule1, rule2d, rule2, rule3}}, + {input: "data.a.b[x].d", expected: []*Rule{rule1, rule3}}, + {input: "data.a.b.c", expected: []*Rule{rule1, rule2d, rule2}}, + {input: "data.a.b.d"}, + {input: "data", expected: []*Rule{rule1, rule2d, rule2, rule3, rule4, rule5, rule5b, rule5c}}, + {input: "data[x]", expected: []*Rule{rule1, rule2d, rule2, rule3, rule4, rule5, rule5b, rule5c}}, + {input: "data[data.complex_computation].b[y]", expected: []*Rule{rule1, rule2d, rule2, rule3}}, + {input: "data[x][y].c.e", expected: []*Rule{rule2d, rule2}}, + {input: "data[x][y].r3", expected: []*Rule{rule3}}, + {input: "data[x][y]", expected: []*Rule{rule1, rule2d, rule2, rule3, rule5, rule5b, rule5c}, excludeHidden: true}, // old behaviour of GetRulesDynamic + {input: "data.b.c", expected: []*Rule{rule5, rule5b, rule5c}}, + {input: "data.b.c.r5", expected: []*Rule{rule5, rule5b, rule5c}}, + {input: "data.b.c.r5.bar", expected: []*Rule{rule5, rule5b}}, // rule5 might still define a value for the "bar" key + {input: "data.b.c.r5.baz", expected: []*Rule{rule5, rule5c}}, + } + + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + result := compiler.GetRulesDynamicWithOpts( + MustParseRef(tc.input), + RulesOptions{IncludeHiddenModules: !tc.excludeHidden}, + ) + + if len(result) != len(tc.expected) { + t.Fatalf("Expected %v but got: %v", tc.expected, result) + } + + for i := range result { + found := slices.ContainsFunc(tc.expected, result[i].Equal) + if !found { + t.Fatalf("Expected %v but got: %v", tc.expected, result) + } + } + }) + } + +} + +func TestCompileCustomBuiltins(t *testing.T) { + + compiler := NewCompiler().WithBuiltins(map[string]*Builtin{ + "baz": { + Name: "baz", + Decl: types.NewFunction([]types.Type{types.S}, types.A), + }, + "foo.bar": { + Name: "foo.bar", + Decl: types.NewFunction([]types.Type{types.S}, types.A), + }, + }) + + compiler.Compile(map[string]*Module{ + "test.rego": module(` + package test + + p if { baz("x") = x } + q if { foo.bar("x") = x } + `), + }) + + // Ensure no type errors occur. + if compiler.Failed() { + t.Fatal("Unexpected compilation error:", compiler.Errors) + } + + _, err := compiler.QueryCompiler().Compile(MustParseBody(`baz("x") = x; foo.bar("x") = x`)) + if err != nil { + t.Fatal("Unexpected compilation error:", err) + } + + // Ensure type errors occur. + exp1 := `rego_type_error: baz: invalid argument(s)` + exp2 := `rego_type_error: foo.bar: invalid argument(s)` + + _, err = compiler.QueryCompiler().Compile(MustParseBody(`baz(1) = x; foo.bar(1) = x`)) + if err == nil { + t.Fatal("Expected compilation error") + } else if !strings.Contains(err.Error(), exp1) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp1, err) + } else if !strings.Contains(err.Error(), exp2) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp2, err) + } + + compiler.Compile(map[string]*Module{ + "test.rego": module(` + package test + + p if { baz(1) = x } # type error + q if { foo.bar(1) = x } # type error + `), + }) + + assertCompilerErrorStrings(t, compiler, []string{exp1, exp2}) +} + +func TestCompilerLazyLoadingError(t *testing.T) { + + testLoader := func(map[string]*Module) (map[string]*Module, error) { + return nil, errors.New("something went horribly wrong") + } + + compiler := NewCompiler().WithModuleLoader(testLoader) + + compiler.Compile(nil) + + expected := Errors{ + NewError(CompileErr, nil, "something went horribly wrong"), + } + + if !reflect.DeepEqual(expected, compiler.Errors) { + t.Fatalf("Expected error %v but got: %v", expected, compiler.Errors) + } +} + +func TestCompilerLazyLoading(t *testing.T) { + + mod1 := module(`package a.b.c + +import data.x.z1 as z2 + +p = true if { q; r } +q = true if { z2 }`) + orig1 := mod1.Copy() + + mod2 := module(`package a.b.c + +r = true if { true }`) + orig2 := mod2.Copy() + + mod3 := module(`package x + +import data.foo.bar +import input.input + +z1 = true if { [localvar | count(bar.baz.qux, localvar)] }`) + orig3 := mod3.Copy() + + mod4 := module(`package foo.bar.baz + +qux = grault if { true }`) + orig4 := mod4.Copy() + + mod5 := module(`package foo.bar.baz + +import data.d.e.f + +deadbeef = f if { true } +grault = deadbeef if { true }`) + orig5 := mod5.Copy() + + // testLoader will return 4 rounds of parsed modules. + rounds := []map[string]*Module{ + {"mod1": mod1, "mod2": mod2}, + {"mod3": mod3}, + {"mod4": mod4}, + {"mod5": mod5}, + } + + popts := ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true} + + // For each round, run checks. + tests := []func(map[string]*Module){ + func(map[string]*Module) { + // first round, no modules because compiler is invoked with empty + // collection. + }, + func(partial map[string]*Module) { + p := MustParseRuleWithOpts(`p = true { data.a.b.c.q; data.a.b.c.r }`, popts) + if !partial["mod1"].Rules[0].Equal(p) { + t.Errorf("Expected %v but got %v", p, partial["mod1"].Rules[0]) + } + q := MustParseRuleWithOpts(`q = true { data.x.z1 }`, popts) + if !partial["mod1"].Rules[1].Equal(q) { + t.Errorf("Expected %v but got %v", q, partial["mod1"].Rules[0]) + } + }, + func(partial map[string]*Module) { + z1 := MustParseRuleWithOpts(`z1 = true { [localvar | count(data.foo.bar.baz.qux, localvar)] }`, popts) + if !partial["mod3"].Rules[0].Equal(z1) { + t.Errorf("Expected %v but got %v", z1, partial["mod3"].Rules[0]) + } + }, + func(partial map[string]*Module) { + qux := MustParseRuleWithOpts(`qux = grault { true }`, popts) + if !partial["mod4"].Rules[0].Equal(qux) { + t.Errorf("Expected %v but got %v", qux, partial["mod4"].Rules[0]) + } + }, + func(partial map[string]*Module) { + grault := MustParseRuleWithOpts(`qux = data.foo.bar.baz.grault { true }`, popts) // rewrite has not happened yet + f := MustParseRuleWithOpts(`deadbeef = data.d.e.f { true }`, popts) + if !partial["mod4"].Rules[0].Equal(grault) { + t.Errorf("Expected %v but got %v", grault, partial["mod4"].Rules[0]) + } + if !partial["mod5"].Rules[0].Equal(f) { + t.Errorf("Expected %v but got %v", f, partial["mod5"].Rules[0]) + } + }, + } + + round := 0 + + testLoader := func(modules map[string]*Module) (map[string]*Module, error) { + tests[round](modules) + if round >= len(rounds) { + return nil, nil + } + result := rounds[round] + round++ + return result, nil + } + + compiler := NewCompiler().WithModuleLoader(testLoader) + + if compiler.Compile(nil); compiler.Failed() { + t.Fatalf("Got unexpected error from compiler: %v", compiler.Errors) + } + + // Check the original modules are still untouched. + if !mod1.Equal(orig1) || !mod2.Equal(orig2) || !mod3.Equal(orig3) || !mod4.Equal(orig4) || !mod5.Equal(orig5) { + t.Errorf("Compiler lazy loading modified the original modules") + } +} + +func TestCompilerWithMetrics(t *testing.T) { + m := metrics.New() + c := NewCompiler().WithMetrics(m) + mod := MustParseModuleWithOpts(testModule, ParserOptions{AllFutureKeywords: true}) + + c.Compile(map[string]*Module{"testMod": mod}) + assertNotFailed(t, c) + + if len(m.All()) == 0 { + t.Error("Expected to have metrics after compiling") + } +} + +func TestCompilerWithStageAfterWithMetrics(t *testing.T) { + m := metrics.New() + c := NewCompiler().WithStageAfter( + "CheckRecursion", + CompilerStageDefinition{"MockStage", "mock_stage", func(*Compiler) *Error { return nil }}, + ) + + c.WithMetrics(m) + + mod := MustParseModuleWithOpts(testModule, ParserOptions{AllFutureKeywords: true}) + + c.Compile(map[string]*Module{"testMod": mod}) + assertNotFailed(t, c) + + if len(m.All()) == 0 { + t.Error("Expected to have metrics after compiling") + } +} + +func TestCompilerBuildComprehensionIndexKeySet(t *testing.T) { + + type expectedComprehension struct { + term, keys string + } + type exp map[int]expectedComprehension + tests := []struct { + note string + module string + expected exp + wantDebug int + }{ + { + note: "example: invert object", + module: ` + package test + + p if { + value = input[i] + keys = [j | value = input[j]] + } + `, + expected: exp{6: { + term: `[j | value = input[j]]`, + keys: `[value]`, + }}, + wantDebug: 1, + }, + { + note: "example: multiple keys from body", + module: ` + package test + + p if { + v1 = input[i].v1 + v2 = input[i].v2 + keys = [j | v1 = input[j].v1; v2 = input[j].v2] + } + `, + expected: exp{7: { + term: `[j | v1 = input[j].v1; v2 = input[j].v2]`, + keys: `[v1, v2]`, + }}, + wantDebug: 1, + }, + { + note: "example: nested comprehensions are supported", + module: ` + package test + + p = {x: ys | + x = input[i] + ys = {y | x = input[y]} + } + `, + expected: exp{6: { + term: `{y | x = input[y]}`, + keys: `[x]`, + }}, + // there are still things going on here that'll be reported, besides successful indexing + wantDebug: 2, + }, + { + note: "skip: lone comprehensions", + module: ` + package test + + p if { + [v | input[i] = v] # skip because no assignment + }`, + wantDebug: 0, + }, + { + note: "skip: due to with modifier", + module: ` + package test + + p if { + v = input[i] + ks = [j | input[j] = v] with data.x as 1 # skip because of with modifier + }`, + wantDebug: 0, + }, + { + note: "skip: due to negation", + module: ` + package test + + p if { + v = input[i] + a = [] + not a = [j | input[j] = v] # skip due to negation + }`, + wantDebug: 0, + }, + { + note: "skip: due to lack of comprehension", + module: ` + package test + + p if { + v = input[i] + }`, + wantDebug: 0, // nothing interesting to report here + }, + { + note: "skip: due to unsafe comprehension body", + module: ` + package test + + f(x) if { + v = input[i] + ys = [y | y = x[j]] # x is not safe + }`, + wantDebug: 1, + }, + { + note: "skip: due to no candidates", + module: ` + package test + + p if { + ys = [y | y = input[j]] + }`, + wantDebug: 1, + }, + { + note: "mixed: due to nested comprehension containing candidate + indexed nested comprehension with key from rule body", + module: ` + package test + + p if { + x = input[i] # 'x' is a candidate for z (line 7) + y = 2 # 'y' is a candidate for z + z = [1 | + x = data.foo[j] # 'x' is an index key for z + t = [1 | data.bar[k] = y] # 'y' disqualifies indexing of z because it is nested inside a comprehension + ] + } + `, + // Note: no comprehension index for line 7 (`z = [ ...`) + expected: exp{9: { + keys: `[y]`, + term: `[1 | data.bar[k] = y]`, + }}, + wantDebug: 2, + }, + { + note: "skip: avoid increasing runtime (func arg)", + module: ` + package test + + f(x) if { + y = input[x] + ys = [y | y = input[x]] + }`, + wantDebug: 1, + }, + { + note: "skip: avoid increasing runtime (head key)", + module: ` + package test + + p contains x if { + y = input[x] + ys = [y | y = input[x]] + }`, + wantDebug: 1, + }, + { + note: "skip: avoid increasing runtime (walk)", + module: ` + package test + + p contains x if { + y = input.bar[x] + ys = [y | a = input.foo; walk(a, [x, y])] + }`, + wantDebug: 1, + }, + { + note: "bypass: use intermediate var to skip regression check", + module: ` + package test + + p contains x if { + y = input[x] + ys = [y | y = input[z]; z = x] + }`, + expected: exp{6: { + term: ` [y | y = input[z]; z = x]`, + keys: `[x, y]`, + }}, + wantDebug: 1, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + dbg := bytes.Buffer{} + m := metrics.New() + compiler := NewCompiler().WithMetrics(m).WithDebug(&dbg) + mod, err := ParseModuleWithOpts("test.rego", tc.module, ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true}) + if err != nil { + t.Fatal(err) + } + compiler.Compile(map[string]*Module{"test.rego": mod}) + if compiler.Failed() { + t.Fatal(compiler.Errors) + } + + messages := strings.Split(dbg.String(), "\n") + messages = messages[:len(messages)-1] // last one is an empty string + if exp, act := tc.wantDebug, len(messages); exp != act { + t.Errorf("expected %d debug messages, got %d", exp, act) + for i, m := range messages { + t.Logf("%d: %s\n", i, m) + } + } + + n := m.Counter(compileStageComprehensionIndexBuild).Value().(uint64) + if exp, act := len(tc.expected), len(compiler.comprehensionIndices); exp != act { + t.Fatalf("expected %d indices to be built. got: %d", exp, act) + } + if len(tc.expected) == 0 { + return + } + if n == 0 { + t.Fatal("expected counter to be incremented") + } + + for row, exp := range tc.expected { + var comprehension *Term + WalkTerms(compiler.Modules["test.rego"], func(x *Term) bool { + if !IsComprehension(x.Value) { + return true + } + _, ok := tc.expected[x.Location.Row] + if !ok { + return false + } else if comprehension != nil { + t.Fatal("expected at most one comprehension per line in test module") + } + comprehension = x + return false + }) + if comprehension == nil { + t.Fatal("expected comprehension at line:", row) + } + + result := compiler.ComprehensionIndex(comprehension) + if result == nil { + t.Fatal("expected result") + } + + expTerm := MustParseTerm(exp.term) + if !result.Term.Equal(expTerm) { + t.Fatalf("expected term to be %v but got: %v", expTerm, result.Term) + } + + expKeys := MustParseTerm(exp.keys).Value.(*Array) + if NewArray(result.Keys...).Compare(expKeys) != 0 { + t.Fatalf("expected keys to be %v but got: %v", expKeys, result.Keys) + } + } + }) + } +} + +func TestCompilerBuildRequiredCapabilities(t *testing.T) { + tests := []struct { + note string + module string + opts CompileOpts + builtins []string + features []string + keywords []string + }{ + { + note: "trivial v0", + module: ` + package x + + p { input > 7 } + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV0}}, + builtins: []string{"eq", "gt"}, + }, + { + note: "trivial v1", + module: ` + package x + + p if { input > 7 } + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV1}}, + builtins: []string{"eq", "gt"}, + features: []string{"rego_v1"}, + }, + { + note: "rego.v1 import, v0 module", + module: ` + package x + + import rego.v1 + + p if { true } + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV0}}, + features: []string{"rego_v1_import"}, + }, + { + note: "rego.v1 import, v1 module", + module: ` + package x + + import rego.v1 + + p if { true } + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV1}}, + features: []string{"rego_v1"}, + }, + { + note: "rego.v1 import, default rego-version module (v1)", + module: ` + package x + + import rego.v1 + + p if { true } + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV1}}, + features: []string{"rego_v1"}, + }, + { + note: "future.keywords wildcard, v0 module", + module: ` + package x + + import future.keywords + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV0}}, + keywords: []string{"contains", "every", "if", "in"}, + }, + { + note: "future.keywords wildcard, v1 module", + module: ` + package x + + import future.keywords + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV1}}, + features: []string{"rego_v1"}, + }, + { + note: "future.keywords wildcard, default rego-version module (v1)", + module: ` + package x + + import future.keywords + `, + features: []string{"rego_v1"}, + }, + { + note: "future.keywords specific, v0 module", + module: ` + package x + + import future.keywords.in + import future.keywords.if + import future.keywords.contains + import future.keywords.every + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV0}}, + keywords: []string{"contains", "every", "if", "in"}, + }, + { + note: "future.keywords specific, v1 module", + module: ` + package x + + import future.keywords.in + import future.keywords.if + import future.keywords.contains + import future.keywords.every + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV1}}, + features: []string{"rego_v1"}, + }, + { + note: "future.keywords specific, default rego-version module (v1)", + module: ` + package x + + import future.keywords.in + import future.keywords.if + import future.keywords.contains + import future.keywords.every + `, + features: []string{"rego_v1"}, + }, + { + note: "rewriting erases assignment", + module: ` + package x + + p if { a := 7 } + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV1}}, + builtins: []string{"assign", "eq"}, + features: []string{"rego_v1"}, + }, + { + note: "rewriting erases equals", + module: ` + package x + + p if { input == 7 } + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV1}}, + builtins: []string{"eq", "equal"}, + features: []string{"rego_v1"}, + }, + { + note: "rewriting erases print", + module: ` + package x + + p if { print(7) } + `, + opts: CompileOpts{EnablePrintStatements: true, ParserOptions: ParserOptions{RegoVersion: RegoV1}}, + builtins: []string{"eq", "internal.print", "print"}, + features: []string{"rego_v1"}, + }, + + { + note: "rewriting erases print but disabled", + module: ` + package x + + p if { print(7) } + `, + opts: CompileOpts{EnablePrintStatements: false, ParserOptions: ParserOptions{RegoVersion: RegoV1}}, + builtins: []string{"print"}, // only print required because compiler will replace with true + features: []string{"rego_v1"}, + }, + { + note: "dots in the head, v0 module", + module: ` + package x + + a.b.c := 7 + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV0}}, + features: []string{"rule_head_ref_string_prefixes"}, + }, + { + note: "dots in the head, v1 module", + module: ` + package x + + a.b.c := 7 + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV1}}, + features: []string{"rego_v1"}, // rego_v1 includes rule_head_ref_string_prefixes + }, + { + note: "dots in the head, default rego-version module (v1)", + module: ` + package x + + a.b.c := 7 + `, + features: []string{"rego_v1"}, // rego_v1 includes rule_head_ref_string_prefixes + }, + { + note: "dynamic dots in the head, v0 module", + module: ` + package x + + a[x].c[y] := z { x := "b"; y := "c"; z := "d" } + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV0}}, + builtins: []string{"assign", "eq"}, + features: []string{"rule_head_refs"}, + }, + { + note: "dynamic dots in the head, v1 module", + module: ` + package x + + a[x].c[y] := z if { x := "b"; y := "c"; z := "d" } + `, + opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV1}}, + builtins: []string{"assign", "eq"}, + features: []string{"rego_v1"}, // rego_v1 includes rule_head_refs + }, + { + note: "dynamic dots in the head, default rego-version module (v1)", + module: ` + package x + + a[x].c[y] := z if { x := "b"; y := "c"; z := "d" } + `, + builtins: []string{"assign", "eq"}, + features: []string{"rego_v1"}, // rego_v1 includes rule_head_refs + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + compiler := MustCompileModulesWithOpts(map[string]string{ + "test.rego": tc.module, + }, tc.opts) + + var names []string + for i := range compiler.Required.Builtins { + names = append(names, compiler.Required.Builtins[i].Name) + } + + if !slices.Equal(names, tc.builtins) { + t.Fatalf("expected builtins to be %v but got %v", tc.builtins, names) + } + + if !slices.Equal(compiler.Required.FutureKeywords, tc.keywords) { + t.Fatalf("expected keywords to be %v but got %v", tc.keywords, compiler.Required.FutureKeywords) + } + + if !slices.Equal(compiler.Required.Features, tc.features) { + t.Fatalf("expected features to be %v but got %v", tc.features, compiler.Required.Features) + } + }) + } +} + +func TestCompilerAllowMultipleAssignments(t *testing.T) { + + _, err := CompileModules(map[string]string{"test.rego": ` + package test + + p := 7 + p := 8 + `}) + if err != nil { + t.Fatal(err) + } +} + +func TestQueryCompiler(t *testing.T) { + tests := []struct { + note string + q string + pkg string + imports []string + input string + regoVersion RegoVersion + expected any + }{ + { + note: "empty query", + q: " \t \n # foo \n", + expected: errors.New("1 error occurred: rego_compile_error: empty query cannot be compiled"), + }, + { + note: "invalid eq", + q: "eq()", + expected: errors.New("1 error occurred: 1:1: rego_type_error: eq: arity mismatch\n\thave: ()\n\twant: (any, any)"), + }, + { + note: "invalid eq", + q: "eq(1)", + expected: errors.New("1 error occurred: 1:1: rego_type_error: eq: arity mismatch\n\thave: (number)\n\twant: (any, any)"), + }, + { + note: "rewrite assignment", + q: "a := 1; [b, c] := data.foo", + pkg: "", + imports: nil, + expected: "__localq0__ = 1; [__localq1__, __localq2__] = data.foo", + }, + { + note: "exports resolved", + q: "z", + pkg: `package a.b.c`, + imports: nil, + expected: "data.a.b.c.z", + }, + { + note: "imports resolved", + q: "z", + pkg: `package a.b.c.d`, + imports: []string{"import data.a.b.c.z"}, + expected: "data.a.b.c.z", + }, + { + note: "rewrite comprehensions", + q: "[x[i] | a = [[1], [2]]; x = a[j]]", + pkg: "", + imports: nil, + expected: "[__localq0__ | a = [[1], [2]]; x = a[j]; __localq0__ = x[i]]", + }, + { + note: "unsafe vars", + q: "z", + pkg: "", + imports: nil, + expected: errors.New("1 error occurred: 1:1: rego_unsafe_var_error: var z is unsafe"), + }, + { + note: "unsafe var that is a future keyword", + q: "1 in 2", + expected: errors.New("1 error occurred: 1:3: rego_unsafe_var_error: var in is unsafe (hint: `import future.keywords.in` to import a future keyword)"), + regoVersion: RegoV0, + }, + { + note: "unsafe declared var", + q: "[1 | some x; x == 1]", + pkg: "", + imports: nil, + expected: errors.New("1 error occurred: 1:14: rego_unsafe_var_error: var x is unsafe"), + }, + { + note: "safe vars", + q: `data; abc`, + pkg: `package ex`, + imports: []string{"import input.xyz as abc"}, + expected: `data; input.xyz`, + }, + { + note: "reorder", + q: `x != 1; x = 0`, + pkg: "", + imports: nil, + expected: `x = 0; x != 1`, + }, + { + note: "bad with target", + q: "x = 1 with foo.p as null", + pkg: "", + imports: nil, + expected: errors.New("1 error occurred: 1:12: rego_type_error: with keyword target must reference existing input, data, or a function"), + }, + { + note: "rewrite with value", + q: `1 with input as [z]`, + pkg: "package a.b.c", + imports: nil, + expected: `__localq1__ = data.a.b.c.z; __localq0__ = [__localq1__]; 1 with input as __localq0__`, + }, + { + note: "built-in function arity mismatch", + q: `startswith("x")`, + pkg: "", + imports: nil, + expected: errors.New("1 error occurred: 1:1: rego_type_error: startswith: arity mismatch\n\thave: (string)\n\twant: (search: string, base: string)"), + }, + { + note: "built-in function arity mismatch (arity 0)", + q: `x := opa.runtime("foo")`, + pkg: "", + imports: nil, + expected: errors.New("1 error occurred: 1:6: rego_type_error: opa.runtime: arity mismatch\n\thave: (string, ???)\n\twant: ()"), + }, + { + note: "built-in function arity mismatch, nested", + q: "count(sum())", + pkg: "", + imports: nil, + expected: errors.New("1 error occurred: 1:7: rego_type_error: sum: arity mismatch\n\thave: (???)\n\twant: (collection: any)"), + }, + { + note: "check types", + q: "x = data.a.b.c.z; y = null; x = y", + pkg: "", + imports: nil, + expected: errors.New("match error\n\tleft : number\n\tright : null"), + }, + { + note: "undefined function", + q: "data.deadbeef(x)", + expected: errors.New("rego_type_error: undefined function data.deadbeef"), + }, + { + note: "imports resolved without package", + q: "abc", + pkg: "", + imports: []string{"import input.xyz as abc"}, + expected: "input.xyz", + }, + { + note: "void call used as value", + q: "x = print(1)", + expected: errors.New("rego_type_error: print(1) used as value"), + }, + { + note: "print call erasure", + q: `print(1)`, + expected: "true", + }, + } + for _, tc := range tests { + popts := ParserOptions{RegoVersion: tc.regoVersion} + t.Run(tc.note, runQueryCompilerTest(tc.q, popts, tc.pkg, tc.imports, tc.expected)) + } +} + +func TestQueryCompilerRewrittenVars(t *testing.T) { + tests := []struct { + note string + q string + vars map[string]string + }{ + {"assign", "a := 1", map[string]string{"__localq0__": "a"}}, + {"suppress only seen", "b = 1; a := b", map[string]string{"__localq0__": "a"}}, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler() + c.Compile(nil) + assertNotFailed(t, c) + qc := c.QueryCompiler() + body, err := ParseBody(tc.q) + if err != nil { + t.Fatal(err) + } + _, err = qc.Compile(body) + if err != nil { + t.Fatal(err) + } + vars := qc.RewrittenVars() + if len(tc.vars) != len(vars) { + t.Fatalf("Expected %v but got: %v", tc.vars, vars) + } + for k := range vars { + if vars[k] != Var(tc.vars[string(k)]) { + t.Fatalf("Expected %v but got: %v", tc.vars, vars) + } + } + }) + } +} + +func TestQueryCompilerRecompile(t *testing.T) { + + // Query which contains terms that will be rewritten. + parsed := MustParseBody(`a := [1]; data.bar == data.foo[a[0]]`) + parsed0 := parsed + + qc := NewCompiler().QueryCompiler() + compiled, err := qc.Compile(parsed) + if err != nil { + t.Fatal(err) + } + + compiled2, err := qc.Compile(parsed) + if err != nil { + t.Fatal(err) + } + + if !compiled2.Equal(compiled) { + t.Fatalf("Expected same compiled query. Expected: %v, Got: %v", compiled, compiled2) + } + + if !parsed0.Equal(parsed) { + t.Fatalf("Expected parsed query to be unmodified. Expected %v, Got: %v", parsed0, parsed) + } + +} + +func TestQueryCompilerWithMetrics(t *testing.T) { + m := metrics.New() + c := NewCompiler().WithMetrics(m) + c.Compile(getCompilerTestModules()) + assertNotFailed(t, c) + m.Clear() + + qc := c.QueryCompiler() + + query := MustParseBody("a = 1; a > 2") + _, err := qc.Compile(query) + if err != nil { + t.Fatalf("Unexpected error from %v: %v", query, err) + } + + if len(m.All()) == 0 { + t.Error("Expected to have metrics after compiling") + } +} + +func TestQueryCompilerWithStageAfterWithMetrics(t *testing.T) { + m := metrics.New() + c := NewCompiler().WithMetrics(m) + c.Compile(getCompilerTestModules()) + assertNotFailed(t, c) + m.Clear() + + qc := c.QueryCompiler().WithStageAfter( + "CheckSafety", + QueryCompilerStageDefinition{ + "MockStage", + "mock_stage", + func(_ QueryCompiler, b Body) (Body, error) { + return b, nil + }, + }) + + query := MustParseBody("a = 1; a > 2") + _, err := qc.Compile(query) + if err != nil { + t.Fatalf("Unexpected error from %v: %v", query, err) + } + + if len(m.All()) == 0 { + t.Error("Expected to have metrics after compiling") + } +} + +func TestQueryCompilerWithUnsafeBuiltins(t *testing.T) { + tests := []struct { + note string + query string + compiler *Compiler + opts func(QueryCompiler) QueryCompiler + err string + }{ + { + note: "builtin unsafe via compiler", + query: "count([])", + compiler: NewCompiler().WithUnsafeBuiltins(map[string]struct{}{"count": {}}), + err: "unsafe built-in function calls in expression: count", + }, + { + note: "builtin unsafe via query compiler", + query: "count([])", + compiler: NewCompiler(), + opts: func(qc QueryCompiler) QueryCompiler { + return qc.WithUnsafeBuiltins(map[string]struct{}{"count": {}}) + }, + err: "unsafe built-in function calls in expression: count", + }, + { + note: "builtin unsafe via compiler, 'with' mocking", + query: "is_array([]) with is_array as count", + compiler: NewCompiler().WithUnsafeBuiltins(map[string]struct{}{"count": {}}), + err: `with keyword replacing built-in function: target must not be unsafe: "count"`, + }, + { + note: "builtin unsafe via query compiler, 'with' mocking", + query: "is_array([]) with is_array as count", + compiler: NewCompiler(), + opts: func(qc QueryCompiler) QueryCompiler { + return qc.WithUnsafeBuiltins(map[string]struct{}{"count": {}}) + }, + err: `with keyword replacing built-in function: target must not be unsafe: "count"`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + qc := tc.compiler.QueryCompiler() + if tc.opts != nil { + qc = tc.opts(qc) + } + _, err := qc.Compile(MustParseBody(tc.query)) + var errs Errors + if !errors.As(err, &errs) { + t.Fatalf("expected error type %T, got %v %[2]T", errs, err) + } + if exp, act := 1, len(errs); exp != act { + t.Fatalf("expected %d error(s), got %d", exp, act) + } + if exp, act := tc.err, errs[0].Message; exp != act { + t.Errorf("expected message %q, got %q", exp, act) + } + }) + } +} + +func TestQueryCompilerWithDeprecatedBuiltins(t *testing.T) { + cases := []strictnessQueryTestCase{ + { + note: "all() built-in", + query: "all([true, false])", + expectedErrors: errors.New("1 error occurred: 1:1: rego_type_error: deprecated built-in function calls in expression: all"), + }, + { + note: "any() built-in", + query: "any([true, false])", + expectedErrors: errors.New("1 error occurred: 1:1: rego_type_error: deprecated built-in function calls in expression: any"), + }, + } + + runStrictnessQueryTestCase(t, cases) +} + +func TestQueryCompilerWithUnusedAssignedVar(t *testing.T) { + cases := []strictnessQueryTestCase{ + { + note: "array comprehension", + query: "[1 | x := 2]", + expectedErrors: errors.New("1 error occurred: 1:6: rego_compile_error: assigned var x unused"), + }, + { + note: "set comprehension", + query: "{1 | x := 2}", + expectedErrors: errors.New("1 error occurred: 1:6: rego_compile_error: assigned var x unused"), + }, + { + note: "object comprehension", + query: "{1: 2 | x := 2}", + expectedErrors: errors.New("1 error occurred: 1:9: rego_compile_error: assigned var x unused"), + }, + { + note: "every: unused var in body", + query: "every _ in [] { x := 10 }", + expectedErrors: errors.New("1 error occurred: 1:17: rego_compile_error: assigned var x unused"), + }, + } + + runStrictnessQueryTestCase(t, cases) +} + +func TestQueryCompilerCheckKeywordOverrides(t *testing.T) { + cases := []strictnessQueryTestCase{ + { + note: "input assigned", + query: "input := 1", + expectedErrors: errors.New("1 error occurred: 1:1: rego_compile_error: variables must not shadow input (use a different variable name)"), + }, + { + note: "data assigned", + query: "data := 1", + expectedErrors: errors.New("1 error occurred: 1:1: rego_compile_error: variables must not shadow data (use a different variable name)"), + }, + { + note: "nested input assigned", + query: "d := [input | input := 1]", + expectedErrors: errors.New("1 error occurred: 1:15: rego_compile_error: variables must not shadow input (use a different variable name)"), + }, + } + + runStrictnessQueryTestCase(t, cases) +} + +type strictnessQueryTestCase struct { + note string + query string + expectedErrors error +} + +func runStrictnessQueryTestCase(t *testing.T, cases []strictnessQueryTestCase) { + t.Helper() + makeTestRunner := func(tc strictnessQueryTestCase, strict bool) func(t *testing.T) { + return func(t *testing.T) { + c := NewCompiler().WithStrict(strict) + opts := ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true} + result, err := c.QueryCompiler().Compile(MustParseBodyWithOpts(tc.query, opts)) + + if strict { + if err == nil { + t.Fatalf("Expected error from %v but got: %v", tc.query, result) + } + if !strings.Contains(err.Error(), tc.expectedErrors.Error()) { + t.Fatalf("Expected error %v but got: %v", tc.expectedErrors, err) + } + } else if err != nil { + t.Fatalf("Unexpected error from %v: %v", tc.query, err) + } + } + } + + for _, tc := range cases { + t.Run(tc.note+"_strict", makeTestRunner(tc, true)) + t.Run(tc.note+"_non-strict", makeTestRunner(tc, false)) + } +} + +func assertCompilerErrorStrings(t *testing.T, compiler *Compiler, expected []string) { + t.Helper() + result := compilerErrsToStringSlice(compiler.Errors) + + if len(result) != len(expected) { + t.Fatalf("Expected %d:\n%v\nBut got %d:\n%v", len(expected), strings.Join(expected, "\n"), len(result), strings.Join(result, "\n")) + } + for i := range result { + if !strings.Contains(result[i], expected[i]) { + t.Errorf("Expected %v but got: %v", expected[i], result[i]) + } + } +} + +func assertNotFailed(t *testing.T, c *Compiler) { + t.Helper() + if c.Failed() { + t.Fatalf("Unexpected compilation error: %v", c.Errors) + } +} + +func getCompilerWithParsedModules(mods map[string]string) *Compiler { + + parsed := map[string]*Module{} + popts := ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true} + + for id, input := range mods { + mod, err := ParseModuleWithOpts(id, input, popts) + if err != nil { + panic(err) + } + parsed[id] = mod + } + + compiler := NewCompiler() + compiler.Modules = parsed + + return compiler +} + +// helper function to run compiler upto given stage. If nil is provided, a +// normal compile run is performed. +func compileStages(c *Compiler, upto func()) { + + c.init() + + for name := range c.Modules { + c.sorted = append(c.sorted, name) + } + + c.localvargen = newLocalVarGeneratorForModuleSet(c.sorted, c.Modules) + + sort.Strings(c.sorted) + c.SetErrorLimit(0) + + if upto == nil { + c.compile() + return + } + + target := reflect.ValueOf(upto) + + for _, s := range c.stages { + if s.f(); c.Failed() { + return + } + if reflect.ValueOf(s.f).Pointer() == target.Pointer() { + break + } + } +} + +func getCompilerTestModules() map[string]*Module { + + mod1 := MustParseModule(`package a.b.c +import rego.v1 +import data.x.y.z as foo +import data.g.h.k + +p contains x if { q[x]; not r[x] } +q contains x if { foo[i] = x } +z = 400 if { true }`, + ) + + mod2 := MustParseModule(`package a.b.c +import rego.v1 +import data.bar +import data.x.y.p + +r contains x if { bar[x] = 100; p = 101 }`) + + mod3 := MustParseModule(`package a.b.d +import rego.v1 +import input.x as y + +t = true if { input = {y.secret: [{y.keyid}]} } +x = false if { true }`) + + mod4 := MustParseModule(`package a.b.empty`) + + mod5 := MustParseModule(`package a.b.compr +import rego.v1 +import input.x as y +import data.a.b.c.q + +p = true if { [y.a | true] } +r = true if { [q.a | true] } +s = true if { [true | y.a = 0] } +t = true if { [true | q[i] = 1] } +u = true if { [true | _ = [y.a | true]] } +v = true if { [true | _ = [true | q[i] = 1]] } +`, + ) + + mod6 := MustParseModule(`package a.b.nested +import rego.v1 +import data.x +import data.z +import input.x as y + +p = true if { x[y[i].a[z.b[j]]] } +q = true if { x = v; v[y[i]] } +r = 1 if { true } +s = true if { x[r] }`, + ) + + mod7 := MustParseModule(`package a.b.funcs +import rego.v1 +fn(x) = y if { + trim(x, ".", y) +} + +bar([x, y]) = [a, [b, c]] if { + fn(x, a) + y[1].b = b + y[i].a = "hi" + c = y[i].b +} + +foorule = true if { + bar(["hi.there", [{"a": "hi", "b": 1}, {"a": "bye", "b": 0}]], [a, [b, c]]) +}`) + + return map[string]*Module{ + "mod1": mod1, + "mod2": mod2, + "mod3": mod3, + "mod4": mod4, + "mod5": mod5, + "mod6": mod6, + "mod7": mod7, + } +} + +func compilerErrsToStringSlice(errors []*Error) []string { + result := []string{} + for _, e := range errors { + msg := strings.SplitN(e.Error(), ":", 3)[2] + result = append(result, strings.TrimSpace(msg)) + } + sort.Strings(result) + return result +} + +func runQueryCompilerTest(q string, popts ParserOptions, pkg string, imports []string, expected any) func(*testing.T) { + return func(t *testing.T) { + t.Helper() + c := NewCompiler().WithEnablePrintStatements(false) + c.Compile(getCompilerTestModules()) + assertNotFailed(t, c) + qc := c.QueryCompiler() + query := MustParseBodyWithOpts(q, popts) + var qctx *QueryContext + + if pkg != "" { + qctx = qctx.WithPackage(MustParsePackage(pkg)) + } + if len(imports) != 0 { + qctx = qctx.WithImports(MustParseImports(strings.Join(imports, "\n"))) + } + + if qctx != nil { + qc.WithContext(qctx) + } + + switch expected := expected.(type) { + case string: + expectedQuery := MustParseBody(expected) + result, err := qc.Compile(query) + if err != nil { + t.Fatalf("Unexpected error from %v: %v", query, err) + } + if !expectedQuery.Equal(result) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expectedQuery, result) + } + case error: + result, err := qc.Compile(query) + if err == nil { + t.Fatalf("Expected error from %v but got: %v", query, result) + } + if !strings.Contains(err.Error(), expected.Error()) { + t.Fatalf("Expected error %v but got: %v", expected, err) + } + } + } +} + +func TestCompilerCapabilitiesFeatures(t *testing.T) { + cases := []struct { + note string + module string + features []string + expectedErr string + }{ + { + note: "no features, no ref-head rules", + module: `package test + p := 42`, + }, + { + note: "no features, ref-head rule", + module: `package test + p.q.r := 42`, + expectedErr: "rego_compile_error: rule heads with refs are not supported: p.q.r", + }, + { + note: "no features, general-ref-head rule", + module: `package test + p[q].r[s] := 42 if { q := "foo"; s := "bar" }`, + expectedErr: "rego_compile_error: rule heads with refs are not supported: p[q].r[s]", + }, + { + note: "string-prefix-ref-head feature, no ref-head rules", + features: []string{ + FeatureRefHeadStringPrefixes, + }, + module: `package test + p := 42`, + }, + { + note: "string-prefix-ref-head feature, ref-head rule", + features: []string{ + FeatureRefHeadStringPrefixes, + }, + module: `package test + p.q.r := 42`, + }, + { + note: "ref-head feature, ref-head rule", + features: []string{ + FeatureRefHeads, + }, + module: `package test + p.q.r := 42`, + }, + { + note: "rego-v1 feature, ref-head rule", + features: []string{ + FeatureRegoV1, + }, + module: `package test + p.q.r := 42`, + }, + { + note: "string-prefix-ref-head feature, general-ref-head rule", + features: []string{ + FeatureRefHeadStringPrefixes, + }, + module: `package test + p[q].r[s] := 42 if { q := "foo"; s := "bar" }`, + expectedErr: "rego_type_error: rule heads with general refs (containing variables) are not supported: p[q].r[s]", + }, + { + note: "ref-head feature, general-ref-head rule", + features: []string{ + FeatureRefHeads, + }, + module: `package test + p[q].r[s] := 42 if { q := "foo"; s := "bar" }`, + }, + { + note: "rego-v1 feature, general-ref-head rule", + features: []string{ + FeatureRegoV1, + }, + module: `package test + p[q].r[s] := 42 if { q := "foo"; s := "bar" }`, + }, + { + note: "string-prefix-ref-head & ref-head features, general-ref-head rule", + features: []string{ + FeatureRefHeadStringPrefixes, + FeatureRefHeads, + }, + module: `package test + p[q].r[s] := 42 if { q := "foo"; s := "bar" }`, + }, + { + note: "string-prefix-ref-head & ref-head & rego-v1 features, general-ref-head rule", + features: []string{ + FeatureRefHeadStringPrefixes, + FeatureRefHeads, + FeatureRegoV1, + }, + module: `package test + p[q].r[s] := 42 if { q := "foo"; s := "bar" }`, + }, + { + note: "string-prefix-ref-head & ref-head features, ref-head rule", + features: []string{ + FeatureRefHeadStringPrefixes, + FeatureRefHeads, + }, + module: `package test + p.q.r := 42`, + }, + { + note: "string-prefix-ref-head & ref-head & rego-v1 features, ref-head rule", + features: []string{ + FeatureRefHeadStringPrefixes, + FeatureRefHeads, + FeatureRegoV1, + }, + module: `package test + p.q.r := 42`, + }, + { + note: "no features, string-prefix-ref-head with contains kw", + features: []string{}, + module: `package test + import future.keywords.contains + p.x contains 1`, + expectedErr: "rego_compile_error: rule heads with refs are not supported: p.x", + }, + { + note: "string-prefix-ref-head feature, string-prefix-ref-head with contains kw", + features: []string{ + FeatureRefHeadStringPrefixes, + }, + module: `package test + import future.keywords.contains + p.x contains 1`, + }, + { + note: "ref-head feature, string-prefix-ref-head with contains kw", + features: []string{ + FeatureRefHeads, + }, + module: `package test + import future.keywords.contains + p.x contains 1`, + }, + { + note: "rego-v1 feature, string-prefix-ref-head with contains kw", + features: []string{ + FeatureRegoV1, + }, + module: `package test + import future.keywords.contains + p.x contains 1`, + }, + + { + note: "no features, general-ref-head with contains kw", + features: []string{}, + module: `package test + import future.keywords + p[x] contains 1 if x = "foo"`, + expectedErr: "rego_compile_error: rule heads with refs are not supported: p[x]", + }, + { + note: "string-prefix-ref-head feature, general-ref-head with contains kw", + features: []string{ + FeatureRefHeadStringPrefixes, + }, + module: `package test + import future.keywords + p[x] contains 1 if x = "foo"`, + expectedErr: "rego_type_error: rule heads with general refs (containing variables) are not supported: p[x]", + }, + { + note: "ref-head feature, general-ref-head with contains kw", + features: []string{ + FeatureRefHeads, + }, + module: `package test + import future.keywords + p[x] contains 1 if x = "foo"`, + }, + { + note: "rego-v1 feature, general-ref-head with contains kw", + features: []string{ + FeatureRegoV1, + }, + module: `package test + import future.keywords + p[x] contains 1 if x = "foo"`, + }, + + { + note: "no features, rego.v1 import", + module: `package test + import rego.v1 + p if { true }`, + expectedErr: "rego_compile_error: rego.v1 import is not supported", + }, + { + note: "rego-v1-import feature, rego.v1 import", + module: `package test + import rego.v1 + p if { true }`, + features: []string{ + FeatureRegoV1Import, + }, + }, + { + note: "rego-v1-import feature, rego.v1 import", + module: `package test + import rego.v1 + p if { true }`, + features: []string{ + FeatureRegoV1, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + capabilities := CapabilitiesForThisVersion() + capabilities.Features = tc.features + + // Modules are parsed with full set of capabilities + mod := module(tc.module) + + compiler := NewCompiler().WithCapabilities(capabilities) + compiler.Compile(map[string]*Module{"test": mod}) + if tc.expectedErr != "" { + if !compiler.Failed() { + t.Fatal("expected error but got success") + } + if !strings.Contains(compiler.Errors.Error(), tc.expectedErr) { + t.Fatalf("expected error:\n\n%s\n\nbut got:\n\n%v", tc.expectedErr, compiler.Errors) + } + } else if compiler.Failed() { + t.Fatalf("unexpected error(s): %v", compiler.Errors) + } + }) + } +} + +func TestCompilerCapabilitiesExtendedWithCustomBuiltins(t *testing.T) { + + compiler := NewCompiler().WithCapabilities(&Capabilities{ + Builtins: []*Builtin{ + { + Name: "foo", + Decl: types.NewFunction([]types.Type{types.N}, types.B), + }, + }, + }).WithBuiltins(map[string]*Builtin{ + "bar": { + Name: "bar", + Decl: types.NewFunction([]types.Type{types.N}, types.B), + }, + }) + + module1 := module(`package test + + p if { foo(1); bar(2) }`) + module2 := module(`package test + + p if { plus(1,2,x) }`) + + compiler.Compile(map[string]*Module{"x": module1}) + if compiler.Failed() { + t.Fatal("unexpected error:", compiler.Errors) + } + + compiler.Compile(map[string]*Module{"x": module2}) + if !compiler.Failed() { + t.Fatal("expected error but got success") + } + +} + +func TestCompilerWithUnsafeBuiltins(t *testing.T) { + // Rego includes a number of built-in functions. In some cases, you may not + // want all builtins to be available to a program. This test shows how to + // mark a built-in as unsafe. + compiler := NewCompiler().WithUnsafeBuiltins(map[string]struct{}{"re_match": {}}) + + // This query should not compile because the `re_match` built-in is no + // longer available. + _, err := compiler.QueryCompiler().Compile(MustParseBody(`re_match("a", "a")`)) + if err == nil { + t.Fatalf("Expected error for unsafe built-in") + } else if !strings.Contains(err.Error(), "unsafe built-in function") { + t.Fatalf("Expected error for unsafe built-in but got %v", err) + } + + // These modules should not compile for the same reason. + modules := map[string]*Module{"mod1": module(`package a.b.c +deny if { + re_match(input.user, ".*bob.*") +}`)} + compiler.Compile(modules) + if !compiler.Failed() { + t.Fatalf("Expected error for unsafe built-in") + } else if !strings.Contains(compiler.Errors[0].Error(), "unsafe built-in function") { + t.Fatalf("Expected error for unsafe built-in but got %v", err) + } +} + +func TestCompilerPassesTypeCheck(t *testing.T) { + c := NewCompiler(). + WithCapabilities(&Capabilities{Builtins: []*Builtin{Split}}) + // Must compile to initialize type environment after WithCapabilities + c.Compile(nil) + if c.PassesTypeCheck(MustParseBody(`a = input.a; split(a, ":", x); a0 = x[0]; a0 = null`)) { + t.Fatal("Did not successfully detect a type-checking violation") + } +} + +func TestCompilerPassesTypeCheckRules(t *testing.T) { + inputSchema := `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "OPA Authorization Policy Schema", + "type": "object", + "properties": { + "identity": { + "type": "string" + }, + "path": { + "type": "array", + "items": {} + }, + "params": { + "type": "object" + } + }, + "required": [ + "identity", + "path", + "params" + ] +}` + + ischema := util.MustUnmarshalJSON([]byte(inputSchema)) + + module1 := ` +package policy + +default allow := false + +allow if { + input.identity = "foo" +} + +allow if { + input.path = ["foo", "bar"] +} + +allow if { + input.params = {"foo": "bar"} +}` + + module2 := ` +package policy + +default allow := false + +allow if { + input.identty = "foo" +}` + + module3 := ` +package policy + +default allow := false + +allow if { + input.path = "foo" +}` + + module4 := ` +package policy + +default allow := false + +allow if { + input.identty = "foo" +} + +allow if { + input.path = "foo" +}` + + schemaSet := NewSchemaSet() + schemaSet.Put(SchemaRootRef, ischema) + + tests := []struct { + note string + modules []string + errs []string + }{ + {note: "no error", modules: []string{module1}}, + {note: "typo", modules: []string{module2}, errs: []string{"undefined ref: input.identty"}}, + {note: "wrong type", modules: []string{module3}, errs: []string{"match error"}}, + {note: "multiple errors", modules: []string{module4}, errs: []string{"match error", "undefined ref: input.identty"}}, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var elems []*Rule + + for i, module := range tc.modules { + mod, err := ParseModuleWithOpts(fmt.Sprintf("test%d.rego", i+1), module, ParserOptions{ + ProcessAnnotation: true, + AllFutureKeywords: true, + unreleasedKeywords: true, + }) + if err != nil { + t.Fatal(err) + } + + for _, rule := range mod.Rules { + elems = append(elems, rule) + for next := rule.Else; next != nil; next = next.Else { + elems = append(elems, next) + } + } + } + + errs := NewCompiler().WithSchemas(schemaSet).PassesTypeCheckRules(elems) + + if len(errs) > 0 { + if len(tc.errs) == 0 { + t.Fatalf("Unexpected error: %v", errs) + } + + result := compilerErrsToStringSlice(errs) + + if len(result) != len(tc.errs) { + t.Fatalf("Expected %d:\n%v\nBut got %d:\n%v", len(tc.errs), strings.Join(tc.errs, "\n"), len(result), strings.Join(result, "\n")) + } + + for i := range result { + if !strings.Contains(result[i], tc.errs[i]) { + t.Errorf("Expected %v but got: %v", tc.errs[i], result[i]) + } + } + } else if len(tc.errs) > 0 { + t.Fatalf("Expected error %q but got success", tc.errs) + } + }) + } +} + +func TestCompilerPassesTypeCheckNegative(t *testing.T) { + c := NewCompiler(). + WithCapabilities(&Capabilities{Builtins: []*Builtin{Split, StartsWith}}) + // Must compile to initialize type environment after WithCapabilities + c.Compile(nil) + if !c.PassesTypeCheck(MustParseBody(`a = input.a; split(a, ":", x); a0 = x[0]; startswith(a0, "foo", true)`)) { + t.Fatal("Incorrectly detected a type-checking violation") + } +} + +func TestKeepModules(t *testing.T) { + + t.Run("no keep", func(t *testing.T) { + c := NewCompiler() // no keep is default + + // This one is overwritten by c.Compile() + c.Modules["foo.rego"] = MustParseModule("package foo\np = true") + + c.Compile(map[string]*Module{"bar.rego": MustParseModule("package bar\np = input")}) + + if len(c.Errors) != 0 { + t.Fatalf("expected no error; got %v", c.Errors) + } + + mods := c.ParsedModules() + if mods != nil { + t.Errorf("expected ParsedModules == nil, got %v", mods) + } + }) + + t.Run("keep", func(t *testing.T) { + + c := NewCompiler().WithKeepModules(true) + + // This one is overwritten by c.Compile() + c.Modules["foo.rego"] = MustParseModule("package foo\np = true") + + c.Compile(map[string]*Module{"bar.rego": MustParseModule("package bar\np = input")}) + if len(c.Errors) != 0 { + t.Fatalf("expected no error; got %v", c.Errors) + } + + mods := c.ParsedModules() + if exp, act := 1, len(mods); exp != act { + t.Errorf("expected %d modules, found %d: %v", exp, act, mods) + } + for k := range mods { + if k != "bar.rego" { + t.Errorf("unexpected key: %v, want 'bar.rego'", k) + } + } + + for k := range mods { + compiled := c.Modules[k] + if compiled.Equal(mods[k]) { + t.Errorf("expected module %v to not be compiled: %v", k, mods[k]) + } + } + + // expect ParsedModules to be reset + c.Compile(map[string]*Module{"baz.rego": MustParseModule("package baz\np = input")}) + mods = c.ParsedModules() + if exp, act := 1, len(mods); exp != act { + t.Errorf("expected %d modules, found %d: %v", exp, act, mods) + } + for k := range mods { + if k != "baz.rego" { + t.Errorf("unexpected key: %v, want 'baz.rego'", k) + } + } + + for k := range mods { + compiled := c.Modules[k] + if compiled.Equal(mods[k]) { + t.Errorf("expected module %v to not be compiled: %v", k, mods[k]) + } + } + + // expect ParsedModules to be reset to nil + c = c.WithKeepModules(false) + c.Compile(map[string]*Module{"baz.rego": MustParseModule("package baz\np = input")}) + mods = c.ParsedModules() + if mods != nil { + t.Errorf("expected ParsedModules == nil, got %v", mods) + } + }) + + t.Run("no copies", func(t *testing.T) { + extra := MustParseModule("package extra\np = input") + done := false + testLoader := func(map[string]*Module) (map[string]*Module, error) { + if done { + return nil, nil + } + done = true + return map[string]*Module{"extra.rego": extra}, nil + } + + c := NewCompiler().WithModuleLoader(testLoader).WithKeepModules(true) + + mod := MustParseModule("package bar\np = input") + c.Compile(map[string]*Module{"bar.rego": mod}) + if len(c.Errors) != 0 { + t.Fatalf("expected no error; got %v", c.Errors) + } + + mods := c.ParsedModules() + if exp, act := 2, len(mods); exp != act { + t.Errorf("expected %d modules, found %d: %v", exp, act, mods) + } + newName := Var("q") + mods["bar.rego"].Rules[0].Head.Name = newName + if exp, act := newName, mod.Rules[0].Head.Name; !exp.Equal(act) { + t.Errorf("expected modified rule name %v, found %v", exp, act) + } + mods["extra.rego"].Rules[0].Head.Name = newName + if exp, act := newName, extra.Rules[0].Head.Name; !exp.Equal(act) { + t.Errorf("expected modified rule name %v, found %v", exp, act) + } + }) + + t.Run("keep, with loader", func(t *testing.T) { + extra := MustParseModule("package extra\np = input") + done := false + testLoader := func(map[string]*Module) (map[string]*Module, error) { + if done { + return nil, nil + } + done = true + return map[string]*Module{"extra.rego": extra}, nil + } + + c := NewCompiler().WithModuleLoader(testLoader).WithKeepModules(true) + + // This one is overwritten by c.Compile() + c.Modules["foo.rego"] = MustParseModule("package foo\np = true") + + c.Compile(map[string]*Module{"bar.rego": MustParseModule("package bar\np = input")}) + + if len(c.Errors) != 0 { + t.Fatalf("expected no error; got %v", c.Errors) + } + + mods := c.ParsedModules() + if exp, act := 2, len(mods); exp != act { + t.Errorf("expected %d modules, found %d: %v", exp, act, mods) + } + for k := range mods { + if k != "bar.rego" && k != "extra.rego" { + t.Errorf("unexpected key: %v, want 'extra.rego' and 'bar.rego'", k) + } + } + + for k := range mods { + compiled := c.Modules[k] + if compiled.Equal(mods[k]) { + t.Errorf("expected module %v to not be compiled: %v", k, mods[k]) + } + } + }) +} + +// see https://github.com/open-policy-agent/opa/issues/5166 +func TestCompilerWithRecursiveSchema(t *testing.T) { + + jsonSchema := `{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/open-policy-agent/opa/issues/5166", + "type": "object", + "properties": { + "Something": { + "$ref": "#/$defs/X" + } + }, + "$defs": { + "X": { + "type": "object", + "properties": { + "Name": { "type": "string" }, + "Y": { + "$ref": "#/$defs/Y" + } + } + }, + "Y": { + "type": "object", + "properties": { + "X": { + "$ref": "#/$defs/X" + } + } + } + } +}` + + exampleModule := `# METADATA +# schemas: +# - input: schema.input +package opa.recursion + +deny if { + input.Something.Y.X.Name == "Something" +} +` + + c := NewCompiler() + var schema any + if err := json.Unmarshal([]byte(jsonSchema), &schema); err != nil { + t.Fatal(err) + } + schemaSet := NewSchemaSet() + schemaSet.Put(MustParseRef("schema.input"), schema) + c.WithSchemas(schemaSet) + + m := MustParseModuleWithOpts(exampleModule, ParserOptions{ + ProcessAnnotation: true, + AllFutureKeywords: true, + unreleasedKeywords: true, + }) + c.Compile(map[string]*Module{"testMod": m}) + if c.Failed() { + t.Errorf("Expected compilation to succeed, but got errors: %v", c.Errors) + } +} + +// see https://github.com/open-policy-agent/opa/issues/5166 +func TestCompilerWithRecursiveSchemaAndInvalidSource(t *testing.T) { + + jsonSchema := `{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/open-policy-agent/opa/issues/5166", + "type": "object", + "properties": { + "Something": { + "$ref": "#/$defs/X" + } + }, + "$defs": { + "X": { + "type": "object", + "properties": { + "Name": { "type": "string" }, + "Y": { + "$ref": "#/$defs/Y" + } + } + }, + "Y": { + "type": "object", + "properties": { + "X": { + "$ref": "#/$defs/X" + } + } + } + } +}` + + exampleModule := `# METADATA +# schemas: +# - input: schema.input +package opa.recursion + +deny if { + input.Something.Y.X.ThisDoesNotExist == "Something" +} +` + + c := NewCompiler(). + WithUseTypeCheckAnnotations(true) + var schema any + if err := json.Unmarshal([]byte(jsonSchema), &schema); err != nil { + t.Fatal(err) + } + schemaSet := NewSchemaSet() + schemaSet.Put(MustParseRef("schema.input"), schema) + c.WithSchemas(schemaSet) + + m := MustParseModuleWithOpts(exampleModule, ParserOptions{ + ProcessAnnotation: true, + AllFutureKeywords: true, + unreleasedKeywords: true, + }) + c.Compile(map[string]*Module{"testMod": m}) + if !c.Failed() { + t.Errorf("Expected compilation to fail, but it succeeded") + } else if !strings.HasPrefix(c.Errors.Error(), "1 error occurred: 7:2: rego_type_error: undefined ref: input.Something.Y.X.ThisDoesNotExist") { + t.Errorf("unexpected error: %v", c.Errors.Error()) + } +} + +func modules(ms ...string) []*Module { + opts := ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true} + mods := make([]*Module, len(ms)) + for i, m := range ms { + var err error + mods[i], err = ParseModuleWithOpts(fmt.Sprintf("mod%d.rego", i), m, opts) + if err != nil { + panic(err) + } + } + return mods +} + +// FIXME(v1-test-refactor): In OPA 1.0, a call to here can be replaced with a call to MustParseModule. +func module(raw string, opts ...func(ParserOptions) ParserOptions) *Module { + popts := ParserOptions{ + AllFutureKeywords: true, + unreleasedKeywords: true, + } + + for _, opt := range opts { + popts = opt(popts) + } + + return MustParseModuleWithOpts(raw, popts) +} + +func TestCompilerWithRecursiveSchemaAvoidRace(t *testing.T) { + + jsonSchema := `{ + "type": "object", + "properties": { + "aws": { + "type": "object", + "$ref": "#/$defs/example.pkg.providers.aws.AWS" + } + }, + "$defs": { + "example.pkg.providers.aws.AWS": { + "type": "object", + "properties": { + "iam": { + "type": "object", + "$ref": "#/$defs/example.pkg.providers.aws.iam.IAM" + }, + "sqs": { + "type": "object", + "$ref": "#/$defs/example.pkg.providers.aws.sqs.SQS" + } + } + }, + "example.pkg.providers.aws.iam.Document": { + "type": "object" + }, + "example.pkg.providers.aws.iam.IAM": { + "type": "object", + "properties": { + "policies": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/$defs/example.pkg.providers.aws.iam.Policy" + } + } + } + }, + "example.pkg.providers.aws.iam.Policy": { + "type": "object", + "properties": { + "builtin": { + "type": "object", + "properties": { + "value": { + "type": "boolean" + } + } + }, + "document": { + "type": "object", + "$ref": "#/$defs/example.pkg.providers.aws.iam.Document" + } + } + }, + "example.pkg.providers.aws.sqs.Queue": { + "type": "object", + "properties": { + "policies": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/$defs/example.pkg.providers.aws.iam.Policy" + } + } + } + }, + "example.pkg.providers.aws.sqs.SQS": { + "type": "object", + "properties": { + "queues": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/$defs/example.pkg.providers.aws.sqs.Queue" + } + } + } + } + } +}` + + exampleModule := `# METADATA +# schemas: +# - input: schema.input +package race.condition + +deny if { + queue := input.aws.sqs.queues[_] + policy := queue.policies[_] + doc := json.unmarshal(policy.document.value) + statement = doc.Statement[_] + action := statement.Action[_] + action == "*" +} +` + + c := NewCompiler() + var schema any + if err := json.Unmarshal([]byte(jsonSchema), &schema); err != nil { + t.Fatal(err) + } + schemaSet := NewSchemaSet() + schemaSet.Put(MustParseRef("schema.input"), schema) + c.WithSchemas(schemaSet) + + m := MustParseModuleWithOpts(exampleModule, ParserOptions{ + ProcessAnnotation: true, + AllFutureKeywords: true, + unreleasedKeywords: true, + }) + c.Compile(map[string]*Module{"testMod": m}) + if c.Failed() { + t.Fatal(c.Errors) + } +} + +func TestCompilerRewriteTestRulesForTracing(t *testing.T) { + tests := []struct { + note string + rewrite bool + module string + exp string + }{ + { + note: "ref comparison, no rewrite", + module: `package test + +a := 1 +b := 2 + +test_something if { + a == b +}`, + exp: `package test + +a := 1 if { true } +b := 2 if { true } + +test_something = true if { + data.test.a = data.test.b +}`, + }, + { + note: "ref comparison, rewrite", + rewrite: true, + module: `package test + +a := 1 +b := 2 + +test_something if { + a == b +}`, + // When the test fails on '__local0__ = __local1__', the values for 'a' and 'b' are captured in local bindings, + // accessible by the tracer. + exp: `package test + +a := 1 if { true } +b := 2 if { true } + +test_something = true if { + __local0__ = data.test.a + __local1__ = data.test.b + __local0__ = __local1__ +}`, + }, + { + note: "ref comparison, not-stmt, rewrite", + rewrite: true, + module: `package test + +a := 1 +b := 2 + +test_something if { + not a == b +}`, + // We don't break out local vars from a not-stmt, as that would change the semantics of the rule. + exp: `package test + +a := 1 if { true } +b := 2 if { true } + +test_something = true if { + not data.test.a = data.test.b +}`, + }, + { + note: "ref comparison, inside every-stmt, no rewrite", + module: `package test + +a := 1 +b := 2 +l := [1, 2, 3] + +test_something if { + every x in l { + a < b + x + } +}`, + exp: `package test + +a := 1 if { true } +b := 2 if { true } +l := [1, 2, 3] if { true } + +test_something = true if { + __local2__ = data.test.l + every __local0__, __local1__ in __local2__ { + __local4__ = data.test.b + plus(__local4__, __local1__, __local3__) + __local5__ = data.test.a + lt(__local5__, __local3__) + } +}`, + }, + { + note: "ref comparison, inside every-stmt, rewrite", + rewrite: true, + module: `package test + +a := 1 +b := 2 +l := [1, 2, 3] + +test_something if { + every x in l { + a < b + x + } +}`, + // When tests contain an 'every' statement, we're interested in the circumstances that made the every fail, + // so it's body is rewritten. + exp: `package test + +a := 1 if { true } +b := 2 if { true } +l := [1, 2, 3] if { true } + +test_something = true if { + __local2__ = data.test.l; + every __local0__, __local1__ in __local2__ { + __local4__ = data.test.b + plus(__local4__, __local1__, __local3__) + __local5__ = data.test.a + lt(__local5__, __local3__) + } +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ms := map[string]string{ + "test.rego": tc.module, + } + c := getCompilerWithParsedModules(ms). + WithRewriteTestRules(tc.rewrite) + + compileStages(c, c.rewriteTestRuleEqualities) + assertNotFailed(t, c) + + result := c.Modules["test.rego"] + exp := module(tc.exp) + exp.Imports = nil // We strip the imports since the compiler will too + if result.Compare(exp) != 0 { + t.Fatalf("\nExpected:\n\n%v\n\nGot:\n\n%v", exp, result) + } + }) + } +} + +func TestCompile_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + modules map[string]*Module + expErrs Errors + }{ + { + note: "no module rego-version, no v1 violations", + modules: map[string]*Module{ + "test": { + Package: MustParsePackage(`package test`), + Imports: MustParseImports(`import data.foo + import data.bar`), + }, + }, + }, + { + note: "no module rego-version, v1 violations", // default is v1, errors expected + modules: map[string]*Module{ + "test": { + Package: MustParsePackage(`package test`), + Imports: MustParseImports(`import data.foo + import data.bar as foo`), + }, + }, + expErrs: Errors{ + &Error{ + Code: CompileErr, + Message: "import must not shadow import data.foo", + }, + }, + }, + { + note: "v0 module, v1 violations", + modules: map[string]*Module{ + "test": MustParseModuleWithOpts(`package test + import data.foo + import data.bar as foo`, + ParserOptions{RegoVersion: RegoV0}), + }, + }, + { + note: "v1 module, v1 violations", + modules: map[string]*Module{ + "test": MustParseModuleWithOpts(`package test + import data.foo + import data.bar as foo`, + ParserOptions{RegoVersion: RegoV1}), + }, + expErrs: Errors{ + &Error{ + Code: CompileErr, + Message: "import must not shadow import data.foo", + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + compiler := NewCompiler() + + compiler.Compile(tc.modules) + + if len(tc.expErrs) > 0 { + assertErrors(t, compiler.Errors, tc.expErrs, false) + } else if len(compiler.Errors) > 0 { + t.Fatalf("Unexpected errors: %v", compiler.Errors) + } + }) + } +} + +func TestCompilerInitWithDefaultModuleLoader(t *testing.T) { + // Reset the global variable after the test + defer func() { defaultModuleLoader = nil }() + + // a dummy loader that adds "foo" + loader1 := func(res map[string]*Module) (map[string]*Module, error) { + mod := MustParseModule(`package foo`) + resCopy := map[string]*Module{} + maps.Copy(resCopy, res) + resCopy["foo.rego"] = mod + return resCopy, nil + } + + // a dummy loader that adds "bar" + loader2 := func(res map[string]*Module) (map[string]*Module, error) { + mod := MustParseModule(`package bar`) + resCopy := map[string]*Module{} + maps.Copy(resCopy, res) + resCopy["bar.rego"] = mod + return resCopy, nil + } + + DefaultModuleLoader(loader2) + + c := NewCompiler().WithModuleLoader(loader1) + c.init() + + got, err := c.moduleLoader(make(map[string]*Module)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + expected := map[string]*Module{ + "foo.rego": MustParseModule(`package foo`), + "bar.rego": MustParseModule(`package bar`), + } + // check both modules are present + for k, v := range expected { + gotMod, ok := got[k] + if !ok { + t.Errorf("expected key %q in result", k) + continue + } + if !reflect.DeepEqual(gotMod, v) { + t.Errorf("unexpected module for %q: got %v want %v", k, gotMod, v) + } + } + + // Now, test defaultModuleLoader only + c2 := NewCompiler() + c2.init() + got2, err := c2.moduleLoader(make(map[string]*Module)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if _, ok := got2["bar.rego"]; !ok { + t.Error("expected bar.rego from defaultModuleLoader in result") + } +} diff --git a/third_party/opa/v1/ast/compilehelper.go b/third_party/opa/v1/ast/compilehelper.go new file mode 100644 index 000000000000..7d81d45e6d25 --- /dev/null +++ b/third_party/opa/v1/ast/compilehelper.go @@ -0,0 +1,62 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +// CompileModules takes a set of Rego modules represented as strings and +// compiles them for evaluation. The keys of the map are used as filenames. +func CompileModules(modules map[string]string) (*Compiler, error) { + return CompileModulesWithOpt(modules, CompileOpts{}) +} + +// CompileOpts defines a set of options for the compiler. +type CompileOpts struct { + EnablePrintStatements bool + ParserOptions ParserOptions +} + +// CompileModulesWithOpt takes a set of Rego modules represented as strings and +// compiles them for evaluation. The keys of the map are used as filenames. +func CompileModulesWithOpt(modules map[string]string, opts CompileOpts) (*Compiler, error) { + + parsed := make(map[string]*Module, len(modules)) + + for f, module := range modules { + var pm *Module + var err error + if pm, err = ParseModuleWithOpts(f, module, opts.ParserOptions); err != nil { + return nil, err + } + parsed[f] = pm + } + + compiler := NewCompiler(). + WithDefaultRegoVersion(opts.ParserOptions.RegoVersion). + WithEnablePrintStatements(opts.EnablePrintStatements) + compiler.Compile(parsed) + + if compiler.Failed() { + return nil, compiler.Errors + } + + return compiler, nil +} + +// MustCompileModules compiles a set of Rego modules represented as strings. If +// the compilation process fails, this function panics. +func MustCompileModules(modules map[string]string) *Compiler { + return MustCompileModulesWithOpts(modules, CompileOpts{}) +} + +// MustCompileModulesWithOpts compiles a set of Rego modules represented as strings. If +// the compilation process fails, this function panics. +func MustCompileModulesWithOpts(modules map[string]string, opts CompileOpts) *Compiler { + + compiler, err := CompileModulesWithOpt(modules, opts) + if err != nil { + panic(err) + } + + return compiler +} diff --git a/third_party/opa/v1/ast/compilehelper_test.go b/third_party/opa/v1/ast/compilehelper_test.go new file mode 100644 index 000000000000..0e50f8912e1a --- /dev/null +++ b/third_party/opa/v1/ast/compilehelper_test.go @@ -0,0 +1,232 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "strings" + "testing" +) + +func TestCompileModules_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + modules map[string]string + expErrs []string + }{ + // NOT default rego-version + { + note: "v0 module, no v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + p[x] { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0 module, v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import data.foo + import data.bar as foo + + p[x] { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:5: rego_parse_error: `if` keyword is required before rule body", + "test.rego:5: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + + // cross-rego-version + { + note: "rego.v1 import, no v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import rego.v1 + + p contains x if { + x = "a" + }`, + }, + }, + { + note: "rego.v1 import, v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import rego.v1 + + import data.foo + import data.bar as foo + + p contains x if { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:5: rego_compile_error: import must not shadow import data.foo", + }, + }, + + // default rego-version + { + note: "v1 module, no v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + p contains x if { + x = "a" + }`, + }, + }, + { + note: "v1 module, v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import data.foo + import data.bar as foo + + p contains x if { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:3: rego_compile_error: import must not shadow import data.foo", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + _, err := CompileModules(tc.modules) + + if len(tc.expErrs) > 0 { + for _, expErr := range tc.expErrs { + if err := err.Error(); !strings.Contains(err, expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + } +} + +func TestCompileModulesWithOpt_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + modules map[string]string + expErrs []string + }{ + // NOT default rego-version + { + note: "v0 module, no v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + p[x] { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0 module, v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import data.foo + import data.bar as foo + + p[x] { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:5: rego_parse_error: `if` keyword is required before rule body", + "test.rego:5: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + + // cross-rego-version + { + note: "rego.v1 import, no v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import rego.v1 + + p contains x if { + x = "a" + }`, + }, + }, + { + note: "rego.v1 import, v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import rego.v1 + + import data.foo + import data.bar as foo + + p contains x if { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:5: rego_compile_error: import must not shadow import data.foo", + }, + }, + + // default rego-version + { + note: "v1 module, no v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + p contains x if { + x = "a" + }`, + }, + }, + { + note: "v1 module, v1 compile-time violations", + modules: map[string]string{ + "test.rego": `package test + import data.foo + import data.bar as foo + + p contains x if { + x = "a" + }`, + }, + expErrs: []string{ + "test.rego:3: rego_compile_error: import must not shadow import data.foo", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + _, err := CompileModulesWithOpt(tc.modules, CompileOpts{EnablePrintStatements: true}) + + if len(tc.expErrs) > 0 { + for _, expErr := range tc.expErrs { + if err := err.Error(); !strings.Contains(err, expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + } +} diff --git a/third_party/opa/v1/ast/compilemetrics.go b/third_party/opa/v1/ast/compilemetrics.go new file mode 100644 index 000000000000..5d952258da00 --- /dev/null +++ b/third_party/opa/v1/ast/compilemetrics.go @@ -0,0 +1,9 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +const ( + compileStageComprehensionIndexBuild = "compile_stage_comprehension_index_build" +) diff --git a/third_party/opa/v1/ast/conflicts.go b/third_party/opa/v1/ast/conflicts.go new file mode 100644 index 000000000000..685cc6b6943c --- /dev/null +++ b/third_party/opa/v1/ast/conflicts.go @@ -0,0 +1,79 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "slices" + "strings" +) + +// CheckPathConflicts returns a set of errors indicating paths that +// are in conflict with the result of the provided callable. +func CheckPathConflicts(c *Compiler, exists func([]string) (bool, error)) Errors { + var errs Errors + + root := c.RuleTree.Child(DefaultRootDocument.Value) + if root == nil { + return nil + } + + if len(c.pathConflictCheckRoots) == 0 || slices.Contains(c.pathConflictCheckRoots, "") { + for _, child := range root.Children { + errs = append(errs, checkDocumentConflicts(child, exists, nil)...) + } + return errs + } + + for _, rootPath := range c.pathConflictCheckRoots { + // traverse AST from `path` to go to the new root + paths := strings.Split(rootPath, "/") + node := root + for _, key := range paths { + node = node.Child(String(key)) + if node == nil { + break + } + } + + if node == nil { + // could not find the node from the AST (e.g. `path` is from a data file) + // then no conflict is possible + continue + } + + for _, child := range node.Children { + errs = append(errs, checkDocumentConflicts(child, exists, paths)...) + } + } + + return errs +} + +func checkDocumentConflicts(node *TreeNode, exists func([]string) (bool, error), path []string) Errors { + + switch key := node.Key.(type) { + case String: + path = append(path, string(key)) + default: // other key types cannot conflict with data + return nil + } + + if len(node.Values) > 0 { + s := strings.Join(path, "/") + if ok, err := exists(path); err != nil { + return Errors{NewError(CompileErr, node.Values[0].(*Rule).Loc(), "conflict check for data path %v: %v", s, err.Error())} + } else if ok { + return Errors{NewError(CompileErr, node.Values[0].(*Rule).Loc(), "conflicting rule for data path %v found", s)} + } + } + + var errs Errors + + for _, child := range node.Children { + errs = append(errs, checkDocumentConflicts(child, exists, path)...) + } + + return errs +} diff --git a/third_party/opa/v1/ast/default_module_loader.go b/third_party/opa/v1/ast/default_module_loader.go new file mode 100644 index 000000000000..528c253e1618 --- /dev/null +++ b/third_party/opa/v1/ast/default_module_loader.go @@ -0,0 +1,14 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +var defaultModuleLoader ModuleLoader + +// DefaultModuleLoader lets you inject an `ast.ModuleLoader` that will +// always be used. If another one is provided with the ast package, +// they will both be consulted to enrich the set of modules dynamically. +func DefaultModuleLoader(ml ModuleLoader) { + defaultModuleLoader = ml +} diff --git a/third_party/opa/v1/ast/doc.go b/third_party/opa/v1/ast/doc.go new file mode 100644 index 000000000000..62b04e301eed --- /dev/null +++ b/third_party/opa/v1/ast/doc.go @@ -0,0 +1,36 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package ast declares Rego syntax tree types and also includes a parser and compiler for preparing policies for execution in the policy engine. +// +// Rego policies are defined using a relatively small set of types: modules, package and import declarations, rules, expressions, and terms. At their core, policies consist of rules that are defined by one or more expressions over documents available to the policy engine. The expressions are defined by intrinsic values (terms) such as strings, objects, variables, etc. +// +// Rego policies are typically defined in text files and then parsed and compiled by the policy engine at runtime. The parsing stage takes the text or string representation of the policy and converts it into an abstract syntax tree (AST) that consists of the types mentioned above. The AST is organized as follows: +// +// Module +// | +// +--- Package (Reference) +// | +// +--- Imports +// | | +// | +--- Import (Term) +// | +// +--- Rules +// | +// +--- Rule +// | +// +--- Head +// | | +// | +--- Name (Variable) +// | | +// | +--- Key (Term) +// | | +// | +--- Value (Term) +// | +// +--- Body +// | +// +--- Expression (Term | Terms | Variable Declaration) +// +// At query time, the policy engine expects policies to have been compiled. The compilation stage takes one or more modules and compiles them into a format that the policy engine supports. +package ast diff --git a/third_party/opa/v1/ast/env.go b/third_party/opa/v1/ast/env.go new file mode 100644 index 000000000000..12d4be89185f --- /dev/null +++ b/third_party/opa/v1/ast/env.go @@ -0,0 +1,528 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "strings" + + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +// TypeEnv contains type info for static analysis such as type checking. +type TypeEnv struct { + tree *typeTreeNode + next *TypeEnv + newChecker func() *typeChecker +} + +// newTypeEnv returns an empty TypeEnv. The constructor is not exported because +// type environments should only be created by the type checker. +func newTypeEnv(f func() *typeChecker) *TypeEnv { + return &TypeEnv{ + tree: newTypeTree(), + newChecker: f, + } +} + +// Get returns the type of x. +// Deprecated: Use GetByValue or GetByRef instead, as they are more efficient. +func (env *TypeEnv) Get(x any) types.Type { + if term, ok := x.(*Term); ok { + x = term.Value + } + + if v, ok := x.(Value); ok { + return env.GetByValue(v) + } + + panic("unreachable") +} + +// GetByValue returns the type of v. +func (env *TypeEnv) GetByValue(v Value) types.Type { + switch x := v.(type) { + + // Scalars. + case Null: + return types.Nl + case Boolean: + return types.B + case Number: + return types.N + case String: + return types.S + + // Composites. + case *Array: + static := make([]types.Type, x.Len()) + for i := range static { + tpe := env.GetByValue(x.Elem(i).Value) + static[i] = tpe + } + + var dynamic types.Type + if len(static) == 0 { + dynamic = types.A + } + + return types.NewArray(static, dynamic) + + case *lazyObj: + return env.GetByValue(x.force()) + case *object: + static := []*types.StaticProperty{} + var dynamic *types.DynamicProperty + + x.Foreach(func(k, v *Term) { + if IsConstant(k.Value) { + kjson, err := JSON(k.Value) + if err == nil { + tpe := env.GetByValue(v.Value) + static = append(static, types.NewStaticProperty(kjson, tpe)) + return + } + } + // Can't handle it as a static property, fallback to dynamic + typeK := env.GetByValue(k.Value) + typeV := env.GetByValue(v.Value) + dynamic = types.NewDynamicProperty(typeK, typeV) + }) + + if len(static) == 0 && dynamic == nil { + dynamic = types.NewDynamicProperty(types.A, types.A) + } + + return types.NewObject(static, dynamic) + + case Set: + var tpe types.Type + x.Foreach(func(elem *Term) { + tpe = types.Or(tpe, env.GetByValue(elem.Value)) + }) + if tpe == nil { + tpe = types.A + } + return types.NewSet(tpe) + + // Comprehensions. + case *ArrayComprehension: + cpy, errs := env.newChecker().CheckBody(env, x.Body) + if len(errs) == 0 { + return types.NewArray(nil, cpy.GetByValue(x.Term.Value)) + } + return nil + case *ObjectComprehension: + cpy, errs := env.newChecker().CheckBody(env, x.Body) + if len(errs) == 0 { + return types.NewObject(nil, types.NewDynamicProperty(cpy.GetByValue(x.Key.Value), cpy.GetByValue(x.Value.Value))) + } + return nil + case *SetComprehension: + cpy, errs := env.newChecker().CheckBody(env, x.Body) + if len(errs) == 0 { + return types.NewSet(cpy.GetByValue(x.Term.Value)) + } + return nil + + // Refs. + case Ref: + return env.GetByRef(x) + + // Vars. + case Var: + if node := env.tree.Child(v); node != nil { + return node.Value() + } + if env.next != nil { + return env.next.GetByValue(v) + } + return nil + + // Calls. + case Call: + return nil + } + + return env.Get(v) +} + +// GetByRef returns the type of the value referred to by ref. +func (env *TypeEnv) GetByRef(ref Ref) types.Type { + node := env.tree.Child(ref[0].Value) + if node == nil { + return env.getRefFallback(ref) + } + + return env.getRefRec(node, ref, ref[1:]) +} + +func (env *TypeEnv) getRefFallback(ref Ref) types.Type { + + if env.next != nil { + return env.next.GetByRef(ref) + } + + if RootDocumentNames.Contains(ref[0]) { + return types.A + } + + return nil +} + +func (env *TypeEnv) getRefRec(node *typeTreeNode, ref, tail Ref) types.Type { + if len(tail) == 0 { + return env.getRefRecExtent(node) + } + + if node.Leaf() { + if node.children.Len() > 0 { + if child := node.Child(tail[0].Value); child != nil { + return env.getRefRec(child, ref, tail[1:]) + } + } + return selectRef(node.Value(), tail) + } + + if !IsConstant(tail[0].Value) { + return selectRef(env.getRefRecExtent(node), tail) + } + + child := node.Child(tail[0].Value) + if child == nil { + return env.getRefFallback(ref) + } + + return env.getRefRec(child, ref, tail[1:]) +} + +func (env *TypeEnv) getRefRecExtent(node *typeTreeNode) types.Type { + + if node.Leaf() { + return node.Value() + } + + children := []*types.StaticProperty{} + + node.Children().Iter(func(key Value, child *typeTreeNode) bool { + tpe := env.getRefRecExtent(child) + + // NOTE(sr): Converting to Golang-native types here is an extension of what we did + // before -- only supporting strings. But since we cannot differentiate sets and arrays + // that way, we could reconsider. + switch key.(type) { + case String, Number, Boolean: // skip anything else + propKey, err := JSON(key) + if err != nil { + panic(fmt.Errorf("unreachable, ValueToInterface: %w", err)) + } + children = append(children, types.NewStaticProperty(propKey, tpe)) + } + return false + }) + + // TODO(tsandall): for now, these objects can have any dynamic properties + // because we don't have schema for base docs. Once schemas are supported + // we can improve this. + return types.NewObject(children, types.NewDynamicProperty(types.S, types.A)) +} + +func (env *TypeEnv) wrap() *TypeEnv { + cpy := *env + cpy.next = env + cpy.tree = newTypeTree() + return &cpy +} + +// typeTreeNode is used to store type information in a tree. +type typeTreeNode struct { + key Value + value types.Type + children *util.HasherMap[Value, *typeTreeNode] +} + +func newTypeTree() *typeTreeNode { + return &typeTreeNode{ + key: nil, + value: nil, + children: util.NewHasherMap[Value, *typeTreeNode](ValueEqual), + } +} + +func (n *typeTreeNode) Child(key Value) *typeTreeNode { + value, ok := n.children.Get(key) + if !ok { + return nil + } + return value +} + +func (n *typeTreeNode) Children() *util.HasherMap[Value, *typeTreeNode] { + return n.children +} + +func (n *typeTreeNode) Get(path Ref) types.Type { + curr := n + for _, term := range path { + child, ok := curr.children.Get(term.Value) + if !ok { + return nil + } + curr = child + } + return curr.Value() +} + +func (n *typeTreeNode) Leaf() bool { + return n.value != nil +} + +func (n *typeTreeNode) PutOne(key Value, tpe types.Type) { + c, ok := n.children.Get(key) + + var child *typeTreeNode + if !ok { + child = newTypeTree() + child.key = key + n.children.Put(key, child) + } else { + child = c + } + + child.value = tpe +} + +func (n *typeTreeNode) Put(path Ref, tpe types.Type) { + curr := n + for _, term := range path { + c, ok := curr.children.Get(term.Value) + + var child *typeTreeNode + if !ok { + child = newTypeTree() + child.key = term.Value + curr.children.Put(child.key, child) + } else { + child = c + } + + curr = child + } + curr.value = tpe +} + +// Insert inserts tpe at path in the tree, but also merges the value into any types.Object present along that path. +// If a types.Object is inserted, any leafs already present further down the tree are merged into the inserted object. +// path must be ground. +func (n *typeTreeNode) Insert(path Ref, tpe types.Type, env *TypeEnv) { + curr := n + for i, term := range path { + c, ok := curr.children.Get(term.Value) + + var child *typeTreeNode + if !ok { + child = newTypeTree() + child.key = term.Value + curr.children.Put(child.key, child) + } else { + child = c + if child.value != nil && i+1 < len(path) { + // If child has an object value, merge the new value into it. + if o, ok := child.value.(*types.Object); ok { + var err error + child.value, err = insertIntoObject(o, path[i+1:], tpe, env) + if err != nil { + panic(fmt.Errorf("unreachable, insertIntoObject: %w", err)) + } + } + } + } + + curr = child + } + + curr.value = mergeTypes(curr.value, tpe) + + if _, ok := tpe.(*types.Object); ok && curr.children.Len() > 0 { + // merge all leafs into the inserted object + leafs := curr.Leafs() + for p, t := range leafs { + var err error + curr.value, err = insertIntoObject(curr.value.(*types.Object), *p, t, env) + if err != nil { + panic(fmt.Errorf("unreachable, insertIntoObject: %w", err)) + } + } + } +} + +// mergeTypes merges the types of 'a' and 'b'. If both are sets, their 'of' types are joined with an types.Or. +// If both are objects, the key types of their dynamic properties are joined with types.Or:s, and their value types +// are recursively merged (using mergeTypes). +// If 'a' and 'b' are both objects, and at least one of them have static properties, they are joined +// with an types.Or, instead of being merged. +// If 'a' is an Any containing an Object, and 'b' is an Object (or vice versa); AND both objects have no +// static properties, they are merged. +// If 'a' and 'b' are different types, they are joined with an types.Or. +func mergeTypes(a, b types.Type) types.Type { + if a == nil { + return b + } + + if b == nil { + return a + } + + switch a := a.(type) { + case *types.Object: + if bObj, ok := b.(*types.Object); ok && len(a.StaticProperties()) == 0 && len(bObj.StaticProperties()) == 0 { + if len(a.StaticProperties()) > 0 || len(bObj.StaticProperties()) > 0 { + return types.Or(a, bObj) + } + + aDynProps := a.DynamicProperties() + bDynProps := bObj.DynamicProperties() + dynProps := types.NewDynamicProperty( + types.Or(aDynProps.Key, bDynProps.Key), + mergeTypes(aDynProps.Value, bDynProps.Value)) + return types.NewObject(nil, dynProps) + } else if bAny, ok := b.(types.Any); ok && len(a.StaticProperties()) == 0 { + // If a is an object type with no static components ... + for _, t := range bAny { + if tObj, ok := t.(*types.Object); ok && len(tObj.StaticProperties()) == 0 { + // ... and b is a types.Any containing an object with no static components, we merge them. + aDynProps := a.DynamicProperties() + tDynProps := tObj.DynamicProperties() + tDynProps.Key = types.Or(tDynProps.Key, aDynProps.Key) + tDynProps.Value = types.Or(tDynProps.Value, aDynProps.Value) + return bAny + } + } + } + case *types.Set: + if bSet, ok := b.(*types.Set); ok { + return types.NewSet(types.Or(a.Of(), bSet.Of())) + } + case types.Any: + if _, ok := b.(types.Any); !ok { + return mergeTypes(b, a) + } + } + + return types.Or(a, b) +} + +func (n *typeTreeNode) String() string { + b := strings.Builder{} + + if k := n.key; k != nil { + b.WriteString(k.String()) + } else { + b.WriteString("-") + } + + if v := n.value; v != nil { + b.WriteString(": ") + b.WriteString(v.String()) + } + + n.children.Iter(func(_ Value, child *typeTreeNode) bool { + b.WriteString("\n\t+ ") + s := child.String() + s = strings.ReplaceAll(s, "\n", "\n\t") + b.WriteString(s) + + return false + }) + + return b.String() +} + +func insertIntoObject(o *types.Object, path Ref, tpe types.Type, env *TypeEnv) (*types.Object, error) { + if len(path) == 0 { + return o, nil + } + + key := env.GetByValue(path[0].Value) + + if len(path) == 1 { + var dynamicProps *types.DynamicProperty + if dp := o.DynamicProperties(); dp != nil { + dynamicProps = types.NewDynamicProperty(types.Or(o.DynamicProperties().Key, key), types.Or(o.DynamicProperties().Value, tpe)) + } else { + dynamicProps = types.NewDynamicProperty(key, tpe) + } + return types.NewObject(o.StaticProperties(), dynamicProps), nil + } + + child, err := insertIntoObject(types.NewObject(nil, nil), path[1:], tpe, env) + if err != nil { + return nil, err + } + + var dynamicProps *types.DynamicProperty + if dp := o.DynamicProperties(); dp != nil { + dynamicProps = types.NewDynamicProperty(types.Or(o.DynamicProperties().Key, key), types.Or(o.DynamicProperties().Value, child)) + } else { + dynamicProps = types.NewDynamicProperty(key, child) + } + return types.NewObject(o.StaticProperties(), dynamicProps), nil +} + +func (n *typeTreeNode) Leafs() map[*Ref]types.Type { + leafs := map[*Ref]types.Type{} + n.children.Iter(func(_ Value, v *typeTreeNode) bool { + collectLeafs(v, nil, leafs) + return false + }) + return leafs +} + +func collectLeafs(n *typeTreeNode, path Ref, leafs map[*Ref]types.Type) { + nPath := append(path, NewTerm(n.key)) + if n.Leaf() { + leafs[&nPath] = n.Value() + return + } + n.children.Iter(func(_ Value, v *typeTreeNode) bool { + collectLeafs(v, nPath, leafs) + return false + }) +} + +func (n *typeTreeNode) Value() types.Type { + return n.value +} + +// selectConstant returns the attribute of the type referred to by the term. If +// the attribute type cannot be determined, nil is returned. +func selectConstant(tpe types.Type, term *Term) types.Type { + x, err := JSON(term.Value) + if err == nil { + return types.Select(tpe, x) + } + return nil +} + +// selectRef returns the type of the nested attribute referred to by ref. If +// the attribute type cannot be determined, nil is returned. If the ref +// contains vars or refs, then the returned type will be a union of the +// possible types. +func selectRef(tpe types.Type, ref Ref) types.Type { + + if tpe == nil || len(ref) == 0 { + return tpe + } + + head, tail := ref[0], ref[1:] + + switch head.Value.(type) { + case Var, Ref, *Array, Object, Set: + return selectRef(types.Values(tpe), tail) + default: + return selectRef(selectConstant(tpe, head), tail) + } +} diff --git a/third_party/opa/v1/ast/env_test.go b/third_party/opa/v1/ast/env_test.go new file mode 100644 index 000000000000..9f16e2d3479a --- /dev/null +++ b/third_party/opa/v1/ast/env_test.go @@ -0,0 +1,559 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/types" +) + +func TestInsertIntoObject(t *testing.T) { + tests := []struct { + note string + obj *types.Object + path Ref + tpe types.Type + expected types.Type + }{ + { + note: "adding to empty object", + obj: types.NewObject(nil, nil), + path: Ref{NewTerm(String("a"))}, + tpe: types.S, + expected: types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.S)), + }, + { + note: "empty path", + obj: types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("a", types.S)}, + nil), + path: nil, + tpe: types.S, + expected: types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("a", types.S)}, + nil), + }, + { + note: "adding to populated object", + obj: types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("a", types.S)}, + nil), + path: Ref{NewTerm(String("b"))}, + tpe: types.S, + expected: types.NewObject( + []*types.StaticProperty{ + types.NewStaticProperty("a", types.S), + }, + types.NewDynamicProperty(types.S, types.S)), + }, + { + note: "number key", + obj: types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("a", types.S)}, + nil), + path: Ref{NewTerm(Number("2"))}, + tpe: types.S, + expected: types.NewObject( + []*types.StaticProperty{ + types.NewStaticProperty("a", types.S), + }, + types.NewDynamicProperty(types.N, types.S)), + }, + { + note: "other type value inserted", + obj: types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.S)), + path: Ref{NewTerm(String("a"))}, + tpe: types.B, + expected: types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.Any{types.B, types.S})), + }, + { + note: "any type value inserted", + obj: types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.S)), + path: Ref{NewTerm(String("a"))}, + tpe: types.A, + expected: types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.A)), + }, + { + note: "other type key inserted", + obj: types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.S)), + path: Ref{NewTerm(Number("42"))}, + tpe: types.S, + expected: types.NewObject( + nil, + types.NewDynamicProperty(types.Any{types.N, types.S}, types.S)), + }, + { + note: "other type key and value inserted", + obj: types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.S)), + path: Ref{NewTerm(Number("42"))}, + tpe: types.B, + expected: types.NewObject( + nil, + types.NewDynamicProperty(types.Any{types.N, types.S}, types.Any{types.B, types.S})), + }, + { + note: "any type value present, string inserted", + obj: types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.A)), + path: Ref{NewTerm(String("a"))}, + tpe: types.S, + expected: types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.A)), + }, + { + note: "long path", + obj: types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("a", types.S)}, + nil), + path: Ref{NewTerm(String("b")), NewTerm(String("c")), NewTerm(String("d"))}, + tpe: types.S, + expected: types.NewObject( + []*types.StaticProperty{ + types.NewStaticProperty("a", types.S), + }, + types.NewDynamicProperty(types.S, // b + types.NewObject(nil, types.NewDynamicProperty(types.S, // c + types.NewObject(nil, types.NewDynamicProperty(types.S, types.S)))))), // d + }, + { + note: "long path, dynamic overlap with different key type", + obj: types.NewObject( + nil, + types.NewDynamicProperty(types.N, types.S)), + path: Ref{NewTerm(String("b")), NewTerm(String("c")), NewTerm(String("d"))}, + tpe: types.S, + expected: types.NewObject( + nil, + types.NewDynamicProperty(types.Any{types.N, types.S}, // b + types.Any{types.S, + types.NewObject(nil, types.NewDynamicProperty(types.S, // c + types.NewObject(nil, types.NewDynamicProperty(types.S, types.S))))})), // d + }, + { + note: "long path, dynamic overlap with object", + obj: types.NewObject( + nil, + types.NewDynamicProperty(types.S, + types.NewObject(nil, types.NewDynamicProperty(types.S, types.N)))), + path: Ref{NewTerm(String("b")), NewTerm(String("c")), NewTerm(String("d"))}, + tpe: types.S, + expected: types.NewObject( + nil, + types.NewDynamicProperty(types.S, // b + types.Any{ + types.NewObject(nil, types.NewDynamicProperty(types.S, types.N)), + types.NewObject(nil, types.NewDynamicProperty(types.S, // c + types.NewObject(nil, types.NewDynamicProperty(types.S, types.S)))), // d + })), + }, + { + note: "long path, dynamic overlap with object (2)", + obj: types.NewObject( + nil, + types.NewDynamicProperty(types.S, + types.NewObject(nil, types.NewDynamicProperty(types.S, + types.NewObject(nil, types.NewDynamicProperty(types.S, types.N)))))), + path: Ref{NewTerm(String("b")), NewTerm(String("c")), NewTerm(String("d"))}, + tpe: types.S, + expected: types.NewObject( + nil, + types.NewDynamicProperty(types.S, + types.Any{ // Objects aren't merged, as that would become very complicated if they contain static components + types.NewObject(nil, types.NewDynamicProperty(types.S, + types.NewObject(nil, types.NewDynamicProperty(types.S, types.N)))), + types.NewObject(nil, types.NewDynamicProperty(types.S, + types.NewObject(nil, types.NewDynamicProperty(types.S, types.S)))), + })), + }, + { + note: "long path, dynamic overlap with different value type", + obj: types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.S)), + path: Ref{NewTerm(String("b")), NewTerm(String("c")), NewTerm(String("d"))}, + tpe: types.S, + expected: types.NewObject( + nil, + types.NewDynamicProperty(types.S, // b + types.Any{types.S, + types.NewObject(nil, types.NewDynamicProperty(types.S, // c + types.NewObject(nil, types.NewDynamicProperty(types.S, types.S))))})), // d + }, + } + + env := TypeEnv{} + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + result, err := insertIntoObject(tc.obj, tc.path, tc.tpe, &env) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if types.Compare(result, tc.expected) != 0 { + t.Fatalf("Expected %v but got %v", tc.expected, result) + } + }) + } +} + +type pathAndType struct { + path Ref + tpe types.Type +} + +func TestTypeTreeNode_Insert(t *testing.T) { + cases := []struct { + note string + insertions []pathAndType + expected []pathAndType + }{ + { + note: "only primitives", + insertions: []pathAndType{ + { + path: MustParseRef("data.a.b.c"), + tpe: types.N, + }, + { + path: MustParseRef("data.a.b.c2"), + tpe: types.S, + }, + { + path: MustParseRef("data.a.b[42]"), + tpe: types.B, + }, + }, + expected: []pathAndType{ + { + path: MustParseRef("data.a.b.c"), + tpe: types.N, + }, + { + path: MustParseRef("data.a.b.c2"), + tpe: types.S, + }, + { + path: MustParseRef("data.a.b[42]"), + tpe: types.B, + }, + { + path: MustParseRef("data.a.b"), + tpe: nil, + }, + }, + }, + { + note: "primitive leafs inserted into object", + insertions: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject(nil, types.NewDynamicProperty(types.N, types.B)), + }, + { + path: MustParseRef("data.a.b.c"), + tpe: types.S, + }, + { + path: MustParseRef("data.a.b[true]"), + tpe: types.S, + }, + }, + expected: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject(nil, types.NewDynamicProperty( + types.Any{types.B, types.N, types.S}, types.Any{types.B, types.S})), + }, + }, + }, + { + note: "primitive leafs first, then object", + insertions: []pathAndType{ + { + path: MustParseRef("data.a.b.c"), + tpe: types.S, + }, + { + path: MustParseRef("data.a.b[true]"), + tpe: types.S, + }, + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject(nil, types.NewDynamicProperty(types.N, types.B)), + }, + }, + expected: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject(nil, types.NewDynamicProperty( + types.Any{types.B, types.N, types.S}, + types.Any{types.B, types.S}, + )), + }, + }, + }, + { + note: "object beside object", + insertions: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject(nil, types.NewDynamicProperty(types.N, types.B)), + }, + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject(nil, types.NewDynamicProperty(types.S, types.S)), + }, + }, + expected: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject(nil, types.NewDynamicProperty( + types.Any{types.N, types.S}, + types.Any{types.B, types.S}, + )), + }, + }, + }, + { + note: "object beside object with static types", + insertions: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("foo", types.N)}, + types.NewDynamicProperty(types.N, types.B)), + }, + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("bar", types.S)}, + types.NewDynamicProperty(types.S, types.S)), + }, + }, + expected: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.Any{ + types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("foo", types.N)}, + types.NewDynamicProperty(types.N, types.B)), + types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("bar", types.S)}, + types.NewDynamicProperty(types.S, types.S)), + }, + }, + }, + }, + { + note: "object beside object with static types (2)", + insertions: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject( + nil, + types.NewDynamicProperty(types.N, types.B)), + }, + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("bar", types.S)}, + types.NewDynamicProperty(types.S, types.S)), + }, + }, + expected: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.Any{ + types.NewObject( + nil, + types.NewDynamicProperty(types.N, types.B)), + types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("bar", types.S)}, + types.NewDynamicProperty(types.S, types.S)), + }, + }, + }, + }, + { + note: "object beside object with static types (3)", + insertions: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("foo", types.N)}, + types.NewDynamicProperty(types.N, types.B)), + }, + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.S)), + }, + }, + expected: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.Any{ + types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("foo", types.N)}, + types.NewDynamicProperty(types.N, types.B)), + types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.S)), + }, + }, + }, + }, + { + note: "object beside object with static types (4)", + insertions: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("foo", types.N)}, + types.NewDynamicProperty(types.N, types.B)), + }, + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject( + nil, + types.NewDynamicProperty(types.S, types.S)), + }, + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject( + nil, + types.NewDynamicProperty(types.N, types.B)), + }, + }, + expected: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.Any{ + types.NewObject( + []*types.StaticProperty{types.NewStaticProperty("foo", types.N)}, + types.NewDynamicProperty(types.N, types.B)), + types.NewObject( + nil, + types.NewDynamicProperty(types.Any{types.N, types.S}, types.Any{types.B, types.S})), + }, + }, + }, + }, + { + note: "object into object", + insertions: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject(nil, types.NewDynamicProperty(types.N, types.B)), + }, + { + path: MustParseRef("data.a.b.c"), + tpe: types.NewObject(nil, types.NewDynamicProperty(types.B, types.N)), + }, + }, + expected: []pathAndType{ + { + path: MustParseRef("data.a.b"), + tpe: types.NewObject(nil, types.NewDynamicProperty( + types.Any{types.N, types.S}, + types.Any{types.B, types.NewObject(nil, types.NewDynamicProperty(types.B, types.N))}, + )), + }, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + root := newTypeTree() + env := TypeEnv{tree: root} + + for _, insertion := range tc.insertions { + root.Insert(insertion.path, insertion.tpe, &env) + } + + for _, expected := range tc.expected { + actual := root.Get(expected.path) + if types.Compare(actual, expected.tpe) != 0 { + t.Fatalf("Expected %v but got %v", expected.tpe, actual) + } + } + }) + } +} + +func TestTypeTreeInsert(t *testing.T) { + env := TypeEnv{} + n := newTypeTree() + + abcRef := Ref{NewTerm(String("a")), NewTerm(String("b")), NewTerm(String("c"))} + n.Put(abcRef, types.B) + actual := n.Get(abcRef) + if types.Compare(actual, types.B) != 0 { + t.Fatalf("Expected %v but got %v", types.B, actual) + } + + abdeRef := Ref{NewTerm(String("a")), NewTerm(String("b")), NewTerm(String("d")), NewTerm(String("e"))} + n.Put(abdeRef, types.N) + actual = n.Get(abdeRef) + if types.Compare(actual, types.N) != 0 { + t.Fatalf("Expected %v but got %v", types.N, actual) + } + + // existing "child" leafs should be added to new intermediate object leaf + + abRef := Ref{NewTerm(String("a")), NewTerm(String("b"))} + n.Insert(abRef, types.NewObject(nil, &types.DynamicProperty{Key: types.N, Value: types.S}), &env) + + actual = n.Get(abRef) + expected := types.NewObject( + nil, + types.NewDynamicProperty( + types.Any{types.N, types.S}, + types.Any{types.B, types.S, types.NewObject(nil, types.NewDynamicProperty(types.S, types.N))}), + ) + if types.Compare(actual, expected) != 0 { + t.Fatalf("Expected %v but got %v", expected, actual) + } + + // new "child" leafs should be added to new intermediate object leaf + + abfRef := Ref{NewTerm(String("a")), NewTerm(String("b")), NewTerm(Boolean(true))} + n.Insert(abfRef, types.S, &env) + + actual = n.Get(abfRef) + if types.Compare(actual, types.S) != 0 { + t.Fatalf("Expected %v but got %v", types.S, actual) + } + + actual = n.Get(abRef) + expected = types.NewObject( + nil, + types.NewDynamicProperty( + types.Any{types.B, types.N, types.S}, + types.Any{types.B, types.S, types.NewObject(nil, types.NewDynamicProperty(types.S, types.N))}), + ) + if types.Compare(actual, expected) != 0 { + t.Fatalf("Expected %v but got %v", expected, actual) + } +} diff --git a/third_party/opa/v1/ast/errors.go b/third_party/opa/v1/ast/errors.go new file mode 100644 index 000000000000..75160afc6e14 --- /dev/null +++ b/third_party/opa/v1/ast/errors.go @@ -0,0 +1,124 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "slices" + "strconv" + "strings" +) + +// Errors represents a series of errors encountered during parsing, compiling, +// etc. +type Errors []*Error + +func (e Errors) Error() string { + + if len(e) == 0 { + return "no error(s)" + } + + if len(e) == 1 { + return fmt.Sprintf("1 error occurred: %v", e[0].Error()) + } + + s := make([]string, len(e)) + for i, err := range e { + s[i] = err.Error() + } + + return fmt.Sprintf("%d errors occurred:\n%s", len(e), strings.Join(s, "\n")) +} + +// Sort sorts the error slice by location. If the locations are equal then the +// error message is compared. +func (e Errors) Sort() { + slices.SortFunc(e, func(a, b *Error) int { + if cmp := a.Location.Compare(b.Location); cmp != 0 { + return cmp + } + + return strings.Compare(a.Error(), b.Error()) + }) +} + +const ( + // ParseErr indicates an unclassified parse error occurred. + ParseErr = "rego_parse_error" + + // CompileErr indicates an unclassified compile error occurred. + CompileErr = "rego_compile_error" + + // TypeErr indicates a type error was caught. + TypeErr = "rego_type_error" + + // UnsafeVarErr indicates an unsafe variable was found during compilation. + UnsafeVarErr = "rego_unsafe_var_error" + + // RecursionErr indicates recursion was found during compilation. + RecursionErr = "rego_recursion_error" + + // FormatErr indicates an error occurred during formatting. + FormatErr = "rego_format_error" +) + +// IsError returns true if err is an AST error with code. +func IsError(code string, err error) bool { + if err, ok := err.(*Error); ok { + return err.Code == code + } + return false +} + +// ErrorDetails defines the interface for detailed error messages. +type ErrorDetails interface { + Lines() []string +} + +// Error represents a single error caught during parsing, compiling, etc. +type Error struct { + Code string `json:"code"` + Message string `json:"message"` + Location *Location `json:"location,omitempty"` + Details ErrorDetails `json:"details,omitempty"` +} + +func (e *Error) Error() string { + + var prefix string + + if e.Location != nil { + + if len(e.Location.File) > 0 { + prefix += e.Location.File + ":" + strconv.Itoa(e.Location.Row) + } else { + prefix += strconv.Itoa(e.Location.Row) + ":" + strconv.Itoa(e.Location.Col) + } + } + + msg := fmt.Sprintf("%v: %v", e.Code, e.Message) + + if len(prefix) > 0 { + msg = prefix + ": " + msg + } + + if e.Details != nil { + for _, line := range e.Details.Lines() { + msg += "\n\t" + line + } + } + + return msg +} + +// NewError returns a new Error object. +func NewError(code string, loc *Location, f string, a ...any) *Error { + return &Error{ + Code: code, + Location: loc, + Message: fmt.Sprintf(f, a...), + } +} diff --git a/third_party/opa/v1/ast/errors_test.go b/third_party/opa/v1/ast/errors_test.go new file mode 100644 index 000000000000..7368c0fe134c --- /dev/null +++ b/third_party/opa/v1/ast/errors_test.go @@ -0,0 +1,41 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import "testing" + +func TestErrorsString(t *testing.T) { + + err := Errors{ + NewError(ParseErr, nil, "blah"), + NewError(ParseErr, NewLocation(nil, "", 100, 2), "bleh"), + NewError(ParseErr, NewLocation(nil, "foo.rego", 100, 2), "blarg"), + } + + expected := `3 errors occurred: +rego_parse_error: blah +100:2: rego_parse_error: bleh +foo.rego:100: rego_parse_error: blarg` + result := err.Error() + + if result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } + + err = Errors{NewError(ParseErr, nil, "blah")} + expected = `1 error occurred: rego_parse_error: blah` + result = err.Error() + + if result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } + + expected = `no error(s)` + result = Errors{}.Error() + if result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } + +} diff --git a/third_party/opa/v1/ast/example_test.go b/third_party/opa/v1/ast/example_test.go new file mode 100644 index 000000000000..ea494d3df4d2 --- /dev/null +++ b/third_party/opa/v1/ast/example_test.go @@ -0,0 +1,115 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast_test + +import ( + "fmt" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func ExampleCompiler_Compile() { + + // Define an input module that will be compiled. + exampleModule := `package opa.example + +import rego.v1 +import data.foo +import input.bar + +p[x] if { foo[x]; not bar[x]; x >= min_x } +min_x = 100 if { true }` + + // Parse the input module to obtain the AST representation. + mod, err := ast.ParseModule("my_module", exampleModule) + if err != nil { + fmt.Println("Parse error:", err) + } + + // Create a new compiler instance and compile the module. + c := ast.NewCompiler() + + mods := map[string]*ast.Module{ + "my_module": mod, + } + + if c.Compile(mods); c.Failed() { + fmt.Println("Compile error:", c.Errors) + } + + fmt.Println("Expr 1:", c.Modules["my_module"].Rules[0].Body[0]) + fmt.Println("Expr 2:", c.Modules["my_module"].Rules[0].Body[1]) + fmt.Println("Expr 3:", c.Modules["my_module"].Rules[0].Body[2]) + fmt.Println("Expr 4:", c.Modules["my_module"].Rules[0].Body[3]) + + // Output: + // + // Expr 1: data.foo[x] + // Expr 2: not input.bar[x] + // Expr 3: __local0__ = data.opa.example.min_x + // Expr 4: gte(x, __local0__) +} + +func ExampleQueryCompiler_Compile() { + + // Define an input module that will be compiled. + exampleModule := `package opa.example + +import rego.v1 +import data.foo +import input.bar + +p[x] if { foo[x]; not bar[x]; x >= min_x } +min_x = 100 if { true }` + + // Parse the input module to obtain the AST representation. + mod, err := ast.ParseModule("my_module", exampleModule) + if err != nil { + fmt.Println("Parse error:", err) + } + + // Create a new compiler instance and compile the module. + c := ast.NewCompiler() + + mods := map[string]*ast.Module{ + "my_module": mod, + } + + if c.Compile(mods); c.Failed() { + fmt.Println("Compile error:", c.Errors) + } + + // Obtain the QueryCompiler from the compiler instance. Note, we will + // compile this query within the context of the opa.example package and + // declare that a query input named "queryinput" must be supplied. The + // QueryContext will include the input value so. + qc := c.QueryCompiler(). + WithContext( + // Note, the ast.MustParse functions are meant for test + // purposes only. They will panic if an error occurs. Prefer the + // ast.Parse functions that return meaningful error messages + // instead. + ast.NewQueryContext(). + WithPackage(ast.MustParsePackage(`package opa.example`)). + WithImports(ast.MustParseImports("import input.query_arg")), + ) + + // Parse the input query to obtain the AST representation. + query, err := ast.ParseBody(`p[x]; x < query_arg`) + if err != nil { + fmt.Println("Parse error:", err) + } + + compiled, err := qc.Compile(query) + if err != nil { + fmt.Println("Compile error:", err) + } + + fmt.Println("Compiled:", compiled) + + // Output: + // + // Compiled: data.opa.example.p[x]; __localq0__ = input.query_arg; lt(x, __localq0__) +} diff --git a/third_party/opa/v1/ast/fuzz_test.go b/third_party/opa/v1/ast/fuzz_test.go new file mode 100644 index 000000000000..68d6a33675ae --- /dev/null +++ b/third_party/opa/v1/ast/fuzz_test.go @@ -0,0 +1,41 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint +package ast + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/test/cases" +) + +var testcases = cases.MustLoad("../test/cases/testdata").Sorted().Cases + +func FuzzCompileModules(f *testing.F) { + for _, tc := range testcases { + for _, mod := range tc.Modules { + f.Add(mod) + } + } + f.Fuzz(func(t *testing.T, input string) { + t.Parallel() + CompileModules(map[string]string{"": input}) + }) +} + +func FuzzCompileModulesWithPrintAndAllFutureKWs(f *testing.F) { + for _, tc := range testcases { + for _, mod := range tc.Modules { + f.Add(mod) + } + } + f.Fuzz(func(t *testing.T, input string) { + t.Parallel() + CompileModulesWithOpt(map[string]string{"": input}, CompileOpts{ + EnablePrintStatements: true, + ParserOptions: ParserOptions{AllFutureKeywords: true}, + }) + }) +} diff --git a/third_party/opa/v1/ast/index.go b/third_party/opa/v1/ast/index.go new file mode 100644 index 000000000000..bcaf4a7068a7 --- /dev/null +++ b/third_party/opa/v1/ast/index.go @@ -0,0 +1,968 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "slices" + "sort" + "strings" + "sync" + + "github.com/open-policy-agent/opa/v1/util" +) + +// RuleIndex defines the interface for rule indices. +type RuleIndex interface { + + // Build tries to construct an index for the given rules. If the index was + // constructed, it returns true, otherwise false. + Build(rules []*Rule) bool + + // Lookup searches the index for rules that will match the provided + // resolver. If the resolver returns an error, it is returned via err. + Lookup(resolver ValueResolver) (*IndexResult, error) + + // AllRules traverses the index and returns all rules that will match + // the provided resolver without any optimizations (effectively with + // indexing disabled). If the resolver returns an error, it is returned + // via err. + AllRules(resolver ValueResolver) (*IndexResult, error) +} + +// IndexResult contains the result of an index lookup. +type IndexResult struct { + Rules []*Rule + Else map[*Rule][]*Rule + Default *Rule + Kind RuleKind + EarlyExit bool + OnlyGroundRefs bool +} + +// NewIndexResult returns a new IndexResult object. +func NewIndexResult(kind RuleKind) *IndexResult { + return &IndexResult{ + Kind: kind, + } +} + +// Empty returns true if there are no rules to evaluate. +func (ir *IndexResult) Empty() bool { + return len(ir.Rules) == 0 && ir.Default == nil +} + +type baseDocEqIndex struct { + isVirtual func(Ref) bool + root *trieNode + defaultRule *Rule + kind RuleKind + onlyGroundRefs bool +} + +var ( + equalityRef = Equality.Ref() + equalRef = Equal.Ref() + globMatchRef = GlobMatch.Ref() + internalPrintRef = InternalPrint.Ref() + internalTestCaseRef = InternalTestCase.Ref() + + skipIndexing = NewSet(NewTerm(internalPrintRef), NewTerm(internalTestCaseRef)) +) + +func newBaseDocEqIndex(isVirtual func(Ref) bool) *baseDocEqIndex { + return &baseDocEqIndex{ + isVirtual: isVirtual, + root: newTrieNodeImpl(), + onlyGroundRefs: true, + } +} + +func (i *baseDocEqIndex) Build(rules []*Rule) bool { + if len(rules) == 0 { + return false + } + + i.kind = rules[0].Head.RuleKind() + indices := newrefindices(i.isVirtual) + + // build indices for each rule. + for idx := range rules { + WalkRules(rules[idx], func(rule *Rule) bool { + if rule.Default { + i.defaultRule = rule + return false + } + if i.onlyGroundRefs { + i.onlyGroundRefs = rule.Head.Reference.IsGround() + } + var skip bool + for i := range rule.Body { + if op := rule.Body[i].OperatorTerm(); op != nil && skipIndexing.Contains(op) { + skip = true + break + } + } + if !skip { + for i := range rule.Body { + indices.Update(rule, rule.Body[i]) + } + } + return false + }) + } + + // build trie out of indices. + for idx := range rules { + var prio int + WalkRules(rules[idx], func(rule *Rule) bool { + if rule.Default { + return false + } + node := i.root + if indices.Indexed(rule) { + for _, ref := range indices.Sorted() { + node = node.Insert(ref, indices.Value(rule, ref), indices.Mapper(rule, ref)) + } + } + // Insert rule into trie with (insertion order, priority order) + // tuple. Retaining the insertion order allows us to return rules + // in the order they were passed to this function. + node.append([...]int{idx, prio}, rule) + prio++ + return false + }) + } + return true +} + +func (i *baseDocEqIndex) Lookup(resolver ValueResolver) (*IndexResult, error) { + tr := ttrPool.Get().(*trieTraversalResult) + + defer func() { + clear(tr.unordered) + tr.ordering = tr.ordering[:0] + tr.multiple = false + tr.exist = nil + + ttrPool.Put(tr) + }() + + err := i.root.Traverse(resolver, tr) + if err != nil { + return nil, err + } + + result := IndexResultPool.Get() + + result.Kind = i.kind + result.Default = i.defaultRule + result.OnlyGroundRefs = i.onlyGroundRefs + + if result.Rules == nil { + result.Rules = make([]*Rule, 0, len(tr.ordering)) + } else { + result.Rules = result.Rules[:0] + } + + clear(result.Else) + + for _, pos := range tr.ordering { + slices.SortFunc(tr.unordered[pos], func(a, b *ruleNode) int { + return a.prio[1] - b.prio[1] + }) + nodes := tr.unordered[pos] + root := nodes[0].rule + + result.Rules = append(result.Rules, root) + if len(nodes) > 1 { + if result.Else == nil { + result.Else = map[*Rule][]*Rule{} + } + + result.Else[root] = make([]*Rule, len(nodes)-1) + for i := 1; i < len(nodes); i++ { + result.Else[root][i-1] = nodes[i].rule + } + } + } + + if !tr.multiple { + // even when the indexer hasn't seen multiple values, the rule itself could be one + // where early exit shouldn't be applied. + var lastValue Value + for i := range result.Rules { + if result.Rules[i].Head.DocKind() != CompleteDoc { + tr.multiple = true + break + } + if result.Rules[i].Head.Value != nil { + if lastValue != nil && !ValueEqual(lastValue, result.Rules[i].Head.Value.Value) { + tr.multiple = true + break + } + lastValue = result.Rules[i].Head.Value.Value + } + } + } + + result.EarlyExit = !tr.multiple + + return result, nil +} + +func (i *baseDocEqIndex) AllRules(_ ValueResolver) (*IndexResult, error) { + tr := newTrieTraversalResult() + + // Walk over the rule trie and accumulate _all_ rules + rw := &ruleWalker{result: tr} + i.root.Do(rw) + + result := NewIndexResult(i.kind) + result.Default = i.defaultRule + result.OnlyGroundRefs = i.onlyGroundRefs + result.Rules = make([]*Rule, 0, len(tr.ordering)) + + for _, pos := range tr.ordering { + slices.SortFunc(tr.unordered[pos], func(a, b *ruleNode) int { + return a.prio[1] - b.prio[1] + }) + nodes := tr.unordered[pos] + root := nodes[0].rule + result.Rules = append(result.Rules, root) + if len(nodes) > 1 { + if result.Else == nil { + result.Else = map[*Rule][]*Rule{} + } + + result.Else[root] = make([]*Rule, len(nodes)-1) + for i := 1; i < len(nodes); i++ { + result.Else[root][i-1] = nodes[i].rule + } + } + } + + result.EarlyExit = !tr.multiple + + return result, nil +} + +type ruleWalker struct { + result *trieTraversalResult +} + +func (r *ruleWalker) Do(x any) trieWalker { + tn := x.(*trieNode) + r.result.Add(tn) + return r +} + +type valueMapper struct { + Key string + MapValue func(Value) Value +} + +type refindex struct { + Ref Ref + Value Value + Mapper *valueMapper +} + +type refindices struct { + isVirtual func(Ref) bool + rules map[*Rule][]*refindex + frequency *util.HasherMap[Ref, int] + sorted []Ref +} + +func newrefindices(isVirtual func(Ref) bool) *refindices { + return &refindices{ + isVirtual: isVirtual, + rules: map[*Rule][]*refindex{}, + frequency: util.NewHasherMap[Ref, int](RefEqual), + } +} + +// Update attempts to update the refindices for the given expression in the +// given rule. If the expression cannot be indexed the update does not affect +// the indices. +func (i *refindices) Update(rule *Rule, expr *Expr) { + + if expr.Negated { + return + } + + if len(expr.With) > 0 { + // NOTE(tsandall): In the future, we may need to consider expressions + // that have with statements applied to them. + return + } + + op := expr.Operator() + + switch { + case op.Equal(equalityRef): + i.updateEq(rule, expr) + + case op.Equal(equalRef) && len(expr.Operands()) == 2: + // NOTE(tsandall): if equal() is called with more than two arguments the + // output value is being captured in which case the indexer cannot + // exclude the rule if the equal() call would return false (because the + // false value must still be produced.) + i.updateEq(rule, expr) + + case op.Equal(globMatchRef) && len(expr.Operands()) == 3: + // NOTE(sr): Same as with equal() above -- 4 operands means the output + // of `glob.match` is captured and the rule can thus not be excluded. + i.updateGlobMatch(rule, expr) + } +} + +// Sorted returns a sorted list of references that the indices were built from. +// References that appear more frequently in the indexed rules are ordered +// before less frequently appearing references. +func (i *refindices) Sorted() []Ref { + + if i.sorted == nil { + counts := make([]int, 0, i.frequency.Len()) + i.sorted = make([]Ref, 0, i.frequency.Len()) + + i.frequency.Iter(func(k Ref, v int) bool { + counts = append(counts, v) + i.sorted = append(i.sorted, k) + return false + }) + + sort.Slice(i.sorted, func(a, b int) bool { + if counts[a] > counts[b] { + return true + } else if counts[b] > counts[a] { + return false + } + return i.sorted[a][0].Loc().Compare(i.sorted[b][0].Loc()) < 0 + }) + } + + return i.sorted +} + +func (i *refindices) Indexed(rule *Rule) bool { + return len(i.rules[rule]) > 0 +} + +func (i *refindices) Value(rule *Rule, ref Ref) Value { + if index := i.index(rule, ref); index != nil { + return index.Value + } + return nil +} + +func (i *refindices) Mapper(rule *Rule, ref Ref) *valueMapper { + if index := i.index(rule, ref); index != nil { + return index.Mapper + } + return nil +} + +func (i *refindices) updateEq(rule *Rule, expr *Expr) { + a, b := expr.Operand(0), expr.Operand(1) + args := rule.Head.Args + if idx, ok := eqOperandsToRefAndValue(i.isVirtual, args, a, b); ok { + i.insert(rule, idx) + return + } + if idx, ok := eqOperandsToRefAndValue(i.isVirtual, args, b, a); ok { + i.insert(rule, idx) + return + } +} + +func (i *refindices) updateGlobMatch(rule *Rule, expr *Expr) { + args := rule.Head.Args + + delim, ok := globDelimiterToString(expr.Operand(1)) + if !ok { + return + } + + if arr := globPatternToArray(expr.Operand(0), delim); arr != nil { + // The 3rd operand of glob.match is the value to match. We assume the + // 3rd operand was a reference that has been rewritten and bound to a + // variable earlier in the query OR a function argument variable. + match := expr.Operand(2) + if _, ok := match.Value.(Var); ok { + var ref Ref + for _, other := range i.rules[rule] { + if _, ok := other.Value.(Var); ok && other.Value.Compare(match.Value) == 0 { + ref = other.Ref + } + } + if ref == nil { + for j, arg := range args { + if arg.Equal(match) { + ref = Ref{FunctionArgRootDocument, InternedTerm(j)} + } + } + } + if ref != nil { + i.insert(rule, &refindex{ + Ref: ref, + Value: arr.Value, + Mapper: &valueMapper{ + Key: delim, + MapValue: func(v Value) Value { + if s, ok := v.(String); ok { + return stringSliceToArray(splitStringEscaped(string(s), delim)) + } + return v + }, + }, + }) + } + } + } +} + +func (i *refindices) insert(rule *Rule, index *refindex) { + + count, ok := i.frequency.Get(index.Ref) + if !ok { + count = 0 + } + + i.frequency.Put(index.Ref, count+1) + + for pos, other := range i.rules[rule] { + if other.Ref.Equal(index.Ref) { + i.rules[rule][pos] = index + return + } + } + + i.rules[rule] = append(i.rules[rule], index) +} + +func (i *refindices) index(rule *Rule, ref Ref) *refindex { + for _, index := range i.rules[rule] { + if index.Ref.Equal(ref) { + return index + } + } + return nil +} + +type trieWalker interface { + Do(x any) trieWalker +} + +type trieTraversalResult struct { + unordered map[int][]*ruleNode + ordering []int + exist *Term + multiple bool +} + +var ttrPool = sync.Pool{ + New: func() any { + return newTrieTraversalResult() + }, +} + +func newTrieTraversalResult() *trieTraversalResult { + return &trieTraversalResult{ + unordered: map[int][]*ruleNode{}, + } +} + +func (tr *trieTraversalResult) Add(t *trieNode) { + for _, node := range t.rules { + root := node.prio[0] + nodes, ok := tr.unordered[root] + if !ok { + tr.ordering = append(tr.ordering, root) + } + tr.unordered[root] = append(nodes, node) + } + if t.multiple { + tr.multiple = true + } + if tr.multiple || t.value == nil { + return + } + if t.value.IsGround() && tr.exist == nil || tr.exist.Equal(t.value) { + tr.exist = t.value + return + } + tr.multiple = true +} + +type trieNode struct { + ref Ref + mappers []*valueMapper + next *trieNode + any *trieNode + undefined *trieNode + scalars *util.HasherMap[Value, *trieNode] + array *trieNode + rules []*ruleNode + value *Term + multiple bool +} + +func (node *trieNode) String() string { + var flags []string + flags = append(flags, fmt.Sprintf("self:%p", node)) + if len(node.ref) > 0 { + flags = append(flags, node.ref.String()) + } + if node.next != nil { + flags = append(flags, fmt.Sprintf("next:%p", node.next)) + } + if node.any != nil { + flags = append(flags, fmt.Sprintf("any:%p", node.any)) + } + if node.undefined != nil { + flags = append(flags, fmt.Sprintf("undefined:%p", node.undefined)) + } + if node.array != nil { + flags = append(flags, fmt.Sprintf("array:%p", node.array)) + } + if node.scalars.Len() > 0 { + buf := make([]string, 0, node.scalars.Len()) + node.scalars.Iter(func(key Value, val *trieNode) bool { + buf = append(buf, fmt.Sprintf("scalar(%v):%p", key, val)) + return false + }) + sort.Strings(buf) + flags = append(flags, strings.Join(buf, " ")) + } + if len(node.rules) > 0 { + flags = append(flags, fmt.Sprintf("%d rule(s)", len(node.rules))) + } + if len(node.mappers) > 0 { + flags = append(flags, fmt.Sprintf("%d mapper(s)", len(node.mappers))) + } + if node.value != nil { + flags = append(flags, "value exists") + } + return strings.Join(flags, " ") +} + +func (node *trieNode) append(prio [2]int, rule *Rule) { + node.rules = append(node.rules, &ruleNode{prio, rule}) + + if node.value != nil && rule.Head.Value != nil && !node.value.Equal(rule.Head.Value) { + node.multiple = true + } + + if node.value == nil && rule.Head.DocKind() == CompleteDoc { + node.value = rule.Head.Value + } +} + +type ruleNode struct { + prio [2]int + rule *Rule +} + +func newTrieNodeImpl() *trieNode { + return &trieNode{ + scalars: util.NewHasherMap[Value, *trieNode](ValueEqual), + } +} + +func (node *trieNode) Do(walker trieWalker) { + next := walker.Do(node) + if next == nil { + return + } + if node.any != nil { + node.any.Do(next) + } + if node.undefined != nil { + node.undefined.Do(next) + } + + node.scalars.Iter(func(_ Value, child *trieNode) bool { + child.Do(next) + return false + }) + + if node.array != nil { + node.array.Do(next) + } + if node.next != nil { + node.next.Do(next) + } +} + +func (node *trieNode) Insert(ref Ref, value Value, mapper *valueMapper) *trieNode { + + if node.next == nil { + node.next = newTrieNodeImpl() + node.next.ref = ref + } + + if mapper != nil { + node.next.addMapper(mapper) + } + + return node.next.insertValue(value) +} + +func (node *trieNode) Traverse(resolver ValueResolver, tr *trieTraversalResult) error { + + if node == nil { + return nil + } + + tr.Add(node) + + return node.next.traverse(resolver, tr) +} + +func (node *trieNode) addMapper(mapper *valueMapper) { + for i := range node.mappers { + if node.mappers[i].Key == mapper.Key { + return + } + } + node.mappers = append(node.mappers, mapper) +} + +func (node *trieNode) insertValue(value Value) *trieNode { + + switch value := value.(type) { + case nil: + if node.undefined == nil { + node.undefined = newTrieNodeImpl() + } + return node.undefined + case Var: + if node.any == nil { + node.any = newTrieNodeImpl() + } + return node.any + case Null, Boolean, Number, String: + child, ok := node.scalars.Get(value) + if !ok { + child = newTrieNodeImpl() + node.scalars.Put(value, child) + } + return child + case *Array: + if node.array == nil { + node.array = newTrieNodeImpl() + } + return node.array.insertArray(value) + } + + panic("illegal value") +} + +func (node *trieNode) insertArray(arr *Array) *trieNode { + + if arr.Len() == 0 { + return node + } + + switch head := arr.Elem(0).Value.(type) { + case Var: + if node.any == nil { + node.any = newTrieNodeImpl() + } + return node.any.insertArray(arr.Slice(1, -1)) + case Null, Boolean, Number, String: + child, ok := node.scalars.Get(head) + if !ok { + child = newTrieNodeImpl() + node.scalars.Put(head, child) + } + return child.insertArray(arr.Slice(1, -1)) + } + + panic("illegal value") +} + +func (node *trieNode) traverse(resolver ValueResolver, tr *trieTraversalResult) error { + + if node == nil { + return nil + } + + v, err := resolver.Resolve(node.ref) + if err != nil { + if IsUnknownValueErr(err) { + return node.traverseUnknown(resolver, tr) + } + return err + } + + if node.undefined != nil { + err = node.undefined.Traverse(resolver, tr) + if err != nil { + return err + } + } + + if v == nil { + return nil + } + + if node.any != nil { + err = node.any.Traverse(resolver, tr) + if err != nil { + return err + } + } + + if err := node.traverseValue(resolver, tr, v); err != nil { + return err + } + + for i := range node.mappers { + if err := node.traverseValue(resolver, tr, node.mappers[i].MapValue(v)); err != nil { + return err + } + } + + return nil +} + +func (node *trieNode) traverseValue(resolver ValueResolver, tr *trieTraversalResult, value Value) error { + + switch value := value.(type) { + case *Array: + if node.array == nil { + return nil + } + return node.array.traverseArray(resolver, tr, value) + + case Null, Boolean, Number, String: + child, ok := node.scalars.Get(value) + if !ok { + return nil + } + return child.Traverse(resolver, tr) + } + + return nil +} + +func (node *trieNode) traverseArray(resolver ValueResolver, tr *trieTraversalResult, arr *Array) error { + + if arr.Len() == 0 { + return node.Traverse(resolver, tr) + } + + if node.any != nil { + err := node.any.traverseArray(resolver, tr, arr.Slice(1, -1)) + if err != nil { + return err + } + } + + head := arr.Elem(0).Value + + if !IsScalar(head) { + return nil + } + + switch head := head.(type) { + case Null, Boolean, Number, String: + child, ok := node.scalars.Get(head) + if !ok { + return nil + } + return child.traverseArray(resolver, tr, arr.Slice(1, -1)) + } + + panic("illegal value") +} + +func (node *trieNode) traverseUnknown(resolver ValueResolver, tr *trieTraversalResult) error { + + if node == nil { + return nil + } + + if err := node.Traverse(resolver, tr); err != nil { + return err + } + + if err := node.undefined.traverseUnknown(resolver, tr); err != nil { + return err + } + + if err := node.any.traverseUnknown(resolver, tr); err != nil { + return err + } + + if err := node.array.traverseUnknown(resolver, tr); err != nil { + return err + } + + var iterErr error + node.scalars.Iter(func(_ Value, child *trieNode) bool { + return child.traverseUnknown(resolver, tr) != nil + }) + + return iterErr +} + +// If term `a` is one of the function's operands, we store a Ref: `args[0]` +// for the argument number. So for `f(x, y) { x = 10; y = 12 }`, we'll +// bind `args[0]` and `args[1]` to this rule when called for (x=10) and +// (y=12) respectively. +func eqOperandsToRefAndValue(isVirtual func(Ref) bool, args []*Term, a, b *Term) (*refindex, bool) { + switch v := a.Value.(type) { + case Var: + for i, arg := range args { + if arg.Value.Compare(a.Value) == 0 { + if bval, ok := indexValue(b); ok { + return &refindex{Ref: Ref{FunctionArgRootDocument, InternedTerm(i)}, Value: bval}, true + } + } + } + case Ref: + if !RootDocumentNames.Contains(v[0]) { + return nil, false + } + if isVirtual(v) { + return nil, false + } + if v.IsNested() || !v.IsGround() { + return nil, false + } + if bval, ok := indexValue(b); ok { + return &refindex{Ref: v, Value: bval}, true + } + } + return nil, false +} + +func indexValue(b *Term) (Value, bool) { + switch b := b.Value.(type) { + case Null, Boolean, Number, String, Var: + return b, true + case *Array: + stop := false + first := true + vis := NewGenericVisitor(func(x any) bool { + if first { + first = false + return false + } + switch x.(type) { + // No nested structures or values that require evaluation (other than var). + case *Array, Object, Set, *ArrayComprehension, *ObjectComprehension, *SetComprehension, Ref: + stop = true + } + return stop + }) + vis.Walk(b) + if !stop { + return b, true + } + } + + return nil, false +} + +func globDelimiterToString(delim *Term) (string, bool) { + + arr, ok := delim.Value.(*Array) + if !ok { + return "", false + } + + var result string + + if arr.Len() == 0 { + result = "." + } else { + for i := range arr.Len() { + term := arr.Elem(i) + s, ok := term.Value.(String) + if !ok { + return "", false + } + result += string(s) + } + } + + return result, true +} + +var globwildcard = VarTerm("$globwildcard") + +func globPatternToArray(pattern *Term, delim string) *Term { + + s, ok := pattern.Value.(String) + if !ok { + return nil + } + + parts := splitStringEscaped(string(s), delim) + arr := make([]*Term, len(parts)) + + for i := range parts { + if parts[i] == "*" { + arr[i] = globwildcard + } else { + var escaped bool + for _, c := range parts[i] { + if c == '\\' { + escaped = !escaped + continue + } + if !escaped { + switch c { + case '[', '?', '{', '*': + // TODO(tsandall): super glob and character pattern + // matching not supported yet. + return nil + } + } + escaped = false + } + arr[i] = StringTerm(parts[i]) + } + } + + return ArrayTerm(arr...) +} + +// splits s on characters in delim except if delim characters have been escaped +// with reverse solidus. +func splitStringEscaped(s string, delim string) []string { + + var last, curr int + var escaped bool + var result []string + + for ; curr < len(s); curr++ { + if s[curr] == '\\' || escaped { + escaped = !escaped + continue + } + if strings.ContainsRune(delim, rune(s[curr])) { + result = append(result, s[last:curr]) + last = curr + 1 + } + } + + result = append(result, s[last:]) + + return result +} + +func stringSliceToArray(s []string) *Array { + arr := make([]*Term, len(s)) + for i, v := range s { + arr[i] = StringTerm(v) + } + return NewArray(arr...) +} diff --git a/third_party/opa/v1/ast/index_test.go b/third_party/opa/v1/ast/index_test.go new file mode 100644 index 000000000000..477ad53e7142 --- /dev/null +++ b/third_party/opa/v1/ast/index_test.go @@ -0,0 +1,1403 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "errors" + "testing" +) + +type testResolver struct { + input *Term + failRef Ref + unknownRefs Set + args []Value +} + +func (r testResolver) Resolve(ref Ref) (Value, error) { + if ref[0].Equal(FunctionArgRootDocument) { + if v, ok := ref[1].Value.(Number); ok { + if i, ok := v.Int(); ok && 0 <= i && i < len(r.args) { + return r.args[i], nil + } + } + } + if r.unknownRefs != nil && r.unknownRefs.Contains(NewTerm(ref)) { + return nil, UnknownValueErr{} + } + if ref.Equal(r.failRef) { + return nil, errors.New("some error") + } + if ref.HasPrefix(InputRootRef) { + v, err := r.input.Value.Find(ref[1:]) + if err != nil { + return nil, nil + } + return v, nil + } + panic("illegal value") +} + +func TestBaseDocEqIndexing(t *testing.T) { + opts := ParserOptions{AllFutureKeywords: true, unreleasedKeywords: true} + + expectOnlyGroundRefs := func(exp bool) func(*testing.T, *IndexResult) { + return func(t *testing.T, res *IndexResult) { + t.Helper() + if act := res.OnlyGroundRefs; exp != act { + t.Errorf("OnlyGroundRefs: expected %v, got %v", exp, act) + } + } + } + + everyMod := MustParseModuleWithOpts(`package test + p if { every _ in [] { input.a = 1 } }`, opts) + + // NOTE(sr): This looks a bit silly; but it's what + // + // every x in input.a { input.x == x } + // + // will get rewritten to -- so to assert that the domain of 'every' expressions + // get respected in the rule indexing, we'll need to provide this "pseudo-compiled" + // mod source here. + everyModWithDomain := MustParseModuleWithOpts(`package test + p if { + __local0__ = input.a + every x in __local0__ { input.x = x } + } { + input.b = 1 + }`, opts) + + refMod := MustParseModuleWithOpts(`package test + + ref.single.value.ground = x if x := input.x + + ref.single.value.key[k] = v if { k := input.k; v := input.v } + + ref.multi.value.ground contains x if x := input.x + + ref.multiple.single.value.ground = x if x := input.x + ref.multiple.single.value[y] = x if { x := input.x; y := index.y } + + # ref.multi.value.key[k] contains v if { k := input.k; v := input.v } # not supported yet + `, opts) + + mod := module(` + package test + + exact if { + input.x = 1 + input.y = 2 + } { + input.x = 3 + input.y = 4 + } + + + scalars if { + input.x = 0 + input.y = 1 + } { + 1 = input.y # exercise ordering + input.x = 0 + } { + input.y = 2 + input.z = 2 + } { + input.x = 2 + } + + vars if { + input.x = 1 + input.y = 2 + } { + input.x = x + input.y = 3 + } { + input.x = 4 + input.z = 5 + } + + composite_arr if { + input.x = 1 + input.y = [1,2,3] + input.z = 1 + } { + input.x = 1 + input.y = [1,2,4,x] + } { + input.y = [1,2,y,5] + input.z = 3 + } { + input.y = [] + } { + # Must be included in all results as nested composites are not indexed. + input.y = [1,[2,3],4] + } + + composite_obj if { + input.y = {"foo": "bar", "bar": x} + } + + equal if { + input.x == 1 + } { + input.x == 2 + } { + input.y == 3 + } + + # filtering ruleset contains rules that cannot be indexed (for different reasons). + filtering if { + count([], x) + } { + not input.x = 0 + } { + x = [1,2,3] + x[0] = 1 + } { + input.x[_] = 1 + } { + input.x[input.y] = 1 + } { + # include one rule that can be indexed to exercise merging of root non-indexable + # rules with other rules. + input.x = 1 + } { + input.foo = "bar" with data.baz as "qux" + } + + # exercise default keyword + default allow = false + allow if { + input.x = 1 + } { + input.x = 0 + } + + glob_match if { + x = input.x + glob.match("foo:*:bar", [":"], x) + } { + x = input.x + glob.match("foo:*:baz", [":"], x) + } { + x = input.x + glob.match("foo:*:*", [":"], x) + } { + x = input.x + glob.match("dead:*:beef", [":"], x) + } + + glob_match_mappers if { + input.x = x + glob.match("foo:*", [":"], x) + } + + glob_match_mappers if { + input.x = x + } + + glob_match_mappers_non_mapped_match if { + input.x = "/bar" + } + + glob_match_mappers_non_mapped_match if { + input.x = x + glob.match("bar", ["/"], x) + } + + glob_match_overlapped_mappers if { + input.x = x + glob.match("foo:*", [":"], x) + } + + glob_match_overlapped_mappers if { + input.x = x + glob.match("foo/*", ["/"], x) + } + + glob_match_disjoint_mappers if { + input.x = x + glob.match("foo:*", [":"], x) + } + + glob_match_disjoint_mappers if { + input.x = x + glob.match("bar/*", ["/"], x) + } + `) + + tests := []struct { + note string + module *Module + ruleset string + ruleRef Ref + input string + unknowns []string + args []Value + expectedRS any + expectedDR *Rule + checkResult func(*testing.T, *IndexResult) + }{ + { + note: "exact match", + ruleset: "exact", + input: `{"x": 3, "y": 4}`, + expectedRS: []string{ + `exact if { input.x = 3; input.y = 4 }`, + }, + checkResult: expectOnlyGroundRefs(true), // covering base case + }, + { + note: "undefined match", + ruleset: "scalars", + input: `{"x": 2, "y": 2}`, + expectedRS: []string{ + `scalars if { input.x = 2 }`}, + }, + { + note: "disjoint match", + ruleset: "scalars", + input: `{"x": 2, "y": 2, "z": 2}`, + expectedRS: []string{ + `scalars if { input.x = 2 }`, + `scalars if { input.y = 2; input.z = 2}`}, + }, + { + note: "ordering match", + ruleset: "scalars", + input: `{"x": 0, "y": 1}`, + expectedRS: []string{ + `scalars if { input.x = 0; input.y = 1 }`, + `scalars if { 1 = input.y; input.x = 0 }`}, + }, + { + note: "type no match", + ruleset: "vars", + input: `{"y": 3, "x": {1,2,3}}`, + expectedRS: []string{ + `vars if { input.x = x; input.y = 3 }`, + }, + }, + { + note: "var match", + ruleset: "vars", + input: `{"x": 1, "y": 3}`, + expectedRS: []string{ + `vars if { input.x = x; input.y = 3 }`, + }, + }, + { + note: "var match disjoint", + ruleset: "vars", + input: `{"x": 4, "z": 5, "y": 3}`, + expectedRS: []string{ + `vars if { input.x = x; input.y = 3 }`, + `vars if { input.x = 4; input.z = 5 }`, + }, + }, + { + note: "array match", + ruleset: "composite_arr", + input: `{ + "x": 1, + "y": [1,2,3], + "z": 1, + }`, + expectedRS: []string{ + `composite_arr if { input.x = 1; input.y = [1,2,3]; input.z = 1 }`, + `composite_arr if { input.y = [1,[2,3],4] }`, + }, + }, + { + note: "array var match", + ruleset: "composite_arr", + input: `{ + "x": 1, + "y": [1,2,4,5], + }`, + expectedRS: []string{ + `composite_arr if { input.x = 1; input.y = [1,2,4,x] }`, + `composite_arr if { input.y = [1,[2,3],4] }`, + }, + }, + { + note: "array var multiple match", + ruleset: "composite_arr", + input: `{ + "x": 1, + "y": [1,2,4,5], + "z": 3, + }`, + expectedRS: []string{ + `composite_arr if { input.x = 1; input.y = [1,2,4,x] }`, + `composite_arr if { input.y = [1,2,y,5]; input.z = 3 }`, + `composite_arr if { input.y = [1,[2,3],4] }`, + }, + }, + { + note: "array nested match non-indexable rules", + ruleset: "composite_arr", + input: `{ + "x": 1, + "y": [1,[2,3],4], + }`, + expectedRS: []string{ + `composite_arr if { input.y = [1,[2,3],4] }`, + }, + }, + { + note: "array empty match", + ruleset: "composite_arr", + input: `{"y": []}`, + expectedRS: []string{ + `composite_arr if { input.y = [] }`, + `composite_arr if { input.y = [1,[2,3],4] }`, + }, + }, + { + note: "object match non-indexable rule", + ruleset: "composite_obj", + input: `{"y": {"foo": "bar", "bar": "baz"}}`, + expectedRS: []string{ + `composite_obj if { input.y = {"foo": "bar", "bar": x} }`, + }, + }, + { + note: "match ==", + ruleset: "equal", + input: `{"x": 2, "y": 3}`, + expectedRS: []string{ + "equal if { input.y == 3 }", + "equal if { input.x == 2 }", + }, + }, + { + note: "miss ==", + ruleset: "equal", + input: `{"x": 1000, "y": 1000}`, + expectedRS: []string{}, + }, + { + note: "default rule only", + ruleset: "allow", + input: `{"x": 2}`, + expectedRS: []string{}, + expectedDR: MustParseRule(`default allow = false`), + }, + { + note: "match and default rule", + ruleset: "allow", + input: `{"x": 1}`, + expectedRS: []string{"allow { input.x = 1 }"}, + expectedDR: MustParseRule(`default allow = false`), + }, + { + note: "match and non-indexable rules", + ruleset: "filtering", + input: `{"x": 1}`, + expectedRS: mod.RuleSet(Var("filtering")), + }, + { + note: "non-indexable rules", + ruleset: "filtering", + input: `{}`, + expectedRS: mod.RuleSet(Var("filtering")).Diff(NewRuleSet(MustParseRuleWithOpts(`filtering if { input.x = 1 }`, opts))), + }, + { + note: "unknown: all", + ruleset: "composite_arr", + unknowns: []string{`input.x`, `input.y`, `input.z`}, + expectedRS: mod.RuleSet(Var("composite_arr")), + }, + { + note: "unknown: partial", + ruleset: "composite_arr", + unknowns: []string{`input.x`, `input.y`}, + input: `{"z": 3}`, + expectedRS: mod.RuleSet(Var("composite_arr")).Diff(NewRuleSet(MustParseRuleWithOpts(`composite_arr if { + input.x = 1 + input.y = [1,2,3] + input.z = 1 + }`, opts))), + }, + { + note: "glob.match", + ruleset: "glob_match", + input: `{"x": "foo:1234:bar"}`, + expectedRS: []string{` + glob_match if { + x = input.x + glob.match("foo:*:bar", [":"], x) + }`, ` + glob_match if { + x = input.x + glob.match("foo:*:*", [":"], x) + }`}, + }, + { + note: "glob.match - mapper and no mapper", + ruleset: "glob_match_mappers", + input: `{"x": "foo:bar"}`, + expectedRS: []string{ + ` + glob_match_mappers if { + input.x = x + glob.match("foo:*", [":"], x) + } + `, + ` + glob_match_mappers if { + input.x = x + } + `}, + }, + { + note: "glob.match - mapper and no mapper, non-mapped value matches", + ruleset: "glob_match_mappers_non_mapped_match", + input: `{"x": "/bar"}`, + expectedRS: []string{ + `glob_match_mappers_non_mapped_match if { + input.x = "/bar" + }`}, + }, + { + // NOTE(tsandall): The rule index returns both rules because the trie nodes + // store multiple mappers and will traverse each one. Since both mappers + // generate a trie structure of: + // + // array + // scalar("foo") + // any + // + // The rules are added to the same leaf node. In the future, we could improve + // the indexer to distinguish the trie nodes using the delimiter but until + // then the indexer can just return extra rules. + note: "glob.match - multiple overlapped mappers", + ruleset: "glob_match_overlapped_mappers", + input: `{"x": "foo:bar"}`, + expectedRS: []string{ + ` + glob_match_overlapped_mappers if { + input.x = x + glob.match("foo:*", [":"], x) + } + `, ` + glob_match_overlapped_mappers if { + input.x = x + glob.match("foo/*", ["/"], x) + } + `, + }, + }, + { + note: "glob.match - multiple disjoint mappers", + ruleset: "glob_match_disjoint_mappers", + input: `{"x": "foo:bar"}`, + expectedRS: []string{ + `glob_match_disjoint_mappers if { input.x = x; glob.match("foo:*", [":"], x) }`, + }, + }, + { + note: "glob.match unexpected value type", + ruleset: "glob_match", + input: `{"x": [0]}`, + expectedRS: []string{}, + }, + { + note: "glob.match: do not index captured output", + module: module(`package test + p if { x = input.x; glob.match("/a/*/c", ["/"], x, false) } + `), + ruleset: "p", + input: `{"x": "wrong"}`, + expectedRS: []string{ + `p if { x = input.x; glob.match("/a/*/c", ["/"], x, false) }`, + }, + }, + { + note: "functions: args match", + module: module(`package test + f(x) = y if { + input.a = "foo" + x = 10 + y := 10 + } + f(x) = 12 if { x = 11 } + f(x) = x+1 if { + input.a = x + x != 10 + x != 11 + }`), + ruleset: "f", + input: `{"a": "foo"}`, + args: []Value{Number("11")}, + expectedRS: []string{ + `f(x) = 12 if { x = 11 } `, + `f(x) = plus(x, 1) if { input.a = x; neq(x, 10); neq(x, 11) }`, // neq not respected in index + }, + }, + { + note: "functions: input + args match", + module: module(`package test + f(x) = y if { + input.a = "foo" + x = 10 + y := 10 + } + f(x) = 12 if { x = 11 } + f(x) = x+1 if { + input.a = x + x != 10 + x != 11 + }`), + ruleset: "f", + input: `{"a": "foo"}`, + args: []Value{Number("10")}, + expectedRS: []string{ + `f(x) = y if { input.a = "foo"; x = 10; assign(y, 10) }`, + `f(x) = plus(x, 1) if { input.a = x; neq(x, 10); neq(x, 11) }`, // neq not respected in index + }, + }, + { + note: "functions: multiple args, each matches", + module: module(`package test + g(x, y) = z if { + x = 12 + y = "monkeys" + z = 1 + } + g(a, b) = c if { + a = "a" + b = "b" + c = "c" + }`), + ruleset: "g", + args: []Value{Number("12"), StringTerm("monkeys").Value}, + expectedRS: []string{ + `g(x, y) = z if { x = 12; y = "monkeys"; z = 1 }`, + }, + }, + { + note: "functions: glob.match in function, arg matching first glob", + module: module(`package test + glob_f(a) = true if { + glob.match("foo:*", [":"], a) + } + glob_f(a) = true if { + glob.match("baz:*", [":"], a) + } + glob_f(a) = true if { + a = 12 + }`), + ruleset: "glob_f", + args: []Value{StringTerm("foo:bar").Value}, + expectedRS: []string{ + `glob_f(a) = true if { glob.match("foo:*", [":"], a) }`, + }, + }, + { + note: "functions: glob.match in function, arg matching second glob", + module: module(`package test + glob_f(a) = true if { + glob.match("foo:*", [":"], a) + } + glob_f(a) = true if { + glob.match("baz:*", [":"], a) + } + glob_f(a) = true if { + a = 12 + }`), + ruleset: "glob_f", + args: []Value{StringTerm("baz:bar").Value}, + expectedRS: []string{ + `glob_f(a) = true if { glob.match("baz:*", [":"], a) }`, + }, + }, + { + note: "functions: glob.match in function, arg matching non-glob rule", + module: module(`package test + glob_f(a) = true if { + glob.match("baz:*", [":"], a) + } + glob_f(a) = true if { + a = 12 + }`), + ruleset: "glob_f", + args: []Value{Number("12")}, + expectedRS: []string{ + `glob_f(a) = true if { a = 12 }`, + }, + }, + { + note: "functions: multiple outputs for same inputs", + module: module(`package test + f(x) = y if { a = x; equal(a, 1, r); y = r } + f(x) = y if { a = x; equal(a, 2, r); y = r }`), + ruleset: "f", + input: `{}`, + args: []Value{Number("1")}, + expectedRS: []string{ + `f(x) = y if { a = x; equal(a, 1, r); y = r }`, + `f(x) = y if { a = x; equal(a, 2, r); y = r }`, + }, + }, + { + note: "functions: do not index equal(x,y,z)", + module: module(`package test + f(x) = y if { equal(x, 1, z); y = z } + `), + ruleset: "f", + input: `{}`, + args: []Value{Number("2")}, + expectedRS: []string{ + `f(x) = y if { equal(x, 1, z); y = z }`, + }, + }, + { + note: "every: do not index body", + module: everyMod, + ruleset: "p", + input: `{"a": 2}`, + expectedRS: RuleSet(everyMod.Rules), + }, + { + note: "every: index domain", + module: everyModWithDomain, + ruleset: "p", + input: `{"a": [1]}`, + expectedRS: RuleSet([]*Rule{everyModWithDomain.Rules[0]}), + }, + { + note: "ref: single value, ground ref", + module: refMod, + ruleRef: MustParseRef("ref.single.value.ground"), + input: `{"x": 1}`, + expectedRS: RuleSet([]*Rule{refMod.Rules[0]}), + checkResult: expectOnlyGroundRefs(true), + }, + { + note: "ref: single value, ground ref and non-ground ref", + module: refMod, + ruleRef: MustParseRef("ref.multiple.single.value"), + input: `{"x": 1, "y": "Y"}`, + expectedRS: RuleSet([]*Rule{refMod.Rules[3], refMod.Rules[4]}), + checkResult: expectOnlyGroundRefs(false), + }, + { + note: "ref: single value, var in ref", + module: refMod, + ruleRef: MustParseRef("ref.single.value.key[k]"), + input: `{"k": 1, "v": 2}`, + expectedRS: RuleSet([]*Rule{refMod.Rules[1]}), + checkResult: expectOnlyGroundRefs(false), + }, + { + note: "ref: multi value, ground ref", + module: refMod, + ruleRef: MustParseRef("ref.multi.value.ground"), + input: `{"x": 1}`, + expectedRS: RuleSet([]*Rule{refMod.Rules[2]}), + checkResult: expectOnlyGroundRefs(true), + }, + // { + // note: "ref: multi value, var in ref", + // mod: refMod, + // ruleRef: MustParseRef("ref.multi.value.key[k]"), + // input: `{"k": 1, "v": 2}`, + // expectedRS: RuleSet([]*Rule{refMod.Rules[3]}), + // }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + mod := mod + if tc.module != nil { + mod = tc.module + } + rules := []*Rule{} + for _, rule := range mod.Rules { + if tc.ruleRef == nil { + if rule.Head.Name == Var(tc.ruleset) { + rules = append(rules, rule) + } + } else { + if rule.Head.Ref().HasPrefix(tc.ruleRef) { + rules = append(rules, rule) + } + } + } + if len(rules) == 0 { + t.Fatal("selected empty ruleset") + } + + var input *Term + if tc.input != "" { + input = MustParseTerm(tc.input) + } + + var expectedRS RuleSet + + switch e := tc.expectedRS.(type) { + case []string: + for _, r := range e { + expectedRS.Add(MustParseRuleWithOpts(r, opts)) + } + case RuleSet: + expectedRS = e + default: + panic("Unexpected test case: expected value") + } + + index := newBaseDocEqIndex(func(Ref) bool { + return false + }) + + if !index.Build(rules) { + t.Fatalf("Expected index build to succeed") + } + + var unknownRefs Set + + if len(tc.unknowns) > 0 { + unknownRefs = NewSet() + for _, s := range tc.unknowns { + unknownRefs.Add(MustParseTerm(s)) + } + } + + result, err := index.Lookup(testResolver{input: input, unknownRefs: unknownRefs, args: tc.args}) + if err != nil { + t.Fatalf("Unexpected error during index lookup: %v", err) + } + + if tc.checkResult != nil { + tc.checkResult(t, result) + } + + if !NewRuleSet(result.Rules...).Equal(expectedRS) { + t.Fatalf("Expected ruleset %v but got: %v", expectedRS, result.Rules) + } + + if result.Default == nil && tc.expectedDR != nil { + t.Fatalf("Expected default rule but got nil") + } else if result.Default != nil && tc.expectedDR == nil { + t.Fatalf("Unexpected default rule %v", result.Default) + } else if result.Default != nil && tc.expectedDR != nil && !result.Default.Equal(tc.expectedDR) { + t.Fatalf("Expected default rule %v but got: %v", tc.expectedDR, result.Default) + } + }) + } + +} + +func TestBaseDocEqIndexingPriorities(t *testing.T) { + + module := module(` + package test + + p if { # r1 + false + } else if { # r2 + input.x = "x1" + input.y = "y1" + } else if { # r3 + input.z = "z1" + } + + p if { # r4 + input.x = "x1" + } + + p if { # r5 + input.z = "z2" + } else if { # r6 + input.z = "z1" + } + `) + + index := newBaseDocEqIndex(func(Ref) bool { return false }) + + ok := index.Build(module.Rules) + if !ok { + t.Fatalf("Expected index build to succeed") + } + + input := MustParseTerm(`{"x": "x1", "y": "y1", "z": "z1"}`) + + result, err := index.Lookup(testResolver{input: input}) + if err != nil { + t.Fatalf("Unexpected error during index lookup: %v", err) + } + + expectedRules := NewRuleSet( + module.Rules[0], + module.Rules[1], + module.Rules[2].Else) + + expectedElse := map[*Rule]RuleSet{ + module.Rules[0]: []*Rule{ + module.Rules[0].Else, + module.Rules[0].Else.Else, + }, + } + + if result.Default != nil { + t.Fatalf("Expected default rule to be nil") + } + + if !NewRuleSet(result.Rules...).Equal(expectedRules) { + t.Fatalf("Expected rules to be %v but got: %v", expectedRules, result.Rules) + } + + r1 := module.Rules[0] + + if !NewRuleSet(result.Else[r1]...).Equal(expectedElse[r1]) { + t.Fatalf("Expected else to be %v but got: %v", result.Else[r1], expectedElse[r1]) + } +} + +func TestBaseDocEqIndexingErrors(t *testing.T) { + index := newBaseDocEqIndex(func(Ref) bool { + return false + }) + + module := module(` + package ex + + p if { input.raise_error = 1 }`) + + if !index.Build(module.Rules) { + t.Fatalf("Expected index to build") + } + + _, err := index.Lookup(testResolver{ + input: MustParseTerm(`{}`), + failRef: MustParseRef("input.raise_error")}) + + if err == nil || err.Error() != "some error" { + t.Fatalf("Expected error but got: %v", err) + } + + index = newBaseDocEqIndex(func(Ref) bool { return true }) + if index.Build(nil) { + t.Fatalf("Expected index build to fail") + } +} + +func TestSplitStringEscaped(t *testing.T) { + tests := []struct { + input string + delims string + exp []string + }{ + { + input: "foo:bar:baz", + delims: ":", + exp: []string{"foo", "bar", "baz"}, + }, + { + input: ":foo:", + delims: ":", + exp: []string{"", "foo", ""}, + }, + { + input: `foo\:bar`, + delims: ":", + exp: []string{`foo\:bar`}, + }, + { + input: "foo::bar", + delims: ":", + exp: []string{"foo", "", "bar"}, + }, + { + input: "foo:bar.baz", + delims: ":.", + exp: []string{"foo", "bar", "baz"}, + }, + } + + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + result := splitStringEscaped(tc.input, tc.delims) + if len(result) != len(tc.exp) { + t.Fatalf("Expected %v but got %v", tc.exp, result) + } + for i := range result { + if result[i] != tc.exp[i] { + t.Fatalf("Expected %v in pos %v but got %v", tc.exp[i], i, result[i]) + } + } + }) + } +} + +func TestGetAllRules(t *testing.T) { + module := module(` + package test + + default p = 42 + + p if { + input.x = "x1" + input.y = "y1" + } else if { + true + } else if { + input.z = "z1" + } + + p if { + input.z = "z1" + } + `) + + index := newBaseDocEqIndex(func(Ref) bool { return false }) + + ok := index.Build(module.Rules) + if !ok { + t.Fatalf("Expected index build to succeed") + } + + result, err := index.AllRules(testResolver{input: MustParseTerm(`{}`)}) + if err != nil { + t.Fatalf("Unexpected error during index lookup: %v", err) + } + + expectedRules := NewRuleSet( + module.Rules[1], + module.Rules[2]) + + expectedElse := map[*Rule]RuleSet{ + module.Rules[1]: []*Rule{ + module.Rules[1].Else, + module.Rules[1].Else.Else, + }, + } + + if !NewRuleSet(result.Rules...).Equal(expectedRules) { + t.Fatalf("Expected rules to be %v but got: %v", expectedRules, result.Rules) + } + + r1 := module.Rules[1] + + if !NewRuleSet(result.Else[r1]...).Equal(expectedElse[r1]) { + t.Fatalf("Expected else to be %v but got: %v", result.Else[r1], expectedElse[r1]) + } +} + +func TestSkipIndexing(t *testing.T) { + + module := module(`package test + + p if { + internal.print("here") + input.foo = 7 + } else = false if { + input.bar = 8 + } else = true if { + internal.print("here 2") + input.bar = 9 + } + + p if { + input.foo = 9 + }`) + + index := newBaseDocEqIndex(func(Ref) bool { return false }) + + ok := index.Build(module.Rules) + if !ok { + t.Fatal("expected index build to succeed") + } + + result, err := index.Lookup(testResolver{input: MustParseTerm(`{}`)}) + if err != nil { + t.Fatal(err) + } + + expectedRules := NewRuleSet(module.Rules[0]) + expectedElse := map[*Rule][]*Rule{ + module.Rules[0]: {module.Rules[0].Else.Else}, + } + + if !NewRuleSet(result.Rules...).Equal(expectedRules) { + t.Fatalf("Expected rules to be %v but got: %v", expectedRules, result.Rules) + } + + r0 := module.Rules[0] + + if !NewRuleSet(result.Else[r0]...).Equal(expectedElse[r0]) { + t.Fatalf("Expected else to be %v but got: %v", expectedElse[r0], result.Else[r0]) + } +} + +func TestBaseDocIndexResultEarlyExit(t *testing.T) { + + tests := []struct { + note string + module *Module + input string + disableIndexing bool + expectedRS any + expectedDR *Rule + expectedEE bool + }{ + { + note: "single rule", + expectedEE: true, + module: module(`package test +r if { + input.x = 1 +} +r = 3 if { + input.y = 2 +}`), + input: `{"x": 1}`, + expectedRS: []string{ + `r { input.x = 1 }`, + }, + }, + { + note: "no early exit: two rules, indexing disabled", + disableIndexing: true, + expectedEE: false, + module: module(`package test +r if { + input.x = 1 +} +r = 3 if { + input.y = 2 +}`), + input: `{"x": 1}`, + expectedRS: []string{ + `r = 3 { input.y = 2 }`, + `r { input.x = 1 }`, + }, + }, + { + note: "two rules, indexing disabled", + disableIndexing: true, + expectedEE: true, + module: module(`package test +r if { + input.x = 1 +} +r if { + input.y = 2 +}`), + input: `{"x": 1}`, + expectedRS: []string{ + `r { input.y = 2 }`, + `r { input.x = 1 }`, + }, + }, + { + note: "no early exit: different constant value", + expectedEE: false, + module: module(`package test +r if { + input.x = 1 +} +r = 2 if { + input.x = 1 + input.y = 2 +}`), + input: `{"x": 1, "y": 2}`, + expectedRS: []string{ + `r { input.x = 1 }`, + `r = 2 { input.x = 1; input.y = 2 }`, + }, + }, + { + note: "same constant value", + expectedEE: true, + module: module(`package test +r if { + input.x = 1 +} +r if { + input.y = 1 +}`), + input: `{"x": 1, "y": 1}`, + }, + { + note: "no early exit: one rule with with non-constant value", + expectedEE: false, + module: module(`package test +r if { + input.x = 1 +} +r = x if { + input.y = 1 + x = "foo" +}`), + input: `{"x": 1, "y": 1}`, + expectedRS: []string{ + `r { input.x = 1 }`, + `r = x { input.y = 1; x = "foo" }`, + }, + }, + { + note: "same ref value (input)", + expectedEE: true, + module: module(`package test +r = input.a if { + input.x = 1 +} +r = input.a if { + input.y = 1 +}`), + input: `{"x": 1, "y": 1}`, + }, + { + note: "same ref value (data)", + expectedEE: true, + module: module(`package test +r = data.a if { + input.x = 1 +} +r = data.a if { + input.y = 1 +}`), + input: `{"x": 1, "y": 1}`, + }, + { + note: "else: same constant value", + expectedEE: true, + module: module(`package test +r if { + input.x = 1 +} +else if { + true +} +r if { + input.y = 1 +}`), + input: `{"x": 1, "y": 1}`, + }, + { + note: "else: no early exit: different constant value", + expectedEE: false, + module: module(`package test +r if { + input.x = 1 +} +else = false if { + true +} +r if { + input.y = 1 +}`), + input: `{"x": 1, "y": 1}`, + expectedRS: []string{ + `r = true { input.x = 1 } else = false { true }`, + `r = true { input.y = 1 }`, + }, + }, + { + note: "function: single rule", + expectedEE: true, + module: module(`package test +r(x) if { + input.x = x +} +r = 3 if { + input.y = 2 +}`), + input: `{"x": 1}`, + expectedRS: []string{ + `r(x) { input.x = x }`, + }, + }, + { + note: "function: no early exit: different constant value", + expectedEE: false, + module: module(`package test +r(x) if { + input.x = x +} +r(y) = 2 if { + input.x = 1 + input.y = y +}`), + input: `{"x": 1, "y": 2}`, + expectedRS: []string{ + `r(x) { input.x = x }`, + `r(y) = 2 { input.x = 1; input.y = y }`, + }, + }, + { + note: "function: same constant value", + expectedEE: true, + module: module(`package test +r(x) if { + input.x = x +} +r(y) if { + input.y = y +}`), + input: `{"x": 1, "y": 1}`, + }, + { + note: "function: no early exit: one with with non-constant value", + expectedEE: false, + module: module(`package test +r(x) if { + input.x = x +} +r(y) = x if { + input.y = y + x = "foo" +}`), + input: `{"x": 1, "y": 1}`, + }, + { // NOTE(sr): impossible, the compiler rewrites this + note: "function: same ref value (input)", + expectedEE: true, + module: module(`package test +r(x) = input.a if { + input.x = x +} +r(y) = input.a if { + input.y = y +}`), + input: `{"x": 1, "y": 1}`, + }, + { // NOTE(sr): impossible, the compiler rewrites this + note: "function: same ref value (data)", + expectedEE: true, + module: module(`package test +r(x) = data.a if { + input.x = x +} +r(y) = data.a if { + input.y = y +}`), + input: `{"x": 1, "y": 1}`, + }, + + // NOTE(sr): The remaining cases record the limitations of the current implementation: + // Any matching rules whose values contain non-constant values are not compared, and + // cancel early exit. + { + + note: "no early exit: same ref but bound to vars", + expectedEE: false, + module: module(`package test +r = v if { + input.x = 1 + v = input.a +} +r = v if { + input.y = 1 + v = input.a +}`), + input: `{"x": 1, "y": 1, "a": "a"}`, + expectedRS: []string{ + `r = v { input.x = 1; v = input.a }`, + `r = v { input.y = 1; v = input.a }`, + }, + }, + { + note: "no early exit: same value but with non-ground", + expectedEE: false, + module: module(`package test +r = [1, {"a": v}] if { + input.x = 1 + v = "a" +} +r = [1, {"a": v}] if { + input.y = 1 + v = "a" +}`), + input: `{"x": 1, "y": 1}`, + expectedRS: []string{ + `r = [1, {"a": v}] { input.y = 1; v = "a" }`, + `r = [1, {"a": v}] { input.x = 1; v = "a" }`, + }, + }, + { + note: "no early exit: one rule, set comprehension value", + expectedEE: false, + // NOTE(sr): this is what the indexer gets after rewriting + // r = { i | i := data.arr[i] } { true } + module: module(`package test +r = local0 if { + local0 = {i | i := data.arr[i]} +}`), + input: `{}`, + expectedRS: []string{ + `r = local0 { local0 = {i | i := data.arr[i]} }`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + rules := []*Rule{} + for _, rule := range tc.module.Rules { + if rule.Head.Name == Var("r") { + rules = append(rules, rule) + } + } + + var input *Term + if tc.input != "" { + input = MustParseTerm(tc.input) + } + + var expectedRS RuleSet + + switch e := tc.expectedRS.(type) { + case []string: + for _, r := range e { + expectedRS.Add(MustParseRule(r)) + } + case RuleSet: + expectedRS = e + } + + index := newBaseDocEqIndex(func(Ref) bool { + return false + }) + + if !index.Build(rules) { + t.Fatalf("Expected index build to succeed") + } + + var unknownRefs Set + var result *IndexResult + var err error + if tc.disableIndexing { + result, err = index.AllRules(testResolver{input: input, unknownRefs: unknownRefs}) + } else { + result, err = index.Lookup(testResolver{input: input, unknownRefs: unknownRefs}) + } + if err != nil { + t.Fatalf("Unexpected error during index lookup: %v", err) + } + + if tc.expectedRS != nil && !NewRuleSet(result.Rules...).Equal(expectedRS) { + t.Errorf("Expected ruleset %v but got: %v", expectedRS, result.Rules) + } + + if result.Default == nil && tc.expectedDR != nil { + t.Errorf("Expected default rule but got nil") + } else if result.Default != nil && tc.expectedDR == nil { + t.Errorf("Unexpected default rule %v", result.Default) + } else if result.Default != nil && tc.expectedDR != nil && !result.Default.Equal(tc.expectedDR) { + t.Errorf("Expected default rule %v but got: %v", tc.expectedDR, result.Default) + } + + if exp, act := tc.expectedEE, result.EarlyExit; exp != act { + t.Errorf("expected 'early-exit' %v, got %v", exp, act) + } + }) + } +} diff --git a/third_party/opa/v1/ast/internal/scanner/scanner.go b/third_party/opa/v1/ast/internal/scanner/scanner.go new file mode 100644 index 000000000000..3741d371886b --- /dev/null +++ b/third_party/opa/v1/ast/internal/scanner/scanner.go @@ -0,0 +1,478 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package scanner + +import ( + "fmt" + "io" + "unicode" + "unicode/utf8" + + "github.com/open-policy-agent/opa/v1/ast/internal/tokens" + "github.com/open-policy-agent/opa/v1/util" +) + +const bom = 0xFEFF + +// Scanner is used to tokenize an input stream of +// Rego source code. +type Scanner struct { + keywords map[string]tokens.Token + bs []byte + errors []Error + tabs []int + offset int + row int + col int + width int + curr rune + regoV1Compatible bool +} + +// Error represents a scanner error. +type Error struct { + Message string + Pos Position +} + +// Position represents a point in the scanned source code. +type Position struct { + Tabs []int // positions of any tabs preceding Col + 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 +} + +// New returns an initialized scanner that will scan +// through the source code provided by the io.Reader. +func New(r io.Reader) (*Scanner, error) { + + bs, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + s := &Scanner{ + offset: 0, + row: 1, + col: 0, + bs: bs, + curr: -1, + width: 0, + keywords: tokens.Keywords(), + tabs: []int{}, + } + + s.next() + + if s.curr == bom { + s.next() + } + + return s, nil +} + +// Bytes returns the raw bytes for the full source +// which the scanner has read in. +func (s *Scanner) Bytes() []byte { + return s.bs +} + +// String returns a human readable string of the current scanner state. +func (s *Scanner) String() string { + return fmt.Sprintf("", s.curr, s.offset, len(s.bs)) +} + +// Keyword will return a token for the passed in +// literal value. If the value is a Rego keyword +// then the appropriate token is returned. Everything +// else is an Ident. +func (s *Scanner) Keyword(lit string) tokens.Token { + if tok, ok := s.keywords[lit]; ok { + return tok + } + return tokens.Ident +} + +// AddKeyword adds a string -> token mapping to this Scanner instance. +func (s *Scanner) AddKeyword(kw string, tok tokens.Token) { + s.keywords[kw] = tok + + if tok == tokens.Every { + // importing 'every' means also importing 'in' + s.keywords["in"] = tokens.In + } +} + +func (s *Scanner) HasKeyword(keywords map[string]tokens.Token) bool { + for kw := range s.keywords { + if _, ok := keywords[kw]; ok { + return true + } + } + return false +} + +func (s *Scanner) IsKeyword(str string) bool { + _, ok := s.keywords[str] + return ok +} + +func (s *Scanner) SetRegoV1Compatible() { + s.regoV1Compatible = true +} + +func (s *Scanner) RegoV1Compatible() bool { + return s.regoV1Compatible +} + +// WithKeywords returns a new copy of the Scanner struct `s`, with the set +// of known keywords being that of `s` with `kws` added. +func (s *Scanner) WithKeywords(kws map[string]tokens.Token) *Scanner { + cpy := *s + cpy.keywords = make(map[string]tokens.Token, len(s.keywords)+len(kws)) + for kw, tok := range s.keywords { + cpy.AddKeyword(kw, tok) + } + for k, t := range kws { + cpy.AddKeyword(k, t) + } + return &cpy +} + +// WithoutKeywords returns a new copy of the Scanner struct `s`, with the +// set of known keywords being that of `s` with `kws` removed. +// The previously known keywords are returned for a convenient reset. +func (s *Scanner) WithoutKeywords(kws map[string]tokens.Token) (*Scanner, map[string]tokens.Token) { + cpy := *s + kw := s.keywords + cpy.keywords = make(map[string]tokens.Token, len(s.keywords)-len(kws)) + for kw, tok := range s.keywords { + if _, ok := kws[kw]; !ok { + cpy.AddKeyword(kw, tok) + } + } + return &cpy, kw +} + +// Scan will increment the scanners position in the source +// code until the next token is found. The token, starting position +// of the token, string literal, and any errors encountered are +// returned. A token will always be returned, the caller must check +// 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, Tabs: s.tabs} + var tok tokens.Token + var lit string + + if s.isWhitespace() { + // string(rune) is an unnecessary heap allocation in this case as we know all + // the possible whitespace values, and can simply translate to string ourselves + switch s.curr { + case ' ': + lit = " " + case '\t': + lit = "\t" + case '\n': + lit = "\n" + case '\r': + lit = "\r" + default: + // unreachable unless isWhitespace changes + lit = string(s.curr) + } + s.next() + tok = tokens.Whitespace + } else if isLetter(s.curr) { + lit = s.scanIdentifier() + tok = s.Keyword(lit) + } else if isDecimal(s.curr) { + lit = s.scanNumber() + tok = tokens.Number + } else { + ch := s.curr + s.next() + switch ch { + case -1: + tok = tokens.EOF + case '#': + lit = s.scanComment() + tok = tokens.Comment + case '"': + lit = s.scanString() + tok = tokens.String + case '`': + lit = s.scanRawString() + tok = tokens.String + case '[': + tok = tokens.LBrack + case ']': + tok = tokens.RBrack + case '{': + tok = tokens.LBrace + case '}': + tok = tokens.RBrace + case '(': + tok = tokens.LParen + case ')': + tok = tokens.RParen + case ',': + tok = tokens.Comma + case ':': + if s.curr == '=' { + s.next() + tok = tokens.Assign + } else { + tok = tokens.Colon + } + case '+': + tok = tokens.Add + case '-': + tok = tokens.Sub + case '*': + tok = tokens.Mul + case '/': + tok = tokens.Quo + case '%': + tok = tokens.Rem + case '&': + tok = tokens.And + case '|': + tok = tokens.Or + case '=': + if s.curr == '=' { + s.next() + tok = tokens.Equal + } else { + tok = tokens.Unify + } + case '>': + if s.curr == '=' { + s.next() + tok = tokens.Gte + } else { + tok = tokens.Gt + } + case '<': + if s.curr == '=' { + s.next() + tok = tokens.Lte + } else { + tok = tokens.Lt + } + case '!': + if s.curr == '=' { + s.next() + tok = tokens.Neq + } else { + s.error("illegal ! character") + } + case ';': + tok = tokens.Semicolon + case '.': + tok = tokens.Dot + } + } + + pos.End = s.offset - s.width + errs := s.errors + s.errors = nil + + return tok, pos, lit, errs +} + +func (s *Scanner) scanIdentifier() string { + start := s.offset - 1 + for isLetter(s.curr) || isDigit(s.curr) { + s.next() + } + + return util.ByteSliceToString(s.bs[start : s.offset-1]) +} + +func (s *Scanner) scanNumber() string { + + start := s.offset - 1 + + if s.curr != '.' { + for isDecimal(s.curr) { + s.next() + } + } + + if s.curr == '.' { + s.next() + var found bool + for isDecimal(s.curr) { + s.next() + found = true + } + if !found { + s.error("expected fraction") + } + } + + if lower(s.curr) == 'e' { + s.next() + if s.curr == '+' || s.curr == '-' { + s.next() + } + var found bool + for isDecimal(s.curr) { + s.next() + found = true + } + if !found { + s.error("expected exponent") + } + } + + // Scan any digits following the decimals to get the + // entire invalid number/identifier. + // Example: 0a2b should be a single invalid number "0a2b" + // rather than a number "0", followed by identifier "a2b". + if isLetter(s.curr) { + s.error("illegal number format") + for isLetter(s.curr) || isDigit(s.curr) { + s.next() + } + } + + return util.ByteSliceToString(s.bs[start : s.offset-1]) +} + +func (s *Scanner) scanString() string { + start := s.literalStart() + for { + ch := s.curr + + if ch == '\n' || ch < 0 { + s.error("non-terminated string") + break + } + + s.next() + + if ch == '"' { + break + } + + if ch == '\\' { + switch s.curr { + case '\\', '"', '/', 'b', 'f', 'n', 'r', 't': + s.next() + case 'u': + s.next() + s.next() + s.next() + s.next() + default: + s.error("illegal escape sequence") + } + } + } + + return util.ByteSliceToString(s.bs[start : s.offset-1]) +} + +func (s *Scanner) scanRawString() string { + start := s.literalStart() + for { + ch := s.curr + s.next() + if ch == '`' { + break + } else if ch < 0 { + s.error("non-terminated string") + break + } + } + + return util.ByteSliceToString(s.bs[start : s.offset-1]) +} + +func (s *Scanner) scanComment() string { + start := s.literalStart() + for s.curr != '\n' && s.curr != -1 { + s.next() + } + end := s.offset - 1 + // Trim carriage returns that precede the newline + if s.offset > 1 && s.bs[s.offset-2] == '\r' { + end -= 1 + } + + return util.ByteSliceToString(s.bs[start:end]) +} + +func (s *Scanner) next() { + + if s.offset >= len(s.bs) { + s.curr = -1 + s.offset = len(s.bs) + 1 + return + } + + s.curr = rune(s.bs[s.offset]) + s.width = 1 + + if s.curr == 0 { + s.error("illegal null character") + } else if s.curr >= utf8.RuneSelf { + s.curr, s.width = utf8.DecodeRune(s.bs[s.offset:]) + if s.curr == utf8.RuneError && s.width == 1 { + s.error("illegal utf-8 character") + } else if s.curr == bom && s.offset > 0 { + s.error("illegal byte-order mark") + } + } + + s.offset += s.width + + if s.curr == '\n' { + s.row++ + s.col = 0 + s.tabs = s.tabs[:0] + } else { + s.col++ + if s.curr == '\t' { + s.tabs = append(s.tabs, s.col) + } + } +} + +func (s *Scanner) literalStart() int { + // The current offset is at the first character past the literal delimiter (#, ", `, etc.) + // Need to subtract width of first character (plus one for the delimiter). + return s.offset - (s.width + 1) +} + +// From the Go scanner (src/go/scanner/scanner.go) + +func isLetter(ch rune) bool { + return 'a' <= lower(ch) && lower(ch) <= 'z' || ch == '_' +} + +func isDigit(ch rune) bool { + return isDecimal(ch) || ch >= utf8.RuneSelf && unicode.IsDigit(ch) +} + +func isDecimal(ch rune) bool { return '0' <= ch && ch <= '9' } + +func lower(ch rune) rune { return ('a' - 'A') | ch } // returns lower-case ch iff ch is ASCII letter + +func (s *Scanner) isWhitespace() bool { + return s.curr == ' ' || s.curr == '\t' || s.curr == '\n' || s.curr == '\r' +} + +func (s *Scanner) error(reason string) { + s.errors = append(s.errors, Error{Pos: Position{ + Offset: s.offset, + Row: s.row, + Col: s.col, + }, Message: reason}) +} diff --git a/third_party/opa/v1/ast/internal/scanner/scanner_test.go b/third_party/opa/v1/ast/internal/scanner/scanner_test.go new file mode 100644 index 000000000000..b949cbccc1a3 --- /dev/null +++ b/third_party/opa/v1/ast/internal/scanner/scanner_test.go @@ -0,0 +1,205 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package scanner + +import ( + "bytes" + "testing" + + "github.com/open-policy-agent/opa/v1/ast/internal/tokens" +) + +func TestPositions(t *testing.T) { + tests := []struct { + note string + input string + wantOffset int + wantEnd int + }{ + { + note: "symbol", + input: "(", + wantOffset: 0, + wantEnd: 1, + }, + { + note: "ident", + input: "foo", + wantOffset: 0, + wantEnd: 3, + }, + { + note: "number", + input: "100", + wantOffset: 0, + wantEnd: 3, + }, + { + note: "string", + input: `"foo"`, + wantOffset: 0, + wantEnd: 5, + }, + { + note: "string - wide char", + input: `"foo÷"`, + wantOffset: 0, + wantEnd: 7, + }, + { + note: "comment", + input: `# foo`, + wantOffset: 0, + wantEnd: 5, + }, + { + note: "newline", + input: "foo\n", + wantOffset: 0, + wantEnd: 3, + }, + { + note: "invalid number", + input: "0xDEADBEEF", + wantOffset: 0, + wantEnd: 10, + }, + { + note: "invalid identifier", + input: "0.1e12a1b2c3d", + wantOffset: 0, + wantEnd: 13, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + s, err := New(bytes.NewBufferString(tc.input)) + if err != nil { + t.Fatal(err) + } + _, pos, _, _ := s.Scan() + if pos.Offset != tc.wantOffset { + t.Fatalf("want offset %d but got %d", tc.wantOffset, pos.Offset) + } + if pos.End != tc.wantEnd { + t.Fatalf("want end %d but got %d", tc.wantEnd, pos.End) + } + }) + } +} + +func TestLiterals(t *testing.T) { + + tests := []struct { + note string + input string + wantRow int + wantOffset int + wantTok tokens.Token + wantLit string + }{ + { + note: "ascii chars", + input: `"hello world"`, + wantRow: 1, + wantOffset: 0, + wantTok: tokens.String, + wantLit: `"hello world"`, + }, + { + note: "wide chars", + input: `"¡¡¡foo, bar!!!"`, + wantRow: 1, + wantOffset: 0, + wantTok: tokens.String, + wantLit: `"¡¡¡foo, bar!!!"`, + }, + { + note: "raw strings", + input: "`foo`", + wantRow: 1, + wantOffset: 0, + wantTok: tokens.String, + wantLit: "`foo`", + }, + { + note: "raw strings - wide chars", + input: "`¡¡¡foo, bar!!!`", + wantRow: 1, + wantOffset: 0, + wantTok: tokens.String, + wantLit: "`¡¡¡foo, bar!!!`", + }, + { + note: "comments", + input: "# foo", + wantRow: 1, + wantOffset: 0, + wantTok: tokens.Comment, + wantLit: "# foo", + }, + { + note: "comments - wide chars", + input: "#¡foo", + wantRow: 1, + wantOffset: 0, + wantTok: tokens.Comment, + wantLit: "#¡foo", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + s, err := New(bytes.NewBufferString(tc.input)) + if err != nil { + t.Fatal(err) + } + tok, pos, lit, errs := s.Scan() + if pos.Row != tc.wantRow { + t.Errorf("Expected row %d but got %d", tc.wantRow, pos.Row) + } + if pos.Offset != tc.wantOffset { + t.Errorf("Expected offset %d but got %d", tc.wantOffset, pos.Offset) + } + if tok != tc.wantTok { + t.Errorf("Expected token %v but got %v", tc.wantTok, tok) + } + if lit != tc.wantLit { + t.Errorf("Expected literal %v but got %v", tc.wantLit, lit) + } + if len(errs) > 0 { + t.Fatal("Unexpected error(s):", errs) + } + }) + } + +} + +func TestIllegalTokens(t *testing.T) { + + tests := []struct { + input string + wantErr bool + }{ + {input: `墳`}, + {input: `0e`, wantErr: true}, + } + + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + s, err := New(bytes.NewBufferString(tc.input)) + if err != nil { + t.Fatal(err) + } + tok, _, _, errs := s.Scan() + if !tc.wantErr && tok != tokens.Illegal { + t.Fatalf("expected illegal token on %q but got %v", tc.input, tok) + } else if tc.wantErr && len(errs) == 0 { + t.Fatalf("expected errors on %q but got %v", tc.input, tok) + } + }) + } +} diff --git a/third_party/opa/v1/ast/internal/tokens/tokens.go b/third_party/opa/v1/ast/internal/tokens/tokens.go new file mode 100644 index 000000000000..4033ba81ae61 --- /dev/null +++ b/third_party/opa/v1/ast/internal/tokens/tokens.go @@ -0,0 +1,149 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package tokens + +import "maps" + +// Token represents a single Rego source code token +// for use by the Parser. +type Token uint8 + +func (t Token) String() string { + if int(t) >= len(strings) { + return "unknown" + } + return strings[t] +} + +// All tokens must be defined here +const ( + Illegal Token = iota + EOF + Whitespace + Ident + Comment + + Package + Import + As + Default + Else + Not + Some + With + Null + True + False + + Number + String + + LBrack + RBrack + LBrace + RBrace + LParen + RParen + Comma + Colon + + Add + Sub + Mul + Quo + Rem + And + Or + Unify + Equal + Assign + In + Neq + Gt + Lt + Gte + Lte + Dot + Semicolon + + Every + Contains + If +) + +var strings = [...]string{ + Illegal: "illegal", + EOF: "eof", + Whitespace: "whitespace", + Comment: "comment", + Ident: "identifier", + Package: "package", + Import: "import", + As: "as", + Default: "default", + Else: "else", + Not: "not", + Some: "some", + With: "with", + Null: "null", + True: "true", + False: "false", + Number: "number", + String: "string", + LBrack: "[", + RBrack: "]", + LBrace: "{", + RBrace: "}", + LParen: "(", + RParen: ")", + Comma: ",", + Colon: ":", + Add: "plus", + Sub: "minus", + Mul: "mul", + Quo: "div", + Rem: "rem", + And: "and", + Or: "or", + Unify: "eq", + Equal: "equal", + Assign: "assign", + In: "in", + Neq: "neq", + Gt: "gt", + Lt: "lt", + Gte: "gte", + Lte: "lte", + Dot: ".", + Semicolon: ";", + Every: "every", + Contains: "contains", + If: "if", +} + +var keywords = map[string]Token{ + "package": Package, + "import": Import, + "as": As, + "default": Default, + "else": Else, + "not": Not, + "some": Some, + "with": With, + "null": Null, + "true": True, + "false": False, +} + +// Keywords returns a copy of the default string -> Token keyword map. +func Keywords() map[string]Token { + return maps.Clone(keywords) +} + +// IsKeyword returns if a token is a keyword +func IsKeyword(tok Token) bool { + _, ok := keywords[strings[tok]] + return ok +} diff --git a/third_party/opa/v1/ast/interning.go b/third_party/opa/v1/ast/interning.go new file mode 100644 index 000000000000..564a3cc41f70 --- /dev/null +++ b/third_party/opa/v1/ast/interning.go @@ -0,0 +1,1222 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "strconv" +) + +type internable interface { + bool | string | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 +} + +// NOTE! Great care must be taken **not** to modify the terms returned +// from these functions, as they are shared across all callers. +// This package is currently considered experimental, and may change +// at any time without notice. + +var ( + InternedNullTerm = &Term{Value: Null{}} + + InternedEmptyString = StringTerm("") + InternedEmptyObject = ObjectTerm() + InternedEmptyArray = ArrayTerm() + InternedEmptySet = SetTerm() + + InternedEmptyArrayValue = NewArray() + + booleanTrueTerm = &Term{Value: Boolean(true)} + booleanFalseTerm = &Term{Value: Boolean(false)} + + // since this is by far the most common negative number + minusOneTerm = &Term{Value: Number("-1")} + + internedStringTerms = map[string]*Term{ + "": InternedEmptyString, + } +) + +// InternStringTerm interns the given strings as terms. Note that Interning is +// considered experimental and should not be relied upon by external code. +// WARNING: This must **only** be called at initialization time, as the +// interned terms are shared globally, and the underlying map is not thread-safe. +func InternStringTerm(str ...string) { + for _, s := range str { + if _, ok := internedStringTerms[s]; ok { + continue + } + + internedStringTerms[s] = StringTerm(s) + } +} + +// Interned returns a possibly interned term for the given scalar value. +// If the value is not interned, a new term is created for that value. +func InternedTerm[T internable](v T) *Term { + switch value := any(v).(type) { + case bool: + return internedBooleanTerm(value) + case string: + return internedStringTerm(value) + case int: + return internedIntNumberTerm(value) + case int8: + return internedIntNumberTerm(int(value)) + case int16: + return internedIntNumberTerm(int(value)) + case int32: + return internedIntNumberTerm(int(value)) + case int64: + return internedIntNumberTerm(int(value)) + case uint: + return internedIntNumberTerm(int(value)) + case uint8: + return internedIntNumberTerm(int(value)) + case uint16: + return internedIntNumberTerm(int(value)) + case uint32: + return internedIntNumberTerm(int(value)) + case uint64: + return internedIntNumberTerm(int(value)) + default: + panic("unreachable") + } +} + +// InternedIntFromString returns a term with the given integer value if the string +// maps to an interned term. If the string does not map to an interned term, nil is +// returned. +func InternedIntNumberTermFromString(s string) *Term { + if term, ok := stringToIntNumberTermMap[s]; ok { + return term + } + + return nil +} + +// HasInternedIntNumberTerm returns true if the given integer value maps to an interned +// term, otherwise false. +func HasInternedIntNumberTerm(i int) bool { + return i >= -1 && i < len(intNumberTerms) +} + +// Returns an interned string term representing the integer value i, if +// interned. If not, creates a new StringTerm for the integer value. +func InternedIntegerString(i int) *Term { + // Cheapest option - we don't need to call strconv.Itoa + if HasInternedIntNumberTerm(i) { + if interned, ok := internedStringTerms[IntNumberTerm(i).String()]; ok { + return interned + } + } + + // Next cheapest option — the string could still be interned if the store + // has been extended with more terms than we cucrrently intern. + s := strconv.Itoa(i) + if interned, ok := internedStringTerms[s]; ok { + return interned + } + + // Nope, create a new term + return StringTerm(s) +} + +// InternedBooleanTerm returns an interned term with the given boolean value. +func internedBooleanTerm(b bool) *Term { + if b { + return booleanTrueTerm + } + + return booleanFalseTerm +} + +// InternedIntNumberTerm returns a term with the given integer value. The term is +// cached between -1 to 512, and for values outside of that range, this function +// is equivalent to IntNumberTerm. +func internedIntNumberTerm(i int) *Term { + if i >= 0 && i < len(intNumberTerms) { + return intNumberTerms[i] + } + + if i == -1 { + return minusOneTerm + } + + return &Term{Value: Number(strconv.Itoa(i))} +} + +// InternedStringTerm returns an interned term with the given string value. If the +// provided string is not interned, a new term is created for that value. It does *not* +// modify the global interned terms map. +func internedStringTerm(s string) *Term { + if term, ok := internedStringTerms[s]; ok { + return term + } + + return StringTerm(s) +} + +func init() { + InternStringTerm( + // Numbers + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", + "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", + "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", + "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", + "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", + "93", "94", "95", "96", "97", "98", "99", "100", + // Types + "null", "boolean", "number", "string", "array", "object", "set", "var", "ref", "true", "false", + // Runtime + "config", "env", "version", "commit", "authorization_enabled", "skip_known_schema_check", + // Annotations + "annotations", "scope", "title", "entrypoint", "description", "organizations", "authors", "related_resources", + "schemas", "custom", "name", "email", "schema", "definition", "document", "package", "rule", "subpackages", + // Debug + "text", "value", "bindings", "expressions", + // Various + "data", "input", "result", "keywords", "path", "v1", "error", "partial", + // HTTP + "code", "message", "status_code", "method", "url", "uri", + // JWT + "enc", "cty", "iss", "exp", "nbf", "aud", "secret", "cert", + // Decisions + "revision", "labels", "decision_id", "bundles", "query", "mapped_result", "nd_builtin_cache", + "erased", "masked", "requested_by", "timestamp", "metrics", "req_id", + ) +} + +var stringToIntNumberTermMap = map[string]*Term{ + "-1": minusOneTerm, + "0": intNumberTerms[0], + "1": intNumberTerms[1], + "2": intNumberTerms[2], + "3": intNumberTerms[3], + "4": intNumberTerms[4], + "5": intNumberTerms[5], + "6": intNumberTerms[6], + "7": intNumberTerms[7], + "8": intNumberTerms[8], + "9": intNumberTerms[9], + "10": intNumberTerms[10], + "11": intNumberTerms[11], + "12": intNumberTerms[12], + "13": intNumberTerms[13], + "14": intNumberTerms[14], + "15": intNumberTerms[15], + "16": intNumberTerms[16], + "17": intNumberTerms[17], + "18": intNumberTerms[18], + "19": intNumberTerms[19], + "20": intNumberTerms[20], + "21": intNumberTerms[21], + "22": intNumberTerms[22], + "23": intNumberTerms[23], + "24": intNumberTerms[24], + "25": intNumberTerms[25], + "26": intNumberTerms[26], + "27": intNumberTerms[27], + "28": intNumberTerms[28], + "29": intNumberTerms[29], + "30": intNumberTerms[30], + "31": intNumberTerms[31], + "32": intNumberTerms[32], + "33": intNumberTerms[33], + "34": intNumberTerms[34], + "35": intNumberTerms[35], + "36": intNumberTerms[36], + "37": intNumberTerms[37], + "38": intNumberTerms[38], + "39": intNumberTerms[39], + "40": intNumberTerms[40], + "41": intNumberTerms[41], + "42": intNumberTerms[42], + "43": intNumberTerms[43], + "44": intNumberTerms[44], + "45": intNumberTerms[45], + "46": intNumberTerms[46], + "47": intNumberTerms[47], + "48": intNumberTerms[48], + "49": intNumberTerms[49], + "50": intNumberTerms[50], + "51": intNumberTerms[51], + "52": intNumberTerms[52], + "53": intNumberTerms[53], + "54": intNumberTerms[54], + "55": intNumberTerms[55], + "56": intNumberTerms[56], + "57": intNumberTerms[57], + "58": intNumberTerms[58], + "59": intNumberTerms[59], + "60": intNumberTerms[60], + "61": intNumberTerms[61], + "62": intNumberTerms[62], + "63": intNumberTerms[63], + "64": intNumberTerms[64], + "65": intNumberTerms[65], + "66": intNumberTerms[66], + "67": intNumberTerms[67], + "68": intNumberTerms[68], + "69": intNumberTerms[69], + "70": intNumberTerms[70], + "71": intNumberTerms[71], + "72": intNumberTerms[72], + "73": intNumberTerms[73], + "74": intNumberTerms[74], + "75": intNumberTerms[75], + "76": intNumberTerms[76], + "77": intNumberTerms[77], + "78": intNumberTerms[78], + "79": intNumberTerms[79], + "80": intNumberTerms[80], + "81": intNumberTerms[81], + "82": intNumberTerms[82], + "83": intNumberTerms[83], + "84": intNumberTerms[84], + "85": intNumberTerms[85], + "86": intNumberTerms[86], + "87": intNumberTerms[87], + "88": intNumberTerms[88], + "89": intNumberTerms[89], + "90": intNumberTerms[90], + "91": intNumberTerms[91], + "92": intNumberTerms[92], + "93": intNumberTerms[93], + "94": intNumberTerms[94], + "95": intNumberTerms[95], + "96": intNumberTerms[96], + "97": intNumberTerms[97], + "98": intNumberTerms[98], + "99": intNumberTerms[99], + "100": intNumberTerms[100], + "101": intNumberTerms[101], + "102": intNumberTerms[102], + "103": intNumberTerms[103], + "104": intNumberTerms[104], + "105": intNumberTerms[105], + "106": intNumberTerms[106], + "107": intNumberTerms[107], + "108": intNumberTerms[108], + "109": intNumberTerms[109], + "110": intNumberTerms[110], + "111": intNumberTerms[111], + "112": intNumberTerms[112], + "113": intNumberTerms[113], + "114": intNumberTerms[114], + "115": intNumberTerms[115], + "116": intNumberTerms[116], + "117": intNumberTerms[117], + "118": intNumberTerms[118], + "119": intNumberTerms[119], + "120": intNumberTerms[120], + "121": intNumberTerms[121], + "122": intNumberTerms[122], + "123": intNumberTerms[123], + "124": intNumberTerms[124], + "125": intNumberTerms[125], + "126": intNumberTerms[126], + "127": intNumberTerms[127], + "128": intNumberTerms[128], + "129": intNumberTerms[129], + "130": intNumberTerms[130], + "131": intNumberTerms[131], + "132": intNumberTerms[132], + "133": intNumberTerms[133], + "134": intNumberTerms[134], + "135": intNumberTerms[135], + "136": intNumberTerms[136], + "137": intNumberTerms[137], + "138": intNumberTerms[138], + "139": intNumberTerms[139], + "140": intNumberTerms[140], + "141": intNumberTerms[141], + "142": intNumberTerms[142], + "143": intNumberTerms[143], + "144": intNumberTerms[144], + "145": intNumberTerms[145], + "146": intNumberTerms[146], + "147": intNumberTerms[147], + "148": intNumberTerms[148], + "149": intNumberTerms[149], + "150": intNumberTerms[150], + "151": intNumberTerms[151], + "152": intNumberTerms[152], + "153": intNumberTerms[153], + "154": intNumberTerms[154], + "155": intNumberTerms[155], + "156": intNumberTerms[156], + "157": intNumberTerms[157], + "158": intNumberTerms[158], + "159": intNumberTerms[159], + "160": intNumberTerms[160], + "161": intNumberTerms[161], + "162": intNumberTerms[162], + "163": intNumberTerms[163], + "164": intNumberTerms[164], + "165": intNumberTerms[165], + "166": intNumberTerms[166], + "167": intNumberTerms[167], + "168": intNumberTerms[168], + "169": intNumberTerms[169], + "170": intNumberTerms[170], + "171": intNumberTerms[171], + "172": intNumberTerms[172], + "173": intNumberTerms[173], + "174": intNumberTerms[174], + "175": intNumberTerms[175], + "176": intNumberTerms[176], + "177": intNumberTerms[177], + "178": intNumberTerms[178], + "179": intNumberTerms[179], + "180": intNumberTerms[180], + "181": intNumberTerms[181], + "182": intNumberTerms[182], + "183": intNumberTerms[183], + "184": intNumberTerms[184], + "185": intNumberTerms[185], + "186": intNumberTerms[186], + "187": intNumberTerms[187], + "188": intNumberTerms[188], + "189": intNumberTerms[189], + "190": intNumberTerms[190], + "191": intNumberTerms[191], + "192": intNumberTerms[192], + "193": intNumberTerms[193], + "194": intNumberTerms[194], + "195": intNumberTerms[195], + "196": intNumberTerms[196], + "197": intNumberTerms[197], + "198": intNumberTerms[198], + "199": intNumberTerms[199], + "200": intNumberTerms[200], + "201": intNumberTerms[201], + "202": intNumberTerms[202], + "203": intNumberTerms[203], + "204": intNumberTerms[204], + "205": intNumberTerms[205], + "206": intNumberTerms[206], + "207": intNumberTerms[207], + "208": intNumberTerms[208], + "209": intNumberTerms[209], + "210": intNumberTerms[210], + "211": intNumberTerms[211], + "212": intNumberTerms[212], + "213": intNumberTerms[213], + "214": intNumberTerms[214], + "215": intNumberTerms[215], + "216": intNumberTerms[216], + "217": intNumberTerms[217], + "218": intNumberTerms[218], + "219": intNumberTerms[219], + "220": intNumberTerms[220], + "221": intNumberTerms[221], + "222": intNumberTerms[222], + "223": intNumberTerms[223], + "224": intNumberTerms[224], + "225": intNumberTerms[225], + "226": intNumberTerms[226], + "227": intNumberTerms[227], + "228": intNumberTerms[228], + "229": intNumberTerms[229], + "230": intNumberTerms[230], + "231": intNumberTerms[231], + "232": intNumberTerms[232], + "233": intNumberTerms[233], + "234": intNumberTerms[234], + "235": intNumberTerms[235], + "236": intNumberTerms[236], + "237": intNumberTerms[237], + "238": intNumberTerms[238], + "239": intNumberTerms[239], + "240": intNumberTerms[240], + "241": intNumberTerms[241], + "242": intNumberTerms[242], + "243": intNumberTerms[243], + "244": intNumberTerms[244], + "245": intNumberTerms[245], + "246": intNumberTerms[246], + "247": intNumberTerms[247], + "248": intNumberTerms[248], + "249": intNumberTerms[249], + "250": intNumberTerms[250], + "251": intNumberTerms[251], + "252": intNumberTerms[252], + "253": intNumberTerms[253], + "254": intNumberTerms[254], + "255": intNumberTerms[255], + "256": intNumberTerms[256], + "257": intNumberTerms[257], + "258": intNumberTerms[258], + "259": intNumberTerms[259], + "260": intNumberTerms[260], + "261": intNumberTerms[261], + "262": intNumberTerms[262], + "263": intNumberTerms[263], + "264": intNumberTerms[264], + "265": intNumberTerms[265], + "266": intNumberTerms[266], + "267": intNumberTerms[267], + "268": intNumberTerms[268], + "269": intNumberTerms[269], + "270": intNumberTerms[270], + "271": intNumberTerms[271], + "272": intNumberTerms[272], + "273": intNumberTerms[273], + "274": intNumberTerms[274], + "275": intNumberTerms[275], + "276": intNumberTerms[276], + "277": intNumberTerms[277], + "278": intNumberTerms[278], + "279": intNumberTerms[279], + "280": intNumberTerms[280], + "281": intNumberTerms[281], + "282": intNumberTerms[282], + "283": intNumberTerms[283], + "284": intNumberTerms[284], + "285": intNumberTerms[285], + "286": intNumberTerms[286], + "287": intNumberTerms[287], + "288": intNumberTerms[288], + "289": intNumberTerms[289], + "290": intNumberTerms[290], + "291": intNumberTerms[291], + "292": intNumberTerms[292], + "293": intNumberTerms[293], + "294": intNumberTerms[294], + "295": intNumberTerms[295], + "296": intNumberTerms[296], + "297": intNumberTerms[297], + "298": intNumberTerms[298], + "299": intNumberTerms[299], + "300": intNumberTerms[300], + "301": intNumberTerms[301], + "302": intNumberTerms[302], + "303": intNumberTerms[303], + "304": intNumberTerms[304], + "305": intNumberTerms[305], + "306": intNumberTerms[306], + "307": intNumberTerms[307], + "308": intNumberTerms[308], + "309": intNumberTerms[309], + "310": intNumberTerms[310], + "311": intNumberTerms[311], + "312": intNumberTerms[312], + "313": intNumberTerms[313], + "314": intNumberTerms[314], + "315": intNumberTerms[315], + "316": intNumberTerms[316], + "317": intNumberTerms[317], + "318": intNumberTerms[318], + "319": intNumberTerms[319], + "320": intNumberTerms[320], + "321": intNumberTerms[321], + "322": intNumberTerms[322], + "323": intNumberTerms[323], + "324": intNumberTerms[324], + "325": intNumberTerms[325], + "326": intNumberTerms[326], + "327": intNumberTerms[327], + "328": intNumberTerms[328], + "329": intNumberTerms[329], + "330": intNumberTerms[330], + "331": intNumberTerms[331], + "332": intNumberTerms[332], + "333": intNumberTerms[333], + "334": intNumberTerms[334], + "335": intNumberTerms[335], + "336": intNumberTerms[336], + "337": intNumberTerms[337], + "338": intNumberTerms[338], + "339": intNumberTerms[339], + "340": intNumberTerms[340], + "341": intNumberTerms[341], + "342": intNumberTerms[342], + "343": intNumberTerms[343], + "344": intNumberTerms[344], + "345": intNumberTerms[345], + "346": intNumberTerms[346], + "347": intNumberTerms[347], + "348": intNumberTerms[348], + "349": intNumberTerms[349], + "350": intNumberTerms[350], + "351": intNumberTerms[351], + "352": intNumberTerms[352], + "353": intNumberTerms[353], + "354": intNumberTerms[354], + "355": intNumberTerms[355], + "356": intNumberTerms[356], + "357": intNumberTerms[357], + "358": intNumberTerms[358], + "359": intNumberTerms[359], + "360": intNumberTerms[360], + "361": intNumberTerms[361], + "362": intNumberTerms[362], + "363": intNumberTerms[363], + "364": intNumberTerms[364], + "365": intNumberTerms[365], + "366": intNumberTerms[366], + "367": intNumberTerms[367], + "368": intNumberTerms[368], + "369": intNumberTerms[369], + "370": intNumberTerms[370], + "371": intNumberTerms[371], + "372": intNumberTerms[372], + "373": intNumberTerms[373], + "374": intNumberTerms[374], + "375": intNumberTerms[375], + "376": intNumberTerms[376], + "377": intNumberTerms[377], + "378": intNumberTerms[378], + "379": intNumberTerms[379], + "380": intNumberTerms[380], + "381": intNumberTerms[381], + "382": intNumberTerms[382], + "383": intNumberTerms[383], + "384": intNumberTerms[384], + "385": intNumberTerms[385], + "386": intNumberTerms[386], + "387": intNumberTerms[387], + "388": intNumberTerms[388], + "389": intNumberTerms[389], + "390": intNumberTerms[390], + "391": intNumberTerms[391], + "392": intNumberTerms[392], + "393": intNumberTerms[393], + "394": intNumberTerms[394], + "395": intNumberTerms[395], + "396": intNumberTerms[396], + "397": intNumberTerms[397], + "398": intNumberTerms[398], + "399": intNumberTerms[399], + "400": intNumberTerms[400], + "401": intNumberTerms[401], + "402": intNumberTerms[402], + "403": intNumberTerms[403], + "404": intNumberTerms[404], + "405": intNumberTerms[405], + "406": intNumberTerms[406], + "407": intNumberTerms[407], + "408": intNumberTerms[408], + "409": intNumberTerms[409], + "410": intNumberTerms[410], + "411": intNumberTerms[411], + "412": intNumberTerms[412], + "413": intNumberTerms[413], + "414": intNumberTerms[414], + "415": intNumberTerms[415], + "416": intNumberTerms[416], + "417": intNumberTerms[417], + "418": intNumberTerms[418], + "419": intNumberTerms[419], + "420": intNumberTerms[420], + "421": intNumberTerms[421], + "422": intNumberTerms[422], + "423": intNumberTerms[423], + "424": intNumberTerms[424], + "425": intNumberTerms[425], + "426": intNumberTerms[426], + "427": intNumberTerms[427], + "428": intNumberTerms[428], + "429": intNumberTerms[429], + "430": intNumberTerms[430], + "431": intNumberTerms[431], + "432": intNumberTerms[432], + "433": intNumberTerms[433], + "434": intNumberTerms[434], + "435": intNumberTerms[435], + "436": intNumberTerms[436], + "437": intNumberTerms[437], + "438": intNumberTerms[438], + "439": intNumberTerms[439], + "440": intNumberTerms[440], + "441": intNumberTerms[441], + "442": intNumberTerms[442], + "443": intNumberTerms[443], + "444": intNumberTerms[444], + "445": intNumberTerms[445], + "446": intNumberTerms[446], + "447": intNumberTerms[447], + "448": intNumberTerms[448], + "449": intNumberTerms[449], + "450": intNumberTerms[450], + "451": intNumberTerms[451], + "452": intNumberTerms[452], + "453": intNumberTerms[453], + "454": intNumberTerms[454], + "455": intNumberTerms[455], + "456": intNumberTerms[456], + "457": intNumberTerms[457], + "458": intNumberTerms[458], + "459": intNumberTerms[459], + "460": intNumberTerms[460], + "461": intNumberTerms[461], + "462": intNumberTerms[462], + "463": intNumberTerms[463], + "464": intNumberTerms[464], + "465": intNumberTerms[465], + "466": intNumberTerms[466], + "467": intNumberTerms[467], + "468": intNumberTerms[468], + "469": intNumberTerms[469], + "470": intNumberTerms[470], + "471": intNumberTerms[471], + "472": intNumberTerms[472], + "473": intNumberTerms[473], + "474": intNumberTerms[474], + "475": intNumberTerms[475], + "476": intNumberTerms[476], + "477": intNumberTerms[477], + "478": intNumberTerms[478], + "479": intNumberTerms[479], + "480": intNumberTerms[480], + "481": intNumberTerms[481], + "482": intNumberTerms[482], + "483": intNumberTerms[483], + "484": intNumberTerms[484], + "485": intNumberTerms[485], + "486": intNumberTerms[486], + "487": intNumberTerms[487], + "488": intNumberTerms[488], + "489": intNumberTerms[489], + "490": intNumberTerms[490], + "491": intNumberTerms[491], + "492": intNumberTerms[492], + "493": intNumberTerms[493], + "494": intNumberTerms[494], + "495": intNumberTerms[495], + "496": intNumberTerms[496], + "497": intNumberTerms[497], + "498": intNumberTerms[498], + "499": intNumberTerms[499], + "500": intNumberTerms[500], + "501": intNumberTerms[501], + "502": intNumberTerms[502], + "503": intNumberTerms[503], + "504": intNumberTerms[504], + "505": intNumberTerms[505], + "506": intNumberTerms[506], + "507": intNumberTerms[507], + "508": intNumberTerms[508], + "509": intNumberTerms[509], + "510": intNumberTerms[510], + "511": intNumberTerms[511], + "512": intNumberTerms[512], +} + +var intNumberTerms = [...]*Term{ + {Value: Number("0")}, + {Value: Number("1")}, + {Value: Number("2")}, + {Value: Number("3")}, + {Value: Number("4")}, + {Value: Number("5")}, + {Value: Number("6")}, + {Value: Number("7")}, + {Value: Number("8")}, + {Value: Number("9")}, + {Value: Number("10")}, + {Value: Number("11")}, + {Value: Number("12")}, + {Value: Number("13")}, + {Value: Number("14")}, + {Value: Number("15")}, + {Value: Number("16")}, + {Value: Number("17")}, + {Value: Number("18")}, + {Value: Number("19")}, + {Value: Number("20")}, + {Value: Number("21")}, + {Value: Number("22")}, + {Value: Number("23")}, + {Value: Number("24")}, + {Value: Number("25")}, + {Value: Number("26")}, + {Value: Number("27")}, + {Value: Number("28")}, + {Value: Number("29")}, + {Value: Number("30")}, + {Value: Number("31")}, + {Value: Number("32")}, + {Value: Number("33")}, + {Value: Number("34")}, + {Value: Number("35")}, + {Value: Number("36")}, + {Value: Number("37")}, + {Value: Number("38")}, + {Value: Number("39")}, + {Value: Number("40")}, + {Value: Number("41")}, + {Value: Number("42")}, + {Value: Number("43")}, + {Value: Number("44")}, + {Value: Number("45")}, + {Value: Number("46")}, + {Value: Number("47")}, + {Value: Number("48")}, + {Value: Number("49")}, + {Value: Number("50")}, + {Value: Number("51")}, + {Value: Number("52")}, + {Value: Number("53")}, + {Value: Number("54")}, + {Value: Number("55")}, + {Value: Number("56")}, + {Value: Number("57")}, + {Value: Number("58")}, + {Value: Number("59")}, + {Value: Number("60")}, + {Value: Number("61")}, + {Value: Number("62")}, + {Value: Number("63")}, + {Value: Number("64")}, + {Value: Number("65")}, + {Value: Number("66")}, + {Value: Number("67")}, + {Value: Number("68")}, + {Value: Number("69")}, + {Value: Number("70")}, + {Value: Number("71")}, + {Value: Number("72")}, + {Value: Number("73")}, + {Value: Number("74")}, + {Value: Number("75")}, + {Value: Number("76")}, + {Value: Number("77")}, + {Value: Number("78")}, + {Value: Number("79")}, + {Value: Number("80")}, + {Value: Number("81")}, + {Value: Number("82")}, + {Value: Number("83")}, + {Value: Number("84")}, + {Value: Number("85")}, + {Value: Number("86")}, + {Value: Number("87")}, + {Value: Number("88")}, + {Value: Number("89")}, + {Value: Number("90")}, + {Value: Number("91")}, + {Value: Number("92")}, + {Value: Number("93")}, + {Value: Number("94")}, + {Value: Number("95")}, + {Value: Number("96")}, + {Value: Number("97")}, + {Value: Number("98")}, + {Value: Number("99")}, + {Value: Number("100")}, + {Value: Number("101")}, + {Value: Number("102")}, + {Value: Number("103")}, + {Value: Number("104")}, + {Value: Number("105")}, + {Value: Number("106")}, + {Value: Number("107")}, + {Value: Number("108")}, + {Value: Number("109")}, + {Value: Number("110")}, + {Value: Number("111")}, + {Value: Number("112")}, + {Value: Number("113")}, + {Value: Number("114")}, + {Value: Number("115")}, + {Value: Number("116")}, + {Value: Number("117")}, + {Value: Number("118")}, + {Value: Number("119")}, + {Value: Number("120")}, + {Value: Number("121")}, + {Value: Number("122")}, + {Value: Number("123")}, + {Value: Number("124")}, + {Value: Number("125")}, + {Value: Number("126")}, + {Value: Number("127")}, + {Value: Number("128")}, + {Value: Number("129")}, + {Value: Number("130")}, + {Value: Number("131")}, + {Value: Number("132")}, + {Value: Number("133")}, + {Value: Number("134")}, + {Value: Number("135")}, + {Value: Number("136")}, + {Value: Number("137")}, + {Value: Number("138")}, + {Value: Number("139")}, + {Value: Number("140")}, + {Value: Number("141")}, + {Value: Number("142")}, + {Value: Number("143")}, + {Value: Number("144")}, + {Value: Number("145")}, + {Value: Number("146")}, + {Value: Number("147")}, + {Value: Number("148")}, + {Value: Number("149")}, + {Value: Number("150")}, + {Value: Number("151")}, + {Value: Number("152")}, + {Value: Number("153")}, + {Value: Number("154")}, + {Value: Number("155")}, + {Value: Number("156")}, + {Value: Number("157")}, + {Value: Number("158")}, + {Value: Number("159")}, + {Value: Number("160")}, + {Value: Number("161")}, + {Value: Number("162")}, + {Value: Number("163")}, + {Value: Number("164")}, + {Value: Number("165")}, + {Value: Number("166")}, + {Value: Number("167")}, + {Value: Number("168")}, + {Value: Number("169")}, + {Value: Number("170")}, + {Value: Number("171")}, + {Value: Number("172")}, + {Value: Number("173")}, + {Value: Number("174")}, + {Value: Number("175")}, + {Value: Number("176")}, + {Value: Number("177")}, + {Value: Number("178")}, + {Value: Number("179")}, + {Value: Number("180")}, + {Value: Number("181")}, + {Value: Number("182")}, + {Value: Number("183")}, + {Value: Number("184")}, + {Value: Number("185")}, + {Value: Number("186")}, + {Value: Number("187")}, + {Value: Number("188")}, + {Value: Number("189")}, + {Value: Number("190")}, + {Value: Number("191")}, + {Value: Number("192")}, + {Value: Number("193")}, + {Value: Number("194")}, + {Value: Number("195")}, + {Value: Number("196")}, + {Value: Number("197")}, + {Value: Number("198")}, + {Value: Number("199")}, + {Value: Number("200")}, + {Value: Number("201")}, + {Value: Number("202")}, + {Value: Number("203")}, + {Value: Number("204")}, + {Value: Number("205")}, + {Value: Number("206")}, + {Value: Number("207")}, + {Value: Number("208")}, + {Value: Number("209")}, + {Value: Number("210")}, + {Value: Number("211")}, + {Value: Number("212")}, + {Value: Number("213")}, + {Value: Number("214")}, + {Value: Number("215")}, + {Value: Number("216")}, + {Value: Number("217")}, + {Value: Number("218")}, + {Value: Number("219")}, + {Value: Number("220")}, + {Value: Number("221")}, + {Value: Number("222")}, + {Value: Number("223")}, + {Value: Number("224")}, + {Value: Number("225")}, + {Value: Number("226")}, + {Value: Number("227")}, + {Value: Number("228")}, + {Value: Number("229")}, + {Value: Number("230")}, + {Value: Number("231")}, + {Value: Number("232")}, + {Value: Number("233")}, + {Value: Number("234")}, + {Value: Number("235")}, + {Value: Number("236")}, + {Value: Number("237")}, + {Value: Number("238")}, + {Value: Number("239")}, + {Value: Number("240")}, + {Value: Number("241")}, + {Value: Number("242")}, + {Value: Number("243")}, + {Value: Number("244")}, + {Value: Number("245")}, + {Value: Number("246")}, + {Value: Number("247")}, + {Value: Number("248")}, + {Value: Number("249")}, + {Value: Number("250")}, + {Value: Number("251")}, + {Value: Number("252")}, + {Value: Number("253")}, + {Value: Number("254")}, + {Value: Number("255")}, + {Value: Number("256")}, + {Value: Number("257")}, + {Value: Number("258")}, + {Value: Number("259")}, + {Value: Number("260")}, + {Value: Number("261")}, + {Value: Number("262")}, + {Value: Number("263")}, + {Value: Number("264")}, + {Value: Number("265")}, + {Value: Number("266")}, + {Value: Number("267")}, + {Value: Number("268")}, + {Value: Number("269")}, + {Value: Number("270")}, + {Value: Number("271")}, + {Value: Number("272")}, + {Value: Number("273")}, + {Value: Number("274")}, + {Value: Number("275")}, + {Value: Number("276")}, + {Value: Number("277")}, + {Value: Number("278")}, + {Value: Number("279")}, + {Value: Number("280")}, + {Value: Number("281")}, + {Value: Number("282")}, + {Value: Number("283")}, + {Value: Number("284")}, + {Value: Number("285")}, + {Value: Number("286")}, + {Value: Number("287")}, + {Value: Number("288")}, + {Value: Number("289")}, + {Value: Number("290")}, + {Value: Number("291")}, + {Value: Number("292")}, + {Value: Number("293")}, + {Value: Number("294")}, + {Value: Number("295")}, + {Value: Number("296")}, + {Value: Number("297")}, + {Value: Number("298")}, + {Value: Number("299")}, + {Value: Number("300")}, + {Value: Number("301")}, + {Value: Number("302")}, + {Value: Number("303")}, + {Value: Number("304")}, + {Value: Number("305")}, + {Value: Number("306")}, + {Value: Number("307")}, + {Value: Number("308")}, + {Value: Number("309")}, + {Value: Number("310")}, + {Value: Number("311")}, + {Value: Number("312")}, + {Value: Number("313")}, + {Value: Number("314")}, + {Value: Number("315")}, + {Value: Number("316")}, + {Value: Number("317")}, + {Value: Number("318")}, + {Value: Number("319")}, + {Value: Number("320")}, + {Value: Number("321")}, + {Value: Number("322")}, + {Value: Number("323")}, + {Value: Number("324")}, + {Value: Number("325")}, + {Value: Number("326")}, + {Value: Number("327")}, + {Value: Number("328")}, + {Value: Number("329")}, + {Value: Number("330")}, + {Value: Number("331")}, + {Value: Number("332")}, + {Value: Number("333")}, + {Value: Number("334")}, + {Value: Number("335")}, + {Value: Number("336")}, + {Value: Number("337")}, + {Value: Number("338")}, + {Value: Number("339")}, + {Value: Number("340")}, + {Value: Number("341")}, + {Value: Number("342")}, + {Value: Number("343")}, + {Value: Number("344")}, + {Value: Number("345")}, + {Value: Number("346")}, + {Value: Number("347")}, + {Value: Number("348")}, + {Value: Number("349")}, + {Value: Number("350")}, + {Value: Number("351")}, + {Value: Number("352")}, + {Value: Number("353")}, + {Value: Number("354")}, + {Value: Number("355")}, + {Value: Number("356")}, + {Value: Number("357")}, + {Value: Number("358")}, + {Value: Number("359")}, + {Value: Number("360")}, + {Value: Number("361")}, + {Value: Number("362")}, + {Value: Number("363")}, + {Value: Number("364")}, + {Value: Number("365")}, + {Value: Number("366")}, + {Value: Number("367")}, + {Value: Number("368")}, + {Value: Number("369")}, + {Value: Number("370")}, + {Value: Number("371")}, + {Value: Number("372")}, + {Value: Number("373")}, + {Value: Number("374")}, + {Value: Number("375")}, + {Value: Number("376")}, + {Value: Number("377")}, + {Value: Number("378")}, + {Value: Number("379")}, + {Value: Number("380")}, + {Value: Number("381")}, + {Value: Number("382")}, + {Value: Number("383")}, + {Value: Number("384")}, + {Value: Number("385")}, + {Value: Number("386")}, + {Value: Number("387")}, + {Value: Number("388")}, + {Value: Number("389")}, + {Value: Number("390")}, + {Value: Number("391")}, + {Value: Number("392")}, + {Value: Number("393")}, + {Value: Number("394")}, + {Value: Number("395")}, + {Value: Number("396")}, + {Value: Number("397")}, + {Value: Number("398")}, + {Value: Number("399")}, + {Value: Number("400")}, + {Value: Number("401")}, + {Value: Number("402")}, + {Value: Number("403")}, + {Value: Number("404")}, + {Value: Number("405")}, + {Value: Number("406")}, + {Value: Number("407")}, + {Value: Number("408")}, + {Value: Number("409")}, + {Value: Number("410")}, + {Value: Number("411")}, + {Value: Number("412")}, + {Value: Number("413")}, + {Value: Number("414")}, + {Value: Number("415")}, + {Value: Number("416")}, + {Value: Number("417")}, + {Value: Number("418")}, + {Value: Number("419")}, + {Value: Number("420")}, + {Value: Number("421")}, + {Value: Number("422")}, + {Value: Number("423")}, + {Value: Number("424")}, + {Value: Number("425")}, + {Value: Number("426")}, + {Value: Number("427")}, + {Value: Number("428")}, + {Value: Number("429")}, + {Value: Number("430")}, + {Value: Number("431")}, + {Value: Number("432")}, + {Value: Number("433")}, + {Value: Number("434")}, + {Value: Number("435")}, + {Value: Number("436")}, + {Value: Number("437")}, + {Value: Number("438")}, + {Value: Number("439")}, + {Value: Number("440")}, + {Value: Number("441")}, + {Value: Number("442")}, + {Value: Number("443")}, + {Value: Number("444")}, + {Value: Number("445")}, + {Value: Number("446")}, + {Value: Number("447")}, + {Value: Number("448")}, + {Value: Number("449")}, + {Value: Number("450")}, + {Value: Number("451")}, + {Value: Number("452")}, + {Value: Number("453")}, + {Value: Number("454")}, + {Value: Number("455")}, + {Value: Number("456")}, + {Value: Number("457")}, + {Value: Number("458")}, + {Value: Number("459")}, + {Value: Number("460")}, + {Value: Number("461")}, + {Value: Number("462")}, + {Value: Number("463")}, + {Value: Number("464")}, + {Value: Number("465")}, + {Value: Number("466")}, + {Value: Number("467")}, + {Value: Number("468")}, + {Value: Number("469")}, + {Value: Number("470")}, + {Value: Number("471")}, + {Value: Number("472")}, + {Value: Number("473")}, + {Value: Number("474")}, + {Value: Number("475")}, + {Value: Number("476")}, + {Value: Number("477")}, + {Value: Number("478")}, + {Value: Number("479")}, + {Value: Number("480")}, + {Value: Number("481")}, + {Value: Number("482")}, + {Value: Number("483")}, + {Value: Number("484")}, + {Value: Number("485")}, + {Value: Number("486")}, + {Value: Number("487")}, + {Value: Number("488")}, + {Value: Number("489")}, + {Value: Number("490")}, + {Value: Number("491")}, + {Value: Number("492")}, + {Value: Number("493")}, + {Value: Number("494")}, + {Value: Number("495")}, + {Value: Number("496")}, + {Value: Number("497")}, + {Value: Number("498")}, + {Value: Number("499")}, + {Value: Number("500")}, + {Value: Number("501")}, + {Value: Number("502")}, + {Value: Number("503")}, + {Value: Number("504")}, + {Value: Number("505")}, + {Value: Number("506")}, + {Value: Number("507")}, + {Value: Number("508")}, + {Value: Number("509")}, + {Value: Number("510")}, + {Value: Number("511")}, + {Value: Number("512")}, +} diff --git a/third_party/opa/v1/ast/interning_test.go b/third_party/opa/v1/ast/interning_test.go new file mode 100644 index 000000000000..c55b71d80367 --- /dev/null +++ b/third_party/opa/v1/ast/interning_test.go @@ -0,0 +1,66 @@ +package ast_test + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +var ( + val = ast.String("open-policy-agent") + obj = map[string]ast.Value{"open-policy-agent": val} +) + +//go:noinline +func getPackageVarValue() ast.Value { + return val +} + +//go:noinline +func getObjectValue() ast.Value { + return obj["open-policy-agent"] +} + +//go:noinline +func getInternedValue() ast.Value { + return ast.InternedTerm("open-policy-agent").Value +} + +//go:noinline +func getNewValue() ast.Value { + return ast.String("open-policy-agent") +} + +// Benchmark experiment to compare the performance of accessing values in different ways. +// +// BenchmarkInterningAccessValue/package_var_value-12 100000000 10.95 ns/op 16 B/op 1 allocs/op +// BenchmarkInterningAccessValue/interned_value-12 175498335 6.81 ns/op 0 B/op 0 allocs/op +// BenchmarkInterningAccessValue/object_value-12 247139934 4.78 ns/op 0 B/op 0 allocs/op +// BenchmarkInterningAccessValue/new_value-12 1000000000 0.70 ns/op 0 B/op 0 allocs/op +func BenchmarkInterningAccessValue(b *testing.B) { + ast.InternStringTerm("open-policy-agent") + + b.Run("package var value", func(b *testing.B) { + for range b.N { + _ = getPackageVarValue() + } + }) + + b.Run("interned value", func(b *testing.B) { + for range b.N { + _ = getInternedValue() + } + }) + + b.Run("object value", func(b *testing.B) { + for range b.N { + _ = getObjectValue() + } + }) + + b.Run("new value", func(b *testing.B) { + for range b.N { + _ = getNewValue() + } + }) +} diff --git a/third_party/opa/v1/ast/json/json.go b/third_party/opa/v1/ast/json/json.go new file mode 100644 index 000000000000..9081fe7039a2 --- /dev/null +++ b/third_party/opa/v1/ast/json/json.go @@ -0,0 +1,106 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// This package provides options for JSON marshalling of AST nodes, and location +// data in particular. Since location data occupies a significant portion of the +// AST when included, it is excluded by default. The options provided here allow +// changing that behavior — either for all nodes or for specific types. Since +// JSONMarshaller implementations have access only to the node being marshaled, +// our options are to either attach these settings to *all* nodes in the AST, or +// to provide them via global state. The former is perhaps a little more elegant, +// and is what we went with initially. The cost of attaching these settings to +// every node however turned out to be non-negligible, and given that the number +// of users who have an interest in AST serialization are likely to be few, we +// have since switched to using global state, as provided here. Note that this +// is mostly to provide an equivalent feature to what we had before, should +// anyone depend on that. Users who need fine-grained control over AST +// serialization are recommended to use external libraries for that purpose, +// such as `github.com/json-iterator/go`. +package json + +import "sync" + +// Options defines the options for JSON operations, +// currently only marshaling can be configured +type Options struct { + MarshalOptions MarshalOptions +} + +// MarshalOptions defines the options for JSON marshaling, +// currently only toggling the marshaling of location information is supported +type MarshalOptions struct { + // IncludeLocation toggles the marshaling of location information + IncludeLocation NodeToggle + // IncludeLocationText additionally/optionally includes the text of the location + IncludeLocationText bool + // ExcludeLocationFile additionally/optionally excludes the file of the location + // Note that this is inverted (i.e. not "include" as the default needs to remain false) + ExcludeLocationFile bool +} + +// NodeToggle is a generic struct to allow the toggling of +// settings for different ast node types +type NodeToggle struct { + Term bool + Package bool + Comment bool + Import bool + Rule bool + Head bool + Expr bool + SomeDecl bool + Every bool + With bool + Annotations bool + AnnotationsRef bool +} + +// configuredJSONOptions synchronizes access to the global JSON options +type configuredJSONOptions struct { + options Options + lock sync.RWMutex +} + +var options = &configuredJSONOptions{ + options: Defaults(), +} + +// SetOptions sets the global options for marshalling AST nodes to JSON +func SetOptions(opts Options) { + options.lock.Lock() + defer options.lock.Unlock() + options.options = opts +} + +// GetOptions returns (a copy of) the global options for marshalling AST nodes to JSON +func GetOptions() Options { + options.lock.RLock() + defer options.lock.RUnlock() + return options.options +} + +// Defaults returns the default JSON options, which is to exclude location +// information in serialized JSON AST. +func Defaults() Options { + return Options{ + MarshalOptions: MarshalOptions{ + IncludeLocation: NodeToggle{ + Term: false, + Package: false, + Comment: false, + Import: false, + Rule: false, + Head: false, + Expr: false, + SomeDecl: false, + Every: false, + With: false, + Annotations: false, + AnnotationsRef: false, + }, + IncludeLocationText: false, + ExcludeLocationFile: false, + }, + } +} diff --git a/third_party/opa/v1/ast/location/location.go b/third_party/opa/v1/ast/location/location.go new file mode 100644 index 000000000000..6d1b16cdfcb0 --- /dev/null +++ b/third_party/opa/v1/ast/location/location.go @@ -0,0 +1,132 @@ +// Package location defines locations in Rego source code. +package location + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + + astJSON "github.com/open-policy-agent/opa/v1/ast/json" +) + +// Location records a position in source code +type Location struct { + Text []byte `json:"-"` // The original text fragment from the source. + File string `json:"file"` // The name of the source file (which may be empty). + Row int `json:"row"` // The line in the source. + Col int `json:"col"` // The column in the row. + Offset int `json:"-"` // The byte offset for the location in the source. + + Tabs []int `json:"-"` // The column offsets of tabs in the source. +} + +// NewLocation returns a new Location object. +func NewLocation(text []byte, file string, row int, col int) *Location { + return &Location{Text: text, File: file, Row: row, Col: col} +} + +// Equal checks if two locations are equal to each other. +func (loc *Location) Equal(other *Location) bool { + return bytes.Equal(loc.Text, other.Text) && + loc.File == other.File && + loc.Row == other.Row && + loc.Col == other.Col +} + +// Errorf returns a new error value with a message formatted to include the location +// info (e.g., line, column, filename, etc.) +func (loc *Location) Errorf(f string, a ...any) error { + return errors.New(loc.Format(f, a...)) +} + +// Wrapf returns a new error value that wraps an existing error with a message formatted +// to include the location info (e.g., line, column, filename, etc.) +func (loc *Location) Wrapf(err error, f string, a ...any) error { + return fmt.Errorf(loc.Format(f, a...)+": %w", err) +} + +// Format returns a formatted string prefixed with the location information. +func (loc *Location) Format(f string, a ...any) string { + if len(loc.File) > 0 { + f = fmt.Sprintf("%v:%v: %v", loc.File, loc.Row, f) + } else { + f = fmt.Sprintf("%v:%v: %v", loc.Row, loc.Col, f) + } + return fmt.Sprintf(f, a...) +} + +func (loc *Location) String() string { + if len(loc.File) > 0 { + return fmt.Sprintf("%v:%v", loc.File, loc.Row) + } + if len(loc.Text) > 0 { + return string(loc.Text) + } + return fmt.Sprintf("%v:%v", loc.Row, loc.Col) +} + +// Compare returns -1, 0, or 1 to indicate if this loc is less than, equal to, +// or greater than the other. Comparison is performed on the file, row, and +// column of the Location (but not on the text.) Nil locations are greater than +// non-nil locations. +func (loc *Location) Compare(other *Location) int { + if loc == nil && other == nil { + return 0 + } else if loc == nil { + return 1 + } else if other == nil { + return -1 + } else if loc.File < other.File { + return -1 + } else if loc.File > other.File { + return 1 + } else if loc.Row < other.Row { + return -1 + } else if loc.Row > other.Row { + return 1 + } else if loc.Col < other.Col { + return -1 + } else if loc.Col > other.Col { + return 1 + } + return 0 +} + +func (loc *Location) MarshalJSON() ([]byte, error) { + // structs are used here to preserve the field ordering of the original Location struct + jsonOptions := astJSON.GetOptions().MarshalOptions + if jsonOptions.ExcludeLocationFile { + data := struct { + Row int `json:"row"` + Col int `json:"col"` + Text []byte `json:"text,omitempty"` + }{ + Row: loc.Row, + Col: loc.Col, + } + + if jsonOptions.IncludeLocationText { + data.Text = loc.Text + } + + return json.Marshal(data) + } + + data := struct { + File string `json:"file"` + Row int `json:"row"` + Col int `json:"col"` + Text []byte `json:"text,omitempty"` + }{ + Row: loc.Row, + Col: loc.Col, + File: loc.File, + } + + if jsonOptions.IncludeLocationText { + data.Text = loc.Text + } + + return json.Marshal(data) +} diff --git a/third_party/opa/v1/ast/location/location_test.go b/third_party/opa/v1/ast/location/location_test.go new file mode 100644 index 000000000000..e35cb6d97f55 --- /dev/null +++ b/third_party/opa/v1/ast/location/location_test.go @@ -0,0 +1,149 @@ +package location + +import ( + "encoding/json" + "testing" + + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestLocationCompare(t *testing.T) { + + tests := []struct { + a string + b string + exp int + }{ + { + a: "", + b: "", + exp: 0, + }, + { + a: "", + b: `{"file": "a", "row": 1, "col": 1}`, + exp: 1, + }, + { + a: `{"file": "a", "row": 1, "col": 1}`, + b: "", + exp: -1, + }, + { + a: `{"file": "a", "row": 1, "col": 1}`, + b: `{"file": "a", "row": 1, "col": 1}`, + exp: 0, + }, + { + a: `{"file": "a", "row": 1, "col": 1}`, + b: `{"file": "b", "row": 1, "col": 1}`, + exp: -1, + }, + { + a: `{"file": "b", "row": 1, "col": 1}`, + b: `{"file": "a", "row": 1, "col": 1}`, + exp: 1, + }, + { + a: `{"file": "a", "row": 1, "col": 1}`, + b: `{"file": "a", "row": 2, "col": 1}`, + exp: -1, + }, + { + a: `{"file": "a", "row": 2, "col": 1}`, + b: `{"file": "a", "row": 1, "col": 1}`, + exp: 1, + }, + { + a: `{"file": "a", "row": 1, "col": 1}`, + b: `{"file": "a", "row": 1, "col": 2}`, + exp: -1, + }, + { + a: `{"file": "a", "row": 1, "col": 2}`, + b: `{"file": "a", "row": 1, "col": 1}`, + exp: 1, + }, + } + + unmarshal := func(s string) *Location { + if s != "" { + var loc Location + if err := util.Unmarshal([]byte(s), &loc); err != nil { + t.Fatal(err) + } + return &loc + } + return nil + } + + for _, tc := range tests { + locA := unmarshal(tc.a) + locB := unmarshal(tc.b) + result := locA.Compare(locB) + if tc.exp != result { + t.Fatalf("Expected %v but got %v for %v.Compare(%v)", tc.exp, result, locA, locB) + } + } +} + +func TestLocationMarshal(t *testing.T) { + testCases := map[string]struct { + loc *Location + options astJSON.Options + exp string + }{ + "default json options": { + loc: &Location{ + Text: []byte("text"), + File: "file", + Row: 1, + Col: 1, + }, + exp: `{"file":"file","row":1,"col":1}`, + }, + "including text": { + loc: &Location{ + Text: []byte("text"), + File: "file", + Row: 1, + Col: 1, + }, + options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocationText: true, + }, + }, + exp: `{"file":"file","row":1,"col":1,"text":"dGV4dA=="}`, + }, + "excluding file": { + loc: &Location{ + File: "file", + Row: 1, + Col: 1, + }, + options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + ExcludeLocationFile: true, + }, + }, + exp: `{"row":1,"col":1}`, + }, + } + + for id, tc := range testCases { + t.Run(id, func(t *testing.T) { + astJSON.SetOptions(tc.options) + defer astJSON.SetOptions(astJSON.Defaults()) + + bs, err := json.Marshal(tc.loc) + if err != nil { + t.Fatal(err) + } + if string(bs) != tc.exp { + t.Fatalf("Expected %v but got %v", tc.exp, string(bs)) + } + }) + } +} diff --git a/third_party/opa/v1/ast/map.go b/third_party/opa/v1/ast/map.go new file mode 100644 index 000000000000..31cad4d61183 --- /dev/null +++ b/third_party/opa/v1/ast/map.go @@ -0,0 +1,108 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "encoding/json" + + "github.com/open-policy-agent/opa/v1/util" +) + +// ValueMap represents a key/value map between AST term values. Any type of term +// can be used as a key in the map. +type ValueMap struct { + hashMap *util.TypedHashMap[Value, Value] +} + +// NewValueMap returns a new ValueMap. +func NewValueMap() *ValueMap { + return &ValueMap{ + hashMap: util.NewTypedHashMap(ValueEqual, ValueEqual, Value.Hash, Value.Hash, nil), + } +} + +// MarshalJSON provides a custom marshaller for the ValueMap which +// will include the key, value, and value type. +func (vs *ValueMap) MarshalJSON() ([]byte, error) { + var tmp []map[string]any + vs.Iter(func(k Value, v Value) bool { + tmp = append(tmp, map[string]any{ + "name": k.String(), + "type": ValueName(v), + "value": v, + }) + return false + }) + return json.Marshal(tmp) +} + +// Equal returns true if this ValueMap equals the other. +func (vs *ValueMap) Equal(other *ValueMap) bool { + if vs == nil { + return other == nil || other.Len() == 0 + } + if other == nil { + return vs.Len() == 0 + } + return vs.hashMap.Equal(other.hashMap) +} + +// Len returns the number of elements in the map. +func (vs *ValueMap) Len() int { + if vs == nil { + return 0 + } + return vs.hashMap.Len() +} + +// Get returns the value in the map for k. +func (vs *ValueMap) Get(k Value) Value { + if vs != nil { + if v, ok := vs.hashMap.Get(k); ok { + return v + } + } + return nil +} + +// Hash returns a hash code for this ValueMap. +func (vs *ValueMap) Hash() int { + if vs == nil { + return 0 + } + return vs.hashMap.Hash() +} + +// Iter calls the iter function for each key/value pair in the map. If the iter +// function returns true, iteration stops. +func (vs *ValueMap) Iter(iter func(Value, Value) bool) bool { + if vs == nil { + return false + } + return vs.hashMap.Iter(iter) +} + +// Put inserts a key k into the map with value v. +func (vs *ValueMap) Put(k, v Value) { + if vs == nil { + panic("put on nil value map") + } + vs.hashMap.Put(k, v) +} + +// Delete removes a key k from the map. +func (vs *ValueMap) Delete(k Value) { + if vs == nil { + return + } + vs.hashMap.Delete(k) +} + +func (vs *ValueMap) String() string { + if vs == nil { + return "{}" + } + return vs.hashMap.String() +} diff --git a/third_party/opa/v1/ast/map_test.go b/third_party/opa/v1/ast/map_test.go new file mode 100644 index 000000000000..29a58bd2caf8 --- /dev/null +++ b/third_party/opa/v1/ast/map_test.go @@ -0,0 +1,119 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "reflect" + "sort" + "testing" +) + +func TestValueMapOverwrite(t *testing.T) { + + a := NewValueMap() + a.Put(String("x"), String("foo")) + a.Put(String("x"), String("bar")) + if a.Get(String("x")) != String("bar") { + t.Fatalf("Expected a['x'] = 'bar' but got: %v", a.Get(String("x"))) + } + +} + +func TestValueMapIter(t *testing.T) { + a := NewValueMap() + a.Put(String("x"), String("foo")) + a.Put(String("y"), String("bar")) + a.Put(String("z"), String("baz")) + values := []string{} + a.Iter(func(_, v Value) bool { + values = append(values, string(v.(String))) + return false + }) + sort.Strings(values) + expected := []string{"bar", "baz", "foo"} + if !reflect.DeepEqual(values, expected) { + t.Fatalf("Unexpected value from iteration: %v", values) + } +} + +func TestValueMapEqual(t *testing.T) { + a := NewValueMap() + a.Put(String("x"), String("foo")) + a.Put(String("y"), String("bar")) + + b := NewValueMap() + b.Put(String("x"), String("foo")) + b.Put(String("y"), String("bar")) + + if !a.Equal(b) { + t.Fatalf("Expected a == b but not for: %v / %v", a, b) + } + if a.Hash() != b.Hash() { + t.Fatalf("Expected a.Hash() == b.Hash() but not for: %v / %v", a, b) + } + a.Delete(String("x")) + if a.Equal(b) { + t.Fatalf("Expected a != b but not for: %v / %v", a, b) + } +} + +func TestValueMapGetMissing(t *testing.T) { + a := NewValueMap() + a.Put(String("x"), String("foo")) + a.Put(String("y"), String("bar")) + if a.Get(String("z")) != nil { + t.Fatalf("Expected a['z'] = nil but got: %v", a.Get(String("z"))) + } +} + +func TestValueMapString(t *testing.T) { + a := NewValueMap() + a.Put(MustParseRef("a.b.c[x]"), String("foo")) + a.Put(Var("x"), Number("1")) + result := a.String() + o1 := `{a.b.c[x]: "foo", x: 1}` + o2 := `{x: 1, a.b.c[x]: "foo"}` + if result != o1 && result != o2 { + t.Fatalf("Expected string to equal either %v or %v but got: %v", o1, o2, result) + } +} + +func TestValueMapNil(t *testing.T) { + var a *ValueMap + a.Delete(String("foo")) + var b *ValueMap + if !a.Equal(b) { + t.Fatalf("Expected nil maps to be equal") + } + b = NewValueMap() + if !a.Equal(b) { + t.Fatalf("Expected nil map to equal non-nil, empty map") + } + b.Put(String("foo"), String("bar")) + if a.Equal(b) { + t.Fatalf("Expected nil map to not equal non-empty map") + } + if b.Equal(a) { + t.Fatalf("Expected non-nil map to not equal nil map") + } + if a.Hash() != 0 { + t.Fatalf("Expected nil map to hash to zero") + } + if a.Iter(func(Value, Value) bool { return true }) { + t.Fatalf("Expected nil map iteration to return false") + } + if a.Len() != 0 { + t.Fatalf("Expected nil map length to be zero") + } + if a.String() != "{}" { + t.Fatalf("Expected nil map string to be {}") + } + defer func() { + if r := recover(); r == nil { + t.Fatalf("Expected put to panic") + } + }() + a.Put(String("foo"), String("bar")) +} diff --git a/third_party/opa/v1/ast/marshal_test.go b/third_party/opa/v1/ast/marshal_test.go new file mode 100644 index 000000000000..a748d1a76a1f --- /dev/null +++ b/third_party/opa/v1/ast/marshal_test.go @@ -0,0 +1,1138 @@ +package ast + +import ( + "encoding/json" + "testing" + + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/util" +) + +func resetJSONOptions() { + astJSON.SetOptions(astJSON.Defaults()) +} + +func TestGeneric_MarshalWithLocationJSONOptions(t *testing.T) { + testCases := map[string]struct { + Term *Term + Options astJSON.Options + ExpectedJSON string + }{ + "base case, no location options set": { + Term: func() *Term { + v, _ := InterfaceToValue("example") + return &Term{ + Value: v, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + } + }(), + ExpectedJSON: `{"type":"string","value":"example"}`, + }, + "location included, location text excluded": { + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Term: true, + }, + IncludeLocationText: false, + }, + }, + Term: func() *Term { + v, _ := InterfaceToValue("example") + return &Term{ + Value: v, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + } + }(), + ExpectedJSON: `{"location":{"file":"example.rego","row":1,"col":2},"type":"string","value":"example"}`, + }, + "location included, location text also included": { + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Term: true, + }, + IncludeLocationText: true, + }, + }, + Term: func() *Term { + v, _ := InterfaceToValue("example") + t := &Term{ + Value: v, + Location: NewLocation([]byte("things"), "example.rego", 1, 2), + } + return t + }(), + ExpectedJSON: `{"location":{"file":"example.rego","row":1,"col":2,"text":"dGhpbmdz"},"type":"string","value":"example"}`, + }, + "location included, location text included, file excluded": { + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Term: true, + }, + IncludeLocationText: true, + ExcludeLocationFile: true, + }, + }, + Term: func() *Term { + v, _ := InterfaceToValue("example") + t := &Term{ + Value: v, + Location: NewLocation([]byte("things"), "example.rego", 1, 2), + } + return t + }(), + ExpectedJSON: `{"location":{"row":1,"col":2,"text":"dGhpbmdz"},"type":"string","value":"example"}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(data.Term) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +func TestTerm_MarshalJSON(t *testing.T) { + testCases := map[string]struct { + Term *Term + Options astJSON.Options + ExpectedJSON string + }{ + "base case": { + Term: func() *Term { + v, _ := InterfaceToValue("example") + return &Term{ + Value: v, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + } + }(), + ExpectedJSON: `{"type":"string","value":"example"}`, + }, + "location excluded": { + Term: func() *Term { + v, _ := InterfaceToValue("example") + return &Term{ + Value: v, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + } + }(), + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Term: false, + }, + }, + }, + ExpectedJSON: `{"type":"string","value":"example"}`, + }, + "location included": { + Term: func() *Term { + v, _ := InterfaceToValue("example") + return &Term{ + Value: v, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + } + }(), + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Term: true, + }, + }, + }, + ExpectedJSON: `{"location":{"file":"example.rego","row":1,"col":2},"type":"string","value":"example"}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(data.Term) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +func TestTerm_UnmarshalJSON(t *testing.T) { + testCases := map[string]struct { + JSON string + ExpectedTerm *Term + }{ + "base case": { + JSON: `{"type":"string","value":"example"}`, + ExpectedTerm: func() *Term { + v, _ := InterfaceToValue("example") + return &Term{ + Value: v, + } + }(), + }, + "location case": { + JSON: `{"location":{"file":"example.rego","row":1,"col":2},"type":"string","value":"example"}`, + ExpectedTerm: func() *Term { + v, _ := InterfaceToValue("example") + return &Term{ + Value: v, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + } + }(), + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + var term Term + err := json.Unmarshal([]byte(data.JSON), &term) + if err != nil { + t.Fatal(err) + } + + if !term.Equal(data.ExpectedTerm) { + t.Fatalf("expected:\n%#v got\n%#v", data.ExpectedTerm, term) + } + if data.ExpectedTerm.Location != nil { + if !term.Location.Equal(data.ExpectedTerm.Location) { + t.Fatalf("expected location:\n%#v got\n%#v", data.ExpectedTerm, term) + } + } + }) + } +} + +func TestPackage_MarshalJSON(t *testing.T) { + testCases := map[string]struct { + Package *Package + Options astJSON.Options + ExpectedJSON string + }{ + "base case": { + Package: &Package{ + Path: EmptyRef(), + }, + ExpectedJSON: `{"path":[]}`, + }, + "location excluded": { + Package: &Package{ + Path: EmptyRef(), + Location: NewLocation([]byte{}, "example.rego", 1, 2), + }, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Package: false, + }, + }, + }, + ExpectedJSON: `{"path":[]}`, + }, + "location included": { + Package: &Package{ + Path: EmptyRef(), + Location: NewLocation([]byte{}, "example.rego", 1, 2), + }, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Package: true, + }, + }, + }, + ExpectedJSON: `{"location":{"file":"example.rego","row":1,"col":2},"path":[]}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(data.Package) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +// TODO: Comment has inconsistent JSON field names starting with an upper case letter. Comment Location is +// also always included for legacy reasons +func TestComment_MarshalJSON(t *testing.T) { + testCases := map[string]struct { + Comment *Comment + Options astJSON.Options + ExpectedJSON string + }{ + "base case": { + Comment: &Comment{ + Text: []byte("comment"), + }, + ExpectedJSON: `{"Text":"Y29tbWVudA==","Location":null}`, + }, + "location excluded, still included for legacy reasons": { + Comment: &Comment{ + Text: []byte("comment"), + Location: NewLocation([]byte{}, "example.rego", 1, 2), + }, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Comment: false, // ignored + }, + }, + }, + ExpectedJSON: `{"Text":"Y29tbWVudA==","Location":{"file":"example.rego","row":1,"col":2}}`, + }, + "location included": { + Comment: &Comment{ + Text: []byte("comment"), + Location: NewLocation([]byte{}, "example.rego", 1, 2), + }, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Comment: true, // ignored + }, + }, + }, + ExpectedJSON: `{"Text":"Y29tbWVudA==","Location":{"file":"example.rego","row":1,"col":2}}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(data.Comment) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +func TestImport_MarshalJSON(t *testing.T) { + testCases := map[string]struct { + Import *Import + Options astJSON.Options + ExpectedJSON string + }{ + "base case": { + Import: func() *Import { + v, _ := InterfaceToValue("example") + term := Term{ + Value: v, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + } + return &Import{Path: &term} + }(), + ExpectedJSON: `{"path":{"type":"string","value":"example"}}`, + }, + "location excluded": { + Import: func() *Import { + v, _ := InterfaceToValue("example") + term := Term{ + Value: v, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + } + return &Import{ + Path: &term, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + } + }(), + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Import: false, + }, + }, + }, + ExpectedJSON: `{"path":{"type":"string","value":"example"}}`, + }, + "location included": { + Import: func() *Import { + v, _ := InterfaceToValue("example") + term := Term{ + Value: v, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + } + return &Import{ + Path: &term, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + } + }(), + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Import: true, + }, + }, + }, + ExpectedJSON: `{"location":{"file":"example.rego","row":1,"col":2},"path":{"type":"string","value":"example"}}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(data.Import) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +func TestRule_MarshalJSON(t *testing.T) { + rawModule := ` + package foo + + # comment + + allow if { true } + ` + + module, err := ParseModuleWithOpts("example.rego", rawModule, ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatal(err) + } + + rule := module.Rules[0] + + testCases := map[string]struct { + Rule *Rule + Options astJSON.Options + ExpectedJSON string + }{ + "base case": { + Rule: rule, + ExpectedJSON: `{"body":[{"index":0,"terms":{"type":"boolean","value":true}}],"head":{"name":"allow","value":{"type":"boolean","value":true},"ref":[{"type":"var","value":"allow"}]}}`, + }, + "location excluded": { + Rule: rule, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Rule: false, + }, + }, + }, + ExpectedJSON: `{"body":[{"index":0,"terms":{"type":"boolean","value":true}}],"head":{"name":"allow","value":{"type":"boolean","value":true},"ref":[{"type":"var","value":"allow"}]}}`, + }, + "location included": { + Rule: rule, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Rule: true, + }, + }, + }, + ExpectedJSON: `{"body":[{"index":0,"terms":{"type":"boolean","value":true}}],"head":{"name":"allow","value":{"type":"boolean","value":true},"ref":[{"type":"var","value":"allow"}]},"location":{"file":"example.rego","row":6,"col":2}}`, + }, + "annotations included": { + Rule: func() *Rule { + r := rule.Copy() + r.Annotations = []*Annotations{{ + Scope: "rule", + Title: "My rule", + Entrypoint: true, + Organizations: []string{"org1"}, + Description: "My desc", + Custom: map[string]any{ + "foo": "bar", + }}} + return r + }(), + ExpectedJSON: `{"annotations":[{"custom":{"foo":"bar"},"description":"My desc","entrypoint":true,"organizations":["org1"],"scope":"rule","title":"My rule"}],"body":[{"index":0,"terms":{"type":"boolean","value":true}}],"head":{"name":"allow","value":{"type":"boolean","value":true},"ref":[{"type":"var","value":"allow"}]}}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(data.Rule) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +func TestHead_MarshalJSON(t *testing.T) { + rawModule := ` + package foo + + # comment + + allow if { true } + ` + + module, err := ParseModuleWithOpts("example.rego", rawModule, ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatal(err) + } + + head := module.Rules[0].Head + + testCases := map[string]struct { + Head *Head + Options astJSON.Options + ExpectedJSON string + }{ + "base case": { + Head: head.Copy(), + ExpectedJSON: `{"name":"allow","value":{"type":"boolean","value":true},"ref":[{"type":"var","value":"allow"}]}`, + }, + "location excluded": { + Head: head, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Head: false, + }, + }, + }, + ExpectedJSON: `{"name":"allow","value":{"type":"boolean","value":true},"ref":[{"type":"var","value":"allow"}]}`, + }, + "location included": { + Head: head, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Head: true, + }, + }, + }, + ExpectedJSON: `{"name":"allow","value":{"type":"boolean","value":true},"ref":[{"type":"var","value":"allow"}],"location":{"file":"example.rego","row":6,"col":2}}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(data.Head) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +func TestRuleHeadRefWithTermLocations_MarshalJSON(t *testing.T) { + policy := `package test + +import rego.v1 + +ref.head[rule].test contains "value" if { + rule := "rule" +}` + + astJSON.SetOptions(astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Head: true, + Term: true, + }, + }, + }) + t.Cleanup(resetJSONOptions) + + module, err := ParseModuleWithOpts("test.rego", policy, ParserOptions{}) + if err != nil { + t.Fatal(err) + } + + bs, err := json.Marshal(module.Rules[0].Head) + if err != nil { + t.Fatal(err) + } + + // Ensure marshalled JSON includes location for any term + expectedJSON := `{"key":{"location":{"file":"test.rego","row":5,"col":30},"type":"string","value":"value"},"ref":[{"location":{"file":"test.rego","row":5,"col":1},"type":"var","value":"ref"},{"location":{"file":"test.rego","row":5,"col":5},"type":"string","value":"head"},{"location":{"file":"test.rego","row":5,"col":10},"type":"var","value":"rule"},{"location":{"file":"test.rego","row":5,"col":16},"type":"string","value":"test"}],"location":{"file":"test.rego","row":5,"col":1}}` + + if string(bs) != expectedJSON { + t.Errorf("expected %s but got %s", expectedJSON, string(bs)) + } +} + +func TestExpr_MarshalJSON(t *testing.T) { + rawModule := ` + package foo + + # comment + + allow if { true } + ` + + module, err := ParseModuleWithOpts("example.rego", rawModule, ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatal(err) + } + + expr := module.Rules[0].Body[0] + + testCases := map[string]struct { + Expr *Expr + Options astJSON.Options + ExpectedJSON string + }{ + "base case": { + Expr: expr, + ExpectedJSON: `{"index":0,"terms":{"type":"boolean","value":true}}`, + }, + "location excluded": { + Expr: expr, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Expr: false, + }, + }, + }, + ExpectedJSON: `{"index":0,"terms":{"type":"boolean","value":true}}`, + }, + "location included": { + Expr: expr, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Expr: true, + }, + }, + }, + ExpectedJSON: `{"index":0,"location":{"file":"example.rego","row":6,"col":13},"terms":{"type":"boolean","value":true}}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(data.Expr) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +func TestExpr_UnmarshalJSON(t *testing.T) { + rawModule := ` + package foo + + # comment + + allow if { true } + ` + + module, err := ParseModuleWithOpts("example.rego", rawModule, ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatal(err) + } + + expr := module.Rules[0].Body[0] + // text is not marshalled to JSON so we just drop it in our examples + expr.Location.Text = nil + + testCases := map[string]struct { + JSON string + ExpectedExpr *Expr + }{ + "base case": { + JSON: `{"index":0,"terms":{"type":"boolean","value":true}}`, + ExpectedExpr: func() *Expr { + e := expr.Copy() + e.Location = nil + return e + }(), + }, + "location case": { + JSON: `{"index":0,"location":{"file":"example.rego","row":6,"col":13},"terms":{"type":"boolean","value":true}}`, + ExpectedExpr: expr, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + var expr Expr + err := json.Unmarshal([]byte(data.JSON), &expr) + if err != nil { + t.Fatal(err) + } + + if !expr.Equal(data.ExpectedExpr) { + t.Fatalf("expected:\n%#v got\n%#v", data.ExpectedExpr, expr) + } + if data.ExpectedExpr.Location != nil { + if !expr.Location.Equal(data.ExpectedExpr.Location) { + t.Fatalf("expected location:\n%#v got\n%#v", data.ExpectedExpr.Location, expr.Location) + } + } + }) + } +} + +func TestSomeDecl_MarshalJSON(t *testing.T) { + v, _ := InterfaceToValue("example") + term := &Term{ + Value: v, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + } + + testCases := map[string]struct { + SomeDecl *SomeDecl + Options astJSON.Options + ExpectedJSON string + }{ + "base case": { + SomeDecl: &SomeDecl{ + Symbols: []*Term{term}, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + }, + ExpectedJSON: `{"symbols":[{"type":"string","value":"example"}]}`, + }, + "location excluded": { + SomeDecl: &SomeDecl{ + Symbols: []*Term{term}, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + }, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{IncludeLocation: astJSON.NodeToggle{SomeDecl: false}}, + }, + ExpectedJSON: `{"symbols":[{"type":"string","value":"example"}]}`, + }, + "location included": { + SomeDecl: &SomeDecl{ + Symbols: []*Term{term}, + Location: NewLocation([]byte{}, "example.rego", 1, 2), + }, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{IncludeLocation: astJSON.NodeToggle{SomeDecl: true}}, + }, + ExpectedJSON: `{"location":{"file":"example.rego","row":1,"col":2},"symbols":[{"type":"string","value":"example"}]}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(data.SomeDecl) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +func TestEvery_MarshalJSON(t *testing.T) { + + rawModule := ` +package foo + +allow if { + every e in [1,2,3] { + e == 1 + } +} +` + + module, err := ParseModuleWithOpts("example.rego", rawModule, ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatal(err) + } + + every, ok := module.Rules[0].Body[0].Terms.(*Every) + if !ok { + t.Fatal("expected every term") + } + + testCases := map[string]struct { + Every *Every + Options astJSON.Options + ExpectedJSON string + }{ + "base case": { + Every: every, + ExpectedJSON: `{"body":[{"index":0,"terms":[{"type":"ref","value":[{"type":"var","value":"equal"}]},{"type":"var","value":"e"},{"type":"number","value":1}]}],"domain":{"type":"array","value":[{"type":"number","value":1},{"type":"number","value":2},{"type":"number","value":3}]},"key":null,"value":{"type":"var","value":"e"}}`, + }, + "location excluded": { + Every: every, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{IncludeLocation: astJSON.NodeToggle{Every: false}}, + }, + ExpectedJSON: `{"body":[{"index":0,"terms":[{"type":"ref","value":[{"type":"var","value":"equal"}]},{"type":"var","value":"e"},{"type":"number","value":1}]}],"domain":{"type":"array","value":[{"type":"number","value":1},{"type":"number","value":2},{"type":"number","value":3}]},"key":null,"value":{"type":"var","value":"e"}}`, + }, + "location included": { + Every: every, + Options: astJSON.Options{MarshalOptions: astJSON.MarshalOptions{IncludeLocation: astJSON.NodeToggle{Every: true}}}, + ExpectedJSON: `{"body":[{"index":0,"terms":[{"type":"ref","value":[{"type":"var","value":"equal"}]},{"type":"var","value":"e"},{"type":"number","value":1}]}],"domain":{"type":"array","value":[{"type":"number","value":1},{"type":"number","value":2},{"type":"number","value":3}]},"key":null,"location":{"file":"example.rego","row":5,"col":2},"value":{"type":"var","value":"e"}}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(data.Every) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +func TestWith_MarshalJSON(t *testing.T) { + + rawModule := ` +package foo + +a if {input} + +b if { + a with input as 1 +} +` + + module, err := ParseModuleWithOpts("example.rego", rawModule, ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatal(err) + } + + with := module.Rules[1].Body[0].With[0] + + testCases := map[string]struct { + Options astJSON.Options + ExpectedJSON string + }{ + "base case": { + ExpectedJSON: `{"target":{"type":"ref","value":[{"type":"var","value":"input"}]},"value":{"type":"number","value":1}}`, + }, + "location excluded": { + Options: astJSON.Options{MarshalOptions: astJSON.MarshalOptions{IncludeLocation: astJSON.NodeToggle{With: false}}}, + ExpectedJSON: `{"target":{"type":"ref","value":[{"type":"var","value":"input"}]},"value":{"type":"number","value":1}}`, + }, + "location included": { + Options: astJSON.Options{MarshalOptions: astJSON.MarshalOptions{IncludeLocation: astJSON.NodeToggle{With: true}}}, + ExpectedJSON: `{"location":{"file":"example.rego","row":7,"col":4},"target":{"type":"ref","value":[{"type":"var","value":"input"}]},"value":{"type":"number","value":1}}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(with) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +func TestAnnotations_MarshalJSON(t *testing.T) { + + testCases := map[string]struct { + Annotations *Annotations + Options astJSON.Options + ExpectedJSON string + }{ + "base case": { + Annotations: &Annotations{ + Scope: "rule", + Title: "My rule", + Entrypoint: true, + Organizations: []string{"org1"}, + Description: "My desc", + Custom: map[string]any{ + "foo": "bar", + }, + Location: NewLocation([]byte{}, "example.rego", 1, 4), + }, + ExpectedJSON: `{"custom":{"foo":"bar"},"description":"My desc","entrypoint":true,"organizations":["org1"],"scope":"rule","title":"My rule"}`, + }, + "location excluded": { + Annotations: &Annotations{ + Scope: "rule", + Title: "My rule", + Entrypoint: true, + Organizations: []string{"org1"}, + Description: "My desc", + Custom: map[string]any{ + "foo": "bar", + }, + Location: NewLocation([]byte{}, "example.rego", 1, 4), + }, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{Annotations: false}, + }, + }, + ExpectedJSON: `{"custom":{"foo":"bar"},"description":"My desc","entrypoint":true,"organizations":["org1"],"scope":"rule","title":"My rule"}`, + }, + "location included": { + Annotations: &Annotations{ + Scope: "rule", + Title: "My rule", + Entrypoint: true, + Organizations: []string{"org1"}, + Description: "My desc", + Custom: map[string]any{ + "foo": "bar", + }, + Location: NewLocation([]byte{}, "example.rego", 1, 4), + }, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{Annotations: true}, + }, + }, + ExpectedJSON: `{"custom":{"foo":"bar"},"description":"My desc","entrypoint":true,"location":{"file":"example.rego","row":1,"col":4},"organizations":["org1"],"scope":"rule","title":"My rule"}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(data.Annotations) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +func TestAnnotationsRef_MarshalJSON(t *testing.T) { + + testCases := map[string]struct { + AnnotationsRef *AnnotationsRef + Options astJSON.Options + ExpectedJSON string + }{ + "base case": { + AnnotationsRef: &AnnotationsRef{ + Path: []*Term{}, + // using an empty annotations object here since Annotations marshalling is tested separately + Annotations: &Annotations{}, + Location: NewLocation([]byte{}, "example.rego", 1, 4), + }, + ExpectedJSON: `{"annotations":{"scope":""},"path":[]}`, + }, + "location excluded": { + AnnotationsRef: &AnnotationsRef{ + Path: []*Term{}, + Annotations: &Annotations{}, + Location: NewLocation([]byte{}, "example.rego", 1, 4), + }, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{AnnotationsRef: false}, + }, + }, + ExpectedJSON: `{"annotations":{"scope":""},"path":[]}`, + }, + "location included": { + AnnotationsRef: &AnnotationsRef{ + Path: []*Term{}, + Annotations: &Annotations{}, + Location: NewLocation([]byte{}, "example.rego", 1, 4), + }, + Options: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{AnnotationsRef: true}, + }, + }, + ExpectedJSON: `{"annotations":{"scope":""},"location":{"file":"example.rego","row":1,"col":4},"path":[]}`, + }, + } + + for name, data := range testCases { + t.Run(name, func(t *testing.T) { + astJSON.SetOptions(data.Options) + t.Cleanup(resetJSONOptions) + + bs := util.MustMarshalJSON(data.AnnotationsRef) + got := string(bs) + exp := data.ExpectedJSON + + if got != exp { + t.Fatalf("expected:\n%s got\n%s", exp, got) + } + }) + } +} + +func TestNewAnnotationsRef_JSONOptions(t *testing.T) { + tests := []struct { + note string + module string + expected []string + options ParserOptions + jsonOptions astJSON.Options + }{ + { + note: "all JSON marshaller options set to true", + module: `# METADATA +# title: pkg +# description: pkg +# organizations: +# - pkg +# related_resources: +# - https://pkg +# authors: +# - pkg +# schemas: +# - input.foo: {"type": "boolean"} +# custom: +# pkg: pkg +package test + +# METADATA +# scope: document +# title: doc +# description: doc +# organizations: +# - doc +# related_resources: +# - https://doc +# authors: +# - doc +# schemas: +# - input.bar: {"type": "integer"} +# custom: +# doc: doc + +# METADATA +# title: rule +# description: rule +# organizations: +# - rule +# related_resources: +# - https://rule +# authors: +# - rule +# schemas: +# - input.baz: {"type": "string"} +# custom: +# rule: rule +p = 1`, + options: ParserOptions{ + ProcessAnnotation: true, + }, + jsonOptions: astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Term: true, + Package: true, + Comment: true, + Import: true, + Rule: true, + Head: true, + Expr: true, + SomeDecl: true, + Every: true, + With: true, + Annotations: true, + AnnotationsRef: true, + }, + }, + }, + expected: []string{ + `{"annotations":{"authors":[{"name":"pkg"}],"custom":{"pkg":"pkg"},"description":"pkg","location":{"file":"","row":1,"col":1},"organizations":["pkg"],"related_resources":[{"ref":"https://pkg"}],"schemas":[{"path":[{"type":"var","value":"input"},{"type":"string","value":"foo"}],"definition":{"type":"boolean"}}],"scope":"package","title":"pkg"},"location":{"file":"","row":14,"col":1},"path":[{"location":{"file":"","row":14,"col":9},"type":"var","value":"data"},{"location":{"file":"","row":14,"col":9},"type":"string","value":"test"}]}`, + `{"annotations":{"authors":[{"name":"doc"}],"custom":{"doc":"doc"},"description":"doc","location":{"file":"","row":16,"col":1},"organizations":["doc"],"related_resources":[{"ref":"https://doc"}],"schemas":[{"path":[{"type":"var","value":"input"},{"type":"string","value":"bar"}],"definition":{"type":"integer"}}],"scope":"document","title":"doc"},"location":{"file":"","row":44,"col":1},"path":[{"location":{"file":"","row":14,"col":9},"type":"var","value":"data"},{"location":{"file":"","row":14,"col":9},"type":"string","value":"test"},{"location":{"file":"","row":44,"col":1},"type":"string","value":"p"}]}`, + `{"annotations":{"authors":[{"name":"rule"}],"custom":{"rule":"rule"},"description":"rule","location":{"file":"","row":31,"col":1},"organizations":["rule"],"related_resources":[{"ref":"https://rule"}],"schemas":[{"path":[{"type":"var","value":"input"},{"type":"string","value":"baz"}],"definition":{"type":"string"}}],"scope":"rule","title":"rule"},"location":{"file":"","row":44,"col":1},"path":[{"location":{"file":"","row":14,"col":9},"type":"var","value":"data"},{"location":{"file":"","row":14,"col":9},"type":"string","value":"test"},{"location":{"file":"","row":44,"col":1},"type":"string","value":"p"}]}`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + astJSON.SetOptions(tc.jsonOptions) + t.Cleanup(resetJSONOptions) + + module := MustParseModuleWithOpts(tc.module, tc.options) + + if len(tc.expected) != len(module.Annotations) { + t.Fatalf("expected %d annotations got %d", len(tc.expected), len(module.Annotations)) + } + + for i, a := range module.Annotations { + ref := NewAnnotationsRef(a) + + bytes, err := json.Marshal(ref) + if err != nil { + t.Fatal(err) + } + + got := string(bytes) + expected := tc.expected[i] + + if got != expected { + t.Fatalf("expected:\n%s got\n%s", expected, got) + } + } + + }) + } +} diff --git a/third_party/opa/v1/ast/oracle/oracle.go b/third_party/opa/v1/ast/oracle/oracle.go new file mode 100644 index 000000000000..d5ed14b76b32 --- /dev/null +++ b/third_party/opa/v1/ast/oracle/oracle.go @@ -0,0 +1,382 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. +package oracle + +import ( + "errors" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" +) + +// Error defines the structure of errors returned by the oracle. +type Error struct { + Code string `json:"code"` +} + +func (e Error) Error() string { + return e.Code +} + +// Oracle implements different queries over ASTs, e.g., find definition. +type Oracle struct { + compiler *ast.Compiler +} + +// New returns a new Oracle object. +func New() *Oracle { + return &Oracle{} +} + +// DefinitionQuery defines a Rego definition query. +type DefinitionQuery struct { + Modules map[string]*ast.Module // workspace modules; buffer may shadow a file inside the workspace + Filename string // name of file to search for position inside of + Buffer []byte // buffer that overrides module with filename + Pos int // position to search for +} + +var ( + // ErrNoDefinitionFound indicates the position was valid but no matching definition was found. + ErrNoDefinitionFound = Error{Code: "oracle_no_definition_found"} + + // ErrNoMatchFound indicates the position was invalid. + ErrNoMatchFound = Error{Code: "oracle_no_match_found"} +) + +// DefinitionQueryResult defines output of a definition query. +type DefinitionQueryResult struct { + Result *ast.Location `json:"result"` +} + +// WithCompiler sets the compiler to use for the oracle. If not set, a new ast.Compiler +// will be created when needed. +func (o *Oracle) WithCompiler(compiler *ast.Compiler) *Oracle { + o.compiler = compiler + + return o +} + +// FindDefinition returns the location of the definition referred to by the symbol +// at the position in q. +func (o *Oracle) FindDefinition(q DefinitionQuery) (*DefinitionQueryResult, error) { + // TODO(tsandall): how can we cache the results of compilation and parsing so that + // multiple queries can be executed without having to re-compute the same values? + // Ditto for caching across runs. Avoid repeating the same work. + + // NOTE(sr): "SetRuleTree" because it's needed for compiler.GetRulesExact() below + compiler, parsed, err := o.compileUpto("SetRuleTree", q.Modules, q.Buffer, q.Filename) + if err != nil { + return nil, err + } + + mod, ok := compiler.Modules[q.Filename] + if !ok { + return nil, ErrNoMatchFound + } + + stack := findContainingNodeStack(mod, q.Pos) + if len(stack) == 0 { + return nil, ErrNoMatchFound + } + + // Handle references to rules and imports + if result := findRefDefinition(compiler, parsed, stack); result != nil { + return result, nil + } + + // Handle variable references + if result := findVarDefinition(stack); result != nil { + return result, nil + } + + // Handle some declarations + if result := handleSomeDecl(compiler, stack); result != nil { + return result, nil + } + + // Handle every declarations + if result := handleEvery(compiler, stack); result != nil { + return result, nil + } + + return nil, ErrNoDefinitionFound +} + +// findRefDefinition looks for definitions of references in rules or imports +func findRefDefinition(compiler *ast.Compiler, parsed *ast.Module, stack []ast.Node) *DefinitionQueryResult { + // Walk outwards from the match location, attempting to find the definition via + // references to imports or other rules. This handles intra-module, intra-package, + // and inter-package references. + for i := len(stack) - 1; i >= 0; i-- { + term, ok := stack[i].(*ast.Term) + if !ok { + continue + } + + ref, ok := term.Value.(ast.Ref) + if !ok { + continue + } + + if rulesResult := findRulesDefinition(compiler, ref); rulesResult != nil { + return rulesResult + } + + prefix := ref.ConstantPrefix() + + for _, imp := range parsed.Imports { + path, ok := imp.Path.Value.(ast.Ref) + if !ok { + continue + } + if prefix.HasPrefix(path) { + return &DefinitionQueryResult{imp.Path.Location} + } + } + } + + return nil +} + +// findRulesDefinition looks up rules for a given ref. Rules appear in various +// other scenarios and this shares the rule look up logic. +func findRulesDefinition(compiler *ast.Compiler, ref ast.Ref) *DefinitionQueryResult { + if rules := compiler.GetRules(ref); len(rules) > 0 { + return &DefinitionQueryResult{rules[0].Location} + } + + return nil +} + +// findVarDefinition handles variable definitions. +func findVarDefinition(stack []ast.Node) *DefinitionQueryResult { + top, ok := stack[len(stack)-1].(*ast.Term) + if !ok { + return nil + } + + name, ok := top.Value.(ast.Var) + if !ok { + return nil + } + + return findVarOccurrence(stack, name) +} + +// findVarOccurrence looks for the first occurrence of a variable in the node stack. +func findVarOccurrence(stack []ast.Node, name ast.Var) *DefinitionQueryResult { + for i := range stack { + switch node := stack[i].(type) { + case *ast.Rule: + if match := walkToFirstOccurrence(node.Head.Args, name); match != nil { + return &DefinitionQueryResult{match.Location} + } + case ast.Body: + if match := walkToFirstOccurrence(node, name); match != nil { + return &DefinitionQueryResult{match.Location} + } + } + } + + return nil +} + +// handleSomeDecl extracts variables or references from some declarations. +func handleSomeDecl(compiler *ast.Compiler, stack []ast.Node) *DefinitionQueryResult { + var someDecl *ast.SomeDecl + + // Extract the "some" declaration from the stack + if expr, ok := stack[len(stack)-1].(*ast.Expr); ok { + if sd, ok := expr.Terms.(*ast.SomeDecl); ok { + someDecl = sd + } + } + + if sd, ok := stack[len(stack)-1].(*ast.SomeDecl); ok { + someDecl = sd + } + + if someDecl == nil { + return nil + } + + term := someDecl.Symbols[0] + + call, ok := term.Value.(ast.Call) + if !ok || len(call) == 0 { + return nil + } + + switch v := call[len(call)-1].Value.(type) { + case ast.Var: + return findVarOccurrence(stack, v) + case ast.Ref: + return findRulesDefinition(compiler, v) + } + + return nil +} + +// handleEvery extracts variables or references from every declarations. +func handleEvery(compiler *ast.Compiler, stack []ast.Node) *DefinitionQueryResult { + var every *ast.Every + + if expr, ok := stack[len(stack)-1].(*ast.Expr); ok { + if e, ok := expr.Terms.(*ast.Every); ok { + every = e + } + } + + if every == nil { + return nil + } + + switch v := every.Domain.Value.(type) { + case ast.Var: + return findVarOccurrence(stack, v) + case ast.Ref: + return findRulesDefinition(compiler, v) + } + + return nil +} + +func (o *Oracle) compileUpto(stage string, modules map[string]*ast.Module, bs []byte, filename string) (*ast.Compiler, *ast.Module, error) { + var compiler *ast.Compiler + if o.compiler != nil { + compiler = o.compiler + } else { + compiler = ast.NewCompiler() + } + + compiler = compiler.WithStageAfter(stage, ast.CompilerStageDefinition{ + Name: "halt", + Stage: func(c *ast.Compiler) *ast.Error { + return &ast.Error{ + Code: "halt", + } + }, + }) + + var module *ast.Module + + if len(bs) > 0 { + var err error + module, err = ast.ParseModule(filename, util.ByteSliceToString(bs)) + if err != nil { + return nil, nil, err + } + } else { + module = modules[filename] + } + + if modules == nil { + modules = map[string]*ast.Module{} + } + + if len(bs) > 0 { + modules[filename] = module + } + + compiler.Compile(modules) + + if stage != "" { + if err := halted(compiler); err != nil { + return nil, nil, err + } + } + + return compiler, module, nil +} + +func halted(c *ast.Compiler) error { + if c.Failed() && len(c.Errors) == 1 && c.Errors[0].Code == "halt" { + return nil + } else if len(c.Errors) > 0 { + return c.Errors + } + // NOTE(tsandall): this indicate an internal error in the compiler and should + // not be reachable. + return errors.New("unreachable: did not halt") +} + +func walkToFirstOccurrence(node ast.Node, needle ast.Var) (match *ast.Term) { + ast.WalkNodes(node, func(x ast.Node) bool { + if match == nil { + switch x := x.(type) { + case *ast.SomeDecl: + // NOTE(tsandall): The visitor doesn't traverse into some decl terms + // so special case here. + for i := range x.Symbols { + if x.Symbols[i].Value.Compare(needle) == 0 { + match = x.Symbols[i] + break + } + } + case *ast.Term: + if x.Value.Compare(needle) == 0 { + match = x + } + } + } + return match != nil + }) + return match +} + +func findContainingNodeStack(module *ast.Module, pos int) []ast.Node { + var matches []ast.Node + + ast.WalkNodes(module, func(x ast.Node) bool { + minLoc, maxLoc := getLocMinMax(x) + + if pos < minLoc || pos >= maxLoc { + return true + } + + matches = append(matches, x) + return false + }) + + return matches +} + +func getLocMinMax(x ast.Node) (int, int) { + if x.Loc() == nil { + return -1, -1 + } + + loc := x.Loc() + minOff := loc.Offset + + // Special case bodies because location text is only for the first expr. + if body, ok := x.(ast.Body); ok { + last := findLastExpr(body) + extraLoc := last.Loc() + if extraLoc == nil { + return -1, -1 + } + return minOff, extraLoc.Offset + len(extraLoc.Text) + } + + return minOff, minOff + len(loc.Text) +} + +// findLastExpr returns the last expression in an ast.Body that has not been generated +// by the compiler. It's used to cope with the fact that a compiler stage before SetRuleTree +// has rewritten the rule bodies slightly. By ignoring appended generated body expressions, +// we can still use the "circling in on the variable" logic based on node locations. +func findLastExpr(body ast.Body) *ast.Expr { + for i := len(body) - 1; i >= 0; i-- { + if !body[i].Generated { + return body[i] + } + } + // NOTE(sr): I believe this shouldn't happen -- we only ever start circling in on a node + // inside a body if there's something in that body. A body that only consists of generated + // expressions should not appear here. Either way, the caller deals with `nil` returned by + // this helper. + return nil +} diff --git a/third_party/opa/v1/ast/oracle/oracle_test.go b/third_party/opa/v1/ast/oracle/oracle_test.go new file mode 100644 index 000000000000..710bdd05dba6 --- /dev/null +++ b/third_party/opa/v1/ast/oracle/oracle_test.go @@ -0,0 +1,741 @@ +package oracle + +import ( + "errors" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" +) + +func TestOracleFindDefinitionErrors(t *testing.T) { + cases := []struct { + note string + buffer string + modules map[string]string + pos int + exp error + }{ + { + note: "buffer parse error", + buffer: `package`, + exp: errors.New("buffer.rego:1: rego_parse_error: unexpected eof token"), + }, + { + note: "compile error", + buffer: `package test + +# NOTE(tsandall): if we relax this check then this test case becomes obsolete. +f(input)`, + exp: errors.New("buffer.rego:4: rego_compile_error: args must not shadow input"), + }, + { + note: "no matching node", + buffer: `package test +import rego.v1 + +p if { q } + +q = true`, + pos: 118, + exp: ErrNoMatchFound, + }, + { + note: "no good match - literal", + buffer: `package test +import rego.v1 + +p if { q > 1 }`, + pos: 40, // this points at the number '1' + exp: ErrNoDefinitionFound, + }, + { + note: "no good match - rule name", + buffer: `package test +import rego.v1 + +p if { q > 1 }`, + pos: 32, // this points at the rule 'p' + exp: ErrNoDefinitionFound, + }, + { + note: "no good match - rule whitespace", + buffer: `package test +import rego.v1 + +p if { q > 1 }`, + pos: 39, // this points at the whitespace after '>' + exp: ErrNoDefinitionFound, + }, + { + note: "no match - negative", + buffer: `package test + +p = 1`, + pos: -100, + exp: ErrNoMatchFound, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + modules := map[string]*ast.Module{} + for k, v := range tc.modules { + var err error + modules[k], err = ast.ParseModule(k, v) + if err != nil { + t.Fatal(err) + } + } + o := New() + result, err := o.FindDefinition(DefinitionQuery{ + Modules: modules, + Buffer: []byte(tc.buffer), + Filename: "buffer.rego", + Pos: tc.pos, + }) + if err == nil || result != nil { + t.Fatal("expected error but got:", err, "result:", result) + } + if !strings.Contains(err.Error(), tc.exp.Error()) { + t.Fatalf("expected %v but got %v", tc.exp, err) + } + }) + } +} + +func TestOracleFindDefinition(t *testing.T) { + const aBufferModule = `package test + +import rego.v1 +import data.foo.s +import data.foo.bar as t + +p if { + q + [r] + s[t] +} + +r = true +q = true` + + const aSecondBufferModule = `package test +import rego.v1 + +p if { + q +}` + + const aThirdBufferModule = `package test +import rego.v1 + +f(x) if { + input.foo[x] +} + +u if { + some x + x = 1 +} + +v if { + x := 1 + x < 10 +} + +w if { + y[i] + i > 1 +} + +m if { + [i, j] = [1, 2] + j > i +} + +x = "deadbeef" +y contains 1 +` + + const fooModule = `package foo + +s = input +bar = 7` + + // NOTE(sr): Early ref rewriting adds an expression to the rule body for `x.y` + const varInRuleRefModule = `package foo +import rego.v1 + +q[x.y] = 10 if { + x := input + some z + z = 1 +}` + + cases := []struct { + note string + modules map[string]string + pos int + exp *ast.Location + }{ + { + note: "q - a var in the body", + modules: map[string]string{ + "buffer.rego": aBufferModule, + }, + pos: 84, + exp: &ast.Location{ + File: "buffer.rego", + Row: 14, + Col: 1, + Text: []byte("q = true"), + }, + }, + { + note: "r - another var but embedded", + modules: map[string]string{ + "buffer.rego": aBufferModule, + }, + pos: 91, + exp: &ast.Location{ + File: "buffer.rego", + Row: 13, + Col: 1, + Text: []byte("r = true"), + }, + }, + { + note: "s - reference to other module", + modules: map[string]string{ + "buffer.rego": aBufferModule, + "foo.rego": fooModule, + }, + pos: 98, + exp: &ast.Location{ + File: "foo.rego", + Row: 3, + Col: 1, + Text: []byte("s = input"), + }, + }, + { + note: "s - reference to other module but non symbol node", + modules: map[string]string{ + "buffer.rego": aBufferModule, + "foo.rego": fooModule, + }, + pos: 99, // this refers to the '[' character following 's'--this exercises the case where position does not refer to a symbol + exp: &ast.Location{ + File: "foo.rego", + Row: 3, + Col: 1, + Text: []byte("s = input"), + }, + }, + { + note: "s - reference to other module that is not loaded", + modules: map[string]string{ + "buffer.rego": aBufferModule, + }, + pos: 98, + exp: &ast.Location{ + File: "buffer.rego", + Row: 4, + Col: 8, + Text: []byte("data.foo.s"), + }, + }, + { + note: "some in var", + modules: map[string]string{ + "buffer.rego": `package example + +allow if { + list := input.list + some e in list +}`, + }, + pos: 60, + exp: &ast.Location{ + File: "buffer.rego", + Row: 4, + Col: 2, + Text: []byte("list"), + }, + }, + { + note: "some in rule", + modules: map[string]string{ + "buffer.rego": `package example + +list := [1,2,3] + +allow if { + some e in list + e == 1 +}`, + }, + pos: 56, + exp: &ast.Location{ + File: "buffer.rego", + Row: 3, + Col: 1, + Text: []byte("list := [1,2,3]"), + }, + }, + { + note: "some in rule k, v", + modules: map[string]string{ + "buffer.rego": `package example + +list := [1,2,3] + +allow if { + some k, v in list + e == 1 +}`, + }, + pos: 59, + exp: &ast.Location{ + File: "buffer.rego", + Row: 3, + Col: 1, + Text: []byte("list := [1,2,3]"), + }, + }, + { + note: "every var", + modules: map[string]string{ + "buffer.rego": `package example + +allow if { + list := input.list + every e in list { + e == 1 + } +}`, + }, + pos: 60, + exp: &ast.Location{ + File: "buffer.rego", + Row: 4, + Col: 2, + Text: []byte("list"), + }, + }, + { + note: "every rule", + modules: map[string]string{ + "buffer.rego": `package example + +list := [1,2,3] + +allow if { + every e in list { + e == 1 + } +}`, + }, + pos: 57, + exp: &ast.Location{ + File: "buffer.rego", + Row: 3, + Col: 1, + Text: []byte("list := [1,2,3]"), + }, + }, + { + note: "every in rule k, v", + modules: map[string]string{ + "buffer.rego": `package example + +list := [1,2,3] + +allow if { + every k, v in list { + k == 1 + v == 2 + } +}`, + }, + pos: 60, + exp: &ast.Location{ + File: "buffer.rego", + Row: 3, + Col: 1, + Text: []byte("list := [1,2,3]"), + }, + }, + { + note: "t - embedded ref and import alias", + modules: map[string]string{ + "buffer.rego": aBufferModule, + "foo.rego": fooModule, + }, + pos: 100, + exp: &ast.Location{ + File: "foo.rego", + Row: 4, + Col: 1, + Text: []byte("bar = 7"), + }, + }, + { + note: "t - embedded ref and import alias without other module loaded", + modules: map[string]string{ + "buffer.rego": aBufferModule, + }, + pos: 100, + exp: &ast.Location{ + File: "buffer.rego", + Row: 5, + Col: 8, + Text: []byte("data.foo.bar"), + }, + }, + { + note: "intra-package ref", + modules: map[string]string{ + "buffer.rego": aSecondBufferModule, // use a different module that references q in main buffer module used above + "test.rego": aBufferModule, + }, + pos: 37, + exp: &ast.Location{ + File: "test.rego", + Row: 14, + Col: 1, + Text: []byte("q = true"), + }, + }, + { + note: "intra-rule: function argument", + modules: map[string]string{ + "buffer.rego": aThirdBufferModule, + }, + pos: 50, + exp: &ast.Location{ + File: "buffer.rego", + Row: 4, + Col: 3, + Text: []byte("x"), + }, + }, + { + note: "intra-rule: some decl", + modules: map[string]string{ + "buffer.rego": aThirdBufferModule, + }, + pos: 72, + exp: &ast.Location{ + File: "buffer.rego", + Row: 9, + Col: 7, + Text: []byte("x"), + }, + }, + { + note: "intra-rule: assignment", + modules: map[string]string{ + "buffer.rego": aThirdBufferModule, + }, + pos: 97, + exp: &ast.Location{ + File: "buffer.rego", + Row: 14, + Col: 2, + Text: []byte("x"), + }, + }, + { + note: "intra-rule: ref output", + modules: map[string]string{ + "buffer.rego": aThirdBufferModule, + }, + pos: 121, + exp: &ast.Location{ + File: "buffer.rego", + Row: 19, + Col: 4, + Text: []byte("i"), + }, + }, + { + note: "intra-rule: unify output", + modules: map[string]string{ + "buffer.rego": aThirdBufferModule, + }, + pos: 159, + exp: &ast.Location{ + File: "buffer.rego", + Row: 24, + Col: 3, + Text: []byte("i"), + }, + }, + { + note: "intra-rule: ref head", + modules: map[string]string{ + "buffer.rego": varInRuleRefModule, + }, + pos: 66, // "z" in "z = 1" + exp: &ast.Location{ + File: "buffer.rego", + Row: 6, + Col: 7, + Text: []byte("z"), + }, + }, + { + note: "intra-rule: ref object key", + modules: map[string]string{ + "buffer.rego": `package foo + +allow if obj.key == "value" + +obj := {"key": "value"}`, + }, + pos: 22, // "o" in "obj.key" + exp: &ast.Location{ + File: "buffer.rego", + Row: 5, + Col: 1, + Text: []byte(`obj := {"key": "value"}`), + }, + }, + { + note: "intra-rule: ref object key missing finds object", + modules: map[string]string{ + "buffer.rego": `package foo + +allow if obj.foobar == "value" + +obj := {"key": "value"}`, + }, + pos: 22, // "o" in "obj.foobar" + exp: &ast.Location{ + File: "buffer.rego", + Row: 5, + Col: 1, + Text: []byte(`obj := {"key": "value"}`), + }, + }, + { + note: "intra-rule: ref object key finds correct head", + modules: map[string]string{ + "buffer.rego": `package foo + +allow if obj.key == "value" + +obj.foobar := "value" +obj.key := "value"`, + }, + pos: 22, // "o" in "obj.key" + exp: &ast.Location{ + File: "buffer.rego", + Row: 6, + Col: 1, + Text: []byte(`obj.key := "value"`), + }, + }, + { + note: "intra-rule: ref object key non existent returns self", + modules: map[string]string{ + "buffer.rego": `package foo + +allow if bar.foo == "value"`, + }, + pos: 22, // "b" in "bar.foo" + exp: &ast.Location{ + File: "buffer.rego", + Row: 3, + Col: 10, + Text: []byte(`bar`), + }, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + modules := map[string]*ast.Module{} + + for k, v := range tc.modules { + var err error + modules[k], err = ast.ParseModule(k, v) + if err != nil { + t.Fatal(err) + } + } + + buffer := tc.modules["buffer.rego"] + + t.Logf( + "pos is %d: \"%s<%s>%s\"", + tc.pos, + buffer[max(tc.pos-4, 0):tc.pos], + string(buffer[tc.pos]), + buffer[tc.pos+1:min(tc.pos+5, len(buffer))], + ) + + result, err := New().FindDefinition(DefinitionQuery{ + Modules: modules, + Buffer: []byte(buffer), + Filename: "buffer.rego", + Pos: tc.pos, + }) + if err != nil { + t.Fatal(err) + } + + if !tc.exp.Equal(result.Result) { + t.Logf("exp %q, got %q", tc.exp.Text, result.Result.Text) + t.Errorf(`Location mismatch: +expected file=%q, row=%d, col=%d +got file=%q, row=%d, col=%d`, + tc.exp.File, tc.exp.Row, tc.exp.Col, + result.Result.File, result.Result.Row, result.Result.Col) + } + + if t.Failed() { + showLocationContext(t, tc.modules, tc.exp, "Expected") + showLocationContext(t, tc.modules, result.Result, "Actual") + } + }) + } +} + +// showLocationContext is a helper that can show a row and col position within a +// buffer. e.g. +// +// oracle_test.go:469: Col mismatch: expected 11, got 10 +// oracle_test.go:489: Expected: buffer.rego:3:11 +// allow if bar.foo == "value" +// ^ +// oracle_test.go:489: Actual: buffer.rego:3:10 +// allow if bar.foo == "value" +// ^ +func showLocationContext(t *testing.T, modules map[string]string, loc *ast.Location, label string) { + t.Helper() + if content, exists := modules[loc.File]; exists { + lines := strings.Split(content, "\n") + if loc.Row > 0 && loc.Row <= len(lines) { + line := lines[loc.Row-1] + marker := strings.Repeat(" ", max(0, loc.Col-1)) + "^" + t.Logf("%s: %s:%d:%d\n %s\n %s", label, loc.File, loc.Row, loc.Col, line, marker) + } + } +} + +func TestFindContainingNodeStack(t *testing.T) { + const trivial = `package test +import rego.v1 + +p if { + q + r +} + +r = true +q = true` + + module := ast.MustParseModule(trivial) + module.Package.Location = nil // unset the package location to test nil tolerance + + // offset 46 is the first 'r' variable + result := findContainingNodeStack(module, 46) + + exp := []*ast.Location{ + module.Rules[0].Loc(), + module.Rules[0].Body.Loc(), + module.Rules[0].Body[1].Loc(), + module.Rules[0].Body[1].Terms.(*ast.Term).Loc(), + } + + if len(result) != len(exp) { + t.Fatal("expected an exact set of location pointers but got different number:", len(result), "result:", result) + } + + for i := range result { + if result[i].Loc() != exp[i] { + t.Fatal("expected exact location pointers but found difference on i =", i, "result:", result) + } + } + + // Exercise special case for bodies. + module.Rules[0].Body[1].Location = nil + result = findContainingNodeStack(module, 46) + + exp = []*ast.Location{ + module.Rules[0].Loc(), + } + + if len(result) != len(exp) { + t.Fatal("expected an exact set of location pointers but got different number:", len(result), "result:", result) + } + + for i := range result { + if result[i].Loc() != exp[i] { + t.Fatal("expected exact location pointers but found difference on i =", i, "result:", result) + } + } +} + +func TestCompileUptoNoModules(t *testing.T) { + compiler, module, err := New().compileUpto("SetRuleTree", nil, []byte("package test\np=1"), "test.rego") + if err != nil { + t.Fatal(err) + } + + rules := compiler.GetRulesExact(ast.MustParseRef("data.test.p")) + if len(rules) != 1 { + t.Fatal("unexpected rules:", rules) + } + + if module == nil { + t.Fatal("expected parsed module") + } +} + +func TestCompileUptoNoBuffer(t *testing.T) { + compiler, module, err := New().compileUpto("SetRuleTree", map[string]*ast.Module{ + "test.rego": ast.MustParseModule("package test\np=1"), + }, nil, "test.rego") + if err != nil { + t.Fatal(err) + } + + rules := compiler.GetRulesExact(ast.MustParseRef("data.test.p")) + if len(rules) != 1 { + t.Fatal("unexpected rules:", rules) + } + + if module == nil { + t.Fatal("expected parsed module") + } +} + +func TestCompileUptoBadStageName(t *testing.T) { + _, _, err := New().compileUpto("DEADBEEF", map[string]*ast.Module{ + "test.rego": ast.MustParseModule("package test\np=1"), + }, nil, "test.rego") + + if err.Error() != "unreachable: did not halt" { + t.Fatal("expected halt error but got:", err) + } +} + +func TestUsingCustomCompiler(t *testing.T) { + m := metrics.New() + o := New().WithCompiler(ast.NewCompiler().WithMetrics(m)) + q := DefinitionQuery{Modules: map[string]*ast.Module{"test.rego": ast.MustParseModule("package test\np=1")}} + + if _, err := o.FindDefinition(q); !errors.Is(err, ErrNoMatchFound) { + t.Fatal("expected no definition found error but got:", err) + } + + // Ensure metrics set on the custom compiler have been updated + if m.Timer("compile_stage_check_imports").Int64() == 0 { + t.Fatal("expected metrics to be updated") + } +} diff --git a/third_party/opa/v1/ast/parser.go b/third_party/opa/v1/ast/parser.go new file mode 100644 index 000000000000..8d1213a33a7c --- /dev/null +++ b/third_party/opa/v1/ast/parser.go @@ -0,0 +1,3018 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "maps" + "math/big" + "net/url" + "regexp" + "slices" + "sort" + "strconv" + "strings" + "unicode/utf8" + + "go.yaml.in/yaml/v3" + + "github.com/open-policy-agent/opa/v1/ast/internal/scanner" + "github.com/open-policy-agent/opa/v1/ast/internal/tokens" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/ast/location" +) + +// DefaultMaxParsingRecursionDepth is the default maximum recursion +// depth for the parser +const DefaultMaxParsingRecursionDepth = 100000 + +// ErrMaxParsingRecursionDepthExceeded is returned when the parser +// recursion exceeds the maximum allowed depth +var ErrMaxParsingRecursionDepthExceeded = errors.New("max parsing recursion depth exceeded") + +var RegoV1CompatibleRef = Ref{VarTerm("rego"), InternedTerm("v1")} + +// RegoVersion defines the Rego syntax requirements for a module. +type RegoVersion int + +const DefaultRegoVersion = RegoV1 + +const ( + RegoUndefined RegoVersion = iota + // RegoV0 is the default, original Rego syntax. + RegoV0 + // RegoV0CompatV1 requires modules to comply with both the RegoV0 and RegoV1 syntax (as when 'rego.v1' is imported in a module). + // Shortly, RegoV1 compatibility is required, but 'rego.v1' or 'future.keywords' must also be imported. + RegoV0CompatV1 + // RegoV1 is the Rego syntax enforced by OPA 1.0; e.g.: + // future.keywords part of default keyword set, and don't require imports; + // 'if' and 'contains' required in rule heads; + // (some) strict checks on by default. + RegoV1 +) + +func (v RegoVersion) Int() int { + if v == RegoV1 { + return 1 + } + return 0 +} + +func (v RegoVersion) String() string { + switch v { + case RegoV0: + return "v0" + case RegoV1: + return "v1" + case RegoV0CompatV1: + return "v0v1" + default: + return "unknown" + } +} + +func RegoVersionFromInt(i int) RegoVersion { + if i == 1 { + return RegoV1 + } + return RegoV0 +} + +// Note: This state is kept isolated from the parser so that we +// can do efficient shallow copies of these values when doing a +// save() and restore(). +type state struct { + s *scanner.Scanner + lastEnd int + skippedNL bool + tok tokens.Token + tokEnd int + lit string + loc Location + errors Errors + hints []string + comments []*Comment + wildcard int +} + +func (s *state) String() string { + return fmt.Sprintf("", s.s, s.tok, s.lit, s.loc, len(s.errors), len(s.comments)) +} + +func (s *state) Loc() *location.Location { + cpy := s.loc + return &cpy +} + +func (s *state) Text(offset, end int) []byte { + bs := s.s.Bytes() + if offset >= 0 && offset < len(bs) { + if end >= offset && end <= len(bs) { + return bs[offset:end] + } + } + return nil +} + +// Parser is used to parse Rego statements. +type Parser struct { + r io.Reader + s *state + po ParserOptions + cache parsedTermCache + recursionDepth int + maxRecursionDepth int +} + +type parsedTermCacheItem struct { + t *Term + post *state // post is the post-state that's restored on a cache-hit + offset int + next *parsedTermCacheItem +} + +type parsedTermCache struct { + m *parsedTermCacheItem +} + +func (c parsedTermCache) String() string { + s := strings.Builder{} + s.WriteRune('{') + var e *parsedTermCacheItem + for e = c.m; e != nil; e = e.next { + s.WriteString(e.String()) + } + s.WriteRune('}') + return s.String() +} + +func (e *parsedTermCacheItem) String() string { + return fmt.Sprintf("<%d:%v>", e.offset, e.t) +} + +// ParserOptions defines the options for parsing Rego statements. +type ParserOptions struct { + Capabilities *Capabilities + ProcessAnnotation bool + AllFutureKeywords bool + FutureKeywords []string + SkipRules bool + // RegoVersion is the version of Rego to parse for. + RegoVersion RegoVersion + unreleasedKeywords bool // TODO(sr): cleanup +} + +// EffectiveRegoVersion returns the effective RegoVersion to use for parsing. +func (po *ParserOptions) EffectiveRegoVersion() RegoVersion { + if po.RegoVersion == RegoUndefined { + return DefaultRegoVersion + } + return po.RegoVersion +} + +// NewParser creates and initializes a Parser. +func NewParser() *Parser { + p := &Parser{ + s: &state{}, + po: ParserOptions{}, + maxRecursionDepth: DefaultMaxParsingRecursionDepth, + } + return p +} + +// WithMaxRecursionDepth sets the maximum recursion depth for the parser. +func (p *Parser) WithMaxRecursionDepth(depth int) *Parser { + p.maxRecursionDepth = depth + return p +} + +// WithFilename provides the filename for Location details +// on parsed statements. +func (p *Parser) WithFilename(filename string) *Parser { + p.s.loc.File = filename + return p +} + +// WithReader provides the io.Reader that the parser will +// use as its source. +func (p *Parser) WithReader(r io.Reader) *Parser { + p.r = r + return p +} + +// WithProcessAnnotation enables or disables the processing of +// annotations by the Parser +func (p *Parser) WithProcessAnnotation(processAnnotation bool) *Parser { + p.po.ProcessAnnotation = processAnnotation + return p +} + +// WithFutureKeywords enables "future" keywords, i.e., keywords that can +// be imported via +// +// import future.keywords.kw +// import future.keywords.other +// +// but in a more direct way. The equivalent of this import would be +// +// WithFutureKeywords("kw", "other") +func (p *Parser) WithFutureKeywords(kws ...string) *Parser { + p.po.FutureKeywords = kws + return p +} + +// WithAllFutureKeywords enables all "future" keywords, i.e., the +// ParserOption equivalent of +// +// import future.keywords +func (p *Parser) WithAllFutureKeywords(yes bool) *Parser { + p.po.AllFutureKeywords = yes + return p +} + +// withUnreleasedKeywords allows using keywords that haven't surfaced +// as future keywords (see above) yet, but have tests that require +// them to be parsed +func (p *Parser) withUnreleasedKeywords(yes bool) *Parser { + p.po.unreleasedKeywords = yes + return p +} + +// WithCapabilities sets the capabilities structure on the parser. +func (p *Parser) WithCapabilities(c *Capabilities) *Parser { + p.po.Capabilities = c + return p +} + +// WithSkipRules instructs the parser not to attempt to parse Rule statements. +func (p *Parser) WithSkipRules(skip bool) *Parser { + p.po.SkipRules = skip + return p +} + +// WithJSONOptions sets the JSON options on the parser (now a no-op). +// +// Deprecated: Use SetOptions in the json package instead, where a longer description +// of why this is deprecated also can be found. +func (p *Parser) WithJSONOptions(_ *astJSON.Options) *Parser { + return p +} + +func (p *Parser) WithRegoVersion(version RegoVersion) *Parser { + p.po.RegoVersion = version + return p +} + +func (p *Parser) parsedTermCacheLookup() (*Term, *state) { + l := p.s.loc.Offset + // stop comparing once the cached offsets are lower than l + for h := p.cache.m; h != nil && h.offset >= l; h = h.next { + if h.offset == l { + return h.t, h.post + } + } + return nil, nil +} + +func (p *Parser) parsedTermCachePush(t *Term, s0 *state) { + s1 := p.save() + o0 := s0.loc.Offset + entry := parsedTermCacheItem{t: t, post: s1, offset: o0} + + // find the first one whose offset is smaller than ours + var e *parsedTermCacheItem + for e = p.cache.m; e != nil; e = e.next { + if e.offset < o0 { + break + } + } + entry.next = e + p.cache.m = &entry +} + +// futureParser returns a shallow copy of `p` with an empty +// cache, and a scanner that knows all future keywords. +// It's used to present hints in errors, when statements would +// only parse successfully if some future keyword is enabled. +func (p *Parser) futureParser() *Parser { + q := *p + q.s = p.save() + q.s.s = p.s.s.WithKeywords(allFutureKeywords) + q.cache = parsedTermCache{} + return &q +} + +// presentParser returns a shallow copy of `p` with an empty +// cache, and a scanner that knows none of the future keywords. +// It is used to successfully parse keyword imports, like +// +// import future.keywords.in +// +// even when the parser has already been informed about the +// future keyword "in". This parser won't error out because +// "in" is an identifier. +func (p *Parser) presentParser() (*Parser, map[string]tokens.Token) { + var cpy map[string]tokens.Token + q := *p + q.s = p.save() + q.s.s, cpy = p.s.s.WithoutKeywords(allFutureKeywords) + q.cache = parsedTermCache{} + return &q, cpy +} + +// Parse will read the Rego source and parse statements and +// comments as they are found. Any errors encountered while +// parsing will be accumulated and returned as a list of Errors. +func (p *Parser) Parse() ([]Statement, []*Comment, Errors) { + + if p.po.Capabilities == nil { + p.po.Capabilities = CapabilitiesForThisVersion(CapabilitiesRegoVersion(p.po.RegoVersion)) + } + + allowedFutureKeywords := map[string]tokens.Token{} + + if p.po.EffectiveRegoVersion() == RegoV1 { + if !p.po.Capabilities.ContainsFeature(FeatureRegoV1) { + return nil, nil, Errors{ + &Error{ + Code: ParseErr, + Message: "illegal capabilities: rego_v1 feature required for parsing v1 Rego", + Location: nil, + }, + } + } + + // rego-v1 includes all v0 future keywords in the default language definition + maps.Copy(allowedFutureKeywords, futureKeywordsV0) + + for _, kw := range p.po.Capabilities.FutureKeywords { + if tok, ok := futureKeywords[kw]; ok { + allowedFutureKeywords[kw] = tok + } else { + // For sake of error reporting, we still need to check that keywords in capabilities are known in v0 + if _, ok := futureKeywordsV0[kw]; !ok { + return nil, nil, Errors{ + &Error{ + Code: ParseErr, + Message: fmt.Sprintf("illegal capabilities: unknown keyword: %v", kw), + Location: nil, + }, + } + } + } + } + + // Check that explicitly requested future keywords are known. + for _, kw := range p.po.FutureKeywords { + if _, ok := allowedFutureKeywords[kw]; !ok { + return nil, nil, Errors{ + &Error{ + Code: ParseErr, + Message: fmt.Sprintf("unknown future keyword: %v", kw), + Location: nil, + }, + } + } + } + } else { + for _, kw := range p.po.Capabilities.FutureKeywords { + var ok bool + allowedFutureKeywords[kw], ok = allFutureKeywords[kw] + if !ok { + return nil, nil, Errors{ + &Error{ + Code: ParseErr, + Message: fmt.Sprintf("illegal capabilities: unknown keyword: %v", kw), + Location: nil, + }, + } + } + } + + if p.po.Capabilities.ContainsFeature(FeatureRegoV1) { + // rego-v1 includes all v0 future keywords in the default language definition + maps.Copy(allowedFutureKeywords, futureKeywordsV0) + } + } + + var err error + p.s.s, err = scanner.New(p.r) + if err != nil { + return nil, nil, Errors{ + &Error{ + Code: ParseErr, + Message: err.Error(), + Location: nil, + }, + } + } + + selected := map[string]tokens.Token{} + if p.po.AllFutureKeywords || p.po.EffectiveRegoVersion() == RegoV1 { + maps.Copy(selected, allowedFutureKeywords) + } else { + for _, kw := range p.po.FutureKeywords { + tok, ok := allowedFutureKeywords[kw] + if !ok { + return nil, nil, Errors{ + &Error{ + Code: ParseErr, + Message: fmt.Sprintf("unknown future keyword: %v", kw), + Location: nil, + }, + } + } + selected[kw] = tok + } + } + p.s.s = p.s.s.WithKeywords(selected) + + if p.po.EffectiveRegoVersion() == RegoV1 { + for kw, tok := range allowedFutureKeywords { + p.s.s.AddKeyword(kw, tok) + } + } + + // read the first token to initialize the parser + p.scan() + + var stmts []Statement + + // Read from the scanner until the last token is reached or no statements + // can be parsed. Attempt to parse package statements, import statements, + // rule statements, and then body/query statements (in that order). If a + // statement cannot be parsed, restore the parser state before trying the + // next type of statement. If a statement can be parsed, continue from that + // point trying to parse packages, imports, etc. in the same order. + for p.s.tok != tokens.EOF { + + s := p.save() + + if pkg := p.parsePackage(); pkg != nil { + stmts = append(stmts, pkg) + continue + } else if len(p.s.errors) > 0 { + break + } + + p.restore(s) + s = p.save() + + if imp := p.parseImport(); imp != nil { + if RegoRootDocument.Equal(imp.Path.Value.(Ref)[0]) { + p.regoV1Import(imp) + } + + if FutureRootDocument.Equal(imp.Path.Value.(Ref)[0]) { + p.futureImport(imp, allowedFutureKeywords) + } + + stmts = append(stmts, imp) + continue + } else if len(p.s.errors) > 0 { + break + } + + p.restore(s) + + if !p.po.SkipRules { + s = p.save() + + if rules := p.parseRules(); rules != nil { + for i := range rules { + stmts = append(stmts, rules[i]) + } + continue + } else if len(p.s.errors) > 0 { + break + } + + p.restore(s) + } + + if body := p.parseQuery(true, tokens.EOF); body != nil { + stmts = append(stmts, body) + continue + } + + break + } + + if p.po.ProcessAnnotation { + stmts = p.parseAnnotations(stmts) + } + + return stmts, p.s.comments, p.s.errors +} + +func (p *Parser) parseAnnotations(stmts []Statement) []Statement { + + annotStmts, errs := parseAnnotations(p.s.comments) + for _, err := range errs { + p.error(err.Location, err.Message) + } + + for _, annotStmt := range annotStmts { + stmts = append(stmts, annotStmt) + } + + return stmts +} + +func parseAnnotations(comments []*Comment) ([]*Annotations, Errors) { + + var hint = []byte("METADATA") + var curr *metadataParser + var blocks []*metadataParser + + for i := range comments { + if curr != nil { + if comments[i].Location.Row == comments[i-1].Location.Row+1 && comments[i].Location.Col == 1 { + curr.Append(comments[i]) + continue + } + curr = nil + } + if bytes.HasPrefix(bytes.TrimSpace(comments[i].Text), hint) { + curr = newMetadataParser(comments[i].Location) + blocks = append(blocks, curr) + } + } + + var stmts []*Annotations + var errs Errors + for _, b := range blocks { + a, err := b.Parse() + if err != nil { + errs = append(errs, &Error{ + Code: ParseErr, + Message: err.Error(), + Location: b.loc, + }) + } else { + stmts = append(stmts, a) + } + } + + return stmts, errs +} + +func (p *Parser) parsePackage() *Package { + + var pkg Package + pkg.SetLoc(p.s.Loc()) + + if p.s.tok != tokens.Package { + return nil + } + + p.scanWS() + + // Make sure we allow the first term of refs to be the 'package' keyword. + if p.s.tok == tokens.Dot || p.s.tok == tokens.LBrack { + // This is a ref, not a package declaration. + return nil + } + + if p.s.tok == tokens.Whitespace { + p.scan() + } + + if !isIdentOrAllowedRefKeyword(p) { + p.illegalToken() + return nil + } + + term := p.parseTerm() + + if term != nil { + switch v := term.Value.(type) { + case Var: + pkg.Path = Ref{ + DefaultRootDocument.Copy().SetLocation(term.Location), + StringTerm(string(v)).SetLocation(term.Location), + } + case Ref: + pkg.Path = make(Ref, len(v)+1) + pkg.Path[0] = DefaultRootDocument.Copy().SetLocation(v[0].Location) + first, ok := v[0].Value.(Var) + if !ok { + p.errorf(v[0].Location, "unexpected %v token: expecting var", ValueName(v[0].Value)) + return nil + } + pkg.Path[1] = StringTerm(string(first)).SetLocation(v[0].Location) + for i := 2; i < len(pkg.Path); i++ { + switch v[i-1].Value.(type) { + case String: + pkg.Path[i] = v[i-1] + default: + p.errorf(v[i-1].Location, "unexpected %v token: expecting string", ValueName(v[i-1].Value)) + return nil + } + } + default: + p.illegalToken() + return nil + } + } + + if pkg.Path == nil { + if len(p.s.errors) == 0 { + p.error(p.s.Loc(), "expected path") + } + return nil + } + + return &pkg +} + +func (p *Parser) parseImport() *Import { + + var imp Import + imp.SetLoc(p.s.Loc()) + + if p.s.tok != tokens.Import { + return nil + } + + p.scanWS() + + // Make sure we allow the first term of refs to be the 'import' keyword. + if p.s.tok == tokens.Dot || p.s.tok == tokens.LBrack { + // This is a ref, not an import declaration. + return nil + } + + if p.s.tok == tokens.Whitespace { + p.scan() + } + + if !isIdentOrAllowedRefKeyword(p) { + p.illegalToken() + return nil + } + + q, prev := p.presentParser() + term := q.parseTerm() + if term != nil { + switch v := term.Value.(type) { + case Var: + imp.Path = RefTerm(term).SetLocation(term.Location) + case Ref: + for i := 1; i < len(v); i++ { + if _, ok := v[i].Value.(String); !ok { + p.errorf(v[i].Location, "unexpected %v token: expecting string", ValueName(v[i].Value)) + return nil + } + } + imp.Path = term + } + } + // keep advanced parser state, reset known keywords + p.s = q.s + p.s.s = q.s.s.WithKeywords(prev) + + if imp.Path == nil { + p.error(p.s.Loc(), "expected path") + return nil + } + + path := imp.Path.Value.(Ref) + + switch { + case RootDocumentNames.Contains(path[0]): + case FutureRootDocument.Equal(path[0]): + case RegoRootDocument.Equal(path[0]): + default: + p.hint("if this is unexpected, try updating OPA") + p.errorf(imp.Path.Location, "unexpected import path, must begin with one of: %v, got: %v", + RootDocumentNames.Union(NewSet(FutureRootDocument, RegoRootDocument)), + path[0]) + return nil + } + + if p.s.tok == tokens.As { + p.scan() + + if p.s.tok != tokens.Ident { + p.illegal("expected var") + return nil + } + + if alias := p.parseTerm(); alias != nil { + v, ok := alias.Value.(Var) + if ok { + imp.Alias = v + return &imp + } + } + p.illegal("expected var") + return nil + } + + if imp.Alias != "" { + // Unreachable: parsing the alias var should already have generated an error. + name := imp.Alias.String() + if IsKeywordInRegoVersion(name, p.po.EffectiveRegoVersion()) { + p.errorf(imp.Location, "unexpected import alias, must not be a keyword, got: %s", name) + } + return &imp + } + + r := imp.Path.Value.(Ref) + + // Don't allow keywords in the tail path term unless it's a future import + if len(r) == 1 { + t := r[0] + name := string(t.Value.(Var)) + if IsKeywordInRegoVersion(name, p.po.EffectiveRegoVersion()) { + p.errorf(t.Location, "unexpected import path, must not end with a keyword, got: %s", name) + p.hint("import a different path or use an alias") + } + } else if !FutureRootDocument.Equal(r[0]) { + t := r[len(r)-1] + name := string(t.Value.(String)) + if IsKeywordInRegoVersion(name, p.po.EffectiveRegoVersion()) { + p.errorf(t.Location, "unexpected import path, must not end with a keyword, got: %s", name) + p.hint("import a different path or use an alias") + } + } + + return &imp +} + +// isIdentOrAllowedRefKeyword checks if the current token is an Ident or a keyword in the active rego-version. +// If a keyword, sets p.s.token to token.Ident +func isIdentOrAllowedRefKeyword(p *Parser) bool { + if p.s.tok == tokens.Ident { + return true + } + + if p.isAllowedRefKeyword(p.s.tok) { + p.s.tok = tokens.Ident + return true + } + + return false +} + +func scanAheadRef(p *Parser) bool { + if p.isAllowedRefKeyword(p.s.tok) { + // scan ahead to check if we're parsing a ref + s := p.save() + p.scanWS() + tok := p.s.tok + p.restore(s) + + if tok == tokens.Dot || tok == tokens.LBrack { + p.s.tok = tokens.Ident + return true + } + } + + return false +} + +func (p *Parser) parseRules() []*Rule { + + var rule Rule + rule.SetLoc(p.s.Loc()) + + // This allows keywords in the first var term of the ref + _ = scanAheadRef(p) + + if p.s.tok == tokens.Default { + p.scan() + rule.Default = true + _ = scanAheadRef(p) + } + + if p.s.tok != tokens.Ident { + return nil + } + + usesContains := false + if rule.Head, usesContains = p.parseHead(rule.Default); rule.Head == nil { + return nil + } + + if usesContains { + rule.Head.keywords = append(rule.Head.keywords, tokens.Contains) + } + + if rule.Default { + if !p.validateDefaultRuleValue(&rule) { + return nil + } + + if len(rule.Head.Args) > 0 { + if !p.validateDefaultRuleArgs(&rule) { + return nil + } + } + + rule.Body = NewBody(NewExpr(BooleanTerm(true).SetLocation(rule.Location)).SetLocation(rule.Location)) + return []*Rule{&rule} + } + + // back-compat with `p[x] { ... }`` + hasIf := p.s.tok == tokens.If + + // p[x] if ... becomes a single-value rule p[x] + if hasIf && !usesContains && len(rule.Head.Ref()) == 2 { + v := rule.Head.Ref()[1] + _, isRef := v.Value.(Ref) + if (!v.IsGround() || isRef) && len(rule.Head.Args) == 0 { + rule.Head.Key = rule.Head.Ref()[1] + } + + if rule.Head.Value == nil { + rule.Head.generatedValue = true + rule.Head.Value = BooleanTerm(true).SetLocation(rule.Head.Location) + } else { + // p[x] = y if becomes a single-value rule p[x] with value y, but needs name for compat + v, ok := rule.Head.Ref()[0].Value.(Var) + if !ok { + return nil + } + rule.Head.Name = v + } + } + + // p[x] becomes a multi-value rule p + if !hasIf && !usesContains && + len(rule.Head.Args) == 0 && // not a function + len(rule.Head.Ref()) == 2 { // ref like 'p[x]' + v, ok := rule.Head.Ref()[0].Value.(Var) + if !ok { + return nil + } + rule.Head.Name = v + rule.Head.Key = rule.Head.Ref()[1] + if rule.Head.Value == nil { + rule.Head.SetRef(rule.Head.Ref()[:len(rule.Head.Ref())-1]) + } + } + + switch { + case hasIf: + rule.Head.keywords = append(rule.Head.keywords, tokens.If) + p.scan() + s := p.save() + if expr := p.parseLiteral(); expr != nil { + // NOTE(sr): set literals are never false or undefined, so parsing this as + // p if { true } + // ^^^^^^^^ set of one element, `true` + // isn't valid. + isSetLiteral := false + if t, ok := expr.Terms.(*Term); ok { + _, isSetLiteral = t.Value.(Set) + } + // expr.Term is []*Term or Every + if !isSetLiteral { + rule.Body.Append(expr) + break + } + } + + // parsing as literal didn't work out, expect '{ BODY }' + p.restore(s) + fallthrough + + case p.s.tok == tokens.LBrace: + p.scan() + if rule.Body = p.parseBody(tokens.RBrace); rule.Body == nil { + return nil + } + p.scan() + + case usesContains: + rule.Body = NewBody(NewExpr(BooleanTerm(true).SetLocation(rule.Location)).SetLocation(rule.Location)) + rule.generatedBody = true + rule.Location = rule.Head.Location + + return []*Rule{&rule} + + default: + return nil + } + + if p.s.tok == tokens.Else { + // This might just be a refhead rule with a leading 'else' term. + if !scanAheadRef(p) { + if r := rule.Head.Ref(); len(r) > 1 && !r.IsGround() { + p.error(p.s.Loc(), "else keyword cannot be used on rules with variables in head") + return nil + } + if rule.Head.Key != nil { + p.error(p.s.Loc(), "else keyword cannot be used on multi-value rules") + return nil + } + + if rule.Else = p.parseElse(rule.Head); rule.Else == nil { + return nil + } + } + } + + rule.Location.Text = p.s.Text(rule.Location.Offset, p.s.lastEnd) + + rules := []*Rule{&rule} + + for p.s.tok == tokens.LBrace { + + if rule.Else != nil { + p.error(p.s.Loc(), "expected else keyword") + return nil + } + + loc := p.s.Loc() + + p.scan() + var next Rule + + if next.Body = p.parseBody(tokens.RBrace); next.Body == nil { + return nil + } + p.scan() + + loc.Text = p.s.Text(loc.Offset, p.s.lastEnd) + next.SetLoc(loc) + + // Chained rule head's keep the original + // rule's head AST but have their location + // set to the rule body. + next.Head = rule.Head.Copy() + next.Head.keywords = rule.Head.keywords + for i := range next.Head.Args { + if v, ok := next.Head.Args[i].Value.(Var); ok && v.IsWildcard() { + next.Head.Args[i].Value = Var(p.genwildcard()) + } + } + setLocRecursive(next.Head, loc) + + rules = append(rules, &next) + } + + return rules +} + +func (p *Parser) parseElse(head *Head) *Rule { + + var rule Rule + rule.SetLoc(p.s.Loc()) + + rule.Head = head.Copy() + rule.Head.generatedValue = false + for i := range rule.Head.Args { + if v, ok := rule.Head.Args[i].Value.(Var); ok && v.IsWildcard() { + rule.Head.Args[i].Value = Var(p.genwildcard()) + } + } + rule.Head.SetLoc(p.s.Loc()) + + defer func() { + rule.Location.Text = p.s.Text(rule.Location.Offset, p.s.lastEnd) + }() + + p.scan() + + switch p.s.tok { + case tokens.LBrace, tokens.If: // no value, but a body follows directly + rule.Head.generatedValue = true + rule.Head.Value = BooleanTerm(true) + case tokens.Assign, tokens.Unify: + rule.Head.Assign = tokens.Assign == p.s.tok + p.scan() + rule.Head.Value = p.parseTermInfixCall() + if rule.Head.Value == nil { + return nil + } + rule.Head.Location.Text = p.s.Text(rule.Head.Location.Offset, p.s.lastEnd) + default: + p.illegal("expected else value term or rule body") + return nil + } + + hasIf := p.s.tok == tokens.If + hasLBrace := p.s.tok == tokens.LBrace + + if !hasIf && !hasLBrace { + rule.Body = NewBody(NewExpr(BooleanTerm(true))) + rule.generatedBody = true + setLocRecursive(rule.Body, rule.Location) + return &rule + } + + if hasIf { + rule.Head.keywords = append(rule.Head.keywords, tokens.If) + p.scan() + } + + if p.s.tok == tokens.LBrace { + p.scan() + if rule.Body = p.parseBody(tokens.RBrace); rule.Body == nil { + return nil + } + p.scan() + } else if p.s.tok != tokens.EOF { + expr := p.parseLiteral() + if expr == nil { + return nil + } + rule.Body.Append(expr) + setLocRecursive(rule.Body, rule.Location) + } else { + p.illegal("rule body expected") + return nil + } + + if p.s.tok == tokens.Else { + if rule.Else = p.parseElse(head); rule.Else == nil { + return nil + } + } + return &rule +} + +func (p *Parser) parseHead(defaultRule bool) (*Head, bool) { + head := &Head{} + loc := p.s.Loc() + defer func() { + if head != nil { + head.SetLoc(loc) + head.Location.Text = p.s.Text(head.Location.Offset, p.s.lastEnd) + } + }() + + term := p.parseVar() + if term == nil { + return nil, false + } + + ref := p.parseTermFinish(term, true) + if ref == nil { + p.illegal("expected rule head name") + return nil, false + } + + switch x := ref.Value.(type) { + case Var: + // TODO + head = VarHead(x, ref.Location, nil) + case Ref: + head = RefHead(x) + case Call: + op, args := x[0], x[1:] + var ref Ref + switch y := op.Value.(type) { + case Var: + ref = Ref{op} + case Ref: + if _, ok := y[0].Value.(Var); !ok { + p.illegal("rule head ref %v invalid", y) + return nil, false + } + ref = y + } + head = RefHead(ref) + head.Args = slices.Clone[[]*Term](args) + + default: + return nil, false + } + + name := head.Ref().String() + + switch p.s.tok { + case tokens.Contains: // NOTE: no Value for `contains` heads, we return here + // Catch error case of using 'contains' with a function definition rule head. + if head.Args != nil { + p.illegal("the contains keyword can only be used with multi-value rule definitions (e.g., %s contains { ... })", name) + } + p.scan() + head.Key = p.parseTermInfixCall() + if head.Key == nil { + p.illegal("expected rule key term (e.g., %s contains { ... })", name) + } + return head, true + + case tokens.Unify: + p.scan() + head.Value = p.parseTermInfixCall() + if head.Value == nil { + // FIX HEAD.String() + p.illegal("expected rule value term (e.g., %s[%s] = { ... })", name, head.Key) + } + case tokens.Assign: + p.scan() + head.Assign = true + head.Value = p.parseTermInfixCall() + if head.Value == nil { + switch { + case len(head.Args) > 0: + p.illegal("expected function value term (e.g., %s(...) := { ... })", name) + case head.Key != nil: + p.illegal("expected partial rule value term (e.g., %s[...] := { ... })", name) + case defaultRule: + p.illegal("expected default rule value term (e.g., default %s := )", name) + default: + p.illegal("expected rule value term (e.g., %s := { ... })", name) + } + } + } + + if head.Value == nil && head.Key == nil { + if len(head.Ref()) != 2 || len(head.Args) > 0 { + head.generatedValue = true + head.Value = BooleanTerm(true).SetLocation(head.Location) + } + } + return head, false +} + +func (p *Parser) parseBody(end tokens.Token) Body { + if !p.enter() { + return nil + } + defer p.leave() + return p.parseQuery(false, end) +} + +func (p *Parser) parseQuery(requireSemi bool, end tokens.Token) Body { + body := Body{} + + if p.s.tok == end { + p.error(p.s.Loc(), "found empty body") + return nil + } + + for { + expr := p.parseLiteral() + if expr == nil { + return nil + } + + body.Append(expr) + + if p.s.tok == tokens.Semicolon { + p.scan() + continue + } + + if p.s.tok == end || requireSemi { + return body + } + + if !p.s.skippedNL { + // If there was already an error then don't pile this one on + if len(p.s.errors) == 0 { + p.illegal(`expected \n or %s or %s`, tokens.Semicolon, end) + } + return nil + } + } +} + +func (p *Parser) parseLiteral() (expr *Expr) { + + offset := p.s.loc.Offset + loc := p.s.Loc() + + defer func() { + if expr != nil { + loc.Text = p.s.Text(offset, p.s.lastEnd) + expr.SetLoc(loc) + } + }() + + // Check that we're not parsing a ref + if p.isAllowedRefKeyword(p.s.tok) { + // Scan ahead + s := p.save() + p.scanWS() + tok := p.s.tok + p.restore(s) + + if tok == tokens.Dot || tok == tokens.LBrack { + p.s.tok = tokens.Ident + return p.parseLiteralExpr(false) + } + } + + var negated bool + if p.s.tok == tokens.Not { + s := p.save() + p.scanWS() + tok := p.s.tok + p.restore(s) + + if tok != tokens.Dot && tok != tokens.LBrack { + p.scan() + negated = true + } + } + + switch p.s.tok { + case tokens.Some: + if negated { + p.illegal("illegal negation of 'some'") + return nil + } + return p.parseSome() + case tokens.Every: + if negated { + p.illegal("illegal negation of 'every'") + return nil + } + return p.parseEvery() + default: + return p.parseLiteralExpr(negated) + } +} + +func (p *Parser) isAllowedRefKeyword(t tokens.Token) bool { + return p.isAllowedRefKeywordStr(t.String()) +} + +func (p *Parser) isAllowedRefKeywordStr(s string) bool { + if p.po.Capabilities.ContainsFeature(FeatureKeywordsInRefs) { + return IsKeywordInRegoVersion(s, p.po.EffectiveRegoVersion()) || p.s.s.IsKeyword(s) + } + + return false +} + +func (p *Parser) parseLiteralExpr(negated bool) *Expr { + s := p.save() + expr := p.parseExpr() + if expr != nil { + expr.Negated = negated + if p.s.tok == tokens.With { + if expr.With = p.parseWith(); expr.With == nil { + return nil + } + } + // If we find a plain `every` identifier, attempt to parse an every expression, + // add hint if it succeeds. + if term, ok := expr.Terms.(*Term); ok && Var("every").Equal(term.Value) { + var hint bool + t := p.save() + p.restore(s) + if expr := p.futureParser().parseEvery(); expr != nil { + _, hint = expr.Terms.(*Every) + } + p.restore(t) + if hint { + p.hint("`import future.keywords.every` for `every x in xs { ... }` expressions") + } + } + return expr + } + return nil +} + +func (p *Parser) parseWith() []*With { + + withs := []*With{} + + for { + + with := With{ + Location: p.s.Loc(), + } + p.scan() + + if p.s.tok != tokens.Ident { + p.illegal("expected ident") + return nil + } + + with.Target = p.parseTerm() + if with.Target == nil { + return nil + } + + switch with.Target.Value.(type) { + case Ref, Var: + break + default: + p.illegal("expected with target path") + } + + if p.s.tok != tokens.As { + p.illegal("expected as keyword") + return nil + } + + p.scan() + + if with.Value = p.parseTermInfixCall(); with.Value == nil { + return nil + } + + with.Location.Text = p.s.Text(with.Location.Offset, p.s.lastEnd) + + withs = append(withs, &with) + + if p.s.tok != tokens.With { + break + } + } + + return withs +} + +func (p *Parser) parseSome() *Expr { + + decl := &SomeDecl{} + decl.SetLoc(p.s.Loc()) + + // Attempt to parse "some x in xs", which will end up in + // SomeDecl{Symbols: ["member(x, xs)"]} + s := p.save() + p.scan() + if term := p.parseTermInfixCall(); term != nil { + if call, ok := term.Value.(Call); ok { + switch call[0].String() { + case Member.Name: + if len(call) != 3 { + p.illegal("illegal domain") + return nil + } + case MemberWithKey.Name: + if len(call) != 4 { + p.illegal("illegal domain") + return nil + } + default: + p.illegal("expected `x in xs` or `x, y in xs` expression") + return nil + } + + decl.Symbols = []*Term{term} + expr := NewExpr(decl).SetLocation(decl.Location) + if p.s.tok == tokens.With { + if expr.With = p.parseWith(); expr.With == nil { + return nil + } + } + return expr + } + } + + p.restore(s) + s = p.save() // new copy for later + var hint bool + p.scan() + if term := p.futureParser().parseTermInfixCall(); term != nil { + if call, ok := term.Value.(Call); ok { + switch call[0].String() { + case Member.Name, MemberWithKey.Name: + hint = true + } + } + } + + // go on as before, it's `some x[...]` or illegal + p.restore(s) + if hint { + p.hint("`import future.keywords.in` for `some x in xs` expressions") + } + + for { // collecting var args + + p.scan() + + if p.s.tok != tokens.Ident { + p.illegal("expected var") + return nil + } + + decl.Symbols = append(decl.Symbols, p.parseVar()) + + p.scan() + + if p.s.tok != tokens.Comma { + break + } + } + + return NewExpr(decl).SetLocation(decl.Location) +} + +func (p *Parser) parseEvery() *Expr { + qb := &Every{} + qb.SetLoc(p.s.Loc()) + + // TODO(sr): We'd get more accurate error messages if we didn't rely on + // parseTermInfixCall here, but parsed "var [, var] in term" manually. + p.scan() + term := p.parseTermInfixCall() + if term == nil { + return nil + } + call, ok := term.Value.(Call) + if !ok { + p.illegal("expected `x[, y] in xs { ... }` expression") + return nil + } + switch call[0].String() { + case Member.Name: // x in xs + if len(call) != 3 { + p.illegal("illegal domain") + return nil + } + qb.Value = call[1] + qb.Domain = call[2] + case MemberWithKey.Name: // k, v in xs + if len(call) != 4 { + p.illegal("illegal domain") + return nil + } + qb.Key = call[1] + qb.Value = call[2] + qb.Domain = call[3] + if _, ok := qb.Key.Value.(Var); !ok { + p.illegal("expected key to be a variable") + return nil + } + default: + p.illegal("expected `x[, y] in xs { ... }` expression") + return nil + } + if _, ok := qb.Value.Value.(Var); !ok { + p.illegal("expected value to be a variable") + return nil + } + if p.s.tok == tokens.LBrace { // every x in xs { ... } + p.scan() + body := p.parseBody(tokens.RBrace) + if body == nil { + return nil + } + p.scan() + qb.Body = body + expr := NewExpr(qb).SetLocation(qb.Location) + + if p.s.tok == tokens.With { + if expr.With = p.parseWith(); expr.With == nil { + return nil + } + } + return expr + } + + p.illegal("missing body") + return nil +} + +func (p *Parser) parseExpr() *Expr { + + lhs := p.parseTermInfixCall() + if lhs == nil { + return nil + } + + if op := p.parseTermOp(tokens.Assign, tokens.Unify); op != nil { + if rhs := p.parseTermInfixCall(); rhs != nil { + return NewExpr([]*Term{op, lhs, rhs}) + } + return nil + } + + // NOTE(tsandall): the top-level call term is converted to an expr because + // the evaluator does not support the call term type (nested calls are + // rewritten by the compiler.) + if call, ok := lhs.Value.(Call); ok { + return NewExpr([]*Term(call)) + } + + return NewExpr(lhs) +} + +// parseTermInfixCall consumes the next term from the input and returns it. If a +// term cannot be parsed the return value is nil and error will be recorded. The +// scanner will be advanced to the next token before returning. +// By starting out with infix relations (==, !=, <, etc) and further calling the +// other binary operators (|, &, arithmetics), it constitutes the binding +// precedence. +func (p *Parser) parseTermInfixCall() *Term { + if !p.enter() { + return nil + } + defer p.leave() + + return p.parseTermIn(nil, true, p.s.loc.Offset) +} + +func (p *Parser) parseTermInfixCallInList() *Term { + if !p.enter() { + return nil + } + defer p.leave() + + return p.parseTermIn(nil, false, p.s.loc.Offset) +} + +// use static references to avoid allocations, and +// copy them to the call term only when needed +var memberWithKeyRef = MemberWithKey.Ref() +var memberRef = Member.Ref() + +func (p *Parser) parseTermIn(lhs *Term, keyVal bool, offset int) *Term { + if !p.enter() { + return nil + } + defer p.leave() + + // NOTE(sr): `in` is a bit special: besides `lhs in rhs`, it also + // supports `key, val in rhs`, so it can have an optional second lhs. + // `keyVal` triggers if we attempt to parse a second lhs argument (`mhs`). + if lhs == nil { + lhs = p.parseTermRelation(nil, offset) + } + if lhs != nil { + if keyVal && p.s.tok == tokens.Comma { // second "lhs", or "middle hand side" + s := p.save() + p.scan() + if mhs := p.parseTermRelation(nil, offset); mhs != nil { + + if op := p.parseTermOpName(memberWithKeyRef, tokens.In); op != nil { + if rhs := p.parseTermRelation(nil, p.s.loc.Offset); rhs != nil { + call := p.setLoc(CallTerm(op, lhs, mhs, rhs), lhs.Location, offset, p.s.lastEnd) + switch p.s.tok { + case tokens.In: + return p.parseTermIn(call, keyVal, offset) + default: + return call + } + } + } + } + p.restore(s) + } + + _ = scanAheadRef(p) + + if op := p.parseTermOpName(memberRef, tokens.In); op != nil { + if rhs := p.parseTermRelation(nil, p.s.loc.Offset); rhs != nil { + call := p.setLoc(CallTerm(op, lhs, rhs), lhs.Location, offset, p.s.lastEnd) + switch p.s.tok { + case tokens.In: + return p.parseTermIn(call, keyVal, offset) + default: + return call + } + } + } + } + return lhs +} + +func (p *Parser) parseTermRelation(lhs *Term, offset int) *Term { + if !p.enter() { + return nil + } + defer p.leave() + + if lhs == nil { + lhs = p.parseTermOr(nil, offset) + } + if lhs != nil { + if op := p.parseTermOp(tokens.Equal, tokens.Neq, tokens.Lt, tokens.Gt, tokens.Lte, tokens.Gte); op != nil { + if rhs := p.parseTermOr(nil, p.s.loc.Offset); rhs != nil { + call := p.setLoc(CallTerm(op, lhs, rhs), lhs.Location, offset, p.s.lastEnd) + switch p.s.tok { + case tokens.Equal, tokens.Neq, tokens.Lt, tokens.Gt, tokens.Lte, tokens.Gte: + return p.parseTermRelation(call, offset) + default: + return call + } + } + } + } + return lhs +} + +func (p *Parser) parseTermOr(lhs *Term, offset int) *Term { + if !p.enter() { + return nil + } + defer p.leave() + + if lhs == nil { + lhs = p.parseTermAnd(nil, offset) + } + if lhs != nil { + if op := p.parseTermOp(tokens.Or); op != nil { + if rhs := p.parseTermAnd(nil, p.s.loc.Offset); rhs != nil { + call := p.setLoc(CallTerm(op, lhs, rhs), lhs.Location, offset, p.s.lastEnd) + switch p.s.tok { + case tokens.Or: + return p.parseTermOr(call, offset) + default: + return call + } + } + } + return lhs + } + return nil +} + +func (p *Parser) parseTermAnd(lhs *Term, offset int) *Term { + if !p.enter() { + return nil + } + defer p.leave() + + if lhs == nil { + lhs = p.parseTermArith(nil, offset) + } + if lhs != nil { + if op := p.parseTermOp(tokens.And); op != nil { + if rhs := p.parseTermArith(nil, p.s.loc.Offset); rhs != nil { + call := p.setLoc(CallTerm(op, lhs, rhs), lhs.Location, offset, p.s.lastEnd) + switch p.s.tok { + case tokens.And: + return p.parseTermAnd(call, offset) + default: + return call + } + } + } + return lhs + } + return nil +} + +func (p *Parser) parseTermArith(lhs *Term, offset int) *Term { + if !p.enter() { + return nil + } + defer p.leave() + + if lhs == nil { + lhs = p.parseTermFactor(nil, offset) + } + if lhs != nil { + if op := p.parseTermOp(tokens.Add, tokens.Sub); op != nil { + if rhs := p.parseTermFactor(nil, p.s.loc.Offset); rhs != nil { + call := p.setLoc(CallTerm(op, lhs, rhs), lhs.Location, offset, p.s.lastEnd) + switch p.s.tok { + case tokens.Add, tokens.Sub: + return p.parseTermArith(call, offset) + default: + return call + } + } + } + } + return lhs +} + +func (p *Parser) parseTermFactor(lhs *Term, offset int) *Term { + if !p.enter() { + return nil + } + defer p.leave() + + if lhs == nil { + lhs = p.parseTerm() + } + if lhs != nil { + if op := p.parseTermOp(tokens.Mul, tokens.Quo, tokens.Rem); op != nil { + if rhs := p.parseTerm(); rhs != nil { + call := p.setLoc(CallTerm(op, lhs, rhs), lhs.Location, offset, p.s.lastEnd) + switch p.s.tok { + case tokens.Mul, tokens.Quo, tokens.Rem: + return p.parseTermFactor(call, offset) + default: + return call + } + } + } + } + return lhs +} + +func (p *Parser) parseTerm() *Term { + if !p.enter() { + return nil + } + defer p.leave() + + if term, s := p.parsedTermCacheLookup(); s != nil { + p.restore(s) + return term + } + s0 := p.save() + + var term *Term + switch p.s.tok { + case tokens.Null: + term = NullTerm().SetLocation(p.s.Loc()) + case tokens.True: + term = BooleanTerm(true).SetLocation(p.s.Loc()) + case tokens.False: + term = BooleanTerm(false).SetLocation(p.s.Loc()) + case tokens.Sub, tokens.Dot, tokens.Number: + term = p.parseNumber() + case tokens.String: + term = p.parseString() + case tokens.Ident, tokens.Contains: // NOTE(sr): contains anywhere BUT in rule heads gets no special treatment + term = p.parseVar() + case tokens.LBrack: + term = p.parseArray() + case tokens.LBrace: + term = p.parseSetOrObject() + case tokens.LParen: + offset := p.s.loc.Offset + p.scan() + if r := p.parseTermInfixCall(); r != nil { + if p.s.tok == tokens.RParen { + r.Location.Text = p.s.Text(offset, p.s.tokEnd) + term = r + } else { + p.error(p.s.Loc(), "non-terminated expression") + } + } + default: + p.illegalToken() + } + + term = p.parseTermFinish(term, false) + p.parsedTermCachePush(term, s0) + return term +} + +func (p *Parser) parseTermFinish(head *Term, skipws bool) *Term { + if head == nil { + return nil + } + offset := p.s.loc.Offset + p.doScan(skipws) + + switch p.s.tok { + case tokens.LParen, tokens.Dot, tokens.LBrack: + return p.parseRef(head, offset) + case tokens.Whitespace: + p.scan() + fallthrough + default: + if _, ok := head.Value.(Var); ok && RootDocumentNames.Contains(head) { + return RefTerm(head).SetLocation(head.Location) + } + return head + } +} + +func (p *Parser) parseNumber() *Term { + var prefix string + loc := p.s.Loc() + + // Handle negative sign + if p.s.tok == tokens.Sub { + prefix = "-" + p.scan() + switch p.s.tok { + case tokens.Number, tokens.Dot: + break + default: + p.illegal("expected number") + return nil + } + } + + // Handle decimal point + if p.s.tok == tokens.Dot { + prefix += "." + p.scan() + if p.s.tok != tokens.Number { + p.illegal("expected number") + return nil + } + } + + // Validate leading zeros: reject numbers like "01", "007", etc. + // Skip validation if prefix ends with '.' (like ".123") + hasDecimalPrefix := len(prefix) > 0 && prefix[len(prefix)-1] == '.' + + if !hasDecimalPrefix && len(p.s.lit) > 1 && p.s.lit[0] == '0' { + // These are the only valid cases starting with '0': + isDecimal := p.s.lit[1] == '.' // "0.123" + isScientific := len(p.s.lit) > 2 && (p.s.lit[1] == 'e' || p.s.lit[1] == 'E') // "0e5", "0E-3" + + if !isDecimal && !isScientific { + p.illegal("expected number without leading zero") + return nil + } + } + + // Ensure that the number is valid + s := prefix + p.s.lit + f, ok := new(big.Float).SetString(s) + if !ok { + p.illegal("invalid float") + return nil + } + + // Put limit on size of exponent to prevent non-linear cost of String() + // function on big.Float from causing denial of service: https://github.com/golang/go/issues/11068 + // + // n == sign * mantissa * 2^exp + // 0.5 <= mantissa < 1.0 + // + // The limit is arbitrary. + exp := f.MantExp(nil) + if exp > 1e5 || exp < -1e5 || f.IsInf() { // +/- inf, exp is 0 + p.error(p.s.Loc(), "number too big") + return nil + } + + // Note: Use the original string, do *not* round trip from + // the big.Float as it can cause precision loss. + return NumberTerm(json.Number(s)).SetLocation(loc) +} + +func (p *Parser) parseString() *Term { + if p.s.lit[0] == '"' { + if p.s.lit == "\"\"" { + return NewTerm(InternedEmptyString.Value).SetLocation(p.s.Loc()) + } + + var s string + err := json.Unmarshal([]byte(p.s.lit), &s) + if err != nil { + p.errorf(p.s.Loc(), "illegal string literal: %s", p.s.lit) + return nil + } + term := StringTerm(s).SetLocation(p.s.Loc()) + return term + } + return p.parseRawString() +} + +func (p *Parser) parseRawString() *Term { + if len(p.s.lit) < 2 { + return nil + } + term := StringTerm(p.s.lit[1 : len(p.s.lit)-1]).SetLocation(p.s.Loc()) + return term +} + +// this is the name to use for instantiating an empty set, e.g., `set()`. +var setConstructor = RefTerm(VarTerm("set")) + +func (p *Parser) parseCall(operator *Term, offset int) (term *Term) { + if !p.enter() { + return nil + } + defer p.leave() + + loc := operator.Location + var end int + + defer func() { + p.setLoc(term, loc, offset, end) + }() + + p.scan() // steps over '(' + + if p.s.tok == tokens.RParen { // no args, i.e. set() or any.func() + end = p.s.tokEnd + p.scanWS() + if operator.Equal(setConstructor) { + return SetTerm() + } + return CallTerm(operator) + } + + if r := p.parseTermList(tokens.RParen, []*Term{operator}); r != nil { + end = p.s.tokEnd + p.scanWS() + return CallTerm(r...) + } + + return nil +} + +func (p *Parser) parseRef(head *Term, offset int) (term *Term) { + if !p.enter() { + return nil + } + defer p.leave() + + loc := head.Location + var end int + + defer func() { + p.setLoc(term, loc, offset, end) + }() + + switch h := head.Value.(type) { + case Var, *Array, Object, Set, *ArrayComprehension, *ObjectComprehension, *SetComprehension, Call: + // ok + default: + p.errorf(loc, "illegal ref (head cannot be %v)", ValueName(h)) + } + + ref := []*Term{head} + + for { + switch p.s.tok { + case tokens.Dot: + p.scanWS() + if p.s.tok != tokens.Ident && !p.isAllowedRefKeyword(p.s.tok) { + p.illegal("expected %v", tokens.Ident) + return nil + } + ref = append(ref, StringTerm(p.s.lit).SetLocation(p.s.Loc())) + p.scanWS() + case tokens.LParen: + term = p.parseCall(p.setLoc(RefTerm(ref...), loc, offset, p.s.loc.Offset), offset) + if term != nil { + switch p.s.tok { + case tokens.Whitespace: + p.scan() + end = p.s.lastEnd + return term + case tokens.Dot, tokens.LBrack: + term = p.parseRef(term, offset) + } + } + end = p.s.tokEnd + return term + case tokens.LBrack: + p.scan() + if term := p.parseTermInfixCall(); term != nil { + if p.s.tok != tokens.RBrack { + p.illegal("expected %v", tokens.LBrack) + return nil + } + ref = append(ref, term) + p.scanWS() + } else { + return nil + } + case tokens.Whitespace: + end = p.s.lastEnd + p.scan() + return RefTerm(ref...) + default: + end = p.s.lastEnd + return RefTerm(ref...) + } + } +} + +func (p *Parser) parseArray() (term *Term) { + if !p.enter() { + return nil + } + defer p.leave() + + loc := p.s.Loc() + offset := p.s.loc.Offset + + defer func() { + p.setLoc(term, loc, offset, p.s.tokEnd) + }() + + p.scan() + + if p.s.tok == tokens.RBrack { + return ArrayTerm() + } + + potentialComprehension := true + + // Skip leading commas, eg [, x, y] + // Supported for backwards compatibility. In the future + // we should make this a parse error. + if p.s.tok == tokens.Comma { + potentialComprehension = false + p.scan() + } + + s := p.save() + + // NOTE(tsandall): The parser cannot attempt a relational term here because + // of ambiguity around comprehensions. For example, given: + // + // {1 | 1} + // + // Does this represent a set comprehension or a set containing binary OR + // call? We resolve the ambiguity by prioritizing comprehensions. + head := p.parseTerm() + + if head == nil { + return nil + } + + switch p.s.tok { + case tokens.RBrack: + return ArrayTerm(head) + case tokens.Comma: + p.scan() + if terms := p.parseTermList(tokens.RBrack, []*Term{head}); terms != nil { + return ArrayTerm(terms...) + } + return nil + case tokens.Or: + if potentialComprehension { + // Try to parse as if it is an array comprehension + p.scan() + if body := p.parseBody(tokens.RBrack); body != nil { + return ArrayComprehensionTerm(head, body) + } + if p.s.tok != tokens.Comma { + return nil + } + } + // fall back to parsing as a normal array definition + } + + p.restore(s) + + if terms := p.parseTermList(tokens.RBrack, nil); terms != nil { + return ArrayTerm(terms...) + } + return nil +} + +func (p *Parser) parseSetOrObject() (term *Term) { + if !p.enter() { + return nil + } + defer p.leave() + + loc := p.s.Loc() + offset := p.s.loc.Offset + + defer func() { + p.setLoc(term, loc, offset, p.s.tokEnd) + }() + + p.scan() + + if p.s.tok == tokens.RBrace { + return ObjectTerm() + } + + potentialComprehension := true + + // Skip leading commas, eg {, x, y} + // Supported for backwards compatibility. In the future + // we should make this a parse error. + if p.s.tok == tokens.Comma { + potentialComprehension = false + p.scan() + } + + s := p.save() + + // Try parsing just a single term first to give comprehensions higher + // priority to "or" calls in ambiguous situations. Eg: { a | b } + // will be a set comprehension. + // + // Note: We don't know yet if it is a set or object being defined. + head := p.parseTerm() + if head == nil { + return nil + } + + switch p.s.tok { + case tokens.Or: + if potentialComprehension { + return p.parseSet(s, head, potentialComprehension) + } + case tokens.RBrace, tokens.Comma: + return p.parseSet(s, head, potentialComprehension) + case tokens.Colon: + return p.parseObject(head, potentialComprehension) + } + + p.restore(s) + + head = p.parseTermInfixCallInList() + if head == nil { + return nil + } + + switch p.s.tok { + case tokens.RBrace, tokens.Comma: + return p.parseSet(s, head, false) + case tokens.Colon: + // It still might be an object comprehension, eg { a+1: b | ... } + return p.parseObject(head, potentialComprehension) + } + + p.illegal("non-terminated set") + return nil +} + +func (p *Parser) parseSet(s *state, head *Term, potentialComprehension bool) *Term { + if !p.enter() { + return nil + } + defer p.leave() + + switch p.s.tok { + case tokens.RBrace: + return SetTerm(head) + case tokens.Comma: + p.scan() + if terms := p.parseTermList(tokens.RBrace, []*Term{head}); terms != nil { + return SetTerm(terms...) + } + case tokens.Or: + if potentialComprehension { + // Try to parse as if it is a set comprehension + p.scan() + if body := p.parseBody(tokens.RBrace); body != nil { + return SetComprehensionTerm(head, body) + } + if p.s.tok != tokens.Comma { + return nil + } + } + // Fall back to parsing as normal set definition + p.restore(s) + if terms := p.parseTermList(tokens.RBrace, nil); terms != nil { + return SetTerm(terms...) + } + } + return nil +} + +func (p *Parser) parseObject(k *Term, potentialComprehension bool) *Term { + if !p.enter() { + return nil + } + defer p.leave() + + // NOTE(tsandall): Assumption: this function is called after parsing the key + // of the head element and then receiving a colon token from the scanner. + // Advance beyond the colon and attempt to parse an object. + if p.s.tok != tokens.Colon { + panic("expected colon") + } + p.scan() + + s := p.save() + + // NOTE(sr): We first try to parse the value as a term (`v`), and see + // if we can parse `{ x: v | ...}` as a comprehension. + // However, if we encounter either a Comma or an RBace, it cannot be + // parsed as a comprehension -- so we save double work further down + // where `parseObjectFinish(k, v, false)` would only exercise the + // same code paths once more. + v := p.parseTerm() + if v == nil { + return nil + } + + potentialRelation := true + if potentialComprehension { + switch p.s.tok { + case tokens.RBrace, tokens.Comma: + potentialRelation = false + fallthrough + case tokens.Or: + if term := p.parseObjectFinish(k, v, true); term != nil { + return term + } + } + } + + p.restore(s) + + if potentialRelation { + v := p.parseTermInfixCallInList() + if v == nil { + return nil + } + + switch p.s.tok { + case tokens.RBrace, tokens.Comma: + return p.parseObjectFinish(k, v, false) + } + } + + p.illegal("non-terminated object") + return nil +} + +func (p *Parser) parseObjectFinish(key, val *Term, potentialComprehension bool) *Term { + if !p.enter() { + return nil + } + defer p.leave() + + switch p.s.tok { + case tokens.RBrace: + return ObjectTerm([2]*Term{key, val}) + case tokens.Or: + if potentialComprehension { + p.scan() + if body := p.parseBody(tokens.RBrace); body != nil { + return ObjectComprehensionTerm(key, val, body) + } + } else { + p.illegal("non-terminated object") + } + case tokens.Comma: + p.scan() + if r := p.parseTermPairList(tokens.RBrace, [][2]*Term{{key, val}}); r != nil { + return ObjectTerm(r...) + } + } + return nil +} + +func (p *Parser) parseTermList(end tokens.Token, r []*Term) []*Term { + if p.s.tok == end { + return r + } + for { + term := p.parseTermInfixCallInList() + if term != nil { + r = append(r, term) + switch p.s.tok { + case end: + return r + case tokens.Comma: + p.scan() + if p.s.tok == end { + return r + } + continue + default: + p.illegal(fmt.Sprintf("expected %q or %q", tokens.Comma, end)) + return nil + } + } + return nil + } +} + +func (p *Parser) parseTermPairList(end tokens.Token, r [][2]*Term) [][2]*Term { + if p.s.tok == end { + return r + } + for { + key := p.parseTermInfixCallInList() + if key != nil { + switch p.s.tok { + case tokens.Colon: + p.scan() + if val := p.parseTermInfixCallInList(); val != nil { + r = append(r, [2]*Term{key, val}) + switch p.s.tok { + case end: + return r + case tokens.Comma: + p.scan() + if p.s.tok == end { + return r + } + continue + default: + p.illegal(fmt.Sprintf("expected %q or %q", tokens.Comma, end)) + return nil + } + } + default: + p.illegal(fmt.Sprintf("expected %q", tokens.Colon)) + return nil + } + } + return nil + } +} + +func (p *Parser) parseTermOp(values ...tokens.Token) *Term { + if slices.Contains(values, p.s.tok) { + r := RefTerm(VarTerm(p.s.tok.String()).SetLocation(p.s.Loc())).SetLocation(p.s.Loc()) + p.scan() + return r + } + return nil +} + +func (p *Parser) parseTermOpName(ref Ref, values ...tokens.Token) *Term { + if slices.Contains(values, p.s.tok) { + cp := ref.Copy() + for _, r := range cp { + r.SetLocation(p.s.Loc()) + } + t := RefTerm(cp...) + t.SetLocation(p.s.Loc()) + p.scan() + return t + } + return nil +} + +func (p *Parser) parseVar() *Term { + + s := p.s.lit + + term := VarTerm(s).SetLocation(p.s.Loc()) + + // Update wildcard values with unique identifiers + if term.Equal(Wildcard) { + term.Value = Var(p.genwildcard()) + } + + return term +} + +func (p *Parser) genwildcard() string { + c := p.s.wildcard + p.s.wildcard++ + return fmt.Sprintf("%v%d", WildcardPrefix, c) +} + +func (p *Parser) error(loc *location.Location, reason string) { + p.errorf(loc, reason) //nolint:govet +} + +func (p *Parser) errorf(loc *location.Location, f string, a ...any) { + msg := strings.Builder{} + msg.WriteString(fmt.Sprintf(f, a...)) + + switch len(p.s.hints) { + case 0: // nothing to do + case 1: + msg.WriteString(" (hint: ") + msg.WriteString(p.s.hints[0]) + msg.WriteRune(')') + default: + msg.WriteString(" (hints: ") + for i, h := range p.s.hints { + if i > 0 { + msg.WriteString(", ") + } + msg.WriteString(h) + } + msg.WriteRune(')') + } + + p.s.errors = append(p.s.errors, &Error{ + Code: ParseErr, + Message: msg.String(), + Location: loc, + Details: newParserErrorDetail(p.s.s.Bytes(), loc.Offset), + }) + p.s.hints = nil +} + +func (p *Parser) hint(f string, a ...any) { + p.s.hints = append(p.s.hints, fmt.Sprintf(f, a...)) +} + +func (p *Parser) illegal(note string, a ...any) { + tok := p.s.tok.String() + + if p.s.tok == tokens.Illegal { + p.errorf(p.s.Loc(), "illegal token") + return + } + + tokType := "token" + if tokens.IsKeyword(p.s.tok) { + tokType = "keyword" + } else if _, ok := allFutureKeywords[p.s.tok.String()]; ok { + tokType = "keyword" + } + + note = fmt.Sprintf(note, a...) + if len(note) > 0 { + p.errorf(p.s.Loc(), "unexpected %s %s: %s", tok, tokType, note) + } else { + p.errorf(p.s.Loc(), "unexpected %s %s", tok, tokType) + } +} + +func (p *Parser) illegalToken() { + p.illegal("") +} + +func (p *Parser) scan() { + p.doScan(true) +} + +func (p *Parser) scanWS() { + p.doScan(false) +} + +func (p *Parser) doScan(skipws bool) { + + // NOTE(tsandall): the last position is used to compute the "text" field for + // complex AST nodes. Whitespace never affects the last position of an AST + // node so do not update it when scanning. + if p.s.tok != tokens.Whitespace { + p.s.lastEnd = p.s.tokEnd + p.s.skippedNL = false + } + + var errs []scanner.Error + for { + var pos scanner.Position + p.s.tok, pos, p.s.lit, errs = p.s.s.Scan() + + p.s.tokEnd = pos.End + p.s.loc.Row = pos.Row + 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) + } + + if len(errs) > 0 { + p.s.tok = tokens.Illegal + } + + if p.s.tok == tokens.Whitespace { + if p.s.lit == "\n" { + p.s.skippedNL = true + } + if skipws { + continue + } + } + + if p.s.tok != tokens.Comment { + break + } + + // For backwards compatibility leave a nil + // Text value if there is no text rather than + // an empty string. + var commentText []byte + if len(p.s.lit) > 1 { + commentText = []byte(p.s.lit[1:]) + } + comment := NewComment(commentText) + comment.SetLoc(p.s.Loc()) + p.s.comments = append(p.s.comments, comment) + } +} + +func (p *Parser) save() *state { + cpy := *p.s + s := *cpy.s + cpy.s = &s + return &cpy +} + +func (p *Parser) restore(s *state) { + p.s = s +} + +func setLocRecursive(x any, loc *location.Location) { + NewGenericVisitor(func(x any) bool { + if node, ok := x.(Node); ok { + node.SetLoc(loc) + } + return false + }).Walk(x) +} + +func (p *Parser) setLoc(term *Term, loc *location.Location, offset, end int) *Term { + if term != nil { + cpy := *loc + term.Location = &cpy + term.Location.Text = p.s.Text(offset, end) + } + return term +} + +func (p *Parser) validateDefaultRuleValue(rule *Rule) bool { + if rule.Head.Value == nil { + p.error(rule.Loc(), "illegal default rule (must have a value)") + return false + } + + valid := true + vis := NewGenericVisitor(func(x any) bool { + switch x.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension: // skip closures + return true + case Ref, Var, Call: + p.error(rule.Loc(), fmt.Sprintf("illegal default rule (value cannot contain %v)", TypeName(x))) + valid = false + return true + } + return false + }) + + vis.Walk(rule.Head.Value.Value) + return valid +} + +func (p *Parser) validateDefaultRuleArgs(rule *Rule) bool { + + valid := true + vars := NewVarSet() + + vis := NewGenericVisitor(func(x any) bool { + switch x := x.(type) { + case Var: + if vars.Contains(x) { + p.error(rule.Loc(), fmt.Sprintf("illegal default rule (arguments cannot be repeated %v)", x)) + valid = false + return true + } + vars.Add(x) + + case *Term: + switch v := x.Value.(type) { + case Var: // do nothing + default: + p.error(rule.Loc(), fmt.Sprintf("illegal default rule (arguments cannot contain %v)", ValueName(v))) + valid = false + return true + } + } + + return false + }) + + vis.Walk(rule.Head.Args) + return valid +} + +// We explicitly use yaml unmarshalling, to accommodate for the '_' in 'related_resources', +// which isn't handled properly by json for some reason. +type rawAnnotation struct { + Scope string `yaml:"scope"` + Title string `yaml:"title"` + Entrypoint bool `yaml:"entrypoint"` + Description string `yaml:"description"` + Organizations []string `yaml:"organizations"` + RelatedResources []any `yaml:"related_resources"` + Authors []any `yaml:"authors"` + Schemas []map[string]any `yaml:"schemas"` + Custom map[string]any `yaml:"custom"` +} + +type metadataParser struct { + buf *bytes.Buffer + comments []*Comment + loc *location.Location +} + +func newMetadataParser(loc *Location) *metadataParser { + return &metadataParser{loc: loc, buf: bytes.NewBuffer(nil)} +} + +func (b *metadataParser) Append(c *Comment) { + b.buf.Write(bytes.TrimPrefix(c.Text, []byte(" "))) + b.buf.WriteByte('\n') + b.comments = append(b.comments, c) +} + +var yamlLineErrRegex = regexp.MustCompile(`^yaml:(?: unmarshal errors:[\n\s]*)? line ([[:digit:]]+):`) + +func (b *metadataParser) Parse() (*Annotations, error) { + + var raw rawAnnotation + + if len(bytes.TrimSpace(b.buf.Bytes())) == 0 { + return nil, errors.New("expected METADATA block, found whitespace") + } + + if err := yaml.Unmarshal(b.buf.Bytes(), &raw); err != nil { + var comment *Comment + match := yamlLineErrRegex.FindStringSubmatch(err.Error()) + if len(match) == 2 { + index, err2 := strconv.Atoi(match[1]) + if err2 == nil { + if index >= len(b.comments) { + comment = b.comments[len(b.comments)-1] + } else { + comment = b.comments[index] + } + b.loc = comment.Location + } + } + + if match == nil && len(b.comments) > 0 { + b.loc = b.comments[0].Location + } + + return nil, augmentYamlError(err, b.comments) + } + + var result Annotations + result.comments = b.comments + result.Scope = raw.Scope + result.Entrypoint = raw.Entrypoint + result.Title = raw.Title + result.Description = raw.Description + result.Organizations = raw.Organizations + + for _, v := range raw.RelatedResources { + rr, err := parseRelatedResource(v) + if err != nil { + return nil, fmt.Errorf("invalid related-resource definition %s: %w", v, err) + } + result.RelatedResources = append(result.RelatedResources, rr) + } + + for _, pair := range raw.Schemas { + k, v := unwrapPair(pair) + + var a SchemaAnnotation + var err error + + a.Path, err = ParseRef(k) + if err != nil { + return nil, errors.New("invalid document reference") + } + + switch v := v.(type) { + case string: + a.Schema, err = parseSchemaRef(v) + if err != nil { + return nil, err + } + case map[string]any: + w, err := convertYAMLMapKeyTypes(v, nil) + if err != nil { + return nil, fmt.Errorf("invalid schema definition: %w", err) + } + a.Definition = &w + default: + return nil, fmt.Errorf("invalid schema declaration for path %q", k) + } + + result.Schemas = append(result.Schemas, &a) + } + + for _, v := range raw.Authors { + author, err := parseAuthor(v) + if err != nil { + return nil, fmt.Errorf("invalid author definition %s: %w", v, err) + } + result.Authors = append(result.Authors, author) + } + + result.Custom = make(map[string]any) + for k, v := range raw.Custom { + val, err := convertYAMLMapKeyTypes(v, nil) + if err != nil { + return nil, err + } + result.Custom[k] = val + } + + 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 +} + +// augmentYamlError augments a YAML error with hints intended to help the user figure out the cause of an otherwise +// cryptic error. These are hints, instead of proper errors, because they are educated guesses, and aren't guaranteed +// to be correct. +func augmentYamlError(err error, comments []*Comment) error { + // Adding hints for when key/value ':' separator isn't suffixed with a legal YAML space symbol + for _, comment := range comments { + txt := string(comment.Text) + parts := strings.Split(txt, ":") + if len(parts) > 1 { + parts = parts[1:] + var invalidSpaces []string + for partIndex, part := range parts { + if len(part) == 0 && partIndex == len(parts)-1 { + invalidSpaces = []string{} + break + } + + r, _ := utf8.DecodeRuneInString(part) + if r == ' ' || r == '\t' { + invalidSpaces = []string{} + break + } + + invalidSpaces = append(invalidSpaces, fmt.Sprintf("%+q", r)) + } + if len(invalidSpaces) > 0 { + err = fmt.Errorf( + "%s\n Hint: on line %d, symbol(s) %v immediately following a key/value separator ':' is not a legal yaml space character", + err.Error(), comment.Location.Row, invalidSpaces) + } + } + } + return err +} + +func unwrapPair(pair map[string]any) (string, any) { + for k, v := range pair { + return k, v + } + return "", nil +} + +var errInvalidSchemaRef = errors.New("invalid schema reference") + +// NOTE(tsandall): 'schema' is not registered as a root because it's not +// supported by the compiler or evaluator today. Once we fix that, we can remove +// this function. +func parseSchemaRef(s string) (Ref, error) { + + term, err := ParseTerm(s) + if err == nil { + switch v := term.Value.(type) { + case Var: + if term.Equal(SchemaRootDocument) { + return SchemaRootRef.Copy(), nil + } + case Ref: + if v.HasPrefix(SchemaRootRef) { + return v, nil + } + } + } + + return nil, errInvalidSchemaRef +} + +func parseRelatedResource(rr any) (*RelatedResourceAnnotation, error) { + rr, err := convertYAMLMapKeyTypes(rr, nil) + if err != nil { + return nil, err + } + + switch rr := rr.(type) { + case string: + if len(rr) > 0 { + u, err := url.Parse(rr) + if err != nil { + return nil, err + } + return &RelatedResourceAnnotation{Ref: *u}, nil + } + return nil, errors.New("ref URL may not be empty string") + case map[string]any: + description := strings.TrimSpace(getSafeString(rr, "description")) + ref := strings.TrimSpace(getSafeString(rr, "ref")) + if len(ref) > 0 { + u, err := url.Parse(ref) + if err != nil { + return nil, err + } + return &RelatedResourceAnnotation{Description: description, Ref: *u}, nil + } + return nil, errors.New("'ref' value required in object") + } + + return nil, errors.New("invalid value type, must be string or map") +} + +func parseAuthor(a any) (*AuthorAnnotation, error) { + a, err := convertYAMLMapKeyTypes(a, nil) + if err != nil { + return nil, err + } + + switch a := a.(type) { + case string: + return parseAuthorString(a) + case map[string]any: + name := strings.TrimSpace(getSafeString(a, "name")) + email := strings.TrimSpace(getSafeString(a, "email")) + if len(name) > 0 || len(email) > 0 { + return &AuthorAnnotation{name, email}, nil + } + return nil, errors.New("'name' and/or 'email' values required in object") + } + + return nil, errors.New("invalid value type, must be string or map") +} + +func getSafeString(m map[string]any, k string) string { + if v, found := m[k]; found { + if s, ok := v.(string); ok { + return s + } + } + return "" +} + +const emailPrefix = "<" +const emailSuffix = ">" + +// parseAuthor parses a string into an AuthorAnnotation. If the last word of the input string is enclosed within <>, +// it is extracted as the author's email. The email may not contain whitelines, as it then will be interpreted as +// multiple words. +func parseAuthorString(s string) (*AuthorAnnotation, error) { + parts := strings.Fields(s) + + if len(parts) == 0 { + return nil, errors.New("author is an empty string") + } + + namePartCount := len(parts) + trailing := parts[namePartCount-1] + var email string + if len(trailing) >= len(emailPrefix)+len(emailSuffix) && strings.HasPrefix(trailing, emailPrefix) && + strings.HasSuffix(trailing, emailSuffix) { + email = trailing[len(emailPrefix):] + email = email[0 : len(email)-len(emailSuffix)] + namePartCount -= 1 + } + + name := strings.Join(parts[0:namePartCount], " ") + + return &AuthorAnnotation{Name: name, Email: email}, nil +} + +func convertYAMLMapKeyTypes(x any, path []string) (any, error) { + var err error + switch x := x.(type) { + case map[any]any: + result := make(map[string]any, len(x)) + for k, v := range x { + str, ok := k.(string) + if !ok { + return nil, fmt.Errorf("invalid map key type(s): %v", strings.Join(path, "/")) + } + result[str], err = convertYAMLMapKeyTypes(v, append(path, str)) + if err != nil { + return nil, err + } + } + return result, nil + case []any: + for i := range x { + x[i], err = convertYAMLMapKeyTypes(x[i], append(path, strconv.Itoa(i))) + if err != nil { + return nil, err + } + } + return x, nil + default: + return x, nil + } +} + +// futureKeywords is the source of truth for future keywords that will +// eventually become standard keywords inside of Rego. +var futureKeywords = map[string]tokens.Token{} + +// futureKeywordsV0 is the source of truth for future keywords that were +// not yet a standard part of Rego in v0, and required importing. +var futureKeywordsV0 = map[string]tokens.Token{ + "in": tokens.In, + "every": tokens.Every, + "contains": tokens.Contains, + "if": tokens.If, +} + +var allFutureKeywords map[string]tokens.Token + +func IsFutureKeyword(s string) bool { + return IsFutureKeywordForRegoVersion(s, RegoV1) +} + +func IsFutureKeywordForRegoVersion(s string, v RegoVersion) bool { + var yes bool + + switch v { + case RegoV0, RegoV0CompatV1: + _, yes = futureKeywordsV0[s] + case RegoV1: + _, yes = futureKeywords[s] + } + + return yes +} + +func (p *Parser) futureImport(imp *Import, allowedFutureKeywords map[string]tokens.Token) { + path := imp.Path.Value.(Ref) + + if len(path) == 1 || !path[1].Equal(InternedTerm("keywords")) { + p.errorf(imp.Path.Location, "invalid import, must be `future.keywords`") + return + } + + if imp.Alias != "" { + p.errorf(imp.Path.Location, "`future` imports cannot be aliased") + return + } + + kwds := make([]string, 0, len(allowedFutureKeywords)) + for k := range allowedFutureKeywords { + kwds = append(kwds, k) + } + + switch len(path) { + case 2: // all keywords imported, nothing to do + case 3: // one keyword imported + kw, ok := path[2].Value.(String) + if !ok { + p.errorf(imp.Path.Location, "invalid import, must be `future.keywords.x`, e.g. `import future.keywords.in`") + return + } + keyword := string(kw) + _, ok = allowedFutureKeywords[keyword] + if !ok { + sort.Strings(kwds) // so the error message is stable + p.errorf(imp.Path.Location, "unexpected keyword, must be one of %v", kwds) + return + } + + kwds = []string{keyword} // overwrite + } + for _, kw := range kwds { + p.s.s.AddKeyword(kw, allowedFutureKeywords[kw]) + } +} + +func (p *Parser) regoV1Import(imp *Import) { + if !p.po.Capabilities.ContainsFeature(FeatureRegoV1Import) && !p.po.Capabilities.ContainsFeature(FeatureRegoV1) { + p.errorf(imp.Path.Location, "invalid import, `%s` is not supported by current capabilities", RegoV1CompatibleRef) + return + } + + path := imp.Path.Value.(Ref) + + // v1 is only valid option + if len(path) == 1 || !path[1].Equal(RegoV1CompatibleRef[1]) || len(path) > 2 { + p.errorf(imp.Path.Location, "invalid import `%s`, must be `%s`", path, RegoV1CompatibleRef) + return + } + + if p.po.EffectiveRegoVersion() == RegoV1 { + // We're parsing for Rego v1, where the 'rego.v1' import is a no-op. + return + } + + if imp.Alias != "" { + p.errorf(imp.Path.Location, "`rego` imports cannot be aliased") + return + } + + // import all future keywords with the rego.v1 import + kwds := make([]string, 0, len(futureKeywordsV0)) + for k := range futureKeywordsV0 { + kwds = append(kwds, k) + } + + p.s.s.SetRegoV1Compatible() + for _, kw := range kwds { + p.s.s.AddKeyword(kw, futureKeywordsV0[kw]) + } +} + +func init() { + allFutureKeywords = map[string]tokens.Token{} + maps.Copy(allFutureKeywords, futureKeywords) + maps.Copy(allFutureKeywords, futureKeywordsV0) +} + +// enter increments the recursion depth counter and checks if it exceeds the maximum. +// Returns false if the maximum is exceeded, true otherwise. +// If p.maxRecursionDepth is 0 or negative, the check is effectively disabled. +func (p *Parser) enter() bool { + p.recursionDepth++ + if p.maxRecursionDepth > 0 && p.recursionDepth > p.maxRecursionDepth { + p.error(p.s.Loc(), ErrMaxParsingRecursionDepthExceeded.Error()) + p.recursionDepth-- + return false + } + return true +} + +// leave decrements the recursion depth counter. +func (p *Parser) leave() { + p.recursionDepth-- +} diff --git a/third_party/opa/v1/ast/parser_bench_test.go b/third_party/opa/v1/ast/parser_bench_test.go new file mode 100644 index 000000000000..9a7da64d1c81 --- /dev/null +++ b/third_party/opa/v1/ast/parser_bench_test.go @@ -0,0 +1,242 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "strconv" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/util" +) + +// BenchmarkParseModuleRulesBase gives a baseline for parsing modules with +// what are extremely simple rules. +func BenchmarkParseModuleRulesBase(b *testing.B) { + sizes := []int{1, 10, 100, 1000} + for _, size := range sizes { + b.Run(strconv.Itoa(size), func(b *testing.B) { + mod := generateModule(size) + runParseModuleBenchmark(b, mod) + }) + } +} + +// BenchmarkParseStatementBasic gives a baseline for parsing a simple +// statement with a single call and two variables +func BenchmarkParseStatementBasicCall(b *testing.B) { + runParseStatementBenchmark(b, `a+b`) +} + +func BenchmarkParseStatementMixedJSON(b *testing.B) { + // While nothing in OPA is Kubernetes specific, the webhook admission + // request payload makes for an interesting parse test being a moderately + // deep nested object with several different types of values. + stmt := `{"uid":"d8fdc6db-44e1-11e9-a10f-021ca99d149a","kind":{"group":"apps","version":"v1beta1","kind":"Deployment"},"resource":{"group":"apps","version":"v1beta1","resource":"deployments"},"namespace":"opa-test","operation":"CREATE","userInfo":{"username":"user@acme.com","groups":["system:authenticated"]},"object":{"metadata":{"name":"nginx","namespace":"torin-opa-test","uid":"d8fdc047-44e1-11e9-a10f-021ca99d149a","generation":1,"creationTimestamp":"2019-03-12T16:14:01Z","labels":{"run":"nginx"}},"spec":{"replicas":1,"selector":{"matchLabels":{"run":"nginx"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"run":"nginx"}},"spec":{"containers":[{"name":"nginx","image":"nginx","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","securityContext":{},"schedulerName":"default-scheduler"}},"strategy":{"type":"RollingUpdate","rollingUpdate":{"maxUnavailable":"25%","maxSurge":"25%"}},"revisionHistoryLimit":2,"progressDeadlineSeconds":600},"status":{}},"oldObject":null}` + runParseStatementBenchmark(b, stmt) +} + +// BenchmarkParseStatementSimpleArray gives a baseline for parsing arrays of strings. +// There is no nesting, so all test cases are flat array structures. +func BenchmarkParseStatementSimpleArray(b *testing.B) { + sizes := []int{1, 10, 100, 1000} + for _, size := range sizes { + b.Run(strconv.Itoa(size), func(b *testing.B) { + stmt := generateArrayStatement(size) + runParseStatementBenchmark(b, stmt) + }) + } +} + +func TestParseStatementSimpleArray(b *testing.T) { + sizes := []int{10} // , 10, 100, 1000} + for _, size := range sizes { + b.Run(strconv.Itoa(size), func(b *testing.T) { + stmt := generateArrayStatement(size) + _, err := ParseStatement(stmt) + if err != nil { + b.Fatalf("Unexpected error: %s", err) + } + }) + } +} + +// BenchmarkParseStatementNestedObjects gives a baseline for parsing objects. +// This includes "flat" ones and more deeply nested varieties. +func BenchmarkParseStatementNestedObjects(b *testing.B) { + sizes := [][]int{{1, 1}, {5, 1}, {10, 1}, {1, 5}, {1, 10}, {5, 5}} // Note: 10x10 will essentially hang while parsing + for _, size := range sizes { + b.Run(fmt.Sprintf("%dx%d", size[0], size[1]), func(b *testing.B) { + stmt := generateObjectStatement(size[0], size[1]) + runParseStatementBenchmark(b, stmt) + }) + } +} + +// BenchmarkParseDeepNesting tests the impact of recursion depth tracking +// on parsing performance with deeply nested structures (arrays and objects). +// Different depths are used to measure the overhead at various nesting levels. +func BenchmarkParseDeepNesting(b *testing.B) { + depths := []int{10, 50, 100, 500, 2500, 12500} + + b.Run("NestedArrays", func(b *testing.B) { + for _, depth := range depths { + b.Run(fmt.Sprintf("depth-%d", depth), func(b *testing.B) { + stmt := generateDeeplyNestedArray(depth) + runParseStatementBenchmark(b, stmt) + }) + } + }) + + b.Run("NestedObjects", func(b *testing.B) { + for _, depth := range depths { + b.Run(fmt.Sprintf("depth-%d", depth), func(b *testing.B) { + stmt := generateDeeplyNestedObject(depth) + runParseStatementBenchmark(b, stmt) + }) + } + }) +} + +func BenchmarkParseStatementNestedObjectsOrSets(b *testing.B) { + sizes := []int{1, 5, 10, 15, 20} + for _, size := range sizes { + b.Run(strconv.Itoa(size), func(b *testing.B) { + stmt := generateObjectOrSetStatement(size) + runParseStatementBenchmarkWithError(b, stmt) + }) + } +} + +func BenchmarkParseBasicABACModule(b *testing.B) { + mod := ` + package app.abac + + default allow = false + + allow if { + user_is_owner + } + + allow if { + user_is_employee + action_is_read + } + + allow if { + user_is_employee + user_is_senior + action_is_update + } + + allow if { + user_is_customer + action_is_read + not pet_is_adopted + } + + user_is_owner if { + data.user_attributes[input.user].title == "owner" + } + + user_is_employee if { + data.user_attributes[input.user].title == "employee" + } + + user_is_customer if { + data.user_attributes[input.user].title == "customer" + } + + user_is_senior if { + data.user_attributes[input.user].tenure > 8 + } + + action_is_read if { + input.action == "read" + } + + action_is_update if { + input.action == "update" + } + + pet_is_adopted if { + data.pet_attributes[input.resource].adopted == true + } + ` + runParseModuleBenchmark(b, mod) +} + +func runParseModuleBenchmark(b *testing.B, mod string) { + b.ResetTimer() + for range b.N { + _, err := ParseModuleWithOpts("", mod, ParserOptions{AllFutureKeywords: true}) + if err != nil { + b.Fatalf("Unexpected error: %s", err) + } + } +} + +func runParseStatementBenchmark(b *testing.B, stmt string) { + b.ResetTimer() + for range b.N { + _, err := ParseStatement(stmt) + if err != nil { + b.Fatalf("Unexpected error: %s", err) + } + } +} + +func runParseStatementBenchmarkWithError(b *testing.B, stmt string) { + b.ResetTimer() + for range b.N { + _, err := ParseStatement(stmt) + if err == nil { + b.Fatalf("Expected error: %s", err) + } + } +} + +func generateModule(numRules int) string { + mod := "package bench\n" + for i := range numRules { + mod += fmt.Sprintf("p%d if { input.x%d = %d }\n", i, i, i) + } + return mod +} + +func generateArrayStatement(size int) string { + a := make([]string, size) + for i := range size { + a[i] = fmt.Sprintf("entry-%d", i) + } + return string(util.MustMarshalJSON(a)) +} + +func generateObjectStatement(width, depth int) string { + o := generateObject(width, depth) + return string(util.MustMarshalJSON(o)) +} + +func generateObject(width, depth int) map[string]any { + o := map[string]any{} + for i := range width { + key := fmt.Sprintf("entry-%d", i) + if depth <= 1 { + o[key] = "value" + } else { + o[key] = generateObject(width, depth-1) + } + } + return o +} + +func generateObjectOrSetStatement(depth int) string { + s := strings.Builder{} + for i := range depth { + fmt.Fprintf(&s, `{a%d:a%d|`, i, i) + } + return s.String() +} diff --git a/third_party/opa/v1/ast/parser_ext.go b/third_party/opa/v1/ast/parser_ext.go new file mode 100644 index 000000000000..42b0503690c7 --- /dev/null +++ b/third_party/opa/v1/ast/parser_ext.go @@ -0,0 +1,814 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// This file contains extra functions for parsing Rego. +// Most of the parsing is handled by the code in parser.go, +// however, there are additional utilities that are +// helpful for dealing with Rego source inputs (e.g., REPL +// statements, source files, etc.) + +package ast + +import ( + "bytes" + "errors" + "fmt" + "slices" + "strings" + "unicode" + + "github.com/open-policy-agent/opa/v1/ast/internal/tokens" +) + +// MustParseBody returns a parsed body. +// If an error occurs during parsing, panic. +func MustParseBody(input string) Body { + return MustParseBodyWithOpts(input, ParserOptions{}) +} + +// MustParseBodyWithOpts returns a parsed body. +// If an error occurs during parsing, panic. +func MustParseBodyWithOpts(input string, opts ParserOptions) Body { + parsed, err := ParseBodyWithOpts(input, opts) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseExpr returns a parsed expression. +// If an error occurs during parsing, panic. +func MustParseExpr(input string) *Expr { + parsed, err := ParseExpr(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseImports returns a slice of imports. +// If an error occurs during parsing, panic. +func MustParseImports(input string) []*Import { + parsed, err := ParseImports(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseModule returns a parsed module. +// If an error occurs during parsing, panic. +func MustParseModule(input string) *Module { + return MustParseModuleWithOpts(input, ParserOptions{}) +} + +// MustParseModuleWithOpts returns a parsed module. +// If an error occurs during parsing, panic. +func MustParseModuleWithOpts(input string, opts ParserOptions) *Module { + parsed, err := ParseModuleWithOpts("", input, opts) + if err != nil { + panic(err) + } + return parsed +} + +// MustParsePackage returns a Package. +// If an error occurs during parsing, panic. +func MustParsePackage(input string) *Package { + parsed, err := ParsePackage(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseStatements returns a slice of parsed statements. +// If an error occurs during parsing, panic. +func MustParseStatements(input string) []Statement { + parsed, _, err := ParseStatements("", input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseStatement returns exactly one statement. +// If an error occurs during parsing, panic. +func MustParseStatement(input string) Statement { + parsed, err := ParseStatement(input) + if err != nil { + panic(err) + } + return parsed +} + +func MustParseStatementWithOpts(input string, popts ParserOptions) Statement { + parsed, err := ParseStatementWithOpts(input, popts) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseRef returns a parsed reference. +// If an error occurs during parsing, panic. +func MustParseRef(input string) Ref { + parsed, err := ParseRef(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseRule returns a parsed rule. +// If an error occurs during parsing, panic. +func MustParseRule(input string) *Rule { + parsed, err := ParseRule(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseRuleWithOpts returns a parsed rule. +// If an error occurs during parsing, panic. +func MustParseRuleWithOpts(input string, opts ParserOptions) *Rule { + parsed, err := ParseRuleWithOpts(input, opts) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseTerm returns a parsed term. +// If an error occurs during parsing, panic. +func MustParseTerm(input string) *Term { + parsed, err := ParseTerm(input) + if err != nil { + panic(err) + } + return parsed +} + +// ParseRuleFromBody returns a rule if the body can be interpreted as a rule +// definition. Otherwise, an error is returned. +func ParseRuleFromBody(module *Module, body Body) (*Rule, error) { + + if len(body) != 1 { + return nil, errors.New("multiple expressions cannot be used for rule head") + } + + return ParseRuleFromExpr(module, body[0]) +} + +// ParseRuleFromExpr returns a rule if the expression can be interpreted as a +// rule definition. +func ParseRuleFromExpr(module *Module, expr *Expr) (*Rule, error) { + + if len(expr.With) > 0 { + return nil, errors.New("expressions using with keyword cannot be used for rule head") + } + + if expr.Negated { + return nil, errors.New("negated expressions cannot be used for rule head") + } + + if _, ok := expr.Terms.(*SomeDecl); ok { + return nil, errors.New("'some' declarations cannot be used for rule head") + } + + if term, ok := expr.Terms.(*Term); ok { + switch v := term.Value.(type) { + case Ref: + if len(v) > 2 { // 2+ dots + return ParseCompleteDocRuleWithDotsFromTerm(module, term) + } + return ParsePartialSetDocRuleFromTerm(module, term) + default: + return nil, fmt.Errorf("%v cannot be used for rule name", ValueName(v)) + } + } + + if _, ok := expr.Terms.([]*Term); !ok { + // This is a defensive check in case other kinds of expression terms are + // introduced in the future. + return nil, errors.New("expression cannot be used for rule head") + } + + if expr.IsEquality() { + return parseCompleteRuleFromEq(module, expr) + } else if expr.IsAssignment() { + rule, err := parseCompleteRuleFromEq(module, expr) + if err != nil { + return nil, err + } + rule.Head.Assign = true + return rule, nil + } + + if _, ok := BuiltinMap[expr.Operator().String()]; ok { + return nil, errors.New("rule name conflicts with built-in function") + } + + return ParseRuleFromCallExpr(module, expr.Terms.([]*Term)) +} + +func parseCompleteRuleFromEq(module *Module, expr *Expr) (rule *Rule, err error) { + + // ensure the rule location is set to the expr location + // the helper functions called below try to set the location based + // on the terms they've been provided but that is not as accurate. + defer func() { + if rule != nil { + rule.Location = expr.Location + rule.Head.Location = expr.Location + } + }() + + lhs, rhs := expr.Operand(0), expr.Operand(1) + if lhs == nil || rhs == nil { + return nil, errors.New("assignment requires two operands") + } + + rule, err = ParseRuleFromCallEqExpr(module, lhs, rhs) + if err == nil { + return rule, nil + } + + rule, err = ParsePartialObjectDocRuleFromEqExpr(module, lhs, rhs) + if err == nil { + return rule, nil + } + + return ParseCompleteDocRuleFromEqExpr(module, lhs, rhs) +} + +// ParseCompleteDocRuleFromAssignmentExpr returns a rule if the expression can +// be interpreted as a complete document definition declared with the assignment +// operator. +func ParseCompleteDocRuleFromAssignmentExpr(module *Module, lhs, rhs *Term) (*Rule, error) { + + rule, err := ParseCompleteDocRuleFromEqExpr(module, lhs, rhs) + if err != nil { + return nil, err + } + + rule.Head.Assign = true + + return rule, nil +} + +// ParseCompleteDocRuleFromEqExpr returns a rule if the expression can be +// interpreted as a complete document definition. +func ParseCompleteDocRuleFromEqExpr(module *Module, lhs, rhs *Term) (*Rule, error) { + var head *Head + + if v, ok := lhs.Value.(Var); ok { + // Modify the code to add the location to the head ref + head = VarHead(v, lhs.Location, nil) + } else if r, ok := lhs.Value.(Ref); ok { // groundness ? + if _, ok := r[0].Value.(Var); !ok { + return nil, fmt.Errorf("invalid rule head: %v", r) + } + head = RefHead(r) + if len(r) > 1 && !r[len(r)-1].IsGround() { + return nil, errors.New("ref not ground") + } + } else { + return nil, fmt.Errorf("%v cannot be used for rule name", ValueName(lhs.Value)) + } + head.Value = rhs + head.Location = lhs.Location + + body := NewBody(NewExpr(BooleanTerm(true).SetLocation(rhs.Location)).SetLocation(rhs.Location)) + + return &Rule{ + Location: lhs.Location, + Head: head, + Body: body, + Module: module, + generatedBody: true, + }, nil +} + +func ParseCompleteDocRuleWithDotsFromTerm(module *Module, term *Term) (*Rule, error) { + ref, ok := term.Value.(Ref) + if !ok { + return nil, fmt.Errorf("%v cannot be used for rule name", ValueName(term.Value)) + } + + if _, ok := ref[0].Value.(Var); !ok { + return nil, fmt.Errorf("invalid rule head: %v", ref) + } + head := RefHead(ref, BooleanTerm(true).SetLocation(term.Location)) + head.generatedValue = true + head.Location = term.Location + + body := NewBody(NewExpr(BooleanTerm(true).SetLocation(term.Location)).SetLocation(term.Location)) + + return &Rule{ + Location: term.Location, + Head: head, + Body: body, + Module: module, + }, nil +} + +// ParsePartialObjectDocRuleFromEqExpr returns a rule if the expression can be +// interpreted as a partial object document definition. +func ParsePartialObjectDocRuleFromEqExpr(module *Module, lhs, rhs *Term) (*Rule, error) { + ref, ok := lhs.Value.(Ref) + if !ok { + return nil, fmt.Errorf("%v cannot be used as rule name", ValueName(lhs.Value)) + } + + if _, ok := ref[0].Value.(Var); !ok { + return nil, fmt.Errorf("invalid rule head: %v", ref) + } + + head := RefHead(ref, rhs) + if len(ref) == 2 { // backcompat for naked `foo.bar = "baz"` statements + head.Name = ref[0].Value.(Var) + head.Key = ref[1] + } + head.Location = rhs.Location + + body := NewBody(NewExpr(BooleanTerm(true).SetLocation(rhs.Location)).SetLocation(rhs.Location)) + + rule := &Rule{ + Location: rhs.Location, + Head: head, + Body: body, + Module: module, + } + + return rule, nil +} + +// ParsePartialSetDocRuleFromTerm returns a rule if the term can be interpreted +// as a partial set document definition. +func ParsePartialSetDocRuleFromTerm(module *Module, term *Term) (*Rule, error) { + + ref, ok := term.Value.(Ref) + if !ok || len(ref) == 1 { + return nil, fmt.Errorf("%vs cannot be used for rule head", ValueName(term.Value)) + } + if _, ok := ref[0].Value.(Var); !ok { + return nil, fmt.Errorf("invalid rule head: %v", ref) + } + + head := RefHead(ref) + if len(ref) == 2 { + v, ok := ref[0].Value.(Var) + if !ok { + return nil, fmt.Errorf("%vs cannot be used for rule head", ValueName(term.Value)) + } + // Modify the code to add the location to the head ref + head = VarHead(v, ref[0].Location, nil) + head.Key = ref[1] + } + head.Location = term.Location + + body := NewBody(NewExpr(BooleanTerm(true).SetLocation(term.Location)).SetLocation(term.Location)) + + rule := &Rule{ + Location: term.Location, + Head: head, + Body: body, + Module: module, + } + + return rule, nil +} + +// ParseRuleFromCallEqExpr returns a rule if the term can be interpreted as a +// function definition (e.g., f(x) = y => f(x) = y { true }). +func ParseRuleFromCallEqExpr(module *Module, lhs, rhs *Term) (*Rule, error) { + + call, ok := lhs.Value.(Call) + if !ok { + return nil, errors.New("must be call") + } + + ref, ok := call[0].Value.(Ref) + if !ok { + return nil, fmt.Errorf("%vs cannot be used in function signature", ValueName(call[0].Value)) + } + if _, ok := ref[0].Value.(Var); !ok { + return nil, fmt.Errorf("invalid rule head: %v", ref) + } + + head := RefHead(ref, rhs) + head.Location = lhs.Location + head.Args = Args(call[1:]) + + body := NewBody(NewExpr(BooleanTerm(true).SetLocation(rhs.Location)).SetLocation(rhs.Location)) + + rule := &Rule{ + Location: lhs.Location, + Head: head, + Body: body, + Module: module, + } + + return rule, nil +} + +// ParseRuleFromCallExpr returns a rule if the terms can be interpreted as a +// function returning true or some value (e.g., f(x) => f(x) = true { true }). +func ParseRuleFromCallExpr(module *Module, terms []*Term) (*Rule, error) { + + if len(terms) <= 1 { + return nil, errors.New("rule argument list must take at least one argument") + } + + loc := terms[0].Location + ref := terms[0].Value.(Ref) + if _, ok := ref[0].Value.(Var); !ok { + return nil, fmt.Errorf("invalid rule head: %v", ref) + } + head := RefHead(ref, BooleanTerm(true).SetLocation(loc)) + head.Location = loc + head.Args = terms[1:] + + body := NewBody(NewExpr(BooleanTerm(true).SetLocation(loc)).SetLocation(loc)) + + rule := &Rule{ + Location: loc, + Head: head, + Module: module, + Body: body, + } + return rule, nil +} + +// ParseImports returns a slice of Import objects. +func ParseImports(input string) ([]*Import, error) { + stmts, _, err := ParseStatements("", input) + if err != nil { + return nil, err + } + result := []*Import{} + for _, stmt := range stmts { + if imp, ok := stmt.(*Import); ok { + result = append(result, imp) + } else { + return nil, fmt.Errorf("expected import but got %T", stmt) + } + } + return result, nil +} + +// ParseModule returns a parsed Module object. +// For details on Module objects and their fields, see policy.go. +// Empty input will return nil, nil. +func ParseModule(filename, input string) (*Module, error) { + return ParseModuleWithOpts(filename, input, ParserOptions{}) +} + +// ParseModuleWithOpts returns a parsed Module object, and has an additional input ParserOptions +// For details on Module objects and their fields, see policy.go. +// Empty input will return nil, nil. +func ParseModuleWithOpts(filename, input string, popts ParserOptions) (*Module, error) { + stmts, comments, err := ParseStatementsWithOpts(filename, input, popts) + if err != nil { + return nil, err + } + return parseModule(filename, stmts, comments, popts.RegoVersion) +} + +// ParseBody returns exactly one body. +// If multiple bodies are parsed, an error is returned. +func ParseBody(input string) (Body, error) { + return ParseBodyWithOpts(input, ParserOptions{SkipRules: true}) +} + +// ParseBodyWithOpts returns exactly one body. It does _not_ set SkipRules: true on its own, +// but respects whatever ParserOptions it's been given. +func ParseBodyWithOpts(input string, popts ParserOptions) (Body, error) { + + stmts, _, err := ParseStatementsWithOpts("", input, popts) + if err != nil { + return nil, err + } + + result := Body{} + + for _, stmt := range stmts { + switch stmt := stmt.(type) { + case Body: + for i := range stmt { + result.Append(stmt[i]) + } + case *Comment: + // skip + default: + return nil, fmt.Errorf("expected body but got %T", stmt) + } + } + + return result, nil +} + +// ParseExpr returns exactly one expression. +// If multiple expressions are parsed, an error is returned. +func ParseExpr(input string) (*Expr, error) { + body, err := ParseBody(input) + if err != nil { + return nil, fmt.Errorf("failed to parse expression: %w", err) + } + if len(body) != 1 { + return nil, fmt.Errorf("expected exactly one expression but got: %v", body) + } + return body[0], nil +} + +// ParsePackage returns exactly one Package. +// If multiple statements are parsed, an error is returned. +func ParsePackage(input string) (*Package, error) { + stmt, err := ParseStatement(input) + if err != nil { + return nil, err + } + pkg, ok := stmt.(*Package) + if !ok { + return nil, fmt.Errorf("expected package but got %T", stmt) + } + return pkg, nil +} + +// ParseTerm returns exactly one term. +// If multiple terms are parsed, an error is returned. +func ParseTerm(input string) (*Term, error) { + body, err := ParseBody(input) + if err != nil { + return nil, fmt.Errorf("failed to parse term: %w", err) + } + if len(body) != 1 { + return nil, fmt.Errorf("expected exactly one term but got: %v", body) + } + term, ok := body[0].Terms.(*Term) + if !ok { + return nil, fmt.Errorf("expected term but got %v", body[0].Terms) + } + return term, nil +} + +// ParseRef returns exactly one reference. +func ParseRef(input string) (Ref, error) { + term, err := ParseTerm(input) + if err != nil { + return nil, fmt.Errorf("failed to parse ref: %w", err) + } + ref, ok := term.Value.(Ref) + if !ok { + return nil, fmt.Errorf("expected ref but got %v", term) + } + return ref, nil +} + +// ParseRuleWithOpts returns exactly one rule. +// If multiple rules are parsed, an error is returned. +func ParseRuleWithOpts(input string, opts ParserOptions) (*Rule, error) { + stmts, _, err := ParseStatementsWithOpts("", input, opts) + if err != nil { + return nil, err + } + if len(stmts) != 1 { + return nil, fmt.Errorf("expected exactly one statement (rule), got %v = %T, %T", stmts, stmts[0], stmts[1]) + } + rule, ok := stmts[0].(*Rule) + if !ok { + return nil, fmt.Errorf("expected rule but got %T", stmts[0]) + } + return rule, nil +} + +// ParseRule returns exactly one rule. +// If multiple rules are parsed, an error is returned. +func ParseRule(input string) (*Rule, error) { + return ParseRuleWithOpts(input, ParserOptions{}) +} + +// ParseStatement returns exactly one statement. +// A statement might be a term, expression, rule, etc. Regardless, +// this function expects *exactly* one statement. If multiple +// statements are parsed, an error is returned. +func ParseStatement(input string) (Statement, error) { + stmts, _, err := ParseStatements("", input) + if err != nil { + return nil, err + } + if len(stmts) != 1 { + return nil, errors.New("expected exactly one statement") + } + return stmts[0], nil +} + +func ParseStatementWithOpts(input string, popts ParserOptions) (Statement, error) { + stmts, _, err := ParseStatementsWithOpts("", input, popts) + if err != nil { + return nil, err + } + if len(stmts) != 1 { + return nil, errors.New("expected exactly one statement") + } + return stmts[0], nil +} + +// ParseStatements is deprecated. Use ParseStatementWithOpts instead. +func ParseStatements(filename, input string) ([]Statement, []*Comment, error) { + return ParseStatementsWithOpts(filename, input, ParserOptions{}) +} + +// ParseStatementsWithOpts returns a slice of parsed statements. This is the +// default return value from the parser. +func ParseStatementsWithOpts(filename, input string, popts ParserOptions) ([]Statement, []*Comment, error) { + + parser := NewParser(). + WithFilename(filename). + WithReader(bytes.NewBufferString(input)). + WithProcessAnnotation(popts.ProcessAnnotation). + WithFutureKeywords(popts.FutureKeywords...). + WithAllFutureKeywords(popts.AllFutureKeywords). + WithCapabilities(popts.Capabilities). + WithSkipRules(popts.SkipRules). + WithRegoVersion(popts.RegoVersion). + withUnreleasedKeywords(popts.unreleasedKeywords) + + stmts, comments, errs := parser.Parse() + + if len(errs) > 0 { + return nil, nil, errs + } + + return stmts, comments, nil +} + +func parseModule(filename string, stmts []Statement, comments []*Comment, regoCompatibilityMode RegoVersion) (*Module, error) { + + if len(stmts) == 0 { + return nil, NewError(ParseErr, &Location{File: filename}, "empty module") + } + + var errs Errors + + pkg, ok := stmts[0].(*Package) + if !ok { + loc := stmts[0].Loc() + errs = append(errs, NewError(ParseErr, loc, "package expected")) + } + + mod := &Module{ + Package: pkg, + stmts: stmts, + } + + // The comments slice only holds comments that were not their own statements. + mod.Comments = append(mod.Comments, comments...) + + if regoCompatibilityMode == RegoUndefined { + mod.regoVersion = DefaultRegoVersion + } else { + mod.regoVersion = regoCompatibilityMode + } + + for i, stmt := range stmts[1:] { + switch stmt := stmt.(type) { + case *Import: + mod.Imports = append(mod.Imports, stmt) + if mod.regoVersion == RegoV0 && Compare(stmt.Path.Value, RegoV1CompatibleRef) == 0 { + mod.regoVersion = RegoV0CompatV1 + } + case *Rule: + setRuleModule(stmt, mod) + mod.Rules = append(mod.Rules, stmt) + case Body: + rule, err := ParseRuleFromBody(mod, stmt) + if err != nil { + errs = append(errs, NewError(ParseErr, stmt[0].Location, err.Error())) //nolint:govet + continue + } + rule.generatedBody = true + mod.Rules = append(mod.Rules, rule) + + // NOTE(tsandall): the statement should now be interpreted as a + // rule so update the statement list. This is important for the + // logic below that associates annotations with statements. + stmts[i+1] = rule + case *Package: + errs = append(errs, NewError(ParseErr, stmt.Loc(), "unexpected package")) + case *Annotations: + mod.Annotations = append(mod.Annotations, stmt) + case *Comment: + // Ignore comments, they're handled above. + default: + panic("illegal value") // Indicates grammar is out-of-sync with code. + } + } + + if mod.regoVersion == RegoV0CompatV1 || mod.regoVersion == RegoV1 { + for _, rule := range mod.Rules { + for r := rule; r != nil; r = r.Else { + errs = append(errs, CheckRegoV1(r)...) + } + } + } + + if len(errs) > 0 { + return nil, errs + } + + errs = append(errs, attachAnnotationsNodes(mod)...) + + if len(errs) > 0 { + return nil, errs + } + + attachRuleAnnotations(mod) + + return mod, nil +} + +func ruleDeclarationHasKeyword(rule *Rule, keyword tokens.Token) bool { + return slices.Contains(rule.Head.keywords, keyword) +} + +func newScopeAttachmentErr(a *Annotations, want string) *Error { + var have string + if a.node != nil { + have = fmt.Sprintf(" (have %v)", TypeName(a.node)) + } + return NewError(ParseErr, a.Loc(), "annotation scope '%v' must be applied to %v%v", a.Scope, want, have) +} + +func setRuleModule(rule *Rule, module *Module) { + rule.Module = module + if rule.Else != nil { + setRuleModule(rule.Else, module) + } +} + +// ParserErrorDetail holds additional details for parser errors. +type ParserErrorDetail struct { + Line string `json:"line"` + Idx int `json:"idx"` +} + +func newParserErrorDetail(bs []byte, offset int) *ParserErrorDetail { + + // Find first non-space character at or before offset position. + if offset >= len(bs) { + offset = len(bs) - 1 + } else if offset < 0 { + offset = 0 + } + + for offset > 0 && unicode.IsSpace(rune(bs[offset])) { + offset-- + } + + // Find beginning of line containing offset. + begin := offset + + for begin > 0 && !isNewLineChar(bs[begin]) { + begin-- + } + + if isNewLineChar(bs[begin]) { + begin++ + } + + // Find end of line containing offset. + end := offset + + for end < len(bs) && !isNewLineChar(bs[end]) { + end++ + } + + if begin > end { + begin = end + } + + // Extract line and compute index of offset byte in line. + line := bs[begin:end] + index := offset - begin + + return &ParserErrorDetail{ + Line: string(line), + Idx: index, + } +} + +// Lines returns the pretty formatted line output for the error details. +func (d ParserErrorDetail) Lines() []string { + line := strings.TrimLeft(d.Line, "\t") // remove leading tabs + tabCount := len(d.Line) - len(line) + indent := max(d.Idx-tabCount, 0) + return []string{line, strings.Repeat(" ", indent) + "^"} +} + +func isNewLineChar(b byte) bool { + return b == '\r' || b == '\n' +} diff --git a/third_party/opa/v1/ast/parser_ext_test.go b/third_party/opa/v1/ast/parser_ext_test.go new file mode 100644 index 000000000000..a844c7365908 --- /dev/null +++ b/third_party/opa/v1/ast/parser_ext_test.go @@ -0,0 +1,128 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast_test + +import ( + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/format" +) + +func TestParseModule_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + mod string + expRules []string + expErrs []string + }{ + { + note: "v0", // NOT default rego-version + mod: `package test +p[x] { + x = "a" +}`, + expErrs: []string{ + "test.rego:2: rego_parse_error: `if` keyword is required before rule body", + "test.rego:2: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "import rego.v1", + mod: `package test +import rego.v1 + +p contains x if { + x = "a" +}`, + expRules: []string{"p"}, + }, + { + note: "v1", // default rego-version + mod: `package test +p contains x if { + x = "a" +}`, + expRules: []string{"p"}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + m, err := ast.ParseModule("test.rego", tc.mod) + + if len(tc.expErrs) > 0 { + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\ngot:\n\n%s", expErr, err.Error()) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if len(m.Rules) != len(tc.expRules) { + t.Fatalf("Expected %d rules, got %d", len(tc.expRules), len(m.Rules)) + } + for i, r := range m.Rules { + if r.Head.Name.String() != tc.expRules[i] { + t.Fatalf("Expected rule %q, got %q", tc.expRules[i], r.Head.Name.String()) + } + } + } + }) + } +} + +func TestParseBody_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + body string + expStmts int + assertSame bool + }{ + { + note: "v0", // default rego-version + body: `x := ["a", "b", "c"][i] +`, + expStmts: 1, + assertSame: true, + }, + { + note: "v1", // NOT default rego-version + body: `some x, i in ["a", "b", "c"] +`, + expStmts: 1, + assertSame: true, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + body, err := ast.ParseBody(tc.body) + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if len(body) != tc.expStmts { + t.Fatalf("Expected %d statements, got %d:%q\n\n", tc.expStmts, len(body), body) + } + + if tc.assertSame { + formatted, err := format.AstWithOpts(body, format.Opts{RegoVersion: ast.RegoV1}) // every body is v1-compatible + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if strings.Compare(string(formatted), tc.body) != 0 { + t.Fatalf("Expected body to be %q, got %q", tc.body, string(formatted)) + } + } + }) + } +} diff --git a/third_party/opa/v1/ast/parser_test.go b/third_party/opa/v1/ast/parser_test.go new file mode 100644 index 000000000000..f09afe90f9af --- /dev/null +++ b/third_party/opa/v1/ast/parser_test.go @@ -0,0 +1,8272 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast/internal/tokens" +) + +const ( + testModule = ` +# This policy module belongs the opa.example package. +package opa.examples + +# Refer to data.servers as servers. +import data.servers +# Refer to the data.networks as networks. +import data.networks +# Refer to the data.ports as ports. +import data.ports + +# A server exists in the violations set if... +violations contains server if { + # ...the server exists + server = servers[i] + # ...and any of the server’s protocols is HTTP + server.protocols[j] = "http" + # ...and the server is public. + public_servers[server] +} + +# A server exists in the public_servers set if... +public_servers contains server if { + # Semicolons are optional. Can group expressions onto one line. + server = servers[i]; server.ports[j] = ports[k].id # ...and the server is connected to a port + ports[k].networks[l] = networks[m].id; # ...and the port is connected to a network + networks[m].public = true # ...and the network is public. +}` +) + +func TestNumberTerms(t *testing.T) { + + tests := []struct { + input string + expected string + }{ + {"0", "0"}, + {"100", "100"}, + {"-1", "-1"}, + {"1e6", "1e6"}, + {"1.1e6", "1.1e6"}, + {"-1e-6", "-1e-6"}, + {"1E6", "1E6"}, + {"0.1E6", "0.1E6"}, + {"0.1e6", "0.1e6"}, + {"0.1e-6", "0.1e-6"}, + {"0e6", "0e6"}, + {"0e-6", "0e-6"}, + {"0.1", "0.1"}, + {".1", "0.1"}, + {".0001", "0.0001"}, + {"-.1", "-0.1"}, + {"-0.0001", "-0.0001"}, + {"1e1000", "1e1000"}, + {"0e1", "0"}, + {"-0.1", "-0.1"}, + } + + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + result, err := ParseTerm(tc.input) + if err != nil { + t.Errorf("Unexpected error for %v: %v", tc.input, err) + } else { + e := NumberTerm(json.Number(tc.expected)) + if !result.Equal(e) { + t.Errorf("Expected %v for %v but got: %v", e, tc.input, result) + } + } + }) + } + + errorTests := map[string]struct { + input string + expectedError string + }{ + "leading 0": { + input: "03", + expectedError: "expected number without leading zero", + }, + "leading 0, many": { + input: "003", + expectedError: "expected number without leading zero", + }, + "leading 0, 'octal'": { + input: "0755", + expectedError: "expected number without leading zero", + }, + "leading 0, decimal": { + input: "03.333", + expectedError: "expected number without leading zero", + }, + "leading 0, negative": { + input: "-03", + expectedError: "expected number without leading zero", + }, + "leading 0, exp": { + input: "03e6", + expectedError: "expected number without leading zero", + }, + "leading 0, exp, negative": { + input: "-03e6", + expectedError: "expected number without leading zero", + }, + } + + for name, tc := range errorTests { + t.Run(name, func(t *testing.T) { + assertParseErrorContains(t, name, tc.input, tc.expectedError) + }) + } +} + +func TestStringTerms(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {`""`, ""}, // empty + {`" "`, " "}, // whitespace + {`"\""`, `"`}, // escaped quote + {`"http:\/\/"`, `http://`}, // escaped solidus + {`"\u0001"`, "\x01"}, // control code + {`"foo\u005C"`, "foo\u005c"}, // unicode (upper hex) + {`"foo\u005c"`, "foo\u005C"}, // unicode (lower hex) + {`"\uD834\uDD1E"`, `𝄞`}, // g-clef + {"`hi\\there`", `hi\there`}, // basic raw string + {"`foo\nbar\n baz`", `foo +bar + baz`}, // multi-line raw string + } + + for _, tc := range tests { + result, err := ParseTerm(tc.input) + if err != nil { + t.Errorf("Unexpected error for %v: %v", tc.input, err) + } else { + s := StringTerm(tc.expected) + if !result.Equal(s) { + t.Errorf("Expected %v for %v but got: %v", s, tc.input, result) + } + } + } +} + +func TestScalarTerms(t *testing.T) { + assertParseOneTerm(t, "null", "null", NullTerm()) + assertParseOneTerm(t, "true", "true", BooleanTerm(true)) + assertParseOneTerm(t, "false", "false", BooleanTerm(false)) + assertParseOneTerm(t, "integer", "53", IntNumberTerm(53)) + assertParseOneTerm(t, "integer2", "-53", IntNumberTerm(-53)) + assertParseOneTerm(t, "float", "16.7", FloatNumberTerm(16.7)) + assertParseOneTerm(t, "float2", "-16.7", FloatNumberTerm(-16.7)) + assertParseOneTerm(t, "exponent", "6e7", FloatNumberTerm(6e7)) + assertParseOneTerm(t, "string", "\"a string\"", StringTerm("a string")) + assertParseOneTerm(t, "string", "\"a string u6abc7def8abc0def with unicode\"", StringTerm("a string u6abc7def8abc0def with unicode")) + assertParseErrorContains(t, "hex", "6abc", "illegal number format") + assertParseErrorContains(t, "non-terminated", "\"foo", "non-terminated string") + assertParseErrorContains(t, "non-terminated-raw", "`foo", "non-terminated string") + assertParseErrorContains(t, "non-string", "'a string'", "illegal token") + assertParseErrorContains(t, "non-number", "6zxy", "illegal number format") + assertParseErrorContains(t, "non-number2", "6d7", "illegal number format") + assertParseErrorContains(t, "non-number3", "6\"foo\"", "expected exactly one statement") // ?? + assertParseErrorContains(t, "non-number4", "6true", "illegal number format") + assertParseErrorContains(t, "non-number5", "6false", "illegal number format") + assertParseErrorContains(t, "non-number6", "6[null, null]", "illegal ref (head cannot be number)") // ?? + assertParseErrorContains(t, "non-number7", "6{\"foo\": \"bar\"}", "expected exactly one statement") + assertParseErrorContains(t, "non-number8", ".0.", "expected fraction") + assertParseErrorContains(t, "non-number9", "0e", "expected exponent") + assertParseErrorContains(t, "non-number10", "0e.", "expected exponent") + assertParseErrorContains(t, "non-number11", "0F", "illegal number format") + assertParseErrorContains(t, "non-number12", "00", "expected number") + assertParseErrorContains(t, "non-number13", "00.1", "expected number") + assertParseErrorContains(t, "non-number14", "-00", "expected number") + assertParseErrorContains(t, "non-number15", "-00.1", "expected number") + assertParseErrorContains(t, "non-number16", "-00.01", "expected number") + assertParseErrorContains(t, "non-number17", "00e1", "expected number") + assertParseErrorContains(t, "non-number18", "-00e1", "expected number") + assertParseErrorContains(t, "parsing float fails", "7e3000000000", "invalid float") + assertParseErrorContains(t, "float is +inf", "10245423601e680507880", "number too big") + assertParseErrorContains(t, "float is -inf", "-10245423601e680507880", "number too big") + + // f := big.NewFloat(1); f.SetMantExp(f, -1e6); f.String() // => 1.010034059e-301030 (this takes ~9s) + assertParseErrorContains(t, "float exp < -1e5", "1.010034059e-301030", "number too big") + + // g := big.NewFloat(1); g.SetMantExp(g, 1e6); g.String() // => 9.900656229e+301029 + assertParseErrorContains(t, "float exp > 1e5", "9.900656229e+301029", "number too big") +} + +func TestVarTerms(t *testing.T) { + assertParseOneTerm(t, "var", "foo", VarTerm("foo")) + assertParseOneTerm(t, "var", "foo_bar", VarTerm("foo_bar")) + assertParseOneTerm(t, "var", "foo0", VarTerm("foo0")) + assertParseOneTerm(t, "import prefix", "imports", VarTerm("imports")) + assertParseOneTerm(t, "not prefix", "not_foo", VarTerm("not_foo")) + assertParseOneTerm(t, `package prefix`, "packages", VarTerm("packages")) + assertParseOneTerm(t, `true prefix`, "trueish", VarTerm("trueish")) + assertParseOneTerm(t, `false prefix`, "false_flag", VarTerm("false_flag")) + assertParseOneTerm(t, `null prefix`, "nullable", VarTerm("nullable")) + assertParseError(t, "illegal token", `墳`) + assertParseError(t, "not keyword", "not") + assertParseError(t, `package keyword`, "package") + assertParseError(t, "import keyword", "import") + assertParseError(t, "import invalid path", "import x.") +} + +func TestRefTerms(t *testing.T) { + assertParseOneTerm(t, "constants", "foo.bar.baz", RefTerm(VarTerm("foo"), StringTerm("bar"), StringTerm("baz"))) + assertParseOneTerm(t, "constants 2", "foo.bar[0].baz", RefTerm(VarTerm("foo"), StringTerm("bar"), IntNumberTerm(0), StringTerm("baz"))) + assertParseOneTerm(t, "variables", "foo.bar[0].baz[i]", RefTerm(VarTerm("foo"), StringTerm("bar"), IntNumberTerm(0), StringTerm("baz"), VarTerm("i"))) + assertParseOneTerm(t, "spaces", "foo[\"white space\"].bar", RefTerm(VarTerm("foo"), StringTerm("white space"), StringTerm("bar"))) + assertParseOneTerm(t, "nested", "foo[baz[1][borge[i]]].bar", RefTerm( + VarTerm("foo"), + RefTerm( + VarTerm("baz"), IntNumberTerm(1), RefTerm( + VarTerm("borge"), VarTerm("i"), + ), + ), + StringTerm("bar"), + )) + assertParseOneTerm(t, "composite operand 1", "foo[[1,2,3]].bar", RefTerm(VarTerm("foo"), ArrayTerm(NumberTerm("1"), NumberTerm("2"), NumberTerm("3")), StringTerm("bar"))) + assertParseOneTerm(t, "composite operand 2", `foo[{"foo": 2}].bar`, RefTerm(VarTerm("foo"), ObjectTerm(Item(StringTerm("foo"), NumberTerm("2"))), StringTerm("bar"))) + + assertParseError(t, "missing component 1", "foo.") + assertParseError(t, "missing component 2", "foo[].bar") + assertParseError(t, "invalid composite operand", "foo[1,2]") + assertParseError(t, "invalid call", "bar(..") + assertParseError(t, "invalid ref", "bar[..") + assertParseError(t, "invalid ref head type number", "0[0]") + assertParseError(t, "invalid ref head type number (float)", "1.2[0]") + assertParseError(t, "invalid ref head type string", `"foo"[0]`) + assertParseError(t, "invalid ref head type string (dot)", `"foo".bar`) +} + +func TestRefTermsContainingKeywords(t *testing.T) { + for _, regoVersion := range []RegoVersion{RegoV0, RegoV1} { + popts := ParserOptions{RegoVersion: regoVersion} + + t.Run(regoVersion.String(), func(t *testing.T) { + for _, kw := range Keywords { + t.Run(kw, func(t *testing.T) { + input := "foo." + kw + exp := RefTerm(VarTerm("foo"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = "input." + kw + exp = RefTerm(VarTerm("input"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = "data." + kw + exp = RefTerm(VarTerm("data"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = fmt.Sprintf("data.%s.foo", kw) + exp = RefTerm(VarTerm("data"), StringTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + + input = fmt.Sprintf(`data.%s["foo"]`, kw) + exp = RefTerm(VarTerm("data"), StringTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + + input = "data.foo." + kw + exp = RefTerm(VarTerm("data"), StringTerm("foo"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = `data["foo"].` + kw + exp = RefTerm(VarTerm("data"), StringTerm("foo"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = kw + ".foo" + exp = RefTerm(VarTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + + input = kw + `["foo"]` + exp = RefTerm(VarTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + }) + } + }) + } + + t.Run("v0 with future keywords", func(t *testing.T) { + caps := CapabilitiesForThisVersion(CapabilitiesRegoVersion(RegoV0)) + popts := ParserOptions{RegoVersion: RegoV0, Capabilities: caps} + + for kw := range futureKeywordsV0 { + popts.FutureKeywords = []string{kw} + + t.Run(kw, func(t *testing.T) { + input := "foo." + kw + exp := RefTerm(VarTerm("foo"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = "input." + kw + exp = RefTerm(VarTerm("input"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = "data." + kw + exp = RefTerm(VarTerm("data"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = fmt.Sprintf("data.%s.foo", kw) + exp = RefTerm(VarTerm("data"), StringTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + + input = fmt.Sprintf(`data.%s["foo"]`, kw) + exp = RefTerm(VarTerm("data"), StringTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + + input = "data.foo." + kw + exp = RefTerm(VarTerm("data"), StringTerm("foo"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = `data["foo"].` + kw + exp = RefTerm(VarTerm("data"), StringTerm("foo"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = kw + ".foo" + exp = RefTerm(VarTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + + input = kw + `["foo"]` + exp = RefTerm(VarTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + }) + } + }) +} + +func dropCapabilityFeature(caps *Capabilities, feature string) *Capabilities { + feats := make([]string, 0, len(caps.Features)) + for _, f := range caps.Features { + if f != feature { + feats = append(feats, f) + } + } + caps.Features = feats + return caps +} + +func TestRefTermsContainingKeywords_NoCapability(t *testing.T) { + for _, regoVersion := range []RegoVersion{RegoV0, RegoV1} { + caps := CapabilitiesForThisVersion(CapabilitiesRegoVersion(regoVersion)) + caps = dropCapabilityFeature(caps, FeatureKeywordsInRefs) + popts := ParserOptions{RegoVersion: regoVersion, Capabilities: caps} + + t.Run(regoVersion.String(), func(t *testing.T) { + for _, kw := range KeywordsForRegoVersion(regoVersion) { + t.Run(kw, func(t *testing.T) { + input := "foo." + kw + expErr := fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + foo.%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = "input." + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + input.%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = "data." + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + data.%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = fmt.Sprintf("data.%s.foo", kw) + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + data.%s.foo + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = fmt.Sprintf(`data.%s["foo"]`, kw) + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + data.%s["foo"] + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = "data.foo." + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + data.foo.%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = `data["foo"].` + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + data["foo"].%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + // Special cases for leading kw in ref + // FIXME: The output from before the kw-in-ref change is preserved; but can be improved + switch kw { + case "null": + input = kw + ".foo" + expErr = fmt.Sprintf(`rego_parse_error: illegal ref (head cannot be null) + %s.foo + ^`, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = kw + `["foo"]` + expErr = fmt.Sprintf(`rego_parse_error: illegal ref (head cannot be null) + %s["foo"] + ^`, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + case "true", "false": + input = kw + ".foo" + expErr = fmt.Sprintf(`rego_parse_error: illegal ref (head cannot be boolean) + %s.foo + ^`, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = kw + `["foo"]` + expErr = fmt.Sprintf(`rego_parse_error: illegal ref (head cannot be boolean) + %s["foo"] + ^`, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + case "some": + input = kw + ".foo" + expErr = `rego_parse_error: unexpected . token: expected var + some.foo + ^` + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = kw + `["foo"]` + expErr = `rego_parse_error: unexpected [ token: expected var + some["foo"] + ^` + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + case "every": + input = kw + ".foo" + expErr = `rego_parse_error: unexpected identifier token: expected number + every.foo + ^` + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = kw + `["foo"]` + expErr = `rego_parse_error: unexpected eof token: expected ` + "`" + `x[, y] in xs { ... }` + "`" + ` expression + every["foo"] + ^` + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + case "contains": + input = kw + `.foo` + exp := RefTerm(VarTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + + input = kw + `["foo"]` + exp = RefTerm(VarTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + default: + input = kw + ".foo" + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword + %s.foo + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = kw + `["foo"]` + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword + %s["foo"] + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + } + }) + } + }) + } + + t.Run("v0 with future keywords", func(t *testing.T) { + caps := CapabilitiesForThisVersion(CapabilitiesRegoVersion(RegoV0)) + caps = dropCapabilityFeature(caps, FeatureKeywordsInRefs) + popts := ParserOptions{RegoVersion: RegoV0, Capabilities: caps} + + for kw := range futureKeywordsV0 { + popts.FutureKeywords = []string{kw} + + t.Run(kw, func(t *testing.T) { + input := "foo." + kw + expErr := fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + foo.%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = "input." + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + input.%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = "data." + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + data.%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = fmt.Sprintf("data.%s.foo", kw) + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + data.%s.foo + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = fmt.Sprintf(`data.%s["foo"]`, kw) + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + data.%s["foo"] + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = "data.foo." + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + data.foo.%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = `data["foo"].` + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected identifier + data["foo"].%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + switch kw { + case "every": + input = kw + ".foo" + expErr = `rego_parse_error: unexpected identifier token: expected number + every.foo + ^` + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = kw + `["foo"]` + expErr = `rego_parse_error: unexpected eof token: expected ` + "`" + `x[, y] in xs { ... }` + "`" + ` expression + every["foo"] + ^` + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + case "contains": + input = kw + `.foo` + exp := RefTerm(VarTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + + input = kw + `["foo"]` + exp = RefTerm(VarTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + default: + input = kw + ".foo" + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword + %s.foo + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = kw + `["foo"]` + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword + %s["foo"] + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + } + }) + } + }) +} + +func TestCallRefTermsContainingKeywords(t *testing.T) { + for _, regoVersion := range []RegoVersion{RegoV0, RegoV1} { + popts := ParserOptions{RegoVersion: regoVersion} + + t.Run(regoVersion.String(), func(t *testing.T) { + for _, kw := range Keywords { + t.Run(kw, func(t *testing.T) { + input := fmt.Sprintf("foo.%s(42)", kw) + exp := NewExpr([]*Term{RefTerm(VarTerm("foo"), StringTerm(kw)), NumberTerm("42")}) + assertParseOneExpr(t, input, input, exp, popts) + + input = fmt.Sprintf("input.%s(42)", kw) + exp = NewExpr([]*Term{RefTerm(VarTerm("input"), StringTerm(kw)), NumberTerm("42")}) + assertParseOneExpr(t, input, input, exp, popts) + + input = fmt.Sprintf("data.%s(42)", kw) + exp = NewExpr([]*Term{RefTerm(VarTerm("data"), StringTerm(kw)), NumberTerm("42")}) + assertParseOneExpr(t, input, input, exp, popts) + + input = fmt.Sprintf("data.%s.foo(42)", kw) + exp = NewExpr([]*Term{RefTerm(VarTerm("data"), StringTerm(kw), StringTerm("foo")), NumberTerm("42")}) + assertParseOneExpr(t, input, input, exp, popts) + + input = fmt.Sprintf(`data.%s["foo"](42)`, kw) + exp = NewExpr([]*Term{RefTerm(VarTerm("data"), StringTerm(kw), StringTerm("foo")), NumberTerm("42")}) + assertParseOneExpr(t, input, input, exp, popts) + + input = fmt.Sprintf("data.foo.%s(42)", kw) + exp = NewExpr([]*Term{RefTerm(VarTerm("data"), StringTerm("foo"), StringTerm(kw)), NumberTerm("42")}) + assertParseOneExpr(t, input, input, exp, popts) + + input = fmt.Sprintf(`data["foo"].%s(42)`, kw) + exp = NewExpr([]*Term{RefTerm(VarTerm("data"), StringTerm("foo"), StringTerm(kw)), NumberTerm("42")}) + assertParseOneExpr(t, input, input, exp, popts) + + input = kw + ".foo(42)" + exp = NewExpr([]*Term{RefTerm(VarTerm(kw), StringTerm("foo")), NumberTerm("42")}) + assertParseOneExpr(t, input, input, exp, popts) + + input = kw + `["foo"](42)` + exp = NewExpr([]*Term{RefTerm(VarTerm(kw), StringTerm("foo")), NumberTerm("42")}) + assertParseOneExpr(t, input, input, exp, popts) + }) + } + }) + } + + t.Run("v0 with future keywords", func(t *testing.T) { + caps := CapabilitiesForThisVersion(CapabilitiesRegoVersion(RegoV0)) + popts := ParserOptions{RegoVersion: RegoV0, Capabilities: caps} + + for kw := range futureKeywordsV0 { + popts.FutureKeywords = []string{kw} + + t.Run(kw, func(t *testing.T) { + input := "foo." + kw + exp := RefTerm(VarTerm("foo"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = "input." + kw + exp = RefTerm(VarTerm("input"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = "data." + kw + exp = RefTerm(VarTerm("data"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = fmt.Sprintf("data.%s.foo", kw) + exp = RefTerm(VarTerm("data"), StringTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + + input = fmt.Sprintf(`data.%s["foo"]`, kw) + exp = RefTerm(VarTerm("data"), StringTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + + input = "data.foo." + kw + exp = RefTerm(VarTerm("data"), StringTerm("foo"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = `data["foo"].` + kw + exp = RefTerm(VarTerm("data"), StringTerm("foo"), StringTerm(kw)) + assertParseOneTerm(t, input, input, exp, popts) + + input = kw + ".foo" + exp = RefTerm(VarTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + + input = kw + `["foo"]` + exp = RefTerm(VarTerm(kw), StringTerm("foo")) + assertParseOneTerm(t, input, input, exp, popts) + }) + } + }) +} + +func TestImportContainingKeywords(t *testing.T) { + for _, regoVersion := range []RegoVersion{RegoV0, RegoV1} { + popts := ParserOptions{RegoVersion: regoVersion} + + t.Run(regoVersion.String(), func(t *testing.T) { + for _, kw := range KeywordsForRegoVersion(regoVersion) { + t.Run(kw, func(t *testing.T) { + // Keywords are allowed mid-path in import paths. + + input := fmt.Sprintf("import data.%s.foo", kw) + exp := &Import{Path: RefTerm(VarTerm("data"), StringTerm(kw), StringTerm("foo"))} + t.Run(input, func(t *testing.T) { + assertParseImport(t, input, input, exp, popts) + }) + + input = fmt.Sprintf(`import data.%s["foo"]`, kw) + exp = &Import{Path: RefTerm(VarTerm("data"), StringTerm(kw), StringTerm("foo"))} + t.Run(input, func(t *testing.T) { + assertParseImport(t, input, input, exp, popts) + }) + + // Keywords are not allowed as the first component of import paths (only 'data, 'input', 'future', and 'rego' allowed). + + input = "import " + kw + expErr := fmt.Sprintf(`rego_parse_error: unexpected import path, must begin with one of: {data, future, input, rego}, got: %s (hint: if this is unexpected, try updating OPA) + import %s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = fmt.Sprintf("import %s.foo", kw) + expErr = fmt.Sprintf(`rego_parse_error: unexpected import path, must begin with one of: {data, future, input, rego}, got: %s (hint: if this is unexpected, try updating OPA) + import %s.foo + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = fmt.Sprintf(`import %s["foo"]`, kw) + expErr = fmt.Sprintf(`rego_parse_error: unexpected import path, must begin with one of: {data, future, input, rego}, got: %s (hint: if this is unexpected, try updating OPA) + import %s["foo"] + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + // Keywords are not allowed as the last component of import paths .. + + input = "import input." + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected import path, must not end with a keyword, got: %s + import input.%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = "import data." + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected import path, must not end with a keyword, got: %s + import data.%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = "import data.foo." + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected import path, must not end with a keyword, got: %s + import data.foo.%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + input = `import data["foo"].` + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected import path, must not end with a keyword, got: %s + import data["foo"].%s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + + // .. unless they have an alias. + + input = fmt.Sprintf("import data.%s as foo", kw) + exp = &Import{Path: RefTerm(VarTerm("data"), StringTerm(kw)), Alias: "foo"} + assertParseImport(t, input, input, exp, popts) + + input = fmt.Sprintf(`import data["%s"] as foo`, kw) + exp = &Import{Path: RefTerm(VarTerm("data"), StringTerm(kw)), Alias: "foo"} + assertParseImport(t, input, input, exp, popts) + + // Keywords are not allowed as import aliases + + input = "import data.foo as " + kw + expErr = fmt.Sprintf(`rego_parse_error: unexpected %s keyword: expected var + import data.foo as %s + ^`, kw, kw) + t.Run(input, func(t *testing.T) { + assertParseErrorContains(t, input, input, expErr, popts) + }) + }) + } + }) + } +} + +func TestPackageContainingKeywords(t *testing.T) { + for _, regoVersion := range []RegoVersion{RegoV0, RegoV1} { + popts := ParserOptions{RegoVersion: regoVersion} + + t.Run(regoVersion.String(), func(t *testing.T) { + for _, kw := range KeywordsForRegoVersion(regoVersion) { + t.Run(kw, func(t *testing.T) { + // Keywords are allowed mid-path in package paths. + + input := fmt.Sprintf("package foo.%s.bar", kw) + exp := &Package{Path: []*Term{VarTerm("data"), StringTerm("foo"), StringTerm(kw), StringTerm("bar")}} + t.Run(input, func(t *testing.T) { + assertParsePackage(t, input, input, exp, popts) + }) + + input = fmt.Sprintf(`package foo.%s["bar"]`, kw) + exp = &Package{Path: []*Term{VarTerm("data"), StringTerm("foo"), StringTerm(kw), StringTerm("bar")}} + t.Run(input, func(t *testing.T) { + assertParsePackage(t, input, input, exp, popts) + }) + + // Keywords are allowed as the first component of package paths. + + input = "package " + kw + exp = &Package{Path: []*Term{VarTerm("data"), StringTerm(kw)}} + t.Run(input, func(t *testing.T) { + assertParsePackage(t, input, input, exp, popts) + }) + + input = fmt.Sprintf("package %s.foo", kw) + exp = &Package{Path: []*Term{VarTerm("data"), StringTerm(kw), StringTerm("foo")}} + t.Run(input, func(t *testing.T) { + assertParsePackage(t, input, input, exp, popts) + }) + + input = fmt.Sprintf(`package %s["foo"]`, kw) + exp = &Package{Path: []*Term{VarTerm("data"), StringTerm(kw), StringTerm("foo")}} + t.Run(input, func(t *testing.T) { + assertParsePackage(t, input, input, exp, popts) + }) + + // Keywords are allowed as the last component of import paths. + + input = "package foo." + kw + exp = &Package{Path: []*Term{VarTerm("data"), StringTerm("foo"), StringTerm(kw)}} + t.Run(input, func(t *testing.T) { + assertParsePackage(t, input, input, exp, popts) + }) + + input = fmt.Sprintf(`package foo["%s"]`, kw) + exp = &Package{Path: []*Term{VarTerm("data"), StringTerm("foo"), StringTerm(kw)}} + t.Run(input, func(t *testing.T) { + assertParsePackage(t, input, input, exp, popts) + }) + }) + } + }) + } +} + +func TestRuleHeadsContainingKeywords(t *testing.T) { + for _, kw := range Keywords { + t.Run(kw, func(t *testing.T) { + // Complete rules + + note := "complete rule, kw name" + input := kw + " if { true }" + t.Run(note, func(t *testing.T) { + _, err := ParseRule(input) + if err == nil { + t.Error("Expected error, got none") + } + }) + + note = "complete rule with ref in head, kw in first term" + input = kw + `.foo if { true }` + exp := &Rule{ + Head: RefHead([]*Term{VarTerm(kw), StringTerm("foo")}, BooleanTerm(true)), + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "complete rule with ref in head, kw last term" + input = fmt.Sprintf(`foo.%s if { true }`, kw) + exp = &Rule{ + Head: RefHead([]*Term{VarTerm("foo"), StringTerm(kw)}, BooleanTerm(true)), + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "complete rule with ref in head, kw last term (bracketed)" + input = fmt.Sprintf(`foo["%s"] if { true }`, kw) + exp = &Rule{ + Head: RefHead([]*Term{VarTerm("foo"), StringTerm(kw)}, BooleanTerm(true)), + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "complete rule with ref in head, kw middle term" + input = fmt.Sprintf(`foo.%s.bar if { true }`, kw) + exp = &Rule{ + Head: RefHead([]*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, BooleanTerm(true)), + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "complete rule with ref in head, kw middle term (bracketed)" + input = fmt.Sprintf(`foo["%s"].bar if { true }`, kw) + exp = &Rule{ + Head: RefHead([]*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, BooleanTerm(true)), + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + // Functions + + note = "function, kw name" + input = kw + "(x, y) if { true }" + t.Run(note, func(t *testing.T) { + _, err := ParseRule(input) + if err == nil { + t.Error("Expected error, got none") + } + }) + + note = "function with ref in head, kw in first term" + input = kw + `.foo(x, y) if { true }` + head := RefHead([]*Term{VarTerm(kw), StringTerm("foo")}, BooleanTerm(true)) + head.Name = Var(kw) + head.Args = []*Term{VarTerm("x"), VarTerm("y")} + exp = &Rule{ + Head: head, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "function with ref in head, kw last term" + input = fmt.Sprintf(`foo.%s(x, y) if { true }`, kw) + head = RefHead([]*Term{VarTerm("foo"), StringTerm(kw)}, BooleanTerm(true)) + head.Name = "foo" + head.Args = []*Term{VarTerm("x"), VarTerm("y")} + exp = &Rule{ + Head: head, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "function with ref in head, kw last term (bracketed)" + input = fmt.Sprintf(`foo["%s"](x, y) if { true }`, kw) + head = RefHead([]*Term{VarTerm("foo"), StringTerm(kw)}, BooleanTerm(true)) + head.Name = "foo" + head.Args = []*Term{VarTerm("x"), VarTerm("y")} + exp = &Rule{ + Head: head, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "function with ref in head, kw middle term" + input = fmt.Sprintf(`foo.%s.bar(x, y) if { true }`, kw) + head = RefHead([]*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, BooleanTerm(true)) + head.Args = []*Term{VarTerm("x"), VarTerm("y")} + exp = &Rule{ + Head: head, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "function with ref in head, kw middle term (bracketed)" + input = fmt.Sprintf(`foo["%s"].bar(x, y) if { true }`, kw) + head = RefHead([]*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, BooleanTerm(true)) + head.Args = []*Term{VarTerm("x"), VarTerm("y")} + exp = &Rule{ + Head: head, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + // Partial set rules + + note = "partial set rule, kw name" + input = kw + " contains { true }" + t.Run(note, func(t *testing.T) { + _, err := ParseRule(input) + if err == nil { + t.Error("Expected error, got none") + } + }) + + note = "partial set rule with ref in head, kw in first term" + input = kw + `.foo contains 1 if { true }` + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm(kw), StringTerm("foo")}, + Key: NumberTerm("1"), + }, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "partial set rule with ref in head, kw last term" + input = fmt.Sprintf(`foo.%s contains 1 if { true }`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw)}, + Key: NumberTerm("1"), + }, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "partial set rule with ref in head, kw last term (bracketed)" + input = fmt.Sprintf(`foo["%s"] contains 1 if { true }`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw)}, + Key: NumberTerm("1"), + }, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "partial set rule with ref in head, kw middle term" + input = fmt.Sprintf(`foo.%s.bar contains 1 if { true }`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, + Key: NumberTerm("1"), + }, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "partial set rule with ref in head, kw middle term (bracketed)" + input = fmt.Sprintf(`foo["%s"].bar contains 1 if { true }`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, + Key: NumberTerm("1"), + }, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + // Default rules + + note = "default rule, kw name" + input = fmt.Sprintf("default %s if { true }", kw) + t.Run(note, func(t *testing.T) { + _, err := ParseRule(input) + if err == nil { + t.Error("Expected error, got none") + } + }) + + note = "default rule with ref in head, kw in first term" + input = fmt.Sprintf(`default %s.foo := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm(kw), StringTerm("foo")}, + Value: BooleanTerm(true), + Assign: true, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "default rule with ref in head, kw last term" + input = fmt.Sprintf(`default foo.%s := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw)}, + Value: BooleanTerm(true), + Assign: true, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "default rule with ref in head, kw last term (bracketed)" + input = fmt.Sprintf(`default foo["%s"] := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw)}, + Value: BooleanTerm(true), + Assign: true, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "default rule with ref in head, kw middle term" + input = fmt.Sprintf(`default foo.%s.bar := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, + Value: BooleanTerm(true), + Assign: true, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "default rule with ref in head, kw middle term (bracketed)" + input = fmt.Sprintf(`default foo["%s"].bar := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, + Value: BooleanTerm(true), + Assign: true, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + // Default functions + + note = "default function, kw name" + input = fmt.Sprintf("default %s(_, _) if { true }", kw) + t.Run(note, func(t *testing.T) { + _, err := ParseRule(input) + if err == nil { + t.Error("Expected error, got none") + } + }) + + note = "default function with ref in head, kw in first term" + input = fmt.Sprintf(`default %s.foo(_, _) := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm(kw), StringTerm("foo")}, + Value: BooleanTerm(true), + Assign: true, + Args: []*Term{VarTerm("$0"), VarTerm("$1")}, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "default function with ref in head, kw last term" + input = fmt.Sprintf(`default foo.%s(_, _) := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw)}, + Value: BooleanTerm(true), + Assign: true, + Args: []*Term{VarTerm("$0"), VarTerm("$1")}, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "default function with ref in head, kw last term (bracketed)" + input = fmt.Sprintf(`default foo["%s"](_, _) := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw)}, + Value: BooleanTerm(true), + Assign: true, + Args: []*Term{VarTerm("$0"), VarTerm("$1")}, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "default function with ref in head, kw middle term" + input = fmt.Sprintf(`default foo.%s.bar(_, _) := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, + Value: BooleanTerm(true), + Assign: true, + Args: []*Term{VarTerm("$0"), VarTerm("$1")}, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "default function with ref in head, kw middle term (bracketed)" + input = fmt.Sprintf(`default foo["%s"].bar(_, _) := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, + Value: BooleanTerm(true), + Assign: true, + Args: []*Term{VarTerm("$0"), VarTerm("$1")}, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + }) + } +} + +func TestRuleHeadsContainingKeywords_RegoV0(t *testing.T) { + popts := ParserOptions{RegoVersion: RegoV0} + + for _, kw := range KeywordsV0 { + t.Run(kw, func(t *testing.T) { + // Complete rules + + note := "complete rule, kw name" + input := kw + " { true }" + t.Run(note, func(t *testing.T) { + _, err := ParseRuleWithOpts(input, popts) + if err == nil { + t.Error("Expected error, got none") + } + }) + + note = "complete rule with ref in head, kw in first term" + input = kw + `.foo.bar { true }` + exp := &Rule{ + Head: RefHead([]*Term{VarTerm(kw), StringTerm("foo"), StringTerm("bar")}, BooleanTerm(true)), + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + note = "complete rule with ref in head, kw last term" + input = fmt.Sprintf(`foo.bar.%s { true }`, kw) + exp = &Rule{ + Head: RefHead([]*Term{VarTerm("foo"), StringTerm("bar"), StringTerm(kw)}, BooleanTerm(true)), + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + note = "complete rule with ref in head, kw last term (bracketed)" + input = fmt.Sprintf(`foo.bar["%s"] { true }`, kw) + exp = &Rule{ + Head: RefHead([]*Term{VarTerm("foo"), StringTerm("bar"), StringTerm(kw)}, BooleanTerm(true)), + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + note = "complete rule with ref in head, kw middle term" + input = fmt.Sprintf(`foo.%s.bar { true }`, kw) + exp = &Rule{ + Head: RefHead([]*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, BooleanTerm(true)), + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + note = "complete rule with ref in head, kw middle term (bracketed)" + input = fmt.Sprintf(`foo["%s"].bar { true }`, kw) + exp = &Rule{ + Head: RefHead([]*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, BooleanTerm(true)), + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + // Functions + + note = "function, kw name" + input = kw + "(x, y) { true }" + t.Run(note, func(t *testing.T) { + _, err := ParseRule(input) + if err == nil { + t.Error("Expected error, got none") + } + }) + + note = "function with ref in head, kw in first term" + input = kw + `.foo(x, y) { true }` + head := RefHead([]*Term{VarTerm(kw), StringTerm("foo")}, BooleanTerm(true)) + head.Args = []*Term{VarTerm("x"), VarTerm("y")} + exp = &Rule{ + Head: head, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "function with ref in head, kw last term" + input = fmt.Sprintf(`foo.%s(x, y) { true }`, kw) + head = RefHead([]*Term{VarTerm("foo"), StringTerm(kw)}, BooleanTerm(true)) + head.Args = []*Term{VarTerm("x"), VarTerm("y")} + exp = &Rule{ + Head: head, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "function with ref in head, kw last term (bracketed)" + input = fmt.Sprintf(`foo["%s"](x, y) { true }`, kw) + head = RefHead([]*Term{VarTerm("foo"), StringTerm(kw)}, BooleanTerm(true)) + head.Args = []*Term{VarTerm("x"), VarTerm("y")} + exp = &Rule{ + Head: head, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "function with ref in head, kw middle term" + input = fmt.Sprintf(`foo.%s.bar(x, y) { true }`, kw) + head = RefHead([]*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, BooleanTerm(true)) + head.Args = []*Term{VarTerm("x"), VarTerm("y")} + exp = &Rule{ + Head: head, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "function with ref in head, kw middle term (bracketed)" + input = fmt.Sprintf(`foo["%s"].bar(x, y) { true }`, kw) + head = RefHead([]*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, BooleanTerm(true)) + head.Args = []*Term{VarTerm("x"), VarTerm("y")} + exp = &Rule{ + Head: head, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + // Partial set rules + + note = "partial set rule with ref in head, kw in first term (name)" + input = kw + `.foo { true }` + exp = &Rule{ + Head: &Head{ + Name: Var(kw), + Reference: []*Term{VarTerm(kw)}, + Key: StringTerm("foo"), + }, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + note = "partial set rule with ref in head, kw last term (key)" + input = fmt.Sprintf(`foo.%s { true }`, kw) + exp = &Rule{ + Head: &Head{ + Name: "foo", + Reference: []*Term{VarTerm("foo")}, + Key: StringTerm(kw), + }, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + note = "partial set rule with ref in head, kw last term (key) (bracketed)" + input = fmt.Sprintf(`foo["%s"] { true }`, kw) + exp = &Rule{ + Head: &Head{ + Name: "foo", + Reference: []*Term{VarTerm("foo")}, + Key: StringTerm(kw), + }, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + // Default rules + + note = "default rule, kw name" + input = fmt.Sprintf("default %s { true }", kw) + t.Run(note, func(t *testing.T) { + _, err := ParseRuleWithOpts(input, popts) + if err == nil { + t.Error("Expected error, got none") + } + }) + + note = "default rule with ref in head, kw in first term" + input = fmt.Sprintf(`default %s.foo := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm(kw), StringTerm("foo")}, + Value: BooleanTerm(true), + Assign: true, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + note = "default rule with ref in head, kw last term" + input = fmt.Sprintf(`default foo.%s := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw)}, + Value: BooleanTerm(true), + Assign: true, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + note = "default rule with ref in head, kw last term (bracketed)" + input = fmt.Sprintf(`default foo["%s"] := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw)}, + Value: BooleanTerm(true), + Assign: true, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + note = "default rule with ref in head, kw middle term" + input = fmt.Sprintf(`default foo.%s.bar := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, + Value: BooleanTerm(true), + Assign: true, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + note = "default rule with ref in head, kw middle term (bracketed)" + input = fmt.Sprintf(`default foo["%s"].bar := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, + Value: BooleanTerm(true), + Assign: true, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + + // Default functions + + note = "default function, kw name" + input = fmt.Sprintf("default %s(_, _) if { true }", kw) + t.Run(note, func(t *testing.T) { + _, err := ParseRule(input) + if err == nil { + t.Error("Expected error, got none") + } + }) + + note = "default function with ref in head, kw in first term" + input = fmt.Sprintf(`default %s.foo(_, _) := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm(kw), StringTerm("foo")}, + Value: BooleanTerm(true), + Assign: true, + Args: []*Term{VarTerm("$0"), VarTerm("$1")}, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "default function with ref in head, kw last term" + input = fmt.Sprintf(`default foo.%s(_, _) := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw)}, + Value: BooleanTerm(true), + Assign: true, + Args: []*Term{VarTerm("$0"), VarTerm("$1")}, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "default function with ref in head, kw last term (bracketed)" + input = fmt.Sprintf(`default foo["%s"](_, _) := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw)}, + Value: BooleanTerm(true), + Assign: true, + Args: []*Term{VarTerm("$0"), VarTerm("$1")}, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "default function with ref in head, kw middle term" + input = fmt.Sprintf(`default foo.%s.bar(_, _) := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, + Value: BooleanTerm(true), + Assign: true, + Args: []*Term{VarTerm("$0"), VarTerm("$1")}, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + + note = "default function with ref in head, kw middle term (bracketed)" + input = fmt.Sprintf(`default foo["%s"].bar(_, _) := true`, kw) + exp = &Rule{ + Head: &Head{ + Reference: []*Term{VarTerm("foo"), StringTerm(kw), StringTerm("bar")}, + Value: BooleanTerm(true), + Assign: true, + Args: []*Term{VarTerm("$0"), VarTerm("$1")}, + }, + Default: true, + Body: NewBody( + NewExpr(BooleanTerm(true)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + }) + } +} + +func TestRefKeywordsEdgeCases(t *testing.T) { + t.Run("'in' kw first term in ref head rule following 'contains'", func(t *testing.T) { + input := `package test + foo contains "a" + + in.bar contains "b" if { + false + }` + + exp := &Module{ + Package: MustParsePackage("package test"), + Rules: []*Rule{ + MustParseRule(`foo contains "a"`), + MustParseRule(`in.bar contains "b" if { false }`), + }, + } + + m, err := ParseModule("", input) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if !m.Equal(exp) { + t.Fatalf("expected module:\n\n%v\n\ngot:\n\n%v", exp, m) + } + }) +} + +func TestRuleBodyContainingRefKeywords(t *testing.T) { + for _, kw := range KeywordsForRegoVersion(RegoV1) { + t.Run(kw, func(t *testing.T) { + note := "rule with ref in body" + input := fmt.Sprintf(`p if { + %s.foo == 1 + foo.%s == 2 + foo.%s.bar == 3 + }`, kw, kw, kw) + exp := &Rule{ + Head: NewHead("p", nil, BooleanTerm(true)), + Body: NewBody( + Equal.Expr(RefTerm(VarTerm(kw), StringTerm("foo")), IntNumberTerm(1)), + Equal.Expr(RefTerm(VarTerm("foo"), StringTerm(kw)), IntNumberTerm(2)), + Equal.Expr(RefTerm(VarTerm("foo"), StringTerm(kw), StringTerm("bar")), IntNumberTerm(3)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp) + }) + }) + } +} + +func TestRuleBodyContainingRefKeywords_RegoV0(t *testing.T) { + popts := ParserOptions{RegoVersion: RegoV0} + + for _, kw := range KeywordsForRegoVersion(RegoV0) { + t.Run(kw, func(t *testing.T) { + note := "rule with ref in body" + input := fmt.Sprintf(`p { + %s.foo == 1 + foo.%s == 2 + foo.%s.bar == 3 + }`, kw, kw, kw) + exp := &Rule{ + Head: NewHead("p", nil, BooleanTerm(true)), + Body: NewBody( + Equal.Expr(RefTerm(VarTerm(kw), StringTerm("foo")), IntNumberTerm(1)), + Equal.Expr(RefTerm(VarTerm("foo"), StringTerm(kw)), IntNumberTerm(2)), + Equal.Expr(RefTerm(VarTerm("foo"), StringTerm(kw), StringTerm("bar")), IntNumberTerm(3)), + ), + } + t.Run(note, func(t *testing.T) { + assertParseRule(t, note, input, exp, popts) + }) + }) + } +} + +func TestObjectWithScalars(t *testing.T) { + assertParseOneTerm(t, "number", "{\"abc\": 7, \"def\": 8}", ObjectTerm(Item(StringTerm("abc"), IntNumberTerm(7)), Item(StringTerm("def"), IntNumberTerm(8)))) + assertParseOneTerm(t, "bool", "{\"abc\": false, \"def\": true}", ObjectTerm(Item(StringTerm("abc"), BooleanTerm(false)), Item(StringTerm("def"), BooleanTerm(true)))) + assertParseOneTerm(t, "string", "{\"abc\": \"foo\", \"def\": \"bar\"}", ObjectTerm(Item(StringTerm("abc"), StringTerm("foo")), Item(StringTerm("def"), StringTerm("bar")))) + assertParseOneTerm(t, "mixed", "{\"abc\": 7, \"def\": null}", ObjectTerm(Item(StringTerm("abc"), IntNumberTerm(7)), Item(StringTerm("def"), NullTerm()))) + assertParseOneTerm(t, "number key", "{8: 7, \"def\": null}", ObjectTerm(Item(IntNumberTerm(8), IntNumberTerm(7)), Item(StringTerm("def"), NullTerm()))) + assertParseOneTerm(t, "number key 2", "{8.5: 7, \"def\": null}", ObjectTerm(Item(FloatNumberTerm(8.5), IntNumberTerm(7)), Item(StringTerm("def"), NullTerm()))) + assertParseOneTerm(t, "bool key", "{true: false}", ObjectTerm(Item(BooleanTerm(true), BooleanTerm(false)))) + assertParseOneTerm(t, "trailing comma", `{"a": "bar", "b": 64, }`, ObjectTerm(Item(StringTerm("a"), StringTerm("bar")), Item(StringTerm("b"), IntNumberTerm(64)))) + assertParseOneTerm(t, "leading comma", `{, "a": "bar", "b": 64 }`, ObjectTerm(Item(StringTerm("a"), StringTerm("bar")), Item(StringTerm("b"), IntNumberTerm(64)))) + assertParseOneTerm(t, "leading comma not comprehension", `{, 1 | 1: "bar"}`, ObjectTerm(Item(CallTerm(RefTerm(VarTerm("or")), NumberTerm("1"), NumberTerm("1")), StringTerm("bar")))) +} + +func TestObjectWithVars(t *testing.T) { + assertParseOneTerm(t, "var keys", "{foo: \"bar\", bar: 64}", ObjectTerm(Item(VarTerm("foo"), StringTerm("bar")), Item(VarTerm("bar"), IntNumberTerm(64)))) + assertParseOneTerm(t, "nested var keys", "{baz: {foo: \"bar\", bar: qux}}", ObjectTerm(Item(VarTerm("baz"), ObjectTerm(Item(VarTerm("foo"), StringTerm("bar")), Item(VarTerm("bar"), VarTerm("qux")))))) + assertParseOneTerm(t, "ambiguous or", `{ a: b+c | d }`, ObjectTerm(Item(VarTerm("a"), CallTerm(RefTerm(VarTerm("or")), CallTerm(RefTerm(VarTerm("plus")), VarTerm("b"), VarTerm("c")), VarTerm("d"))))) +} + +func TestObjectWithRelation(t *testing.T) { + assertParseOneTerm(t, "relation term value", `{"x": 1+1}`, ObjectTerm( + Item(StringTerm("x"), CallTerm(RefTerm(VarTerm("plus")), IntNumberTerm(1), IntNumberTerm(1))), + )) + assertParseError(t, "invalid relation term value", `{"x": 0= }`) +} + +func TestObjectFail(t *testing.T) { + assertParseError(t, "non-terminated 1", "{foo: bar, baz: [], qux: corge") + assertParseError(t, "non-terminated 2", "{foo: bar, baz: [], qux: ") + assertParseError(t, "non-terminated 3", "{foo: bar, baz: [], qux ") + assertParseError(t, "non-terminated 4", "{foo: bar, baz: [], ") + assertParseError(t, "missing separator", "{foo: bar baz: []}") + assertParseError(t, "missing start", "foo: bar, baz: [], qux: corge}") + assertParseError(t, "double comma", "{a:1,,b:2}") + assertParseError(t, "leading double comma", "{,,a:1}") + assertParseError(t, "trailing double comma", "{a:1,,}") +} + +func TestArrayWithScalars(t *testing.T) { + assertParseOneTerm(t, "number", "[1,2,3,4.5]", ArrayTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3), FloatNumberTerm(4.5))) + assertParseOneTerm(t, "bool", "[true, false, true]", ArrayTerm(BooleanTerm(true), BooleanTerm(false), BooleanTerm(true))) + assertParseOneTerm(t, "string", "[\"foo\", \"bar\"]", ArrayTerm(StringTerm("foo"), StringTerm("bar"))) + assertParseOneTerm(t, "mixed", "[null, true, 42]", ArrayTerm(NullTerm(), BooleanTerm(true), IntNumberTerm(42))) + assertParseOneTerm(t, "trailing comma - one element", "[null, ]", ArrayTerm(NullTerm())) + assertParseOneTerm(t, "trailing comma", "[null, true, ]", ArrayTerm(NullTerm(), BooleanTerm(true))) + assertParseOneTerm(t, "leading comma", "[, null, true]", ArrayTerm(NullTerm(), BooleanTerm(true))) + assertParseOneTerm(t, "leading comma not comprehension", "[, 1 | 1]", ArrayTerm(CallTerm(RefTerm(VarTerm("or")), NumberTerm("1"), NumberTerm("1")))) + assertParseOneTerm(t, "ambiguous or", "[ 1 + 2 | 3 ]", ArrayTerm(CallTerm(RefTerm(VarTerm("or")), CallTerm(RefTerm(VarTerm("plus")), NumberTerm("1"), NumberTerm("2")), NumberTerm("3")))) +} + +func TestArrayWithVars(t *testing.T) { + assertParseOneTerm(t, "var elements", "[foo, bar, 42]", ArrayTerm(VarTerm("foo"), VarTerm("bar"), IntNumberTerm(42))) + assertParseOneTerm(t, "nested var elements", "[[foo, true], [null, bar], 42]", ArrayTerm(ArrayTerm(VarTerm("foo"), BooleanTerm(true)), ArrayTerm(NullTerm(), VarTerm("bar")), IntNumberTerm(42))) +} + +func TestArrayFail(t *testing.T) { + assertParseError(t, "non-terminated 1", "[foo, bar") + assertParseError(t, "non-terminated 2", "[foo, bar, ") + assertParseError(t, "missing separator", "[foo bar]") + assertParseError(t, "missing start", "foo, bar, baz]") + assertParseError(t, "bad term", "[!!!]") + assertParseError(t, "double comma", "[a,,b]") + assertParseError(t, "leading double comma", "[,,a]") + assertParseError(t, "trailing double comma", "[a,,]") +} + +func TestSetWithScalars(t *testing.T) { + assertParseOneTerm(t, "number", "{1,2,3,4.5}", SetTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3), FloatNumberTerm(4.5))) + assertParseOneTerm(t, "bool", "{true, false, true}", SetTerm(BooleanTerm(true), BooleanTerm(false), BooleanTerm(true))) + assertParseOneTerm(t, "string", "{\"foo\", \"bar\"}", SetTerm(StringTerm("foo"), StringTerm("bar"))) + assertParseOneTerm(t, "mixed", "{null, true, 42}", SetTerm(NullTerm(), BooleanTerm(true), IntNumberTerm(42))) + assertParseOneTerm(t, "trailing comma", "{null, true,}", SetTerm(NullTerm(), BooleanTerm(true))) + assertParseOneTerm(t, "leading comma", "{, null, true}", SetTerm(NullTerm(), BooleanTerm(true))) + assertParseOneTerm(t, "leading comma not comprehension", "{, 1 | 1}", SetTerm(CallTerm(RefTerm(VarTerm("or")), NumberTerm("1"), NumberTerm("1")))) + assertParseOneTerm(t, "ambiguous or", "{ 1 + 2 | 3}", SetTerm(CallTerm(RefTerm(VarTerm("or")), CallTerm(RefTerm(VarTerm("plus")), NumberTerm("1"), NumberTerm("2")), NumberTerm("3")))) +} + +func TestSetWithVars(t *testing.T) { + assertParseOneTerm(t, "var elements", "{foo, bar, 42}", SetTerm(VarTerm("foo"), VarTerm("bar"), IntNumberTerm(42))) + assertParseOneTerm(t, "nested var elements", "{[foo, true], {null, bar}, set()}", SetTerm(ArrayTerm(VarTerm("foo"), BooleanTerm(true)), SetTerm(NullTerm(), VarTerm("bar")), SetTerm())) +} + +func TestSetFail(t *testing.T) { + assertParseError(t, "non-terminated 1", "set(") + assertParseError(t, "non-terminated 2", "{foo, bar") + assertParseError(t, "non-terminated 3", "{foo, bar, ") + assertParseError(t, "missing separator", "{foo bar}") + assertParseError(t, "missing start", "foo, bar, baz}") + assertParseError(t, "bad term", "{!!!}") + assertParseError(t, "double comma", "{a,,b}") + assertParseError(t, "leading double comma", "{,,a}") + assertParseError(t, "trailing double comma", "{a,,}") +} + +func TestEmptyComposites(t *testing.T) { + assertParseOneTerm(t, "empty object", "{}", ObjectTerm()) + assertParseOneTerm(t, "empty array", "[]", ArrayTerm()) + assertParseOneTerm(t, "empty set", "set()", SetTerm()) +} + +func TestNestedComposites(t *testing.T) { + assertParseOneTerm(t, "nested composites", "[{foo: [\"bar\", {baz}]}]", ArrayTerm(ObjectTerm(Item(VarTerm("foo"), ArrayTerm(StringTerm("bar"), SetTerm(VarTerm("baz"))))))) +} + +func TestCompositesWithRefs(t *testing.T) { + ref1 := RefTerm(VarTerm("a"), VarTerm("i"), StringTerm("b")) + ref2 := RefTerm(VarTerm("c"), IntNumberTerm(0), StringTerm("d"), StringTerm("e"), VarTerm("j")) + assertParseOneTerm(t, "ref keys", "[{a[i].b: 8, c[0][\"d\"].e[j]: f}]", ArrayTerm(ObjectTerm(Item(ref1, IntNumberTerm(8)), Item(ref2, VarTerm("f"))))) + assertParseOneTerm(t, "ref values", "[{8: a[i].b, f: c[0][\"d\"].e[j]}]", ArrayTerm(ObjectTerm(Item(IntNumberTerm(8), ref1), Item(VarTerm("f"), ref2)))) + assertParseOneTerm(t, "ref values (sets)", `{a[i].b, {c[0]["d"].e[j]}}`, SetTerm(ref1, SetTerm(ref2))) +} + +func TestArrayComprehensions(t *testing.T) { + + nestedTerm := `[{"x": [a[i] | xs = [{"a": ["baz", j]} | q[p]; p.a != "bar"; j = "foo"]; xs[j].a[k] = "foo"]}]` + nestedExpected := ArrayTerm( + ObjectTerm(Item( + StringTerm("x"), + ArrayComprehensionTerm( + RefTerm(VarTerm("a"), VarTerm("i")), + NewBody( + Equality.Expr( + VarTerm("xs"), + ArrayComprehensionTerm( + ObjectTerm(Item(StringTerm("a"), ArrayTerm(StringTerm("baz"), VarTerm("j")))), + NewBody( + NewExpr(RefTerm(VarTerm("q"), VarTerm("p"))), + NotEqual.Expr(RefTerm(VarTerm("p"), StringTerm("a")), StringTerm("bar")), + Equality.Expr(VarTerm("j"), StringTerm("foo")), + ), + ), + ), + Equality.Expr( + RefTerm(VarTerm("xs"), VarTerm("j"), StringTerm("a"), VarTerm("k")), + StringTerm("foo"), + ), + ), + ), + )), + ) + assertParseOneTerm(t, "nested", nestedTerm, nestedExpected) + assertParseOneTerm(t, "ambiguous or", "[ a | b ]", ArrayComprehensionTerm( + VarTerm("a"), + MustParseBody("b"), + )) +} + +func TestObjectComprehensions(t *testing.T) { + nestedTerm := `[{"x": {a[i]: b[i] | xs = {"foo":{"a": ["baz", j]} | q[p]; p.a != "bar"; j = "foo"}; xs[j].a[k] = "foo"}}]` + nestedExpected := ArrayTerm( + ObjectTerm(Item( + StringTerm("x"), + ObjectComprehensionTerm( + RefTerm(VarTerm("a"), VarTerm("i")), + RefTerm(VarTerm("b"), VarTerm("i")), + NewBody( + Equality.Expr( + VarTerm("xs"), + ObjectComprehensionTerm( + StringTerm("foo"), + ObjectTerm(Item(StringTerm("a"), ArrayTerm(StringTerm("baz"), VarTerm("j")))), + NewBody( + NewExpr(RefTerm(VarTerm("q"), VarTerm("p"))), + NotEqual.Expr(RefTerm(VarTerm("p"), StringTerm("a")), StringTerm("bar")), + Equality.Expr(VarTerm("j"), StringTerm("foo")), + ), + ), + ), + Equality.Expr( + RefTerm(VarTerm("xs"), VarTerm("j"), StringTerm("a"), VarTerm("k")), + StringTerm("foo"), + ), + ), + ), + )), + ) + assertParseOneTerm(t, "nested", nestedTerm, nestedExpected) + assertParseOneTerm(t, "ambiguous or", "{ 1+2: 3 | 4}", ObjectComprehensionTerm( + CallTerm(RefTerm(VarTerm("plus")), NumberTerm("1"), NumberTerm("2")), + NumberTerm("3"), + MustParseBody("4"), + )) +} + +func TestObjectComprehensionError(t *testing.T) { + assertParseError(t, "bad body", "{x: y|!!!}") +} + +func TestSetComprehensions(t *testing.T) { + nestedTerm := `[{"x": {a[i] | xs = {{"a": ["baz", j]} | q[p]; p.a != "bar"; j = "foo"}; xs[j].a[k] = "foo"}}]` + nestedExpected := ArrayTerm( + ObjectTerm(Item( + StringTerm("x"), + SetComprehensionTerm( + RefTerm(VarTerm("a"), VarTerm("i")), + NewBody( + Equality.Expr( + VarTerm("xs"), + SetComprehensionTerm( + ObjectTerm(Item(StringTerm("a"), ArrayTerm(StringTerm("baz"), VarTerm("j")))), + NewBody( + NewExpr(RefTerm(VarTerm("q"), VarTerm("p"))), + NotEqual.Expr(RefTerm(VarTerm("p"), StringTerm("a")), StringTerm("bar")), + Equality.Expr(VarTerm("j"), StringTerm("foo")), + ), + ), + ), + Equality.Expr( + RefTerm(VarTerm("xs"), VarTerm("j"), StringTerm("a"), VarTerm("k")), + StringTerm("foo"), + ), + ), + ), + )), + ) + + assertParseOneTerm(t, "nested", nestedTerm, nestedExpected) + assertParseOneTerm(t, "ambiguous or", "{ a | b }", SetComprehensionTerm( + VarTerm("a"), + MustParseBody("b"), + )) +} + +func TestSetComprehensionError(t *testing.T) { + assertParseError(t, "bad body", "{x|!!!}") +} + +func TestSetComprehensionsAlone(t *testing.T) { + input := `{k | a = [1,2,3]; a[k]}` + + expected := SetComprehensionTerm( + VarTerm("k"), + NewBody( + Equality.Expr( + VarTerm("a"), + ArrayTerm(NumberTerm("1"), NumberTerm("2"), NumberTerm("3")), + ), + &Expr{ + Terms: RefTerm(VarTerm("a"), VarTerm("k")), + }, + ), + ) + + assertParseOneTerm(t, "alone", input, expected) +} + +func TestCalls(t *testing.T) { + + assertParseOneExpr(t, "ne", "100 != 200", NotEqual.Expr(IntNumberTerm(100), IntNumberTerm(200))) + assertParseOneExpr(t, "gt", "17.4 > \"hello\"", GreaterThan.Expr(FloatNumberTerm(17.4), StringTerm("hello"))) + assertParseOneExpr(t, "lt", "17.4 < \"hello\"", LessThan.Expr(FloatNumberTerm(17.4), StringTerm("hello"))) + assertParseOneExpr(t, "gte", "17.4 >= \"hello\"", GreaterThanEq.Expr(FloatNumberTerm(17.4), StringTerm("hello"))) + assertParseOneExpr(t, "lte", "17.4 <= \"hello\"", LessThanEq.Expr(FloatNumberTerm(17.4), StringTerm("hello"))) + + left2 := ArrayTerm(ObjectTerm(Item(FloatNumberTerm(14.2), BooleanTerm(true)), Item(StringTerm("a"), NullTerm()))) + right2 := ObjectTerm(Item(VarTerm("foo"), ObjectTerm(Item(RefTerm(VarTerm("a"), StringTerm("b"), IntNumberTerm(0)), ArrayTerm(IntNumberTerm(10)))))) + assertParseOneExpr(t, "composites", "[{14.2: true, \"a\": null}] != {foo: {a.b[0]: [10]}}", NotEqual.Expr(left2, right2)) + + assertParseOneExpr(t, "plus", "1 + 2", Plus.Expr(IntNumberTerm(1), IntNumberTerm(2))) + assertParseOneExpr(t, "minus", "1 - 2", Minus.Expr(IntNumberTerm(1), IntNumberTerm(2))) + assertParseOneExpr(t, "mul", "1 * 2", Multiply.Expr(IntNumberTerm(1), IntNumberTerm(2))) + assertParseOneExpr(t, "div", "1 / 2", Divide.Expr(IntNumberTerm(1), IntNumberTerm(2))) + assertParseOneExpr(t, "rem", "3 % 2", Rem.Expr(IntNumberTerm(3), IntNumberTerm(2))) + assertParseOneExpr(t, "and", "{1,2,3} & {2,3,4}", And.Expr(SetTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3)), SetTerm(IntNumberTerm(2), IntNumberTerm(3), IntNumberTerm(4)))) + assertParseOneExpr(t, "or", "{1,2,3} | {3,4,5}", Or.Expr(SetTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3)), SetTerm(IntNumberTerm(3), IntNumberTerm(4), IntNumberTerm(5)))) + + assertParseOneExpr(t, "call", "count([true, false])", Count.Expr(ArrayTerm(BooleanTerm(true), BooleanTerm(false)))) + assertParseOneExpr(t, "call-ref", "foo.bar(1)", NewExpr( + []*Term{RefTerm(VarTerm("foo"), StringTerm("bar")), + IntNumberTerm(1)})) + assertParseOneExpr(t, "call-void", "foo()", NewExpr( + []*Term{RefTerm(VarTerm("foo"))})) + + opts := ParserOptions{FutureKeywords: []string{"in"}} + assertParseOneExpr(t, "internal.member_2", "x in xs", Member.Expr(VarTerm("x"), VarTerm("xs")), opts) + assertParseOneExpr(t, "internal.member_3", "x, y in xs", MemberWithKey.Expr(VarTerm("x"), VarTerm("y"), VarTerm("xs")), opts) +} + +func TestInfixExpr(t *testing.T) { + assertParseOneExpr(t, "scalars 1", "true = false", Equality.Expr(BooleanTerm(true), BooleanTerm(false))) + assertParseOneExpr(t, "scalars 2", "3.14 = null", Equality.Expr(FloatNumberTerm(3.14), NullTerm())) + assertParseOneExpr(t, "scalars 3", "42 = \"hello world\"", Equality.Expr(IntNumberTerm(42), StringTerm("hello world"))) + assertParseOneExpr(t, "vars 1", "hello = world", Equality.Expr(VarTerm("hello"), VarTerm("world"))) + assertParseOneExpr(t, "vars 2", "42 = hello", Equality.Expr(IntNumberTerm(42), VarTerm("hello"))) + + ref1 := RefTerm(VarTerm("foo"), IntNumberTerm(0), StringTerm("bar"), VarTerm("x")) + ref2 := RefTerm(VarTerm("baz"), BooleanTerm(false), StringTerm("qux"), StringTerm("hello")) + assertParseOneExpr(t, "refs 1", "foo[0].bar[x] = baz[false].qux[\"hello\"]", Equality.Expr(ref1, ref2)) + + left1 := ObjectTerm(Item(VarTerm("a"), ArrayTerm(ref1))) + right1 := ArrayTerm(ObjectTerm(Item(IntNumberTerm(42), BooleanTerm(true)))) + assertParseOneExpr(t, "composites", "{a: [foo[0].bar[x]]} = [{42: true}]", Equality.Expr(left1, right1)) + + assertParseOneExpr(t, "plus", "x = 1 + 2", Equality.Expr(VarTerm("x"), Plus.Call(IntNumberTerm(1), IntNumberTerm(2)))) + assertParseOneExpr(t, "plus reverse", "1 + 2 = x", Equality.Expr(Plus.Call(IntNumberTerm(1), IntNumberTerm(2)), VarTerm("x"))) + + assertParseOneExpr(t, "call", "count([true, false]) = x", Equality.Expr(Count.Call(ArrayTerm(BooleanTerm(true), BooleanTerm(false))), VarTerm("x"))) + assertParseOneExpr(t, "call-reverse", "x = count([true, false])", Equality.Expr(VarTerm("x"), Count.Call(ArrayTerm(BooleanTerm(true), BooleanTerm(false))))) +} + +func TestNegatedExpr(t *testing.T) { + assertParseOneTermNegated(t, "scalars 1", "not true", BooleanTerm(true)) + assertParseOneTermNegated(t, "scalars 2", "not \"hello\"", StringTerm("hello")) + assertParseOneTermNegated(t, "scalars 3", "not 100", IntNumberTerm(100)) + assertParseOneTermNegated(t, "scalars 4", "not null", NullTerm()) + assertParseOneTermNegated(t, "var", "not x", VarTerm("x")) + assertParseOneTermNegated(t, "ref", "not x[y].z", RefTerm(VarTerm("x"), VarTerm("y"), StringTerm("z"))) + assertParseOneExprNegated(t, "vars", "not x = y", Equality.Expr(VarTerm("x"), VarTerm("y"))) + + ref1 := RefTerm(VarTerm("x"), VarTerm("y"), StringTerm("z"), VarTerm("a")) + + assertParseOneExprNegated(t, "membership", "not x[y].z[a] = \"b\"", Equality.Expr(ref1, StringTerm("b"))) + assertParseOneExprNegated(t, "misc. builtin", "not sorted(x[y].z[a])", NewExpr([]*Term{RefTerm(VarTerm("sorted")), ref1})) +} + +func TestExprWith(t *testing.T) { + assertParseOneExpr(t, "input", "data.foo with input as baz", &Expr{ + Terms: MustParseTerm("data.foo"), + With: []*With{ + { + Target: NewTerm(InputRootRef), + Value: VarTerm("baz"), + }, + }, + }) + + assertParseOneExpr(t, "builtin/ref target/composites", `plus(data.foo, 1, x) with input.com.acmecorp.obj as {"count": [{1,2,3}]}`, &Expr{ + Terms: MustParseExpr("plus(data.foo, 1, x)").Terms, + With: []*With{ + { + Target: MustParseTerm("input.com.acmecorp.obj"), + Value: MustParseTerm(`{"count": [{1,2,3}]}`), + }, + }, + }) + + assertParseOneExpr(t, "multiple", `data.foo with input.obj as baz with input.com.acmecorp.obj as {"count": [{1,2,3}]}`, &Expr{ + Terms: MustParseTerm("data.foo"), + With: []*With{ + { + Target: MustParseTerm("input.obj"), + Value: VarTerm("baz"), + }, + { + Target: MustParseTerm("input.com.acmecorp.obj"), + Value: MustParseTerm(`{"count": [{1,2,3}]}`), + }, + }, + }) + + assertParseOneExpr(t, "variable target", "true with x as 1", &Expr{ + Terms: BooleanTerm(true), + With: []*With{ + { + Target: VarTerm("x"), + Value: IntNumberTerm(1), + }, + }, + }) +} + +func TestExprWithLocation(t *testing.T) { + cases := []struct { + note string + input string + expected []*Location + }{ + { + note: "base", + input: "a with b as c", + expected: []*Location{ + { + Row: 1, + Col: 3, + Offset: 2, + Text: []byte("with b as c"), + }, + }, + }, + { + note: "with line break", + input: "a with b\nas c", + expected: []*Location{ + { + Row: 1, + Col: 3, + Offset: 2, + Text: []byte("with b\nas c"), + }, + }, + }, + { + note: "multiple withs on single line", + input: "a with b as c with d as e", + expected: []*Location{ + { + Row: 1, + Col: 3, + Offset: 2, + Text: []byte("with b as c"), + }, + { + Row: 1, + Col: 15, + Offset: 14, + Text: []byte("with d as e"), + }, + }, + }, + { + note: "multiple withs on multiple line", + input: "a with b as c\n\t\twith d as e", + expected: []*Location{ + { + Row: 1, + Col: 3, + Offset: 2, + Text: []byte("with b as c"), + }, + { + Row: 2, + Col: 3, + Offset: 16, + Text: []byte("with d as e"), + }, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + parsed, err := ParseStatement(tc.input) + if err != nil { + t.Errorf("Unexpected error on %s: %s", tc.input, err) + return + } + + body := parsed.(Body) + if len(body) != 1 { + t.Errorf("Parser returned multiple expressions: %v", body) + return + } + expr := body[0] + if len(expr.With) != len(tc.expected) { + t.Fatalf("Expected %d with statements, got %d", len(expr.With), len(tc.expected)) + } + for i, with := range expr.With { + if !with.Location.Equal(tc.expected[i]) { + t.Errorf("Expected location %+v for '%v' but got %+v ", *(tc.expected[i]), with.String(), *with.Location) + } + } + }) + } +} + +func TestSomeDeclExpr(t *testing.T) { + opts := ParserOptions{FutureKeywords: []string{"in"}} + + assertParseOneExpr(t, "one", "some x", &Expr{ + Terms: &SomeDecl{ + Symbols: []*Term{ + VarTerm("x"), + }, + }, + }) + + assertParseOneExpr(t, "internal.member_2", "some x in xs", &Expr{ + Terms: &SomeDecl{ + Symbols: []*Term{ + Member.Call( + VarTerm("x"), + VarTerm("xs"), + ), + }, + }, + }, opts) + + assertParseOneExpr(t, "internal.member_3", "some x, y in xs", &Expr{ + Terms: &SomeDecl{ + Symbols: []*Term{ + MemberWithKey.Call( + VarTerm("x"), + VarTerm("y"), + VarTerm("xs"), + ), + }, + }, + }, opts) + + assertParseErrorContains(t, "not some", "not some x, y in xs", + "unexpected some keyword: illegal negation of 'some'", + opts) + + assertParseErrorContains(t, "some + function call", "some f(x)", + "expected `x in xs` or `x, y in xs` expression") + + assertParseOneExpr(t, "multiple", "some x, y", &Expr{ + Terms: &SomeDecl{ + Symbols: []*Term{ + VarTerm("x"), + VarTerm("y"), + }, + }, + }, opts) + + assertParseOneExpr(t, "multiple split across lines", `some x, y, + z`, &Expr{ + Terms: &SomeDecl{ + Symbols: []*Term{ + VarTerm("x"), + VarTerm("y"), + VarTerm("z"), + }, + }, + }) + + assertParseRule(t, "whitespace separated", ` + + p[x] { + some x + q[x] + } + `, &Rule{ + Head: NewHead(Var("p"), VarTerm("x")), + Body: NewBody( + NewExpr(&SomeDecl{Symbols: []*Term{VarTerm("x")}}), + NewExpr(RefTerm(VarTerm("q"), VarTerm("x"))), + ), + }) + + // Only relevant for v0, as the 'in' keyword isn't a permitted var name in v1. + assertParseRule(t, "whitespace separated, following `in` rule ref", ` + p[x] { + some x + in[x] + } +`, &Rule{ + Head: NewHead(Var("p"), VarTerm("x")), + Body: NewBody( + NewExpr(&SomeDecl{Symbols: []*Term{VarTerm("x")}}), + NewExpr(RefTerm(VarTerm("in"), VarTerm("x"))), + ), + }, ParserOptions{RegoVersion: RegoV0}) + + // Only relevant for v0, as the 'in' keyword is included in v1. + assertParseErrorContains(t, "some x in ... usage is hinted properly", ` + p contains x if { + some x in {"foo": "bar"} + }`, + "unexpected identifier token: expected \\n or ; or } (hint: `import future.keywords.in` for `some x in xs` expressions)", + ParserOptions{RegoVersion: RegoV0}) + + // Only relevant for v0, as the 'in' keyword is included in v1. + assertParseErrorContains(t, "some x, y in ... usage is hinted properly", ` + p[y] = x if { + some x, y in {"foo": "bar"} + }`, + "unexpected identifier token: expected \\n or ; or } (hint: `import future.keywords.in` for `some x in xs` expressions)", + ParserOptions{RegoVersion: RegoV0}) + + assertParseRule(t, "whitespace terminated", ` + + p[x] { + some x + x + } +`, &Rule{ + Head: NewHead(Var("p"), VarTerm("x")), + Body: NewBody( + NewExpr(&SomeDecl{Symbols: []*Term{VarTerm("x")}}), + NewExpr(VarTerm("x")), + ), + }) + + assertParseOneExpr(t, "with modifier on expr", "some x, y in input with input as []", + &Expr{ + Terms: &SomeDecl{ + Symbols: []*Term{ + MemberWithKey.Call( + VarTerm("x"), + VarTerm("y"), + NewTerm(MustParseRef("input")), + ), + }, + }, + With: []*With{{Value: ArrayTerm(), Target: NewTerm(MustParseRef("input"))}}, + }, opts) + + assertParseErrorContains(t, "invalid domain (internal.member_2)", "some internal.member_2()", "illegal domain", opts) + assertParseErrorContains(t, "invalid domain (internal.member_3)", "some internal.member_3()", "illegal domain", opts) + +} + +func TestEvery(t *testing.T) { + opts := ParserOptions{unreleasedKeywords: true, FutureKeywords: []string{"every"}} + assertParseOneExpr(t, "simple", "every x in xs { true }", + &Expr{ + Terms: &Every{ + Value: VarTerm("x"), + Domain: VarTerm("xs"), + Body: []*Expr{ + NewExpr(BooleanTerm(true)), + }, + }, + }, + opts) + + assertParseOneExpr(t, "with key", "every k, v in [1,2] { true }", + &Expr{ + Terms: &Every{ + Key: VarTerm("k"), + Value: VarTerm("v"), + Domain: ArrayTerm(IntNumberTerm(1), IntNumberTerm(2)), + Body: []*Expr{ + NewExpr(BooleanTerm(true)), + }, + }, + }, opts) + + assertParseErrorContains(t, "arbitrary term", "every 10", "expected `x[, y] in xs { ... }` expression", opts) + assertParseErrorContains(t, "non-var value", "every 10 in xs { true }", "unexpected { token: expected value to be a variable", opts) + assertParseErrorContains(t, "non-var key", "every 10, x in xs { true }", "unexpected { token: expected key to be a variable", opts) + assertParseErrorContains(t, "arbitrary call", "every f(10)", "expected `x[, y] in xs { ... }` expression", opts) + assertParseErrorContains(t, "no body", "every x in xs", "missing body", opts) + assertParseErrorContains(t, "invalid body", "every x in xs { + }", "unexpected plus token", opts) + assertParseErrorContains(t, "not every", "not every x in xs { true }", "unexpected every keyword: illegal negation of 'every'", opts) + + assertParseOneExpr(t, `"every" kw implies "in" kw`, "x in xs", Member.Expr( + VarTerm("x"), + VarTerm("xs"), + ), opts) + + assertParseOneExpr(t, "with modifier on expr", "every x in input { x } with input as []", + &Expr{ + Terms: &Every{ + Value: VarTerm("x"), + Domain: NewTerm(MustParseRef("input")), + Body: []*Expr{ + NewExpr(VarTerm("x")), + }, + }, + With: []*With{{Value: ArrayTerm(), Target: NewTerm(MustParseRef("input"))}}, + }, opts) + + // Only relevant for v0, as the 'every' keyword is included in v1. + assertParseErrorContains(t, "every x, y in ... usage is hinted properly", ` + p { + every x, y in {"foo": "bar"} { is_string(x); is_string(y) } + }`, + "unexpected identifier token: expected \\n or ; or } (hint: `import future.keywords.every` for `every x in xs { ... }` expressions)", + ParserOptions{RegoVersion: RegoV0}) + + // Only relevant for v0, as the 'in' keyword is included in v1. + assertParseErrorContains(t, "not every 'every' gets a hint", ` + p { + every x + }`, + "unexpected identifier token: expected \\n or ; or }\n\tevery x\n", // this asserts that the tail of the error message doesn't contain a hint + ParserOptions{RegoVersion: RegoV0}) + + assertParseErrorContains(t, "invalid domain (internal.member_2)", "every internal.member_2()", "illegal domain", opts) + assertParseErrorContains(t, "invalid domain (internal.member_3)", "every internal.member_3()", "illegal domain", opts) +} + +func TestNestedExpressions(t *testing.T) { + + n1 := IntNumberTerm(1) + n2 := IntNumberTerm(2) + n3 := IntNumberTerm(3) + n4 := IntNumberTerm(4) + n6 := IntNumberTerm(6) + x := VarTerm("x") + y := VarTerm("y") + z := VarTerm("z") + w := VarTerm("w") + f := RefTerm(VarTerm("f")) + g := RefTerm(VarTerm("g")) + + tests := []struct { + note string + input string + expected *Expr + }{ + {"associativity", "1 + 2 * 6 / 3", + Plus.Expr( + n1, + Divide.Call( + Multiply.Call( + n2, + n6), + n3))}, + {"associativity - factors", "x * y / z % w", + Rem.Expr(Divide.Call(Multiply.Call(x, y), z), w)}, + {"associativity - factors", "w % z / x * y", + Multiply.Expr(Divide.Call(Rem.Call(w, z), x), y)}, + {"associativity - arithetic", "x + y - z", + Minus.Expr(Plus.Call(x, y), z)}, + {"associativity - arithmetic", "z - x + y", + Plus.Expr(Minus.Call(z, x), y)}, + {"associativity - and", "z & x & y", + And.Expr(And.Call(z, x), y)}, + {"associativity - or", "z | x | y", + Or.Expr(Or.Call(z, x), y)}, + {"associativity - relations", "x == y != z", + NotEqual.Expr(Equal.Call(x, y), z)}, + {"grouping", "(1 + 2 * 6 / 3) > 4", + GreaterThan.Expr( + Plus.Call( + n1, + Divide.Call( + Multiply.Call( + n2, + n6), + n3)), + n4)}, + {"nested parens", "(((1 + 2) * (6 / (3))) > 4) != false", + NotEqual.Expr( + GreaterThan.Call( + Multiply.Call( + Plus.Call( + n1, + n2), + Divide.Call( + n6, + n3)), + n4, + ), + BooleanTerm(false))}, + {"bitwise or", "x + 1 | 2", Or.Expr(Plus.Call(x, n1), n2)}, + {"bitwise and", "x + 1 | 2 & 3", Or.Expr(Plus.Call(x, n1), And.Call(n2, n3))}, + {"array", "[x + 1, y > 2, z]", NewExpr(ArrayTerm(Plus.Call(x, n1), GreaterThan.Call(y, n2), z))}, + {"object", "{x * 2: y < 2, z[3]: 1 + 6/2}", NewExpr( + ObjectTerm( + Item(Multiply.Call(x, n2), LessThan.Call(y, n2)), + Item(RefTerm(z, n3), Plus.Call(n1, Divide.Call(n6, n2))), + ), + )}, + {"set", "{x + 1, y + 2, set()}", NewExpr( + SetTerm( + Plus.Call(x, n1), + Plus.Call(y, n2), + SetTerm(), + ), + )}, + {"ref", `x[1][y + z[w + 1]].b`, NewExpr( + RefTerm( + x, + n1, + Plus.Call( + y, + RefTerm( + z, + Plus.Call(w, n1))), + StringTerm("b"), + ), + )}, + {"call void", "f()", NewExpr([]*Term{f})}, + {"call unary", "f(x)", NewExpr([]*Term{f, x})}, + {"call binary", "f(x, y)", NewExpr([]*Term{f, x, y})}, + {"call embedded", "f([g(x), y+1])", NewExpr([]*Term{ + f, + ArrayTerm( + CallTerm(g, x), + Plus.Call(y, n1))})}, + {"call fqn", "foo.bar(1)", NewExpr([]*Term{ + RefTerm(VarTerm("foo"), StringTerm("bar")), + n1, + })}, + {"unify", "x = 1", Equality.Expr(x, n1)}, + {"unify embedded", "1 + x = 2 - y", Equality.Expr(Plus.Call(n1, x), Minus.Call(n2, y))}, + {"not keyword", "not x = y", Equality.Expr(x, y).Complement()}, + {"with keyword", "x with p[q] as f([x+1])", NewExpr(x).IncludeWith( + RefTerm(VarTerm("p"), VarTerm("q")), + CallTerm(f, ArrayTerm(Plus.Call(x, n1))), + )}, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + expr, err := ParseExpr(tc.input) + if err != nil { + t.Fatal(err) + } + if !expr.Equal(tc.expected) { + t.Fatalf("Expected %v but got %v", tc.expected, expr) + } + }) + } +} + +func TestChainedCall(t *testing.T) { + result, err := ParseExpr("foo.bar(1)[0](1).baz") + if err != nil { + t.Fatal(err) + } + + exp := NewExpr(RefTerm( + CallTerm( + RefTerm( + CallTerm( + RefTerm(VarTerm("foo"), StringTerm("bar")), + IntNumberTerm(1)), + IntNumberTerm(0)), + IntNumberTerm(1)), + StringTerm("baz"))) + + if !result.Equal(exp) { + t.Fatalf("expected %v but got: %v", exp, result) + } +} + +func TestMultiLineBody(t *testing.T) { + tests := []struct { + note string + input string + exp Body + }{ + { + note: "three definitions", + input: ` +x = 1 +y = 2 +z = [ i | [x,y] = arr + arr[_] = i] +`, + exp: MustParseBody(`x = 1; y = 2; z = [i | [x,y] = arr; arr[_] = i]`), + }, + { + note: "three definitions, with comments and w/o enclosing braces", + input: ` +x = 1 ; # comment after semicolon +y = 2 # comment without semicolon +z = [ i | [x,y] = arr # comment in comprehension + arr[_] = i] +`, + exp: MustParseBody(`x = 1; y = 2; z = [i | [x,y] = arr; arr[_] = i]`), + }, + { + note: "array following call w/ whitespace", + input: "f(x)\n [1]", + exp: NewBody( + NewExpr([]*Term{RefTerm(VarTerm("f")), VarTerm("x")}), + NewExpr(ArrayTerm(IntNumberTerm(1))), + ), + }, + { + note: "set following call w/ semicolon", + input: "f(x);{1}", + exp: NewBody( + NewExpr([]*Term{RefTerm(VarTerm("f")), VarTerm("x")}), + NewExpr(SetTerm(IntNumberTerm(1))), + ), + }, + { + note: "array following array w/ whitespace", + input: "[1]\n [2]", + exp: NewBody( + NewExpr(ArrayTerm(IntNumberTerm(1))), + NewExpr(ArrayTerm(IntNumberTerm(2))), + ), + }, + { + note: "array following set w/ whitespace", + input: "{1}\n [2]", + exp: NewBody( + NewExpr(SetTerm(IntNumberTerm(1))), + NewExpr(ArrayTerm(IntNumberTerm(2))), + ), + }, + { + note: "set following call w/ whitespace", + input: "f(x)\n {1}", + exp: NewBody( + NewExpr([]*Term{RefTerm(VarTerm("f")), VarTerm("x")}), + NewExpr(SetTerm(IntNumberTerm(1))), + ), + }, + { + note: "set following ref w/ whitespace", + input: "data.p.q\n {1}", + exp: NewBody( + NewExpr(&Term{Value: MustParseRef("data.p.q")}), + NewExpr(SetTerm(IntNumberTerm(1))), + ), + }, + { + note: "set following variable w/ whitespace", + input: "input\n {1}", + exp: NewBody( + NewExpr(&Term{Value: MustParseRef("input")}), + NewExpr(SetTerm(IntNumberTerm(1))), + ), + }, + { + note: "set following equality w/ whitespace", + input: "input = 2 \n {1}", + exp: NewBody( + Equality.Expr(&Term{Value: MustParseRef("input")}, IntNumberTerm(2)), + NewExpr(SetTerm(IntNumberTerm(1))), + ), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + assertParseOneBody(t, tc.note, tc.input, tc.exp) + }) + } +} + +func TestBitwiseOrVsComprehension(t *testing.T) { + + x := VarTerm("x") + y := VarTerm("y") + z := VarTerm("z") + a := VarTerm("a") + b := VarTerm("b") + + tests := []struct { + note string + input string + exp *Term + }{ + { + note: "array containing bitwise or", + input: "[x|y,z]", + exp: ArrayTerm(Or.Call(x, y), z), + }, + { + note: "array containing bitwise or - last element", + input: "[z,x|y]", + exp: ArrayTerm(z, Or.Call(x, y)), + }, + { + note: "array containing bitwise or - middle", + input: "[z,x|y,a]", + exp: ArrayTerm(z, Or.Call(x, y), a), + }, + { + note: "array containing single bitwise or", + input: "[x|y,]", + exp: ArrayTerm(Or.Call(x, y)), + }, + { + note: "set containing bitwise or", + input: "{x|y,z}", + exp: SetTerm(Or.Call(x, y), z), + }, + { + note: "set containing bitwise or - last element", + input: "{z,x|y}", + exp: SetTerm(z, Or.Call(x, y)), + }, + { + note: "set containing bitwise or - middle", + input: "{z,x|y,a}", + exp: SetTerm(z, Or.Call(x, y), a), + }, + { + note: "set containing single bitwise or", + input: "{x|y,}", + exp: SetTerm(Or.Call(x, y)), + }, + { + note: "object containing bitwise or", + input: "{x:y|z,a:b}", + exp: ObjectTerm([2]*Term{x, Or.Call(y, z)}, [2]*Term{a, b}), + }, + { + note: "object containing single bitwise or", + input: "{x:y|z,}", + exp: ObjectTerm([2]*Term{x, Or.Call(y, z)}), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + + term, err := ParseTerm(tc.input) + if err != nil { + t.Fatal(err) + } + + if !term.Equal(tc.exp) { + t.Fatalf("Expected %v but got %v", tc.exp, term) + } + }) + } + +} + +func TestPackage(t *testing.T) { + ref1 := RefTerm(DefaultRootDocument, StringTerm("foo")) + assertParsePackage(t, "single", `package foo`, &Package{Path: ref1.Value.(Ref)}) + ref2 := RefTerm(DefaultRootDocument, StringTerm("f00"), StringTerm("bar_baz"), StringTerm("qux")) + assertParsePackage(t, "multiple", `package f00.bar_baz.qux`, &Package{Path: ref2.Value.(Ref)}) + ref3 := RefTerm(DefaultRootDocument, StringTerm("foo"), StringTerm("bar baz")) + assertParsePackage(t, "space", `package foo["bar baz"]`, &Package{Path: ref3.Value.(Ref)}) + assertParseError(t, "non-ground ref", "package foo[x]") + assertParseError(t, "non-string value", "package foo.bar[42].baz") + assertParseError(t, "invalid term", "package 42") + assertParseError(t, "scanner error", "package foo.") + assertParseError(t, "non-string first value", "package e().s") +} + +func TestImport(t *testing.T) { + foo := RefTerm(VarTerm("input"), StringTerm("foo")) + foobarbaz := RefTerm(VarTerm("input"), StringTerm("foo"), StringTerm("bar"), StringTerm("baz")) + whitespace := RefTerm(VarTerm("input"), StringTerm("foo"), StringTerm("bar"), StringTerm("white space")) + assertParseImport(t, "single-input", "import input", &Import{Path: RefTerm(InputRootDocument)}) + assertParseImport(t, "single-data", "import data", &Import{Path: RefTerm(DefaultRootDocument)}) + assertParseImport(t, "multiple", "import input.foo.bar.baz", &Import{Path: foobarbaz}) + assertParseImport(t, "single alias", "import input.foo as bar", &Import{Path: foo, Alias: Var("bar")}) + assertParseImport(t, "multiple alias", "import input.foo.bar.baz as qux", &Import{Path: foobarbaz, Alias: Var("qux")}) + assertParseImport(t, "white space", "import input.foo.bar[\"white space\"]", &Import{Path: whitespace}) + assertParseErrorContains(t, "non-ground ref", "import data.foo[x]", "rego_parse_error: unexpected var token: expecting string") + assertParseErrorContains(t, "non-string", "import input.foo[0]", "rego_parse_error: unexpected number token: expecting string") + assertParseErrorContains(t, "unknown root", "import foo.bar", "rego_parse_error: unexpected import path, must begin with one of: {data, future, input, rego}, got: foo") + assertParseErrorContains(t, "bad variable term", "import input as A(", "rego_parse_error: unexpected eof token: expected var") + + _, _, err := ParseStatements("", "package foo\nimport bar.data\ndefault foo=1") + if err == nil { + t.Fatalf("Expected error, but got nil") + } + if len(err.(Errors)) > 1 { + t.Fatalf("Expected a single error, got %s", err) + } + txt := err.(Errors)[0].Details.Lines()[0] + expected := "import bar.data" + if txt != expected { + t.Fatalf("Expected error detail text '%s' but got '%s'", expected, txt) + } +} + +func TestFutureImports(t *testing.T) { + assertParseErrorContains(t, "future", "import future", "invalid import, must be `future.keywords`") + assertParseErrorContains(t, "future.a", "import future.a", "invalid import, must be `future.keywords`") + assertParseErrorContains(t, "unknown keyword", "import future.keywords.xyz", "unexpected keyword, must be one of [contains every if in]") + assertParseErrorContains(t, "all keyword import + alias", "import future.keywords as xyz", "`future` imports cannot be aliased") + assertParseErrorContains(t, "keyword import + alias", "import future.keywords.in as xyz", "`future` imports cannot be aliased") + + assertParseImport(t, "import kw with kw in options", + "import future.keywords.in", &Import{Path: RefTerm(VarTerm("future"), InternedTerm("keywords"), StringTerm("in"))}, + ParserOptions{FutureKeywords: []string{"in"}}) + assertParseImport(t, "import kw with all kw in options", + "import future.keywords.in", &Import{Path: RefTerm(VarTerm("future"), InternedTerm("keywords"), StringTerm("in"))}, + ParserOptions{AllFutureKeywords: true}) + + mod := ` + package p + import future.keywords + import future.keywords.in + ` + parsed := Module{ + Package: MustParseStatement(`package p`).(*Package), + Imports: []*Import{ + MustParseStatement("import future.keywords").(*Import), + MustParseStatement("import future.keywords.in").(*Import), + }, + } + assertParseModule(t, "multiple imports, all kw in options", mod, &parsed, ParserOptions{AllFutureKeywords: true}) + assertParseModule(t, "multiple imports, single in options", mod, &parsed, ParserOptions{FutureKeywords: []string{"in"}}) +} + +func TestFutureAndRegoV1ImportsExtraction(t *testing.T) { + // These tests assert that "import future..." and "import rego.v1" statements in policies cause + // the proper keywords to be added to the parser's list of known keywords. + tests := []struct { + note, imp string + exp map[string]tokens.Token + }{ + { + note: "simple import", + imp: "import future.keywords.in", + exp: map[string]tokens.Token{"in": tokens.In}, + }, + { + note: "all keywords imported", + imp: "import future.keywords", + exp: map[string]tokens.Token{ + "in": tokens.In, + "every": tokens.Every, + "contains": tokens.Contains, + "if": tokens.If, + }, + }, + { + note: "all keywords + single keyword imported", + imp: ` + import future.keywords + import future.keywords.in`, + exp: map[string]tokens.Token{ + "in": tokens.In, + "every": tokens.Every, + "contains": tokens.Contains, + "if": tokens.If, + }, + }, + { + note: "rego.v1 imported", + imp: "import rego.v1", + exp: map[string]tokens.Token{ + "in": tokens.In, + "every": tokens.Every, + "contains": tokens.Contains, + "if": tokens.If, + }, + }, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + parser := NewParser().WithFilename("").WithReader(bytes.NewBufferString(tc.imp)) + _, _, errs := parser.Parse() + if exp, act := 0, len(errs); exp != act { + t.Fatalf("expected %d errors, got %d: %v", exp, act, errs) + } + for kw, exp := range tc.exp { + act := parser.s.s.Keyword(kw) + if act != exp { + t.Errorf("expected keyword %q to yield token %v, got %v", kw, exp, act) + } + } + }) + } +} + +func TestHintsOnUnknownImport(t *testing.T) { + assertParseErrorContains(t, "unknown", "import unknown", + "unexpected import path, must begin with one of: {data, future, input, rego}, got: unknown (hint: if this is unexpected, try updating OPA)") +} + +func TestRegoV1Import(t *testing.T) { + // These tests assert that the 'rego.v1' import is correctly handled in v0. + popts := ParserOptions{RegoVersion: RegoV0} + + assertParseErrorContains(t, "rego", "import rego", "invalid import `rego`, must be `rego.v1`", popts) + assertParseErrorContains(t, "rego.foo", "import rego.foo", "invalid import `rego.foo`, must be `rego.v1`", popts) + assertParseErrorContains(t, "rego.foo.bar", "import rego.foo.bar", "invalid import `rego.foo.bar`, must be `rego.v1`", popts) + assertParseErrorContains(t, "rego.v1.bar", "import rego.v1.bar", "invalid import `rego.v1.bar`, must be `rego.v1`", popts) + assertParseErrorContains(t, "rego.v1 + alias", "import rego.v1 as xyz", "`rego` imports cannot be aliased", popts) + + assertParseImport(t, "import rego.v1", + "import rego.v1", &Import{Path: RefTerm(VarTerm("rego"), StringTerm("v1"))}, + ParserOptions{}) + + tests := []struct { + note string + module string + expectedErrors []string + }{ + { + note: "only rego.v1 imported", + module: `package test +import rego.v1 +p contains 1 if 1 == 1`, + }, + { + note: "rego.v1 and future.keywords imported", + module: `package test +import rego.v1 +import future.keywords +p contains 1 if { + input.x == 1 +}`, + }, + { + note: "`if` keyword used on rule", + module: `package test +import rego.v1 +p if { + input.x == 1 +}`, + }, + { + note: "`if` keyword not used on rule", + module: `package test +import rego.v1 +p { + input.x == 1 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before rule body"}, + }, + { + note: "constant definition", + module: `package test +import rego.v1 +p := 1`, + }, + { + note: "`if` keyword used before else body", + module: `package test +import rego.v1 +p if { + input.x == 1 +} else if { + input.x == 2 +}`, + }, + { + note: "`if` keyword used before else body (value assignment)", + module: `package test +import rego.v1 +p := "foo" if { + input.x == 1 +} else := "bar" if { + input.x == 2 +} else := "baz" if input.x == 3 +else := "qux"`, + }, + { + note: "no else body (value assignment, but not on primary head) (regression test for #6364)", + module: `package test +import rego.v1 +p if { + input.x == 1 +} else := "baz" if input.x == 3 +else := "qux"`, + }, + { + note: "`if` keyword used before else body (value assignment, but not on primary head) (regression test for #6364)", + module: `package test +import rego.v1 +p if { + input.x == 1 +} else := "bar" if { + input.x == 2 +} else := "baz" if input.x == 3 +else := "qux"`, + }, + { + note: "`if` keyword not used before else body", + module: `package test +import rego.v1 +p if { + input.x == 1 +} else { + input.x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before rule body"}, + }, + { + note: "`if` keyword not used before else body (value assignment)", + module: `package test +import rego.v1 +p := "foo" if { + input.x == 1 +} else := "bar" { + input.x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before rule body"}, + }, + { + note: "`contains` keyword used on partial set rule (const key)", + module: `package test +import rego.v1 +p contains "q"`, + }, + { + note: "`contains` keyword used on partial set rule (ref-head, const key)", + module: `package test +import rego.v1 +p.q contains "r"`, + }, + { + note: "`contains` keyword not used on partial set rule (const key)", + module: `package test +import rego.v1 +p.q`, + expectedErrors: []string{"rego_parse_error: `contains` keyword is required for partial set rules"}, + }, + { + note: "object definition (naked ref-head with implicit `true` value)", + module: `package test +import rego.v1 +p.q.r`, + expectedErrors: []string{"rego_parse_error: rule must have value assignment and/or body declaration"}, + }, + { + note: "`contains` keyword used on partial set rule (var key, no body)", + module: `package test +import rego.v1 +p contains input.x`, + }, + { + note: "`contains` keyword not used on partial set rule (var key, no body)", + module: `package test +import rego.v1 +p[input.x]`, + expectedErrors: []string{"rego_parse_error: `contains` keyword is required for partial set rules"}, + }, + { + note: "`if` keyword not used on partial object rule (ref-head, var key, implicit `true` value, no body)", + module: `package test +import rego.v1 +p.q[input.x]`, + expectedErrors: []string{"rego_parse_error: rule must have value assignment and/or body declaration"}, + }, + { + note: "`contains` keyword used on partial set rule (var key)", + module: `package test +import rego.v1 +p contains x if { x = input.x}`, + }, + { + note: "`if` keyword used on partial map rule (would be multi-value without `if`)", + module: `package test +import rego.v1 +p[x] if { x = input.x}`, + }, + { + note: "`contains` and `if` keyword not used on partial rule", + module: `package test +import rego.v1 +p[x] { x = input.x}`, + // The developer likely intended a partial set. + expectedErrors: []string{ + "rego_parse_error: `contains` keyword is required for partial set rules", + "rego_parse_error: `if` keyword is required before rule body", + }, + }, + { + note: "`if` keyword not used on partial object rule (ref-head)", + module: `package test +import rego.v1 +p.q[x] { x = input.x}`, + expectedErrors: []string{ + "rego_parse_error: `if` keyword is required before rule body", + }, + }, + { + note: "`if` keyword not used on default rule", + module: `package test +import rego.v1 +default allow := false`, + }, + { + note: "function, value assignment, no body", + module: `package test +import rego.v1 +f(x) := x`, + }, + { + note: "function, value assignment, body, with if", + module: `package test +import rego.v1 +f(x) := x if { + x == 1 +}`, + }, + { + note: "function, value assignment, body, no if", + module: `package test +import rego.v1 +f(x) := x { + x == 1 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, no value assignment, body, with if", + module: `package test +import rego.v1 +f(x) if { + x == 1 +}`, + }, + { + note: "function, no value assignment, body, no if", + module: `package test +import rego.v1 +f(x) { + x == 1 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, else without body, value assignment", + module: `package test +import rego.v1 +f(x) := x if { + x == 1 +} else := 42`, + }, + { + note: "function, else without body, value assignment only on else (regression test for #6364)", + module: `package test +import rego.v1 +f(x) if { + x == 1 +} else := 42`, + }, + { + note: "function, else with body and if, value assignment", + module: `package test +import rego.v1 +f(x) := x if { + x == 1 +} else := 42 if { + x == 2 +}`, + }, + { + note: "function, else with body and if, no value assignment", + module: `package test +import rego.v1 +f(x) if { + x == 1 +} else if { + x == 2 +}`, + }, + { + note: "function, else with body and no if, value assignment", + module: `package test +import rego.v1 +f(x) := x if { + x == 1 +} else := 42 { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, else with body and no if, no value assignment", + module: `package test +import rego.v1 +f(x) if { + x == 1 +} else { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, else with body and no if, value assignment on primary head", + module: `package test +import rego.v1 +f(x) if { + x == 1 +} else := 42 { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, else with body and no if, value assignment on else", + module: `package test +import rego.v1 +f(x) := x if { + x == 1 +} else { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, multiple else with body, no if on last else, value assignment", + module: `package test +import rego.v1 +f(x) := x if { + x == 1 +} else := 1 if { + x == 2 +} else := 42 { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, multiple else with body, no if on last else, value assignment on primary head", + module: `package test +import rego.v1 +f(x) := x if { + x == 1 +} else if { + x == 2 +} else { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, multiple else with body, no if on last else, value assignment on first else", + module: `package test +import rego.v1 +f(x) if { + x == 1 +} else := 1 if { + x == 2 +} else { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, multiple else with body, no if on last else, value assignment on last else", + module: `package test +import rego.v1 +f(x) if { + x == 1 +} else if { + x == 2 +} else := 42 { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, multiple else with body, no if on first else, value assignment", + module: `package test +import rego.v1 +f(x) := x if { + x == 1 +} else := 1 { + x == 2 +} else := 42 if { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, multiple else with body, no if on first else, value assignment on primary head", + module: `package test +import rego.v1 +f(x) := x if { + x == 1 +} else { + x == 2 +} else if { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, multiple else with body, no if on first else, value assignment on first else", + module: `package test +import rego.v1 +f(x) if { + x == 1 +} else := 1 { + x == 2 +} else if { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, multiple else with body, no if on first else, value assignment on last else", + module: `package test +import rego.v1 +f(x) if { + x == 1 +} else { + x == 2 +} else := 42 if { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, multiple else with body, no if on any else, value assignment", + module: `package test +import rego.v1 +f(x) := x if { + x == 1 +} else := 1 { + x == 2 +} else := 42 { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function, multiple else with body, no if, value assignment", + module: `package test +import rego.v1 +f(x) := x { + x == 1 +} else := 1 { + x == 2 +} else := 42 { + x == 2 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "rule with chained bodies, no `if`", + module: `package test +import rego.v1 +p { + input.x == 1 +} { + input.x == 2 +} { + input.x == 3 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before rule body"}, + }, + { + note: "rule with chained bodies, `if` on first body", + module: `package test +import rego.v1 +p if { + input.x == 1 +} { + input.x == 2 +} { + input.x == 3 +}`, + }, + { + note: "rule with chained bodies, `if` on second body", + module: `package test +import rego.v1 +p if { + input.x == 1 +} if { + input.x == 2 +} { + input.x == 3 +}`, + expectedErrors: []string{`5:3: rego_parse_error: unexpected if keyword + } if { + ^`}, + }, + { + note: "rule with chained bodies, `if` on third/last body", + module: `package test +import rego.v1 +p if { + input.x == 1 +} { + input.x == 2 +} if { + input.x == 3 +}`, + expectedErrors: []string{`7:3: rego_parse_error: unexpected if keyword + } if { + ^`}, + }, + { + note: "rule with chained bodies, `if` and `contains` on first body", + module: `package test +import rego.v1 +p contains x if { + x == 1 +} { + x == 2 +} { + x == 3 +}`, + }, + { + note: "function with chained bodies, no `if`", + module: `package test +import rego.v1 +f(x) { + x == 1 +} { + x == 2 +} { + x == 3 +}`, + expectedErrors: []string{"rego_parse_error: `if` keyword is required before function body"}, + }, + { + note: "function with chained bodies, `if` on first body", + module: `package test +import rego.v1 +f(x) if { + x == 1 +} { + x == 2 +} { + x == 3 +}`, + }, + { + note: "function with chained bodies, `if` on other than first body", + module: `package test +import rego.v1 +f(x) if { + x == 1 +} if { + x == 2 +} { + x == 3 +}`, + expectedErrors: []string{`5:3: rego_parse_error: unexpected if keyword + } if { + ^`}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + _, errs := ParseModuleWithOpts("", tc.module, popts) + + if len(tc.expectedErrors) == 0 && errs != nil { + t.Fatalf("expected no errors, got:\n\n%v", errs) + } + + actual := "" + if errs != nil { + actual = errs.Error() + } + + for _, expected := range tc.expectedErrors { + if !strings.Contains(actual, expected) { + t.Errorf("expected error:\n\n%q\n\ngot:\n\n%v", expected, actual) + } + } + }) + } +} + +func TestIsValidImportPath(t *testing.T) { + tests := []struct { + path string + expected error + }{ + {"[1,2,3]", errors.New("invalid path [1, 2, 3]: path must be ref or var")}, + } + + for _, tc := range tests { + path := MustParseTerm(tc.path).Value + result := IsValidImportPath(path) + if tc.expected == nil && result != nil { + t.Errorf("Unexpected error for %v: %v", path, result) + } else if tc.expected.Error() != result.Error() { + t.Errorf("For %v expected %v but got: %v", path, tc.expected, result) + } + } + +} + +func TestRule(t *testing.T) { + + assertParseRule(t, "constant", `p = true { true }`, &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: NewBody( + &Expr{Terms: BooleanTerm(true)}, + ), + }) + + assertParseRule(t, "set", `p[x] { x = 42 }`, &Rule{ + Head: NewHead(Var("p"), VarTerm("x")), + Body: NewBody( + Equality.Expr(VarTerm("x"), IntNumberTerm(42)), + ), + }) + + assertParseRule(t, "object", `p[x] = y { x = 42; y = "hello" }`, &Rule{ + Head: NewHead(Var("p"), VarTerm("x"), VarTerm("y")), + Body: NewBody( + Equality.Expr(VarTerm("x"), IntNumberTerm(42)), + Equality.Expr(VarTerm("y"), StringTerm("hello")), + ), + }) + + assertParseRule(t, "constant composite", `p = [{"foo": [1, 2, 3, 4]}] { true }`, &Rule{ + Head: NewHead(Var("p"), nil, ArrayTerm( + ObjectTerm(Item(StringTerm("foo"), ArrayTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3), IntNumberTerm(4)))))), + Body: NewBody( + &Expr{Terms: BooleanTerm(true)}, + ), + }) + + assertParseRule(t, "true", `p = true { true }`, &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: NewBody( + &Expr{Terms: BooleanTerm(true)}, + ), + }) + + assertParseRule(t, "composites in head", `p[[{"x": [a, b]}]] { a = 1; b = 2 }`, &Rule{ + Head: NewHead(Var("p"), ArrayTerm( + ObjectTerm( + Item(StringTerm("x"), ArrayTerm(VarTerm("a"), VarTerm("b"))), + ), + )), + Body: NewBody( + Equality.Expr(VarTerm("a"), IntNumberTerm(1)), + Equality.Expr(VarTerm("b"), IntNumberTerm(2)), + ), + }) + + assertParseRule(t, "refs in head", `p = data.foo[x] { x = 1 }`, &Rule{ + Head: NewHead(Var("p"), nil, &Term{ + Value: MustParseRef("data.foo[x]"), + }), + Body: MustParseBody("x = 1"), + }) + + assertParseRule(t, "refs in head", `p[data.foo[x]] { true }`, &Rule{ + Head: NewHead(Var("p"), &Term{ + Value: MustParseRef("data.foo[x]"), + }), + Body: MustParseBody("true"), + }) + + assertParseRule(t, "refs in head", `p[data.foo[x]] = data.bar[y] { true }`, &Rule{ + Head: NewHead(Var("p"), &Term{ + Value: MustParseRef("data.foo[x]"), + }, &Term{ + Value: MustParseRef("data.bar[y]"), + }), + Body: MustParseBody("true"), + }) + + assertParseRule(t, "data", `data = true { true }`, &Rule{ + Head: NewHead(Var("data"), nil, MustParseTerm("true")), + Body: MustParseBody("true"), + }) + + assertParseRule(t, "input", `input = true { true }`, &Rule{ + Head: NewHead(Var("input"), nil, MustParseTerm("true")), + Body: MustParseBody("true"), + }) + + assertParseRule(t, "default", `default allow = false`, &Rule{ + Default: true, + Head: NewHead(Var("allow"), nil, MustParseTerm("false")), + Body: NewBody(NewExpr(BooleanTerm(true))), + }) + + assertParseRule(t, "default w/ assignment", `default allow := false`, &Rule{ + Default: true, + Head: &Head{ + Name: "allow", + Reference: Ref{VarTerm("allow")}, + Value: BooleanTerm(false), + Assign: true, + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }) + + assertParseRule(t, "default w/ comprehension", `default widgets = [x | x = data.fooz[_]]`, &Rule{ + Default: true, + Head: NewHead(Var("widgets"), nil, MustParseTerm(`[x | x = data.fooz[_]]`)), + Body: NewBody(NewExpr(BooleanTerm(true))), + }) + + assertParseRule(t, "one line with braces", `p[x] { x = data.a[_]; count(x, 3) }`, &Rule{ + Head: NewHead(Var("p"), VarTerm("x")), + Body: MustParseBody(`x = data.a[_]; count(x, 3)`), + }) + + assertParseRule(t, "multiple lines with braces", `p[[x, y]] { [data.a[0]] = [{"x": x}]; count(x, 3); sum(x, y); y > 100 }`, + + &Rule{ + Head: NewHead(Var("p"), MustParseTerm("[x, y]")), + Body: MustParseBody(`[data.a[0]] = [{"x": x}]; count(x, 3); sum(x, y); y > 100`), + }) + + fxy := &Head{ + Name: Var("f"), + Reference: Ref{VarTerm("f")}, + Args: Args{VarTerm("x")}, + Value: VarTerm("y"), + } + + assertParseRule(t, "identity", `f(x) = y { y = x }`, &Rule{ + Head: fxy, + Body: NewBody( + Equality.Expr(VarTerm("y"), VarTerm("x")), + ), + }) + + assertParseRule(t, "composite arg", `f([x, y]) = z { split(x, y, z) }`, &Rule{ + Head: &Head{ + Name: Var("f"), + Reference: Ref{VarTerm("f")}, + Args: Args{ArrayTerm(VarTerm("x"), VarTerm("y"))}, + Value: VarTerm("z"), + }, + Body: NewBody( + Split.Expr(VarTerm("x"), VarTerm("y"), VarTerm("z")), + ), + }) + + assertParseRule(t, "composite result", `f(1) = [x, y] { split("foo.bar", x, y) }`, &Rule{ + Head: &Head{ + Name: Var("f"), + Reference: Ref{VarTerm("f")}, + Args: Args{IntNumberTerm(1)}, + Value: ArrayTerm(VarTerm("x"), VarTerm("y")), + }, + Body: NewBody( + Split.Expr(StringTerm("foo.bar"), VarTerm("x"), VarTerm("y")), + ), + }) + + assertParseRule(t, "expr terms: key", `p[f(x) + g(x)] { true }`, &Rule{ + Head: &Head{ + Name: Var("p"), + Reference: Ref{VarTerm("p")}, + Key: Plus.Call( + CallTerm(RefTerm(VarTerm("f")), VarTerm("x")), + CallTerm(RefTerm(VarTerm("g")), VarTerm("x")), + ), + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }) + + assertParseRule(t, "expr terms: value", `p = f(x) + g(x) { true }`, &Rule{ + Head: &Head{ + Name: Var("p"), + Reference: Ref{VarTerm("p")}, + Value: Plus.Call( + CallTerm(RefTerm(VarTerm("f")), VarTerm("x")), + CallTerm(RefTerm(VarTerm("g")), VarTerm("x")), + ), + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }) + + assertParseRule(t, "expr terms: args", `p(f(x) + g(x)) { true }`, &Rule{ + Head: &Head{ + Name: Var("p"), + Reference: Ref{VarTerm("p")}, + Args: Args{ + Plus.Call( + CallTerm(RefTerm(VarTerm("f")), VarTerm("x")), + CallTerm(RefTerm(VarTerm("g")), VarTerm("x")), + ), + }, + Value: BooleanTerm(true), + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }) + + assertParseRule(t, "assignment operator", `x := 1 { true }`, &Rule{ + Head: &Head{ + Name: Var("x"), + Reference: Ref{VarTerm("x")}, + Value: IntNumberTerm(1), + Assign: true, + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }) + + assertParseRule(t, "else assignment", `x := 1 { false } else := 2`, &Rule{ + Head: &Head{ + Name: "x", // ha! clever! + Reference: Ref{VarTerm("x")}, + Value: IntNumberTerm(1), + Assign: true, + }, + Body: NewBody(NewExpr(BooleanTerm(false))), + Else: &Rule{ + Head: &Head{ + Name: "x", + Reference: Ref{VarTerm("x")}, + Value: IntNumberTerm(2), + Assign: true, + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }) + + assertParseRule(t, "partial assignment", `p[x] := y { true }`, &Rule{ + Head: &Head{ + Name: "p", + Reference: MustParseRef("p[x]"), + Value: VarTerm("y"), + Key: VarTerm("x"), + Assign: true, + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }) + + assertParseRule(t, "function assignment", `f(x) := y { true }`, &Rule{ + Head: &Head{ + Name: "f", + Reference: Ref{VarTerm("f")}, + Value: VarTerm("y"), + Args: Args{ + VarTerm("x"), + }, + Assign: true, + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }) + + // TODO: expect expressions instead? + assertParseErrorContains(t, "empty body", `f(_) = y {}`, "rego_parse_error: found empty body") + assertParseErrorContains(t, "empty rule body", "p {}", "rego_parse_error: found empty body") + assertParseErrorContains(t, "unmatched braces", `f(x) = y { trim(x, ".", y) `, `rego_parse_error: unexpected eof token: expected \n or ; or } + f(x) = y { trim(x, ".", y) + ^`) + + assertParseErrorContains(t, "no output", `f(_) = { "foo" = "bar" }`, "rego_parse_error: unexpected eq token: expected rule value term") + + assertParseErrorContains(t, "no output", `f(_) := { "foo" = "bar" }`, `rego_parse_error: unexpected eq token: non-terminated set + f(_) := { "foo" = "bar" } + ^ +1:17: rego_parse_error: unexpected eq token: expected function value term (e.g., f(...) := { ... }) + f(_) := { "foo" = "bar" } + ^`) + + assertParseErrorContains(t, "no output", `f := { "foo" = "bar" }`, `rego_parse_error: unexpected eq token: non-terminated set + f := { "foo" = "bar" } + ^ +1:14: rego_parse_error: unexpected eq token: expected rule value term (e.g., f := { ... }) + f := { "foo" = "bar" } + ^`) + assertParseErrorContains(t, "no output", `f[_] := { "foo" = "bar" }`, `rego_parse_error: unexpected eq token: non-terminated set + f[_] := { "foo" = "bar" } + ^ +1:17: rego_parse_error: unexpected eq token: expected rule value term (e.g., f[_] := { ... }) + f[_] := { "foo" = "bar" } + ^`) + assertParseErrorContains(t, "no output", `default f :=`, `rego_parse_error: unexpected eof token + default f := + ^ +1:12: rego_parse_error: unexpected eof token: expected default rule value term (e.g., default f := ) + default f := + ^`) + + // TODO(tsandall): improve error checking here. This is a common mistake + // and the current error message is not very good. Need to investigate if the + // parser can be improved. + assertParseError(t, "dangling semicolon", "p { true; false; }") + + assertParseErrorContains(t, "default invalid rule name", `default 0[0`, "unexpected default keyword") + assertParseErrorContains(t, "default invalid rule value", `default a[0]`, "illegal default rule (must have a value)") + assertParseRule(t, "default missing value", `default a`, &Rule{ + Default: true, + Head: &Head{ + Name: Var("a"), + Reference: Ref{VarTerm("a")}, + Value: BooleanTerm(true), + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }) + assertParseRule(t, "empty arguments", `f() { x := 1 }`, &Rule{ + Head: &Head{ + Name: "f", + Reference: Ref{VarTerm("f")}, + Value: BooleanTerm(true), + }, + Body: MustParseBody(`x := 1`), + }) + + assertParseErrorContains(t, "default invalid rule head ref", `default a = b.c.d`, "illegal default rule (value cannot contain ref)") + assertParseErrorContains(t, "default invalid rule head call", `default a = g(x)`, "illegal default rule (value cannot contain call)") + assertParseErrorContains(t, "default invalid rule head builtin call", `default a = upper("foo")`, "illegal default rule (value cannot contain call)") + assertParseErrorContains(t, "default invalid rule head call", `default a = b`, "illegal default rule (value cannot contain var)") + + assertParseErrorContains(t, "default invalid function head ref", `default f(x) = b.c.d`, "illegal default rule (value cannot contain ref)") + assertParseErrorContains(t, "default invalid function head call", `default f(x) = g(x)`, "illegal default rule (value cannot contain call)") + assertParseErrorContains(t, "default invalid function head builtin call", `default f(x) = upper("foo")`, "illegal default rule (value cannot contain call)") + assertParseErrorContains(t, "default invalid function head call", `default f(x) = b`, "illegal default rule (value cannot contain var)") + assertParseErrorContains(t, "default invalid function composite argument", `default f([x]) = 1`, "illegal default rule (arguments cannot contain array)") + assertParseErrorContains(t, "default invalid function number argument", `default f(1) = 1`, "illegal default rule (arguments cannot contain number)") + assertParseErrorContains(t, "default invalid function repeated vars", `default f(x, x) = 1`, "illegal default rule (arguments cannot be repeated x)") + + assertParseError(t, "extra braces", `{ a := 1 }`) + assertParseError(t, "invalid rule name hyphen", `a-b = x { x := 1 }`) + + assertParseRule(t, "wildcard name", `_ { x == 1 }`, &Rule{ + Head: &Head{ + Name: "$0", + Reference: Ref{VarTerm("$0")}, + Value: BooleanTerm(true), + }, + Body: MustParseBody(`x == 1`), + }) + + assertParseRule(t, "partial object array key", `p[[a, 1, 2]] = x { a := 1; x := "foo" }`, &Rule{ + Head: &Head{ + Name: "p", + Reference: MustParseRef("p[[a,1,2]]"), + Key: ArrayTerm(VarTerm("a"), NumberTerm("1"), NumberTerm("2")), + Value: VarTerm("x"), + }, + Body: MustParseBody(`a := 1; x := "foo"`), + }) + assertParseError(t, "invalid rule body no separator", `p { a = "foo"bar }`) + assertParseError(t, "invalid rule body no newline", `p { a b c }`) + + assertParseRule(t, "wildcard in else args", `f(_) { true } else := false`, &Rule{ + Head: &Head{ + Name: "f", + Reference: Ref{VarTerm("f")}, + Args: Args{ + VarTerm("$0"), + }, + Value: BooleanTerm(true), + }, + Body: MustParseBody(`true`), + Else: &Rule{ + Head: &Head{ + Name: "f", + Assign: true, + Reference: Ref{VarTerm("f")}, + Args: Args{ + VarTerm("$1"), + }, + Value: BooleanTerm(false), + }, + Body: MustParseBody(`true`), + }, + }) + + name := Var("f") + ref := Ref{VarTerm("f")} + tr := BooleanTerm(true) + head := func(v string) *Head { return &Head{Name: name, Reference: ref, Value: tr, Args: []*Term{VarTerm(v)}} } + assertParseModule(t, "wildcard in chained function heads", `package test + f(_) if { true } { true } +`, &Module{ + Package: MustParsePackage(`package test`), + Rules: []*Rule{ + { + Head: head("$0"), + Body: MustParseBody("true"), + }, + { + Head: head("$1"), + Body: MustParseBody("true"), + }, + }, + }, + ParserOptions{AllFutureKeywords: true}) +} + +func TestRuleContains(t *testing.T) { + opts := ParserOptions{FutureKeywords: []string{"contains", "if"}} + + tests := []struct { + note string + rule string + exp *Rule + }{ + { + note: "simple", + rule: `p contains "x" { true }`, + exp: &Rule{ + Head: NewHead(Var("p"), StringTerm("x")), + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + { + note: "no body", + rule: `p contains "x"`, + exp: &Rule{ + Head: NewHead(Var("p"), StringTerm("x")), + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + { + note: "ref head, no body", + rule: `p.q contains "x"`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q"), + Key: StringTerm("x"), + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + { + note: "ref head", + rule: `p.q contains "x" { true }`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q"), + Key: StringTerm("x"), + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + { + note: "set with var element", + rule: `deny contains msg { msg := "nonono" }`, + exp: &Rule{ + Head: NewHead(Var("deny"), VarTerm("msg")), + Body: MustParseBody(`msg := "nonono"`), + }, + }, + { + note: "set with object elem", + rule: `deny contains {"allow": false, "msg": msg} { msg := "nonono" }`, + exp: &Rule{ + Head: NewHead(Var("deny"), MustParseTerm(`{"allow": false, "msg": msg}`)), + Body: MustParseBody(`msg := "nonono"`), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + assertParseRule(t, tc.note, tc.rule, tc.exp, opts) + }) + } +} + +func TestRuleContainsFail(t *testing.T) { + opts := ParserOptions{FutureKeywords: []string{"contains", "if", "every"}} + + tests := []struct { + note string + rule string + expected string + }{ + { + note: "contains used with a 1+ argument function", + rule: "p(a) contains x { x := a }", + expected: "the contains keyword can only be used with multi-value rule definitions (e.g., p contains { ... })", + }, + { + note: "contains used with a 0 argument function", + rule: "p() contains x { x := 1 }", + expected: "the contains keyword can only be used with multi-value rule definitions (e.g., p contains { ... })", + }, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + assertParseErrorContains(t, tc.note, tc.rule, tc.expected, opts) + }) + } +} + +func TestRuleIf(t *testing.T) { + opts := ParserOptions{FutureKeywords: []string{"contains", "if", "every"}} + + tests := []struct { + note string + rule string + exp *Rule + }{ + { + note: "complete", + rule: `p if { true }`, + exp: &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + { + note: "else", + rule: `p if { true } else if { true }`, + exp: &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: NewBody(NewExpr(BooleanTerm(true))), + Else: &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + }, + { + note: "ref head, complete", + rule: `p.q if { true }`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q"), + Value: BooleanTerm(true), + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + { + note: "complete, normal body", + rule: `p if { x := 10; x > y }`, + exp: &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: MustParseBody(`x := 10; x > y`), + }, + }, + { + note: "complete+else, normal bodies, assign", + rule: `p := "yes" if { 10 > y } else := "no" { 10 <= y }`, + exp: &Rule{ + Head: &Head{ + Reference: Ref{VarTerm("p")}, + Name: Var("p"), + Value: StringTerm("yes"), + Assign: true, + }, + Body: MustParseBody(`10 > y`), + Else: &Rule{ + Head: &Head{ + Reference: Ref{VarTerm("p")}, + Name: Var("p"), + Value: StringTerm("no"), + Assign: true, + }, + Body: MustParseBody(`10 <= y`), + }, + }, + }, + { + note: "complete+else, normal bodies, assign; if", + rule: `p := "yes" if { 10 > y } else := "no" if { 10 <= y }`, + exp: &Rule{ + Head: &Head{ + Reference: Ref{VarTerm("p")}, + Name: Var("p"), + Value: StringTerm("yes"), + Assign: true, + }, + Body: MustParseBody(`10 > y`), + Else: &Rule{ + Head: &Head{ + Reference: Ref{VarTerm("p")}, + Name: Var("p"), + Value: StringTerm("no"), + Assign: true, + }, + Body: MustParseBody(`10 <= y`), + }, + }, + }, + { + note: "complete, shorthand", + rule: `p if true`, + exp: &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + { + note: "complete, else, shorthand", + rule: `p if true else if true`, + exp: &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: NewBody(NewExpr(BooleanTerm(true))), + Else: &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + }, + { + note: "complete, else, assignment+shorthand", + rule: `p if true else := 3 if 2 < 1`, + exp: &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: NewBody(NewExpr(BooleanTerm(true))), + Else: &Rule{ + Head: &Head{ + Reference: Ref{VarTerm("p")}, + Name: Var("p"), + Value: NumberTerm("3"), + Assign: true, + }, + Body: NewBody(LessThan.Expr(IntNumberTerm(2), IntNumberTerm(1))), + }, + }, + }, + { + note: "complete+not, shorthand", + rule: `p if not q`, + exp: &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: MustParseBody(`not q`), + }, + }, + { + note: "complete+else, shorthand", + rule: `p if 1 > 2 else = 42 { 2 > 1 }`, + exp: &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: MustParseBody(`1 > 2`), + Else: &Rule{ + Head: &Head{ + Reference: Ref{VarTerm("p")}, + Name: Var("p"), + Value: NumberTerm("42"), + }, + Body: MustParseBody(`2 > 1`), + }, + }, + }, + { + note: "complete+call, shorthand", + rule: `p if count(q) > 0`, + exp: &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: MustParseBody(`count(q) > 0`), + }, + }, + { + note: "function, shorthand", + rule: `f(x) = y if y := x + 1`, + exp: &Rule{ + Head: &Head{ + Reference: Ref{VarTerm("f")}, + Name: Var("f"), + Args: []*Term{VarTerm("x")}, + Value: VarTerm("y"), + }, + Body: MustParseBody(`y := x + 1`), + }, + }, + { + note: "function+every, shorthand", + rule: `f(xs) if every x in xs { x != 0 }`, + exp: &Rule{ + Head: &Head{ + Reference: Ref{VarTerm("f")}, + Name: Var("f"), + Args: []*Term{VarTerm("xs")}, + Value: BooleanTerm(true), + }, + Body: MustParseBodyWithOpts(`every x in xs { x != 0 }`, opts), + }, + }, + { + note: "object", + rule: `p["foo"] = "bar" if { true }`, + exp: &Rule{ + Head: &Head{ + Name: Var("p"), + Reference: MustParseRef("p.foo"), + Value: StringTerm("bar"), + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + { + note: "object, shorthand", + rule: `p["foo"] = "bar" if true`, + exp: &Rule{ + Head: &Head{ + Name: Var("p"), + Reference: MustParseRef("p.foo"), + Value: StringTerm("bar"), + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + { + note: "object with vars", + rule: `p[x] = y if { + x := "foo" + y := "bar" + }`, + exp: &Rule{ + Head: &Head{ + Name: Var("p"), + Reference: MustParseRef("p[x]"), + Key: VarTerm("x"), + Value: VarTerm("y"), + }, + Body: MustParseBody(`x := "foo"; y := "bar"`), + }, + }, + { + note: "set", + rule: `p contains "foo" if { true }`, + exp: &Rule{ + Head: NewHead(Var("p"), StringTerm("foo")), + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + { + note: "set, shorthand", + rule: `p contains "foo" if true`, + exp: &Rule{ + Head: NewHead(Var("p"), StringTerm("foo")), + Body: NewBody(NewExpr(BooleanTerm(true))), + }, + }, + { + note: "set+var+shorthand", + rule: `p contains x if { x := "foo" }`, + exp: &Rule{ + Head: NewHead(Var("p"), VarTerm("x")), + Body: MustParseBody(`x := "foo"`), + }, + }, + { + note: "partial set+if, shorthand", // these are now Head.Ref rules, previously forbidden + rule: `p[x] if x := 1`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p[x]"), + Key: VarTerm("x"), + Value: BooleanTerm(true), + }, + Body: MustParseBody(`x := 1`), + }, + }, + { + note: "partial set+if", // these are now Head.Ref rules, previously forbidden + rule: `p[x] if { x := 1 }`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p[x]"), + Key: VarTerm("x"), + Value: BooleanTerm(true), + }, + Body: MustParseBody(`x := 1`), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + assertParseRule(t, tc.note, tc.rule, tc.exp, opts) + }) + } +} + +func TestRuleRefHeads(t *testing.T) { + opts := ParserOptions{FutureKeywords: []string{"contains", "if", "every"}} + trueBody := NewBody(NewExpr(BooleanTerm(true))) + + tests := []struct { + note string + rule string + exp *Rule + }{ + { + note: "single-value rule", + rule: "p.q.r = 1 if true", + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q.r"), + Value: IntNumberTerm(1), + }, + Body: trueBody, + }, + }, + { + note: "single-value with brackets, string key", + rule: `p.q["r"] = 1 if true`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q.r"), + Value: IntNumberTerm(1), + }, + Body: trueBody, + }, + }, + { + note: "single-value with brackets, number key", + rule: `p.q[2] = 1 if true`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q[2]"), + Value: IntNumberTerm(1), + }, + Body: trueBody, + }, + }, + { + note: "single-value with brackets, no value", + rule: `p.q[2] if true`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q[2]"), + Value: BooleanTerm(true), + }, + Body: trueBody, + }, + }, + { + note: "single-value with brackets, var key", + rule: `p.q[x] = 1 if x := 2`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q[x]"), + Value: IntNumberTerm(1), + }, + Body: MustParseBody("x := 2"), + }, + }, + { + note: "single-value with brackets, var key, no dot", + rule: `p[x] = 1 if x := 2`, + exp: &Rule{ + Head: &Head{ + Name: Var("p"), + Reference: MustParseRef("p[x]"), + Key: VarTerm("x"), + Value: IntNumberTerm(1), + }, + Body: MustParseBody("x := 2"), + }, + }, + { + note: "multi-value, simple", + rule: `p.q.r contains x if x := 2`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q.r"), + Key: VarTerm("x"), + }, + Body: MustParseBody("x := 2"), + }, + }, + { + note: "backcompat: multi-value, no dot", + rule: `p[x] { x := 2 }`, // no "if", which triggers ref-interpretation + exp: &Rule{ + Head: &Head{ + Name: "p", + Reference: Ref{VarTerm("p")}, // we're defining p as multi-val rule + Key: VarTerm("x"), + }, + Body: MustParseBody("x := 2"), + }, + }, + { + note: "backcompat: single-value, no dot", + rule: `p[x] = 3 { x := 2 }`, + exp: &Rule{ + Head: &Head{ + Name: "p", + Reference: MustParseRef("p[x]"), + Key: VarTerm("x"), // not used + Value: IntNumberTerm(3), + }, + Body: MustParseBody("x := 2"), + }, + }, + { + note: "backcompat: single-value, no dot, complex object", + rule: `partialobj[x] = {"foo": y} { y = "bar"; x = y }`, + exp: &Rule{ + Head: &Head{ + Name: "partialobj", + Reference: MustParseRef("partialobj[x]"), + Key: VarTerm("x"), // not used + Value: MustParseTerm(`{"foo": y}`), + }, + Body: MustParseBody(`y = "bar"; x = y`), + }, + }, + { + note: "function, simple", + rule: `p.q.f(x) = 1 if true`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q.f"), + Args: Args([]*Term{VarTerm("x")}), + Value: IntNumberTerm(1), + }, + Body: trueBody, + }, + }, + { + note: "function, no value", + rule: `p.q.f(x) if true`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q.f"), + Args: Args([]*Term{VarTerm("x")}), + Value: BooleanTerm(true), + }, + Body: trueBody, + }, + }, + { + note: "function, with value", + rule: `p.q.f(x) = x + 1 if true`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q.f"), + Args: Args([]*Term{VarTerm("x")}), + Value: Plus.Call(VarTerm("x"), IntNumberTerm(1)), + }, + Body: trueBody, + }, + }, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + assertParseRule(t, tc.note, tc.rule, tc.exp, opts) + }) + } + + assertParseErrorContains(t, "first ref head term is call", `package p +q(0).r(0) { true }`, + "unexpected { token: rule head ref q(0).r invalid", opts) +} + +func TestRuleElseKeyword(t *testing.T) { + mod := `package test + + p if { + "p0" + } + + p if { + "p1" + } else if { + "p1_e1" + } else = [null] if { + "p1_e2" + } else = x if { + x = "p1_e3" + } + + p if { + "p2" + } + + f(x) if { + x < 100 + } else = false if { + x > 200 + } else if { + x != 150 + } + + _ if { + x > 0 + } else if { + x == -1 + } else if { + x > -100 + } + + nobody = 1 if { + false + } else = 7 + + nobody_f(x) = 1 if { + false + } else = 7 + ` + + parsed, err := ParseModuleWithOpts("", mod, ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatalf("Unexpected parse error: %v", err) + } + + name := Var("p") + ref := Ref{VarTerm("p")} + tr := BooleanTerm(true) + head := &Head{Name: name, Reference: ref, Value: tr} + + expected := &Module{ + Package: MustParsePackage(`package test`), + Rules: []*Rule{ + { + Head: head, + Body: MustParseBody(`"p0"`), + }, + { + Head: head, + Body: MustParseBody(`"p1"`), + Else: &Rule{ + Head: head, + Body: MustParseBody(`"p1_e1"`), + Else: &Rule{ + Head: &Head{ + Name: name, + Reference: ref, + Value: ArrayTerm(NullTerm()), + }, + Body: MustParseBody(`"p1_e2"`), + Else: &Rule{ + Head: &Head{ + Name: name, + Reference: ref, + Value: VarTerm("x"), + }, + Body: MustParseBody(`x = "p1_e3"`), + }, + }, + }, + }, + { + Head: head, + Body: MustParseBody(`"p2"`), + }, + { + Head: &Head{ + Name: Var("f"), + Reference: Ref{VarTerm("f")}, + Args: Args{VarTerm("x")}, + Value: BooleanTerm(true), + }, + Body: MustParseBody(`x < 100`), + Else: &Rule{ + Head: &Head{ + Name: Var("f"), + Reference: Ref{VarTerm("f")}, + Args: Args{VarTerm("x")}, + Value: BooleanTerm(false), + }, + Body: MustParseBody(`x > 200`), + Else: &Rule{ + Head: &Head{ + Name: Var("f"), + Reference: Ref{VarTerm("f")}, + Args: Args{VarTerm("x")}, + Value: BooleanTerm(true), + }, + Body: MustParseBody(`x != 150`), + }, + }, + }, + + { + Head: &Head{ + Name: Var("$0"), + Reference: Ref{VarTerm("$0")}, + Value: BooleanTerm(true), + }, + Body: MustParseBody(`x > 0`), + Else: &Rule{ + Head: &Head{ + Name: Var("$0"), + Reference: Ref{VarTerm("$0")}, + Value: BooleanTerm(true), + }, + Body: MustParseBody(`x == -1`), + Else: &Rule{ + Head: &Head{ + Name: Var("$0"), + Reference: Ref{VarTerm("$0")}, + Value: BooleanTerm(true), + }, + Body: MustParseBody(`x > -100`), + }, + }, + }, + { + Head: &Head{ + Name: Var("nobody"), + Reference: Ref{VarTerm("nobody")}, + Value: IntNumberTerm(1), + }, + Body: MustParseBody("false"), + Else: &Rule{ + Head: &Head{ + Name: Var("nobody"), + Reference: Ref{VarTerm("nobody")}, + Value: IntNumberTerm(7), + }, + Body: MustParseBody("true"), + }, + }, + { + Head: &Head{ + Name: Var("nobody_f"), + Reference: Ref{VarTerm("nobody_f")}, + Args: Args{VarTerm("x")}, + Value: IntNumberTerm(1), + }, + Body: MustParseBody("false"), + Else: &Rule{ + Head: &Head{ + Name: Var("nobody_f"), + Reference: Ref{VarTerm("nobody_f")}, + Args: Args{VarTerm("x")}, + Value: IntNumberTerm(7), + }, + Body: MustParseBody("true"), + }, + }, + }, + } + + if parsed.Compare(expected) != 0 { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, parsed) + } + + notExpected := &Module{ + Package: MustParsePackage(`package test`), + Rules: []*Rule{ + { + Head: head, + Body: MustParseBody(`"p0"`), + }, + { + Head: head, + Body: MustParseBody(`"p1"`), + Else: &Rule{ + Head: head, + Body: MustParseBody(`"p1_e1"`), + Else: &Rule{ + Head: &Head{ + Name: Var("p"), + Reference: Ref{VarTerm("p")}, + Value: ArrayTerm(NullTerm()), + }, + Body: MustParseBody(`"p1_e2"`), + Else: &Rule{ + Head: &Head{ + Name: name, + Reference: ref, + Value: VarTerm("x"), + }, + Body: MustParseBody(`x = "p1_e4"`), + }, + }, + }, + }, + { + Head: head, + Body: MustParseBody(`"p2"`), + }, + }, + } + + if parsed.Compare(notExpected) != -1 { + t.Fatalf("Expected not equal:\n%v\n\nGot:\n%v", parsed, notExpected) + } + + _, err = ParseModule("", ` + package test + p[1] { false } else { true } + `) + + if err == nil || !strings.Contains(err.Error(), "else keyword cannot be used on multi-value rules") { + t.Fatalf("Expected parse error but got: %v", err) + } + + _, err = ParseModule("", ` + package test + p { false } { false } else { true } + `) + + if err == nil || !strings.Contains(err.Error(), "unexpected else keyword") { + t.Fatalf("Expected parse error but got: %v", err) + } + + _, err = ParseModule("", ` + package test + p { false } else { false } { true } + `) + + if err == nil || !strings.Contains(err.Error(), "expected else keyword") { + t.Fatalf("Expected parse error but got: %v", err) + } + +} + +func TestRuleElseRefHeads(t *testing.T) { + tests := []struct { + note string + rule string + exp *Rule + err string + }{ + { + note: "simple ref head", + rule: ` +a.b.c := 1 if false +else := 2 +`, + exp: &Rule{ + Head: &Head{ + Reference: MustParseRef("a.b.c"), + Value: NumberTerm("1"), + Assign: true, + }, + Body: MustParseBody("false"), + Else: &Rule{ + Head: &Head{ + Reference: MustParseRef("a.b.c"), + Value: NumberTerm("2"), + Assign: true, + }, + Body: MustParseBody("true"), + }, + }, + }, + { + note: "multi-value ref head", + rule: ` +a.b.c contains 1 if false +else := 2 +`, + err: "else keyword cannot be used on multi-value rules", + }, + { + note: "single-value ref head with var", + rule: ` +a.b[x] := 1 if false +else := 2 +`, + err: "else keyword cannot be used on rules with variables in head", + }, + { + note: "single-value general ref head with var", + rule: ` +a.b[x].c := 1 if false +else := 2 +`, + err: "else keyword cannot be used on rules with variables in head", + }, + { + note: "single-value ref head with length 1 (last is var)", + rule: ` +a := 1 if false +else := 2 +`, + exp: &Rule{ + Head: &Head{ + Reference: Ref{VarTerm("a")}, + Name: Var("a"), + Value: NumberTerm("1"), + Assign: true, + }, + Body: MustParseBody("false"), + Else: &Rule{ + Head: &Head{ + Reference: Ref{VarTerm("a")}, + Name: Var("a"), + Value: NumberTerm("2"), + Assign: true, + }, + Body: MustParseBody("true"), + }, + }, + }, + } + + opts := ParserOptions{FutureKeywords: []string{"if", "contains"}} + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + if tc.err != "" { + assertParseErrorContains(t, tc.note, tc.rule, tc.err, opts) + return + } + if tc.exp != nil { + testModule := "package test\n" + tc.rule + assertParseModule(t, tc.note, testModule, &Module{ + Package: MustParseStatement(`package test`).(*Package), + Rules: []*Rule{tc.exp}, + }, opts) + } + }) + } +} + +func TestMultipleEnclosedBodies(t *testing.T) { + + result := module(`package ex + +p[x] = y if { + x = "a" + y = 1 +} { + x = "b" + y = 2 +} + +q = 1 + +f(x) if { + x < 10 +} { + x > 1000 +} +`) + + expected := module(`package ex + +p[x] = y if { x = "a"; y = 1 } +p[x] = y if { x = "b"; y = 2 } +q = 1 if { true } +f(x) if { x < 10 } +f(x) if { x > 1000 }`, + ) + + if !expected.Equal(result) { + t.Fatal("Expected modules to be equal but got:\n\n", result, "\n\nExpected:\n\n", expected) + } +} + +func TestEmptyModule(t *testing.T) { + r, err := ParseModule("", " ") + if err == nil { + t.Error("Expected error for empty module") + return + } + if r != nil { + t.Errorf("Expected nil for empty module: %v", r) + } +} + +func TestComments(t *testing.T) { + testModule := `package a.b.c + + import input.e.f as g # end of line + import input.h + + # by itself + + p[x] = y if { y = "foo"; + # inside a rule + x = "bar"; + x != y; + q[x] + } + + import input.xyz.abc + + q # interrupting + + contains a # the head of a rule + + if { m = [1,2, + 3, ]; + a = m[i] + + } + + r contains x if { x = [ a | # inside comprehension + a = z[i] + b[i].a = a ] + + y = { a | # inside set comprehension + a = z[i] + b[i].a = a} + + z = {a: i | # inside object comprehension + a = z[i] + b[i].a = a} + }` + + popts := ParserOptions{AllFutureKeywords: true} + + assertParseModule(t, "module comments", testModule, &Module{ + Package: MustParseStatement(`package a.b.c`).(*Package), + Imports: []*Import{ + MustParseStatement("import input.e.f as g").(*Import), + MustParseStatement("import input.h").(*Import), + MustParseStatement("import input.xyz.abc").(*Import), + }, + Rules: []*Rule{ + MustParseStatementWithOpts(`p[x] = y if { y = "foo"; x = "bar"; x != y; q[x] }`, popts).(*Rule), + MustParseStatementWithOpts(`q contains a if { m = [1, 2, 3]; a = m[i] }`, popts).(*Rule), + MustParseStatementWithOpts(`r contains x if { x = [a | a = z[i]; b[i].a = a]; y = {a | a = z[i]; b[i].a = a}; z = {a: i | a = z[i]; b[i].a = a} }`, popts).(*Rule), + }, + }, popts) + + module, err := ParseModuleWithOpts("test.rego", testModule, popts) + if err != nil { + t.Fatal("Unexpected error:", err) + } + + exp := []struct { + text string + row int + col int + }{ + {text: "end of line", row: 3, col: 28}, + {text: "by itself", row: 6, col: 5}, + {text: "inside a rule", row: 9, col: 9}, + {text: "interrupting", row: 17, col: 7}, + {text: "the head of a rule", row: 19, col: 14}, + {text: "inside comprehension", row: 27, col: 30}, + {text: "inside set comprehension", row: 31, col: 13}, + {text: "inside object comprehension", row: 35, col: 15}, + } + + if len(module.Comments) != len(exp) { + t.Fatalf("Expected %v comments but got %v", len(exp), len(module.Comments)) + } + + for i := range exp { + + expc := &Comment{ + Text: []byte(" " + exp[i].text), + Location: &Location{ + File: "test.rego", + Text: []byte("# " + exp[i].text), + Row: exp[i].row, + Col: exp[i].col, + }, + } + + if !expc.Equal(module.Comments[i]) { + comment := module.Comments[i] + fmt.Printf("comment: %v %v %v %v\n", comment.Location.File, comment.Location.Text, comment.Location.Col, comment.Location.Row) + fmt.Printf("expcomm: %v %v %v %v\n", expc.Location.File, expc.Location.Text, expc.Location.Col, expc.Location.Row) + t.Errorf("Expected %q but got: %q (want: %d:%d, got: %d:%d)", expc, comment, exp[i].row, exp[i].col, comment.Location.Row, comment.Location.Col) + } + } +} + +func TestCommentsV0(t *testing.T) { + testModule := `package a.b.c + + import input.e.f as g # end of line + import input.h + + # by itself + + p[x] = y { y = "foo"; + # inside a rule + x = "bar"; + x != y; + q[x] + } + + import input.xyz.abc + + q # interrupting + + [a] # the head of a rule + + { m = [1,2, + 3, ]; + a = m[i] + + } + + r[x] { x = [ a | # inside comprehension + a = z[i] + b[i].a = a ] + + y = { a | # inside set comprehension + a = z[i] + b[i].a = a} + + z = {a: i | # inside object comprehension + a = z[i] + b[i].a = a} + }` + + popts := ParserOptions{RegoVersion: RegoV0} + + assertParseModule(t, "module comments", testModule, &Module{ + Package: MustParseStatement(`package a.b.c`).(*Package), + Imports: []*Import{ + MustParseStatement("import input.e.f as g").(*Import), + MustParseStatement("import input.h").(*Import), + MustParseStatement("import input.xyz.abc").(*Import), + }, + Rules: []*Rule{ + MustParseStatementWithOpts(`p[x] = y { y = "foo"; x = "bar"; x != y; q[x] }`, popts).(*Rule), + MustParseStatementWithOpts(`q[a] { m = [1, 2, 3]; a = m[i] }`, popts).(*Rule), + MustParseStatementWithOpts(`r[x] { x = [a | a = z[i]; b[i].a = a]; y = {a | a = z[i]; b[i].a = a}; z = {a: i | a = z[i]; b[i].a = a} }`, popts).(*Rule), + }, + }, popts) + + module, err := ParseModuleWithOpts("test.rego", testModule, popts) + if err != nil { + t.Fatal("Unexpected error:", err) + } + + exp := []struct { + text string + row int + col int + }{ + {text: "end of line", row: 3, col: 28}, + {text: "by itself", row: 6, col: 5}, + {text: "inside a rule", row: 9, col: 9}, + {text: "interrupting", row: 17, col: 7}, + {text: "the head of a rule", row: 19, col: 6}, + {text: "inside comprehension", row: 27, col: 19}, + {text: "inside set comprehension", row: 31, col: 13}, + {text: "inside object comprehension", row: 35, col: 15}, + } + + if len(module.Comments) != len(exp) { + t.Fatalf("Expected %v comments but got %v", len(exp), len(module.Comments)) + } + + for i := range exp { + + expc := &Comment{ + Text: []byte(" " + exp[i].text), + Location: &Location{ + File: "test.rego", + Text: []byte("# " + exp[i].text), + Row: exp[i].row, + Col: exp[i].col, + }, + } + + if !expc.Equal(module.Comments[i]) { + comment := module.Comments[i] + fmt.Printf("comment: %v %v %v %v\n", comment.Location.File, comment.Location.Text, comment.Location.Col, comment.Location.Row) + fmt.Printf("expcomm: %v %v %v %v\n", expc.Location.File, expc.Location.Text, expc.Location.Col, expc.Location.Row) + t.Errorf("Expected %q but got: %q (want: %d:%d, got: %d:%d)", expc, comment, exp[i].row, exp[i].col, comment.Location.Row, comment.Location.Col) + } + } +} + +func TestCommentsWhitespace(t *testing.T) { + cases := []struct { + note string + module string + expected []string + }{ + { + note: "trailing spaces", + module: "# a comment \t \n", + expected: []string{" a comment \t "}, + }, + { + note: "trailing carriage return", + module: "# a comment\r\n", + expected: []string{" a comment"}, + }, + { + note: "trailing carriage return double newline", + module: "# a comment\r\n\n", + expected: []string{" a comment"}, + }, + { + note: "double trailing carriage return newline", + module: "#\r\r\n", + expected: []string{"\r"}, + }, + { + note: "double trailing carriage return", + module: "#\r\r", + expected: []string{"\r"}, + }, + { + note: "carriage return", + module: "#\r", + expected: []string{""}, + }, + { + note: "carriage return in comment", + module: "# abc\rdef\r\n", + expected: []string{" abc\rdef"}, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + _, comments, err := ParseStatements("", tc.module) + if err != nil { + t.Fatalf("Unexpected parse error: %s", err) + } + + for i, exp := range tc.expected { + actual := string(comments[i].Text) + if exp != actual { + t.Errorf("Expected comment text (len %d):\n\n\t%q\n\nbut got (len %d):\n\n\t%q\n\n", len(exp), exp, len(actual), actual) + } + } + }) + } +} + +func TestExample(t *testing.T) { + popts := ParserOptions{AllFutureKeywords: true} + + assertParseModule(t, "example module", testModule, &Module{ + Package: MustParseStatement(`package opa.examples`).(*Package), + Imports: []*Import{ + MustParseStatement("import data.servers").(*Import), + MustParseStatement("import data.networks").(*Import), + MustParseStatement("import data.ports").(*Import), + }, + Rules: []*Rule{ + MustParseStatementWithOpts(`violations contains server if { server = servers[i]; server.protocols[j] = "http"; public_servers[server] }`, popts).(*Rule), + MustParseStatementWithOpts(`public_servers contains server if { server = servers[i]; server.ports[j] = ports[k].id; ports[k].networks[l] = networks[m].id; networks[m].public = true }`, popts).(*Rule), + }, + }, popts) +} + +func TestModuleParseErrors(t *testing.T) { + input := ` + x = 1 # expect package + package a # unexpected package + 1 = 2 # non-var head + 1 != 2 # non-equality expr + x = y; x = 1 # multiple exprs + ` + + mod, err := ParseModule("test.rego", input) + if err == nil { + t.Fatalf("Expected error but got: %v", mod) + } + + errs, ok := err.(Errors) + if !ok { + panic("unexpected error value") + } + + if len(errs) != 5 { + t.Fatalf("Expected exactly 5 errors but got: %v", err) + } +} + +func TestLocation(t *testing.T) { + mod, err := ParseModuleWithOpts("test", testModule, ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Errorf("Unexpected error while parsing test module: %v", err) + return + } + expr := mod.Rules[0].Body[0] + if expr.Location.Col != 5 { + t.Errorf("Expected column of %v to be 5 but got: %v", expr, expr.Location.Col) + } + if expr.Location.Row != 15 { + t.Errorf("Expected row of %v to be 8 but got: %v", expr, expr.Location.Row) + } + if expr.Location.File != "test" { + t.Errorf("Expected file of %v to be test but got: %v", expr, expr.Location.File) + } +} + +func TestRuleFromBodyRefs(t *testing.T) { + opts := ParserOptions{FutureKeywords: []string{"if", "contains"}} + + // NOTE(sr): These tests assert that the other code path, parsing a module, and + // then interpreting naked expressions into (shortcut) rule definitions, works + // the same as parsing the string as a Rule directly. Without also passing + // TestRuleRefHeads, these tests are not to be trusted -- if changing something, + // start with getting TestRuleRefHeads to PASS. + // + // NOTE: Some of these test cases are invalid v1 Rego, and are locked to v0. + tests := []struct { + note string + regoVersion RegoVersion + rule string + exp string + }{ + { + note: "no dots: single-value rule (complete doc)", + regoVersion: RegoV0, + rule: `foo["bar"] = 12`, + exp: `foo["bar"] = 12 { true }`, + }, + { + note: "no dots: partial set of numbers", + regoVersion: RegoV0, + rule: `foo[1]`, + exp: `foo[1] { true }`, + }, + { + note: "no dots: shorthand set of strings", // back compat + regoVersion: RegoV0, + rule: `foo.one`, + exp: `foo["one"] { true }`, + }, + { + note: "no dots: partial set", + regoVersion: RegoV0, + rule: `foo[x] { x = 1 }`, + exp: `foo[x] { x = 1 }`, + }, + { + note: "no dots + contains + if: partial set", + rule: `foo contains x if { x = 1 }`, + exp: `foo contains x if { x = 1 }`, + }, + { + note: "no dots + if: complete doc", + rule: `foo[x] if x := 1`, + exp: `foo[x] if x := 1`, + }, + { + note: "no dots: function", + rule: `foo(x)`, + exp: `foo(x) { true }`, + }, + { + note: "no dots: function with value", + rule: `foo(x) = y`, + exp: `foo(x) = y { true }`, + }, + { + note: "no dots: partial set, ref element", + regoVersion: RegoV0, + rule: `test[arr[0]]`, + exp: `test[arr[0]] { true }`, + }, + { + note: "no dots + contains: partial set, ref element", + rule: `test contains arr[0]`, + exp: `test contains arr[0] if { true }`, + }, + { + note: "one dot: complete rule shorthand", + rule: `foo.bar = "buz"`, + exp: `foo.bar = "buz" { true }`, + }, + { + note: "one dot, bracket with var: partial object", + rule: `foo.bar[x] = "buz"`, + exp: `foo.bar[x] = "buz" { true }`, + }, + { + note: "one dot, bracket with var: partial set", + regoVersion: RegoV0, + rule: `foo.bar[x] { x = 1 }`, + exp: `foo.bar[x] { x = 1 }`, + }, + { + note: "one dot, contains with var: partial set", + rule: `foo.bar contains x if { x = 1 }`, + exp: `foo.bar contains x if { x = 1 }`, + }, + { + note: "one dot, bracket with string: complete doc", + rule: `foo.bar["baz"] = "buz"`, + exp: `foo.bar.baz = "buz" { true }`, + }, + { + note: "one dot, bracket with var, rule body: partial object", + rule: `foo.bar[x] = "buz" if { x = 1 }`, + exp: `foo.bar[x] = "buz" if { x = 1 }`, + }, + { + note: "one dot: function", + rule: `foo.bar(x)`, + exp: `foo.bar(x) { true }`, + }, + { + note: "one dot: function with value", + rule: `foo.bar(x) = y`, + exp: `foo.bar(x) = y { true }`, + }, + { + note: "two dots, bracket with var: partial object", + rule: `foo.bar.baz[x] = "buz" if { x = 1 }`, + exp: `foo.bar.baz[x] = "buz" if { x = 1 }`, + }, + { + note: "two dots, bracket with var: partial set", + regoVersion: RegoV0, + rule: `foo.bar.baz[x] { x = 1 }`, + exp: `foo.bar.baz[x] { x = 1 }`, + }, + { + note: "two dots, contains with var: partial set", + rule: `foo.bar.baz contains x if { x = 1 }`, + exp: `foo.bar.baz contains x if { x = 1 }`, + }, + { + note: "one dot, bracket with string, no key: complete doc", + regoVersion: RegoV0, + rule: `foo.bar["baz"]`, + exp: `foo.bar.baz { true }`, + }, + { + note: "one dot, bracket with string, no key, value: complete doc", + rule: `foo.bar["baz"] := true`, + exp: `foo.bar.baz := true if { true }`, + }, + { + note: "two dots: function", + rule: `foo.bar("baz")`, + exp: `foo.bar("baz") { true }`, + }, + { + note: "two dots: function with value", + rule: `foo.bar("baz") = y`, + exp: `foo.bar("baz") = y { true }`, + }, + { + note: "non-ground ref: complete doc", + rule: `foo.bar[i].baz if { i := 1 }`, + exp: `foo.bar[i].baz if { i := 1 }`, + }, + { + note: "non-ground ref, bracket-key: partial set", + regoVersion: RegoV0, + rule: `foo.bar[i].baz[x] { i := 1; x := 2 }`, + exp: `foo.bar[i].baz[x] { i := 1; x := 2 }`, + }, + { + note: "non-ground ref, contains-key: partial set", + rule: `foo.bar[i].baz contains x if { i := 1; x := 2 }`, + exp: `foo.bar[i].baz contains x if { i := 1; x := 2 }`, + }, + { + note: "non-ground ref: partial object", + rule: `foo.bar[i].baz[x] = 3 if { i := 1; x := 2 }`, + exp: `foo.bar[i].baz[x] = 3 if { i := 1; x := 2 }`, + }, + { + note: "non-ground ref: function", + rule: `foo.bar[i].baz(x) = 3 if { i := 1 }`, + exp: `foo.bar[i].baz(x) = 3 if { i := 1 }`, + }, + { + note: "last term is number: partial set", + regoVersion: RegoV0, + rule: `foo.bar.baz[3] { true }`, + exp: `foo.bar.baz[3] { true }`, + }, + { + note: "contains with number: partial set", + rule: `foo.bar.baz contains 3 if { true }`, + exp: `foo.bar.baz contains 3 if { true }`, + }, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + opts.RegoVersion = tc.regoVersion + + r, err := ParseRuleWithOpts(tc.exp, opts) + if err != nil { + t.Fatal(err) + } + + testModule := "package a.b.c\n" + tc.rule + m, err := ParseModuleWithOpts("", testModule, opts) + if err != nil { + t.Fatal(err) + } + mr := m.Rules[0] + + if r.Head.Name.Compare(mr.Head.Name) != 0 { + t.Errorf("rule.Head.Name differs:\n exp = %#v\nrule = %#v", r.Head.Name, mr.Head.Name) + } + if r.Head.Ref().Compare(mr.Head.Ref()) != 0 { + t.Errorf("rule.Head.Ref() differs:\n exp = %v\nrule = %v", r.Head.Ref(), mr.Head.Ref()) + } + exp, err := ParseRuleWithOpts(tc.exp, opts) + if err != nil { + t.Fatal(err) + } + assertParseModule(t, tc.note, testModule, &Module{ + Package: MustParseStatement(`package a.b.c`).(*Package), + Rules: []*Rule{exp}, + }, opts) + }) + } + + // edge cases + t.Run("errors", func(t *testing.T) { + t.Run("naked 'data' ref", func(t *testing.T) { + _, err := ParseModuleWithOpts("", "package a.b.c\ndata", opts) + assertErrorWithMessage(t, err, "refs cannot be used for rule head") + }) + t.Run("naked 'input' ref", func(t *testing.T) { + _, err := ParseModuleWithOpts("", "package a.b.c\ninput", opts) + assertErrorWithMessage(t, err, "refs cannot be used for rule head") + }) + }) +} + +func assertErrorWithMessage(t *testing.T, err error, msg string) { + t.Helper() + var errs Errors + if !errors.As(err, &errs) { + t.Fatalf("expected Errors, got %v %[1]T", err) + } + if exp, act := 1, len(errs); exp != act { + t.Fatalf("expected %d errors, got %d", exp, act) + } + e := errs[0] + if exp, act := msg, e.Message; exp != act { + t.Fatalf("expected error message %q, got %q", exp, act) + } +} + +func TestRuleFromBody(t *testing.T) { + popts := ParserOptions{RegoVersion: RegoV0} + + tests := []struct { + input string + exp string + }{ + {`pi = 3.14159`, `pi = 3.14159 { true }`}, + {`p[x] { x = 1 }`, `p[x] { x = 1 }`}, + {`greeting = "hello"`, `greeting = "hello" { true }`}, + {`cores = [{0: 1}, {1: 2}]`, `cores = [{0: 1}, {1: 2}] { true }`}, + {`wrapper = cores[0][1]`, `wrapper = cores[0][1] { true }`}, + {`pi = [3, 1, 4, x, y, z]`, `pi = [3, 1, 4, x, y, z] { true }`}, + {`foo["bar"] = "buz"`, `foo["bar"] = "buz" { true }`}, + {`foo["9"] = "10"`, `foo["9"] = "10" { true }`}, + {`foo.buz = "bar"`, `foo["buz"] = "bar" { true }`}, + {`bar[1]`, `bar[1] { true }`}, + {`bar[[{"foo":"baz"}]]`, `bar[[{"foo":"baz"}]] { true }`}, + {`bar.qux`, `bar["qux"] { true }`}, + {`input = 1`, `input = 1 { true }`}, + {`data = 2`, `data = 2 { true }`}, + {`f(1) = 2`, `f(1) = 2 { true }`}, + {`f(1)`, `f(1) = true { true }`}, + {`d1 := 1234`, "d1 := 1234 { true }"}, + } + + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + testModule := "package a.b.c\n" + tc.input + assertParseModule(t, tc.input, testModule, &Module{ + Package: MustParseStatement(`package a.b.c`).(*Package), + Rules: []*Rule{ + MustParseRule(tc.exp), + }, + }, popts) + }) + } + + // Verify the rule and rule and rule head col/loc values + testModule := "package a.b.c\n\n" + for _, tc := range tests { + testModule += tc.input + "\n" + } + module, err := ParseModuleWithOpts("test.rego", testModule, popts) + if err != nil { + t.Fatal(err) + } + + for i := range module.Rules { + col := module.Rules[i].Location.Col + if col != 1 { + t.Errorf("expected rule %v column to be 1 but got %v", module.Rules[i].Head.Name, col) + } + row := module.Rules[i].Location.Row + if row != 3+i { // 'pi' rule starts on row 3 + t.Errorf("expected rule %v row to be %v but got %v", module.Rules[i].Head.Name, 3+i, row) + } + col = module.Rules[i].Head.Location.Col + if col != 1 { + t.Errorf("expected rule head %v column to be 1 but got %v", module.Rules[i].Head.Name, col) + } + row = module.Rules[i].Head.Location.Row + if row != 3+i { // 'pi' rule starts on row 3 + t.Errorf("expected rule head %v row to be %v but got %v", module.Rules[i].Head.Name, 3+i, row) + } + } + + mockModule := `package ex + +input = {"foo": 1} +data = {"bar": 2}` + + assertParseModule(t, "rule name: input/data", mockModule, &Module{ + Package: MustParsePackage(`package ex`), + Rules: []*Rule{ + MustParseRule(`input = {"foo": 1} { true }`), + MustParseRule(`data = {"bar": 2} { true }`), + }, + }) + + multipleExprs := ` + package a.b.c + + pi = 3.14159; pi > 3 + ` + + nonEquality := ` + package a.b.c + + pi > 3 + ` + + nonVarName := ` + package a.b.c + + "pi" = 3 + ` + + withExpr := ` + package a.b.c + + foo = input with input as 1 + ` + + negated := ` + package a.b.c + + not p = 1` + + nonRefTerm := ` + package a.b.c + + p` + + zeroArgs := ` + package a.b.c + + p()` + + assignToTerm := ` + package a.b.c + + "foo" := 1` + + someDecl := ` + package a + + some x` + + arrayTerm := ` + package a + [][0] + ` + + callWithRuleKeyPartialSet := ` + package a + f(x)[x] { true }` + + callWithRuleKeyPartialObject := ` + package a + f(x)[x] = x { true }` + + assignNoOperands := ` + package a + assign()` + + assignOneOperand := ` + package a + assign(x)` + + eqNoOperands := ` + package a + eq()` + + eqOneOperand := ` + package a + eq(x)` + + assertParseModuleError(t, "multiple expressions", multipleExprs) + assertParseModuleError(t, "non-equality", nonEquality) + assertParseModuleError(t, "non-var name", nonVarName) + assertParseModuleError(t, "with expr", withExpr) + assertParseModuleError(t, "negated", negated) + assertParseModuleError(t, "non ref term", nonRefTerm) + assertParseModuleError(t, "zero args", zeroArgs) + assertParseModuleError(t, "assign to term", assignToTerm) + assertParseModuleError(t, "some decl", someDecl) + assertParseModuleError(t, "array term", arrayTerm) + assertParseModuleError(t, "call in ref partial set", "package test\nf().x {}") + assertParseModuleError(t, "call in ref partial object", "package test\nf().x = y {}") + assertParseModuleError(t, "number in ref", "package a\n12[3]()=4") + assertParseModuleError(t, "rule with args and key", callWithRuleKeyPartialObject) + assertParseModuleError(t, "rule with args and key", callWithRuleKeyPartialSet) + assertParseModuleError(t, "assign without operands", assignNoOperands) + assertParseModuleError(t, "assign with only one operand", assignOneOperand) + assertParseModuleError(t, "eq without operands", eqNoOperands) + assertParseModuleError(t, "eq with only one operand", eqOneOperand) + + if _, err := ParseRuleFromExpr(&Module{}, &Expr{ + Terms: struct{}{}, + }); err == nil { + t.Fatal("expected error for unknown expression term type") + } +} + +func TestWildcards(t *testing.T) { + + assertParseOneTerm(t, "ref", "a.b[_].c[_]", RefTerm( + VarTerm("a"), + StringTerm("b"), + VarTerm("$0"), + StringTerm("c"), + VarTerm("$1"), + )) + + assertParseOneTerm(t, "nested", `[{"a": a[_]}, _, {"b": _}]`, ArrayTerm( + ObjectTerm( + Item(StringTerm("a"), RefTerm(VarTerm("a"), VarTerm("$0"))), + ), + VarTerm("$1"), + ObjectTerm( + Item(StringTerm("b"), VarTerm("$2")), + ), + )) + + assertParseOneExpr(t, "expr", `_ = [a[_]]`, Equality.Expr( + VarTerm("$0"), + ArrayTerm( + RefTerm(VarTerm("a"), VarTerm("$1")), + ))) + + assertParseOneExpr(t, "comprehension", `_ = [x | a = a[_]]`, Equality.Expr( + VarTerm("$0"), + ArrayComprehensionTerm( + VarTerm("x"), + NewBody( + Equality.Expr( + VarTerm("a"), + RefTerm(VarTerm("a"), VarTerm("$1")), + ), + ), + ))) + + assertParseRule(t, "functions", `f(_) = y { true }`, &Rule{ + Head: &Head{ + Name: Var("f"), + Reference: Ref{VarTerm("f")}, + Args: Args{ + VarTerm("$0"), + }, + Value: VarTerm("y"), + }, + Body: NewBody(NewExpr(BooleanTerm(true))), + }) +} + +// https://github.com/open-policy-agent/opa/issues/7128 +func TestParseMultiValueRuleGeneratedBodyLocationText(t *testing.T) { + t.Parallel() + + mod := `package test + + import rego.v1 + + foo contains "bar" + ` + + parsed, err := ParseModule("test.rego", mod) + if err != nil { + t.Fatal(err) + } + + text := string(parsed.Rules[0].Location.Text) + + if text != `foo contains "bar"` { + t.Errorf("Expected rule location text to be %q but got %q", `foo contains "bar"`, text) + } +} + +func TestRuleModulePtr(t *testing.T) { + mod := `package test + + p if { true } + p if { true } + q if { true } + r = 1 + default s = 2 + ` + + parsed, err := ParseModuleWithOpts("", mod, ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatalf("Unexpected parse error: %v", err) + } + + for _, rule := range parsed.Rules { + if rule.Module != parsed { + t.Fatalf("Expected module ptr to be %p but got %p", parsed, rule.Module) + } + } +} + +func TestNoMatchError(t *testing.T) { + mod := `package test + + p if { true; + 1 != 0; # <-- parse error: no match + }` + + _, err := ParseModule("foo.rego", mod) + + expected := "1 error occurred: foo.rego:5: rego_parse_error: unexpected } token" + + if !strings.HasPrefix(err.Error(), expected) { + t.Fatalf("Bad parse error, expected %v but got: %v", expected, err) + } + + mod = `package test + + p if { true // <-- parse error: no match` + + _, err = ParseModuleWithOpts("foo.rego", mod, ParserOptions{AllFutureKeywords: true}) + + loc := NewLocation([]byte{'/'}, "foo.rego", 3, 15) + + if !loc.Equal(err.(Errors)[0].Location) { + t.Fatalf("Expected %v but got: %v", loc, err) + } +} + +func TestBraceBracketParenMatchingErrors(t *testing.T) { + // Checks to prevent regression on issue #4672. + // Error location is important here, which is why we check + // the error strings directly. + tests := []struct { + note string + err string + input string + }{ + { + note: "Unmatched ')' case", + err: `1 error occurred: test.rego:4: rego_parse_error: unexpected , token: expected \n or ; or } + y := contains("a"), "b") + ^`, + input: `package test +p { + x := 5 + y := contains("a"), "b") +}`, + }, + { + note: "Unmatched '}' case", + err: `1 error occurred: test.rego:4: rego_parse_error: unexpected , token: expected \n or ; or } + y := {"a", "b", "c"}, "a"} + ^`, + input: `package test +p { + x := 5 + y := {"a", "b", "c"}, "a"} +}`, + }, + { + note: "Unmatched ']' case", + err: `1 error occurred: test.rego:4: rego_parse_error: unexpected , token: expected \n or ; or } + y := ["a", "b", "c"], "a"] + ^`, + input: `package test +p { + x := 5 + y := ["a", "b", "c"], "a"] +}`, + }, + { + note: "Unmatched '(' case", + err: `1 error occurred: test.rego:5: rego_parse_error: unexpected } token: expected "," or ")" + } + ^`, + input: `package test +p { + x := 5 + y := contains("a", "b" +}`, + }, + { + note: "Unmatched '{' case", + + err: `1 error occurred: test.rego:5: rego_parse_error: unexpected eof token: expected \n or ; or } + } + ^`, + input: `package test +p { + x := 5 + y := {{"a", "b", "c"}, "a" +}`, + }, + { + note: "Unmatched '[' case", + err: `1 error occurred: test.rego:5: rego_parse_error: unexpected } token: expected "," or "]" + } + ^`, + input: `package test +p { + x := 5 + y := [["a", "b", "c"], "a" +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + _, err := ParseModule("test.rego", tc.input) + if err == nil { + t.Fatal("Expected error") + } + if tc.err != "" && tc.err != err.Error() { + t.Fatalf("Expected error string %q but got: %q", tc.err, err.Error()) + } + }) + } +} + +func TestParseErrorDetails(t *testing.T) { + + tests := []struct { + note string + exp *ParserErrorDetail + err string + input string + }{ + { + note: "no match: bad rule name", + exp: &ParserErrorDetail{ + Line: ".", + Idx: 0, + }, + input: ` +package test +.`, + }, + { + note: "no match: bad termination for comprehension", + exp: &ParserErrorDetail{ + Line: "p = [true | true}", + Idx: 16, + }, + input: ` +package test +p = [true | true}`}, + { + note: "no match: non-terminated comprehension", + exp: &ParserErrorDetail{ + Line: "p = [true | true", + Idx: 15, + }, + input: ` +package test +p = [true | true`}, + { + note: "no match: expected expression", + exp: &ParserErrorDetail{ + Line: "p { true; }", + Idx: 10, + }, + input: ` +package test +p { true; }`}, + { + note: "empty body", + exp: &ParserErrorDetail{ + Line: "p { }", + Idx: 4, + }, + input: ` +package test +p { }`}, + { + note: "non-terminated string", + exp: &ParserErrorDetail{ + Line: `p = "foo`, + Idx: 4, + }, + input: ` +package test +p = "foo`}, + { + note: "rule with error begins with one tab", + exp: &ParserErrorDetail{ + Line: "\tas", + Idx: 1, + }, + input: ` +package test + as`, + err: `1 error occurred: test.rego:3: rego_parse_error: unexpected as keyword + as + ^`}, + { + note: "rule term with error begins with two tabs", + exp: &ParserErrorDetail{ + Line: "\t\tas", + Idx: 2, + }, + input: ` +package test +p = true { + as +}`, + err: `1 error occurred: test.rego:4: rego_parse_error: unexpected as keyword + as + ^`}, + { + note: "input is tab and space tokens only", + exp: &ParserErrorDetail{ + Line: "\t\v\f ", + Idx: 0, + }, + input: "\t\v\f ", + // NOTE(sr): With the unprintable control characters, the output is pretty + // useless. But it's also quite an edge case. + err: "1 error occurred: test.rego:1: rego_parse_error: illegal token\n\t\v\f \n\t^", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + _, err := ParseModule("test.rego", tc.input) + if err == nil { + t.Fatal("Expected error") + } + detail := err.(Errors)[0].Details + if !reflect.DeepEqual(detail, tc.exp) { + t.Errorf("Expected %v but got: %v", tc.exp, detail) + } + if tc.err != "" && tc.err != err.Error() { + t.Fatalf("Expected error string %q but got: %q", tc.err, err.Error()) + } + }) + } +} + +func TestNamespacedBuiltins(t *testing.T) { + + tests := []struct { + expr string + expected *Term + wantErr bool + }{ + {`foo.bar.baz(1, 2)`, MustParseTerm("foo.bar.baz"), false}, + {`foo.(1,2)`, nil, true}, + {`foo.#.bar(1,2)`, nil, true}, + } + + for _, tc := range tests { + expr, err := ParseExpr(tc.expr) + if !tc.wantErr { + if err != nil { + t.Fatalf("Unexpected parse error: %v", err) + } + terms, ok := expr.Terms.([]*Term) + if !ok { + t.Fatalf("Expected terms not: %T", expr.Terms) + } + if !terms[0].Equal(tc.expected) { + t.Fatalf("Expected builtin-name to equal %v but got: %v", tc.expected, terms) + } + } else if err == nil { + t.Fatalf("Expected error from %v but got: %v", tc.expr, expr) + } + } +} + +func TestRuleHeadLocation(t *testing.T) { + + const input = `package pkg + +p contains x if { + x = "hi" +} { + x = "bye" +} + +f(x) if { + false +} else = false if { + true +} +` + + module := module(input) + + for _, tc := range []struct { + note string + location *Location + expectedRow int + expectedText string + }{ + { + note: "partial rule", + location: module.Rules[0].Location, + expectedRow: 3, + expectedText: ` +p contains x if { + x = "hi" +} + `, + }, + { + note: "partial rule head", + location: module.Rules[0].Head.Location, + expectedRow: 3, + expectedText: `p contains x`, + }, + { + note: "partial rule head key", + location: module.Rules[0].Head.Key.Location, + expectedRow: 3, + expectedText: `x`, + }, + { + note: "chained rule", + location: module.Rules[1].Location, + expectedRow: 5, + expectedText: ` +{ + x = "bye" +} + `, + }, + { + note: "chained rule head", + location: module.Rules[1].Head.Location, + expectedRow: 5, + expectedText: ` +{ + x = "bye" +} + `, + }, + { + note: "chained rule head key", + location: module.Rules[1].Head.Key.Location, + expectedRow: 5, + expectedText: ` +{ + x = "bye" +} + `, + }, + { + note: "rule with args", + location: module.Rules[2].Location, + expectedRow: 9, + expectedText: ` +f(x) if { + false +} else = false if { + true +} + `, + }, + { + note: "rule with args head", + location: module.Rules[2].Head.Location, + expectedRow: 9, + expectedText: `f(x)`, + }, + { + note: "rule with args head arg 0", + location: module.Rules[2].Head.Args[0].Location, + expectedRow: 9, + expectedText: `x`, + }, + { + note: "else with args", + location: module.Rules[2].Else.Location, + expectedRow: 11, + expectedText: ` +else = false if { + true +} + `, + }, + { + note: "else with args head", + location: module.Rules[2].Else.Head.Location, + expectedRow: 11, + expectedText: `else = false`, + }, + { + note: "else with args head arg 0", + location: module.Rules[2].Else.Head.Args[0].Location, + expectedRow: 9, + expectedText: `x`, + }, + } { + t.Run(tc.note, func(t *testing.T) { + if tc.location.Row != tc.expectedRow { + t.Errorf("Expected %d but got %d", tc.expectedRow, tc.location.Row) + } + exp := strings.TrimSpace(tc.expectedText) + if string(tc.location.Text) != exp { + t.Errorf("Expected text:\n%s\n\ngot:\n%s\n\n", exp, tc.location.Text) + } + }) + } +} + +func TestParserText(t *testing.T) { + + tests := []struct { + note string + input string + want string + }{ + { + note: "relational term", + input: `(1 == (2 > 3))`, + }, + { + note: "array - empty", + input: `[ ]`, + }, + { + note: "array - one element", + input: `[ 1 ]`, + }, + { + note: "array - multiple elements", + input: `[1 , 2 , 3]`, + }, + { + note: "object - empty", + input: `{ }`, + }, + { + note: "object - one element", + input: `{ "foo": 1 }`, + }, + { + note: "object - multiple elements", + input: `{"foo": 1, "bar": 2}`, + }, + { + note: "set - one element", + input: `{ 1 }`, + }, + { + note: "set - multiple elements", + input: `{1 , 2 , 3}`, + }, + { + note: "idents", + input: "foo", + }, + { + note: "ref", + input: `data.foo[x].bar`, + }, + { + note: "call", + input: `data.foo.bar(x)`, + }, + { + note: "ref and call", + input: `data.foo[1](x).bar(y)[z]`, + }, + { + note: "infix", + input: "input = 1", + }, + { + note: "negated", + input: "not x = 1", + }, + { + note: "expr with statements", + input: "x = 1 with input as 2 with input as 3", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + for _, suffix := range []string{"", "\t\n "} { + input := tc.input + suffix + + stmts, _, err := ParseStatements("test.rego", input) + if err != nil { + t.Fatal(err) + } + + if len(stmts) != 1 { + t.Fatal("expected exactly one statement but got:", stmts) + } + + result := string(stmts[0].Loc().Text) + + if result != tc.input { + t.Fatalf("expected %q but got: %q", tc.input, result) + } + } + }) + } +} + +func TestRuleText(t *testing.T) { + input := ` package test + +r[x] = y if { + x = input.a + x = "foo" +} { + x = input.b + x = "bar" +} { + x = input.c + x = "baz" +} + +r[x] = y if { + x = input.d + x = "qux" +} +` + + mod := module(input) + rules := mod.Rules + + if len(rules) != 4 { + t.Fatalf("Expected 4 rules, got %d", len(rules)) + } + + expectedRuleText := []string{ + ` +r[x] = y if { + x = input.a + x = "foo" +} + `, + ` +{ + x = input.b + x = "bar" +} + `, + ` +{ + x = input.c + x = "baz" +} + `, + ` +r[x] = y if { + x = input.d + x = "qux" +} + `, + } + + assertLocationText(t, strings.TrimSpace(expectedRuleText[0]), rules[0].Location) + assertLocationText(t, "r[x] = y", rules[0].Head.Location) + assertLocationText(t, "y", rules[0].Head.Value.Location) + + // Chained rules recursively set text on heads to be the full rule + for i := 1; i < len(expectedRuleText)-1; i++ { + text := strings.TrimSpace(expectedRuleText[i]) + assertLocationText(t, text, rules[i].Location) + assertLocationText(t, text, rules[i].Head.Location) + assertLocationText(t, text, rules[i].Head.Value.Location) + } + + assertLocationText(t, strings.TrimSpace(expectedRuleText[3]), rules[3].Location) + assertLocationText(t, "r[x] = y", rules[3].Head.Location) + assertLocationText(t, "y", rules[3].Head.Value.Location) +} + +func TestRuleElseText(t *testing.T) { + input := ` +r1 = x { + a == "foo" +} else = y { + b == "bar" +} + +else { + c == "baz" +} + +else = { + "k1": 1, + "k2": 2 +} { + true +} +` + + rule := MustParseRule(input) + assertLocationText(t, strings.TrimSpace(input), rule.Location) + assertLocationText(t, "r1 = x", rule.Head.Location) + assertLocationText(t, "x", rule.Head.Value.Location) + + curElse := rule.Else + if curElse == nil { + t.Fatalf("Expected an else block, got nil") + } + assertLocationText(t, strings.TrimSpace(` +else = y { + b == "bar" +} + +else { + c == "baz" +} + +else = { + "k1": 1, + "k2": 2 +} { + true +} + `), curElse.Location) + assertLocationText(t, "else = y", curElse.Head.Location) + assertLocationText(t, "y", curElse.Head.Value.Location) + + curElse = curElse.Else + if curElse == nil { + t.Fatalf("Expected an else block, got nil") + } + assertLocationText(t, strings.TrimSpace(` +else { + c == "baz" +} + +else = { + "k1": 1, + "k2": 2 +} { + true +} + `), curElse.Location) + assertLocationText(t, "else", curElse.Head.Location) + if curElse.Head.Value.Location != nil { + t.Errorf("Expected a nil location") + } + + curElse = curElse.Else + if curElse == nil { + t.Fatalf("Expected an else block, got nil") + } + assertLocationText(t, strings.TrimSpace(` +else = { + "k1": 1, + "k2": 2 +} { + true +} + `), curElse.Location) + assertLocationText(t, strings.TrimSpace(` +else = { + "k1": 1, + "k2": 2 +} + `), curElse.Head.Location) + assertLocationText(t, strings.TrimSpace(` +{ + "k1": 1, + "k2": 2 +} + `), curElse.Head.Value.Location) +} + +func TestAnnotations(t *testing.T) { + + dataServers := MustParseRef("data.servers") + dataNetworks := MustParseRef("data.networks") + dataPorts := MustParseRef("data.ports") + + schemaServers := MustParseRef("schema.servers") + schemaNetworks := MustParseRef("schema.networks") + schemaPorts := MustParseRef("schema.ports") + + stringSchemaAsMap := map[string]any{ + "type": "string", + } + var stringSchema any = stringSchemaAsMap + + tests := []struct { + note string + module string + expNumComments int + expAnnotations []*Annotations + expError string + expErrorRow int + }{ + { + note: "Single valid annotation", + module: ` +package opa.examples + +import data.servers +import data.networks +import data.ports + +# METADATA +# scope: rule +# schemas: +# - data.servers: schema.servers +public_servers contains server if { + server = servers[i]; server.ports[j] = ports[k].id + ports[k].networks[l] = networks[m].id; + networks[m].public = true +}`, + expNumComments: 4, + expAnnotations: []*Annotations{ + { + Schemas: []*SchemaAnnotation{ + {Path: dataServers, Schema: schemaServers}, + }, + Scope: annotationScopeRule, + }, + }, + }, + { + note: "Multiple annotations on multiple lines", + module: ` +package opa.examples + +import data.servers +import data.networks +import data.ports + +# METADATA +# scope: rule +# schemas: +# - data.servers: schema.servers +# - data.networks: schema.networks +# - data.ports: schema.ports +public_servers contains server if { + server = servers[i]; server.ports[j] = ports[k].id + ports[k].networks[l] = networks[m].id; + networks[m].public = true +}`, + expNumComments: 6, + expAnnotations: []*Annotations{ + { + Schemas: []*SchemaAnnotation{ + {Path: dataServers, Schema: schemaServers}, + {Path: dataNetworks, Schema: schemaNetworks}, + {Path: dataPorts, Schema: schemaPorts}, + }, + Scope: annotationScopeRule, + }, + }, + }, + { + note: "Comment in between metadata and rule (valid)", + module: ` +package opa.examples + +import data.servers +import data.networks +import data.ports + +# METADATA +# scope: rule +# schemas: +# - data.servers: schema.servers +# - data.networks: schema.networks +# - data.ports: schema.ports + +# This is a comment after the metadata YAML +public_servers contains server if { + server = servers[i]; server.ports[j] = ports[k].id + ports[k].networks[l] = networks[m].id; + networks[m].public = true +}`, + expNumComments: 7, + expAnnotations: []*Annotations{ + { + Schemas: []*SchemaAnnotation{ + {Path: dataServers, Schema: schemaServers}, + {Path: dataNetworks, Schema: schemaNetworks}, + {Path: dataPorts, Schema: schemaPorts}, + }, + Scope: annotationScopeRule, + }, + }, + }, + { + note: "Empty comment line in between metadata and rule (valid)", + module: ` +package opa.examples + +import data.servers +import data.networks +import data.ports + +# METADATA +# scope: rule +# schemas: +# - data.servers: schema.servers +# - data.networks: schema.networks +# - data.ports: schema.ports +# +public_servers contains server if { + server = servers[i]; server.ports[j] = ports[k].id + ports[k].networks[l] = networks[m].id; + networks[m].public = true +}`, + expNumComments: 7, + expAnnotations: []*Annotations{ + { + Schemas: []*SchemaAnnotation{ + {Path: dataServers, Schema: schemaServers}, + {Path: dataNetworks, Schema: schemaNetworks}, + {Path: dataPorts, Schema: schemaPorts}, + }, + Scope: annotationScopeRule, + }, + }, + }, + { + note: "Ill-structured (invalid) metadata start", + module: ` +package opa.examples + +import data.servers +import data.networks +import data.ports + +# METADATA +# scope: rule +# schemas: +# - data.servers: schema.servers +# - data.networks: schema.networks +# - data.ports: schema.ports +# METADATA +public_servers[server] { + server = servers[i]; server.ports[j] = ports[k].id + ports[k].networks[l] = networks[m].id; + networks[m].public = true +}`, + expError: "test.rego:14: rego_parse_error: yaml: line 6: could not find expected ':'", + }, + { + note: "Ill-structured (invalid) annotation document path", + module: ` +package opa.examples + +import data.servers +import data.networks +import data.ports + +# METADATA +# scope: rule +# schemas: +# - data/servers: schema.servers +public_servers[server] { + server = servers[i]; server.ports[j] = ports[k].id + ports[k].networks[l] = networks[m].id; + networks[m].public = true +}`, + expNumComments: 4, + expError: "rego_parse_error: invalid document reference", + }, + { + note: "Ill-structured (invalid) annotation schema path", + module: ` +package opa.examples + +import data.servers +import data.networks +import data.ports + +# METADATA +# scope: rule +# schemas: +# - data.servers: schema/servers +public_servers[server] { + server = servers[i]; server.ports[j] = ports[k].id + ports[k].networks[l] = networks[m].id; + networks[m].public = true +}`, + expNumComments: 4, + expError: "rego_parse_error: invalid schema reference", + }, + { + note: "Ill-structured (invalid) annotation", + module: ` +package opa.examples + +import data.servers +import data.networks +import data.ports + +# METADATA +# scope: rule +# schemas: +# - data.servers= schema +public_servers[server] { + server = servers[i]; server.ports[j] = ports[k].id + ports[k].networks[l] = networks[m].id; + networks[m].public = true +}`, + expNumComments: 5, + expError: "rego_parse_error: yaml: unmarshal errors:\n line 3: cannot unmarshal !!str", + expErrorRow: 11, + }, + { + note: "Ill-structured (invalid) annotation with control character (vertical tab)", + module: "# METADATA\n" + + "# title: foo\vbar\n" + + "package opa.examples\n", + expError: "rego_parse_error: yaml: control characters are not allowed", + }, + { + note: "Indentation error in yaml", + module: ` +package opa.examples + +import data.servers +import data.networks +import data.ports + +# METADATA +# scope: rule +# schemas: +# - data.servers: schema.servers +# - data.networks: schema.networks +# - data.ports: schema.ports +public_servers[server] { + server = servers[i]; server.ports[j] = ports[k].id + ports[k].networks[l] = networks[m].id; + networks[m].public = true +}`, + expNumComments: 6, + expError: "rego_parse_error: yaml: line 2: did not find expected key", + }, + { + note: "Multiple rules with and without metadata", + module: ` +package opa.examples + +import data.servers +import data.networks +import data.ports + +# METADATA +# scope: rule +# schemas: +# - data.servers: schema.servers +# - data.networks: schema.networks +# - data.ports: schema.ports +public_servers contains server if { + server = servers[i]; server.ports[j] = ports[k].id + ports[k].networks[l] = networks[m].id; + networks[m].public = true +} + +public_servers_1 contains server if { + server = servers[i]; server.ports[j] = ports[k].id + ports[k].networks[l] = networks[m].id; + networks[m].public = true + server.typo # won't catch this type error since rule has no schema metadata +}`, + expNumComments: 7, + expAnnotations: []*Annotations{ + { + Schemas: []*SchemaAnnotation{ + {Path: dataServers, Schema: schemaServers}, + {Path: dataNetworks, Schema: schemaNetworks}, + {Path: dataPorts, Schema: schemaPorts}, + }, + Scope: annotationScopeRule, + }, + }, + }, + { + note: "Multiple rules with metadata", + module: ` +package opa.examples + +import data.servers +import data.networks +import data.ports + +# METADATA +# scope: rule +# schemas: +# - data.servers: schema.servers +public_servers contains server if { + server = servers[i] +} + +# METADATA +# scope: rule +# schemas: +# - data.networks: schema.networks +# - data.ports: schema.ports +public_servers_1 contains server if { + ports[k].networks[l] = networks[m].id; + networks[m].public = true +}`, + expNumComments: 9, + expAnnotations: []*Annotations{ + { + Schemas: []*SchemaAnnotation{ + {Path: dataServers, Schema: schemaServers}, + }, + Scope: annotationScopeRule, + node: MustParseRule(`public_servers[server] { server = servers[i] }`), + }, + { + Schemas: []*SchemaAnnotation{ + + {Path: dataNetworks, Schema: schemaNetworks}, + {Path: dataPorts, Schema: schemaPorts}, + }, + Scope: annotationScopeRule, + node: MustParseRule(`public_servers_1[server] { ports[k].networks[l] = networks[m].id; networks[m].public = true }`), + }, + }, + }, + { + note: "multiple metadata blocks on a single rule", + module: `package test + +# METADATA +# title: My rule + +# METADATA +# title: My rule 2 +p if { input = "str" }`, + expNumComments: 4, + expAnnotations: []*Annotations{ + { + Scope: annotationScopeRule, + Title: "My rule", + }, + { + Scope: annotationScopeRule, + Title: "My rule 2", + }, + }, + }, + { + note: "Empty annotation error due to whitespace following METADATA hint", + module: `package test + +# METADATA + +# scope: rule +p if { input.x > 7 }`, + expError: "test.rego:3: rego_parse_error: expected METADATA block, found whitespace", + }, + { + note: "Annotation on constant", + module: ` +package test + +# METADATA +# scope: rule +p := 7`, + expNumComments: 2, + expAnnotations: []*Annotations{ + {Scope: annotationScopeRule}, + }, + }, + { + note: "annotation on package", + module: `# METADATA +# title: My package +package test + +p if { input = "str" }`, + expNumComments: 2, + expAnnotations: []*Annotations{ + { + Scope: annotationScopePackage, + Title: "My package", + }, + }, + }, + { + note: "annotation on import", + module: `package test + +# METADATA +# title: My import +import input.foo + +p if { input = "str" }`, + expNumComments: 2, + expError: "1 error occurred: test.rego:3: rego_parse_error: invalid annotation scope 'import'", + }, + { + note: "Default rule scope", + module: ` +package test + +# METADATA +# {} +p := 7`, + expNumComments: 2, + expAnnotations: []*Annotations{ + {Scope: annotationScopeRule}, + }, + }, + { + note: "Unknown scope", + module: ` +package test + +# METADATA +# scope: deadbeef +p := 7`, + expNumComments: 2, + expError: "invalid annotation scope 'deadbeef'", + }, + { + note: "Invalid rule scope/attachment", + module: ` +# METADATA +# scope: rule +package test + +p := 7`, + expNumComments: 2, + expError: "test.rego:2: rego_parse_error: annotation scope 'rule' must be applied to rule (have package)", + }, + { + note: "Scope attachment error: document on import", + module: `package test +# METADATA +# scope: document +import data.foo.bar`, + expError: "test.rego:2: rego_parse_error: annotation scope 'document' must be applied to rule (have import)", + }, + { + note: "Scope attachment error: unattached", + module: `package test + +# METADATA +# scope: package`, + expError: "test.rego:3: rego_parse_error: annotation scope 'package' must be applied to package", + }, + { + note: "Scope attachment error: package on non-package", + module: `package test +# METADATA +# scope: package +import data.foo`, + expError: "test.rego:2: rego_parse_error: annotation scope 'package' must be applied to package (have import)", + }, + { + note: "Inline schema definition", + module: `package test + +# METADATA +# schemas: +# - input: {"type": "string"} +p if { input = "str" }`, + expNumComments: 3, + expAnnotations: []*Annotations{ + { + Schemas: []*SchemaAnnotation{ + {Path: InputRootRef, Definition: &stringSchema}, + }, + Scope: annotationScopeRule, + }, + }, + }, + { + note: "Rich meta", + module: `package test + +# METADATA +# title: My rule +# description: | +# My rule has a +# multiline description. +# organizations: +# - Acme Corp. +# - Soylent Corp. +# - Tyrell Corp. +# related_resources: +# - https://example.com +# - +# ref: http://john:123@do.re/mi?foo=bar#baz +# description: foo bar +# authors: +# - John Doe +# - name: Jane Doe +# email: jane@example.com +# custom: +# list: +# - a +# - b +# map: +# a: 1 +# b: 2.2 +# c: +# "3": d +# "4": e +# number: 42 +# string: foo bar baz +# flag: +p if { input = "str" }`, + expNumComments: 31, + expAnnotations: []*Annotations{ + { + Scope: annotationScopeRule, + Title: "My rule", + Description: "My rule has a\nmultiline description.\n", + Organizations: []string{"Acme Corp.", "Soylent Corp.", "Tyrell Corp."}, + RelatedResources: []*RelatedResourceAnnotation{ + { + Ref: mustParseURL("https://example.com"), + }, + { + Ref: mustParseURL("http://john:123@do.re/mi?foo=bar#baz"), + Description: "foo bar", + }, + }, + Authors: []*AuthorAnnotation{ + { + Name: "John Doe", + Email: "john@example.com", + }, + { + Name: "Jane Doe", + Email: "jane@example.com", + }, + }, + Custom: map[string]any{ + "list": []any{ + "a", "b", + }, + "map": map[string]any{ + "a": 1, + "b": 2.2, + "c": map[string]any{ + "3": "d", + "4": "e", + }, + }, + "number": 42, + "string": "foo bar baz", + "flag": nil, + }, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + mod, err := ParseModuleWithOpts("test.rego", tc.module, ParserOptions{ + ProcessAnnotation: true, + AllFutureKeywords: true, + }) + if err != nil { + if tc.expError == "" || !strings.Contains(err.Error(), tc.expError) { + t.Fatalf("Unexpected parse error when getting annotations: %v", err) + } + if tc.expErrorRow != 0 { + if errs, ok := err.(Errors); !ok { + t.Fatalf("expected ast.Errors, got %v", err) + } else if len(errs) != 1 { + t.Fatalf("expected exactly one ast.Error, got %v: %v", len(errs), errs) + } else if loc := errs[0].Location; tc.expErrorRow != loc.Row { + t.Fatalf("expected error location row %v, got %v", tc.expErrorRow, loc.Row) + } + } + return + } else if tc.expError != "" { + t.Fatalf("Expected err: %v but no error from parse module", tc.expError) + } + + if len(mod.Comments) != tc.expNumComments { + t.Fatalf("Expected %v comments but got %v", tc.expNumComments, len(mod.Comments)) + } + + if annotationsCompare(tc.expAnnotations, mod.Annotations) != 0 { + t.Fatalf("expected %v but got %v", tc.expAnnotations, mod.Annotations) + } + }) + } +} + +func TestAnnotationsAttachedToRule(t *testing.T) { + + tests := []struct { + note string + module string + expAnnotations map[int][]*Annotations + }{ + { + note: "single metadata block for rule (implied rule scope)", + module: `# METADATA +# title: pkg +# description: pkg +package test + +# METADATA +# title: p +# description: p +p := 1`, + expAnnotations: map[int][]*Annotations{9: {{ + Description: "p", + Scope: "rule", + Title: "p", + }}}, + }, + { + note: "single metadata block for rule (explicit rule scope)", + module: `# METADATA +# title: pkg +# description: pkg +package test + +# METADATA +# title: p +# description: p +# scope: rule +p := 1`, + expAnnotations: map[int][]*Annotations{10: {{ + Description: "p", + Scope: "rule", + Title: "p", + }}}, + }, + { + note: "multiple metadata blocks for single rule", + module: `# METADATA +# title: pkg +# description: pkg +package test + +# METADATA +# title: One + +# METADATA +# title: Two + +# METADATA +# title: Three + +# METADATA +# title: Four +p := 1`, + expAnnotations: map[int][]*Annotations{17: { + { + Scope: "rule", + Title: "One", + }, + { + Scope: "rule", + Title: "Two", + }, { + Scope: "rule", + Title: "Three", + }, + { + Scope: "rule", + Title: "Four", + }, + }}, + }, + { + note: "document scope", + module: `# METADATA +# title: pkg +# description: pkg +package test + +# METADATA +# scope: document +# title: doc +# description: doc + +p := 1`, + expAnnotations: map[int][]*Annotations{11: {{ + Description: "doc", + Scope: "document", + Title: "doc", + }}}, + }, + { + note: "document and rule scope (single rule)", + module: `# METADATA +# title: pkg +# description: pkg +package test + +# METADATA +# scope: document +# title: doc +# description: doc + +# METADATA +# title: p +# description: p +p := 1`, + expAnnotations: map[int][]*Annotations{14: { + { + Description: "doc", + Scope: "document", + Title: "doc", + }, + { + Description: "p", + Scope: "rule", + Title: "p", + }, + }}, + }, + { + note: "document and rule scope (multiple rules)", + module: `# METADATA +# title: pkg +# description: pkg +package test + +# METADATA +# scope: document +# title: doc +# description: doc + +# METADATA +# title: p +# description: p +p := 1 + +# METADATA +# title: q +# description: q +q := 1`, + expAnnotations: map[int][]*Annotations{ + 14: { + { + Description: "doc", + Scope: "document", + Title: "doc", + }, + { + Description: "p", + Scope: "rule", + Title: "p", + }, + }, + 19: { + { + Description: "q", + Scope: "rule", + Title: "q", + }, + }, + }, + }, + { + note: "document and rule scope (unordered annotations, multiple rules)", + module: `# METADATA +# title: pkg +# description: pkg +package test + +# METADATA +# scope: document +# title: p-rules + +# METADATA +# title: p-1 +# description: p-1 +p contains 1 + +# METADATA +# title: p-2 +# description: p-2 +p contains 2 + +# METADATA +# title: q +# description: q +q := 1`, + expAnnotations: map[int][]*Annotations{ + 13: { + { + Scope: "document", + Title: "p-rules", + }, + { + Description: "p-1", + Scope: "rule", + Title: "p-1", + }, + }, + 18: { + { + Scope: "document", + Title: "p-rules", + }, + { + Description: "p-2", + Scope: "rule", + Title: "p-2", + }, + }, + 23: { + { + Description: "q", + Scope: "rule", + Title: "q", + }, + }, + }, + }, + { + note: "document and rule scope (unordered annotations, multiple unordered rules)", + module: `# METADATA +# title: pkg +# description: pkg +package test + +# METADATA +# scope: document +# title: p-rules + +# METADATA +# title: p-1 +# description: p-1 +p contains 1 + +# METADATA +# title: q +# description: q +q := 1 + +# METADATA +# title: p-2 +# description: p-2 +p contains 2 +`, + expAnnotations: map[int][]*Annotations{ + 13: { + { + Scope: "document", + Title: "p-rules", + }, + { + Description: "p-1", + Scope: "rule", + Title: "p-1", + }, + }, + 18: { + { + Description: "q", + Scope: "rule", + Title: "q", + }, + }, + 23: { + { + Scope: "document", + Title: "p-rules", + }, + { + Description: "p-2", + Scope: "rule", + Title: "p-2", + }, + }, + }, + }, + { + note: "rule with variable in ref head", + module: `package test + +# METADATA +# title: foo +rule[x] := true if x := 1 + `, + expAnnotations: map[int][]*Annotations{ + 5: { + { + Scope: "rule", + Title: "foo", + }, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + + pm, err := ParseModuleWithOpts("test.rego", tc.module, ParserOptions{ + ProcessAnnotation: true, + AllFutureKeywords: true, + }) + if err != nil { + t.Fatal(err) + } + + for _, rule := range pm.Rules { + annotations, ok := tc.expAnnotations[rule.Location.Row] + if !ok { + t.Fatalf("No annotations for rule on row %v", rule.Location.Row) + } + + if annotationsCompare(annotations, rule.Annotations) != 0 { + t.Fatalf("expected rule on row %d to have annotations:\n\n%v\n\nbut got:\n\n%v", + rule.Location.Row, annotations, rule.Annotations) + } + } + }) + } +} + +func TestAnnotationsAttachedToRuleMixScope(t *testing.T) { + + module := `# METADATA +# title: pkg +# description: pkg +package test + +import rego.v1 + +# METADATA +# scope: document +# title: doc +# description: doc + +# METADATA +# title: p1 +# description: p1 +p contains x if { + input.x == 1 + x := "hello" +} + +# METADATA +# title: p2 +# description: p2 +p contains x if { + input.x == 2 + x := "world" +} + +# METADATA +# title: q +# description: q +q := 1` + + pm, err := ParseModuleWithOpts("test.rego", module, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + + a1 := []*Annotations{ + { + Description: "doc", + Scope: "document", + Title: "doc", + }, + { + Description: "p1", + Scope: "rule", + Title: "p1", + }, + } + + a2 := []*Annotations{ + { + Description: "doc", + Scope: "document", + Title: "doc", + }, + { + Description: "p2", + Scope: "rule", + Title: "p2", + }, + } + + a3 := []*Annotations{ + { + Description: "q", + Scope: "rule", + Title: "q", + }, + } + + expAnnotations := [][]*Annotations{a1, a2, a3} + + for i, rule := range pm.Rules { + if annotationsCompare(expAnnotations[i], rule.Annotations) != 0 { + t.Fatalf("expected %v but got %v", expAnnotations[i], rule.Annotations) + } + } +} + +func TestAnnotationsAttachedToRuleDocScopeBeforeRule(t *testing.T) { + + module := `# METADATA +# title: pkg +# description: pkg +package test + +import rego.v1 + +# METADATA +# title: p1 +# description: p1 + +# METADATA +# scope: document +# title: doc +# description: doc + +p contains x if { + input.x == 1 + x := "hello" +} + +# METADATA +# title: p2 +# description: p2 +p contains x if { + input.x == 2 + x := "world" +} + +# METADATA +# title: q +# description: q +q := 1` + + pm, err := ParseModuleWithOpts("test.rego", module, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + + a1 := []*Annotations{ + { + Description: "p1", + Scope: "rule", + Title: "p1", + }, + { + Description: "doc", + Scope: "document", + Title: "doc", + }, + } + + a2 := []*Annotations{ + { + Description: "doc", + Scope: "document", + Title: "doc", + }, + { + Description: "p2", + Scope: "rule", + Title: "p2", + }, + } + + a3 := []*Annotations{ + { + Description: "q", + Scope: "rule", + Title: "q", + }, + } + + expAnnotations := [][]*Annotations{a1, a2, a3} + + for i, rule := range pm.Rules { + if annotationsCompare(expAnnotations[i], rule.Annotations) != 0 { + t.Fatalf("expected %v but got %v", expAnnotations[i], rule.Annotations) + } + } +} + +func TestAnnotationsAugmentedError(t *testing.T) { + tests := []struct { + note string + module string + expAnnotations []*Annotations + expErrorHint string + expErrorRow int + }{ + { + note: "no whitespace after key/value separator", + module: "package opa.examples\n" + + "\n" + + "# METADATA\n" + + "# description:p is true\n" + + "p := true\n", + expErrorHint: "Hint: on line 4, symbol(s) ['p'] immediately following a key/value separator ':' is not a legal yaml space character", + expErrorRow: 4, + }, + { + note: "non-breaking whitespace (\\u00A0) after key/value separator", + module: "package opa.examples\n" + + "\n" + + "# METADATA\n" + + "# description:\u00A0p is true\n" + + "p := true\n", + expErrorHint: "Hint: on line 4, symbol(s) ['\\u00a0'] immediately following a key/value separator ':' is not a legal yaml space character", + expErrorRow: 4, + }, + { + note: "non-breaking whitespace (\\u00A0) after key/value separator (different line)", + module: "package opa.examples\n" + + "\n" + + "# METADATA\n" + + "# title: P\n" + + "# description:\u00A0p is true\n" + + "p := true\n", + expErrorHint: "Hint: on line 5, symbol(s) ['\\u00a0'] immediately following a key/value separator ':' is not a legal yaml space character", + expErrorRow: 5, + }, + { + note: "non-breaking whitespace (\\u00A0) after key/value separator (different line)", + module: "package opa.examples\n" + + "\n" + + "# METADATA\n" + + "# title:\n" + + "# P\n" + + "# description:\u00A0p is true\n" + + "p := true\n", + expErrorHint: "Hint: on line 6, symbol(s) ['\\u00a0'] immediately following a key/value separator ':' is not a legal yaml space character", + expErrorRow: 6, + }, + { + note: "non-breaking whitespace (\\u00A0) after key/value separator (different line)", + module: "package opa.examples\n" + + "\n" + + "# METADATA\n" + + "# title:\u00A0P\n" + + "# description: p is true\n" + + "# scope: rule\n" + + "p := true\n", + expErrorHint: "Hint: on line 4, symbol(s) ['\\u00a0'] immediately following a key/value separator ':' is not a legal yaml space character", + expErrorRow: 6, // Should be 5 really, and yaml.v2 reported the error as on line 1, but v3 on line 3.. + }, + { + note: "thin whitespace (\\u2009) after key/value separator", + module: "package opa.examples\n" + + "\n" + + "# METADATA\n" + + "# description:\u2009p is true\n" + + "p := true\n", + expErrorHint: "Hint: on line 4, symbol(s) ['\\u2009'] immediately following a key/value separator ':' is not a legal yaml space character", + expErrorRow: 4, + }, + { + note: "ideographic whitespace (\\u3000) after key/value separator", + module: "package opa.examples\n" + + "\n" + + "# METADATA\n" + + "# description:\u3000p is true\n" + + "p := true\n", + expErrorHint: "Hint: on line 4, symbol(s) ['\\u3000'] immediately following a key/value separator ':' is not a legal yaml space character", + expErrorRow: 4, + }, + { + note: "several offending runes after key/value separator on single line", + module: "package opa.examples\n" + + "\n" + + "# METADATA\n" + + "# descr:iption:\u3000p is true\n" + + "p := true\n", + expErrorHint: "Hint: on line 4, symbol(s) ['i' '\\u3000'] immediately following a key/value separator ':' is not a legal yaml space character", + expErrorRow: 4, + }, + { + note: "several offending runes after key/value separator on single line", + module: "package opa.examples\n" + + "\n" + + "# METADATA\n" + + "# title:\u3000p\n" + + "# scope: rule\n" + + "# description:\u2009p is true\n" + + "p := true\n", + expErrorHint: "Hint: on line 4, symbol(s) ['\\u3000'] immediately following a key/value separator ':' is not a legal yaml space character\n" + + " Hint: on line 6, symbol(s) ['\\u2009'] immediately following a key/value separator ':' is not a legal yaml space character", + expErrorRow: 6, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + _, err := ParseModuleWithOpts("test.rego", tc.module, ParserOptions{ + ProcessAnnotation: true, + }) + + if err == nil { + t.Fatalf("Expected err with hint: %v but no error from parse module", tc.expErrorHint) + } + + if !strings.Contains(err.Error(), tc.expErrorHint) { + t.Fatalf("Unexpected parse error when getting annotations: %v", err) + } + + if errs, ok := err.(Errors); !ok { + t.Fatalf("expected ast.Errors, got %v", err) + } else if len(errs) != 1 { + t.Fatalf("expected exactly one ast.Error, got %v: %v", len(errs), errs) + } else if loc := errs[0].Location; tc.expErrorRow != loc.Row { + t.Fatalf("expected error location row %v, got %v", tc.expErrorRow, loc.Row) + } + }) + } +} + +func TestAnnotationsAreParsedAsYamlv1_2(t *testing.T) { + policy := `package p + +# METADATA +# custom: +# string: yes +is_string := rego.metadata.rule().custom.string == "yes" +` + mod := MustParseModuleWithOpts(policy, ParserOptions{ProcessAnnotation: true}) + + if len(mod.Annotations) != 1 { + t.Fatalf("Expected exactly one annotation but got %v", len(mod.Annotations)) + } + + anno := mod.Annotations[0] + if value, ok := anno.Custom["string"].(string); !ok || value != "yes" { + t.Fatalf("Expected custom.string to be 'yes' but got %v", value) + } +} + +// https://github.com/open-policy-agent/opa/issues/6587 +func TestAnnotationsParseErrorOnFirstRowGetsCorrectLocation(t *testing.T) { + module := `# METADATA +# description: ` + "`foo` bars" + ` +# title: foo +package foo` + + _, err := ParseModuleWithOpts("test.rego", module, ParserOptions{ProcessAnnotation: true}) + if err == nil { + t.Fatalf("Expected error but got none") + } + + if len(err.(Errors)) != 1 { + t.Fatalf("Expected exactly one error but got %v", err) + } + + if err.(Errors)[0].Location.Row != 2 { + t.Errorf("Expected error on row 2 but got error on row %d", err.(Errors)[0].Location.Row) + } +} + +func TestAuthorAnnotation(t *testing.T) { + tests := []struct { + note string + raw any + expected any + }{ + { + note: "no name", + raw: "", + expected: errors.New("author is an empty string"), + }, + { + note: "only whitespaces", + raw: " \t", + expected: errors.New("author is an empty string"), + }, + { + note: "one name only", + raw: "John", + expected: AuthorAnnotation{Name: "John"}, + }, + { + note: "multiple names", + raw: "John Jr.\tDoe", + expected: AuthorAnnotation{Name: "John Jr. Doe"}, + }, + { + note: "email only", + raw: "", + expected: AuthorAnnotation{Email: "john@example.com"}, + }, + { + note: "name and email", + raw: "John Doe ", + expected: AuthorAnnotation{Name: "John Doe", Email: "john@example.com"}, + }, + { + note: "empty email", + raw: "John Doe <>", + expected: AuthorAnnotation{Name: "John Doe"}, + }, + { + note: "name with reserved characters", + raw: "John Doe < >", + expected: AuthorAnnotation{Name: "John Doe < >"}, + }, + { + note: "name with reserved characters (email with space)", + raw: "", + expected: AuthorAnnotation{Name: ""}, + }, + { + note: "map with name", + raw: map[string]any{ + "name": "John Doe", + }, + expected: AuthorAnnotation{Name: "John Doe"}, + }, + { + note: "map with email", + raw: map[string]any{ + "email": "john@example.com", + }, + expected: AuthorAnnotation{Email: "john@example.com"}, + }, + { + note: "map with name and email", + raw: map[string]any{ + "name": "John Doe", + "email": "john@example.com", + }, + expected: AuthorAnnotation{Name: "John Doe", Email: "john@example.com"}, + }, + { + note: "map with extra entry", + raw: map[string]any{ + "name": "John Doe", + "email": "john@example.com", + "foo": "bar", + }, + expected: AuthorAnnotation{Name: "John Doe", Email: "john@example.com"}, + }, + { + note: "empty map", + raw: map[string]any{}, + expected: errors.New("'name' and/or 'email' values required in object"), + }, + { + note: "map with empty name", + raw: map[string]any{ + "name": "", + }, + expected: errors.New("'name' and/or 'email' values required in object"), + }, + { + note: "map with email and empty name", + raw: map[string]any{ + "name": "", + "email": "john@example.com", + }, + expected: AuthorAnnotation{Email: "john@example.com"}, + }, + { + note: "map with empty email", + raw: map[string]any{ + "email": "", + }, + expected: errors.New("'name' and/or 'email' values required in object"), + }, + { + note: "map with name and empty email", + raw: map[string]any{ + "name": "John Doe", + "email": "", + }, + expected: AuthorAnnotation{Name: "John Doe"}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + parsed, err := parseAuthor(tc.raw) + + switch expected := tc.expected.(type) { + case AuthorAnnotation: + if err != nil { + t.Fatal(err) + } + + if parsed.Compare(&expected) != 0 { + t.Fatalf("expected %v but got %v", tc.expected, parsed) + } + case error: + if err == nil { + t.Fatalf("expected '%v' error but got %v", tc.expected, parsed) + } + + if strings.Compare(expected.Error(), err.Error()) != 0 { + t.Fatalf("expected %v but got %v", tc.expected, err) + } + default: + t.Fatalf("Unexpected result type: %T", expected) + } + }) + } +} + +func TestRelatedResourceAnnotation(t *testing.T) { + tests := []struct { + note string + raw any + expected any + }{ + { + note: "empty ref URL", + raw: "", + expected: errors.New("ref URL may not be empty string"), + }, + { + note: "only whitespaces in ref URL", + raw: " \t", + expected: errors.New("parse \" \\t\": net/url: invalid control character in URL"), + }, + { + note: "invalid ref URL", + raw: "https://foo:bar", + expected: errors.New("parse \"https://foo:bar\": invalid port \":bar\" after host"), + }, + { + note: "ref URL as string", + raw: "https://example.com/foo?bar#baz", + expected: RelatedResourceAnnotation{Ref: mustParseURL("https://example.com/foo?bar#baz")}, + }, + { + note: "map with only ref", + raw: map[string]any{ + "ref": "https://example.com/foo?bar#baz", + }, + expected: RelatedResourceAnnotation{Ref: mustParseURL("https://example.com/foo?bar#baz")}, + }, + { + note: "map with only description", + raw: map[string]any{ + "description": "foo bar", + }, + expected: errors.New("'ref' value required in object"), + }, + { + note: "map with ref and description", + raw: map[string]any{ + "ref": "https://example.com/foo?bar#baz", + "description": "foo bar", + }, + expected: RelatedResourceAnnotation{ + Ref: mustParseURL("https://example.com/foo?bar#baz"), + Description: "foo bar", + }, + }, + { + note: "map with ref and description", + raw: map[string]any{ + "ref": "https://example.com/foo?bar#baz", + "description": "foo bar", + "foo": "bar", + }, + expected: RelatedResourceAnnotation{ + Ref: mustParseURL("https://example.com/foo?bar#baz"), + Description: "foo bar", + }, + }, + { + note: "empty map", + raw: map[string]any{}, + expected: errors.New("'ref' value required in object"), + }, + { + note: "map with empty ref", + raw: map[string]any{ + "ref": "", + }, + expected: errors.New("'ref' value required in object"), + }, + { + note: "map with only whitespace in ref", + raw: map[string]any{ + "ref": " \t", + }, + expected: errors.New("'ref' value required in object"), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + parsed, err := parseRelatedResource(tc.raw) + + switch expected := tc.expected.(type) { + case RelatedResourceAnnotation: + if err != nil { + t.Fatal(err) + } + + if parsed.Compare(&expected) != 0 { + t.Fatalf("expected %v but got %v", tc.expected, parsed) + } + case error: + if err == nil { + t.Fatalf("expected '%v' error but got %v", tc.expected, parsed) + } + + if strings.Compare(expected.Error(), err.Error()) != 0 { + t.Fatalf("expected %v but got %v", tc.expected, err) + } + default: + t.Fatalf("Unexpected result type: %T", expected) + } + }) + } +} + +func TestAnnotationsLocationText(t *testing.T) { + module := `# METADATA +# title: pkg +# description: a package +package pkg + +import rego.v1 + +# METADATA +# title: rule +allow if { + true +} +` + + m, err := ParseModuleWithOpts("test.rego", module, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + + assertLocationText(t, "# METADATA\n# title: pkg\n# description: a package", m.Annotations[0].Location) + assertLocationText(t, "# METADATA\n# title: rule", m.Annotations[1].Location) + + assertLocationText(t, "# METADATA\n# title: rule", m.Rules[0].Annotations[0].Location) +} + +func TestMaxParsingRecursionDepth(t *testing.T) { + tests := []struct { + name string + input string + depthLimit int + expectError bool + }{ + { + name: "deeply nested array exceeds default limit", + input: generateDeeplyNestedArray(DefaultMaxParsingRecursionDepth + 1), + expectError: true, + }, + { + name: "deeply nested array exceeds limit", + input: generateDeeplyNestedArray(1000), + depthLimit: 500, + expectError: true, + }, + { + name: "deeply nested array within limit", + input: generateDeeplyNestedArray(100), + depthLimit: 500, + expectError: false, + }, + { + name: "deeply nested object exceeds limit", + input: generateDeeplyNestedObject(1000), + depthLimit: 500, + expectError: true, + }, + { + name: "deeply nested object within limit", + input: generateDeeplyNestedObject(100), + depthLimit: 500, + expectError: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Create module code + moduleCode := "package test\n\nvalue = " + tc.input + + // Parse module + parser := NewParser(). + WithFilename("test.rego"). + WithReader(strings.NewReader(moduleCode)) + + if tc.depthLimit != 0 { + parser = parser.WithMaxRecursionDepth(tc.depthLimit) + } + + _, _, errs := parser.Parse() + + hasErr := len(errs) > 0 + if hasErr != tc.expectError { + t.Errorf("Test %q: expected error: %v, got error: %v, error detail: %v", tc.name, tc.expectError, hasErr, errs) + } + + if tc.expectError && hasErr { + // Verify the error contains our expected error + errFound := false + for _, e := range errs { + if strings.Contains(e.Message, ErrMaxParsingRecursionDepthExceeded.Error()) { + errFound = true + break + } + } + if !errFound { + t.Errorf("Expected error to contain %q, but got: %v", + ErrMaxParsingRecursionDepthExceeded.Error(), errs) + } + } + }) + } +} + +// generateDeeplyNestedArray creates a deeply nested array as a string +// with the specified depth. +func generateDeeplyNestedArray(depth int) string { + return strings.Repeat("[", depth) + "1" + strings.Repeat("]", depth) +} + +// generateDeeplyNestedObject creates a deeply nested object as a string +// with the specified depth. +func generateDeeplyNestedObject(depth int) string { + var sb strings.Builder + for i := range depth { + sb.WriteString(fmt.Sprintf(`{"key%d": `, i)) + } + sb.WriteString("1") + for range depth { + sb.WriteString("}") + } + + return sb.String() +} + +func assertLocationText(t *testing.T, expected string, actual *Location) { + t.Helper() + if actual == nil || actual.Text == nil { + t.Errorf("Expected a non nil location and text") + return + } + if string(actual.Text) != expected { + t.Errorf("Unexpected Location text, got:\n%s\n\nExpected:\n%s\n\n", actual.Text, expected) + } +} + +func assertParseError(t *testing.T, msg string, input string, opts ...ParserOptions) { + t.Helper() + t.Run(msg, func(t *testing.T) { + assertParseErrorFunc(t, msg, input, func(string) {}, opts...) + }) +} + +func assertParseErrorContains(t *testing.T, msg string, input string, expected string, opts ...ParserOptions) { + t.Helper() + assertParseErrorFunc(t, msg, input, func(result string) { + t.Helper() + if !strings.Contains(result, expected) { + t.Errorf("Error on test \"%s\": expected parse error to contain:\n\n%v\n\nbut got:\n\n%v", msg, expected, result) + } + }, opts...) +} + +func assertParseErrorFunc(t *testing.T, msg string, input string, f func(string), opts ...ParserOptions) { + t.Helper() + opt := ParserOptions{} + if len(opts) == 1 { + opt = opts[0] + } + stmts, _, err := ParseStatementsWithOpts("", input, opt) + if err == nil && len(stmts) != 1 { + err = errors.New("expected exactly one statement") + } + if err == nil { + t.Errorf("Error on test \"%s\": expected parse error on %s: expected no statements, got %d: %v", msg, input, len(stmts), stmts) + return + } + result := err.Error() + // error occurred: :: + parts := strings.SplitN(result, ":", 4) + result = strings.TrimSpace(parts[len(parts)-1]) + f(result) +} + +func assertParseImport(t *testing.T, msg string, input string, correct *Import, opts ...ParserOptions) { + t.Helper() + assertParseOne(t, msg, input, func(parsed any) { + t.Helper() + imp := parsed.(*Import) + if !imp.Equal(correct) { + t.Errorf("Error on test \"%s\": imports not equal: %v (parsed), %v (correct)", msg, imp, correct) + } + }, opts...) +} + +func assertParseModule(t *testing.T, msg string, input string, correct *Module, opts ...ParserOptions) { + t.Helper() + + opt := ParserOptions{} + if len(opts) == 1 { + opt = opts[0] + } + m, err := ParseModuleWithOpts("", input, opt) + if err != nil { + t.Errorf("Error on test \"%s\": parse error on %s: %s", msg, input, err) + return + } + + if !m.Equal(correct) { + t.Errorf("Error on test %s: modules not equal: %v (parsed), %v (correct)", msg, m, correct) + } + +} + +func assertParseModuleError(t *testing.T, msg, input string) { + m, err := ParseModule("", input) + if err == nil { + t.Errorf("Error on test \"%s\": expected parse error: %v (parsed)", msg, m) + } +} + +func assertParsePackage(t *testing.T, msg string, input string, correct *Package, opts ...ParserOptions) { + assertParseOne(t, msg, input, func(parsed any) { + pkg := parsed.(*Package) + if !pkg.Equal(correct) { + t.Errorf("Error on test \"%s\": packages not equal: %v (parsed), %v (correct)", msg, pkg, correct) + } + }, opts...) +} + +func assertParseOne(t *testing.T, msg string, input string, correct func(any), opts ...ParserOptions) { + t.Helper() + opt := ParserOptions{} + if len(opts) == 1 { + opt = opts[0] + } + stmts, _, err := ParseStatementsWithOpts("", input, opt) + if err != nil { + t.Errorf("Error on test \"%s\": parse error on %s: %s", msg, input, err) + return + } + if len(stmts) != 1 { + t.Errorf("Error on test \"%s\": parse error on %s: expected exactly one statement, got %d: %v", msg, input, len(stmts), stmts) + return + } + correct(stmts[0]) +} + +func assertParseOneBody(t *testing.T, msg string, input string, correct Body) { + t.Helper() + body, err := ParseBody(input) + if err != nil { + t.Fatal(err) + } + if !body.Equal(correct) { + t.Fatalf("Error on test \"%s\": bodies not equal:\n%v (parsed)\n%v (correct)", msg, body, correct) + } +} + +func assertParseOneExpr(t *testing.T, msg string, input string, correct *Expr, opts ...ParserOptions) { + t.Helper() + assertParseOne(t, msg, input, func(parsed any) { + t.Helper() + body := parsed.(Body) + if len(body) != 1 { + t.Errorf("Error on test \"%s\": parser returned multiple expressions: %v", msg, body) + return + } + expr := body[0] + if !expr.Equal(correct) { + t.Errorf("Error on test \"%s\": expressions not equal:\n%v (parsed)\n%v (correct)", msg, expr, correct) + } + }, opts...) +} + +func assertParseOneExprNegated(t *testing.T, msg string, input string, correct *Expr) { + correct.Negated = true + assertParseOneExpr(t, msg, input, correct) +} + +func assertParseOneTerm(t *testing.T, msg string, input string, correct *Term, opts ...ParserOptions) { + t.Helper() + t.Run(msg, func(t *testing.T) { + assertParseOneExpr(t, msg, input, &Expr{Terms: correct}, opts...) + }) +} + +func assertParseOneTermNegated(t *testing.T, msg string, input string, correct *Term) { + t.Helper() + assertParseOneExprNegated(t, msg, input, &Expr{Terms: correct}) +} + +func assertParseRule(t *testing.T, msg string, input string, correct *Rule, opts ...ParserOptions) { + t.Helper() + assertParseOne(t, msg, input, func(parsed any) { + t.Helper() + rule := parsed.(*Rule) + if rule.Head.Name != correct.Head.Name { + t.Errorf("Error on test \"%s\": rule heads not equal: name = %v (parsed), name = %v (correct)", msg, rule.Head.Name, correct.Head.Name) + } + if !rule.Head.Ref().Equal(correct.Head.Ref()) { + t.Errorf("Error on test \"%s\": rule heads not equal: ref = %v (parsed), ref = %v (correct)", msg, rule.Head.Ref(), correct.Head.Ref()) + } + if !rule.Head.Equal(correct.Head) { + t.Errorf("Error on test \"%s\": rule heads not equal: %v (parsed), %v (correct)", msg, rule.Head, correct.Head) + } + if !rule.Equal(correct) { + t.Errorf("Error on test \"%s\": rules not equal: %v (parsed), %v (correct)", msg, rule, correct) + } + }, + opts...) +} diff --git a/third_party/opa/v1/ast/policy.go b/third_party/opa/v1/ast/policy.go new file mode 100644 index 000000000000..62c82f51ec0c --- /dev/null +++ b/third_party/opa/v1/ast/policy.go @@ -0,0 +1,2005 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "bytes" + "encoding/json" + "fmt" + "slices" + "strings" + + "github.com/open-policy-agent/opa/v1/ast/internal/tokens" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/util" +) + +// DefaultRootDocument is the default root document. +// +// All package directives inside source files are implicitly prefixed with the +// DefaultRootDocument value. +var DefaultRootDocument = VarTerm("data") + +// InputRootDocument names the document containing query arguments. +var InputRootDocument = VarTerm("input") + +// SchemaRootDocument names the document containing external data schemas. +var SchemaRootDocument = VarTerm("schema") + +// FunctionArgRootDocument names the document containing function arguments. +// It's only for internal usage, for referencing function arguments between +// the index and topdown. +var FunctionArgRootDocument = VarTerm("args") + +// FutureRootDocument names the document containing new, to-become-default, +// features. +var FutureRootDocument = VarTerm("future") + +// RegoRootDocument names the document containing new, to-become-default, +// features in a future versioned release. +var RegoRootDocument = VarTerm("rego") + +// RootDocumentNames contains the names of top-level documents that can be +// referred to in modules and queries. +// +// Note, the schema document is not currently implemented in the evaluator so it +// is not registered as a root document name (yet). +var RootDocumentNames = NewSet( + DefaultRootDocument, + InputRootDocument, +) + +// DefaultRootRef is a reference to the root of the default document. +// +// All refs to data in the policy engine's storage layer are prefixed with this ref. +var DefaultRootRef = Ref{DefaultRootDocument} + +// InputRootRef is a reference to the root of the input document. +// +// All refs to query arguments are prefixed with this ref. +var InputRootRef = Ref{InputRootDocument} + +// SchemaRootRef is a reference to the root of the schema document. +// +// All refs to schema documents are prefixed with this ref. Note, the schema +// document is not currently implemented in the evaluator so it is not +// registered as a root document ref (yet). +var SchemaRootRef = Ref{SchemaRootDocument} + +// RootDocumentRefs contains the prefixes of top-level documents that all +// non-local references start with. +var RootDocumentRefs = NewSet( + NewTerm(DefaultRootRef), + NewTerm(InputRootRef), +) + +// SystemDocumentKey is the name of the top-level key that identifies the system +// document. +const SystemDocumentKey = String("system") + +// ReservedVars is the set of names that refer to implicitly ground vars. +var ReservedVars = NewVarSet( + DefaultRootDocument.Value.(Var), + InputRootDocument.Value.(Var), +) + +// Wildcard represents the wildcard variable as defined in the language. +var Wildcard = &Term{Value: Var("_")} + +// WildcardPrefix is the special character that all wildcard variables are +// prefixed with when the statement they are contained in is parsed. +const WildcardPrefix = "$" + +// Keywords contains strings that map to language keywords. +var Keywords = KeywordsForRegoVersion(DefaultRegoVersion) + +var KeywordsV0 = [...]string{ + "not", + "package", + "import", + "as", + "default", + "else", + "with", + "null", + "true", + "false", + "some", +} + +var KeywordsV1 = [...]string{ + "not", + "package", + "import", + "as", + "default", + "else", + "with", + "null", + "true", + "false", + "some", + "if", + "contains", + "in", + "every", +} + +func KeywordsForRegoVersion(v RegoVersion) []string { + switch v { + case RegoV0: + return KeywordsV0[:] + case RegoV1, RegoV0CompatV1: + return KeywordsV1[:] + } + return nil +} + +// IsKeyword returns true if s is a language keyword. +func IsKeyword(s string) bool { + return IsInKeywords(s, Keywords) +} + +func IsInKeywords(s string, keywords []string) bool { + return slices.Contains(keywords, s) +} + +// IsKeywordInRegoVersion returns true if s is a language keyword. +func IsKeywordInRegoVersion(s string, regoVersion RegoVersion) bool { + switch regoVersion { + case RegoV0: + for _, x := range KeywordsV0 { + if x == s { + return true + } + } + case RegoV1, RegoV0CompatV1: + for _, x := range KeywordsV1 { + if x == s { + return true + } + } + } + + return false +} + +type ( + // Node represents a node in an AST. Nodes may be statements in a policy module + // or elements of an ad-hoc query, expression, etc. + Node interface { + fmt.Stringer + Loc() *Location + SetLoc(*Location) + } + + // Statement represents a single statement in a policy module. + Statement interface { + Node + } +) + +type ( + + // Module represents a collection of policies (defined by rules) + // within a namespace (defined by the package) and optional + // dependencies on external documents (defined by imports). + Module struct { + Package *Package `json:"package"` + Imports []*Import `json:"imports,omitempty"` + Annotations []*Annotations `json:"annotations,omitempty"` + Rules []*Rule `json:"rules,omitempty"` + Comments []*Comment `json:"comments,omitempty"` + stmts []Statement + regoVersion RegoVersion + } + + // Comment contains the raw text from the comment in the definition. + Comment struct { + // TODO: these fields have inconsistent JSON keys with other structs in this package. + Text []byte + Location *Location + } + + // Package represents the namespace of the documents produced + // by rules inside the module. + Package struct { + Path Ref `json:"path"` + Location *Location `json:"location,omitempty"` + } + + // Import represents a dependency on a document outside of the policy + // namespace. Imports are optional. + Import struct { + Path *Term `json:"path"` + Alias Var `json:"alias,omitempty"` + Location *Location `json:"location,omitempty"` + } + + // 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"` + 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 + // left unset. The pointer is not included in any standard operations + // on the rule (e.g., printing, comparison, visiting, etc.) + Module *Module `json:"-"` + + generatedBody bool + } + + // Head represents the head of a rule. + Head struct { + Name Var `json:"name,omitempty"` + Reference Ref `json:"ref,omitempty"` + Args Args `json:"args,omitempty"` + Key *Term `json:"key,omitempty"` + Value *Term `json:"value,omitempty"` + Assign bool `json:"assign,omitempty"` + Location *Location `json:"location,omitempty"` + + keywords []tokens.Token + generatedValue bool + } + + // Args represents zero or more arguments to a rule. + Args []*Term + + // Body represents one or more expressions contained inside a rule or user + // function. + Body []*Expr + + // Expr represents a single expression contained inside the body of a rule. + Expr struct { + With []*With `json:"with,omitempty"` + Terms any `json:"terms"` + Index int `json:"index"` + Generated bool `json:"generated,omitempty"` + Negated bool `json:"negated,omitempty"` + Location *Location `json:"location,omitempty"` + + generatedFrom *Expr + generates []*Expr + } + + // SomeDecl represents a variable declaration statement. The symbols are variables. + SomeDecl struct { + Symbols []*Term `json:"symbols"` + Location *Location `json:"location,omitempty"` + } + + Every struct { + Key *Term `json:"key"` + Value *Term `json:"value"` + Domain *Term `json:"domain"` + Body Body `json:"body"` + Location *Location `json:"location,omitempty"` + } + + // With represents a modifier on an expression. + With struct { + Target *Term `json:"target"` + Value *Term `json:"value"` + Location *Location `json:"location,omitempty"` + } +) + +// SetModuleRegoVersion sets the RegoVersion for the Module. +func SetModuleRegoVersion(mod *Module, v RegoVersion) { + mod.regoVersion = v +} + +// Compare returns an integer indicating whether mod is less than, equal to, +// or greater than other. +func (mod *Module) Compare(other *Module) int { + if mod == nil { + if other == nil { + return 0 + } + return -1 + } else if other == nil { + return 1 + } + if cmp := mod.Package.Compare(other.Package); cmp != 0 { + return cmp + } + if cmp := importsCompare(mod.Imports, other.Imports); cmp != 0 { + return cmp + } + if cmp := annotationsCompare(mod.Annotations, other.Annotations); cmp != 0 { + return cmp + } + return rulesCompare(mod.Rules, other.Rules) +} + +// Copy returns a deep copy of mod. +func (mod *Module) Copy() *Module { + cpy := *mod + cpy.Rules = make([]*Rule, len(mod.Rules)) + + nodes := make(map[Node]Node, len(mod.Rules)+len(mod.Imports)+1 /* package */) + + for i := range mod.Rules { + cpy.Rules[i] = mod.Rules[i].Copy() + cpy.Rules[i].Module = &cpy + nodes[mod.Rules[i]] = cpy.Rules[i] + } + + cpy.Imports = make([]*Import, len(mod.Imports)) + for i := range mod.Imports { + cpy.Imports[i] = mod.Imports[i].Copy() + nodes[mod.Imports[i]] = cpy.Imports[i] + } + + cpy.Package = mod.Package.Copy() + nodes[mod.Package] = cpy.Package + + cpy.Annotations = make([]*Annotations, len(mod.Annotations)) + for i, a := range mod.Annotations { + cpy.Annotations[i] = a.Copy(nodes[a.node]) + } + + cpy.Comments = make([]*Comment, len(mod.Comments)) + for i := range mod.Comments { + cpy.Comments[i] = mod.Comments[i].Copy() + } + + cpy.stmts = make([]Statement, len(mod.stmts)) + for i := range mod.stmts { + cpy.stmts[i] = nodes[mod.stmts[i]] + } + + return &cpy +} + +// Equal returns true if mod equals other. +func (mod *Module) Equal(other *Module) bool { + return mod.Compare(other) == 0 +} + +func (mod *Module) String() string { + byNode := map[Node][]*Annotations{} + for _, a := range mod.Annotations { + byNode[a.node] = append(byNode[a.node], a) + } + + appendAnnotationStrings := func(buf []string, node Node) []string { + if as, ok := byNode[node]; ok { + for i := range as { + buf = append(buf, "# METADATA") + buf = append(buf, "# "+as[i].String()) + } + } + return buf + } + + buf := []string{} + buf = appendAnnotationStrings(buf, mod.Package) + buf = append(buf, mod.Package.String()) + + if len(mod.Imports) > 0 { + buf = append(buf, "") + for _, imp := range mod.Imports { + buf = appendAnnotationStrings(buf, imp) + buf = append(buf, imp.String()) + } + } + if len(mod.Rules) > 0 { + buf = append(buf, "") + for _, rule := range mod.Rules { + buf = appendAnnotationStrings(buf, rule) + buf = append(buf, rule.stringWithOpts(toStringOpts{regoVersion: mod.regoVersion})) + } + } + return strings.Join(buf, "\n") +} + +// RuleSet returns a RuleSet containing named rules in the mod. +func (mod *Module) RuleSet(name Var) RuleSet { + rs := NewRuleSet() + for _, rule := range mod.Rules { + if rule.Head.Name.Equal(name) { + rs.Add(rule) + } + } + return rs +} + +// UnmarshalJSON parses bs and stores the result in mod. The rules in the module +// will have their module pointer set to mod. +func (mod *Module) UnmarshalJSON(bs []byte) error { + + // Declare a new type and use a type conversion to avoid recursively calling + // Module#UnmarshalJSON. + type module Module + + if err := util.UnmarshalJSON(bs, (*module)(mod)); err != nil { + return err + } + + WalkRules(mod, func(rule *Rule) bool { + rule.Module = mod + return false + }) + + return nil +} + +func (mod *Module) regoV1Compatible() bool { + return mod.regoVersion == RegoV1 || mod.regoVersion == RegoV0CompatV1 +} + +func (mod *Module) RegoVersion() RegoVersion { + return mod.regoVersion +} + +// SetRegoVersion sets the RegoVersion for the module. +// Note: Setting a rego-version that does not match the module's rego-version might have unintended consequences. +func (mod *Module) SetRegoVersion(v RegoVersion) { + mod.regoVersion = v +} + +// NewComment returns a new Comment object. +func NewComment(text []byte) *Comment { + return &Comment{ + Text: text, + } +} + +// Loc returns the location of the comment in the definition. +func (c *Comment) Loc() *Location { + if c == nil { + return nil + } + return c.Location +} + +// SetLoc sets the location on c. +func (c *Comment) SetLoc(loc *Location) { + c.Location = loc +} + +func (c *Comment) String() string { + return "#" + string(c.Text) +} + +// Copy returns a deep copy of c. +func (c *Comment) Copy() *Comment { + cpy := *c + cpy.Text = make([]byte, len(c.Text)) + copy(cpy.Text, c.Text) + return &cpy +} + +// Equal returns true if this comment equals the other comment. +// Unlike other equality checks on AST nodes, comment equality +// depends on location. +func (c *Comment) Equal(other *Comment) bool { + return c.Location.Equal(other.Location) && bytes.Equal(c.Text, other.Text) +} + +// Compare returns an integer indicating whether pkg is less than, equal to, +// or greater than other. +func (pkg *Package) Compare(other *Package) int { + return termSliceCompare(pkg.Path, other.Path) +} + +// Copy returns a deep copy of pkg. +func (pkg *Package) Copy() *Package { + cpy := *pkg + cpy.Path = pkg.Path.Copy() + return &cpy +} + +// Equal returns true if pkg is equal to other. +func (pkg *Package) Equal(other *Package) bool { + return pkg.Compare(other) == 0 +} + +// Loc returns the location of the Package in the definition. +func (pkg *Package) Loc() *Location { + if pkg == nil { + return nil + } + return pkg.Location +} + +// SetLoc sets the location on pkg. +func (pkg *Package) SetLoc(loc *Location) { + pkg.Location = loc +} + +func (pkg *Package) String() string { + if pkg == nil { + return "" + } else if len(pkg.Path) <= 1 { + return fmt.Sprintf("package ", pkg.Path) + } + // Omit head as all packages have the DefaultRootDocument prepended at parse time. + path := make(Ref, len(pkg.Path)-1) + path[0] = VarTerm(string(pkg.Path[1].Value.(String))) + copy(path[1:], pkg.Path[2:]) + return fmt.Sprintf("package %v", path) +} + +func (pkg *Package) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "path": pkg.Path, + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Package { + if pkg.Location != nil { + data["location"] = pkg.Location + } + } + + return json.Marshal(data) +} + +// IsValidImportPath returns an error indicating if the import path is invalid. +// If the import path is valid, err is nil. +func IsValidImportPath(v Value) (err error) { + switch v := v.(type) { + case Var: + if !v.Equal(DefaultRootDocument.Value) && !v.Equal(InputRootDocument.Value) { + return fmt.Errorf("invalid path %v: path must begin with input or data", v) + } + case Ref: + if err := IsValidImportPath(v[0].Value); err != nil { + return fmt.Errorf("invalid path %v: path must begin with input or data", v) + } + for _, e := range v[1:] { + if _, ok := e.Value.(String); !ok { + return fmt.Errorf("invalid path %v: path elements must be strings", v) + } + } + default: + return fmt.Errorf("invalid path %v: path must be ref or var", v) + } + return nil +} + +// Compare returns an integer indicating whether imp is less than, equal to, +// or greater than other. +func (imp *Import) Compare(other *Import) int { + if imp == nil { + if other == nil { + return 0 + } + return -1 + } else if other == nil { + return 1 + } + if cmp := Compare(imp.Path, other.Path); cmp != 0 { + return cmp + } + + return VarCompare(imp.Alias, other.Alias) +} + +// Copy returns a deep copy of imp. +func (imp *Import) Copy() *Import { + cpy := *imp + cpy.Path = imp.Path.Copy() + return &cpy +} + +// Equal returns true if imp is equal to other. +func (imp *Import) Equal(other *Import) bool { + return imp.Compare(other) == 0 +} + +// Loc returns the location of the Import in the definition. +func (imp *Import) Loc() *Location { + if imp == nil { + return nil + } + return imp.Location +} + +// SetLoc sets the location on imp. +func (imp *Import) SetLoc(loc *Location) { + imp.Location = loc +} + +// Name returns the variable that is used to refer to the imported virtual +// document. This is the alias if defined otherwise the last element in the +// path. +func (imp *Import) Name() Var { + if len(imp.Alias) != 0 { + return imp.Alias + } + switch v := imp.Path.Value.(type) { + case Var: + return v + case Ref: + if len(v) == 1 { + return v[0].Value.(Var) + } + return Var(v[len(v)-1].Value.(String)) + } + panic("illegal import") +} + +func (imp *Import) String() string { + buf := []string{"import", imp.Path.String()} + if len(imp.Alias) > 0 { + buf = append(buf, "as", imp.Alias.String()) + } + return strings.Join(buf, " ") +} + +func (imp *Import) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "path": imp.Path, + } + + if len(imp.Alias) != 0 { + data["alias"] = imp.Alias + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Import { + if imp.Location != nil { + data["location"] = imp.Location + } + } + + return json.Marshal(data) +} + +// Compare returns an integer indicating whether rule is less than, equal to, +// or greater than other. +func (rule *Rule) Compare(other *Rule) int { + if rule == nil { + if other == nil { + return 0 + } + return -1 + } else if other == nil { + return 1 + } + if cmp := rule.Head.Compare(other.Head); cmp != 0 { + return cmp + } + if rule.Default != other.Default { + if !rule.Default { + return -1 + } + return 1 + } + 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) +} + +// Copy returns a deep copy of rule. +func (rule *Rule) Copy() *Rule { + cpy := *rule + cpy.Head = rule.Head.Copy() + cpy.Body = rule.Body.Copy() + + if len(cpy.Annotations) > 0 { + 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() + } + return &cpy +} + +// Equal returns true if rule is equal to other. +func (rule *Rule) Equal(other *Rule) bool { + return rule.Compare(other) == 0 +} + +// Loc returns the location of the Rule in the definition. +func (rule *Rule) Loc() *Location { + if rule == nil { + return nil + } + return rule.Location +} + +// SetLoc sets the location on rule. +func (rule *Rule) SetLoc(loc *Location) { + rule.Location = loc +} + +// Path returns a ref referring to the document produced by this rule. If rule +// is not contained in a module, this function panics. +// Deprecated: Poor handling of ref rules. Use `(*Rule).Ref()` instead. +func (rule *Rule) Path() Ref { + if rule.Module == nil { + panic("assertion failed") + } + return rule.Module.Package.Path.Extend(rule.Head.Ref().GroundPrefix()) +} + +// Ref returns a ref referring to the document produced by this rule. If rule +// is not contained in a module, this function panics. The returned ref may +// contain variables in the last position. +func (rule *Rule) Ref() Ref { + if rule.Module == nil { + panic("assertion failed") + } + return rule.Module.Package.Path.Extend(rule.Head.Ref()) +} + +func (rule *Rule) String() string { + regoVersion := DefaultRegoVersion + if rule.Module != nil { + regoVersion = rule.Module.RegoVersion() + } + return rule.stringWithOpts(toStringOpts{regoVersion: regoVersion}) +} + +type toStringOpts struct { + regoVersion RegoVersion +} + +func (o toStringOpts) RegoVersion() RegoVersion { + if o.regoVersion == RegoUndefined { + return DefaultRegoVersion + } + return o.regoVersion +} + +func (rule *Rule) stringWithOpts(opts toStringOpts) string { + buf := []string{} + if rule.Default { + buf = append(buf, "default") + } + buf = append(buf, rule.Head.stringWithOpts(opts)) + if !rule.Default { + switch opts.RegoVersion() { + case RegoV1, RegoV0CompatV1: + buf = append(buf, "if") + } + buf = append(buf, "{", rule.Body.String(), "}") + } + if rule.Else != nil { + buf = append(buf, rule.Else.elseString(opts)) + } + return strings.Join(buf, " ") +} + +func (rule *Rule) isFunction() bool { + return len(rule.Head.Args) > 0 +} + +func (rule *Rule) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "head": rule.Head, + "body": rule.Body, + } + + if rule.Default { + data["default"] = true + } + + if rule.Else != nil { + data["else"] = rule.Else + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Rule { + if rule.Location != nil { + data["location"] = rule.Location + } + } + + if len(rule.Annotations) != 0 { + data["annotations"] = rule.Annotations + } + + return json.Marshal(data) +} + +func (rule *Rule) elseString(opts toStringOpts) string { + var buf []string + + buf = append(buf, "else") + + value := rule.Head.Value + if value != nil { + buf = append(buf, "=", value.String()) + } + + switch opts.RegoVersion() { + case RegoV1, RegoV0CompatV1: + buf = append(buf, "if") + } + + buf = append(buf, "{", rule.Body.String(), "}") + + if rule.Else != nil { + buf = append(buf, rule.Else.elseString(opts)) + } + + return strings.Join(buf, " ") +} + +// NewHead returns a new Head object. If args are provided, the first will be +// used for the key and the second will be used for the value. +func NewHead(name Var, args ...*Term) *Head { + head := &Head{ + Name: name, // backcompat + Reference: []*Term{NewTerm(name)}, + } + if len(args) == 0 { + return head + } + head.Key = args[0] + if len(args) == 1 { + return head + } + head.Value = args[1] + if head.Key != nil && head.Value != nil { + head.Reference = head.Reference.Append(args[0]) + } + return head +} + +// VarHead creates a head object, initializes its Name and Location and returns the new head. +// NOTE: The JSON options argument is no longer used, and kept only for backwards compatibility. +func VarHead(name Var, location *Location, _ *astJSON.Options) *Head { + h := NewHead(name) + h.Reference[0].Location = location + return h +} + +// RefHead returns a new Head object with the passed Ref. If args are provided, +// the first will be used for the value. +func RefHead(ref Ref, args ...*Term) *Head { + head := &Head{} + head.SetRef(ref) + if len(ref) < 2 { + head.Name = ref[0].Value.(Var) + } + if len(args) >= 1 { + head.Value = args[0] + } + return head +} + +// DocKind represents the collection of document types that can be produced by rules. +type DocKind byte + +const ( + // CompleteDoc represents a document that is completely defined by the rule. + CompleteDoc = iota + + // PartialSetDoc represents a set document that is partially defined by the rule. + PartialSetDoc + + // PartialObjectDoc represents an object document that is partially defined by the rule. + PartialObjectDoc +) // TODO(sr): Deprecate? + +// DocKind returns the type of document produced by this rule. +func (head *Head) DocKind() DocKind { + if head.Key != nil { + if head.Value != nil { + return PartialObjectDoc + } + return PartialSetDoc + } else if head.HasDynamicRef() { + return PartialObjectDoc + } + return CompleteDoc +} + +type RuleKind byte + +const ( + SingleValue = iota + MultiValue +) + +// RuleKind returns the type of rule this is +func (head *Head) RuleKind() RuleKind { + // NOTE(sr): This is bit verbose, since the key is irrelevant for single vs + // multi value, but as good a spot as to assert the invariant. + switch { + case head.Value != nil: + return SingleValue + case head.Key != nil: + return MultiValue + default: + panic("unreachable") + } +} + +// Ref returns the Ref of the rule. If it doesn't have one, it's filled in +// via the Head's Name. +func (head *Head) Ref() Ref { + if len(head.Reference) > 0 { + return head.Reference + } + return Ref{&Term{Value: head.Name}} +} + +// SetRef can be used to set a rule head's Reference +func (head *Head) SetRef(r Ref) { + head.Reference = r +} + +// Compare returns an integer indicating whether head is less than, equal to, +// or greater than other. +func (head *Head) Compare(other *Head) int { + if head == nil { + if other == nil { + return 0 + } + return -1 + } else if other == nil { + return 1 + } + if head.Assign && !other.Assign { + return -1 + } else if !head.Assign && other.Assign { + return 1 + } + if cmp := Compare(head.Args, other.Args); cmp != 0 { + return cmp + } + if cmp := Compare(head.Reference, other.Reference); cmp != 0 { + return cmp + } + if cmp := VarCompare(head.Name, other.Name); cmp != 0 { + return cmp + } + if cmp := Compare(head.Key, other.Key); cmp != 0 { + return cmp + } + return Compare(head.Value, other.Value) +} + +// Copy returns a deep copy of head. +func (head *Head) Copy() *Head { + cpy := *head + cpy.Reference = head.Reference.Copy() + cpy.Args = head.Args.Copy() + cpy.Key = head.Key.Copy() + cpy.Value = head.Value.Copy() + cpy.keywords = nil + return &cpy +} + +// Equal returns true if this head equals other. +func (head *Head) Equal(other *Head) bool { + return head.Compare(other) == 0 +} + +func (head *Head) String() string { + return head.stringWithOpts(toStringOpts{}) +} + +func (head *Head) stringWithOpts(opts toStringOpts) string { + buf := strings.Builder{} + buf.WriteString(head.Ref().String()) + containsAdded := false + + switch { + case len(head.Args) != 0: + buf.WriteString(head.Args.String()) + case len(head.Reference) == 1 && head.Key != nil: + switch opts.RegoVersion() { + case RegoV0: + buf.WriteRune('[') + buf.WriteString(head.Key.String()) + buf.WriteRune(']') + default: + containsAdded = true + buf.WriteString(" contains ") + buf.WriteString(head.Key.String()) + } + } + if head.Value != nil { + if head.Assign { + buf.WriteString(" := ") + } else { + buf.WriteString(" = ") + } + buf.WriteString(head.Value.String()) + } else if !containsAdded && head.Name == "" && head.Key != nil { + buf.WriteString(" contains ") + buf.WriteString(head.Key.String()) + } + return buf.String() +} + +func (head *Head) MarshalJSON() ([]byte, error) { + var loc *Location + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Head && head.Location != nil { + loc = head.Location + } + + // NOTE(sr): we do this to override the rendering of `head.Reference`. + // It's still what'll be used via the default means of encoding/json + // for unmarshaling a json object into a Head struct! + type h Head + return json.Marshal(struct { + h + Ref Ref `json:"ref"` + Location *Location `json:"location,omitempty"` + }{ + h: h(*head), + Ref: head.Ref(), + Location: loc, + }) +} + +// Vars returns a set of vars found in the head. +func (head *Head) Vars() VarSet { + vis := NewVarVisitor() + // TODO: improve test coverage for this. + if head.Args != nil { + vis.WalkArgs(head.Args) + } + if head.Key != nil { + vis.Walk(head.Key) + } + if head.Value != nil { + vis.Walk(head.Value) + } + if len(head.Reference) > 0 { + vis.WalkRef(head.Reference[1:]) + } + return vis.vars +} + +// Loc returns the Location of head. +func (head *Head) Loc() *Location { + if head == nil { + return nil + } + return head.Location +} + +// SetLoc sets the location on head. +func (head *Head) SetLoc(loc *Location) { + head.Location = loc +} + +func (head *Head) HasDynamicRef() bool { + pos := head.Reference.Dynamic() + return pos > 0 && (pos < len(head.Reference)) +} + +// Copy returns a deep copy of a. +func (a Args) Copy() Args { + cpy := Args{} + for _, t := range a { + cpy = append(cpy, t.Copy()) + } + return cpy +} + +func (a Args) String() string { + buf := make([]string, 0, len(a)) + for _, t := range a { + buf = append(buf, t.String()) + } + return "(" + strings.Join(buf, ", ") + ")" +} + +// Loc returns the Location of a. +func (a Args) Loc() *Location { + if len(a) == 0 { + return nil + } + return a[0].Location +} + +// SetLoc sets the location on a. +func (a Args) SetLoc(loc *Location) { + if len(a) != 0 { + a[0].SetLocation(loc) + } +} + +// Vars returns a set of vars that appear in a. +func (a Args) Vars() VarSet { + vis := NewVarVisitor() + vis.WalkArgs(a) + return vis.vars +} + +// NewBody returns a new Body containing the given expressions. The indices of +// the immediate expressions will be reset. +func NewBody(exprs ...*Expr) Body { + for i, expr := range exprs { + expr.Index = i + } + return Body(exprs) +} + +// MarshalJSON returns JSON encoded bytes representing body. +func (body Body) MarshalJSON() ([]byte, error) { + // Serialize empty Body to empty array. This handles both the empty case and the + // nil case (whereas by default the result would be null if body was nil.) + if len(body) == 0 { + return []byte(`[]`), nil + } + ret, err := json.Marshal([]*Expr(body)) + return ret, err +} + +// Append adds the expr to the body and updates the expr's index accordingly. +func (body *Body) Append(expr *Expr) { + n := len(*body) + expr.Index = n + *body = append(*body, expr) +} + +// Set sets the expr in the body at the specified position and updates the +// expr's index accordingly. +func (body Body) Set(expr *Expr, pos int) { + body[pos] = expr + expr.Index = pos +} + +// Compare returns an integer indicating whether body is less than, equal to, +// or greater than other. +// +// If body is a subset of other, it is considered less than (and vice versa). +func (body Body) Compare(other Body) int { + minLen := min(len(other), len(body)) + for i := range minLen { + if cmp := body[i].Compare(other[i]); cmp != 0 { + return cmp + } + } + if len(body) < len(other) { + return -1 + } + if len(other) < len(body) { + return 1 + } + return 0 +} + +// Copy returns a deep copy of body. +func (body Body) Copy() Body { + cpy := make(Body, len(body)) + for i := range body { + cpy[i] = body[i].Copy() + } + return cpy +} + +// Contains returns true if this body contains the given expression. +func (body Body) Contains(x *Expr) bool { + return slices.ContainsFunc(body, x.Equal) +} + +// Equal returns true if this Body is equal to the other Body. +func (body Body) Equal(other Body) bool { + return body.Compare(other) == 0 +} + +// Hash returns the hash code for the Body. +func (body Body) Hash() int { + s := 0 + for _, e := range body { + s += e.Hash() + } + return s +} + +// IsGround returns true if all of the expressions in the Body are ground. +func (body Body) IsGround() bool { + for _, e := range body { + if !e.IsGround() { + return false + } + } + return true +} + +// Loc returns the location of the Body in the definition. +func (body Body) Loc() *Location { + if len(body) == 0 { + return nil + } + return body[0].Location +} + +// SetLoc sets the location on body. +func (body Body) SetLoc(loc *Location) { + if len(body) != 0 { + body[0].SetLocation(loc) + } +} + +func (body Body) String() string { + buf := make([]string, 0, len(body)) + for _, v := range body { + buf = append(buf, v.String()) + } + return strings.Join(buf, "; ") +} + +// Vars returns a VarSet containing variables in body. The params can be set to +// control which vars are included. +func (body Body) Vars(params VarVisitorParams) VarSet { + vis := NewVarVisitor().WithParams(params) + vis.WalkBody(body) + return vis.Vars() +} + +// NewExpr returns a new Expr object. +func NewExpr(terms any) *Expr { + switch terms.(type) { + case *SomeDecl, *Every, *Term, []*Term: // ok + default: + panic("unreachable") + } + return &Expr{ + Negated: false, + Terms: terms, + Index: 0, + With: nil, + } +} + +// Complement returns a copy of this expression with the negation flag flipped. +func (expr *Expr) Complement() *Expr { + cpy := *expr + cpy.Negated = !cpy.Negated + return &cpy +} + +// ComplementNoWith returns a copy of this expression with the negation flag flipped +// and the with modifier removed. This is the same as calling .Complement().NoWith() +// but without making an intermediate copy. +func (expr *Expr) ComplementNoWith() *Expr { + cpy := *expr + cpy.Negated = !cpy.Negated + cpy.With = nil + return &cpy +} + +// Equal returns true if this Expr equals the other Expr. +func (expr *Expr) Equal(other *Expr) bool { + return expr.Compare(other) == 0 +} + +// Compare returns an integer indicating whether expr is less than, equal to, +// or greater than other. +// +// Expressions are compared as follows: +// +// 1. Declarations are always less than other expressions. +// 2. Preceding expression (by Index) is always less than the other expression. +// 3. Non-negated expressions are always less than negated expressions. +// 4. Single term expressions are always less than built-in expressions. +// +// Otherwise, the expression terms are compared normally. If both expressions +// have the same terms, the modifiers are compared. +func (expr *Expr) Compare(other *Expr) int { + + if expr == nil { + if other == nil { + return 0 + } + return -1 + } else if other == nil { + return 1 + } + + o1 := expr.sortOrder() + o2 := other.sortOrder() + if o1 < o2 { + return -1 + } else if o2 < o1 { + return 1 + } + + switch { + case expr.Index < other.Index: + return -1 + case expr.Index > other.Index: + return 1 + } + + switch { + case expr.Negated && !other.Negated: + return 1 + case !expr.Negated && other.Negated: + return -1 + } + + switch t := expr.Terms.(type) { + case *Term: + if cmp := Compare(t.Value, other.Terms.(*Term).Value); cmp != 0 { + return cmp + } + case []*Term: + if cmp := termSliceCompare(t, other.Terms.([]*Term)); cmp != 0 { + return cmp + } + case *SomeDecl: + if cmp := Compare(t, other.Terms.(*SomeDecl)); cmp != 0 { + return cmp + } + case *Every: + if cmp := Compare(t, other.Terms.(*Every)); cmp != 0 { + return cmp + } + } + + return withSliceCompare(expr.With, other.With) +} + +func (expr *Expr) sortOrder() int { + switch expr.Terms.(type) { + case *SomeDecl: + return 0 + case *Term: + return 1 + case []*Term: + return 2 + case *Every: + return 3 + } + return -1 +} + +// CopyWithoutTerms returns a deep copy of expr without its Terms +func (expr *Expr) CopyWithoutTerms() *Expr { + cpy := *expr + + if expr.With != nil { + cpy.With = make([]*With, len(expr.With)) + for i := range expr.With { + cpy.With[i] = expr.With[i].Copy() + } + } + + return &cpy +} + +// Copy returns a deep copy of expr. +func (expr *Expr) Copy() *Expr { + + cpy := expr.CopyWithoutTerms() + + switch ts := expr.Terms.(type) { + case *SomeDecl: + cpy.Terms = ts.Copy() + case []*Term: + cpy.Terms = termSliceCopy(ts) + case *Term: + cpy.Terms = ts.Copy() + case *Every: + cpy.Terms = ts.Copy() + } + + return cpy +} + +// Hash returns the hash code of the Expr. +func (expr *Expr) Hash() int { + s := expr.Index + switch ts := expr.Terms.(type) { + case *SomeDecl: + s += ts.Hash() + case []*Term: + for _, t := range ts { + s += t.Value.Hash() + } + case *Term: + s += ts.Value.Hash() + } + if expr.Negated { + s++ + } + for _, w := range expr.With { + s += w.Hash() + } + return s +} + +// IncludeWith returns a copy of expr with the with modifier appended. +func (expr *Expr) IncludeWith(target *Term, value *Term) *Expr { + cpy := *expr + cpy.With = append(cpy.With, &With{Target: target, Value: value}) + return &cpy +} + +// NoWith returns a copy of expr where the with modifier has been removed. +func (expr *Expr) NoWith() *Expr { + cpy := *expr + cpy.With = nil + return &cpy +} + +// IsEquality returns true if this is an equality expression. +func (expr *Expr) IsEquality() bool { + return isGlobalBuiltin(expr, Var(Equality.Name)) +} + +// IsAssignment returns true if this an assignment expression. +func (expr *Expr) IsAssignment() bool { + return isGlobalBuiltin(expr, Var(Assign.Name)) +} + +// IsCall returns true if this expression calls a function. +func (expr *Expr) IsCall() bool { + _, ok := expr.Terms.([]*Term) + return ok +} + +// IsEvery returns true if this expression is an 'every' expression. +func (expr *Expr) IsEvery() bool { + _, ok := expr.Terms.(*Every) + return ok +} + +// IsSome returns true if this expression is a 'some' expression. +func (expr *Expr) IsSome() bool { + _, ok := expr.Terms.(*SomeDecl) + return ok +} + +// Operator returns the name of the function or built-in this expression refers +// to. If this expression is not a function call, returns nil. +func (expr *Expr) Operator() Ref { + op := expr.OperatorTerm() + if op == nil { + return nil + } + return op.Value.(Ref) +} + +// OperatorTerm returns the name of the function or built-in this expression +// refers to. If this expression is not a function call, returns nil. +func (expr *Expr) OperatorTerm() *Term { + terms, ok := expr.Terms.([]*Term) + if !ok || len(terms) == 0 { + return nil + } + return terms[0] +} + +// Operand returns the term at the zero-based pos. If the expr does not include +// at least pos+1 terms, this function returns nil. +func (expr *Expr) Operand(pos int) *Term { + terms, ok := expr.Terms.([]*Term) + if !ok { + return nil + } + idx := pos + 1 + if idx < len(terms) { + return terms[idx] + } + return nil +} + +// Operands returns the built-in function operands. +func (expr *Expr) Operands() []*Term { + terms, ok := expr.Terms.([]*Term) + if !ok { + return nil + } + return terms[1:] +} + +// IsGround returns true if all of the expression terms are ground. +func (expr *Expr) IsGround() bool { + switch ts := expr.Terms.(type) { + case []*Term: + for _, t := range ts[1:] { + if !t.IsGround() { + return false + } + } + case *Term: + return ts.IsGround() + } + return true +} + +// SetOperator sets the expr's operator and returns the expr itself. If expr is +// not a call expr, this function will panic. +func (expr *Expr) SetOperator(term *Term) *Expr { + expr.Terms.([]*Term)[0] = term + return expr +} + +// SetLocation sets the expr's location and returns the expr itself. +func (expr *Expr) SetLocation(loc *Location) *Expr { + expr.Location = loc + return expr +} + +// Loc returns the Location of expr. +func (expr *Expr) Loc() *Location { + if expr == nil { + return nil + } + return expr.Location +} + +// SetLoc sets the location on expr. +func (expr *Expr) SetLoc(loc *Location) { + expr.SetLocation(loc) +} + +func (expr *Expr) String() string { + buf := make([]string, 0, 2+len(expr.With)) + if expr.Negated { + buf = append(buf, "not") + } + switch t := expr.Terms.(type) { + case []*Term: + if expr.IsEquality() && validEqAssignArgCount(expr) { + buf = append(buf, fmt.Sprintf("%v %v %v", t[1], Equality.Infix, t[2])) + } else { + buf = append(buf, Call(t).String()) + } + case fmt.Stringer: + buf = append(buf, t.String()) + } + + for i := range expr.With { + buf = append(buf, expr.With[i].String()) + } + + return strings.Join(buf, " ") +} + +func (expr *Expr) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "terms": expr.Terms, + "index": expr.Index, + } + + if len(expr.With) > 0 { + data["with"] = expr.With + } + + if expr.Generated { + data["generated"] = true + } + + if expr.Negated { + data["negated"] = true + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Expr { + if expr.Location != nil { + data["location"] = expr.Location + } + } + + return json.Marshal(data) +} + +// UnmarshalJSON parses the byte array and stores the result in expr. +func (expr *Expr) UnmarshalJSON(bs []byte) error { + v := map[string]any{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + return unmarshalExpr(expr, v) +} + +// Vars returns a VarSet containing variables in expr. The params can be set to +// control which vars are included. +func (expr *Expr) Vars(params VarVisitorParams) VarSet { + vis := NewVarVisitor().WithParams(params) + vis.Walk(expr) + return vis.Vars() +} + +// NewBuiltinExpr creates a new Expr object with the supplied terms. +// The builtin operator must be the first term. +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 { + return "some " + call[1].String() + ", " + call[2].String() + " in " + call[3].String() + } + return "some " + call[1].String() + " in " + call[2].String() + } + buf := make([]string, len(d.Symbols)) + for i := range buf { + buf[i] = d.Symbols[i].String() + } + return "some " + strings.Join(buf, ", ") +} + +// SetLoc sets the Location on d. +func (d *SomeDecl) SetLoc(loc *Location) { + d.Location = loc +} + +// Loc returns the Location of d. +func (d *SomeDecl) Loc() *Location { + return d.Location +} + +// Copy returns a deep copy of d. +func (d *SomeDecl) Copy() *SomeDecl { + cpy := *d + cpy.Symbols = termSliceCopy(d.Symbols) + return &cpy +} + +// Compare returns an integer indicating whether d is less than, equal to, or +// greater than other. +func (d *SomeDecl) Compare(other *SomeDecl) int { + return termSliceCompare(d.Symbols, other.Symbols) +} + +// Hash returns a hash code of d. +func (d *SomeDecl) Hash() int { + return termSliceHash(d.Symbols) +} + +func (d *SomeDecl) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "symbols": d.Symbols, + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.SomeDecl { + if d.Location != nil { + data["location"] = d.Location + } + } + + return json.Marshal(data) +} + +func (q *Every) String() string { + if q.Key != nil { + return fmt.Sprintf("every %s, %s in %s { %s }", + q.Key, + q.Value, + q.Domain, + q.Body) + } + return fmt.Sprintf("every %s in %s { %s }", + q.Value, + q.Domain, + q.Body) +} + +func (q *Every) Loc() *Location { + return q.Location +} + +func (q *Every) SetLoc(l *Location) { + q.Location = l +} + +// Copy returns a deep copy of d. +func (q *Every) Copy() *Every { + cpy := *q + cpy.Key = q.Key.Copy() + cpy.Value = q.Value.Copy() + cpy.Domain = q.Domain.Copy() + cpy.Body = q.Body.Copy() + return &cpy +} + +func (q *Every) Compare(other *Every) int { + for _, terms := range [][2]*Term{ + {q.Key, other.Key}, + {q.Value, other.Value}, + {q.Domain, other.Domain}, + } { + if d := Compare(terms[0], terms[1]); d != 0 { + return d + } + } + return q.Body.Compare(other.Body) +} + +// KeyValueVars returns the key and val arguments of an `every` +// expression, if they are non-nil and not wildcards. +func (q *Every) KeyValueVars() VarSet { + vis := NewVarVisitor() + if q.Key != nil { + vis.Walk(q.Key) + } + vis.Walk(q.Value) + return vis.vars +} + +func (q *Every) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "key": q.Key, + "value": q.Value, + "domain": q.Domain, + "body": q.Body, + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Every { + if q.Location != nil { + data["location"] = q.Location + } + } + + return json.Marshal(data) +} + +func (w *With) String() string { + return "with " + w.Target.String() + " as " + w.Value.String() +} + +// Equal returns true if this With is equals the other With. +func (w *With) Equal(other *With) bool { + return Compare(w, other) == 0 +} + +// Compare returns an integer indicating whether w is less than, equal to, or +// greater than other. +func (w *With) Compare(other *With) int { + if w == nil { + if other == nil { + return 0 + } + return -1 + } else if other == nil { + return 1 + } + if cmp := Compare(w.Target, other.Target); cmp != 0 { + return cmp + } + return Compare(w.Value, other.Value) +} + +// Copy returns a deep copy of w. +func (w *With) Copy() *With { + cpy := *w + cpy.Value = w.Value.Copy() + cpy.Target = w.Target.Copy() + return &cpy +} + +// Hash returns the hash code of the With. +func (w With) Hash() int { + return w.Target.Hash() + w.Value.Hash() +} + +// SetLocation sets the location on w. +func (w *With) SetLocation(loc *Location) *With { + w.Location = loc + return w +} + +// Loc returns the Location of w. +func (w *With) Loc() *Location { + if w == nil { + return nil + } + return w.Location +} + +// SetLoc sets the location on w. +func (w *With) SetLoc(loc *Location) { + w.Location = loc +} + +func (w *With) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "target": w.Target, + "value": w.Value, + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.With { + if w.Location != nil { + data["location"] = w.Location + } + } + + return json.Marshal(data) +} + +// Copy returns a deep copy of the AST node x. If x is not an AST node, x is returned unmodified. +func Copy(x any) any { + switch x := x.(type) { + case *Module: + return x.Copy() + case *Package: + return x.Copy() + case *Import: + return x.Copy() + case *Rule: + return x.Copy() + case *Head: + return x.Copy() + case Args: + return x.Copy() + case Body: + return x.Copy() + case *Expr: + return x.Copy() + case *With: + return x.Copy() + case *SomeDecl: + return x.Copy() + case *Every: + return x.Copy() + case *Term: + return x.Copy() + case *ArrayComprehension: + return x.Copy() + case *SetComprehension: + return x.Copy() + case *ObjectComprehension: + return x.Copy() + case Set: + return x.Copy() + case *object: + return x.Copy() + case *Array: + return x.Copy() + case Ref: + return x.Copy() + case Call: + return x.Copy() + case *Comment: + return x.Copy() + } + return x +} + +// RuleSet represents a collection of rules that produce a virtual document. +type RuleSet []*Rule + +// NewRuleSet returns a new RuleSet containing the given rules. +func NewRuleSet(rules ...*Rule) RuleSet { + rs := make(RuleSet, 0, len(rules)) + for _, rule := range rules { + rs.Add(rule) + } + return rs +} + +// Add inserts the rule into rs. +func (rs *RuleSet) Add(rule *Rule) { + for _, exist := range *rs { + if exist.Equal(rule) { + return + } + } + *rs = append(*rs, rule) +} + +// Contains returns true if rs contains rule. +func (rs RuleSet) Contains(rule *Rule) bool { + for i := range rs { + if rs[i].Equal(rule) { + return true + } + } + return false +} + +// Diff returns a new RuleSet containing rules in rs that are not in other. +func (rs RuleSet) Diff(other RuleSet) RuleSet { + result := NewRuleSet() + for i := range rs { + if !other.Contains(rs[i]) { + result.Add(rs[i]) + } + } + return result +} + +// Equal returns true if rs equals other. +func (rs RuleSet) Equal(other RuleSet) bool { + return len(rs.Diff(other)) == 0 && len(other.Diff(rs)) == 0 +} + +// Merge returns a ruleset containing the union of rules from rs an other. +func (rs RuleSet) Merge(other RuleSet) RuleSet { + result := NewRuleSet() + for i := range rs { + result.Add(rs[i]) + } + for i := range other { + result.Add(other[i]) + } + return result +} + +func (rs RuleSet) String() string { + buf := make([]string, 0, len(rs)) + for _, rule := range rs { + buf = append(buf, rule.String()) + } + return "{" + strings.Join(buf, ", ") + "}" +} + +// Returns true if the equality or assignment expression referred to by expr +// has a valid number of arguments. +func validEqAssignArgCount(expr *Expr) bool { + return len(expr.Operands()) == 2 +} + +// this function checks if the expr refers to a non-namespaced (global) built-in +// function like eq, gt, plus, etc. +func isGlobalBuiltin(expr *Expr, name Var) bool { + terms, ok := expr.Terms.([]*Term) + if !ok { + return false + } + + // NOTE(tsandall): do not use Term#Equal or Value#Compare to avoid + // allocation here. + ref, ok := terms[0].Value.(Ref) + if !ok || len(ref) != 1 { + return false + } + if head, ok := ref[0].Value.(Var); ok { + return head.Equal(name) + } + return false +} diff --git a/third_party/opa/v1/ast/policy_test.go b/third_party/opa/v1/ast/policy_test.go new file mode 100644 index 000000000000..3a86c23669e3 --- /dev/null +++ b/third_party/opa/v1/ast/policy_test.go @@ -0,0 +1,1139 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "bytes" + "encoding/json" + "errors" + "net/url" + "testing" + + "github.com/open-policy-agent/opa/v1/ast/location" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestModuleJSONRoundTrip(t *testing.T) { + + mod, err := ParseModuleWithOpts("test.rego", `package a.b.c + +import rego.v1 +import data.x.y as z +import data.u.i + +p = [1, 2, {"foo": 3.14}] if { r[x] = 1; not q[x] } +r[y] = v if { i[1] = y; v = i[2] } +q contains x if { a = [true, false, null, {"x": [1, 2, 3]}]; a[i] = x } +t = true if { xs = [{"x": a[i].a} | a[i].n = "bob"; b[x]] } +big = 1e+1000 if { true } +odd = -0.1 if { true } +s = {1, 2, 3} if { true } +s = set() if { false } +empty_obj = true if { {} } +empty_arr = true if { [] } +empty_set = true if { set() } +using_with = true if { x = data.foo + 1 with input.foo as bar } +x = 2 if { input = null } +default allow = true +f(x) = y if { y = x } +a = true if { xs = {a: b | input.y[a] = "foo"; b = input.z["bar"]} } +b = true if { xs = {{"x": a[i].a} | a[i].n = "bob"; b[x]} } +call_values if { f(x) != g(x) } +assigned := 1 +rule.having.ref.head[1] = x if x := 2 + +# METADATA +# scope: rule +metadata := 7 +`, ParserOptions{ProcessAnnotation: true}) + + if err != nil { + t.Fatal(err) + } + + bs, err := json.Marshal(mod) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + roundtrip := &Module{} + + err = util.UnmarshalJSON(bs, roundtrip) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if !roundtrip.Equal(mod) { + t.Fatalf("Expected roundtripped module to be equal to original:\nExpected:\n\n%v\n\nGot:\n\n%v\n", mod, roundtrip) + } + + if mod.Rules[3].Ref().GroundPrefix().String() != "data.a.b.c.t" { + t.Fatal("expected path data.a.b.c.t for 4th rule in module but got:", mod.Rules[3].Ref().GroundPrefix()) + } + + if len(roundtrip.Annotations) != 1 { + t.Fatal("expected exactly one annotation") + } +} + +func TestBodyEmptyJSON(t *testing.T) { + var body Body + bs := util.MustMarshalJSON(body) + if string(bs) != "[]" { + t.Fatalf("Unexpected JSON value for empty body") + } + body = Body{} + bs = util.MustMarshalJSON(body) + if string(bs) != "[]" { + t.Fatalf("Unexpected JSON value for empty body") + } +} + +func TestPackageEquals(t *testing.T) { + pkg1 := &Package{Path: RefTerm(VarTerm("foo"), StringTerm("bar"), StringTerm("baz")).Value.(Ref)} + pkg2 := &Package{Path: RefTerm(VarTerm("foo"), StringTerm("bar"), StringTerm("baz")).Value.(Ref)} + pkg3 := &Package{Path: RefTerm(VarTerm("foo"), StringTerm("qux"), StringTerm("baz")).Value.(Ref)} + assertPackagesEqual(t, pkg1, pkg1) + assertPackagesEqual(t, pkg1, pkg2) + assertPackagesNotEqual(t, pkg1, pkg3) + assertPackagesNotEqual(t, pkg2, pkg3) +} + +func TestPackageString(t *testing.T) { + pkg1 := &Package{Path: RefTerm(VarTerm("foo"), StringTerm("bar"), StringTerm("baz")).Value.(Ref)} + result1 := pkg1.String() + expected1 := `package bar.baz` + if result1 != expected1 { + t.Errorf("Expected %v but got %v", expected1, result1) + } + + var nilPkg *Package + + expNil := "" + if nilPkg.String() != expNil { + t.Fatal("Unexpected package repr:", nilPkg.String()) + } + + badPathPkg := &Package{ + Path: RefTerm(VarTerm("x")).Value.(Ref), + } + + expBadPath := "package " + if badPathPkg.String() != expBadPath { + t.Fatal("Unexpected package repr:", badPathPkg.String()) + } + +} + +func TestImportEquals(t *testing.T) { + imp1 := &Import{Path: VarTerm("foo"), Alias: Var("bar")} + imp11 := &Import{Path: VarTerm("foo"), Alias: Var("bar")} + imp2 := &Import{Path: VarTerm("foo")} + imp3 := &Import{Path: RefTerm(VarTerm("bar"), VarTerm("baz"), VarTerm("qux")), Alias: Var("corge")} + imp33 := &Import{Path: RefTerm(VarTerm("bar"), VarTerm("baz"), VarTerm("qux")), Alias: Var("corge")} + imp4 := &Import{Path: RefTerm(VarTerm("bar"), VarTerm("baz"), VarTerm("qux"))} + assertImportsEqual(t, imp1, imp1) + assertImportsEqual(t, imp1, imp11) + assertImportsEqual(t, imp3, imp3) + assertImportsEqual(t, imp3, imp33) + imps := []*Import{imp1, imp2, imp3, imp4} + for i := range imps { + for j := range imps { + if i != j { + assertImportsNotEqual(t, imps[i], imps[j]) + } + } + } +} + +func TestImportName(t *testing.T) { + imp1 := &Import{Path: VarTerm("foo"), Alias: Var("bar")} + imp2 := &Import{Path: VarTerm("foo")} + imp3 := &Import{Path: RefTerm(VarTerm("bar"), StringTerm("baz"), StringTerm("qux")), Alias: Var("corge")} + imp4 := &Import{Path: RefTerm(VarTerm("bar"), StringTerm("baz"), StringTerm("qux"))} + imp5 := &Import{Path: DefaultRootDocument} + expected := []Var{ + "bar", + "foo", + "corge", + "qux", + "data", + } + tests := []*Import{ + imp1, imp2, imp3, imp4, imp5, + } + for i := range tests { + result := tests[i].Name() + if !result.Equal(expected[i]) { + t.Errorf("Expected %v but got: %v", expected[i], result) + } + } +} + +func TestImportString(t *testing.T) { + imp1 := &Import{Path: VarTerm("foo"), Alias: Var("bar")} + imp2 := &Import{Path: VarTerm("foo")} + imp3 := &Import{Path: RefTerm(VarTerm("bar"), StringTerm("baz"), StringTerm("qux")), Alias: Var("corge")} + imp4 := &Import{Path: RefTerm(VarTerm("bar"), StringTerm("baz"), StringTerm("qux"))} + assertImportToString(t, imp1, "import foo as bar") + assertImportToString(t, imp2, "import foo") + assertImportToString(t, imp3, "import bar.baz.qux as corge") + assertImportToString(t, imp4, "import bar.baz.qux") +} + +func TestExprEquals(t *testing.T) { + + // Scalars + expr1 := &Expr{Terms: BooleanTerm(true)} + expr2 := &Expr{Terms: BooleanTerm(true)} + expr3 := &Expr{Terms: StringTerm("true")} + assertExprEqual(t, expr1, expr2) + assertExprNotEqual(t, expr1, expr3) + + // Vars, refs, and composites + ref1 := RefTerm(VarTerm("foo"), StringTerm("bar"), VarTerm("i")) + ref2 := RefTerm(VarTerm("foo"), StringTerm("bar"), VarTerm("i")) + obj1 := ObjectTerm(Item(ref1, ArrayTerm(IntNumberTerm(1), NullTerm()))) + obj2 := ObjectTerm(Item(ref2, ArrayTerm(IntNumberTerm(1), NullTerm()))) + obj3 := ObjectTerm(Item(ref2, ArrayTerm(StringTerm("1"), NullTerm()))) + expr10 := &Expr{Terms: obj1} + expr11 := &Expr{Terms: obj2} + expr12 := &Expr{Terms: obj3} + assertExprEqual(t, expr10, expr11) + assertExprNotEqual(t, expr10, expr12) + + // Builtins and negation + expr20 := &Expr{ + Negated: true, + Terms: []*Term{StringTerm("="), VarTerm("x"), ref1}, + } + expr21 := &Expr{ + Negated: true, + Terms: []*Term{StringTerm("="), VarTerm("x"), ref1}, + } + expr22 := &Expr{ + Negated: false, + Terms: []*Term{StringTerm("="), VarTerm("x"), ref1}, + } + expr23 := &Expr{ + Negated: true, + Terms: []*Term{StringTerm("="), VarTerm("y"), ref1}, + } + assertExprEqual(t, expr20, expr21) + assertExprNotEqual(t, expr20, expr22) + assertExprNotEqual(t, expr20, expr23) + + // Modifiers + expr30 := &Expr{ + Terms: MustParseTerm("data.foo.bar"), + With: []*With{ + { + Target: MustParseTerm("input"), + Value: MustParseTerm("bar"), + }, + }, + } + + expr31 := &Expr{ + Terms: MustParseTerm("data.foo.bar"), + With: []*With{ + { + Target: MustParseTerm("input"), + Value: MustParseTerm("bar"), + }, + }, + } + + expr32 := &Expr{ + Terms: MustParseTerm("data.foo.bar"), + With: []*With{ + { + Target: MustParseTerm("input.foo"), + Value: MustParseTerm("baz"), + }, + }, + } + + assertExprEqual(t, expr30, expr31) + assertExprNotEqual(t, expr30, expr32) +} + +func TestBodyIsGround(t *testing.T) { + if MustParseBody(`a.b[0] = 1; a = [1, 2, x]`).IsGround() { + t.Errorf("Expected body to be non-ground") + } +} + +func TestExprString(t *testing.T) { + expr1 := &Expr{ + Terms: RefTerm(VarTerm("q"), StringTerm("r"), VarTerm("x")), + } + expr2 := &Expr{ + Negated: true, + Terms: RefTerm(VarTerm("q"), StringTerm("r"), VarTerm("x")), + } + expr3 := Equality.Expr(StringTerm("a"), FloatNumberTerm(17.1)) + expr4 := NotEqual.Expr( + ObjectTerm(Item(VarTerm("foo"), ArrayTerm( + IntNumberTerm(1), RefTerm(VarTerm("a"), StringTerm("b")), + ))), + BooleanTerm(false), + ) + expr5 := &Expr{ + Terms: BooleanTerm(true), + With: []*With{ + { + Target: VarTerm("foo"), + Value: VarTerm("bar"), + }, + { + Target: VarTerm("baz"), + Value: VarTerm("qux"), + }, + }, + } + expr6 := Plus.Expr( + IntNumberTerm(1), + IntNumberTerm(2), + IntNumberTerm(3), + ) + expr7 := Count.Expr( + StringTerm("foo"), + VarTerm("x"), + ) + expr8 := &Expr{ + Terms: []*Term{ + RefTerm(VarTerm("data"), StringTerm("test"), StringTerm("f")), + IntNumberTerm(1), + VarTerm("x"), + }, + } + expr9 := Contains.Expr(StringTerm("foo.bar"), StringTerm(".")) + expr10 := Member.Expr(StringTerm("foo"), VarTerm("xs")) + expr11 := MemberWithKey.Expr(VarTerm("x"), StringTerm("foo"), VarTerm("xs")) + assertExprString(t, expr1, "q.r[x]") + assertExprString(t, expr2, "not q.r[x]") + assertExprString(t, expr3, "\"a\" = 17.1") + assertExprString(t, expr4, "neq({foo: [1, a.b]}, false)") + assertExprString(t, expr5, "true with foo as bar with baz as qux") + assertExprString(t, expr6, "plus(1, 2, 3)") + assertExprString(t, expr7, "count(\"foo\", x)") + assertExprString(t, expr8, "data.test.f(1, x)") + assertExprString(t, expr9, `contains("foo.bar", ".")`) + assertExprString(t, expr10, `internal.member_2("foo", xs)`) + assertExprString(t, expr11, `internal.member_3(x, "foo", xs)`) +} + +func TestExprBadJSON(t *testing.T) { + + assert := func(js string, exp error) { + expr := Expr{} + err := util.UnmarshalJSON([]byte(js), &expr) + if exp.Error() != err.Error() { + t.Errorf("For %v Expected %v but got: %v", js, exp, err) + } + } + + js := ` + { + "negated": 100, + "terms": { + "value": "foo", + "type": "string" + }, + "index": 0 + } + ` + + exp := errors.New("ast: unable to unmarshal negated field with type: json.Number (expected true or false)") + assert(js, exp) + + js = ` + { + "terms": [ + "foo" + ], + "index": 0 + } + ` + exp = errors.New("ast: unable to unmarshal term") + assert(js, exp) + + js = ` + { + "terms": "bad value", + "index": 0 + } + ` + exp = errors.New(`ast: unable to unmarshal terms field with type: string (expected {"value": ..., "type": ...} or [{"value": ..., "type": ...}, ...])`) + assert(js, exp) + + js = ` + { + "terms": {"value": "foo", "type": "string"} + }` + exp = errors.New("ast: unable to unmarshal index field with type: (expected integer)") + assert(js, exp) +} + +func TestExprEveryCopy(t *testing.T) { + opts := ParserOptions{AllFutureKeywords: true} + newEvery := func() *Expr { + return MustParseBodyWithOpts( + `every k, v in [1,2,3] { true }`, opts, + )[0] + } + e0 := newEvery() + e1 := e0.Copy() + e1.Terms.(*Every).Body = NewBody(NewExpr(BooleanTerm(false))) + if exp := newEvery(); exp.Compare(e0) != 0 { + t.Errorf("expected e0 unchanged (%v), found %v", exp, e0) + } +} + +func TestRuleHeadJSON(t *testing.T) { + // NOTE(sr): we may get to see Rule objects that aren't the result of parsing, but + // fed as-is into the compiler. We need to be able to make sense of their refs, too. + head := Head{ + Name: Var("allow"), + } + + rule := Rule{ + Head: &head, + } + bs, err := json.Marshal(&rule) + if err != nil { + t.Fatal(err) + } + if exp, act := `{"body":[],"head":{"name":"allow","ref":[{"type":"var","value":"allow"}]}}`, string(bs); act != exp { + t.Errorf("expected %q, got %q", exp, act) + } + + var readRule Rule + if err := json.Unmarshal(bs, &readRule); err != nil { + t.Fatal(err) + } + if exp, act := 1, len(readRule.Head.Reference); act != exp { + t.Errorf("expected unmarshalled rule to have Reference, got %v", readRule.Head.Reference) + } + bs0, err := json.Marshal(&readRule) + if err != nil { + t.Fatal(err) + } + if exp, act := string(bs), string(bs0); exp != act { + t.Errorf("expected json repr to match %q, got %q", exp, act) + } + + var readAgainRule Rule + if err := json.Unmarshal(bs, &readAgainRule); err != nil { + t.Fatal(err) + } + if !readAgainRule.Equal(&readRule) { + t.Errorf("expected roundtripped rule reference to match %v, got %v", readRule.Head.Reference, readAgainRule.Head.Reference) + } +} + +func TestRuleHeadEquals(t *testing.T) { + assertHeadsEqual(t, &Head{}, &Head{}) + + // Same name/ref/key/value + assertHeadsEqual(t, &Head{Name: Var("p")}, &Head{Name: Var("p")}) + assertHeadsEqual(t, &Head{Reference: Ref{VarTerm("p"), StringTerm("r")}}, &Head{Reference: Ref{VarTerm("p"), StringTerm("r")}}) // TODO: string for first section + assertHeadsEqual(t, &Head{Key: VarTerm("x")}, &Head{Key: VarTerm("x")}) + assertHeadsEqual(t, &Head{Value: VarTerm("x")}, &Head{Value: VarTerm("x")}) + assertHeadsEqual(t, &Head{Args: []*Term{VarTerm("x"), VarTerm("y")}}, &Head{Args: []*Term{VarTerm("x"), VarTerm("y")}}) + + // Different name/ref/key/value + assertHeadsNotEqual(t, &Head{Name: Var("p")}, &Head{Name: Var("q")}) + assertHeadsNotEqual(t, &Head{Reference: Ref{VarTerm("p")}}, &Head{Reference: Ref{VarTerm("q")}}) // TODO: string for first section + assertHeadsNotEqual(t, &Head{Key: VarTerm("x")}, &Head{Key: VarTerm("y")}) + assertHeadsNotEqual(t, &Head{Value: VarTerm("x")}, &Head{Value: VarTerm("y")}) + assertHeadsNotEqual(t, &Head{Args: []*Term{VarTerm("x"), VarTerm("z")}}, &Head{Args: []*Term{VarTerm("x"), VarTerm("y")}}) +} + +func TestRuleBodyEquals(t *testing.T) { + + true1 := &Expr{Terms: []*Term{BooleanTerm(true)}} + true2 := &Expr{Terms: []*Term{BooleanTerm(true)}} + false1 := &Expr{Terms: []*Term{BooleanTerm(false)}} + head := NewHead(Var("p")) + + ruleTrue1 := &Rule{Head: head, Body: NewBody(true1)} + ruleTrue12 := &Rule{Head: head, Body: NewBody(true1, true2)} + ruleTrue2 := &Rule{Head: head, Body: NewBody(true2)} + ruleTrue12_2 := &Rule{Head: head, Body: NewBody(true1, true2)} + ruleFalse1 := &Rule{Head: head, Body: NewBody(false1)} + ruleTrueFalse := &Rule{Head: head, Body: NewBody(true1, false1)} + ruleFalseTrue := &Rule{Head: head, Body: NewBody(false1, true1)} + + // Same expressions + assertRulesEqual(t, ruleTrue1, ruleTrue2) + assertRulesEqual(t, ruleTrue12, ruleTrue12_2) + + // Different expressions/different order + assertRulesNotEqual(t, ruleTrue1, ruleFalse1) + assertRulesNotEqual(t, ruleTrueFalse, ruleFalseTrue) + + // Assigned versus not. + assigned := ruleTrue1.Copy() + assigned.Head.Assign = true + assertRulesNotEqual(t, ruleTrue1, assigned) +} + +func TestRuleString(t *testing.T) { + trueBody := NewBody(NewExpr(BooleanTerm(true))) + + tests := []struct { + rule *Rule + expV0 string + expV1 string + }{ + { + rule: &Rule{ + Head: NewHead(Var("p"), nil, BooleanTerm(true)), + Body: NewBody( + Equality.Expr(StringTerm("foo"), StringTerm("bar")), + ), + }, + expV0: `p = true { "foo" = "bar" }`, + expV1: `p = true if { "foo" = "bar" }`, + }, + { + rule: &Rule{ + Head: NewHead(Var("p"), VarTerm("x")), + Body: trueBody, + }, + expV0: `p[x] { true }`, + expV1: `p contains x if { true }`, + }, + { + rule: &Rule{ + Head: RefHead(MustParseRef("p[x]"), BooleanTerm(true)), + Body: MustParseBody("x = 1"), + }, + expV0: `p[x] = true { x = 1 }`, + expV1: `p[x] = true if { x = 1 }`, + }, + { + rule: &Rule{ + Head: RefHead(MustParseRef("p.q.r[x]"), BooleanTerm(true)), + Body: MustParseBody("x = 1"), + }, + expV0: `p.q.r[x] = true { x = 1 }`, + expV1: `p.q.r[x] = true if { x = 1 }`, + }, + { + rule: &Rule{ + Head: &Head{ + Reference: MustParseRef("p.q.r"), + Key: VarTerm("1"), + }, + Body: MustParseBody("x = 1"), + }, + expV0: `p.q.r contains 1 { x = 1 }`, + expV1: `p.q.r contains 1 if { x = 1 }`, + }, + { + rule: &Rule{ + Head: NewHead(Var("p"), VarTerm("x"), VarTerm("y")), + Body: NewBody( + Equality.Expr(StringTerm("foo"), VarTerm("x")), + &Expr{ + Negated: true, + Terms: RefTerm(VarTerm("a"), StringTerm("b"), VarTerm("x")), + }, + Equality.Expr(StringTerm("b"), VarTerm("y")), + ), + }, + expV0: `p[x] = y { "foo" = x; not a.b[x]; "b" = y }`, + expV1: `p[x] = y if { "foo" = x; not a.b[x]; "b" = y }`, + }, + { + rule: &Rule{ + Default: true, + Head: NewHead("p", nil, BooleanTerm(true)), + }, + expV0: `default p = true`, + expV1: `default p = true`, + }, + { + rule: &Rule{ + Head: &Head{ + Name: Var("f"), + Args: Args{VarTerm("x"), VarTerm("y")}, + Value: VarTerm("z"), + }, + Body: NewBody(Plus.Expr(VarTerm("x"), VarTerm("y"), VarTerm("z"))), + }, + expV0: "f(x, y) = z { plus(x, y, z) }", + expV1: "f(x, y) = z if { plus(x, y, z) }", + }, + { + rule: &Rule{ + Head: &Head{ + Name: Var("p"), + Value: BooleanTerm(true), + Assign: true, + }, + Body: NewBody( + Equality.Expr(StringTerm("foo"), StringTerm("bar")), + ), + }, + expV0: `p := true { "foo" = "bar" }`, + expV1: `p := true if { "foo" = "bar" }`, + }, + { + rule: &Rule{ + Head: RefHead(MustParseRef("p.q.r")), + Body: trueBody, + }, + expV0: `p.q.r { true }`, + expV1: `p.q.r if { true }`, + }, + { + rule: &Rule{ + Head: RefHead(MustParseRef("p.q.r"), StringTerm("foo")), + Body: trueBody, + }, + expV0: `p.q.r = "foo" { true }`, + expV1: `p.q.r = "foo" if { true }`, + }, + { + rule: &Rule{ + Head: RefHead(MustParseRef("p.q.r[x]"), StringTerm("foo")), + Body: MustParseBody(`x := 1`), + }, + expV0: `p.q.r[x] = "foo" { assign(x, 1) }`, + expV1: `p.q.r[x] = "foo" if { assign(x, 1) }`, + }, + } + + for _, tc := range tests { + t.Run(tc.expV0, func(t *testing.T) { + assertRuleString(t, tc.rule, tc.expV0, toStringOpts{regoVersion: RegoV0}) + assertRuleString(t, tc.rule, tc.expV1, toStringOpts{regoVersion: RegoV1}) + + switch DefaultRegoVersion { + case RegoV0: + assertRuleString(t, tc.rule, tc.expV0, toStringOpts{}) + case RegoV1: + assertRuleString(t, tc.rule, tc.expV1, toStringOpts{}) + } + }) + } +} + +func TestRuleString_DefaultRegoVersion(t *testing.T) { + // ast.Rule.String() will respect the rego-version of the ast.Module it is part of. + + tests := []struct { + note string + module string + regoVersion RegoVersion + exp string + }{ + { + note: "v0", + regoVersion: RegoV0, + module: `package a.b.c + +p[x] { x = "a" }`, + exp: `p[x] { x = "a" }`, + }, + { + note: "v1", + regoVersion: RegoV1, + module: `package a.b.c + +p contains x if { x = "a" }`, + exp: `p contains x if { x = "a" }`, + }, + { + note: "default rego-version", + module: `package a.b.c + +p contains x if { x = "a" }`, + exp: `p contains x if { x = "a" }`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var mod *Module + + if tc.regoVersion == RegoUndefined { + mod = MustParseModule(tc.module) + } else { + mod = MustParseModuleWithOpts(tc.module, ParserOptions{RegoVersion: tc.regoVersion}) + } + + rule := mod.Rules[0] + act := rule.String() + + if act != tc.exp { + t.Fatalf("Expected:\n\n%s\n\nbut got:\n\n%s", tc.exp, act) + } + }) + } +} + +func TestRulePath(t *testing.T) { + ruleWithMod := func(r string) Ref { + mod := module("package pkg\n" + r) + return mod.Rules[0].Ref().GroundPrefix() + } + if exp, act := MustParseRef("data.pkg.p.q.r"), ruleWithMod("p.q.r if { true }"); !exp.Equal(act) { + t.Errorf("expected %v, got %v", exp, act) + } + + if exp, act := MustParseRef("data.pkg.p"), ruleWithMod("p if { true }"); !exp.Equal(act) { + t.Errorf("expected %v, got %v", exp, act) + } +} + +func TestModuleString(t *testing.T) { + + // v1 module + input := `package a.b.c + +import data.foo.bar +import input.xyz + +p = true if { not bar } +q = true if { xyz.abc = 2 } +wildcard = true if { bar[_] = 1 }` + + mod := MustParseModule(input) + + roundtrip, err := ParseModule("", mod.String()) + if err != nil { + t.Fatalf("Unexpected error while parsing roundtripped module: %v", err) + } + + if !roundtrip.Equal(mod) { + t.Fatalf("Expected roundtripped to equal original but:\n\nExpected:\n\n%v\n\nDoes not equal result:\n\n%v", mod, roundtrip) + } +} + +func TestModuleCopy(t *testing.T) { + + input := `package foo + + # comment + p := 7` + + mod := MustParseModule(input) + cpy := mod.Copy() + cpy.Comments[0].Text[0] = 'X' + + if !bytes.Equal(mod.Comments[0].Text, []byte(" comment")) { + t.Fatal("expected comment text to be unchanged") + } +} + +func TestWithString(t *testing.T) { + + with1 := &With{ + Target: VarTerm("foo"), + Value: VarTerm("bar"), + } + + result := with1.String() + expected := "with foo as bar" + if result != expected { + t.Fatalf("Expected %v but got %v", expected, result) + } + + with2 := &With{ + Target: MustParseTerm("com.example.input"), + Value: MustParseTerm(`{[1,2,3], {"x": y}}`), + } + + result = with2.String() + expected = `with com.example.input as {[1, 2, 3], {"x": y}}` + + if result != expected { + t.Fatalf("Expected %v but got %v", expected, result) + } +} + +func TestSomeDeclString(t *testing.T) { + + decl := &SomeDecl{ + Symbols: []*Term{ + VarTerm("a"), + VarTerm("b"), + }, + } + + result := decl.String() + expected := "some a, b" + + if result != expected { + t.Errorf("Expected %v but got %v", expected, result) + } + + s := &SomeDecl{ + Symbols: []*Term{Member.Call(VarTerm("x"), VarTerm("xs"))}, + } + if exp, act := "some x in xs", s.String(); act != exp { + t.Errorf("Expected %v but got %v", exp, act) + } + + s1 := &SomeDecl{ + Symbols: []*Term{Member.Call(VarTerm("x"), VarTerm("y"), VarTerm("xs"))}, + } + if exp, act := "some x, y in xs", s1.String(); act != exp { + t.Errorf("Expected %v but got %v", exp, act) + } +} + +func TestEveryString(t *testing.T) { + tests := []struct { + every Every + exp string + }{ + { + exp: `every x in ["foo", "bar"] { true; true }`, + every: Every{ + Value: VarTerm("x"), + Domain: ArrayTerm(StringTerm("foo"), StringTerm("bar")), + Body: []*Expr{ + { + Terms: BooleanTerm(true), + }, + { + Terms: BooleanTerm(true), + }, + }, + }, + }, + { + exp: `every k, v in ["foo", "bar"] { true; true }`, + every: Every{ + Key: VarTerm("k"), + Value: VarTerm("v"), + Domain: ArrayTerm(StringTerm("foo"), StringTerm("bar")), + Body: []*Expr{ + { + Terms: BooleanTerm(true), + }, + { + Terms: BooleanTerm(true), + }, + }, + }, + }, + } + for _, tc := range tests { + if act := tc.every.String(); act != tc.exp { + t.Errorf("expected %q, got %q", tc.exp, act) + } + } +} + +func TestAnnotationsString(t *testing.T) { + a := &Annotations{ + Scope: "foo", + Title: "bar", + Description: "baz", + Authors: []*AuthorAnnotation{ + { + Name: "John Doe", + Email: "john@example.com", + }, + { + Name: "Jane Doe", + }, + }, + Organizations: []string{"mi", "fa"}, + RelatedResources: []*RelatedResourceAnnotation{ + { + Ref: mustParseURL("https://example.com"), + }, + { + Ref: mustParseURL("https://example.com/2"), + Description: "Some resource", + }, + }, + Schemas: []*SchemaAnnotation{ + { + Path: MustParseRef("data.bar"), + Schema: MustParseRef("schema.baz"), + }, + }, + Custom: map[string]any{ + "list": []int{ + 1, 2, 3, + }, + "map": map[string]any{ + "one": 1, + "two": map[int]any{ + 3: "three", + }, + }, + "flag": true, + }, + } + + // NOTE(tsandall): for now, annotations are represented as JSON objects + // which are a subset of YAML. We could improve this in the future. + exp := `{"authors":[{"name":"John Doe","email":"john@example.com"},{"name":"Jane Doe"}],"custom":{"flag":true,"list":[1,2,3],"map":{"one":1,"two":{"3":"three"}}},"description":"baz","organizations":["mi","fa"],"related_resources":[{"ref":"https://example.com"},{"description":"Some resource","ref":"https://example.com/2"}],"schemas":[{"path":[{"type":"var","value":"data"},{"type":"string","value":"bar"}],"schema":[{"type":"var","value":"schema"},{"type":"string","value":"baz"}]}],"scope":"foo","title":"bar"}` + + if got := a.String(); exp != got { + t.Fatalf("expected\n%s\nbut got\n%s", exp, got) + } +} + +func mustParseURL(str string) url.URL { + parsed, err := url.Parse(str) + if err != nil { + panic(err) + } + return *parsed +} + +func TestModuleStringAnnotations(t *testing.T) { + module, err := ParseModuleWithOpts("test.rego", `package test +import rego.v1 + +# METADATA +# scope: rule +p := 7`, ParserOptions{ProcessAnnotation: true}) + + if err != nil { + t.Fatal(err) + } + + exp := `package test + +import rego.v1 + +# METADATA +# {"scope":"rule"} +p := 7 if { true }` + + if module.String() != exp { + t.Fatalf("expected %q but got %q", exp, module.String()) + } +} + +func TestModuleStringWithRegoVersion(t *testing.T) { + tests := []struct { + note string + regoVersion RegoVersion + module string + exp string + }{ + { + note: "v0, basic", + regoVersion: RegoV0, + module: `package test +a := 1 +b[1] +c[1] := 2 +d.e.f := 3 +e.f.g[1] +f.g.h[1] := 4 + +g := 5 { + false +} else := 6 { + false +} else := 7`, + exp: `package test + +a := 1 { true } +b[1] { true } +c[1] := 2 { true } +d.e.f := 3 { true } +e.f.g[1] = true { true } +f.g.h[1] := 4 { true } +g := 5 { false } else = 6 { false } else = 7 { true }`, + }, + { + note: "v0, rego.v1 import", + regoVersion: RegoV0, + module: `package test + +import rego.v1 + +a := 1 +b contains 1 +c[1] := 2 +d.e.f := 3 +e.f.g contains 1 +f.g.h[1] := 4 + +g := 5 if { + false +} else := 6 if { + false +} else := 7`, + exp: `package test + +import rego.v1 + +a := 1 if { true } +b contains 1 if { true } +c[1] := 2 if { true } +d.e.f := 3 if { true } +e.f.g contains 1 if { true } +f.g.h[1] := 4 if { true } +g := 5 if { false } else = 6 if { false } else = 7 if { true }`, + }, + { + note: "v1, basic", + regoVersion: RegoV1, + module: `package test + +a := 1 +b contains 1 +c[1] := 2 +d.e.f := 3 +e.f.g contains 1 +f.g.h[1] := 4 + +g := 5 if { + false +} else := 6 if { + false +} else := 7`, + exp: `package test + +a := 1 if { true } +b contains 1 if { true } +c[1] := 2 if { true } +d.e.f := 3 if { true } +e.f.g contains 1 if { true } +f.g.h[1] := 4 if { true } +g := 5 if { false } else = 6 if { false } else = 7 if { true }`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + module, err := ParseModuleWithOpts("test.rego", tc.module, ParserOptions{RegoVersion: tc.regoVersion}) + if err != nil { + t.Fatal(err) + } + if act := module.String(); act != tc.exp { + t.Errorf("expected:\n\n%s\n\ngot:\n\n%s", tc.exp, act) + } + }) + } +} + +func TestCommentCopy(t *testing.T) { + comment := &Comment{ + Text: []byte("foo bar baz"), + Location: &location.Location{}, // location must be set for comment equality + } + + cpy := comment.Copy() + if !cpy.Equal(comment) { + t.Fatal("expected copy to be equal") + } + + comment.Text[1] = '0' + + if cpy.Equal(comment) { + t.Fatal("expected copy to be unmodified") + } +} + +func assertExprEqual(t *testing.T, a, b *Expr) { + t.Helper() + if !a.Equal(b) { + t.Errorf("Expressions are not equal (expected equal): a=%v b=%v", a, b) + } +} + +func assertExprNotEqual(t *testing.T, a, b *Expr) { + t.Helper() + if a.Equal(b) { + t.Errorf("Expressions are equal (expected not equal): a=%v b=%v", a, b) + } +} + +func assertExprString(t *testing.T, expr *Expr, expected string) { + t.Helper() + result := expr.String() + if result != expected { + t.Errorf("Expected %v but got %v", expected, result) + } +} + +func assertImportsEqual(t *testing.T, a, b *Import) { + t.Helper() + if !a.Equal(b) { + t.Errorf("Imports are not equal (expected equal): a=%v b=%v", a, b) + } +} + +func assertImportsNotEqual(t *testing.T, a, b *Import) { + t.Helper() + if a.Equal(b) { + t.Errorf("Imports are equal (expected not equal): a=%v b=%v", a, b) + } +} + +func assertImportToString(t *testing.T, imp *Import, expected string) { + t.Helper() + result := imp.String() + if result != expected { + t.Errorf("Expected %v but got %v", expected, result) + } +} + +func assertPackagesEqual(t *testing.T, a, b *Package) { + t.Helper() + if !a.Equal(b) { + t.Errorf("Packages are not equal (expected equal): a=%v b=%v", a, b) + } +} + +func assertPackagesNotEqual(t *testing.T, a, b *Package) { + t.Helper() + if a.Equal(b) { + t.Errorf("Packages are not equal (expected not equal): a=%v b=%v", a, b) + } +} + +func assertRulesEqual(t *testing.T, a, b *Rule) { + t.Helper() + if !a.Equal(b) { + t.Errorf("Rules are not equal (expected equal):\na=%v\nb=%v", a, b) + } +} + +func assertRulesNotEqual(t *testing.T, a, b *Rule) { + t.Helper() + if a.Equal(b) { + t.Errorf("Rules are equal (expected not equal): a=%v b=%v", a, b) + } +} + +func assertHeadsEqual(t *testing.T, a, b *Head) { + t.Helper() + if !a.Equal(b) { + t.Errorf("Heads are not equal (expected equal): a=%v b=%v", a, b) + } +} + +func assertHeadsNotEqual(t *testing.T, a, b *Head) { + t.Helper() + if a.Equal(b) { + t.Errorf("Heads are equal (expected not equal): a=%v b=%v", a, b) + } +} + +func assertRuleString(t *testing.T, rule *Rule, expected string, opts toStringOpts) { + t.Helper() + result := rule.stringWithOpts(opts) + if result != expected { + t.Errorf("Expected %v but got %v for rego-version %v", expected, result, opts.regoVersion) + } +} diff --git a/third_party/opa/v1/ast/pretty.go b/third_party/opa/v1/ast/pretty.go new file mode 100644 index 000000000000..aa34f374711b --- /dev/null +++ b/third_party/opa/v1/ast/pretty.go @@ -0,0 +1,82 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "io" + "strings" +) + +// Pretty writes a pretty representation of the AST rooted at x to w. +// +// This is function is intended for debug purposes when inspecting ASTs. +func Pretty(w io.Writer, x any) { + pp := &prettyPrinter{ + depth: -1, + w: w, + } + NewBeforeAfterVisitor(pp.Before, pp.After).Walk(x) +} + +type prettyPrinter struct { + depth int + w io.Writer +} + +func (pp *prettyPrinter) Before(x any) bool { + switch x.(type) { + case *Term: + default: + pp.depth++ + } + + switch x := x.(type) { + case *Term: + return false + case Args: + if len(x) == 0 { + return false + } + pp.writeType(x) + case *Expr: + extras := []string{} + if x.Negated { + extras = append(extras, "negated") + } + extras = append(extras, fmt.Sprintf("index=%d", x.Index)) + pp.writeIndent("%v %v", TypeName(x), strings.Join(extras, " ")) + case Null, Boolean, Number, String, Var: + pp.writeValue(x) + default: + pp.writeType(x) + } + return false +} + +func (pp *prettyPrinter) After(x any) { + switch x.(type) { + case *Term: + default: + pp.depth-- + } +} + +func (pp *prettyPrinter) writeValue(x any) { + pp.writeIndent(fmt.Sprint(x)) +} + +func (pp *prettyPrinter) writeType(x any) { + pp.writeIndent(TypeName(x)) +} + +func (pp *prettyPrinter) writeIndent(f string, a ...any) { + pad := strings.Repeat(" ", pp.depth) + pp.write(pad+f, a...) +} + +func (pp *prettyPrinter) write(f string, a ...any) { + fmt.Fprintf(pp.w, f+"\n", a...) +} diff --git a/third_party/opa/v1/ast/pretty_test.go b/third_party/opa/v1/ast/pretty_test.go new file mode 100644 index 000000000000..96603d5ed18c --- /dev/null +++ b/third_party/opa/v1/ast/pretty_test.go @@ -0,0 +1,103 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "bytes" + "strings" + "testing" +) + +func TestPretty(t *testing.T) { + + module := module(` + package foo.bar + + import data.baz as qux + + p[x] = y if { + x = a + b + y = {"foo": [{1, null}, true]} + } + + f(x) = g(x) + `) + + var buf bytes.Buffer + Pretty(&buf, module) + + expected := `module + package + ref + data + "foo" + "bar" + import + ref + data + "baz" + qux + rule + head + ref + p + x + y + body + expr index=0 + ref + eq + x + call + ref + plus + a + b + expr index=1 + ref + eq + y + object + "foo" + array + set + null + 1 + true + rule + head + ref + f + args + x + call + ref + g + x + body + expr index=0 + true` + + result := strings.TrimSpace(buf.String()) + expected = strings.TrimSpace(expected) + + if result != expected { + + resultLines := strings.Split(result, "\n") + expectedLines := strings.Split(expected, "\n") + + minLines := min(len(resultLines), len(expectedLines)) + + for i := range minLines { + if resultLines[i] != expectedLines[i] { + t.Fatalf("Expected line %d to be:\n\n%q\n\nGot:\n\n%q", i, expectedLines[i], resultLines[i]) + } + } + + if len(resultLines) != len(expectedLines) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", expectedLines, resultLines) + } + } +} diff --git a/third_party/opa/v1/ast/rego_compiler.go b/third_party/opa/v1/ast/rego_compiler.go new file mode 100644 index 000000000000..78d0efc59ab5 --- /dev/null +++ b/third_party/opa/v1/ast/rego_compiler.go @@ -0,0 +1,17 @@ +package ast + +import "context" + +type regoCompileCtx struct{} + +func WithCompiler(ctx context.Context, c *Compiler) context.Context { + return context.WithValue(ctx, regoCompileCtx{}, c) +} + +func CompilerFromContext(ctx context.Context) (*Compiler, bool) { + if ctx == nil { + return nil, false + } + v, ok := ctx.Value(regoCompileCtx{}).(*Compiler) + return v, ok +} diff --git a/third_party/opa/v1/ast/rego_v1.go b/third_party/opa/v1/ast/rego_v1.go new file mode 100644 index 000000000000..a702d9294c27 --- /dev/null +++ b/third_party/opa/v1/ast/rego_v1.go @@ -0,0 +1,208 @@ +package ast + +import ( + "fmt" + + "github.com/open-policy-agent/opa/v1/ast/internal/tokens" +) + +func checkDuplicateImports(modules []*Module) (errors Errors) { + for _, module := range modules { + processedImports := map[Var]*Import{} + + for _, imp := range module.Imports { + name := imp.Name() + + if processed, conflict := processedImports[name]; conflict { + errors = append(errors, NewError(CompileErr, imp.Location, "import must not shadow %v", processed)) + } else { + processedImports[name] = imp + } + } + } + return +} + +func checkRootDocumentOverrides(node any) Errors { + errors := Errors{} + + WalkRules(node, func(rule *Rule) bool { + var name string + if len(rule.Head.Reference) > 0 { + name = rule.Head.Reference[0].Value.(Var).String() + } else { + name = rule.Head.Name.String() + } + if RootDocumentRefs.Contains(RefTerm(VarTerm(name))) { + errors = append(errors, NewError(CompileErr, rule.Location, "rules must not shadow %v (use a different rule name)", name)) + } + + for _, arg := range rule.Head.Args { + if _, ok := arg.Value.(Ref); ok { + if RootDocumentRefs.Contains(arg) { + errors = append(errors, NewError(CompileErr, arg.Location, "args must not shadow %v (use a different variable name)", arg)) + } + } + } + + return true + }) + + WalkExprs(node, func(expr *Expr) bool { + if expr.IsAssignment() { + // assign() can be called directly, so we need to assert its given first operand exists before checking its name. + if nameOp := expr.Operand(0); nameOp != nil { + name := nameOp.String() + if RootDocumentRefs.Contains(RefTerm(VarTerm(name))) { + errors = append(errors, NewError(CompileErr, expr.Location, "variables must not shadow %v (use a different variable name)", name)) + } + } + } + return false + }) + + return errors +} + +func walkCalls(node any, f func(any) bool) { + vis := &GenericVisitor{func(x any) bool { + switch x := x.(type) { + case Call: + return f(x) + case *Expr: + if x.IsCall() { + return f(x) + } + case *Head: + // GenericVisitor doesn't walk the rule head ref + walkCalls(x.Reference, f) + } + return false + }} + vis.Walk(node) +} + +func checkDeprecatedBuiltins(deprecatedBuiltinsMap map[string]struct{}, node any) Errors { + errs := make(Errors, 0) + + walkCalls(node, func(x any) bool { + var operator string + var loc *Location + + switch x := x.(type) { + case *Expr: + operator = x.Operator().String() + loc = x.Loc() + case Call: + terms := []*Term(x) + if len(terms) > 0 { + operator = terms[0].Value.String() + loc = terms[0].Loc() + } + } + + if operator != "" { + if _, ok := deprecatedBuiltinsMap[operator]; ok { + errs = append(errs, NewError(TypeErr, loc, "deprecated built-in function calls in expression: %v", operator)) + } + } + + return false + }) + + return errs +} + +func checkDeprecatedBuiltinsForCurrentVersion(node any) Errors { + deprecatedBuiltins := make(map[string]struct{}) + capabilities := CapabilitiesForThisVersion() + for _, bi := range capabilities.Builtins { + if bi.IsDeprecated() { + deprecatedBuiltins[bi.Name] = struct{}{} + } + } + + return checkDeprecatedBuiltins(deprecatedBuiltins, node) +} + +type RegoCheckOptions struct { + NoDuplicateImports bool + NoRootDocumentOverrides bool + NoDeprecatedBuiltins bool + NoKeywordsAsRuleNames bool + RequireIfKeyword bool + RequireContainsKeyword bool + RequireRuleBodyOrValue bool +} + +func NewRegoCheckOptions() RegoCheckOptions { + // all options are enabled by default + return RegoCheckOptions{ + NoDuplicateImports: true, + NoRootDocumentOverrides: true, + NoDeprecatedBuiltins: true, + NoKeywordsAsRuleNames: true, + RequireIfKeyword: true, + RequireContainsKeyword: true, + RequireRuleBodyOrValue: true, + } +} + +// CheckRegoV1 checks the given module or rule for errors that are specific to Rego v1. +// Passing something other than an *ast.Rule or *ast.Module is considered a programming error, and will cause a panic. +func CheckRegoV1(x any) Errors { + return CheckRegoV1WithOptions(x, NewRegoCheckOptions()) +} + +func CheckRegoV1WithOptions(x any, opts RegoCheckOptions) Errors { + switch x := x.(type) { + case *Module: + return checkRegoV1Module(x, opts) + case *Rule: + return checkRegoV1Rule(x, opts) + } + panic(fmt.Sprintf("cannot check rego-v1 compatibility on type %T", x)) +} + +func checkRegoV1Module(module *Module, opts RegoCheckOptions) Errors { + var errors Errors + if opts.NoDuplicateImports { + errors = append(errors, checkDuplicateImports([]*Module{module})...) + } + if opts.NoRootDocumentOverrides { + errors = append(errors, checkRootDocumentOverrides(module)...) + } + if opts.NoDeprecatedBuiltins { + errors = append(errors, checkDeprecatedBuiltinsForCurrentVersion(module)...) + } + + for _, rule := range module.Rules { + errors = append(errors, checkRegoV1Rule(rule, opts)...) + } + + return errors +} + +func checkRegoV1Rule(rule *Rule, opts RegoCheckOptions) Errors { + t := "rule" + if rule.isFunction() { + t = "function" + } + + var errs Errors + + if opts.NoKeywordsAsRuleNames && len(rule.Head.Reference) < 2 && IsKeywordInRegoVersion(rule.Head.Name.String(), RegoV1) { + errs = append(errs, NewError(ParseErr, rule.Location, "%s keyword cannot be used for rule name", rule.Head.Name.String())) + } + if opts.RequireRuleBodyOrValue && rule.generatedBody && rule.Head.generatedValue { + errs = append(errs, NewError(ParseErr, rule.Location, "%s must have value assignment and/or body declaration", t)) + } + if opts.RequireIfKeyword && rule.Body != nil && !rule.generatedBody && !ruleDeclarationHasKeyword(rule, tokens.If) && !rule.Default { + errs = append(errs, NewError(ParseErr, rule.Location, "`if` keyword is required before %s body", t)) + } + if opts.RequireContainsKeyword && rule.Head.RuleKind() == MultiValue && !ruleDeclarationHasKeyword(rule, tokens.Contains) { + errs = append(errs, NewError(ParseErr, rule.Location, "`contains` keyword is required for partial set rules")) + } + + return errs +} diff --git a/third_party/opa/v1/ast/schema.go b/third_party/opa/v1/ast/schema.go new file mode 100644 index 000000000000..3f9e2001d5ac --- /dev/null +++ b/third_party/opa/v1/ast/schema.go @@ -0,0 +1,54 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +// SchemaSet holds a map from a path to a schema. +type SchemaSet struct { + m *util.HasherMap[Ref, any] +} + +// NewSchemaSet returns an empty SchemaSet. +func NewSchemaSet() *SchemaSet { + return &SchemaSet{ + m: util.NewHasherMap[Ref, any](RefEqual), + } +} + +// Put inserts a raw schema into the set. +func (ss *SchemaSet) Put(path Ref, raw any) { + ss.m.Put(path, raw) +} + +// Get returns the raw schema identified by the path. +func (ss *SchemaSet) Get(path Ref) any { + if ss != nil { + if x, ok := ss.m.Get(path); ok { + return x + } + } + return nil +} + +func loadSchema(raw any, allowNet []string) (types.Type, error) { + + jsonSchema, err := compileSchema(raw, allowNet) + if err != nil { + return nil, err + } + + tpe, err := newSchemaParser().parseSchema(jsonSchema.RootSchema) + if err != nil { + return nil, fmt.Errorf("type checking: %w", err) + } + + return tpe, nil +} diff --git a/third_party/opa/v1/ast/schema_test.go b/third_party/opa/v1/ast/schema_test.go new file mode 100644 index 000000000000..eaa3e7d196c9 --- /dev/null +++ b/third_party/opa/v1/ast/schema_test.go @@ -0,0 +1,1656 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "errors" + "fmt" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +func testParseSchema(t *testing.T, schema string, expectedType types.Type, expectedError error) { + t.Helper() + + var sch any + err := util.Unmarshal([]byte(schema), &sch) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + newtype, err := loadSchema(sch, nil) + if err != nil && errors.Is(err, expectedError) { + t.Fatalf("unexpected error: %v", err) + } + if newtype == nil && expectedType != nil { + t.Fatalf("parseSchema returned nil type") + } + if newtype != nil && expectedType == nil { + t.Fatalf("expected nil but parseSchema returned a not nil type") + } + if types.Compare(newtype, expectedType) != 0 { + t.Fatalf("parseSchema returned an incorrect type: %s, expected: %s", newtype.String(), expectedType.String()) + } +} + +func TestParseSchemaObject(t *testing.T) { + innerObjectStaticProps := []*types.StaticProperty{} + innerObjectStaticProps = append(innerObjectStaticProps, &types.StaticProperty{Key: "a", Value: types.N}) + innerObjectStaticProps = append(innerObjectStaticProps, &types.StaticProperty{Key: "b", Value: types.NewArray(nil, types.N)}) + innerObjectStaticProps = append(innerObjectStaticProps, &types.StaticProperty{Key: "c", Value: types.A}) + innerObjectType := types.NewObject(innerObjectStaticProps, nil) + + staticProps := []*types.StaticProperty{} + staticProps = append(staticProps, &types.StaticProperty{Key: "b", Value: types.NewArray(nil, innerObjectType)}) + staticProps = append(staticProps, &types.StaticProperty{Key: "foo", Value: types.S}) + + expectedType := types.NewObject(staticProps, nil) + testParseSchema(t, objectSchema, expectedType, nil) +} + +func TestSetTypesWithSchemaRef(t *testing.T) { + var sch any + + ts := kubeSchemaServer(t) + t.Cleanup(ts.Close) + refSchemaReplaced := strings.ReplaceAll(refSchema, "https://kubernetesjsonschema.dev/v1.14.0/", ts.URL+"/") + err := util.Unmarshal([]byte(refSchemaReplaced), &sch) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + t.Run("remote refs disabled", func(t *testing.T) { + _, err := loadSchema(sch, []string{}) + if err == nil { + t.Fatal("expected error, got nil") + } + expErr := fmt.Sprintf("unable to compile the schema: remote reference loading disabled: %s/_definitions.json", ts.URL) + if exp, act := expErr, err.Error(); act != exp { + t.Errorf("expected message %q, got %q", exp, act) + } + }) + + t.Run("all remote refs enabled", func(t *testing.T) { + newtype, err := loadSchema(sch, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if newtype == nil { + t.Fatalf("parseSchema returned nil type") + } + if newtype.String() != "object], result: object], group: string, kind: string, name: string, retryAfterSeconds: number, uid: string>, kind: string, message: string, metadata: object, reason: string, status: string>>, labels: object[any: any], managedFields: array[object], name: string, namespace: string, ownerReferences: array[object], resourceVersion: string, selfLink: string, uid: string>>" { + t.Fatalf("parseSchema returned an incorrect type: %s", newtype.String()) + } + }) + + t.Run("desired remote ref selectively enabled", func(t *testing.T) { + newtype, err := loadSchema(sch, []string{"127.0.0.1"}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if newtype == nil { + t.Fatalf("parseSchema returned nil type") + } + if newtype.String() != "object], result: object], group: string, kind: string, name: string, retryAfterSeconds: number, uid: string>, kind: string, message: string, metadata: object, reason: string, status: string>>, labels: object[any: any], managedFields: array[object], name: string, namespace: string, ownerReferences: array[object], resourceVersion: string, selfLink: string, uid: string>>" { + t.Fatalf("parseSchema returned an incorrect type: %s", newtype.String()) + } + }) + + t.Run("different remote ref selectively enabled", func(t *testing.T) { + _, err := loadSchema(sch, []string{"foo"}) + if err == nil { + t.Fatal("expected error, got nil") + } + expErr := fmt.Sprintf("unable to compile the schema: remote reference loading disabled: %s/_definitions.json", ts.URL) + if exp, act := expErr, err.Error(); act != exp { + t.Errorf("expected message %q, got %q", exp, act) + } + }) +} + +func TestSetTypesWithPodSchema(t *testing.T) { + var sch any + + ts := kubeSchemaServer(t) + t.Cleanup(ts.Close) + + podSchemaReplaced := strings.ReplaceAll(podSchema, "https://kubernetesjsonschema.dev/v1.14.0/", ts.URL+"/") + err := util.Unmarshal([]byte(podSchemaReplaced), &sch) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + newtype, err := loadSchema(sch, nil) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + if newtype == nil { + t.Fatalf("parseSchema returned nil type") + } + if newtype.String() == "object" { + t.Fatalf("parseSchema returned an incorrect type: %s", newtype.String()) + } + +} + +func TestAllOfSchemas(t *testing.T) { + // Test 1: object schema + objectSchemaStaticProps := []*types.StaticProperty{} + objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "AddressLine1", Value: types.S}) + objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "AddressLine2", Value: types.S}) + objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "City", Value: types.S}) + objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "State", Value: types.S}) + objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "ZipCode", Value: types.S}) + objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "County", Value: types.S}) + objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "PostCode", Value: types.S}) + objectSchemaExpectedType := types.NewObject(objectSchemaStaticProps, nil) + + // Test 2: array schema + arrayExpectedType := types.NewArray(nil, types.N) + + // Test 3: parent variation + parentVariationStaticProps := []*types.StaticProperty{} + parentVariationStaticProps = append(parentVariationStaticProps, &types.StaticProperty{Key: "State", Value: types.S}) + parentVariationStaticProps = append(parentVariationStaticProps, &types.StaticProperty{Key: "ZipCode", Value: types.S}) + parentVariationStaticProps = append(parentVariationStaticProps, &types.StaticProperty{Key: "County", Value: types.S}) + parentVariationStaticProps = append(parentVariationStaticProps, &types.StaticProperty{Key: "PostCode", Value: types.S}) + parentVariationExpectedType := types.NewObject(parentVariationStaticProps, nil) + + // Test 4: empty schema with allOf + emptyExpectedType := types.A + + // Tests 5 & 6: schema with array of arrays, object and array as siblings + expectedError := errors.New("unable to merge these schemas") + + // Test 7: array of objects + arrayOfObjectsStaticProps := []*types.StaticProperty{} + arrayOfObjectsStaticProps = append(arrayOfObjectsStaticProps, &types.StaticProperty{Key: "State", Value: types.S}) + arrayOfObjectsStaticProps = append(arrayOfObjectsStaticProps, &types.StaticProperty{Key: "ZipCode", Value: types.S}) + arrayOfObjectsStaticProps = append(arrayOfObjectsStaticProps, &types.StaticProperty{Key: "County", Value: types.S}) + arrayOfObjectsStaticProps = append(arrayOfObjectsStaticProps, &types.StaticProperty{Key: "PostCode", Value: types.S}) + arrayOfObjectsStaticProps = append(arrayOfObjectsStaticProps, &types.StaticProperty{Key: "Street", Value: types.S}) + arrayOfObjectsStaticProps = append(arrayOfObjectsStaticProps, &types.StaticProperty{Key: "House", Value: types.S}) + innerType := types.NewObject(arrayOfObjectsStaticProps, nil) + arrayOfObjectsExpectedType := types.NewArray(nil, innerType) + + // Tests 8 & 9: allOf schema with type not specified + objectMissingStaticProps := []*types.StaticProperty{} + objectMissingStaticProps = append(objectMissingStaticProps, &types.StaticProperty{Key: "AddressLine", Value: types.S}) + objectMissingStaticProps = append(objectMissingStaticProps, &types.StaticProperty{Key: "State", Value: types.S}) + objectMissingStaticProps = append(objectMissingStaticProps, &types.StaticProperty{Key: "ZipCode", Value: types.S}) + objectMissingStaticProps = append(objectMissingStaticProps, &types.StaticProperty{Key: "County", Value: types.S}) + objectMissingStaticProps = append(objectMissingStaticProps, &types.StaticProperty{Key: "PostCode", Value: types.N}) + objectMissingExpectedType := types.NewObject(objectMissingStaticProps, nil) + arrayMissingExpectedType := types.NewArray([]types.Type{types.N, types.N}, nil) + + // Tests 10 & 11: allOf schema with array that contains different types (with and without error) + arrayDifTypesExpectedType := types.NewArray([]types.Type{types.S, types.N}, nil) + + // Test 12: array inside of object + arrayInObjectstaticProps := []*types.StaticProperty{} + arrayInObjectstaticProps = append(arrayInObjectstaticProps, &types.StaticProperty{Key: "age", Value: types.N}) + arrayInObjectstaticProps = append(arrayInObjectstaticProps, &types.StaticProperty{Key: "name", Value: types.S}) + arrayInObjectstaticProps = append(arrayInObjectstaticProps, &types.StaticProperty{Key: "personality", Value: types.S}) + arrayInObjectstaticProps = append(arrayInObjectstaticProps, &types.StaticProperty{Key: "nickname", Value: types.S}) + innerObjectsType := types.NewObject(arrayInObjectstaticProps, nil) + arrayInObjectInnerType := types.NewArray(nil, innerObjectsType) + arrayInObjectExpectedType := types.NewObject([]*types.StaticProperty{ + types.NewStaticProperty("familyMembers", arrayInObjectInnerType)}, nil) + + // Test 13: allOf inside core schema + coreStaticProps := []*types.StaticProperty{} + coreStaticProps = append(coreStaticProps, &types.StaticProperty{Key: "accessMe", Value: types.S}) + coreStaticProps = append(coreStaticProps, &types.StaticProperty{Key: "accessYou", Value: types.S}) + insideType := types.NewObject(coreStaticProps, nil) + outerType := []*types.StaticProperty{} + outerType = append(outerType, &types.StaticProperty{Key: "RandomInfo", Value: insideType}) + outerType = append(outerType, &types.StaticProperty{Key: "AddressLine", Value: types.S}) + coreSchemaExpectedType := types.NewObject(outerType, nil) + + // Test 14-17: other types besides array and object + // Test 18: array with uneven numbers of items children to merge + expectedUnevenArrayType := types.NewArray([]types.Type{types.N, types.N, types.S}, nil) + + tests := []struct { + note string + schema string + expectedType types.Type + expectedError error + }{ + { + note: "allOf with mergeable Object types in schema", + schema: allOfObjectSchema, + expectedType: objectSchemaExpectedType, + expectedError: nil, + }, + { + note: "allOf with mergeable Array types in schema", + schema: allOfArraySchema, + expectedType: arrayExpectedType, + expectedError: nil, + }, + { + note: "allOf without a parent schema", + schema: allOfSchemaParentVariation, + expectedType: parentVariationExpectedType, + expectedError: nil, + }, + { + note: "allOf with empty schema", + schema: emptySchema, + expectedType: emptyExpectedType, + expectedError: nil, + }, + { + note: "allOf schema with unmergeable Array of Arrays", + schema: allOfArrayOfArrays, + expectedType: nil, + expectedError: expectedError, + }, + { + note: "allOf with mergeable Array of Object types in schema", + schema: allOfArrayOfObjects, + expectedType: arrayOfObjectsExpectedType, + expectedError: nil, + }, + { + note: "allOf schema with Array and Object types as siblings", + schema: allOfObjectAndArray, + expectedType: nil, + expectedError: expectedError, + }, + { + note: "allOf with mergeable Object types in schema with type declaration missing", + schema: allOfObjectMissing, + expectedType: objectMissingExpectedType, + expectedError: nil, + }, + { + note: "allOf with mergeable Array types in schema with type declaration missing", + schema: allOfArrayMissing, + expectedType: arrayMissingExpectedType, + expectedError: nil, + }, + { + note: "allOf schema with an Array that contains different mergeable types", + schema: allOfArrayDifTypes, + expectedType: arrayDifTypesExpectedType, + expectedError: nil, + }, + { + note: "allOf schema with Array type that contains different unmergeable types", + schema: allOfArrayDifTypesWithError, + expectedType: nil, + expectedError: expectedError, + }, + { + note: "allOf with mergeable Object containing Array types in schema", + schema: allOfArrayInsideObject, + expectedType: arrayInObjectExpectedType, + expectedError: nil, + }, + { + note: "allOf with mergeable types inside of core schema", + schema: allOfInsideCoreSchema, + expectedType: coreSchemaExpectedType, + expectedError: nil, + }, + { + note: "allOf with mergeable String types in schema", + schema: allOfStringSchema, + expectedType: types.S, + expectedError: nil, + }, + { + note: "allOf with mergeable Integer types in schema", + schema: allOfIntegerSchema, + expectedType: types.N, + expectedError: nil, + }, + { + note: "allOf with mergeable Boolean types in schema", + schema: allOfBooleanSchema, + expectedType: types.B, + expectedError: nil, + }, + { + note: "allOf schema with different unmergeable types", + schema: allOfTypeErrorSchema, + expectedType: nil, + expectedError: expectedError, + }, + { + note: "allOf schema with unmergeable types String and Boolean", + schema: allOfStringSchemaWithError, + expectedType: nil, + expectedError: expectedError, + }, + { + note: "allOf unmergeable schema with different parent and items types", + schema: allOfSchemaWithParentError, + expectedType: nil, + expectedError: expectedError, + }, + { + note: "allOf schema of Array type with uneven numbers of items to merge", + schema: allOfSchemaWithUnevenArray, + expectedType: expectedUnevenArrayType, + expectedError: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + testParseSchema(t, tc.schema, tc.expectedType, tc.expectedError) + }) + } +} + +func TestParseSchemaUntypedField(t *testing.T) { + // Expected type is: object + staticProps := []*types.StaticProperty{} + staticProps = append(staticProps, &types.StaticProperty{Key: "foo", Value: types.A}) + expectedType := types.NewObject(staticProps, nil) + testParseSchema(t, untypedFieldObjectSchema, expectedType, nil) +} + +func TestParseSchemaNoChildren(t *testing.T) { + // Expected type is: object[any: any] + expectedType := types.NewObject(nil, &types.DynamicProperty{Key: types.A, Value: types.A}) + testParseSchema(t, noChildrenObjectSchema, expectedType, nil) +} + +func TestParseSchemaArrayNoItems(t *testing.T) { + // Expected type is: object + staticProps := []*types.StaticProperty{} + staticProps = append(staticProps, &types.StaticProperty{Key: "b", Value: types.NewArray(nil, types.A)}) + expectedType := types.NewObject(staticProps, nil) + testParseSchema(t, arrayNoItemsSchema, expectedType, nil) +} + +func TestParseSchemaBooleanField(t *testing.T) { + // Expected type is: object + staticProps := []*types.StaticProperty{} + staticProps = append(staticProps, &types.StaticProperty{Key: "a", Value: types.B}) + expectedType := types.NewObject(staticProps, nil) + testParseSchema(t, booleanSchema, expectedType, nil) +} + +func TestParseSchemaBasics(t *testing.T) { + tests := []struct { + note string + schema string + exp types.Type + }{ + { + note: "number", + schema: `{"type": "number"}`, + exp: types.N, + }, + { + note: "array of objects", + schema: `{ + "type": "array", + "items": { + "type": "object", + "properties": { + "id": {"type": "string"}, + "value": {"type": "number"} + } + } + }`, + exp: types.NewArray(nil, types.NewObject([]*types.StaticProperty{ + types.NewStaticProperty("id", types.S), + types.NewStaticProperty("value", types.N), + }, nil)), + }, + { + note: "static array items", + schema: `{ + "type": "array", + "items": [ + {"type": "string"}, + {"type": "number"} + ] + }`, + exp: types.NewArray([]types.Type{ + types.S, + types.N, + }, nil), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + testParseSchema(t, tc.schema, tc.exp, nil) + }) + } +} + +func TestCompileSchemaEmptySchema(t *testing.T) { + schema := "" + var sch any + err := util.Unmarshal([]byte(schema), &sch) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + jsonSchema, _ := compileSchema(sch, []string{}) + if jsonSchema != nil { + t.Fatalf("Incorrect return from parseSchema with an empty schema") + } +} + +func TestParseSchemaWithSchemaBadSchema(t *testing.T) { + var sch any + err := util.Unmarshal([]byte(objectSchema), &sch) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + jsonSchema, err := compileSchema(sch, []string{}) + if err != nil { + t.Fatalf("Unable to compile schema: %v", err) + } + newtype, err := newSchemaParser().parseSchema(jsonSchema) // Did not pass the subschema + if err == nil { + t.Fatalf("Expected parseSchema() = error, got nil") + } + if newtype != nil { + t.Fatalf("Incorrect return from parseSchema with a bad schema") + } +} + +func TestAnyOfSchema(t *testing.T) { + + // Test 1 & 3: anyOf extends the core schema + extendCoreStaticPropsExp1 := []*types.StaticProperty{types.NewStaticProperty("AddressLine", types.S)} + extendCoreStaticPropsExp2 := []*types.StaticProperty{ + types.NewStaticProperty("State", types.S), + types.NewStaticProperty("ZipCode", types.S)} + extendCoreStaticPropsExp3 := []*types.StaticProperty{ + types.NewStaticProperty("County", types.S), + types.NewStaticProperty("PostCode", types.N)} + extendCoreSchemaTypeExp := types.Or(types.NewObject(extendCoreStaticPropsExp3, nil), + types.Or( + types.NewObject(extendCoreStaticPropsExp1, nil), + types.NewObject(extendCoreStaticPropsExp2, nil))) + + // Test 2: anyOf is inside the core schema + insideCoreObjType := types.NewObject([]*types.StaticProperty{ + types.NewStaticProperty("accessMe", types.S)}, nil) + insideCoreAnyType := types.Or(insideCoreObjType, types.N) + insideCoreStaticProps := []*types.StaticProperty{ + types.NewStaticProperty("AddressLine", types.S), + types.NewStaticProperty("RandomInfo", insideCoreAnyType)} + insideCoreTypeExp := types.NewObject(insideCoreStaticProps, nil) + + // Test 4: anyOf in an array + arrayObjType1 := types.NewObject([]*types.StaticProperty{ + types.NewStaticProperty("age", types.N), + types.NewStaticProperty("name", types.S)}, nil) + arrayObjType2 := types.NewObject([]*types.StaticProperty{ + types.NewStaticProperty("personality", types.S), + types.NewStaticProperty("nickname", types.S)}, nil) + arrayOrType := types.Or(arrayObjType1, arrayObjType2) + arrayArrayType := types.NewArray(nil, arrayOrType) + arrayObjtype := types.NewObject([]*types.StaticProperty{ + types.NewStaticProperty("familyMembers", arrayArrayType)}, nil) + + // Test 5: anyOf array has items but not specified + arrayMissing1 := types.NewArray([]types.Type{ + types.N, types.S}, nil) + arrayMissing2 := types.NewArray([]types.Type{types.N}, nil) + arrayMissingType := types.Or(arrayMissing1, arrayMissing2) + + // Test 6: anyOf Parent variation schema + anyOfParentVar1 := types.NewObject([]*types.StaticProperty{ + types.NewStaticProperty("State", types.S), + types.NewStaticProperty("ZipCode", types.S)}, nil) + anyOfParentVar2 := types.NewObject([]*types.StaticProperty{ + types.NewStaticProperty("County", types.S), + types.NewStaticProperty("PostCode", types.S)}, nil) + anyOfParentVarType := types.Or(anyOfParentVar1, anyOfParentVar2) + + tests := []struct { + note string + schema string + expected types.Type + }{ + { + note: "anyOf extend core schema", + schema: anyOfExtendCoreSchema, + expected: extendCoreSchemaTypeExp, + }, + { + note: "anyOf inside core schema", + schema: anyOfInsideCoreSchema, + expected: insideCoreTypeExp, + }, + { + note: "anyOf object missing type", + schema: anyOfObjectMissing, + expected: extendCoreSchemaTypeExp, + }, + { + note: "anyOf of an array", + schema: anyOfArraySchema, + expected: arrayObjtype, + }, + { + note: "anyOf array missing type", + schema: anyOfArrayMissing, + expected: arrayMissingType, + }, + { + note: "anyOf as parent", + schema: anyOfSchemaParentVariation, + expected: anyOfParentVarType, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + testParseSchema(t, tc.schema, tc.expected, nil) + }) + } +} + +func kubeSchemaServer(t *testing.T) *httptest.Server { + t.Helper() + bs, err := os.ReadFile("testdata/_definitions.json") + if err != nil { + t.Fatal(err) + } + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + _, err := w.Write(bs) + if err != nil { + panic(err) + } + })) + return ts +} + +func TestCompilerCheckTypesWithSchema(t *testing.T) { + c := NewCompiler() + var schema any + err := util.Unmarshal([]byte(objectSchema), &schema) + if err != nil { + t.Fatal("Unexpected error:", err) + } + schemaSet := NewSchemaSet() + schemaSet.Put(SchemaRootRef, schema) + c.WithSchemas(schemaSet) + compileStages(c, c.checkTypes) + assertNotFailed(t, c) +} + +func TestCompilerCheckTypesWithRegexPatternInSchema(t *testing.T) { + c := NewCompiler() + var schema any + // Negative lookahead is not supported in the Go regex dialect, but this is still a valid + // JSON schema. Since we don't rely on the "pattern" attribute for type checking, ensure + // that this still works (by being ignored) + err := util.Unmarshal([]byte(`{ + "properties": { + "name": { + "pattern": "^(?!testing:.*)[a-z]+$", + "type": "string" + } + } + }`), &schema) + if err != nil { + t.Fatal("Unexpected error:", err) + } + schemaSet := NewSchemaSet() + schemaSet.Put(SchemaRootRef, schema) + c.WithSchemas(schemaSet) + compileStages(c, c.checkTypes) + assertNotFailed(t, c) +} + +func TestCompilerCheckTypesWithAllOfSchema(t *testing.T) { + + tests := []struct { + note string + schema string + expectedError error + }{ + { + note: "allOf with mergeable Object types in schema", + schema: allOfObjectSchema, + expectedError: nil, + }, + { + note: "allOf with mergeable Array types in schema", + schema: allOfArraySchema, + expectedError: nil, + }, + { + note: "allOf without a parent schema", + schema: allOfSchemaParentVariation, + expectedError: nil, + }, + { + note: "allOf with empty schema", + schema: emptySchema, + expectedError: nil, + }, + { + note: "allOf with mergeable Array of Object types in schema", + schema: allOfArrayOfObjects, + expectedError: nil, + }, + { + note: "allOf with mergeable Object types in schema with type declaration missing", + schema: allOfObjectMissing, + expectedError: nil, + }, + { + note: "allOf with Array of mergeable different types in schema", + schema: allOfArrayDifTypes, + expectedError: nil, + }, + { + note: "allOf with mergeable Object containing Array types in schema", + schema: allOfArrayInsideObject, + expectedError: nil, + }, + { + note: "allOf with mergeable Array types in schema with type declaration missing", + schema: allOfArrayMissing, + expectedError: nil, + }, + { + note: "allOf with mergeable types inside of core schema", + schema: allOfInsideCoreSchema, + expectedError: nil, + }, + { + note: "allOf with mergeable String types in schema", + schema: allOfStringSchema, + expectedError: nil, + }, + { + note: "allOf with mergeable Integer types in schema", + schema: allOfIntegerSchema, + expectedError: nil, + }, + { + note: "allOf with mergeable Boolean types in schema", + schema: allOfBooleanSchema, + expectedError: nil, + }, + { + note: "allOf with mergeable Array types with uneven numbers of items", + schema: allOfSchemaWithUnevenArray, + expectedError: nil, + }, + { + note: "allOf schema with unmergeable Array of Arrays", + schema: allOfArrayOfArrays, + expectedError: errors.New("unable to merge these schemas"), + }, + { + note: "allOf schema with Array and Object types as siblings", + schema: allOfObjectAndArray, + expectedError: errors.New("unable to merge these schemas"), + }, + { + note: "allOf schema with Array type that contains different unmergeable types", + schema: allOfArrayDifTypesWithError, + expectedError: errors.New("unable to merge these schemas"), + }, + { + note: "allOf schema with different unmergeable types", + schema: allOfTypeErrorSchema, + expectedError: errors.New("unable to merge these schemas"), + }, + { + note: "allOf unmergeable schema with different parent and items types", + schema: allOfSchemaWithParentError, + expectedError: errors.New("unable to merge these schemas"), + }, + { + note: "allOf schema of Array type with uneven numbers of items to merge", + schema: allOfSchemaWithUnevenArray, + expectedError: nil, + }, + { + note: "allOf schema with unmergeable types String and Boolean", + schema: allOfStringSchemaWithError, + expectedError: errors.New("unable to merge these schemas"), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + c := NewCompiler() + var schema any + err := util.Unmarshal([]byte(tc.schema), &schema) + if err != nil { + t.Fatal("Unexpected error:", err) + } + schemaSet := NewSchemaSet() + schemaSet.Put(SchemaRootRef, schema) + c.WithSchemas(schemaSet) + compileStages(c, c.checkTypes) + if tc.expectedError != nil { + if errors.Is(c.Errors, tc.expectedError) { + t.Fatal("Unexpected error:", err) + } + } else { + assertNotFailed(t, c) + } + }) + } +} + +func TestWithSchema(t *testing.T) { + c := NewCompiler() + schemaSet := NewSchemaSet() + schemaSet.Put(SchemaRootRef, objectSchema) + c.WithSchemas(schemaSet) + if c.schemaSet == nil { + t.Fatalf("WithSchema did not set the schema correctly in the compiler") + } +} + +func TestAnyOfObjectSchema1(t *testing.T) { + c := NewCompiler() + schemaSet := NewSchemaSet() + schemaSet.Put(SchemaRootRef, anyOfExtendCoreSchema) + c.WithSchemas(schemaSet) + if c.schemaSet == nil { + t.Fatalf("Did not correctly compile an object type schema with anyOf outside core schema") + } +} + +func TestAnyOfObjectSchema2(t *testing.T) { + c := NewCompiler() + schemaSet := NewSchemaSet() + schemaSet.Put(SchemaRootRef, anyOfInsideCoreSchema) + c.WithSchemas(schemaSet) + if c.schemaSet == nil { + t.Fatalf("Did not correctly compile an object type schema with anyOf inside core schema") + } +} + +func TestAnyOfArraySchema(t *testing.T) { + c := NewCompiler() + schemaSet := NewSchemaSet() + schemaSet.Put(SchemaRootRef, anyOfArraySchema) + c.WithSchemas(schemaSet) + if c.schemaSet == nil { + t.Fatalf("Did not correctly compile an array type schema with anyOf") + } +} + +func TestAnyOfObjectMissing(t *testing.T) { + c := NewCompiler() + schemaSet := NewSchemaSet() + schemaSet.Put(SchemaRootRef, anyOfObjectMissing) + c.WithSchemas(schemaSet) + if c.schemaSet == nil { + t.Fatalf("Did not correctly compile an object type schema with anyOf where one of the props did not explicitly claim type") + } +} + +func TestAnyOfArrayMissing(t *testing.T) { + c := NewCompiler() + schemaSet := NewSchemaSet() + schemaSet.Put(SchemaRootRef, anyOfArrayMissing) + c.WithSchemas(schemaSet) + if c.schemaSet == nil { + t.Fatalf("Did not correctly compile an array type schema with anyOf where items are inside anyOf") + } +} + +func TestRecursiveSchema(t *testing.T) { + c := NewCompiler() + schemaSet := NewSchemaSet() + schemaSet.Put(SchemaRootRef, recursiveElements) + c.WithSchemas(schemaSet) + if c.schemaSet == nil { + t.Fatalf("Did not correctly compile an object schema with recursive elements") + } +} + +const objectSchema = `{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/example.json", + "type": "object", + "title": "The root schema", + "description": "The root schema comprises the entire JSON document.", + "required": [ + "foo", + "b" + ], + "properties": { + "foo": { + "$id": "#/properties/foo", + "type": "string", + "title": "The foo schema", + "description": "An explanation about the purpose of this instance." + }, + "b": { + "$id": "#/properties/b", + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "additionalItems": false, + "items": { + "$id": "#/properties/b/items", + "type": "object", + "title": "The items schema", + "description": "An explanation about the purpose of this instance.", + "required": [ + "a", + "b", + "c" + ], + "properties": { + "a": { + "$id": "#/properties/b/items/properties/a", + "type": "integer", + "title": "The a schema", + "description": "An explanation about the purpose of this instance." + }, + "b": { + "$id": "#/properties/b/items/properties/b", + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "additionalItems": false, + "items": { + "$id": "#/properties/b/items/properties/b/items", + "type": "integer", + "title": "The items schema", + "description": "An explanation about the purpose of this instance." + } + }, + "c": { + "$id": "#/properties/b/items/properties/c", + "type": "null", + "title": "The c schema", + "description": "An explanation about the purpose of this instance." + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +}` + +const arrayNoItemsSchema = `{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/example.json", + "type": "object", + "title": "The root schema", + "description": "The root schema comprises the entire JSON document.", + "required": [ + "b" + ], + "properties": { + "b": { + "$id": "#/properties/b", + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "additionalItems": true + } + }, + "additionalProperties": false +}` + +const noChildrenObjectSchema = `{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/example.json", + "type": "object", + "title": "The root schema", + "description": "The root schema comprises the entire JSON document.", + "additionalProperties": true +}` + +const untypedFieldObjectSchema = `{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/example.json", + "type": "object", + "title": "The root schema", + "description": "The root schema comprises the entire JSON document.", + "required": [ + "foo" + ], + "properties": { + "foo": { + "$id": "#/properties/foo" + } + }, + "additionalProperties": false +}` + +const booleanSchema = `{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/example.json", + "type": "object", + "title": "The root schema", + "description": "The root schema comprises the entire JSON document.", + "required": [ + "a" + ], + "properties": { + "a": { + "$id": "#/properties/foo", + "type": "boolean", + "title": "The foo schema", + "description": "An explanation about the purpose of this instance." + } + }, + "additionalProperties": false +}` + +const refSchema = ` +{ + "description": "Pod is a collection of containers that can run on a host. This resource is created by clients and scheduled onto hosts.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": [ + "string", + "null" + ] + }, + + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": [ + "string", + "null" + ], + "enum": [ + "Pod" + ] + }, + "metadata": { + "$ref": "https://kubernetesjsonschema.dev/v1.14.0/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + } +} +` +const podSchema = ` +{ + "description": "Pod is a collection of containers that can run on a host. This resource is created by clients and scheduled onto hosts.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": [ + "string", + "null" + ] + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": [ + "string", + "null" + ], + "enum": [ + "Pod" + ] + }, + "metadata": { + "$ref": "https://kubernetesjsonschema.dev/v1.14.0/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "https://kubernetesjsonschema.dev/v1.14.0/_definitions.json#/definitions/io.k8s.api.core.v1.PodSpec", + "description": "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "https://kubernetesjsonschema.dev/v1.14.0/_definitions.json#/definitions/io.k8s.api.core.v1.PodStatus", + "description": "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Pod", + "version": "v1" + } + ], + "$schema": "http://json-schema.org/schema#" + }` + +const anyOfArraySchema = `{ + "type": "object", + "properties": { + "familyMembers": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "age": { "type": "integer" }, + "name": {"type": "string"} + } + },{ + "type": "object", + "properties": { + "personality": { "type": "string" }, + "nickname": { "type": "string" } + } + } + ] + } + } + } +}` + +const anyOfExtendCoreSchema = `{ + "type": "object", + "properties": { + "AddressLine": { "type": "string" } + }, + "anyOf": [ + { + "type": "object", + "properties": { + "State": { "type": "string" }, + "ZipCode": { "type": "string" } + } + }, + { + "type": "object", + "properties": { + "County": { "type": "string" }, + "PostCode": { "type": "integer" } + } + } + ] +}` + +const allOfObjectSchema = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "title": "My schema", + "properties": { + "AddressLine1": { "type": "string" }, + "AddressLine2": { "type": "string" }, + "City": { "type": "string" } + }, + "allOf": [ + { + "type": "object", + "properties": { + "State": { "type": "string" }, + "ZipCode": { "type": "string" } + }, + }, + { + "type": "object", + "properties": { + "County": { "type": "string" }, + "PostCode": { "type": "string" } + }, + } + ] +}` + +const allOfArraySchema = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "items": { + "type": "integer", + "title": "The items schema", + "description": "An explanation about the purpose of this instance." + }, + "allOf": [ + { + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "items": { + "type": "integer", + "title": "The items schema", + "description": "An explanation about the purpose of this instance." + } + }, + { + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "items": { + "type": "integer", + "title": "The items schema", + "description": "An explanation about the purpose of this instance." + } + } + ] +}` + +const allOfSchemaParentVariation = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "allOf": [ + { + "type": "object", + "properties": { + "State": { "type": "string" }, + "ZipCode": { "type": "string" } + }, + }, + { + "type": "object", + "properties": { + "County": { "type": "string" }, + "PostCode": { "type": "string" } + }, + } + ] +}` + +const emptySchema = `{ + "allof" : [] + }` + +const allOfArrayOfArrays = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "items": { + "type": "array", + "title": "The items schema", + "description": "An explanation about the purpose of this instance.", + "items": { + "type": "integer", + "title": "The items schema", + "description": "An explanation about the purpose of this instance." + } + }, + "allOf": [{ + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "items": { + "type": "array", + "title": "The items schema", + "description": "An explanation about the purpose of this instance.", + "items": { + "type": "integer", + "title": "The items schema", + "description": "An explanation about the purpose of this instance." + } + } + }, + { + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "items": { + "type": "integer", + "title": "The items schema", + "description": "An explanation about the purpose of this instance." + } + } + ] +}` + +const anyOfInsideCoreSchema = ` { + "type": "object", + "properties": { + "AddressLine": { "type": "string" }, + "RandomInfo": { + "anyOf": [ + { "type": "object", + "properties": { + "accessMe": {"type": "string"} + } + }, + { "type": "number", "minimum": 0 } + ] + } + } +}` + +const anyOfObjectMissing = `{ + "type": "object", + "properties": { + "AddressLine": { "type": "string" } + }, + "anyOf": [ + { + "type": "object", + "properties": { + "State": { "type": "string" }, + "ZipCode": { "type": "string" } + } + }, + { + "properties": { + "County": { "type": "string" }, + "PostCode": { "type": "integer" } + } + } + ] +}` + +const allOfArrayOfObjects = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "items": { + "type": "object", + "title": "The items schema", + "description": "An explanation about the purpose of this instance.", + "properties": { + "State": { + "type": "string" + }, + "ZipCode": { + "type": "string" + } + }, + "allOf": [{ + "type": "object", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "properties": { + "County": { + "type": "string" + }, + "PostCode": { + "type": "string" + } + } + }, + { + "type": "object", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "properties": { + "Street": { + "type": "string" + }, + "House": { + "type": "string" + } + } + } + ] + } +}` + +const allOfObjectAndArray = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "title": "My schema", + "properties": { + "AddressLine1": { + "type": "string" + }, + "AddressLine2": { + "type": "string" + }, + "City": { + "type": "string" + } + }, + "allOf": [{ + "type": "object", + "properties": { + "State": { + "type": "string" + }, + "ZipCode": { + "type": "string" + } + } + }, + { + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "items": { + "type": "integer", + "title": "The items schema", + "description": "An explanation about the purpose of this instance." + } + } + ] +}` + +const allOfObjectMissing = `{ + "type": "object", + "properties": { + "AddressLine": { "type": "string" } + }, + "allOf": [ + { + "type": "object", + "properties": { + "State": { "type": "string" }, + "ZipCode": { "type": "string" } + } + }, + { + "properties": { + "County": { "type": "string" }, + "PostCode": { "type": "integer" } + } + } + ] +}` + +const allOfArrayDifTypes = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "allOf": [{ + "type": "array", + "items": [{ + "type": "string" + }, + { + "type": "integer" + } + ] + }, + { + "type": "array", + "items": [{ + "type": "string" + }, + { + "type": "integer" + } + ] + } + ] +}` + +const allOfArrayInsideObject = `{ + "type": "object", + "properties": { + "familyMembers": { + "type": "array", + "items": { + "allOf": [{ + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "name": { + "type": "string" + } + } + }, { + "type": "object", + "properties": { + "personality": { + "type": "string" + }, + "nickname": { + "type": "string" + } + } + }] + } + } + } +}` + +const anyOfArrayMissing = `{ + "type": "array", + "anyOf": [ + { + "items": [ + {"type": "number"}, + {"type": "string"}] + }, + { "items": [ + {"type": "integer"}] + } + ] +}` + +const allOfArrayMissing = `{ + "type": "array", + "allOf": [{ + "items": [{ + "type": "integer" + }, + { + "type": "integer" + } + ] + }, + { + "items": [{ + "type": "integer" + }] + } + ] +}` + +const anyOfSchemaParentVariation = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "anyOf": [ + { + "type": "object", + "properties": { + "State": { "type": "string" }, + "ZipCode": { "type": "string" } + }, + }, + { + "type": "object", + "properties": { + "County": { "type": "string" }, + "PostCode": { "type": "string" } + }, + } + ] + } +}` + +const allOfInsideCoreSchema = `{ + "type": "object", + "properties": { + "AddressLine": { "type": "string" }, + "RandomInfo": { + "allOf": [ + { "type": "object", + "properties": { + "accessMe": {"type": "string"} + } + }, + { "type": "object", + "properties": { + "accessYou": {"type": "string"} + }} + ] + } + } +}` + +const allOfArrayDifTypesWithError = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "allOf": [{ + "type": "array", + "items": [{ + "type": "string" + }, + { + "type": "integer" + } + ] + }, + { + "type": "array", + "items": [{ + "type": "boolean" + }, + { + "type": "integer" + } + ] + } + ] +}` + +const allOfStringSchema = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "string", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "allOf": [{ + "type": "string", + }, + { + "type": "string", + } + ] +}` + +const allOfIntegerSchema = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "integer", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "allOf": [{ + "type": "integer", + }, + { + "type": "integer", + } + ] +}` + +const allOfBooleanSchema = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "boolean", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "allOf": [{ + "type": "boolean", + }, + { + "type": "boolean", + } + ] +}` + +const allOfTypeErrorSchema = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "string", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "allOf": [{ + "type": "string", + }, + { + "type": "integer", + } + ] +}` + +const allOfStringSchemaWithError = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "string", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "allOf": [{ + "type": "string", + }, + { + "type": "string", + }, + { + "type": "boolean", + } + ] +}` + +const allOfSchemaWithParentError = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "string", + "title": "The b schema", + "description": "An explanation about the purpose of this instance.", + "allOf": [{ + "type": "integer", + }, + { + "type": "integer", + } + ] +}` + +const allOfSchemaWithUnevenArray = `{ + "type": "array", + "allOf": [{ + "items": [{ + "type": "integer" + }, + { + "type": "integer" + } + ] + }, + { + "items": [{ + "type": "integer" + }, + { + "type": "integer" + }, + { + "type": "string" + }] + } + ] +}` + +const recursiveElements = `{ + "type": "object", + "properties": { + "Something": { + "$ref": "#/$defs/X" + } + }, + "$defs": { + "X": { + "type": "object", + "properties": { + "Y": { + "$ref": "#/$defs/Y" + } + } + }, + "Y": { + "type": "object", + "properties": { + "X": { + "$ref": "#/$defs/X" + } + } + } + } +} +` diff --git a/third_party/opa/v1/ast/strings.go b/third_party/opa/v1/ast/strings.go new file mode 100644 index 000000000000..84475224126d --- /dev/null +++ b/third_party/opa/v1/ast/strings.go @@ -0,0 +1,54 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "reflect" + "strings" +) + +// TypeName returns a human readable name for the AST element type. +func TypeName(x any) string { + if _, ok := x.(*lazyObj); ok { + return "object" + } + return strings.ToLower(reflect.Indirect(reflect.ValueOf(x)).Type().Name()) +} + +// ValueName returns a human readable name for the AST Value type. +// This is preferrable over calling TypeName when the argument is known to be +// a Value, as this doesn't require reflection (= heap allocations). +func ValueName(x Value) string { + switch x.(type) { + case String: + return "string" + case Boolean: + return "boolean" + case Number: + return "number" + case Null: + return "null" + case Var: + return "var" + case Object: + return "object" + case Set: + return "set" + case Ref: + return "ref" + case Call: + return "call" + case *Array: + return "array" + case *ArrayComprehension: + return "arraycomprehension" + case *ObjectComprehension: + return "objectcomprehension" + case *SetComprehension: + return "setcomprehension" + } + + return TypeName(x) +} diff --git a/third_party/opa/v1/ast/strings_bench_test.go b/third_party/opa/v1/ast/strings_bench_test.go new file mode 100644 index 000000000000..a76e007cea0c --- /dev/null +++ b/third_party/opa/v1/ast/strings_bench_test.go @@ -0,0 +1,27 @@ +package ast + +import "testing" + +// BenchmarkTypeName-10 32207775 38.93 ns/op 8 B/op 1 allocs/op +func BenchmarkTypeName(b *testing.B) { + term := StringTerm("foo") + + for range b.N { + name := TypeName(term.Value) + if name != "string" { + b.Fatalf("expected string but got %v", name) + } + } +} + +// BenchmarkValueName-10 508312227 2.374 ns/op 0 B/op 0 allocs/op +func BenchmarkValueName(b *testing.B) { + term := StringTerm("foo") + + for range b.N { + name := ValueName(term.Value) + if name != "string" { + b.Fatalf("expected string but got %v", name) + } + } +} diff --git a/third_party/opa/v1/ast/syncpools.go b/third_party/opa/v1/ast/syncpools.go new file mode 100644 index 000000000000..82977c836bf7 --- /dev/null +++ b/third_party/opa/v1/ast/syncpools.go @@ -0,0 +1,92 @@ +package ast + +import ( + "strings" + "sync" +) + +type termPtrPool struct { + pool sync.Pool +} + +type stringBuilderPool struct { + pool sync.Pool +} + +type indexResultPool struct { + pool sync.Pool +} + +type vvPool struct { + pool sync.Pool +} + +func (p *termPtrPool) Get() *Term { + return p.pool.Get().(*Term) +} + +func (p *termPtrPool) Put(t *Term) { + p.pool.Put(t) +} + +func (p *stringBuilderPool) Get() *strings.Builder { + return p.pool.Get().(*strings.Builder) +} + +func (p *stringBuilderPool) Put(sb *strings.Builder) { + sb.Reset() + p.pool.Put(sb) +} + +func (p *indexResultPool) Get() *IndexResult { + return p.pool.Get().(*IndexResult) +} + +func (p *indexResultPool) Put(x *IndexResult) { + if x != nil { + p.pool.Put(x) + } +} + +func (p *vvPool) Get() *VarVisitor { + return p.pool.Get().(*VarVisitor) +} + +func (p *vvPool) Put(vv *VarVisitor) { + if vv != nil { + vv.Clear() + p.pool.Put(vv) + } +} + +var TermPtrPool = &termPtrPool{ + pool: sync.Pool{ + New: func() any { + return &Term{} + }, + }, +} + +var sbPool = &stringBuilderPool{ + pool: sync.Pool{ + New: func() any { + return &strings.Builder{} + }, + }, +} + +var varVisitorPool = &vvPool{ + pool: sync.Pool{ + New: func() any { + return NewVarVisitor() + }, + }, +} + +var IndexResultPool = &indexResultPool{ + pool: sync.Pool{ + New: func() any { + return &IndexResult{} + }, + }, +} diff --git a/third_party/opa/v1/ast/term.go b/third_party/opa/v1/ast/term.go new file mode 100644 index 000000000000..62779e63e101 --- /dev/null +++ b/third_party/opa/v1/ast/term.go @@ -0,0 +1,3424 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: deadcode // Public API. +package ast + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "math/big" + "net/url" + "regexp" + "slices" + "strconv" + "strings" + "sync" + "unicode" + + "github.com/cespare/xxhash/v2" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/ast/location" + "github.com/open-policy-agent/opa/v1/util" +) + +var errFindNotFound = errors.New("find: not found") + +// Location records a position in source code. +type Location = location.Location + +// NewLocation returns a new Location object. +func NewLocation(text []byte, file string, row int, col int) *Location { + return location.NewLocation(text, file, row, col) +} + +// Value declares the common interface for all Term values. Every kind of Term value +// in the language is represented as a type that implements this interface: +// +// - Null, Boolean, Number, String +// - Object, Array, Set +// - Variables, References +// - Array, Set, and Object Comprehensions +// - Calls +type Value interface { + Compare(other Value) int // Compare returns <0, 0, or >0 if this Value is less than, equal to, or greater than other, respectively. + Find(path Ref) (Value, error) // Find returns value referred to by path or an error if path is not found. + Hash() int // Returns hash code of the value. + IsGround() bool // IsGround returns true if this value is not a variable or contains no variables. + String() string // String returns a human readable string representation of the value. +} + +// InterfaceToValue converts a native Go value x to a Value. +func InterfaceToValue(x any) (Value, error) { + switch x := x.(type) { + case Value: + return x, nil + case nil: + return NullValue, nil + case bool: + return InternedTerm(x).Value, nil + case json.Number: + if interned := InternedIntNumberTermFromString(string(x)); interned != nil { + return interned.Value, nil + } + return Number(x), nil + case int64: + return int64Number(x), nil + case uint64: + return uint64Number(x), nil + case float64: + return floatNumber(x), nil + case int: + return intNumber(x), nil + case string: + return String(x), nil + case []any: + r := util.NewPtrSlice[Term](len(x)) + for i, e := range x { + e, err := InterfaceToValue(e) + if err != nil { + return nil, err + } + r[i].Value = e + } + return NewArray(r...), nil + case []string: + r := util.NewPtrSlice[Term](len(x)) + for i, e := range x { + r[i].Value = String(e) + } + return NewArray(r...), nil + case map[string]any: + kvs := util.NewPtrSlice[Term](len(x) * 2) + idx := 0 + for k, v := range x { + kvs[idx].Value = String(k) + v, err := InterfaceToValue(v) + if err != nil { + return nil, err + } + kvs[idx+1].Value = v + idx += 2 + } + tuples := make([][2]*Term, len(kvs)/2) + for i := 0; i < len(kvs); i += 2 { + tuples[i/2] = *(*[2]*Term)(kvs[i : i+2]) + } + return NewObject(tuples...), nil + case map[string]string: + r := newobject(len(x)) + for k, v := range x { + r.Insert(StringTerm(k), StringTerm(v)) + } + return r, nil + default: + ptr := util.Reference(x) + if err := util.RoundTrip(ptr); err != nil { + return nil, fmt.Errorf("ast: interface conversion: %w", err) + } + return InterfaceToValue(*ptr) + } +} + +// ValueFromReader returns an AST value from a JSON serialized value in the reader. +func ValueFromReader(r io.Reader) (Value, error) { + var x any + if err := util.NewJSONDecoder(r).Decode(&x); err != nil { + return nil, err + } + return InterfaceToValue(x) +} + +// As converts v into a Go native type referred to by x. +func As(v Value, x any) error { + return util.NewJSONDecoder(strings.NewReader(v.String())).Decode(x) +} + +// Resolver defines the interface for resolving references to native Go values. +type Resolver interface { + Resolve(Ref) (any, error) +} + +// ValueResolver defines the interface for resolving references to AST values. +type ValueResolver interface { + Resolve(Ref) (Value, error) +} + +// UnknownValueErr indicates a ValueResolver was unable to resolve a reference +// because the reference refers to an unknown value. +type UnknownValueErr struct{} + +func (UnknownValueErr) Error() string { + return "unknown value" +} + +// IsUnknownValueErr returns true if the err is an UnknownValueErr. +func IsUnknownValueErr(err error) bool { + _, ok := err.(UnknownValueErr) + return ok +} + +type illegalResolver struct{} + +func (illegalResolver) Resolve(ref Ref) (any, error) { + return nil, fmt.Errorf("illegal value: %v", ref) +} + +// ValueToInterface returns the Go representation of an AST value. The AST +// value should not contain any values that require evaluation (e.g., vars, +// comprehensions, etc.) +func ValueToInterface(v Value, resolver Resolver) (any, error) { + return valueToInterface(v, resolver, JSONOpt{}) +} + +func valueToInterface(v Value, resolver Resolver, opt JSONOpt) (any, error) { + switch v := v.(type) { + case Null: + return nil, nil + case Boolean: + return bool(v), nil + case Number: + return json.Number(v), nil + case String: + return string(v), nil + case *Array: + buf := []any{} + for i := range v.Len() { + x1, err := valueToInterface(v.Elem(i).Value, resolver, opt) + if err != nil { + return nil, err + } + buf = append(buf, x1) + } + return buf, nil + case *object: + buf := make(map[string]any, v.Len()) + err := v.Iter(func(k, v *Term) error { + ki, err := valueToInterface(k.Value, resolver, opt) + if err != nil { + return err + } + var str string + var ok bool + if str, ok = ki.(string); !ok { + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(ki); err != nil { + return err + } + str = strings.TrimSpace(buf.String()) + } + vi, err := valueToInterface(v.Value, resolver, opt) + if err != nil { + return err + } + buf[str] = vi + return nil + }) + if err != nil { + return nil, err + } + return buf, nil + case *lazyObj: + if opt.CopyMaps { + return valueToInterface(v.force(), resolver, opt) + } + return v.native, nil + case Set: + buf := []any{} + iter := func(x *Term) error { + x1, err := valueToInterface(x.Value, resolver, opt) + if err != nil { + return err + } + buf = append(buf, x1) + return nil + } + var err error + if opt.SortSets { + err = v.Sorted().Iter(iter) + } else { + err = v.Iter(iter) + } + if err != nil { + return nil, err + } + return buf, nil + case Ref: + return resolver.Resolve(v) + default: + return nil, fmt.Errorf("%v requires evaluation", TypeName(v)) + } +} + +// JSON returns the JSON representation of v. The value must not contain any +// refs or terms that require evaluation (e.g., vars, comprehensions, etc.) +func JSON(v Value) (any, error) { + return JSONWithOpt(v, JSONOpt{}) +} + +// JSONOpt defines parameters for AST to JSON conversion. +type JSONOpt struct { + SortSets bool // sort sets before serializing (this makes conversion more expensive) + CopyMaps bool // enforces copying of map[string]any read from the store +} + +// JSONWithOpt returns the JSON representation of v. The value must not contain any +// refs or terms that require evaluation (e.g., vars, comprehensions, etc.) +func JSONWithOpt(v Value, opt JSONOpt) (any, error) { + return valueToInterface(v, illegalResolver{}, opt) +} + +// MustJSON returns the JSON representation of v. The value must not contain any +// refs or terms that require evaluation (e.g., vars, comprehensions, etc.) If +// the conversion fails, this function will panic. This function is mostly for +// test purposes. +func MustJSON(v Value) any { + r, err := JSON(v) + if err != nil { + panic(err) + } + return r +} + +// MustInterfaceToValue converts a native Go value x to a Value. If the +// conversion fails, this function will panic. This function is mostly for test +// purposes. +func MustInterfaceToValue(x any) Value { + v, err := InterfaceToValue(x) + if err != nil { + panic(err) + } + return v +} + +// Term is an argument to a function. +type Term struct { + Value Value `json:"value"` // the value of the Term as represented in Go + Location *Location `json:"location,omitempty"` // the location of the Term in the source +} + +// NewTerm returns a new Term object. +func NewTerm(v Value) *Term { + return &Term{ + Value: v, + } +} + +// SetLocation updates the term's Location and returns the term itself. +func (term *Term) SetLocation(loc *Location) *Term { + term.Location = loc + return term +} + +// Loc returns the Location of term. +func (term *Term) Loc() *Location { + if term == nil { + return nil + } + return term.Location +} + +// SetLoc sets the location on term. +func (term *Term) SetLoc(loc *Location) { + term.SetLocation(loc) +} + +// Copy returns a deep copy of term. +func (term *Term) Copy() *Term { + if term == nil { + return nil + } + + cpy := *term + + switch v := term.Value.(type) { + case Null, Boolean, Number, String, Var: + cpy.Value = v + case Ref: + cpy.Value = v.Copy() + case *Array: + cpy.Value = v.Copy() + case Set: + cpy.Value = v.Copy() + case *object: + cpy.Value = v.Copy() + case *ArrayComprehension: + cpy.Value = v.Copy() + case *ObjectComprehension: + cpy.Value = v.Copy() + case *SetComprehension: + cpy.Value = v.Copy() + case Call: + cpy.Value = v.Copy() + } + + return &cpy +} + +// Equal returns true if this term equals the other term. Equality is +// defined for each kind of term, and does not compare the Location. +func (term *Term) Equal(other *Term) bool { + if term == nil && other != nil { + return false + } + if term != nil && other == nil { + return false + } + if term == other { + return true + } + + return ValueEqual(term.Value, other.Value) +} + +// Get returns a value referred to by name from the term. +func (term *Term) Get(name *Term) *Term { + switch v := term.Value.(type) { + case *object: + return v.Get(name) + case *Array: + return v.Get(name) + case interface { + Get(*Term) *Term + }: + return v.Get(name) + case Set: + if v.Contains(name) { + return name + } + } + return nil +} + +// Hash returns the hash code of the Term's Value. Its Location +// is ignored. +func (term *Term) Hash() int { + return term.Value.Hash() +} + +// IsGround returns true if this term's Value is ground. +func (term *Term) IsGround() bool { + return term.Value.IsGround() +} + +// MarshalJSON returns the JSON encoding of the term. +// +// Specialized marshalling logic is required to include a type hint for Value. +func (term *Term) MarshalJSON() ([]byte, error) { + d := map[string]any{ + "type": ValueName(term.Value), + "value": term.Value, + } + jsonOptions := astJSON.GetOptions().MarshalOptions + if jsonOptions.IncludeLocation.Term { + if term.Location != nil { + d["location"] = term.Location + } + } + return json.Marshal(d) +} + +func (term *Term) String() string { + return term.Value.String() +} + +// UnmarshalJSON parses the byte array and stores the result in term. +// Specialized unmarshalling is required to handle Value and Location. +func (term *Term) UnmarshalJSON(bs []byte) error { + v := map[string]any{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + val, err := unmarshalValue(v) + if err != nil { + return err + } + term.Value = val + + if loc, ok := v["location"].(map[string]any); ok { + term.Location = &Location{} + err := unmarshalLocation(term.Location, loc) + if err != nil { + return err + } + } + return nil +} + +// Vars returns a VarSet with variables contained in this term. +func (term *Term) Vars() VarSet { + vis := NewVarVisitor() + vis.Walk(term) + return vis.vars +} + +// IsConstant returns true if the AST value is constant. +func IsConstant(v Value) bool { + found := false + vis := GenericVisitor{ + func(x any) bool { + switch x.(type) { + case Var, Ref, *ArrayComprehension, *ObjectComprehension, *SetComprehension, Call: + found = true + return true + } + return false + }, + } + vis.Walk(v) + return !found +} + +// IsComprehension returns true if the supplied value is a comprehension. +func IsComprehension(x Value) bool { + switch x.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension: + return true + } + return false +} + +// ContainsRefs returns true if the Value v contains refs. +func ContainsRefs(v any) bool { + found := false + WalkRefs(v, func(Ref) bool { + found = true + return found + }) + return found +} + +// ContainsComprehensions returns true if the Value v contains comprehensions. +func ContainsComprehensions(v any) bool { + found := false + WalkClosures(v, func(x any) bool { + switch x.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension: + found = true + return found + } + return found + }) + return found +} + +// ContainsClosures returns true if the Value v contains closures. +func ContainsClosures(v any) bool { + found := false + WalkClosures(v, func(x any) bool { + switch x.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension, *Every: + found = true + return found + } + return found + }) + return found +} + +// IsScalar returns true if the AST value is a scalar. +func IsScalar(v Value) bool { + switch v.(type) { + case String, Number, Boolean, Null: + return true + } + return false +} + +// Null represents the null value defined by JSON. +type Null struct{} + +var NullValue Value = Null{} + +// NullTerm creates a new Term with a Null value. +func NullTerm() *Term { + return &Term{Value: NullValue} +} + +// Equal returns true if the other term Value is also Null. +func (Null) Equal(other Value) bool { + switch other.(type) { + case Null: + return true + default: + return false + } +} + +// Compare compares null to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (Null) Compare(other Value) int { + if _, ok := other.(Null); ok { + return 0 + } + return -1 +} + +// Find returns the current value or a not found error. +func (Null) Find(path Ref) (Value, error) { + if len(path) == 0 { + return NullValue, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code for the Value. +func (Null) Hash() int { + return 0 +} + +// IsGround always returns true. +func (Null) IsGround() bool { + return true +} + +func (Null) String() string { + return "null" +} + +// Boolean represents a boolean value defined by JSON. +type Boolean bool + +// BooleanTerm creates a new Term with a Boolean value. +func BooleanTerm(b bool) *Term { + if b { + return &Term{Value: InternedTerm(true).Value} + } + return &Term{Value: InternedTerm(false).Value} +} + +// Equal returns true if the other Value is a Boolean and is equal. +func (bol Boolean) Equal(other Value) bool { + switch other := other.(type) { + case Boolean: + return bol == other + default: + return false + } +} + +// Compare compares bol to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (bol Boolean) Compare(other Value) int { + switch other := other.(type) { + case Boolean: + if bol == other { + return 0 + } + if !bol { + return -1 + } + return 1 + case Null: + return 1 + } + + return -1 +} + +// Find returns the current value or a not found error. +func (bol Boolean) Find(path Ref) (Value, error) { + if len(path) == 0 { + return InternedTerm(bool(bol)).Value, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code for the Value. +func (bol Boolean) Hash() int { + if bol { + return 1 + } + return 0 +} + +// IsGround always returns true. +func (Boolean) IsGround() bool { + return true +} + +func (bol Boolean) String() string { + return strconv.FormatBool(bool(bol)) +} + +// Number represents a numeric value as defined by JSON. +type Number json.Number + +// NumberTerm creates a new Term with a Number value. +func NumberTerm(n json.Number) *Term { + return &Term{Value: Number(n)} +} + +// IntNumberTerm creates a new Term with an integer Number value. +func IntNumberTerm(i int) *Term { + return &Term{Value: Number(strconv.Itoa(i))} +} + +// UIntNumberTerm creates a new Term with an unsigned integer Number value. +func UIntNumberTerm(u uint64) *Term { + return &Term{Value: uint64Number(u)} +} + +// FloatNumberTerm creates a new Term with a floating point Number value. +func FloatNumberTerm(f float64) *Term { + s := strconv.FormatFloat(f, 'g', -1, 64) + return &Term{Value: Number(s)} +} + +// Equal returns true if the other Value is a Number and is equal. +func (num Number) Equal(other Value) bool { + switch other := other.(type) { + case Number: + if n1, ok1 := num.Int64(); ok1 { + n2, ok2 := other.Int64() + if ok1 && ok2 { + return n1 == n2 + } + } + + return num.Compare(other) == 0 + default: + return false + } +} + +// Compare compares num to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (num Number) Compare(other Value) int { + // Optimize for the common case, as calling Compare allocates on heap. + if otherNum, yes := other.(Number); yes { + if ai, ok := num.Int64(); ok { + if bi, ok := otherNum.Int64(); ok { + if ai == bi { + return 0 + } + if ai < bi { + return -1 + } + return 1 + } + } + } + + return Compare(num, other) +} + +// Find returns the current value or a not found error. +func (num Number) Find(path Ref) (Value, error) { + if len(path) == 0 { + return num, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code for the Value. +func (num Number) Hash() int { + f, err := json.Number(num).Float64() + if err != nil { + bs := []byte(num) + h := xxhash.Sum64(bs) + return int(h) + } + return int(f) +} + +// Int returns the int representation of num if possible. +func (num Number) Int() (int, bool) { + i64, ok := num.Int64() + return int(i64), ok +} + +// Int64 returns the int64 representation of num if possible. +func (num Number) Int64() (int64, bool) { + i, err := json.Number(num).Int64() + if err != nil { + return 0, false + } + return i, true +} + +// Float64 returns the float64 representation of num if possible. +func (num Number) Float64() (float64, bool) { + f, err := json.Number(num).Float64() + if err != nil { + return 0, false + } + return f, true +} + +// IsGround always returns true. +func (Number) IsGround() bool { + return true +} + +// MarshalJSON returns JSON encoded bytes representing num. +func (num Number) MarshalJSON() ([]byte, error) { + return json.Marshal(json.Number(num)) +} + +func (num Number) String() string { + return string(num) +} + +func intNumber(i int) Number { + return Number(strconv.Itoa(i)) +} + +func int64Number(i int64) Number { + return Number(strconv.FormatInt(i, 10)) +} + +func uint64Number(u uint64) Number { + return Number(strconv.FormatUint(u, 10)) +} + +func floatNumber(f float64) Number { + return Number(strconv.FormatFloat(f, 'g', -1, 64)) +} + +// String represents a string value as defined by JSON. +type String string + +// StringTerm creates a new Term with a String value. +func StringTerm(s string) *Term { + return &Term{Value: String(s)} +} + +// Equal returns true if the other Value is a String and is equal. +func (str String) Equal(other Value) bool { + switch other := other.(type) { + case String: + return str == other + default: + return false + } +} + +// Compare compares str to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (str String) Compare(other Value) int { + // Optimize for the common case of one string being compared to another by + // using a direct comparison of values. This avoids the allocation performed + // when calling Compare and its any argument conversion. + if otherStr, ok := other.(String); ok { + if str == otherStr { + return 0 + } + if str < otherStr { + return -1 + } + return 1 + } + + return Compare(str, other) +} + +// Find returns the current value or a not found error. +func (str String) Find(path Ref) (Value, error) { + if len(path) == 0 { + return str, nil + } + return nil, errFindNotFound +} + +// IsGround always returns true. +func (String) IsGround() bool { + return true +} + +func (str String) String() string { + return strconv.Quote(string(str)) +} + +// Hash returns the hash code for the Value. +func (str String) Hash() int { + return int(xxhash.Sum64String(string(str))) +} + +// Var represents a variable as defined by the language. +type Var string + +// VarTerm creates a new Term with a Variable value. +func VarTerm(v string) *Term { + return &Term{Value: Var(v)} +} + +// Equal returns true if the other Value is a Variable and has the same value +// (name). +func (v Var) Equal(other Value) bool { + switch other := other.(type) { + case Var: + return v == other + default: + return false + } +} + +// Compare compares v to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (v Var) Compare(other Value) int { + if otherVar, ok := other.(Var); ok { + return strings.Compare(string(v), string(otherVar)) + } + return Compare(v, other) +} + +// Find returns the current value or a not found error. +func (v Var) Find(path Ref) (Value, error) { + if len(path) == 0 { + return v, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code for the Value. +func (v Var) Hash() int { + return int(xxhash.Sum64String(string(v))) +} + +// IsGround always returns false. +func (Var) IsGround() bool { + return false +} + +// IsWildcard returns true if this is a wildcard variable. +func (v Var) IsWildcard() bool { + return strings.HasPrefix(string(v), WildcardPrefix) +} + +// IsGenerated returns true if this variable was generated during compilation. +func (v Var) IsGenerated() bool { + return strings.HasPrefix(string(v), "__local") +} + +func (v Var) String() string { + // Special case for wildcard so that string representation is parseable. The + // parser mangles wildcard variables to make their names unique and uses an + // illegal variable name character (WildcardPrefix) to avoid conflicts. When + // we serialize the variable here, we need to make sure it's parseable. + if v.IsWildcard() { + return Wildcard.String() + } + return string(v) +} + +// Ref represents a reference as defined by the language. +type Ref []*Term + +// EmptyRef returns a new, empty reference. +func EmptyRef() Ref { + return Ref([]*Term{}) +} + +// PtrRef returns a new reference against the head for the pointer +// s. Path components in the pointer are unescaped. +func PtrRef(head *Term, s string) (Ref, error) { + s = strings.Trim(s, "/") + if s == "" { + return Ref{head}, nil + } + parts := strings.Split(s, "/") + if maxLen := math.MaxInt32; len(parts) >= maxLen { + return nil, fmt.Errorf("path too long: %s, %d > %d (max)", s, len(parts), maxLen) + } + ref := make(Ref, uint(len(parts))+1) + ref[0] = head + for i := range parts { + var err error + parts[i], err = url.PathUnescape(parts[i]) + if err != nil { + return nil, err + } + ref[i+1] = StringTerm(parts[i]) + } + return ref, nil +} + +// RefTerm creates a new Term with a Ref value. +func RefTerm(r ...*Term) *Term { + return &Term{Value: Ref(r)} +} + +// Append returns a copy of ref with the term appended to the end. +func (ref Ref) Append(term *Term) Ref { + n := len(ref) + dst := make(Ref, n+1) + copy(dst, ref) + dst[n] = term + return dst +} + +// Insert returns a copy of the ref with x inserted at pos. If pos < len(ref), +// existing elements are shifted to the right. If pos > len(ref)+1 this +// function panics. +func (ref Ref) Insert(x *Term, pos int) Ref { + switch { + case pos == len(ref): + return ref.Append(x) + case pos > len(ref)+1: + panic("illegal index") + } + cpy := make(Ref, len(ref)+1) + copy(cpy, ref[:pos]) + cpy[pos] = x + copy(cpy[pos+1:], ref[pos:]) + return cpy +} + +// Extend returns a copy of ref with the terms from other appended. The head of +// other will be converted to a string. +func (ref Ref) Extend(other Ref) Ref { + dst := make(Ref, len(ref)+len(other)) + copy(dst, ref) + + head := other[0].Copy() + head.Value = String(head.Value.(Var)) + offset := len(ref) + dst[offset] = head + + copy(dst[offset+1:], other[1:]) + return dst +} + +// Concat returns a ref with the terms appended. +func (ref Ref) Concat(terms []*Term) Ref { + if len(terms) == 0 { + return ref + } + cpy := make(Ref, len(ref)+len(terms)) + copy(cpy, ref) + copy(cpy[len(ref):], terms) + return cpy +} + +// Dynamic returns the offset of the first non-constant operand of ref. +func (ref Ref) Dynamic() int { + switch ref[0].Value.(type) { + case Call: + return 0 + } + for i := 1; i < len(ref); i++ { + if !IsConstant(ref[i].Value) { + return i + } + } + return -1 +} + +// Copy returns a deep copy of ref. +func (ref Ref) Copy() Ref { + return termSliceCopy(ref) +} + +// CopyNonGround returns a new ref with deep copies of the non-ground parts and shallow +// copies of the ground parts. This is a *much* cheaper operation than Copy for operations +// that only intend to modify (e.g. plug) the non-ground parts. The head element of the ref +// is always shallow copied. +func (ref Ref) CopyNonGround() Ref { + cpy := make(Ref, len(ref)) + cpy[0] = ref[0] + + for i := 1; i < len(ref); i++ { + if ref[i].Value.IsGround() { + cpy[i] = ref[i] + } else { + cpy[i] = ref[i].Copy() + } + } + + return cpy +} + +// Equal returns true if ref is equal to other. +func (ref Ref) Equal(other Value) bool { + switch o := other.(type) { + case Ref: + if len(ref) == len(o) { + for i := range ref { + if !ref[i].Equal(o[i]) { + return false + } + } + + return true + } + } + + return false +} + +// Compare compares ref to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (ref Ref) Compare(other Value) int { + if o, ok := other.(Ref); ok { + return termSliceCompare(ref, o) + } + + return Compare(ref, other) +} + +// Find returns the current value or a "not found" error. +func (ref Ref) Find(path Ref) (Value, error) { + if len(path) == 0 { + return ref, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code for the Value. +func (ref Ref) Hash() int { + return termSliceHash(ref) +} + +// HasPrefix returns true if the other ref is a prefix of this ref. +func (ref Ref) HasPrefix(other Ref) bool { + if len(other) > len(ref) { + return false + } + for i := range other { + if !ref[i].Equal(other[i]) { + return false + } + } + return true +} + +// ConstantPrefix returns the constant portion of the ref starting from the head. +func (ref Ref) ConstantPrefix() Ref { + i := ref.Dynamic() + if i < 0 { + return ref.Copy() + } + return ref[:i].Copy() +} + +func (ref Ref) StringPrefix() Ref { + for i := 1; i < len(ref); i++ { + switch ref[i].Value.(type) { + case String: // pass + default: // cut off + return ref[:i].Copy() + } + } + + return ref.Copy() +} + +// GroundPrefix returns the ground portion of the ref starting from the head. By +// definition, the head of the reference is always ground. +func (ref Ref) GroundPrefix() Ref { + if ref.IsGround() { + return ref + } + + prefix := make(Ref, 0, len(ref)) + + for i, x := range ref { + if i > 0 && !x.IsGround() { + break + } + prefix = append(prefix, x) + } + + return prefix +} + +func (ref Ref) DynamicSuffix() Ref { + i := ref.Dynamic() + if i < 0 { + return nil + } + return ref[i:] +} + +// IsGround returns true if all of the parts of the Ref are ground. +func (ref Ref) IsGround() bool { + if len(ref) == 0 { + return true + } + return termSliceIsGround(ref[1:]) +} + +// IsNested returns true if this ref contains other Refs. +func (ref Ref) IsNested() bool { + for _, x := range ref { + if _, ok := x.Value.(Ref); ok { + return true + } + } + return false +} + +// Ptr returns a slash-separated path string for this ref. If the ref +// contains non-string terms this function returns an error. Path +// components are escaped. +func (ref Ref) Ptr() (string, error) { + parts := make([]string, 0, len(ref)-1) + for _, term := range ref[1:] { + if str, ok := term.Value.(String); ok { + parts = append(parts, url.PathEscape(string(str))) + } else { + return "", errors.New("invalid path value type") + } + } + return strings.Join(parts, "/"), nil +} + +var varRegexp = regexp.MustCompile("^[[:alpha:]_][[:alpha:][:digit:]_]*$") + +func IsVarCompatibleString(s string) bool { + return varRegexp.MatchString(s) +} + +func (ref Ref) String() string { + if len(ref) == 0 { + return "" + } + + if len(ref) == 1 { + switch p := ref[0].Value.(type) { + case Var: + return p.String() + } + } + + sb := sbPool.Get() + defer sbPool.Put(sb) + + sb.Grow(10 * len(ref)) + sb.WriteString(ref[0].Value.String()) + + for _, p := range ref[1:] { + switch p := p.Value.(type) { + case String: + str := string(p) + if varRegexp.MatchString(str) && !IsKeyword(str) { + sb.WriteByte('.') + sb.WriteString(str) + } else { + sb.WriteByte('[') + // Determine whether we need the full JSON-escaped form + if strings.ContainsFunc(str, isControlOrBackslash) { + // only now pay the cost of expensive JSON-escaped form + sb.WriteString(p.String()) + } else { + sb.WriteByte('"') + sb.WriteString(str) + sb.WriteByte('"') + } + sb.WriteByte(']') + } + default: + sb.WriteByte('[') + sb.WriteString(p.String()) + sb.WriteByte(']') + } + } + + return sb.String() +} + +// OutputVars returns a VarSet containing variables that would be bound by evaluating +// this expression in isolation. +func (ref Ref) OutputVars() VarSet { + vis := NewVarVisitor().WithParams(VarVisitorParams{SkipRefHead: true}) + vis.WalkRef(ref) + return vis.Vars() +} + +func (ref Ref) toArray() *Array { + terms := make([]*Term, 0, len(ref)) + for _, term := range ref { + if _, ok := term.Value.(String); ok { + terms = append(terms, term) + } else { + terms = append(terms, InternedTerm(term.Value.String())) + } + } + return NewArray(terms...) +} + +// QueryIterator defines the interface for querying AST documents with references. +type QueryIterator func(map[Var]Value, Value) error + +// ArrayTerm creates a new Term with an Array value. +func ArrayTerm(a ...*Term) *Term { + return NewTerm(NewArray(a...)) +} + +// NewArray creates an Array with the terms provided. The array will +// use the provided term slice. +func NewArray(a ...*Term) *Array { + hs := make([]int, len(a)) + for i, e := range a { + hs[i] = e.Value.Hash() + } + arr := &Array{elems: a, hashs: hs, ground: termSliceIsGround(a)} + arr.rehash() + return arr +} + +// Array represents an array as defined by the language. Arrays are similar to the +// same types as defined by JSON with the exception that they can contain Vars +// and References. +type Array struct { + elems []*Term + hashs []int // element hashes + hash int + ground bool +} + +// Copy returns a deep copy of arr. +func (arr *Array) Copy() *Array { + cpy := make([]int, len(arr.elems)) + copy(cpy, arr.hashs) + return &Array{ + elems: termSliceCopy(arr.elems), + hashs: cpy, + hash: arr.hash, + ground: arr.IsGround()} +} + +// Equal returns true if arr is equal to other. +func (arr *Array) Equal(other Value) bool { + if arr == other { + return true + } + + if other, ok := other.(*Array); ok && len(arr.elems) == len(other.elems) { + for i := range arr.elems { + if !arr.elems[i].Equal(other.elems[i]) { + return false + } + } + return true + } + + return false +} + +// Compare compares arr to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (arr *Array) Compare(other Value) int { + if b, ok := other.(*Array); ok { + return termSliceCompare(arr.elems, b.elems) + } + + sortA := sortOrder(arr) + sortB := sortOrder(other) + + if sortA < sortB { + return -1 + } else if sortB < sortA { + return 1 + } + + return Compare(arr, other) +} + +// Find returns the value at the index or an out-of-range error. +func (arr *Array) Find(path Ref) (Value, error) { + if len(path) == 0 { + return arr, nil + } + num, ok := path[0].Value.(Number) + if !ok { + return nil, errFindNotFound + } + i, ok := num.Int() + if !ok { + return nil, errFindNotFound + } + if i < 0 || i >= arr.Len() { + return nil, errFindNotFound + } + + term := arr.Elem(i) + // Using Find on scalar values costs an allocation (type -> Value conversion) + // and since we already have the Value here, we can avoid that. + if len(path) == 1 && IsScalar(term.Value) { + return term.Value, nil + } + + return term.Value.Find(path[1:]) +} + +// Get returns the element at pos or nil if not possible. +func (arr *Array) Get(pos *Term) *Term { + num, ok := pos.Value.(Number) + if !ok { + return nil + } + + i, ok := num.Int() + if !ok { + return nil + } + + if i >= 0 && i < len(arr.elems) { + return arr.elems[i] + } + + return nil +} + +// Sorted returns a new Array that contains the sorted elements of arr. +func (arr *Array) Sorted() *Array { + cpy := make([]*Term, len(arr.elems)) + for i := range cpy { + cpy[i] = arr.elems[i] + } + + slices.SortFunc(cpy, TermValueCompare) + + a := NewArray(cpy...) + a.hashs = arr.hashs + return a +} + +// Hash returns the hash code for the Value. +func (arr *Array) Hash() int { + return arr.hash +} + +// IsGround returns true if all of the Array elements are ground. +func (arr *Array) IsGround() bool { + return arr.ground +} + +// MarshalJSON returns JSON encoded bytes representing arr. +func (arr *Array) MarshalJSON() ([]byte, error) { + if len(arr.elems) == 0 { + return []byte(`[]`), nil + } + return json.Marshal(arr.elems) +} + +func (arr *Array) String() string { + sb := sbPool.Get() + sb.Grow(len(arr.elems) * 16) + + defer sbPool.Put(sb) + + sb.WriteByte('[') + for i, e := range arr.elems { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(e.String()) + } + sb.WriteByte(']') + + return sb.String() +} + +// Len returns the number of elements in the array. +func (arr *Array) Len() int { + return len(arr.elems) +} + +// Elem returns the element i of arr. +func (arr *Array) Elem(i int) *Term { + return arr.elems[i] +} + +// Set sets the element i of arr. +func (arr *Array) Set(i int, v *Term) { + arr.set(i, v) +} + +// rehash updates the cached hash of arr. +func (arr *Array) rehash() { + arr.hash = 0 + for _, h := range arr.hashs { + arr.hash += h + } +} + +// set sets the element i of arr. +func (arr *Array) set(i int, v *Term) { + arr.ground = arr.ground && v.IsGround() + arr.elems[i] = v + arr.hashs[i] = v.Value.Hash() + arr.rehash() +} + +// Slice returns a slice of arr starting from i index to j. -1 +// indicates the end of the array. The returned value array is not a +// copy and any modifications to either of arrays may be reflected to +// the other. +func (arr *Array) Slice(i, j int) *Array { + var elems []*Term + var hashs []int + if j == -1 { + elems = arr.elems[i:] + hashs = arr.hashs[i:] + } else { + elems = arr.elems[i:j] + hashs = arr.hashs[i:j] + } + // If arr is ground, the slice is, too. + // If it's not, the slice could still be. + gr := arr.ground || termSliceIsGround(elems) + + s := &Array{elems: elems, hashs: hashs, ground: gr} + s.rehash() + return s +} + +// Iter calls f on each element in arr. If f returns an error, +// iteration stops and the return value is the error. +func (arr *Array) Iter(f func(*Term) error) error { + for i := range arr.elems { + if err := f(arr.elems[i]); err != nil { + return err + } + } + return nil +} + +// Until calls f on each element in arr. If f returns true, iteration stops. +func (arr *Array) Until(f func(*Term) bool) bool { + return slices.ContainsFunc(arr.elems, f) +} + +// Foreach calls f on each element in arr. +func (arr *Array) Foreach(f func(*Term)) { + for _, term := range arr.elems { + f(term) + } +} + +// Append appends a term to arr, returning the appended array. +func (arr *Array) Append(v *Term) *Array { + cpy := *arr + cpy.elems = append(arr.elems, v) + cpy.hashs = append(arr.hashs, v.Value.Hash()) + cpy.hash = arr.hash + v.Value.Hash() + cpy.ground = arr.ground && v.IsGround() + return &cpy +} + +// Set represents a set as defined by the language. +type Set interface { + Value + Len() int + Copy() Set + Diff(Set) Set + Intersect(Set) Set + Union(Set) Set + Add(*Term) + Iter(func(*Term) error) error + Until(func(*Term) bool) bool + Foreach(func(*Term)) + Contains(*Term) bool + Map(func(*Term) (*Term, error)) (Set, error) + Reduce(*Term, func(*Term, *Term) (*Term, error)) (*Term, error) + Sorted() *Array + Slice() []*Term +} + +// NewSet returns a new Set containing t. +func NewSet(t ...*Term) Set { + s := newset(len(t)) + for _, term := range t { + s.insert(term, false) + } + return s +} + +func newset(n int) *set { + var keys []*Term + if n > 0 { + keys = make([]*Term, 0, n) + } + return &set{ + elems: make(map[int]*Term, n), + keys: keys, + hash: 0, + ground: true, + sortGuard: sync.Once{}, + } +} + +// SetTerm returns a new Term representing a set containing terms t. +func SetTerm(t ...*Term) *Term { + set := NewSet(t...) + return &Term{ + Value: set, + } +} + +type set struct { + elems map[int]*Term + keys []*Term + hash int + ground bool + // Prevents race condition around sorting. + // We can avoid (the allocation cost of) using a pointer here as all + // methods of `set` use a pointer receiver, and the `sync.Once` value + // is never copied. + sortGuard sync.Once +} + +// Copy returns a deep copy of s. +func (s *set) Copy() Set { + terms := make([]*Term, len(s.keys)) + for i := range s.keys { + terms[i] = s.keys[i].Copy() + } + cpy := NewSet(terms...).(*set) + cpy.hash = s.hash + cpy.ground = s.ground + return cpy +} + +// IsGround returns true if all terms in s are ground. +func (s *set) IsGround() bool { + return s.ground +} + +// Hash returns a hash code for s. +func (s *set) Hash() int { + return s.hash +} + +func (s *set) String() string { + if s.Len() == 0 { + return "set()" + } + + sb := sbPool.Get() + sb.Grow(s.Len() * 16) + + defer sbPool.Put(sb) + + sb.WriteByte('{') + for i := range s.sortedKeys() { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(s.keys[i].Value.String()) + } + sb.WriteByte('}') + + return sb.String() +} + +func (s *set) sortedKeys() []*Term { + s.sortGuard.Do(func() { + slices.SortFunc(s.keys, TermValueCompare) + }) + return s.keys +} + +// Compare compares s to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (s *set) Compare(other Value) int { + o1 := sortOrder(s) + o2 := sortOrder(other) + if o1 < o2 { + return -1 + } else if o1 > o2 { + return 1 + } + t := other.(*set) + return termSliceCompare(s.sortedKeys(), t.sortedKeys()) +} + +// Find returns the set or dereferences the element itself. +func (s *set) Find(path Ref) (Value, error) { + if len(path) == 0 { + return s, nil + } + if !s.Contains(path[0]) { + return nil, errFindNotFound + } + return path[0].Value.Find(path[1:]) +} + +// Diff returns elements in s that are not in other. +func (s *set) Diff(other Set) Set { + if s.Compare(other) == 0 { + return NewSet() + } + + terms := make([]*Term, 0, len(s.keys)) + for _, term := range s.sortedKeys() { + if !other.Contains(term) { + terms = append(terms, term) + } + } + + return NewSet(terms...) +} + +// Intersect returns the set containing elements in both s and other. +func (s *set) Intersect(other Set) Set { + o := other.(*set) + n, m := s.Len(), o.Len() + ss := s + so := o + if m < n { + ss = o + so = s + n = m + } + + terms := make([]*Term, 0, n) + for _, term := range ss.sortedKeys() { + if so.Contains(term) { + terms = append(terms, term) + } + } + + return NewSet(terms...) +} + +// Union returns the set containing all elements of s and other. +func (s *set) Union(other Set) Set { + r := NewSet() + s.Foreach(r.Add) + other.Foreach(r.Add) + return r +} + +// Add updates s to include t. +func (s *set) Add(t *Term) { + s.insert(t, true) +} + +// Iter calls f on each element in s. If f returns an error, iteration stops +// and the return value is the error. +func (s *set) Iter(f func(*Term) error) error { + for _, term := range s.sortedKeys() { + if err := f(term); err != nil { + return err + } + } + return nil +} + +// Until calls f on each element in s. If f returns true, iteration stops. +func (s *set) Until(f func(*Term) bool) bool { + return slices.ContainsFunc(s.sortedKeys(), f) +} + +// Foreach calls f on each element in s. +func (s *set) Foreach(f func(*Term)) { + for _, term := range s.sortedKeys() { + f(term) + } +} + +// Map returns a new Set obtained by applying f to each value in s. +func (s *set) Map(f func(*Term) (*Term, error)) (Set, error) { + mapped := make([]*Term, 0, len(s.keys)) + for _, x := range s.sortedKeys() { + term, err := f(x) + if err != nil { + return nil, err + } + mapped = append(mapped, term) + } + return NewSet(mapped...), nil +} + +// Reduce returns a Term produced by applying f to each value in s. The first +// argument to f is the reduced value (starting with i) and the second argument +// to f is the element in s. +func (s *set) Reduce(i *Term, f func(*Term, *Term) (*Term, error)) (*Term, error) { + err := s.Iter(func(x *Term) error { + var err error + i, err = f(i, x) + if err != nil { + return err + } + return nil + }) + return i, err +} + +// Contains returns true if t is in s. +func (s *set) Contains(t *Term) bool { + return s.get(t) != nil +} + +// Len returns the number of elements in the set. +func (s *set) Len() int { + return len(s.keys) +} + +// MarshalJSON returns JSON encoded bytes representing s. +func (s *set) MarshalJSON() ([]byte, error) { + if s.keys == nil { + return []byte(`[]`), nil + } + return json.Marshal(s.sortedKeys()) +} + +// Sorted returns an Array that contains the sorted elements of s. +func (s *set) Sorted() *Array { + cpy := make([]*Term, len(s.keys)) + copy(cpy, s.sortedKeys()) + return NewArray(cpy...) +} + +// Slice returns a slice of terms contained in the set. +func (s *set) Slice() []*Term { + return s.sortedKeys() +} + +// NOTE(philipc): We assume a many-readers, single-writer model here. +// This method should NOT be used concurrently, or else we risk data races. +func (s *set) insert(x *Term, resetSortGuard bool) { + hash := x.Hash() + insertHash := hash + // This `equal` utility is duplicated and manually inlined a number of + // time in this file. Inlining it avoids heap allocations, so it makes + // a big performance difference: some operations like lookup become twice + // as slow without it. + var equal func(v Value) bool + + switch x := x.Value.(type) { + case Null, Boolean, String, Var: + equal = func(y Value) bool { return x == y } + case Number: + if xi, err := json.Number(x).Int64(); err == nil { + equal = func(y Value) bool { + if y, ok := y.(Number); ok { + if yi, err := json.Number(y).Int64(); err == nil { + return xi == yi + } + } + + return false + } + break + } + + // We use big.Rat for comparing big numbers. + // It replaces big.Float due to following reason: + // big.Float comes with a default precision of 64, and setting a + // larger precision results in more memory being allocated + // (regardless of the actual number we are parsing with SetString). + // + // Note: If we're so close to zero that big.Float says we are zero, do + // *not* big.Rat).SetString on the original string it'll potentially + // take very long. + var a *big.Rat + fa, ok := new(big.Float).SetString(string(x)) + if !ok { + panic("illegal value") + } + if fa.IsInt() { + if i, _ := fa.Int64(); i == 0 { + a = new(big.Rat).SetInt64(0) + } + } + if a == nil { + a, ok = new(big.Rat).SetString(string(x)) + if !ok { + panic("illegal value") + } + } + + equal = func(b Value) bool { + if bNum, ok := b.(Number); ok { + var b *big.Rat + fb, ok := new(big.Float).SetString(string(bNum)) + if !ok { + panic("illegal value") + } + if fb.IsInt() { + if i, _ := fb.Int64(); i == 0 { + b = new(big.Rat).SetInt64(0) + } + } + if b == nil { + b, ok = new(big.Rat).SetString(string(bNum)) + if !ok { + panic("illegal value") + } + } + + return a.Cmp(b) == 0 + } + + return false + } + default: + equal = func(y Value) bool { return Compare(x, y) == 0 } + } + + for curr, ok := s.elems[insertHash]; ok; { + if equal(curr.Value) { + return + } + + insertHash++ + curr, ok = s.elems[insertHash] + } + + s.elems[insertHash] = x + // O(1) insertion, but we'll have to re-sort the keys later. + s.keys = append(s.keys, x) + + if resetSortGuard { + // Reset the sync.Once instance. + // See https://github.com/golang/go/issues/25955 for why we do it this way. + // Note that this will always be the case when external code calls insert via + // Add, or otherwise. Internal code may however benefit from not having to + // re-create this pointer when it's known not to be needed. + s.sortGuard = sync.Once{} + } + + s.hash += hash + s.ground = s.ground && x.IsGround() +} + +func (s *set) get(x *Term) *Term { + hash := x.Hash() + // This `equal` utility is duplicated and manually inlined a number of + // time in this file. Inlining it avoids heap allocations, so it makes + // a big performance difference: some operations like lookup become twice + // as slow without it. + var equal func(v Value) bool + + switch x := x.Value.(type) { + case Null, Boolean, String, Var: + equal = func(y Value) bool { return x == y } + case Number: + if xi, err := json.Number(x).Int64(); err == nil { + equal = func(y Value) bool { + if y, ok := y.(Number); ok { + if yi, err := json.Number(y).Int64(); err == nil { + return xi == yi + } + } + + return false + } + break + } + + // We use big.Rat for comparing big numbers. + // It replaces big.Float due to following reason: + // big.Float comes with a default precision of 64, and setting a + // larger precision results in more memory being allocated + // (regardless of the actual number we are parsing with SetString). + // + // Note: If we're so close to zero that big.Float says we are zero, do + // *not* big.Rat).SetString on the original string it'll potentially + // take very long. + var a *big.Rat + fa, ok := new(big.Float).SetString(string(x)) + if !ok { + panic("illegal value") + } + if fa.IsInt() { + if i, _ := fa.Int64(); i == 0 { + a = new(big.Rat).SetInt64(0) + } + } + if a == nil { + a, ok = new(big.Rat).SetString(string(x)) + if !ok { + panic("illegal value") + } + } + + equal = func(b Value) bool { + if bNum, ok := b.(Number); ok { + var b *big.Rat + fb, ok := new(big.Float).SetString(string(bNum)) + if !ok { + panic("illegal value") + } + if fb.IsInt() { + if i, _ := fb.Int64(); i == 0 { + b = new(big.Rat).SetInt64(0) + } + } + if b == nil { + b, ok = new(big.Rat).SetString(string(bNum)) + if !ok { + panic("illegal value") + } + } + + return a.Cmp(b) == 0 + } + return false + + } + + default: + equal = func(y Value) bool { return Compare(x, y) == 0 } + } + + for curr, ok := s.elems[hash]; ok; { + if equal(curr.Value) { + return curr + } + + hash++ + curr, ok = s.elems[hash] + } + return nil +} + +// Object represents an object as defined by the language. +type Object interface { + Value + Len() int + Get(*Term) *Term + Copy() Object + Insert(*Term, *Term) + Iter(func(*Term, *Term) error) error + Until(func(*Term, *Term) bool) bool + Foreach(func(*Term, *Term)) + Map(func(*Term, *Term) (*Term, *Term, error)) (Object, error) + Diff(other Object) Object + Intersect(other Object) [][3]*Term + Merge(other Object) (Object, bool) + MergeWith(other Object, conflictResolver func(v1, v2 *Term) (*Term, bool)) (Object, bool) + Filter(filter Object) (Object, error) + Keys() []*Term + KeysIterator() ObjectKeysIterator + get(k *Term) *objectElem // To prevent external implementations +} + +// NewObject creates a new Object with t. +func NewObject(t ...[2]*Term) Object { + obj := newobject(len(t)) + for i := range t { + obj.insert(t[i][0], t[i][1], false) + } + return obj +} + +// ObjectTerm creates a new Term with an Object value. +func ObjectTerm(o ...[2]*Term) *Term { + return &Term{Value: NewObject(o...)} +} + +func LazyObject(blob map[string]any) Object { + return &lazyObj{native: blob, cache: map[string]Value{}} +} + +type lazyObj struct { + strict Object + cache map[string]Value + native map[string]any +} + +func (l *lazyObj) force() Object { + if l.strict == nil { + l.strict = MustInterfaceToValue(l.native).(Object) + // NOTE(jf): a possible performance improvement here would be to check how many + // entries have been realized to AST in the cache, and if some threshold compared to the + // total number of keys is exceeded, realize the remaining entries and set l.strict to l.cache. + l.cache = map[string]Value{} // We don't need the cache anymore; drop it to free up memory. + } + return l.strict +} + +func (l *lazyObj) Compare(other Value) int { + o1 := sortOrder(l) + o2 := sortOrder(other) + if o1 < o2 { + return -1 + } else if o2 < o1 { + return 1 + } + return l.force().Compare(other) +} + +func (l *lazyObj) Copy() Object { + return l +} + +func (l *lazyObj) Diff(other Object) Object { + return l.force().Diff(other) +} + +func (l *lazyObj) Intersect(other Object) [][3]*Term { + return l.force().Intersect(other) +} + +func (l *lazyObj) Iter(f func(*Term, *Term) error) error { + return l.force().Iter(f) +} + +func (l *lazyObj) Until(f func(*Term, *Term) bool) bool { + // NOTE(sr): there could be benefits in not forcing here -- if we abort because + // `f` returns true, we could save us from converting the rest of the object. + return l.force().Until(f) +} + +func (l *lazyObj) Foreach(f func(*Term, *Term)) { + l.force().Foreach(f) +} + +func (l *lazyObj) Filter(filter Object) (Object, error) { + return l.force().Filter(filter) +} + +func (l *lazyObj) Map(f func(*Term, *Term) (*Term, *Term, error)) (Object, error) { + return l.force().Map(f) +} + +func (l *lazyObj) MarshalJSON() ([]byte, error) { + return l.force().(*object).MarshalJSON() +} + +func (l *lazyObj) Merge(other Object) (Object, bool) { + return l.force().Merge(other) +} + +func (l *lazyObj) MergeWith(other Object, conflictResolver func(v1, v2 *Term) (*Term, bool)) (Object, bool) { + return l.force().MergeWith(other, conflictResolver) +} + +func (l *lazyObj) Len() int { + return len(l.native) +} + +func (l *lazyObj) String() string { + return l.force().String() +} + +// get is merely there to implement the Object interface -- `get` there serves the +// purpose of prohibiting external implementations. It's never called for lazyObj. +func (*lazyObj) get(*Term) *objectElem { + return nil +} + +func (l *lazyObj) Get(k *Term) *Term { + if l.strict != nil { + return l.strict.Get(k) + } + if s, ok := k.Value.(String); ok { + if v, ok := l.cache[string(s)]; ok { + return NewTerm(v) + } + + if val, ok := l.native[string(s)]; ok { + var converted Value + switch val := val.(type) { + case map[string]any: + converted = LazyObject(val) + default: + converted = MustInterfaceToValue(val) + } + l.cache[string(s)] = converted + return NewTerm(converted) + } + } + return nil +} + +func (l *lazyObj) Insert(k, v *Term) { + l.force().Insert(k, v) +} + +func (*lazyObj) IsGround() bool { + return true +} + +func (l *lazyObj) Hash() int { + return l.force().Hash() +} + +func (l *lazyObj) Keys() []*Term { + if l.strict != nil { + return l.strict.Keys() + } + ret := make([]*Term, 0, len(l.native)) + for k := range l.native { + ret = append(ret, StringTerm(k)) + } + slices.SortFunc(ret, TermValueCompare) + + return ret +} + +func (l *lazyObj) KeysIterator() ObjectKeysIterator { + return &lazyObjKeysIterator{keys: l.Keys()} +} + +type lazyObjKeysIterator struct { + current int + keys []*Term +} + +func (ki *lazyObjKeysIterator) Next() (*Term, bool) { + if ki.current == len(ki.keys) { + return nil, false + } + ki.current++ + return ki.keys[ki.current-1], true +} + +func (l *lazyObj) Find(path Ref) (Value, error) { + if l.strict != nil { + return l.strict.Find(path) + } + if len(path) == 0 { + return l, nil + } + if p0, ok := path[0].Value.(String); ok { + if v, ok := l.cache[string(p0)]; ok { + return v.Find(path[1:]) + } + + if v, ok := l.native[string(p0)]; ok { + var converted Value + switch v := v.(type) { + case map[string]any: + converted = LazyObject(v) + default: + converted = MustInterfaceToValue(v) + } + l.cache[string(p0)] = converted + return converted.Find(path[1:]) + } + } + return nil, errFindNotFound +} + +type object struct { + elems map[int]*objectElem + keys objectElemSlice + ground int // number of key and value grounds. Counting is + // required to support insert's key-value replace. + hash int + sortGuard sync.Once // Prevents race condition around sorting. +} + +func newobject(n int) *object { + var keys objectElemSlice + if n > 0 { + keys = make(objectElemSlice, 0, n) + } + return &object{ + elems: make(map[int]*objectElem, n), + keys: keys, + ground: 0, + hash: 0, + sortGuard: sync.Once{}, + } +} + +type objectElem struct { + key *Term + value *Term + next *objectElem +} + +type objectElemSlice []*objectElem + +func (s objectElemSlice) Less(i, j int) bool { return Compare(s[i].key.Value, s[j].key.Value) < 0 } +func (s objectElemSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s objectElemSlice) Len() int { return len(s) } + +// Item is a helper for constructing an tuple containing two Terms +// representing a key/value pair in an Object. +func Item(key, value *Term) [2]*Term { + return [2]*Term{key, value} +} + +func (obj *object) sortedKeys() objectElemSlice { + obj.sortGuard.Do(func() { + slices.SortFunc(obj.keys, func(a, b *objectElem) int { + return a.key.Value.Compare(b.key.Value) + }) + }) + return obj.keys +} + +// Compare compares obj to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (obj *object) Compare(other Value) int { + if x, ok := other.(*lazyObj); ok { + other = x.force() + } + o1 := sortOrder(obj) + o2 := sortOrder(other) + if o1 < o2 { + return -1 + } else if o2 < o1 { + return 1 + } + a := obj + b := other.(*object) + // Ensure that keys are in canonical sorted order before use! + akeys := a.sortedKeys() + bkeys := b.sortedKeys() + minLen := len(akeys) + if len(b.keys) < len(akeys) { + minLen = len(bkeys) + } + for i := range minLen { + keysCmp := Compare(akeys[i].key, bkeys[i].key) + if keysCmp < 0 { + return -1 + } + if keysCmp > 0 { + return 1 + } + valA := akeys[i].value + valB := bkeys[i].value + valCmp := Compare(valA, valB) + if valCmp != 0 { + return valCmp + } + } + if len(akeys) < len(bkeys) { + return -1 + } + if len(bkeys) < len(akeys) { + return 1 + } + return 0 +} + +// Find returns the value at the key or undefined. +func (obj *object) Find(path Ref) (Value, error) { + if len(path) == 0 { + return obj, nil + } + term := obj.Get(path[0]) + if term == nil { + return nil, errFindNotFound + } + // Using Find on scalar values costs an allocation (type -> Value conversion) + // and since we already have the Value here, we can avoid that. + if len(path) == 1 && IsScalar(term.Value) { + return term.Value, nil + } + + return term.Value.Find(path[1:]) +} + +func (obj *object) Insert(k, v *Term) { + obj.insert(k, v, true) +} + +// Get returns the value of k in obj if k exists, otherwise nil. +func (obj *object) Get(k *Term) *Term { + if elem := obj.get(k); elem != nil { + return elem.value + } + return nil +} + +// Hash returns the hash code for the Value. +func (obj *object) Hash() int { + return obj.hash +} + +// IsGround returns true if all of the Object key/value pairs are ground. +func (obj *object) IsGround() bool { + return obj.ground == 2*len(obj.keys) +} + +// Copy returns a deep copy of obj. +func (obj *object) Copy() Object { + cpy, _ := obj.Map(func(k, v *Term) (*Term, *Term, error) { + return k.Copy(), v.Copy(), nil + }) + cpy.(*object).hash = obj.hash + return cpy +} + +// Diff returns a new Object that contains only the key/value pairs that exist in obj. +func (obj *object) Diff(other Object) Object { + r := newobject(obj.Len()) + for _, node := range obj.sortedKeys() { + if other.Get(node.key) == nil { + r.insert(node.key, node.value, false) + } + } + return r +} + +// Intersect returns a slice of term triplets that represent the intersection of keys +// between obj and other. For each intersecting key, the values from obj and other are included +// as the last two terms in the triplet (respectively). +func (obj *object) Intersect(other Object) [][3]*Term { + r := [][3]*Term{} + obj.Foreach(func(k, v *Term) { + if v2 := other.Get(k); v2 != nil { + r = append(r, [3]*Term{k, v, v2}) + } + }) + return r +} + +// Iter calls the function f for each key-value pair in the object. If f +// returns an error, iteration stops and the error is returned. +func (obj *object) Iter(f func(*Term, *Term) error) error { + for _, node := range obj.sortedKeys() { + if err := f(node.key, node.value); err != nil { + return err + } + } + return nil +} + +// Until calls f for each key-value pair in the object. If f returns +// true, iteration stops and Until returns true. Otherwise, return +// false. +func (obj *object) Until(f func(*Term, *Term) bool) bool { + for _, node := range obj.sortedKeys() { + if f(node.key, node.value) { + return true + } + } + return false +} + +// Foreach calls f for each key-value pair in the object. +func (obj *object) Foreach(f func(*Term, *Term)) { + for _, node := range obj.sortedKeys() { + f(node.key, node.value) + } +} + +// Map returns a new Object constructed by mapping each element in the object +// using the function f. If f returns an error, the error is returned by Map. +// If f return a nil key, the element is skipped. +func (obj *object) Map(f func(*Term, *Term) (*Term, *Term, error)) (Object, error) { + cpy := newobject(obj.Len()) + for _, node := range obj.sortedKeys() { + k, v, err := f(node.key, node.value) + if err != nil { + return nil, err + } + if k != nil { + cpy.insert(k, v, false) + } + } + return cpy, nil +} + +// Keys returns the keys of obj. +func (obj *object) Keys() []*Term { + keys := make([]*Term, len(obj.keys)) + + for i, elem := range obj.sortedKeys() { + keys[i] = elem.key + } + + return keys +} + +// Returns an iterator over the obj's keys. +func (obj *object) KeysIterator() ObjectKeysIterator { + return newobjectKeysIterator(obj) +} + +// MarshalJSON returns JSON encoded bytes representing obj. +func (obj *object) MarshalJSON() ([]byte, error) { + sl := make([][2]*Term, obj.Len()) + for i, node := range obj.sortedKeys() { + sl[i] = Item(node.key, node.value) + } + return json.Marshal(sl) +} + +// Merge returns a new Object containing the non-overlapping keys of obj and other. If there are +// overlapping keys between obj and other, the values of associated with the keys are merged. Only +// objects can be merged with other objects. If the values cannot be merged, the second turn value +// will be false. +func (obj *object) Merge(other Object) (Object, bool) { + return obj.MergeWith(other, func(v1, v2 *Term) (*Term, bool) { + obj1, ok1 := v1.Value.(Object) + obj2, ok2 := v2.Value.(Object) + if !ok1 || !ok2 { + return nil, true + } + obj3, ok := obj1.Merge(obj2) + if !ok { + return nil, true + } + return NewTerm(obj3), false + }) +} + +// MergeWith returns a new Object containing the merged keys of obj and other. +// If there are overlapping keys between obj and other, the conflictResolver +// is called. The conflictResolver can return a merged value and a boolean +// indicating if the merge has failed and should stop. +func (obj *object) MergeWith(other Object, conflictResolver func(v1, v2 *Term) (*Term, bool)) (Object, bool) { + result := NewObject() + stop := obj.Until(func(k, v *Term) bool { + v2 := other.Get(k) + // The key didn't exist in other, keep the original value + if v2 == nil { + result.Insert(k, v) + return false + } + + // The key exists in both, resolve the conflict if possible + merged, stop := conflictResolver(v, v2) + if !stop { + result.Insert(k, merged) + } + return stop + }) + + if stop { + return nil, false + } + + // Copy in any values from other for keys that don't exist in obj + other.Foreach(func(k, v *Term) { + if v2 := obj.Get(k); v2 == nil { + result.Insert(k, v) + } + }) + return result, true +} + +// Filter returns a new object from values in obj where the keys are +// found in filter. Array indices for values can be specified as +// number strings. +func (obj *object) Filter(filter Object) (Object, error) { + filtered, err := filterObject(obj, filter) + if err != nil { + return nil, err + } + return filtered.(Object), nil +} + +// Len returns the number of elements in the object. +func (obj *object) Len() int { + return len(obj.keys) +} + +func (obj *object) String() string { + sb := sbPool.Get() + sb.Grow(obj.Len() * 32) + + defer sbPool.Put(sb) + + sb.WriteByte('{') + + for i, elem := range obj.sortedKeys() { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(elem.key.String()) + sb.WriteString(": ") + sb.WriteString(elem.value.String()) + } + sb.WriteByte('}') + + return sb.String() +} + +func (obj *object) get(k *Term) *objectElem { + hash := k.Hash() + + // This `equal` utility is duplicated and manually inlined a number of + // time in this file. Inlining it avoids heap allocations, so it makes + // a big performance difference: some operations like lookup become twice + // as slow without it. + var equal func(v Value) bool + + switch x := k.Value.(type) { + case Null, Boolean, String, Var: + equal = func(y Value) bool { return x == y } + case Number: + if xi, ok := x.Int64(); ok { + equal = func(y Value) bool { + if y, ok := y.(Number); ok { + if yi, ok := y.Int64(); ok { + return xi == yi + } + } + + return false + } + break + } + + // We use big.Rat for comparing big numbers. + // It replaces big.Float due to following reason: + // big.Float comes with a default precision of 64, and setting a + // larger precision results in more memory being allocated + // (regardless of the actual number we are parsing with SetString). + // + // Note: If we're so close to zero that big.Float says we are zero, do + // *not* big.Rat).SetString on the original string it'll potentially + // take very long. + var a *big.Rat + fa, ok := new(big.Float).SetString(string(x)) + if !ok { + panic("illegal value") + } + if fa.IsInt() { + if i, _ := fa.Int64(); i == 0 { + a = new(big.Rat).SetInt64(0) + } + } + if a == nil { + a, ok = new(big.Rat).SetString(string(x)) + if !ok { + panic("illegal value") + } + } + + equal = func(b Value) bool { + if bNum, ok := b.(Number); ok { + var b *big.Rat + fb, ok := new(big.Float).SetString(string(bNum)) + if !ok { + panic("illegal value") + } + if fb.IsInt() { + if i, _ := fb.Int64(); i == 0 { + b = new(big.Rat).SetInt64(0) + } + } + if b == nil { + b, ok = new(big.Rat).SetString(string(bNum)) + if !ok { + panic("illegal value") + } + } + + return a.Cmp(b) == 0 + } + + return false + } + default: + equal = func(y Value) bool { return Compare(x, y) == 0 } + } + + for curr := obj.elems[hash]; curr != nil; curr = curr.next { + if equal(curr.key.Value) { + return curr + } + } + return nil +} + +// NOTE(philipc): We assume a many-readers, single-writer model here. +// This method should NOT be used concurrently, or else we risk data races. +func (obj *object) insert(k, v *Term, resetSortGuard bool) { + hash := k.Hash() + head := obj.elems[hash] + // This `equal` utility is duplicated and manually inlined a number of + // time in this file. Inlining it avoids heap allocations, so it makes + // a big performance difference: some operations like lookup become twice + // as slow without it. + var equal func(v Value) bool + + switch x := k.Value.(type) { + case Null, Boolean, String, Var: + equal = func(y Value) bool { return x == y } + case Number: + if xi, err := json.Number(x).Int64(); err == nil { + equal = func(y Value) bool { + if y, ok := y.(Number); ok { + if yi, err := json.Number(y).Int64(); err == nil { + return xi == yi + } + } + + return false + } + break + } + + // We use big.Rat for comparing big numbers. + // It replaces big.Float due to following reason: + // big.Float comes with a default precision of 64, and setting a + // larger precision results in more memory being allocated + // (regardless of the actual number we are parsing with SetString). + // + // Note: If we're so close to zero that big.Float says we are zero, do + // *not* big.Rat).SetString on the original string it'll potentially + // take very long. + var a *big.Rat + fa, ok := new(big.Float).SetString(string(x)) + if !ok { + panic("illegal value") + } + if fa.IsInt() { + if i, _ := fa.Int64(); i == 0 { + a = new(big.Rat).SetInt64(0) + } + } + if a == nil { + a, ok = new(big.Rat).SetString(string(x)) + if !ok { + panic("illegal value") + } + } + + equal = func(b Value) bool { + if bNum, ok := b.(Number); ok { + var b *big.Rat + fb, ok := new(big.Float).SetString(string(bNum)) + if !ok { + panic("illegal value") + } + if fb.IsInt() { + if i, _ := fb.Int64(); i == 0 { + b = new(big.Rat).SetInt64(0) + } + } + if b == nil { + b, ok = new(big.Rat).SetString(string(bNum)) + if !ok { + panic("illegal value") + } + } + + return a.Cmp(b) == 0 + } + + return false + } + default: + equal = func(y Value) bool { return Compare(x, y) == 0 } + } + + for curr := head; curr != nil; curr = curr.next { + if equal(curr.key.Value) { + // The ground bit of the value may change in + // replace, hence adjust the counter per old + // and new value. + + if curr.value.IsGround() { + obj.ground-- + } + if v.IsGround() { + obj.ground++ + } + + curr.value = v + + obj.rehash() + return + } + } + elem := &objectElem{ + key: k, + value: v, + next: head, + } + obj.elems[hash] = elem + // O(1) insertion, but we'll have to re-sort the keys later. + obj.keys = append(obj.keys, elem) + + if resetSortGuard { + // Reset the sync.Once instance. + // See https://github.com/golang/go/issues/25955 for why we do it this way. + // Note that this will always be the case when external code calls insert via + // Add, or otherwise. Internal code may however benefit from not having to + // re-create this when it's known not to be needed. + obj.sortGuard = sync.Once{} + } + + obj.hash += hash + v.Hash() + + if k.IsGround() { + obj.ground++ + } + if v.IsGround() { + obj.ground++ + } +} + +func (obj *object) rehash() { + // obj.keys is considered truth, from which obj.hash and obj.elems are recalculated. + + obj.hash = 0 + obj.elems = make(map[int]*objectElem, len(obj.keys)) + + for _, elem := range obj.keys { + hash := elem.key.Hash() + obj.hash += hash + elem.value.Hash() + obj.elems[hash] = elem + } +} + +func filterObject(o Value, filter Value) (Value, error) { + if (Null{}).Equal(filter) { + return o, nil + } + + filteredObj, ok := filter.(*object) + if !ok { + return nil, fmt.Errorf("invalid filter value %q, expected an object", filter) + } + + switch v := o.(type) { + case String, Number, Boolean, Null: + return o, nil + case *Array: + values := NewArray() + for i := range v.Len() { + subFilter := filteredObj.Get(InternedIntegerString(i)) + if subFilter != nil { + filteredValue, err := filterObject(v.Elem(i).Value, subFilter.Value) + if err != nil { + return nil, err + } + values = values.Append(NewTerm(filteredValue)) + } + } + return values, nil + case Set: + terms := make([]*Term, 0, v.Len()) + for _, t := range v.Slice() { + if filteredObj.Get(t) != nil { + filteredValue, err := filterObject(t.Value, filteredObj.Get(t).Value) + if err != nil { + return nil, err + } + terms = append(terms, NewTerm(filteredValue)) + } + } + return NewSet(terms...), nil + case *object: + values := NewObject() + + iterObj := v + other := filteredObj + if v.Len() < filteredObj.Len() { + iterObj = filteredObj + other = v + } + + 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 { + return err + } + values.Insert(key, NewTerm(filteredValue)) + } + return nil + }) + return values, err + default: + return nil, fmt.Errorf("invalid object value type %q", v) + } +} + +// NOTE(philipc): The only way to get an ObjectKeyIterator should be +// from an Object. This ensures that the iterator can have implementation- +// specific details internally, with no contracts except to the very +// limited interface. +type ObjectKeysIterator interface { + Next() (*Term, bool) +} + +type objectKeysIterator struct { + obj *object + numKeys int + index int +} + +func newobjectKeysIterator(o *object) ObjectKeysIterator { + return &objectKeysIterator{ + obj: o, + numKeys: o.Len(), + index: 0, + } +} + +func (oki *objectKeysIterator) Next() (*Term, bool) { + if oki.index == oki.numKeys || oki.numKeys == 0 { + return nil, false + } + oki.index++ + return oki.obj.sortedKeys()[oki.index-1].key, true +} + +// ArrayComprehension represents an array comprehension as defined in the language. +type ArrayComprehension struct { + Term *Term `json:"term"` + Body Body `json:"body"` +} + +// ArrayComprehensionTerm creates a new Term with an ArrayComprehension value. +func ArrayComprehensionTerm(term *Term, body Body) *Term { + return &Term{ + Value: &ArrayComprehension{ + Term: term, + Body: body, + }, + } +} + +// Copy returns a deep copy of ac. +func (ac *ArrayComprehension) Copy() *ArrayComprehension { + cpy := *ac + cpy.Body = ac.Body.Copy() + cpy.Term = ac.Term.Copy() + return &cpy +} + +// Equal returns true if ac is equal to other. +func (ac *ArrayComprehension) Equal(other Value) bool { + return Compare(ac, other) == 0 +} + +// Compare compares ac to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (ac *ArrayComprehension) Compare(other Value) int { + return Compare(ac, other) +} + +// Find returns the current value or a not found error. +func (ac *ArrayComprehension) Find(path Ref) (Value, error) { + if len(path) == 0 { + return ac, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code of the Value. +func (ac *ArrayComprehension) Hash() int { + return ac.Term.Hash() + ac.Body.Hash() +} + +// IsGround returns true if the Term and Body are ground. +func (ac *ArrayComprehension) IsGround() bool { + return ac.Term.IsGround() && ac.Body.IsGround() +} + +func (ac *ArrayComprehension) String() string { + return "[" + ac.Term.String() + " | " + ac.Body.String() + "]" +} + +// ObjectComprehension represents an object comprehension as defined in the language. +type ObjectComprehension struct { + Key *Term `json:"key"` + Value *Term `json:"value"` + Body Body `json:"body"` +} + +// ObjectComprehensionTerm creates a new Term with an ObjectComprehension value. +func ObjectComprehensionTerm(key, value *Term, body Body) *Term { + return &Term{ + Value: &ObjectComprehension{ + Key: key, + Value: value, + Body: body, + }, + } +} + +// Copy returns a deep copy of oc. +func (oc *ObjectComprehension) Copy() *ObjectComprehension { + cpy := *oc + cpy.Body = oc.Body.Copy() + cpy.Key = oc.Key.Copy() + cpy.Value = oc.Value.Copy() + return &cpy +} + +// Equal returns true if oc is equal to other. +func (oc *ObjectComprehension) Equal(other Value) bool { + return Compare(oc, other) == 0 +} + +// Compare compares oc to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (oc *ObjectComprehension) Compare(other Value) int { + return Compare(oc, other) +} + +// Find returns the current value or a not found error. +func (oc *ObjectComprehension) Find(path Ref) (Value, error) { + if len(path) == 0 { + return oc, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code of the Value. +func (oc *ObjectComprehension) Hash() int { + return oc.Key.Hash() + oc.Value.Hash() + oc.Body.Hash() +} + +// IsGround returns true if the Key, Value and Body are ground. +func (oc *ObjectComprehension) IsGround() bool { + return oc.Key.IsGround() && oc.Value.IsGround() && oc.Body.IsGround() +} + +func (oc *ObjectComprehension) String() string { + return "{" + oc.Key.String() + ": " + oc.Value.String() + " | " + oc.Body.String() + "}" +} + +// SetComprehension represents a set comprehension as defined in the language. +type SetComprehension struct { + Term *Term `json:"term"` + Body Body `json:"body"` +} + +// SetComprehensionTerm creates a new Term with an SetComprehension value. +func SetComprehensionTerm(term *Term, body Body) *Term { + return &Term{ + Value: &SetComprehension{ + Term: term, + Body: body, + }, + } +} + +// Copy returns a deep copy of sc. +func (sc *SetComprehension) Copy() *SetComprehension { + cpy := *sc + cpy.Body = sc.Body.Copy() + cpy.Term = sc.Term.Copy() + return &cpy +} + +// Equal returns true if sc is equal to other. +func (sc *SetComprehension) Equal(other Value) bool { + return Compare(sc, other) == 0 +} + +// Compare compares sc to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (sc *SetComprehension) Compare(other Value) int { + return Compare(sc, other) +} + +// Find returns the current value or a not found error. +func (sc *SetComprehension) Find(path Ref) (Value, error) { + if len(path) == 0 { + return sc, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code of the Value. +func (sc *SetComprehension) Hash() int { + return sc.Term.Hash() + sc.Body.Hash() +} + +// IsGround returns true if the Term and Body are ground. +func (sc *SetComprehension) IsGround() bool { + return sc.Term.IsGround() && sc.Body.IsGround() +} + +func (sc *SetComprehension) String() string { + return "{" + sc.Term.String() + " | " + sc.Body.String() + "}" +} + +// Call represents as function call in the language. +type Call []*Term + +// CallTerm returns a new Term with a Call value defined by terms. The first +// term is the operator and the rest are operands. +func CallTerm(terms ...*Term) *Term { + return NewTerm(Call(terms)) +} + +// Copy returns a deep copy of c. +func (c Call) Copy() Call { + return termSliceCopy(c) +} + +// Compare compares c to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (c Call) Compare(other Value) int { + return Compare(c, other) +} + +// Find returns the current value or a not found error. +func (Call) Find(Ref) (Value, error) { + return nil, errFindNotFound +} + +// Hash returns the hash code for the Value. +func (c Call) Hash() int { + return termSliceHash(c) +} + +// IsGround returns true if the Value is ground. +func (c Call) IsGround() bool { + return termSliceIsGround(c) +} + +// MakeExpr returns an ew Expr from this call. +func (c Call) MakeExpr(output *Term) *Expr { + terms := []*Term(c) + return NewExpr(append(terms, output)) +} + +func (c Call) String() string { + args := make([]string, len(c)-1) + for i := 1; i < len(c); i++ { + args[i-1] = c[i].String() + } + return fmt.Sprintf("%v(%v)", c[0], strings.Join(args, ", ")) +} + +func termSliceCopy(a []*Term) []*Term { + cpy := make([]*Term, len(a)) + for i := range a { + cpy[i] = a[i].Copy() + } + return cpy +} + +func termSliceEqual(a, b []*Term) bool { + if len(a) == len(b) { + for i := range a { + if !a[i].Equal(b[i]) { + return false + } + } + return true + } + return false +} + +func termSliceHash(a []*Term) int { + var hash int + for _, v := range a { + hash += v.Value.Hash() + } + return hash +} + +func termSliceIsGround(a []*Term) bool { + for _, v := range a { + if !v.IsGround() { + return false + } + } + return true +} + +// Detect when String() need to use expensive JSON‐escaped form +func isControlOrBackslash(r rune) bool { + return r == '\\' || unicode.IsControl(r) +} + +// NOTE(tsandall): The unmarshalling errors in these functions are not +// helpful for callers because they do not identify the source of the +// unmarshalling error. Because OPA doesn't accept JSON describing ASTs +// from callers, this is acceptable (for now). If that changes in the future, +// the error messages should be revisited. The current approach focuses +// on the happy path and treats all errors the same. If better error +// reporting is needed, the error paths will need to be fleshed out. + +func unmarshalBody(b []any) (Body, error) { + buf := Body{} + for _, e := range b { + if m, ok := e.(map[string]any); ok { + expr := &Expr{} + if err := unmarshalExpr(expr, m); err == nil { + buf = append(buf, expr) + continue + } + } + goto unmarshal_error + } + return buf, nil +unmarshal_error: + return nil, errors.New("ast: unable to unmarshal body") +} + +func unmarshalExpr(expr *Expr, v map[string]any) error { + if x, ok := v["negated"]; ok { + if b, ok := x.(bool); ok { + expr.Negated = b + } else { + return fmt.Errorf("ast: unable to unmarshal negated field with type: %T (expected true or false)", v["negated"]) + } + } + if generatedRaw, ok := v["generated"]; ok { + if b, ok := generatedRaw.(bool); ok { + expr.Generated = b + } else { + return fmt.Errorf("ast: unable to unmarshal generated field with type: %T (expected true or false)", v["generated"]) + } + } + + if err := unmarshalExprIndex(expr, v); err != nil { + return err + } + switch ts := v["terms"].(type) { + case map[string]any: + t, err := unmarshalTerm(ts) + if err != nil { + return err + } + expr.Terms = t + case []any: + terms, err := unmarshalTermSlice(ts) + if err != nil { + return err + } + expr.Terms = terms + default: + return fmt.Errorf(`ast: unable to unmarshal terms field with type: %T (expected {"value": ..., "type": ...} or [{"value": ..., "type": ...}, ...])`, v["terms"]) + } + if x, ok := v["with"]; ok { + if sl, ok := x.([]any); ok { + ws := make([]*With, len(sl)) + for i := range sl { + var err error + ws[i], err = unmarshalWith(sl[i]) + if err != nil { + return err + } + } + expr.With = ws + } + } + if loc, ok := v["location"].(map[string]any); ok { + expr.Location = &Location{} + if err := unmarshalLocation(expr.Location, loc); err != nil { + return err + } + } + return nil +} + +func unmarshalLocation(loc *Location, v map[string]any) error { + if x, ok := v["file"]; ok { + if s, ok := x.(string); ok { + loc.File = s + } else { + return fmt.Errorf("ast: unable to unmarshal file field with type: %T (expected string)", v["file"]) + } + } + if x, ok := v["row"]; ok { + if n, ok := x.(json.Number); ok { + i64, err := n.Int64() + if err != nil { + return err + } + loc.Row = int(i64) + } else { + return fmt.Errorf("ast: unable to unmarshal row field with type: %T (expected number)", v["row"]) + } + } + if x, ok := v["col"]; ok { + if n, ok := x.(json.Number); ok { + i64, err := n.Int64() + if err != nil { + return err + } + loc.Col = int(i64) + } else { + return fmt.Errorf("ast: unable to unmarshal col field with type: %T (expected number)", v["col"]) + } + } + + return nil +} + +func unmarshalExprIndex(expr *Expr, v map[string]any) error { + if x, ok := v["index"]; ok { + if n, ok := x.(json.Number); ok { + i, err := n.Int64() + if err == nil { + expr.Index = int(i) + return nil + } + } + } + return fmt.Errorf("ast: unable to unmarshal index field with type: %T (expected integer)", v["index"]) +} + +func unmarshalTerm(m map[string]any) (*Term, error) { + var term Term + + v, err := unmarshalValue(m) + if err != nil { + return nil, err + } + term.Value = v + + if loc, ok := m["location"].(map[string]any); ok { + term.Location = &Location{} + if err := unmarshalLocation(term.Location, loc); err != nil { + return nil, err + } + } + + return &term, nil +} + +func unmarshalTermSlice(s []any) ([]*Term, error) { + buf := []*Term{} + for _, x := range s { + if m, ok := x.(map[string]any); ok { + t, err := unmarshalTerm(m) + if err == nil { + buf = append(buf, t) + continue + } + return nil, err + } + return nil, errors.New("ast: unable to unmarshal term") + } + return buf, nil +} + +func unmarshalTermSliceValue(d map[string]any) ([]*Term, error) { + if s, ok := d["value"].([]any); ok { + return unmarshalTermSlice(s) + } + return nil, errors.New(`ast: unable to unmarshal term (expected {"value": [...], "type": ...} where type is one of: ref, array, or set)`) +} + +func unmarshalWith(i any) (*With, error) { + if m, ok := i.(map[string]any); ok { + tgt, _ := m["target"].(map[string]any) + target, err := unmarshalTerm(tgt) + if err == nil { + val, _ := m["value"].(map[string]any) + value, err := unmarshalTerm(val) + if err == nil { + return &With{ + Target: target, + Value: value, + }, nil + } + return nil, err + } + return nil, err + } + return nil, errors.New(`ast: unable to unmarshal with modifier (expected {"target": {...}, "value": {...}})`) +} + +func unmarshalValue(d map[string]any) (Value, error) { + v := d["value"] + switch d["type"] { + case "null": + return NullValue, nil + case "boolean": + if b, ok := v.(bool); ok { + return Boolean(b), nil + } + case "number": + if n, ok := v.(json.Number); ok { + return Number(n), nil + } + case "string": + if s, ok := v.(string); ok { + return String(s), nil + } + case "var": + if s, ok := v.(string); ok { + return Var(s), nil + } + case "ref": + if s, err := unmarshalTermSliceValue(d); err == nil { + return Ref(s), nil + } + case "array": + if s, err := unmarshalTermSliceValue(d); err == nil { + return NewArray(s...), nil + } + case "set": + if s, err := unmarshalTermSliceValue(d); err == nil { + return NewSet(s...), nil + } + case "object": + if s, ok := v.([]any); ok { + buf := NewObject() + for _, x := range s { + if i, ok := x.([]any); ok && len(i) == 2 { + p, err := unmarshalTermSlice(i) + if err == nil { + buf.Insert(p[0], p[1]) + continue + } + } + goto unmarshal_error + } + return buf, nil + } + case "arraycomprehension", "setcomprehension": + if m, ok := v.(map[string]any); ok { + t, ok := m["term"].(map[string]any) + if !ok { + goto unmarshal_error + } + + term, err := unmarshalTerm(t) + if err != nil { + goto unmarshal_error + } + + b, ok := m["body"].([]any) + if !ok { + goto unmarshal_error + } + + body, err := unmarshalBody(b) + if err != nil { + goto unmarshal_error + } + + if d["type"] == "arraycomprehension" { + return &ArrayComprehension{Term: term, Body: body}, nil + } + return &SetComprehension{Term: term, Body: body}, nil + } + case "objectcomprehension": + if m, ok := v.(map[string]any); ok { + k, ok := m["key"].(map[string]any) + if !ok { + goto unmarshal_error + } + + key, err := unmarshalTerm(k) + if err != nil { + goto unmarshal_error + } + + v, ok := m["value"].(map[string]any) + if !ok { + goto unmarshal_error + } + + value, err := unmarshalTerm(v) + if err != nil { + goto unmarshal_error + } + + b, ok := m["body"].([]any) + if !ok { + goto unmarshal_error + } + + body, err := unmarshalBody(b) + if err != nil { + goto unmarshal_error + } + + return &ObjectComprehension{Key: key, Value: value, Body: body}, nil + } + case "call": + if s, err := unmarshalTermSliceValue(d); err == nil { + return Call(s), nil + } + } +unmarshal_error: + return nil, errors.New("ast: unable to unmarshal term") +} diff --git a/third_party/opa/v1/ast/term_bench_test.go b/third_party/opa/v1/ast/term_bench_test.go new file mode 100644 index 000000000000..1d8b26ced6c1 --- /dev/null +++ b/third_party/opa/v1/ast/term_bench_test.go @@ -0,0 +1,498 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. +package ast + +import ( + "encoding/json" + "fmt" + "math/rand" + "strconv" + "strings" + "testing" + "time" +) + +func BenchmarkObjectLookup(b *testing.B) { + sizes := []int{5, 50, 500, 5000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + obj := NewObject() + for i := range n { + obj.Insert(StringTerm(strconv.Itoa(i)), InternedTerm(i)) + } + key := StringTerm(strconv.Itoa(n - 1)) + b.ResetTimer() + for range b.N { + value := obj.Get(key) + if value == nil { + b.Fatal("expected hit") + } + } + }) + } +} + +func BenchmarkObjectFind(b *testing.B) { + sizes := []int{5, 50, 500, 5000} + for _, n := range sizes { + for _, m := range sizes { + b.Run(fmt.Sprintf("%d_%d", n, m), func(b *testing.B) { + obj := NewObject() + for i := range n { + arr := NewArray() + for j := range m { + arr = arr.Append(IntNumberTerm(j)) + } + obj.Insert(StringTerm(strconv.Itoa(i)), NewTerm(arr)) + } + key := Ref{StringTerm(strconv.Itoa(n - 1)), IntNumberTerm(m - 1)} + b.ResetTimer() + for range b.N { + value, err := obj.Find(key) + if err != nil { + b.Fatal(err) + } + if value == nil { + b.Fatal("expected hit") + } + } + }) + } + } +} + +func BenchmarkObjectCreationAndLookup(b *testing.B) { + sizes := []int{5, 50, 500, 5000, 50000, 500000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + obj := NewObject() + for i := range n { + obj.Insert(StringTerm(strconv.Itoa(i)), IntNumberTerm(i)) + } + key := StringTerm(strconv.Itoa(n - 1)) + for range b.N { + value := obj.Get(key) + if value == nil { + b.Fatal("expected hit") + } + } + }) + } +} + +func BenchmarkLazyObjectLookup(b *testing.B) { + sizes := []int{5, 50, 500, 5000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + data := make(map[string]any, n) + for i := range n { + data[strconv.Itoa(i)] = i + } + obj := LazyObject(data) + key := StringTerm(strconv.Itoa(n - 1)) + b.ResetTimer() + for range b.N { + value := obj.Get(key) + if value == nil { + b.Fatal("expected hit") + } + } + }) + } +} + +func BenchmarkLazyObjectFind(b *testing.B) { + sizes := []int{5, 50, 500, 5000} + for _, n := range sizes { + for _, m := range sizes { + b.Run(fmt.Sprintf("%d_%d", n, m), func(b *testing.B) { + data := make(map[string]any, n) + for i := range n { + arr := make([]string, 0, m) + for j := range m { + arr = append(arr, strconv.Itoa(j)) + } + data[strconv.Itoa(i)] = arr + } + obj := LazyObject(data) + key := Ref{StringTerm(strconv.Itoa(n - 1)), IntNumberTerm(m - 1)} + b.ResetTimer() + for range b.N { + value, err := obj.Find(key) + if err != nil { + b.Fatal(err) + } + if value == nil { + b.Fatal("expected hit") + } + } + }) + } + } +} + +func BenchmarkSetCreationAndLookup(b *testing.B) { + sizes := []int{5, 50, 500, 5000, 50000, 500000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + set := NewSet() + for i := range n { + set.Add(StringTerm(strconv.Itoa(i))) + } + key := StringTerm(strconv.Itoa(n - 1)) + for range b.N { + present := set.Contains(key) + if !present { + b.Fatal("expected hit") + } + } + }) + } +} + +func BenchmarkSetIntersection(b *testing.B) { + sizes := []int{5, 50, 500, 5000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + setA := NewSet() + setB := NewSet() + for i := range n { + setA.Add(IntNumberTerm(i)) + setB.Add(IntNumberTerm(i)) + } + b.ResetTimer() + for range b.N { + setC := setA.Intersect(setB) + if setC.Len() != setA.Len() || setC.Len() != setB.Len() { + b.Fatal("expected equal") + } + } + }) + } +} + +func BenchmarkSetIntersectionDifferentSize(b *testing.B) { + sizes := []int{4, 50, 500, 5000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + setA := NewSet() + setB := NewSet() + for i := range n { + setA.Add(IntNumberTerm(i)) + } + for i := range sizes[0] { + setB.Add(IntNumberTerm(i)) + } + setB.Add(IntNumberTerm(-1)) + b.ResetTimer() + for range b.N { + setC := setA.Intersect(setB) + if setC.Len() != sizes[0] { + b.Fatal("expected size to be equal") + } + } + }) + } +} + +func BenchmarkSetMembership(b *testing.B) { + sizes := []int{5, 50, 500, 5000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + setA := NewSet() + for i := range n { + setA.Add(IntNumberTerm(i)) + } + key := IntNumberTerm(n - 1) + b.ResetTimer() + for range b.N { + if !setA.Contains(key) { + b.Fatal("expected hit") + } + } + }) + } +} + +func BenchmarkTermHashing(b *testing.B) { + sizes := []int{10, 100, 1000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + s := String(strings.Repeat("a", n)) + b.ResetTimer() + for range b.N { + _ = s.Hash() + } + }) + } +} + +var ( + str string + bs []byte +) + +// BenchmarkObjectString generates several objects of different sizes, and +// marshals them to JSON via two ways: +// +// map[string]int -> ast.Value -> .String() +// +// and +// +// map[string]int -> json.Marshal() +// +// The difference between these two is relevant for feeding input into the +// wasm vm: when calling rego.New(...) with rego.Target("wasm"), it's up to +// the caller to provide the input in parsed form (ast.Value), or +// raw (any). +func BenchmarkObjectString(b *testing.B) { + var err error + sizes := []int{5, 50, 500, 5000} + + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + obj := map[string]int{} + for i := range n { + obj[strconv.Itoa(i)] = i + } + val := MustInterfaceToValue(obj) + + b.Run("String()", func(b *testing.B) { + b.ResetTimer() + for range b.N { + str = val.String() + } + }) + b.Run("json.Marshal", func(b *testing.B) { + b.ResetTimer() + for range b.N { + bs, err = json.Marshal(obj) + if err != nil { + b.Fatal(err) + } + } + }) + }) + } +} + +// This benchmark works similarly to BenchmarkObjectString, but with a key +// difference: it benchmarks the String and MarshalJSON interface functions +// for the Objec, instead of the underlying data structure. This ensures +// that we catch the full performance properties of Object's implementation. +func BenchmarkObjectStringInterfaces(b *testing.B) { + var err error + sizes := []int{5, 50, 500, 5000, 50000} + + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + obj := map[string]int{} + for i := range n { + obj[strconv.Itoa(i)] = i + } + valString := MustInterfaceToValue(obj) + valJSON := MustInterfaceToValue(obj) + + b.Run("String()", func(b *testing.B) { + b.ResetTimer() + for range b.N { + str = valString.String() + } + }) + b.Run("json.Marshal", func(b *testing.B) { + b.ResetTimer() + for range b.N { + bs, err = json.Marshal(valJSON) + if err != nil { + b.Fatal(err) + } + } + }) + }) + } +} + +func BenchmarkObjectConstruction(b *testing.B) { + sizes := []int{5, 50, 500, 5000, 50000, 500000} + seed := time.Now().UnixNano() + + b.Run("shuffled keys", func(b *testing.B) { + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + es := []struct{ k, v int }{} + for i := range n { + es = append(es, struct{ k, v int }{i, i}) + } + r := rand.New(rand.NewSource(seed)) // Seed the PRNG. + r.Shuffle(len(es), func(i, j int) { es[i], es[j] = es[j], es[i] }) + b.ResetTimer() + for range b.N { + obj := NewObject() + for _, e := range es { + obj.Insert(IntNumberTerm(e.k), IntNumberTerm(e.v)) + } + } + }) + } + }) + b.Run("increasing keys", func(b *testing.B) { + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + es := []struct{ k, v int }{} + for v := range n { + es = append(es, struct{ k, v int }{v, v}) + } + b.ResetTimer() + for range b.N { + obj := NewObject() + for _, e := range es { + obj.Insert(IntNumberTerm(e.k), IntNumberTerm(e.v)) + } + } + }) + } + }) +} + +// BenchmarkArrayString compares the performance characteristics of +// (ast.Value).String() with the stdlib-native json.Marshal. See +// BenchmarkObjectString above for details. +func BenchmarkArrayString(b *testing.B) { + var err error + sizes := []int{5, 50, 500, 5000} + + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + obj := make([]string, n) + for i := range n { + obj[i] = strconv.Itoa(i) + } + val := MustInterfaceToValue(obj) + + b.Run("String()", func(b *testing.B) { + b.ResetTimer() + for range b.N { + str = val.String() + } + }) + b.Run("json.Marshal", func(b *testing.B) { + b.ResetTimer() + for range b.N { + bs, err = json.Marshal(obj) + if err != nil { + b.Fatal(err) + } + } + }) + }) + } +} + +// This was used primarily to test the performance of the Equal method using the +// current implementation vs that of the previous implementation, which simply called +// the Compare function to test for equality (== 0). This was about as fast as the current +// implementation when both arrays were equal, but significantly slower when they +// were not (135 nanoseconds for the old implementation vs 4 nanoseconds now). +func BenchmarkArrayEquality(b *testing.B) { + sizes := []int{5, 50, 500, 5000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + arrA := NewArray() + arrB := NewArray() + for i := range n { + arrA = arrA.Append(IntNumberTerm(i)) + arrB = arrB.Append(IntNumberTerm(i)) + } + // make sure the arrays are not equal + arrB = arrB.Append(IntNumberTerm(10000)) + b.ResetTimer() + b.ReportAllocs() + for range b.N { + if arrA.Equal(arrB) { + b.Fatal("expected not equal") + } + } + }) + } +} + +func BenchmarkSetString(b *testing.B) { + sizes := []int{5, 50, 500, 5000, 50000} + + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + val := NewSet() + for i := range n { + val.Add(IntNumberTerm(i)) + } + + b.Run("String()", func(b *testing.B) { + b.ResetTimer() + for range b.N { + str = val.String() + } + }) + }) + } +} + +func BenchmarkSetMarshalJSON(b *testing.B) { + var err error + sizes := []int{5, 50, 500, 5000, 50000} + + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + set := NewSet() + for i := range n { + set.Add(StringTerm(strconv.Itoa(i))) + } + + b.Run("json.Marshal", func(b *testing.B) { + b.ResetTimer() + for range b.N { + bs, err = json.Marshal(set) + if err != nil { + b.Fatal(err) + } + } + }) + }) + } +} + +// BenchmarkRefString benchmarks the performance of Ref.String(). +func BenchmarkRefString(b *testing.B) { + // prepare all the refs before timing + simpleRef := MustParseRef(`data.policy["main"]`) + controlRef := MustParseRef(`data.policy["ma\tin"]`) + longInputRef := MustParseRef( + `data.policy.test1.test2.test3.test4` + + `["main1"]["main2"]["main3"]["main4"]`, + ) + singleTerm := Ref{VarTerm("is_object")} + + b.Run("Simple", func(b *testing.B) { + for range b.N { + _ = simpleRef.String() + } + }) + + b.Run("WithControl", func(b *testing.B) { + for range b.N { + _ = controlRef.String() + } + }) + + b.Run("LongInput", func(b *testing.B) { + for range b.N { + _ = longInputRef.String() + } + }) + + b.Run("SingleTerm", func(b *testing.B) { + for range b.N { + _ = singleTerm.String() + } + }) +} diff --git a/third_party/opa/v1/ast/term_test.go b/third_party/opa/v1/ast/term_test.go new file mode 100644 index 000000000000..137c232ccce2 --- /dev/null +++ b/third_party/opa/v1/ast/term_test.go @@ -0,0 +1,1577 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "bytes" + "encoding/json" + "errors" + "math/rand" + "reflect" + "runtime" + "sort" + "strings" + "sync" + "testing" + + "github.com/open-policy-agent/opa/v1/util" +) + +func TestInterfaceToValue(t *testing.T) { + // Test util package unmarshalled inputs + input := ` + { + "x": [ + 1, + true, + false, + null, + "hello", + ["goodbye", 1], + ["dummy", "tummy"], + {"y": 3.1} + ] + } + ` + var x any + if err := util.UnmarshalJSON([]byte(input), &x); err != nil { + t.Fatal(err) + } + + expected := MustParseTerm(input).Value + + v, err := InterfaceToValue(x) + if err != nil { + t.Fatal(err) + } + + if v.Compare(expected) != 0 { + t.Fatalf("Expected %v but got: %v", expected, v) + } + + // Test standard JSON package unmarshalled inputs + if err := json.Unmarshal([]byte(input), &x); err != nil { + t.Fatal(err) + } + + expected = MustParseTerm(input).Value + if v, err = InterfaceToValue(x); err != nil { + t.Fatal(err) + } + + if expected.Compare(v) != 0 { + t.Fatalf("Expected %v but got: %v", expected, v) + } + + // Test misc. types + tests := []struct { + input any + expected string + }{ + {int64(100), "100"}, + {float64(100), "100"}, + {int(100), "100"}, + {map[string]string{"foo": "bar"}, `{"foo": "bar"}`}, + {uint64(100), "100"}, + {[]string{"dummy", "tummy"}, `["dummy", "tummy"]`}, + {String("bob"), `"bob"`}, + {[]byte("base64ed"), `"YmFzZTY0ZWQ="`}, // []byte is base64 encoded. + } + + for _, tc := range tests { + expected := MustParseTerm(tc.expected).Value + v, err := InterfaceToValue(tc.input) + if err != nil { + t.Fatal(err) + } + if v.Compare(expected) != 0 { + t.Fatalf("Expected %v but got: %v", expected, v) + } + } +} + +func TestInterfaceToValueStructs(t *testing.T) { + var x struct { + Foo struct { + Baz string `json:"baz"` + } `json:"foo"` + bar string + } + + x.Foo.Baz = "a" + x.bar = "b" + + result, err := InterfaceToValue(x) + if err != nil { + t.Fatal(err) + } + + exp := MustParseTerm(`{"foo": {"baz": "a"}}`) + + if result.Compare(exp.Value) != 0 { + t.Fatalf("expected %v but got %v", exp, result) + } + + var m brokenMarshaller + + _, err = InterfaceToValue(m) + if err == nil || err.Error() != "ast: interface conversion: json: error calling MarshalJSON for type ast.brokenMarshaller: broken" { + t.Fatal("expected error but got:", err) + } +} + +type brokenMarshaller struct{} + +func (brokenMarshaller) MarshalJSON() ([]byte, error) { + return nil, errors.New("broken") +} + +func TestObjectInsertGetLen(t *testing.T) { + tests := []struct { + insert [][2]string + expected map[string]string + }{ + {[][2]string{{`null`, `value1`}, {`null`, `value2`}}, map[string]string{`null`: `value2`}}, + {[][2]string{{`false`, `value`}, {`true`, `value1`}, {`true`, `value2`}}, map[string]string{`false`: `value`, `true`: `value2`}}, + {[][2]string{{`0`, `value`}, {`1`, `value1`}, {`1`, `value2`}, {`1.5`, `value`}}, map[string]string{`0`: `value`, `1`: `value2`, `1.5`: `value`}}, + {[][2]string{{`"string"`, `value1`}, {`"string"`, `value2`}}, map[string]string{`"string"`: `value2`}}, + {[][2]string{{`["other"]`, `value1`}, {`["other"]`, `value2`}}, map[string]string{`["other"]`: `value2`}}, + } + + for _, tc := range tests { + o := NewObject() + for _, kv := range tc.insert { + o.Insert(MustParseTerm(kv[0]), MustParseTerm(kv[1])) + + if v := o.Get(MustParseTerm(kv[0])); v == nil || !MustParseTerm(kv[1]).Equal(v) { + t.Errorf("Expected the object to contain %v", v) + } + } + + if o.Len() != len(tc.expected) { + t.Errorf("Expected the object to have %v entries", len(tc.expected)) + } + + for k, v := range tc.expected { + if x := o.Get(MustParseTerm(k)); x == nil || !MustParseTerm(v).Equal(x) { + t.Errorf("Expected the object to contain %v", k) + } + } + } +} + +func TestObjectSetOperations(t *testing.T) { + a := MustParseTerm(`{"a": "b", "c": "d"}`).Value.(Object) + b := MustParseTerm(`{"c": "q", "d": "e"}`).Value.(Object) + + r1 := a.Diff(b) + if r1.Compare(MustParseTerm(`{"a": "b"}`).Value) != 0 { + t.Errorf(`Expected a.Diff(b) to equal {"a": "b"} but got: %v`, r1) + } + + r2 := a.Intersect(b) + var expectedTerms []*Term + MustParseTerm(`["c", "d", "q"]`).Value.(*Array).Foreach(func(t *Term) { + expectedTerms = append(expectedTerms, t) + }) + if len(r2) != 1 || !termSliceEqual(r2[0][:], expectedTerms) { + t.Errorf(`Expected a.Intersect(b) to equal [["a", "d", "q"]] but got: %v`, r2) + } + + if r3, ok := a.Merge(b); ok { + t.Errorf("Expected a.Merge(b) to fail but got: %v", r3) + } + + c := MustParseTerm(`{"a": {"b": [1], "c": {"d": 2}}}`).Value.(Object) + d := MustParseTerm(`{"a": {"x": [3], "c": {"y": 4}}}`).Value.(Object) + r3, ok := c.Merge(d) + expected := MustParseTerm(`{"a": {"b": [1], "x": [3], "c": {"d": 2, "y": 4}}}`).Value.(Object) + + if !ok || r3.Compare(expected) != 0 { + t.Errorf("Expected c.Merge(d) to equal %v but got: %v", expected, r3) + } +} + +func TestObjectFilter(t *testing.T) { + cases := []struct { + note string + object string + filter string + expected string + }{ + { + note: "base", + object: `{"a": {"b": {"c": 7, "d": 8}}, "e": 9}`, + filter: `{"a": {"b": {"c": null}}}`, + expected: `{"a": {"b": {"c": 7}}}`, + }, + { + note: "multiple roots", + object: `{"a": {"b": {"c": 7, "d": 8}}, "e": 9}`, + filter: `{"a": {"b": {"c": null}}, "e": null}`, + expected: `{"a": {"b": {"c": 7}}, "e": 9}`, + }, + { + note: "shared roots", + object: `{"a": {"b": {"c": 7, "d": 8}, "e": 9}}`, + filter: `{"a": {"b": {"c": null}, "e": null}}`, + expected: `{"a": {"b": {"c": 7}, "e": 9}}`, + }, + { + note: "empty filter", + object: `{"a": 7}`, + filter: `{}`, + expected: `{}`, + }, + { + note: "empty object", + object: `{}`, + filter: `{"a": {"b": null}}`, + expected: `{}`, + }, + { + note: "arrays", + object: `{"a": [{"b": 7, "c": 8}, {"d": 9}]}`, + filter: `{"a": {"0": {"b": null}, "1": null}}`, + expected: `{"a": [{"b": 7}, {"d": 9}]}`, + }, + { + note: "object with number keys", + object: `{"a": [{"1":["b", "c", "d"]}, {"x": "y"}]}`, + filter: `{"a": {"0": {"1": {"2": null}}}}`, + expected: `{"a": [{"1": ["d"]}]}`, + }, + { + note: "sets", + object: `{"a": {"b", "c", "d"}, "x": {"y"}}`, + filter: `{"a": {"b": null, "d": null}, "x": null}`, + expected: `{"a": {"b", "d"}, "x": {"y"}}`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + obj := MustParseTerm(tc.object).Value.(Object) + filterObj := MustParseTerm(tc.filter).Value.(Object) + expected := MustParseTerm(tc.expected).Value.(Object) + actual, err := obj.Filter(filterObj) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + if actual.Compare(expected) != 0 { + t.Errorf("Expected:\n\n\t%s\n\nGot:\n\n\t%s\n\n", expected, actual) + } + }) + } +} + +func TestTermBadJSON(t *testing.T) { + input := `{ + "Value": [[ + {"Value": [{"Value": "a", "Type": "var"}, {"Value": "x", "Type": "string"}], "Type": "ref"}, + {"Value": [{"Value": "x", "Type": "var"}], "Type": "array"} + ], [ + {"Value": 100, "Type": "array"}, + {"Value": "foo", "Type": "string"} + ]], + "Type": "object" + }` + + term := Term{} + err := util.UnmarshalJSON([]byte(input), &term) + expected := errors.New("ast: unable to unmarshal term") + if expected.Error() != err.Error() { + t.Errorf("Expected %v but got: %v", expected, err) + } +} + +func TestTermEqual(t *testing.T) { + assertTermEqual(t, NullTerm(), NullTerm()) + assertTermEqual(t, BooleanTerm(true), BooleanTerm(true)) + assertTermEqual(t, IntNumberTerm(5), IntNumberTerm(5)) + assertTermEqual(t, NumberTerm(json.Number("1e6")), NumberTerm("1000000")) + assertTermEqual(t, StringTerm("a string"), StringTerm("a string")) + assertTermEqual(t, ObjectTerm(), ObjectTerm()) + assertTermEqual(t, ArrayTerm(), ArrayTerm()) + assertTermEqual(t, ObjectTerm(Item(IntNumberTerm(1), IntNumberTerm(2))), ObjectTerm(Item(IntNumberTerm(1), IntNumberTerm(2)))) + assertTermEqual(t, ObjectTerm(Item(IntNumberTerm(1), IntNumberTerm(2)), Item(IntNumberTerm(3), IntNumberTerm(4))), ObjectTerm(Item(IntNumberTerm(1), IntNumberTerm(2)), Item(IntNumberTerm(3), IntNumberTerm(4)))) + assertTermEqual(t, ArrayTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3)), ArrayTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3))) + assertTermEqual(t, VarTerm("foo"), VarTerm("foo")) + assertTermEqual(t, RefTerm(VarTerm("foo"), VarTerm("i"), IntNumberTerm(2)), RefTerm(VarTerm("foo"), VarTerm("i"), IntNumberTerm(2))) + assertTermEqual(t, ArrayComprehensionTerm(VarTerm("x"), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))})), ArrayComprehensionTerm(VarTerm("x"), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))}))) + assertTermEqual(t, ObjectComprehensionTerm(VarTerm("x"), VarTerm("y"), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))})), ObjectComprehensionTerm(VarTerm("x"), VarTerm("y"), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))}))) + assertTermEqual(t, SetComprehensionTerm(VarTerm("x"), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))})), SetComprehensionTerm(VarTerm("x"), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))}))) + + assertTermNotEqual(t, NullTerm(), BooleanTerm(true)) + assertTermNotEqual(t, BooleanTerm(true), BooleanTerm(false)) + assertTermNotEqual(t, IntNumberTerm(5), IntNumberTerm(7)) + assertTermNotEqual(t, StringTerm("a string"), StringTerm("abc")) + assertTermNotEqual(t, ObjectTerm(Item(IntNumberTerm(3), IntNumberTerm(2))), ObjectTerm(Item(IntNumberTerm(1), IntNumberTerm(2)))) + assertTermNotEqual(t, ObjectTerm(Item(IntNumberTerm(1), IntNumberTerm(2)), Item(IntNumberTerm(3), IntNumberTerm(7))), ObjectTerm(Item(IntNumberTerm(1), IntNumberTerm(2)), Item(IntNumberTerm(3), IntNumberTerm(4)))) + assertTermNotEqual(t, IntNumberTerm(5), StringTerm("a string")) + assertTermNotEqual(t, IntNumberTerm(1), BooleanTerm(true)) + assertTermNotEqual(t, ObjectTerm(Item(IntNumberTerm(1), IntNumberTerm(2)), Item(IntNumberTerm(3), IntNumberTerm(7))), ArrayTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(7))) + assertTermNotEqual(t, ArrayTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3)), ArrayTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(4))) + assertTermNotEqual(t, VarTerm("foo"), VarTerm("bar")) + assertTermNotEqual(t, RefTerm(VarTerm("foo"), VarTerm("i"), IntNumberTerm(2)), RefTerm(VarTerm("foo"), StringTerm("i"), IntNumberTerm(2))) + assertTermNotEqual(t, ArrayComprehensionTerm(VarTerm("x"), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("j"))})), ArrayComprehensionTerm(VarTerm("x"), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))}))) + assertTermNotEqual(t, ObjectComprehensionTerm(VarTerm("x"), VarTerm("y"), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("j"))})), ObjectComprehensionTerm(VarTerm("x"), VarTerm("y"), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))}))) + assertTermNotEqual(t, SetComprehensionTerm(VarTerm("x"), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("j"))})), SetComprehensionTerm(VarTerm("x"), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))}))) +} + +func TestFind(t *testing.T) { + term := MustParseTerm(`{"foo": [1,{"bar": {2,3,4}}], "baz": {"qux": ["hello", "world"]}}`) + + tests := []struct { + path *Term + expected any + }{ + {RefTerm(StringTerm("foo"), IntNumberTerm(1), StringTerm("bar")), MustParseTerm(`{2, 3, 4}`)}, + {RefTerm(StringTerm("foo"), IntNumberTerm(1), StringTerm("bar"), IntNumberTerm(4)), MustParseTerm(`4`)}, + {RefTerm(StringTerm("foo"), IntNumberTerm(2)), errors.New("not found")}, + {RefTerm(StringTerm("baz"), StringTerm("qux"), IntNumberTerm(0)), MustParseTerm(`"hello"`)}, + } + + for _, tc := range tests { + result, err := term.Value.Find(tc.path.Value.(Ref)) + switch expected := tc.expected.(type) { + case *Term: + if err != nil { + t.Fatalf("Unexpected error occurred for %v: %v", tc.path, err) + } + if result.Compare(expected.Value) != 0 { + t.Fatalf("Expected value %v for %v but got: %v", expected, tc.path, result) + } + case error: + if err == nil { + t.Fatalf("Expected error but got: %v", result) + } + if !strings.Contains(err.Error(), expected.Error()) { + t.Fatalf("Expected error to contain %v but got: %v", expected, err) + } + default: + panic("bad expected type") + } + } +} + +func TestHashObject(t *testing.T) { + doc := `{"a": [[true, {"b": [null]}, {"c": "d"}]], "e": {100: a[i].b}, "k": ["foo" | true], "o": {"foo": "bar" | true}, "sc": {"foo" | true}, "s": {1, 2, {3, 4}}, "big": 1e+1000}` + + stmt1 := MustParseStatement(doc) + stmt2 := MustParseStatement(doc) + + obj1 := stmt1.(Body)[0].Terms.(*Term).Value.(Object) + obj2 := stmt2.(Body)[0].Terms.(*Term).Value.(Object) + + if obj1.Hash() != obj2.Hash() { + t.Errorf("Expected hash codes to be equal") + } + + // Calculate hash like we did before moving the caching to create/update: + obj := obj1.(*object) + exp := 0 + for h, curr := range obj.elems { + for ; curr != nil; curr = curr.next { + exp += h + exp += curr.value.Hash() + } + } + + if act := obj1.Hash(); exp != act { + t.Errorf("expected %v, got %v", exp, act) + } +} + +func TestHashArray(t *testing.T) { + doc := `[{"a": [[true, {"b": [null]}, {"c": "d"}]]}, 100, true, [a[i].b], {100: a[i].b}, ["foo" | true], {"foo": "bar" | true}, {"foo" | true}, {1, 2, {3, 4}}, 1e+1000]` + + stmt1 := MustParseStatement(doc) + stmt2 := MustParseStatement(doc) + + arr1 := stmt1.(Body)[0].Terms.(*Term).Value.(*Array) + arr2 := stmt2.(Body)[0].Terms.(*Term).Value.(*Array) + + if arr1.Hash() != arr2.Hash() { + t.Errorf("Expected hash codes to be equal") + } + + // Calculate hash like we did before moving the caching to create/update: + exp := termSliceHash(arr1.elems) + + if act := arr1.Hash(); exp != act { + t.Errorf("expected %v, got %v", exp, act) + } + + for j := range arr1.Len() { + for i := 0; i <= j; i++ { + slice := arr1.Slice(i, j) + exp := termSliceHash(slice.elems) + if act := slice.Hash(); exp != act { + t.Errorf("arr1[%d:%d]: expected %v, got %v", i, j, exp, act) + } + } + } +} + +func TestHashSet(t *testing.T) { + doc := `{{"a": [[true, {"b": [null]}, {"c": "d"}]]}, 100, 100, 100, true, [a[i].b], {100: a[i].b}, ["foo" | true], {"foo": "bar" | true}, {"foo" | true}, {1, 2, {3, 4}}, 1e+1000}` + + stmt1 := MustParseStatement(doc) + stmt2 := MustParseStatement(doc) + + set1 := stmt1.(Body)[0].Terms.(*Term).Value.(Set) + set2 := stmt2.(Body)[0].Terms.(*Term).Value.(Set) + + if set1.Hash() != set2.Hash() { + t.Errorf("Expected hash codes to be equal") + } + + // Calculate hash like we did before moving the caching to create/update: + exp := 0 + set1.Foreach(func(x *Term) { + exp += x.Hash() + }) + + if act := set1.Hash(); exp != act { + t.Errorf("expected %v, got %v", exp, act) + } +} + +func TestTermIsGround(t *testing.T) { + tests := []struct { + note string + term string + expected bool + }{ + {"null", "null", true}, + {"string", `"foo"`, true}, + {"number", "42.1", true}, + {"boolean", "false", true}, + {"var", "x", false}, + {"ref ground", "a.b[0]", true}, + {"ref non-ground", "a.b[i].x", false}, + {"array ground", "[1,2,3]", true}, + {"array non-ground", "[1,2,x]", false}, + {"set ground", "{1,2,3}", true}, + {"Set non-ground", "{1,2,x}", false}, + {"object ground", `{"a": 1}`, true}, + {"object non-ground key", `{"x": 1, y: 2}`, false}, + {"object non-ground value", `{"x": 1, "y": y}`, false}, + {"array compr ground", `["a" | true]`, true}, + {"array compr non-ground", `[x | x = a[i]]`, false}, + } + + for i, tc := range tests { + term := MustParseTerm(tc.term) + if term.IsGround() != tc.expected { + expected := "ground" + if !tc.expected { + expected = "non-ground" + } + t.Errorf("Expected term %v to be %s (test case %d: %v)", term, expected, i, tc.note) + } + } +} + +func TestObjectRemainsGround(t *testing.T) { + tests := []struct { + key string + value string + ground bool + }{ + {`"a"`, `"value1"`, true}, + {`"b"`, `"value2"`, true}, + {`"a"`, `x`, false}, + {`"a"`, `"value1"`, true}, + {`"b"`, `y`, false}, + {`"c"`, `value3`, false}, + } + + obj := NewObject() + + for i, tc := range tests { + obj.Insert(MustParseTerm(tc.key), MustParseTerm(tc.value)) + if obj.IsGround() != tc.ground { + t.Errorf("Unexpected object is ground (test case %d)", i) + } + } +} + +func TestIsConstant(t *testing.T) { + tests := []struct { + term string + expected bool + }{ + {`[{"foo": {true, false, [1, 2]}}]`, true}, + {`[{"foo": {x}}]`, false}, + } + for _, tc := range tests { + term := MustParseTerm(tc.term) + if IsConstant(term.Value) != tc.expected { + t.Fatalf("Expected IsConstant(%v) = %v", term, tc.expected) + } + } +} + +func TestIsScalar(t *testing.T) { + tests := []struct { + term string + expected bool + }{ + {"null", true}, + {`"string"`, true}, + {"3.14", true}, + {"false", true}, + {"[1,2,3]", false}, + {"{1,2,3}", false}, + {`{"a": 1}`, false}, + {`[x | x = 0]`, false}, + } + for _, tc := range tests { + term := MustParseTerm(tc.term) + if IsScalar(term.Value) != tc.expected { + t.Errorf("Expected IsScalar(%v) = %v", term, tc.expected) + } + } +} + +func TestTermString(t *testing.T) { + assertToString(t, Null{}, "null") + assertToString(t, Boolean(true), "true") + assertToString(t, Boolean(false), "false") + assertToString(t, Number("4"), "4") + assertToString(t, Number("42.1"), "42.1") + assertToString(t, Number("6e7"), "6e7") + assertToString(t, UIntNumberTerm(uint64(1)).Value, "1") + assertToString(t, String("foo"), "\"foo\"") + assertToString(t, String("\"foo\""), "\"\\\"foo\\\"\"") + assertToString(t, String("foo bar"), "\"foo bar\"") + assertToString(t, Var("foo"), "foo") + assertToString(t, RefTerm(VarTerm("foo"), StringTerm("bar")).Value, "foo.bar") + assertToString(t, RefTerm(VarTerm("foo"), StringTerm("bar"), VarTerm("i"), IntNumberTerm(0), StringTerm("baz")).Value, "foo.bar[i][0].baz") + assertToString(t, RefTerm(VarTerm("foo"), BooleanTerm(false), NullTerm(), StringTerm("bar")).Value, "foo[false][null].bar") + assertToString(t, RefTerm(VarTerm("p"), StringTerm("not")).Value, `p["not"]`) + assertToString(t, RefTerm(CallTerm(VarTerm("f"), VarTerm("x")), IntNumberTerm(0)).Value, "f(x)[0]") + assertToString(t, RefTerm(ArrayTerm(StringTerm("a"), StringTerm("b")), IntNumberTerm(0)).Value, "[\"a\", \"b\"][0]") + assertToString(t, ArrayTerm().Value, "[]") + assertToString(t, ObjectTerm().Value, "{}") + assertToString(t, SetTerm().Value, "set()") + assertToString(t, ArrayTerm(ObjectTerm(Item(VarTerm("foo"), ArrayTerm(RefTerm(VarTerm("bar"), VarTerm("i"))))), StringTerm("foo"), SetTerm(BooleanTerm(true), NullTerm()), FloatNumberTerm(42.1)).Value, "[{foo: [bar[i]]}, \"foo\", {null, true}, 42.1]") + assertToString(t, ArrayComprehensionTerm(ArrayTerm(VarTerm("x")), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))})).Value, `[[x] | a[i]]`) + assertToString(t, ObjectComprehensionTerm(VarTerm("y"), ArrayTerm(VarTerm("x")), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))})).Value, `{y: [x] | a[i]}`) + assertToString(t, SetComprehensionTerm(ArrayTerm(VarTerm("x")), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))})).Value, `{[x] | a[i]}`) + + // ensure that objects and sets have deterministic String() results + assertToString(t, SetTerm(VarTerm("y"), VarTerm("x")).Value, "{x, y}") + assertToString(t, ObjectTerm([2]*Term{VarTerm("y"), VarTerm("b")}, [2]*Term{VarTerm("x"), VarTerm("a")}).Value, "{x: a, y: b}") +} + +func TestRefString_Escapes(t *testing.T) { + cases := []struct { + name string + input string + want []byte + }{ + { + name: "Tab", + input: `data.policy["ma\tin"]`, + want: []byte{ + 100, 97, 116, 97, // data + 46, // . + 112, 111, 108, 105, 99, 121, // policy + 91, 34, // [" + 109, 97, // m a + 92, 116, // \ t + 105, 110, // i n + 34, 93, // "] + }, + }, + { + name: "NewLine", + input: `data.policy["ma\nin"]`, + want: []byte{ + 100, 97, 116, 97, + 46, + 112, 111, 108, 105, 99, 121, + 91, 34, + 109, 97, + 92, 110, // \ n + 105, 110, + 34, 93, + }, + }, + { + name: "CarriageReturn", + input: `data.policy["ma\rin"]`, + want: []byte{ + 100, 97, 116, 97, + 46, + 112, 111, 108, 105, 99, 121, + 91, 34, + 109, 97, + 92, 114, // \ r + 105, 110, + 34, 93, + }, + }, + { + name: "Backspace", + input: `data.policy["ma\bin"]`, + want: []byte{ + 100, 97, 116, 97, + 46, + 112, 111, 108, 105, 99, 121, + 91, 34, + 109, 97, + 92, 98, // \ b + 105, 110, + 34, 93, + }, + }, + { + name: "FormFeed", + input: `data.policy["ma\fin"]`, + want: []byte{ + 100, 97, 116, 97, + 46, + 112, 111, 108, 105, 99, 121, + 91, 34, + 109, 97, + 92, 102, // \ f + 105, 110, + 34, 93, + }, + }, + { + name: "LiteralBackslash", + input: `data.policy["ma\\in"]`, + want: []byte{ + 100, 97, 116, 97, + 46, + 112, 111, 108, 105, 99, 121, + 91, 34, + 109, 97, + 92, 92, // \\ + 105, 110, + 34, 93, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := []byte(MustParseRef(tc.input).String()) + if !bytes.Equal(got, tc.want) { + t.Errorf("%s: got %v\nwant %v", tc.name, got, tc.want) + } + }) + } +} + +func TestRefHasPrefix(t *testing.T) { + a := MustParseRef("foo.bar.baz") + b := MustParseRef("foo.bar") + c := MustParseRef("foo.bar[0][x]") + + if !a.HasPrefix(b) { + t.Error("Expected a.HasPrefix(b)") + } + + if b.HasPrefix(a) { + t.Error("Expected !b.HasPrefix(a)") + } + + if !c.HasPrefix(b) { + t.Error("Expected c.HasPrefix(b)") + } +} + +func TestRefAppend(t *testing.T) { + a := MustParseRef("foo.bar.baz") + b := a.Append(VarTerm("x")) + if !b.Equal(MustParseRef("foo.bar.baz[x]")) { + t.Error("Expected foo.bar.baz[x]") + } +} + +func TestRefInsert(t *testing.T) { + ref := MustParseRef("test.ex") + cases := []struct { + pos int + term *Term + expected string + }{ + {0, VarTerm("foo"), `foo[test].ex`}, + {1, StringTerm("foo"), `test.foo.ex`}, + {2, StringTerm("foo"), `test.ex.foo`}, + } + for i := range cases { + result := ref.Insert(cases[i].term, cases[i].pos) + expected := MustParseRef(cases[i].expected) + if !expected.Equal(result) { + t.Fatalf("Expected %v (len: %d) but got: %v (len: %d)", expected, len(expected), result, len(result)) + } + } +} + +func TestRefDynamic(t *testing.T) { + a := MustParseRef("foo.bar[baz.qux].corge") + if a.Dynamic() != 2 { + t.Fatalf("Expected dynamic offset to be baz.qux for foo.bar[baz.qux].corge") + } + if a[:a.Dynamic()].Dynamic() != -1 { + t.Fatalf("Expected dynamic offset to be -1 for foo.bar") + } + + if MustParseRef("f(x)[0]").Dynamic() != 0 { + t.Fatalf("Expected dynamic offset to be f(x) for foo.bar[baz.qux].corge") + } +} + +func TestRefExtend(t *testing.T) { + a := MustParseRef("foo.bar.baz") + b := MustParseRef("qux.corge") + c := MustParseRef("data") + result := a.Extend(b) + expected := MustParseRef("foo.bar.baz.qux.corge") + if !result.Equal(expected) { + t.Fatalf("Expected %v but got %v", expected, result) + } + result = result.Extend(c) + expected = MustParseRef("foo.bar.baz.qux.corge.data") + if !result.Equal(expected) { + t.Fatalf("Expected %v but got %v", expected, result) + } +} + +func TestRefConcat(t *testing.T) { + a := MustParseRef("foo.bar.baz") + terms := []*Term{} + if !a.Concat(terms).Equal(a) { + t.Fatal("Expected no change") + } + terms = append(terms, StringTerm("qux")) + exp := MustParseTerm("foo.bar.baz.qux") + result := a.Concat(terms) + if !result.Equal(exp.Value) { + t.Fatalf("Expected %v but got %v", exp, result) + } + exp = MustParseTerm("foo.bar.baz.qux[0]") + terms = append(terms, IntNumberTerm(0)) + result = a.Concat(terms) + if !result.Equal(exp.Value) { + t.Fatalf("Expected %v but got %v", exp, result) + } + exp = MustParseTerm("foo.bar.baz") + if !a.Equal(exp.Value) { + t.Fatalf("Expected %v but got %v (want a to be unchanged)", exp, a) + } +} + +func TestRefPtr(t *testing.T) { + cases := []string{ + "", + "a", + "a/b", + "/a/b", + "/a/b/", + "a%2Fb", + } + + for _, tc := range cases { + ref, err := PtrRef(DefaultRootDocument.Copy(), tc) + if err != nil { + t.Fatal("Unexpected error:", err) + } + + ptr, err := ref.Ptr() + if err != nil { + t.Fatal("Unexpected error:", err) + } + + roundtrip, err := PtrRef(DefaultRootDocument.Copy(), ptr) + if err != nil { + t.Fatal("Unexpected error:", err) + } + + if !ref.Equal(roundtrip) { + t.Fatalf("Expected roundtrip of %q to be equal but got %v and %v", tc, ref, roundtrip) + } + } + + if _, err := PtrRef(DefaultRootDocument.Copy(), "2%"); err == nil { + t.Fatalf("Expected error from %q", "2%") + } + + ref := Ref{VarTerm("x"), IntNumberTerm(1)} + + if _, err := ref.Ptr(); err == nil { + t.Fatal("Expected error from x[1]") + } +} + +func TestSetEqual(t *testing.T) { + tests := []struct { + a string + b string + expected bool + }{ + {"set()", "set()", true}, + {"{1,{2,3},4}", "{1,{2,3},4}", true}, + {"{1,{2,3},4}", "{4,{3,2},1}", true}, + {"{1,2,{3,4}}", "{1,2,{3,4},1,2,{3,4}}", true}, + {"{1,2,3,4}", "{1,2,3}", false}, + {"{1,2,3}", "{1,2,3,4}", false}, + } + for _, tc := range tests { + a := MustParseTerm(tc.a) + b := MustParseTerm(tc.b) + if a.Equal(b) != tc.expected { + if tc.expected { + t.Errorf("Expected %v to equal %v", a, b) + } else { + t.Errorf("Expected %v to NOT equal %v", a, b) + } + } + } +} + +func TestSetMap(t *testing.T) { + set := MustParseTerm(`{"foo", "bar", "baz", "qux"}`).Value.(Set) + + result, err := set.Map(func(term *Term) (*Term, error) { + s := string(term.Value.(String)) + if strings.Contains(s, "a") { + return &Term{Value: String(strings.ToUpper(s))}, nil + } + return term, nil + }) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected := MustParseTerm(`{"foo", "BAR", "BAZ", "qux"}`).Value + + if result.Compare(expected) != 0 { + t.Fatalf("Expected map result to be %v but got: %v", expected, result) + } + + result, err = set.Map(func(*Term) (*Term, error) { + return nil, errors.New("oops") + }) + + if err.Error() != "oops" { + t.Fatalf("Expected oops to be returned but got: %v, %v", result, err) + } +} + +func TestSetAddContainsLen(t *testing.T) { + tests := []struct { + add []string + expected []string + }{ + {[]string{`null`, `null`}, []string{`null`}}, + {[]string{`true`, `true`, `false`}, []string{`true`, `false`}}, + {[]string{`0`, `1`, `1`, `1.5`}, []string{`0`, `1`, `1.5`}}, + {[]string{`"string"`, `"string"`}, []string{`"string"`}}, + {[]string{`["other"]`, `["other"]`}, []string{`["other"]`}}, + } + + for _, tc := range tests { + s := NewSet() + for _, v := range tc.add { + x := MustParseTerm(v) + s.Add(x) + + if !s.Contains(x) { + t.Errorf("Expected the set to contain %v", v) + } + } + + if s.Len() != len(tc.expected) { + t.Errorf("Expected the set to have %v entries", len(tc.expected)) + } + + for _, v := range tc.expected { + if !s.Contains(MustParseTerm(v)) { + t.Errorf("Expected the set to contain %v", v) + } + } + } +} + +func TestSetOperations(t *testing.T) { + tests := []struct { + a string + b string + c string + op string + }{ + {`{1,2,3,4}`, `{1,3,5}`, `{2,4}`, "-"}, + {`{1,3,5}`, `{1,2,3,4}`, `{5,}`, "-"}, + {`{1,2,3,4}`, `{1,3,5}`, `{1,3}`, "&"}, + {`{1,3,5}`, `{1,2,3,4}`, `{1,3}`, "&"}, + {`{1,2,3,4}`, `{1,3,5}`, `{1,2,3,4,5}`, "|"}, + {`{1,3,5}`, `{1,2,3,4}`, `{1,2,3,4,5}`, "|"}, + } + + for _, tc := range tests { + s1 := MustParseTerm(tc.a).Value.(Set) + s2 := MustParseTerm(tc.b).Value.(Set) + s3 := MustParseTerm(tc.c).Value.(Set) + var result Set + switch tc.op { + case "-": + result = s1.Diff(s2) + case "&": + result = s1.Intersect(s2) + case "|": + result = s1.Union(s2) + default: + panic("bad operation") + } + if result.Compare(s3) != 0 { + t.Errorf("Expected %v for %v %v %v but got: %v", s3, tc.a, tc.op, tc.b, result) + } + } +} + +func TestSetCopy(t *testing.T) { + orig := MustParseTerm("{1,2,3}") + cpy := orig.Copy() + vis := NewGenericVisitor(func(x any) bool { + if Compare(IntNumberTerm(2), x) == 0 { + // NOTE(sr): If we mess up the rank, our sort-on-insert approach fails us + x.(*Term).Value = Number("2.5") + } + return false + }) + vis.Walk(orig) + expOrig := MustParseTerm(`{1,2.5,3}`) + expCpy := MustParseTerm(`{1,2,3}`) + if !expOrig.Equal(orig) { + t.Errorf("Expected %v but got %v", expOrig, orig) + } + if !expCpy.Equal(cpy) { + t.Errorf("Expected %v but got %v", expCpy, cpy) + } +} + +// Constructs a set, and then has several reader goroutines attempt to +// concurrently iterate across it. This should pretty consistently +// hit a race condition around sorting the underlying key slice if +// the sorting isn't guarded properly. +func TestSetConcurrentReads(t *testing.T) { + // Create array of numbers. + numbers := make([]*Term, 10000) + for i := range 10000 { + numbers[i] = IntNumberTerm(i) + } + // Shuffle numbers array for random insertion order. + rand.Shuffle(len(numbers), func(i, j int) { + numbers[i], numbers[j] = numbers[j], numbers[i] + }) + // Build set with numbers in unsorted order. + s := NewSet() + for i := range numbers { + s.Add(numbers[i]) + } + // In-place sort on numbers. + sort.Sort(termSlice(numbers)) + + // Check if race condition on key sorting is present. + var wg sync.WaitGroup + num := runtime.NumCPU() + wg.Add(num) + for range num { + go func() { + defer wg.Done() + var retrieved []*Term + s.Foreach(func(v *Term) { + retrieved = append(retrieved, v) + }) + // Check for sortedness of retrieved results. + // This will hit a race condition around `s.sortedKeys`. + for n := range retrieved { + if retrieved[n] != numbers[n] { + t.Errorf("Expected: %v at iteration %d but got %v instead", numbers[n], n, retrieved[n]) + } + } + }() + } + wg.Wait() +} + +func TestObjectConcurrentReads(t *testing.T) { + // Create array of numbers. + numbers := make([]*Term, 10000) + for i := range 10000 { + numbers[i] = IntNumberTerm(i) + } + // Shuffle numbers array for random insertion order. + r := rand.New(rand.NewSource(10000)) // Seed the PRNG. + r.Shuffle(len(numbers), func(i, j int) { + numbers[i], numbers[j] = numbers[j], numbers[i] + }) + // Build an object with numbers in unsorted order. + o := NewObject() + for i := range numbers { + o.Insert(numbers[i], NullTerm()) + } + // In-place sort on numbers. + sort.Sort(termSlice(numbers)) + + // Check if race condition on key sorting is present. + var wg sync.WaitGroup + num := runtime.NumCPU() + wg.Add(num) + for range num { + go func() { + defer wg.Done() + var retrieved []*Term + o.Foreach(func(k, _ *Term) { + retrieved = append(retrieved, k) + }) + // Check for sortedness of retrieved results. + // This will hit a race condition around `s.sortedKeys`. + for n := range retrieved { + if retrieved[n] != numbers[n] { + t.Errorf("Expected: %v at iteration %d but got %v instead", numbers[n], n, retrieved[n]) + } + } + }() + } + wg.Wait() +} + +func TestArrayOperations(t *testing.T) { + arr := MustParseTerm(`[1,2,3,4]`).Value.(*Array) + + getTests := []struct { + input string + expected string + }{ + {"x", ""}, + {"4.1", ""}, + {"-1", ""}, + {"4", ""}, + {"0", "1"}, + {"3", "4"}, + } + + for _, tc := range getTests { + input := MustParseTerm(tc.input) + result := arr.Get(input) + + if result != nil { + if tc.expected != "" { + expected := MustParseTerm(tc.expected) + if expected.Equal(result) { + continue + } + } + } else if tc.expected == "" { + continue + } + + t.Errorf("Expected %v.get(%v) => %v but got: %v", arr, input, tc.expected, result) + } + + // Iteration, append and slice tests + + var results []*Term + tests := []struct { + note string + input string + expected []string + iterator func(arr *Array) + }{ + { + "for", + `[1, 2, 3, 4]`, + []string{"1", "2", "3", "4"}, + func(arr *Array) { + for i := range arr.Len() { + results = append(results, arr.Elem(i)) + } + }, + }, + { + "foreach", + "[1, 2, 3, 4]", + []string{"1", "2", "3", "4"}, + func(arr *Array) { + arr.Foreach(func(v *Term) { + results = append(results, v) + }) + }, + }, + { + "until", + "[1, 2, 3, 4]", + []string{"1"}, + func(arr *Array) { + arr.Until(func(v *Term) bool { + results = append(results, v) + return len(results) == 1 + }) + }, + }, + { + "append", + "[1, 2]", + []string{"1", "2", "3"}, + func(arr *Array) { + arr.Append(MustParseTerm("3")).Foreach(func(v *Term) { + results = append(results, v) + }) + }, + }, + { + "slice", + "[1, 2, 3, 4]", + []string{"3", "4"}, + func(arr *Array) { + arr.Slice(2, 4).Foreach(func(v *Term) { + results = append(results, v) + }) + }, + }, + { + "slice", + "[1, 2, 3, 4]", + []string{"3", "4"}, + func(arr *Array) { + arr.Slice(2, -1).Foreach(func(v *Term) { + results = append(results, v) + }) + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + arr := MustParseTerm(tc.input).Value.(*Array) + + var expected []*Term + for _, e := range tc.expected { + expected = append(expected, MustParseTerm(e)) + } + + results = nil + tc.iterator(arr) + + if !termSliceEqual(results, expected) { + t.Errorf("Expected iteration to return %v but got %v", expected, results) + } + }) + } +} + +func TestValueToInterface(t *testing.T) { + // Happy path + term := MustParseTerm(`{ + "foo": [1, "two", true, null, {3, + }] + }`) + + value, err := JSON(term.Value) + if err != nil { + t.Fatalf("Unexpected error while converting term %v to JSON: %v", term, err) + } + + var expected any + if err := util.UnmarshalJSON([]byte(`{"foo": [1, "two", true, null, [3]]}`), &expected); err != nil { + panic(err) + } + + if util.Compare(value, expected) != 0 { + t.Fatalf("Expected %v but got: %v", expected, value) + } + + // Nested ref value + term = MustParseTerm(`{ + "foo": [{data.a.b.c,}] + }`) + + _, err = JSON(term.Value) + + if err == nil { + t.Fatalf("Expected error from JSON(%v)", term) + } + + // Ref key + term = MustParseTerm(`{ + data.foo.a: 1 + }`) + + _, err = JSON(term.Value) + + if err == nil { + t.Fatalf("Expected error from JSON(%v)", term) + } + + // Requires evaluation + term = MustParseTerm(`{ + "foo": [x | x = 1] + }`) + + _, err = JSON(term.Value) + + if err == nil { + t.Fatalf("Expected error from JSON(%v)", term) + } + + // Ordering option + // + // These inputs exercise all of the cases (i.e., sets nested in arrays, object keys, and object values.) + // + a, err := JSONWithOpt(MustParseTerm(`[{{3, 4}: {1, 2}}]`).Value, JSONOpt{SortSets: true}) + if err != nil { + t.Fatal(err) + } + + b, err := JSONWithOpt(MustParseTerm(`[{{4, 3}: {2, 1}}]`).Value, JSONOpt{SortSets: true}) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(a, b) { + t.Fatalf("expcted %v = %v", a, b) + } +} + +// NOTE(sr): Without the opt-out, we don't allocate another object for +// the conversion back to any if it can be avoided. As a result, +// the value held by the store could be changed. +func TestJSONWithOptLazyObjDefault(t *testing.T) { + // would live in the store + m := map[string]any{ + "foo": "bar", + } + o := LazyObject(m) + + n, err := JSONWithOpt(o, JSONOpt{}) + if err != nil { + t.Fatal(err) + } + n0, ok := n.(map[string]any) + if !ok { + t.Fatalf("expected %T, got %T: %[2]v", n0, n) + } + n0["baz"] = true + + if v, ok := m["baz"]; !ok || !v.(bool) { + t.Errorf("expected change in m, found none: %v", m) + } +} + +func TestJSONWithOptLazyObjOptOut(t *testing.T) { + // would live in the store + m := map[string]any{ + "foo": "bar", + } + o := LazyObject(m) + + n, err := JSONWithOpt(o, JSONOpt{CopyMaps: true}) + if err != nil { + t.Fatal(err) + } + n0, ok := n.(map[string]any) + if !ok { + t.Fatalf("expected %T, got %T: %[2]v", n0, n) + } + n0["baz"] = true + + if _, ok := m["baz"]; ok { + t.Errorf("expected no change in m, found one: %v", m) + } +} + +func assertTermEqual(t *testing.T, x *Term, y *Term) { + t.Helper() + if !x.Equal(y) { + t.Errorf("Failure on equality: \n%s and \n%s\n", x, y) + } +} + +func assertTermNotEqual(t *testing.T, x *Term, y *Term) { + t.Helper() + if x.Equal(y) { + t.Errorf("Failure on non-equality: \n%s and \n%s\n", x, y) + } +} + +func assertToString(t *testing.T, val Value, expected string) { + t.Helper() + result := val.String() + if result != expected { + t.Errorf("Expected %v but got %v", expected, result) + } +} + +func TestLazyObjectGet(t *testing.T) { + x := LazyObject(map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "c": true, + }, + }, + }) + y := x.Get(StringTerm("a")) + _, ok := y.Value.(*lazyObj) + if !ok { + t.Errorf("expected Get() to return another lazy object, got %v %[1]T", y.Value) + } + assertForced(t, x, false) +} + +func TestLazyObjectGetCache(t *testing.T) { + x := LazyObject(map[string]any{ + "a": true, + "b": false, + "d": map[string]any{ + "e": "f", + "f": "g", + }, + }) + + // Assert that non-objects are cached + + y := x.Get(StringTerm("a")) + + if x.(*lazyObj).cache["a"].Compare(y.Value) != 0 { + t.Errorf("expected cache to be populated with retreived value") + } + + if x.(*lazyObj).cache["b"] != nil { + t.Errorf("expected cache to not be populated with non-retrieved value") + } + + // Assert that objects are cached as lazy objects + + y = x.Get(StringTerm("d")) + + expected := NewObject(Item(StringTerm("e"), StringTerm("f")), Item(StringTerm("f"), StringTerm("g"))) + if y.Value.Compare(expected) != 0 { + t.Errorf("expected returned value to be %v, got %v", expected, y) + } + + d := x.(*lazyObj).cache["d"] + ld, ok := d.(*lazyObj) + if !ok { + t.Errorf("expected cache to be populated with lazy object, got %v", d) + } + if ld.Compare(expected) != 0 { + t.Errorf("expected cached intermediate value to be %v, got %v", expected, y) + } +} + +func TestLazyObjectFind(t *testing.T) { + x := LazyObject(map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "c": true, + }, + "d": []any{true, true, true}, + }, + }) + // retrieve object via Find + y, err := x.Find(Ref{StringTerm("a"), StringTerm("b")}) + if err != nil { + t.Fatal(err) + } + _, ok := y.(*lazyObj) + if !ok { + t.Errorf("expected Find() to return another lazy object, got %v %[1]T", y) + } + assertForced(t, x, false) + + // retrieve array via Find + z, err := x.Find(Ref{StringTerm("a"), StringTerm("d")}) + if err != nil { + t.Fatal(err) + } + _, ok = z.(*Array) + if !ok { + t.Errorf("expected Find() to return array, got %v %[1]T", z) + } +} + +func TestLazyObjectFindCache(t *testing.T) { + x := LazyObject(map[string]any{ + "a": []string{ + "b", "c", "d", + }, + "c": []string{ + "d", "e", "f", + }, + "d": map[string]any{ + "e": "f", + "f": "g", + }, + }) + + // Assert that non-objects are cached + + y, err := x.Find(Ref{StringTerm("a"), IntNumberTerm(1)}) + if err != nil { + t.Fatal(err) + } + + if y.Compare(String("c")) != 0 { + t.Errorf("expected returned value to be 'c', got %v", y) + } + + expected := NewArray(StringTerm("b"), StringTerm("c"), StringTerm("d")) + if x.(*lazyObj).cache["a"].Compare(expected) != 0 { + t.Errorf("expected cache to be populated with type-converted intermediate value, got %v", + x.(*lazyObj).cache["a"]) + } + + if x.(*lazyObj).cache["b"] != nil { + t.Errorf("expected cache to not be populated non-retrieved intermediate value, got %v", + x.(*lazyObj).cache["b"]) + } + + // Assert that objects are cached as lazy objects + + y, err = x.Find(Ref{StringTerm("d"), StringTerm("e")}) + if err != nil { + t.Fatal(err) + } + + if y.Compare(String("f")) != 0 { + t.Errorf("expected returned value to be 'c', got %v", y) + } + + d := x.(*lazyObj).cache["d"] + ld, ok := d.(*lazyObj) + if !ok { + t.Errorf("expected cache to be populated with lazy object, got %v", d) + } + if ld.cache["e"].Compare(String("f")) != 0 { + t.Errorf("expected cache of intermediate lazyObj to be populated with type-converted intermediate value, got %v", + ld.cache["e"]) + } +} + +func TestLazyObjectCopy(t *testing.T) { + x := LazyObject(map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "c": true, + }, + }, + }) + y := x.Copy() + _, ok := y.(*lazyObj) + if !ok { + t.Errorf("expected Get() to return another lazy object, got %v %[1]T", y) + } + assertForced(t, x, false) +} + +func TestLazyObjectLen(t *testing.T) { + x := LazyObject(map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "c": true, + }, + }, + }) + if exp, act := 1, x.Len(); exp != act { + t.Errorf("expected Len() %v, got %v", exp, act) + } + assertForced(t, x, false) +} + +func TestLazyObjectIsGround(t *testing.T) { + x := LazyObject(map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "c": true, + }, + }, + }) + if exp, act := true, x.IsGround(); exp != act { + t.Errorf("expected IsGround() %v, got %v", exp, act) + } + assertForced(t, x, false) +} + +func TestLazyObjectInsert(t *testing.T) { + x := LazyObject(map[string]any{ + "a": "b", + }) + x.Insert(StringTerm("c"), StringTerm("d")) + assertForced(t, x, true) + + // NOTE(sr): We compare after asserting that it was forced, since comparison + // forces the lazy object, too. + if act, exp := x, NewObject(Item(StringTerm("a"), StringTerm("b")), Item(StringTerm("c"), StringTerm("d"))); exp.Compare(act) != 0 { + t.Errorf("expected %v to be equal to %v", act, exp) + } +} + +func TestLazyObjectKeys(t *testing.T) { + x := LazyObject(map[string]any{ + "a": "A", + "c": "C", + "b": "B", + }) + act := x.Keys() + exp := []*Term{StringTerm("a"), StringTerm("b"), StringTerm("c")} + if !termSliceEqual(exp, act) { + t.Errorf("expected Keys() %v, got %v", exp, act) + } + assertForced(t, x, false) +} + +func TestLazyObjectKeysIterator(t *testing.T) { + x := LazyObject(map[string]any{ + "a": "A", + "c": "C", + "b": "B", + }) + ki := x.KeysIterator() + act := make([]*Term, 0, x.Len()) + for k, next := ki.Next(); next; k, next = ki.Next() { + act = append(act, k) + } + exp := []*Term{StringTerm("a"), StringTerm("b"), StringTerm("c")} + if !termSliceEqual(exp, act) { + t.Errorf("expected Keys() %v, got %v", exp, act) + } + assertForced(t, x, false) +} + +func TestLazyObjectCompare(t *testing.T) { + x := LazyObject(map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "c": true, + }, + }, + }) + if exp, act := 1, x.Compare(NewObject()); exp != act { + t.Errorf("expected Compare() => %v, got %v", exp, act) + } + assertForced(t, x, true) +} + +func assertForced(t *testing.T, x Object, forced bool) { + t.Helper() + l, ok := x.(*lazyObj) + switch { + case !ok: + t.Errorf("expected lazy object, got %v %[1]T", x) + case !forced && l.strict != nil: + t.Errorf("expected %v to not be forced", l) + case forced && l.strict == nil: + t.Errorf("expected %v to be forced", l) + } +} diff --git a/third_party/opa/v1/ast/testdata/_definitions.json b/third_party/opa/v1/ast/testdata/_definitions.json new file mode 100644 index 000000000000..30b1fab4c202 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/_definitions.json @@ -0,0 +1,18864 @@ +{ + "definitions": { + "io.k8s.api.admissionregistration.v1beta1.MutatingWebhookConfiguration": { + "description": "MutatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and may change the object.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "MutatingWebhookConfiguration" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata." + }, + "webhooks": { + "description": "Webhooks is a list of webhooks and the affected resources and operations.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.Webhook" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "MutatingWebhookConfiguration", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.admissionregistration.v1beta1.MutatingWebhookConfigurationList": { + "description": "MutatingWebhookConfigurationList is a list of MutatingWebhookConfiguration.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of MutatingWebhookConfiguration.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.MutatingWebhookConfiguration" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "MutatingWebhookConfigurationList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "MutatingWebhookConfigurationList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.admissionregistration.v1beta1.RuleWithOperations": { + "description": "RuleWithOperations is a tuple of Operations and Resources. It is recommended to make sure that all the tuple expansions are valid.", + "properties": { + "apiGroups": { + "description": "APIGroups is the API groups the resources belong to. '*' is all groups. If '*' is present, the length of the slice must be one. Required.", + "items": { + "type": "string" + }, + "type": "array" + }, + "apiVersions": { + "description": "APIVersions is the API versions the resources belong to. '*' is all versions. If '*' is present, the length of the slice must be one. Required.", + "items": { + "type": "string" + }, + "type": "array" + }, + "operations": { + "description": "Operations is the operations the admission hook cares about - CREATE, UPDATE, or * for all operations. If '*' is present, the length of the slice must be one. Required.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resources": { + "description": "Resources is a list of resources this rule applies to.\n\nFor example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources.\n\nIf wildcard is present, the validation rule will ensure resources do not overlap with each other.\n\nDepending on the enclosing object, subresources might not be allowed. Required.", + "items": { + "type": "string" + }, + "type": "array" + }, + "scope": { + "description": "scope specifies the scope of this rule. Valid values are \"Cluster\", \"Namespaced\", and \"*\" \"Cluster\" means that only cluster-scoped resources will match this rule. Namespace API objects are cluster-scoped. \"Namespaced\" means that only namespaced resources will match this rule. \"*\" means that there are no scope restrictions. Subresources match the scope of their parent resource. Default is \"*\".", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.admissionregistration.v1beta1.ServiceReference": { + "description": "ServiceReference holds a reference to Service.legacy.k8s.io", + "properties": { + "name": { + "description": "`name` is the name of the service. Required", + "type": "string" + }, + "namespace": { + "description": "`namespace` is the namespace of the service. Required", + "type": "string" + }, + "path": { + "description": "`path` is an optional URL path which will be sent in any request to this service.", + "type": "string" + } + }, + "required": [ + "namespace", + "name" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfiguration": { + "description": "ValidatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and object without changing it.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ValidatingWebhookConfiguration" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata." + }, + "webhooks": { + "description": "Webhooks is a list of webhooks and the affected resources and operations.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.Webhook" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingWebhookConfiguration", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfigurationList": { + "description": "ValidatingWebhookConfigurationList is a list of ValidatingWebhookConfiguration.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ValidatingWebhookConfiguration.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfiguration" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ValidatingWebhookConfigurationList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingWebhookConfigurationList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.admissionregistration.v1beta1.Webhook": { + "description": "Webhook describes an admission webhook and the resources and operations it applies to.", + "properties": { + "admissionReviewVersions": { + "description": "AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` versions the Webhook expects. API server will try to use first version in the list which it supports. If none of the versions specified in this list supported by API server, validation will fail for this object. If a persisted webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail and be subject to the failure policy. Default to `['v1beta1']`.", + "items": { + "type": "string" + }, + "type": "array" + }, + "clientConfig": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.WebhookClientConfig", + "description": "ClientConfig defines how to communicate with the hook. Required" + }, + "failurePolicy": { + "description": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Ignore.", + "type": "string" + }, + "name": { + "description": "The name of the admission webhook. Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where \"imagepolicy\" is the name of the webhook, and kubernetes.io is the name of the organization. Required.", + "type": "string" + }, + "namespaceSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything." + }, + "rules": { + "description": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule. However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks from putting the cluster in a state which cannot be recovered from without completely disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.RuleWithOperations" + }, + "type": "array" + }, + "sideEffects": { + "description": "SideEffects states whether this webhookk has side effects. Acceptable values are: Unknown, None, Some, NoneOnDryRun Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission change and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some. Defaults to Unknown.", + "type": "string" + }, + "timeoutSeconds": { + "description": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 30 seconds.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "name", + "clientConfig" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1beta1.WebhookClientConfig": { + "description": "WebhookClientConfig contains the information to make a TLS connection with the webhook", + "properties": { + "caBundle": { + "description": "`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. If unspecified, system trust roots on the apiserver are used.", + "format": "byte", + "type": "string" + }, + "service": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ServiceReference", + "description": "`service` is a reference to the service for this webhook. Either `service` or `url` must be specified.\n\nIf the webhook is running within the cluster, then you should use `service`.\n\nPort 443 will be used if it is open, otherwise it is an error." + }, + "url": { + "description": "`url` gives the location of the webhook, in standard URL form (`scheme://host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.ControllerRevision": { + "description": "ControllerRevision implements an immutable snapshot of state data. Clients are responsible for serializing and deserializing the objects that contain their internal state. Once a ControllerRevision has been successfully created, it can not be updated. The API Server will fail validation of all requests that attempt to mutate the Data field. ControllerRevisions may, however, be deleted. Note that, due to its use by both the DaemonSet and StatefulSet controllers for update and rollback, this object is beta. However, it may be subject to name and representation changes in future releases, and clients should not depend on its stability. It is primarily for internal use by controllers.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "data": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.runtime.RawExtension", + "description": "Data is the serialized representation of the state." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ControllerRevision" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "revision": { + "description": "Revision indicates the revision of the state represented by Data.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "revision" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ControllerRevision", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.ControllerRevisionList": { + "description": "ControllerRevisionList is a resource containing a list of ControllerRevision objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of ControllerRevisions", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.ControllerRevision" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ControllerRevisionList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ControllerRevisionList", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.DaemonSet": { + "description": "DaemonSet represents the configuration of a daemon set.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DaemonSet" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DaemonSetSpec", + "description": "The desired behavior of this daemon set. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DaemonSetStatus", + "description": "The current status of this daemon set. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "DaemonSet", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.DaemonSetCondition": { + "description": "DaemonSetCondition describes the state of a DaemonSet at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of DaemonSet condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.DaemonSetList": { + "description": "DaemonSetList is a collection of daemon sets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "A list of daemon sets.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DaemonSet" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DaemonSetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "DaemonSetList", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.DaemonSetSpec": { + "description": "DaemonSetSpec is the specification of a daemon set.", + "properties": { + "minReadySeconds": { + "description": "The minimum number of seconds for which a newly created DaemonSet pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready).", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "The number of old history to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "A label query over pods that are managed by the daemon set. Must match in order to be controlled. It must match the pod template's labels. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "An object that describes the pod that will be created. The DaemonSet will create exactly one copy of this pod on every node that matches the template's node selector (or on every node if no node selector is specified). More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template" + }, + "updateStrategy": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DaemonSetUpdateStrategy", + "description": "An update strategy to replace existing DaemonSet pods with new pods." + } + }, + "required": [ + "selector", + "template" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.DaemonSetStatus": { + "description": "DaemonSetStatus represents the current status of a daemon set.", + "properties": { + "collisionCount": { + "description": "Count of hash collisions for the DaemonSet. The DaemonSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a DaemonSet's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DaemonSetCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "currentNumberScheduled": { + "description": "The number of nodes that are running at least 1 daemon pod and are supposed to run the daemon pod. More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", + "format": "int32", + "type": "integer" + }, + "desiredNumberScheduled": { + "description": "The total number of nodes that should be running the daemon pod (including nodes correctly running the daemon pod). More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", + "format": "int32", + "type": "integer" + }, + "numberAvailable": { + "description": "The number of nodes that should be running the daemon pod and have one or more of the daemon pod running and available (ready for at least spec.minReadySeconds)", + "format": "int32", + "type": "integer" + }, + "numberMisscheduled": { + "description": "The number of nodes that are running the daemon pod, but are not supposed to run the daemon pod. More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", + "format": "int32", + "type": "integer" + }, + "numberReady": { + "description": "The number of nodes that should be running the daemon pod and have one or more of the daemon pod running and ready.", + "format": "int32", + "type": "integer" + }, + "numberUnavailable": { + "description": "The number of nodes that should be running the daemon pod and have none of the daemon pod running and available (ready for at least spec.minReadySeconds)", + "format": "int32", + "type": "integer" + }, + "observedGeneration": { + "description": "The most recent generation observed by the daemon set controller.", + "format": "int64", + "type": "integer" + }, + "updatedNumberScheduled": { + "description": "The total number of nodes that are running updated daemon pod", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "currentNumberScheduled", + "numberMisscheduled", + "desiredNumberScheduled", + "numberReady" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.DaemonSetUpdateStrategy": { + "description": "DaemonSetUpdateStrategy is a struct used to control the update strategy for a DaemonSet.", + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.apps.v1.RollingUpdateDaemonSet", + "description": "Rolling update config params. Present only if type = \"RollingUpdate\"." + }, + "type": { + "description": "Type of daemon set update. Can be \"RollingUpdate\" or \"OnDelete\". Default is RollingUpdate.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.Deployment": { + "description": "Deployment enables declarative updates for Pods and ReplicaSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Deployment" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentSpec", + "description": "Specification of the desired behavior of the Deployment." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentStatus", + "description": "Most recently observed status of the Deployment." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "Deployment", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.DeploymentCondition": { + "description": "DeploymentCondition describes the state of a deployment at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "lastUpdateTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The last time this condition was updated." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of deployment condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.DeploymentList": { + "description": "DeploymentList is a list of Deployments.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of Deployments.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.Deployment" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DeploymentList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "DeploymentList", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.DeploymentSpec": { + "description": "DeploymentSpec is the specification of the desired behavior of the Deployment.", + "properties": { + "minReadySeconds": { + "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + "format": "int32", + "type": "integer" + }, + "paused": { + "description": "Indicates that the deployment is paused.", + "type": "boolean" + }, + "progressDeadlineSeconds": { + "description": "The maximum time in seconds for a deployment to make progress before it is considered to be failed. The deployment controller will continue to process failed deployments and a condition with a ProgressDeadlineExceeded reason will be surfaced in the deployment status. Note that progress will not be estimated during the time a deployment is paused. Defaults to 600s.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1.", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "The number of old ReplicaSets to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Label selector for pods. Existing ReplicaSets whose pods are selected by this will be the ones affected by this deployment. It must match the pod template's labels." + }, + "strategy": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentStrategy", + "description": "The deployment strategy to use to replace existing pods with new ones.", + "x-kubernetes-patch-strategy": "retainKeys" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template describes the pods that will be created." + } + }, + "required": [ + "selector", + "template" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.DeploymentStatus": { + "description": "DeploymentStatus is the most recently observed status of the Deployment.", + "properties": { + "availableReplicas": { + "description": "Total number of available pods (ready for at least minReadySeconds) targeted by this deployment.", + "format": "int32", + "type": "integer" + }, + "collisionCount": { + "description": "Count of hash collisions for the Deployment. The Deployment controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ReplicaSet.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a deployment's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "The generation observed by the deployment controller.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "Total number of ready pods targeted by this deployment.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Total number of non-terminated pods targeted by this deployment (their labels match the selector).", + "format": "int32", + "type": "integer" + }, + "unavailableReplicas": { + "description": "Total number of unavailable pods targeted by this deployment. This is the total number of pods that are still required for the deployment to have 100% available capacity. They may either be pods that are running but not yet available or pods that still have not been created.", + "format": "int32", + "type": "integer" + }, + "updatedReplicas": { + "description": "Total number of non-terminated pods targeted by this deployment that have the desired template spec.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.DeploymentStrategy": { + "description": "DeploymentStrategy describes how to replace existing pods with new ones.", + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.apps.v1.RollingUpdateDeployment", + "description": "Rolling update config params. Present only if DeploymentStrategyType = RollingUpdate." + }, + "type": { + "description": "Type of deployment. Can be \"Recreate\" or \"RollingUpdate\". Default is RollingUpdate.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.ReplicaSet": { + "description": "ReplicaSet ensures that a specified number of pod replicas are running at any given time.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ReplicaSet" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "If the Labels of a ReplicaSet are empty, they are defaulted to be the same as the Pod(s) that the ReplicaSet manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1.ReplicaSetSpec", + "description": "Spec defines the specification of the desired behavior of the ReplicaSet. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1.ReplicaSetStatus", + "description": "Status is the most recently observed status of the ReplicaSet. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ReplicaSet", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.ReplicaSetCondition": { + "description": "ReplicaSetCondition describes the state of a replica set at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of replica set condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.ReplicaSetList": { + "description": "ReplicaSetList is a collection of ReplicaSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ReplicaSets. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.ReplicaSet" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ReplicaSetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ReplicaSetList", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.ReplicaSetSpec": { + "description": "ReplicaSetSpec is the specification of a ReplicaSet.", + "properties": { + "minReadySeconds": { + "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/#what-is-a-replicationcontroller", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Selector is a label query over pods that should match the replica count. Label keys and values that must match in order to be controlled by this replica set. It must match the pod template's labels. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template is the object that describes the pod that will be created if insufficient replicas are detected. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template" + } + }, + "required": [ + "selector" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.ReplicaSetStatus": { + "description": "ReplicaSetStatus represents the current status of a ReplicaSet.", + "properties": { + "availableReplicas": { + "description": "The number of available replicas (ready for at least minReadySeconds) for this replica set.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a replica set's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.ReplicaSetCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "fullyLabeledReplicas": { + "description": "The number of pods that have labels matching the labels of the pod template of the replicaset.", + "format": "int32", + "type": "integer" + }, + "observedGeneration": { + "description": "ObservedGeneration reflects the generation of the most recently observed ReplicaSet.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "The number of ready replicas for this replica set.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Replicas is the most recently oberved number of replicas. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/#what-is-a-replicationcontroller", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.RollingUpdateDaemonSet": { + "description": "Spec to control the desired behavior of daemon set rolling update.", + "properties": { + "maxUnavailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of DaemonSet pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of total number of DaemonSet pods at the start of the update (ex: 10%). Absolute number is calculated from percentage by rounding up. This cannot be 0. Default value is 1. Example: when this is set to 30%, at most 30% of the total number of nodes that should be running the daemon pod (i.e. status.desiredNumberScheduled) can have their pods stopped for an update at any given time. The update starts by stopping at most 30% of those DaemonSet pods and then brings up new DaemonSet pods in their place. Once the new pods are available, it then proceeds onto other DaemonSet pods, thus ensuring that at least 70% of original number of DaemonSet pods are available at all times during the update." + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.RollingUpdateDeployment": { + "description": "Spec to control the desired behavior of rolling update.", + "properties": { + "maxSurge": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of pods that can be scheduled above the desired number of pods. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxUnavailable is 0. Absolute number is calculated from percentage by rounding up. Defaults to 25%. Example: when this is set to 30%, the new ReplicaSet can be scaled up immediately when the rolling update starts, such that the total number of old and new pods do not exceed 130% of desired pods. Once old pods have been killed, new ReplicaSet can be scaled up further, ensuring that total number of pods running at any time during the update is at most 130% of desired pods." + }, + "maxUnavailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding down. This can not be 0 if MaxSurge is 0. Defaults to 25%. Example: when this is set to 30%, the old ReplicaSet can be scaled down to 70% of desired pods immediately when the rolling update starts. Once new pods are ready, old ReplicaSet can be scaled down further, followed by scaling up the new ReplicaSet, ensuring that the total number of pods available at all times during the update is at least 70% of desired pods." + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.RollingUpdateStatefulSetStrategy": { + "description": "RollingUpdateStatefulSetStrategy is used to communicate parameter for RollingUpdateStatefulSetStrategyType.", + "properties": { + "partition": { + "description": "Partition indicates the ordinal at which the StatefulSet should be partitioned. Default value is 0.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.StatefulSet": { + "description": "StatefulSet represents a set of pods with consistent identities. Identities are defined as:\n - Network: A single stable DNS and hostname.\n - Storage: As many VolumeClaims as requested.\nThe StatefulSet guarantees that a given network identity will always map to the same storage identity.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StatefulSet" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1.StatefulSetSpec", + "description": "Spec defines the desired identities of pods in this set." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1.StatefulSetStatus", + "description": "Status is the current status of Pods in this StatefulSet. This data may be out of date by some window of time." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "StatefulSet", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.StatefulSetCondition": { + "description": "StatefulSetCondition describes the state of a statefulset at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of statefulset condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.StatefulSetList": { + "description": "StatefulSetList is a collection of StatefulSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.StatefulSet" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StatefulSetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "StatefulSetList", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.StatefulSetSpec": { + "description": "A StatefulSetSpec is the specification of a StatefulSet.", + "properties": { + "podManagementPolicy": { + "description": "podManagementPolicy controls how pods are created during initial scale up, when replacing pods on nodes, or when scaling down. The default policy is `OrderedReady`, where pods are created in increasing order (pod-0, then pod-1, etc) and the controller will wait until each pod is ready before continuing. When scaling down, the pods are removed in the opposite order. The alternative policy is `Parallel` which will create pods in parallel to match the desired scale without waiting, and on scale down will delete all pods at once.", + "type": "string" + }, + "replicas": { + "description": "replicas is the desired number of replicas of the given Template. These are replicas in the sense that they are instantiations of the same Template, but individual replicas also have a consistent identity. If unspecified, defaults to 1.", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "revisionHistoryLimit is the maximum number of revisions that will be maintained in the StatefulSet's revision history. The revision history consists of all revisions not represented by a currently applied StatefulSetSpec version. The default value is 10.", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector is a label query over pods that should match the replica count. It must match the pod template's labels. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "serviceName": { + "description": "serviceName is the name of the service that governs this StatefulSet. This service must exist before the StatefulSet, and is responsible for the network identity of the set. Pods get DNS/hostnames that follow the pattern: pod-specific-string.serviceName.default.svc.cluster.local where \"pod-specific-string\" is managed by the StatefulSet controller.", + "type": "string" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "template is the object that describes the pod that will be created if insufficient replicas are detected. Each pod stamped out by the StatefulSet will fulfill this Template, but have a unique identity from the rest of the StatefulSet." + }, + "updateStrategy": { + "$ref": "#/definitions/io.k8s.api.apps.v1.StatefulSetUpdateStrategy", + "description": "updateStrategy indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the StatefulSet when a revision is made to Template." + }, + "volumeClaimTemplates": { + "description": "volumeClaimTemplates is a list of claims that pods are allowed to reference. The StatefulSet controller is responsible for mapping network identities to claims in a way that maintains the identity of a pod. Every claim in this list must have at least one matching (by name) volumeMount in one container in the template. A claim in this list takes precedence over any volumes in the template, with the same name.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaim" + }, + "type": "array" + } + }, + "required": [ + "selector", + "template", + "serviceName" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.StatefulSetStatus": { + "description": "StatefulSetStatus represents the current state of a StatefulSet.", + "properties": { + "collisionCount": { + "description": "collisionCount is the count of hash collisions for the StatefulSet. The StatefulSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a statefulset's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.StatefulSetCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "currentReplicas": { + "description": "currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by currentRevision.", + "format": "int32", + "type": "integer" + }, + "currentRevision": { + "description": "currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [0,currentReplicas).", + "type": "string" + }, + "observedGeneration": { + "description": "observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the StatefulSet's generation, which is updated on mutation by the API Server.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "replicas is the number of Pods created by the StatefulSet controller.", + "format": "int32", + "type": "integer" + }, + "updateRevision": { + "description": "updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [replicas-updatedReplicas,replicas)", + "type": "string" + }, + "updatedReplicas": { + "description": "updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by updateRevision.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.StatefulSetUpdateStrategy": { + "description": "StatefulSetUpdateStrategy indicates the strategy that the StatefulSet controller will use to perform updates. It includes any additional parameters necessary to perform the update for the indicated strategy.", + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.apps.v1.RollingUpdateStatefulSetStrategy", + "description": "RollingUpdate is used to communicate parameters when Type is RollingUpdateStatefulSetStrategyType." + }, + "type": { + "description": "Type indicates the type of the StatefulSetUpdateStrategy. Default is RollingUpdate.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta1.ControllerRevision": { + "description": "DEPRECATED - This group version of ControllerRevision is deprecated by apps/v1beta2/ControllerRevision. See the release notes for more information. ControllerRevision implements an immutable snapshot of state data. Clients are responsible for serializing and deserializing the objects that contain their internal state. Once a ControllerRevision has been successfully created, it can not be updated. The API Server will fail validation of all requests that attempt to mutate the Data field. ControllerRevisions may, however, be deleted. Note that, due to its use by both the DaemonSet and StatefulSet controllers for update and rollback, this object is beta. However, it may be subject to name and representation changes in future releases, and clients should not depend on its stability. It is primarily for internal use by controllers.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "data": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.runtime.RawExtension", + "description": "Data is the serialized representation of the state." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ControllerRevision" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "revision": { + "description": "Revision indicates the revision of the state represented by Data.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "revision" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ControllerRevision", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.apps.v1beta1.ControllerRevisionList": { + "description": "ControllerRevisionList is a resource containing a list of ControllerRevision objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of ControllerRevisions", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.ControllerRevision" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ControllerRevisionList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ControllerRevisionList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.apps.v1beta1.Deployment": { + "description": "DEPRECATED - This group version of Deployment is deprecated by apps/v1beta2/Deployment. See the release notes for more information. Deployment enables declarative updates for Pods and ReplicaSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Deployment" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.DeploymentSpec", + "description": "Specification of the desired behavior of the Deployment." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.DeploymentStatus", + "description": "Most recently observed status of the Deployment." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "Deployment", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.apps.v1beta1.DeploymentCondition": { + "description": "DeploymentCondition describes the state of a deployment at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "lastUpdateTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The last time this condition was updated." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of deployment condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta1.DeploymentList": { + "description": "DeploymentList is a list of Deployments.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of Deployments.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.Deployment" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DeploymentList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "DeploymentList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.apps.v1beta1.DeploymentRollback": { + "description": "DEPRECATED. DeploymentRollback stores the information required to rollback a deployment.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DeploymentRollback" + ] + }, + "name": { + "description": "Required: This must match the Name of a deployment.", + "type": "string" + }, + "rollbackTo": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.RollbackConfig", + "description": "The config of this deployment rollback." + }, + "updatedAnnotations": { + "additionalProperties": { + "type": "string" + }, + "description": "The annotations to be updated to a deployment", + "type": "object" + } + }, + "required": [ + "name", + "rollbackTo" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "DeploymentRollback", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.apps.v1beta1.DeploymentSpec": { + "description": "DeploymentSpec is the specification of the desired behavior of the Deployment.", + "properties": { + "minReadySeconds": { + "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + "format": "int32", + "type": "integer" + }, + "paused": { + "description": "Indicates that the deployment is paused.", + "type": "boolean" + }, + "progressDeadlineSeconds": { + "description": "The maximum time in seconds for a deployment to make progress before it is considered to be failed. The deployment controller will continue to process failed deployments and a condition with a ProgressDeadlineExceeded reason will be surfaced in the deployment status. Note that progress will not be estimated during the time a deployment is paused. Defaults to 600s.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1.", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "The number of old ReplicaSets to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 2.", + "format": "int32", + "type": "integer" + }, + "rollbackTo": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.RollbackConfig", + "description": "DEPRECATED. The config this deployment is rolling back to. Will be cleared after rollback is done." + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Label selector for pods. Existing ReplicaSets whose pods are selected by this will be the ones affected by this deployment." + }, + "strategy": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.DeploymentStrategy", + "description": "The deployment strategy to use to replace existing pods with new ones.", + "x-kubernetes-patch-strategy": "retainKeys" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template describes the pods that will be created." + } + }, + "required": [ + "template" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta1.DeploymentStatus": { + "description": "DeploymentStatus is the most recently observed status of the Deployment.", + "properties": { + "availableReplicas": { + "description": "Total number of available pods (ready for at least minReadySeconds) targeted by this deployment.", + "format": "int32", + "type": "integer" + }, + "collisionCount": { + "description": "Count of hash collisions for the Deployment. The Deployment controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ReplicaSet.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a deployment's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.DeploymentCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "The generation observed by the deployment controller.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "Total number of ready pods targeted by this deployment.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Total number of non-terminated pods targeted by this deployment (their labels match the selector).", + "format": "int32", + "type": "integer" + }, + "unavailableReplicas": { + "description": "Total number of unavailable pods targeted by this deployment. This is the total number of pods that are still required for the deployment to have 100% available capacity. They may either be pods that are running but not yet available or pods that still have not been created.", + "format": "int32", + "type": "integer" + }, + "updatedReplicas": { + "description": "Total number of non-terminated pods targeted by this deployment that have the desired template spec.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta1.DeploymentStrategy": { + "description": "DeploymentStrategy describes how to replace existing pods with new ones.", + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.RollingUpdateDeployment", + "description": "Rolling update config params. Present only if DeploymentStrategyType = RollingUpdate." + }, + "type": { + "description": "Type of deployment. Can be \"Recreate\" or \"RollingUpdate\". Default is RollingUpdate.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta1.RollbackConfig": { + "description": "DEPRECATED.", + "properties": { + "revision": { + "description": "The revision to rollback to. If set to 0, rollback to the last revision.", + "format": "int64", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta1.RollingUpdateDeployment": { + "description": "Spec to control the desired behavior of rolling update.", + "properties": { + "maxSurge": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of pods that can be scheduled above the desired number of pods. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxUnavailable is 0. Absolute number is calculated from percentage by rounding up. Defaults to 25%. Example: when this is set to 30%, the new ReplicaSet can be scaled up immediately when the rolling update starts, such that the total number of old and new pods do not exceed 130% of desired pods. Once old pods have been killed, new ReplicaSet can be scaled up further, ensuring that total number of pods running at any time during the update is at most 130% of desired pods." + }, + "maxUnavailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding down. This can not be 0 if MaxSurge is 0. Defaults to 25%. Example: when this is set to 30%, the old ReplicaSet can be scaled down to 70% of desired pods immediately when the rolling update starts. Once new pods are ready, old ReplicaSet can be scaled down further, followed by scaling up the new ReplicaSet, ensuring that the total number of pods available at all times during the update is at least 70% of desired pods." + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta1.RollingUpdateStatefulSetStrategy": { + "description": "RollingUpdateStatefulSetStrategy is used to communicate parameter for RollingUpdateStatefulSetStrategyType.", + "properties": { + "partition": { + "description": "Partition indicates the ordinal at which the StatefulSet should be partitioned.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta1.Scale": { + "description": "Scale represents a scaling request for a resource.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Scale" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.ScaleSpec", + "description": "defines the behavior of the scale. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.ScaleStatus", + "description": "current status of the scale. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status. Read-only." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "Scale", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.apps.v1beta1.ScaleSpec": { + "description": "ScaleSpec describes the attributes of a scale subresource", + "properties": { + "replicas": { + "description": "desired number of instances for the scaled object.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta1.ScaleStatus": { + "description": "ScaleStatus represents the current status of a scale subresource.", + "properties": { + "replicas": { + "description": "actual number of observed instances of the scaled object.", + "format": "int32", + "type": "integer" + }, + "selector": { + "additionalProperties": { + "type": "string" + }, + "description": "label query over pods that should match the replicas count. More info: http://kubernetes.io/docs/user-guide/labels#label-selectors", + "type": "object" + }, + "targetSelector": { + "description": "label selector for pods that should match the replicas count. This is a serializated version of both map-based and more expressive set-based selectors. This is done to avoid introspection in the clients. The string will be in the same format as the query-param syntax. If the target type only supports map-based selectors, both this field and map-based selector field are populated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", + "type": "string" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta1.StatefulSet": { + "description": "DEPRECATED - This group version of StatefulSet is deprecated by apps/v1beta2/StatefulSet. See the release notes for more information. StatefulSet represents a set of pods with consistent identities. Identities are defined as:\n - Network: A single stable DNS and hostname.\n - Storage: As many VolumeClaims as requested.\nThe StatefulSet guarantees that a given network identity will always map to the same storage identity.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StatefulSet" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.StatefulSetSpec", + "description": "Spec defines the desired identities of pods in this set." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.StatefulSetStatus", + "description": "Status is the current status of Pods in this StatefulSet. This data may be out of date by some window of time." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "StatefulSet", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.apps.v1beta1.StatefulSetCondition": { + "description": "StatefulSetCondition describes the state of a statefulset at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of statefulset condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta1.StatefulSetList": { + "description": "StatefulSetList is a collection of StatefulSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.StatefulSet" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StatefulSetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "StatefulSetList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.apps.v1beta1.StatefulSetSpec": { + "description": "A StatefulSetSpec is the specification of a StatefulSet.", + "properties": { + "podManagementPolicy": { + "description": "podManagementPolicy controls how pods are created during initial scale up, when replacing pods on nodes, or when scaling down. The default policy is `OrderedReady`, where pods are created in increasing order (pod-0, then pod-1, etc) and the controller will wait until each pod is ready before continuing. When scaling down, the pods are removed in the opposite order. The alternative policy is `Parallel` which will create pods in parallel to match the desired scale without waiting, and on scale down will delete all pods at once.", + "type": "string" + }, + "replicas": { + "description": "replicas is the desired number of replicas of the given Template. These are replicas in the sense that they are instantiations of the same Template, but individual replicas also have a consistent identity. If unspecified, defaults to 1.", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "revisionHistoryLimit is the maximum number of revisions that will be maintained in the StatefulSet's revision history. The revision history consists of all revisions not represented by a currently applied StatefulSetSpec version. The default value is 10.", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector is a label query over pods that should match the replica count. If empty, defaulted to labels on the pod template. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "serviceName": { + "description": "serviceName is the name of the service that governs this StatefulSet. This service must exist before the StatefulSet, and is responsible for the network identity of the set. Pods get DNS/hostnames that follow the pattern: pod-specific-string.serviceName.default.svc.cluster.local where \"pod-specific-string\" is managed by the StatefulSet controller.", + "type": "string" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "template is the object that describes the pod that will be created if insufficient replicas are detected. Each pod stamped out by the StatefulSet will fulfill this Template, but have a unique identity from the rest of the StatefulSet." + }, + "updateStrategy": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.StatefulSetUpdateStrategy", + "description": "updateStrategy indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the StatefulSet when a revision is made to Template." + }, + "volumeClaimTemplates": { + "description": "volumeClaimTemplates is a list of claims that pods are allowed to reference. The StatefulSet controller is responsible for mapping network identities to claims in a way that maintains the identity of a pod. Every claim in this list must have at least one matching (by name) volumeMount in one container in the template. A claim in this list takes precedence over any volumes in the template, with the same name.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaim" + }, + "type": "array" + } + }, + "required": [ + "template", + "serviceName" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta1.StatefulSetStatus": { + "description": "StatefulSetStatus represents the current state of a StatefulSet.", + "properties": { + "collisionCount": { + "description": "collisionCount is the count of hash collisions for the StatefulSet. The StatefulSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a statefulset's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.StatefulSetCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "currentReplicas": { + "description": "currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by currentRevision.", + "format": "int32", + "type": "integer" + }, + "currentRevision": { + "description": "currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [0,currentReplicas).", + "type": "string" + }, + "observedGeneration": { + "description": "observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the StatefulSet's generation, which is updated on mutation by the API Server.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "replicas is the number of Pods created by the StatefulSet controller.", + "format": "int32", + "type": "integer" + }, + "updateRevision": { + "description": "updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [replicas-updatedReplicas,replicas)", + "type": "string" + }, + "updatedReplicas": { + "description": "updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by updateRevision.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta1.StatefulSetUpdateStrategy": { + "description": "StatefulSetUpdateStrategy indicates the strategy that the StatefulSet controller will use to perform updates. It includes any additional parameters necessary to perform the update for the indicated strategy.", + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.RollingUpdateStatefulSetStrategy", + "description": "RollingUpdate is used to communicate parameters when Type is RollingUpdateStatefulSetStrategyType." + }, + "type": { + "description": "Type indicates the type of the StatefulSetUpdateStrategy.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta2.ControllerRevision": { + "description": "DEPRECATED - This group version of ControllerRevision is deprecated by apps/v1/ControllerRevision. See the release notes for more information. ControllerRevision implements an immutable snapshot of state data. Clients are responsible for serializing and deserializing the objects that contain their internal state. Once a ControllerRevision has been successfully created, it can not be updated. The API Server will fail validation of all requests that attempt to mutate the Data field. ControllerRevisions may, however, be deleted. Note that, due to its use by both the DaemonSet and StatefulSet controllers for update and rollback, this object is beta. However, it may be subject to name and representation changes in future releases, and clients should not depend on its stability. It is primarily for internal use by controllers.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "data": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.runtime.RawExtension", + "description": "Data is the serialized representation of the state." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ControllerRevision" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "revision": { + "description": "Revision indicates the revision of the state represented by Data.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "revision" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ControllerRevision", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.apps.v1beta2.ControllerRevisionList": { + "description": "ControllerRevisionList is a resource containing a list of ControllerRevision objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of ControllerRevisions", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.ControllerRevision" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ControllerRevisionList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ControllerRevisionList", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.apps.v1beta2.DaemonSet": { + "description": "DEPRECATED - This group version of DaemonSet is deprecated by apps/v1/DaemonSet. See the release notes for more information. DaemonSet represents the configuration of a daemon set.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DaemonSet" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.DaemonSetSpec", + "description": "The desired behavior of this daemon set. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.DaemonSetStatus", + "description": "The current status of this daemon set. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "DaemonSet", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.apps.v1beta2.DaemonSetCondition": { + "description": "DaemonSetCondition describes the state of a DaemonSet at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of DaemonSet condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta2.DaemonSetList": { + "description": "DaemonSetList is a collection of daemon sets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "A list of daemon sets.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.DaemonSet" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DaemonSetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "DaemonSetList", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.apps.v1beta2.DaemonSetSpec": { + "description": "DaemonSetSpec is the specification of a daemon set.", + "properties": { + "minReadySeconds": { + "description": "The minimum number of seconds for which a newly created DaemonSet pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready).", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "The number of old history to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "A label query over pods that are managed by the daemon set. Must match in order to be controlled. It must match the pod template's labels. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "An object that describes the pod that will be created. The DaemonSet will create exactly one copy of this pod on every node that matches the template's node selector (or on every node if no node selector is specified). More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template" + }, + "updateStrategy": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.DaemonSetUpdateStrategy", + "description": "An update strategy to replace existing DaemonSet pods with new pods." + } + }, + "required": [ + "selector", + "template" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta2.DaemonSetStatus": { + "description": "DaemonSetStatus represents the current status of a daemon set.", + "properties": { + "collisionCount": { + "description": "Count of hash collisions for the DaemonSet. The DaemonSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a DaemonSet's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.DaemonSetCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "currentNumberScheduled": { + "description": "The number of nodes that are running at least 1 daemon pod and are supposed to run the daemon pod. More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", + "format": "int32", + "type": "integer" + }, + "desiredNumberScheduled": { + "description": "The total number of nodes that should be running the daemon pod (including nodes correctly running the daemon pod). More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", + "format": "int32", + "type": "integer" + }, + "numberAvailable": { + "description": "The number of nodes that should be running the daemon pod and have one or more of the daemon pod running and available (ready for at least spec.minReadySeconds)", + "format": "int32", + "type": "integer" + }, + "numberMisscheduled": { + "description": "The number of nodes that are running the daemon pod, but are not supposed to run the daemon pod. More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", + "format": "int32", + "type": "integer" + }, + "numberReady": { + "description": "The number of nodes that should be running the daemon pod and have one or more of the daemon pod running and ready.", + "format": "int32", + "type": "integer" + }, + "numberUnavailable": { + "description": "The number of nodes that should be running the daemon pod and have none of the daemon pod running and available (ready for at least spec.minReadySeconds)", + "format": "int32", + "type": "integer" + }, + "observedGeneration": { + "description": "The most recent generation observed by the daemon set controller.", + "format": "int64", + "type": "integer" + }, + "updatedNumberScheduled": { + "description": "The total number of nodes that are running updated daemon pod", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "currentNumberScheduled", + "numberMisscheduled", + "desiredNumberScheduled", + "numberReady" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta2.DaemonSetUpdateStrategy": { + "description": "DaemonSetUpdateStrategy is a struct used to control the update strategy for a DaemonSet.", + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.RollingUpdateDaemonSet", + "description": "Rolling update config params. Present only if type = \"RollingUpdate\"." + }, + "type": { + "description": "Type of daemon set update. Can be \"RollingUpdate\" or \"OnDelete\". Default is RollingUpdate.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta2.Deployment": { + "description": "DEPRECATED - This group version of Deployment is deprecated by apps/v1/Deployment. See the release notes for more information. Deployment enables declarative updates for Pods and ReplicaSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Deployment" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.DeploymentSpec", + "description": "Specification of the desired behavior of the Deployment." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.DeploymentStatus", + "description": "Most recently observed status of the Deployment." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "Deployment", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.apps.v1beta2.DeploymentCondition": { + "description": "DeploymentCondition describes the state of a deployment at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "lastUpdateTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The last time this condition was updated." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of deployment condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta2.DeploymentList": { + "description": "DeploymentList is a list of Deployments.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of Deployments.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.Deployment" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DeploymentList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "DeploymentList", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.apps.v1beta2.DeploymentSpec": { + "description": "DeploymentSpec is the specification of the desired behavior of the Deployment.", + "properties": { + "minReadySeconds": { + "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + "format": "int32", + "type": "integer" + }, + "paused": { + "description": "Indicates that the deployment is paused.", + "type": "boolean" + }, + "progressDeadlineSeconds": { + "description": "The maximum time in seconds for a deployment to make progress before it is considered to be failed. The deployment controller will continue to process failed deployments and a condition with a ProgressDeadlineExceeded reason will be surfaced in the deployment status. Note that progress will not be estimated during the time a deployment is paused. Defaults to 600s.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1.", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "The number of old ReplicaSets to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Label selector for pods. Existing ReplicaSets whose pods are selected by this will be the ones affected by this deployment. It must match the pod template's labels." + }, + "strategy": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.DeploymentStrategy", + "description": "The deployment strategy to use to replace existing pods with new ones.", + "x-kubernetes-patch-strategy": "retainKeys" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template describes the pods that will be created." + } + }, + "required": [ + "selector", + "template" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta2.DeploymentStatus": { + "description": "DeploymentStatus is the most recently observed status of the Deployment.", + "properties": { + "availableReplicas": { + "description": "Total number of available pods (ready for at least minReadySeconds) targeted by this deployment.", + "format": "int32", + "type": "integer" + }, + "collisionCount": { + "description": "Count of hash collisions for the Deployment. The Deployment controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ReplicaSet.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a deployment's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.DeploymentCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "The generation observed by the deployment controller.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "Total number of ready pods targeted by this deployment.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Total number of non-terminated pods targeted by this deployment (their labels match the selector).", + "format": "int32", + "type": "integer" + }, + "unavailableReplicas": { + "description": "Total number of unavailable pods targeted by this deployment. This is the total number of pods that are still required for the deployment to have 100% available capacity. They may either be pods that are running but not yet available or pods that still have not been created.", + "format": "int32", + "type": "integer" + }, + "updatedReplicas": { + "description": "Total number of non-terminated pods targeted by this deployment that have the desired template spec.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta2.DeploymentStrategy": { + "description": "DeploymentStrategy describes how to replace existing pods with new ones.", + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.RollingUpdateDeployment", + "description": "Rolling update config params. Present only if DeploymentStrategyType = RollingUpdate." + }, + "type": { + "description": "Type of deployment. Can be \"Recreate\" or \"RollingUpdate\". Default is RollingUpdate.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta2.ReplicaSet": { + "description": "DEPRECATED - This group version of ReplicaSet is deprecated by apps/v1/ReplicaSet. See the release notes for more information. ReplicaSet ensures that a specified number of pod replicas are running at any given time.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ReplicaSet" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "If the Labels of a ReplicaSet are empty, they are defaulted to be the same as the Pod(s) that the ReplicaSet manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.ReplicaSetSpec", + "description": "Spec defines the specification of the desired behavior of the ReplicaSet. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.ReplicaSetStatus", + "description": "Status is the most recently observed status of the ReplicaSet. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ReplicaSet", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.apps.v1beta2.ReplicaSetCondition": { + "description": "ReplicaSetCondition describes the state of a replica set at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of replica set condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta2.ReplicaSetList": { + "description": "ReplicaSetList is a collection of ReplicaSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ReplicaSets. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.ReplicaSet" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ReplicaSetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ReplicaSetList", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.apps.v1beta2.ReplicaSetSpec": { + "description": "ReplicaSetSpec is the specification of a ReplicaSet.", + "properties": { + "minReadySeconds": { + "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/#what-is-a-replicationcontroller", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Selector is a label query over pods that should match the replica count. Label keys and values that must match in order to be controlled by this replica set. It must match the pod template's labels. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template is the object that describes the pod that will be created if insufficient replicas are detected. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template" + } + }, + "required": [ + "selector" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta2.ReplicaSetStatus": { + "description": "ReplicaSetStatus represents the current status of a ReplicaSet.", + "properties": { + "availableReplicas": { + "description": "The number of available replicas (ready for at least minReadySeconds) for this replica set.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a replica set's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.ReplicaSetCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "fullyLabeledReplicas": { + "description": "The number of pods that have labels matching the labels of the pod template of the replicaset.", + "format": "int32", + "type": "integer" + }, + "observedGeneration": { + "description": "ObservedGeneration reflects the generation of the most recently observed ReplicaSet.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "The number of ready replicas for this replica set.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Replicas is the most recently oberved number of replicas. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/#what-is-a-replicationcontroller", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta2.RollingUpdateDaemonSet": { + "description": "Spec to control the desired behavior of daemon set rolling update.", + "properties": { + "maxUnavailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of DaemonSet pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of total number of DaemonSet pods at the start of the update (ex: 10%). Absolute number is calculated from percentage by rounding up. This cannot be 0. Default value is 1. Example: when this is set to 30%, at most 30% of the total number of nodes that should be running the daemon pod (i.e. status.desiredNumberScheduled) can have their pods stopped for an update at any given time. The update starts by stopping at most 30% of those DaemonSet pods and then brings up new DaemonSet pods in their place. Once the new pods are available, it then proceeds onto other DaemonSet pods, thus ensuring that at least 70% of original number of DaemonSet pods are available at all times during the update." + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta2.RollingUpdateDeployment": { + "description": "Spec to control the desired behavior of rolling update.", + "properties": { + "maxSurge": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of pods that can be scheduled above the desired number of pods. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxUnavailable is 0. Absolute number is calculated from percentage by rounding up. Defaults to 25%. Example: when this is set to 30%, the new ReplicaSet can be scaled up immediately when the rolling update starts, such that the total number of old and new pods do not exceed 130% of desired pods. Once old pods have been killed, new ReplicaSet can be scaled up further, ensuring that total number of pods running at any time during the update is at most 130% of desired pods." + }, + "maxUnavailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding down. This can not be 0 if MaxSurge is 0. Defaults to 25%. Example: when this is set to 30%, the old ReplicaSet can be scaled down to 70% of desired pods immediately when the rolling update starts. Once new pods are ready, old ReplicaSet can be scaled down further, followed by scaling up the new ReplicaSet, ensuring that the total number of pods available at all times during the update is at least 70% of desired pods." + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta2.RollingUpdateStatefulSetStrategy": { + "description": "RollingUpdateStatefulSetStrategy is used to communicate parameter for RollingUpdateStatefulSetStrategyType.", + "properties": { + "partition": { + "description": "Partition indicates the ordinal at which the StatefulSet should be partitioned. Default value is 0.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta2.Scale": { + "description": "Scale represents a scaling request for a resource.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Scale" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.ScaleSpec", + "description": "defines the behavior of the scale. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.ScaleStatus", + "description": "current status of the scale. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status. Read-only." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "Scale", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.apps.v1beta2.ScaleSpec": { + "description": "ScaleSpec describes the attributes of a scale subresource", + "properties": { + "replicas": { + "description": "desired number of instances for the scaled object.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1beta2.ScaleStatus": { + "description": "ScaleStatus represents the current status of a scale subresource.", + "properties": { + "replicas": { + "description": "actual number of observed instances of the scaled object.", + "format": "int32", + "type": "integer" + }, + "selector": { + "additionalProperties": { + "type": "string" + }, + "description": "label query over pods that should match the replicas count. More info: http://kubernetes.io/docs/user-guide/labels#label-selectors", + "type": "object" + }, + "targetSelector": { + "description": "label selector for pods that should match the replicas count. This is a serializated version of both map-based and more expressive set-based selectors. This is done to avoid introspection in the clients. The string will be in the same format as the query-param syntax. If the target type only supports map-based selectors, both this field and map-based selector field are populated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", + "type": "string" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta2.StatefulSet": { + "description": "DEPRECATED - This group version of StatefulSet is deprecated by apps/v1/StatefulSet. See the release notes for more information. StatefulSet represents a set of pods with consistent identities. Identities are defined as:\n - Network: A single stable DNS and hostname.\n - Storage: As many VolumeClaims as requested.\nThe StatefulSet guarantees that a given network identity will always map to the same storage identity.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StatefulSet" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.StatefulSetSpec", + "description": "Spec defines the desired identities of pods in this set." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.StatefulSetStatus", + "description": "Status is the current status of Pods in this StatefulSet. This data may be out of date by some window of time." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "StatefulSet", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.apps.v1beta2.StatefulSetCondition": { + "description": "StatefulSetCondition describes the state of a statefulset at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of statefulset condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta2.StatefulSetList": { + "description": "StatefulSetList is a collection of StatefulSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.StatefulSet" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StatefulSetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "StatefulSetList", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.apps.v1beta2.StatefulSetSpec": { + "description": "A StatefulSetSpec is the specification of a StatefulSet.", + "properties": { + "podManagementPolicy": { + "description": "podManagementPolicy controls how pods are created during initial scale up, when replacing pods on nodes, or when scaling down. The default policy is `OrderedReady`, where pods are created in increasing order (pod-0, then pod-1, etc) and the controller will wait until each pod is ready before continuing. When scaling down, the pods are removed in the opposite order. The alternative policy is `Parallel` which will create pods in parallel to match the desired scale without waiting, and on scale down will delete all pods at once.", + "type": "string" + }, + "replicas": { + "description": "replicas is the desired number of replicas of the given Template. These are replicas in the sense that they are instantiations of the same Template, but individual replicas also have a consistent identity. If unspecified, defaults to 1.", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "revisionHistoryLimit is the maximum number of revisions that will be maintained in the StatefulSet's revision history. The revision history consists of all revisions not represented by a currently applied StatefulSetSpec version. The default value is 10.", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector is a label query over pods that should match the replica count. It must match the pod template's labels. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "serviceName": { + "description": "serviceName is the name of the service that governs this StatefulSet. This service must exist before the StatefulSet, and is responsible for the network identity of the set. Pods get DNS/hostnames that follow the pattern: pod-specific-string.serviceName.default.svc.cluster.local where \"pod-specific-string\" is managed by the StatefulSet controller.", + "type": "string" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "template is the object that describes the pod that will be created if insufficient replicas are detected. Each pod stamped out by the StatefulSet will fulfill this Template, but have a unique identity from the rest of the StatefulSet." + }, + "updateStrategy": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.StatefulSetUpdateStrategy", + "description": "updateStrategy indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the StatefulSet when a revision is made to Template." + }, + "volumeClaimTemplates": { + "description": "volumeClaimTemplates is a list of claims that pods are allowed to reference. The StatefulSet controller is responsible for mapping network identities to claims in a way that maintains the identity of a pod. Every claim in this list must have at least one matching (by name) volumeMount in one container in the template. A claim in this list takes precedence over any volumes in the template, with the same name.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaim" + }, + "type": "array" + } + }, + "required": [ + "selector", + "template", + "serviceName" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta2.StatefulSetStatus": { + "description": "StatefulSetStatus represents the current state of a StatefulSet.", + "properties": { + "collisionCount": { + "description": "collisionCount is the count of hash collisions for the StatefulSet. The StatefulSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a statefulset's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.StatefulSetCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "currentReplicas": { + "description": "currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by currentRevision.", + "format": "int32", + "type": "integer" + }, + "currentRevision": { + "description": "currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [0,currentReplicas).", + "type": "string" + }, + "observedGeneration": { + "description": "observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the StatefulSet's generation, which is updated on mutation by the API Server.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "replicas is the number of Pods created by the StatefulSet controller.", + "format": "int32", + "type": "integer" + }, + "updateRevision": { + "description": "updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [replicas-updatedReplicas,replicas)", + "type": "string" + }, + "updatedReplicas": { + "description": "updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by updateRevision.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.apps.v1beta2.StatefulSetUpdateStrategy": { + "description": "StatefulSetUpdateStrategy indicates the strategy that the StatefulSet controller will use to perform updates. It includes any additional parameters necessary to perform the update for the indicated strategy.", + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.RollingUpdateStatefulSetStrategy", + "description": "RollingUpdate is used to communicate parameters when Type is RollingUpdateStatefulSetStrategyType." + }, + "type": { + "description": "Type indicates the type of the StatefulSetUpdateStrategy. Default is RollingUpdate.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.auditregistration.v1alpha1.AuditSink": { + "description": "AuditSink represents a cluster level audit sink", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "AuditSink" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.auditregistration.v1alpha1.AuditSinkSpec", + "description": "Spec defines the audit configuration spec" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "auditregistration.k8s.io", + "kind": "AuditSink", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.auditregistration.v1alpha1.AuditSinkList": { + "description": "AuditSinkList is a list of AuditSink items.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of audit configurations.", + "items": { + "$ref": "#/definitions/io.k8s.api.auditregistration.v1alpha1.AuditSink" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "AuditSinkList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "auditregistration.k8s.io", + "kind": "AuditSinkList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.auditregistration.v1alpha1.AuditSinkSpec": { + "description": "AuditSinkSpec holds the spec for the audit sink", + "properties": { + "policy": { + "$ref": "#/definitions/io.k8s.api.auditregistration.v1alpha1.Policy", + "description": "Policy defines the policy for selecting which events should be sent to the webhook required" + }, + "webhook": { + "$ref": "#/definitions/io.k8s.api.auditregistration.v1alpha1.Webhook", + "description": "Webhook to send events required" + } + }, + "required": [ + "policy", + "webhook" + ], + "type": "object" + }, + "io.k8s.api.auditregistration.v1alpha1.Policy": { + "description": "Policy defines the configuration of how audit events are logged", + "properties": { + "level": { + "description": "The Level that all requests are recorded at. available options: None, Metadata, Request, RequestResponse required", + "type": "string" + }, + "stages": { + "description": "Stages is a list of stages for which events are created.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "level" + ], + "type": "object" + }, + "io.k8s.api.auditregistration.v1alpha1.ServiceReference": { + "description": "ServiceReference holds a reference to Service.legacy.k8s.io", + "properties": { + "name": { + "description": "`name` is the name of the service. Required", + "type": "string" + }, + "namespace": { + "description": "`namespace` is the namespace of the service. Required", + "type": "string" + }, + "path": { + "description": "`path` is an optional URL path which will be sent in any request to this service.", + "type": "string" + } + }, + "required": [ + "namespace", + "name" + ], + "type": "object" + }, + "io.k8s.api.auditregistration.v1alpha1.Webhook": { + "description": "Webhook holds the configuration of the webhook", + "properties": { + "clientConfig": { + "$ref": "#/definitions/io.k8s.api.auditregistration.v1alpha1.WebhookClientConfig", + "description": "ClientConfig holds the connection parameters for the webhook required" + }, + "throttle": { + "$ref": "#/definitions/io.k8s.api.auditregistration.v1alpha1.WebhookThrottleConfig", + "description": "Throttle holds the options for throttling the webhook" + } + }, + "required": [ + "clientConfig" + ], + "type": "object" + }, + "io.k8s.api.auditregistration.v1alpha1.WebhookClientConfig": { + "description": "WebhookClientConfig contains the information to make a connection with the webhook", + "properties": { + "caBundle": { + "description": "`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. If unspecified, system trust roots on the apiserver are used.", + "format": "byte", + "type": "string" + }, + "service": { + "$ref": "#/definitions/io.k8s.api.auditregistration.v1alpha1.ServiceReference", + "description": "`service` is a reference to the service for this webhook. Either `service` or `url` must be specified.\n\nIf the webhook is running within the cluster, then you should use `service`.\n\nPort 443 will be used if it is open, otherwise it is an error." + }, + "url": { + "description": "`url` gives the location of the webhook, in standard URL form (`scheme://host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.auditregistration.v1alpha1.WebhookThrottleConfig": { + "description": "WebhookThrottleConfig holds the configuration for throttling events", + "properties": { + "burst": { + "description": "ThrottleBurst is the maximum number of events sent at the same moment default 15 QPS", + "format": "int64", + "type": "integer" + }, + "qps": { + "description": "ThrottleQPS maximum number of batches per second default 10 QPS", + "format": "int64", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1.TokenReview": { + "description": "TokenReview attempts to authenticate a token to a known user. Note: TokenReview requests may be cached by the webhook token authenticator plugin in the kube-apiserver.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "TokenReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.TokenReviewSpec", + "description": "Spec holds information about the request being evaluated" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.TokenReviewStatus", + "description": "Status is filled in by the server and indicates whether the request can be authenticated." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authentication.k8s.io", + "kind": "TokenReview", + "version": "v1" + } + ] + }, + "io.k8s.api.authentication.v1.TokenReviewSpec": { + "description": "TokenReviewSpec is a description of the token authentication request.", + "properties": { + "audiences": { + "description": "Audiences is a list of the identifiers that the resource server presented with the token identifies as. Audience-aware token authenticators will verify that the token was intended for at least one of the audiences in this list. If no audiences are provided, the audience will default to the audience of the Kubernetes apiserver.", + "items": { + "type": "string" + }, + "type": "array" + }, + "token": { + "description": "Token is the opaque bearer token.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1.TokenReviewStatus": { + "description": "TokenReviewStatus is the result of the token authentication request.", + "properties": { + "audiences": { + "description": "Audiences are audience identifiers chosen by the authenticator that are compatible with both the TokenReview and token. An identifier is any identifier in the intersection of the TokenReviewSpec audiences and the token's audiences. A client of the TokenReview API that sets the spec.audiences field should validate that a compatible audience identifier is returned in the status.audiences field to ensure that the TokenReview server is audience aware. If a TokenReview returns an empty status.audience field where status.authenticated is \"true\", the token is valid against the audience of the Kubernetes API server.", + "items": { + "type": "string" + }, + "type": "array" + }, + "authenticated": { + "description": "Authenticated indicates that the token was associated with a known user.", + "type": "boolean" + }, + "error": { + "description": "Error indicates that the token couldn't be checked", + "type": "string" + }, + "user": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.UserInfo", + "description": "User is the UserInfo associated with the provided token." + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1.UserInfo": { + "description": "UserInfo holds the information about the user needed to implement the user.Info interface.", + "properties": { + "extra": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": "Any additional information provided by the authenticator.", + "type": "object" + }, + "groups": { + "description": "The names of groups this user is a part of.", + "items": { + "type": "string" + }, + "type": "array" + }, + "uid": { + "description": "A unique value that identifies this user across time. If this user is deleted and another user by the same name is added, they will have different UIDs.", + "type": "string" + }, + "username": { + "description": "The name that uniquely identifies this user among all active users.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1beta1.TokenReview": { + "description": "TokenReview attempts to authenticate a token to a known user. Note: TokenReview requests may be cached by the webhook token authenticator plugin in the kube-apiserver.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "TokenReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authentication.v1beta1.TokenReviewSpec", + "description": "Spec holds information about the request being evaluated" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authentication.v1beta1.TokenReviewStatus", + "description": "Status is filled in by the server and indicates whether the request can be authenticated." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authentication.k8s.io", + "kind": "TokenReview", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.authentication.v1beta1.TokenReviewSpec": { + "description": "TokenReviewSpec is a description of the token authentication request.", + "properties": { + "audiences": { + "description": "Audiences is a list of the identifiers that the resource server presented with the token identifies as. Audience-aware token authenticators will verify that the token was intended for at least one of the audiences in this list. If no audiences are provided, the audience will default to the audience of the Kubernetes apiserver.", + "items": { + "type": "string" + }, + "type": "array" + }, + "token": { + "description": "Token is the opaque bearer token.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1beta1.TokenReviewStatus": { + "description": "TokenReviewStatus is the result of the token authentication request.", + "properties": { + "audiences": { + "description": "Audiences are audience identifiers chosen by the authenticator that are compatible with both the TokenReview and token. An identifier is any identifier in the intersection of the TokenReviewSpec audiences and the token's audiences. A client of the TokenReview API that sets the spec.audiences field should validate that a compatible audience identifier is returned in the status.audiences field to ensure that the TokenReview server is audience aware. If a TokenReview returns an empty status.audience field where status.authenticated is \"true\", the token is valid against the audience of the Kubernetes API server.", + "items": { + "type": "string" + }, + "type": "array" + }, + "authenticated": { + "description": "Authenticated indicates that the token was associated with a known user.", + "type": "boolean" + }, + "error": { + "description": "Error indicates that the token couldn't be checked", + "type": "string" + }, + "user": { + "$ref": "#/definitions/io.k8s.api.authentication.v1beta1.UserInfo", + "description": "User is the UserInfo associated with the provided token." + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1beta1.UserInfo": { + "description": "UserInfo holds the information about the user needed to implement the user.Info interface.", + "properties": { + "extra": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": "Any additional information provided by the authenticator.", + "type": "object" + }, + "groups": { + "description": "The names of groups this user is a part of.", + "items": { + "type": "string" + }, + "type": "array" + }, + "uid": { + "description": "A unique value that identifies this user across time. If this user is deleted and another user by the same name is added, they will have different UIDs.", + "type": "string" + }, + "username": { + "description": "The name that uniquely identifies this user among all active users.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1.LocalSubjectAccessReview": { + "description": "LocalSubjectAccessReview checks whether or not a user or group can perform an action in a given namespace. Having a namespace scoped resource makes it much easier to grant namespace scoped policy that includes permissions checking.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "LocalSubjectAccessReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SubjectAccessReviewSpec", + "description": "Spec holds information about the request being evaluated. spec.namespace must be equal to the namespace you made the request against. If empty, it is defaulted." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SubjectAccessReviewStatus", + "description": "Status is filled in by the server and indicates whether the request is allowed or not" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authorization.k8s.io", + "kind": "LocalSubjectAccessReview", + "version": "v1" + } + ] + }, + "io.k8s.api.authorization.v1.NonResourceAttributes": { + "description": "NonResourceAttributes includes the authorization attributes available for non-resource requests to the Authorizer interface", + "properties": { + "path": { + "description": "Path is the URL path of the request", + "type": "string" + }, + "verb": { + "description": "Verb is the standard HTTP verb", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1.NonResourceRule": { + "description": "NonResourceRule holds information that describes a rule for the non-resource", + "properties": { + "nonResourceURLs": { + "description": "NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + }, + "verbs": { + "description": "Verb is a list of kubernetes non-resource API verbs, like: get, post, put, delete, patch, head, options. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "verbs" + ], + "type": "object" + }, + "io.k8s.api.authorization.v1.ResourceAttributes": { + "description": "ResourceAttributes includes the authorization attributes available for resource requests to the Authorizer interface", + "properties": { + "group": { + "description": "Group is the API Group of the Resource. \"*\" means all.", + "type": "string" + }, + "name": { + "description": "Name is the name of the resource being requested for a \"get\" or deleted for a \"delete\". \"\" (empty) means all.", + "type": "string" + }, + "namespace": { + "description": "Namespace is the namespace of the action being requested. Currently, there is no distinction between no namespace and all namespaces \"\" (empty) is defaulted for LocalSubjectAccessReviews \"\" (empty) is empty for cluster-scoped resources \"\" (empty) means \"all\" for namespace scoped resources from a SubjectAccessReview or SelfSubjectAccessReview", + "type": "string" + }, + "resource": { + "description": "Resource is one of the existing resource types. \"*\" means all.", + "type": "string" + }, + "subresource": { + "description": "Subresource is one of the existing resource types. \"\" means none.", + "type": "string" + }, + "verb": { + "description": "Verb is a kubernetes resource API verb, like: get, list, watch, create, update, delete, proxy. \"*\" means all.", + "type": "string" + }, + "version": { + "description": "Version is the API Version of the Resource. \"*\" means all.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1.ResourceRule": { + "description": "ResourceRule is the list of actions the subject is allowed to perform on resources. The list ordering isn't significant, may contain duplicates, and possibly be incomplete.", + "properties": { + "apiGroups": { + "description": "APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of the enumerated resources in any API group will be allowed. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resourceNames": { + "description": "ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resources": { + "description": "Resources is a list of resources this rule applies to. \"*\" means all in the specified apiGroups.\n \"*/foo\" represents the subresource 'foo' for all resources in the specified apiGroups.", + "items": { + "type": "string" + }, + "type": "array" + }, + "verbs": { + "description": "Verb is a list of kubernetes resource API verbs, like: get, list, watch, create, update, delete, proxy. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "verbs" + ], + "type": "object" + }, + "io.k8s.api.authorization.v1.SelfSubjectAccessReview": { + "description": "SelfSubjectAccessReview checks whether or the current user can perform an action. Not filling in a spec.namespace means \"in all namespaces\". Self is a special case, because users should always be able to check whether they can perform an action", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SelfSubjectAccessReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SelfSubjectAccessReviewSpec", + "description": "Spec holds information about the request being evaluated. user and groups must be empty" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SubjectAccessReviewStatus", + "description": "Status is filled in by the server and indicates whether the request is allowed or not" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authorization.k8s.io", + "kind": "SelfSubjectAccessReview", + "version": "v1" + } + ] + }, + "io.k8s.api.authorization.v1.SelfSubjectAccessReviewSpec": { + "description": "SelfSubjectAccessReviewSpec is a description of the access request. Exactly one of ResourceAuthorizationAttributes and NonResourceAuthorizationAttributes must be set", + "properties": { + "nonResourceAttributes": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.NonResourceAttributes", + "description": "NonResourceAttributes describes information for a non-resource access request" + }, + "resourceAttributes": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.ResourceAttributes", + "description": "ResourceAuthorizationAttributes describes information for a resource access request" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1.SelfSubjectRulesReview": { + "description": "SelfSubjectRulesReview enumerates the set of actions the current user can perform within a namespace. The returned list of actions may be incomplete depending on the server's authorization mode, and any errors experienced during the evaluation. SelfSubjectRulesReview should be used by UIs to show/hide actions, or to quickly let an end user reason about their permissions. It should NOT Be used by external systems to drive authorization decisions as this raises confused deputy, cache lifetime/revocation, and correctness concerns. SubjectAccessReview, and LocalAccessReview are the correct way to defer authorization decisions to the API server.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SelfSubjectRulesReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SelfSubjectRulesReviewSpec", + "description": "Spec holds information about the request being evaluated." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SubjectRulesReviewStatus", + "description": "Status is filled in by the server and indicates the set of actions a user can perform." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authorization.k8s.io", + "kind": "SelfSubjectRulesReview", + "version": "v1" + } + ] + }, + "io.k8s.api.authorization.v1.SelfSubjectRulesReviewSpec": { + "properties": { + "namespace": { + "description": "Namespace to evaluate rules for. Required.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1.SubjectAccessReview": { + "description": "SubjectAccessReview checks whether or not a user or group can perform an action.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SubjectAccessReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SubjectAccessReviewSpec", + "description": "Spec holds information about the request being evaluated" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SubjectAccessReviewStatus", + "description": "Status is filled in by the server and indicates whether the request is allowed or not" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authorization.k8s.io", + "kind": "SubjectAccessReview", + "version": "v1" + } + ] + }, + "io.k8s.api.authorization.v1.SubjectAccessReviewSpec": { + "description": "SubjectAccessReviewSpec is a description of the access request. Exactly one of ResourceAuthorizationAttributes and NonResourceAuthorizationAttributes must be set", + "properties": { + "extra": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": "Extra corresponds to the user.Info.GetExtra() method from the authenticator. Since that is input to the authorizer it needs a reflection here.", + "type": "object" + }, + "groups": { + "description": "Groups is the groups you're testing for.", + "items": { + "type": "string" + }, + "type": "array" + }, + "nonResourceAttributes": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.NonResourceAttributes", + "description": "NonResourceAttributes describes information for a non-resource access request" + }, + "resourceAttributes": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.ResourceAttributes", + "description": "ResourceAuthorizationAttributes describes information for a resource access request" + }, + "uid": { + "description": "UID information about the requesting user.", + "type": "string" + }, + "user": { + "description": "User is the user you're testing for. If you specify \"User\" but not \"Groups\", then is it interpreted as \"What if User were not a member of any groups", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1.SubjectAccessReviewStatus": { + "description": "SubjectAccessReviewStatus", + "properties": { + "allowed": { + "description": "Allowed is required. True if the action would be allowed, false otherwise.", + "type": "boolean" + }, + "denied": { + "description": "Denied is optional. True if the action would be denied, otherwise false. If both allowed is false and denied is false, then the authorizer has no opinion on whether to authorize the action. Denied may not be true if Allowed is true.", + "type": "boolean" + }, + "evaluationError": { + "description": "EvaluationError is an indication that some error occurred during the authorization check. It is entirely possible to get an error and be able to continue determine authorization status in spite of it. For instance, RBAC can be missing a role, but enough roles are still present and bound to reason about the request.", + "type": "string" + }, + "reason": { + "description": "Reason is optional. It indicates why a request was allowed or denied.", + "type": "string" + } + }, + "required": [ + "allowed" + ], + "type": "object" + }, + "io.k8s.api.authorization.v1.SubjectRulesReviewStatus": { + "description": "SubjectRulesReviewStatus contains the result of a rules check. This check can be incomplete depending on the set of authorizers the server is configured with and any errors experienced during evaluation. Because authorization rules are additive, if a rule appears in a list it's safe to assume the subject has that permission, even if that list is incomplete.", + "properties": { + "evaluationError": { + "description": "EvaluationError can appear in combination with Rules. It indicates an error occurred during rule evaluation, such as an authorizer that doesn't support rule evaluation, and that ResourceRules and/or NonResourceRules may be incomplete.", + "type": "string" + }, + "incomplete": { + "description": "Incomplete is true when the rules returned by this call are incomplete. This is most commonly encountered when an authorizer, such as an external authorizer, doesn't support rules evaluation.", + "type": "boolean" + }, + "nonResourceRules": { + "description": "NonResourceRules is the list of actions the subject is allowed to perform on non-resources. The list ordering isn't significant, may contain duplicates, and possibly be incomplete.", + "items": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.NonResourceRule" + }, + "type": "array" + }, + "resourceRules": { + "description": "ResourceRules is the list of actions the subject is allowed to perform on resources. The list ordering isn't significant, may contain duplicates, and possibly be incomplete.", + "items": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.ResourceRule" + }, + "type": "array" + } + }, + "required": [ + "resourceRules", + "nonResourceRules", + "incomplete" + ], + "type": "object" + }, + "io.k8s.api.authorization.v1beta1.LocalSubjectAccessReview": { + "description": "LocalSubjectAccessReview checks whether or not a user or group can perform an action in a given namespace. Having a namespace scoped resource makes it much easier to grant namespace scoped policy that includes permissions checking.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "LocalSubjectAccessReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.SubjectAccessReviewSpec", + "description": "Spec holds information about the request being evaluated. spec.namespace must be equal to the namespace you made the request against. If empty, it is defaulted." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.SubjectAccessReviewStatus", + "description": "Status is filled in by the server and indicates whether the request is allowed or not" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authorization.k8s.io", + "kind": "LocalSubjectAccessReview", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.authorization.v1beta1.NonResourceAttributes": { + "description": "NonResourceAttributes includes the authorization attributes available for non-resource requests to the Authorizer interface", + "properties": { + "path": { + "description": "Path is the URL path of the request", + "type": "string" + }, + "verb": { + "description": "Verb is the standard HTTP verb", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1beta1.NonResourceRule": { + "description": "NonResourceRule holds information that describes a rule for the non-resource", + "properties": { + "nonResourceURLs": { + "description": "NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + }, + "verbs": { + "description": "Verb is a list of kubernetes non-resource API verbs, like: get, post, put, delete, patch, head, options. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "verbs" + ], + "type": "object" + }, + "io.k8s.api.authorization.v1beta1.ResourceAttributes": { + "description": "ResourceAttributes includes the authorization attributes available for resource requests to the Authorizer interface", + "properties": { + "group": { + "description": "Group is the API Group of the Resource. \"*\" means all.", + "type": "string" + }, + "name": { + "description": "Name is the name of the resource being requested for a \"get\" or deleted for a \"delete\". \"\" (empty) means all.", + "type": "string" + }, + "namespace": { + "description": "Namespace is the namespace of the action being requested. Currently, there is no distinction between no namespace and all namespaces \"\" (empty) is defaulted for LocalSubjectAccessReviews \"\" (empty) is empty for cluster-scoped resources \"\" (empty) means \"all\" for namespace scoped resources from a SubjectAccessReview or SelfSubjectAccessReview", + "type": "string" + }, + "resource": { + "description": "Resource is one of the existing resource types. \"*\" means all.", + "type": "string" + }, + "subresource": { + "description": "Subresource is one of the existing resource types. \"\" means none.", + "type": "string" + }, + "verb": { + "description": "Verb is a kubernetes resource API verb, like: get, list, watch, create, update, delete, proxy. \"*\" means all.", + "type": "string" + }, + "version": { + "description": "Version is the API Version of the Resource. \"*\" means all.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1beta1.ResourceRule": { + "description": "ResourceRule is the list of actions the subject is allowed to perform on resources. The list ordering isn't significant, may contain duplicates, and possibly be incomplete.", + "properties": { + "apiGroups": { + "description": "APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of the enumerated resources in any API group will be allowed. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resourceNames": { + "description": "ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resources": { + "description": "Resources is a list of resources this rule applies to. \"*\" means all in the specified apiGroups.\n \"*/foo\" represents the subresource 'foo' for all resources in the specified apiGroups.", + "items": { + "type": "string" + }, + "type": "array" + }, + "verbs": { + "description": "Verb is a list of kubernetes resource API verbs, like: get, list, watch, create, update, delete, proxy. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "verbs" + ], + "type": "object" + }, + "io.k8s.api.authorization.v1beta1.SelfSubjectAccessReview": { + "description": "SelfSubjectAccessReview checks whether or the current user can perform an action. Not filling in a spec.namespace means \"in all namespaces\". Self is a special case, because users should always be able to check whether they can perform an action", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SelfSubjectAccessReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.SelfSubjectAccessReviewSpec", + "description": "Spec holds information about the request being evaluated. user and groups must be empty" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.SubjectAccessReviewStatus", + "description": "Status is filled in by the server and indicates whether the request is allowed or not" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authorization.k8s.io", + "kind": "SelfSubjectAccessReview", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.authorization.v1beta1.SelfSubjectAccessReviewSpec": { + "description": "SelfSubjectAccessReviewSpec is a description of the access request. Exactly one of ResourceAuthorizationAttributes and NonResourceAuthorizationAttributes must be set", + "properties": { + "nonResourceAttributes": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.NonResourceAttributes", + "description": "NonResourceAttributes describes information for a non-resource access request" + }, + "resourceAttributes": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.ResourceAttributes", + "description": "ResourceAuthorizationAttributes describes information for a resource access request" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1beta1.SelfSubjectRulesReview": { + "description": "SelfSubjectRulesReview enumerates the set of actions the current user can perform within a namespace. The returned list of actions may be incomplete depending on the server's authorization mode, and any errors experienced during the evaluation. SelfSubjectRulesReview should be used by UIs to show/hide actions, or to quickly let an end user reason about their permissions. It should NOT Be used by external systems to drive authorization decisions as this raises confused deputy, cache lifetime/revocation, and correctness concerns. SubjectAccessReview, and LocalAccessReview are the correct way to defer authorization decisions to the API server.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SelfSubjectRulesReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.SelfSubjectRulesReviewSpec", + "description": "Spec holds information about the request being evaluated." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.SubjectRulesReviewStatus", + "description": "Status is filled in by the server and indicates the set of actions a user can perform." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authorization.k8s.io", + "kind": "SelfSubjectRulesReview", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.authorization.v1beta1.SelfSubjectRulesReviewSpec": { + "properties": { + "namespace": { + "description": "Namespace to evaluate rules for. Required.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1beta1.SubjectAccessReview": { + "description": "SubjectAccessReview checks whether or not a user or group can perform an action.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SubjectAccessReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.SubjectAccessReviewSpec", + "description": "Spec holds information about the request being evaluated" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.SubjectAccessReviewStatus", + "description": "Status is filled in by the server and indicates whether the request is allowed or not" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authorization.k8s.io", + "kind": "SubjectAccessReview", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.authorization.v1beta1.SubjectAccessReviewSpec": { + "description": "SubjectAccessReviewSpec is a description of the access request. Exactly one of ResourceAuthorizationAttributes and NonResourceAuthorizationAttributes must be set", + "properties": { + "extra": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": "Extra corresponds to the user.Info.GetExtra() method from the authenticator. Since that is input to the authorizer it needs a reflection here.", + "type": "object" + }, + "group": { + "description": "Groups is the groups you're testing for.", + "items": { + "type": "string" + }, + "type": "array" + }, + "nonResourceAttributes": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.NonResourceAttributes", + "description": "NonResourceAttributes describes information for a non-resource access request" + }, + "resourceAttributes": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.ResourceAttributes", + "description": "ResourceAuthorizationAttributes describes information for a resource access request" + }, + "uid": { + "description": "UID information about the requesting user.", + "type": "string" + }, + "user": { + "description": "User is the user you're testing for. If you specify \"User\" but not \"Group\", then is it interpreted as \"What if User were not a member of any groups", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1beta1.SubjectAccessReviewStatus": { + "description": "SubjectAccessReviewStatus", + "properties": { + "allowed": { + "description": "Allowed is required. True if the action would be allowed, false otherwise.", + "type": "boolean" + }, + "denied": { + "description": "Denied is optional. True if the action would be denied, otherwise false. If both allowed is false and denied is false, then the authorizer has no opinion on whether to authorize the action. Denied may not be true if Allowed is true.", + "type": "boolean" + }, + "evaluationError": { + "description": "EvaluationError is an indication that some error occurred during the authorization check. It is entirely possible to get an error and be able to continue determine authorization status in spite of it. For instance, RBAC can be missing a role, but enough roles are still present and bound to reason about the request.", + "type": "string" + }, + "reason": { + "description": "Reason is optional. It indicates why a request was allowed or denied.", + "type": "string" + } + }, + "required": [ + "allowed" + ], + "type": "object" + }, + "io.k8s.api.authorization.v1beta1.SubjectRulesReviewStatus": { + "description": "SubjectRulesReviewStatus contains the result of a rules check. This check can be incomplete depending on the set of authorizers the server is configured with and any errors experienced during evaluation. Because authorization rules are additive, if a rule appears in a list it's safe to assume the subject has that permission, even if that list is incomplete.", + "properties": { + "evaluationError": { + "description": "EvaluationError can appear in combination with Rules. It indicates an error occurred during rule evaluation, such as an authorizer that doesn't support rule evaluation, and that ResourceRules and/or NonResourceRules may be incomplete.", + "type": "string" + }, + "incomplete": { + "description": "Incomplete is true when the rules returned by this call are incomplete. This is most commonly encountered when an authorizer, such as an external authorizer, doesn't support rules evaluation.", + "type": "boolean" + }, + "nonResourceRules": { + "description": "NonResourceRules is the list of actions the subject is allowed to perform on non-resources. The list ordering isn't significant, may contain duplicates, and possibly be incomplete.", + "items": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.NonResourceRule" + }, + "type": "array" + }, + "resourceRules": { + "description": "ResourceRules is the list of actions the subject is allowed to perform on resources. The list ordering isn't significant, may contain duplicates, and possibly be incomplete.", + "items": { + "$ref": "#/definitions/io.k8s.api.authorization.v1beta1.ResourceRule" + }, + "type": "array" + } + }, + "required": [ + "resourceRules", + "nonResourceRules", + "incomplete" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v1.CrossVersionObjectReference": { + "description": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", + "properties": { + "apiVersion": { + "description": "API version of the referent", + "type": "string" + }, + "kind": { + "description": "Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\"", + "type": "string" + }, + "name": { + "description": "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v1.HorizontalPodAutoscaler": { + "description": "configuration of a horizontal pod autoscaler.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "HorizontalPodAutoscaler" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerSpec", + "description": "behaviour of autoscaler. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerStatus", + "description": "current information about the autoscaler." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "autoscaling", + "kind": "HorizontalPodAutoscaler", + "version": "v1" + } + ] + }, + "io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerList": { + "description": "list of horizontal pod autoscaler objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "list of horizontal pod autoscaler objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v1.HorizontalPodAutoscaler" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "HorizontalPodAutoscalerList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "autoscaling", + "kind": "HorizontalPodAutoscalerList", + "version": "v1" + } + ] + }, + "io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerSpec": { + "description": "specification of a horizontal pod autoscaler.", + "properties": { + "maxReplicas": { + "description": "upper limit for the number of pods that can be set by the autoscaler; cannot be smaller than MinReplicas.", + "format": "int32", + "type": "integer" + }, + "minReplicas": { + "description": "lower limit for the number of pods that can be set by the autoscaler, default 1.", + "format": "int32", + "type": "integer" + }, + "scaleTargetRef": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v1.CrossVersionObjectReference", + "description": "reference to scaled resource; horizontal pod autoscaler will learn the current resource consumption and will set the desired number of pods by using its Scale subresource." + }, + "targetCPUUtilizationPercentage": { + "description": "target average CPU utilization (represented as a percentage of requested CPU) over all the pods; if not specified the default autoscaling policy will be used.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "scaleTargetRef", + "maxReplicas" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerStatus": { + "description": "current status of a horizontal pod autoscaler", + "properties": { + "currentCPUUtilizationPercentage": { + "description": "current average CPU utilization over all pods, represented as a percentage of requested CPU, e.g. 70 means that an average pod is using now 70% of its requested CPU.", + "format": "int32", + "type": "integer" + }, + "currentReplicas": { + "description": "current number of replicas of pods managed by this autoscaler.", + "format": "int32", + "type": "integer" + }, + "desiredReplicas": { + "description": "desired number of replicas of pods managed by this autoscaler.", + "format": "int32", + "type": "integer" + }, + "lastScaleTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "last time the HorizontalPodAutoscaler scaled the number of pods; used by the autoscaler to control how often the number of pods is changed." + }, + "observedGeneration": { + "description": "most recent generation observed by this autoscaler.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "currentReplicas", + "desiredReplicas" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v1.Scale": { + "description": "Scale represents a scaling request for a resource.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Scale" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v1.ScaleSpec", + "description": "defines the behavior of the scale. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v1.ScaleStatus", + "description": "current status of the scale. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status. Read-only." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "autoscaling", + "kind": "Scale", + "version": "v1" + } + ] + }, + "io.k8s.api.autoscaling.v1.ScaleSpec": { + "description": "ScaleSpec describes the attributes of a scale subresource.", + "properties": { + "replicas": { + "description": "desired number of instances for the scaled object.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.autoscaling.v1.ScaleStatus": { + "description": "ScaleStatus represents the current status of a scale subresource.", + "properties": { + "replicas": { + "description": "actual number of observed instances of the scaled object.", + "format": "int32", + "type": "integer" + }, + "selector": { + "description": "label query over pods that should match the replicas count. This is same as the label selector but in the string format to avoid introspection by clients. The string will be in the same format as the query-param syntax. More info about label selectors: http://kubernetes.io/docs/user-guide/labels#label-selectors", + "type": "string" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference": { + "description": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", + "properties": { + "apiVersion": { + "description": "API version of the referent", + "type": "string" + }, + "kind": { + "description": "Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\"", + "type": "string" + }, + "name": { + "description": "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.ExternalMetricSource": { + "description": "ExternalMetricSource indicates how to scale on a metric not associated with any Kubernetes object (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster). Exactly one \"target\" type should be set.", + "properties": { + "metricName": { + "description": "metricName is the name of the metric in question.", + "type": "string" + }, + "metricSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "metricSelector is used to identify a specific time series within a given metric." + }, + "targetAverageValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "targetAverageValue is the target per-pod value of global metric (as a quantity). Mutually exclusive with TargetValue." + }, + "targetValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "targetValue is the target value of the metric (as a quantity). Mutually exclusive with TargetAverageValue." + } + }, + "required": [ + "metricName" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.ExternalMetricStatus": { + "description": "ExternalMetricStatus indicates the current value of a global metric not associated with any Kubernetes object.", + "properties": { + "currentAverageValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "currentAverageValue is the current value of metric averaged over autoscaled pods." + }, + "currentValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "currentValue is the current value of the metric (as a quantity)" + }, + "metricName": { + "description": "metricName is the name of a metric used for autoscaling in metric system.", + "type": "string" + }, + "metricSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "metricSelector is used to identify a specific time series within a given metric." + } + }, + "required": [ + "metricName", + "currentValue" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscaler": { + "description": "HorizontalPodAutoscaler is the configuration for a horizontal pod autoscaler, which automatically manages the replica count of any resource implementing the scale subresource based on the metrics specified.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "HorizontalPodAutoscaler" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "metadata is the standard object metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerSpec", + "description": "spec is the specification for the behaviour of the autoscaler. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerStatus", + "description": "status is the current information about the autoscaler." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "autoscaling", + "kind": "HorizontalPodAutoscaler", + "version": "v2beta1" + } + ] + }, + "io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerCondition": { + "description": "HorizontalPodAutoscalerCondition describes the state of a HorizontalPodAutoscaler at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastTransitionTime is the last time the condition transitioned from one status to another" + }, + "message": { + "description": "message is a human-readable explanation containing details about the transition", + "type": "string" + }, + "reason": { + "description": "reason is the reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "status is the status of the condition (True, False, Unknown)", + "type": "string" + }, + "type": { + "description": "type describes the current condition", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerList": { + "description": "HorizontalPodAutoscaler is a list of horizontal pod autoscaler objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of horizontal pod autoscaler objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscaler" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "HorizontalPodAutoscalerList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "metadata is the standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "autoscaling", + "kind": "HorizontalPodAutoscalerList", + "version": "v2beta1" + } + ] + }, + "io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerSpec": { + "description": "HorizontalPodAutoscalerSpec describes the desired functionality of the HorizontalPodAutoscaler.", + "properties": { + "maxReplicas": { + "description": "maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. It cannot be less that minReplicas.", + "format": "int32", + "type": "integer" + }, + "metrics": { + "description": "metrics contains the specifications for which to use to calculate the desired replica count (the maximum replica count across all metrics will be used). The desired replica count is calculated multiplying the ratio between the target value and the current value by the current number of pods. Ergo, metrics used must decrease as the pod count is increased, and vice-versa. See the individual metric source types for more information about how each type of metric must respond.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.MetricSpec" + }, + "type": "array" + }, + "minReplicas": { + "description": "minReplicas is the lower limit for the number of replicas to which the autoscaler can scale down. It defaults to 1 pod.", + "format": "int32", + "type": "integer" + }, + "scaleTargetRef": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference", + "description": "scaleTargetRef points to the target resource to scale, and is used to the pods for which metrics should be collected, as well as to actually change the replica count." + } + }, + "required": [ + "scaleTargetRef", + "maxReplicas" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerStatus": { + "description": "HorizontalPodAutoscalerStatus describes the current status of a horizontal pod autoscaler.", + "properties": { + "conditions": { + "description": "conditions is the set of conditions required for this autoscaler to scale its target, and indicates whether or not those conditions are met.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerCondition" + }, + "type": "array" + }, + "currentMetrics": { + "description": "currentMetrics is the last read state of the metrics used by this autoscaler.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.MetricStatus" + }, + "type": "array" + }, + "currentReplicas": { + "description": "currentReplicas is current number of replicas of pods managed by this autoscaler, as last seen by the autoscaler.", + "format": "int32", + "type": "integer" + }, + "desiredReplicas": { + "description": "desiredReplicas is the desired number of replicas of pods managed by this autoscaler, as last calculated by the autoscaler.", + "format": "int32", + "type": "integer" + }, + "lastScaleTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastScaleTime is the last time the HorizontalPodAutoscaler scaled the number of pods, used by the autoscaler to control how often the number of pods is changed." + }, + "observedGeneration": { + "description": "observedGeneration is the most recent generation observed by this autoscaler.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "currentReplicas", + "desiredReplicas", + "conditions" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.MetricSpec": { + "description": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).", + "properties": { + "external": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.ExternalMetricSource", + "description": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster)." + }, + "object": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.ObjectMetricSource", + "description": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object)." + }, + "pods": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.PodsMetricSource", + "description": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value." + }, + "resource": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.ResourceMetricSource", + "description": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source." + }, + "type": { + "description": "type is the type of metric source. It should be one of \"Object\", \"Pods\" or \"Resource\", each mapping to a matching field in the object.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.MetricStatus": { + "description": "MetricStatus describes the last-read state of a single metric.", + "properties": { + "external": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.ExternalMetricStatus", + "description": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster)." + }, + "object": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.ObjectMetricStatus", + "description": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object)." + }, + "pods": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.PodsMetricStatus", + "description": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value." + }, + "resource": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.ResourceMetricStatus", + "description": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source." + }, + "type": { + "description": "type is the type of metric source. It will be one of \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.ObjectMetricSource": { + "description": "ObjectMetricSource indicates how to scale on a metric describing a kubernetes object (for example, hits-per-second on an Ingress object).", + "properties": { + "averageValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "averageValue is the target value of the average of the metric across all relevant pods (as a quantity)" + }, + "metricName": { + "description": "metricName is the name of the metric in question.", + "type": "string" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector is the string-encoded form of a standard kubernetes label selector for the given metric When set, it is passed as an additional parameter to the metrics server for more specific metrics scoping When unset, just the metricName will be used to gather metrics." + }, + "target": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference", + "description": "target is the described Kubernetes object." + }, + "targetValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "targetValue is the target value of the metric (as a quantity)." + } + }, + "required": [ + "target", + "metricName", + "targetValue" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.ObjectMetricStatus": { + "description": "ObjectMetricStatus indicates the current value of a metric describing a kubernetes object (for example, hits-per-second on an Ingress object).", + "properties": { + "averageValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "averageValue is the current value of the average of the metric across all relevant pods (as a quantity)" + }, + "currentValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "currentValue is the current value of the metric (as a quantity)." + }, + "metricName": { + "description": "metricName is the name of the metric in question.", + "type": "string" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector is the string-encoded form of a standard kubernetes label selector for the given metric When set in the ObjectMetricSource, it is passed as an additional parameter to the metrics server for more specific metrics scoping. When unset, just the metricName will be used to gather metrics." + }, + "target": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference", + "description": "target is the described Kubernetes object." + } + }, + "required": [ + "target", + "metricName", + "currentValue" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.PodsMetricSource": { + "description": "PodsMetricSource indicates how to scale on a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.", + "properties": { + "metricName": { + "description": "metricName is the name of the metric in question", + "type": "string" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector is the string-encoded form of a standard kubernetes label selector for the given metric When set, it is passed as an additional parameter to the metrics server for more specific metrics scoping When unset, just the metricName will be used to gather metrics." + }, + "targetAverageValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "targetAverageValue is the target value of the average of the metric across all relevant pods (as a quantity)" + } + }, + "required": [ + "metricName", + "targetAverageValue" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.PodsMetricStatus": { + "description": "PodsMetricStatus indicates the current value of a metric describing each pod in the current scale target (for example, transactions-processed-per-second).", + "properties": { + "currentAverageValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "currentAverageValue is the current value of the average of the metric across all relevant pods (as a quantity)" + }, + "metricName": { + "description": "metricName is the name of the metric in question", + "type": "string" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector is the string-encoded form of a standard kubernetes label selector for the given metric When set in the PodsMetricSource, it is passed as an additional parameter to the metrics server for more specific metrics scoping. When unset, just the metricName will be used to gather metrics." + } + }, + "required": [ + "metricName", + "currentAverageValue" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.ResourceMetricSource": { + "description": "ResourceMetricSource indicates how to scale on a resource metric known to Kubernetes, as specified in requests and limits, describing each pod in the current scale target (e.g. CPU or memory). The values will be averaged together before being compared to the target. Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. Only one \"target\" type should be set.", + "properties": { + "name": { + "description": "name is the name of the resource in question.", + "type": "string" + }, + "targetAverageUtilization": { + "description": "targetAverageUtilization is the target value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods.", + "format": "int32", + "type": "integer" + }, + "targetAverageValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "targetAverageValue is the target value of the average of the resource metric across all relevant pods, as a raw value (instead of as a percentage of the request), similar to the \"pods\" metric source type." + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta1.ResourceMetricStatus": { + "description": "ResourceMetricStatus indicates the current value of a resource metric known to Kubernetes, as specified in requests and limits, describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.", + "properties": { + "currentAverageUtilization": { + "description": "currentAverageUtilization is the current value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods. It will only be present if `targetAverageValue` was set in the corresponding metric specification.", + "format": "int32", + "type": "integer" + }, + "currentAverageValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "currentAverageValue is the current value of the average of the resource metric across all relevant pods, as a raw value (instead of as a percentage of the request), similar to the \"pods\" metric source type. It will always be set, regardless of the corresponding metric specification." + }, + "name": { + "description": "name is the name of the resource in question.", + "type": "string" + } + }, + "required": [ + "name", + "currentAverageValue" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference": { + "description": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", + "properties": { + "apiVersion": { + "description": "API version of the referent", + "type": "string" + }, + "kind": { + "description": "Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds\"", + "type": "string" + }, + "name": { + "description": "Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.ExternalMetricSource": { + "description": "ExternalMetricSource indicates how to scale on a metric not associated with any Kubernetes object (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).", + "properties": { + "metric": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricIdentifier", + "description": "metric identifies the target metric by name and selector" + }, + "target": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricTarget", + "description": "target specifies the target value for the given metric" + } + }, + "required": [ + "metric", + "target" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.ExternalMetricStatus": { + "description": "ExternalMetricStatus indicates the current value of a global metric not associated with any Kubernetes object.", + "properties": { + "current": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricValueStatus", + "description": "current contains the current value for the given metric" + }, + "metric": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricIdentifier", + "description": "metric identifies the target metric by name and selector" + } + }, + "required": [ + "metric", + "current" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscaler": { + "description": "HorizontalPodAutoscaler is the configuration for a horizontal pod autoscaler, which automatically manages the replica count of any resource implementing the scale subresource based on the metrics specified.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "HorizontalPodAutoscaler" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "metadata is the standard object metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerSpec", + "description": "spec is the specification for the behaviour of the autoscaler. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerStatus", + "description": "status is the current information about the autoscaler." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "autoscaling", + "kind": "HorizontalPodAutoscaler", + "version": "v2beta2" + } + ] + }, + "io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerCondition": { + "description": "HorizontalPodAutoscalerCondition describes the state of a HorizontalPodAutoscaler at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastTransitionTime is the last time the condition transitioned from one status to another" + }, + "message": { + "description": "message is a human-readable explanation containing details about the transition", + "type": "string" + }, + "reason": { + "description": "reason is the reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "status is the status of the condition (True, False, Unknown)", + "type": "string" + }, + "type": { + "description": "type describes the current condition", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerList": { + "description": "HorizontalPodAutoscalerList is a list of horizontal pod autoscaler objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of horizontal pod autoscaler objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscaler" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "HorizontalPodAutoscalerList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "metadata is the standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "autoscaling", + "kind": "HorizontalPodAutoscalerList", + "version": "v2beta2" + } + ] + }, + "io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerSpec": { + "description": "HorizontalPodAutoscalerSpec describes the desired functionality of the HorizontalPodAutoscaler.", + "properties": { + "maxReplicas": { + "description": "maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. It cannot be less that minReplicas.", + "format": "int32", + "type": "integer" + }, + "metrics": { + "description": "metrics contains the specifications for which to use to calculate the desired replica count (the maximum replica count across all metrics will be used). The desired replica count is calculated multiplying the ratio between the target value and the current value by the current number of pods. Ergo, metrics used must decrease as the pod count is increased, and vice-versa. See the individual metric source types for more information about how each type of metric must respond. If not set, the default metric will be set to 80% average CPU utilization.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricSpec" + }, + "type": "array" + }, + "minReplicas": { + "description": "minReplicas is the lower limit for the number of replicas to which the autoscaler can scale down. It defaults to 1 pod.", + "format": "int32", + "type": "integer" + }, + "scaleTargetRef": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference", + "description": "scaleTargetRef points to the target resource to scale, and is used to the pods for which metrics should be collected, as well as to actually change the replica count." + } + }, + "required": [ + "scaleTargetRef", + "maxReplicas" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerStatus": { + "description": "HorizontalPodAutoscalerStatus describes the current status of a horizontal pod autoscaler.", + "properties": { + "conditions": { + "description": "conditions is the set of conditions required for this autoscaler to scale its target, and indicates whether or not those conditions are met.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerCondition" + }, + "type": "array" + }, + "currentMetrics": { + "description": "currentMetrics is the last read state of the metrics used by this autoscaler.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricStatus" + }, + "type": "array" + }, + "currentReplicas": { + "description": "currentReplicas is current number of replicas of pods managed by this autoscaler, as last seen by the autoscaler.", + "format": "int32", + "type": "integer" + }, + "desiredReplicas": { + "description": "desiredReplicas is the desired number of replicas of pods managed by this autoscaler, as last calculated by the autoscaler.", + "format": "int32", + "type": "integer" + }, + "lastScaleTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastScaleTime is the last time the HorizontalPodAutoscaler scaled the number of pods, used by the autoscaler to control how often the number of pods is changed." + }, + "observedGeneration": { + "description": "observedGeneration is the most recent generation observed by this autoscaler.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "currentReplicas", + "desiredReplicas", + "conditions" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.MetricIdentifier": { + "description": "MetricIdentifier defines the name and optionally selector for a metric", + "properties": { + "name": { + "description": "name is the name of the given metric", + "type": "string" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector is the string-encoded form of a standard kubernetes label selector for the given metric When set, it is passed as an additional parameter to the metrics server for more specific metrics scoping. When unset, just the metricName will be used to gather metrics." + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.MetricSpec": { + "description": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).", + "properties": { + "external": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.ExternalMetricSource", + "description": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster)." + }, + "object": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.ObjectMetricSource", + "description": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object)." + }, + "pods": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.PodsMetricSource", + "description": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value." + }, + "resource": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.ResourceMetricSource", + "description": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source." + }, + "type": { + "description": "type is the type of metric source. It should be one of \"Object\", \"Pods\" or \"Resource\", each mapping to a matching field in the object.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.MetricStatus": { + "description": "MetricStatus describes the last-read state of a single metric.", + "properties": { + "external": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.ExternalMetricStatus", + "description": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster)." + }, + "object": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.ObjectMetricStatus", + "description": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object)." + }, + "pods": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.PodsMetricStatus", + "description": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value." + }, + "resource": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.ResourceMetricStatus", + "description": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source." + }, + "type": { + "description": "type is the type of metric source. It will be one of \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.MetricTarget": { + "description": "MetricTarget defines the target value, average value, or average utilization of a specific metric", + "properties": { + "averageUtilization": { + "description": "averageUtilization is the target value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods. Currently only valid for Resource metric source type", + "format": "int32", + "type": "integer" + }, + "averageValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "averageValue is the target value of the average of the metric across all relevant pods (as a quantity)" + }, + "type": { + "description": "type represents whether the metric type is Utilization, Value, or AverageValue", + "type": "string" + }, + "value": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "value is the target value of the metric (as a quantity)." + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.MetricValueStatus": { + "description": "MetricValueStatus holds the current value for a metric", + "properties": { + "averageUtilization": { + "description": "currentAverageUtilization is the current value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods.", + "format": "int32", + "type": "integer" + }, + "averageValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "averageValue is the current value of the average of the metric across all relevant pods (as a quantity)" + }, + "value": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "value is the current value of the metric (as a quantity)." + } + }, + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.ObjectMetricSource": { + "description": "ObjectMetricSource indicates how to scale on a metric describing a kubernetes object (for example, hits-per-second on an Ingress object).", + "properties": { + "describedObject": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference" + }, + "metric": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricIdentifier", + "description": "metric identifies the target metric by name and selector" + }, + "target": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricTarget", + "description": "target specifies the target value for the given metric" + } + }, + "required": [ + "describedObject", + "target", + "metric" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.ObjectMetricStatus": { + "description": "ObjectMetricStatus indicates the current value of a metric describing a kubernetes object (for example, hits-per-second on an Ingress object).", + "properties": { + "current": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricValueStatus", + "description": "current contains the current value for the given metric" + }, + "describedObject": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference" + }, + "metric": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricIdentifier", + "description": "metric identifies the target metric by name and selector" + } + }, + "required": [ + "metric", + "current", + "describedObject" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.PodsMetricSource": { + "description": "PodsMetricSource indicates how to scale on a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.", + "properties": { + "metric": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricIdentifier", + "description": "metric identifies the target metric by name and selector" + }, + "target": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricTarget", + "description": "target specifies the target value for the given metric" + } + }, + "required": [ + "metric", + "target" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.PodsMetricStatus": { + "description": "PodsMetricStatus indicates the current value of a metric describing each pod in the current scale target (for example, transactions-processed-per-second).", + "properties": { + "current": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricValueStatus", + "description": "current contains the current value for the given metric" + }, + "metric": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricIdentifier", + "description": "metric identifies the target metric by name and selector" + } + }, + "required": [ + "metric", + "current" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.ResourceMetricSource": { + "description": "ResourceMetricSource indicates how to scale on a resource metric known to Kubernetes, as specified in requests and limits, describing each pod in the current scale target (e.g. CPU or memory). The values will be averaged together before being compared to the target. Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. Only one \"target\" type should be set.", + "properties": { + "name": { + "description": "name is the name of the resource in question.", + "type": "string" + }, + "target": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricTarget", + "description": "target specifies the target value for the given metric" + } + }, + "required": [ + "name", + "target" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2beta2.ResourceMetricStatus": { + "description": "ResourceMetricStatus indicates the current value of a resource metric known to Kubernetes, as specified in requests and limits, describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.", + "properties": { + "current": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2beta2.MetricValueStatus", + "description": "current contains the current value for the given metric" + }, + "name": { + "description": "Name is the name of the resource in question.", + "type": "string" + } + }, + "required": [ + "name", + "current" + ], + "type": "object" + }, + "io.k8s.api.batch.v1.Job": { + "description": "Job represents the configuration of a single job.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Job" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.batch.v1.JobSpec", + "description": "Specification of the desired behavior of a job. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.batch.v1.JobStatus", + "description": "Current status of a job. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "batch", + "kind": "Job", + "version": "v1" + } + ] + }, + "io.k8s.api.batch.v1.JobCondition": { + "description": "JobCondition describes current state of a job.", + "properties": { + "lastProbeTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition was checked." + }, + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transit from one status to another." + }, + "message": { + "description": "Human readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "(brief) reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of job condition, Complete or Failed.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.batch.v1.JobList": { + "description": "JobList is a collection of jobs.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of Jobs.", + "items": { + "$ref": "#/definitions/io.k8s.api.batch.v1.Job" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "JobList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "batch", + "kind": "JobList", + "version": "v1" + } + ] + }, + "io.k8s.api.batch.v1.JobSpec": { + "description": "JobSpec describes how the job execution will look like.", + "properties": { + "activeDeadlineSeconds": { + "description": "Specifies the duration in seconds relative to the startTime that the job may be active before the system tries to terminate it; value must be positive integer", + "format": "int64", + "type": "integer" + }, + "backoffLimit": { + "description": "Specifies the number of retries before marking this job failed. Defaults to 6", + "format": "int32", + "type": "integer" + }, + "completions": { + "description": "Specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", + "format": "int32", + "type": "integer" + }, + "manualSelector": { + "description": "manualSelector controls generation of pod labels and pod selectors. Leave `manualSelector` unset unless you are certain what you are doing. When false or unset, the system pick labels unique to this job and appends those labels to the pod template. When true, the user is responsible for picking unique labels and specifying the selector. Failure to pick a unique label may cause this and other jobs to not function correctly. However, You may see `manualSelector=true` in jobs that were created with the old `extensions/v1beta1` API. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#specifying-your-own-pod-selector", + "type": "boolean" + }, + "parallelism": { + "description": "Specifies the maximum desired number of pods the job should run at any given time. The actual number of pods running in steady state will be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism), i.e. when the work left to do is less than max parallelism. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "A label query over pods that should match the pod count. Normally, the system sets this field for you. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Describes the pod that will be created when executing a job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/" + }, + "ttlSecondsAfterFinished": { + "description": "ttlSecondsAfterFinished limits the lifetime of a Job that has finished execution (either Complete or Failed). If this field is set, ttlSecondsAfterFinished after the Job finishes, it is eligible to be automatically deleted. When the Job is being deleted, its lifecycle guarantees (e.g. finalizers) will be honored. If this field is unset, the Job won't be automatically deleted. If this field is set to zero, the Job becomes eligible to be deleted immediately after it finishes. This field is alpha-level and is only honored by servers that enable the TTLAfterFinished feature.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "template" + ], + "type": "object" + }, + "io.k8s.api.batch.v1.JobStatus": { + "description": "JobStatus represents the current state of a Job.", + "properties": { + "active": { + "description": "The number of actively running pods.", + "format": "int32", + "type": "integer" + }, + "completionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC." + }, + "conditions": { + "description": "The latest available observations of an object's current state. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", + "items": { + "$ref": "#/definitions/io.k8s.api.batch.v1.JobCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "failed": { + "description": "The number of pods which reached phase Failed.", + "format": "int32", + "type": "integer" + }, + "startTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Represents time when the job was acknowledged by the job controller. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC." + }, + "succeeded": { + "description": "The number of pods which reached phase Succeeded.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.batch.v1beta1.CronJob": { + "description": "CronJob represents the configuration of a single cron job.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CronJob" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.batch.v1beta1.CronJobSpec", + "description": "Specification of the desired behavior of a cron job, including the schedule. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.batch.v1beta1.CronJobStatus", + "description": "Current status of a cron job. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "batch", + "kind": "CronJob", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.batch.v1beta1.CronJobList": { + "description": "CronJobList is a collection of cron jobs.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of CronJobs.", + "items": { + "$ref": "#/definitions/io.k8s.api.batch.v1beta1.CronJob" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CronJobList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "batch", + "kind": "CronJobList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.batch.v1beta1.CronJobSpec": { + "description": "CronJobSpec describes how the job execution will look like and when it will actually run.", + "properties": { + "concurrencyPolicy": { + "description": "Specifies how to treat concurrent executions of a Job. Valid values are: - \"Allow\" (default): allows CronJobs to run concurrently; - \"Forbid\": forbids concurrent runs, skipping next run if previous run hasn't finished yet; - \"Replace\": cancels currently running job and replaces it with a new one", + "type": "string" + }, + "failedJobsHistoryLimit": { + "description": "The number of failed finished jobs to retain. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1.", + "format": "int32", + "type": "integer" + }, + "jobTemplate": { + "$ref": "#/definitions/io.k8s.api.batch.v1beta1.JobTemplateSpec", + "description": "Specifies the job that will be created when executing a CronJob." + }, + "schedule": { + "description": "The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.", + "type": "string" + }, + "startingDeadlineSeconds": { + "description": "Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones.", + "format": "int64", + "type": "integer" + }, + "successfulJobsHistoryLimit": { + "description": "The number of successful finished jobs to retain. This is a pointer to distinguish between explicit zero and not specified. Defaults to 3.", + "format": "int32", + "type": "integer" + }, + "suspend": { + "description": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.", + "type": "boolean" + } + }, + "required": [ + "schedule", + "jobTemplate" + ], + "type": "object" + }, + "io.k8s.api.batch.v1beta1.CronJobStatus": { + "description": "CronJobStatus represents the current state of a cron job.", + "properties": { + "active": { + "description": "A list of pointers to currently running jobs.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference" + }, + "type": "array" + }, + "lastScheduleTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Information when was the last time the job was successfully scheduled." + } + }, + "type": "object" + }, + "io.k8s.api.batch.v1beta1.JobTemplateSpec": { + "description": "JobTemplateSpec describes the data a Job should have when created from a template", + "properties": { + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata of the jobs created from this template. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.batch.v1.JobSpec", + "description": "Specification of the desired behavior of the job. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object" + }, + "io.k8s.api.batch.v2alpha1.CronJob": { + "description": "CronJob represents the configuration of a single cron job.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CronJob" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.batch.v2alpha1.CronJobSpec", + "description": "Specification of the desired behavior of a cron job, including the schedule. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.batch.v2alpha1.CronJobStatus", + "description": "Current status of a cron job. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "batch", + "kind": "CronJob", + "version": "v2alpha1" + } + ] + }, + "io.k8s.api.batch.v2alpha1.CronJobList": { + "description": "CronJobList is a collection of cron jobs.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of CronJobs.", + "items": { + "$ref": "#/definitions/io.k8s.api.batch.v2alpha1.CronJob" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CronJobList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "batch", + "kind": "CronJobList", + "version": "v2alpha1" + } + ] + }, + "io.k8s.api.batch.v2alpha1.CronJobSpec": { + "description": "CronJobSpec describes how the job execution will look like and when it will actually run.", + "properties": { + "concurrencyPolicy": { + "description": "Specifies how to treat concurrent executions of a Job. Valid values are: - \"Allow\" (default): allows CronJobs to run concurrently; - \"Forbid\": forbids concurrent runs, skipping next run if previous run hasn't finished yet; - \"Replace\": cancels currently running job and replaces it with a new one", + "type": "string" + }, + "failedJobsHistoryLimit": { + "description": "The number of failed finished jobs to retain. This is a pointer to distinguish between explicit zero and not specified.", + "format": "int32", + "type": "integer" + }, + "jobTemplate": { + "$ref": "#/definitions/io.k8s.api.batch.v2alpha1.JobTemplateSpec", + "description": "Specifies the job that will be created when executing a CronJob." + }, + "schedule": { + "description": "The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.", + "type": "string" + }, + "startingDeadlineSeconds": { + "description": "Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones.", + "format": "int64", + "type": "integer" + }, + "successfulJobsHistoryLimit": { + "description": "The number of successful finished jobs to retain. This is a pointer to distinguish between explicit zero and not specified.", + "format": "int32", + "type": "integer" + }, + "suspend": { + "description": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.", + "type": "boolean" + } + }, + "required": [ + "schedule", + "jobTemplate" + ], + "type": "object" + }, + "io.k8s.api.batch.v2alpha1.CronJobStatus": { + "description": "CronJobStatus represents the current state of a cron job.", + "properties": { + "active": { + "description": "A list of pointers to currently running jobs.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference" + }, + "type": "array" + }, + "lastScheduleTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Information when was the last time the job was successfully scheduled." + } + }, + "type": "object" + }, + "io.k8s.api.batch.v2alpha1.JobTemplateSpec": { + "description": "JobTemplateSpec describes the data a Job should have when created from a template", + "properties": { + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata of the jobs created from this template. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.batch.v1.JobSpec", + "description": "Specification of the desired behavior of the job. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object" + }, + "io.k8s.api.certificates.v1beta1.CertificateSigningRequest": { + "description": "Describes a certificate signing request", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CertificateSigningRequest" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.CertificateSigningRequestSpec", + "description": "The certificate request itself and any additional information." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.CertificateSigningRequestStatus", + "description": "Derived information about the request." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "certificates.k8s.io", + "kind": "CertificateSigningRequest", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.certificates.v1beta1.CertificateSigningRequestCondition": { + "properties": { + "lastUpdateTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "timestamp for the last update to this condition" + }, + "message": { + "description": "human readable message with details about the request state", + "type": "string" + }, + "reason": { + "description": "brief reason for the request state", + "type": "string" + }, + "type": { + "description": "request approval state, currently Approved or Denied.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "io.k8s.api.certificates.v1beta1.CertificateSigningRequestList": { + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "items": { + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.CertificateSigningRequest" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CertificateSigningRequestList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "certificates.k8s.io", + "kind": "CertificateSigningRequestList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.certificates.v1beta1.CertificateSigningRequestSpec": { + "description": "This information is immutable after the request is created. Only the Request and Usages fields can be set on creation, other fields are derived by Kubernetes and cannot be modified by users.", + "properties": { + "extra": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": "Extra information about the requesting user. See user.Info interface for details.", + "type": "object" + }, + "groups": { + "description": "Group information about the requesting user. See user.Info interface for details.", + "items": { + "type": "string" + }, + "type": "array" + }, + "request": { + "description": "Base64-encoded PKCS#10 CSR data", + "format": "byte", + "type": "string" + }, + "uid": { + "description": "UID information about the requesting user. See user.Info interface for details.", + "type": "string" + }, + "usages": { + "description": "allowedUsages specifies a set of usage contexts the key will be valid for. See: https://tools.ietf.org/html/rfc5280#section-4.2.1.3\n https://tools.ietf.org/html/rfc5280#section-4.2.1.12", + "items": { + "type": "string" + }, + "type": "array" + }, + "username": { + "description": "Information about the requesting user. See user.Info interface for details.", + "type": "string" + } + }, + "required": [ + "request" + ], + "type": "object" + }, + "io.k8s.api.certificates.v1beta1.CertificateSigningRequestStatus": { + "properties": { + "certificate": { + "description": "If request was approved, the controller will place the issued certificate here.", + "format": "byte", + "type": "string" + }, + "conditions": { + "description": "Conditions applied to the request, such as approval or denial.", + "items": { + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.CertificateSigningRequestCondition" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.coordination.v1.Lease": { + "description": "Lease defines a lease concept.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Lease" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.coordination.v1.LeaseSpec", + "description": "Specification of the Lease. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "coordination.k8s.io", + "kind": "Lease", + "version": "v1" + } + ] + }, + "io.k8s.api.coordination.v1.LeaseList": { + "description": "LeaseList is a list of Lease objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.coordination.v1.Lease" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "LeaseList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "coordination.k8s.io", + "kind": "LeaseList", + "version": "v1" + } + ] + }, + "io.k8s.api.coordination.v1.LeaseSpec": { + "description": "LeaseSpec is a specification of a Lease.", + "properties": { + "acquireTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "acquireTime is a time when the current lease was acquired." + }, + "holderIdentity": { + "description": "holderIdentity contains the identity of the holder of a current lease.", + "type": "string" + }, + "leaseDurationSeconds": { + "description": "leaseDurationSeconds is a duration that candidates for a lease need to wait to force acquire it. This is measure against time of last observed RenewTime.", + "format": "int32", + "type": "integer" + }, + "leaseTransitions": { + "description": "leaseTransitions is the number of transitions of a lease between holders.", + "format": "int32", + "type": "integer" + }, + "renewTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "renewTime is a time when the current holder of a lease has last updated the lease." + } + }, + "type": "object" + }, + "io.k8s.api.coordination.v1beta1.Lease": { + "description": "Lease defines a lease concept.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Lease" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.coordination.v1beta1.LeaseSpec", + "description": "Specification of the Lease. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "coordination.k8s.io", + "kind": "Lease", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.coordination.v1beta1.LeaseList": { + "description": "LeaseList is a list of Lease objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.coordination.v1beta1.Lease" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "LeaseList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "coordination.k8s.io", + "kind": "LeaseList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.coordination.v1beta1.LeaseSpec": { + "description": "LeaseSpec is a specification of a Lease.", + "properties": { + "acquireTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "acquireTime is a time when the current lease was acquired." + }, + "holderIdentity": { + "description": "holderIdentity contains the identity of the holder of a current lease.", + "type": "string" + }, + "leaseDurationSeconds": { + "description": "leaseDurationSeconds is a duration that candidates for a lease need to wait to force acquire it. This is measure against time of last observed RenewTime.", + "format": "int32", + "type": "integer" + }, + "leaseTransitions": { + "description": "leaseTransitions is the number of transitions of a lease between holders.", + "format": "int32", + "type": "integer" + }, + "renewTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "renewTime is a time when the current holder of a lease has last updated the lease." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.AWSElasticBlockStoreVolumeSource": { + "description": "Represents a Persistent Disk resource in AWS.\n\nAn AWS EBS disk must exist before mounting to a container. The disk must also be in the same AWS zone as the kubelet. An AWS EBS disk can only be mounted as read/write once. AWS EBS volumes support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + "type": "string" + }, + "partition": { + "description": "The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty).", + "format": "int32", + "type": "integer" + }, + "readOnly": { + "description": "Specify \"true\" to force and set the ReadOnly property in VolumeMounts to \"true\". If omitted, the default is \"false\". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + "type": "boolean" + }, + "volumeID": { + "description": "Unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + "type": "string" + } + }, + "required": [ + "volumeID" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Affinity": { + "description": "Affinity is a group of affinity scheduling rules.", + "properties": { + "nodeAffinity": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeAffinity", + "description": "Describes node affinity scheduling rules for the pod." + }, + "podAffinity": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodAffinity", + "description": "Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s))." + }, + "podAntiAffinity": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodAntiAffinity", + "description": "Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s))." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.AttachedVolume": { + "description": "AttachedVolume describes a volume attached to a node", + "properties": { + "devicePath": { + "description": "DevicePath represents the device path where the volume should be available", + "type": "string" + }, + "name": { + "description": "Name of the attached volume", + "type": "string" + } + }, + "required": [ + "name", + "devicePath" + ], + "type": "object" + }, + "io.k8s.api.core.v1.AzureDiskVolumeSource": { + "description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", + "properties": { + "cachingMode": { + "description": "Host Caching mode: None, Read Only, Read Write.", + "type": "string" + }, + "diskName": { + "description": "The Name of the data disk in the blob storage", + "type": "string" + }, + "diskURI": { + "description": "The URI the data disk in the blob storage", + "type": "string" + }, + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "kind": { + "description": "Expected values Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared", + "type": "string" + }, + "readOnly": { + "description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + } + }, + "required": [ + "diskName", + "diskURI" + ], + "type": "object" + }, + "io.k8s.api.core.v1.AzureFilePersistentVolumeSource": { + "description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + "properties": { + "readOnly": { + "description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretName": { + "description": "the name of secret that contains Azure Storage Account Name and Key", + "type": "string" + }, + "secretNamespace": { + "description": "the namespace of the secret that contains Azure Storage Account Name and Key default is the same as the Pod", + "type": "string" + }, + "shareName": { + "description": "Share Name", + "type": "string" + } + }, + "required": [ + "secretName", + "shareName" + ], + "type": "object" + }, + "io.k8s.api.core.v1.AzureFileVolumeSource": { + "description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + "properties": { + "readOnly": { + "description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretName": { + "description": "the name of secret that contains Azure Storage Account Name and Key", + "type": "string" + }, + "shareName": { + "description": "Share Name", + "type": "string" + } + }, + "required": [ + "secretName", + "shareName" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Binding": { + "description": "Binding ties one object to another; for example, a pod is bound to a node by a scheduler. Deprecated in 1.7, please use the bindings subresource of pods instead.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Binding" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "target": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "The target object that you want to bind to the standard object." + } + }, + "required": [ + "target" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Binding", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.CSIPersistentVolumeSource": { + "description": "Represents storage that is managed by an external CSI volume driver (Beta feature)", + "properties": { + "controllerPublishSecretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "ControllerPublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerPublishVolume and ControllerUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed." + }, + "driver": { + "description": "Driver is the name of the driver to use for this volume. Required.", + "type": "string" + }, + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\".", + "type": "string" + }, + "nodePublishSecretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed." + }, + "nodeStageSecretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "NodeStageSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeStageVolume and NodeStageVolume and NodeUnstageVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed." + }, + "readOnly": { + "description": "Optional: The value to pass to ControllerPublishVolumeRequest. Defaults to false (read/write).", + "type": "boolean" + }, + "volumeAttributes": { + "additionalProperties": { + "type": "string" + }, + "description": "Attributes of the volume to publish.", + "type": "object" + }, + "volumeHandle": { + "description": "VolumeHandle is the unique volume name returned by the CSI volume plugin\u2019s CreateVolume to refer to the volume on all subsequent calls. Required.", + "type": "string" + } + }, + "required": [ + "driver", + "volumeHandle" + ], + "type": "object" + }, + "io.k8s.api.core.v1.CSIVolumeSource": { + "description": "Represents a source location of a volume to mount, managed by an external CSI driver", + "properties": { + "driver": { + "description": "Driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.", + "type": "string" + }, + "fsType": { + "description": "Filesystem type to mount. Ex. \"ext4\", \"xfs\", \"ntfs\". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.", + "type": "string" + }, + "nodePublishSecretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed." + }, + "readOnly": { + "description": "Specifies a read-only configuration for the volume. Defaults to false (read/write).", + "type": "boolean" + }, + "volumeAttributes": { + "additionalProperties": { + "type": "string" + }, + "description": "VolumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.", + "type": "object" + } + }, + "required": [ + "driver" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Capabilities": { + "description": "Adds and removes POSIX capabilities from running containers.", + "properties": { + "add": { + "description": "Added capabilities", + "items": { + "type": "string" + }, + "type": "array" + }, + "drop": { + "description": "Removed capabilities", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.CephFSPersistentVolumeSource": { + "description": "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.", + "properties": { + "monitors": { + "description": "Required: Monitors is a collection of Ceph monitors More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "items": { + "type": "string" + }, + "type": "array" + }, + "path": { + "description": "Optional: Used as the mounted root, rather than the full Ceph tree, default is /", + "type": "string" + }, + "readOnly": { + "description": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "type": "boolean" + }, + "secretFile": { + "description": "Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "type": "string" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it" + }, + "user": { + "description": "Optional: User is the rados user name, default is admin More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "type": "string" + } + }, + "required": [ + "monitors" + ], + "type": "object" + }, + "io.k8s.api.core.v1.CephFSVolumeSource": { + "description": "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.", + "properties": { + "monitors": { + "description": "Required: Monitors is a collection of Ceph monitors More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "items": { + "type": "string" + }, + "type": "array" + }, + "path": { + "description": "Optional: Used as the mounted root, rather than the full Ceph tree, default is /", + "type": "string" + }, + "readOnly": { + "description": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "type": "boolean" + }, + "secretFile": { + "description": "Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "type": "string" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it" + }, + "user": { + "description": "Optional: User is the rados user name, default is admin More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "type": "string" + } + }, + "required": [ + "monitors" + ], + "type": "object" + }, + "io.k8s.api.core.v1.CinderPersistentVolumeSource": { + "description": "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "type": "string" + }, + "readOnly": { + "description": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "Optional: points to a secret object containing parameters used to connect to OpenStack." + }, + "volumeID": { + "description": "volume id used to identify the volume in cinder More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "type": "string" + } + }, + "required": [ + "volumeID" + ], + "type": "object" + }, + "io.k8s.api.core.v1.CinderVolumeSource": { + "description": "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "type": "string" + }, + "readOnly": { + "description": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "Optional: points to a secret object containing parameters used to connect to OpenStack." + }, + "volumeID": { + "description": "volume id used to identify the volume in cinder More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "type": "string" + } + }, + "required": [ + "volumeID" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ClientIPConfig": { + "description": "ClientIPConfig represents the configurations of Client IP based session affinity.", + "properties": { + "timeoutSeconds": { + "description": "timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be >0 && <=86400(for 1 day) if ServiceAffinity == \"ClientIP\". Default value is 10800(for 3 hours).", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ComponentCondition": { + "description": "Information about the condition of a component.", + "properties": { + "error": { + "description": "Condition error code for a component. For example, a health check error code.", + "type": "string" + }, + "message": { + "description": "Message about the condition for a component. For example, information about a health check.", + "type": "string" + }, + "status": { + "description": "Status of the condition for a component. Valid values for \"Healthy\": \"True\", \"False\", or \"Unknown\".", + "type": "string" + }, + "type": { + "description": "Type of condition for a component. Valid value: \"Healthy\"", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ComponentStatus": { + "description": "ComponentStatus (and ComponentStatusList) holds the cluster validation info.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "conditions": { + "description": "List of component conditions observed", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ComponentCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ComponentStatus" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ComponentStatus", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ComponentStatusList": { + "description": "Status of all the conditions for the component as a list of ComponentStatus objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ComponentStatus objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ComponentStatus" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ComponentStatusList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ComponentStatusList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ConfigMap": { + "description": "ConfigMap holds configuration data for pods to consume.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "binaryData": { + "additionalProperties": { + "format": "byte", + "type": "string" + }, + "description": "BinaryData contains the binary data. Each key must consist of alphanumeric characters, '-', '_' or '.'. BinaryData can contain byte sequences that are not in the UTF-8 range. The keys stored in BinaryData must not overlap with the ones in the Data field, this is enforced during validation process. Using this field will require 1.10+ apiserver and kubelet.", + "type": "object" + }, + "data": { + "additionalProperties": { + "type": "string" + }, + "description": "Data contains the configuration data. Each key must consist of alphanumeric characters, '-', '_' or '.'. Values with non-UTF-8 byte sequences must use the BinaryData field. The keys stored in Data must not overlap with the keys in the BinaryData field, this is enforced during validation process.", + "type": "object" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ConfigMap" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ConfigMap", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ConfigMapEnvSource": { + "description": "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.", + "properties": { + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "Specify whether the ConfigMap must be defined", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ConfigMapKeySelector": { + "description": "Selects a key from a ConfigMap.", + "properties": { + "key": { + "description": "The key to select.", + "type": "string" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "Specify whether the ConfigMap or it's key must be defined", + "type": "boolean" + } + }, + "required": [ + "key" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ConfigMapList": { + "description": "ConfigMapList is a resource containing a list of ConfigMap objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of ConfigMaps.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMap" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ConfigMapList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ConfigMapList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ConfigMapNodeConfigSource": { + "description": "ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node.", + "properties": { + "kubeletConfigKey": { + "description": "KubeletConfigKey declares which key of the referenced ConfigMap corresponds to the KubeletConfiguration structure This field is required in all cases.", + "type": "string" + }, + "name": { + "description": "Name is the metadata.name of the referenced ConfigMap. This field is required in all cases.", + "type": "string" + }, + "namespace": { + "description": "Namespace is the metadata.namespace of the referenced ConfigMap. This field is required in all cases.", + "type": "string" + }, + "resourceVersion": { + "description": "ResourceVersion is the metadata.ResourceVersion of the referenced ConfigMap. This field is forbidden in Node.Spec, and required in Node.Status.", + "type": "string" + }, + "uid": { + "description": "UID is the metadata.UID of the referenced ConfigMap. This field is forbidden in Node.Spec, and required in Node.Status.", + "type": "string" + } + }, + "required": [ + "namespace", + "name", + "kubeletConfigKey" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ConfigMapProjection": { + "description": "Adapts a ConfigMap into a projected volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a projected volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. Note that this is identical to a configmap volume source without the default mode.", + "properties": { + "items": { + "description": "If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.KeyToPath" + }, + "type": "array" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "Specify whether the ConfigMap or it's keys must be defined", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ConfigMapVolumeSource": { + "description": "Adapts a ConfigMap into a volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. ConfigMap volumes support ownership management and SELinux relabeling.", + "properties": { + "defaultMode": { + "description": "Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + "format": "int32", + "type": "integer" + }, + "items": { + "description": "If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.KeyToPath" + }, + "type": "array" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "Specify whether the ConfigMap or it's keys must be defined", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Container": { + "description": "A single application container that you want to run within a pod.", + "properties": { + "args": { + "description": "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "items": { + "type": "string" + }, + "type": "array" + }, + "command": { + "description": "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "items": { + "type": "string" + }, + "type": "array" + }, + "env": { + "description": "List of environment variables to set in the container. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EnvVar" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "envFrom": { + "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EnvFromSource" + }, + "type": "array" + }, + "image": { + "description": "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.", + "type": "string" + }, + "imagePullPolicy": { + "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + "type": "string" + }, + "lifecycle": { + "$ref": "#/definitions/io.k8s.api.core.v1.Lifecycle", + "description": "Actions that the management system should take in response to container lifecycle events. Cannot be updated." + }, + "livenessProbe": { + "$ref": "#/definitions/io.k8s.api.core.v1.Probe", + "description": "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes" + }, + "name": { + "description": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", + "type": "string" + }, + "ports": { + "description": "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerPort" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "containerPort", + "protocol" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "containerPort", + "x-kubernetes-patch-strategy": "merge" + }, + "readinessProbe": { + "$ref": "#/definitions/io.k8s.api.core.v1.Probe", + "description": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes" + }, + "resources": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements", + "description": "Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/" + }, + "securityContext": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecurityContext", + "description": "Security options the pod should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/" + }, + "stdin": { + "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + "type": "boolean" + }, + "stdinOnce": { + "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + "type": "boolean" + }, + "terminationMessagePath": { + "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + "type": "string" + }, + "terminationMessagePolicy": { + "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + "type": "string" + }, + "tty": { + "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + "type": "boolean" + }, + "volumeDevices": { + "description": "volumeDevices is the list of block devices to be used by the container. This is a beta feature.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.VolumeDevice" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge" + }, + "volumeMounts": { + "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.VolumeMount" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge" + }, + "workingDir": { + "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ContainerImage": { + "description": "Describe a container image", + "properties": { + "names": { + "description": "Names by which this image is known. e.g. [\"k8s.gcr.io/hyperkube:v1.0.7\", \"dockerhub.io/google_containers/hyperkube:v1.0.7\"]", + "items": { + "type": "string" + }, + "type": "array" + }, + "sizeBytes": { + "description": "The size of the image in bytes.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "names" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ContainerPort": { + "description": "ContainerPort represents a network port in a single container.", + "properties": { + "containerPort": { + "description": "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.", + "format": "int32", + "type": "integer" + }, + "hostIP": { + "description": "What host IP to bind the external port to.", + "type": "string" + }, + "hostPort": { + "description": "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.", + "format": "int32", + "type": "integer" + }, + "name": { + "description": "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.", + "type": "string" + }, + "protocol": { + "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".", + "type": "string" + } + }, + "required": [ + "containerPort" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ContainerState": { + "description": "ContainerState holds a possible state of container. Only one of its members may be specified. If none of them is specified, the default one is ContainerStateWaiting.", + "properties": { + "running": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerStateRunning", + "description": "Details about a running container" + }, + "terminated": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerStateTerminated", + "description": "Details about a terminated container" + }, + "waiting": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerStateWaiting", + "description": "Details about a waiting container" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ContainerStateRunning": { + "description": "ContainerStateRunning is a running state of a container.", + "properties": { + "startedAt": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Time at which the container was last (re-)started" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ContainerStateTerminated": { + "description": "ContainerStateTerminated is a terminated state of a container.", + "properties": { + "containerID": { + "description": "Container's ID in the format 'docker://'", + "type": "string" + }, + "exitCode": { + "description": "Exit status from the last termination of the container", + "format": "int32", + "type": "integer" + }, + "finishedAt": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Time at which the container last terminated" + }, + "message": { + "description": "Message regarding the last termination of the container", + "type": "string" + }, + "reason": { + "description": "(brief) reason from the last termination of the container", + "type": "string" + }, + "signal": { + "description": "Signal from the last termination of the container", + "format": "int32", + "type": "integer" + }, + "startedAt": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Time at which previous execution of the container started" + } + }, + "required": [ + "exitCode" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ContainerStateWaiting": { + "description": "ContainerStateWaiting is a waiting state of a container.", + "properties": { + "message": { + "description": "Message regarding why the container is not yet running.", + "type": "string" + }, + "reason": { + "description": "(brief) reason the container is not yet running.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ContainerStatus": { + "description": "ContainerStatus contains details for the current status of this container.", + "properties": { + "containerID": { + "description": "Container's ID in the format 'docker://'.", + "type": "string" + }, + "image": { + "description": "The image the container is running. More info: https://kubernetes.io/docs/concepts/containers/images", + "type": "string" + }, + "imageID": { + "description": "ImageID of the container's image.", + "type": "string" + }, + "lastState": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerState", + "description": "Details about the container's last termination condition." + }, + "name": { + "description": "This must be a DNS_LABEL. Each container in a pod must have a unique name. Cannot be updated.", + "type": "string" + }, + "ready": { + "description": "Specifies whether the container has passed its readiness probe.", + "type": "boolean" + }, + "restartCount": { + "description": "The number of times the container has been restarted, currently based on the number of dead containers that have not yet been removed. Note that this is calculated from dead containers. But those containers are subject to garbage collection. This value will get capped at 5 by GC.", + "format": "int32", + "type": "integer" + }, + "state": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerState", + "description": "Details about the container's current condition." + } + }, + "required": [ + "name", + "ready", + "restartCount", + "image", + "imageID" + ], + "type": "object" + }, + "io.k8s.api.core.v1.DaemonEndpoint": { + "description": "DaemonEndpoint contains information about a single Daemon endpoint.", + "properties": { + "Port": { + "description": "Port number of the given endpoint.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "Port" + ], + "type": "object" + }, + "io.k8s.api.core.v1.DownwardAPIProjection": { + "description": "Represents downward API info for projecting into a projected volume. Note that this is identical to a downwardAPI volume source without the default mode.", + "properties": { + "items": { + "description": "Items is a list of DownwardAPIVolume file", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.DownwardAPIVolumeFile" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.DownwardAPIVolumeFile": { + "description": "DownwardAPIVolumeFile represents information to create the file containing the pod field", + "properties": { + "fieldRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectFieldSelector", + "description": "Required: Selects a field of the pod: only annotations, labels, name and namespace are supported." + }, + "mode": { + "description": "Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + "format": "int32", + "type": "integer" + }, + "path": { + "description": "Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'", + "type": "string" + }, + "resourceFieldRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceFieldSelector", + "description": "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported." + } + }, + "required": [ + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.DownwardAPIVolumeSource": { + "description": "DownwardAPIVolumeSource represents a volume containing downward API info. Downward API volumes support ownership management and SELinux relabeling.", + "properties": { + "defaultMode": { + "description": "Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + "format": "int32", + "type": "integer" + }, + "items": { + "description": "Items is a list of downward API volume file", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.DownwardAPIVolumeFile" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.EmptyDirVolumeSource": { + "description": "Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.", + "properties": { + "medium": { + "description": "What type of storage medium should back this directory. The default is \"\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir", + "type": "string" + }, + "sizeLimit": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "Total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.EndpointAddress": { + "description": "EndpointAddress is a tuple that describes single IP address.", + "properties": { + "hostname": { + "description": "The Hostname of this endpoint", + "type": "string" + }, + "ip": { + "description": "The IP of this endpoint. May not be loopback (127.0.0.0/8), link-local (169.254.0.0/16), or link-local multicast ((224.0.0.0/24). IPv6 is also accepted but not fully supported on all platforms. Also, certain kubernetes components, like kube-proxy, are not IPv6 ready.", + "type": "string" + }, + "nodeName": { + "description": "Optional: Node hosting this endpoint. This can be used to determine endpoints local to a node.", + "type": "string" + }, + "targetRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "Reference to object providing the endpoint." + } + }, + "required": [ + "ip" + ], + "type": "object" + }, + "io.k8s.api.core.v1.EndpointPort": { + "description": "EndpointPort is a tuple that describes a single port.", + "properties": { + "name": { + "description": "The name of this port (corresponds to ServicePort.Name). Must be a DNS_LABEL. Optional only if one port is defined.", + "type": "string" + }, + "port": { + "description": "The port number of the endpoint.", + "format": "int32", + "type": "integer" + }, + "protocol": { + "description": "The IP protocol for this port. Must be UDP, TCP, or SCTP. Default is TCP.", + "type": "string" + } + }, + "required": [ + "port" + ], + "type": "object" + }, + "io.k8s.api.core.v1.EndpointSubset": { + "description": "EndpointSubset is a group of addresses with a common set of ports. The expanded set of endpoints is the Cartesian product of Addresses x Ports. For example, given:\n {\n Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n }\nThe resulting set of endpoints can be viewed as:\n a: [ 10.10.1.1:8675, 10.10.2.2:8675 ],\n b: [ 10.10.1.1:309, 10.10.2.2:309 ]", + "properties": { + "addresses": { + "description": "IP addresses which offer the related ports that are marked as ready. These endpoints should be considered safe for load balancers and clients to utilize.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EndpointAddress" + }, + "type": "array" + }, + "notReadyAddresses": { + "description": "IP addresses which offer the related ports but are not currently marked as ready because they have not yet finished starting, have recently failed a readiness check, or have recently failed a liveness check.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EndpointAddress" + }, + "type": "array" + }, + "ports": { + "description": "Port numbers available on the related IP addresses.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EndpointPort" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Endpoints": { + "description": "Endpoints is a collection of endpoints that implement the actual service. Example:\n Name: \"mysvc\",\n Subsets: [\n {\n Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n },\n {\n Addresses: [{\"ip\": \"10.10.3.3\"}],\n Ports: [{\"name\": \"a\", \"port\": 93}, {\"name\": \"b\", \"port\": 76}]\n },\n ]", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Endpoints" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "subsets": { + "description": "The set of all endpoints is the union of all subsets. Addresses are placed into subsets according to the IPs they share. A single address with multiple ports, some of which are ready and some of which are not (because they come from different containers) will result in the address being displayed in different subsets for the different ports. No address will appear in both Addresses and NotReadyAddresses in the same subset. Sets of addresses and ports that comprise a service.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EndpointSubset" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Endpoints", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.EndpointsList": { + "description": "EndpointsList is a list of endpoints.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of endpoints.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Endpoints" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "EndpointsList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "EndpointsList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.EnvFromSource": { + "description": "EnvFromSource represents the source of a set of ConfigMaps", + "properties": { + "configMapRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapEnvSource", + "description": "The ConfigMap to select from" + }, + "prefix": { + "description": "An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.", + "type": "string" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretEnvSource", + "description": "The Secret to select from" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.EnvVar": { + "description": "EnvVar represents an environment variable present in a Container.", + "properties": { + "name": { + "description": "Name of the environment variable. Must be a C_IDENTIFIER.", + "type": "string" + }, + "value": { + "description": "Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".", + "type": "string" + }, + "valueFrom": { + "$ref": "#/definitions/io.k8s.api.core.v1.EnvVarSource", + "description": "Source for the environment variable's value. Cannot be used if value is not empty." + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.EnvVarSource": { + "description": "EnvVarSource represents a source for the value of an EnvVar.", + "properties": { + "configMapKeyRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapKeySelector", + "description": "Selects a key of a ConfigMap." + }, + "fieldRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectFieldSelector", + "description": "Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP." + }, + "resourceFieldRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceFieldSelector", + "description": "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported." + }, + "secretKeyRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretKeySelector", + "description": "Selects a key of a secret in the pod's namespace" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Event": { + "description": "Event is a report of an event somewhere in the cluster.", + "properties": { + "action": { + "description": "What action was taken/failed regarding to the Regarding object.", + "type": "string" + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "count": { + "description": "The number of times this event has occurred.", + "format": "int32", + "type": "integer" + }, + "eventTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "Time when this Event was first observed." + }, + "firstTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The time at which the event was first recorded. (Time of server receipt is in TypeMeta.)" + }, + "involvedObject": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "The object that this event is about." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Event" + ] + }, + "lastTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The time at which the most recent occurrence of this event was recorded." + }, + "message": { + "description": "A human-readable description of the status of this operation.", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "reason": { + "description": "This should be a short, machine understandable string that gives the reason for the transition into the object's current status.", + "type": "string" + }, + "related": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "Optional secondary object for more complex actions." + }, + "reportingComponent": { + "description": "Name of the controller that emitted this Event, e.g. `kubernetes.io/kubelet`.", + "type": "string" + }, + "reportingInstance": { + "description": "ID of the controller instance, e.g. `kubelet-xyzf`.", + "type": "string" + }, + "series": { + "$ref": "#/definitions/io.k8s.api.core.v1.EventSeries", + "description": "Data about the Event series this event represents or nil if it's a singleton Event." + }, + "source": { + "$ref": "#/definitions/io.k8s.api.core.v1.EventSource", + "description": "The component reporting this event. Should be a short machine understandable string." + }, + "type": { + "description": "Type of this event (Normal, Warning), new types could be added in the future", + "type": "string" + } + }, + "required": [ + "metadata", + "involvedObject" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Event", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.EventList": { + "description": "EventList is a list of events.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of events", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Event" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "EventList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "EventList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.EventSeries": { + "description": "EventSeries contain information on series of events, i.e. thing that was/is happening continuously for some time.", + "properties": { + "count": { + "description": "Number of occurrences in this series up to the last heartbeat time", + "format": "int32", + "type": "integer" + }, + "lastObservedTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "Time of the last occurrence observed" + }, + "state": { + "description": "State of this Series: Ongoing or Finished", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.EventSource": { + "description": "EventSource contains information for an event.", + "properties": { + "component": { + "description": "Component from which the event is generated.", + "type": "string" + }, + "host": { + "description": "Node name on which the event is generated.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ExecAction": { + "description": "ExecAction describes a \"run in container\" action.", + "properties": { + "command": { + "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.FCVolumeSource": { + "description": "Represents a Fibre Channel volume. Fibre Channel volumes can only be mounted as read/write once. Fibre Channel volumes support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "lun": { + "description": "Optional: FC target lun number", + "format": "int32", + "type": "integer" + }, + "readOnly": { + "description": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "targetWWNs": { + "description": "Optional: FC target worldwide names (WWNs)", + "items": { + "type": "string" + }, + "type": "array" + }, + "wwids": { + "description": "Optional: FC volume world wide identifiers (wwids) Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.FlexPersistentVolumeSource": { + "description": "FlexPersistentVolumeSource represents a generic persistent volume resource that is provisioned/attached using an exec based plugin.", + "properties": { + "driver": { + "description": "Driver is the name of the driver to use for this volume.", + "type": "string" + }, + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default filesystem depends on FlexVolume script.", + "type": "string" + }, + "options": { + "additionalProperties": { + "type": "string" + }, + "description": "Optional: Extra command options if any.", + "type": "object" + }, + "readOnly": { + "description": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts." + } + }, + "required": [ + "driver" + ], + "type": "object" + }, + "io.k8s.api.core.v1.FlexVolumeSource": { + "description": "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", + "properties": { + "driver": { + "description": "Driver is the name of the driver to use for this volume.", + "type": "string" + }, + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default filesystem depends on FlexVolume script.", + "type": "string" + }, + "options": { + "additionalProperties": { + "type": "string" + }, + "description": "Optional: Extra command options if any.", + "type": "object" + }, + "readOnly": { + "description": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts." + } + }, + "required": [ + "driver" + ], + "type": "object" + }, + "io.k8s.api.core.v1.FlockerVolumeSource": { + "description": "Represents a Flocker volume mounted by the Flocker agent. One and only one of datasetName and datasetUUID should be set. Flocker volumes do not support ownership management or SELinux relabeling.", + "properties": { + "datasetName": { + "description": "Name of the dataset stored as metadata -> name on the dataset for Flocker should be considered as deprecated", + "type": "string" + }, + "datasetUUID": { + "description": "UUID of the dataset. This is unique identifier of a Flocker dataset", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.GCEPersistentDiskVolumeSource": { + "description": "Represents a Persistent Disk resource in Google Compute Engine.\n\nA GCE PD must exist before mounting to a container. The disk must also be in the same GCE project and zone as the kubelet. A GCE PD can only be mounted as read/write once or read-only many times. GCE PDs support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + "type": "string" + }, + "partition": { + "description": "The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + "format": "int32", + "type": "integer" + }, + "pdName": { + "description": "Unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + "type": "string" + }, + "readOnly": { + "description": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + "type": "boolean" + } + }, + "required": [ + "pdName" + ], + "type": "object" + }, + "io.k8s.api.core.v1.GitRepoVolumeSource": { + "description": "Represents a volume that is populated with the contents of a git repository. Git repo volumes do not support ownership management. Git repo volumes support SELinux relabeling.\n\nDEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.", + "properties": { + "directory": { + "description": "Target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name.", + "type": "string" + }, + "repository": { + "description": "Repository URL", + "type": "string" + }, + "revision": { + "description": "Commit hash for the specified revision.", + "type": "string" + } + }, + "required": [ + "repository" + ], + "type": "object" + }, + "io.k8s.api.core.v1.GlusterfsPersistentVolumeSource": { + "description": "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", + "properties": { + "endpoints": { + "description": "EndpointsName is the endpoint name that details Glusterfs topology. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md#create-a-pod", + "type": "string" + }, + "endpointsNamespace": { + "description": "EndpointsNamespace is the namespace that contains Glusterfs endpoint. If this field is empty, the EndpointNamespace defaults to the same namespace as the bound PVC. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md#create-a-pod", + "type": "string" + }, + "path": { + "description": "Path is the Glusterfs volume path. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md#create-a-pod", + "type": "string" + }, + "readOnly": { + "description": "ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md#create-a-pod", + "type": "boolean" + } + }, + "required": [ + "endpoints", + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.GlusterfsVolumeSource": { + "description": "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", + "properties": { + "endpoints": { + "description": "EndpointsName is the endpoint name that details Glusterfs topology. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md#create-a-pod", + "type": "string" + }, + "path": { + "description": "Path is the Glusterfs volume path. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md#create-a-pod", + "type": "string" + }, + "readOnly": { + "description": "ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md#create-a-pod", + "type": "boolean" + } + }, + "required": [ + "endpoints", + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.HTTPGetAction": { + "description": "HTTPGetAction describes an action based on HTTP Get requests.", + "properties": { + "host": { + "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.", + "type": "string" + }, + "httpHeaders": { + "description": "Custom headers to set in the request. HTTP allows repeated headers.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.HTTPHeader" + }, + "type": "array" + }, + "path": { + "description": "Path to access on the HTTP server.", + "type": "string" + }, + "port": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME." + }, + "scheme": { + "description": "Scheme to use for connecting to the host. Defaults to HTTP.", + "type": "string" + } + }, + "required": [ + "port" + ], + "type": "object" + }, + "io.k8s.api.core.v1.HTTPHeader": { + "description": "HTTPHeader describes a custom header to be used in HTTP probes", + "properties": { + "name": { + "description": "The header field name", + "type": "string" + }, + "value": { + "description": "The header field value", + "type": "string" + } + }, + "required": [ + "name", + "value" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Handler": { + "description": "Handler defines a specific action that should be taken", + "properties": { + "exec": { + "$ref": "#/definitions/io.k8s.api.core.v1.ExecAction", + "description": "One and only one of the following should be specified. Exec specifies the action to take." + }, + "httpGet": { + "$ref": "#/definitions/io.k8s.api.core.v1.HTTPGetAction", + "description": "HTTPGet specifies the http request to perform." + }, + "tcpSocket": { + "$ref": "#/definitions/io.k8s.api.core.v1.TCPSocketAction", + "description": "TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.HostAlias": { + "description": "HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the pod's hosts file.", + "properties": { + "hostnames": { + "description": "Hostnames for the above IP address.", + "items": { + "type": "string" + }, + "type": "array" + }, + "ip": { + "description": "IP address of the host file entry.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.HostPathVolumeSource": { + "description": "Represents a host path mapped into a pod. Host path volumes do not support ownership management or SELinux relabeling.", + "properties": { + "path": { + "description": "Path of the directory on the host. If the path is a symlink, it will follow the link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + "type": "string" + }, + "type": { + "description": "Type for HostPath Volume Defaults to \"\" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + "type": "string" + } + }, + "required": [ + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ISCSIPersistentVolumeSource": { + "description": "ISCSIPersistentVolumeSource represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling.", + "properties": { + "chapAuthDiscovery": { + "description": "whether support iSCSI Discovery CHAP authentication", + "type": "boolean" + }, + "chapAuthSession": { + "description": "whether support iSCSI Session CHAP authentication", + "type": "boolean" + }, + "fsType": { + "description": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi", + "type": "string" + }, + "initiatorName": { + "description": "Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection.", + "type": "string" + }, + "iqn": { + "description": "Target iSCSI Qualified Name.", + "type": "string" + }, + "iscsiInterface": { + "description": "iSCSI Interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).", + "type": "string" + }, + "lun": { + "description": "iSCSI Target Lun number.", + "format": "int32", + "type": "integer" + }, + "portals": { + "description": "iSCSI Target Portal List. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + "items": { + "type": "string" + }, + "type": "array" + }, + "readOnly": { + "description": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "CHAP Secret for iSCSI target and initiator authentication" + }, + "targetPortal": { + "description": "iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + "type": "string" + } + }, + "required": [ + "targetPortal", + "iqn", + "lun" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ISCSIVolumeSource": { + "description": "Represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling.", + "properties": { + "chapAuthDiscovery": { + "description": "whether support iSCSI Discovery CHAP authentication", + "type": "boolean" + }, + "chapAuthSession": { + "description": "whether support iSCSI Session CHAP authentication", + "type": "boolean" + }, + "fsType": { + "description": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi", + "type": "string" + }, + "initiatorName": { + "description": "Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection.", + "type": "string" + }, + "iqn": { + "description": "Target iSCSI Qualified Name.", + "type": "string" + }, + "iscsiInterface": { + "description": "iSCSI Interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).", + "type": "string" + }, + "lun": { + "description": "iSCSI Target Lun number.", + "format": "int32", + "type": "integer" + }, + "portals": { + "description": "iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + "items": { + "type": "string" + }, + "type": "array" + }, + "readOnly": { + "description": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "CHAP Secret for iSCSI target and initiator authentication" + }, + "targetPortal": { + "description": "iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + "type": "string" + } + }, + "required": [ + "targetPortal", + "iqn", + "lun" + ], + "type": "object" + }, + "io.k8s.api.core.v1.KeyToPath": { + "description": "Maps a string key to a path within a volume.", + "properties": { + "key": { + "description": "The key to project.", + "type": "string" + }, + "mode": { + "description": "Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + "format": "int32", + "type": "integer" + }, + "path": { + "description": "The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.", + "type": "string" + } + }, + "required": [ + "key", + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Lifecycle": { + "description": "Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.", + "properties": { + "postStart": { + "$ref": "#/definitions/io.k8s.api.core.v1.Handler", + "description": "PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks" + }, + "preStop": { + "$ref": "#/definitions/io.k8s.api.core.v1.Handler", + "description": "PreStop is called immediately before a container is terminated due to an API request or management event such as liveness probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod's termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod's termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.LimitRange": { + "description": "LimitRange sets resource usage limits for each kind of resource in a Namespace.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "LimitRange" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.LimitRangeSpec", + "description": "Spec defines the limits enforced. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "LimitRange", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.LimitRangeItem": { + "description": "LimitRangeItem defines a min/max usage limit for any resource that matches on kind.", + "properties": { + "default": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Default resource requirement limit value by resource name if resource limit is omitted.", + "type": "object" + }, + "defaultRequest": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "DefaultRequest is the default resource requirement request value by resource name if resource request is omitted.", + "type": "object" + }, + "max": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Max usage constraints on this kind by resource name.", + "type": "object" + }, + "maxLimitRequestRatio": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "MaxLimitRequestRatio if specified, the named resource must have a request and limit that are both non-zero where limit divided by request is less than or equal to the enumerated value; this represents the max burst for the named resource.", + "type": "object" + }, + "min": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Min usage constraints on this kind by resource name.", + "type": "object" + }, + "type": { + "description": "Type of resource that this limit applies to.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.LimitRangeList": { + "description": "LimitRangeList is a list of LimitRange items.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of LimitRange objects. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.LimitRange" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "LimitRangeList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "LimitRangeList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.LimitRangeSpec": { + "description": "LimitRangeSpec defines a min/max usage limit for resources that match on kind.", + "properties": { + "limits": { + "description": "Limits is the list of LimitRangeItem objects that are enforced.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.LimitRangeItem" + }, + "type": "array" + } + }, + "required": [ + "limits" + ], + "type": "object" + }, + "io.k8s.api.core.v1.LoadBalancerIngress": { + "description": "LoadBalancerIngress represents the status of a load-balancer ingress point: traffic intended for the service should be sent to an ingress point.", + "properties": { + "hostname": { + "description": "Hostname is set for load-balancer ingress points that are DNS based (typically AWS load-balancers)", + "type": "string" + }, + "ip": { + "description": "IP is set for load-balancer ingress points that are IP based (typically GCE or OpenStack load-balancers)", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.LoadBalancerStatus": { + "description": "LoadBalancerStatus represents the status of a load-balancer.", + "properties": { + "ingress": { + "description": "Ingress is a list containing ingress points for the load-balancer. Traffic intended for the service should be sent to these ingress points.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.LoadBalancerIngress" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.LocalObjectReference": { + "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.", + "properties": { + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.LocalVolumeSource": { + "description": "Local represents directly-attached storage with node affinity (Beta feature)", + "properties": { + "fsType": { + "description": "Filesystem type to mount. It applies only when the Path is a block device. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default value is to auto-select a fileystem if unspecified.", + "type": "string" + }, + "path": { + "description": "The full path to the volume on the node. It can be either a directory or block device (disk, partition, ...).", + "type": "string" + } + }, + "required": [ + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.NFSVolumeSource": { + "description": "Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do not support ownership management or SELinux relabeling.", + "properties": { + "path": { + "description": "Path that is exported by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + "type": "string" + }, + "readOnly": { + "description": "ReadOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + "type": "boolean" + }, + "server": { + "description": "Server is the hostname or IP address of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + "type": "string" + } + }, + "required": [ + "server", + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Namespace": { + "description": "Namespace provides a scope for Names. Use of multiple namespaces is optional.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Namespace" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.NamespaceSpec", + "description": "Spec defines the behavior of the Namespace. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.NamespaceStatus", + "description": "Status describes the current status of a Namespace. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Namespace", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.NamespaceList": { + "description": "NamespaceList is a list of Namespaces.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of Namespace objects in the list. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Namespace" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "NamespaceList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "NamespaceList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.NamespaceSpec": { + "description": "NamespaceSpec describes the attributes on a Namespace.", + "properties": { + "finalizers": { + "description": "Finalizers is an opaque list of values that must be empty to permanently remove object from storage. More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NamespaceStatus": { + "description": "NamespaceStatus is information about the current status of a Namespace.", + "properties": { + "phase": { + "description": "Phase is the current lifecycle phase of the namespace. More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Node": { + "description": "Node is a worker node in Kubernetes. Each node will have a unique identifier in the cache (i.e. in etcd).", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Node" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSpec", + "description": "Spec defines the behavior of a node. https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeStatus", + "description": "Most recently observed status of the node. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Node", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.NodeAddress": { + "description": "NodeAddress contains information for the node's address.", + "properties": { + "address": { + "description": "The node address.", + "type": "string" + }, + "type": { + "description": "Node address type, one of Hostname, ExternalIP or InternalIP.", + "type": "string" + } + }, + "required": [ + "type", + "address" + ], + "type": "object" + }, + "io.k8s.api.core.v1.NodeAffinity": { + "description": "Node affinity is a group of node affinity scheduling rules.", + "properties": { + "preferredDuringSchedulingIgnoredDuringExecution": { + "description": "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PreferredSchedulingTerm" + }, + "type": "array" + }, + "requiredDuringSchedulingIgnoredDuringExecution": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelector", + "description": "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to an update), the system may or may not try to eventually evict the pod from its node." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeCondition": { + "description": "NodeCondition contains condition information for a node.", + "properties": { + "lastHeartbeatTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time we got an update on a given condition." + }, + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transit from one status to another." + }, + "message": { + "description": "Human readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "(brief) reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of node condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.core.v1.NodeConfigSource": { + "description": "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.", + "properties": { + "configMap": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapNodeConfigSource", + "description": "ConfigMap is a reference to a Node's ConfigMap" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeConfigStatus": { + "description": "NodeConfigStatus describes the status of the config assigned by Node.Spec.ConfigSource.", + "properties": { + "active": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigSource", + "description": "Active reports the checkpointed config the node is actively using. Active will represent either the current version of the Assigned config, or the current LastKnownGood config, depending on whether attempting to use the Assigned config results in an error." + }, + "assigned": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigSource", + "description": "Assigned reports the checkpointed config the node will try to use. When Node.Spec.ConfigSource is updated, the node checkpoints the associated config payload to local disk, along with a record indicating intended config. The node refers to this record to choose its config checkpoint, and reports this record in Assigned. Assigned only updates in the status after the record has been checkpointed to disk. When the Kubelet is restarted, it tries to make the Assigned config the Active config by loading and validating the checkpointed payload identified by Assigned." + }, + "error": { + "description": "Error describes any problems reconciling the Spec.ConfigSource to the Active config. Errors may occur, for example, attempting to checkpoint Spec.ConfigSource to the local Assigned record, attempting to checkpoint the payload associated with Spec.ConfigSource, attempting to load or validate the Assigned config, etc. Errors may occur at different points while syncing config. Earlier errors (e.g. download or checkpointing errors) will not result in a rollback to LastKnownGood, and may resolve across Kubelet retries. Later errors (e.g. loading or validating a checkpointed config) will result in a rollback to LastKnownGood. In the latter case, it is usually possible to resolve the error by fixing the config assigned in Spec.ConfigSource. You can find additional information for debugging by searching the error message in the Kubelet log. Error is a human-readable description of the error state; machines can check whether or not Error is empty, but should not rely on the stability of the Error text across Kubelet versions.", + "type": "string" + }, + "lastKnownGood": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigSource", + "description": "LastKnownGood reports the checkpointed config the node will fall back to when it encounters an error attempting to use the Assigned config. The Assigned config becomes the LastKnownGood config when the node determines that the Assigned config is stable and correct. This is currently implemented as a 10-minute soak period starting when the local record of Assigned config is updated. If the Assigned config is Active at the end of this period, it becomes the LastKnownGood. Note that if Spec.ConfigSource is reset to nil (use local defaults), the LastKnownGood is also immediately reset to nil, because the local default config is always assumed good. You should not make assumptions about the node's method of determining config stability and correctness, as this may change or become configurable in the future." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeDaemonEndpoints": { + "description": "NodeDaemonEndpoints lists ports opened by daemons running on the Node.", + "properties": { + "kubeletEndpoint": { + "$ref": "#/definitions/io.k8s.api.core.v1.DaemonEndpoint", + "description": "Endpoint on which Kubelet is listening." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeList": { + "description": "NodeList is the whole list of all Nodes which have been registered with master.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of nodes", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Node" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "NodeList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "NodeList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.NodeSelector": { + "description": "A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.", + "properties": { + "nodeSelectorTerms": { + "description": "Required. A list of node selector terms. The terms are ORed.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelectorTerm" + }, + "type": "array" + } + }, + "required": [ + "nodeSelectorTerms" + ], + "type": "object" + }, + "io.k8s.api.core.v1.NodeSelectorRequirement": { + "description": "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "properties": { + "key": { + "description": "The label key that the selector applies to.", + "type": "string" + }, + "operator": { + "description": "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.", + "type": "string" + }, + "values": { + "description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "key", + "operator" + ], + "type": "object" + }, + "io.k8s.api.core.v1.NodeSelectorTerm": { + "description": "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.", + "properties": { + "matchExpressions": { + "description": "A list of node selector requirements by node's labels.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelectorRequirement" + }, + "type": "array" + }, + "matchFields": { + "description": "A list of node selector requirements by node's fields.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelectorRequirement" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeSpec": { + "description": "NodeSpec describes the attributes that a node is created with.", + "properties": { + "configSource": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigSource", + "description": "If specified, the source to get node configuration from The DynamicKubeletConfig feature gate must be enabled for the Kubelet to use this field" + }, + "externalID": { + "description": "Deprecated. Not all kubelets will set this field. Remove field after 1.13. see: https://issues.k8s.io/61966", + "type": "string" + }, + "podCIDR": { + "description": "PodCIDR represents the pod IP range assigned to the node.", + "type": "string" + }, + "providerID": { + "description": "ID of the node assigned by the cloud provider in the format: ://", + "type": "string" + }, + "taints": { + "description": "If specified, the node's taints.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Taint" + }, + "type": "array" + }, + "unschedulable": { + "description": "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: https://kubernetes.io/docs/concepts/nodes/node/#manual-node-administration", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeStatus": { + "description": "NodeStatus is information about the current status of a node.", + "properties": { + "addresses": { + "description": "List of addresses reachable to the node. Queried from cloud provider, if available. More info: https://kubernetes.io/docs/concepts/nodes/node/#addresses", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeAddress" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "allocatable": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Allocatable represents the resources of a node that are available for scheduling. Defaults to Capacity.", + "type": "object" + }, + "capacity": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Capacity represents the total resources of a node. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity", + "type": "object" + }, + "conditions": { + "description": "Conditions is an array of current observed node conditions. More info: https://kubernetes.io/docs/concepts/nodes/node/#condition", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "config": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigStatus", + "description": "Status of the config assigned to the node via the dynamic Kubelet config feature." + }, + "daemonEndpoints": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeDaemonEndpoints", + "description": "Endpoints of daemons running on the Node." + }, + "images": { + "description": "List of container images on this node", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerImage" + }, + "type": "array" + }, + "nodeInfo": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSystemInfo", + "description": "Set of ids/uuids to uniquely identify the node. More info: https://kubernetes.io/docs/concepts/nodes/node/#info" + }, + "phase": { + "description": "NodePhase is the recently observed lifecycle phase of the node. More info: https://kubernetes.io/docs/concepts/nodes/node/#phase The field is never populated, and now is deprecated.", + "type": "string" + }, + "volumesAttached": { + "description": "List of volumes that are attached to the node.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.AttachedVolume" + }, + "type": "array" + }, + "volumesInUse": { + "description": "List of attachable volumes in use (mounted) by the node.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeSystemInfo": { + "description": "NodeSystemInfo is a set of ids/uuids to uniquely identify the node.", + "properties": { + "architecture": { + "description": "The Architecture reported by the node", + "type": "string" + }, + "bootID": { + "description": "Boot ID reported by the node.", + "type": "string" + }, + "containerRuntimeVersion": { + "description": "ContainerRuntime Version reported by the node through runtime remote API (e.g. docker://1.5.0).", + "type": "string" + }, + "kernelVersion": { + "description": "Kernel Version reported by the node from 'uname -r' (e.g. 3.16.0-0.bpo.4-amd64).", + "type": "string" + }, + "kubeProxyVersion": { + "description": "KubeProxy Version reported by the node.", + "type": "string" + }, + "kubeletVersion": { + "description": "Kubelet Version reported by the node.", + "type": "string" + }, + "machineID": { + "description": "MachineID reported by the node. For unique machine identification in the cluster this field is preferred. Learn more from man(5) machine-id: http://man7.org/linux/man-pages/man5/machine-id.5.html", + "type": "string" + }, + "operatingSystem": { + "description": "The Operating System reported by the node", + "type": "string" + }, + "osImage": { + "description": "OS Image reported by the node from /etc/os-release (e.g. Debian GNU/Linux 7 (wheezy)).", + "type": "string" + }, + "systemUUID": { + "description": "SystemUUID reported by the node. For unique machine identification MachineID is preferred. This field is specific to Red Hat hosts https://access.redhat.com/documentation/en-US/Red_Hat_Subscription_Management/1/html/RHSM/getting-system-uuid.html", + "type": "string" + } + }, + "required": [ + "machineID", + "systemUUID", + "bootID", + "kernelVersion", + "osImage", + "containerRuntimeVersion", + "kubeletVersion", + "kubeProxyVersion", + "operatingSystem", + "architecture" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ObjectFieldSelector": { + "description": "ObjectFieldSelector selects an APIVersioned field of an object.", + "properties": { + "apiVersion": { + "description": "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".", + "type": "string" + }, + "fieldPath": { + "description": "Path of the field to select in the specified API version.", + "type": "string" + } + }, + "required": [ + "fieldPath" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ObjectReference": { + "description": "ObjectReference contains enough information to let you inspect or modify the referred object.", + "properties": { + "apiVersion": { + "description": "API version of the referent.", + "type": "string" + }, + "fieldPath": { + "description": "If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: \"spec.containers{name}\" (where \"name\" refers to the name of the container that triggered the event) or if no container name is specified \"spec.containers[2]\" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object.", + "type": "string" + }, + "kind": { + "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "namespace": { + "description": "Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/", + "type": "string" + }, + "resourceVersion": { + "description": "Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "uid": { + "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PersistentVolume": { + "description": "PersistentVolume (PV) is a storage resource provisioned by an administrator. It is analogous to a node. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PersistentVolume" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeSpec", + "description": "Spec defines a specification of a persistent volume owned by the cluster. Provisioned by an administrator. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeStatus", + "description": "Status represents the current information/status for the persistent volume. Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PersistentVolume", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PersistentVolumeClaim": { + "description": "PersistentVolumeClaim is a user's request for and claim to a persistent volume", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PersistentVolumeClaim" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaimSpec", + "description": "Spec defines the desired characteristics of a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaimStatus", + "description": "Status represents the current information/status of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PersistentVolumeClaim", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PersistentVolumeClaimCondition": { + "description": "PersistentVolumeClaimCondition contails details about state of pvc", + "properties": { + "lastProbeTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time we probed the condition." + }, + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "Human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \"ResizeStarted\" that means the underlying persistent volume is being resized.", + "type": "string" + }, + "status": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PersistentVolumeClaimList": { + "description": "PersistentVolumeClaimList is a list of PersistentVolumeClaim items.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "A list of persistent volume claims. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaim" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PersistentVolumeClaimList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PersistentVolumeClaimList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PersistentVolumeClaimSpec": { + "description": "PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes", + "properties": { + "accessModes": { + "description": "AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1", + "items": { + "type": "string" + }, + "type": "array" + }, + "dataSource": { + "$ref": "#/definitions/io.k8s.api.core.v1.TypedLocalObjectReference", + "description": "This field requires the VolumeSnapshotDataSource alpha feature gate to be enabled and currently VolumeSnapshot is the only supported data source. If the provisioner can support VolumeSnapshot data source, it will create a new volume and data will be restored to the volume at the same time. If the provisioner does not support VolumeSnapshot data source, volume will not be created and the failure will be reported as an event. In the future, we plan to support more data source types and the behavior of the provisioner may change." + }, + "resources": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements", + "description": "Resources represents the minimum resources the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "A label query over volumes to consider for binding." + }, + "storageClassName": { + "description": "Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1", + "type": "string" + }, + "volumeMode": { + "description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.", + "type": "string" + }, + "volumeName": { + "description": "VolumeName is the binding reference to the PersistentVolume backing this claim.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PersistentVolumeClaimStatus": { + "description": "PersistentVolumeClaimStatus is the current status of a persistent volume claim.", + "properties": { + "accessModes": { + "description": "AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1", + "items": { + "type": "string" + }, + "type": "array" + }, + "capacity": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Represents the actual resources of the underlying volume.", + "type": "object" + }, + "conditions": { + "description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaimCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "phase": { + "description": "Phase represents the current phase of PersistentVolumeClaim.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PersistentVolumeClaimVolumeSource": { + "description": "PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace. This volume finds the bound PV and mounts that volume for the pod. A PersistentVolumeClaimVolumeSource is, essentially, a wrapper around another type of volume that is owned by someone else (the system).", + "properties": { + "claimName": { + "description": "ClaimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + "type": "string" + }, + "readOnly": { + "description": "Will force the ReadOnly setting in VolumeMounts. Default false.", + "type": "boolean" + } + }, + "required": [ + "claimName" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PersistentVolumeList": { + "description": "PersistentVolumeList is a list of PersistentVolume items.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of persistent volumes. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolume" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PersistentVolumeList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PersistentVolumeList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PersistentVolumeSpec": { + "description": "PersistentVolumeSpec is the specification of a persistent volume.", + "properties": { + "accessModes": { + "description": "AccessModes contains all ways the volume can be mounted. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes", + "items": { + "type": "string" + }, + "type": "array" + }, + "awsElasticBlockStore": { + "$ref": "#/definitions/io.k8s.api.core.v1.AWSElasticBlockStoreVolumeSource", + "description": "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore" + }, + "azureDisk": { + "$ref": "#/definitions/io.k8s.api.core.v1.AzureDiskVolumeSource", + "description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod." + }, + "azureFile": { + "$ref": "#/definitions/io.k8s.api.core.v1.AzureFilePersistentVolumeSource", + "description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod." + }, + "capacity": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "A description of the persistent volume's resources and capacity. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity", + "type": "object" + }, + "cephfs": { + "$ref": "#/definitions/io.k8s.api.core.v1.CephFSPersistentVolumeSource", + "description": "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime" + }, + "cinder": { + "$ref": "#/definitions/io.k8s.api.core.v1.CinderPersistentVolumeSource", + "description": "Cinder represents a cinder volume attached and mounted on kubelets host machine More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md" + }, + "claimRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "ClaimRef is part of a bi-directional binding between PersistentVolume and PersistentVolumeClaim. Expected to be non-nil when bound. claim.VolumeName is the authoritative bind between PV and PVC. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#binding" + }, + "csi": { + "$ref": "#/definitions/io.k8s.api.core.v1.CSIPersistentVolumeSource", + "description": "CSI represents storage that is handled by an external CSI driver (Beta feature)." + }, + "fc": { + "$ref": "#/definitions/io.k8s.api.core.v1.FCVolumeSource", + "description": "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod." + }, + "flexVolume": { + "$ref": "#/definitions/io.k8s.api.core.v1.FlexPersistentVolumeSource", + "description": "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin." + }, + "flocker": { + "$ref": "#/definitions/io.k8s.api.core.v1.FlockerVolumeSource", + "description": "Flocker represents a Flocker volume attached to a kubelet's host machine and exposed to the pod for its usage. This depends on the Flocker control service being running" + }, + "gcePersistentDisk": { + "$ref": "#/definitions/io.k8s.api.core.v1.GCEPersistentDiskVolumeSource", + "description": "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk" + }, + "glusterfs": { + "$ref": "#/definitions/io.k8s.api.core.v1.GlusterfsPersistentVolumeSource", + "description": "Glusterfs represents a Glusterfs volume that is attached to a host and exposed to the pod. Provisioned by an admin. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md" + }, + "hostPath": { + "$ref": "#/definitions/io.k8s.api.core.v1.HostPathVolumeSource", + "description": "HostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath" + }, + "iscsi": { + "$ref": "#/definitions/io.k8s.api.core.v1.ISCSIPersistentVolumeSource", + "description": "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin." + }, + "local": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalVolumeSource", + "description": "Local represents directly-attached storage with node affinity" + }, + "mountOptions": { + "description": "A list of mount options, e.g. [\"ro\", \"soft\"]. Not validated - mount will simply fail if one is invalid. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#mount-options", + "items": { + "type": "string" + }, + "type": "array" + }, + "nfs": { + "$ref": "#/definitions/io.k8s.api.core.v1.NFSVolumeSource", + "description": "NFS represents an NFS mount on the host. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs" + }, + "nodeAffinity": { + "$ref": "#/definitions/io.k8s.api.core.v1.VolumeNodeAffinity", + "description": "NodeAffinity defines constraints that limit what nodes this volume can be accessed from. This field influences the scheduling of pods that use this volume." + }, + "persistentVolumeReclaimPolicy": { + "description": "What happens to a persistent volume when released from its claim. Valid options are Retain (default for manually created PersistentVolumes), Delete (default for dynamically provisioned PersistentVolumes), and Recycle (deprecated). Recycle must be supported by the volume plugin underlying this PersistentVolume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#reclaiming", + "type": "string" + }, + "photonPersistentDisk": { + "$ref": "#/definitions/io.k8s.api.core.v1.PhotonPersistentDiskVolumeSource", + "description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine" + }, + "portworxVolume": { + "$ref": "#/definitions/io.k8s.api.core.v1.PortworxVolumeSource", + "description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine" + }, + "quobyte": { + "$ref": "#/definitions/io.k8s.api.core.v1.QuobyteVolumeSource", + "description": "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime" + }, + "rbd": { + "$ref": "#/definitions/io.k8s.api.core.v1.RBDPersistentVolumeSource", + "description": "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md" + }, + "scaleIO": { + "$ref": "#/definitions/io.k8s.api.core.v1.ScaleIOPersistentVolumeSource", + "description": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes." + }, + "storageClassName": { + "description": "Name of StorageClass to which this persistent volume belongs. Empty value means that this volume does not belong to any StorageClass.", + "type": "string" + }, + "storageos": { + "$ref": "#/definitions/io.k8s.api.core.v1.StorageOSPersistentVolumeSource", + "description": "StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md" + }, + "volumeMode": { + "description": "volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec. This is a beta feature.", + "type": "string" + }, + "vsphereVolume": { + "$ref": "#/definitions/io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource", + "description": "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PersistentVolumeStatus": { + "description": "PersistentVolumeStatus is the current status of a persistent volume.", + "properties": { + "message": { + "description": "A human-readable message indicating details about why the volume is in this state.", + "type": "string" + }, + "phase": { + "description": "Phase indicates if a volume is available, bound to a claim, or released by a claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#phase", + "type": "string" + }, + "reason": { + "description": "Reason is a brief CamelCase string that describes any failure and is meant for machine parsing and tidy display in the CLI.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PhotonPersistentDiskVolumeSource": { + "description": "Represents a Photon Controller persistent disk resource.", + "properties": { + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "pdID": { + "description": "ID that identifies Photon Controller persistent disk", + "type": "string" + } + }, + "required": [ + "pdID" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Pod": { + "description": "Pod is a collection of containers that can run on a host. This resource is created by clients and scheduled onto hosts.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Pod" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodSpec", + "description": "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodStatus", + "description": "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Pod", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PodAffinity": { + "description": "Pod affinity is a group of inter pod affinity scheduling rules.", + "properties": { + "preferredDuringSchedulingIgnoredDuringExecution": { + "description": "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.WeightedPodAffinityTerm" + }, + "type": "array" + }, + "requiredDuringSchedulingIgnoredDuringExecution": { + "description": "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodAffinityTerm" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodAffinityTerm": { + "description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running", + "properties": { + "labelSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "A label query over a set of resources, in this case pods." + }, + "namespaces": { + "description": "namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \"this pod's namespace\"", + "items": { + "type": "string" + }, + "type": "array" + }, + "topologyKey": { + "description": "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.", + "type": "string" + } + }, + "required": [ + "topologyKey" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PodAntiAffinity": { + "description": "Pod anti affinity is a group of inter pod anti affinity scheduling rules.", + "properties": { + "preferredDuringSchedulingIgnoredDuringExecution": { + "description": "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.WeightedPodAffinityTerm" + }, + "type": "array" + }, + "requiredDuringSchedulingIgnoredDuringExecution": { + "description": "If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodAffinityTerm" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodCondition": { + "description": "PodCondition contains details for the current condition of this pod.", + "properties": { + "lastProbeTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time we probed the condition." + }, + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "Human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "Unique, one-word, CamelCase reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status is the status of the condition. Can be True, False, Unknown. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", + "type": "string" + }, + "type": { + "description": "Type is the type of the condition. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PodDNSConfig": { + "description": "PodDNSConfig defines the DNS parameters of a pod in addition to those generated from DNSPolicy.", + "properties": { + "nameservers": { + "description": "A list of DNS name server IP addresses. This will be appended to the base nameservers generated from DNSPolicy. Duplicated nameservers will be removed.", + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "description": "A list of DNS resolver options. This will be merged with the base options generated from DNSPolicy. Duplicated entries will be removed. Resolution options given in Options will override those that appear in the base DNSPolicy.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodDNSConfigOption" + }, + "type": "array" + }, + "searches": { + "description": "A list of DNS search domains for host-name lookup. This will be appended to the base search paths generated from DNSPolicy. Duplicated search paths will be removed.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodDNSConfigOption": { + "description": "PodDNSConfigOption defines DNS resolver options of a pod.", + "properties": { + "name": { + "description": "Required.", + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodList": { + "description": "PodList is a list of Pods.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of pods. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Pod" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PodList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PodReadinessGate": { + "description": "PodReadinessGate contains the reference to a pod condition", + "properties": { + "conditionType": { + "description": "ConditionType refers to a condition in the pod's condition list with matching type.", + "type": "string" + } + }, + "required": [ + "conditionType" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PodSecurityContext": { + "description": "PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.", + "properties": { + "fsGroup": { + "description": "A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\n\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\n\nIf unset, the Kubelet will not modify the ownership and permissions of any volume.", + "format": "int64", + "type": "integer" + }, + "runAsGroup": { + "description": "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.", + "format": "int64", + "type": "integer" + }, + "runAsNonRoot": { + "description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + "type": "boolean" + }, + "runAsUser": { + "description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.", + "format": "int64", + "type": "integer" + }, + "seLinuxOptions": { + "$ref": "#/definitions/io.k8s.api.core.v1.SELinuxOptions", + "description": "The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container." + }, + "supplementalGroups": { + "description": "A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.", + "items": { + "format": "int64", + "type": "integer" + }, + "type": "array" + }, + "sysctls": { + "description": "Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Sysctl" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodSpec": { + "description": "PodSpec is a description of a pod.", + "properties": { + "activeDeadlineSeconds": { + "description": "Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer.", + "format": "int64", + "type": "integer" + }, + "affinity": { + "$ref": "#/definitions/io.k8s.api.core.v1.Affinity", + "description": "If specified, the pod's scheduling constraints" + }, + "automountServiceAccountToken": { + "description": "AutomountServiceAccountToken indicates whether a service account token should be automatically mounted.", + "type": "boolean" + }, + "containers": { + "description": "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Container" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "dnsConfig": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodDNSConfig", + "description": "Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy." + }, + "dnsPolicy": { + "description": "Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'.", + "type": "string" + }, + "enableServiceLinks": { + "description": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.", + "type": "boolean" + }, + "hostAliases": { + "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.HostAlias" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "ip", + "x-kubernetes-patch-strategy": "merge" + }, + "hostIPC": { + "description": "Use the host's ipc namespace. Optional: Default to false.", + "type": "boolean" + }, + "hostNetwork": { + "description": "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.", + "type": "boolean" + }, + "hostPID": { + "description": "Use the host's pid namespace. Optional: Default to false.", + "type": "boolean" + }, + "hostname": { + "description": "Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value.", + "type": "string" + }, + "imagePullSecrets": { + "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "initContainers": { + "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, or Liveness probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Container" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "nodeName": { + "description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + "type": "string" + }, + "nodeSelector": { + "additionalProperties": { + "type": "string" + }, + "description": "NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/", + "type": "object" + }, + "priority": { + "description": "The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority.", + "format": "int32", + "type": "integer" + }, + "priorityClassName": { + "description": "If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default.", + "type": "string" + }, + "readinessGates": { + "description": "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodReadinessGate" + }, + "type": "array" + }, + "restartPolicy": { + "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + "type": "string" + }, + "runtimeClassName": { + "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md This is an alpha feature and may change in the future.", + "type": "string" + }, + "schedulerName": { + "description": "If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler.", + "type": "string" + }, + "securityContext": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodSecurityContext", + "description": "SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field." + }, + "serviceAccount": { + "description": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + "type": "string" + }, + "serviceAccountName": { + "description": "ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/", + "type": "string" + }, + "shareProcessNamespace": { + "description": "Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false. This field is beta-level and may be disabled with the PodShareProcessNamespace feature.", + "type": "boolean" + }, + "subdomain": { + "description": "If specified, the fully qualified Pod hostname will be \"...svc.\". If not specified, the pod will not have a domainname at all.", + "type": "string" + }, + "terminationGracePeriodSeconds": { + "description": "Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds.", + "format": "int64", + "type": "integer" + }, + "tolerations": { + "description": "If specified, the pod's tolerations.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Toleration" + }, + "type": "array" + }, + "volumes": { + "description": "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Volume" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge,retainKeys" + } + }, + "required": [ + "containers" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PodStatus": { + "description": "PodStatus represents information about the status of a pod. Status may trail the actual state of a system, especially if the node that hosts the pod cannot contact the control plane.", + "properties": { + "conditions": { + "description": "Current service state of pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "containerStatuses": { + "description": "The list has one entry per container in the manifest. Each entry is currently the output of `docker inspect`. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerStatus" + }, + "type": "array" + }, + "hostIP": { + "description": "IP address of the host to which the pod is assigned. Empty if not yet scheduled.", + "type": "string" + }, + "initContainerStatuses": { + "description": "The list has one entry per init container in the manifest. The most recent successful init container will have ready = true, the most recently started container will have startTime set. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerStatus" + }, + "type": "array" + }, + "message": { + "description": "A human readable message indicating details about why the pod is in this condition.", + "type": "string" + }, + "nominatedNodeName": { + "description": "nominatedNodeName is set only when this pod preempts other pods on the node, but it cannot be scheduled right away as preemption victims receive their graceful termination periods. This field does not guarantee that the pod will be scheduled on this node. Scheduler may decide to place the pod elsewhere if other nodes become available sooner. Scheduler may also decide to give the resources on this node to a higher priority pod that is created after preemption. As a result, this field may be different than PodSpec.nodeName when the pod is scheduled.", + "type": "string" + }, + "phase": { + "description": "The phase of a Pod is a simple, high-level summary of where the Pod is in its lifecycle. The conditions array, the reason and message fields, and the individual container status arrays contain more detail about the pod's status. There are five possible phase values:\n\nPending: The pod has been accepted by the Kubernetes system, but one or more of the container images has not been created. This includes time before being scheduled as well as time spent downloading images over the network, which could take a while. Running: The pod has been bound to a node, and all of the containers have been created. At least one container is still running, or is in the process of starting or restarting. Succeeded: All containers in the pod have terminated in success, and will not be restarted. Failed: All containers in the pod have terminated, and at least one container has terminated in failure. The container either exited with non-zero status or was terminated by the system. Unknown: For some reason the state of the pod could not be obtained, typically due to an error in communicating with the host of the pod.\n\nMore info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-phase", + "type": "string" + }, + "podIP": { + "description": "IP address allocated to the pod. Routable at least within the cluster. Empty if not yet allocated.", + "type": "string" + }, + "qosClass": { + "description": "The Quality of Service (QOS) classification assigned to the pod based on resource requirements See PodQOSClass type for available QOS classes More info: https://git.k8s.io/community/contributors/design-proposals/node/resource-qos.md", + "type": "string" + }, + "reason": { + "description": "A brief CamelCase message indicating details about why the pod is in this state. e.g. 'Evicted'", + "type": "string" + }, + "startTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "RFC 3339 date and time at which the object was acknowledged by the Kubelet. This is before the Kubelet pulled the container image(s) for the pod." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodTemplate": { + "description": "PodTemplate describes a template for creating copies of a predefined pod.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodTemplate" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template defines the pods that will be created from this pod template. https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PodTemplate", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PodTemplateList": { + "description": "PodTemplateList is a list of PodTemplates.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of pod templates", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplate" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodTemplateList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PodTemplateList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PodTemplateSpec": { + "description": "PodTemplateSpec describes the data a pod should have when created from a template", + "properties": { + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodSpec", + "description": "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PortworxVolumeSource": { + "description": "PortworxVolumeSource represents a Portworx volume resource.", + "properties": { + "fsType": { + "description": "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "readOnly": { + "description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "volumeID": { + "description": "VolumeID uniquely identifies a Portworx volume", + "type": "string" + } + }, + "required": [ + "volumeID" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PreferredSchedulingTerm": { + "description": "An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).", + "properties": { + "preference": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelectorTerm", + "description": "A node selector term, associated with the corresponding weight." + }, + "weight": { + "description": "Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "weight", + "preference" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Probe": { + "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.", + "properties": { + "exec": { + "$ref": "#/definitions/io.k8s.api.core.v1.ExecAction", + "description": "One and only one of the following should be specified. Exec specifies the action to take." + }, + "failureThreshold": { + "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.", + "format": "int32", + "type": "integer" + }, + "httpGet": { + "$ref": "#/definitions/io.k8s.api.core.v1.HTTPGetAction", + "description": "HTTPGet specifies the http request to perform." + }, + "initialDelaySeconds": { + "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "format": "int32", + "type": "integer" + }, + "periodSeconds": { + "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.", + "format": "int32", + "type": "integer" + }, + "successThreshold": { + "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.", + "format": "int32", + "type": "integer" + }, + "tcpSocket": { + "$ref": "#/definitions/io.k8s.api.core.v1.TCPSocketAction", + "description": "TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported" + }, + "timeoutSeconds": { + "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ProjectedVolumeSource": { + "description": "Represents a projected volume source", + "properties": { + "defaultMode": { + "description": "Mode bits to use on created files by default. Must be a value between 0 and 0777. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + "format": "int32", + "type": "integer" + }, + "sources": { + "description": "list of volume projections", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.VolumeProjection" + }, + "type": "array" + } + }, + "required": [ + "sources" + ], + "type": "object" + }, + "io.k8s.api.core.v1.QuobyteVolumeSource": { + "description": "Represents a Quobyte mount that lasts the lifetime of a pod. Quobyte volumes do not support ownership management or SELinux relabeling.", + "properties": { + "group": { + "description": "Group to map volume access to Default is no group", + "type": "string" + }, + "readOnly": { + "description": "ReadOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false.", + "type": "boolean" + }, + "registry": { + "description": "Registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes", + "type": "string" + }, + "tenant": { + "description": "Tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin", + "type": "string" + }, + "user": { + "description": "User to map volume access to Defaults to serivceaccount user", + "type": "string" + }, + "volume": { + "description": "Volume is a string that references an already created Quobyte volume by name.", + "type": "string" + } + }, + "required": [ + "registry", + "volume" + ], + "type": "object" + }, + "io.k8s.api.core.v1.RBDPersistentVolumeSource": { + "description": "Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd", + "type": "string" + }, + "image": { + "description": "The rados image name. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "type": "string" + }, + "keyring": { + "description": "Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "type": "string" + }, + "monitors": { + "description": "A collection of Ceph monitors. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "items": { + "type": "string" + }, + "type": "array" + }, + "pool": { + "description": "The rados pool name. Default is rbd. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "type": "string" + }, + "readOnly": { + "description": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it" + }, + "user": { + "description": "The rados user name. Default is admin. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "type": "string" + } + }, + "required": [ + "monitors", + "image" + ], + "type": "object" + }, + "io.k8s.api.core.v1.RBDVolumeSource": { + "description": "Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd", + "type": "string" + }, + "image": { + "description": "The rados image name. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "type": "string" + }, + "keyring": { + "description": "Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "type": "string" + }, + "monitors": { + "description": "A collection of Ceph monitors. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "items": { + "type": "string" + }, + "type": "array" + }, + "pool": { + "description": "The rados pool name. Default is rbd. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "type": "string" + }, + "readOnly": { + "description": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it" + }, + "user": { + "description": "The rados user name. Default is admin. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "type": "string" + } + }, + "required": [ + "monitors", + "image" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ReplicationController": { + "description": "ReplicationController represents the configuration of a replication controller.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ReplicationController" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "If the Labels of a ReplicationController are empty, they are defaulted to be the same as the Pod(s) that the replication controller manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.ReplicationControllerSpec", + "description": "Spec defines the specification of the desired behavior of the replication controller. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.ReplicationControllerStatus", + "description": "Status is the most recently observed status of the replication controller. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ReplicationController", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ReplicationControllerCondition": { + "description": "ReplicationControllerCondition describes the state of a replication controller at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of replication controller condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ReplicationControllerList": { + "description": "ReplicationControllerList is a collection of replication controllers.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of replication controllers. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ReplicationController" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ReplicationControllerList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ReplicationControllerList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ReplicationControllerSpec": { + "description": "ReplicationControllerSpec is the specification of a replication controller.", + "properties": { + "minReadySeconds": { + "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller", + "format": "int32", + "type": "integer" + }, + "selector": { + "additionalProperties": { + "type": "string" + }, + "description": "Selector is a label query over pods that should match the Replicas count. If Selector is empty, it is defaulted to the labels present on the Pod template. Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", + "type": "object" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template is the object that describes the pod that will be created if insufficient replicas are detected. This takes precedence over a TemplateRef. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ReplicationControllerStatus": { + "description": "ReplicationControllerStatus represents the current status of a replication controller.", + "properties": { + "availableReplicas": { + "description": "The number of available replicas (ready for at least minReadySeconds) for this replication controller.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a replication controller's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ReplicationControllerCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "fullyLabeledReplicas": { + "description": "The number of pods that have labels matching the labels of the pod template of the replication controller.", + "format": "int32", + "type": "integer" + }, + "observedGeneration": { + "description": "ObservedGeneration reflects the generation of the most recently observed replication controller.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "The number of ready replicas for this replication controller.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Replicas is the most recently oberved number of replicas. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ResourceFieldSelector": { + "description": "ResourceFieldSelector represents container resources (cpu, memory) and their output format", + "properties": { + "containerName": { + "description": "Container name: required for volumes, optional for env vars", + "type": "string" + }, + "divisor": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "Specifies the output format of the exposed resources, defaults to \"1\"" + }, + "resource": { + "description": "Required: resource to select", + "type": "string" + } + }, + "required": [ + "resource" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ResourceQuota": { + "description": "ResourceQuota sets aggregate quota restrictions enforced per namespace", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ResourceQuota" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceQuotaSpec", + "description": "Spec defines the desired quota. https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceQuotaStatus", + "description": "Status defines the actual enforced quota and its current usage. https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ResourceQuota", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ResourceQuotaList": { + "description": "ResourceQuotaList is a list of ResourceQuota items.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of ResourceQuota objects. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceQuota" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ResourceQuotaList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ResourceQuotaList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ResourceQuotaSpec": { + "description": "ResourceQuotaSpec defines the desired hard limits to enforce for Quota.", + "properties": { + "hard": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "hard is the set of desired hard limits for each named resource. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", + "type": "object" + }, + "scopeSelector": { + "$ref": "#/definitions/io.k8s.api.core.v1.ScopeSelector", + "description": "scopeSelector is also a collection of filters like scopes that must match each object tracked by a quota but expressed using ScopeSelectorOperator in combination with possible values. For a resource to match, both scopes AND scopeSelector (if specified in spec), must be matched." + }, + "scopes": { + "description": "A collection of filters that must match each object tracked by a quota. If not specified, the quota matches all objects.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ResourceQuotaStatus": { + "description": "ResourceQuotaStatus defines the enforced hard limits and observed use.", + "properties": { + "hard": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Hard is the set of enforced hard limits for each named resource. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", + "type": "object" + }, + "used": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Used is the current observed total usage of the resource in the namespace.", + "type": "object" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ResourceRequirements": { + "description": "ResourceRequirements describes the compute resource requirements.", + "properties": { + "limits": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + "type": "object" + }, + "requests": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + "type": "object" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.SELinuxOptions": { + "description": "SELinuxOptions are the labels to be applied to the container", + "properties": { + "level": { + "description": "Level is SELinux level label that applies to the container.", + "type": "string" + }, + "role": { + "description": "Role is a SELinux role label that applies to the container.", + "type": "string" + }, + "type": { + "description": "Type is a SELinux type label that applies to the container.", + "type": "string" + }, + "user": { + "description": "User is a SELinux user label that applies to the container.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ScaleIOPersistentVolumeSource": { + "description": "ScaleIOPersistentVolumeSource represents a persistent ScaleIO volume", + "properties": { + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Default is \"xfs\"", + "type": "string" + }, + "gateway": { + "description": "The host address of the ScaleIO API Gateway.", + "type": "string" + }, + "protectionDomain": { + "description": "The name of the ScaleIO Protection Domain for the configured storage.", + "type": "string" + }, + "readOnly": { + "description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail." + }, + "sslEnabled": { + "description": "Flag to enable/disable SSL communication with Gateway, default false", + "type": "boolean" + }, + "storageMode": { + "description": "Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.", + "type": "string" + }, + "storagePool": { + "description": "The ScaleIO Storage Pool associated with the protection domain.", + "type": "string" + }, + "system": { + "description": "The name of the storage system as configured in ScaleIO.", + "type": "string" + }, + "volumeName": { + "description": "The name of a volume already created in the ScaleIO system that is associated with this volume source.", + "type": "string" + } + }, + "required": [ + "gateway", + "system", + "secretRef" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ScaleIOVolumeSource": { + "description": "ScaleIOVolumeSource represents a persistent ScaleIO volume", + "properties": { + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Default is \"xfs\".", + "type": "string" + }, + "gateway": { + "description": "The host address of the ScaleIO API Gateway.", + "type": "string" + }, + "protectionDomain": { + "description": "The name of the ScaleIO Protection Domain for the configured storage.", + "type": "string" + }, + "readOnly": { + "description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail." + }, + "sslEnabled": { + "description": "Flag to enable/disable SSL communication with Gateway, default false", + "type": "boolean" + }, + "storageMode": { + "description": "Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.", + "type": "string" + }, + "storagePool": { + "description": "The ScaleIO Storage Pool associated with the protection domain.", + "type": "string" + }, + "system": { + "description": "The name of the storage system as configured in ScaleIO.", + "type": "string" + }, + "volumeName": { + "description": "The name of a volume already created in the ScaleIO system that is associated with this volume source.", + "type": "string" + } + }, + "required": [ + "gateway", + "system", + "secretRef" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ScopeSelector": { + "description": "A scope selector represents the AND of the selectors represented by the scoped-resource selector requirements.", + "properties": { + "matchExpressions": { + "description": "A list of scope selector requirements by scope of the resources.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ScopedResourceSelectorRequirement" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ScopedResourceSelectorRequirement": { + "description": "A scoped-resource selector requirement is a selector that contains values, a scope name, and an operator that relates the scope name and values.", + "properties": { + "operator": { + "description": "Represents a scope's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist.", + "type": "string" + }, + "scopeName": { + "description": "The name of the scope that the selector applies to.", + "type": "string" + }, + "values": { + "description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "scopeName", + "operator" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Secret": { + "description": "Secret holds secret data of a certain type. The total bytes of the values in the Data field must be less than MaxSecretSize bytes.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "data": { + "additionalProperties": { + "format": "byte", + "type": "string" + }, + "description": "Data contains the secret data. Each key must consist of alphanumeric characters, '-', '_' or '.'. The serialized form of the secret data is a base64 encoded string, representing the arbitrary (possibly non-string) data value here. Described in https://tools.ietf.org/html/rfc4648#section-4", + "type": "object" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Secret" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "stringData": { + "additionalProperties": { + "type": "string" + }, + "description": "stringData allows specifying non-binary secret data in string form. It is provided as a write-only convenience method. All keys and values are merged into the data field on write, overwriting any existing values. It is never output when reading from the API.", + "type": "object" + }, + "type": { + "description": "Used to facilitate programmatic handling of secret data.", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Secret", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.SecretEnvSource": { + "description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.", + "properties": { + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "Specify whether the Secret must be defined", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.SecretKeySelector": { + "description": "SecretKeySelector selects a key of a Secret.", + "properties": { + "key": { + "description": "The key of the secret to select from. Must be a valid secret key.", + "type": "string" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "Specify whether the Secret or it's key must be defined", + "type": "boolean" + } + }, + "required": [ + "key" + ], + "type": "object" + }, + "io.k8s.api.core.v1.SecretList": { + "description": "SecretList is a list of Secret.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of secret objects. More info: https://kubernetes.io/docs/concepts/configuration/secret", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Secret" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SecretList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "SecretList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.SecretProjection": { + "description": "Adapts a secret into a projected volume.\n\nThe contents of the target Secret's Data field will be presented in a projected volume as files using the keys in the Data field as the file names. Note that this is identical to a secret volume source without the default mode.", + "properties": { + "items": { + "description": "If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.KeyToPath" + }, + "type": "array" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "Specify whether the Secret or its key must be defined", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.SecretReference": { + "description": "SecretReference represents a Secret Reference. It has enough information to retrieve secret in any namespace", + "properties": { + "name": { + "description": "Name is unique within a namespace to reference a secret resource.", + "type": "string" + }, + "namespace": { + "description": "Namespace defines the space within which the secret name must be unique.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.SecretVolumeSource": { + "description": "Adapts a Secret into a volume.\n\nThe contents of the target Secret's Data field will be presented in a volume as files using the keys in the Data field as the file names. Secret volumes support ownership management and SELinux relabeling.", + "properties": { + "defaultMode": { + "description": "Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + "format": "int32", + "type": "integer" + }, + "items": { + "description": "If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.KeyToPath" + }, + "type": "array" + }, + "optional": { + "description": "Specify whether the Secret or it's keys must be defined", + "type": "boolean" + }, + "secretName": { + "description": "Name of the secret in the pod's namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.SecurityContext": { + "description": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.", + "properties": { + "allowPrivilegeEscalation": { + "description": "AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN", + "type": "boolean" + }, + "capabilities": { + "$ref": "#/definitions/io.k8s.api.core.v1.Capabilities", + "description": "The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime." + }, + "privileged": { + "description": "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.", + "type": "boolean" + }, + "procMount": { + "description": "procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.", + "type": "string" + }, + "readOnlyRootFilesystem": { + "description": "Whether this container has a read-only root filesystem. Default is false.", + "type": "boolean" + }, + "runAsGroup": { + "description": "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + "format": "int64", + "type": "integer" + }, + "runAsNonRoot": { + "description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + "type": "boolean" + }, + "runAsUser": { + "description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + "format": "int64", + "type": "integer" + }, + "seLinuxOptions": { + "$ref": "#/definitions/io.k8s.api.core.v1.SELinuxOptions", + "description": "The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Service": { + "description": "Service is a named abstraction of software service (for example, mysql) consisting of local port (for example 3306) that the proxy listens on, and the selector that determines which pods will answer requests sent through the proxy.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Service" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.ServiceSpec", + "description": "Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.ServiceStatus", + "description": "Most recently observed status of the service. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Service", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ServiceAccount": { + "description": "ServiceAccount binds together: * a name, understood by users, and perhaps by peripheral systems, for an identity * a principal that can be authenticated and authorized * a set of secrets", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "automountServiceAccountToken": { + "description": "AutomountServiceAccountToken indicates whether pods running as this service account should have an API token automatically mounted. Can be overridden at the pod level.", + "type": "boolean" + }, + "imagePullSecrets": { + "description": "ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this ServiceAccount. ImagePullSecrets are distinct from Secrets because Secrets can be mounted in the pod, but ImagePullSecrets are only accessed by the kubelet. More info: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ServiceAccount" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "secrets": { + "description": "Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount. More info: https://kubernetes.io/docs/concepts/configuration/secret", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ServiceAccount", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ServiceAccountList": { + "description": "ServiceAccountList is a list of ServiceAccount objects", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ServiceAccounts. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ServiceAccount" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ServiceAccountList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ServiceAccountList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ServiceAccountTokenProjection": { + "description": "ServiceAccountTokenProjection represents a projected service account token volume. This projection can be used to insert a service account token into the pods runtime filesystem for use against APIs (Kubernetes API Server or otherwise).", + "properties": { + "audience": { + "description": "Audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token. The audience defaults to the identifier of the apiserver.", + "type": "string" + }, + "expirationSeconds": { + "description": "ExpirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet will start trying to rotate the token if the token is older than 80 percent of its time to live or if the token is older than 24 hours.Defaults to 1 hour and must be at least 10 minutes.", + "format": "int64", + "type": "integer" + }, + "path": { + "description": "Path is the path relative to the mount point of the file to project the token into.", + "type": "string" + } + }, + "required": [ + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ServiceList": { + "description": "ServiceList holds a list of services.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of services", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Service" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ServiceList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ServiceList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ServicePort": { + "description": "ServicePort contains information on service's port.", + "properties": { + "name": { + "description": "The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. This maps to the 'Name' field in EndpointPort objects. Optional if only one ServicePort is defined on this service.", + "type": "string" + }, + "nodePort": { + "description": "The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport", + "format": "int32", + "type": "integer" + }, + "port": { + "description": "The port that will be exposed by this service.", + "format": "int32", + "type": "integer" + }, + "protocol": { + "description": "The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\". Default is TCP.", + "type": "string" + }, + "targetPort": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service" + } + }, + "required": [ + "port" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ServiceSpec": { + "description": "ServiceSpec describes the attributes that a user creates on a service.", + "properties": { + "clusterIP": { + "description": "clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are \"None\", empty string (\"\"), or a valid IP address. \"None\" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + "type": "string" + }, + "externalIPs": { + "description": "externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system.", + "items": { + "type": "string" + }, + "type": "array" + }, + "externalName": { + "description": "externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName.", + "type": "string" + }, + "externalTrafficPolicy": { + "description": "externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \"Local\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \"Cluster\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.", + "type": "string" + }, + "healthCheckNodePort": { + "description": "healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local.", + "format": "int32", + "type": "integer" + }, + "loadBalancerIP": { + "description": "Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.", + "type": "string" + }, + "loadBalancerSourceRanges": { + "description": "If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature.\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/", + "items": { + "type": "string" + }, + "type": "array" + }, + "ports": { + "description": "The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ServicePort" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "port", + "protocol" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "port", + "x-kubernetes-patch-strategy": "merge" + }, + "publishNotReadyAddresses": { + "description": "publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery.", + "type": "boolean" + }, + "selector": { + "additionalProperties": { + "type": "string" + }, + "description": "Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/", + "type": "object" + }, + "sessionAffinity": { + "description": "Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + "type": "string" + }, + "sessionAffinityConfig": { + "$ref": "#/definitions/io.k8s.api.core.v1.SessionAffinityConfig", + "description": "sessionAffinityConfig contains the configurations of session affinity." + }, + "type": { + "description": "type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \"ExternalName\" maps to the specified externalName. \"ClusterIP\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \"None\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \"NodePort\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \"LoadBalancer\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ServiceStatus": { + "description": "ServiceStatus represents the current status of a service.", + "properties": { + "loadBalancer": { + "$ref": "#/definitions/io.k8s.api.core.v1.LoadBalancerStatus", + "description": "LoadBalancer contains the current status of the load-balancer, if one is present." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.SessionAffinityConfig": { + "description": "SessionAffinityConfig represents the configurations of session affinity.", + "properties": { + "clientIP": { + "$ref": "#/definitions/io.k8s.api.core.v1.ClientIPConfig", + "description": "clientIP contains the configurations of Client IP based session affinity." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.StorageOSPersistentVolumeSource": { + "description": "Represents a StorageOS persistent volume resource.", + "properties": { + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "readOnly": { + "description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted." + }, + "volumeName": { + "description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.", + "type": "string" + }, + "volumeNamespace": { + "description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.StorageOSVolumeSource": { + "description": "Represents a StorageOS persistent volume resource.", + "properties": { + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "readOnly": { + "description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted." + }, + "volumeName": { + "description": "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.", + "type": "string" + }, + "volumeNamespace": { + "description": "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Sysctl": { + "description": "Sysctl defines a kernel parameter to be set", + "properties": { + "name": { + "description": "Name of a property to set", + "type": "string" + }, + "value": { + "description": "Value of a property to set", + "type": "string" + } + }, + "required": [ + "name", + "value" + ], + "type": "object" + }, + "io.k8s.api.core.v1.TCPSocketAction": { + "description": "TCPSocketAction describes an action based on opening a socket", + "properties": { + "host": { + "description": "Optional: Host name to connect to, defaults to the pod IP.", + "type": "string" + }, + "port": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME." + } + }, + "required": [ + "port" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Taint": { + "description": "The node this Taint is attached to has the \"effect\" on any pod that does not tolerate the Taint.", + "properties": { + "effect": { + "description": "Required. The effect of the taint on pods that do not tolerate the taint. Valid effects are NoSchedule, PreferNoSchedule and NoExecute.", + "type": "string" + }, + "key": { + "description": "Required. The taint key to be applied to a node.", + "type": "string" + }, + "timeAdded": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "TimeAdded represents the time at which the taint was added. It is only written for NoExecute taints." + }, + "value": { + "description": "Required. The taint value corresponding to the taint key.", + "type": "string" + } + }, + "required": [ + "key", + "effect" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Toleration": { + "description": "The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator .", + "properties": { + "effect": { + "description": "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.", + "type": "string" + }, + "key": { + "description": "Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.", + "type": "string" + }, + "operator": { + "description": "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.", + "type": "string" + }, + "tolerationSeconds": { + "description": "TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.", + "format": "int64", + "type": "integer" + }, + "value": { + "description": "Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.TopologySelectorLabelRequirement": { + "description": "A topology selector requirement is a selector that matches given label. This is an alpha feature and may change in the future.", + "properties": { + "key": { + "description": "The label key that the selector applies to.", + "type": "string" + }, + "values": { + "description": "An array of string values. One value must match the label to be selected. Each entry in Values is ORed.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "key", + "values" + ], + "type": "object" + }, + "io.k8s.api.core.v1.TopologySelectorTerm": { + "description": "A topology selector term represents the result of label queries. A null or empty topology selector term matches no objects. The requirements of them are ANDed. It provides a subset of functionality as NodeSelectorTerm. This is an alpha feature and may change in the future.", + "properties": { + "matchLabelExpressions": { + "description": "A list of topology selector requirements by labels.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.TopologySelectorLabelRequirement" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.TypedLocalObjectReference": { + "description": "TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.", + "properties": { + "apiGroup": { + "description": "APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.", + "type": "string" + }, + "kind": { + "description": "Kind is the type of resource being referenced", + "type": "string" + }, + "name": { + "description": "Name is the name of resource being referenced", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Volume": { + "description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.", + "properties": { + "awsElasticBlockStore": { + "$ref": "#/definitions/io.k8s.api.core.v1.AWSElasticBlockStoreVolumeSource", + "description": "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore" + }, + "azureDisk": { + "$ref": "#/definitions/io.k8s.api.core.v1.AzureDiskVolumeSource", + "description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod." + }, + "azureFile": { + "$ref": "#/definitions/io.k8s.api.core.v1.AzureFileVolumeSource", + "description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod." + }, + "cephfs": { + "$ref": "#/definitions/io.k8s.api.core.v1.CephFSVolumeSource", + "description": "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime" + }, + "cinder": { + "$ref": "#/definitions/io.k8s.api.core.v1.CinderVolumeSource", + "description": "Cinder represents a cinder volume attached and mounted on kubelets host machine More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md" + }, + "configMap": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapVolumeSource", + "description": "ConfigMap represents a configMap that should populate this volume" + }, + "csi": { + "$ref": "#/definitions/io.k8s.api.core.v1.CSIVolumeSource", + "description": "CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature)." + }, + "downwardAPI": { + "$ref": "#/definitions/io.k8s.api.core.v1.DownwardAPIVolumeSource", + "description": "DownwardAPI represents downward API about the pod that should populate this volume" + }, + "emptyDir": { + "$ref": "#/definitions/io.k8s.api.core.v1.EmptyDirVolumeSource", + "description": "EmptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir" + }, + "fc": { + "$ref": "#/definitions/io.k8s.api.core.v1.FCVolumeSource", + "description": "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod." + }, + "flexVolume": { + "$ref": "#/definitions/io.k8s.api.core.v1.FlexVolumeSource", + "description": "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin." + }, + "flocker": { + "$ref": "#/definitions/io.k8s.api.core.v1.FlockerVolumeSource", + "description": "Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running" + }, + "gcePersistentDisk": { + "$ref": "#/definitions/io.k8s.api.core.v1.GCEPersistentDiskVolumeSource", + "description": "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk" + }, + "gitRepo": { + "$ref": "#/definitions/io.k8s.api.core.v1.GitRepoVolumeSource", + "description": "GitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container." + }, + "glusterfs": { + "$ref": "#/definitions/io.k8s.api.core.v1.GlusterfsVolumeSource", + "description": "Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md" + }, + "hostPath": { + "$ref": "#/definitions/io.k8s.api.core.v1.HostPathVolumeSource", + "description": "HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath" + }, + "iscsi": { + "$ref": "#/definitions/io.k8s.api.core.v1.ISCSIVolumeSource", + "description": "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://releases.k8s.io/HEAD/examples/volumes/iscsi/README.md" + }, + "name": { + "description": "Volume's name. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "nfs": { + "$ref": "#/definitions/io.k8s.api.core.v1.NFSVolumeSource", + "description": "NFS represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs" + }, + "persistentVolumeClaim": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaimVolumeSource", + "description": "PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims" + }, + "photonPersistentDisk": { + "$ref": "#/definitions/io.k8s.api.core.v1.PhotonPersistentDiskVolumeSource", + "description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine" + }, + "portworxVolume": { + "$ref": "#/definitions/io.k8s.api.core.v1.PortworxVolumeSource", + "description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine" + }, + "projected": { + "$ref": "#/definitions/io.k8s.api.core.v1.ProjectedVolumeSource", + "description": "Items for all in one resources secrets, configmaps, and downward API" + }, + "quobyte": { + "$ref": "#/definitions/io.k8s.api.core.v1.QuobyteVolumeSource", + "description": "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime" + }, + "rbd": { + "$ref": "#/definitions/io.k8s.api.core.v1.RBDVolumeSource", + "description": "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md" + }, + "scaleIO": { + "$ref": "#/definitions/io.k8s.api.core.v1.ScaleIOVolumeSource", + "description": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes." + }, + "secret": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretVolumeSource", + "description": "Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret" + }, + "storageos": { + "$ref": "#/definitions/io.k8s.api.core.v1.StorageOSVolumeSource", + "description": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes." + }, + "vsphereVolume": { + "$ref": "#/definitions/io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource", + "description": "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.VolumeDevice": { + "description": "volumeDevice describes a mapping of a raw block device within a container.", + "properties": { + "devicePath": { + "description": "devicePath is the path inside of the container that the device will be mapped to.", + "type": "string" + }, + "name": { + "description": "name must match the name of a persistentVolumeClaim in the pod", + "type": "string" + } + }, + "required": [ + "name", + "devicePath" + ], + "type": "object" + }, + "io.k8s.api.core.v1.VolumeMount": { + "description": "VolumeMount describes a mounting of a Volume within a container.", + "properties": { + "mountPath": { + "description": "Path within the container at which the volume should be mounted. Must not contain ':'.", + "type": "string" + }, + "mountPropagation": { + "description": "mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.", + "type": "string" + }, + "name": { + "description": "This must match the Name of a Volume.", + "type": "string" + }, + "readOnly": { + "description": "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.", + "type": "boolean" + }, + "subPath": { + "description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).", + "type": "string" + }, + "subPathExpr": { + "description": "Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \"\" (volume's root). SubPathExpr and SubPath are mutually exclusive. This field is alpha in 1.14.", + "type": "string" + } + }, + "required": [ + "name", + "mountPath" + ], + "type": "object" + }, + "io.k8s.api.core.v1.VolumeNodeAffinity": { + "description": "VolumeNodeAffinity defines constraints that limit what nodes this volume can be accessed from.", + "properties": { + "required": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelector", + "description": "Required specifies hard node constraints that must be met." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.VolumeProjection": { + "description": "Projection that may be projected along with other supported volume types", + "properties": { + "configMap": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapProjection", + "description": "information about the configMap data to project" + }, + "downwardAPI": { + "$ref": "#/definitions/io.k8s.api.core.v1.DownwardAPIProjection", + "description": "information about the downwardAPI data to project" + }, + "secret": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretProjection", + "description": "information about the secret data to project" + }, + "serviceAccountToken": { + "$ref": "#/definitions/io.k8s.api.core.v1.ServiceAccountTokenProjection", + "description": "information about the serviceAccountToken data to project" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource": { + "description": "Represents a vSphere volume resource.", + "properties": { + "fsType": { + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "storagePolicyID": { + "description": "Storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName.", + "type": "string" + }, + "storagePolicyName": { + "description": "Storage Policy Based Management (SPBM) profile name.", + "type": "string" + }, + "volumePath": { + "description": "Path that identifies vSphere volume vmdk", + "type": "string" + } + }, + "required": [ + "volumePath" + ], + "type": "object" + }, + "io.k8s.api.core.v1.WeightedPodAffinityTerm": { + "description": "The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)", + "properties": { + "podAffinityTerm": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodAffinityTerm", + "description": "Required. A pod affinity term, associated with the corresponding weight." + }, + "weight": { + "description": "weight associated with matching the corresponding podAffinityTerm, in the range 1-100.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "weight", + "podAffinityTerm" + ], + "type": "object" + }, + "io.k8s.api.events.v1beta1.Event": { + "description": "Event is a report of an event somewhere in the cluster. It generally denotes some state change in the system.", + "properties": { + "action": { + "description": "What action was taken/failed regarding to the regarding object.", + "type": "string" + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "deprecatedCount": { + "description": "Deprecated field assuring backward compatibility with core.v1 Event type", + "format": "int32", + "type": "integer" + }, + "deprecatedFirstTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Deprecated field assuring backward compatibility with core.v1 Event type" + }, + "deprecatedLastTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Deprecated field assuring backward compatibility with core.v1 Event type" + }, + "deprecatedSource": { + "$ref": "#/definitions/io.k8s.api.core.v1.EventSource", + "description": "Deprecated field assuring backward compatibility with core.v1 Event type" + }, + "eventTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "Required. Time when this Event was first observed." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Event" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "note": { + "description": "Optional. A human-readable description of the status of this operation. Maximal length of the note is 1kB, but libraries should be prepared to handle values up to 64kB.", + "type": "string" + }, + "reason": { + "description": "Why the action was taken.", + "type": "string" + }, + "regarding": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "The object this Event is about. In most cases it's an Object reporting controller implements. E.g. ReplicaSetController implements ReplicaSets and this event is emitted because it acts on some changes in a ReplicaSet object." + }, + "related": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "Optional secondary object for more complex actions. E.g. when regarding object triggers a creation or deletion of related object." + }, + "reportingController": { + "description": "Name of the controller that emitted this Event, e.g. `kubernetes.io/kubelet`.", + "type": "string" + }, + "reportingInstance": { + "description": "ID of the controller instance, e.g. `kubelet-xyzf`.", + "type": "string" + }, + "series": { + "$ref": "#/definitions/io.k8s.api.events.v1beta1.EventSeries", + "description": "Data about the Event series this event represents or nil if it's a singleton Event." + }, + "type": { + "description": "Type of this event (Normal, Warning), new types could be added in the future.", + "type": "string" + } + }, + "required": [ + "eventTime" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "events.k8s.io", + "kind": "Event", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.events.v1beta1.EventList": { + "description": "EventList is a list of Event objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.events.v1beta1.Event" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "EventList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "events.k8s.io", + "kind": "EventList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.events.v1beta1.EventSeries": { + "description": "EventSeries contain information on series of events, i.e. thing that was/is happening continuously for some time.", + "properties": { + "count": { + "description": "Number of occurrences in this series up to the last heartbeat time", + "format": "int32", + "type": "integer" + }, + "lastObservedTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "Time when last Event from the series was seen before last heartbeat." + }, + "state": { + "description": "Information whether this series is ongoing or finished.", + "type": "string" + } + }, + "required": [ + "count", + "lastObservedTime", + "state" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.AllowedCSIDriver": { + "description": "AllowedCSIDriver represents a single inline CSI Driver that is allowed to be used.", + "properties": { + "name": { + "description": "Name is the registered name of the CSI driver", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.AllowedFlexVolume": { + "description": "AllowedFlexVolume represents a single Flexvolume that is allowed to be used. Deprecated: use AllowedFlexVolume from policy API Group instead.", + "properties": { + "driver": { + "description": "driver is the name of the Flexvolume driver.", + "type": "string" + } + }, + "required": [ + "driver" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.AllowedHostPath": { + "description": "AllowedHostPath defines the host volume conditions that will be enabled by a policy for pods to use. It requires the path prefix to be defined. Deprecated: use AllowedHostPath from policy API Group instead.", + "properties": { + "pathPrefix": { + "description": "pathPrefix is the path prefix that the host volume must match. It does not support `*`. Trailing slashes are trimmed when validating the path prefix with a host path.\n\nExamples: `/foo` would allow `/foo`, `/foo/` and `/foo/bar` `/foo` would not allow `/food` or `/etc/foo`", + "type": "string" + }, + "readOnly": { + "description": "when set to true, will allow host volumes matching the pathPrefix only if all volume mounts are readOnly.", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.DaemonSet": { + "description": "DEPRECATED - This group version of DaemonSet is deprecated by apps/v1beta2/DaemonSet. See the release notes for more information. DaemonSet represents the configuration of a daemon set.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DaemonSet" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.DaemonSetSpec", + "description": "The desired behavior of this daemon set. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.DaemonSetStatus", + "description": "The current status of this daemon set. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "DaemonSet", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.DaemonSetCondition": { + "description": "DaemonSetCondition describes the state of a DaemonSet at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of DaemonSet condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.DaemonSetList": { + "description": "DaemonSetList is a collection of daemon sets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "A list of daemon sets.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.DaemonSet" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DaemonSetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "DaemonSetList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.DaemonSetSpec": { + "description": "DaemonSetSpec is the specification of a daemon set.", + "properties": { + "minReadySeconds": { + "description": "The minimum number of seconds for which a newly created DaemonSet pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready).", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "The number of old history to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "A label query over pods that are managed by the daemon set. Must match in order to be controlled. If empty, defaulted to labels on Pod template. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "An object that describes the pod that will be created. The DaemonSet will create exactly one copy of this pod on every node that matches the template's node selector (or on every node if no node selector is specified). More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template" + }, + "templateGeneration": { + "description": "DEPRECATED. A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.", + "format": "int64", + "type": "integer" + }, + "updateStrategy": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.DaemonSetUpdateStrategy", + "description": "An update strategy to replace existing DaemonSet pods with new pods." + } + }, + "required": [ + "template" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.DaemonSetStatus": { + "description": "DaemonSetStatus represents the current status of a daemon set.", + "properties": { + "collisionCount": { + "description": "Count of hash collisions for the DaemonSet. The DaemonSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a DaemonSet's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.DaemonSetCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "currentNumberScheduled": { + "description": "The number of nodes that are running at least 1 daemon pod and are supposed to run the daemon pod. More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", + "format": "int32", + "type": "integer" + }, + "desiredNumberScheduled": { + "description": "The total number of nodes that should be running the daemon pod (including nodes correctly running the daemon pod). More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", + "format": "int32", + "type": "integer" + }, + "numberAvailable": { + "description": "The number of nodes that should be running the daemon pod and have one or more of the daemon pod running and available (ready for at least spec.minReadySeconds)", + "format": "int32", + "type": "integer" + }, + "numberMisscheduled": { + "description": "The number of nodes that are running the daemon pod, but are not supposed to run the daemon pod. More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", + "format": "int32", + "type": "integer" + }, + "numberReady": { + "description": "The number of nodes that should be running the daemon pod and have one or more of the daemon pod running and ready.", + "format": "int32", + "type": "integer" + }, + "numberUnavailable": { + "description": "The number of nodes that should be running the daemon pod and have none of the daemon pod running and available (ready for at least spec.minReadySeconds)", + "format": "int32", + "type": "integer" + }, + "observedGeneration": { + "description": "The most recent generation observed by the daemon set controller.", + "format": "int64", + "type": "integer" + }, + "updatedNumberScheduled": { + "description": "The total number of nodes that are running updated daemon pod", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "currentNumberScheduled", + "numberMisscheduled", + "desiredNumberScheduled", + "numberReady" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.DaemonSetUpdateStrategy": { + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.RollingUpdateDaemonSet", + "description": "Rolling update config params. Present only if type = \"RollingUpdate\"." + }, + "type": { + "description": "Type of daemon set update. Can be \"RollingUpdate\" or \"OnDelete\". Default is OnDelete.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.Deployment": { + "description": "DEPRECATED - This group version of Deployment is deprecated by apps/v1beta2/Deployment. See the release notes for more information. Deployment enables declarative updates for Pods and ReplicaSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Deployment" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.DeploymentSpec", + "description": "Specification of the desired behavior of the Deployment." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.DeploymentStatus", + "description": "Most recently observed status of the Deployment." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "Deployment", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.DeploymentCondition": { + "description": "DeploymentCondition describes the state of a deployment at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "lastUpdateTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The last time this condition was updated." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of deployment condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.DeploymentList": { + "description": "DeploymentList is a list of Deployments.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of Deployments.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.Deployment" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DeploymentList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "DeploymentList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.DeploymentRollback": { + "description": "DEPRECATED. DeploymentRollback stores the information required to rollback a deployment.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DeploymentRollback" + ] + }, + "name": { + "description": "Required: This must match the Name of a deployment.", + "type": "string" + }, + "rollbackTo": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.RollbackConfig", + "description": "The config of this deployment rollback." + }, + "updatedAnnotations": { + "additionalProperties": { + "type": "string" + }, + "description": "The annotations to be updated to a deployment", + "type": "object" + } + }, + "required": [ + "name", + "rollbackTo" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "DeploymentRollback", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.DeploymentSpec": { + "description": "DeploymentSpec is the specification of the desired behavior of the Deployment.", + "properties": { + "minReadySeconds": { + "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + "format": "int32", + "type": "integer" + }, + "paused": { + "description": "Indicates that the deployment is paused and will not be processed by the deployment controller.", + "type": "boolean" + }, + "progressDeadlineSeconds": { + "description": "The maximum time in seconds for a deployment to make progress before it is considered to be failed. The deployment controller will continue to process failed deployments and a condition with a ProgressDeadlineExceeded reason will be surfaced in the deployment status. Note that progress will not be estimated during the time a deployment is paused. This is set to the max value of int32 (i.e. 2147483647) by default, which means \"no deadline\".", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1.", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "The number of old ReplicaSets to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. This is set to the max value of int32 (i.e. 2147483647) by default, which means \"retaining all old RelicaSets\".", + "format": "int32", + "type": "integer" + }, + "rollbackTo": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.RollbackConfig", + "description": "DEPRECATED. The config this deployment is rolling back to. Will be cleared after rollback is done." + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Label selector for pods. Existing ReplicaSets whose pods are selected by this will be the ones affected by this deployment." + }, + "strategy": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.DeploymentStrategy", + "description": "The deployment strategy to use to replace existing pods with new ones.", + "x-kubernetes-patch-strategy": "retainKeys" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template describes the pods that will be created." + } + }, + "required": [ + "template" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.DeploymentStatus": { + "description": "DeploymentStatus is the most recently observed status of the Deployment.", + "properties": { + "availableReplicas": { + "description": "Total number of available pods (ready for at least minReadySeconds) targeted by this deployment.", + "format": "int32", + "type": "integer" + }, + "collisionCount": { + "description": "Count of hash collisions for the Deployment. The Deployment controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ReplicaSet.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a deployment's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.DeploymentCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "The generation observed by the deployment controller.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "Total number of ready pods targeted by this deployment.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Total number of non-terminated pods targeted by this deployment (their labels match the selector).", + "format": "int32", + "type": "integer" + }, + "unavailableReplicas": { + "description": "Total number of unavailable pods targeted by this deployment. This is the total number of pods that are still required for the deployment to have 100% available capacity. They may either be pods that are running but not yet available or pods that still have not been created.", + "format": "int32", + "type": "integer" + }, + "updatedReplicas": { + "description": "Total number of non-terminated pods targeted by this deployment that have the desired template spec.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.DeploymentStrategy": { + "description": "DeploymentStrategy describes how to replace existing pods with new ones.", + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.RollingUpdateDeployment", + "description": "Rolling update config params. Present only if DeploymentStrategyType = RollingUpdate." + }, + "type": { + "description": "Type of deployment. Can be \"Recreate\" or \"RollingUpdate\". Default is RollingUpdate.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.FSGroupStrategyOptions": { + "description": "FSGroupStrategyOptions defines the strategy type and options used to create the strategy. Deprecated: use FSGroupStrategyOptions from policy API Group instead.", + "properties": { + "ranges": { + "description": "ranges are the allowed ranges of fs groups. If you would like to force a single fs group then supply a single range with the same start and end. Required for MustRunAs.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.IDRange" + }, + "type": "array" + }, + "rule": { + "description": "rule is the strategy that will dictate what FSGroup is used in the SecurityContext.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.HTTPIngressPath": { + "description": "HTTPIngressPath associates a path regex with a backend. Incoming urls matching the path are forwarded to the backend.", + "properties": { + "backend": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.IngressBackend", + "description": "Backend defines the referenced service endpoint to which the traffic will be forwarded to." + }, + "path": { + "description": "Path is an extended POSIX regex as defined by IEEE Std 1003.1, (i.e this follows the egrep/unix syntax, not the perl syntax) matched against the path of an incoming request. Currently it can contain characters disallowed from the conventional \"path\" part of a URL as defined by RFC 3986. Paths must begin with a '/'. If unspecified, the path defaults to a catch all sending traffic to the backend.", + "type": "string" + } + }, + "required": [ + "backend" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue": { + "description": "HTTPIngressRuleValue is a list of http selectors pointing to backends. In the example: http:///? -> backend where where parts of the url correspond to RFC 3986, this resource will be used to match against everything after the last '/' and before the first '?' or '#'.", + "properties": { + "paths": { + "description": "A collection of paths that map requests to backends.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.HTTPIngressPath" + }, + "type": "array" + } + }, + "required": [ + "paths" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.HostPortRange": { + "description": "HostPortRange defines a range of host ports that will be enabled by a policy for pods to use. It requires both the start and end to be defined. Deprecated: use HostPortRange from policy API Group instead.", + "properties": { + "max": { + "description": "max is the end of the range, inclusive.", + "format": "int32", + "type": "integer" + }, + "min": { + "description": "min is the start of the range, inclusive.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "min", + "max" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.IDRange": { + "description": "IDRange provides a min/max of an allowed range of IDs. Deprecated: use IDRange from policy API Group instead.", + "properties": { + "max": { + "description": "max is the end of the range, inclusive.", + "format": "int64", + "type": "integer" + }, + "min": { + "description": "min is the start of the range, inclusive.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "min", + "max" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.IPBlock": { + "description": "DEPRECATED 1.9 - This group version of IPBlock is deprecated by networking/v1/IPBlock. IPBlock describes a particular CIDR (Ex. \"192.168.1.1/24\") that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs that should not be included within this rule.", + "properties": { + "cidr": { + "description": "CIDR is a string representing the IP Block Valid examples are \"192.168.1.1/24\"", + "type": "string" + }, + "except": { + "description": "Except is a slice of CIDRs that should not be included within an IP Block Valid examples are \"192.168.1.1/24\" Except values will be rejected if they are outside the CIDR range", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "cidr" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.Ingress": { + "description": "Ingress is a collection of rules that allow inbound connections to reach the endpoints defined by a backend. An Ingress can be configured to give services externally-reachable urls, load balance traffic, terminate SSL, offer name based virtual hosting etc. DEPRECATED - This group version of Ingress is deprecated by networking.k8s.io/v1beta1 Ingress. See the release notes for more information.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Ingress" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.IngressSpec", + "description": "Spec is the desired state of the Ingress. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.IngressStatus", + "description": "Status is the current state of the Ingress. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "Ingress", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.IngressBackend": { + "description": "IngressBackend describes all endpoints for a given service and port.", + "properties": { + "serviceName": { + "description": "Specifies the name of the referenced service.", + "type": "string" + }, + "servicePort": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "Specifies the port of the referenced service." + } + }, + "required": [ + "serviceName", + "servicePort" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.IngressList": { + "description": "IngressList is a collection of Ingress.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of Ingress.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.Ingress" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "IngressList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "IngressList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.IngressRule": { + "description": "IngressRule represents the rules mapping the paths under a specified host to the related backend services. Incoming requests are first evaluated for a host match, then routed to the backend associated with the matching IngressRuleValue.", + "properties": { + "host": { + "description": "Host is the fully qualified domain name of a network host, as defined by RFC 3986. Note the following deviations from the \"host\" part of the URI as defined in the RFC: 1. IPs are not allowed. Currently an IngressRuleValue can only apply to the\n\t IP in the Spec of the parent Ingress.\n2. The `:` delimiter is not respected because ports are not allowed.\n\t Currently the port of an Ingress is implicitly :80 for http and\n\t :443 for https.\nBoth these may change in the future. Incoming requests are matched against the host before the IngressRuleValue. If the host is unspecified, the Ingress routes all traffic based on the specified IngressRuleValue.", + "type": "string" + }, + "http": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.IngressSpec": { + "description": "IngressSpec describes the Ingress the user wishes to exist.", + "properties": { + "backend": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.IngressBackend", + "description": "A default backend capable of servicing requests that don't match any rule. At least one of 'backend' or 'rules' must be specified. This field is optional to allow the loadbalancer controller or defaulting logic to specify a global default." + }, + "rules": { + "description": "A list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.IngressRule" + }, + "type": "array" + }, + "tls": { + "description": "TLS configuration. Currently the Ingress only supports a single TLS port, 443. If multiple members of this list specify different hosts, they will be multiplexed on the same port according to the hostname specified through the SNI TLS extension, if the ingress controller fulfilling the ingress supports SNI.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.IngressTLS" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.IngressStatus": { + "description": "IngressStatus describe the current state of the Ingress.", + "properties": { + "loadBalancer": { + "$ref": "#/definitions/io.k8s.api.core.v1.LoadBalancerStatus", + "description": "LoadBalancer contains the current status of the load-balancer." + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.IngressTLS": { + "description": "IngressTLS describes the transport layer security associated with an Ingress.", + "properties": { + "hosts": { + "description": "Hosts are a list of hosts included in the TLS certificate. The values in this list must match the name/s used in the tlsSecret. Defaults to the wildcard host setting for the loadbalancer controller fulfilling this Ingress, if left unspecified.", + "items": { + "type": "string" + }, + "type": "array" + }, + "secretName": { + "description": "SecretName is the name of the secret used to terminate SSL traffic on 443. Field is left optional to allow SSL routing based on SNI hostname alone. If the SNI host in a listener conflicts with the \"Host\" header field used by an IngressRule, the SNI host is used for termination and value of the Host header is used for routing.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.NetworkPolicy": { + "description": "DEPRECATED 1.9 - This group version of NetworkPolicy is deprecated by networking/v1/NetworkPolicy. NetworkPolicy describes what network traffic is allowed for a set of Pods", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "NetworkPolicy" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.NetworkPolicySpec", + "description": "Specification of the desired behavior for this NetworkPolicy." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "NetworkPolicy", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.NetworkPolicyEgressRule": { + "description": "DEPRECATED 1.9 - This group version of NetworkPolicyEgressRule is deprecated by networking/v1/NetworkPolicyEgressRule. NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to. This type is beta-level in 1.8", + "properties": { + "ports": { + "description": "List of destination ports for outgoing traffic. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.NetworkPolicyPort" + }, + "type": "array" + }, + "to": { + "description": "List of destinations for outgoing traffic of pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all destinations (traffic not restricted by destination). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the to list.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.NetworkPolicyPeer" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.NetworkPolicyIngressRule": { + "description": "DEPRECATED 1.9 - This group version of NetworkPolicyIngressRule is deprecated by networking/v1/NetworkPolicyIngressRule. This NetworkPolicyIngressRule matches traffic if and only if the traffic matches both ports AND from.", + "properties": { + "from": { + "description": "List of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least on item, this rule allows traffic only if the traffic matches at least one item in the from list.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.NetworkPolicyPeer" + }, + "type": "array" + }, + "ports": { + "description": "List of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.NetworkPolicyPort" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.NetworkPolicyList": { + "description": "DEPRECATED 1.9 - This group version of NetworkPolicyList is deprecated by networking/v1/NetworkPolicyList. Network Policy List is a list of NetworkPolicy objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.NetworkPolicy" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "NetworkPolicyList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "NetworkPolicyList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.NetworkPolicyPeer": { + "description": "DEPRECATED 1.9 - This group version of NetworkPolicyPeer is deprecated by networking/v1/NetworkPolicyPeer.", + "properties": { + "ipBlock": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.IPBlock", + "description": "IPBlock defines policy on a particular IPBlock. If this field is set then neither of the other fields can be." + }, + "namespaceSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Selects Namespaces using cluster-scoped labels. This field follows standard label selector semantics; if present but empty, it selects all namespaces.\n\nIf PodSelector is also set, then the NetworkPolicyPeer as a whole selects the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. Otherwise it selects all Pods in the Namespaces selected by NamespaceSelector." + }, + "podSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "This is a label selector which selects Pods. This field follows standard label selector semantics; if present but empty, it selects all pods.\n\nIf NamespaceSelector is also set, then the NetworkPolicyPeer as a whole selects the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. Otherwise it selects the Pods matching PodSelector in the policy's own Namespace." + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.NetworkPolicyPort": { + "description": "DEPRECATED 1.9 - This group version of NetworkPolicyPort is deprecated by networking/v1/NetworkPolicyPort.", + "properties": { + "port": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "If specified, the port on the given protocol. This can either be a numerical or named port on a pod. If this field is not provided, this matches all port names and numbers. If present, only traffic on the specified protocol AND port will be matched." + }, + "protocol": { + "description": "Optional. The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.NetworkPolicySpec": { + "description": "DEPRECATED 1.9 - This group version of NetworkPolicySpec is deprecated by networking/v1/NetworkPolicySpec.", + "properties": { + "egress": { + "description": "List of egress rules to be applied to the selected pods. Outgoing traffic is allowed if there are no NetworkPolicies selecting the pod (and cluster policy otherwise allows the traffic), OR if the traffic matches at least one egress rule across all of the NetworkPolicy objects whose podSelector matches the pod. If this field is empty then this NetworkPolicy limits all outgoing traffic (and serves solely to ensure that the pods it selects are isolated by default). This field is beta-level in 1.8", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.NetworkPolicyEgressRule" + }, + "type": "array" + }, + "ingress": { + "description": "List of ingress rules to be applied to the selected pods. Traffic is allowed to a pod if there are no NetworkPolicies selecting the pod OR if the traffic source is the pod's local node, OR if the traffic matches at least one ingress rule across all of the NetworkPolicy objects whose podSelector matches the pod. If this field is empty then this NetworkPolicy does not allow any traffic (and serves solely to ensure that the pods it selects are isolated by default).", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.NetworkPolicyIngressRule" + }, + "type": "array" + }, + "podSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Selects the pods to which this NetworkPolicy object applies. The array of ingress rules is applied to any pods selected by this field. Multiple network policies can select the same set of pods. In this case, the ingress rules for each are combined additively. This field is NOT optional and follows standard label selector semantics. An empty podSelector matches all pods in this namespace." + }, + "policyTypes": { + "description": "List of rule types that the NetworkPolicy relates to. Valid options are \"Ingress\", \"Egress\", or \"Ingress,Egress\". If this field is not specified, it will default based on the existence of Ingress or Egress rules; policies that contain an Egress section are assumed to affect Egress, and all policies (whether or not they contain an Ingress section) are assumed to affect Ingress. If you want to write an egress-only policy, you must explicitly specify policyTypes [ \"Egress\" ]. Likewise, if you want to write a policy that specifies that no egress is allowed, you must specify a policyTypes value that include \"Egress\" (since such a policy would not include an Egress section and would otherwise default to just [ \"Ingress\" ]). This field is beta-level in 1.8", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "podSelector" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.PodSecurityPolicy": { + "description": "PodSecurityPolicy governs the ability to make requests that affect the Security Context that will be applied to a pod and container. Deprecated: use PodSecurityPolicy from policy API Group instead.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodSecurityPolicy" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.PodSecurityPolicySpec", + "description": "spec defines the policy enforced." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "PodSecurityPolicy", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.PodSecurityPolicyList": { + "description": "PodSecurityPolicyList is a list of PodSecurityPolicy objects. Deprecated: use PodSecurityPolicyList from policy API Group instead.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.PodSecurityPolicy" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodSecurityPolicyList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "PodSecurityPolicyList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.PodSecurityPolicySpec": { + "description": "PodSecurityPolicySpec defines the policy enforced. Deprecated: use PodSecurityPolicySpec from policy API Group instead.", + "properties": { + "allowPrivilegeEscalation": { + "description": "allowPrivilegeEscalation determines if a pod can request to allow privilege escalation. If unspecified, defaults to true.", + "type": "boolean" + }, + "allowedCSIDrivers": { + "description": "AllowedCSIDrivers is a whitelist of inline CSI drivers that must be explicitly set to be embedded within a pod spec. An empty value means no CSI drivers can run inline within a pod spec.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.AllowedCSIDriver" + }, + "type": "array" + }, + "allowedCapabilities": { + "description": "allowedCapabilities is a list of capabilities that can be requested to add to the container. Capabilities in this field may be added at the pod author's discretion. You must not list a capability in both allowedCapabilities and requiredDropCapabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, + "allowedFlexVolumes": { + "description": "allowedFlexVolumes is a whitelist of allowed Flexvolumes. Empty or nil indicates that all Flexvolumes may be used. This parameter is effective only when the usage of the Flexvolumes is allowed in the \"volumes\" field.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.AllowedFlexVolume" + }, + "type": "array" + }, + "allowedHostPaths": { + "description": "allowedHostPaths is a white list of allowed host paths. Empty indicates that all host paths may be used.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.AllowedHostPath" + }, + "type": "array" + }, + "allowedProcMountTypes": { + "description": "AllowedProcMountTypes is a whitelist of allowed ProcMountTypes. Empty or nil indicates that only the DefaultProcMountType may be used. This requires the ProcMountType feature flag to be enabled.", + "items": { + "type": "string" + }, + "type": "array" + }, + "allowedUnsafeSysctls": { + "description": "allowedUnsafeSysctls is a list of explicitly allowed unsafe sysctls, defaults to none. Each entry is either a plain sysctl name or ends in \"*\" in which case it is considered as a prefix of allowed sysctls. Single * means all unsafe sysctls are allowed. Kubelet has to whitelist all allowed unsafe sysctls explicitly to avoid rejection.\n\nExamples: e.g. \"foo/*\" allows \"foo/bar\", \"foo/baz\", etc. e.g. \"foo.*\" allows \"foo.bar\", \"foo.baz\", etc.", + "items": { + "type": "string" + }, + "type": "array" + }, + "defaultAddCapabilities": { + "description": "defaultAddCapabilities is the default set of capabilities that will be added to the container unless the pod spec specifically drops the capability. You may not list a capability in both defaultAddCapabilities and requiredDropCapabilities. Capabilities added here are implicitly allowed, and need not be included in the allowedCapabilities list.", + "items": { + "type": "string" + }, + "type": "array" + }, + "defaultAllowPrivilegeEscalation": { + "description": "defaultAllowPrivilegeEscalation controls the default setting for whether a process can gain more privileges than its parent process.", + "type": "boolean" + }, + "forbiddenSysctls": { + "description": "forbiddenSysctls is a list of explicitly forbidden sysctls, defaults to none. Each entry is either a plain sysctl name or ends in \"*\" in which case it is considered as a prefix of forbidden sysctls. Single * means all sysctls are forbidden.\n\nExamples: e.g. \"foo/*\" forbids \"foo/bar\", \"foo/baz\", etc. e.g. \"foo.*\" forbids \"foo.bar\", \"foo.baz\", etc.", + "items": { + "type": "string" + }, + "type": "array" + }, + "fsGroup": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.FSGroupStrategyOptions", + "description": "fsGroup is the strategy that will dictate what fs group is used by the SecurityContext." + }, + "hostIPC": { + "description": "hostIPC determines if the policy allows the use of HostIPC in the pod spec.", + "type": "boolean" + }, + "hostNetwork": { + "description": "hostNetwork determines if the policy allows the use of HostNetwork in the pod spec.", + "type": "boolean" + }, + "hostPID": { + "description": "hostPID determines if the policy allows the use of HostPID in the pod spec.", + "type": "boolean" + }, + "hostPorts": { + "description": "hostPorts determines which host port ranges are allowed to be exposed.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.HostPortRange" + }, + "type": "array" + }, + "privileged": { + "description": "privileged determines if a pod can request to be run as privileged.", + "type": "boolean" + }, + "readOnlyRootFilesystem": { + "description": "readOnlyRootFilesystem when set to true will force containers to run with a read only root file system. If the container specifically requests to run with a non-read only root file system the PSP should deny the pod. If set to false the container may run with a read only root file system if it wishes but it will not be forced to.", + "type": "boolean" + }, + "requiredDropCapabilities": { + "description": "requiredDropCapabilities are the capabilities that will be dropped from the container. These are required to be dropped and cannot be added.", + "items": { + "type": "string" + }, + "type": "array" + }, + "runAsGroup": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.RunAsGroupStrategyOptions", + "description": "RunAsGroup is the strategy that will dictate the allowable RunAsGroup values that may be set. If this field is omitted, the pod's RunAsGroup can take any value. This field requires the RunAsGroup feature gate to be enabled." + }, + "runAsUser": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.RunAsUserStrategyOptions", + "description": "runAsUser is the strategy that will dictate the allowable RunAsUser values that may be set." + }, + "seLinux": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.SELinuxStrategyOptions", + "description": "seLinux is the strategy that will dictate the allowable labels that may be set." + }, + "supplementalGroups": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.SupplementalGroupsStrategyOptions", + "description": "supplementalGroups is the strategy that will dictate what supplemental groups are used by the SecurityContext." + }, + "volumes": { + "description": "volumes is a white list of allowed volume plugins. Empty indicates that no volumes may be used. To allow all volumes you may use '*'.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "seLinux", + "runAsUser", + "supplementalGroups", + "fsGroup" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.ReplicaSet": { + "description": "DEPRECATED - This group version of ReplicaSet is deprecated by apps/v1beta2/ReplicaSet. See the release notes for more information. ReplicaSet ensures that a specified number of pod replicas are running at any given time.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ReplicaSet" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "If the Labels of a ReplicaSet are empty, they are defaulted to be the same as the Pod(s) that the ReplicaSet manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.ReplicaSetSpec", + "description": "Spec defines the specification of the desired behavior of the ReplicaSet. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.ReplicaSetStatus", + "description": "Status is the most recently observed status of the ReplicaSet. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "ReplicaSet", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.ReplicaSetCondition": { + "description": "ReplicaSetCondition describes the state of a replica set at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of replica set condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.ReplicaSetList": { + "description": "ReplicaSetList is a collection of ReplicaSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ReplicaSets. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.ReplicaSet" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ReplicaSetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "ReplicaSetList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.ReplicaSetSpec": { + "description": "ReplicaSetSpec is the specification of a ReplicaSet.", + "properties": { + "minReadySeconds": { + "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/#what-is-a-replicationcontroller", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Selector is a label query over pods that should match the replica count. If the selector is empty, it is defaulted to the labels present on the pod template. Label keys and values that must match in order to be controlled by this replica set. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template is the object that describes the pod that will be created if insufficient replicas are detected. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.ReplicaSetStatus": { + "description": "ReplicaSetStatus represents the current status of a ReplicaSet.", + "properties": { + "availableReplicas": { + "description": "The number of available replicas (ready for at least minReadySeconds) for this replica set.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a replica set's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.ReplicaSetCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "fullyLabeledReplicas": { + "description": "The number of pods that have labels matching the labels of the pod template of the replicaset.", + "format": "int32", + "type": "integer" + }, + "observedGeneration": { + "description": "ObservedGeneration reflects the generation of the most recently observed ReplicaSet.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "The number of ready replicas for this replica set.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Replicas is the most recently oberved number of replicas. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/#what-is-a-replicationcontroller", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.RollbackConfig": { + "description": "DEPRECATED.", + "properties": { + "revision": { + "description": "The revision to rollback to. If set to 0, rollback to the last revision.", + "format": "int64", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.RollingUpdateDaemonSet": { + "description": "Spec to control the desired behavior of daemon set rolling update.", + "properties": { + "maxUnavailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of DaemonSet pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of total number of DaemonSet pods at the start of the update (ex: 10%). Absolute number is calculated from percentage by rounding up. This cannot be 0. Default value is 1. Example: when this is set to 30%, at most 30% of the total number of nodes that should be running the daemon pod (i.e. status.desiredNumberScheduled) can have their pods stopped for an update at any given time. The update starts by stopping at most 30% of those DaemonSet pods and then brings up new DaemonSet pods in their place. Once the new pods are available, it then proceeds onto other DaemonSet pods, thus ensuring that at least 70% of original number of DaemonSet pods are available at all times during the update." + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.RollingUpdateDeployment": { + "description": "Spec to control the desired behavior of rolling update.", + "properties": { + "maxSurge": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of pods that can be scheduled above the desired number of pods. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxUnavailable is 0. Absolute number is calculated from percentage by rounding up. By default, a value of 1 is used. Example: when this is set to 30%, the new RC can be scaled up immediately when the rolling update starts, such that the total number of old and new pods do not exceed 130% of desired pods. Once old pods have been killed, new RC can be scaled up further, ensuring that total number of pods running at any time during the update is at most 130% of desired pods." + }, + "maxUnavailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding down. This can not be 0 if MaxSurge is 0. By default, a fixed value of 1 is used. Example: when this is set to 30%, the old RC can be scaled down to 70% of desired pods immediately when the rolling update starts. Once new pods are ready, old RC can be scaled down further, followed by scaling up the new RC, ensuring that the total number of pods available at all times during the update is at least 70% of desired pods." + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.RunAsGroupStrategyOptions": { + "description": "RunAsGroupStrategyOptions defines the strategy type and any options used to create the strategy. Deprecated: use RunAsGroupStrategyOptions from policy API Group instead.", + "properties": { + "ranges": { + "description": "ranges are the allowed ranges of gids that may be used. If you would like to force a single gid then supply a single range with the same start and end. Required for MustRunAs.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.IDRange" + }, + "type": "array" + }, + "rule": { + "description": "rule is the strategy that will dictate the allowable RunAsGroup values that may be set.", + "type": "string" + } + }, + "required": [ + "rule" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.RunAsUserStrategyOptions": { + "description": "RunAsUserStrategyOptions defines the strategy type and any options used to create the strategy. Deprecated: use RunAsUserStrategyOptions from policy API Group instead.", + "properties": { + "ranges": { + "description": "ranges are the allowed ranges of uids that may be used. If you would like to force a single uid then supply a single range with the same start and end. Required for MustRunAs.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.IDRange" + }, + "type": "array" + }, + "rule": { + "description": "rule is the strategy that will dictate the allowable RunAsUser values that may be set.", + "type": "string" + } + }, + "required": [ + "rule" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.SELinuxStrategyOptions": { + "description": "SELinuxStrategyOptions defines the strategy type and any options used to create the strategy. Deprecated: use SELinuxStrategyOptions from policy API Group instead.", + "properties": { + "rule": { + "description": "rule is the strategy that will dictate the allowable labels that may be set.", + "type": "string" + }, + "seLinuxOptions": { + "$ref": "#/definitions/io.k8s.api.core.v1.SELinuxOptions", + "description": "seLinuxOptions required to run as; required for MustRunAs More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/" + } + }, + "required": [ + "rule" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.Scale": { + "description": "represents a scaling request for a resource.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Scale" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.ScaleSpec", + "description": "defines the behavior of the scale. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.ScaleStatus", + "description": "current status of the scale. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status. Read-only." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "extensions", + "kind": "Scale", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.extensions.v1beta1.ScaleSpec": { + "description": "describes the attributes of a scale subresource", + "properties": { + "replicas": { + "description": "desired number of instances for the scaled object.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.ScaleStatus": { + "description": "represents the current status of a scale subresource.", + "properties": { + "replicas": { + "description": "actual number of observed instances of the scaled object.", + "format": "int32", + "type": "integer" + }, + "selector": { + "additionalProperties": { + "type": "string" + }, + "description": "label query over pods that should match the replicas count. More info: http://kubernetes.io/docs/user-guide/labels#label-selectors", + "type": "object" + }, + "targetSelector": { + "description": "label selector for pods that should match the replicas count. This is a serializated version of both map-based and more expressive set-based selectors. This is done to avoid introspection in the clients. The string will be in the same format as the query-param syntax. If the target type only supports map-based selectors, both this field and map-based selector field are populated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", + "type": "string" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.extensions.v1beta1.SupplementalGroupsStrategyOptions": { + "description": "SupplementalGroupsStrategyOptions defines the strategy type and options used to create the strategy. Deprecated: use SupplementalGroupsStrategyOptions from policy API Group instead.", + "properties": { + "ranges": { + "description": "ranges are the allowed ranges of supplemental groups. If you would like to force a single supplemental group then supply a single range with the same start and end. Required for MustRunAs.", + "items": { + "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.IDRange" + }, + "type": "array" + }, + "rule": { + "description": "rule is the strategy that will dictate what supplemental groups is used in the SecurityContext.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.IPBlock": { + "description": "IPBlock describes a particular CIDR (Ex. \"192.168.1.1/24\") that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs that should not be included within this rule.", + "properties": { + "cidr": { + "description": "CIDR is a string representing the IP Block Valid examples are \"192.168.1.1/24\"", + "type": "string" + }, + "except": { + "description": "Except is a slice of CIDRs that should not be included within an IP Block Valid examples are \"192.168.1.1/24\" Except values will be rejected if they are outside the CIDR range", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "cidr" + ], + "type": "object" + }, + "io.k8s.api.networking.v1.NetworkPolicy": { + "description": "NetworkPolicy describes what network traffic is allowed for a set of Pods", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "NetworkPolicy" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicySpec", + "description": "Specification of the desired behavior for this NetworkPolicy." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "NetworkPolicy", + "version": "v1" + } + ] + }, + "io.k8s.api.networking.v1.NetworkPolicyEgressRule": { + "description": "NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to. This type is beta-level in 1.8", + "properties": { + "ports": { + "description": "List of destination ports for outgoing traffic. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyPort" + }, + "type": "array" + }, + "to": { + "description": "List of destinations for outgoing traffic of pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all destinations (traffic not restricted by destination). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the to list.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyPeer" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.NetworkPolicyIngressRule": { + "description": "NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from.", + "properties": { + "from": { + "description": "List of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least on item, this rule allows traffic only if the traffic matches at least one item in the from list.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyPeer" + }, + "type": "array" + }, + "ports": { + "description": "List of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyPort" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.NetworkPolicyList": { + "description": "NetworkPolicyList is a list of NetworkPolicy objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicy" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "NetworkPolicyList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "NetworkPolicyList", + "version": "v1" + } + ] + }, + "io.k8s.api.networking.v1.NetworkPolicyPeer": { + "description": "NetworkPolicyPeer describes a peer to allow traffic from. Only certain combinations of fields are allowed", + "properties": { + "ipBlock": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IPBlock", + "description": "IPBlock defines policy on a particular IPBlock. If this field is set then neither of the other fields can be." + }, + "namespaceSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Selects Namespaces using cluster-scoped labels. This field follows standard label selector semantics; if present but empty, it selects all namespaces.\n\nIf PodSelector is also set, then the NetworkPolicyPeer as a whole selects the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. Otherwise it selects all Pods in the Namespaces selected by NamespaceSelector." + }, + "podSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "This is a label selector which selects Pods. This field follows standard label selector semantics; if present but empty, it selects all pods.\n\nIf NamespaceSelector is also set, then the NetworkPolicyPeer as a whole selects the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. Otherwise it selects the Pods matching PodSelector in the policy's own Namespace." + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.NetworkPolicyPort": { + "description": "NetworkPolicyPort describes a port to allow traffic on", + "properties": { + "port": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The port on the given protocol. This can either be a numerical or named port on a pod. If this field is not provided, this matches all port names and numbers." + }, + "protocol": { + "description": "The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.NetworkPolicySpec": { + "description": "NetworkPolicySpec provides the specification of a NetworkPolicy", + "properties": { + "egress": { + "description": "List of egress rules to be applied to the selected pods. Outgoing traffic is allowed if there are no NetworkPolicies selecting the pod (and cluster policy otherwise allows the traffic), OR if the traffic matches at least one egress rule across all of the NetworkPolicy objects whose podSelector matches the pod. If this field is empty then this NetworkPolicy limits all outgoing traffic (and serves solely to ensure that the pods it selects are isolated by default). This field is beta-level in 1.8", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyEgressRule" + }, + "type": "array" + }, + "ingress": { + "description": "List of ingress rules to be applied to the selected pods. Traffic is allowed to a pod if there are no NetworkPolicies selecting the pod (and cluster policy otherwise allows the traffic), OR if the traffic source is the pod's local node, OR if the traffic matches at least one ingress rule across all of the NetworkPolicy objects whose podSelector matches the pod. If this field is empty then this NetworkPolicy does not allow any traffic (and serves solely to ensure that the pods it selects are isolated by default)", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyIngressRule" + }, + "type": "array" + }, + "podSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Selects the pods to which this NetworkPolicy object applies. The array of ingress rules is applied to any pods selected by this field. Multiple network policies can select the same set of pods. In this case, the ingress rules for each are combined additively. This field is NOT optional and follows standard label selector semantics. An empty podSelector matches all pods in this namespace." + }, + "policyTypes": { + "description": "List of rule types that the NetworkPolicy relates to. Valid options are \"Ingress\", \"Egress\", or \"Ingress,Egress\". If this field is not specified, it will default based on the existence of Ingress or Egress rules; policies that contain an Egress section are assumed to affect Egress, and all policies (whether or not they contain an Ingress section) are assumed to affect Ingress. If you want to write an egress-only policy, you must explicitly specify policyTypes [ \"Egress\" ]. Likewise, if you want to write a policy that specifies that no egress is allowed, you must specify a policyTypes value that include \"Egress\" (since such a policy would not include an Egress section and would otherwise default to just [ \"Ingress\" ]). This field is beta-level in 1.8", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "podSelector" + ], + "type": "object" + }, + "io.k8s.api.networking.v1beta1.HTTPIngressPath": { + "description": "HTTPIngressPath associates a path regex with a backend. Incoming urls matching the path are forwarded to the backend.", + "properties": { + "backend": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressBackend", + "description": "Backend defines the referenced service endpoint to which the traffic will be forwarded to." + }, + "path": { + "description": "Path is an extended POSIX regex as defined by IEEE Std 1003.1, (i.e this follows the egrep/unix syntax, not the perl syntax) matched against the path of an incoming request. Currently it can contain characters disallowed from the conventional \"path\" part of a URL as defined by RFC 3986. Paths must begin with a '/'. If unspecified, the path defaults to a catch all sending traffic to the backend.", + "type": "string" + } + }, + "required": [ + "backend" + ], + "type": "object" + }, + "io.k8s.api.networking.v1beta1.HTTPIngressRuleValue": { + "description": "HTTPIngressRuleValue is a list of http selectors pointing to backends. In the example: http:///? -> backend where where parts of the url correspond to RFC 3986, this resource will be used to match against everything after the last '/' and before the first '?' or '#'.", + "properties": { + "paths": { + "description": "A collection of paths that map requests to backends.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.HTTPIngressPath" + }, + "type": "array" + } + }, + "required": [ + "paths" + ], + "type": "object" + }, + "io.k8s.api.networking.v1beta1.Ingress": { + "description": "Ingress is a collection of rules that allow inbound connections to reach the endpoints defined by a backend. An Ingress can be configured to give services externally-reachable urls, load balance traffic, terminate SSL, offer name based virtual hosting etc.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Ingress" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressSpec", + "description": "Spec is the desired state of the Ingress. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressStatus", + "description": "Status is the current state of the Ingress. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "Ingress", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.networking.v1beta1.IngressBackend": { + "description": "IngressBackend describes all endpoints for a given service and port.", + "properties": { + "serviceName": { + "description": "Specifies the name of the referenced service.", + "type": "string" + }, + "servicePort": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "Specifies the port of the referenced service." + } + }, + "required": [ + "serviceName", + "servicePort" + ], + "type": "object" + }, + "io.k8s.api.networking.v1beta1.IngressList": { + "description": "IngressList is a collection of Ingress.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of Ingress.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.Ingress" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "IngressList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "IngressList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.networking.v1beta1.IngressRule": { + "description": "IngressRule represents the rules mapping the paths under a specified host to the related backend services. Incoming requests are first evaluated for a host match, then routed to the backend associated with the matching IngressRuleValue.", + "properties": { + "host": { + "description": "Host is the fully qualified domain name of a network host, as defined by RFC 3986. Note the following deviations from the \"host\" part of the URI as defined in the RFC: 1. IPs are not allowed. Currently an IngressRuleValue can only apply to the\n\t IP in the Spec of the parent Ingress.\n2. The `:` delimiter is not respected because ports are not allowed.\n\t Currently the port of an Ingress is implicitly :80 for http and\n\t :443 for https.\nBoth these may change in the future. Incoming requests are matched against the host before the IngressRuleValue. If the host is unspecified, the Ingress routes all traffic based on the specified IngressRuleValue.", + "type": "string" + }, + "http": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.HTTPIngressRuleValue" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1beta1.IngressSpec": { + "description": "IngressSpec describes the Ingress the user wishes to exist.", + "properties": { + "backend": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressBackend", + "description": "A default backend capable of servicing requests that don't match any rule. At least one of 'backend' or 'rules' must be specified. This field is optional to allow the loadbalancer controller or defaulting logic to specify a global default." + }, + "rules": { + "description": "A list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressRule" + }, + "type": "array" + }, + "tls": { + "description": "TLS configuration. Currently the Ingress only supports a single TLS port, 443. If multiple members of this list specify different hosts, they will be multiplexed on the same port according to the hostname specified through the SNI TLS extension, if the ingress controller fulfilling the ingress supports SNI.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressTLS" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1beta1.IngressStatus": { + "description": "IngressStatus describe the current state of the Ingress.", + "properties": { + "loadBalancer": { + "$ref": "#/definitions/io.k8s.api.core.v1.LoadBalancerStatus", + "description": "LoadBalancer contains the current status of the load-balancer." + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1beta1.IngressTLS": { + "description": "IngressTLS describes the transport layer security associated with an Ingress.", + "properties": { + "hosts": { + "description": "Hosts are a list of hosts included in the TLS certificate. The values in this list must match the name/s used in the tlsSecret. Defaults to the wildcard host setting for the loadbalancer controller fulfilling this Ingress, if left unspecified.", + "items": { + "type": "string" + }, + "type": "array" + }, + "secretName": { + "description": "SecretName is the name of the secret used to terminate SSL traffic on 443. Field is left optional to allow SSL routing based on SNI hostname alone. If the SNI host in a listener conflicts with the \"Host\" header field used by an IngressRule, the SNI host is used for termination and value of the Host header is used for routing.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.node.v1alpha1.RuntimeClass": { + "description": "RuntimeClass defines a class of container runtime supported in the cluster. The RuntimeClass is used to determine which container runtime is used to run all containers in a pod. RuntimeClasses are (currently) manually defined by a user or cluster provisioner, and referenced in the PodSpec. The Kubelet is responsible for resolving the RuntimeClassName reference before running the pod. For more details, see https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RuntimeClass" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.node.v1alpha1.RuntimeClassSpec", + "description": "Specification of the RuntimeClass More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "node.k8s.io", + "kind": "RuntimeClass", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.node.v1alpha1.RuntimeClassList": { + "description": "RuntimeClassList is a list of RuntimeClass objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.node.v1alpha1.RuntimeClass" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RuntimeClassList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "node.k8s.io", + "kind": "RuntimeClassList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.node.v1alpha1.RuntimeClassSpec": { + "description": "RuntimeClassSpec is a specification of a RuntimeClass. It contains parameters that are required to describe the RuntimeClass to the Container Runtime Interface (CRI) implementation, as well as any other components that need to understand how the pod will be run. The RuntimeClassSpec is immutable.", + "properties": { + "runtimeHandler": { + "description": "RuntimeHandler specifies the underlying runtime and configuration that the CRI implementation will use to handle pods of this class. The possible values are specific to the node & CRI configuration. It is assumed that all handlers are available on every node, and handlers of the same name are equivalent on every node. For example, a handler called \"runc\" might specify that the runc OCI runtime (using native Linux containers) will be used to run the containers in a pod. The RuntimeHandler must conform to the DNS Label (RFC 1123) requirements and is immutable.", + "type": "string" + } + }, + "required": [ + "runtimeHandler" + ], + "type": "object" + }, + "io.k8s.api.node.v1beta1.RuntimeClass": { + "description": "RuntimeClass defines a class of container runtime supported in the cluster. The RuntimeClass is used to determine which container runtime is used to run all containers in a pod. RuntimeClasses are (currently) manually defined by a user or cluster provisioner, and referenced in the PodSpec. The Kubelet is responsible for resolving the RuntimeClassName reference before running the pod. For more details, see https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "handler": { + "description": "Handler specifies the underlying runtime and configuration that the CRI implementation will use to handle pods of this class. The possible values are specific to the node & CRI configuration. It is assumed that all handlers are available on every node, and handlers of the same name are equivalent on every node. For example, a handler called \"runc\" might specify that the runc OCI runtime (using native Linux containers) will be used to run the containers in a pod. The Handler must conform to the DNS Label (RFC 1123) requirements, and is immutable.", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RuntimeClass" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "handler" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "node.k8s.io", + "kind": "RuntimeClass", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.node.v1beta1.RuntimeClassList": { + "description": "RuntimeClassList is a list of RuntimeClass objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.node.v1beta1.RuntimeClass" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RuntimeClassList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "node.k8s.io", + "kind": "RuntimeClassList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.policy.v1beta1.AllowedCSIDriver": { + "description": "AllowedCSIDriver represents a single inline CSI Driver that is allowed to be used.", + "properties": { + "name": { + "description": "Name is the registered name of the CSI driver", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.policy.v1beta1.AllowedFlexVolume": { + "description": "AllowedFlexVolume represents a single Flexvolume that is allowed to be used.", + "properties": { + "driver": { + "description": "driver is the name of the Flexvolume driver.", + "type": "string" + } + }, + "required": [ + "driver" + ], + "type": "object" + }, + "io.k8s.api.policy.v1beta1.AllowedHostPath": { + "description": "AllowedHostPath defines the host volume conditions that will be enabled by a policy for pods to use. It requires the path prefix to be defined.", + "properties": { + "pathPrefix": { + "description": "pathPrefix is the path prefix that the host volume must match. It does not support `*`. Trailing slashes are trimmed when validating the path prefix with a host path.\n\nExamples: `/foo` would allow `/foo`, `/foo/` and `/foo/bar` `/foo` would not allow `/food` or `/etc/foo`", + "type": "string" + }, + "readOnly": { + "description": "when set to true, will allow host volumes matching the pathPrefix only if all volume mounts are readOnly.", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.policy.v1beta1.Eviction": { + "description": "Eviction evicts a pod from its node subject to certain policies and safety constraints. This is a subresource of Pod. A request to cause such an eviction is created by POSTing to .../pods//evictions.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "deleteOptions": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions", + "description": "DeleteOptions may be provided" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Eviction" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "ObjectMeta describes the pod that is being evicted." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "policy", + "kind": "Eviction", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.policy.v1beta1.FSGroupStrategyOptions": { + "description": "FSGroupStrategyOptions defines the strategy type and options used to create the strategy.", + "properties": { + "ranges": { + "description": "ranges are the allowed ranges of fs groups. If you would like to force a single fs group then supply a single range with the same start and end. Required for MustRunAs.", + "items": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.IDRange" + }, + "type": "array" + }, + "rule": { + "description": "rule is the strategy that will dictate what FSGroup is used in the SecurityContext.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.policy.v1beta1.HostPortRange": { + "description": "HostPortRange defines a range of host ports that will be enabled by a policy for pods to use. It requires both the start and end to be defined.", + "properties": { + "max": { + "description": "max is the end of the range, inclusive.", + "format": "int32", + "type": "integer" + }, + "min": { + "description": "min is the start of the range, inclusive.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "min", + "max" + ], + "type": "object" + }, + "io.k8s.api.policy.v1beta1.IDRange": { + "description": "IDRange provides a min/max of an allowed range of IDs.", + "properties": { + "max": { + "description": "max is the end of the range, inclusive.", + "format": "int64", + "type": "integer" + }, + "min": { + "description": "min is the start of the range, inclusive.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "min", + "max" + ], + "type": "object" + }, + "io.k8s.api.policy.v1beta1.PodDisruptionBudget": { + "description": "PodDisruptionBudget is an object to define the max disruption that can be caused to a collection of pods", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodDisruptionBudget" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.PodDisruptionBudgetSpec", + "description": "Specification of the desired behavior of the PodDisruptionBudget." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.PodDisruptionBudgetStatus", + "description": "Most recently observed status of the PodDisruptionBudget." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "policy", + "kind": "PodDisruptionBudget", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.policy.v1beta1.PodDisruptionBudgetList": { + "description": "PodDisruptionBudgetList is a collection of PodDisruptionBudgets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "items": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.PodDisruptionBudget" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodDisruptionBudgetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "policy", + "kind": "PodDisruptionBudgetList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.policy.v1beta1.PodDisruptionBudgetSpec": { + "description": "PodDisruptionBudgetSpec is a description of a PodDisruptionBudget.", + "properties": { + "maxUnavailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "An eviction is allowed if at most \"maxUnavailable\" pods selected by \"selector\" are unavailable after the eviction, i.e. even in absence of the evicted pod. For example, one can prevent all voluntary evictions by specifying 0. This is a mutually exclusive setting with \"minAvailable\"." + }, + "minAvailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "An eviction is allowed if at least \"minAvailable\" pods selected by \"selector\" will still be available after the eviction, i.e. even in the absence of the evicted pod. So for example you can prevent all voluntary evictions by specifying \"100%\"." + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Label query over pods whose evictions are managed by the disruption budget." + } + }, + "type": "object" + }, + "io.k8s.api.policy.v1beta1.PodDisruptionBudgetStatus": { + "description": "PodDisruptionBudgetStatus represents information about the status of a PodDisruptionBudget. Status may trail the actual state of a system.", + "properties": { + "currentHealthy": { + "description": "current number of healthy pods", + "format": "int32", + "type": "integer" + }, + "desiredHealthy": { + "description": "minimum desired number of healthy pods", + "format": "int32", + "type": "integer" + }, + "disruptedPods": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + }, + "description": "DisruptedPods contains information about pods whose eviction was processed by the API server eviction subresource handler but has not yet been observed by the PodDisruptionBudget controller. A pod will be in this map from the time when the API server processed the eviction request to the time when the pod is seen by PDB controller as having been marked for deletion (or after a timeout). The key in the map is the name of the pod and the value is the time when the API server processed the eviction request. If the deletion didn't occur and a pod is still there it will be removed from the list automatically by PodDisruptionBudget controller after some time. If everything goes smooth this map should be empty for the most of the time. Large number of entries in the map may indicate problems with pod deletions.", + "type": "object" + }, + "disruptionsAllowed": { + "description": "Number of pod disruptions that are currently allowed.", + "format": "int32", + "type": "integer" + }, + "expectedPods": { + "description": "total number of pods counted by this disruption budget", + "format": "int32", + "type": "integer" + }, + "observedGeneration": { + "description": "Most recent generation observed when updating this PDB status. PodDisruptionsAllowed and other status informatio is valid only if observedGeneration equals to PDB's object generation.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "disruptionsAllowed", + "currentHealthy", + "desiredHealthy", + "expectedPods" + ], + "type": "object" + }, + "io.k8s.api.policy.v1beta1.PodSecurityPolicy": { + "description": "PodSecurityPolicy governs the ability to make requests that affect the Security Context that will be applied to a pod and container.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodSecurityPolicy" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.PodSecurityPolicySpec", + "description": "spec defines the policy enforced." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "policy", + "kind": "PodSecurityPolicy", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.policy.v1beta1.PodSecurityPolicyList": { + "description": "PodSecurityPolicyList is a list of PodSecurityPolicy objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.PodSecurityPolicy" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodSecurityPolicyList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "policy", + "kind": "PodSecurityPolicyList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.policy.v1beta1.PodSecurityPolicySpec": { + "description": "PodSecurityPolicySpec defines the policy enforced.", + "properties": { + "allowPrivilegeEscalation": { + "description": "allowPrivilegeEscalation determines if a pod can request to allow privilege escalation. If unspecified, defaults to true.", + "type": "boolean" + }, + "allowedCSIDrivers": { + "description": "AllowedCSIDrivers is a whitelist of inline CSI drivers that must be explicitly set to be embedded within a pod spec. An empty value means no CSI drivers can run inline within a pod spec.", + "items": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.AllowedCSIDriver" + }, + "type": "array" + }, + "allowedCapabilities": { + "description": "allowedCapabilities is a list of capabilities that can be requested to add to the container. Capabilities in this field may be added at the pod author's discretion. You must not list a capability in both allowedCapabilities and requiredDropCapabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, + "allowedFlexVolumes": { + "description": "allowedFlexVolumes is a whitelist of allowed Flexvolumes. Empty or nil indicates that all Flexvolumes may be used. This parameter is effective only when the usage of the Flexvolumes is allowed in the \"volumes\" field.", + "items": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.AllowedFlexVolume" + }, + "type": "array" + }, + "allowedHostPaths": { + "description": "allowedHostPaths is a white list of allowed host paths. Empty indicates that all host paths may be used.", + "items": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.AllowedHostPath" + }, + "type": "array" + }, + "allowedProcMountTypes": { + "description": "AllowedProcMountTypes is a whitelist of allowed ProcMountTypes. Empty or nil indicates that only the DefaultProcMountType may be used. This requires the ProcMountType feature flag to be enabled.", + "items": { + "type": "string" + }, + "type": "array" + }, + "allowedUnsafeSysctls": { + "description": "allowedUnsafeSysctls is a list of explicitly allowed unsafe sysctls, defaults to none. Each entry is either a plain sysctl name or ends in \"*\" in which case it is considered as a prefix of allowed sysctls. Single * means all unsafe sysctls are allowed. Kubelet has to whitelist all allowed unsafe sysctls explicitly to avoid rejection.\n\nExamples: e.g. \"foo/*\" allows \"foo/bar\", \"foo/baz\", etc. e.g. \"foo.*\" allows \"foo.bar\", \"foo.baz\", etc.", + "items": { + "type": "string" + }, + "type": "array" + }, + "defaultAddCapabilities": { + "description": "defaultAddCapabilities is the default set of capabilities that will be added to the container unless the pod spec specifically drops the capability. You may not list a capability in both defaultAddCapabilities and requiredDropCapabilities. Capabilities added here are implicitly allowed, and need not be included in the allowedCapabilities list.", + "items": { + "type": "string" + }, + "type": "array" + }, + "defaultAllowPrivilegeEscalation": { + "description": "defaultAllowPrivilegeEscalation controls the default setting for whether a process can gain more privileges than its parent process.", + "type": "boolean" + }, + "forbiddenSysctls": { + "description": "forbiddenSysctls is a list of explicitly forbidden sysctls, defaults to none. Each entry is either a plain sysctl name or ends in \"*\" in which case it is considered as a prefix of forbidden sysctls. Single * means all sysctls are forbidden.\n\nExamples: e.g. \"foo/*\" forbids \"foo/bar\", \"foo/baz\", etc. e.g. \"foo.*\" forbids \"foo.bar\", \"foo.baz\", etc.", + "items": { + "type": "string" + }, + "type": "array" + }, + "fsGroup": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.FSGroupStrategyOptions", + "description": "fsGroup is the strategy that will dictate what fs group is used by the SecurityContext." + }, + "hostIPC": { + "description": "hostIPC determines if the policy allows the use of HostIPC in the pod spec.", + "type": "boolean" + }, + "hostNetwork": { + "description": "hostNetwork determines if the policy allows the use of HostNetwork in the pod spec.", + "type": "boolean" + }, + "hostPID": { + "description": "hostPID determines if the policy allows the use of HostPID in the pod spec.", + "type": "boolean" + }, + "hostPorts": { + "description": "hostPorts determines which host port ranges are allowed to be exposed.", + "items": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.HostPortRange" + }, + "type": "array" + }, + "privileged": { + "description": "privileged determines if a pod can request to be run as privileged.", + "type": "boolean" + }, + "readOnlyRootFilesystem": { + "description": "readOnlyRootFilesystem when set to true will force containers to run with a read only root file system. If the container specifically requests to run with a non-read only root file system the PSP should deny the pod. If set to false the container may run with a read only root file system if it wishes but it will not be forced to.", + "type": "boolean" + }, + "requiredDropCapabilities": { + "description": "requiredDropCapabilities are the capabilities that will be dropped from the container. These are required to be dropped and cannot be added.", + "items": { + "type": "string" + }, + "type": "array" + }, + "runAsGroup": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.RunAsGroupStrategyOptions", + "description": "RunAsGroup is the strategy that will dictate the allowable RunAsGroup values that may be set. If this field is omitted, the pod's RunAsGroup can take any value. This field requires the RunAsGroup feature gate to be enabled." + }, + "runAsUser": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.RunAsUserStrategyOptions", + "description": "runAsUser is the strategy that will dictate the allowable RunAsUser values that may be set." + }, + "seLinux": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.SELinuxStrategyOptions", + "description": "seLinux is the strategy that will dictate the allowable labels that may be set." + }, + "supplementalGroups": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.SupplementalGroupsStrategyOptions", + "description": "supplementalGroups is the strategy that will dictate what supplemental groups are used by the SecurityContext." + }, + "volumes": { + "description": "volumes is a white list of allowed volume plugins. Empty indicates that no volumes may be used. To allow all volumes you may use '*'.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "seLinux", + "runAsUser", + "supplementalGroups", + "fsGroup" + ], + "type": "object" + }, + "io.k8s.api.policy.v1beta1.RunAsGroupStrategyOptions": { + "description": "RunAsGroupStrategyOptions defines the strategy type and any options used to create the strategy.", + "properties": { + "ranges": { + "description": "ranges are the allowed ranges of gids that may be used. If you would like to force a single gid then supply a single range with the same start and end. Required for MustRunAs.", + "items": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.IDRange" + }, + "type": "array" + }, + "rule": { + "description": "rule is the strategy that will dictate the allowable RunAsGroup values that may be set.", + "type": "string" + } + }, + "required": [ + "rule" + ], + "type": "object" + }, + "io.k8s.api.policy.v1beta1.RunAsUserStrategyOptions": { + "description": "RunAsUserStrategyOptions defines the strategy type and any options used to create the strategy.", + "properties": { + "ranges": { + "description": "ranges are the allowed ranges of uids that may be used. If you would like to force a single uid then supply a single range with the same start and end. Required for MustRunAs.", + "items": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.IDRange" + }, + "type": "array" + }, + "rule": { + "description": "rule is the strategy that will dictate the allowable RunAsUser values that may be set.", + "type": "string" + } + }, + "required": [ + "rule" + ], + "type": "object" + }, + "io.k8s.api.policy.v1beta1.SELinuxStrategyOptions": { + "description": "SELinuxStrategyOptions defines the strategy type and any options used to create the strategy.", + "properties": { + "rule": { + "description": "rule is the strategy that will dictate the allowable labels that may be set.", + "type": "string" + }, + "seLinuxOptions": { + "$ref": "#/definitions/io.k8s.api.core.v1.SELinuxOptions", + "description": "seLinuxOptions required to run as; required for MustRunAs More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/" + } + }, + "required": [ + "rule" + ], + "type": "object" + }, + "io.k8s.api.policy.v1beta1.SupplementalGroupsStrategyOptions": { + "description": "SupplementalGroupsStrategyOptions defines the strategy type and options used to create the strategy.", + "properties": { + "ranges": { + "description": "ranges are the allowed ranges of supplemental groups. If you would like to force a single supplemental group then supply a single range with the same start and end. Required for MustRunAs.", + "items": { + "$ref": "#/definitions/io.k8s.api.policy.v1beta1.IDRange" + }, + "type": "array" + }, + "rule": { + "description": "rule is the strategy that will dictate what supplemental groups is used in the SecurityContext.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.rbac.v1.AggregationRule": { + "description": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole", + "properties": { + "clusterRoleSelectors": { + "description": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.rbac.v1.ClusterRole": { + "description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.", + "properties": { + "aggregationRule": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.AggregationRule", + "description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller." + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRole" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "rules": { + "description": "Rules holds all the PolicyRules for this ClusterRole", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.PolicyRule" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRole", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.ClusterRoleBinding": { + "description": "ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace, and adds who information via Subject.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRoleBinding" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "roleRef": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.RoleRef", + "description": "RoleRef can only reference a ClusterRole in the global namespace. If the RoleRef cannot be resolved, the Authorizer must return an error." + }, + "subjects": { + "description": "Subjects holds references to the objects the role applies to.", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.Subject" + }, + "type": "array" + } + }, + "required": [ + "roleRef" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRoleBinding", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.ClusterRoleBindingList": { + "description": "ClusterRoleBindingList is a collection of ClusterRoleBindings", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of ClusterRoleBindings", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.ClusterRoleBinding" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRoleBindingList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRoleBindingList", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.ClusterRoleList": { + "description": "ClusterRoleList is a collection of ClusterRoles", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of ClusterRoles", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.ClusterRole" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRoleList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRoleList", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.PolicyRule": { + "description": "PolicyRule holds information that describes a policy rule, but does not contain information about who the rule applies to or which namespace the rule applies to.", + "properties": { + "apiGroups": { + "description": "APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of the enumerated resources in any API group will be allowed.", + "items": { + "type": "string" + }, + "type": "array" + }, + "nonResourceURLs": { + "description": "NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path Since non-resource URLs are not namespaced, this field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. Rules can either apply to API resources (such as \"pods\" or \"secrets\") or non-resource URL paths (such as \"/api\"), but not both.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resourceNames": { + "description": "ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resources": { + "description": "Resources is a list of resources this rule applies to. ResourceAll represents all resources.", + "items": { + "type": "string" + }, + "type": "array" + }, + "verbs": { + "description": "Verbs is a list of Verbs that apply to ALL the ResourceKinds and AttributeRestrictions contained in this rule. VerbAll represents all kinds.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "verbs" + ], + "type": "object" + }, + "io.k8s.api.rbac.v1.Role": { + "description": "Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Role" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "rules": { + "description": "Rules holds all the PolicyRules for this Role", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.PolicyRule" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "Role", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.RoleBinding": { + "description": "RoleBinding references a role, but does not contain it. It can reference a Role in the same namespace or a ClusterRole in the global namespace. It adds who information via Subjects and namespace information by which namespace it exists in. RoleBindings in a given namespace only have effect in that namespace.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RoleBinding" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "roleRef": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.RoleRef", + "description": "RoleRef can reference a Role in the current namespace or a ClusterRole in the global namespace. If the RoleRef cannot be resolved, the Authorizer must return an error." + }, + "subjects": { + "description": "Subjects holds references to the objects the role applies to.", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.Subject" + }, + "type": "array" + } + }, + "required": [ + "roleRef" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "RoleBinding", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.RoleBindingList": { + "description": "RoleBindingList is a collection of RoleBindings", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of RoleBindings", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.RoleBinding" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RoleBindingList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "RoleBindingList", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.RoleList": { + "description": "RoleList is a collection of Roles", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of Roles", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.Role" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RoleList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "RoleList", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.RoleRef": { + "description": "RoleRef contains information that points to the role being used", + "properties": { + "apiGroup": { + "description": "APIGroup is the group for the resource being referenced", + "type": "string" + }, + "kind": { + "description": "Kind is the type of resource being referenced", + "type": "string" + }, + "name": { + "description": "Name is the name of resource being referenced", + "type": "string" + } + }, + "required": [ + "apiGroup", + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.rbac.v1.Subject": { + "description": "Subject contains a reference to the object or user identities a role binding applies to. This can either hold a direct API object reference, or a value for non-objects such as user and group names.", + "properties": { + "apiGroup": { + "description": "APIGroup holds the API group of the referenced subject. Defaults to \"\" for ServiceAccount subjects. Defaults to \"rbac.authorization.k8s.io\" for User and Group subjects.", + "type": "string" + }, + "kind": { + "description": "Kind of object being referenced. Values defined by this API group are \"User\", \"Group\", and \"ServiceAccount\". If the Authorizer does not recognized the kind value, the Authorizer should report an error.", + "type": "string" + }, + "name": { + "description": "Name of the object being referenced.", + "type": "string" + }, + "namespace": { + "description": "Namespace of the referenced object. If the object kind is non-namespace, such as \"User\" or \"Group\", and this value is not empty the Authorizer should report an error.", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.rbac.v1alpha1.AggregationRule": { + "description": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole", + "properties": { + "clusterRoleSelectors": { + "description": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.rbac.v1alpha1.ClusterRole": { + "description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.", + "properties": { + "aggregationRule": { + "$ref": "#/definitions/io.k8s.api.rbac.v1alpha1.AggregationRule", + "description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller." + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRole" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "rules": { + "description": "Rules holds all the PolicyRules for this ClusterRole", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1alpha1.PolicyRule" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRole", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.rbac.v1alpha1.ClusterRoleBinding": { + "description": "ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace, and adds who information via Subject.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRoleBinding" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "roleRef": { + "$ref": "#/definitions/io.k8s.api.rbac.v1alpha1.RoleRef", + "description": "RoleRef can only reference a ClusterRole in the global namespace. If the RoleRef cannot be resolved, the Authorizer must return an error." + }, + "subjects": { + "description": "Subjects holds references to the objects the role applies to.", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1alpha1.Subject" + }, + "type": "array" + } + }, + "required": [ + "roleRef" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRoleBinding", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.rbac.v1alpha1.ClusterRoleBindingList": { + "description": "ClusterRoleBindingList is a collection of ClusterRoleBindings", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of ClusterRoleBindings", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1alpha1.ClusterRoleBinding" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRoleBindingList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRoleBindingList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.rbac.v1alpha1.ClusterRoleList": { + "description": "ClusterRoleList is a collection of ClusterRoles", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of ClusterRoles", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1alpha1.ClusterRole" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRoleList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRoleList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.rbac.v1alpha1.PolicyRule": { + "description": "PolicyRule holds information that describes a policy rule, but does not contain information about who the rule applies to or which namespace the rule applies to.", + "properties": { + "apiGroups": { + "description": "APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of the enumerated resources in any API group will be allowed.", + "items": { + "type": "string" + }, + "type": "array" + }, + "nonResourceURLs": { + "description": "NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path This name is intentionally different than the internal type so that the DefaultConvert works nicely and because the ordering may be different. Since non-resource URLs are not namespaced, this field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. Rules can either apply to API resources (such as \"pods\" or \"secrets\") or non-resource URL paths (such as \"/api\"), but not both.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resourceNames": { + "description": "ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resources": { + "description": "Resources is a list of resources this rule applies to. ResourceAll represents all resources.", + "items": { + "type": "string" + }, + "type": "array" + }, + "verbs": { + "description": "Verbs is a list of Verbs that apply to ALL the ResourceKinds and AttributeRestrictions contained in this rule. VerbAll represents all kinds.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "verbs" + ], + "type": "object" + }, + "io.k8s.api.rbac.v1alpha1.Role": { + "description": "Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Role" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "rules": { + "description": "Rules holds all the PolicyRules for this Role", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1alpha1.PolicyRule" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "Role", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.rbac.v1alpha1.RoleBinding": { + "description": "RoleBinding references a role, but does not contain it. It can reference a Role in the same namespace or a ClusterRole in the global namespace. It adds who information via Subjects and namespace information by which namespace it exists in. RoleBindings in a given namespace only have effect in that namespace.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RoleBinding" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "roleRef": { + "$ref": "#/definitions/io.k8s.api.rbac.v1alpha1.RoleRef", + "description": "RoleRef can reference a Role in the current namespace or a ClusterRole in the global namespace. If the RoleRef cannot be resolved, the Authorizer must return an error." + }, + "subjects": { + "description": "Subjects holds references to the objects the role applies to.", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1alpha1.Subject" + }, + "type": "array" + } + }, + "required": [ + "roleRef" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "RoleBinding", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.rbac.v1alpha1.RoleBindingList": { + "description": "RoleBindingList is a collection of RoleBindings", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of RoleBindings", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1alpha1.RoleBinding" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RoleBindingList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "RoleBindingList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.rbac.v1alpha1.RoleList": { + "description": "RoleList is a collection of Roles", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of Roles", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1alpha1.Role" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RoleList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "RoleList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.rbac.v1alpha1.RoleRef": { + "description": "RoleRef contains information that points to the role being used", + "properties": { + "apiGroup": { + "description": "APIGroup is the group for the resource being referenced", + "type": "string" + }, + "kind": { + "description": "Kind is the type of resource being referenced", + "type": "string" + }, + "name": { + "description": "Name is the name of resource being referenced", + "type": "string" + } + }, + "required": [ + "apiGroup", + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.rbac.v1alpha1.Subject": { + "description": "Subject contains a reference to the object or user identities a role binding applies to. This can either hold a direct API object reference, or a value for non-objects such as user and group names.", + "properties": { + "apiVersion": { + "description": "APIVersion holds the API group and version of the referenced subject. Defaults to \"v1\" for ServiceAccount subjects. Defaults to \"rbac.authorization.k8s.io/v1alpha1\" for User and Group subjects.", + "type": "string" + }, + "kind": { + "description": "Kind of object being referenced. Values defined by this API group are \"User\", \"Group\", and \"ServiceAccount\". If the Authorizer does not recognized the kind value, the Authorizer should report an error.", + "type": "string" + }, + "name": { + "description": "Name of the object being referenced.", + "type": "string" + }, + "namespace": { + "description": "Namespace of the referenced object. If the object kind is non-namespace, such as \"User\" or \"Group\", and this value is not empty the Authorizer should report an error.", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.rbac.v1beta1.AggregationRule": { + "description": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole", + "properties": { + "clusterRoleSelectors": { + "description": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.rbac.v1beta1.ClusterRole": { + "description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.", + "properties": { + "aggregationRule": { + "$ref": "#/definitions/io.k8s.api.rbac.v1beta1.AggregationRule", + "description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller." + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRole" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "rules": { + "description": "Rules holds all the PolicyRules for this ClusterRole", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1beta1.PolicyRule" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRole", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.rbac.v1beta1.ClusterRoleBinding": { + "description": "ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace, and adds who information via Subject.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRoleBinding" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "roleRef": { + "$ref": "#/definitions/io.k8s.api.rbac.v1beta1.RoleRef", + "description": "RoleRef can only reference a ClusterRole in the global namespace. If the RoleRef cannot be resolved, the Authorizer must return an error." + }, + "subjects": { + "description": "Subjects holds references to the objects the role applies to.", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1beta1.Subject" + }, + "type": "array" + } + }, + "required": [ + "roleRef" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRoleBinding", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.rbac.v1beta1.ClusterRoleBindingList": { + "description": "ClusterRoleBindingList is a collection of ClusterRoleBindings", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of ClusterRoleBindings", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1beta1.ClusterRoleBinding" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRoleBindingList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRoleBindingList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.rbac.v1beta1.ClusterRoleList": { + "description": "ClusterRoleList is a collection of ClusterRoles", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of ClusterRoles", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1beta1.ClusterRole" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRoleList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRoleList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.rbac.v1beta1.PolicyRule": { + "description": "PolicyRule holds information that describes a policy rule, but does not contain information about who the rule applies to or which namespace the rule applies to.", + "properties": { + "apiGroups": { + "description": "APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of the enumerated resources in any API group will be allowed.", + "items": { + "type": "string" + }, + "type": "array" + }, + "nonResourceURLs": { + "description": "NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path Since non-resource URLs are not namespaced, this field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. Rules can either apply to API resources (such as \"pods\" or \"secrets\") or non-resource URL paths (such as \"/api\"), but not both.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resourceNames": { + "description": "ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resources": { + "description": "Resources is a list of resources this rule applies to. '*' represents all resources in the specified apiGroups. '*/foo' represents the subresource 'foo' for all resources in the specified apiGroups.", + "items": { + "type": "string" + }, + "type": "array" + }, + "verbs": { + "description": "Verbs is a list of Verbs that apply to ALL the ResourceKinds and AttributeRestrictions contained in this rule. VerbAll represents all kinds.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "verbs" + ], + "type": "object" + }, + "io.k8s.api.rbac.v1beta1.Role": { + "description": "Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Role" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "rules": { + "description": "Rules holds all the PolicyRules for this Role", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1beta1.PolicyRule" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "Role", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.rbac.v1beta1.RoleBinding": { + "description": "RoleBinding references a role, but does not contain it. It can reference a Role in the same namespace or a ClusterRole in the global namespace. It adds who information via Subjects and namespace information by which namespace it exists in. RoleBindings in a given namespace only have effect in that namespace.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RoleBinding" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "roleRef": { + "$ref": "#/definitions/io.k8s.api.rbac.v1beta1.RoleRef", + "description": "RoleRef can reference a Role in the current namespace or a ClusterRole in the global namespace. If the RoleRef cannot be resolved, the Authorizer must return an error." + }, + "subjects": { + "description": "Subjects holds references to the objects the role applies to.", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1beta1.Subject" + }, + "type": "array" + } + }, + "required": [ + "roleRef" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "RoleBinding", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.rbac.v1beta1.RoleBindingList": { + "description": "RoleBindingList is a collection of RoleBindings", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of RoleBindings", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1beta1.RoleBinding" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RoleBindingList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "RoleBindingList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.rbac.v1beta1.RoleList": { + "description": "RoleList is a collection of Roles", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of Roles", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1beta1.Role" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RoleList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "RoleList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.rbac.v1beta1.RoleRef": { + "description": "RoleRef contains information that points to the role being used", + "properties": { + "apiGroup": { + "description": "APIGroup is the group for the resource being referenced", + "type": "string" + }, + "kind": { + "description": "Kind is the type of resource being referenced", + "type": "string" + }, + "name": { + "description": "Name is the name of resource being referenced", + "type": "string" + } + }, + "required": [ + "apiGroup", + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.rbac.v1beta1.Subject": { + "description": "Subject contains a reference to the object or user identities a role binding applies to. This can either hold a direct API object reference, or a value for non-objects such as user and group names.", + "properties": { + "apiGroup": { + "description": "APIGroup holds the API group of the referenced subject. Defaults to \"\" for ServiceAccount subjects. Defaults to \"rbac.authorization.k8s.io\" for User and Group subjects.", + "type": "string" + }, + "kind": { + "description": "Kind of object being referenced. Values defined by this API group are \"User\", \"Group\", and \"ServiceAccount\". If the Authorizer does not recognized the kind value, the Authorizer should report an error.", + "type": "string" + }, + "name": { + "description": "Name of the object being referenced.", + "type": "string" + }, + "namespace": { + "description": "Namespace of the referenced object. If the object kind is non-namespace, such as \"User\" or \"Group\", and this value is not empty the Authorizer should report an error.", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.scheduling.v1.PriorityClass": { + "description": "PriorityClass defines mapping from a priority class name to the priority integer value. The value can be any valid integer.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "description": { + "description": "description is an arbitrary string that usually provides guidelines on when this priority class should be used.", + "type": "string" + }, + "globalDefault": { + "description": "globalDefault specifies whether this PriorityClass should be considered as the default priority for pods that do not have any priority class. Only one PriorityClass can be marked as `globalDefault`. However, if more than one PriorityClasses exists with their `globalDefault` field set to true, the smallest value of such global default PriorityClasses will be used as the default priority.", + "type": "boolean" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PriorityClass" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "value": { + "description": "The value of this priority class. This is the actual priority that pods receive when they have the name of this class in their pod spec.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "value" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "scheduling.k8s.io", + "kind": "PriorityClass", + "version": "v1" + } + ] + }, + "io.k8s.api.scheduling.v1.PriorityClassList": { + "description": "PriorityClassList is a collection of priority classes.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of PriorityClasses", + "items": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1.PriorityClass" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PriorityClassList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "scheduling.k8s.io", + "kind": "PriorityClassList", + "version": "v1" + } + ] + }, + "io.k8s.api.scheduling.v1alpha1.PriorityClass": { + "description": "DEPRECATED - This group version of PriorityClass is deprecated by scheduling.k8s.io/v1/PriorityClass. PriorityClass defines mapping from a priority class name to the priority integer value. The value can be any valid integer.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "description": { + "description": "description is an arbitrary string that usually provides guidelines on when this priority class should be used.", + "type": "string" + }, + "globalDefault": { + "description": "globalDefault specifies whether this PriorityClass should be considered as the default priority for pods that do not have any priority class. Only one PriorityClass can be marked as `globalDefault`. However, if more than one PriorityClasses exists with their `globalDefault` field set to true, the smallest value of such global default PriorityClasses will be used as the default priority.", + "type": "boolean" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PriorityClass" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "value": { + "description": "The value of this priority class. This is the actual priority that pods receive when they have the name of this class in their pod spec.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "value" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "scheduling.k8s.io", + "kind": "PriorityClass", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.scheduling.v1alpha1.PriorityClassList": { + "description": "PriorityClassList is a collection of priority classes.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of PriorityClasses", + "items": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.PriorityClass" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PriorityClassList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "scheduling.k8s.io", + "kind": "PriorityClassList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.scheduling.v1beta1.PriorityClass": { + "description": "DEPRECATED - This group version of PriorityClass is deprecated by scheduling.k8s.io/v1/PriorityClass. PriorityClass defines mapping from a priority class name to the priority integer value. The value can be any valid integer.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "description": { + "description": "description is an arbitrary string that usually provides guidelines on when this priority class should be used.", + "type": "string" + }, + "globalDefault": { + "description": "globalDefault specifies whether this PriorityClass should be considered as the default priority for pods that do not have any priority class. Only one PriorityClass can be marked as `globalDefault`. However, if more than one PriorityClasses exists with their `globalDefault` field set to true, the smallest value of such global default PriorityClasses will be used as the default priority.", + "type": "boolean" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PriorityClass" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "value": { + "description": "The value of this priority class. This is the actual priority that pods receive when they have the name of this class in their pod spec.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "value" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "scheduling.k8s.io", + "kind": "PriorityClass", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.scheduling.v1beta1.PriorityClassList": { + "description": "PriorityClassList is a collection of priority classes.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of PriorityClasses", + "items": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1beta1.PriorityClass" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PriorityClassList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "scheduling.k8s.io", + "kind": "PriorityClassList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.settings.v1alpha1.PodPreset": { + "description": "PodPreset is a policy resource that defines additional runtime requirements for a Pod.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodPreset" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.settings.v1alpha1.PodPresetSpec" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "settings.k8s.io", + "kind": "PodPreset", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.settings.v1alpha1.PodPresetList": { + "description": "PodPresetList is a list of PodPreset objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.settings.v1alpha1.PodPreset" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodPresetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "settings.k8s.io", + "kind": "PodPresetList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.settings.v1alpha1.PodPresetSpec": { + "description": "PodPresetSpec is a description of a pod preset.", + "properties": { + "env": { + "description": "Env defines the collection of EnvVar to inject into containers.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EnvVar" + }, + "type": "array" + }, + "envFrom": { + "description": "EnvFrom defines the collection of EnvFromSource to inject into containers.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EnvFromSource" + }, + "type": "array" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Selector is a label query over a set of resources, in this case pods. Required." + }, + "volumeMounts": { + "description": "VolumeMounts defines the collection of VolumeMount to inject into containers.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.VolumeMount" + }, + "type": "array" + }, + "volumes": { + "description": "Volumes defines the collection of Volume to inject into the pod.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Volume" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.storage.v1.StorageClass": { + "description": "StorageClass describes the parameters for a class of storage for which PersistentVolumes can be dynamically provisioned.\n\nStorageClasses are non-namespaced; the name of the storage class according to etcd is in ObjectMeta.Name.", + "properties": { + "allowVolumeExpansion": { + "description": "AllowVolumeExpansion shows whether the storage class allow volume expand", + "type": "boolean" + }, + "allowedTopologies": { + "description": "Restrict the node topologies where volumes can be dynamically provisioned. Each volume plugin defines its own supported topology specifications. An empty TopologySelectorTerm list means there is no topology restriction. This field is only honored by servers that enable the VolumeScheduling feature.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.TopologySelectorTerm" + }, + "type": "array" + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StorageClass" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "mountOptions": { + "description": "Dynamically provisioned PersistentVolumes of this storage class are created with these mountOptions, e.g. [\"ro\", \"soft\"]. Not validated - mount of the PVs will simply fail if one is invalid.", + "items": { + "type": "string" + }, + "type": "array" + }, + "parameters": { + "additionalProperties": { + "type": "string" + }, + "description": "Parameters holds the parameters for the provisioner that should create volumes of this storage class.", + "type": "object" + }, + "provisioner": { + "description": "Provisioner indicates the type of the provisioner.", + "type": "string" + }, + "reclaimPolicy": { + "description": "Dynamically provisioned PersistentVolumes of this storage class are created with this reclaimPolicy. Defaults to Delete.", + "type": "string" + }, + "volumeBindingMode": { + "description": "VolumeBindingMode indicates how PersistentVolumeClaims should be provisioned and bound. When unset, VolumeBindingImmediate is used. This field is only honored by servers that enable the VolumeScheduling feature.", + "type": "string" + } + }, + "required": [ + "provisioner" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "StorageClass", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.StorageClassList": { + "description": "StorageClassList is a collection of storage classes.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of StorageClasses", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StorageClassList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "StorageClassList", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.VolumeAttachment": { + "description": "VolumeAttachment captures the intent to attach or detach the specified volume to/from the specified node.\n\nVolumeAttachment objects are non-namespaced.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "VolumeAttachment" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachmentSpec", + "description": "Specification of the desired attach/detach volume behavior. Populated by the Kubernetes system." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachmentStatus", + "description": "Status of the VolumeAttachment request. Populated by the entity completing the attach or detach operation, i.e. the external-attacher." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "VolumeAttachment", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.VolumeAttachmentList": { + "description": "VolumeAttachmentList is a collection of VolumeAttachment objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of VolumeAttachments", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "VolumeAttachmentList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "VolumeAttachmentList", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.VolumeAttachmentSource": { + "description": "VolumeAttachmentSource represents a volume that should be attached. Right now only PersistenVolumes can be attached via external attacher, in future we may allow also inline volumes in pods. Exactly one member can be set.", + "properties": { + "persistentVolumeName": { + "description": "Name of the persistent volume to attach.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.storage.v1.VolumeAttachmentSpec": { + "description": "VolumeAttachmentSpec is the specification of a VolumeAttachment request.", + "properties": { + "attacher": { + "description": "Attacher indicates the name of the volume driver that MUST handle this request. This is the name returned by GetPluginName().", + "type": "string" + }, + "nodeName": { + "description": "The node that the volume should be attached to.", + "type": "string" + }, + "source": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachmentSource", + "description": "Source represents the volume that should be attached." + } + }, + "required": [ + "attacher", + "source", + "nodeName" + ], + "type": "object" + }, + "io.k8s.api.storage.v1.VolumeAttachmentStatus": { + "description": "VolumeAttachmentStatus is the status of a VolumeAttachment request.", + "properties": { + "attachError": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeError", + "description": "The last error encountered during attach operation, if any. This field must only be set by the entity completing the attach operation, i.e. the external-attacher." + }, + "attached": { + "description": "Indicates the volume is successfully attached. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.", + "type": "boolean" + }, + "attachmentMetadata": { + "additionalProperties": { + "type": "string" + }, + "description": "Upon successful attach, this field is populated with any information returned by the attach operation that must be passed into subsequent WaitForAttach or Mount calls. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.", + "type": "object" + }, + "detachError": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeError", + "description": "The last error encountered during detach operation, if any. This field must only be set by the entity completing the detach operation, i.e. the external-attacher." + } + }, + "required": [ + "attached" + ], + "type": "object" + }, + "io.k8s.api.storage.v1.VolumeError": { + "description": "VolumeError captures an error encountered during a volume operation.", + "properties": { + "message": { + "description": "String detailing the error encountered during Attach or Detach operation. This string may be logged, so it should not contain sensitive information.", + "type": "string" + }, + "time": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Time the error was encountered." + } + }, + "type": "object" + }, + "io.k8s.api.storage.v1alpha1.VolumeAttachment": { + "description": "VolumeAttachment captures the intent to attach or detach the specified volume to/from the specified node.\n\nVolumeAttachment objects are non-namespaced.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "VolumeAttachment" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttachmentSpec", + "description": "Specification of the desired attach/detach volume behavior. Populated by the Kubernetes system." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttachmentStatus", + "description": "Status of the VolumeAttachment request. Populated by the entity completing the attach or detach operation, i.e. the external-attacher." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "VolumeAttachment", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.storage.v1alpha1.VolumeAttachmentList": { + "description": "VolumeAttachmentList is a collection of VolumeAttachment objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of VolumeAttachments", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttachment" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "VolumeAttachmentList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "VolumeAttachmentList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.storage.v1alpha1.VolumeAttachmentSource": { + "description": "VolumeAttachmentSource represents a volume that should be attached. Right now only PersistenVolumes can be attached via external attacher, in future we may allow also inline volumes in pods. Exactly one member can be set.", + "properties": { + "persistentVolumeName": { + "description": "Name of the persistent volume to attach.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.storage.v1alpha1.VolumeAttachmentSpec": { + "description": "VolumeAttachmentSpec is the specification of a VolumeAttachment request.", + "properties": { + "attacher": { + "description": "Attacher indicates the name of the volume driver that MUST handle this request. This is the name returned by GetPluginName().", + "type": "string" + }, + "nodeName": { + "description": "The node that the volume should be attached to.", + "type": "string" + }, + "source": { + "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttachmentSource", + "description": "Source represents the volume that should be attached." + } + }, + "required": [ + "attacher", + "source", + "nodeName" + ], + "type": "object" + }, + "io.k8s.api.storage.v1alpha1.VolumeAttachmentStatus": { + "description": "VolumeAttachmentStatus is the status of a VolumeAttachment request.", + "properties": { + "attachError": { + "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeError", + "description": "The last error encountered during attach operation, if any. This field must only be set by the entity completing the attach operation, i.e. the external-attacher." + }, + "attached": { + "description": "Indicates the volume is successfully attached. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.", + "type": "boolean" + }, + "attachmentMetadata": { + "additionalProperties": { + "type": "string" + }, + "description": "Upon successful attach, this field is populated with any information returned by the attach operation that must be passed into subsequent WaitForAttach or Mount calls. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.", + "type": "object" + }, + "detachError": { + "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeError", + "description": "The last error encountered during detach operation, if any. This field must only be set by the entity completing the detach operation, i.e. the external-attacher." + } + }, + "required": [ + "attached" + ], + "type": "object" + }, + "io.k8s.api.storage.v1alpha1.VolumeError": { + "description": "VolumeError captures an error encountered during a volume operation.", + "properties": { + "message": { + "description": "String detailing the error encountered during Attach or Detach operation. This string maybe logged, so it should not contain sensitive information.", + "type": "string" + }, + "time": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Time the error was encountered." + } + }, + "type": "object" + }, + "io.k8s.api.storage.v1beta1.CSIDriver": { + "description": "CSIDriver captures information about a Container Storage Interface (CSI) volume driver deployed on the cluster. CSI drivers do not need to create the CSIDriver object directly. Instead they may use the cluster-driver-registrar sidecar container. When deployed with a CSI driver it automatically creates a CSIDriver object representing the driver. Kubernetes attach detach controller uses this object to determine whether attach is required. Kubelet uses this object to determine whether pod information needs to be passed on mount. CSIDriver objects are non-namespaced.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CSIDriver" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata. metadata.Name indicates the name of the CSI driver that this object refers to; it MUST be the same name returned by the CSI GetPluginName() call for that driver. The driver name must be 63 characters or less, beginning and ending with an alphanumeric character ([a-z0-9A-Z]) with dashes (-), dots (.), and alphanumerics between. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.storage.v1beta1.CSIDriverSpec", + "description": "Specification of the CSI Driver." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "CSIDriver", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.storage.v1beta1.CSIDriverList": { + "description": "CSIDriverList is a collection of CSIDriver objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of CSIDriver", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1beta1.CSIDriver" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CSIDriverList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "CSIDriverList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.storage.v1beta1.CSIDriverSpec": { + "description": "CSIDriverSpec is the specification of a CSIDriver.", + "properties": { + "attachRequired": { + "description": "attachRequired indicates this CSI volume driver requires an attach operation (because it implements the CSI ControllerPublishVolume() method), and that the Kubernetes attach detach controller should call the attach volume interface which checks the volumeattachment status and waits until the volume is attached before proceeding to mounting. The CSI external-attacher coordinates with CSI volume driver and updates the volumeattachment status when the attach operation is complete. If the CSIDriverRegistry feature gate is enabled and the value is specified to false, the attach operation will be skipped. Otherwise the attach operation will be called.", + "type": "boolean" + }, + "podInfoOnMount": { + "description": "If set to true, podInfoOnMount indicates this CSI volume driver requires additional pod information (like podName, podUID, etc.) during mount operations. If set to false, pod information will not be passed on mount. Default is false. The CSI driver specifies podInfoOnMount as part of driver deployment. If true, Kubelet will pass pod information as VolumeContext in the CSI NodePublishVolume() calls. The CSI driver is responsible for parsing and validating the information passed in as VolumeContext. The following VolumeConext will be passed if podInfoOnMount is set to true. This list might grow, but the prefix will be used. \"csi.storage.k8s.io/pod.name\": pod.Name \"csi.storage.k8s.io/pod.namespace\": pod.Namespace \"csi.storage.k8s.io/pod.uid\": string(pod.UID)", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.storage.v1beta1.CSINode": { + "description": "CSINode holds information about all CSI drivers installed on a node. CSI drivers do not need to create the CSINode object directly. As long as they use the node-driver-registrar sidecar container, the kubelet will automatically populate the CSINode object for the CSI driver as part of kubelet plugin registration. CSINode has the same name as a node. If the object is missing, it means either there are no CSI Drivers available on the node, or the Kubelet version is low enough that it doesn't create this object. CSINode has an OwnerReference that points to the corresponding node object.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CSINode" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "metadata.name must be the Kubernetes node name." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.storage.v1beta1.CSINodeSpec", + "description": "spec is the specification of CSINode" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "CSINode", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.storage.v1beta1.CSINodeDriver": { + "description": "CSINodeDriver holds information about the specification of one CSI driver installed on a node", + "properties": { + "name": { + "description": "This is the name of the CSI driver that this object refers to. This MUST be the same name returned by the CSI GetPluginName() call for that driver.", + "type": "string" + }, + "nodeID": { + "description": "nodeID of the node from the driver point of view. This field enables Kubernetes to communicate with storage systems that do not share the same nomenclature for nodes. For example, Kubernetes may refer to a given node as \"node1\", but the storage system may refer to the same node as \"nodeA\". When Kubernetes issues a command to the storage system to attach a volume to a specific node, it can use this field to refer to the node name using the ID that the storage system will understand, e.g. \"nodeA\" instead of \"node1\". This field is required.", + "type": "string" + }, + "topologyKeys": { + "description": "topologyKeys is the list of keys supported by the driver. When a driver is initialized on a cluster, it provides a set of topology keys that it understands (e.g. \"company.com/zone\", \"company.com/region\"). When a driver is initialized on a node, it provides the same topology keys along with values. Kubelet will expose these topology keys as labels on its own node object. When Kubernetes does topology aware provisioning, it can use this list to determine which labels it should retrieve from the node object and pass back to the driver. It is possible for different nodes to use different topology keys. This can be empty if driver does not support topology.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "name", + "nodeID" + ], + "type": "object" + }, + "io.k8s.api.storage.v1beta1.CSINodeList": { + "description": "CSINodeList is a collection of CSINode objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of CSINode", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1beta1.CSINode" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CSINodeList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "CSINodeList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.storage.v1beta1.CSINodeSpec": { + "description": "CSINodeSpec holds information about the specification of all CSI drivers installed on a node", + "properties": { + "drivers": { + "description": "drivers is a list of information of all CSI Drivers existing on a node. If all drivers in the list are uninstalled, this can become empty.", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1beta1.CSINodeDriver" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + } + }, + "required": [ + "drivers" + ], + "type": "object" + }, + "io.k8s.api.storage.v1beta1.StorageClass": { + "description": "StorageClass describes the parameters for a class of storage for which PersistentVolumes can be dynamically provisioned.\n\nStorageClasses are non-namespaced; the name of the storage class according to etcd is in ObjectMeta.Name.", + "properties": { + "allowVolumeExpansion": { + "description": "AllowVolumeExpansion shows whether the storage class allow volume expand", + "type": "boolean" + }, + "allowedTopologies": { + "description": "Restrict the node topologies where volumes can be dynamically provisioned. Each volume plugin defines its own supported topology specifications. An empty TopologySelectorTerm list means there is no topology restriction. This field is only honored by servers that enable the VolumeScheduling feature.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.TopologySelectorTerm" + }, + "type": "array" + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StorageClass" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "mountOptions": { + "description": "Dynamically provisioned PersistentVolumes of this storage class are created with these mountOptions, e.g. [\"ro\", \"soft\"]. Not validated - mount of the PVs will simply fail if one is invalid.", + "items": { + "type": "string" + }, + "type": "array" + }, + "parameters": { + "additionalProperties": { + "type": "string" + }, + "description": "Parameters holds the parameters for the provisioner that should create volumes of this storage class.", + "type": "object" + }, + "provisioner": { + "description": "Provisioner indicates the type of the provisioner.", + "type": "string" + }, + "reclaimPolicy": { + "description": "Dynamically provisioned PersistentVolumes of this storage class are created with this reclaimPolicy. Defaults to Delete.", + "type": "string" + }, + "volumeBindingMode": { + "description": "VolumeBindingMode indicates how PersistentVolumeClaims should be provisioned and bound. When unset, VolumeBindingImmediate is used. This field is only honored by servers that enable the VolumeScheduling feature.", + "type": "string" + } + }, + "required": [ + "provisioner" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "StorageClass", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.storage.v1beta1.StorageClassList": { + "description": "StorageClassList is a collection of storage classes.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of StorageClasses", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1beta1.StorageClass" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StorageClassList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "StorageClassList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.storage.v1beta1.VolumeAttachment": { + "description": "VolumeAttachment captures the intent to attach or detach the specified volume to/from the specified node.\n\nVolumeAttachment objects are non-namespaced.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "VolumeAttachment" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.storage.v1beta1.VolumeAttachmentSpec", + "description": "Specification of the desired attach/detach volume behavior. Populated by the Kubernetes system." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.storage.v1beta1.VolumeAttachmentStatus", + "description": "Status of the VolumeAttachment request. Populated by the entity completing the attach or detach operation, i.e. the external-attacher." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "VolumeAttachment", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.storage.v1beta1.VolumeAttachmentList": { + "description": "VolumeAttachmentList is a collection of VolumeAttachment objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of VolumeAttachments", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1beta1.VolumeAttachment" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "VolumeAttachmentList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "VolumeAttachmentList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.storage.v1beta1.VolumeAttachmentSource": { + "description": "VolumeAttachmentSource represents a volume that should be attached. Right now only PersistenVolumes can be attached via external attacher, in future we may allow also inline volumes in pods. Exactly one member can be set.", + "properties": { + "persistentVolumeName": { + "description": "Name of the persistent volume to attach.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.storage.v1beta1.VolumeAttachmentSpec": { + "description": "VolumeAttachmentSpec is the specification of a VolumeAttachment request.", + "properties": { + "attacher": { + "description": "Attacher indicates the name of the volume driver that MUST handle this request. This is the name returned by GetPluginName().", + "type": "string" + }, + "nodeName": { + "description": "The node that the volume should be attached to.", + "type": "string" + }, + "source": { + "$ref": "#/definitions/io.k8s.api.storage.v1beta1.VolumeAttachmentSource", + "description": "Source represents the volume that should be attached." + } + }, + "required": [ + "attacher", + "source", + "nodeName" + ], + "type": "object" + }, + "io.k8s.api.storage.v1beta1.VolumeAttachmentStatus": { + "description": "VolumeAttachmentStatus is the status of a VolumeAttachment request.", + "properties": { + "attachError": { + "$ref": "#/definitions/io.k8s.api.storage.v1beta1.VolumeError", + "description": "The last error encountered during attach operation, if any. This field must only be set by the entity completing the attach operation, i.e. the external-attacher." + }, + "attached": { + "description": "Indicates the volume is successfully attached. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.", + "type": "boolean" + }, + "attachmentMetadata": { + "additionalProperties": { + "type": "string" + }, + "description": "Upon successful attach, this field is populated with any information returned by the attach operation that must be passed into subsequent WaitForAttach or Mount calls. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.", + "type": "object" + }, + "detachError": { + "$ref": "#/definitions/io.k8s.api.storage.v1beta1.VolumeError", + "description": "The last error encountered during detach operation, if any. This field must only be set by the entity completing the detach operation, i.e. the external-attacher." + } + }, + "required": [ + "attached" + ], + "type": "object" + }, + "io.k8s.api.storage.v1beta1.VolumeError": { + "description": "VolumeError captures an error encountered during a volume operation.", + "properties": { + "message": { + "description": "String detailing the error encountered during Attach or Detach operation. This string may be logged, so it should not contain sensitive information.", + "type": "string" + }, + "time": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Time the error was encountered." + } + }, + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition": { + "description": "CustomResourceColumnDefinition specifies a column for server side printing.", + "properties": { + "JSONPath": { + "description": "JSONPath is a simple JSON path, i.e. with array notation.", + "type": "string" + }, + "description": { + "description": "description is a human readable description of this column.", + "type": "string" + }, + "format": { + "description": "format is an optional OpenAPI type definition for this column. The 'name' format is applied to the primary identifier column to assist in clients identifying column is the resource name. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.", + "type": "string" + }, + "name": { + "description": "name is a human readable name for the column.", + "type": "string" + }, + "priority": { + "description": "priority is an integer defining the relative importance of this column compared to others. Lower numbers are considered higher priority. Columns that may be omitted in limited space scenarios should be given a higher priority.", + "format": "int32", + "type": "integer" + }, + "type": { + "description": "type is an OpenAPI type definition for this column. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.", + "type": "string" + } + }, + "required": [ + "name", + "type", + "JSONPath" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceConversion": { + "description": "CustomResourceConversion describes how to convert different versions of a CR.", + "properties": { + "conversionReviewVersions": { + "description": "ConversionReviewVersions is an ordered list of preferred `ConversionReview` versions the Webhook expects. API server will try to use first version in the list which it supports. If none of the versions specified in this list supported by API server, conversion will fail for this object. If a persisted Webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail. Default to `['v1beta1']`.", + "items": { + "type": "string" + }, + "type": "array" + }, + "strategy": { + "description": "`strategy` specifies the conversion strategy. Allowed values are: - `None`: The converter only change the apiVersion and would not touch any other field in the CR. - `Webhook`: API Server will call to an external webhook to do the conversion. Additional information is needed for this option.", + "type": "string" + }, + "webhookClientConfig": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.WebhookClientConfig", + "description": "`webhookClientConfig` is the instructions for how to call the webhook if strategy is `Webhook`. This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature." + } + }, + "required": [ + "strategy" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition": { + "description": "CustomResourceDefinition represents a resource that should be exposed on the API server. Its name MUST be in the format <.spec.name>.<.spec.group>.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CustomResourceDefinition" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionSpec", + "description": "Spec describes how the user wants the resources to appear" + }, + "status": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionStatus", + "description": "Status indicates the actual state of the CustomResourceDefinition" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apiextensions.k8s.io", + "kind": "CustomResourceDefinition", + "version": "v1beta1" + } + ] + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionCondition": { + "description": "CustomResourceDefinitionCondition contains details for the current condition of this pod.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "Human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "Unique, one-word, CamelCase reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status is the status of the condition. Can be True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type is the type of the condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionList": { + "description": "CustomResourceDefinitionList is a list of CustomResourceDefinition objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items individual CustomResourceDefinitions", + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CustomResourceDefinitionList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apiextensions.k8s.io", + "kind": "CustomResourceDefinitionList", + "version": "v1beta1" + } + ] + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionNames": { + "description": "CustomResourceDefinitionNames indicates the names to serve this CustomResourceDefinition", + "properties": { + "categories": { + "description": "Categories is a list of grouped resources custom resources belong to (e.g. 'all')", + "items": { + "type": "string" + }, + "type": "array" + }, + "kind": { + "description": "Kind is the serialized kind of the resource. It is normally CamelCase and singular.", + "type": "string" + }, + "listKind": { + "description": "ListKind is the serialized kind of the list for this resource. Defaults to List.", + "type": "string" + }, + "plural": { + "description": "Plural is the plural name of the resource to serve. It must match the name of the CustomResourceDefinition-registration too: plural.group and it must be all lowercase.", + "type": "string" + }, + "shortNames": { + "description": "ShortNames are short names for the resource. It must be all lowercase.", + "items": { + "type": "string" + }, + "type": "array" + }, + "singular": { + "description": "Singular is the singular name of the resource. It must be all lowercase Defaults to lowercased ", + "type": "string" + } + }, + "required": [ + "plural", + "kind" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionSpec": { + "description": "CustomResourceDefinitionSpec describes how a user wants their resource to appear", + "properties": { + "additionalPrinterColumns": { + "description": "AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column. Optional, the global columns for all versions. Top-level and per-version columns are mutually exclusive.", + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition" + }, + "type": "array" + }, + "conversion": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceConversion", + "description": "`conversion` defines conversion settings for the CRD." + }, + "group": { + "description": "Group is the group this resource belongs in", + "type": "string" + }, + "names": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionNames", + "description": "Names are the names used to describe this custom resource" + }, + "scope": { + "description": "Scope indicates whether this resource is cluster or namespace scoped. Default is namespaced", + "type": "string" + }, + "subresources": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresources", + "description": "Subresources describes the subresources for CustomResource Optional, the global subresources for all versions. Top-level and per-version subresources are mutually exclusive." + }, + "validation": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceValidation", + "description": "Validation describes the validation methods for CustomResources Optional, the global validation schema for all versions. Top-level and per-version schemas are mutually exclusive." + }, + "version": { + "description": "Version is the version this resource belongs in Should be always first item in Versions field if provided. Optional, but at least one of Version or Versions must be set. Deprecated: Please use `Versions`.", + "type": "string" + }, + "versions": { + "description": "Versions is the list of all supported versions for this resource. If Version field is provided, this field is optional. Validation: All versions must use the same validation schema for now. i.e., top level Validation field is applied to all of these versions. Order: The version name will be used to compute the order. If the version string is \"kube-like\", it will sort above non \"kube-like\" version strings, which are ordered lexicographically. \"Kube-like\" versions start with a \"v\", then are followed by a number (the major version), then optionally the string \"alpha\" or \"beta\" and another number (the minor version). These are sorted first by GA > beta > alpha (where GA is a version with no suffix such as beta or alpha), and then by comparing major version, then minor version. An example sorted list of versions: v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10.", + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionVersion" + }, + "type": "array" + } + }, + "required": [ + "group", + "names", + "scope" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionStatus": { + "description": "CustomResourceDefinitionStatus indicates the state of the CustomResourceDefinition", + "properties": { + "acceptedNames": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionNames", + "description": "AcceptedNames are the names that are actually being used to serve discovery They may be different than the names in spec." + }, + "conditions": { + "description": "Conditions indicate state for particular aspects of a CustomResourceDefinition", + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionCondition" + }, + "type": "array" + }, + "storedVersions": { + "description": "StoredVersions are all versions of CustomResources that were ever persisted. Tracking these versions allows a migration path for stored versions in etcd. The field is mutable so the migration controller can first finish a migration to another version (i.e. that no old objects are left in the storage), and then remove the rest of the versions from this list. None of the versions in this list can be removed from the spec.Versions field.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "conditions", + "acceptedNames", + "storedVersions" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionVersion": { + "description": "CustomResourceDefinitionVersion describes a version for CRD.", + "properties": { + "additionalPrinterColumns": { + "description": "AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column. Top-level and per-version columns are mutually exclusive. Per-version columns must not all be set to identical values (top-level columns should be used instead) This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature. NOTE: CRDs created prior to 1.13 populated the top-level additionalPrinterColumns field by default. To apply an update that changes to per-version additionalPrinterColumns, the top-level additionalPrinterColumns field must be explicitly set to null", + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition" + }, + "type": "array" + }, + "name": { + "description": "Name is the version name, e.g. \u201cv1\u201d, \u201cv2beta1\u201d, etc.", + "type": "string" + }, + "schema": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceValidation", + "description": "Schema describes the schema for CustomResource used in validation, pruning, and defaulting. Top-level and per-version schemas are mutually exclusive. Per-version schemas must not all be set to identical values (top-level validation schema should be used instead) This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature." + }, + "served": { + "description": "Served is a flag enabling/disabling this version from being served via REST APIs", + "type": "boolean" + }, + "storage": { + "description": "Storage flags the version as storage version. There must be exactly one flagged as storage version.", + "type": "boolean" + }, + "subresources": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresources", + "description": "Subresources describes the subresources for CustomResource Top-level and per-version subresources are mutually exclusive. Per-version subresources must not all be set to identical values (top-level subresources should be used instead) This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature." + } + }, + "required": [ + "name", + "served", + "storage" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresourceScale": { + "description": "CustomResourceSubresourceScale defines how to serve the scale subresource for CustomResources.", + "properties": { + "labelSelectorPath": { + "description": "LabelSelectorPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Selector. Only JSON paths without the array notation are allowed. Must be a JSON Path under .status. Must be set to work with HPA. If there is no value under the given path in the CustomResource, the status label selector value in the /scale subresource will default to the empty string.", + "type": "string" + }, + "specReplicasPath": { + "description": "SpecReplicasPath defines the JSON path inside of a CustomResource that corresponds to Scale.Spec.Replicas. Only JSON paths without the array notation are allowed. Must be a JSON Path under .spec. If there is no value under the given path in the CustomResource, the /scale subresource will return an error on GET.", + "type": "string" + }, + "statusReplicasPath": { + "description": "StatusReplicasPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Replicas. Only JSON paths without the array notation are allowed. Must be a JSON Path under .status. If there is no value under the given path in the CustomResource, the status replica value in the /scale subresource will default to 0.", + "type": "string" + } + }, + "required": [ + "specReplicasPath", + "statusReplicasPath" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresourceStatus": { + "description": "CustomResourceSubresourceStatus defines how to serve the status subresource for CustomResources. Status is represented by the `.status` JSON path inside of a CustomResource. When set, * exposes a /status subresource for the custom resource * PUT requests to the /status subresource take a custom resource object, and ignore changes to anything except the status stanza * PUT/POST/PATCH requests to the custom resource ignore changes to the status stanza", + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresources": { + "description": "CustomResourceSubresources defines the status and scale subresources for CustomResources.", + "properties": { + "scale": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresourceScale", + "description": "Scale denotes the scale subresource for CustomResources" + }, + "status": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresourceStatus", + "description": "Status denotes the status subresource for CustomResources" + } + }, + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceValidation": { + "description": "CustomResourceValidation is a list of validation methods for CustomResources.", + "properties": { + "openAPIV3Schema": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps", + "description": "OpenAPIV3Schema is the OpenAPI v3 schema to be validated against." + } + }, + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.ExternalDocumentation": { + "description": "ExternalDocumentation allows referencing an external resource for extended documentation.", + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON": { + "description": "JSON represents any valid JSON value. These types are supported: bool, int64, float64, string, []interface{}, map[string]interface{} and nil." + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps": { + "description": "JSONSchemaProps is a JSON-Schema following Specification Draft 4 (http://json-schema.org/).", + "properties": { + "$ref": { + "type": "string" + }, + "$schema": { + "type": "string" + }, + "additionalItems": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrBool" + }, + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrBool" + }, + "allOf": { + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps" + }, + "type": "array" + }, + "anyOf": { + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps" + }, + "type": "array" + }, + "default": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON" + }, + "definitions": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps" + }, + "type": "object" + }, + "dependencies": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrStringArray" + }, + "type": "object" + }, + "description": { + "type": "string" + }, + "enum": { + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON" + }, + "type": "array" + }, + "example": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON" + }, + "exclusiveMaximum": { + "type": "boolean" + }, + "exclusiveMinimum": { + "type": "boolean" + }, + "externalDocs": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.ExternalDocumentation" + }, + "format": { + "type": "string" + }, + "id": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrArray" + }, + "maxItems": { + "format": "int64", + "type": "integer" + }, + "maxLength": { + "format": "int64", + "type": "integer" + }, + "maxProperties": { + "format": "int64", + "type": "integer" + }, + "maximum": { + "format": "double", + "type": "number" + }, + "minItems": { + "format": "int64", + "type": "integer" + }, + "minLength": { + "format": "int64", + "type": "integer" + }, + "minProperties": { + "format": "int64", + "type": "integer" + }, + "minimum": { + "format": "double", + "type": "number" + }, + "multipleOf": { + "format": "double", + "type": "number" + }, + "not": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps" + }, + "nullable": { + "type": "boolean" + }, + "oneOf": { + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps" + }, + "type": "array" + }, + "pattern": { + "type": "string" + }, + "patternProperties": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps" + }, + "type": "object" + }, + "properties": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps" + }, + "type": "object" + }, + "required": { + "items": { + "type": "string" + }, + "type": "array" + }, + "title": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uniqueItems": { + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrArray": { + "description": "JSONSchemaPropsOrArray represents a value that can either be a JSONSchemaProps or an array of JSONSchemaProps. Mainly here for serialization purposes." + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrBool": { + "description": "JSONSchemaPropsOrBool represents JSONSchemaProps or a boolean value. Defaults to true for the boolean property." + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrStringArray": { + "description": "JSONSchemaPropsOrStringArray represents a JSONSchemaProps or a string array." + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.ServiceReference": { + "description": "ServiceReference holds a reference to Service.legacy.k8s.io", + "properties": { + "name": { + "description": "`name` is the name of the service. Required", + "type": "string" + }, + "namespace": { + "description": "`namespace` is the namespace of the service. Required", + "type": "string" + }, + "path": { + "description": "`path` is an optional URL path which will be sent in any request to this service.", + "type": "string" + } + }, + "required": [ + "namespace", + "name" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.WebhookClientConfig": { + "description": "WebhookClientConfig contains the information to make a TLS connection with the webhook. It has the same field as admissionregistration.v1beta1.WebhookClientConfig.", + "properties": { + "caBundle": { + "description": "`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. If unspecified, system trust roots on the apiserver are used.", + "format": "byte", + "type": "string" + }, + "service": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.ServiceReference", + "description": "`service` is a reference to the service for this webhook. Either `service` or `url` must be specified.\n\nIf the webhook is running within the cluster, then you should use `service`.\n\nPort 443 will be used if it is open, otherwise it is an error." + }, + "url": { + "description": "`url` gives the location of the webhook, in standard URL form (`scheme://host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.api.resource.Quantity": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup": { + "description": "APIGroup contains the name, the supported versions, and the preferred version of a group.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIGroup" + ] + }, + "name": { + "description": "name is the name of the group.", + "type": "string" + }, + "preferredVersion": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery", + "description": "preferredVersion is the version preferred by the API server, which probably is the storage version." + }, + "serverAddressByClientCIDRs": { + "description": "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR" + }, + "type": "array" + }, + "versions": { + "description": "versions are the versions supported in this group.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery" + }, + "type": "array" + } + }, + "required": [ + "name", + "versions" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "APIGroup", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIGroupList": { + "description": "APIGroupList is a list of APIGroup, to allow clients to discover the API at /apis.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "groups": { + "description": "groups is a list of APIGroup.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIGroupList" + ] + } + }, + "required": [ + "groups" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "APIGroupList", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": { + "description": "APIResource specifies the name of a resource and whether it is namespaced.", + "properties": { + "categories": { + "description": "categories is a list of the grouped resources this resource belongs to (e.g. 'all')", + "items": { + "type": "string" + }, + "type": "array" + }, + "group": { + "description": "group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale\".", + "type": "string" + }, + "kind": { + "description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')", + "type": "string" + }, + "name": { + "description": "name is the plural name of the resource.", + "type": "string" + }, + "namespaced": { + "description": "namespaced indicates if a resource is namespaced or not.", + "type": "boolean" + }, + "shortNames": { + "description": "shortNames is a list of suggested short names of the resource.", + "items": { + "type": "string" + }, + "type": "array" + }, + "singularName": { + "description": "singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface.", + "type": "string" + }, + "storageVersionHash": { + "description": "The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates.", + "type": "string" + }, + "verbs": { + "description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)", + "items": { + "type": "string" + }, + "type": "array" + }, + "version": { + "description": "version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)\".", + "type": "string" + } + }, + "required": [ + "name", + "singularName", + "namespaced", + "kind", + "verbs" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList": { + "description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "groupVersion": { + "description": "groupVersion is the group and version this APIResourceList is for.", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIResourceList" + ] + }, + "resources": { + "description": "resources contains the name of the resources and if they are namespaced.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIResource" + }, + "type": "array" + } + }, + "required": [ + "groupVersion", + "resources" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "APIResourceList", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIVersions": { + "description": "APIVersions lists the versions that are available, to allow clients to discover the API at /api, which is the root path of the legacy v1 API.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIVersions" + ] + }, + "serverAddressByClientCIDRs": { + "description": "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR" + }, + "type": "array" + }, + "versions": { + "description": "versions are the api versions that are available.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "versions", + "serverAddressByClientCIDRs" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "APIVersions", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": { + "description": "DeleteOptions may be provided when deleting an API object.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "dryRun": { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "items": { + "type": "string" + }, + "type": "array" + }, + "gracePeriodSeconds": { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "format": "int64", + "type": "integer" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DeleteOptions" + ] + }, + "orphanDependents": { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "type": "boolean" + }, + "preconditions": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions", + "description": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned." + }, + "propagationPolicy": { + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "apps", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "DeleteOptions", + "version": "v1beta2" + }, + { + "group": "auditregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "authentication.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "authentication.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v2beta1" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v2beta2" + }, + { + "group": "batch", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "batch", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "batch", + "kind": "DeleteOptions", + "version": "v2alpha1" + }, + { + "group": "certificates.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "coordination.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "coordination.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "events.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "extensions", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "imagepolicy.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "networking.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "networking.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "node.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "node.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "policy", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "scheduling.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "scheduling.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "scheduling.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "settings.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "storage.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "storage.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "storage.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Fields": { + "description": "Fields stores a set of fields in a data structure like a Trie. To understand how this is used, see: https://github.com/kubernetes-sigs/structured-merge-diff", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery": { + "description": "GroupVersion contains the \"group/version\" and \"version\" string of a version. It is made a struct to keep extensibility.", + "properties": { + "groupVersion": { + "description": "groupVersion specifies the API group and version in the form \"group/version\"", + "type": "string" + }, + "version": { + "description": "version specifies the version in the form of \"version\". This is to save the clients the trouble of splitting the GroupVersion.", + "type": "string" + } + }, + "required": [ + "groupVersion", + "version" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Initializer": { + "description": "Initializer is information about an initializer that has not yet completed.", + "properties": { + "name": { + "description": "name of the process that is responsible for initializing this object.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Initializers": { + "description": "Initializers tracks the progress of initialization.", + "properties": { + "pending": { + "description": "Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Initializer" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "result": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status", + "description": "If result is set with the Failure field, the object will be persisted to storage and then deleted, ensuring that other clients can observe the deletion." + } + }, + "required": [ + "pending" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector": { + "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "properties": { + "matchExpressions": { + "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelectorRequirement" + }, + "type": "array" + }, + "matchLabels": { + "additionalProperties": { + "type": "string" + }, + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "type": "object" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelectorRequirement": { + "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "properties": { + "key": { + "description": "key is the label key that the selector applies to.", + "type": "string", + "x-kubernetes-patch-merge-key": "key", + "x-kubernetes-patch-strategy": "merge" + }, + "operator": { + "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", + "type": "string" + }, + "values": { + "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "key", + "operator" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": { + "description": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", + "properties": { + "continue": { + "description": "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.", + "type": "string" + }, + "resourceVersion": { + "description": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "selfLink": { + "description": "selfLink is a URL representing this object. Populated by the system. Read-only.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry": { + "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", + "type": "string" + }, + "fields": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Fields", + "description": "Fields identifies a set of fields." + }, + "manager": { + "description": "Manager is an identifier of the workflow managing these fields.", + "type": "string" + }, + "operation": { + "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", + "type": "string" + }, + "time": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Time is timestamp of when these fields were set. It should always be empty if Operation is 'Apply'" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime": { + "description": "MicroTime is version of Time with microsecond level precision.", + "format": "date-time", + "type": "string" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": { + "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", + "properties": { + "annotations": { + "additionalProperties": { + "type": "string" + }, + "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations", + "type": "object" + }, + "clusterName": { + "description": "The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.", + "type": "string" + }, + "creationTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "deletionGracePeriodSeconds": { + "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.", + "format": "int64", + "type": "integer" + }, + "deletionTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + }, + "finalizers": { + "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-patch-strategy": "merge" + }, + "generateName": { + "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency", + "type": "string" + }, + "generation": { + "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", + "format": "int64", + "type": "integer" + }, + "initializers": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Initializers", + "description": "An initializer is a controller which enforces some system invariant at object creation time. This field is a list of initializers that have not yet acted on this object. If nil or empty, this object has been completely initialized. Otherwise, the object is considered uninitialized and is hidden (in list/watch and get calls) from clients that haven't explicitly asked to observe uninitialized objects.\n\nWhen an object is created, the system will populate this list with the current set of initializers. Only privileged users may set or modify this list. Once it is empty, it may not be modified further by any user.\n\nDEPRECATED - initializers are an alpha field and will be removed in v1.15." + }, + "labels": { + "additionalProperties": { + "type": "string" + }, + "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels", + "type": "object" + }, + "managedFields": { + "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.\n\nThis field is alpha and can be changed or removed without notice.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry" + }, + "type": "array" + }, + "name": { + "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names", + "type": "string" + }, + "namespace": { + "description": "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces", + "type": "string" + }, + "ownerReferences": { + "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "uid", + "x-kubernetes-patch-strategy": "merge" + }, + "resourceVersion": { + "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "selfLink": { + "description": "SelfLink is a URL representing this object. Populated by the system. Read-only.", + "type": "string" + }, + "uid": { + "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference": { + "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.", + "properties": { + "apiVersion": { + "description": "API version of the referent.", + "type": "string" + }, + "blockOwnerDeletion": { + "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.", + "type": "boolean" + }, + "controller": { + "description": "If true, this reference points to the managing controller.", + "type": "boolean" + }, + "kind": { + "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names", + "type": "string" + }, + "uid": { + "description": "UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids", + "type": "string" + } + }, + "required": [ + "apiVersion", + "kind", + "name", + "uid" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Patch": { + "description": "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions": { + "description": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.", + "properties": { + "resourceVersion": { + "description": "Specifies the target ResourceVersion", + "type": "string" + }, + "uid": { + "description": "Specifies the target UID.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR": { + "description": "ServerAddressByClientCIDR helps the client to determine the server address that they should use, depending on the clientCIDR that they match.", + "properties": { + "clientCIDR": { + "description": "The CIDR with which clients can match their IP to figure out the server address that they should use.", + "type": "string" + }, + "serverAddress": { + "description": "Address of this server, suitable for a client that matches the above CIDR. This can be a hostname, hostname:port, IP or IP:port.", + "type": "string" + } + }, + "required": [ + "clientCIDR", + "serverAddress" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Status": { + "description": "Status is a return value for calls that don't return other objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "code": { + "description": "Suggested HTTP return code for this status, 0 if not set.", + "format": "int32", + "type": "integer" + }, + "details": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails", + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Status" + ] + }, + "message": { + "description": "A human-readable description of the status of this operation.", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + }, + "reason": { + "description": "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.", + "type": "string" + }, + "status": { + "description": "Status of the operation. One of: \"Success\" or \"Failure\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Status", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause": { + "description": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", + "properties": { + "field": { + "description": "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"", + "type": "string" + }, + "message": { + "description": "A human-readable description of the cause of the error. This field may be presented as-is to a reader.", + "type": "string" + }, + "reason": { + "description": "A machine-readable description of the cause of the error. If this value is empty there is no information available.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails": { + "description": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", + "properties": { + "causes": { + "description": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause" + }, + "type": "array" + }, + "group": { + "description": "The group attribute of the resource associated with the status StatusReason.", + "type": "string" + }, + "kind": { + "description": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", + "type": "string" + }, + "retryAfterSeconds": { + "description": "If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.", + "format": "int32", + "type": "integer" + }, + "uid": { + "description": "UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Time": { + "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", + "format": "date-time", + "type": "string" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent": { + "description": "Event represents a single event to a watched resource.", + "properties": { + "object": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.runtime.RawExtension", + "description": "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Error: *Status is recommended; other types may make sense\n depending on context." + }, + "type": { + "type": "string" + } + }, + "required": [ + "type", + "object" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "apps", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "WatchEvent", + "version": "v1beta2" + }, + { + "group": "auditregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "authentication.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "authentication.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v2beta1" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v2beta2" + }, + { + "group": "batch", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "batch", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "batch", + "kind": "WatchEvent", + "version": "v2alpha1" + }, + { + "group": "certificates.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "coordination.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "coordination.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "events.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "extensions", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "imagepolicy.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "networking.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "networking.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "node.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "node.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "policy", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "scheduling.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "scheduling.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "scheduling.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "settings.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "storage.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "storage.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "storage.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + } + ] + }, + "io.k8s.apimachinery.pkg.runtime.RawExtension": { + "description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package: type MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.Object `json:\"myPlugin\"`\n} type PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// External package: type MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n} type PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// On the wire, the JSON will look something like this: {\n\t\"kind\":\"MyAPIObject\",\n\t\"apiVersion\":\"v1\",\n\t\"myPlugin\": {\n\t\t\"kind\":\"PluginA\",\n\t\t\"aOption\":\"foo\",\n\t},\n}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)", + "properties": { + "Raw": { + "description": "Raw is the underlying serialization of this object.", + "format": "byte", + "type": "string" + } + }, + "required": [ + "Raw" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.util.intstr.IntOrString": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "io.k8s.apimachinery.pkg.version.Info": { + "description": "Info contains versioning information. how we'll want to distribute that information.", + "properties": { + "buildDate": { + "type": "string" + }, + "compiler": { + "type": "string" + }, + "gitCommit": { + "type": "string" + }, + "gitTreeState": { + "type": "string" + }, + "gitVersion": { + "type": "string" + }, + "goVersion": { + "type": "string" + }, + "major": { + "type": "string" + }, + "minor": { + "type": "string" + }, + "platform": { + "type": "string" + } + }, + "required": [ + "major", + "minor", + "gitVersion", + "gitCommit", + "gitTreeState", + "buildDate", + "goVersion", + "compiler", + "platform" + ], + "type": "object" + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIService": { + "description": "APIService represents a server for a particular GroupVersion. Name must be \"version.group\".", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIService" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceSpec", + "description": "Spec contains information for locating and communicating with a server" + }, + "status": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceStatus", + "description": "Status contains derived information about an API server" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apiregistration.k8s.io", + "kind": "APIService", + "version": "v1" + } + ] + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceCondition": { + "description": "APIServiceCondition describes the state of an APIService at a particular point", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "Human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "Unique, one-word, CamelCase reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status is the status of the condition. Can be True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type is the type of the condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceList": { + "description": "APIServiceList is a list of APIService objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "items": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIService" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIServiceList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apiregistration.k8s.io", + "kind": "APIServiceList", + "version": "v1" + } + ] + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceSpec": { + "description": "APIServiceSpec contains information for locating and communicating with a server. Only https is supported, though you are able to disable certificate verification.", + "properties": { + "caBundle": { + "description": "CABundle is a PEM encoded CA bundle which will be used to validate an API server's serving certificate. If unspecified, system trust roots on the apiserver are used.", + "format": "byte", + "type": "string" + }, + "group": { + "description": "Group is the API group name this server hosts", + "type": "string" + }, + "groupPriorityMinimum": { + "description": "GroupPriorityMininum is the priority this group should have at least. Higher priority means that the group is preferred by clients over lower priority ones. Note that other versions of this group might specify even higher GroupPriorityMininum values such that the whole group gets a higher priority. The primary sort is based on GroupPriorityMinimum, ordered highest number to lowest (20 before 10). The secondary sort is based on the alphabetical comparison of the name of the object. (v1.bar before v1.foo) We'd recommend something like: *.k8s.io (except extensions) at 18000 and PaaSes (OpenShift, Deis) are recommended to be in the 2000s", + "format": "int32", + "type": "integer" + }, + "insecureSkipTLSVerify": { + "description": "InsecureSkipTLSVerify disables TLS certificate verification when communicating with this server. This is strongly discouraged. You should use the CABundle instead.", + "type": "boolean" + }, + "service": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.ServiceReference", + "description": "Service is a reference to the service for this API server. It must communicate on port 443 If the Service is nil, that means the handling for the API groupversion is handled locally on this server. The call will simply delegate to the normal handler chain to be fulfilled." + }, + "version": { + "description": "Version is the API version this server hosts. For example, \"v1\"", + "type": "string" + }, + "versionPriority": { + "description": "VersionPriority controls the ordering of this API version inside of its group. Must be greater than zero. The primary sort is based on VersionPriority, ordered highest to lowest (20 before 10). Since it's inside of a group, the number can be small, probably in the 10s. In case of equal version priorities, the version string will be used to compute the order inside a group. If the version string is \"kube-like\", it will sort above non \"kube-like\" version strings, which are ordered lexicographically. \"Kube-like\" versions start with a \"v\", then are followed by a number (the major version), then optionally the string \"alpha\" or \"beta\" and another number (the minor version). These are sorted first by GA > beta > alpha (where GA is a version with no suffix such as beta or alpha), and then by comparing major version, then minor version. An example sorted list of versions: v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "service", + "groupPriorityMinimum", + "versionPriority" + ], + "type": "object" + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceStatus": { + "description": "APIServiceStatus contains derived information about an API server", + "properties": { + "conditions": { + "description": "Current service state of apiService.", + "items": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object" + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.ServiceReference": { + "description": "ServiceReference holds a reference to Service.legacy.k8s.io", + "properties": { + "name": { + "description": "Name is the name of the service", + "type": "string" + }, + "namespace": { + "description": "Namespace is the namespace of the service", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIService": { + "description": "APIService represents a server for a particular GroupVersion. Name must be \"version.group\".", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIService" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceSpec", + "description": "Spec contains information for locating and communicating with a server" + }, + "status": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceStatus", + "description": "Status contains derived information about an API server" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apiregistration.k8s.io", + "kind": "APIService", + "version": "v1beta1" + } + ] + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceCondition": { + "description": "APIServiceCondition describes the state of an APIService at a particular point", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "Human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "Unique, one-word, CamelCase reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status is the status of the condition. Can be True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type is the type of the condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceList": { + "description": "APIServiceList is a list of APIService objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "items": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIService" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIServiceList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apiregistration.k8s.io", + "kind": "APIServiceList", + "version": "v1beta1" + } + ] + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceSpec": { + "description": "APIServiceSpec contains information for locating and communicating with a server. Only https is supported, though you are able to disable certificate verification.", + "properties": { + "caBundle": { + "description": "CABundle is a PEM encoded CA bundle which will be used to validate an API server's serving certificate. If unspecified, system trust roots on the apiserver are used.", + "format": "byte", + "type": "string" + }, + "group": { + "description": "Group is the API group name this server hosts", + "type": "string" + }, + "groupPriorityMinimum": { + "description": "GroupPriorityMininum is the priority this group should have at least. Higher priority means that the group is preferred by clients over lower priority ones. Note that other versions of this group might specify even higher GroupPriorityMininum values such that the whole group gets a higher priority. The primary sort is based on GroupPriorityMinimum, ordered highest number to lowest (20 before 10). The secondary sort is based on the alphabetical comparison of the name of the object. (v1.bar before v1.foo) We'd recommend something like: *.k8s.io (except extensions) at 18000 and PaaSes (OpenShift, Deis) are recommended to be in the 2000s", + "format": "int32", + "type": "integer" + }, + "insecureSkipTLSVerify": { + "description": "InsecureSkipTLSVerify disables TLS certificate verification when communicating with this server. This is strongly discouraged. You should use the CABundle instead.", + "type": "boolean" + }, + "service": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.ServiceReference", + "description": "Service is a reference to the service for this API server. It must communicate on port 443 If the Service is nil, that means the handling for the API groupversion is handled locally on this server. The call will simply delegate to the normal handler chain to be fulfilled." + }, + "version": { + "description": "Version is the API version this server hosts. For example, \"v1\"", + "type": "string" + }, + "versionPriority": { + "description": "VersionPriority controls the ordering of this API version inside of its group. Must be greater than zero. The primary sort is based on VersionPriority, ordered highest to lowest (20 before 10). Since it's inside of a group, the number can be small, probably in the 10s. In case of equal version priorities, the version string will be used to compute the order inside a group. If the version string is \"kube-like\", it will sort above non \"kube-like\" version strings, which are ordered lexicographically. \"Kube-like\" versions start with a \"v\", then are followed by a number (the major version), then optionally the string \"alpha\" or \"beta\" and another number (the minor version). These are sorted first by GA > beta > alpha (where GA is a version with no suffix such as beta or alpha), and then by comparing major version, then minor version. An example sorted list of versions: v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "service", + "groupPriorityMinimum", + "versionPriority" + ], + "type": "object" + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceStatus": { + "description": "APIServiceStatus contains derived information about an API server", + "properties": { + "conditions": { + "description": "Current service state of apiService.", + "items": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object" + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.ServiceReference": { + "description": "ServiceReference holds a reference to Service.legacy.k8s.io", + "properties": { + "name": { + "description": "Name is the name of the service", + "type": "string" + }, + "namespace": { + "description": "Namespace is the namespace of the service", + "type": "string" + } + }, + "type": "object" + } + } +} \ No newline at end of file diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00000.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00000.stmt new file mode 100644 index 000000000000..e756c8c657d4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00000.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("context") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00001.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00001.stmt new file mode 100644 index 000000000000..f6047b88209f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00001.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encoding/json") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00002.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00002.stmt new file mode 100644 index 000000000000..88fad1b6b464 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00002.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("errors") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00003.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00003.stmt new file mode 100644 index 000000000000..c079266b68c3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00003.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("fmt") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00004.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00004.stmt new file mode 100644 index 000000000000..ae8a1f7ec193 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00004.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("math/rand") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00005.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00005.stmt new file mode 100644 index 000000000000..5c72c74b0de0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00005.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("reflect") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00006.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00006.stmt new file mode 100644 index 000000000000..ec29e192850d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00006.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("sync") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00007.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00007.stmt new file mode 100644 index 000000000000..88e4fcbd96ef --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00007.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("testing") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00008.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00008.stmt new file mode 100644 index 000000000000..891d5c2b5a33 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00008.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("time") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00009.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00009.stmt new file mode 100644 index 000000000000..53023c30f3fa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00009.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("github.com/open-policy-agent/opa/ast") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00010.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00010.stmt new file mode 100644 index 000000000000..c68bef5a0842 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00010.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("github.com/open-policy-agent/opa/rego") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00011.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00011.stmt new file mode 100644 index 000000000000..49d0cfe0b1d5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00011.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("github.com/open-policy-agent/opa/storage") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00012.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00012.stmt new file mode 100644 index 000000000000..66f4c1a1e2ce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00012.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("github.com/open-policy-agent/opa/storage/inmem") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00013.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00013.stmt new file mode 100644 index 000000000000..b617b7ae4314 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00013.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("github.com/open-policy-agent/opa/util") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00014.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00014.stmt new file mode 100644 index 000000000000..7475b8dd1ded --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00014.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x = data.a") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00015.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00015.stmt new file mode 100644 index 000000000000..b3b425177614 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00015.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("hello") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00016.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00016.stmt new file mode 100644 index 000000000000..908287b49f4a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00016.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("bye") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00017.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00017.stmt new file mode 100644 index 000000000000..fe2b25beb831 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00017.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("foo") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00018.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00018.stmt new file mode 100644 index 000000000000..89d7c899b54f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00018.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("5") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00019.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00019.stmt new file mode 100644 index 000000000000..5ef9334a51c7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00019.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00020.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00020.stmt new file mode 100644 index 000000000000..3288bbf6caf2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00020.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00021.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00021.stmt new file mode 100644 index 000000000000..2851318cefcc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00021.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("3") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00022.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00022.stmt new file mode 100644 index 000000000000..7475b8dd1ded --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00022.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x = data.a") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00023.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00023.stmt new file mode 100644 index 000000000000..161bace7ebd6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00023.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00024.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00024.stmt new file mode 100644 index 000000000000..5ef9334a51c7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00024.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00025.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00025.stmt new file mode 100644 index 000000000000..3288bbf6caf2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00025.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00026.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00026.stmt new file mode 100644 index 000000000000..2851318cefcc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00026.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("3") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00027.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00027.stmt new file mode 100644 index 000000000000..eaa1b9c9d780 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00027.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("4") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00028.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00028.stmt new file mode 100644 index 000000000000..7475b8dd1ded --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00028.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x = data.a") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00029.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00029.stmt new file mode 100644 index 000000000000..161bace7ebd6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00029.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00030.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00030.stmt new file mode 100644 index 000000000000..d9bb3406a4cd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00030.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Expected non-empty metrics") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00031.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00031.stmt new file mode 100644 index 000000000000..b563767d5846 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00031.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Unexpected notification %v") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00032.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00032.stmt new file mode 100644 index 000000000000..fb37481a0038 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00032.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Query was not unregistered") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00033.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00033.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00033.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00034.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00034.stmt new file mode 100644 index 000000000000..d1afe13c77d0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00034.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package y\n\n\t\tr[\"foo\"] = y {\n\t\t\ty = data.a[0]\n\t\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00035.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00035.stmt new file mode 100644 index 000000000000..fc5d3ac9c923 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00035.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("plus(data.y.r[\"foo\"], 1, x)") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00036.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00036.stmt new file mode 100644 index 000000000000..fc5d3ac9c923 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00036.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("plus(data.y.r[\"foo\"], 1, x)") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00037.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00037.stmt new file mode 100644 index 000000000000..161bace7ebd6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00037.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00038.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00038.stmt new file mode 100644 index 000000000000..3288bbf6caf2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00038.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00039.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00039.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00039.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00040.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00040.stmt new file mode 100644 index 000000000000..d19ff1081012 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00040.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package y\n\n\t\tr[\"foo\"] = y {\n\t\t\ty = \"bar\"\n\t\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00041.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00041.stmt new file mode 100644 index 000000000000..fc5d3ac9c923 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00041.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("plus(data.y.r[\"foo\"], 1, x)") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00042.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00042.stmt new file mode 100644 index 000000000000..92c94f038dec --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00042.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Invalid watch channel was not closed") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00043.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00043.stmt new file mode 100644 index 000000000000..c504f0690bc2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00043.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Invalid watch channel present in new watcher") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00044.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00044.stmt new file mode 100644 index 000000000000..fbfd07e40061 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00044.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Invalid watch channel notify channel present in new watcher") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00045.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00045.stmt new file mode 100644 index 000000000000..7b91e65c77de --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00045.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x0 = data.x0") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00046.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00046.stmt new file mode 100644 index 000000000000..53b2d22a3d8f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00046.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x1 = data.x1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00047.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00047.stmt new file mode 100644 index 000000000000..3727d532908f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00047.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x2 = data.y.r[\"foo\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00048.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00048.stmt new file mode 100644 index 000000000000..a262ed3094fa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00048.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x3 = data.x0+1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00049.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00049.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00049.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00050.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00050.stmt new file mode 100644 index 000000000000..dec47283c697 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00050.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package y\n\n\t\tr[\"foo\"] = y {\n\t\t\ty = data.x2\n\t\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00051.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00051.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00051.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00052.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00052.stmt new file mode 100644 index 000000000000..dec47283c697 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00052.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package y\n\n\t\tr[\"foo\"] = y {\n\t\t\ty = data.x2\n\t\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00053.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00053.stmt new file mode 100644 index 000000000000..7b91e65c77de --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00053.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x0 = data.x0") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00054.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00054.stmt new file mode 100644 index 000000000000..53b2d22a3d8f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00054.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x1 = data.x1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00055.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00055.stmt new file mode 100644 index 000000000000..9b22ffae294e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00055.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x2 = data") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00056.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00056.stmt new file mode 100644 index 000000000000..a262ed3094fa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00056.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x3 = data.x0+1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00057.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00057.stmt new file mode 100644 index 000000000000..6393e6aee3fa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00057.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("y") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00058.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00058.stmt new file mode 100644 index 000000000000..99193dedd483 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00058.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("r") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00059.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00059.stmt new file mode 100644 index 000000000000..fe2b25beb831 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00059.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("foo") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00060.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00060.stmt new file mode 100644 index 000000000000..b88121f51e21 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00060.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x0") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00061.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00061.stmt new file mode 100644 index 000000000000..d9f526419e25 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00061.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00062.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00062.stmt new file mode 100644 index 000000000000..91d7016e04b7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00062.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00063.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00063.stmt new file mode 100644 index 000000000000..1e2720188844 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00063.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x3") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00064.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00064.stmt new file mode 100644 index 000000000000..5be10963c06e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00064.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\n \"a\": [1,2,3,4],\n \"b\": {\n \"v1\": \"hello\",\n \"v2\": \"goodbye\"\n },\n \"c\": [{\n \"x\": [true, false, \"foo\"],\n \"y\": [null, 3.14159],\n \"z\": {\"p\": true, \"q\": false}\n }],\n \"d\": {\n \"e\": [\"bar\", \"baz\"]\n },\n\t\t\"g\": {\n\t\t\t\"a\": [1, 0, 0, 0],\n\t\t\t\"b\": [0, 2, 0, 0],\n\t\t\t\"c\": [0, 0, 0, 4]\n\t\t},\n\t\t\"h\": [\n\t\t\t[1,2,3],\n\t\t\t[2,3,4]\n\t\t]\n }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00065.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00065.stmt new file mode 100644 index 000000000000..16b9c2cbf2a9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00065.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00066.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00066.stmt new file mode 100644 index 000000000000..cedb29a2b927 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00066.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00067.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00067.stmt new file mode 100644 index 000000000000..805aa0a4f869 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00067.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("any") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00068.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00068.stmt new file mode 100644 index 000000000000..805aa0a4f869 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00068.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("any") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00069.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00069.stmt new file mode 100644 index 000000000000..1048e14498e1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00069.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("superset") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00070.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00070.stmt new file mode 100644 index 000000000000..ebe5dac36203 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00070.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("scalar-1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00071.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00071.stmt new file mode 100644 index 000000000000..fe268df0c862 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00071.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("scalar-2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00072.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00072.stmt new file mode 100644 index 000000000000..177eec6ed928 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00072.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("scalar-3") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00073.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00073.stmt new file mode 100644 index 000000000000..2b0f75e03286 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00073.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("scalar-4") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00074.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00074.stmt new file mode 100644 index 000000000000..2dac8d77cf37 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00074.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00075.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00075.stmt new file mode 100644 index 000000000000..e48b044b1ee4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00075.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array dynamic") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00076.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00076.stmt new file mode 100644 index 000000000000..16b9c2cbf2a9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00076.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00077.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00077.stmt new file mode 100644 index 000000000000..5a7e76e5cb4b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00077.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("a") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00078.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00078.stmt new file mode 100644 index 000000000000..92983034c8eb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00078.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("b") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00079.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00079.stmt new file mode 100644 index 000000000000..1e64a1848ce5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00079.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object dynamic") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00080.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00080.stmt new file mode 100644 index 000000000000..5a7e76e5cb4b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00080.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("a") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00081.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00081.stmt new file mode 100644 index 000000000000..92983034c8eb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00081.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("b") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00082.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00082.stmt new file mode 100644 index 000000000000..cedb29a2b927 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00082.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00083.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00083.stmt new file mode 100644 index 000000000000..1048e14498e1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00083.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("superset") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00084.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00084.stmt new file mode 100644 index 000000000000..805aa0a4f869 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00084.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("any") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00085.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00085.stmt new file mode 100644 index 000000000000..ebe5dac36203 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00085.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("scalar-1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00086.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00086.stmt new file mode 100644 index 000000000000..fe268df0c862 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00086.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("scalar-2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00087.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00087.stmt new file mode 100644 index 000000000000..177eec6ed928 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00087.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("scalar-3") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00088.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00088.stmt new file mode 100644 index 000000000000..2b0f75e03286 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00088.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("scalar-4") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00089.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00089.stmt new file mode 100644 index 000000000000..fe2b25beb831 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00089.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("foo") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00090.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00090.stmt new file mode 100644 index 000000000000..5ef9334a51c7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00090.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00091.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00091.stmt new file mode 100644 index 000000000000..b3b425177614 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00091.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("hello") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00092.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00092.stmt new file mode 100644 index 000000000000..fe2b25beb831 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00092.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("foo") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00093.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00093.stmt new file mode 100644 index 000000000000..fe2b25beb831 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00093.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("foo") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00094.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00094.stmt new file mode 100644 index 000000000000..01c57337b4e0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00094.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Expected %v type to be unknown") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00095.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00095.stmt new file mode 100644 index 000000000000..fe2b25beb831 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00095.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("foo") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00096.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00096.stmt new file mode 100644 index 000000000000..a552ce7ce3c7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00096.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("func") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00097.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00097.stmt new file mode 100644 index 000000000000..a38c0858b5d5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00097.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t{\n\t\t\"type\": \"any\",\n\t\t\"of\": [\n\t\t\t{\n\t\t\t\t\"type\": \"object\",\n\t\t\t\t\"static\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"foo\",\n\t\t\t\t\t\t\"value\": {\"type\": \"number\"}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"func\",\n\t\t\t\t\t\t\"value\": {\n\t\t\t\t\t\t\t\"type\": \"function\",\n\t\t\t\t\t\t\t\"args\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"result\": {\n\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"dynamic\": {\n\t\t\t\t\t\"key\": {\"type\": \"string\"},\n\t\t\t\t\t\"value\": {\n\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\"static\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"type\": \"set\",\n\t\t\t\t\t\t\t\t\"of\": {\"type\": \"boolean\"}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"dynamic\": {\"type\": \"number\"}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t]\n\t}\n\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00098.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00098.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00098.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00099.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00099.stmt new file mode 100644 index 000000000000..824ffc85bbce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00099.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("number non-zero") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00100.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00100.stmt new file mode 100644 index 000000000000..7e6f9f34a5c5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00100.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { -3.14 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00101.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00101.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00101.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00102.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00102.stmt new file mode 100644 index 000000000000..000801d74459 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00102.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("number zero") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00103.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00103.stmt new file mode 100644 index 000000000000..87ee913eb772 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00103.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { null }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00104.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00104.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00104.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00105.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00105.stmt new file mode 100644 index 000000000000..56314680ecf4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00105.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("null") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00106.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00106.stmt new file mode 100644 index 000000000000..87ee913eb772 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00106.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { null }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00107.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00107.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00107.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00108.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00108.stmt new file mode 100644 index 000000000000..101ad4f91c02 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00108.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("string non-empty") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00109.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00109.stmt new file mode 100644 index 000000000000..bb51c2eed48b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00109.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { \"abc\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00110.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00110.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00110.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00111.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00111.stmt new file mode 100644 index 000000000000..23b719e8af18 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00111.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("string empty") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00112.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00112.stmt new file mode 100644 index 000000000000..c42604cfc40c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00112.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { \"\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00113.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00113.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00113.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00114.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00114.stmt new file mode 100644 index 000000000000..24cd397d8c2a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00114.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array non-empty") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00115.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00115.stmt new file mode 100644 index 000000000000..f5ee7258f8fb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00115.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { [1, 2, 3] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00116.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00116.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00116.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00117.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00117.stmt new file mode 100644 index 000000000000..1fb42edf52a2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00117.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array empty") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00118.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00118.stmt new file mode 100644 index 000000000000..21bad12b5cf7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00118.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { [] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00119.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00119.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00119.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00120.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00120.stmt new file mode 100644 index 000000000000..f8a71188f8a8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00120.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object non-empty") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00121.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00121.stmt new file mode 100644 index 000000000000..02b80cd696f6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00121.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { {\"a\": 1} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00122.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00122.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00122.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00123.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00123.stmt new file mode 100644 index 000000000000..6733cba41d2e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00123.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object empty") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00124.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00124.stmt new file mode 100644 index 000000000000..14c960e08c1f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00124.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { {} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00125.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00125.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00125.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00126.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00126.stmt new file mode 100644 index 000000000000..33e4a17af446 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00126.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set non-empty") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00127.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00127.stmt new file mode 100644 index 000000000000..20c5d5db1b8a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00127.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { {1, 2, 3} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00128.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00128.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00128.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00129.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00129.stmt new file mode 100644 index 000000000000..368256854288 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00129.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set empty") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00130.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00130.stmt new file mode 100644 index 000000000000..9b09d64cda29 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00130.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { set() }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00131.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00131.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00131.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00132.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00132.stmt new file mode 100644 index 000000000000..461f07e69a8b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00132.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ref") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00133.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00133.stmt new file mode 100644 index 000000000000..6ee54600e695 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00133.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[i] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00134.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00134.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00134.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00135.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00135.stmt new file mode 100644 index 000000000000..d9f466a8e8c6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00135.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ref undefined") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00136.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00136.stmt new file mode 100644 index 000000000000..8809b144702a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00136.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { data.deadbeef[i] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00137.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00137.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00137.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00138.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00138.stmt new file mode 100644 index 000000000000..2ae7800b7fb2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00138.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ref undefined (path)") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00139.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00139.stmt new file mode 100644 index 000000000000..9867f0201e2c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00139.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { data.a[true] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00140.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00140.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00140.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00141.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00141.stmt new file mode 100644 index 000000000000..29001339c2f0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00141.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ref false") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00142.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00142.stmt new file mode 100644 index 000000000000..e0a7c3a7e4cf --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00142.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { data.c[0].x[1] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00143.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00143.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00143.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00144.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00144.stmt new file mode 100644 index 000000000000..40580ceeca3e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00144.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array comprehension") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00145.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00145.stmt new file mode 100644 index 000000000000..9d8503bfb285 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00145.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { [x | x = 1] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00146.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00146.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00146.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00147.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00147.stmt new file mode 100644 index 000000000000..69a5caf74274 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00147.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array comprehension empty") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00148.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00148.stmt new file mode 100644 index 000000000000..23001e9188b9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00148.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { [x | x = 1; x = 2] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00149.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00149.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00149.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00150.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00150.stmt new file mode 100644 index 000000000000..25173d2a8f19 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00150.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("arbitrary position") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00151.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00151.stmt new file mode 100644 index 000000000000..921b31d8d428 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00151.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[i] = x; x; i }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00152.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00152.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00152.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00153.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00153.stmt new file mode 100644 index 000000000000..34e228666dce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00153.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { true = false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00154.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00154.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00154.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00155.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00155.stmt new file mode 100644 index 000000000000..11cc88ad0bda --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00155.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { [1, 2, 3] = [1, 3, 2] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00156.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00156.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00156.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00157.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00157.stmt new file mode 100644 index 000000000000..8bc66bc0923a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00157.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[3] = 9999 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00158.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00158.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00158.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00159.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00159.stmt new file mode 100644 index 000000000000..7ba57abf019d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00159.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[i] = 9999 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00160.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00160.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00160.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00161.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00161.stmt new file mode 100644 index 000000000000..bdc69364e351 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00161.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[3] = x; x = 3 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00162.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00162.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00162.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00163.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00163.stmt new file mode 100644 index 000000000000..6aa563586bc1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00163.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { [1, x, x] = [1, 2, 3] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00164.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00164.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00164.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00165.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00165.stmt new file mode 100644 index 000000000000..8f68682878a1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00165.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { [1, x, 3] = [1, 2, x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00166.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00166.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00166.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00167.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00167.stmt new file mode 100644 index 000000000000..29d9fb8db898 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00167.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { {\"a\": 1, \"b\": 2} = {\"a\": a, \"b\": a} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00168.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00168.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00168.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00169.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00169.stmt new file mode 100644 index 000000000000..ea2bad52c424 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00169.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { [[1, x], [3, x]] = [[1, 2], [3, 4]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00170.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00170.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00170.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00171.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00171.stmt new file mode 100644 index 000000000000..688a3db140f4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00171.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { [[1, x], [3, 4]] = [[1, 2], [x, 4]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00172.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00172.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00172.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00173.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00173.stmt new file mode 100644 index 000000000000..f60de1d6a44f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00173.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { {1, 2, 3} = {1, 2, 4} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00174.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00174.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00174.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00175.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00175.stmt new file mode 100644 index 000000000000..ecf1eeeaa071 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00175.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { true = true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00176.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00176.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00176.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00177.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00177.stmt new file mode 100644 index 000000000000..7082977afb58 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00177.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { \"string\" = \"string\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00178.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00178.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00178.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00179.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00179.stmt new file mode 100644 index 000000000000..eec15eb80e41 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00179.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 17 = 17 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00180.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00180.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00180.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00181.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00181.stmt new file mode 100644 index 000000000000..1c841e266182 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00181.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { null = null }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00182.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00182.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00182.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00183.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00183.stmt new file mode 100644 index 000000000000..ab369b27f864 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00183.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { [1, 2, 3] = [1, 2, 3] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00184.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00184.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00184.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00185.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00185.stmt new file mode 100644 index 000000000000..ae251a4993fb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00185.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { {1, 2, 3} = {3, 2, 1} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00186.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00186.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00186.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00187.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00187.stmt new file mode 100644 index 000000000000..ea50fb0429f6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00187.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { {\"b\": false, \"a\": [1, 2, 3]} = {\"a\": [1, 2, 3], \"b\": false} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00188.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00188.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00188.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00189.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00189.stmt new file mode 100644 index 000000000000..7a0efd6b11ed --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00189.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[2] = 3 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00190.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00190.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00190.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00191.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00191.stmt new file mode 100644 index 000000000000..4df8dafa01eb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00191.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { b.v2 = \"goodbye\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00192.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00192.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00192.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00193.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00193.stmt new file mode 100644 index 000000000000..6ba5c5adfd6d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00193.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { d.e = [\"bar\", \"baz\"] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00194.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00194.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00194.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00195.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00195.stmt new file mode 100644 index 000000000000..b63fdb017cbb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00195.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { c[0].x[1] = c[0].z.q }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00196.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00196.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00196.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00197.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00197.stmt new file mode 100644 index 000000000000..a51dbc52b19f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00197.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = y; z = 42; y = z }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00198.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00198.stmt new file mode 100644 index 000000000000..1d2985b12a5f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00198.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[42]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00199.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00199.stmt new file mode 100644 index 000000000000..395a8379a08c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00199.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[3] = x; x = 4 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00200.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00200.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00200.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00201.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00201.stmt new file mode 100644 index 000000000000..f9d487e880f1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00201.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[i] = x; x = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00202.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00202.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00202.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00203.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00203.stmt new file mode 100644 index 000000000000..9f81baafb0a6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00203.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[i] = 4; x = 3 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00204.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00204.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00204.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00205.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00205.stmt new file mode 100644 index 000000000000..f125ade396ee --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00205.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[i] = x; i = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00206.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00206.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00206.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00207.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00207.stmt new file mode 100644 index 000000000000..e60ceabee083 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00207.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { i = 2; a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00208.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00208.stmt new file mode 100644 index 000000000000..a90806808a7d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00208.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00209.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00209.stmt new file mode 100644 index 000000000000..759eebfde0c8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00209.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { c[0].x[i] = c[0].z[j]; x = [i, j] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00210.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00210.stmt new file mode 100644 index 000000000000..84965f3625cb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00210.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[0, \"p\"], [1, \"q\"]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00211.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00211.stmt new file mode 100644 index 000000000000..a224b0ff26d9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00211.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { [1, x, 3] = [1, 2, 3] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00212.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00212.stmt new file mode 100644 index 000000000000..23725d1f2916 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00212.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00213.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00213.stmt new file mode 100644 index 000000000000..791b52de2073 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00213.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { [[1, x], [3, 4]] = [[1, 2], [3, 4]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00214.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00214.stmt new file mode 100644 index 000000000000..23725d1f2916 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00214.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00215.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00215.stmt new file mode 100644 index 000000000000..f2b63cf3e718 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00215.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { [2, x, 3] = [x, 2, 3] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00216.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00216.stmt new file mode 100644 index 000000000000..23725d1f2916 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00216.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00217.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00217.stmt new file mode 100644 index 000000000000..5c2653fdb5e4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00217.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[z] { [1, x, y] = [1, 2, 3]; z = [x, y] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00218.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00218.stmt new file mode 100644 index 000000000000..8501196d0a7c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00218.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[2, 3]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00219.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00219.stmt new file mode 100644 index 000000000000..c85b0413fae1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00219.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[z] { [1, x, 3] = [y, 2, 3]; z = [x, y] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00220.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00220.stmt new file mode 100644 index 000000000000..2e67b705dc10 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00220.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[2, 1]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00221.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00221.stmt new file mode 100644 index 000000000000..13a94106f553 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00221.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { [1, 2, 3, x] = [a[0], a[1], a[2], a[3]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00222.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00222.stmt new file mode 100644 index 000000000000..e5eccd1cf0fc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00222.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00223.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00223.stmt new file mode 100644 index 000000000000..ac954957e9da --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00223.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { [1, 2, 3, x] = [a[0], a[1], a[2], a[i]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00224.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00224.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00224.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00225.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00225.stmt new file mode 100644 index 000000000000..3880d4891bc5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00225.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { [true, false, x] = c[i][j] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00226.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00226.stmt new file mode 100644 index 000000000000..dd00378a62c1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00226.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"foo\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00227.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00227.stmt new file mode 100644 index 000000000000..ee96ffbf8d4c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00227.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { c[i][j] = [true, false, x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00228.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00228.stmt new file mode 100644 index 000000000000..dd00378a62c1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00228.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"foo\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00229.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00229.stmt new file mode 100644 index 000000000000..d0e99ce065e6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00229.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { [1, 2, x] = y; x = 3 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00230.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00230.stmt new file mode 100644 index 000000000000..12fb703c7c94 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00230.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1,2,3]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00231.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00231.stmt new file mode 100644 index 000000000000..292ac3aadca0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00231.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { {\"x\": y} = {\"x\": \"y\"} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00232.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00232.stmt new file mode 100644 index 000000000000..5185fa5ddf88 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00232.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"y\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00233.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00233.stmt new file mode 100644 index 000000000000..5e030a503229 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00233.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { {\"x\": x, \"y\": x} = {\"x\": 1, \"y\": 1} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00234.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00234.stmt new file mode 100644 index 000000000000..e75637f20883 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00234.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00235.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00235.stmt new file mode 100644 index 000000000000..56e7e69c68ab --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00235.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[z] { {\"x\": x, \"y\": y} = {\"x\": 1, \"y\": 2}; z = [x, y] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00236.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00236.stmt new file mode 100644 index 000000000000..09676ad4f63a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00236.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1, 2]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00237.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00237.stmt new file mode 100644 index 000000000000..5dc9b9063655 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00237.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[z] { {\"x\": x, \"y\": 2} = {\"x\": 1, \"y\": y}; z = [x, y] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00238.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00238.stmt new file mode 100644 index 000000000000..09676ad4f63a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00238.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1, 2]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00239.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00239.stmt new file mode 100644 index 000000000000..04595a4b970f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00239.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { {\"p\": c[0].x[0], \"q\": x} = c[i][j] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00240.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00240.stmt new file mode 100644 index 000000000000..cb35854d1f65 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00240.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[false]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00241.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00241.stmt new file mode 100644 index 000000000000..7ef289ad297a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00241.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { {\"a\": 1, \"b\": x} = {\"a\": 1, \"b\": c[0].x[i]} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00242.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00242.stmt new file mode 100644 index 000000000000..8707153ccd9f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00242.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[true, false, \"foo\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00243.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00243.stmt new file mode 100644 index 000000000000..04b0856ecda1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00243.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { {\"p\": y, \"q\": z} = c[i][j]; x = [i, j, y, z] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00244.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00244.stmt new file mode 100644 index 000000000000..b2c716f547f7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00244.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[0, \"z\", true, false]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00245.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00245.stmt new file mode 100644 index 000000000000..bcff96200d7e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00245.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { c[i][j] = {\"p\": y, \"q\": z}; x = [i, j, y, z] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00246.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00246.stmt new file mode 100644 index 000000000000..b2c716f547f7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00246.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[0, \"z\", true, false]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00247.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00247.stmt new file mode 100644 index 000000000000..48e39baea81c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00247.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { {\"a\": 1, \"b\": y} = x; y = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00248.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00248.stmt new file mode 100644 index 000000000000..17ee4b491d33 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00248.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[{\"a\": 1, \"b\": 2}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00249.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00249.stmt new file mode 100644 index 000000000000..81259bb21eb7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00249.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[ys] { f[i] = {\"xs\": [2], \"ys\": ys} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00250.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00250.stmt new file mode 100644 index 000000000000..0bf60e60f977 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00250.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[3.0]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00251.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00251.stmt new file mode 100644 index 000000000000..1e0c9aa01123 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00251.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[v] { f[i] = {\"xs\": [x], \"ys\": [y]}; v = [x, y] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00252.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00252.stmt new file mode 100644 index 000000000000..3f4324419d41 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00252.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1.0, 2.0], [2.0, 3.0]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00253.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00253.stmt new file mode 100644 index 000000000000..ad0a699ba6fe --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00253.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = 2; {1,x,3} = {1,2,3} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00254.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00254.stmt new file mode 100644 index 000000000000..23725d1f2916 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00254.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00255.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00255.stmt new file mode 100644 index 000000000000..9a727f472244 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00255.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = \"a\"; {x: 1} = {\"a\": 1} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00256.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00256.stmt new file mode 100644 index 000000000000..9296f9c8234f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00256.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"a\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00257.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00257.stmt new file mode 100644 index 000000000000..fdc8536be5c7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00257.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = \"a\"; {\"a\": 1} = {x: 1} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00258.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00258.stmt new file mode 100644 index 000000000000..9296f9c8234f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00258.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"a\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00259.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00259.stmt new file mode 100644 index 000000000000..541be86884c0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00259.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[i] = g[i][j] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00260.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00260.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00260.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00261.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00261.stmt new file mode 100644 index 000000000000..7197589c31c6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00261.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array-type") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00262.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00262.stmt new file mode 100644 index 000000000000..731ef7d451fd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00262.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { arr = [[1, [2]], [1, null], [2, [2]]]; [x, [2]] = arr[_] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00263.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00263.stmt new file mode 100644 index 000000000000..42ef66872e0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00263.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00264.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00264.stmt new file mode 100644 index 000000000000..155b3f90ae24 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00264.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("arrays-element") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00265.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00265.stmt new file mode 100644 index 000000000000..613d2190349b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00265.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { arr = [[1, 2], [1, null], [2, 2]]; arr[_] = [x, 2] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00266.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00266.stmt new file mode 100644 index 000000000000..42ef66872e0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00266.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00267.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00267.stmt new file mode 100644 index 000000000000..13d454f3fdaa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00267.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("arrays-length") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00268.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00268.stmt new file mode 100644 index 000000000000..a7f0d6bd27fc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00268.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { arr = [[1, [2]], [1, []], [2, [2]]]; arr[_] = [x, [2]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00269.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00269.stmt new file mode 100644 index 000000000000..42ef66872e0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00269.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00270.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00270.stmt new file mode 100644 index 000000000000..0df2cce94d03 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00270.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array-ref-element") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00271.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00271.stmt new file mode 100644 index 000000000000..5e022e3bebbc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00271.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { arr = [[1, 2], data.arr_ref, [2, 2]]; arr[_] = [x, 2] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00272.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00272.stmt new file mode 100644 index 000000000000..42ef66872e0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00272.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00273.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00273.stmt new file mode 100644 index 000000000000..7ede2db07be6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00273.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object-type") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00274.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00274.stmt new file mode 100644 index 000000000000..fbf98a64e74d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00274.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { obj = {\"a\": {\"x\": 1, \"y\": {\"v\": 2}}, \"b\": {\"x\": 1, \"y\": null}, \"c\": {\"x\": 2, \"y\": {\"v\": 2}}}; {\"x\": x, \"y\": {\"v\": 2}} = obj[_] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00275.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00275.stmt new file mode 100644 index 000000000000..42ef66872e0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00275.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00276.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00276.stmt new file mode 100644 index 000000000000..10b83d70a2a6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00276.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("objects-element") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00277.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00277.stmt new file mode 100644 index 000000000000..569a1c7fd66a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00277.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { obj = {\"a\": {\"x\": 1, \"y\": 2}, \"b\": {\"x\": 1, \"y\": null}, \"c\": {\"x\": 2, \"y\": 2}}; obj[_] = {\"x\": x, \"y\": 2}}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00278.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00278.stmt new file mode 100644 index 000000000000..42ef66872e0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00278.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00279.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00279.stmt new file mode 100644 index 000000000000..dda5cc8003c9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00279.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("objects-length") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00280.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00280.stmt new file mode 100644 index 000000000000..eb0c2ae1f0d7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00280.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { obj = {\"a\": {\"x\": 1, \"y\": {\"v\": 2}}, \"b\": {\"x\": 1, \"y\": {}}, \"c\": {\"x\": 2, \"y\": {\"v\": 2}}}; obj[_] = {\"x\": x, \"y\": {\"v\": 2}}}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00281.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00281.stmt new file mode 100644 index 000000000000..42ef66872e0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00281.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00282.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00282.stmt new file mode 100644 index 000000000000..e3c1e38441d8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00282.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object-ref-element") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00283.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00283.stmt new file mode 100644 index 000000000000..54f16344c0f6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00283.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { obj = {\"a\": {\"x\": 1, \"y\": 2}, \"b\": obj_ref, \"c\": {\"x\": 2, \"y\": 2}}; obj[_] = {\"x\": x, \"y\": 2}}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00284.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00284.stmt new file mode 100644 index 000000000000..42ef66872e0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00284.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00285.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00285.stmt new file mode 100644 index 000000000000..9f37661620d2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00285.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object-ref-missing-key") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00286.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00286.stmt new file mode 100644 index 000000000000..2b29965e1d8b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00286.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { obj = {\"a\": {\"x\": 1, \"y\": 2}, \"b\": obj_ref_missing_key, \"c\": {\"x\": 2, \"y\": 2}}; obj[_] = {\"x\": x, \"y\": 2}}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00287.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00287.stmt new file mode 100644 index 000000000000..42ef66872e0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00287.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00288.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00288.stmt new file mode 100644 index 000000000000..ceeddd269261 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00288.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\t{\n\t\t\t\"arr_ref\": [1, null],\n\t\t\t\"obj_ref\": {\"x\": 1, \"y\": null},\n\t\t\t\"obj_ref_missing_key\": {\"x\": 3, \"z\": 2}\n\t\t}\n\t\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00289.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00289.stmt new file mode 100644 index 000000000000..b8d58f076636 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00289.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("equals") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00290.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00290.stmt new file mode 100644 index 000000000000..b038da4a1382 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00290.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 1 == 1; a[i] = x; x == 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00291.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00291.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00291.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00292.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00292.stmt new file mode 100644 index 000000000000..55beb90b6746 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00292.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("noteq") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00293.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00293.stmt new file mode 100644 index 000000000000..1bedd28712aa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00293.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 0 != 1; a[i] = x; x != 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00294.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00294.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00294.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00295.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00295.stmt new file mode 100644 index 000000000000..da364b0d880e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00295.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("gt") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00296.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00296.stmt new file mode 100644 index 000000000000..f022b341dd97 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00296.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 1 > 0; a[i] = x; x > 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00297.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00297.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00297.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00298.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00298.stmt new file mode 100644 index 000000000000..26804ca29ac9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00298.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("gteq") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00299.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00299.stmt new file mode 100644 index 000000000000..a4b53a696938 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00299.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 1 >= 1; a[i] = x; x >= 4 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00300.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00300.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00300.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00301.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00301.stmt new file mode 100644 index 000000000000..8e4f58895742 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00301.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("lt") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00302.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00302.stmt new file mode 100644 index 000000000000..431e202129b5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00302.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { -1 < 0; a[i] = x; x < 5 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00303.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00303.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00303.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00304.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00304.stmt new file mode 100644 index 000000000000..f9000a1bebc8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00304.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("lteq") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00305.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00305.stmt new file mode 100644 index 000000000000..8dd8a3f8a4de --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00305.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { -1 <= 0; a[i] = x; x <= 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00306.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00306.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00306.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00307.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00307.stmt new file mode 100644 index 000000000000..37a71bf54310 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00307.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 0 == 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00308.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00308.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00308.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00309.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00309.stmt new file mode 100644 index 000000000000..7a09ec070859 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00309.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 0 != 0 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00310.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00310.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00310.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00311.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00311.stmt new file mode 100644 index 000000000000..61eda5850470 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00311.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 1 > 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00312.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00312.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00312.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00313.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00313.stmt new file mode 100644 index 000000000000..5d1aff8f36af --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00313.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 1 >= 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00314.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00314.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00314.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00315.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00315.stmt new file mode 100644 index 000000000000..dd49c693f8d2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00315.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 1 < -1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00316.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00316.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00316.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00317.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00317.stmt new file mode 100644 index 000000000000..dd49c693f8d2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00317.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 1 < -1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00318.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00318.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00318.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00319.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00319.stmt new file mode 100644 index 000000000000..c37d8d94403f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00319.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q[1] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00320.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00320.stmt new file mode 100644 index 000000000000..371e9b4cb9c8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00320.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00321.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00321.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00321.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00322.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00322.stmt new file mode 100644 index 000000000000..ad8cfbeba9cd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00322.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[1] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00323.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00323.stmt new file mode 100644 index 000000000000..371e9b4cb9c8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00323.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00324.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00324.stmt new file mode 100644 index 000000000000..e75637f20883 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00324.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00325.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00325.stmt new file mode 100644 index 000000000000..b828b2508f55 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00325.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = {\"b\": [q[2]]} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00326.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00326.stmt new file mode 100644 index 000000000000..371e9b4cb9c8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00326.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00327.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00327.stmt new file mode 100644 index 000000000000..5197ef9e0727 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00327.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[{\"b\": [2]}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00328.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00328.stmt new file mode 100644 index 000000000000..67dea3fd8c59 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00328.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q[1000] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00329.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00329.stmt new file mode 100644 index 000000000000..4a762f4b7938 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00329.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] { a[x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00330.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00330.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00330.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00331.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00331.stmt new file mode 100644 index 000000000000..066b3894c25d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00331.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = y { x = [1]; q[x][0] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00332.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00332.stmt new file mode 100644 index 000000000000..3cef3d28ed19 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00332.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[[x]] { a[_] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00333.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00333.stmt new file mode 100644 index 000000000000..5ef9334a51c7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00333.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00334.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00334.stmt new file mode 100644 index 000000000000..e91fbc88e1b2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00334.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = 1; q[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00335.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00335.stmt new file mode 100644 index 000000000000..9bda0f263648 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00335.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[y] { a[y] = i }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00336.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00336.stmt new file mode 100644 index 000000000000..e75637f20883 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00336.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00337.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00337.stmt new file mode 100644 index 000000000000..7ee4f033db7c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00337.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { z = [[1, 2], 2]; q[z] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00338.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00338.stmt new file mode 100644 index 000000000000..fb0e4224c187 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00338.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[[x, y]] { x = [1, y]; y = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00339.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00339.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00339.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00340.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00340.stmt new file mode 100644 index 000000000000..3d479464480d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00340.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { y = 2; z = [[1, y], y]; q[z] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00341.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00341.stmt new file mode 100644 index 000000000000..fb0e4224c187 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00341.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[[x, y]] { x = [1, y]; y = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00342.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00342.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00342.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00343.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00343.stmt new file mode 100644 index 000000000000..46b8f144d90b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00343.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { y = 2; x = [1, y]; z = [x, y]; q[z] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00344.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00344.stmt new file mode 100644 index 000000000000..fb0e4224c187 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00344.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[[x, y]] { x = [1, y]; y = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00345.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00345.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00345.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00346.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00346.stmt new file mode 100644 index 000000000000..242c18194ce8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00346.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[u] { y = 2; x = [1, u]; z = [x, y]; q[z] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00347.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00347.stmt new file mode 100644 index 000000000000..fb0e4224c187 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00347.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[[x, y]] { x = [1, y]; y = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00348.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00348.stmt new file mode 100644 index 000000000000..23725d1f2916 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00348.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00349.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00349.stmt new file mode 100644 index 000000000000..3d7f17d3b128 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00349.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q[1] = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00350.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00350.stmt new file mode 100644 index 000000000000..b2614f8b742e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00350.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[i] = x { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00351.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00351.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00351.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00352.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00352.stmt new file mode 100644 index 000000000000..c6e0c88b6384 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00352.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q[1] = 0 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00353.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00353.stmt new file mode 100644 index 000000000000..45d61d8f539a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00353.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] = i { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00354.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00354.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00354.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00355.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00355.stmt new file mode 100644 index 000000000000..21dcbce2786f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00355.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = [1, q[3], q[2]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00356.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00356.stmt new file mode 100644 index 000000000000..b2614f8b742e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00356.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[i] = x { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00357.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00357.stmt new file mode 100644 index 000000000000..25bbb6579ab2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00357.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1,4,3]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00358.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00358.stmt new file mode 100644 index 000000000000..0e6c208ff540 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00358.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = {\"a\": [q[3]], \"b\": [q[2]]} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00359.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00359.stmt new file mode 100644 index 000000000000..b2614f8b742e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00359.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[i] = x { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00360.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00360.stmt new file mode 100644 index 000000000000..5344ab1e970e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00360.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[{\"a\": [4], \"b\": [3]}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00361.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00361.stmt new file mode 100644 index 000000000000..78c73d3e59bb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00361.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q[1] = 9999 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00362.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00362.stmt new file mode 100644 index 000000000000..b2614f8b742e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00362.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[i] = x { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00363.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00363.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00363.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00364.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00364.stmt new file mode 100644 index 000000000000..f66e2970e3c3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00364.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q[9999] = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00365.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00365.stmt new file mode 100644 index 000000000000..b2614f8b742e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00365.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[i] = x { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00366.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00366.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00366.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00367.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00367.stmt new file mode 100644 index 000000000000..a7e6ba82c828 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00367.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q.foo = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00368.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00368.stmt new file mode 100644 index 000000000000..b2614f8b742e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00368.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[i] = x { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00369.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00369.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00369.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00370.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00370.stmt new file mode 100644 index 000000000000..ac43ab3d3687 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00370.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q[0].x[1] = false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00371.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00371.stmt new file mode 100644 index 000000000000..d93d504764b3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00371.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[i] = x { x = c[i] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00372.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00372.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00372.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00373.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00373.stmt new file mode 100644 index 000000000000..716df17d0f65 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00373.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[v] { x = \"a\"; q[x][y] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00374.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00374.stmt new file mode 100644 index 000000000000..b8058bb3c8a3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00374.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { k = \"a\"; v = data.a }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00375.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00375.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00375.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00376.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00376.stmt new file mode 100644 index 000000000000..f1bd8c30721b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00376.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q[0][x][y] = false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00377.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00377.stmt new file mode 100644 index 000000000000..d93d504764b3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00377.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[i] = x { x = c[i] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00378.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00378.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00378.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00379.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00379.stmt new file mode 100644 index 000000000000..749e27adfcf9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00379.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { x = \"b\"; q[x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00380.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00380.stmt new file mode 100644 index 000000000000..e0048f20045f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00380.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { x = {\"a\": 1, \"b\": 2}; x[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00381.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00381.stmt new file mode 100644 index 000000000000..23725d1f2916 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00381.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00382.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00382.stmt new file mode 100644 index 000000000000..fe0f5612d4de --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00382.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] = y { r[z] = y; q[x] = z }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00383.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00383.stmt new file mode 100644 index 000000000000..dbea62faa1a1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00383.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("r[k] = v { x = {\"a\": 1, \"b\": 2, \"c\": 3, \"d\": 4}; x[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00384.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00384.stmt new file mode 100644 index 000000000000..cd6c104ccb9d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00384.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[y] = x { z = {\"a\": \"a\", \"b\": \"b\", \"d\": \"d\"}; z[y] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00385.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00385.stmt new file mode 100644 index 000000000000..593a4e98248c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00385.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"a\": 1, \"b\": 2, \"d\": 4}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00386.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00386.stmt new file mode 100644 index 000000000000..18646fd7548c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00386.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00387.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00387.stmt new file mode 100644 index 000000000000..698a6c731e6e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00387.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[y] { a[i] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00388.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00388.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00388.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00389.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00389.stmt new file mode 100644 index 000000000000..0f2789fc32ef --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00389.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[i] { {i: [i]} = {i: [q[i]]} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00390.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00390.stmt new file mode 100644 index 000000000000..76cd5b67b018 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00390.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] { d.e[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00391.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00391.stmt new file mode 100644 index 000000000000..3efd2d460d20 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00391.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"bar\", \"baz\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00392.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00392.stmt new file mode 100644 index 000000000000..18646fd7548c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00392.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00393.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00393.stmt new file mode 100644 index 000000000000..27dee3e3a688 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00393.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[y] { y = [i, j]; i = 1; j = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00394.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00394.stmt new file mode 100644 index 000000000000..bb3b5f26caac --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00394.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1,2]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00395.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00395.stmt new file mode 100644 index 000000000000..1dfb842e117d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00395.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { q[x][0] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00396.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00396.stmt new file mode 100644 index 000000000000..3cef3d28ed19 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00396.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[[x]] { a[_] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00397.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00397.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00397.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00398.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00398.stmt new file mode 100644 index 000000000000..e43d1f5ece24 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00398.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { q[i][j][k][x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00399.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00399.stmt new file mode 100644 index 000000000000..92eda1c68d4d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00399.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[{{[1], [2]}, {[3], [4]}}] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00400.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00400.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00400.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00401.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00401.stmt new file mode 100644 index 000000000000..18646fd7548c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00401.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00402.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00402.stmt new file mode 100644 index 000000000000..054ce16ba1b1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00402.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {0, \"\", false, null, [], {}, set()} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00403.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00403.stmt new file mode 100644 index 000000000000..0e9deb9cb741 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00403.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[0, \"\", null, [], {}, []]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00404.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00404.stmt new file mode 100644 index 000000000000..9f98a272abc3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00404.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[x] = 4 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00405.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00405.stmt new file mode 100644 index 000000000000..b2614f8b742e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00405.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[i] = x { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00406.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00406.stmt new file mode 100644 index 000000000000..a90806808a7d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00406.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00407.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00407.stmt new file mode 100644 index 000000000000..a6603cd9c9ba --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00407.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] = y { q[x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00408.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00408.stmt new file mode 100644 index 000000000000..de07d87daf75 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00408.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { b[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00409.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00409.stmt new file mode 100644 index 000000000000..4b9611d86565 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00409.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"v1\": \"hello\", \"v2\": \"goodbye\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00410.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00410.stmt new file mode 100644 index 000000000000..3ba9ed70b1c8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00410.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[k] = v { {k: [q[k]]} = {k: [v]} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00411.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00411.stmt new file mode 100644 index 000000000000..d86d494665e9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00411.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] = y { b[x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00412.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00412.stmt new file mode 100644 index 000000000000..4b9611d86565 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00412.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"v1\": \"hello\", \"v2\": \"goodbye\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00413.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00413.stmt new file mode 100644 index 000000000000..076999d0c2fc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00413.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[i] { q[i].x[1] = false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00414.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00414.stmt new file mode 100644 index 000000000000..d93d504764b3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00414.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[i] = x { x = c[i] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00415.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00415.stmt new file mode 100644 index 000000000000..7c37abf4bb5c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00415.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[0]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00416.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00416.stmt new file mode 100644 index 000000000000..e7329c4d3735 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00416.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[r] { q[x][y][z] = false; r = [x, y, z] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00417.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00417.stmt new file mode 100644 index 000000000000..d93d504764b3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00417.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[i] = x { x = c[i] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00418.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00418.stmt new file mode 100644 index 000000000000..f0694a5de02d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00418.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[0, \"x\", 1], [0, \"z\", \"q\"]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00419.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00419.stmt new file mode 100644 index 000000000000..8110cf12bd6d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00419.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[_][0].c[_] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00420.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00420.stmt new file mode 100644 index 000000000000..ce0e7f7d9aca --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00420.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { d.e[_] = k; v = [r | r = l[_]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00421.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00421.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00421.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00422.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00422.stmt new file mode 100644 index 000000000000..60cce239f687 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00422.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[_].x[0].c[_] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00423.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00423.stmt new file mode 100644 index 000000000000..182b2ee1ec3a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00423.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { d.e[_] = k; v = {\"x\": [r | r = l[_]]} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00424.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00424.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00424.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00425.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00425.stmt new file mode 100644 index 000000000000..bd11dd49d399 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00425.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q.bar[1].alice[0] = 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00426.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00426.stmt new file mode 100644 index 000000000000..b896167315e0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00426.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { d.e[_] = k; v = [x | x = {l[_].a: [1]}] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00427.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00427.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00427.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00428.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00428.stmt new file mode 100644 index 000000000000..bfdc6d8900b4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00428.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[z] { q[x] = y; z = [x, y] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00429.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00429.stmt new file mode 100644 index 000000000000..80b012a8d7eb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00429.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { v = [x, y]; x = \"a\"; y = \"b\"; k = \"foo\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00430.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00430.stmt new file mode 100644 index 000000000000..3a8ef9bd2308 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00430.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[\"foo\", [\"a\", \"b\"]]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00431.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00431.stmt new file mode 100644 index 000000000000..bfdc6d8900b4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00431.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[z] { q[x] = y; z = [x, y] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00432.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00432.stmt new file mode 100644 index 000000000000..b988a0182b05 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00432.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { k = y; y = x; x = \"a\"; v = \"foo\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00433.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00433.stmt new file mode 100644 index 000000000000..1b3bdc5670f1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00433.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[\"a\", \"foo\"]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00434.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00434.stmt new file mode 100644 index 000000000000..9157a93cde3a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00434.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[[x, y]] { q[x] = 1; q[y] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00435.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00435.stmt new file mode 100644 index 000000000000..45d61d8f539a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00435.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] = i { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00436.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00436.stmt new file mode 100644 index 000000000000..c47ba21ef327 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00436.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[2,3]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00437.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00437.stmt new file mode 100644 index 000000000000..baa680fac6dc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00437.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[x] = r[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00438.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00438.stmt new file mode 100644 index 000000000000..7ba9c0f394c2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00438.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] = y { z = {\"a\": 1, \"b\": 2, \"d\": 4}; z[x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00439.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00439.stmt new file mode 100644 index 000000000000..718c7b3dcdff --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00439.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("r[k] = v { x = {\"a\": 1, \"b\": 2, \"c\": 4, \"d\": 3}; x[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00440.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00440.stmt new file mode 100644 index 000000000000..2bd6be995b63 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00440.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"a\", \"b\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00441.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00441.stmt new file mode 100644 index 000000000000..6df257fe2e96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00441.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { q[x]; r[x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00442.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00442.stmt new file mode 100644 index 000000000000..1f4c48d96931 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00442.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] { z = [\"a\", \"b\", \"c\", \"d\"]; z[y] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00443.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00443.stmt new file mode 100644 index 000000000000..ce5e24685708 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00443.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("r[k] = v { x = {\"a\": 1, \"b\": 2, \"d\": 4}; x[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00444.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00444.stmt new file mode 100644 index 000000000000..dd31baaf0345 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00444.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2, 4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00445.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00445.stmt new file mode 100644 index 000000000000..3d7f17d3b128 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00445.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q[1] = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00446.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00446.stmt new file mode 100644 index 000000000000..90a807b48ee9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00446.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = [1, 2, 3, 4] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00447.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00447.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00447.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00448.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00448.stmt new file mode 100644 index 000000000000..d1c84c287101 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00448.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q.b = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00449.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00449.stmt new file mode 100644 index 000000000000..986ab02af244 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00449.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {\"a\": 1, \"b\": 2} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00450.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00450.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00450.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00451.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00451.stmt new file mode 100644 index 000000000000..06a9cb0d9bc3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00451.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q[3] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00452.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00452.stmt new file mode 100644 index 000000000000..0a187bdd7bcf --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00452.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {1, 2, 3, 4} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00453.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00453.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00453.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00454.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00454.stmt new file mode 100644 index 000000000000..d74260799604 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00454.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q[1][1] = 3 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00455.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00455.stmt new file mode 100644 index 000000000000..7ed0dde0f090 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00455.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = [[0, 1], [2, 3]] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00456.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00456.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00456.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00457.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00457.stmt new file mode 100644 index 000000000000..6e65850267fa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00457.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q.b[1] = 4 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00458.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00458.stmt new file mode 100644 index 000000000000..68216709c32d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00458.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {\"a\": [1, 2], \"b\": [3, 4]} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00459.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00459.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00459.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00460.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00460.stmt new file mode 100644 index 000000000000..c5bb1c0f8c12 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00460.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { z = [1, 2]; z[i] = y; q[y] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00461.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00461.stmt new file mode 100644 index 000000000000..90a807b48ee9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00461.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = [1, 2, 3, 4] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00462.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00462.stmt new file mode 100644 index 000000000000..a65543081eca --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00462.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00463.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00463.stmt new file mode 100644 index 000000000000..9bbc7918ff36 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00463.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { z = [\"b\", \"c\"]; z[i] = y; q[y] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00464.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00464.stmt new file mode 100644 index 000000000000..49dfeba42ac9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00464.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {\"a\": 1, \"b\": 2, \"c\": 3, \"d\": 4} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00465.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00465.stmt new file mode 100644 index 000000000000..a65543081eca --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00465.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00466.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00466.stmt new file mode 100644 index 000000000000..ef77fb720806 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00466.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q[1][1] = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00467.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00467.stmt new file mode 100644 index 000000000000..2dafd251cace --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00467.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = [{\"x\": x, \"y\": y}, z] { x = 1; y = 2; z = [1, 2, 3] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00468.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00468.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00468.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00469.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00469.stmt new file mode 100644 index 000000000000..75b40320c692 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00469.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[i] = e; x = [i, e] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00470.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00470.stmt new file mode 100644 index 000000000000..90a807b48ee9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00470.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = [1, 2, 3, 4] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00471.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00471.stmt new file mode 100644 index 000000000000..6b6cbf596dfe --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00471.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[0,1],[1,2],[2,3],[3,4]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00472.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00472.stmt new file mode 100644 index 000000000000..75b40320c692 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00472.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[i] = e; x = [i, e] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00473.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00473.stmt new file mode 100644 index 000000000000..986ab02af244 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00473.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {\"a\": 1, \"b\": 2} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00474.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00474.stmt new file mode 100644 index 000000000000..6112629c6e22 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00474.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[\"a\", 1], [\"b\", 2]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00475.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00475.stmt new file mode 100644 index 000000000000..18646fd7548c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00475.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00476.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00476.stmt new file mode 100644 index 000000000000..0a187bdd7bcf --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00476.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {1, 2, 3, 4} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00477.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00477.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00477.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00478.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00478.stmt new file mode 100644 index 000000000000..9ad69b0be0f9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00478.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[r] { q[i][j] = 2; r = [i, j] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00479.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00479.stmt new file mode 100644 index 000000000000..3a2b2cbf5031 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00479.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = [[1, 2], [3, 2]] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00480.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00480.stmt new file mode 100644 index 000000000000..b01c480b3c35 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00480.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[0, 1], [1, 1]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00481.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00481.stmt new file mode 100644 index 000000000000..9e4e48a74ff6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00481.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[r] { q[x][y] = 2; r = [x, y] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00482.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00482.stmt new file mode 100644 index 000000000000..c0a295a632da --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00482.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {\"a\": {\"x\": 1}, \"b\": {\"y\": 2}, \"c\": {\"z\": 2}} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00483.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00483.stmt new file mode 100644 index 000000000000..43c9be8442a2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00483.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[\"b\", \"y\"], [\"c\", \"z\"]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00484.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00484.stmt new file mode 100644 index 000000000000..2ec30f70d4e1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00484.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[_][_] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00485.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00485.stmt new file mode 100644 index 000000000000..2dafd251cace --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00485.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = [{\"x\": x, \"y\": y}, z] { x = 1; y = 2; z = [1, 2, 3] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00486.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00486.stmt new file mode 100644 index 000000000000..419ebc08ac6d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00486.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00487.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00487.stmt new file mode 100644 index 000000000000..d68cb349a4f7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00487.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00488.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00488.stmt new file mode 100644 index 000000000000..c755552b0bdc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00488.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = true { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00489.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00489.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00489.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00490.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00490.stmt new file mode 100644 index 000000000000..d68cb349a4f7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00490.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00491.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00491.stmt new file mode 100644 index 000000000000..be6a331d047e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00491.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = x { x = true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00492.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00492.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00492.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00493.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00493.stmt new file mode 100644 index 000000000000..d68cb349a4f7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00493.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00494.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00494.stmt new file mode 100644 index 000000000000..b62c2843cec4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00494.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = false { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00495.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00495.stmt new file mode 100644 index 000000000000..c755552b0bdc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00495.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = true { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00496.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00496.stmt new file mode 100644 index 000000000000..ad734c2658f2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00496.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { not q }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00497.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00497.stmt new file mode 100644 index 000000000000..63fcd1ae00a7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00497.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = true { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00498.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00498.stmt new file mode 100644 index 000000000000..b62c2843cec4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00498.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = false { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00499.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00499.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00499.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00500.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00500.stmt new file mode 100644 index 000000000000..14810fbed3b7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00500.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] = y { q = o; o[x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00501.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00501.stmt new file mode 100644 index 000000000000..d86d494665e9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00501.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] = y { b[x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00502.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00502.stmt new file mode 100644 index 000000000000..4b9611d86565 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00502.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"v1\": \"hello\", \"v2\": \"goodbye\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00503.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00503.stmt new file mode 100644 index 000000000000..14810fbed3b7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00503.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] = y { q = o; o[x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00504.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00504.stmt new file mode 100644 index 000000000000..d86d494665e9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00504.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] = y { b[x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00505.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00505.stmt new file mode 100644 index 000000000000..35bc11eb6fc6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00505.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x1] = y1 { d.e[y1] = x1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00506.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00506.stmt new file mode 100644 index 000000000000..0c1777458aad --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00506.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"v1\": \"hello\", \"v2\": \"goodbye\", \"bar\": 0, \"baz\": 1}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00507.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00507.stmt new file mode 100644 index 000000000000..02e70944ad11 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00507.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q = x; x[i] = 4 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00508.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00508.stmt new file mode 100644 index 000000000000..ac71027457ed --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00508.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { r = x; x[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00509.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00509.stmt new file mode 100644 index 000000000000..fa6de0a1aff2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00509.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("r[k] = v { s = x; x[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00510.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00510.stmt new file mode 100644 index 000000000000..01bdc4500f10 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00510.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("r[k] = v { t = x; x[v] = k }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00511.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00511.stmt new file mode 100644 index 000000000000..afd5115cd2b5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00511.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("s = {\"a\": 1, \"b\": 2, \"c\": 4} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00512.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00512.stmt new file mode 100644 index 000000000000..742dbc680e2a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00512.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("t = [\"d\", \"e\", \"g\"] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00513.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00513.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00513.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00514.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00514.stmt new file mode 100644 index 000000000000..b86c012ee67e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00514.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00515.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00515.stmt new file mode 100644 index 000000000000..ce7552f4e503 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00515.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { v = [i, j]; k = i; i = \"a\"; j = 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00516.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00516.stmt new file mode 100644 index 000000000000..75797ecc654f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00516.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[{\"a\": [\"a\", 1]}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00517.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00517.stmt new file mode 100644 index 000000000000..b86c012ee67e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00517.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00518.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00518.stmt new file mode 100644 index 000000000000..17d9e6117de1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00518.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = {\"v\": v} { v = [i, j]; k = i; i = \"a\"; j = 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00519.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00519.stmt new file mode 100644 index 000000000000..4eac94d5f3ee --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00519.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[{\"a\": {\"v\": [\"a\", 1]}}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00520.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00520.stmt new file mode 100644 index 000000000000..0320ef365953 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00520.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q; q }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00521.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00521.stmt new file mode 100644 index 000000000000..b06e0ed9cb93 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00521.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = x { x = data.c[0].z.p }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00522.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00522.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00522.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00523.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00523.stmt new file mode 100644 index 000000000000..3c1bbfdcc9f7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00523.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] = y { xs = [\"a\", \"b\", \"c\", \"a\"]; x = xs[i]; y = a[i] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00524.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00524.stmt new file mode 100644 index 000000000000..8287c1ccae1e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00524.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q = s; s[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00525.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00525.stmt new file mode 100644 index 000000000000..371e9b4cb9c8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00525.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00526.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00526.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00526.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00527.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00527.stmt new file mode 100644 index 000000000000..3ee577086064 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00527.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("empty partial set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00528.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00528.stmt new file mode 100644 index 000000000000..60acd0fdce58 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00528.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[1] { a[0] = 100 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00529.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00529.stmt new file mode 100644 index 000000000000..51bdc7b802b0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00529.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00530.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00530.stmt new file mode 100644 index 000000000000..d95da06b2382 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00530.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("empty partial object") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00531.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00531.stmt new file mode 100644 index 000000000000..297ee07f0997 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00531.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[\"x\"] = 1 { a[0] = 100 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00532.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00532.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00532.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00533.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00533.stmt new file mode 100644 index 000000000000..f475f30a3636 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00533.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { q.a.b = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00534.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00534.stmt new file mode 100644 index 000000000000..2afaa7e1f38a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00534.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {x: {y: 1}} { x = \"a\"; y = \"b\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00535.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00535.stmt new file mode 100644 index 000000000000..5ef9334a51c7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00535.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00536.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00536.stmt new file mode 100644 index 000000000000..5168a70af7de --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00536.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p { q[\"c\"] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00537.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00537.stmt new file mode 100644 index 000000000000..d76e5fcdf539 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00537.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {x, \"b\", z} { x = \"a\"; z = \"c\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00538.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00538.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00538.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00539.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00539.stmt new file mode 100644 index 000000000000..c4bc30bec222 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00539.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[i][j] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00540.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00540.stmt new file mode 100644 index 000000000000..3fdde4808c3f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00540.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {x: {x1: 1}, y: {y1: 2}} { x = \"a\"; y = \"b\"; x1 = \"a1\"; y1 = \"b1\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00541.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00541.stmt new file mode 100644 index 000000000000..42ef66872e0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00541.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00542.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00542.stmt new file mode 100644 index 000000000000..18646fd7548c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00542.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00543.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00543.stmt new file mode 100644 index 000000000000..d76e5fcdf539 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00543.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {x, \"b\", z} { x = \"a\"; z = \"c\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00544.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00544.stmt new file mode 100644 index 000000000000..3652193ac242 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00544.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"a\", \"b\", \"c\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00545.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00545.stmt new file mode 100644 index 000000000000..97b523302d68 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00545.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t{\n\t\t\"topdown\": {\n\t\t\t\"a\": {\n\t\t\t\t\"b\": {\n\t\t\t\t\t\"c\": {\n\t\t\t\t\t\t\"x\": [100,200],\n\t\t\t\t\t\t\"y\": false,\n\t\t\t\t\t\t\"z\": {\n\t\t\t\t\t\t\t\"a\": \"b\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"g\": {\n\t\t\t\t\"h\": {\n\t\t\t\t\t\"k\": [1,2,3]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"set\": {\n\t\t\t\t\"u\": [1,2,3,4]\n\t\t\t},\n\t\t\t\"conflicts\": {\n\t\t\t\t\"k\": \"foo\"\n\t\t\t}\n\t\t}\n\t}\n\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00546.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00546.stmt new file mode 100644 index 000000000000..f8719dcc9407 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00546.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown.a.b.c\n\np = [1, 2] { true }\nq = [3, 4] { true }\nr[\"a\"] = 1 { true }\nr[\"b\"] = 2 { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00547.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00547.stmt new file mode 100644 index 000000000000..a9af34022b43 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00547.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown.a.b.c.s\n\nw = {\"f\": 10, \"g\": 9.9} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00548.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00548.stmt new file mode 100644 index 000000000000..e7786f46c047 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00548.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown.set\n\nv[data.topdown.set.u[_]] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00549.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00549.stmt new file mode 100644 index 000000000000..adf25bacb3d4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00549.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown.no.base.doc\n\np = true { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00550.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00550.stmt new file mode 100644 index 000000000000..f59c06fb8b10 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00550.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown.a.b.c.undefined1\n\np = true { false }\np = true { false }\nq = true { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00551.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00551.stmt new file mode 100644 index 000000000000..6f87b618d308 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00551.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown.a.b.c.undefined2\n\np = true { input.foo }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00552.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00552.stmt new file mode 100644 index 000000000000..802df186ba48 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00552.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown.a.b.c.empty") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00553.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00553.stmt new file mode 100644 index 000000000000..2b59ff4d0713 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00553.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown.g.h\n\np = true { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00554.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00554.stmt new file mode 100644 index 000000000000..6ede8dbc2d14 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00554.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown.virtual.constants\n\n\t\tp = 1\n\t\tq = 2\n\t\tr = 1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00555.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00555.stmt new file mode 100644 index 000000000000..7509dc21ebd6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00555.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown.missing.input.value\n\n\t\tp = input.deadbeef") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00556.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00556.stmt new file mode 100644 index 000000000000..60ec852c97a8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00556.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown\n\np[[x1, x2, x3, x4]] { data.topdown.a.b[x1][x2][x3] = x4 }\nq[[x1, x2, x3]] { data.topdown.a.b[x1][x2][0] = x3 }\nr[[x1, x2]] { data.topdown.a.b[x1] = x2 }\ns = data.topdown.no { true }\nt = data.topdown.a.b.c.undefined1 { true }\nu = data.topdown.missing.input.value { true }\nv = data.topdown.g { true }\nw = data.topdown.set { true }\n\niterate_ground[x] { data.topdown.virtual.constants[x] = 1 }\n") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00557.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00557.stmt new file mode 100644 index 000000000000..99351c49d19b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00557.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown.conflicts\n\n\t\tk = \"bar\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00558.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00558.stmt new file mode 100644 index 000000000000..e32c3e402ab2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00558.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("base/virtual") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00559.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00559.stmt new file mode 100644 index 000000000000..bb5d680ee6e2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00559.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("topdown") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00560.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00560.stmt new file mode 100644 index 000000000000..beac7e34c303 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00560.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00561.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00561.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00561.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00562.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00562.stmt new file mode 100644 index 000000000000..b54907487f32 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00562.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\n\t\t[\"c\", \"p\", 0, 1],\n\t\t[\"c\", \"p\", 1, 2],\n\t\t[\"c\", \"q\", 0, 3],\n\t\t[\"c\", \"q\", 1, 4],\n\t\t[\"c\", \"r\", \"a\", 1],\n\t\t[\"c\", \"r\", \"b\", 2],\n\t\t[\"c\", \"x\", 0, 100],\n\t\t[\"c\", \"x\", 1, 200],\n\t\t[\"c\", \"z\", \"a\", \"b\"],\n\t\t[\"c\", \"s\", \"w\", {\"f\":10, \"g\": 9.9}]\n\t]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00563.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00563.stmt new file mode 100644 index 000000000000..bb5d680ee6e2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00563.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("topdown") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00564.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00564.stmt new file mode 100644 index 000000000000..5cfaccceb022 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00564.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00565.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00565.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00565.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00566.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00566.stmt new file mode 100644 index 000000000000..7b62b7486e7f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00566.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\n\t\t[\"c\", \"p\", 1],\n\t\t[\"c\", \"q\", 3],\n\t\t[\"c\", \"x\", 100]\n\t]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00567.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00567.stmt new file mode 100644 index 000000000000..bb5d680ee6e2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00567.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("topdown") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00568.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00568.stmt new file mode 100644 index 000000000000..99193dedd483 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00568.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("r") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00569.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00569.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00569.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00570.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00570.stmt new file mode 100644 index 000000000000..7c791454f17a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00570.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\n\t\t[\"c\", {\n\t\t\t\"p\": [1,2],\n\t\t\t\"q\": [3,4],\n\t\t\t\"r\": {\"a\": 1, \"b\": 2},\n\t\t\t\"s\": {\"w\": {\"f\": 10, \"g\": 9.9}},\n\t\t\t\"x\": [100,200],\n\t\t\t\"y\": false,\n\t\t\t\"z\": {\"a\": \"b\"},\n\t\t\t\"undefined1\": {},\n\t\t\t\"undefined2\": {},\n\t\t\t\"empty\": {}\n\t\t}]\n\t]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00571.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00571.stmt new file mode 100644 index 000000000000..bb5d680ee6e2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00571.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("topdown") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00572.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00572.stmt new file mode 100644 index 000000000000..6871b8c2e57e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00572.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("w") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00573.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00573.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00573.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00574.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00574.stmt new file mode 100644 index 000000000000..fbca192776cc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00574.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\n\t\t\"v\": [1,2,3,4],\n\t\t\"u\": [1,2,3,4]\n\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00575.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00575.stmt new file mode 100644 index 000000000000..bb5d680ee6e2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00575.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("topdown") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00576.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00576.stmt new file mode 100644 index 000000000000..b943e76c3abd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00576.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("s") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00577.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00577.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00577.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00578.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00578.stmt new file mode 100644 index 000000000000..e4d9d6f7a545 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00578.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"base\": {\"doc\": {\"p\": true}}}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00579.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00579.stmt new file mode 100644 index 000000000000..bb5d680ee6e2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00579.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("topdown") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00580.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00580.stmt new file mode 100644 index 000000000000..23e2f7f1d5d7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00580.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00581.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00581.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00581.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00582.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00582.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00582.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00583.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00583.stmt new file mode 100644 index 000000000000..bb5d680ee6e2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00583.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("topdown") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00584.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00584.stmt new file mode 100644 index 000000000000..4c913e763f7d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00584.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("v") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00585.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00585.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00585.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00586.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00586.stmt new file mode 100644 index 000000000000..6fde98c15e02 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00586.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"h\": {\"k\": [1,2,3]}}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00587.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00587.stmt new file mode 100644 index 000000000000..bb5d680ee6e2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00587.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("topdown") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00588.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00588.stmt new file mode 100644 index 000000000000..7fc48c9aef73 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00588.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("u") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00589.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00589.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00589.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00590.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00590.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00590.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00591.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00591.stmt new file mode 100644 index 000000000000..a61cbb7ffbda --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00591.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("iterate ground") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00592.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00592.stmt new file mode 100644 index 000000000000..bb5d680ee6e2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00592.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("topdown") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00593.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00593.stmt new file mode 100644 index 000000000000..cc754d578049 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00593.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("iterate_ground") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00594.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00594.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00594.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00595.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00595.stmt new file mode 100644 index 000000000000..6d81c05fe170 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00595.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"p\", \"r\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00596.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00596.stmt new file mode 100644 index 000000000000..fb4b64dc1d4b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00596.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("topdown.conflicts") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00597.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00597.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00597.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00598.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00598.stmt new file mode 100644 index 000000000000..c8b9a1bbff2b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00598.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"k\": \"foo\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00599.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00599.stmt new file mode 100644 index 000000000000..3412780639e9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00599.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ground ref") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00600.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00600.stmt new file mode 100644 index 000000000000..35bf9903fc2e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00600.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[h[0][0]] = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00601.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00601.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00601.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00602.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00602.stmt new file mode 100644 index 000000000000..a81e44050d71 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00602.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("non-ground ref") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00603.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00603.stmt new file mode 100644 index 000000000000..2d062afb61f8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00603.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = a[h[i][j]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00604.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00604.stmt new file mode 100644 index 000000000000..2742b118226a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00604.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00605.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00605.stmt new file mode 100644 index 000000000000..941bdcb705da --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00605.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("two deep") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00606.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00606.stmt new file mode 100644 index 000000000000..cd9b8b2e1999 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00606.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = a[a[a[i]]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00607.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00607.stmt new file mode 100644 index 000000000000..b553655062b3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00607.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00608.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00608.stmt new file mode 100644 index 000000000000..941bdcb705da --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00608.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("two deep") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00609.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00609.stmt new file mode 100644 index 000000000000..986cb6157a81 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00609.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = a[h[i][a[j]]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00610.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00610.stmt new file mode 100644 index 000000000000..b553655062b3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00610.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00611.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00611.stmt new file mode 100644 index 000000000000..679441e168b5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00611.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("two deep repeated var") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00612.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00612.stmt new file mode 100644 index 000000000000..58195c087301 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00612.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = a[h[i][a[i]]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00613.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00613.stmt new file mode 100644 index 000000000000..a90806808a7d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00613.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00614.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00614.stmt new file mode 100644 index 000000000000..f76e69436744 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00614.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("no suffix") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00615.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00615.stmt new file mode 100644 index 000000000000..d8abcd222238 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00615.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 4 = a[three] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00616.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00616.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00616.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00617.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00617.stmt new file mode 100644 index 000000000000..dc64e84d770b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00617.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("var ref") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00618.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00618.stmt new file mode 100644 index 000000000000..d2920f352ccd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00618.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { x = [1, 2, 3]; y = a[x[_]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00619.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00619.stmt new file mode 100644 index 000000000000..2742b118226a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00619.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00620.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00620.stmt new file mode 100644 index 000000000000..87c38d941393 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00620.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("undefined") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00621.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00621.stmt new file mode 100644 index 000000000000..106d9e5a3167 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00621.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[three.deadbeef] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00622.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00622.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00622.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00623.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00623.stmt new file mode 100644 index 000000000000..7bbd6ae664b7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00623.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = a[q[_]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00624.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00624.stmt new file mode 100644 index 000000000000..93f975e6049d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00624.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = [2, 3] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00625.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00625.stmt new file mode 100644 index 000000000000..b553655062b3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00625.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00626.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00626.stmt new file mode 100644 index 000000000000..cc5c7f3961ff --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00626.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = a[q[1]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00627.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00627.stmt new file mode 100644 index 000000000000..93f975e6049d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00627.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = [2, 3] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00628.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00628.stmt new file mode 100644 index 000000000000..e5eccd1cf0fc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00628.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00629.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00629.stmt new file mode 100644 index 000000000000..800784899a70 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00629.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { 2 = a[q] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00630.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00630.stmt new file mode 100644 index 000000000000..fb8e6baa1768 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00630.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = 1 { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00631.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00631.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00631.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00632.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00632.stmt new file mode 100644 index 000000000000..7bbd6ae664b7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00632.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = a[q[_]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00633.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00633.stmt new file mode 100644 index 000000000000..f54d2f65c764 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00633.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { o = {\"a\": 2, \"b\": 3, \"c\": 100}; o[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00634.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00634.stmt new file mode 100644 index 000000000000..b553655062b3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00634.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00635.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00635.stmt new file mode 100644 index 000000000000..ccd81fa34c3a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00635.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = a[q.b] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00636.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00636.stmt new file mode 100644 index 000000000000..f54d2f65c764 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00636.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { o = {\"a\": 2, \"b\": 3, \"c\": 100}; o[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00637.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00637.stmt new file mode 100644 index 000000000000..e5eccd1cf0fc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00637.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00638.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00638.stmt new file mode 100644 index 000000000000..04e5fc4924da --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00638.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = a[q[b[_]]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00639.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00639.stmt new file mode 100644 index 000000000000..7057f9d7a075 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00639.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {\"hello\": 1, \"goodbye\": 3, \"deadbeef\": 1000} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00640.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00640.stmt new file mode 100644 index 000000000000..d6de5bcda55a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00640.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00641.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00641.stmt new file mode 100644 index 000000000000..04e5fc4924da --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00641.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = a[q[b[_]]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00642.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00642.stmt new file mode 100644 index 000000000000..0745039bf873 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00642.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { o = {\"hello\": 1, \"goodbye\": 3, \"deadbeef\": 1000}; o[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00643.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00643.stmt new file mode 100644 index 000000000000..d6de5bcda55a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00643.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00644.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00644.stmt new file mode 100644 index 000000000000..c21bb9c599e5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00644.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = a[q[d.e[_]]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00645.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00645.stmt new file mode 100644 index 000000000000..36b98f7345b5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00645.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { strings[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00646.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00646.stmt new file mode 100644 index 000000000000..b553655062b3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00646.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00647.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00647.stmt new file mode 100644 index 000000000000..7e3b7c45ae6f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00647.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { x = q[a[_]].v[r[a[_]]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00648.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00648.stmt new file mode 100644 index 000000000000..dabea399e27b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00648.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = [{\"v\": {}}, {\"v\": [0, 0, 1, 2]}, {\"v\": [0, 0, 3, 4]}, {\"v\": [0, 0]}, {}] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00649.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00649.stmt new file mode 100644 index 000000000000..bee75d85c163 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00649.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("r = [1, 2, 3, 4] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00650.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00650.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00650.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00651.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00651.stmt new file mode 100644 index 000000000000..9cb2f1cd2d13 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00651.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ground") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00652.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00652.stmt new file mode 100644 index 000000000000..191ba3309778 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00652.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { v = [[1, 2], [2, 3], [3, 4]]; x = v[2][1] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00653.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00653.stmt new file mode 100644 index 000000000000..e5eccd1cf0fc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00653.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00654.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00654.stmt new file mode 100644 index 000000000000..cb535db0d633 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00654.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("non-ground") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00655.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00655.stmt new file mode 100644 index 000000000000..5bbc0ea63ad1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00655.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { v = [[1, 2], [2, 3], [3, 4]]; x = v[i][j] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00656.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00656.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00656.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00657.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00657.stmt new file mode 100644 index 000000000000..6dfa2fdc9b96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00657.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("mixed") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00658.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00658.stmt new file mode 100644 index 000000000000..6017aae8fd83 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00658.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] = y { v = [{\"a\": 1, \"b\": 2}, {\"c\": 3, \"z\": [4]}]; y = v[i][x][j] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00659.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00659.stmt new file mode 100644 index 000000000000..c6587ed185ee --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00659.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"z\": 4}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00660.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00660.stmt new file mode 100644 index 000000000000..74d151f9ce08 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00660.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ref binding") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00661.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00661.stmt new file mode 100644 index 000000000000..bc2070f90462 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00661.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { v = c[i][j]; x = v[k]; x = true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00662.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00662.stmt new file mode 100644 index 000000000000..fa2b43b6a407 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00662.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[true]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00663.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00663.stmt new file mode 100644 index 000000000000..abd03d9d3e2a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00663.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("existing ref binding") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00664.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00664.stmt new file mode 100644 index 000000000000..f9c5f5f5447b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00664.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { q = a; q[0] = x; q[0] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00665.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00665.stmt new file mode 100644 index 000000000000..5ef9334a51c7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00665.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00666.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00666.stmt new file mode 100644 index 000000000000..fb56143c3909 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00666.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("embedded") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00667.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00667.stmt new file mode 100644 index 000000000000..fc617e01467f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00667.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { v = [1, 2, 3]; x = [{\"a\": v[i]}] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00668.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00668.stmt new file mode 100644 index 000000000000..6e928d20b987 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00668.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[{\"a\": 1}], [{\"a\": 2}], [{\"a\": 3}]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00669.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00669.stmt new file mode 100644 index 000000000000..1caa39896a90 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00669.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("embedded ref binding") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00670.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00670.stmt new file mode 100644 index 000000000000..c46bbb5ce877 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00670.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { v = c[i][j]; w = [v[0], v[1]]; x = w[y] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00671.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00671.stmt new file mode 100644 index 000000000000..8b90cda64f27 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00671.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[null, false, true, 3.14159]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00672.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00672.stmt new file mode 100644 index 000000000000..0be483f5acb6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00672.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { i = [1, 2, 3, 4]; j = [1, 2, 999]; j[k] = y; i[y] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00673.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00673.stmt new file mode 100644 index 000000000000..a65543081eca --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00673.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00674.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00674.stmt new file mode 100644 index 000000000000..dfb0daa346b5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00674.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { i = [1,2,3,4]; x = data.a[_]; i[x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00675.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00675.stmt new file mode 100644 index 000000000000..27405b82e6fa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00675.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2, 3, 4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00676.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00676.stmt new file mode 100644 index 000000000000..15d6063aa8f5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00676.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { i = {\"a\": 1, \"b\": 2, \"c\": 3}; j = [\"a\", \"c\", \"deadbeef\"]; j[k] = y; i[y] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00677.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00677.stmt new file mode 100644 index 000000000000..236924c4fc7c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00677.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00678.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00678.stmt new file mode 100644 index 000000000000..b16b624118f7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00678.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { i = {\"1\": 1, \"2\": 2, \"4\": 4}; x = data.numbers[_]; i[x] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00679.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00679.stmt new file mode 100644 index 000000000000..dd31baaf0345 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00679.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2, 4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00680.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00680.stmt new file mode 100644 index 000000000000..59ce67afc0dc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00680.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { i = {1, 2, 3, 4}; j = {1, 2, 99}; j[x]; i[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00681.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00681.stmt new file mode 100644 index 000000000000..01a870d8949f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00681.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00682.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00682.stmt new file mode 100644 index 000000000000..3c9b625a6fa9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00682.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { i = {1, 2, 3, 4}; x = data.a[_]; i[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00683.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00683.stmt new file mode 100644 index 000000000000..d244a5c8f3c4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00683.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2, 3, 4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00684.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00684.stmt new file mode 100644 index 000000000000..cef2e2502033 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00684.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { v = {[1, 999], [3, 4]}; pair = [a[2], 4]; v[pair] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00685.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00685.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00685.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00686.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00686.stmt new file mode 100644 index 000000000000..5c71e647edc7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00686.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { x = [{}, {[1, 2], [3, 4]}]; y = [3, 4]; x[i][y] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00687.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00687.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00687.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00688.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00688.stmt new file mode 100644 index 000000000000..f9769da270da --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00688.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[[i, z, r]] { x = [{}, {[1, 2], [3, 4]}]; y = [3, 4]; x[i][y][z] = r }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00689.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00689.stmt new file mode 100644 index 000000000000..697a721f4f2a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00689.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1,0,3], [1,1,4]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00690.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00690.stmt new file mode 100644 index 000000000000..cf31e88450e1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00690.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("avoids indexer") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00691.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00691.stmt new file mode 100644 index 000000000000..e2be398c31ba --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00691.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { somevar = [1, 2, 3]; somevar[i] = 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00692.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00692.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00692.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00693.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00693.stmt new file mode 100644 index 000000000000..2dac8d77cf37 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00693.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00694.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00694.stmt new file mode 100644 index 000000000000..f9854fac74cf --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00694.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = fixture.r[[1, 2]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00695.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00695.stmt new file mode 100644 index 000000000000..42ef66872e0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00695.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00696.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00696.stmt new file mode 100644 index 000000000000..16b9c2cbf2a9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00696.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00697.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00697.stmt new file mode 100644 index 000000000000..0cd9535e76bf --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00697.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = fixture.r[{\"foo\": \"bar\"}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00698.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00698.stmt new file mode 100644 index 000000000000..585cecb94855 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00698.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"foo\": \"bar\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00699.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00699.stmt new file mode 100644 index 000000000000..cedb29a2b927 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00699.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00700.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00700.stmt new file mode 100644 index 000000000000..13b5530b3025 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00700.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = fixture.r[{1, 2}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00701.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00701.stmt new file mode 100644 index 000000000000..42ef66872e0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00701.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00702.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00702.stmt new file mode 100644 index 000000000000..93c037d12d78 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00702.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("unify array") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00703.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00703.stmt new file mode 100644 index 000000000000..1a7b2c8ce0a1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00703.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [x | fixture.r[[1, x]]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00704.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00704.stmt new file mode 100644 index 000000000000..3ef2c923fa28 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00704.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2, 3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00705.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00705.stmt new file mode 100644 index 000000000000..95b5b243ded5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00705.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("unify object") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00706.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00706.stmt new file mode 100644 index 000000000000..be03e0a8b9da --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00706.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [x | fixture.r[{\"foo\": x}]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00707.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00707.stmt new file mode 100644 index 000000000000..a756643d4575 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00707.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"bar\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00708.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00708.stmt new file mode 100644 index 000000000000..b0b567790924 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00708.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("unify partial ground array") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00709.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00709.stmt new file mode 100644 index 000000000000..1eb781ea8ccb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00709.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [x | fixture.p1[[x,2]]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00710.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00710.stmt new file mode 100644 index 000000000000..01a870d8949f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00710.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00711.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00711.stmt new file mode 100644 index 000000000000..203b704b32cf --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00711.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("complete doc unify") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00712.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00712.stmt new file mode 100644 index 000000000000..e6bd38a9a556 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00712.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [[x,y] | fixture.s[[x, y]]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00713.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00713.stmt new file mode 100644 index 000000000000..124cde6e8cbf --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00713.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1, 2], [1, 3], [2, 7], [[1,1], 4]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00714.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00714.stmt new file mode 100644 index 000000000000..d47823ae09fa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00714.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("partial doc unify") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00715.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00715.stmt new file mode 100644 index 000000000000..66ab11e58946 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00715.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [[x,y] | fixture.r[[x, y]]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00716.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00716.stmt new file mode 100644 index 000000000000..124cde6e8cbf --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00716.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1, 2], [1, 3], [2, 7], [[1,1], 4]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00717.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00717.stmt new file mode 100644 index 000000000000..f1b3e0d163fd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00717.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("empty set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00718.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00718.stmt new file mode 100644 index 000000000000..f8ce27d78889 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00718.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p { fixture.empty[set()]} ") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00719.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00719.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00719.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00720.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00720.stmt new file mode 100644 index 000000000000..461f07e69a8b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00720.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ref") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00721.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00721.stmt new file mode 100644 index 000000000000..3606cec4230e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00721.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = fixture.r[[fixture.foo.bar, 3]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00722.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00722.stmt new file mode 100644 index 000000000000..4d8186f6dba6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00722.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00723.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00723.stmt new file mode 100644 index 000000000000..b29664759ceb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00723.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("nested ref") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00724.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00724.stmt new file mode 100644 index 000000000000..b8cc610dc407 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00724.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = fixture.r[[fixture.foo[fixture.o.foo], 3]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00725.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00725.stmt new file mode 100644 index 000000000000..4d8186f6dba6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00725.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00726.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00726.stmt new file mode 100644 index 000000000000..ad12a40fb741 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00726.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("comprehension") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00727.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00727.stmt new file mode 100644 index 000000000000..eebe17a234d6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00727.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = fixture.s[[[x | x = y[_]; y = [1, 1]], 4]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00728.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00728.stmt new file mode 100644 index 000000000000..13e2d662beb3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00728.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1,1],4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00729.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00729.stmt new file mode 100644 index 000000000000..60ddf193fcf9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00729.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("missing array") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00730.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00730.stmt new file mode 100644 index 000000000000..79d1d985fb04 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00730.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = fixture.r[[1, 4]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00731.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00731.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00731.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00732.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00732.stmt new file mode 100644 index 000000000000..0c6c4e9f2041 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00732.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("missing object value") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00733.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00733.stmt new file mode 100644 index 000000000000..4f0cce936ef8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00733.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = fixture.r[{\"foo\": \"baz\"}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00734.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00734.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00734.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00735.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00735.stmt new file mode 100644 index 000000000000..3f12f08ec12b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00735.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("missing set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00736.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00736.stmt new file mode 100644 index 000000000000..38793fab7331 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00736.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = fixture.r[{1, 3}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00737.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00737.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00737.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00738.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00738.stmt new file mode 100644 index 000000000000..ab89c671ef0c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00738.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package fixture\n\t\tempty = {set()}\n\t\ts = {[1, 2], [1, 3], {\"foo\": \"bar\"}, {1, 2}, [2, 7], [[1,1], 4]}\n\t\tr[x] { s[x] }\n\t\ta = [1, 2]\n\t\to = {\"foo\": \"bar\"}\n\t\tfoo = {\"bar\": 1}\n\n\t\tp1[[1,2]]\n\t\tp1[[1,3]]\n\t\tp1[[2,2]]\n\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00739.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00739.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00739.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00740.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00740.stmt new file mode 100644 index 000000000000..beac7e34c303 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00740.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00741.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00741.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00741.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00742.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00742.stmt new file mode 100644 index 000000000000..2ed94163a022 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00742.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00743.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00743.stmt new file mode 100644 index 000000000000..16e2095d8160 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00743.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { b[j] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00744.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00744.stmt new file mode 100644 index 000000000000..2bc8b7d3a527 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00744.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4,\"hello\",\"goodbye\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00745.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00745.stmt new file mode 100644 index 000000000000..819eee17c614 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00745.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[100] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00746.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00746.stmt new file mode 100644 index 000000000000..6bf88cc930ce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00746.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { a[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00747.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00747.stmt new file mode 100644 index 000000000000..6b4e6602e826 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00747.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[0,1,2,3,100]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00748.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00748.stmt new file mode 100644 index 000000000000..7d24b0105962 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00748.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[k] = v { b[v] = k }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00749.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00749.stmt new file mode 100644 index 000000000000..226e53472dc4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00749.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[k] = v { a[i] = v; g[k][j] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00750.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00750.stmt new file mode 100644 index 000000000000..748325f42e16 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00750.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"b\": 2, \"c\": 4, \"hello\": \"v1\", \"goodbye\": \"v2\", \"a\": 1}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00751.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00751.stmt new file mode 100644 index 000000000000..96b53b375de6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00751.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[\"a\"] = 1 { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00752.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00752.stmt new file mode 100644 index 000000000000..dcd38bc92877 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00752.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[\"b\"] = 2 { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00753.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00753.stmt new file mode 100644 index 000000000000..a9259de75196 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00753.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"a\": 1, \"b\": 2}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00754.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00754.stmt new file mode 100644 index 000000000000..18646fd7548c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00754.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00755.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00755.stmt new file mode 100644 index 000000000000..371e9b4cb9c8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00755.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] { a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00756.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00756.stmt new file mode 100644 index 000000000000..6e24ff0a2f10 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00756.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[y] { b[j] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00757.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00757.stmt new file mode 100644 index 000000000000..2bc8b7d3a527 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00757.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4,\"hello\",\"goodbye\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00758.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00758.stmt new file mode 100644 index 000000000000..8287c1ccae1e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00758.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q = s; s[x] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00759.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00759.stmt new file mode 100644 index 000000000000..cedfb2957004 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00759.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] { a[_] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00760.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00760.stmt new file mode 100644 index 000000000000..7e18a7b3ca83 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00760.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[y] { b[_] = y }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00761.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00761.stmt new file mode 100644 index 000000000000..2bc8b7d3a527 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00761.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4,\"hello\",\"goodbye\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00762.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00762.stmt new file mode 100644 index 000000000000..22dd129f6a58 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00762.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[k] = v { q[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00763.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00763.stmt new file mode 100644 index 000000000000..81810451e04a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00763.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { b[v] = k }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00764.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00764.stmt new file mode 100644 index 000000000000..5c2f436490a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00764.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { a[i] = v; g[k][j] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00765.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00765.stmt new file mode 100644 index 000000000000..748325f42e16 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00765.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"b\": 2, \"c\": 4, \"hello\": \"v1\", \"goodbye\": \"v2\", \"a\": 1}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00766.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00766.stmt new file mode 100644 index 000000000000..22dd129f6a58 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00766.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[k] = v { q[k] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00767.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00767.stmt new file mode 100644 index 000000000000..44165fac41ee --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00767.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[\"a\"] = 1 { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00768.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00768.stmt new file mode 100644 index 000000000000..24be1f5ea13d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00768.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[\"b\"] = 2 { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00769.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00769.stmt new file mode 100644 index 000000000000..a9259de75196 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00769.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"a\": 1, \"b\": 2}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00770.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00770.stmt new file mode 100644 index 000000000000..c1a3f3ec7da8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00770.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00771.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00771.stmt new file mode 100644 index 000000000000..c1a3f3ec7da8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00771.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00772.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00772.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00772.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00773.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00773.stmt new file mode 100644 index 000000000000..e042c19606a3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00773.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00774.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00774.stmt new file mode 100644 index 000000000000..fcfd400dff0d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00774.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = false { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00775.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00775.stmt new file mode 100644 index 000000000000..dca38c54ce52 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00775.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = false { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00776.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00776.stmt new file mode 100644 index 000000000000..e042c19606a3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00776.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00777.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00777.stmt new file mode 100644 index 000000000000..e042c19606a3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00777.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00778.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00778.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00778.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00779.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00779.stmt new file mode 100644 index 000000000000..e042c19606a3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00779.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00780.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00780.stmt new file mode 100644 index 000000000000..fcfd400dff0d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00780.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = false { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00781.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00781.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00781.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00782.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00782.stmt new file mode 100644 index 000000000000..d68cb349a4f7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00782.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00783.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00783.stmt new file mode 100644 index 000000000000..c755552b0bdc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00783.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = true { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00784.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00784.stmt new file mode 100644 index 000000000000..b62c2843cec4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00784.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = false { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00785.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00785.stmt new file mode 100644 index 000000000000..d68cb349a4f7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00785.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { q }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00786.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00786.stmt new file mode 100644 index 000000000000..c755552b0bdc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00786.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = true { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00787.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00787.stmt new file mode 100644 index 000000000000..c755552b0bdc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00787.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = true { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00788.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00788.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00788.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00789.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00789.stmt new file mode 100644 index 000000000000..5e601df7e400 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00789.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { not true = false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00790.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00790.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00790.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00791.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00791.stmt new file mode 100644 index 000000000000..37e610e305dd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00791.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { not true = true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00792.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00792.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00792.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00793.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00793.stmt new file mode 100644 index 000000000000..ac4f8f4acd92 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00793.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { not q.v0 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00794.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00794.stmt new file mode 100644 index 000000000000..d3221dfdcf05 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00794.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] { b[x] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00795.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00795.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00795.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00796.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00796.stmt new file mode 100644 index 000000000000..c2ebffe4dfcc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00796.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { not q.v2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00797.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00797.stmt new file mode 100644 index 000000000000..d3221dfdcf05 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00797.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[x] { b[x] = v }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00798.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00798.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00798.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00799.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00799.stmt new file mode 100644 index 000000000000..082c87da4379 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00799.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array simple") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00800.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00800.stmt new file mode 100644 index 000000000000..0fc6851aa2e8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00800.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[i] { xs = [x | x = a[_]]; xs[i] > 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00801.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00801.stmt new file mode 100644 index 000000000000..419ebc08ac6d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00801.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00802.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00802.stmt new file mode 100644 index 000000000000..8ef258f23157 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00802.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array nested") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00803.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00803.stmt new file mode 100644 index 000000000000..5d47f5ed8d7e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00803.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[i] { ys = [y | y = x[_]; x = [z | z = a[_]]]; ys[i] > 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00804.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00804.stmt new file mode 100644 index 000000000000..419ebc08ac6d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00804.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00805.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00805.stmt new file mode 100644 index 000000000000..05c3974c5f43 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00805.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array embedded array") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00806.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00806.stmt new file mode 100644 index 000000000000..a51ee1ffe686 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00806.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[i] { xs = [[x | x = a[_]]]; xs[0][i] > 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00807.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00807.stmt new file mode 100644 index 000000000000..419ebc08ac6d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00807.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00808.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00808.stmt new file mode 100644 index 000000000000..0085123b563a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00808.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array embedded object") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00809.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00809.stmt new file mode 100644 index 000000000000..9348bf67cc4a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00809.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[i] { xs = {\"a\": [x | x = a[_]]}; xs.a[i] > 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00810.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00810.stmt new file mode 100644 index 000000000000..419ebc08ac6d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00810.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00811.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00811.stmt new file mode 100644 index 000000000000..1579e0059e6b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00811.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array embedded set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00812.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00812.stmt new file mode 100644 index 000000000000..376edd7a0d18 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00812.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = xs { xs = {[x | x = a[_]]} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00813.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00813.stmt new file mode 100644 index 000000000000..3d300b02347a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00813.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1,2,3,4]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00814.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00814.stmt new file mode 100644 index 000000000000..1217e99049f0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00814.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array closure") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00815.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00815.stmt new file mode 100644 index 000000000000..c33e5ad3b6b5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00815.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { y = 1; x = [y | y = 1] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00816.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00816.stmt new file mode 100644 index 000000000000..0498d0008751 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00816.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00817.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00817.stmt new file mode 100644 index 000000000000..a711df3b6cdd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00817.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array dereference embedded") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00818.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00818.stmt new file mode 100644 index 000000000000..c4d21c679de3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00818.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q.a[2][i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00819.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00819.stmt new file mode 100644 index 000000000000..a7b7c92e2d86 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00819.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { k = \"a\"; v = [y | i[_] = _; i = y; i = [z | z = a[_]]] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00820.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00820.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00820.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00821.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00821.stmt new file mode 100644 index 000000000000..3f5ecc9a65bc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00821.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object simple") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00822.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00822.stmt new file mode 100644 index 000000000000..22b86c70d982 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00822.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[i] { xs = {s: x | x = a[_]; format_int(x, 10, s)}; y = xs[i]; y > 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00823.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00823.stmt new file mode 100644 index 000000000000..69239a67ccee --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00823.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"2\",\"3\",\"4\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00824.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00824.stmt new file mode 100644 index 000000000000..d430765069de --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00824.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object nested") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00825.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00825.stmt new file mode 100644 index 000000000000..900402cadc8c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00825.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = r { r = {x: y | z = {i: q | i = b[q]}; x = z[y]}}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00826.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00826.stmt new file mode 100644 index 000000000000..4b9611d86565 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00826.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"v1\": \"hello\", \"v2\": \"goodbye\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00827.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00827.stmt new file mode 100644 index 000000000000..2c29db2cdfef --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00827.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object embedded array") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00828.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00828.stmt new file mode 100644 index 000000000000..b0a281f29cf2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00828.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[i] { xs = [{s: x | x = a[_]; format_int(x, 10, s)}]; xs[0][i] > 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00829.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00829.stmt new file mode 100644 index 000000000000..69239a67ccee --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00829.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"2\",\"3\",\"4\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00830.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00830.stmt new file mode 100644 index 000000000000..74f0d198cb9b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00830.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object embedded object") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00831.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00831.stmt new file mode 100644 index 000000000000..b77871b23e87 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00831.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[i] { xs = {\"a\": {s: x | x = a[_]; format_int(x, 10, s)}}; xs.a[i] > 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00832.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00832.stmt new file mode 100644 index 000000000000..69239a67ccee --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00832.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"2\",\"3\",\"4\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00833.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00833.stmt new file mode 100644 index 000000000000..bfb8dbe8075e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00833.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object embedded set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00834.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00834.stmt new file mode 100644 index 000000000000..092654c80298 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00834.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = xs { xs = {{s: x | x = a[_]; format_int(x, 10, s)}} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00835.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00835.stmt new file mode 100644 index 000000000000..39f956e63971 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00835.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[{\"1\":1,\"2\":2,\"3\":3,\"4\":4}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00836.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00836.stmt new file mode 100644 index 000000000000..38483ef770e7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00836.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object closure") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00837.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00837.stmt new file mode 100644 index 000000000000..cefe0f55629b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00837.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { y = 1; x = {\"foo\":y | y = 1} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00838.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00838.stmt new file mode 100644 index 000000000000..9b8abe4f4816 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00838.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[{\"foo\": 1}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00839.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00839.stmt new file mode 100644 index 000000000000..0f9c749d2eec --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00839.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object dereference embedded") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00840.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00840.stmt new file mode 100644 index 000000000000..9823882df818 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00840.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("a = [4] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00841.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00841.stmt new file mode 100644 index 000000000000..02bb6b2b9a5a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00841.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q.a = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00842.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00842.stmt new file mode 100644 index 000000000000..c8a0bfaedffb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00842.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { k = \"a\"; v = {\"bar\": y | i[_] = _; i = y; i = {\"foo\": z | z = a[_]}} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00843.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00843.stmt new file mode 100644 index 000000000000..1e6cd1e426b5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00843.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[{\"bar\": {\"foo\": 4}}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00844.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00844.stmt new file mode 100644 index 000000000000..c4a8ec0b6808 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00844.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object conflict") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00845.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00845.stmt new file mode 100644 index 000000000000..02bb6b2b9a5a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00845.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q.a = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00846.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00846.stmt new file mode 100644 index 000000000000..c8a0bfaedffb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00846.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { k = \"a\"; v = {\"bar\": y | i[_] = _; i = y; i = {\"foo\": z | z = a[_]}} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00847.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00847.stmt new file mode 100644 index 000000000000..4ad672911b6b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00847.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set simple") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00848.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00848.stmt new file mode 100644 index 000000000000..43928374158d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00848.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = y {y = {x | x = a[_]; x > 1}}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00849.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00849.stmt new file mode 100644 index 000000000000..2742b118226a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00849.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00850.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00850.stmt new file mode 100644 index 000000000000..387b459a0e01 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00850.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set nested") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00851.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00851.stmt new file mode 100644 index 000000000000..d9521e315c36 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00851.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[i] { ys = {y | y = x[_]; x = {z | z = a[_]}}; ys[i] > 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00852.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00852.stmt new file mode 100644 index 000000000000..2742b118226a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00852.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00853.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00853.stmt new file mode 100644 index 000000000000..e5240f96fb33 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00853.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set embedded array") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00854.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00854.stmt new file mode 100644 index 000000000000..13e53456f946 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00854.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[i] { xs = [{x | x = a[_]}]; xs[0][i] > 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00855.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00855.stmt new file mode 100644 index 000000000000..2742b118226a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00855.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00856.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00856.stmt new file mode 100644 index 000000000000..eb2d256767d0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00856.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set embedded object") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00857.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00857.stmt new file mode 100644 index 000000000000..496707067693 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00857.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[i] { xs = {\"a\": {x | x = a[_]}}; xs.a[i] > 1 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00858.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00858.stmt new file mode 100644 index 000000000000..2742b118226a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00858.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00859.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00859.stmt new file mode 100644 index 000000000000..6830304e0046 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00859.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set embedded set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00860.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00860.stmt new file mode 100644 index 000000000000..8251c30d8a52 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00860.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = xs { xs = {{x | x = a[_]}} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00861.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00861.stmt new file mode 100644 index 000000000000..3d300b02347a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00861.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1,2,3,4]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00862.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00862.stmt new file mode 100644 index 000000000000..c03bc0342caa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00862.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set closure") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00863.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00863.stmt new file mode 100644 index 000000000000..e93bb65b0a40 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00863.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { y = 1; x = {y | y = 1} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00864.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00864.stmt new file mode 100644 index 000000000000..0498d0008751 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00864.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[1]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00865.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00865.stmt new file mode 100644 index 000000000000..926fcbc0a691 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00865.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set dereference embedded") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00866.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00866.stmt new file mode 100644 index 000000000000..02bb6b2b9a5a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00866.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { q.a = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00867.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00867.stmt new file mode 100644 index 000000000000..22c93045c2b7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00867.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q[k] = v { k = \"a\"; v = {y | i[_] = _; i = y; i = {z | z = a[_]}} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00868.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00868.stmt new file mode 100644 index 000000000000..ab9398bb92cc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00868.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[[1,2,3,4]]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00869.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00869.stmt new file mode 100644 index 000000000000..87c38d941393 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00869.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("undefined") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00870.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00870.stmt new file mode 100644 index 000000000000..ed806f703cc7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00870.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = 1 { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00871.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00871.stmt new file mode 100644 index 000000000000..d7a66b9e4a04 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00871.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("default p = 0") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00872.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00872.stmt new file mode 100644 index 000000000000..b29b19d8abec --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00872.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = 2 { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00873.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00873.stmt new file mode 100644 index 000000000000..ee3f33997f92 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00873.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("0") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00874.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00874.stmt new file mode 100644 index 000000000000..19972a96d69d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00874.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("defined") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00875.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00875.stmt new file mode 100644 index 000000000000..d7a66b9e4a04 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00875.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("default p = 0") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00876.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00876.stmt new file mode 100644 index 000000000000..f739c859a4f5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00876.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = 1 { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00877.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00877.stmt new file mode 100644 index 000000000000..b29b19d8abec --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00877.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = 2 { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00878.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00878.stmt new file mode 100644 index 000000000000..5ef9334a51c7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00878.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00879.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00879.stmt new file mode 100644 index 000000000000..501ebbfcc649 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00879.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("defined-ooo") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00880.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00880.stmt new file mode 100644 index 000000000000..f739c859a4f5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00880.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = 1 { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00881.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00881.stmt new file mode 100644 index 000000000000..d7a66b9e4a04 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00881.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("default p = 0") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00882.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00882.stmt new file mode 100644 index 000000000000..b29b19d8abec --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00882.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = 2 { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00883.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00883.stmt new file mode 100644 index 000000000000..5ef9334a51c7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00883.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00884.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00884.stmt new file mode 100644 index 000000000000..40580ceeca3e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00884.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array comprehension") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00885.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00885.stmt new file mode 100644 index 000000000000..ed806f703cc7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00885.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = 1 { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00886.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00886.stmt new file mode 100644 index 000000000000..f88377491a2f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00886.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("default p = [x | a[_] = x]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00887.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00887.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00887.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00888.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00888.stmt new file mode 100644 index 000000000000..f52ac2ce222f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00888.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object comprehension") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00889.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00889.stmt new file mode 100644 index 000000000000..ed806f703cc7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00889.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = 1 { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00890.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00890.stmt new file mode 100644 index 000000000000..a4f61f190096 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00890.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("default p = {x: k | d[k][_] = x}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00891.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00891.stmt new file mode 100644 index 000000000000..842cf6dfeb8d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00891.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"bar\": \"e\", \"baz\": \"e\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00892.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00892.stmt new file mode 100644 index 000000000000..002940e69931 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00892.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set comprehension") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00893.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00893.stmt new file mode 100644 index 000000000000..ed806f703cc7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00893.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = 1 { false }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00894.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00894.stmt new file mode 100644 index 000000000000..021fa08fe643 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00894.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("default p = {x | a[_] = x}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00895.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00895.stmt new file mode 100644 index 000000000000..0dc272cd9fe6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00895.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00896.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00896.stmt new file mode 100644 index 000000000000..3e9f11b05702 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00896.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("plus") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00897.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00897.stmt new file mode 100644 index 000000000000..cbb1f4c847fb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00897.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { a[i] = x; y = i + x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00898.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00898.stmt new file mode 100644 index 000000000000..e9039cf85d46 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00898.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1,3,5,7]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00899.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00899.stmt new file mode 100644 index 000000000000..d5a8c4997e63 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00899.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("minus") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00900.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00900.stmt new file mode 100644 index 000000000000..807e4b411620 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00900.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { a[i] = x; y = i - x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00901.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00901.stmt new file mode 100644 index 000000000000..dc7d0f6ba6a8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00901.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[-1]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00902.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00902.stmt new file mode 100644 index 000000000000..b7fbd33007ec --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00902.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("multiply") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00903.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00903.stmt new file mode 100644 index 000000000000..378f55d8aa1d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00903.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { a[i] = x; y = i * x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00904.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00904.stmt new file mode 100644 index 000000000000..8d7437e04dfa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00904.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[0,2,6,12]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00905.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00905.stmt new file mode 100644 index 000000000000..14ef0e043dd2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00905.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("divide+round") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00906.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00906.stmt new file mode 100644 index 000000000000..1f04bc90bfed --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00906.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[z] { a[i] = x; y = i / x; round(y, z) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00907.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00907.stmt new file mode 100644 index 000000000000..ec1b64658847 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00907.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[0, 1]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00908.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00908.stmt new file mode 100644 index 000000000000..ef77bd931de4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00908.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("divide+error") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00909.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00909.stmt new file mode 100644 index 000000000000..e071ab5abf38 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00909.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[y] { a[i] = x; y = x / i }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00910.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00910.stmt new file mode 100644 index 000000000000..063d5c201550 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00910.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("divide by zero") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00911.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00911.stmt new file mode 100644 index 000000000000..531819703120 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00911.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("abs") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00912.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00912.stmt new file mode 100644 index 000000000000..7fe091ddd212 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00912.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { abs(-10, x); x = 10 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00913.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00913.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00913.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00914.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00914.stmt new file mode 100644 index 000000000000..dd1410fc0aed --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00914.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("remainder") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00915.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00915.stmt new file mode 100644 index 000000000000..2492eba9295d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00915.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { x = 7 % 4 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00916.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00916.stmt new file mode 100644 index 000000000000..2851318cefcc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00916.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("3") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00917.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00917.stmt new file mode 100644 index 000000000000..e637e9bba840 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00917.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("remainder+error") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00918.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00918.stmt new file mode 100644 index 000000000000..c30b05aead0b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00918.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { x = 7 % 0 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00919.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00919.stmt new file mode 100644 index 000000000000..26f28a68e462 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00919.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("modulo by zero") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00920.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00920.stmt new file mode 100644 index 000000000000..a31d830ca2ad --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00920.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("arity 1 ref dest") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00921.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00921.stmt new file mode 100644 index 000000000000..ff2f72a64a69 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00921.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { abs(-4, a[3]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00922.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00922.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00922.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00923.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00923.stmt new file mode 100644 index 000000000000..3680134b23b4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00923.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("arity 1 ref dest (2)") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00924.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00924.stmt new file mode 100644 index 000000000000..29c7c1c69a3d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00924.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { not abs(-5, a[3]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00925.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00925.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00925.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00926.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00926.stmt new file mode 100644 index 000000000000..8dff5b25eda9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00926.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("arity 2 ref dest") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00927.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00927.stmt new file mode 100644 index 000000000000..235212b1adba --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00927.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { a[2] = 1 + 2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00928.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00928.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00928.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00929.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00929.stmt new file mode 100644 index 000000000000..a1fc6e952e85 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00929.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("arity 2 ref dest (2)") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00930.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00930.stmt new file mode 100644 index 000000000000..28974d8756d8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00930.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { not a[2] = 2 + 3 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00931.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00931.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00931.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00932.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00932.stmt new file mode 100644 index 000000000000..92125b90a74f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00932.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("to_number") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00933.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00933.stmt new file mode 100644 index 000000000000..d37518563d68 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00933.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [x, y, z, i, j] { to_number(\"-42.0\", x); to_number(false, y); to_number(100.1, z); to_number(null, i); to_number(true, j) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00934.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00934.stmt new file mode 100644 index 000000000000..38e9f26a6dbc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00934.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[-42.0, 0, 100.1, 0, 1]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00935.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00935.stmt new file mode 100644 index 000000000000..e9f8cf5960e5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00935.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("to_number ref dest") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00936.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00936.stmt new file mode 100644 index 000000000000..235aad8d7e37 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00936.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { to_number(\"3\", a[2]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00937.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00937.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00937.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00938.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00938.stmt new file mode 100644 index 000000000000..e9f8cf5960e5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00938.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("to_number ref dest") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00939.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00939.stmt new file mode 100644 index 000000000000..7e9e78114eb5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00939.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { not to_number(\"-1\", a[2]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00940.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00940.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00940.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00941.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00941.stmt new file mode 100644 index 000000000000..451d557fab84 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00941.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p { to_number(\"broken\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00942.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00942.stmt new file mode 100644 index 000000000000..6d1cdae41292 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00942.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("invalid syntax") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00943.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00943.stmt new file mode 100644 index 000000000000..43b9e15f151f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00943.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_number") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00944.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00944.stmt new file mode 100644 index 000000000000..b93b2eea6c4a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00944.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [x, y, z] { is_number(-42.0, x); is_number(0, y); is_number(100.1, z) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00945.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00945.stmt new file mode 100644 index 000000000000..8291cd1debd3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00945.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[true, true, true]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00946.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00946.stmt new file mode 100644 index 000000000000..43b9e15f151f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00946.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_number") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00947.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00947.stmt new file mode 100644 index 000000000000..67a5cf60bffe --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00947.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { is_number(null, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00948.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00948.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00948.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00949.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00949.stmt new file mode 100644 index 000000000000..43b9e15f151f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00949.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_number") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00950.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00950.stmt new file mode 100644 index 000000000000..e29e0d0eafaa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00950.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { is_number(false, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00951.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00951.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00951.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00952.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00952.stmt new file mode 100644 index 000000000000..43b9e15f151f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00952.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_number") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00953.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00953.stmt new file mode 100644 index 000000000000..00a6a6934b23 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00953.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] {arr = [true, 1]; arr[_] = x; is_number(x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00954.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00954.stmt new file mode 100644 index 000000000000..e75637f20883 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00954.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00955.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00955.stmt new file mode 100644 index 000000000000..d1db1d5e3e84 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00955.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_string") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00956.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00956.stmt new file mode 100644 index 000000000000..2080e4737637 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00956.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [x, y, z] { is_string(\"Hello\", x); is_string(\"There\", y); is_string(\"OPA\", z) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00957.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00957.stmt new file mode 100644 index 000000000000..8291cd1debd3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00957.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[true, true, true]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00958.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00958.stmt new file mode 100644 index 000000000000..d1db1d5e3e84 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00958.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_string") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00959.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00959.stmt new file mode 100644 index 000000000000..fab914eb56c6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00959.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { is_string(null, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00960.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00960.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00960.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00961.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00961.stmt new file mode 100644 index 000000000000..d1db1d5e3e84 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00961.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_string") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00962.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00962.stmt new file mode 100644 index 000000000000..2030398a741c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00962.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { is_string(false, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00963.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00963.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00963.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00964.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00964.stmt new file mode 100644 index 000000000000..d1db1d5e3e84 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00964.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_string") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00965.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00965.stmt new file mode 100644 index 000000000000..4bcfab7a304f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00965.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] {arr = [true, 1, \"Hey\"]; arr[_] = x; is_string(x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00966.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00966.stmt new file mode 100644 index 000000000000..c960ffb9ad8f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00966.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_boolean") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00967.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00967.stmt new file mode 100644 index 000000000000..1cb9f8330687 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00967.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [x, y] { is_boolean(true, x); is_boolean(false, y) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00968.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00968.stmt new file mode 100644 index 000000000000..9fccac49f363 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00968.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[true, true]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00969.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00969.stmt new file mode 100644 index 000000000000..c960ffb9ad8f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00969.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_boolean") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00970.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00970.stmt new file mode 100644 index 000000000000..e9f9bb176358 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00970.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { is_boolean(null, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00971.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00971.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00971.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00972.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00972.stmt new file mode 100644 index 000000000000..c960ffb9ad8f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00972.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_boolean") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00973.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00973.stmt new file mode 100644 index 000000000000..376656137213 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00973.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { is_boolean(\"Hello\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00974.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00974.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00974.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00975.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00975.stmt new file mode 100644 index 000000000000..c960ffb9ad8f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00975.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_boolean") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00976.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00976.stmt new file mode 100644 index 000000000000..90b293b107d2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00976.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] {arr = [false, 1, \"Hey\"]; arr[_] = x; is_boolean(x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00977.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00977.stmt new file mode 100644 index 000000000000..cb35854d1f65 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00977.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[false]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00978.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00978.stmt new file mode 100644 index 000000000000..c6a48c30d72c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00978.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_array") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00979.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00979.stmt new file mode 100644 index 000000000000..b56a9a988463 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00979.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [x, y] { is_array([1,2,3], x); is_array([\"a\", \"b\"], y) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00980.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00980.stmt new file mode 100644 index 000000000000..9fccac49f363 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00980.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[true, true]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00981.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00981.stmt new file mode 100644 index 000000000000..c6a48c30d72c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00981.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_array") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00982.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00982.stmt new file mode 100644 index 000000000000..9ff9b63bb5e7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00982.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { is_array({1,2,3}, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00983.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00983.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00983.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00984.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00984.stmt new file mode 100644 index 000000000000..b23a87407c43 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00984.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00985.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00985.stmt new file mode 100644 index 000000000000..3bf133674b79 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00985.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [x, y] { is_set({1,2,3}, x); is_set({\"a\", \"b\"}, y) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00986.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00986.stmt new file mode 100644 index 000000000000..9fccac49f363 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00986.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[true, true]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00987.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00987.stmt new file mode 100644 index 000000000000..b23a87407c43 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00987.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00988.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00988.stmt new file mode 100644 index 000000000000..d744f43e778e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00988.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { is_set([1,2,3], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00989.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00989.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00989.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00990.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00990.stmt new file mode 100644 index 000000000000..05a5c9a09216 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00990.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_object") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00991.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00991.stmt new file mode 100644 index 000000000000..2db0c39b6315 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00991.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { is_object({\"foo\": yy | yy = 1}, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00992.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00992.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00992.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00993.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00993.stmt new file mode 100644 index 000000000000..05a5c9a09216 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00993.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_object") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00994.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00994.stmt new file mode 100644 index 000000000000..4a99bdca5ba1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00994.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { is_object(\"foo\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00995.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00995.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00995.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00996.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00996.stmt new file mode 100644 index 000000000000..27eafc40aa80 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00996.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_null") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00997.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00997.stmt new file mode 100644 index 000000000000..72a8a1b351f4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00997.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { is_null(null, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00998.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00998.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00998.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00999.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00999.stmt new file mode 100644 index 000000000000..27eafc40aa80 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/00999.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("is_null") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01000.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01000.stmt new file mode 100644 index 000000000000..fb99960498f0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01000.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { is_null(true, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01001.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01001.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01001.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01002.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01002.stmt new file mode 100644 index 000000000000..ac87b9be724c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01002.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("type_name") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01003.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01003.stmt new file mode 100644 index 000000000000..48c2eafd12e3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01003.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { type_name(null, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01004.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01004.stmt new file mode 100644 index 000000000000..56314680ecf4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01004.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("null") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01005.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01005.stmt new file mode 100644 index 000000000000..ac87b9be724c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01005.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("type_name") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01006.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01006.stmt new file mode 100644 index 000000000000..8493372e7fb0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01006.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { type_name(true, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01007.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01007.stmt new file mode 100644 index 000000000000..2bb27415bcf8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01007.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("boolean") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01008.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01008.stmt new file mode 100644 index 000000000000..ac87b9be724c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01008.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("type_name") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01009.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01009.stmt new file mode 100644 index 000000000000..faec6ec7455c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01009.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { type_name(100, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01010.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01010.stmt new file mode 100644 index 000000000000..0ab48bac0dce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01010.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("number") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01011.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01011.stmt new file mode 100644 index 000000000000..ac87b9be724c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01011.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("type_name") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01012.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01012.stmt new file mode 100644 index 000000000000..73a51c8057e5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01012.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { type_name(\"Hello\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01013.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01013.stmt new file mode 100644 index 000000000000..7373d536e2ff --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01013.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("string") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01014.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01014.stmt new file mode 100644 index 000000000000..ac87b9be724c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01014.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("type_name") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01015.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01015.stmt new file mode 100644 index 000000000000..8af6c253777f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01015.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { type_name([1,2,3], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01016.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01016.stmt new file mode 100644 index 000000000000..2dac8d77cf37 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01016.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01017.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01017.stmt new file mode 100644 index 000000000000..ac87b9be724c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01017.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("type_name") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01018.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01018.stmt new file mode 100644 index 000000000000..92227d7f7ea9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01018.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { type_name({1,2,3}, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01019.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01019.stmt new file mode 100644 index 000000000000..cedb29a2b927 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01019.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01020.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01020.stmt new file mode 100644 index 000000000000..ac87b9be724c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01020.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("type_name") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01021.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01021.stmt new file mode 100644 index 000000000000..9e57daedbda2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01021.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { type_name({\"foo\": yy | yy = 1}, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01022.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01022.stmt new file mode 100644 index 000000000000..16b9c2cbf2a9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01022.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01023.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01023.stmt new file mode 100644 index 000000000000..52ecb2e27e4b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01023.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("re_match") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01024.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01024.stmt new file mode 100644 index 000000000000..873b406c5f4f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01024.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { re_match(\"^[a-z]+\\\\[[0-9]+\\\\]$\", \"foo[1]\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01025.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01025.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01025.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01026.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01026.stmt new file mode 100644 index 000000000000..738d91f16c78 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01026.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { re_match(\"^[a-z]+\\\\[[0-9]+\\\\]$\", \"foo[\\\"bar\\\"]\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01027.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01027.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01027.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01028.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01028.stmt new file mode 100644 index 000000000000..f7ab99ee6c42 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01028.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { re_match(\"][\", \"foo[\\\"bar\\\"]\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01029.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01029.stmt new file mode 100644 index 000000000000..5aa65a68d954 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01029.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { re_match(\"^b.*$\", d.e[x]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01030.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01030.stmt new file mode 100644 index 000000000000..f5ac5d548630 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01030.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[0,1]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01031.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01031.stmt new file mode 100644 index 000000000000..0279e8cb27dc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01031.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("`^[a-z]+\\\\[[0-9]+\\\\]$`") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01032.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01032.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01032.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01033.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01033.stmt new file mode 100644 index 000000000000..0279e8cb27dc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01033.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("`^[a-z]+\\\\[[0-9]+\\\\]$`") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01034.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01034.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01034.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01035.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01035.stmt new file mode 100644 index 000000000000..164868b06e41 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01035.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { regex.split(\"^[a-z]+\\\\[[0-9]+\\\\]$\", \"\", [x]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01036.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01036.stmt new file mode 100644 index 000000000000..4c5f9b2dd869 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01036.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01037.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01037.stmt new file mode 100644 index 000000000000..fc3f2e4f7898 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01037.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [v,w,x,y] { regex.split(\"a\", \"banana\", [v,w,x,y]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01038.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01038.stmt new file mode 100644 index 000000000000..4286f12ac632 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01038.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"b\",\"n\",\"n\",\"\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01039.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01039.stmt new file mode 100644 index 000000000000..8c05ec7d9e4a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01039.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [v,w] { regex.split(\"z+\", \"pizza\", [v,w]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01040.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01040.stmt new file mode 100644 index 000000000000..1aff9026cb09 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01040.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"pi\",\"a\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01041.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01041.stmt new file mode 100644 index 000000000000..8c1441103327 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01041.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("regex.globs_match") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01042.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01042.stmt new file mode 100644 index 000000000000..77cda8300511 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01042.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { regex.globs_match(\"a.a.[0-9]+z\", \".b.b2359825792*594823z\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01043.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01043.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01043.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01044.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01044.stmt new file mode 100644 index 000000000000..8c1441103327 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01044.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("regex.globs_match") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01045.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01045.stmt new file mode 100644 index 000000000000..a8a7a329636e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01045.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { regex.globs_match(\"[a-z]+\", \"[0-9]*\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01046.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01046.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01046.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01047.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01047.stmt new file mode 100644 index 000000000000..06a4b1f928c6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01047.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { regex.globs_match(\"pqrs]\", \"[a-b]+\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01048.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01048.stmt new file mode 100644 index 000000000000..640495047585 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01048.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { regex.globs_match(\"b.*\", d.e[x]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01049.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01049.stmt new file mode 100644 index 000000000000..f5ac5d548630 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01049.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[0,1]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01050.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01050.stmt new file mode 100644 index 000000000000..ac9c06c41206 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01050.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("`[a-z]+\\\\[[0-9]+\\\\]`") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01051.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01051.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01051.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01052.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01052.stmt new file mode 100644 index 000000000000..ac9c06c41206 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01052.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("`[a-z]+\\\\[[0-9]+\\\\]`") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01053.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01053.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01053.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01054.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01054.stmt new file mode 100644 index 000000000000..23bdbc440e19 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01054.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set_diff") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01055.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01055.stmt new file mode 100644 index 000000000000..9c5d6c90b849 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01055.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { s1 = {1, 2, 3, 4}; s2 = {1, 3}; x = s1 - s2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01056.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01056.stmt new file mode 100644 index 000000000000..d6de5bcda55a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01056.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01057.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01057.stmt new file mode 100644 index 000000000000..be324957ade7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01057.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { s1 = {a[2], a[1], a[0]}; s2 = {a[0], 2}; set_diff(s1, s2, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01058.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01058.stmt new file mode 100644 index 000000000000..a90806808a7d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01058.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01059.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01059.stmt new file mode 100644 index 000000000000..1d58d3abb98a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01059.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { {1} = {1, 2, 3} - {2, 3} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01060.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01060.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01060.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01061.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01061.stmt new file mode 100644 index 000000000000..6b282217c24a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01061.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { x = s1 - s2 }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01062.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01062.stmt new file mode 100644 index 000000000000..c30fde2aa955 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01062.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("s1[1] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01063.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01063.stmt new file mode 100644 index 000000000000..f83af9985a9d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01063.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("s1[2] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01064.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01064.stmt new file mode 100644 index 000000000000..cf64cf55ac1b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01064.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("s1[\"c\"] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01065.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01065.stmt new file mode 100644 index 000000000000..86ab4b004c2b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01065.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("s2 = {\"c\", 1} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01066.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01066.stmt new file mode 100644 index 000000000000..23725d1f2916 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01066.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01067.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01067.stmt new file mode 100644 index 000000000000..5c4edb3aad33 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01067.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("intersect") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01068.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01068.stmt new file mode 100644 index 000000000000..0cfa0ce37db5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01068.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { x = {a[1], a[2], 3} & {a[2], 4, 3} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01069.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01069.stmt new file mode 100644 index 000000000000..a90806808a7d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01069.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01070.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01070.stmt new file mode 100644 index 000000000000..5ad4c2b916cf --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01070.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("union") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01071.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01071.stmt new file mode 100644 index 000000000000..e0d9f3d0d0cb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01071.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { {2, 3, 4} = {a[1], a[2], 3} | {a[2], 4, 3} }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01072.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01072.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01072.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01073.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01073.stmt new file mode 100644 index 000000000000..16a510213e28 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01073.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("format_int") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01074.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01074.stmt new file mode 100644 index 000000000000..466ce66816d9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01074.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { format_int(15.5, 16, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01075.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01075.stmt new file mode 100644 index 000000000000..588176d258a1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01075.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"f\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01076.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01076.stmt new file mode 100644 index 000000000000..5999977bd7fe --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01076.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { format_int(15.5, 16, \"10000\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01077.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01077.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01077.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01078.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01078.stmt new file mode 100644 index 000000000000..786881fdda18 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01078.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { format_int(3.1, 10, numbers[2]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01079.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01079.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01079.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01080.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01080.stmt new file mode 100644 index 000000000000..e3cc93dd62e4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01080.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { not format_int(4.1, 10, numbers[2]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01081.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01081.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01081.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01082.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01082.stmt new file mode 100644 index 000000000000..232d48ebbbb7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01082.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { format_int(4.1, 199, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01083.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01083.stmt new file mode 100644 index 000000000000..0e3609ddc6c8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01083.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("operand 2 must be one of {2, 8, 10, 16}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01084.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01084.stmt new file mode 100644 index 000000000000..46a993d06e3d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01084.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("concat") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01085.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01085.stmt new file mode 100644 index 000000000000..fe867bf153e7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01085.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { concat(\"/\", [\"\", \"foo\", \"bar\", \"0\", \"baz\"], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01086.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01086.stmt new file mode 100644 index 000000000000..3324a9b0c366 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01086.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"/foo/bar/0/baz\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01087.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01087.stmt new file mode 100644 index 000000000000..29a3432800e3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01087.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { concat(\",\", {\"1\", \"2\", \"3\"}, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01088.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01088.stmt new file mode 100644 index 000000000000..3ecc4c98fb8a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01088.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"1,2,3\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01089.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01089.stmt new file mode 100644 index 000000000000..b0ce16100955 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01089.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { concat(\"/\", [\"a\", \"b\"], \"deadbeef\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01090.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01090.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01090.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01091.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01091.stmt new file mode 100644 index 000000000000..bcb1995862a3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01091.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { concat(\"\", [\"f\", \"o\", \"o\"], c[0].x[2]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01092.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01092.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01092.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01093.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01093.stmt new file mode 100644 index 000000000000..c0ba728fe893 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01093.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { not concat(\"\", [\"b\", \"a\", \"r\"], c[0].x[2]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01094.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01094.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01094.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01095.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01095.stmt new file mode 100644 index 000000000000..4a668d705143 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01095.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("indexof") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01096.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01096.stmt new file mode 100644 index 000000000000..5887c009a1e7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01096.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { indexof(\"abcdefgh\", \"cde\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01097.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01097.stmt new file mode 100644 index 000000000000..3288bbf6caf2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01097.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01098.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01098.stmt new file mode 100644 index 000000000000..88e4475de359 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01098.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { indexof(\"abcdefgh\", \"xyz\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01099.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01099.stmt new file mode 100644 index 000000000000..90078716e39a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01099.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("-1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01100.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01100.stmt new file mode 100644 index 000000000000..0ab003f41b98 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01100.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("substring") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01101.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01101.stmt new file mode 100644 index 000000000000..bf25d1f794b6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01101.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { substring(\"abcdefgh\", 2, 3, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01102.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01102.stmt new file mode 100644 index 000000000000..ed73d66dfa79 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01102.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"cde\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01103.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01103.stmt new file mode 100644 index 000000000000..b1ecb931e502 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01103.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { substring(\"abcdefgh\", 2, -1, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01104.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01104.stmt new file mode 100644 index 000000000000..0beec79b56f7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01104.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"cdefgh\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01105.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01105.stmt new file mode 100644 index 000000000000..dfe5718f0d99 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01105.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { substring(\"abcdefgh\", 2, 10000, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01106.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01106.stmt new file mode 100644 index 000000000000..0beec79b56f7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01106.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"cdefgh\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01107.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01107.stmt new file mode 100644 index 000000000000..e6adc1fef23e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01107.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("contains") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01108.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01108.stmt new file mode 100644 index 000000000000..ac940fcfd1f2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01108.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { contains(\"abcdefgh\", \"defg\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01109.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01109.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01109.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01110.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01110.stmt new file mode 100644 index 000000000000..aed183c68583 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01110.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { contains(\"abcdefgh\", \"ac\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01111.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01111.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01111.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01112.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01112.stmt new file mode 100644 index 000000000000..a989fde2130f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01112.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("startswith") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01113.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01113.stmt new file mode 100644 index 000000000000..7d362d6455ca --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01113.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { startswith(\"abcdefgh\", \"abcd\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01114.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01114.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01114.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01115.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01115.stmt new file mode 100644 index 000000000000..354536885fb5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01115.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { startswith(\"abcdefgh\", \"bcd\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01116.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01116.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01116.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01117.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01117.stmt new file mode 100644 index 000000000000..09daec6a0fa4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01117.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("endswith") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01118.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01118.stmt new file mode 100644 index 000000000000..8dd3dd34050e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01118.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { endswith(\"abcdefgh\", \"fgh\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01119.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01119.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01119.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01120.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01120.stmt new file mode 100644 index 000000000000..7756ec72a356 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01120.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = true { endswith(\"abcdefgh\", \"fg\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01121.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01121.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01121.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01122.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01122.stmt new file mode 100644 index 000000000000..1f9c234f944c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01122.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("lower") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01123.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01123.stmt new file mode 100644 index 000000000000..5bbfa7544ddd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01123.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { lower(\"AbCdEf\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01124.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01124.stmt new file mode 100644 index 000000000000..848b296568c0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01124.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"abcdef\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01125.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01125.stmt new file mode 100644 index 000000000000..6b6315dcc801 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01125.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("upper") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01126.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01126.stmt new file mode 100644 index 000000000000..eb01e68e1f27 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01126.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { upper(\"AbCdEf\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01127.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01127.stmt new file mode 100644 index 000000000000..421f5b81955f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01127.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"ABCDEF\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01128.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01128.stmt new file mode 100644 index 000000000000..ad2a4d5f5951 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01128.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { split(\"\", \".\", [x]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01129.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01129.stmt new file mode 100644 index 000000000000..4c5f9b2dd869 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01129.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01130.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01130.stmt new file mode 100644 index 000000000000..f5a92ee7ca42 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01130.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { split(\"foo\", \".\", [x]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01131.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01131.stmt new file mode 100644 index 000000000000..c6e18f6d45db --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01131.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"foo\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01132.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01132.stmt new file mode 100644 index 000000000000..9e48c5c3e32a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01132.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [x,y] { split(\"foo.bar.baz\", \".\", [x,\"bar\",y]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01133.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01133.stmt new file mode 100644 index 000000000000..8ccc05d2043c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01133.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"foo\",\"baz\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01134.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01134.stmt new file mode 100644 index 000000000000..5973eac55e42 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01134.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { replace(\"\", \"hi\", \"bye\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01135.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01135.stmt new file mode 100644 index 000000000000..4c5f9b2dd869 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01135.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01136.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01136.stmt new file mode 100644 index 000000000000..29f631aa4d45 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01136.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { replace(\"foo.bar\", \".\", \",\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01137.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01137.stmt new file mode 100644 index 000000000000..b21103b42334 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01137.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"foo,bar\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01138.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01138.stmt new file mode 100644 index 000000000000..db59d44f34d8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01138.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { replace(\"foo.bar.baz\", \".\", \",\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01139.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01139.stmt new file mode 100644 index 000000000000..fd7d1c21ce07 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01139.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"foo,bar,baz\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01140.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01140.stmt new file mode 100644 index 000000000000..16bfb475c7e8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01140.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { replace(\"foo...bar\", \"..\", \",,\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01141.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01141.stmt new file mode 100644 index 000000000000..0dac4c6c4748 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01141.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"foo,,.bar\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01142.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01142.stmt new file mode 100644 index 000000000000..32a0c8b4abec --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01142.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { trim(\"\", \".\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01143.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01143.stmt new file mode 100644 index 000000000000..4c5f9b2dd869 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01143.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01144.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01144.stmt new file mode 100644 index 000000000000..1b110f6eef48 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01144.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { trim(\"foo.bar...\", \".\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01145.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01145.stmt new file mode 100644 index 000000000000..9646808b657d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01145.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"foo.bar\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01146.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01146.stmt new file mode 100644 index 000000000000..20d6f48bdbdd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01146.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { trim(\"...foo.bar\", \".\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01147.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01147.stmt new file mode 100644 index 000000000000..9646808b657d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01147.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"foo.bar\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01148.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01148.stmt new file mode 100644 index 000000000000..396ecee11060 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01148.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { trim(\"...foo.bar...\", \".\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01149.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01149.stmt new file mode 100644 index 000000000000..9646808b657d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01149.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"foo.bar\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01150.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01150.stmt new file mode 100644 index 000000000000..511f856bb6d7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01150.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { trim(\"...foo.bar...\", \".fr\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01151.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01151.stmt new file mode 100644 index 000000000000..c3d1b7227796 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01151.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"oo.ba\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01152.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01152.stmt new file mode 100644 index 000000000000..217f49c86f81 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01152.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { trim(\"...foo.bar...\", \".o\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01153.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01153.stmt new file mode 100644 index 000000000000..9646808b657d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01153.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"foo.bar\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01154.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01154.stmt new file mode 100644 index 000000000000..93b25fca0a33 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01154.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { sprintf(\"hi\", [], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01155.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01155.stmt new file mode 100644 index 000000000000..a4d65d88af20 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01155.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"hi\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01156.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01156.stmt new file mode 100644 index 000000000000..b2c4d2f8520e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01156.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { sprintf(\"hi %s\", [\"there\"], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01157.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01157.stmt new file mode 100644 index 000000000000..5a60ec2a8e5f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01157.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"hi there\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01158.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01158.stmt new file mode 100644 index 000000000000..7c34afe718a5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01158.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { sprintf(\"hi %02d\", [5], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01159.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01159.stmt new file mode 100644 index 000000000000..66a71715b12d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01159.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"hi 05\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01160.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01160.stmt new file mode 100644 index 000000000000..31302eb15413 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01160.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { sprintf(\"hi %02X.%02X\", [127, 1], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01161.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01161.stmt new file mode 100644 index 000000000000..0ef5a25697f8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01161.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"hi 7F.01\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01162.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01162.stmt new file mode 100644 index 000000000000..7f1bb740b952 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01162.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { sprintf(\"hi %.2f\", [3.1415], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01163.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01163.stmt new file mode 100644 index 000000000000..58b4b8616153 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01163.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"hi 3.14\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01164.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01164.stmt new file mode 100644 index 000000000000..5e458489a932 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01164.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { sprintf(\"hi %v\", [2e308], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01165.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01165.stmt new file mode 100644 index 000000000000..6bb5f019400b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01165.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"hi 2e+308\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01166.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01166.stmt new file mode 100644 index 000000000000..3d7ea6b609a1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01166.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { sprintf(\"hi %s\", [true], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01167.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01167.stmt new file mode 100644 index 000000000000..06f1a7c09b08 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01167.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"hi true\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01168.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01168.stmt new file mode 100644 index 000000000000..c0fa38b7dc7a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01168.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { sprintf(\"hi %v\", [[\"there\", 5, 3.14]], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01169.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01169.stmt new file mode 100644 index 000000000000..a4408e74055b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01169.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"hi [\\\"there\\\", 5, 3.14]\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01170.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01170.stmt new file mode 100644 index 000000000000..779d0034e09d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01170.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("marshal") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01171.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01171.stmt new file mode 100644 index 000000000000..05d20230c71a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01171.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { json.marshal([{\"foo\": {1,2,3}}], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01172.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01172.stmt new file mode 100644 index 000000000000..17c0776587e9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01172.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"[{\\\"foo\\\":[1,2,3]}]\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01173.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01173.stmt new file mode 100644 index 000000000000..5b5ef24289f9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01173.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("unmarshal") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01174.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01174.stmt new file mode 100644 index 000000000000..0e1be8576da0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01174.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { json.unmarshal(\"[{\\\"foo\\\":[1,2,3]}]\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01175.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01175.stmt new file mode 100644 index 000000000000..218b6fed3cb8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01175.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("unmarshal-non-string") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01176.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01176.stmt new file mode 100644 index 000000000000..df5b5c086947 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01176.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { json.unmarshal(data.a[0], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01177.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01177.stmt new file mode 100644 index 000000000000..c81f08a91e11 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01177.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("operand 1 must be string but got number") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01178.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01178.stmt new file mode 100644 index 000000000000..427a0bcc6a3e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01178.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("yaml round-trip") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01179.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01179.stmt new file mode 100644 index 000000000000..61f8146495d1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01179.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = y { yaml.marshal([{\"foo\": {1,2,3}}], x); yaml.unmarshal(x, y) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01180.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01180.stmt new file mode 100644 index 000000000000..cdead0235964 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01180.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[{\"foo\": [1,2,3]}]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01181.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01181.stmt new file mode 100644 index 000000000000..a891bdd643b7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01181.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("yaml unmarshal error") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01182.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01182.stmt new file mode 100644 index 000000000000..272b31a32d0c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01182.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p { yaml.unmarshal(\"[1,2,3\", _) } ") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01183.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01183.stmt new file mode 100644 index 000000000000..acf666c71b6a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01183.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encode-1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01184.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01184.stmt new file mode 100644 index 000000000000..1b5ce4e7405c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01184.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { base64.encode(\"hello\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01185.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01185.stmt new file mode 100644 index 000000000000..558b7c056cb6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01185.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"aGVsbG8=\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01186.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01186.stmt new file mode 100644 index 000000000000..611e866437e7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01186.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encode-2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01187.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01187.stmt new file mode 100644 index 000000000000..8694c1619703 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01187.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { base64.encode(\"there\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01188.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01188.stmt new file mode 100644 index 000000000000..c3f5c2a1a68b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01188.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"dGhlcmU=\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01189.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01189.stmt new file mode 100644 index 000000000000..50f1cfd88bb9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01189.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("decode-1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01190.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01190.stmt new file mode 100644 index 000000000000..ee42cadeff7f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01190.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { base64.decode(\"aGVsbG8=\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01191.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01191.stmt new file mode 100644 index 000000000000..7b2cf0a34aeb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01191.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"hello\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01192.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01192.stmt new file mode 100644 index 000000000000..6694615d5783 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01192.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("decode-2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01193.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01193.stmt new file mode 100644 index 000000000000..e5c0713f2e58 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01193.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { base64.decode(\"dGhlcmU=\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01194.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01194.stmt new file mode 100644 index 000000000000..0e3ba95ebcbd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01194.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"there\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01195.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01195.stmt new file mode 100644 index 000000000000..a2bc48d56102 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01195.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encode-slash") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01196.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01196.stmt new file mode 100644 index 000000000000..2692333f9be5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01196.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { base64.encode(\"subjects?_d\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01197.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01197.stmt new file mode 100644 index 000000000000..f34198fb1b93 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01197.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"c3ViamVjdHM/X2Q=\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01198.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01198.stmt new file mode 100644 index 000000000000..d1c7e60e73c4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01198.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("decode-slash") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01199.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01199.stmt new file mode 100644 index 000000000000..ed0a351dea9d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01199.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { base64.decode(\"c3ViamVjdHM/X2Q=\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01200.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01200.stmt new file mode 100644 index 000000000000..9cafbed6d517 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01200.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"subjects?_d\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01201.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01201.stmt new file mode 100644 index 000000000000..acf666c71b6a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01201.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encode-1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01202.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01202.stmt new file mode 100644 index 000000000000..e07c1eefef93 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01202.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { base64url.encode(\"hello\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01203.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01203.stmt new file mode 100644 index 000000000000..558b7c056cb6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01203.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"aGVsbG8=\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01204.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01204.stmt new file mode 100644 index 000000000000..611e866437e7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01204.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encode-2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01205.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01205.stmt new file mode 100644 index 000000000000..4e74581f6829 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01205.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { base64url.encode(\"there\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01206.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01206.stmt new file mode 100644 index 000000000000..c3f5c2a1a68b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01206.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"dGhlcmU=\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01207.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01207.stmt new file mode 100644 index 000000000000..50f1cfd88bb9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01207.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("decode-1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01208.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01208.stmt new file mode 100644 index 000000000000..f7abf593e5f4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01208.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { base64url.decode(\"aGVsbG8=\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01209.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01209.stmt new file mode 100644 index 000000000000..7b2cf0a34aeb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01209.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"hello\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01210.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01210.stmt new file mode 100644 index 000000000000..6694615d5783 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01210.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("decode-2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01211.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01211.stmt new file mode 100644 index 000000000000..d60cb4521829 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01211.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { base64url.decode(\"dGhlcmU=\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01212.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01212.stmt new file mode 100644 index 000000000000..0e3ba95ebcbd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01212.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"there\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01213.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01213.stmt new file mode 100644 index 000000000000..0bec16ff7e3b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01213.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encode") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01214.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01214.stmt new file mode 100644 index 000000000000..af9a95929632 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01214.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { urlquery.encode(\"a=b+1\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01215.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01215.stmt new file mode 100644 index 000000000000..18404653aae5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01215.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"a%3Db%2B1\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01216.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01216.stmt new file mode 100644 index 000000000000..026142f23ccd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01216.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encode empty") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01217.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01217.stmt new file mode 100644 index 000000000000..62e050463278 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01217.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { urlquery.encode(\"\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01218.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01218.stmt new file mode 100644 index 000000000000..4c5f9b2dd869 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01218.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01219.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01219.stmt new file mode 100644 index 000000000000..aede3667fd57 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01219.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("decode") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01220.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01220.stmt new file mode 100644 index 000000000000..057898978a7a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01220.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { urlquery.decode(\"a%3Db%2B1\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01221.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01221.stmt new file mode 100644 index 000000000000..36a5130b741b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01221.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"a=b+1\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01222.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01222.stmt new file mode 100644 index 000000000000..7e7645523670 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01222.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encode_object empty") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01223.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01223.stmt new file mode 100644 index 000000000000..f1336b6cc1a1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01223.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { urlquery.encode_object({}, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01224.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01224.stmt new file mode 100644 index 000000000000..4c5f9b2dd869 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01224.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01225.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01225.stmt new file mode 100644 index 000000000000..6da03d0e8712 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01225.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encode_object strings") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01226.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01226.stmt new file mode 100644 index 000000000000..1fcf508ff39e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01226.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { urlquery.encode_object({\"a\": \"b\", \"c\": \"d\"}, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01227.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01227.stmt new file mode 100644 index 000000000000..ad33418f61e2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01227.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"a=b&c=d\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01228.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01228.stmt new file mode 100644 index 000000000000..b823ac74725c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01228.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encode_object escape") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01229.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01229.stmt new file mode 100644 index 000000000000..adfc59caf09c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01229.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { urlquery.encode_object({\"a\": \"c=b+1\"}, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01230.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01230.stmt new file mode 100644 index 000000000000..74fffdae4559 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01230.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"a=c%3Db%2B1\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01231.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01231.stmt new file mode 100644 index 000000000000..dfddcc8f666b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01231.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encode_object array") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01232.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01232.stmt new file mode 100644 index 000000000000..19f71b11bf9a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01232.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { urlquery.encode_object({\"a\": [\"b+1\",\"c+2\"]}, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01233.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01233.stmt new file mode 100644 index 000000000000..0bd25b6f1e9e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01233.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"a=b%2B1&a=c%2B2\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01234.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01234.stmt new file mode 100644 index 000000000000..1356e5321f4f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01234.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encode_object set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01235.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01235.stmt new file mode 100644 index 000000000000..3ecad6a07bf6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01235.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { urlquery.encode_object({\"a\": {\"b+1\"}}, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01236.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01236.stmt new file mode 100644 index 000000000000..e30fff00fab7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01236.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"a=b%2B1\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01237.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01237.stmt new file mode 100644 index 000000000000..d70d87829a08 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01237.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("simple") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01238.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01238.stmt new file mode 100644 index 000000000000..523817fee60f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01238.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0.XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01239.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01239.stmt new file mode 100644 index 000000000000..f3560c44f114 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01239.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{ \"alg\": \"HS256\", \"typ\": \"JWT\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01240.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01240.stmt new file mode 100644 index 000000000000..4a04a122dd8e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01240.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{ \"sub\": \"0\", \"iss\": \"opa\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01241.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01241.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01241.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01242.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01242.stmt new file mode 100644 index 000000000000..5f2628e2a67b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01242.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("simple-non-registered") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01243.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01243.stmt new file mode 100644 index 000000000000..f3560c44f114 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01243.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{ \"alg\": \"HS256\", \"typ\": \"JWT\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01244.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01244.stmt new file mode 100644 index 000000000000..3f8882a1d960 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01244.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{ \"new\": \"I am a user created field\", \"iss\": \"opa\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01245.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01245.stmt new file mode 100644 index 000000000000..db6a62085d68 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01245.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("e949a3b1c9550c60fd8dc997fc5f112735601ed519b8bbb6a71905fd41100ab1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01246.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01246.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01246.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01247.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01247.stmt new file mode 100644 index 000000000000..c29d4c1aee28 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01247.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("no-support-jwe") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01248.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01248.stmt new file mode 100644 index 000000000000..2c3c9d15fe69 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01248.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImVuYyI6ImJsYWgifQ.eyJuZXciOiJJIGFtIGEgdXNlciBjcmVhdGVkIGZpZWxkIiwiaXNzIjoib3BhIn0.McGUb1e-UviZKy6UyQErNNQzEUgeV25Buwk7OHOa8U8") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01249.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01249.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01249.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01250.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01250.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01250.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01251.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01251.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01251.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01252.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01252.stmt new file mode 100644 index 000000000000..23dac58d7ab7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01252.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("no-periods") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01253.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01253.stmt new file mode 100644 index 000000000000..a2364c64c461 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01253.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01254.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01254.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01254.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01255.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01255.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01255.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01256.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01256.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01256.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01257.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01257.stmt new file mode 100644 index 000000000000..924b96a084d2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01257.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("encoded JWT had no period separators") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01258.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01258.stmt new file mode 100644 index 000000000000..4802a6d5db11 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01258.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("wrong-period-count") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01259.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01259.stmt new file mode 100644 index 000000000000..8aede8e43de8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01259.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXV.CJ9eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01260.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01260.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01260.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01261.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01261.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01261.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01262.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01262.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01262.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01263.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01263.stmt new file mode 100644 index 000000000000..219f56095a12 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01263.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("bad-header-encoding") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01264.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01264.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01264.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01265.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01265.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01265.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01266.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01266.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01266.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01267.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01267.stmt new file mode 100644 index 000000000000..1495817a96c8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01267.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("bad-payload-encoding") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01268.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01268.stmt new file mode 100644 index 000000000000..a614bee76017 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01268.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwia/XNzIjoib3BhIn0.XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01269.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01269.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01269.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01270.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01270.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01270.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01271.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01271.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01271.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01272.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01272.stmt new file mode 100644 index 000000000000..26ed28099567 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01272.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("bad-signature-encoding") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01273.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01273.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01273.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01274.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01274.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01274.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01275.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01275.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01275.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01276.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01276.stmt new file mode 100644 index 000000000000..1e4fbb625206 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01276.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("nested") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01277.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01277.stmt new file mode 100644 index 000000000000..f3560c44f114 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01277.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{ \"alg\": \"HS256\", \"typ\": \"JWT\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01278.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01278.stmt new file mode 100644 index 000000000000..4a04a122dd8e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01278.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{ \"sub\": \"0\", \"iss\": \"opa\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01279.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01279.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01279.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01280.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01280.stmt new file mode 100644 index 000000000000..547e6d9fae22 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01280.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("double-nested") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01281.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01281.stmt new file mode 100644 index 000000000000..686c6652a587 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01281.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.ImV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNJc0ltTjBlU0k2SWtwWFZDSjkuSW1WNVNtaGlSMk5wVDJsS1NWVjZTVEZPYVVselNXNVNOV05EU1RaSmEzQllWa05LT1M1bGVVcDZaRmRKYVU5cFNYZEphWGRwWVZoT2VrbHFiMmxpTTBKb1NXNHdMbGh0Vm05TWIwaEpNM0I0VFhSTlQxOVhVazlPVFZOS2VrZFZSRkE1Y0VScWVUaEtjREJmZEdSU1dGa2kuOFcwcXg0bUx4c2xtWmw3d0VNVVdCeEg3dFNUM1hzRXVXWHhlc1hxRm5SSSI.U8rwnGAJ-bJoGrAYKEzNtbJQWd3x1eW0Y25nLKHDCgo") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01282.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01282.stmt new file mode 100644 index 000000000000..f3560c44f114 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01282.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{ \"alg\": \"HS256\", \"typ\": \"JWT\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01283.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01283.stmt new file mode 100644 index 000000000000..4a04a122dd8e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01283.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{ \"sub\": \"0\", \"iss\": \"opa\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01284.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01284.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01284.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01285.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01285.stmt new file mode 100644 index 000000000000..844408812652 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01285.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("complex-values") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01286.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01286.stmt new file mode 100644 index 000000000000..b102dd570193 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01286.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwiaXNzIjoib3BhIiwiZXh0Ijp7ImFiYyI6IjEyMyIsImNiYSI6WzEwLCIxMCJdfX0.IIxF-uJ6i4K5Dj71xNLnUeqB9jmujl6ujTInhii1PxE") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01287.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01287.stmt new file mode 100644 index 000000000000..f3560c44f114 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01287.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{ \"alg\": \"HS256\", \"typ\": \"JWT\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01288.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01288.stmt new file mode 100644 index 000000000000..c279cc1191c8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01288.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{ \"sub\": \"0\", \"iss\": \"opa\", \"ext\": { \"abc\": \"123\", \"cba\": [10, \"10\"] } }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01289.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01289.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01289.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01290.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01290.stmt new file mode 100644 index 000000000000..005360bdf64b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01290.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("duplicate-keys") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01291.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01291.stmt new file mode 100644 index 000000000000..9f117b4cfe51 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01291.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiAiMCIsImlzcyI6ICJub3Qgb3BhIiwgImlzcyI6ICJhbHNvIG5vdCBvcGEiLCAiaXNzIjogIm9wYSJ9.XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01292.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01292.stmt new file mode 100644 index 000000000000..f3560c44f114 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01292.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{ \"alg\": \"HS256\", \"typ\": \"JWT\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01293.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01293.stmt new file mode 100644 index 000000000000..4a04a122dd8e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01293.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{ \"sub\": \"0\", \"iss\": \"opa\" }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01294.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01294.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01294.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01295.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01295.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01295.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01296.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01296.stmt new file mode 100644 index 000000000000..9ea2f66b5dbf --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01296.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = [x, y, z] { io.jwt.decode(\"%s\", [x, y, z]) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01297.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01297.stmt new file mode 100644 index 000000000000..959a0ef52408 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01297.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("success") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01298.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01298.stmt new file mode 100644 index 000000000000..3c099c341aee --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01298.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01299.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01299.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01299.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01300.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01300.stmt new file mode 100644 index 000000000000..305dfbdbf371 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01300.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("success-ps256") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01301.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01301.stmt new file mode 100644 index 000000000000..0f5d144d2e30 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01301.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ps256") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01302.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01302.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01302.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01303.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01303.stmt new file mode 100644 index 000000000000..4aedd54e4a5a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01303.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("success-es256") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01304.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01304.stmt new file mode 100644 index 000000000000..c0c72f1644b7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01304.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("es256") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01305.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01305.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01305.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01306.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01306.stmt new file mode 100644 index 000000000000..7d5a01009f9f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01306.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("failure-bad token") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01307.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01307.stmt new file mode 100644 index 000000000000..3c099c341aee --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01307.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01308.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01308.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01308.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01309.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01309.stmt new file mode 100644 index 000000000000..2b143dd2c04f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01309.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("failure-wrong key") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01310.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01310.stmt new file mode 100644 index 000000000000..0f5d144d2e30 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01310.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ps256") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01311.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01311.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01311.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01312.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01312.stmt new file mode 100644 index 000000000000..b6daa6a91feb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01312.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("failure-wrong alg") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01313.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01313.stmt new file mode 100644 index 000000000000..0f5d144d2e30 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01313.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ps256") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01314.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01314.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01314.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01315.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01315.stmt new file mode 100644 index 000000000000..fe5e7f74ce2f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01315.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("failure-invalid token") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01316.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01316.stmt new file mode 100644 index 000000000000..3c099c341aee --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01316.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01317.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01317.stmt new file mode 100644 index 000000000000..8188016a70bb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01317.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01318.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01318.stmt new file mode 100644 index 000000000000..e8424103be66 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01318.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("failure-bad cert") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01319.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01319.stmt new file mode 100644 index 000000000000..3c099c341aee --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01319.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01320.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01320.stmt new file mode 100644 index 000000000000..b2415cab5068 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01320.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("failed to decode PEM block containing certificate") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01321.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01321.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01321.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01322.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01322.stmt new file mode 100644 index 000000000000..e1d2ce755771 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01322.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { io.jwt.verify_%s(\"%s\", \"%s\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01323.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01323.stmt new file mode 100644 index 000000000000..959a0ef52408 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01323.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("success") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01324.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01324.stmt new file mode 100644 index 000000000000..12b4d447ac3f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01324.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01325.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01325.stmt new file mode 100644 index 000000000000..3e0cda8e9999 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01325.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("secret") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01326.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01326.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01326.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01327.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01327.stmt new file mode 100644 index 000000000000..7d5a01009f9f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01327.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("failure-bad token") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01328.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01328.stmt new file mode 100644 index 000000000000..ddb79c8dcea3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01328.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.R0NDxM1gHTucWQKwayMDre2PbMNR9K9efmOfygDZWcE") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01329.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01329.stmt new file mode 100644 index 000000000000..3e0cda8e9999 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01329.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("secret") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01330.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01330.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01330.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01331.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01331.stmt new file mode 100644 index 000000000000..fe5e7f74ce2f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01331.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("failure-invalid token") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01332.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01332.stmt new file mode 100644 index 000000000000..2ebf6e77174c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01332.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01333.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01333.stmt new file mode 100644 index 000000000000..3e0cda8e9999 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01333.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("secret") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01334.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01334.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01334.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01335.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01335.stmt new file mode 100644 index 000000000000..b8da2862d32a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01335.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p = x { io.jwt.verify_hs256(\"%s\", \"%s\", x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01336.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01336.stmt new file mode 100644 index 000000000000..770598c9acf6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01336.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ps256-unconstrained") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01337.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01337.stmt new file mode 100644 index 000000000000..45c4f13ec56c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01337.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01338.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01338.stmt new file mode 100644 index 000000000000..d01df943ef33 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01338.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"alg\": \"PS256\", \"typ\": \"JWT\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01339.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01339.stmt new file mode 100644 index 000000000000..bc0d5a372751 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01339.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"iss\": \"xxx\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01340.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01340.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01340.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01341.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01341.stmt new file mode 100644 index 000000000000..3e14dd890c35 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01341.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ps256-key-wrong") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01342.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01342.stmt new file mode 100644 index 000000000000..45c4f13ec56c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01342.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01343.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01343.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01343.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01344.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01344.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01344.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01345.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01345.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01345.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01346.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01346.stmt new file mode 100644 index 000000000000..c2d613ca9769 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01346.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-key-wrong") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01347.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01347.stmt new file mode 100644 index 000000000000..45c4f13ec56c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01347.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01348.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01348.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01348.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01349.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01349.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01349.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01350.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01350.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01350.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01351.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01351.stmt new file mode 100644 index 000000000000..f71255b93b59 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01351.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ps256-iss-ok") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01352.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01352.stmt new file mode 100644 index 000000000000..fc9f7c8d2bbd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01352.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"iss\": \"xxx\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01353.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01353.stmt new file mode 100644 index 000000000000..d01df943ef33 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01353.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"alg\": \"PS256\", \"typ\": \"JWT\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01354.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01354.stmt new file mode 100644 index 000000000000..bc0d5a372751 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01354.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"iss\": \"xxx\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01355.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01355.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01355.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01356.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01356.stmt new file mode 100644 index 000000000000..b0ee62193386 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01356.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ps256-iss-wrong") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01357.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01357.stmt new file mode 100644 index 000000000000..8ceac7425ad0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01357.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"iss\": \"yyy\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01358.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01358.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01358.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01359.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01359.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01359.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01360.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01360.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01360.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01361.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01361.stmt new file mode 100644 index 000000000000..779731cf6622 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01361.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ps256-alg-ok") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01362.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01362.stmt new file mode 100644 index 000000000000..4c88a0ee870c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01362.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"alg\": \"PS256\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01363.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01363.stmt new file mode 100644 index 000000000000..d01df943ef33 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01363.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"alg\": \"PS256\", \"typ\": \"JWT\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01364.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01364.stmt new file mode 100644 index 000000000000..bc0d5a372751 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01364.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"iss\": \"xxx\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01365.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01365.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01365.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01366.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01366.stmt new file mode 100644 index 000000000000..8ddef05fea51 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01366.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ps256-alg-wrong") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01367.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01367.stmt new file mode 100644 index 000000000000..c847a83de844 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01367.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"alg\": \"RS256\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01368.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01368.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01368.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01369.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01369.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01369.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01370.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01370.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01370.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01371.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01371.stmt new file mode 100644 index 000000000000..4e059e60906c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01371.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-exp-ok") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01372.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01372.stmt new file mode 100644 index 000000000000..9d8a259b9428 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01372.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"time\": 2000000000000}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01373.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01373.stmt new file mode 100644 index 000000000000..41fdc1772485 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01373.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"alg\": \"RS256\", \"typ\": \"JWT\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01374.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01374.stmt new file mode 100644 index 000000000000..ae91dd85e85a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01374.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"iss\": \"xxx\", \"exp\": 3000}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01375.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01375.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01375.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01376.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01376.stmt new file mode 100644 index 000000000000..b558f3cf1057 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01376.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-exp-expired") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01377.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01377.stmt new file mode 100644 index 000000000000..61f730bdd6dc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01377.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"time\": 4000000000000}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01378.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01378.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01378.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01379.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01379.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01379.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01380.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01380.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01380.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01381.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01381.stmt new file mode 100644 index 000000000000..a4e2c09a403d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01381.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-exp-now-expired") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01382.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01382.stmt new file mode 100644 index 000000000000..45c4f13ec56c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01382.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01383.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01383.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01383.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01384.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01384.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01384.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01385.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01385.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01385.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01386.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01386.stmt new file mode 100644 index 000000000000..65aa2ed4fe10 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01386.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-exp-now-explicit-expired") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01387.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01387.stmt new file mode 100644 index 000000000000..7d0762e8fdd1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01387.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"time\": now}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01388.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01388.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01388.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01389.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01389.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01389.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01390.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01390.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01390.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01391.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01391.stmt new file mode 100644 index 000000000000..7ff224174c7b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01391.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-nbf-ok") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01392.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01392.stmt new file mode 100644 index 000000000000..e9f2b6abb12f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01392.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJuYmYiOiAxMDAwLCAiaXNzIjogInh4eCJ9.cwwYDfJhU_ambPIpwBJwDek05miffoudprr41IAYsl0IKekb1ii2uEgwkNM-LJtVXHe9hsK3gANFyfqoJuCZIBvaNMx_3Z0BUdeBs4k1UwBiZCpuud0ofgHKURwvehNgqDvRfchq_-K_Agi2iRdl0oShgLjN-gVbBl8pRwUbQrvASlcsCpZIKUyOzXNtaIZEFh1z6ISDy8UHHOdoieKpN23swya7QAcEb0wXEEKMkkhiRd5QHgWLk37Lnw2K89mKcq4Om0CtV9nHrxxmpYGSMPojCy16Gjdg5-xKyJWvxCfb3YUBUVM4RWa7ICOPRJWPuHxu9pPYG63hb_qDU6NLsw") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01393.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01393.stmt new file mode 100644 index 000000000000..9d8a259b9428 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01393.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"time\": 2000000000000}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01394.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01394.stmt new file mode 100644 index 000000000000..41fdc1772485 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01394.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"alg\": \"RS256\", \"typ\": \"JWT\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01395.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01395.stmt new file mode 100644 index 000000000000..3ee20aef8268 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01395.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"iss\": \"xxx\", \"nbf\": 1000}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01396.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01396.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01396.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01397.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01397.stmt new file mode 100644 index 000000000000..2314344febf2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01397.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-nbf-now-ok") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01398.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01398.stmt new file mode 100644 index 000000000000..e9f2b6abb12f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01398.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJuYmYiOiAxMDAwLCAiaXNzIjogInh4eCJ9.cwwYDfJhU_ambPIpwBJwDek05miffoudprr41IAYsl0IKekb1ii2uEgwkNM-LJtVXHe9hsK3gANFyfqoJuCZIBvaNMx_3Z0BUdeBs4k1UwBiZCpuud0ofgHKURwvehNgqDvRfchq_-K_Agi2iRdl0oShgLjN-gVbBl8pRwUbQrvASlcsCpZIKUyOzXNtaIZEFh1z6ISDy8UHHOdoieKpN23swya7QAcEb0wXEEKMkkhiRd5QHgWLk37Lnw2K89mKcq4Om0CtV9nHrxxmpYGSMPojCy16Gjdg5-xKyJWvxCfb3YUBUVM4RWa7ICOPRJWPuHxu9pPYG63hb_qDU6NLsw") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01399.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01399.stmt new file mode 100644 index 000000000000..45c4f13ec56c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01399.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01400.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01400.stmt new file mode 100644 index 000000000000..41fdc1772485 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01400.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"alg\": \"RS256\", \"typ\": \"JWT\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01401.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01401.stmt new file mode 100644 index 000000000000..3ee20aef8268 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01401.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"iss\": \"xxx\", \"nbf\": 1000}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01402.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01402.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01402.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01403.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01403.stmt new file mode 100644 index 000000000000..dc0ff18b426b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01403.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-nbf-toosoon") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01404.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01404.stmt new file mode 100644 index 000000000000..e9f2b6abb12f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01404.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJuYmYiOiAxMDAwLCAiaXNzIjogInh4eCJ9.cwwYDfJhU_ambPIpwBJwDek05miffoudprr41IAYsl0IKekb1ii2uEgwkNM-LJtVXHe9hsK3gANFyfqoJuCZIBvaNMx_3Z0BUdeBs4k1UwBiZCpuud0ofgHKURwvehNgqDvRfchq_-K_Agi2iRdl0oShgLjN-gVbBl8pRwUbQrvASlcsCpZIKUyOzXNtaIZEFh1z6ISDy8UHHOdoieKpN23swya7QAcEb0wXEEKMkkhiRd5QHgWLk37Lnw2K89mKcq4Om0CtV9nHrxxmpYGSMPojCy16Gjdg5-xKyJWvxCfb3YUBUVM4RWa7ICOPRJWPuHxu9pPYG63hb_qDU6NLsw") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01405.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01405.stmt new file mode 100644 index 000000000000..9daa16e71d79 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01405.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"time\": 500000000000}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01406.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01406.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01406.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01407.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01407.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01407.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01408.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01408.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01408.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01409.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01409.stmt new file mode 100644 index 000000000000..8c5cfafdbd92 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01409.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-alg-missing") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01410.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01410.stmt new file mode 100644 index 000000000000..45c4f13ec56c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01410.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01411.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01411.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01411.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01412.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01412.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01412.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01413.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01413.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01413.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01414.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01414.stmt new file mode 100644 index 000000000000..07ccf4dae931 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01414.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-crit-junk") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01415.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01415.stmt new file mode 100644 index 000000000000..6f22ff0027a2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01415.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJjcml0IjogWyJqdW5rIl0sICJraWQiOiAiazEiLCAiYWxnIjogIlJTMjU2IiwgInR5cCI6ICJKV1QiLCAianVuayI6ICJ4eHgifQ.eyJpc3MiOiAieHh4IiwgInN1YiI6ICJmcmVkIn0.YfoUpW5CgDBtxtBuOix3cdYJGT8cX9Mq7wOhIbjDK7eRQUsAmMY_0EQPh7bd7Yi1gLI3e11BKzguf2EHqAa1kbkHWwFniBO-RIi8q42v2uxC4lpEpIjfaaXB5XmsLfAXtYRqh0AObvbSho6VDXBP_Kn81nhIiE2yFbH14_jhRMSxDBs5ToSkXV-XJHw5bONP8NxPqEk9KF3ZJGzN7J_KoD6LjqfYai5K0eLNEIZh4C1WjTdmCKMR4K6ieZRQWZiSsnhSqLSQERir4n22G3QsdY7dOnCp-SS4VYu3V-PfsOSFMvQ-TTAN1geqMZ9A7k1CCLW0wxKBs-KCiYzmRTzwxA") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01416.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01416.stmt new file mode 100644 index 000000000000..45c4f13ec56c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01416.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01417.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01417.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01417.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01418.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01418.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01418.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01419.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01419.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01419.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01420.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01420.stmt new file mode 100644 index 000000000000..1c548bee79e2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01420.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rsa256-nested") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01421.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01421.stmt new file mode 100644 index 000000000000..45c4f13ec56c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01421.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01422.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01422.stmt new file mode 100644 index 000000000000..41fdc1772485 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01422.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"alg\": \"RS256\", \"typ\": \"JWT\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01423.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01423.stmt new file mode 100644 index 000000000000..bc0d5a372751 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01423.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"iss\": \"xxx\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01424.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01424.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01424.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01425.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01425.stmt new file mode 100644 index 000000000000..82c84a1b5e2f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01425.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rsa256-nested2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01426.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01426.stmt new file mode 100644 index 000000000000..45c4f13ec56c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01426.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01427.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01427.stmt new file mode 100644 index 000000000000..41fdc1772485 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01427.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"alg\": \"RS256\", \"typ\": \"JWT\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01428.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01428.stmt new file mode 100644 index 000000000000..bc0d5a372751 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01428.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"iss\": \"xxx\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01429.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01429.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01429.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01430.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01430.stmt new file mode 100644 index 000000000000..f773792d0e8e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01430.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("es256-unconstrained") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01431.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01431.stmt new file mode 100644 index 000000000000..45c4f13ec56c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01431.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01432.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01432.stmt new file mode 100644 index 000000000000..f47cbbf8281a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01432.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"alg\": \"ES256\", \"typ\": \"JWT\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01433.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01433.stmt new file mode 100644 index 000000000000..bc0d5a372751 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01433.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"iss\": \"xxx\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01434.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01434.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01434.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01435.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01435.stmt new file mode 100644 index 000000000000..c3992bd88f75 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01435.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("hs256-unconstrained") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01436.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01436.stmt new file mode 100644 index 000000000000..12b4d447ac3f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01436.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01437.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01437.stmt new file mode 100644 index 000000000000..c70d28d3129e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01437.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"secret\": \"secret\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01438.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01438.stmt new file mode 100644 index 000000000000..96db05b64463 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01438.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"alg\": \"HS256\", \"typ\": \"JWT\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01439.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01439.stmt new file mode 100644 index 000000000000..843fe865b79f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01439.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"user\": \"alice\", \"azp\": \"alice\", \"subordinates\": [], \"hr\": false}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01440.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01440.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01440.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01441.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01441.stmt new file mode 100644 index 000000000000..9b56d193a47f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01441.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("hs256-key-wrong") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01442.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01442.stmt new file mode 100644 index 000000000000..12b4d447ac3f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01442.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01443.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01443.stmt new file mode 100644 index 000000000000..95c342f5d94c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01443.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"secret\": \"the wrong key\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01444.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01444.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01444.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01445.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01445.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01445.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01446.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01446.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01446.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01447.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01447.stmt new file mode 100644 index 000000000000..1cbdc06016b3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01447.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-aud") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01448.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01448.stmt new file mode 100644 index 000000000000..2118db9f4fae --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01448.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"aud\": \"fred\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01449.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01449.stmt new file mode 100644 index 000000000000..41fdc1772485 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01449.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"alg\": \"RS256\", \"typ\": \"JWT\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01450.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01450.stmt new file mode 100644 index 000000000000..6252eba65924 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01450.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"aud\": \"fred\", \"iss\": \"xxx\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01451.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01451.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01451.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01452.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01452.stmt new file mode 100644 index 000000000000..815fa32599ab --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01452.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-aud-list") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01453.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01453.stmt new file mode 100644 index 000000000000..a66698074b64 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01453.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"aud\": \"bob\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01454.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01454.stmt new file mode 100644 index 000000000000..41fdc1772485 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01454.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"alg\": \"RS256\", \"typ\": \"JWT\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01455.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01455.stmt new file mode 100644 index 000000000000..34763324ac8b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01455.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"aud\": [\"fred\", \"bob\"], \"iss\": \"xxx\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01456.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01456.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01456.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01457.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01457.stmt new file mode 100644 index 000000000000..c14af5ba82d0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01457.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ps256-no-aud") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01458.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01458.stmt new file mode 100644 index 000000000000..02ccf6007c09 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01458.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"aud\": \"cath\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01459.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01459.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01459.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01460.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01460.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01460.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01461.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01461.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01461.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01462.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01462.stmt new file mode 100644 index 000000000000..123fa7684745 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01462.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-missing-aud") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01463.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01463.stmt new file mode 100644 index 000000000000..45c4f13ec56c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01463.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01464.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01464.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01464.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01465.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01465.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01465.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01466.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01466.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01466.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01467.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01467.stmt new file mode 100644 index 000000000000..4a35d8a02a40 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01467.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-wrong-aud") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01468.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01468.stmt new file mode 100644 index 000000000000..02ccf6007c09 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01468.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"aud\": \"cath\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01469.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01469.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01469.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01470.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01470.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01470.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01471.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01471.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01471.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01472.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01472.stmt new file mode 100644 index 000000000000..da2822d8ed26 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01472.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("rs256-wrong-aud-list") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01473.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01473.stmt new file mode 100644 index 000000000000..02ccf6007c09 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01473.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"cert\": \"%s\", \"aud\": \"cath\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01474.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01474.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01474.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01475.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01475.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01475.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01476.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01476.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01476.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01477.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01477.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01477.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01478.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01478.stmt new file mode 100644 index 000000000000..1694d4c4a4ea --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01478.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("time caching") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01479.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01479.stmt new file mode 100644 index 000000000000..01758be51d62 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01479.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tp { time.now_ns(t0); test.sleep(\"10ms\"); time.now_ns(t1); t1 = t2 }\n\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01480.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01480.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01480.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01481.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01481.stmt new file mode 100644 index 000000000000..cc0756b1813c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01481.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("parse nanos") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01482.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01482.stmt new file mode 100644 index 000000000000..3c49be2fe252 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01482.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tp = ns { time.parse_ns(\"2006-01-02T15:04:05Z07:00\", \"2017-06-02T19:00:00-07:00\", ns) }\n\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01483.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01483.stmt new file mode 100644 index 000000000000..1603a3d7a25f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01483.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("1496455200000000000") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01484.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01484.stmt new file mode 100644 index 000000000000..67af99ca83e7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01484.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("parse rfc3339 nanos") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01485.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01485.stmt new file mode 100644 index 000000000000..5c388eadaf64 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01485.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tp = ns { time.parse_rfc3339_ns(\"2017-06-02T19:00:00-07:00\", ns) }\n\t\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01486.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01486.stmt new file mode 100644 index 000000000000..1603a3d7a25f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01486.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("1496455200000000000") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01487.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01487.stmt new file mode 100644 index 000000000000..43758bff2fcd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01487.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("parse duration nanos") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01488.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01488.stmt new file mode 100644 index 000000000000..e0404207c354 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01488.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tp = ns { time.parse_duration_ns(\"100ms\", ns) }\n\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01489.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01489.stmt new file mode 100644 index 000000000000..67a1bea7d59a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01489.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("100000000") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01490.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01490.stmt new file mode 100644 index 000000000000..f40f87cfc45d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01490.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("date") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01491.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01491.stmt new file mode 100644 index 000000000000..cf939e65b873 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01491.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tp = [year, month, day] { [year, month, day] := time.date(1517832000*1000*1000*1000) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01492.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01492.stmt new file mode 100644 index 000000000000..8f2483f5216d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01492.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2018, 2, 5]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01493.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01493.stmt new file mode 100644 index 000000000000..4df834abc260 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01493.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("date leap day") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01494.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01494.stmt new file mode 100644 index 000000000000..18a175267d20 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01494.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tp = [year, month, day] { [year, month, day] := time.date(1582977600*1000*1000*1000) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01495.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01495.stmt new file mode 100644 index 000000000000..4f233b52b973 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01495.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2020, 2, 29]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01496.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01496.stmt new file mode 100644 index 000000000000..e83681aba69f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01496.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("date too big") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01497.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01497.stmt new file mode 100644 index 000000000000..e4164d0b2968 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01497.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tp = [year, month, day] { [year, month, day] := time.date(1582977600*1000*1000*1000*1000) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01498.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01498.stmt new file mode 100644 index 000000000000..674eb75a5941 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01498.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("timestamp too big") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01499.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01499.stmt new file mode 100644 index 000000000000..90b085aa2303 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01499.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("clock") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01500.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01500.stmt new file mode 100644 index 000000000000..b3fb83366b37 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01500.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tp = [hour, minute, second] { [hour, minute, second] := time.clock(1517832000*1000*1000*1000) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01501.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01501.stmt new file mode 100644 index 000000000000..a6b5d07c5bf4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01501.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[12, 0, 0]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01502.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01502.stmt new file mode 100644 index 000000000000..04d070a1f3df --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01502.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("clock leap day") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01503.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01503.stmt new file mode 100644 index 000000000000..33db1e00505f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01503.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tp = [hour, minute, second] { [hour, minute, second] := time.clock(1582977600*1000*1000*1000) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01504.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01504.stmt new file mode 100644 index 000000000000..a6b5d07c5bf4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01504.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[12, 0, 0]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01505.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01505.stmt new file mode 100644 index 000000000000..4d52e8ea0da9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01505.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("clock too big") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01506.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01506.stmt new file mode 100644 index 000000000000..0d77fa6d2720 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01506.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tp = [hour, minute, second] { [hour, minute, second] := time.clock(1582977600*1000*1000*1000*1000) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01507.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01507.stmt new file mode 100644 index 000000000000..674eb75a5941 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01507.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("timestamp too big") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01508.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01508.stmt new file mode 100644 index 000000000000..201eae6b2813 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01508.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Monday") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01509.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01509.stmt new file mode 100644 index 000000000000..2309d6294b1b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01509.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Tuesday") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01510.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01510.stmt new file mode 100644 index 000000000000..38b939521adb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01510.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Wednesday") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01511.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01511.stmt new file mode 100644 index 000000000000..65ca98bb67ca --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01511.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Thursday") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01512.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01512.stmt new file mode 100644 index 000000000000..c92f6a04e4fa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01512.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Friday") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01513.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01513.stmt new file mode 100644 index 000000000000..803cd9abd536 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01513.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Saturday") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01514.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01514.stmt new file mode 100644 index 000000000000..cc2e127fecf3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01514.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Sunday") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01515.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01515.stmt new file mode 100644 index 000000000000..18a5f886e9e5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01515.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("weekday") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01516.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01516.stmt new file mode 100644 index 000000000000..07a45269f6ed --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01516.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("weekday too big") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01517.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01517.stmt new file mode 100644 index 000000000000..a363f9755e03 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01517.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tp = weekday { weekday := time.weekday(1582977600*1000*1000*1000*1000) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01518.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01518.stmt new file mode 100644 index 000000000000..674eb75a5941 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01518.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("timestamp too big") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01519.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01519.stmt new file mode 100644 index 000000000000..749bf16a9015 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01519.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("scalar") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01520.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01520.stmt new file mode 100644 index 000000000000..adc24ce5b7c6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01520.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { walk(data.a[0], x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01521.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01521.stmt new file mode 100644 index 000000000000..0c727869f6c9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01521.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\n\t\t\t\t[[], 1]\n\t\t\t]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01522.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01522.stmt new file mode 100644 index 000000000000..4c9a167e0bcb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01522.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("arrays") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01523.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01523.stmt new file mode 100644 index 000000000000..605806bf6498 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01523.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { walk(data.a, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01524.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01524.stmt new file mode 100644 index 000000000000..2a9b961b5b1e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01524.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\n\t\t\t\t[[], [1,2,3,4]],\n\t\t\t\t[[0], 1],\n\t\t\t\t[[1], 2],\n\t\t\t\t[[2], 3],\n\t\t\t\t[[3], 4]\n\t\t\t]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01525.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01525.stmt new file mode 100644 index 000000000000..fb3109a3540a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01525.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("objects") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01526.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01526.stmt new file mode 100644 index 000000000000..53aa1d7ab821 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01526.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { walk(data.b, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01527.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01527.stmt new file mode 100644 index 000000000000..f3a93f345f2b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01527.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\n\t\t\t\t[[], {\"v1\": \"hello\", \"v2\": \"goodbye\"}],\n\t\t\t\t[[\"v1\"], \"hello\"],\n\t\t\t\t[[\"v2\"], \"goodbye\"]\n\t\t\t]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01528.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01528.stmt new file mode 100644 index 000000000000..54c695061733 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01528.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("sets") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01529.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01529.stmt new file mode 100644 index 000000000000..f1911dec2d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01529.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[x] { walk(q, x) }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01530.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01530.stmt new file mode 100644 index 000000000000..a53788f6a4f0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01530.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {{1,2,3}} { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01531.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01531.stmt new file mode 100644 index 000000000000..d4b62ebe7279 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01531.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\n\t\t\t\t[[], [[1,2,3]]],\n\t\t\t\t[[[1,2,3]], [1,2,3]],\n\t\t\t\t[[[1,2,3], 1], 1],\n\t\t\t\t[[[1,2,3], 2], 2],\n\t\t\t\t[[[1,2,3], 3], 3]\n\t\t\t]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01532.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01532.stmt new file mode 100644 index 000000000000..4fb1aaf23590 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01532.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("match and filter") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01533.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01533.stmt new file mode 100644 index 000000000000..a5656dafe3f8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01533.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[[k,x]] { walk(q, [k, x]); contains(k[1], \"oo\") }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01534.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01534.stmt new file mode 100644 index 000000000000..4e704b6467a9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01534.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"foo\": 1,\n\t\t\t\t\t\t\"bar\": 2,\n\t\t\t\t\t\t\"bazoo\": 3,\n\t\t\t\t\t}\n\t\t\t\t] { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01535.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01535.stmt new file mode 100644 index 000000000000..5361f5176833 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01535.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[[0, \"foo\"], 1], [[0, \"bazoo\"], 3]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01536.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01536.stmt new file mode 100644 index 000000000000..c88083ddc058 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01536.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("partially ground path") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01537.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01537.stmt new file mode 100644 index 000000000000..564ee20e9310 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01537.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p[[k1,k2,x]] {\n\t\t\t\t\twalk(q, [[\"a\", k1, \"b\", k2], x])\n\t\t\t\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01538.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01538.stmt new file mode 100644 index 000000000000..dfd3cc4f6af9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01538.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("q = {\n\t\t\t\t\t\"a\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"b\": {\"foo\": 1, \"bar\": 2},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"b\": {\"baz\": 3, \"qux\": 4},\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t} { true }\n\t\t\t\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01539.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01539.stmt new file mode 100644 index 000000000000..a4b559426384 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01539.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[0, \"foo\", 1], [0, \"bar\", 2], [1, \"baz\", 3], [1, \"qux\", 4]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01540.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01540.stmt new file mode 100644 index 000000000000..b8d00426387c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01540.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package b.c.d\n\nimport data.a\nimport data.g\n\np[x] { a[i] = x; q[x] }\nq[x] { g[j][k] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01541.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01541.stmt new file mode 100644 index 000000000000..59b1374be162 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01541.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("deep embedded vdoc") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01542.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01542.stmt new file mode 100644 index 000000000000..92983034c8eb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01542.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("b") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01543.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01543.stmt new file mode 100644 index 000000000000..049d49a4233a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01543.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("c") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01544.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01544.stmt new file mode 100644 index 000000000000..3c6e696947d4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01544.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("d") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01545.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01545.stmt new file mode 100644 index 000000000000..beac7e34c303 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01545.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01546.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01546.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01546.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01547.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01547.stmt new file mode 100644 index 000000000000..dd31baaf0345 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01547.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[1, 2, 4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01548.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01548.stmt new file mode 100644 index 000000000000..d24cb982cc1d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01548.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package z\n\nimport data.a\nimport input.req1\nimport input.req2 as req2as\nimport input.req3.a.b\nimport input.req4.a.b as req4as\n\np = true { a[i] = x; req1.foo = x; req2as.bar = x; q[x] }\nq[x] { req1.foo = x; req2as.bar = x; r[x] }\nr[x] { {\"foo\": req2as.bar, \"bar\": [x]} = {\"foo\": x, \"bar\": [req1.foo]} }\ns = true { b.x[0] = 1 }\nt = true { req4as.x[0] = 1 }\nu[x] { b[_] = x; x > 1 }\nw = [[1, 2], [3, 4]] { true }\ngt1 = true { req1 > 1 }\nkeys[x] = y { data.numbers[_] = x; to_number(x, y) }\nloopback = input { true }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01549.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01549.stmt new file mode 100644 index 000000000000..cec2f601d99a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01549.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("loopback") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01550.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01550.stmt new file mode 100644 index 000000000000..5866254eae35 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01550.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("z") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01551.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01551.stmt new file mode 100644 index 000000000000..cec2f601d99a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01551.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("loopback") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01552.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01552.stmt new file mode 100644 index 000000000000..ac9b73355be0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01552.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"foo\": 1}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01553.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01553.stmt new file mode 100644 index 000000000000..ac9b73355be0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01553.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"foo\": 1}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01554.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01554.stmt new file mode 100644 index 000000000000..8c8b39b43954 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01554.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("loopback undefined") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01555.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01555.stmt new file mode 100644 index 000000000000..5866254eae35 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01555.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("z") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01556.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01556.stmt new file mode 100644 index 000000000000..cec2f601d99a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01556.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("loopback") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01557.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01557.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01557.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01558.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01558.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01558.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01559.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01559.stmt new file mode 100644 index 000000000000..d70d87829a08 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01559.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("simple") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01560.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01560.stmt new file mode 100644 index 000000000000..5866254eae35 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01560.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("z") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01561.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01561.stmt new file mode 100644 index 000000000000..beac7e34c303 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01561.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01562.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01562.stmt new file mode 100644 index 000000000000..1c8f3b737aa1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01562.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\n\t\t\"req1\": {\"foo\": 4},\n\t\t\"req2\": {\"bar\": 4}\n\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01563.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01563.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01563.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01564.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01564.stmt new file mode 100644 index 000000000000..e6cc1c074454 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01564.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("missing") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01565.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01565.stmt new file mode 100644 index 000000000000..5866254eae35 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01565.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("z") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01566.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01566.stmt new file mode 100644 index 000000000000..beac7e34c303 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01566.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01567.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01567.stmt new file mode 100644 index 000000000000..399e1cd59535 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01567.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\n\t\t\"req1\": {\"foo\": 4}\n\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01568.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01568.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01568.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01569.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01569.stmt new file mode 100644 index 000000000000..9fd7ff4d2e55 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01569.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("namespaced") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01570.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01570.stmt new file mode 100644 index 000000000000..5866254eae35 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01570.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("z") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01571.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01571.stmt new file mode 100644 index 000000000000..b943e76c3abd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01571.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("s") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01572.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01572.stmt new file mode 100644 index 000000000000..fa3ba4511a44 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01572.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\n\t\t\"req3\": {\n\t\t\t\"a\": {\n\t\t\t\t\"b\": {\n\t\t\t\t\t\"x\": [1,2,3,4]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01573.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01573.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01573.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01574.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01574.stmt new file mode 100644 index 000000000000..5866254eae35 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01574.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("z") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01575.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01575.stmt new file mode 100644 index 000000000000..23e2f7f1d5d7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01575.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01576.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01576.stmt new file mode 100644 index 000000000000..15acae092b01 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01576.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\n\t\t\"req4\": {\n\t\t\t\"a\": {\n\t\t\t\t\"b\": {\n\t\t\t\t\t\"x\": [1,2,3,4]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01577.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01577.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01577.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01578.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01578.stmt new file mode 100644 index 000000000000..1080e018c6fb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01578.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package ex\n\n\t\tfoo[\"bar\"] = 0\n\t\tfoo[\"baz\"] = 1\n\t\tfoo[\"*\"] = [1, 2, 3] {\n\t\t\tinput.foo = 7\n\t\t}\n\n\t\tbar[\"x\"]\n\t\tbar[\"y\"]\n\t\tbar[\"*\"] {\n\t\t\tinput.foo = 7\n\t\t}\n\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01579.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01579.stmt new file mode 100644 index 000000000000..b7c30ae6d128 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01579.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("obj-1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01580.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01580.stmt new file mode 100644 index 000000000000..58032e32d3f0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01580.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.foo.bar") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01581.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01581.stmt new file mode 100644 index 000000000000..ee3f33997f92 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01581.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("0") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01582.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01582.stmt new file mode 100644 index 000000000000..388482fc5fd9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01582.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("obj") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01583.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01583.stmt new file mode 100644 index 000000000000..dace4e5bad12 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01583.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.foo") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01584.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01584.stmt new file mode 100644 index 000000000000..e8fe6a552f7b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01584.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"bar\": 0, \"baz\": 1}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01585.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01585.stmt new file mode 100644 index 000000000000..9af57f059f52 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01585.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("obj-all") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01586.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01586.stmt new file mode 100644 index 000000000000..dace4e5bad12 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01586.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.foo") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01587.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01587.stmt new file mode 100644 index 000000000000..87db9995ca68 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01587.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"foo\": 7}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01588.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01588.stmt new file mode 100644 index 000000000000..c1abe6879144 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01588.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"bar\": 0, \"baz\": 1, \"*\": [1,2,3]}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01589.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01589.stmt new file mode 100644 index 000000000000..cf9e3801a809 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01589.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set-1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01590.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01590.stmt new file mode 100644 index 000000000000..bc6b4bac7943 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01590.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.bar.x") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01591.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01591.stmt new file mode 100644 index 000000000000..04004fc29595 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01591.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"x\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01592.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01592.stmt new file mode 100644 index 000000000000..cedb29a2b927 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01592.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01593.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01593.stmt new file mode 100644 index 000000000000..c6fc8ce918e1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01593.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.bar") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01594.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01594.stmt new file mode 100644 index 000000000000..ed1ff415deb4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01594.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"x\", \"y\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01595.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01595.stmt new file mode 100644 index 000000000000..3553a3818fb8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01595.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("set-all") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01596.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01596.stmt new file mode 100644 index 000000000000..c6fc8ce918e1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01596.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.bar") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01597.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01597.stmt new file mode 100644 index 000000000000..87db9995ca68 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01597.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"foo\": 7}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01598.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01598.stmt new file mode 100644 index 000000000000..effa46699c84 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01598.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"x\", \"y\", \"*\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01599.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01599.stmt new file mode 100644 index 000000000000..488b8f1020cf --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01599.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package ex\n\n\t\tfoo(x) = y {\n\t\t\tsplit(x, \"i\", y)\n\t\t}\n\n\t\tbar[x] = y {\n\t\t\tdata.l[_].a = x\n\t\t\tfoo(x, y)\n\t\t}\n\n\t\tchain0(x) = y {\n\t\t\tfoo(x, y)\n\t\t}\n\n\t\tchain1(a) = b {\n\t\t\tchain0(a, b)\n\t\t}\n\n\t\tchain2 = d {\n\t\t\tchain1(\"fooibar\", d)\n\t\t}\n\n\t\tcross(x) = [a, b] {\n\t\t\tsplit(x, \"i\", y)\n\t\t\tfoo(y[1], b)\n\t\t\tdata.test.foo(y[2], a)\n\t\t}\n\n\t\tfalsy_func(x) = false\n\n\t\tfalsy_func_else(x) = true { x = 1 } else = false { true }\n\n\t\tfalsy_undefined {\n\t\t\tfalsy_func(1)\n\t\t}\n\n\t\tfalsy_negation {\n\t\t\tnot falsy_func(1)\n\t\t}\n\n\t\tfalsy_else_value = falsy_func_else(2)\n\n\t\tfalsy_else_undefined {\n\t\t\tfalsy_func_else(2)\n\t\t}\n\n\t\tfalsy_else_negation {\n\t\t\tnot falsy_func_else(2)\n\t\t}\n\n\t\tarrays([x, y]) = [a, b] {\n\t\t\tfoo(x, a)\n\t\t\tfoo(y, b)\n\t\t}\n\n\t\tarraysrule = y {\n\t\t\tarrays([\"hih\", \"foo\"], y)\n\t\t}\n\n\t\tobjects({\"foo\": x, \"bar\": y}) = z {\n\t\t\tfoo(x, a)\n\t\t\tdata.test.foo(y, b)\n\t\t\tz = [a, b]\n\t\t}\n\n\t\tobjectsrule = y {\n\t\t\tobjects({\"foo\": \"hih\", \"bar\": \"hi ho\"}, y)\n\t\t}\n\n\t\trefoutput = y {\n\t\t\tfoo(\"hih\", z)\n\t\t\ty = z[1]\n\t\t}\n\n\t\tvoid(x) {\n\t\t\tx = \"foo\"\n\t\t}\n\n\t\tvoidGood {\n\t\t\tnot void(\"bar\", true)\n\t\t}\n\n\t\tvoidBad {\n\t\t\tvoid(\"bar\", true)\n\t\t}\n\n\t\tmulti(1, x) = y {\n\t\t\ty = x\n\t\t}\n\n\t\tmulti(2, x) = y {\n\t\t\ta = 2*x\n\t\t\ty = a+1\n\t\t}\n\n\t\tmulti(3, x) = y {\n\t\t\ty = x*10\n\t\t}\n\n\t\tmulti(\"foo\", x) = y {\n\t\t\ty = \"bar\"\n\t\t}\n\n\t\tmulti1 = y {\n\t\t\tmulti(1, 2, y)\n\t\t}\n\n\t\tmulti2 = y {\n\t\t\tmulti(2, 2, y)\n\t\t}\n\n\t\tmulti3 = y {\n\t\t\tmulti(3, 2, y)\n\t\t}\n\n\t\tmulti4 = y {\n\t\t\tmulti(\"foo\", 2, y)\n\t\t}\n\n\t\talways_true_fn(x)\n\n\t\talways_true {\n\t\t\talways_true_fn(1)\n\t\t}\n\t\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01600.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01600.stmt new file mode 100644 index 000000000000..5783709062ca --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01600.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tpackage test\n\n\t\timport data.ex\n\n\t\tfoo(x) = y {\n\t\t\ttrim(x, \"h o\", y)\n\t\t}\n\n\t\tcross = y {\n\t\t\tex.cross(\"hi, my name is foo\", y)\n\t\t}\n\n\t\tmulti(\"foo\", x) = y {\n\t\t\ty = x\n\t\t}\n\n\t\tmulti(\"bar\", x) = y {\n\t\t\ty = \"baz\"\n\t\t}\n\n\t\tmulti_cross_pkg = [y, z] {\n\t\t\tmulti(\"foo\", \"bar\", y)\n\t\t\tex.multi(2, 1, z)\n\t\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01601.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01601.stmt new file mode 100644 index 000000000000..2f69acd1d4a9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01601.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tpackage test\n\n\t\tsamepkg = y {\n\t\t\tfoo(\"how do you do?\", y)\n\t\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01602.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01602.stmt new file mode 100644 index 000000000000..edf84c3f62a2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01602.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tpackage test.l1.l3\n\n\t\tg(x) = x") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01603.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01603.stmt new file mode 100644 index 000000000000..8c1389bde7fa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01603.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tpackage test.l1.l2\n\n\t\tp = true\n\t\tf(x) = x") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01604.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01604.stmt new file mode 100644 index 000000000000..51239f5425b8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01604.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tpackage test.omit_result\n\n\t\tf(x) = x\n\n\t\tp { f(1) }\n\t\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01605.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01605.stmt new file mode 100644 index 000000000000..431f892f2736 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01605.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("basic call") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01606.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01606.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01606.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01607.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01607.stmt new file mode 100644 index 000000000000..46d20e07339e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01607.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("bar") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01608.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01608.stmt new file mode 100644 index 000000000000..748b52c3d828 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01608.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("alice") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01609.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01609.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01609.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01610.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01610.stmt new file mode 100644 index 000000000000..c2983d208f40 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01610.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"al\", \"ce\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01611.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01611.stmt new file mode 100644 index 000000000000..c9610fb1a9a1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01611.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("false result") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01612.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01612.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01612.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01613.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01613.stmt new file mode 100644 index 000000000000..43f68d1b89f1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01613.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("falsy_undefined") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01614.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01614.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01614.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01615.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01615.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01615.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01616.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01616.stmt new file mode 100644 index 000000000000..5bf8e33f60bb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01616.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("false result negation") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01617.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01617.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01617.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01618.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01618.stmt new file mode 100644 index 000000000000..2bd39178fbfd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01618.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("falsy_negation") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01619.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01619.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01619.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01620.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01620.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01620.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01621.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01621.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01621.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01622.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01622.stmt new file mode 100644 index 000000000000..6eebdc303fea --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01622.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("falsy_else_value") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01623.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01623.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01623.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01624.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01624.stmt new file mode 100644 index 000000000000..a82f0e2e52ce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01624.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("false") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01625.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01625.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01625.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01626.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01626.stmt new file mode 100644 index 000000000000..d8f4e180b9c2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01626.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("falsy_else_undefined") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01627.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01627.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01627.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01628.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01628.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01628.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01629.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01629.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01629.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01630.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01630.stmt new file mode 100644 index 000000000000..ddf56916b3b0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01630.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("falsy_else_negation") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01631.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01631.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01631.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01632.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01632.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01632.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01633.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01633.stmt new file mode 100644 index 000000000000..7b8df80d8718 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01633.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("chained") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01634.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01634.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01634.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01635.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01635.stmt new file mode 100644 index 000000000000..1e87c5173719 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01635.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("chain2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01636.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01636.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01636.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01637.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01637.stmt new file mode 100644 index 000000000000..f424562c91e6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01637.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"foo\", \"bar\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01638.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01638.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01638.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01639.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01639.stmt new file mode 100644 index 000000000000..2f908580e2cd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01639.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("cross") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01640.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01640.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01640.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01641.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01641.stmt new file mode 100644 index 000000000000..d3ea69a9f786 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01641.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"s f\", [\", my name \"]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01642.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01642.stmt new file mode 100644 index 000000000000..4a9273df2c52 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01642.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("array params") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01643.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01643.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01643.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01644.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01644.stmt new file mode 100644 index 000000000000..fdf4f950f3e1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01644.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("arraysrule") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01645.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01645.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01645.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01646.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01646.stmt new file mode 100644 index 000000000000..01ec60497256 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01646.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[\"h\", \"h\"], [\"foo\"]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01647.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01647.stmt new file mode 100644 index 000000000000..c18fb65770f7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01647.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("object params") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01648.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01648.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01648.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01649.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01649.stmt new file mode 100644 index 000000000000..70f2acf03a2f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01649.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("objectsrule") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01650.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01650.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01650.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01651.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01651.stmt new file mode 100644 index 000000000000..e85beaa7ada8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01651.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[\"h\", \"h\"], \"i\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01652.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01652.stmt new file mode 100644 index 000000000000..aed508f23215 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01652.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ref func output") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01653.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01653.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01653.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01654.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01654.stmt new file mode 100644 index 000000000000..f3f2efcabe57 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01654.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("refoutput") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01655.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01655.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01655.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01656.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01656.stmt new file mode 100644 index 000000000000..0f8ef729b7a6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01656.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"h\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01657.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01657.stmt new file mode 100644 index 000000000000..038b4a2a0fb4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01657.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("always_true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01658.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01658.stmt new file mode 100644 index 000000000000..8f65a47ffdd7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01658.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.always_true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01659.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01659.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01659.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01660.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01660.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01660.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01661.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01661.stmt new file mode 100644 index 000000000000..64ffbccdfa27 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01661.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("same package call") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01662.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01662.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01662.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01663.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01663.stmt new file mode 100644 index 000000000000..71087f811b65 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01663.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("samepkg") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01664.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01664.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01664.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01665.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01665.stmt new file mode 100644 index 000000000000..43a38af906de --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01665.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"w do you do?\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01666.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01666.stmt new file mode 100644 index 000000000000..d8542e40d8b1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01666.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("void good") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01667.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01667.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01667.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01668.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01668.stmt new file mode 100644 index 000000000000..a5f6a4a38ebb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01668.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("voidGood") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01669.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01669.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01669.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01670.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01670.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01670.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01671.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01671.stmt new file mode 100644 index 000000000000..d4d29c2928a0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01671.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("void bad") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01672.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01672.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01672.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01673.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01673.stmt new file mode 100644 index 000000000000..26c5d0f6116f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01673.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("voidBad") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01674.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01674.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01674.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01675.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01675.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01675.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01676.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01676.stmt new file mode 100644 index 000000000000..0054d4a69f21 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01676.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("multi1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01677.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01677.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01677.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01678.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01678.stmt new file mode 100644 index 000000000000..0054d4a69f21 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01678.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("multi1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01679.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01679.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01679.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01680.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01680.stmt new file mode 100644 index 000000000000..3288bbf6caf2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01680.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01681.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01681.stmt new file mode 100644 index 000000000000..af56c7db7606 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01681.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("multi2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01682.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01682.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01682.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01683.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01683.stmt new file mode 100644 index 000000000000..af56c7db7606 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01683.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("multi2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01684.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01684.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01684.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01685.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01685.stmt new file mode 100644 index 000000000000..89d7c899b54f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01685.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("5") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01686.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01686.stmt new file mode 100644 index 000000000000..52d458d08e72 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01686.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("multi3") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01687.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01687.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01687.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01688.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01688.stmt new file mode 100644 index 000000000000..52d458d08e72 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01688.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("multi3") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01689.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01689.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01689.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01690.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01690.stmt new file mode 100644 index 000000000000..55c6c8cd02b9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01690.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("20") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01691.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01691.stmt new file mode 100644 index 000000000000..9503ddf631d8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01691.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("multi4") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01692.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01692.stmt new file mode 100644 index 000000000000..377bc45fbdce --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01692.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01693.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01693.stmt new file mode 100644 index 000000000000..9503ddf631d8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01693.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("multi4") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01694.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01694.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01694.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01695.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01695.stmt new file mode 100644 index 000000000000..02cde1182eaa --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01695.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"bar\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01696.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01696.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01696.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01697.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01697.stmt new file mode 100644 index 000000000000..a0eeb7219aa9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01697.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("multi_cross_pkg") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01698.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01698.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01698.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01699.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01699.stmt new file mode 100644 index 000000000000..231313955839 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01699.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"bar\", 3]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01700.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01700.stmt new file mode 100644 index 000000000000..a0cbf394a34b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01700.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("skip-functions") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01701.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01701.stmt new file mode 100644 index 000000000000..755e266ab029 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01701.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test.l1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01702.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01702.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01702.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01703.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01703.stmt new file mode 100644 index 000000000000..a95e632c2de2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01703.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"l2\": {\"p\": true}, \"l3\": {}}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01704.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01704.stmt new file mode 100644 index 000000000000..5b7265dd4930 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01704.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("omit result") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01705.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01705.stmt new file mode 100644 index 000000000000..4ca3137e751b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01705.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test.omit_result.p") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01706.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01706.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01706.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01707.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01707.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01707.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01708.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01708.stmt new file mode 100644 index 000000000000..259d8201f75b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01708.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tpackage test1\n\n\t\tp(x) = y {\n\t\t\ty = x[_]\n\t\t}\n\n\t\tr = y {\n\t\t\tp([1, 2, 3], y)\n\t\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01709.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01709.stmt new file mode 100644 index 000000000000..e2cb4cb8c730 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01709.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tpackage test2\n\n\t\tp(1, x) = y {\n\t\t\ty = x\n\t\t}\n\n\t\tp(2, x) = y {\n\t\t\ty = x+1\n\t\t}\n\n\t\tr = y {\n\t\t\tp(3, 0, y)\n\t\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01710.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01710.stmt new file mode 100644 index 000000000000..6c233c0e6700 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01710.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tpackage test3\n\n\t\tp(1, x) = y {\n\t\t\ty = x\n\t\t}\n\n\t\tp(2, x) = y {\n\t\t\ty = x+1\n\t\t}\n\n\t\tp(x, y) = z {\n\t\t\tz = x\n\t\t}\n\n\t\tr = y {\n\t\t\tp(1, 0, y)\n\t\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01711.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01711.stmt new file mode 100644 index 000000000000..bfd86c0699f2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01711.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("function output conflict single") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01712.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01712.stmt new file mode 100644 index 000000000000..091c6053a605 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01712.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01713.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01713.stmt new file mode 100644 index 000000000000..99193dedd483 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01713.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("r") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01714.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01714.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01714.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01715.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01715.stmt new file mode 100644 index 000000000000..d3c4b006b795 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01715.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("function input no match") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01716.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01716.stmt new file mode 100644 index 000000000000..b4d241fc4a0e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01716.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01717.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01717.stmt new file mode 100644 index 000000000000..99193dedd483 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01717.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("r") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01718.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01718.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01718.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01719.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01719.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01719.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01720.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01720.stmt new file mode 100644 index 000000000000..be46dd2bf700 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01720.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("function output conflict multiple") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01721.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01721.stmt new file mode 100644 index 000000000000..f0ceb9c0f0e8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01721.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test3") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01722.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01722.stmt new file mode 100644 index 000000000000..99193dedd483 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01722.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("r") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01723.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01723.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01723.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01724.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01724.stmt new file mode 100644 index 000000000000..af28cc241cc5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01724.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package ex\n\nloopback = input { true }\ncomposite[x] { input.foo[_] = x; x > 2 }\nvars = {\"foo\": input.foo, \"bar\": input.bar} { true }\ninput_eq { input.x = 1 }\nallow_basic = true {data.a = \"testdata\"}\nallow_merge_1 = true {data.b = {\"v1\": \"hello\", \"v2\": \"world\"}}\nallow_merge_2 = true {data.b = {\"v1\": \"hello\", \"v2\": \"world\", \"v3\": \"again\"}}\nvirtual[x] { data.a.b[x] = 1 }\nmock_var = {\"a\": 0, \"b\": 0}\nmock_rule = false {1 = 2}\n\nallow1 {\n\tdata.label.b.c = [1,2,3]\n}\n\nallow2 {\n\tdata.label.b.c[x] = 2\n}\n\nallow3 {\n\tdata.label.b[x] = 1\n}\n\nallow4 {\n\tdata.label.b.c.d[x] = 1\n}\n\nallow {\n\tallow1\n\tallow2\n\tnot allow3\n\tnot allow4\n}\n") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01725.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01725.stmt new file mode 100644 index 000000000000..675f9de8741f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01725.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package test\n\nimport data.ex\n\nbasic = true { ex.loopback = true with input as true; ex.loopback = false with input as false }\nnegation = true { not ex.loopback with input as false; ex.loopback with input as true }\ncomposite[x] { ex.composite[x] with input.foo as [1, 2, 3, 4] }\nvars = x { foo = \"hello\"; bar = \"world\"; x = ex.vars with input.foo as foo with input.bar as bar }\nconflict = true { ex.loopback with input.foo as \"x\" with input.foo.bar as \"y\" }\nnegation_invalidate[x] { data.a[_] = x; not data.ex.input_eq with input.x as x }\nbasic_data = true {ex.allow_basic = true with data.a as \"testdata\"}\nmap_data_1 = true {ex.allow_merge_1 = true with data.b.v2 as \"world\"}\nmap_data_2 = true {ex.allow_merge_2 = true with data.b.v2 as \"world\" with data.b.v3 as \"again\"}\ndata_conflict = true {ex.allow_basic = true with data.a.b as 5}\nbase_doc_exact_value[x] { data.a.b[x] = 1 with data.a.b as {\"c\": 1, \"d\": 2, \"e\": 1} }\nbase_doc_any_index[x] { data.a.b[x] with data.a.b as {\"c\": 1, \"d\": 2, \"e\": 1} }\nundefined_1 { data.a.b.c with data.a.b as 1 }\nundefined_2 { data.l.a with data.l as 1 }\nvirtual_doc_exact_value[x] { ex.virtual = x with data.a.b as {\"c\": 1, \"d\": 2, \"e\": 1} }\nvirtual_doc_any_index[x] { ex.virtual[x] with data.a.b as {\"c\": 1, \"d\": 2, \"e\": 1} }\nvirtual_doc_specific_index = y { y = ex.virtual[\"c\"] with data.a.b as {\"c\": 1, \"d\": 2, \"e\": 1} }\nvirtual_doc_not_specific_index = true { not ex.virtual[\"d\"] with data.a.b as {\"c\": 1, \"d\": 2, \"e\": 1} }\ntest_mock_var = y {y = ex.mock_var with ex.mock_var as {\"c\": 1, \"d\": 2}}\ntest_mock_rule {ex.mock_rule with ex.mock_rule as true}\ntest_rule_chain {ex.allow with data.label.b.c as [1,2,3]}\n") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01726.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01726.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01726.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01727.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01727.stmt new file mode 100644 index 000000000000..5ab7c5c3df99 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01727.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("basic") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01728.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01728.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01728.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01729.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01729.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01729.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01730.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01730.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01730.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01731.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01731.stmt new file mode 100644 index 000000000000..38fcb17dbeef --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01731.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("negation") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01732.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01732.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01732.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01733.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01733.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01733.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01734.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01734.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01734.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01735.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01735.stmt new file mode 100644 index 000000000000..94c258043777 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01735.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("composite") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01736.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01736.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01736.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01737.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01737.stmt new file mode 100644 index 000000000000..b553655062b3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01737.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01738.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01738.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01738.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01739.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01739.stmt new file mode 100644 index 000000000000..2537d69fc5ca --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01739.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("vars") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01740.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01740.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01740.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01741.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01741.stmt new file mode 100644 index 000000000000..00b26a57fc3b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01741.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"foo\": \"hello\", \"bar\": \"world\"}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01742.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01742.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01742.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01743.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01743.stmt new file mode 100644 index 000000000000..f68084844b59 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01743.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("conflict") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01744.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01744.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01744.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01745.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01745.stmt new file mode 100644 index 000000000000..3d8041383849 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01745.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("conflicting input documents") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01746.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01746.stmt new file mode 100644 index 000000000000..e0a343c30208 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01746.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("With invalidate") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01747.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01747.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01747.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01748.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01748.stmt new file mode 100644 index 000000000000..33d6567c2c76 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01748.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("negation_invalidate") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01749.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01749.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01749.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01750.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01750.stmt new file mode 100644 index 000000000000..2742b118226a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01750.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[2,3,4]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01751.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01751.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01751.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01752.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01752.stmt new file mode 100644 index 000000000000..afa2ef820009 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01752.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("basic_data") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01753.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01753.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01753.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01754.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01754.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01754.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01755.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01755.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01755.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01756.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01756.stmt new file mode 100644 index 000000000000..52566cb472ad --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01756.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("map_data_1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01757.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01757.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01757.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01758.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01758.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01758.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01759.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01759.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01759.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01760.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01760.stmt new file mode 100644 index 000000000000..6679759fafb8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01760.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("map_data_2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01761.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01761.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01761.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01762.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01762.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01762.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01763.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01763.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01763.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01764.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01764.stmt new file mode 100644 index 000000000000..408a56b9dd8d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01764.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("data_conflict") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01765.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01765.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01765.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01766.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01766.stmt new file mode 100644 index 000000000000..040a6d21fbe8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01766.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("eval_with_merge_error") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01767.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01767.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01767.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01768.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01768.stmt new file mode 100644 index 000000000000..ae90aeb6f5b4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01768.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("base_doc_exact_value") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01769.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01769.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01769.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01770.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01770.stmt new file mode 100644 index 000000000000..848ca6a3c84b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01770.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"c\", \"e\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01771.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01771.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01771.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01772.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01772.stmt new file mode 100644 index 000000000000..39016d394cdf --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01772.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("base_doc_any_index") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01773.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01773.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01773.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01774.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01774.stmt new file mode 100644 index 000000000000..2a1911681657 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01774.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"c\", \"d\", \"e\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01775.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01775.stmt new file mode 100644 index 000000000000..3abc73acc9e5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01775.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("undefined_1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01776.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01776.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01776.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01777.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01777.stmt new file mode 100644 index 000000000000..3abc73acc9e5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01777.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("undefined_1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01778.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01778.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01778.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01779.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01779.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01779.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01780.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01780.stmt new file mode 100644 index 000000000000..c0c2f7192986 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01780.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("undefined_2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01781.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01781.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01781.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01782.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01782.stmt new file mode 100644 index 000000000000..c0c2f7192986 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01782.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("undefined_2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01783.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01783.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01783.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01784.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01784.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01784.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01785.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01785.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01785.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01786.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01786.stmt new file mode 100644 index 000000000000..8a26739fb872 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01786.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("virtual_doc_exact_value") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01787.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01787.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01787.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01788.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01788.stmt new file mode 100644 index 000000000000..9d0749e1ffc3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01788.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[[\"c\", \"e\"]]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01789.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01789.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01789.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01790.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01790.stmt new file mode 100644 index 000000000000..9db140b9f53d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01790.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("virtual_doc_any_index") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01791.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01791.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01791.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01792.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01792.stmt new file mode 100644 index 000000000000..848ca6a3c84b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01792.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"c\", \"e\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01793.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01793.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01793.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01794.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01794.stmt new file mode 100644 index 000000000000..88a6a28b8e50 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01794.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("virtual_doc_specific_index") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01795.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01795.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01795.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01796.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01796.stmt new file mode 100644 index 000000000000..9863c4756b83 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01796.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"c\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01797.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01797.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01797.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01798.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01798.stmt new file mode 100644 index 000000000000..372acd077eec --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01798.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("virtual_doc_not_specific_index") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01799.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01799.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01799.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01800.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01800.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01800.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01801.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01801.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01801.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01802.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01802.stmt new file mode 100644 index 000000000000..7673b7be39cb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01802.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test_mock_var") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01803.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01803.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01803.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01804.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01804.stmt new file mode 100644 index 000000000000..586b9e6f7686 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01804.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\"c\": 1, \"d\": 2}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01805.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01805.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01805.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01806.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01806.stmt new file mode 100644 index 000000000000..25de10c81451 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01806.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test_mock_rule") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01807.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01807.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01807.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01808.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01808.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01808.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01809.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01809.stmt new file mode 100644 index 000000000000..5d1c5e2ae17c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01809.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01810.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01810.stmt new file mode 100644 index 000000000000..850b2ef439f8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01810.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test_rule_chain") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01811.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01811.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01811.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01812.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01812.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01812.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01813.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01813.stmt new file mode 100644 index 000000000000..44fc1b6a83b1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01813.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("no-op") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01814.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01814.stmt new file mode 100644 index 000000000000..2a933b1dc04c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01814.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.no_op") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01815.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01815.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01815.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01816.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01816.stmt new file mode 100644 index 000000000000..995108147cf6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01816.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("trivial") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01817.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01817.stmt new file mode 100644 index 000000000000..8cad14d99273 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01817.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.bool") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01818.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01818.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01818.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01819.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01819.stmt new file mode 100644 index 000000000000..f78de8ed192e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01819.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("trivial-non-bool") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01820.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01820.stmt new file mode 100644 index 000000000000..d11545e7ba21 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01820.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.non_bool") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01821.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01821.stmt new file mode 100644 index 000000000000..59f0c27d20b9 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01821.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[100]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01822.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01822.stmt new file mode 100644 index 000000000000..727809e08792 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01822.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("trivial-3") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01823.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01823.stmt new file mode 100644 index 000000000000..3c4578985917 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01823.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.triple") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01824.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01824.stmt new file mode 100644 index 000000000000..7b2cf0a34aeb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01824.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\"hello\"") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01825.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01825.stmt new file mode 100644 index 000000000000..788e66de5242 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01825.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("var-head") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01826.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01826.stmt new file mode 100644 index 000000000000..f61af505d9ac --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01826.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.vars") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01827.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01827.stmt new file mode 100644 index 000000000000..1afa4722f084 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01827.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"hello\", \"goodbye\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01828.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01828.stmt new file mode 100644 index 000000000000..97c9501c8709 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01828.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ref-head") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01829.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01829.stmt new file mode 100644 index 000000000000..0099db559f6d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01829.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.refs") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01830.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01830.stmt new file mode 100644 index 000000000000..1afa4722f084 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01830.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"hello\", \"goodbye\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01831.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01831.stmt new file mode 100644 index 000000000000..1ff9e70e8276 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01831.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("first-match") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01832.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01832.stmt new file mode 100644 index 000000000000..7a0a6e2e4d62 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01832.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.multiple_defined") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01833.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01833.stmt new file mode 100644 index 000000000000..3f8a647c50a4 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01833.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("true") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01834.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01834.stmt new file mode 100644 index 000000000000..4b835a255800 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01834.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.default_1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01835.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01835.stmt new file mode 100644 index 000000000000..3288bbf6caf2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01835.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01836.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01836.stmt new file mode 100644 index 000000000000..3639da68d167 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01836.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.default_2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01837.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01837.stmt new file mode 100644 index 000000000000..3288bbf6caf2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01837.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01838.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01838.stmt new file mode 100644 index 000000000000..c7b16d3b3ace --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01838.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("multiple-roots") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01839.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01839.stmt new file mode 100644 index 000000000000..ba997166d80d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01839.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.multiple_roots") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01840.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01840.stmt new file mode 100644 index 000000000000..3288bbf6caf2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01840.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01841.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01841.stmt new file mode 100644 index 000000000000..482958a8a0f5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01841.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("indexed") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01842.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01842.stmt new file mode 100644 index 000000000000..68ce4b2f8a64 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01842.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.indexed") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01843.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01843.stmt new file mode 100644 index 000000000000..3288bbf6caf2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01843.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01844.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01844.stmt new file mode 100644 index 000000000000..c6ad5aeebb96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01844.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("conflict-1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01845.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01845.stmt new file mode 100644 index 000000000000..44f15684a55b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01845.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.conflict_1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01846.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01846.stmt new file mode 100644 index 000000000000..3ffe697884f6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01846.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("conflict-2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01847.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01847.stmt new file mode 100644 index 000000000000..d92a738031cc --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01847.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.conflict_2") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01848.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01848.stmt new file mode 100644 index 000000000000..cadfb5ce0158 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01848.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("functions") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01849.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01849.stmt new file mode 100644 index 000000000000..0c0756a0eef7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01849.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("ex.fn_result") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01850.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01850.stmt new file mode 100644 index 000000000000..c65c9a6248b6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01850.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("[\"large\", \"small\", \"medium\"]") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01851.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01851.stmt new file mode 100644 index 000000000000..04abd36c0c7e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01851.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package ex\n\n\t\t\tno_op { true } else = false { true }\n\t\t\tbool { false } else { true }\n\t\t\tnon_bool = null { false } else = [100] { true }\n\t\t\ttriple { false } else { false } else = \"hello\" { true }\n\t\t\tvars { false } else = [\"hello\", x] { data.b.v2 = x }\n\t\t\trefs { false } else = [\"hello\", data.b.v2] { true }\n\t\t\tmultiple_defined = false { false } else = true { true } else = false { true }\n\n\t\t\tdefault default_1 = 1\n\t\t\tdefault_1 { false } default_1 = 2 { true }\n\n\t\t\tdefault default_2 = 2\n\t\t\tdefault_2 { false } default_2 = 1 { false }\n\n\t\t\tmultiple_roots {\n\t\t\t\tfalse\n\t\t\t} else = 1 {\n\t\t\t\tfalse\n\t\t\t} else = 2 {\n\t\t\t\ttrue\n\t\t\t} else = 3 {\n\t\t\t\ttrue\n\t\t\t}\n\n\t\t\tmultiple_roots = 2\n\n\t\t\tmultiple_roots = 3 {\n\t\t\t\tfalse\n\t\t\t} else = 2 {\n\t\t\t\ttrue\n\t\t\t}\n\n\t\t\tindexed {\n\t\t\t\tdata.a[0] = 0\n\t\t\t} else = 2 {\n\t\t\t\tdata.a[0] = 1\n\t\t\t} else = 3 {\n\t\t\t\tdata.a[0] = 1\n\t\t\t}\n\n\t\t\tindexed {\n\t\t\t\tdata.a[0] = 1\n\t\t\t\tdata.a[2] = 2\n\t\t\t} else {\n\t\t\t\tfalse\n\t\t\t} else = 2 {\n\t\t\t\tdata.a[0] = x\n\t\t\t\tx = 1\n\t\t\t\tdata.a[2] = 3\n\t\t\t}\n\n\t\t\tconflict_1 { false } else { true }\n\t\t\tconflict_1 = false { true }\n\n\t\t\tconflict_2 { false } else = false { true }\n\t\t\tconflict_2 { false } else = true { true }\n\n\t\t\tfn_result = [x,y,z] { fn(101, true, x); fn(100, true, y); fn(100, false, z) }\n\n\t\t\tfn(x, y) = \"large\" {\n\t\t\t\tx > 100\n\t\t\t} else = \"small\" {\n\t\t\t\ty = true\n\t\t\t} else = \"medium\" {\n\t\t\t\ttrue\n\t\t\t}\n\t\t\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01852.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01852.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01852.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01853.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01853.stmt new file mode 100644 index 000000000000..f7af96c3b429 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01853.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tpackage system.somepolicy\n\n\t\tfoo = \"hello\"\n\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01854.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01854.stmt new file mode 100644 index 000000000000..0af6ab652e3d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01854.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tpackage topdown.system\n\n\t\tbar = \"goodbye\"\n\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01855.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01855.stmt new file mode 100644 index 000000000000..a6995367a6cb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01855.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("system") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01856.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01856.stmt new file mode 100644 index 000000000000..17ca57885ba3 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01856.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("somedata") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01857.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01857.stmt new file mode 100644 index 000000000000..5a7e76e5cb4b --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01857.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("a") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01858.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01858.stmt new file mode 100644 index 000000000000..92983034c8eb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01858.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("b") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01859.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01859.stmt new file mode 100644 index 000000000000..049d49a4233a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01859.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("c") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01860.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01860.stmt new file mode 100644 index 000000000000..0c5cc1eaedb5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01860.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("com") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01861.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01861.stmt new file mode 100644 index 000000000000..a6995367a6cb --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01861.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("system") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01862.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01862.stmt new file mode 100644 index 000000000000..39671115e5a8 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01862.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("deadbeef") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01863.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01863.stmt new file mode 100644 index 000000000000..9cab4da8c221 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01863.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("root query") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01864.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01864.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01864.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01865.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01865.stmt new file mode 100644 index 000000000000..cf8fff92abc1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01865.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\n\t\t\"topdown\": {\n\t\t\t\"system\": {\n\t\t\t\t\"bar\": \"goodbye\"\n\t\t\t}\n\t\t},\n\t\t\"com\": {\n\t\t\t\"system\": \"deadbeef\"\n\t\t}\n\t}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01866.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01866.stmt new file mode 100644 index 000000000000..3fb85cebc6d7 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01866.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n {\n \"servers\": [\n {\"id\": \"s1\", \"name\": \"app\", \"protocols\": [\"https\", \"ssh\"], \"ports\": [\"p1\", \"p2\", \"p3\"]},\n {\"id\": \"s2\", \"name\": \"db\", \"protocols\": [\"mysql\"], \"ports\": [\"p3\"]},\n {\"id\": \"s3\", \"name\": \"cache\", \"protocols\": [\"memcache\", \"http\"], \"ports\": [\"p3\"]},\n {\"id\": \"s4\", \"name\": \"dev\", \"protocols\": [\"http\"], \"ports\": [\"p1\", \"p2\"]}\n ],\n \"networks\": [\n {\"id\": \"n1\", \"public\": false},\n {\"id\": \"n2\", \"public\": false},\n {\"id\": \"n3\", \"public\": true}\n ],\n \"ports\": [\n {\"id\": \"p1\", \"networks\": [\"n1\"]},\n {\"id\": \"p2\", \"networks\": [\"n3\"]},\n {\"id\": \"p3\", \"networks\": [\"n2\"]}\n ]\n }\n ") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01867.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01867.stmt new file mode 100644 index 000000000000..509dd5ceb981 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01867.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package opa.example\n\nimport data.servers\nimport data.networks\nimport data.ports\n\npublic_servers[server] { server = servers[_]; server.ports[_] = ports[i].id; ports[i].networks[_] = networks[j].id; networks[j].public = true }\nviolations[server] { server = servers[_]; server.protocols[_] = \"http\"; public_servers[server] }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01868.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01868.stmt new file mode 100644 index 000000000000..ff9a425a5dca --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01868.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("public servers") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01869.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01869.stmt new file mode 100644 index 000000000000..f95d82a87221 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01869.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("opa") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01870.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01870.stmt new file mode 100644 index 000000000000..f3566e71ce5d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01870.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("example") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01871.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01871.stmt new file mode 100644 index 000000000000..f4a34b6e71d1 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01871.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("public_servers") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01872.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01872.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01872.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01873.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01873.stmt new file mode 100644 index 000000000000..c91741b5848e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01873.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n [\n {\"id\": \"s1\", \"name\": \"app\", \"protocols\": [\"https\", \"ssh\"], \"ports\": [\"p1\", \"p2\", \"p3\"]},\n {\"id\": \"s4\", \"name\": \"dev\", \"protocols\": [\"http\"], \"ports\": [\"p1\", \"p2\"]}\n ]\n ") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01874.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01874.stmt new file mode 100644 index 000000000000..1539b521a101 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01874.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("violations") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01875.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01875.stmt new file mode 100644 index 000000000000..f95d82a87221 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01875.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("opa") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01876.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01876.stmt new file mode 100644 index 000000000000..f3566e71ce5d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01876.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("example") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01877.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01877.stmt new file mode 100644 index 000000000000..1539b521a101 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01877.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("violations") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01878.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01878.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01878.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01879.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01879.stmt new file mode 100644 index 000000000000..3598e837bb89 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01879.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t [\n\t {\"id\": \"s4\", \"name\": \"dev\", \"protocols\": [\"http\"], \"ports\": [\"p1\", \"p2\"]}\n\t ]\n\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01880.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01880.stmt new file mode 100644 index 000000000000..1eb4ec4ee06a --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01880.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("both") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01881.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01881.stmt new file mode 100644 index 000000000000..f95d82a87221 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01881.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("opa") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01882.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01882.stmt new file mode 100644 index 000000000000..f3566e71ce5d --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01882.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("example") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01883.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01883.stmt new file mode 100644 index 000000000000..03e0101d5d96 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01883.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{}") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01884.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01884.stmt new file mode 100644 index 000000000000..3510da852724 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01884.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\t{\n\t\t\t\"public_servers\": [\n\t\t\t\t{\"id\": \"s1\", \"name\": \"app\", \"protocols\": [\"https\", \"ssh\"], \"ports\": [\"p1\", \"p2\", \"p3\"]},\n\t\t\t\t{\"id\": \"s4\", \"name\": \"dev\", \"protocols\": [\"http\"], \"ports\": [\"p1\", \"p2\"]}\n\t\t\t],\n\t\t\t\"violations\": [\n\t\t\t\t{\"id\": \"s4\", \"name\": \"dev\", \"protocols\": [\"http\"], \"ports\": [\"p1\", \"p2\"]}\n\t\t\t]\n\t\t}\n\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01885.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01885.stmt new file mode 100644 index 000000000000..ba8e06f674ac --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01885.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("unsupported_builtin") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01886.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01886.stmt new file mode 100644 index 000000000000..68e64a356fe2 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01886.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("unsupported_builtin()") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01887.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01887.stmt new file mode 100644 index 000000000000..c74f2a67d301 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01887.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\t\tpackage test\n\n\t\tp { data.arr[_] = _; test.sleep(\"1ms\") }\n\t\t") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01888.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01888.stmt new file mode 100644 index 000000000000..93076cf67bae --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01888.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("arr") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01889.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01889.stmt new file mode 100644 index 000000000000..2b0cfe04137c --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01889.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("data.test.p") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01890.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01890.stmt new file mode 100644 index 000000000000..46d20e07339e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01890.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("bar") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01891.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01891.stmt new file mode 100644 index 000000000000..2157cabc8a85 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01891.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("mod1") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01892.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01892.stmt new file mode 100644 index 000000000000..2a7c5da69bcd --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01892.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package ex\n\np[x] { data.a[i] = x }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01893.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01893.stmt new file mode 100644 index 000000000000..8301f2285905 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01893.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("data.ex.p") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01894.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01894.stmt new file mode 100644 index 000000000000..46d20e07339e --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01894.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("bar") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01895.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01895.stmt new file mode 100644 index 000000000000..83f515796e55 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01895.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("testMod%d") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01896.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01896.stmt new file mode 100644 index 000000000000..4467fcd16565 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01896.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("testMod") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01897.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01897.stmt new file mode 100644 index 000000000000..428c1b2380c0 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01897.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("{\n \"a\": [1,2,3,4],\n \"b\": {\n \"v1\": \"hello\",\n \"v2\": \"goodbye\"\n },\n \"c\": [{\n \"x\": [true, false, \"foo\"],\n \"y\": [null, 3.14159],\n \"z\": {\"p\": true, \"q\": false}\n }],\n \"d\": {\n \"e\": [\"bar\", \"baz\"]\n },\n \"f\": [\n {\"xs\": [1.0], \"ys\": [2.0]},\n {\"xs\": [2.0], \"ys\": [3.0]}\n ],\n \"g\": {\n \"a\": [1, 0, 0, 0],\n \"b\": [0, 2, 0, 0],\n \"c\": [0, 0, 0, 4]\n },\n \"h\": [\n [1,2,3],\n [2,3,4]\n ],\n \"l\": [\n {\n \"a\": \"bob\",\n \"b\": -1,\n \"c\": [1,2,3,4]\n },\n {\n \"a\": \"alice\",\n \"b\": 1,\n \"c\": [2,3,4,5],\n \"d\": null\n }\n ],\n\t\t\"strings\": {\n\t\t\t\"foo\": 1,\n\t\t\t\"bar\": 2,\n\t\t\t\"baz\": 3\n\t\t},\n\t\t\"three\": 3,\n \"m\": [],\n\t\t\"numbers\": [\n\t\t\t\"1\",\n\t\t\t\"2\",\n\t\t\t\"3\",\n\t\t\t\"4\"\n\t\t]\n }") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01898.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01898.stmt new file mode 100644 index 000000000000..beac7e34c303 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01898.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("p") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01899.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01899.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01899.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01900.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01900.stmt new file mode 100644 index 000000000000..0f5a9e334f10 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01900.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("result") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01901.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01901.stmt new file mode 100644 index 000000000000..601dd1931b59 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01901.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("OPA_TRACE_TEST") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01902.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01902.stmt new file mode 100644 index 000000000000..64c3abaff82f --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01902.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01903.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01903.stmt new file mode 100644 index 000000000000..6c0dd93bd347 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01903.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Expected %v but got undefined") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01904.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01904.stmt new file mode 100644 index 000000000000..f47dd5fd5f59 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01904.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("input") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01905.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01905.stmt new file mode 100644 index 000000000000..202784882fee --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01905.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("package topdown_test_partial") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01906.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01906.stmt new file mode 100644 index 000000000000..e74c3b3b9e44 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01906.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("__result__") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01907.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01907.stmt new file mode 100644 index 000000000000..c220acd67434 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01907.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("topdown_test_partial") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01908.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01908.stmt new file mode 100644 index 000000000000..5f810c8082f5 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01908.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("topdown_test_support_%d") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01909.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01909.stmt new file mode 100644 index 000000000000..7b4d4e62d259 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01909.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("data.topdown_test_partial.__result__ = x") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01910.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01910.stmt new file mode 100644 index 000000000000..2f2952250b54 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01910.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Expected %v but got undefined from query after partial evaluation") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01911.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01911.stmt new file mode 100644 index 000000000000..161bace7ebd6 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01911.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("x") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01912.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01912.stmt new file mode 100644 index 000000000000..01cbb3867001 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01912.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test.sleep") diff --git a/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01913.stmt b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01913.stmt new file mode 100644 index 000000000000..01cbb3867001 --- /dev/null +++ b/third_party/opa/v1/ast/testdata/fuzz/FuzzParseStatementsAndCompileModules/01913.stmt @@ -0,0 +1,2 @@ +go test fuzz v1 +string("test.sleep") diff --git a/third_party/opa/v1/ast/transform.go b/third_party/opa/v1/ast/transform.go new file mode 100644 index 000000000000..197ab6457dc2 --- /dev/null +++ b/third_party/opa/v1/ast/transform.go @@ -0,0 +1,431 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" +) + +// Transformer defines the interface for transforming AST elements. If the +// transformer returns nil and does not indicate an error, the AST element will +// be set to nil and no transformations will be applied to children of the +// element. +type Transformer interface { + Transform(any) (any, error) +} + +// Transform iterates the AST and calls the Transform function on the +// Transformer t for x before recursing. +func Transform(t Transformer, x any) (any, error) { + + if term, ok := x.(*Term); ok { + return Transform(t, term.Value) + } + + y, err := t.Transform(x) + if err != nil { + return x, err + } + + if y == nil { + return nil, nil + } + + var ok bool + switch y := y.(type) { + case *Module: + p, err := Transform(t, y.Package) + if err != nil { + return nil, err + } + if y.Package, ok = p.(*Package); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Package, p) + } + for i := range y.Imports { + imp, err := Transform(t, y.Imports[i]) + if err != nil { + return nil, err + } + if y.Imports[i], ok = imp.(*Import); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Imports[i], imp) + } + } + for i := range y.Rules { + rule, err := Transform(t, y.Rules[i]) + if err != nil { + return nil, err + } + if y.Rules[i], ok = rule.(*Rule); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Rules[i], rule) + } + } + for i := range y.Annotations { + a, err := Transform(t, y.Annotations[i]) + if err != nil { + return nil, err + } + if y.Annotations[i], ok = a.(*Annotations); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Annotations[i], a) + } + } + for i := range y.Comments { + comment, err := Transform(t, y.Comments[i]) + if err != nil { + return nil, err + } + if y.Comments[i], ok = comment.(*Comment); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Comments[i], comment) + } + } + return y, nil + case *Package: + ref, err := Transform(t, y.Path) + if err != nil { + return nil, err + } + if y.Path, ok = ref.(Ref); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Path, ref) + } + return y, nil + case *Import: + y.Path, err = transformTerm(t, y.Path) + if err != nil { + return nil, err + } + if y.Alias, err = transformVar(t, y.Alias); err != nil { + return nil, err + } + return y, nil + case *Rule: + if y.Head, err = transformHead(t, y.Head); err != nil { + return nil, err + } + if y.Body, err = transformBody(t, y.Body); err != nil { + return nil, err + } + if y.Else != nil { + rule, err := Transform(t, y.Else) + if err != nil { + return nil, err + } + if y.Else, ok = rule.(*Rule); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Else, rule) + } + } + return y, nil + case *Head: + if y.Reference, err = transformRef(t, y.Reference); err != nil { + return nil, err + } + if y.Name, err = transformVar(t, y.Name); err != nil { + return nil, err + } + if y.Args, err = transformArgs(t, y.Args); err != nil { + return nil, err + } + if y.Key != nil { + if y.Key, err = transformTerm(t, y.Key); err != nil { + return nil, err + } + } + if y.Value != nil { + if y.Value, err = transformTerm(t, y.Value); err != nil { + return nil, err + } + } + return y, nil + case Args: + for i := range y { + if y[i], err = transformTerm(t, y[i]); err != nil { + return nil, err + } + } + return y, nil + case Body: + for i, e := range y { + e, err := Transform(t, e) + if err != nil { + return nil, err + } + if y[i], ok = e.(*Expr); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y[i], e) + } + } + return y, nil + case *Expr: + switch ts := y.Terms.(type) { + case *SomeDecl: + decl, err := Transform(t, ts) + if err != nil { + return nil, err + } + if y.Terms, ok = decl.(*SomeDecl); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y, decl) + } + return y, nil + case []*Term: + for i := range ts { + if ts[i], err = transformTerm(t, ts[i]); err != nil { + return nil, err + } + } + case *Term: + if y.Terms, err = transformTerm(t, ts); err != nil { + return nil, err + } + case *Every: + if ts.Key != nil { + ts.Key, err = transformTerm(t, ts.Key) + if err != nil { + return nil, err + } + } + ts.Value, err = transformTerm(t, ts.Value) + if err != nil { + return nil, err + } + ts.Domain, err = transformTerm(t, ts.Domain) + if err != nil { + return nil, err + } + ts.Body, err = transformBody(t, ts.Body) + if err != nil { + return nil, err + } + y.Terms = ts + } + for i, w := range y.With { + w, err := Transform(t, w) + if err != nil { + return nil, err + } + if y.With[i], ok = w.(*With); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.With[i], w) + } + } + return y, nil + case *With: + if y.Target, err = transformTerm(t, y.Target); err != nil { + return nil, err + } + if y.Value, err = transformTerm(t, y.Value); err != nil { + return nil, err + } + return y, nil + case Ref: + for i, term := range y { + if y[i], err = transformTerm(t, term); err != nil { + return nil, err + } + } + return y, nil + case *object: + return y.Map(func(k, v *Term) (*Term, *Term, error) { + k, err := transformTerm(t, k) + if err != nil { + return nil, nil, err + } + v, err = transformTerm(t, v) + if err != nil { + return nil, nil, err + } + return k, v, nil + }) + case *Array: + for i := range y.Len() { + v, err := transformTerm(t, y.Elem(i)) + if err != nil { + return nil, err + } + y.set(i, v) + } + return y, nil + case Set: + y, err = y.Map(func(term *Term) (*Term, error) { + return transformTerm(t, term) + }) + if err != nil { + return nil, err + } + return y, nil + case *ArrayComprehension: + if y.Term, err = transformTerm(t, y.Term); err != nil { + return nil, err + } + if y.Body, err = transformBody(t, y.Body); err != nil { + return nil, err + } + return y, nil + case *ObjectComprehension: + if y.Key, err = transformTerm(t, y.Key); err != nil { + return nil, err + } + if y.Value, err = transformTerm(t, y.Value); err != nil { + return nil, err + } + if y.Body, err = transformBody(t, y.Body); err != nil { + return nil, err + } + return y, nil + case *SetComprehension: + if y.Term, err = transformTerm(t, y.Term); err != nil { + return nil, err + } + if y.Body, err = transformBody(t, y.Body); err != nil { + return nil, err + } + return y, nil + case Call: + for i := range y { + if y[i], err = transformTerm(t, y[i]); err != nil { + return nil, err + } + } + return y, nil + default: + return y, nil + } +} + +// TransformRefs calls the function f on all references under x. +func TransformRefs(x any, f func(Ref) (Value, error)) (any, error) { + t := &GenericTransformer{func(x any) (any, error) { + if r, ok := x.(Ref); ok { + return f(r) + } + return x, nil + }} + return Transform(t, x) +} + +// TransformVars calls the function f on all vars under x. +func TransformVars(x any, f func(Var) (Value, error)) (any, error) { + t := &GenericTransformer{func(x any) (any, error) { + if v, ok := x.(Var); ok { + return f(v) + } + return x, nil + }} + return Transform(t, x) +} + +// TransformComprehensions calls the functio nf on all comprehensions under x. +func TransformComprehensions(x any, f func(any) (Value, error)) (any, error) { + t := &GenericTransformer{func(x any) (any, error) { + switch x := x.(type) { + case *ArrayComprehension: + return f(x) + case *SetComprehension: + return f(x) + case *ObjectComprehension: + return f(x) + } + return x, nil + }} + return Transform(t, x) +} + +// GenericTransformer implements the Transformer interface to provide a utility +// to transform AST nodes using a closure. +type GenericTransformer struct { + f func(any) (any, error) +} + +// NewGenericTransformer returns a new GenericTransformer that will transform +// AST nodes using the function f. +func NewGenericTransformer(f func(x any) (any, error)) *GenericTransformer { + return &GenericTransformer{ + f: f, + } +} + +// Transform calls the function f on the GenericTransformer. +func (t *GenericTransformer) Transform(x any) (any, error) { + return t.f(x) +} + +func transformHead(t Transformer, head *Head) (*Head, error) { + y, err := Transform(t, head) + if err != nil { + return nil, err + } + h, ok := y.(*Head) + if !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", head, y) + } + return h, nil +} + +func transformArgs(t Transformer, args Args) (Args, error) { + y, err := Transform(t, args) + if err != nil { + return nil, err + } + a, ok := y.(Args) + if !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", args, y) + } + return a, nil +} + +func transformBody(t Transformer, body Body) (Body, error) { + y, err := Transform(t, body) + if err != nil { + return nil, err + } + r, ok := y.(Body) + if !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", body, y) + } + return r, nil +} + +func transformTerm(t Transformer, term *Term) (*Term, error) { + v, err := transformValue(t, term.Value) + if err != nil { + return nil, err + } + r := &Term{ + Value: v, + Location: term.Location, + } + return r, nil +} + +func transformValue(t Transformer, v Value) (Value, error) { + v1, err := Transform(t, v) + if err != nil { + return nil, err + } + r, ok := v1.(Value) + if !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", v, v1) + } + return r, nil +} + +func transformVar(t Transformer, v Var) (Var, error) { + v1, err := Transform(t, v) + if err != nil { + return "", err + } + r, ok := v1.(Var) + if !ok { + return "", fmt.Errorf("illegal transform: %T != %T", v, v1) + } + return r, nil +} + +func transformRef(t Transformer, r Ref) (Ref, error) { + r1, err := Transform(t, r) + if err != nil { + return nil, err + } + r2, ok := r1.(Ref) + if !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", r, r2) + } + return r2, nil +} diff --git a/third_party/opa/v1/ast/transform_test.go b/third_party/opa/v1/ast/transform_test.go new file mode 100644 index 000000000000..9642b3eefc99 --- /dev/null +++ b/third_party/opa/v1/ast/transform_test.go @@ -0,0 +1,137 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "testing" +) + +func TestTransform(t *testing.T) { + mod := module(`package ex.this + +import input.foo +import data.bar.this as qux +import future.keywords.every + +p = true if { "this" = "that" } +p = "this" if { false } +p contains "this" if { false } +p[y] = {"this": ["this"]} if { false } +p = true if { ["this" | "this"] } +p = n if { count({"this", "that"}, n) with input.foo.this as {"this": true} } +p if { false } else = "this" if { "this" } else = ["this"] if { true } +foo(x) = y if { split(x, "this", y) } +p if { every x in ["this"] { x == "this" } } +a.b.c.this["this"] = d if { d := "this" } +`) + + result, err := Transform(&GenericTransformer{ + func(x any) (any, error) { + if s, ok := x.(String); ok && s == String("this") { + return String("that"), nil + } + return x, nil + }, + }, mod) + + if err != nil { + t.Fatalf("Unexpected error during transform: %v", err) + } + + resultMod, ok := result.(*Module) + if !ok { + t.Fatalf("Expected module from transform but got: %v", result) + } + + expected := module(`package ex.that + +import input.foo +import data.bar.that as qux +import future.keywords.every + +p = true if { "that" = "that" } +p = "that" if { false } +p contains "that" if { false } +p[y] = {"that": ["that"]} if { false } +p = true if { ["that" | "that"] } +p = n if { count({"that"}, n) with input.foo.that as {"that": true} } +p if { false } else = "that" if { "that" } else = ["that"] if { true } +foo(x) = y if { split(x, "that", y) } +p if { every x in ["that"] { x == "that" } } +a.b.c.that["that"] = d if { d := "that" } +`) + + if !expected.Equal(resultMod) { + t.Fatalf("Expected module:\n%v\n\nGot:\n%v\n", expected, resultMod) + } + +} + +func TestTransformAnnotations(t *testing.T) { + + module, err := ParseModuleWithOpts("test.rego", `package test + +# METADATA +# scope: rule +p := 7`, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + + result, err := Transform(&GenericTransformer{ + func(x any) (any, error) { + if s, ok := x.(*Annotations); ok { + cpy := *s + cpy.Scope = "deadbeef" + return &cpy, nil + } + return x, nil + }, + }, module) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + resultMod, ok := result.(*Module) + if !ok { + t.Fatalf("Expected module from transform but got: %v", result) + } + + exp, err := ParseModuleWithOpts("test.rego", `package test + +# METADATA +# scope: rule +p := 7`, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + + exp.Annotations[0].Scope = "deadbeef" + + if resultMod.Compare(exp) != 0 { + t.Fatalf("expected:\n\n%v\n\ngot:\n\n%v", exp, resultMod) + } + +} + +func TestTransformRefsAndRuleHeads(t *testing.T) { + module := module(`package test +p.q.this.fo[x] = y if { x := "x"; y := "y" }`) + + result, err := TransformRefs(module, func(r Ref) (Value, error) { + if r[0].Value.Compare(Var("p")) == 0 { + r[2] = StringTerm("that") + } + return r, nil + }) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + resultMod := result.(*Module) + if exp, act := MustParseRef("p.q.that.fo[x]"), resultMod.Rules[0].Head.Reference; !act.Equal(exp) { + t.Errorf("expected %v, got %v", exp, act) + } +} diff --git a/third_party/opa/v1/ast/unify.go b/third_party/opa/v1/ast/unify.go new file mode 100644 index 000000000000..acbe275c0fcc --- /dev/null +++ b/third_party/opa/v1/ast/unify.go @@ -0,0 +1,240 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +func isRefSafe(ref Ref, safe VarSet) bool { + switch head := ref[0].Value.(type) { + case Var: + return safe.Contains(head) + case Call: + return isCallSafe(head, safe) + default: + for v := range ref[0].Vars() { + if !safe.Contains(v) { + return false + } + } + return true + } +} + +func isCallSafe(call Call, safe VarSet) bool { + vis := varVisitorPool.Get().WithParams(SafetyCheckVisitorParams) + vis.Walk(call) + isSafe := vis.Vars().DiffCount(safe) == 0 + varVisitorPool.Put(vis) + + return isSafe +} + +// Unify returns a set of variables that will be unified when the equality expression defined by +// terms a and b is evaluated. The unifier assumes that variables in the VarSet safe are already +// unified. +func Unify(safe VarSet, a *Term, b *Term) VarSet { + u := &unifier{ + safe: safe, + unified: VarSet{}, + unknown: map[Var]VarSet{}, + } + u.unify(a, b) + return u.unified +} + +type unifier struct { + safe VarSet + unified VarSet + unknown map[Var]VarSet +} + +func (u *unifier) isSafe(x Var) bool { + return u.safe.Contains(x) || u.unified.Contains(x) +} + +func (u *unifier) unify(a *Term, b *Term) { + + switch a := a.Value.(type) { + + case Var: + switch b := b.Value.(type) { + case Var: + if u.isSafe(b) { + u.markSafe(a) + } else if u.isSafe(a) { + u.markSafe(b) + } else { + u.markUnknown(a, b) + u.markUnknown(b, a) + } + case *Array, Object: + u.unifyAll(a, b) + case Ref: + if isRefSafe(b, u.safe) { + u.markSafe(a) + } + case Call: + if isCallSafe(b, u.safe) { + u.markSafe(a) + } + default: + u.markSafe(a) + } + + case Ref: + if isRefSafe(a, u.safe) { + switch b := b.Value.(type) { + case Var: + u.markSafe(b) + case *Array, Object: + u.markAllSafe(b) + } + } + + case Call: + if isCallSafe(a, u.safe) { + switch b := b.Value.(type) { + case Var: + u.markSafe(b) + case *Array, Object: + u.markAllSafe(b) + } + } + + case *ArrayComprehension: + switch b := b.Value.(type) { + case Var: + u.markSafe(b) + case *Array: + u.markAllSafe(b) + } + case *ObjectComprehension: + switch b := b.Value.(type) { + case Var: + u.markSafe(b) + case *object: + u.markAllSafe(b) + } + case *SetComprehension: + switch b := b.Value.(type) { + case Var: + u.markSafe(b) + } + + case *Array: + switch b := b.Value.(type) { + case Var: + u.unifyAll(b, a) + case *ArrayComprehension, *ObjectComprehension, *SetComprehension: + u.markAllSafe(a) + case Ref: + if isRefSafe(b, u.safe) { + u.markAllSafe(a) + } + case Call: + if isCallSafe(b, u.safe) { + u.markAllSafe(a) + } + case *Array: + if a.Len() == b.Len() { + for i := range a.Len() { + u.unify(a.Elem(i), b.Elem(i)) + } + } + } + + case *object: + switch b := b.Value.(type) { + case Var: + u.unifyAll(b, a) + case Ref: + if isRefSafe(b, u.safe) { + u.markAllSafe(a) + } + case Call: + if isCallSafe(b, u.safe) { + u.markAllSafe(a) + } + case *object: + if a.Len() == b.Len() { + _ = a.Iter(func(k, v *Term) error { + if v2 := b.Get(k); v2 != nil { + u.unify(v, v2) + } + return nil + }) // impossible to return error + } + } + + default: + switch b := b.Value.(type) { + case Var: + u.markSafe(b) + } + } +} + +func (u *unifier) markAllSafe(x Value) { + vis := varVisitorPool.Get().WithParams(VarVisitorParams{ + SkipRefHead: true, + SkipObjectKeys: true, + SkipClosures: true, + }) + vis.Walk(x) + for v := range vis.Vars() { + u.markSafe(v) + } + varVisitorPool.Put(vis) +} + +func (u *unifier) markSafe(x Var) { + u.unified.Add(x) + + // Add dependencies of 'x' to safe set + vs := u.unknown[x] + delete(u.unknown, x) + for v := range vs { + u.markSafe(v) + } + + // Add dependants of 'x' to safe set if they have no more + // dependencies. + for v, deps := range u.unknown { + if deps.Contains(x) { + delete(deps, x) + if len(deps) == 0 { + u.markSafe(v) + } + } + } +} + +func (u *unifier) markUnknown(a, b Var) { + if _, ok := u.unknown[a]; !ok { + u.unknown[a] = NewVarSet(b) + } else { + u.unknown[a].Add(b) + } +} + +func (u *unifier) unifyAll(a Var, b Value) { + if u.isSafe(a) { + u.markAllSafe(b) + } else { + vis := varVisitorPool.Get().WithParams(VarVisitorParams{ + SkipRefHead: true, + SkipObjectKeys: true, + SkipClosures: true, + }) + vis.Walk(b) + unsafe := vis.Vars().Diff(u.safe).Diff(u.unified) + if len(unsafe) == 0 { + u.markSafe(a) + } else { + for v := range unsafe { + u.markUnknown(a, v) + } + } + varVisitorPool.Put(vis) + } +} diff --git a/third_party/opa/v1/ast/unify_test.go b/third_party/opa/v1/ast/unify_test.go new file mode 100644 index 000000000000..9ea34eb6d77c --- /dev/null +++ b/third_party/opa/v1/ast/unify_test.go @@ -0,0 +1,124 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "testing" +) + +func TestUnify(t *testing.T) { + + tests := []struct { + note string + expr string + safe string + expected string + }{ + // collection cases + {"array/ref", "[1,2,x] = a[_]", "[a]", "[x]"}, + {"array/ref (reversed)", "a[_] = [1,2,x]", "[a]", "[x]"}, + {"array/var", "[1,2,x] = y", "[x]", "[y]"}, + {"array/var (reversed)", "y = [1,2,x]", "[x]", "[y]"}, + {"array/var-2", "[1,2,x] = y", "[y]", "[x]"}, + {"array/var-2 (reversed)", "y = [1,2,x]", "[y]", "[x]"}, + {"array/uneven", "[1,2,x] = [y,x]", "[]", "[]"}, + {"array/uneven-2", "[1,2,x] = [y,x]", "[x]", "[]"}, + {"object/ref", `{"x": x} = a[_]`, "[a]", "[x]"}, + {"object/ref (reversed)", `a[_] = {"x": x}`, "[a]", "[x]"}, + {"object/var", `{"x": 1, "y": x} = y`, "[x]", "[y]"}, + {"object/var (reversed)", `y = {"x": 1, "y": x}`, "[x]", "[y]"}, + {"object/var-2", `{"x": 1, "y": x} = y`, "[y]", "[x]"}, + {"object/uneven", `{"x": x, "y": 1} = {"x": y}`, "[]", "[]"}, + {"object/uneven", `{"x": x, "y": 1} = {"x": y}`, "[x]", "[]"}, + {"var/call-ref", "x = f(y)[z]", "[y]", "[x]"}, + {"var/call-ref (reversed)", "f(y)[z] = x", "[y]", "[x]"}, + {"var/call", "x = f(z)", "[z]", "[x]"}, + {"var/call (reversed)", "f(z) = x", "[z]", "[x]"}, + {"array/call", "[x, y] = f(z)", "[z]", "[x,y]"}, + {"array/call (reversed)", "f(z) = [x, y]", "[z]", "[x,y]"}, + {"object/call", `{"a": x} = f(z)`, "[z]", "[x]"}, + {"object/call (reversed)", `f(z) = {"a": x}`, "[z]", "[x]"}, + + // transitive cases + {"trans/redundant", "[x, x] = [x, 0]", "[]", "[x]"}, + {"trans/simple", "[x, 1] = [y, y]", "[]", "[y, x]"}, + {"trans/array", "[x, y] = [y, [z, a]]", "[x]", "[a, y, z]"}, + {"trans/object", `[x, y] = [y, {"a":a,"z":z}]`, "[x]", "[a, y, z]"}, + {"trans/ref", "[x, y, [x, y, i]] = [1, a[i], z]", "[a, i]", "[x, y, z]"}, + {"trans/lazy", "[x, z, 2] = [1, [y, x], y]", "[]", "[x, y, z]"}, + {"trans/redundant-nested", "[x, z, z] = [1, [y, x], [2, 1]]", "[]", "[x, y, z]"}, + {"trans/bidirectional", "[x, z, y] = [[z,y], [1,y], 2]", "[]", "[x, y, z]"}, + {"trans/occurs", "[x, z, y] = [[y,z], [y, 1], [2, x]]", "[]", "[]"}, + + // unsafe refs + {note: "array/ref", expr: "[1,2,x] = a[_]"}, + {note: "array/ref (reversed)", expr: "a[_] = [1,2,x]"}, + {note: "object/ref", expr: `{"x": x} = a[_]`}, + {note: "object/ref (reversed)", expr: `a[_] = {"x": x}`}, + {note: "var/call-ref", expr: "x = f(y)[z]"}, + {note: "var/call-ref (reversed)", expr: "f(y)[z] = x"}, + + // unsafe vars + {note: "array/var", expr: "[1,2,x] = y"}, + {note: "array/var (reversed)", expr: "y = [1,2,x]"}, + {note: "object/var", expr: `{"x": 1, "y": x} = y`}, + {note: "object/var (reversed)", expr: `y = {"x": 1, "y": x}`}, + {note: "var/call", expr: "x = f(z)"}, + {note: "var/call (reversed)", expr: "f(z) = x"}, + + // unsafe call args + {note: "var/call-2", expr: "x = f(z)", safe: "[x]"}, + {note: "var/call-2 (reversed)", expr: "f(z) = x", safe: "[x]"}, + {note: "array/call", expr: "[x, y] = f(z)", safe: "[x,y]"}, + {note: "array/call (reversed)", expr: "f(z) = [x, y]", safe: "[x,y]"}, + {note: "object/call", expr: `{"a": x} = f(z)`, safe: "[x]"}, + {note: "object/call (reversed)", expr: `f(z) = {"a": x}`, safe: "[x]"}, + + // partial cases + {note: "trans/ref", expr: "[x, y, [x, y, i]] = [1, a[i], z]", safe: "[a]", expected: "[x, y]"}, + {note: "trans/ref", expr: "[x, y, [x, y, i]] = [1, a[i], z]", expected: "[x]"}, + } + + for _, tc := range tests { + if tc.expected == "" { + tc.expected = "[]" + } + if tc.safe == "" { + tc.safe = "[]" + } + t.Run(fmt.Sprintf("%s/%s/%s", tc.note, tc.safe, tc.expected), func(t *testing.T) { + + expr := MustParseBody(tc.expr)[0] + safe := VarSet{} + MustParseTerm(tc.safe).Value.(*Array).Foreach(func(x *Term) { + safe.Add(x.Value.(Var)) + }) + + terms := expr.Terms.([]*Term) + if !expr.IsEquality() { + panic(expr) + } + + a, b := terms[1], terms[2] + unified := Unify(safe, a, b) + result := VarSet{} + for k := range unified { + result.Add(k) + } + + expected := VarSet{} + MustParseTerm(tc.expected).Value.(*Array).Foreach(func(x *Term) { + expected.Add(x.Value.(Var)) + }) + + missing := expected.Diff(result) + extra := result.Diff(expected) + if len(missing) != 0 || len(extra) != 0 { + t.Fatalf("missing vars: %v, extra vars: %v", missing, extra) + } + }) + } +} diff --git a/third_party/opa/v1/ast/varset.go b/third_party/opa/v1/ast/varset.go new file mode 100644 index 000000000000..e5bd52ae8c86 --- /dev/null +++ b/third_party/opa/v1/ast/varset.go @@ -0,0 +1,121 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "slices" + + "github.com/open-policy-agent/opa/v1/util" +) + +// VarSet represents a set of variables. +type VarSet map[Var]struct{} + +// NewVarSet returns a new VarSet containing the specified variables. +func NewVarSet(vs ...Var) VarSet { + s := make(VarSet, len(vs)) + for _, v := range vs { + s.Add(v) + } + return s +} + +// NewVarSet returns a new VarSet containing the specified variables. +func NewVarSetOfSize(size int) VarSet { + return make(VarSet, size) +} + +// Add updates the set to include the variable "v". +func (s VarSet) Add(v Var) { + s[v] = struct{}{} +} + +// Contains returns true if the set contains the variable "v". +func (s VarSet) Contains(v Var) bool { + _, ok := s[v] + return ok +} + +// Copy returns a shallow copy of the VarSet. +func (s VarSet) Copy() VarSet { + cpy := NewVarSetOfSize(len(s)) + for v := range s { + cpy.Add(v) + } + return cpy +} + +// Diff returns a VarSet containing variables in s that are not in vs. +func (s VarSet) Diff(vs VarSet) VarSet { + r := NewVarSetOfSize(s.DiffCount(vs)) + for v := range s { + if !vs.Contains(v) { + r.Add(v) + } + } + return r +} + +// DiffCount returns the number of variables in s that are not in vs. +func (s VarSet) DiffCount(vs VarSet) (i int) { + for v := range s { + if !vs.Contains(v) { + i++ + } + } + return +} + +// Equal returns true if s contains exactly the same elements as vs. +func (s VarSet) Equal(vs VarSet) bool { + if len(s) != len(vs) { + return false + } + for v := range s { + if !vs.Contains(v) { + return false + } + } + return true +} + +// Intersect returns a VarSet containing variables in s that are in vs. +func (s VarSet) Intersect(vs VarSet) VarSet { + i := 0 + for v := range s { + if vs.Contains(v) { + i++ + } + } + r := NewVarSetOfSize(i) + for v := range s { + if vs.Contains(v) { + r.Add(v) + } + } + return r +} + +// Sorted returns a new sorted slice of vars from s. +func (s VarSet) Sorted() []Var { + sorted := make([]Var, 0, len(s)) + for v := range s { + sorted = append(sorted, v) + } + slices.SortFunc(sorted, VarCompare) + return sorted +} + +// Update merges the other VarSet into this VarSet. +func (s VarSet) Update(vs VarSet) { + for v := range vs { + s.Add(v) + } +} + +func (s VarSet) String() string { + return fmt.Sprintf("%v", util.KeysSorted(s)) +} diff --git a/third_party/opa/v1/ast/version_index.json b/third_party/opa/v1/ast/version_index.json new file mode 100644 index 000000000000..b84f09a29003 --- /dev/null +++ b/third_party/opa/v1/ast/version_index.json @@ -0,0 +1,1471 @@ +{ + "builtins": { + "abs": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "all": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "and": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "any": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "array.concat": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "array.reverse": { + "Major": 0, + "Minor": 36, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "array.slice": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "assign": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "base64.decode": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "base64.encode": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "base64.is_valid": { + "Major": 0, + "Minor": 24, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "base64url.decode": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "base64url.encode": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "base64url.encode_no_pad": { + "Major": 0, + "Minor": 25, + "Patch": 0, + "PreRelease": "rc2", + "Metadata": "" + }, + "bits.and": { + "Major": 0, + "Minor": 18, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "bits.lsh": { + "Major": 0, + "Minor": 18, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "bits.negate": { + "Major": 0, + "Minor": 18, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "bits.or": { + "Major": 0, + "Minor": 18, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "bits.rsh": { + "Major": 0, + "Minor": 18, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "bits.xor": { + "Major": 0, + "Minor": 18, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "cast_array": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "cast_boolean": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "cast_null": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "cast_object": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "cast_set": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "cast_string": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "ceil": { + "Major": 0, + "Minor": 26, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "concat": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "contains": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "count": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.hmac.equal": { + "Major": 0, + "Minor": 52, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.hmac.md5": { + "Major": 0, + "Minor": 36, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.hmac.sha1": { + "Major": 0, + "Minor": 36, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.hmac.sha256": { + "Major": 0, + "Minor": 36, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.hmac.sha512": { + "Major": 0, + "Minor": 36, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.md5": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.parse_private_keys": { + "Major": 0, + "Minor": 55, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.sha1": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.sha256": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.x509.parse_and_verify_certificates": { + "Major": 0, + "Minor": 31, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.x509.parse_and_verify_certificates_with_options": { + "Major": 0, + "Minor": 63, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.x509.parse_certificate_request": { + "Major": 0, + "Minor": 21, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.x509.parse_certificates": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.x509.parse_keypair": { + "Major": 0, + "Minor": 53, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "crypto.x509.parse_rsa_private_key": { + "Major": 0, + "Minor": 33, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "div": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "endswith": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "eq": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "equal": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "floor": { + "Major": 0, + "Minor": 26, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "format_int": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "glob.match": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "glob.quote_meta": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "graph.reachable": { + "Major": 0, + "Minor": 20, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "graph.reachable_paths": { + "Major": 0, + "Minor": 37, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "graphql.is_valid": { + "Major": 0, + "Minor": 41, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "graphql.parse": { + "Major": 0, + "Minor": 41, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "graphql.parse_and_verify": { + "Major": 0, + "Minor": 41, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "graphql.parse_query": { + "Major": 0, + "Minor": 41, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "graphql.parse_schema": { + "Major": 0, + "Minor": 41, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "graphql.schema_is_valid": { + "Major": 0, + "Minor": 46, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "gt": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "gte": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "hex.decode": { + "Major": 0, + "Minor": 25, + "Patch": 0, + "PreRelease": "rc2", + "Metadata": "" + }, + "hex.encode": { + "Major": 0, + "Minor": 25, + "Patch": 0, + "PreRelease": "rc2", + "Metadata": "" + }, + "http.send": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "indexof": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "indexof_n": { + "Major": 0, + "Minor": 37, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "internal.member_2": { + "Major": 0, + "Minor": 34, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "internal.member_3": { + "Major": 0, + "Minor": 34, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "internal.print": { + "Major": 0, + "Minor": 34, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "internal.test_case": { + "Major": 1, + "Minor": 2, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "intersection": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.decode": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.decode_verify": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.encode_sign": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.encode_sign_raw": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.verify_es256": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.verify_es384": { + "Major": 0, + "Minor": 20, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.verify_es512": { + "Major": 0, + "Minor": 20, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.verify_hs256": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.verify_hs384": { + "Major": 0, + "Minor": 20, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.verify_hs512": { + "Major": 0, + "Minor": 20, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.verify_ps256": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.verify_ps384": { + "Major": 0, + "Minor": 20, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.verify_ps512": { + "Major": 0, + "Minor": 20, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.verify_rs256": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.verify_rs384": { + "Major": 0, + "Minor": 20, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "io.jwt.verify_rs512": { + "Major": 0, + "Minor": 20, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "is_array": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "is_boolean": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "is_null": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "is_number": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "is_object": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "is_set": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "is_string": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "json.filter": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "json.is_valid": { + "Major": 0, + "Minor": 25, + "Patch": 0, + "PreRelease": "rc1", + "Metadata": "" + }, + "json.marshal": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "json.marshal_with_options": { + "Major": 0, + "Minor": 64, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "json.match_schema": { + "Major": 0, + "Minor": 50, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "json.patch": { + "Major": 0, + "Minor": 25, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "json.remove": { + "Major": 0, + "Minor": 18, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "json.unmarshal": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "json.verify_schema": { + "Major": 0, + "Minor": 50, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "lower": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "lt": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "lte": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "max": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "min": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "minus": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "mul": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "neq": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "net.cidr_contains": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "net.cidr_contains_matches": { + "Major": 0, + "Minor": 19, + "Patch": 0, + "PreRelease": "rc1", + "Metadata": "" + }, + "net.cidr_expand": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "net.cidr_intersects": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "net.cidr_is_valid": { + "Major": 0, + "Minor": 46, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "net.cidr_merge": { + "Major": 0, + "Minor": 24, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "net.cidr_overlap": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "net.lookup_ip_addr": { + "Major": 0, + "Minor": 35, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "numbers.range": { + "Major": 0, + "Minor": 22, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "numbers.range_step": { + "Major": 0, + "Minor": 56, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "object.filter": { + "Major": 0, + "Minor": 17, + "Patch": 2, + "PreRelease": "", + "Metadata": "" + }, + "object.get": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "object.keys": { + "Major": 0, + "Minor": 47, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "object.remove": { + "Major": 0, + "Minor": 17, + "Patch": 2, + "PreRelease": "", + "Metadata": "" + }, + "object.subset": { + "Major": 0, + "Minor": 42, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "object.union": { + "Major": 0, + "Minor": 17, + "Patch": 2, + "PreRelease": "", + "Metadata": "" + }, + "object.union_n": { + "Major": 0, + "Minor": 37, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "opa.runtime": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "or": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "plus": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "print": { + "Major": 0, + "Minor": 34, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "product": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "providers.aws.sign_req": { + "Major": 0, + "Minor": 47, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "rand.intn": { + "Major": 0, + "Minor": 31, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "re_match": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "regex.find_all_string_submatch_n": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "regex.find_n": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "regex.globs_match": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "regex.is_valid": { + "Major": 0, + "Minor": 23, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "regex.match": { + "Major": 0, + "Minor": 23, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "regex.replace": { + "Major": 0, + "Minor": 45, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "regex.split": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "regex.template_match": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "rego.metadata.chain": { + "Major": 0, + "Minor": 40, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "rego.metadata.rule": { + "Major": 0, + "Minor": 40, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "rego.parse_module": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "rem": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "replace": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "round": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "semver.compare": { + "Major": 0, + "Minor": 22, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "semver.is_valid": { + "Major": 0, + "Minor": 22, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "set_diff": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "sort": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "split": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "sprintf": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "startswith": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "strings.any_prefix_match": { + "Major": 0, + "Minor": 44, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "strings.any_suffix_match": { + "Major": 0, + "Minor": 44, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "strings.count": { + "Major": 0, + "Minor": 67, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "strings.render_template": { + "Major": 0, + "Minor": 59, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "strings.replace_n": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "strings.reverse": { + "Major": 0, + "Minor": 36, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "substring": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "sum": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "time.add_date": { + "Major": 0, + "Minor": 19, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "time.clock": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "time.date": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "time.diff": { + "Major": 0, + "Minor": 28, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "time.format": { + "Major": 0, + "Minor": 48, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "time.now_ns": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "time.parse_duration_ns": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "time.parse_ns": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "time.parse_rfc3339_ns": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "time.weekday": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "to_number": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "trace": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "trim": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "trim_left": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "trim_prefix": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "trim_right": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "trim_space": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "trim_suffix": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "type_name": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "union": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "units.parse": { + "Major": 0, + "Minor": 41, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "units.parse_bytes": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "upper": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "urlquery.decode": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "urlquery.decode_object": { + "Major": 0, + "Minor": 24, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "urlquery.encode": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "urlquery.encode_object": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "uuid.parse": { + "Major": 0, + "Minor": 57, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "uuid.rfc4122": { + "Major": 0, + "Minor": 20, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "walk": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "yaml.is_valid": { + "Major": 0, + "Minor": 25, + "Patch": 0, + "PreRelease": "rc1", + "Metadata": "" + }, + "yaml.marshal": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "yaml.unmarshal": { + "Major": 0, + "Minor": 17, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + } + }, + "features": { + "keywords_in_refs": { + "Major": 1, + "Minor": 6, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "rego_v1": { + "Major": 1, + "Minor": 0, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "rego_v1_import": { + "Major": 0, + "Minor": 59, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "rule_head_ref_string_prefixes": { + "Major": 0, + "Minor": 46, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "rule_head_refs": { + "Major": 0, + "Minor": 59, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + } + }, + "keywords": { + "contains": { + "Major": 0, + "Minor": 42, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "every": { + "Major": 0, + "Minor": 38, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "if": { + "Major": 0, + "Minor": 42, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + }, + "in": { + "Major": 0, + "Minor": 34, + "Patch": 0, + "PreRelease": "", + "Metadata": "" + } + } +} diff --git a/third_party/opa/v1/ast/visit.go b/third_party/opa/v1/ast/visit.go new file mode 100644 index 000000000000..4ae6569ad768 --- /dev/null +++ b/third_party/opa/v1/ast/visit.go @@ -0,0 +1,832 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +// Visitor defines the interface for iterating AST elements. The Visit function +// can return a Visitor w which will be used to visit the children of the AST +// element v. If the Visit function returns nil, the children will not be +// visited. +// Deprecated: use GenericVisitor or another visitor implementation +type Visitor interface { + Visit(v any) (w Visitor) +} + +// BeforeAndAfterVisitor wraps Visitor to provide hooks for being called before +// and after the AST has been visited. +// Deprecated: use GenericVisitor or another visitor implementation +type BeforeAndAfterVisitor interface { + Visitor + Before(x any) + After(x any) +} + +// Walk iterates the AST by calling the Visit function on the Visitor +// v for x before recursing. +// Deprecated: use GenericVisitor.Walk +func Walk(v Visitor, x any) { + if bav, ok := v.(BeforeAndAfterVisitor); !ok { + walk(v, x) + } else { + bav.Before(x) + defer bav.After(x) + walk(bav, x) + } +} + +// WalkBeforeAndAfter iterates the AST by calling the Visit function on the +// Visitor v for x before recursing. +// Deprecated: use GenericVisitor.Walk +func WalkBeforeAndAfter(v BeforeAndAfterVisitor, x any) { + Walk(v, x) +} + +func walk(v Visitor, x any) { + w := v.Visit(x) + if w == nil { + return + } + switch x := x.(type) { + case *Module: + Walk(w, x.Package) + for i := range x.Imports { + Walk(w, x.Imports[i]) + } + for i := range x.Rules { + Walk(w, x.Rules[i]) + } + for i := range x.Annotations { + Walk(w, x.Annotations[i]) + } + for i := range x.Comments { + Walk(w, x.Comments[i]) + } + case *Package: + Walk(w, x.Path) + case *Import: + Walk(w, x.Path) + Walk(w, x.Alias) + case *Rule: + Walk(w, x.Head) + Walk(w, x.Body) + if x.Else != nil { + Walk(w, x.Else) + } + case *Head: + Walk(w, x.Name) + Walk(w, x.Args) + if x.Key != nil { + Walk(w, x.Key) + } + if x.Value != nil { + Walk(w, x.Value) + } + case Body: + for i := range x { + Walk(w, x[i]) + } + case Args: + for i := range x { + Walk(w, x[i]) + } + case *Expr: + switch ts := x.Terms.(type) { + case *Term, *SomeDecl, *Every: + Walk(w, ts) + case []*Term: + for i := range ts { + Walk(w, ts[i]) + } + } + for i := range x.With { + Walk(w, x.With[i]) + } + case *With: + Walk(w, x.Target) + Walk(w, x.Value) + case *Term: + Walk(w, x.Value) + case Ref: + for i := range x { + Walk(w, x[i]) + } + case *object: + x.Foreach(func(k, vv *Term) { + Walk(w, k) + Walk(w, vv) + }) + case *Array: + x.Foreach(func(t *Term) { + Walk(w, t) + }) + case Set: + x.Foreach(func(t *Term) { + Walk(w, t) + }) + case *ArrayComprehension: + Walk(w, x.Term) + Walk(w, x.Body) + case *ObjectComprehension: + Walk(w, x.Key) + Walk(w, x.Value) + Walk(w, x.Body) + case *SetComprehension: + Walk(w, x.Term) + Walk(w, x.Body) + case Call: + for i := range x { + Walk(w, x[i]) + } + case *Every: + if x.Key != nil { + Walk(w, x.Key) + } + Walk(w, x.Value) + Walk(w, x.Domain) + Walk(w, x.Body) + case *SomeDecl: + for i := range x.Symbols { + Walk(w, x.Symbols[i]) + } + } +} + +// WalkVars calls the function f on all vars under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkVars(x any, f func(Var) bool) { + vis := &GenericVisitor{func(x any) bool { + if v, ok := x.(Var); ok { + return f(v) + } + return false + }} + vis.Walk(x) +} + +// WalkClosures calls the function f on all closures under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkClosures(x any, f func(any) bool) { + vis := &GenericVisitor{func(x any) bool { + switch x := x.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension, *Every: + return f(x) + } + return false + }} + vis.Walk(x) +} + +// WalkRefs calls the function f on all references under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkRefs(x any, f func(Ref) bool) { + vis := &GenericVisitor{func(x any) bool { + if r, ok := x.(Ref); ok { + return f(r) + } + return false + }} + vis.Walk(x) +} + +// WalkTerms calls the function f on all terms under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkTerms(x any, f func(*Term) bool) { + vis := &GenericVisitor{func(x any) bool { + if term, ok := x.(*Term); ok { + return f(term) + } + return false + }} + vis.Walk(x) +} + +// WalkWiths calls the function f on all with modifiers under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkWiths(x any, f func(*With) bool) { + vis := &GenericVisitor{func(x any) bool { + if w, ok := x.(*With); ok { + return f(w) + } + return false + }} + vis.Walk(x) +} + +// WalkExprs calls the function f on all expressions under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkExprs(x any, f func(*Expr) bool) { + vis := &GenericVisitor{func(x any) bool { + if r, ok := x.(*Expr); ok { + return f(r) + } + return false + }} + vis.Walk(x) +} + +// WalkBodies calls the function f on all bodies under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkBodies(x any, f func(Body) bool) { + vis := &GenericVisitor{func(x any) bool { + if b, ok := x.(Body); ok { + return f(b) + } + return false + }} + vis.Walk(x) +} + +// WalkRules calls the function f on all rules under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkRules(x any, f func(*Rule) bool) { + vis := &GenericVisitor{func(x any) bool { + if r, ok := x.(*Rule); ok { + stop := f(r) + // NOTE(tsandall): since rules cannot be embedded inside of queries + // we can stop early if there is no else block. + if stop || r.Else == nil { + return true + } + } + return false + }} + vis.Walk(x) +} + +// WalkNodes calls the function f on all nodes under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkNodes(x any, f func(Node) bool) { + vis := &GenericVisitor{func(x any) bool { + if n, ok := x.(Node); ok { + return f(n) + } + return false + }} + vis.Walk(x) +} + +// GenericVisitor provides a utility to walk over AST nodes using a +// closure. If the closure returns true, the visitor will not walk +// over AST nodes under x. +type GenericVisitor struct { + f func(x any) bool +} + +// NewGenericVisitor returns a new GenericVisitor that will invoke the function +// f on AST nodes. +func NewGenericVisitor(f func(x any) bool) *GenericVisitor { + return &GenericVisitor{f} +} + +// Walk iterates the AST by calling the function f on the +// GenericVisitor before recursing. Contrary to the generic Walk, this +// does not require allocating the visitor from heap. +func (vis *GenericVisitor) Walk(x any) { + if vis.f(x) { + return + } + + switch x := x.(type) { + case *Module: + vis.Walk(x.Package) + for i := range x.Imports { + vis.Walk(x.Imports[i]) + } + for i := range x.Rules { + vis.Walk(x.Rules[i]) + } + for i := range x.Annotations { + vis.Walk(x.Annotations[i]) + } + for i := range x.Comments { + vis.Walk(x.Comments[i]) + } + case *Package: + vis.Walk(x.Path) + case *Import: + vis.Walk(x.Path) + vis.Walk(x.Alias) + case *Rule: + vis.Walk(x.Head) + vis.Walk(x.Body) + if x.Else != nil { + vis.Walk(x.Else) + } + case *Head: + vis.Walk(x.Name) + vis.Walk(x.Args) + if x.Key != nil { + vis.Walk(x.Key) + } + if x.Value != nil { + vis.Walk(x.Value) + } + case Body: + for i := range x { + vis.Walk(x[i]) + } + case Args: + for i := range x { + vis.Walk(x[i]) + } + case *Expr: + switch ts := x.Terms.(type) { + case *Term, *SomeDecl, *Every: + vis.Walk(ts) + case []*Term: + for i := range ts { + vis.Walk(ts[i]) + } + } + for i := range x.With { + vis.Walk(x.With[i]) + } + case *With: + vis.Walk(x.Target) + vis.Walk(x.Value) + case *Term: + vis.Walk(x.Value) + case Ref: + for i := range x { + vis.Walk(x[i]) + } + case *object: + x.Foreach(func(k, _ *Term) { + vis.Walk(k) + vis.Walk(x.Get(k)) + }) + case Object: + for _, k := range x.Keys() { + vis.Walk(k) + vis.Walk(x.Get(k)) + } + case *Array: + for i := range x.Len() { + vis.Walk(x.Elem(i)) + } + case Set: + xSlice := x.Slice() + for i := range xSlice { + vis.Walk(xSlice[i]) + } + case *ArrayComprehension: + vis.Walk(x.Term) + vis.Walk(x.Body) + case *ObjectComprehension: + vis.Walk(x.Key) + vis.Walk(x.Value) + vis.Walk(x.Body) + case *SetComprehension: + vis.Walk(x.Term) + vis.Walk(x.Body) + case Call: + for i := range x { + vis.Walk(x[i]) + } + case *Every: + if x.Key != nil { + vis.Walk(x.Key) + } + vis.Walk(x.Value) + vis.Walk(x.Domain) + vis.Walk(x.Body) + case *SomeDecl: + for i := range x.Symbols { + vis.Walk(x.Symbols[i]) + } + } +} + +// BeforeAfterVisitor provides a utility to walk over AST nodes using +// closures. If the before closure returns true, the visitor will not +// walk over AST nodes under x. The after closure is invoked always +// after visiting a node. +type BeforeAfterVisitor struct { + before func(x any) bool + after func(x any) +} + +// NewBeforeAfterVisitor returns a new BeforeAndAfterVisitor that +// will invoke the functions before and after AST nodes. +func NewBeforeAfterVisitor(before func(x any) bool, after func(x any)) *BeforeAfterVisitor { + return &BeforeAfterVisitor{before, after} +} + +// Walk iterates the AST by calling the functions on the +// BeforeAndAfterVisitor before and after recursing. Contrary to the +// generic Walk, this does not require allocating the visitor from +// heap. +func (vis *BeforeAfterVisitor) Walk(x any) { + defer vis.after(x) + if vis.before(x) { + return + } + + switch x := x.(type) { + case *Module: + vis.Walk(x.Package) + for i := range x.Imports { + vis.Walk(x.Imports[i]) + } + for i := range x.Rules { + vis.Walk(x.Rules[i]) + } + for i := range x.Annotations { + vis.Walk(x.Annotations[i]) + } + for i := range x.Comments { + vis.Walk(x.Comments[i]) + } + case *Package: + vis.Walk(x.Path) + case *Import: + vis.Walk(x.Path) + vis.Walk(x.Alias) + case *Rule: + vis.Walk(x.Head) + vis.Walk(x.Body) + if x.Else != nil { + vis.Walk(x.Else) + } + case *Head: + if len(x.Reference) > 0 { + vis.Walk(x.Reference) + } else { + vis.Walk(x.Name) + if x.Key != nil { + vis.Walk(x.Key) + } + } + vis.Walk(x.Args) + if x.Value != nil { + vis.Walk(x.Value) + } + case Body: + for i := range x { + vis.Walk(x[i]) + } + case Args: + for i := range x { + vis.Walk(x[i]) + } + case *Expr: + switch ts := x.Terms.(type) { + case *Term, *SomeDecl, *Every: + vis.Walk(ts) + case []*Term: + for i := range ts { + vis.Walk(ts[i]) + } + } + for i := range x.With { + vis.Walk(x.With[i]) + } + case *With: + vis.Walk(x.Target) + vis.Walk(x.Value) + case *Term: + vis.Walk(x.Value) + case Ref: + for i := range x { + vis.Walk(x[i]) + } + case *object: + x.Foreach(func(k, _ *Term) { + vis.Walk(k) + vis.Walk(x.Get(k)) + }) + case Object: + x.Foreach(func(k, _ *Term) { + vis.Walk(k) + vis.Walk(x.Get(k)) + }) + case *Array: + x.Foreach(func(t *Term) { + vis.Walk(t) + }) + case Set: + xSlice := x.Slice() + for i := range xSlice { + vis.Walk(xSlice[i]) + } + case *ArrayComprehension: + vis.Walk(x.Term) + vis.Walk(x.Body) + case *ObjectComprehension: + vis.Walk(x.Key) + vis.Walk(x.Value) + vis.Walk(x.Body) + case *SetComprehension: + vis.Walk(x.Term) + vis.Walk(x.Body) + case Call: + for i := range x { + vis.Walk(x[i]) + } + case *Every: + if x.Key != nil { + vis.Walk(x.Key) + } + vis.Walk(x.Value) + vis.Walk(x.Domain) + vis.Walk(x.Body) + case *SomeDecl: + for i := range x.Symbols { + vis.Walk(x.Symbols[i]) + } + } +} + +// VarVisitor walks AST nodes under a given node and collects all encountered +// variables. The collected variables can be controlled by specifying +// VarVisitorParams when creating the visitor. +type VarVisitor struct { + params VarVisitorParams + vars VarSet +} + +// VarVisitorParams contains settings for a VarVisitor. +type VarVisitorParams struct { + SkipRefHead bool + SkipRefCallHead bool + SkipObjectKeys bool + SkipClosures bool + SkipWithTarget bool + SkipSets bool +} + +// NewVarVisitor returns a new VarVisitor object. +func NewVarVisitor() *VarVisitor { + return &VarVisitor{ + vars: NewVarSet(), + } +} + +// Clear resets the visitor to its initial state, and returns it for chaining. +func (vis *VarVisitor) Clear() *VarVisitor { + vis.params = VarVisitorParams{} + clear(vis.vars) + + return vis +} + +// ClearOrNew returns a new VarVisitor if vis is nil, or else a cleared VarVisitor. +func (vis *VarVisitor) ClearOrNew() *VarVisitor { + if vis == nil { + return NewVarVisitor() + } + return vis.Clear() +} + +// WithParams sets the parameters in params on vis. +func (vis *VarVisitor) WithParams(params VarVisitorParams) *VarVisitor { + vis.params = params + return vis +} + +// Add adds a variable v to the visitor's set of variables. +func (vis *VarVisitor) Add(v Var) { + if vis.vars == nil { + vis.vars = NewVarSet(v) + } else { + vis.vars.Add(v) + } +} + +// Vars returns a VarSet that contains collected vars. +func (vis *VarVisitor) Vars() VarSet { + return vis.vars +} + +// visit determines if the VarVisitor will recurse into x: if it returns `true`, +// the visitor will _skip_ that branch of the AST +func (vis *VarVisitor) visit(v any) bool { + if vis.params.SkipObjectKeys { + if o, ok := v.(Object); ok { + o.Foreach(func(_, v *Term) { + vis.Walk(v) + }) + return true + } + } + if vis.params.SkipRefHead { + if r, ok := v.(Ref); ok { + rSlice := r[1:] + for i := range rSlice { + vis.Walk(rSlice[i]) + } + return true + } + } + if vis.params.SkipClosures { + switch v := v.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension: + return true + case *Expr: + if ev, ok := v.Terms.(*Every); ok { + vis.Walk(ev.Domain) + // We're _not_ walking ev.Body -- that's the closure here + return true + } + } + } + if vis.params.SkipWithTarget { + if v, ok := v.(*With); ok { + vis.Walk(v.Value) + return true + } + } + if vis.params.SkipSets { + if _, ok := v.(Set); ok { + return true + } + } + if vis.params.SkipRefCallHead { + switch v := v.(type) { + case *Expr: + if terms, ok := v.Terms.([]*Term); ok { + termSlice := terms[0].Value.(Ref)[1:] + for i := range termSlice { + vis.Walk(termSlice[i]) + } + for i := 1; i < len(terms); i++ { + vis.Walk(terms[i]) + } + for i := range v.With { + vis.Walk(v.With[i]) + } + return true + } + case Call: + operator := v[0].Value.(Ref) + for i := 1; i < len(operator); i++ { + vis.Walk(operator[i]) + } + for i := 1; i < len(v); i++ { + vis.Walk(v[i]) + } + return true + case *With: + if ref, ok := v.Target.Value.(Ref); ok { + refSlice := ref[1:] + for i := range refSlice { + vis.Walk(refSlice[i]) + } + } + if ref, ok := v.Value.Value.(Ref); ok { + refSlice := ref[1:] + for i := range refSlice { + vis.Walk(refSlice[i]) + } + } else { + vis.Walk(v.Value) + } + return true + } + } + if v, ok := v.(Var); ok { + vis.Add(v) + } + return false +} + +// Walk iterates the AST by calling the function f on the +// GenericVisitor before recursing. Contrary to the generic Walk, this +// does not require allocating the visitor from heap. +func (vis *VarVisitor) Walk(x any) { + if vis.visit(x) { + return + } + + switch x := x.(type) { + case *Module: + vis.Walk(x.Package) + for i := range x.Imports { + vis.Walk(x.Imports[i]) + } + for i := range x.Rules { + vis.Walk(x.Rules[i]) + } + for i := range x.Comments { + vis.Walk(x.Comments[i]) + } + case *Package: + vis.WalkRef(x.Path) + case *Import: + vis.Walk(x.Path) + if x.Alias != "" { + vis.Add(x.Alias) + } + case *Rule: + vis.Walk(x.Head) + vis.WalkBody(x.Body) + if x.Else != nil { + vis.Walk(x.Else) + } + case *Head: + if len(x.Reference) > 0 { + vis.WalkRef(x.Reference) + } else { + vis.Add(x.Name) + if x.Key != nil { + vis.Walk(x.Key) + } + } + vis.WalkArgs(x.Args) + if x.Value != nil { + vis.Walk(x.Value) + } + case Body: + vis.WalkBody(x) + case Args: + vis.WalkArgs(x) + case *Expr: + switch ts := x.Terms.(type) { + case *Term, *SomeDecl, *Every: + vis.Walk(ts) + case []*Term: + for i := range ts { + vis.Walk(ts[i].Value) + } + } + for i := range x.With { + vis.Walk(x.With[i]) + } + case *With: + vis.Walk(x.Target.Value) + vis.Walk(x.Value.Value) + case *Term: + vis.Walk(x.Value) + case Ref: + for i := range x { + vis.Walk(x[i].Value) + } + case *object: + x.Foreach(func(k, _ *Term) { + vis.Walk(k) + vis.Walk(x.Get(k)) + }) + case *Array: + x.Foreach(func(t *Term) { + vis.Walk(t) + }) + case Set: + xSlice := x.Slice() + for i := range xSlice { + vis.Walk(xSlice[i]) + } + case *ArrayComprehension: + vis.Walk(x.Term.Value) + vis.WalkBody(x.Body) + case *ObjectComprehension: + vis.Walk(x.Key.Value) + vis.Walk(x.Value.Value) + vis.WalkBody(x.Body) + case *SetComprehension: + vis.Walk(x.Term.Value) + vis.WalkBody(x.Body) + case Call: + for i := range x { + vis.Walk(x[i].Value) + } + case *Every: + if x.Key != nil { + vis.Walk(x.Key.Value) + } + vis.Walk(x.Value) + vis.Walk(x.Domain) + vis.WalkBody(x.Body) + case *SomeDecl: + for i := range x.Symbols { + vis.Walk(x.Symbols[i]) + } + } +} + +// WalkArgs exists only to avoid the allocation cost of boxing Args to `any` in the VarVisitor. +// Use it when you know beforehand that the type to walk is Args. +func (vis *VarVisitor) WalkArgs(x Args) { + for i := range x { + vis.Walk(x[i].Value) + } +} + +// WalkRef exists only to avoid the allocation cost of boxing Ref to `any` in the VarVisitor. +// Use it when you know beforehand that the type to walk is a Ref. +func (vis *VarVisitor) WalkRef(ref Ref) { + if vis.params.SkipRefHead { + ref = ref[1:] + } + for _, term := range ref { + vis.Walk(term.Value) + } +} + +// WalkBody exists only to avoid the allocation cost of boxing Body to `any` in the VarVisitor. +// Use it when you know beforehand that the type to walk is a Body. +func (vis *VarVisitor) WalkBody(body Body) { + for _, expr := range body { + vis.Walk(expr) + } +} diff --git a/third_party/opa/v1/ast/visit_bench_test.go b/third_party/opa/v1/ast/visit_bench_test.go new file mode 100644 index 000000000000..c7a5c87273d6 --- /dev/null +++ b/third_party/opa/v1/ast/visit_bench_test.go @@ -0,0 +1,51 @@ +package ast + +import "testing" + +// Simple benchmark to demonstrate the cost of boxing to `any`, and why it's +// good not to when it's possible to avoid it. +// +// BenchmarkVarVisitorWalkAnyVsSpecific/Walk-12 33266721 36.70 ns/op 24 B/op 1 allocs/op +// BenchmarkVarVisitorWalkAnyVsSpecific/WalkBody-12 70195105 17.41 ns/op 0 B/op 0 allocs/op +func BenchmarkVarVisitorWalkAnyVsSpecific(b *testing.B) { + bod := MustParseBody("foo") + vis := NewVarVisitor() + + b.Run("Walk", func(b *testing.B) { + for range b.N { + vis.Walk(bod) + } + }) + + if len(vis.vars) != 1 { + b.Fatalf("Expected exactly one variable in AST but got %d: %v", len(vis.vars), vis.vars) + } + + vis.Clear() + b.ResetTimer() + + b.Run("WalkBody", func(b *testing.B) { + for range b.N { + vis.WalkBody(bod) + } + }) + + if len(vis.vars) != 1 { + b.Fatalf("Expected exactly one variable in AST but got %d: %v", len(vis.vars), vis.vars) + } +} + +// Example benchmark of us over-allocating in place like [outputVarsForExprEq] +// +// BenchmarkVarSetUpdateEmpty-12 15169982 79.69 ns/op 128 B/op 4 allocs/op +func BenchmarkVarSetUpdateEmpty(b *testing.B) { + ref := MustParseRef("foo.bar.baz") + used := NewVarSet() + + for range b.N { + for _, t := range ref[1:] { + vars := t.Vars() + used.Update(vars) + } + } +} diff --git a/third_party/opa/v1/ast/visit_test.go b/third_party/opa/v1/ast/visit_test.go new file mode 100644 index 000000000000..790fc706aa54 --- /dev/null +++ b/third_party/opa/v1/ast/visit_test.go @@ -0,0 +1,228 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "testing" +) + +type testVis struct { + elems []any +} + +func (vis *testVis) Visit(x any) bool { + vis.elems = append(vis.elems, x) + return false +} + +func TestVisitor(t *testing.T) { + + rule := module(`package a.b + +import input.x.y as z + +t[x] = y if { + p[x] = {"foo": [y, 2, {"bar": 3}]} + not q[x] + y = [[x, z] | x = "x"; z = "z"] + z = {"foo": [x, z] | x = "x"; z = "z"} + s = {1 | a[i] = "foo"} + some x0, y0, z0 + count({1, 2, 3}, n) with input.foo.bar as x +} + +p if { false } else if { false } else if { true } + +fn([x, y]) = z if { json.unmarshal(x, z); z > y } +`) + vis := &testVis{} + NewGenericVisitor(vis.Visit).Walk(rule) + + if exp, act := 254, len(vis.elems); exp != act { + t.Errorf("Expected exactly %d elements in AST but got %d: %v", exp, act, vis.elems) + } +} + +func TestVisitorAnnotations(t *testing.T) { + + module, err := ParseModuleWithOpts("test.rego", `package test + +# METADATA +# scope: rule +p := 7`, ParserOptions{ProcessAnnotation: true}) + if err != nil { + t.Fatal(err) + } + + vis := &testVis{} + + NewGenericVisitor(vis.Visit).Walk(module) + + exp := 20 + + if len(vis.elems) != exp { + t.Fatalf("expected %d elements but got %v: %v", exp, len(vis.elems), vis.elems) + } +} + +func TestWalkVars(t *testing.T) { + x := MustParseBody(`x = 1; data.abc[2] = y; y[z] = [q | q = 1]`) + found := NewVarSet() + WalkVars(x, func(v Var) bool { + found.Add(v) + return false + }) + expected := NewVarSet(Var("x"), Var("data"), Var("y"), Var("z"), Var("q"), Var("eq")) + if !expected.Equal(found) { + t.Fatalf("Expected %v but got: %v", expected, found) + } +} + +func TestGenericVisitor(t *testing.T) { + rule := module(`package a.b + +import input.x.y as z + +t[x] = y if { + p[x] = {"foo": [y, 2, {"bar": 3}]} + not q[x] + y = [[x, z] | x = "x"; z = "z"] + z = {"foo": [x, z] | x = "x"; z = "z"} + s = {1 | a[i] = "foo"} + some x0, y0, z0 + count({1, 2, 3}, n) with input.foo.bar as x +} + +p if { false } else if { false } else if { true } + +fn([x, y]) = z if { json.unmarshal(x, z); z > y } +`) + + var elems []any + vis := NewGenericVisitor(func(x any) bool { + elems = append(elems, x) + return false + }) + vis.Walk(rule) + + if len(elems) != 254 { + t.Errorf("Expected exactly 254 elements in AST but got %d: %v", len(elems), elems) + } +} + +func TestBeforeAfterVisitor(t *testing.T) { + rule := module(`package a.b + +import input.x.y as z + +t[x] = y if { + p[x] = {"foo": [y, 2, {"bar": 3}]} + not q[x] + y = [[x, z] | x = "x"; z = "z"] + z = {"foo": [x, z] | x = "x"; z = "z"} + s = {1 | a[i] = "foo"} + some x0, y0, z0 + count({1, 2, 3}, n) with input.foo.bar as x +} + +p if { false } else if { false } else if { true } + +fn([x, y]) = z if { json.unmarshal(x, z); z > y } +`) + + var before, after []any + vis := NewBeforeAfterVisitor(func(x any) bool { + before = append(before, x) + return false + }, + func(x any) { + after = append(after, x) + }) + vis.Walk(rule) + + if exp, act := 264, len(before); exp != act { + t.Errorf("Expected exactly %d before elements in AST but got %d: %v", exp, act, before) + } + + if exp, act := 264, len(before); exp != act { + t.Errorf("Expected exactly %d after elements in AST but got %d: %v", exp, act, after) + } +} + +func TestVarVisitor(t *testing.T) { + + tests := []struct { + stmt string + params VarVisitorParams + expected string + }{ + {"{x: y}", VarVisitorParams{SkipObjectKeys: true}, "[y]"}, + {"foo with input.bar.baz as qux[corge]", VarVisitorParams{SkipWithTarget: true}, "[foo, qux, corge]"}, + {"data.foo[x] = bar.baz[y]", VarVisitorParams{SkipRefHead: true}, "[x, y]"}, + {`foo = [x | data.a[i] = x]`, VarVisitorParams{SkipClosures: true}, "[foo, eq]"}, + {`x = 1; y = 2; z = x + y; count([x, y, z], z)`, VarVisitorParams{}, "[x, y, z, eq, plus, count]"}, + {"some x, y", VarVisitorParams{}, "[x, y]"}, + } + + for _, tc := range tests { + t.Run(tc.stmt, func(t *testing.T) { + stmt := MustParseStatement(tc.stmt) + + expected := NewVarSet() + MustParseTerm(tc.expected).Value.(*Array).Foreach(func(x *Term) { + expected.Add(x.Value.(Var)) + }) + + vis := NewVarVisitor().WithParams(tc.params) + vis.Walk(stmt) + + if !vis.Vars().Equal(expected) { + t.Errorf("Params %#v expected %v but got: %v", tc.params, expected, vis.Vars()) + } + }) + } +} + +func TestGenericVisitorLazyObject(t *testing.T) { + o := LazyObject(map[string]any{"foo": 3}) + act := 0 + WalkTerms(o, func(n *Term) bool { + switch n.Value { + case String("foo"): + act++ + case Number("3"): + act++ + } + + return false + }) + if exp := 2; exp != act { + t.Errorf("expected %v, got %v", exp, act) + } +} + +func TestGenericBeforeAfterVisitorLazyObject(t *testing.T) { + o := LazyObject(map[string]any{"foo": 3}) + act := 0 + vis := NewBeforeAfterVisitor(func(x any) bool { + t, ok := x.(*Term) + if !ok { + return false + } + switch t.Value { + case String("foo"): + act++ + case Number("3"): + act++ + } + + return false + }, + func(any) {}) + vis.Walk(o) + if exp := 2; exp != act { + t.Errorf("expected %v, got %v", exp, act) + } +} diff --git a/third_party/opa/v1/bundle/bundle.go b/third_party/opa/v1/bundle/bundle.go new file mode 100644 index 000000000000..5b418c360b2a --- /dev/null +++ b/third_party/opa/v1/bundle/bundle.go @@ -0,0 +1,1845 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package bundle implements bundle loading. +package bundle + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "maps" + "net/url" + "os" + "path" + "path/filepath" + "reflect" + "strings" + "sync" + + "github.com/gobwas/glob" + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/internal/merge" + "github.com/open-policy-agent/opa/v1/ast" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/format" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/util" +) + +// Common file extensions and file names. +const ( + RegoExt = ".rego" + WasmFile = "policy.wasm" + PlanFile = "plan.json" + ManifestExt = ".manifest" + SignaturesFile = "signatures.json" + patchFile = "patch.json" + dataFile = "data.json" + yamlDataFile = "data.yaml" + ymlDataFile = "data.yml" + defaultHashingAlg = "SHA-256" + DefaultSizeLimitBytes = (1024 * 1024 * 1024) // limit bundle reads to 1GB to protect against gzip bombs + DeltaBundleType = "delta" + SnapshotBundleType = "snapshot" +) + +// Bundle represents a loaded bundle. The bundle can contain data and policies. +type Bundle struct { + Signatures SignaturesConfig + Manifest Manifest + Data map[string]any + Modules []ModuleFile + Wasm []byte // Deprecated. Use WasmModules instead + WasmModules []WasmModuleFile + PlanModules []PlanModuleFile + Patch Patch + Etag string + Raw []Raw + + lazyLoadingMode bool + sizeLimitBytes int64 +} + +// Raw contains raw bytes representing the bundle's content +type Raw struct { + Path string + Value []byte + module *ModuleFile +} + +// Patch contains an array of objects wherein each object represents the patch operation to be +// applied to the bundle data. +type Patch struct { + Data []PatchOperation `json:"data,omitempty"` +} + +// PatchOperation models a single patch operation against a document. +type PatchOperation struct { + Op string `json:"op"` + Path string `json:"path"` + Value any `json:"value"` +} + +// SignaturesConfig represents an array of JWTs that encapsulate the signatures for the bundle. +type SignaturesConfig struct { + Signatures []string `json:"signatures,omitempty"` + Plugin string `json:"plugin,omitempty"` +} + +// isEmpty returns if the SignaturesConfig is empty. +func (s SignaturesConfig) isEmpty() bool { + return reflect.DeepEqual(s, SignaturesConfig{}) +} + +// DecodedSignature represents the decoded JWT payload. +type DecodedSignature struct { + Files []FileInfo `json:"files"` + KeyID string `json:"keyid"` // Deprecated, use kid in the JWT header instead. + Scope string `json:"scope"` + IssuedAt int64 `json:"iat"` + Issuer string `json:"iss"` +} + +// FileInfo contains the hashing algorithm used, resulting digest etc. +type FileInfo struct { + Name string `json:"name"` + Hash string `json:"hash"` + Algorithm string `json:"algorithm"` +} + +// NewFile returns a new FileInfo. +func NewFile(name, hash, alg string) FileInfo { + return FileInfo{ + Name: name, + Hash: hash, + Algorithm: alg, + } +} + +// Manifest represents the manifest from a bundle. The manifest may contain +// metadata such as the bundle revision. +type Manifest struct { + Revision string `json:"revision"` + Roots *[]string `json:"roots,omitempty"` + WasmResolvers []WasmResolver `json:"wasm,omitempty"` + // RegoVersion is the global Rego version for the bundle described by this Manifest. + // The Rego version of individual files can be overridden in FileRegoVersions. + // We don't use ast.RegoVersion here, as this iota type's order isn't guaranteed to be stable over time. + // We use a pointer so that we can support hand-made bundles that don't have an explicit version appropriately. + // E.g. in OPA 0.x if --v1-compatible is used when consuming the bundle, and there is no specified version, + // we should default to v1; if --v1-compatible isn't used, we should default to v0. In OPA 1.0, no --x-compatible + // flag and no explicit bundle version should default to v1. + RegoVersion *int `json:"rego_version,omitempty"` + // FileRegoVersions is a map from file paths to Rego versions. + // This allows individual files to override the global Rego version specified by RegoVersion. + FileRegoVersions map[string]int `json:"file_rego_versions,omitempty"` + Metadata map[string]any `json:"metadata,omitempty"` + + compiledFileRegoVersions []fileRegoVersion +} + +type fileRegoVersion struct { + path glob.Glob + version int +} + +// WasmResolver maps a wasm module to an entrypoint ref. +type WasmResolver struct { + Entrypoint string `json:"entrypoint,omitempty"` + Module string `json:"module,omitempty"` + Annotations []*ast.Annotations `json:"annotations,omitempty"` +} + +// Init initializes the manifest. If you instantiate a manifest +// manually, call Init to ensure that the roots are set properly. +func (m *Manifest) Init() { + if m.Roots == nil { + defaultRoots := []string{""} + m.Roots = &defaultRoots + } +} + +// AddRoot adds r to the roots of m. This function is idempotent. +func (m *Manifest) AddRoot(r string) { + m.Init() + if !RootPathsContain(*m.Roots, r) { + *m.Roots = append(*m.Roots, r) + } +} + +func (m *Manifest) SetRegoVersion(v ast.RegoVersion) { + m.Init() + regoVersion := 0 + if v == ast.RegoV1 { + regoVersion = 1 + } + m.RegoVersion = ®oVersion +} + +// Equal returns true if m is semantically equivalent to other. +func (m Manifest) Equal(other Manifest) bool { + + // This is safe since both are passed by value. + m.Init() + other.Init() + + if m.Revision != other.Revision { + return false + } + + if m.RegoVersion == nil && other.RegoVersion != nil { + return false + } + if m.RegoVersion != nil && other.RegoVersion == nil { + return false + } + if m.RegoVersion != nil && other.RegoVersion != nil && *m.RegoVersion != *other.RegoVersion { + return false + } + + // If both are nil, or both are empty, we consider them equal. + if !(len(m.FileRegoVersions) == 0 && len(other.FileRegoVersions) == 0) && + !reflect.DeepEqual(m.FileRegoVersions, other.FileRegoVersions) { + return false + } + + if !reflect.DeepEqual(m.Metadata, other.Metadata) { + return false + } + + return m.equalWasmResolversAndRoots(other) +} + +func (m Manifest) Empty() bool { + return m.Equal(Manifest{}) +} + +// Copy returns a deep copy of the manifest. +func (m Manifest) Copy() Manifest { + m.Init() + roots := make([]string, len(*m.Roots)) + copy(roots, *m.Roots) + m.Roots = &roots + + wasmModules := make([]WasmResolver, len(m.WasmResolvers)) + copy(wasmModules, m.WasmResolvers) + m.WasmResolvers = wasmModules + + metadata := m.Metadata + + if metadata != nil { + m.Metadata = make(map[string]any) + maps.Copy(m.Metadata, metadata) + } + + return m +} + +func (m Manifest) String() string { + m.Init() + if m.RegoVersion != nil { + return fmt.Sprintf("", + m.Revision, *m.RegoVersion, *m.Roots, m.WasmResolvers, m.Metadata) + } + return fmt.Sprintf("", + m.Revision, *m.Roots, m.WasmResolvers, m.Metadata) +} + +func (m Manifest) rootSet() stringSet { + rs := map[string]struct{}{} + + for _, r := range *m.Roots { + rs[r] = struct{}{} + } + + return stringSet(rs) +} + +func (m Manifest) equalWasmResolversAndRoots(other Manifest) bool { + if len(m.WasmResolvers) != len(other.WasmResolvers) { + return false + } + + for i := range len(m.WasmResolvers) { + if !m.WasmResolvers[i].Equal(&other.WasmResolvers[i]) { + return false + } + } + + return m.rootSet().Equal(other.rootSet()) +} + +func (wr *WasmResolver) Equal(other *WasmResolver) bool { + if wr == nil && other == nil { + return true + } + + if wr == nil || other == nil { + return false + } + + if wr.Module != other.Module { + return false + } + + if wr.Entrypoint != other.Entrypoint { + return false + } + + annotLen := len(wr.Annotations) + if annotLen != len(other.Annotations) { + return false + } + + for i := range annotLen { + if wr.Annotations[i].Compare(other.Annotations[i]) != 0 { + return false + } + } + + return true +} + +type stringSet map[string]struct{} + +func (ss stringSet) Equal(other stringSet) bool { + if len(ss) != len(other) { + return false + } + for k := range other { + if _, ok := ss[k]; !ok { + return false + } + } + return true +} + +func (m *Manifest) validateAndInjectDefaults(b Bundle) error { + + m.Init() + + // Validate roots in bundle. + roots := *m.Roots + + // Standardize the roots (no starting or trailing slash) + for i := range roots { + roots[i] = strings.Trim(roots[i], "/") + } + + for i := range len(roots) - 1 { + for j := i + 1; j < len(roots); j++ { + if RootPathsOverlap(roots[i], roots[j]) { + return fmt.Errorf("manifest has overlapped roots: '%v' and '%v'", roots[i], roots[j]) + } + } + } + + // Validate modules in bundle. + for _, module := range b.Modules { + found := false + if path, err := module.Parsed.Package.Path.Ptr(); err == nil { + found = RootPathsContain(roots, path) + } + if !found { + return fmt.Errorf("manifest roots %v do not permit '%v' in module '%v'", roots, module.Parsed.Package, module.Path) + } + } + + // Build a set of wasm module entrypoints to validate + wasmModuleToEps := map[string]string{} + seenEps := map[string]struct{}{} + for _, wm := range b.WasmModules { + wasmModuleToEps[wm.Path] = "" + } + + for _, wmConfig := range b.Manifest.WasmResolvers { + _, ok := wasmModuleToEps[wmConfig.Module] + if !ok { + return fmt.Errorf("manifest references wasm module '%s' but the module file does not exist", wmConfig.Module) + } + + // Ensure wasm module entrypoint in within bundle roots + if !RootPathsContain(roots, wmConfig.Entrypoint) { + return fmt.Errorf("manifest roots %v do not permit '%v' entrypoint for wasm module '%v'", roots, wmConfig.Entrypoint, wmConfig.Module) + } + + if _, ok := seenEps[wmConfig.Entrypoint]; ok { + return fmt.Errorf("entrypoint '%s' cannot be used by more than one wasm module", wmConfig.Entrypoint) + } + seenEps[wmConfig.Entrypoint] = struct{}{} + + wasmModuleToEps[wmConfig.Module] = wmConfig.Entrypoint + } + + // Validate data patches in bundle. + for _, patch := range b.Patch.Data { + path := strings.Trim(patch.Path, "/") + if !RootPathsContain(roots, path) { + return fmt.Errorf("manifest roots %v do not permit data patch at path '%s'", roots, path) + } + } + + if b.lazyLoadingMode { + return nil + } + + // Validate data in bundle. + return dfs(b.Data, "", func(path string, node any) (bool, error) { + path = strings.Trim(path, "/") + if RootPathsContain(roots, path) { + return true, nil + } + + if _, ok := node.(map[string]any); ok { + for i := range roots { + if RootPathsContain(strings.Split(path, "/"), roots[i]) { + return false, nil + } + } + } + return false, fmt.Errorf("manifest roots %v do not permit data at path '/%s' (hint: check bundle directory structure)", roots, path) + }) +} + +// ModuleFile represents a single module contained in a bundle. +type ModuleFile struct { + URL string + Path string + RelativePath string + Raw []byte + Parsed *ast.Module +} + +// WasmModuleFile represents a single wasm module contained in a bundle. +type WasmModuleFile struct { + URL string + Path string + Entrypoints []ast.Ref + Raw []byte +} + +// PlanModuleFile represents a single plan module contained in a bundle. +// +// NOTE(tsandall): currently the plans are just opaque binary blobs. In the +// future we could inject the entrypoints so that the plans could be executed +// inside of OPA proper like we do for Wasm modules. +type PlanModuleFile struct { + URL string + Path string + Raw []byte +} + +var ( + pluginMtx sync.Mutex + + // The bundle activator to use by default. + bundleExtActivator string + + // The function to use for creating a storage.Store for bundles. + BundleExtStore func() storage.Store +) + +// RegisterDefaultBundleActivator sets the default bundle activator for OPA to use for bundle activation. +// The id must already have been registered with RegisterActivator. +func RegisterDefaultBundleActivator(id string) { + pluginMtx.Lock() + defer pluginMtx.Unlock() + + bundleExtActivator = id +} + +// RegisterStoreFunc sets the function to use for creating storage for bundles +// in OPA. If no function is registered, OPA will use situational defaults to +// decide on what sort of storage.Store to create when bundle storage is +// needed. Typically the default is inmem.Store. +func RegisterStoreFunc(s func() storage.Store) { + pluginMtx.Lock() + defer pluginMtx.Unlock() + + BundleExtStore = s +} + +// HasExtension returns true if a default bundle activator has been set +// with RegisterDefaultBundleActivator. +func HasExtension() bool { + pluginMtx.Lock() + defer pluginMtx.Unlock() + + return bundleExtActivator != "" +} + +// Reader contains the reader to load the bundle from. +type Reader struct { + loader DirectoryLoader + includeManifestInData bool + metrics metrics.Metrics + baseDir string + verificationConfig *VerificationConfig + skipVerify bool + processAnnotations bool + capabilities *ast.Capabilities + files map[string]FileInfo // files in the bundle signature payload + sizeLimitBytes int64 + etag string + lazyLoadingMode bool + name string + persist bool + regoVersion ast.RegoVersion + followSymlinks bool +} + +// NewReader is deprecated. Use NewCustomReader instead. +func NewReader(r io.Reader) *Reader { + return NewCustomReader(NewTarballLoader(r)) +} + +// NewCustomReader returns a new Reader configured to use the +// specified DirectoryLoader. +func NewCustomReader(loader DirectoryLoader) *Reader { + nr := Reader{ + loader: loader, + metrics: metrics.New(), + files: make(map[string]FileInfo), + sizeLimitBytes: DefaultSizeLimitBytes + 1, + lazyLoadingMode: HasExtension(), + } + return &nr +} + +// IncludeManifestInData sets whether the manifest metadata should be +// included in the bundle's data. +func (r *Reader) IncludeManifestInData(includeManifestInData bool) *Reader { + r.includeManifestInData = includeManifestInData + return r +} + +// WithMetrics sets the metrics object to be used while loading bundles +func (r *Reader) WithMetrics(m metrics.Metrics) *Reader { + r.metrics = m + return r +} + +// WithBaseDir sets a base directory for file paths of loaded Rego +// modules. This will *NOT* affect the loaded path of data files. +func (r *Reader) WithBaseDir(dir string) *Reader { + r.baseDir = dir + return r +} + +// WithBundleVerificationConfig sets the key configuration used to verify a signed bundle +func (r *Reader) WithBundleVerificationConfig(config *VerificationConfig) *Reader { + r.verificationConfig = config + return r +} + +// WithSkipBundleVerification skips verification of a signed bundle +func (r *Reader) WithSkipBundleVerification(skipVerify bool) *Reader { + r.skipVerify = skipVerify + return r +} + +// WithProcessAnnotations enables annotation processing during .rego file parsing. +func (r *Reader) WithProcessAnnotations(yes bool) *Reader { + r.processAnnotations = yes + return r +} + +// WithCapabilities sets the supported capabilities when loading the files +func (r *Reader) WithCapabilities(caps *ast.Capabilities) *Reader { + r.capabilities = caps + return r +} + +// WithJSONOptions sets the JSON options on the parser (now a no-op). +// +// Deprecated: Use SetOptions in the json package instead, where a longer description +// of why this is deprecated also can be found. +func (r *Reader) WithJSONOptions(*astJSON.Options) *Reader { + return r +} + +// WithSizeLimitBytes sets the size limit to apply to files in the bundle. If files are larger +// than this, an error will be returned by the reader. +func (r *Reader) WithSizeLimitBytes(n int64) *Reader { + r.sizeLimitBytes = n + 1 + return r +} + +// WithBundleEtag sets the given etag value on the bundle +func (r *Reader) WithBundleEtag(etag string) *Reader { + r.etag = etag + return r +} + +// WithBundleName specifies the bundle name +func (r *Reader) WithBundleName(name string) *Reader { + r.name = name + return r +} + +func (r *Reader) WithFollowSymlinks(yes bool) *Reader { + r.followSymlinks = yes + return r +} + +// WithLazyLoadingMode sets the bundle loading mode. If true, +// bundles will be read in lazy mode. In this mode, data files in the bundle will not be +// deserialized and the check to validate that the bundle data does not contain paths +// outside the bundle's roots will not be performed while reading the bundle. +func (r *Reader) WithLazyLoadingMode(yes bool) *Reader { + r.lazyLoadingMode = yes + return r +} + +// WithBundlePersistence specifies if the downloaded bundle will eventually be persisted to disk. +func (r *Reader) WithBundlePersistence(persist bool) *Reader { + r.persist = persist + return r +} + +func (r *Reader) WithRegoVersion(version ast.RegoVersion) *Reader { + r.regoVersion = version + return r +} + +func (r *Reader) ParserOptions() ast.ParserOptions { + return ast.ParserOptions{ + ProcessAnnotation: r.processAnnotations, + Capabilities: r.capabilities, + RegoVersion: r.regoVersion, + } +} + +// Read returns a new Bundle loaded from the reader. +func (r *Reader) Read() (Bundle, error) { + + var bundle Bundle + var descriptors []*Descriptor + var err error + var raw []Raw + + bundle.Signatures, bundle.Patch, descriptors, err = preProcessBundle(r.loader, r.skipVerify, r.sizeLimitBytes) + if err != nil { + return bundle, err + } + + bundle.lazyLoadingMode = r.lazyLoadingMode + bundle.sizeLimitBytes = r.sizeLimitBytes + + if bundle.Type() == SnapshotBundleType { + err = r.checkSignaturesAndDescriptors(bundle.Signatures) + if err != nil { + return bundle, err + } + + bundle.Data = map[string]any{} + } + + var modules []ModuleFile + for _, f := range descriptors { + buf, err := readFile(f, r.sizeLimitBytes) + if err != nil { + return bundle, err + } + + // verify the file content + if bundle.Type() == SnapshotBundleType && !bundle.Signatures.isEmpty() { + path := f.Path() + if r.baseDir != "" { + path = f.URL() + } + path = strings.TrimPrefix(path, "/") + + // check if the file is to be excluded from bundle verification + if r.isFileExcluded(path) { + delete(r.files, path) + } else { + if err = r.verifyBundleFile(path, buf); err != nil { + return bundle, err + } + } + } + + // Normalize the paths to use `/` separators + path := filepath.ToSlash(f.Path()) + + if strings.HasSuffix(path, RegoExt) { + fullPath := r.fullPath(path) + bs := buf.Bytes() + + // Modules are parsed after we've had a chance to read the manifest + mf := ModuleFile{ + URL: f.URL(), + Path: fullPath, + RelativePath: path, + Raw: bs, + } + modules = append(modules, mf) + + if r.lazyLoadingMode { + p := fullPath + if r.name != "" { + p = modulePathWithPrefix(r.name, fullPath) + } + + raw = append(raw, Raw{Path: p, Value: bs, module: &mf}) + } + } else if filepath.Base(path) == WasmFile { + bundle.WasmModules = append(bundle.WasmModules, WasmModuleFile{ + URL: f.URL(), + Path: r.fullPath(path), + Raw: buf.Bytes(), + }) + } else if filepath.Base(path) == PlanFile { + bundle.PlanModules = append(bundle.PlanModules, PlanModuleFile{ + URL: f.URL(), + Path: r.fullPath(path), + Raw: buf.Bytes(), + }) + } else if filepath.Base(path) == dataFile { + if r.lazyLoadingMode { + raw = append(raw, Raw{Path: path, Value: buf.Bytes()}) + continue + } + + var value any + + r.metrics.Timer(metrics.RegoDataParse).Start() + err := util.UnmarshalJSON(buf.Bytes(), &value) + r.metrics.Timer(metrics.RegoDataParse).Stop() + + if err != nil { + return bundle, fmt.Errorf("bundle load failed on %v: %w", r.fullPath(path), err) + } + + if err := insertValue(&bundle, path, value); err != nil { + return bundle, err + } + + } else if filepath.Base(path) == yamlDataFile || filepath.Base(path) == ymlDataFile { + if r.lazyLoadingMode { + raw = append(raw, Raw{Path: path, Value: buf.Bytes()}) + continue + } + + var value any + + r.metrics.Timer(metrics.RegoDataParse).Start() + err := util.Unmarshal(buf.Bytes(), &value) + r.metrics.Timer(metrics.RegoDataParse).Stop() + + if err != nil { + return bundle, fmt.Errorf("bundle load failed on %v: %w", r.fullPath(path), err) + } + + if err := insertValue(&bundle, path, value); err != nil { + return bundle, err + } + + } else if strings.HasSuffix(path, ManifestExt) { + if err := util.NewJSONDecoder(&buf).Decode(&bundle.Manifest); err != nil { + return bundle, fmt.Errorf("bundle load failed on manifest decode: %w", err) + } + } + } + + // Parse modules + popts := r.ParserOptions() + popts.RegoVersion = bundle.RegoVersion(popts.EffectiveRegoVersion()) + for _, mf := range modules { + modulePopts := popts + if regoVersion, err := bundle.RegoVersionForFile(mf.RelativePath, popts.EffectiveRegoVersion()); err != nil { + return bundle, err + } else if regoVersion != ast.RegoUndefined { + // We don't expect ast.RegoUndefined here, but don't override configured rego-version if we do just to be extra protective + modulePopts.RegoVersion = regoVersion + } + r.metrics.Timer(metrics.RegoModuleParse).Start() + mf.Parsed, err = ast.ParseModuleWithOpts(mf.Path, util.ByteSliceToString(mf.Raw), modulePopts) + r.metrics.Timer(metrics.RegoModuleParse).Stop() + if err != nil { + return bundle, err + } + bundle.Modules = append(bundle.Modules, mf) + } + + if bundle.Type() == DeltaBundleType { + if len(bundle.Data) != 0 { + return bundle, errors.New("delta bundle expected to contain only patch file but data files found") + } + + if len(bundle.Modules) != 0 { + return bundle, errors.New("delta bundle expected to contain only patch file but policy files found") + } + + if len(bundle.WasmModules) != 0 { + return bundle, errors.New("delta bundle expected to contain only patch file but wasm files found") + } + + if r.persist { + return bundle, errors.New("'persist' property is true in config. persisting delta bundle to disk is not supported") + } + } + + // check if the bundle signatures specify any files that weren't found in the bundle + if bundle.Type() == SnapshotBundleType && len(r.files) != 0 { + extra := []string{} + for k := range r.files { + extra = append(extra, k) + } + return bundle, fmt.Errorf("file(s) %v specified in bundle signatures but not found in the target bundle", extra) + } + + if err := bundle.Manifest.validateAndInjectDefaults(bundle); err != nil { + return bundle, err + } + + // Inject the wasm module entrypoint refs into the WasmModuleFile structs + epMap := map[string][]string{} + for _, r := range bundle.Manifest.WasmResolvers { + epMap[r.Module] = append(epMap[r.Module], r.Entrypoint) + } + for i := range len(bundle.WasmModules) { + entrypoints := epMap[bundle.WasmModules[i].Path] + for _, entrypoint := range entrypoints { + ref, err := ast.PtrRef(ast.DefaultRootDocument, entrypoint) + if err != nil { + return bundle, fmt.Errorf("failed to parse wasm module entrypoint '%s': %s", entrypoint, err) + } + bundle.WasmModules[i].Entrypoints = append(bundle.WasmModules[i].Entrypoints, ref) + } + } + + if r.includeManifestInData { + var metadata map[string]any + + b, err := json.Marshal(&bundle.Manifest) + if err != nil { + return bundle, fmt.Errorf("bundle load failed on manifest marshal: %w", err) + } + + err = util.UnmarshalJSON(b, &metadata) + if err != nil { + return bundle, fmt.Errorf("bundle load failed on manifest unmarshal: %w", err) + } + + // For backwards compatibility always write to the old unnamed manifest path + // This will *not* be correct if >1 bundle is in use... + if err := bundle.insertData(legacyManifestStoragePath, metadata); err != nil { + return bundle, fmt.Errorf("bundle load failed on %v: %w", legacyRevisionStoragePath, err) + } + } + + bundle.Etag = r.etag + bundle.Raw = raw + + return bundle, nil +} + +func (r *Reader) isFileExcluded(path string) bool { + for _, e := range r.verificationConfig.Exclude { + match, _ := filepath.Match(e, path) + if match { + return true + } + } + return false +} + +func (r *Reader) checkSignaturesAndDescriptors(signatures SignaturesConfig) error { + if r.skipVerify { + return nil + } + + if signatures.isEmpty() && r.verificationConfig != nil && r.verificationConfig.KeyID != "" { + return errors.New("bundle missing .signatures.json file") + } + + if !signatures.isEmpty() { + if r.verificationConfig == nil { + return errors.New("verification key not provided") + } + + // verify the JWT signatures included in the `.signatures.json` file + if err := r.verifyBundleSignature(signatures); err != nil { + return err + } + } + return nil +} + +func (r *Reader) verifyBundleSignature(sc SignaturesConfig) error { + var err error + r.files, err = VerifyBundleSignature(sc, r.verificationConfig) + return err +} + +func (r *Reader) verifyBundleFile(path string, data bytes.Buffer) error { + return VerifyBundleFile(path, data, r.files) +} + +func (r *Reader) fullPath(path string) string { + if r.baseDir != "" { + path = filepath.Join(r.baseDir, path) + } + return path +} + +// Write is deprecated. Use NewWriter instead. +func Write(w io.Writer, bundle Bundle) error { + return NewWriter(w). + UseModulePath(true). + DisableFormat(true). + Write(bundle) +} + +// Writer implements bundle serialization. +type Writer struct { + usePath bool + disableFormat bool + w io.Writer +} + +// NewWriter returns a bundle writer that writes to w. +func NewWriter(w io.Writer) *Writer { + return &Writer{ + w: w, + } +} + +// UseModulePath configures the writer to use the module file path instead of the +// module file URL during serialization. This is for backwards compatibility. +func (w *Writer) UseModulePath(yes bool) *Writer { + w.usePath = yes + return w +} + +// DisableFormat configures the writer to just write out raw bytes instead +// of formatting modules before serialization. +func (w *Writer) DisableFormat(yes bool) *Writer { + w.disableFormat = yes + return w +} + +// Write writes the bundle to the writer's output stream. +func (w *Writer) Write(bundle Bundle) error { + gw := gzip.NewWriter(w.w) + tw := tar.NewWriter(gw) + + bundleType := bundle.Type() + + if bundleType == SnapshotBundleType { + var buf bytes.Buffer + + if err := json.NewEncoder(&buf).Encode(bundle.Data); err != nil { + return err + } + + if err := archive.WriteFile(tw, "data.json", buf.Bytes()); err != nil { + return err + } + + for _, module := range bundle.Modules { + path := module.URL + if w.usePath { + path = module.Path + } + + if err := archive.WriteFile(tw, path, module.Raw); err != nil { + return err + } + } + + if err := w.writeWasm(tw, bundle); err != nil { + return err + } + + if err := writeSignatures(tw, bundle); err != nil { + return err + } + + if err := w.writePlan(tw, bundle); err != nil { + return err + } + } else if bundleType == DeltaBundleType { + if err := writePatch(tw, bundle); err != nil { + return err + } + } + + if err := writeManifest(tw, bundle); err != nil { + return err + } + + if err := tw.Close(); err != nil { + return err + } + + return gw.Close() +} + +func (w *Writer) writeWasm(tw *tar.Writer, bundle Bundle) error { + for _, wm := range bundle.WasmModules { + path := wm.URL + if w.usePath { + path = wm.Path + } + + err := archive.WriteFile(tw, path, wm.Raw) + if err != nil { + return err + } + } + + if len(bundle.Wasm) > 0 { + err := archive.WriteFile(tw, "/"+WasmFile, bundle.Wasm) + if err != nil { + return err + } + } + + return nil +} + +func (w *Writer) writePlan(tw *tar.Writer, bundle Bundle) error { + for _, wm := range bundle.PlanModules { + path := wm.URL + if w.usePath { + path = wm.Path + } + + err := archive.WriteFile(tw, path, wm.Raw) + if err != nil { + return err + } + } + + return nil +} + +func writeManifest(tw *tar.Writer, bundle Bundle) error { + + if bundle.Manifest.Empty() { + return nil + } + + var buf bytes.Buffer + + if err := json.NewEncoder(&buf).Encode(bundle.Manifest); err != nil { + return err + } + + return archive.WriteFile(tw, ManifestExt, buf.Bytes()) +} + +func writePatch(tw *tar.Writer, bundle Bundle) error { + + var buf bytes.Buffer + + if err := json.NewEncoder(&buf).Encode(bundle.Patch); err != nil { + return err + } + + return archive.WriteFile(tw, patchFile, buf.Bytes()) +} + +func writeSignatures(tw *tar.Writer, bundle Bundle) error { + + if bundle.Signatures.isEmpty() { + return nil + } + + bs, err := json.MarshalIndent(bundle.Signatures, "", " ") + if err != nil { + return err + } + + return archive.WriteFile(tw, fmt.Sprintf(".%v", SignaturesFile), bs) +} + +func hashBundleFiles(hash SignatureHasher, b *Bundle) ([]FileInfo, error) { + + files := []FileInfo{} + + bs, err := hash.HashFile(b.Data) + if err != nil { + return files, err + } + files = append(files, NewFile(strings.TrimPrefix("data.json", "/"), hex.EncodeToString(bs), defaultHashingAlg)) + + if len(b.Wasm) != 0 { + bs, err := hash.HashFile(b.Wasm) + if err != nil { + return files, err + } + files = append(files, NewFile(strings.TrimPrefix(WasmFile, "/"), hex.EncodeToString(bs), defaultHashingAlg)) + } + + for _, wasmModule := range b.WasmModules { + bs, err := hash.HashFile(wasmModule.Raw) + if err != nil { + return files, err + } + files = append(files, NewFile(strings.TrimPrefix(wasmModule.Path, "/"), hex.EncodeToString(bs), defaultHashingAlg)) + } + + for _, planmodule := range b.PlanModules { + bs, err := hash.HashFile(planmodule.Raw) + if err != nil { + return files, err + } + files = append(files, NewFile(strings.TrimPrefix(planmodule.Path, "/"), hex.EncodeToString(bs), defaultHashingAlg)) + } + + // If the manifest is essentially empty, don't add it to the signatures since it + // won't be written to the bundle. Otherwise: + // parse the manifest into a JSON structure; + // then recursively order the fields of all objects alphabetically and then apply + // the hash function to result to compute the hash. + if !b.Manifest.Empty() { + mbs, err := json.Marshal(b.Manifest) + if err != nil { + return files, err + } + + var result map[string]any + if err := util.Unmarshal(mbs, &result); err != nil { + return files, err + } + + bs, err = hash.HashFile(result) + if err != nil { + return files, err + } + + files = append(files, NewFile(strings.TrimPrefix(ManifestExt, "/"), hex.EncodeToString(bs), defaultHashingAlg)) + } + + return files, err +} + +// FormatModules formats Rego modules +// Modules will be formatted to comply with [ast.DefaultRegoVersion], but Rego compatibility of individual parsed modules will be respected (e.g. if 'rego.v1' is imported). +func (b *Bundle) FormatModules(useModulePath bool) error { + return b.FormatModulesForRegoVersion(ast.DefaultRegoVersion, true, useModulePath) +} + +// FormatModulesForRegoVersion formats Rego modules to comply with a given Rego version +func (b *Bundle) FormatModulesForRegoVersion(version ast.RegoVersion, preserveModuleRegoVersion bool, useModulePath bool) error { + return b.FormatModulesWithOptions(BundleFormatOptions{ + RegoVersion: version, + PreserveModuleRegoVersion: preserveModuleRegoVersion, + UseModulePath: useModulePath, + }) +} + +type BundleFormatOptions struct { + RegoVersion ast.RegoVersion + Capabilities *ast.Capabilities + PreserveModuleRegoVersion bool + UseModulePath bool +} + +// FormatModulesWithOptions formats Rego modules with the given options. +func (b *Bundle) FormatModulesWithOptions(opts BundleFormatOptions) error { + var err error + + for i, module := range b.Modules { + fmtOpts := format.Opts{ + RegoVersion: opts.RegoVersion, + Capabilities: opts.Capabilities, + } + + if module.Parsed != nil { + fmtOpts.ParserOptions = &ast.ParserOptions{ + RegoVersion: module.Parsed.RegoVersion(), + } + if opts.PreserveModuleRegoVersion { + fmtOpts.RegoVersion = module.Parsed.RegoVersion() + } + } + + if fmtOpts.Capabilities == nil { + fmtOpts.Capabilities = ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(fmtOpts.RegoVersion)) + } + + if module.Raw == nil { + module.Raw, err = format.AstWithOpts(module.Parsed, fmtOpts) + if err != nil { + return err + } + } else { + p := module.URL + if opts.UseModulePath { + p = module.Path + } + + module.Raw, err = format.SourceWithOpts(p, module.Raw, fmtOpts) + if err != nil { + return err + } + } + b.Modules[i].Raw = module.Raw + } + return nil +} + +// GenerateSignature generates the signature for the given bundle. +func (b *Bundle) GenerateSignature(signingConfig *SigningConfig, keyID string, useModulePath bool) error { + + hash, err := NewSignatureHasher(HashingAlgorithm(defaultHashingAlg)) + if err != nil { + return err + } + + files := []FileInfo{} + + for _, module := range b.Modules { + bytes, err := hash.HashFile(module.Raw) + if err != nil { + return err + } + + path := module.URL + if useModulePath { + path = module.Path + } + files = append(files, NewFile(strings.TrimPrefix(path, "/"), hex.EncodeToString(bytes), defaultHashingAlg)) + } + + result, err := hashBundleFiles(hash, b) + if err != nil { + return err + } + files = append(files, result...) + + // generate signed token + token, err := GenerateSignedToken(files, signingConfig, keyID) + if err != nil { + return err + } + + if b.Signatures.isEmpty() { + b.Signatures = SignaturesConfig{} + } + + if signingConfig.Plugin != "" { + b.Signatures.Plugin = signingConfig.Plugin + } + + b.Signatures.Signatures = []string{token} + + return nil +} + +// ParsedModules returns a map of parsed modules with names that are +// unique and human readable for the given a bundle name. +func (b *Bundle) ParsedModules(bundleName string) map[string]*ast.Module { + + mods := make(map[string]*ast.Module, len(b.Modules)) + + for _, mf := range b.Modules { + mods[modulePathWithPrefix(bundleName, mf.Path)] = mf.Parsed + } + + return mods +} + +func (b *Bundle) RegoVersion(def ast.RegoVersion) ast.RegoVersion { + if v := b.Manifest.RegoVersion; v != nil { + if *v == 0 { + return ast.RegoV0 + } else if *v == 1 { + return ast.RegoV1 + } + } + return def +} + +func (b *Bundle) SetRegoVersion(v ast.RegoVersion) { + b.Manifest.SetRegoVersion(v) +} + +// RegoVersionForFile returns the rego-version for the specified file path. +// 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) { + version, err := b.Manifest.numericRegoVersionForFile(path) + if err != nil { + return def, err + } else if version == nil { + return def, nil + } else if *version == 0 { + return ast.RegoV0, nil + } else if *version == 1 { + return ast.RegoV1, nil + } + return def, fmt.Errorf("unknown bundle rego-version %d for file '%s'", *version, path) +} + +func (m *Manifest) RegoVersionForFile(path string) (ast.RegoVersion, error) { + v, err := m.numericRegoVersionForFile(path) + if err != nil { + return ast.RegoUndefined, err + } + + if v == nil { + return ast.RegoUndefined, nil + } + + return ast.RegoVersionFromInt(*v), nil +} + +func (m *Manifest) numericRegoVersionForFile(path string) (*int, error) { + var version *int + + if len(m.FileRegoVersions) != len(m.compiledFileRegoVersions) { + m.compiledFileRegoVersions = make([]fileRegoVersion, 0, len(m.FileRegoVersions)) + for pattern, v := range m.FileRegoVersions { + compiled, err := glob.Compile(pattern) + if err != nil { + return nil, fmt.Errorf("failed to compile glob pattern %s: %s", pattern, err) + } + m.compiledFileRegoVersions = append(m.compiledFileRegoVersions, fileRegoVersion{compiled, v}) + } + } + + for _, fv := range m.compiledFileRegoVersions { + if fv.path.Match(path) { + version = &fv.version + break + } + } + + if version == nil { + version = m.RegoVersion + } + return version, nil +} + +// Equal returns true if this bundle's contents equal the other bundle's +// contents. +func (b Bundle) Equal(other Bundle) bool { + if !reflect.DeepEqual(b.Data, other.Data) { + return false + } + + if len(b.Modules) != len(other.Modules) { + return false + } + for i := range b.Modules { + // To support bundles built from rootless filesystems we ignore a "/" prefix + // for URLs and Paths, such that "/file" and "file" are equivalent + if strings.TrimPrefix(b.Modules[i].URL, string(filepath.Separator)) != + strings.TrimPrefix(other.Modules[i].URL, string(filepath.Separator)) { + return false + } + if strings.TrimPrefix(b.Modules[i].Path, string(filepath.Separator)) != + strings.TrimPrefix(other.Modules[i].Path, string(filepath.Separator)) { + return false + } + if !b.Modules[i].Parsed.Equal(other.Modules[i].Parsed) { + return false + } + if !bytes.Equal(b.Modules[i].Raw, other.Modules[i].Raw) { + return false + } + } + if (b.Wasm == nil && other.Wasm != nil) || (b.Wasm != nil && other.Wasm == nil) { + return false + } + + return bytes.Equal(b.Wasm, other.Wasm) +} + +// Copy returns a deep copy of the bundle. +func (b Bundle) Copy() Bundle { + + // Copy data. + var x any = b.Data + + if err := util.RoundTrip(&x); err != nil { + panic(err) + } + + if x != nil { + b.Data = x.(map[string]any) + } + + // Copy modules. + for i := range b.Modules { + bs := make([]byte, len(b.Modules[i].Raw)) + copy(bs, b.Modules[i].Raw) + b.Modules[i].Raw = bs + b.Modules[i].Parsed = b.Modules[i].Parsed.Copy() + } + + // Copy manifest. + b.Manifest = b.Manifest.Copy() + + return b +} + +func (b *Bundle) insertData(key []string, value any) error { + // Build an object with the full structure for the value + obj, err := mktree(key, value) + if err != nil { + return err + } + + // Merge the new data in with the current bundle data object + merged, ok := merge.InterfaceMaps(b.Data, obj) + if !ok { + return fmt.Errorf("failed to insert data file from path %s", filepath.Join(key...)) + } + + b.Data = merged + + return nil +} + +func (b *Bundle) readData(key []string) *any { + + if len(key) == 0 { + if len(b.Data) == 0 { + return nil + } + var result any = b.Data + return &result + } + + node := b.Data + + for i := range len(key) - 1 { + + child, ok := node[key[i]] + if !ok { + return nil + } + + childObj, ok := child.(map[string]any) + if !ok { + return nil + } + + node = childObj + } + + child, ok := node[key[len(key)-1]] + if !ok { + return nil + } + + return &child +} + +// Type returns the type of the bundle. +func (b *Bundle) Type() string { + if len(b.Patch.Data) != 0 { + return DeltaBundleType + } + return SnapshotBundleType +} + +func mktree(path []string, value any) (map[string]any, error) { + if len(path) == 0 { + // For 0 length path the value is the full tree. + obj, ok := value.(map[string]any) + if !ok { + return nil, errors.New("root value must be object") + } + return obj, nil + } + + dir := map[string]any{} + for i := len(path) - 1; i > 0; i-- { + dir[path[i]] = value + value = dir + dir = map[string]any{} + } + dir[path[0]] = value + + return dir, nil +} + +// Merge accepts a set of bundles and merges them into a single result bundle. If there are +// any conflicts during the merge (e.g., with roots) an error is returned. The result bundle +// will have an empty revision except in the special case where a single bundle is provided +// (and in that case the bundle is just returned unmodified.) +func Merge(bundles []*Bundle) (*Bundle, error) { + return MergeWithRegoVersion(bundles, ast.DefaultRegoVersion, false) +} + +// MergeWithRegoVersion creates a merged bundle from the provided bundles, similar to Merge. +// If more than one bundle is provided, the rego version of the result bundle is set to the provided regoVersion. +// Any Rego files in a bundle of conflicting rego version will be marked in the result's manifest with the rego version +// of its original bundle. If the Rego file already had an overriding rego version, it will be preserved. +// If a single bundle is provided, it will retain any rego version information it already had. If it has none, the +// provided regoVersion will be applied to it. +// If usePath is true, per-file rego-versions will be calculated using the file's ModuleFile.Path; otherwise, the file's +// ModuleFile.URL will be used. +func MergeWithRegoVersion(bundles []*Bundle, regoVersion ast.RegoVersion, usePath bool) (*Bundle, error) { + + if len(bundles) == 0 { + return nil, errors.New("expected at least one bundle") + } + + if regoVersion == ast.RegoUndefined { + regoVersion = ast.DefaultRegoVersion + } + + if len(bundles) == 1 { + result := bundles[0] + // We respect the bundle rego-version, defaulting to the provided rego version if not set. + result.SetRegoVersion(result.RegoVersion(regoVersion)) + fileRegoVersions, err := bundleRegoVersions(result, result.RegoVersion(regoVersion), usePath) + if err != nil { + return nil, err + } + result.Manifest.FileRegoVersions = fileRegoVersions + return result, nil + } + + var roots []string + var result Bundle + + for _, b := range bundles { + + if b.Manifest.Roots == nil { + return nil, errors.New("bundle manifest not initialized") + } + + roots = append(roots, *b.Manifest.Roots...) + + result.Modules = append(result.Modules, b.Modules...) + + for _, root := range *b.Manifest.Roots { + key := strings.Split(root, "/") + if val := b.readData(key); val != nil { + if err := result.insertData(key, *val); err != nil { + return nil, err + } + } + } + + result.Manifest.WasmResolvers = append(result.Manifest.WasmResolvers, b.Manifest.WasmResolvers...) + result.WasmModules = append(result.WasmModules, b.WasmModules...) + result.PlanModules = append(result.PlanModules, b.PlanModules...) + + if b.Manifest.RegoVersion != nil || len(b.Manifest.FileRegoVersions) > 0 { + if result.Manifest.FileRegoVersions == nil { + result.Manifest.FileRegoVersions = map[string]int{} + } + + fileRegoVersions, err := bundleRegoVersions(b, regoVersion, usePath) + if err != nil { + return nil, err + } + maps.Copy(result.Manifest.FileRegoVersions, fileRegoVersions) + } + } + + // We respect the bundle rego-version, defaulting to the provided rego version if not set. + result.SetRegoVersion(result.RegoVersion(regoVersion)) + + if result.Data == nil { + result.Data = map[string]any{} + } + + result.Manifest.Roots = &roots + + if err := result.Manifest.validateAndInjectDefaults(result); err != nil { + return nil, err + } + + return &result, nil +} + +func bundleRegoVersions(bundle *Bundle, regoVersion ast.RegoVersion, usePath bool) (map[string]int, error) { + fileRegoVersions := map[string]int{} + + // we drop the bundle-global rego versions and record individual rego versions for each module. + for _, m := range bundle.Modules { + // We fetch rego-version by the path relative to the bundle root, as the complete path of the module might + // contain the path between OPA working directory and the bundle root. + v, err := bundle.RegoVersionForFile(bundleRelativePath(m, usePath), bundle.RegoVersion(regoVersion)) + if err != nil { + return nil, err + } + + // only record the rego version if it's different from the one applied globally to the result bundle + if v != ast.RegoUndefined { + if regoVersion == ast.RegoUndefined { + // We store the rego version by the absolute path to the bundle root, as this will be the - possibly new - path + // to the module inside the merged bundle. + fileRegoVersions[bundleAbsolutePath(m, usePath)] = v.Int() + } else { + vInt := v.Int() + gVInt := regoVersion.Int() + if vInt != gVInt { + fileRegoVersions[bundleAbsolutePath(m, usePath)] = vInt + } + } + } + } + + return fileRegoVersions, nil +} + +func bundleRelativePath(m ModuleFile, usePath bool) string { + p := m.RelativePath + if p == "" { + if usePath { + p = m.Path + } else { + p = m.URL + } + } + return p +} + +func bundleAbsolutePath(m ModuleFile, usePath bool) string { + var p string + if usePath { + p = m.Path + } else { + p = m.URL + } + if !path.IsAbs(p) { + p = "/" + p + } + return path.Clean(p) +} + +// RootPathsOverlap takes in two bundle root paths and returns true if they overlap. +func RootPathsOverlap(pathA string, pathB string) bool { + a := rootPathSegments(pathA) + b := rootPathSegments(pathB) + return rootContains(a, b) || rootContains(b, a) +} + +// RootPathsContain takes a set of bundle root paths and returns true if the path is contained. +func RootPathsContain(roots []string, path string) bool { + segments := rootPathSegments(path) + for i := range roots { + if rootContains(rootPathSegments(roots[i]), segments) { + return true + } + } + return false +} + +func rootPathSegments(path string) []string { + return strings.Split(path, "/") +} + +func rootContains(root []string, other []string) bool { + + // A single segment, empty string root always contains the other. + if len(root) == 1 && root[0] == "" { + return true + } + + if len(root) > len(other) { + return false + } + + for j := range root { + if root[j] != other[j] { + return false + } + } + + return true +} + +func insertValue(b *Bundle, path string, value any) error { + if err := b.insertData(getNormalizedPath(path), value); err != nil { + return fmt.Errorf("bundle load failed on %v: %w", path, err) + } + return nil +} + +func getNormalizedPath(path string) []string { + // Remove leading / and . characters from the directory path. If the bundle + // was written with OPA then the paths will contain a leading slash. On the + // other hand, if the path is empty, filepath.Dir will return '.'. + // Note: filepath.Dir can return paths with '\' separators, always use + // filepath.ToSlash to keep them normalized. + dirpath := strings.TrimLeft(normalizePath(filepath.Dir(path)), "/.") + var key []string + if dirpath != "" { + key = strings.Split(dirpath, "/") + } + return key +} + +func dfs(value any, path string, fn func(string, any) (bool, error)) error { + if stop, err := fn(path, value); err != nil { + return err + } else if stop { + return nil + } + obj, ok := value.(map[string]any) + if !ok { + return nil + } + for key := range obj { + if err := dfs(obj[key], path+"/"+key, fn); err != nil { + return err + } + } + return nil +} + +func modulePathWithPrefix(bundleName string, modulePath string) string { + // Default prefix is just the bundle name + prefix := bundleName + + // Bundle names are sometimes just file paths, some of which + // are full urls (file:///foo/). Parse these and only use the path. + parsed, err := url.Parse(bundleName) + if err == nil { + prefix = filepath.Join(parsed.Host, parsed.Path) + } + + // Note: filepath.Join can return paths with '\' separators, always use + // filepath.ToSlash to keep them normalized. + return normalizePath(filepath.Join(prefix, modulePath)) +} + +// IsStructuredDoc checks if the file name equals a structured file extension ex. ".json" +func IsStructuredDoc(name string) bool { + return filepath.Base(name) == dataFile || filepath.Base(name) == yamlDataFile || + filepath.Base(name) == SignaturesFile || filepath.Base(name) == ManifestExt +} + +func preProcessBundle(loader DirectoryLoader, skipVerify bool, sizeLimitBytes int64) (SignaturesConfig, Patch, []*Descriptor, error) { + descriptors := []*Descriptor{} + var signatures SignaturesConfig + var patch Patch + + for { + f, err := loader.NextFile() + if err == io.EOF { + break + } + + if err != nil { + return signatures, patch, nil, fmt.Errorf("bundle read failed: %w", err) + } + + // check for the signatures file + if !skipVerify && strings.HasSuffix(f.Path(), SignaturesFile) { + buf, err := readFile(f, sizeLimitBytes) + if err != nil { + return signatures, patch, nil, err + } + + if err := util.NewJSONDecoder(&buf).Decode(&signatures); err != nil { + return signatures, patch, nil, fmt.Errorf("bundle load failed on signatures decode: %w", err) + } + } else if !strings.HasSuffix(f.Path(), SignaturesFile) { + descriptors = append(descriptors, f) + + if filepath.Base(f.Path()) == patchFile { + + var b bytes.Buffer + tee := io.TeeReader(f.reader, &b) + f.reader = tee + + buf, err := readFile(f, sizeLimitBytes) + if err != nil { + return signatures, patch, nil, err + } + + if err := util.NewJSONDecoder(&buf).Decode(&patch); err != nil { + return signatures, patch, nil, fmt.Errorf("bundle load failed on patch decode: %w", err) + } + + f.reader = &b + } + } + } + return signatures, patch, descriptors, nil +} + +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 + + if int64(bb.Len()) >= sizeLimitBytes { + return *bb, fmt.Errorf("bundle file '%v' size (%d bytes) exceeded max size (%v bytes)", + strings.TrimPrefix(f.Path(), "/"), bb.Len(), sizeLimitBytes-1) + } + + 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 + + if err != nil && err != io.EOF { + return buf, err + } else if err == nil && n >= sizeLimitBytes { + return buf, fmt.Errorf(maxSizeLimitBytesErrMsg, strings.TrimPrefix(f.Path(), "/"), n, sizeLimitBytes-1) + } + + 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/third_party/opa/v1/bundle/bundle_ext_test.go b/third_party/opa/v1/bundle/bundle_ext_test.go new file mode 100644 index 000000000000..198ff606e542 --- /dev/null +++ b/third_party/opa/v1/bundle/bundle_ext_test.go @@ -0,0 +1,181 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. +package bundle_test + +import ( + "context" + "maps" + "slices" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/disk" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/util/test" +) + +type customBundleActivator struct { + activator *bundle.DefaultActivator + BundleNames []string + Files map[string]map[string][]byte +} + +func (cba *customBundleActivator) Activate(opts *bundle.ActivateOpts) error { + cba.activator = &bundle.DefaultActivator{} + cba.BundleNames = slices.Collect(maps.Keys(opts.Bundles)) + if cba.Files == nil { + cba.Files = make(map[string]map[string][]byte, len(cba.BundleNames)) + } + for k, v := range opts.Bundles { + cba.Files[k] = make(map[string][]byte, len(v.Raw)) + for _, r := range v.Raw { + cba.Files[k][r.Path] = r.Value + } + } + return cba.activator.Activate(opts) +} + +// Warning: This test modifies package variables, and as +// a result, cannot be run in parallel with other tests. +func TestRegisterBundleActivatorWithStore(t *testing.T) { + getInmemStore := func() storage.Store { + return inmem.NewFromObject(map[string]any{}) + } + + // Top-level variables, shared between tests. + // These are only needed to make disk storage tests use an external dir name and context value. + var dir string + var ctx context.Context + getDiskStore := func() storage.Store { + s, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{Dir: dir}) + if err != nil { + t.Fatal(err) + } + return s + } + + buf := archive.MustWriteTarGz([][2]string{ + {"/.manifest", `{"revision": "abcd"}`}, + {"/data.json", `{"a": 1}`}, + {"/x.rego", `package foo`}, + }) + b, err := bundle.NewReader(buf).IncludeManifestInData(true).Read() + if err != nil { + t.Fatal(err) + } + + tests := []struct { + note string + activator bundle.Activator + storeFunc func() storage.Store + disk bool + }{ + { + note: "package init default activator, default store", + }, + { + note: "default activator, default store", + activator: &bundle.DefaultActivator{}, + }, + { + note: "default activator, inmem store", + activator: &bundle.DefaultActivator{}, + storeFunc: getInmemStore, + }, + { + note: "custom activator, inmem store", + activator: &customBundleActivator{}, + storeFunc: getInmemStore, + }, + { + note: "package init default activator, disk store", + storeFunc: getDiskStore, + disk: true, + }, + { + note: "default activator, disk store", + activator: &bundle.DefaultActivator{}, + storeFunc: getDiskStore, + disk: true, + }, + { + note: "custom activator, disk store", + activator: &customBundleActivator{}, + storeFunc: getDiskStore, + disk: true, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var store storage.Store + ctx = context.Background() + + // Plumb in the bundle store if func provided. + if tc.storeFunc != nil { + bundle.RegisterStoreFunc(tc.storeFunc) + // Create temp folder when using disk storage. + if tc.disk { + rootDir, cleanup, err := test.MakeTempFS("", "opa_test", make(map[string]string)) + dir = rootDir // Set top-level var for the storage function to pick up. + if err != nil { + panic(err) + } + defer cleanup() + } + store = bundle.BundleExtStore() + } else { + store = getInmemStore() // The default store. + } + + // Register our custom bundle activator if provided. + if tc.activator != nil { + bundle.RegisterActivator("example-activator", tc.activator) + bundle.RegisterDefaultBundleActivator("example-activator") + } + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + bundles := map[string]*bundle.Bundle{"example": &b} + opts := &bundle.ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: ast.NewCompiler(), + Metrics: metrics.New(), + Bundles: bundles, + } + if tc.activator != nil { + opts.Plugin = "example-activator" + } + + if err := bundle.Activate(opts); err != nil { + t.Fatal(err) + } + + // If using the custom bundle activator, inspect contents. + if cba, ok := tc.activator.(*customBundleActivator); ok { + expNames := []string{"example"} + actNames := cba.BundleNames + if slices.Compare(expNames, actNames) != 0 { + t.Fatalf("wrong bundle names. expected: %v, got: %v", expNames, actNames) + } + for k, v := range bundles { + actFilenames := slices.Collect(maps.Keys(cba.Files[k])) + expFilenames := make([]string, len(v.Raw)) + for _, r := range v.Raw { + expFilenames = append(expFilenames, r.Path) + } + if slices.Compare(expFilenames, actFilenames) != 0 { + t.Fatalf("wrong bundle file names. expected: %v, got: %v", expFilenames, actFilenames) + } + } + } + }) + } +} diff --git a/third_party/opa/v1/bundle/bundle_test.go b/third_party/opa/v1/bundle/bundle_test.go new file mode 100644 index 000000000000..7b9fb5ca2e01 --- /dev/null +++ b/third_party/opa/v1/bundle/bundle_test.go @@ -0,0 +1,2114 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: goconst // string duplication is for test readability. +package bundle + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "encoding/json" + "errors" + "fmt" + "io" + "path/filepath" + "reflect" + "strings" + "testing" + "testing/fstest" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestManifestAddRoot(t *testing.T) { + m := Manifest{Roots: &[]string{}} + m.AddRoot("x/y") + m.AddRoot("y/z") + exp, act := stringSet{"x/y": struct{}{}, "y/z": struct{}{}}, m.rootSet() + if !act.Equal(exp) { + t.Fatalf("expected roots to be %v, got %v", exp, act) + } +} + +func TestManifestEqual(t *testing.T) { + var m Manifest + var n Manifest + + assertEqual := func() { + t.Helper() + if !m.Equal(n) { + t.Fatal("expected manifests to be equal") + } + } + + assertNotEqual := func() { + t.Helper() + if m.Equal(n) { + t.Fatal("expected manifests to be different") + } + } + + assertEqual() + + n.Revision = "xxx" + assertNotEqual() + + m.Revision = "xxx" + assertEqual() + + n.WasmResolvers = append(n.WasmResolvers, WasmResolver{}) + assertNotEqual() + + m.WasmResolvers = append(m.WasmResolvers, WasmResolver{}) + assertEqual() + + n.WasmResolvers[0].Module = "yyy" + assertNotEqual() + + m.WasmResolvers[0].Module = "yyy" + assertEqual() + + n.Metadata = map[string]any{ + "foo": "bar", + } + assertNotEqual() + + m.Metadata = map[string]any{ + "foo": "bar", + } + assertEqual() + + // rego-version + + n.RegoVersion = pointTo(1) + assertNotEqual() + + m.RegoVersion = pointTo(0) + assertNotEqual() + + m.RegoVersion = pointTo(1) + assertEqual() + + n.FileRegoVersions = map[string]int{ + "foo": 1, + } + assertNotEqual() + + m.FileRegoVersions = map[string]int{ + "foo": 1, + } + assertEqual() + + n.FileRegoVersions["*/bar"] = 0 + assertNotEqual() + + m.FileRegoVersions["*/bar"] = 0 + assertEqual() +} + +func TestBundleRegoVersion(t *testing.T) { + b := Bundle{} + + if b.Manifest.RegoVersion != nil { + t.Fatal("expected nil") + } + + // No rego-version set, expect default + if b.RegoVersion(ast.RegoV0) != ast.RegoV0 { + t.Fatal("expected v0") + } + if b.RegoVersion(ast.RegoV1) != ast.RegoV1 { + t.Fatal("expected v1") + } + + // Set rego-version to v0 + b.SetRegoVersion(ast.RegoV0) + + if b.Manifest.RegoVersion == nil || *b.Manifest.RegoVersion != 0 { + t.Fatal("expected v0") + } + + if b.RegoVersion(ast.RegoV1) != ast.RegoV0 { + t.Fatal("expected v0") + } + + // Set rego-version to v1 + b.SetRegoVersion(ast.RegoV1) + + if b.Manifest.RegoVersion == nil || *b.Manifest.RegoVersion != 1 { + t.Fatal("expected v1") + } + + if b.RegoVersion(ast.RegoV0) != ast.RegoV1 { + t.Fatal("expected v1") + } + + // Set rego-version to v0-compat1 + b.SetRegoVersion(ast.RegoV0CompatV1) + + if b.Manifest.RegoVersion == nil || *b.Manifest.RegoVersion != 0 { + t.Fatal("expected v0") + } + + if b.RegoVersion(ast.RegoV1) != ast.RegoV0 { + t.Fatal("expected v0") + } +} + +func TestRead(t *testing.T) { + for _, useMemoryFS := range []bool{false, true} { + testReadBundle(t, "", useMemoryFS) + } +} + +func TestReadWithBaseDir(t *testing.T) { + for _, useMemoryFS := range []bool{false, true} { + testReadBundle(t, "/foo/bar", useMemoryFS) + } +} + +func TestRead_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0", + module: `package example + +p[x] { + x := "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "rego.v1 import", + module: `package example +import rego.v1 + +p contains x if { + x := "a" +}`, + }, + { + note: "v1", // v1 is the default rego-version + module: `package example + +p contains x if { + x := "a" +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + module := tc.module + files := [][2]string{ + {"test.rego", module}, + } + + buf := archive.MustWriteTarGz(files) + loader := NewTarballLoaderWithBaseURL(buf, "") + br := NewCustomReader(loader) + + bundle, err := br.Read() + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error(s):\n\n%v\n\nbut got nil", tc.expErrs) + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if len(bundle.Modules) != 1 { + t.Fatalf("expected 1 module but got %d", len(bundle.Modules)) + } + } + }) + } +} + +func TestReadWithSizeLimit(t *testing.T) { + + buf := archive.MustWriteTarGz([][2]string{ + {"data.json", `"foo"`}, + }) + + loader := NewTarballLoaderWithBaseURL(buf, "") + br := NewCustomReader(loader).WithSizeLimitBytes(4) + + _, err := br.Read() + if err == nil || err.Error() != "bundle file 'data.json' size (5 bytes) exceeded max size (4 bytes)" { + t.Fatal("expected error but got:", err) + } + + buf = archive.MustWriteTarGz([][2]string{ + {".signatures.json", `"foo"`}, + }) + + loader = NewTarballLoaderWithBaseURL(buf, "") + br = NewCustomReader(loader).WithSizeLimitBytes(4) + + _, err = br.Read() + if err == nil || err.Error() != "bundle file '.signatures.json' size (5 bytes) exceeded max size (4 bytes)" { + t.Fatal("expected error but got:", err) + } +} + +func TestReadBundleInLazyMode(t *testing.T) { + files := [][2]string{ + {"/a/b/c/data.json", "[1,2,3]"}, + {"/a/b/d/data.json", "true"}, + {"/a/b/y/data.yaml", `foo: 1`}, + {"/example/example.rego", `package example`}, + {"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`}, + {"/.manifest", `{"revision": "foo", "roots": ["example"]}`}, // data is outside roots but validation skipped in lazy mode + } + + buf := archive.MustWriteTarGz(files) + loader := NewTarballLoaderWithBaseURL(buf, "") + br := NewCustomReader(loader).WithLazyLoadingMode(true) + + bundle, err := br.Read() + if err != nil { + t.Fatal(err) + } + + if len(bundle.Data) != 0 { + t.Fatal("expected the bundle object to contain no data") + } + + if len(bundle.Raw) == 0 { + t.Fatal("raw bundle bytes not set on bundle object") + } +} + +func TestReadWithBundleEtag(t *testing.T) { + + files := [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + } + + buf := archive.MustWriteTarGz(files) + bundle, err := NewReader(buf).WithBundleEtag("foo").Read() + if err != nil { + t.Fatal(err) + } + + if bundle.Etag != "foo" { + t.Fatalf("Expected bundle etag foo but got %v\n", bundle.Etag) + } +} + +func testReadBundle(t *testing.T, baseDir string, useMemoryFS bool) { + module := `package example` + if useMemoryFS && baseDir == "" { + baseDir = "." + } + + modulePath := "/example/example.rego" + if baseDir != "" { + modulePath = filepath.Join(baseDir, modulePath) + } + + legacyWasmModulePath := "/policy.wasm" + if baseDir != "" { + legacyWasmModulePath = filepath.Join(baseDir, legacyWasmModulePath) + } + + wasmResolverPath := "/authz/allow/policy.wasm" + fullWasmResolverPath := wasmResolverPath + if baseDir != "" { + fullWasmResolverPath = filepath.Join(baseDir, wasmResolverPath) + } + + files := [][2]string{ + {"/a/b/c/data.json", "[1,2,3]"}, + {"/a/b/d/data.json", "true"}, + {"/a/b/y/data.yaml", `foo: 1`}, + {"/a/b/g/data.yml", "1"}, + {"/example/example.rego", `package example`}, + {"/policy.wasm", `legacy-wasm-module`}, + {wasmResolverPath, `wasm-module`}, + {"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`}, + {"/.manifest", fmt.Sprintf(`{"wasm":[{"entrypoint": "authz/allow", "module": "%s"}]}`, fullWasmResolverPath)}, + } + + buf := archive.MustWriteTarGz(files) + var loader DirectoryLoader + if useMemoryFS { + fsys := make(fstest.MapFS, 1) + fsys["test.tar"] = &fstest.MapFile{Data: buf.Bytes()} + fh, err := fsys.Open("test.tar") + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + loader = NewTarballLoaderWithBaseURL(fh, baseDir) + } else { + loader = NewTarballLoaderWithBaseURL(buf, baseDir) + } + br := NewCustomReader(loader).WithBaseDir(baseDir) + + bundle, err := br.Read() + if err != nil { + t.Fatal(err) + } + + expManifest := Manifest{} + expManifest.Init() + expManifest.WasmResolvers = []WasmResolver{ + { + Entrypoint: "authz/allow", + Module: fullWasmResolverPath, + }, + } + + exp := Bundle{ + Manifest: expManifest, + Data: map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "c": []any{json.Number("1"), json.Number("2"), json.Number("3")}, + "d": true, + "g": json.Number("1"), + "y": map[string]any{ + "foo": json.Number("1"), + }, + "z": true, + }, + }, + "x": map[string]any{ + "y": true, + }, + }, + Modules: []ModuleFile{ + { + URL: modulePath, + Path: modulePath, + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + WasmModules: []WasmModuleFile{ + { + URL: legacyWasmModulePath, + Path: legacyWasmModulePath, + Raw: []byte(`legacy-wasm-module`), + }, + { + URL: fullWasmResolverPath, + Path: fullWasmResolverPath, + Raw: []byte("wasm-module"), + Entrypoints: []ast.Ref{ast.MustParseRef("data.authz.allow")}, + }, + }, + } + + if !exp.Equal(bundle) { + t.Fatalf("\nExp: %+v\nGot: %+v", exp, bundle) + } +} + +func TestReadWithManifest(t *testing.T) { + files := [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + } + buf := archive.MustWriteTarGz(files) + bundle, err := NewReader(buf).Read() + if err != nil { + t.Fatal(err) + } + if bundle.Manifest.Revision != "quickbrownfaux" { + t.Fatalf("Unexpected manifest.revision value: %v", bundle.Manifest.Revision) + } +} + +func TestManifestMetadata(t *testing.T) { + files := [][2]string{ + {"/.manifest", `{ + "metadata": { + "foo": { + "version": "1.0.0" + } + } + }`}, + } + buf := archive.MustWriteTarGz(files) + bundle, err := NewReader(buf).Read() + if err != nil { + t.Fatal(err) + } + if bundle.Manifest.Metadata["foo"] == nil { + t.Fatal("Unexpected nil metadata key") + } + data, ok := bundle.Manifest.Metadata["foo"].(map[string]any) + if !ok { + t.Fatal("Unexpected structure in metadata") + } + if data["version"] != "1.0.0" { + t.Fatalf("Unexpected metadata value: %v", data["version"]) + } +} + +func TestReadWithManifestInData(t *testing.T) { + files := [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + } + buf := archive.MustWriteTarGz(files) + bundle, err := NewReader(buf).IncludeManifestInData(true).Read() + if err != nil { + t.Fatal(err) + } + + system := bundle.Data["system"].(map[string]any) + b := system["bundle"].(map[string]any) + m := b["manifest"].(map[string]any) + + if m["revision"] != "quickbrownfaux" { + t.Fatalf("Unexpected manifest.revision value: %v. Expected: %v", m["revision"], "quickbrownfaux") + } +} + +func TestReadWithSignaturesSkipVerify(t *testing.T) { + signedBadTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaWxlcyI6W3sibmFtZSI6Ii5tYW5pZmVzdCIsImhhc2giOiI2MDdhMmMzOGExNDQxZGI1OGQyY2I4Nzk4MmM0MmFhOTFhNDM0MmVmNDIyYTZiNTQyZWRkZWJlZWY2ZjA0MTJmIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9LHsibmFtZSI6ImEvYi9jL2RhdGEuanNvbiIsImhhc2giOiI0MmNmZTY3NjhiNTdiYjVmNzUwM2MxNjVjMjhkZDA3YWM1YjgxMzU1NGViYzg1MGYyY2MzNTg0M2U3MTM3YjFkIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9LHsibmFtZSI6Imh0dHAvcG9saWN5L3BvbGljeS5yZWdvIiwiaGFzaCI6ImE2MTVlZWFlZTIxZGU1MTc5ZGUwODBkZThjMzA1MmM4ZGE5MDExMzg0MDZiYTcxYzM4YzAzMjg0NWY3ZDU0ZjQiLCJhbGdvcml0aG0iOiJTSEEtMjU2In1dLCJpYXQiOjE1OTIyNDgwMjcsImlzcyI6IkpXVFNlcnZpY2UiLCJrZXlpZCI6ImZvbyIsInNjb3BlIjoid3JpdGUifQ.sQTuw9tBp6DvvQG-MXSxTzJA3hSnKYxjX5fnxiR22JA` + + files := [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + {"/.signatures.json", fmt.Sprintf(`{"signatures": ["%v"]}`, signedBadTokenHS256)}, + {"/a/b/c/data.json", "[1,2,3]"}, + {"/http/policy/policy.rego", `package example`}, + } + + vc := NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", nil) + + buf := archive.MustWriteTarGz(files) + + loader := NewTarballLoaderWithBaseURL(buf, "/foo/bar") + reader := NewCustomReader(loader).WithBaseDir("/foo/bar").WithBundleVerificationConfig(vc).WithSkipBundleVerification(true) + _, err := reader.Read() + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +func TestReadWithSignatures(t *testing.T) { + + signedTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJmaWxlcyI6W3sibmFtZSI6Ii5tYW5pZmVzdCIsImhhc2giOiI1MDdhMmMzOGExNDQxZGI1OGQyY2I4Nzk4MmM0MmFhOTFhNDM0MmVmNDIyYTZiNTQyZWRkZWJlZWY2ZjA0MTJmIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9LHsibmFtZSI6ImEvYi9jL2RhdGEuanNvbiIsImhhc2giOiI0MmNmZTY3NjhiNTdiYjVmNzUwM2MxNjVjMjhkZDA3YWM1YjgxMzU1NGViYzg1MGYyY2MzNTg0M2U3MTM3YjFkIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9LHsibmFtZSI6Imh0dHAvcG9saWN5L3BvbGljeS5yZWdvIiwiaGFzaCI6ImE2MTVlZWFlZTIxZGU1MTc5ZGUwODBkZThjMzA1MmM4ZGE5MDExMzg0MDZiYTcxYzM4YzAzMjg0NWY3ZDU0ZjQiLCJhbGdvcml0aG0iOiJTSEEtMjU2In1dLCJpYXQiOjE1OTIyNDgwMjcsImlzcyI6IkpXVFNlcnZpY2UiLCJrZXlpZCI6ImZvbyIsInNjb3BlIjoid3JpdGUifQ.grzWHYvyVS6LfWy0oiFTEJThKooOAwic8sexYaflzOM` + otherSignedTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJmaWxlcyI6W3sibmFtZSI6ImEvYi9jL2RhdGEuanNvbiIsImhhc2giOiJmOWNhYzA3MTQ3MDVkMjBkMWEyMDg4MDE4NWNkZWQ2ZTBmNmQwNDA2NjJkMmViYjA5NjFkM2Q5ZjMxN2Q4YWNiIn1dLCJpYXQiOjE1OTIyNDgwMjcsImlzcyI6IkpXVFNlcnZpY2UiLCJzY29wZSI6IndyaXRlIn0.WJhnUjwaVvckSgOd4QcVvKThN6oc99NiPiwHKYnoG7c` + defaultSigner, _ := GetSigner(defaultSignerID) + defaultVerifier, _ := GetVerifier(defaultVerifierID) + if err := RegisterSigner("_bar", defaultSigner); err != nil { + t.Fatal(err) + } + if err := RegisterVerifier("_bar", defaultVerifier); err != nil { + t.Fatal(err) + } + + tests := map[string]struct { + files [][2]string + vc *VerificationConfig + wantErr bool + err error + }{ + "no_signature_verification_config": { + [][2]string{{"/.signatures.json", `{"signatures": []}`}}, + nil, + true, errors.New("verification key not provided"), + }, + "no_signatures_file_no_keyid": { + [][2]string{{"/.manifest", `{"revision": "quickbrownfaux"}`}}, + NewVerificationConfig(map[string]*KeyConfig{}, "", "", nil), + false, nil, + }, + "no_signatures_file": { + [][2]string{{"/.manifest", `{"revision": "quickbrownfaux"}`}}, + NewVerificationConfig(map[string]*KeyConfig{}, "somekey", "", nil), + true, errors.New("bundle missing .signatures.json file"), + }, + "no_signatures": { + [][2]string{{"/.signatures.json", `{"signatures": []}`}}, + NewVerificationConfig(map[string]*KeyConfig{}, "", "", nil), + true, errors.New(".signatures.json: missing JWT (expected exactly one)"), + }, + "digest_mismatch": { + [][2]string{ + {"/.signatures.json", fmt.Sprintf(`{"signatures": ["%v"]}`, signedTokenHS256)}, + {"/a/b/c/data.json", "[1,2,3]"}, + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + }, + NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", nil), + true, errors.New("a/b/c/data.json: digest mismatch (want: 42cfe6768b57bb5f7503c165c28dd07ac5b813554ebc850f2cc35843e7137b1d, got: a615eeaee21de5179de080de8c3052c8da901138406ba71c38c032845f7d54f4)"), + }, + "no_hashing_alg": { + [][2]string{ + {"/.signatures.json", fmt.Sprintf(`{"signatures": ["%v"]}`, otherSignedTokenHS256)}, + {"/a/b/c/data.json", "[1,2,3]"}, + }, + NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", nil), + true, errors.New("no hashing algorithm provided for file a/b/c/data.json"), + }, + "exclude_files": { + [][2]string{ + {"/.signatures.json", fmt.Sprintf(`{"signatures": ["%v"]}`, signedTokenHS256)}, + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + {"/a/b/c/data.json", "[1,2,3]"}, + {"/http/policy/policy.rego", `package example`}, + }, + NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", []string{".*", "a/b/c/data.json", "http/policy/policy.rego"}), + false, nil, + }, + "customer_signer_verifier": { + [][2]string{ + {"/.signatures.json", fmt.Sprintf(`{"signatures": ["%v"],"plugin":"_bar"}`, signedTokenHS256)}, + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + {"/a/b/c/data.json", "[1,2,3]"}, + {"/http/policy/policy.rego", `package example`}, + }, + NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", []string{".*", "a/b/c/data.json", "http/policy/policy.rego"}), + false, nil, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + buf := archive.MustWriteTarGz(tc.files) + reader := NewReader(buf).WithBundleVerificationConfig(tc.vc) + _, err := reader.Read() + + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + } +} + +func TestReadWithSignaturesWithBaseDir(t *testing.T) { + signedTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJmaWxlcyI6W3sibmFtZSI6ImZvby9iYXIvLm1hbmlmZXN0IiwiaGFzaCI6IjUwN2EyYzM4YTE0NDFkYjU4ZDJjYjg3OTgyYzQyYWE5MWE0MzQyZWY0MjJhNmI1NDJlZGRlYmVlZjZmMDQxMmYiLCJhbGdvcml0aG0iOiJTSEEtMjU2In0seyJuYW1lIjoiZm9vL2Jhci9hL2IvYy9kYXRhLmpzb24iLCJoYXNoIjoiYTYxNWVlYWVlMjFkZTUxNzlkZTA4MGRlOGMzMDUyYzhkYTkwMTEzODQwNmJhNzFjMzhjMDMyODQ1ZjdkNTRmNCIsImFsZ29yaXRobSI6IlNIQS0yNTYifSx7Im5hbWUiOiJmb28vYmFyL2h0dHAvcG9saWN5L3BvbGljeS5yZWdvIiwiaGFzaCI6ImY2NjQ0NjFlMzAzYjM3YzIwYzVlMGJlMjkwMDg4MTY3OGNkZjhlODYwYWE0MzNhNWExNGQ0OTRiYTNjNjY2NDkiLCJhbGdvcml0aG0iOiJTSEEtMjU2In1dLCJpYXQiOjE1OTIyNDgwMjcsImlzcyI6IkpXVFNlcnZpY2UiLCJzY29wZSI6IndyaXRlIn0.qTHkuBDVuT-Zl5pbJdZ6LoJ9eooFOhhpRdCheauDrlA` + + files := [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + {"/.signatures.json", fmt.Sprintf(`{"signatures": ["%v"]}`, signedTokenHS256)}, + {"/a/b/c/data.json", "[1,2,3]"}, + {"/http/policy/policy.rego", `package example`}, + } + + vc := NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", nil) + + buf := archive.MustWriteTarGz(files) + + loader := NewTarballLoaderWithBaseURL(buf, "/foo/bar") + reader := NewCustomReader(loader).WithBaseDir("/foo/bar").WithBundleVerificationConfig(vc) + _, err := reader.Read() + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +func TestReadWithPatch(t *testing.T) { + files := [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux", "roots": ["a"]}`}, + {"/patch.json", `{"data": [{"op": "add", "path": "/a/b/d", "value": "foo"}, {"op": "remove", "path": "a/b/c"}]}`}, + } + + buf := archive.MustWriteTarGz(files) + + loader := NewTarballLoaderWithBaseURL(buf, "/foo/bar") + reader := NewCustomReader(loader).WithBaseDir("/foo/bar") + b, err := reader.Read() + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + actual := b.Type() + if actual != DeltaBundleType { + t.Fatalf("Expected delta bundle but got %v", actual) + } + + if len(b.Patch.Data) != 2 { + t.Fatalf("Expected two patch operations but got %v", len(b.Patch.Data)) + } + + p1 := PatchOperation{ + Op: "add", + Path: "/a/b/d", + Value: "foo", + } + + p2 := PatchOperation{ + Op: "remove", + Path: "a/b/c", + } + + expected := Patch{Data: []PatchOperation{p1, p2}} + + if !reflect.DeepEqual(b.Patch.Data, expected.Data) { + t.Fatalf("Expected patch %v but got %v", expected.Data, b.Patch.Data) + } +} + +func TestReadWithPatchExtraFiles(t *testing.T) { + cases := []struct { + note string + files [][2]string + err string + }{ + { + note: "extra data file", + files: [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux", "roots": ["a"]}`}, + {"/patch.json", `{"data": [{"op": "add", "path": "/a/b/d", "value": "foo"}, {"op": "remove", "path": "a/b/c"}]}`}, + {"/a/b/c/data.json", "[1,2,3]"}, + }, + err: "delta bundle expected to contain only patch file but data files found", + }, + { + note: "extra policy file", + files: [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux", "roots": ["a"]}`}, + {"/patch.json", `{"data": [{"op": "add", "path": "/a/b/d", "value": "foo"}, {"op": "remove", "path": "a/b/c"}]}`}, + {"/http/policy/policy.rego", `package example`}, + }, + err: "delta bundle expected to contain only patch file but policy files found", + }, + { + note: "extra wasm file", + files: [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux", "roots": ["a"]}`}, + {"/patch.json", `{"data": [{"op": "add", "path": "/a/b/d", "value": "foo"}, {"op": "remove", "path": "a/b/c"}]}`}, + {"/policy.wasm", `modules-compiled-as-wasm-binary`}, + }, + err: "delta bundle expected to contain only patch file but wasm files found", + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + buf := archive.MustWriteTarGz(tc.files) + loader := NewTarballLoaderWithBaseURL(buf, "/foo/bar") + reader := NewCustomReader(loader).WithBaseDir("/foo/bar") + _, err := reader.Read() + if tc.err == "" && err != nil { + t.Fatal("Unexpected error occurred:", err) + } else if tc.err != "" && err == nil { + t.Fatal("Expected error but got success") + } else if tc.err != "" && err != nil { + if tc.err != err.Error() { + t.Fatalf("Expected error to contain %q but got: %v", tc.err, err) + } + } + }) + } + +} + +func TestReadWithPatchPersistProperty(t *testing.T) { + cases := []struct { + note string + files [][2]string + persist bool + err string + }{ + { + note: "persist true property", + files: [][2]string{ + {"/patch.json", `{"data": [{"op": "add", "path": "/a/b/d", "value": "foo"}, {"op": "remove", "path": "a/b/c"}]}`}, + }, + persist: true, + err: "'persist' property is true in config. persisting delta bundle to disk is not supported", + }, + { + note: "persist false property", + files: [][2]string{ + {"/patch.json", `{"data": [{"op": "add", "path": "/a/b/d", "value": "foo"}, {"op": "remove", "path": "a/b/c"}]}`}, + }, + persist: false, + err: "", + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + buf := archive.MustWriteTarGz(tc.files) + loader := NewTarballLoaderWithBaseURL(buf, "/foo/bar") + reader := NewCustomReader(loader). + WithBundlePersistence(tc.persist).WithBaseDir("/foo/bar") + _, err := reader.Read() + if tc.err == "" && err != nil { + t.Fatal("Unexpected error occurred:", err) + } else if tc.err != "" && err == nil { + t.Fatal("Expected error but got success") + } else if tc.err != "" && err != nil { + if tc.err != err.Error() { + t.Fatalf("Expected error to contain %q but got: %v", tc.err, err) + } + } + }) + } +} + +func TestReadWithSignaturesExtraFiles(t *testing.T) { + signedTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJmaWxlcyI6W3sibmFtZSI6Ii5tYW5pZmVzdCIsImhhc2giOiI1MDdhMmMzOGExNDQxZGI1OGQyY2I4Nzk4MmM0MmFhOTFhNDM0MmVmNDIyYTZiNTQyZWRkZWJlZWY2ZjA0MTJmIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9LHsibmFtZSI6ImEvYi9jL2RhdGEuanNvbiIsImhhc2giOiI0MmNmZTY3NjhiNTdiYjVmNzUwM2MxNjVjMjhkZDA3YWM1YjgxMzU1NGViYzg1MGYyY2MzNTg0M2U3MTM3YjFkIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9LHsibmFtZSI6Imh0dHAvcG9saWN5L3BvbGljeS5yZWdvIiwiaGFzaCI6ImE2MTVlZWFlZTIxZGU1MTc5ZGUwODBkZThjMzA1MmM4ZGE5MDExMzg0MDZiYTcxYzM4YzAzMjg0NWY3ZDU0ZjQiLCJhbGdvcml0aG0iOiJTSEEtMjU2In1dLCJpYXQiOjE1OTIyNDgwMjcsImlzcyI6IkpXVFNlcnZpY2UiLCJzY29wZSI6IndyaXRlIn0.Vmm9UDiInUnXXlk-OOjiCy3rR7EVvXS-OFst1rbh3Zo` + + files := [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + {"/.signatures.json", fmt.Sprintf(`{"signatures": ["%v"]}`, signedTokenHS256)}, + } + + vc := NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", nil) + + buf := archive.MustWriteTarGz(files) + reader := NewReader(buf).WithBundleVerificationConfig(vc) + _, err := reader.Read() + if err == nil { + t.Fatal("Expected error but got nil") + } + + expected := []string{ + "file(s) [a/b/c/data.json http/policy/policy.rego] specified in bundle signatures but not found in the target bundle", + "file(s) [http/policy/policy.rego a/b/c/data.json] specified in bundle signatures but not found in the target bundle", + } + + var found bool + if err.Error() == expected[0] || err.Error() == expected[1] { + found = true + } + + if !found { + t.Fatalf("Expected error message to be one of %v but got %v", expected, err.Error()) + } +} + +func TestVerifyBundleFileHash(t *testing.T) { + // add files to the bundle and reader + // compare the hash the for target files + files := [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + {"/a/b/c/data.json", "[1,2,3]"}, + {"/a/b/d/data.json", "true"}, + {"/a/b/y/data.yaml", `foo: 1`}, + {"/example/example.rego", `package example`}, + {"/policy.wasm", `modules-compiled-as-wasm-binary`}, + {"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`}, + } + + buf := archive.MustWriteTarGz(files) + reader := NewReader(buf) + reader.files = map[string]FileInfo{} + + expDigests := make([]string, len(files)) + expDigests[0] = "a005c38a509dc2d5a7407b9494efb2ad" + expDigests[1] = "60f7b5dc86ded48785436192a08dbfd04894d7f1b417c4f8d3714679a7f78cb3c833f16a8559a1cf1f32968747dc1d95ef34826263dacf125ded8f5c374be4c0" + expDigests[2] = "b326b5062b2f0e69046810717534cb09" + expDigests[3] = "20f27a640a233e6524fe7d138898583cd43475724806feb26be7f214e1d10b29edf6a0d3cb08f82107a45686b61b8fdabab6406cf4e70efe134f42238dbd70ab" + expDigests[4] = "ceecc199d432a4eeae305914ea4816cb" + expDigests[5] = "4f73765168fd8b5c294b739436da312cc5e979faf09f67bf576d36ea79a4f79c70cbb3c33d06ff65f531a9f42abd0a8f4daacc554cb521837e876dc28f56ce89" + expDigests[6] = "36669864a622563256817033b1fc53db" + + // populate the files on the reader + // this simulates the files seen by the reader after + // decoding the signatures in the "signatures.json" file + for i, f := range files { + file := FileInfo{ + Name: f[0], + Hash: expDigests[i], + } + + if i%2 == 0 { + file.Algorithm = MD5.String() + } else { + file.Algorithm = SHA512.String() + } + + reader.files[f[0]] = file + } + + for _, f := range files { + buf := bytes.NewBufferString(f[1]) + err := reader.verifyBundleFile(f[0], *buf) + if err != nil { + t.Fatal(err) + } + } + + // check there are no files left on the reader + if len(reader.files) != 0 { + t.Fatalf("Expected no files on the reader but got %v", len(reader.files)) + } +} + +func TestIsFileExcluded(t *testing.T) { + cases := []struct { + note string + file string + pattern []string + exp bool + }{ + { + note: "exact", + file: "data.json", + pattern: []string{"data.json"}, + exp: true, + }, + { + note: "hidden", + file: ".manifest", + pattern: []string{".*"}, + exp: true, + }, + { + note: "no_match", + file: "data.json", + pattern: []string{".*"}, + exp: false, + }, + { + note: "dir_match", + file: "/a/b/data.json", + pattern: []string{"/a/b/*"}, + exp: true, + }, + { + note: "dir_no_match", + file: "/a/b/c/data.json", + pattern: []string{"/a/b/*"}, + exp: false, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + + buf := archive.MustWriteTarGz([][2]string{}) + vc := NewVerificationConfig(map[string]*KeyConfig{}, "", "", tc.pattern) + reader := NewReader(buf).WithBundleVerificationConfig(vc) + actual := reader.isFileExcluded(tc.file) + + if actual != tc.exp { + t.Fatalf("Expected file exclude result for %v %v but got %v", tc.file, tc.exp, actual) + } + }) + } +} + +func TestReadRootValidation(t *testing.T) { + cases := []struct { + note string + files [][2]string + err string + }{ + { + note: "default full extent", + files: [][2]string{ + {"/.manifest", `{"revision": "abcd"}`}, + {"/data.json", `{"a": 1}`}, + {"/x.rego", `package foo`}, + }, + err: "", + }, + { + note: "explicit full extent", + files: [][2]string{ + {"/.manifest", `{"revision": "abcd", "roots": [""]}`}, + {"/data.json", `{"a": 1}`}, + {"/x.rego", `package foo`}, + }, + err: "", + }, + { + note: "implicit prefixed", + files: [][2]string{ + {"/.manifest", `{"revision": "abcd", "roots": ["a/b", "foo"]}`}, + {"/data.json", `{"a": {"b": 1}}`}, + {"/x.rego", `package foo.bar`}, + }, + err: "", + }, + { + note: "err empty", + files: [][2]string{ + {"/.manifest", `{"revision": "abcd", "roots": []}`}, + {"/x.rego", `package foo`}, + }, + err: "manifest roots [] do not permit 'package foo' in module '/x.rego'", + }, + { + note: "err overlapped", + files: [][2]string{ + {"/.manifest", `{"revision": "abcd", "roots": ["a/b", "a"]}`}, + }, + err: "manifest has overlapped roots: 'a/b' and 'a'", + }, + { + note: "edge overlapped partial segment", + files: [][2]string{ + {"/.manifest", `{"revision": "abcd", "roots": ["a", "another_root"]}`}, + }, + err: "", + }, + { + note: "err package outside scope", + files: [][2]string{ + {"/.manifest", `{"revision": "abcd", "roots": ["a", "b", "c/d"]}`}, + {"/a.rego", `package b.c`}, + {"/x.rego", `package c.e`}, + }, + err: "manifest roots [a b c/d] do not permit 'package c.e' in module '/x.rego'", + }, + { + note: "err data outside scope", + files: [][2]string{ + {"/.manifest", `{"revision": "abcd", "roots": ["a", "b", "c/d"]}`}, + {"/data.json", `{"a": 1}`}, + {"/c/e/data.json", `"bad bad bad"`}, + }, + err: "manifest roots [a b c/d] do not permit data at path '/c/e'", + }, + { + note: "err data patch outside scope", + files: [][2]string{ + {"/.manifest", `{"revision": "abcd", "roots": ["a", "b", "c/d"]}`}, + {"/patch.json", `{"data": [{"op": "add", "path": "/a/b/d", "value": "foo"}, {"op": "remove", "path": "/c/e"}]}`}, + }, + err: "manifest roots [a b c/d] do not permit data patch at path 'c/e'", + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + buf := archive.MustWriteTarGz(tc.files) + _, err := NewReader(buf).IncludeManifestInData(true).Read() + if tc.err == "" && err != nil { + t.Fatal("Unexpected error occurred:", err) + } else if tc.err != "" && err == nil { + t.Fatal("Expected error but got success") + } else if tc.err != "" && err != nil { + if !strings.Contains(err.Error(), tc.err) { + t.Fatalf("Expected error to contain %q but got: %v", tc.err, err) + } + } + }) + } +} + +func TestRootPathsContain(t *testing.T) { + tests := []struct { + note string + roots []string + path string + want bool + }{ + { + note: "empty contains empty", + roots: []string{""}, + path: "", + want: true, + }, + { + note: "empty contains non-empty", + roots: []string{""}, + path: "foo/bar", + want: true, + }, + { + note: "single prefix", + roots: []string{"foo"}, + path: "foo/bar", + want: true, + }, + { + note: "single prefix no match", + roots: []string{"bar"}, + path: "foo/bar", + want: false, + }, + { + note: "multiple prefix", + roots: []string{"baz", "foo"}, + path: "foo/bar", + want: true, + }, + { + note: "multiple prefix no match", + roots: []string{"baz", "qux"}, + path: "foo/bar", + want: false, + }, + { + note: "single exact", + roots: []string{"foo/bar"}, + path: "foo/bar", + want: true, + }, + { + note: "single exact no match", + roots: []string{"foo/ba"}, + path: "foo/bar", + want: false, + }, + { + note: "multiple exact", + roots: []string{"baz/bar", "foo/bar"}, + path: "foo/bar", + want: true, + }, + { + note: "root too long", + roots: []string{"foo/bar/"}, + path: "foo/bar", + want: false, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + if RootPathsContain(tc.roots, tc.path) != tc.want { + t.Fatalf("expected %v contains %v to be %v", tc.roots, tc.path, tc.want) + } + }) + } +} + +func TestReadErrorBadGzip(t *testing.T) { + buf := bytes.NewBufferString("bad gzip bytes") + _, err := NewReader(buf).Read() + if err == nil { + t.Fatal("expected error") + } +} + +func TestReadErrorBadTar(t *testing.T) { + var buf bytes.Buffer + gw := gzip.NewWriter(&buf) + _, _ = gw.Write([]byte("bad tar bytes")) + _ = gw.Close() + _, err := NewReader(&buf).Read() + if err == nil { + t.Fatal("expected error") + } +} + +func TestReadErrorBadContents(t *testing.T) { + tests := []struct { + files [][2]string + }{ + {[][2]string{{"/test.rego", "lkafjasdkljf"}}}, + {[][2]string{{"/data.json", "lskjafkljsdf"}}}, + {[][2]string{{"/data.json", "[1,2,3]"}}}, + {[][2]string{ + {"/a/b/data.json", "[1,2,3]"}, + {"a/b/c/data.json", "true"}, + }}, + {[][2]string{{"/test.rego", ""}}}, + {[][2]string{ + {"/a/b/data.json", `{"c": "foo"}`}, + {"/data.json", `{"a": {"b": {"c": [123]}}}`}, + }}, + } + for _, test := range tests { + buf := archive.MustWriteTarGz(test.files) + _, err := NewReader(buf).Read() + if err == nil { + t.Fatal("expected error") + } + } + +} + +func TestRoundtripDeprecatedWrite(t *testing.T) { + + bundle := Bundle{ + Data: map[string]any{ + "foo": map[string]any{ + "bar": []any{json.Number("1"), json.Number("2"), json.Number("3")}, + "baz": true, + "qux": "hello", + }, + }, + Modules: []ModuleFile{ + { + URL: "/foo/corge/corge.rego", + Path: "/foo/corge/corge.rego", + Parsed: ast.MustParseModule(`package foo.corge`), + Raw: []byte(`package foo.corge`), + }, + }, + WasmModules: []WasmModuleFile{ + { + Path: "/policy.wasm", + URL: "/policy.wasm", + Raw: []byte("modules-compiled-as-wasm-binary"), + }, + }, + Manifest: Manifest{ + Revision: "quickbrownfaux", + }, + } + + var buf bytes.Buffer + + if err := Write(&buf, bundle); err != nil { + t.Fatal("Unexpected error:", err) + } + + bundle2, err := NewReader(&buf).Read() + if err != nil { + t.Fatal("Unexpected error:", err) + } + + if !bundle2.Equal(bundle) { + t.Fatalf("\nExp: %+v\nGot: %+v", bundle, bundle2) + } + +} + +func TestRoundtrip(t *testing.T) { + + bundle := Bundle{ + Data: map[string]any{ + "foo": map[string]any{ + "bar": []any{json.Number("1"), json.Number("2"), json.Number("3")}, + "baz": true, + "qux": "hello", + }, + }, + Modules: []ModuleFile{ + { + URL: "/foo/corge/corge.rego", + Path: "/foo/corge/corge.rego", + Parsed: ast.MustParseModule(`package foo.corge`), + Raw: []byte("package foo.corge\n"), + }, + }, + WasmModules: []WasmModuleFile{ + { + Path: "/policy.wasm", + URL: "/policy.wasm", + Raw: []byte("modules-compiled-as-wasm-binary"), + }, + }, + Manifest: Manifest{ + Roots: &[]string{""}, + Revision: "quickbrownfaux", + Metadata: map[string]any{"version": "v1", "hello": "world"}, + }, + } + + if err := bundle.GenerateSignature(NewSigningConfig("secret", "HS256", ""), "foo", false); err != nil { + t.Fatal("Unexpected error:", err) + } + + var buf bytes.Buffer + + if err := NewWriter(&buf).Write(bundle); err != nil { + t.Fatal("Unexpected error:", err) + } + + vc := NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "foo", "", nil) + + bundle2, err := NewReader(&buf).WithBundleVerificationConfig(vc).Read() + if err != nil { + t.Fatal("Unexpected error:", err) + } + + if !bundle2.Equal(bundle) { + t.Fatal("Exp:", bundle, "\n\nGot:", bundle2) + } + + if !reflect.DeepEqual(bundle2.Signatures, bundle.Signatures) { + t.Fatal("Expected signatures to be same") + } +} + +func TestRoundtripWithPlanModules(t *testing.T) { + + b := Bundle{ + Data: map[string]any{}, + PlanModules: []PlanModuleFile{ + { + URL: "/plan.json", + Path: "/plan.json", + Raw: []byte(`{"foo": 7}`), // NOTE(tsandall): contents are ignored + }, + }, + } + + var buf bytes.Buffer + if err := Write(&buf, b); err != nil { + t.Fatal(err) + } + + b2, err := NewReader(&buf).Read() + if err != nil { + t.Fatal(err) + } + + if len(b2.PlanModules) != 1 || + b2.PlanModules[0].Path != b.PlanModules[0].Path || + b2.PlanModules[0].URL != b.PlanModules[0].URL || + !bytes.Equal(b2.PlanModules[0].Raw, b.PlanModules[0].Raw) { + t.Fatalf("expected %+v but got %+v", b, b2) + } +} + +func TestRoundtripDeltaBundle(t *testing.T) { + + // replace a value + p1 := PatchOperation{ + Op: "replace", + Path: "a/baz", + Value: "bux", + } + + // add a new object member + p2 := PatchOperation{ + Op: "add", + Path: "/a/foo", + Value: []string{"hello", "world"}, + } + + bundle := Bundle{ + Patch: Patch{Data: []PatchOperation{p1, p2}}, + Manifest: Manifest{ + Revision: "delta", + Roots: &[]string{"a"}, + }, + } + + var buf bytes.Buffer + + if err := NewWriter(&buf).Write(bundle); err != nil { + t.Fatal("Unexpected error:", err) + } + + bundle2, err := NewReader(&buf).Read() + if err != nil { + t.Fatal("Unexpected error:", err) + } + + if !bundle2.Equal(bundle) { + t.Fatal("Exp:", bundle, "\n\nGot:", bundle2) + } +} + +func TestWriterUsePath(t *testing.T) { + + bundle := Bundle{ + Data: map[string]any{}, + Modules: []ModuleFile{ + { + URL: "/url.rego", + Path: "/path.rego", + Parsed: ast.MustParseModule(`package x`), + Raw: []byte("package x\n"), + }, + }, + Manifest: Manifest{Revision: "quickbrownfaux"}, + } + + var buf bytes.Buffer + + if err := NewWriter(&buf).UseModulePath(true).Write(bundle); err != nil { + t.Fatal("Unexpected error:", err) + } + + bundle2, err := NewReader(&buf).Read() + if err != nil { + t.Fatal("Unexpected error:", err) + } + + if bundle2.Modules[0].URL != "/path.rego" || bundle2.Modules[0].Path != "/path.rego" { + t.Fatal("expected module path to be used but got:", bundle2.Modules[0]) + } +} + +func TestWriterSkipEmptyManifest(t *testing.T) { + + bundle := Bundle{ + Data: map[string]any{}, + Manifest: Manifest{}, + } + + var buf bytes.Buffer + + if err := NewWriter(&buf).Write(bundle); err != nil { + t.Fatal("Unexpected error:", err) + } + + gr, err := gzip.NewReader(&buf) + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(gr) + for { + f, err := tr.Next() + if err != nil { + if err != io.EOF { + t.Fatal(err) + } + break + } + + if f.Name != "/data.json" { + t.Fatal("expected only /data.json and /.manifest but got:", f.Name) + } + } +} + +func TestGenerateSignature(t *testing.T) { + signatures := SignaturesConfig{Signatures: []string{"some_token"}} + + bundle := Bundle{ + Data: map[string]any{ + "foo": map[string]any{ + "bar": []any{json.Number("1"), json.Number("2"), json.Number("3")}, + "baz": true, + "qux": "hello", + }, + }, + Modules: []ModuleFile{ + { + URL: "/foo/corge/corge.rego", + Path: "/foo/corge/corge.rego", + Parsed: ast.MustParseModule(`package foo.corge`), + Raw: []byte("package foo.corge\n"), + }, + }, + Wasm: []byte("modules-compiled-as-wasm-binary"), + Manifest: Manifest{ + Revision: "quickbrownfaux", + }, + Signatures: signatures, + } + + sc := NewSigningConfig("secret", "HS256", "") + + err := bundle.GenerateSignature(sc, "", false) + if err != nil { + t.Fatal("Unexpected error:", err) + } + + if reflect.DeepEqual(signatures, bundle.Signatures) { + t.Fatal("Expected signatures to be different") + } + + current := bundle.Signatures + err = bundle.GenerateSignature(sc, "", false) + if err != nil { + t.Fatal("Unexpected error:", err) + } + + if !reflect.DeepEqual(current, bundle.Signatures) { + t.Fatal("Expected signatures to be same") + } +} + +func TestGenerateSignatureWithPlugin(t *testing.T) { + signatures := SignaturesConfig{Signatures: []string{"some_token"}, Plugin: "_foo"} + + bundle := Bundle{ + Data: map[string]any{ + "foo": map[string]any{ + "bar": []any{json.Number("1"), json.Number("2"), json.Number("3")}, + "baz": true, + "qux": "hello", + }, + }, + Modules: []ModuleFile{ + { + URL: "/foo/corge/corge.rego", + Path: "/foo/corge/corge.rego", + Parsed: ast.MustParseModule(`package foo.corge`), + Raw: []byte("package foo.corge\n"), + }, + }, + Wasm: []byte("modules-compiled-as-wasm-binary"), + Manifest: Manifest{ + Revision: "quickbrownfaux", + }, + Signatures: signatures, + } + + defaultSigner, _ := GetSigner(defaultSignerID) + defaultVerifier, _ := GetVerifier(defaultVerifierID) + if err := RegisterSigner("_foo", defaultSigner); err != nil { + t.Fatal(err) + } + if err := RegisterVerifier("_foo", defaultVerifier); err != nil { + t.Fatal(err) + } + sc := NewSigningConfig("secret", "HS256", "").WithPlugin("_foo") + + err := bundle.GenerateSignature(sc, "", false) + if err != nil { + t.Fatal("Unexpected error:", err) + } + + if reflect.DeepEqual(signatures, bundle.Signatures) { + t.Fatal("Expected signatures to be different") + } + + current := bundle.Signatures + err = bundle.GenerateSignature(sc, "", false) + if err != nil { + t.Fatal("Unexpected error:", err) + } + + if !reflect.DeepEqual(current, bundle.Signatures) { + t.Fatal("Expected signatures to be same") + } +} + +func TestFormatModulesRaw(t *testing.T) { + + bundle1 := Bundle{ + Modules: []ModuleFile{ + { + URL: "/foo/corge/corge.rego", + Path: "/foo/corge/corge.rego", + Parsed: ast.MustParseModule(`package foo.corge`), + Raw: []byte("package foo.corge\n"), + }, + }, + } + + bundle2 := Bundle{ + Modules: []ModuleFile{ + { + URL: "/foo/corge/corge.rego", + Path: "/foo/corge/corge.rego", + Parsed: ast.MustParseModule(`package foo.corge`), + Raw: []byte("package foo.corge"), + }, + }, + } + + tests := map[string]struct { + bundle Bundle + exp bool + }{ + "equal": {bundle: bundle1, exp: true}, + "not_equal": {bundle: bundle2, exp: false}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + orig := tc.bundle.Modules[0].Raw + err := tc.bundle.FormatModules(false) + if err != nil { + t.Fatal("Unexpected error:", err) + } + + actual := bytes.Equal(orig, tc.bundle.Modules[0].Raw) + if actual != tc.exp { + t.Fatalf("Expected result %v but got %v", tc.exp, actual) + } + }) + } +} + +func TestFormatModulesParsed(t *testing.T) { + + bundle := Bundle{ + Modules: []ModuleFile{ + { + URL: "/foo/corge/corge.rego", + Path: "/foo/corge/corge.rego", + Parsed: ast.MustParseModule(`package foo.corge`), + Raw: nil, + }, + }, + } + + tests := map[string]struct { + bundle Bundle + }{ + "parsed": {bundle: bundle}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + err := tc.bundle.FormatModules(false) + if err != nil { + t.Fatal("Unexpected error:", err) + } + + exp := []byte("package foo.corge\n") + if !bytes.Equal(tc.bundle.Modules[0].Raw, exp) { + t.Fatalf("Expected raw policy %v but got %v", exp, tc.bundle.Modules[0].Raw) + } + }) + } +} + +func TestHashBundleFiles(t *testing.T) { + h, _ := NewSignatureHasher(SHA256) + + tests := map[string]struct { + data map[string]any + manifest Manifest + wasm []byte + plan []byte + exp int + }{ + "no_content": {map[string]any{}, Manifest{}, nil, nil, 1}, + "data": {map[string]any{"foo": "bar"}, Manifest{}, nil, nil, 1}, + "data_and_manifest": {map[string]any{"foo": "bar"}, Manifest{Revision: "quickbrownfaux"}, []byte{}, nil, 2}, + "data_and_manifest_and_wasm": {map[string]any{"foo": "bar"}, Manifest{Revision: "quickbrownfaux"}, []byte("modules-compiled-as-wasm-binary"), nil, 3}, + "data_and_plan": {map[string]any{"foo": "bar"}, Manifest{Revision: "quickbrownfaux"}, nil, []byte("not a plan but good enough"), 3}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + var plans []PlanModuleFile + if len(tc.plan) > 0 { + plans = append(plans, PlanModuleFile{ + URL: "/plan.json", + Path: "/plan.json", + Raw: tc.plan, + }) + } + + f, err := hashBundleFiles(h, &Bundle{Data: tc.data, Manifest: tc.manifest, Wasm: tc.wasm, PlanModules: plans}) + if err != nil { + t.Fatal("Unexpected error:", err) + } + + if len(f) != tc.exp { + t.Fatalf("Expected %v file(s) to be added to the signature but got %v", tc.exp, len(f)) + } + }) + } +} + +func TestWriterUseURL(t *testing.T) { + + bundle := Bundle{ + Data: map[string]any{}, + Modules: []ModuleFile{ + { + URL: "/url.rego", + Path: "/path.rego", + Parsed: ast.MustParseModule(`package x`), + Raw: []byte("package x\n"), + }, + }, + Manifest: Manifest{Revision: "quickbrownfaux"}, + } + + var buf bytes.Buffer + + if err := NewWriter(&buf).UseModulePath(false).Write(bundle); err != nil { + t.Fatal("Unexpected error:", err) + } + + bundle2, err := NewReader(&buf).Read() + if err != nil { + t.Fatal("Unexpected error:", err) + } + + if bundle2.Modules[0].URL != "/url.rego" || bundle2.Modules[0].Path != "/url.rego" { + t.Fatal("expected module path to be used but got:", bundle2.Modules[0]) + } +} + +func TestRootPathsOverlap(t *testing.T) { + cases := []struct { + note string + rootA string + rootB string + expected bool + }{ + {"both empty", "", "", true}, + {"a empty", "", "foo/bar", true}, + {"b empty", "foo/bar", "", true}, + {"no overlap", "a/b/c", "x/y", false}, + {"partial segment overlap a", "a/b", "a/banana", false}, + {"partial segment overlap b", "a/banana", "a/b", false}, + {"overlap a", "a/b", "a/b/c", true}, + {"overlap b", "a/b/c", "a/b", true}, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + actual := RootPathsOverlap(tc.rootA, tc.rootB) + if actual != tc.expected { + t.Errorf("Expected %t, got %t", tc.expected, actual) + } + }) + } +} + +func TestParsedModules(t *testing.T) { + cases := []struct { + note string + bundle Bundle + name string + expectedModules []string + }{ + { + note: "base", + bundle: Bundle{ + Modules: []ModuleFile{ + { + Path: "/foo/policy.rego", + Parsed: ast.MustParseModule(`package foo`), + Raw: []byte(`package foo`), + }, + }, + }, + name: "test-bundle", + expectedModules: []string{ + "test-bundle/foo/policy.rego", + }, + }, + { + note: "filepath name", + bundle: Bundle{ + Modules: []ModuleFile{ + { + Path: "/foo/policy.rego", + Parsed: ast.MustParseModule(`package foo`), + Raw: []byte(`package foo`), + }, + }, + }, + name: "/some/system/path", + expectedModules: []string{ + "/some/system/path/foo/policy.rego", + }, + }, + { + note: "file url name", + bundle: Bundle{ + Modules: []ModuleFile{ + { + Path: "/foo/policy.rego", + Parsed: ast.MustParseModule(`package foo`), + Raw: []byte(`package foo`), + }, + }, + }, + name: "file:///some/system/path", + expectedModules: []string{ + "/some/system/path/foo/policy.rego", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + parsedMods := tc.bundle.ParsedModules(tc.name) + + for _, exp := range tc.expectedModules { + mod, ok := parsedMods[exp] + if !ok { + t.Fatalf("Missing expected module %s, got: %+v", exp, parsedMods) + } + if mod == nil { + t.Fatalf("Expected module to be non-nil") + } + } + }) + } +} + +func TestMergeCorruptManifest(t *testing.T) { + _, err := Merge([]*Bundle{ + {}, + {}, + }) + if err == nil || err.Error() != "bundle manifest not initialized" { + t.Fatal("unexpected error:", err) + } +} + +func TestMerge(t *testing.T) { + + expRegoVersion := ast.DefaultRegoVersion.Int() + + cases := []struct { + note string + bundles []*Bundle + wantBundle *Bundle + wantErr error + }{ + { + note: "empty list", + wantErr: errors.New("expected at least one bundle"), + }, + { + note: "no op", + bundles: []*Bundle{ + { + Manifest: Manifest{ + Revision: "abcdef", + }, + Modules: []ModuleFile{ + { + Path: "x.rego", + Parsed: ast.MustParseModule(`package foo`), + Raw: []byte("package foo"), + }, + }, + }, + }, + wantBundle: &Bundle{ + Manifest: Manifest{ + Revision: "abcdef", + Roots: &[]string{""}, + RegoVersion: &expRegoVersion, + FileRegoVersions: map[string]int{}, + }, + Modules: []ModuleFile{ + { + Path: "x.rego", + Parsed: ast.MustParseModule(`package foo`), + Raw: []byte("package foo"), + }, + }, + }, + }, + { + note: "wasm merge legacy error", + bundles: []*Bundle{ + { + Manifest: Manifest{ + Roots: &[]string{ + "foo", + }, + }, + Wasm: []byte("not really wasm, but good enough"), + }, + { + Manifest: Manifest{ + Roots: &[]string{ + "bar", + }, + }, + Wasm: []byte("not really wasm, but good enough"), + }, + }, + wantBundle: &Bundle{ + Manifest: Manifest{ + Roots: &[]string{ + "foo", + "bar", + }, + RegoVersion: &expRegoVersion, + }, + Data: map[string]any{}, + }, + }, + { + note: "wasm merge ok", + bundles: []*Bundle{ + { + Manifest: Manifest{ + Roots: &[]string{ + "logs", + }, + }, + WasmModules: []WasmModuleFile{ + { + URL: "logs/mask/policy.wasm", + Path: "logs/mask/policy.wasm", + Entrypoints: []ast.Ref{ast.MustParseRef("system.log.mask")}, + Raw: []byte("not really wasm, but good enough"), + }, + }, + }, + { + Manifest: Manifest{ + Roots: &[]string{ + "authz", + }, + }, + WasmModules: []WasmModuleFile{ + { + URL: "authz/allow/policy.wasm", + Path: "authz/allow/policy.wasm", + Entrypoints: []ast.Ref{ast.MustParseRef("authz.allow")}, + Raw: []byte("not really wasm, but good enough"), + }, + }, + }, + }, + wantBundle: &Bundle{ + Manifest: Manifest{ + Roots: &[]string{ + "logs", + "authz", + }, + RegoVersion: &expRegoVersion, + }, + WasmModules: []WasmModuleFile{ + { + URL: "logs/mask/policy.wasm", + Path: "logs/mask/policy.wasm", + Entrypoints: []ast.Ref{ast.MustParseRef("system.log.mask")}, + Raw: []byte("not really wasm, but good enough"), + }, + { + URL: "authz/allow/policy.wasm", + Path: "authz/allow/policy.wasm", + Entrypoints: []ast.Ref{ast.MustParseRef("authz.allow")}, + Raw: []byte("not really wasm, but good enough"), + }, + }, + Data: map[string]any{}, + }, + }, + { + note: "merge policy", + bundles: []*Bundle{ + { + Manifest: Manifest{ + Roots: &[]string{ + "foo", + }, + }, + Modules: []ModuleFile{ + { + URL: "foo/bar.rego", + Parsed: ast.MustParseModule(`package foo`), + Raw: []byte("package foo"), + }, + }, + }, + { + Manifest: Manifest{ + Roots: &[]string{ + "baz", + }, + }, + Modules: []ModuleFile{ + { + URL: "baz/qux.rego", + Parsed: ast.MustParseModule(`package baz`), + Raw: []byte("package baz"), + }, + }, + }, + }, + wantBundle: &Bundle{ + Manifest: Manifest{ + Roots: &[]string{ + "foo", + "baz", + }, + RegoVersion: &expRegoVersion, + }, + Modules: []ModuleFile{ + { + URL: "foo/bar.rego", + Parsed: ast.MustParseModule(`package foo`), + Raw: []byte("package foo"), + }, + { + URL: "baz/qux.rego", + Parsed: ast.MustParseModule(`package baz`), + Raw: []byte("package baz"), + }, + }, + Data: map[string]any{}, + }, + }, + { + note: "merge data", + bundles: []*Bundle{ + { + Manifest: Manifest{ + Roots: &[]string{ + "foo/bar", + }, + }, + Data: map[string]any{ + "foo": map[string]any{ + "bar": "val1", + }, + }, + }, + { + Manifest: Manifest{ + Roots: &[]string{ + "baz", + }, + }, + Data: map[string]any{ + "baz": "val2", + }, + }, + }, + wantBundle: &Bundle{ + Manifest: Manifest{ + Roots: &[]string{ + "foo/bar", + "baz", + }, + RegoVersion: &expRegoVersion, + }, + Data: map[string]any{ + "foo": map[string]any{ + "bar": "val1", + }, + "baz": "val2", + }, + }, + }, + { + note: "merge empty data", + bundles: []*Bundle{ + { + Manifest: Manifest{ + Roots: &[]string{ + "foo/bar", + }, + }, + Data: map[string]any{}, + }, + { + Manifest: Manifest{ + Roots: &[]string{ + "baz", + }, + }, + Data: map[string]any{}, + }, + }, + wantBundle: &Bundle{ + Manifest: Manifest{ + Roots: &[]string{ + "foo/bar", + "baz", + }, + RegoVersion: &expRegoVersion, + }, + Data: map[string]any{}, + }, + }, + { + note: "merge plans", + bundles: []*Bundle{ + { + Manifest: Manifest{ + Roots: &[]string{"a"}, + }, + PlanModules: []PlanModuleFile{ + { + URL: "a/plan.json", + Path: "a/plan.json", + Raw: []byte("not a real plan but good enough"), + }, + }, + }, + { + Manifest: Manifest{ + Roots: &[]string{"b"}, + }, + PlanModules: []PlanModuleFile{ + { + URL: "b/plan.json", + Path: "b/plan.json", + Raw: []byte("not a real plan but good enough"), + }, + }, + }, + }, + wantBundle: &Bundle{ + Data: map[string]any{}, + Manifest: Manifest{ + Roots: &[]string{"a", "b"}, + RegoVersion: &expRegoVersion, + }, + PlanModules: []PlanModuleFile{ + { + URL: "a/plan.json", + Path: "a/plan.json", + Raw: []byte("not a real plan but good enough"), + }, + { + URL: "b/plan.json", + Path: "b/plan.json", + Raw: []byte("not a real plan but good enough"), + }, + }, + }, + }, + { + note: "conflicting roots", + bundles: []*Bundle{ + { + Manifest: Manifest{ + Roots: &[]string{ + "foo/bar", + }, + }, + }, + { + Manifest: Manifest{ + Roots: &[]string{ + "foo", + }, + }, + }, + }, + wantErr: errors.New("manifest has overlapped roots: 'foo/bar' and 'foo'"), + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + for i := range tc.bundles { + if err := tc.bundles[i].Manifest.validateAndInjectDefaults(*tc.bundles[i]); err != nil { + panic(err) + } + } + b, err := Merge(tc.bundles) + if tc.wantErr != nil { + if err == nil { + t.Fatal("expected error") + } else if err.Error() != tc.wantErr.Error() { + t.Fatalf("expected error %q but got: %q", tc.wantErr, err) + } + } else if err != nil { + t.Fatal("unexpected error:", err) + } else if !b.Equal(*tc.wantBundle) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", tc.wantBundle, b) + } else if !reflect.DeepEqual(b.Manifest, tc.wantBundle.Manifest) { + t.Fatalf("Expected manifest:\n\n%v\n\nGot manifest:\n\n%v", tc.wantBundle.Manifest, b.Manifest) + } + }) + } +} + +func pointTo[T any](x T) *T { + return &x +} diff --git a/third_party/opa/v1/bundle/file.go b/third_party/opa/v1/bundle/file.go new file mode 100644 index 000000000000..12e159254cf9 --- /dev/null +++ b/third_party/opa/v1/bundle/file.go @@ -0,0 +1,517 @@ +package bundle + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "sort" + "strings" + "sync" + + "github.com/open-policy-agent/opa/v1/loader/filter" + + "github.com/open-policy-agent/opa/v1/storage" +) + +const maxSizeLimitBytesErrMsg = "bundle file %s size (%d bytes) exceeds configured size_limit_bytes (%d bytes)" + +// Descriptor contains information about a file and +// can be used to read the file contents. +type Descriptor struct { + url string + path string + reader io.Reader + closer io.Closer + closeOnce *sync.Once +} + +// lazyFile defers reading the file until the first call of Read +type lazyFile struct { + path string + file *os.File +} + +// newLazyFile creates a new instance of lazyFile +func newLazyFile(path string) *lazyFile { + return &lazyFile{path: path} +} + +// Read implements io.Reader. It will check if the file has been opened +// and open it if it has not before attempting to read using the file's +// read method +func (f *lazyFile) Read(b []byte) (int, error) { + var err error + + if f.file == nil { + if f.file, err = os.Open(f.path); err != nil { + return 0, fmt.Errorf("failed to open file %s: %w", f.path, err) + } + } + + return f.file.Read(b) +} + +// Close closes the lazy file if it has been opened using the file's +// close method +func (f *lazyFile) Close() error { + if f.file != nil { + return f.file.Close() + } + + return nil +} + +func NewDescriptor(url, path string, reader io.Reader) *Descriptor { + return &Descriptor{ + url: url, + path: path, + reader: reader, + } +} + +func (d *Descriptor) WithCloser(closer io.Closer) *Descriptor { + d.closer = closer + d.closeOnce = new(sync.Once) + return d +} + +// Path returns the path of the file. +func (d *Descriptor) Path() string { + return d.path +} + +// URL returns the url of the file. +func (d *Descriptor) URL() string { + return d.url +} + +// Read will read all the contents from the file the Descriptor refers to +// into the dest writer up n bytes. Will return an io.EOF error +// if EOF is encountered before n bytes are read. +func (d *Descriptor) Read(dest io.Writer, n int64) (int64, error) { + n, err := io.CopyN(dest, d.reader, n) + return n, err +} + +// Close the file, on some Loader implementations this might be a no-op. +// It should *always* be called regardless of file. +func (d *Descriptor) Close() error { + var err error + if d.closer != nil { + d.closeOnce.Do(func() { + err = d.closer.Close() + }) + } + return err +} + +type PathFormat int64 + +const ( + Chrooted PathFormat = iota + SlashRooted + Passthrough +) + +// DirectoryLoader defines an interface which can be used to load +// files from a directory by iterating over each one in the tree. +type DirectoryLoader interface { + // NextFile must return io.EOF if there is no next value. The returned + // descriptor should *always* be closed when no longer needed. + NextFile() (*Descriptor, error) + WithFilter(filter filter.LoaderFilter) DirectoryLoader + WithPathFormat(PathFormat) DirectoryLoader + WithSizeLimitBytes(sizeLimitBytes int64) DirectoryLoader + WithFollowSymlinks(followSymlinks bool) DirectoryLoader +} + +type dirLoader struct { + root string + files []string + idx int + filter filter.LoaderFilter + pathFormat PathFormat + maxSizeLimitBytes int64 + followSymlinks bool +} + +// Normalize root directory, ex "./src/bundle" -> "src/bundle" +// We don't need an absolute path, but this makes the joined/trimmed +// paths more uniform. +func normalizeRootDirectory(root string) string { + if len(root) > 1 { + if root[0] == '.' && root[1] == filepath.Separator { + if len(root) == 2 { + root = root[:1] // "./" -> "." + } else { + root = root[2:] // remove leading "./" + } + } + } + return root +} + +// NewDirectoryLoader returns a basic DirectoryLoader implementation +// that will load files from a given root directory path. +func NewDirectoryLoader(root string) DirectoryLoader { + d := dirLoader{ + root: normalizeRootDirectory(root), + pathFormat: Chrooted, + } + return &d +} + +// WithFilter specifies the filter object to use to filter files while loading bundles +func (d *dirLoader) WithFilter(filter filter.LoaderFilter) DirectoryLoader { + d.filter = filter + return d +} + +// WithPathFormat specifies how a path is formatted in a Descriptor +func (d *dirLoader) WithPathFormat(pathFormat PathFormat) DirectoryLoader { + d.pathFormat = pathFormat + return d +} + +// WithSizeLimitBytes specifies the maximum size of any file in the directory to read +func (d *dirLoader) WithSizeLimitBytes(sizeLimitBytes int64) DirectoryLoader { + d.maxSizeLimitBytes = sizeLimitBytes + return d +} + +// WithFollowSymlinks specifies whether to follow symlinks when loading files from the directory +func (d *dirLoader) WithFollowSymlinks(followSymlinks bool) DirectoryLoader { + d.followSymlinks = followSymlinks + return d +} + +func formatPath(fileName string, root string, pathFormat PathFormat) string { + switch pathFormat { + case SlashRooted: + if !strings.HasPrefix(fileName, string(filepath.Separator)) { + return string(filepath.Separator) + fileName + } + return fileName + case Chrooted: + // Trim off the root directory and return path as if chrooted + result := strings.TrimPrefix(fileName, filepath.FromSlash(root)) + if root == "." && filepath.Base(fileName) == ManifestExt { + result = fileName + } + if !strings.HasPrefix(result, string(filepath.Separator)) { + result = string(filepath.Separator) + result + } + return result + case Passthrough: + fallthrough + default: + return fileName + } +} + +// NextFile iterates to the next file in the directory tree +// and returns a file Descriptor for the file. +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, _ error) error { + if info == nil { + return nil + } + + if info.Mode().IsRegular() { + if d.filter != nil && d.filter(filepath.ToSlash(path), info, getdepth(path, false)) { + return nil + } + if d.maxSizeLimitBytes > 0 && info.Size() > d.maxSizeLimitBytes { + return fmt.Errorf(maxSizeLimitBytesErrMsg, strings.TrimPrefix(path, "/"), info.Size(), d.maxSizeLimitBytes) + } + d.files = append(d.files, path) + } else if d.followSymlinks && info.Mode().Type()&fs.ModeSymlink == fs.ModeSymlink { + if d.filter != nil && d.filter(filepath.ToSlash(path), info, getdepth(path, false)) { + return nil + } + if d.maxSizeLimitBytes > 0 && info.Size() > d.maxSizeLimitBytes { + return fmt.Errorf(maxSizeLimitBytesErrMsg, strings.TrimPrefix(path, "/"), info.Size(), d.maxSizeLimitBytes) + } + d.files = append(d.files, path) + } else if info.Mode().IsDir() { + if d.filter != nil && d.filter(filepath.ToSlash(path), info, getdepth(path, true)) { + return filepath.SkipDir + } + } + return nil + }) + if err != nil { + return nil, fmt.Errorf("failed to list files: %w", err) + } + } + + // If done reading files then just return io.EOF + // errors for each NextFile() call + if d.idx >= len(d.files) { + return nil, io.EOF + } + + fileName := d.files[d.idx] + d.idx++ + fh := newLazyFile(fileName) + + cleanedPath := formatPath(fileName, d.root, d.pathFormat) + f := NewDescriptor(filepath.Join(d.root, cleanedPath), cleanedPath, fh).WithCloser(fh) + return f, nil +} + +type tarballLoader struct { + baseURL string + r io.Reader + tr *tar.Reader + files []file + idx int + filter filter.LoaderFilter + skipDir map[string]struct{} + pathFormat PathFormat + maxSizeLimitBytes int64 +} + +type file struct { + name string + reader io.Reader + path storage.Path + raw []byte +} + +// NewTarballLoader is deprecated. Use NewTarballLoaderWithBaseURL instead. +func NewTarballLoader(r io.Reader) DirectoryLoader { + l := tarballLoader{ + r: r, + pathFormat: Passthrough, + } + return &l +} + +// NewTarballLoaderWithBaseURL returns a new DirectoryLoader that reads +// files out of a gzipped tar archive. The file URLs will be prefixed +// with the baseURL. +func NewTarballLoaderWithBaseURL(r io.Reader, baseURL string) DirectoryLoader { + l := tarballLoader{ + baseURL: strings.TrimSuffix(baseURL, "/"), + r: r, + pathFormat: Passthrough, + } + return &l +} + +// WithFilter specifies the filter object to use to filter files while loading bundles +func (t *tarballLoader) WithFilter(filter filter.LoaderFilter) DirectoryLoader { + t.filter = filter + return t +} + +// WithPathFormat specifies how a path is formatted in a Descriptor +func (t *tarballLoader) WithPathFormat(pathFormat PathFormat) DirectoryLoader { + t.pathFormat = pathFormat + return t +} + +// WithSizeLimitBytes specifies the maximum size of any file in the tarball to read +func (t *tarballLoader) WithSizeLimitBytes(sizeLimitBytes int64) DirectoryLoader { + t.maxSizeLimitBytes = sizeLimitBytes + return t +} + +// WithFollowSymlinks is a no-op for tarballLoader +func (t *tarballLoader) WithFollowSymlinks(_ bool) DirectoryLoader { + return t +} + +// NextFile iterates to the next file in the directory tree +// and returns a file Descriptor for the file. +func (t *tarballLoader) NextFile() (*Descriptor, error) { + if t.tr == nil { + gr, err := gzip.NewReader(t.r) + if err != nil { + return nil, fmt.Errorf("archive read failed: %w", err) + } + + t.tr = tar.NewReader(gr) + } + + if t.files == nil { + t.files = []file{} + + if t.skipDir == nil { + t.skipDir = map[string]struct{}{} + } + + for { + header, err := t.tr.Next() + + if err == io.EOF { + break + } + + if err != nil { + return nil, err + } + + // Keep iterating on the archive until we find a normal file + if header.Typeflag == tar.TypeReg { + + if t.filter != nil { + + if t.filter(filepath.ToSlash(header.Name), header.FileInfo(), getdepth(header.Name, false)) { + continue + } + + basePath := strings.Trim(filepath.Dir(filepath.ToSlash(header.Name)), "/") + + // check if the directory is to be skipped + if _, ok := t.skipDir[basePath]; ok { + continue + } + + match := false + for p := range t.skipDir { + if strings.HasPrefix(basePath, p) { + match = true + break + } + } + + if match { + continue + } + } + + if t.maxSizeLimitBytes > 0 && header.Size > t.maxSizeLimitBytes { + return nil, fmt.Errorf(maxSizeLimitBytesErrMsg, header.Name, header.Size, t.maxSizeLimitBytes) + } + + f := file{name: header.Name} + + // 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 + + t.files = append(t.files, f) + } else if header.Typeflag == tar.TypeDir { + cleanedPath := filepath.ToSlash(header.Name) + if t.filter != nil && t.filter(cleanedPath, header.FileInfo(), getdepth(header.Name, true)) { + t.skipDir[strings.Trim(cleanedPath, "/")] = struct{}{} + } + } + } + } + + // If done reading files then just return io.EOF + // errors for each NextFile() call + if t.idx >= len(t.files) { + return nil, io.EOF + } + + f := t.files[t.idx] + t.idx++ + + cleanedPath := formatPath(f.name, "", t.pathFormat) + d := NewDescriptor(filepath.Join(t.baseURL, cleanedPath), cleanedPath, f.reader) + return d, nil +} + +// Next implements the storage.Iterator interface. +// It iterates to the next policy or data file in the directory tree +// and returns a storage.Update for the file. +func (it *iterator) Next() (*storage.Update, error) { + if it.files == nil { + it.files = []file{} + + for _, item := range it.raw { + f := file{name: item.Path} + + p, err := getFileStoragePath(f.name) + if err != nil { + return nil, err + } + + f.path = p + + f.raw = item.Value + + it.files = append(it.files, f) + } + + sortFilePathAscend(it.files) + } + + // If done reading files then just return io.EOF + // errors for each NextFile() call + if it.idx >= len(it.files) { + return nil, io.EOF + } + + f := it.files[it.idx] + it.idx++ + + isPolicy := false + if strings.HasSuffix(f.name, RegoExt) { + isPolicy = true + } + + return &storage.Update{ + Path: f.path, + Value: f.raw, + IsPolicy: isPolicy, + }, nil +} + +type iterator struct { + raw []Raw + files []file + idx int +} + +func NewIterator(raw []Raw) storage.Iterator { + it := iterator{ + raw: raw, + } + return &it +} + +func sortFilePathAscend(files []file) { + sort.Slice(files, func(i, j int) bool { + return len(files[i].path) < len(files[j].path) + }) +} + +func getdepth(path string, isDir bool) int { + if isDir { + cleanedPath := strings.Trim(filepath.ToSlash(path), "/") + return len(strings.Split(cleanedPath, "/")) + } + + basePath := strings.Trim(filepath.Dir(filepath.ToSlash(path)), "/") + return len(strings.Split(basePath, "/")) +} + +func getFileStoragePath(path string) (storage.Path, error) { + fpath := strings.TrimLeft(normalizePath(filepath.Dir(path)), "/.") + if strings.HasSuffix(path, RegoExt) { + fpath = strings.Trim(normalizePath(path), "/") + } + + p, ok := storage.ParsePathEscaped("/" + fpath) + if !ok { + return nil, fmt.Errorf("storage path invalid: %v", path) + } + return p, nil +} diff --git a/third_party/opa/v1/bundle/file_bench_test.go b/third_party/opa/v1/bundle/file_bench_test.go new file mode 100644 index 000000000000..cda456dbfef2 --- /dev/null +++ b/third_party/opa/v1/bundle/file_bench_test.go @@ -0,0 +1,130 @@ +package bundle + +import ( + "maps" + "os" + "path/filepath" + "strconv" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/util" + + "github.com/open-policy-agent/opa/v1/util/test" +) + +var benchTestArchiveFiles = map[string]string{ + "/a.json": `"a"`, + "/a/b.json": `"b"`, + "/a/b/c.json": `"c"`, + "/a/b/d/data.json": `"hello"`, + "/a/c/data.yaml": "12", + "/some.txt": "text", + "/policy.rego": "package foo\n p = 1", + "/roles/policy.rego": "package bar\n p = 1", + "/deeper/dir/path/than/others/foo": "bar", +} + +func BenchmarkTarballLoader(b *testing.B) { + files := map[string]string{ + "/archive.tar.gz": "", + } + sizes := []int{1000, 10000, 100000, 250000} + + for _, n := range sizes { + expectedFiles := make(map[string]string, len(benchTestArchiveFiles)+1) + maps.Copy(expectedFiles, benchTestArchiveFiles) + expectedFiles["/x/data.json"] = benchTestGetFlatDataJSON(n) + + // We generate the tarball once in the tempfs, and then reuse it many + // times in the benchmark. + test.WithTempFS(files, func(rootDir string) { + tarballFile := filepath.Join(rootDir, "archive.tar.gz") + benchTestCreateTarballFile(b, rootDir, expectedFiles) + + b.ResetTimer() + + f, err := os.Open(tarballFile) + if err != nil { + b.Fatalf("Unexpected error: %s", err) + } + defer f.Close() + + b.Run(strconv.Itoa(n), func(b *testing.B) { + for range b.N { + // Reset the file reader. + if _, err := f.Seek(0, 0); err != nil { + b.Fatalf("Unexpected error: %s", err) + } + loader := NewTarballLoaderWithBaseURL(f, tarballFile) + benchTestLoader(b, loader) + } + }) + }) + } +} + +func BenchmarkDirectoryLoader(b *testing.B) { + for _, n := range []int{10000, 100000, 250000, 500000} { + expectedFiles := make(map[string]string, len(benchTestArchiveFiles)+1) + maps.Copy(expectedFiles, benchTestArchiveFiles) + expectedFiles["/x/data.json"] = benchTestGetFlatDataJSON(n) + + test.WithTempFS(expectedFiles, func(rootDir string) { + b.ResetTimer() + b.Run(strconv.Itoa(n), func(b *testing.B) { + for range b.N { + benchTestLoader(b, NewDirectoryLoader(rootDir)) + } + }) + }) + } +} + +// Creates a flat JSON object of configurable size. +func benchTestGetFlatDataJSON(numKeys int) string { + largeFile := make(map[string]string, numKeys) + for i := range numKeys { + largeFile[strconv.FormatInt(int64(i), 10)] = strings.Repeat("A", 1024) + } + return string(util.MustMarshalJSON(largeFile)) +} + +// Generates a tarball with a data.json of variable size. +func benchTestCreateTarballFile(b *testing.B, root string, filesToWrite map[string]string) { + b.Helper() + + tarballFile := filepath.Join(root, "archive.tar.gz") + f, err := os.Create(tarballFile) + if err != nil { + b.Fatalf("Unexpected error: %s", err) + } + + gzFiles := make([][2]string, 0, len(filesToWrite)) + for name, content := range filesToWrite { + gzFiles = append(gzFiles, [2]string{name, content}) + } + + _, err = f.Write(archive.MustWriteTarGz(gzFiles).Bytes()) + if err != nil { + b.Fatalf("Unexpected error: %s", err) + } + f.Close() +} + +// We specifically invoke the loader through the bundle reader to mimic +// real-world usage. +func benchTestLoader(b *testing.B, loader DirectoryLoader) { + b.Helper() + + br := NewCustomReader(loader).WithLazyLoadingMode(true) + bundle, err := br.Read() + if err != nil { + b.Fatal(err) + } + + if len(bundle.Raw) == 0 { + b.Fatal("bundle.Raw is unexpectedly empty") + } +} diff --git a/third_party/opa/v1/bundle/file_test.go b/third_party/opa/v1/bundle/file_test.go new file mode 100644 index 000000000000..82e87d174c12 --- /dev/null +++ b/third_party/opa/v1/bundle/file_test.go @@ -0,0 +1,546 @@ +package bundle + +import ( + "bytes" + "io" + "os" + "path/filepath" + "slices" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" + + "github.com/open-policy-agent/opa/v1/loader/filter" + "github.com/open-policy-agent/opa/v1/util/test" +) + +const testReadLimit = (1024 * 1024) + 1 + +var archiveFiles = map[string]string{ + "/a.json": "a", + "/a/b.json": "b", + "/a/b/c.json": "c", + "/a/b/d/data.json": "hello", + "/a/c/data.yaml": "12", + "/some.txt": "text", + "/policy.rego": "package foo\n p = 1", + "/roles/policy.rego": "package bar\n p = 1", + "/deeper/dir/path/than/others/foo": "bar", +} + +func TestTarballLoader(t *testing.T) { + + files := map[string]string{ + "/archive.tar.gz": "", + } + + test.WithTempFS(files, func(rootDir string) { + tarballFile := filepath.Join(rootDir, "archive.tar.gz") + f := testGetTarballFile(t, rootDir) + + loader := NewTarballLoaderWithBaseURL(f, tarballFile) + + defer f.Close() + + testLoader(t, loader, tarballFile, archiveFiles) + }) +} + +func TestIterator(t *testing.T) { + + files := make([][2]string, 0, len(archiveFiles)) + for name, content := range archiveFiles { + files = append(files, [2]string{name, content}) + } + + buf := archive.MustWriteTarGz(files) + bundle, err := NewReader(buf).WithLazyLoadingMode(true).Read() + if err != nil { + t.Fatal(err) + } + + iterator := NewIterator(bundle.Raw) + fileCount := 0 + for { + _, err := iterator.Next() + if err != nil && err != io.EOF { + t.Fatalf("Unexpected error: %s", err) + } else if err == io.EOF { + break + } + fileCount++ + } + + expCount := 4 + if fileCount != expCount { + t.Fatalf("Expected to read %d files, read %d", expCount, fileCount) + } +} + +func TestIteratorOrder(t *testing.T) { + + var archFiles = map[string]string{ + "/a/b/c/data.json": "[1,2,3]", + "/a/b/d/e/data.json": `e: true`, + "/data.json": `{"x": {"y": true}, "a": {"b": {"z": true}}}`, + "/a/b/y/x/z/data.yaml": `foo: 1`, + "/a/b/data.json": "[4,5,6]", + "/a/data.json": "hello", + "/policy.rego": "package foo\n p = 1", + "/roles/policy.rego": "package bar\n p = 1", + } + + files := make([][2]string, 0, len(archFiles)) + for name, content := range archFiles { + files = append(files, [2]string{name, content}) + } + + buf := archive.MustWriteTarGz(files) + bundle, err := NewReader(buf).WithLazyLoadingMode(true).Read() + if err != nil { + t.Fatal(err) + } + + iterator := NewIterator(bundle.Raw) + + fileCount := 0 + actualDataFiles := []string{} + + for { + i, err := iterator.Next() + if err != nil && err != io.EOF { + t.Fatalf("Unexpected error: %s", err) + } else if err == io.EOF { + break + } + fileCount++ + + if !strings.HasSuffix(i.Path.String(), RegoExt) { + actualDataFiles = append(actualDataFiles, i.Path.String()) + } + } + + expCount := 8 + if fileCount != expCount { + t.Fatalf("Expected to read %d files, read %d", expCount, fileCount) + } + + expDataFiles := []string{"/", "/a", "/a/b", "/a/b/c", "/a/b/d/e", "/a/b/y/x/z"} + + if !slices.Equal(expDataFiles, actualDataFiles) { + t.Fatalf("Expected data files %v but got %v", expDataFiles, actualDataFiles) + } +} + +func TestDirectoryLoader(t *testing.T) { + test.WithTempFS(archiveFiles, func(rootDir string) { + loader := NewDirectoryLoader(rootDir) + + testLoader(t, loader, rootDir, archiveFiles) + }) +} + +func TestTarballLoaderWithMaxSizeBytesLimit(t *testing.T) { + rootDir := t.TempDir() + tarballFile := filepath.Join(rootDir, "archive.tar.gz") + + f := testGetTarballFile(t, rootDir) + + loader := NewTarballLoaderWithBaseURL(f, tarballFile).WithSizeLimitBytes(5) + + defer f.Close() + + _, err := loader.NextFile() + if err == nil { + t.Fatal("Expected error but got nil") + } + + // Order of iteration over files in the tarball aren't necessarily in a deterministic order, + // but luckily we have 2 files of 18 bytes. Just skip checking for the name here. + expected := "size (18 bytes) exceeds configured size_limit_bytes (5 bytes)" + + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q but got %v", expected, err) + } +} +func TestTarballLoaderWithFilter(t *testing.T) { + + files := map[string]string{ + "/a/data.json": `{"foo": "not-bar"}`, + "/policy.rego": "package foo\n p = 1", + "/policy_test.rego": "package foo\n test_p { p }", + "/a/b/c/policy.rego": "package bar\n q = 1", + "/a/b/c/policy_test.rego": "package bar\n test_q { q }", + "/a/.manifest": `{"roots": ["a", "foo"]}`, + } + + expectedFiles := map[string]string{ + "/a/data.json": `{"foo": "not-bar"}`, + "/policy.rego": "package foo\n p = 1", + "/a/b/c/policy.rego": "package bar\n q = 1", + "/a/.manifest": `{"roots": ["a", "foo"]}`, + } + + gzFileIn := map[string]string{ + "/archive.tar.gz": "", + } + + test.WithTempFS(gzFileIn, func(rootDir string) { + tarballFile := filepath.Join(rootDir, "archive.tar.gz") + f, err := os.Create(tarballFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + gzFiles := make([][2]string, 0, len(files)) + for name, content := range files { + gzFiles = append(gzFiles, [2]string{name, content}) + } + + _, err = f.Write(archive.MustWriteTarGz(gzFiles).Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + f.Close() + + f, err = os.Open(tarballFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + loader := NewTarballLoaderWithBaseURL(f, tarballFile).WithFilter(func(abspath string, info os.FileInfo, depth int) bool { + return getFilter("*_test.rego", 1)(abspath, info, depth) + }) + + defer f.Close() + + testLoader(t, loader, tarballFile, expectedFiles) + }) +} + +func TestTarballLoaderWithFilterDir(t *testing.T) { + + files := map[string]string{ + "/a/data.json": `{"foo": "not-bar"}`, + "/policy.rego": "package foo\n p = 1", + "/policy_test.rego": "package foo\n test_p { p }", + "/a/b/c/policy.rego": "package bar\n q = 1", + "/a/b/c/policy_test.rego": "package bar\n test_q { q }", + "/a/.manifest": `{"roots": ["a", "foo"]}`, + } + + expectedFiles := map[string]string{ + "/policy.rego": "package foo\n p = 1", + } + + gzFileIn := map[string]string{ + "/archive.tar.gz": "", + } + + test.WithTempFS(gzFileIn, func(rootDir string) { + tarballFile := filepath.Join(rootDir, "archive.tar.gz") + f, err := os.Create(tarballFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + gzFiles := make([][2]string, 0, len(files)) + for name, content := range files { + gzFiles = append(gzFiles, [2]string{name, content}) + } + + _, err = f.Write(archive.MustWriteTarGz(gzFiles).Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + f.Close() + + f, err = os.Open(tarballFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + loader := NewTarballLoaderWithBaseURL(f, tarballFile).WithFilter(func(abspath string, info os.FileInfo, depth int) bool { + return getFilter("*_test.rego", 1)(abspath, info, depth) + }) + + defer f.Close() + + tl, ok := loader.(*tarballLoader) + if !ok { + t.Fatal("Expected tar loader instance") + } + + tl.skipDir = map[string]struct{}{"a": {}} + + fileCount := 0 + for { + f, err := tl.NextFile() + if err != nil && err != io.EOF { + t.Fatalf("Unexpected error: %s", err) + } else if err == io.EOF { + break + } + + expPath := strings.TrimPrefix(f.URL(), tarballFile) + if f.Path() != expPath { + t.Fatalf("Expected path to be %v but got %v", expPath, f.Path()) + } + + _, found := expectedFiles[f.Path()] + if !found { + t.Fatalf("Found unexpected file %s", f.Path()) + } + + fileCount++ + } + + if fileCount != len(expectedFiles) { + t.Fatalf("Expected to read %d files, read %d", len(expectedFiles), fileCount) + } + }) +} + +func TestDirectoryLoaderWithFilter(t *testing.T) { + files := map[string]string{ + "/a/data.json": `{"foo": "not-bar"}`, + "/policy.rego": "package foo\n p = 1", + "/policy_test.rego": "package foo\n test_p { p }", + "/a/b/c/policy.rego": "package bar\n q = 1", + "/a/b/c/policy_test.rego": "package bar\n test_q { q }", + "/a/.manifest": `{"roots": ["a", "foo"]}`, + } + + expectedFiles := map[string]struct{}{ + "/a/data.json": {}, + "/policy.rego": {}, + "/a/b/c/policy.rego": {}, + "/a/.manifest": {}, + } + + test.WithTempFS(files, func(rootDir string) { + + dl := NewDirectoryLoader(rootDir).WithFilter(func(abspath string, info os.FileInfo, depth int) bool { + return getFilter("*_test.rego", 1)(abspath, info, depth) + }) + + fileCount := 0 + for { + f, err := dl.NextFile() + if err != nil && err != io.EOF { + t.Fatalf("Unexpected error: %s", err) + } else if err == io.EOF { + break + } + + expPath := strings.TrimPrefix(f.URL(), rootDir) + if f.Path() != expPath { + t.Fatalf("Expected path to be %v but got %v", expPath, f.Path()) + } + + _, found := expectedFiles[f.Path()] + if !found { + t.Fatalf("Found unexpected file %s", f.Path()) + } + + fileCount++ + } + + if fileCount != len(expectedFiles) { + t.Fatalf("Expected to read %d files, read %d", len(expectedFiles), fileCount) + } + }) +} + +func TestDirectoryLoaderWithFilterDir(t *testing.T) { + files := map[string]string{ + "/a/data.json": `{"foo": "not-bar"}`, + "/policy.rego": "package foo\n p = 1", + "/policy_test.rego": "package foo\n test_p { p }", + "/a/b/c/policy.rego": "package bar\n q = 1", + "/a/b/c/policy_test.rego": "package bar\n test_q { q }", + "/a/.manifest": `{"roots": ["a", "foo"]}`, + } + + expectedFiles := map[string]struct{}{ + "/policy.rego": {}, + "/policy_test.rego": {}, + } + + test.WithTempFS(files, func(rootDir string) { + + dl := NewDirectoryLoader(rootDir).WithFilter(func(abspath string, info os.FileInfo, depth int) bool { + return getFilter("a", 1)(abspath, info, depth) + }) + + fileCount := 0 + for { + f, err := dl.NextFile() + if err != nil && err != io.EOF { + t.Fatalf("Unexpected error: %s", err) + } else if err == io.EOF { + break + } + + expPath := strings.TrimPrefix(f.URL(), rootDir) + if f.Path() != expPath { + t.Fatalf("Expected path to be %v but got %v", expPath, f.Path()) + } + + _, found := expectedFiles[f.Path()] + if !found { + t.Fatalf("Found unexpected file %s", f.Path()) + } + + fileCount++ + } + + if fileCount != len(expectedFiles) { + t.Fatalf("Expected to read %d files, read %d", len(expectedFiles), fileCount) + } + }) + +} + +func testGetTarballFile(t *testing.T, root string) *os.File { + t.Helper() + + tarballFile := filepath.Join(root, "archive.tar.gz") + f, err := os.Create(tarballFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + gzFiles := make([][2]string, 0, len(archiveFiles)) + for name, content := range archiveFiles { + gzFiles = append(gzFiles, [2]string{name, content}) + } + + _, err = f.Write(archive.MustWriteTarGz(gzFiles).Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + f.Close() + + f, err = os.Open(tarballFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + return f +} + +func testLoader(t *testing.T, loader DirectoryLoader, baseURL string, expectedFiles map[string]string) { + t.Helper() + + fileCount := 0 + for { + f, err := loader.NextFile() + if err != nil && err != io.EOF { + t.Fatalf("Unexpected error: %s", err) + } else if err == io.EOF { + break + } + + expPath := strings.TrimPrefix(f.URL(), baseURL) + if f.Path() != expPath { + t.Fatalf("Expected path to be %v but got %v", expPath, f.Path()) + } + + var buf bytes.Buffer + n, err := f.Read(&buf, testReadLimit) + f.Close() // always close, even on error + if err != nil && err != io.EOF { + t.Fatalf("Unexpected error: %s", err) + } else if err == nil && n >= testReadLimit { + t.Fatalf("Attempted to read too much data") + } + + expectedContent, found := expectedFiles[f.Path()] + if !found { + t.Fatalf("Found unexpected file %s", f.Path()) + } + if expectedContent != buf.String() { + t.Fatalf("Content mismatch for file %s\n\nExpected:\n%s\n\nActual:\n%s\n\n", + f.Path(), expectedContent, buf.String()) + } + + fileCount++ + } + + if fileCount != len(expectedFiles) { + t.Fatalf("Expected to read %d files, read %d", len(expectedFiles), fileCount) + } +} + +func TestNewDirectoryLoaderNormalizedRoot(t *testing.T) { + cases := []struct { + note string + root string + expected string + }{ + { + note: "abs", + root: "/a/b/c", + expected: "/a/b/c", + }, + { + note: "trailing slash", + root: "/a/b/c/", + expected: "/a/b/c/", + }, + { + note: "empty", + root: "", + expected: "", + }, + { + note: "single abs", + root: "/", + expected: "/", + }, + { + note: "single relative", + root: "foo", + expected: "foo", + }, + { + note: "single relative dot", + root: ".", + expected: ".", + }, + { + note: "single relative dot slash", + root: "./", + expected: ".", + }, + { + note: "relative leading dot slash", + root: "./a/b/c", + expected: "a/b/c", + }, + { + note: "relative", + root: "a/b/c", + expected: "a/b/c", + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + l := NewDirectoryLoader(tc.root) + actual := l.(*dirLoader).root + if actual != tc.expected { + t.Fatalf("Expected root %s got %s", tc.expected, actual) + } + }) + } +} + +func getFilter(pattern string, minDepth int) filter.LoaderFilter { + return func(_ string, info os.FileInfo, depth int) bool { + match, _ := filepath.Match(pattern, info.Name()) + return match && depth >= minDepth + } +} diff --git a/third_party/opa/v1/bundle/filefs.go b/third_party/opa/v1/bundle/filefs.go new file mode 100644 index 000000000000..7ab3de989c1d --- /dev/null +++ b/third_party/opa/v1/bundle/filefs.go @@ -0,0 +1,143 @@ +//go:build go1.16 +// +build go1.16 + +package bundle + +import ( + "fmt" + "io" + "io/fs" + "path/filepath" + "sync" + + "github.com/open-policy-agent/opa/v1/loader/filter" +) + +const ( + defaultFSLoaderRoot = "." +) + +type dirLoaderFS struct { + sync.Mutex + filesystem fs.FS + files []string + idx int + filter filter.LoaderFilter + root string + pathFormat PathFormat + maxSizeLimitBytes int64 + followSymlinks bool +} + +// NewFSLoader returns a basic DirectoryLoader implementation +// that will load files from a fs.FS interface +func NewFSLoader(filesystem fs.FS) (DirectoryLoader, error) { + return NewFSLoaderWithRoot(filesystem, defaultFSLoaderRoot), nil +} + +// NewFSLoaderWithRoot returns a basic DirectoryLoader implementation +// that will load files from a fs.FS interface at the supplied root +func NewFSLoaderWithRoot(filesystem fs.FS, root string) DirectoryLoader { + d := dirLoaderFS{ + filesystem: filesystem, + root: normalizeRootDirectory(root), + pathFormat: Chrooted, + } + + return &d +} + +func (d *dirLoaderFS) walkDir(path string, dirEntry fs.DirEntry, err error) error { + if err != nil { + return err + } + + if dirEntry != nil { + info, err := dirEntry.Info() + if err != nil { + return err + } + + if dirEntry.Type().IsRegular() { + if d.filter != nil && d.filter(filepath.ToSlash(path), info, getdepth(path, false)) { + return nil + } + + if d.maxSizeLimitBytes > 0 && info.Size() > d.maxSizeLimitBytes { + return fmt.Errorf("file %s size %d exceeds limit of %d", path, info.Size(), d.maxSizeLimitBytes) + } + + d.files = append(d.files, path) + } else if dirEntry.Type()&fs.ModeSymlink != 0 && d.followSymlinks { + if d.filter != nil && d.filter(filepath.ToSlash(path), info, getdepth(path, false)) { + return nil + } + + if d.maxSizeLimitBytes > 0 && info.Size() > d.maxSizeLimitBytes { + return fmt.Errorf("file %s size %d exceeds limit of %d", path, info.Size(), d.maxSizeLimitBytes) + } + + d.files = append(d.files, path) + } else if dirEntry.Type().IsDir() { + if d.filter != nil && d.filter(filepath.ToSlash(path), info, getdepth(path, true)) { + return fs.SkipDir + } + } + } + return nil +} + +// WithFilter specifies the filter object to use to filter files while loading bundles +func (d *dirLoaderFS) WithFilter(filter filter.LoaderFilter) DirectoryLoader { + d.filter = filter + return d +} + +// WithPathFormat specifies how a path is formatted in a Descriptor +func (d *dirLoaderFS) WithPathFormat(pathFormat PathFormat) DirectoryLoader { + d.pathFormat = pathFormat + return d +} + +// WithSizeLimitBytes specifies the maximum size of any file in the filesystem directory to read +func (d *dirLoaderFS) WithSizeLimitBytes(sizeLimitBytes int64) DirectoryLoader { + d.maxSizeLimitBytes = sizeLimitBytes + return d +} + +func (d *dirLoaderFS) WithFollowSymlinks(followSymlinks bool) DirectoryLoader { + d.followSymlinks = followSymlinks + return d +} + +// NextFile iterates to the next file in the directory tree +// and returns a file Descriptor for the file. +func (d *dirLoaderFS) NextFile() (*Descriptor, error) { + d.Lock() + defer d.Unlock() + + if d.files == nil { + err := fs.WalkDir(d.filesystem, d.root, d.walkDir) + if err != nil { + return nil, fmt.Errorf("failed to list files: %w", err) + } + } + + // If done reading files then just return io.EOF + // errors for each NextFile() call + if d.idx >= len(d.files) { + return nil, io.EOF + } + + fileName := d.files[d.idx] + d.idx++ + + fh, err := d.filesystem.Open(fileName) + if err != nil { + return nil, fmt.Errorf("failed to open file %s: %w", fileName, err) + } + + cleanedPath := formatPath(fileName, d.root, d.pathFormat) + f := NewDescriptor(cleanedPath, cleanedPath, fh).WithCloser(fh) + return f, nil +} diff --git a/third_party/opa/v1/bundle/filefs_test.go b/third_party/opa/v1/bundle/filefs_test.go new file mode 100644 index 000000000000..1530b5e47627 --- /dev/null +++ b/third_party/opa/v1/bundle/filefs_test.go @@ -0,0 +1,64 @@ +//go:build go1.16 +// +build go1.16 + +package bundle + +import ( + "os" + "strings" + "testing" + "testing/fstest" +) + +func TestFSLoader(t *testing.T) { + archiveFS := make(fstest.MapFS) + for k, v := range archiveFiles { + file := strings.TrimPrefix(k, "/") + archiveFS[file] = &fstest.MapFile{ + Data: []byte(v), + } + } + + loader, err := NewFSLoader(archiveFS) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + testLoader(t, loader, "", archiveFiles) +} + +func TestFSLoaderWithFilter(t *testing.T) { + files := map[string]string{ + "/a/data.json": `{"foo": "not-bar"}`, + "/policy.rego": "package foo\n p = 1", + "/policy_test.rego": "package foo\n test_p { p }", + "/a/b/c/policy.rego": "package bar\n q = 1", + "/a/b/c/policy_test.rego": "package bar\n test_q { q }", + } + + expectedFiles := map[string]string{ + "/a/data.json": `{"foo": "not-bar"}`, + "/policy.rego": "package foo\n p = 1", + "/a/b/c/policy.rego": "package bar\n q = 1", + } + + archiveFS := make(fstest.MapFS) + + for k, v := range files { + file := strings.TrimPrefix(k, "/") + archiveFS[file] = &fstest.MapFile{ + Data: []byte(v), + } + } + + loader, err := NewFSLoader(archiveFS) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + loader.WithFilter(func(abspath string, info os.FileInfo, depth int) bool { + return getFilter("*_test.rego", 1)(abspath, info, depth) + }) + + testLoader(t, loader, "", expectedFiles) +} diff --git a/third_party/opa/v1/bundle/hash.go b/third_party/opa/v1/bundle/hash.go new file mode 100644 index 000000000000..5a62d2dc004f --- /dev/null +++ b/third_party/opa/v1/bundle/hash.go @@ -0,0 +1,136 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "bytes" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/json" + "fmt" + "hash" + "io" + "strings" + + "github.com/open-policy-agent/opa/v1/util" +) + +// HashingAlgorithm represents a subset of hashing algorithms implemented in Go +type HashingAlgorithm string + +// Supported values for HashingAlgorithm +const ( + MD5 HashingAlgorithm = "MD5" + SHA1 HashingAlgorithm = "SHA-1" + SHA224 HashingAlgorithm = "SHA-224" + SHA256 HashingAlgorithm = "SHA-256" + SHA384 HashingAlgorithm = "SHA-384" + SHA512 HashingAlgorithm = "SHA-512" + SHA512224 HashingAlgorithm = "SHA-512-224" + SHA512256 HashingAlgorithm = "SHA-512-256" +) + +// String returns the string representation of a HashingAlgorithm +func (alg HashingAlgorithm) String() string { + return string(alg) +} + +// SignatureHasher computes a signature digest for a file with (structured or unstructured) data and policy +type SignatureHasher interface { + HashFile(v any) ([]byte, error) +} + +type hasher struct { + h func() hash.Hash // hash function factory +} + +// NewSignatureHasher returns a signature hasher suitable for a particular hashing algorithm +func NewSignatureHasher(alg HashingAlgorithm) (SignatureHasher, error) { + h := &hasher{} + + switch alg { + case MD5: + h.h = md5.New + case SHA1: + h.h = sha1.New + case SHA224: + h.h = sha256.New224 + case SHA256: + h.h = sha256.New + case SHA384: + h.h = sha512.New384 + case SHA512: + h.h = sha512.New + case SHA512224: + h.h = sha512.New512_224 + case SHA512256: + h.h = sha512.New512_256 + default: + return nil, fmt.Errorf("unsupported hashing algorithm: %s", alg) + } + + return h, nil +} + +// HashFile hashes the file content, JSON or binary, both in golang native format. +func (h *hasher) HashFile(v any) ([]byte, error) { + hf := h.h() + walk(v, hf) + return hf.Sum(nil), nil +} + +// walk hashes the file content, JSON or binary, both in golang native format. +// +// Computation for unstructured documents is a hash of the document. +// +// Computation for the types of structured JSON document is as follows: +// +// object: Hash {, then each key (in alphabetical order) and digest of the value, then comma (between items) and finally }. +// +// array: Hash [, then digest of the value, then comma (between items) and finally ]. +func walk(v any, h io.Writer) { + + switch x := v.(type) { + case map[string]any: + _, _ = h.Write([]byte("{")) + + for i, key := range util.KeysSorted(x) { + if i > 0 { + _, _ = h.Write([]byte(",")) + } + + _, _ = h.Write(encodePrimitive(key)) + _, _ = h.Write([]byte(":")) + walk(x[key], h) + } + + _, _ = h.Write([]byte("}")) + case []any: + _, _ = h.Write([]byte("[")) + + for i, e := range x { + if i > 0 { + _, _ = h.Write([]byte(",")) + } + walk(e, h) + } + + _, _ = h.Write([]byte("]")) + case []byte: + _, _ = h.Write(x) + default: + _, _ = h.Write(encodePrimitive(x)) + } +} + +func encodePrimitive(v any) []byte { + var buf bytes.Buffer + encoder := json.NewEncoder(&buf) + encoder.SetEscapeHTML(false) + _ = encoder.Encode(v) + return []byte(strings.Trim(buf.String(), "\n")) +} diff --git a/third_party/opa/v1/bundle/hash_test.go b/third_party/opa/v1/bundle/hash_test.go new file mode 100644 index 000000000000..948426e85a44 --- /dev/null +++ b/third_party/opa/v1/bundle/hash_test.go @@ -0,0 +1,123 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "bytes" + "encoding/json" + "testing" +) + +func TestHashFile(t *testing.T) { + + mapInput := map[string]any{ + "key1": []any{ + "element1", + "element2", + }, + "key2": map[string]any{ + "a": 0, + "b": 1, + "c": json.Number("123.45678911111111111111111111111111111111111111111111111"), + }, + } + + arrayInput := []any{ + []string{"foo", "bar"}, + mapInput, + `package example`, + []string{"$", "α", "©", "™"}, + } + + tests := map[string]struct { + input any + algorithm HashingAlgorithm + }{ + "map": {mapInput, SHA256}, + "array": {arrayInput, MD5}, + "string": {"abc", SHA256}, + "string_with_html_chars": {"", SHA256}, + "null": {`null`, SHA512}, + "bool": {false, SHA256}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + h, _ := NewSignatureHasher(tc.algorithm) + + // compute hash from the raw bytes + a := encodePrimitive(tc.input) + hash := h.(*hasher).h() + hash.Write(a) + d1 := hash.Sum(nil) + + // compute hash on the input + d2, err := h.(*hasher).HashFile(tc.input) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if !bytes.Equal(d1, d2) { + t.Fatalf("Digests are not equal. Expected: %x but got: %x", d1, d2) + } + }) + } +} + +func TestHashFileBytes(t *testing.T) { + + mapInput := map[string]any{ + "key1": []any{ + "element1", + "element2", + }, + "key2": map[string]any{ + "a": 0, + "b": 1, + "c": json.Number("123.45678911111111111111111111111111111111111111111111111"), + }, + } + + arrayInput := []any{ + []string{"foo", "bar"}, + mapInput, + `package example`, + []string{"$", "α", "©", "™"}, + } + + arrayBytes, _ := json.Marshal(arrayInput) + mapBytes, _ := json.Marshal(mapInput) + + tests := map[string]struct { + input []byte + algorithm HashingAlgorithm + }{ + "map_byte_array": {mapBytes, SHA256}, + "array_byte_array": {arrayBytes, MD5}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + h, _ := NewSignatureHasher(tc.algorithm) + + // compute hash from the raw bytes + hash := h.(*hasher).h() + hash.Write(tc.input) + d1 := hash.Sum(nil) + + // compute hash on the input + d2, err := h.(*hasher).HashFile(tc.input) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if !bytes.Equal(d1, d2) { + t.Fatalf("Digests are not equal. Expected: %x but got: %x", d1, d2) + } + }) + } +} diff --git a/third_party/opa/v1/bundle/keys.go b/third_party/opa/v1/bundle/keys.go new file mode 100644 index 000000000000..dbd8ff26971c --- /dev/null +++ b/third_party/opa/v1/bundle/keys.go @@ -0,0 +1,144 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package bundle provide helpers that assist in creating the verification and signing key configuration +package bundle + +import ( + "encoding/pem" + "fmt" + "os" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jws/sign" + "github.com/open-policy-agent/opa/v1/keys" + + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + defaultTokenSigningAlg = "RS256" +) + +// KeyConfig holds the keys used to sign or verify bundles and tokens +// Moved to own package, alias kept for backwards compatibility +type KeyConfig = keys.Config + +// VerificationConfig represents the key configuration used to verify a signed bundle +type VerificationConfig struct { + PublicKeys map[string]*KeyConfig + KeyID string `json:"keyid"` + Scope string `json:"scope"` + Exclude []string `json:"exclude_files"` +} + +// NewVerificationConfig return a new VerificationConfig +func NewVerificationConfig(keys map[string]*KeyConfig, id, scope string, exclude []string) *VerificationConfig { + return &VerificationConfig{ + PublicKeys: keys, + KeyID: id, + Scope: scope, + Exclude: exclude, + } +} + +// ValidateAndInjectDefaults validates the config and inserts default values +func (vc *VerificationConfig) ValidateAndInjectDefaults(keys map[string]*KeyConfig) error { + vc.PublicKeys = keys + + if vc.KeyID != "" { + found := false + for key := range keys { + if key == vc.KeyID { + found = true + break + } + } + + if !found { + return fmt.Errorf("key id %s not found", vc.KeyID) + } + } + return nil +} + +// GetPublicKey returns the public key corresponding to the given key id +func (vc *VerificationConfig) GetPublicKey(id string) (*KeyConfig, error) { + var kc *KeyConfig + var ok bool + + if kc, ok = vc.PublicKeys[id]; !ok { + return nil, fmt.Errorf("verification key corresponding to ID %v not found", id) + } + return kc, nil +} + +// SigningConfig represents the key configuration used to generate a signed bundle +type SigningConfig struct { + Plugin string + Key string + Algorithm string + ClaimsPath string +} + +// NewSigningConfig return a new SigningConfig +func NewSigningConfig(key, alg, claimsPath string) *SigningConfig { + if alg == "" { + alg = defaultTokenSigningAlg + } + + return &SigningConfig{ + Plugin: defaultSignerID, + Key: key, + Algorithm: alg, + ClaimsPath: claimsPath, + } +} + +// WithPlugin sets the signing plugin in the signing config +func (s *SigningConfig) WithPlugin(plugin string) *SigningConfig { + if plugin != "" { + s.Plugin = plugin + } + return s +} + +// GetPrivateKey returns the private key or secret from the signing config +func (s *SigningConfig) GetPrivateKey() (any, error) { + + block, _ := pem.Decode([]byte(s.Key)) + if block != nil { + return sign.GetSigningKey(s.Key, jwa.SignatureAlgorithm(s.Algorithm)) + } + + var priv string + if _, err := os.Stat(s.Key); err == nil { + bs, err := os.ReadFile(s.Key) + if err != nil { + return nil, err + } + priv = string(bs) + } else if os.IsNotExist(err) { + priv = s.Key + } else { + return nil, err + } + + return sign.GetSigningKey(priv, jwa.SignatureAlgorithm(s.Algorithm)) +} + +// GetClaims returns the claims by reading the file specified in the signing config +func (s *SigningConfig) GetClaims() (map[string]any, error) { + var claims map[string]any + + bs, err := os.ReadFile(s.ClaimsPath) + if err != nil { + return claims, err + } + + if err := util.UnmarshalJSON(bs, &claims); err != nil { + return claims, err + } + return claims, nil +} diff --git a/third_party/opa/v1/bundle/keys_test.go b/third_party/opa/v1/bundle/keys_test.go new file mode 100644 index 000000000000..2d108423d2dc --- /dev/null +++ b/third_party/opa/v1/bundle/keys_test.go @@ -0,0 +1,346 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "crypto/ecdsa" + "crypto/rsa" + "errors" + "path/filepath" + "reflect" + "testing" + + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestValidateAndInjectDefaultsVerificationConfig(t *testing.T) { + + tests := map[string]struct { + publicKeys map[string]*KeyConfig + vc *VerificationConfig + wantErr bool + err error + }{ + "valid_config_no_key": { + map[string]*KeyConfig{}, + NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "", nil), + false, nil, + }, + "valid_config_with_key": { + map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, + NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "foo", "", nil), + false, nil, + }, + "valid_config_with_key_not_found": { + map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, + NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "bar", "", nil), + true, errors.New("key id bar not found"), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + err := tc.vc.ValidateAndInjectDefaults(tc.publicKeys) + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if !reflect.DeepEqual(tc.vc.PublicKeys, tc.publicKeys) { + t.Fatalf("Expected public keys %v but got %v", tc.publicKeys, tc.vc.PublicKeys) + } + }) + } +} + +func TestGetPublicKey(t *testing.T) { + tests := map[string]struct { + input string + vc *VerificationConfig + kc *KeyConfig + wantErr bool + err error + }{ + "key_found": { + "foo", + NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "", nil), + &KeyConfig{Key: "secret", Algorithm: "HS256"}, + false, nil, + }, + "key_not_found": { + "foo", + NewVerificationConfig(map[string]*KeyConfig{}, "", "", nil), + nil, + true, errors.New("verification key corresponding to ID foo not found"), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + kc, err := tc.vc.GetPublicKey(tc.input) + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if !reflect.DeepEqual(kc, tc.kc) { + t.Fatalf("Expected key config %v but got %v", tc.kc, kc) + } + }) + } +} + +func TestGetPrivateKey(t *testing.T) { + privateKey := `-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY-----` + + pkcs8privateKey := `-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDa6aUuhBZnXWnm +Cfy8ZIMchsnabGGatVW5LjEQnwHjREArZ49Y/THMcZdnhZ1cn5WuyRyCoUqXd9uv +IOyMH2M2fFPwR1np4ylDpS+AIJbpGIHuqmcvBfMrD1ADn6HoNCwdARyziot+9js+ +A6brtDSbuw+kvTqsndF97rLvzBCxiM1vMGlQ91u6Qcwxrw6eydisRpzXRkSTeJB2 +rR54sVNbV8PY5/LVtjqF5VJ1ZzIgByU1gKuVz7ngX/CLSqM2O4C1mKAet3Ib1+Ws +r9wc0b5EGx1kdKumtfqBSFc78ShuhV1URlbYxs8GpNOcD8szI5hRlLYhb4zq3Viz +OxKjOKmBAgMBAAECggEBAK+DJCxnOo8lFgKZf0iMTZJRfwTgYGDpghE2N6Bb2+ea +kNg773IpjgOcDwew2LmqORgppfIV3vgR4NBIVV8Cy0ij5ah/jFc5CZxyk+LmPhgk +zgfMF25cFtovLLe7BNRm//dBLQHF0pG4WUcfJnVTxdoV4DT0glZjMdMFzfD0a23q +BS+Eu68mItCy6Mll7ys+Z9F1+iTwMCVAxKi7/bavqa5tifhk+i9/9ioGBKo+gbHz +fbOrWGrlfSn1eJX539ekVTA3TnWe+IMHjBSIYJcUtHFa5ngdnG2p0vFSVGwcoQPP +1Sbd1A1qbe6lz25V+Km+QFvYMXC3WBSMykOeFFCweQECgYEA7iQzaU81VUruw3ZQ +3tfbyu79hQZV9WNTJhMi8dlPU7BnAxtZeAXzYf0eTokquphq1KVdtie5IjVg7djO +AaKIXCOB9GdL+l+wwbmgn0iHXbpVrWZCYXd+SC99xdUQs+m28pgt/IF1Dp78bv4G +KFZSDUSmOj0i//AFm72r8aOYe5sCgYEA61RLXewT8eZU784BTVDXgpe4+l/bANFt +jWv0oK7AofoHPZfo+K9cYxZcJazsHc2Cg4KHTeCI1VUAFujO//i1G22Lm5hUKyy3 +v7hWsjruBhUT5hQA7EXzGCaUSMGwD1UsJs3umC56nzr42cL2HRhrX+SWfMipHepy +3y3NED6WxxMCgYAz3vi/0Hv6dxboxmW5FGWQn1vjVMz2ZUsgOPzclwv7W6okeBmV +1h38Uwj97Ey9ViO268osuhxOQjg5toawvnlbMHTHCpT3FU7H86nz5/VsSgENgv+k +gUWlbYrEw7MerSKnVtR1crFPnPu5JWWr9ZlrwG9Asj5kZyChmr/QI2U8TwKBgQDN +BA/w0EYD/T1L+bXKrL5D6Hhfr/i0ur9tcHqbLgNmWdPLBjgRx3x+WrGGpSLDSBIH +DkVgRFgROs8sJkCIYh0tuv7gXBIf1wJyBV+KQKqzI9PFIvI25S3GgX238P24LeSc +HdZaQEvVwuOfmykc6fRJg3TTW2FyTZkr89Pt7gkffwKBgHGeJkFc6LFeHIwa3SbS +qAVebnCAfNo9hHxz3xYA0PaCF3Kr1X9z4X2tF2Za7nWfVbfWViAncLrJgjnHRdrs +f10hbJEuLFhD1c2dNjwqflANV5OanG1syqYqil5TgWm1AaRFj+PbRPk0FRfF9y+e +tKaHBn4eyNlKjQaEn16ZxKJm +-----END PRIVATE KEY-----` + + pkcs1ecPrivateKey := `-----BEGIN EC PRIVATE KEY----- +MHcCAQEEINMW3Ro+oSlbPebDGzeu9w4Eug5ZS/TdjnfnqBP0tMVaoAoGCCqGSM49 +AwEHoUQDQgAEkla2v5uQDXr/WoXdCyD3OfAn21K+suzymtp9qAWqRTXWK0a09/cW +Go/Uf1QsCMvmJJ5n9QZb15mhdReiCy4bNw== +-----END EC PRIVATE KEY-----` + + pkcs8ecPrivateKey := `-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgZAUy0S0Dow25efPX +SXNNy1EFGSxFEjEQMWSo5/PoL16hRANCAAS1MkJ0tCo++7BktJcmXusp55WyB6n1 +qnby6ICFV1o3cV2WFc5PVToBVoPEyUZQ7KFz/3znYQ44fbclemgU/5mf +-----END PRIVATE KEY-----` + + files := map[string]string{ + "private.pem": privateKey, + "pkcs8.pem": pkcs8privateKey, + "pkcs1ec.pem": pkcs1ecPrivateKey, + "pkcs8ec.pem": pkcs8ecPrivateKey, + } + + test.WithTempFS(files, func(rootDir string) { + + sc := NewSigningConfig(filepath.Join(rootDir, "private.pem"), "", "") + + result, err := sc.GetPrivateKey() + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, ok := result.(*rsa.PrivateKey) + if !ok { + t.Fatalf("Expected key type *rsa.PrivateKey but got %T", result) + } + + sc = NewSigningConfig(filepath.Join(rootDir, "pkcs8.pem"), "", "") + + result, err = sc.GetPrivateKey() + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, ok = result.(*rsa.PrivateKey) + if !ok { + t.Fatalf("Expected key type *rsa.PrivateKey but got %T", result) + } + + sc = NewSigningConfig(filepath.Join(rootDir, "pkcs1ec.pem"), "ES256", "") + result, err = sc.GetPrivateKey() + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, ok = result.(*ecdsa.PrivateKey) + if !ok { + t.Fatalf("Expected key type *ecdsa.PrivateKey but got %T", result) + } + + sc = NewSigningConfig(filepath.Join(rootDir, "pkcs8ec.pem"), "ES256", "") + result, err = sc.GetPrivateKey() + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, ok = result.(*ecdsa.PrivateKey) + if !ok { + t.Fatalf("Expected key type *ecdsa.PrivateKey but got %T", result) + } + + // key file does not exist, check that error generated with RS56 as the signing algorithm + sc = NewSigningConfig("private.pem", "", "") + _, err = sc.GetPrivateKey() + if err == nil { + t.Fatal("Expected error but got nil") + } + + errMsg := "failed to parse PEM block containing the key" + if err.Error() != errMsg { + t.Fatalf("Expected error message %v but got %v", errMsg, err.Error()) + } + + // secret provided on command-line + sc = NewSigningConfig("mysecret", "HS256", "") + result, err = sc.GetPrivateKey() + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + hmackey, ok := result.([]byte) + if !ok { + t.Fatalf("Expected key type []byte but got %T", result) + } + + if string(hmackey) != "mysecret" { + t.Fatalf("Expected HMAC key %v but got %v", "mysecret", string(hmackey)) + } + }) +} + +func TestGetPrivateKeyNoFilePrivateKey(t *testing.T) { + + privateKey := `-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCv9GCKxswe5axmZ0XUVoi2JB6fZWtquTMq+EwIHfqDT6Ch252W +sJNF0XxupVdzxLlGax2enE0i4oKf8fNNylX2cRTvHJxlwdJjwZ2oARYScqacZjA5 +JRDDUsuzW9Qqru2fB6lXkgN1Aklzgnf0bkx6CoqwsHBkPVNyts2fpDLFBQIDAQAB +AoGAK4rUIUOU28iGY0kHNMa9SiWiFlvoux5dlTKgzhltFvWrkKJiWxoTN+HhYxgz +jgiOuOhlCg0v4YQgQyiCxytdHhgI+2fwh8upknNMLJdO485wbSe/nOaX1HO49yR/ +LAL3RX4+7pjSdlofGzu+mLaactU4M6i2QxBYj1xSfqJkyTkCQQDkFooNmHZV9cLr +8msT3Dp2QpVJ4/hIwLCSPkPJLGfMUBpW/mBSgrBP1WkF5Us1564WEe9gsX6eZur5 +rlZewYNbAkEAxXyesM/ZYe1j3AlBOnQ69882vyeXK1oou1pHbKv7zbIgTjnusk5N +GeqQfLvB+9MMLF2XVzqfbnBGoLYs0BMnHwJAAxVi7GghQWw/JF10oSIbEDo6NnOE +icdBG9kHpZKaHKMAmCh8OOFXbNzfvJqq96GYMugvKkl8Arw1dQasWD+ZfQJBAMMz +4futBxcXucwFzdbEioDl7hxWOsMcNAS0QMM24Ac62VnZQ4o1gVprk3Pndt++hVrZ +C72p8WsNSZKTX4owVEsCQHIe9bXtAS8M02ftqHpp4Lvvu5R3WdLV2Jg/tCa/YogM +b6giP1ZPqZArXaaZaUXZrWcsYx956X6wA4RkjrvaG40= +-----END RSA PRIVATE KEY-----` + + sc := NewSigningConfig(privateKey, "RS256", "") + + result, err := sc.GetPrivateKey() + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, ok := result.(*rsa.PrivateKey) + if !ok { + t.Fatalf("Expected key type *rsa.PrivateKey but got %T", result) + } +} + +func TestGetClaimsErrors(t *testing.T) { + files := map[string]string{ + "claims.json": `["foo", "read"]`, + } + + test.WithTempFS(files, func(rootDir string) { + // json unmarshal error + sc := NewSigningConfig("secret", "HS256", filepath.Join(rootDir, "claims.json")) + _, err := sc.GetClaims() + if err == nil { + t.Fatal("Expected error but got nil") + } + + // claims.json does not exist + sc = NewSigningConfig("secret", "HS256", "claims.json") + _, err = sc.GetClaims() + if err == nil { + t.Fatal("Expected error but got nil") + } + + errMsg := "open claims.json: no such file or directory" + if err.Error() != errMsg { + t.Fatalf("Expected error message %v but got %v", errMsg, err.Error()) + } + }) +} diff --git a/third_party/opa/v1/bundle/sign.go b/third_party/opa/v1/bundle/sign.go new file mode 100644 index 000000000000..edc41a1e503e --- /dev/null +++ b/third_party/opa/v1/bundle/sign.go @@ -0,0 +1,132 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package bundle provide helpers that assist in the creating a signed bundle +package bundle + +import ( + "crypto/rand" + "encoding/json" + "fmt" + "maps" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jws" +) + +const defaultSignerID = "_default" + +var signers map[string]Signer + +// Signer is the interface expected for implementations that generate bundle signatures. +type Signer interface { + GenerateSignedToken([]FileInfo, *SigningConfig, string) (string, error) +} + +// GenerateSignedToken will retrieve the Signer implementation based on the Plugin specified +// in SigningConfig, and call its implementation of GenerateSignedToken. The signer generates +// a signed token given the list of files to be included in the payload and the bundle +// signing config. The keyID if non-empty, represents the value for the "keyid" claim in the token. +func GenerateSignedToken(files []FileInfo, sc *SigningConfig, keyID string) (string, error) { + var plugin string + // for backwards compatibility, check if there is no plugin specified, and use default + if sc.Plugin == "" { + plugin = defaultSignerID + } else { + plugin = sc.Plugin + } + signer, err := GetSigner(plugin) + if err != nil { + return "", err + } + return signer.GenerateSignedToken(files, sc, keyID) +} + +// DefaultSigner is the default bundle signing implementation. It signs bundles by generating +// a JWT and signing it using a locally-accessible private key. +type DefaultSigner struct{} + +// GenerateSignedToken generates a signed token given the list of files to be +// included in the payload and the bundle signing config. The keyID if non-empty, +// represents the value for the "keyid" claim in the token +func (*DefaultSigner) GenerateSignedToken(files []FileInfo, sc *SigningConfig, keyID string) (string, error) { + payload, err := generatePayload(files, sc, keyID) + if err != nil { + return "", err + } + + privateKey, err := sc.GetPrivateKey() + if err != nil { + return "", err + } + + var headers jws.StandardHeaders + + if err := headers.Set(jws.AlgorithmKey, jwa.SignatureAlgorithm(sc.Algorithm)); err != nil { + return "", err + } + + if keyID != "" { + if err := headers.Set(jws.KeyIDKey, keyID); err != nil { + return "", err + } + } + + hdr, err := json.Marshal(headers) + if err != nil { + return "", err + } + + token, err := jws.SignLiteral(payload, + jwa.SignatureAlgorithm(sc.Algorithm), + privateKey, + hdr, + rand.Reader) + if err != nil { + return "", err + } + return string(token), nil +} + +func generatePayload(files []FileInfo, sc *SigningConfig, keyID string) ([]byte, error) { + payload := make(map[string]any) + payload["files"] = files + + if sc.ClaimsPath != "" { + claims, err := sc.GetClaims() + if err != nil { + return nil, err + } + + maps.Copy(payload, claims) + } else if keyID != "" { + // keyid claim is deprecated but include it for backwards compatibility. + payload["keyid"] = keyID + } + return json.Marshal(payload) +} + +// GetSigner returns the Signer registered under the given id +func GetSigner(id string) (Signer, error) { + signer, ok := signers[id] + if !ok { + return nil, fmt.Errorf("no signer exists under id %s", id) + } + return signer, nil +} + +// RegisterSigner registers a Signer under the given id +func RegisterSigner(id string, s Signer) error { + if id == defaultSignerID { + return fmt.Errorf("signer id %s is reserved, use a different id", id) + } + signers[id] = s + return nil +} + +func init() { + signers = map[string]Signer{ + defaultSignerID: &DefaultSigner{}, + } +} diff --git a/third_party/opa/v1/bundle/sign_test.go b/third_party/opa/v1/bundle/sign_test.go new file mode 100644 index 000000000000..93eede412cb5 --- /dev/null +++ b/third_party/opa/v1/bundle/sign_test.go @@ -0,0 +1,226 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + + "github.com/open-policy-agent/opa/v1/util" + + "github.com/open-policy-agent/opa/v1/util/test" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jws" +) + +func TestGenerateSignedToken(t *testing.T) { + + files := [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + {"/a/b/c/data.json", "[1,2,3]"}, + {"/a/b/d/data.json", "true"}, + {"/a/b/y/data.yaml", `foo: 1`}, + {"/example/example.rego", `package example`}, + {"/policy.wasm", `modules-compiled-as-wasm-binary`}, + {"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`}, + } + + input := []FileInfo{} + + expDigests := make([]string, len(files)) + expDigests[0] = "a005c38a509dc2d5a7407b9494efb2ad" + expDigests[1] = "60f7b5dc86ded48785436192a08dbfd04894d7f1b417c4f8d3714679a7f78cb3c833f16a8559a1cf1f32968747dc1d95ef34826263dacf125ded8f5c374be4c0" + expDigests[2] = "b326b5062b2f0e69046810717534cb09" + expDigests[3] = "20f27a640a233e6524fe7d138898583cd43475724806feb26be7f214e1d10b29edf6a0d3cb08f82107a45686b61b8fdabab6406cf4e70efe134f42238dbd70ab" + expDigests[4] = "655578028abb7b9006e93aff9dda8620" + expDigests[5] = "6347e9be8e3051dc054fbbd3db72fb3f7ae7051c4ef6353e29895aa495452179e10e434fb4a60316e06916464bcc5d4ecabbb2797e04c0213943cf8e69f4c0ae" + expDigests[6] = "36669864a622563256817033b1fc53db" + + for i, f := range files { + file := FileInfo{ + Name: f[0], + Hash: expDigests[i], + } + + if i%2 == 0 { + file.Algorithm = MD5.String() + } else { + file.Algorithm = SHA512.String() + } + + input = append(input, file) + } + + sc := NewSigningConfig("secret", "HS256", "") + token, err := GenerateSignedToken(input, sc, "") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // verify the signed token + _, err = jws.Verify([]byte(token), jwa.SignatureAlgorithm("HS256"), []byte("secret")) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +func TestGenerateSignedTokenWithClaims(t *testing.T) { + + files := [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + {"/a/b/d/data.json", "true"}, + {"/example/example.rego", `package example`}, + {"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}}`}, + } + + input := []FileInfo{} + + expDigests := make([]string, len(files)) + expDigests[0] = "a005c38a509dc2d5a7407b9494efb2ad" + expDigests[1] = "b326b5062b2f0e69046810717534cb09" + expDigests[2] = "655578028abb7b9006e93aff9dda8620" + expDigests[3] = "36669864a622563256817033b1fc53db" + + for i, f := range files { + file := FileInfo{ + Name: f[0], + Hash: expDigests[i], + Algorithm: MD5.String(), + } + input = append(input, file) + } + + test.WithTempFS(map[string]string{}, func(rootDir string) { + claims := make(map[string]any) + claims["scope"] = "read" + + claimBytes, err := json.Marshal(claims) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // create claims file + claimsFile := filepath.Join(rootDir, "claims.json") + if err := os.WriteFile(claimsFile, claimBytes, 0644); err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + keyid := "foo" + + sc := NewSigningConfig("secret", "HS256", filepath.Join(rootDir, "claims.json")) + + token, err := GenerateSignedToken(input, sc, keyid) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // verify the signed token + _, err = jws.Verify([]byte(token), jwa.SignatureAlgorithm("HS256"), []byte("secret")) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // check the kid is in the header + m, err := jws.ParseString(token) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if v, ok := m.GetSignatures()[0].ProtectedHeaders().Get(jws.KeyIDKey); !ok || v != keyid { + t.Errorf("key id not set") + } + }) +} + +func TestGeneratePayload(t *testing.T) { + + files := [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + } + + input := []FileInfo{} + + file := FileInfo{ + Name: files[0][0], + Hash: "a005c38a509dc2d5a7407b9494efb2ad", + Algorithm: MD5.String(), + } + input = append(input, file) + + sc := NewSigningConfig("secret", "HS256", "") + keyID := "default" + + // non-empty key id + bytes, err := generatePayload(input, sc, keyID) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + payload := make(map[string]any) + if err := util.UnmarshalJSON(bytes, &payload); err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if _, ok := payload["keyid"]; !ok { + t.Fatal("Expected claim \"keyid\" in token") + } + + if payload["keyid"] != keyID { + t.Fatalf("Expected key id %v but got %v", keyID, payload["keyid"]) + } + + // empty key id + bytes, err = generatePayload(input, sc, "") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + payload = make(map[string]any) + err = util.UnmarshalJSON(bytes, &payload) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if _, ok := payload["keyid"]; ok { + t.Fatal("Unexpected claim \"keyid\" in token") + } +} + +type CustomSigner struct{} + +func (*CustomSigner) GenerateSignedToken(_ []FileInfo, _ *SigningConfig, _ string) (string, error) { + return "", nil +} + +func TestCustomSigner(t *testing.T) { + custom := &CustomSigner{} + err := RegisterSigner(defaultSignerID, custom) + if err == nil { + t.Fatalf("Expected error when registering with default ID") + } + if err := RegisterSigner("_test", custom); err != nil { + t.Fatal(err) + } + defaultSigner, err := GetSigner(defaultSignerID) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if _, isDefault := defaultSigner.(*DefaultSigner); !isDefault { + t.Fatalf("Expected DefaultSigner to be registered at key %s", defaultSignerID) + } + customSigner, err := GetSigner("_test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if _, isCustom := customSigner.(*CustomSigner); !isCustom { + t.Fatalf("Expected CustomSigner to be registered at key _test") + } + if _, err = GetSigner("_unregistered"); err == nil { + t.Fatalf("Expected error when no Signer exists at provided key") + } +} diff --git a/third_party/opa/v1/bundle/store.go b/third_party/opa/v1/bundle/store.go new file mode 100644 index 000000000000..f203f7086b5f --- /dev/null +++ b/third_party/opa/v1/bundle/store.go @@ -0,0 +1,1245 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "maps" + "path/filepath" + "slices" + "sort" + "strings" + "sync" + + iCompiler "github.com/open-policy-agent/opa/internal/compiler" + "github.com/open-policy-agent/opa/internal/json/patch" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/util" +) + +const defaultActivatorID = "_default" + +var ( + activators = map[string]Activator{ + defaultActivatorID: &DefaultActivator{}, + } + activatorMtx sync.Mutex +) + +// BundlesBasePath is the storage path used for storing bundle metadata +var BundlesBasePath = storage.MustParsePath("/system/bundles") + +var ModulesInfoBasePath = storage.MustParsePath("/system/modules") + +// Note: As needed these helpers could be memoized. + +// ManifestStoragePath is the storage path used for the given named bundle manifest. +func ManifestStoragePath(name string) storage.Path { + return append(BundlesBasePath, name, "manifest") +} + +// EtagStoragePath is the storage path used for the given named bundle etag. +func EtagStoragePath(name string) storage.Path { + return append(BundlesBasePath, name, "etag") +} + +func namedBundlePath(name string) storage.Path { + return append(BundlesBasePath, name) +} + +func rootsPath(name string) storage.Path { + return append(BundlesBasePath, name, "manifest", "roots") +} + +func revisionPath(name string) storage.Path { + return append(BundlesBasePath, name, "manifest", "revision") +} + +func wasmModulePath(name string) storage.Path { + return append(BundlesBasePath, name, "wasm") +} + +func wasmEntrypointsPath(name string) storage.Path { + return append(BundlesBasePath, name, "manifest", "wasm") +} + +func metadataPath(name string) storage.Path { + return append(BundlesBasePath, name, "manifest", "metadata") +} + +func moduleRegoVersionPath(id string) storage.Path { + return append(ModulesInfoBasePath, strings.Trim(id, "/"), "rego_version") +} + +func moduleInfoPath(id string) storage.Path { + return append(ModulesInfoBasePath, strings.Trim(id, "/")) +} + +func read(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path) (any, error) { + value, err := store.Read(ctx, txn, path) + if err != nil { + return nil, err + } + + if astValue, ok := value.(ast.Value); ok { + value, err = ast.JSON(astValue) + if err != nil { + return nil, err + } + } + + return value, nil +} + +// ReadBundleNamesFromStore will return a list of bundle names which have had their metadata stored. +func ReadBundleNamesFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) ([]string, error) { + value, err := read(ctx, store, txn, BundlesBasePath) + if err != nil { + return nil, err + } + + bundleMap, ok := value.(map[string]any) + if !ok { + return nil, errors.New("corrupt manifest roots") + } + + bundles := make([]string, len(bundleMap)) + idx := 0 + for name := range bundleMap { + bundles[idx] = name + idx++ + } + return bundles, nil +} + +// WriteManifestToStore will write the manifest into the storage. This function is called when +// the bundle is activated. +func WriteManifestToStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string, manifest Manifest) error { + return write(ctx, store, txn, ManifestStoragePath(name), manifest) +} + +// WriteEtagToStore will write the bundle etag into the storage. This function is called when the bundle is activated. +func WriteEtagToStore(ctx context.Context, store storage.Store, txn storage.Transaction, name, etag string) error { + return write(ctx, store, txn, EtagStoragePath(name), etag) +} + +func write(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path, value any) error { + if err := util.RoundTrip(&value); err != nil { + return err + } + + var dir []string + if len(path) > 1 { + dir = path[:len(path)-1] + } + + if err := storage.MakeDir(ctx, store, txn, dir); err != nil { + return err + } + + return store.Write(ctx, txn, storage.AddOp, path, value) +} + +// EraseManifestFromStore will remove the manifest from storage. This function is called +// when the bundle is deactivated. +func EraseManifestFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) error { + path := namedBundlePath(name) + err := store.Write(ctx, txn, storage.RemoveOp, path, nil) + return suppressNotFound(err) +} + +// eraseBundleEtagFromStore will remove the bundle etag from storage. This function is called +// when the bundle is deactivated. +func eraseBundleEtagFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) error { + path := EtagStoragePath(name) + err := store.Write(ctx, txn, storage.RemoveOp, path, nil) + return suppressNotFound(err) +} + +func suppressNotFound(err error) error { + if err == nil || storage.IsNotFound(err) { + return nil + } + return err +} + +func writeWasmModulesToStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string, b *Bundle) error { + basePath := wasmModulePath(name) + for _, wm := range b.WasmModules { + path := append(basePath, wm.Path) + err := write(ctx, store, txn, path, base64.StdEncoding.EncodeToString(wm.Raw)) + if err != nil { + return err + } + } + return nil +} + +func eraseWasmModulesFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) error { + path := wasmModulePath(name) + + err := store.Write(ctx, txn, storage.RemoveOp, path, nil) + return suppressNotFound(err) +} + +func eraseModuleRegoVersionsFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, modules []string) error { + for _, module := range modules { + err := store.Write(ctx, txn, storage.RemoveOp, moduleInfoPath(module), nil) + if err := suppressNotFound(err); err != nil { + return err + } + } + return nil +} + +// ReadWasmMetadataFromStore will read Wasm module resolver metadata from the store. +func ReadWasmMetadataFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) ([]WasmResolver, error) { + path := wasmEntrypointsPath(name) + value, err := read(ctx, store, txn, path) + if err != nil { + return nil, err + } + + bs, err := json.Marshal(value) + if err != nil { + return nil, errors.New("corrupt wasm manifest data") + } + + var wasmMetadata []WasmResolver + + err = util.UnmarshalJSON(bs, &wasmMetadata) + if err != nil { + return nil, errors.New("corrupt wasm manifest data") + } + + return wasmMetadata, nil +} + +// ReadWasmModulesFromStore will write Wasm module resolver metadata from the store. +func ReadWasmModulesFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (map[string][]byte, error) { + path := wasmModulePath(name) + value, err := read(ctx, store, txn, path) + if err != nil { + return nil, err + } + + encodedModules, ok := value.(map[string]any) + if !ok { + return nil, errors.New("corrupt wasm modules") + } + + rawModules := map[string][]byte{} + for path, enc := range encodedModules { + encStr, ok := enc.(string) + if !ok { + return nil, errors.New("corrupt wasm modules") + } + bs, err := base64.StdEncoding.DecodeString(encStr) + if err != nil { + return nil, err + } + rawModules[path] = bs + } + return rawModules, nil +} + +// ReadBundleRootsFromStore returns the roots in the specified bundle. +// If the bundle is not activated, this function will return +// storage NotFound error. +func ReadBundleRootsFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) ([]string, error) { + value, err := read(ctx, store, txn, rootsPath(name)) + if err != nil { + return nil, err + } + + sl, ok := value.([]any) + if !ok { + return nil, errors.New("corrupt manifest roots") + } + + roots := make([]string, len(sl)) + + for i := range sl { + roots[i], ok = sl[i].(string) + if !ok { + return nil, errors.New("corrupt manifest root") + } + } + + return roots, nil +} + +// ReadBundleRevisionFromStore returns the revision in the specified bundle. +// If the bundle is not activated, this function will return +// storage NotFound error. +func ReadBundleRevisionFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (string, error) { + return readRevisionFromStore(ctx, store, txn, revisionPath(name)) +} + +func readRevisionFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path) (string, error) { + value, err := read(ctx, store, txn, path) + if err != nil { + return "", err + } + + str, ok := value.(string) + if !ok { + return "", errors.New("corrupt manifest revision") + } + + return str, nil +} + +// ReadBundleMetadataFromStore returns the metadata in the specified bundle. +// If the bundle is not activated, this function will return +// storage NotFound error. +func ReadBundleMetadataFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (map[string]any, error) { + return readMetadataFromStore(ctx, store, txn, metadataPath(name)) +} + +func readMetadataFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path) (map[string]any, error) { + value, err := read(ctx, store, txn, path) + if err != nil { + return nil, suppressNotFound(err) + } + + data, ok := value.(map[string]any) + if !ok { + return nil, errors.New("corrupt manifest metadata") + } + + return data, nil +} + +// ReadBundleEtagFromStore returns the etag for the specified bundle. +// If the bundle is not activated, this function will return +// storage NotFound error. +func ReadBundleEtagFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (string, error) { + return readEtagFromStore(ctx, store, txn, EtagStoragePath(name)) +} + +func readEtagFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path) (string, error) { + value, err := read(ctx, store, txn, path) + if err != nil { + return "", err + } + + str, ok := value.(string) + if !ok { + return "", errors.New("corrupt bundle etag") + } + + return str, nil +} + +// Activator is the interface expected for implementations that activate bundles. +type Activator interface { + Activate(*ActivateOpts) error +} + +// ActivateOpts defines options for the Activate API call. +type ActivateOpts struct { + Ctx context.Context + Store storage.Store + Txn storage.Transaction + TxnCtx *storage.Context + Compiler *ast.Compiler + Metrics metrics.Metrics + Bundles map[string]*Bundle // Optional + ExtraModules map[string]*ast.Module // Optional + AuthorizationDecisionRef ast.Ref + ParserOptions ast.ParserOptions + Plugin string + + legacy bool +} + +type DefaultActivator struct{} + +func (*DefaultActivator) Activate(opts *ActivateOpts) error { + opts.legacy = false + return activateBundles(opts) +} + +// Activate the bundle(s) by loading into the given Store. This will load policies, data, and record +// the manifest in storage. The compiler provided will have had the polices compiled on it. +func Activate(opts *ActivateOpts) error { + plugin := opts.Plugin + + // For backwards compatibility, check if there is no plugin specified, and use default. + if plugin == "" { + // Invoke extension activator if supplied. Otherwise, use default. + if HasExtension() { + plugin = bundleExtActivator + } else { + plugin = defaultActivatorID + } + } + + activator, err := GetActivator(plugin) + if err != nil { + return err + } + + return activator.Activate(opts) +} + +// DeactivateOpts defines options for the Deactivate API call +type DeactivateOpts struct { + Ctx context.Context + Store storage.Store + Txn storage.Transaction + BundleNames map[string]struct{} + ParserOptions ast.ParserOptions +} + +// Deactivate the bundle(s). This will erase associated data, policies, and the manifest entry from the store. +func Deactivate(opts *DeactivateOpts) error { + erase := map[string]struct{}{} + for name := range opts.BundleNames { + roots, err := ReadBundleRootsFromStore(opts.Ctx, opts.Store, opts.Txn, name) + if suppressNotFound(err) != nil { + return err + } + for _, root := range roots { + erase[root] = struct{}{} + } + } + _, err := eraseBundles(opts.Ctx, opts.Store, opts.Txn, opts.ParserOptions, opts.BundleNames, erase) + return err +} + +func activateBundles(opts *ActivateOpts) error { + + // Build collections of bundle names, modules, and roots to erase + erase := map[string]struct{}{} + names := map[string]struct{}{} + deltaBundles := map[string]*Bundle{} + snapshotBundles := map[string]*Bundle{} + + for name, b := range opts.Bundles { + if b.Type() == DeltaBundleType { + deltaBundles[name] = b + } else { + snapshotBundles[name] = b + names[name] = struct{}{} + + roots, err := ReadBundleRootsFromStore(opts.Ctx, opts.Store, opts.Txn, name) + if suppressNotFound(err) != nil { + return err + } + for _, root := range roots { + erase[root] = struct{}{} + } + + // Erase data at new roots to prepare for writing the new data + for _, root := range *b.Manifest.Roots { + erase[root] = struct{}{} + } + } + } + + // Before changing anything make sure the roots don't collide with any + // other bundles that already are activated or other bundles being activated. + err := hasRootsOverlap(opts.Ctx, opts.Store, opts.Txn, opts.Bundles) + if err != nil { + return err + } + + if len(deltaBundles) != 0 { + err := activateDeltaBundles(opts, deltaBundles) + if err != nil { + return err + } + } + + // Erase data and policies at new + old roots, and remove the old + // manifests before activating a new snapshot bundle. + remaining, err := eraseBundles(opts.Ctx, opts.Store, opts.Txn, opts.ParserOptions, names, erase) + if err != nil { + return err + } + + // Validate data in bundle does not contain paths outside the bundle's roots. + for _, b := range snapshotBundles { + + if b.lazyLoadingMode { + + for _, item := range b.Raw { + path := filepath.ToSlash(item.Path) + + if filepath.Base(path) == dataFile || filepath.Base(path) == yamlDataFile { + var val map[string]json.RawMessage + err = util.Unmarshal(item.Value, &val) + if err == nil { + err = doDFS(val, filepath.Dir(strings.Trim(path, "/")), *b.Manifest.Roots) + if err != nil { + return err + } + } else { + // Build an object for the value + p := getNormalizedPath(path) + + if len(p) == 0 { + return errors.New("root value must be object") + } + + // verify valid YAML or JSON value + var x any + err := util.Unmarshal(item.Value, &x) + if err != nil { + return err + } + + value := item.Value + dir := map[string]json.RawMessage{} + for i := len(p) - 1; i > 0; i-- { + dir[p[i]] = value + + bs, err := json.Marshal(dir) + if err != nil { + return err + } + + value = bs + dir = map[string]json.RawMessage{} + } + dir[p[0]] = value + + err = doDFS(dir, filepath.Dir(strings.Trim(path, "/")), *b.Manifest.Roots) + if err != nil { + return err + } + } + } + } + } + } + + // Compile the modules all at once to avoid having to re-do work. + remainingAndExtra := make(map[string]*ast.Module) + maps.Copy(remainingAndExtra, remaining) + maps.Copy(remainingAndExtra, opts.ExtraModules) + + err = compileModules(opts.Compiler, opts.Metrics, snapshotBundles, remainingAndExtra, opts.legacy, opts.AuthorizationDecisionRef) + if err != nil { + return err + } + + if err := writeDataAndModules(opts.Ctx, opts.Store, opts.Txn, opts.TxnCtx, snapshotBundles, opts.legacy, opts.ParserOptions.RegoVersion); err != nil { + return err + } + + if err := ast.CheckPathConflicts(opts.Compiler, storage.NonEmpty(opts.Ctx, opts.Store, opts.Txn)); len(err) > 0 { + return err + } + + for name, b := range snapshotBundles { + if err := writeManifestToStore(opts, name, b.Manifest); err != nil { + return err + } + + if err := writeEtagToStore(opts, name, b.Etag); err != nil { + return err + } + + if err := writeWasmModulesToStore(opts.Ctx, opts.Store, opts.Txn, name, b); err != nil { + return err + } + } + + return nil +} + +func doDFS(obj map[string]json.RawMessage, path string, roots []string) error { + if len(roots) == 1 && roots[0] == "" { + return nil + } + + for key := range obj { + + newPath := filepath.Join(strings.Trim(path, "/"), key) + + // Note: filepath.Join can return paths with '\' separators, always use + // filepath.ToSlash to keep them normalized. + newPath = strings.TrimLeft(normalizePath(newPath), "/.") + + contains := false + prefix := false + if RootPathsContain(roots, newPath) { + contains = true + } else { + for i := range roots { + if strings.HasPrefix(strings.Trim(roots[i], "/"), newPath) { + prefix = true + break + } + } + } + + if !contains && !prefix { + return fmt.Errorf("manifest roots %v do not permit data at path '/%s' (hint: check bundle directory structure)", roots, newPath) + } + + if contains { + continue + } + + var next map[string]json.RawMessage + err := util.Unmarshal(obj[key], &next) + if err != nil { + return fmt.Errorf("manifest roots %v do not permit data at path '/%s' (hint: check bundle directory structure)", roots, newPath) + } + + if err := doDFS(next, newPath, roots); err != nil { + return err + } + } + return nil +} + +func activateDeltaBundles(opts *ActivateOpts, bundles map[string]*Bundle) error { + + // Check that the manifest roots and wasm resolvers in the delta bundle + // match with those currently in the store + for name, b := range bundles { + value, err := opts.Store.Read(opts.Ctx, opts.Txn, ManifestStoragePath(name)) + if err != nil { + if storage.IsNotFound(err) { + continue + } + return err + } + + manifest, err := valueToManifest(value) + if err != nil { + return fmt.Errorf("corrupt manifest data: %w", err) + } + + if !b.Manifest.equalWasmResolversAndRoots(manifest) { + return fmt.Errorf("delta bundle '%s' has wasm resolvers or manifest roots that are different from those in the store", name) + } + } + + for _, b := range bundles { + err := applyPatches(opts.Ctx, opts.Store, opts.Txn, b.Patch.Data) + if err != nil { + return err + } + } + + if err := ast.CheckPathConflicts(opts.Compiler, storage.NonEmpty(opts.Ctx, opts.Store, opts.Txn)); len(err) > 0 { + return err + } + + for name, b := range bundles { + if err := writeManifestToStore(opts, name, b.Manifest); err != nil { + return err + } + + if err := writeEtagToStore(opts, name, b.Etag); err != nil { + return err + } + } + + return nil +} + +func valueToManifest(v any) (Manifest, error) { + if astV, ok := v.(ast.Value); ok { + var err error + v, err = ast.JSON(astV) + if err != nil { + return Manifest{}, err + } + } + + var manifest Manifest + + bs, err := json.Marshal(v) + if err != nil { + return Manifest{}, err + } + + err = util.UnmarshalJSON(bs, &manifest) + if err != nil { + return Manifest{}, err + } + + return manifest, nil +} + +// erase bundles by name and roots. This will clear all policies and data at its roots and remove its +// manifest from storage. +func eraseBundles(ctx context.Context, store storage.Store, txn storage.Transaction, parserOpts ast.ParserOptions, names map[string]struct{}, roots map[string]struct{}) (map[string]*ast.Module, error) { + + if err := eraseData(ctx, store, txn, roots); err != nil { + return nil, err + } + + remaining, removed, err := erasePolicies(ctx, store, txn, parserOpts, roots) + if err != nil { + return nil, err + } + + for name := range names { + if err := EraseManifestFromStore(ctx, store, txn, name); suppressNotFound(err) != nil { + return nil, err + } + + if err := LegacyEraseManifestFromStore(ctx, store, txn); suppressNotFound(err) != nil { + return nil, err + } + + if err := eraseBundleEtagFromStore(ctx, store, txn, name); suppressNotFound(err) != nil { + return nil, err + } + + if err := eraseWasmModulesFromStore(ctx, store, txn, name); suppressNotFound(err) != nil { + return nil, err + } + } + + err = eraseModuleRegoVersionsFromStore(ctx, store, txn, removed) + if err != nil { + return nil, err + } + + return remaining, nil +} + +func eraseData(ctx context.Context, store storage.Store, txn storage.Transaction, roots map[string]struct{}) error { + for root := range roots { + path, ok := storage.ParsePathEscaped("/" + root) + if !ok { + return fmt.Errorf("manifest root path invalid: %v", root) + } + + if len(path) > 0 { + if err := store.Write(ctx, txn, storage.RemoveOp, path, nil); suppressNotFound(err) != nil { + return err + } + } + } + return nil +} + +type moduleInfo struct { + RegoVersion ast.RegoVersion `json:"rego_version"` +} + +func readModuleInfoFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) (map[string]moduleInfo, error) { + value, err := read(ctx, store, txn, ModulesInfoBasePath) + if suppressNotFound(err) != nil { + return nil, err + } + + if value == nil { + return nil, nil + } + + if m, ok := value.(map[string]any); ok { + versions := make(map[string]moduleInfo, len(m)) + + for k, v := range m { + if m0, ok := v.(map[string]any); ok { + if ver, ok := m0["rego_version"]; ok { + if vs, ok := ver.(json.Number); ok { + i, err := vs.Int64() + if err != nil { + return nil, errors.New("corrupt rego version") + } + versions[k] = moduleInfo{RegoVersion: ast.RegoVersionFromInt(int(i))} + } + } + } + } + return versions, nil + } + + return nil, errors.New("corrupt rego version") +} + +func erasePolicies(ctx context.Context, store storage.Store, txn storage.Transaction, parserOpts ast.ParserOptions, roots map[string]struct{}) (map[string]*ast.Module, []string, error) { + + ids, err := store.ListPolicies(ctx, txn) + if err != nil { + return nil, nil, err + } + + modulesInfo, err := readModuleInfoFromStore(ctx, store, txn) + if err != nil { + return nil, nil, fmt.Errorf("failed to read module info from store: %w", err) + } + + getRegoVersion := func(modId string) (ast.RegoVersion, bool) { + info, ok := modulesInfo[modId] + if !ok { + return ast.RegoUndefined, false + } + return info.RegoVersion, true + } + + remaining := map[string]*ast.Module{} + var removed []string + + for _, id := range ids { + bs, err := store.GetPolicy(ctx, txn, id) + if err != nil { + return nil, nil, err + } + + parserOptsCpy := parserOpts + if regoVersion, ok := getRegoVersion(id); ok { + parserOptsCpy.RegoVersion = regoVersion + } + + module, err := ast.ParseModuleWithOpts(id, string(bs), parserOptsCpy) + if err != nil { + return nil, nil, err + } + path, err := module.Package.Path.Ptr() + if err != nil { + return nil, nil, err + } + deleted := false + for root := range roots { + if RootPathsContain([]string{root}, path) { + if err := store.DeletePolicy(ctx, txn, id); err != nil { + return nil, nil, err + } + deleted = true + break + } + } + + if deleted { + removed = append(removed, id) + } else { + remaining[id] = module + } + } + + return remaining, removed, nil +} + +func writeManifestToStore(opts *ActivateOpts, name string, manifest Manifest) error { + // Always write manifests to the named location. If the plugin is in the older style config + // then also write to the old legacy unnamed location. + if err := WriteManifestToStore(opts.Ctx, opts.Store, opts.Txn, name, manifest); err != nil { + return err + } + + if opts.legacy { + if err := LegacyWriteManifestToStore(opts.Ctx, opts.Store, opts.Txn, manifest); err != nil { + return err + } + } + + return nil +} + +func writeEtagToStore(opts *ActivateOpts, name, etag string) error { + if err := WriteEtagToStore(opts.Ctx, opts.Store, opts.Txn, name, etag); err != nil { + return err + } + + return nil +} + +func writeModuleRegoVersionToStore(ctx context.Context, store storage.Store, txn storage.Transaction, b *Bundle, + mf ModuleFile, storagePath string, runtimeRegoVersion ast.RegoVersion) error { + + var regoVersion ast.RegoVersion + if mf.Parsed != nil { + regoVersion = mf.Parsed.RegoVersion() + } + + if regoVersion == ast.RegoUndefined { + var err error + regoVersion, err = b.RegoVersionForFile(mf.Path, runtimeRegoVersion) + if err != nil { + return fmt.Errorf("failed to get rego version for module '%s' in bundle: %w", mf.Path, err) + } + } + + if regoVersion != ast.RegoUndefined && regoVersion != runtimeRegoVersion { + if err := write(ctx, store, txn, moduleRegoVersionPath(storagePath), regoVersion.Int()); err != nil { + return fmt.Errorf("failed to write rego version for module '%s': %w", storagePath, err) + } + } + return nil +} + +func writeDataAndModules(ctx context.Context, store storage.Store, txn storage.Transaction, txnCtx *storage.Context, bundles map[string]*Bundle, legacy bool, runtimeRegoVersion ast.RegoVersion) error { + params := storage.WriteParams + params.Context = txnCtx + + for name, b := range bundles { + if len(b.Raw) == 0 { + // Write data from each new bundle into the store. Only write under the + // roots contained in their manifest. + if err := writeData(ctx, store, txn, *b.Manifest.Roots, b.Data); err != nil { + return err + } + + for _, mf := range b.Modules { + var path string + + // For backwards compatibility, in legacy mode, upsert policies to + // the unprefixed path. + if legacy { + path = mf.Path + } else { + path = modulePathWithPrefix(name, mf.Path) + } + + if err := store.UpsertPolicy(ctx, txn, path, mf.Raw); err != nil { + return err + } + + if err := writeModuleRegoVersionToStore(ctx, store, txn, b, mf, path, runtimeRegoVersion); err != nil { + return err + } + } + } else { + params.BasePaths = *b.Manifest.Roots + + err := store.Truncate(ctx, txn, params, NewIterator(b.Raw)) + if err != nil { + return fmt.Errorf("store truncate failed for bundle '%s': %v", name, err) + } + + for _, f := range b.Raw { + if strings.HasSuffix(f.Path, RegoExt) { + p, err := getFileStoragePath(f.Path) + if err != nil { + return fmt.Errorf("failed get storage path for module '%s' in bundle '%s': %w", f.Path, name, err) + } + + if m := f.module; m != nil { + // 'f.module.Path' contains the module's path as it relates to the bundle root, and can be used for looking up the rego-version. + // 'f.Path' can differ, based on how the bundle reader was initialized. + if err := writeModuleRegoVersionToStore(ctx, store, txn, b, *m, p.String(), runtimeRegoVersion); err != nil { + return err + } + } + } + } + } + } + + return nil +} + +func writeData(ctx context.Context, store storage.Store, txn storage.Transaction, roots []string, data map[string]any) error { + for _, root := range roots { + path, ok := storage.ParsePathEscaped("/" + root) + if !ok { + return fmt.Errorf("manifest root path invalid: %v", root) + } + if value, ok := lookup(path, data); ok { + if len(path) > 0 { + if err := storage.MakeDir(ctx, store, txn, path[:len(path)-1]); err != nil { + return err + } + } + if err := store.Write(ctx, txn, storage.AddOp, path, value); err != nil { + return err + } + } + } + return nil +} + +func compileModules(compiler *ast.Compiler, m metrics.Metrics, bundles map[string]*Bundle, extraModules map[string]*ast.Module, legacy bool, authorizationDecisionRef ast.Ref) error { + + m.Timer(metrics.RegoModuleCompile).Start() + defer m.Timer(metrics.RegoModuleCompile).Stop() + + modules := map[string]*ast.Module{} + + // preserve any modules already on the compiler + maps.Copy(modules, compiler.Modules) + + // preserve any modules passed in from the store + maps.Copy(modules, extraModules) + + // include all the new bundle modules + for bundleName, b := range bundles { + if legacy { + for _, mf := range b.Modules { + modules[mf.Path] = mf.Parsed + } + } else { + maps.Copy(modules, b.ParsedModules(bundleName)) + } + } + + if compiler.Compile(modules); compiler.Failed() { + return compiler.Errors + } + + if authorizationDecisionRef.Equal(ast.EmptyRef()) { + return nil + } + + return iCompiler.VerifyAuthorizationPolicySchema(compiler, authorizationDecisionRef) +} + +func writeModules(ctx context.Context, store storage.Store, txn storage.Transaction, compiler *ast.Compiler, m metrics.Metrics, bundles map[string]*Bundle, extraModules map[string]*ast.Module, legacy bool) error { + + m.Timer(metrics.RegoModuleCompile).Start() + defer m.Timer(metrics.RegoModuleCompile).Stop() + + modules := map[string]*ast.Module{} + + // preserve any modules already on the compiler + maps.Copy(modules, compiler.Modules) + + // preserve any modules passed in from the store + maps.Copy(modules, extraModules) + + // include all the new bundle modules + for bundleName, b := range bundles { + if legacy { + for _, mf := range b.Modules { + modules[mf.Path] = mf.Parsed + } + } else { + maps.Copy(modules, b.ParsedModules(bundleName)) + } + } + + if compiler.Compile(modules); compiler.Failed() { + return compiler.Errors + } + for bundleName, b := range bundles { + for _, mf := range b.Modules { + var path string + + // For backwards compatibility, in legacy mode, upsert policies to + // the unprefixed path. + if legacy { + path = mf.Path + } else { + path = modulePathWithPrefix(bundleName, mf.Path) + } + + if err := store.UpsertPolicy(ctx, txn, path, mf.Raw); err != nil { + return err + } + } + } + return nil +} + +func lookup(path storage.Path, data map[string]any) (any, bool) { + if len(path) == 0 { + return data, true + } + for i := range len(path) - 1 { + value, ok := data[path[i]] + if !ok { + return nil, false + } + obj, ok := value.(map[string]any) + if !ok { + return nil, false + } + data = obj + } + value, ok := data[path[len(path)-1]] + return value, ok +} + +func hasRootsOverlap(ctx context.Context, store storage.Store, txn storage.Transaction, newBundles map[string]*Bundle) error { + storeBundles, err := ReadBundleNamesFromStore(ctx, store, txn) + if suppressNotFound(err) != nil { + return err + } + + allRoots := map[string][]string{} + bundlesWithEmptyRoots := map[string]bool{} + + // Build a map of roots for existing bundles already in the system + for _, name := range storeBundles { + roots, err := ReadBundleRootsFromStore(ctx, store, txn, name) + if suppressNotFound(err) != nil { + return err + } + allRoots[name] = roots + if slices.Contains(roots, "") { + bundlesWithEmptyRoots[name] = true + } + } + + // Add in any bundles that are being activated, overwrite existing roots + // with new ones where bundles are in both groups. + for name, bundle := range newBundles { + allRoots[name] = *bundle.Manifest.Roots + if slices.Contains(*bundle.Manifest.Roots, "") { + bundlesWithEmptyRoots[name] = true + } + } + + // Now check for each new bundle if it conflicts with any of the others + collidingBundles := map[string]bool{} + conflictSet := map[string]bool{} + for name, bundle := range newBundles { + for otherBundle, otherRoots := range allRoots { + if name == otherBundle { + // Skip the current bundle being checked + continue + } + + // Compare the "new" roots with other existing (or a different bundles new roots) + for _, newRoot := range *bundle.Manifest.Roots { + for _, otherRoot := range otherRoots { + if !RootPathsOverlap(newRoot, otherRoot) { + continue + } + + collidingBundles[name] = true + collidingBundles[otherBundle] = true + + // Different message required if the roots are same + if newRoot == otherRoot { + conflictSet[fmt.Sprintf("root %s is in multiple bundles", newRoot)] = true + } else { + paths := []string{newRoot, otherRoot} + sort.Strings(paths) + conflictSet[fmt.Sprintf("%s overlaps %s", paths[0], paths[1])] = true + } + } + } + } + } + + if len(collidingBundles) == 0 { + return nil + } + + bundleNames := strings.Join(util.KeysSorted(collidingBundles), ", ") + + if len(bundlesWithEmptyRoots) > 0 { + return fmt.Errorf( + "bundles [%s] have overlapping roots and cannot be activated simultaneously because bundle(s) [%s] specify empty root paths ('') which overlap with any other bundle root", + bundleNames, + strings.Join(util.KeysSorted(bundlesWithEmptyRoots), ", "), + ) + } + + return fmt.Errorf("detected overlapping roots in manifests for these bundles: [%s] (%s)", bundleNames, strings.Join(util.KeysSorted(conflictSet), ", ")) +} + +func applyPatches(ctx context.Context, store storage.Store, txn storage.Transaction, patches []PatchOperation) error { + for _, pat := range patches { + + // construct patch path + path, ok := patch.ParsePatchPathEscaped("/" + strings.Trim(pat.Path, "/")) + if !ok { + return errors.New("error parsing patch path") + } + + var op storage.PatchOp + switch pat.Op { + case "upsert": + op = storage.AddOp + + _, err := store.Read(ctx, txn, path[:len(path)-1]) + if err != nil { + if !storage.IsNotFound(err) { + return err + } + + if err := storage.MakeDir(ctx, store, txn, path[:len(path)-1]); err != nil { + return err + } + } + case "remove": + op = storage.RemoveOp + case "replace": + op = storage.ReplaceOp + default: + return fmt.Errorf("bad patch operation: %v", pat.Op) + } + + // apply the patch + if err := store.Write(ctx, txn, op, path, pat.Value); err != nil { + return err + } + } + + return nil +} + +// Helpers for the older single (unnamed) bundle style manifest storage. + +// LegacyManifestStoragePath is the older unnamed bundle path for manifests to be stored. +// Deprecated: Use ManifestStoragePath and named bundles instead. +var legacyManifestStoragePath = storage.MustParsePath("/system/bundle/manifest") +var legacyRevisionStoragePath = append(legacyManifestStoragePath, "revision") + +// LegacyWriteManifestToStore will write the bundle manifest to the older single (unnamed) bundle manifest location. +// Deprecated: Use WriteManifestToStore and named bundles instead. +func LegacyWriteManifestToStore(ctx context.Context, store storage.Store, txn storage.Transaction, manifest Manifest) error { + return write(ctx, store, txn, legacyManifestStoragePath, manifest) +} + +// LegacyEraseManifestFromStore will erase the bundle manifest from the older single (unnamed) bundle manifest location. +// Deprecated: Use WriteManifestToStore and named bundles instead. +func LegacyEraseManifestFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) error { + err := store.Write(ctx, txn, storage.RemoveOp, legacyManifestStoragePath, nil) + if err != nil { + return err + } + return nil +} + +// LegacyReadRevisionFromStore will read the bundle manifest revision from the older single (unnamed) bundle manifest location. +// Deprecated: Use ReadBundleRevisionFromStore and named bundles instead. +func LegacyReadRevisionFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) (string, error) { + return readRevisionFromStore(ctx, store, txn, legacyRevisionStoragePath) +} + +// ActivateLegacy calls Activate for the bundles but will also write their manifest to the older unnamed store location. +// Deprecated: Use Activate with named bundles instead. +func ActivateLegacy(opts *ActivateOpts) error { + opts.legacy = true + return activateBundles(opts) +} + +// GetActivator returns the Activator registered under the given id +func GetActivator(id string) (Activator, error) { + activator, ok := activators[id] + + if !ok { + return nil, fmt.Errorf("no activator exists under id %s", id) + } + + return activator, nil +} + +// RegisterActivator registers a bundle Activator under the given id. +// The id value can later be referenced in ActivateOpts.Plugin to specify +// which activator should be used for that bundle activation operation. +// Note: This must be called *before* RegisterDefaultBundleActivator. +func RegisterActivator(id string, a Activator) { + activatorMtx.Lock() + defer activatorMtx.Unlock() + + if id == defaultActivatorID { + panic("cannot use reserved activator id, use a different id") + } + + activators[id] = a +} diff --git a/third_party/opa/v1/bundle/store_test.go b/third_party/opa/v1/bundle/store_test.go new file mode 100644 index 000000000000..517e4d0106ad --- /dev/null +++ b/third_party/opa/v1/bundle/store_test.go @@ -0,0 +1,7179 @@ +package bundle + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "os" + "path/filepath" + "reflect" + "slices" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/internal/storage/mock" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" + + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/disk" + "github.com/open-policy-agent/opa/v1/storage/inmem" + inmemtst "github.com/open-policy-agent/opa/v1/storage/inmem/test" +) + +func TestManifestStoreLifecycleSingleBundle(t *testing.T) { + store := inmemtst.New() + ctx := context.Background() + tb := Manifest{ + Revision: "abc123", + Roots: &[]string{"/a/b", "/a/c"}, + } + name := "test_bundle" + verifyWriteManifests(ctx, t, store, map[string]Manifest{name: tb}) // write one + verifyReadBundleNames(ctx, t, store, []string{name}) // read one + verifyDeleteManifest(ctx, t, store, name) // delete it + verifyReadBundleNames(ctx, t, store, []string{}) // ensure it was removed +} + +func TestManifestStoreLifecycleMultiBundle(t *testing.T) { + store := inmemtst.New() + ctx := context.Background() + + bundles := map[string]Manifest{ + "bundle1": { + Revision: "abc123", + Roots: &[]string{"/a/b", "/a/c"}, + }, + "bundle2": { + Revision: "def123", + Roots: &[]string{"/x/y", "/z"}, + }, + } + + verifyWriteManifests(ctx, t, store, bundles) // write multiple + verifyReadBundleNames(ctx, t, store, []string{"bundle1", "bundle2"}) // read them + verifyDeleteManifest(ctx, t, store, "bundle1") // delete one + verifyReadBundleNames(ctx, t, store, []string{"bundle2"}) // ensure it was removed + verifyDeleteManifest(ctx, t, store, "bundle2") // delete the last one + verifyReadBundleNames(ctx, t, store, []string{}) // ensure it was removed +} + +func TestLegacyManifestStoreLifecycle(t *testing.T) { + store := inmemtst.New() + ctx := context.Background() + tb := Manifest{ + Revision: "abc123", + Roots: &[]string{"/a/b", "/a/c"}, + } + + // write a "legacy" manifest + err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + if err := LegacyWriteManifestToStore(ctx, store, txn, tb); err != nil { + t.Fatalf("Failed to write manifest to store: %s", err) + return err + } + return nil + }) + if err != nil { + t.Fatalf("Unexpected error finishing transaction: %s", err) + } + + // make sure it can be retrieved + verifyReadLegacyRevision(ctx, t, store, tb.Revision) + + // delete it + err = storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + if err := LegacyEraseManifestFromStore(ctx, store, txn); err != nil { + t.Fatalf("Failed to erase manifest from store: %s", err) + return err + } + return nil + }) + if err != nil { + t.Fatalf("Unexpected error finishing transaction: %s", err) + } + + verifyReadLegacyRevision(ctx, t, store, "") +} + +func TestMixedManifestStoreLifecycle(t *testing.T) { + store := inmemtst.New() + ctx := context.Background() + bundles := map[string]Manifest{ + "bundle1": { + Revision: "abc123", + Roots: &[]string{"/a/b", "/a/c"}, + }, + "bundle2": { + Revision: "def123", + Roots: &[]string{"/x/y", "/z"}, + }, + } + + // Write the legacy one first + err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + if err := LegacyWriteManifestToStore(ctx, store, txn, bundles["bundle1"]); err != nil { + t.Fatalf("Failed to write manifest to store: %s", err) + return err + } + return nil + }) + if err != nil { + t.Fatalf("Unexpected error finishing transaction: %s", err) + } + + verifyReadBundleNames(ctx, t, store, []string{}) + + // Write both new ones + verifyWriteManifests(ctx, t, store, bundles) + verifyReadBundleNames(ctx, t, store, []string{"bundle1", "bundle2"}) + + // Ensure the original legacy one is still there + verifyReadLegacyRevision(ctx, t, store, bundles["bundle1"].Revision) +} + +func verifyDeleteManifest(ctx context.Context, t *testing.T, store storage.Store, name string) { + err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + err := EraseManifestFromStore(ctx, store, txn, name) + if err != nil { + t.Fatalf("Failed to delete manifest from store: %s", err) + } + return err + }) + if err != nil { + t.Fatalf("Unexpected error finishing transaction: %s", err) + } +} + +func verifyWriteManifests(ctx context.Context, t *testing.T, store storage.Store, bundles map[string]Manifest) { + t.Helper() + for name, manifest := range bundles { + err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + err := WriteManifestToStore(ctx, store, txn, name, manifest) + if err != nil { + t.Fatalf("Failed to write manifest to store: %s", err) + } + return err + }) + if err != nil { + t.Fatalf("Unexpected error finishing transaction: %s", err) + } + } +} + +func verifyReadBundleNames(ctx context.Context, t *testing.T, store storage.Store, expected []string) { + t.Helper() + var actualNames []string + err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + var err error + actualNames, err = ReadBundleNamesFromStore(ctx, store, txn) + if err != nil && !storage.IsNotFound(err) { + t.Fatalf("Failed to read manifest names from store: %s", err) + return err + } + return nil + }) + if err != nil { + t.Fatalf("Unexpected error finishing transaction: %s", err) + } + + if len(actualNames) != len(expected) { + t.Fatalf("Expected %d name, found %d \n\t\tActual: %v\n", len(expected), len(actualNames), actualNames) + } + + for _, actualName := range actualNames { + found := slices.Contains(expected, actualName) + if !found { + t.Errorf("Found unexpecxted bundle name %s, expected names: %+v", actualName, expected) + } + } +} + +func verifyReadLegacyRevision(ctx context.Context, t *testing.T, store storage.Store, expected string) { + t.Helper() + var actual string + err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + var err error + if actual, err = LegacyReadRevisionFromStore(ctx, store, txn); err != nil && !storage.IsNotFound(err) { + t.Fatalf("Failed to read manifest revision from store: %s", err) + return err + } + return nil + }) + if err != nil { + t.Fatalf("Unexpected error finishing transaction: %s", err) + } + + if actual != expected { + t.Fatalf("Expected revision %s, got %s", expected, actual) + } +} + +func TestBundleLazyModeNoPolicyOrData(t *testing.T) { + ctx := context.Background() + mockStore := mock.New() + + compiler := ast.NewCompiler() + m := metrics.New() + + bundles := map[string]*Bundle{ + "bundle1": { + Manifest: Manifest{ + Roots: &[]string{"a"}, + Revision: "foo", + }, + Etag: "foo", + lazyLoadingMode: true, + }, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err := Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + + if err != nil { + t.Fatal(err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the bundle was activated + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err := ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatal(err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` +{ + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "foo", + "roots": ["a"] + }, + "etag": "foo" + } + } + } +} +` + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } +} + +func TestBundleLifecycle_ModuleRegoVersions(t *testing.T) { + type files [][2]string + type bundles map[string]files + type deactivation struct { + bundles map[string]struct{} + expData string + } + type activation struct { + bundles bundles + lazy bool + readWithBundleName bool + expData string + } + + tests := []struct { + note string + updates []any + runtimeRegoVersion ast.RegoVersion + }{ + // single v0 bundle + { + note: "v0 bundle, lazy, read with bundle name", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + // Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader initialized with bundle name, so prefix is expected. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":0}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + { + note: "v0 bundle, not lazy, read with bundle name", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + // Not lazy mode, bundle store decides that module name should be prefixed with bundle name. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":0}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + { + note: "v0 bundle, lazy, read with NO bundle name", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: false, + // Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader not initialized with bundle name, so prefix not expected. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}}, + "modules":{"a/policy.rego":{"rego_version":0}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + { + note: "v0 bundle, not lazy, read with NO bundle name", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: false, + readWithBundleName: false, + // Not lazy mode, bundle store decides that module name should be prefixed with bundle name. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":0}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + + { + note: "v0 bundle, not lazy, --v0-compatible", + runtimeRegoVersion: ast.RegoV0, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + // Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader initialized with bundle name, so prefix is expected. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + { + note: "v0 bundle, lazy, read with bundle name, --v0-compatible", + runtimeRegoVersion: ast.RegoV0, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + // Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader initialized with bundle name, so prefix is expected. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + { + note: "v0 bundle, lazy, read with NO bundle name, --v0-compatible", + runtimeRegoVersion: ast.RegoV0, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: false, + // Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader initialized with bundle name, so prefix is expected. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + + // single v1 bundle + { + note: "v1 bundle, lazy, read with bundle name", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + // Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader initialized with bundle name, so prefix is expected. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + { + note: "v1 bundle, not lazy, read with bundle name", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + // Not lazy mode, bundle store decides that module name should be prefixed with bundle name. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + { + note: "v1 bundle, lazy, read with NO bundle name", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: true, + readWithBundleName: false, + // Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader not initialized with bundle name, so prefix not expected. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + { + note: "v1 bundle, not lazy, read with NO bundle name", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: false, + readWithBundleName: false, + // Not lazy mode, bundle store decides that module name should be prefixed with bundle name. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + + { + note: "v1 bundle, not lazy, --v0-compatible", + runtimeRegoVersion: ast.RegoV0, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + // Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader initialized with bundle name, so prefix is expected. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":1}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}, "modules":{}}}`, + }, + }, + }, + { + note: "v1 bundle, lazy, read with bundle name, --v0-compatible", + runtimeRegoVersion: ast.RegoV0, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + // Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader initialized with bundle name, so prefix is expected. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":1}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}, "modules":{}}}`, + }, + }, + }, + { + note: "v1 bundle, lazy, read with NO bundle name, --v0-compatible", + runtimeRegoVersion: ast.RegoV0, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: true, + readWithBundleName: false, + // Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader initialized with bundle name, so prefix is expected. + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}}, + "modules":{"a/policy.rego":{"rego_version":1}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}, "modules":{}}}`, + }, + }, + }, + + { + note: "custom bundle without rego-version, lazy", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, + {"a/policy.rego", `package a + p contains 1337 if { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + { + note: "custom bundle without rego-version, lazy, v1 runtime (explicit)", + runtimeRegoVersion: ast.RegoV1, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, + {"a/policy.rego", `package a + p contains 1337 if { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + { + note: "custom bundle without rego-version, lazy, --v0-compatible", + runtimeRegoVersion: ast.RegoV0, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, + {"a/policy.rego", `package a + p[1337] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + + { + note: "custom bundle without rego-version, not lazy", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, + {"a/policy.rego", `package a + p contains 1337 if { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + { + note: "custom bundle without rego-version, not lazy, v1 runtime (explicit)", + runtimeRegoVersion: ast.RegoV1, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, + {"a/policy.rego", `package a + p contains 1337 if { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + { + note: "custom bundle without rego-version, not lazy, --v0-compatible", + runtimeRegoVersion: ast.RegoV0, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, + {"a/policy.rego", `package a + p[1337] { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{}}}`, + }, + }, + }, + + { + note: "v0, lazy replaced by non-lazy", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":0}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}}, + "modules":{} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + + { + note: "v0 bundle replaced by v1 bundle, lazy", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":0}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}}, + "modules":{} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + { + note: "v0 bundle replaced by v1 bundle, not lazy", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":0}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}}, + "modules":{} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + { + note: "v0 bundle replaced by custom bundle, not lazy", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":0}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, // no rego-version + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}}, + "modules":{} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + + { + note: "v1 bundle replaced by v0 bundle, lazy", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":0}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + { + note: "v1 bundle replaced by v0 bundle, not lazy", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":0}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + { + note: "custom bundle replaced by v0 bundle, lazy", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"]}`}, // no rego-version + {"a/policy.rego", `package a + p contains 42 if { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":0}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + + { + note: "multiple v0 bundles, all dropped", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["b"], "rego_version": 0}`}, + {"b/policy.rego", `package b + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}, + "bundle2":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["b"]}} + }, + "modules":{"bundle1/a/policy.rego":{"rego_version":0},"bundle2/b/policy.rego":{"rego_version":0}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + + { + note: "multiple v0 bundles, one dropped", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["b"], "rego_version": 0}`}, + {"b/policy.rego", `package b + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}, + "bundle2":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["b"]}} + }, + "modules":{"bundle1/a/policy.rego":{"rego_version":0},"bundle2/b/policy.rego":{"rego_version":0}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}}, + expData: `{ + "system":{ + "bundles":{ + "bundle2":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["b"]}} + }, + "modules":{"bundle2/b/policy.rego":{"rego_version":0}} + } + }`, + }, + }, + }, + + { + note: "v0 bundle with v1 bundle added", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a"], "rego_version": 0}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}}, + "modules":{"bundle1/a/policy.rego":{"rego_version":0}} + } + }`, + }, + activation{ + bundles: bundles{ + "bundle2": { + {"/.manifest", `{"roots": ["b"], "rego_version": 1}`}, + {"b/policy.rego", `package b + p contains 42 if { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}, + "bundle2":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["b"]}} + }, + "modules":{"bundle1/a/policy.rego":{"rego_version":0}} + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + + { + note: "mixed-version bundles, lazy", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy.rego": 1}}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + {"b/policy.rego", `package b + p contains 42 if { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy.rego": 0}}`}, + {"c/policy.rego", `package c + p contains 42 if { true }`}, + {"d/policy.rego", `package d + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}}, + "bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}} + }, + "modules":{ + "bundle1/a/policy.rego":{"rego_version":0}, + "bundle2/d/policy.rego":{"rego_version":0} + } + } + }`, + }, + // replacing bundles + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy2.rego": 1}}`}, + {"a/policy2.rego", `package a + q[42] { true }`}, + {"b/policy2.rego", `package b + q contains 42 if { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy2.rego": 0}}`}, + {"c/policy2.rego", `package c + q contains 42 if { true }`}, + {"d/policy2.rego", `package d + q[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy2.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}}, + "bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy2.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}} + }, + "modules":{ + "bundle1/a/policy2.rego":{"rego_version":0}, + "bundle2/d/policy2.rego":{"rego_version":0} + } + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + { + note: "mixed-version bundles, lazy, read with NO bundle name", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy.rego": 1}}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + {"b/policy.rego", `package b + p contains 42 if { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy.rego": 0}}`}, + {"c/policy.rego", `package c + p contains 42 if { true }`}, + {"d/policy.rego", `package d + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: false, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}}, + "bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}} + }, + "modules":{ + "a/policy.rego":{"rego_version":0}, + "d/policy.rego":{"rego_version":0} + } + } + }`, + }, + // replacing bundles + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy2.rego": 1}}`}, + {"a/policy2.rego", `package a + q[42] { true }`}, + {"b/policy2.rego", `package b + q contains 42 if { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy2.rego": 0}}`}, + {"c/policy2.rego", `package c + q contains 42 if { true }`}, + {"d/policy2.rego", `package d + q[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: false, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy2.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}}, + "bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy2.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}} + }, + "modules":{ + "a/policy2.rego":{"rego_version":0}, + "d/policy2.rego":{"rego_version":0} + } + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + { + note: "mixed-version bundles, not lazy", + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy.rego": 1}}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + {"b/policy.rego", `package b + p contains 42 if { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy.rego": 0}}`}, + {"c/policy.rego", `package c + p contains 42 if { true }`}, + {"d/policy.rego", `package d + p[42] { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}}, + "bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}} + }, + "modules":{ + "bundle1/a/policy.rego":{"rego_version":0}, + "bundle2/d/policy.rego":{"rego_version":0} + } + } + }`, + }, + // replacing bundles + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy2.rego": 1}}`}, + {"a/policy2.rego", `package a + q[42] { true }`}, + {"b/policy2.rego", `package b + q contains 42 if { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy2.rego": 0}}`}, + {"c/policy2.rego", `package c + q contains 42 if { true }`}, + {"d/policy2.rego", `package d + q[42] { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy2.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}}, + "bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy2.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}} + }, + "modules":{ + "bundle1/a/policy2.rego":{"rego_version":0}, + "bundle2/d/policy2.rego":{"rego_version":0} + } + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + + { + note: "mixed-version bundles, lazy, --v0-compatible", + runtimeRegoVersion: ast.RegoV0, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy.rego": 1}}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + {"b/policy.rego", `package b + p contains 42 if { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy.rego": 0}}`}, + {"c/policy.rego", `package c + p contains 42 if { true }`}, + {"d/policy.rego", `package d + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}}, + "bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}} + }, + "modules":{ + "bundle1/b/policy.rego":{"rego_version":1}, + "bundle2/c/policy.rego":{"rego_version":1} + } + } + }`, + }, + // replacing bundles + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy2.rego": 1}}`}, + {"a/policy2.rego", `package a + q[42] { true }`}, + {"b/policy2.rego", `package b + q contains 42 if { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy2.rego": 0}}`}, + {"c/policy2.rego", `package c + q contains 42 if { true }`}, + {"d/policy2.rego", `package d + q[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy2.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}}, + "bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy2.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}} + }, + "modules":{ + "bundle1/b/policy2.rego":{"rego_version":1}, + "bundle2/c/policy2.rego":{"rego_version":1} + } + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + { + note: "mixed-version bundles, lazy, read with NO bundle name, --v0-compatible", + runtimeRegoVersion: ast.RegoV0, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy.rego": 1}}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + {"b/policy.rego", `package b + p contains 42 if { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy.rego": 0}}`}, + {"c/policy.rego", `package c + p contains 42 if { true }`}, + {"d/policy.rego", `package d + p[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: false, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}}, + "bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}} + }, + "modules":{ + "b/policy.rego":{"rego_version":1}, + "c/policy.rego":{"rego_version":1} + } + } + }`, + }, + // replacing bundles + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy2.rego": 1}}`}, + {"a/policy2.rego", `package a + q[42] { true }`}, + {"b/policy2.rego", `package b + q contains 42 if { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy2.rego": 0}}`}, + {"c/policy2.rego", `package c + q contains 42 if { true }`}, + {"d/policy2.rego", `package d + q[42] { true }`}, + }, + }, + lazy: true, + readWithBundleName: false, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy2.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}}, + "bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy2.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}} + }, + "modules":{ + "b/policy2.rego":{"rego_version":1}, + "c/policy2.rego":{"rego_version":1} + } + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + { + note: "mixed-version bundles, not lazy, --v0-compatible", + runtimeRegoVersion: ast.RegoV0, + updates: []any{ + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy.rego": 1}}`}, + {"a/policy.rego", `package a + p[42] { true }`}, + {"b/policy.rego", `package b + p contains 42 if { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy.rego": 0}}`}, + {"c/policy.rego", `package c + p contains 42 if { true }`}, + {"d/policy.rego", `package d + p[42] { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}}, + "bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}} + }, + "modules":{ + "bundle1/b/policy.rego":{"rego_version":1}, + "bundle2/c/policy.rego":{"rego_version":1} + } + } + }`, + }, + // replacing bundles + activation{ + bundles: bundles{ + "bundle1": { + {"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy2.rego": 1}}`}, + {"a/policy2.rego", `package a + q[42] { true }`}, + {"b/policy2.rego", `package b + q contains 42 if { true }`}, + }, + "bundle2": { + {"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy2.rego": 0}}`}, + {"c/policy2.rego", `package c + q contains 42 if { true }`}, + {"d/policy2.rego", `package d + q[42] { true }`}, + }, + }, + lazy: false, + readWithBundleName: true, + expData: `{ + "system":{ + "bundles":{ + "bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy2.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}}, + "bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy2.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}} + }, + "modules":{ + "bundle1/b/policy2.rego":{"rego_version":1}, + "bundle2/c/policy2.rego":{"rego_version":1} + } + } + }`, + }, + deactivation{ + bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}}, + expData: `{"system":{"bundles":{},"modules":{}}}`, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + mockStore := mock.New() + + compiler := ast.NewCompiler() + m := metrics.New() + + runtimeRegoVersion := ast.DefaultRegoVersion + if tc.runtimeRegoVersion != ast.RegoUndefined { + runtimeRegoVersion = tc.runtimeRegoVersion + } + + for _, update := range tc.updates { + if act, ok := update.(activation); ok { + bundles := map[string]*Bundle{} + for bundleName, files := range act.bundles { + buf := archive.MustWriteTarGz(files) + loader := NewTarballLoaderWithBaseURL(buf, "") + br := NewCustomReader(loader). + WithBundleEtag("bar"). + WithLazyLoadingMode(act.lazy). + WithRegoVersion(runtimeRegoVersion) + if act.readWithBundleName { + br = br.WithBundleName(bundleName) + } + + bundle, err := br.Read() + if err != nil { + t.Fatal(err) + } + + bundles[bundleName] = &bundle + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err := Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + ParserOptions: ast.ParserOptions{RegoVersion: runtimeRegoVersion}, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Start read transaction + txn = storage.NewTransactionOrDie(ctx, mockStore) + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := act.expData + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected:\n\n%s\n\ngot:\n\n%s", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + } else if deact, ok := update.(deactivation); ok { + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err := Deactivate(&DeactivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + BundleNames: deact.bundles, + ParserOptions: ast.ParserOptions{RegoVersion: runtimeRegoVersion}, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Start read transaction + txn = storage.NewTransactionOrDie(ctx, mockStore) + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := deact.expData + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected:\n\n%s\n\ngot:\n\n%s", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + } + } + }) + } +} + +func TestBundleLazyModeLifecycleRaw(t *testing.T) { + files := [][2]string{ + {"/a/b/c/data.json", "[1,2,3]"}, + {"/a/b/d/data.json", "true"}, + {"/a/b/y/data.yaml", `foo: 1`}, + {"/example/example.rego", `package example + p contains 42 if { true } + `}, + {"/example/example_v0.rego", `package example + q[42] { true } + `}, + {"/authz/allow/policy.wasm", `wasm-module`}, + {"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`}, + {"/.manifest", `{ + "revision": "foo", + "roots": ["a", "example", "x", "authz"], + "wasm":[{"entrypoint": "authz/allow", "module": "/authz/allow/policy.wasm"}], + "rego_version": 1, + "file_rego_versions": {"/example/example_v0.rego": 0} + }`}, + } + + buf := archive.MustWriteTarGz(files) + loader := NewTarballLoaderWithBaseURL(buf, "") + br := NewCustomReader(loader).WithBundleEtag("bar").WithLazyLoadingMode(true) + + bundle, err := br.Read() + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + mockStore := mock.New() + + compiler := ast.NewCompiler() + m := metrics.New() + + extraMods := map[string]*ast.Module{ + "mod1": ast.MustParseModule("package x\np = true"), + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + ExtraModules: extraMods, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the bundle was activated + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err := ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatal(err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + expectedRaw := ` +{ + "a": { + "b": { + "c": [1,2,3], + "d": true, + "y": { + "foo": 1 + }, + "z": true + } + }, + "x": { + "y": true + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "foo", + "roots": ["a", "example", "x", "authz"], + "wasm": [ + { + "entrypoint": "authz/allow", + "module": "/authz/allow/policy.wasm" + } + ], + "rego_version": 1, + "file_rego_versions": { + "/example/example_v0.rego": 0 + } + }, + "etag": "bar", + "wasm": { + "/authz/allow/policy.wasm": "d2FzbS1tb2R1bGU=" + } + } + }, + "modules":{ + "example/example.rego":{ + "rego_version":1 + }, + "example/example_v0.rego":{ + "rego_version":0 + } + } + } +} +` + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %s, got %s", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Ensure that the extra module was included + if _, ok := compiler.Modules["mod1"]; !ok { + t.Fatalf("expected extra module to be compiled") + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Deactivate(&DeactivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + BundleNames: map[string]struct{}{"bundle1": {}}, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Expect the store to have been cleared out after deactivating the bundle + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err = ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatal(err) + } + + if len(names) != 0 { + t.Fatalf("expected 0 bundles in store, found %d", len(names)) + } + + actual, err = mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + expectedRaw = `{"system": {"bundles": {}, "modules": {}}}` + expected = loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + mockStore.AssertValid(t) +} + +func TestBundleLazyModeLifecycleRawInvalidData(t *testing.T) { + + tests := map[string]struct { + files [][2]string + err error + }{ + "non-object root": {[][2]string{{"/data.json", `[1,2,3]`}}, errors.New("root value must be object")}, + "invalid yaml": {[][2]string{{"/a/b/data.yaml", `"foo`}}, errors.New("yaml: found unexpected end of stream")}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + buf := archive.MustWriteTarGz(tc.files) + loader := NewTarballLoaderWithBaseURL(buf, "") + br := NewCustomReader(loader).WithBundleEtag("bar").WithLazyLoadingMode(true) + + bundle, err := br.Read() + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + mockStore := mock.New() + + compiler := ast.NewCompiler() + m := metrics.New() + + bundles := map[string]*Bundle{ + "bundle1": &bundle, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + }) + } +} + +func TestBundleLazyModeLifecycle(t *testing.T) { + ctx := context.Background() + mockStore := mock.New() + + compiler := ast.NewCompiler() + m := metrics.New() + + extraMods := map[string]*ast.Module{ + "mod1": ast.MustParseModule("package x\np = true"), + } + + // v1 bundle + + mod1 := `package a + p contains 42 if { true } + ` + + b1Files := [][2]string{ + {"/.manifest", `{"roots": ["a"], "rego_version": 1}`}, + {"a/policy.rego", mod1}, + {"/data.json", `{"a": {"b": "foo"}}`}, + } + + buf := archive.MustWriteTarGz(b1Files) + loader := NewTarballLoaderWithBaseURL(buf, "") + br := NewCustomReader(loader).WithBundleEtag("foo").WithLazyLoadingMode(true).WithBundleName("bundle1") + + bundle1, err := br.Read() + if err != nil { + t.Fatal(err) + } + + // v0 bundle + + mod2 := `package b + p[42] { true } + ` + + b2Files := [][2]string{ + {"/.manifest", `{"roots": ["b", "c"], "rego_version": 0}`}, + {"b/policy.rego", mod2}, + {"/data.json", `{}`}, + } + + buf = archive.MustWriteTarGz(b2Files) + loader = NewTarballLoaderWithBaseURL(buf, "") + br = NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2") + + bundle2, err := br.Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + "bundle2": &bundle2, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + ExtraModules: extraMods, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the bundle was activated + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err := ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatal(err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + expectedRaw := ` +{ + "a": { + "b": "foo" + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "", + "roots": ["a"], + "rego_version": 1 + }, + "etag": "foo" + }, + "bundle2": { + "manifest": { + "revision": "", + "roots": ["b", "c"], + "rego_version": 0 + }, + "etag": "" + } + }, + "modules":{ + "bundle1/a/policy.rego":{ + "rego_version":1 + }, + "bundle2/b/policy.rego":{ + "rego_version":0 + } + } + } +} +` + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Ensure that the extra module was included + if _, ok := compiler.Modules["mod1"]; !ok { + t.Fatalf("expected extra module to be compiled") + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Deactivate(&DeactivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + BundleNames: map[string]struct{}{"bundle1": {}, "bundle2": {}}, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Expect the store to have been cleared out after deactivating the bundles + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err = ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatal(err) + } + + if len(names) != 0 { + t.Fatalf("expected 0 bundles in store, found %d", len(names)) + } + + actual, err = mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + expectedRaw = `{"system": {"bundles": {}, "modules": {}}}` + expected = loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + mockStore.AssertValid(t) +} + +func TestBundleLazyModeLifecycleRawNoBundleRoots(t *testing.T) { + files := [][2]string{ + {"/a/b/c/data.json", "[1,2,3]"}, + {"/a/b/d/data.json", "true"}, + {"/a/b/y/data.yaml", `foo: 1`}, + {"/example/example.rego", `package example`}, + {"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`}, + {"/.manifest", `{"revision": "rev-1"}`}, + } + + buf := archive.MustWriteTarGz(files) + loader := NewTarballLoaderWithBaseURL(buf, "") + br := NewCustomReader(loader).WithBundleEtag("foo").WithLazyLoadingMode(true) + + bundle, err := br.Read() + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + mockStore := mock.New() + + compiler := ast.NewCompiler() + m := metrics.New() + + bundles := map[string]*Bundle{ + "bundle1": &bundle, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the bundle was activated + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err := ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatal(err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` +{ + "a": { + "b": { + "c": [1,2,3], + "d": true, + "y": { + "foo": 1 + }, + "z": true + } + }, + "x": { + "y": true + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "rev-1", + "roots": [""] + }, + "etag": "foo" + } + } + } +} +` + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + files = [][2]string{ + {"/c/data.json", `{"hello": "world"}`}, + {"/.manifest", `{"revision": "rev-2"}`}, + } + + buf = archive.MustWriteTarGz(files) + loader = NewTarballLoaderWithBaseURL(buf, "") + br = NewCustomReader(loader).WithBundleEtag("bar").WithLazyLoadingMode(true) + + bundle, err = br.Read() + if err != nil { + t.Fatal(err) + } + + bundles = map[string]*Bundle{ + "bundle1": &bundle, + } + + txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + txn = storage.NewTransactionOrDie(ctx, mockStore) + + actual, err = mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw = ` + { + "c": { + "hello": "world" + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "rev-2", + "roots": [""] + }, + "etag": "bar" + } + } + } + }` + + expected = loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + +} + +func TestBundleLazyModeLifecycleRawNoBundleRootsDiskStorage(t *testing.T) { + ctx := context.Background() + + test.WithTempFS(nil, func(dir string) { + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + }) + if err != nil { + t.Fatal(err) + } + + compiler := ast.NewCompiler() + m := metrics.New() + + files := [][2]string{ + {"/a/b/c/data.json", "[1,2,3]"}, + {"/a/b/d/data.json", "true"}, + {"/a/b/y/data.yaml", `foo: 1`}, + {"/example/example.rego", `package example`}, + {"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`}, + {"/.manifest", `{"revision": "rev-1"}`}, + } + + buf := archive.MustWriteTarGz(files) + loader := NewTarballLoaderWithBaseURL(buf, "") + br := NewCustomReader(loader).WithBundleEtag("foo").WithLazyLoadingMode(true) + + bundle, err := br.Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle, + } + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the bundle was activated + txn = storage.NewTransactionOrDie(ctx, store) + names, err := ReadBundleNamesFromStore(ctx, store, txn) + if err != nil { + t.Fatal(err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + actual, err := store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` +{ + "a": { + "b": { + "c": [1,2,3], + "d": true, + "y": { + "foo": 1 + }, + "z": true + } + }, + "x": { + "y": true + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "rev-1", + "roots": [""] + }, + "etag": "foo" + } + } + } +} +` + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + + files = [][2]string{ + {"/c/data.json", `{"hello": "world"}`}, + {"/.manifest", `{"revision": "rev-2"}`}, + } + + buf = archive.MustWriteTarGz(files) + loader = NewTarballLoaderWithBaseURL(buf, "") + br = NewCustomReader(loader).WithBundleEtag("bar").WithLazyLoadingMode(true) + + bundle, err = br.Read() + if err != nil { + t.Fatal(err) + } + + bundles = map[string]*Bundle{ + "bundle1": &bundle, + } + + txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + txn = storage.NewTransactionOrDie(ctx, store) + + actual, err = store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw = ` + { + "c": { + "hello": "world" + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "rev-2", + "roots": [""] + }, + "etag": "bar" + } + } + } + }` + + expected = loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + }) +} + +func TestBundleLazyModeLifecycleNoBundleRoots(t *testing.T) { + ctx := context.Background() + mockStore := mock.New() + compiler := ast.NewCompiler() + m := metrics.New() + + mod1 := "package a\np = true" + + b := Bundle{ + Manifest: Manifest{Revision: "rev-1"}, + Data: map[string]any{ + "a": map[string]any{ + "b": "foo", + "e": map[string]any{ + "f": "bar", + }, + "x": []map[string]string{{"name": "john"}, {"name": "jane"}}, + }, + }, + Modules: []ModuleFile{ + { + Path: "a/policy.rego", + Raw: []byte(mod1), + Parsed: ast.MustParseModule(mod1), + }, + }, + Etag: "foo", + } + + var buf1 bytes.Buffer + if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader := NewTarballLoaderWithBaseURL(&buf1, "") + bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the patches were applied + txn = storage.NewTransactionOrDie(ctx, mockStore) + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": "foo", + "e": { + "f": "bar" + }, + "x": [{"name": "john"}, {"name": "jane"}] + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "rev-1", + "roots": [""] + }, + "etag": "" + } + } + } + }` + + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + // add a new bundle with no roots. this means all the data from the currently activated should be removed + b = Bundle{ + Manifest: Manifest{Revision: "rev-2"}, + Data: map[string]any{ + "c": map[string]any{ + "hello": "world", + }, + }, + Etag: "bar", + } + + var buf2 bytes.Buffer + if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + + loader = NewTarballLoaderWithBaseURL(&buf2, "") + bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + bundles = map[string]*Bundle{ + "bundle1": &bundle2, + } + + txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the patches were applied + txn = storage.NewTransactionOrDie(ctx, mockStore) + + actual, err = mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw = ` + { + "c": { + "hello": "world" + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "rev-2", + "roots": [""] + }, + "etag": "" + } + } + } + }` + + expected = loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) +} + +func TestBundleLazyModeLifecycleNoBundleRootsDiskStorage(t *testing.T) { + ctx := context.Background() + + test.WithTempFS(nil, func(dir string) { + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + }) + if err != nil { + t.Fatal(err) + } + + compiler := ast.NewCompiler() + m := metrics.New() + + mod1 := "package a\np = true" + + b := Bundle{ + Manifest: Manifest{Revision: "rev-1"}, + Data: map[string]any{ + "a": map[string]any{ + "b": "foo", + "e": map[string]any{ + "f": "bar", + }, + "x": []map[string]string{{"name": "john"}, {"name": "jane"}}, + }, + }, + Modules: []ModuleFile{ + { + Path: "a/policy.rego", + Raw: []byte(mod1), + Parsed: ast.MustParseModule(mod1), + }, + }, + Etag: "foo", + } + + var buf1 bytes.Buffer + if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader := NewTarballLoaderWithBaseURL(&buf1, "") + bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + } + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundle was activated + txn = storage.NewTransactionOrDie(ctx, store) + + names, err := ReadBundleNamesFromStore(ctx, store, txn) + if err != nil { + t.Fatal(err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + actual, err := store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": "foo", + "e": { + "f": "bar" + }, + "x": [{"name": "john"}, {"name": "jane"}] + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "rev-1", + "roots": [""] + }, + "etag": "" + } + } + } + }` + + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + + // add a new bundle with no roots. this means all the data from the currently activated should be removed + b = Bundle{ + Manifest: Manifest{Revision: "rev-2"}, + Data: map[string]any{ + "c": map[string]any{ + "hello": "world", + }, + }, + Etag: "bar", + } + + var buf2 bytes.Buffer + if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + + loader = NewTarballLoaderWithBaseURL(&buf2, "") + bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + bundles = map[string]*Bundle{ + "bundle1": &bundle2, + } + + txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundle was activated + txn = storage.NewTransactionOrDie(ctx, store) + + actual, err = store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw = ` + { + "c": { + "hello": "world" + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "rev-2", + "roots": [""] + }, + "etag": "" + } + } + } + }` + + expected = loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + + }) +} + +func TestBundleLazyModeLifecycleMixBundleTypeActivationDiskStorage(t *testing.T) { + ctx := context.Background() + + test.WithTempFS(nil, func(dir string) { + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + }) + if err != nil { + t.Fatal(err) + } + + compiler := ast.NewCompiler() + m := metrics.New() + + mod1 := "package a\np = true" + + b := Bundle{ + Manifest: Manifest{ + Revision: "snap-1", + Roots: &[]string{"a"}, + }, + Data: map[string]any{ + "a": map[string]any{ + "b": "foo", + "e": map[string]any{ + "f": "bar", + }, + "x": []map[string]string{{"name": "john"}, {"name": "jane"}}, + }, + }, + Modules: []ModuleFile{ + { + Path: "a/policy.rego", + Raw: []byte(mod1), + Parsed: ast.MustParseModule(mod1), + }, + }, + Etag: "foo", + } + + var buf1 bytes.Buffer + if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader := NewTarballLoaderWithBaseURL(&buf1, "") + bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + // create a delta bundle and activate it + + // add a new object member + p1 := PatchOperation{ + Op: "upsert", + Path: "/x/y", + Value: []string{"foo", "bar"}, + } + + b = Bundle{ + Manifest: Manifest{ + Revision: "delta-1", + Roots: &[]string{"x"}, + }, + Patch: Patch{Data: []PatchOperation{p1}}, + Etag: "bar", + } + + var buf2 bytes.Buffer + if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + + loader = NewTarballLoaderWithBaseURL(&buf2, "") + bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + "bundle2": &bundle2, + } + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the patches were applied + txn = storage.NewTransactionOrDie(ctx, store) + + actual, err := store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": "foo", + "e": { + "f": "bar" + }, + "x": [{"name": "john"}, {"name": "jane"}] + }, + "x": { + "y": ["foo","bar"] + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "snap-1", + "roots": ["a"] + }, + "etag": "" + }, + "bundle2": { + "manifest": { + "revision": "delta-1", + "roots": ["x"] + }, + "etag": "" + } + } + } + }` + + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + }) +} + +func TestBundleLazyModeLifecycleOldBundleEraseDiskStorage(t *testing.T) { + ctx := context.Background() + + test.WithTempFS(nil, func(dir string) { + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + }) + if err != nil { + t.Fatal(err) + } + + compiler := ast.NewCompiler() + m := metrics.New() + + mod1 := "package a\np = true" + + b := Bundle{ + Manifest: Manifest{Revision: "rev-1", Roots: &[]string{"a"}}, + Data: map[string]any{ + "a": map[string]any{ + "b": "foo", + "e": map[string]any{ + "f": "bar", + }, + "x": []map[string]string{{"name": "john"}, {"name": "jane"}}, + }, + }, + Modules: []ModuleFile{ + { + Path: "a/policy.rego", + Raw: []byte(mod1), + Parsed: ast.MustParseModule(mod1), + }, + }, + Etag: "foo", + } + + var buf1 bytes.Buffer + if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader := NewTarballLoaderWithBaseURL(&buf1, "") + bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + } + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundle was activated + txn = storage.NewTransactionOrDie(ctx, store) + + names, err := ReadBundleNamesFromStore(ctx, store, txn) + if err != nil { + t.Fatal(err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + actual, err := store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": "foo", + "e": { + "f": "bar" + }, + "x": [{"name": "john"}, {"name": "jane"}] + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "rev-1", + "roots": ["a"] + }, + "etag": "" + } + } + } + }` + + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + + // add a new bundle and verify data from the currently activated is removed + b = Bundle{ + Manifest: Manifest{Revision: "rev-2", Roots: &[]string{"c"}}, + Data: map[string]any{ + "c": map[string]any{ + "hello": "world", + }, + }, + Etag: "bar", + } + + var buf2 bytes.Buffer + if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + + loader = NewTarballLoaderWithBaseURL(&buf2, "") + bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + bundles = map[string]*Bundle{ + "bundle1": &bundle2, + } + + txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundle was activated + txn = storage.NewTransactionOrDie(ctx, store) + + actual, err = store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw = ` + { + "c": { + "hello": "world" + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "rev-2", + "roots": ["c"] + }, + "etag": "" + } + } + } + }` + + expected = loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + + }) +} + +func TestBundleLazyModeLifecycleRestoreBackupDB(t *testing.T) { + ctx := context.Background() + + test.WithTempFS(nil, func(dir string) { + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + }) + if err != nil { + t.Fatal(err) + } + + compiler := ast.NewCompiler() + m := metrics.New() + + mod1 := "package a\np = true" + + b := Bundle{ + Manifest: Manifest{Revision: "rev-1", Roots: &[]string{"a"}}, + Data: map[string]any{ + "a": map[string]any{ + "b": "foo", + "e": map[string]any{ + "f": "bar", + }, + "x": []map[string]string{{"name": "john"}, {"name": "jane"}}, + }, + }, + Modules: []ModuleFile{ + { + Path: "a/policy.rego", + Raw: []byte(mod1), + Parsed: ast.MustParseModule(mod1), + }, + }, + Etag: "foo", + } + + var buf1 bytes.Buffer + if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader := NewTarballLoaderWithBaseURL(&buf1, "") + bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + } + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundle was activated + txn = storage.NewTransactionOrDie(ctx, store) + + names, err := ReadBundleNamesFromStore(ctx, store, txn) + if err != nil { + t.Fatal(err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + actual, err := store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": "foo", + "e": { + "f": "bar" + }, + "x": [{"name": "john"}, {"name": "jane"}] + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "rev-1", + "roots": ["a"] + }, + "etag": "" + } + } + } + }` + + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + + // add a new bundle but abort the transaction and verify only old the bundle data is kept in store + b = Bundle{ + Manifest: Manifest{Revision: "rev-2", Roots: &[]string{"c"}}, + Data: map[string]any{ + "c": map[string]any{ + "hello": "world", + }, + }, + Etag: "bar", + } + + var buf2 bytes.Buffer + if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + + loader = NewTarballLoaderWithBaseURL(&buf2, "") + bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + bundles = map[string]*Bundle{ + "bundle1": &bundle2, + } + + txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + store.Abort(ctx, txn) + + txn = storage.NewTransactionOrDie(ctx, store) + + actual, err = store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw = ` + { + "a": { + "b": "foo", + "e": { + "f": "bar" + }, + "x": [{"name": "john"}, {"name": "jane"}] + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "rev-1", + "roots": ["a"] + }, + "etag": "" + } + } + } + }` + + expected = loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + + // check symlink is created + symlink := filepath.Join(dir, "active") + _, err = os.Lstat(symlink) + if err != nil { + t.Fatal(err) + } + + // check symlink target + _, err = filepath.EvalSymlinks(symlink) + if err != nil { + t.Fatalf("eval symlinks: %v", err) + } + }) +} + +func TestDeltaBundleLazyModeLifecycleDiskStorage(t *testing.T) { + ctx := context.Background() + + test.WithTempFS(nil, func(dir string) { + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + }) + if err != nil { + t.Fatal(err) + } + + compiler := ast.NewCompiler() + m := metrics.New() + + mod1 := "package a\np = true" + mod2 := "package b\np = true" + + b := Bundle{ + Manifest: Manifest{ + Roots: &[]string{"a"}, + }, + Data: map[string]any{ + "a": map[string]any{ + "b": "foo", + "e": map[string]any{ + "f": "bar", + }, + "x": []map[string]string{{"name": "john"}, {"name": "jane"}}, + }, + }, + Modules: []ModuleFile{ + { + Path: "a/policy.rego", + Raw: []byte(mod1), + Parsed: ast.MustParseModule(mod1), + }, + }, + Etag: "foo", + } + + var buf1 bytes.Buffer + if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader := NewTarballLoaderWithBaseURL(&buf1, "") + bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + b = Bundle{ + Manifest: Manifest{ + Roots: &[]string{"b", "c"}, + }, + Data: nil, + Modules: []ModuleFile{ + { + Path: "b/policy.rego", + Raw: []byte(mod2), + Parsed: ast.MustParseModule(mod2), + }, + }, + Etag: "foo", + } + + var buf2 bytes.Buffer + if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + + loader = NewTarballLoaderWithBaseURL(&buf2, "") + bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + "bundle2": &bundle2, + } + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundles were activated + txn = storage.NewTransactionOrDie(ctx, store) + names, err := ReadBundleNamesFromStore(ctx, store, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + + // create a delta bundle and activate it + + // add a new object member + p1 := PatchOperation{ + Op: "upsert", + Path: "/a/c/d", + Value: []string{"foo", "bar"}, + } + + // append value to array + p2 := PatchOperation{ + Op: "upsert", + Path: "/a/c/d/-", + Value: "baz", + } + + // replace a value + p3 := PatchOperation{ + Op: "replace", + Path: "a/b", + Value: "bar", + } + + // add a new object root + p4 := PatchOperation{ + Op: "upsert", + Path: "/c/d", + Value: []string{"foo", "bar"}, + } + + deltaBundles := map[string]*Bundle{ + "bundle1": { + Manifest: Manifest{ + Revision: "delta-1", + Roots: &[]string{"a"}, + }, + Patch: Patch{Data: []PatchOperation{p1, p2, p3}}, + Etag: "bar", + }, + "bundle2": { + Manifest: Manifest{ + Revision: "delta-2", + Roots: &[]string{"b", "c"}, + }, + Patch: Patch{Data: []PatchOperation{p4}}, + Etag: "baz", + }, + "bundle3": { + Manifest: Manifest{ + Roots: &[]string{"d"}, + }, + Data: map[string]any{ + "d": map[string]any{ + "e": "foo", + }, + }, + }, + } + + txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: deltaBundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // check the modules from the snapshot bundles are on the compiler + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + // Ensure the patches were applied + txn = storage.NewTransactionOrDie(ctx, store) + + actual, err := store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": "bar", + "c": { + "d": ["foo", "bar", "baz"] + }, + "e": { + "f": "bar" + }, + "x": [{"name": "john"}, {"name": "jane"}] + }, + "c": {"d": ["foo", "bar"]}, + "d": {"e": "foo"}, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "delta-1", + "roots": ["a"] + }, + "etag": "bar" + }, + "bundle2": { + "manifest": { + "revision": "delta-2", + "roots": ["b", "c"] + }, + "etag": "baz" + }, + "bundle3": { + "manifest": { + "revision": "", + "roots": ["d"] + }, + "etag": "" + } + } + } + }` + + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + }) +} + +func TestBundleLazyModeLifecycleOverlappingBundleRoots(t *testing.T) { + ctx := context.Background() + mockStore := mock.New() + + compiler := ast.NewCompiler() + m := metrics.New() + + b := Bundle{ + Manifest: Manifest{ + Revision: "foo", + Roots: &[]string{"a/b", "a/c", "a/d"}, + }, + Data: map[string]any{ + "a": map[string]any{ + "b": "foo", + "c": map[string]any{ + "d": "bar", + }, + "d": []map[string]string{{"name": "john"}, {"name": "jane"}}, + }, + }, + } + + var buf1 bytes.Buffer + if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader := NewTarballLoaderWithBaseURL(&buf1, "") + bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + b = Bundle{ + Manifest: Manifest{ + Revision: "bar", + Roots: &[]string{"a/e"}, + }, + Data: map[string]any{ + "a": map[string]any{ + "e": map[string]any{ + "f": "bar", + }, + }, + }, + } + + var buf2 bytes.Buffer + if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader = NewTarballLoaderWithBaseURL(&buf2, "") + bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + "bundle2": &bundle2, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundles were activated + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err := ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + // Ensure the patches were applied + txn = storage.NewTransactionOrDie(ctx, mockStore) + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": "foo", + "c": { + "d": "bar" + }, + "e": { + "f": "bar" + }, + "d": [{"name": "john"}, {"name": "jane"}] + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "foo", + "roots": ["a/b", "a/c", "a/d"] + }, + "etag": "" + }, + "bundle2": { + "manifest": { + "revision": "bar", + "roots": ["a/e"] + }, + "etag": "" + } + } + } + }` + + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) +} + +func TestBundleLazyModeLifecycleOverlappingBundleRootsDiskStorage(t *testing.T) { + ctx := context.Background() + + test.WithTempFS(nil, func(dir string) { + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + }) + if err != nil { + t.Fatal(err) + } + + compiler := ast.NewCompiler() + m := metrics.New() + + b := Bundle{ + Manifest: Manifest{ + Revision: "foo", + Roots: &[]string{"a/b/c", "a/b/d", "a/b/e"}, + }, + Data: map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "c": "bar", + "d": []map[string]string{{"name": "john"}, {"name": "jane"}}, + "e": []string{"foo", "bar"}, + }, + }, + }, + } + + var buf1 bytes.Buffer + if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader := NewTarballLoaderWithBaseURL(&buf1, "") + bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + b = Bundle{ + Manifest: Manifest{ + Revision: "bar", + Roots: &[]string{"a/b/f"}, + }, + Data: map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "f": map[string]any{ + "hello": "world", + }, + }, + }, + }, + } + + var buf2 bytes.Buffer + if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader = NewTarballLoaderWithBaseURL(&buf2, "") + bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + "bundle2": &bundle2, + } + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundles were activated + txn = storage.NewTransactionOrDie(ctx, store) + names, err := ReadBundleNamesFromStore(ctx, store, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + + // Ensure the patches were applied + txn = storage.NewTransactionOrDie(ctx, store) + + actual, err := store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": { + "c": "bar", + "d": [{"name": "john"}, {"name": "jane"}], + "e": ["foo", "bar"], + "f": {"hello": "world"} + } + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "foo", + "roots": ["a/b/c", "a/b/d", "a/b/e"] + }, + "etag": "" + }, + "bundle2": { + "manifest": { + "revision": "bar", + "roots": ["a/b/f"] + }, + "etag": "" + } + } + } + }` + + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + }) +} + +func TestBundleLazyModeLifecycleRawOverlappingBundleRoots(t *testing.T) { + ctx := context.Background() + mockStore := mock.New() + + compiler := ast.NewCompiler() + m := metrics.New() + + files := [][2]string{ + {"/a/b/x/data.json", "[1,2,3]"}, + {"/a/c/y/data.json", "true"}, + {"/a/d/z/data.yaml", `foo: 1`}, + {"/data.json", `{"a": {"b": {"z": true}}}`}, + {"/.manifest", `{"revision": "foo", "roots": ["a/b", "a/c", "a/d"]}`}, + } + + buf := archive.MustWriteTarGz(files) + loader := NewTarballLoaderWithBaseURL(buf, "") + bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + files = [][2]string{ + {"/a/e/x/data.json", "[4,5,6]"}, + {"/data.json", `{"a": {"e": {"f": true}}}`}, + {"/.manifest", `{"revision": "bar", "roots": ["a/e"]}`}, + } + + buf = archive.MustWriteTarGz(files) + loader = NewTarballLoaderWithBaseURL(buf, "") + bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + "bundle2": &bundle2, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundles were activated + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err := ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + // Ensure the patches were applied + txn = storage.NewTransactionOrDie(ctx, mockStore) + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": { + "x": [1,2,3], + "z": true + }, + "c": { + "y": true + }, + "d": { + "z": {"foo": 1} + }, + "e": { + "x": [4,5,6], + "f": true + } + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "foo", + "roots": ["a/b", "a/c", "a/d"] + }, + "etag": "" + }, + "bundle2": { + "manifest": { + "revision": "bar", + "roots": ["a/e"] + }, + "etag": "" + } + } + } + }` + + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) +} + +func TestBundleLazyModeLifecycleRawOverlappingBundleRootsDiskStorage(t *testing.T) { + ctx := context.Background() + + test.WithTempFS(nil, func(dir string) { + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + }) + if err != nil { + t.Fatal(err) + } + + compiler := ast.NewCompiler() + m := metrics.New() + + files := [][2]string{ + {"/a/b/u/data.json", "[1,2,3]"}, + {"/a/b/v/data.json", "true"}, + {"/a/b/w/data.yaml", `foo: 1`}, + {"/data.json", `{"a": {"b": {"x": true}}}`}, + {"/.manifest", `{"revision": "foo", "roots": ["a/b"]}`}, + } + + buf := archive.MustWriteTarGz(files) + loader := NewTarballLoaderWithBaseURL(buf, "") + bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + files = [][2]string{ + {"/a/c/x/data.json", "[4,5,6]"}, + {"/data.json", `{"a": {"c": {"y": true}}}`}, + {"/.manifest", `{"revision": "bar", "roots": ["a/c"]}`}, + } + + buf = archive.MustWriteTarGz(files) + loader = NewTarballLoaderWithBaseURL(buf, "") + bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + "bundle2": &bundle2, + } + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundles were activated + txn = storage.NewTransactionOrDie(ctx, store) + names, err := ReadBundleNamesFromStore(ctx, store, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + + // Ensure the patches were applied + txn = storage.NewTransactionOrDie(ctx, store) + + actual, err := store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": { + "u": [1,2,3], + "v": true, + "w": {"foo": 1}, + "x": true + }, + "c": { + "x": [4,5,6], + "y": true + } + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "foo", + "roots": ["a/b"] + }, + "etag": "" + }, + "bundle2": { + "manifest": { + "revision": "bar", + "roots": ["a/c"] + }, + "etag": "" + } + } + } + }` + + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + store.Abort(ctx, txn) + }) +} + +func TestDeltaBundleLazyModeLifecycle(t *testing.T) { + ctx := context.Background() + mockStore := mock.New() + + compiler := ast.NewCompiler() + m := metrics.New() + + mod1 := "package a\np = true" + mod2 := "package b\np = true" + + b := Bundle{ + Manifest: Manifest{ + Roots: &[]string{"a"}, + }, + Data: map[string]any{ + "a": map[string]any{ + "b": "foo", + "e": map[string]any{ + "f": "bar", + }, + "x": []map[string]string{{"name": "john"}, {"name": "jane"}}, + }, + }, + Modules: []ModuleFile{ + { + Path: "policy.rego", + Raw: []byte(mod1), + Parsed: ast.MustParseModule(mod1), + }, + }, + Etag: "foo", + } + + var buf1 bytes.Buffer + if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader := NewTarballLoaderWithBaseURL(&buf1, "") + bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + b = Bundle{ + Manifest: Manifest{ + Roots: &[]string{"b", "c"}, + }, + Data: nil, + Modules: []ModuleFile{ + { + Path: "policy.rego", + Raw: []byte(mod2), + Parsed: ast.MustParseModule(mod2), + }, + }, + Etag: "foo", + lazyLoadingMode: true, + sizeLimitBytes: DefaultSizeLimitBytes + 1, + } + + var buf2 bytes.Buffer + if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader = NewTarballLoaderWithBaseURL(&buf2, "") + bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + "bundle2": &bundle2, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundles were activated + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err := ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + // create a delta bundle and activate it + + // add a new object member + p1 := PatchOperation{ + Op: "upsert", + Path: "/a/c/d", + Value: []string{"foo", "bar"}, + } + + // append value to array + p2 := PatchOperation{ + Op: "upsert", + Path: "/a/c/d/-", + Value: "baz", + } + + // insert value in array + p3 := PatchOperation{ + Op: "upsert", + Path: "/a/x/1", + Value: map[string]string{"name": "alice"}, + } + + // replace a value + p4 := PatchOperation{ + Op: "replace", + Path: "a/b", + Value: "bar", + } + + // remove a value + p5 := PatchOperation{ + Op: "remove", + Path: "a/e", + } + + // add a new object with an escaped character in the path + p6 := PatchOperation{ + Op: "upsert", + Path: "a/y/~0z", + Value: []int{1, 2, 3}, + } + + // add a new object root + p7 := PatchOperation{ + Op: "upsert", + Path: "/c/d", + Value: []string{"foo", "bar"}, + } + + deltaBundles := map[string]*Bundle{ + "bundle1": { + Manifest: Manifest{ + Revision: "delta-1", + Roots: &[]string{"a"}, + }, + Patch: Patch{Data: []PatchOperation{p1, p2, p3, p4, p5, p6}}, + Etag: "bar", + }, + "bundle2": { + Manifest: Manifest{ + Revision: "delta-2", + Roots: &[]string{"b", "c"}, + }, + Patch: Patch{Data: []PatchOperation{p7}}, + Etag: "baz", + }, + "bundle3": { + Manifest: Manifest{ + Roots: &[]string{"d"}, + }, + Data: map[string]any{ + "d": map[string]any{ + "e": "foo", + }, + }, + }, + } + + txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: deltaBundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // check the modules from the snapshot bundles are on the compiler + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + // Ensure the patches were applied + txn = storage.NewTransactionOrDie(ctx, mockStore) + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": "bar", + "c": { + "d": ["foo", "bar", "baz"] + }, + "x": [{"name": "john"}, {"name": "alice"}, {"name": "jane"}], + "y": {"~z": [1, 2, 3]} + }, + "c": {"d": ["foo", "bar"]}, + "d": {"e": "foo"}, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "delta-1", + "roots": ["a"] + }, + "etag": "bar" + }, + "bundle2": { + "manifest": { + "revision": "delta-2", + "roots": ["b", "c"] + }, + "etag": "baz" + }, + "bundle3": { + "manifest": { + "revision": "", + "roots": ["d"] + }, + "etag": "" + } + } + } + }` + + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + mockStore.AssertValid(t) +} + +func TestDeltaBundleLazyModeWithDefaultRules(t *testing.T) { + ctx := context.Background() + mockStore := mock.New() + + compiler := ast.NewCompiler() + m := metrics.New() + + mod1 := "package a\ndefault p = true" + mod2 := "package b\ndefault p = true" + + b := Bundle{ + Manifest: Manifest{ + Roots: &[]string{"a"}, + }, + Data: map[string]any{ + "a": map[string]any{ + "b": "foo", + "e": map[string]any{ + "f": "bar", + }, + "x": []map[string]string{{"name": "john"}, {"name": "jane"}}, + }, + }, + Modules: []ModuleFile{ + { + Path: "policy.rego", + Raw: []byte(mod1), + Parsed: ast.MustParseModule(mod1), + }, + }, + Etag: "foo", + } + + var buf1 bytes.Buffer + if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader := NewTarballLoaderWithBaseURL(&buf1, "") + bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read() + if err != nil { + t.Fatal(err) + } + + b = Bundle{ + Manifest: Manifest{ + Roots: &[]string{"b", "c"}, + }, + Data: nil, + Modules: []ModuleFile{ + { + Path: "policy.rego", + Raw: []byte(mod2), + Parsed: ast.MustParseModule(mod2), + }, + }, + Etag: "foo", + lazyLoadingMode: true, + sizeLimitBytes: DefaultSizeLimitBytes + 1, + } + + var buf2 bytes.Buffer + if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil { + t.Fatal("Unexpected error:", err) + } + loader = NewTarballLoaderWithBaseURL(&buf2, "") + bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read() + if err != nil { + t.Fatal(err) + } + + bundles := map[string]*Bundle{ + "bundle1": &bundle1, + "bundle2": &bundle2, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundles were activated + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err := ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + // create a delta bundle and activate it + + // add a new object member + p1 := PatchOperation{ + Op: "upsert", + Path: "/a/c/d", + Value: []string{"foo", "bar"}, + } + + // append value to array + p2 := PatchOperation{ + Op: "upsert", + Path: "/a/c/d/-", + Value: "baz", + } + + // insert value in array + p3 := PatchOperation{ + Op: "upsert", + Path: "/a/x/1", + Value: map[string]string{"name": "alice"}, + } + + // replace a value + p4 := PatchOperation{ + Op: "replace", + Path: "a/b", + Value: "bar", + } + + // remove a value + p5 := PatchOperation{ + Op: "remove", + Path: "a/e", + } + + // add a new object with an escaped character in the path + p6 := PatchOperation{ + Op: "upsert", + Path: "a/y/~0z", + Value: []int{1, 2, 3}, + } + + // add a new object root + p7 := PatchOperation{ + Op: "upsert", + Path: "/c/d", + Value: []string{"foo", "bar"}, + } + + deltaBundles := map[string]*Bundle{ + "bundle1": { + Manifest: Manifest{ + Revision: "delta-1", + Roots: &[]string{"a"}, + }, + Patch: Patch{Data: []PatchOperation{p1, p2, p3, p4, p5, p6}}, + Etag: "bar", + }, + "bundle2": { + Manifest: Manifest{ + Revision: "delta-2", + Roots: &[]string{"b", "c"}, + }, + Patch: Patch{Data: []PatchOperation{p7}}, + Etag: "baz", + }, + "bundle3": { + Manifest: Manifest{ + Roots: &[]string{"d"}, + }, + Data: map[string]any{ + "d": map[string]any{ + "e": "foo", + }, + }, + }, + } + + txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + expectedModuleCount := len(compiler.Modules) + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: deltaBundles, + }) + if expectedModuleCount != len(compiler.Modules) { + t.Fatalf("Expected %d modules, got %d", expectedModuleCount, len(compiler.Modules)) + } + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // check the modules from the snapshot bundles are on the compiler + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + // Ensure the patches were applied + txn = storage.NewTransactionOrDie(ctx, mockStore) + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": "bar", + "c": { + "d": ["foo", "bar", "baz"] + }, + "x": [{"name": "john"}, {"name": "alice"}, {"name": "jane"}], + "y": {"~z": [1, 2, 3]} + }, + "c": {"d": ["foo", "bar"]}, + "d": {"e": "foo"}, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "delta-1", + "roots": ["a"] + }, + "etag": "bar" + }, + "bundle2": { + "manifest": { + "revision": "delta-2", + "roots": ["b", "c"] + }, + "etag": "baz" + }, + "bundle3": { + "manifest": { + "revision": "", + "roots": ["d"] + }, + "etag": "" + } + } + } + }` + + expected := loadExpectedSortedResult(expectedRaw) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual))) + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + mockStore.AssertValid(t) +} + +func TestBundleLifecycle(t *testing.T) { + tests := []struct { + note string + readAst bool + }{ + { + note: "read raw", + readAst: false, + }, + { + note: "read ast", + readAst: true, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + mockStore := mock.New(inmem.OptReturnASTValuesOnRead(tc.readAst)) + + compiler := ast.NewCompiler() + m := metrics.New() + + extraMods := map[string]*ast.Module{ + "mod1": ast.MustParseModule("package x\np = true"), + } + + const mod2 = "package a\np = true" + mod3 := "package b\np = true" + + bundles := map[string]*Bundle{ + "bundle1": { + Manifest: Manifest{ + Roots: &[]string{"a"}, + }, + Data: map[string]any{ + "a": map[string]any{ + "b": "foo", + }, + }, + Modules: []ModuleFile{ + { + Path: "a/policy.rego", + Raw: []byte(mod2), + Parsed: ast.MustParseModule(mod2), + }, + }, + Etag: "foo"}, + "bundle2": { + Manifest: Manifest{ + Roots: &[]string{"b", "c"}, + }, + Data: nil, + Modules: []ModuleFile{ + { + Path: "b/policy.rego", + Raw: []byte(mod3), + Parsed: ast.MustParseModule(mod3), + }, + }, + }, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err := Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + ExtraModules: extraMods, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the bundle was activated + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err := ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatal(err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + expectedRaw := ` +{ + "a": { + "b": "foo" + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "", + "roots": ["a"] + }, + "etag": "foo" + }, + "bundle2": { + "manifest": { + "revision": "", + "roots": ["b", "c"] + }, + "etag": "" + } + }, + "modules": { + "bundle1/a/policy.rego": { + "rego_version": 1 + }, + "bundle2/b/policy.rego": { + "rego_version": 1 + } + } + } +} +` + assertEqual(t, tc.readAst, expectedRaw, actual) + + // Ensure that the extra module was included + if _, ok := compiler.Modules["mod1"]; !ok { + t.Fatalf("expected extra module to be compiled") + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Deactivate(&DeactivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + BundleNames: map[string]struct{}{"bundle1": {}, "bundle2": {}}, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Expect the store to have been cleared out after deactivating the bundles + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err = ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatal(err) + } + + if len(names) != 0 { + t.Fatalf("expected 0 bundles in store, found %d", len(names)) + } + + actual, err = mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + expectedRaw = `{"system": {"bundles": {}, "modules": {}}}` + assertEqual(t, tc.readAst, expectedRaw, actual) + + mockStore.AssertValid(t) + }) + } +} + +func TestDeltaBundleLifecycle(t *testing.T) { + tests := []struct { + note string + readAst bool + }{ + { + note: "read raw", + readAst: false, + }, + { + note: "read ast", + readAst: true, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + mockStore := mock.New(inmem.OptReturnASTValuesOnRead(tc.readAst)) + + compiler := ast.NewCompiler() + m := metrics.New() + + mod1 := "package a\np = true" + mod2 := "package b\np = true" + + bundles := map[string]*Bundle{ + "bundle1": { + Manifest: Manifest{ + Roots: &[]string{"a"}, + }, + Data: map[string]any{ + "a": map[string]any{ + "b": "foo", + "e": map[string]any{ + "f": "bar", + }, + "x": []map[string]string{{"name": "john"}, {"name": "jane"}}, + }, + }, + Modules: []ModuleFile{ + { + Path: "a/policy.rego", + Raw: []byte(mod1), + Parsed: ast.MustParseModule(mod1), + }, + }, + Etag: "foo", + }, + "bundle2": { + Manifest: Manifest{ + Roots: &[]string{"b", "c"}, + }, + Data: nil, + Modules: []ModuleFile{ + { + Path: "b/policy.rego", + Raw: []byte(mod2), + Parsed: ast.MustParseModule(mod2), + }, + }, + }, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err := Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundles were activated + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err := ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + // create a delta bundle and activate it + + // add a new object member + p1 := PatchOperation{ + Op: "upsert", + Path: "/a/c/d", + Value: []string{"foo", "bar"}, + } + + // append value to array + p2 := PatchOperation{ + Op: "upsert", + Path: "/a/c/d/-", + Value: "baz", + } + + // insert value in array + p3 := PatchOperation{ + Op: "upsert", + Path: "/a/x/1", + Value: map[string]string{"name": "alice"}, + } + + // replace a value + p4 := PatchOperation{ + Op: "replace", + Path: "a/b", + Value: "bar", + } + + // remove a value + p5 := PatchOperation{ + Op: "remove", + Path: "a/e", + } + + // add a new object with an escaped character in the path + p6 := PatchOperation{ + Op: "upsert", + Path: "a/y/~0z", + Value: []int{1, 2, 3}, + } + + // add a new object root + p7 := PatchOperation{ + Op: "upsert", + Path: "/c/d", + Value: []string{"foo", "bar"}, + } + + deltaBundles := map[string]*Bundle{ + "bundle1": { + Manifest: Manifest{ + Revision: "delta-1", + Roots: &[]string{"a"}, + }, + Patch: Patch{Data: []PatchOperation{p1, p2, p3, p4, p5, p6}}, + Etag: "bar", + }, + "bundle2": { + Manifest: Manifest{ + Revision: "delta-2", + Roots: &[]string{"b", "c"}, + }, + Patch: Patch{Data: []PatchOperation{p7}}, + Etag: "baz", + }, + "bundle3": { + Manifest: Manifest{ + Roots: &[]string{"d"}, + }, + Data: map[string]any{ + "d": map[string]any{ + "e": "foo", + }, + }, + }, + } + + txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: deltaBundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // check the modules from the snapshot bundles are on the compiler + for bundleName, bundle := range bundles { + for modName := range bundle.ParsedModules(bundleName) { + if _, ok := compiler.Modules[modName]; !ok { + t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName) + } + } + } + + // Ensure the patches were applied + txn = storage.NewTransactionOrDie(ctx, mockStore) + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "b": "bar", + "c": { + "d": ["foo", "bar", "baz"] + }, + "x": [{"name": "john"}, {"name": "alice"}, {"name": "jane"}], + "y": {"~z": [1, 2, 3]} + }, + "c": {"d": ["foo", "bar"]}, + "d": {"e": "foo"}, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "delta-1", + "roots": ["a"] + }, + "etag": "bar" + }, + "bundle2": { + "manifest": { + "revision": "delta-2", + "roots": ["b", "c"] + }, + "etag": "baz" + }, + "bundle3": { + "manifest": { + "revision": "", + "roots": ["d"] + }, + "etag": "" + } + }, + "modules":{ + "bundle1/a/policy.rego":{ + "rego_version":1 + }, + "bundle2/b/policy.rego":{ + "rego_version":1 + } + } + } + }` + + assertEqual(t, tc.readAst, expectedRaw, actual) + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + mockStore.AssertValid(t) + }) + } +} + +func TestDeltaBundleActivate(t *testing.T) { + tests := []struct { + note string + readAst bool + }{ + { + note: "read raw", + readAst: false, + }, + { + note: "read ast", + readAst: true, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + mockStore := mock.New(inmem.OptReturnASTValuesOnRead(tc.readAst)) + + compiler := ast.NewCompiler() + m := metrics.New() + + // create a delta bundle + p1 := PatchOperation{ + Op: "upsert", + Path: "/a/c/d", + Value: []string{"foo", "bar"}, + } + + deltaBundles := map[string]*Bundle{ + "bundle1": { + Manifest: Manifest{ + Revision: "delta", + Roots: &[]string{"a"}, + }, + Patch: Patch{Data: []PatchOperation{p1}}, + Etag: "foo", + }, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err := Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: deltaBundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the delta bundle was activated + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err := ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if len(names) != len(deltaBundles) { + t.Fatalf("expected %d bundles in store, found %d", len(deltaBundles), len(names)) + } + + for _, name := range names { + if _, ok := deltaBundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + // Ensure the patches were applied + txn = storage.NewTransactionOrDie(ctx, mockStore) + + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedRaw := ` + { + "a": { + "c": { + "d": ["foo", "bar"] + } + }, + "system": { + "bundles": { + "bundle1": { + "manifest": { + "revision": "delta", + "roots": ["a"] + }, + "etag": "foo" + } + } + } + } + ` + assertEqual(t, tc.readAst, expectedRaw, actual) + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + mockStore.AssertValid(t) + }) + } +} + +func assertEqual(t *testing.T, expectAst bool, expected string, actual any) { + t.Helper() + + if expectAst { + exp := ast.MustParseTerm(expected) + if ast.Compare(exp, actual) != 0 { + t.Errorf("expected:\n\n%v\n\ngot:\n\n%v", expected, actual) + } + } else { + exp := loadExpectedSortedResult(expected) + if !reflect.DeepEqual(exp, actual) { + t.Errorf("expected:\n\n%v\n\ngot:\n\n%v", expected, actual) + } + } +} + +func TestDeltaBundleBadManifest(t *testing.T) { + + ctx := context.Background() + mockStore := mock.New() + + compiler := ast.NewCompiler() + m := metrics.New() + + mod1 := "package a\np = true" + + bundles := map[string]*Bundle{ + "bundle1": { + Manifest: Manifest{ + Roots: &[]string{"a"}, + }, + Modules: []ModuleFile{ + { + Path: "a/policy.rego", + Raw: []byte(mod1), + Parsed: ast.MustParseModule(mod1), + }, + }, + }, + } + + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err := Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + }) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // Ensure the snapshot bundle was activated + txn = storage.NewTransactionOrDie(ctx, mockStore) + names, err := ReadBundleNamesFromStore(ctx, mockStore, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if len(names) != len(bundles) { + t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names)) + } + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Fatalf("unexpected bundle name found in store: %s", name) + } + } + + // Stop the "read" transaction + mockStore.Abort(ctx, txn) + + // create a delta bundle with a different manifest from the snapshot bundle + + p1 := PatchOperation{ + Op: "upsert", + Path: "/a/c/d", + Value: []string{"foo", "bar"}, + } + + deltaBundles := map[string]*Bundle{ + "bundle1": { + Manifest: Manifest{ + Roots: &[]string{"b"}, + }, + Patch: Patch{Data: []PatchOperation{p1}}, + }, + } + + txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err = Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: deltaBundles, + }) + if err == nil { + t.Fatal("expected error but got nil") + } + + expected := "delta bundle 'bundle1' has wasm resolvers or manifest roots that are different from those in the store" + if err.Error() != expected { + t.Fatalf("Expected error %v but got %v", expected, err.Error()) + } + + mockStore.AssertValid(t) +} + +func TestEraseData(t *testing.T) { + storeReadModes := []struct { + note string + readAst bool + }{ + { + note: "read raw", + readAst: false, + }, + { + note: "read ast", + readAst: true, + }, + } + + ctx := context.Background() + cases := []struct { + note string + initialData map[string]any + roots []string + expectErr bool + expected string + }{ + { + note: "erase all", + initialData: map[string]any{ + "a": map[string]any{ + "b": "foo", + }, + "b": "bar", + }, + roots: []string{"a", "b"}, + expectErr: false, + expected: `{}`, + }, + { + note: "erase none", + initialData: map[string]any{ + "a": map[string]any{ + "b": "foo", + }, + "b": "bar", + }, + roots: []string{}, + expectErr: false, + expected: `{"a": {"b": "foo"}, "b": "bar"}`, + }, + { + note: "erase partial", + initialData: map[string]any{ + "a": map[string]any{ + "b": "foo", + }, + "b": "bar", + }, + roots: []string{"a"}, + expectErr: false, + expected: `{"b": "bar"}`, + }, + { + note: "erase partial path", + initialData: map[string]any{ + "a": map[string]any{ + "b": "foo", + "c": map[string]any{ + "d": 123, + }, + }, + }, + roots: []string{"a/c/d"}, + expectErr: false, + expected: `{"a": {"b": "foo", "c":{}}}`, + }, + } + + for _, rm := range storeReadModes { + t.Run(rm.note, func(t *testing.T) { + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + mockStore := mock.NewWithData(tc.initialData, inmem.OptReturnASTValuesOnRead(rm.readAst)) + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + roots := map[string]struct{}{} + for _, root := range tc.roots { + roots[root] = struct{}{} + } + + err := eraseData(ctx, mockStore, txn, roots) + if !tc.expectErr && err != nil { + t.Fatalf("unepected error: %s", err) + } else if tc.expectErr && err == nil { + t.Fatalf("expected error, got: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + mockStore.AssertValid(t) + + txn = storage.NewTransactionOrDie(ctx, mockStore) + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + assertEqual(t, rm.readAst, tc.expected, actual) + }) + } + }) + } +} + +func TestErasePolicies(t *testing.T) { + ctx := context.Background() + cases := []struct { + note string + initialPolicies map[string][]byte + roots []string + expectErr bool + expectedRemaining []string + }{ + { + note: "erase all", + initialPolicies: map[string][]byte{ + "mod1": []byte("package a\np = true"), + }, + roots: []string{""}, + expectErr: false, + expectedRemaining: []string{}, + }, + { + note: "erase none", + initialPolicies: map[string][]byte{ + "mod1": []byte("package a\np = true"), + "mod2": []byte("package b\np = true"), + }, + roots: []string{"c"}, + expectErr: false, + expectedRemaining: []string{"mod1", "mod2"}, + }, + { + note: "erase correct paths", + initialPolicies: map[string][]byte{ + "mod1": []byte("package a.test\np = true"), + "mod2": []byte("package a.test_v2\np = true"), + }, + roots: []string{"a/test"}, + expectErr: false, + expectedRemaining: []string{"mod2"}, + }, + { + note: "erase some", + initialPolicies: map[string][]byte{ + "mod1": []byte("package a\np = true"), + "mod2": []byte("package b\np = true"), + }, + roots: []string{"b"}, + expectErr: false, + expectedRemaining: []string{"mod1"}, + }, + { + note: "error: parsing module", + initialPolicies: map[string][]byte{ + "mod1": []byte("package a\np = true"), + "mod2": []byte("bad-policy-syntax"), + }, + roots: []string{"b"}, + expectErr: true, + expectedRemaining: []string{"mod1"}, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + mockStore := mock.New() + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + for name, mod := range tc.initialPolicies { + err := mockStore.UpsertPolicy(ctx, txn, name, mod) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + } + + roots := map[string]struct{}{} + for _, root := range tc.roots { + roots[root] = struct{}{} + } + remaining, _, err := erasePolicies(ctx, mockStore, txn, ast.ParserOptions{}, roots) + if !tc.expectErr && err != nil { + t.Fatalf("unepected error: %s", err) + } else if tc.expectErr && err == nil { + t.Fatalf("expected error, got: %s", err) + } + + if !tc.expectErr { + if len(remaining) != len(tc.expectedRemaining) { + t.Fatalf("expected %d modules remaining, got %d", len(tc.expectedRemaining), len(remaining)) + } + for _, name := range tc.expectedRemaining { + if _, ok := remaining[name]; !ok { + t.Fatalf("expected remaining module %s not found", name) + } + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + mockStore.AssertValid(t) + + txn = storage.NewTransactionOrDie(ctx, mockStore) + actualRemaining, err := mockStore.ListPolicies(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if len(actualRemaining) != len(tc.expectedRemaining) { + t.Fatalf("expected %d modules remaining in the store, got %d", len(tc.expectedRemaining), len(actualRemaining)) + } + for _, expectedName := range tc.expectedRemaining { + found := slices.Contains(actualRemaining, expectedName) + if !found { + t.Fatalf("expected remaining module %s not found", expectedName) + } + } + } + }) + } +} + +func TestWriteData(t *testing.T) { + storeReadModes := []struct { + note string + readAst bool + }{ + { + note: "read raw", + readAst: false, + }, + { + note: "read ast", + readAst: true, + }, + } + + ctx := context.Background() + cases := []struct { + note string + existingData map[string]any + roots []string + data map[string]any + expected string + expectErr bool + }{ + { + note: "single root", + roots: []string{"a"}, + data: map[string]any{ + "a": map[string]any{ + "b": map[string]any{ + "c": 123, + }, + }, + }, + expected: `{"a": {"b": {"c": 123}}}`, + expectErr: false, + }, + { + note: "multiple roots", + roots: []string{"a", "b/c/d"}, + data: map[string]any{ + "a": "foo", + "b": map[string]any{ + "c": map[string]any{ + "d": "bar", + }, + }, + }, + expected: `{"a": "foo","b": {"c": {"d": "bar"}}}`, + expectErr: false, + }, + { + note: "data not in roots", + roots: []string{"a"}, + data: map[string]any{ + "a": "foo", + "b": map[string]any{ + "c": map[string]any{ + "d": "bar", + }, + }, + }, + expected: `{"a": "foo"}`, + expectErr: false, + }, + { + note: "no data", + roots: []string{"a"}, + existingData: map[string]any{}, + data: map[string]any{}, + expected: `{}`, + expectErr: false, + }, + { + note: "no new data", + roots: []string{"a"}, + existingData: map[string]any{ + "a": "foo", + }, + data: map[string]any{}, + expected: `{"a": "foo"}`, + expectErr: false, + }, + { + note: "overwrite data", + roots: []string{"a"}, + existingData: map[string]any{ + "a": map[string]any{ + "b": "foo", + }, + }, + data: map[string]any{ + "a": "bar", + }, + expected: `{"a": "bar"}`, + expectErr: false, + }, + } + + for _, rm := range storeReadModes { + t.Run(rm.note, func(t *testing.T) { + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + mockStore := mock.NewWithData(tc.existingData, inmem.OptReturnASTValuesOnRead(rm.readAst)) + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + err := writeData(ctx, mockStore, txn, tc.roots, tc.data) + if !tc.expectErr && err != nil { + t.Fatalf("unepected error: %s", err) + } else if tc.expectErr && err == nil { + t.Fatalf("expected error, got: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + mockStore.AssertValid(t) + + txn = storage.NewTransactionOrDie(ctx, mockStore) + actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + assertEqual(t, rm.readAst, tc.expected, actual) + }) + } + }) + } +} + +func loadExpectedResult(input string) any { + if len(input) == 0 { + return nil + } + var data any + if err := util.UnmarshalJSON([]byte(input), &data); err != nil { + panic(err) + } + return data +} + +func loadExpectedSortedResult(input string) any { + data := loadExpectedResult(input) + switch data := data.(type) { + case []any: + return data + default: + return data + } +} + +type testWriteModuleCase struct { + note string + bundles map[string]*Bundle // Only need to give raw text and path for modules + extraMods map[string]*ast.Module + compilerMods map[string]*ast.Module + storeData map[string]any + expectErr bool +} + +func TestWriteModules(t *testing.T) { + + cases := []testWriteModuleCase{ + { + note: "module files only", + bundles: map[string]*Bundle{ + "bundle1": { + Modules: []ModuleFile{ + { + Path: "mod1", + Raw: []byte("package a\np = true"), + }, + }, + }, + }, + expectErr: false, + }, + { + note: "extra modules only", + extraMods: map[string]*ast.Module{ + "mod1": ast.MustParseModule("package a\np = true"), + }, + expectErr: false, + }, + { + note: "compiler modules only", + compilerMods: map[string]*ast.Module{ + "mod1": ast.MustParseModule("package a\np = true"), + }, + expectErr: false, + }, + { + note: "module files and extra modules", + bundles: map[string]*Bundle{ + "bundle1": { + Modules: []ModuleFile{ + { + Path: "mod1", + Raw: []byte("package a\np = true"), + }, + }, + }, + }, + extraMods: map[string]*ast.Module{ + "mod2": ast.MustParseModule("package b\np = false"), + }, + expectErr: false, + }, + { + note: "module files and compiler modules", + bundles: map[string]*Bundle{ + "bundle1": { + Modules: []ModuleFile{ + { + Path: "mod1", + Raw: []byte("package a\np = true"), + }, + }, + }, + }, + compilerMods: map[string]*ast.Module{ + "mod2": ast.MustParseModule("package b\np = false"), + }, + expectErr: false, + }, + { + note: "extra modules and compiler modules", + extraMods: map[string]*ast.Module{ + "mod1": ast.MustParseModule("package a\np = true"), + }, + compilerMods: map[string]*ast.Module{ + "mod2": ast.MustParseModule("package b\np = false"), + }, + expectErr: false, + }, + { + note: "compile error: path conflict", + bundles: map[string]*Bundle{ + "bundle1": { + Modules: []ModuleFile{ + { + Path: "mod1", + Raw: []byte("package a\np = true"), + }, + }, + }, + }, + storeData: map[string]any{ + "a": map[string]any{ + "p": "foo", + }, + }, + expectErr: true, + }, + } + + for _, tc := range cases { + testWriteData(t, tc, false) + testWriteData(t, tc, true) + } +} + +func testWriteData(t *testing.T, tc testWriteModuleCase, legacy bool) { + t.Helper() + + testName := tc.note + if legacy { + testName += "_legacy" + } + + t.Run(testName, func(t *testing.T) { + + ctx := context.Background() + mockStore := mock.NewWithData(tc.storeData) + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + compiler := ast.NewCompiler().WithPathConflictsCheck(storage.NonEmpty(ctx, mockStore, txn)) + m := metrics.New() + + // if supplied, pre-parse the module files + + for _, b := range tc.bundles { + var parsedMods []ModuleFile + for _, mf := range b.Modules { + parsedMods = append(parsedMods, ModuleFile{ + Path: mf.Path, + Raw: mf.Raw, + Parsed: ast.MustParseModule(string(mf.Raw)), + }) + } + b.Modules = parsedMods + } + + // if supplied, setup the compiler with modules already compiled on it + if len(tc.compilerMods) > 0 { + compiler.Compile(tc.compilerMods) + if len(compiler.Errors) > 0 { + t.Fatalf("unexpected error: %s", compiler.Errors) + } + } + + err := writeModules(ctx, mockStore, txn, compiler, m, tc.bundles, tc.extraMods, legacy) + if !tc.expectErr && err != nil { + t.Fatalf("unepected error: %s", err) + } else if tc.expectErr && err == nil { + t.Fatalf("expected error, got: %s", err) + } + + if !tc.expectErr { + // ensure all policy files were saved to storage + policies, err := mockStore.ListPolicies(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedNumMods := 0 + for _, b := range tc.bundles { + expectedNumMods += len(b.Modules) + } + + if len(policies) != expectedNumMods { + t.Fatalf("expected %d policies in storage, found %d", expectedNumMods, len(policies)) + } + + for bundleName, b := range tc.bundles { + for _, mf := range b.Modules { + found := false + for _, p := range policies { + var expectedPath string + if legacy { + expectedPath = mf.Path + } else { + expectedPath = filepath.Join(bundleName, mf.Path) + } + if p == expectedPath { + found = true + break + } + } + if !found { + t.Fatalf("policy %s not found in storage", mf.Path) + } + } + } + + // ensure all the modules were compiled together and we aren't missing any + expectedModCount := expectedNumMods + len(tc.extraMods) + len(tc.compilerMods) + if len(compiler.Modules) != expectedModCount { + t.Fatalf("expected %d modules on compiler, found %d", expectedModCount, len(compiler.Modules)) + } + + for moduleName := range compiler.Modules { + found := false + if _, ok := tc.extraMods[moduleName]; ok { + continue + } + if _, ok := tc.compilerMods[moduleName]; ok { + continue + } + for bundleName, b := range tc.bundles { + if legacy { + for _, mf := range b.Modules { + if moduleName == mf.Path { + found = true + break + } + } + } else { + for bundleModuleName := range b.ParsedModules(bundleName) { + if moduleName == bundleModuleName { + found = true + break + } + } + } + } + if found { + continue + } + t.Errorf("unexpected module %s on compiler", moduleName) + } + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + mockStore.AssertValid(t) + }) +} + +func TestDoDFS(t *testing.T) { + + cases := []struct { + note string + input map[string]json.RawMessage + path string + roots []string + wantErr bool + err error + }{ + { + note: "bundle owns all", + input: nil, + path: "/", + roots: []string{""}, + wantErr: false, + }, + { + note: "data within roots root case", + input: map[string]json.RawMessage{"a": json.RawMessage(`true`)}, + path: "", + roots: []string{"a"}, + wantErr: false, + }, + { + note: "data within roots nested 1", + input: map[string]json.RawMessage{"d": json.RawMessage(`true`)}, + path: filepath.Dir(strings.Trim("a/b/c/data.json", "/")), + roots: []string{"a/b/c"}, + wantErr: false, + }, + { + note: "data within roots nested 2", + input: map[string]json.RawMessage{"d": json.RawMessage(`{"hello": "world"}`)}, + path: filepath.Dir(strings.Trim("a/b/c/data.json", "/")), + roots: []string{"a/b/c"}, + wantErr: false, + }, + { + note: "data within roots nested 3", + input: map[string]json.RawMessage{"d": json.RawMessage(`{"hello": "world"}`)}, + path: filepath.Dir(strings.Trim("a/data.json", "/")), + roots: []string{"a/d"}, + wantErr: false, + }, + { + note: "data within multiple roots 1", + input: map[string]json.RawMessage{"a": json.RawMessage(`{"b": "c"}`), "c": json.RawMessage(`true`)}, + path: filepath.Dir(strings.Trim("/data.json", "/")), + roots: []string{"a/b", "c"}, + wantErr: false, + }, + { + note: "data within multiple roots 2", + input: map[string]json.RawMessage{"a": json.RawMessage(`{"b": "c"}`), "c": []byte(`{"d": {"e": {"f": true}}}`)}, + path: filepath.Dir(strings.Trim("/data.json", "/")), + roots: []string{"a/b", "c/d/e"}, + wantErr: false, + }, + { + note: "data outside roots 1", + input: map[string]json.RawMessage{"d": json.RawMessage(`{"hello": "world"}`)}, + path: filepath.Dir(strings.Trim("/data.json", "/")), + roots: []string{"a/d"}, + wantErr: true, + err: errors.New("manifest roots [a/d] do not permit data at path '/d' (hint: check bundle directory structure)"), + }, + { + note: "data outside roots 2", + input: map[string]json.RawMessage{"a": []byte(`{"b": {"c": {"e": true}}}`)}, + path: filepath.Dir(strings.Trim("/x/data.json", "/")), + roots: []string{"x/a/b/c/d"}, + wantErr: true, + err: errors.New("manifest roots [x/a/b/c/d] do not permit data at path '/x/a/b/c/e' (hint: check bundle directory structure)"), + }, + { + note: "data outside roots 3", + input: map[string]json.RawMessage{"a": []byte(`{"b": {"c": true}}`)}, + path: filepath.Dir(strings.Trim("/data.json", "/")), + roots: []string{"a/b/c/d"}, + wantErr: true, + err: errors.New("manifest roots [a/b/c/d] do not permit data at path '/a/b/c' (hint: check bundle directory structure)"), + }, + { + note: "data outside multiple roots", + input: map[string]json.RawMessage{"a": json.RawMessage(`{"b": "c"}`), "e": []byte(`{"b": {"c": true}}`)}, + path: filepath.Dir(strings.Trim("/data.json", "/")), + roots: []string{"a/b", "c"}, + wantErr: true, + err: errors.New("manifest roots [a/b c] do not permit data at path '/e' (hint: check bundle directory structure)"), + }, + { + note: "data outside multiple roots 2", + input: map[string]json.RawMessage{"a": json.RawMessage(`{"b": "c"}`), "c": []byte(`{"d": true}`)}, + path: filepath.Dir(strings.Trim("/data.json", "/")), + roots: []string{"a/b", "c/d/e"}, + wantErr: true, + err: errors.New("manifest roots [a/b c/d/e] do not permit data at path '/c/d' (hint: check bundle directory structure)"), + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + + err := doDFS(tc.input, tc.path, tc.roots) + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + } +} + +func TestHasRootsOverlap(t *testing.T) { + ctx := context.Background() + + cases := []struct { + note string + storeRoots map[string]*[]string + newBundleRoots map[string]*[]string + expectedError string + }{ + { + note: "no overlap between store and new bundles", + storeRoots: map[string]*[]string{"bundle1": {"a", "b"}}, + newBundleRoots: map[string]*[]string{"bundle2": {"c"}}, + }, + { + note: "no overlap between store and multiple new bundles", + storeRoots: map[string]*[]string{"bundle1": {"a", "b"}}, + newBundleRoots: map[string]*[]string{"bundle2": {"c"}, "bundle3": {"d"}}, + }, + { + note: "no overlap with empty store", + storeRoots: map[string]*[]string{}, + newBundleRoots: map[string]*[]string{"bundle1": {"a", "b"}}, + }, + { + note: "no overlap between multiple new bundles with empty store", + storeRoots: map[string]*[]string{}, + newBundleRoots: map[string]*[]string{"bundle1": {"a", "b"}, "bundle2": {"c"}}, + }, + { + note: "overlap between multiple new bundles with empty store", + storeRoots: map[string]*[]string{}, + newBundleRoots: map[string]*[]string{"bundle1": {"a", "b"}, "bundle2": {"a", "c"}}, + expectedError: "detected overlapping roots in manifests for these bundles: [bundle1, bundle2] (root a is in multiple bundles)", + }, + { + note: "overlap between store and new bundle", + storeRoots: map[string]*[]string{"bundle1": {"a", "b"}}, + newBundleRoots: map[string]*[]string{"bundle2": {"c", "a"}}, + expectedError: "detected overlapping roots in manifests for these bundles: [bundle1, bundle2] (root a is in multiple bundles)", + }, + { + note: "overlap between store and multiple new bundles", + storeRoots: map[string]*[]string{"bundle1": {"a", "b"}}, + newBundleRoots: map[string]*[]string{"bundle2": {"c", "a"}, "bundle3": {"a"}}, + expectedError: "detected overlapping roots in manifests for these bundles: [bundle1, bundle2, bundle3] (root a is in multiple bundles)", + }, + { + note: "overlap between store bundle and new empty root bundle", + storeRoots: map[string]*[]string{"bundle1": {"a", "b"}}, + newBundleRoots: map[string]*[]string{"bundle2": {""}}, + expectedError: "bundles [bundle1, bundle2] have overlapping roots and cannot be activated simultaneously because bundle(s) [bundle2] specify empty root paths ('') which overlap with any other bundle root", + }, + { + note: "overlap between multiple new empty root bundles", + storeRoots: map[string]*[]string{}, + newBundleRoots: map[string]*[]string{"bundle1": {""}, "bundle2": {""}}, + expectedError: "bundles [bundle1, bundle2] have overlapping roots and cannot be activated simultaneously because bundle(s) [bundle1, bundle2] specify empty root paths ('') which overlap with any other bundle root", + }, + { + note: "overlap between new empty root and new regular root bundles", + storeRoots: map[string]*[]string{}, + newBundleRoots: map[string]*[]string{"bundle1": {"a"}, "bundle2": {""}}, + expectedError: "bundles [bundle1, bundle2] have overlapping roots and cannot be activated simultaneously because bundle(s) [bundle2] specify empty root paths ('') which overlap with any other bundle root", + }, + { + note: "overlap between nested paths", + storeRoots: map[string]*[]string{}, + newBundleRoots: map[string]*[]string{"bundle1": {"a"}, "bundle2": {"a/b"}}, + expectedError: "detected overlapping roots in manifests for these bundles: [bundle1, bundle2] (a overlaps a/b)", + }, + { + note: "overlap between store nested path and new bundle path", + storeRoots: map[string]*[]string{"bundle1": {"a/b"}}, + newBundleRoots: map[string]*[]string{"bundle2": {"a"}}, + expectedError: "detected overlapping roots in manifests for these bundles: [bundle1, bundle2] (a overlaps a/b)", + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + mockStore := mock.New() + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + + for name, roots := range tc.storeRoots { + err := WriteManifestToStore(ctx, mockStore, txn, name, Manifest{Roots: roots}) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + } + + bundles := map[string]*Bundle{} + for name, roots := range tc.newBundleRoots { + bundles[name] = &Bundle{ + Manifest: Manifest{ + Roots: roots, + }, + } + } + + err := hasRootsOverlap(ctx, mockStore, txn, bundles) + if tc.expectedError != "" { + if err == nil { + t.Fatalf("expected error %q, got nil", tc.expectedError) + } + if err.Error() != tc.expectedError { + t.Fatalf("expected error message %q, got %q", tc.expectedError, err.Error()) + } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + err = mockStore.Commit(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + mockStore.AssertValid(t) + }) + } +} + +func TestBundleStoreHelpers(t *testing.T) { + storeReadModes := []struct { + note string + readAst bool + }{ + { + note: "read raw", + readAst: false, + }, + { + note: "read ast", + readAst: true, + }, + } + + ctx := context.Background() + + bundles := map[string]*Bundle{ + "bundle1": { + Manifest: Manifest{ + Roots: &[]string{}, + }, + }, + "bundle2": { + Manifest: Manifest{ + Roots: &[]string{"a"}, + Revision: "foo", + Metadata: map[string]any{ + "a": "b", + }, + WasmResolvers: []WasmResolver{ + { + Entrypoint: "foo/bar", + Module: "m.wasm", + }, + }, + }, + Etag: "bar", + WasmModules: []WasmModuleFile{ + { + Path: "/m.wasm", + Raw: []byte("d2FzbS1tb2R1bGU="), + }, + }, + }, + } + + for _, srm := range storeReadModes { + t.Run(srm.note, func(t *testing.T) { + mockStore := mock.NewWithData(nil, inmem.OptReturnASTValuesOnRead(srm.readAst)) + txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams) + c := ast.NewCompiler() + m := metrics.New() + + err := Activate(&ActivateOpts{ + Ctx: ctx, + Store: mockStore, + Txn: txn, + Compiler: c, + Metrics: m, + Bundles: bundles, + }) + + if err != nil { + t.Fatal(err) + } + + // Bundle names + + if names, err := ReadBundleNamesFromStore(ctx, mockStore, txn); err != nil { + t.Fatalf("unexpected error: %s", err) + } else if len(names) != len(bundles) { + t.Errorf("expected bundle names:\n\n%v\n\nin store, found\n\n%v", bundles, names) + } else { + for _, name := range names { + if _, ok := bundles[name]; !ok { + t.Errorf("expected bundle names:\n\n%v\n\nin store, found\n\n%v", bundles, names) + } + } + } + + // Etag + + if etag, err := ReadBundleEtagFromStore(ctx, mockStore, txn, "bundle1"); err != nil { + t.Fatalf("unexpected error: %s", err) + } else if etag != "" { + t.Errorf("expected empty etag but got %s", etag) + } + + if etag, err := ReadBundleEtagFromStore(ctx, mockStore, txn, "bundle2"); err != nil { + t.Fatalf("unexpected error: %s", err) + } else if exp := "bar"; etag != exp { + t.Errorf("expected etag %s but got %s", exp, etag) + } + + // Revision + + if rev, err := ReadBundleRevisionFromStore(ctx, mockStore, txn, "bundle1"); err != nil { + t.Fatalf("unexpected error: %s", err) + } else if rev != "" { + t.Errorf("expected empty revision but got %s", rev) + } + + if rev, err := ReadBundleRevisionFromStore(ctx, mockStore, txn, "bundle2"); err != nil { + t.Fatalf("unexpected error: %s", err) + } else if exp := "foo"; rev != exp { + t.Errorf("expected revision %s but got %s", exp, rev) + } + + // Roots + + if roots, err := ReadBundleRootsFromStore(ctx, mockStore, txn, "bundle1"); err != nil { + t.Fatalf("unexpected error: %s", err) + } else if len(roots) != 0 { + t.Errorf("expected empty roots but got %v", roots) + } + + if roots, err := ReadBundleRootsFromStore(ctx, mockStore, txn, "bundle2"); err != nil { + t.Fatalf("unexpected error: %s", err) + } else if exp := *bundles["bundle2"].Manifest.Roots; !reflect.DeepEqual(exp, roots) { + t.Errorf("expected roots %v but got %v", exp, roots) + } + + // Bundle metadata + + if meta, err := ReadBundleMetadataFromStore(ctx, mockStore, txn, "bundle1"); err != nil { + t.Fatalf("unexpected error: %s", err) + } else if len(meta) != 0 { + t.Errorf("expected empty metadata but got %v", meta) + } + + if meta, err := ReadBundleMetadataFromStore(ctx, mockStore, txn, "bundle2"); err != nil { + t.Fatalf("unexpected error: %s", err) + } else if exp := bundles["bundle2"].Manifest.Metadata; !reflect.DeepEqual(exp, meta) { + t.Errorf("expected metadata %v but got %v", exp, meta) + } + + // Wasm metadata + + if _, err := ReadWasmMetadataFromStore(ctx, mockStore, txn, "bundle1"); err == nil { + t.Fatalf("expected error but got nil") + } else if exp, act := "storage_not_found_error: /bundles/bundle1/manifest/wasm: document does not exist", err.Error(); !strings.Contains(act, exp) { + t.Fatalf("expected error:\n\n%s\n\nbut got:\n\n%v", exp, act) + } + + if resolvers, err := ReadWasmMetadataFromStore(ctx, mockStore, txn, "bundle2"); err != nil { + t.Fatalf("unexpected error: %s", err) + } else if exp := bundles["bundle2"].Manifest.WasmResolvers; !reflect.DeepEqual(exp, resolvers) { + t.Errorf("expected wasm metadata:\n\n%v\n\nbut got:\n\n%v", exp, resolvers) + } + + // Wasm modules + + if _, err := ReadWasmModulesFromStore(ctx, mockStore, txn, "bundle1"); err == nil { + t.Fatalf("expected error but got nil") + } else if exp, act := "storage_not_found_error: /bundles/bundle1/wasm: document does not exist", err.Error(); !strings.Contains(act, exp) { + t.Fatalf("expected error:\n\n%s\n\nbut got:\n\n%v", exp, act) + } + + if modules, err := ReadWasmModulesFromStore(ctx, mockStore, txn, "bundle2"); err != nil { + t.Fatalf("unexpected error: %s", err) + } else if exp := bundles["bundle2"].WasmModules; len(exp) != len(modules) { + t.Errorf("expected wasm modules:\n\n%v\n\nbut got:\n\n%v", exp, modules) + } else { + for _, exp := range bundles["bundle2"].WasmModules { + act := modules[exp.Path] + if act == nil { + t.Errorf("expected wasm module %s but got nil", exp.Path) + } + if !bytes.Equal(exp.Raw, act) { + t.Errorf("expected wasm module %s to have raw data:\n\n%v\n\nbut got:\n\n%v", exp.Path, exp.Raw, act) + } + } + } + + }) + } +} + +func TestActivate_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + customRegoVersion ast.RegoVersion + expErrs []string + }{ + // NOT default rego-version + { + note: "v0 module", + module: `package test + p[x] { + x = "a" + }`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + + // cross-rego-version + { + note: "rego.v1 import, no v1 parse-time violations", + module: `package test + import rego.v1 + + p contains x if { + x = "a" + }`, + }, + { + note: "rego.v1 import, v1 parse-time violations", + module: `package test + import rego.v1 + + p contains x { + x = "a" + }`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + }, + }, + + // default rego-version + { + note: "v1 module, no v1 parse-time violations", + module: `package test + + p contains x if { + x = "a" + }`, + }, + { + note: "v1 module, v1 parse-time violations", + module: `package test + + p contains x { + x = "a" + }`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + }, + }, + + // custom rego-version + { + note: "v0 module, v0 custom rego-version", + module: `package test + p[x] { + x = "a" + }`, + customRegoVersion: ast.RegoV0, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := mock.New() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + compiler := ast.NewCompiler().WithDefaultRegoVersion(ast.RegoV0CompatV1) + m := metrics.New() + + bundleName := "bundle1" + modulePath := "test/policy.rego" + + // We want to make assert that the default rego-version is used, which it is when a module is erased from storage and we don't know what version it has. + // Therefore, we add a module to the store, which is the replaced by the Activate() call, causing an erase. + if err := store.UpsertPolicy(ctx, txn, modulePathWithPrefix(bundleName, modulePath), []byte(tc.module)); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + newModule := `package test` + bundles := map[string]*Bundle{ + bundleName: { + Manifest: Manifest{ + Roots: &[]string{"test"}, + }, + Modules: []ModuleFile{ + { + Path: modulePath, + Raw: []byte(newModule), + Parsed: ast.MustParseModule(newModule), + }, + }, + }, + } + + opts := ActivateOpts{ + Ctx: ctx, + Txn: txn, + Store: store, + Compiler: compiler, + Metrics: m, + Bundles: bundles, + } + + if tc.customRegoVersion != ast.RegoUndefined { + opts.ParserOptions.RegoVersion = tc.customRegoVersion + } + + err := Activate(&opts) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got nil for test: %s", tc.note) + } + for _, expErr := range tc.expErrs { + if err := err.Error(); !strings.Contains(err, expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + } +} + +func TestDeactivate_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + customRegoVersion ast.RegoVersion + expErrs []string + }{ + // NOT default rego-version + { + note: "v0 module", + module: `package test + p[x] { + x = "a" + }`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + + // cross-rego-version + { + note: "rego.v1 import, no v1 parse-time violations", + module: `package test + import rego.v1 + + p contains x if { + x = "a" + }`, + }, + { + note: "rego.v1 import, v1 parse-time violations", + module: `package test + import rego.v1 + + p contains x { + x = "a" + }`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + }, + }, + + // default rego-version + { + note: "v1 module, no v1 parse-time violations", + module: `package test + + p contains x if { + x = "a" + }`, + }, + { + note: "v1 module, v1 parse-time violations", + module: `package test + + p contains x { + x = "a" + }`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + }, + }, + + // custom rego-version + { + note: "v0 module, v0 custom rego-version", + module: `package test + p[x] { + x = "a" + }`, + customRegoVersion: ast.RegoV0, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := mock.New() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + bundleName := "bundle1" + modulePath := "test/policy.rego" + + // We want to make assert that the default rego-version is used, which it is when a module is erased from storage and we don't know what version it has. + // Therefore, we add a module to the store, which is the replaced by the Activate() call, causing an erase. + if err := store.UpsertPolicy(ctx, txn, modulePathWithPrefix(bundleName, modulePath), []byte(tc.module)); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + opts := DeactivateOpts{ + Ctx: ctx, + Txn: txn, + Store: store, + BundleNames: map[string]struct{}{ + modulePathWithPrefix(bundleName, modulePath): {}, + }, + } + + if tc.customRegoVersion != ast.RegoUndefined { + opts.ParserOptions.RegoVersion = tc.customRegoVersion + } + + err := Deactivate(&opts) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got nil for test: %s", tc.note) + } + for _, expErr := range tc.expErrs { + if err := err.Error(); !strings.Contains(err, expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + } +} diff --git a/third_party/opa/v1/bundle/verify.go b/third_party/opa/v1/bundle/verify.go new file mode 100644 index 000000000000..829e98acdf8c --- /dev/null +++ b/third_party/opa/v1/bundle/verify.go @@ -0,0 +1,232 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package bundle provide helpers that assist in the bundle signature verification process +package bundle + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jws" + "github.com/open-policy-agent/opa/internal/jwx/jws/verify" + "github.com/open-policy-agent/opa/v1/util" +) + +const defaultVerifierID = "_default" + +var verifiers map[string]Verifier + +// Verifier is the interface expected for implementations that verify bundle signatures. +type Verifier interface { + VerifyBundleSignature(SignaturesConfig, *VerificationConfig) (map[string]FileInfo, error) +} + +// VerifyBundleSignature will retrieve the Verifier implementation based +// on the Plugin specified in SignaturesConfig, and call its implementation +// of VerifyBundleSignature. VerifyBundleSignature verifies the bundle signature +// using the given public keys or secret. If a signature is verified, it keeps +// track of the files specified in the JWT payload +func VerifyBundleSignature(sc SignaturesConfig, bvc *VerificationConfig) (map[string]FileInfo, error) { + // default implementation does not return a nil for map, so don't + // do it here either + files := make(map[string]FileInfo) + var plugin string + // for backwards compatibility, check if there is no plugin specified, and use default + if sc.Plugin == "" { + plugin = defaultVerifierID + } else { + plugin = sc.Plugin + } + verifier, err := GetVerifier(plugin) + if err != nil { + return files, err + } + return verifier.VerifyBundleSignature(sc, bvc) +} + +// DefaultVerifier is the default bundle verification implementation. It verifies bundles by checking +// the JWT signature using a locally-accessible public key. +type DefaultVerifier struct{} + +// VerifyBundleSignature verifies the bundle signature using the given public keys or secret. +// If a signature is verified, it keeps track of the files specified in the JWT payload +func (*DefaultVerifier) VerifyBundleSignature(sc SignaturesConfig, bvc *VerificationConfig) (map[string]FileInfo, error) { + files := make(map[string]FileInfo) + + if len(sc.Signatures) == 0 { + return files, errors.New(".signatures.json: missing JWT (expected exactly one)") + } + + if len(sc.Signatures) > 1 { + return files, errors.New(".signatures.json: multiple JWTs not supported (expected exactly one)") + } + + for _, token := range sc.Signatures { + payload, err := verifyJWTSignature(token, bvc) + if err != nil { + return files, err + } + + for _, file := range payload.Files { + files[file.Name] = file + } + } + return files, nil +} + +func verifyJWTSignature(token string, bvc *VerificationConfig) (*DecodedSignature, error) { + // decode JWT to check if the header specifies the key to use and/or if claims have the scope. + + parts, err := jws.SplitCompact(token) + if err != nil { + return nil, err + } + + var decodedHeader []byte + if decodedHeader, err = base64.RawURLEncoding.DecodeString(parts[0]); err != nil { + return nil, fmt.Errorf("failed to base64 decode JWT headers: %w", err) + } + + var hdr jws.StandardHeaders + if err := json.Unmarshal(decodedHeader, &hdr); err != nil { + return nil, fmt.Errorf("failed to parse JWT headers: %w", err) + } + + payload, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + return nil, err + } + + var ds DecodedSignature + if err := json.Unmarshal(payload, &ds); err != nil { + return nil, err + } + + // check for the id of the key to use for JWT signature verification + // first in the OPA config. If not found, then check the JWT kid. + keyID := bvc.KeyID + if keyID == "" { + keyID = hdr.KeyID + } + if keyID == "" { + // If header has no key id, check the deprecated key claim. + keyID = ds.KeyID + } + + if keyID == "" { + return nil, errors.New("verification key ID is empty") + } + + // now that we have the keyID, fetch the actual key + keyConfig, err := bvc.GetPublicKey(keyID) + if err != nil { + return nil, err + } + + // verify JWT signature + alg := jwa.SignatureAlgorithm(keyConfig.Algorithm) + key, err := verify.GetSigningKey(keyConfig.Key, alg) + if err != nil { + return nil, err + } + + _, err = jws.Verify([]byte(token), alg, key) + if err != nil { + return nil, err + } + + // verify the scope + scope := bvc.Scope + if scope == "" { + scope = keyConfig.Scope + } + + if ds.Scope != scope { + return nil, errors.New("scope mismatch") + } + return &ds, nil +} + +// VerifyBundleFile verifies the hash of a file in the bundle matches to that provided in the bundle's signature +func VerifyBundleFile(path string, data bytes.Buffer, files map[string]FileInfo) error { + var file FileInfo + var ok bool + + if file, ok = files[path]; !ok { + return fmt.Errorf("file %v not included in bundle signature", path) + } + + if file.Algorithm == "" { + return fmt.Errorf("no hashing algorithm provided for file %v", path) + } + + hash, err := NewSignatureHasher(HashingAlgorithm(file.Algorithm)) + if err != nil { + return err + } + + // hash the file content + // For unstructured files, hash the byte stream of the file + // For structured files, read the byte stream and parse into a JSON structure; + // then recursively order the fields of all objects alphabetically and then apply + // the hash function to result to compute the hash. This ensures that the digital signature is + // independent of whitespace and other non-semantic JSON features. + var value any + if IsStructuredDoc(path) { + err := util.Unmarshal(data.Bytes(), &value) + if err != nil { + return err + } + } else { + value = data.Bytes() + } + + bs, err := hash.HashFile(value) + if err != nil { + return err + } + + // compare file hash with same file in the JWT payloads + fb, err := hex.DecodeString(file.Hash) + if err != nil { + return err + } + + if !bytes.Equal(fb, bs) { + return fmt.Errorf("%v: digest mismatch (want: %x, got: %x)", path, fb, bs) + } + + delete(files, path) + return nil +} + +// GetVerifier returns the Verifier registered under the given id +func GetVerifier(id string) (Verifier, error) { + verifier, ok := verifiers[id] + if !ok { + return nil, fmt.Errorf("no verifier exists under id %s", id) + } + return verifier, nil +} + +// RegisterVerifier registers a Verifier under the given id +func RegisterVerifier(id string, v Verifier) error { + if id == defaultVerifierID { + return fmt.Errorf("verifier id %s is reserved, use a different id", id) + } + verifiers[id] = v + return nil +} + +func init() { + verifiers = map[string]Verifier{ + defaultVerifierID: &DefaultVerifier{}, + } +} diff --git a/third_party/opa/v1/bundle/verify_test.go b/third_party/opa/v1/bundle/verify_test.go new file mode 100644 index 000000000000..c73b293d99b1 --- /dev/null +++ b/third_party/opa/v1/bundle/verify_test.go @@ -0,0 +1,315 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "bytes" + "errors" + "testing" +) + +func TestVerifyBundleSignature(t *testing.T) { + badToken := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.4nrInalqppAc9EjsNUD9Y35amVpDGoRk4bkxzdY8fhs` + signedTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.u7b_h88osJpU6VQLhIUmZjblsPNCO_kTqsVtpEAHavs` + otherSignedTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTMvcm9sZXMvZGF0YS5qc29uIiwiaGFzaCI6ImMyMTMxNTQ0YzcxNmEyNWE1ZTMxZjUwNDMwMGY1MjQwZTgyMzVjYWRiOWE1N2YwYmQxYjZmNGJkNzRiMjY2MTIiLCJhbGdvcml0aG0iOiJtZDUifSx7Im5hbWUiOiJkYi91YW0zL3BvbGljeS9wb2xpY3kucmVnbyIsImhhc2giOiI0MmNmZTY3NjhiNTdiYjVmNzUwM2MxNjVjMjhkZDA3YWM1YjgxMzU1NGViYzg1MGYyY2MzNTg0M2U3MTM3YjFkIn0seyJuYW1lIjoiZGIvdWFtNC9wb2xpY3kvcmVnby5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQiLCJhbGdvcml0aG0iOiJzaGEzODQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsImtleWlkIjoiYmFyIiwic2NvcGUiOiJ3cml0ZSJ9.d_NiBXF3zqNPZCEubQC1FC1IYwmwkYwjv00B5UyJ9Dk` + badTokenHeaderBase64 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvby 9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.u7b_h88osJpU6VQLhIUmZjblsPNCO_kTqsVtpEAHavs` + badTokenHeaderJSON := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyX9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.u7b_h88osJpU6VQLhIUmZjblsPNCO_kTqsVtpEAHavs` + badTokenPayload := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaWxlcyI6eyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifSwiaWF0IjoxNTkyMjQ4MDI3LCJpc3MiOiJKV1RTZXJ2aWNlIiwia2V5aWQiOiJmb28iLCJzY29wZSI6IndyaXRlIn0.J3KJFOycHPy4Wkw_LzzIKvTMqCsV8L8DdQW5Q-vieKg` + + tests := map[string]struct { + input SignaturesConfig + readerVerifyConfig *VerificationConfig + wantErr bool + err error + }{ + "no_signatures": {SignaturesConfig{}, nil, true, errors.New(".signatures.json: missing JWT (expected exactly one)")}, + "multiple_signatures": {SignaturesConfig{Signatures: []string{signedTokenHS256, otherSignedTokenHS256}}, nil, true, errors.New(".signatures.json: multiple JWTs not supported (expected exactly one)")}, + "invalid_token": {SignaturesConfig{Signatures: []string{badToken}}, nil, true, errors.New("failed to split compact serialization")}, + "invalid_token_header_base64": { + SignaturesConfig{Signatures: []string{badTokenHeaderBase64}}, + NewVerificationConfig(nil, "", "", nil), + true, errors.New("failed to base64 decode JWT headers: illegal base64 data at input byte 50"), + }, + "invalid_token_header_json": { + SignaturesConfig{Signatures: []string{badTokenHeaderJSON}}, + NewVerificationConfig(nil, "", "", nil), + true, errors.New("failed to parse JWT headers: unexpected end of JSON input"), + }, + "bad_token_payload": {SignaturesConfig{Signatures: []string{badTokenPayload}}, nil, true, errors.New("json: cannot unmarshal object into Go struct field DecodedSignature.files of type []bundle.FileInfo")}, + "valid_token_and_scope": { + SignaturesConfig{Signatures: []string{signedTokenHS256}}, + NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", nil), + false, nil, + }, + "valid_token_and_scope_mismatch": { + SignaturesConfig{Signatures: []string{signedTokenHS256}}, + NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "bad_scope", nil), + true, errors.New("scope mismatch"), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + _, err := VerifyBundleSignature(tc.input, tc.readerVerifyConfig) + + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + } + + // verify the number files on the reader collected from the JWT + sc := SignaturesConfig{Signatures: []string{signedTokenHS256}} + verificationConfig := NewVerificationConfig(map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", nil) + files, err := VerifyBundleSignature(sc, verificationConfig) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + expectedNumFiles := 2 + if len(files) != expectedNumFiles { + t.Fatalf("Expected %v files in the JWT payloads but got %v", expectedNumFiles, len(files)) + } +} + +func TestVerifyJWTSignature(t *testing.T) { + signedNoKeyIDTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.N2DaEyhfACMAij2sRC0ZDwNz4wI7BX_flH3IkKqMvE4` + + signedTokenHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.u7b_h88osJpU6VQLhIUmZjblsPNCO_kTqsVtpEAHavs` + + signedTokenWithDeprecatedKidClaimHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsImtleWlkIjoiZm9vIiwic2NvcGUiOiJ3cml0ZSJ9.4nrInalqppAc9EjsNUD9Y35amVpDGoRk4bkxzdY8fhs` + + signedTokenWithBarKidHS256 := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJhciJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI0ODAyNywiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.xW04tph2-dSy47uZ-CgiObvFWFLUsMym8N7ermgPZ0s` + + signedTokenRS256 := `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJmaWxlcyI6W3sibmFtZSI6ImRiL3VhbTIvZW50aXRsZW1lbnRzL2RhdGEuanNvbiIsImhhc2giOiJjMjEzMTU0NGM3MTZhMjVhNWUzMWY1MDQzMDBmNTI0MGU4MjM1Y2FkYjlhNTdmMGJkMWI2ZjRiZDc0YjI2NjEyIiwiYWxnb3JpdGhtIjoic2hhMjU2In0seyJuYW1lIjoiZGIvdWFtMi9wb2xpY3kvb3BhLXBvbGljeS5yZWdvIiwiaGFzaCI6IjQyY2ZlNjc2OGI1N2JiNWY3NTAzYzE2NWMyOGRkMDdhYzViODEzNTU0ZWJjODUwZjJjYzM1ODQzZTcxMzdiMWQifV0sImlhdCI6MTU5MjI1MTQwNiwiaXNzIjoiSldUU2VydmljZSIsInNjb3BlIjoid3JpdGUifQ.UZcX_Qj29o213nwZ5mysiyNcgAp1AOqCkDLkj1KGZ7Cw3l17TbkF8t3FJd654sf762NGcABA0WQ2zxq-labXl_d5TKQmq23lurO3qLHI1cKQPyoxPbddGo78sJpMwWsiMyEYcyyKC9FpNUpa8cT81GPo4q9Zui8TAD0wtHh2YxBL7E8QYLJxcLMJTJxVBgrQGLXFT7vYRc6hJz9Vu-i0SWuLBGxOCmYoHr4DWFfy0IhAbhiUFyLRpukfBPjCJNYY5LgaZdN6LaYivtIEUms5jUuXubBimLJm7KOT5-bgJMXTcUsbrbl2Ma7PwG5IOKjjgyEvH8KVfGFZSzNrpuxKmA` + + publicKeyValid := `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9KaakMv1XKKDaSch3PFR +3a27oaHp1GNTTNqvb1ZaHZXp+wuhYDwc/MTE67x9GCifvQBWzEGorgTq7aisiOyl +vKifwz6/wQ+62WHKG/sqKn2Xikp3P63aBIPlZcHbkyyRmL62yeyuzYoGvLEYel+m +z5SiKGBwviSY0Th2L4e5sGJuk2HOut6emxDi+E2Fuuj5zokFJvIT6Urlq8f3h6+l +GeR6HUOXqoYVf7ff126GP7dticTVBgibxkkuJFmpvQSW6xmxruT4k6iwjzbZHY7P +ypZ/TdlnuGC1cOpAVyU7k32IJ9CRbt3nwEf5U54LRXLLQjFixWZHwKdDiMTF4ws0 ++wIDAQAB +-----END PUBLIC KEY-----` + + publicKeyInvalid := `-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMYfnvWtC8Id5bPKae5yXSxQTt ++Zpul6AnnZWfI2TtIarvjHBFUtXRo96y7hoL4VWOPKGCsRqMFDkrbeUjRrx8iL91 +4/srnyf6sh9c8Zk04xEOpK1ypvBz+Ks4uZObtjnnitf0NBGdjMKxveTq+VE7BWUI +yQjtQ8mbDOsiLLvh7wIDAQAB +-----END PUBLIC KEY-----` + + publicKeyBad := `-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMYfnvWtC8Id5bPKae5yXSxQTt ++Zpul6AnnZWfI2TtIarvjHBFUtXRo96y7hoL4VWOPKGCsRqMFDkrbeUjRrx8iL91 +4/srnyf6sh9c8Zk04xEOpK1ypvBz+Ks4uZObtjnnitf0NBGdjMKxveTq+VE7BWUI +yQjtQ8mbDOsiLLvh7wIDAQAB== +-----END PUBLIC KEY-----` + + // Private key corresponding to the valid public key, for + // re-generating the test signatures. + /* privateKey := `-----BEGIN RSA PRIVATE KEY----- + MIIEpQIBAAKCAQEA9KaakMv1XKKDaSch3PFR3a27oaHp1GNTTNqvb1ZaHZXp+wuh + YDwc/MTE67x9GCifvQBWzEGorgTq7aisiOylvKifwz6/wQ+62WHKG/sqKn2Xikp3 + P63aBIPlZcHbkyyRmL62yeyuzYoGvLEYel+mz5SiKGBwviSY0Th2L4e5sGJuk2HO + ut6emxDi+E2Fuuj5zokFJvIT6Urlq8f3h6+lGeR6HUOXqoYVf7ff126GP7dticTV + BgibxkkuJFmpvQSW6xmxruT4k6iwjzbZHY7PypZ/TdlnuGC1cOpAVyU7k32IJ9CR + bt3nwEf5U54LRXLLQjFixWZHwKdDiMTF4ws0+wIDAQABAoIBAQDDL0BVkUNZ+pYZ + CI1ttmH4GCmAFKt3NR86S6Z3j08qF3arQWYoXw1JZLsu0ByFb7OxmFmncCLhYy8D + GPU98H9x+p4rqR5XKvOJhwk2NbY4XCbQwARPm6Y6v/f+rSE/U+l9EXrHsrrrZNln + JWtABpwRNKYCzJ5mNNBu6zrvRLuSygXG/U5wMnO7bLAJh7u3SUPd6Mkg+RTEu4jG + wHuhcnbsbBdpLRHZKeGlfVL62sUXE1mrf09406U2i7us9BhvYC6pEgbVi7PZ0FBs + Lx0W1PB0GdKS0hq6yKQhskBFI/CsGHoIjIsG7AiUyGwxLgeVHyOfUTZGaVRVZ99j + +nz6rOhRAoGBAP6nW/oDaaVZM0JiO09KdbEmtUeNqKPGM1rNtakbiBz5JZ6M636L + 2Q///7jvyIrI6WNRTQh2ByqKpxwHjd0+mJa1CiOrTVE44mcxaXoSdQWtXJhZt24O + kOwVWgy5P/nyIVq+l+vYFJvMq387c9h/l2cFaSa5fIiTbTFq3ue8Mmp1AoGBAPXx + tPCHeGa8WwKvMwVB4hZ5XogTRLqtTIftqhrJvSp/2md60Xz7A35HuOTV22PFuoUt + pIaJggPVHOd7tsSYEx2wjJpTNf+EWhIdp7kziHIqGgjj/d+NftRDvx3e/mJip4TI + XEN0BzxCplU/eFPL8bvi03fjnCOh2iG599d8vtOvAoGBAMlB1ZRDLDSMydE2N2+U + Bm3qjKyvTU+aLi4ek+rBopJbahrjfp61weg+R4mOoGznGmTu9TWxqjo5+JZTdhAc + D5ZUIF5OXT3K+kvaJmVevvOsrpiNl0W451peCZwysFhGv4urRAAV9zumxwc4IndB + Z5P5F8COKdj6wvqiXubAuwudAoGAD6gPaLB3DbM35/fXO6JyDhQz3F29plSZ5p1O + kt382NPCx4ueAmLIWiWes5KZoMRZl1jMfHQMfsn2SRYrEGDN9rnieYCKk3WNdlHE + 95k8OmhLt/0rkCulw0V8yR4E+6ZkG6PVm8WrID7t78dWlZ8KCHfsFlm6+tm21SbN + jD44t6kCgYEAnTHH8SOMoW/kMckESubV2y4rvdLol7nVX3ia7IGP8TA6NHsGT0Qb + EOuKwnuQ3ZToMdNVrS2nFyLe/HohSbT0SrNu2j4YkZDxHGQImZvw/KtMFPyT+Ff8 + rtvSvuiJNjLUr6OcqxARXrmispBO/GxvWucF0tcpOcSGUDYHAD2yIAQ= + -----END RSA PRIVATE KEY-----`*/ + + tests := map[string]struct { + token string + keys map[string]*KeyConfig + keyID string + scope string + wantErr bool + err error + }{ + "no_public_key_id": {signedNoKeyIDTokenHS256, map[string]*KeyConfig{}, "", "", true, errors.New("verification key ID is empty")}, + "actual_public_key_missing": {signedTokenHS256, map[string]*KeyConfig{}, "", "", true, errors.New("verification key corresponding to ID foo not found")}, + "deprecated_key_id_claim": { + signedTokenWithDeprecatedKidClaimHS256, + map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "", "write", // check valid keyId in deprecated claim is used + false, nil, + }, + "bad_public_key_algorithm": { + signedTokenHS256, + map[string]*KeyConfig{"foo": {Key: "somekey", Algorithm: "RS007"}}, "", "", + true, errors.New("unsupported signature algorithm: RS007"), + }, + "public_key_with_valid_HS256_sign": { + signedTokenWithBarKidHS256, + map[string]*KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "foo", "write", // check keyId in OPA config takes precedence + false, nil, + }, + "public_key_with_invalid_HS256_sign": { + signedTokenHS256, + map[string]*KeyConfig{"foo": {Key: "bad_secret", Algorithm: "HS256"}}, "", "", + true, errors.New("failed to verify message: failed to match hmac signature"), + }, + "public_key_with_valid_RS256_sign": { + signedTokenRS256, + map[string]*KeyConfig{"foo": {Key: publicKeyValid, Algorithm: "RS256"}}, "", "write", + false, nil, + }, + "public_key_with_invalid_RS256_sign": { + signedTokenRS256, + map[string]*KeyConfig{"foo": {Key: publicKeyInvalid, Algorithm: "RS256"}}, "", "", + true, errors.New("failed to verify message: crypto/rsa: verification error"), + }, + "public_key_with_bad_cert_RS256": { + signedTokenRS256, + map[string]*KeyConfig{"foo": {Key: publicKeyBad, Algorithm: "RS256"}}, "foo", "", + true, errors.New("failed to parse PEM block containing the key"), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + _, err := verifyJWTSignature(tc.token, NewVerificationConfig(tc.keys, tc.keyID, tc.scope, nil)) + + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + } + + // public key id provided in OPA config, actual public key signed using RS256. Valid signature + keys := map[string]*KeyConfig{} + keys["foo"] = &KeyConfig{ + Key: publicKeyValid, + Algorithm: "RS256", + } + + _, err := verifyJWTSignature(signedTokenRS256, NewVerificationConfig(keys, "foo", "write", nil)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +func TestVerifyBundleFile(t *testing.T) { + + tests := map[string]struct { + files [][2]string + readerFiles map[string]FileInfo + wantErr bool + err error + }{ + "file_not_found": { + [][2]string{{"/.manifest", `{"revision": "quickbrownfaux"}`}}, + map[string]FileInfo{}, + true, errors.New("file /.manifest not included in bundle signature"), + }, + "bad_hashing_algorithm": { + [][2]string{{"/.manifest", `{"revision": "quickbrownfaux"}`}}, + map[string]FileInfo{"/.manifest": { + Name: "/.manifest", + Hash: "e7dc95e14ad6cd75d044c13d52ee3ab1", + Algorithm: "MD6", + }}, + true, errors.New("unsupported hashing algorithm: MD6"), + }, + "bad_digest": { + [][2]string{{"/.manifest", `{"revision": "quickbrownfaux"}`}}, + map[string]FileInfo{"/.manifest": { + Name: "/.manifest", + Hash: "874984d68515ba2439c04dddf5b21574", + Algorithm: MD5.String(), + }}, + true, errors.New("/.manifest: digest mismatch (want: 874984d68515ba2439c04dddf5b21574, got: a005c38a509dc2d5a7407b9494efb2ad)"), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + data := bytes.NewBufferString(tc.files[0][1]) + err := VerifyBundleFile(tc.files[0][0], *data, tc.readerFiles) + + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + } +} + +type CustomVerifier struct{} + +func (*CustomVerifier) VerifyBundleSignature(_ SignaturesConfig, _ *VerificationConfig) (map[string]FileInfo, error) { + return map[string]FileInfo{}, nil +} + +func TestCustomVerifier(t *testing.T) { + custom := &CustomVerifier{} + err := RegisterVerifier(defaultVerifierID, custom) + if err == nil { + t.Fatalf("Expected error when registering with default ID") + } + if err := RegisterVerifier("_test", custom); err != nil { + t.Fatal(err) + } + defaultVerifier, err := GetVerifier(defaultVerifierID) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if _, isDefault := defaultVerifier.(*DefaultVerifier); !isDefault { + t.Fatalf("Expected DefaultVerifier to be registered at key %s", defaultVerifierID) + } + customVerifier, err := GetVerifier("_test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if _, isCustom := customVerifier.(*CustomVerifier); !isCustom { + t.Fatalf("Expected CustomVerifier to be registered at key _test") + } + if _, err = GetVerifier("_unregistered"); err == nil { + t.Fatalf("Expected error when no Verifier exists at provided key") + } +} diff --git a/third_party/opa/v1/capabilities/capabilities.go b/third_party/opa/v1/capabilities/capabilities.go new file mode 100644 index 000000000000..5b0bb1ea523d --- /dev/null +++ b/third_party/opa/v1/capabilities/capabilities.go @@ -0,0 +1,18 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build go1.16 +// +build go1.16 + +package capabilities + +import ( + v0 "github.com/open-policy-agent/opa/capabilities" +) + +// FS contains the embedded capabilities/ directory of the built version, +// which has all the capabilities of previous versions: +// "v0.18.0.json" contains the capabilities JSON of version v0.18.0, etc + +var FS = v0.FS diff --git a/third_party/opa/v1/capabilities/capabilities_test.go b/third_party/opa/v1/capabilities/capabilities_test.go new file mode 100644 index 000000000000..789a4be961a3 --- /dev/null +++ b/third_party/opa/v1/capabilities/capabilities_test.go @@ -0,0 +1,36 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build go1.16 +// +build go1.16 + +package capabilities_test + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/capabilities" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestCapabilitiesEmbedded(t *testing.T) { + ents, err := capabilities.FS.ReadDir(".") + if err != nil { + t.Fatal(err) + } + if len(ents) == 0 { + t.Error("expected capabilities to be present") + } + for _, ent := range ents { + cont, err := capabilities.FS.ReadFile(ent.Name()) + if err != nil { + t.Errorf("file %v: %v", ent.Name(), err) + } + var x any + err = util.UnmarshalJSON(cont, &x) + if err != nil { + t.Errorf("file %v: %v", ent.Name(), err) + } + } +} diff --git a/third_party/opa/v1/compile/compile.go b/third_party/opa/v1/compile/compile.go new file mode 100644 index 000000000000..2c832253d2ed --- /dev/null +++ b/third_party/opa/v1/compile/compile.go @@ -0,0 +1,1367 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package compile implements bundles compilation and linking. +package compile + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/fs" + "path/filepath" + "regexp" + "slices" + "sort" + "strings" + + "github.com/open-policy-agent/opa/internal/compiler/wasm" + "github.com/open-policy-agent/opa/internal/debug" + "github.com/open-policy-agent/opa/internal/planner" + "github.com/open-policy-agent/opa/internal/ref" + initload "github.com/open-policy-agent/opa/internal/runtime/init" + "github.com/open-policy-agent/opa/internal/wasm/encoding" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/ir" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" +) + +const ( + // TargetRego is the default target. The source rego is copied (potentially + // rewritten for optimization purpsoes) into the bundle. The target supports + // base documents. + TargetRego = "rego" + + // TargetWasm is an alternative target that compiles the policy into a wasm + // module instead of Rego. The target supports base documents. + TargetWasm = "wasm" + + // TargetPlan is an altertive target that compiles the policy into an + // imperative query plan that can be further transpiled or interpreted. + TargetPlan = "plan" +) + +// Targets contains the list of targets supported by the compiler. +var Targets = []string{ + TargetRego, + TargetWasm, + TargetPlan, +} + +const resultVar = ast.Var("result") + +// Compiler implements bundle compilation and linking. +type Compiler struct { + capabilities *ast.Capabilities // the capabilities that compiled policies may require + bundle *bundle.Bundle // the bundle that the compiler operates on + revision *string // the revision to set on the output bundle + asBundle bool // whether to assume bundle layout on file loading or not + pruneUnused bool // whether to extend the entrypoint set for semantic equivalence of built bundles + filter loader.Filter // filter to apply to file loader + paths []string // file paths to load. TODO(tsandall): add support for supplying readers for embedded users. + entrypoints orderedStringSet // policy entrypoints required for optimization and certain targets + roots []string // optionally, bundle roots can be provided + useRegoAnnotationEntrypoints bool // allow compiler to late-bind entrypoints from annotated rules in policies. + optimizationLevel int // how aggressive should optimization be + target string // target type (wasm, rego, etc.) + output *io.Writer // output stream to write bundle to + entrypointrefs []*ast.Term // validated entrypoints computed from default decision or manually supplied entrypoints + compiler *ast.Compiler // rego ast compiler used for semantic checks and rewriting + policy *ir.Policy // planner output when wasm or plan targets are enabled + debug debug.Debug // optionally outputs debug information produced during build + enablePrintStatements bool // optionally enable rego print statements + bvc *bundle.VerificationConfig // represents the key configuration used to verify a signed bundle + bsc *bundle.SigningConfig // represents the key configuration used to generate a signed bundle + keyID string // represents the name of the default key used to verify a signed bundle + enableBundleLazyLoadingMode bool // bundle lazy loading mode + metadata *map[string]any // represents additional data included in .manifest file + fsys fs.FS // file system to use when loading paths + ns string + regoVersion ast.RegoVersion + followSymlinks bool // optionally follow symlinks in the bundle directory when building the bundle +} + +// New returns a new compiler instance that can be invoked. +func New() *Compiler { + return &Compiler{ + asBundle: false, + optimizationLevel: 0, + target: TargetRego, + debug: debug.Discard(), + regoVersion: ast.DefaultRegoVersion, + } +} + +// WithRevision sets the revision to include in the output bundle manifest. +func (c *Compiler) WithRevision(r string) *Compiler { + c.revision = &r + return c +} + +// WithAsBundle sets file loading mode on the compiler. +func (c *Compiler) WithAsBundle(enabled bool) *Compiler { + c.asBundle = enabled + return c +} + +// WithPruneUnused will make rules be ignored that are defined on the same +// package as the entrypoint, but that are not in the entrypoint set. +// +// Notably this includes functions (they can't be entrypoints) and causes +// the built bundle to no longer be semantically equivalent to the bundle built +// without wasm. +// +// This affects the 'wasm' and 'plan' targets only. It has no effect on +// building 'rego' bundles, i.e., "ordinary bundles". +func (c *Compiler) WithPruneUnused(enabled bool) *Compiler { + c.pruneUnused = enabled + return c +} + +// WithEntrypoints sets the policy entrypoints on the compiler. Entrypoints tell the +// compiler what rules to expect and where optimizations can be targeted. The wasm +// target requires at least one entrypoint as does optimization. +func (c *Compiler) WithEntrypoints(e ...string) *Compiler { + c.entrypoints = c.entrypoints.Append(e...) + return c +} + +// WithRegoAnnotationEntrypoints allows the compiler to late-bind entrypoints, based +// on Rego entrypoint annotations. The rules tagged with entrypoint annotations are +// added to the global list of entrypoints before optimizations/target compilation. +func (c *Compiler) WithRegoAnnotationEntrypoints(enabled bool) *Compiler { + c.useRegoAnnotationEntrypoints = enabled + return c +} + +// WithOptimizationLevel sets the optimization level on the compiler. By default +// optimizations are disabled. Higher levels apply more aggressive optimizations +// but can take longer. +func (c *Compiler) WithOptimizationLevel(n int) *Compiler { + c.optimizationLevel = n + return c +} + +// WithTarget sets the output target type to use. +func (c *Compiler) WithTarget(t string) *Compiler { + c.target = t + return c +} + +// WithOutput sets the output stream to write the bundle to. +func (c *Compiler) WithOutput(w io.Writer) *Compiler { + c.output = &w + return c +} + +// WithDebug sets the output stream to write debug info to. +func (c *Compiler) WithDebug(sink io.Writer) *Compiler { + if sink != nil { + c.debug = debug.New(sink) + } + return c +} + +// WithEnablePrintStatements enables print statements inside of modules compiled +// by the compiler. If print statements are not enabled, calls to print() are +// erased at compile-time. +func (c *Compiler) WithEnablePrintStatements(yes bool) *Compiler { + c.enablePrintStatements = yes + return c +} + +// WithPaths adds input filepaths to read policy and data from. +func (c *Compiler) WithPaths(p ...string) *Compiler { + c.paths = append(c.paths, p...) + return c +} + +// WithFilter sets the loader filter to use when reading non-bundle input files. +func (c *Compiler) WithFilter(filter loader.Filter) *Compiler { + c.filter = filter + return c +} + +// WithBundle sets the input bundle to compile. This should be used as an +// alternative to reading from paths. This function overrides any file +// loading options. +func (c *Compiler) WithBundle(b *bundle.Bundle) *Compiler { + c.bundle = b + return c +} + +// WithBundleVerificationConfig sets the key configuration to use to verify a signed bundle +func (c *Compiler) WithBundleVerificationConfig(config *bundle.VerificationConfig) *Compiler { + c.bvc = config + return c +} + +// WithBundleSigningConfig sets the key configuration to use to generate a signed bundle +func (c *Compiler) WithBundleSigningConfig(config *bundle.SigningConfig) *Compiler { + c.bsc = config + return c +} + +// WithBundleVerificationKeyID sets the key to use to verify a signed bundle. +// If provided, the "keyid" claim in the bundle signature, will be set to this value +func (c *Compiler) WithBundleVerificationKeyID(keyID string) *Compiler { + c.keyID = keyID + return c +} + +// WithBundleLazyLoadingMode sets the additional data to be included in .manifest +func (c *Compiler) WithBundleLazyLoadingMode(mode bool) *Compiler { + c.enableBundleLazyLoadingMode = mode + return c +} + +// WithCapabilities sets the capabilities to use while checking policies. +func (c *Compiler) WithCapabilities(capabilities *ast.Capabilities) *Compiler { + c.capabilities = capabilities + return c +} + +// WithFollowSymlinks sets whether or not to follow symlinks in the bundle directory when building the bundle +func (c *Compiler) WithFollowSymlinks(yes bool) *Compiler { + c.followSymlinks = yes + return c +} + +// WithMetadata sets the additional data to be included in .manifest +func (c *Compiler) WithMetadata(metadata *map[string]any) *Compiler { + c.metadata = metadata + return c +} + +// WithRoots sets the roots to include in the output bundle manifest. +func (c *Compiler) WithRoots(r ...string) *Compiler { + c.roots = append(c.roots, r...) + return c +} + +// WithFS sets the file system to use when loading paths +func (c *Compiler) WithFS(fsys fs.FS) *Compiler { + c.fsys = fsys + return c +} + +// WithPartialNamespace sets the namespace to use for partial evaluation results +func (c *Compiler) WithPartialNamespace(ns string) *Compiler { + c.ns = ns + return c +} + +func (c *Compiler) WithRegoVersion(v ast.RegoVersion) *Compiler { + c.regoVersion = v + return c +} + +func addEntrypointsFromAnnotations(c *Compiler, arefs []*ast.AnnotationsRef) error { + for _, aref := range arefs { + var entrypoint ast.Ref + scope := aref.Annotations.Scope + + if aref.Annotations.Entrypoint { + // Build up the entrypoint path from either package path or rule. + switch scope { + case "package": + if p := aref.GetPackage(); p != nil { + entrypoint = p.Path + } + case "document": + if r := aref.GetRule(); r != nil { + entrypoint = r.Ref().GroundPrefix() + } + default: + continue // Wrong scope type. Bail out early. + } + + // Get a slash-based path, as with a CLI-provided entrypoint. + escapedPath, err := storage.NewPathForRef(entrypoint) + if err != nil { + return err + } + slashPath := strings.Join(escapedPath, "/") + + // Add new entrypoints to the appropriate places. + c.entrypoints = c.entrypoints.Append(slashPath) + c.entrypointrefs = append(c.entrypointrefs, ast.NewTerm(entrypoint)) + } + } + + return nil +} + +// Build compiles and links the input files and outputs a bundle to the writer. +func (c *Compiler) Build(ctx context.Context) error { + + if c.regoVersion == ast.RegoUndefined { + return errors.New("rego-version not set") + } + + if err := c.init(); err != nil { + return err + } + + // Fail early if not using Rego annotation entrypoints. + if !c.useRegoAnnotationEntrypoints { + if err := c.checkNumEntrypoints(); err != nil { + return err + } + } + + if err := c.initBundle(false); err != nil { + return err + } + + // Extract annotations, and generate new entrypoints as needed. + if c.useRegoAnnotationEntrypoints { + moduleList := make([]*ast.Module, 0, len(c.bundle.Modules)) + for _, modfile := range c.bundle.Modules { + moduleList = append(moduleList, modfile.Parsed) + } + as, errs := ast.BuildAnnotationSet(moduleList) + if len(errs) > 0 { + return errs + } + ar := as.Flatten() + + // Patch in entrypoints from Rego annotations. + err := addEntrypointsFromAnnotations(c, ar) + if err != nil { + return err + } + } + + // Ensure we have at least one valid entrypoint, or fail before compilation. + if err := c.checkNumEntrypoints(); err != nil { + return err + } + + // Dedup entrypoint refs, if both CLI and entrypoint metadata annotations + // were used. + if err := c.dedupEntrypointRefs(); err != nil { + return err + } + + if err := c.optimize(ctx); err != nil { + return err + } + + switch c.target { + case TargetWasm: + if err := c.compileWasm(ctx); err != nil { + return err + } + case TargetPlan: + if err := c.compilePlan(ctx); err != nil { + return err + } + + bs, err := json.Marshal(c.policy) + if err != nil { + return err + } + + c.bundle.PlanModules = append(c.bundle.PlanModules, bundle.PlanModuleFile{ + Path: bundle.PlanFile, + URL: bundle.PlanFile, + Raw: bs, + }) + case TargetRego: + // nop + } + + if c.revision != nil { + c.bundle.Manifest.Revision = *c.revision + } + + if c.metadata != nil { + c.bundle.Manifest.Metadata = *c.metadata + } + + if err := c.bundle.FormatModulesWithOptions(bundle.BundleFormatOptions{ + RegoVersion: c.regoVersion, + Capabilities: c.capabilities, + PreserveModuleRegoVersion: true, + }); err != nil { + return err + } + + if c.bsc != nil { + if err := c.bundle.GenerateSignature(c.bsc, c.keyID, false); err != nil { + return err + } + } + + if c.output == nil { + return nil + } + + return bundle.NewWriter(*c.output).Write(*c.bundle) +} + +func (c *Compiler) init() error { + + if c.capabilities == nil { + c.capabilities = ast.CapabilitiesForThisVersion() + } + + var found bool + if slices.Contains(Targets, c.target) { + found = true + } + + if !found { + return fmt.Errorf("invalid target %q", c.target) + } + + for _, e := range c.entrypoints { + r, err := ref.ParseDataPath(e) + if err != nil { + return fmt.Errorf("entrypoint %v not valid: use /", e) + } + + c.entrypointrefs = append(c.entrypointrefs, ast.NewTerm(r)) + } + + return nil +} + +// Once the bundle has been loaded, we can check the entrypoint counts. +func (c *Compiler) checkNumEntrypoints() error { + if c.optimizationLevel > 0 && len(c.entrypointrefs) == 0 { + return errors.New("bundle optimizations require at least one entrypoint") + } + + // Rego target does not require an entrypoint. Others currently do. + if c.target != TargetRego && len(c.entrypointrefs) == 0 { + return fmt.Errorf("%s compilation requires at least one entrypoint", c.target) + } + + return nil +} + +// Note(philipc): When an entrypoint is provided on the CLI and from an +// entrypoint annotation, it can lead to duplicates in the slice of +// entrypoint refs. This can cause panics down the line due to c.entrypoints +// being a different length than c.entrypointrefs. As a result, we have to +// trim out the duplicates. +func (c *Compiler) dedupEntrypointRefs() error { + // Build list of entrypoint refs, without duplicates. + newEntrypointRefs := make([]*ast.Term, 0, len(c.entrypointrefs)) + entrypointRefSet := make(map[string]struct{}, len(c.entrypointrefs)) + for i, r := range c.entrypointrefs { + refString := r.String() + // Store only the first index in the list that matches. + if _, ok := entrypointRefSet[refString]; !ok { + entrypointRefSet[refString] = struct{}{} + newEntrypointRefs = append(newEntrypointRefs, c.entrypointrefs[i]) + } + } + c.entrypointrefs = newEntrypointRefs + return nil +} + +// Bundle returns the compiled bundle. This function can be called to retrieve the +// output of the compiler (as an alternative to having the bundle written to a stream.) +func (c *Compiler) Bundle() *bundle.Bundle { + return c.bundle +} + +func (c *Compiler) initBundle(usePath bool) error { + // If the bundle is already set, skip file loading. + if c.bundle != nil { + return nil + } + + // TODO(tsandall): the metrics object should passed through here so we that + // we can track read and parse times. + + load, err := initload.LoadPathsForRegoVersion( + c.regoVersion, + c.paths, + c.filter, + c.asBundle, + c.bvc, + false, + c.enableBundleLazyLoadingMode, + c.useRegoAnnotationEntrypoints, + c.followSymlinks, + c.capabilities, + c.fsys) + if err != nil { + return fmt.Errorf("load error: %w", err) + } + + if c.asBundle { + var names []string + + for k := range load.Bundles { + names = append(names, k) + } + + sort.Strings(names) + var bundles []*bundle.Bundle + + for _, k := range names { + bundles = append(bundles, load.Bundles[k]) + } + + result, err := bundle.MergeWithRegoVersion(bundles, c.regoVersion, usePath) + if err != nil { + return fmt.Errorf("bundle merge failed: %v", err) + } + + c.bundle = result + return nil + } + + // TODO(tsandall): roots could be automatically inferred based on the packages and data + // contents. That would require changes to the loader to preserve the + // locations where base documents were mounted under data. + result := &bundle.Bundle{} + result.SetRegoVersion(c.regoVersion) + if len(c.roots) > 0 { + result.Manifest.Roots = &c.roots + } + + result.Manifest.Init() + result.Data = load.Files.Documents + + modules := make([]string, 0, len(load.Files.Modules)) + for k := range load.Files.Modules { + modules = append(modules, k) + } + + sort.Strings(modules) + + for _, module := range modules { + path := filepath.ToSlash(load.Files.Modules[module].Name) + result.Modules = append(result.Modules, bundle.ModuleFile{ + URL: path, + Path: path, + Parsed: load.Files.Modules[module].Parsed, + Raw: load.Files.Modules[module].Raw, + }) + } + + c.bundle = result + + return nil +} + +func (c *Compiler) optimize(ctx context.Context) error { + if c.optimizationLevel <= 0 { + var err error + c.compiler, err = compile(c.capabilities, c.bundle, c.debug, c.enablePrintStatements) + return err + } + + o := newOptimizer(c.capabilities, c.bundle). + WithEntrypoints(c.entrypointrefs). + WithDebug(c.debug.Writer()). + WithShallowInlining(c.optimizationLevel <= 1). + WithEnablePrintStatements(c.enablePrintStatements). + WithRegoVersion(c.regoVersion) + + if c.ns != "" { + o = o.WithPartialNamespace(c.ns) + } + + err := o.Do(ctx) + if err != nil { + return err + } + + c.bundle = o.Bundle() + + return nil +} + +func (c *Compiler) compilePlan(context.Context) error { + + // Lazily compile the modules if needed. If optimizations were run, the + // AST compiler will not be set because the default target does not require it. + if c.compiler == nil { + var err error + c.compiler, err = compile(c.capabilities, c.bundle, c.debug, c.enablePrintStatements) + if err != nil { + return err + } + } + + if !c.pruneUnused { + // Find transitive dependents of entrypoints and add them to the set to compile. + // + // NOTE(tsandall): We compile entrypoints because the evaluator does not support + // evaluation of wasm-compiled rules when 'with' statements are in-scope. Compiling + // out the dependents avoids the need to support that case for now. + deps := map[*ast.Rule]struct{}{} + for i := range c.entrypointrefs { + transitiveDocumentDependents(c.compiler, c.entrypointrefs[i], deps) + } + + extras := ast.NewSet() + for rule := range deps { + extras.Add(ast.NewTerm(rule.Path())) + } + + sorted := extras.Sorted() + + for i := range sorted.Len() { + p, err := sorted.Elem(i).Value.(ast.Ref).Ptr() + if err != nil { + return err + } + + if !c.entrypoints.Contains(p) { + c.entrypoints = append(c.entrypoints, p) + c.entrypointrefs = append(c.entrypointrefs, sorted.Elem(i)) + } + } + } + + // Create query sets for each of the entrypoints. + resultSym := ast.NewTerm(resultVar) + queries := make([]planner.QuerySet, len(c.entrypointrefs)) + var unmappedEntrypoints []string + + for i := range c.entrypointrefs { + qc := c.compiler.QueryCompiler() + query := ast.NewBody(ast.Equality.Expr(resultSym, c.entrypointrefs[i])) + compiled, err := qc.Compile(query) + if err != nil { + return err + } + + if len(c.compiler.GetRules(c.entrypointrefs[i].Value.(ast.Ref))) == 0 { + unmappedEntrypoints = append(unmappedEntrypoints, c.entrypoints[i]) + } + + queries[i] = planner.QuerySet{ + Name: c.entrypoints[i], + Queries: []ast.Body{compiled}, + RewrittenVars: qc.RewrittenVars(), + } + } + + if len(unmappedEntrypoints) > 0 { + return fmt.Errorf("entrypoint %q does not refer to a rule or policy decision", unmappedEntrypoints[0]) + } + + // Prepare modules and builtins for the planner. + modules := make([]*ast.Module, 0, len(c.compiler.Modules)) + for _, module := range c.compiler.Modules { + modules = append(modules, module) + } + + builtins := make(map[string]*ast.Builtin, len(c.capabilities.Builtins)) + for _, bi := range c.capabilities.Builtins { + builtins[bi.Name] = bi + } + + // Plan the query sets. + p := planner.New(). + WithQueries(queries). + WithModules(modules). + WithBuiltinDecls(builtins). + WithDebug(c.debug.Writer()) + policy, err := p.Plan() + if err != nil { + return err + } + + // dump policy IR (if "debug" wasn't requested, debug.Writer will discard it) + err = ir.Pretty(c.debug.Writer(), policy) + if err != nil { + return err + } + + c.policy = policy + + return nil +} + +func (c *Compiler) compileWasm(ctx context.Context) error { + + compiler := wasm.New() + + found := false + have := compiler.ABIVersion() + if c.capabilities.WasmABIVersions == nil { // discern nil from len=0 + c.debug.Printf("no wasm ABI versions in capabilities, building for %v", have) + found = true + } + for _, v := range c.capabilities.WasmABIVersions { + if v.Version == have.Version && v.Minor <= have.Minor { + found = true + break + } + } + if !found { + return fmt.Errorf("compiler ABI version not in capabilities (have %v, want %d)", + c.capabilities.WasmABIVersions, + compiler.ABIVersion(), + ) + } + + if err := c.compilePlan(ctx); err != nil { + return err + } + + // Compile the policy into a wasm binary. + m, err := compiler.WithPolicy(c.policy).WithDebug(c.debug.Writer()).Compile() + if err != nil { + return err + } + + var buf bytes.Buffer + if err := encoding.WriteModule(&buf, m); err != nil { + return err + } + + modulePath := bundle.WasmFile + + c.bundle.WasmModules = []bundle.WasmModuleFile{{ + URL: modulePath, + Path: modulePath, + Raw: buf.Bytes(), + }} + + flattenedAnnotations := c.compiler.GetAnnotationSet().Flatten() + + // Each entrypoint needs an entry in the manifest + for i, e := range c.entrypointrefs { + entrypointPath := c.entrypoints[i] + + var annotations []*ast.Annotations + if !c.isPackage(e) { + annotations = findAnnotationsForTerm(e, flattenedAnnotations) + } + + c.bundle.Manifest.WasmResolvers = append(c.bundle.Manifest.WasmResolvers, bundle.WasmResolver{ + Module: "/" + strings.TrimLeft(modulePath, "/"), + Entrypoint: entrypointPath, + Annotations: annotations, + }) + } + + // Remove the entrypoints from remaining source rego files + return pruneBundleEntrypoints(c.bundle, c.entrypointrefs) +} + +func (c *Compiler) isPackage(term *ast.Term) bool { + for _, m := range c.compiler.Modules { + if m.Package.Path.Equal(term.Value) { + return true + } + } + return false +} + +// findAnnotationsForTerm returns a slice of all annotations directly associated with the given term. +func findAnnotationsForTerm(term *ast.Term, annotationRefs []*ast.AnnotationsRef) []*ast.Annotations { + r, ok := term.Value.(ast.Ref) + if !ok { + return nil + } + + var result []*ast.Annotations + + for _, ar := range annotationRefs { + if r.Equal(ar.Path) { + result = append(result, ar.Annotations) + } + } + + return result +} + +// pruneBundleEntrypoints will modify modules in the provided bundle to remove +// rules matching the entrypoints along with injecting import statements to +// preserve their ability to compile. +func pruneBundleEntrypoints(b *bundle.Bundle, entrypointrefs []*ast.Term) error { + + // For each package path keep a list of new imports to add. + requiredImports := map[string][]*ast.Import{} + + for _, entrypoint := range entrypointrefs { + for i := range len(b.Modules) { + mf := &b.Modules[i] + + // Drop any rules that match the entrypoint path. + var rules []*ast.Rule + for _, rule := range mf.Parsed.Rules { + rulePath := rule.Path() + if !rulePath.Equal(entrypoint.Value) { + rules = append(rules, rule) + } else { + pkgPath := rule.Module.Package.Path.String() + newImport := &ast.Import{Path: ast.NewTerm(rulePath)} + shouldAdd := true + currentImports := requiredImports[pkgPath] + for _, imp := range currentImports { + if imp.Equal(newImport) { + shouldAdd = false + break + } + } + if shouldAdd { + requiredImports[pkgPath] = append(currentImports, newImport) + } + } + } + + // Drop any Annotations for rules matching the entrypoint path + var annotations []*ast.Annotations + var prunedAnnotations []*ast.Annotations + for _, annotation := range mf.Parsed.Annotations { + p := annotation.GetTargetPath() + // We prune annotations of dropped rules, but not packages, as the Rego file is always retained + if p.Equal(entrypoint.Value) && !mf.Parsed.Package.Path.Equal(entrypoint.Value) { + prunedAnnotations = append(prunedAnnotations, annotation) + } else { + annotations = append(annotations, annotation) + } + } + + // Drop comments associated with pruned annotations + var comments []*ast.Comment + for _, comment := range mf.Parsed.Comments { + pruned := false + for _, annotation := range prunedAnnotations { + if comment.Location.Row >= annotation.Location.Row && + comment.Location.Row <= annotation.EndLoc().Row { + pruned = true + break + } + } + + if !pruned { + comments = append(comments, comment) + } + } + + // If any rules or annotations were dropped update the module accordingly + if len(rules) != len(mf.Parsed.Rules) || len(comments) != len(mf.Parsed.Comments) { + mf.Parsed.Rules = rules + mf.Parsed.Annotations = annotations + mf.Parsed.Comments = comments + // Remove the original raw source, we're editing the AST + // directly, so it won't be in sync anymore. + mf.Raw = nil + } + } + } + + // Any packages which had rules removed need an import injected for the + // removed rule to keep the policies valid. + for i := range len(b.Modules) { + mf := &b.Modules[i] + pkgPath := mf.Parsed.Package.Path.String() + if imports, ok := requiredImports[pkgPath]; ok { + mf.Raw = nil + mf.Parsed.Imports = append(mf.Parsed.Imports, imports...) + } + } + + return nil +} + +type invalidEntrypointErr struct { + Entrypoint *ast.Term + Msg string +} + +func (err invalidEntrypointErr) Error() string { + return fmt.Sprintf("invalid entrypoint %v: %s", err.Entrypoint, err.Msg) +} + +type undefinedEntrypointErr struct { + Entrypoint *ast.Term +} + +func (err undefinedEntrypointErr) Error() string { + return fmt.Sprintf("undefined entrypoint %v", err.Entrypoint) +} + +type optimizer struct { + capabilities *ast.Capabilities + bundle *bundle.Bundle + compiler *ast.Compiler + entrypoints []*ast.Term + nsprefix string + resultsymprefix string + outputprefix string + shallow bool + debug debug.Debug + enablePrintStatements bool + regoVersion ast.RegoVersion +} + +func newOptimizer(c *ast.Capabilities, b *bundle.Bundle) *optimizer { + return &optimizer{ + capabilities: c, + bundle: b, + nsprefix: "partial", + resultsymprefix: ast.WildcardPrefix, + outputprefix: "optimized", + debug: debug.Discard(), + } +} + +func (o *optimizer) WithDebug(sink io.Writer) *optimizer { + if sink != nil { + o.debug = debug.New(sink) + } + return o +} + +func (o *optimizer) WithEnablePrintStatements(yes bool) *optimizer { + o.enablePrintStatements = yes + return o +} + +func (o *optimizer) WithEntrypoints(es []*ast.Term) *optimizer { + o.entrypoints = es + return o +} + +func (o *optimizer) WithShallowInlining(yes bool) *optimizer { + o.shallow = yes + return o +} + +func (o *optimizer) WithPartialNamespace(ns string) *optimizer { + o.nsprefix = ns + return o +} + +func (o *optimizer) WithRegoVersion(regoVersion ast.RegoVersion) *optimizer { + o.regoVersion = regoVersion + return o +} + +func (o *optimizer) Do(ctx context.Context) error { + + // NOTE(tsandall): if there are multiple entrypoints, copy the bundle because + // if any of the optimization steps fail, we do not want to leave the caller's + // bundle in a partially modified state. + if len(o.entrypoints) > 1 { + cpy := o.bundle.Copy() + o.bundle = &cpy + } + + // initialize other inputs to the optimization process (store, symbols, etc.) + data := o.bundle.Data + if data == nil { + data = map[string]any{} + } + + store := inmem.NewFromObjectWithOpts(data, inmem.OptRoundTripOnWrite(false)) + resultsym := ast.VarTerm(o.resultsymprefix + "__result__") + usedFilenames := map[string]int{} + var unknowns []*ast.Term + + // NOTE(tsandall): the entrypoints are optimized in order so that the optimization + // of entrypoint[1] sees the optimization of entrypoint[0] and so on. This is needed + // because otherwise the optimization outputs (e.g., support rules) would have to + // merged somehow. Instead of dealing with that, just run the optimizations in the + // order the user supplied the entrypoints in. + // FIXME: entrypoint order is not user defined when declared as annotations. + for i, e := range o.entrypoints { + + if r := e.Value.(ast.Ref); len(r) <= 2 { + // To create a support module for the query, it must be possible to split the entrypoint ref into two parts; + // one for the package ref; and one for the rule name/ref. The package part must be two terms in size, as the first term + // is always the 'data' root. The rule name/ref must be at least one term in size. + return invalidEntrypointErr{ + Entrypoint: e, + Msg: "to create optimized support module, the entrypoint ref must have at least two components in addition to the 'data' root", + } + } + + var err error + o.compiler, err = compile(o.capabilities, o.bundle, o.debug, o.enablePrintStatements) + if err != nil { + return err + } + + if unknowns == nil { + unknowns = o.findUnknowns() + } + + required := o.findRequiredDocuments(e) + + r := rego.New( + rego.ParsedQuery(ast.NewBody(ast.Equality.Expr(resultsym, e))), + rego.PartialNamespace(o.nsprefix), + rego.DisableInlining(required), + rego.ShallowInlining(o.shallow), + rego.SkipPartialNamespace(true), + rego.ParsedUnknowns(unknowns), + rego.Compiler(o.compiler), + rego.Store(store), + rego.Capabilities(o.capabilities), + rego.SetRegoVersion(o.regoVersion), + ) + + o.debug.Printf("optimizer: entrypoint: %v", e) + o.debug.Printf(" partial-namespace: %v", o.nsprefix) + o.debug.Printf(" disable-inlining: %v", required) + o.debug.Printf(" shallow-inlining: %v", o.shallow) + + for i := range unknowns { + o.debug.Printf(" unknown: %v", unknowns[i]) + } + + pq, err := r.Partial(ctx) + if err != nil { + return err + } + + // NOTE(tsandall): this might be a bit too strict but in practice it's + // unlikely users will want to ignore undefined entrypoints. make this + // optional in the future. + if len(pq.Queries) == 0 { + return undefinedEntrypointErr{Entrypoint: e} + } + + if module := o.getSupportForEntrypoint(pq.Queries, e, resultsym); module != nil { + pq.Support = append(pq.Support, module) + } + + modules := make([]bundle.ModuleFile, len(pq.Support)) + + for j := range pq.Support { + fileName := o.getSupportModuleFilename(usedFilenames, pq.Support[j], i, j) + modules[j] = bundle.ModuleFile{ + URL: fileName, + Path: fileName, + Parsed: pq.Support[j], + } + } + + o.bundle.Modules = o.merge(o.bundle.Modules, modules) + } + + sort.Slice(o.bundle.Modules, func(i, j int) bool { + return o.bundle.Modules[i].URL < o.bundle.Modules[j].URL + }) + + // NOTE(tsandall): prune out rules and data that are not referenced in the bundle + // in the future. + o.bundle.Manifest.AddRoot(o.nsprefix) + o.bundle.Manifest.Revision = "" + + return nil +} + +func (o *optimizer) Bundle() *bundle.Bundle { + return o.bundle +} + +func (o *optimizer) findRequiredDocuments(ref *ast.Term) []string { + + keep := map[string]*ast.Location{} + deps := map[*ast.Rule]struct{}{} + + transitiveDocumentDependents(o.compiler, ref, deps) + + for rule := range deps { + ast.WalkExprs(rule, func(expr *ast.Expr) bool { + for _, with := range expr.With { + // TODO(tsandall): this should be improved to exclude refs that are + // marked as unknown. Since the build command does not allow users to + // set unknowns, we can hardcode to assume 'input'. + if !with.Target.Value.(ast.Ref).HasPrefix(ast.InputRootRef) { + keep[with.Target.String()] = with.Target.Location + } + } + return false + }) + } + + result := make([]string, 0, len(keep)) + + for k := range keep { + result = append(result, k) + } + + sort.Strings(result) + + for _, k := range result { + o.debug.Printf("%s: disables inlining of %v", keep[k], k) + } + + return result +} + +func (o *optimizer) findUnknowns() []*ast.Term { + + // Initialize set of refs representing the bundle roots. + refs := newRefSet(stringsToRefs(*o.bundle.Manifest.Roots)...) + + // Initialize set of refs for the result (i.e., refs outside the bundle roots.) + unknowns := newRefSet(ast.InputRootRef) + + // Find data references that are not prefixed by one of the roots. + for _, module := range o.compiler.Modules { + ast.WalkRefs(module, func(x ast.Ref) bool { + prefix := x.ConstantPrefix() + if !prefix.HasPrefix(ast.DefaultRootRef) { + return true + } + if !refs.ContainsPrefix(prefix) { + unknowns.AddPrefix(prefix) + } + return false + }) + } + + return unknowns.Sorted() +} + +func (o *optimizer) getSupportForEntrypoint(queries []ast.Body, entrypoint *ast.Term, resultsym *ast.Term) *ast.Module { + + path := entrypoint.Value.(ast.Ref) + name := ast.Var(path[len(path)-1].Value.(ast.String)) + module := &ast.Module{Package: &ast.Package{Path: path[:len(path)-1]}} + module.SetRegoVersion(o.regoVersion) + + for _, query := range queries { + // NOTE(tsandall): when the query refers to the original entrypoint, throw it + // away since this would create a recursive rule--this occurs if the entrypoint + // cannot be partially evaluated. + stop := false + ast.WalkRefs(query, func(x ast.Ref) bool { + if !stop { + if x.HasPrefix(path) { + stop = true + } + } + return stop + }) + if stop { + o.debug.Printf("optimizer: entrypoint: %v: discard due to self-reference", entrypoint) + return nil + } + module.Rules = append(module.Rules, &ast.Rule{ // TODO(sr): use RefHead instead? + Head: ast.NewHead(name, nil, resultsym), + Body: query, + Module: module, + }) + } + + return module +} + +// merge combines two sets of modules and returns the result. The rules from modules +// in 'b' override rules from modules in 'a'. If all rules in a module in 'a' are overridden +// by rules in modules in 'b' then the module from 'a' is discarded. +// NOTE(sr): This function assumes that `b` is the result of partial eval, and thus does NOT +// contain any rules that genuinely need their ref heads. +func (*optimizer) merge(a, b []bundle.ModuleFile) []bundle.ModuleFile { + + prefixes := ast.NewSet() + + for i := range b { + // NOTE(tsandall): use a set to memoize the prefix add operation--it's only + // needed once per rule set and constructing the path for every rule in the + // module could expensive for PE output (which can contain hundreds of thousands + // of rules.) + seen := ast.NewSet() + for _, rule := range b[i].Parsed.Rules { + name := ast.NewTerm(rule.Head.Ref()) + if !seen.Contains(name) { + prefixes.Add(ast.NewTerm(rule.Ref().ConstantPrefix())) + seen.Add(name) + } + } + } + + for i := range a { + + var keep []*ast.Rule + + // NOTE(tsandall): same as above--memoize keep/discard decision. If multiple + // entrypoints are provided the dst module may contain a large number of rules. + seen, discarded := ast.NewSet(), ast.NewSet() + for _, rule := range a[i].Parsed.Rules { + refT := ast.NewTerm(rule.Ref()) + switch { + case seen.Contains(refT): + keep = append(keep, rule) + continue + case discarded.Contains(refT): + continue + } + + path := rule.Ref().ConstantPrefix() + overlap := prefixes.Until(func(x *ast.Term) bool { + r := x.Value.(ast.Ref) + return r.HasPrefix(path) || path.HasPrefix(r) + }) + if overlap { + discarded.Add(refT) + continue + } + seen.Add(refT) + keep = append(keep, rule) + } + + if len(keep) > 0 { + a[i].Parsed.Rules = keep + a[i].Raw = nil + b = append(b, a[i]) + } + } + + return b +} + +func (o *optimizer) getSupportModuleFilename(used map[string]int, module *ast.Module, entrypointIndex int, supportIndex int) string { + + fileName, err := module.Package.Path.Ptr() + + if err == nil && safePathPattern.MatchString(fileName) { + fileName = o.outputprefix + "/" + fileName + uniqueFileName := fileName + if c, ok := used[fileName]; ok { + uniqueFileName += fmt.Sprintf(".%d", c) + } + used[fileName]++ + uniqueFileName += ".rego" + return uniqueFileName + } + + return fmt.Sprintf("%v/%v/%v/%v.rego", o.outputprefix, o.nsprefix, entrypointIndex, supportIndex) +} + +var safePathPattern = regexp.MustCompile(`^[\w-_/]+$`) + +func compile(c *ast.Capabilities, b *bundle.Bundle, dbg debug.Debug, enablePrintStatements bool) (*ast.Compiler, error) { + + modules := map[string]*ast.Module{} + + for _, mf := range b.Modules { + if _, ok := modules[mf.URL]; ok { + return nil, fmt.Errorf("duplicate module URL: %s", mf.URL) + } + + modules[mf.URL] = mf.Parsed + } + + compiler := ast.NewCompiler().WithCapabilities(c).WithDebug(dbg.Writer()).WithEnablePrintStatements(enablePrintStatements) + compiler.Compile(modules) + + if compiler.Failed() { + return nil, compiler.Errors + } + + minVersion, ok := compiler.Required.MinimumCompatibleVersion() + if !ok { + dbg.Printf("could not determine minimum compatible version!") + } else { + dbg.Printf("minimum compatible version: %v", minVersion) + } + + return compiler, nil +} + +func transitiveDocumentDependents(compiler *ast.Compiler, ref *ast.Term, deps map[*ast.Rule]struct{}) { + for _, rule := range compiler.GetRules(ref.Value.(ast.Ref)) { + transitiveDependents(compiler, rule, deps) + } +} + +func transitiveDependents(compiler *ast.Compiler, rule *ast.Rule, deps map[*ast.Rule]struct{}) { + for x := range compiler.Graph.Dependents(rule) { + other := x.(*ast.Rule) + deps[other] = struct{}{} + transitiveDependents(compiler, other, deps) + } +} + +type orderedStringSet []string + +func (ss orderedStringSet) Append(s ...string) orderedStringSet { + for _, x := range s { + var found bool + for _, other := range ss { + if x == other { + found = true + } + } + if !found { + ss = append(ss, x) + } + } + return ss +} + +func (ss orderedStringSet) Contains(s string) bool { + return slices.Contains(ss, s) +} + +func stringsToRefs(x []string) []ast.Ref { + result := make([]ast.Ref, len(x)) + for i := range result { + result[i] = storage.MustParsePath("/" + x[i]).Ref(ast.DefaultRootDocument) + } + return result +} + +type refSet struct { + s []ast.Ref +} + +func newRefSet(x ...ast.Ref) *refSet { + result := &refSet{} + for i := range x { + result.AddPrefix(x[i]) + } + return result +} + +// ContainsPrefix returns true if r is prefixed by any of the existing refs in the set. +func (rs *refSet) ContainsPrefix(r ast.Ref) bool { + return slices.ContainsFunc(rs.s, r.HasPrefix) +} + +// AddPrefix inserts r into the set if r is not prefixed by any existing +// refs in the set. If any existing refs are prefixed by r, those existing +// refs are removed. +func (rs *refSet) AddPrefix(r ast.Ref) { + if rs.ContainsPrefix(r) { + return + } + cpy := []ast.Ref{r} + for i := range rs.s { + if !rs.s[i].HasPrefix(r) { + cpy = append(cpy, rs.s[i]) + } + } + rs.s = cpy +} + +// Sorted returns a sorted slice of terms for refs in the set. +func (rs *refSet) Sorted() []*ast.Term { + terms := make([]*ast.Term, len(rs.s)) + for i := range rs.s { + terms[i] = ast.NewTerm(rs.s[i]) + } + sort.Slice(terms, func(i, j int) bool { + return terms[i].Value.Compare(terms[j].Value) < 0 + }) + return terms +} diff --git a/third_party/opa/v1/compile/compile_bench_test.go b/third_party/opa/v1/compile/compile_bench_test.go new file mode 100644 index 000000000000..fc0a939a045b --- /dev/null +++ b/third_party/opa/v1/compile/compile_bench_test.go @@ -0,0 +1,123 @@ +package compile + +import ( + "context" + "fmt" + "io/fs" + "strconv" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/util/test" +) + +// type compileBenchTestData struct { +// filename string +// module string +// } + +func BenchmarkCompileDynamicPolicy(b *testing.B) { + // This benchmarks the compiler against increasingly large numbers of dynamically-selected policies. + // See: https://github.com/open-policy-agent/opa/issues/5216 + numPolicies := []int{1000, 2500, 5000, 7500, 10000} + + for _, n := range numPolicies { + testcase := generateDynamicPolicyBenchmarkData(n) + test.WithTestFS(testcase, true, func(root string, fileSys fs.FS) { + b.ResetTimer() + b.Run(strconv.Itoa(n), func(b *testing.B) { + for range b.N { + compiler := New().WithFS(fileSys).WithPaths(root) + + if err := compiler.Build(context.Background()); err != nil { + b.Fatal("unexpected error", err) + } + } + }) + }) + } +} + +func generateDynamicPolicyBenchmarkData(n int) map[string]string { + files := map[string]string{ + "main.rego": ` + package main + + denies contains x if { + x := data.policies[input.type][input.subtype][_].denies[_] + } + any_denies if { + denies[_] + } + allow if { + not any_denies + }`, + } + + for i := range n { + files[fmt.Sprintf("policy%d.rego", i)] = generateDynamicMockPolicy(i) + } + + return files +} + +func generateDynamicMockPolicy(n int) string { + return fmt.Sprintf(`package policies["%d"]["%d"].policy%d +denies contains x if { + input.attribute == "%d" + x := "policy%d" +}`, n, n, n, n, n) +} + +func BenchmarkLargePartialRulePolicy(b *testing.B) { + // This benchmarks the compiler against very large partial rule sets. + // See: https://github.com/open-policy-agent/opa/issues/5756 + numPolicies := []int{1000, 2500, 5000, 7500} + + for _, n := range numPolicies { + testcase := generateLargePartialRuleBenchmarkData(n) + b.ResetTimer() + b.Run(strconv.Itoa(n), func(b *testing.B) { + test.WithTempFS(testcase, func(root string) { + b.ResetTimer() + + for range b.N { + compiler := New().WithPaths(root) + + if err := compiler.Build(context.Background()); err != nil { + b.Fatal("unexpected error", err) + } + } + }) + }) + } +} + +func generateLargePartialRuleBenchmarkData(n int) map[string]string { + var policy strings.Builder + policy.Grow((140 * n) + 100) // Each rule takes around 130 characters. + + policy.WriteString(`package example.large.partial.rules.policy["dynamic_part"].main`) + policy.WriteString("\n\n") + for i := range n { + policy.WriteString(generateLargePartialRuleMockRule(i)) + policy.WriteString("\n\n") + } + policy.WriteString(`number_denies = x if { + x := count(deny) + }`) + + files := map[string]string{ + "main.rego": policy.String(), + } + return files +} + +func generateLargePartialRuleMockRule(n int) string { + return fmt.Sprintf(`deny contains [resource, errormsg] if { + resource := "example.%d" + i := %d + i %% 2 != 0 + errormsg := "denied because %d is an odd number." +}`, n, n, n) +} diff --git a/third_party/opa/v1/compile/compile_test.go b/third_party/opa/v1/compile/compile_test.go new file mode 100644 index 000000000000..6cb5258c88e2 --- /dev/null +++ b/third_party/opa/v1/compile/compile_test.go @@ -0,0 +1,3713 @@ +package compile + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io/fs" + "maps" + "os" + "path" + "path/filepath" + "slices" + "strconv" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/internal/ref" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/format" + "github.com/open-policy-agent/opa/v1/ir" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestCompilerV1Module(t *testing.T) { + + files := map[string]string{ + "test.rego": ` + package test + + p contains x if { + x = "a" + }`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root) + + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + // Verify result is just bundle load. + exp, err := loader.NewFileLoader().WithFS(fsys).AsBundle(root) + if err != nil { + panic(err) + } + + err = exp.FormatModules(false) + if err != nil { + t.Fatal(err) + } + + if !compiler.Bundle().Equal(*exp) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", compiler.Bundle(), exp) + } + }) + } +} + +func TestOrderedStringSet(t *testing.T) { + var ss orderedStringSet + result := ss.Append("a", "b", "b", "a", "e", "c", "e") + if !slices.Equal(result, orderedStringSet{"a", "b", "e", "c"}) { + t.Fatal(result) + } +} + +func TestCompilerInitErrors(t *testing.T) { + + ctx := context.Background() + + tests := []struct { + note string + c *Compiler + want error + }{ + { + note: "bad target", + c: New().WithTarget("deadbeef"), + want: errors.New("invalid target \"deadbeef\""), + }, + { + note: "optimizations require entrypoint", + c: New().WithOptimizationLevel(1), + want: errors.New("bundle optimizations require at least one entrypoint"), + }, + { + note: "wasm compilation requires at least one entrypoint", + c: New().WithTarget("wasm"), + want: errors.New("wasm compilation requires at least one entrypoint"), + }, + { + note: "plan compilation requires at least one entrypoint", + c: New().WithTarget("plan"), + want: errors.New("plan compilation requires at least one entrypoint"), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + err := tc.c.Build(ctx) + if err == nil { + t.Fatal("expected error") + } else if err.Error() != tc.want.Error() { + t.Fatalf("expected %v but got %v", tc.want, err) + } + }) + } + +} + +func TestCompilerLoadError(t *testing.T) { + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(nil, useMemoryFS, func(root string, fsys fs.FS) { + + err := New(). + WithFS(fsys). + WithPaths(path.Join(root, "does-not-exist")). + Build(context.Background()) + if err == nil { + t.Fatal("expected failure") + } + }) + } +} + +func TestCompilerLoadAsBundleSuccess(t *testing.T) { + + ctx := context.Background() + rv := strconv.Itoa(ast.DefaultRegoVersion.Int()) + + files := map[string]string{ + "b1/.manifest": `{"roots": ["b1"], "rego_version": ` + rv + `}`, + "b1/test.rego": ` + package b1.test + import rego.v1 + + p = 1`, + "b1/data.json": ` + {"b1": {"k": "v"}}`, + "b2/.manifest": `{"roots": ["b2"], "rego_version": ` + rv + `}`, + "b2/data.json": ` + {"b2": {"k2": "v2"}}`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + root1 := path.Join(root, "b1") + root2 := path.Join(root, "b2") + + compiler := New(). + WithFS(fsys). + WithPaths(root1, root2). + WithAsBundle(true) + + err := compiler.Build(ctx) + if err != nil { + t.Fatal(err) + } + + // Verify result is just merger of two bundles. + a, err := loader.NewFileLoader().WithFS(fsys).AsBundle(root1) + if err != nil { + panic(err) + } + + b, err := loader.NewFileLoader().WithFS(fsys).AsBundle(root2) + if err != nil { + panic(err) + } + + exp, err := bundle.Merge([]*bundle.Bundle{a, b}) + if err != nil { + panic(err) + } + + err = exp.FormatModules(false) + if err != nil { + t.Fatal(err) + } + + if !compiler.bundle.Equal(*exp) { + t.Fatalf("expected:\n\n%v\n\nbut got:\n\n%v", exp, compiler.bundle) + } + + expRoots := []string{"b1", "b2"} + expManifest := bundle.Manifest{ + Roots: &expRoots, + } + expManifest.SetRegoVersion(ast.DefaultRegoVersion) + + if !compiler.bundle.Manifest.Equal(expManifest) { + t.Fatalf("expected %v but got %v", compiler.bundle.Manifest, expManifest) + } + }) + } +} + +func TestCompilerLoadAsBundleWithBundleRegoVersion(t *testing.T) { + tests := []struct { + note string + files map[string]string + expErrs []string + }{ + { + note: "No bundle rego version (default version)", + files: map[string]string{ + ".manifest": `{}`, + "test.rego": `package test +import rego.v1 +p[1] if { + input.x == 2 +}`, + }, + }, + { + note: "v0 bundle rego version", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "test.rego": `package test +p[1] { + input.x == 2 +}`, + }, + }, + { + note: "v0 bundle rego version, missing keyword imports", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "test.rego": `package test +p contains 1 if { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v1 bundle rego version", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "test.rego": `package test +p contains 1 if { + input.x == 2 +}`, + }, + }, + { + note: "v1 bundle rego version, no keywords", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "test.rego": `package test +p[1] { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1 bundle rego version, duplicate imports", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "test.rego": `package test +import data.foo +import data.foo + +p contains 1 if { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + // file overrides + { + note: "v0 bundle rego version, v1 file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "test1.rego": `package test +p["A"] { + input.x == 1 +}`, + "test2.rego": `package test +p contains "B" if { + input.x == 2 +}`, + }, + }, + { + note: "v0 bundle rego version, v1 file override, missing file", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "test1.rego": `package test +p["A"] { + input.x == 1 +}`, + }, + }, + { + note: "v0 bundle rego version, v1 file override, no keywords", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "test1.rego": `package test +p["A"] { + input.x == 1 +}`, + "test2.rego": `package test +p["B"] { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0 bundle rego version, v1 file override, duplicate imports", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "*/test2.rego": 1 + } +}`, + "test1.rego": `package test +p["A"] { + input.x == 1 +}`, + "test2.rego": `package test +import data.foo +import data.foo + +p contains "B" if { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v1 bundle rego version, v0 file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "*/test1.rego": 0 + } +}`, + "test1.rego": `package test +p["A"] { + input.x == 1 +}`, + "test2.rego": `package test +p contains "B" if { + input.x == 2 +}`, + }, + }, + { + note: "v1 bundle rego version, v0 file override, no import", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "*/test1.rego": 0 + } +}`, + "test1.rego": `package test +p contains "A" if { + input.x == 1 +}`, + "test2.rego": `package test +p contains "B" if { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: string cannot be used for rule name", + }, + }, + } + + bundleTypeCases := []struct { + note string + tar bool + }{ + { + "bundle dir", false, + }, + { + "bundle tar", true, + }, + } + + for _, bundleType := range bundleTypeCases { + for _, tc := range tests { + ctx := context.Background() + t.Run(fmt.Sprintf("%s, %s", bundleType.note, tc.note), func(t *testing.T) { + files := map[string]string{} + if bundleType.tar { + files["bundle.tar"] = "" + } else { + maps.Copy(files, tc.files) + } + + test.WithTestFS(tc.files, false, func(root string, fsys fs.FS) { + var path string + if bundleType.tar { + path = filepath.Join(root, "bundle.tar.gz") + files := make([][2]string, 0, len(tc.files)) + for k, v := range tc.files { + files = append(files, [2]string{k, v}) + } + buf := archive.MustWriteTarGz(files) + + bf, err := os.Create(path) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } else { + path = root + } + + compiler := New(). + WithFS(fsys). + WithPaths(path). + WithAsBundle(true) + + err := compiler.Build(ctx) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatal("expected error, got none") + } + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected error to contain:\n\n%s\n\ngot:\n\n%v", expErr, err) + } + } + } else if err != nil { + t.Fatal(err) + } + }) + }) + } + } +} + +func TestCompilerBundleMergeWithBundleRegoVersion(t *testing.T) { + regoV0 := ast.RegoV0.Int() + regoV1 := ast.RegoV1.Int() + regoDef := ast.RegoV1.Int() + + tests := []struct { + note string + bundles []*bundle.Bundle + regoVersion ast.RegoVersion + expGlobalRegoVersion *int + expFileRegoVersions map[string]int + }{ + { + note: "single bundle, no bundle rego version (default version)", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{}, + }, + }, + expGlobalRegoVersion: ®oDef, + expFileRegoVersions: map[string]int{}, + }, + { + note: "single bundle, global rego version", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + RegoVersion: ®oV1, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{}, + }, + }, + expGlobalRegoVersion: ®oV1, + expFileRegoVersions: map[string]int{}, + }, + { + note: "no global rego versions", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{}, + }, + { + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{}, + }, + }, + regoVersion: ast.RegoV1, + expGlobalRegoVersion: ®oV1, + }, + { + note: "global rego versions, v1 bundles, v0 provided", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + RegoVersion: ®oV1, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "a/test1.rego", + URL: "a/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package a"), + }, + }, + }, + { + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + RegoVersion: ®oV1, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "b/test1.rego", + URL: "b/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package b"), + }, + }, + }, + }, + regoVersion: ast.RegoV0, + // global rego-version in bundles are dropped in favor of the provided rego-version + expGlobalRegoVersion: ®oV0, + expFileRegoVersions: map[string]int{ + "/a/test1.rego": 1, + "/b/test1.rego": 1, + }, + }, + { + note: "global rego versions, v0 bundles, v1 provided", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + RegoVersion: ®oV0, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "a/test1.rego", + URL: "a/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package a"), + }, + }, + }, + { + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + RegoVersion: ®oV0, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "b/test1.rego", + URL: "b/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package b"), + }, + }, + }, + }, + regoVersion: ast.RegoV1, + // global rego-version in bundles are dropped in favor of the provided rego-version + expGlobalRegoVersion: ®oV1, + expFileRegoVersions: map[string]int{ + "/a/test1.rego": 0, + "/b/test1.rego": 0, + }, + }, + { + note: "different global rego versions", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + RegoVersion: ®oV0, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "a/test1.rego", + URL: "a/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package a"), + }, + }, + }, + { + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + RegoVersion: ®oV1, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "b/test1.rego", + URL: "b/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package b"), + }, + }, + }, + }, + regoVersion: ast.RegoV0, + // global rego-version in bundles are dropped in favor of the provided rego-version + expGlobalRegoVersion: ®oV0, + expFileRegoVersions: map[string]int{ + "/b/test1.rego": 1, + }, + }, + { + note: "different global rego versions, per-file overrides", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + RegoVersion: ®oV1, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "a/test1.rego", + URL: "a/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package a"), + }, + { + Path: "a/test2.rego", + URL: "a/test2.rego", + RelativePath: "/test2.rego", + Raw: []byte("package a"), + }, + }, + }, + { + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + RegoVersion: ®oV1, + FileRegoVersions: map[string]int{ + "/test1.rego": 0, + }, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + // we don't expect this file to get an individual rego-version in the result, as + // it has the same rego-version as the global rego-version + Path: "b/test1.rego", + URL: "b/test1.rego", + RelativePath: "/test1.rego", + Raw: []byte("package b"), + }, + { + Path: "b/test2.rego", + URL: "b/test2.rego", + RelativePath: "/test2.rego", + Raw: []byte("package b"), + }, + }, + }, + { + Manifest: bundle.Manifest{ + RegoVersion: ®oV0, + Roots: &[]string{"c"}, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + // we don't expect these files to get individual rego-versions in the result, + // as they have the same rego-version as the global rego-version + { + Path: "c/test1.rego", + URL: "c/test1.rego", + RelativePath: "test1.rego", + Raw: []byte("package c"), + }, + { + Path: "c/test2.rego", + URL: "c/test2.rego", + RelativePath: "test2.rego", + Raw: []byte("package c"), + }, + }, + }, + }, + regoVersion: ast.RegoV0, + // global rego-version in bundles are dropped in favor of the provided rego-version + expGlobalRegoVersion: ®oV0, + // rego-versions is expected for all modules with different rego-version than the global rego-version + expFileRegoVersions: map[string]int{ + "/a/test1.rego": 1, + "/a/test2.rego": 1, + "/b/test2.rego": 1, + }, + }, + { + note: "glob per-file overrides", + bundles: []*bundle.Bundle{ + { + Manifest: bundle.Manifest{ + Roots: &[]string{"a"}, + RegoVersion: ®oV0, + FileRegoVersions: map[string]int{ + "a/*": 1, + }, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "a/foo/test.rego", + URL: "a/foo/test.rego", + Raw: []byte("package a"), + }, + { + Path: "a/bar/test.rego", + URL: "a/bar/test.rego", + Raw: []byte("package a"), + }, + { + Path: "a/baz/test.rego", + URL: "a/baz/test.rego", + Raw: []byte("package a"), + }, + }, + }, + { + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + RegoVersion: ®oV1, + FileRegoVersions: map[string]int{ + // glob should not affect files with matching path in the other bundle + "*/bar/*": 0, + }, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "b/foo/test.rego", + URL: "b/foo/test.rego", + Raw: []byte("package b"), + }, + { + Path: "b/bar/test.rego", + URL: "b/bar/test.rego", + Raw: []byte("package b"), + }, + { + Path: "b/baz/test.rego", + URL: "b/baz/test.rego", + Raw: []byte("package b"), + }, + }, + }, + }, + regoVersion: ast.RegoV0, + expGlobalRegoVersion: ®oV0, + expFileRegoVersions: map[string]int{ + "/a/foo/test.rego": 1, + "/a/bar/test.rego": 1, + "/a/baz/test.rego": 1, + "/b/foo/test.rego": 1, + "/b/baz/test.rego": 1, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + for _, b := range tc.bundles { + b.Manifest.Init() + for i, m := range b.Modules { + b.Modules[i].Parsed = ast.MustParseModule(string(m.Raw)) + } + } + + result, err := bundle.MergeWithRegoVersion(tc.bundles, tc.regoVersion, false) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + compareRegoVersions(t, tc.expGlobalRegoVersion, result.Manifest.RegoVersion) + + if !maps.Equal(tc.expFileRegoVersions, result.Manifest.FileRegoVersions) { + t.Fatalf("expected file rego versions to be:\n\n%v\n\nbut got:\n\n%v", tc.expFileRegoVersions, result.Manifest.FileRegoVersions) + } + }) + } +} + +func compareRegoVersions(t *testing.T, exp, act *int) { + t.Helper() + if exp == nil { + if act != nil { + t.Errorf("expected no rego version, but got %v", *act) + } + } else { + if act == nil { + t.Errorf("expected rego version to be %v, but got none", *exp) + } else if *act != *exp { + t.Errorf("expected rego version to be %v, but got %v", *exp, *act) + } + } +} + +func TestCompilerLoadAsBundleMergeError(t *testing.T) { + + ctx := context.Background() + + // Omit manifests (defaulting to '') to trigger a merge error + files := map[string]string{ + "b1/test.rego": ` + package b1.test + + p = 1`, + "b1/data.json": ` + {"b1": {"k": "v"}}`, + "b2/data.json": ` + {"b2": {"k2": "v2"}}`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + root1 := path.Join(root, "b1") + root2 := path.Join(root, "b2") + + compiler := New(). + WithFS(fsys). + WithPaths(root1, root2). + WithAsBundle(true) + + err := compiler.Build(ctx) + if err == nil || err.Error() != "bundle merge failed: manifest has overlapped roots: '' and ''" { + t.Fatal(err) + } + }) + } +} + +func TestCompilerLoadFilesystem(t *testing.T) { + + files := map[string]string{ + "test.rego": ` + package b1.test + + p = 1`, + "data.json": ` + {"b1": {"k": "v"}}`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root) + + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + // Verify result is just bundle load. + exp, err := loader.NewFileLoader().WithFS(fsys).AsBundle(root) + if err != nil { + panic(err) + } + + err = exp.FormatModules(false) + if err != nil { + t.Fatal(err) + } + + if !compiler.bundle.Equal(*exp) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", compiler.bundle, exp) + } + }) + } +} + +func TestCompilerLoadFilesystemWithEnablePrintStatementsFalse(t *testing.T) { + files := map[string]string{ + "test.rego": ` + package test + import rego.v1 + + allow if { print(1) } + `, + "data.json": ` + {"b1": {"k": "v"}}`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("plan").WithEntrypoints("test/allow"). + WithEnablePrintStatements(false) + + if err := compiler.Build(context.Background()); err != nil { + t.Fatal(err) + } + + bundle := compiler.Bundle() + + if strings.Contains(string(bundle.PlanModules[0].Raw), "internal.print") { + t.Fatalf("output different than expected:\n\ngot: %v\n\nfound: internal.print", string(bundle.PlanModules[0].Raw)) + } + }) + } +} + +func TestCompilerLoadFilesystemWithEnablePrintStatementsTrue(t *testing.T) { + files := map[string]string{ + "test.rego": ` + package test + import rego.v1 + + allow if { print(1) } + `, + "data.json": ` + {"b1": {"k": "v"}}`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("plan"). + WithEntrypoints("test/allow"). + WithEnablePrintStatements(true) + + if err := compiler.Build(context.Background()); err != nil { + t.Fatal(err) + } + + bundle := compiler.Bundle() + + if !strings.Contains(string(bundle.PlanModules[0].Raw), "internal.print") { + t.Fatalf("output different than expected:\n\ngot: %v\n\nmissing: internal.print", string(bundle.PlanModules[0].Raw)) + } + }) + } +} + +func TestCompilerLoadHonorsFilter(t *testing.T) { + files := map[string]string{ + "test.rego": ` + package b1.test + + p = 1`, + "data.json": ` + {"b1": {"k": "v"}}`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithFilter(func(abspath string, _ os.FileInfo, _ int) bool { + return strings.HasSuffix(abspath, ".json") + }) + + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(compiler.bundle.Data) > 0 { + t.Fatal("expected no data to be loaded") + } + }) + } +} + +func TestCompilerInputBundle(t *testing.T) { + + b := &bundle.Bundle{ + Modules: []bundle.ModuleFile{ + { + URL: "/foo.rego", + Path: "/foo.rego", + Raw: []byte("package test\np := 7"), + Parsed: ast.MustParseModule("package test\np = 7"), + }, + }, + } + + compiler := New().WithBundle(b) + + if err := compiler.Build(context.Background()); err != nil { + t.Fatal(err) + } + + exp := "package test\n\np := 7\n" + + if exp != string(compiler.Bundle().Modules[0].Raw) { + t.Fatalf("expected module to have been formatted (output different than expected):\n\ngot: %v\n\nwant: %v", string(compiler.Bundle().Modules[0].Raw), exp) + } +} + +func TestCompilerInputInvalidBundle(t *testing.T) { + + b := &bundle.Bundle{ + Modules: []bundle.ModuleFile{ + { + URL: "/url", + Path: "/foo.rego", + Raw: []byte("package test\np = 0"), + Parsed: ast.MustParseModule("package test\np = 0"), + }, + { + URL: "/url", + Path: "/bar.rego", + Raw: []byte("package test\nq = 1"), + Parsed: ast.MustParseModule("package test\nq = 1"), + }, + }, + } + + compiler := New().WithBundle(b) + + if err := compiler.Build(context.Background()); err == nil { + t.Fatal("duplicate module URL not detected") + } else if err.Error() != "duplicate module URL: /url" { + t.Fatal(err) + } +} + +func TestCompilerError(t *testing.T) { + files := map[string]string{ + "test.rego": ` + package test + import rego.v1 + default p := false + p if { p }`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root) + + err := compiler.Build(context.Background()) + if err == nil { + t.Fatal("expected error") + } + + astErr, ok := err.(ast.Errors) + if !ok || len(astErr) != 1 || astErr[0].Code != ast.RecursionErr { + t.Fatal("unexpected error:", err) + } + }) + } +} + +func TestCompilerOptimizationL1(t *testing.T) { + + files := map[string]string{ + "test.rego": ` + package test + + default p := false + p if { q } + q if { input.x = data.foo }`, + "data.json": ` + {"foo": 1}`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithRegoVersion(ast.RegoV1). + WithFS(fsys). + WithPaths(root). + WithOptimizationLevel(1). + WithEntrypoints("test/p") + + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + optimizedExp := ast.MustParseModuleWithOpts(` + package test + + default p = false + p if { data.test.q = X; X } + q if { input.x = 1 } + `, ast.ParserOptions{RegoVersion: ast.RegoV1}) + + // NOTE(tsandall): PE generates vars with wildcard prefix. Instead of + // constructing the AST manually, just rewrite to the expected value + // here. If this becomes a common pattern, we could refactor (e.g., + // allow caller to control var prefix, split into a reusable function, + // etc.) + _, err = ast.TransformVars(optimizedExp, func(x ast.Var) (ast.Value, error) { + if x == ast.Var("X") { + return ast.Var("$_term_1_01"), nil + } + return x, nil + }) + if err != nil { + t.Fatal(err) + } + + if len(compiler.bundle.Modules) != 1 { + t.Fatalf("expected 1 module but got: %v", compiler.bundle.Modules) + } + + if !compiler.bundle.Modules[0].Parsed.Equal(optimizedExp) { + t.Fatalf("expected optimized module to be:\n\n%v\n\ngot:\n\n%v", optimizedExp, compiler.bundle.Modules[0]) + } + }) + } +} + +func TestCompilerOptimizationL2(t *testing.T) { + + files := map[string]string{ + "test.rego": ` + package test + default p := false + p if { q } + q if { input.x = data.foo }`, + "data.json": ` + {"foo": 1}`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithRegoVersion(ast.RegoV1). + WithFS(fsys). + WithPaths(root). + WithOptimizationLevel(2). + WithEntrypoints("test/p") + + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + prunedExp := ast.MustParseModuleWithOpts(` + package test + + q if { input.x = data.foo }`, + ast.ParserOptions{RegoVersion: ast.RegoV1}) + + optimizedExp := ast.MustParseModuleWithOpts(` + package test + + default p = false + p if { input.x = 1 }`, + ast.ParserOptions{RegoVersion: ast.RegoV1}) + + if len(compiler.bundle.Modules) != 2 { + t.Fatalf("expected two modules but got: %v", compiler.bundle.Modules) + } + + // Note: L2 optimized ModuleFile ordering in a bundle is non-deterministic... + if !compiler.bundle.Modules[0].Parsed.Equal(prunedExp) { + if !compiler.bundle.Modules[0].Parsed.Equal(optimizedExp) { + t.Fatalf("expected optimized module to be:\n\n%v\n\ngot:\n\n%v", optimizedExp, compiler.bundle.Modules[0]) + } + if !compiler.bundle.Modules[1].Parsed.Equal(prunedExp) { + t.Fatalf("expected pruned module to be:\n\n%v\n\ngot:\n\n%v", prunedExp, compiler.bundle.Modules[1]) + } + } else if !compiler.bundle.Modules[1].Parsed.Equal(optimizedExp) { + t.Fatalf("expected optimized module to be:\n\n%v\n\ngot:\n\n%v", optimizedExp, compiler.bundle.Modules[1]) + } + }) + } +} + +func TestCompilerOptimizationWithConfiguredNamespace(t *testing.T) { + + files := map[string]string{ + "test.rego": ` + package test + import rego.v1 + + p if { not q } + q if { k[input.a]; k[input.b] } # generate a product that is not inlined + k = {1,2,3} + `, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithOptimizationLevel(1). + WithEntrypoints("test/p"). + WithPartialNamespace("custom") + + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(compiler.bundle.Modules) != 2 { + t.Fatalf("expected two modules but got: %v", len(compiler.bundle.Modules)) + } + + // The compiler will drop the rego.v1 import, so we need to affix the rego-version of the expected module to + // v1, to have its string serialization include 'if'/'else' keywords but not the rego.v1 import. + optimizedExp := ast.MustParseModuleWithOpts(`package custom + __not1_0_2__ = true if { data.test.q = _; _ }`, + ast.ParserOptions{RegoVersion: ast.RegoV1}) + + if optimizedExp.String() != compiler.bundle.Modules[0].Parsed.String() { + t.Fatalf("expected optimized module to be:\n\n%v\n\ngot:\n\n%v", optimizedExp, compiler.bundle.Modules[0]) + } + + expected := ast.MustParseModuleWithOpts(`package test + k = {1, 2, 3} if { true } + p = true if { not data.custom.__not1_0_2__ } + q = true if { __local0__3 = input.a; data.test.k[__local0__3] = _; _; __local1__3 = input.b; data.test.k[__local1__3] = _; _ }`, + ast.ParserOptions{RegoVersion: ast.RegoV1}) + + if expected.String() != compiler.bundle.Modules[1].Parsed.String() { + t.Fatalf("expected module to be:\n\n%v\n\ngot:\n\n%v", expected, compiler.bundle.Modules[1]) + } + }) + } +} + +func TestCompilerOptimizationWithGeneralRefs(t *testing.T) { + tests := []struct { + note string + entrypoint string + files map[string]string + expected []string + }{ + { + note: "special characters in ref term", + files: map[string]string{ + "base.rego": `package base +allow["entity/slash"].action { + action := "action" + input.principal == input.entity +}`, + "query.rego": `package query +main { + data.base.allow[input.entity.type][input.action] +}`, + }, + entrypoint: "query/main", + expected: []string{ + `package base + +allow["entity/slash"].action { + input.principal = input.entity +} +`, + `package query + +main { + __local1__1 = input.entity.type + __local2__1 = input.action + "entity/slash" = __local1__1 + "action" = __local2__1 + data.base.allow[__local1__1][__local2__1] = _term_1_21 + _term_1_21 +} +`, + }, + }, + { + note: "single rule (one key), no ref, no unknowns", + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test +p[r] { + r := ["do", "re"][_] +}`, + }, + expected: []string{ + `package test + +p = __result__ { + __result__ = {"do", "re"} +} +`, + }, + }, + { + note: "single rule (one key), no ref, unknown in body", + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test +p[r] { + r := ["do", "re"][_] + input.x +}`, + }, + expected: []string{ + `package test + +p["do"] { + input.x = _term_1_21 + _term_1_21 +} + +p["re"] { + input.x = _term_1_21 + _term_1_21 +} +`, + }, + }, + { + note: "single rule (one key), no unknowns", + entrypoint: "test/p/q", + files: map[string]string{ + "test.rego": `package test +p.q[r] { + r := ["do", "re"][_] + input.x +}`, + }, + expected: []string{ + `package test.p.q + +do { + input.x = _term_1_21 + _term_1_21 +} + +re { + input.x = _term_1_21 + _term_1_21 +} +`, + }, + }, + { + note: "single rule, no unknowns", + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test +p[q][r] { + q := ["foo", "bar"][_] + r := ["do", "re"][_] +}`, + }, + expected: []string{ + `package test + +p = __result__ { + __result__ = {"foo": {"do": true, "re": true}, "bar": {"do": true, "re": true}} +} +`, + }, + }, + { + note: "single rule, unknown value", + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test +p[q][r] := s { + q := ["foo", "bar"][_] + r := ["do", "re"][_] + s := input.x +}`, + }, + expected: []string{ + `package test.p.foo + +do = __local2__1 { + __local2__1 = input.x +} + +re = __local2__1 { + __local2__1 = input.x +} +`, + `package test.p.bar + +do = __local2__1 { + __local2__1 = input.x +} + +re = __local2__1 { + __local2__1 = input.x +} +`, + }, + }, + { + note: "single rule, unknown key (first)", + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test +p[q][r] { + q := input.x[_] + r := ["do", "re"][_] +}`, + }, + expected: []string{ + `package test + +p[__local0__1].do { + __local0__1 = input.x[_01] +} + +p[__local0__1].re { + __local0__1 = input.x[_01] +} +`, + }, + }, + { + note: "single rule, unknown key (second)", + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test +p[q][r] { + q := ["foo", "bar"][_] + r := input.x[_] +}`, + }, + expected: []string{ + `package test.p + +bar[__local1__1] = true { + __local1__1 = input.x[_11] +} + +foo[__local1__1] = true { + __local1__1 = input.x[_11] +} +`, + }, + }, + { + note: "regression test for #6338", + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test + +p { + q[input.x][input.y] +} + +q["foo/bar"][x] { + x := "baz" + input.x == 1 +}`, + }, + expected: []string{ + `package test + +p { + __local1__1 = input.x + __local2__1 = input.y + "foo/bar" = __local1__1 + data.test.q[__local1__1][__local2__1] = _term_1_21 + _term_1_21 +} + +q["foo/bar"].baz { + input.x = 1 +} +`, + }, + }, + { + note: "regression test for #6339", + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test + +import future.keywords.in + +p { + q[input.x][input.y] +} + +q[entity][action] { + some action in ["show", "update"] + some entity in ["pay", "roll"] + input.z == 1 +}`, + }, + expected: []string{ + `package test + +p { + __local8__1 = input.x + __local9__1 = input.y + data.test.q[__local8__1][__local9__1] = _term_1_21 + _term_1_21 +} +`, + `package test.q.pay + +show { + input.z = 1 +} + +update { + input.z = 1 +} +`, + `package test.q.roll + +show { + input.z = 1 +} + +update { + input.z = 1 +} +`, + }, + }, + { + note: "not", + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test + +import future.keywords.in + +p { + not q[input.x][input.y] +} + +q[entity][action] { + some action in ["show", "update"] + some entity in ["pay", "roll"] + input.z == 1 +}`, + }, + expected: []string{ + `package partial + +__not1_2_2__(__local8__1, __local9__1) { + data.test.q[__local8__1][__local9__1] = _term_2_01 + _term_2_01 +} +`, + `package test + +p { + __local8__1 = input.x + __local9__1 = input.y + not data.partial.__not1_2_2__(__local8__1, __local9__1) +} +`, + `package test.q.pay + +show { + input.z = 1 +} + +update { + input.z = 1 +} +`, + `package test.q.roll + +show { + input.z = 1 +} + +update { + input.z = 1 +} +`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(tc.files, useMemoryFS, func(root string, fsys fs.FS) { + + caps := ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)) + caps.Features = []string{ + ast.FeatureRefHeadStringPrefixes, + ast.FeatureRefHeads, + } + + compiler := New(). + // In v1, the rego.v1 import is stripped from optimized modules, but in v0 it is not. + // Therefore, we need to tie down the test modules to a specific version. + WithRegoVersion(ast.RegoV0). + WithFS(fsys). + WithPaths(root). + WithOptimizationLevel(1). + WithEntrypoints(tc.entrypoint). + WithCapabilities(caps) + + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(compiler.bundle.Modules) != len(tc.expected) { + t.Fatalf("expected %v modules but got: %v:\n\n%v", + len(tc.expected), len(compiler.bundle.Modules), modulesToString(compiler.bundle.Modules)) + } + + actual := make(map[string]struct{}) + for _, m := range compiler.bundle.Modules { + actual[string(m.Raw)] = struct{}{} + } + + for _, e := range tc.expected { + if _, ok := actual[e]; !ok { + t.Fatalf("expected to find module:\n\n%v\n\nin bundle but got:\n\n%v", + e, modulesToString(compiler.bundle.Modules)) + } + } + }) + } + }) + } +} + +func TestCompilerOptimizationSupportRegoVersion(t *testing.T) { + tests := []struct { + note string + modulesRegoVersion ast.RegoVersion + noKeywordsInRefsCapability bool + regoV1ImportCapable bool + entrypoint string + files map[string]string + expected []string + }{ + { + note: "v0 module, rego.v1 capable", + modulesRegoVersion: ast.RegoV0, + regoV1ImportCapable: true, + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test +p { + input.x == 1 +}`, + }, + expected: []string{ + `package test + +import rego.v1 + +p if { + input.x = 1 +} +`, + }, + }, + { + note: "v0 module, rego.v1 capable, rule name conflict with keyword", + modulesRegoVersion: ast.RegoV0, + regoV1ImportCapable: true, + entrypoint: "test/contains", + files: map[string]string{ + "test.rego": `package test +contains { + input.x == 1 +}`, + }, + // rego.v1 import not used, since rule name conflicts with future keyword + expected: []string{ + `package test + +contains { + input.x = 1 +} +`, + }, + }, + { + note: "v0 module, rego.v1 capable, import conflict with keyword", + modulesRegoVersion: ast.RegoV0, + regoV1ImportCapable: true, + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test +import data.foo.contains + +p { + input.x == contains +}`, + "foo.rego": `package foo +contains := 2 { + input.a == input.b +}`, + }, + // rego.v1 import used for data.test, since complete ref to data.foo.contains is used locally without original import + expected: []string{ + `package test + +import rego.v1 + +p if { + data.foo.contains = input.x +} +`, + `package foo + +contains = 2 { + input.a = input.b +} +`, + }, + }, + { + note: "v0 module, rego.v1 capable, import conflict with keyword, no capability", + modulesRegoVersion: ast.RegoV0, + noKeywordsInRefsCapability: true, + regoV1ImportCapable: true, + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test +import data.foo.contains + +p { + input.x == contains +}`, + "foo.rego": `package foo +contains := 2 { + input.a == input.b +}`, + }, + // rego.v1 import used for data.test, since complete ref to data.foo.contains is used locally without original import + expected: []string{ + `package test + +import rego.v1 + +p if { + data.foo["contains"] = input.x +} +`, + `package foo + +contains = 2 { + input.a = input.b +} +`, + }, + }, + { + note: "v0 module, rego.v1 capable, rule ref conflict with keyword", + modulesRegoVersion: ast.RegoV0, + regoV1ImportCapable: true, + entrypoint: "test/contains", + files: map[string]string{ + "test.rego": `package test +contains[input.x][input.y] { + input.z == 1 +}`, + }, + // rego.v1 import not used, since leading var in rule ref conflicts with future keyword + expected: []string{ + `package test + +contains[__local0__1][__local1__1] { + input.z = 1 + __local0__1 = input.x + __local1__1 = input.y +} +`, + }, + }, + { + note: "v0 module, not rego.v1 capable", + modulesRegoVersion: ast.RegoV0, + regoV1ImportCapable: false, + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test +p { + input.x == 1 +}`, + }, + expected: []string{ + `package test + +p { + input.x = 1 +} +`, + }, + }, + { + note: "v0-compat_v1 module, rego.v1 capable", + modulesRegoVersion: ast.RegoV0CompatV1, + regoV1ImportCapable: true, + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test + +import rego.v1 + +p if { + input.x == 1 +}`, + }, + expected: []string{ + `package test + +import rego.v1 + +p if { + input.x = 1 +} +`, + }, + }, + { + note: "v1 module, rego.v1 capable", + modulesRegoVersion: ast.RegoV1, + regoV1ImportCapable: true, + entrypoint: "test/p", + files: map[string]string{ + "test.rego": `package test +p if { + input.x == 1 +}`, + }, + expected: []string{ + `package test + +p if { + input.x = 1 +} +`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTestFS(tc.files, true, func(root string, fsys fs.FS) { + capabilities := ast.CapabilitiesForThisVersion() + capabilities.Features = []string{ + ast.FeatureRefHeadStringPrefixes, + ast.FeatureRefHeads, + } + if tc.modulesRegoVersion == ast.RegoV1 { + capabilities.Features = append(capabilities.Features, ast.FeatureRegoV1) + } + if tc.regoV1ImportCapable { + capabilities.Features = append(capabilities.Features, ast.FeatureRegoV1Import) + } + if !tc.noKeywordsInRefsCapability { + capabilities.Features = append(capabilities.Features, ast.FeatureKeywordsInRefs) + } + + compiler := New(). + WithCapabilities(capabilities). + WithRegoVersion(tc.modulesRegoVersion). + WithFS(fsys). + WithPaths(root). + WithOptimizationLevel(1). + WithEntrypoints(tc.entrypoint) + + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(compiler.bundle.Modules) != len(tc.expected) { + t.Fatalf("expected %v modules but got: %v:\n\n%v", + len(tc.expected), len(compiler.bundle.Modules), modulesToString(compiler.bundle.Modules)) + } + + actual := make(map[string]struct{}) + for _, m := range compiler.bundle.Modules { + actual[string(m.Raw)] = struct{}{} + } + + for _, e := range tc.expected { + if _, ok := actual[e]; !ok { + t.Fatalf("expected to find module:\n\n%v\n\nin bundle but got:\n\n%v", + e, modulesToString(compiler.bundle.Modules)) + } + } + }) + }) + } +} + +func modulesToString(modules []bundle.ModuleFile) string { + var buf bytes.Buffer + for i, m := range modules { + buf.WriteString(strconv.Itoa(i)) + buf.WriteString(":\n") + buf.Write(m.Raw) + buf.WriteString("\n\n") + } + return buf.String() +} + +// NOTE(sr): we override this to not depend on build tags in tests +func wasmABIVersions(vs ...ast.WasmABIVersion) *ast.Capabilities { + caps := ast.CapabilitiesForThisVersion() + caps.WasmABIVersions = vs + return caps +} + +func TestCompilerWasmTarget(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + + p = 7 + q = p+1`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("wasm"). + WithEntrypoints("test/p", "test/q"). + WithCapabilities(wasmABIVersions(ast.WasmABIVersion{Version: 1})) + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(compiler.bundle.WasmModules) == 0 { + t.Fatal("expected to find compiled wasm module") + } + + if len(compiler.bundle.Wasm) != 0 { + t.Error("expected NOT to find deprecated bundle `Wasm` value") + } + + ensureEntrypointRemoved(t, compiler.bundle, "test/p") + }) + } +} + +// If we're building a wasm bundle, and the `opa` binary we use to do that +// does not support wasm _itself_, then it shouldn't bother. +func TestCompilerWasmTargetWithCapabilitiesUnset(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + + p = 7 + q = p+1`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("wasm"). + WithEntrypoints("test/p", "test/q") + err := compiler.Build(context.Background()) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + }) + } +} + +func TestCompilerWasmTargetWithCapabilitiesMismatch(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + + p = 7 + q = p+1`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + for note, wabis := range map[string][]ast.WasmABIVersion{ + "none": {}, + "mismatch": {{Version: 0}, {Version: 1, Minor: 2000}}, + } { + t.Run(note, func(t *testing.T) { + caps := ast.CapabilitiesForThisVersion() + caps.WasmABIVersions = wabis + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("wasm"). + WithEntrypoints("test/p", "test/q"). + WithCapabilities(caps) + err := compiler.Build(context.Background()) + if err == nil { + t.Fatal("expected err, got nil") + } + }) + } + }) + } +} + +func TestCompilerWasmTargetMultipleEntrypoints(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + + p := true`, + "policy.rego": `package policy + + authz := true`, + "mask.rego": `package system.log + import rego.v1 + + mask contains "/input/password"`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("wasm"). + WithEntrypoints("test/p", "policy/authz"). + WithCapabilities(wasmABIVersions(ast.WasmABIVersion{Version: 1})) + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(compiler.bundle.WasmModules) != 1 { + t.Fatalf("expected 1 Wasm modules, got: %d", len(compiler.bundle.WasmModules)) + } + + expManifest := bundle.Manifest{} + expManifest.Init() + expManifest.SetRegoVersion(ast.DefaultRegoVersion) + expManifest.WasmResolvers = []bundle.WasmResolver{ + { + Entrypoint: "test/p", + Module: "/policy.wasm", + }, + { + Entrypoint: "policy/authz", + Module: "/policy.wasm", + }, + } + + if !compiler.bundle.Manifest.Equal(expManifest) { + t.Fatalf("\nExpected manifest: %+v\nGot: %+v\n", expManifest, compiler.bundle.Manifest) + } + + ensureEntrypointRemoved(t, compiler.bundle, "test/p") + ensureEntrypointRemoved(t, compiler.bundle, "policy/authz") + }) + } +} + +func TestCompilerWasmTargetAnnotations(t *testing.T) { + files := map[string]string{ + "test.rego": ` +# METADATA +# title: My test package +package test + +# METADATA +# title: My P rule +# entrypoint: true +p = true`, + "policy.rego": ` +package policy + +# METADATA +# title: All my Q rules +# scope: document + +# METADATA +# title: My Q rule +q = true`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("wasm"). + WithEntrypoints("test", "policy/q"). + WithRegoAnnotationEntrypoints(true) + + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(compiler.bundle.WasmModules) != 1 { + t.Fatalf("expected 1 Wasm modules, got: %d", len(compiler.bundle.WasmModules)) + } + + expWasmResolvers := []bundle.WasmResolver{ + { + Entrypoint: "test", + Module: "/policy.wasm", + }, + { + Entrypoint: "policy/q", + Module: "/policy.wasm", + Annotations: []*ast.Annotations{ + { + Title: "All my Q rules", + Scope: "document", + }, + { + Title: "My Q rule", + Scope: "rule", + }, + }, + }, + { + Entrypoint: "test/p", + Module: "/policy.wasm", + Annotations: []*ast.Annotations{ + { + Title: "My P rule", + Scope: "document", + Entrypoint: true, + }, + }, + }, + } + + if len(expWasmResolvers) != len(compiler.bundle.Manifest.WasmResolvers) { + t.Fatalf("\nExpected WasmResolvers:\n %+v\nGot:\n %+v\n", expWasmResolvers, compiler.bundle.Manifest.WasmResolvers) + } + + for i, expWasmResolver := range expWasmResolvers { + if !expWasmResolver.Equal(&compiler.bundle.Manifest.WasmResolvers[i]) { + t.Fatalf("WasmResolver at index %v mismatch\n\nExpected WasmResolvers:\n %+v\nGot:\n %+v\n", + i, expWasmResolvers, compiler.bundle.Manifest.WasmResolvers) + } + } + }) + } +} + +func TestCompilerWasmTargetEntrypointDependents(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + import rego.v1 + + p if { q } + q if { r } + r := 1 + s := 2 + z if { r }`} + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("wasm"). + WithEntrypoints("test/r", "test/z"). + WithCapabilities(wasmABIVersions(ast.WasmABIVersion{Version: 1})) + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(compiler.bundle.WasmModules) != 1 { + t.Fatalf("expected 1 Wasm modules, got: %d", len(compiler.bundle.WasmModules)) + } + + expManifest := bundle.Manifest{} + expManifest.Init() + expManifest.SetRegoVersion(ast.DefaultRegoVersion) + expManifest.WasmResolvers = []bundle.WasmResolver{ + { + Entrypoint: "test/r", + Module: "/policy.wasm", + }, + { + Entrypoint: "test/z", + Module: "/policy.wasm", + }, + { + Entrypoint: "test/p", + Module: "/policy.wasm", + }, + { + Entrypoint: "test/q", + Module: "/policy.wasm", + }, + } + + if !compiler.bundle.Manifest.Equal(expManifest) { + t.Fatalf("\nExpected manifest: %+v\nGot: %+v\n", expManifest, compiler.bundle.Manifest) + } + + ensureEntrypointRemoved(t, compiler.bundle, "test/p") + ensureEntrypointRemoved(t, compiler.bundle, "test/q") + ensureEntrypointRemoved(t, compiler.bundle, "test/r") + }) + } +} + +func TestCompilerWasmTargetLazyCompile(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + import rego.v1 + + p if { input.x = q } + q := "foo"`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("wasm"). + WithEntrypoints("test/p"). + WithOptimizationLevel(1). + WithCapabilities(wasmABIVersions(ast.WasmABIVersion{Version: 1})) + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(compiler.bundle.WasmModules) == 0 { + t.Fatal("expected to find compiled wasm module") + } + + if _, exists := compiler.compiler.Modules["optimized/test.rego"]; !exists { + t.Fatal("expected to find optimized module on compiler") + } + + ensureEntrypointRemoved(t, compiler.bundle, "test/p") + }) + } +} + +func ensureEntrypointRemoved(t *testing.T, b *bundle.Bundle, e string) { + t.Helper() + r, err := ref.ParseDataPath(e) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + for _, mf := range b.Modules { + for _, rule := range mf.Parsed.Rules { + if rule.Path().Equal(r) { + t.Errorf("expected entrypoint to be removed from rego all modules in bundle, found rule: %s in %s", rule.Path(), mf.Path) + } + } + } +} + +func TestCompilerPlanTarget(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + + p = 7 + q = p+1`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("plan"). + WithEntrypoints("test/p", "test/q") + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(compiler.bundle.PlanModules) == 0 { + t.Fatal("expected to find compiled plan module") + } + }) + } +} + +func TestCompilerPlanTargetPruneUnused(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + import rego.v1 + p contains 1 + f(x) if { p[x] }`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("plan"). + WithEntrypoints("test"). + WithPruneUnused(true) + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(compiler.bundle.PlanModules) == 0 { + t.Fatal("expected to find compiled plan module") + } + + plan := compiler.bundle.PlanModules[0].Raw + var policy ir.Policy + + if err := json.Unmarshal(plan, &policy); err != nil { + t.Fatal(err) + } + if exp, act := 1, len(policy.Funcs.Funcs); act != exp { + t.Fatalf("expected %d funcs, got %d", exp, act) + } + f := policy.Funcs.Funcs[0] + if exp, act := "g0.data.test.p", f.Name; act != exp { + t.Fatalf("expected func named %v, got %v", exp, act) + } + }) + } +} + +func TestCompilerPlanTargetUnmatchedEntrypoints(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + + p := 7 + q := p + 1`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("plan"). + WithEntrypoints("test/p", "test/q", "test/no") + err := compiler.Build(context.Background()) + if err == nil { + t.Error("expected error from unmatched entrypoint") + } + expectError := "entrypoint \"test/no\" does not refer to a rule or policy decision" + if err.Error() != expectError { + t.Errorf("expected error %s, got: %s", expectError, err.Error()) + } + }) + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("plan"). + WithEntrypoints("foo", "foo.bar", "test/no") + err := compiler.Build(context.Background()) + if err == nil { + t.Error("expected error from unmatched entrypoints") + } + expectError := "entrypoint \"foo\" does not refer to a rule or policy decision" + if err.Error() != expectError { + t.Errorf("expected error %s, got: %s", expectError, err.Error()) + } + }) + } +} + +func TestCompilerRegoEntrypointAnnotations(t *testing.T) { + tests := []struct { + note string + entrypoints []string + modules map[string]string + data string + roots []string + wantEntrypoints map[string]struct{} + }{ + { + note: "implied document scope annotation", + entrypoints: []string{}, + modules: map[string]string{ + "test.rego": ` +package test + +import rego.v1 + +# METADATA +# entrypoint: true +p if { + q[input.x] +} + +q contains 1 +q contains 2 +q contains 3 + `, + }, + wantEntrypoints: map[string]struct{}{ + "test/p": {}, + }, + }, + { + note: "package annotation", + entrypoints: []string{}, + modules: map[string]string{ + "test.rego": ` +# METADATA +# entrypoint: true +package test + +import rego.v1 + +p if { + q[input.x] +} + +q contains 1 +q contains 2 +q contains 3 + `, + }, + wantEntrypoints: map[string]struct{}{ + "test": {}, + }, + }, + { + note: "nested rule annotation", + entrypoints: []string{}, + modules: map[string]string{ + "test.rego": ` +package test + +import rego.v1 +import data.test.nested + +p if { + q[input.x] + nested.p +} + +q contains 1 +q contains 2 +q contains 3 + `, + "test/nested.rego": ` +package test.nested + +import rego.v1 + +# METADATA +# entrypoint: true +p if { + q[input.x] +} + +q contains 1 +q contains 2 +q contains 3 + `, + }, + wantEntrypoints: map[string]struct{}{ + "test/nested/p": {}, + }, + }, + { + note: "nested package annotation", + entrypoints: []string{}, + modules: map[string]string{ + "test.rego": ` +package test + +import rego.v1 +import data.test.nested + +p if { + q[input.x] + nested.p +} + +q contains 1 +q contains 2 +q contains 3 + `, + "test/nested.rego": ` +# METADATA +# entrypoint: true +package test.nested + +import rego.v1 + +p if { + q[input.x] +} + +q contains 1 +q contains 2 +q contains 3 + `, + }, + wantEntrypoints: map[string]struct{}{ + "test/nested": {}, + }, + }, + { + note: "mixed manual entrypoints + annotation entrypoints", + entrypoints: []string{"test/p"}, + modules: map[string]string{ + "test.rego": ` +package test + +import rego.v1 +import data.test.nested + +p if { + q[input.x] + nested.p +} + +q contains 1 +q contains 2 +q contains 3 + `, + "test/nested.rego": ` +# METADATA +# entrypoint: true +package test.nested + +import rego.v1 + +p if { + q[input.x] +} + +q contains 1 +q contains 2 +q contains 3 + `, + }, + wantEntrypoints: map[string]struct{}{ + "test/nested": {}, + "test/p": {}, + }, + }, + { + note: "overlapping manual entrypoints + annotation entrypoints", + entrypoints: []string{"test/p"}, + modules: map[string]string{ + "test.rego": ` +package test + +import rego.v1 + +# METADATA +# entrypoint: true +p if { + q[input.x] +} + +q contains 1 +q contains 2 +q contains 3 + `, + }, + wantEntrypoints: map[string]struct{}{ + "test/p": {}, + }, + }, + { + note: "ref head rule annotation", + entrypoints: []string{}, + modules: map[string]string{ + "test.rego": ` +package test + +import rego.v1 + +# METADATA +# entrypoint: true +a.b.c.p if { + q[input.x] +} + +q contains 1 +q contains 2 +q contains 3 + `, + }, + wantEntrypoints: map[string]struct{}{ + "test/a/b/c/p": {}, + }, + }, + { + note: "mixed ref head rule/package annotation", + entrypoints: []string{}, + modules: map[string]string{ + "test.rego": ` +# METADATA +# entrypoint: true +package test.a.b.c + +import rego.v1 + +# METADATA +# entrypoint: true +d.e.f.g if { + q[input.x] +} + +q contains 1 +q contains 2 +q contains 3 + `, + }, + wantEntrypoints: map[string]struct{}{ + "test/a/b/c": {}, + "test/a/b/c/d/e/f/g": {}, + }, + }, + { + note: "numbers in refs annotation", + entrypoints: []string{}, + modules: map[string]string{ + "test.rego": ` +package test + +import rego.v1 + +# METADATA +# entrypoint: true +a.b[1.0] if { + q[input.x] +} + +q contains 1 +q contains 2 +q contains 3 + `, + }, + wantEntrypoints: map[string]struct{}{ + "test/a/b/1.0": {}, + }, + }, + { + note: "string path with brackets annotation", + entrypoints: []string{}, + modules: map[string]string{ + "test.rego": ` +package test + +import rego.v1 + +# METADATA +# entrypoint: true +a.b["1.0.0"].foo if { + q[input.x] +} + +q contains 1 +q contains 2 +q contains 3 + `, + }, + wantEntrypoints: map[string]struct{}{ + "test/a/b/1.0.0/foo": {}, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(tc.modules, useMemoryFS, func(root string, fsys fs.FS) { + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithTarget("plan"). + WithEntrypoints(tc.entrypoints...). + WithRegoAnnotationEntrypoints(true). + WithPruneUnused(true) + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + // Ensure we have the right number of entrypoints. + if len(compiler.entrypoints) != len(tc.wantEntrypoints) { + t.Fatalf("Wrong number of entrypoints. Expected %v, got %v.", tc.wantEntrypoints, compiler.entrypoints) + } + + // Ensure those entrypoints match the ones we expect. + for _, entrypoint := range compiler.entrypoints { + if _, found := tc.wantEntrypoints[entrypoint]; !found { + t.Fatalf("Unexpected entrypoint '%s'", entrypoint) + } + } + }) + } + }) + } +} + +func TestCompilerSetRevision(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + + p = true`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithRevision("deadbeef") + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if compiler.bundle.Manifest.Revision != "deadbeef" { + t.Fatal("expected revision to be set but got:", compiler.bundle.Manifest) + } + }) + } +} + +func TestCompilerSetMetadata(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + + p = true`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + metadata := map[string]any{"OPA version": "0.36.1"} + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithMetadata(&metadata) + + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if compiler.bundle.Manifest.Metadata["OPA version"] != "0.36.1" { + t.Fatal("expected metadata to be set but got:", compiler.bundle.Manifest) + } + }) + } +} + +func TestCompilerSetRoots(t *testing.T) { + files := map[string]string{ + "test.rego": `package test + + import data.common + + x = true`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + compiler := New(). + WithFS(fsys). + WithPaths(root). + WithRoots("test") + + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(*compiler.bundle.Manifest.Roots) != 1 || (*compiler.bundle.Manifest.Roots)[0] != "test" { + t.Fatal("expected roots to be set to ['test'] but got:", compiler.bundle.Manifest.Roots) + } + }) + } +} + +func TestCompilerOutput(t *testing.T) { + // NOTE(tsandall): must use format package here because the compiler formats. + mod := ast.MustParseModuleWithOpts(`package test + p { input.x = data.foo }`, ast.ParserOptions{RegoVersion: ast.RegoV0}) + files := map[string]string{ + "test.rego": string(format.MustAstWithOpts(mod, format.Opts{RegoVersion: ast.RegoV0})), + "data.json": `{"foo": 1}`, + ".manifest": `{"rego_version": 0}`, + } + + for _, useMemoryFS := range []bool{false, true} { + test.WithTestFS(files, useMemoryFS, func(root string, fsys fs.FS) { + + buf := bytes.NewBuffer(nil) + compiler := New(). + WithAsBundle(true). // To respect the manifest file. + WithFS(fsys). + WithPaths(root). + WithOutput(buf) + err := compiler.Build(context.Background()) + if err != nil { + t.Fatal(err) + } + + // Check that the written bundle is expected. + result, err := bundle.NewReader(buf).Read() + if err != nil { + t.Fatal(err) + } + + exp, err := loader.NewFileLoader().WithFS(fsys).AsBundle(root) + if err != nil { + t.Fatal(err) + } + + if !exp.Equal(result) { + t.Fatalf("expected-1:\n\n%+v\n\ngot-1:\n\n%+v", *exp, result) + } + + if !exp.Manifest.Equal(result.Manifest) { + t.Fatalf("expected-2:\n\n%+v\n\ngot-2:\n\n%+v", exp.Manifest, result.Manifest) + } + + // Check that the returned bundle is the expected. + compiled := compiler.Bundle() + + if !exp.Equal(*compiled) { + t.Fatalf("expected-3:\n\n%v\n\ngot-3:\n\n%v", *exp, *compiled) + } + + if !exp.Manifest.Equal(compiled.Manifest) { + t.Fatalf("expected-4:\n\n%v\n\ngot-4:\n\n%v", exp.Manifest, compiled.Manifest) + } + }) + } +} + +func TestOptimizerNoops(t *testing.T) { + tests := []struct { + note string + entrypoints []string + modules map[string]string + }{ + { + note: "recursive result", + entrypoints: []string{"data.test.foo"}, + modules: map[string]string{ + "test.rego": ` + package test.foo.bar + + p if { input.x = 1 } + `, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + o := getOptimizer(tc.modules, "", tc.entrypoints, nil, "", ast.ParserOptions{AllFutureKeywords: true}) + cpy := o.bundle.Copy() + err := o.Do(context.Background()) + if err != nil { + t.Fatal(err) + } + if !o.bundle.Equal(cpy) { + t.Fatalf("Expected no change:\n\n%v\n\nGot:\n\n%v", prettyBundle{cpy}, prettyBundle{*o.bundle}) + } + }) + } +} + +func TestOptimizerErrors(t *testing.T) { + tests := []struct { + note string + entrypoints []string + modules map[string]string + wantErr error + }{ + { + note: "undefined entrypoint", + entrypoints: []string{"data.test.p"}, + wantErr: errors.New("undefined entrypoint data.test.p"), + }, + { + note: "compile error", + entrypoints: []string{"data.test.p"}, + modules: map[string]string{ + "test.rego": ` + package test + p if { data.test.p } + `, + }, + wantErr: errors.New("1 error occurred: test.rego:3: rego_recursion_error: rule data.test.p is recursive: data.test.p -> data.test.p"), + }, + { + note: "partial eval error", + entrypoints: []string{"data.test.p"}, + modules: map[string]string{ + "test.rego": ` + package test + p if { {k: v | k = ["a", "a"][_]; v = [0, 1][_] } } + `, + }, + wantErr: errors.New("test.rego:3: eval_conflict_error: object keys must be unique"), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + o := getOptimizer(tc.modules, "", tc.entrypoints, nil, "", ast.ParserOptions{AllFutureKeywords: true}) + cpy := o.bundle.Copy() + got := o.Do(context.Background()) + if got == nil || got.Error() != tc.wantErr.Error() { + t.Fatalf("expected error to be %v but got %v", tc.wantErr, got) + } + if !o.bundle.Equal(cpy) { + t.Fatalf("Expected no change:\n\n%v\n\nGot:\n\n%v", prettyBundle{cpy}, prettyBundle{*o.bundle}) + } + }) + } +} + +func TestOptimizerOutput(t *testing.T) { + tests := []struct { + note string + entrypoints []string + modules map[string]string + data string + roots []string + namespace string + wantModules map[string]string + }{ + { + note: "rule pruning", + entrypoints: []string{"data.test.p"}, + modules: map[string]string{ + "test.rego": ` + package test + + p if { + q[input.x] + } + + q contains 1 + q contains 2 + q contains 3 + `, + }, + wantModules: map[string]string{ + "optimized/test.rego": ` + package test + + p = __result__ if { 1 = input.x; __result__ = true } + p = __result__ if { 2 = input.x; __result__ = true } + p = __result__ if { 3 = input.x; __result__ = true } + `, + "test.rego": ` + package test + + q contains 1 + q contains 2 + q contains 3 + `, + }, + }, + { + note: "support rules", + entrypoints: []string{"data.test.p"}, + modules: map[string]string{ + "test.rego": ` + package test + + default p = false + + p if { q[input.x] } + + q contains 1 + q contains 2`, + }, + wantModules: map[string]string{ + "optimized/test.rego": ` + package test + + default p = false + + p = true if { 1 = input.x } + p = true if { 2 = input.x } + + `, + "test.rego": ` + package test + + q contains 1 + q contains 2 + `, + }, + }, + { + note: "support rules, ref heads", + entrypoints: []string{"data.test.p.q.r"}, + modules: map[string]string{ + "test.rego": ` + package test + + default p.q.r = false + p.q.r if { q[input.x] } + + q contains 1 + q contains 2`, + }, + wantModules: map[string]string{ + "optimized/test/p/q.rego": ` + package test.p.q + + default r = false + r = true if { 1 = input.x } + r = true if { 2 = input.x } + + `, + "test.rego": ` + package test + + q contains 1 + q contains 2 + `, + }, + }, + { + note: "multiple entrypoints", + entrypoints: []string{"data.test.p", "data.test.r", "data.test.s"}, + modules: map[string]string{ + "test.rego": ` + package test + + p if { + q[input.x] + } + + r if { + q[input.x] + } + + s if { + q[input.x] + } + + q contains 1 + `, + }, + wantModules: map[string]string{ + "optimized/test.rego": ` + package test + + p = __result__ if { 1 = input.x; __result__ = true } + `, + "optimized/test.1.rego": ` + package test + + r = __result__ if { 1 = input.x; __result__ = true } + `, + "optimized/test.2.rego": ` + package test + + s = __result__ if { 1 = input.x; __result__ = true } + `, + "test.rego": ` + package test + + q contains 1 if { true } + `, + }, + }, + { + note: "package pruning", + entrypoints: []string{"data.test.foo"}, + modules: map[string]string{ + "test.rego": ` + package test.foo.bar + + p = true + `, + }, + wantModules: map[string]string{ + "optimized/test.rego": ` + package test + + foo = __result__ if { __result__ = {"bar": {"p": true}} }`, + }, + }, + { + note: "entrypoint dependent integrity", + entrypoints: []string{"data.test.p"}, + modules: map[string]string{ + "test.rego": ` + package test + + p if { q[input.x] } + + q contains x if { + s[x] + } + + s contains 1 + s contains 2 + + t if { + p + } + + r if { t with q as {3} } + `, + }, + wantModules: map[string]string{ + "optimized/test.1.rego": ` + package test + + p = __result__ if { data.test.q[input.x]; __result__ = true } + `, + "optimized/test.rego": ` + package test + + q contains 1 if { true } + q contains 2 if { true } + `, + "test.rego": ` + package test + + s contains 1 if { true } + s contains 2 if { true } + t if { p } + r = true if { t with q as {3} } + `, + }, + }, + { + note: "output filename safety", + entrypoints: []string{`data.test["foo bar"].p`}, + modules: map[string]string{ + "x.rego": ` + package test["foo bar"] # package does not match safe pattern so use alt. format + p if { q[input.x] } + q contains 1 + q contains 2 + `, + }, + wantModules: map[string]string{ + "optimized/partial/0/0.rego": ` + package test["foo bar"] + p = __result__ if { 1 = input.x; __result__ = true } + p = __result__ if { 2 = input.x; __result__ = true } + `, + "x.rego": ` + package test["foo bar"] + + q contains 1 + q contains 2 + `, + }, + }, + { + note: "generated package namespace", + entrypoints: []string{"data.test.p"}, + modules: map[string]string{ + "test.rego": ` + package test + + p if { not q } + q if { k[input.a]; k[input.b] } # generate a product that is not inlined + k = {1,2,3} + `, + }, + wantModules: map[string]string{ + "optimized/partial.rego": ` + package partial + + __not1_0_2__ = true if { 1 = input.a; 1 = input.b } + __not1_0_2__ = true if { 1 = input.a; 2 = input.b } + __not1_0_2__ = true if { 1 = input.a; 3 = input.b } + __not1_0_2__ = true if { 2 = input.a; 1 = input.b } + __not1_0_2__ = true if { 2 = input.a; 2 = input.b } + __not1_0_2__ = true if { 2 = input.a; 3 = input.b } + __not1_0_2__ = true if { 3 = input.a; 1 = input.b } + __not1_0_2__ = true if { 3 = input.a; 2 = input.b } + __not1_0_2__ = true if { 3 = input.a; 3 = input.b } + `, + "optimized/test.rego": ` + package test + + p = __result__ if { not data.partial.__not1_0_2__; __result__ = true } + `, + "test.rego": ` + package test + + q = true if { k[input.a]; k[input.b] } + k = {1, 2, 3} if { true } + `, + }, + }, + { + note: "configured package namespace", + entrypoints: []string{"data.test.p"}, + namespace: "custom", + modules: map[string]string{ + "test.rego": ` + package test + + p if { not q } + q if { k[input.a]; k[input.b] } # generate a product that is not inlined + k = {1,2,3} + `, + }, + wantModules: map[string]string{ + "optimized/custom.rego": ` + package custom + + __not1_0_2__ = true if { 1 = input.a; 1 = input.b } + __not1_0_2__ = true if { 1 = input.a; 2 = input.b } + __not1_0_2__ = true if { 1 = input.a; 3 = input.b } + __not1_0_2__ = true if { 2 = input.a; 1 = input.b } + __not1_0_2__ = true if { 2 = input.a; 2 = input.b } + __not1_0_2__ = true if { 2 = input.a; 3 = input.b } + __not1_0_2__ = true if { 3 = input.a; 1 = input.b } + __not1_0_2__ = true if { 3 = input.a; 2 = input.b } + __not1_0_2__ = true if { 3 = input.a; 3 = input.b } + `, + "optimized/test.rego": ` + package test + + p = __result__ if { not data.custom.__not1_0_2__; __result__ = true } + `, + "test.rego": ` + package test + + q = true if { k[input.a]; k[input.b] } + k = {1, 2, 3} if { true } + `, + }, + }, + { + note: "infer unknowns from roots", + entrypoints: []string{"data.test.p"}, + modules: map[string]string{ + "test.rego": ` + package test + + p if { + q[x] + data.external.users[x] == input.user + } + + q contains "foo" + q contains "bar" + `, + }, + roots: []string{"test"}, + wantModules: map[string]string{ + "optimized/test.rego": ` + package test + + p = __result__ if { data.external.users.bar = input.user; __result__ = true } + p = __result__ if { data.external.users.foo = input.user; __result__ = true } + `, + "test.rego": ` + package test + + q contains "foo" + q contains "bar" + `, + }, + }, + { + note: "generate rules with type violations: complete doc", + entrypoints: []string{"data.test.p"}, + modules: map[string]string{ + "test.rego": ` + package test + + p if { + x := split(input.a, ":") + f(x[0]) + } + + f(x) if { x == null } + f(x) if { startswith(x, "foo") } + `, + }, + roots: []string{"test"}, + wantModules: map[string]string{ + "optimized/test.rego": ` + package test + + p = __result__ if { split(input.a, ":", __local3__1); startswith(__local3__1[0], "foo"); __result__ = true } + `, + "test.rego": ` + package test + + f(x) if { x == null } + f(x) if { startswith(x, "foo") } + `, + }, + }, + { + note: "generate rules with type violations: partial set", + entrypoints: []string{"data.test.p"}, + modules: map[string]string{ + "test.rego": ` + package test + + p contains msg if { + x := split(input.a, ":") + f(x[0]) + msg := "test string" + } + + f(x) if { x == null } + f(x) if { startswith(x, "foo") } + `, + }, + roots: []string{"test"}, + wantModules: map[string]string{ + "optimized/test.rego": ` + package test + + p contains "test string" if { split(input.a, ":", __local4__1); startswith(__local4__1[0], "foo") } + `, + "test.rego": ` + package test + + f(x) if { x == null } + f(x) if { startswith(x, "foo") } + `, + }, + }, + { + note: "generate rules with type violations: partial object", + entrypoints: []string{"data.test.p"}, + modules: map[string]string{ + "test.rego": ` + package test + + p[k] = value if { + x := split(input.a, ":") + f(x[0]) + k := "a" + value := 1 + } + + f(x) if { x == null } + f(x) if { startswith(x, "foo") } + `, + }, + roots: []string{"test"}, + wantModules: map[string]string{ + "optimized/test/p.rego": ` + package test.p + + a = 1 if { split(input.a, ":", __local5__1); startswith(__local5__1[0], "foo") } + `, + "test.rego": ` + package test + + f(x) if { x == null } + f(x) if { startswith(x, "foo") } + `, + }, + }, + { + note: "generate rules with type violations: negation", + entrypoints: []string{"data.test.p"}, + modules: map[string]string{ + "test.rego": ` + package test + + p if { not q } + q if { + x := split(input.a, ":") + f(x[0]) + } + f(x) if { x == null } + f(x) if { startswith(x, "foo") } + `, + }, + roots: []string{"test"}, + wantModules: map[string]string{ + "optimized/test.rego": ` + package test + + p = __result__ if { not data.partial.__not1_0_2__; __result__ = true } + `, + "test.rego": ` + package test + q = true if { assign(x, split(input.a, ":")); f(x[0]) } + f(x) if { x == null } + f(x) if { startswith(x, "foo") } + `, + "optimized/partial.rego": ` + package partial + __not1_0_2__ = true if { split(input.a, ":", __local3__3); startswith(__local3__3[0], "foo") } + `, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + + popts := ast.ParserOptions{AllFutureKeywords: true} + o := getOptimizer(tc.modules, tc.data, tc.entrypoints, tc.roots, tc.namespace, popts) + original := o.bundle.Copy() + err := o.Do(context.Background()) + if err != nil { + t.Fatal(err) + } + + exp := &bundle.Bundle{ + Modules: getModuleFiles(tc.wantModules, false, popts), + Data: original.Data, // data is not pruned at all today + } + + if len(tc.roots) > 0 { + exp.Manifest.Roots = &tc.roots + + // optimizer will add the manifest root in the optimized bundle automatically + if tc.namespace != "" { + exp.Manifest.AddRoot(tc.namespace) + } else { + exp.Manifest.AddRoot("partial") + } + } + + exp.Manifest.Revision = "" // optimizations must reset the revision. + + if !exp.Equal(*o.bundle) { + t.Errorf("Expected:\n\n%v\n\nGot:\n\n%v", prettyBundle{*exp}, prettyBundle{*o.bundle}) + } + + if !o.bundle.Manifest.Equal(exp.Manifest) { + t.Errorf("Expected manifest: %v\n\nGot manifest: %v", exp.Manifest, o.bundle.Manifest) + } + }) + } +} + +func TestOptimizerError(t *testing.T) { + tests := []struct { + note string + roots []string + entrypoints []string + modules map[string]string + expErr string + }{ + { + // Regression test for https://github.com/open-policy-agent/opa/issues/7321 + note: "short entrypoint ref", + roots: []string{"test"}, + entrypoints: []string{"data.test"}, + modules: map[string]string{ + "test.rego": ` + package test + p = true`, + }, + expErr: `invalid entrypoint data.test: to create optimized support module, the entrypoint ref must have at least two components in addition to the 'data' root`, + }, + { + // Regression test for https://github.com/open-policy-agent/opa/issues/7321 + note: "data only entrypoint ref", + roots: []string{"test"}, + entrypoints: []string{"data"}, + modules: map[string]string{ + "test.rego": ` + package test + p = true`, + }, + expErr: `invalid entrypoint data: to create optimized support module, the entrypoint ref must have at least two components in addition to the 'data' root`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + popts := ast.ParserOptions{AllFutureKeywords: true} + o := getOptimizer(tc.modules, "", tc.entrypoints, tc.roots, "", popts) + + err := o.Do(context.Background()) + if err == nil { + t.Fatal("expected error but got nil") + } + if err.Error() != tc.expErr { + t.Fatalf("expected error to be:\n\n%v\n\nbut got:\n\n%v", tc.expErr, err) + } + }) + } +} + +func TestRefSet(t *testing.T) { + rs := newRefSet(ast.MustParseRef("input"), ast.MustParseRef("data.foo.bar")) + + expFound := []string{ + "input", + "input.foo", + "data.foo.bar", + "data.foo.bar.baz", + "data.foo.bar[1]", + } + + for _, exp := range expFound { + if !rs.ContainsPrefix(ast.MustParseRef(exp)) { + t.Fatal("expected to find:", exp) + } + } + + expNotFound := []string{ + "x.bar", + "data", + "data.bar", + "data.foo", + } + + for _, exp := range expNotFound { + if rs.ContainsPrefix(ast.MustParseRef(exp)) { + t.Fatal("expected not to find:", exp) + } + } + + rs.AddPrefix(ast.MustParseRef("data.foo")) + + if !rs.ContainsPrefix(ast.MustParseRef("data.foo")) { + t.Fatal("expected to find data.foo after adding to set") + } + + sorted := rs.Sorted() + + if len(sorted) != 2 || !sorted[0].Equal(ast.MustParseTerm("data.foo")) || !sorted[1].Equal(ast.MustParseTerm("input")) { + t.Fatal("expected 2 prefixes (data.foo and input) but got:", sorted) + } + + // The prefixes should not be affected (because data.foo already exists). + rs.AddPrefix(ast.MustParseRef("data.foo.qux")) + sorted = rs.Sorted() + + if len(sorted) != 2 || !sorted[0].Equal(ast.MustParseTerm("data.foo")) || !sorted[1].Equal(ast.MustParseTerm("input")) { + t.Fatal("expected 2 prefixes (data.foo and input) but got:", sorted) + } + +} + +func getOptimizer(modules map[string]string, data string, entries []string, roots []string, ns string, popts ast.ParserOptions) *optimizer { + + b := &bundle.Bundle{ + Modules: getModuleFiles(modules, true, popts), + } + + if data != "" { + b.Data = util.MustUnmarshalJSON([]byte(data)).(map[string]any) + } + + if len(roots) > 0 { + b.Manifest.Roots = &roots + } + + b.Manifest.Init() + b.Manifest.Revision = "DEADBEEF" // ensures that the manifest revision is getting reset + entrypoints := make([]*ast.Term, len(entries)) + + for i := range entrypoints { + entrypoints[i] = ast.MustParseTerm(entries[i]) + } + + o := newOptimizer(ast.CapabilitiesForThisVersion(), b). + WithEntrypoints(entrypoints) + + if ns != "" { + o = o.WithPartialNamespace(ns) + } + + o.resultsymprefix = "" + + return o +} + +func getModuleFiles(src map[string]string, includeRaw bool, popts ast.ParserOptions) []bundle.ModuleFile { + + keys := util.KeysSorted(src) + modules := make([]bundle.ModuleFile, 0, len(keys)) + + for _, k := range keys { + module, err := ast.ParseModuleWithOpts(k, src[k], popts) + if err != nil { + panic(err) + } + modules = append(modules, bundle.ModuleFile{ + Parsed: module, + Path: k, + URL: k, + }) + if includeRaw { + modules[len(modules)-1].Raw = []byte(src[k]) + } + } + + return modules +} + +type prettyBundle struct { + bundle.Bundle +} + +func (p prettyBundle) String() string { + + buf := []string{fmt.Sprintf("%d module(s) (hiding data):", len(p.Modules)), ""} + + for _, mf := range p.Modules { + buf = append(buf, "#") + buf = append(buf, fmt.Sprintf("# Module: %q", mf.Path)) + buf = append(buf, "#") + buf = append(buf, mf.Parsed.String()) + buf = append(buf, "") + } + + return strings.Join(buf, "\n") +} diff --git a/third_party/opa/v1/config/config.go b/third_party/opa/v1/config/config.go new file mode 100644 index 000000000000..62bfc65537f4 --- /dev/null +++ b/third_party/opa/v1/config/config.go @@ -0,0 +1,260 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package config implements OPA configuration file parsing and validation. +package config + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "reflect" + "sort" + "strings" + + "github.com/open-policy-agent/opa/internal/ref" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/version" +) + +// Config represents the configuration file that OPA can be started with. +type Config struct { + Services json.RawMessage `json:"services,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + Discovery json.RawMessage `json:"discovery,omitempty"` + Bundle json.RawMessage `json:"bundle,omitempty"` // Deprecated: Use `bundles` instead + Bundles json.RawMessage `json:"bundles,omitempty"` + DecisionLogs json.RawMessage `json:"decision_logs,omitempty"` + Status json.RawMessage `json:"status,omitempty"` + Plugins map[string]json.RawMessage `json:"plugins,omitempty"` + Keys json.RawMessage `json:"keys,omitempty"` + DefaultDecision *string `json:"default_decision,omitempty"` + DefaultAuthorizationDecision *string `json:"default_authorization_decision,omitempty"` + Caching json.RawMessage `json:"caching,omitempty"` + NDBuiltinCache bool `json:"nd_builtin_cache,omitempty"` + PersistenceDirectory *string `json:"persistence_directory,omitempty"` + DistributedTracing json.RawMessage `json:"distributed_tracing,omitempty"` + Server *struct { + Encoding json.RawMessage `json:"encoding,omitempty"` + Decoding json.RawMessage `json:"decoding,omitempty"` + Metrics json.RawMessage `json:"metrics,omitempty"` + } `json:"server,omitempty"` + Storage *struct { + Disk json.RawMessage `json:"disk,omitempty"` + } `json:"storage,omitempty"` + Extra map[string]json.RawMessage `json:"-"` +} + +// ParseConfig returns a valid Config object with defaults injected. The id +// and version parameters will be set in the labels map. +func ParseConfig(raw []byte, id string) (*Config, error) { + // NOTE(sr): based on https://stackoverflow.com/a/33499066/993018 + var result Config + objValue := reflect.ValueOf(&result).Elem() + knownFields := map[string]reflect.Value{} + for i := 0; i != objValue.NumField(); i++ { + jsonName := strings.Split(objValue.Type().Field(i).Tag.Get("json"), ",")[0] + knownFields[jsonName] = objValue.Field(i) + } + + if err := util.Unmarshal(raw, &result.Extra); err != nil { + return nil, err + } + + for key, chunk := range result.Extra { + if field, found := knownFields[key]; found { + if err := util.Unmarshal(chunk, field.Addr().Interface()); err != nil { + return nil, err + } + delete(result.Extra, key) + } + } + if len(result.Extra) == 0 { + result.Extra = nil + } + return &result, result.validateAndInjectDefaults(id) +} + +// PluginNames returns a sorted list of names of enabled plugins. +func (c Config) PluginNames() (result []string) { + if c.Bundle != nil || c.Bundles != nil { + result = append(result, "bundles") + } + if c.Status != nil { + result = append(result, "status") + } + if c.DecisionLogs != nil { + result = append(result, "decision_logs") + } + for name := range c.Plugins { + result = append(result, name) + } + sort.Strings(result) + return result +} + +// PluginsEnabled returns true if one or more plugin features are enabled. +// +// Deprecated: Use PluginNames instead. +func (c Config) PluginsEnabled() bool { + return c.Bundle != nil || c.Bundles != nil || c.DecisionLogs != nil || c.Status != nil || len(c.Plugins) > 0 +} + +// DefaultDecisionRef returns the default decision as a reference. +func (c Config) DefaultDecisionRef() ast.Ref { + r, _ := ref.ParseDataPath(*c.DefaultDecision) + return r +} + +// DefaultAuthorizationDecisionRef returns the default authorization decision +// as a reference. +func (c Config) DefaultAuthorizationDecisionRef() ast.Ref { + r, _ := ref.ParseDataPath(*c.DefaultAuthorizationDecision) + return r +} + +// NDBuiltinCacheEnabled returns if the ND builtins cache should be used. +func (c Config) NDBuiltinCacheEnabled() bool { + return c.NDBuiltinCache +} + +func (c *Config) validateAndInjectDefaults(id string) error { + + if c.DefaultDecision == nil { + s := defaultDecisionPath + c.DefaultDecision = &s + } + + _, err := ref.ParseDataPath(*c.DefaultDecision) + if err != nil { + return err + } + + if c.DefaultAuthorizationDecision == nil { + s := defaultAuthorizationDecisionPath + c.DefaultAuthorizationDecision = &s + } + + _, err = ref.ParseDataPath(*c.DefaultAuthorizationDecision) + if err != nil { + return err + } + + if c.Labels == nil { + c.Labels = map[string]string{} + } + + c.Labels["id"] = id + c.Labels["version"] = version.Version + + return nil +} + +// GetPersistenceDirectory returns the configured persistence directory, or $PWD/.opa if none is configured +func (c Config) GetPersistenceDirectory() (string, error) { + if c.PersistenceDirectory == nil { + pwd, err := os.Getwd() + if err != nil { + return "", err + } + return filepath.Join(pwd, ".opa"), nil + } + return *c.PersistenceDirectory, nil +} + +// ActiveConfig returns OPA's active configuration +// with the credentials and crypto keys removed +func (c *Config) ActiveConfig() (any, error) { + bs, err := json.Marshal(c) + if err != nil { + return nil, err + } + + var result map[string]any + if err := util.UnmarshalJSON(bs, &result); err != nil { + return nil, err + } + for k, e := range c.Extra { + var v any + if err := util.UnmarshalJSON(e, &v); err != nil { + return nil, err + } + result[k] = v + } + + if err := removeServiceCredentials(result["services"]); err != nil { + return nil, err + } + + if err := removeCryptoKeys(result["keys"]); err != nil { + return nil, err + } + + return result, nil +} + +func removeServiceCredentials(x any) error { + switch x := x.(type) { + case nil: + return nil + case []any: + for _, v := range x { + err := removeKey(v, "credentials") + if err != nil { + return err + } + } + + case map[string]any: + for _, v := range x { + err := removeKey(v, "credentials") + if err != nil { + return err + } + } + default: + return fmt.Errorf("illegal service config type: %T", x) + } + + return nil +} + +func removeCryptoKeys(x any) error { + switch x := x.(type) { + case nil: + return nil + case map[string]any: + for _, v := range x { + err := removeKey(v, "key", "private_key") + if err != nil { + return err + } + } + default: + return fmt.Errorf("illegal keys config type: %T", x) + } + + return nil +} + +func removeKey(x any, keys ...string) error { + val, ok := x.(map[string]any) + if !ok { + return errors.New("type assertion error") + } + + for _, key := range keys { + delete(val, key) + } + + return nil +} + +const ( + defaultDecisionPath = "/system/main" + defaultAuthorizationDecisionPath = "/system/authz/allow" +) diff --git a/third_party/opa/v1/config/config_test.go b/third_party/opa/v1/config/config_test.go new file mode 100644 index 000000000000..eb25bd8bd4c4 --- /dev/null +++ b/third_party/opa/v1/config/config_test.go @@ -0,0 +1,440 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package config + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "reflect" + "slices" + "testing" + + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/version" +) + +func TestConfigPluginNames(t *testing.T) { + tests := []struct { + name string + conf Config + expected []string + }{ + { + name: "empty config", + conf: Config{}, + expected: nil, + }, + { + name: "bundle", + conf: Config{ + Bundle: []byte(`{"bundle": {"name": "test-bundle"}}`), + }, + expected: []string{"bundles"}, + }, + { + name: "bundles", + conf: Config{ + Bundles: []byte(`{"bundles": {"test-bundle": {}}`), + }, + expected: []string{"bundles"}, + }, + { + name: "decision_logs", + conf: Config{ + DecisionLogs: []byte(`{decision_logs: {}}`), + }, + expected: []string{"decision_logs"}, + }, + { + name: "status", + conf: Config{ + Status: []byte(`{status: {}}`), + }, + expected: []string{"status"}, + }, + { + name: "plugins", + conf: Config{ + Plugins: map[string]json.RawMessage{ + "some-plugin": {}, + }, + }, + expected: []string{"some-plugin"}, + }, + { + name: "sorted", + conf: Config{ + DecisionLogs: []byte(`{decision_logs: {}}`), + Status: []byte(`{status: {}}`), + }, + expected: []string{"decision_logs", "status"}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual := test.conf.PluginNames() + if !slices.Equal(actual, test.expected) { + t.Errorf("Expected %v but got %v", test.expected, actual) + } + }) + } +} + +func TestConfigPluginsEnabled(t *testing.T) { + tests := []struct { + name string + conf Config + expected bool + }{ + { + name: "empty config", + conf: Config{}, + expected: false, + }, + { + name: "bundle", + conf: Config{ + Bundle: []byte(`{"bundle": {"name": "test-bundle"}}`), + }, + expected: true, + }, + { + name: "bundles", + conf: Config{ + Bundles: []byte(`{"bundles": {"test-bundle": {}}`), + }, + expected: true, + }, + { + name: "decision_logs", + conf: Config{ + DecisionLogs: []byte(`{decision_logs: {}}`), + }, + expected: true, + }, + { + name: "status", + conf: Config{ + Status: []byte(`{status: {}}`), + }, + expected: true, + }, + { + name: "plugins", + conf: Config{ + Plugins: map[string]json.RawMessage{ + "some-plugin": {}, + }, + }, + expected: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual := test.conf.PluginsEnabled() + if actual != test.expected { + t.Errorf("Expected %t but got %t", test.expected, actual) + } + }) + } +} + +func TestPersistDirectory(t *testing.T) { + pwd, err := os.Getwd() + if err != nil { + t.Fatalf("%v", err) + } + + c := Config{} + persistDir, err := c.GetPersistenceDirectory() + if err != nil { + t.Fatalf("%v", err) + } + + if persistDir != filepath.Join(pwd, ".opa") { + t.Errorf("expected persistDir to be %v, got %v", filepath.Join(pwd, ".opa"), persistDir) + } + + dir := "/var/opa" + c.PersistenceDirectory = &dir + persistDir, err = c.GetPersistenceDirectory() + if err != nil { + t.Fatalf("%v", err) + } + + if persistDir != dir { + t.Errorf("expected peristDir %v and dir %v to be equal", persistDir, dir) + } +} + +func TestActiveConfig(t *testing.T) { + + common := `"labels": { + "region": "west" + }, + "keys": { + "global_key": { + "algorithm": HS256, + "key": "secret" + }, + "local_key": { + "private_key": "some_private_key" + } + }, + "decision_logs": { + "service": "acmecorp", + "reporting": { + "min_delay_seconds": 300, + "max_delay_seconds": 600 + } + }, + "plugins": { + "some-plugin": {} + }, + "server": { + "decoding": { + "max_length": 134217728, + "gzip": { + "max_length": 268435456 + } + }, + "encoding": { + "gzip": { + "min_length": 1024, + "compression_level": 1 + } + }, + "metrics": { + "prom": { + "http_request_duration_seconds": { + "buckets": [0.1, 0.2] + } + } + } + }, + "discovery": {"name": "config"}` + + serviceObj := `"services": { + "acmecorp": { + "url": "https://example.com/control-plane-api/v1", + "response_header_timeout_seconds": 5, + "headers": {"foo": "bar"}, + "credentials": {"bearer": {"token": "test"}} + }, + "opa.example.com": { + "url": "https://opa.example.com", + "headers": {"foo": "bar"}, + "credentials": {"gcp_metadata": {"audience": "test"}} + } + },` + + servicesList := `"services": [ + { + "name": "acmecorp", + "url": "https://example.com/control-plane-api/v1", + "response_header_timeout_seconds": 5, + "headers": {"foo": "bar"}, + "credentials": {"bearer": {"token": "test"}} + }, + { + "name": "opa.example.com", + "url": "https://opa.example.com", + "headers": {"foo": "bar"}, + "credentials": {"gcp_metadata": {"audience": "test"}} + } + ],` + + expectedCommon := fmt.Sprintf(`"labels": { + "id": "foo", + "version": %v, + "region": "west" + }, + "keys": { + "global_key": { + "algorithm": HS256 + }, + "local_key": {} + }, + "decision_logs": { + "service": "acmecorp", + "reporting": { + "min_delay_seconds": 300, + "max_delay_seconds": 600 + } + }, + "plugins": { + "some-plugin": {} + }, + "server": { + "decoding": { + "max_length": 134217728, + "gzip": { + "max_length": 268435456 + } + }, + "encoding": { + "gzip": { + "min_length": 1024, + "compression_level": 1 + } + }, + "metrics": { + "prom": { + "http_request_duration_seconds": { + "buckets": [0.1, 0.2] + } + } + } + }, + "default_authorization_decision": "/system/authz/allow", + "default_decision": "/system/main", + "discovery": {"name": "config"}`, version.Version) + + expectedServiceObj := `"services": { + "acmecorp": { + "url": "https://example.com/control-plane-api/v1", + "response_header_timeout_seconds": 5, + "headers": {"foo": "bar"} + }, + "opa.example.com": { + "url": "https://opa.example.com", + "headers": {"foo": "bar"} + } + },` + + expectedServicesList := `"services": [ + { + "name": "acmecorp", + "url": "https://example.com/control-plane-api/v1", + "response_header_timeout_seconds": 5, + "headers": {"foo": "bar"} + }, + { + "name": "opa.example.com", + "url": "https://opa.example.com", + "headers": {"foo": "bar"} + } + ],` + + badKeysConfig := []byte(`{ + "keys": [ + { + "algorithm": "HS256" + } + ] + }`) + + badServicesConfig := []byte(`{ + "services": { + "acmecorp": ["foo"] + } + }`) + + tests := map[string]struct { + raw []byte + expected []byte + wantErr bool + err error + }{ + "valid_config_with_svc_object": { + fmt.Appendf(nil, `{ %v %v }`, serviceObj, common), + fmt.Appendf(nil, `{ %v %v }`, expectedServiceObj, expectedCommon), + false, + nil, + }, + "valid_config_with_svc_list": { + fmt.Appendf(nil, `{ %v %v }`, servicesList, common), + fmt.Appendf(nil, `{ %v %v }`, expectedServicesList, expectedCommon), + false, + nil, + }, + "invalid_config_with_bad_keys": { + badKeysConfig, + nil, + true, + errors.New("illegal keys config type: []interface {}"), + }, + "invalid_config_with_bad_creds": { + badServicesConfig, + nil, + true, + errors.New("type assertion error"), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + conf, err := ParseConfig(tc.raw, "foo") + if err != nil { + t.Fatal(err) + } + + actual, err := conf.ActiveConfig() + + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else { + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + var expected map[string]any + if err := util.Unmarshal(tc.expected, &expected); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("want %v got %v", expected, actual) + } + } + }) + } + +} + +func TestExtraConfigFieldsRoundtrip(t *testing.T) { + raw := []byte(` +decision_logger: + console: true +foo: baz +bar: + really: yes!`) + conf, err := ParseConfig(raw, "id") + if err != nil { + t.Fatal(err) + } + + actual, err := conf.ActiveConfig() + if err != nil { + t.Fatal(err) + } + + expected := map[string]any{ + "foo": "baz", + "bar": map[string]any{"really": "yes!"}, + "decision_logger": map[string]any{"console": true}, + "default_authorization_decision": "/system/authz/allow", + "default_decision": "/system/main", + "labels": map[string]any{ + "id": "id", + "version": version.Version, + }, + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("want %v got %v", expected, actual) + } +} diff --git a/third_party/opa/v1/cover/cover.go b/third_party/opa/v1/cover/cover.go new file mode 100644 index 000000000000..00c8655befaf --- /dev/null +++ b/third_party/opa/v1/cover/cover.go @@ -0,0 +1,309 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package cover reports coverage on modules. +package cover + +import ( + "bytes" + "fmt" + "slices" + "sync" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/util" +) + +// Cover computes and reports on coverage. +type Cover struct { + mu sync.Mutex + hits map[string]map[Position]struct{} +} + +// New returns a new Cover object. +func New() *Cover { + return &Cover{ + hits: map[string]map[Position]struct{}{}, + } +} + +// Enabled returns true if coverage is enabled. +func (*Cover) Enabled() bool { + return true +} + +// Config returns the standard Tracer configuration for the Cover tracer +func (*Cover) Config() topdown.TraceConfig { + return topdown.TraceConfig{ + PlugLocalVars: false, // Event variable metadata is not required for the Coverage report + } +} + +// Report returns a coverage Report for the given modules. +func (c *Cover) Report(modules map[string]*ast.Module) (report Report) { + report.Files = map[string]*FileReport{} + for file, hits := range c.hits { + covered := make(PositionSlice, 0, len(hits)) + for pos := range hits { + covered = append(covered, pos) + } + covered.Sort() + fr, ok := report.Files[file] + if !ok { + fr = &FileReport{} + report.Files[file] = fr + } + fr.Covered = sortedPositionSliceToRangeSlice(covered) + } + for file, module := range modules { + notCovered := PositionSlice{} + ast.WalkRules(module, func(x *ast.Rule) bool { + if hasFileLocation(x.Head.Location) { + if !report.IsCovered(x.Location.File, x.Location.Row) { + notCovered = append(notCovered, Position{x.Head.Location.Row}) + } + } + return false + }) + ast.WalkExprs(module, func(x *ast.Expr) bool { + if includeExprInCoverage(x) { + if !report.IsCovered(x.Location.File, x.Location.Row) { + notCovered = append(notCovered, Position{x.Location.Row}) + } + } + return false + }) + notCovered.Sort() + fr, ok := report.Files[file] + if !ok { + fr = &FileReport{} + report.Files[file] = fr + } + fr.NotCovered = sortedPositionSliceToRangeSlice(notCovered) + } + + var coveredLoc, notCoveredLoc int + var overallCoverage float64 + + for _, fr := range report.Files { + fr.Coverage = fr.computeCoveragePercentage() + fr.CoveredLines = fr.locCovered() + fr.NotCoveredLines = fr.locNotCovered() + coveredLoc += fr.CoveredLines + notCoveredLoc += fr.NotCoveredLines + } + totalLoc := coveredLoc + notCoveredLoc + + if totalLoc != 0 { + overallCoverage = 100.0 * float64(coveredLoc) / float64(totalLoc) + } + report.CoveredLines = coveredLoc + report.NotCoveredLines = notCoveredLoc + report.Coverage = overallCoverage + + return +} + +// Trace updates the coverage state. +// Deprecated: Use TraceEvent instead. +func (c *Cover) Trace(event *topdown.Event) { + c.TraceEvent(*event) +} + +// TraceEvent updates the coverage state. +func (c *Cover) TraceEvent(event topdown.Event) { + switch event.Op { + case topdown.ExitOp: + if rule, ok := event.Node.(*ast.Rule); ok { + c.setHit(rule.Head.Location) + } + case topdown.EvalOp: + if expr := event.Node.(*ast.Expr); expr != nil { + c.setHit(expr.Location) + } + } +} + +func (c *Cover) setHit(loc *ast.Location) { + if hasFileLocation(loc) { + c.mu.Lock() + defer c.mu.Unlock() + hits, ok := c.hits[loc.File] + if !ok { + hits = map[Position]struct{}{} + c.hits[loc.File] = hits + } + hits[Position{loc.Row}] = struct{}{} + } +} + +// Position represents a file location. +type Position struct { + Row int `json:"row"` +} + +// PositionSlice is a collection of position that can be sorted. +type PositionSlice []Position + +// Sort sorts the slice by line number. +func (sl PositionSlice) Sort() { + slices.SortFunc(sl, func(a, b Position) int { + return a.Row - b.Row + }) +} + +// Range represents a range of positions in a file. +type Range struct { + Start Position `json:"start"` + End Position `json:"end"` +} + +// In returns true if the row is inside the range. +func (r Range) In(row int) bool { + return row >= r.Start.Row && row <= r.End.Row +} + +// FileReport represents a coverage report for a single file. +type FileReport struct { + Covered []Range `json:"covered,omitempty"` + NotCovered []Range `json:"not_covered,omitempty"` + CoveredLines int `json:"covered_lines,omitempty"` + NotCoveredLines int `json:"not_covered_lines,omitempty"` + Coverage float64 `json:"coverage,omitempty"` +} + +// IsCovered returns true if the row is marked as covered in the report. +func (fr *FileReport) IsCovered(row int) bool { + if fr == nil { + return false + } + for _, r := range fr.Covered { + if r.In(row) { + return true + } + } + return false +} + +// IsNotCovered returns true if the row is marked as NOT covered in the report. +// This is not the same as simply not being reported. For example, certain +// statements like imports are not included in the report. +func (fr *FileReport) IsNotCovered(row int) bool { + if fr == nil { + return false + } + for _, r := range fr.NotCovered { + if r.In(row) { + return true + } + } + return false +} + +// locCovered returns the number of lines of code covered by tests +func (fr *FileReport) locCovered() (loc int) { + for _, r := range fr.Covered { + loc += r.End.Row - r.Start.Row + 1 + } + return +} + +// locNotCovered returns the number of lines of code not covered by tests +func (fr *FileReport) locNotCovered() (loc int) { + for _, r := range fr.NotCovered { + loc += r.End.Row - r.Start.Row + 1 + } + return +} + +// computeCoveragePercentage returns the code coverage percentage of the file +func (fr *FileReport) computeCoveragePercentage() float64 { + coveredLoc := fr.locCovered() + notCoveredLoc := fr.locNotCovered() + totalLoc := coveredLoc + notCoveredLoc + + if totalLoc == 0 { + return 0.0 + } + + return 100.0 * float64(coveredLoc) / float64(totalLoc) +} + +// Report represents a coverage report for a set of files. +type Report struct { + Files map[string]*FileReport `json:"files"` + CoveredLines int `json:"covered_lines"` + NotCoveredLines int `json:"not_covered_lines"` + Coverage float64 `json:"coverage"` +} + +// IsCovered returns true if the row in the given file is covered. +func (r Report) IsCovered(file string, row int) bool { + return r.Files[file].IsCovered(row) +} + +// CoverageThresholdError represents an error raised when the global +// code coverage percentage is lower than the specified threshold. +type CoverageThresholdError struct { + Coverage float64 + Threshold float64 + Report *Report +} + +func (e *CoverageThresholdError) Error() string { + var buffer bytes.Buffer + buffer.WriteString(fmt.Sprintf( + "Code coverage threshold not met: got %.2f instead of %.2f", + e.Coverage, + e.Threshold)) + + if e.Report != nil && len(e.Report.Files) > 0 { + buffer.WriteString("\nLines not covered:") + + for _, file := range util.KeysSorted(e.Report.Files) { + report := e.Report.Files[file] + for _, r := range report.NotCovered { + if r.Start.Row == r.End.Row { + buffer.WriteString(fmt.Sprintf("\n\t%s:%d", file, r.Start.Row)) + } else { + buffer.WriteString(fmt.Sprintf("\n\t%s:%d-%d", file, r.Start.Row, r.End.Row)) + } + } + } + } + + return buffer.String() +} + +func sortedPositionSliceToRangeSlice(sorted []Position) (result []Range) { + if len(sorted) == 0 { + return + } + start, end := sorted[0], sorted[0] + for i := 1; i < len(sorted); i++ { + curr := sorted[i] + switch { + case curr.Row == end.Row: // skip + case curr.Row == end.Row+1: + end = curr + default: + result = append(result, Range{start, end}) + start, end = curr, curr + } + } + result = append(result, Range{start, end}) + return +} + +func hasFileLocation(loc *ast.Location) bool { + return loc != nil && loc.File != "" +} + +// Check the expression and return true if it should be included in the coverage report +func includeExprInCoverage(x *ast.Expr) bool { + _, excludeExprType := x.Terms.(*ast.SomeDecl) + + return !excludeExprType && hasFileLocation(x.Location) +} diff --git a/third_party/opa/v1/cover/cover_bench_test.go b/third_party/opa/v1/cover/cover_bench_test.go new file mode 100644 index 000000000000..9acdb42649cd --- /dev/null +++ b/third_party/opa/v1/cover/cover_bench_test.go @@ -0,0 +1,74 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cover + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/rego" +) + +func BenchmarkCoverBigLocalVar(b *testing.B) { + iterations := []int{1, 100, 1000} + vars := []int{1, 10} + + for _, iterationCount := range iterations { + for _, varCount := range vars { + name := fmt.Sprintf("%dVars%dIterations", varCount, iterationCount) + b.Run(name, func(b *testing.B) { + module := generateModule(varCount, iterationCount) + + if _, err := ast.ParseModule("test.rego", module); err != nil { + b.Fatal(err) + } + + ctx := context.Background() + + pq, err := rego.New( + rego.Module("test.rego", module), + rego.Query("data.test.p"), + ).PrepareForEval(ctx) + + if err != nil { + b.Fatal(err) + } + + cover := New() + + b.ResetTimer() + + for range b.N { + if _, err = pq.Eval(ctx, rego.EvalQueryTracer(cover)); err != nil { + b.Fatal(err) + } + } + }) + } + } +} + +func generateModule(numVars int, dataSize int) string { + sb := strings.Builder{} + sb.WriteString(`package test + +p if { + x := a + v := x[i] +`) + for i := range numVars { + sb.WriteString(fmt.Sprintf("\tv%d := x[i+%d]\n", i, i)) + } + sb.WriteString("\tfalse\n}\n") + sb.WriteString("\na := [\n") + for i := range dataSize { + sb.WriteString(fmt.Sprintf("\t%d,\n", i)) + } + sb.WriteString("]\n") + return sb.String() +} diff --git a/third_party/opa/v1/cover/cover_test.go b/third_party/opa/v1/cover/cover_test.go new file mode 100644 index 000000000000..596f7a502631 --- /dev/null +++ b/third_party/opa/v1/cover/cover_test.go @@ -0,0 +1,246 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cover + +import ( + "context" + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/topdown" +) + +func TestCover(t *testing.T) { + + cover := New() + + module := `package test + +import data.deadbeef # expect not reported + +foo if { + bar + p + not baz +} + +bar if { + a := 1 + b := 2 + a != b +} + +baz if { # expect no exit + true + false # expect eval but fail + true # expect not covered +} + +p if { + some bar # should not be included in coverage report + bar = 1 + bar + 1 == 2 +} +` + + parsedModule, err := ast.ParseModuleWithOpts("test.rego", module, ast.ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatal(err) + } + + eval := rego.New( + rego.ParsedModule(parsedModule), + rego.Query("data.test.foo"), + rego.QueryTracer(cover), + ) + + ctx := context.Background() + _, err = eval.Eval(ctx) + + if err != nil { + t.Fatal(err) + } + + report := cover.Report(map[string]*ast.Module{ + "test.rego": parsedModule, + }) + + fr, ok := report.Files["test.rego"] + if !ok { + t.Fatal("Expected file report for test.rego") + } + + expectedCovered := []Position{ + {5}, // foo head + {6}, {7}, {8}, // foo body + {11}, // bar head + {12}, {13}, {14}, // bar body + {18}, {19}, // baz body hits + {23}, // p head + {25}, {26}, // p body + } + + expectedNotCovered := []Position{ + {17}, // baz head + {20}, // baz body miss + } + + for _, exp := range expectedCovered { + if !fr.IsCovered(exp.Row) { + t.Errorf("Expected %v to be covered", exp) + } + } + + for _, exp := range expectedNotCovered { + if !fr.IsNotCovered(exp.Row) { + t.Errorf("Expected %v to NOT be covered", exp) + } + } + + if len(expectedCovered) != fr.locCovered() { + t.Errorf( + "Expected %d loc to be covered, got %d instead", + len(expectedCovered), + fr.locCovered()) + } + + if len(expectedNotCovered) != fr.locNotCovered() { + t.Errorf( + "Expected %d loc to not be covered, got %d instead", + len(expectedNotCovered), + fr.locNotCovered()) + } + + expectedCoveragePercentage := 100.0 * float64(len(expectedCovered)) / float64(len(expectedCovered)+len(expectedNotCovered)) + if expectedCoveragePercentage != fr.Coverage { + t.Errorf("Expected coverage %v != %v", expectedCoveragePercentage, fr.Coverage) + } + + // there's just one file, hence the overall coverage is equal to the + // one of the only file report we have + if expectedCoveragePercentage != report.Coverage { + t.Errorf("Expected report coverage %f != %f", + expectedCoveragePercentage, + report.Coverage) + } + + if t.Failed() { + bs, err := json.MarshalIndent(fr, "", " ") + if err != nil { + t.Fatal(err) + } + fmt.Println(string(bs)) + } +} + +func TestCoverNoDuplicates(t *testing.T) { + + cover := New() + + module := `package test + +# Both a rule and an expression, but should not be counted twice +foo := 1 + +allow if { true } +` + + parsedModule, err := ast.ParseModuleWithOpts("test.rego", module, ast.ParserOptions{AllFutureKeywords: true}) + if err != nil { + t.Fatal(err) + } + + eval := rego.New( + rego.ParsedModule(parsedModule), + rego.Query("data.test.allow"), + rego.QueryTracer(cover), + ) + + ctx := context.Background() + _, err = eval.Eval(ctx) + + if err != nil { + t.Fatal(err) + } + + report := cover.Report(map[string]*ast.Module{ + "test.rego": parsedModule, + }) + + fr, ok := report.Files["test.rego"] + if !ok { + t.Fatal("Expected file report for test.rego") + } + + expectedCovered := []Position{ + {6}, // allow + } + + expectedNotCovered := []Position{ + {4}, // foo + } + + for _, exp := range expectedCovered { + if !fr.IsCovered(exp.Row) { + t.Errorf("Expected %v to be covered", exp) + } + } + + for _, exp := range expectedNotCovered { + if !fr.IsNotCovered(exp.Row) { + t.Errorf("Expected %v to NOT be covered", exp) + } + } + + if len(expectedCovered) != fr.locCovered() { + t.Errorf( + "Expected %d loc to be covered, got %d instead", + len(expectedCovered), + fr.locCovered()) + } + + if len(expectedNotCovered) != fr.locNotCovered() { + t.Errorf( + "Expected %d loc to not be covered, got %d instead", + len(expectedNotCovered), + fr.locNotCovered()) + } + + expectedCoveragePercentage := 100.0 * float64(len(expectedCovered)) / float64(len(expectedCovered)+len(expectedNotCovered)) + if expectedCoveragePercentage != fr.Coverage { + t.Errorf("Expected coverage %f != %f", expectedCoveragePercentage, fr.Coverage) + } + + if expectedCoveragePercentage != report.Coverage { + t.Errorf("Expected report coverage %f != %f", + expectedCoveragePercentage, + report.Coverage) + } + + if t.Failed() { + bs, err := json.MarshalIndent(fr, "", " ") + if err != nil { + t.Fatal(err) + } + fmt.Println(string(bs)) + } +} + +func TestCoverTraceConfig(t *testing.T) { + ct := topdown.QueryTracer(New()) + conf := ct.Config() + + expected := topdown.TraceConfig{ + PlugLocalVars: false, + } + + if !reflect.DeepEqual(expected, conf) { + t.Fatalf("Expected config: %+v, got %+v", expected, conf) + } +} diff --git a/third_party/opa/v1/debug/README.md b/third_party/opa/v1/debug/README.md new file mode 100644 index 000000000000..368965d26ee9 --- /dev/null +++ b/third_party/opa/v1/debug/README.md @@ -0,0 +1,218 @@ +# OPA Debug API + +This directory contains the OPA Debug API. The Debug API facilitates +programmatic debugging of Rego policies, on top of which 3rd parties can build +tools for debugging. + +This API takes inspiration from the +[Debug Adapter Protocol (DAP)](https://microsoft.github.io/debug-adapter-protocol/), +and follows the conventions established therein for managing threads, +breakpoints, and variable scopes. + +> [!TIP] +> The Debug API current actively supported in two clients +> [VS Code](https://github.com/open-policy-agent/vscode-opa) and +> [Neovim](https://github.com/rinx/nvim-dap-rego/tree/main). Both +> [Regal's Debug Adapter](https://docs.styra.com/regal/debug-adapter) as the +> backend, which is based on this API. + +> [!WARNING] +> The Debug API is experimental and subject to change. + +## Creating a Debug Session + +```go +debugger := debug.NewDebugger() + +ctx := context.Background() +evalProps := debug.EvalProperties{ + Query: "data.example.allow = x", + InputPath: "/path/to/input.json", + LaunchProperties: LaunchProperties{ + DataPaths: []string{"/path/to/data.json", "/path/to/policy.rego"}, + }, +} +session, err := s.debugger.LaunchEval(ctx, evalProps) +if err != nil { + // handle error +} + +// The session is launched in a paused state. +// Before resuming the session, here is the opportunity to set breakpoints + +// Resume execution of all threads associated with the session +err = session.ResumeAll() +if err != nil { + // handle error +} +``` + +## Managing Breakpoints + +Breakpoints can be added, removed, and enumerated. + +Breakpoints are added to file-and-row locations in a module, and are triggered when the policy evaluation reaches that location. +Breakpoints can be added at any time during policy evaluation. + +```go +// Add a breakpoint +br, err := session.AddBreakpoint(location.Location{ + File: "/path/to/policy.rego", + Row: 10, +}) +if err != nil { + // handle error +} + +// ... + +// Remove the breakpoint +_, err = session.RemoveBreakpoint(br.ID) +if err != nil { + // handle error +} +``` + +## Stepping Through Policy Evaluation + +When evaluation execution is paused, either immidiately after launching a session or when a breakpoint is hit, the session can be stepped through. + +### Step Over + +`StepOver()` executes the next expression in the current scope and then stops on the next expression in the same scope, +not stopping on expressions in sub-scopes; e.g. execution of referenced rule, called function, comprehension, or every expression. + +```go +threads, err := session.Threads() +if err != nil { + // handle error +} + +if err := session.StepOver(threads[0].ID); err != nil { + // handle error +} +``` + +#### Example 1 + +``` +allow if { + x := f(input) >-+ + x == 1 | +} | + | +f(x) := y if { <-+ + y := x + 1 +} +``` + +### Example 2 + +``` +allow if { + every x in l { >-+ + x < 10 <-+ + } + input.x == 1 +``` + +### Step In + +`StepIn()` executes the next expression in the current scope and then stops on the next expression in the same scope or sub-scope; +stepping into any referenced rule, called function, comprehension, or every expression. + +```go +if err := session.StepIn(threads[0].ID); err != nil { + // handle error +} +``` + +### Example 1 + +``` +allow if { + x := f(input) >-+ + x == 1 | +} | + | +f(x) := y if { <-+ + y := x + 1 +} +``` + +### Example 2 + +``` +allow if { + every x in l { >-+ + x < 10 <-+ + } + input.x == 1 +} +``` + +### Step Out + +`StepOut()` steps out of the current scope (rule, function, comprehension, every expression) and stops on the next expression in the parent scope. + +```go +if err := session.StepOut(threads[0].ID); err != nil { + // handle error +} +``` + +#### Example 1 + +``` +allow if { + x := f(input) <-+ + x == 1 | +} | + | +f(x) := y if { | + y := x + 1 >-+ +} +``` + +### Example 2 + +``` +allow if { + every x in l { + x < 10 >-+ + } | + input.x == 1 <-+ +} +``` + +## Fetching Variable Values + +The current values of local and global variables are organized into scopes: + +- `Local`: contains variables defined in the current rule, function, comprehension, or every expression. +- `Virtual Cache`: contains the state of the global Virtual Cache, where calculated return values for rules and functions are stored. +- `Input`: contains the input document. +- `Data`: contains the data document. +- `Result Set`: contains the result set of the current query. This scope is only available on the final expression of the query evaluation. + +```go +scopes, err := session.Scopes(thread.ID) +if err != nil { + // handle error +} + +var localScope debug.Scope +for _, scope := range scopes { + if scope.Name == "Local" { + localScope = scope + break + } +} + +variables, err := session.Variables(localScope.VariablesReference()) +if err != nil { + // handle error +} + +// Enumerate and process variables +``` diff --git a/third_party/opa/v1/debug/breakpoint.go b/third_party/opa/v1/debug/breakpoint.go new file mode 100644 index 000000000000..03dd6246d144 --- /dev/null +++ b/third_party/opa/v1/debug/breakpoint.go @@ -0,0 +1,151 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import ( + "bytes" + "fmt" + "sync" + + "github.com/open-policy-agent/opa/v1/ast/location" +) + +type BreakpointID int + +type Breakpoint interface { + ID() BreakpointID + Location() location.Location +} + +type breakpoint struct { + id BreakpointID + location location.Location +} + +func (b breakpoint) ID() BreakpointID { + return b.id +} + +func (b breakpoint) Location() location.Location { + return b.location +} + +func (b breakpoint) String() string { + return fmt.Sprintf("<%d> %s:%d", b.id, b.location.File, b.location.Row) +} + +type breakpointList []Breakpoint + +func (b breakpointList) String() string { + if b == nil { + return "[]" + } + + buf := new(bytes.Buffer) + buf.WriteString("[") + for i, bp := range b { + if i > 0 { + buf.WriteString(", ") + } + _, _ = fmt.Fprint(buf, bp) + } + buf.WriteString("]") + return buf.String() +} + +type breakpointCollection struct { + breakpoints map[string]breakpointList + idCounter BreakpointID + mtx sync.Mutex +} + +func newBreakpointCollection() *breakpointCollection { + return &breakpointCollection{ + breakpoints: map[string]breakpointList{}, + } +} + +func (bc *breakpointCollection) newID() BreakpointID { + bc.idCounter++ + return bc.idCounter +} + +func (bc *breakpointCollection) add(location location.Location) Breakpoint { + bc.mtx.Lock() + defer bc.mtx.Unlock() + + bp := breakpoint{ + id: bc.newID(), + location: location, + } + bps := bc.breakpoints[bp.location.File] + bps = append(bps, bp) + bc.breakpoints[bp.location.File] = bps + return bp +} + +func (bc *breakpointCollection) all() breakpointList { + bc.mtx.Lock() + defer bc.mtx.Unlock() + + var bps breakpointList + for _, list := range bc.breakpoints { + bps = append(bps, list...) + } + return bps +} + +func (bc *breakpointCollection) allForFilePath(path string) breakpointList { + bc.mtx.Lock() + defer bc.mtx.Unlock() + + return bc.breakpoints[path] +} + +func (bc *breakpointCollection) remove(id BreakpointID) Breakpoint { + bc.mtx.Lock() + defer bc.mtx.Unlock() + + var removed Breakpoint + for path, bps := range bc.breakpoints { + var newBps breakpointList + for _, bp := range bps { + if bp.ID() != id { + newBps = append(newBps, bp) + } else { + removed = bp + } + } + bc.breakpoints[path] = newBps + } + + return removed +} + +func (bc *breakpointCollection) clear() { + bc.mtx.Lock() + defer bc.mtx.Unlock() + + bc.breakpoints = map[string]breakpointList{} +} + +func (bc *breakpointCollection) String() string { + if bc == nil { + return "[]" + } + + buf := new(bytes.Buffer) + buf.WriteString("[") + for _, bps := range bc.breakpoints { + for i, bp := range bps { + if i > 0 { + buf.WriteString(", ") + } + _, _ = fmt.Fprint(buf, bp) + } + } + buf.WriteString("]") + return buf.String() +} diff --git a/third_party/opa/v1/debug/debugger.go b/third_party/opa/v1/debug/debugger.go new file mode 100644 index 000000000000..9e74410a25d1 --- /dev/null +++ b/third_party/opa/v1/debug/debugger.go @@ -0,0 +1,912 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package debug +// EXPERIMENTAL: This package is under active development and is subject to change. +package debug + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "os" + "slices" + "strings" + "sync" + + fileurl "github.com/open-policy-agent/opa/internal/file/url" + "github.com/open-policy-agent/opa/v1/ast/location" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/topdown" + prnt "github.com/open-policy-agent/opa/v1/topdown/print" + "github.com/open-policy-agent/opa/v1/util" +) + +// Debugger is the interface for launching OPA debugger Session(s). +// This implementation is similar in structure to the Debug Adapter Protocol (DAP) +// to make such integrations easier, but is not intended to be a direct implementation. +// See: https://microsoft.github.io/debug-adapter-protocol/specification +// +// EXPERIMENTAL: These interfaces are under active development and is subject to change. +type Debugger interface { + // LaunchEval starts a new eval debug session with the given LaunchEvalProperties. + // The returned session is in a stopped state, and must be resumed to start execution. + LaunchEval(ctx context.Context, props LaunchEvalProperties, opts ...LaunchOption) (Session, error) +} + +type debugger struct { + logger logging.Logger + printHook *printHook + eventHandler EventHandler +} + +type Session interface { + // Resume resumes execution of the thread with the given ID. + Resume(threadID ThreadID) error + + // ResumeAll resumes execution of all threads in the session. + ResumeAll() error + + // StepOver executes the next expression in the current scope and then stops on the next expression in the same scope, + // not stopping on expressions in sub-scopes; e.g. execution of referenced rule, called function, comprehension, or every expression. + // + // Example 1: + // + // allow if { + // x := f(input) >-+ + // x == 1 <-+ + // } + // + // Example 2: + // + // allow if { + // every x in l { >-+ + // x < 10 | + // } | + // input.x == 1 <-+ + // } + StepOver(threadID ThreadID) error + + // StepIn executes the next expression in the current scope and then stops on the next expression in the same scope or sub-scope; + // stepping into any referenced rule, called function, comprehension, or every expression. + // + // Example 1: + // + // allow if { + // x := f(input) >-+ + // x == 1 | + // } | + // | + // f(x) := y if { <-+ + // y := x + 1 + // } + // + // Example 2: + // + // allow if { + // every x in l { >-+ + // x < 10 <-+ + // } + // input.x == 1 + // } + StepIn(threadID ThreadID) error + + // StepOut steps out of the current scope (rule, function, comprehension, every expression) and stops on the next expression in the parent scope. + // + // Example 1: + // + // allow if { + // x := f(input) <-+ + // x == 1 | + // } | + // | + // f(x) := y if { | + // y := x + 1 >-+ + // } + // + // Example 2: + // + // allow if { + // every x in l { + // x < 10 >-+ + // } | + // input.x == 1 <-+ + // } + StepOut(threadID ThreadID) error + + // Threads returns a list of all threads in the session. + Threads() ([]Thread, error) + + // Breakpoints returns a list of all set breakpoints. + Breakpoints() ([]Breakpoint, error) + + // AddBreakpoint sets a breakpoint at the given location. + AddBreakpoint(loc location.Location) (Breakpoint, error) + + // RemoveBreakpoint removes a given breakpoint. + // The removed breakpoint is returned. If the breakpoint does not exist, nil is returned. + RemoveBreakpoint(ID BreakpointID) (Breakpoint, error) + + // ClearBreakpoints removes all breakpoints. + ClearBreakpoints() error + + // StackTrace returns the StackTrace for the thread with the given ID. + // The stack trace is ordered from the most recent frame to the least recent frame. + StackTrace(threadID ThreadID) (StackTrace, error) + + // Scopes returns the Scope list for the frame with the given ID. + Scopes(frameID FrameID) ([]Scope, error) + + // Variables returns the Variable list for the given reference. + Variables(varRef VarRef) ([]Variable, error) + + // Terminate stops all threads in the session. + Terminate() error +} + +type printHook struct { + prnt.Hook + d *debugger +} + +func (h *printHook) Print(_ prnt.Context, str string) error { + if h == nil || h.d == nil { + return nil + } + h.d.eventHandler(Event{Type: StdoutEventType, Message: str}) + return nil +} + +type DebuggerOption func(*debugger) + +func NewDebugger(options ...DebuggerOption) Debugger { + return newDebugger(options...) +} + +func newDebugger(options ...DebuggerOption) *debugger { + d := &debugger{ + eventHandler: newNopEventHandler(), + logger: logging.NewNoOpLogger(), + } + d.printHook = &printHook{d: d} + + for _, option := range options { + option(d) + } + + return d +} + +func SetLogger(logger logging.Logger) DebuggerOption { + return func(d *debugger) { + d.logger = logger + } +} + +func SetEventHandler(handler EventHandler) DebuggerOption { + return func(d *debugger) { + d.eventHandler = handler + } +} + +type LaunchEvalProperties struct { + LaunchProperties + Query string + Input any + InputPath string +} + +type LaunchTestProperties struct { + LaunchProperties + Run string +} + +type LaunchProperties struct { + BundlePaths []string + DataPaths []string + StopOnResult bool + StopOnEntry bool + StopOnFail bool + EnablePrint bool + SkipOps []topdown.Op + StrictBuiltinErrors bool + RuleIndexing bool +} + +type LaunchOption func(options *launchOptions) + +type launchOptions struct { + regoOptions []func(*rego.Rego) +} + +func newLaunchOptions(opts []LaunchOption) *launchOptions { + options := &launchOptions{} + for _, opt := range opts { + opt(options) + } + return options +} + +// RegoOption adds a rego option to the internal Rego instance. +// Options may be overridden by the debugger, and it is recommended to +// use LaunchEvalProperties for commonly used options. +func RegoOption(opt func(*rego.Rego)) LaunchOption { + return func(options *launchOptions) { + options.regoOptions = append(options.regoOptions, opt) + } +} + +func (lp LaunchProperties) String() string { + b, err := json.Marshal(lp) + if err != nil { + return "{}" + } + return string(b) +} + +func (d *debugger) LaunchEval(ctx context.Context, props LaunchEvalProperties, opts ...LaunchOption) (Session, error) { + options := newLaunchOptions(opts) + + store := inmem.New() + txn, err := store.NewTransaction(ctx, storage.TransactionParams{Write: true}) + if err != nil { + return nil, fmt.Errorf("failed to create store transaction: %v", err) + } + + regoArgs := make([]func(*rego.Rego), 0, 4) + + // We apply all user options first, so the debugger can make overrides if necessary. + regoArgs = append(regoArgs, options.regoOptions...) + + regoArgs = append(regoArgs, rego.Query(props.Query)) + regoArgs = append(regoArgs, rego.Store(store)) + regoArgs = append(regoArgs, rego.Transaction(txn)) + regoArgs = append(regoArgs, rego.StrictBuiltinErrors(props.StrictBuiltinErrors)) + + if props.SkipOps == nil { + props.SkipOps = []topdown.Op{topdown.IndexOp, topdown.RedoOp, topdown.SaveOp, topdown.UnifyOp} + } + + if props.EnablePrint { + regoArgs = append(regoArgs, rego.EnablePrintStatements(true), + rego.PrintHook(d.printHook)) + } + + if len(props.DataPaths) > 0 { + regoArgs = append(regoArgs, rego.Load(props.DataPaths, nil)) + } + + for _, bundlePath := range props.BundlePaths { + regoArgs = append(regoArgs, rego.LoadBundle(bundlePath)) + } + + if props.InputPath != "" && props.Input != nil { + return nil, errors.New("cannot specify both input and input path") + } + + if props.Input != nil { + regoArgs = append(regoArgs, rego.Input(props.Input)) + } else if props.InputPath != "" { + input, err := readInput(props.InputPath) + if err != nil { + return nil, fmt.Errorf("failed to read input: %v", err) + } + regoArgs = append(regoArgs, rego.Input(input)) + } + + r := rego.New(regoArgs...) + + pq, err := r.PrepareForEval(ctx) + if err != nil { + return nil, fmt.Errorf("failed to prepare query for evaluation: %v", err) + } + + // Committing the store transaction here to make any data added in previous steps are available during eval. + if err := store.Commit(ctx, txn); err != nil { + return nil, fmt.Errorf("failed to commit store transaction: %v", err) + } + + tracer := newDebugTracer() + + vc := topdown.NewVirtualCache() + + evalArgs := []rego.EvalOption{ + rego.EvalRuleIndexing(true), + rego.EvalEarlyExit(true), + rego.EvalQueryTracer(tracer), + rego.EvalRuleIndexing(props.RuleIndexing), + rego.EvalVirtualCache(vc), + } + + varManager := newVariableManager() + // Threads are 1-indexed. + t := newThread(1, "main", tracer, varManager, vc, store, d.logger) + s := newSession(ctx, d, varManager, props.LaunchProperties, []*thread{t}) + + go func() { + defer func() { _ = tracer.Close() }() + rs, evalErr := pq.Eval(s.ctx, evalArgs...) + if evalErr != nil { + var topdownErr *topdown.Error + if errors.As(evalErr, &topdownErr) && topdownErr.Code == topdown.CancelErr { + return + } + d.logger.Error("Evaluation failed: %v", evalErr) + return + } + + tracer.resultSet = rs + s.result(t, rs) + }() + + if err := s.start(); err != nil { + return nil, err + } + return s, nil +} + +func readInput(path string) (any, error) { + path, err := fileurl.Clean(path) + if err != nil { + return nil, err + } + + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var input any + if err := util.Unmarshal(data, &input); err != nil { + return nil, err + } + + return input, nil +} + +type session struct { + d *debugger + properties LaunchProperties + threads []*thread + frames []*stackFrame + framesByThread map[ThreadID][]*stackFrame + breakpoints *breakpointCollection + ctx context.Context + cancel context.CancelFunc + varManager *variableManager + mtx sync.Mutex +} + +func newSession(ctx context.Context, debugger *debugger, varManager *variableManager, props LaunchProperties, threads []*thread) *session { + ctx, cancel := context.WithCancel(ctx) + s := &session{ + d: debugger, + varManager: varManager, + properties: props, + threads: threads, + frames: []*stackFrame{}, + framesByThread: map[ThreadID][]*stackFrame{}, + breakpoints: newBreakpointCollection(), + ctx: ctx, + cancel: cancel, + } + + for _, t := range threads { + t.eventHandler = s.handleEvent + } + + return s +} + +func (s *session) start() error { + if s == nil { + return errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + for _, t := range s.threads { + go func() { + s.d.logger.Debug("Thread %d started", t.id) + s.d.sendEvent(Event{Type: ThreadEventType, Thread: t.id, Message: "started"}) + if err := t.run(s.ctx); err != nil { + s.d.logger.Error("Thread %d failed: %v", t.id, err) + } + s.d.logger.Debug("Thread %d stopped", t.id) + s.d.sendEvent(Event{Type: ThreadEventType, Thread: t.id, Message: "exited"}) + + allStopped := true + for _, t := range s.threads { + if !t.done() { + allStopped = false + break + } + } + + if allStopped { + s.d.logger.Debug("All threads stopped") + s.d.sendEvent(Event{Type: TerminatedEventType}) + } + }() + } + + return nil +} + +func (s *session) thread(id ThreadID) (*thread, error) { + if s == nil { + return nil, errors.New("no active debug session") + } + + index := int(id - 1) + if index < 0 || index >= len(s.threads) { + return nil, fmt.Errorf("invalid thread id: %d", id) + } + return s.threads[index], nil +} + +func (s *session) Resume(threadID ThreadID) error { + if s == nil { + return errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + t, err := s.thread(threadID) + if err != nil { + return err + } + + return t.resume() +} + +func (s *session) ResumeAll() error { + if s == nil { + return errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + for _, t := range s.threads { + if err := t.resume(); err != nil { + return err + } + } + return nil +} + +func (s *session) StepOver(threadID ThreadID) error { + if s == nil { + return errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + t, err := s.thread(threadID) + if err != nil { + return err + } + + err = t.stepOver() + if err == nil { + i, e, _ := t.current() + if e != nil { + s.d.sendEvent(Event{Type: StoppedEventType, Thread: t.id, Message: "step", stackIndex: i, stackEvent: e}) + } + } + if t.done() { + s.d.sendEvent(Event{Type: ThreadEventType, Thread: t.id, Message: "exited"}) + allStopped := true + for _, t := range s.threads { + if !t.done() { + allStopped = false + break + } + } + if allStopped { + s.d.sendEvent(Event{Type: TerminatedEventType}) + } + } + + return err +} + +func (s *session) StepIn(threadID ThreadID) error { + if s == nil { + return errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + t, err := s.thread(threadID) + if err != nil { + return err + } + + _, err = t.stepIn() + if err == nil { + i, e, _ := t.current() + if e != nil { + s.d.sendEvent(Event{Type: StoppedEventType, Thread: t.id, Message: "step", stackIndex: i, stackEvent: e}) + } + } + if t.done() { + s.d.sendEvent(Event{Type: ThreadEventType, Thread: t.id, Message: "exited"}) + allStopped := true + for _, t := range s.threads { + if !t.done() { + allStopped = false + break + } + } + if allStopped { + s.d.sendEvent(Event{Type: TerminatedEventType}) + } + } + + return err +} + +func (s *session) StepOut(threadID ThreadID) error { + if s == nil { + return errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + t, err := s.thread(threadID) + if err != nil { + return err + } + + err = t.stepOut() + if err == nil { + i, e, _ := t.current() + if e != nil { + s.d.sendEvent(Event{Type: StoppedEventType, Thread: t.id, Message: "step", stackIndex: i, stackEvent: e}) + } + } + if t.done() { + s.d.sendEvent(Event{Type: ThreadEventType, Thread: t.id, Message: "exited"}) + allStopped := true + for _, t := range s.threads { + if !t.done() { + allStopped = false + break + } + } + if allStopped { + s.d.sendEvent(Event{Type: TerminatedEventType}) + } + } + + return err +} + +func (s *session) Threads() ([]Thread, error) { + if s == nil { + return nil, errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + threads := make([]Thread, 0, len(s.threads)) + for _, t := range s.threads { + threads = append(threads, t) + } + + return threads, nil +} + +type sessionThreadState struct { + entered bool + ended bool + prevQueryID uint64 +} + +func (s *sessionThreadState) String() string { + return fmt.Sprintf("{entered: %v, ended: %v, prevQueryID: %d}", s.entered, s.ended, s.prevQueryID) +} + +func (s *session) handleEvent(t *thread, stackIndex int, e *topdown.Event, ts threadState) (eventAction, threadState, error) { + state, ok := ts.(*sessionThreadState) + if state != nil && !ok { + s.d.logger.Warn("invalid thread state: %v", s) + } + if state == nil { + state = &sessionThreadState{} + } + + defer func() { + if e != nil { + state.prevQueryID = e.QueryID + } else { + state.prevQueryID = 0 + } + }() + + if e == nil { + handleEnd := func() (eventAction, threadState, error) { + _ = t.close() + return stopAction, state, nil + } + + if state.ended { + s.d.logger.Debug("End of trace already handled") + return handleEnd() + } + + s.d.logger.Debug("Handling end of trace") + + state.ended = true + if s.properties.StopOnResult { + s.d.logger.Info("Thread %d stopped at end of trace", t.id) + s.d.sendEvent(Event{Type: StoppedEventType, Thread: t.id, Message: "result", stackIndex: stackIndex, stackEvent: e}) + return breakAction, state, nil + } + + return handleEnd() + } + + if e.Location == nil { + s.d.logger.Debug("Handling event:\n%v\n\nstate:\n%s", e, state) + } else { + s.d.logger.Debug("Handling event:\n%v\n\nloc:\n%s\n\nstate:\n%s", e, e.Location, state) + } + + if s.skipOp(e.Op) { + s.d.logger.Debug("Skipping event (op: %v)", e.Op) + return skipAction, state, nil + } + + if s.properties.StopOnEntry && !state.entered && e.Location != nil && e.Location.File != "" { + state.entered = true + s.d.logger.Info("Thread %d stopped at entry", t.id) + s.d.sendEvent(Event{Type: StoppedEventType, Thread: t.id, Message: "entry", stackIndex: stackIndex, stackEvent: e}) + return breakAction, state, nil + } + + if s.properties.StopOnFail && e.Op == topdown.FailOp { + s.d.logger.Info("Thread %d stopped on failure", t.id) + s.d.sendEvent(Event{Type: ExceptionEventType, Thread: t.id, Message: string(e.Op), stackIndex: stackIndex, stackEvent: e}) + return breakAction, state, nil + } + + if e.Location != nil && e.Location.File != "" { + for _, bp := range s.breakpoints.allForFilePath(e.Location.File) { + if bp.Location().Row == e.Location.Row { + // if the last event also caused a breakpoint AND we're still on the same line, skip this breakpoint. + s.d.logger.Info("Thread %d stopped at breakpoint: %s:%d", t.id, e.Location.File, e.Location.Row) + s.d.sendEvent(Event{Type: StoppedEventType, Thread: t.id, Message: "breakpoint", stackIndex: stackIndex, stackEvent: e}) + return breakAction, state, nil + } + } + } + + return nopAction, state, nil +} + +func (s *session) skipOp(op topdown.Op) bool { + return slices.Contains(s.properties.SkipOps, op) +} + +func (s *session) result(t *thread, rs rego.ResultSet) { + if rsJSON, err := json.MarshalIndent(rs, "", " "); err == nil { + s.d.logger.Debug("Result: %s\n", rsJSON) + s.d.sendEvent(Event{Type: StdoutEventType, Thread: t.id, Message: string(rsJSON)}) + } else { + s.d.logger.Debug("Result: %v\n", rs) + } +} + +func (s *session) StackTrace(threadID ThreadID) (StackTrace, error) { + if s == nil { + return nil, errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + t, err := s.thread(threadID) + if err != nil { + return nil, err + } + + threadFrames := s.framesByThread[t.id] + if threadFrames == nil { + threadFrames = []*stackFrame{} + } + + stackIndex := 0 + if len(threadFrames) > 0 { + stackIndex = threadFrames[len(threadFrames)-1].stackIndex + 1 + } + newEvents := t.stackEvents(stackIndex) + for _, e := range newEvents { + info := s.newStackFrame(e, t, stackIndex) + stackIndex++ + threadFrames = append(threadFrames, info) + } + s.framesByThread[t.id] = threadFrames + + frames := make([]StackFrame, 0, len(threadFrames)) + for _, f := range threadFrames { + frames = append(frames, f) + } + slices.Reverse(frames) + + return frames, nil +} + +func (s *session) newStackFrame(e *topdown.Event, t *thread, stackIndex int) *stackFrame { + id := len(s.frames) + 1 // frames are 1-indexed + + var expl string + if e.Node != nil { + pretty := new(bytes.Buffer) + topdown.PrettyTrace(pretty, []*topdown.Event{e}) + expl = strings.Trim(pretty.String(), "\n") + } else { + expl = fmt.Sprintf("%s, %s", e.Op, e.Location) + } + + frame := &stackFrame{ + id: FrameID(id), + name: fmt.Sprintf("#%d: %d %s", id, e.QueryID, expl), + location: e.Location, + thread: t.id, + stackIndex: stackIndex, + e: e, + } + + s.frames = append(s.frames, frame) + return frame +} + +func (s *session) frame(id FrameID) (*stackFrame, error) { + index := int(id) - 1 + if index < 0 || index >= len(s.frames) { + return nil, fmt.Errorf("invalid frame id: %d", id) + } + return s.frames[index], nil +} + +func (s *session) Scopes(frameID FrameID) ([]Scope, error) { + if s == nil { + return nil, errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + f, err := s.frame(frameID) + if err != nil { + return nil, err + } + + t, err := s.thread(f.thread) + if err != nil { + return nil, err + } + + return t.scopes(f.stackIndex), nil +} + +func (s *session) Variables(varRef VarRef) ([]Variable, error) { + if s == nil { + return nil, errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + s.d.logger.Debug("Variables requested: %d", varRef) + + vars, err := s.varManager.vars(varRef) + if err != nil { + return nil, err + } + + return vars, nil +} + +func (s *session) Breakpoints() ([]Breakpoint, error) { + if s == nil { + return nil, errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + return s.breakpoints.all(), nil +} + +func (s *session) AddBreakpoint(loc location.Location) (Breakpoint, error) { + if s == nil { + return nil, errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + return s.breakpoints.add(loc), nil +} + +func (s *session) RemoveBreakpoint(id BreakpointID) (Breakpoint, error) { + if s == nil { + return nil, errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + bp := s.breakpoints.remove(id) + if bp == nil { + return nil, fmt.Errorf("breakpoint %d not found", id) + } + + return bp, nil +} + +func (s *session) ClearBreakpoints() error { + if s == nil { + return errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + s.d.logger.Debug("Clearing existing breakpoints") + s.breakpoints.clear() + return nil +} + +func (s *session) Terminate() error { + if s == nil { + return errors.New("no active debug session") + } + + s.mtx.Lock() + defer s.mtx.Unlock() + + s.cancel() + + var hasErrors bool + for _, t := range s.threads { + if err := t.close(); err != nil { + hasErrors = true + s.d.logger.Error("Failed to stop thread %d: %v", t.id, err) + } else { + s.d.sendEvent(Event{Type: ThreadEventType, Thread: t.id, Message: "exited"}) + } + } + + if !hasErrors { + s.d.sendEvent(Event{Type: TerminatedEventType}) + } + + return nil +} + +func (d *debugger) sendEvent(e Event) { + if d == nil || d.eventHandler == nil { + return + } + + d.logger.Debug("Sending event: %v", e) + + d.eventHandler(e) +} diff --git a/third_party/opa/v1/debug/debugger_test.go b/third_party/opa/v1/debug/debugger_test.go new file mode 100644 index 000000000000..5b23930f54f3 --- /dev/null +++ b/third_party/opa/v1/debug/debugger_test.go @@ -0,0 +1,2306 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import ( + "context" + "encoding/json" + "fmt" + "path" + "slices" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ast/location" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestDebuggerEvalStepOver(t *testing.T) { + tests := []struct { + note string + module string + brRow int + brHits int + expRow int + }{ + { + note: "rule", + module: `package test +import rego.v1 + +p if { + q # breakpoint + true # step-over to here +} + +q := y if { + y := 1 * 2 +} +`, + brRow: 5, + expRow: 6, + }, + { + note: "partial rule (success)", + module: `package test +import rego.v1 + +p contains 1 if { # breakpoint + true +} + +p contains 2 if { # step-over to here, the next partial rule 'p' + true +} +`, + brRow: 4, + brHits: 2, // First enter, then exit + expRow: 8, + }, + { + note: "partial rule (fail)", + module: `package test +import rego.v1 + +p contains 1 if { + false # breakpoint +} + +p contains 2 if { # step-over to here, the next partial rule 'p' + true +} +`, + brRow: 5, + brHits: 2, // First eval, then fail + expRow: 8, + }, + { + note: "function", + module: `package test +import rego.v1 + +p if { + f(1) # breakpoint + true # step-over to here +} + +f(x) := y if { + y := x * 2 +} +`, + brRow: 5, + expRow: 6, + }, + { + note: "every", + module: `package test +import rego.v1 + +p if { + every x in [1, 2, 3] { # breakpoint + f(x) + } + true # step-over to here +} + +f(x) := y if { + y := x * 2 +} +`, + brRow: 5, + brHits: 4, // every "header" is composed of multiple expressions that will all cause a breakpoint hit. + expRow: 8, + }, + { + note: "comprehension", + module: `package test +import rego.v1 + +p if { + l := [x | # breakpoint + x := ["a", "b", "c"][_] + ] + true # step-over to here +} + +f(x) := y if { + y := x * 2 +} +`, + brRow: 5, + expRow: 8, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + modName := "test1.rego" + files := map[string]string{ + modName: tc.module, + } + + test.WithTempFS(files, func(rootDir string) { + eh := newTestEventHandler() + d := NewDebugger(SetEventHandler(eh.HandleEvent)) + + launchProps := LaunchEvalProperties{ + LaunchProperties: LaunchProperties{ + BundlePaths: []string{rootDir}, + }, + Query: "x = data.test.p", + } + + // There is only one thread + thr := ThreadID(1) + + s, err := d.LaunchEval(ctx, launchProps) + if err != nil { + t.Fatalf("Unexpected error launching debgug session: %v", err) + } + + if _, err := s.AddBreakpoint(location.Location{File: path.Join(rootDir, modName), Row: tc.brRow}); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expBrHits := 1 + if tc.brHits > 0 { + expBrHits = tc.brHits + } + + for range expBrHits { + if err := s.ResumeAll(); err != nil { + t.Fatalf("Unexpected error resuming threads: %v", err) + } + + // wait for breakpoint + if e := eh.WaitFor(ctx, StoppedEventType); e == nil { + t.Fatal("Expected stopped event") + } else if e.stackEvent.Location.Row != tc.brRow { + t.Fatalf("Expected to stop on row 5, got %d", e.stackEvent.Location.Row) + } + } + + // Release the event handler, so we don't block the debugger + eh.IgnoreAll(ctx) + + // step over + if err := s.StepOver(thr); err != nil { + t.Fatalf("Unexpected error stepping over: %v", err) + } + + if frame := topOfStack(t, s); frame == nil { + t.Fatal("Expected frame") + } else if frame.location.Row != tc.expRow { + t.Fatalf("Expected to stop on row %d, got %d", tc.expRow, frame.location.Row) + } + }) + }) + } +} + +func TestDebuggerEvalStepIn(t *testing.T) { + tests := []struct { + note string + module string + brRow int + brHits int + expRow int + }{ + { + note: "rule", + module: `package test +import rego.v1 + +p if { + q # breakpoint + true +} + +q := y if { # step-in to here + y := 2 * 2 +} +`, + brRow: 5, + expRow: 9, + }, + { + note: "function", + module: `package test +import rego.v1 + +p if { + f(1) # breakpoint + true +} + +f(x) := y if { # step-in to here + y := x * 2 +} +`, + brRow: 5, + expRow: 9, + }, + { + note: "every", + module: `package test +import rego.v1 + +p if { + every x in [1, 2, 3] { # breakpoint + x < 4 # step-in to here + } + true +} +`, + brRow: 5, + brHits: 4, // every "header" is composed of multiple expressions that will all cause a breakpoint hit. + expRow: 6, + }, + { + note: "comprehension", + module: `package test +import rego.v1 + +p if { + l := [x | # breakpoint + x := ["a", "b", "c"][_] # step-in to here + ] + count(l) == 3 +} +`, + brRow: 5, + expRow: 6, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + modName := "test1.rego" + files := map[string]string{ + modName: tc.module, + } + + test.WithTempFS(files, func(rootDir string) { + eh := newTestEventHandler() + d := NewDebugger(SetEventHandler(eh.HandleEvent)) + + launchProps := LaunchEvalProperties{ + LaunchProperties: LaunchProperties{ + BundlePaths: []string{rootDir}, + }, + Query: "x = data.test.p", + } + + // There is only one thread + thr := ThreadID(1) + + s, err := d.LaunchEval(ctx, launchProps) + if err != nil { + t.Fatalf("Unexpected error launching debgug session: %v", err) + } + + if _, err := s.AddBreakpoint(location.Location{File: path.Join(rootDir, modName), Row: tc.brRow}); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expBrHits := 1 + if tc.brHits > 0 { + expBrHits = tc.brHits + } + for range expBrHits { + if err := s.ResumeAll(); err != nil { + t.Fatalf("Unexpected error resuming threads: %v", err) + } + + // wait for breakpoint + if e := eh.WaitFor(ctx, StoppedEventType); e == nil { + t.Fatal("Expected stopped event") + } else if e.stackEvent.Location.Row != tc.brRow { + t.Fatalf("Expected to stop on row 5, got %d", e.stackEvent.Location.Row) + } + } + + // Release the event handler, so we don't block the debugger + eh.IgnoreAll(ctx) + + // step into function + if err := s.StepIn(thr); err != nil { + t.Fatalf("Unexpected error stepping in: %v", err) + } + + if frame := topOfStack(t, s); frame == nil { + t.Fatal("Expected frame") + } else if frame.location.Row != tc.expRow { + t.Fatalf("Expected to stop on row %d, got %d", tc.expRow, frame.location.Row) + } + }) + }) + } +} + +func TestDebuggerEvalStepOut(t *testing.T) { + tests := []struct { + note string + module string + brRow int + brHits int + expRow int + }{ + { + note: "rule", + module: `package test +import rego.v1 + +p if { + q + true # step-out to here +} + +q := y if { + true # breakpoint + y := 2 * 2 +} +`, + brRow: 10, + expRow: 6, + }, + { + note: "function", + module: `package test +import rego.v1 + +p if { + f(1) + true # step-out to here +} + +f(x) := y if { + true # breakpoint + y := x * 2 +} +`, + brRow: 10, + expRow: 6, + }, + { + note: "every", + module: `package test +import rego.v1 + +p if { + every x in [1, 2, 3] { + true + true # breakpoint + x < 4 + } + true # step-out to here +} +`, + brRow: 7, + brHits: 3, // every expr enumeration + expRow: 10, + }, + { + note: "comprehension", + module: `package test +import rego.v1 + +p if { + l := [x | + true + true # breakpoint + x := ["a", "b", "c"][_] + ] + true # step-out to here +} +`, + brRow: 7, + expRow: 10, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + modName := "test1.rego" + files := map[string]string{ + modName: tc.module, + } + + test.WithTempFS(files, func(rootDir string) { + eh := newTestEventHandler() + d := NewDebugger(SetEventHandler(eh.HandleEvent)) + + launchProps := LaunchEvalProperties{ + LaunchProperties: LaunchProperties{ + BundlePaths: []string{rootDir}, + }, + Query: "x = data.test.p", + } + + // There is only one thread + thr := ThreadID(1) + + s, err := d.LaunchEval(ctx, launchProps) + if err != nil { + t.Fatalf("Unexpected error launching debgug session: %v", err) + } + + if _, err := s.AddBreakpoint(location.Location{File: path.Join(rootDir, modName), Row: tc.brRow}); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // wait for breakpoint + expBrHits := 1 + if tc.brHits > 0 { + expBrHits = tc.brHits + } + for range expBrHits { + if err := s.ResumeAll(); err != nil { + t.Fatalf("Unexpected error resuming threads: %v", err) + } + + if e := eh.WaitFor(ctx, StoppedEventType); e == nil { + t.Fatal("Expected stopped event") + } else if e.stackEvent.Location.Row != tc.brRow { + t.Fatalf("Expected to stop on row 10, got %d", e.stackEvent.Location.Row) + } + } + + // Release the event handler, so we don't block the debugger + eh.IgnoreAll(ctx) + + // step out of function + if err := s.StepOut(thr); err != nil { + t.Fatalf("Unexpected error stepping in: %v", err) + } + + if frame := topOfStack(t, s); frame == nil { + t.Fatal("Expected frame") + } else if frame.location.Row != tc.expRow { + t.Fatalf("Expected to stop on row %d, got %d", tc.expRow, frame.location.Row) + } + }) + }) + } +} + +func TestDebuggerEvalPrint(t *testing.T) { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second)) + defer cancel() + + files := map[string]string{ + "test1.rego": `package test +import rego.v1 + +p if { + print("hello") +} +`, + } + + test.WithTempFS(files, func(rootDir string) { + eh := newTestEventHandler() + d := NewDebugger(SetEventHandler(eh.HandleEvent)) + + launchProps := LaunchEvalProperties{ + LaunchProperties: LaunchProperties{ + BundlePaths: []string{rootDir}, + EnablePrint: true, + }, + Query: "x = data.test.p", + } + + s, err := d.LaunchEval(ctx, launchProps) + if err != nil { + t.Fatalf("Unexpected error launching debgug session: %v", err) + } + + if err := s.ResumeAll(); err != nil { + t.Fatalf("Unexpected error resuming threads: %v", err) + } + + // print output + e := eh.WaitFor(ctx, StdoutEventType) + if e.Message != "hello" { + t.Fatalf("Expected message to be 'hello', got %q", e.Message) + } + + // result output + exp := `[ + { + "expressions": [ + { + "value": true, + "text": "x = data.test.p", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": true + } + } +]` + e = eh.WaitFor(ctx, StdoutEventType) + if e.Message != exp { + t.Fatalf("Expected message to be:\n\n%s\n\ngot:\n\n%s", exp, e.Message) + } + }) +} + +func TestFiles(t *testing.T) { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second)) + defer cancel() + + files := map[string]string{ + "mod.rego": `package test +import rego.v1 + +p if { + input.foo == "a" + input.bar == "b" + data.baz == "c" + data.qux == "d" +} +`, + "input.json": `{ + "foo": "a", + "bar": "b" +}`, + "input.yaml": ` +foo: a +bar: b +`, + "data.json": `{ + "baz": "c", + "qux": "d" +}`, + "data.yaml": ` +baz: c +qux: d +`, + } + + for _, ext := range []string{"json", "yaml"} { + t.Run(ext, func(t *testing.T) { + test.WithTempFS(files, func(rootDir string) { + eh := newTestEventHandler() + d := NewDebugger(SetEventHandler(eh.HandleEvent)) + + launchProps := LaunchEvalProperties{ + LaunchProperties: LaunchProperties{ + DataPaths: []string{ + path.Join(rootDir, "mod.rego"), + path.Join(rootDir, "data."+ext), + }, + EnablePrint: true, + }, + Query: "x = data.test.p", + InputPath: path.Join(rootDir, "input."+ext), + } + + s, err := d.LaunchEval(ctx, launchProps) + if err != nil { + t.Fatalf("Unexpected error launching debgug session: %v", err) + } + + if err := s.ResumeAll(); err != nil { + t.Fatalf("Unexpected error resuming threads: %v", err) + } + + // result output + exp := `[ + { + "expressions": [ + { + "value": true, + "text": "x = data.test.p", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": true + } + } +]` + e := eh.WaitFor(ctx, StdoutEventType) + if e.Message != exp { + t.Fatalf("Expected message to be:\n\n%s\n\ngot:\n\n%s", exp, e.Message) + } + }) + }) + } +} + +func topOfStack(t *testing.T, s Session) *stackFrame { + t.Helper() + stk, err := s.StackTrace(ThreadID(1)) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + frame, ok := stk[0].(*stackFrame) + if !ok { + t.Fatalf("Expected stackFrame, got %T", stk[0]) + } + return frame +} + +func TestDebuggerAutomaticStop(t *testing.T) { + tests := []struct { + note string + props LaunchProperties + expEventType EventType + expEventIndex int + }{ + { + note: "No automatic stop", + expEventType: TerminatedEventType, + expEventIndex: -1, + }, + { + note: "Stop on entry", + props: LaunchProperties{ + StopOnEntry: true, + }, + expEventType: StoppedEventType, + expEventIndex: 1, + }, + { + note: "Stop on end of trace", + props: LaunchProperties{ + StopOnResult: true, + }, + expEventType: StoppedEventType, + expEventIndex: -1, + }, + { + note: "Stop on fail", + props: LaunchProperties{ + StopOnFail: true, + }, + expEventType: ExceptionEventType, + expEventIndex: 3, + }, + } + + testEvents := []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 0, + }, + { // 1 + Op: topdown.EnterOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 1, + }, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 2, + }, + }, + { // 3 + Op: topdown.FailOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 2, + }, + }, + { // 4 + Op: topdown.RedoOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 2, + }, + }, + { // 5 + Op: topdown.ExitOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 1, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + stk := newTestStack(testEvents...) + eh := newTestEventHandler() + _, s, _ := setupDebuggerSession(ctx, stk, tc.props, eh.HandleEvent, nil, nil, nil) + + if err := s.start(); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := s.ResumeAll(); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + test.EventuallyOrFatal(t, 5*time.Second, func() bool { + e := eh.Next(10 * time.Millisecond) + if e != nil && e.Type == tc.expEventType { + i, _ := stk.Current() + return i == tc.expEventIndex + } + return false + }) + }) + } +} + +func TestDebuggerStopOnBreakpoint(t *testing.T) { + tests := []struct { + note string + breakpoint location.Location + events []*topdown.Event + expEventIndices []int + }{ + { + note: "breakpoint on line with single event", + breakpoint: location.Location{File: "test.rego", Row: 1}, + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 0, + }, + { // 1 + Op: topdown.EnterOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 1, + }, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 2, + }, + }, + }, + expEventIndices: []int{1}, + }, + { + note: "breakpoint on line with single event (2)", + breakpoint: location.Location{File: "test.rego", Row: 2}, + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 0, + }, + { // 1 + Op: topdown.EnterOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 1, + }, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 2, + }, + }, + }, + expEventIndices: []int{2}, + }, + { + note: "breakpoint on line with multiple consecutive events", + breakpoint: location.Location{File: "test.rego", Row: 2}, + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 0, + }, + { // 1 + Op: topdown.EnterOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 1, + }, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 2, + }, + }, + { // 3 + Op: topdown.UnifyOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 2, + }, + }, + { // 4 + Op: topdown.EvalOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 3, + }, + }, + }, + expEventIndices: []int{2, 3}, + }, + { + note: "breakpoint on reoccurring line", + breakpoint: location.Location{File: "test.rego", Row: 2}, + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + }, + { // 1 + Op: topdown.EnterOp, + Location: &location.Location{ + File: "test.rego", + Row: 1, + }, + }, + { // 2 + Op: topdown.EvalOp, + Location: &location.Location{ + File: "test.rego", + Row: 2, + }, + }, + { // 3 + Op: topdown.EvalOp, + Location: &location.Location{ + File: "test.rego", + Row: 3, + }, + }, + { // 4 + Op: topdown.RedoOp, + Location: &location.Location{ + File: "test.rego", + Row: 2, + }, + }, + { // 5 + Op: topdown.RedoOp, + Location: &location.Location{ + File: "test.rego", + Row: 1, + }, + }, + }, + expEventIndices: []int{2, 4}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + stk := newTestStack(tc.events...) + eh := newTestEventHandler() + _, s, _ := setupDebuggerSession(ctx, stk, LaunchProperties{}, eh.HandleEvent, nil, nil, nil) + + bp, err := s.AddBreakpoint(tc.breakpoint) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if bp.Location().File != tc.breakpoint.File { + t.Errorf("Expected breakpoint file %s, got %s", tc.breakpoint.File, bp.Location().File) + } + + if bp.Location().Row != tc.breakpoint.Row { + t.Errorf("Expected breakpoint row %d, got %d", tc.breakpoint.Row, bp.Location().Row) + } + + if err := s.start(); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := s.ResumeAll(); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + var stoppedAt []int + test.EventuallyOrFatal(t, 5*time.Second, func() bool { + for { + e := eh.NextBlocking() + if e == nil || e.Type == TerminatedEventType { + return true + } + if e.Type == StoppedEventType { + stoppedAt = append(stoppedAt, e.stackIndex) + if err := s.Resume(e.Thread); err != nil { + t.Fatalf("Unexpected error resuming: %v", err) + } + } + } + }) + + if !slices.Equal(stoppedAt, tc.expEventIndices) { + t.Errorf("Expected to stop at event indices %v, got %v", tc.expEventIndices, stoppedAt) + } + }) + } +} + +func TestDebuggerStepIn(t *testing.T) { + tests := []struct { + note string + events []*topdown.Event + expEventIndices []int + }{ + { + note: "single query", + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 1 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 1, + }, + }, + expEventIndices: []int{0, 1, 2}, + }, + { + note: "nested queries", + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 0, + }, + { // 1 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 2, + }, + { // 3 + Op: topdown.RedoOp, + QueryID: 2, + }, + { // 4 + Op: topdown.RedoOp, + QueryID: 1, + }, + { // 5 + Op: topdown.RedoOp, + QueryID: 0, + }, + }, + expEventIndices: []int{0, 1, 2, 3, 4, 5}, + }, + { + note: "sequential queries", + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 0, + }, + { // 1 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 2, + }, + { // 3 + Op: topdown.RedoOp, + QueryID: 2, + }, + { // 4 + Op: topdown.RedoOp, + QueryID: 1, + }, + { // 5 + Op: topdown.RedoOp, + QueryID: 0, + }, + { // 6 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 7 + Op: topdown.EvalOp, + QueryID: 2, + }, + }, + expEventIndices: []int{0, 1, 2, 3, 4, 5, 6, 7}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + stk := newTestStack(tc.events...) + eh := newTestEventHandler() + _, s, thr := setupDebuggerSession(ctx, stk, LaunchProperties{}, eh.HandleEvent, nil, nil, nil) + + var stoppedAt []int + doneCh := make(chan struct{}) + defer close(doneCh) + go func() { + for { + e := eh.NextBlocking() + + if e == nil || e.Type == TerminatedEventType { + break + } + + if e.Type == StoppedEventType { + stoppedAt = append(stoppedAt, e.stackIndex) + } + } + doneCh <- struct{}{} + }() + + go func() { + for { + if err := s.StepIn(thr.id); err != nil { + t.Errorf("Unexpected error stepping in: %v", err) + break + } + } + }() + + select { + case <-time.After(5 * time.Second): + t.Fatal("Timed out waiting for debugger to finish") + case <-doneCh: + } + + if !slices.Equal(stoppedAt, tc.expEventIndices) { + t.Errorf("Expected to stop at event indices %v, got %v", tc.expEventIndices, stoppedAt) + } + }) + } +} + +func TestDebuggerStepOver(t *testing.T) { + tests := []struct { + note string + events []*topdown.Event + expEventIndices []int + }{ + { + note: "single query", + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 1 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 1, + }, + }, + expEventIndices: []int{0, 1, 2}, + }, + { + note: "nested queries", + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 1 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 2, + }, + { // 3 + Op: topdown.RedoOp, + QueryID: 2, + }, + { // 4 + Op: topdown.RedoOp, + QueryID: 1, + }, + { // 5 + Op: topdown.RedoOp, + QueryID: 1, + }, + }, + expEventIndices: []int{0, 1, 4, 5}, + }, + { + note: "multiple nested queries", + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 1 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 2, + }, + { // 3 + Op: topdown.EvalOp, + QueryID: 3, + }, + { // 4 + Op: topdown.RedoOp, + QueryID: 3, + }, + { // 5 + Op: topdown.RedoOp, + QueryID: 2, + }, + { // 6 + Op: topdown.RedoOp, + QueryID: 1, + }, + { // 7 + Op: topdown.RedoOp, + QueryID: 1, + }, + }, + expEventIndices: []int{0, 1, 6, 7}, + }, + { + note: "sequential queries", + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 1 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 2, + }, + { // 3 + Op: topdown.RedoOp, + QueryID: 2, + }, + { // 4 + Op: topdown.RedoOp, + QueryID: 1, + }, + { // 5 + Op: topdown.EvalOp, + QueryID: 2, + }, + { // 6 + Op: topdown.EvalOp, + QueryID: 1, + }, + }, + expEventIndices: []int{0, 1, 4, 6}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + stk := newTestStack(tc.events...) + eh := newTestEventHandler() + _, s, thr := setupDebuggerSession(ctx, stk, LaunchProperties{}, eh.HandleEvent, nil, nil, nil) + + var stoppedAt []int + doneCh := make(chan struct{}) + defer close(doneCh) + go func() { + for { + e := eh.NextBlocking() + + if e == nil || e.Type == TerminatedEventType { + break + } + + if e.Type == StoppedEventType { + stoppedAt = append(stoppedAt, e.stackIndex) + } + } + doneCh <- struct{}{} + }() + + go func() { + for { + if err := s.StepOver(thr.id); err != nil { + t.Errorf("Unexpected error stepping over: %v", err) + break + } + } + }() + + select { + case <-time.After(5 * time.Second): + t.Fatal("Timed out waiting for debugger to finish") + case <-doneCh: + } + + if !slices.Equal(stoppedAt, tc.expEventIndices) { + t.Errorf("Expected to stop at event indices %v, got %v", tc.expEventIndices, stoppedAt) + } + }) + } +} + +func TestDebuggerStepOut(t *testing.T) { + tests := []struct { + note string + events []*topdown.Event + expEventIndices []int + }{ + { + note: "single query", + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 1 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 1, + }, + }, + // We always expect to stop on the first stack event + expEventIndices: []int{0}, + }, + { + note: "single query to step out of", + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 2, + }, + { // 1 + Op: topdown.EvalOp, + QueryID: 2, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 1, + }, + { // 3 + Op: topdown.RedoOp, + QueryID: 1, + }, + }, + + expEventIndices: []int{0, 2}, + }, + { + note: "multiple queries to step out of", + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 3, + }, + { // 1 + Op: topdown.EvalOp, + QueryID: 3, + }, + { // 2 + Op: topdown.EvalOp, + QueryID: 2, + }, + { // 3 + Op: topdown.RedoOp, + QueryID: 2, + }, + { // 4 + Op: topdown.RedoOp, + QueryID: 1, + }, + { // 5 + Op: topdown.EvalOp, + QueryID: 1, + }, + }, + + expEventIndices: []int{0, 2, 4}, + }, + { + note: "step-out also steps-over", + events: []*topdown.Event{ + { // 0 + Op: topdown.EvalOp, + QueryID: 3, + }, + // Extra query to step over + { // 1 + Op: topdown.EvalOp, + QueryID: 4, + }, + { // 2 + Op: topdown.RedoOp, + QueryID: 4, + }, + { // 3 + Op: topdown.EvalOp, + QueryID: 3, + }, + { // 4 + Op: topdown.EvalOp, + QueryID: 2, + }, + { // 5 + Op: topdown.RedoOp, + QueryID: 2, + }, + }, + expEventIndices: []int{0, 4}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + stk := newTestStack(tc.events...) + eh := newTestEventHandler() + _, s, thr := setupDebuggerSession(ctx, stk, LaunchProperties{}, eh.HandleEvent, nil, nil, nil) + + var stoppedAt []int + doneCh := make(chan struct{}) + defer close(doneCh) + go func() { + for { + e := eh.NextBlocking() + + if e == nil || e.Type == TerminatedEventType { + break + } + + if e.Type == StoppedEventType { + stoppedAt = append(stoppedAt, e.stackIndex) + } + } + doneCh <- struct{}{} + }() + + go func() { + for { + if err := s.StepOut(thr.id); err != nil { + t.Errorf("Unexpected error stepping over: %v", err) + break + } + } + }() + + select { + case <-time.After(5 * time.Second): + t.Fatal("Timed out waiting for debugger to finish") + case <-doneCh: + } + + if !slices.Equal(stoppedAt, tc.expEventIndices) { + t.Errorf("Expected to stop at event indices %v, got %v", tc.expEventIndices, stoppedAt) + } + }) + } +} + +func TestDebuggerStackTrace(t *testing.T) { + tests := []struct { + note string + events []*topdown.Event + expTrace []*stackFrame + }{ + { + note: "empty stack", + expTrace: []*stackFrame{}, + }, + { + note: "single stack frame, no event node", + events: []*topdown.Event{ + { + Op: topdown.EvalOp, + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 42, + }, + }, + }, + expTrace: []*stackFrame{ + { + id: 1, + name: "#1: 1 Eval, test.rego:42", + thread: 1, + location: &location.Location{ + File: "test.rego", + Row: 42, + }, + }, + }, + }, + { + note: "single stack frame, event node", + events: []*topdown.Event{ + { + Op: topdown.EvalOp, + Node: ast.MustParseExpr("data.test.p[x]"), + QueryID: 1, + Location: &location.Location{ + File: "test.rego", + Row: 42, + }, + }, + }, + expTrace: []*stackFrame{ + { + id: 1, + name: "#1: 1 | Eval data.test.p[x]", + thread: 1, + location: &location.Location{ + File: "test.rego", + Row: 42, + }, + }, + }, + }, + { + note: "multiple stack frames", + events: []*topdown.Event{ + { + Op: topdown.EvalOp, + Node: ast.MustParseExpr("y := data.test.p[x]"), + QueryID: 5, + Location: &location.Location{ + File: "test.rego", + Row: 2, + }, + }, + { + Op: topdown.UnifyOp, + Node: ast.MustParseExpr("y = 1"), + QueryID: 5, + Location: &location.Location{ + File: "test.rego", + Row: 3, + }, + }, + }, + // Reversed order + expTrace: []*stackFrame{ + { + id: 2, + name: "#2: 5 | Unify y = 1", + thread: 1, + location: &location.Location{ + File: "test.rego", + Row: 3, + }, + }, + { + id: 1, + name: "#1: 5 | Eval assign(y, data.test.p[x])", + thread: 1, + location: &location.Location{ + File: "test.rego", + Row: 2, + }, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second)) + defer cancel() + + stk := newTestStack(tc.events...) + eh := newTestEventHandler() + _, s, thr := setupDebuggerSession(ctx, stk, LaunchProperties{}, eh.HandleEvent, nil, nil, nil) + + if err := s.start(); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := s.ResumeAll(); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if e := eh.WaitFor(ctx, TerminatedEventType); e == nil { + t.Fatal("Run never terminated") + } + + trace, err := s.StackTrace(thr.id) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(trace) != len(tc.expTrace) { + t.Fatalf("Expected %d stack frames, got %d", len(tc.expTrace), len(trace)) + } + + if len(trace) != len(tc.expTrace) { + t.Errorf("Expected stack trace:\n\n%v\n\ngot:\n\n%v", tc.expTrace, trace) + } + for i := range trace { + if !trace[i].Equal(tc.expTrace[i]) { + t.Errorf("Expected stack frame (%d):\n\n%v\n\ngot:\n\n%v", i, tc.expTrace[i], trace[i]) + } + } + }) + } +} + +func TestDebuggerScopeVariables(t *testing.T) { + tests := []struct { + note string + input *ast.Term + locals map[ast.Var]ast.Value + virtualCache map[string]ast.Value + data map[string]any + result *rego.ResultSet + expScopes map[string]scopeInfo + }{ + { + note: "no variables", + expScopes: map[string]scopeInfo{ + "Locals": { + name: "Locals", + namedVariables: 0, + }, + "Input (not provided)": { + name: "Input (not provided)", + namedVariables: 0, + }, + "Data (not provided)": { + name: "Data (not provided)", + namedVariables: 0, + }, + }, + }, + { + note: "input (object)", + input: ast.ObjectTerm( + ast.Item(ast.StringTerm("x"), ast.NumberTerm("1")), + ast.Item(ast.StringTerm("y"), ast.BooleanTerm(true))), + expScopes: map[string]scopeInfo{ + "Locals": { + name: "Locals", + namedVariables: 0, + }, + "Input": { + name: "Input", + namedVariables: 1, + variables: map[string]varInfo{ + "input": { + typ: "object", + val: `{"x": 1, "y": true}`, + children: map[string]varInfo{ + `"x"`: { + typ: "number", + val: "1", + }, + `"y"`: { + typ: "boolean", + val: "true", + }, + }, + }, + }, + }, + "Data (not provided)": { + name: "Data (not provided)", + namedVariables: 0, + }, + }, + }, + { + note: "input (array)", + input: ast.ArrayTerm( + ast.StringTerm("foo"), + ast.NumberTerm("1"), + ast.BooleanTerm(true)), + expScopes: map[string]scopeInfo{ + "Locals": { + name: "Locals", + namedVariables: 0, + }, + "Input": { + name: "Input", + namedVariables: 1, + variables: map[string]varInfo{ + "input": { + typ: "array", + val: `["foo", 1, true]`, + children: map[string]varInfo{ + "0": { + typ: "string", + val: `"foo"`, + }, + "1": { + typ: "number", + val: "1", + }, + "2": { + typ: "boolean", + val: "true", + }, + }, + }, + }, + }, + "Data (not provided)": { + name: "Data (not provided)", + namedVariables: 0, + }, + }, + }, + { + note: "local vars", + locals: map[ast.Var]ast.Value{ + ast.Var("x"): ast.Number("42"), + ast.Var("y"): ast.Boolean(true), + ast.Var("z"): ast.String("foo"), + ast.Var("obj"): ast.NewObject( + ast.Item(ast.StringTerm("a"), ast.NumberTerm("1")), + ast.Item(ast.StringTerm("b"), ast.NumberTerm("2"))), + ast.Var("arr"): ast.NewArray(ast.NumberTerm("1"), ast.NumberTerm("2"), ast.NumberTerm("3")), + }, + expScopes: map[string]scopeInfo{ + "Locals": { + name: "Locals", + namedVariables: 5, + variables: map[string]varInfo{ + "x": { + typ: "number", + val: "42", + }, + "y": { + typ: "boolean", + val: "true", + }, + "z": { + typ: "string", + val: `"foo"`, + }, + "obj": { + typ: "object", + val: `{"a": 1, "b": 2}`, + children: map[string]varInfo{ + `"a"`: { + typ: "number", + val: "1", + }, + `"b"`: { + typ: "number", + val: "2", + }, + }, + }, + "arr": { + typ: "array", + val: "[1, 2, 3]", + children: map[string]varInfo{ + "0": { + typ: "number", + val: "1", + }, + "1": { + typ: "number", + val: "2", + }, + "2": { + typ: "number", + val: "3", + }, + }, + }, + }, + }, + "Input (not provided)": { + name: "Input (not provided)", + namedVariables: 0, + }, + "Data (not provided)": { + name: "Data (not provided)", + namedVariables: 0, + }, + }, + }, + { + note: "local var with long text description", + locals: map[ast.Var]ast.Value{ + ast.Var("x"): ast.String(strings.Repeat("x", 1000)), + }, + expScopes: map[string]scopeInfo{ + "Locals": { + name: "Locals", + namedVariables: 1, + variables: map[string]varInfo{ + "x": { + typ: "string", + val: fmt.Sprintf(`"%s...`, strings.Repeat("x", 97)), + }, + }, + }, + "Input (not provided)": { + name: "Input (not provided)", + namedVariables: 0, + }, + "Data (not provided)": { + name: "Data (not provided)", + namedVariables: 0, + }, + }, + }, + { + note: "result", + result: ®o.ResultSet{ + rego.Result{ + Expressions: []*rego.ExpressionValue{ + { + Value: ast.Boolean(true), + Text: "x = data.test.allow", + }, + }, + Bindings: map[string]any{ + "x": ast.Boolean(true), + }, + }, + }, + expScopes: map[string]scopeInfo{ + "Locals": { + name: "Locals", + namedVariables: 0, + }, + "Input (not provided)": { + name: "Input (not provided)", + namedVariables: 0, + }, + "Data (not provided)": { + name: "Data (not provided)", + namedVariables: 0, + }, + "Result Set": { + name: "Result Set", + namedVariables: 1, + variables: map[string]varInfo{ + "0": { + typ: "object", + val: `{"bindings": {"x": true}, "expressions": [{"text": "x = data.test.allow", "value": true}]}`, + children: map[string]varInfo{ + `"bindings"`: { + typ: "object", + val: `{"x": true}`, + children: map[string]varInfo{ + `"x"`: { + typ: "boolean", + val: "true", + }, + }, + }, + `"expressions"`: { + typ: "array", + val: `[{"text": "x = data.test.allow", "value": true}]`, + children: map[string]varInfo{ + "0": { + typ: "object", + val: `{"text": "x = data.test.allow", "value": true}`, + children: map[string]varInfo{ + `"text"`: { + typ: "string", + val: `"x = data.test.allow"`, + }, + `"value"`: { + typ: "boolean", + val: "true", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + note: "virtual cache", + virtualCache: map[string]ast.Value{ + "data.foo": ast.String("bar"), + "data.baz": ast.Number("42"), + }, + expScopes: map[string]scopeInfo{ + "Locals": { + name: "Locals", + namedVariables: 0, + }, + "Input (not provided)": { + name: "Input (not provided)", + namedVariables: 0, + }, + "Data (not provided)": { + name: "Data (not provided)", + namedVariables: 0, + }, + "Virtual Cache": { + name: "Virtual Cache", + namedVariables: 2, + variables: map[string]varInfo{ + "data.foo": { + typ: "string", + val: `"bar"`, + }, + "data.baz": { + typ: "number", + val: "42", + }, + }, + }, + }, + }, + { + note: "data", + data: map[string]any{ + "foo": "bar", + "baz": 42, + }, + expScopes: map[string]scopeInfo{ + "Locals": { + name: "Locals", + namedVariables: 0, + }, + "Input (not provided)": { + name: "Input (not provided)", + namedVariables: 0, + }, + "Data": { + name: "Data", + namedVariables: 1, + variables: map[string]varInfo{ + "data": { + typ: "object", + val: `{"baz": 42, "foo": "bar"}`, + children: map[string]varInfo{ + `"foo"`: { + typ: "string", + val: `"bar"`, + }, + `"baz"`: { + typ: "number", + val: "42", + }, + }, + }, + }, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + locals := ast.NewValueMap() + for k, v := range tc.locals { + locals.Put(k, v) + } + + e := topdown.Event{ + Op: topdown.EvalOp, + Locals: locals, + } + + e.WithInput(tc.input) + events := []*topdown.Event{&e} + + stk := newTestStack(events...) + + if tc.result != nil { + stk.result = *tc.result + } + + stk.Next() // Move forward to the first event + eh := newTestEventHandler() + + var vc topdown.VirtualCache + if tc.virtualCache != nil { + vc = topdown.NewVirtualCache() + for k, v := range tc.virtualCache { + vc.Put(ast.MustParseRef(k), ast.NewTerm(v)) + } + } + + var store storage.Store + if tc.data != nil { + store = inmem.NewFromObject(tc.data) + } + + _, s, thr := setupDebuggerSession(ctx, stk, LaunchProperties{}, eh.HandleEvent, vc, store, nil) + + trace, err := s.StackTrace(thr.id) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if len(trace) != 1 { + t.Fatalf("Expected 1 stack frame, got %d", len(trace)) + } + + scopes, err := s.Scopes(trace[0].ID()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(scopes) != len(tc.expScopes) { + t.Fatalf("Expected %d scopes, got %d", len(tc.expScopes), len(scopes)) + } + + for i, scope := range scopes { + expScope, ok := tc.expScopes[scope.Name()] + if !ok { + t.Errorf("Unexpected scope: %s", scopes[i].Name()) + continue + } + + if scope.Name() != expScope.name { + t.Errorf("Expected scope name %s, got %s", expScope.name, scope.Name()) + } + + if scope.NamedVariables() != expScope.namedVariables { + t.Errorf("Expected %d named variables, got %d", expScope.namedVariables, scope.NamedVariables()) + } + + if scope.NamedVariables() > 0 && scope.VariablesReference() == 0 { + t.Errorf("Expected non-zero variables reference") + } + + if expScope.namedVariables > 0 { + vars, err := s.Variables(scope.VariablesReference()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(vars) != expScope.namedVariables { + t.Fatalf("Expected nuber of variables to equal named variables for scope (%d), got %d", expScope.namedVariables, len(vars)) + } + + assertVariables(t, s, vars, expScope.variables) + } + } + }) + } +} + +type varInfo struct { + typ string + val string + children map[string]varInfo +} + +type scopeInfo struct { + name string + namedVariables int + variables map[string]varInfo +} + +func assertVariables(t *testing.T, s Session, variables []Variable, exp map[string]varInfo) { + for _, v := range variables { + expVar, ok := exp[v.Name()] + if !ok { + t.Errorf("Unexpected variable: %s", v.Name()) + continue + } + + if v.Type() != expVar.typ { + t.Errorf("Expected variable type %s, got %s", expVar.typ, v.Type()) + } + + if v.Value() != expVar.val { + t.Errorf("Expected variable value %s, got %s", expVar.val, v.Value()) + } + + if len(expVar.children) != 0 { + if v.VariablesReference() == 0 { + t.Errorf("Expected non-zero variables reference") + } + + vars, err := s.Variables(v.VariablesReference()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + assertVariables(t, s, vars, expVar.children) + } else if v.VariablesReference() != 0 { + t.Errorf("Expected zero variables reference") + } + } +} + +func setupDebuggerSession(ctx context.Context, stk stack, launchProperties LaunchProperties, eh EventHandler, + vc topdown.VirtualCache, store storage.Store, l logging.Logger) (*debugger, *session, *thread) { + if l == nil { + l = logging.NewNoOpLogger() + } + + opts := []DebuggerOption{SetLogger(l)} + if eh != nil { + opts = append(opts, SetEventHandler(eh)) + } + + varManager := newVariableManager() + d := newDebugger(opts...) + t := newThread(1, "test", stk, varManager, vc, store, l) + s := newSession(ctx, d, varManager, launchProperties, []*thread{t}) + + return d, s, t +} + +type testEventHandler struct { + ch chan *Event +} + +func newTestEventHandler() *testEventHandler { + return &testEventHandler{ + ch: make(chan *Event), + } +} + +func (eh *testEventHandler) HandleEvent(event Event) { + eh.ch <- &event +} + +func (eh *testEventHandler) Next(duration time.Duration) *Event { + select { + case e := <-eh.ch: + return e + case <-time.After(duration): + return nil + } +} + +func (eh *testEventHandler) NextBlocking() *Event { + return <-eh.ch +} + +func (eh *testEventHandler) WaitFor(ctx context.Context, eventType EventType) *Event { + for { + select { + case e := <-eh.ch: + if e.Type == eventType { + return e + } + case <-ctx.Done(): + return nil + } + } +} + +func (eh *testEventHandler) IgnoreAll(ctx context.Context) { + go func() { + for { + select { + case <-eh.ch: + case <-ctx.Done(): + return + } + } + }() +} + +func (eh *testEventHandler) Do(task func() error) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + eh.IgnoreAll(ctx) + + return task() +} + +type testStack struct { + events []*topdown.Event + index int + result rego.ResultSet + closed bool +} + +func newTestStack(events ...*topdown.Event) *testStack { + return &testStack{ + events: events, + index: -1, + } +} + +func (*testStack) Enabled() bool { + return true +} + +func (*testStack) TraceEvent(_ topdown.Event) { +} + +func (*testStack) Config() topdown.TraceConfig { + return topdown.TraceConfig{} +} + +func (ts *testStack) Current() (int, *topdown.Event) { + if ts.index < 0 || ts.index >= len(ts.events) { + return -1, nil + } + return ts.index, ts.events[ts.index] +} + +func (ts *testStack) Event(i int) *topdown.Event { + if i >= 0 && i < len(ts.events) { + return ts.events[i] + } + return nil +} + +func (ts *testStack) Next() (int, *topdown.Event) { + if ts.closed || ts.index >= len(ts.events)-1 { + ts.index++ + return -1, nil + } + ts.index++ + return ts.Current() +} + +func (ts *testStack) Result() rego.ResultSet { + return ts.result +} + +func (ts *testStack) Close() error { + ts.closed = true + return nil +} + +func TestDebuggerCustomBuiltIn(t *testing.T) { + ctx := context.Background() + + decl := ®o.Function{ + Name: "my.builtin", + Description: "My built-in", + Decl: types.NewFunction( + types.Args(types.S, types.S), + types.S, + ), + } + + fn := func(_ rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error) { + aStr, err := builtins.StringOperand(a.Value, 1) + if err != nil { + return nil, err + } + + bStr, err := builtins.StringOperand(b.Value, 2) + if err != nil { + return nil, err + } + + return ast.StringTerm(fmt.Sprintf("%s+%s", aStr, bStr)), nil + } + + props := LaunchEvalProperties{ + Query: `x := my.builtin("hello", "world")`, + } + + exp := `[{"expressions":[{"value":true,"text":"x := my.builtin(\"hello\", \"world\")","location":{"row":1,"col":1}}],"bindings":{"x":"\"hello\"+\"world\""}}]` + + eh := newTestEventHandler() + + d := NewDebugger(SetEventHandler(eh.HandleEvent)) + + s, err := d.LaunchEval(ctx, props, RegoOption(rego.Function2(decl, fn))) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := s.ResumeAll(); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // wait for result + if e := eh.WaitFor(ctx, TerminatedEventType); e == nil { + t.Fatal("Expected terminated event") + } + + ts, err := s.Threads() + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + res := ts[0].(*thread).stack.Result() + bs, err := json.Marshal(res) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + actual := string(bs) + if actual != exp { + t.Fatalf("Expected:\n\n%v\n\nbut got:\n\n%v", exp, actual) + } +} diff --git a/third_party/opa/v1/debug/event.go b/third_party/opa/v1/debug/event.go new file mode 100644 index 000000000000..2218c92cf781 --- /dev/null +++ b/third_party/opa/v1/debug/event.go @@ -0,0 +1,55 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import ( + "fmt" + "strings" + + "github.com/open-policy-agent/opa/v1/topdown" +) + +type EventType string + +const ( + ExceptionEventType = "exception" + StdoutEventType = "stdout" + StoppedEventType = "stopped" + TerminatedEventType = "terminated" + ThreadEventType = "thread" +) + +type Event struct { + Type EventType + Thread ThreadID + Message string + stackIndex int + stackEvent *topdown.Event +} + +func (d Event) String() string { + buf := new(strings.Builder) + + buf.WriteString(fmt.Sprintf("%s{", d.Type)) + buf.WriteString(fmt.Sprintf("thread=%d", d.Thread)) + + if d.Message != "" { + buf.WriteString(fmt.Sprintf(", message=%q", d.Message)) + } + + if d.stackEvent != nil { + buf.WriteString(fmt.Sprintf(", stackIndex=%d", d.stackIndex)) + } + + buf.WriteString("}") + + return buf.String() +} + +type EventHandler func(Event) + +func newNopEventHandler() EventHandler { + return func(_ Event) {} +} diff --git a/third_party/opa/v1/debug/frame.go b/third_party/opa/v1/debug/frame.go new file mode 100644 index 000000000000..0b530d5899d1 --- /dev/null +++ b/third_party/opa/v1/debug/frame.go @@ -0,0 +1,95 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import ( + "fmt" + + "github.com/open-policy-agent/opa/v1/ast/location" + "github.com/open-policy-agent/opa/v1/topdown" +) + +type FrameID int + +type StackFrame interface { + // ID returns the unique identifier for the frame. + ID() FrameID + + // Name returns the human-readable name of the frame. + Name() string + + // Location returns the location of the frame in the source code. + Location() *location.Location + + // Thread returns the ID of the thread that the frame is associated with. + Thread() ThreadID + + // String returns a human-readable string representation of the frame. + String() string + + // Equal returns true if the frame is equal to the other frame. + Equal(other StackFrame) bool +} + +type stackFrame struct { + id FrameID + name string + location *location.Location + thread ThreadID + + e *topdown.Event + stackIndex int +} + +func (f *stackFrame) ID() FrameID { + return f.id +} + +func (f *stackFrame) Name() string { + return f.name +} + +func (f *stackFrame) Location() *location.Location { + return f.location +} + +func (f *stackFrame) Thread() ThreadID { + return f.thread +} + +func (f *stackFrame) String() string { + return fmt.Sprintf("{id: %d, name: %v, location: %v}", f.id, f.name, f.location) +} + +func (f *stackFrame) Equal(other StackFrame) bool { + if f.ID() != other.ID() { + return false + } + if f.Name() != other.Name() { + return false + } + if !f.Location().Equal(other.Location()) { + return false + } + if f.Thread() != other.Thread() { + return false + } + return true +} + +// StackTrace represents a StackFrame stack. +type StackTrace []StackFrame + +func (s StackTrace) Equal(other StackTrace) bool { + if len(s) != len(other) { + return false + } + for i := range s { + if !s[i].Equal(other[i]) { + return false + } + } + return true +} diff --git a/third_party/opa/v1/debug/latch.go b/third_party/opa/v1/debug/latch.go new file mode 100644 index 000000000000..c58c26fe947a --- /dev/null +++ b/third_party/opa/v1/debug/latch.go @@ -0,0 +1,38 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import "sync" + +type latch struct { + paused bool + waitGroup sync.WaitGroup + lock sync.Mutex +} + +func (l *latch) block() { + l.lock.Lock() + defer l.lock.Unlock() + if !l.paused { + l.waitGroup.Add(1) + l.paused = true + } +} + +func (l *latch) unblock() { + l.lock.Lock() + defer l.lock.Unlock() + if l.paused { + l.waitGroup.Done() + l.paused = false + } +} + +func (l *latch) wait() { + l.waitGroup.Wait() +} + +func (*latch) Close() { +} diff --git a/third_party/opa/v1/debug/thread.go b/third_party/opa/v1/debug/thread.go new file mode 100644 index 000000000000..b688de503ceb --- /dev/null +++ b/third_party/opa/v1/debug/thread.go @@ -0,0 +1,534 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import ( + "context" + "errors" + "strconv" + "sync" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ast/location" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/topdown" +) + +type threadState any + +type eventAction string + +const ( + nopAction eventAction = "nop" + breakAction eventAction = "break" + skipAction eventAction = "skip" + stopAction eventAction = "stop" +) + +type eventHandler func(t *thread, stackIndex int, e *topdown.Event, s threadState) (eventAction, threadState, error) + +type ThreadID int + +// Thread represents a single thread of execution. +type Thread interface { + // ID returns the unique identifier for the thread. + ID() ThreadID + // Name returns the human-readable name of the thread. + Name() string +} + +type thread struct { + id ThreadID + name string + stack stack + eventHandler eventHandler + breakpointLatch latch + stopped bool + state threadState + varManager *variableManager + virtualCache topdown.VirtualCache + store storage.Store + logger logging.Logger + mtx sync.Mutex +} + +func (t *thread) ID() ThreadID { + return t.id +} + +func (t *thread) Name() string { + return t.name +} + +func newThread(id ThreadID, name string, stack stack, varManager *variableManager, virtualCache topdown.VirtualCache, + store storage.Store, logger logging.Logger) *thread { + t := &thread{ + id: id, + name: name, + stack: stack, + logger: logger, + varManager: varManager, + virtualCache: virtualCache, + store: store, + } + + // Threads are always created in a paused state. + _ = t.pause() + + return t +} + +func (t *thread) run(ctx context.Context) error { + for { + if t.stopped { + return nil + } + + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + t.logger.Debug("Waiting on breakpoint latch") + t.breakpointLatch.wait() + t.logger.Debug("Breakpoint latch released") + + // The thread could get resumed by another goroutine before the eventHandler returns, so we preemptively lock the + // breakpoint latch and unlock it of we're not supposed to break. + t.logger.Debug("Preemptively blocking breakpoint latch") + t.breakpointLatch.block() + + a, err := t.stepIn() + if err != nil { + t.stopped = true + return err + } + + if a == breakAction { + t.logger.Debug("break requested; not unblocking breakpoint latch") + } else { + t.logger.Debug("No break requested; unblocking breakpoint latch") + t.breakpointLatch.unblock() + } + } +} + +func (t *thread) pause() error { + t.logger.Debug("Pausing thread: %d", t.id) + t.breakpointLatch.block() + return nil +} + +func (t *thread) resume() error { + t.logger.Debug("Resuming thread: %d", t.id) + t.breakpointLatch.unblock() + return nil +} + +func (t *thread) current() (int, *topdown.Event, error) { + i, e := t.stack.Current() + return i, e, nil +} + +func (t *thread) stepIn() (eventAction, error) { + t.mtx.Lock() + defer t.mtx.Unlock() + + if t.stopped { + return nopAction, errors.New("thread stopped") + } + + var a eventAction + for { + i, e := t.stack.Next() + t.logger.Debug("Step-in on event: #%d", i) + + var s threadState + var err error + a, s, err = t.eventHandler(t, i, e, t.state) + if err != nil { + return nopAction, err + } + t.state = s + + if a != skipAction { + break + } + } + + return a, nil +} + +func (t *thread) stepOver() error { + t.mtx.Lock() + defer t.mtx.Unlock() + + if t.stopped { + return errors.New("thread stopped") + } + + _, startE, err := t.current() + if err != nil { + return err + } + + hasExited := startE != nil && (startE.Op == topdown.ExitOp || startE.Op == topdown.FailOp) + + baseQueryVisited := false +Loop: + for { + i, e := t.stack.Next() + t.logger.Debug("Step-over on event #%d:\n%v", i, e) + + if e != nil && e.QueryID == 0 { + baseQueryVisited = true + } + + a, s, err := t.eventHandler(t, i, e, t.state) + if err != nil { + return err + } + t.state = s + + if a == skipAction { + continue + } + + var qid uint64 + if e != nil { + qid = e.QueryID + } + + switch { + case startE == nil: + t.logger.Debug("Resuming on query: %d; first event", qid) + break Loop + case a == breakAction: + t.logger.Debug("Resuming on query: %d; break-action", qid) + break Loop + case e == nil: + t.logger.Debug("Resuming on query: %d; no event", qid) + break Loop + case e.QueryID == 0: + t.logger.Debug("Continuing past query: %d; base-query", qid) + case e.QueryID <= startE.QueryID: + t.logger.Debug("Resuming on query: %d; start-query: %d", qid, startE.QueryID) + break Loop + case hasExited && e.Op == topdown.EnterOp && qid != startE.QueryID: + // We have exited the current query scope, and entered a new one; probably a relative partial rule. + t.logger.Debug("Resuming on query: %d; query-exited", qid) + break Loop + case baseQueryVisited: + t.logger.Debug("Resuming on query: %d; base-query visited", qid) + break Loop + default: + t.logger.Debug("Continuing past query: %d", qid) + } + } + + return nil +} + +func (t *thread) stepOut() error { + t.mtx.Lock() + defer t.mtx.Unlock() + + if t.stopped { + return errors.New("thread stopped") + } + + _, c, err := t.current() + if err != nil { + return err + } + + for { + i, e := t.stack.Next() + t.logger.Debug("Step-out on event: #%d", i) + + a, s, err := t.eventHandler(t, i, e, t.state) + if err != nil { + return err + } + t.state = s + + if a == skipAction { + continue + } + + var qid uint64 + if e != nil { + qid = e.QueryID + } + + if a == breakAction || e == nil || c == nil || qid < c.QueryID { + t.logger.Debug("Resuming on query: %d", qid) + break + } + t.logger.Debug("Continuing past query: %d", qid) + } + + return nil +} + +func (t *thread) stackEvents(from int) []*topdown.Event { + var events []*topdown.Event + for { + e := t.stack.Event(from) + if e == nil { + break + } + events = append(events, e) + from++ + } + return events +} + +// Scope represents the variable state of a StackFrame. +type Scope interface { + // Name returns the human-readable name of the scope. + Name() string + + // NamedVariables returns the number of named variables in the scope. + NamedVariables() int + + // VariablesReference returns a reference to the variables in the scope. + VariablesReference() VarRef + + // Location returns the in-source location of the scope. + Location() *location.Location +} + +type scope struct { + name string + namedVariables int + variablesReference VarRef + location *location.Location +} + +func (s scope) Name() string { + return s.name +} + +func (s scope) NamedVariables() int { + return s.namedVariables +} + +func (s scope) VariablesReference() VarRef { + return s.variablesReference +} + +func (s scope) Location() *location.Location { + return s.location +} + +func (t *thread) scopes(stackIndex int) []Scope { + e := t.stack.Event(stackIndex) + if e == nil { + return nil + } + + scopes := make([]Scope, 0, 3) + + // TODO: Clients are expected to keep track of fetched scopes and variable references (vs-code does), + // but it wouldn't hurt to not register the same var-getter callback more than once. + localScope := scope{ + name: "Locals", + namedVariables: e.Locals.Len(), + variablesReference: t.localVars(e), + location: e.Location, + } + scopes = append(scopes, localScope) + + if t.virtualCache != nil { + // We only show "global vars" from the virtual cache when at the top of the stack, + // to not need to store a copy for every frame. + top, _ := t.stack.Current() + if stackIndex == top { + keys := t.virtualCache.Keys() + virtualCacheScope := scope{ + name: "Virtual Cache", + namedVariables: len(keys), + variablesReference: t.virtualCacheVars(keys, t.virtualCache), + } + scopes = append(scopes, virtualCacheScope) + } + } + + if e.Input() != nil { + inputScope := scope{ + name: "Input", + namedVariables: 1, + variablesReference: t.inputVars(e), + } + scopes = append(scopes, inputScope) + } else { + inputScope := scope{ + name: "Input (not provided)", + namedVariables: 0, + } + scopes = append(scopes, inputScope) + } + + if t.store != nil { + dataScope := scope{ + name: "Data", + namedVariables: 1, + variablesReference: t.dataVars(), + } + scopes = append(scopes, dataScope) + } else { + dataScope := scope{ + name: "Data (not provided)", + namedVariables: 0, + } + scopes = append(scopes, dataScope) + } + + if rs := t.stack.Result(); rs != nil { + resultScope := scope{ + name: "Result Set", + namedVariables: 1, + variablesReference: t.resultVars(rs), + } + scopes = append(scopes, resultScope) + } + + return scopes +} + +func (t *thread) localVars(e *topdown.Event) VarRef { + return t.varManager.addVars(func() []namedVar { + if e == nil { + return nil + } + + vars := make([]namedVar, 0, e.Locals.Len()) + + e.Locals.Iter(func(k, v ast.Value) bool { + name := k.(ast.Var) + variable := namedVar{ + name: string(name), + value: v, + } + + meta, ok := e.LocalMetadata[name] + if ok { + variable.name = string(meta.Name) + } + + vars = append(vars, variable) + return false + }) + + return vars + }) +} + +func (t *thread) virtualCacheVars(keys []ast.Ref, cache topdown.VirtualCache) VarRef { + return t.varManager.addVars(func() []namedVar { + if cache == nil { + return nil + } + + vars := make([]namedVar, 0, len(keys)) + for _, key := range keys { + term, undefined := cache.Get(key) + var value ast.Value + if undefined { + value = ast.NullValue + } else { + value = term.Value + } + + variable := namedVar{ + name: key.String(), + value: value, + } + vars = append(vars, variable) + } + + return vars + }) +} + +func (t *thread) inputVars(e *topdown.Event) VarRef { + return t.varManager.addVars(func() []namedVar { + input := e.Input() + if input == nil { + return nil + } + + return []namedVar{{name: "input", value: input.Value}} + }) +} + +func (t *thread) dataVars() VarRef { + return t.varManager.addVars(func() []namedVar { + ctx := context.Background() + d, err := storage.ReadOne(ctx, t.store, storage.Path{}) + if err != nil { + return nil + } + v, err := ast.InterfaceToValue(d) + if err != nil { + return nil + } + return []namedVar{{name: "data", value: v}} + }) +} + +func (t *thread) resultVars(rs rego.ResultSet) VarRef { + vars := make([]namedVar, 0, len(rs)) + for i, result := range rs { + bindings, err := ast.InterfaceToValue(result.Bindings) + if err != nil { + continue + } + + expressions := &ast.Array{} + for _, expr := range result.Expressions { + t := ast.StringTerm(expr.Text) + v, err := ast.InterfaceToValue(expr.Value) + if err != nil { + continue + } + expressions = expressions.Append(ast.ObjectTerm( + ast.Item(ast.InternedTerm("text"), t), + ast.Item(ast.InternedTerm("value"), ast.NewTerm(v)), + )) + } + + res := ast.NewObject( + ast.Item(ast.InternedTerm("bindings"), ast.NewTerm(bindings)), + ast.Item(ast.InternedTerm("expressions"), ast.NewTerm(expressions)), + ) + + vars = append(vars, namedVar{ + name: strconv.Itoa(i), + value: res, + }) + } + + return t.varManager.addVars(func() []namedVar { + return vars + }) +} + +func (t *thread) close() error { + t.stopped = true + t.breakpointLatch.Close() + return t.stack.Close() +} + +func (t *thread) done() bool { + t.mtx.Lock() + defer t.mtx.Unlock() + + return t.stopped || !t.stack.Enabled() +} diff --git a/third_party/opa/v1/debug/trace.go b/third_party/opa/v1/debug/trace.go new file mode 100644 index 000000000000..f37c245fc372 --- /dev/null +++ b/third_party/opa/v1/debug/trace.go @@ -0,0 +1,109 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import ( + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/topdown" +) + +type stack interface { + topdown.QueryTracer + Current() (int, *topdown.Event) + Event(i int) *topdown.Event + Next() (int, *topdown.Event) + Result() rego.ResultSet + Close() error +} + +type debugTracer struct { + history []*topdown.Event + eventChan chan *topdown.Event + waitChan chan bool + enabled bool + resultSet rego.ResultSet +} + +func newDebugTracer() *debugTracer { + return &debugTracer{ + eventChan: make(chan *topdown.Event), + waitChan: make(chan bool), + enabled: true, + } +} + +func (dt *debugTracer) Enabled() bool { + return dt.enabled +} + +func (dt *debugTracer) TraceEvent(e topdown.Event) { + if !dt.enabled { + return + } + + defer func() { + if recover() != nil { + dt.enabled = false + } + }() + + dt.eventChan <- &e + // Block until the consumer wants another event, so that evaluation isn't "ahead" + <-dt.waitChan +} + +func (*debugTracer) Config() topdown.TraceConfig { + return topdown.TraceConfig{ + PlugLocalVars: true, + } +} + +func (dt *debugTracer) Current() (int, *topdown.Event) { + stackLength := len(dt.history) + if stackLength > 0 { + return stackLength - 1, dt.history[len(dt.history)-1] + } + return -1, nil +} + +func (dt *debugTracer) Event(i int) *topdown.Event { + if i >= 0 && i < len(dt.history) { + return dt.history[i] + } + return nil +} + +func (dt *debugTracer) Next() (int, *topdown.Event) { + if !dt.enabled { + return -1, nil + } + + if len(dt.history) > 0 { + // We don't need to unblock the producer for the first event. + dt.waitChan <- true + } + + e, ok := <-dt.eventChan + if ok { + dt.history = append(dt.history, e) + return len(dt.history) - 1, e + } + return len(dt.history) - 1, nil +} + +func (dt *debugTracer) Result() rego.ResultSet { + return dt.resultSet +} + +func (dt *debugTracer) Close() error { + if !dt.enabled { + return nil + } + + dt.enabled = false + close(dt.eventChan) + close(dt.waitChan) + return nil +} diff --git a/third_party/opa/v1/debug/variable.go b/third_party/opa/v1/debug/variable.go new file mode 100644 index 000000000000..bccb4b54a013 --- /dev/null +++ b/third_party/opa/v1/debug/variable.go @@ -0,0 +1,182 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package debug + +import ( + "fmt" + "slices" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" +) + +type Variable interface { + // Name returns the name of the variable. + Name() string + + // Type returns the type of the variable. + Type() string + + // Value returns the value of the variable. + Value() string + + // VariablesReference returns a reference to the variables that are children of this variable. + // E.g. this variable is a collection, such as an array, set, or object. + VariablesReference() VarRef +} + +type namedVar struct { + name string + value ast.Value +} + +func (nv namedVar) Name() string { + return nv.name +} + +func (nv namedVar) Type() string { + return valueTypeName(nv.value) +} + +func (nv namedVar) Value() string { + return truncatedString(nv.value.String(), 100) +} + +type variableGetter func() []namedVar + +type variableManager struct { + getters []variableGetter +} + +func newVariableManager() *variableManager { + return &variableManager{} +} + +func (vs *variableManager) addVars(getter variableGetter) VarRef { + vs.getters = append(vs.getters, getter) + return VarRef(len(vs.getters)) +} + +type VarRef int + +type variable struct { + v namedVar + ref VarRef +} + +func (v variable) Name() string { + return v.v.Name() +} + +func (v variable) Type() string { + return v.v.Type() +} + +func (v variable) Value() string { + return v.v.Value() +} + +func (v variable) VariablesReference() VarRef { + return v.ref +} + +func (vs *variableManager) vars(varRef VarRef) ([]Variable, error) { + i := int(varRef) - 1 + if i < 0 || i >= len(vs.getters) { + return nil, fmt.Errorf("invalid variable reference: %d", varRef) + } + + namedVar := vs.getters[i]() + vars := make([]Variable, len(namedVar)) + + for i, nv := range namedVar { + vars[i] = variable{ + v: nv, + ref: vs.subVars(nv.value), + } + } + + slices.SortFunc(vars, func(a, b Variable) int { + return strings.Compare(a.Name(), b.Name()) + }) + + return vars, nil +} + +func truncatedString(s string, max int) string { + if len(s) > max { + return s[:max-2] + "..." + } + return s +} + +func valueTypeName(v ast.Value) string { + switch v.(type) { + case ast.Null: + return "null" + case ast.Boolean: + return "boolean" + case ast.Number: + return "number" + case ast.String: + return "string" + case *ast.Array: + return "array" + case ast.Object: + return "object" + case ast.Set: + return "set" + case ast.Ref: + return "ref" + default: + return "unknown" + } +} + +func (vs *variableManager) subVars(v ast.Value) VarRef { + if obj, ok := v.(ast.Object); ok { + vars := make([]namedVar, 0, obj.Len()) + if err := obj.Iter(func(k, v *ast.Term) error { + vars = append(vars, namedVar{ + name: k.String(), + value: v.Value, + }) + return nil + }); err != nil { + return 0 + } + return vs.addVars(func() []namedVar { + return vars + }) + } + + if arr, ok := v.(*ast.Array); ok { + vars := make([]namedVar, 0, arr.Len()) + for i := range arr.Len() { + vars = append(vars, namedVar{ + name: strconv.Itoa(i), + value: arr.Elem(i).Value, + }) + } + return vs.addVars(func() []namedVar { + return vars + }) + } + + if set, ok := v.(ast.Set); ok { + vars := make([]namedVar, 0, set.Len()) + for _, elem := range set.Slice() { + vars = append(vars, namedVar{ + value: elem.Value, + }) + } + return vs.addVars(func() []namedVar { + return vars + }) + } + + return 0 +} diff --git a/third_party/opa/v1/dependencies/deps.go b/third_party/opa/v1/dependencies/deps.go new file mode 100644 index 000000000000..1635e41545e6 --- /dev/null +++ b/third_party/opa/v1/dependencies/deps.go @@ -0,0 +1,464 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package dependencies + +import ( + "fmt" + "slices" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" +) + +// All returns the list of data ast.Refs that the given AST element depends on. +func All(x any) (resolved []ast.Ref, err error) { + var rawResolved []ast.Ref + switch x := x.(type) { + case *ast.Module, *ast.Package, *ast.Import, *ast.Rule, *ast.Head, ast.Body, *ast.Expr, *ast.With, *ast.Term, ast.Ref, ast.Object, *ast.Array, ast.Set, *ast.ArrayComprehension: + default: + return nil, fmt.Errorf("not an ast element: %v", x) + } + + visitor := ast.NewGenericVisitor(func(x any) bool { + switch x := x.(type) { + case *ast.Package, *ast.Import: + return true + case *ast.Module, *ast.Head, *ast.Expr, *ast.With, *ast.Term, ast.Object, *ast.Array, *ast.Set, *ast.ArrayComprehension: + case *ast.Rule: + rawResolved = append(rawResolved, ruleDeps(x)...) + return true + case ast.Body: + vars := ast.NewVarVisitor() + vars.Walk(x) + + arr := ast.NewArray() + for v := range vars.Vars() { + if v.IsWildcard() { + continue + } + arr = arr.Append(ast.NewTerm(v)) + } + + // The analysis will discard variables that are not used in + // direct comparisons or in the output. Since lone Bodies are + // often queries, we want all the variables to be in the output. + r := &ast.Rule{ + Head: &ast.Head{Name: ast.Var("_"), Value: ast.NewTerm(arr)}, + Body: x, + } + rawResolved = append(rawResolved, ruleDeps(r)...) + return true + case ast.Ref: + rawResolved = append(rawResolved, x) + } + return false + }) + visitor.Walk(x) + if len(rawResolved) == 0 { + return nil, nil + } + + return dedup(rawResolved), nil +} + +// Minimal returns the list of data ast.Refs that the given AST element depends on. +// If an AST element depends on a ast.Ref that is a prefix of another dependency, the +// ast.Ref that is the prefix of the other will be the only one in the returned list. +// +// As an example, if an element depends on data.x and data.x.y, only data.x will +// be in the returned list. +func Minimal(x any) (resolved []ast.Ref, err error) { + rawResolved, err := All(x) + if err != nil { + return nil, err + } + + if len(rawResolved) == 0 { + return nil, nil + } + + return filter(rawResolved, func(a, b ast.Ref) bool { + return b.HasPrefix(a) + }), nil +} + +// Base returns the list of base data documents that the given AST element depends on. +// +// The returned refs are always constant and are truncated at any point where they become +// dynamic. That is, a ref like data.a.b[x] will be truncated to data.a.b. +func Base(compiler *ast.Compiler, x any) ([]ast.Ref, error) { + baseRefs := newRefSet() + err := base(compiler, x, baseRefs) + if err != nil { + return nil, err + } + + return dedup(baseRefs.toSlice()), nil +} + +func base(compiler *ast.Compiler, x any, baseRefs *dependencies) error { + refs, err := Minimal(x) + if err != nil { + return err + } + + for _, r := range refs { + r = r.ConstantPrefix() + if rules := compiler.GetRules(r); len(rules) > 0 { + for _, rule := range rules { + if baseRefs.visited(rule) { + continue + } + baseRefs.visit(rule) + if err := base(compiler, rule, baseRefs); err != nil { + panic("not reached") + } + + } + } else { + baseRefs.add(r) + } + } + + return nil +} + +// Virtual returns the list of virtual data documents that the given AST element depends +// on. +// +// The returned refs are always constant and are truncated at any point where they become +// dynamic. That is, a ref like data.a.b[x] will be truncated to data.a.b. +func Virtual(compiler *ast.Compiler, x any) ([]ast.Ref, error) { + virtualRefs := newRefSet() + err := virtual(compiler, x, virtualRefs) + if err != nil { + return nil, err + } + + return dedup(virtualRefs.toSlice()), nil +} + +func virtual(compiler *ast.Compiler, x any, virtualRefs *dependencies) error { + refs, err := Minimal(x) + if err != nil { + return err + } + + for _, r := range refs { + r = r.ConstantPrefix() + if rules := compiler.GetRules(r); len(rules) > 0 { + for _, rule := range rules { + if virtualRefs.visited(rule) { + continue + } + virtualRefs.visit(rule) + err := virtual(compiler, rule, virtualRefs) + if err != nil { + panic("not reached") + } + + virtualRefs.add(rule.Path()) + } + } + } + + return nil +} + +type dependencies struct { + refs *util.HasherMap[ast.Ref, ast.Ref] + visitedRules *util.TypedHashMap[*ast.Rule, *ast.Rule] +} + +func newRefSet() *dependencies { + return &dependencies{ + refs: util.NewHasherMap[ast.Ref, ast.Ref](ast.RefEqual), + visitedRules: util.NewTypedHashMap[*ast.Rule, *ast.Rule]( + (*ast.Rule).Equal, + nil, + ruleHash, + nil, + nil, + ), + } +} + +func ruleHash(r *ast.Rule) int { + return r.Ref().Hash() +} + +func (rs *dependencies) add(r ast.Ref) { + rs.refs.Put(r, r) +} + +func (rs *dependencies) visit(rule *ast.Rule) { + rs.visitedRules.Put(rule, rule) +} + +func (rs *dependencies) visited(rule *ast.Rule) bool { + _, found := rs.visitedRules.Get(rule) + return found +} + +func (rs *dependencies) toSlice() []ast.Ref { + result := make([]ast.Ref, 0, rs.refs.Len()) + rs.refs.Iter(func(k, _ ast.Ref) bool { + result = append(result, k) + return false + }) + return result +} + +func dedup(refs []ast.Ref) []ast.Ref { + slices.SortFunc(refs, ast.RefCompare) + + return slices.CompactFunc(refs, ast.RefEqual) +} + +// filter removes all items from the list that cause pred to return true. It is +// called on adjacent pairs of elements, and the one passed as the second argument +// to pred is considered the current one being examined. The first argument will +// be the element immediately preceding it. +func filter(rs []ast.Ref, pred func(ast.Ref, ast.Ref) bool) (filtered []ast.Ref) { + if len(rs) == 0 { + return nil + } + + last := rs[0] + filtered = append(filtered, last) + for i := 1; i < len(rs); i++ { + cur := rs[i] + if pred(last, cur) { + continue + } + + filtered = append(filtered, cur) + last = cur + } + + return filtered +} + +// FIXME(tsandall): this logic should be revisited as it seems overly +// complicated. It should be possible to compute all dependencies in two +// passes: +// +// 1. perform syntactic unification on vars +// 2. gather all refs rooted at data after plugging the head with substitution +// from (1) +func ruleDeps(rule *ast.Rule) (resolved []ast.Ref) { + vars, others := extractEq(rule.Body) + joined := joinVarRefs(vars) + + headVars := rule.Head.Vars() + headRefs, others := resolveOthers(others, headVars, joined) + + resolveRef := func(r ast.Ref) bool { + resolved = append(resolved, expandRef(r, joined)...) + return false + } + + varVisitor := ast.NewVarVisitor().WithParams(ast.VarVisitorParams{SkipRefHead: true}) + // Clean up whatever refs are remaining among the other expressions. + for _, expr := range others { + ast.WalkRefs(expr, resolveRef) + varVisitor.Walk(expr) + } + + // If a reference ending in a header variable is a prefix of an already + // resolved reference, skip it and simply walk the nodes below it. + visitor := &skipVisitor{fn: resolveRef} + for _, r := range headRefs { + if !containsPrefix(resolved, r) { + resolved = append(resolved, r.Copy()) + } + visitor.skipped = false + ast.NewGenericVisitor(visitor.Visit).Walk(r) + } + + usedVars := varVisitor.Vars() + + // Vars included in refs must be counted as used. + ast.WalkRefs(rule.Body, func(r ast.Ref) bool { + for i := 1; i < len(r); i++ { + if v, ok := r[i].Value.(ast.Var); ok { + usedVars.Add(v) + } + } + return false + }) + + resolveRemainingVars(joined, visitor, usedVars, headVars) + return resolved +} + +// Extract the equality expressions from each rule, they contain +// the potential split references. In order to be considered for +// joining, an equality must have a variable on one side and a +// reference on the other. Any other construct is thrown into +// the others list to be resolved later. +func extractEq(exprs ast.Body) (vars map[ast.Var][]ast.Ref, others []*ast.Expr) { + vars = map[ast.Var][]ast.Ref{} + for v := range exprs.Vars(ast.VarVisitorParams{}) { + vars[v] = nil + } + + for _, expr := range exprs { + if !expr.IsEquality() { + others = append(others, expr) + continue + } + + terms := expr.Terms.([]*ast.Term) + left, right := terms[1], terms[2] + if l, ok := left.Value.(ast.Var); ok { + if r, ok := right.Value.(ast.Ref); ok { + vars[l] = append(vars[l], r) + continue + } + } else if r, ok := right.Value.(ast.Var); ok { + if l, ok := left.Value.(ast.Ref); ok { + vars[r] = append(vars[r], l) + continue + } + } + + others = append(others, expr) + } + return vars, others +} + +func expandRef(r ast.Ref, vars map[ast.Var]*util.HashMap) []ast.Ref { + head, rest := r[0], r[1:] + if ast.RootDocumentNames.Contains(head) { + return []ast.Ref{r} + } + + h := head.Value.(ast.Var) + rs, ok := vars[h] + if !ok { + return nil + } + + var expanded []ast.Ref + rs.Iter(func(a, _ util.T) bool { + ref := a.(ast.Ref) + expanded = append(expanded, append(ref.Copy(), rest...)) + return false + }) + return expanded +} + +func joinVarRefs(vars map[ast.Var][]ast.Ref) map[ast.Var]*util.HashMap { + joined := map[ast.Var]*util.HashMap{} + for v := range vars { + joined[v] = util.NewHashMap(refEq, refHash) + } + + done := false + for !done { + done = true + for v, rs := range vars { + for _, r := range rs { + head, rest := r[0], r[1:] + if ast.RootDocumentNames.Contains(head) { + if _, ok := joined[v].Get(r); !ok { + joined[v].Put(r, struct{}{}) + done = false + } + continue + } + + h, ok := head.Value.(ast.Var) + if !ok { + panic("not reached") + } + + joined[h].Iter(func(a, _ util.T) bool { + jr := a.(ast.Ref) + join := append(jr.Copy(), rest...) + if _, ok := joined[v].Get(join); !ok { + joined[v].Put(join, struct{}{}) + done = false + } + return false + }) + } + } + } + + return joined +} + +func resolveOthers(others []*ast.Expr, headVars ast.VarSet, joined map[ast.Var]*util.HashMap) (headRefs []ast.Ref, leftover []*ast.Expr) { + for _, expr := range others { + if term, ok := expr.Terms.(*ast.Term); ok { + if r, ok := term.Value.(ast.Ref); ok { + end := r[len(r)-1] + v, ok := end.Value.(ast.Var) + if ok && headVars.Contains(v) { + headRefs = append(headRefs, expandRef(r, joined)...) + continue + } + } + } + + leftover = append(leftover, expr) + } + + return headRefs, leftover +} + +func resolveRemainingVars(joined map[ast.Var]*util.HashMap, visitor *skipVisitor, usedVars ast.VarSet, headVars ast.VarSet) { + for v, refs := range joined { + skipped := false + + if headVars.Contains(v) || refs.Len() > 1 || usedVars.Contains(v) { + skipped = true + } + + refs.Iter(func(a, _ util.T) bool { + visitor.skipped = skipped + r := a.(ast.Ref) + ast.NewGenericVisitor(visitor.Visit).Walk(r) + return false + }) + } +} + +func containsPrefix(refs []ast.Ref, r ast.Ref) bool { + for _, ref := range refs { + if ref.HasPrefix(r) { + return true + } + } + return false +} + +func refEq(a, b util.T) bool { + ar, aok := a.(ast.Ref) + br, bok := b.(ast.Ref) + return aok && bok && ar.Equal(br) +} + +func refHash(a util.T) int { + return a.(ast.Ref).Hash() +} + +type skipVisitor struct { + fn func(ast.Ref) bool + skipped bool +} + +func (sv *skipVisitor) Visit(v any) bool { + if sv.skipped { + if r, ok := v.(ast.Ref); ok { + return sv.fn(r) + } + } + + sv.skipped = true + return false +} diff --git a/third_party/opa/v1/dependencies/deps_bench_test.go b/third_party/opa/v1/dependencies/deps_bench_test.go new file mode 100644 index 000000000000..e30a1d0ab3cf --- /dev/null +++ b/third_party/opa/v1/dependencies/deps_bench_test.go @@ -0,0 +1,82 @@ +package dependencies + +import ( + "fmt" + "strconv" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func BenchmarkBase(b *testing.B) { + ruleCounts := []int{10, 20, 50} + for _, ruleCount := range ruleCounts { + b.Run(strconv.Itoa(ruleCount), func(b *testing.B) { + policy := makePolicy(ruleCount) + module := ast.MustParseModule(policy) + compiler := ast.NewCompiler() + if compiler.Compile(map[string]*ast.Module{"test": module}); compiler.Failed() { + b.Fatalf("Failed to compile policy: %v", compiler.Errors) + } + + ref := ast.MustParseRef("data.test.main") + + b.ResetTimer() + + for range b.N { + if _, err := Base(compiler, ref); err != nil { + b.Fatalf("Failed to compute base doc deps: %v", err) + } + } + }) + } +} + +func BenchmarkVirtual(b *testing.B) { + ruleCounts := []int{10, 20, 50} + for _, ruleCount := range ruleCounts { + b.Run(strconv.Itoa(ruleCount), func(b *testing.B) { + module := ast.MustParseModule(makePolicy(ruleCount)) + compiler := ast.NewCompiler() + if compiler.Compile(map[string]*ast.Module{"test": module}); compiler.Failed() { + b.Fatalf("Failed to compile policy: %v", compiler.Errors) + } + + ref := ast.MustParseRef("data.test.main") + + b.ResetTimer() + + for range b.N { + if _, err := Virtual(compiler, ref); err != nil { + b.Fatalf("Failed to compute virtual doc deps: %v", err) + } + } + }) + } +} + +// makePolicy constructs a policy with ruleCount number of rules. +// Each rule will depend on as many other rules as possible without creating circular dependencies. +func makePolicy(ruleCount int) string { + var b strings.Builder + b.WriteString("package test\n\n") + + b.WriteString("main if {\n") + for i := range ruleCount { + b.WriteString(fmt.Sprintf(" p_%d\n", i)) + } + b.WriteString("}\n\n") + + for i := range ruleCount { + b.WriteString(fmt.Sprintf("p_%d if {\n", i)) + for j := i + 1; j < ruleCount; j++ { + b.WriteString(fmt.Sprintf(" p_%d\n", j)) + } + b.WriteString(" input.x == 1\n") + b.WriteString(" input.y == 2\n") + b.WriteString(" input.z == 3\n") + b.WriteString("}\n") + } + return b.String() +} diff --git a/third_party/opa/v1/dependencies/deps_test.go b/third_party/opa/v1/dependencies/deps_test.go new file mode 100644 index 000000000000..8d260f9c0415 --- /dev/null +++ b/third_party/opa/v1/dependencies/deps_test.go @@ -0,0 +1,518 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package dependencies + +import ( + "sort" + "strconv" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +type testData struct { + ast string + min []string + full []string +} + +func TestDependencies(t *testing.T) { + tests := []testData{ + { + ast: `package a.b.c + import data.a.x + import data.a.y + + d if { + a = x + b = data.a.y + a = b + }`, + min: []string{"a.x", "a.y"}, + }, + { + ast: `package a.b.c + import data.a.x + + d if { + a = x + b = a.y + a = "4" + }`, + + min: []string{"a.x"}, + }, + { + ast: `package a.b.c + import data.a.x + + d if { + true = a.y + a = x + }`, + + min: []string{"a.x.y"}, + }, + { + ast: `package a.b.c + import data.a.x + + d = f if { + a = x.y + e = "foo" + f = [b | a[i].b = e + a[i].c = b] + }`, + + min: []string{"a.x.y[i].b", "a.x.y[i].c"}, + }, + { + ast: `package a.b.c + import data.a.x + + d[i] = f if { + x[i] + count([1 | x[i].foo = "foo"], f) + }`, + + min: []string{"a.x[i].foo"}, + }, + { + ast: `package a.b.c + import data.a.x + + d[i] = f if { + count([1 | x[i].foo = "foo"], f) + x[i] + }`, + + min: []string{"a.x[i].foo"}, + }, + { + ast: `package a.b.c + import data.a.x + + d[i] = f if { + b = x.y + b[i] + count([1 | x.y[_].foo = "foo"], f) + }`, + + min: []string{"a.x.y[i]", "a.x.y[_].foo"}, + }, + { + ast: `package a.b.c + import data.a.x + + d[i] = f if { + b = x.y + b[i] + count([1 | x.y[0].foo = "foo"], f) + }`, + + min: []string{"a.x.y[i]", "a.x.y[0].foo"}, + }, + { + ast: `package a.b.c + import data.a.x + + d contains i if { + x[i] + }`, + + min: []string{"a.x[i]"}, + }, + { + ast: `package a.b.c + import data.a.x + + d contains i if { + b = x.y + b[i] + }`, + + min: []string{"a.x.y[i]"}, + }, + { + ast: `package a.b.c + import data.a.x + + d[a] = b if { + a = x.y + b = x.z + x.y.z = "foo" + x.z.a.b = "fizz" + x.a.b = "bar" + }`, + + min: []string{"a.x.y", "a.x.z", "a.x.a.b"}, + full: []string{"a.x.y.z", "a.x.z.a.b"}, + }, + { + ast: `package a.b.c + import data.a.x + + f if { + a = x + b = a.y + c = b.z + d = c.a + e = d.b + e.c = "foo" + }`, + + min: []string{"a.x.y.z.a.b.c"}, + }, + { + ast: `package a.b.c + import data.a.x + import data.a.y + import data.j + + f if { + a = x + b = a.y + c = b.z + d = c.a + e = d.b + d = j + e.c = "foo" + a = y + e["foo"] = "bar" + }`, + + min: []string{"a.x", "a.y", "j"}, + full: []string{ + "a.x.y", + "a.x.y.z", + "a.x.y.z.a", + "a.x.y.z.a.b", + "a.x.y.z.a.b.c", + "a.x.y.z.a.b.foo", + "a.y.y", + "a.y.y.z", + "a.y.y.z.a", + "a.y.y.z.a.b", + "a.y.y.z.a.b.c", + "a.y.y.z.a.b.foo", + "j.b", + "j.b.c", + "j.b.foo", + }, + }, + { + ast: `package a.b.c + import data.a.x + import data.a.y + import data.j + + f if { + a = x + b = a.y + c = b.z + d = c.a + d.b = "foo" + } + + g if { + a = x.z.b + e = x + h = e.y + d = h.z + i = d.j + a = 9 + i = "bar" + }`, + + min: []string{ + "a.x.y.z.a.b", + "a.x.y.z.j", + "a.x.z.b", + }, + }, + { + ast: `package a.b.c + import data.a.x + import data.a.y + import data.a.z + import data.a.f + import data.a.g + + a[i] = [j, k] if { + b = x.y + y[b.z[0]] = "foo" + z[b.a.c][i] + f[j][b.a.b] = "foo" + g["foo"][k] + }`, + + min: []string{ + "a.x.y.z[0]", + "a.x.y.a.c", + "a.x.y.a.b", + "a.y[__local0__]", + "a.z[__local1__][i]", + "a.f[j][__local2__]", + "a.g.foo[k]", + }, + }, + { + ast: `package a.b.c + import data.a.x + + f if { + a = x + b = a.y + b = a.z + c = b.i + c = b.j + d = c.m + d = c.n + d.f = "foo" + }`, + + min: []string{"a.x.y", "a.x.z"}, + full: []string{ + "a.x.y.i", + "a.x.y.i.m", + "a.x.y.i.m.f", + "a.x.y.i.n", + "a.x.y.i.n.f", + "a.x.y.j", + "a.x.y.j.m", + "a.x.y.j.m.f", + "a.x.y.j.n", + "a.x.y.j.n.f", + "a.x.z.i", + "a.x.z.i.m", + "a.x.z.i.m.f", + "a.x.z.i.n", + "a.x.z.i.n.f", + "a.x.z.j", + "a.x.z.j.m", + "a.x.z.j.m.f", + "a.x.z.j.n", + "a.x.z.j.n.f", + }, + }, + { + ast: `package a.b.c + import data.a.x + + f = [z, g] if { + a = x + b = a.y.z + indexof(a.b, b, z) + c = b.e + indexof(c, a.b[a.c].c, g) + }`, + + min: []string{"a.x.y.z", "a.x.b", "a.x.c"}, + full: []string{"a.x.b[__local1__].c", "a.x.y.z.e"}, + }, + { + ast: `package a.b.c + import data.a.x + + f = [g] if { + a = x + b = a.y.z + c = b.e + d = a.u + indexof(b, a.c[d].c, g) + c = "foo" + }`, + + min: []string{"a.x.y.z", "a.x.u", "a.x.c[d].c"}, + full: []string{"a.x.y.z.e"}, + }, + { + ast: `package a.b.c + import data.a.x + + f if { + x + }`, + + min: []string{"a.x"}, + }, + } + + for n, test := range tests { + t.Run(strconv.Itoa(n), func(t *testing.T) { + module := ast.MustParseModuleWithOpts(test.ast, ast.ParserOptions{AllFutureKeywords: true}) + compiler := ast.NewCompiler() + if compiler.Compile(map[string]*ast.Module{"test": module}); compiler.Failed() { + t.Fatalf("Failed to compile policy: %v", compiler.Errors) + } + + var exp []ast.Ref + for _, e := range test.min { + r := ast.MustParseRef("data." + e) + exp = append(exp, r) + } + sort.Slice(exp, func(i, j int) bool { + return exp[i].Compare(exp[j]) < 0 + }) + + mod := compiler.Modules["test"] + minOfAll, full := runDeps(t, mod) + + // Test that we get the same result by analyzing all the + // rules separately. + var minRules, fullRules []ast.Ref + for _, rule := range mod.Rules { + m, f := runDeps(t, rule) + minRules = append(minRules, m...) + fullRules = append(fullRules, f...) + } + + assertRefSliceEq(t, exp, minOfAll) + assertRefSliceEq(t, exp, minRules) + + for _, full := range test.full { + r := ast.MustParseRef("data." + full) + exp = append(exp, r) + } + sort.Slice(exp, func(i, j int) bool { + return exp[i].Compare(exp[j]) < 0 + }) + + assertRefSliceEq(t, exp, full) + assertRefSliceEq(t, exp, fullRules) + }) + } +} + +func TestBaseAndVirtual(t *testing.T) { + mods := map[string]*ast.Module{ + "one": ast.MustParseModule(`package x + import rego.v1 + + y = "foo" + + z[x] = w if { + w = x + x = "bar" + y = x + }`), + "two": ast.MustParseModule(`package a + import rego.v1 + + b = {"buz": "bar"} + + c[a] = e if { + e = data.d[_] + data.x.z[a] = e[2] + }`), + } + + compiler := ast.NewCompiler() + if compiler.Compile(mods); compiler.Failed() { + t.Fatalf("Compilation failed: %v", compiler.Errors) + } + + body := ast.MustParseBody("x = data.a.c[y]") + base, err := Base(compiler, body) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expBase := ast.MustParseRef("data.d") + if len(base) != 1 || !expBase.Equal(base[0]) { + t.Errorf("Expected base ref %v, got %v", expBase, base) + } + + virtual, err := Virtual(compiler, body) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expVirtual := []ast.Ref{ast.MustParseRef("data.a.c"), ast.MustParseRef("data.x.y"), ast.MustParseRef("data.x.z")} + if len(virtual) != len(expVirtual) { + t.Errorf("Expected base refs %v, got %v", expVirtual, virtual) + } + + for i, r := range expVirtual { + if !r.Equal(virtual[i]) { + t.Fatalf("Expected base refs %v, got %v", expVirtual, virtual) + } + } +} + +func TestBase(t *testing.T) { + modules := map[string]*ast.Module{ + "test": ast.MustParseModule(` + package test + import rego.v1 + + p if { + input = x + x = y + y.z = "foo" + } + + q if { + input.a = "bar" + } + `), + } + + compiler := ast.NewCompiler() + compiler.Compile(modules) + if compiler.Failed() { + t.Fatal(compiler.Errors) + } + + body := ast.MustParseBody("data.test.p") + + refs, err := Base(compiler, body) + if err != nil { + t.Fatal(err) + } + + // TODO(tsandall): dependency analysis should be able to identify that full + // extent of input is not required here (only input.z and input.a are + // needed) + exp := []ast.Ref{ast.MustParseRef("input")} + + if len(exp) != 1 { + t.Fatalf("Expected %v but got %v", exp, refs) + } + + for i := range refs { + if refs[i].Compare(exp[i]) != 0 { + t.Fatalf("Expected %v but got: %v", exp, refs) + } + } + +} + +func runDeps(t *testing.T, x any) (min, full []ast.Ref) { + min, err := Minimal(x) + if err != nil { + t.Fatalf("Unexpected dependency error: %v", err) + } + + full, err = All(x) + if err != nil { + t.Fatalf("Unexpected dependency error: %v", err) + } + + return min, full +} + +// For some reason, reflect.DeepEqual doesn't work on Ref slices. +func assertRefSliceEq(t *testing.T, exp, result []ast.Ref) { + if len(result) != len(exp) { + t.Fatalf("Expected refs %v, got %v", exp, result) + } + + for i, e := range exp { + if !e.Equal(result[i]) { + t.Fatalf("Expected refs %v, got %v", exp, result) + } + } +} diff --git a/third_party/opa/v1/dependencies/doc.go b/third_party/opa/v1/dependencies/doc.go new file mode 100644 index 000000000000..a37c4ae3efdb --- /dev/null +++ b/third_party/opa/v1/dependencies/doc.go @@ -0,0 +1,7 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package dependencies provides functions for determining the set of ast.Refs that AST +// elements depend on. +package dependencies diff --git a/third_party/opa/v1/doc.go b/third_party/opa/v1/doc.go new file mode 100644 index 000000000000..b4991633ba31 --- /dev/null +++ b/third_party/opa/v1/doc.go @@ -0,0 +1,9 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package v1 implements the v1 API for the Open Policy Agent (OPA). +// The v1 API defaults to enforcing the v1 Rego syntax ([github.com/open-policy-agent/opa/v1/ast.RegoV1]). +// Most packages outside the v1 API are deprecated. These constitute the older v0 API, which defaults to the v0 Rego syntax ([github.com/open-policy-agent/opa/v1/ast.RegoV0]). +// The v0 API is provided as a means to ease transition to OPA 1.0 for 3rd party integrations, see [TODO: LINK TO V0 MIGRATION GUIDE]. +package v1 diff --git a/third_party/opa/v1/download/config.go b/third_party/opa/v1/download/config.go new file mode 100644 index 000000000000..9cd5aa61692c --- /dev/null +++ b/third_party/opa/v1/download/config.go @@ -0,0 +1,85 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package download + +import ( + "errors" + "fmt" + "time" + + "github.com/open-policy-agent/opa/v1/plugins" +) + +const ( + defaultMinDelaySeconds = int64(60) + defaultMaxDelaySeconds = int64(120) + + // deltaBundleMode indicates that OPA supports delta bundle processing + deltaBundleMode = "delta" + + // defaultBundleMode indicates that OPA supports snapshot bundle processing + defaultBundleMode = "snapshot" +) + +// PollingConfig represents polling configuration for the downloader. +type PollingConfig struct { + MinDelaySeconds *int64 `json:"min_delay_seconds,omitempty"` // min amount of time to wait between successful poll attempts + MaxDelaySeconds *int64 `json:"max_delay_seconds,omitempty"` // max amount of time to wait between poll attempts + LongPollingTimeoutSeconds *int64 `json:"long_polling_timeout_seconds,omitempty"` // max amount of time the server should wait before issuing a timeout if there's no update available +} + +// Config represents the configuration for the downloader. +type Config struct { + Trigger *plugins.TriggerMode `json:"trigger,omitempty"` + Polling PollingConfig `json:"polling"` +} + +// ValidateAndInjectDefaults checks for configuration errors and ensures all +// values are set on the Config object. +func (c *Config) ValidateAndInjectDefaults() error { + + if c.Trigger == nil { + t := plugins.DefaultTriggerMode + c.Trigger = &t + } + + switch *c.Trigger { + case plugins.TriggerPeriodic, plugins.TriggerManual: + break + default: + return fmt.Errorf("invalid trigger mode %q (want %q or %q)", *c.Trigger, plugins.TriggerPeriodic, plugins.TriggerManual) + } + + min := defaultMinDelaySeconds + max := defaultMaxDelaySeconds + + // reject bad min/max values + if c.Polling.MaxDelaySeconds != nil && c.Polling.MinDelaySeconds != nil { + if *c.Polling.MaxDelaySeconds < *c.Polling.MinDelaySeconds { + return errors.New("max polling delay must be >= min polling delay") + } + min = *c.Polling.MinDelaySeconds + max = *c.Polling.MaxDelaySeconds + } else if c.Polling.MaxDelaySeconds == nil && c.Polling.MinDelaySeconds != nil { + return errors.New("polling configuration missing 'max_delay_seconds'") + } else if c.Polling.MinDelaySeconds == nil && c.Polling.MaxDelaySeconds != nil { + return errors.New("polling configuration missing 'min_delay_seconds'") + } + + // scale to seconds + minSeconds := int64(time.Duration(min) * time.Second) + c.Polling.MinDelaySeconds = &minSeconds + + maxSeconds := int64(time.Duration(max) * time.Second) + c.Polling.MaxDelaySeconds = &maxSeconds + + if c.Polling.LongPollingTimeoutSeconds != nil { + if *c.Polling.LongPollingTimeoutSeconds < 1 { + return errors.New("'long_polling_timeout_seconds' must be at least 1") + } + } + + return nil +} diff --git a/third_party/opa/v1/download/config_test.go b/third_party/opa/v1/download/config_test.go new file mode 100644 index 000000000000..5feae9237346 --- /dev/null +++ b/third_party/opa/v1/download/config_test.go @@ -0,0 +1,100 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package download + +import ( + "encoding/json" + "testing" + "time" +) + +func TestConfigValidation(t *testing.T) { + + tests := []struct { + note string + input string + wantErr bool + expMin time.Duration + expMax time.Duration + }{ + { + note: "min > max", + input: `{ + "polling": { + "min_delay_seconds": 10, + "max_delay_seconds": 1 + } + }`, + wantErr: true, + }, + { + note: "empty", + input: `{}`, + expMin: time.Second * time.Duration(defaultMinDelaySeconds), + expMax: time.Second * time.Duration(defaultMaxDelaySeconds), + }, + { + note: "min missing", + input: `{ + "polling": { + "max_delay_seconds": 10 + } + }`, + wantErr: true, + }, + { + note: "max missing", + input: `{ + "polling": { + "min_delay_seconds": 1 + } + }`, + wantErr: true, + }, + { + note: "user supplied", + input: `{ + "polling": { + "min_delay_seconds": 10, + "max_delay_seconds": 30 + } + }`, + expMin: time.Second * 10, + expMax: time.Second * 30, + }, + { + note: "long polling timeout < 1", + input: `{ + "polling": { + "long_polling_timeout_seconds": 0 + } + }`, + wantErr: true, + }, + } + + for _, test := range tests { + + var config Config + + if err := json.Unmarshal([]byte(test.input), &config); err != nil { + t.Fatal(err) + } + + err := config.ValidateAndInjectDefaults() + if err != nil && !test.wantErr { + t.Errorf("Unexpected error on: %v, err: %v", test.input, err) + } + + if err == nil { + if time.Duration(*config.Polling.MinDelaySeconds) != test.expMin { + t.Errorf("For %q expected min %v but got %v", test.note, test.expMin, time.Duration(*config.Polling.MinDelaySeconds)) + } + if time.Duration(*config.Polling.MaxDelaySeconds) != test.expMax { + t.Errorf("For %q expected min %v but got %v", test.note, test.expMax, time.Duration(*config.Polling.MaxDelaySeconds)) + } + } + } +} diff --git a/third_party/opa/v1/download/download.go b/third_party/opa/v1/download/download.go new file mode 100644 index 000000000000..4a8a42a94673 --- /dev/null +++ b/third_party/opa/v1/download/download.go @@ -0,0 +1,444 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package download implements low-level OPA bundle downloading. +package download + +import ( + "bytes" + "context" + "fmt" + "io" + "math/rand" + "net/http" + "path" + "slices" + "strconv" + "strings" + "sync" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/plugins/rest" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + minRetryDelay = time.Millisecond * 100 +) + +// Update contains the result of a download. If an error occurred, the Error +// field will be non-nil. If a new bundle is available, the Bundle field will +// be non-nil. +type Update struct { + ETag string + Bundle *bundle.Bundle + Error error + Metrics metrics.Metrics + Raw io.Reader + Size int +} + +// Downloader implements low-level OPA bundle downloading. Downloader can be +// started and stopped. After starting, the downloader will request bundle +// updates from the remote HTTP endpoint that the client is configured to +// connect to. +type Downloader struct { + config Config // downloader configuration for tuning polling and other downloader behaviour + client rest.Client // HTTP client to use for bundle downloading + path string // path to use in bundle download request + trigger chan chan struct{} // channel to signal downloads when manual triggering is enabled + stop chan chan struct{} // used to signal plugin to stop running + f func(context.Context, Update) // callback function invoked when download updates occur + etag string // HTTP Etag for caching purposes + sizeLimitBytes *int64 // max bundle file size in bytes (passed to reader) + bvc *bundle.VerificationConfig + respHdrTimeoutSec int64 + wg sync.WaitGroup + logger logging.Logger + mtx sync.Mutex + stopped bool + persist bool + longPollingEnabled bool + lazyLoadingMode bool + bundleName string + bundleParserOpts ast.ParserOptions +} + +type downloaderResponse struct { + b *bundle.Bundle + raw io.Reader + etag string + longPoll bool + size int +} + +// New returns a new Downloader that can be started. +func New(config Config, client rest.Client, path string) *Downloader { + return &Downloader{ + config: config, + client: client, + path: path, + trigger: make(chan chan struct{}), + stop: make(chan chan struct{}), + logger: client.Logger(), + longPollingEnabled: config.Polling.LongPollingTimeoutSeconds != nil, + } +} + +// WithCallback registers a function f to be called when download updates occur. +func (d *Downloader) WithCallback(f func(context.Context, Update)) *Downloader { + d.f = f + return d +} + +// WithLogAttrs sets an optional set of key/value pair attributes to include in +// log messages emitted by the downloader. +func (d *Downloader) WithLogAttrs(attrs map[string]any) *Downloader { + d.logger = d.logger.WithFields(attrs) + return d +} + +// WithBundleVerificationConfig sets the key configuration used to verify a signed bundle +func (d *Downloader) WithBundleVerificationConfig(config *bundle.VerificationConfig) *Downloader { + d.bvc = config + return d +} + +// WithSizeLimitBytes sets the file size limit for bundles read by this downloader. +func (d *Downloader) WithSizeLimitBytes(n int64) *Downloader { + d.sizeLimitBytes = &n + return d +} + +// WithBundlePersistence specifies if the downloaded bundle will eventually be persisted to disk. +func (d *Downloader) WithBundlePersistence(persist bool) *Downloader { + d.persist = persist + return d +} + +// WithLazyLoadingMode specifies how the downloaded bundle should be read. +// If true, data files in the bundle will not be deserialized +// and the check to validate that the bundle data does not contain paths +// outside the bundle's roots will not be performed while reading the bundle. +func (d *Downloader) WithLazyLoadingMode(yes bool) *Downloader { + d.lazyLoadingMode = yes + return d +} + +// WithBundleName specifies the name of the downloaded bundle. +func (d *Downloader) WithBundleName(bundleName string) *Downloader { + d.bundleName = bundleName + return d +} + +// WithBundleParserOpts specifies the parser options to use when parsing downloaded bundles. +func (d *Downloader) WithBundleParserOpts(opts ast.ParserOptions) *Downloader { + d.bundleParserOpts = opts + return d +} + +// ClearCache is deprecated. Use SetCache instead. +func (d *Downloader) ClearCache() { + d.etag = "" +} + +// SetCache sets the given etag value on the downloader. +func (d *Downloader) SetCache(etag string) { + d.etag = etag +} + +// Trigger can be used to control when the downloader attempts to download +// a new bundle in manual triggering mode. +func (d *Downloader) Trigger(ctx context.Context) error { + done := make(chan error) + + go func() { + err := d.oneShot(ctx) + if err != nil { + d.logger.Error("Bundle download failed: %v.", err) + if ctx.Err() == nil { + done <- err + } + } + close(done) + }() + + select { + case err := <-done: + return err + case <-ctx.Done(): + return ctx.Err() + } +} + +// Start tells the Downloader to begin downloading bundles. +func (d *Downloader) Start(ctx context.Context) { + if *d.config.Trigger == plugins.TriggerPeriodic { + go d.doStart(ctx) + } +} + +func (d *Downloader) doStart(context.Context) { + // We'll revisit context passing/usage later. + ctx, cancel := context.WithCancel(context.Background()) + + d.wg.Add(1) + go d.loop(ctx) + + done := <-d.stop // blocks until there's something to read + cancel() + d.wg.Wait() + d.stopped = true + close(done) +} + +// Stop tells the Downloader to stop downloading bundles. +func (d *Downloader) Stop(context.Context) { + if *d.config.Trigger == plugins.TriggerManual { + return + } + + d.mtx.Lock() + defer d.mtx.Unlock() + + if d.stopped { + return + } + + done := make(chan struct{}) + d.stop <- done + <-done +} + +func (d *Downloader) loop(ctx context.Context) { + defer d.wg.Done() + + var retry int + + for { + + var delay time.Duration + + err := d.oneShot(ctx) + + if ctx.Err() != nil { + return + } + + if err != nil { + delay = util.DefaultBackoff(float64(minRetryDelay), float64(*d.config.Polling.MaxDelaySeconds), retry) + } else if !d.longPollingEnabled || d.config.Polling.LongPollingTimeoutSeconds == nil { + // revert the response header timeout value on the http client's transport + if *d.client.Config().ResponseHeaderTimeoutSeconds == 0 { + d.client = d.client.SetResponseHeaderTimeout(&d.respHdrTimeoutSec) + } + min := float64(*d.config.Polling.MinDelaySeconds) + max := float64(*d.config.Polling.MaxDelaySeconds) + delay = time.Duration(((max - min) * rand.Float64()) + min) + } + + d.logger.Debug("Waiting %v before next download/retry.", delay) + + timer, timerCancel := util.TimerWithCancel(delay) + select { + case <-timer.C: + if err != nil { + retry++ + } else { + retry = 0 + } + case <-ctx.Done(): + timerCancel() // explicitly cancel the timer. + return + } + } +} + +func (d *Downloader) oneShot(ctx context.Context) error { + m := metrics.New() + resp, err := d.download(ctx, m) + + if err != nil { + d.etag = "" + + if d.f != nil { + d.f(ctx, Update{ETag: "", Bundle: nil, Error: err, Metrics: m, Raw: nil}) + } + return err + } + + d.etag = resp.etag + d.longPollingEnabled = resp.longPoll + + if d.f != nil { + d.f(ctx, Update{ETag: resp.etag, Bundle: resp.b, Error: nil, Metrics: m, Raw: resp.raw, Size: resp.size}) + } + return nil +} + +func (d *Downloader) download(ctx context.Context, m metrics.Metrics) (*downloaderResponse, error) { + d.logger.Debug("Download starting.") + + d.client = d.client.WithHeader("If-None-Match", d.etag) + + preferences := []string{fmt.Sprintf("modes=%v,%v", defaultBundleMode, deltaBundleMode)} + + if d.longPollingEnabled && d.config.Polling.LongPollingTimeoutSeconds != nil { + wait := "wait=" + strconv.FormatInt(*d.config.Polling.LongPollingTimeoutSeconds, 10) + preferences = append(preferences, wait) + + // fetch existing response header timeout value on the http client's transport and + // clear it for the long poll request + current := d.client.Config().ResponseHeaderTimeoutSeconds + if *current != 0 { + d.respHdrTimeoutSec = *current + t := int64(0) + d.client = d.client.SetResponseHeaderTimeout(&t) + } + } + + preferValue := strings.Join(preferences, ";") + d.client = d.client.WithHeader("Prefer", preferValue) + + m.Timer(metrics.BundleRequest).Start() + resp, err := d.client.Do(ctx, "GET", d.path) + m.Timer(metrics.BundleRequest).Stop() + if err != nil { + return nil, fmt.Errorf("request failed: %w", err) + } + + defer util.Close(resp) + + switch resp.StatusCode { + case http.StatusOK: + var buf bytes.Buffer + if resp.Body != nil { + d.logger.Debug("Download in progress.") + m.Timer(metrics.RegoLoadBundles).Start() + defer m.Timer(metrics.RegoLoadBundles).Stop() + baseURL := path.Join(d.client.Config().URL, d.path) + + cnt := &count{} + r := io.TeeReader(resp.Body, cnt) + + var loader bundle.DirectoryLoader + if d.persist { + tee := io.TeeReader(r, &buf) + loader = bundle.NewTarballLoaderWithBaseURL(tee, baseURL) + } else { + loader = bundle.NewTarballLoaderWithBaseURL(r, baseURL) + } + + // Setting the size limit on the loader allows early exit in the case + // of any file exceeding the limit, without the file getting loaded + if d.sizeLimitBytes != nil { + loader = loader.WithSizeLimitBytes(*d.sizeLimitBytes) + } + + etag := resp.Header.Get("ETag") + + reader := bundle.NewCustomReader(loader). + WithRegoVersion(d.bundleParserOpts.RegoVersion). + WithMetrics(m). + WithBundleVerificationConfig(d.bvc). + WithBundleEtag(etag). + WithLazyLoadingMode(d.lazyLoadingMode). + WithBundleName(d.bundleName). + WithBundlePersistence(d.persist) + + if d.sizeLimitBytes != nil { + reader = reader.WithSizeLimitBytes(*d.sizeLimitBytes) + } + + if d.logger.GetLevel() >= logging.Debug { + expectedBundleContentType := []string{ + "application/gzip", + "application/octet-stream", + "application/vnd.openpolicyagent.bundles", + } + contentType := resp.Header.Get("content-type") + + if !slices.Contains(expectedBundleContentType, contentType) { + d.logger.Debug("Content-Type response header set to %v. Expected one of %v. "+ + "Possibly not a bundle being downloaded.", + contentType, + expectedBundleContentType, + ) + } + } + + b, err := reader.Read() + if err != nil { + return nil, err + } + + return &downloaderResponse{ + b: &b, + raw: &buf, + etag: etag, + longPoll: isLongPollSupported(resp.Header), + size: cnt.Bytes(), + }, nil + } + + d.logger.Debug("Server replied with empty body.") + return &downloaderResponse{ + b: nil, + raw: nil, + etag: "", + longPoll: isLongPollSupported(resp.Header), + }, nil + case http.StatusNotModified: + etag := resp.Header.Get("ETag") + if etag == "" { + etag = d.etag + } + return &downloaderResponse{ + b: nil, + raw: nil, + etag: etag, + longPoll: d.longPollingEnabled, + }, nil + default: + if d.logger.GetLevel() == logging.Debug && resp.Body != nil { + body, err := io.ReadAll(resp.Body) + if err == nil { + d.logger.Debug("bundle download error response with response body: %s", body) + } + } + + return nil, HTTPError{StatusCode: resp.StatusCode} + } +} + +type count struct { + total int +} + +func (c *count) Write(p []byte) (n int, err error) { + n = len(p) + c.total += n + return +} + +func (c *count) Bytes() int { + return c.total +} + +func isLongPollSupported(header http.Header) bool { + return header.Get("Content-Type") == "application/vnd.openpolicyagent.bundles" +} + +type HTTPError struct { + StatusCode int +} + +func (e HTTPError) Error() string { + return "server replied with " + http.StatusText(e.StatusCode) +} diff --git a/third_party/opa/v1/download/download_test.go b/third_party/opa/v1/download/download_test.go new file mode 100644 index 000000000000..9b8143b4889d --- /dev/null +++ b/third_party/opa/v1/download/download_test.go @@ -0,0 +1,1121 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build slow +// +build slow + +package download + +import ( + "context" + "errors" + "fmt" + "reflect" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/logging/test" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" +) + +func TestStartStop(t *testing.T) { + t.Parallel() + + ctx := context.Background() + fixture := newTestFixture(t) + + updates := make(chan *Update) + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + d := New(config, fixture.client, "/bundles/test/bundle1").WithCallback(func(_ context.Context, u Update) { + updates <- &u + }) + + d.Start(ctx) + + // Give time for some download events to occur + time.Sleep(1 * time.Second) + + u1 := <-updates + + if u1.Bundle == nil || len(u1.Bundle.Modules) == 0 { + t.Fatal("expected bundle with at least one module but got:", u1) + } + + if !strings.HasSuffix(u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) { + t.Fatalf("expected URL to have path as suffix but got %v and %v", u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) + } + + if u1.Size == 0 { + t.Fatal("expected non-0 size") + } + + d.Stop(ctx) +} + +func TestStartStopWithBundlePersistence(t *testing.T) { + t.Parallel() + + ctx := context.Background() + fixture := newTestFixture(t) + + updates := make(chan *Update) + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + d := New(config, fixture.client, "/bundles/test/bundle1").WithCallback(func(_ context.Context, u Update) { + updates <- &u + }).WithBundlePersistence(true) + + d.Start(ctx) + + // Give time for some download events to occur + time.Sleep(1 * time.Second) + + u1 := <-updates + + if u1.Bundle == nil || len(u1.Bundle.Modules) == 0 { + t.Fatal("expected bundle with at least one module but got:", u1) + } + + if !strings.HasSuffix(u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) { + t.Fatalf("expected URL to have path as suffix but got %v and %v", u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) + } + + if u1.Raw == nil { + t.Fatal("expected bundle reader to be non-nil") + } + + if u1.Size == 0 { + t.Fatal("expected non-0 size") + } + + r := bundle.NewReader(u1.Raw) + + b, err := r.Read() + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(b.Data, u1.Bundle.Data) { + t.Fatal("expected the bundle object and reader to have the same data") + } + + if len(b.Modules) != len(u1.Bundle.Modules) { + t.Fatal("expected the bundle object and reader to have the same number of bundle modules") + } + + d.Stop(ctx) +} + +func TestStopWithMultipleCalls(t *testing.T) { + t.Parallel() + + ctx := context.Background() + fixture := newTestFixture(t) + + updates := make(chan *Update) + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + d := New(config, fixture.client, "/bundles/test/bundle1").WithCallback(func(_ context.Context, u Update) { + updates <- &u + }) + + d.Start(ctx) + + // Give time for some download events to occur + time.Sleep(1 * time.Second) + + u1 := <-updates + + if u1.Bundle == nil || len(u1.Bundle.Modules) == 0 { + t.Fatal("expected bundle with at least one module but got:", u1) + } + + done := make(chan struct{}) + go func() { + d.Stop(ctx) + close(done) + }() + + d.Stop(ctx) + <-done + + if !d.stopped { + t.Fatal("expected downloader to be stopped") + } +} + +func TestStartStopWithLazyLoadingMode(t *testing.T) { + t.Parallel() + + ctx := context.Background() + fixture := newTestFixture(t) + + updates := make(chan *Update) + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + d := New(config, fixture.client, "/bundles/test/bundle1").WithCallback(func(_ context.Context, u Update) { + updates <- &u + }).WithLazyLoadingMode(true) + + d.Start(ctx) + + // Give time for some download events to occur + time.Sleep(1 * time.Second) + + u1 := <-updates + + if u1.Bundle == nil || len(u1.Bundle.Modules) == 0 { + t.Fatal("expected bundle with at least one module but got:", u1) + } + + if !strings.HasSuffix(u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) { + t.Fatalf("expected URL to have path as suffix but got %v and %v", u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) + } + + if len(u1.Bundle.Raw) == 0 { + t.Fatal("expected bundle to contain raw bytes") + } + + if len(u1.Bundle.Data) != 0 { + t.Fatal("expected the bundle object to contain no data") + } + + d.Stop(ctx) +} + +func TestStartStopWithDeltaBundleMode(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + updates := make(chan *Update) + + config := Config{} + + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + fixture := newTestFixture(t) + + d := New(config, fixture.client, "/bundles/test/bundle2").WithCallback(func(_ context.Context, u Update) { + updates <- &u + }) + + d.Start(ctx) + + // Give time for some download events to occur + time.Sleep(1 * time.Second) + + u1 := <-updates + + if u1.Bundle == nil || u1.Bundle.Manifest.Revision != deltaBundleMode { + t.Fatal("expected delta bundle but got:", u1) + } + + if u1.Size == 0 { + t.Fatal("expected non-0 size") + } + + d.Stop(ctx) +} + +func TestStartStopWithLongPollNotSupported(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + config := Config{} + min := int64(1) + max := int64(2) + timeout := int64(1) + config.Polling.MinDelaySeconds = &min + config.Polling.MaxDelaySeconds = &max + config.Polling.LongPollingTimeoutSeconds = &timeout + + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + fixture := newTestFixture(t) + fixture.d = New(config, fixture.client, "/bundles/test/bundle1").WithCallback(fixture.oneShot) + defer fixture.server.stop() + + fixture.d.Start(ctx) + + // Give time for some download events to occur + time.Sleep(5 * time.Second) + + fixture.d.Stop(ctx) + if len(fixture.updates) < 2 { + t.Fatalf("Expected at least 2 updates but got %v\n", len(fixture.updates)) + } +} + +func TestStartStopWithLongPollSupportedByServer(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + config := Config{} + min := int64(1) + max := int64(2) + config.Polling.MinDelaySeconds = &min + config.Polling.MaxDelaySeconds = &max + + // simulate scenario where server supports long polling but long polling timeout not provided + config.Polling.LongPollingTimeoutSeconds = nil + + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + fixture := newTestFixture(t) + fixture.d = New(config, fixture.client, "/bundles/test/bundle1").WithCallback(fixture.oneShot) + fixture.server.longPoll = true + defer fixture.server.stop() + + fixture.d.Start(ctx) + + // Give time for some download events to occur + time.Sleep(3 * time.Second) + + fixture.d.Stop(ctx) + if len(fixture.updates) == 0 { + t.Fatal("expected update but got none") + } + + if *fixture.d.client.Config().ResponseHeaderTimeoutSeconds == 0 { + t.Fatal("expected non-zero value for response header timeout") + } +} + +func TestStartStopWithLongPollSupported(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + config := Config{} + timeout := int64(1) + config.Polling.LongPollingTimeoutSeconds = &timeout + + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + fixture := newTestFixture(t) + fixture.d = New(config, fixture.client, "/bundles/test/bundle1").WithCallback(fixture.oneShot) + fixture.server.longPoll = true + defer fixture.server.stop() + + fixture.d.Start(ctx) + + // Give time for some download events to occur + time.Sleep(3 * time.Second) + + fixture.d.Stop(ctx) + if len(fixture.updates) == 0 { + t.Fatal("expected update but got none") + } +} + +func TestStartStopWithLongPollWithLongTimeout(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + config := Config{} + timeout := int64(3) // this will result in the test server sleeping for 3 seconds + config.Polling.LongPollingTimeoutSeconds = &timeout + + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + fixture := newTestFixture(t) + fixture.d = New(config, fixture.client, "/bundles/test/bundle1").WithCallback(fixture.oneShot) + fixture.server.longPoll = true + defer fixture.server.stop() + + fixture.d.Start(ctx) + + time.Sleep(500 * time.Millisecond) + + if len(fixture.updates) != 0 { + t.Fatalf("expected no update but got %v", len(fixture.updates)) + } + + fixture.d.Stop(ctx) + + if len(fixture.updates) != 1 { + t.Fatalf("expected one update but got %v", len(fixture.updates)) + } + + if fixture.updates[0].Error == nil { + t.Fatal("expected error but got nil") + } +} + +func TestEtagCachingLifecycle(t *testing.T) { + t.Parallel() + + ctx := context.Background() + fixture := newTestFixture(t) + fixture.d = New(Config{}, fixture.client, "/bundles/test/bundle1").WithCallback(fixture.oneShot) + defer fixture.server.stop() + + // check etag on the downloader is empty + if fixture.d.etag != "" { + t.Fatalf("Expected empty downloader ETag but got %v", fixture.d.etag) + } + + // simulate downloader error on first bundle download + fixture.server.expCode = 500 + fixture.server.expEtag = "some etag value" + err := fixture.d.oneShot(ctx) + if err == nil { + t.Fatal("Expected error but got nil") + } else if len(fixture.updates) != 1 { + t.Fatal("expected update") + } else if fixture.d.etag != "" { + t.Fatalf("Expected empty downloader ETag but got %v", fixture.d.etag) + } + + // simulate successful bundle activation and check updated etag on the downloader + fixture.server.expCode = 0 + err = fixture.d.oneShot(ctx) + if err != nil { + t.Fatal("Unexpected:", err) + } else if len(fixture.updates) != 2 { + t.Fatal("expected update") + } else if fixture.d.etag != fixture.server.expEtag { + t.Fatalf("Expected downloader ETag %v but got %v", fixture.server.expEtag, fixture.d.etag) + } + + // simulate another successful bundle activation and check updated etag on the downloader + fixture.server.expEtag = "some etag value - 2" + err = fixture.d.oneShot(ctx) + if err != nil { + t.Fatal("Unexpected:", err) + } else if len(fixture.updates) != 3 { + t.Fatal("expected update") + } else if fixture.d.etag != fixture.server.expEtag { + t.Fatalf("Expected downloader ETag %v but got %v", fixture.server.expEtag, fixture.d.etag) + } + + // simulate bundle activation error and check etag is set from the last successful activation + fixture.mockBundleActivationError = true + fixture.server.expEtag = "some newer etag value - 3" + err = fixture.d.oneShot(ctx) + if err != nil { + t.Fatal("Unexpected:", err) + } else if len(fixture.updates) != 4 { + t.Fatal("expected update") + } else if fixture.d.etag != "some etag value - 2" { + t.Fatalf("Expected downloader ETag %v but got %v", "some etag value - 2", fixture.d.etag) + } + + // simulate successful bundle activation and check updated etag on the downloader + fixture.server.expCode = 0 + fixture.mockBundleActivationError = false + err = fixture.d.oneShot(ctx) + if err != nil { + t.Fatal("Unexpected:", err) + } else if len(fixture.updates) != 5 { + t.Fatal("expected update") + } else if fixture.d.etag != fixture.server.expEtag { + t.Fatalf("Expected downloader ETag %v but got %v", fixture.server.expEtag, fixture.d.etag) + } + + // simulate downloader error and check etag is set from the last successful activation + fixture.server.expCode = 500 + err = fixture.d.oneShot(ctx) + if err == nil { + t.Fatal("Expected error but got nil") + } else if len(fixture.updates) != 6 { + t.Fatal("expected update") + } else if fixture.d.etag != fixture.server.expEtag { + t.Fatalf("Expected downloader ETag %v but got %v", fixture.server.expEtag, fixture.d.etag) + } + + // simulate bundle activation error and check etag is set from the last successful activation + fixture.mockBundleActivationError = true + fixture.server.expCode = 0 + err = fixture.d.oneShot(ctx) + if err != nil { + t.Fatal("Unexpected:", err) + } else if len(fixture.updates) != 7 { + t.Fatal("expected update") + } else if fixture.d.etag != fixture.server.expEtag { + t.Fatalf("Expected downloader ETag %v but got %v", fixture.server.expEtag, fixture.d.etag) + } +} + +func TestOneShotWithBundleEtag(t *testing.T) { + t.Parallel() + + ctx := context.Background() + fixture := newTestFixture(t) + fixture.d = New(Config{}, fixture.client, "/bundles/test/bundle1").WithCallback(fixture.oneShot) + fixture.server.expEtag = "some etag value" + defer fixture.server.stop() + + // check etag on the downloader is empty + if fixture.d.etag != "" { + t.Fatalf("Expected empty downloader ETag but got %v", fixture.d.etag) + } + + // simulate successful bundle activation and check updated etag on the downloader + fixture.server.expCode = 0 + err := fixture.d.oneShot(ctx) + if err != nil { + t.Fatal("Unexpected:", err) + } + + if fixture.d.etag != fixture.server.expEtag { + t.Fatalf("Expected downloader ETag %v but got %v", fixture.server.expEtag, fixture.d.etag) + } + + if fixture.updates[0].Bundle == nil { + // 200 response on first request, bundle should be present + t.Errorf("Expected bundle in response") + } + + if fixture.updates[0].Bundle.Etag != fixture.server.expEtag { + t.Fatalf("Expected bundle ETag %v but got %v", fixture.server.expEtag, fixture.updates[0].Bundle.Etag) + } +} + +func TestOneShotV1Compatible(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + v1Compatible bool + bundlePath string + expErrs []string + }{ + { + note: "v0.x, keywords not used", + bundlePath: "/bundles/test/v1compat/keywords_not_used", + }, + { + note: "v0.x, keywords used, not imported", + bundlePath: "/bundles/test/v1compat/no_imports", + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0.x, keywords used, rego.v1 imported", + bundlePath: "/bundles/test/v1compat/rego_import", + }, + + { + note: "v1.0, keywords not used", + v1Compatible: true, + bundlePath: "/bundles/test/v1compat/keywords_not_used", + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1.0, keywords used, not imported", + v1Compatible: true, + bundlePath: "/bundles/test/v1compat/no_imports", + }, + { + note: "v1.0, keywords used, rego.v1 imported", + v1Compatible: true, + bundlePath: "/bundles/test/v1compat/rego_import", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + regoVersion := ast.RegoV0 + if tc.v1Compatible { + regoVersion = ast.RegoV1 + } + popts := ast.ParserOptions{RegoVersion: regoVersion} + ctx := context.Background() + fixture := newTestFixture(t) + fixture.d = New(Config{}, fixture.client, tc.bundlePath). + WithBundleParserOpts(popts). + WithCallback(fixture.oneShot) + fixture.server.expEtag = "some etag value" + defer fixture.server.stop() + + // check etag on the downloader is empty + if fixture.d.etag != "" { + t.Fatalf("Expected empty downloader ETag but got %v", fixture.d.etag) + } + + // simulate successful bundle activation and check updated etag on the downloader + fixture.server.expCode = 0 + err := fixture.d.oneShot(ctx) + + if tc.expErrs != nil { + if err == nil { + t.Fatal("Expected error but got nil") + } + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%v\n\nbut got\n\n%v", expErr, err) + } + } + } else { + if err != nil { + t.Fatal("Unexpected:", err) + } + + if fixture.d.etag != fixture.server.expEtag { + t.Fatalf("Expected downloader ETag %v but got %v", fixture.server.expEtag, fixture.d.etag) + } + + if fixture.updates[0].Bundle == nil { + // 200 response on first request, bundle should be present + t.Errorf("Expected bundle in response") + } + + if fixture.updates[0].Bundle.Etag != fixture.server.expEtag { + t.Fatalf("Expected bundle ETag %v but got %v", fixture.server.expEtag, fixture.updates[0].Bundle.Etag) + } + } + }) + } +} + +func TestOneShotWithBundleRegoVersion(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + bundleRegoVersion int + module string + expErrs []string + }{ + { + note: "v0.x bundle, keywords not used", + bundleRegoVersion: 0, + module: `package test +p[1] { + input.x == 2 +}`, + }, + { + note: "v0.x bundle, keywords used but not imported", + bundleRegoVersion: 0, + module: `package test +p contains 1 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0.x bundle, keywords used, rego.v1 imported", + bundleRegoVersion: 0, + module: `package test +import rego.v1 +p contains 1 if { + input.x == 2 +}`, + }, + + { + note: "v1.0 bundle, keywords not used", + bundleRegoVersion: 1, + module: `package test +p[1] { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1.0 bundle, keywords used, not imported", + bundleRegoVersion: 1, + module: `package test +p contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0, keywords used, rego.v1 imported", + bundleRegoVersion: 1, + module: `package test +import rego.v1 +p contains 1 if { + input.x == 2 +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + fixture := newTestFixture(t) + fixture.d = New(Config{}, fixture.client, "bundles/custom"). + WithCallback(fixture.oneShot) + fixture.server.expEtag = "some etag value" + + fixture.server.bundles["custom"] = bundle.Bundle{ + Manifest: bundle.Manifest{RegoVersion: &tc.bundleRegoVersion}, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "test.rego", + Raw: []byte(tc.module), + }, + }, + } + + defer fixture.server.stop() + + // check etag on the downloader is empty + if fixture.d.etag != "" { + t.Fatalf("Expected empty downloader ETag but got %v", fixture.d.etag) + } + + // simulate successful bundle activation and check updated etag on the downloader + fixture.server.expCode = 0 + err := fixture.d.oneShot(ctx) + + if tc.expErrs != nil { + if err == nil { + t.Fatal("Expected error but got nil") + } + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%v\n\nbut got\n\n%v", expErr, err) + } + } + } else { + if err != nil { + t.Fatal("Unexpected:", err) + } + + if fixture.d.etag != fixture.server.expEtag { + t.Fatalf("Expected downloader ETag %v but got %v", fixture.server.expEtag, fixture.d.etag) + } + + if fixture.updates[0].Bundle == nil { + // 200 response on first request, bundle should be present + t.Errorf("Expected bundle in response") + } + + if fixture.updates[0].Bundle.Etag != fixture.server.expEtag { + t.Fatalf("Expected bundle ETag %v but got %v", fixture.server.expEtag, fixture.updates[0].Bundle.Etag) + } + } + }) + } +} + +func TestFailureAuthn(t *testing.T) { + t.Parallel() + + ctx := context.Background() + fixture := newTestFixture(t) + fixture.server.expAuth = "Bearer anothersecret" + defer fixture.server.stop() + + d := New(Config{}, fixture.client, "/bundles/test/bundle1") + + err := d.oneShot(ctx) + if err == nil { + t.Fatal("expected error") + } +} + +func TestFailureNotFound(t *testing.T) { + t.Parallel() + + ctx := context.Background() + fixture := newTestFixture(t) + delete(fixture.server.bundles, "test/bundle1") + defer fixture.server.stop() + + d := New(Config{}, fixture.client, "/bundles/test/non-existent") + + err := d.oneShot(ctx) + if err == nil { + t.Fatal("expected error") + } +} + +func TestFailureUnexpected(t *testing.T) { + t.Parallel() + + ctx := context.Background() + fixture := newTestFixture(t) + fixture.server.expCode = 500 + defer fixture.server.stop() + + d := New(Config{}, fixture.client, "/bundles/test/bundle1") + + err := d.oneShot(ctx) + if err == nil { + t.Fatal("expected error") + } + var hErr HTTPError + if !errors.As(err, &hErr) { + t.Fatal("expected HTTPError") + } + if hErr.StatusCode != 500 { + t.Fatal("expected status code 500") + } +} + +func TestFailureUnexpectedWithResponseBody(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + expResp := "this is a bad http response" + + fixture := newTestFixture(t) + fixture.server.expCode = 500 + fixture.server.expResp = expResp + + defer fixture.server.stop() + + d := New(Config{}, fixture.client, "/bundles/test/bundle1") + + logger := test.New() + logger.SetLevel(logging.Debug) + d.logger = logger + + err := d.oneShot(ctx) + if err == nil { + t.Fatal("expected error") + } + var hErr HTTPError + if !errors.As(err, &hErr) { + t.Fatal("expected HTTPError") + } + if hErr.StatusCode != 500 { + t.Fatal("expected status code 500") + } + + expectLogged := fmt.Sprintf("bundle download error response with response body: %s", expResp) + + var found bool + for _, entry := range logger.Entries() { + if entry.Message == expectLogged { + found = true + break + } + } + + if !found { + t.Errorf("Expected log entry: %s", expectLogged) + } +} + +func TestEtagInResponse(t *testing.T) { + t.Parallel() + + ctx := context.Background() + fixture := newTestFixture(t) + fixture.server.etagInResponse = true + fixture.d = New(Config{}, fixture.client, "/bundles/test/bundle1").WithCallback(fixture.oneShot) + defer fixture.server.stop() + + if fixture.d.etag != "" { + t.Fatalf("Expected empty downloader ETag but got %v", fixture.d.etag) + } + + fixture.server.expEtag = "some etag value" + + err := fixture.d.oneShot(ctx) + if err != nil { + t.Fatal("Unexpected:", err) + } else if len(fixture.updates) != 1 { + t.Fatal("expected update") + } else if fixture.d.etag != fixture.server.expEtag { + t.Fatalf("Expected downloader ETag %v but got %v", fixture.server.expEtag, fixture.d.etag) + } + + if fixture.updates[0].Bundle == nil { + // 200 response on first request, bundle should be present + t.Errorf("Expected bundle in response") + } + + err = fixture.d.oneShot(ctx) + if err != nil { + t.Fatal("Unexpected:", err) + } else if len(fixture.updates) != 2 { + t.Fatal("expected two updates") + } else if fixture.d.etag != fixture.server.expEtag { + t.Fatalf("Expected downloader ETag %v but got %v", fixture.server.expEtag, fixture.d.etag) + } + + if fixture.updates[1].Bundle != nil { + // 304 response on second request, bundle should _not_ be present + t.Errorf("Expected no bundle in response") + } +} + +func TestTriggerManual(t *testing.T) { + t.Parallel() + + ctx := context.Background() + fixture := newTestFixture(t) + + config := Config{} + tr := plugins.TriggerManual + config.Trigger = &tr + + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + updates := make(chan *Update) + + d := New(config, fixture.client, "/bundles/test/bundle1"). + WithCallback(func(_ context.Context, u Update) { + updates <- &u + }) + + d.Start(ctx) + + // execute a series of triggers and expect responses + for i := 0; i < 10; i++ { + + // mutate the fixture server's bundle for this trigger + exp := fmt.Sprintf("rev%d", i) + b := fixture.server.bundles["test/bundle1"] + b.Manifest.Revision = exp + fixture.server.bundles["test/bundle1"] = b + + // trigger the downloader + go func() { + d.Trigger(ctx) + }() + + // wait for the update + u := <-updates + + if u.Bundle.Manifest.Revision != exp { + t.Fatalf("expected revision %q but got %q", exp, u.Bundle.Manifest.Revision) + } + } + + d.Stop(ctx) +} + +func TestTriggerManualWithTimeout(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + fixture := newTestFixture(t) + + config := Config{} + tr := plugins.TriggerManual + config.Trigger = &tr + + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + d := New(config, fixture.client, "/bundles/test/bundle1"). + WithCallback(func(context.Context, Update) { + time.Sleep(3 * time.Second) // this should cause the context deadline to exceed + }) + + d.Start(ctx) + + b := fixture.server.bundles["test/bundle1"] + b.Manifest.Revision = "rev%0" + fixture.server.bundles["test/bundle1"] = b + + // trigger the downloader + done := make(chan struct{}) + go func() { + // this call should block till the context deadline exceeds + d.Trigger(ctx) + close(done) + }() + <-done + + if ctx.Err() == nil { + t.Fatal("Expected error but got nil") + } + + exp := context.DeadlineExceeded + if ctx.Err() != exp { + t.Fatalf("Expected error %v but got %v", exp, ctx.Err()) + } + + d.Stop(context.Background()) +} + +func TestDownloadLongPollNotModifiedOn304(t *testing.T) { + t.Parallel() + + ctx := context.Background() + config := Config{} + timeout := int64(3) // this will result in the test server sleeping for 3 seconds + config.Polling.LongPollingTimeoutSeconds = &timeout + + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + fixture := newTestFixture(t) + fixture.d = New(config, fixture.client, "/bundles/test/bundle1").WithCallback(fixture.oneShot) + fixture.server.longPoll = true + fixture.server.expEtag = "foo" + fixture.d.etag = fixture.server.expEtag // not modified + fixture.server.expCode = 0 + defer fixture.server.stop() + + resp, err := fixture.d.download(ctx, metrics.New()) + if err != nil { + t.Fatal("Unexpected:", err) + } + if resp.longPoll != fixture.d.longPollingEnabled { + t.Fatalf("Expected same value for longPoll and longPollingEnabled") + } +} + +func TestOneShotLongPollingSwitch(t *testing.T) { + t.Parallel() + + ctx := context.Background() + config := Config{} + timeout := int64(3) // this will result in the test server sleeping for 3 seconds + config.Polling.LongPollingTimeoutSeconds = &timeout + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + fixture := newTestFixture(t) + fixture.d = New(config, fixture.client, "/bundles/test/bundle1").WithCallback(fixture.oneShot) + fixture.server.expCode = 0 + defer fixture.server.stop() + + fixture.server.longPoll = true + err := fixture.d.oneShot(ctx) + if err != nil { + t.Fatal("Unexpected:", err) + } + if fixture.d.longPollingEnabled != fixture.server.longPoll { + t.Fatalf("Expected same value for longPoll and longPollingEnabled") + } + + fixture.server.longPoll = false + err = fixture.d.oneShot(ctx) + if err != nil { + t.Fatal("Unexpected:", err) + } + if fixture.d.longPollingEnabled != fixture.server.longPoll { + t.Fatalf("Expected same value for longPollingEnabled and longPoll") + } +} + +func TestOneShotNotLongPollingSwitch(t *testing.T) { + t.Parallel() + + ctx := context.Background() + config := Config{} + timeout := int64(3) + config.Polling.LongPollingTimeoutSeconds = &timeout + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + fixture := newTestFixture(t) + fixture.d = New(config, fixture.client, "/bundles/test/bundle1").WithCallback(fixture.oneShot) + fixture.server.expCode = 0 + + defer fixture.server.stop() + + fixture.server.longPoll = true + err := fixture.d.oneShot(ctx) + if err != nil { + t.Fatal("Unexpected:", err) + } + if !fixture.d.longPollingEnabled { + t.Fatal("Expected long polling to be enabled") + } + + fixture.server.longPoll = false + err = fixture.d.oneShot(ctx) + if err != nil { + t.Fatal("Unexpected:", err) + } + if fixture.d.longPollingEnabled { + t.Fatal("Expected long polling to be disabled") + } +} + +func TestWarnOnNonBundleContentType(t *testing.T) { + t.Parallel() + + ctx := context.Background() + fixture := newTestFixture(t) + fixture.server.bundles["not-a-bundle"] = bundle.Bundle{} + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + d := New(config, fixture.client, "/bundles/not-a-bundle") + logger := test.New() + logger.SetLevel(logging.Debug) + d.logger = logger + + d.Start(ctx) + + time.Sleep(1 * time.Second) + + d.Stop(ctx) + + expectLogged := "Content-Type response header set to text/html. " + + "Expected one of [application/gzip application/octet-stream application/vnd.openpolicyagent.bundles]. " + + "Possibly not a bundle being downloaded." + var found bool + for _, entry := range logger.Entries() { + if entry.Message == expectLogged { + found = true + break + } + } + + if !found { + t.Errorf("Expected log entry: %s", expectLogged) + } +} diff --git a/third_party/opa/v1/download/oci_download.go b/third_party/opa/v1/download/oci_download.go new file mode 100644 index 000000000000..3019117958b2 --- /dev/null +++ b/third_party/opa/v1/download/oci_download.go @@ -0,0 +1,461 @@ +//go:build !opa_no_oci + +package download + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "math/rand" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" + "time" + + "github.com/containerd/containerd/v2/core/remotes" + "github.com/containerd/containerd/v2/core/remotes/docker" + "github.com/containerd/errdefs" + + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + oraslib "oras.land/oras-go/v2" + "oras.land/oras-go/v2/content/oci" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/plugins/rest" + "github.com/open-policy-agent/opa/v1/util" +) + +// NewOCI returns a new Downloader that can be started. +func NewOCI(config Config, client rest.Client, path, storePath string) *OCIDownloader { + localstore, err := oci.New(storePath) + if err != nil { + panic(err) + } + return &OCIDownloader{ + config: config, + path: path, + localStorePath: storePath, + client: client, + trigger: make(chan chan struct{}), + stop: make(chan chan struct{}), + logger: client.Logger(), + store: localstore, + } +} + +// WithCallback registers a function f to be called when download updates occur. +func (d *OCIDownloader) WithCallback(f func(context.Context, Update)) *OCIDownloader { + d.f = f + return d +} + +// WithLogAttrs sets an optional set of key/value pair attributes to include in +// log messages emitted by the downloader. +func (d *OCIDownloader) WithLogAttrs(attrs map[string]any) *OCIDownloader { + d.logger = d.logger.WithFields(attrs) + return d +} + +// WithBundleVerificationConfig sets the key configuration used to verify a signed bundle +func (d *OCIDownloader) WithBundleVerificationConfig(config *bundle.VerificationConfig) *OCIDownloader { + d.bvc = config + return d +} + +// WithSizeLimitBytes sets the file size limit for bundles read by this downloader. +func (d *OCIDownloader) WithSizeLimitBytes(n int64) *OCIDownloader { + d.sizeLimitBytes = &n + return d +} + +// WithBundlePersistence specifies if the downloaded bundle will eventually be persisted to disk. +func (d *OCIDownloader) WithBundlePersistence(persist bool) *OCIDownloader { + d.persist = persist + return d +} + +// WithBundleParserOpts specifies the parser options to use when parsing downloaded bundles. +func (d *OCIDownloader) WithBundleParserOpts(opts ast.ParserOptions) *OCIDownloader { + d.bundleParserOpts = opts + return d +} + +// ClearCache is deprecated. Use SetCache instead. +func (*OCIDownloader) ClearCache() { +} + +// SetCache sets the etag value to the SHA of the loaded bundle +func (d *OCIDownloader) SetCache(etag string) { + d.etag = etag +} + +// Trigger can be used to control when the downloader attempts to download +// a new bundle in manual triggering mode. +func (d *OCIDownloader) Trigger(ctx context.Context) error { + done := make(chan error) + + go func() { + err := d.oneShot(ctx) + if err != nil { + d.logger.Error("OCI - Bundle download failed: %v.", err) + if ctx.Err() == nil { + done <- err + } + } + close(done) + }() + + select { + case err := <-done: + return err + case <-ctx.Done(): + return ctx.Err() + } +} + +// Start tells the Downloader to begin downloading bundles. +func (d *OCIDownloader) Start(ctx context.Context) { + if *d.config.Trigger == plugins.TriggerPeriodic { + go d.doStart(ctx) + } +} + +// Stop tells the Downloader to stop downloading bundles. +func (d *OCIDownloader) Stop(context.Context) { + if *d.config.Trigger == plugins.TriggerManual { + return + } + + d.mtx.Lock() + defer d.mtx.Unlock() + + if d.stopped { + return + } + + done := make(chan struct{}) + d.stop <- done + <-done +} + +func (d *OCIDownloader) doStart(context.Context) { + // We'll revisit context passing/usage later. + ctx, cancel := context.WithCancel(context.Background()) + + d.wg.Add(1) + go d.loop(ctx) + + done := <-d.stop // blocks until there's something to read + cancel() + d.wg.Wait() + d.stopped = true + close(done) +} + +func (d *OCIDownloader) loop(ctx context.Context) { + defer d.wg.Done() + + var retry int + + for { + + var delay time.Duration + + err := d.oneShot(ctx) + + if ctx.Err() != nil { + return + } + + if err != nil { + delay = util.DefaultBackoff(float64(minRetryDelay), float64(*d.config.Polling.MaxDelaySeconds), retry) + } else { + // revert the response header timeout value on the http client's transport + min := float64(*d.config.Polling.MinDelaySeconds) + max := float64(*d.config.Polling.MaxDelaySeconds) + delay = time.Duration(((max - min) * rand.Float64()) + min) + } + + d.logger.Debug("OCI - Waiting %v before next download/retry.", delay) + + timer, timerCancel := util.TimerWithCancel(delay) + select { + case <-timer.C: + if err != nil { + retry++ + } else { + retry = 0 + } + case <-ctx.Done(): + timerCancel() // explicitly cancel the timer. + return + } + } +} + +func (d *OCIDownloader) oneShot(ctx context.Context) error { + m := metrics.New() + resp, err := d.download(ctx, m) + if err != nil { + if d.f != nil { + d.f(ctx, Update{ETag: "", Bundle: nil, Error: err, Metrics: m, Raw: nil}) + } + return err + } + d.SetCache(resp.etag) // set the current etag sha to the cache + + if d.f != nil { + d.f(ctx, Update{ETag: resp.etag, Bundle: resp.b, Error: nil, Metrics: m, Raw: resp.raw, Size: resp.size}) + } + return nil +} + +func (d *OCIDownloader) download(ctx context.Context, m metrics.Metrics) (*downloaderResponse, error) { + d.logger.Debug("OCI - Download starting.") + var buf bytes.Buffer + + preferences := []string{fmt.Sprintf("modes=%v,%v", defaultBundleMode, deltaBundleMode)} + + preferValue := strings.Join(preferences, ";") + d.client = d.client.WithHeader("Prefer", preferValue) + + m.Timer(metrics.BundleRequest).Start() + desc, err := d.pull(ctx, d.path) + if err != nil { + return &downloaderResponse{}, fmt.Errorf("failed to pull %s: %w", d.path, err) + } + + manifest, err := manifestFromDesc(ctx, d.store, desc) + if err != nil { + return nil, err + } + + tarballDescriptor := ocispec.Descriptor{} + for _, descriptor := range manifest.Layers { + if descriptor.MediaType == "application/vnd.oci.image.layer.v1.tar+gzip" { + tarballDescriptor = descriptor + break + } + } + if tarballDescriptor.MediaType == "" { + return nil, errors.New("no tarball descriptor found in the layers") + } + etag := tarballDescriptor.Digest.Hex() + bundleFilePath := filepath.Join(d.localStorePath, "blobs", "sha256", etag) + // if the downloader etag sha is the same with digest of the tarball it was already loaded + if d.etag == etag { + return &downloaderResponse{ + b: nil, + raw: nil, + etag: etag, + longPoll: false, + }, nil + } + fileReader, err := os.Open(bundleFilePath) + + cnt := &count{} + r := io.TeeReader(fileReader, cnt) + tee := io.TeeReader(r, &buf) + + if err != nil { + return nil, err + } + loader := bundle.NewTarballLoaderWithBaseURL(tee, d.localStorePath) + reader := bundle.NewCustomReader(loader). + WithMetrics(m). + WithBundleVerificationConfig(d.bvc). + WithBundleEtag(etag). + WithRegoVersion(d.bundleParserOpts.RegoVersion) + bundleInfo, err := reader.Read() + if err != nil { + return &downloaderResponse{}, fmt.Errorf("unexpected error %w", err) + } + + m.Timer(metrics.BundleRequest).Stop() + + return &downloaderResponse{ + b: &bundleInfo, + raw: &buf, + etag: etag, + longPoll: false, + size: cnt.Bytes(), + }, nil +} + +func (d *OCIDownloader) pull(ctx context.Context, ref string) (*ocispec.Descriptor, error) { + lookup := d.client.AuthPluginLookup() + + plugin, err := d.client.Config().AuthPlugin(lookup) + if err != nil { + return nil, fmt.Errorf("failed to look up auth plugin: %w", err) + } + + d.logger.Debug("OCIDownloader: using auth plugin: %T", plugin) + + resolver, err := dockerResolver(plugin, d.client.Config(), d.logger) + if err != nil { + return nil, fmt.Errorf("invalid host url %s: %w", d.client.Config().URL, err) + } + + target := remoteManager{ + resolver: resolver, + srcRef: ref, + } + + manifestDescriptor, err := oraslib.Copy(ctx, &target, ref, d.store, "", oraslib.DefaultCopyOptions) + if err != nil { + return nil, fmt.Errorf("download for '%s' failed: %w", ref, err) + } + + return &manifestDescriptor, nil +} + +func dockerResolver(plugin rest.HTTPAuthPlugin, config *rest.Config, logger logging.Logger) (remotes.Resolver, error) { + client, err := plugin.NewClient(*config) + if err != nil { + return nil, fmt.Errorf("failed to create auth client: %w", err) + } + + urlInfo, err := url.Parse(config.URL) + if err != nil { + return nil, fmt.Errorf("failed to parse url: %w", err) + } + + authorizer := pluginAuthorizer{ + plugin: plugin, + client: client, + logger: logger, + } + + registryHost := docker.RegistryHost{ + Host: urlInfo.Host, + Scheme: urlInfo.Scheme, + Capabilities: docker.HostCapabilityPull | docker.HostCapabilityResolve | docker.HostCapabilityPush, + Client: client, + Path: "/v2", + Authorizer: &authorizer, + } + + opts := docker.ResolverOptions{ + Hosts: func(string) ([]docker.RegistryHost, error) { + return []docker.RegistryHost{registryHost}, nil + }, + } + + return docker.NewResolver(opts), nil +} + +type pluginAuthorizer struct { + plugin rest.HTTPAuthPlugin + client *http.Client + + // authorizer will be populated by the first call to pluginAuthorizer.Prepare + // since it requires a first pass through the plugin.Prepare method. + authorizer docker.Authorizer + + logger logging.Logger +} + +var _ docker.Authorizer = &pluginAuthorizer{} + +func (a *pluginAuthorizer) AddResponses(ctx context.Context, responses []*http.Response) error { + return a.authorizer.AddResponses(ctx, responses) +} + +// Authorize uses a rest.HTTPAuthPlugin to Prepare a request before passing it on +// to the docker.Authorizer. +func (a *pluginAuthorizer) Authorize(ctx context.Context, req *http.Request) error { + if err := a.plugin.Prepare(req); err != nil { + err = fmt.Errorf("failed to prepare docker request: %w", err) + + // Make sure to log this before passing the error back to docker + a.logger.Error(err.Error()) + + return err + } + + if a.authorizer == nil { + // Some registry authentication implementations require a token fetch from + // a separate authenticated token server. This flow is described in the + // docker token auth spec: + // https://docs.docker.com/registry/spec/auth/token/#requesting-a-token + // + // Unfortunately, the containerd implementation does not use the Prepare + // mechanism to authenticate these token requests and we need to add + // auth information in form of a static docker.WithAuthHeader. + // + // Since rest.HTTPAuthPlugins will set the auth header on the request + // passed to HTTPAuthPlugin.Prepare, we can use it afterwards to build + // our docker.Authorizer. + a.authorizer = docker.NewDockerAuthorizer( + docker.WithAuthHeader(req.Header), + docker.WithAuthClient(a.client), + ) + } + + return a.authorizer.Authorize(ctx, req) +} + +func manifestFromDesc(ctx context.Context, target oraslib.Target, desc *ocispec.Descriptor) (*ocispec.Manifest, error) { + var manifest ocispec.Manifest + + descReader, err := target.Fetch(ctx, *desc) + if err != nil { + return nil, fmt.Errorf("unable to fetch descriptor with digest %q: %w", desc.Digest, err) + } + defer descReader.Close() + + descBytes, err := io.ReadAll(descReader) + if err != nil { + return nil, fmt.Errorf("unable to read bytes from descriptor: %w", err) + } + + if err = json.Unmarshal(descBytes, &manifest); err != nil { + return nil, fmt.Errorf("unable to unmarshal manifest: %w", err) + } + + if len(manifest.Layers) < 1 { + return nil, errors.New("no layers in manifest") + } + + return &manifest, nil +} + +type remoteManager struct { + resolver remotes.Resolver + srcRef string +} + +func (r *remoteManager) Resolve(ctx context.Context, ref string) (ocispec.Descriptor, error) { + _, desc, err := r.resolver.Resolve(ctx, ref) + if err != nil { + return ocispec.Descriptor{}, err + } + return desc, nil +} + +func (r *remoteManager) Fetch(ctx context.Context, target ocispec.Descriptor) (io.ReadCloser, error) { + fetcher, err := r.resolver.Fetcher(ctx, r.srcRef) + if err != nil { + return nil, err + } + return fetcher.Fetch(ctx, target) +} + +func (r *remoteManager) Exists(ctx context.Context, target ocispec.Descriptor) (bool, error) { + _, err := r.Fetch(ctx, target) + if err == nil { + return true, nil + } + + return !errdefs.IsNotFound(err), err +} diff --git a/third_party/opa/v1/download/oci_download_test.go b/third_party/opa/v1/download/oci_download_test.go new file mode 100644 index 000000000000..2cae55e09b92 --- /dev/null +++ b/third_party/opa/v1/download/oci_download_test.go @@ -0,0 +1,461 @@ +//go:build slow +// +build slow + +package download + +import ( + "context" + "encoding/base64" + "fmt" + "net/http" + "reflect" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/plugins/rest" +) + +// when changed the layer hash & size should be updated in .manifest files +//go:generate go run github.com/open-policy-agent/opa build -b --signing-alg HS256 testdata/latest_bundle_data --output testdata/latest.tar.gz +//go:generate go run github.com/open-policy-agent/opa build -b --signing-alg HS256 --signing-key secret testdata/signed_bundle_data --output testdata/signed.tar.gz +//go:generate go run github.com/open-policy-agent/opa build --v1-compatible -b --signing-alg HS256 --signing-key secret testdata/rego_v1_bundle_data --output testdata/rego_v1.tar.gz + +func TestOCIDownloaderWithBundleVerificationConfig(t *testing.T) { + vc := bundle.NewVerificationConfig(map[string]*bundle.KeyConfig{"default": {Key: "secret", Algorithm: "HS256"}}, "", "", nil) + ctx := context.Background() + fixture := newTestFixture(t) + fixture.server.expEtag = "sha256:c5834dbce332cabe6ae68a364de171a50bf5b08024c27d7c08cc72878b4df7ff" + + updates := make(chan *Update) + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + d := NewOCI(config, fixture.client, "ghcr.io/org/repo:signed", "/tmp/opa/").WithCallback(func(_ context.Context, u Update) { + if u.Error != nil { + t.Fatalf("expected no error but got: %v", u.Error) + } + updates <- &u + }).WithBundleVerificationConfig(vc) + + d.Start(ctx) + + // Give time for some download events to occur + time.Sleep(1 * time.Second) + + u1 := <-updates + + if u1.Bundle == nil || len(u1.Bundle.Modules) == 0 { + t.Fatal("expected bundle with at least one module but got:", u1) + } + + if !strings.HasSuffix(u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) { + t.Fatalf("expected URL to have path as suffix but got %v and %v", u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) + } + + d.Stop(ctx) + +} + +func TestOCIDownloaderWithRegoV1Bundle(t *testing.T) { + tests := []struct { + note string + regoVersion ast.RegoVersion + expErr string + }{ + // The bundle contains a v1 rego_version attr, so we expect no errors regardless of parser regoVersion. + { + note: "non-1.0 compatible OCI downloader", + }, + { + note: "1.0 compatible OCI downloader", + regoVersion: ast.RegoV1, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + vc := bundle.NewVerificationConfig(map[string]*bundle.KeyConfig{"default": {Key: "secret", Algorithm: "HS256"}}, "", "", nil) + ctx := context.Background() + fixture := newTestFixture(t) + fixture.server.expEtag = "sha256:c5834dbce332cabe6ae68a364de171a50bf5b08024c27d7c08cc72878b4df7ff" + + // We might get multiple updates, a buffered channel will make sure we save the first one. + updates := make(chan *Update, 1) + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + d := NewOCI(config, fixture.client, "ghcr.io/org/repo:rego_v1", "/tmp/opa/"). + WithBundleParserOpts(ast.ParserOptions{RegoVersion: tc.regoVersion}). + WithCallback(func(_ context.Context, u Update) { + // We might get multiple updates before the test ends, and we don't want to block indefinitely. + select { + case updates <- &u: + } + }).WithBundleVerificationConfig(vc) + + d.Start(ctx) + + // Give time for some download events to occur + time.Sleep(1 * time.Second) + + // We only care about the first update + u1 := <-updates + + if tc.expErr != "" { + if u1.Error == nil { + t.Fatalf("expected error but got: %v", u1) + } else { + if !strings.Contains(u1.Error.Error(), tc.expErr) { + t.Fatalf("expected error:\n\n%v\n\nbut got:\n\n%v", tc.expErr, u1.Error) + } + } + } else { + if u1.Error != nil { + t.Fatalf("expected no error but got: %v", u1.Error) + } + + if u1.Bundle == nil || len(u1.Bundle.Modules) == 0 { + t.Fatal("expected bundle with at least one module but got:", u1) + } + + if !strings.HasSuffix(u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) { + t.Fatalf("expected URL to have path as suffix but got %v and %v", u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) + } + } + + d.Stop(ctx) + }) + } +} + +func TestOCIStartStop(t *testing.T) { + ctx := context.Background() + fixture := newTestFixture(t) + fixture.server.expEtag = "sha256:cc09b0f5ac97b11637c96ff1b0fbbc287c5ba0169813edaa71fe58424e95f0b7" + + updates := make(chan *Update) + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + d := NewOCI(config, fixture.client, "ghcr.io/org/repo:latest", "/tmp/opa/").WithCallback(func(_ context.Context, u Update) { + updates <- &u + }) + + d.Start(ctx) + + // Give time for some download events to occur + time.Sleep(1 * time.Second) + + u1 := <-updates + + if u1.Bundle == nil || len(u1.Bundle.Modules) == 0 { + t.Fatal("expected bundle with at least one module but got:", u1) + } + + if !strings.HasSuffix(u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) { + t.Fatalf("expected URL to have path as suffix but got %v and %v", u1.Bundle.Modules[0].URL, u1.Bundle.Modules[0].Path) + } + + d.Stop(ctx) +} + +func TestOCIBearerAuthPlugin(t *testing.T) { + ctx := context.Background() + fixture := newTestFixture(t) + plainToken := "secret" + token := base64.StdEncoding.EncodeToString([]byte(plainToken)) // token should be base64 encoded + fixture.server.expAuth = fmt.Sprintf("Bearer %s", token) // test on private repository + fixture.server.expEtag = "sha256:c5834dbce332cabe6ae68a364de171a50bf5b08024c27d7c08cc72878b4df7ff" + + restConf := fmt.Sprintf(`{ + "url": %q, + "type": "oci", + "credentials": { + "bearer": { + "token": %q + } + } + }`, fixture.server.server.URL, plainToken) + + client, err := rest.New([]byte(restConf), map[string]*keys.Config{}) + if err != nil { + t.Fatal(err) + } + + fixture.setClient(client) + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + d := NewOCI(config, fixture.client, "ghcr.io/org/repo:latest", "/tmp/oci") + + if err := d.oneShot(ctx); err != nil { + t.Fatal(err) + } +} + +func TestOCIFailureAuthn(t *testing.T) { + ctx := context.Background() + fixture := newTestFixture(t) + fixture.server.expAuth = "Bearer badsecret" + defer fixture.server.stop() + + d := NewOCI(Config{}, fixture.client, "ghcr.io/org/repo:latest", "/tmp/oci") + + err := d.oneShot(ctx) + if err == nil { + t.Fatal("expected error") + } + if !strings.Contains(err.Error(), "401 Unauthorized") { + t.Fatal("expected 401 Unauthorized message") + } +} + +func TestOCIEtag(t *testing.T) { + fixture := newTestFixture(t) + token := base64.StdEncoding.EncodeToString([]byte("secret")) // token should be base64 encoded + fixture.server.expAuth = fmt.Sprintf("Bearer %s", token) // test on private repository + fixture.server.expEtag = "sha256:c5834dbce332cabe6ae68a364de171a50bf5b08024c27d7c08cc72878b4df7ff" + + restConfig := []byte(fmt.Sprintf(`{ + "url": %q, + "type": "oci", + "credentials": { + "bearer": { + "token": "secret" + } + } + }`, fixture.server.server.URL)) + + client, err := rest.New(restConfig, map[string]*keys.Config{}) + if err != nil { + t.Fatal(err) + } + + fixture.setClient(client) + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + firstResponse := Update{ETag: ""} + d := NewOCI(config, fixture.client, "ghcr.io/org/repo:latest", "/tmp/oci").WithCallback(func(_ context.Context, u Update) { + if firstResponse.ETag == "" { + firstResponse = u + return + } + + if u.ETag != firstResponse.ETag || u.Bundle != nil { + t.Fatal("expected nil bundle and same etag but got:", u) + } + }) + + // fill firstResponse + if err := d.oneShot(context.Background()); err != nil { + t.Fatal(err) + } + // Give time for some download events to occur + time.Sleep(1 * time.Second) + + // second call to verify if nil bundle is returned and same etag + err = d.oneShot(context.Background()) + if err != nil { + t.Fatal(err) + } +} + +// TestOCIPublicRegistryAuth tests the registry `token` auth +// that is implemented by public registries (more details are +// in the doc comment of withPublicRegistryAuth). +// +// Other tests that don't explicitly set an authentication method +// implicitly test no authentication - this is different from +// the mechanism used by public registries. +func TestOCIPublicRegistryAuth(t *testing.T) { + fixture := newTestFixture(t, withPublicRegistryAuth()) + + restConfig := []byte(fmt.Sprintf(`{ + "url": %q, + "type": "oci" + }`, fixture.server.server.URL)) + + client, err := rest.New(restConfig, map[string]*keys.Config{}) + if err != nil { + t.Fatalf("failed to create rest client: %s", err) + } + fixture.client = client + + d := NewOCI(Config{}, fixture.client, "ghcr.io/org/repo:latest", t.TempDir()) + + if err := d.oneShot(context.Background()); err != nil { + t.Fatalf("Unexpected error: %s", err) + } +} + +// TestOCITokenAuth tests the registry `token` auth that is used for some registries (f.e. gitlab). +// After the initial fetch the token has to be added to the request that fetches the temporary token. +// This test verifies that the token is added to the second token request. +func TestOCITokenAuth(t *testing.T) { + ctx := context.Background() + fixture := newTestFixture(t, withAuthenticatedTokenAuth()) + plainToken := "secret" + token := base64.StdEncoding.EncodeToString([]byte(plainToken)) // token should be base64 encoded + fixture.server.expAuth = fmt.Sprintf("Bearer %s", token) // test on private repository + fixture.server.expEtag = "sha256:c5834dbce332cabe6ae68a364de171a50bf5b08024c27d7c08cc72878b4df7ff" + + restConf := fmt.Sprintf(`{ + "url": %q, + "type": "oci", + "credentials": { + "bearer": { + "token": %q + } + } + }`, fixture.server.server.URL, plainToken) + + client, err := rest.New([]byte(restConf), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("failed to create rest client: %s", err) + } + fixture.setClient(client) + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + d := NewOCI(Config{}, fixture.client, "ghcr.io/org/repo:latest", t.TempDir()) + + if err := d.oneShot(ctx); err != nil { + t.Fatalf("Unexpected error: %s", err) + } +} + +func TestOCICustomAuthPlugin(t *testing.T) { + fixture := newTestFixture(t) + defer fixture.server.stop() + + restConfig := []byte(fmt.Sprintf(`{ + "url": %q, + "credentials": { + "plugin": "my_plugin" + } + }`, fixture.server.server.URL)) + + client, err := rest.New( + restConfig, + map[string]*keys.Config{}, + rest.AuthPluginLookup(mockAuthPluginLookup), + ) + if err != nil { + t.Fatal(err) + } + + fixture.setClient(client) + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + tmpDir := t.TempDir() + + d := NewOCI(config, fixture.client, "ghcr.io/org/repo:latest", tmpDir) + + if err := d.oneShot(context.Background()); err != nil { + t.Fatal(err) + } +} + +func TestOCIValidateAndInjectDefaults(t *testing.T) { + ctx := context.Background() + fixture := newTestFixture(t) + fixture.server.expEtag = "sha256:c5834dbce332cabe6ae68a364de171a50bf5b08024c27d7c08cc72878b4df7ff" + + updates := make(chan *Update) + + config := Config{} + if err := config.ValidateAndInjectDefaults(); err != nil { + t.Fatal(err) + } + + d := NewOCI(config, fixture.client, "ghcr.io/org/repo:latest", t.TempDir()).WithCallback(func(_ context.Context, u Update) { + updates <- &u + }).WithBundlePersistence(true) + + d.Start(ctx) + + // Give time for some download events to occur + time.Sleep(1 * time.Second) + + u1 := <-updates + + if u1.Size == 0 { + t.Fatal("expected non-0 size") + } + + if u1.Raw == nil { + t.Fatal("expected bundle reader to be non-nil") + } + + r := bundle.NewReader(u1.Raw) + + b, err := r.Read() + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(b.Data, u1.Bundle.Data) { + t.Fatal("expected the bundle object and reader to have the same data") + } + + if len(b.Modules) != len(u1.Bundle.Modules) { + t.Fatal("expected the bundle object and reader to have the same number of bundle modules") + } + + d.Stop(ctx) +} + +func mockAuthPluginLookup(string) rest.HTTPAuthPlugin { + return &mockAuthPlugin{} +} + +type mockAuthPlugin struct{} + +func (p *mockAuthPlugin) NewClient(c rest.Config) (*http.Client, error) { + tlsConfig, err := rest.DefaultTLSConfig(c) + if err != nil { + return nil, err + } + + timeoutSec := 10 + + client := rest.DefaultRoundTripperClient( + tlsConfig, + int64(timeoutSec), + ) + + return client, nil +} + +func (*mockAuthPlugin) Prepare(r *http.Request) error { + r.Header.Set("Authorization", "Bearer secret") + return nil +} diff --git a/third_party/opa/v1/download/oci_download_unavailable.go b/third_party/opa/v1/download/oci_download_unavailable.go new file mode 100644 index 000000000000..f0bef46620e0 --- /dev/null +++ b/third_party/opa/v1/download/oci_download_unavailable.go @@ -0,0 +1,59 @@ +//go:build opa_no_oci + +package download + +import ( + "context" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/plugins/rest" +) + +func NewOCI(Config, rest.Client, string, string) *OCIDownloader { + panic("built without OCI support") +} + +func (d *OCIDownloader) WithCallback(f func(context.Context, Update)) *OCIDownloader { + panic("built without OCI support") +} + +func (d *OCIDownloader) WithLogAttrs(map[string]any) *OCIDownloader { + panic("built without OCI support") +} + +func (d *OCIDownloader) WithBundleVerificationConfig(*bundle.VerificationConfig) *OCIDownloader { + panic("built without OCI support") +} + +func (d *OCIDownloader) WithSizeLimitBytes(int64) *OCIDownloader { + panic("built without OCI support") +} + +func (d *OCIDownloader) WithBundlePersistence(bool) *OCIDownloader { + panic("built without OCI support") +} + +func (d *OCIDownloader) ClearCache() { + panic("built without OCI support") +} + +func (d *OCIDownloader) SetCache(string) { + panic("built without OCI support") +} + +func (d *OCIDownloader) Trigger(context.Context) error { + panic("built without OCI support") +} + +func (d *OCIDownloader) Start(context.Context) { + panic("built without OCI support") +} + +func (d *OCIDownloader) Stop(context.Context) { + panic("built without OCI support") +} + +func (*OCIDownloader) WithBundleParserOpts(ast.ParserOptions) *OCIDownloader { + panic("built without OCI support") +} diff --git a/third_party/opa/v1/download/oci_downloader.go b/third_party/opa/v1/download/oci_downloader.go new file mode 100644 index 000000000000..27939d92d083 --- /dev/null +++ b/third_party/opa/v1/download/oci_downloader.go @@ -0,0 +1,32 @@ +package download + +import ( + "context" + "sync" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/plugins/rest" + "oras.land/oras-go/v2/content/oci" +) + +type OCIDownloader struct { + config Config // downloader configuration for tuning polling and other downloader behaviour + client rest.Client // HTTP client to use for bundle downloading + path string // path for OCI image as //: + localStorePath string // path for the local OCI storage + trigger chan chan struct{} // channel to signal downloads when manual triggering is enabled + stop chan chan struct{} // used to signal plugin to stop running + f func(context.Context, Update) // callback function invoked when download updates occur + sizeLimitBytes *int64 // max bundle file size in bytes (passed to reader) + bvc *bundle.VerificationConfig + wg sync.WaitGroup + logger logging.Logger + mtx sync.Mutex + stopped bool + persist bool + store *oci.Store + etag string + bundleParserOpts ast.ParserOptions +} diff --git a/third_party/opa/v1/download/testdata/config.layer b/third_party/opa/v1/download/testdata/config.layer new file mode 100644 index 000000000000..9e26dfeeb6e6 --- /dev/null +++ b/third_party/opa/v1/download/testdata/config.layer @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/third_party/opa/v1/download/testdata/latest.manifest b/third_party/opa/v1/download/testdata/latest.manifest new file mode 100644 index 000000000000..852adf9eef1d --- /dev/null +++ b/third_party/opa/v1/download/testdata/latest.manifest @@ -0,0 +1,19 @@ +{ + "schemaVersion":2, + "config":{ + "mediaType":"application/vnd.oci.image.config.v1+json", + "digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", + "size":2 + }, + "layers":[ + { + "mediaType":"application/vnd.oci.image.layer.v1.tar+gzip", + "digest":"sha256:d85a3b7072e295a091f4ec50e85fefcd5285a1e2c60c298c0b87c498f1cb0613", + "size":610, + "annotations":{ + "org.opencontainers.image.created":"2022-02-11T09:00:07Z", + "org.opencontainers.image.title":"dani/testpol" + } + } + ] +} \ No newline at end of file diff --git a/third_party/opa/v1/download/testdata/latest.tar.gz b/third_party/opa/v1/download/testdata/latest.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..eb8e15283b73d8d239870d169b3749dfc7fc9d27 GIT binary patch literal 610 zcmV-o0-gOIiwFP!00000|LmDvi{d;KfW3Y5D?+Y^G_9=z1Kz!|EbKB1JA1V(CEBB8 zH`9b9oxKqM`_ek1) z*Mb%njHTsokC1rQ$~Y`?UAU@)a_5Yg77i@cMrklkfW_S6vNtW8>}FQ^$k3M%p$z7{ zkPdSx)nCZ)!x+!Gw6N`Y5mrnkH1t5ur*d!3<|<;2m*~IkvNQwDjeCaN;qe%gM-7HM zp>j+1hB@IRR3kn=;jcXB3orz&5ZY{_%Y|16ZFabx_PRUteuHdB{}-nDzz5l(? z=>NCJ?}q{40@=SglDsYXUtx|uCtdw7&XE7(I627wquBf32hINfc;EW3J5IEJf7;Id zh^!pGZZKd~VP-(ZjR%I~{XePnuV}6RliK&ch~4}A-w*Bezc2l7ndALGsr0XC^FOJ7 z|Bu2jj=cYUa76w8p+Ed>-wtl`wIQI-JQs7Y?tE*s`Oog7ga7|V-v3@$5(DKEuX6H0 w$Q?0CIZGaXlHGtne%+B$=@s4_<53EIb^xF{|5hcuJ^_LB0K^Q^dEs={{*0ai%#%~argA}r2TAV z4PQY!53{`P0quPlrS}i$-ajNO8Qwq7ZSs8A-lb9m<@eC4|Mr79KJq^@yKnuEkqJAE z|G$Er7i_&K)WgD4I~i))gGi;9i!{mfAWd>lB$=+VNEd7UQTweP_0M&%38hSd_M~Z^ zlriWY$$yu2D-%)T<%fNkebzDH1OFp@`p*9do9MLh|5xy+ z>ELw-fWCgK={g9@(Z;s}F%~0dQ8|bTCv#N3cFbe&`>17o^nH`C3vF_Ikj)~k59X36rv?Lyw*>1JgQ9=xk53@&>|9~V1j zY##!H^gQocby}NFDinE zf>cXMl~b@vZdp7qiA{>}d?cvL3{ouxQHzpQTSRSw_;YE)jN@ME-Aq%z)-I_tnZc6I znOM(?Q%7|+zPqSD$4fnGSNYg{t8ISK;r60r^FhV!k*amEcyQ!%z9CjnP-{}qY9(kq z72JwUZWGMyrKDAas6|YsXiTl3q_K>7U6;!coBfD3=is};gXeM?7?U^J=$t^RSViLt zW9&{!q6j7y6-`yFG*f5U#r+vQW<$t&SEmzy z7M)s)IRu)1ccSTFCl8TGKwk&U;O+#xTnWPc%Q9-taSaU(4gVkh4gdiE|8W8P IP5>4F07vJWG5`Po literal 0 HcmV?d00001 diff --git a/third_party/opa/v1/download/testdata/rego_v1_bundle_data/a/b/c/data.json b/third_party/opa/v1/download/testdata/rego_v1_bundle_data/a/b/c/data.json new file mode 100644 index 000000000000..3a26a2e5e94d --- /dev/null +++ b/third_party/opa/v1/download/testdata/rego_v1_bundle_data/a/b/c/data.json @@ -0,0 +1 @@ +[1,2,3] \ No newline at end of file diff --git a/third_party/opa/v1/download/testdata/rego_v1_bundle_data/http/policy/policy.rego b/third_party/opa/v1/download/testdata/rego_v1_bundle_data/http/policy/policy.rego new file mode 100644 index 000000000000..65bf71b531d3 --- /dev/null +++ b/third_party/opa/v1/download/testdata/rego_v1_bundle_data/http/policy/policy.rego @@ -0,0 +1,9 @@ +package example + +violations contains msg if { + msg := "hello" +} + +allow if { + count(violations) == 0 +} \ No newline at end of file diff --git a/third_party/opa/v1/download/testdata/signed.manifest b/third_party/opa/v1/download/testdata/signed.manifest new file mode 100644 index 000000000000..f5e5b6b98f1f --- /dev/null +++ b/third_party/opa/v1/download/testdata/signed.manifest @@ -0,0 +1,19 @@ +{ + "schemaVersion":2, + "config":{ + "mediaType":"application/vnd.oci.image.config.v1+json", + "digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", + "size":2 + }, + "layers":[ + { + "mediaType":"application/vnd.oci.image.layer.v1.tar+gzip", + "digest":"sha256:7fccf82798e6e627afd04144889570d966583788473db2888f0d0d325904d273", + "size":764, + "annotations":{ + "org.opencontainers.image.created":"2022-02-11T09:00:07Z", + "org.opencontainers.image.title":"dani/testpol" + } + } + ] +} \ No newline at end of file diff --git a/third_party/opa/v1/download/testdata/signed.tar.gz b/third_party/opa/v1/download/testdata/signed.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..9e8374cdd7cb4464f96ae5383d4bec8e692fc17c GIT binary patch literal 764 zcmV-MCBZSC& zxo_tU16t^;58uwx3sq72$20h!wZ1Rzf5Id1!SoRX?PUk1H^#smpxdXXr{1r2*6|*U z%BU#YJz!LbeHMjxL6wCvx_h3A%Q7G32Pt-^e}~~Q+8=&aG27+Dw--X=Y zrT#wZM~$y?8Fi`u%@_^a-1}v9jEc8v1769$Vg6x#_y0G?#-z*td+^xngO@J=O#3&z zejh}qc^o)9u@G~-(b%8@7g&`!|AZ%4tt3~r5nL_B{c;$c=F0aPxjW&BhYArWyX0#; z!P!dg&?;qLR`E5WIX5lI?Q;=&RvI|ld+ykftn z&4N=a8^=3y-&&Ex+0=AK8kR84-2Is7jX@Nysk?8exzxm*$Hb+I>hwetToYF&+}SFY ztR(epNoK5O8e0pGG~C%V+{0rmYQelF3(!0dow&rwL2O`raqYHc&(E)~zN$&B;?TLQ z{ER*X;%jf%A~TtC_gOz>iXraVg18rws>_<{OGsyzh?>}>&OBYr;*@Ega&LncvkfFV z7R)4&YHTh%s+hYsc(RRu%A2rC=2@gbLzHNoMJoCSl5yEvZfoW)e+Y8S{3H6P|K47c{}>HE&T u0FH-4S%B#c0Jj6sHv4xE(ea 0 { + return nil, errs + } + } + + formatted, err := AstWithOpts(module, opts) + if err != nil { + return nil, fmt.Errorf("%s: %v", filename, err) + } + + return formatted, nil +} + +// MustAst is a helper function to format a Rego AST element. If any errors +// occur this function will panic. This is mostly used for test +func MustAst(x any) []byte { + bs, err := Ast(x) + if err != nil { + panic(err) + } + return bs +} + +// MustAstWithOpts is a helper function to format a Rego AST element. If any errors +// occur this function will panic. This is mostly used for test +func MustAstWithOpts(x any, opts Opts) []byte { + bs, err := AstWithOpts(x, opts) + if err != nil { + panic(err) + } + return bs +} + +// Ast formats a Rego AST element. If the passed value is not a valid AST +// element, Ast returns nil and an error. If AST nodes are missing locations +// an arbitrary location will be used. +func Ast(x any) ([]byte, error) { + return AstWithOpts(x, Opts{}) +} + +type fmtOpts struct { + // When the future keyword "contains" is imported, all the pretty-printed + // modules will use that format for partial sets. + // NOTE(sr): For ref-head rules, this will be the default behaviour, since + // we need "contains" to disambiguate complete rules from partial sets. + contains bool + + // Same logic applies as for "contains": if `future.keywords.if` (or all + // future keywords) is imported, we'll render rules that can use `if` with + // `if`. + ifs bool + + // We check all rule ref heads to see if any of them _requires_ support + // for ref heads -- if they do, we'll print all of them in a different way + // than if they don't. + refHeads bool + + regoV1 bool + regoV1Imported bool + futureKeywords []string + + // If true, the formatter will retain keywords in refs, e.g. `p.not ` instead of `p["not"]`. + // The format of the original ref is preserved, so `p["not"]` will still be formatted as `p["not"]`. + allowKeywordsInRefs bool +} + +func (o fmtOpts) keywords() []string { + if o.regoV1 { + return ast.KeywordsV1[:] + } + kws := ast.KeywordsV0[:] + return append(kws, o.futureKeywords...) +} + +func AstWithOpts(x any, opts Opts) ([]byte, error) { + // The node has to be deep copied because it may be mutated below. Alternatively, + // we could avoid the copy by checking if mutation will occur first. For now, + // since format is not latency sensitive, just deep copy in all cases. + x = ast.Copy(x) + + wildcards := map[ast.Var]*ast.Term{} + + // NOTE(sr): When the formatter encounters a call to internal.member_2 + // or internal.member_3, it will sugarize them into usage of the `in` + // operator. It has to ensure that the proper future keyword import is + // present. + extraFutureKeywordImports := map[string]struct{}{} + + o := fmtOpts{} + + regoVersion := opts.effectiveRegoVersion() + if regoVersion == ast.RegoV0CompatV1 || regoVersion == ast.RegoV1 { + o.regoV1 = true + o.ifs = true + o.contains = true + } + + capabilities := opts.Capabilities + if capabilities == nil { + capabilities = ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(opts.effectiveRegoVersion())) + } + o.allowKeywordsInRefs = capabilities.ContainsFeature(ast.FeatureKeywordsInRefs) + + memberRef := ast.Member.Ref() + memberWithKeyRef := ast.MemberWithKey.Ref() + + // Preprocess the AST. Set any required defaults and calculate + // values required for printing the formatted output. + ast.WalkNodes(x, func(x ast.Node) bool { + switch n := x.(type) { + case ast.Body: + if len(n) == 0 { + return false + } + case *ast.Term: + unmangleWildcardVar(wildcards, n) + + case *ast.Expr: + switch { + case n.IsCall() && memberRef.Equal(n.Operator()) || memberWithKeyRef.Equal(n.Operator()): + extraFutureKeywordImports["in"] = struct{}{} + case n.IsEvery(): + extraFutureKeywordImports["every"] = struct{}{} + } + + case *ast.Import: + if kw, ok := future.WhichFutureKeyword(n); ok { + o.futureKeywords = append(o.futureKeywords, kw) + } + + switch { + case isRegoV1Compatible(n): + o.regoV1Imported = true + o.contains = true + o.ifs = true + case future.IsAllFutureKeywords(n): + o.contains = true + o.ifs = true + case future.IsFutureKeyword(n, "contains"): + o.contains = true + case future.IsFutureKeyword(n, "if"): + o.ifs = true + } + + case *ast.Rule: + if len(n.Head.Ref()) > 2 { + o.refHeads = true + } + if len(n.Head.Ref()) == 2 && n.Head.Key != nil && n.Head.Value == nil { // p.q contains "x" + o.refHeads = true + } + } + + if opts.IgnoreLocations || x.Loc() == nil { + x.SetLoc(defaultLocation(x)) + } + return false + }) + + w := &writer{ + indent: "\t", + errs: make([]*ast.Error, 0), + fmtOpts: o, + } + + switch x := x.(type) { + case *ast.Module: + if regoVersion == ast.RegoV1 && opts.DropV0Imports { + x.Imports = filterRegoV1Import(x.Imports) + } else if regoVersion == ast.RegoV0CompatV1 { + x.Imports = ensureRegoV1Import(x.Imports) + } + + regoV1Imported := slices.ContainsFunc(x.Imports, isRegoV1Compatible) + if regoVersion == ast.RegoV0CompatV1 || regoVersion == ast.RegoV1 || regoV1Imported { + if !opts.DropV0Imports && !regoV1Imported { + for _, kw := range o.futureKeywords { + x.Imports = ensureFutureKeywordImport(x.Imports, kw) + } + } else { + x.Imports = future.FilterFutureImports(x.Imports) + } + } else { + for kw := range extraFutureKeywordImports { + x.Imports = ensureFutureKeywordImport(x.Imports, kw) + } + } + err := w.writeModule(x) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + case *ast.Package: + _, err := w.writePackage(x, nil) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + case *ast.Import: + _, err := w.writeImports([]*ast.Import{x}, nil) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + case *ast.Rule: + _, err := w.writeRule(x, false /* isElse */, nil) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + case *ast.Head: + _, err := w.writeHead(x, + false, // isDefault + false, // isExpandedConst + nil) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + case ast.Body: + _, err := w.writeBody(x, nil) + if err != nil { + return nil, err + } + case *ast.Expr: + _, err := w.writeExpr(x, nil) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + case *ast.With: + _, err := w.writeWith(x, nil, false) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + case *ast.Term: + _, err := w.writeTerm(x, nil) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + case ast.Value: + _, err := w.writeTerm(&ast.Term{Value: x, Location: &ast.Location{}}, nil) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + case *ast.Comment: + err := w.writeComments([]*ast.Comment{x}) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + default: + return nil, fmt.Errorf("not an ast element: %v", x) + } + + if len(w.errs) > 0 { + return nil, w.errs + } + return squashTrailingNewlines(w.buf.Bytes()), nil +} + +func unmangleWildcardVar(wildcards map[ast.Var]*ast.Term, n *ast.Term) { + + v, ok := n.Value.(ast.Var) + if !ok || !v.IsWildcard() { + return + } + + first, ok := wildcards[v] + if !ok { + wildcards[v] = n + return + } + + w := v[len(ast.WildcardPrefix):] + + // Prepend an underscore to ensure the variable will parse. + if len(w) == 0 || w[0] != '_' { + w = "_" + w + } + + if first != nil { + first.Value = w + wildcards[v] = nil + } + + n.Value = w +} + +func squashTrailingNewlines(bs []byte) []byte { + if bytes.HasSuffix(bs, []byte("\n")) { + return append(bytes.TrimRight(bs, "\n"), '\n') + } + return bs +} + +func defaultLocation(x ast.Node) *ast.Location { + return ast.NewLocation([]byte(x.String()), defaultLocationFile, 1, 1) +} + +type writer struct { + buf bytes.Buffer + + indent string + level int + inline bool + beforeEnd *ast.Comment + delay bool + errs ast.Errors + fmtOpts fmtOpts + writeCommentOnFinalLine bool +} + +func (w *writer) writeModule(module *ast.Module) error { + var pkg *ast.Package + var others []any + var comments []*ast.Comment + visitor := ast.NewGenericVisitor(func(x any) bool { + switch x := x.(type) { + case *ast.Comment: + comments = append(comments, x) + return true + case *ast.Import, *ast.Rule: + others = append(others, x) + return true + case *ast.Package: + pkg = x + return true + default: + return false + } + }) + visitor.Walk(module) + + sort.Slice(comments, func(i, j int) bool { + l, err := locLess(comments[i], comments[j]) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + return l + }) + + sort.Slice(others, func(i, j int) bool { + l, err := locLess(others[i], others[j]) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + return l + }) + + comments = trimTrailingWhitespaceInComments(comments) + + var err error + comments, err = w.writePackage(pkg, comments) + if err != nil { + return err + } + var imports []*ast.Import + var rules []*ast.Rule + for len(others) > 0 { + imports, others = gatherImports(others) + comments, err = w.writeImports(imports, comments) + if err != nil { + return err + } + rules, others = gatherRules(others) + comments, err = w.writeRules(rules, comments) + if err != nil { + return err + } + } + + for i, c := range comments { + w.writeLine(c.String()) + if i == len(comments)-1 { + w.write("\n") + } + } + + return nil +} + +func trimTrailingWhitespaceInComments(comments []*ast.Comment) []*ast.Comment { + for _, c := range comments { + c.Text = bytes.TrimRightFunc(c.Text, unicode.IsSpace) + } + + return comments +} + +func (w *writer) writePackage(pkg *ast.Package, comments []*ast.Comment) ([]*ast.Comment, error) { + var err error + comments, err = w.insertComments(comments, pkg.Location) + if err != nil { + return nil, err + } + + w.startLine() + + // Omit head as all packages have the DefaultRootDocument prepended at parse time. + path := make(ast.Ref, len(pkg.Path)-1) + if len(path) == 0 { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, pkg.Location, "invalid package path: %s", pkg.Path)) + return comments, nil + } + + path[0] = ast.VarTerm(string(pkg.Path[1].Value.(ast.String))) + copy(path[1:], pkg.Path[2:]) + + w.write("package ") + _, err = w.writeRef(path, nil) + if err != nil { + return nil, err + } + + w.blankLine() + + return comments, nil +} + +func (w *writer) writeComments(comments []*ast.Comment) error { + for i := range comments { + if i > 0 { + l, err := locCmp(comments[i], comments[i-1]) + if err != nil { + return err + } + if l > 1 { + w.blankLine() + } + } + + w.writeLine(comments[i].String()) + } + + return nil +} + +func (w *writer) writeRules(rules []*ast.Rule, comments []*ast.Comment) ([]*ast.Comment, error) { + for i, rule := range rules { + var err error + comments, err = w.insertComments(comments, rule.Location) + if err != nil && !errors.As(err, &unexpectedCommentError{}) { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + + comments, err = w.writeRule(rule, false, comments) + if err != nil && !errors.As(err, &unexpectedCommentError{}) { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + + if i < len(rules)-1 && w.groupableOneLiner(rule) { + next := rules[i+1] + if w.groupableOneLiner(next) && next.Location.Row == rule.Location.Row+1 { + // Current rule and the next are both groupable one-liners, and + // adjacent in the original policy (i.e. no extra newlines between them). + continue + } + } + w.blankLine() + } + return comments, nil +} + +var expandedConst = ast.NewBody(ast.NewExpr(ast.InternedTerm(true))) + +func (w *writer) groupableOneLiner(rule *ast.Rule) bool { + // Location required to determine if two rules are adjacent in the policy. + // If not, we respect line breaks between rules. + if len(rule.Body) > 1 || rule.Default || rule.Location == nil { + return false + } + + partialSetException := w.fmtOpts.contains || rule.Head.Value != nil + + return (w.fmtOpts.regoV1 || w.fmtOpts.ifs) && partialSetException +} + +func (w *writer) writeRule(rule *ast.Rule, isElse bool, comments []*ast.Comment) ([]*ast.Comment, error) { + if rule == nil { + return comments, nil + } + + if !isElse { + w.startLine() + } + + if rule.Default { + w.write("default ") + } + + // OPA transforms lone bodies like `foo = {"a": "b"}` into rules of the form + // `foo = {"a": "b"} { true }` in the AST. We want to preserve that notation + // in the formatted code instead of expanding the bodies into rules, so we + // pretend that the rule has no body in this case. + isExpandedConst := rule.Body.Equal(expandedConst) && rule.Else == nil + w.writeCommentOnFinalLine = isExpandedConst + + var err error + var unexpectedComment bool + comments, err = w.writeHead(rule.Head, rule.Default, isExpandedConst, comments) + if err != nil { + if errors.As(err, &unexpectedCommentError{}) { + unexpectedComment = true + } else { + return nil, err + } + } + + if len(rule.Body) == 0 || isExpandedConst { + w.endLine() + return comments, nil + } + + w.writeCommentOnFinalLine = true + + // this excludes partial sets UNLESS `contains` is used + partialSetException := w.fmtOpts.contains || rule.Head.Value != nil + + if (w.fmtOpts.regoV1 || w.fmtOpts.ifs) && partialSetException { + w.write(" if") + if len(rule.Body) == 1 { + if rule.Body[0].Location.Row == rule.Head.Location.Row { + w.write(" ") + var err error + comments, err = w.writeExpr(rule.Body[0], comments) + if err != nil { + return nil, err + } + w.endLine() + if rule.Else != nil { + comments, err = w.writeElse(rule, comments) + if err != nil { + return nil, err + } + } + return comments, nil + } + } + } + if unexpectedComment && len(comments) > 0 { + w.write(" { ") + } else { + w.write(" {") + w.endLine() + } + + w.up() + + comments, err = w.writeBody(rule.Body, comments) + if err != nil { + // the unexpected comment error is passed up to be handled by writeHead + if !errors.As(err, &unexpectedCommentError{}) { + return nil, err + } + } + + var closeLoc *ast.Location + + if len(rule.Head.Args) > 0 { + closeLoc = closingLoc('(', ')', '{', '}', rule.Location) + } else if rule.Head.Key != nil { + closeLoc = closingLoc('[', ']', '{', '}', rule.Location) + } else { + closeLoc = closingLoc(0, 0, '{', '}', rule.Location) + } + + comments, err = w.insertComments(comments, closeLoc) + if err != nil { + return nil, err + } + + if err := w.down(); err != nil { + return nil, err + } + w.startLine() + w.write("}") + if rule.Else != nil { + comments, err = w.writeElse(rule, comments) + if err != nil { + return nil, err + } + } + return comments, nil +} + +var elseVar ast.Value = ast.Var("else") + +func (w *writer) writeElse(rule *ast.Rule, comments []*ast.Comment) ([]*ast.Comment, error) { + // If there was nothing else on the line before the "else" starts + // then preserve this style of else block, otherwise it will be + // started as an "inline" else eg: + // + // p { + // ... + // } + // + // else { + // ... + // } + // + // versus + // + // p { + // ... + // } else { + // ... + // } + // + // Note: This doesn't use the `close` as it currently isn't accurate for all + // types of values. Checking the actual line text is the most consistent approach. + wasInline := false + ruleLines := bytes.Split(rule.Location.Text, []byte("\n")) + relativeElseRow := rule.Else.Location.Row - rule.Location.Row + if relativeElseRow > 0 && relativeElseRow < len(ruleLines) { + elseLine := ruleLines[relativeElseRow] + if !bytes.HasPrefix(bytes.TrimSpace(elseLine), []byte("else")) { + wasInline = true + } + } + + // If there are any comments between the closing brace of the previous rule and the start + // of the else block we will always insert a new blank line between them. + hasCommentAbove := len(comments) > 0 && comments[0].Location.Row-rule.Else.Head.Location.Row < 0 || w.beforeEnd != nil + + if !hasCommentAbove && wasInline { + w.write(" ") + } else { + w.blankLine() + w.startLine() + } + + rule.Else.Head.Name = "else" // NOTE(sr): whaaat + + elseHeadReference := ast.NewTerm(elseVar) // construct a reference for the term + elseHeadReference.Location = rule.Else.Head.Location // and set the location to match the rule location + + rule.Else.Head.Reference = ast.Ref{elseHeadReference} + rule.Else.Head.Args = nil + var err error + comments, err = w.insertComments(comments, rule.Else.Head.Location) + if err != nil { + return nil, err + } + + if hasCommentAbove && !wasInline { + // The comments would have ended the line, be sure to start one again + // before writing the rest of the "else" rule. + w.startLine() + } + + // For backwards compatibility adjust the rule head value location + // TODO: Refactor the logic for inserting comments, or special + // case comments in a rule head value so this can be removed + if rule.Else.Head.Value != nil { + rule.Else.Head.Value.Location = rule.Else.Head.Location + } + + return w.writeRule(rule.Else, true, comments) +} + +func (w *writer) writeHead(head *ast.Head, isDefault bool, isExpandedConst bool, comments []*ast.Comment) ([]*ast.Comment, error) { + ref := head.Ref() + if head.Key != nil && head.Value == nil && !head.HasDynamicRef() { + ref = ref.GroundPrefix() + } + if w.fmtOpts.refHeads || len(ref) == 1 { + var err error + comments, err = w.writeRef(ref, comments) + if err != nil { + return nil, err + } + } else { + // if there are comments within the object in the rule head, don't format it + if len(comments) > 0 && ref[1].Location.Row == comments[0].Location.Row { + comments, err := w.writeUnformatted(head.Location, comments) + if err != nil { + return nil, err + } + return comments, nil + } + + w.write(ref[0].String()) + w.write("[") + w.write(ref[1].String()) + w.write("]") + } + + if len(head.Args) > 0 { + w.write("(") + var args []any + for _, arg := range head.Args { + args = append(args, arg) + } + var err error + comments, err = w.writeIterable(args, head.Location, closingLoc(0, 0, '(', ')', head.Location), comments, w.listWriter()) + w.write(")") + if err != nil { + return comments, err + } + } + if head.Key != nil { + if w.fmtOpts.contains && head.Value == nil { + w.write(" contains ") + var err error + comments, err = w.writeTerm(head.Key, comments) + if err != nil { + return comments, err + } + } else if head.Value == nil { // no `if` for p[x] notation + w.write("[") + var err error + comments, err = w.writeTerm(head.Key, comments) + if err != nil { + return comments, err + } + w.write("]") + } + } + + if head.Value != nil && + (head.Key != nil || !ast.InternedTerm(true).Equal(head.Value) || isExpandedConst || isDefault) { + + // in rego v1, explicitly print value for ref-head constants that aren't partial set assignments, e.g.: + // * a -> parser error, won't reach here + // * a.b -> a contains "b" + // * a.b.c -> a.b.c := true + // * a.b.c.d -> a.b.c.d := true + isRegoV1RefConst := w.fmtOpts.regoV1 && isExpandedConst && head.Key == nil && len(head.Args) == 0 + + if head.Location == head.Value.Location && + head.Name != "else" && + ast.InternedTerm(true).Equal(head.Value) && + !isRegoV1RefConst { + // If the value location is the same as the location of the head, + // we know that the value is generated, i.e. f(1) + // Don't print the value (` = true`) as it is implied. + return comments, nil + } + + if head.Assign || w.fmtOpts.regoV1 { + // preserve assignment operator, and enforce it if formatting for Rego v1 + w.write(" := ") + } else { + w.write(" = ") + } + var err error + comments, err = w.writeTerm(head.Value, comments) + if err != nil { + return comments, err + } + } + return comments, nil +} + +func (w *writer) insertComments(comments []*ast.Comment, loc *ast.Location) ([]*ast.Comment, error) { + before, at, comments := partitionComments(comments, loc) + + err := w.writeComments(before) + if err != nil { + return nil, err + } + if len(before) > 0 && loc.Row-before[len(before)-1].Location.Row > 1 { + w.blankLine() + } + + return comments, w.beforeLineEnd(at) +} + +func (w *writer) writeBody(body ast.Body, comments []*ast.Comment) ([]*ast.Comment, error) { + var err error + comments, err = w.insertComments(comments, body.Loc()) + if err != nil { + return comments, err + } + for i, expr := range body { + // Insert a blank line in before the expression if it was not right + // after the previous expression. + if i > 0 { + lastRow := body[i-1].Location.Row + for _, c := range body[i-1].Location.Text { + if c == '\n' { + lastRow++ + } + } + if expr.Location.Row > lastRow+1 { + w.blankLine() + } + } + w.startLine() + + comments, err = w.writeExpr(expr, comments) + if err != nil && !errors.As(err, &unexpectedCommentError{}) { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + w.endLine() + } + return comments, nil +} + +func (w *writer) writeExpr(expr *ast.Expr, comments []*ast.Comment) ([]*ast.Comment, error) { + var err error + comments, err = w.insertComments(comments, expr.Location) + if err != nil { + return comments, err + } + if !w.inline { + w.startLine() + } + + if expr.Negated { + w.write("not ") + } + + switch t := expr.Terms.(type) { + case *ast.SomeDecl: + comments, err = w.writeSomeDecl(t, comments) + if err != nil { + return nil, err + } + case *ast.Every: + comments, err = w.writeEvery(t, comments) + if err != nil { + return nil, err + } + case []*ast.Term: + comments, err = w.writeFunctionCall(expr, comments) + if err != nil { + return comments, err + } + case *ast.Term: + comments, err = w.writeTerm(t, comments) + if err != nil { + return comments, err + } + } + + var indented, down bool + for i, with := range expr.With { + if i == 0 || with.Location.Row == expr.With[i-1].Location.Row { // we're on the same line + comments, err = w.writeWith(with, comments, false) + if err != nil { + return nil, err + } + } else { // we're on a new line + if !indented { + indented = true + + w.up() + down = true + } + w.endLine() + w.startLine() + comments, err = w.writeWith(with, comments, true) + if err != nil { + return nil, err + } + } + } + + if down { + if err := w.down(); err != nil { + return nil, err + } + } + + return comments, nil +} + +func (w *writer) writeSomeDecl(decl *ast.SomeDecl, comments []*ast.Comment) ([]*ast.Comment, error) { + var err error + comments, err = w.insertComments(comments, decl.Location) + if err != nil { + return nil, err + } + w.write("some ") + + row := decl.Location.Row + + for i, term := range decl.Symbols { + switch val := term.Value.(type) { + case ast.Var: + if term.Location.Row > row { + w.endLine() + w.startLine() + w.write(w.indent) + row = term.Location.Row + } else if i > 0 { + w.write(" ") + } + + comments, err = w.writeTerm(term, comments) + if err != nil { + return nil, err + } + + if i < len(decl.Symbols)-1 { + w.write(",") + } + case ast.Call: + comments, err = w.writeInOperator(false, val[1:], comments, decl.Location, ast.BuiltinMap[val[0].String()].Decl) + if err != nil { + return nil, err + } + } + } + + return comments, nil +} + +func (w *writer) writeEvery(every *ast.Every, comments []*ast.Comment) ([]*ast.Comment, error) { + var err error + comments, err = w.insertComments(comments, every.Location) + if err != nil { + return nil, err + } + w.write("every ") + if every.Key != nil { + comments, err = w.writeTerm(every.Key, comments) + if err != nil { + return nil, err + } + w.write(", ") + } + comments, err = w.writeTerm(every.Value, comments) + if err != nil { + return nil, err + } + w.write(" in ") + comments, err = w.writeTerm(every.Domain, comments) + if err != nil { + return nil, err + } + w.write(" {") + comments, err = w.writeComprehensionBody('{', '}', every.Body, every.Loc(), every.Loc(), comments) + if err != nil { + // the unexpected comment error is passed up to be handled by writeHead + if !errors.As(err, &unexpectedCommentError{}) { + return nil, err + } + } + + if len(every.Body) == 1 && + every.Body[0].Location.Row == every.Location.Row { + w.write(" ") + } + w.write("}") + return comments, nil +} + +func (w *writer) writeFunctionCall(expr *ast.Expr, comments []*ast.Comment) ([]*ast.Comment, error) { + + terms := expr.Terms.([]*ast.Term) + operator := terms[0].Value.String() + + switch operator { + case ast.Member.Name, ast.MemberWithKey.Name: + return w.writeInOperator(false, terms[1:], comments, terms[0].Location, ast.BuiltinMap[terms[0].String()].Decl) + } + + bi, ok := ast.BuiltinMap[operator] + if !ok || bi.Infix == "" { + return w.writeFunctionCallPlain(terms, comments) + } + + numDeclArgs := bi.Decl.Arity() + numCallArgs := len(terms) - 1 + + var err error + switch numCallArgs { + case numDeclArgs: // Print infix where result is unassigned (e.g., x != y) + comments, err = w.writeTerm(terms[1], comments) + if err != nil { + return nil, err + } + w.write(" " + bi.Infix + " ") + return w.writeTerm(terms[2], comments) + case numDeclArgs + 1: // Print infix where result is assigned (e.g., z = x + y) + comments, err = w.writeTerm(terms[3], comments) + if err != nil { + return nil, err + } + w.write(" " + ast.Equality.Infix + " ") + comments, err = w.writeTerm(terms[1], comments) + if err != nil { + return nil, err + } + w.write(" " + bi.Infix + " ") + comments, err = w.writeTerm(terms[2], comments) + if err != nil { + return nil, err + } + return comments, nil + } + // NOTE(Trolloldem): in this point we are operating with a built-in function with the + // wrong arity even when the assignment notation is used + w.errs = append(w.errs, ArityFormatMismatchError(terms[1:], terms[0].String(), terms[0].Location, bi.Decl)) + return w.writeFunctionCallPlain(terms, comments) +} + +func (w *writer) writeFunctionCallPlain(terms []*ast.Term, comments []*ast.Comment) ([]*ast.Comment, error) { + if r, ok := terms[0].Value.(ast.Ref); ok { + if c, err := w.writeRef(r, comments); err != nil { + return c, err + } + } else { + w.write(terms[0].String()) + } + w.write("(") + defer w.write(")") + + args := make([]any, len(terms)-1) + for i, t := range terms[1:] { + args[i] = t + } + loc := terms[0].Location + var err error + comments, err = w.writeIterable(args, loc, closingLoc(0, 0, '(', ')', loc), comments, w.listWriter()) + if err != nil { + return nil, err + } + return comments, nil +} + +func (w *writer) writeWith(with *ast.With, comments []*ast.Comment, indented bool) ([]*ast.Comment, error) { + var err error + comments, err = w.insertComments(comments, with.Location) + if err != nil { + return nil, err + } + if !indented { + w.write(" ") + } + w.write("with ") + comments, err = w.writeTerm(with.Target, comments) + if err != nil { + return nil, err + } + w.write(" as ") + comments, err = w.writeTerm(with.Value, comments) + if err != nil { + return nil, err + } + return comments, nil +} + +func (w *writer) writeTerm(term *ast.Term, comments []*ast.Comment) ([]*ast.Comment, error) { + currentComments := make([]*ast.Comment, len(comments)) + copy(currentComments, comments) + + currentLen := w.buf.Len() + + comments, err := w.writeTermParens(false, term, comments) + if err != nil { + if errors.As(err, &unexpectedCommentError{}) { + w.buf.Truncate(currentLen) + + comments, uErr := w.writeUnformatted(term.Location, currentComments) + if uErr != nil { + return nil, uErr + } + return comments, err + } + return nil, err + } + + return comments, nil +} + +// writeUnformatted writes the unformatted text instead and updates the comment state +func (w *writer) writeUnformatted(location *ast.Location, currentComments []*ast.Comment) ([]*ast.Comment, error) { + if len(location.Text) == 0 { + return nil, errors.New("original unformatted text is empty") + } + + rawRule := string(location.Text) + rowNum := len(strings.Split(rawRule, "\n")) + + w.write(string(location.Text)) + + comments := make([]*ast.Comment, 0, len(currentComments)) + for _, c := range currentComments { + // if there is a body then wait to write the last comment + if w.writeCommentOnFinalLine && c.Location.Row == location.Row+rowNum-1 { + w.write(" " + string(c.Location.Text)) + continue + } + + // drop comments that occur within the rule raw text + if c.Location.Row < location.Row+rowNum-1 { + continue + } + comments = append(comments, c) + } + return comments, nil +} + +func (w *writer) writeTermParens(parens bool, term *ast.Term, comments []*ast.Comment) ([]*ast.Comment, error) { + var err error + comments, err = w.insertComments(comments, term.Location) + if err != nil { + return nil, err + } + if !w.inline { + w.startLine() + } + + switch x := term.Value.(type) { + case ast.Ref: + comments, err = w.writeRef(x, comments) + if err != nil { + return nil, err + } + case ast.Object: + comments, err = w.writeObject(x, term.Location, comments) + if err != nil { + return nil, err + } + case *ast.Array: + comments, err = w.writeArray(x, term.Location, comments) + if err != nil { + return nil, err + } + case ast.Set: + comments, err = w.writeSet(x, term.Location, comments) + if err != nil { + return nil, err + } + case *ast.ArrayComprehension: + comments, err = w.writeArrayComprehension(x, term.Location, comments) + if err != nil { + return nil, err + } + case *ast.ObjectComprehension: + comments, err = w.writeObjectComprehension(x, term.Location, comments) + if err != nil { + return nil, err + } + case *ast.SetComprehension: + comments, err = w.writeSetComprehension(x, term.Location, comments) + if err != nil { + return nil, err + } + case ast.String: + if term.Location.Text[0] == '`' { + // To preserve raw strings, we need to output the original text, + w.write(string(term.Location.Text)) + } else { + // x.String() cannot be used by default because it can change the input string "\u0000" to "\x00" + var after, quote string + var found bool + // term.Location.Text could contain the prefix `else :=`, remove it + switch term.Location.Text[len(term.Location.Text)-1] { + case '"': + quote = "\"" + _, after, found = strings.Cut(string(term.Location.Text), quote) + case '`': + quote = "`" + _, after, found = strings.Cut(string(term.Location.Text), quote) + } + + if !found { + // If no quoted string was found, that means it is a key being formatted to a string + // e.g. partial_set.y to partial_set["y"] + w.write(x.String()) + } else { + w.write(quote + after) + } + + } + case ast.Var: + w.write(w.formatVar(x)) + case ast.Call: + comments, err = w.writeCall(parens, x, term.Location, comments) + if err != nil { + return nil, err + } + case fmt.Stringer: + w.write(x.String()) + } + + if !w.inline { + w.startLine() + } + return comments, nil +} + +func (w *writer) writeRef(x ast.Ref, comments []*ast.Comment) ([]*ast.Comment, error) { + if len(x) > 0 { + parens := false + _, ok := x[0].Value.(ast.Call) + if ok { + parens = x[0].Location.Text[0] == 40 // Starts with "(" + } + var err error + comments, err = w.writeTermParens(parens, x[0], comments) + if err != nil { + return nil, err + } + path := x[1:] + for _, t := range path { + switch p := t.Value.(type) { + case ast.String: + w.writeRefStringPath(p, t.Location) + case ast.Var: + w.writeBracketed(w.formatVar(p)) + default: + w.write("[") + comments, err = w.writeTerm(t, comments) + if err != nil { + if errors.As(err, &unexpectedCommentError{}) { + // add a new line so that the closing bracket isn't part of the unexpected comment + w.write("\n") + } else { + return nil, err + } + } + w.write("]") + } + } + } + + return comments, nil +} + +func (w *writer) writeBracketed(str string) { + w.write("[" + str + "]") +} + +var varRegexp = regexp.MustCompile("^[[:alpha:]_][[:alpha:][:digit:]_]*$") + +func (w *writer) writeRefStringPath(s ast.String, l *ast.Location) { + str := string(s) + if w.shouldBracketRefTerm(str, l) { + w.writeBracketed(s.String()) + } else { + w.write("." + str) + } +} + +func (w *writer) shouldBracketRefTerm(s string, l *ast.Location) bool { + if !varRegexp.MatchString(s) { + return true + } + + if ast.IsInKeywords(s, w.fmtOpts.keywords()) { + if !w.fmtOpts.allowKeywordsInRefs { + return true + } + + if l != nil && l.Text[0] == 34 { // If the original term text starts with '"', we preserve the brackets and quotes + return true + } + } + + return false +} + +func (*writer) formatVar(v ast.Var) string { + if v.IsWildcard() { + return ast.Wildcard.String() + } + return v.String() +} + +func (w *writer) writeCall(parens bool, x ast.Call, loc *ast.Location, comments []*ast.Comment) ([]*ast.Comment, error) { + bi, ok := ast.BuiltinMap[x[0].String()] + if !ok || bi.Infix == "" { + return w.writeFunctionCallPlain(x, comments) + } + + if bi.Infix == "in" { + // NOTE(sr): `in` requires special handling, mirroring what happens in the parser, + // since there can be one or two lhs arguments. + return w.writeInOperator(true, x[1:], comments, loc, bi.Decl) + } + + // TODO(tsandall): improve to consider precedence? + if parens { + w.write("(") + } + + // NOTE(Trolloldem): writeCall is only invoked when the function call is a term + // of another function. The only valid arity is the one of the + // built-in function + if bi.Decl.Arity() != len(x)-1 { + w.errs = append(w.errs, ArityFormatMismatchError(x[1:], x[0].String(), loc, bi.Decl)) + return comments, nil + } + + var err error + comments, err = w.writeTermParens(true, x[1], comments) + if err != nil { + return nil, err + } + w.write(" " + bi.Infix + " ") + comments, err = w.writeTermParens(true, x[2], comments) + if err != nil { + return nil, err + } + if parens { + w.write(")") + } + + return comments, nil +} + +func (w *writer) writeInOperator(parens bool, operands []*ast.Term, comments []*ast.Comment, loc *ast.Location, f *types.Function) ([]*ast.Comment, error) { + + if len(operands) != f.Arity() { + // The number of operands does not math the arity of the `in` operator + operator := ast.Member.Name + if f.Arity() == 3 { + operator = ast.MemberWithKey.Name + } + w.errs = append(w.errs, ArityFormatMismatchError(operands, operator, loc, f)) + return comments, nil + } + kw := "in" + var err error + switch len(operands) { + case 2: + comments, err = w.writeTermParens(true, operands[0], comments) + if err != nil { + return nil, err + } + w.write(" ") + w.write(kw) + w.write(" ") + comments, err = w.writeTermParens(true, operands[1], comments) + if err != nil { + return nil, err + } + case 3: + if parens { + w.write("(") + defer w.write(")") + } + comments, err = w.writeTermParens(true, operands[0], comments) + if err != nil { + return nil, err + } + w.write(", ") + comments, err = w.writeTermParens(true, operands[1], comments) + if err != nil { + return nil, err + } + w.write(" ") + w.write(kw) + w.write(" ") + comments, err = w.writeTermParens(true, operands[2], comments) + if err != nil { + return nil, err + } + } + return comments, nil +} + +func (w *writer) writeObject(obj ast.Object, loc *ast.Location, comments []*ast.Comment) ([]*ast.Comment, error) { + w.write("{") + defer w.write("}") + + var s []any + obj.Foreach(func(k, v *ast.Term) { + s = append(s, ast.Item(k, v)) + }) + return w.writeIterable(s, loc, closingLoc(0, 0, '{', '}', loc), comments, w.objectWriter()) +} + +func (w *writer) writeArray(arr *ast.Array, loc *ast.Location, comments []*ast.Comment) ([]*ast.Comment, error) { + w.write("[") + defer w.write("]") + + var s []any + arr.Foreach(func(t *ast.Term) { + s = append(s, t) + }) + var err error + comments, err = w.writeIterable(s, loc, closingLoc(0, 0, '[', ']', loc), comments, w.listWriter()) + if err != nil { + return nil, err + } + return comments, nil +} + +func (w *writer) writeSet(set ast.Set, loc *ast.Location, comments []*ast.Comment) ([]*ast.Comment, error) { + + if set.Len() == 0 { + w.write("set()") + var err error + comments, err = w.insertComments(comments, closingLoc(0, 0, '(', ')', loc)) + if err != nil { + return nil, err + } + return comments, nil + } + + w.write("{") + defer w.write("}") + + var s []any + set.Foreach(func(t *ast.Term) { + s = append(s, t) + }) + var err error + comments, err = w.writeIterable(s, loc, closingLoc(0, 0, '{', '}', loc), comments, w.listWriter()) + if err != nil { + return nil, err + } + return comments, nil +} + +func (w *writer) writeArrayComprehension(arr *ast.ArrayComprehension, loc *ast.Location, comments []*ast.Comment) ([]*ast.Comment, error) { + w.write("[") + defer w.write("]") + + return w.writeComprehension('[', ']', arr.Term, arr.Body, loc, comments) +} + +func (w *writer) writeSetComprehension(set *ast.SetComprehension, loc *ast.Location, comments []*ast.Comment) ([]*ast.Comment, error) { + w.write("{") + defer w.write("}") + + return w.writeComprehension('{', '}', set.Term, set.Body, loc, comments) +} + +func (w *writer) writeObjectComprehension(object *ast.ObjectComprehension, loc *ast.Location, comments []*ast.Comment) ([]*ast.Comment, error) { + w.write("{") + defer w.write("}") + + object.Value.Location = object.Key.Location // Ensure the value is not written on the next line. + if object.Key.Location.Row-loc.Row > 1 { + w.endLine() + w.startLine() + } + + var err error + comments, err = w.writeTerm(object.Key, comments) + if err != nil { + return nil, err + } + w.write(": ") + return w.writeComprehension('{', '}', object.Value, object.Body, loc, comments) +} + +func (w *writer) writeComprehension(openChar, closeChar byte, term *ast.Term, body ast.Body, loc *ast.Location, comments []*ast.Comment) ([]*ast.Comment, error) { + if term.Location.Row-loc.Row >= 1 { + w.endLine() + w.startLine() + } + + parens := false + _, ok := term.Value.(ast.Call) + if ok { + parens = term.Location.Text[0] == 40 // Starts with "(" + } + var err error + comments, err = w.writeTermParens(parens, term, comments) + if err != nil { + return nil, err + } + w.write(" |") + + return w.writeComprehensionBody(openChar, closeChar, body, term.Location, loc, comments) +} + +func (w *writer) writeComprehensionBody(openChar, closeChar byte, body ast.Body, term, compr *ast.Location, comments []*ast.Comment) ([]*ast.Comment, error) { + exprs := make([]any, 0, len(body)) + for _, expr := range body { + exprs = append(exprs, expr) + } + lines, err := w.groupIterable(exprs, term) + if err != nil { + return nil, err + } + + if body.Loc().Row-term.Row > 0 || len(lines) > 1 { + w.endLine() + w.up() + defer w.startLine() + defer func() { + if err := w.down(); err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + }() + + var err error + comments, err = w.writeBody(body, comments) + if err != nil { + return comments, err + } + } else { + w.write(" ") + i := 0 + for ; i < len(body)-1; i++ { + comments, err = w.writeExpr(body[i], comments) + if err != nil { + return comments, err + } + w.write("; ") + } + comments, err = w.writeExpr(body[i], comments) + if err != nil { + return comments, err + } + } + comments, err = w.insertComments(comments, closingLoc(0, 0, openChar, closeChar, compr)) + if err != nil { + return nil, err + } + return comments, nil +} + +func (w *writer) writeImports(imports []*ast.Import, comments []*ast.Comment) ([]*ast.Comment, error) { + m, comments := mapImportsToComments(imports, comments) + + groups := groupImports(imports) + for _, group := range groups { + var err error + comments, err = w.insertComments(comments, group[0].Loc()) + if err != nil { + return nil, err + } + + // Sort imports within a newline grouping. + slices.SortFunc(group, (*ast.Import).Compare) + for _, i := range group { + w.startLine() + err = w.writeImport(i) + if err != nil { + return nil, err + } + if c, ok := m[i]; ok { + w.write(" " + c.String()) + } + w.endLine() + } + w.blankLine() + } + + return comments, nil +} + +func (w *writer) writeImport(imp *ast.Import) error { + path := imp.Path.Value.(ast.Ref) + + w.write("import ") + + if _, ok := future.WhichFutureKeyword(imp); ok { + // We don't want to wrap future.keywords imports in parens, so we create a new writer that doesn't + w2 := writer{ + buf: bytes.Buffer{}, + } + _, err := w2.writeRef(path, nil) + if err != nil { + return err + } + w.write(w2.buf.String()) + } else { + _, err := w.writeRef(path, nil) + if err != nil { + return err + } + } + + if len(imp.Alias) > 0 { + w.write(" as " + imp.Alias.String()) + } + + return nil +} + +type entryWriter func(any, []*ast.Comment) ([]*ast.Comment, error) + +func (w *writer) writeIterable(elements []any, last *ast.Location, close *ast.Location, comments []*ast.Comment, fn entryWriter) ([]*ast.Comment, error) { + lines, err := w.groupIterable(elements, last) + if err != nil { + return nil, err + } + if len(lines) > 1 { + w.delayBeforeEnd() + w.startMultilineSeq() + } + + i := 0 + for ; i < len(lines)-1; i++ { + comments, err = w.writeIterableLine(lines[i], comments, fn) + if err != nil { + return nil, err + } + w.write(",") + + w.endLine() + w.startLine() + } + + comments, err = w.writeIterableLine(lines[i], comments, fn) + if err != nil { + return nil, err + } + + if len(lines) > 1 { + w.write(",") + w.endLine() + comments, err = w.insertComments(comments, close) + if err != nil { + return nil, err + } + if err := w.down(); err != nil { + return nil, err + } + w.startLine() + } + + return comments, nil +} + +func (w *writer) writeIterableLine(elements []any, comments []*ast.Comment, fn entryWriter) ([]*ast.Comment, error) { + if len(elements) == 0 { + return comments, nil + } + + i := 0 + for ; i < len(elements)-1; i++ { + var err error + comments, err = fn(elements[i], comments) + if err != nil { + return nil, err + } + w.write(", ") + } + + return fn(elements[i], comments) +} + +func (w *writer) objectWriter() entryWriter { + return func(x any, comments []*ast.Comment) ([]*ast.Comment, error) { + entry := x.([2]*ast.Term) + + call, isCall := entry[0].Value.(ast.Call) + + paren := false + if isCall && ast.Or.Ref().Equal(call[0].Value) && entry[0].Location.Text[0] == 40 { // Starts with "(" + paren = true + w.write("(") + } + + var err error + comments, err = w.writeTerm(entry[0], comments) + if err != nil { + return nil, err + } + if paren { + w.write(")") + } + + w.write(": ") + + call, isCall = entry[1].Value.(ast.Call) + if isCall && ast.Or.Ref().Equal(call[0].Value) && entry[1].Location.Text[0] == 40 { // Starts with "(" + w.write("(") + defer w.write(")") + } + + return w.writeTerm(entry[1], comments) + } +} + +func (w *writer) listWriter() entryWriter { + return func(x any, comments []*ast.Comment) ([]*ast.Comment, error) { + t, ok := x.(*ast.Term) + if ok { + call, isCall := t.Value.(ast.Call) + if isCall && ast.Or.Ref().Equal(call[0].Value) && t.Location.Text[0] == 40 { // Starts with "(" + w.write("(") + defer w.write(")") + } + } + + return w.writeTerm(t, comments) + } +} + +// groupIterable will group the `elements` slice into slices according to their +// location: anything on the same line will be put into a slice. +func (w *writer) groupIterable(elements []any, last *ast.Location) ([][]any, error) { + // Generated vars occur in the AST when we're rendering the result of + // partial evaluation in a bundle build with optimization. + // Those variables, and wildcard variables have the "default location", + // set in `Ast()`). That is no proper file location, and the grouping + // based on source location will yield a bad result. + // Another case is generated variables: they do have proper file locations, + // but their row/col information may no longer match their AST location. + // So, for generated variables, we also don't trust the location, but + // keep them ungrouped. + def := false // default location found? + for _, elem := range elements { + ast.WalkTerms(elem, func(t *ast.Term) bool { + if t.Location.File == defaultLocationFile { + def = true + return true + } + return false + }) + ast.WalkVars(elem, func(v ast.Var) bool { + if v.IsGenerated() { + def = true + return true + } + return false + }) + if def { // return as-is + return [][]any{elements}, nil + } + } + + slices.SortFunc(elements, func(i, j any) int { + l, err := locCmp(i, j) + if err != nil { + w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, err.Error())) + } + return l + }) + + var lines [][]any + cur := make([]any, 0, len(elements)) + for i, t := range elements { + elem := t + loc, err := getLoc(elem) + if err != nil { + return nil, err + } + lineDiff := loc.Row - last.Row + if lineDiff > 0 && i > 0 { + lines = append(lines, cur) + cur = nil + } + + last = loc + cur = append(cur, elem) + } + return append(lines, cur), nil +} + +func mapImportsToComments(imports []*ast.Import, comments []*ast.Comment) (map[*ast.Import]*ast.Comment, []*ast.Comment) { + var leftovers []*ast.Comment + m := map[*ast.Import]*ast.Comment{} + + for _, c := range comments { + matched := false + for _, i := range imports { + if c.Loc().Row == i.Loc().Row { + m[i] = c + matched = true + break + } + } + if !matched { + leftovers = append(leftovers, c) + } + } + + return m, leftovers +} + +func groupImports(imports []*ast.Import) [][]*ast.Import { + switch len(imports) { // shortcuts + case 0: + return nil + case 1: + return [][]*ast.Import{imports} + } + // there are >=2 imports to group + + var groups [][]*ast.Import + group := []*ast.Import{imports[0]} + + for _, i := range imports[1:] { + last := group[len(group)-1] + + // nil-location imports have been sorted up to come first + if i.Loc() != nil && last.Loc() != nil && // first import with a location, or + i.Loc().Row-last.Loc().Row > 1 { // more than one row apart from previous import + + // start a new group + groups = append(groups, group) + group = []*ast.Import{} + } + group = append(group, i) + } + if len(group) > 0 { + groups = append(groups, group) + } + + return groups +} + +func partitionComments(comments []*ast.Comment, l *ast.Location) ([]*ast.Comment, *ast.Comment, []*ast.Comment) { + if len(comments) == 0 { + return nil, nil, nil + } + + numBefore, numAfter := 0, 0 + for _, c := range comments { + switch cmp := c.Location.Row - l.Row; { + case cmp < 0: + numBefore++ + case cmp > 0: + numAfter++ + } + } + + if numAfter == len(comments) { + return nil, nil, comments + } + + var at *ast.Comment + + before := make([]*ast.Comment, 0, numBefore) + after := comments[0 : 0 : len(comments)-numBefore] + + for _, c := range comments { + switch cmp := c.Location.Row - l.Row; { + case cmp < 0: + before = append(before, c) + case cmp > 0: + after = append(after, c) + default: + at = c + } + } + + return before, at, after +} + +func gatherImports(others []any) (imports []*ast.Import, rest []any) { + i := 0 +loop: + for ; i < len(others); i++ { + switch x := others[i].(type) { + case *ast.Import: + imports = append(imports, x) + case *ast.Rule: + break loop + } + } + return imports, others[i:] +} + +func gatherRules(others []any) (rules []*ast.Rule, rest []any) { + i := 0 +loop: + for ; i < len(others); i++ { + switch x := others[i].(type) { + case *ast.Rule: + rules = append(rules, x) + case *ast.Import: + break loop + } + } + return rules, others[i:] +} + +func locLess(a, b any) (bool, error) { + c, err := locCmp(a, b) + return c < 0, err +} + +func locCmp(a, b any) (int, error) { + al, err := getLoc(a) + if err != nil { + return 0, err + } + bl, err := getLoc(b) + if err != nil { + return 0, err + } + switch { + case al == nil && bl == nil: + return 0, nil + case al == nil: + return -1, nil + case bl == nil: + return 1, nil + } + + if cmp := al.Row - bl.Row; cmp != 0 { + return cmp, nil + + } + return al.Col - bl.Col, nil +} + +func getLoc(x any) (*ast.Location, error) { + switch x := x.(type) { + case ast.Node: // *ast.Head, *ast.Expr, *ast.With, *ast.Term + return x.Loc(), nil + case *ast.Location: + return x, nil + case [2]*ast.Term: // Special case to allow for easy printing of objects. + return x[0].Location, nil + default: + return nil, fmt.Errorf("unable to get location for type %v", x) + } +} + +var negativeRow = &ast.Location{Row: -1} + +func closingLoc(skipOpen, skipClose, openChar, closeChar byte, loc *ast.Location) *ast.Location { + i, offset := 0, 0 + + // Skip past parens/brackets/braces in rule heads. + if skipOpen > 0 { + i, offset = skipPast(skipOpen, skipClose, loc) + } + + for ; i < len(loc.Text); i++ { + if loc.Text[i] == openChar { + break + } + } + + if i >= len(loc.Text) { + return negativeRow + } + + state := 1 + for state > 0 { + i++ + if i >= len(loc.Text) { + return negativeRow + } + + switch loc.Text[i] { + case openChar: + state++ + case closeChar: + state-- + case '\n': + offset++ + } + } + + return &ast.Location{Row: loc.Row + offset} +} + +func skipPast(openChar, closeChar byte, loc *ast.Location) (int, int) { + i := 0 + for ; i < len(loc.Text); i++ { + if loc.Text[i] == openChar { + break + } + } + + state := 1 + offset := 0 + for state > 0 { + i++ + if i >= len(loc.Text) { + return i, offset + } + + switch loc.Text[i] { + case openChar: + state++ + case closeChar: + state-- + case '\n': + offset++ + } + } + + return i, offset +} + +// startLine begins a line with the current indentation level. +func (w *writer) startLine() { + w.inline = true + for range w.level { + w.write(w.indent) + } +} + +// endLine ends a line with a newline. +func (w *writer) endLine() { + w.inline = false + if w.beforeEnd != nil && !w.delay { + w.write(" " + w.beforeEnd.String()) + w.beforeEnd = nil + } + w.delay = false + w.write("\n") +} + +type unexpectedCommentError struct { + newComment string + newCommentRow int + existingComment string + existingCommentRow int +} + +func (u unexpectedCommentError) Error() string { + return fmt.Sprintf("unexpected new comment (%s) on line %d because there is already a comment (%s) registered for line %d", + u.newComment, u.newCommentRow, u.existingComment, u.existingCommentRow) +} + +// beforeLineEnd registers a comment to be printed at the end of the current line. +func (w *writer) beforeLineEnd(c *ast.Comment) error { + if w.beforeEnd != nil { + if c == nil { + return nil + } + + existingComment := truncatedString(w.beforeEnd.String(), 100) + existingCommentRow := w.beforeEnd.Location.Row + newComment := truncatedString(c.String(), 100) + w.beforeEnd = nil + + return unexpectedCommentError{ + newComment: newComment, + newCommentRow: c.Location.Row, + existingComment: existingComment, + existingCommentRow: existingCommentRow, + } + } + w.beforeEnd = c + return nil +} + +func truncatedString(s string, max int) string { + if len(s) > max { + return s[:max-2] + "..." + } + return s +} + +func (w *writer) delayBeforeEnd() { + w.delay = true +} + +// line prints a blank line. If the writer is currently in the middle of a line, +// line ends it and then prints a blank one. +func (w *writer) blankLine() { + if w.inline { + w.endLine() + } + w.write("\n") +} + +// write the input string and writes it to the buffer. +func (w *writer) write(s string) { + w.buf.WriteString(s) +} + +// writeLine writes the string on a newly started line, then terminate the line. +func (w *writer) writeLine(s string) { + if !w.inline { + w.startLine() + } + w.write(s) + w.endLine() +} + +func (w *writer) startMultilineSeq() { + w.endLine() + w.up() + w.startLine() +} + +// up increases the indentation level +func (w *writer) up() { + w.level++ +} + +// down decreases the indentation level +func (w *writer) down() error { + if w.level == 0 { + return errors.New("negative indentation level") + } + w.level-- + return nil +} + +func ensureFutureKeywordImport(imps []*ast.Import, kw string) []*ast.Import { + for _, imp := range imps { + if future.IsAllFutureKeywords(imp) || + future.IsFutureKeyword(imp, kw) || + (future.IsFutureKeyword(imp, "every") && kw == "in") { // "every" implies "in", so we don't need to add both + return imps + } + } + imp := &ast.Import{ + // NOTE: This is a hack to not error on the ref containing a keyword already present in v1. + // A cleaner solution would be to instead allow refs to contain keyword terms. + // E.g. in v1, `import future.keywords["in"]` is valid, but `import future.keywords.in` is not + // as it contains a reserved keyword. + Path: ast.MustParseTerm("future.keywords[\"" + kw + "\"]"), + //Path: ast.MustParseTerm("future.keywords." + kw), + } + imp.Location = defaultLocation(imp) + return append(imps, imp) +} + +func ensureRegoV1Import(imps []*ast.Import) []*ast.Import { + return ensureImport(imps, ast.RegoV1CompatibleRef) +} + +func filterRegoV1Import(imps []*ast.Import) []*ast.Import { + var ret []*ast.Import + for _, imp := range imps { + path := imp.Path.Value.(ast.Ref) + if !ast.RegoV1CompatibleRef.Equal(path) { + ret = append(ret, imp) + } + } + return ret +} + +func ensureImport(imps []*ast.Import, path ast.Ref) []*ast.Import { + for _, imp := range imps { + p := imp.Path.Value.(ast.Ref) + if p.Equal(path) { + return imps + } + } + imp := &ast.Import{ + Path: ast.NewTerm(path), + } + imp.Location = defaultLocation(imp) + return append(imps, imp) +} + +// ArityFormatErrDetail but for `fmt` checks since compiler has not run yet. +type ArityFormatErrDetail struct { + Have []string `json:"have"` + Want []string `json:"want"` +} + +// ArityFormatMismatchError but for `fmt` checks since the compiler has not run yet. +func ArityFormatMismatchError(operands []*ast.Term, operator string, loc *ast.Location, f *types.Function) *ast.Error { + want := make([]string, f.Arity()) + for i, arg := range f.FuncArgs().Args { + want[i] = types.Sprint(arg) + } + + have := make([]string, len(operands)) + for i := range operands { + have[i] = ast.ValueName(operands[i].Value) + } + err := ast.NewError(ast.TypeErr, loc, "%s: %s", operator, "arity mismatch") + err.Details = &ArityFormatErrDetail{ + Have: have, + Want: want, + } + return err +} + +// Lines returns the string representation of the detail. +func (d *ArityFormatErrDetail) Lines() []string { + return []string{ + "have: (" + strings.Join(d.Have, ",") + ")", + "want: (" + strings.Join(d.Want, ",") + ")", + } +} + +// isRegoV1Compatible returns true if the passed *ast.Import is `rego.v1` +func isRegoV1Compatible(imp *ast.Import) bool { + path := imp.Path.Value.(ast.Ref) + return len(path) == 2 && + ast.RegoRootDocument.Equal(path[0]) && + path[1].Equal(ast.InternedTerm("v1")) +} diff --git a/third_party/opa/v1/format/format_test.go b/third_party/opa/v1/format/format_test.go new file mode 100644 index 000000000000..16d8a030f941 --- /dev/null +++ b/third_party/opa/v1/format/format_test.go @@ -0,0 +1,1028 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package format + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ast/location" +) + +func TestFormatNilLocation(t *testing.T) { + tests := []struct { + note string + regoVersion ast.RegoVersion + rule string + exp string + }{ + { + note: "v0", + regoVersion: ast.RegoV0, + rule: `r = y { y = "foo" }`, + exp: `r = y { + y = "foo" +}`, + }, + { + note: "v1", + regoVersion: ast.RegoV1, + rule: `r = y if { y = "foo" }`, + exp: `r := y if y = "foo" +`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + rule := ast.MustParseRuleWithOpts(tc.rule, ast.ParserOptions{RegoVersion: tc.regoVersion}) + rule.Head.Location = nil + + bs, err := AstWithOpts(rule, Opts{RegoVersion: tc.regoVersion}) + if err != nil { + t.Fatal(err) + } + + if string(bs) != tc.exp { + t.Fatalf("Expected:\n\n%q\n\nbut got:\n\n%q", tc.exp, string(bs)) + } + }) + } +} + +func TestFormatNilLocationEmptyBody(t *testing.T) { + b := ast.NewBody() + x, err := Ast(b) + if len(x) != 0 || err != nil { + t.Fatalf("Expected empty result but got: %q, err: %v", string(x), err) + } +} + +func TestFormatNilLocationFunctionArgs(t *testing.T) { + b := ast.NewBody() + s := ast.StringTerm(" ") + s.SetLocation(location.NewLocation([]byte("\" \""), "p.rego", 2, 2)) + b.Append(ast.Split.Expr(ast.NewTerm(ast.Var("__local1__")), s, ast.NewTerm(ast.Var("__local2__")))) + exp := "split(__local1__, \" \", __local2__)\n" + bs, err := Ast(b) + if err != nil { + t.Fatal(err) + } + if string(bs) != exp { + t.Fatalf("Expected %q but got %q", exp, string(bs)) + } +} + +func TestFormatSourceError(t *testing.T) { + rego := "testfiles/v0/test.rego.error" + contents, err := os.ReadFile(rego) + if err != nil { + t.Fatalf("Failed to read rego source: %v", err) + } + + _, err = Source(rego, contents) + if err == nil { + t.Fatal("Expected parsing error, not nil") + } + + exp := "1 error occurred: testfiles/v0/test.rego.error:27: rego_parse_error: unexpected eof token" + + if !strings.HasPrefix(err.Error(), exp) { + t.Fatalf("Expected error message '%s', got '%s'", exp, err.Error()) + } +} + +func TestFormatV0Source(t *testing.T) { + regoFiles, err := filepath.Glob("testfiles/v0/*.rego") + if err != nil { + panic(err) + } + + for _, rego := range regoFiles { + t.Run(rego, func(t *testing.T) { + contents, err := os.ReadFile(rego) + if err != nil { + t.Fatalf("Failed to read rego source: %v", err) + } + + expected, err := os.ReadFile(rego + ".formatted") + if err != nil { + t.Fatalf("Failed to read expected rego source: %v", err) + } + + popts := ast.ParserOptions{ + RegoVersion: ast.RegoV0, + } + opts := Opts{ + RegoVersion: ast.RegoV0, + ParserOptions: &popts, + } + + formatted, err := SourceWithOpts(rego, contents, opts) + if err != nil { + t.Fatalf("Failed to format file: %v", err) + } + + if ln, at := differsAt(formatted, expected); ln != 0 { + t.Fatalf("Expected formatted bytes to equal expected bytes but differed near line %d / byte %d (got: %q, expected: %q):\n%s", ln, at, formatted[at], expected[at], prefixWithLineNumbers(formatted)) + } + + if _, err := ast.ParseModuleWithOpts(rego+".tmp", string(formatted), popts); err != nil { + t.Fatalf("Failed to parse formatted bytes: %v", err) + } + + formatted, err = SourceWithOpts(rego, formatted, opts) + if err != nil { + t.Fatalf("Failed to double format file") + } + + if ln, at := differsAt(formatted, expected); ln != 0 { + t.Fatalf("Expected roundtripped bytes to equal expected bytes but differed near line %d / byte %d:\n%s", ln, at, prefixWithLineNumbers(formatted)) + } + + }) + } +} + +func TestFormatV1Source(t *testing.T) { + regoFiles, err := filepath.Glob("testfiles/v1/*.rego") + if err != nil { + panic(err) + } + + for _, rego := range regoFiles { + t.Run(rego, func(t *testing.T) { + contents, err := os.ReadFile(rego) + if err != nil { + t.Fatalf("Failed to read rego source: %v", err) + } + + expected, err := os.ReadFile(rego + ".formatted") + if err != nil { + t.Fatalf("Failed to read expected rego source: %v", err) + } + + popts := ast.ParserOptions{ + RegoVersion: ast.RegoV1, + } + opts := Opts{ + RegoVersion: ast.RegoV1, + ParserOptions: &popts, + } + + formatted, err := SourceWithOpts(rego, contents, opts) + if err != nil { + t.Fatalf("Failed to format file: %v", err) + } + + if ln, at := differsAt(formatted, expected); ln != 0 { + t.Fatalf("Expected formatted bytes to equal expected bytes but differed near line %d / byte %d (got: %q, expected: %q):\n%s", ln, at, formatted[at], expected[at], prefixWithLineNumbers(formatted)) + } + + if _, err := ast.ParseModuleWithOpts(rego+".tmp", string(formatted), popts); err != nil { + t.Fatalf("Failed to parse formatted bytes: %v", err) + } + + formatted, err = SourceWithOpts(rego, formatted, opts) + if err != nil { + t.Fatalf("Failed to double format file") + } + + if ln, at := differsAt(formatted, expected); ln != 0 { + t.Fatalf("Expected roundtripped bytes to equal expected bytes but differed near line %d / byte %d:\n%s", ln, at, prefixWithLineNumbers(formatted)) + } + + }) + } +} + +func TestFormatV0SourceToRegoV1(t *testing.T) { + regoFiles, err := filepath.Glob("testfiles/v0_to_v1/*.rego") + if err != nil { + panic(err) + } + + for _, rego := range regoFiles { + t.Run(rego, func(t *testing.T) { + contents, err := os.ReadFile(rego) + if err != nil { + t.Fatalf("Failed to read rego source: %v", err) + } + + errorExpected := false + expected, err := os.ReadFile(rego + ".formatted") + if err != nil { + if os.IsNotExist(err) { + errorExpected = true + expected, err = os.ReadFile(rego + ".error") + if err != nil { + t.Fatalf("Failed to read expected error source: %v", err) + } + } + if !errorExpected { + t.Fatalf("Failed to read expected rego source: %v", err) + } + } + + sourceOpts := Opts{ + RegoVersion: ast.RegoV0CompatV1, // Target syntax is v0 compat v1 + ParserOptions: &ast.ParserOptions{ + RegoVersion: ast.RegoV0, // Original syntax is v0 + }, + } + targetOpts := Opts{ + RegoVersion: ast.RegoV0CompatV1, // Target syntax is v0 compat v1 + } + + if errorExpected { + formatted, err := SourceWithOpts(rego, contents, sourceOpts) + + if err == nil { + t.Fatalf("Expected error, got: %s", formatted) + } + if err.Error() != string(expected) { + t.Fatalf("Expected error:\n\n'%s'\n\ngot:\n\n'%s'", expected, err.Error()) + } + } else { + formatted, err := SourceWithOpts(rego, contents, sourceOpts) + + if err != nil { + t.Fatalf("Failed to format file: %v", err) + } + + if ln, at := differsAt(formatted, expected); ln != 0 { + t.Fatalf("Expected formatted bytes to equal expected bytes but differed near line %d / byte %d (got: %q, expected: %q):\n%s", ln, at, formatted[at], expected[at], prefixWithLineNumbers(formatted)) + } + + if _, err := ast.ParseModule(rego+".tmp", string(formatted)); err != nil { + t.Fatalf("Failed to parse formatted bytes: %v", err) + } + + formatted, err = SourceWithOpts(rego, formatted, targetOpts) + if err != nil { + t.Fatalf("Failed to double format file") + } + + if ln, at := differsAt(formatted, expected); ln != 0 { + t.Fatalf("Expected roundtripped bytes to equal expected bytes but differed near line %d / byte %d:\n%s", ln, at, prefixWithLineNumbers(formatted)) + } + + // rego-v1 formatted code is still compliant with v0, and should not be changed if formatted as such + formatted, err = SourceWithOpts(rego, formatted, targetOpts) + if err != nil { + t.Fatalf("Failed to double format file as v0") + } + + if ln, at := differsAt(formatted, expected); ln != 0 { + t.Fatalf("Expected roundtripped bytes to equal expected bytes but differed near line %d / byte %d:\n%s", ln, at, prefixWithLineNumbers(formatted)) + } + } + }) + } +} + +func TestFormatAST(t *testing.T) { + cases := []struct { + note string + regoVersion ast.RegoVersion + toFmt any + expected string + }{ + { + note: "var", + toFmt: ast.Var(`foo`), + expected: "foo", + }, + { + note: "string", + toFmt: &ast.Term{ + Value: ast.String("foo"), + Location: &ast.Location{Text: []byte(`"foo"`)}, + }, + expected: `"foo"`, + }, + { + note: "var wildcard", + toFmt: ast.Var(`$12`), + expected: "_", + }, + { + note: "string with wildcard prefix", + toFmt: &ast.Term{ + Value: ast.String("$01"), + Location: &ast.Location{Text: []byte(`"$01"`)}, + }, + expected: `"$01"`, + }, + { + note: "ref var only", + toFmt: ast.MustParseRef(`data.foo`), + expected: "data.foo", + }, + { + note: "ref multi vars", + toFmt: ast.MustParseRef(`data.foo.bar.baz`), + expected: "data.foo.bar.baz", + }, + { + note: "ref with string", + toFmt: ast.MustParseRef(`data["foo"]`), + expected: `data.foo`, + }, + { + note: "ref multi string", + toFmt: ast.MustParseRef(`data["foo"]["bar"]["baz"]`), + expected: `data.foo.bar.baz`, + }, + { + note: "ref with string needs brackets", + toFmt: ast.MustParseRef(`data["foo my-var\nbar"]`), + expected: `data["foo my-var\nbar"]`, + }, + { + note: "ref multi string needs brackets", + toFmt: ast.MustParseRef(`data["foo my-var"]["bar"]["almost.baz"]`), + expected: `data["foo my-var"].bar["almost.baz"]`, + }, + { + note: "ref var wildcard", + toFmt: ast.MustParseRef(`data.foo[_]`), + expected: "data.foo[_]", + }, + { + note: "ref var wildcard", + toFmt: ast.MustParseRef(`foo[_]`), + expected: "foo[_]", + }, + { + note: "ref string with wildcard prefix", + toFmt: ast.MustParseRef(`foo["$01"]`), + expected: `foo["$01"]`, + }, + { + note: "nested ref var wildcard", + toFmt: ast.MustParseRef(`foo[bar[baz[_]]]`), + expected: "foo[bar[baz[_]]]", + }, + { + note: "ref mixed", + toFmt: ast.MustParseRef(`foo["bar"].baz[_]["bar-2"].qux`), + expected: `foo.bar.baz[_]["bar-2"].qux`, + }, + { + note: "ref empty", + toFmt: ast.Ref{}, + expected: ``, + }, + { + note: "ref nil", + toFmt: ast.Ref(nil), + expected: ``, + }, + { + note: "ref operator", + toFmt: ast.MustParseRef(`foo[count(foo) - 1]`), + expected: `foo[count(foo) - 1]`, + }, + { + note: "x in xs", + toFmt: ast.Member.Call(ast.VarTerm("x"), ast.VarTerm("xs")), + expected: `x in xs`, + }, + { + note: "x, y in xs", + toFmt: ast.MemberWithKey.Call(ast.VarTerm("x"), ast.VarTerm("y"), ast.VarTerm("xs")), + expected: `(x, y in xs)`, + }, + { + note: "some x in xs", + toFmt: ast.NewExpr(&ast.SomeDecl{Symbols: []*ast.Term{ + ast.Member.Call(ast.VarTerm("x"), ast.VarTerm("xs")), + }}), + expected: `some x in xs`, + }, + { + note: "some x, y in xs", + toFmt: ast.NewExpr(&ast.SomeDecl{Symbols: []*ast.Term{ + ast.MemberWithKey.Call(ast.VarTerm("x"), ast.VarTerm("y"), ast.VarTerm("xs")), + }}), + expected: `some x, y in xs`, + }, + { + note: "v0, every adds import if missing", + regoVersion: ast.RegoV0, + toFmt: ast.MustParseModuleWithOpts(`package test + p { + every k, v in [1, 2] { k != v } + }`, + ast.ParserOptions{ + RegoVersion: ast.RegoV0, + FutureKeywords: []string{"every"}, + }), + expected: `package test + +import future.keywords.every + +p { + every k, v in [1, 2] { k != v } +}`, + }, + { + note: "v1, every doesn't add import if missing", + regoVersion: ast.RegoV1, + toFmt: ast.MustParseModuleWithOpts(`package test + p if { + every k, v in [1, 2] { k != v } + }`, + ast.ParserOptions{RegoVersion: ast.RegoV1}), + expected: `package test + +p if { + every k, v in [1, 2] { k != v } +}`, + }, + { + note: "v0: every does not add import if all future KWs are there", + regoVersion: ast.RegoV0, + toFmt: ast.MustParseModuleWithOpts(`package test + import future.keywords + p { + every k, v in [1, 2] { k != v } + }`, + ast.ParserOptions{ + FutureKeywords: []string{"every"}, + RegoVersion: ast.RegoV0, + }), + expected: `package test + +import future.keywords + +p if { + every k, v in [1, 2] { k != v } +}`, + }, + { + note: "v0: every does not add import if already present", + regoVersion: ast.RegoV0, + toFmt: ast.MustParseModuleWithOpts(`package test + import future.keywords + p { + every k, v in [1, 2] { k != v } + }`, + ast.ParserOptions{ + FutureKeywords: []string{"every"}, + RegoVersion: ast.RegoV0, + }), + expected: `package test + +import future.keywords + +p if { + every k, v in [1, 2] { k != v } +}`, + }, + { + note: "body shared wildcard", + toFmt: ast.Body{ + &ast.Expr{ + Index: 0, + Terms: []*ast.Term{ + ast.RefTerm(ast.VarTerm("eq")), + ast.RefTerm(ast.VarTerm("input"), ast.StringTerm("arr"), ast.VarTerm("$01"), ast.StringTerm("some key"), ast.VarTerm("$02")), + ast.VarTerm("bar"), + }, + }, + &ast.Expr{ + Index: 1, + Location: &ast.Location{ + Row: 2, + Col: 1, + }, + Terms: []*ast.Term{ + ast.RefTerm(ast.VarTerm("eq")), + ast.RefTerm(ast.VarTerm("input"), ast.StringTerm("arr"), ast.VarTerm("$01"), ast.StringTerm("bar")), + ast.VarTerm("qux"), + }, + }, + &ast.Expr{ + Index: 1, + Location: &ast.Location{ + Row: 2, + Col: 1, + }, + Terms: []*ast.Term{ + ast.RefTerm(ast.VarTerm("eq")), + ast.RefTerm(ast.VarTerm("foo"), ast.VarTerm("$03"), ast.VarTerm("$01"), ast.StringTerm("bar")), + ast.RefTerm(ast.VarTerm("bar"), ast.VarTerm("$03"), ast.VarTerm("$04"), ast.VarTerm("$01"), ast.StringTerm("bar")), + }, + }, + }, + expected: `input.arr[_01]["some key"][_] = bar +input.arr[_01].bar = qux +foo[_03][_01].bar = bar[_03][_][_01].bar +`, + }, + { + note: "body shared wildcard - ref head", + toFmt: ast.Body{ + &ast.Expr{ + Index: 0, + Terms: ast.VarTerm("$x"), + }, + &ast.Expr{ + Index: 1, + Terms: ast.RefTerm(ast.VarTerm("$x"), ast.VarTerm("y")), + }, + }, + expected: `_x +_x[y]`, + }, + { + note: "body shared wildcard - nested ref", + toFmt: ast.Body{ + &ast.Expr{ + Index: 0, + Terms: ast.VarTerm("$x"), + }, + &ast.Expr{ + Index: 1, + Terms: ast.RefTerm(ast.VarTerm("a"), ast.RefTerm(ast.VarTerm("$x"), ast.VarTerm("y"))), + }, + }, + expected: `_x +a[_x[y]]`, + }, + { + note: "body shared wildcard - nested ref array", + toFmt: ast.Body{ + &ast.Expr{ + Index: 0, + Terms: ast.VarTerm("$x"), + }, + &ast.Expr{ + Index: 1, + Terms: ast.RefTerm(ast.VarTerm("a"), ast.RefTerm(ast.VarTerm("$x"), ast.VarTerm("y"), ast.ArrayTerm(ast.VarTerm("z"), ast.VarTerm("w")))), + }, + }, + expected: `_x +a[_x[y][[z, w]]]`, + }, + { + note: "expr with wildcard that has a default location", + toFmt: func() *ast.Expr { + expr := ast.MustParseExpr(`["foo", _] = split(input.foo, ":")`) + ast.WalkTerms(expr, func(term *ast.Term) bool { + v, ok := term.Value.(ast.Var) + if ok && v.IsWildcard() { + term.Location = defaultLocation(term) + return true + } + term.Location.File = "foo.rego" + term.Location.Row = 2 + return false + }) + return expr + }(), + expected: `["foo", _] = split(input.foo, ":")`, + }, + { + note: "expr all terms having empty-file locations", + toFmt: ast.MustParseExpr(`[ + "foo", + _ + ] = split(input.foo, ":")`), + expected: ` +[ + "foo", + _, +] = split(input.foo, ":")`, + }, + { + note: "expr where all terms having empty-file locations, and one is a default location", + toFmt: func() *ast.Expr { + expr := ast.MustParseExpr(` +["foo", __local1__] = split(input.foo, ":")`) + ast.WalkTerms(expr, func(term *ast.Term) bool { + if ast.VarTerm("__local1__").Equal(term) { + term.Location = defaultLocation(term) + return true + } + return false + }) + return expr + }(), + expected: `["foo", __local1__] = split(input.foo, ":")`, + }, + { + note: "expr where generated var has an AST location not matching its source location", + toFmt: func() *ast.Expr { + e := ast.MustParseExpr(`__local0__ = concat(",", [__local1__])`) + ast.WalkTerms(e, func(t *ast.Term) bool { + t.Location.File = "t.rego" + return false + }) + // mangling that may happen in PE + return ast.Concat.Expr( + e.Operand(1).Value.(ast.Call)[1], + e.Operand(1).Value.(ast.Call)[2], + e.Operand(0), + ).SetLocation(e.Location) + }(), + expected: `concat(",", [__local1__], __local0__)`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + bs, err := AstWithOpts(tc.toFmt, Opts{ + RegoVersion: tc.regoVersion, + ParserOptions: &ast.ParserOptions{ + RegoVersion: tc.regoVersion, + }, + }) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + expected := strings.TrimSpace(tc.expected) + actual := strings.TrimSpace(string(bs)) + if actual != expected { + t.Fatalf("Expected:\n\n%q\n\nGot:\n\n%q\n\n", expected, actual) + } + }) + + // consistency check: disregarding source locations, it shouldn't panic + t.Run("no_loc/"+tc.note, func(t *testing.T) { + _, err := AstWithOpts(tc.toFmt, Opts{IgnoreLocations: true}) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + }) + } +} + +func TestFormatAST_Error(t *testing.T) { + cases := []struct { + note string + regoVersion ast.RegoVersion + toFmt any + expErr string + }{ + { + note: "package with only data term", + toFmt: &ast.Package{Path: ast.Ref{ast.DefaultRootDocument}}, + expErr: `rego_format_error: invalid package path: data`, + }, + { + note: "module with package with only data term", + toFmt: &ast.Module{ + Package: &ast.Package{Path: ast.Ref{ast.DefaultRootDocument}}, + }, + expErr: `rego_format_error: invalid package path: data`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + _, err := AstWithOpts(tc.toFmt, Opts{ + RegoVersion: tc.regoVersion, + ParserOptions: &ast.ParserOptions{ + RegoVersion: tc.regoVersion, + }, + }) + if err == nil { + t.Fatalf("Expected error, got nil") + } + if !strings.Contains(err.Error(), tc.expErr) { + t.Fatalf("Expected error to contain:\n\n%q\n\ngot:\n\n%q", tc.expErr, err.Error()) + } + }) + } +} + +func TestFormatDeepCopy(t *testing.T) { + + original := ast.Body{ + &ast.Expr{ + Index: 0, + Terms: ast.VarTerm("$x"), + }, + &ast.Expr{ + Index: 1, + Terms: ast.RefTerm(ast.VarTerm("$x"), ast.VarTerm("y")), + }, + } + + cpy := original.Copy() + + _, err := Ast(original) + if err != nil { + t.Fatal(err) + } + + if !cpy.Equal(original) { + t.Fatal("expected original to be unmodified") + } + +} + +func differsAt(a, b []byte) (int, int) { + if bytes.Equal(a, b) { + return 0, 0 + } + minLen := min(len(a), len(b)) + ln := 1 + for i := range minLen { + if a[i] == '\n' { + ln++ + } + if a[i] != b[i] { + return ln, i + } + } + return ln, minLen - 1 +} + +func prefixWithLineNumbers(bs []byte) []byte { + raw := string(bs) + lines := strings.Split(raw, "\n") + format := fmt.Sprintf("%%%dd %%s", len(strconv.Itoa(len(lines)+1))) + for i, line := range lines { + lines[i] = fmt.Sprintf(format, i+1, line) + } + return []byte(strings.Join(lines, "\n")) +} + +func TestSource_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expFormatted string + expErrs []string + }{ + { + note: "v0", // from default rego-version + module: `package test + +p[x] { + x = "a" +}`, + + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1", + module: `package test + +p contains x if { + x = "a" +}`, + expFormatted: `package test + +p contains x if { + x = "a" +} +`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + formatted, err := Source("test.rego", []byte(tc.module)) + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected error:\n\n%q\n\nbut got:\n\n%q", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + formattedStr := string(formatted) + if formattedStr != tc.expFormatted { + t.Fatalf("expected %q but got %q", tc.expFormatted, formattedStr) + } + } + }) + } +} + +func TestSourceWithOpts_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + toRegoVersion ast.RegoVersion + module string + expFormatted string + expErrs []string + }{ + { + note: "v0 -> v0", // from default rego-version + toRegoVersion: ast.RegoV0, + module: `package test + +p[x] { + x = "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0 -> v1", // from default rego-version + toRegoVersion: ast.RegoV1, + module: `package test + +p[x] { + x = "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1 -> v1", // from non-default rego-version + toRegoVersion: ast.RegoV1, + module: `package test + +p contains x if { + x = "a" +}`, + expFormatted: `package test + +p contains x if { + x = "a" +} +`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + formatted, err := SourceWithOpts("test.rego", []byte(tc.module), Opts{RegoVersion: tc.toRegoVersion}) + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("expected error:\n\n%q\n\nbut got:\n\n%q", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + formattedStr := string(formatted) + if formattedStr != tc.expFormatted { + t.Fatalf("expected %q but got %q", tc.expFormatted, formattedStr) + } + } + }) + } +} + +func TestGroupableOneLinerRules(t *testing.T) { + contents := []byte(`package test + +foo := 1 if input.x +foo := 2 if not input.x + +a := 1 +b := 2 + +c := 3 + +d := 4 + +# comment above group +e := 5 +f := 6 +`) + + formatted, err := Source("test.rego", contents) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if !bytes.Equal(formatted, contents) { + t.Fatalf("expected %q but got %q", formatted, contents) + } +} + +func TestFormatKeywordsInRefs(t *testing.T) { + regoVersions := map[string]ast.RegoVersion{ + "v0": ast.RegoV0, + "v1": ast.RegoV1, + } + + for versionName, regoVersion := range regoVersions { + t.Run(versionName, func(t *testing.T) { + regoFiles, err := filepath.Glob(fmt.Sprintf("testfiles/%s/*.rego.formatted_no_keywords_in_refs", versionName)) + if err != nil { + panic(err) + } + + for _, expected_rego := range regoFiles { + original_rego := strings.TrimSuffix(expected_rego, ".formatted_no_keywords_in_refs") + t.Run(original_rego, func(t *testing.T) { + expected, err := os.ReadFile(expected_rego) + if err != nil { + t.Fatalf("Failed to read expected rego source: %v", err) + } + + original, err := os.ReadFile(original_rego) + if err != nil { + t.Fatalf("Failed to read rego source: %v", err) + } + + caps := ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(regoVersion)) + feats := []string{} + for _, f := range caps.Features { + if f != ast.FeatureKeywordsInRefs { + feats = append(feats, f) + } + } + caps.Features = feats + + popts := ast.ParserOptions{ + RegoVersion: regoVersion, + } + opts := Opts{ + RegoVersion: regoVersion, + ParserOptions: &popts, + Capabilities: caps, + } + + formatted, err := SourceWithOpts(original_rego, original, opts) + if err != nil { + t.Fatalf("Failed to format file: %v", err) + } + + if ln, at := differsAt(formatted, expected); ln != 0 { + t.Fatalf("Expected formatted bytes to equal expected bytes but differed near line %d / byte %d (got: %q, expected: %q):\n%s", ln, at, formatted[at], expected[at], prefixWithLineNumbers(formatted)) + } + + if _, err := ast.ParseModuleWithOpts(original_rego+".tmp", string(formatted), popts); err != nil { + t.Fatalf("Failed to parse formatted bytes: %v", err) + } + + formatted, err = SourceWithOpts(original_rego, formatted, opts) + if err != nil { + t.Fatalf("Failed to double format file") + } + + if ln, at := differsAt(formatted, expected); ln != 0 { + t.Fatalf("Expected roundtripped bytes to equal expected bytes but differed near line %d / byte %d:\n%s", ln, at, prefixWithLineNumbers(formatted)) + } + + }) + } + }) + } +} + +// 382 3064960 ns/op 4573131 B/op 26266 allocs/op // no optimizations +// 685 1737719 ns/op 1972193 B/op 14160 allocs/op // pre-allocate partitionComments +// 708 1674343 ns/op 1916700 B/op 11556 allocs/op // static memberRef & memberWithKeyRef +// 746 1594546 ns/op 1882652 B/op 10644 allocs/op // various minor fixes +// 1250 853508 ns/op 441730 B/op 8895 allocs/op // partitionComments early return if unchanged +// 1396 812859 ns/op 362651 B/op 8811 allocs/op // partitionComments reuse backing array +func BenchmarkFormatLargePolicy(b *testing.B) { + contents, err := os.ReadFile("testdata/bench.rego") + if err != nil { + b.Fatalf("Failed to read rego source: %v", err) + } + module := ast.MustParseModule(string(contents)) + + b.ResetTimer() + + for range b.N { + _, err := AstWithOpts(module, Opts{RegoVersion: ast.RegoV1}) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/third_party/opa/v1/format/testdata/bench.rego b/third_party/opa/v1/format/testdata/bench.rego new file mode 100644 index 000000000000..49800299e116 --- /dev/null +++ b/third_party/opa/v1/format/testdata/bench.rego @@ -0,0 +1,802 @@ +# METADATA +# description: | +# the 'ast' package provides the base functionality for working +# with OPA's AST, more recently in the form of RoAST +package regal.ast + +import data.regal.config +import data.regal.util + +# METADATA +# description: set of Rego's scalar type +scalar_types := {"boolean", "null", "number", "string"} + +# METADATA +# description: set containing names of all built-in functions counting as operators +operators := { + "and", + "assign", + "div", + "eq", + "equal", + "gt", + "gte", + "internal.member_2", + "internal.member_3", + "lt", + "lte", + "minus", + "mul", + "neq", + "or", + "plus", + "rem", +} + +# METADATA +# description: | +# returns true if provided term is either a scalar or a collection of ground values +# scope: document +is_constant(term) if term.type in scalar_types # regal ignore:external-reference + +is_constant(term) if { + term.type in {"array", "object"} + not has_term_var(term.value) +} + +# METADATA +# description: true if provided term represents a wildcard (`_`) variable +is_wildcard(term) if { + term.type == "var" + startswith(term.value, "$") +} + +default builtin_names := set() + +# METADATA +# description: set containing the name of all built-in functions (given the active capabilities) +# scope: document +builtin_names := object.keys(config.capabilities.builtins) + +# METADATA +# description: | +# set containing the namespaces of all built-in functions (given the active capabilities), +# like "http" in `http.send` or "sum" in `sum`` +builtin_namespaces contains namespace if { + some name in builtin_names + namespace := split(name, ".")[0] +} + +# METADATA +# description: | +# provides the package path values (strings) as an array starting _from_ "data": +# package foo.bar -> ["foo", "bar"] +package_path := [path.value | + some i, path in input["package"].path + i > 0 +] + +# METADATA +# description: | +# provide the package name / path as originally declared in the +# input policy, so "package foo.bar" would return "foo.bar" +package_name := concat(".", package_path) + +# METADATA +# description: provides all static string values from ref +named_refs(ref) := [term | + some i, term in ref + _is_name(term, i) +] + +_is_name(term, 0) if term.type == "var" + +_is_name(term, pos) if { + pos > 0 + term.type == "string" +} + +# METADATA +# description: all the rules (excluding functions) in the input AST +rules := [rule | + some rule in input.rules + not rule.head.args +] + +# METADATA +# description: all the test rules in the input AST +tests := [rule | + some rule in input.rules + not rule.head.args + + startswith(ref_to_string(rule.head.ref), "test_") +] + +# METADATA +# description: all the functions declared in the input AST +functions := [rule | + some rule in input.rules + rule.head.args +] + +# METADATA +# description: | +# all rules and functions in the input AST not denoted as private, i.e. excluding +# any rule/function with a `_` prefix. it's not unthinkable that more ways to denote +# private rules (or even packages), so using this rule should be preferred over +# manually checking for this using the rule ref +public_rules_and_functions := [rule | + some rule in input.rules + + count([part | + some i, part in rule.head.ref + + _private_rule(i, part) + ]) == 0 +] + +_private_rule(0, part) if startswith(part.value, "_") + +_private_rule(i, part) if { + i > 0 + part.type == "string" + startswith(part.value, "_") +} + +# METADATA +# description: a list of the argument names for the given rule (if function) +function_arg_names(rule) := [arg.value | some arg in rule.head.args] + +# METADATA +# description: all the rule and function names in the input AST +rule_and_function_names contains ref_to_string(rule.head.ref) if some rule in input.rules + +# METADATA +# description: all identifiers in the input AST (rule and function names, plus imported names) +identifiers := rule_and_function_names | imported_identifiers + +# METADATA +# description: all rule names in the input AST (excluding functions) +rule_names contains ref_to_string(rule.head.ref) if some rule in rules + +# METADATA +# description: | +# determine if var in var (e.g. `x` in `input[x]`) is used as input or output +# scope: document +is_output_var(rule, var) if { + # test the cheap and common case first, and 'else' only when it's not + is_wildcard(var) +} else if { + not var.value in (rule_names | imported_identifiers) # regal ignore:external-reference + + num_above := count([1 | + some above in find_vars_in_local_scope(rule, var.location) + above.value == var.value + ]) + num_some := count([1 | + some name in find_some_decl_names_in_scope(rule, var.location) + name == var.value + ]) + + # only the first ref variable in scope can be an output! meaning that: + # allow if { + # some x + # input[x] # <--- output + # data.bar[x] # <--- input + # } + num_above - num_some == 0 +} + +# METADATA +# description: as the name implies, answers whether provided value is a ref +# scope: document +is_ref(value) if value.type == "ref" + +is_ref(value) if value[0].type == "ref" + +# METADATA +# description: | +# returns an array of all rule indices, as strings. this will be needed until +# https://github.com/open-policy-agent/opa/issues/6736 is fixed +rule_index_strings := [s | + some i, _ in _rules + s := sprintf("%d", [i]) +] + +# METADATA +# description: | +# a map containing all function calls (built-in and custom) in the input AST +# keyed by rule index +function_calls[rule_index] contains call if { + some rule_index in rule_index_strings + some ref in found.refs[rule_index] + + name := ref_to_string(ref[0].value) + args := [arg | + some i, arg in array.slice(ref, 1, 100) + + not _exclude_arg(name, i, arg) + ] + + call := { + "name": ref_to_string(ref[0].value), + "location": ref[0].location, + "args": args, + } +} + +# these will be aggregated as calls anyway, so let's try and keep this flat +_exclude_arg(_, _, arg) if arg.type == "call" + +# first "arg" of assign is the variable to assign to.. special case we simply +# ignore here, as it's covered elsewhere +_exclude_arg("assign", 0, _) + +# METADATA +# description: returns the "path" string of any given ref value +ref_to_string(ref) := concat("", [_ref_part_to_string(i, part) | some i, part in ref]) + +_ref_part_to_string(0, part) := part.value + +_ref_part_to_string(i, part) := _format_part(part) if i > 0 + +_format_part(part) := sprintf(".%s", [part.value]) if { + part.type == "string" + regex.match(`^[a-zA-Z_][a-zA-Z1-9_]*$`, part.value) +} else := sprintf(`["%v"]`, [part.value]) if { + part.type == "string" +} else := sprintf(`[%v]`, [part.value]) + +# METADATA +# description: | +# returns the string representation of a ref up until its first +# non-static (i.e. variable) value, if any: +# foo.bar -> foo.bar +# foo.bar[baz] -> foo.bar +ref_static_to_string(ref) := str if { + rs := ref_to_string(ref) + str := _trim_from_var(rs, regex.find_n(`\[[^"]`, rs, 1)) +} + +_trim_from_var(ref_str, vars) := ref_str if { + count(vars) == 0 +} else := substring(ref_str, 0, indexof(ref_str, vars[0])) + +# METADATA +# description: true if ref contains only static parts +static_ref(ref) if not _non_static_ref(ref) + +# optimized inverse of static_ref benefitting from early exit +# 128 is used only as a reasonable (well...) upper limit for a ref, but the +# slice will be capped at the length of the ref anyway (avoids count) +_non_static_ref(ref) if array.slice(ref.value, 1, 128)[_].type in {"var", "ref"} + +# METADATA +# description: provides a set of names of all built-in functions called in the input policy +builtin_functions_called contains name if { + name := function_calls[_][_].name + name in builtin_names +} + +# METADATA +# description: | +# Returns custom functions declared in input policy in the same format as builtin capabilities +function_decls(rules) := {rule_name: decl | + # regal ignore:external-reference + some rule in functions + + rule_name := ref_to_string(rule.head.ref) + + # ensure we only get one set of args, or we'll have a conflict + args := [[item | + some arg in rule.head.args + item := {"type": "any"} + ] | + some rule in rules + ref_to_string(rule.head.ref) == rule_name + ][0] + + decl := {"decl": {"args": args, "result": {"type": "any"}}} +} + +# METADATA +# description: returns the args for function past the expected number of args +function_ret_args(fn_name, terms) := array.slice(terms, count(all_functions[fn_name].decl.args) + 1, count(terms)) + +# METADATA +# description: true if last argument of function is a return assignment +function_ret_in_args(fn_name, terms) if { + # special case: print does not have a last argument as it's variadic + fn_name != "print" + + rest := array.slice(terms, 1, count(terms)) + + # for now, bail out of nested calls + not "call" in {term.type | some term in rest} + + count(rest) > count(all_functions[fn_name].decl.args) +} + +# METADATA +# description: answers if provided rule is implicitly assigned boolean true, i.e. allow { .. } or not +# scope: document +implicit_boolean_assignment(rule) if { + # note the missing location attribute here, which is how we distinguish + # between implicit and explicit assignments + rule.head.value == {"type": "boolean", "value": true} +} + +# or sometimes, like this... +implicit_boolean_assignment(rule) if rule.head.value.location == rule.head.location + +implicit_boolean_assignment(rule) if util.to_location_object(rule.head.value.location).col == 1 + +# METADATA +# description: | +# object containing all available built-in and custom functions in the +# scope of the input AST, keyed by function name +all_functions := object.union(config.capabilities.builtins, function_decls(input.rules)) + +# METADATA +# description: | +# set containing all available built-in and custom function names in the +# scope of the input AST +all_function_names := object.keys(all_functions) + +# METADATA +# description: set containing all negated expressions in input AST +negated_expressions[rule_index] contains value if { + some i, rule in _rules + + # converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed + rule_index := sprintf("%d", [i]) + + walk(rule, [_, value]) + + value.negated +} + +# METADATA +# description: | +# true if rule head contains no identifier, but is a chained rule body immediately following the previous one: +# foo { +# input.bar +# } { # <-- chained rule body +# input.baz +# } +is_chained_rule_body(rule, lines) if { + head_loc := util.to_location_object(rule.head.location) + + row_text := lines[head_loc.row - 1] + col_text := substring(row_text, head_loc.col - 1, -1) + + startswith(col_text, "{") +} + +# METADATA +# description: answers wether variable of `name` is found anywhere in provided rule `head` +# scope: document +var_in_head(head, name) if { + head.value.value == name +} else if { + head.key.value == name +} else if { + some var in find_term_vars(head.value.value) + var.value == name +} else if { + some var in find_term_vars(head.key.value) + var.value == name +} else if { + some i, var in head.ref + i > 0 + var.value == name +} + +# METADATA +# description: | +# true if var of `name` is referenced in any `calls` (likely, +# `ast.function_calls`) in the rule of given `rule_index` +# scope: document +var_in_call(calls, rule_index, name) if _var_in_arg(calls[rule_index][_].args[_], name) + +_var_in_arg(arg, name) if { + arg.type == "var" + arg.value == name +} + +_var_in_arg(arg, name) if { + arg.type in {"array", "object", "set"} + + some var in find_term_vars(arg) + + var.value == name +} + +# METADATA +# description: answers wether provided expression is an assignment (using `:=`) +is_assignment(expr) if { + expr.terms[0].type == "ref" + expr.terms[0].value[0].type == "var" + expr.terms[0].value[0].value == "assign" +} + +# METADATA +# description: returns the terms in an assignment (`:=`) expression, or undefined if not assignment +assignment_terms(expr) := [expr.terms[1], expr.terms[2]] if is_assignment(expr) + +# METADATA +# description: | +# For a given rule head name, this rule contains a list of locations where +# there is a rule head with that name. +rule_head_locations[name] contains {"row": loc.row, "col": loc.col} if { + some rule in input.rules + + name := concat(".", [ + "data", + package_name, + ref_static_to_string(rule.head.ref), + ]) + + loc := util.to_location_object(rule.head.location) +} + +_find_nested_vars(obj) := [value | + walk(obj, [_, value]) + value.type == "var" + indexof(value.value, "$") == -1 +] + +# simple assignment, i.e. `x := 100` returns `x` +# always returns a single var, but wrapped in an +# array for consistency +_find_assign_vars(value) := var if { + value[1].type == "var" + var := [value[1]] +} + +# 'destructuring' array assignment, i.e. +# [a, b, c] := [1, 2, 3] +# or +# {a: b} := {"foo": "bar"} +_find_assign_vars(value) := vars if { + value[1].type in {"array", "object"} + vars := _find_nested_vars(value[1]) +} + +# var declared via `some`, i.e. `some x` or `some x, y` +_find_some_decl_vars(value) := [v | + some v in value + v.type == "var" +] + +# single var declared via `some in`, i.e. `some x in y` +_find_some_in_decl_vars(value) := vars if { + arr := value[0].value + count(arr) == 3 + + vars := _find_nested_vars(arr[1]) +} + +# two vars declared via `some in`, i.e. `some x, y in z` +_find_some_in_decl_vars(value) := vars if { + arr := value[0].value + count(arr) == 4 + + vars := [v | + some i in [1, 2] + some v in _find_nested_vars(arr[i]) + ] +} + +# METADATA +# description: | +# find vars like input[x].foo[y] where x and y are vars +# note: value.type == "ref" check must have been done before calling this function +find_ref_vars(value) := [var | + some i, var in value.value + + i > 0 + var.type == "var" +] + +# one or two vars declared via `every`, i.e. `every x in y {}` +# or `every`, i.e. `every x, y in y {}` +_find_every_vars(value) := vars if { + key_var := [value.key | + value.key.type == "var" + indexof(value.key.value, "$") == -1 + ] + val_var := [value.value | + value.value.type == "var" + indexof(value.value.value, "$") == -1 + ] + + vars := array.concat(key_var, val_var) +} + +# METADATA +# description: | +# traverses all nodes in provided terms (using `walk`), and returns an array with +# all variables declared in terms, i,e [x, y] or {x: y}, etc. +find_term_vars(terms) := [term | + walk(terms, [_, term]) + + term.type == "var" +] + +# METADATA +# description: | +# traverses all nodes in provided terms (using `walk`), and returns true if any variable +# is found in terms, with early exit (as opposed to find_term_vars) +has_term_var(terms) if { + walk(terms, [_, term]) + + term.type == "var" +} + +_find_vars(value, last) := {"term": find_term_vars(function_ret_args(fn_name, value))} if { + last == "terms" + value[0].type == "ref" + value[0].value[0].type == "var" + value[0].value[0].value != "assign" + + fn_name := ref_to_string(value[0].value) + + not contains(fn_name, "$") + fn_name in all_function_names # regal ignore:external-reference + function_ret_in_args(fn_name, value) +} + +# `=` isn't necessarily assignment, and only considering the variable on the +# left-hand side is equally dubious, but we'll treat `x = 1` as `x := 1` for +# the purpose of this function until we have a more robust way of dealing with +# unification +_find_vars(value, last) := {"assign": _find_assign_vars(value)} if { + last == "terms" + value[0].type == "ref" + value[0].value[0].type == "var" + value[0].value[0].value in {"assign", "eq"} +} + +_find_vars(value, last) := {"somein": _find_some_in_decl_vars(value)} if { + last == "symbols" + value[0].type == "call" +} + +_find_vars(value, last) := {"some": _find_some_decl_vars(value)} if { + last == "symbols" + value[0].type != "call" +} + +_find_vars(value, last) := {"every": _find_every_vars(value)} if { + last == "terms" + value.domain +} + +_find_vars(value, last) := {"args": arg_vars} if { + last == "args" + + arg_vars := [arg | + some arg in value + arg.type == "var" + ] + + count(arg_vars) > 0 +} + +_rule_index(rule) := sprintf("%d", [i]) if { + some i, r in _rules # regal ignore:external-reference + r == rule +} + +# METADATA +# description: | +# traverses all nodes under provided node (using `walk`), and returns an array with +# all variables declared via assignment (:=), `some`, `every` and in comprehensions +# DEPRECATED: uses ast.found.vars instead +find_vars(node) := array.concat( + [var | + walk(node, [path, value]) + + last := regal.last(path) + last in {"terms", "symbols", "args"} + + var := _find_vars(value, last)[_][_] + ], + [var | + walk(node, [_, value]) + + value.type == "ref" + + some x, var in value.value + x > 0 + var.type == "var" + ], +) + +# hack to work around the different input models of linting vs. the lsp package.. we +# should probably consider something more robust +_rules := input.rules + +_rules := data.workspace.parsed[input.regal.file.uri].rules if not input.rules + +# METADATA: +# description: | +# object containing all variables found in the input AST, keyed first by the index of +# the rule where the variables were found (as a numeric string), and then the context +# of the variable, which will be one of: +# - term +# - assign +# - every +# - some +# - somein +# - ref +found.vars[rule_index][context] contains var if { + some i, rule in _rules + + # converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed + rule_index := sprintf("%d", [i]) + + walk(rule, [path, value]) + + last := regal.last(path) + last in {"terms", "symbols", "args"} + + some context, vars in _find_vars(value, last) + some var in vars +} + +found.vars[rule_index].ref contains var if { + some i, rule in _rules + + # converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed + rule_index := sprintf("%d", [i]) + + walk(rule, [_, value]) + + value.type == "ref" + + some x, var in value.value + x > 0 + var.type == "var" +} + +# METADATA +# description: all refs found in module +# scope: document +found.refs[rule_index] contains value if { + some i, rule in _rules + + # converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed + rule_index := sprintf("%d", [i]) + + walk(rule, [_, value]) + + value.type == "ref" +} + +found.refs[rule_index] contains value if { + some i, rule in _rules + + # converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed + rule_index := sprintf("%d", [i]) + + walk(rule, [_, value]) + + value[0].type == "ref" +} + +# METADATA +# description: all symbols found in module +found.symbols[rule_index] contains value.symbols if { + some i, rule in _rules + + # converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed + rule_index := sprintf("%d", [i]) + + walk(rule, [_, value]) +} + +# METADATA +# description: all comprehensions found in module +found.comprehensions[rule_index] contains value if { + some i, rule in _rules + + # converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed + rule_index := sprintf("%d", [i]) + + walk(rule, [_, value]) + + value.type in {"arraycomprehension", "objectcomprehension", "setcomprehension"} +} + +# METADATA +# description: | +# finds all vars declared in `rule` *before* the `location` provided +# note: this isn't 100% accurate, as it doesn't take into account `=` +# assignments / unification, but it's likely good enough since other rules +# recommend against those +find_vars_in_local_scope(rule, location) := [var | + var := found.vars[_rule_index(rule)][_][_] # regal ignore:external-reference + + not is_wildcard(var) + _before_location(rule, var, util.to_location_object(location)) +] + +_end_location(location) := end if { + loc := util.to_location_object(location) + lines := split(loc.text, "\n") + end := { + "row": (loc.row + count(lines)) - 1, + "col": loc.col + count(regal.last(lines)), + } +} + +# special case — the value location of the rule head "sees" +# all local variables declared in the rule body +_before_location(rule, _, location) if { + loc := util.to_location_object(location) + + value_start := util.to_location_object(rule.head.value.location) + + loc.row >= value_start.row + loc.col >= value_start.col + + value_end := _end_location(util.to_location_object(rule.head.value.location)) + + loc.row <= value_end.row + loc.col <= value_end.col +} + +_before_location(_, var, location) if { + util.to_location_object(var.location).row < util.to_location_object(location).row +} + +_before_location(_, var, location) if { + var_loc := util.to_location_object(var.location) + loc := util.to_location_object(location) + + var_loc.row == loc.row + var_loc.col < loc.col +} + +# METADATA +# description: find *only* names in the local scope, and not e.g. rule names +find_names_in_local_scope(rule, location) := names if { + fn_arg_names := _function_arg_names(rule) + var_names := {var.value | some var in find_vars_in_local_scope(rule, util.to_location_object(location))} + + names := fn_arg_names | var_names +} + +_function_arg_names(rule) := {arg.value | + some arg in rule.head.args + arg.type == "var" +} + +# METADATA +# description: | +# similar to `find_vars_in_local_scope`, but returns all variable names in scope +# of the given location *and* the rule names present in the scope (i.e. module) +find_names_in_scope(rule, location) := names if { + locals := find_names_in_local_scope(rule, util.to_location_object(location)) + + # parens below added by opa-fmt :) + names := (rule_names | imported_identifiers) | locals +} + +# METADATA +# description: | +# find all variables declared via `some` declarations (and *not* `some .. in`) +# in the scope of the given location +find_some_decl_names_in_scope(rule, location) := {some_var.value | + some some_var in found.vars[_rule_index(rule)]["some"] # regal ignore:external-reference + _before_location(rule, some_var, location) +} + +# METADATA +# description: all expressions in module +exprs[rule_index][expr_index] := expr if { + some rule_index, rule in input.rules + some expr_index, expr in rule.body +} diff --git a/third_party/opa/v1/format/testfiles/v0/test.rego b/third_party/opa/v1/format/testfiles/v0/test.rego new file mode 100644 index 000000000000..e0d30eb1d0e6 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test.rego @@ -0,0 +1,223 @@ +# The blank lines below me should be gone! (except one) + + +# Comment! +package a.b + +# I also, am a comment. +import data.x.y.z +import data.a.b.c # Another comment! +# I belong with data.a, there should be a newline before me. +import data.a +import data.f.g + +default foo = false +foo[x] { +not x = g + f(x) = 1 + g( + x, "foo" + ) = z +} + +globals = {"foo": "bar", +"fizz": "buzz"} + +partial_obj["x"] = 1 +partial_obj.y = 2 + +partial_obj["z"] = 3 { + true +} + +partial_set["x"] +partial_set.y + +# Latent comment. + +r = y { + y = x + split("foo.bar", ".", input.x) with input as {"x": x} + } + +# Comment on else + else = y { + y = ["howdy"] + x = {"x": { + "y": "z", + }} + a = {"a": { + "b": "c", + }, "b": "c", "c": [1, 2, + 3, 4]} + } + +fn(x) = y { + y = x +} + +fn_else(x) = 1 { + true +} # foo +else = +# bar +2 +{ + true +} else = 3 +# baz +{ + false +} + +long(x) = true { + x = "foo %host" + } + + short(x) { + x = "bar" + } + +raw_string = `hi\there` +raw_multiline = `this +string + is on +multiple lines` + +fn2([x, y, +z], {"foo": a}) = b { +split(x, y, c) +trim(a, z, d) # function comment 1 +split(c[0], d, b) +x = sprintf("hello %v", +["world"]) +#function comment 2 +} # function comment 3 + +f[x] { + x = "hi" +} { # Comment on chain + x = "bye" +} + + import data.foo.bar + import data.bar.foo # data.bar.foo should be first + +p[x] = y { y = x + y = "foo" + z = { "a": "b", # Comment inside object 1 + "b": "c" , "c": "d", # comment on object entry line + # Comment inside object 2 +"d": "e", +# Comment before closing object brace. +} # Comment on closing object brace. +a = {"a": "b", "c": "d"} +b = [1, 2, 3, 4] +c = [1, 2, +# Comment inside array +3, 4, +5, 6, 7, +8, +# Comment before nested composite. +[ +["foo"], # Comment inside nested composite. +["bar"], # Comment after last element in nested composite. +# Comment before nested composite closing bracket. +], # Comment on nested composite closing bracket. +# Comment before closing array bracket. +] # Comment on closing array bracket. + +d = [1 | b[_]] +e = [1 | split("foo.bar", ".", x); x[_]] +f = [1 | split("foo.bar", ".", x) +x[_]] +g = [1 | +split("foo.bar", ".", x) # comment in array comprehension +x[_] +# inner comment +] + +h = {1 | b[_]} +i = {1 | split("foo.bar", ".", x); x[_]} +j = {1 | split("foo.bar", ".", x) +x[_]} +k = {1 | +split("foo.bar", ".", x) # comment in set comprehension +x[_] + +# inner comment +} + +l = {"foo":1 | b[_]} +m = {y: +x | split("foo.bar", ".", x); y = x[_]} +n = {y: x | split("foo.bar", ".", x) +y = x[_]} +o = {y: x | +split("foo.bar", ".", x) # comment in object comprehension +y = x[_] + +# inner comment +} +} # Comment on rule closing brace + +nested_infix { + x + 1 + x = y + 2 + plus(x, 1, 2) + plus(x, 1) + y = f(x) + f(x, y) + y = x + 1 + 2 + x = y + # comment + z + x = (a + b) / 2 + f((a+b)/2) + y = q() +} + +expanded_const = true + +partial_obj["why"] = true { false } + +empty_sets { + set() + set() # comment at end of set +} + +vardecls { + some v1, v2,# c1 + v3,v4, # c2 + v5 # c3 +} + +declare1 := 1 + +declare2 := 2 { false } + +declare3 := {1,2,3} +declare4 := {4,5,6} + +union_object := {"response": (declare3|declare4)} + +union_set := {(declare3|declare4)} + +union_list := [(declare3|declare4)] + +union_set_2 := {(((declare3|declare4)))} + +union_object_multi_line := {"response": ( + declare3 | declare4 +)} + +union_object_key := {(declare3|declare4): "foo"} + +union_object_key_multi_line := {(declare3| +declare4): +"foo" +} + +# more comments! +# more comments! +# more comments! +# more comments! diff --git a/third_party/opa/v1/format/testfiles/v0/test.rego.error b/third_party/opa/v1/format/testfiles/v0/test.rego.error new file mode 100644 index 000000000000..77482680fbbd --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test.rego.error @@ -0,0 +1,26 @@ +# The blank lines below me should be gone! (except one) + + +# Comment! +package a.b + +# I also, am a comment. +import data.x.y.z +import data.a.b.c # Another comment! +# I belong with data.a, there should be a newline before me. +import data.a +import data.f.g + +default foo = false +foo[x] { +not x = g +} + +globals = {"foo": "bar", +"fizz": "buzz"} + +# Latent comment. + +r = y { + y = x + split("foo.bar", ".", input.x) with input as {"x": x} diff --git a/third_party/opa/v1/format/testfiles/v0/test.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test.rego.formatted new file mode 100644 index 000000000000..9f316c5e8587 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test.rego.formatted @@ -0,0 +1,243 @@ +# The blank lines below me should be gone! (except one) + +# Comment! +package a.b + +# I also, am a comment. +import data.a.b.c # Another comment! +import data.x.y.z + +# I belong with data.a, there should be a newline before me. +import data.a +import data.f.g + +default foo = false + +foo[x] { + not x = g + f(x) = 1 + g(x, "foo") = z +} + +globals = { + "foo": "bar", + "fizz": "buzz", +} + +partial_obj["x"] = 1 + +partial_obj["y"] = 2 + +partial_obj["z"] = 3 + +partial_set["x"] + +partial_set["y"] + +# Latent comment. + +r = y { + y = x + split("foo.bar", ".", input.x) with input as {"x": x} +} + +# Comment on else +else = y { + y = ["howdy"] + x = {"x": {"y": "z"}} + a = { + "a": {"b": "c"}, + "b": "c", "c": [ + 1, 2, + 3, 4, + ], + } +} + +fn(x) = y { + y = x +} + +fn_else(x) = 1 { + true +} # foo + +else = 2 { + # bar + + true +} else = 3 { + # baz + + false +} + +long(x) { + x = "foo %host" +} + +short(x) { + x = "bar" +} + +raw_string = `hi\there` + +raw_multiline = `this +string + is on +multiple lines` + +fn2( + [ + x, y, + z, + ], + {"foo": a}, +) = b { + split(x, y, c) + trim(a, z, d) # function comment 1 + split(c[0], d, b) + x = sprintf( + "hello %v", + ["world"], + ) + #function comment 2 +} # function comment 3 + +f[x] { + x = "hi" +} # Comment on chain + +f[x] { + x = "bye" +} + +import data.bar.foo # data.bar.foo should be first +import data.foo.bar + +p[x] = y { + y = x + y = "foo" + z = { + "a": "b", # Comment inside object 1 + "b": "c", "c": "d", # comment on object entry line + # Comment inside object 2 + "d": "e", + # Comment before closing object brace. + } # Comment on closing object brace. + a = {"a": "b", "c": "d"} + b = [1, 2, 3, 4] + c = [ + 1, 2, + # Comment inside array + 3, 4, + 5, 6, 7, + 8, + # Comment before nested composite. + [ + ["foo"], # Comment inside nested composite. + ["bar"], # Comment after last element in nested composite. + # Comment before nested composite closing bracket. + ], # Comment on nested composite closing bracket. + # Comment before closing array bracket. + ] # Comment on closing array bracket. + + d = [1 | b[_]] + e = [1 | split("foo.bar", ".", x); x[_]] + f = [1 | + split("foo.bar", ".", x) + x[_] + ] + g = [1 | + split("foo.bar", ".", x) # comment in array comprehension + x[_] + # inner comment + ] + + h = {1 | b[_]} + i = {1 | split("foo.bar", ".", x); x[_]} + j = {1 | + split("foo.bar", ".", x) + x[_] + } + k = {1 | + split("foo.bar", ".", x) # comment in set comprehension + x[_] + # inner comment + } + + l = {"foo": 1 | b[_]} + m = {y: x | + split("foo.bar", ".", x) + y = x[_] + } + n = {y: x | + split("foo.bar", ".", x) + y = x[_] + } + o = {y: x | + split("foo.bar", ".", x) # comment in object comprehension + y = x[_] + # inner comment + } +} # Comment on rule closing brace + +nested_infix { + x + 1 + x = y + 2 + 2 = x + 1 + x + 1 + y = f(x) + f(x, y) + y = (x + 1) + 2 + x = y + z # comment + x = (a + b) / 2 + f((a + b) / 2) + y = q() +} + +expanded_const = true + +partial_obj["why"] = true { + false +} + +empty_sets { + set() + set() # comment at end of set +} + +vardecls { + some v1, v2, # c1 + v3, v4, # c2 + v5 # c3 +} + +declare1 := 1 + +declare2 := 2 { + false +} + +declare3 := {1, 2, 3} + +declare4 := {4, 5, 6} + +union_object := {"response": (declare3 | declare4)} + +union_set := {(declare3 | declare4)} + +union_list := [(declare3 | declare4)] + +union_set_2 := {(declare3 | declare4)} + +union_object_multi_line := {"response": (declare3 | declare4)} + +union_object_key := {(declare3 | declare4): "foo"} + +union_object_key_multi_line := {(declare3 | declare4): "foo"} + +# more comments! +# more comments! +# more comments! +# more comments! diff --git a/third_party/opa/v1/format/testfiles/v0/test_assignments.rego b/third_party/opa/v1/format/testfiles/v0/test_assignments.rego new file mode 100644 index 000000000000..16b3710bb882 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_assignments.rego @@ -0,0 +1,24 @@ +package assignments + +# default value assignment +default a := 1 + +# rule +b := 2 + +# else keyword +c := 3 { + false +} else := 4 { + true +} + +# partial rule +d[msg] := 5 { + msg = [1, 2, 3][_] +} + +# function return value +e := f(6) + +f(x) := x diff --git a/third_party/opa/v1/format/testfiles/v0/test_assignments.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_assignments.rego.formatted new file mode 100644 index 000000000000..a3d1589bc518 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_assignments.rego.formatted @@ -0,0 +1,22 @@ +package assignments + +# default value assignment +default a := 1 + +# rule +b := 2 + +# else keyword +c := 3 { + false +} else := 4 + +# partial rule +d[msg] := 5 { + msg = [1, 2, 3][_] +} + +# function return value +e := f(6) + +f(x) := x diff --git a/third_party/opa/v1/format/testfiles/v0/test_contains.rego b/third_party/opa/v1/format/testfiles/v0/test_contains.rego new file mode 100644 index 000000000000..ba229ca97415 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_contains.rego @@ -0,0 +1,12 @@ +package test.contains +import future.keywords.contains + +p contains "foo" { true } + +deny contains msg { + msg := "foo" +} +deny[msg] {msg := "bar" } + +# partial objects unchanged +o[k] = v { k := "ok"; v := "nok" } diff --git a/third_party/opa/v1/format/testfiles/v0/test_contains.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_contains.rego.formatted new file mode 100644 index 000000000000..7b712fa9dba3 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_contains.rego.formatted @@ -0,0 +1,19 @@ +package test.contains + +import future.keywords.contains + +p contains "foo" + +deny contains msg { + msg := "foo" +} + +deny contains msg { + msg := "bar" +} + +# partial objects unchanged +o[k] = v { + k := "ok" + v := "nok" +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_contains.rego.formatted_no_keywords_in_refs b/third_party/opa/v1/format/testfiles/v0/test_contains.rego.formatted_no_keywords_in_refs new file mode 100644 index 000000000000..b372275212f4 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_contains.rego.formatted_no_keywords_in_refs @@ -0,0 +1,19 @@ +package test["contains"] + +import future.keywords.contains + +p contains "foo" + +deny contains msg { + msg := "foo" +} + +deny contains msg { + msg := "bar" +} + +# partial objects unchanged +o[k] = v { + k := "ok" + v := "nok" +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_contains_if.rego b/third_party/opa/v1/format/testfiles/v0/test_contains_if.rego new file mode 100644 index 000000000000..8506083c62d4 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_contains_if.rego @@ -0,0 +1,17 @@ +package test.if + +import future.keywords + +q[x] = y if { + y := 10 + x := "ten" +} + +q[x] = y { # not using if + y := 11 + x := "eleven" +} + +r[x] { # no if here before + x := "set" +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_contains_if.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_contains_if.rego.formatted new file mode 100644 index 000000000000..8170c70ec2cf --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_contains_if.rego.formatted @@ -0,0 +1,17 @@ +package test.if + +import future.keywords + +q[x] = y if { + y := 10 + x := "ten" +} + +q[x] = y if { # not using if + y := 11 + x := "eleven" +} + +r contains x if { # no if here before + x := "set" +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_end_of_rule_comment.rego b/third_party/opa/v1/format/testfiles/v0/test_end_of_rule_comment.rego new file mode 100644 index 000000000000..0c3bd75beebf --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_end_of_rule_comment.rego @@ -0,0 +1,7 @@ +package foo + +bar { + # before + input.bar + # after +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_end_of_rule_comment.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_end_of_rule_comment.rego.formatted new file mode 100644 index 000000000000..0c3bd75beebf --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_end_of_rule_comment.rego.formatted @@ -0,0 +1,7 @@ +package foo + +bar { + # before + input.bar + # after +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_every.rego b/third_party/opa/v1/format/testfiles/v0/test_every.rego new file mode 100644 index 000000000000..c8f0c1cd987f --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_every.rego @@ -0,0 +1,19 @@ +package p + +import future.keywords.every + +r { + every x in [1,3,5] { + is_odd(x) + } + + every x in [1,3,5] { is_odd(x); true } + + every x in [1,3,5] { + is_odd(x) + true + x < 10 + } +} + +is_odd(x) = x % 2 == 0 \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_every.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_every.rego.formatted new file mode 100644 index 000000000000..8e54b23eb46c --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_every.rego.formatted @@ -0,0 +1,19 @@ +package p + +import future.keywords.every + +r { + every x in [1, 3, 5] { + is_odd(x) + } + + every x in [1, 3, 5] { is_odd(x); true} + + every x in [1, 3, 5] { + is_odd(x) + true + x < 10 + } +} + +is_odd(x) = (x % 2) == 0 diff --git a/third_party/opa/v1/format/testfiles/v0/test_every_with_key.rego b/third_party/opa/v1/format/testfiles/v0/test_every_with_key.rego new file mode 100644 index 000000000000..b8b6cbd8c186 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_every_with_key.rego @@ -0,0 +1,21 @@ +package p + +import future.keywords.every + +r { + every i, x in [1,3,5] { + is_odd(x) + i < 10 + } + + every i, x in {"foo": 1, "bar": 3, "baz": 5} { is_odd(x); i != 20 } + + every i, x in [1,3,5] { + is_odd(x) + true + x < 10 + x > i + } +} + +is_odd(x) = x % 2 == 0 \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_every_with_key.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_every_with_key.rego.formatted new file mode 100644 index 000000000000..2e8e3cfecfad --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_every_with_key.rego.formatted @@ -0,0 +1,21 @@ +package p + +import future.keywords.every + +r { + every i, x in [1, 3, 5] { + is_odd(x) + i < 10 + } + + every i, x in {"foo": 1, "bar": 3, "baz": 5} { is_odd(x); i != 20} + + every i, x in [1, 3, 5] { + is_odd(x) + true + x < 10 + x > i + } +} + +is_odd(x) = (x % 2) == 0 diff --git a/third_party/opa/v1/format/testfiles/v0/test_fun_args_with_linebreaks.rego b/third_party/opa/v1/format/testfiles/v0/test_fun_args_with_linebreaks.rego new file mode 100644 index 000000000000..332dbb427392 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_fun_args_with_linebreaks.rego @@ -0,0 +1,16 @@ +package test + +p { + x := count( + [1, 2, 3] # four + ) + y := concat( + "/", + ["foo", "bar"], + ) + z := concat("/", + [ + "foo", + "bar", + ]) +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_fun_args_with_linebreaks.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_fun_args_with_linebreaks.rego.formatted new file mode 100644 index 000000000000..fce99c25cd47 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_fun_args_with_linebreaks.rego.formatted @@ -0,0 +1,16 @@ +package test + +p { + x := count([1, 2, 3]) # four + y := concat( + "/", + ["foo", "bar"], + ) + z := concat( + "/", + [ + "foo", + "bar", + ], + ) +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_functions.rego b/third_party/opa/v1/format/testfiles/v0/test_functions.rego new file mode 100644 index 000000000000..e1278daade1f --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_functions.rego @@ -0,0 +1,48 @@ +package p + +f1(x) = x + +f2(x) := x + +f3(1) + +f4(1) { + true +} + +f5(1) := x { + x := 5 +} + +f6(x) { + true +} else := false + +f7(x) := 1 { + x.key1 +} else := false { + x.key2 +} else { + false +} + +f(x) = 1 { + input.x == x +} { + input.x < 10 +} + +f(_) { + input.x +} { + input.y +} + +# Non-functions +foo[x] = y { + x = 1 + y = 2 +} { + x = 3 + y = 4 +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_functions.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_functions.rego.formatted new file mode 100644 index 000000000000..c8900b5488e0 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_functions.rego.formatted @@ -0,0 +1,52 @@ +package p + +f1(x) = x + +f2(x) := x + +f3(1) + +f4(1) = true + +f5(1) := x { + x := 5 +} + +f6(x) { + true +} else := false + +f7(x) := 1 { + x.key1 +} else := false { + x.key2 +} else { + false +} + +f(x) = 1 { + input.x == x +} + +f(x) = 1 { + input.x < 10 +} + +f(_) { + input.x +} + +f(_) { + input.y +} + +# Non-functions +foo[x] = y { + x = 1 + y = 2 +} + +foo[x] = y { + x = 3 + y = 4 +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_if.rego b/third_party/opa/v1/format/testfiles/v0/test_if.rego new file mode 100644 index 000000000000..5f7dca32b676 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_if.rego @@ -0,0 +1,32 @@ +package test.if + +import future.keywords.if + +p if 1 > 0 # shorthand + +p if { 1 > 0 } # longhand one line + +p if { + 1 > 0 # longhand two lines +} + +# same without the comment +p if { + 1 > 0 +} + +p if { # comment one + 1 > 0 # comment two +} + +q[x] = y if { + y := 10 + x := "ten" +} + +q[x] = y { # not using if + y := 11 + x := "eleven" +} + +r[x] { x := "set" } # no if here before diff --git a/third_party/opa/v1/format/testfiles/v0/test_if.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_if.rego.formatted new file mode 100644 index 000000000000..b657f105fd0c --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_if.rego.formatted @@ -0,0 +1,34 @@ +package test.if + +import future.keywords.if + +p if 1 > 0 # shorthand + +p if 1 > 0 # longhand one line + +p if { + 1 > 0 # longhand two lines +} + +# same without the comment +p if { + 1 > 0 +} + +p if { # comment one + 1 > 0 # comment two +} + +q[x] = y if { + y := 10 + x := "ten" +} + +q[x] = y if { # not using if + y := 11 + x := "eleven" +} + +r[x] { # no if here before + x := "set" +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_if.rego.formatted_no_keywords_in_refs b/third_party/opa/v1/format/testfiles/v0/test_if.rego.formatted_no_keywords_in_refs new file mode 100644 index 000000000000..cae30b526c6e --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_if.rego.formatted_no_keywords_in_refs @@ -0,0 +1,34 @@ +package test["if"] + +import future.keywords.if + +p if 1 > 0 # shorthand + +p if 1 > 0 # longhand one line + +p if { + 1 > 0 # longhand two lines +} + +# same without the comment +p if { + 1 > 0 +} + +p if { # comment one + 1 > 0 # comment two +} + +q[x] = y if { + y := 10 + x := "ten" +} + +q[x] = y if { # not using if + y := 11 + x := "eleven" +} + +r[x] { # no if here before + x := "set" +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_if_else.rego b/third_party/opa/v1/format/testfiles/v0/test_if_else.rego new file mode 100644 index 000000000000..8dabf52e2c55 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_if_else.rego @@ -0,0 +1,20 @@ +package test.if + +import future.keywords.if + +p := 1 if { 1 > 0 } +else := 2 + +q := 1 if { 1 > 0 } else := 2 { 2 > 1 } + +q := 1 if { + 1 > 0 + 2 > 1 +} else := 2 { 2 > 1 } + +r := 1 if { + 1 > 0 +} +else := 2 { + 2 > 1 +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_if_else.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_if_else.rego.formatted new file mode 100644 index 000000000000..76aeb5639e66 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_if_else.rego.formatted @@ -0,0 +1,24 @@ +package test.if + +import future.keywords.if + +p := 1 if 1 > 0 + +else := 2 + +q := 1 if 1 > 0 + +else := 2 if 2 > 1 + +q := 1 if { + 1 > 0 + 2 > 1 +} else := 2 if 2 > 1 + +r := 1 if { + 1 > 0 +} + +else := 2 if { + 2 > 1 +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_if_else.rego.formatted_no_keywords_in_refs b/third_party/opa/v1/format/testfiles/v0/test_if_else.rego.formatted_no_keywords_in_refs new file mode 100644 index 000000000000..12880d2cfb1b --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_if_else.rego.formatted_no_keywords_in_refs @@ -0,0 +1,24 @@ +package test["if"] + +import future.keywords.if + +p := 1 if 1 > 0 + +else := 2 + +q := 1 if 1 > 0 + +else := 2 if 2 > 1 + +q := 1 if { + 1 > 0 + 2 > 1 +} else := 2 if 2 > 1 + +r := 1 if { + 1 > 0 +} + +else := 2 if { + 2 > 1 +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_in.rego b/third_party/opa/v1/format/testfiles/v0/test_in.rego new file mode 100644 index 000000000000..63be7918b144 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_in.rego @@ -0,0 +1,13 @@ +package test.in + +import future.keywords.in + +a["in"] := "foo" + +b.c["in"] := "bar" + +c["in"].d := "baz" + +p { + input["in"] +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_in.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_in.rego.formatted new file mode 100644 index 000000000000..63be7918b144 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_in.rego.formatted @@ -0,0 +1,13 @@ +package test.in + +import future.keywords.in + +a["in"] := "foo" + +b.c["in"] := "bar" + +c["in"].d := "baz" + +p { + input["in"] +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_in.rego.formatted_no_keywords_in_refs b/third_party/opa/v1/format/testfiles/v0/test_in.rego.formatted_no_keywords_in_refs new file mode 100644 index 000000000000..8d4dec17f886 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_in.rego.formatted_no_keywords_in_refs @@ -0,0 +1,13 @@ +package test["in"] + +import future.keywords.in + +a["in"] := "foo" + +b.c["in"] := "bar" + +c["in"].d := "baz" + +p { + input["in"] +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_in_operator_with_all_keywords_import.rego b/third_party/opa/v1/format/testfiles/v0/test_in_operator_with_all_keywords_import.rego new file mode 100644 index 000000000000..3c7286a94796 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_in_operator_with_all_keywords_import.rego @@ -0,0 +1,9 @@ +package p + +import input.foo +import future.keywords + +r { + internal.member_2(1, [1]) + internal.member_3(0, 1, [1]) +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_in_operator_with_all_keywords_import.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_in_operator_with_all_keywords_import.rego.formatted new file mode 100644 index 000000000000..2841f604c05b --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_in_operator_with_all_keywords_import.rego.formatted @@ -0,0 +1,9 @@ +package p + +import future.keywords +import input.foo + +r if { + 1 in [1] + 0, 1 in [1] +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_in_operator_with_parenthesis.rego b/third_party/opa/v1/format/testfiles/v0/test_in_operator_with_parenthesis.rego new file mode 100644 index 000000000000..5e32bab83b60 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_in_operator_with_parenthesis.rego @@ -0,0 +1,19 @@ +package test + +import future.keywords.in + +z { + { 1, 2 in [2, 2, 2] } + { 1, (1, 2 in [2, 2, 2]) } + { (x, x in [2]) | x := numbers.range(1,10)[_]} + { x: (x, x in [2]) | x := numbers.range(1,10)[_]} + { (x in [2]): 2 | x := numbers.range(1,10)[_]} + { (x, x in [2]): 2 | x := numbers.range(1,10)[_]} + { (1, 2 in [2, 2, 2]) } + { 1, (2 in [2, 2, 2]) } + f(1, 2 in [2, 2]) + g((1, 2 in [2, 2])) +} + +f(_, _) = true +g(_) = true \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_in_operator_with_parenthesis.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_in_operator_with_parenthesis.rego.formatted new file mode 100644 index 000000000000..9337d9cd10f5 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_in_operator_with_parenthesis.rego.formatted @@ -0,0 +1,20 @@ +package test + +import future.keywords.in + +z { + {1, 2 in [2, 2, 2]} + {1, (1, 2 in [2, 2, 2])} + {(x, x in [2]) | x := numbers.range(1, 10)[_]} + {x: (x, x in [2]) | x := numbers.range(1, 10)[_]} + {x in [2]: 2 | x := numbers.range(1, 10)[_]} + {(x, x in [2]): 2 | x := numbers.range(1, 10)[_]} + {(1, 2 in [2, 2, 2])} + {1, 2 in [2, 2, 2]} + f(1, 2 in [2, 2]) + g((1, 2 in [2, 2])) +} + +f(_, _) = true + +g(_) = true diff --git a/third_party/opa/v1/format/testfiles/v0/test_in_operator_without_import.rego b/third_party/opa/v1/format/testfiles/v0/test_in_operator_without_import.rego new file mode 100644 index 000000000000..d467074aebe8 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_in_operator_without_import.rego @@ -0,0 +1,8 @@ +package p + +import input.foo + +r { + internal.member_2(1, [1]) + internal.member_3(0, 1, [1]) +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_in_operator_without_import.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_in_operator_without_import.rego.formatted new file mode 100644 index 000000000000..040c28dfdb7d --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_in_operator_without_import.rego.formatted @@ -0,0 +1,10 @@ +package p + +import future.keywords.in + +import input.foo + +r { + 1 in [1] + 0, 1 in [1] +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_1560.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_1560.rego new file mode 100644 index 000000000000..45cbab02a812 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_1560.rego @@ -0,0 +1,29 @@ +package x + +p { + symbol + + # comment + some x +} + +p { + symbol + + # comment + f(x) +} + +p { + symbol + + # comment + not x +} + +p { + symbol + + # comment + not f(x) +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_1560.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_1560.rego.formatted new file mode 100644 index 000000000000..69aba4e01e61 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_1560.rego.formatted @@ -0,0 +1,29 @@ +package x + +p { + symbol + + # comment + some x +} + +p { + symbol + + # comment + f(x) +} + +p { + symbol + + # comment + not x +} + +p { + symbol + + # comment + not f(x) +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_2299.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_2299.rego new file mode 100644 index 000000000000..5af69145d9ed --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_2299.rego @@ -0,0 +1,40 @@ +package foo + +# Compact else cases +authorize = "allow" { + input.user == "superuser" +} else = "deny" { + input.path[0] == "admin" + input.source_network == "external" +} + +# Newline separated else blocks +q = x { + foo == "bar" +} + +else = y { + foo == "baz" +} + +else = z { + foo == "qux" +} + + +# Mixed compact and newline separated +p = x { + foo == "bar" +} +# some special case +# with lots of comments +# describing it +else = y { + bar == "foo" +} else = z { + bar == "bar" +} + +else { + bar == "baz" +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_2299.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_2299.rego.formatted new file mode 100644 index 000000000000..46681a9fe19f --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_2299.rego.formatted @@ -0,0 +1,40 @@ +package foo + +# Compact else cases +authorize = "allow" { + input.user == "superuser" +} else = "deny" { + input.path[0] == "admin" + input.source_network == "external" +} + +# Newline separated else blocks +q = x { + foo == "bar" +} + +else = y { + foo == "baz" +} + +else = z { + foo == "qux" +} + +# Mixed compact and newline separated +p = x { + foo == "bar" +} + +# some special case +# with lots of comments +# describing it +else = y { + bar == "foo" +} else = z { + bar == "bar" +} + +else { + bar == "baz" +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_2420.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_2420.rego new file mode 100644 index 000000000000..f5bab2c0e677 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_2420.rego @@ -0,0 +1,11 @@ +package example + +allow { + some_condition +} + +# some comments + +else { + another_condition +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_2420.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_2420.rego.formatted new file mode 100644 index 000000000000..4684a00ab2e6 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_2420.rego.formatted @@ -0,0 +1,11 @@ +package example + +allow { + some_condition +} + +# some comments + +else { + another_condition +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_3836.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_3836.rego new file mode 100644 index 000000000000..d42c88ae9636 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_3836.rego @@ -0,0 +1,6 @@ +package testcase + +rule1 = contains( + "", # first comment + "", # second comment +) \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_3836.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_3836.rego.formatted new file mode 100644 index 000000000000..4e0ae20b31a1 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_3836.rego.formatted @@ -0,0 +1,6 @@ +package testcase + +rule1 = contains( + "", # first comment + "", # second comment +) diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_3849.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_3849.rego new file mode 100644 index 000000000000..4ebfeb60ce73 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_3849.rego @@ -0,0 +1,33 @@ +package test_issue_3849 + +test_require_context { + require_context("monkey", "eat", "banana") with input as { + "principal": {"id": 101, "type": "monkey"}, + "action": "eat", + "entity": {"id": 102, "type": "banana"}, + } +} + +test_contrived { + allow with input as { + "a": 101, + "b": 101, + "z": 101, + "y": 101, + "x": 101, + "w": 101, + "v": 101, + "u": 101, + "t": 101, + "s": 101, + "r": 101, + "q": 101, + "p": 101, + "o": 101, + "n": 101, + "j": 101, + "k": 101, + "l": 101, + "m": 101, + } +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_3849.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_3849.rego.formatted new file mode 100644 index 000000000000..d3194bbe6a3b --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_3849.rego.formatted @@ -0,0 +1,33 @@ +package test_issue_3849 + +test_require_context { + require_context("monkey", "eat", "banana") with input as { + "principal": {"id": 101, "type": "monkey"}, + "action": "eat", + "entity": {"id": 102, "type": "banana"}, + } +} + +test_contrived { + allow with input as { + "a": 101, + "b": 101, + "z": 101, + "y": 101, + "x": 101, + "w": 101, + "v": 101, + "u": 101, + "t": 101, + "s": 101, + "r": 101, + "q": 101, + "p": 101, + "o": 101, + "n": 101, + "j": 101, + "k": 101, + "l": 101, + "m": 101, + } +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_4606.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_4606.rego new file mode 100644 index 000000000000..a71142ed97b3 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_4606.rego @@ -0,0 +1,6 @@ +package p + +# +import future.keywords.every + +r { 1 in [] } \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_4606.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_4606.rego.formatted new file mode 100644 index 000000000000..ce00ead8c986 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_4606.rego.formatted @@ -0,0 +1,8 @@ +package p + +# +import future.keywords.every + +r { + 1 in [] +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5348.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_5348.rego new file mode 100644 index 000000000000..5ec68f16cd2b --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5348.rego @@ -0,0 +1,5 @@ +package p + +f(x) { + true +} else := false diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5348.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_5348.rego.formatted new file mode 100644 index 000000000000..5ec68f16cd2b --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5348.rego.formatted @@ -0,0 +1,5 @@ +package p + +f(x) { + true +} else := false diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5449.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_5449.rego new file mode 100644 index 000000000000..0ed8f6599062 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5449.rego @@ -0,0 +1,3 @@ +package demo + +foo["bar"] = "baz" { input } diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5449.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_5449.rego.formatted new file mode 100644 index 000000000000..1e0452fe09f5 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5449.rego.formatted @@ -0,0 +1,5 @@ +package demo + +foo["bar"] = "baz" { + input +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_contains_ref_rule.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_contains_ref_rule.rego new file mode 100644 index 000000000000..b2223c007ff7 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_contains_ref_rule.rego @@ -0,0 +1,9 @@ +# This is the same as test_issue_5449.rego, but with another rule +# that gives the formatter the assurance that using ref rules is OK +package demo + +import future.keywords.contains + +foo["bar"] = "baz" { input } + +a.deep contains "ref" diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_contains_ref_rule.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_contains_ref_rule.rego.formatted new file mode 100644 index 000000000000..37794d61a040 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_contains_ref_rule.rego.formatted @@ -0,0 +1,11 @@ +# This is the same as test_issue_5449.rego, but with another rule +# that gives the formatter the assurance that using ref rules is OK +package demo + +import future.keywords.contains + +foo.bar = "baz" { + input +} + +a.deep contains "ref" diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_ref_rule.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_ref_rule.rego new file mode 100644 index 000000000000..642767191a3b --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_ref_rule.rego @@ -0,0 +1,7 @@ +# This is the same as test_issue_5449.rego, but with a rule that gives +# the formatter the assurance that using ref rules is OK +package demo + +foo["bar"] = "baz" { input } + +a.deep.ref := true diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_ref_rule.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_ref_rule.rego.formatted new file mode 100644 index 000000000000..35f425701a0f --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5449_with_ref_rule.rego.formatted @@ -0,0 +1,9 @@ +# This is the same as test_issue_5449.rego, but with a rule that gives +# the formatter the assurance that using ref rules is OK +package demo + +foo.bar = "baz" { + input +} + +a.deep.ref := true diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_comprehension.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_comprehension.rego new file mode 100644 index 000000000000..abe0330eae62 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_comprehension.rego @@ -0,0 +1,3 @@ +package p + +array := [(input.thing[i] == input.other[i]) | true] diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_comprehension.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_comprehension.rego.formatted new file mode 100644 index 000000000000..abe0330eae62 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_comprehension.rego.formatted @@ -0,0 +1,3 @@ +package p + +array := [(input.thing[i] == input.other[i]) | true] diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_ref.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_ref.rego new file mode 100644 index 000000000000..1e599843e22f --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_ref.rego @@ -0,0 +1,8 @@ +package p + +first := {"one", "two"} +second := {"two", "three"} + +example[msg] { + msg := (first | second)[_] +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_ref.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_ref.rego.formatted new file mode 100644 index 000000000000..789bb9805da4 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5537_with_ref.rego.formatted @@ -0,0 +1,9 @@ +package p + +first := {"one", "two"} + +second := {"two", "three"} + +example[msg] { + msg := (first | second)[_] +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5798.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_5798.rego new file mode 100644 index 000000000000..f4551d7ea137 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5798.rego @@ -0,0 +1,9 @@ +package test + +rule01 = fail +{ + fail = { # this + x | # panics + set[x]; f(x) + } +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_5798.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_5798.rego.formatted new file mode 100644 index 000000000000..90b45a79e060 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_5798.rego.formatted @@ -0,0 +1,9 @@ +package test + +rule01 = fail { + fail = { # this + x | # panics + set[x] + f(x) + } +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_6161.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_6161.rego new file mode 100644 index 000000000000..8f2839b42c7b --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_6161.rego @@ -0,0 +1,8 @@ +package p + +# this is a comment with trailing whitespace + +allow { + # another comment with trailing whitespace + 1 == 1 +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_6161.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_6161.rego.formatted new file mode 100644 index 000000000000..9f78be7b0516 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_6161.rego.formatted @@ -0,0 +1,8 @@ +package p + +# this is a comment with trailing whitespace + +allow { + # another comment with trailing whitespace + 1 == 1 +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_6330.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_6330.rego new file mode 100644 index 000000000000..c10f47636362 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_6330.rego @@ -0,0 +1,45 @@ +package a + +value := {"a": +{"b": +{"c": +"d"}}} + +value := {"a": # test 1 +"b"} # test 2 + +value := {"a": # test 1 +"b"} + +value := {"a": +{"b": +{"c": +"d"}}} + +value := {"a": # this is +{"b": # my ridiculous +{"c": # way of +"d"}}} # commenting code + +value := {"a": +{"b": +{"c": +"d"}}} + +p[ +{"a": # +"b"} # +] := true + +p.foo.bar[ +{"a": # +"b"} # +] := true + +p[ +{"a": # +"b"} # +][ +{"c": # +"d"} # +] := true \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_6330.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_6330.rego.formatted new file mode 100644 index 000000000000..0209af21c7d4 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_6330.rego.formatted @@ -0,0 +1,31 @@ +package a + +value := {"a": {"b": {"c": "d"}}} + +value := {"a": # test 1 +"b"} # test 2 + +value := {"a": "b"} # test 1 + +value := {"a": {"b": {"c": "d"}}} + +value := {"a": # this is +{"b": # my ridiculous +{"c": # way of +"d"}}} # commenting code + +value := {"a": {"b": {"c": "d"}}} + +p[{"a": # +"b"} # +] := true + +p.foo.bar[{"a": # +"b"} # +] := true + +p[{"a": # +"b"} # +][{"c": # +"d"} # +] := true diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_6330_1.rego b/third_party/opa/v1/format/testfiles/v0/test_issue_6330_1.rego new file mode 100644 index 000000000000..996cf400dc16 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_6330_1.rego @@ -0,0 +1,6 @@ +package a + +p[ +{"a": # +"b"} # +] := true \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_issue_6330_1.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_issue_6330_1.rego.formatted new file mode 100644 index 000000000000..e185363e6f91 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_issue_6330_1.rego.formatted @@ -0,0 +1,6 @@ +package a + +p[ +{"a": # +"b"} # +] := true diff --git a/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs.rego b/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs.rego new file mode 100644 index 000000000000..f3c0f0bd1252 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs.rego @@ -0,0 +1,171 @@ +package test.not.package.import.as.default.else.with.null.true.false.some + +import data.not as foo1 +import data.package as foo2 +import data.import as foo3 +import data.as as foo4 +import data.default as foo5 +import data.else as foo6 +import data.with as foo7 +import data.null as foo8 +import data.true as foo9 +import data.false as foo10 +import data.some as foo11 + +import data.not.bar1 +import data.package.bar2 +import data.import.bar3 +import data.as.bar4 +import data.default.bar5 +import data.else.bar6 +import data.with.bar7 +import data.null.bar8 +import data.true.bar9 +import data.false.bar10 +import data.some.bar11 + +p { + a.not == 1 + a.package == 1 + a.import == 1 + a.as == 1 + a.default == 1 + a.else == 1 + a.with == 1 + a.null == 1 + a.true == 1 + a.false == 1 + a.some == 1 + + b.c.not == 1 + b.c.package == 1 + b.c.import == 1 + b.c.as == 1 + b.c.default == 1 + b.c.else == 1 + b.c.with == 1 + b.c.null == 1 + b.c.true == 1 + b.c.false == 1 + b.c.some == 1 + + d.not.e == 1 + d.package.e == 1 + d.import.e == 1 + d.as.e == 1 + d.default.e == 1 + d.else.e == 1 + d.with.e == 1 + d.null.e == 1 + d.true.e == 1 + d.false.e == 1 + d.some.e == 1 + + f.not(2) + f.package(2) + f.import(2) + f.as(2) + f.default(2) + f.else(2) + f.with(2) + f.null(2) + f.true(2) + f.false(2) + f.some(2) + + g.h.not(2) + g.h.package(2) + g.h.import(2) + g.h.as(2) + g.h.default(2) + g.h.else(2) + g.h.with(2) + g.h.null(2) + g.h.true(2) + g.h.false(2) + g.h.some(2) + + i.not.j(2) + i.package.j(2) + i.import.j(2) + i.as.j(2) + i.default.j(2) + i.else.j(2) + i.with.j(2) + i.null.j(2) + i.true.j(2) + i.false.j(2) + i.some.j(2) +} + +a.not := 1 +a.package := 1 +a.import := 1 +a.as := 1 +a.default := 1 +a.else := 1 +a.with := 1 +a.null := 1 +a.true := 1 +a.false := 1 +a.some := 1 + +b.c.not := 1 +b.c.package := 1 +b.c.import := 1 +b.c.as := 1 +b.c.default := 1 +b.c.else := 1 +b.c.with := 1 +b.c.null := 1 +b.c.true := 1 +b.c.false := 1 +b.c.some := 1 + +d.not.e := 1 +d.package.e := 1 +d.import.e := 1 +d.as.e := 1 +d.default.e := 1 +d.else.e := 1 +d.with.e := 1 +d.null.e := 1 +d.true.e := 1 +d.false.e := 1 +d.some.e := 1 + +f.not(x) := x +f.package(x) := x +f.import(x) := x +f.as(x) := x +f.default(x) := x +f.else(x) := x +f.with(x) := x +f.null(x) := x +f.true(x) := x +f.false(x) := x +f.some(x) := x + +g.h.not(x) := x +g.h.package(x) := x +g.h.import(x) := x +g.h.as(x) := x +g.h.default(x) := x +g.h.else(x) := x +g.h.with(x) := x +g.h.null(x) := x +g.h.true(x) := x +g.h.false(x) := x +g.h.some(x) := x + +i.not.j(x) := x +i.package.j(x) := x +i.import.j(x) := x +i.as.j(x) := x +i.default.j(x) := x +i.else.j(x) := x +i.with.j(x) := x +i.null.j(x) := x +i.true.j(x) := x +i.false.j(x) := x +i.some.j(x) := x diff --git a/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs.rego.formatted new file mode 100644 index 000000000000..66b2232332f9 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs.rego.formatted @@ -0,0 +1,231 @@ +package test.not.package.import.as.default.else.with.null.true.false.some + +import data.as as foo4 +import data.default as foo5 +import data.else as foo6 +import data.false as foo10 +import data.import as foo3 +import data.not as foo1 +import data.null as foo8 +import data.package as foo2 +import data.some as foo11 +import data.true as foo9 +import data.with as foo7 + +import data.as.bar4 +import data.default.bar5 +import data.else.bar6 +import data.false.bar10 +import data.import.bar3 +import data.not.bar1 +import data.null.bar8 +import data.package.bar2 +import data.some.bar11 +import data.true.bar9 +import data.with.bar7 + +p { + a.not == 1 + a.package == 1 + a.import == 1 + a.as == 1 + a.default == 1 + a.else == 1 + a.with == 1 + a.null == 1 + a.true == 1 + a.false == 1 + a.some == 1 + + b.c.not == 1 + b.c.package == 1 + b.c.import == 1 + b.c.as == 1 + b.c.default == 1 + b.c.else == 1 + b.c.with == 1 + b.c.null == 1 + b.c.true == 1 + b.c.false == 1 + b.c.some == 1 + + d.not.e == 1 + d.package.e == 1 + d.import.e == 1 + d.as.e == 1 + d.default.e == 1 + d.else.e == 1 + d.with.e == 1 + d.null.e == 1 + d.true.e == 1 + d.false.e == 1 + d.some.e == 1 + + f.not(2) + f.package(2) + f.import(2) + f.as(2) + f.default(2) + f.else(2) + f.with(2) + f.null(2) + f.true(2) + f.false(2) + f.some(2) + + g.h.not(2) + g.h.package(2) + g.h.import(2) + g.h.as(2) + g.h.default(2) + g.h.else(2) + g.h.with(2) + g.h.null(2) + g.h.true(2) + g.h.false(2) + g.h.some(2) + + i.not.j(2) + i.package.j(2) + i.import.j(2) + i.as.j(2) + i.default.j(2) + i.else.j(2) + i.with.j(2) + i.null.j(2) + i.true.j(2) + i.false.j(2) + i.some.j(2) +} + +a.not := 1 + +a.package := 1 + +a.import := 1 + +a.as := 1 + +a.default := 1 + +a.else := 1 + +a.with := 1 + +a.null := 1 + +a.true := 1 + +a.false := 1 + +a.some := 1 + +b.c.not := 1 + +b.c.package := 1 + +b.c.import := 1 + +b.c.as := 1 + +b.c.default := 1 + +b.c.else := 1 + +b.c.with := 1 + +b.c.null := 1 + +b.c.true := 1 + +b.c.false := 1 + +b.c.some := 1 + +d.not.e := 1 + +d.package.e := 1 + +d.import.e := 1 + +d.as.e := 1 + +d.default.e := 1 + +d.else.e := 1 + +d.with.e := 1 + +d.null.e := 1 + +d.true.e := 1 + +d.false.e := 1 + +d.some.e := 1 + +f.not(x) := x + +f.package(x) := x + +f.import(x) := x + +f.as(x) := x + +f.default(x) := x + +f.else(x) := x + +f.with(x) := x + +f.null(x) := x + +f.true(x) := x + +f.false(x) := x + +f.some(x) := x + +g.h.not(x) := x + +g.h.package(x) := x + +g.h.import(x) := x + +g.h.as(x) := x + +g.h.default(x) := x + +g.h.else(x) := x + +g.h.with(x) := x + +g.h.null(x) := x + +g.h.true(x) := x + +g.h.false(x) := x + +g.h.some(x) := x + +i.not.j(x) := x + +i.package.j(x) := x + +i.import.j(x) := x + +i.as.j(x) := x + +i.default.j(x) := x + +i.else.j(x) := x + +i.with.j(x) := x + +i.null.j(x) := x + +i.true.j(x) := x + +i.false.j(x) := x + +i.some.j(x) := x diff --git a/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs.rego.formatted_no_keywords_in_refs b/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs.rego.formatted_no_keywords_in_refs new file mode 100644 index 000000000000..09e4444f91d5 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs.rego.formatted_no_keywords_in_refs @@ -0,0 +1,231 @@ +package test["not"]["package"]["import"]["as"]["default"]["else"]["with"]["null"]["true"]["false"]["some"] + +import data["as"] as foo4 +import data["default"] as foo5 +import data["else"] as foo6 +import data["false"] as foo10 +import data["import"] as foo3 +import data["not"] as foo1 +import data["null"] as foo8 +import data["package"] as foo2 +import data["some"] as foo11 +import data["true"] as foo9 +import data["with"] as foo7 + +import data["as"].bar4 +import data["default"].bar5 +import data["else"].bar6 +import data["false"].bar10 +import data["import"].bar3 +import data["not"].bar1 +import data["null"].bar8 +import data["package"].bar2 +import data["some"].bar11 +import data["true"].bar9 +import data["with"].bar7 + +p { + a["not"] == 1 + a["package"] == 1 + a["import"] == 1 + a["as"] == 1 + a["default"] == 1 + a["else"] == 1 + a["with"] == 1 + a["null"] == 1 + a["true"] == 1 + a["false"] == 1 + a["some"] == 1 + + b.c["not"] == 1 + b.c["package"] == 1 + b.c["import"] == 1 + b.c["as"] == 1 + b.c["default"] == 1 + b.c["else"] == 1 + b.c["with"] == 1 + b.c["null"] == 1 + b.c["true"] == 1 + b.c["false"] == 1 + b.c["some"] == 1 + + d["not"].e == 1 + d["package"].e == 1 + d["import"].e == 1 + d["as"].e == 1 + d["default"].e == 1 + d["else"].e == 1 + d["with"].e == 1 + d["null"].e == 1 + d["true"].e == 1 + d["false"].e == 1 + d["some"].e == 1 + + f["not"](2) + f["package"](2) + f["import"](2) + f["as"](2) + f["default"](2) + f["else"](2) + f["with"](2) + f["null"](2) + f["true"](2) + f["false"](2) + f["some"](2) + + g.h["not"](2) + g.h["package"](2) + g.h["import"](2) + g.h["as"](2) + g.h["default"](2) + g.h["else"](2) + g.h["with"](2) + g.h["null"](2) + g.h["true"](2) + g.h["false"](2) + g.h["some"](2) + + i["not"].j(2) + i["package"].j(2) + i["import"].j(2) + i["as"].j(2) + i["default"].j(2) + i["else"].j(2) + i["with"].j(2) + i["null"].j(2) + i["true"].j(2) + i["false"].j(2) + i["some"].j(2) +} + +a["not"] := 1 + +a["package"] := 1 + +a["import"] := 1 + +a["as"] := 1 + +a["default"] := 1 + +a["else"] := 1 + +a["with"] := 1 + +a["null"] := 1 + +a["true"] := 1 + +a["false"] := 1 + +a["some"] := 1 + +b.c["not"] := 1 + +b.c["package"] := 1 + +b.c["import"] := 1 + +b.c["as"] := 1 + +b.c["default"] := 1 + +b.c["else"] := 1 + +b.c["with"] := 1 + +b.c["null"] := 1 + +b.c["true"] := 1 + +b.c["false"] := 1 + +b.c["some"] := 1 + +d["not"].e := 1 + +d["package"].e := 1 + +d["import"].e := 1 + +d["as"].e := 1 + +d["default"].e := 1 + +d["else"].e := 1 + +d["with"].e := 1 + +d["null"].e := 1 + +d["true"].e := 1 + +d["false"].e := 1 + +d["some"].e := 1 + +f["not"](x) := x + +f["package"](x) := x + +f["import"](x) := x + +f["as"](x) := x + +f["default"](x) := x + +f["else"](x) := x + +f["with"](x) := x + +f["null"](x) := x + +f["true"](x) := x + +f["false"](x) := x + +f["some"](x) := x + +g.h["not"](x) := x + +g.h["package"](x) := x + +g.h["import"](x) := x + +g.h["as"](x) := x + +g.h["default"](x) := x + +g.h["else"](x) := x + +g.h["with"](x) := x + +g.h["null"](x) := x + +g.h["true"](x) := x + +g.h["false"](x) := x + +g.h["some"](x) := x + +i["not"].j(x) := x + +i["package"].j(x) := x + +i["import"].j(x) := x + +i["as"].j(x) := x + +i["default"].j(x) := x + +i["else"].j(x) := x + +i["with"].j(x) := x + +i["null"].j(x) := x + +i["true"].j(x) := x + +i["false"].j(x) := x + +i["some"].j(x) := x diff --git a/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs_keep_brackets.rego b/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs_keep_brackets.rego new file mode 100644 index 000000000000..7d97534a76d9 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs_keep_brackets.rego @@ -0,0 +1,171 @@ +package test["not"]["package"]["import"]["as"]["default"]["else"]["with"]["null"]["true"]["false"]["some"] + +import data["not"] as foo1 +import data["package"] as foo2 +import data["import"] as foo3 +import data["as"] as foo4 +import data["default"] as foo5 +import data["else"] as foo6 +import data["with"] as foo7 +import data["null"] as foo8 +import data["true"] as foo9 +import data["false"] as foo10 +import data["some"] as foo11 + +import data["not"].bar1 +import data["package"].bar2 +import data["import"].bar3 +import data["as"].bar4 +import data["default"].bar5 +import data["else"].bar6 +import data["with"].bar7 +import data["null"].bar8 +import data["true"].bar9 +import data["false"].bar10 +import data["some"].bar11 + +p { + a["not"] == 1 + a["package"] == 1 + a["import"] == 1 + a["as"] == 1 + a["default"] == 1 + a["else"] == 1 + a["with"] == 1 + a["null"] == 1 + a["true"] == 1 + a["false"] == 1 + a["some"] == 1 + + b.c["not"] == 1 + b.c["package"] == 1 + b.c["import"] == 1 + b.c["as"] == 1 + b.c["default"] == 1 + b.c["else"] == 1 + b.c["with"] == 1 + b.c["null"] == 1 + b.c["true"] == 1 + b.c["false"] == 1 + b.c["some"] == 1 + + d["not"].e == 1 + d["package"].e == 1 + d["import"].e == 1 + d["as"].e == 1 + d["default"].e == 1 + d["else"].e == 1 + d["with"].e == 1 + d["null"].e == 1 + d["true"].e == 1 + d["false"].e == 1 + d["some"].e == 1 + + f["not"](2) + f["package"](2) + f["import"](2) + f["as"](2) + f["default"](2) + f["else"](2) + f["with"](2) + f["null"](2) + f["true"](2) + f["false"](2) + f["some"](2) + + g.h["not"](2) + g.h["package"](2) + g.h["import"](2) + g.h["as"](2) + g.h["default"](2) + g.h["else"](2) + g.h["with"](2) + g.h["null"](2) + g.h["true"](2) + g.h["false"](2) + g.h["some"](2) + + i["not"].j(2) + i["package"].j(2) + i["import"].j(2) + i["as"].j(2) + i["default"].j(2) + i["else"].j(2) + i["with"].j(2) + i["null"].j(2) + i["true"].j(2) + i["false"].j(2) + i["some"].j(2) +} + +a["not"] := 1 +a["package"] := 1 +a["import"] := 1 +a["as"] := 1 +a["default"] := 1 +a["else"] := 1 +a["with"] := 1 +a["null"] := 1 +a["true"] := 1 +a["false"] := 1 +a["some"] := 1 + +b.c["not"] := 1 +b.c["package"] := 1 +b.c["import"] := 1 +b.c["as"] := 1 +b.c["default"] := 1 +b.c["else"] := 1 +b.c["with"] := 1 +b.c["null"] := 1 +b.c["true"] := 1 +b.c["false"] := 1 +b.c["some"] := 1 + +d["not"].e := 1 +d["package"].e := 1 +d["import"].e := 1 +d["as"].e := 1 +d["default"].e := 1 +d["else"].e := 1 +d["with"].e := 1 +d["null"].e := 1 +d["true"].e := 1 +d["false"].e := 1 +d["some"].e := 1 + +f["not"](x) := x +f["package"](x) := x +f["import"](x) := x +f["as"](x) := x +f["default"](x) := x +f["else"](x) := x +f["with"](x) := x +f["null"](x) := x +f["true"](x) := x +f["false"](x) := x +f["some"](x) := x + +g.h["not"](x) := x +g.h["package"](x) := x +g.h["import"](x) := x +g.h["as"](x) := x +g.h["default"](x) := x +g.h["else"](x) := x +g.h["with"](x) := x +g.h["null"](x) := x +g.h["true"](x) := x +g.h["false"](x) := x +g.h["some"](x) := x + +i["not"].j(x) := x +i["package"].j(x) := x +i["import"].j(x) := x +i["as"].j(x) := x +i["default"].j(x) := x +i["else"].j(x) := x +i["with"].j(x) := x +i["null"].j(x) := x +i["true"].j(x) := x +i["false"].j(x) := x +i["some"].j(x) := x diff --git a/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs_keep_brackets.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs_keep_brackets.rego.formatted new file mode 100644 index 000000000000..09e4444f91d5 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_keywords_in_refs_keep_brackets.rego.formatted @@ -0,0 +1,231 @@ +package test["not"]["package"]["import"]["as"]["default"]["else"]["with"]["null"]["true"]["false"]["some"] + +import data["as"] as foo4 +import data["default"] as foo5 +import data["else"] as foo6 +import data["false"] as foo10 +import data["import"] as foo3 +import data["not"] as foo1 +import data["null"] as foo8 +import data["package"] as foo2 +import data["some"] as foo11 +import data["true"] as foo9 +import data["with"] as foo7 + +import data["as"].bar4 +import data["default"].bar5 +import data["else"].bar6 +import data["false"].bar10 +import data["import"].bar3 +import data["not"].bar1 +import data["null"].bar8 +import data["package"].bar2 +import data["some"].bar11 +import data["true"].bar9 +import data["with"].bar7 + +p { + a["not"] == 1 + a["package"] == 1 + a["import"] == 1 + a["as"] == 1 + a["default"] == 1 + a["else"] == 1 + a["with"] == 1 + a["null"] == 1 + a["true"] == 1 + a["false"] == 1 + a["some"] == 1 + + b.c["not"] == 1 + b.c["package"] == 1 + b.c["import"] == 1 + b.c["as"] == 1 + b.c["default"] == 1 + b.c["else"] == 1 + b.c["with"] == 1 + b.c["null"] == 1 + b.c["true"] == 1 + b.c["false"] == 1 + b.c["some"] == 1 + + d["not"].e == 1 + d["package"].e == 1 + d["import"].e == 1 + d["as"].e == 1 + d["default"].e == 1 + d["else"].e == 1 + d["with"].e == 1 + d["null"].e == 1 + d["true"].e == 1 + d["false"].e == 1 + d["some"].e == 1 + + f["not"](2) + f["package"](2) + f["import"](2) + f["as"](2) + f["default"](2) + f["else"](2) + f["with"](2) + f["null"](2) + f["true"](2) + f["false"](2) + f["some"](2) + + g.h["not"](2) + g.h["package"](2) + g.h["import"](2) + g.h["as"](2) + g.h["default"](2) + g.h["else"](2) + g.h["with"](2) + g.h["null"](2) + g.h["true"](2) + g.h["false"](2) + g.h["some"](2) + + i["not"].j(2) + i["package"].j(2) + i["import"].j(2) + i["as"].j(2) + i["default"].j(2) + i["else"].j(2) + i["with"].j(2) + i["null"].j(2) + i["true"].j(2) + i["false"].j(2) + i["some"].j(2) +} + +a["not"] := 1 + +a["package"] := 1 + +a["import"] := 1 + +a["as"] := 1 + +a["default"] := 1 + +a["else"] := 1 + +a["with"] := 1 + +a["null"] := 1 + +a["true"] := 1 + +a["false"] := 1 + +a["some"] := 1 + +b.c["not"] := 1 + +b.c["package"] := 1 + +b.c["import"] := 1 + +b.c["as"] := 1 + +b.c["default"] := 1 + +b.c["else"] := 1 + +b.c["with"] := 1 + +b.c["null"] := 1 + +b.c["true"] := 1 + +b.c["false"] := 1 + +b.c["some"] := 1 + +d["not"].e := 1 + +d["package"].e := 1 + +d["import"].e := 1 + +d["as"].e := 1 + +d["default"].e := 1 + +d["else"].e := 1 + +d["with"].e := 1 + +d["null"].e := 1 + +d["true"].e := 1 + +d["false"].e := 1 + +d["some"].e := 1 + +f["not"](x) := x + +f["package"](x) := x + +f["import"](x) := x + +f["as"](x) := x + +f["default"](x) := x + +f["else"](x) := x + +f["with"](x) := x + +f["null"](x) := x + +f["true"](x) := x + +f["false"](x) := x + +f["some"](x) := x + +g.h["not"](x) := x + +g.h["package"](x) := x + +g.h["import"](x) := x + +g.h["as"](x) := x + +g.h["default"](x) := x + +g.h["else"](x) := x + +g.h["with"](x) := x + +g.h["null"](x) := x + +g.h["true"](x) := x + +g.h["false"](x) := x + +g.h["some"](x) := x + +i["not"].j(x) := x + +i["package"].j(x) := x + +i["import"].j(x) := x + +i["as"].j(x) := x + +i["default"].j(x) := x + +i["else"].j(x) := x + +i["with"].j(x) := x + +i["null"].j(x) := x + +i["true"].j(x) := x + +i["false"].j(x) := x + +i["some"].j(x) := x diff --git a/third_party/opa/v1/format/testfiles/v0/test_ref_heads.rego b/third_party/opa/v1/format/testfiles/v0/test_ref_heads.rego new file mode 100644 index 000000000000..f67663a4d17c --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_ref_heads.rego @@ -0,0 +1,20 @@ +package test + +import future.keywords + +a.b.c = "d" if true +a.b.e = "f" if true +a.b.g contains x if some x in numbers.range(1, 3) +a.b.h[x] = 1 if x := "one" + +q[1] = y if true +r[x] if x := 10 +p.q.r[x] if x := 10 +p.q.r[2] if true + +g[h].i[j].k { true } +g[h].i[j].k { h := 1; j = 2 } +g[3].i[j].k = x { j := 3; x = 4 } +g[h].i[j].k[l] if { true } +g[h].i[j].k[l] contains x { x = "foo" } +g[h].i[j].k[l] contains x { h := 5; j := 6; l = 7; x = "foo" } diff --git a/third_party/opa/v1/format/testfiles/v0/test_ref_heads.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_ref_heads.rego.formatted new file mode 100644 index 000000000000..4be7081b2be9 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_ref_heads.rego.formatted @@ -0,0 +1,35 @@ +package test + +import future.keywords + +a.b.c = "d" +a.b.e = "f" +a.b.g contains x if some x in numbers.range(1, 3) +a.b.h[x] = 1 if x := "one" + +q[1] = y +r[x] if x := 10 +p.q.r[x] if x := 10 +p.q.r[2] = true + +g[h].i[j].k = true + +g[h].i[j].k if { + h := 1 + j = 2 +} + +g[3].i[j].k = x if { + j := 3 + x = 4 +} + +g[h].i[j].k[l] = true +g[h].i[j].k[l] contains x if x = "foo" + +g[h].i[j].k[l] contains x if { + h := 5 + j := 6 + l = 7 + x = "foo" +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_rego_v1.rego b/third_party/opa/v1/format/testfiles/v0/test_rego_v1.rego new file mode 100644 index 000000000000..58fea5c96622 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_rego_v1.rego @@ -0,0 +1,26 @@ +package example + +import rego.v1 + +# R1: constant +a := 1 + +# R2: set +b contains "c" + +# R3: boolean +c.d.e := true + +# R4: set +d contains x if { + x := "e" +} + +# R5: boolean +e.f[x] if { + x := "g" +} + +f if true in [true, false] + +g if every x in [1, 2, 3] { x < 4 } diff --git a/third_party/opa/v1/format/testfiles/v0/test_rego_v1.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_rego_v1.rego.formatted new file mode 100644 index 000000000000..58fea5c96622 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_rego_v1.rego.formatted @@ -0,0 +1,26 @@ +package example + +import rego.v1 + +# R1: constant +a := 1 + +# R2: set +b contains "c" + +# R3: boolean +c.d.e := true + +# R4: set +d contains x if { + x := "e" +} + +# R5: boolean +e.f[x] if { + x := "g" +} + +f if true in [true, false] + +g if every x in [1, 2, 3] { x < 4 } diff --git a/third_party/opa/v1/format/testfiles/v0/test_unicode.rego b/third_party/opa/v1/format/testfiles/v0/test_unicode.rego new file mode 100644 index 000000000000..03c29fac880d --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_unicode.rego @@ -0,0 +1,15 @@ +package test + +x := "\u0000" +x := "\u0000 \"" + +_fg := { + "black": "\u001b[30m", + "red": "\u001b[31m", + "green": "\u001b[32m", + "yellow": "\u001b[33m", + "blue": "\u001b[34m", + "magenta": "\u001b[35m", + "cyan": "\u001b[36m", + "white": "\u001b[37m", +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_unicode.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_unicode.rego.formatted new file mode 100644 index 000000000000..8370bb838276 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_unicode.rego.formatted @@ -0,0 +1,16 @@ +package test + +x := "\u0000" + +x := "\u0000 \"" + +_fg := { + "black": "\u001b[30m", + "red": "\u001b[31m", + "green": "\u001b[32m", + "yellow": "\u001b[33m", + "blue": "\u001b[34m", + "magenta": "\u001b[35m", + "cyan": "\u001b[36m", + "white": "\u001b[37m", +} diff --git a/third_party/opa/v1/format/testfiles/v0/test_with.rego b/third_party/opa/v1/format/testfiles/v0/test_with.rego new file mode 100644 index 000000000000..5ff97ad8e7ce --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_with.rego @@ -0,0 +1,38 @@ +package p + +single_line_with { + fn(1) with input.a as "a" +} + +multi_line_with { + fn(1) with input.a as "a" + with input.b as "b" + with input.c as { + "foo": "bar", + } + with input.d as [ + 1, + 2, + 3] +} + +mixed_new_lines_with { + true with input.a as "a" + with input.b as "b" with input.c as "c" + with input.d as "d" +} + +mock_f(_) = 123 + +func_replacements { + count(array.concat(input.x, [])) with input.x as "foo" + with array.concat as true + with count as mock_f +} + +original(x) = x+1 + +more_func_replacements { + original(1) with original as mock_f + original(1) with original as 1234 +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0/test_with.rego.formatted b/third_party/opa/v1/format/testfiles/v0/test_with.rego.formatted new file mode 100644 index 000000000000..538d356bf888 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0/test_with.rego.formatted @@ -0,0 +1,37 @@ +package p + +single_line_with { + fn(1) with input.a as "a" +} + +multi_line_with { + fn(1) with input.a as "a" + with input.b as "b" + with input.c as {"foo": "bar"} + with input.d as [ + 1, + 2, + 3, + ] +} + +mixed_new_lines_with { + true with input.a as "a" + with input.b as "b" with input.c as "c" + with input.d as "d" +} + +mock_f(_) = 123 + +func_replacements { + count(array.concat(input.x, [])) with input.x as "foo" + with array.concat as true + with count as mock_f +} + +original(x) = x + 1 + +more_func_replacements { + original(1) with original as mock_f + original(1) with original as 1234 +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/constants.rego b/third_party/opa/v1/format/testfiles/v0_to_v1/constants.rego new file mode 100644 index 000000000000..664a8f7a4f8b --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/constants.rego @@ -0,0 +1,9 @@ +package test + +a := 1 + +b.c := 2 + +c.d.e := 3 + +d.e.f.g := 4 diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/constants.rego.formatted b/third_party/opa/v1/format/testfiles/v0_to_v1/constants.rego.formatted new file mode 100644 index 000000000000..5d2070260235 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/constants.rego.formatted @@ -0,0 +1,11 @@ +package test + +import rego.v1 + +a := 1 + +b.c := 2 + +c.d.e := 3 + +d.e.f.g := 4 diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/deprecated_builtins.rego b/third_party/opa/v1/format/testfiles/v0_to_v1/deprecated_builtins.rego new file mode 100644 index 000000000000..8e1f16bf224d --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/deprecated_builtins.rego @@ -0,0 +1,21 @@ +package test + +p { + any([true, false]) + all([true, false]) + cast_array(["foo", "bar"]) + cast_boolean(true) + cast_null(null) + cast_object({"foo": "bar"}) + cast_set({"foo", "bar"}) + cast_string("foo") + net.cidr_overlap("127.0.0.1/24", "127.0.0.64/26") + re_match("f.o", "foo") + set_diff({"a", "b", "c"},{"b", "c"}) +} + +q := any([true, false]) + +r[any([true, false])] + +s[any([true, false])][all([true, false])] diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/deprecated_builtins.rego.error b/third_party/opa/v1/format/testfiles/v0_to_v1/deprecated_builtins.rego.error new file mode 100644 index 000000000000..480d9c48eee6 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/deprecated_builtins.rego.error @@ -0,0 +1,16 @@ +15 errors occurred: +testfiles/v0_to_v1/deprecated_builtins.rego:4: rego_type_error: deprecated built-in function calls in expression: any +testfiles/v0_to_v1/deprecated_builtins.rego:5: rego_type_error: deprecated built-in function calls in expression: all +testfiles/v0_to_v1/deprecated_builtins.rego:6: rego_type_error: deprecated built-in function calls in expression: cast_array +testfiles/v0_to_v1/deprecated_builtins.rego:7: rego_type_error: deprecated built-in function calls in expression: cast_boolean +testfiles/v0_to_v1/deprecated_builtins.rego:8: rego_type_error: deprecated built-in function calls in expression: cast_null +testfiles/v0_to_v1/deprecated_builtins.rego:9: rego_type_error: deprecated built-in function calls in expression: cast_object +testfiles/v0_to_v1/deprecated_builtins.rego:10: rego_type_error: deprecated built-in function calls in expression: cast_set +testfiles/v0_to_v1/deprecated_builtins.rego:11: rego_type_error: deprecated built-in function calls in expression: cast_string +testfiles/v0_to_v1/deprecated_builtins.rego:12: rego_type_error: deprecated built-in function calls in expression: net.cidr_overlap +testfiles/v0_to_v1/deprecated_builtins.rego:13: rego_type_error: deprecated built-in function calls in expression: re_match +testfiles/v0_to_v1/deprecated_builtins.rego:14: rego_type_error: deprecated built-in function calls in expression: set_diff +testfiles/v0_to_v1/deprecated_builtins.rego:17: rego_type_error: deprecated built-in function calls in expression: any +testfiles/v0_to_v1/deprecated_builtins.rego:19: rego_type_error: deprecated built-in function calls in expression: any +testfiles/v0_to_v1/deprecated_builtins.rego:21: rego_type_error: deprecated built-in function calls in expression: any +testfiles/v0_to_v1/deprecated_builtins.rego:21: rego_type_error: deprecated built-in function calls in expression: all \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/duplicate_imports.rego b/third_party/opa/v1/format/testfiles/v0_to_v1/duplicate_imports.rego new file mode 100644 index 000000000000..6f092f70a341 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/duplicate_imports.rego @@ -0,0 +1,5 @@ +package test + +import data.foo +import data.bar.foo +import data.baz as foo diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/duplicate_imports.rego.error b/third_party/opa/v1/format/testfiles/v0_to_v1/duplicate_imports.rego.error new file mode 100644 index 000000000000..a74cd5f27af5 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/duplicate_imports.rego.error @@ -0,0 +1,3 @@ +2 errors occurred: +testfiles/v0_to_v1/duplicate_imports.rego:4: rego_compile_error: import must not shadow import data.foo +testfiles/v0_to_v1/duplicate_imports.rego:5: rego_compile_error: import must not shadow import data.foo \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/functions.rego b/third_party/opa/v1/format/testfiles/v0_to_v1/functions.rego new file mode 100644 index 000000000000..e947a0bc1412 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/functions.rego @@ -0,0 +1,69 @@ +package test + +import future.keywords.if + +a(_) + +b("foo") + +c(x) { + x == 1 +} + +d(x) if { + x == 1 +} + +e(x) := x + +f(x) := x { + x == 1 +} + +g(x) := x if { + x == 1 +} + +h.i(_) + +i.j("foo") + +j.k(x) { + x == 1 +} + +k.l(x) if { + x == 1 +} + +l.m(x) := x + +m.n(x) := x { + x == 1 +} + +n.o(x) := x if { + x == 1 +} + +o.p.q(_) + +p.q.r("foo") + +q.r.s(x) { + x == 1 +} + +r.s.t(x) if { + x == 1 +} + +s.t.u(x) := x + +t.u.v(x) := x { + x == 1 +} + +u.v.w(x) := x if { + x == 1 +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/functions.rego.formatted b/third_party/opa/v1/format/testfiles/v0_to_v1/functions.rego.formatted new file mode 100644 index 000000000000..03a3d05a7c8a --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/functions.rego.formatted @@ -0,0 +1,69 @@ +package test + +import rego.v1 + +a(_) + +b("foo") + +c(x) if { + x == 1 +} + +d(x) if { + x == 1 +} + +e(x) := x + +f(x) := x if { + x == 1 +} + +g(x) := x if { + x == 1 +} + +h.i(_) + +i.j("foo") + +j.k(x) if { + x == 1 +} + +k.l(x) if { + x == 1 +} + +l.m(x) := x + +m.n(x) := x if { + x == 1 +} + +n.o(x) := x if { + x == 1 +} + +o.p.q(_) + +p.q.r("foo") + +q.r.s(x) if { + x == 1 +} + +r.s.t(x) if { + x == 1 +} + +s.t.u(x) := x + +t.u.v(x) := x if { + x == 1 +} + +u.v.w(x) := x if { + x == 1 +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/keyword_errors.rego b/third_party/opa/v1/format/testfiles/v0_to_v1/keyword_errors.rego new file mode 100644 index 000000000000..f77a4a4c0a71 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/keyword_errors.rego @@ -0,0 +1,13 @@ +package test + +if := 1 + +contains := 2 + +in := 3 + +every := 4 + +p { + data.foo.contains.bar == 42 +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/keyword_errors.rego.error b/third_party/opa/v1/format/testfiles/v0_to_v1/keyword_errors.rego.error new file mode 100644 index 000000000000..cc44add905ef --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/keyword_errors.rego.error @@ -0,0 +1,5 @@ +4 errors occurred: +testfiles/v0_to_v1/keyword_errors.rego:3: rego_parse_error: if keyword cannot be used for rule name +testfiles/v0_to_v1/keyword_errors.rego:5: rego_parse_error: contains keyword cannot be used for rule name +testfiles/v0_to_v1/keyword_errors.rego:7: rego_parse_error: in keyword cannot be used for rule name +testfiles/v0_to_v1/keyword_errors.rego:9: rego_parse_error: every keyword cannot be used for rule name \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/keywords.rego b/third_party/opa/v1/format/testfiles/v0_to_v1/keywords.rego new file mode 100644 index 000000000000..c853342515e4 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/keywords.rego @@ -0,0 +1,6 @@ +package test.if.contains.in.every + +p { + data.if.contains.in.every == 1 + data["if"]["contains"]["in"]["every"] == 2 +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/keywords.rego.formatted b/third_party/opa/v1/format/testfiles/v0_to_v1/keywords.rego.formatted new file mode 100644 index 000000000000..cc7ea3c6c5cd --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/keywords.rego.formatted @@ -0,0 +1,8 @@ +package test.if.contains.in.every + +import rego.v1 + +p if { + data.if.contains.in.every == 1 + data["if"]["contains"]["in"]["every"] == 2 +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/multi_value.rego b/third_party/opa/v1/format/testfiles/v0_to_v1/multi_value.rego new file mode 100644 index 000000000000..b2aeb0004d42 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/multi_value.rego @@ -0,0 +1,32 @@ +package test + +import future.keywords.contains +import future.keywords.if + +a.b + +b.c { + input.x +} + +c contains "d" + +d contains "e" if { + input.x +} + +e.f contains "g" { + input.x +} + +f.g contains "h" if { + input.x +} + +g[h].i contains "j" { + h := input.h +} + +h[i].j contains "k" if { + i := input.h +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/multi_value.rego.formatted b/third_party/opa/v1/format/testfiles/v0_to_v1/multi_value.rego.formatted new file mode 100644 index 000000000000..0aca02df7874 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/multi_value.rego.formatted @@ -0,0 +1,31 @@ +package test + +import rego.v1 + +a contains "b" + +b contains "c" if { + input.x +} + +c contains "d" + +d contains "e" if { + input.x +} + +e.f contains "g" if { + input.x +} + +f.g contains "h" if { + input.x +} + +g[h].i contains "j" if { + h := input.h +} + +h[i].j contains "k" if { + i := input.h +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/multi_value_no_future_imports.rego b/third_party/opa/v1/format/testfiles/v0_to_v1/multi_value_no_future_imports.rego new file mode 100644 index 000000000000..c1878cb88f89 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/multi_value_no_future_imports.rego @@ -0,0 +1,13 @@ +package test + +a.b + +b["c"] + +c.d { + input.x +} + +d["e"] { + input.x +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/multi_value_no_future_imports.rego.formatted b/third_party/opa/v1/format/testfiles/v0_to_v1/multi_value_no_future_imports.rego.formatted new file mode 100644 index 000000000000..586b9ae12fca --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/multi_value_no_future_imports.rego.formatted @@ -0,0 +1,15 @@ +package test + +import rego.v1 + +a contains "b" + +b contains "c" + +c contains "d" if { + input.x +} + +d contains "e" if { + input.x +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/shadowing.rego b/third_party/opa/v1/format/testfiles/v0_to_v1/shadowing.rego new file mode 100644 index 000000000000..be7eee1f2c91 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/shadowing.rego @@ -0,0 +1,34 @@ +package test + +input := "do" + +input.x := "re" + +input.x.y := "mi" + +data := "fa" + +data.x := "so" + +data.x.y := "la" + +p { + input := 1 + data := 2 +} + +q[input] { + input := 3 +} + +r[data] { + data := 4 +} + +s(input) { + input == 5 +} + +t(data) { + data == 6 +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/shadowing.rego.error b/third_party/opa/v1/format/testfiles/v0_to_v1/shadowing.rego.error new file mode 100644 index 000000000000..b050552aa2da --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/shadowing.rego.error @@ -0,0 +1,13 @@ +12 errors occurred: +testfiles/v0_to_v1/shadowing.rego:3: rego_compile_error: rules must not shadow input (use a different rule name) +testfiles/v0_to_v1/shadowing.rego:5: rego_compile_error: rules must not shadow input (use a different rule name) +testfiles/v0_to_v1/shadowing.rego:7: rego_compile_error: rules must not shadow input (use a different rule name) +testfiles/v0_to_v1/shadowing.rego:9: rego_compile_error: rules must not shadow data (use a different rule name) +testfiles/v0_to_v1/shadowing.rego:11: rego_compile_error: rules must not shadow data (use a different rule name) +testfiles/v0_to_v1/shadowing.rego:13: rego_compile_error: rules must not shadow data (use a different rule name) +testfiles/v0_to_v1/shadowing.rego:28: rego_compile_error: args must not shadow input (use a different variable name) +testfiles/v0_to_v1/shadowing.rego:32: rego_compile_error: args must not shadow data (use a different variable name) +testfiles/v0_to_v1/shadowing.rego:16: rego_compile_error: variables must not shadow input (use a different variable name) +testfiles/v0_to_v1/shadowing.rego:17: rego_compile_error: variables must not shadow data (use a different variable name) +testfiles/v0_to_v1/shadowing.rego:21: rego_compile_error: variables must not shadow input (use a different variable name) +testfiles/v0_to_v1/shadowing.rego:25: rego_compile_error: variables must not shadow data (use a different variable name) \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/single_value.rego b/third_party/opa/v1/format/testfiles/v0_to_v1/single_value.rego new file mode 100644 index 000000000000..22c7859bb47d --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/single_value.rego @@ -0,0 +1,35 @@ +package test + +import future.keywords.if + +a.b := "c" { + input.x +} + +b.c := "d" if { + input.d +} + +c.d.e + +d.e.f { + input.x +} + +e[f] := "g" { + f := input.f +} + +f[g] := "h" if { + g := input.g +} + +g.h[i].j[k] := "l" { + i := input.i + k := input.k +} + +h.i[j].k[l] := "m" if { + j := input.j + l := input.l +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/single_value.rego.formatted b/third_party/opa/v1/format/testfiles/v0_to_v1/single_value.rego.formatted new file mode 100644 index 000000000000..c2c316dc7680 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/single_value.rego.formatted @@ -0,0 +1,35 @@ +package test + +import rego.v1 + +a.b := "c" if { + input.x +} + +b.c := "d" if { + input.d +} + +c.d.e := true + +d.e.f if { + input.x +} + +e[f] := "g" if { + f := input.f +} + +f[g] := "h" if { + g := input.g +} + +g.h[i].j[k] := "l" if { + i := input.i + k := input.k +} + +h.i[j].k[l] := "m" if { + j := input.j + l := input.l +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/single_value_no_future_imports.rego b/third_party/opa/v1/format/testfiles/v0_to_v1/single_value_no_future_imports.rego new file mode 100644 index 000000000000..696ccb60255b --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/single_value_no_future_imports.rego @@ -0,0 +1,32 @@ +package test + +a.b := "c" { + input.x +} + +b["c/d"] := "e" { + input.d +} + +c.d.e + +d.e.f { + input.x +} + +e[f] := "g" { + f := input.f +} + +f["g"] := "h" { + input.x +} + +g.h[i].j[k] := "l" { + i := input.i + k := input.k +} + +h.i["j/k"].l["m"] := "n" { + input.x +} diff --git a/third_party/opa/v1/format/testfiles/v0_to_v1/single_value_no_future_imports.rego.formatted b/third_party/opa/v1/format/testfiles/v0_to_v1/single_value_no_future_imports.rego.formatted new file mode 100644 index 000000000000..ac9bb794c5a5 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v0_to_v1/single_value_no_future_imports.rego.formatted @@ -0,0 +1,34 @@ +package test + +import rego.v1 + +a.b := "c" if { + input.x +} + +b["c/d"] := "e" if { + input.d +} + +c.d.e := true + +d.e.f if { + input.x +} + +e[f] := "g" if { + f := input.f +} + +f.g := "h" if { + input.x +} + +g.h[i].j[k] := "l" if { + i := input.i + k := input.k +} + +h.i["j/k"].l.m := "n" if { + input.x +} diff --git a/third_party/opa/v1/format/testfiles/v1/test.rego b/third_party/opa/v1/format/testfiles/v1/test.rego new file mode 100644 index 000000000000..9e8eed6a97b5 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test.rego @@ -0,0 +1,222 @@ +# The blank lines below me should be gone! (except one) + + +# Comment! +package a.b + +# I also, am a comment. +import data.x.y.z +import data.a.b.c # Another comment! +# I belong with data.a, there should be a newline before me. +import data.a +import data.f.g + +default foo = false +foo contains x if { +not x = g + f(x) = 1 + g( + x, "foo" + ) = z +} + +globals = {"foo": "bar", +"fizz": "buzz"} + +partial_obj["x"] = 1 +partial_obj.y = 2 + +partial_obj["z"] = 3 if { + true +} + +partial_set contains "x" + +# Latent comment. + +r = y if { + y = x + split("foo.bar", ".", input.x) with input as {"x": x} + } + +# Comment on else + else = y if { + y = ["howdy"] + x = {"x": { + "y": "z", + }} + a = {"a": { + "b": "c", + }, "b": "c", "c": [1, 2, + 3, 4]} + } + +fn(x) = y if { + y = x +} + +fn_else(x) = 1 if { + true +} # foo +else = +# bar +2 +if { + true +} else = 3 +# baz +if { + false +} + +long(x) = true if { + x = "foo %host" + } + + short(x) if { + x = "bar" + } + +raw_string = `hi\there` +raw_multiline = `this +string + is on +multiple lines` + +fn2([x, y, +z], {"foo": a}) = b if { +split(x, y, c) +trim(a, z, d) # function comment 1 +split(c[0], d, b) +x = sprintf("hello %v", +["world"]) +#function comment 2 +} # function comment 3 + +f contains x if { + x = "hi" +} { # Comment on chain + x = "bye" +} + + import data.foo.bar + import data.bar.foo # data.bar.foo should be first + +p[x] = y if { y = x + y = "foo" + z = { "a": "b", # Comment inside object 1 + "b": "c" , "c": "d", # comment on object entry line + # Comment inside object 2 +"d": "e", +# Comment before closing object brace. +} # Comment on closing object brace. +a = {"a": "b", "c": "d"} +b = [1, 2, 3, 4] +c = [1, 2, +# Comment inside array +3, 4, +5, 6, 7, +8, +# Comment before nested composite. +[ +["foo"], # Comment inside nested composite. +["bar"], # Comment after last element in nested composite. +# Comment before nested composite closing bracket. +], # Comment on nested composite closing bracket. +# Comment before closing array bracket. +] # Comment on closing array bracket. + +d = [1 | b[_]] +e = [1 | split("foo.bar", ".", x); x[_]] +f = [1 | split("foo.bar", ".", x) +x[_]] +g = [1 | +split("foo.bar", ".", x) # comment in array comprehension +x[_] +# inner comment +] + +h = {1 | b[_]} +i = {1 | split("foo.bar", ".", x); x[_]} +j = {1 | split("foo.bar", ".", x) +x[_]} +k = {1 | +split("foo.bar", ".", x) # comment in set comprehension +x[_] + +# inner comment +} + +l = {"foo":1 | b[_]} +m = {y: +x | split("foo.bar", ".", x); y = x[_]} +n = {y: x | split("foo.bar", ".", x) +y = x[_]} +o = {y: x | +split("foo.bar", ".", x) # comment in object comprehension +y = x[_] + +# inner comment +} +} # Comment on rule closing brace + +nested_infix if { + x + 1 + x = y + 2 + plus(x, 1, 2) + plus(x, 1) + y = f(x) + f(x, y) + y = x + 1 + 2 + x = y + # comment + z + x = (a + b) / 2 + f((a+b)/2) + y = q() +} + +expanded_const = true + +partial_obj["why"] = true if { false } + +empty_sets if { + set() + set() # comment at end of set +} + +vardecls if { + some v1, v2,# c1 + v3,v4, # c2 + v5 # c3 +} + +declare1 := 1 + +declare2 := 2 if { false } + +declare3 := {1,2,3} +declare4 := {4,5,6} + +union_object := {"response": (declare3|declare4)} + +union_set := {(declare3|declare4)} + +union_list := [(declare3|declare4)] + +union_set_2 := {(((declare3|declare4)))} + +union_object_multi_line := {"response": ( + declare3 | declare4 +)} + +union_object_key := {(declare3|declare4): "foo"} + +union_object_key_multi_line := {(declare3| +declare4): +"foo" +} + +# more comments! +# more comments! +# more comments! +# more comments! diff --git a/third_party/opa/v1/format/testfiles/v1/test.rego.error b/third_party/opa/v1/format/testfiles/v1/test.rego.error new file mode 100644 index 000000000000..77482680fbbd --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test.rego.error @@ -0,0 +1,26 @@ +# The blank lines below me should be gone! (except one) + + +# Comment! +package a.b + +# I also, am a comment. +import data.x.y.z +import data.a.b.c # Another comment! +# I belong with data.a, there should be a newline before me. +import data.a +import data.f.g + +default foo = false +foo[x] { +not x = g +} + +globals = {"foo": "bar", +"fizz": "buzz"} + +# Latent comment. + +r = y { + y = x + split("foo.bar", ".", input.x) with input as {"x": x} diff --git a/third_party/opa/v1/format/testfiles/v1/test.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test.rego.formatted new file mode 100644 index 000000000000..53b2f71f5574 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test.rego.formatted @@ -0,0 +1,234 @@ +# The blank lines below me should be gone! (except one) + +# Comment! +package a.b + +# I also, am a comment. +import data.a.b.c # Another comment! +import data.x.y.z + +# I belong with data.a, there should be a newline before me. +import data.a +import data.f.g + +default foo := false + +foo contains x if { + not x = g + f(x) = 1 + g(x, "foo") = z +} + +globals := { + "foo": "bar", + "fizz": "buzz", +} + +partial_obj["x"] := 1 +partial_obj["y"] := 2 + +partial_obj["z"] := 3 + +partial_set contains "x" + +# Latent comment. + +r := y if { + y = x + split("foo.bar", ".", input.x) with input as {"x": x} +} + +# Comment on else +else := y if { + y = ["howdy"] + x = {"x": {"y": "z"}} + a = { + "a": {"b": "c"}, + "b": "c", "c": [ + 1, 2, + 3, 4, + ], + } +} + +fn(x) := y if { + y = x +} + +fn_else(x) := 1 if { + true +} # foo + +else := 2 if { + # bar + + true +} else := 3 if { + # baz + + false +} + +long(x) if { + x = "foo %host" +} + +short(x) if { + x = "bar" +} + +raw_string := `hi\there` +raw_multiline := `this +string + is on +multiple lines` + +fn2( + [ + x, y, + z, + ], + {"foo": a}, +) := b if { + split(x, y, c) + trim(a, z, d) # function comment 1 + split(c[0], d, b) + x = sprintf( + "hello %v", + ["world"], + ) + #function comment 2 +} # function comment 3 + +f contains x if { + x = "hi" +} + +f contains x if { # Comment on chain + x = "bye" +} + +import data.bar.foo # data.bar.foo should be first +import data.foo.bar + +p[x] := y if { + y = x + y = "foo" + z = { + "a": "b", # Comment inside object 1 + "b": "c", "c": "d", # comment on object entry line + # Comment inside object 2 + "d": "e", + # Comment before closing object brace. + } # Comment on closing object brace. + a = {"a": "b", "c": "d"} + b = [1, 2, 3, 4] + c = [ + 1, 2, + # Comment inside array + 3, 4, + 5, 6, 7, + 8, + # Comment before nested composite. + [ + ["foo"], # Comment inside nested composite. + ["bar"], # Comment after last element in nested composite. + # Comment before nested composite closing bracket. + ], # Comment on nested composite closing bracket. + # Comment before closing array bracket. + ] # Comment on closing array bracket. + + d = [1 | b[_]] + e = [1 | split("foo.bar", ".", x); x[_]] + f = [1 | + split("foo.bar", ".", x) + x[_] + ] + g = [1 | + split("foo.bar", ".", x) # comment in array comprehension + x[_] + # inner comment + ] + + h = {1 | b[_]} + i = {1 | split("foo.bar", ".", x); x[_]} + j = {1 | + split("foo.bar", ".", x) + x[_] + } + k = {1 | + split("foo.bar", ".", x) # comment in set comprehension + x[_] + # inner comment + } + + l = {"foo": 1 | b[_]} + m = {y: x | + split("foo.bar", ".", x) + y = x[_] + } + n = {y: x | + split("foo.bar", ".", x) + y = x[_] + } + o = {y: x | + split("foo.bar", ".", x) # comment in object comprehension + y = x[_] + # inner comment + } +} # Comment on rule closing brace + +nested_infix if { + x + 1 + x = y + 2 + 2 = x + 1 + x + 1 + y = f(x) + f(x, y) + y = (x + 1) + 2 + x = y + z # comment + x = (a + b) / 2 + f((a + b) / 2) + y = q() +} + +expanded_const := true + +partial_obj["why"] if false + +empty_sets if { + set() + set() # comment at end of set +} + +vardecls if { + some v1, v2, # c1 + v3, v4, # c2 + v5 # c3 +} + +declare1 := 1 + +declare2 := 2 if false + +declare3 := {1, 2, 3} +declare4 := {4, 5, 6} + +union_object := {"response": (declare3 | declare4)} + +union_set := {(declare3 | declare4)} + +union_list := [(declare3 | declare4)] + +union_set_2 := {(declare3 | declare4)} + +union_object_multi_line := {"response": (declare3 | declare4)} + +union_object_key := {(declare3 | declare4): "foo"} + +union_object_key_multi_line := {(declare3 | declare4): "foo"} + +# more comments! +# more comments! +# more comments! +# more comments! diff --git a/third_party/opa/v1/format/testfiles/v1/test_assignments.rego b/third_party/opa/v1/format/testfiles/v1/test_assignments.rego new file mode 100644 index 000000000000..838be30c89ff --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_assignments.rego @@ -0,0 +1,24 @@ +package assignments + +# default value assignment +default a := 1 + +# rule +b := 2 + +# else keyword +c := 3 if { + false +} else := 4 if { + true +} + +# partial rule +d[msg] := 5 if { + msg = [1, 2, 3][_] +} + +# function return value +e := f(6) + +f(x) := x diff --git a/third_party/opa/v1/format/testfiles/v1/test_assignments.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_assignments.rego.formatted new file mode 100644 index 000000000000..570cc6b8f484 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_assignments.rego.formatted @@ -0,0 +1,22 @@ +package assignments + +# default value assignment +default a := 1 + +# rule +b := 2 + +# else keyword +c := 3 if { + false +} else := 4 + +# partial rule +d[msg] := 5 if { + msg = [1, 2, 3][_] +} + +# function return value +e := f(6) + +f(x) := x diff --git a/third_party/opa/v1/format/testfiles/v1/test_contains.rego b/third_party/opa/v1/format/testfiles/v1/test_contains.rego new file mode 100644 index 000000000000..b0728edde20d --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_contains.rego @@ -0,0 +1,13 @@ +package test["contains"] + +p contains "foo" if { true } + +deny contains msg if { + msg := "foo" +} +deny contains msg if {msg := "bar" } + +# partial objects unchanged +o[k] = v if { k := "ok"; v := "nok" } + +foo["contains"] := 42 diff --git a/third_party/opa/v1/format/testfiles/v1/test_contains.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_contains.rego.formatted new file mode 100644 index 000000000000..7f3a09ab3c91 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_contains.rego.formatted @@ -0,0 +1,17 @@ +package test["contains"] + +p contains "foo" + +deny contains msg if { + msg := "foo" +} + +deny contains msg if msg := "bar" + +# partial objects unchanged +o[k] := v if { + k := "ok" + v := "nok" +} + +foo["contains"] := 42 diff --git a/third_party/opa/v1/format/testfiles/v1/test_contains_if.rego b/third_party/opa/v1/format/testfiles/v1/test_contains_if.rego new file mode 100644 index 000000000000..04ab5191077a --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_contains_if.rego @@ -0,0 +1,10 @@ +package test["if"] + +q[x] = y if { + y := 10 + x := "ten" +} + +r contains x if { # no if here before + x := "set" +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_contains_if.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_contains_if.rego.formatted new file mode 100644 index 000000000000..4375b0d3de55 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_contains_if.rego.formatted @@ -0,0 +1,10 @@ +package test["if"] + +q[x] := y if { + y := 10 + x := "ten" +} + +r contains x if { # no if here before + x := "set" +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_else_strings.rego b/third_party/opa/v1/format/testfiles/v1/test_else_strings.rego new file mode 100644 index 000000000000..2ef1a5c04e35 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_else_strings.rego @@ -0,0 +1,14 @@ +package test + +a := "foo" if { + false +} else := `{"foo":"bar"}` + + +a := "foo" if { + false +} else := "`{\"foo\":\"bar\"}`" + +a := "foo" if { + false +} else := "foo" diff --git a/third_party/opa/v1/format/testfiles/v1/test_else_strings.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_else_strings.rego.formatted new file mode 100644 index 000000000000..eb1846294521 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_else_strings.rego.formatted @@ -0,0 +1,13 @@ +package test + +a := "foo" if { + false +} else := `{"foo":"bar"}` + +a := "foo" if { + false +} else := "`{\"foo\":\"bar\"}`" + +a := "foo" if { + false +} else := "foo" diff --git a/third_party/opa/v1/format/testfiles/v1/test_end_of_rule_comment.rego b/third_party/opa/v1/format/testfiles/v1/test_end_of_rule_comment.rego new file mode 100644 index 000000000000..2538e118a809 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_end_of_rule_comment.rego @@ -0,0 +1,7 @@ +package foo + +bar if { + # before + input.bar + # after +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_end_of_rule_comment.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_end_of_rule_comment.rego.formatted new file mode 100644 index 000000000000..2538e118a809 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_end_of_rule_comment.rego.formatted @@ -0,0 +1,7 @@ +package foo + +bar if { + # before + input.bar + # after +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_every.rego b/third_party/opa/v1/format/testfiles/v1/test_every.rego new file mode 100644 index 000000000000..c84e3f125a4d --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_every.rego @@ -0,0 +1,17 @@ +package p + +r if { + every x in [1,3,5] { + is_odd(x) + } + + every x in [1,3,5] { is_odd(x); true } + + every x in [1,3,5] { + is_odd(x) + true + x < 10 + } +} + +is_odd(x) = x % 2 == 0 \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_every.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_every.rego.formatted new file mode 100644 index 000000000000..b3dfa9447ac7 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_every.rego.formatted @@ -0,0 +1,17 @@ +package p + +r if { + every x in [1, 3, 5] { + is_odd(x) + } + + every x in [1, 3, 5] { is_odd(x); true} + + every x in [1, 3, 5] { + is_odd(x) + true + x < 10 + } +} + +is_odd(x) := (x % 2) == 0 diff --git a/third_party/opa/v1/format/testfiles/v1/test_every_with_key.rego b/third_party/opa/v1/format/testfiles/v1/test_every_with_key.rego new file mode 100644 index 000000000000..7cbd31936169 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_every_with_key.rego @@ -0,0 +1,19 @@ +package p + +r if { + every i, x in [1,3,5] { + is_odd(x) + i < 10 + } + + every i, x in {"foo": 1, "bar": 3, "baz": 5} { is_odd(x); i != 20 } + + every i, x in [1,3,5] { + is_odd(x) + true + x < 10 + x > i + } +} + +is_odd(x) := x % 2 == 0 \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_every_with_key.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_every_with_key.rego.formatted new file mode 100644 index 000000000000..0685b43d539a --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_every_with_key.rego.formatted @@ -0,0 +1,19 @@ +package p + +r if { + every i, x in [1, 3, 5] { + is_odd(x) + i < 10 + } + + every i, x in {"foo": 1, "bar": 3, "baz": 5} { is_odd(x); i != 20} + + every i, x in [1, 3, 5] { + is_odd(x) + true + x < 10 + x > i + } +} + +is_odd(x) := (x % 2) == 0 diff --git a/third_party/opa/v1/format/testfiles/v1/test_fun_args_with_linebreaks.rego b/third_party/opa/v1/format/testfiles/v1/test_fun_args_with_linebreaks.rego new file mode 100644 index 000000000000..09cfeecf443d --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_fun_args_with_linebreaks.rego @@ -0,0 +1,16 @@ +package test + +p if { + x := count( + [1, 2, 3] # four + ) + y := concat( + "/", + ["foo", "bar"], + ) + z := concat("/", + [ + "foo", + "bar", + ]) +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_fun_args_with_linebreaks.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_fun_args_with_linebreaks.rego.formatted new file mode 100644 index 000000000000..c1c2cd55350c --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_fun_args_with_linebreaks.rego.formatted @@ -0,0 +1,16 @@ +package test + +p if { + x := count([1, 2, 3]) # four + y := concat( + "/", + ["foo", "bar"], + ) + z := concat( + "/", + [ + "foo", + "bar", + ], + ) +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_functions.rego b/third_party/opa/v1/format/testfiles/v1/test_functions.rego new file mode 100644 index 000000000000..1dc44b9e3433 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_functions.rego @@ -0,0 +1,48 @@ +package p + +f1(x) = x + +f2(x) := x + +f3(1) + +f4(1) if { + true +} + +f5(1) := x if { + x := 5 +} + +f6(x) if { + true +} else := false + +f7(x) := 1 if { + x.key1 +} else := false if { + x.key2 +} else if { + false +} + +f(x) = 1 if { + input.x == x +} { + input.x < 10 +} + +f(_) if { + input.x +} { + input.y +} + +# Non-functions +foo[x] = y if { + x = 1 + y = 2 +} { + x = 3 + y = 4 +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_functions.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_functions.rego.formatted new file mode 100644 index 000000000000..529e7cac3749 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_functions.rego.formatted @@ -0,0 +1,52 @@ +package p + +f1(x) := x + +f2(x) := x + +f3(1) + +f4(1) := true + +f5(1) := x if { + x := 5 +} + +f6(x) if { + true +} else := false + +f7(x) := 1 if { + x.key1 +} else := false if { + x.key2 +} else if { + false +} + +f(x) := 1 if { + input.x == x +} + +f(x) := 1 if { + input.x < 10 +} + +f(_) if { + input.x +} + +f(_) if { + input.y +} + +# Non-functions +foo[x] := y if { + x = 1 + y = 2 +} + +foo[x] := y if { + x = 3 + y = 4 +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_future_kw_import.rego b/third_party/opa/v1/format/testfiles/v1/test_future_kw_import.rego new file mode 100644 index 000000000000..9c8538310650 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_future_kw_import.rego @@ -0,0 +1,21 @@ +package p + +# existing future.keywords imports kept for broadest compatibility surface +import future.keywords.every +import future.keywords.if + +r if { + every x in [1,3,5] { + is_odd(x) + } + + every x in [1,3,5] { is_odd(x); true } + + every x in [1,3,5] { + is_odd(x) + true + x < 10 + } +} + +is_odd(x) = x % 2 == 0 \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_future_kw_import.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_future_kw_import.rego.formatted new file mode 100644 index 000000000000..16033bed7d08 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_future_kw_import.rego.formatted @@ -0,0 +1,21 @@ +package p + +# existing future.keywords imports kept for broadest compatibility surface +import future.keywords.every +import future.keywords.if + +r if { + every x in [1, 3, 5] { + is_odd(x) + } + + every x in [1, 3, 5] { is_odd(x); true} + + every x in [1, 3, 5] { + is_odd(x) + true + x < 10 + } +} + +is_odd(x) := (x % 2) == 0 diff --git a/third_party/opa/v1/format/testfiles/v1/test_grouping.rego b/third_party/opa/v1/format/testfiles/v1/test_grouping.rego new file mode 100644 index 000000000000..a57e3f8b0edb --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_grouping.rego @@ -0,0 +1,49 @@ +package p + +x := 1 +y := 2 + +x1 := 1 + +x2 := 2 + +a.b.c := 1 +a.b.d := 2 + +s contains 1 +s contains 2 + +s contains 3 + +s contains 4 + +rule if foo == bar +rule if bar == foo + +long if { + x := 1 + y := 2 +} +long if { + x := 2 + y := 3 +} + +short if condition +not_short if { + rule + body +} + +not_short if { + rule + body +} +short if condition + +s contains "foo" +s contains "bar" if { + foo + bar +} + diff --git a/third_party/opa/v1/format/testfiles/v1/test_grouping.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_grouping.rego.formatted new file mode 100644 index 000000000000..c7e79ba6a3c6 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_grouping.rego.formatted @@ -0,0 +1,52 @@ +package p + +x := 1 +y := 2 + +x1 := 1 + +x2 := 2 + +a.b.c := 1 +a.b.d := 2 + +s contains 1 +s contains 2 + +s contains 3 + +s contains 4 + +rule if foo == bar +rule if bar == foo + +long if { + x := 1 + y := 2 +} + +long if { + x := 2 + y := 3 +} + +short if condition + +not_short if { + rule + body +} + +not_short if { + rule + body +} + +short if condition + +s contains "foo" + +s contains "bar" if { + foo + bar +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_if.rego b/third_party/opa/v1/format/testfiles/v1/test_if.rego new file mode 100644 index 000000000000..02482f926974 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_if.rego @@ -0,0 +1,25 @@ +package test["if"] + +p if 1 > 0 # shorthand + +p if { 1 > 0 } # longhand one line + +p if { + 1 > 0 # longhand two lines +} + +# same without the comment +p if { + 1 > 0 +} + +p if { # comment one + 1 > 0 # comment two +} + +q[x] = y if { + y := 10 + x := "ten" +} + +r[x] if { x := "set" } # comment diff --git a/third_party/opa/v1/format/testfiles/v1/test_if.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_if.rego.formatted new file mode 100644 index 000000000000..90f8b37d1d7a --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_if.rego.formatted @@ -0,0 +1,25 @@ +package test["if"] + +p if 1 > 0 # shorthand + +p if 1 > 0 # longhand one line + +p if { + 1 > 0 # longhand two lines +} + +# same without the comment +p if { + 1 > 0 +} + +p if { # comment one + 1 > 0 # comment two +} + +q[x] := y if { + y := 10 + x := "ten" +} + +r[x] if x := "set" # comment diff --git a/third_party/opa/v1/format/testfiles/v1/test_if_else.rego b/third_party/opa/v1/format/testfiles/v1/test_if_else.rego new file mode 100644 index 000000000000..e8949028734c --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_if_else.rego @@ -0,0 +1,18 @@ +package test["if"] + +p := 1 if { 1 > 0 } +else := 2 + +q := 1 if { 1 > 0 } else := 2 if { 2 > 1 } + +q := 1 if { + 1 > 0 + 2 > 1 +} else := 2 if { 2 > 1 } + +r := 1 if { + 1 > 0 +} +else := 2 if { + 2 > 1 +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_if_else.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_if_else.rego.formatted new file mode 100644 index 000000000000..361bcd62997c --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_if_else.rego.formatted @@ -0,0 +1,22 @@ +package test["if"] + +p := 1 if 1 > 0 + +else := 2 + +q := 1 if 1 > 0 + +else := 2 if 2 > 1 + +q := 1 if { + 1 > 0 + 2 > 1 +} else := 2 if 2 > 1 + +r := 1 if { + 1 > 0 +} + +else := 2 if { + 2 > 1 +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_in.rego b/third_party/opa/v1/format/testfiles/v1/test_in.rego new file mode 100644 index 000000000000..1397087c806e --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_in.rego @@ -0,0 +1,11 @@ +package test["in"] + +a["in"] := "foo" + +b.c["in"] := "bar" + +c["in"].d := "baz" + +p if { + input["in"] +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_in.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_in.rego.formatted new file mode 100644 index 000000000000..1397087c806e --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_in.rego.formatted @@ -0,0 +1,11 @@ +package test["in"] + +a["in"] := "foo" + +b.c["in"] := "bar" + +c["in"].d := "baz" + +p if { + input["in"] +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_in_operator_with_all_keywords_import.rego b/third_party/opa/v1/format/testfiles/v1/test_in_operator_with_all_keywords_import.rego new file mode 100644 index 000000000000..6c6e6611e81c --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_in_operator_with_all_keywords_import.rego @@ -0,0 +1,8 @@ +package p + +import input.foo + +r if { + internal.member_2(1, [1]) + internal.member_3(0, 1, [1]) +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_in_operator_with_all_keywords_import.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_in_operator_with_all_keywords_import.rego.formatted new file mode 100644 index 000000000000..17b92a9c4ad7 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_in_operator_with_all_keywords_import.rego.formatted @@ -0,0 +1,8 @@ +package p + +import input.foo + +r if { + 1 in [1] + 0, 1 in [1] +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_in_operator_with_parenthesis.rego b/third_party/opa/v1/format/testfiles/v1/test_in_operator_with_parenthesis.rego new file mode 100644 index 000000000000..ef3a5a342a55 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_in_operator_with_parenthesis.rego @@ -0,0 +1,17 @@ +package test + +z if { + { 1, 2 in [2, 2, 2] } + { 1, (1, 2 in [2, 2, 2]) } + { (x, x in [2]) | x := numbers.range(1,10)[_]} + { x: (x, x in [2]) | x := numbers.range(1,10)[_]} + { (x in [2]): 2 | x := numbers.range(1,10)[_]} + { (x, x in [2]): 2 | x := numbers.range(1,10)[_]} + { (1, 2 in [2, 2, 2]) } + { 1, (2 in [2, 2, 2]) } + f(1, 2 in [2, 2]) + g((1, 2 in [2, 2])) +} + +f(_, _) = true +g(_) = true \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_in_operator_with_parenthesis.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_in_operator_with_parenthesis.rego.formatted new file mode 100644 index 000000000000..ab1183698532 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_in_operator_with_parenthesis.rego.formatted @@ -0,0 +1,17 @@ +package test + +z if { + {1, 2 in [2, 2, 2]} + {1, (1, 2 in [2, 2, 2])} + {(x, x in [2]) | x := numbers.range(1, 10)[_]} + {x: (x, x in [2]) | x := numbers.range(1, 10)[_]} + {x in [2]: 2 | x := numbers.range(1, 10)[_]} + {(x, x in [2]): 2 | x := numbers.range(1, 10)[_]} + {(1, 2 in [2, 2, 2])} + {1, 2 in [2, 2, 2]} + f(1, 2 in [2, 2]) + g((1, 2 in [2, 2])) +} + +f(_, _) := true +g(_) := true diff --git a/third_party/opa/v1/format/testfiles/v1/test_in_operator_without_import.rego b/third_party/opa/v1/format/testfiles/v1/test_in_operator_without_import.rego new file mode 100644 index 000000000000..6c6e6611e81c --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_in_operator_without_import.rego @@ -0,0 +1,8 @@ +package p + +import input.foo + +r if { + internal.member_2(1, [1]) + internal.member_3(0, 1, [1]) +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_in_operator_without_import.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_in_operator_without_import.rego.formatted new file mode 100644 index 000000000000..17b92a9c4ad7 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_in_operator_without_import.rego.formatted @@ -0,0 +1,8 @@ +package p + +import input.foo + +r if { + 1 in [1] + 0, 1 in [1] +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_1560.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_1560.rego new file mode 100644 index 000000000000..cf7fa9877cdc --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_1560.rego @@ -0,0 +1,29 @@ +package x + +p if { + symbol + + # comment + some x +} + +p if { + symbol + + # comment + f(x) +} + +p if { + symbol + + # comment + not x +} + +p if { + symbol + + # comment + not f(x) +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_1560.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_1560.rego.formatted new file mode 100644 index 000000000000..fb1a36af3f13 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_1560.rego.formatted @@ -0,0 +1,29 @@ +package x + +p if { + symbol + + # comment + some x +} + +p if { + symbol + + # comment + f(x) +} + +p if { + symbol + + # comment + not x +} + +p if { + symbol + + # comment + not f(x) +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_2299.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_2299.rego new file mode 100644 index 000000000000..42aeac114df1 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_2299.rego @@ -0,0 +1,40 @@ +package foo + +# Compact else cases +authorize = "allow" if { + input.user == "superuser" +} else = "deny" if { + input.path[0] == "admin" + input.source_network == "external" +} + +# Newline separated else blocks +q = x if { + foo == "bar" +} + +else = y if { + foo == "baz" +} + +else = z if { + foo == "qux" +} + + +# Mixed compact and newline separated +p = x if { + foo == "bar" +} +# some special case +# with lots of comments +# describing it +else = y if { + bar == "foo" +} else = z if { + bar == "bar" +} + +else if { + bar == "baz" +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_2299.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_2299.rego.formatted new file mode 100644 index 000000000000..7424e3ee3834 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_2299.rego.formatted @@ -0,0 +1,40 @@ +package foo + +# Compact else cases +authorize := "allow" if { + input.user == "superuser" +} else := "deny" if { + input.path[0] == "admin" + input.source_network == "external" +} + +# Newline separated else blocks +q := x if { + foo == "bar" +} + +else := y if { + foo == "baz" +} + +else := z if { + foo == "qux" +} + +# Mixed compact and newline separated +p := x if { + foo == "bar" +} + +# some special case +# with lots of comments +# describing it +else := y if { + bar == "foo" +} else := z if { + bar == "bar" +} + +else if { + bar == "baz" +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_2420.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_2420.rego new file mode 100644 index 000000000000..8b784eee0310 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_2420.rego @@ -0,0 +1,11 @@ +package example + +allow if { + some_condition +} + +# some comments + +else if { + another_condition +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_2420.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_2420.rego.formatted new file mode 100644 index 000000000000..d9b298c46f90 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_2420.rego.formatted @@ -0,0 +1,11 @@ +package example + +allow if { + some_condition +} + +# some comments + +else if { + another_condition +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_3836.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_3836.rego new file mode 100644 index 000000000000..d42c88ae9636 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_3836.rego @@ -0,0 +1,6 @@ +package testcase + +rule1 = contains( + "", # first comment + "", # second comment +) \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_3836.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_3836.rego.formatted new file mode 100644 index 000000000000..532dfd4bb88d --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_3836.rego.formatted @@ -0,0 +1,6 @@ +package testcase + +rule1 := contains( + "", # first comment + "", # second comment +) diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_3849.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_3849.rego new file mode 100644 index 000000000000..db7ff7ba50d7 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_3849.rego @@ -0,0 +1,33 @@ +package test_issue_3849 + +test_require_context if { + require_context("monkey", "eat", "banana") with input as { + "principal": {"id": 101, "type": "monkey"}, + "action": "eat", + "entity": {"id": 102, "type": "banana"}, + } +} + +test_contrived if { + allow with input as { + "a": 101, + "b": 101, + "z": 101, + "y": 101, + "x": 101, + "w": 101, + "v": 101, + "u": 101, + "t": 101, + "s": 101, + "r": 101, + "q": 101, + "p": 101, + "o": 101, + "n": 101, + "j": 101, + "k": 101, + "l": 101, + "m": 101, + } +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_3849.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_3849.rego.formatted new file mode 100644 index 000000000000..0b4108fe575a --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_3849.rego.formatted @@ -0,0 +1,33 @@ +package test_issue_3849 + +test_require_context if { + require_context("monkey", "eat", "banana") with input as { + "principal": {"id": 101, "type": "monkey"}, + "action": "eat", + "entity": {"id": 102, "type": "banana"}, + } +} + +test_contrived if { + allow with input as { + "a": 101, + "b": 101, + "z": 101, + "y": 101, + "x": 101, + "w": 101, + "v": 101, + "u": 101, + "t": 101, + "s": 101, + "r": 101, + "q": 101, + "p": 101, + "o": 101, + "n": 101, + "j": 101, + "k": 101, + "l": 101, + "m": 101, + } +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_4606.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_4606.rego new file mode 100644 index 000000000000..1c7fe57da2c0 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_4606.rego @@ -0,0 +1,3 @@ +package p + +r if { 1 in [] } \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_4606.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_4606.rego.formatted new file mode 100644 index 000000000000..290a95617b43 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_4606.rego.formatted @@ -0,0 +1,3 @@ +package p + +r if 1 in [] diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5348.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_5348.rego new file mode 100644 index 000000000000..4c128fefe9ce --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5348.rego @@ -0,0 +1,5 @@ +package p + +f(x) if { + true +} else := false diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5348.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_5348.rego.formatted new file mode 100644 index 000000000000..4c128fefe9ce --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5348.rego.formatted @@ -0,0 +1,5 @@ +package p + +f(x) if { + true +} else := false diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5449.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_5449.rego new file mode 100644 index 000000000000..98b842401a64 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5449.rego @@ -0,0 +1,3 @@ +package demo + +foo["bar"] = "baz" if { input } diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5449.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_5449.rego.formatted new file mode 100644 index 000000000000..8596fdab1eb3 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5449.rego.formatted @@ -0,0 +1,3 @@ +package demo + +foo["bar"] := "baz" if input diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_contains_ref_rule.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_contains_ref_rule.rego new file mode 100644 index 000000000000..e5ab28992a7e --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_contains_ref_rule.rego @@ -0,0 +1,7 @@ +# This is the same as test_issue_5449.rego, but with another rule +# that gives the formatter the assurance that using ref rules is OK +package demo + +foo["bar"] = "baz" if { input } + +a.deep contains "ref" diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_contains_ref_rule.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_contains_ref_rule.rego.formatted new file mode 100644 index 000000000000..0e466534097b --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_contains_ref_rule.rego.formatted @@ -0,0 +1,7 @@ +# This is the same as test_issue_5449.rego, but with another rule +# that gives the formatter the assurance that using ref rules is OK +package demo + +foo.bar := "baz" if input + +a.deep contains "ref" diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_ref_rule.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_ref_rule.rego new file mode 100644 index 000000000000..9d6eac8a690c --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_ref_rule.rego @@ -0,0 +1,7 @@ +# This is the same as test_issue_5449.rego, but with a rule that gives +# the formatter the assurance that using ref rules is OK +package demo + +foo["bar"] = "baz" if { input } + +a.deep.ref := true diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_ref_rule.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_ref_rule.rego.formatted new file mode 100644 index 000000000000..b0a979c99336 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5449_with_ref_rule.rego.formatted @@ -0,0 +1,7 @@ +# This is the same as test_issue_5449.rego, but with a rule that gives +# the formatter the assurance that using ref rules is OK +package demo + +foo.bar := "baz" if input + +a.deep.ref := true diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_comprehension.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_comprehension.rego new file mode 100644 index 000000000000..abe0330eae62 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_comprehension.rego @@ -0,0 +1,3 @@ +package p + +array := [(input.thing[i] == input.other[i]) | true] diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_comprehension.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_comprehension.rego.formatted new file mode 100644 index 000000000000..abe0330eae62 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_comprehension.rego.formatted @@ -0,0 +1,3 @@ +package p + +array := [(input.thing[i] == input.other[i]) | true] diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_ref.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_ref.rego new file mode 100644 index 000000000000..93d4f1934a2c --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_ref.rego @@ -0,0 +1,8 @@ +package p + +first := {"one", "two"} +second := {"two", "three"} + +example contains msg if { + msg := (first | second)[_] +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_ref.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_ref.rego.formatted new file mode 100644 index 000000000000..e27b0568e159 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5537_with_ref.rego.formatted @@ -0,0 +1,8 @@ +package p + +first := {"one", "two"} +second := {"two", "three"} + +example contains msg if { + msg := (first | second)[_] +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5798.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_5798.rego new file mode 100644 index 000000000000..b35138420447 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5798.rego @@ -0,0 +1,9 @@ +package test + +rule01 = fail +if { + fail = { # this + x | # panics + set[x]; f(x) + } +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_5798.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_5798.rego.formatted new file mode 100644 index 000000000000..df21ad6b80e7 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_5798.rego.formatted @@ -0,0 +1,9 @@ +package test + +rule01 := fail if { + fail = { # this + x | # panics + set[x] + f(x) + } +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_6161.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_6161.rego new file mode 100644 index 000000000000..e78a9bb48cd9 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_6161.rego @@ -0,0 +1,8 @@ +package p + +# this is a comment with trailing whitespace + +allow if { + # another comment with trailing whitespace + 1 == 1 +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_6161.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_6161.rego.formatted new file mode 100644 index 000000000000..e0c5d68b2cca --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_6161.rego.formatted @@ -0,0 +1,8 @@ +package p + +# this is a comment with trailing whitespace + +allow if { + # another comment with trailing whitespace + 1 == 1 +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_6330.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_6330.rego new file mode 100644 index 000000000000..14b738959f19 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_6330.rego @@ -0,0 +1,135 @@ +package a + +p[ +{"a": # +"b"} # +] := true + +value := {"a": +{"b": +{"c": +"d"}}} + +value := {"a": # test 1 +"b"} # test 2 + +value := {"a": # test 1 +"b"} + +value := {"a": +{"b": +{"c": +"d"}}} + +value := {"a": # this is +{"b": # my ridiculous +{"c": # way of +"d"}}} # commenting code + +p := {"a": # +"b"} if { # + str := "my \n string" +} + +value := {"a": +{"b": +{"c": +"d"}}} + +f(_) := value if { + value := {"a": # this is + {"b": # my ridiculous + {"c": # way of + "d"}}} # commenting code +} + +p := value if { + value := {"a": # this is + {"b": # my ridiculous + {"c": # way of + "d"}}} # commenting code +} + +p := x if { + x := [v | v := {"a": # this is + {"b": # my ridiculous + {"c": # way of + "d"}}} # commenting code + ] +} + +p if { + every x in input.foo { + x == {"a": # this is + {"b": # my ridiculous + {"c": # way of + "d"}}} # commenting code + } +} + +value contains {"a": # +"b"} # + +p := {"a": # +str} if { + str := "my \n string" +} + +p := {"a": +str} if { + # + str := "my \n string" +} + +authorize := "allow" if { + value := {"a": # this is + {"b": # my ridiculous + {"c": # way of + "d"}}} # commenting code + input.user == "superuser" # allow 'superuser' to perform any operation. +} else := "deny" if { + value := {"a": # this is + {"b": # my ridiculous + {"c": # way of + "d"}}} # commenting code + input.path[0] == value # disallow 'admin' operations... + input.source_network == "external" # from external networks. +} # ... more rules + +p[ +{"a": # +"b"} # +] := true + +p.foo.bar[ +{"a": # +"b"} # +] := true + +p[ +{"a": # +"b"} # +][ +{"c": # +"d"} # +] := true + +p if { + x := {"a": # do + "b"} # re + 1 + 2 == 3 + y := {"c": # mi + "d"} # fa + x != y +} + +p[x].r := y if { + x := "q" + y := 1 + y := {"c": # hello +"d"} # world + + + + y := 1 +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_6330.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_6330.rego.formatted new file mode 100644 index 000000000000..0f3a7ea9482f --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_6330.rego.formatted @@ -0,0 +1,116 @@ +package a + +p[{"a": # +"b"} # +] := true + +value := {"a": {"b": {"c": "d"}}} + +value := {"a": # test 1 +"b"} # test 2 + +value := {"a": "b"} # test 1 + +value := {"a": {"b": {"c": "d"}}} + +value := {"a": # this is +{"b": # my ridiculous +{"c": # way of +"d"}}} # commenting code + +p := {"a": # +"b"} if { # + str := "my \n string" +} + +value := {"a": {"b": {"c": "d"}}} + +f(_) := value if { + value := {"a": # this is + {"b": # my ridiculous + {"c": # way of + "d"}}} # commenting code +} + +p := value if { + value := {"a": # this is + {"b": # my ridiculous + {"c": # way of + "d"}}} # commenting code +} + +p := x if { + x := [v | v := {"a": # this is + {"b": # my ridiculous + {"c": # way of + "d"}}} # commenting code + ] +} + +p if { + every x in input.foo { + x == {"a": # this is + {"b": # my ridiculous + {"c": # way of + "d"}}} # commenting code + } +} + +value contains {"a": # +"b"} # + +p := {"a": str} if { # + str := "my \n string" +} + +p := {"a": str} if { + # + str := "my \n string" +} + +authorize := "allow" if { + value := {"a": # this is + {"b": # my ridiculous + {"c": # way of + "d"}}} # commenting code + input.user == "superuser" # allow 'superuser' to perform any operation. +} else := "deny" if { + value := {"a": # this is + {"b": # my ridiculous + {"c": # way of + "d"}}} # commenting code + input.path[0] == value # disallow 'admin' operations... + input.source_network == "external" # from external networks. +} # ... more rules + +p[{"a": # +"b"} # +] := true + +p.foo.bar[{"a": # +"b"} # +] := true + +p[{"a": # +"b"} # +][{"c": # +"d"} # +] := true + +p if { + x := {"a": # do + "b"} # re + 1 + 2 == 3 + y := {"c": # mi + "d"} # fa + x != y +} + +p[x].r := y if { + x := "q" + y := 1 + y := {"c": # hello +"d"} # world + + y := 1 +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_6330_1.rego b/third_party/opa/v1/format/testfiles/v1/test_issue_6330_1.rego new file mode 100644 index 000000000000..414c3c152430 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_6330_1.rego @@ -0,0 +1,9 @@ +package a + +# A ref-head rule by itself is formatted differently when there are multiple ref-head rules in a file. +# This file handles the case when there is a single ref-head rule, while `test_issue_6330.rego` handles multiple. + +p[ +{"a": # +"b"} # +] := true \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_issue_6330_1.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_issue_6330_1.rego.formatted new file mode 100644 index 000000000000..72a0670d0926 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_issue_6330_1.rego.formatted @@ -0,0 +1,9 @@ +package a + +# A ref-head rule by itself is formatted differently when there are multiple ref-head rules in a file. +# This file handles the case when there is a single ref-head rule, while `test_issue_6330.rego` handles multiple. + +p[ +{"a": # +"b"} # +] := true diff --git a/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs.rego b/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs.rego new file mode 100644 index 000000000000..46f5a02f1601 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs.rego @@ -0,0 +1,227 @@ +package test.not.package.import.as.default.else.with.null.true.false.some.if.contains.in.every + +import data.not as foo1 +import data.package as foo2 +import data.import as foo3 +import data.as as foo4 +import data.default as foo5 +import data.else as foo6 +import data.with as foo7 +import data.null as foo8 +import data.true as foo9 +import data.false as foo10 +import data.some as foo11 +import data.if as foo12 +import data.contains as foo13 +import data.in as foo14 +import data.every as foo15 + +import data.not.bar1 +import data.package.bar2 +import data.import.bar3 +import data.as.bar4 +import data.default.bar5 +import data.else.bar6 +import data.with.bar7 +import data.null.bar8 +import data.true.bar9 +import data.false.bar10 +import data.some.bar11 +import data.if.bar12 +import data.contains.bar13 +import data.in.bar14 +import data.every.bar15 + +p if { + a.not == 1 + a.package == 1 + a.import == 1 + a.as == 1 + a.default == 1 + a.else == 1 + a.with == 1 + a.null == 1 + a.true == 1 + a.false == 1 + a.some == 1 + a.if == 1 + a.contains == 1 + a.in == 1 + a.every == 1 + + b.c.not == 1 + b.c.package == 1 + b.c.import == 1 + b.c.as == 1 + b.c.default == 1 + b.c.else == 1 + b.c.with == 1 + b.c.null == 1 + b.c.true == 1 + b.c.false == 1 + b.c.some == 1 + b.c.if == 1 + b.c.contains == 1 + b.c.in == 1 + b.c.every == 1 + + d.not.e == 1 + d.package.e == 1 + d.import.e == 1 + d.as.e == 1 + d.default.e == 1 + d.else.e == 1 + d.with.e == 1 + d.null.e == 1 + d.true.e == 1 + d.false.e == 1 + d.some.e == 1 + d.if.e == 1 + d.contains.e == 1 + d.in.e == 1 + d.every.e == 1 + + f.not(2) + f.package(2) + f.import(2) + f.as(2) + f.default(2) + f.else(2) + f.with(2) + f.null(2) + f.true(2) + f.false(2) + f.some(2) + f.if(2) + f.contains(2) + f.in(2) + f.every(2) + + g.h.not(2) + g.h.package(2) + g.h.import(2) + g.h.as(2) + g.h.default(2) + g.h.else(2) + g.h.with(2) + g.h.null(2) + g.h.true(2) + g.h.false(2) + g.h.some(2) + g.h.if(2) + g.h.contains(2) + g.h.in(2) + g.h.every(2) + + i.not.j(2) + i.package.j(2) + i.import.j(2) + i.as.j(2) + i.default.j(2) + i.else.j(2) + i.with.j(2) + i.null.j(2) + i.true.j(2) + i.false.j(2) + i.some.j(2) + i.if.j(2) + i.contains.j(2) + i.in.j(2) + i.every.j(2) +} + +a.not := 1 +a.package := 1 +a.import := 1 +a.as := 1 +a.default := 1 +a.else := 1 +a.with := 1 +a.null := 1 +a.true := 1 +a.false := 1 +a.some := 1 +a.if := 1 +a.contains := 1 +a.in := 1 +a.every := 1 + +b.c.not := 1 +b.c.package := 1 +b.c.import := 1 +b.c.as := 1 +b.c.default := 1 +b.c.else := 1 +b.c.with := 1 +b.c.null := 1 +b.c.true := 1 +b.c.false := 1 +b.c.some := 1 +b.c.if := 1 +b.c.contains := 1 +b.c.in := 1 +b.c.every := 1 + +d.not.e := 1 +d.package.e := 1 +d.import.e := 1 +d.as.e := 1 +d.default.e := 1 +d.else.e := 1 +d.with.e := 1 +d.null.e := 1 +d.true.e := 1 +d.false.e := 1 +d.some.e := 1 +d.if.e := 1 +d.contains.e := 1 +d.in.e := 1 +d.every.e := 1 + +f.not(x) := x +f.package(x) := x +f.import(x) := x +f.as(x) := x +f.default(x) := x +f.else(x) := x +f.with(x) := x +f.null(x) := x +f.true(x) := x +f.false(x) := x +f.some(x) := x +f.if(x) := x +f.contains(x) := x +f.in(x) := x +f.every(x) := x + +g.h.not(x) := x +g.h.package(x) := x +g.h.import(x) := x +g.h.as(x) := x +g.h.default(x) := x +g.h.else(x) := x +g.h.with(x) := x +g.h.null(x) := x +g.h.true(x) := x +g.h.false(x) := x +g.h.some(x) := x +g.h.if(x) := x +g.h.contains(x) := x +g.h.in(x) := x +g.h.every(x) := x + +i.not.j(x) := x +i.package.j(x) := x +i.import.j(x) := x +i.as.j(x) := x +i.default.j(x) := x +i.else.j(x) := x +i.with.j(x) := x +i.null.j(x) := x +i.true.j(x) := x +i.false.j(x) := x +i.some.j(x) := x +i.if.j(x) := x +i.contains.j(x) := x +i.in.j(x) := x +i.every.j(x) := x diff --git a/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs.rego.formatted new file mode 100644 index 000000000000..9f532db173b7 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs.rego.formatted @@ -0,0 +1,227 @@ +package test.not.package.import.as.default.else.with.null.true.false.some.if.contains.in.every + +import data.as as foo4 +import data.contains as foo13 +import data.default as foo5 +import data.else as foo6 +import data.every as foo15 +import data.false as foo10 +import data.if as foo12 +import data.import as foo3 +import data.in as foo14 +import data.not as foo1 +import data.null as foo8 +import data.package as foo2 +import data.some as foo11 +import data.true as foo9 +import data.with as foo7 + +import data.as.bar4 +import data.contains.bar13 +import data.default.bar5 +import data.else.bar6 +import data.every.bar15 +import data.false.bar10 +import data.if.bar12 +import data.import.bar3 +import data.in.bar14 +import data.not.bar1 +import data.null.bar8 +import data.package.bar2 +import data.some.bar11 +import data.true.bar9 +import data.with.bar7 + +p if { + a.not == 1 + a.package == 1 + a.import == 1 + a.as == 1 + a.default == 1 + a.else == 1 + a.with == 1 + a.null == 1 + a.true == 1 + a.false == 1 + a.some == 1 + a.if == 1 + a.contains == 1 + a.in == 1 + a.every == 1 + + b.c.not == 1 + b.c.package == 1 + b.c.import == 1 + b.c.as == 1 + b.c.default == 1 + b.c.else == 1 + b.c.with == 1 + b.c.null == 1 + b.c.true == 1 + b.c.false == 1 + b.c.some == 1 + b.c.if == 1 + b.c.contains == 1 + b.c.in == 1 + b.c.every == 1 + + d.not.e == 1 + d.package.e == 1 + d.import.e == 1 + d.as.e == 1 + d.default.e == 1 + d.else.e == 1 + d.with.e == 1 + d.null.e == 1 + d.true.e == 1 + d.false.e == 1 + d.some.e == 1 + d.if.e == 1 + d.contains.e == 1 + d.in.e == 1 + d.every.e == 1 + + f.not(2) + f.package(2) + f.import(2) + f.as(2) + f.default(2) + f.else(2) + f.with(2) + f.null(2) + f.true(2) + f.false(2) + f.some(2) + f.if(2) + f.contains(2) + f.in(2) + f.every(2) + + g.h.not(2) + g.h.package(2) + g.h.import(2) + g.h.as(2) + g.h.default(2) + g.h.else(2) + g.h.with(2) + g.h.null(2) + g.h.true(2) + g.h.false(2) + g.h.some(2) + g.h.if(2) + g.h.contains(2) + g.h.in(2) + g.h.every(2) + + i.not.j(2) + i.package.j(2) + i.import.j(2) + i.as.j(2) + i.default.j(2) + i.else.j(2) + i.with.j(2) + i.null.j(2) + i.true.j(2) + i.false.j(2) + i.some.j(2) + i.if.j(2) + i.contains.j(2) + i.in.j(2) + i.every.j(2) +} + +a.not := 1 +a.package := 1 +a.import := 1 +a.as := 1 +a.default := 1 +a.else := 1 +a.with := 1 +a.null := 1 +a.true := 1 +a.false := 1 +a.some := 1 +a.if := 1 +a.contains := 1 +a.in := 1 +a.every := 1 + +b.c.not := 1 +b.c.package := 1 +b.c.import := 1 +b.c.as := 1 +b.c.default := 1 +b.c.else := 1 +b.c.with := 1 +b.c.null := 1 +b.c.true := 1 +b.c.false := 1 +b.c.some := 1 +b.c.if := 1 +b.c.contains := 1 +b.c.in := 1 +b.c.every := 1 + +d.not.e := 1 +d.package.e := 1 +d.import.e := 1 +d.as.e := 1 +d.default.e := 1 +d.else.e := 1 +d.with.e := 1 +d.null.e := 1 +d.true.e := 1 +d.false.e := 1 +d.some.e := 1 +d.if.e := 1 +d.contains.e := 1 +d.in.e := 1 +d.every.e := 1 + +f.not(x) := x +f.package(x) := x +f.import(x) := x +f.as(x) := x +f.default(x) := x +f.else(x) := x +f.with(x) := x +f.null(x) := x +f.true(x) := x +f.false(x) := x +f.some(x) := x +f.if(x) := x +f.contains(x) := x +f.in(x) := x +f.every(x) := x + +g.h.not(x) := x +g.h.package(x) := x +g.h.import(x) := x +g.h.as(x) := x +g.h.default(x) := x +g.h.else(x) := x +g.h.with(x) := x +g.h.null(x) := x +g.h.true(x) := x +g.h.false(x) := x +g.h.some(x) := x +g.h.if(x) := x +g.h.contains(x) := x +g.h.in(x) := x +g.h.every(x) := x + +i.not.j(x) := x +i.package.j(x) := x +i.import.j(x) := x +i.as.j(x) := x +i.default.j(x) := x +i.else.j(x) := x +i.with.j(x) := x +i.null.j(x) := x +i.true.j(x) := x +i.false.j(x) := x +i.some.j(x) := x +i.if.j(x) := x +i.contains.j(x) := x +i.in.j(x) := x +i.every.j(x) := x diff --git a/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs.rego.formatted_no_keywords_in_refs b/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs.rego.formatted_no_keywords_in_refs new file mode 100644 index 000000000000..75037cec6f46 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs.rego.formatted_no_keywords_in_refs @@ -0,0 +1,227 @@ +package test["not"]["package"]["import"]["as"]["default"]["else"]["with"]["null"]["true"]["false"]["some"]["if"]["contains"]["in"]["every"] + +import data["as"] as foo4 +import data["contains"] as foo13 +import data["default"] as foo5 +import data["else"] as foo6 +import data["every"] as foo15 +import data["false"] as foo10 +import data["if"] as foo12 +import data["import"] as foo3 +import data["in"] as foo14 +import data["not"] as foo1 +import data["null"] as foo8 +import data["package"] as foo2 +import data["some"] as foo11 +import data["true"] as foo9 +import data["with"] as foo7 + +import data["as"].bar4 +import data["contains"].bar13 +import data["default"].bar5 +import data["else"].bar6 +import data["every"].bar15 +import data["false"].bar10 +import data["if"].bar12 +import data["import"].bar3 +import data["in"].bar14 +import data["not"].bar1 +import data["null"].bar8 +import data["package"].bar2 +import data["some"].bar11 +import data["true"].bar9 +import data["with"].bar7 + +p if { + a["not"] == 1 + a["package"] == 1 + a["import"] == 1 + a["as"] == 1 + a["default"] == 1 + a["else"] == 1 + a["with"] == 1 + a["null"] == 1 + a["true"] == 1 + a["false"] == 1 + a["some"] == 1 + a["if"] == 1 + a["contains"] == 1 + a["in"] == 1 + a["every"] == 1 + + b.c["not"] == 1 + b.c["package"] == 1 + b.c["import"] == 1 + b.c["as"] == 1 + b.c["default"] == 1 + b.c["else"] == 1 + b.c["with"] == 1 + b.c["null"] == 1 + b.c["true"] == 1 + b.c["false"] == 1 + b.c["some"] == 1 + b.c["if"] == 1 + b.c["contains"] == 1 + b.c["in"] == 1 + b.c["every"] == 1 + + d["not"].e == 1 + d["package"].e == 1 + d["import"].e == 1 + d["as"].e == 1 + d["default"].e == 1 + d["else"].e == 1 + d["with"].e == 1 + d["null"].e == 1 + d["true"].e == 1 + d["false"].e == 1 + d["some"].e == 1 + d["if"].e == 1 + d["contains"].e == 1 + d["in"].e == 1 + d["every"].e == 1 + + f["not"](2) + f["package"](2) + f["import"](2) + f["as"](2) + f["default"](2) + f["else"](2) + f["with"](2) + f["null"](2) + f["true"](2) + f["false"](2) + f["some"](2) + f["if"](2) + f["contains"](2) + f["in"](2) + f["every"](2) + + g.h["not"](2) + g.h["package"](2) + g.h["import"](2) + g.h["as"](2) + g.h["default"](2) + g.h["else"](2) + g.h["with"](2) + g.h["null"](2) + g.h["true"](2) + g.h["false"](2) + g.h["some"](2) + g.h["if"](2) + g.h["contains"](2) + g.h["in"](2) + g.h["every"](2) + + i["not"].j(2) + i["package"].j(2) + i["import"].j(2) + i["as"].j(2) + i["default"].j(2) + i["else"].j(2) + i["with"].j(2) + i["null"].j(2) + i["true"].j(2) + i["false"].j(2) + i["some"].j(2) + i["if"].j(2) + i["contains"].j(2) + i["in"].j(2) + i["every"].j(2) +} + +a["not"] := 1 +a["package"] := 1 +a["import"] := 1 +a["as"] := 1 +a["default"] := 1 +a["else"] := 1 +a["with"] := 1 +a["null"] := 1 +a["true"] := 1 +a["false"] := 1 +a["some"] := 1 +a["if"] := 1 +a["contains"] := 1 +a["in"] := 1 +a["every"] := 1 + +b.c["not"] := 1 +b.c["package"] := 1 +b.c["import"] := 1 +b.c["as"] := 1 +b.c["default"] := 1 +b.c["else"] := 1 +b.c["with"] := 1 +b.c["null"] := 1 +b.c["true"] := 1 +b.c["false"] := 1 +b.c["some"] := 1 +b.c["if"] := 1 +b.c["contains"] := 1 +b.c["in"] := 1 +b.c["every"] := 1 + +d["not"].e := 1 +d["package"].e := 1 +d["import"].e := 1 +d["as"].e := 1 +d["default"].e := 1 +d["else"].e := 1 +d["with"].e := 1 +d["null"].e := 1 +d["true"].e := 1 +d["false"].e := 1 +d["some"].e := 1 +d["if"].e := 1 +d["contains"].e := 1 +d["in"].e := 1 +d["every"].e := 1 + +f["not"](x) := x +f["package"](x) := x +f["import"](x) := x +f["as"](x) := x +f["default"](x) := x +f["else"](x) := x +f["with"](x) := x +f["null"](x) := x +f["true"](x) := x +f["false"](x) := x +f["some"](x) := x +f["if"](x) := x +f["contains"](x) := x +f["in"](x) := x +f["every"](x) := x + +g.h["not"](x) := x +g.h["package"](x) := x +g.h["import"](x) := x +g.h["as"](x) := x +g.h["default"](x) := x +g.h["else"](x) := x +g.h["with"](x) := x +g.h["null"](x) := x +g.h["true"](x) := x +g.h["false"](x) := x +g.h["some"](x) := x +g.h["if"](x) := x +g.h["contains"](x) := x +g.h["in"](x) := x +g.h["every"](x) := x + +i["not"].j(x) := x +i["package"].j(x) := x +i["import"].j(x) := x +i["as"].j(x) := x +i["default"].j(x) := x +i["else"].j(x) := x +i["with"].j(x) := x +i["null"].j(x) := x +i["true"].j(x) := x +i["false"].j(x) := x +i["some"].j(x) := x +i["if"].j(x) := x +i["contains"].j(x) := x +i["in"].j(x) := x +i["every"].j(x) := x diff --git a/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs_keep_brackets.rego b/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs_keep_brackets.rego new file mode 100644 index 000000000000..92f23d1a3eb8 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs_keep_brackets.rego @@ -0,0 +1,227 @@ +package test["not"]["package"]["import"]["as"]["default"]["else"]["with"]["null"]["true"]["false"]["some"]["if"]["contains"]["in"]["every"] + +import data["not"] as foo1 +import data["package"] as foo2 +import data["import"] as foo3 +import data["as"] as foo4 +import data["default"] as foo5 +import data["else"] as foo6 +import data["with"] as foo7 +import data["null"] as foo8 +import data["true"] as foo9 +import data["false"] as foo10 +import data["some"] as foo11 +import data["if"] as foo12 +import data["contains"] as foo13 +import data["in"] as foo14 +import data["every"] as foo15 + +import data["not"].bar1 +import data["package"].bar2 +import data["import"].bar3 +import data["as"].bar4 +import data["default"].bar5 +import data["else"].bar6 +import data["with"].bar7 +import data["null"].bar8 +import data["true"].bar9 +import data["false"].bar10 +import data["some"].bar11 +import data["if"].bar12 +import data["contains"].bar13 +import data["in"].bar14 +import data["every"].bar15 + +p if { + a["not"] == 1 + a["package"] == 1 + a["import"] == 1 + a["as"] == 1 + a["default"] == 1 + a["else"] == 1 + a["with"] == 1 + a["null"] == 1 + a["true"] == 1 + a["false"] == 1 + a["some"] == 1 + a["if"] == 1 + a["contains"] == 1 + a["in"] == 1 + a["every"] == 1 + + b.c["not"] == 1 + b.c["package"] == 1 + b.c["import"] == 1 + b.c["as"] == 1 + b.c["default"] == 1 + b.c["else"] == 1 + b.c["with"] == 1 + b.c["null"] == 1 + b.c["true"] == 1 + b.c["false"] == 1 + b.c["some"] == 1 + b.c["if"] == 1 + b.c["contains"] == 1 + b.c["in"] == 1 + b.c["every"] == 1 + + d["not"].e == 1 + d["package"].e == 1 + d["import"].e == 1 + d["as"].e == 1 + d["default"].e == 1 + d["else"].e == 1 + d["with"].e == 1 + d["null"].e == 1 + d["true"].e == 1 + d["false"].e == 1 + d["some"].e == 1 + d["if"].e == 1 + d["contains"].e == 1 + d["in"].e == 1 + d["every"].e == 1 + + f["not"](2) + f["package"](2) + f["import"](2) + f["as"](2) + f["default"](2) + f["else"](2) + f["with"](2) + f["null"](2) + f["true"](2) + f["false"](2) + f["some"](2) + f["if"](2) + f["contains"](2) + f["in"](2) + f["every"](2) + + g.h["not"](2) + g.h["package"](2) + g.h["import"](2) + g.h["as"](2) + g.h["default"](2) + g.h["else"](2) + g.h["with"](2) + g.h["null"](2) + g.h["true"](2) + g.h["false"](2) + g.h["some"](2) + g.h["if"](2) + g.h["contains"](2) + g.h["in"](2) + g.h["every"](2) + + i["not"].j(2) + i["package"].j(2) + i["import"].j(2) + i["as"].j(2) + i["default"].j(2) + i["else"].j(2) + i["with"].j(2) + i["null"].j(2) + i["true"].j(2) + i["false"].j(2) + i["some"].j(2) + i["if"].j(2) + i["contains"].j(2) + i["in"].j(2) + i["every"].j(2) +} + +a["not"] := 1 +a["package"] := 1 +a["import"] := 1 +a["as"] := 1 +a["default"] := 1 +a["else"] := 1 +a["with"] := 1 +a["null"] := 1 +a["true"] := 1 +a["false"] := 1 +a["some"] := 1 +a["if"] := 1 +a["contains"] := 1 +a["in"] := 1 +a["every"] := 1 + +b.c["not"] := 1 +b.c["package"] := 1 +b.c["import"] := 1 +b.c["as"] := 1 +b.c["default"] := 1 +b.c["else"] := 1 +b.c["with"] := 1 +b.c["null"] := 1 +b.c["true"] := 1 +b.c["false"] := 1 +b.c["some"] := 1 +b.c["if"] := 1 +b.c["contains"] := 1 +b.c["in"] := 1 +b.c["every"] := 1 + +d["not"].e := 1 +d["package"].e := 1 +d["import"].e := 1 +d["as"].e := 1 +d["default"].e := 1 +d["else"].e := 1 +d["with"].e := 1 +d["null"].e := 1 +d["true"].e := 1 +d["false"].e := 1 +d["some"].e := 1 +d["if"].e := 1 +d["contains"].e := 1 +d["in"].e := 1 +d["every"].e := 1 + +f["not"](x) := x +f["package"](x) := x +f["import"](x) := x +f["as"](x) := x +f["default"](x) := x +f["else"](x) := x +f["with"](x) := x +f["null"](x) := x +f["true"](x) := x +f["false"](x) := x +f["some"](x) := x +f["if"](x) := x +f["contains"](x) := x +f["in"](x) := x +f["every"](x) := x + +g.h["not"](x) := x +g.h["package"](x) := x +g.h["import"](x) := x +g.h["as"](x) := x +g.h["default"](x) := x +g.h["else"](x) := x +g.h["with"](x) := x +g.h["null"](x) := x +g.h["true"](x) := x +g.h["false"](x) := x +g.h["some"](x) := x +g.h["if"](x) := x +g.h["contains"](x) := x +g.h["in"](x) := x +g.h["every"](x) := x + +i["not"].j(x) := x +i["package"].j(x) := x +i["import"].j(x) := x +i["as"].j(x) := x +i["default"].j(x) := x +i["else"].j(x) := x +i["with"].j(x) := x +i["null"].j(x) := x +i["true"].j(x) := x +i["false"].j(x) := x +i["some"].j(x) := x +i["if"].j(x) := x +i["contains"].j(x) := x +i["in"].j(x) := x +i["every"].j(x) := x diff --git a/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs_keep_brackets.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs_keep_brackets.rego.formatted new file mode 100644 index 000000000000..75037cec6f46 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_keywords_in_refs_keep_brackets.rego.formatted @@ -0,0 +1,227 @@ +package test["not"]["package"]["import"]["as"]["default"]["else"]["with"]["null"]["true"]["false"]["some"]["if"]["contains"]["in"]["every"] + +import data["as"] as foo4 +import data["contains"] as foo13 +import data["default"] as foo5 +import data["else"] as foo6 +import data["every"] as foo15 +import data["false"] as foo10 +import data["if"] as foo12 +import data["import"] as foo3 +import data["in"] as foo14 +import data["not"] as foo1 +import data["null"] as foo8 +import data["package"] as foo2 +import data["some"] as foo11 +import data["true"] as foo9 +import data["with"] as foo7 + +import data["as"].bar4 +import data["contains"].bar13 +import data["default"].bar5 +import data["else"].bar6 +import data["every"].bar15 +import data["false"].bar10 +import data["if"].bar12 +import data["import"].bar3 +import data["in"].bar14 +import data["not"].bar1 +import data["null"].bar8 +import data["package"].bar2 +import data["some"].bar11 +import data["true"].bar9 +import data["with"].bar7 + +p if { + a["not"] == 1 + a["package"] == 1 + a["import"] == 1 + a["as"] == 1 + a["default"] == 1 + a["else"] == 1 + a["with"] == 1 + a["null"] == 1 + a["true"] == 1 + a["false"] == 1 + a["some"] == 1 + a["if"] == 1 + a["contains"] == 1 + a["in"] == 1 + a["every"] == 1 + + b.c["not"] == 1 + b.c["package"] == 1 + b.c["import"] == 1 + b.c["as"] == 1 + b.c["default"] == 1 + b.c["else"] == 1 + b.c["with"] == 1 + b.c["null"] == 1 + b.c["true"] == 1 + b.c["false"] == 1 + b.c["some"] == 1 + b.c["if"] == 1 + b.c["contains"] == 1 + b.c["in"] == 1 + b.c["every"] == 1 + + d["not"].e == 1 + d["package"].e == 1 + d["import"].e == 1 + d["as"].e == 1 + d["default"].e == 1 + d["else"].e == 1 + d["with"].e == 1 + d["null"].e == 1 + d["true"].e == 1 + d["false"].e == 1 + d["some"].e == 1 + d["if"].e == 1 + d["contains"].e == 1 + d["in"].e == 1 + d["every"].e == 1 + + f["not"](2) + f["package"](2) + f["import"](2) + f["as"](2) + f["default"](2) + f["else"](2) + f["with"](2) + f["null"](2) + f["true"](2) + f["false"](2) + f["some"](2) + f["if"](2) + f["contains"](2) + f["in"](2) + f["every"](2) + + g.h["not"](2) + g.h["package"](2) + g.h["import"](2) + g.h["as"](2) + g.h["default"](2) + g.h["else"](2) + g.h["with"](2) + g.h["null"](2) + g.h["true"](2) + g.h["false"](2) + g.h["some"](2) + g.h["if"](2) + g.h["contains"](2) + g.h["in"](2) + g.h["every"](2) + + i["not"].j(2) + i["package"].j(2) + i["import"].j(2) + i["as"].j(2) + i["default"].j(2) + i["else"].j(2) + i["with"].j(2) + i["null"].j(2) + i["true"].j(2) + i["false"].j(2) + i["some"].j(2) + i["if"].j(2) + i["contains"].j(2) + i["in"].j(2) + i["every"].j(2) +} + +a["not"] := 1 +a["package"] := 1 +a["import"] := 1 +a["as"] := 1 +a["default"] := 1 +a["else"] := 1 +a["with"] := 1 +a["null"] := 1 +a["true"] := 1 +a["false"] := 1 +a["some"] := 1 +a["if"] := 1 +a["contains"] := 1 +a["in"] := 1 +a["every"] := 1 + +b.c["not"] := 1 +b.c["package"] := 1 +b.c["import"] := 1 +b.c["as"] := 1 +b.c["default"] := 1 +b.c["else"] := 1 +b.c["with"] := 1 +b.c["null"] := 1 +b.c["true"] := 1 +b.c["false"] := 1 +b.c["some"] := 1 +b.c["if"] := 1 +b.c["contains"] := 1 +b.c["in"] := 1 +b.c["every"] := 1 + +d["not"].e := 1 +d["package"].e := 1 +d["import"].e := 1 +d["as"].e := 1 +d["default"].e := 1 +d["else"].e := 1 +d["with"].e := 1 +d["null"].e := 1 +d["true"].e := 1 +d["false"].e := 1 +d["some"].e := 1 +d["if"].e := 1 +d["contains"].e := 1 +d["in"].e := 1 +d["every"].e := 1 + +f["not"](x) := x +f["package"](x) := x +f["import"](x) := x +f["as"](x) := x +f["default"](x) := x +f["else"](x) := x +f["with"](x) := x +f["null"](x) := x +f["true"](x) := x +f["false"](x) := x +f["some"](x) := x +f["if"](x) := x +f["contains"](x) := x +f["in"](x) := x +f["every"](x) := x + +g.h["not"](x) := x +g.h["package"](x) := x +g.h["import"](x) := x +g.h["as"](x) := x +g.h["default"](x) := x +g.h["else"](x) := x +g.h["with"](x) := x +g.h["null"](x) := x +g.h["true"](x) := x +g.h["false"](x) := x +g.h["some"](x) := x +g.h["if"](x) := x +g.h["contains"](x) := x +g.h["in"](x) := x +g.h["every"](x) := x + +i["not"].j(x) := x +i["package"].j(x) := x +i["import"].j(x) := x +i["as"].j(x) := x +i["default"].j(x) := x +i["else"].j(x) := x +i["with"].j(x) := x +i["null"].j(x) := x +i["true"].j(x) := x +i["false"].j(x) := x +i["some"].j(x) := x +i["if"].j(x) := x +i["contains"].j(x) := x +i["in"].j(x) := x +i["every"].j(x) := x diff --git a/third_party/opa/v1/format/testfiles/v1/test_ref_heads.rego b/third_party/opa/v1/format/testfiles/v1/test_ref_heads.rego new file mode 100644 index 000000000000..fbdaa8778d2e --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_ref_heads.rego @@ -0,0 +1,18 @@ +package test + +a.b.c = "d" if true +a.b.e = "f" if true +a.b.g contains x if some x in numbers.range(1, 3) +a.b.h[x] = 1 if x := "one" + +q[1] = y if true +r[x] if x := 10 +p.q.r[x] if x := 10 +p.q.r[2] if true + +g[h].i[j].k if { true } +g[h].i[j].k if { h := 1; j = 2 } +g[3].i[j].k = x if { j := 3; x = 4 } +g[h].i[j].k[l] if { true } +g[h].i[j].k[l] contains x if { x = "foo" } +g[h].i[j].k[l] contains x if { h := 5; j := 6; l = 7; x = "foo" } diff --git a/third_party/opa/v1/format/testfiles/v1/test_ref_heads.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_ref_heads.rego.formatted new file mode 100644 index 000000000000..ff28f75ea65e --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_ref_heads.rego.formatted @@ -0,0 +1,33 @@ +package test + +a.b.c := "d" +a.b.e := "f" +a.b.g contains x if some x in numbers.range(1, 3) +a.b.h[x] := 1 if x := "one" + +q[1] := y +r[x] if x := 10 +p.q.r[x] if x := 10 +p.q.r[2] := true + +g[h].i[j].k := true + +g[h].i[j].k if { + h := 1 + j = 2 +} + +g[3].i[j].k := x if { + j := 3 + x = 4 +} + +g[h].i[j].k[l] := true +g[h].i[j].k[l] contains x if x = "foo" + +g[h].i[j].k[l] contains x if { + h := 5 + j := 6 + l = 7 + x = "foo" +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_rego_v1.rego b/third_party/opa/v1/format/testfiles/v1/test_rego_v1.rego new file mode 100644 index 000000000000..6dbfde0a6ea3 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_rego_v1.rego @@ -0,0 +1,27 @@ +package example + +import rego.v1 # rego.v1 import kept for broadest compatibility surface +import future.keywords.if # future.keywords imports are dropped, as they're covered by rego.v1 + +# R1: constant +a := 1 + +# R2: set +b contains "c" + +# R3: boolean +c.d.e := true + +# R4: set +d contains x if { + x := "e" +} + +# R5: boolean +e.f[x] if { + x := "g" +} + +f if true in [true, false] + +g if every x in [1, 2, 3] { x < 4 } diff --git a/third_party/opa/v1/format/testfiles/v1/test_rego_v1.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_rego_v1.rego.formatted new file mode 100644 index 000000000000..d7558fd545b3 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_rego_v1.rego.formatted @@ -0,0 +1,28 @@ +package example + +import rego.v1 # rego.v1 import kept for broadest compatibility surface + +# future.keywords imports are dropped, as they're covered by rego.v1 + +# R1: constant +a := 1 + +# R2: set +b contains "c" + +# R3: boolean +c.d.e := true + +# R4: set +d contains x if { + x := "e" +} + +# R5: boolean +e.f[x] if { + x := "g" +} + +f if true in [true, false] + +g if every x in [1, 2, 3] { x < 4 } diff --git a/third_party/opa/v1/format/testfiles/v1/test_unicode.rego b/third_party/opa/v1/format/testfiles/v1/test_unicode.rego new file mode 100644 index 000000000000..145466fc0051 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_unicode.rego @@ -0,0 +1,22 @@ +package test + +x := "\u0000" +x := "\u0000 \"" + +authorize = "\u0000" if { + input.user == "\u0000" +} else = "\u0000" if { + input.path[0] == "\u0000" + input.source_network == "\u0000" +} + +_fg := { + "black": "\u001b[30m", + "red": "\u001b[31m", + "green": "\u001b[32m", + "yellow": "\u001b[33m", + "blue": "\u001b[34m", + "magenta": "\u001b[35m", + "cyan": "\u001b[36m", + "white": "\u001b[37m", +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_unicode.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_unicode.rego.formatted new file mode 100644 index 000000000000..09394c59064c --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_unicode.rego.formatted @@ -0,0 +1,22 @@ +package test + +x := "\u0000" +x := "\u0000 \"" + +authorize := "\u0000" if { + input.user == "\u0000" +} else := "\u0000" if { + input.path[0] == "\u0000" + input.source_network == "\u0000" +} + +_fg := { + "black": "\u001b[30m", + "red": "\u001b[31m", + "green": "\u001b[32m", + "yellow": "\u001b[33m", + "blue": "\u001b[34m", + "magenta": "\u001b[35m", + "cyan": "\u001b[36m", + "white": "\u001b[37m", +} diff --git a/third_party/opa/v1/format/testfiles/v1/test_with.rego b/third_party/opa/v1/format/testfiles/v1/test_with.rego new file mode 100644 index 000000000000..d7c775b58370 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_with.rego @@ -0,0 +1,38 @@ +package p + +single_line_with if { + fn(1) with input.a as "a" +} + +multi_line_with if { + fn(1) with input.a as "a" + with input.b as "b" + with input.c as { + "foo": "bar", + } + with input.d as [ + 1, + 2, + 3] +} + +mixed_new_lines_with if { + true with input.a as "a" + with input.b as "b" with input.c as "c" + with input.d as "d" +} + +mock_f(_) = 123 + +func_replacements if { + count(array.concat(input.x, [])) with input.x as "foo" + with array.concat as true + with count as mock_f +} + +original(x) = x+1 + +more_func_replacements if { + original(1) with original as mock_f + original(1) with original as 1234 +} \ No newline at end of file diff --git a/third_party/opa/v1/format/testfiles/v1/test_with.rego.formatted b/third_party/opa/v1/format/testfiles/v1/test_with.rego.formatted new file mode 100644 index 000000000000..eb960c701914 --- /dev/null +++ b/third_party/opa/v1/format/testfiles/v1/test_with.rego.formatted @@ -0,0 +1,37 @@ +package p + +single_line_with if { + fn(1) with input.a as "a" +} + +multi_line_with if { + fn(1) with input.a as "a" + with input.b as "b" + with input.c as {"foo": "bar"} + with input.d as [ + 1, + 2, + 3, + ] +} + +mixed_new_lines_with if { + true with input.a as "a" + with input.b as "b" with input.c as "c" + with input.d as "d" +} + +mock_f(_) := 123 + +func_replacements if { + count(array.concat(input.x, [])) with input.x as "foo" + with array.concat as true + with count as mock_f +} + +original(x) := x + 1 + +more_func_replacements if { + original(1) with original as mock_f + original(1) with original as 1234 +} diff --git a/third_party/opa/v1/hooks/hooks.go b/third_party/opa/v1/hooks/hooks.go new file mode 100644 index 000000000000..cb756e5020f4 --- /dev/null +++ b/third_party/opa/v1/hooks/hooks.go @@ -0,0 +1,97 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package hooks + +import ( + "context" + "fmt" + + "github.com/open-policy-agent/opa/v1/config" + topdown_cache "github.com/open-policy-agent/opa/v1/topdown/cache" +) + +// Hook is a hook to be called in some select places in OPA's operation. +// +// The base Hook interface is any, and wherever a hook can occur, the calling code +// will check if your hook implements an appropriate interface. If so, your hook +// is called. +// +// This allows you to only hook in to behavior you care about, and it allows the +// OPA to add more hooks in the future. +// +// All hook interfaces in this package have Hook in the name. Hooks must be safe +// for concurrent use. It is expected that hooks are fast; if a hook needs to take +// time, then copy what you need and ensure the hook is async. +// +// When multiple instances of a hook are provided, they are all going to be executed +// in an unspecified order (it's a map-range call underneath). If you need hooks to +// be run in order, you can wrap them into another hook, and configure that one. +type Hook any + +// Hooks is the type used for every struct in OPA that can work with hooks. +type Hooks struct { + m map[Hook]struct{} // we are NOT providing a stable invocation ordering +} + +// New creates a new instance of Hooks. +func New(hs ...Hook) Hooks { + h := Hooks{m: make(map[Hook]struct{}, len(hs))} + for i := range hs { + h.m[hs[i]] = struct{}{} + } + return h +} + +func (hs Hooks) Each(fn func(Hook)) { + for h := range hs.m { + fn(h) + } +} + +func (hs Hooks) Len() int { + return len(hs.m) +} + +// ConfigHook allows inspecting or rewriting the configuration when the plugin +// manager is processing it. +// Note that this hook is not run when the plugin manager is reconfigured. This +// usually only happens when there's a new config from a discovery bundle, and +// for processing _that_, there's `ConfigDiscoveryHook`. +type ConfigHook interface { + OnConfig(context.Context, *config.Config) (*config.Config, error) +} + +// ConfigHook allows inspecting or rewriting the discovered configuration when +// the discovery plugin is processing it. +type ConfigDiscoveryHook interface { + OnConfigDiscovery(context.Context, *config.Config) (*config.Config, error) +} + +// InterQueryCacheHook allows access to the server's inter-query cache instance. +// It's useful for out-of-tree handlers that also need to evaluate something. +// Using this hook, they can share the caches with the rest of OPA. +type InterQueryCacheHook interface { + OnInterQueryCache(context.Context, topdown_cache.InterQueryCache) error +} + +// InterQueryValueCacheHook allows access to the server's inter-query value cache +// instance. +type InterQueryValueCacheHook interface { + OnInterQueryValueCache(context.Context, topdown_cache.InterQueryValueCache) error +} + +func (hs Hooks) Validate() error { + for h := range hs.m { + switch h.(type) { + case InterQueryCacheHook, + InterQueryValueCacheHook, + ConfigHook, + ConfigDiscoveryHook: // OK + default: + return fmt.Errorf("unknown hook type %T", h) + } + } + return nil +} diff --git a/third_party/opa/v1/ir/encoding/encoding_test.go b/third_party/opa/v1/ir/encoding/encoding_test.go new file mode 100644 index 000000000000..5712b012a037 --- /dev/null +++ b/third_party/opa/v1/ir/encoding/encoding_test.go @@ -0,0 +1,95 @@ +package planner + +import ( + "bytes" + "encoding/json" + "testing" + + "github.com/open-policy-agent/opa/internal/planner" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ir" +) + +func TestRoundTrip(t *testing.T) { + tests := []struct { + note string + modules map[string]string + }{ + { + note: "simple", + modules: map[string]string{ + "test.rego": ` + package test + p if { + input.foo == 7 + } + `, + }, + }, + { + note: "every", + modules: map[string]string{ + "test.rego": ` + package test + p if { + every i in input.foo { i > 0 } + } + `, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + // Note: v1 module + c, err := ast.CompileModules(tc.modules) + + if err != nil { + t.Fatal(err) + } + + modules := []*ast.Module{} + + for _, m := range c.Modules { + modules = append(modules, m) + } + + planner := planner.New(). + WithQueries([]planner.QuerySet{ + { + Name: "main", + Queries: []ast.Body{ + ast.MustParseBody("data.test.p = true"), + }, + }, + }). + WithModules(modules). + WithBuiltinDecls(ast.BuiltinMap) + + plan, err := planner.Plan() + if err != nil { + t.Fatal(err) + } + + bs, err := json.MarshalIndent(plan, "", " ") + if err != nil { + t.Fatal(err) + } + + var cpy ir.Policy + err = json.Unmarshal(bs, &cpy) + if err != nil { + t.Fatal(err) + } + + bs2, err := json.MarshalIndent(plan, "", " ") + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(bs, bs2) { + t.Fatal("expected bytes to be equal") + } + }) + } +} diff --git a/third_party/opa/v1/ir/ir.go b/third_party/opa/v1/ir/ir.go new file mode 100644 index 000000000000..3657a9b673e9 --- /dev/null +++ b/third_party/opa/v1/ir/ir.go @@ -0,0 +1,486 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package ir defines an intermediate representation (IR) for Rego. +// +// The IR specifies an imperative execution model for Rego policies similar to a +// query plan in traditional databases. +package ir + +import ( + "fmt" + + "github.com/open-policy-agent/opa/v1/types" +) + +type ( + // Policy represents a planned policy query. + Policy struct { + Static *Static `json:"static,omitempty"` + Plans *Plans `json:"plans,omitempty"` + Funcs *Funcs `json:"funcs,omitempty"` + } + + // Static represents a static data segment that is indexed into by the policy. + Static struct { + Strings []*StringConst `json:"strings,omitempty"` + BuiltinFuncs []*BuiltinFunc `json:"builtin_funcs,omitempty"` + Files []*StringConst `json:"files,omitempty"` + } + + // BuiltinFunc represents a built-in function that may be required by the + // policy. + BuiltinFunc struct { + Name string `json:"name"` + Decl *types.Function `json:"decl"` + } + + // Plans represents a collection of named query plans to expose in the policy. + Plans struct { + Plans []*Plan `json:"plans"` + } + + // Funcs represents a collection of planned functions to include in the + // policy. + Funcs struct { + Funcs []*Func `json:"funcs"` + } + + // Func represents a named plan (function) that can be invoked. Functions + // accept one or more parameters and return a value. By convention, the + // input document and data documents are always passed as the first and + // second arguments (respectively). + Func struct { + Name string `json:"name"` + Params []Local `json:"params"` + Return Local `json:"return"` + Blocks []*Block `json:"blocks"` // TODO(tsandall): should this be a plan? + Path []string `json:"path,omitempty"` // optional: if non-nil, include in data function tree + } + + // Plan represents an ordered series of blocks to execute. Plan execution + // stops when a return statement is reached. Blocks are executed in-order. + Plan struct { + Name string `json:"name"` + Blocks []*Block `json:"blocks"` + } + + // Block represents an ordered sequence of statements to execute. Blocks are + // executed until a return statement is encountered, a statement is undefined, + // or there are no more statements. If all statements are defined but no return + // statement is encountered, the block is undefined. + Block struct { + Stmts []Stmt `json:"stmts"` + } + + // Stmt represents an operation (e.g., comparison, loop, dot, etc.) to execute. + Stmt interface { + locationStmt + } + + locationStmt interface { + SetLocation(index, row, col int, file, text string) + GetLocation() *Location + } + + // Local represents a plan-scoped variable. + // + // TODO(tsandall): should this be int32 for safety? + Local int + + // StringConst represents a string value. + StringConst struct { + Value string `json:"value"` + } +) + +const ( + // Input is the local variable that refers to the global input document. + Input Local = iota + + // Data is the local variable that refers to the global data document. + Data + + // Unused is the free local variable that can be allocated in a plan. + Unused +) + +func (*Policy) String() string { + return "Policy" +} + +func (a *Static) String() string { + return fmt.Sprintf("Static (%d strings, %d files)", len(a.Strings), len(a.Files)) +} + +func (a *Funcs) String() string { + return fmt.Sprintf("Funcs (%d funcs)", len(a.Funcs)) +} + +func (a *Func) String() string { + return fmt.Sprintf("%v (%d params: %v, %d blocks, path: %v)", a.Name, len(a.Params), a.Params, len(a.Blocks), a.Path) +} + +func (a *Plan) String() string { + return fmt.Sprintf("Plan %v (%d blocks)", a.Name, len(a.Blocks)) +} + +func (a *Block) String() string { + return fmt.Sprintf("Block (%d statements)", len(a.Stmts)) +} + +// Operand represents a value that a statement operates on. +type Operand struct { + Value Val `json:"value"` +} + +// Val represents an abstract value that statements operate on. There are currently +// 3 types of values: +// +// 1. Local - a local variable that can refer to any type. +// 2. StringIndex - a string constant that refers to a compiled string. +// 3. Bool - a boolean constant. +type Val interface { + fmt.Stringer + typeHint() string +} + +func (Local) typeHint() string { return "local" } +func (l Local) String() string { + return fmt.Sprintf("Local<%d>", int(l)) +} + +// StringIndex represents the index into the plan's list of constant strings +// of a constant string. +type StringIndex int + +func (StringIndex) typeHint() string { return "string_index" } +func (s StringIndex) String() string { + return fmt.Sprintf("String<%d>", int(s)) +} + +// Bool represents a constant boolean. +type Bool bool + +func (Bool) typeHint() string { return "bool" } +func (b Bool) String() string { + return fmt.Sprintf("Bool<%v>", bool(b)) +} + +// ReturnLocalStmt represents a return statement that yields a local value. +type ReturnLocalStmt struct { + Source Local `json:"source"` + + Location +} + +// CallStmt represents a named function call. The result should be stored in the +// result local. +type CallStmt struct { + Func string `json:"func"` + Args []Operand `json:"args"` + Result Local `json:"result"` + + Location +} + +// CallDynamicStmt represents an indirect (data) function call. The result should +// be stored in the result local. +type CallDynamicStmt struct { + Args []Local `json:"args"` + Result Local `json:"result"` + Path []Operand `json:"path"` + + Location +} + +// BlockStmt represents a nested block. Nested blocks and break statements can +// be used to short-circuit execution. +type BlockStmt struct { + Blocks []*Block `json:"blocks"` + + Location +} + +func (a *BlockStmt) String() string { + return fmt.Sprintf("BlockStmt (%d blocks) %v", len(a.Blocks), a.GetLocation()) +} + +// BreakStmt represents a jump out of the current block. The index specifies how +// many blocks to jump starting from zero (the current block). Execution will +// continue from the end of the block that is jumped to. +type BreakStmt struct { + Index uint32 `json:"index"` + + Location +} + +// DotStmt represents a lookup operation on a value (e.g., array, object, etc.) +// The source of a DotStmt may be a scalar value in which case the statement +// will be undefined. +type DotStmt struct { + Source Operand `json:"source"` + Key Operand `json:"key"` + Target Local `json:"target"` + + Location +} + +// LenStmt represents a length() operation on a local variable. The +// result is stored in the target local variable. +type LenStmt struct { + Source Operand `json:"source"` + Target Local `json:"target"` + + Location +} + +// ScanStmt represents a linear scan over a composite value. The +// source may be a scalar in which case the block will never execute. +type ScanStmt struct { + Source Local `json:"source"` + Key Local `json:"key"` + Value Local `json:"value"` + Block *Block `json:"block"` + + Location +} + +// NotStmt represents a negated statement. +type NotStmt struct { + Block *Block `json:"block"` + + Location +} + +// AssignIntStmt represents an assignment of an integer value to a +// local variable. +type AssignIntStmt struct { + Value int64 `json:"value"` + Target Local `json:"target"` + + Location +} + +// AssignVarStmt represents an assignment of one local variable to another. +type AssignVarStmt struct { + Source Operand `json:"source"` + Target Local `json:"target"` + + Location +} + +// AssignVarOnceStmt represents an assignment of one local variable to another. +// If the target is defined, execution aborts with a conflict error. +// +// TODO(tsandall): is there a better name for this? +type AssignVarOnceStmt struct { + Source Operand `json:"source"` + Target Local `json:"target"` + + Location +} + +// ResetLocalStmt resets a local variable to 0. +type ResetLocalStmt struct { + Target Local `json:"target"` + + Location +} + +// MakeNullStmt constructs a local variable that refers to a null value. +type MakeNullStmt struct { + Target Local `json:"target"` + + Location +} + +// MakeNumberIntStmt constructs a local variable that refers to an integer value. +type MakeNumberIntStmt struct { + Value int64 `json:"value"` + Target Local `json:"target"` + + Location +} + +// MakeNumberRefStmt constructs a local variable that refers to a number stored as a string. +type MakeNumberRefStmt struct { + Index int + Target Local `json:"target"` + + Location +} + +// MakeArrayStmt constructs a local variable that refers to an array value. +type MakeArrayStmt struct { + Capacity int32 `json:"capacity"` + Target Local `json:"target"` + + Location +} + +// MakeObjectStmt constructs a local variable that refers to an object value. +type MakeObjectStmt struct { + Target Local `json:"target"` + + Location +} + +// MakeSetStmt constructs a local variable that refers to a set value. +type MakeSetStmt struct { + Target Local `json:"target"` + + Location +} + +// EqualStmt represents an value-equality check of two local variables. +type EqualStmt struct { + A Operand `json:"a"` + B Operand `json:"b"` + + Location +} + +// NotEqualStmt represents a != check of two local variables. +type NotEqualStmt struct { + A Operand `json:"a"` + B Operand `json:"b"` + + Location +} + +// IsArrayStmt represents a dynamic type check on a local variable. +type IsArrayStmt struct { + Source Operand `json:"source"` + + Location +} + +// IsObjectStmt represents a dynamic type check on a local variable. +type IsObjectStmt struct { + Source Operand `json:"source"` + + 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"` + + Location +} + +// IsUndefinedStmt represents a check of whether local variable is undefined. +type IsUndefinedStmt struct { + Source Local `json:"source"` + + Location +} + +// ArrayAppendStmt represents a dynamic append operation of a value +// onto an array. +type ArrayAppendStmt struct { + Value Operand `json:"value"` + Array Local `json:"array"` + + Location +} + +// ObjectInsertStmt represents a dynamic insert operation of a +// key/value pair into an object. +type ObjectInsertStmt struct { + Key Operand `json:"key"` + Value Operand `json:"value"` + Object Local `json:"object"` + + Location +} + +// ObjectInsertOnceStmt represents a dynamic insert operation of a key/value +// pair into an object. If the key already exists and the value differs, +// execution aborts with a conflict error. +type ObjectInsertOnceStmt struct { + Key Operand `json:"key"` + Value Operand `json:"value"` + Object Local `json:"object"` + + Location +} + +// ObjectMergeStmt performs a recursive merge of two object values. If either of +// the locals refer to non-object values this operation will abort with a +// conflict error. Overlapping object keys are merged recursively. +type ObjectMergeStmt struct { + A Local `json:"a"` + B Local `json:"b"` + Target Local `json:"target"` + + Location +} + +// SetAddStmt represents a dynamic add operation of an element into a set. +type SetAddStmt struct { + Value Operand `json:"value"` + Set Local `json:"set"` + + Location +} + +// WithStmt replaces the Local or a portion of the document referred to by the +// Local with the Value and executes the contained block. If the Path is +// non-empty, the Value is upserted into the Local. If the intermediate nodes in +// the Local referred to by the Path do not exist, they will be created. When +// the WithStmt finishes the Local is reset to it's original value. +type WithStmt struct { + Local Local `json:"local"` + Path []int `json:"path"` + Value Operand `json:"value"` + Block *Block `json:"block"` + + Location +} + +// NopStmt adds a nop instruction. Useful during development and debugging only. +type NopStmt struct { + Location +} + +// ResultSetAddStmt adds a value into the result set returned by the query plan. +type ResultSetAddStmt struct { + Value Local `json:"value"` + + Location +} + +// Location records the filen index, and the row and column inside that file +// that a statement can be connected to. +type Location struct { + File int `json:"file"` // filename string constant index + Col int `json:"col"` + Row int `json:"row"` + file, text string // only used for debugging +} + +// SetLocation sets the Location for a given Stmt. +func (l *Location) SetLocation(index, row, col int, file, text string) { + *l = Location{ + File: index, + Row: row, + Col: col, + file: file, + text: text, + } +} + +// GetLocation returns a Stmt's Location. +func (l *Location) GetLocation() *Location { + return l +} diff --git a/third_party/opa/v1/ir/marshal.go b/third_party/opa/v1/ir/marshal.go new file mode 100644 index 000000000000..f792e2c1b697 --- /dev/null +++ b/third_party/opa/v1/ir/marshal.go @@ -0,0 +1,147 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ir + +import ( + "encoding/json" + "fmt" + "reflect" +) + +func (a *Block) MarshalJSON() ([]byte, error) { + var result typedBlock + result.Stmts = make([]typedStmt, len(a.Stmts)) + for i := range a.Stmts { + tpe := reflect.Indirect(reflect.ValueOf(a.Stmts[i])).Type().Name() + result.Stmts[i] = typedStmt{ + Type: tpe, + Stmt: a.Stmts[i], + } + } + return json.Marshal(result) +} + +func (a *Block) UnmarshalJSON(bs []byte) error { + var typed rawTypedBlock + if err := json.Unmarshal(bs, &typed); err != nil { + return err + } + a.Stmts = make([]Stmt, len(typed.Stmts)) + for i := range typed.Stmts { + var err error + a.Stmts[i], err = typed.Stmts[i].Unmarshal() + if err != nil { + return err + } + } + return nil +} + +func (a *Operand) MarshalJSON() ([]byte, error) { + var result typedOperand + result.Value = a.Value + result.Type = a.Value.typeHint() + return json.Marshal(result) +} + +func (a *Operand) UnmarshalJSON(bs []byte) error { + var typed rawTypedOperand + if err := json.Unmarshal(bs, &typed); err != nil { + return err + } + f, ok := valFactories[typed.Type] + if !ok { + return fmt.Errorf("unrecognized value type %q", typed.Type) + } + x := f() + if err := json.Unmarshal(typed.Value, &x); err != nil { + return err + } + a.Value = x + return nil +} + +type typedBlock struct { + Stmts []typedStmt `json:"stmts"` +} + +type typedStmt struct { + Type string `json:"type"` + Stmt Stmt `json:"stmt"` +} + +type rawTypedBlock struct { + Stmts []rawTypedStmt `json:"stmts"` +} + +type rawTypedStmt struct { + Type string `json:"type"` + Stmt json.RawMessage `json:"stmt"` +} + +func (raw rawTypedStmt) Unmarshal() (Stmt, error) { + f, ok := stmtFactories[raw.Type] + if !ok { + return nil, fmt.Errorf("unrecognized statement type %q", raw.Type) + } + x := f() + if err := json.Unmarshal(raw.Stmt, &x); err != nil { + return nil, err + } + return x, nil +} + +type rawTypedOperand struct { + Type string `json:"type"` + Value json.RawMessage `json:"value"` +} + +type typedOperand struct { + Type string `json:"type"` + Value Val `json:"value"` +} + +var stmtFactories = map[string]func() Stmt{ + "ReturnLocalStmt": func() Stmt { return &ReturnLocalStmt{} }, + "CallStmt": func() Stmt { return &CallStmt{} }, + "CallDynamicStmt": func() Stmt { return &CallDynamicStmt{} }, + "BlockStmt": func() Stmt { return &BlockStmt{} }, + "BreakStmt": func() Stmt { return &BreakStmt{} }, + "DotStmt": func() Stmt { return &DotStmt{} }, + "LenStmt": func() Stmt { return &LenStmt{} }, + "ScanStmt": func() Stmt { return &ScanStmt{} }, + "NotStmt": func() Stmt { return &NotStmt{} }, + "AssignIntStmt": func() Stmt { return &AssignIntStmt{} }, + "AssignVarStmt": func() Stmt { return &AssignVarStmt{} }, + "AssignVarOnceStmt": func() Stmt { return &AssignVarOnceStmt{} }, + "ResetLocalStmt": func() Stmt { return &ResetLocalStmt{} }, + "MakeNullStmt": func() Stmt { return &MakeNullStmt{} }, + "MakeNumberIntStmt": func() Stmt { return &MakeNumberIntStmt{} }, + "MakeNumberRefStmt": func() Stmt { return &MakeNumberRefStmt{} }, + "MakeArrayStmt": func() Stmt { return &MakeArrayStmt{} }, + "MakeObjectStmt": func() Stmt { return &MakeObjectStmt{} }, + "MakeSetStmt": func() Stmt { return &MakeSetStmt{} }, + "EqualStmt": func() Stmt { return &EqualStmt{} }, + "NotEqualStmt": func() Stmt { return &NotEqualStmt{} }, + "IsArrayStmt": func() Stmt { return &IsArrayStmt{} }, + "IsObjectStmt": func() Stmt { return &IsObjectStmt{} }, + "IsDefinedStmt": func() Stmt { return &IsDefinedStmt{} }, + "IsSetStmt": func() Stmt { return &IsSetStmt{} }, + "IsUndefinedStmt": func() Stmt { return &IsUndefinedStmt{} }, + "ArrayAppendStmt": func() Stmt { return &ArrayAppendStmt{} }, + "ObjectInsertStmt": func() Stmt { return &ObjectInsertStmt{} }, + "ObjectInsertOnceStmt": func() Stmt { return &ObjectInsertOnceStmt{} }, + "ObjectMergeStmt": func() Stmt { return &ObjectMergeStmt{} }, + "SetAddStmt": func() Stmt { return &SetAddStmt{} }, + "WithStmt": func() Stmt { return &WithStmt{} }, + "NopStmt": func() Stmt { return &NopStmt{} }, + "ResultSetAddStmt": func() Stmt { return &ResultSetAddStmt{} }, +} + +var valFactories = map[string]func() Val{ + "bool": func() Val { var x Bool; return &x }, + "string_index": func() Val { var x StringIndex; return &x }, + "local": func() Val { var x Local; return &x }, +} diff --git a/third_party/opa/v1/ir/pretty.go b/third_party/opa/v1/ir/pretty.go new file mode 100644 index 000000000000..53d7cbae8870 --- /dev/null +++ b/third_party/opa/v1/ir/pretty.go @@ -0,0 +1,44 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ir + +import ( + "fmt" + "io" + "strings" +) + +// Pretty writes a human-readable representation of an IR object to w. +func Pretty(w io.Writer, x any) error { + + pp := &prettyPrinter{ + depth: -1, + w: w, + } + return Walk(pp, x) +} + +type prettyPrinter struct { + depth int + w io.Writer +} + +func (pp *prettyPrinter) Before(_ any) { + pp.depth++ +} + +func (pp *prettyPrinter) After(_ any) { + pp.depth-- +} + +func (pp *prettyPrinter) Visit(x any) (Visitor, error) { + pp.writeIndent("%T %+v", x, x) + return pp, nil +} + +func (pp *prettyPrinter) writeIndent(f string, a ...any) { + pad := strings.Repeat("| ", pp.depth) + fmt.Fprintf(pp.w, pad+f+"\n", a...) +} diff --git a/third_party/opa/v1/ir/walk.go b/third_party/opa/v1/ir/walk.go new file mode 100644 index 000000000000..788f36cd8e33 --- /dev/null +++ b/third_party/opa/v1/ir/walk.go @@ -0,0 +1,93 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ir + +// Visitor defines the interface for visiting IR nodes. +type Visitor interface { + Before(x any) + Visit(x any) (Visitor, error) + After(x any) +} + +// Walk invokes the visitor for nodes under x. +func Walk(vis Visitor, x any) error { + impl := walkerImpl{ + vis: vis, + } + impl.walk(x) + return impl.err +} + +type walkerImpl struct { + vis Visitor + err error +} + +func (w *walkerImpl) walk(x any) { + if w.err != nil { // abort on error + return + } + if x == nil { + return + } + + prev := w.vis + w.vis.Before(x) + defer w.vis.After(x) + w.vis, w.err = w.vis.Visit(x) + if w.err != nil { + return + } else if w.vis == nil { + w.vis = prev + return + } + + switch x := x.(type) { + case *Policy: + w.walk(x.Static) + w.walk(x.Plans) + w.walk(x.Funcs) + case *Static: + for _, s := range x.Strings { + w.walk(s) + } + for _, f := range x.BuiltinFuncs { + w.walk(f) + } + for _, f := range x.Files { + w.walk(f) + } + case *Plans: + for _, pl := range x.Plans { + w.walk(pl) + } + case *Funcs: + for _, fn := range x.Funcs { + w.walk(fn) + } + case *Func: + for _, b := range x.Blocks { + w.walk(b) + } + case *Plan: + for _, b := range x.Blocks { + w.walk(b) + } + case *Block: + for _, s := range x.Stmts { + w.walk(s) + } + case *BlockStmt: + for _, b := range x.Blocks { + w.walk(b) + } + case *ScanStmt: + w.walk(x.Block) + case *NotStmt: + w.walk(x.Block) + case *WithStmt: + w.walk(x.Block) + } +} diff --git a/third_party/opa/v1/keys/keys.go b/third_party/opa/v1/keys/keys.go new file mode 100644 index 000000000000..fba7a9c9398e --- /dev/null +++ b/third_party/opa/v1/keys/keys.go @@ -0,0 +1,99 @@ +package keys + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/open-policy-agent/opa/v1/util" +) + +const defaultSigningAlgorithm = "RS256" + +var supportedAlgos = map[string]struct{}{ + "ES256": {}, "ES384": {}, "ES512": {}, + "HS256": {}, "HS384": {}, "HS512": {}, + "PS256": {}, "PS384": {}, "PS512": {}, + "RS256": {}, "RS384": {}, "RS512": {}, +} + +// IsSupportedAlgorithm true if provided alg is supported +func IsSupportedAlgorithm(alg string) bool { + _, ok := supportedAlgos[alg] + return ok +} + +// Config holds the keys used to sign or verify bundles and tokens +type Config struct { + Key string `json:"key"` + PrivateKey string `json:"private_key"` + Algorithm string `json:"algorithm"` + Scope string `json:"scope"` +} + +// Equal returns true if this key config is equal to the other. +func (k *Config) Equal(other *Config) bool { + return other != nil && *k == *other +} + +func (k *Config) validateAndInjectDefaults(id string) error { + if k.Key == "" && k.PrivateKey == "" { + return fmt.Errorf("invalid keys configuration: no keys provided for key ID %v", id) + } + + if k.Algorithm == "" { + k.Algorithm = defaultSigningAlgorithm + } + + if !IsSupportedAlgorithm(k.Algorithm) { + return fmt.Errorf("unsupported algorithm '%v'", k.Algorithm) + } + + return nil +} + +// NewKeyConfig return a new Config +func NewKeyConfig(key, alg, scope string) (*Config, error) { + var pubKey string + if _, err := os.Stat(key); err == nil { + bs, err := os.ReadFile(key) + if err != nil { + return nil, err + } + pubKey = string(bs) + } else if os.IsNotExist(err) { + pubKey = key + } else { + return nil, err + } + + return &Config{ + Key: pubKey, + Algorithm: alg, + Scope: scope, + }, nil +} + +// ParseKeysConfig returns a map containing the key and the signing algorithm +func ParseKeysConfig(raw json.RawMessage) (map[string]*Config, error) { + keys := map[string]*Config{} + var obj map[string]json.RawMessage + + if err := util.Unmarshal(raw, &obj); err == nil { + for k := range obj { + var keyConfig Config + if err = util.Unmarshal(obj[k], &keyConfig); err != nil { + return nil, err + } + + if err = keyConfig.validateAndInjectDefaults(k); err != nil { + return nil, err + } + + keys[k] = &keyConfig + } + } else { + return nil, err + } + return keys, nil +} diff --git a/third_party/opa/v1/keys/keys_test.go b/third_party/opa/v1/keys/keys_test.go new file mode 100644 index 000000000000..480ef9d9264d --- /dev/null +++ b/third_party/opa/v1/keys/keys_test.go @@ -0,0 +1,212 @@ +package keys + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "reflect" + "testing" + + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestParseKeysConfig(t *testing.T) { + + key := `-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7nJwME0QNM6g0Ou9Sylj lcIY4cnBcs8oWVHe74bJ7JTgYmDOk2CA14RE3wJNkUKERP/cRdesKDA/BToJXJUr oYvhjXxUYn+i3wK5vOGRY9WUtTF9paIIpIV4USUOwDh3ufhA9K3tyh+ZVsqn80em 0Lj2ME0EgScuk6u0/UYjjNvcmnQl+uDmghG8xBZh7TZW2+aceMwlb4LJIP36VRhg jKQGIxg2rW8ROXgJaFbNRCbiOUUqlq9SUZuhHo8TNOARXXxp9R4Fq7Cl7ZbwWtNP wAtM1y+Z+iyu/i91m0YLlU2XBOGLu9IA8IZjPlbCnk/SygpV9NNwTY9DSQ0QfXcP TGlsbFwzRzTlhH25wEl3j+2Ub9w/NX7Yo+j/Ei9eGZ8cq0bcvEwDeIo98HeNZWrL UUArayRYvh8zutOlzqehw8waFk9AxpfEp9oWekSz8gZw9OL773EhnglYxxjkPHNz k66CufLuTEf6uE9NLE5HnlQMbiqBFirIyAWGKyU3v2tphKvcogxmzzWA51p0GY0l GZvlLNt2NrJv2oGecyl3BLqHnBi+rGAosa/8XgfQT8RIk7YR/tDPDmPfaqSIc0po +NcHYEH82Yv+gfKSK++1fyssGCsSRJs8PFMuPGgv62fFrE/EHSsHJaNWojSYce/T rxm2RaHhw/8O4oKcfrbaRf8CAwEAAQ== -----END PUBLIC KEY-----` + + config := fmt.Sprintf(`{"foo": {"algorithm": "HS256", "key": "FdFYFzERwC2uCBB46pZQi4GG85LujR8obt-KWRBICVQ"}, + "bar": {"key": %v} + }`, key) + + tests := map[string]struct { + input string + result map[string]*Config + wantErr bool + err error + }{ + "valid_config_one_key": { + `{"foo": {"algorithm": "HS256", "key": "FdFYFzERwC2uCBB46pZQi4GG85LujR8obt-KWRBICVQ"}}`, + map[string]*Config{"foo": {Key: "FdFYFzERwC2uCBB46pZQi4GG85LujR8obt-KWRBICVQ", Algorithm: "HS256"}}, + false, nil, + }, + "valid_config_two_key": { + config, + map[string]*Config{ + "foo": {Key: "FdFYFzERwC2uCBB46pZQi4GG85LujR8obt-KWRBICVQ", Algorithm: "HS256"}, + "bar": {Key: key, Algorithm: "RS256"}, + }, + false, nil, + }, + "invalid_config_no_key": { + `{"foo": {"algorithm": "HS256"}}`, + nil, + true, errors.New("invalid keys configuration: no keys provided for key ID foo"), + }, + "valid_config_default_alg": { + `{"foo": {"key": "FdFYFzERwC2uCBB46pZQi4GG85LujR8obt-KWRBICVQ"}}`, + map[string]*Config{"foo": {Key: "FdFYFzERwC2uCBB46pZQi4GG85LujR8obt-KWRBICVQ", Algorithm: "RS256"}}, + false, nil, + }, + "invalid_raw_key_config": { + `{"bar": [1,2,3]}`, + nil, + true, errors.New("json: cannot unmarshal array into Go value of type keys.Config"), + }, + "invalid_raw_config": { + `[1,2,3]`, + nil, + true, errors.New("json: cannot unmarshal array into Go value of type map[string]json.RawMessage"), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + kc, err := ParseKeysConfig([]byte(tc.input)) + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if !reflect.DeepEqual(kc, tc.result) { + t.Fatalf("Expected key config %v but got %v", tc.result, kc) + } + }) + } +} + +func TestNewKeyConfig(t *testing.T) { + publicKey := `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9KaakMv1XKKDaSch3PFR +3a27oaHp1GNTTNqvb1ZaHZXp+wuhYDwc/MTE67x9GCifvQBWzEGorgTq7aisiOyl +vKifwz6/wQ+62WHKG/sqKn2Xikp3P63aBIPlZcHbkyyRmL62yeyuzYoGvLEYel+m +z5SiKGBwviSY0Th2L4e5sGJuk2HOut6emxDi+E2Fuuj5zokFJvIT6Urlq8f3h6+l +GeR6HUOXqoYVf7ff126GP7dticTVBgibxkkuJFmpvQSW6xmxruT4k6iwjzbZHY7P +ypZ/TdlnuGC1cOpAVyU7k32IJ9CRbt3nwEf5U54LRXLLQjFixWZHwKdDiMTF4ws0 ++wIDAQAB +-----END PUBLIC KEY-----` + + files := map[string]string{ + "public.pem": publicKey, + } + + test.WithTempFS(files, func(rootDir string) { + + kc, err := NewKeyConfig(filepath.Join(rootDir, "public.pem"), "RS256", "read") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + expected := &Config{ + Key: publicKey, + Algorithm: "RS256", + Scope: "read", + } + + if !reflect.DeepEqual(kc, expected) { + t.Fatalf("Expected key config %v but got %v", expected, kc) + } + + // secret provided on command-line + kc, err = NewKeyConfig(publicKey, "HS256", "") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + expected = &Config{ + Key: publicKey, + Algorithm: "HS256", + Scope: "", + } + + if !reflect.DeepEqual(kc, expected) { + t.Fatalf("Expected key config %v but got %v", expected, kc) + } + }) +} + +func TestNewKeyConfigFileError(t *testing.T) { + if os.Geteuid() == 0 { + t.Skip("cannot run as root") + } + publicKey := `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9KaakMv1XKKDaSch3PFR +3a27oaHp1GNTTNqvb1ZaHZXp+wuhYDwc/MTE67x9GCifvQBWzEGorgTq7aisiOyl +vKifwz6/wQ+62WHKG/sqKn2Xikp3P63aBIPlZcHbkyyRmL62yeyuzYoGvLEYel+m +z5SiKGBwviSY0Th2L4e5sGJuk2HOut6emxDi+E2Fuuj5zokFJvIT6Urlq8f3h6+l +GeR6HUOXqoYVf7ff126GP7dticTVBgibxkkuJFmpvQSW6xmxruT4k6iwjzbZHY7P +ypZ/TdlnuGC1cOpAVyU7k32IJ9CRbt3nwEf5U54LRXLLQjFixWZHwKdDiMTF4ws0 ++wIDAQAB +-----END PUBLIC KEY-----` + + files := map[string]string{ + "public.pem": publicKey, + } + + test.WithTempFS(files, func(rootDir string) { + + // simulate error while reading file + err := os.Chmod(filepath.Join(rootDir, "public.pem"), 0111) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, err = NewKeyConfig(filepath.Join(rootDir, "public.pem"), "RS256", "read") + if err == nil { + t.Fatal("Expected error but got nil") + } + }) +} + +func TestKeyConfigEqual(t *testing.T) { + tests := map[string]struct { + a *Config + b *Config + exp bool + }{ + "equal": { + &Config{ + Key: "foo", + Algorithm: "RS256", + Scope: "read", + }, + &Config{ + Key: "foo", + Algorithm: "RS256", + Scope: "read", + }, + true, + }, + "not_equal": { + &Config{ + Key: "foo", + Algorithm: "RS256", + Scope: "read", + }, + &Config{ + Key: "foo", + Algorithm: "RS256", + Scope: "write", + }, + false, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + actual := tc.a.Equal(tc.b) + + if actual != tc.exp { + t.Fatalf("Expected config equal result %v but got %v", tc.exp, actual) + } + }) + } +} diff --git a/third_party/opa/v1/loader/errors.go b/third_party/opa/v1/loader/errors.go new file mode 100644 index 000000000000..55b8e7dc4462 --- /dev/null +++ b/third_party/opa/v1/loader/errors.go @@ -0,0 +1,62 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package loader + +import ( + "fmt" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// Errors is a wrapper for multiple loader errors. +type Errors []error + +func (e Errors) Error() string { + if len(e) == 0 { + return "no error(s)" + } + if len(e) == 1 { + return "1 error occurred during loading: " + e[0].Error() + } + buf := make([]string, len(e)) + for i := range buf { + buf[i] = e[i].Error() + } + return fmt.Sprintf("%v errors occurred during loading:\n", len(e)) + strings.Join(buf, "\n") +} + +func (e *Errors) add(err error) { + if errs, ok := err.(ast.Errors); ok { + for i := range errs { + *e = append(*e, errs[i]) + } + } else { + *e = append(*e, err) + } +} + +type unsupportedDocumentType string + +func (path unsupportedDocumentType) Error() string { + return string(path) + ": document must be of type object" +} + +type unrecognizedFile string + +func (path unrecognizedFile) Error() string { + return string(path) + ": can't recognize file type" +} + +func isUnrecognizedFile(err error) bool { + _, ok := err.(unrecognizedFile) + return ok +} + +type mergeError string + +func (e mergeError) Error() string { + return string(e) + ": merge error" +} diff --git a/third_party/opa/v1/loader/extension/extension.go b/third_party/opa/v1/loader/extension/extension.go new file mode 100644 index 000000000000..905c48634289 --- /dev/null +++ b/third_party/opa/v1/loader/extension/extension.go @@ -0,0 +1,40 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package extension + +import ( + "sync" +) + +var pluginMtx sync.Mutex +var bundleExtensions map[string]Handler + +// Handler is used to unmarshal a byte slice of a registered extension +// EXPERIMENTAL: Please don't rely on this functionality, it may go +// away or change in the future. +type Handler func([]byte, any) error + +// RegisterExtension registers a Handler for a certain file extension, including +// the dot: ".json", not "json". +// EXPERIMENTAL: Please don't rely on this functionality, it may go +// away or change in the future. +func RegisterExtension(name string, handler Handler) { + pluginMtx.Lock() + defer pluginMtx.Unlock() + + if bundleExtensions == nil { + bundleExtensions = map[string]Handler{} + } + bundleExtensions[name] = handler +} + +// FindExtension ios used to look up a registered extension Handler +// EXPERIMENTAL: Please don't rely on this functionality, it may go +// away or change in the future. +func FindExtension(ext string) Handler { + pluginMtx.Lock() + defer pluginMtx.Unlock() + return bundleExtensions[ext] +} diff --git a/third_party/opa/v1/loader/extension/extension_test.go b/third_party/opa/v1/loader/extension/extension_test.go new file mode 100644 index 000000000000..e7c745e16e6f --- /dev/null +++ b/third_party/opa/v1/loader/extension/extension_test.go @@ -0,0 +1,56 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package extension_test + +import ( + "crypto/rand" + "errors" + "reflect" + "testing" + "testing/fstest" + + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/loader/extension" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestLoaderExtensionUnmarshal(t *testing.T) { + sentinelErr := errors.New("test handler called") + extension.RegisterExtension(".json", func([]byte, any) error { + return sentinelErr + }) + defer extension.RegisterExtension(".json", nil) + + bs := make([]byte, 128) + _, err := rand.Read(bs) + if err != nil { + t.Fatal(err) + } + var v any + if err := util.Unmarshal(bs, &v); err != sentinelErr { + t.Error(err) + } +} + +func TestLoaderExtensionBundle(t *testing.T) { + data := map[string]any{"foo": "bar"} + extension.RegisterExtension(".json", func(_ []byte, x any) error { + *(x.(*any)) = data + return nil + }) + defer extension.RegisterExtension(".json", nil) + + fs := fstest.MapFS{ + "data.json": {}, + } + ldr := loader.NewFileLoader().WithFS(fs) + res, err := ldr.All([]string{"."}) + if err != nil { + t.Error(err) + } + if exp, act := data, res.Documents; !reflect.DeepEqual(exp, act) { + t.Errorf("expected %v, got %v", exp, act) + } +} diff --git a/third_party/opa/v1/loader/filter/filter.go b/third_party/opa/v1/loader/filter/filter.go new file mode 100644 index 000000000000..f0554e0aa6fb --- /dev/null +++ b/third_party/opa/v1/loader/filter/filter.go @@ -0,0 +1,5 @@ +package filter + +import "io/fs" + +type LoaderFilter func(abspath string, info fs.FileInfo, depth int) bool diff --git a/third_party/opa/v1/loader/loader.go b/third_party/opa/v1/loader/loader.go new file mode 100644 index 000000000000..42a59d031f1d --- /dev/null +++ b/third_party/opa/v1/loader/loader.go @@ -0,0 +1,861 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package loader contains utilities for loading files into OPA. +package loader + +import ( + "bytes" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "strings" + + "sigs.k8s.io/yaml" + + fileurl "github.com/open-policy-agent/opa/internal/file/url" + "github.com/open-policy-agent/opa/internal/merge" + "github.com/open-policy-agent/opa/v1/ast" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/loader/extension" + "github.com/open-policy-agent/opa/v1/loader/filter" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/util" +) + +// Result represents the result of successfully loading zero or more files. +type Result struct { + Documents map[string]any + Modules map[string]*RegoFile + path []string +} + +// ParsedModules returns the parsed modules stored on the result. +func (l *Result) ParsedModules() map[string]*ast.Module { + modules := make(map[string]*ast.Module) + for _, module := range l.Modules { + modules[module.Name] = module.Parsed + } + return modules +} + +// Compiler returns a Compiler object with the compiled modules from this loader +// result. +func (l *Result) Compiler() (*ast.Compiler, error) { + compiler := ast.NewCompiler() + compiler.Compile(l.ParsedModules()) + if compiler.Failed() { + return nil, compiler.Errors + } + return compiler, nil +} + +// Store returns a Store object with the documents from this loader result. +func (l *Result) Store() (storage.Store, error) { + return l.StoreWithOpts() +} + +// StoreWithOpts returns a Store object with the documents from this loader result, +// instantiated with the passed options. +func (l *Result) StoreWithOpts(opts ...inmem.Opt) (storage.Store, error) { + return inmem.NewFromObjectWithOpts(l.Documents, opts...), nil +} + +// RegoFile represents the result of loading a single Rego source file. +type RegoFile struct { + Name string + Parsed *ast.Module + Raw []byte +} + +// Filter defines the interface for filtering files during loading. If the +// filter returns true, the file should be excluded from the result. +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(_ string, info fs.FileInfo, depth int) bool { + match, _ := filepath.Match(pattern, info.Name()) + return match && depth >= minDepth + } +} + +// FileLoader defines an interface for loading OPA data files +// and Rego policies. +type FileLoader interface { + All(paths []string) (*Result, error) + Filtered(paths []string, filter Filter) (*Result, error) + AsBundle(path string) (*bundle.Bundle, error) + WithReader(io.Reader) FileLoader + WithFS(fs.FS) FileLoader + WithMetrics(metrics.Metrics) FileLoader + WithFilter(Filter) FileLoader + WithBundleVerificationConfig(*bundle.VerificationConfig) FileLoader + WithSkipBundleVerification(bool) FileLoader + WithBundleLazyLoadingMode(bool) FileLoader + WithProcessAnnotation(bool) FileLoader + WithCapabilities(*ast.Capabilities) FileLoader + // Deprecated: Use SetOptions in the json package instead, where a longer description + // of why this is deprecated also can be found. + WithJSONOptions(*astJSON.Options) FileLoader + WithRegoVersion(ast.RegoVersion) FileLoader + WithFollowSymlinks(bool) FileLoader +} + +// NewFileLoader returns a new FileLoader instance. +func NewFileLoader() FileLoader { + return &fileLoader{ + metrics: metrics.New(), + files: make(map[string]bundle.FileInfo), + } +} + +type fileLoader struct { + metrics metrics.Metrics + filter Filter + bvc *bundle.VerificationConfig + skipVerify bool + bundleLazyLoading bool + files map[string]bundle.FileInfo + opts ast.ParserOptions + fsys fs.FS + reader io.Reader + followSymlinks bool +} + +// WithFS provides an fs.FS to use for loading files. You can pass nil to +// use plain IO calls (e.g. os.Open, os.Stat, etc.), this is the default +// behaviour. +func (fl *fileLoader) WithFS(fsys fs.FS) FileLoader { + fl.fsys = fsys + return fl +} + +// WithReader provides an io.Reader to use for loading the bundle tarball. +// An io.Reader passed via WithReader takes precedence over an fs.FS passed +// via WithFS. +func (fl *fileLoader) WithReader(rdr io.Reader) FileLoader { + fl.reader = rdr + return fl +} + +// WithMetrics provides the metrics instance to use while loading +func (fl *fileLoader) WithMetrics(m metrics.Metrics) FileLoader { + fl.metrics = m + return fl +} + +// WithFilter specifies the filter object to use to filter files while loading +func (fl *fileLoader) WithFilter(filter Filter) FileLoader { + fl.filter = filter + return fl +} + +// WithBundleVerificationConfig sets the key configuration used to verify a signed bundle +func (fl *fileLoader) WithBundleVerificationConfig(config *bundle.VerificationConfig) FileLoader { + fl.bvc = config + return fl +} + +// WithSkipBundleVerification skips verification of a signed bundle +func (fl *fileLoader) WithSkipBundleVerification(skipVerify bool) FileLoader { + fl.skipVerify = skipVerify + return fl +} + +// WithBundleLazyLoadingMode enables or disables bundle lazy loading mode +func (fl *fileLoader) WithBundleLazyLoadingMode(bundleLazyLoading bool) FileLoader { + fl.bundleLazyLoading = bundleLazyLoading + return fl +} + +// WithProcessAnnotation enables or disables processing of schema annotations on rules +func (fl *fileLoader) WithProcessAnnotation(processAnnotation bool) FileLoader { + fl.opts.ProcessAnnotation = processAnnotation + return fl +} + +// WithCapabilities sets the supported capabilities when loading the files +func (fl *fileLoader) WithCapabilities(caps *ast.Capabilities) FileLoader { + fl.opts.Capabilities = caps + return fl +} + +// WithJSONOptions sets the JSON options on the parser (now a no-op). +// +// Deprecated: Use SetOptions in the json package instead, where a longer description +// of why this is deprecated also can be found. +func (fl *fileLoader) WithJSONOptions(*astJSON.Options) FileLoader { + return fl +} + +// WithRegoVersion sets the ast.RegoVersion to use when parsing and compiling modules. +func (fl *fileLoader) WithRegoVersion(version ast.RegoVersion) FileLoader { + fl.opts.RegoVersion = version + return fl +} + +// WithFollowSymlinks enables or disables following symlinks when loading files +func (fl *fileLoader) WithFollowSymlinks(followSymlinks bool) FileLoader { + fl.followSymlinks = followSymlinks + return fl +} + +// All returns a Result object loaded (recursively) from the specified paths. +func (fl fileLoader) All(paths []string) (*Result, error) { + return fl.Filtered(paths, nil) +} + +// Filtered returns a Result object loaded (recursively) from the specified +// paths while applying the given filters. If any filter returns true, the +// file/directory is excluded. +func (fl fileLoader) Filtered(paths []string, filter Filter) (*Result, error) { + return all(fl.fsys, paths, filter, func(curr *Result, path string, depth int) error { + + var ( + bs []byte + err error + ) + if fl.fsys != nil { + bs, err = fs.ReadFile(fl.fsys, path) + } else { + bs, err = os.ReadFile(path) + } + if err != nil { + return err + } + + result, err := loadKnownTypes(path, bs, fl.metrics, fl.opts, fl.bundleLazyLoading) + if err != nil { + if !isUnrecognizedFile(err) { + return err + } + if depth > 0 { + return nil + } + result, err = loadFileForAnyType(path, bs, fl.metrics, fl.opts) + if err != nil { + return err + } + } + + return curr.merge(path, result) + }) +} + +// AsBundle loads a path as a bundle. If it is a single file +// it will be treated as a normal tarball bundle. If a directory +// is supplied it will be loaded as an unzipped bundle tree. +func (fl fileLoader) AsBundle(path string) (*bundle.Bundle, error) { + path, err := fileurl.Clean(path) + if err != nil { + return nil, err + } + + if err := checkForUNCPath(path); err != nil { + return nil, err + } + + var bundleLoader bundle.DirectoryLoader + var isDir bool + if fl.reader != nil { + bundleLoader = bundle.NewTarballLoaderWithBaseURL(fl.reader, path).WithFilter(fl.filter) + } else { + bundleLoader, isDir, err = GetBundleDirectoryLoaderFS(fl.fsys, path, fl.filter) + } + + if err != nil { + return nil, err + } + bundleLoader = bundleLoader.WithFollowSymlinks(fl.followSymlinks) + + br := bundle.NewCustomReader(bundleLoader). + WithMetrics(fl.metrics). + WithBundleVerificationConfig(fl.bvc). + WithSkipBundleVerification(fl.skipVerify). + WithLazyLoadingMode(fl.bundleLazyLoading). + WithProcessAnnotations(fl.opts.ProcessAnnotation). + WithCapabilities(fl.opts.Capabilities). + WithFollowSymlinks(fl.followSymlinks). + WithRegoVersion(fl.opts.RegoVersion). + WithLazyLoadingMode(fl.bundleLazyLoading). + WithBundleName(path) + + // For bundle directories add the full path in front of module file names + // to simplify debugging. + if isDir { + br.WithBaseDir(path) + } + + b, err := br.Read() + if err != nil { + err = fmt.Errorf("bundle %s: %w", path, err) + } + + return &b, err +} + +// GetBundleDirectoryLoader returns a bundle directory loader which can be used to load +// files in the directory +func GetBundleDirectoryLoader(path string) (bundle.DirectoryLoader, bool, error) { + return GetBundleDirectoryLoaderFS(nil, path, nil) +} + +// GetBundleDirectoryLoaderWithFilter returns a bundle directory loader which can be used to load +// files in the directory after applying the given filter. +func GetBundleDirectoryLoaderWithFilter(path string, filter Filter) (bundle.DirectoryLoader, bool, error) { + return GetBundleDirectoryLoaderFS(nil, path, filter) +} + +// GetBundleDirectoryLoaderFS returns a bundle directory loader which can be used to load +// files in the directory. +func GetBundleDirectoryLoaderFS(fsys fs.FS, path string, filter Filter) (bundle.DirectoryLoader, bool, error) { + path, err := fileurl.Clean(path) + if err != nil { + return nil, false, err + } + + if err := checkForUNCPath(path); err != nil { + return nil, false, err + } + + var fi fs.FileInfo + if fsys != nil { + fi, err = fs.Stat(fsys, path) + } else { + fi, err = os.Stat(path) + } + if err != nil { + return nil, false, fmt.Errorf("error reading %q: %s", path, err) + } + + var bundleLoader bundle.DirectoryLoader + if fi.IsDir() { + if fsys != nil { + bundleLoader = bundle.NewFSLoaderWithRoot(fsys, path) + } else { + bundleLoader = bundle.NewDirectoryLoader(path) + } + } else { + var fh fs.File + if fsys != nil { + fh, err = fsys.Open(path) + } else { + fh, err = os.Open(path) + } + if err != nil { + return nil, false, err + } + bundleLoader = bundle.NewTarballLoaderWithBaseURL(fh, path) + } + + if filter != nil { + bundleLoader = bundleLoader.WithFilter(filter) + } + return bundleLoader, fi.IsDir(), nil +} + +// FilteredPaths is the same as FilterPathsFS using the current diretory file +// system +func FilteredPaths(paths []string, filter Filter) ([]string, error) { + return FilteredPathsFS(nil, paths, filter) +} + +// FilteredPathsFS return a list of files from the specified +// paths while applying the given filters. If any filter returns true, the +// file/directory is excluded. +func FilteredPathsFS(fsys fs.FS, paths []string, filter Filter) ([]string, error) { + result := []string{} + + _, err := all(fsys, paths, filter, func(_ *Result, path string, _ int) error { + result = append(result, path) + return nil + }) + if err != nil { + return nil, err + } + return result, nil +} + +// Schemas loads a schema set from the specified file path. +func Schemas(schemaPath string) (*ast.SchemaSet, error) { + + var errs Errors + ss, err := loadSchemas(schemaPath) + if err != nil { + errs.add(err) + return nil, errs + } + + return ss, nil +} + +func loadSchemas(schemaPath string) (*ast.SchemaSet, error) { + + if schemaPath == "" { + return nil, nil + } + + ss := ast.NewSchemaSet() + path, err := fileurl.Clean(schemaPath) + if err != nil { + return nil, err + } + + info, err := os.Stat(path) + if err != nil { + return nil, err + } + + // Handle single file case. + if !info.IsDir() { + schema, err := loadOneSchema(path) + if err != nil { + return nil, err + } + ss.Put(ast.SchemaRootRef, schema) + return ss, nil + + } + + // Handle directory case. + rootDir := path + + err = filepath.Walk(path, + func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } else if info.IsDir() { + return nil + } + + schema, err := loadOneSchema(path) + if err != nil { + return err + } + + relPath, err := filepath.Rel(rootDir, path) + if err != nil { + return err + } + + key := getSchemaSetByPathKey(relPath) + ss.Put(key, schema) + return nil + }) + + if err != nil { + return nil, err + } + + return ss, nil +} + +func getSchemaSetByPathKey(path string) ast.Ref { + + front := filepath.Dir(path) + last := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)) + + var parts []string + + if front != "." { + parts = append(strings.Split(filepath.ToSlash(front), "/"), last) + } else { + parts = []string{last} + } + + key := make(ast.Ref, 1+len(parts)) + key[0] = ast.SchemaRootDocument + for i := range parts { + key[i+1] = ast.InternedTerm(parts[i]) + } + + return key +} + +func loadOneSchema(path string) (any, error) { + bs, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var schema any + if err := util.Unmarshal(bs, &schema); err != nil { + return nil, fmt.Errorf("%s: %w", path, err) + } + + return schema, nil +} + +// All returns a Result object loaded (recursively) from the specified paths. +// Deprecated: Use FileLoader.Filtered() instead. +func All(paths []string) (*Result, error) { + return NewFileLoader().Filtered(paths, nil) +} + +// Filtered returns a Result object loaded (recursively) from the specified +// paths while applying the given filters. If any filter returns true, the +// file/directory is excluded. +// Deprecated: Use FileLoader.Filtered() instead. +func Filtered(paths []string, filter Filter) (*Result, error) { + return NewFileLoader().Filtered(paths, filter) +} + +// AsBundle loads a path as a bundle. If it is a single file +// it will be treated as a normal tarball bundle. If a directory +// is supplied it will be loaded as an unzipped bundle tree. +// Deprecated: Use FileLoader.AsBundle() instead. +func AsBundle(path string) (*bundle.Bundle, error) { + return NewFileLoader().AsBundle(path) +} + +// 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, _ int) bool { + return !info.IsDir() && !strings.HasSuffix(info.Name(), bundle.RegoExt) + }) +} + +// Rego is deprecated. Use RegoWithOpts instead. +func Rego(path string) (*RegoFile, error) { + return RegoWithOpts(path, ast.ParserOptions{}) +} + +// RegoWithOpts returns a RegoFile object loaded from the given path. +func RegoWithOpts(path string, opts ast.ParserOptions) (*RegoFile, error) { + path, err := fileurl.Clean(path) + if err != nil { + return nil, err + } + bs, err := os.ReadFile(path) + if err != nil { + return nil, err + } + return loadRego(path, bs, metrics.New(), opts) +} + +// CleanPath returns the normalized version of a path that can be used as an identifier. +func CleanPath(path string) string { + return strings.Trim(path, "/") +} + +// Paths returns a sorted list of files contained at path. If recurse is true +// and path is a directory, then Paths will walk the directory structure +// recursively and list files at each level. +func Paths(path string, recurse bool) (paths []string, err error) { + path, err = fileurl.Clean(path) + if err != nil { + return nil, err + } + err = filepath.Walk(path, func(f string, _ os.FileInfo, _ error) error { + if !recurse { + if path != f && path != filepath.Dir(f) { + return filepath.SkipDir + } + } + paths = append(paths, f) + return nil + }) + return paths, err +} + +// Dirs resolves filepaths to directories. It will return a list of unique +// directories. +func Dirs(paths []string) []string { + unique := map[string]struct{}{} + + for _, path := range paths { + // TODO: /dir/dir will register top level directory /dir + dir := filepath.Dir(path) + unique[dir] = struct{}{} + } + + return util.KeysSorted(unique) +} + +// SplitPrefix returns a tuple specifying the document prefix and the file +// path. +func SplitPrefix(path string) ([]string, string) { + // Non-prefixed URLs can be returned without modification and their contents + // can be rooted directly under data. + if strings.Index(path, "://") == strings.Index(path, ":") { + return nil, path + } + parts := strings.SplitN(path, ":", 2) + if len(parts) == 2 && len(parts[0]) > 0 { + return strings.Split(parts[0], "."), parts[1] + } + return nil, path +} + +func (l *Result) merge(path string, result any) error { + switch result := result.(type) { + case bundle.Bundle: + for _, module := range result.Modules { + l.Modules[module.Path] = &RegoFile{ + Name: module.Path, + Parsed: module.Parsed, + Raw: module.Raw, + } + } + return l.mergeDocument(path, result.Data) + case *RegoFile: + l.Modules[CleanPath(path)] = result + return nil + default: + return l.mergeDocument(path, result) + } +} + +func (l *Result) mergeDocument(path string, doc any) error { + obj, ok := makeDir(l.path, doc) + if !ok { + return unsupportedDocumentType(path) + } + merged, ok := merge.InterfaceMaps(l.Documents, obj) + if !ok { + return mergeError(path) + } + for k := range merged { + l.Documents[k] = merged[k] + } + return nil +} + +func (l *Result) withParent(p string) *Result { + path := append(l.path, p) + return &Result{ + Documents: l.Documents, + Modules: l.Modules, + path: path, + } +} + +func newResult() *Result { + return &Result{ + Documents: map[string]any{}, + Modules: map[string]*RegoFile{}, + } +} + +func all(fsys fs.FS, paths []string, filter Filter, f func(*Result, string, int) error) (*Result, error) { + errs := Errors{} + root := newResult() + + for _, path := range paths { + + // Paths can be prefixed with a string that specifies where content should be + // loaded under data. E.g., foo.bar:/path/to/some.json will load the content + // of some.json under {"foo": {"bar": ...}}. + loaded := root + prefix, path := SplitPrefix(path) + if len(prefix) > 0 { + for _, part := range prefix { + loaded = loaded.withParent(part) + } + } + + allRec(fsys, path, filter, &errs, loaded, 0, f) + } + + if len(errs) > 0 { + return nil, errs + } + + return root, nil +} + +func allRec(fsys fs.FS, path string, filter Filter, errors *Errors, loaded *Result, depth int, f func(*Result, string, int) error) { + + path, err := fileurl.Clean(path) + if err != nil { + errors.add(err) + return + } + + if err := checkForUNCPath(path); err != nil { + errors.add(err) + return + } + + var info fs.FileInfo + if fsys != nil { + info, err = fs.Stat(fsys, path) + } else { + info, err = os.Stat(path) + } + + if err != nil { + errors.add(err) + return + } + + if filter != nil && filter(path, info, depth) { + return + } + + if !info.IsDir() { + if err := f(loaded, path, depth); err != nil { + errors.add(err) + } + return + } + + // If we are recursing on directories then content must be loaded under path + // specified by directory hierarchy. + if depth > 0 { + loaded = loaded.withParent(info.Name()) + } + + var files []fs.DirEntry + if fsys != nil { + files, err = fs.ReadDir(fsys, path) + } else { + files, err = os.ReadDir(path) + } + if err != nil { + errors.add(err) + return + } + + for _, file := range files { + allRec(fsys, filepath.Join(path, file.Name()), filter, errors, loaded, depth+1, f) + } +} + +func loadKnownTypes(path string, bs []byte, m metrics.Metrics, opts ast.ParserOptions, bundleLazyLoadingMode bool) (any, error) { + ext := filepath.Ext(path) + if handler := extension.FindExtension(ext); handler != nil { + m.Timer(metrics.RegoDataParse).Start() + + var value any + err := handler(bs, &value) + + m.Timer(metrics.RegoDataParse).Stop() + if err != nil { + return nil, fmt.Errorf("bundle %s: %w", path, err) + } + + return value, nil + } + switch ext { + case ".json": + return loadJSON(path, bs, m) + case ".rego": + return loadRego(path, bs, m, opts) + case ".yaml", ".yml": + return loadYAML(path, bs, m) + default: + if strings.HasSuffix(path, ".tar.gz") { + r, err := loadBundleFile(path, bs, m, opts, bundleLazyLoadingMode) + if err != nil { + err = fmt.Errorf("bundle %s: %w", path, err) + } + return r, err + } + } + return nil, unrecognizedFile(path) +} + +func loadFileForAnyType(path string, bs []byte, m metrics.Metrics, opts ast.ParserOptions) (any, error) { + module, err := loadRego(path, bs, m, opts) + if err == nil { + return module, nil + } + doc, err := loadJSON(path, bs, m) + if err == nil { + return doc, nil + } + doc, err = loadYAML(path, bs, m) + if err == nil { + return doc, nil + } + return nil, unrecognizedFile(path) +} + +func loadBundleFile(path string, bs []byte, m metrics.Metrics, opts ast.ParserOptions, bundleLazyLoadingMode bool) (bundle.Bundle, error) { + tl := bundle.NewTarballLoaderWithBaseURL(bytes.NewBuffer(bs), path) + br := bundle.NewCustomReader(tl). + WithRegoVersion(opts.RegoVersion). + WithCapabilities(opts.Capabilities). + WithProcessAnnotations(opts.ProcessAnnotation). + WithMetrics(m). + WithSkipBundleVerification(true). + WithLazyLoadingMode(bundleLazyLoadingMode). + IncludeManifestInData(true) + return br.Read() +} + +func loadRego(path string, bs []byte, m metrics.Metrics, opts ast.ParserOptions) (*RegoFile, error) { + m.Timer(metrics.RegoModuleParse).Start() + var module *ast.Module + var err error + module, err = ast.ParseModuleWithOpts(path, string(bs), opts) + m.Timer(metrics.RegoModuleParse).Stop() + if err != nil { + return nil, err + } + result := &RegoFile{ + Name: path, + Parsed: module, + Raw: bs, + } + return result, nil +} + +func loadJSON(path string, bs []byte, m metrics.Metrics) (any, error) { + m.Timer(metrics.RegoDataParse).Start() + var x any + err := util.UnmarshalJSON(bs, &x) + m.Timer(metrics.RegoDataParse).Stop() + + if err != nil { + return nil, fmt.Errorf("%s: %w", path, err) + } + return x, nil +} + +func loadYAML(path string, bs []byte, m metrics.Metrics) (any, error) { + m.Timer(metrics.RegoDataParse).Start() + bs, err := yaml.YAMLToJSON(bs) + m.Timer(metrics.RegoDataParse).Stop() + if err != nil { + return nil, fmt.Errorf("%v: error converting YAML to JSON: %v", path, err) + } + return loadJSON(path, bs, m) +} + +func makeDir(path []string, x any) (map[string]any, bool) { + if len(path) == 0 { + obj, ok := x.(map[string]any) + if !ok { + return nil, false + } + return obj, true + } + return makeDir(path[:len(path)-1], map[string]any{path[len(path)-1]: x}) +} + +// isUNC reports whether path is a UNC path. +func isUNC(path string) bool { + return len(path) > 1 && isSlash(path[0]) && isSlash(path[1]) +} + +func isSlash(c uint8) bool { + return c == '\\' || c == '/' +} + +func checkForUNCPath(path string) error { + if isUNC(path) { + return fmt.Errorf("UNC path read is not allowed: %s", path) + } + return nil +} diff --git a/third_party/opa/v1/loader/loader_test.go b/third_party/opa/v1/loader/loader_test.go new file mode 100644 index 000000000000..d42a8a766a86 --- /dev/null +++ b/third_party/opa/v1/loader/loader_test.go @@ -0,0 +1,1490 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package loader + +import ( + "bytes" + "embed" + "encoding/json" + "errors" + "io" + "io/fs" + "os" + "path" + "path/filepath" + "reflect" + "slices" + "sort" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/loader/extension" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestLoadJSON(t *testing.T) { + + files := map[string]string{ + "/foo.json": `{"a": [1,2,3]}`, + } + + test.WithTempFS(files, func(rootDir string) { + + loaded, err := NewFileLoader().All([]string{filepath.Join(rootDir, "foo.json")}) + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected := parseJSON(files["/foo.json"]) + + if !reflect.DeepEqual(loaded.Documents, expected) { + t.Fatalf("Expected %v but got: %v", expected, loaded.Documents) + } + }) +} + +func TestAll_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0", + module: `package test + +p[x] { + x := "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "rego.v1 import", + module: `package test +import rego.v1 + +p contains x if { + x := "a" +}`, + }, + { + note: "v1", // v1 is the default rego-version + module: `package test + +p contains x if { + x := "a" +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "/test.rego": tc.module} + + test.WithTempFS(files, func(rootDir string) { + moduleFile := filepath.Join(rootDir, "test.rego") + loaded, err := All([]string{moduleFile}) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := ast.MustParseModule(files["/test.rego"]) + if !expected.Equal(loaded.Modules[CleanPath(moduleFile)].Parsed) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, loaded.Modules[moduleFile]) + } + } + }) + }) + } +} + +func TestFiltered_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0", + module: `package test + +p[x] { + x := "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "rego.v1 import", + module: `package test +import rego.v1 + +p contains x if { + x := "a" +}`, + }, + { + note: "v1", // v1 is the default rego-version + module: `package test + +p contains x if { + x := "a" +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "/test.rego": tc.module} + + test.WithTempFS(files, func(rootDir string) { + moduleFile := filepath.Join(rootDir, "test.rego") + filter := func(string, os.FileInfo, int) bool { + return false + } + + loaded, err := Filtered([]string{moduleFile}, filter) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := ast.MustParseModule(files["/test.rego"]) + if !expected.Equal(loaded.Modules[CleanPath(moduleFile)].Parsed) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, loaded.Modules[moduleFile]) + } + } + }) + }) + } +} + +func TestRego_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0", + module: `package test + +p[x] { + x := "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "rego.v1 import", + module: `package test +import rego.v1 + +p contains x if { + x := "a" +}`, + }, + { + note: "v1", // v1 is the default rego-version + module: `package test + +p contains x if { + x := "a" +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "/test.rego": tc.module} + + test.WithTempFS(files, func(rootDir string) { + moduleFile := filepath.Join(rootDir, "test.rego") + loaded, err := Rego(moduleFile) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := ast.MustParseModule(files["/test.rego"]) + if !expected.Equal(loaded.Parsed) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, loaded.Parsed) + } + } + }) + }) + } +} + +func TestAllRegos_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0", + module: `package test + +p[x] { + x := "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "rego.v1 import", + module: `package test +import rego.v1 + +p contains x if { + x := "a" +}`, + }, + { + note: "v1", // v1 is the default rego-version + module: `package test + +p contains x if { + x := "a" +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "/test.rego": tc.module} + + test.WithTempFS(files, func(rootDir string) { + moduleFile := filepath.Join(rootDir, "test.rego") + loaded, err := AllRegos([]string{moduleFile}) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := ast.MustParseModule(files["/test.rego"]) + if !expected.Equal(loaded.Modules[CleanPath(moduleFile)].Parsed) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, loaded.Modules[moduleFile]) + } + } + }) + }) + } +} + +func TestLoadRego_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0", + module: `package test + +p[x] { + x := "a" +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "rego.v1 import", + module: `package test +import rego.v1 + +p contains x if { + x := "a" +}`, + }, + { + note: "v1", // v1 is the default rego-version + module: `package test + +p contains x if { + x := "a" +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "/test.rego": tc.module} + + test.WithTempFS(files, func(rootDir string) { + moduleFile := filepath.Join(rootDir, "test.rego") + loaded, err := NewFileLoader().All([]string{moduleFile}) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected errors but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%s\n\nbut got:\n\n%s", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := ast.MustParseModule(files["/test.rego"]) + if !expected.Equal(loaded.Modules[CleanPath(moduleFile)].Parsed) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, loaded.Modules[moduleFile]) + } + } + }) + }) + } +} + +func TestLoadRego(t *testing.T) { + + files := map[string]string{ + "/foo.rego": `package ex +import rego.v1 + +p = true if { true }`} + + test.WithTempFS(files, func(rootDir string) { + moduleFile := filepath.Join(rootDir, "foo.rego") + loaded, err := NewFileLoader().All([]string{moduleFile}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := ast.MustParseModule(files["/foo.rego"]) + if !expected.Equal(loaded.Modules[CleanPath(moduleFile)].Parsed) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, loaded.Modules[moduleFile]) + } + }) +} + +func TestLoadYAML(t *testing.T) { + + files := map[string]string{ + "/foo.yml": ` + a: + - 1 + - b + - "c" + - null + - true + - false + `, + } + + test.WithTempFS(files, func(rootDir string) { + yamlFile := filepath.Join(rootDir, "foo.yml") + loaded, err := NewFileLoader().All([]string{yamlFile}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := parseJSON(` + {"a": [1, "b", "c", null, true, false]}`) + if !reflect.DeepEqual(loaded.Documents, expected) { + t.Fatalf("Expected %v but got: %v", expected, loaded.Documents) + } + }) +} + +func TestLoadGuessYAML(t *testing.T) { + files := map[string]string{ + "/foo": ` + a: b + `, + } + test.WithTempFS(files, func(rootDir string) { + yamlFile := filepath.Join(rootDir, "foo") + loaded, err := NewFileLoader().All([]string{yamlFile}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := parseJSON(`{"a": "b"}`) + if !reflect.DeepEqual(loaded.Documents, expected) { + t.Fatalf("Expected %v but got: %v", expected, loaded.Documents) + } + }) +} + +func TestLoadExtension(t *testing.T) { + files := map[string]string{ + "/foo.mock": `{"a": [1,2,3]}`, + } + + extension.RegisterExtension(".mock", util.UnmarshalJSON) + + test.WithTempFS(files, func(rootDir string) { + loaded, err := NewFileLoader().All([]string{filepath.Join(rootDir, "foo.mock")}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected := parseJSON(files["/foo.mock"]) + + if !reflect.DeepEqual(loaded.Documents, expected) { + t.Fatalf("Expected %v but got: %v", expected, loaded.Documents) + } + }) +} + +func TestLoadExtensionFail(t *testing.T) { + files := map[string]string{ + "/foo.mock": `{"a": [1,2,3]}`, + } + + extension.RegisterExtension(".mock", func([]byte, interface{}) error { + return errors.New("Load .mock file failed") + }) + + test.WithTempFS(files, func(rootDir string) { + _, err := NewFileLoader().All([]string{filepath.Join(rootDir, "foo.mock")}) + + if err == nil { + t.Fatal("Expected extension error") + } + + if !strings.Contains(err.Error(), "foo.mock: Load .mock file failed") { + t.Fatal(err) + } + }) +} + +func TestLoadDirRecursive(t *testing.T) { + files := map[string]string{ + "/a/data1.json": `{"a": [1,2,3]}`, + "/a/e.rego": `package q`, + "/b/data2.yaml": `{"aaa": {"bbb": 1}}`, + "/b/data3.yaml": `{"aaa": {"ccc": 2}}`, + "/b/d/x.json": "null", + "/b/d/e.rego": `package p`, + "/b/d/ignore": `deadbeef`, + "/foo": `{"zzz": "b"}`, + } + + test.WithTempFS(files, func(rootDir string) { + loaded, err := NewFileLoader().All(mustListPaths(rootDir, false)[1:]) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectedDocuments := parseJSON(` + { + "zzz": "b", + "a": [1,2,3], + "aaa": { + "bbb": 1, + "ccc": 2 + }, + "d": null + } + `) + if !reflect.DeepEqual(loaded.Documents, expectedDocuments) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expectedDocuments, loaded.Documents) + } + mod1 := ast.MustParseModule(files["/a/e.rego"]) + mod2 := ast.MustParseModule(files["/b/d/e.rego"]) + expectedMod1 := loaded.Modules[CleanPath(filepath.Join(rootDir, "a", "e.rego"))].Parsed + expectedMod2 := loaded.Modules[CleanPath(filepath.Join(rootDir, "b", "d", "e.rego"))].Parsed + if !mod1.Equal(expectedMod1) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expectedMod1, mod1) + } + if !mod2.Equal(expectedMod2) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expectedMod2, mod2) + } + }) +} + +func TestFilteredPaths(t *testing.T) { + files := map[string]string{ + "/a/data1.json": `{"a": [1,2,3]}`, + "/a/e.rego": `package q`, + "/b/data2.yaml": `{"aaa": {"bbb": 1}}`, + "/b/data3.yaml": `{"aaa": {"ccc": 2}}`, + "/b/d/x.json": "null", + "/b/d/e.rego": `package p`, + "/b/d/ignore": `deadbeef`, + "/foo": `{"zzz": "b"}`, + } + + test.WithTempFS(files, func(rootDir string) { + + paths := []string{ + filepath.Join(rootDir, "a"), + filepath.Join(rootDir, "b"), + filepath.Join(rootDir, "foo"), + } + + result, err := FilteredPaths(paths, nil) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if len(result) != len(files) { + t.Fatalf("Expected %v files across directories but got %v", len(files), len(result)) + } + }) +} + +func TestGetBundleDirectoryLoader(t *testing.T) { + files := map[string]string{ + "bundle.tar.gz": "", + } + + mod := "package b.c\np=1" + + test.WithTempFS(files, func(rootDir string) { + + bundleFile := filepath.Join(rootDir, "bundle.tar.gz") + + f, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + b := &bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"a", "b/c"}, + Revision: "123", + }, + Data: map[string]any{ + "a": map[string]any{ + "b": []int{4, 5, 6}, + }, + }, + Modules: []bundle.ModuleFile{ + { + URL: path.Join(bundleFile, "policy.rego"), + Path: "/policy.rego", + Raw: []byte(mod), + Parsed: ast.MustParseModule(mod), + }, + }, + } + + err = bundle.Write(f, *b) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + err = f.Close() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + bl, isDir, err := GetBundleDirectoryLoader(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if isDir { + t.Fatal("Expected bundle to be gzipped tarball but got directory") + } + + // check files + var result []string + for { + f, err := bl.NextFile() + if err == io.EOF { + break + } + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + result = append(result, f.Path()) + } + + if len(result) != 3 { + t.Fatalf("Expected 3 files in the bundle but got %v", len(result)) + } + }) +} + +func TestLoadBundle(t *testing.T) { + + test.WithTempFS(nil, func(rootDir string) { + + f, err := os.Create(filepath.Join(rootDir, "bundle.tar.gz")) + if err != nil { + t.Fatal(err) + } + + var testBundle = bundle.Bundle{ + Modules: []bundle.ModuleFile{ + { + Path: "x.rego", + Raw: []byte(` + package baz + + p = 1`), + }, + }, + Data: map[string]any{ + "foo": "bar", + }, + Manifest: bundle.Manifest{ + Revision: "", + Roots: &[]string{""}, + }, + } + + if err := bundle.Write(f, testBundle); err != nil { + t.Fatal(err) + } + + paths := mustListPaths(rootDir, false)[1:] + loaded, err := NewFileLoader().All(paths) + if err != nil { + t.Fatal(err) + } + + actualData := testBundle.Data + actualData["system"] = map[string]any{"bundle": map[string]any{"manifest": map[string]any{"revision": "", "roots": []any{""}}}} + + if !reflect.DeepEqual(actualData, loaded.Documents) { + t.Fatalf("Expected %v but got: %v", actualData, loaded.Documents) + } + + if !bytes.Equal(testBundle.Modules[0].Raw, loaded.Modules["/x.rego"].Raw) { + t.Fatalf("Expected %v but got: %v", string(testBundle.Modules[0].Raw), loaded.Modules["/x.rego"].Raw) + } + }) +} + +func TestLoadBundleWithReader(t *testing.T) { + + buf := bytes.Buffer{} + testBundle := bundle.Bundle{ + Modules: []bundle.ModuleFile{ + { + Path: "x.rego", + Raw: []byte(` + package baz + + p = 1`), + }, + }, + Data: map[string]any{ + "foo": "bar", + }, + Manifest: bundle.Manifest{ + Revision: "", + Roots: &[]string{"foo", "baz"}, + }, + } + + if err := bundle.Write(&buf, testBundle); err != nil { + t.Fatal(err) + } + + b, err := NewFileLoader().WithReader(&buf).AsBundle("bundle.tar.gz") + if err != nil { + t.Fatal(err) + } + if b == nil { + t.Fatalf("Expected bundle to be non-nil") + } + + if exp, act := 1, len(b.Modules); exp != act { + t.Fatalf("expected %d modules, got %d", exp, act) + } + + expectedModulePaths := map[string]struct{}{ + "/x.rego": {}, + } + for _, mf := range b.Modules { + if _, found := expectedModulePaths[mf.Path]; !found { + t.Errorf("Unexpected module file with path %s in bundle modules", mf.Path) + } + } + + if exp, act := map[string]any{"foo": "bar"}, b.Data; !reflect.DeepEqual(act, exp) { + t.Fatalf("expected data %+v, got %+v", exp, act) + } + if exp, act := []string{"foo", "baz"}, *b.Manifest.Roots; !reflect.DeepEqual(act, exp) { + t.Fatalf("expected roots %v, got %v", exp, act) + } +} + +func TestLoadBundleSubDir(t *testing.T) { + + test.WithTempFS(nil, func(rootDir string) { + + if err := os.MkdirAll(filepath.Join(rootDir, "a", "b"), 0777); err != nil { + t.Fatal(err) + } + + f, err := os.Create(filepath.Join(rootDir, "a", "b", "bundle.tar.gz")) + if err != nil { + t.Fatal(err) + } + + var testBundle = bundle.Bundle{ + Modules: []bundle.ModuleFile{ + { + Path: "x.rego", + Raw: []byte(` + package baz + + p = 1`), + }, + }, + Data: map[string]any{ + "foo": "bar", + }, + Manifest: bundle.Manifest{ + Revision: "", + Roots: &[]string{""}, + }, + } + + if err := bundle.Write(f, testBundle); err != nil { + t.Fatal(err) + } + + paths := mustListPaths(rootDir, false)[1:] + loaded, err := NewFileLoader().All(paths) + if err != nil { + t.Fatal(err) + } + + actualData := testBundle.Data + actualData["system"] = map[string]any{"bundle": map[string]any{"manifest": map[string]any{"revision": "", "roots": []any{""}}}} + + if !reflect.DeepEqual(map[string]any{"b": testBundle.Data}, loaded.Documents) { + t.Fatalf("Expected %v but got: %v", testBundle.Data, loaded.Documents) + } + + if !bytes.Equal(testBundle.Modules[0].Raw, loaded.Modules["/x.rego"].Raw) { + t.Fatalf("Expected %v but got: %v", string(testBundle.Modules[0].Raw), loaded.Modules["/x.rego"].Raw) + } + }) +} + +func TestAsBundleWithDir(t *testing.T) { + files := map[string]string{ + "/foo/data.json": "[1,2,3]", + "/bar/bar.yaml": "abc", // Should be ignored + "/baz/qux/qux.json": "null", // Should be ignored + "/foo/policy.rego": "package foo\np = 1", + "base.rego": "package bar\nx = 1", + "/.manifest": `{"roots": ["foo", "bar", "baz"]}`, + } + + test.WithTempFS(files, func(rootDir string) { + b, err := NewFileLoader().AsBundle(rootDir) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if b == nil { + t.Fatalf("Expected bundle to be non-nil") + } + + if len(b.Modules) != 2 { + t.Fatalf("expected 2 modules, got %d", len(b.Modules)) + } + + expectedModulePaths := map[string]struct{}{ + filepath.Join(rootDir, "foo", "policy.rego"): {}, + filepath.Join(rootDir, "base.rego"): {}, + } + for _, mf := range b.Modules { + if _, found := expectedModulePaths[mf.Path]; !found { + t.Errorf("Unexpected module file with path %s in bundle modules", mf.Path) + } + } + + expectedData := util.MustUnmarshalJSON([]byte(`{"foo": [1,2,3]}`)) + if !reflect.DeepEqual(b.Data, expectedData) { + t.Fatalf("expected data %+v, got %+v", expectedData, b.Data) + } + + expectedRoots := []string{"foo", "bar", "baz"} + if !slices.Equal(*b.Manifest.Roots, expectedRoots) { + t.Fatalf("expected roots %s, got: %s", expectedRoots, *b.Manifest.Roots) + } + }) +} + +func TestAsBundleWithFileURLDir(t *testing.T) { + files := map[string]string{ + "/foo/data.json": "[1,2,3]", + "/foo/policy.rego": "package foo.bar\np = 1", + "/.manifest": `{"roots": ["foo"]}`, + } + + test.WithTempFS(files, func(rootDir string) { + b, err := NewFileLoader().AsBundle("file://" + rootDir) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if b == nil { + t.Fatalf("Expected bundle to be non-nil") + } + + if len(b.Modules) != 1 { + t.Fatalf("expected 1 modules, got %d", len(b.Modules)) + } + expectedModulePaths := map[string]struct{}{ + filepath.Join(rootDir, "foo", "policy.rego"): {}, + } + for _, mf := range b.Modules { + if _, found := expectedModulePaths[mf.Path]; !found { + t.Errorf("Unexpected module file with path %s in bundle modules", mf.Path) + } + } + + expectedData := util.MustUnmarshalJSON([]byte(`{"foo": [1,2,3]}`)) + if !reflect.DeepEqual(b.Data, expectedData) { + t.Fatalf("expected data %+v, got %+v", expectedData, b.Data) + } + + expectedRoots := []string{"foo"} + if !slices.Equal(*b.Manifest.Roots, expectedRoots) { + t.Fatalf("expected roots %s, got: %s", expectedRoots, *b.Manifest.Roots) + } + }) +} + +func TestAsBundleWithFile(t *testing.T) { + files := map[string]string{ + "bundle.tar.gz": "", + } + + mod := "package b.c\np=1" + + test.WithTempFS(files, func(rootDir string) { + bundleFile := filepath.Join(rootDir, "bundle.tar.gz") + + f, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + b := &bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"a", "b/c"}, + Revision: "123", + }, + Data: map[string]any{ + "a": map[string]any{ + "b": []int{4, 5, 6}, + }, + }, + Modules: []bundle.ModuleFile{ + { + URL: path.Join(bundleFile, "policy.rego"), + Path: "/policy.rego", + Raw: []byte(mod), + Parsed: ast.MustParseModule(mod), + }, + }, + } + + err = bundle.Write(f, *b) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + err = f.Close() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + actual, err := NewFileLoader().AsBundle(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + var tmp any = b + err = util.RoundTrip(&tmp) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if !actual.Equal(*b) { + t.Fatalf("Loaded bundle doesn't match expected.\n\nExpected: %+v\n\nActual: %+v\n\n", b, actual) + } + }) +} + +// Test that lazy loading mode disables data validation checks. +func TestBundleLazyLoadingMode(t *testing.T) { + mod := "package b.c\np=1" + bundleFile := filepath.Join(t.TempDir(), "bundle.tar.gz") + + f, err := os.Create(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + b := &bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"a", "b/c"}, + Revision: "123", + }, + Data: nil, + Modules: []bundle.ModuleFile{ + { + URL: path.Join(bundleFile, "policy.rego"), + Path: "/policy.rego", + Raw: []byte(mod), + Parsed: ast.MustParseModule(mod), + }, + }, + } + + err = bundle.Write(f, *b) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + err = f.Close() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + // Loading a nil data value normally is an error, but lazy loading mode defers it. + _, err = NewFileLoader().WithBundleLazyLoadingMode(true).AsBundle(bundleFile) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } +} + +func TestCheckForUNCPath(t *testing.T) { + cases := []struct { + input string + wantErr bool + err error + }{ + { + input: "c:/foo", + wantErr: false, + }, + { + input: "file:///c:/a/b", + wantErr: false, + }, + { + input: `\\localhost\c$`, + wantErr: true, + err: errors.New("UNC path read is not allowed: \\\\localhost\\c$"), + }, + { + input: `\\\\localhost\c$`, + wantErr: true, + err: errors.New("UNC path read is not allowed: \\\\\\\\localhost\\c$"), + }, + { + input: `//localhost/foo`, + wantErr: true, + err: errors.New("UNC path read is not allowed: //localhost/foo"), + }, + { + input: `file:///a/b/c`, + wantErr: false, + }, + } + + for _, tc := range cases { + t.Run(tc.input, func(t *testing.T) { + err := checkForUNCPath(tc.input) + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + } +} + +func TestLoadRooted(t *testing.T) { + files := map[string]string{ + "/foo.json": "[1,2,3]", + "/bar/bar.yaml": "abc", + "/baz/qux/qux.json": "null", + } + + test.WithTempFS(files, func(rootDir string) { + paths := mustListPaths(rootDir, false)[1:] + sort.Strings(paths) + paths[0] = "one.two:" + paths[0] + paths[1] = "three:" + paths[1] + paths[2] = "four:" + paths[2] + t.Log(paths) + loaded, err := NewFileLoader().All(paths) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := parseJSON(` + {"four": [1,2,3], "one": {"two": "abc"}, "three": {"qux": null}} + `) + if !reflect.DeepEqual(loaded.Documents, expected) { + t.Fatalf("Expected %v but got: %v", expected, loaded.Documents) + } + }) +} + +//go:embed testdata/embedtest +var embedTestFS embed.FS + +func TestLoadFS(t *testing.T) { + paths := []string{ + "four:foo.json", + "one.two:bar", + "three:baz", + } + + fsys, err := fs.Sub(embedTestFS, "testdata/embedtest") + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + loaded, err := NewFileLoader().WithFS(fsys).All(paths) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expectedRegoBytes, err := fs.ReadFile(fsys, "bar/bar.rego") + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectedRego := ast.MustParseModule(string(expectedRegoBytes)) + moduleFile := "bar/bar.rego" + if !expectedRego.Equal(loaded.Modules[moduleFile].Parsed) { + t.Fatalf( + "Expected:\n%v\n\nGot:\n%v", + expectedRego, + loaded.Modules[moduleFile], + ) + } + + expected := parseJSON(` + {"four": [1,2,3], "one": {"two": "abc"}, "three": {"qux": null}} + `) + if !reflect.DeepEqual(loaded.Documents, expected) { + t.Fatalf("Expected %v but got: %v", expected, loaded.Documents) + } +} + +func TestLoadWithJSONOptions(t *testing.T) { + paths := []string{ + "four:foo.json", + "one.two:bar", + "three:baz", + } + + fsys, err := fs.Sub(embedTestFS, "testdata/embedtest") + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + astJSON.SetOptions(astJSON.Options{ + MarshalOptions: astJSON.MarshalOptions{ + IncludeLocation: astJSON.NodeToggle{ + Package: true, + }, + }, + }) + + t.Cleanup(func() { + astJSON.SetOptions(astJSON.Defaults()) + }) + + // load the file with JSON options set to include location data + loaded, err := NewFileLoader().WithFS(fsys).All(paths) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + mod, ok := loaded.Modules["bar/bar.rego"] + if !ok { + t.Fatalf("Expected bar/bar.rego to be loaded") + } + + bs, err := json.Marshal(mod.Parsed.Package) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + exp := `{"location":{"file":"bar/bar.rego","row":1,"col":1},"path":[{"type":"var","value":"data"},{"type":"string","value":"bar"}]}` + if string(bs) != exp { + t.Fatalf("Expected %v but got: %v", exp, string(bs)) + } +} + +func TestGlobExcludeName(t *testing.T) { + + files := map[string]string{ + "/.data.json": `{"x":1}`, + "/.y/data.json": `{"y": 2}`, + "/.y/z/data.json": `3`, + "/z/.hidden/data.json": `"donotinclude"`, + "/z/a/.hidden.json": `"donotinclude"`, + } + + test.WithTempFS(files, func(rootDir string) { + paths := mustListPaths(rootDir, false)[1:] + sort.Strings(paths) + result, err := NewFileLoader().Filtered(paths, GlobExcludeName(".*", 1)) + if err != nil { + t.Fatal(err) + } + exp := parseJSON(`{ + "x": 1, + "y": 2, + "z": 3 + }`) + if !reflect.DeepEqual(exp, result.Documents) { + t.Fatalf("Expected %v but got %v", exp, result.Documents) + } + }) +} + +func TestLoadErrors(t *testing.T) { + files := map[string]string{ + "/x1.json": `{"x": [1,2,3]}`, + "/x2.json": `{"x": {"y": 1}}`, + "/empty.rego": ` `, + "/dir/a.json": ``, + "/dir/b.yaml": ` + foo: + - bar: + `, + "/bad_doc.json": "[1,2,3]", + } + test.WithTempFS(files, func(rootDir string) { + paths := mustListPaths(rootDir, false)[1:] + sort.Strings(paths) + _, err := NewFileLoader().All(paths) + if err == nil { + t.Fatalf("Expected failure") + } + + expected := []string{ + "bad_doc.json: document must be of type object", + "a.json: EOF", + "b.yaml: error converting YAML to JSON", + "empty.rego:0: rego_parse_error: empty module", + "x2.json: merge error", + "rego_parse_error: empty module", + } + + for _, s := range expected { + if !strings.Contains(err.Error(), s) { + t.Fatalf("Expected error to contain %v but got:\n%v", s, err) + } + } + }) +} + +func TestLoadFileURL(t *testing.T) { + files := map[string]string{ + "/a/a/1.json": `1`, // this will load as a directory (e.g., file://a/a) + "b.json": `{"b": 2}`, // this will load as a normal file + "c.json": `3`, // this will loas as rooted file + } + test.WithTempFS(files, func(rootDir string) { + + paths := mustListPaths(rootDir, false)[1:] + sort.Strings(paths) + + for i := range paths { + paths[i] = "file://" + paths[i] + } + + paths[2] = "c:" + paths[2] + + result, err := NewFileLoader().All(paths) + if err != nil { + t.Fatal(err) + } + + exp := parseJSON(`{"a": 1, "b": 2, "c": 3}`) + if !reflect.DeepEqual(exp, result.Documents) { + t.Fatalf("Expected %v but got %v", exp, result.Documents) + } + }) +} + +func TestUnsupportedURLScheme(t *testing.T) { + _, err := NewFileLoader().All([]string{"http://openpolicyagent.org"}) + if err == nil || !strings.Contains(err.Error(), "unsupported URL scheme: http://openpolicyagent.org") { + t.Fatal(err) + } +} + +func TestSplitPrefix(t *testing.T) { + + tests := []struct { + input string + wantParts []string + wantPath string + }{ + { + input: "foo/bar", + wantPath: "foo/bar", + }, + { + input: "foo:/bar", + wantParts: []string{"foo"}, + wantPath: "/bar", + }, + { + input: "foo.bar:/baz", + wantParts: []string{"foo", "bar"}, + wantPath: "/baz", + }, + { + input: "file:///a/b/c", + wantPath: "file:///a/b/c", + }, + { + input: "x.y:file:///a/b/c", + wantParts: []string{"x", "y"}, + wantPath: "file:///a/b/c", + }, + { + input: "file:///c:/a/b/c", + wantPath: "file:///c:/a/b/c", + }, + { + input: "x.y:file:///c:/a/b/c", + wantParts: []string{"x", "y"}, + wantPath: "file:///c:/a/b/c", + }, + } + + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + parts, gotPath := SplitPrefix(tc.input) + if !slices.Equal(parts, tc.wantParts) { + t.Errorf("wanted parts %v but got %v", tc.wantParts, parts) + } + if gotPath != tc.wantPath { + t.Errorf("wanted path %q but got %q", gotPath, tc.wantPath) + } + }) + } +} + +func TestLoadRegos(t *testing.T) { + files := map[string]string{ + "/x.rego": ` + package x + p = true + `, + "/y.reg": ` + package x + p = true { # syntax error missing } + `, + "/subdir/z.rego": ` + package x + q = true + `, + } + + test.WithTempFS(files, func(rootDir string) { + paths := mustListPaths(rootDir, false)[1:] + sort.Strings(paths) + result, err := AllRegos(paths) + if err != nil { + t.Fatal(err) + } + if len(result.Modules) != 2 { + t.Fatalf("Expected exactly two modules but found: %v", result) + } + }) +} + +func parseJSON(x string) any { + return util.MustUnmarshalJSON([]byte(x)) +} + +func mustListPaths(path string, recurse bool) (paths []string) { + paths, err := Paths(path, recurse) + if err != nil { + panic(err) + } + return paths +} + +func TestDirs(t *testing.T) { + paths := []string{ + "/foo/bar.json", "/foo/bar/baz.json", "/foo.json", + } + + e := []string{"/", "/foo", "/foo/bar"} + sorted := Dirs(paths) + if !slices.Equal(sorted, e) { + t.Errorf("got: %q wanted: %q", sorted, e) + } +} + +func TestSchemas(t *testing.T) { + + tests := []struct { + note string + path string + files map[string]string + exp map[string]string + expErr string + }{ + { + note: "empty path", + path: "", // no error, no files + }, + { + note: "bad file path", + path: "foo/bar/baz.json", + expErr: "stat foo/bar/baz.json: no such file or directory", + }, + { + note: "bad file content", + path: "foo/bar/baz.json", + files: map[string]string{ + "foo/bar/baz.json": `{ + "foo + }`, + }, + expErr: "found unexpected end of stream", + }, + { + note: "one global file", + path: "foo/bar/baz.json", + files: map[string]string{ + "foo/bar/baz.json": `{"type": "string"}`, + }, + exp: map[string]string{ + "schema": `{"type": "string"}`, + }, + }, + { + note: "directory loading", + path: "foo/", + files: map[string]string{ + "foo/qux.json": `{"type": "number"}`, + "foo/bar/baz.json": `{"type": "string"}`, + }, + exp: map[string]string{ + "schema.qux": `{"type": "number"}`, + "schema.bar.baz": `{"type": "string"}`, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(tc.files, func(rootDir string) { + err := os.Chdir(rootDir) + if err != nil { + t.Fatal(err) + } + ss, err := Schemas(tc.path) + if tc.expErr != "" { + if err == nil { + t.Fatal("expected error") + } + if !strings.Contains(err.Error(), tc.expErr) { + t.Fatalf("expected error to contain %q but got %q", tc.expErr, err) + } + } else { + if err != nil { + t.Fatal("unexpected error:", err) + } + for k, v := range tc.exp { + var key ast.Ref + if k == "schema" { + key = ast.SchemaRootRef.Copy() + } else { + key = ast.MustParseRef(k) + } + var schema any + err = util.Unmarshal([]byte(v), &schema) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := ss.Get(key) + if result == nil { + t.Fatalf("expected schema with key %v", key) + } + if !reflect.DeepEqual(schema, result) { + t.Fatalf("expected schema %v but got %v", schema, result) + } + } + } + }) + }) + } +} diff --git a/third_party/opa/v1/loader/testdata/embedtest/bar/bar.rego b/third_party/opa/v1/loader/testdata/embedtest/bar/bar.rego new file mode 100644 index 000000000000..7b51d41cd456 --- /dev/null +++ b/third_party/opa/v1/loader/testdata/embedtest/bar/bar.rego @@ -0,0 +1,4 @@ +package bar +import rego.v1 + +p = true if { true } diff --git a/third_party/opa/v1/loader/testdata/embedtest/bar/bar.yaml b/third_party/opa/v1/loader/testdata/embedtest/bar/bar.yaml new file mode 100644 index 000000000000..8baef1b4abc4 --- /dev/null +++ b/third_party/opa/v1/loader/testdata/embedtest/bar/bar.yaml @@ -0,0 +1 @@ +abc diff --git a/third_party/opa/v1/loader/testdata/embedtest/baz/qux/qux.json b/third_party/opa/v1/loader/testdata/embedtest/baz/qux/qux.json new file mode 100644 index 000000000000..19765bd501b6 --- /dev/null +++ b/third_party/opa/v1/loader/testdata/embedtest/baz/qux/qux.json @@ -0,0 +1 @@ +null diff --git a/third_party/opa/v1/loader/testdata/embedtest/foo.json b/third_party/opa/v1/loader/testdata/embedtest/foo.json new file mode 100644 index 000000000000..3cc0ecbedfe4 --- /dev/null +++ b/third_party/opa/v1/loader/testdata/embedtest/foo.json @@ -0,0 +1 @@ +[1,2,3] diff --git a/third_party/opa/v1/logging/logging.go b/third_party/opa/v1/logging/logging.go new file mode 100644 index 000000000000..5ff27a211678 --- /dev/null +++ b/third_party/opa/v1/logging/logging.go @@ -0,0 +1,274 @@ +package logging + +import ( + "context" + "io" + "maps" + "net/http" + + "github.com/sirupsen/logrus" +) + +// Level log level for Logger +type Level uint8 + +const ( + // Error error log level + Error Level = iota + // Warn warn log level + Warn + // Info info log level + Info + // Debug debug log level + Debug +) + +// Logger provides interface for OPA logger implementations +type Logger interface { + Debug(fmt string, a ...any) + Info(fmt string, a ...any) + Error(fmt string, a ...any) + Warn(fmt string, a ...any) + + WithFields(map[string]any) Logger + + GetLevel() Level + SetLevel(Level) +} + +// StandardLogger is the default OPA logger implementation. +type StandardLogger struct { + logger *logrus.Logger + fields map[string]any +} + +// New returns a new standard logger. +func New() *StandardLogger { + return &StandardLogger{ + logger: logrus.New(), + } +} + +// Get returns the standard logger used throughout OPA. +// +// Deprecated. Do not rely on the global logger. +func Get() *StandardLogger { + return &StandardLogger{ + logger: logrus.StandardLogger(), + } +} + +// SetOutput sets the underlying logrus output. +func (l *StandardLogger) SetOutput(w io.Writer) { + l.logger.SetOutput(w) +} + +// SetFormatter sets the underlying logrus formatter. +func (l *StandardLogger) SetFormatter(formatter logrus.Formatter) { + l.logger.SetFormatter(formatter) +} + +// WithFields provides additional fields to include in log output +func (l *StandardLogger) WithFields(fields map[string]any) Logger { + cp := *l + cp.fields = make(map[string]any) + maps.Copy(cp.fields, l.fields) + maps.Copy(cp.fields, fields) + return &cp +} + +// getFields returns additional fields of this logger +func (l *StandardLogger) getFields() map[string]any { + return l.fields +} + +// SetLevel sets the standard logger level. +func (l *StandardLogger) SetLevel(level Level) { + var logrusLevel logrus.Level + switch level { + case Error: // set logging level report Warn or higher (includes Error) + logrusLevel = logrus.WarnLevel + case Warn: + logrusLevel = logrus.WarnLevel + case Info: + logrusLevel = logrus.InfoLevel + case Debug: + logrusLevel = logrus.DebugLevel + default: + l.Warn("unknown log level %v", level) + logrusLevel = logrus.InfoLevel + } + + l.logger.SetLevel(logrusLevel) +} + +// GetLevel returns the standard logger level. +func (l *StandardLogger) GetLevel() Level { + logrusLevel := l.logger.GetLevel() + + var level Level + switch logrusLevel { + case logrus.WarnLevel: + level = Error + case logrus.InfoLevel: + level = Info + case logrus.DebugLevel: + level = Debug + default: + l.Warn("unknown log level %v", logrusLevel) + level = Info + } + + return level +} + +// Debug logs at debug level +func (l *StandardLogger) Debug(fmt string, a ...any) { + if len(a) == 0 { + l.logger.WithFields(l.getFields()).Debug(fmt) + return + } + l.logger.WithFields(l.getFields()).Debugf(fmt, a...) +} + +// Info logs at info level +func (l *StandardLogger) Info(fmt string, a ...any) { + if len(a) == 0 { + l.logger.WithFields(l.getFields()).Info(fmt) + return + } + l.logger.WithFields(l.getFields()).Infof(fmt, a...) +} + +// Error logs at error level +func (l *StandardLogger) Error(fmt string, a ...any) { + if len(a) == 0 { + l.logger.WithFields(l.getFields()).Error(fmt) + return + } + l.logger.WithFields(l.getFields()).Errorf(fmt, a...) +} + +// Warn logs at warn level +func (l *StandardLogger) Warn(fmt string, a ...any) { + if len(a) == 0 { + l.logger.WithFields(l.getFields()).Warn(fmt) + return + } + l.logger.WithFields(l.getFields()).Warnf(fmt, a...) +} + +// NoOpLogger logging implementation that does nothing +type NoOpLogger struct { + level Level + fields map[string]any +} + +// NewNoOpLogger instantiates new NoOpLogger +func NewNoOpLogger() *NoOpLogger { + return &NoOpLogger{ + level: Info, + } +} + +// WithFields provides additional fields to include in log output. +// Implemented here primarily to be able to switch between implementations without loss of data. +func (l *NoOpLogger) WithFields(fields map[string]any) Logger { + cp := *l + cp.fields = fields + return &cp +} + +// Debug noop +func (*NoOpLogger) Debug(string, ...any) {} + +// Info noop +func (*NoOpLogger) Info(string, ...any) {} + +// Error noop +func (*NoOpLogger) Error(string, ...any) {} + +// Warn noop +func (*NoOpLogger) Warn(string, ...any) {} + +// SetLevel set log level +func (l *NoOpLogger) SetLevel(level Level) { + l.level = level +} + +// GetLevel get log level +func (l *NoOpLogger) GetLevel() Level { + return l.level +} + +type requestContextKey string + +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 + HTTPRequestContext HTTPRequestContext +} + +type HTTPRequestContext struct { + Header http.Header +} + +// Fields adapts the RequestContext fields to logrus.Fields. +func (rctx RequestContext) Fields() logrus.Fields { + return logrus.Fields{ + "client_addr": rctx.ClientAddr, + "req_id": rctx.ReqID, + "req_method": rctx.ReqMethod, + "req_path": rctx.ReqPath, + } +} + +// NewContext returns a copy of parent with an associated RequestContext. +func NewContext(parent context.Context, val *RequestContext) context.Context { + return context.WithValue(parent, reqCtxKey, val) +} + +// FromContext returns the RequestContext associated with ctx, if any. +func FromContext(ctx context.Context) (*RequestContext, bool) { + requestContext, ok := ctx.Value(reqCtxKey).(*RequestContext) + return requestContext, ok +} + +const httpReqCtxKey = requestContextKey("http-request-context-key") + +func WithHTTPRequestContext(parent context.Context, val *HTTPRequestContext) context.Context { + return context.WithValue(parent, httpReqCtxKey, val) +} + +func HTTPRequestContextFromContext(ctx context.Context) (*HTTPRequestContext, bool) { + requestContext, ok := ctx.Value(httpReqCtxKey).(*HTTPRequestContext) + return requestContext, ok +} + +const decisionCtxKey = requestContextKey("decision_id") + +func WithDecisionID(parent context.Context, id string) context.Context { + return context.WithValue(parent, decisionCtxKey, id) +} + +func DecisionIDFromContext(ctx context.Context) (string, bool) { + s, ok := ctx.Value(decisionCtxKey).(string) + return s, ok +} + +const batchDecisionCtxKey = requestContextKey("batch_decision_id") + +func WithBatchDecisionID(parent context.Context, id string) context.Context { + return context.WithValue(parent, batchDecisionCtxKey, id) +} + +func BatchDecisionIDFromContext(ctx context.Context) (string, bool) { + s, ok := ctx.Value(batchDecisionCtxKey).(string) + return s, ok +} diff --git a/third_party/opa/v1/logging/logging_test.go b/third_party/opa/v1/logging/logging_test.go new file mode 100644 index 000000000000..120ef382e800 --- /dev/null +++ b/third_party/opa/v1/logging/logging_test.go @@ -0,0 +1,181 @@ +package logging + +import ( + "bytes" + "context" + "crypto/rand" + "net/url" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/uuid" +) + +func TestWithFields(t *testing.T) { + logger := New().WithFields(map[string]any{"context": "contextvalue"}) + + var fieldvalue any + var ok bool + + if fieldvalue, ok = logger.(*StandardLogger).fields["context"]; !ok { + t.Fatal("Logger did not contain configured field") + } + + if fieldvalue.(string) != "contextvalue" { + t.Fatal("Logger did not contain configured field value") + } +} + +func TestCaptureWarningWithErrorSet(t *testing.T) { + buf := bytes.Buffer{} + logger := New() + logger.SetOutput(&buf) + logger.SetLevel(Error) + + logger.Warn("This is a warning. Next time, I won't compile.") + logger.Error("Fix your issues. I'm not compiling.") + + expected := []string{ + `level=warning msg="This is a warning. Next time, I won't compile."`, + `level=error msg="Fix your issues. I'm not compiling."`, + } + for _, exp := range expected { + if !strings.Contains(buf.String(), exp) { + t.Errorf("expected string %q not found in logs", exp) + } + } +} + +func TestNoFormattingForSingleString(t *testing.T) { + buf := bytes.Buffer{} + logger := New() + logger.SetOutput(&buf) + logger.SetLevel(Debug) + + // NOTE(sr): This construction is somewhat realistic: If we fed logger.Error() + // a format string but no args, the golang linters would yell. The indirection + // taken here is enough to not trigger linters. + x := url.PathEscape("/foo/bar/bar") + logger.Debug(x) //nolint:govet + logger.Info(x) //nolint:govet + logger.Warn(x) //nolint:govet + logger.Error(x) //nolint:govet + + exp := `"%2Ffoo%2Fbar%2Fbar"` + expected := []string{ + `level=error msg=` + exp, + `level=warning msg=` + exp, + `level=info msg=` + exp, + `level=debug msg=` + exp, + } + for _, exp := range expected { + if !strings.Contains(buf.String(), exp) { + t.Errorf("expected string %q not found in logs", exp) + } + } + if t.Failed() { + t.Logf("actual output:\n%s", buf.String()) + } +} + +func TestWithFieldsOverrides(t *testing.T) { + logger := New(). + WithFields(map[string]any{"context": "contextvalue"}). + WithFields(map[string]any{"context": "changedcontextvalue"}) + + var fieldvalue any + var ok bool + + if fieldvalue, ok = logger.(*StandardLogger).fields["context"]; !ok { + t.Fatal("Logger did not contain configured field") + } + + if fieldvalue.(string) != "changedcontextvalue" { + t.Fatal("Logger did not contain configured field value") + } +} + +func TestWithFieldsMerges(t *testing.T) { + logger := New(). + WithFields(map[string]any{"context": "contextvalue"}). + WithFields(map[string]any{"anothercontext": "anothercontextvalue"}) + + var fieldvalue any + var ok bool + + if fieldvalue, ok = logger.(*StandardLogger).fields["context"]; !ok { + t.Fatal("Logger did not contain configured field") + } + + if fieldvalue.(string) != "contextvalue" { + t.Fatal("Logger did not contain configured field value") + } + + if fieldvalue, ok = logger.(*StandardLogger).fields["anothercontext"]; !ok { + t.Fatal("Logger did not contain configured field") + } + + if fieldvalue.(string) != "anothercontextvalue" { + t.Fatal("Logger did not contain configured field value") + } +} + +func TestRequestContextFields(t *testing.T) { + fields := RequestContext{ + ClientAddr: "127.0.0.1", + ReqID: 1, + ReqMethod: "GET", + ReqPath: "/test", + }.Fields() + + var fieldvalue any + var ok bool + + if fieldvalue, ok = fields["client_addr"]; !ok { + t.Fatal("Fields did not contain the client_addr field") + } + + if fieldvalue.(string) != "127.0.0.1" { + t.Fatal("Fields did not contain the configured client_addr value") + } + + if fieldvalue, ok = fields["req_id"]; !ok { + t.Fatal("Fields did not contain the req_id field") + } + + if fieldvalue.(uint64) != 1 { + t.Fatal("Fields did not contain the configured req_id value") + } + + if fieldvalue, ok = fields["req_method"]; !ok { + t.Fatal("Fields did not contain the req_method field") + } + + if fieldvalue.(string) != "GET" { + t.Fatal("Fields did not contain the configured req_method value") + } + + if fieldvalue, ok = fields["req_path"]; !ok { + t.Fatal("Fields did not contain the req_path field") + } + + if fieldvalue.(string) != "/test" { + t.Fatal("Fields did not contain the configured req_path value") + } +} + +func TestDecsionIDFromContext(t *testing.T) { + id, err := uuid.New(rand.Reader) + if err != nil { + t.Fatal(err) + } + ctx := WithDecisionID(context.Background(), id) + + act, ok := DecisionIDFromContext(ctx) + if !ok { + t.Fatalf("expected 'ok' to be true") + } + if exp := id; act != exp { + t.Errorf("Expected %q to be %q", act, exp) + } +} diff --git a/third_party/opa/v1/logging/test/test.go b/third_party/opa/v1/logging/test/test.go new file mode 100644 index 000000000000..dacefe5596d3 --- /dev/null +++ b/third_party/opa/v1/logging/test/test.go @@ -0,0 +1,98 @@ +package test + +import ( + "fmt" + "maps" + "sync" + + "github.com/open-policy-agent/opa/v1/logging" +) + +// LogEntry represents a log message. +type LogEntry struct { + Level logging.Level + Fields map[string]any + Message string +} + +// Logger implementation that buffers messages for test purposes. +type Logger struct { + level logging.Level + fields map[string]any + entries *[]LogEntry + mtx *sync.Mutex +} + +// New instantiates new Logger. +func New() *Logger { + return &Logger{ + level: logging.Info, + entries: &[]LogEntry{}, + mtx: &sync.Mutex{}, + } +} + +// WithFields provides additional fields to include in log output. +// Implemented here primarily to be able to switch between implementations without loss of data. +func (l *Logger) WithFields(fields map[string]any) logging.Logger { + l.mtx.Lock() + defer l.mtx.Unlock() + cp := Logger{ + level: l.level, + entries: l.entries, + fields: l.fields, + mtx: l.mtx, + } + flds := make(map[string]any) + maps.Copy(flds, cp.fields) + maps.Copy(flds, fields) + cp.fields = flds + return &cp +} + +// Debug buffers a log message. +func (l *Logger) Debug(f string, a ...any) { + l.append(logging.Debug, f, a...) +} + +// Info buffers a log message. +func (l *Logger) Info(f string, a ...any) { + l.append(logging.Info, f, a...) +} + +// Error buffers a log message. +func (l *Logger) Error(f string, a ...any) { + l.append(logging.Error, f, a...) +} + +// Warn buffers a log message. +func (l *Logger) Warn(f string, a ...any) { + l.append(logging.Warn, f, a...) +} + +// SetLevel set log level. +func (l *Logger) SetLevel(level logging.Level) { + l.level = level +} + +// GetLevel get log level. +func (l *Logger) GetLevel() logging.Level { + return l.level +} + +// Entries returns buffered log entries. +func (l *Logger) Entries() []LogEntry { + l.mtx.Lock() + defer l.mtx.Unlock() + return *l.entries +} + +func (l *Logger) append(lvl logging.Level, f string, a ...any) { + l.mtx.Lock() + defer l.mtx.Unlock() + *l.entries = append(*l.entries, LogEntry{ + Level: lvl, + Fields: l.fields, + Message: fmt.Sprintf(f, a...), + }) +} diff --git a/third_party/opa/v1/metrics/metrics.go b/third_party/opa/v1/metrics/metrics.go new file mode 100644 index 000000000000..316ffe78971e --- /dev/null +++ b/third_party/opa/v1/metrics/metrics.go @@ -0,0 +1,364 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package metrics contains helpers for performance metric management inside the policy engine. +package metrics + +import ( + "encoding/json" + "fmt" + "slices" + "strings" + "sync" + "sync/atomic" + "time" + + go_metrics "github.com/rcrowley/go-metrics" +) + +// Well-known metric names. +const ( + BundleRequest = "bundle_request" + ServerHandler = "server_handler" + ServerQueryCacheHit = "server_query_cache_hit" + SDKDecisionEval = "sdk_decision_eval" + RegoQueryCompile = "rego_query_compile" + RegoQueryEval = "rego_query_eval" + RegoQueryParse = "rego_query_parse" + RegoModuleParse = "rego_module_parse" + RegoDataParse = "rego_data_parse" + RegoModuleCompile = "rego_module_compile" + RegoPartialEval = "rego_partial_eval" + RegoInputParse = "rego_input_parse" + RegoLoadFiles = "rego_load_files" + RegoLoadBundles = "rego_load_bundles" + RegoExternalResolve = "rego_external_resolve" +) + +// Info contains attributes describing the underlying metrics provider. +type Info struct { + Name string `json:"name"` // name is a unique human-readable identifier for the provider. +} + +// Metrics defines the interface for a collection of performance metrics in the +// policy engine. +type Metrics interface { + Info() Info + Timer(name string) Timer + Histogram(name string) Histogram + Counter(name string) Counter + All() map[string]any + Clear() + json.Marshaler +} + +type TimerMetrics interface { + Timers() map[string]any +} + +type metrics struct { + mtx sync.Mutex + timers map[string]Timer + histograms map[string]Histogram + counters map[string]Counter +} + +// New returns a new Metrics object. +func New() Metrics { + return &metrics{ + timers: map[string]Timer{}, + histograms: map[string]Histogram{}, + counters: map[string]Counter{}, + } +} + +// NoOp returns a Metrics implementation that does nothing and costs nothing. +// Used when metrics are expected, but not of interest. +func NoOp() Metrics { + return noOpMetricsInstance +} + +type metric struct { + Key string + Value any +} + +func (*metrics) Info() Info { + return Info{ + Name: "", + } +} + +func (m *metrics) String() string { + all := m.All() + sorted := make([]metric, 0, len(all)) + + for key, value := range all { + sorted = append(sorted, metric{ + Key: key, + Value: value, + }) + } + + slices.SortFunc(sorted, func(a, b metric) int { + return strings.Compare(a.Key, b.Key) + }) + + buf := make([]string, len(sorted)) + for i := range sorted { + buf[i] = fmt.Sprintf("%v:%v", sorted[i].Key, sorted[i].Value) + } + + return strings.Join(buf, " ") +} + +func (m *metrics) MarshalJSON() ([]byte, error) { + return json.Marshal(m.All()) +} + +func (m *metrics) Timer(name string) Timer { + m.mtx.Lock() + defer m.mtx.Unlock() + t, ok := m.timers[name] + if !ok { + t = &timer{} + m.timers[name] = t + } + return t +} + +func (m *metrics) Histogram(name string) Histogram { + m.mtx.Lock() + defer m.mtx.Unlock() + h, ok := m.histograms[name] + if !ok { + h = newHistogram() + m.histograms[name] = h + } + return h +} + +func (m *metrics) Counter(name string) Counter { + m.mtx.Lock() + defer m.mtx.Unlock() + c, ok := m.counters[name] + if !ok { + zero := counter{} + c = &zero + m.counters[name] = c + } + return c +} + +func (m *metrics) All() map[string]any { + m.mtx.Lock() + defer m.mtx.Unlock() + result := make(map[string]any, len(m.timers)+len(m.histograms)+len(m.counters)) + for name, timer := range m.timers { + result[m.formatKey(name, timer)] = timer.Value() + } + for name, hist := range m.histograms { + result[m.formatKey(name, hist)] = hist.Value() + } + for name, cntr := range m.counters { + result[m.formatKey(name, cntr)] = cntr.Value() + } + return result +} + +func (m *metrics) Timers() map[string]any { + m.mtx.Lock() + defer m.mtx.Unlock() + ts := make(map[string]any, len(m.timers)) + for n, t := range m.timers { + ts[m.formatKey(n, t)] = t.Value() + } + return ts +} + +func (m *metrics) Clear() { + m.mtx.Lock() + defer m.mtx.Unlock() + m.timers = map[string]Timer{} + m.histograms = map[string]Histogram{} + m.counters = map[string]Counter{} +} + +func (*metrics) formatKey(name string, metrics any) string { + switch metrics.(type) { + case Timer: + return "timer_" + name + "_ns" + case Histogram: + return "histogram_" + name + case Counter: + return "counter_" + name + default: + return name + } +} + +// Timer defines the interface for a restartable timer that accumulates elapsed +// time. +type Timer interface { + Value() any + Int64() int64 + // Start or resume a timer's time tracking. + Start() + // Stop a timer, and accumulate the delta (in nanoseconds) since it was last + // started. + Stop() int64 +} + +type timer struct { + mtx sync.Mutex + start time.Time + value int64 +} + +func (t *timer) Start() { + t.mtx.Lock() + t.start = time.Now() + t.mtx.Unlock() +} + +func (t *timer) Stop() int64 { + t.mtx.Lock() + defer t.mtx.Unlock() + + var delta int64 + if !t.start.IsZero() { + // Add the delta to the accumulated time value so far. + delta = time.Since(t.start).Nanoseconds() + t.value += delta + t.start = time.Time{} // Reset the start time to zero. + } + + return delta +} + +func (t *timer) Value() any { + return t.Int64() +} + +func (t *timer) Int64() int64 { + t.mtx.Lock() + defer t.mtx.Unlock() + return t.value +} + +// Histogram defines the interface for a histogram with hardcoded percentiles. +type Histogram interface { + Value() any + Update(int64) +} + +type histogram struct { + hist go_metrics.Histogram // is thread-safe because of the underlying ExpDecaySample +} + +func newHistogram() Histogram { + // NOTE(tsandall): the reservoir size and alpha factor are taken from + // https://github.com/rcrowley/go-metrics. They may need to be tweaked in + // the future. + sample := go_metrics.NewExpDecaySample(1028, 0.015) + hist := go_metrics.NewHistogram(sample) + return &histogram{hist} +} + +func (h *histogram) Update(v int64) { + h.hist.Update(v) +} + +func (h *histogram) Value() any { + values := make(map[string]any, 12) + snap := h.hist.Snapshot() + percentiles := snap.Percentiles([]float64{ + 0.5, + 0.75, + 0.9, + 0.95, + 0.99, + 0.999, + 0.9999, + }) + values["count"] = snap.Count() + values["min"] = snap.Min() + values["max"] = snap.Max() + values["mean"] = snap.Mean() + values["stddev"] = snap.StdDev() + values["median"] = percentiles[0] + values["75%"] = percentiles[1] + values["90%"] = percentiles[2] + values["95%"] = percentiles[3] + values["99%"] = percentiles[4] + values["99.9%"] = percentiles[5] + values["99.99%"] = percentiles[6] + return values +} + +// Counter defines the interface for a monotonic increasing counter. +type Counter interface { + Value() any + Incr() + Add(n uint64) +} + +type counter struct { + c uint64 +} + +func (c *counter) Incr() { + atomic.AddUint64(&c.c, 1) +} + +func (c *counter) Add(n uint64) { + atomic.AddUint64(&c.c, n) +} + +func (c *counter) Value() any { + return atomic.LoadUint64(&c.c) +} + +func Statistics(num ...int64) any { + t := newHistogram() + for _, n := range num { + t.Update(n) + } + return t.Value() +} + +type noOpMetrics struct{} +type noOpTimer struct{} +type noOpHistogram struct{} +type noOpCounter struct{} + +var ( + noOpMetricsInstance = &noOpMetrics{} + noOpTimerInstance = &noOpTimer{} + noOpHistogramInstance = &noOpHistogram{} + noOpCounterInstance = &noOpCounter{} +) + +func (*noOpMetrics) Info() Info { return Info{Name: ""} } +func (*noOpMetrics) Timer(name string) Timer { return noOpTimerInstance } +func (*noOpMetrics) Histogram(name string) Histogram { return noOpHistogramInstance } +func (*noOpMetrics) Counter(name string) Counter { return noOpCounterInstance } +func (*noOpMetrics) All() map[string]any { return nil } +func (*noOpMetrics) Clear() {} +func (*noOpMetrics) MarshalJSON() ([]byte, error) { + return []byte(`{"name": ""}`), nil +} + +func (*noOpTimer) Start() {} +func (*noOpTimer) Stop() int64 { return 0 } +func (*noOpTimer) Value() any { return 0 } +func (*noOpTimer) Int64() int64 { return 0 } + +func (*noOpHistogram) Update(v int64) {} +func (*noOpHistogram) Value() any { return nil } + +func (*noOpCounter) Incr() {} +func (*noOpCounter) Add(_ uint64) {} +func (*noOpCounter) Value() any { return 0 } +func (*noOpCounter) Int64() int64 { return 0 } diff --git a/third_party/opa/v1/metrics/metrics_bench_test.go b/third_party/opa/v1/metrics/metrics_bench_test.go new file mode 100644 index 000000000000..c459b2e04375 --- /dev/null +++ b/third_party/opa/v1/metrics/metrics_bench_test.go @@ -0,0 +1,61 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package metrics_test + +import ( + "encoding/json" + "fmt" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/metrics" +) + +func BenchmarkMetricsMarshaling(b *testing.B) { + m := metrics.New() + + // Setup a handful of metrics across each type. + for i := range 10 { + m.Timer(fmt.Sprintf("rego_timer_example_%d", i)).Start() + } + time.Sleep(1 * time.Millisecond) + for i := range 10 { + m.Timer(fmt.Sprintf("rego_timer_example_%d", i)).Stop() + } + + for i := range 10 { + m.Counter(fmt.Sprintf("rego_counter_example_%d", i)).Add(uint64(i)) + } + + for i := range 10 { + for j := range 100 { + m.Histogram(fmt.Sprintf("rego_histogram_example_%d", i)).Update(int64(i + j)) + } + } + + b.ResetTimer() + + for range b.N { + bs, err := json.Marshal(m) + if err != nil { + b.Fatalf("Unexpected error: %v", err) + } + if len(bs) == 0 { + b.Fatalf("No output") + } + } +} + +func BenchmarkMetricsTimerStartStopRestart(b *testing.B) { + m := metrics.New() + + for range b.N { + m.Timer("foo").Start() + _ = m.Timer("foo").Stop() + _ = m.Timer("foo").Stop() // Second stop to exercise the sync guard. + m.Timer("foo").Start() + _ = m.Timer("foo").Stop() + } +} diff --git a/third_party/opa/v1/metrics/metrics_test.go b/third_party/opa/v1/metrics/metrics_test.go new file mode 100644 index 000000000000..f74853b8a0dc --- /dev/null +++ b/third_party/opa/v1/metrics/metrics_test.go @@ -0,0 +1,61 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package metrics + +import ( + "testing" + "time" +) + +func TestMetricsTimer(t *testing.T) { + m := New() + m.Timer("foo").Start() + time.Sleep(time.Millisecond) + m.Timer("foo").Stop() + if m.All()["timer_foo_ns"] == 0 { + t.Fatalf("Expected foo timer to be non-zero: %v", m.All()) + } + m.Clear() + + if len(m.All()) > 0 { + t.Fatalf("Expected metrics to be cleared, but found %v", m.All()) + } +} + +func TestMetricsTimerDoubleStop(t *testing.T) { + m := New() + m.Timer("foo").Start() + + time.Sleep(time.Millisecond) + m.Timer("foo").Stop() + t1 := m.Timer("foo").Int64() + + time.Sleep(time.Millisecond) + m.Timer("foo").Stop() + t2 := m.Timer("foo").Int64() + + if t1 != t2 { + t.Fatalf("Unexpected difference in stopped timer values: %v, %v", t1, t2) + } +} + +func TestMetricsTimerRestart(t *testing.T) { + m := New() + m.Timer("foo").Start() + + time.Sleep(time.Millisecond) + m.Timer("foo").Stop() + t1 := m.Timer("foo").Int64() + + // Restart the timer. + m.Timer("foo").Start() + time.Sleep(time.Millisecond) + m.Timer("foo").Stop() + t2 := m.Timer("foo").Int64() + + if t1 >= t2 { + t.Fatalf("Expected restarted timer to advance, but got same value.: %v, %v", t1, t2) + } +} diff --git a/third_party/opa/v1/plugins/bundle/config.go b/third_party/opa/v1/plugins/bundle/config.go new file mode 100644 index 000000000000..cad437b6bd26 --- /dev/null +++ b/third_party/opa/v1/plugins/bundle/config.go @@ -0,0 +1,256 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "fmt" + "net/url" + "path" + "slices" + "strings" + + "github.com/open-policy-agent/opa/v1/plugins" + + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/download" + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/util" +) + +// ParseConfig validates the config and injects default values. This is +// for the legacy single bundle configuration. This will add the bundle +// to the `Bundles` map to provide compatibility with newer clients. +// Deprecated: Use `ParseBundlesConfig` with `bundles` OPA config option instead +func ParseConfig(config []byte, services []string) (*Config, error) { + if config == nil { + return nil, nil + } + + var parsedConfig Config + + if err := util.Unmarshal(config, &parsedConfig); err != nil { + return nil, err + } + + if err := parsedConfig.validateAndInjectDefaults(services, nil, nil); err != nil { + return nil, err + } + + // For forwards compatibility make a new Source as if the bundle + // was configured with `bundles` in the newer format. + parsedConfig.Bundles = map[string]*Source{ + parsedConfig.Name: { + Config: parsedConfig.Config, + Service: parsedConfig.Service, + Resource: parsedConfig.generateLegacyResourcePath(), + Signing: nil, + Persist: false, + SizeLimitBytes: bundle.DefaultSizeLimitBytes, + }, + } + + return &parsedConfig, nil +} + +// ParseBundlesConfig validates the config and injects default values for +// the defined `bundles`. This expects a map of bundle names to resource +// configurations. +func ParseBundlesConfig(config []byte, services []string) (*Config, error) { + t := plugins.DefaultTriggerMode + return NewConfigBuilder().WithBytes(config).WithServices(services).WithTriggerMode(&t).Parse() +} + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the bundle config +func NewConfigBuilder() *ConfigBuilder { + return &ConfigBuilder{} +} + +// WithBytes sets the raw bundle config +func (b *ConfigBuilder) WithBytes(config []byte) *ConfigBuilder { + b.raw = config + return b +} + +// WithServices sets the services that implement control plane APIs +func (b *ConfigBuilder) WithServices(services []string) *ConfigBuilder { + b.services = services + return b +} + +// WithKeyConfigs sets the public keys to verify a signed bundle +func (b *ConfigBuilder) WithKeyConfigs(keys map[string]*keys.Config) *ConfigBuilder { + b.keys = keys + return b +} + +// WithTriggerMode sets the plugin trigger mode +func (b *ConfigBuilder) WithTriggerMode(trigger *plugins.TriggerMode) *ConfigBuilder { + b.trigger = trigger + return b +} + +// Parse validates the config and injects default values for the defined `bundles`. +func (b *ConfigBuilder) Parse() (*Config, error) { + if b.raw == nil { + return nil, nil + } + + var bundleConfigs map[string]*Source + + if err := util.Unmarshal(b.raw, &bundleConfigs); err != nil { + return nil, err + } + + // Build a `Config` out of the parsed map + c := Config{Bundles: map[string]*Source{}} + for name, source := range bundleConfigs { + if source != nil { + c.Bundles[name] = source + } + } + + err := c.validateAndInjectDefaults(b.services, b.keys, b.trigger) + if err != nil { + return nil, err + } + + return &c, nil +} + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder struct { + raw []byte + services []string + keys map[string]*keys.Config + trigger *plugins.TriggerMode +} + +// Config represents the configuration of the plugin. +// The Config can define a single bundle source or a map of +// `Source` objects defining where/how to download bundles. The +// older single bundle configuration is deprecated and will be +// removed in the future in favor of the `Bundles` map. +type Config struct { + download.Config // Deprecated: Use `Bundles` map instead + + Bundles map[string]*Source + + Name string `json:"name"` // Deprecated: Use `Bundles` map instead + Service string `json:"service"` // Deprecated: Use `Bundles` map instead + Prefix *string `json:"prefix"` // Deprecated: Use `Bundles` map instead +} + +// Source is a configured bundle source to download bundles from +type Source struct { + download.Config + + Service string `json:"service"` + Resource string `json:"resource"` + Signing *bundle.VerificationConfig `json:"signing"` + Persist bool `json:"persist"` + SizeLimitBytes int64 `json:"size_limit_bytes"` +} + +// IsMultiBundle returns whether or not the config is the newer multi-bundle +// style config that uses `bundles` instead of top level bundle information. +// If/when we drop support for the older style config we can remove this too. +func (c *Config) IsMultiBundle() bool { + // If a `Name` was set then the config is in "legacy" single plugin mode + return c.Name == "" +} + +func (c *Config) validateAndInjectDefaults(services []string, keys map[string]*keys.Config, trigger *plugins.TriggerMode) error { + if c.Bundles == nil { + return c.validateAndInjectDefaultsLegacy(services) + } + + for name, source := range c.Bundles { + if source.Resource == "" { + source.Resource = path.Join(defaultBundlePathPrefix, name) + } + + if source.Signing != nil { + err := source.Signing.ValidateAndInjectDefaults(keys) + if err != nil { + return fmt.Errorf("invalid configuration for bundle %q: %s", name, err.Error()) + } + } else if len(keys) > 0 { + source.Signing = bundle.NewVerificationConfig(keys, "", "", nil) + } + + if strings.HasPrefix(source.Resource, "file://") { + if _, err := url.Parse(source.Resource); err != nil { + return fmt.Errorf("invalid URL for bundle %q: %v", name, err) + } + } else { + svc, err := c.getServiceFromList(source.Service, services) + if err != nil { + return fmt.Errorf("invalid configuration for bundle %q: %s", name, err.Error()) + } + source.Service = svc + } + + t, err := plugins.ValidateAndInjectDefaultsForTriggerMode(trigger, source.Trigger) + if err != nil { + return fmt.Errorf("invalid configuration for bundle %q: %w", name, err) + } + + source.Trigger = t + + if err := source.Config.ValidateAndInjectDefaults(); err != nil { + return fmt.Errorf("invalid configuration for bundle %q: %w", name, err) + } + + if source.SizeLimitBytes <= 0 { + source.SizeLimitBytes = bundle.DefaultSizeLimitBytes + } + } + + return nil +} + +func (c *Config) validateAndInjectDefaultsLegacy(services []string) error { + if c.Name == "" { + return fmt.Errorf("invalid bundle name %q", c.Name) + } + + if c.Prefix == nil { + s := defaultBundlePathPrefix + c.Prefix = &s + } + + var err error + c.Service, err = c.getServiceFromList(c.Service, services) + if err == nil { + err = c.Config.ValidateAndInjectDefaults() + } + + if err != nil { + return fmt.Errorf("invalid configuration for bundle %q: %s", c.Name, err.Error()) + } + + return nil +} + +func (*Config) getServiceFromList(service string, services []string) (string, error) { + if service == "" && len(services) != 0 { + return services[0], nil + } + if slices.Contains(services, service) { + return service, nil + } + return service, fmt.Errorf("service name %q not found", service) +} + +// generateLegacyResourcePath will return the Resource path +// from the older style prefix+name configuration. +func (c *Config) generateLegacyResourcePath() string { + joined := path.Join(*c.Prefix, c.Name) + return strings.TrimPrefix(joined, "/") +} + +const ( + defaultBundlePathPrefix = "bundles" +) diff --git a/third_party/opa/v1/plugins/bundle/config_test.go b/third_party/opa/v1/plugins/bundle/config_test.go new file mode 100644 index 000000000000..83d32ffdb0ff --- /dev/null +++ b/third_party/opa/v1/plugins/bundle/config_test.go @@ -0,0 +1,508 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "errors" + "fmt" + "strconv" + "testing" + + "github.com/open-policy-agent/opa/v1/plugins" + + "github.com/open-policy-agent/opa/v1/keys" + + "github.com/open-policy-agent/opa/v1/bundle" + + "sigs.k8s.io/yaml" +) + +func TestConfigValidation(t *testing.T) { + + tests := []struct { + input string + wantErr bool + }{ + { + input: `{}`, + wantErr: true, + }, + { + input: `{"name": "a/b/c", "service": "invalid"}`, + wantErr: true, + }, + { + input: `{"name": "a/b/c", "service": "service2"}`, + wantErr: false, + }, + { + input: `{"name": "a/b/c", "service": "service2", "prefix": "mybundle"}`, + wantErr: false, + }, + { + input: `{"name": "a/b/c", "service": "service2", "prefix": "/"}`, + wantErr: false, + }, + { + input: `{"name": "a/b/c", "service": "service2", "prefix": "/"}`, + wantErr: false, + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("TestConfigValidation_case_%d", i), func(t *testing.T) { + _, err := ParseConfig([]byte(test.input), []string{"service1", "service2"}) + if err != nil && !test.wantErr { + t.Fail() + } + if err == nil && test.wantErr { + t.Fail() + } + }) + } +} + +func TestConfigValid(t *testing.T) { + + in := `{ + "name": "a/b/c", + "service": "service2", + "prefix": "mybundle", + }` + + config, err := ParseConfig([]byte(in), []string{"service1", "service2"}) + if err != nil { + t.Fail() + } + + if config.Name != "a/b/c" { + t.Fatalf("want %v got %v", "a/b/c", config.Name) + } + if config.Service != "service2" { + t.Fatalf("want %v got %v", "service2", config.Name) + } + if *(config.Prefix) != "mybundle" { + t.Fatalf("want %v got %v", "mybundle", *(config.Prefix)) + } +} + +func TestConfigCorrupted(t *testing.T) { + + in := `{"name": "a/b/c", "service": "service2", "prefix: mybundle"}` + + config, err := ParseConfig([]byte(in), []string{"service1", "service2"}) + if err != nil { + t.Fail() + } + + if config.Name != "a/b/c" { + t.Fatalf("want %v got %v", "a/b/c", config.Name) + } + if config.Service != "service2" { + t.Fatalf("want %v got %v", "service2", config.Name) + } + if *(config.Prefix) != "bundles" { + t.Fatalf("want %v got %v", "bundles", *(config.Prefix)) + } +} + +func TestLegacyDownloadPath(t *testing.T) { + testCases := []struct { + prefix string + name string + result string + }{ + { + prefix: "/", + name: "bundles/bundles.tar.gz", + result: "bundles/bundles.tar.gz", + }, + { + prefix: "bundles", + name: "bundles.tar.gz", + result: "bundles/bundles.tar.gz", + }, + { + prefix: "", + name: "bundles/bundles.tar.gz", + result: "bundles/bundles.tar.gz", + }, + { + prefix: "", + name: "/bundles.tar.gz", + result: "bundles.tar.gz", + }, + } + for i, test := range testCases { + t.Run(fmt.Sprintf("case_%d", i), func(t *testing.T) { + config := Config{ + Name: test.name, + Prefix: &test.prefix, + } + + bs, err := yaml.Marshal(&config) + if err != nil { + t.Fatalf("Unexpected error marshalling config: %s", err) + } + + parsed, err := ParseConfig(bs, []string{"service1"}) + if err != nil { + t.Fatalf("Unexpected error parsing config: %s", err) + } + + b, ok := parsed.Bundles[test.name] + if !ok { + t.Fatalf("Expected resource %q on bundle with name %q", test.result, test.name) + } + + if b.Resource != test.result { + t.Errorf("Expected resource %q on bundle with name %q, actual: %s", test.result, test.name, b.Resource) + } + + if b.SizeLimitBytes != bundle.DefaultSizeLimitBytes { + t.Errorf("Expected bundle %q to have the default size limit configured", test.name) + } + }) + } +} + +func TestParseAndValidateBundlesConfig(t *testing.T) { + tests := []struct { + conf string + services []string + wantError bool + }{ + { + conf: "", + services: []string{}, + wantError: false, + }, + { + conf: "{{{", + services: []string{}, + wantError: true, + }, + { + conf: `{"b1":{"service": "s1"}}`, + services: []string{}, + wantError: true, + }, + { + conf: `{"b1":{"service": "s1"}}`, + services: []string{"s1"}, + wantError: false, + }, + { + conf: `{"b1":{"service": "s1"}, "b2":{"service": "s1"}}`, + services: []string{"s1"}, + wantError: false, + }, + { + conf: `{"b1":{"service": "s1"}, "b2":{"service": "s2"}}`, + services: []string{"s1"}, + wantError: true, + }, + { + conf: `{"b1":{"service": "s1"}, "b2":{"service": "s2"}}`, + services: []string{"s1", "s2"}, + wantError: false, + }, + { + conf: `{"b1":{"service": "s1", "polling": {"min_delay_seconds": 1, "max_delay_seconds": 5}}}`, + services: []string{"s1"}, + wantError: false, + }, + { + conf: `{"b1":{"service": "s1", "polling": {"min_delay_seconds": 5, "max_delay_seconds": 1}}}`, + services: []string{"s1"}, + wantError: true, + }, + { + conf: `{"b1":{"service": "s1", "signing": {"keyid": "foo", "scope": "write"}}}`, + services: []string{"s1"}, + wantError: false, + }, + { + conf: `{"b1":{"service": "s1", "signing": {"keyid": "bar", "scope": "write"}}}`, + services: []string{"s1"}, + wantError: true, + }, + } + + keys := map[string]*keys.Config{"foo": {Key: "secret"}} + for i := range tests { + t.Run(strconv.Itoa(i), func(t *testing.T) { + _, err := NewConfigBuilder().WithBytes([]byte(tests[i].conf)).WithServices(tests[i].services). + WithKeyConfigs(keys).Parse() + if err != nil && !tests[i].wantError { + t.Fatalf("Unexpected error: %s", err) + } + if err == nil && tests[i].wantError { + t.Fatalf("Expected an error but didn't get one") + } + }) + } +} + +func TestParseBundlesConfigWithSigning(t *testing.T) { + conf := []byte(` +bundle.tar.gz: + service: s1 +b2: + service: s1 + resource: /b2/path/ +b3: + service: s3 + resource: /some/longer/path/bundle.tar.gz +`) + services := []string{"s1", "s3"} + parsedConfig, err := NewConfigBuilder().WithBytes(conf).WithServices(services).Parse() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if parsedConfig.Name != "" { + t.Fatalf("Expected config `Name` to be empty, actual: %s", parsedConfig.Name) + } + + if len(parsedConfig.Bundles) != 3 { + t.Fatalf("Expected 3 bundles in parsed config, got: %+v", parsedConfig.Bundles) + } + + expectedSources := map[string]struct { + service string + resource string + }{ + "bundle.tar.gz": { + service: "s1", + resource: "bundles/bundle.tar.gz", + }, + "b2": { + service: "s1", + resource: "/b2/path/", + }, + "b3": { + service: "s3", + resource: "/some/longer/path/bundle.tar.gz", + }, + } + + for name, expected := range expectedSources { + actual, ok := parsedConfig.Bundles[name] + if !ok { + t.Fatalf("Expected to have bundle with name %s configured, actual: %+v", name, parsedConfig.Bundles) + } + if expected.resource != actual.Resource { + t.Errorf("Expected resource '%s', found '%s'", expected.resource, actual.Resource) + } + if expected.service != actual.Service { + t.Errorf("Expected service '%s', found '%s'", expected.service, actual.Service) + } + } +} + +func TestParseBundlesConfig(t *testing.T) { + conf := []byte(` +bundle.tar.gz: + service: s1 +b2: + service: s1 + resource: /b2/path/ +b3: + service: s3 + resource: /some/longer/path/bundle.tar.gz +b4: + resource: file:///foo/bar +`) + services := []string{"s1", "s3"} + parsedConfig, err := ParseBundlesConfig(conf, services) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if parsedConfig.Name != "" { + t.Fatalf("Expected config `Name` to be empty, actual: %s", parsedConfig.Name) + } + + expectedSources := map[string]struct { + service string + resource string + }{ + "bundle.tar.gz": { + service: "s1", + resource: "bundles/bundle.tar.gz", + }, + "b2": { + service: "s1", + resource: "/b2/path/", + }, + "b3": { + service: "s3", + resource: "/some/longer/path/bundle.tar.gz", + }, + "b4": { + service: "", // service will be unset because it is a file:// url + resource: "file:///foo/bar", + }, + } + + if len(parsedConfig.Bundles) != len(expectedSources) { + t.Fatalf("Expected %d bundles in parsed config, got: %+v", len(expectedSources), parsedConfig.Bundles) + } + + for name, expected := range expectedSources { + actual, ok := parsedConfig.Bundles[name] + if !ok { + t.Fatalf("Expected to have bundle with name %s configured, actual: %+v", name, parsedConfig.Bundles) + } + if expected.resource != actual.Resource { + t.Errorf("Expected resource '%s', found '%s'", expected.resource, actual.Resource) + } + if expected.service != actual.Service { + t.Errorf("Expected service '%s', found '%s'", expected.service, actual.Service) + } + } +} + +func TestParseBundlesConfigSimpleFileURL(t *testing.T) { + + config := []byte(`{"test": {"resource": "file:///b.tar.gz"}}`) + + _, err := ParseBundlesConfig(config, nil) + if err != nil { + t.Fatal(err) + } + +} + +func TestConfigIsMultiBundle(t *testing.T) { + tests := []struct { + conf Config + expected bool + }{ + { + conf: Config{}, + expected: true, + }, + { + conf: Config{Name: "bundle.tar.gz"}, + expected: false, + }, + { + conf: Config{ + Name: "bundle.tar.gz", + Bundles: map[string]*Source{ + "bundle.tar.gz": {}, + }, + }, + expected: false, + }, + { + conf: Config{ + Name: "", + Bundles: map[string]*Source{ + "bundle.tar.gz": {}, + }, + }, + expected: true, + }, + } + + for i := range tests { + t.Run(strconv.Itoa(i), func(t *testing.T) { + actual := tests[i].conf.IsMultiBundle() + if actual != tests[i].expected { + t.Errorf("expected %t but got %t", tests[i].expected, actual) + } + }) + } +} + +func TestParseConfigTriggerMode(t *testing.T) { + + tm := plugins.TriggerManual + + tests := []struct { + conf string + services []string + triggerMode *plugins.TriggerMode + expected plugins.TriggerMode + wantError bool + err error + }{ + { + conf: `{"b1":{"service": "s1"}}`, + services: []string{"s1"}, + wantError: false, + triggerMode: nil, + expected: plugins.TriggerPeriodic, + }, + { + conf: `{"b1":{"service": "s1", "trigger": "manual"}}`, + services: []string{"s1"}, + wantError: false, + triggerMode: nil, + expected: plugins.TriggerManual, + }, + { + conf: `{"b1":{"service": "s1"}}`, + services: []string{"s1"}, + wantError: false, + triggerMode: &tm, + expected: plugins.TriggerManual, + }, + { + conf: `{"b1":{"service": "s1", "trigger": "manual"}}`, + services: []string{"s1"}, + wantError: false, + triggerMode: &tm, + expected: plugins.TriggerManual, + }, + { + conf: `{"b1":{"service": "s1", "trigger": "periodic"}}`, + services: []string{"s1"}, + wantError: true, + err: errors.New("invalid configuration for bundle \"b1\": trigger mode mismatch: manual and periodic (hint: check discovery configuration)"), + triggerMode: &tm, + }, + { + conf: `{"b1":{"service": "s1", "trigger": "foo"}}`, + services: []string{"s1"}, + wantError: true, + err: errors.New("invalid configuration for bundle \"b1\": invalid trigger mode \"foo\" (want \"periodic\" or \"manual\")"), + triggerMode: nil, + }, + } + + for i := range tests { + t.Run(strconv.Itoa(i), func(t *testing.T) { + config, err := NewConfigBuilder().WithBytes([]byte(tests[i].conf)).WithServices(tests[i].services).WithTriggerMode(tests[i].triggerMode).Parse() + if err != nil && !tests[i].wantError { + t.Fatalf("Unexpected error: %s", err) + } + if err == nil && tests[i].wantError { + t.Fatalf("Expected an error but didn't get one") + } + + if tests[i].wantError { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tests[i].err != nil && tests[i].err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tests[i].err.Error(), err.Error()) + } + } else { + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if *config.Bundles["b1"].Trigger != tests[i].expected { + t.Fatalf("Expected trigger mode %v but got %v", tests[i].expected, *config.Bundles["b1"].Trigger) + } + } + }) + } +} diff --git a/third_party/opa/v1/plugins/bundle/errors.go b/third_party/opa/v1/plugins/bundle/errors.go new file mode 100644 index 000000000000..16fcbb5ec25c --- /dev/null +++ b/third_party/opa/v1/plugins/bundle/errors.go @@ -0,0 +1,53 @@ +package bundle + +import ( + "errors" + "fmt" + + "github.com/open-policy-agent/opa/v1/download" +) + +// Errors represents a list of errors that occurred during a bundle load enriched by the bundle name. +type Errors []Error + +func (e Errors) Unwrap() []error { + output := make([]error, len(e)) + for i := range e { + output[i] = e[i] + } + return output +} +func (e Errors) Error() string { + err := errors.Join(e.Unwrap()...) + return err.Error() +} + +type Error struct { + BundleName string + Code string + HTTPCode int + Message string + Err error +} + +func NewBundleError(bundleName string, cause error) Error { + var ( + httpError download.HTTPError + ) + switch { + case cause == nil: + return Error{BundleName: bundleName, Code: "", HTTPCode: -1, Message: "", Err: nil} + case errors.As(cause, &httpError): + return Error{BundleName: bundleName, Code: errCode, HTTPCode: httpError.StatusCode, Message: httpError.Error(), Err: cause} + default: + return Error{BundleName: bundleName, Code: errCode, HTTPCode: -1, Message: cause.Error(), Err: cause} + } +} + +func (e Error) Error() string { + return fmt.Sprintf("Bundle name: %s, Code: %s, HTTPCode: %d, Message: %s", e.BundleName, errCode, e.HTTPCode, e.Message) +} + +func (e Error) Unwrap() error { + return e.Err +} diff --git a/third_party/opa/v1/plugins/bundle/errors_test.go b/third_party/opa/v1/plugins/bundle/errors_test.go new file mode 100644 index 000000000000..d177dd8aa799 --- /dev/null +++ b/third_party/opa/v1/plugins/bundle/errors_test.go @@ -0,0 +1,144 @@ +package bundle + +import ( + "errors" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/download" +) + +func TestErrors(t *testing.T) { + errs := Errors{ + NewBundleError("foo", errors.New("foo error")), + NewBundleError("bar", errors.New("bar error")), + } + + expected := "Bundle name: foo, Code: bundle_error, HTTPCode: -1, Message: foo error\nBundle name: bar, Code: bundle_error, HTTPCode: -1, Message: bar error" + result := errs.Error() + + if result != expected { + t.Errorf("Expected: %v \nbut got: %v", expected, result) + } +} + +func TestUnwrapSlice(t *testing.T) { + fooErr := NewBundleError("foo", errors.New("foo error")) + barErr := NewBundleError("bar", errors.New("bar error")) + + errs := Errors{fooErr, barErr} + + result := errs.Unwrap() + + if result[0].Error() != fooErr.Error() { + t.Fatalf("expected %v \nbut got: %v", fooErr, result[0]) + } + if result[1].Error() != barErr.Error() { + t.Fatalf("expected %v \nbut got: %v", barErr, result[1]) + } +} + +func TestUnwrap(t *testing.T) { + serverHTTPError := NewBundleError("server", download.HTTPError{StatusCode: 500}) + clientHTTPError := NewBundleError("client", download.HTTPError{StatusCode: 400}) + astErrors := ast.Errors{ast.NewError(ast.ParseErr, ast.NewLocation(nil, "foo.rego", 100, 2), "blarg")} + + errs := Errors{serverHTTPError, clientHTTPError, NewBundleError("ast", astErrors)} + + // unwrap first bundle.Error + var bundleError Error + if !errors.As(errs, &bundleError) { + t.Fatal("failed to unwrap Error") + } + if bundleError.Error() != serverHTTPError.Error() { + t.Fatalf("expected: %v \ngot: %v", serverHTTPError, bundleError) + } + + // unwrap first HTTPError + var httpError download.HTTPError + if !errors.As(errs, &httpError) { + t.Fatal("failed to unwrap Error") + } + if httpError.Error() != serverHTTPError.Err.Error() { + t.Fatalf("expected: %v \ngot: %v", serverHTTPError.Err, httpError) + } + + // unwrap HTTPError from bundle.Error + if !errors.As(bundleError, &httpError) { + t.Fatal("failed to unwrap HTTPError") + } + if httpError.Error() != serverHTTPError.Err.Error() { + t.Fatalf("expected: %v \nbgot: %v", serverHTTPError.Err, httpError) + } + + var unwrappedAstErrors ast.Errors + if !errors.As(errs, &unwrappedAstErrors) { + t.Fatal("failed to unwrap ast.Errors") + } + if unwrappedAstErrors.Error() != astErrors.Error() { + t.Fatalf("expected: %v \ngot: %v", astErrors, unwrappedAstErrors) + } +} + +func TestHTTPErrorWrapping(t *testing.T) { + err := download.HTTPError{StatusCode: 500} + bundleErr := NewBundleError("foo", err) + + if bundleErr.BundleName != "foo" { + t.Fatalf("BundleName: expected: %v \ngot: %v", "foo", bundleErr.BundleName) + } + if bundleErr.HTTPCode != err.StatusCode { + t.Fatalf("HTTPCode: expected: %v \ngot: %v", err.StatusCode, bundleErr.HTTPCode) + } + if bundleErr.Message != err.Error() { + t.Fatalf("Message: expected: %v \ngot: %v", err.Error(), bundleErr.Message) + } + if bundleErr.Code != errCode { + t.Fatalf("Code: expected: %v \ngot: %v", errCode, bundleErr.Code) + } + if bundleErr.Err != err { + t.Fatalf("Err: expected: %v \ngot: %v", err, bundleErr.Err) + } +} + +func TestASTErrorsWrapping(t *testing.T) { + err := ast.Errors{ast.NewError(ast.ParseErr, ast.NewLocation(nil, "foo.rego", 100, 2), "blarg")} + bundleErr := NewBundleError("foo", err) + + if bundleErr.BundleName != "foo" { + t.Fatalf("BundleName: expected: %v \ngot: %v", "foo", bundleErr.BundleName) + } + if bundleErr.HTTPCode != -1 { + t.Fatalf("HTTPCode: expected: %v \ngot: %v", -1, bundleErr.HTTPCode) + } + if bundleErr.Message != err.Error() { + t.Fatalf("Message: expected: %v \ngot: %v", err.Error(), bundleErr.Message) + } + if bundleErr.Code != errCode { + t.Fatalf("Code: expected: %v \ngot: %v", errCode, bundleErr.Code) + } + if bundleErr.Err.Error() != err.Error() { + t.Fatalf("Err: expected: %v \ngot: %v", err.Error(), bundleErr.Err.Error()) + } +} + +func TestGenericErrorWrapping(t *testing.T) { + err := errors.New("foo error") + bundleErr := NewBundleError("foo", err) + + if bundleErr.BundleName != "foo" { + t.Fatalf("BundleName: expected: %v \ngot: %v", "foo", bundleErr.BundleName) + } + if bundleErr.HTTPCode != -1 { + t.Fatalf("HTTPCode: expected: %v \ngot: %v", -1, bundleErr.HTTPCode) + } + if bundleErr.Message != err.Error() { + t.Fatalf("Message: expected: %v \ngot: %v", err.Error(), bundleErr.Message) + } + if bundleErr.Code != errCode { + t.Fatalf("Code: expected: %v \ngot: %v", errCode, bundleErr.Code) + } + if bundleErr.Err.Error() != err.Error() { + t.Fatalf("Err: expected: %v \ngot: %v", err.Error(), bundleErr.Err.Error()) + } +} diff --git a/third_party/opa/v1/plugins/bundle/plugin.go b/third_party/opa/v1/plugins/bundle/plugin.go new file mode 100644 index 000000000000..63d2c27694e8 --- /dev/null +++ b/third_party/opa/v1/plugins/bundle/plugin.go @@ -0,0 +1,875 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package bundle implements bundle loading. +package bundle + +import ( + "context" + "errors" + "fmt" + "io" + "maps" + "net/url" + "os" + "path/filepath" + "reflect" + "runtime" + "strings" + "sync" + "time" + + bundleUtils "github.com/open-policy-agent/opa/internal/bundle" + "github.com/open-policy-agent/opa/internal/ref" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/download" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/storage" +) + +// maxActivationRetry represents the maximum number of attempts +// to activate persisted bundles. Activation retries are useful +// in scenarios where a persisted bundle may have a dependency on some +// other persisted bundle. As there are no ordering guarantees for which +// bundle loads first, retries could help in the bundle activation process. +// Typically, multiple bundles are not encouraged. The value chosen for +// maxActivationRetry allows upto 10 bundles to successfully activate +// in the worst case that they depend on each other. At the same time, it also +// ensures that too much time is not spent to activate bundles that will never +// successfully activate. +const maxActivationRetry = 10 + +var goos = runtime.GOOS + +// Loader defines the interface that the bundle plugin uses to control bundle +// loading via HTTP, disk, etc. +type Loader interface { + Start(context.Context) + Stop(context.Context) + Trigger(context.Context) error + SetCache(string) + ClearCache() +} + +// Plugin implements bundle activation. +type Plugin struct { + config Config + manager *plugins.Manager // plugin manager for storage and service clients + status map[string]*Status // current status for each bundle + etags map[string]string // etag on last successful activation + listeners map[any]func(Status) // listeners to send status updates to + bulkListeners map[any]func(map[string]*Status) // listeners to send aggregated status updates to + downloaders map[string]Loader + logger logging.Logger + mtx sync.Mutex + cfgMtx sync.RWMutex + ready bool + bundlePersistPath string + stopped bool +} + +// New returns a new Plugin with the given config. +func New(parsedConfig *Config, manager *plugins.Manager) *Plugin { + initialStatus := map[string]*Status{} + for name := range parsedConfig.Bundles { + initialStatus[name] = &Status{ + Name: name, + } + } + + p := &Plugin{ + manager: manager, + config: *parsedConfig, + status: initialStatus, + downloaders: make(map[string]Loader), + etags: make(map[string]string), + ready: false, + logger: manager.Logger(), + } + + manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateNotReady}) + return p +} + +// Name identifies the plugin on manager. +const Name = "bundle" + +// Lookup returns the bundle plugin registered with the manager. +func Lookup(manager *plugins.Manager) *Plugin { + if p := manager.Plugin(Name); p != nil { + return p.(*Plugin) + } + return nil +} + +// Start runs the plugin. The plugin will periodically try to download bundles +// from the configured service. When a new bundle is downloaded, the data and +// policies are extracted and inserted into storage. +func (p *Plugin) Start(ctx context.Context) error { + p.mtx.Lock() + defer p.mtx.Unlock() + + var err error + + p.bundlePersistPath, err = p.getBundlePersistPath() + if err != nil { + return err + } + + p.loadAndActivateBundlesFromDisk(ctx) + + p.initDownloaders(ctx) + for name, dl := range p.downloaders { + p.log(name).Info("Starting bundle loader.") + dl.Start(ctx) + } + return nil +} + +// Stop stops the plugin. +func (p *Plugin) Stop(ctx context.Context) { + p.mtx.Lock() + stopDownloaders := map[string]Loader{} + maps.Copy(stopDownloaders, p.downloaders) + p.downloaders = nil + p.stopped = true + p.mtx.Unlock() + + for name, dl := range stopDownloaders { + p.log(name).Info("Stopping bundle loader.") + dl.Stop(ctx) + } +} + +// Reconfigure notifies the plugin that it's configuration has changed. +// Any bundle configs that have changed or been added/removed will take +// affect. +func (p *Plugin) Reconfigure(ctx context.Context, config any) { + // Reconfiguring should not occur in parallel, lock to ensure + // nothing swaps underneath us with the current p.config and the updated one. + // Use p.cfgMtx instead of p.mtx to not block any bundle downloads/activations + // that are in progress. We upgrade to p.mtx locking after stopping downloaders. + p.cfgMtx.Lock() + + // Look for any bundles that have had their config changed, are new, or have been removed + newConfig := config.(*Config) + newBundles, updatedBundles, deletedBundles := p.configDelta(newConfig) + p.config = *newConfig + + p.cfgMtx.Unlock() + + if len(updatedBundles) == 0 && len(newBundles) == 0 && len(deletedBundles) == 0 { + // no relevant config changes + return + } + + // Stop the downloaders outside p.mtx to allow them to finish handling any in-progress requests. + for name, dl := range p.downloaders { + _, updated := updatedBundles[name] + _, deleted := deletedBundles[name] + if updated || deleted { + dl.Stop(ctx) + } + } + + // Only lock p.mtx once we start changing the internal maps + // and downloader configs. + p.mtx.Lock() + defer p.mtx.Unlock() + + // Cleanup existing downloaders that are deleted + for name := range p.downloaders { + if _, deleted := deletedBundles[name]; deleted { + p.log(name).Info("Bundle loader configuration removed. Stopping bundle loader.") + delete(p.downloaders, name) + delete(p.status, name) + delete(p.etags, name) + } + } + + // Deactivate the bundles that were removed + params := storage.WriteParams + params.Context = storage.NewContext() // TODO(sr): metrics? + err := storage.Txn(ctx, p.manager.Store, params, func(txn storage.Transaction) error { + opts := &bundle.DeactivateOpts{ + Ctx: ctx, + Store: p.manager.Store, + Txn: txn, + BundleNames: deletedBundles, + ParserOptions: p.manager.ParserOptions(), + } + err := bundle.Deactivate(opts) + if err != nil { + p.manager.Logger().Error(fmt.Sprint(deletedBundles), "Failed to deactivate bundles: %s", err) + return err + } + return nil + }) + if err != nil { + // TODO(patrick-east): This probably shouldn't panic.. But OPA shouldn't + // continue in a potentially inconsistent state. + panic(errors.New("Unable deactivate bundle: " + err.Error())) + } + + readyNow := p.ready + + bundles := p.getBundlesCpy() + for name, source := range bundles { + _, updated := updatedBundles[name] + _, isNew := newBundles[name] + + if isNew || updated { + if isNew { + p.status[name] = &Status{Name: name} + p.log(name).Info("New bundle loader configuration added. Starting bundle loader.") + } else { + p.log(name).Info("Bundle loader configuration changed. Restarting bundle loader.") + } + + downloader := p.newDownloader(name, source, bundles) + + etag := p.readBundleEtagFromStore(ctx, name) + downloader.SetCache(etag) + + p.downloaders[name] = downloader + p.etags[name] = etag + p.downloaders[name].Start(ctx) + + readyNow = false + } + } + + if !readyNow { + p.ready = false + p.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateNotReady}) + } + +} + +// Loaders returns the map of bundle loaders configured on this plugin. +func (p *Plugin) Loaders() map[string]Loader { + return p.downloaders +} + +// Trigger triggers a bundle download on all configured bundles. +func (p *Plugin) Trigger(ctx context.Context) error { + var errs Errors + + p.mtx.Lock() + downloaders := map[string]Loader{} + maps.Copy(downloaders, p.downloaders) + p.mtx.Unlock() + + for name, d := range downloaders { + // plugin callback will also log the trigger error and include it in the bundle status + err := d.Trigger(ctx) + + // only return errors for TriggerMode manual as periodic bundles will be retried + if err != nil { + trigger := p.Config().Bundles[name].Trigger + if trigger != nil && *trigger == plugins.TriggerManual { + errs = append(errs, NewBundleError(name, err)) + } + } + } + if len(errs) > 0 { + return errs + } + return nil +} + +// Register a listener to receive status updates. The name must be comparable. +// The listener will receive a status update for each bundle configured, they are +// not going to be aggregated. For all status updates use `RegisterBulkListener`. +func (p *Plugin) Register(name any, listener func(Status)) { + p.mtx.Lock() + defer p.mtx.Unlock() + + if p.listeners == nil { + p.listeners = map[any]func(Status){} + } + + p.listeners[name] = listener +} + +// Unregister a listener to stop receiving status updates. +func (p *Plugin) Unregister(name any) { + p.mtx.Lock() + defer p.mtx.Unlock() + + delete(p.listeners, name) +} + +// RegisterBulkListener registers a listener to receive bulk (aggregated) status updates. The name must be comparable. +func (p *Plugin) RegisterBulkListener(name any, listener func(map[string]*Status)) { + p.mtx.Lock() + defer p.mtx.Unlock() + + if p.bulkListeners == nil { + p.bulkListeners = map[any]func(map[string]*Status){} + } + + p.bulkListeners[name] = listener +} + +// UnregisterBulkListener unregisters a listener to stop receiving aggregated status updates. +func (p *Plugin) UnregisterBulkListener(name any) { + p.mtx.Lock() + defer p.mtx.Unlock() + + delete(p.bulkListeners, name) +} + +// Config returns the plugins current configuration +func (p *Plugin) Config() *Config { + p.cfgMtx.RLock() + defer p.cfgMtx.RUnlock() + return &Config{ + Name: p.config.Name, + Bundles: p.getBundlesCpy(), + } +} + +func (p *Plugin) initDownloaders(ctx context.Context) { + bundles := p.getBundlesCpy() + + // Initialize a downloader for each bundle configured. + for name, source := range bundles { + downloader := p.newDownloader(name, source, bundles) + + etag := p.readBundleEtagFromStore(ctx, name) + downloader.SetCache(etag) + + p.downloaders[name] = downloader + p.etags[name] = etag + } +} + +func (p *Plugin) readBundleEtagFromStore(ctx context.Context, name string) string { + var etag string + err := storage.Txn(ctx, p.manager.Store, storage.TransactionParams{}, func(txn storage.Transaction) error { + var loadErr error + etag, loadErr = bundle.ReadBundleEtagFromStore(ctx, p.manager.Store, txn, name) + if loadErr != nil && !storage.IsNotFound(loadErr) { + p.log(name).Error("Failed to load bundle etag from store: %v", loadErr) + return loadErr + } + return nil + }) + if err != nil { + // TODO: This probably shouldn't panic. But OPA shouldn't + // continue in a potentially inconsistent state. + panic(errors.New("Unable to load bundle etag from store: " + err.Error())) + } + + return etag +} + +func (p *Plugin) loadAndActivateBundlesFromDisk(ctx context.Context) { + + persistedBundles := map[string]*bundle.Bundle{} + + bundles := p.getBundlesCpy() + + p.cfgMtx.RLock() + isMultiBundle := p.config.IsMultiBundle() + p.cfgMtx.RUnlock() + + for name, src := range bundles { + if p.persistBundle(name, bundles) { + b, err := p.loadBundleFromDisk(p.bundlePersistPath, name, src) + if err != nil { + p.log(name).Error("Failed to load bundle from disk: %v", err) + p.status[name].SetError(err) + continue + } + + if b == nil { + continue + } + + persistedBundles[name] = b + } + } + + if len(persistedBundles) == 0 { + return + } + + for range maxActivationRetry { + + numActivatedBundles := 0 + for name, b := range persistedBundles { + p.status[name].Metrics = metrics.New() + p.status[name].Type = b.Type() + + err := p.activate(ctx, name, b, isMultiBundle) + if err != nil { + p.log(name).Error("Bundle activation failed: %v", err) + p.status[name].SetError(err) + continue + } + + p.status[name].SetError(nil) + p.status[name].SetActivateSuccess(b.Manifest.Revision) + + p.checkPluginReadiness() + + p.log(name).Debug("Bundle loaded from disk and activated successfully.") + numActivatedBundles++ + } + + if numActivatedBundles == len(persistedBundles) { + return + } + } +} + +func (p *Plugin) newDownloader(name string, source *Source, bundles map[string]*Source) Loader { + + if u, err := url.Parse(source.Resource); err == nil && u.Scheme == "file" { + return &fileLoader{ + name: name, + path: u.Path, + bvc: source.Signing, + sizeLimitBytes: source.SizeLimitBytes, + f: p.oneShot, + bundleParserOpts: p.manager.ParserOptions(), + } + } + + conf := source.Config + client := p.manager.Client(source.Service) + path := source.Resource + callback := func(ctx context.Context, u download.Update) { + // wrap the callback to include the name of the bundle that was updated + p.oneShot(ctx, name, u) + } + if strings.ToLower(client.Config().Type) == "oci" { + ociStorePath := filepath.Join(os.TempDir(), "opa", "oci") // use temporary folder /tmp/opa/oci + if p.manager.Config.PersistenceDirectory != nil { + ociStorePath = filepath.Join(*p.manager.Config.PersistenceDirectory, "oci") + } + return download.NewOCI(conf, client, path, ociStorePath). + WithCallback(callback). + WithBundleVerificationConfig(source.Signing). + WithSizeLimitBytes(source.SizeLimitBytes). + WithBundlePersistence(p.persistBundle(name, bundles)). + WithBundleParserOpts(p.manager.ParserOptions()) + } + return download.New(conf, client, path). + WithCallback(callback). + WithBundleVerificationConfig(source.Signing). + WithSizeLimitBytes(source.SizeLimitBytes). + WithBundlePersistence(p.persistBundle(name, bundles)). + WithLazyLoadingMode(true). + WithBundleName(name). + WithBundleParserOpts(p.manager.ParserOptions()) +} + +func (p *Plugin) oneShot(ctx context.Context, name string, u download.Update) { + p.mtx.Lock() + defer p.mtx.Unlock() + + p.process(ctx, name, u) + + for _, listener := range p.listeners { + listener(*p.status[name]) + } + + for _, listener := range p.bulkListeners { + // Send a copy of the full status map to the bulk listeners. + // They shouldn't have access to the original underlying + // map, primarily for thread safety issues with modifications + // made to it. + statusCpy := map[string]*Status{} + for k, v := range p.status { + v := *v + statusCpy[k] = &v + } + listener(statusCpy) + } +} + +func (p *Plugin) process(ctx context.Context, name string, u download.Update) { + + if u.Metrics != nil { + p.status[name].Metrics = u.Metrics + } else { + p.status[name].Metrics = metrics.New() + } + + p.status[name].SetRequest() + + if u.Error != nil { + p.log(name).Error("Bundle load failed: %v", u.Error) + p.status[name].SetError(u.Error) + if !p.stopped { + etag := p.etags[name] + p.downloaders[name].SetCache(etag) + } + return + } + + p.status[name].LastSuccessfulRequest = p.status[name].LastRequest + + if u.Bundle != nil { + p.status[name].Type = u.Bundle.Type() + p.status[name].LastSuccessfulDownload = p.status[name].LastSuccessfulRequest + + p.status[name].Metrics.Timer(metrics.RegoLoadBundles).Start() + defer p.status[name].Metrics.Timer(metrics.RegoLoadBundles).Stop() + + p.cfgMtx.RLock() + isMultiBundle := p.config.IsMultiBundle() + p.cfgMtx.RUnlock() + + if err := p.activate(ctx, name, u.Bundle, isMultiBundle); err != nil { + p.log(name).Error("Bundle activation failed: %v", err) + p.status[name].SetError(err) + if !p.stopped { + etag := p.etags[name] + p.downloaders[name].SetCache(etag) + } + return + } + + if u.Bundle.Type() == bundle.SnapshotBundleType && p.persistBundle(name, p.getBundlesCpy()) { + p.log(name).Debug("Persisting bundle to disk in progress.") + + err := p.saveBundleToDisk(name, u.Raw) + if err != nil { + p.log(name).Error("Persisting bundle to disk failed: %v", err) + p.status[name].SetError(err) + if !p.stopped { + etag := p.etags[name] + p.downloaders[name].SetCache(etag) + } + return + } + p.log(name).Debug("Bundle persisted to disk successfully at path %v.", filepath.Join(p.bundlePersistPath, name)) + } + + p.status[name].SetError(nil) + p.status[name].SetActivateSuccess(u.Bundle.Manifest.Revision) + p.status[name].SetBundleSize(u.Size) + + if u.ETag != "" { + p.log(name).Info("Bundle loaded and activated successfully. Etag updated to %v.", u.ETag) + } else { + p.log(name).Info("Bundle loaded and activated successfully.") + } + p.etags[name] = u.ETag + + // If the plugin wasn't ready yet then check if we are now after activating this bundle. + p.checkPluginReadiness() + return + } + + if etag, ok := p.etags[name]; ok && u.ETag == etag { + p.log(name).Debug("Bundle load skipped, server replied with not modified.") + p.status[name].SetError(nil) + + // The downloader received a 304 (same etag as saved in local state), update plugin readiness + p.checkPluginReadiness() + return + } +} + +func (p *Plugin) checkPluginReadiness() { + if !p.ready { + readyNow := true // optimistically + for _, status := range p.status { + if len(status.Errors) > 0 || (status.LastSuccessfulActivation == time.Time{}) { + readyNow = false // Not ready yet, check again on next bundle activation. + break + } + } + + if readyNow { + p.ready = true + p.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateOK}) + } + } +} + +func (p *Plugin) activate(ctx context.Context, name string, b *bundle.Bundle, isMultiBundle bool) error { + p.log(name).Debug("Bundle activation in progress (%v). Opening storage transaction.", b.Manifest.Revision) + + params := storage.WriteParams + params.Context = storage.NewContext().WithMetrics(p.status[name].Metrics) + + err := storage.Txn(ctx, p.manager.Store, params, func(txn storage.Transaction) error { + p.log(name).Debug("Opened storage transaction (%v).", txn.ID()) + defer p.log(name).Debug("Closing storage transaction (%v).", txn.ID()) + + // Compile the bundle modules with a new compiler and set it on the + // transaction params for use by onCommit hooks. + // If activating a delta bundle, use the manager's compiler which should have + // the polices compiled on it. + var compiler *ast.Compiler + if b.Type() == bundle.DeltaBundleType { + compiler = p.manager.GetCompiler() + } + + if compiler == nil { + compiler = ast.NewCompiler() + } + + compiler = compiler.WithPathConflictsCheck(storage.NonEmpty(ctx, p.manager.Store, txn)). + WithEnablePrintStatements(p.manager.EnablePrintStatements()) + + if b.Manifest.Roots != nil { + compiler = compiler.WithPathConflictsCheckRoots(*b.Manifest.Roots) + } + + var activateErr error + + opts := &bundle.ActivateOpts{ + Ctx: ctx, + Store: p.manager.Store, + Txn: txn, + TxnCtx: params.Context, + Compiler: compiler, + Metrics: p.status[name].Metrics, + Bundles: map[string]*bundle.Bundle{name: b}, + ParserOptions: p.manager.ParserOptions(), + } + + if p.manager.Info != nil { + + skipKnownSchemaCheck := p.manager.Info.Get(ast.StringTerm("skip_known_schema_check")) + isAuthzEnabled := p.manager.Info.Get(ast.StringTerm("authorization_enabled")) + + if ast.BooleanTerm(true).Equal(isAuthzEnabled) && ast.BooleanTerm(false).Equal(skipKnownSchemaCheck) { + authorizationDecisionRef, err := ref.ParseDataPath(*p.manager.Config.DefaultAuthorizationDecision) + if err != nil { + return err + } + opts.AuthorizationDecisionRef = authorizationDecisionRef + } + } + + if isMultiBundle { + activateErr = bundle.Activate(opts) + } else { + activateErr = bundle.ActivateLegacy(opts) + } + + plugins.SetCompilerOnContext(params.Context, compiler) + + resolvers, err := bundleUtils.LoadWasmResolversFromStore(ctx, p.manager.Store, txn, nil) + if err != nil { + return err + } + + plugins.SetWasmResolversOnContext(params.Context, resolvers) + + return activateErr + }) + + return err +} + +func (*Plugin) persistBundle(name string, bundles map[string]*Source) bool { + bundleSrc := bundles[name] + + if bundleSrc == nil { + return false + } + return bundleSrc.Persist +} + +// configDelta will return a map of new bundle sources, updated bundle sources, and a set of deleted bundle names +func (p *Plugin) configDelta(newConfig *Config) (map[string]*Source, map[string]*Source, map[string]struct{}) { + deletedBundles := map[string]struct{}{} + + // p.cfgMtx lock held at calling site, so we don't need + // to get a copy of the bundles map here + for name := range p.config.Bundles { + deletedBundles[name] = struct{}{} + } + newBundles := map[string]*Source{} + updatedBundles := map[string]*Source{} + for name, source := range newConfig.Bundles { + oldSource, found := p.config.Bundles[name] + if !found { + newBundles[name] = source + } else { + delete(deletedBundles, name) + if !reflect.DeepEqual(oldSource, source) { + updatedBundles[name] = source + } + } + } + + return newBundles, updatedBundles, deletedBundles +} + +func (p *Plugin) saveBundleToDisk(name string, raw io.Reader) error { + + bundleName := getNormalizedBundleName(name) + + bundleDir := filepath.Join(p.bundlePersistPath, bundleName) + bundleFile := filepath.Join(bundleDir, "bundle.tar.gz") + + tmpFile, saveErr := saveCurrentBundleToDisk(bundleDir, raw) + if saveErr != nil { + p.log(name).Error("Failed to save new bundle to disk: %v", saveErr) + + if err := os.Remove(tmpFile); err != nil { + p.log(name).Warn("Failed to remove temp file ('%s'): %v", tmpFile, err) + } + + if _, err := os.Stat(bundleFile); err == nil { + p.log(name).Warn("Older version of activated bundle persisted, ignoring error") + return nil + } + return saveErr + } + + return os.Rename(tmpFile, bundleFile) +} + +func saveCurrentBundleToDisk(path string, raw io.Reader) (string, error) { + return bundleUtils.SaveBundleToDisk(path, raw) +} + +func (p *Plugin) loadBundleFromDisk(path, name string, src *Source) (*bundle.Bundle, error) { + bundleName := getNormalizedBundleName(name) + + if src != nil { + return bundleUtils.LoadBundleFromDiskForRegoVersion(p.manager.ParserOptions().RegoVersion, path, bundleName, src.Signing) + } + return bundleUtils.LoadBundleFromDiskForRegoVersion(p.manager.ParserOptions().RegoVersion, path, bundleName, nil) +} + +func (p *Plugin) log(name string) logging.Logger { + if p.logger == nil { + p.logger = logging.Get() + } + return p.logger.WithFields(map[string]any{"name": name, "plugin": Name}) +} + +func (p *Plugin) getBundlePersistPath() (string, error) { + persistDir, err := p.manager.Config.GetPersistenceDirectory() + if err != nil { + return "", err + } + + return filepath.Join(persistDir, "bundles"), nil +} + +func (p *Plugin) getBundlesCpy() map[string]*Source { + p.cfgMtx.RLock() + defer p.cfgMtx.RUnlock() + bundlesCpy := map[string]*Source{} + for k, v := range p.config.Bundles { + v := *v + bundlesCpy[k] = &v + } + return bundlesCpy +} + +// getNormalizedBundleName returns a version of the input with +// invalid file and directory name characters on Windows escaped. +// It returns the input as-is for non-Windows systems. +func getNormalizedBundleName(name string) string { + if goos != "windows" { + return name + } + + sb := new(strings.Builder) + for i := range len(name) { + if isReservedCharacter(rune(name[i])) { + sb.WriteString(fmt.Sprintf("\\%c", name[i])) + } else { + sb.WriteByte(name[i]) + } + } + + return sb.String() +} + +// isReservedCharacter checks if the input is a reserved character on Windows that should not be +// used in file and directory names +// For details, see https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions. +func isReservedCharacter(r rune) bool { + return r == '<' || r == '>' || r == ':' || r == '"' || r == '/' || r == '\\' || r == '|' || r == '?' || r == '*' +} + +type fileLoader struct { + name string + path string + bvc *bundle.VerificationConfig + sizeLimitBytes int64 + f func(context.Context, string, download.Update) + bundleParserOpts ast.ParserOptions +} + +func (fl *fileLoader) Start(ctx context.Context) { + go func() { + fl.oneShot(ctx) + }() +} + +func (*fileLoader) Stop(context.Context) { + +} + +func (*fileLoader) ClearCache() { + +} + +func (*fileLoader) SetCache(string) { + +} + +func (fl *fileLoader) Trigger(ctx context.Context) error { + fl.oneShot(ctx) + return nil +} + +func (fl *fileLoader) oneShot(ctx context.Context) { + var u download.Update + u.Metrics = metrics.New() + + info, err := os.Stat(fl.path) + u.Error = err + if err != nil { + fl.f(ctx, fl.name, u) + return + } + + var reader *bundle.Reader + + if info.IsDir() { + reader = bundle.NewCustomReader(bundle.NewDirectoryLoader(fl.path)) + } else { + f, err := os.Open(fl.path) + u.Error = err + if err != nil { + fl.f(ctx, fl.name, u) + return + } + defer f.Close() + reader = bundle.NewReader(f) + } + + b, err := reader. + WithMetrics(u.Metrics). + WithBundleVerificationConfig(fl.bvc). + WithLazyLoadingMode(bundle.HasExtension()). + WithSizeLimitBytes(fl.sizeLimitBytes). + WithRegoVersion(fl.bundleParserOpts.RegoVersion). + Read() + u.Error = err + if err == nil { + u.Bundle = &b + } + fl.f(ctx, fl.name, u) +} diff --git a/third_party/opa/v1/plugins/bundle/plugin_test.go b/third_party/opa/v1/plugins/bundle/plugin_test.go new file mode 100644 index 000000000000..9d19613d4c0b --- /dev/null +++ b/third_party/opa/v1/plugins/bundle/plugin_test.go @@ -0,0 +1,7460 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: goconst // string duplication is for test readability. +package bundle + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + "path" + "path/filepath" + "reflect" + "slices" + "sort" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/internal/runtime" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/config" + "github.com/open-policy-agent/opa/v1/download" + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/disk" + "github.com/open-policy-agent/opa/v1/storage/inmem" + inmemtst "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +const ( + deltaBundleSize = 128 + snapshotBundleSize = 1024 +) + +func TestPluginOneShot(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := New(&Config{}, manager) + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + module := "package foo\n\ncorge=1" + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: util.MustUnmarshalJSON([]byte(`{"foo": {"bar": 1, "baz": "qux"}}`)).(map[string]any), + Modules: []bundle.ModuleFile{ + { + Path: "/foo/bar", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + Etag: "foo", + } + + b.Manifest.Init() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New(), Size: snapshotBundleSize}) + + ensurePluginState(t, plugin, plugins.StateOK) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", status.Type) + } else if status.Size != snapshotBundleSize { + t.Fatalf("expected snapshot bundle size %d but got %d", snapshotBundleSize, status.Size) + } + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 { + t.Fatal("Expected 1 policy") + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + exp := []byte("package foo\n\ncorge=1") + if err != nil { + t.Fatal(err) + } else if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + expData := util.MustUnmarshalJSON([]byte(`{ + "foo": {"bar": 1, "baz": "qux"}, + "system": { + "bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}} + } + }`)) + if err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(data, expData) { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } +} + +func TestPluginOneShotWithAstStore(t *testing.T) { + t.Parallel() + + ctx := context.Background() + store := inmem.NewWithOpts(inmem.OptRoundTripOnWrite(false), inmem.OptReturnASTValuesOnRead(true)) + manager := getTestManagerWithOpts(nil, store) + plugin := New(&Config{}, manager) + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: util.MustUnmarshalJSON([]byte(`{"foo": {"bar": 1, "baz": "qux"}}`)).(map[string]any), + Etag: "foo", + } + + b.Manifest.Init() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New(), Size: snapshotBundleSize}) + + ensurePluginState(t, plugin, plugins.StateOK) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", status.Type) + } else if status.Size != snapshotBundleSize { + t.Fatalf("expected snapshot bundle size %d but got %d", snapshotBundleSize, status.Size) + } + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + expData := ast.MustParseTerm(`{"foo": {"bar": 1, "baz": "qux"}, "system": {"bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}}}}`) + if err != nil { + t.Fatal(err) + } else if ast.Compare(data, expData) != 0 { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } +} + +func TestPluginOneShotV1Compatible(t *testing.T) { + t.Parallel() + + // Note: modules are parsed before passed to plugin, so any expected errors must be triggered by the compiler stage. + tests := []struct { + note string + v1Compatible bool + module string + expErrs []string + }{ + { + note: "v0.x", + module: `package foo +import future.keywords +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v0.x, shadowed import (no error)", + module: `package foo +import future.keywords +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0", + v1Compatible: true, + module: `package foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0, shadowed import", + v1Compatible: true, + module: `package foo +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + regoVersion := ast.RegoV0 + if tc.v1Compatible { + regoVersion = ast.RegoV1 + } + popts := ast.ParserOptions{RegoVersion: regoVersion} + + ctx := context.Background() + manager := getTestManager() + plugin := New(&Config{}, manager) + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "/foo/bar", + Parsed: ast.MustParseModuleWithOpts(tc.module, popts), + Raw: []byte(tc.module), + }, + }, + Etag: "foo", + } + + b.Manifest.Init() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New(), Size: snapshotBundleSize}) + + if tc.expErrs != nil { + ensurePluginState(t, plugin, plugins.StateNotReady) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", status.Type) + } else if errs := status.Errors; len(errs) != len(tc.expErrs) { + t.Fatalf("expected errors:\n\n%v\n\nbut got:\n\n%v", tc.expErrs, errs) + } else { + for _, expErr := range tc.expErrs { + found := false + for _, err := range errs { + if strings.Contains(err.Error(), expErr) { + found = true + break + } + } + if !found { + t.Fatalf("expected error:\n\n%v\n\nbut got:\n\n%v", expErr, errs) + } + } + } + } else { + ensurePluginState(t, plugin, plugins.StateOK) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", status.Type) + } else if status.Size != snapshotBundleSize { + t.Fatalf("expected snapshot bundle size %d but got %d", snapshotBundleSize, status.Size) + } + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 { + t.Fatal("Expected 1 policy") + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + exp := []byte(tc.module) + if err != nil { + t.Fatal(err) + } else if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + } + }) + } +} + +func TestPluginOneShotWithBundleRegoVersion(t *testing.T) { + t.Parallel() + + // Note: modules are parsed before passed to plugin, so any expected errors must be triggered by the compiler stage. + tests := []struct { + note string + managerRegoVersion ast.RegoVersion + bundleRegoVersion *ast.RegoVersion + module string + expErrs []string + }{ + { + note: "v0.x manager, no bundle version", + managerRegoVersion: ast.RegoV0, + module: `package foo +import future.keywords +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, no bundle version, shadowed import (no error)", + managerRegoVersion: ast.RegoV0, + module: `package foo +import future.keywords +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + }, + + { + note: "v0.x manager, v0.x bundle", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: pointTo(ast.RegoV0), + module: `package foo +import future.keywords +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v0.x bundle, shadowed import (no error)", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: pointTo(ast.RegoV0), + module: `package foo +import future.keywords +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + }, + + { + note: "v0.x manager, v1.0 bundle", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: pointTo(ast.RegoV1), + module: `package foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v1.0 bundle, shadowed import (error)", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: pointTo(ast.RegoV1), + module: `package foo +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + + { + note: "v1.0 manager, no bundle version", + managerRegoVersion: ast.RegoV1, + module: `package foo +import future.keywords +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, no bundle version, shadowed import (error)", + managerRegoVersion: ast.RegoV1, + module: `package foo +import future.keywords +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + + { + note: "v1.0 manager, v0.x bundle", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: pointTo(ast.RegoV0), + module: `package foo +import future.keywords +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v0.x bundle, shadowed import (no error)", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: pointTo(ast.RegoV0), + module: `package foo +import future.keywords +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + }, + + { + note: "v1.0 manager, v1.0 bundle", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: pointTo(ast.RegoV1), + module: `package foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v1.0 bundle, shadowed import (error)", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: pointTo(ast.RegoV1), + module: `package foo +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + managerPopts := ast.ParserOptions{RegoVersion: tc.managerRegoVersion} + manager, err := plugins.New(nil, "test-instance-id", inmemtst.New(), + plugins.WithParserOptions(managerPopts)) + if err != nil { + t.Fatal(err) + } + plugin := New(&Config{}, manager) + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + var bundlePopts ast.ParserOptions + m := bundle.Manifest{Revision: "quickbrownfaux"} + if tc.bundleRegoVersion != nil { + m.SetRegoVersion(*tc.bundleRegoVersion) + bundlePopts = ast.ParserOptions{RegoVersion: *tc.bundleRegoVersion} + } else { + bundlePopts = managerPopts + } + b := bundle.Bundle{ + Manifest: m, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "/foo/bar", + Parsed: ast.MustParseModuleWithOpts(tc.module, bundlePopts), + Raw: []byte(tc.module), + }, + }, + Etag: "foo", + } + + b.Manifest.Init() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New(), Size: snapshotBundleSize}) + + if tc.expErrs != nil { + ensurePluginState(t, plugin, plugins.StateNotReady) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", status.Type) + } else if errs := status.Errors; len(errs) != len(tc.expErrs) { + t.Fatalf("expected errors:\n\n%v\n\nbut got:\n\n%v", tc.expErrs, errs) + } else { + for _, expErr := range tc.expErrs { + found := false + for _, err := range errs { + if strings.Contains(err.Error(), expErr) { + found = true + break + } + } + if !found { + t.Fatalf("expected error:\n\n%v\n\nbut got:\n\n%v", expErr, errs) + } + } + } + } else { + ensurePluginState(t, plugin, plugins.StateOK) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", status.Type) + } else if status.Size != snapshotBundleSize { + t.Fatalf("expected snapshot bundle size %d but got %d", snapshotBundleSize, status.Size) + } + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 { + t.Fatal("Expected 1 policy") + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + exp := []byte(tc.module) + if err != nil { + t.Fatal(err) + } else if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + } + }) + } +} + +func TestPluginOneShotWithAuthzSchemaVerification(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + manager := getTestManager() + + info, err := runtime.Term(runtime.Params{Config: nil, IsAuthorizationEnabled: true}) + if err != nil { + t.Fatal(err) + } + manager.Info = info + + plugin := New(&Config{}, manager) + + bundleName := "test-bundle" + + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + // authz rules with no error + authzModule := `package system.authz + import rego.v1 + + default allow := false + + allow if { + input.identity == "foo" + }` + + module := "package foo\n\ncorge=1" + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + URL: "/authz.rego", + Path: "/authz.rego", + Parsed: ast.MustParseModule(authzModule), + Raw: []byte(authzModule), + }, + { + Path: "/foo/bar", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New()}) + + ensurePluginState(t, plugin, plugins.StateOK) + + // authz rules with errors + authzModule = `package system.authz + import rego.v1 + + default allow := false + + allow if { + input.identty == "foo" # type error 1 + } + + allow if { + helper1 + } + + helper1 if { + helper2 + } + + helper2 if { + input.method == 123 # type error 2 + } + + dont_type_check_me if { + input.methd == "GET" # type error 3 + }` + + b = bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + URL: "/authz.rego", + Path: "/authz.rego", + Parsed: ast.MustParseModule(authzModule), + Raw: []byte(authzModule), + }, + { + Path: "/foo/bar", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New()}) + + ensurePluginState(t, plugin, plugins.StateOK) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if len(status.Errors) != 2 { + t.Fatalf("expected 2 errors but got %v", len(status.Errors)) + } + + // disable authorization to ensure bundle activates with bad authz policy + info, err = runtime.Term(runtime.Params{Config: nil, IsAuthorizationEnabled: false}) + if err != nil { + t.Fatal(err) + } + plugin.manager.Info = info + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New()}) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if len(status.Errors) != 0 { + t.Fatalf("expected 0 errors but got %v", len(status.Errors)) + } + + // enable authorization but skip type checking of known input schemas + info, err = runtime.Term(runtime.Params{Config: nil, IsAuthorizationEnabled: true, SkipKnownSchemaCheck: true}) + if err != nil { + t.Fatal(err) + } + plugin.manager.Info = info + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New()}) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if len(status.Errors) != 0 { + t.Fatalf("expected 0 errors but got %v", len(status.Errors)) + } +} + +func TestPluginOneShotWithAuthzSchemaVerificationNonDefaultAuthzPath(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + manager := getTestManager() + + s := "/foo/authz/allow" + manager.Config.DefaultAuthorizationDecision = &s + + info, err := runtime.Term(runtime.Params{Config: nil, IsAuthorizationEnabled: true}) + if err != nil { + t.Fatal(err) + } + manager.Info = info + + plugin := New(&Config{}, manager) + + bundleName := "test-bundle" + + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + module := "package foo\n\ncorge=1" + + authzModule := `package foo.authz + import rego.v1 + + default allow := false + + allow if { + input.identty == "foo" # type error 1 + } + + allow if { + helper + } + + helper if { + input.method == 123 # type error 2 + } + + dont_type_check_me if { + input.methd == "GET" # type error 3 + }` + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + URL: "/authz.rego", + Path: "/authz.rego", + Parsed: ast.MustParseModule(authzModule), + Raw: []byte(authzModule), + }, + { + Path: "/foo/bar", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New()}) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if len(status.Errors) != 2 { + t.Fatalf("expected 2 errors but got %v", len(status.Errors)) + } + + // no authz policy + b = bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "/foo/bar", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New()}) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if len(status.Errors) != 0 { + t.Fatalf("expected 0 errors but got %v", len(status.Errors)) + } +} + +func TestPluginStartLazyLoadInMem(t *testing.T) { + t.Parallel() + + readMode := []struct { + note string + readAst bool + }{ + { + note: "read raw", + readAst: false, + }, + { + note: "read ast", + readAst: true, + }, + } + + for _, rm := range readMode { + t.Run(rm.note, func(t *testing.T) { + ctx := context.Background() + + module := "package authz\n\ncorge=1" + + // setup fake http server with mock bundle + mockBundle1 := bundle.Bundle{ + Data: map[string]any{"p": "x1"}, + Modules: []bundle.ModuleFile{ + { + URL: "/bar/policy.rego", + Path: "/bar/policy.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + Manifest: bundle.Manifest{ + Roots: &[]string{"p", "authz"}, + }, + } + + s1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + err := bundle.NewWriter(w).Write(mockBundle1) + if err != nil { + t.Fatal(err) + } + })) + + mockBundle2 := bundle.Bundle{ + Data: map[string]any{"q": "x2"}, + Modules: []bundle.ModuleFile{}, + Manifest: bundle.Manifest{ + Roots: &[]string{"q"}, + }, + } + + s2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + err := bundle.NewWriter(w).Write(mockBundle2) + if err != nil { + t.Fatal(err) + } + })) + + config := fmt.Appendf(nil, `{ + "services": { + "default": { + "url": %q + }, + "acmecorp": { + "url": %q + } + } + }`, s1.URL, s2.URL) + + manager := getTestManagerWithOpts(config, inmem.NewWithOpts(inmem.OptReturnASTValuesOnRead(rm.readAst))) + defer manager.Stop(ctx) + + var mode plugins.TriggerMode = "manual" + + plugin := New(&Config{ + Bundles: map[string]*Source{ + "test-1": { + Service: "default", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + Config: download.Config{Trigger: &mode}, + }, + "test-2": { + Service: "acmecorp", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + Config: download.Config{Trigger: &mode}, + }, + }, + }, manager) + + statusCh := make(chan map[string]*Status) + + // register for bundle updates to observe changes and start the plugin + plugin.RegisterBulkListener("test-case", func(st map[string]*Status) { + statusCh <- st + }) + + err := plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + + // manually trigger bundle download on all configured bundles + go func() { + _ = plugin.Trigger(ctx) + }() + + // wait for bundle update and then assert on data content + <-statusCh + <-statusCh + + result, err := storage.ReadOne(ctx, manager.Store, storage.Path{"p"}) + if err != nil { + t.Fatal(err) + } + + if rm.readAst { + expected, _ := ast.InterfaceToValue(mockBundle1.Data["p"]) + if ast.Compare(result, expected) != 0 { + t.Fatalf("expected data to be %v but got %v", expected, result) + } + } else if !reflect.DeepEqual(result, mockBundle1.Data["p"]) { + t.Fatalf("expected data to be %v but got %v", mockBundle1.Data, result) + } + + result, err = storage.ReadOne(ctx, manager.Store, storage.Path{"q"}) + if err != nil { + t.Fatal(err) + } + + if rm.readAst { + expected, _ := ast.InterfaceToValue(mockBundle2.Data["q"]) + if ast.Compare(result, expected) != 0 { + t.Fatalf("expected data to be %v but got %v", expected, result) + } + } else if !reflect.DeepEqual(result, mockBundle2.Data["q"]) { + t.Fatalf("expected data to be %v but got %v", mockBundle2.Data, result) + } + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 { + t.Fatal("Expected 1 policy") + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + exp := []byte("package authz\n\ncorge=1") + if err != nil { + t.Fatal(err) + } else if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + if err != nil { + t.Fatal(err) + } + + expected := `{ + "p": "x1", "q": "x2", + "system": { + "bundles": {"test-1": {"etag": "", "manifest": {"revision": "", "roots": ["p", "authz"]}}, "test-2": {"etag": "", "manifest": {"revision": "", "roots": ["q"]}}} + } + }` + if rm.readAst { + expData := ast.MustParseTerm(expected) + if ast.Compare(data, expData) != 0 { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } + } else { + expData := util.MustUnmarshalJSON([]byte(expected)) + if !reflect.DeepEqual(data, expData) { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } + } + }) + } +} + +func TestPluginOneShotDiskStorageMetrics(t *testing.T) { + t.Parallel() + + test.WithTempFS(nil, func(dir string) { + ctx := context.Background() + met := metrics.New() + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + Partitions: []storage.Path{ + storage.MustParsePath("/foo"), + }, + }) + if err != nil { + t.Fatal(err) + } + manager := getTestManagerWithOpts(nil, store) + defer manager.Stop(ctx) + plugin := New(&Config{}, manager) + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: met} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + module := "package foo\n\ncorge=1" + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: util.MustUnmarshalJSON([]byte(`{"foo": {"bar": 1, "baz": "qux"}}`)).(map[string]any), + Modules: []bundle.ModuleFile{ + { + Path: "/foo/bar", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + met = metrics.New() + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: met}) + + ensurePluginState(t, plugin, plugins.StateOK) + + // NOTE(sr): These assertions reflect the current behaviour only! Not prescriptive. + name := "disk_deleted_keys" + if exp, act := 3, met.Counter(name).Value(); act.(uint64) != uint64(exp) { + t.Errorf("%s: expected %v, got %v", name, exp, act) + } + name = "disk_written_keys" + if exp, act := 6, met.Counter(name).Value(); act.(uint64) != uint64(exp) { + t.Errorf("%s: expected %v, got %v", name, exp, act) + } + name = "disk_read_keys" + if exp, act := 13, met.Counter(name).Value(); act.(uint64) != uint64(exp) { + t.Errorf("%s: expected %v, got %v", name, exp, act) + } + name = "disk_read_bytes" + if exp, act := 269, met.Counter(name).Value(); act.(uint64) != uint64(exp) { + t.Errorf("%s: expected %v, got %v", name, exp, act) + } + for _, timer := range []string{ + "disk_commit", + "disk_write", + "disk_read", + } { + if act := met.Timer(timer).Int64(); act <= 0 { + t.Errorf("%s: expected non-zero timer, got %v", timer, act) + } + } + if t.Failed() { + t.Logf("all metrics: %v", met.All()) + } + + // Ensure we can read it all back -- this is the only bundle plugin test using disk storage, + // so some duplicating with TestPluginOneShot is OK: + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 { + t.Fatal("Expected 1 policy") + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + exp := []byte("package foo\n\ncorge=1") + if err != nil { + t.Fatal(err) + } else if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + expData := util.MustUnmarshalJSON([]byte(`{ + "foo": {"bar": 1, "baz": "qux"}, + "system": { + "bundles": {"test-bundle": {"etag": "", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}} + } + }`)) + if err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(data, expData) { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } + }) +} + +func TestPluginOneShotDeltaBundle(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := New(&Config{}, manager) + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + module := "package a\n\ncorge=1" + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux", Roots: &[]string{"a"}}, + Data: map[string]any{ + "a": map[string]any{ + "baz": "qux", + }, + }, + Modules: []bundle.ModuleFile{ + { + Path: "a/policy.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New()}) + + ensurePluginState(t, plugin, plugins.StateOK) + + // simulate a delta bundle download + + // replace a value + p1 := bundle.PatchOperation{ + Op: "replace", + Path: "a/baz", + Value: "bux", + } + + // add a new object member + p2 := bundle.PatchOperation{ + Op: "upsert", + Path: "/a/foo", + Value: []any{"hello", "world"}, + } + + b2 := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "delta", Roots: &[]string{"a"}}, + Patch: bundle.Patch{Data: []bundle.PatchOperation{p1, p2}}, + Etag: "foo", + } + + plugin.process(ctx, bundleName, download.Update{Bundle: &b2, Metrics: metrics.New(), Size: deltaBundleSize}) + + ensurePluginState(t, plugin, plugins.StateOK) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.DeltaBundleType { + t.Fatalf("expected delta bundle but got %v", status.Type) + } else if status.Size != deltaBundleSize { + t.Fatalf("expected delta bundle size %d but got %d", deltaBundleSize, status.Size) + } + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } + if len(ids) != 1 { + t.Fatalf("Expected 1 policy, got %d", len(ids)) + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + if err != nil { + t.Fatal(err) + } + exp := []byte("package a\n\ncorge=1") + if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + if err != nil { + t.Fatal(err) + } + expData := util.MustUnmarshalJSON([]byte(`{ + "a": {"baz": "bux", "foo": ["hello", "world"]}, + "system": { + "bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "delta", "roots": ["a"]}}} + } + }`)) + if !reflect.DeepEqual(data, expData) { + t.Fatalf("Bad data content. Exp:\n%#v\n\nGot:\n\n%#v", expData, data) + } +} + +func TestPluginOneShotDeltaBundleWithAstStore(t *testing.T) { + t.Parallel() + + ctx := context.Background() + store := inmem.NewWithOpts(inmem.OptRoundTripOnWrite(false), inmem.OptReturnASTValuesOnRead(true)) + manager := getTestManagerWithOpts(nil, store) + plugin := New(&Config{}, manager) + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + module := "package a\n\ncorge=1" + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux", Roots: &[]string{"a"}}, + Data: map[string]any{ + "a": map[string]any{ + "baz": "qux", + }, + }, + Modules: []bundle.ModuleFile{ + { + Path: "a/policy.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New()}) + + ensurePluginState(t, plugin, plugins.StateOK) + + // simulate a delta bundle download + + // replace a value + p1 := bundle.PatchOperation{ + Op: "replace", + Path: "a/baz", + Value: "bux", + } + + // add a new object member + p2 := bundle.PatchOperation{ + Op: "upsert", + Path: "/a/foo", + Value: []any{"hello", "world"}, + } + + b2 := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "delta", Roots: &[]string{"a"}}, + Patch: bundle.Patch{Data: []bundle.PatchOperation{p1, p2}}, + Etag: "foo", + } + + plugin.process(ctx, bundleName, download.Update{Bundle: &b2, Metrics: metrics.New(), Size: deltaBundleSize}) + + ensurePluginState(t, plugin, plugins.StateOK) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.DeltaBundleType { + t.Fatalf("expected delta bundle but got %v", status.Type) + } else if status.Size != deltaBundleSize { + t.Fatalf("expected delta bundle size %d but got %d", deltaBundleSize, status.Size) + } + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } + if len(ids) != 1 { + t.Fatalf("Expected 1 policy, got %d", len(ids)) + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + if err != nil { + t.Fatal(err) + } + exp := []byte("package a\n\ncorge=1") + if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + if err != nil { + t.Fatal(err) + } + expData := ast.MustParseTerm(`{ + "a": {"baz": "bux", "foo": ["hello", "world"]}, + "system": { + "bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "delta", "roots": ["a"]}}} + } + }`) + if ast.Compare(data, expData) != 0 { + t.Fatalf("Bad data content. Exp:\n%#v\n\nGot:\n\n%#v", expData, data) + } +} + +func TestPluginStart(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + bundles := map[string]*Source{} + + plugin := New(&Config{Bundles: bundles}, manager) + err := plugin.Start(ctx) + if err != nil { + t.Fatal("unexpected error:", err) + } +} + +func TestStop(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + var longPollTimeout int64 = 3 + done := make(chan struct{}) + tsURLBase := "/opa-test/" + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !strings.HasPrefix(r.URL.Path, tsURLBase) { + t.Fatalf("Invalid request URL path: %s, expected prefix %s", r.URL.Path, tsURLBase) + } + + close(done) + + // simulate long operation + time.Sleep(time.Duration(longPollTimeout) * time.Second) + fmt.Fprintln(w) // Note: this is an invalid bundle and will fail the download + })) + defer ts.Close() + + ctx := context.Background() + manager := getTestManager() + + serviceName := "test-svc" + err := manager.Reconfigure(&config.Config{ + Services: fmt.Appendf(nil, "{%q:{ \"url\": %q}}", serviceName, ts.URL+tsURLBase), + }) + if err != nil { + t.Fatalf("Error configuring plugin manager: %s", err) + } + + triggerPolling := plugins.TriggerPeriodic + baseConf := download.Config{Polling: download.PollingConfig{LongPollingTimeoutSeconds: &longPollTimeout}, Trigger: &triggerPolling} + + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + } + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName} + + callback := func(ctx context.Context, u download.Update) { + plugin.oneShot(ctx, bundleName, u) + } + plugin.downloaders[bundleName] = download.New(baseConf, plugin.manager.Client(serviceName), bundleName).WithCallback(callback) + + err = plugin.Start(ctx) + if err != nil { + t.Fatal("unexpected error:", err) + } + + // Give time for a long poll request to be initiated + <-done + + plugin.Stop(ctx) + + if plugin.status[bundleName].Code != errCode { + t.Fatalf("expected error code %v but got %v", errCode, plugin.status[bundleName].Code) + } + + if !strings.Contains(plugin.status[bundleName].Message, "context canceled") { + t.Fatalf("unexpected error message %v", plugin.status[bundleName].Message) + } +} + +func TestPluginOneShotBundlePersistence(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + + dir := t.TempDir() + + bundleName := "test-bundle" + bundleSource := Source{ + Persist: true, + } + + bundles := map[string]*Source{} + bundles[bundleName] = &bundleSource + + plugin := New(&Config{Bundles: bundles}, manager) + + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + plugin.bundlePersistPath = filepath.Join(dir, ".opa") + + ensurePluginState(t, plugin, plugins.StateNotReady) + + // simulate a bundle download error with no bundle on disk + plugin.oneShot(ctx, bundleName, download.Update{Error: errors.New("unknown error")}) + + if plugin.status[bundleName].Message == "" { + t.Fatal("expected error but got none") + } + + ensurePluginState(t, plugin, plugins.StateNotReady) + + // download a bundle and persist to disk. Then verify the bundle persisted to disk + module := "package foo\n\ncorge=1" + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: util.MustUnmarshalJSON([]byte(`{"foo": {"bar": 1, "baz": "qux"}}`)).(map[string]any), + Modules: []bundle.ModuleFile{ + { + URL: "/foo/bar.rego", + Path: "/foo/bar.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + Etag: "foo", + } + + b.Manifest.Init() + expBndl := b.Copy() // We're opting out of roundtripping in storage/inmem, so we copy ourselves. + + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).UseModulePath(true).Write(b); err != nil { + t.Fatal("unexpected error:", err) + } + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New(), Raw: &buf}) + + ensurePluginState(t, plugin, plugins.StateOK) + + result, err := plugin.loadBundleFromDisk(plugin.bundlePersistPath, bundleName, nil) + if err != nil { + t.Fatal("unexpected error:", err) + } + + if !result.Equal(expBndl) { + t.Fatalf("expected the downloaded bundle to be equal to the one loaded from disk: result=%v, exp=%v", result, expBndl) + } + + // simulate a bundle download error and verify that the bundle on disk is activated + plugin.oneShot(ctx, bundleName, download.Update{Error: errors.New("unknown error")}) + + ensurePluginState(t, plugin, plugins.StateOK) + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 { + t.Fatal("Expected 1 policy") + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + exp := []byte("package foo\n\ncorge=1") + if err != nil { + t.Fatal(err) + } else if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + expData := util.MustUnmarshalJSON([]byte(`{ + "foo": {"bar": 1, "baz": "qux"}, + "system": { + "bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}} + } + }`)) + if err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(data, expData) { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } +} + +func TestPluginOneShotBundlePersistenceV1Compatible(t *testing.T) { + t.Parallel() + + // Note: modules are parsed before passed to plugin, so any expected errors must be triggered by the compiler stage. + tests := []struct { + note string + v1Compatible bool + module string + expErrs []string + }{ + { + note: "v0.x", + module: `package foo +import future.keywords +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v0.x, shadowed import (no error)", + module: `package foo +import future.keywords +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0", + v1Compatible: true, + module: `package foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0, shadowed import", + v1Compatible: true, + module: `package foo +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + regoVersion := ast.RegoV0 + if tc.v1Compatible { + regoVersion = ast.RegoV1 + } + popts := ast.ParserOptions{RegoVersion: regoVersion} + + ctx := context.Background() + manager, err := plugins.New(nil, "test-instance-id", inmemtst.New(), plugins.WithParserOptions(popts)) + if err != nil { + t.Fatal("unexpected error:", err) + } + + dir := t.TempDir() + + bundleName := "test-bundle" + bundleSource := Source{ + Persist: true, + } + + bundles := map[string]*Source{} + bundles[bundleName] = &bundleSource + + plugin := New(&Config{Bundles: bundles}, manager) + + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + plugin.bundlePersistPath = filepath.Join(dir, ".opa") + + ensurePluginState(t, plugin, plugins.StateNotReady) + + // simulate a bundle download error with no bundle on disk + plugin.oneShot(ctx, bundleName, download.Update{Error: errors.New("unknown error")}) + + if plugin.status[bundleName].Message == "" { + t.Fatal("expected error but got none") + } + + ensurePluginState(t, plugin, plugins.StateNotReady) + + // download a bundle and persist to disk. Then verify the bundle persisted to disk + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: util.MustUnmarshalJSON([]byte(`{"foo": {"bar": 1, "baz": "qux"}}`)).(map[string]any), + Modules: []bundle.ModuleFile{ + { + URL: "/foo/bar.rego", + Path: "/foo/bar.rego", + Parsed: ast.MustParseModuleWithOpts(tc.module, popts), + Raw: []byte(tc.module), + }, + }, + Etag: "foo", + } + + b.Manifest.Init() + expBndl := b.Copy() // We're opting out of roundtripping in storage/inmem, so we copy ourselves. + + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).UseModulePath(true).Write(b); err != nil { + t.Fatal("unexpected error:", err) + } + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New(), Raw: &buf}) + + if tc.expErrs != nil { + ensurePluginState(t, plugin, plugins.StateNotReady) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", status.Type) + } else if errs := status.Errors; len(errs) != len(tc.expErrs) { + t.Fatalf("expected errors:\n\n%v\n\nbut got:\n\n%v", tc.expErrs, errs) + } else { + for _, expErr := range tc.expErrs { + found := false + for _, err := range errs { + if strings.Contains(err.Error(), expErr) { + found = true + break + } + } + if !found { + t.Fatalf("expected error:\n\n%v\n\nbut got:\n\n%v", expErr, errs) + } + } + } + } else { + ensurePluginState(t, plugin, plugins.StateOK) + + result, err := plugin.loadBundleFromDisk(plugin.bundlePersistPath, bundleName, nil) + if err != nil { + t.Fatal("unexpected error:", err) + } + + if !result.Equal(expBndl) { + t.Fatalf("expected the downloaded bundle to be equal to the one loaded from disk: result=%v, exp=%v", result, expBndl) + } + + // simulate a bundle download error and verify that the bundle on disk is activated + plugin.oneShot(ctx, bundleName, download.Update{Error: errors.New("unknown error")}) + + ensurePluginState(t, plugin, plugins.StateOK) + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 { + t.Fatal("Expected 1 policy") + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + exp := []byte(tc.module) + if err != nil { + t.Fatal(err) + } else if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + + expData := util.MustUnmarshalJSON([]byte(`{ + "foo": {"bar": 1, "baz": "qux"}, + "system": { + "bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}} + } + }`)) + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + if err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(data, expData) { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } + } + }) + } +} + +func TestPluginOneShotBundlePersistenceWithBundleRegoVersion(t *testing.T) { + t.Parallel() + + // Note: modules are parsed before passed to plugin, so any expected errors must be triggered by the compiler stage. + tests := []struct { + note string + managerRegoVersion ast.RegoVersion + bundleRegoVersion *ast.RegoVersion + module string + expErrs []string + }{ + { + note: "v0.x manager, no bundle rego version", + managerRegoVersion: ast.RegoV0, + module: `package foo +import future.keywords +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, no bundle rego version, shadowed import (no error)", + managerRegoVersion: ast.RegoV0, + module: `package foo +import future.keywords +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v0.x bundle", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: pointTo(ast.RegoV0), + module: `package foo +import future.keywords +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v0.x bundle, shadowed import (no error)", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: pointTo(ast.RegoV0), + module: `package foo +import future.keywords +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v1.0 bundle", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: pointTo(ast.RegoV1), + module: `package foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v1.0 bundle, shadowed import", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: pointTo(ast.RegoV1), + module: `package foo +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + + { + note: "v1.0 manager, no bundle rego version", + managerRegoVersion: ast.RegoV1, + module: `package foo +import future.keywords +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, no bundle rego version, shadowed import (no error)", + managerRegoVersion: ast.RegoV1, + module: `package foo +import future.keywords +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v1.0 manager, v0.x bundle", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: pointTo(ast.RegoV0), + module: `package foo +import future.keywords +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v0.x bundle, shadowed import (no error)", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: pointTo(ast.RegoV0), + module: `package foo +import future.keywords +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v1.0 bundle", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: pointTo(ast.RegoV1), + module: `package foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v1.0 bundle, shadowed import", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: pointTo(ast.RegoV1), + module: `package foo +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + managerPopts := ast.ParserOptions{RegoVersion: tc.managerRegoVersion} + manager, err := plugins.New(nil, "test-instance-id", inmemtst.New(), + plugins.WithParserOptions(managerPopts)) + if err != nil { + t.Fatal("unexpected error:", err) + } + + dir := t.TempDir() + + bundleName := "test-bundle" + bundleSource := Source{ + Persist: true, + } + + bundles := map[string]*Source{} + bundles[bundleName] = &bundleSource + + plugin := New(&Config{Bundles: bundles}, manager) + + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + plugin.bundlePersistPath = filepath.Join(dir, ".opa") + + ensurePluginState(t, plugin, plugins.StateNotReady) + + // simulate a bundle download error with no bundle on disk + plugin.oneShot(ctx, bundleName, download.Update{Error: errors.New("unknown error")}) + + if plugin.status[bundleName].Message == "" { + t.Fatal("expected error but got none") + } + + ensurePluginState(t, plugin, plugins.StateNotReady) + + // download a bundle and persist to disk. Then verify the bundle persisted to disk + var bundlePopts ast.ParserOptions + m := bundle.Manifest{Revision: "quickbrownfaux"} + if tc.bundleRegoVersion != nil { + m.SetRegoVersion(*tc.bundleRegoVersion) + bundlePopts = ast.ParserOptions{RegoVersion: *tc.bundleRegoVersion} + } else { + bundlePopts = managerPopts + } + b := bundle.Bundle{ + Manifest: m, + Data: util.MustUnmarshalJSON([]byte(`{"foo": {"bar": 1, "baz": "qux"}}`)).(map[string]any), + Modules: []bundle.ModuleFile{ + { + URL: "/foo/bar.rego", + Path: "/foo/bar.rego", + Parsed: ast.MustParseModuleWithOpts(tc.module, bundlePopts), + Raw: []byte(tc.module), + }, + }, + Etag: "foo", + } + + b.Manifest.Init() + expBndl := b.Copy() // We're opting out of roundtripping in storage/inmem, so we copy ourselves. + + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).UseModulePath(true).Write(b); err != nil { + t.Fatal("unexpected error:", err) + } + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New(), Raw: &buf}) + + if tc.expErrs != nil { + ensurePluginState(t, plugin, plugins.StateNotReady) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", status.Type) + } else if errs := status.Errors; len(errs) != len(tc.expErrs) { + t.Fatalf("expected errors:\n\n%v\n\nbut got:\n\n%v", tc.expErrs, errs) + } else { + for _, expErr := range tc.expErrs { + found := false + for _, err := range errs { + if strings.Contains(err.Error(), expErr) { + found = true + break + } + } + if !found { + t.Fatalf("expected error:\n\n%v\n\nbut got:\n\n%v", expErr, errs) + } + } + } + } else { + ensurePluginState(t, plugin, plugins.StateOK) + + result, err := plugin.loadBundleFromDisk(plugin.bundlePersistPath, bundleName, nil) + if err != nil { + t.Fatal("unexpected error:", err) + } + + if !result.Equal(expBndl) { + t.Fatalf("expected the downloaded bundle to be equal to the one loaded from disk: result=%v, exp=%v", result, expBndl) + } + + // simulate a bundle download error and verify that the bundle on disk is activated + plugin.oneShot(ctx, bundleName, download.Update{Error: errors.New("unknown error")}) + + ensurePluginState(t, plugin, plugins.StateOK) + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 { + t.Fatal("Expected 1 policy") + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + exp := []byte(tc.module) + if err != nil { + t.Fatal(err) + } else if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + + var manifestRegoVersion string + var moduleRegoVersion string + if tc.bundleRegoVersion != nil { + manifestRegoVersion = fmt.Sprintf(`, "rego_version": %d`, bundleRegoVersion(*tc.bundleRegoVersion)) + + if *tc.bundleRegoVersion != tc.managerRegoVersion { + moduleRegoVersion = fmt.Sprintf(`,"modules": {"test-bundle/foo/bar.rego": {"rego_version": %d}}`, tc.bundleRegoVersion.Int()) + } + } + + expData := util.MustUnmarshalJSON(fmt.Appendf(nil, `{ + "foo": {"bar": 1, "baz": "qux"}, + "system": { + "bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "quickbrownfaux"%s, "roots": [""]}}}%s + } + }`, + manifestRegoVersion, moduleRegoVersion)) + + if err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(data, expData) { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } + } + }) + } +} + +func TestPluginOneShotSignedBundlePersistence(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + + dir := t.TempDir() + + bundleName := "test-bundle" + vc := bundle.NewVerificationConfig(map[string]*bundle.KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "foo", "", nil) + bundleSource := Source{ + Persist: true, + Signing: vc, + } + + bundles := map[string]*Source{} + bundles[bundleName] = &bundleSource + + plugin := New(&Config{Bundles: bundles}, manager) + + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + plugin.bundlePersistPath = filepath.Join(dir, ".opa") + + ensurePluginState(t, plugin, plugins.StateNotReady) + + // simulate a bundle download error with no bundle on disk + plugin.oneShot(ctx, bundleName, download.Update{Error: errors.New("unknown error")}) + + if plugin.status[bundleName].Message == "" { + t.Fatal("expected error but got none") + } + + ensurePluginState(t, plugin, plugins.StateNotReady) + + // download a signed bundle and persist to disk. Then verify the bundle persisted to disk + signedTokenHS256 := `eyJhbGciOiJIUzI1NiJ9.eyJmaWxlcyI6W3sibmFtZSI6Ii5tYW5pZmVzdCIsImhhc2giOiI1MDdhMmMzOGExNDQxZGI1OGQyY2I4Nzk4MmM0MmFhOTFhNDM0MmVmNDIyYTZiNTQyZWRkZWJlZWY2ZjA0MTJmIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9LHsibmFtZSI6ImV4YW1wbGUxL2RhdGEuanNvbiIsImhhc2giOiI3YTM4YmY4MWYzODNmNjk0MzNhZDZlOTAwZDM1YjNlMjM4NTU5M2Y3NmE3YjdhYjVkNDM1NWI4YmE0MWVlMjRiIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9LHsibmFtZSI6ImV4YW1wbGUyL2RhdGEuanNvbiIsImhhc2giOiI5ZTRmMTg5YmY0MDc5ZDFiNmViNjQ0Njg3OTg2NmNkNWYzOWMyNjg4MGQ0ZmI1MThmNGUwMWNkMWJiZmU1MTNlIiwiYWxnb3JpdGhtIjoiU0hBLTI1NiJ9XX0.jCLRMyys5u8S2sTS2pWWY82IAeKDpLh3S641_BskCtY` + + files := [][2]string{ + {"/.manifest", `{"revision": "quickbrownfaux"}`}, + {"/.signatures.json", fmt.Sprintf(`{"signatures": ["%v"]}`, signedTokenHS256)}, + {"/example1/data.json", `{"foo": "bar"}`}, + {"/example2/data.json", `{"x": true}`}, + } + + buf := archive.MustWriteTarGz(files) + + var dup bytes.Buffer + tee := io.TeeReader(buf, &dup) + reader := bundle.NewReader(tee).WithBundleVerificationConfig(vc).WithBundleEtag("foo") + b, err := reader.Read() + if err != nil { + t.Fatal("unexpected error:", err) + } + // We've opted out of having storage/inmem roundtrip our data, so we need to copy ourselves. + expBndl := b.Copy() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New(), Raw: &dup}) + + ensurePluginState(t, plugin, plugins.StateOK) + + // load signed bundle from disk + result, err := plugin.loadBundleFromDisk(plugin.bundlePersistPath, bundleName, bundles[bundleName]) + if err != nil { + t.Fatal("unexpected error:", err) + } + + if !result.Equal(expBndl) { + t.Fatal("expected the downloaded bundle to be equal to the one loaded from disk") + } + + // simulate a bundle download error and verify that the bundle on disk is activated + plugin.oneShot(ctx, bundleName, download.Update{Error: errors.New("unknown error")}) + + ensurePluginState(t, plugin, plugins.StateOK) + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 0 { + t.Fatal("Expected no policy") + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + if err != nil { + t.Fatal(err) + } + + expData := util.MustUnmarshalJSON([]byte(`{"example1": {"foo": "bar"}, "example2": {"x": true}, "system": {"bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}}}}`)) + if !reflect.DeepEqual(data, expData) { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } +} + +func TestLoadAndActivateBundlesFromDisk(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + + dir := t.TempDir() + + bundleName := "test-bundle" + bundleSource := Source{ + Persist: true, + } + + bundleNameOther := "test-bundle-other" + bundleSourceOther := Source{} + + bundles := map[string]*Source{} + bundles[bundleName] = &bundleSource + bundles[bundleNameOther] = &bundleSourceOther + + plugin := New(&Config{Bundles: bundles}, manager) + plugin.bundlePersistPath = filepath.Join(dir, ".opa") + + plugin.loadAndActivateBundlesFromDisk(ctx) + + // persist a bundle to disk and then load it + module := "package foo\n\ncorge=1" + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: util.MustUnmarshalJSON([]byte(`{"foo": {"bar": 1, "baz": "qux"}}`)).(map[string]any), + Modules: []bundle.ModuleFile{ + { + URL: "/foo/bar.rego", + Path: "/foo/bar.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).UseModulePath(true).Write(b); err != nil { + t.Fatal("unexpected error:", err) + } + + err := plugin.saveBundleToDisk(bundleName, &buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + plugin.loadAndActivateBundlesFromDisk(ctx) + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 { + t.Fatal("Expected 1 policy") + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + exp := []byte("package foo\n\ncorge=1") + if err != nil { + t.Fatal(err) + } else if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + expData := util.MustUnmarshalJSON([]byte(`{ + "foo": {"bar": 1, "baz": "qux"}, + "system": { + "bundles": {"test-bundle": {"etag": "", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}} + } + }`)) + if err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(data, expData) { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } +} + +// Warning: This test modifies package variables, and as +// a result, cannot be run in parallel with other tests. +func TestLoadAndActivateBundlesFromDiskReservedChars(t *testing.T) { + ctx := context.Background() + manager := getTestManager() + + dir := t.TempDir() + + goos = "windows" + + bundleName := "test?bundle=opa" // bundle name contains reserved characters + bundleSource := Source{ + Persist: true, + } + + bundles := map[string]*Source{} + bundles[bundleName] = &bundleSource + + plugin := New(&Config{Bundles: bundles}, manager) + plugin.bundlePersistPath = filepath.Join(dir, ".opa") + + plugin.loadAndActivateBundlesFromDisk(ctx) + + // persist a bundle to disk and then load it + module := "package foo\n\ncorge=1" + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: util.MustUnmarshalJSON([]byte(`{"foo": {"bar": 1, "baz": "qux"}}`)).(map[string]any), + Modules: []bundle.ModuleFile{ + { + URL: "/foo/bar.rego", + Path: "/foo/bar.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).UseModulePath(true).Write(b); err != nil { + t.Fatal("unexpected error:", err) + } + + err := plugin.saveBundleToDisk(bundleName, &buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + plugin.loadAndActivateBundlesFromDisk(ctx) + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 { + t.Fatal("Expected 1 policy") + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + exp := []byte("package foo\n\ncorge=1") + if err != nil { + t.Fatal(err) + } else if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + expData := util.MustUnmarshalJSON([]byte(`{ + "foo": {"bar": 1, "baz": "qux"}, + "system": { + "bundles": {"test?bundle=opa": {"etag": "", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}} + } + }`)) + if err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(data, expData) { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } +} + +func TestLoadAndActivateBundlesFromDiskV1Compatible(t *testing.T) { + t.Parallel() + + type update struct { + modules map[string]string + expErrs []string + } + // Note: modules are parsed before passed to plugin, so any expected errors must be triggered by the compiler stage. + tests := []struct { + note string + v1Compatible bool + updates []update + }{ + { + note: "v0.x", + updates: []update{ + { + modules: map[string]string{ + "/foo/bar.rego": `package foo +import future.keywords +corge contains 1 if { + input.x == 2 +}`, + }, + }, + }, + }, + { + note: "v0.x, shadowed import (no error)", + updates: []update{ + { + modules: map[string]string{ + "/foo/bar.rego": `package foo +import future.keywords +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + }, + }, + }, + }, + { + note: "v1.0", + v1Compatible: true, + updates: []update{ + { + modules: map[string]string{ + "/foo/bar.rego": `package foo +corge contains 1 if { + input.x == 2 +}`, + }, + }, + }, + }, + { + note: "v1.0, shadowed import", + v1Compatible: true, + updates: []update{ + { + modules: map[string]string{ + "/foo/bar.rego": `package foo +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + }, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + }, + }, + { + note: "v1.0, module updated", + v1Compatible: true, + updates: []update{ + { + modules: map[string]string{ + "/foo/bar.rego": `package foo +corge contains 1 if { + input.x == 2 +}`, + }, + }, + { + modules: map[string]string{ + "/foo/bar.rego": `package foo +corge contains 2 if { + input.x == 3 +}`, + }, + }, + }, + }, + { + note: "v1.0, module updated, shadowed import", + v1Compatible: true, + updates: []update{ + { + modules: map[string]string{ + "/foo/bar.rego": `package foo +corge contains 1 if { + input.x == 2 +}`, + }, + }, + { + modules: map[string]string{ + "/foo/bar.rego": `package foo +import data.foo +import data.bar as foo +corge contains 2 if { + input.x == 3 +}`, + }, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + regoVersion := ast.RegoV0 + if tc.v1Compatible { + regoVersion = ast.RegoV1 + } + popts := ast.ParserOptions{RegoVersion: regoVersion} + + ctx := context.Background() + manager, err := plugins.New(nil, "test-instance-id", inmemtst.New(), + plugins.WithParserOptions(popts)) + if err != nil { + t.Fatal("unexpected error:", err) + } + + dir := t.TempDir() + + bundleName := "test-bundle" + bundleSource := Source{ + Persist: true, + } + + bundleNameOther := "test-bundle-other" + bundleSourceOther := Source{} + + bundles := map[string]*Source{} + bundles[bundleName] = &bundleSource + bundles[bundleNameOther] = &bundleSourceOther + + plugin := New(&Config{Bundles: bundles}, manager) + plugin.bundlePersistPath = filepath.Join(dir, ".opa") + + plugin.loadAndActivateBundlesFromDisk(ctx) + + for _, update := range tc.updates { + // persist a bundle to disk and then load it + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: util.MustUnmarshalJSON([]byte(`{"foo": {"bar": 1, "baz": "qux"}}`)).(map[string]any), + } + for url, module := range update.modules { + b.Modules = append(b.Modules, bundle.ModuleFile{ + URL: url, + Path: url, + Parsed: ast.MustParseModuleWithOpts(module, popts), + Raw: []byte(module), + }) + } + + b.Manifest.Init() + + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).UseModulePath(true).Write(b); err != nil { + t.Fatal("unexpected error:", err) + } + + err = plugin.saveBundleToDisk(bundleName, &buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + plugin.loadAndActivateBundlesFromDisk(ctx) + + if update.expErrs != nil { + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", status.Type) + } else if errs := status.Errors; len(errs) != len(update.expErrs) { + t.Fatalf("expected errors:\n\n%v\n\nbut got:\n\n%v", update.expErrs, errs) + } else { + for _, expErr := range update.expErrs { + found := false + for _, err := range errs { + if strings.Contains(err.Error(), expErr) { + found = true + break + } + } + if !found { + t.Fatalf("expected error:\n\n%v\n\nbut got:\n\n%v", expErr, errs) + } + } + } + } else { + txn := storage.NewTransactionOrDie(ctx, manager.Store) + fatal := func(args ...any) { + t.Helper() + manager.Store.Abort(ctx, txn) + t.Fatal(args...) + } + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + fatal(err) + } + for _, id := range ids { + bs, err := manager.Store.GetPolicy(ctx, txn, id) + p, _ := strings.CutPrefix(id, bundleName) + module := update.modules[p] + exp := []byte(module) + if err != nil { + fatal(err) + } else if !bytes.Equal(bs, exp) { + fatal("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + } + + expData := util.MustUnmarshalJSON([]byte(`{ + "foo": {"bar": 1, "baz": "qux"}, + "system": { + "bundles": {"test-bundle": {"etag": "", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}} + } + }`)) + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + if err != nil { + fatal(err) + } else if !reflect.DeepEqual(data, expData) { + fatal("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } + + manager.Store.Abort(ctx, txn) + } + } + }) + } +} + +func TestLoadAndActivateBundlesFromDiskWithBundleRegoVersion(t *testing.T) { + t.Parallel() + + // Note: modules are parsed before passed to plugin, so any expected errors must be triggered by the compiler stage. + tests := []struct { + note string + managerRegoVersion ast.RegoVersion + bundleRegoVersion *ast.RegoVersion + module string + expErrs []string + }{ + { + note: "v0.x manager, no bundle rego version", + managerRegoVersion: ast.RegoV0, + module: `package foo +corge[1] { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v0.x bundle", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: pointTo(ast.RegoV0), + module: `package foo +corge[1] { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v1.0 bundle", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: pointTo(ast.RegoV1), + module: `package foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v1.0 bundle, compiler err (shadowed import)", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: pointTo(ast.RegoV1), + module: `package foo +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v1.0 manager, no bundle rego version", + managerRegoVersion: ast.RegoV1, + module: `package foo +corge contains 1 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, no bundle rego version, compiler err (shadowed import)", + managerRegoVersion: ast.RegoV1, + module: `package foo +import data.foo +import data.bar as foo +corge contains 1 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v1.0 manager, v0.x bundle", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: pointTo(ast.RegoV0), + module: `package foo +corge[1] { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v1.0 bundle", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: pointTo(ast.RegoV1), + module: `package foo +corge contains 1 if { + input.x == 2 +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + managerPopts := ast.ParserOptions{RegoVersion: tc.managerRegoVersion} + manager, err := plugins.New(nil, "test-instance-id", inmemtst.New(), + plugins.WithParserOptions(managerPopts)) + if err != nil { + t.Fatal("unexpected error:", err) + } + + dir := t.TempDir() + + bundleName := "test-bundle" + bundleSource := Source{ + Persist: true, + } + + bundleNameOther := "test-bundle-other" + bundleSourceOther := Source{} + + bundles := map[string]*Source{} + bundles[bundleName] = &bundleSource + bundles[bundleNameOther] = &bundleSourceOther + + plugin := New(&Config{Bundles: bundles}, manager) + plugin.bundlePersistPath = filepath.Join(dir, ".opa") + + plugin.loadAndActivateBundlesFromDisk(ctx) + + // persist a bundle to disk and then load it + m := bundle.Manifest{Revision: "quickbrownfaux"} + var bundlePopts ast.ParserOptions + if tc.bundleRegoVersion != nil { + m.SetRegoVersion(*tc.bundleRegoVersion) + bundlePopts = ast.ParserOptions{RegoVersion: *tc.bundleRegoVersion} + } else { + bundlePopts = managerPopts + } + b := bundle.Bundle{ + Manifest: m, + Data: util.MustUnmarshalJSON([]byte(`{"foo": {"bar": 1, "baz": "qux"}}`)).(map[string]any), + Modules: []bundle.ModuleFile{ + { + URL: "/foo/bar.rego", + Path: "/foo/bar.rego", + Parsed: ast.MustParseModuleWithOpts(tc.module, bundlePopts), + Raw: []byte(tc.module), + }, + }, + } + + b.Manifest.Init() + + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).UseModulePath(true).Write(b); err != nil { + t.Fatal("unexpected error:", err) + } + + err = plugin.saveBundleToDisk(bundleName, &buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + plugin.loadAndActivateBundlesFromDisk(ctx) + + if tc.expErrs != nil { + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", status.Type) + } else if errs := status.Errors; len(errs) != len(tc.expErrs) { + t.Fatalf("expected errors:\n\n%v\n\nbut got:\n\n%v", tc.expErrs, errs) + } else { + for _, expErr := range tc.expErrs { + found := false + for _, err := range errs { + if strings.Contains(err.Error(), expErr) { + found = true + break + } + } + if !found { + t.Fatalf("expected error:\n\n%v\n\nbut got:\n\n%v", expErr, errs) + } + } + } + } else { + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 { + t.Fatal("Expected 1 policy") + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + exp := []byte(tc.module) + if err != nil { + t.Fatal(err) + } else if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + + manifestRegoVersionStr := "" + if tc.bundleRegoVersion != nil { + manifestRegoVersionStr = fmt.Sprintf(`, "rego_version": %d`, bundleRegoVersion(*tc.bundleRegoVersion)) + } + + runtimeRegoVersion := manager.ParserOptions().RegoVersion.Int() + var moduleRegoVersion int + if tc.bundleRegoVersion != nil { + moduleRegoVersion = tc.bundleRegoVersion.Int() + } else { + moduleRegoVersion = runtimeRegoVersion + } + + var expData any + if moduleRegoVersion != runtimeRegoVersion { + expData = util.MustUnmarshalJSON(fmt.Appendf(nil, `{ + "foo": {"bar": 1, "baz": "qux"}, + "system": { + "bundles": {"test-bundle": {"etag": "", "manifest": {"revision": "quickbrownfaux"%s, "roots": [""]}}}, + "modules": {"test-bundle/foo/bar.rego": {"rego_version": %d}} + } + }`, + manifestRegoVersionStr, moduleRegoVersion)) + } else { + expData = util.MustUnmarshalJSON(fmt.Appendf(nil, `{ + "foo": {"bar": 1, "baz": "qux"}, + "system": { + "bundles": {"test-bundle": {"etag": "", "manifest": {"revision": "quickbrownfaux"%s, "roots": [""]}}} + } + }`, + manifestRegoVersionStr)) + } + + if err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(data, expData) { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } + } + }) + } +} + +func pointTo[T any](v T) *T { + return &v +} + +func bundleRegoVersion(v ast.RegoVersion) int { + switch v { + case ast.RegoV0: + return 0 + case ast.RegoV0CompatV1: + return 0 + case ast.RegoV1: + return 1 + } + panic("unknown ast.RegoVersion") +} + +func TestLoadAndActivateDepBundlesFromDisk(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + + dir := t.TempDir() + + bundleName := "test-bundle-main" + bundleSource := Source{ + Persist: true, + } + + bundleNameOther := "test-bundle-lib" + bundleSourceOther := Source{ + Persist: true, + } + + bundles := map[string]*Source{} + bundles[bundleName] = &bundleSource + bundles[bundleNameOther] = &bundleSourceOther + + plugin := New(&Config{Bundles: bundles}, manager) + plugin.bundlePersistPath = filepath.Join(dir, ".opa") + + module1 := ` +package bar + +import rego.v1 +import data.foo + +default allow = false + +allow if { + foo.is_one(1) +}` + + module2 := ` +package foo +import rego.v1 + +is_one(x) if { + x == 1 +}` + + b1 := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfauxbar", Roots: &[]string{"bar"}}, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + URL: "/bar/policy.rego", + Path: "/bar/policy.rego", + Parsed: ast.MustParseModule(module1), + Raw: []byte(module1), + }, + }, + } + + b1.Manifest.Init() + + b2 := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfauxfoo", Roots: &[]string{"foo"}}, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + URL: "/foo/policy.rego", + Path: "/foo/policy.rego", + Parsed: ast.MustParseModule(module2), + Raw: []byte(module2), + }, + }, + } + + b2.Manifest.Init() + + var buf1 bytes.Buffer + if err := bundle.NewWriter(&buf1).UseModulePath(true).Write(b1); err != nil { + t.Fatal("unexpected error:", err) + } + + err := plugin.saveBundleToDisk(bundleName, &buf1) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + var buf2 bytes.Buffer + if err := bundle.NewWriter(&buf2).UseModulePath(true).Write(b2); err != nil { + t.Fatal("unexpected error:", err) + } + + err = plugin.saveBundleToDisk(bundleNameOther, &buf2) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + plugin.loadAndActivateBundlesFromDisk(ctx) + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 2 { + t.Fatal("Expected 2 policies") + } +} + +func TestLoadAndActivateDepBundlesFromDiskMaxAttempts(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + + dir := t.TempDir() + + bundleName := "test-bundle-main" + bundleSource := Source{ + Persist: true, + } + + bundles := map[string]*Source{} + bundles[bundleName] = &bundleSource + + plugin := New(&Config{Bundles: bundles}, manager) + plugin.bundlePersistPath = filepath.Join(dir, ".opa") + + module := ` +package bar + +import rego.v1 +import data.foo + +default allow = false + +allow if { + foo.is_one(1) +}` + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux", Roots: &[]string{"bar"}}, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + URL: "/bar/policy.rego", + Path: "/bar/policy.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).UseModulePath(true).Write(b); err != nil { + t.Fatal("unexpected error:", err) + } + + err := plugin.saveBundleToDisk(bundleName, &buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + plugin.loadAndActivateBundlesFromDisk(ctx) + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 0 { + t.Fatal("Expected 0 policies") + } +} + +func TestPluginOneShotCompileError(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := New(&Config{}, manager) + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + raw1 := `package foo +import rego.v1 + +p contains x if { x = 1 }` + + b1 := &bundle.Bundle{ + Data: map[string]any{"a": "b"}, + Modules: []bundle.ModuleFile{ + { + Path: "/example.rego", + Raw: []byte(raw1), + Parsed: ast.MustParseModule(raw1), + }, + }, + } + + b1.Manifest.Init() + plugin.oneShot(ctx, bundleName, download.Update{Bundle: b1, Metrics: metrics.New()}) + + ensurePluginState(t, plugin, plugins.StateOK) + + b2 := &bundle.Bundle{ + Data: map[string]any{"a": "b"}, + Modules: []bundle.ModuleFile{ + { + Path: "/example2.rego", + Parsed: ast.MustParseModule(`package foo +import rego.v1 + +p contains x`), + }, + }, + } + + b2.Manifest.Init() + plugin.oneShot(ctx, bundleName, download.Update{Bundle: b2}) + + ensurePluginState(t, plugin, plugins.StateOK) + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + + _, err := manager.Store.GetPolicy(ctx, txn, filepath.Join(bundleName, "example.rego")) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{"a"}) + if err != nil || !reflect.DeepEqual("b", data) { + t.Fatalf("Expected data to be intact but got: %v, err: %v", data, err) + } + + manager.Store.Abort(ctx, txn) + + b3 := &bundle.Bundle{ + Data: map[string]any{"foo": map[string]any{"p": "a"}}, + Modules: []bundle.ModuleFile{ + { + Path: "/example3.rego", + Parsed: ast.MustParseModule("package foo\np=1"), + }, + }, + } + + b3.Manifest.Init() + plugin.oneShot(ctx, bundleName, download.Update{Bundle: b3}) + + ensurePluginState(t, plugin, plugins.StateOK) + + txn = storage.NewTransactionOrDie(ctx, manager.Store) + + _, err = manager.Store.GetPolicy(ctx, txn, filepath.Join(bundleName, "example.rego")) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + data, err = manager.Store.Read(ctx, txn, storage.Path{"a"}) + if err != nil || !reflect.DeepEqual("b", data) { + t.Fatalf("Expected data to be intact but got: %v, err: %v", data, err) + } +} + +func TestPluginOneShotHTTPError(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := New(&Config{}, manager) + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + ch := make(chan Status) + listenerName := "test" + plugin.Register(listenerName, func(status Status) { + ch <- status + }) + go plugin.oneShot(ctx, bundleName, download.Update{Error: download.HTTPError{StatusCode: 403}}) + s := <-ch + if s.HTTPCode != "403" { + t.Fatal("expected http_code to be 403 instead of ", s.HTTPCode) + } + + module := "package foo\n\ncorge=1" + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: util.MustUnmarshalJSON([]byte(`{"foo": {"bar": 1, "baz": "qux"}}`)).(map[string]any), + Modules: []bundle.ModuleFile{ + { + Path: "/foo/bar", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + go plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + s = <-ch + if s.HTTPCode != "" { + t.Fatal("expected http_code to be empty instead of ", s.HTTPCode) + } +} + +func TestPluginOneShotActivationRemovesOld(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := New(&Config{}, manager) + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + module1 := `package example + + p = 1` + + b1 := bundle.Bundle{ + Data: map[string]any{ + "foo": "bar", + }, + Modules: []bundle.ModuleFile{ + { + Path: "/example.rego", + Raw: []byte(module1), + Parsed: ast.MustParseModule(module1), + }, + }, + } + + b1.Manifest.Init() + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b1}) + + ensurePluginState(t, plugin, plugins.StateOK) + + module2 := `package example + + p = 2` + + b2 := bundle.Bundle{ + Data: map[string]any{ + "baz": "qux", + }, + Modules: []bundle.ModuleFile{ + { + Path: "/example2.rego", + Raw: []byte(module2), + Parsed: ast.MustParseModule(module2), + }, + }, + } + + b2.Manifest.Init() + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b2}) + + ensurePluginState(t, plugin, plugins.StateOK) + + err := storage.Txn(ctx, manager.Store, storage.TransactionParams{}, func(txn storage.Transaction) error { + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + return err + } else if !slices.Equal([]string{filepath.Join(bundleName, "example2.rego")}, ids) { + return errors.New("expected updated policy ids") + } + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + // remove system key to make comparison simpler + delete(data.(map[string]any), "system") + if err != nil { + return err + } else if !reflect.DeepEqual(data, map[string]any{"baz": "qux"}) { + return errors.New("expected updated data") + } + return nil + }) + if err != nil { + t.Fatal("Unexpected:", err) + } +} + +func TestPluginOneShotActivationConflictingRoots(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := New(&Config{}, manager) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + bundleNames := []string{"test-bundle1", "test-bundle2", "test-bundle3"} + + for _, name := range bundleNames { + plugin.status[name] = &Status{Name: name} + plugin.downloaders[name] = download.New(download.Config{}, plugin.manager.Client(""), name) + } + + // Start with non-conflicting updates + plugin.oneShot(ctx, bundleNames[0], download.Update{Bundle: &bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"a/b"}, + }, + }}) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + plugin.oneShot(ctx, bundleNames[1], download.Update{Bundle: &bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"a/c"}, + }, + }}) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + // ensure that both bundles are *not* in error status + ensureBundleOverlapStatus(t, plugin, bundleNames, []bool{false, false, false}) + + // Add a third bundle that conflicts with one + plugin.oneShot(ctx, bundleNames[2], download.Update{Bundle: &bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"a/b/aa"}, + }, + }}) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + // ensure that both in the conflict go into error state + ensureBundleOverlapStatus(t, plugin, bundleNames, []bool{false, false, true}) + + // Update to fix conflict + plugin.oneShot(ctx, bundleNames[2], download.Update{Bundle: &bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"b"}, + }, + }}) + + ensurePluginState(t, plugin, plugins.StateOK) + ensureBundleOverlapStatus(t, plugin, bundleNames, []bool{false, false, false}) + + // Ensure empty roots conflict with all roots + plugin.oneShot(ctx, bundleNames[2], download.Update{Bundle: &bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{""}, + }, + }}) + + ensurePluginState(t, plugin, plugins.StateOK) + ensureBundleOverlapStatus(t, plugin, bundleNames, []bool{false, false, true}) +} + +func TestPluginOneShotActivationPrefixMatchingRoots(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + } + bundleNames := []string{"test-bundle1", "test-bundle2"} + + for _, name := range bundleNames { + plugin.status[name] = &Status{Name: name} + plugin.downloaders[name] = download.New(download.Config{}, plugin.manager.Client(""), name) + } + + plugin.oneShot(ctx, bundleNames[0], download.Update{Bundle: &bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"a/b/c"}, + }, + }}) + + plugin.oneShot(ctx, bundleNames[1], download.Update{Bundle: &bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{"a/b/cat"}, + }, + }}) + + ensureBundleOverlapStatus(t, &plugin, bundleNames, []bool{false, false}) + + // Ensure that empty roots conflict + plugin.oneShot(ctx, bundleNames[1], download.Update{Bundle: &bundle.Bundle{ + Manifest: bundle.Manifest{ + Roots: &[]string{""}, + }, + }}) + + ensureBundleOverlapStatus(t, &plugin, bundleNames, []bool{false, true}) +} + +func ensureBundleOverlapStatus(t *testing.T, p *Plugin, bundleNames []string, expectedErrs []bool) { + t.Helper() + for i, name := range bundleNames { + hasErr := p.status[name].Message != "" + if expectedErrs[i] && !hasErr { + t.Fatalf("expected bundle %s to be in an error state", name) + } else if !expectedErrs[i] && hasErr { + t.Fatalf("unexpected error state for bundle %s", name) + } else if hasErr && expectedErrs[i] && !strings.Contains(p.status[name].Message, "overlapping roots") { + t.Fatalf("expected bundle overlap error for bundle %s, got: %s", name, p.status[name].Message) + } + } +} + +func TestPluginListener(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := New(&Config{}, manager) + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + ch := make(chan Status) + + listenerName := "test" + plugin.Register(listenerName, func(status Status) { + ch <- status + }) + + if len(plugin.listeners) != 1 || plugin.listeners[listenerName] == nil { + t.Fatal("Listener not properly registered") + } + + module := `package gork +import rego.v1 + +p contains x if { x = 1 }` + + b := bundle.Bundle{ + Manifest: bundle.Manifest{ + Revision: "quickbrownfaux", + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "/foo.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + // Test that initial bundle is ok. Defer to separate goroutine so we can + // check result with channel. + go plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + s1 := <-ch + + validateStatus(t, s1, "quickbrownfaux", false) + + module = `package gork +import rego.v1 + +p contains x` + + b.Manifest.Revision = "slowgreenburd" + b.Modules[0] = bundle.ModuleFile{ + Path: "/foo.rego", + Raw: []byte(module), + Parsed: ast.MustParseModule(module), + } + + // Test that next update is failed. + go plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + s2 := <-ch + + validateStatus(t, s2, "quickbrownfaux", true) + + module = `package gork +import rego.v1 + +p contains 1` + b.Manifest.Revision = "fancybluederg" + b.Modules[0] = bundle.ModuleFile{ + Path: "/foo.rego", + Raw: []byte(module), + Parsed: ast.MustParseModule(module), + } + + // Test that the new update is successful. + go plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + s3 := <-ch + + validateStatus(t, s3, "fancybluederg", false) + + // Test that empty download update results in status update. + go plugin.oneShot(ctx, bundleName, download.Update{}) + s4 := <-ch + + // Nothing should have changed in the update + validateStatus(t, s4, s3.ActiveRevision, false) + + plugin.Unregister(listenerName) + if len(plugin.listeners) != 0 { + t.Fatal("Listener not properly unregistered") + } +} + +func isErrStatus(s Status) bool { + return s.Code != "" || len(s.Errors) != 0 || s.Message != "" +} + +func validateStatus(t *testing.T, actual Status, expected string, expectStatusErr bool) { + t.Helper() + + if expectStatusErr && !isErrStatus(actual) { + t.Errorf("Expected status to be in an error state, but no error has occurred.") + } else if !expectStatusErr && isErrStatus(actual) { + t.Errorf("Unexpected error status %v", actual) + } + + if actual.ActiveRevision != expected { + t.Errorf("Expected status revision %s, got %s", expected, actual.ActiveRevision) + } +} + +func TestPluginListenerErrorClearedOn304(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + } + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + ch := make(chan Status) + + plugin.Register("test", func(status Status) { + ch <- status + }) + + b := bundle.Bundle{ + Manifest: bundle.Manifest{ + Revision: "quickbrownfaux", + }, + Data: map[string]any{"foo": "bar"}, + } + + b.Manifest.Init() + + // Test that initial bundle is ok. + go plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + s1 := <-ch + + if s1.ActiveRevision != "quickbrownfaux" || s1.Code != "" { + t.Fatal("Unexpected status update, got:", s1) + } + + // Test that service error triggers failure notification. + go plugin.oneShot(ctx, bundleName, download.Update{Error: errors.New("some error")}) + s2 := <-ch + + if s2.ActiveRevision != "quickbrownfaux" || s2.Code == "" { + t.Fatal("Unexpected status update, got:", s2) + } + + // Test that service recovery triggers healthy notification. + go plugin.oneShot(ctx, bundleName, download.Update{}) + s3 := <-ch + + if s3.ActiveRevision != "quickbrownfaux" || s3.Code != "" { + t.Fatal("Unexpected status update, got:", s3) + } +} + +func TestPluginBulkListener(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + } + bundleNames := []string{ + "b1", + "b2", + "b3", + } + for _, name := range bundleNames { + plugin.status[name] = &Status{Name: name} + plugin.downloaders[name] = download.New(download.Config{}, plugin.manager.Client(""), name) + } + bulkChan := make(chan map[string]*Status) + + listenerName := "bulk test" + plugin.RegisterBulkListener(listenerName, func(status map[string]*Status) { + bulkChan <- status + }) + + if len(plugin.bulkListeners) != 1 || plugin.bulkListeners[listenerName] == nil { + t.Fatal("Bulk listener not properly registered") + } + + module := `package gork +import rego.v1 + +p contains x if { x = 1 }` + + b := bundle.Bundle{ + Manifest: bundle.Manifest{ + Revision: "quickbrownfaux", + Roots: &[]string{"gork"}, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "/foo.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + // Test that initial bundle is ok. Defer to separate goroutine so we can + // check result with channel. + go plugin.oneShot(ctx, bundleNames[0], download.Update{Bundle: &b}) + s1 := <-bulkChan + + s := s1[bundleNames[0]] + if s.ActiveRevision != "quickbrownfaux" || s.Code != "" { + t.Fatal("Unexpected status update, got:", s1) + } + + for i := 1; i < len(bundleNames); i++ { + name := bundleNames[i] + s, ok := s1[name] + if !ok { + t.Errorf("Expected to have bundle status for %q included in update, got: %+v", name, s1) + } + // they should be defaults at this point + if !s.Equal(&Status{Name: name}) { + t.Errorf("Expected bundle %q to have an empty status, got: %+v", name, s1) + } + } + + module = `package gork +import rego.v1 + +p contains x` + + b.Manifest.Revision = "slowgreenburd" + b.Modules[0] = bundle.ModuleFile{ + Path: "/foo.rego", + Raw: []byte(module), + Parsed: ast.MustParseModule(module), + } + + // Test that next update is failed. + go plugin.oneShot(ctx, bundleNames[0], download.Update{Bundle: &b}) + s2 := <-bulkChan + + s = s2[bundleNames[0]] + if s.ActiveRevision != "quickbrownfaux" || s.Code == "" || s.Message == "" || len(s.Errors) == 0 { + t.Fatal("Unexpected status update, got:", s2) + } + + for i := 1; i < len(bundleNames); i++ { + name := bundleNames[i] + s, ok := s2[name] + if !ok { + t.Errorf("Expected to have bundle status for %q included in update, got: %+v", name, s2) + } + // they should be still defaults + if !s.Equal(&Status{Name: name}) { + t.Errorf("Expected bundle %q to have an empty status, got: %+v", name, s2) + } + } + + module = `package gork +import rego.v1 + +p contains 1` + b.Manifest.Revision = "fancybluederg" + b.Modules[0] = bundle.ModuleFile{ + Path: "/foo.rego", + Raw: []byte(module), + Parsed: ast.MustParseModule(module), + } + + // Test that new update is successful. + go plugin.oneShot(ctx, bundleNames[0], download.Update{Bundle: &b}) + s3 := <-bulkChan + + s = s3[bundleNames[0]] + if s.ActiveRevision != "fancybluederg" || s.Code != "" || s.Message != "" || len(s.Errors) != 0 { + t.Fatal("Unexpected status update, got:", s3) + } + + for i := 1; i < len(bundleNames); i++ { + name := bundleNames[i] + s, ok := s3[name] + if !ok { + t.Errorf("Expected to have bundle status for %q included in update, got: %+v", name, s3) + } + // they should still be defaults + if !s.Equal(&Status{Name: name}) { + t.Errorf("Expected bundle %q to have an empty status, got: %+v", name, s3) + } + } + + // Test that empty download update results in status update. + go plugin.oneShot(ctx, bundleNames[0], download.Update{}) + s4 := <-bulkChan + + s = s4[bundleNames[0]] + if s.ActiveRevision != "fancybluederg" || s.Code != "" || s.Message != "" || len(s.Errors) != 0 { + t.Errorf("Unexpected same status update for bundle %q, got: %v", bundleNames[0], s) + } + + // Test updates the other bundles + module = `package p1 +import rego.v1 + +p contains x if { x = 1 }` + + b1 := bundle.Bundle{ + Manifest: bundle.Manifest{ + Revision: "123", + Roots: &[]string{"p1"}, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "/foo1.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b1.Manifest.Init() + + // Test that new update is successful. + go plugin.oneShot(ctx, bundleNames[1], download.Update{Bundle: &b1}) + s5 := <-bulkChan + + s = s5[bundleNames[1]] + if s.ActiveRevision != "123" || s.Code != "" || s.Message != "" || len(s.Errors) != 0 { + t.Fatal("Unexpected status update, got:", s5) + } + + if !s5[bundleNames[0]].Equal(s4[bundleNames[0]]) { + t.Fatalf("Expected bundle %q to have the same status as before updating bundle %q, got: %+v", bundleNames[0], bundleNames[1], s5) + } + + for i := 2; i < len(bundleNames); i++ { + name := bundleNames[i] + s, ok := s5[name] + if !ok { + t.Errorf("Expected to have bundle status for %q included in update, got: %+v", name, s5) + } + // they should still be defaults + if !s.Equal(&Status{Name: name}) { + t.Errorf("Expected bundle %q to have an empty status, got: %+v", name, s5) + } + } + + plugin.UnregisterBulkListener(listenerName) + if len(plugin.bulkListeners) != 0 { + t.Fatal("Bulk listener not properly unregistered") + } +} + +func TestPluginBulkListenerStatusCopyOnly(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + } + bundleNames := []string{ + "b1", + "b2", + "b3", + } + for _, name := range bundleNames { + plugin.status[name] = &Status{Name: name} + plugin.downloaders[name] = download.New(download.Config{}, plugin.manager.Client(""), name) + } + bulkChan := make(chan map[string]*Status) + + plugin.RegisterBulkListener("bulk test", func(status map[string]*Status) { + bulkChan <- status + }) + + module := `package gork +import rego.v1 + +p contains x if { x = 1 }` + + b := bundle.Bundle{ + Manifest: bundle.Manifest{ + Revision: "quickbrownfaux", + Roots: &[]string{"gork"}, + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "/foo.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + // Test that initial bundle is ok. Defer to separate goroutine so we can + // check result with channel. + go plugin.oneShot(ctx, bundleNames[0], download.Update{Bundle: &b}) + s1 := <-bulkChan + + // Modify the status map received and ensure it doesn't affect the one on the plugin + delete(s1, "b1") + + if _, ok := plugin.status["b1"]; !ok { + t.Fatalf("Expected status for 'b1' to still be in 'plugin.status'") + } +} + +func TestPluginActivateScopedBundle(t *testing.T) { + t.Parallel() + + readMode := []struct { + note string + readAst bool + }{ + { + note: "read raw", + readAst: false, + }, + { + note: "read ast", + readAst: true, + }, + } + + for _, rm := range readMode { + t.Run(rm.note, func(t *testing.T) { + ctx := context.Background() + manager := getTestManagerWithOpts(nil, inmem.NewWithOpts(inmem.OptReturnASTValuesOnRead(rm.readAst))) + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + } + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + // Transact test data and policies that represent data coming from + // _outside_ the bundle. The test will verify that data _outside_ + // the bundle is both not erased and is overwritten appropriately. + // + // The test data claims a/{a1-6} where even paths are policy and + // odd paths are raw JSON. + if err := storage.Txn(ctx, manager.Store, storage.WriteParams, func(txn storage.Transaction) error { + + externalData := map[string]any{"a": map[string]any{"a1": "x1", "a3": "x2", "a5": "x3"}} + + if err := manager.Store.Write(ctx, txn, storage.AddOp, storage.Path{}, externalData); err != nil { + return err + } + if err := manager.Store.UpsertPolicy(ctx, txn, "some/id1", []byte(`package a.a2`)); err != nil { + return err + } + if err := manager.Store.UpsertPolicy(ctx, txn, "some/id2", []byte(`package a.a4`)); err != nil { + return err + } + return manager.Store.UpsertPolicy(ctx, txn, "some/id3", []byte(`package a.a6`)) + }); err != nil { + t.Fatal(err) + } + + // Activate a bundle that is scoped to a/a1 and a/a2. This will + // erase and overwrite the external data at these paths but leave + // a3-6 untouched. + module := "package a.a2\n\nbar=1" + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux", Roots: &[]string{"a/a1", "a/a2"}}, + Data: map[string]any{ + "a": map[string]any{ + "a1": "foo", + }, + }, + Modules: []bundle.ModuleFile{ + { + Path: "bundle/id1", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + + // Ensure a/a3-6 are intact. a1-2 are overwritten by bundle, and + // that the manifest has been written to storage. + exp := `{"a1": "foo", "a3": "x2", "a5": "x3"}` + var expData any + if rm.readAst { + expData = ast.MustParseTerm(exp).Value + } else { + expData = util.MustUnmarshalJSON([]byte(exp)) + } + expIDs := []string{filepath.Join(bundleName, "bundle", "id1"), "some/id2", "some/id3"} + validateStoreState(ctx, t, manager.Store, "/a", expData, expIDs, bundleName, "quickbrownfaux", nil) + + // Activate a bundle that is scoped to a/a3 ad a/a6. Include a function + // inside package a.a4 that we can depend on outside of the bundle scope to + // exercise the compile check with remaining modules. + module = "package a.a4\n\nbar=1\n\nfunc(x) = x" + + b = bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux-2", Roots: &[]string{"a/a3", "a/a4"}, + Metadata: map[string]any{ + "a": map[string]any{ + "a1": "deadbeef", + }, + }, + }, + Data: map[string]any{ + "a": map[string]any{ + "a3": "foo", + }, + }, + Modules: []bundle.ModuleFile{ + { + Path: "bundle/id2", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + + // Ensure a/a5-a6 are intact. a3 and a4 are overwritten by bundle. + exp = `{"a3": "foo", "a5": "x3"}` + if rm.readAst { + expData = ast.MustParseTerm(exp).Value + } else { + expData = util.MustUnmarshalJSON([]byte(exp)) + } + expIDs = []string{filepath.Join(bundleName, "bundle", "id2"), "some/id3"} + validateStoreState(ctx, t, manager.Store, "/a", expData, expIDs, bundleName, "quickbrownfaux-2", + map[string]any{ + "a": map[string]any{"a1": "deadbeef"}, + }) + + // Upsert policy outside of bundle scope that depends on bundle. + if err := storage.Txn(ctx, manager.Store, storage.WriteParams, func(txn storage.Transaction) error { + return manager.Store.UpsertPolicy(ctx, txn, "not_scoped", []byte("package not_scoped\np { data.a.a4.func(1) = 1 }")) + }); err != nil { + t.Fatal(err) + } + + b = bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux-3", Roots: &[]string{"a/a3", "a/a4"}}, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{}, + } + + b.Manifest.Init() + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + + // Ensure bundle activation failed by checking that previous revision is + // still active. + expIDs = []string{filepath.Join(bundleName, "bundle", "id2"), "not_scoped", "some/id3"} + validateStoreState(ctx, t, manager.Store, "/a", expData, expIDs, bundleName, "quickbrownfaux-2", + map[string]any{ + "a": map[string]any{"a1": "deadbeef"}, + }) + }) + } +} + +func TestPluginSetCompilerOnContext(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + } + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + module := ` + package test + + p = 1 + ` + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux"}, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "/test.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + events := []storage.TriggerEvent{} + + if err := storage.Txn(ctx, manager.Store, storage.WriteParams, func(txn storage.Transaction) error { + _, err := manager.Store.Register(ctx, txn, storage.TriggerConfig{ + OnCommit: func(_ context.Context, _ storage.Transaction, event storage.TriggerEvent) { + events = append(events, event) + }, + }) + return err + }); err != nil { + t.Fatal(err) + } + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + + exp := ast.MustParseModule(module) + + // Expect two events. One for trigger registration, one for policy update. + if len(events) != 2 { + t.Fatalf("Expected 2 events but got: %+v", events) + } else if compiler := plugins.GetCompilerOnContext(events[1].Context); compiler == nil { + t.Fatalf("Expected compiler on 2nd event but got: %+v", events) + } else if !compiler.Modules[filepath.Join(bundleName, "test.rego")].Equal(exp) { + t.Fatalf("Expected module on compiler but got: %v", compiler.Modules) + } +} + +func getTestManager() *plugins.Manager { + return getTestManagerWithOpts(nil) +} + +func getTestManagerWithOpts(config []byte, stores ...storage.Store) *plugins.Manager { + store := inmemtst.New() + if len(stores) == 1 { + store = stores[0] + } + + manager, err := plugins.New(config, "test-instance-id", store) + if err != nil { + panic(err) + } + return manager +} + +func TestPluginReconfigure(t *testing.T) { + t.Parallel() + + tsURLBase := "/opa-test/" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !strings.HasPrefix(r.URL.Path, tsURLBase) { + t.Fatalf("Invalid request URL path: %s, expected prefix %s", r.URL.Path, tsURLBase) + } + fmt.Fprintln(w, "") // Note: this is an invalid bundle and will fail the download + })) + defer ts.Close() + + ctx := context.Background() + manager := getTestManager() + + serviceName := "test-svc" + err := manager.Reconfigure(&config.Config{ + Services: fmt.Appendf(nil, "{\"%s\":{ \"url\": \"%s\"}}", serviceName, ts.URL+tsURLBase), + }) + if err != nil { + t.Fatalf("Error configuring plugin manager: %s", err) + } + + plugin := New(&Config{}, manager) + + var delay int64 = 10 + + triggerPolling := plugins.TriggerPeriodic + baseConf := download.Config{Polling: download.PollingConfig{MinDelaySeconds: &delay, MaxDelaySeconds: &delay}, Trigger: &triggerPolling} + + // Expect the plugin to emit a "not ready" status update each time we change the configuration + updateCount := 0 + manager.RegisterPluginStatusListener(t.Name(), func(status map[string]*plugins.Status) { + updateCount++ + bStatus, ok := status[Name] + if !ok { + t.Errorf("Expected to find status for %s in plugin status update, got: %+v", Name, status) + } + + if bStatus.State != plugins.StateNotReady { + t.Errorf("Expected plugin status update to have state = %s, got %s", plugins.StateNotReady, bStatus.State) + } + }) + + // Note: test stages are accumulating state with reconfigures between them, the order does matter! + // Each stage defines the new config, side effects are validated. + stages := []struct { + name string + cfg *Config + }{ + { + name: "start with single legacy bundle", + cfg: &Config{ + Name: "bundle.tar.gz", + Service: serviceName, + Config: baseConf, + // Note: the config validation and default injection will add an entry + // to the Bundles map for the older style configuration. + Bundles: map[string]*Source{ + "bundle.tar.gz": {Config: baseConf, Service: serviceName, Resource: "/bundles/bundle.tar.gz"}, + }, + }, + }, + { + name: "switch to multi-bundle", + cfg: &Config{ + Bundles: map[string]*Source{ + "b1": {Config: baseConf, Service: serviceName, Resource: "/bundles/bundle.tar.gz"}, + }, + }, + }, + { + name: "add second bundle", + cfg: &Config{ + Bundles: map[string]*Source{ + "b1": {Config: baseConf, Service: serviceName, Resource: "/bundles/bundle1.tar.gz"}, + "b2": {Config: baseConf, Service: serviceName, Resource: "/bundles/bundle2.tar.gz"}, + }, + }, + }, + { + name: "remove initial bundle", + cfg: &Config{ + Bundles: map[string]*Source{ + "b2": {Config: baseConf, Service: serviceName, Resource: "/bundles/bundle2.tar.gz"}, + }, + }, + }, + { + name: "Update single bundle", + cfg: &Config{ + Bundles: map[string]*Source{ + "b2": {Config: baseConf, Service: serviceName, Resource: "/new/path/bundles/bundle2.tar.gz"}, + }, + }, + }, + { + name: "Add multiple new bundles", + cfg: &Config{ + Bundles: map[string]*Source{ + "b3": {Config: baseConf, Service: serviceName, Resource: "/bundle3.tar.gz"}, + "b4": {Config: baseConf, Service: serviceName, Resource: "/bundles/bundle4.tar.gz"}, + "b5": {Config: baseConf, Service: serviceName, Resource: "/bundles/bundle5.tar.gz"}, + }, + }, + }, + { + name: "Remove multiple bundles", + cfg: &Config{ + Bundles: map[string]*Source{ + "b2": {Config: baseConf, Service: serviceName, Resource: "/new/path/bundles/bundle2.tar.gz"}, + "b4": {Config: baseConf, Service: serviceName, Resource: "/bundles/bundle4.tar.gz"}, + }, + }, + }, + { + name: "Update multiple bundles", + cfg: &Config{ + Bundles: map[string]*Source{ + "b2": {Config: baseConf, Service: serviceName, Resource: "/update2/bundle2.tar.gz"}, + "b4": {Config: baseConf, Service: serviceName, Resource: "/update2/bundle4.tar.gz"}, + }, + }, + }, + { + name: "Remove and add bundle", + cfg: &Config{ + Bundles: map[string]*Source{ + "b6": {Config: baseConf, Service: serviceName, Resource: "bundle6.tar.gz"}, + }, + }, + }, + { + name: "Add and update bundle", + cfg: &Config{ + Bundles: map[string]*Source{ + "b6": {Config: baseConf, Service: serviceName, Resource: "/update3/bundle6.tar.gz"}, + "b7": {Config: baseConf, Service: serviceName, Resource: "bundle7.tar.gz"}, + "b8": {Config: baseConf, Service: serviceName, Resource: "bundle8.tar.gz"}, + }, + }, + }, + { + name: "Update and remove", + cfg: &Config{ + Bundles: map[string]*Source{ + "b6": {Config: baseConf, Service: serviceName, Resource: "/update4/bundle6.tar.gz"}, + "b8": {Config: baseConf, Service: serviceName, Resource: "bundle8.tar.gz"}, + }, + }, + }, + // Add, Update, and Remove + { + name: "Add update and remove", + cfg: &Config{ + Bundles: map[string]*Source{ + "b8": {Config: baseConf, Service: serviceName, Resource: "/update5/bundle8.tar.gz"}, + "b9": {Config: baseConf, Service: serviceName, Resource: "bundle9.tar.gz"}, + }, + }, + }, + } + + for _, stage := range stages { + t.Run(stage.name, func(t *testing.T) { + + plugin.Reconfigure(ctx, stage.cfg) + + var expectedNumBundles int + if stage.cfg.Name != "" { + expectedNumBundles = 1 + } else { + expectedNumBundles = len(stage.cfg.Bundles) + } + + if expectedNumBundles != len(plugin.downloaders) { + t.Fatalf("Expected a downloader for each configured bundle, expected %d found %d", expectedNumBundles, len(plugin.downloaders)) + } + + if expectedNumBundles != len(plugin.status) { + t.Fatalf("Expected a status entry for each configured bundle, expected %d found %d", expectedNumBundles, len(plugin.status)) + } + + for name := range stage.cfg.Bundles { + if _, found := plugin.downloaders[name]; !found { + t.Fatalf("bundle %q not found in downloaders map", name) + } + + if _, found := plugin.status[name]; !found { + t.Fatalf("bundle %q not found in status map", name) + } + } + }) + } + if len(stages) != updateCount { + t.Fatalf("Expected to have received %d updates, got %d", len(stages), updateCount) + } +} + +func TestPluginRequestVsDownloadTimestamp(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + } + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + b := &bundle.Bundle{} + b.Manifest.Init() + + // simulate HTTP 200 response from downloader + plugin.oneShot(ctx, bundleName, download.Update{Bundle: b}) + + if plugin.status[bundleName].LastSuccessfulDownload != plugin.status[bundleName].LastSuccessfulRequest || plugin.status[bundleName].LastSuccessfulDownload != plugin.status[bundleName].LastRequest { + t.Fatal("expected last successful request to be same as download and request") + } + + // The time resolution is 1ns so sleeping for 1ms should be more than enough. + time.Sleep(time.Millisecond) + + // simulate HTTP 304 response from downloader. + plugin.oneShot(ctx, bundleName, download.Update{Bundle: nil}) + + if plugin.status[bundleName].LastSuccessfulDownload == plugin.status[bundleName].LastSuccessfulRequest || plugin.status[bundleName].LastSuccessfulDownload == plugin.status[bundleName].LastRequest { + t.Fatal("expected last successful request to differ from download and request") + } + + // simulate HTTP 200 response from downloader + plugin.oneShot(ctx, bundleName, download.Update{Bundle: b}) + + if plugin.status[bundleName].LastSuccessfulDownload != plugin.status[bundleName].LastSuccessfulRequest || plugin.status[bundleName].LastSuccessfulDownload != plugin.status[bundleName].LastRequest { + t.Fatal("expected last successful request to be same as download and request") + } + + // simulate error response from downloader + plugin.oneShot(ctx, bundleName, download.Update{Error: errors.New("xxx")}) + + if plugin.status[bundleName].LastSuccessfulDownload != plugin.status[bundleName].LastSuccessfulRequest || plugin.status[bundleName].LastSuccessfulDownload == plugin.status[bundleName].LastRequest { + t.Fatal("expected last successful request to be same as download but different from request") + } +} + +func TestReconfigurePlugin_OneShot_BundleDeactivation(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + runtimeRegoVersion ast.RegoVersion + bundleRegoVersion ast.RegoVersion + moduleRegoVersion ast.RegoVersion + module string + }{ + { + note: "v0 runtime, v0 bundle", + runtimeRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV0, + moduleRegoVersion: ast.RegoV0, + module: `package a + p[42] { true }`, + }, + { + note: "v0 runtime, v1 bundle", + runtimeRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV1, + moduleRegoVersion: ast.RegoV1, + module: `package a + p contains 42 if { true }`, + }, + { + note: "v0 runtime, custom bundle", + runtimeRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoUndefined, + moduleRegoVersion: ast.RegoV0, + module: `package a + p[42] { true }`, + }, + { + note: "v1 runtime, v0 bundle", + runtimeRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV0, + moduleRegoVersion: ast.RegoV0, + module: `package a + p[42] { true }`, + }, + { + note: "v1 runtime, v1 bundle", + runtimeRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV1, + moduleRegoVersion: ast.RegoV1, + module: `package a + p contains 42 if { true }`, + }, + { + note: "v1 runtime, custom bundle", + runtimeRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoUndefined, + moduleRegoVersion: ast.RegoV1, + module: `package a + p contains 42 if { true }`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + manager, err := plugins.New(nil, "test-instance-id", inmemtst.New(), plugins.WithParserOptions(ast.ParserOptions{RegoVersion: tc.runtimeRegoVersion})) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + bundleName := "test-bundle" + + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + config: Config{ + Bundles: map[string]*Source{ + bundleName: { + Service: "s1", + }, + }, + }, + } + + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{Trigger: pointTo(plugins.TriggerManual)}, plugin.manager.Client(""), bundleName) + + b := bundle.Bundle{ + Manifest: bundle.Manifest{ + Revision: "quickbrownfaux", + Roots: &[]string{"a"}, + }, + Modules: []bundle.ModuleFile{ + { + Path: "bundle/id1", + Parsed: ast.MustParseModuleWithOpts(tc.module, ast.ParserOptions{RegoVersion: tc.moduleRegoVersion}), + Raw: []byte(tc.module), + }, + }, + } + + if tc.bundleRegoVersion != ast.RegoUndefined { + b.Manifest.RegoVersion = pointTo(tc.bundleRegoVersion.Int()) + } + + b.Manifest.Init() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + + // Ensure it has been activated + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expIDs := []string{"test-bundle/bundle/id1"} + + sort.Strings(ids) + sort.Strings(expIDs) + + if !slices.Equal(ids, expIDs) { + t.Fatalf("expected ids %v but got %v", expIDs, ids) + } + + manager.Store.Abort(ctx, txn) + + // reconfigure with dropped bundle + + plugin.Reconfigure(ctx, &Config{ + Bundles: map[string]*Source{}, + }) + + // bundle was removed from store + + txn = storage.NewTransactionOrDie(ctx, manager.Store) + + ids, err = manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expIDs = []string{} + + sort.Strings(ids) + + if !slices.Equal(ids, expIDs) { + t.Fatalf("expected ids %v but got %v", expIDs, ids) + } + + manager.Store.Abort(ctx, txn) + }) + } +} + +func TestReconfigurePlugin_ManagerInit_BundleDeactivation(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + runtimeRegoVersion ast.RegoVersion + bundleRegoVersion ast.RegoVersion + moduleRegoVersion ast.RegoVersion + module string + }{ + { + note: "v0 runtime, v0 bundle", + runtimeRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV0, + moduleRegoVersion: ast.RegoV0, + module: `package a + p[42] { true }`, + }, + { + note: "v0 runtime, v1 bundle", + runtimeRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV1, + moduleRegoVersion: ast.RegoV1, + module: `package a + p contains 42 if { true }`, + }, + { + note: "v0 runtime, custom bundle", + runtimeRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoUndefined, + moduleRegoVersion: ast.RegoV0, + module: `package a + p[42] { true }`, + }, + { + note: "v1 runtime, v0 bundle", + runtimeRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV0, + moduleRegoVersion: ast.RegoV0, + module: `package a + p[42] { true }`, + }, + { + note: "v1 runtime, v1 bundle", + runtimeRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV1, + moduleRegoVersion: ast.RegoV1, + module: `package a + p contains 42 if { true }`, + }, + { + note: "v1 runtime, custom bundle", + runtimeRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoUndefined, + moduleRegoVersion: ast.RegoV1, + module: `package a + p contains 42 if { true }`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + bundleName := "test-bundle" + + b := bundle.Bundle{ + Manifest: bundle.Manifest{ + Revision: "quickbrownfaux", + Roots: &[]string{"a"}, + }, + Modules: []bundle.ModuleFile{ + { + Path: "bundle/id1", + Parsed: ast.MustParseModuleWithOpts(tc.module, ast.ParserOptions{RegoVersion: tc.moduleRegoVersion}), + Raw: []byte(tc.module), + }, + }, + } + + if tc.bundleRegoVersion != ast.RegoUndefined { + b.Manifest.RegoVersion = pointTo(tc.bundleRegoVersion.Int()) + } + + b.Manifest.Init() + + bundles := map[string]*bundle.Bundle{ + bundleName: &b, + } + + ctx := context.Background() + manager, err := plugins.New(nil, "test-instance-id", inmemtst.New(), + plugins.WithParserOptions(ast.ParserOptions{RegoVersion: tc.runtimeRegoVersion}), + plugins.InitBundles(bundles)) + + if err := manager.Init(ctx); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + config: Config{ + Bundles: map[string]*Source{ + bundleName: { + Service: "s1", + }, + }, + }, + } + + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{Trigger: pointTo(plugins.TriggerManual)}, plugin.manager.Client(""), bundleName) + + // Ensure it has been activated + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expIDs := []string{"test-bundle/bundle/id1"} + + sort.Strings(ids) + sort.Strings(expIDs) + + if !slices.Equal(ids, expIDs) { + t.Fatalf("expected ids %v but got %v", expIDs, ids) + } + + manager.Store.Abort(ctx, txn) + + // reconfigure with dropped bundle + + plugin.Reconfigure(ctx, &Config{ + Bundles: map[string]*Source{}, + }) + + // bundle was removed from store + + txn = storage.NewTransactionOrDie(ctx, manager.Store) + + ids, err = manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expIDs = []string{} + + sort.Strings(ids) + + if !slices.Equal(ids, expIDs) { + t.Fatalf("expected ids %v but got %v", expIDs, ids) + } + + manager.Store.Abort(ctx, txn) + }) + } +} + +func TestUpgradeLegacyBundleToMuiltiBundleSameBundle(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + } + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + // Start with a "legacy" style config for a single bundle + plugin.config = Config{ + Bundles: map[string]*Source{ + bundleName: { + Service: "s1", + }, + }, + Name: bundleName, + Service: "s1", + Prefix: nil, + } + + module := "package a.a1\n\nbar=1" + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux", Roots: &[]string{"a/a1", "a/a2"}}, + Data: map[string]any{ + "a": map[string]any{ + "a2": "foo", + }, + }, + Modules: []bundle.ModuleFile{ + { + Path: "bundle/id1", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + + // Ensure it has been activated + expData := util.MustUnmarshalJSON([]byte(`{"a2": "foo"}`)) + expIDs := []string{"bundle/id1"} + validateStoreState(ctx, t, manager.Store, "/a", expData, expIDs, bundleName, "quickbrownfaux", nil) + + if plugin.config.IsMultiBundle() { + t.Fatalf("Expected plugin to be in non-multi bundle config mode") + } + + // Update to the newer style config with the same bundle + multiBundleConf := &Config{ + Bundles: map[string]*Source{ + bundleName: { + Service: "s1", + }, + }, + } + + plugin.Reconfigure(ctx, multiBundleConf) + b.Manifest.Revision = "quickbrownfaux-2" + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + + // The only thing that should have changed is the store id for the policy + expIDs = []string{"test-bundle/bundle/id1"} + validateStoreState(ctx, t, manager.Store, "/a", expData, expIDs, bundleName, "quickbrownfaux-2", nil) + + // Make sure the legacy path is gone now that we are in multi-bundle mode + var actual string + err := storage.Txn(ctx, plugin.manager.Store, storage.WriteParams, func(txn storage.Transaction) error { + var err error + if actual, err = bundle.LegacyReadRevisionFromStore(ctx, plugin.manager.Store, txn); err != nil && !storage.IsNotFound(err) { + t.Fatalf("Failed to read manifest revision from store: %s", err) + return err + } + return nil + }) + if err != nil { + t.Fatalf("Unexpected error finishing transaction: %s", err) + } + if actual != "" { + t.Fatalf("Expected to not find manifest revision but got %s", actual) + } + + if !plugin.config.IsMultiBundle() { + t.Fatalf("Expected plugin to be in multi bundle config mode") + } +} + +func TestUpgradeLegacyBundleToMultiBundleNewBundles(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager := getTestManager() + + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + } + + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + tsURLBase := "/opa-test/" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !strings.HasPrefix(r.URL.Path, tsURLBase) { + t.Fatalf("Invalid request URL path: %s, expected prefix %s", r.URL.Path, tsURLBase) + } + fmt.Fprintln(w, "") // Note: this is an invalid bundle and will fail the download + })) + defer ts.Close() + + serviceName := "test-svc" + err := manager.Reconfigure(&config.Config{ + Services: fmt.Appendf(nil, "{\"%s\":{ \"url\": \"%s\"}}", serviceName, ts.URL+tsURLBase), + }) + if err != nil { + t.Fatalf("Error configuring plugin manager: %s", err) + } + + var delay int64 = 10 + triggerPolling := plugins.TriggerPeriodic + downloadConf := download.Config{Polling: download.PollingConfig{MinDelaySeconds: &delay, MaxDelaySeconds: &delay}, Trigger: &triggerPolling} + + // Start with a "legacy" style config for a single bundle + plugin.config = Config{ + Bundles: map[string]*Source{ + bundleName: { + Config: downloadConf, + Service: serviceName, + }, + }, + Name: bundleName, + Service: serviceName, + Prefix: nil, + } + + module := "package a.a1\n\nbar=1" + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux", Roots: &[]string{"a/a1", "a/a2"}}, + Data: map[string]any{ + "a": map[string]any{ + "a2": "foo", + }, + }, + Modules: []bundle.ModuleFile{ + { + Path: "bundle/id1", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + + // Ensure it has been activated + expData := util.MustUnmarshalJSON([]byte(`{"a2": "foo"}`)) + expIDs := []string{"bundle/id1"} + validateStoreState(ctx, t, manager.Store, "/a", expData, expIDs, bundleName, "quickbrownfaux", nil) + + if plugin.config.IsMultiBundle() { + t.Fatalf("Expected plugin to be in non-multi bundle config mode") + } + + // Update to the newer style config with a new bundle + multiBundleConf := &Config{ + Bundles: map[string]*Source{ + "b2": { + Config: downloadConf, + Service: serviceName, + }, + }, + } + + delete(plugin.downloaders, bundleName) + plugin.downloaders["b2"] = download.New(download.Config{}, plugin.manager.Client(""), "b2") + plugin.Reconfigure(ctx, multiBundleConf) + + module = "package a.c\n\nbar=1" + b = bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "b2-1", Roots: &[]string{"a/b2", "a/c"}}, + Data: map[string]any{ + "a": map[string]any{ + "b2": "foo", + }, + }, + Modules: []bundle.ModuleFile{ + { + Path: "id1", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + b.Manifest.Init() + plugin.oneShot(ctx, "b2", download.Update{Bundle: &b}) + + expData = util.MustUnmarshalJSON([]byte(`{"b2": "foo"}`)) + expIDs = []string{"b2/id1"} + validateStoreState(ctx, t, manager.Store, "/a", expData, expIDs, "b2", "b2-1", nil) + + // Make sure the legacy path is gone now that we are in multi-bundle mode + var actual string + err = storage.Txn(ctx, plugin.manager.Store, storage.WriteParams, func(txn storage.Transaction) error { + var err error + if actual, err = bundle.LegacyReadRevisionFromStore(ctx, plugin.manager.Store, txn); err != nil && !storage.IsNotFound(err) { + t.Fatalf("Failed to read manifest revision from store: %s", err) + return err + } + return nil + }) + if err != nil { + t.Fatalf("Unexpected error finishing transaction: %s", err) + } + if actual != "" { + t.Fatalf("Expected to not find manifest revision but got %s", actual) + } + + if !plugin.config.IsMultiBundle() { + t.Fatalf("Expected plugin to be in multi bundle config mode") + } +} + +func TestLegacyBundleDataRead(t *testing.T) { + t.Parallel() + + readModes := []struct { + note string + readAst bool + }{ + { + note: "read raw", + readAst: false, + }, + { + note: "read ast", + readAst: true, + }, + } + + for _, rm := range readModes { + t.Run(rm.note, func(t *testing.T) { + ctx := context.Background() + manager := getTestManagerWithOpts(nil, inmem.NewWithOpts(inmem.OptReturnASTValuesOnRead(rm.readAst))) + + plugin := Plugin{ + manager: manager, + status: map[string]*Status{}, + etags: map[string]string{}, + downloaders: map[string]Loader{}, + } + + bundleName := "test-bundle" + plugin.status[bundleName] = &Status{Name: bundleName} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + tsURLBase := "/opa-test/" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !strings.HasPrefix(r.URL.Path, tsURLBase) { + t.Fatalf("Invalid request URL path: %s, expected prefix %s", r.URL.Path, tsURLBase) + } + fmt.Fprintln(w, "") // Note: this is an invalid bundle and will fail the download + })) + defer ts.Close() + + serviceName := "test-svc" + err := manager.Reconfigure(&config.Config{ + Services: fmt.Appendf(nil, "{\"%s\":{ \"url\": \"%s\"}}", serviceName, ts.URL+tsURLBase), + }) + if err != nil { + t.Fatalf("Error configuring plugin manager: %s", err) + } + + var delay int64 = 10 + triggerPolling := plugins.TriggerPeriodic + downloadConf := download.Config{Polling: download.PollingConfig{MinDelaySeconds: &delay, MaxDelaySeconds: &delay}, Trigger: &triggerPolling} + + // Start with a "legacy" style config for a single bundle + plugin.config = Config{ + Bundles: map[string]*Source{ + bundleName: { + Config: downloadConf, + Service: serviceName, + }, + }, + Name: bundleName, + Service: serviceName, + Prefix: nil, + } + + module := "package a.a1\n\nbar=1" + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux", Roots: &[]string{"a/a1", "a/a2"}}, + Data: map[string]any{ + "a": map[string]any{ + "a2": "foo", + }, + }, + Modules: []bundle.ModuleFile{ + { + Path: "bundle/id1", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + + if plugin.config.IsMultiBundle() { + t.Fatalf("Expected plugin to be in non-multi bundle config mode") + } + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b}) + + exp := `{"a2": "foo"}` + var expData any + if rm.readAst { + expData = ast.MustParseTerm(exp).Value + } else { + expData = util.MustUnmarshalJSON([]byte(exp)) + } + + expIDs := []string{"bundle/id1"} + validateStoreState(ctx, t, manager.Store, "/a", expData, expIDs, bundleName, "quickbrownfaux", nil) + }) + } +} + +func TestSaveBundleToDiskNew(t *testing.T) { + t.Parallel() + + manager := getTestManager() + + dir := t.TempDir() + + bundles := map[string]*Source{} + plugin := New(&Config{Bundles: bundles}, manager) + plugin.bundlePersistPath = filepath.Join(dir, ".opa") + + err := plugin.saveBundleToDisk("foo", getTestRawBundle(t)) + if err != nil { + t.Fatalf("unexpected error %v", err) + } +} + +func TestSaveBundleToDiskNewConfiguredPersistDir(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + + manager := getTestManager() + manager.Config.PersistenceDirectory = &dir + bundles := map[string]*Source{} + plugin := New(&Config{Bundles: bundles}, manager) + + err := plugin.Start(context.Background()) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + err = plugin.saveBundleToDisk("foo", getTestRawBundle(t)) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + expectBundlePath := filepath.Join(dir, "bundles", "foo", "bundle.tar.gz") + _, err = os.Stat(expectBundlePath) + if err != nil { + t.Errorf("expected bundle persisted at path %v, %v", expectBundlePath, err) + } +} + +func TestSaveBundleToDiskOverWrite(t *testing.T) { + t.Parallel() + + manager := getTestManager() + + // test to check existing bundle is replaced + dir := t.TempDir() + + bundles := map[string]*Source{} + plugin := New(&Config{Bundles: bundles}, manager) + plugin.bundlePersistPath = filepath.Join(dir, ".opa") + + bundleName := "foo" + bundleDir := filepath.Join(plugin.bundlePersistPath, bundleName) + + err := os.MkdirAll(bundleDir, os.ModePerm) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + b2 := writeTestBundleToDisk(t, bundleDir, false) + + module := "package a.a1\n\nbar=1" + + newBundle := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "quickbrownfaux", Roots: &[]string{"a/a1", "a/a2"}}, + Data: map[string]any{ + "a": map[string]any{ + "a2": "foo", + }, + }, + Modules: []bundle.ModuleFile{ + { + Path: "bundle/id1", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + newBundle.Manifest.Init() + + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).UseModulePath(true).Write(newBundle); err != nil { + t.Fatal("unexpected error:", err) + } + + err = plugin.saveBundleToDisk("foo", &buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + actual, err := plugin.loadBundleFromDisk(plugin.bundlePersistPath, "foo", nil) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + if actual.Equal(b2) { + t.Fatal("expected existing bundle to be overwritten") + } +} + +func TestSaveCurrentBundleToDisk(t *testing.T) { + t.Parallel() + + srcDir := t.TempDir() + + bundlePath, err := saveCurrentBundleToDisk(srcDir, getTestRawBundle(t)) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + if _, err := os.Stat(bundlePath); err != nil { + t.Fatalf("unexpected error %v", err) + } + + _, err = saveCurrentBundleToDisk(srcDir, nil) + if err == nil { + t.Fatal("expected error but got nil") + } + + expErrMsg := "no raw bundle bytes to persist to disk" + if err.Error() != expErrMsg { + t.Fatalf("expected error: %v but got: %v", expErrMsg, err) + } +} + +func TestLoadBundleFromDisk(t *testing.T) { + t.Parallel() + + manager := getTestManager() + plugin := New(&Config{}, manager) + + // no bundle on disk + _, err := plugin.loadBundleFromDisk("foo", "bar", nil) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + // create a test bundle and load it from disk + dir := t.TempDir() + + bundleName := "foo" + bundleDir := filepath.Join(dir, bundleName) + + err = os.MkdirAll(bundleDir, os.ModePerm) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + b := writeTestBundleToDisk(t, bundleDir, false) + + result, err := plugin.loadBundleFromDisk(dir, bundleName, nil) + if err != nil { + t.Fatal("unexpected error:", err) + } + + if !result.Equal(b) { + t.Fatal("expected the test bundle to be equal to the one loaded from disk") + } +} + +func TestLoadBundleFromDiskV1Compatible(t *testing.T) { + t.Parallel() + + popts := ast.ParserOptions{RegoVersion: ast.RegoV1} + + manager, err := plugins.New(nil, "test-instance-id", inmemtst.New(), plugins.WithParserOptions(popts)) + if err != nil { + t.Fatal("unexpected error:", err) + } + plugin := New(&Config{}, manager) + + // create a test bundle and load it from disk + dir := t.TempDir() + + bundleName := "foo" + bundleDir := filepath.Join(dir, bundleName) + + err = os.MkdirAll(bundleDir, os.ModePerm) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + // v1.0 policy + policy := `package test +p contains 1 if { + input.x == 2 +}` + + b := bundle.Bundle{ + Manifest: bundle.Manifest{Revision: "test-revision"}, + Modules: []bundle.ModuleFile{ + { + URL: `policy.rego`, + Path: `/policy.rego`, + Raw: []byte(policy), + Parsed: ast.MustParseModuleWithOpts(policy, popts), + }, + }, + Data: map[string]any{}, + } + + b.Manifest.Init() + + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).UseModulePath(true).Write(b); err != nil { + t.Fatalf("unexpected error %v", err) + } + + if err := os.WriteFile(filepath.Join(bundleDir, "bundle.tar.gz"), buf.Bytes(), 0644); err != nil { + t.Fatalf("unexpected error %v", err) + } + + result, err := plugin.loadBundleFromDisk(dir, bundleName, nil) + if err != nil { + t.Fatal("unexpected error:", err) + } + + if !result.Equal(b) { + t.Fatal("expected the test bundle to be equal to the one loaded from disk") + } +} + +func TestLoadSignedBundleFromDisk(t *testing.T) { + t.Parallel() + + manager := getTestManager() + plugin := New(&Config{}, manager) + + // no bundle on disk + _, err := plugin.loadBundleFromDisk("foo", "bar", nil) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + // create a test signed bundle and load it from disk + dir := t.TempDir() + + bundleName := "foo" + bundleDir := filepath.Join(dir, bundleName) + + err = os.MkdirAll(bundleDir, os.ModePerm) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + b := writeTestBundleToDisk(t, bundleDir, true) + + src := Source{ + Signing: bundle.NewVerificationConfig(map[string]*keys.Config{"foo": {Key: "secret", Algorithm: "HS256"}}, "foo", "", nil), + } + + result, err := plugin.loadBundleFromDisk(dir, bundleName, &src) + if err != nil { + t.Fatal("unexpected error:", err) + } + + if !result.Equal(b) { + t.Fatal("expected the test bundle to be equal to the one loaded from disk") + } + + if !reflect.DeepEqual(result.Signatures, b.Signatures) { + t.Fatal("Expected signatures to be same") + } +} + +func TestGetDefaultBundlePersistPath(t *testing.T) { + t.Parallel() + + plugin := New(&Config{}, getTestManager()) + path, err := plugin.getBundlePersistPath() + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + if !strings.HasSuffix(path, ".opa/bundles") { + t.Fatal("expected default persist path to end with '.opa/bundles' dir") + } +} + +func TestConfiguredBundlePersistPath(t *testing.T) { + t.Parallel() + + persistPath := "/var/opa" + manager := getTestManager() + manager.Config.PersistenceDirectory = &persistPath + plugin := New(&Config{}, manager) + + path, err := plugin.getBundlePersistPath() + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + if path != "/var/opa/bundles" { + t.Errorf("expected configured persist path '/var/opa/bundles'") + } +} + +func TestPluginUsingFileLoader(t *testing.T) { + t.Parallel() + + test.WithTempFS(map[string]string{}, func(dir string) { + + b := bundle.Bundle{ + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + URL: "test.rego", + Raw: []byte(`package test + + p = 7`), + }, + }, + } + + name := path.Join(dir, "bundle.tar.gz") + + f, err := os.Create(name) + if err != nil { + t.Fatal(err) + } + + if err := bundle.NewWriter(f).Write(b); err != nil { + t.Fatal(err) + } + + f.Close() + + mgr := getTestManager() + url := "file://" + name + + p := New(&Config{Bundles: map[string]*Source{ + "test": { + SizeLimitBytes: 1e5, + Resource: url, + }, + }}, mgr) + + ch := make(chan Status) + + p.Register("test", func(s Status) { + ch <- s + }) + + if err := p.Start(context.Background()); err != nil { + t.Fatal(err) + } + + s := <-ch + + if s.LastSuccessfulActivation.IsZero() { + t.Fatal("expected successful activation") + } + }) +} + +func TestPluginUsingFileLoaderV1Compatible(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + v1Compatible bool + module string + expErrs []string + }{ + { + note: "v0.x, keywords not used", + module: `package test +p[7] { + input.x == 2 +}`, + }, + { + note: "v0.x, shadowed import", + module: `package test +import future.keywords +import data.foo +import data.bar as foo +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v0.x, keywords not imported", + module: `package test +p contains 7 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0.x, keywords imported", + module: `package test +import future.keywords +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v0.x, rego.ve imported", + module: `package test +import rego.v1 +p contains 7 if { + input.x == 2 +}`, + }, + // parse-time error + { + note: "v1.0, keywords not used", + v1Compatible: true, + module: `package test +p[7] { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + // compile-time error + { + note: "v1.0, shadowed import", + v1Compatible: true, + module: `package test +import data.foo +import data.bar as foo +p contains 7 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v1.0, keywords not imported", + v1Compatible: true, + module: `package test +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v1.0, keywords imported", + v1Compatible: true, + module: `package test +import future.keywords +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v1.0, rego.ve imported", + v1Compatible: true, + module: `package test +import rego.v1 +p contains 7 if { + input.x == 2 +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + regoVersion := ast.RegoV0 + if tc.v1Compatible { + regoVersion = ast.RegoV1 + } + popts := ast.ParserOptions{RegoVersion: regoVersion} + + test.WithTempFS(map[string]string{}, func(dir string) { + + b := bundle.Bundle{ + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + URL: "test.rego", + Raw: []byte(tc.module), + }, + }, + } + + name := path.Join(dir, "bundle.tar.gz") + + f, err := os.Create(name) + if err != nil { + t.Fatal(err) + } + + if err := bundle.NewWriter(f).Write(b); err != nil { + t.Fatal(err) + } + + f.Close() + + manager, err := plugins.New(nil, "test-instance-id", inmemtst.New(), plugins.WithParserOptions(popts)) + if err != nil { + t.Fatal("unexpected error:", err) + } + url := "file://" + name + + p := New(&Config{Bundles: map[string]*Source{ + "test": { + SizeLimitBytes: 1e5, + Resource: url, + }, + }}, manager) + + ch := make(chan Status) + + p.Register("test", func(s Status) { + ch <- s + }) + + if err := p.Start(context.Background()); err != nil { + t.Fatal(err) + } + + s := <-ch + + if tc.expErrs != nil { + for _, expErr := range tc.expErrs { + found := false + for _, e := range s.Errors { + if strings.Contains(e.Error(), expErr) { + found = true + break + } + } + if !found { + t.Fatalf("expected error:\n\n%s\n\nbut got:\n\n%v", expErr, s.Errors) + } + } + } else if s.LastSuccessfulActivation.IsZero() { + t.Fatal("expected successful activation") + } + }) + }) + } +} + +func TestPluginUsingFileLoaderWithBundleRegoVersion(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + managerRegoVersion ast.RegoVersion + bundleRegoVersion ast.RegoVersion + module string + expErrs []string + }{ + { + note: "v0.x manager, v0.x bundle, keywords not used", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV0, + module: `package test +p[7] { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v0.x bundle, shadowed import", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV0, + module: `package test +import future.keywords +import data.foo +import data.bar as foo +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v0.x bundle, keywords not imported", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV0, + module: `package test +p contains 7 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0.x manager, v0.x bundle, keywords imported", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV0, + module: `package test +import future.keywords +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v0.x bundle, rego.v1 imported", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV0, + module: `package test +import rego.v1 +p contains 7 if { + input.x == 2 +}`, + }, + // parse-time error + { + note: "v0.x manager, v1.0 bundle, keywords not used", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV1, + module: `package test +p[7] { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + // compile-time error + { + note: "v0.x manager, v1.0 bundle, shadowed import", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV1, + module: `package test +import data.foo +import data.bar as foo +p contains 7 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v0.x manager, v1.0 bundle, keywords not imported", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV1, + module: `package test +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v1.0 bundle, keywords imported", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV1, + module: `package test +import future.keywords +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v1.0 bundle, rego.ve imported", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV1, + module: `package test +import rego.v1 +p contains 7 if { + input.x == 2 +}`, + }, + + { + note: "v1.0 manager, v0.x bundle, keywords not used", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV0, + module: `package test +p[7] { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v0.x bundle, shadowed import", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV0, + module: `package test +import future.keywords +import data.foo +import data.bar as foo +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v0.x bundle, keywords not imported", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV0, + module: `package test +p contains 7 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v1.0 manager, v0.x bundle, keywords imported", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV0, + module: `package test +import future.keywords +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v0.x bundle, rego.v1 imported", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV0, + module: `package test +import rego.v1 +p contains 7 if { + input.x == 2 +}`, + }, + // parse-time error + { + note: "v1.0 manager, v1.0 bundle, keywords not used", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV1, + module: `package test +p[7] { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + // compile-time error + { + note: "v1.0 manager, v1.0 bundle, shadowed import", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV1, + module: `package test +import data.foo +import data.bar as foo +p contains 7 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v1.0 manager, v1.0 bundle, keywords not imported", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV1, + module: `package test +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v1.0 bundle, keywords imported", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV1, + module: `package test +import future.keywords +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v1.0 bundle, rego.ve imported", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV1, + module: `package test +import rego.v1 +p contains 7 if { + input.x == 2 +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(map[string]string{}, func(dir string) { + + manifest := bundle.Manifest{} + manifest.SetRegoVersion(tc.bundleRegoVersion) + b := bundle.Bundle{ + Manifest: manifest, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + URL: "test.rego", + Raw: []byte(tc.module), + }, + }, + } + + name := path.Join(dir, "bundle.tar.gz") + + f, err := os.Create(name) + if err != nil { + t.Fatal(err) + } + + if err := bundle.NewWriter(f).Write(b); err != nil { + t.Fatal(err) + } + + f.Close() + + managerPopts := ast.ParserOptions{RegoVersion: tc.managerRegoVersion} + manager, err := plugins.New(nil, "test-instance-id", inmemtst.New(), + plugins.WithParserOptions(managerPopts)) + if err != nil { + t.Fatal("unexpected error:", err) + } + url := "file://" + name + + p := New(&Config{Bundles: map[string]*Source{ + "test": { + SizeLimitBytes: 1e5, + Resource: url, + }, + }}, manager) + + ch := make(chan Status) + + p.Register("test", func(s Status) { + ch <- s + }) + + if err := p.Start(context.Background()); err != nil { + t.Fatal(err) + } + + s := <-ch + + if tc.expErrs != nil { + for _, expErr := range tc.expErrs { + found := false + for _, e := range s.Errors { + if strings.Contains(e.Error(), expErr) { + found = true + break + } + } + if !found { + t.Fatalf("expected error:\n\n%s\n\nbut got:\n\n%v", expErr, s.Errors) + } + } + } else if s.LastSuccessfulActivation.IsZero() { + t.Fatal("expected successful activation") + } + }) + }) + } +} + +func TestPluginUsingDirectoryLoader(t *testing.T) { + t.Parallel() + + test.WithTempFS(map[string]string{ + "test.rego": `package test + + p := 7`, + }, func(dir string) { + + mgr := getTestManager() + url := "file://" + dir + + p := New(&Config{Bundles: map[string]*Source{ + "test": { + SizeLimitBytes: 1e5, + Resource: url, + }, + }}, mgr) + + ch := make(chan Status) + + p.Register("test", func(s Status) { + ch <- s + }) + + if err := p.Start(context.Background()); err != nil { + t.Fatal(err) + } + + s := <-ch + + if s.LastSuccessfulActivation.IsZero() { + t.Fatal("expected successful activation") + } + }) +} + +func TestPluginUsingDirectoryLoaderV1Compatible(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + v1Compatible bool + module string + expErrs []string + }{ + { + note: "v0.x, keywords not used", + module: `package test +p[7] { + input.x == 2 +}`, + }, + { + note: "v0.x, shadowed import", + module: `package test +import future.keywords +import data.foo +import data.bar as foo +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v0.x, keywords not imported", + module: `package test +p contains 7 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0.x, keywords imported", + module: `package test +import future.keywords +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v0.x, rego.ve imported", + module: `package test +import rego.v1 +p contains 7 if { + input.x == 2 +}`, + }, + // parse-time error + { + note: "v1.0, keywords not used", + v1Compatible: true, + module: `package test +p[7] { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + // compile-time error + { + note: "v1.0, shadowed import", + v1Compatible: true, + module: `package test +import data.foo +import data.bar as foo +p contains 7 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v1.0, keywords not imported", + v1Compatible: true, + module: `package test +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v1.0, keywords imported", + v1Compatible: true, + module: `package test +import future.keywords +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v1.0, rego.ve imported", + v1Compatible: true, + module: `package test +import rego.v1 +p contains 7 if { + input.x == 2 +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + regoVersion := ast.RegoV0 + if tc.v1Compatible { + regoVersion = ast.RegoV1 + } + popts := ast.ParserOptions{RegoVersion: regoVersion} + + test.WithTempFS(map[string]string{ + "test.rego": tc.module, + }, func(dir string) { + + manager, err := plugins.New(nil, "test-instance-id", inmemtst.New(), plugins.WithParserOptions(popts)) + if err != nil { + t.Fatal("unexpected error:", err) + } + url := "file://" + dir + + p := New(&Config{Bundles: map[string]*Source{ + "test": { + SizeLimitBytes: 1e5, + Resource: url, + }, + }}, manager) + + ch := make(chan Status) + + p.Register("test", func(s Status) { + ch <- s + }) + + if err := p.Start(context.Background()); err != nil { + t.Fatal(err) + } + + s := <-ch + + if tc.expErrs != nil { + for _, expErr := range tc.expErrs { + found := false + for _, e := range s.Errors { + if strings.Contains(e.Error(), expErr) { + found = true + break + } + } + if !found { + t.Fatalf("expected error:\n\n%s\n\nbut got:\n\n%v", expErr, s.Errors) + } + } + } else if s.LastSuccessfulActivation.IsZero() { + t.Fatal("expected successful activation") + } + }) + }) + } +} + +func TestPluginUsingDirectoryLoaderWithBundleRegoVersion(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + managerRegoVersion ast.RegoVersion + bundleRegoVersion ast.RegoVersion + module string + expErrs []string + }{ + { + note: "v0.x manager, v0.x bundle, keywords not used", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV0, + module: `package test +p[7] { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v0.x bundle, shadowed import", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV0, + module: `package test +import future.keywords +import data.foo +import data.bar as foo +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v0.x bundle, keywords not imported", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV0, + module: `package test +p contains 7 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0.x manager, v0.x bundle, keywords imported", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV0, + module: `package test +import future.keywords +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v0.x bundle, rego.v1 imported", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV0, + module: `package test +import rego.v1 +p contains 7 if { + input.x == 2 +}`, + }, + // parse-time error + { + note: "v0.x manager, v1.0 bundle, keywords not used", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV1, + module: `package test +p[7] { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + // compile-time error + { + note: "v0.x manager, v1.0 bundle, shadowed import", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV1, + module: `package test +import data.foo +import data.bar as foo +p contains 7 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v0.x manager, v1.0 bundle, keywords not imported", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV1, + module: `package test +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v1.0 bundle, keywords imported", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV1, + module: `package test +import future.keywords +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v0.x manager, v1.0 bundle, rego.ve imported", + managerRegoVersion: ast.RegoV0, + bundleRegoVersion: ast.RegoV1, + module: `package test +import rego.v1 +p contains 7 if { + input.x == 2 +}`, + }, + + { + note: "v1.0 manager, v0.x bundle, keywords not used", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV0, + module: `package test +p[7] { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v0.x bundle, shadowed import", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV0, + module: `package test +import future.keywords +import data.foo +import data.bar as foo +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v0.x bundle, keywords not imported", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV0, + module: `package test +p contains 7 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v1.0 manager, v0.x bundle, keywords imported", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV0, + module: `package test +import future.keywords +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v0.x bundle, rego.v1 imported", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV0, + module: `package test +import rego.v1 +p contains 7 if { + input.x == 2 +}`, + }, + // parse-time error + { + note: "v1.0 manager, v1.0 bundle, keywords not used", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV1, + module: `package test +p[7] { + input.x == 2 +}`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + // compile-time error + { + note: "v1.0 manager, v1.0 bundle, shadowed import", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV1, + module: `package test +import data.foo +import data.bar as foo +p contains 7 if { + input.x == 2 +}`, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + { + note: "v1.0 manager, v1.0 bundle, keywords not imported", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV1, + module: `package test +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v1.0 bundle, keywords imported", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV1, + module: `package test +import future.keywords +p contains 7 if { + input.x == 2 +}`, + }, + { + note: "v1.0 manager, v1.0 bundle, rego.ve imported", + managerRegoVersion: ast.RegoV1, + bundleRegoVersion: ast.RegoV1, + module: `package test +import rego.v1 +p contains 7 if { + input.x == 2 +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(map[string]string{ + "test.rego": tc.module, + ".manifest": fmt.Sprintf(`{"rego_version": %d}`, bundleRegoVersion(tc.bundleRegoVersion)), + }, func(dir string) { + + managerPopts := ast.ParserOptions{RegoVersion: tc.managerRegoVersion} + manager, err := plugins.New(nil, "test-instance-id", inmemtst.New(), + plugins.WithParserOptions(managerPopts)) + if err != nil { + t.Fatal("unexpected error:", err) + } + url := "file://" + dir + + p := New(&Config{Bundles: map[string]*Source{ + "test": { + SizeLimitBytes: 1e5, + Resource: url, + }, + }}, manager) + + ch := make(chan Status) + + p.Register("test", func(s Status) { + ch <- s + }) + + if err := p.Start(context.Background()); err != nil { + t.Fatal(err) + } + + s := <-ch + + if tc.expErrs != nil { + for _, expErr := range tc.expErrs { + found := false + for _, e := range s.Errors { + if strings.Contains(e.Error(), expErr) { + found = true + break + } + } + if !found { + t.Fatalf("expected error:\n\n%s\n\nbut got:\n\n%v", expErr, s.Errors) + } + } + } else if s.LastSuccessfulActivation.IsZero() { + t.Fatal("expected successful activation") + } + }) + }) + } +} + +func TestPluginReadBundleEtagFromDiskStore(t *testing.T) { + t.Parallel() + + // setup fake http server with mock bundle + mockBundle := bundle.Bundle{ + Data: map[string]any{"p": "x1"}, + Modules: []bundle.ModuleFile{}, + } + + notModifiedCount := 0 + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + etag := r.Header.Get("If-None-Match") + if etag == "foo" { + notModifiedCount++ + w.WriteHeader(304) + return + } + + w.Header().Add("Etag", "foo") + w.WriteHeader(200) + + err := bundle.NewWriter(w).Write(mockBundle) + if err != nil { + t.Fatal(err) + } + })) + + test.WithTempFS(nil, func(dir string) { + ctx := context.Background() + + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + Partitions: []storage.Path{ + storage.MustParsePath("/foo"), + }, + }) + if err != nil { + t.Fatal(err) + } + + // setup plugin pointing at fake server + manager := getTestManagerWithOpts(fmt.Appendf(nil, `{ + "services": { + "default": { + "url": %q + } + } + }`, s.URL), store) + + var mode plugins.TriggerMode = "manual" + + plugin := New(&Config{ + Bundles: map[string]*Source{ + "test": { + Service: "default", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + Config: download.Config{Trigger: &mode}, + }, + }, + }, manager) + + statusCh := make(chan map[string]*Status) + + // register for bundle updates to observe changes and start the plugin + plugin.RegisterBulkListener("test-case", func(st map[string]*Status) { + statusCh <- st + }) + + err = plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + + // manually trigger bundle download + go func() { + _ = plugin.Loaders()["test"].Trigger(ctx) + }() + + // wait for bundle update and then verify that activated bundle etag written to store + <-statusCh + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + + actual, err := manager.Store.Read(ctx, txn, storage.MustParsePath("/system/bundles/test/etag")) + if err != nil { + t.Fatal(err) + } + + if actual != "foo" { + t.Fatalf("Expected etag foo but got %v", actual) + } + + // Stop the "read" transaction + manager.Store.Abort(ctx, txn) + + // Stop the plugin and reinitialize it. Verify that etag is retrieved from store in the bundle request. + // The server should respond with a 304 as OPA has the right bundle loaded. + plugin.Stop(ctx) + + plugin = New(&Config{ + Bundles: map[string]*Source{ + "test": { + Service: "default", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + Config: download.Config{Trigger: &mode}, + }, + }, + }, manager) + + statusCh = make(chan map[string]*Status) + + // register for bundle updates to observe changes and start the plugin + plugin.RegisterBulkListener("test-case", func(st map[string]*Status) { + statusCh <- st + }) + + err = plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + + val, ok := plugin.etags["test"] + if !ok { + t.Fatal("Expected etag entry for bundle \"test\"") + } + + if val != "foo" { + t.Fatalf("Expected etag foo but got %v", val) + } + + // manually trigger bundle download + go func() { + _ = plugin.Loaders()["test"].Trigger(ctx) + }() + + <-statusCh + + if notModifiedCount != 1 { + t.Fatalf("Expected one bundle response with HTTP status 304 but got %v", notModifiedCount) + } + + // reconfigure the plugin + cfg := &Config{ + Bundles: map[string]*Source{ + "test": { + Service: "default", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + Config: download.Config{Trigger: &mode}, + Resource: "/new/path/bundles/bundle.tar.gz", + }, + }, + } + + plugin.Reconfigure(ctx, cfg) + + // manually trigger bundle download + go func() { + _ = plugin.Loaders()["test"].Trigger(ctx) + }() + + <-statusCh + + if notModifiedCount != 2 { + t.Fatalf("Expected two bundle responses with HTTP status 304 but got %v", notModifiedCount) + } + + val, ok = plugin.etags["test"] + if !ok { + t.Fatal("Expected etag entry for bundle \"test\"") + } + + if val != "foo" { + t.Fatalf("Expected etag foo but got %v", val) + } + }) +} + +func TestPluginStateReconciliationOnReconfigure(t *testing.T) { + t.Parallel() + + // setup fake http server with mock bundle + mockBundles := map[string]bundle.Bundle{ + "b1": { + Data: map[string]any{"b1": "x1"}, + Modules: []bundle.ModuleFile{}, + Manifest: bundle.Manifest{ + Roots: &[]string{"b1"}, + }, + }, + "b2": { + Data: map[string]any{"b2": "x1"}, + Modules: []bundle.ModuleFile{}, + Manifest: bundle.Manifest{ + Roots: &[]string{"b2"}, + }, + }, + "b3_frequently_changing": { + Data: map[string]any{"b3": "x1"}, + Modules: []bundle.ModuleFile{}, + Manifest: bundle.Manifest{ + Roots: &[]string{"b3"}, + }, + }, + } + + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + name := strings.TrimPrefix(r.URL.Path, "/") + etag := r.Header.Get("If-None-Match") + if etag == name && name != "b3_frequently_changing" { + w.WriteHeader(304) + return + } + + if name != "b3_frequently_changing" { + w.Header().Add("Etag", name) + } + w.WriteHeader(200) + + err := bundle.NewWriter(w).Write(mockBundles[name]) + if err != nil { + t.Fatal(err) + } + })) + + // setup plugin pointing at fake server + manager := getTestManagerWithOpts(fmt.Appendf(nil, `{ + "services": { + "default": { + "url": %q + } + } + }`, s.URL)) + + // setup manual trigger mode to simulate the downloader + var mode plugins.TriggerMode = "manual" + var delay int64 = 10 + polling := download.PollingConfig{MinDelaySeconds: &delay, MaxDelaySeconds: &delay} + serviceName := "default" + plugin := New(&Config{ + Bundles: map[string]*Source{ + "b1": { + Service: serviceName, + Config: download.Config{Trigger: &mode}, + Resource: "/b1", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + }, + }, + }, manager) + + statusCh := make(chan map[string]*Status) + + // register for bundle updates to observe changes + plugin.RegisterBulkListener("test-case", func(st map[string]*Status) { + statusCh <- st + }) + + ctx := context.Background() + err := plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + + // manually trigger bundle download + go func() { _ = plugin.Loaders()["b1"].Trigger(ctx) }() + <-statusCh + + // validate plugin started as expected + ensurePluginState(t, plugin, plugins.StateOK) + + // change the plugin state with multiple stages + stages := []struct { + name string + cfg *Config + noChangeDetected bool + }{ + { + name: "Add a bundle", // b1 is NOT Modified + cfg: &Config{ + Bundles: map[string]*Source{ + "b1": {Service: serviceName, Config: download.Config{Trigger: &mode}, Resource: "/b1", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + "b2": {Service: serviceName, Config: download.Config{Trigger: &mode}, Resource: "/b2", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + }, + }, + }, + { + name: "change download config", // both bundles are Not Modified + cfg: &Config{ + Bundles: map[string]*Source{ + "b1": {Service: serviceName, Config: download.Config{Trigger: &mode, Polling: polling}, Resource: "/b1", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + "b2": {Service: serviceName, Config: download.Config{Trigger: &mode, Polling: polling}, Resource: "/b2", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + }, + }, + }, + { + name: "pass the same config", // should be no change detected + cfg: &Config{ + Bundles: map[string]*Source{ + "b1": {Service: serviceName, Config: download.Config{Trigger: &mode, Polling: polling}, Resource: "/b1", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + "b2": {Service: serviceName, Config: download.Config{Trigger: &mode, Polling: polling}, Resource: "/b2", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + }, + }, + noChangeDetected: true, + }, + { + name: "revert download config for one bundle", // both bundles are Not Modified + cfg: &Config{ + Bundles: map[string]*Source{ + "b1": {Service: serviceName, Config: download.Config{Trigger: &mode}, Resource: "/b1", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + "b2": {Service: serviceName, Config: download.Config{Trigger: &mode, Polling: polling}, Resource: "/b2", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + }, + }, + }, + { + name: "remove a bundle", // b1 is Not Modified + cfg: &Config{ + Bundles: map[string]*Source{ + "b1": {Service: serviceName, Config: download.Config{Trigger: &mode}, Resource: "/b1", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + }, + }, + noChangeDetected: true, + }, + { + name: "change download config again", // b1 is Not Modified + cfg: &Config{ + Bundles: map[string]*Source{ + "b1": {Service: serviceName, Config: download.Config{Trigger: &mode, Polling: polling}, Resource: "/b1", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + }, + }, + }, + { + name: "add frequently changing bundle", + cfg: &Config{ + Bundles: map[string]*Source{ + "b1": {Service: serviceName, Config: download.Config{Trigger: &mode, Polling: polling}, Resource: "/b1", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + "b3_frequently_changing": {Service: serviceName, Config: download.Config{Trigger: &mode}, Resource: "/b3_frequently_changing", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + }, + }, + }, + { + name: "revert download config for Not Modified bundle", // b1 is Not Modified while b3_frequently_changing is modified + cfg: &Config{ + Bundles: map[string]*Source{ + "b1": {Service: serviceName, Config: download.Config{Trigger: &mode}, Resource: "/b2", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + "b3_frequently_changing": {Service: serviceName, Config: download.Config{Trigger: &mode}, Resource: "/b3_frequently_changing", SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes)}, + }, + }, + }, + } + + for _, stage := range stages { + t.Run(stage.name, func(t *testing.T) { + plugin.Reconfigure(ctx, stage.cfg) + + if stage.noChangeDetected { + ensurePluginState(t, plugin, plugins.StateOK) + return + } + + // if there is a change in config + // Reconfigure sets the plugin state as StateNotReady + ensurePluginState(t, plugin, plugins.StateNotReady) + + for name := range stage.cfg.Bundles { + go func(name string) { + _ = plugin.Loaders()[name].Trigger(ctx) + }(name) + <-statusCh + } + + // after all downloaders are processed the state should + // reconcile to StateOK, if there are no errors + ensurePluginState(t, plugin, plugins.StateOK) + }) + } +} + +func TestPluginManualTrigger(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + // setup fake http server with mock bundle + mockBundle := bundle.Bundle{ + Data: map[string]any{"p": "x1"}, + Modules: []bundle.ModuleFile{}, + } + + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + err := bundle.NewWriter(w).Write(mockBundle) + if err != nil { + t.Fatal(err) + } + })) + + // setup plugin pointing at fake server + manager := getTestManagerWithOpts(fmt.Appendf(nil, `{ + "services": { + "default": { + "url": %q + } + } + }`, s.URL)) + + var mode plugins.TriggerMode = "manual" + + plugin := New(&Config{ + Bundles: map[string]*Source{ + "test": { + Service: "default", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + Config: download.Config{Trigger: &mode}, + }, + }, + }, manager) + + statusCh := make(chan map[string]*Status) + + // register for bundle updates to observe changes and start the plugin + plugin.RegisterBulkListener("test-case", func(st map[string]*Status) { + statusCh <- st + }) + + err := plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + + // manually trigger bundle download + go func() { + _ = plugin.Loaders()["test"].Trigger(ctx) + }() + + // wait for bundle update and then assert on data content + <-statusCh + + result, err := storage.ReadOne(ctx, manager.Store, storage.Path{"p"}) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(result, mockBundle.Data["p"]) { + t.Fatalf("expected data to be %v but got %v", mockBundle.Data, result) + } + + // update data and trigger another bundle download + mockBundle.Data["p"] = "x2" + + // manually trigger bundle download + go func() { + _ = plugin.Loaders()["test"].Trigger(ctx) + }() + + // wait for bundle update and then assert on data content + <-statusCh + + result, err = storage.ReadOne(ctx, manager.Store, storage.Path{"p"}) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(result, mockBundle.Data["p"]) { + t.Fatalf("expected data to be %v but got %v", mockBundle.Data, result) + } +} + +func TestPluginManualTriggerMultipleDiskStorage(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + module := "package authz\n\ncorge=1" + + // setup fake http server with mock bundle + mockBundle1 := bundle.Bundle{ + Data: map[string]any{"p": "x1"}, + Modules: []bundle.ModuleFile{ + { + URL: "/bar/policy.rego", + Path: "/bar/policy.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + Manifest: bundle.Manifest{ + Roots: &[]string{"p", "authz"}, + }, + } + + s1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + err := bundle.NewWriter(w).Write(mockBundle1) + if err != nil { + t.Fatal(err) + } + })) + + mockBundle2 := bundle.Bundle{ + Data: map[string]any{"q": "x2"}, + Modules: []bundle.ModuleFile{}, + Manifest: bundle.Manifest{ + Roots: &[]string{"q"}, + }, + } + + s2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + err := bundle.NewWriter(w).Write(mockBundle2) + if err != nil { + t.Fatal(err) + } + })) + + test.WithTempFS(nil, func(dir string) { + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + }) + if err != nil { + t.Fatal(err) + } + config := fmt.Appendf(nil, `{ + "services": { + "default": { + "url": %q + }, + "acmecorp": { + "url": %q + } + } + }`, s1.URL, s2.URL) + + manager := getTestManagerWithOpts(config, store) + defer manager.Stop(ctx) + + var mode plugins.TriggerMode = "manual" + + plugin := New(&Config{ + Bundles: map[string]*Source{ + "test-1": { + Service: "default", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + Config: download.Config{Trigger: &mode}, + }, + "test-2": { + Service: "acmecorp", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + Config: download.Config{Trigger: &mode}, + }, + }, + }, manager) + + statusCh := make(chan map[string]*Status) + + // register for bundle updates to observe changes and start the plugin + plugin.RegisterBulkListener("test-case", func(st map[string]*Status) { + statusCh <- st + }) + + err = plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + + // manually trigger bundle download on all configured bundles + go func() { + _ = plugin.Trigger(ctx) + }() + + // wait for bundle update and then assert on data content + <-statusCh + <-statusCh + + result, err := storage.ReadOne(ctx, manager.Store, storage.Path{"p"}) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(result, mockBundle1.Data["p"]) { + t.Fatalf("expected data to be %v but got %v", mockBundle1.Data, result) + } + + result, err = storage.ReadOne(ctx, manager.Store, storage.Path{"q"}) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(result, mockBundle2.Data["q"]) { + t.Fatalf("expected data to be %v but got %v", mockBundle2.Data, result) + } + + txn := storage.NewTransactionOrDie(ctx, manager.Store) + defer manager.Store.Abort(ctx, txn) + + ids, err := manager.Store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 { + t.Fatal("Expected 1 policy") + } + + bs, err := manager.Store.GetPolicy(ctx, txn, ids[0]) + exp := []byte("package authz\n\ncorge=1") + if err != nil { + t.Fatal(err) + } else if !bytes.Equal(bs, exp) { + t.Fatalf("Bad policy content. Exp:\n%v\n\nGot:\n\n%v", string(exp), string(bs)) + } + + data, err := manager.Store.Read(ctx, txn, storage.Path{}) + expData := util.MustUnmarshalJSON([]byte(`{ + "p": "x1", "q": "x2", + "system": { + "bundles": {"test-1": {"etag": "", "manifest": {"revision": "", "roots": ["p", "authz"]}}, "test-2": {"etag": "", "manifest": {"revision": "", "roots": ["q"]}}} + } + }`)) + if err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(data, expData) { + t.Fatalf("Bad data content. Exp:\n%v\n\nGot:\n\n%v", expData, data) + } + }) +} + +func TestPluginManualTriggerMultiple(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + // setup fake http server with mock bundle + mockBundle1 := bundle.Bundle{ + Data: map[string]any{"p": "x1"}, + Modules: []bundle.ModuleFile{}, + Manifest: bundle.Manifest{ + Roots: &[]string{"p"}, + }, + } + + s1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + err := bundle.NewWriter(w).Write(mockBundle1) + if err != nil { + t.Fatal(err) + } + })) + + mockBundle2 := bundle.Bundle{ + Data: map[string]any{"q": "x2"}, + Modules: []bundle.ModuleFile{}, + Manifest: bundle.Manifest{ + Roots: &[]string{"q"}, + }, + } + + s2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + err := bundle.NewWriter(w).Write(mockBundle2) + if err != nil { + t.Fatal(err) + } + })) + + // setup plugin pointing at fake server + manager := getTestManagerWithOpts(fmt.Appendf(nil, `{ + "services": { + "default": { + "url": %q + }, + "acmecorp": { + "url": %q + } + } + }`, s1.URL, s2.URL)) + + var mode plugins.TriggerMode = "manual" + + plugin := New(&Config{ + Bundles: map[string]*Source{ + "test-1": { + Service: "default", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + Config: download.Config{Trigger: &mode}, + }, + "test-2": { + Service: "acmecorp", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + Config: download.Config{Trigger: &mode}, + }, + }, + }, manager) + + statusCh := make(chan map[string]*Status) + + // register for bundle updates to observe changes and start the plugin + plugin.RegisterBulkListener("test-case", func(st map[string]*Status) { + statusCh <- st + }) + + err := plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + + // manually trigger bundle download on all configured bundles + go func() { + _ = plugin.Trigger(ctx) + }() + + // wait for bundle update and then assert on data content + <-statusCh + <-statusCh + + result, err := storage.ReadOne(ctx, manager.Store, storage.Path{"p"}) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(result, mockBundle1.Data["p"]) { + t.Fatalf("expected data to be %v but got %v", mockBundle1.Data, result) + } + + result, err = storage.ReadOne(ctx, manager.Store, storage.Path{"q"}) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(result, mockBundle2.Data["q"]) { + t.Fatalf("expected data to be %v but got %v", mockBundle2.Data, result) + } +} + +func TestPluginManualTriggerWithTimeout(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + s := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) { + time.Sleep(3 * time.Second) // this should cause the context deadline to exceed + })) + + // setup plugin pointing at fake server + manager := getTestManagerWithOpts(fmt.Appendf(nil, `{ + "services": { + "default": { + "url": %q + } + } + }`, s.URL)) + + var mode plugins.TriggerMode = "manual" + + bundleName := "test" + plugin := New(&Config{ + Bundles: map[string]*Source{ + bundleName: { + Service: "default", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + Config: download.Config{Trigger: &mode}, + }, + }, + }, manager) + + statusCh := make(chan map[string]*Status) + + // register for bundle updates to observe changes and start the plugin + plugin.RegisterBulkListener("test-case", func(st map[string]*Status) { + statusCh <- st + }) + + err := plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + + // manually trigger bundle download + go func() { + _ = plugin.Loaders()[bundleName].Trigger(ctx) + }() + + // wait for bundle update + u := <-statusCh + + if u[bundleName].Code != errCode { + t.Fatalf("expected error code %v but got %v", errCode, u[bundleName].Code) + } + + if !strings.Contains(u[bundleName].Message, "context deadline exceeded") { + t.Fatalf("unexpected error message %v", u[bundleName].Message) + } +} + +func TestPluginManualTriggerWithServerError(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + s := httptest.NewServer(http.HandlerFunc(func(resp http.ResponseWriter, _ *http.Request) { + resp.WriteHeader(500) + })) + + // setup plugin pointing at fake server + manager := getTestManagerWithOpts(fmt.Appendf(nil, `{ + "services": { + "default": { + "url": %q + } + } + }`, s.URL)) + + var manual plugins.TriggerMode = "manual" + + plugin := New(&Config{ + Bundles: map[string]*Source{ + "test": { + Service: "default", + SizeLimitBytes: int64(bundle.DefaultSizeLimitBytes), + Config: download.Config{Trigger: &manual}, + }, + }, + }, manager) + + err := plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + // manually trigger bundle download + err = plugin.Trigger(ctx) + + plugin.Stop(ctx) + + var bundleErrors Errors + if errors.As(err, &bundleErrors) { + if len(bundleErrors) != 1 { + t.Fatalf("expected exactly one error, got %d", len(bundleErrors)) + } + for _, e := range bundleErrors { + if e.BundleName != "test" { + t.Fatalf("expected error for bundle 'test' but got '%s'", e.BundleName) + } + } + } else { + t.Fatalf("expected type of error to be %s but got %s", reflect.TypeOf(bundleErrors), reflect.TypeOf(err)) + } +} + +// Warning: This test modifies package variables, and as +// a result, cannot be run in parallel with other tests. +func TestGetNormalizedBundleName(t *testing.T) { + cases := []struct { + input string + goos string + exp string + }{ + { + input: "foo", + exp: "foo", + }, + { + input: "foo=bar", + exp: "foo=bar", + goos: "windows", + }, + { + input: "c:/foo", + exp: "c:/foo", + }, + { + input: "c:/foo", + exp: "c\\:\\/foo", + goos: "windows", + }, + { + input: "file:\"<>c:/a", + exp: "file\\:\\\"\\<\\>c\\:\\/a", + goos: "windows", + }, + { + input: "|a?b*c", + exp: "\\|a\\?b\\*c", + goos: "windows", + }, + { + input: "a?b=c", + exp: "a\\?b=c", + goos: "windows", + }, + } + + for _, tc := range cases { + t.Run(tc.input, func(t *testing.T) { + goos = tc.goos + actual := getNormalizedBundleName(tc.input) + if actual != tc.exp { + t.Fatalf("Want %v but got: %v", tc.exp, actual) + } + }) + } +} + +func TestBundleActivationWithRootOverlap(t *testing.T) { + ctx := context.Background() + plugin := getPluginWithExistingLoadedBundle( + t, + "policy-bundle", + []string{"foo/bar"}, + nil, + []testModule{ + { + Path: "foo/bar/bar.rego", + Data: `package foo.bar +result := true`, + }, + }, + ) + + bundleName := "new-bundle" + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + b := getTestBundleWithData( + []string{"foo/bar/baz"}, + []byte(`{"foo": {"bar": 1, "baz": "qux"}}`), + nil, + ) + + b.Manifest.Init() + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New(), Size: snapshotBundleSize}) + + // "foo/bar" and "foo/bar/baz" overlap with each other; activation will fail + status, ok := plugin.status[bundleName] + if !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } + if status.Code != errCode { + t.Fatalf("Expected status code to be %s, found %s", errCode, status.Code) + } + if exp := "detected overlapping roots"; !strings.Contains(status.Message, exp) { + t.Fatalf(`Expected status message to contain "%s", found %s`, exp, status.Message) + } +} + +func TestBundleActivationWithNoManifestRootsButWithPathConflict(t *testing.T) { + ctx := context.Background() + plugin := getPluginWithExistingLoadedBundle( + t, + "policy-bundle", + []string{"foo/bar"}, + nil, + []testModule{ + { + Path: "foo/bar/bar.rego", + Data: `package foo.bar +result := true`, + }, + }, + ) + + bundleName := "new-bundle" + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + b := getTestBundleWithData( + nil, + []byte(`{"foo": {"bar": 1, "baz": "qux"}}`), + nil, + ) + + b.Manifest.Init() + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New(), Size: snapshotBundleSize}) + + // new bundle has path "foo/bar" which overlaps with existing bundle with path "foo/bar"; activation will fail + status, ok := plugin.status[bundleName] + if !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } + if status.Code != errCode { + t.Fatalf("Expected status code to be %s, found %s", errCode, status.Code) + } + if !strings.Contains(status.Message, "specify empty root paths") { + t.Fatalf(`Expected status message to contain "specify empty root paths", found %s`, status.Message) + } +} + +func TestBundleActivationWithNoManifestRootsOverlap(t *testing.T) { + ctx := context.Background() + plugin := getPluginWithExistingLoadedBundle( + t, + "policy-bundle", + []string{"foo/bar"}, + nil, + []testModule{ + { + Path: "foo/bar/bar.rego", + Data: `package foo.bar +result := true`, + }, + }, + ) + + bundleName := "new-bundle" + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + b := getTestBundleWithData( + []string{"foo/baz"}, + nil, + []testModule{ + { + Path: "foo/bar/baz.rego", + Data: `package foo.baz +result := true`, + }, + }, + ) + + b.Manifest.Init() + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New(), Size: snapshotBundleSize}) + + status, ok := plugin.status[bundleName] + if !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } + if status.Code != "" { + t.Fatalf("Expected status code to be empty, found %s", status.Code) + } +} + +type testModule struct { + Path string + Data string +} + +func getTestBundleWithData(roots []string, data []byte, modules []testModule) bundle.Bundle { + b := bundle.Bundle{} + + if len(roots) > 0 { + b.Manifest = bundle.Manifest{Roots: &roots} + } + + if len(data) > 0 { + b.Data = util.MustUnmarshalJSON(data).(map[string]any) + } + + for _, m := range modules { + if len(m.Data) > 0 { + b.Modules = append(b.Modules, + bundle.ModuleFile{ + Path: m.Path, + Parsed: ast.MustParseModule(m.Data), + Raw: []byte(m.Data), + }, + ) + } + } + + b.Manifest.Init() + + return b +} + +func getPluginWithExistingLoadedBundle(t *testing.T, bundleName string, roots []string, data []byte, modules []testModule) *Plugin { + ctx := context.Background() + store := inmem.NewWithOpts(inmem.OptRoundTripOnWrite(false), inmem.OptReturnASTValuesOnRead(true)) + manager := getTestManagerWithOpts(nil, store) + plugin := New(&Config{}, manager) + plugin.status[bundleName] = &Status{Name: bundleName, Metrics: metrics.New()} + plugin.downloaders[bundleName] = download.New(download.Config{}, plugin.manager.Client(""), bundleName) + + ensurePluginState(t, plugin, plugins.StateNotReady) + + b := getTestBundleWithData(roots, data, modules) + + plugin.oneShot(ctx, bundleName, download.Update{Bundle: &b, Metrics: metrics.New(), Size: snapshotBundleSize}) + + ensurePluginState(t, plugin, plugins.StateOK) + + if status, ok := plugin.status[bundleName]; !ok { + t.Fatalf("Expected to find status for %s, found nil", bundleName) + } else if status.Type != bundle.SnapshotBundleType { + t.Fatalf("Expected snapshot bundle but got %v", status.Type) + } else if status.Size != snapshotBundleSize { + t.Fatalf("Expected snapshot bundle size %d but got %d", snapshotBundleSize, status.Size) + } + + return plugin +} + +func writeTestBundleToDisk(t *testing.T, srcDir string, signed bool) bundle.Bundle { + t.Helper() + + var b bundle.Bundle + + if signed { + b = getTestSignedBundle(t) + } else { + b = getTestBundle(t) + } + + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).UseModulePath(true).Write(b); err != nil { + t.Fatalf("unexpected error %v", err) + } + + if err := os.WriteFile(filepath.Join(srcDir, "bundle.tar.gz"), buf.Bytes(), 0644); err != nil { + t.Fatalf("unexpected error %v", err) + } + + return b +} + +func getTestBundle(t *testing.T) bundle.Bundle { + t.Helper() + + module := `package gork +import rego.v1 + + +p contains x if { x = 1 }` + + b := bundle.Bundle{ + Manifest: bundle.Manifest{ + Revision: "quickbrownfaux", + }, + Data: map[string]any{}, + Modules: []bundle.ModuleFile{ + { + Path: "/foo.rego", + URL: "/foo.rego", + Parsed: ast.MustParseModule(module), + Raw: []byte(module), + }, + }, + } + + b.Manifest.Init() + return b +} + +func getTestSignedBundle(t *testing.T) bundle.Bundle { + t.Helper() + + b := getTestBundle(t) + + if err := b.GenerateSignature(bundle.NewSigningConfig("secret", "HS256", ""), "foo", false); err != nil { + t.Fatal("Unexpected error:", err) + } + return b +} + +func getTestRawBundle(t *testing.T) io.Reader { + t.Helper() + + b := getTestBundle(t) + + var buf bytes.Buffer + if err := bundle.NewWriter(&buf).UseModulePath(true).Write(b); err != nil { + t.Fatal("unexpected error:", err) + } + + return &buf +} + +func validateStoreState(ctx context.Context, t *testing.T, store storage.Store, root string, expData any, expIDs []string, expBundleName string, expBundleRev string, expMetadata map[string]any) { + t.Helper() + if err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + value, err := store.Read(ctx, txn, storage.MustParsePath(root)) + if err != nil { + return err + } + + if expAst, ok := expData.(ast.Value); ok { + if ast.Compare(value, expAst) != 0 { + return fmt.Errorf("expected %v but got %v", expAst, value) + } + } else { + if !reflect.DeepEqual(value, expData) { + return fmt.Errorf("expected %v but got %v", expData, value) + } + } + + ids, err := store.ListPolicies(ctx, txn) + if err != nil { + return err + } + + sort.Strings(ids) + sort.Strings(expIDs) + + if !slices.Equal(ids, expIDs) { + return fmt.Errorf("expected ids %v but got %v", expIDs, ids) + } + + rev, err := bundle.ReadBundleRevisionFromStore(ctx, store, txn, expBundleName) + if err != nil { + return fmt.Errorf("unexpected error when reading bundle revision from store: %s", err) + } + + if rev != expBundleRev { + return fmt.Errorf("unexpected revision found on bundle: %s", rev) + } + + metadata, err := bundle.ReadBundleMetadataFromStore(ctx, store, txn, expBundleName) + if err != nil { + return fmt.Errorf("unexpected error when reading bundle metadata from store: %s", err) + } + if !reflect.DeepEqual(expMetadata, metadata) { + return fmt.Errorf("unexpected metadata found on bundle: %v", metadata) + } + + return nil + + }); err != nil { + t.Fatal(err) + } +} + +func ensurePluginState(t *testing.T, p *Plugin, state plugins.State) { + t.Helper() + status, ok := p.manager.PluginStatus()[Name] + if !ok { + t.Fatalf("Expected to find state for %s, found nil", Name) + return + } + if status.State != state { + t.Fatalf("Unexpected status state found in plugin manager for %s:\n\n\tFound:%+v\n\n\tExpected: %s", Name, status.State, state) + } +} diff --git a/third_party/opa/v1/plugins/bundle/status.go b/third_party/opa/v1/plugins/bundle/status.go new file mode 100644 index 000000000000..5d4006e78179 --- /dev/null +++ b/third_party/opa/v1/plugins/bundle/status.go @@ -0,0 +1,135 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "encoding/json" + "errors" + "reflect" + "strconv" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/download" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/server/types" +) + +const ( + errCode = "bundle_error" +) + +// Status represents the status of processing a bundle. +type Status struct { + Name string `json:"name"` + ActiveRevision string `json:"active_revision,omitempty"` + LastSuccessfulActivation time.Time `json:"last_successful_activation,omitempty"` + Type string `json:"type,omitempty"` + Size int `json:"size,omitempty"` + LastSuccessfulDownload time.Time `json:"last_successful_download,omitempty"` + LastSuccessfulRequest time.Time `json:"last_successful_request,omitempty"` + LastRequest time.Time `json:"last_request,omitempty"` + Code string `json:"code,omitempty"` + Message string `json:"message,omitempty"` + Errors []error `json:"errors,omitempty"` + Metrics metrics.Metrics `json:"metrics,omitempty"` + HTTPCode json.Number `json:"http_code,omitempty"` +} + +// SetActivateSuccess updates the status object to reflect a successful +// activation. +func (s *Status) SetActivateSuccess(revision string) { + s.LastSuccessfulActivation = time.Now().UTC() + s.ActiveRevision = revision +} + +// SetDownloadSuccess updates the status object to reflect a successful +// download. +func (s *Status) SetDownloadSuccess() { + s.LastSuccessfulDownload = time.Now().UTC() +} + +// SetRequest updates the status object to reflect a download attempt. +func (s *Status) SetRequest() { + s.LastRequest = time.Now().UTC() +} + +func (s *Status) SetBundleSize(size int) { + s.Size = size +} + +// SetError updates the status object to reflect a failure to download or +// activate. If err is nil, the error status is cleared. +func (s *Status) SetError(err error) { + var ( + astErrors ast.Errors + httpError download.HTTPError + ) + switch { + case err == nil: + s.Code = "" + s.HTTPCode = "" + s.Message = "" + s.Errors = nil + + case errors.As(err, &astErrors): + s.Code = errCode + s.HTTPCode = "" + s.Message = types.MsgCompileModuleError + s.Errors = make([]error, len(astErrors)) + for i := range astErrors { + s.Errors[i] = astErrors[i] + } + + case errors.As(err, &httpError): + s.Code = errCode + s.HTTPCode = json.Number(strconv.Itoa(httpError.StatusCode)) + s.Message = err.Error() + s.Errors = nil + + default: + s.Code = errCode + s.HTTPCode = "" + s.Message = err.Error() + s.Errors = nil + } +} + +func (s *Status) Equal(other *Status) bool { + if s == nil || other == nil { + return s == nil && other == nil + } + + equal := s.Name == other.Name && + s.Type == other.Type && + s.Size == other.Size && + s.Code == other.Code && + s.Message == other.Message && + s.HTTPCode == other.HTTPCode && + s.ActiveRevision == other.ActiveRevision && + s.LastSuccessfulActivation.Equal(other.LastSuccessfulActivation) && + s.LastSuccessfulDownload.Equal(other.LastSuccessfulDownload) && + s.LastSuccessfulRequest.Equal(other.LastSuccessfulRequest) && + s.LastRequest.Equal(other.LastRequest) + + if !equal { + return false + } + + if len(s.Errors) != len(other.Errors) { + return false + } + for i := range s.Errors { + if s.Errors[i].Error() != other.Errors[i].Error() { + return false + } + } + + if s.Metrics != nil && other.Metrics != nil && s.Metrics.All() != nil && other.Metrics.All() != nil { + return reflect.DeepEqual(s.Metrics.All(), other.Metrics.All()) + } + + return s.Metrics == nil && other.Metrics == nil +} diff --git a/third_party/opa/v1/plugins/discovery/config.go b/third_party/opa/v1/plugins/discovery/config.go new file mode 100644 index 000000000000..aeb3ded8bafa --- /dev/null +++ b/third_party/opa/v1/plugins/discovery/config.go @@ -0,0 +1,152 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package discovery + +import ( + "errors" + "fmt" + "maps" + "slices" + "strings" + + "github.com/open-policy-agent/opa/v1/keys" + + "github.com/open-policy-agent/opa/v1/bundle" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/download" + "github.com/open-policy-agent/opa/v1/util" +) + +// Config represents the configuration for the discovery feature. +type Config struct { + download.Config // bundle downloader configuration + Name *string `json:"name"` // Deprecated: name of the discovery bundle, use `Resource` instead. + Prefix *string `json:"prefix,omitempty"` // Deprecated: use `Resource` instead. + Decision *string `json:"decision"` // the name of the query to run on the bundle to get the config + Service string `json:"service"` // the name of the service used to download discovery bundle from + Resource *string `json:"resource,omitempty"` // the resource path which will be downloaded from the service + Signing *bundle.VerificationConfig `json:"signing,omitempty"` // configuration used to verify a signed bundle + Persist bool `json:"persist"` // control whether to persist activated discovery bundle to disk + + service string + path string + query string +} + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder struct { + raw []byte + services []string + keys map[string]*keys.Config +} + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the discovery config +func NewConfigBuilder() *ConfigBuilder { + return &ConfigBuilder{} +} + +// WithBytes sets the raw discovery config +func (b *ConfigBuilder) WithBytes(config []byte) *ConfigBuilder { + b.raw = config + return b +} + +// WithServices sets the services that implement control plane APIs +func (b *ConfigBuilder) WithServices(services []string) *ConfigBuilder { + b.services = services + return b +} + +// WithKeyConfigs sets the public keys to verify a signed bundle +func (b *ConfigBuilder) WithKeyConfigs(keys map[string]*keys.Config) *ConfigBuilder { + b.keys = keys + return b +} + +// Parse returns a valid Config object with defaults injected. +func (b *ConfigBuilder) Parse() (*Config, error) { + if b.raw == nil { + return nil, nil + } + + var result Config + + if err := util.Unmarshal(b.raw, &result); err != nil { + return nil, err + } + + return &result, result.validateAndInjectDefaults(b.services, b.keys) +} + +// ParseConfig returns a valid Config object with defaults injected. +func ParseConfig(bs []byte, services []string) (*Config, error) { + return NewConfigBuilder().WithBytes(bs).WithServices(services).Parse() +} + +func (c *Config) validateAndInjectDefaults(services []string, confKeys map[string]*keys.Config) error { + + if c.Resource == nil && c.Name == nil { + return errors.New("missing required discovery.resource field") + } + + // make a copy of the keys map + cpy := map[string]*keys.Config{} + maps.Copy(cpy, confKeys) + + if c.Signing != nil { + err := c.Signing.ValidateAndInjectDefaults(cpy) + if err != nil { + return fmt.Errorf("invalid configuration for discovery service: %s", err.Error()) + } + } else if len(confKeys) > 0 { + c.Signing = bundle.NewVerificationConfig(cpy, "", "", nil) + } + + if c.Resource != nil { + c.path = *c.Resource + } else { + if c.Prefix == nil { + s := defaultDiscoveryPathPrefix + c.Prefix = &s + } + + c.path = fmt.Sprintf("%v/%v", strings.Trim(*c.Prefix, "/"), strings.Trim(*c.Name, "/")) + } + + service, err := c.getServiceFromList(c.Service, services) + if err != nil { + return fmt.Errorf("invalid configuration for discovery service: %s", err.Error()) + } + + c.service = service + + if c.Decision != nil { + c.query = fmt.Sprintf("%v.%v", ast.DefaultRootDocument, strings.ReplaceAll(strings.Trim(*c.Decision, "/"), "/", ".")) + } else if c.Name != nil { + c.query = fmt.Sprintf("%v.%v", ast.DefaultRootDocument, strings.ReplaceAll(strings.Trim(*c.Name, "/"), "/", ".")) + } else { + c.query = ast.DefaultRootDocument.String() + } + + return c.Config.ValidateAndInjectDefaults() +} + +func (*Config) getServiceFromList(service string, services []string) (string, error) { + if service == "" { + if len(services) != 1 { + return "", errors.New("more than one service is defined") + } + return services[0], nil + } + if slices.Contains(services, service) { + return service, nil + } + return service, fmt.Errorf("service name %q not found", service) +} + +const ( + defaultDiscoveryPathPrefix = "bundles" +) diff --git a/third_party/opa/v1/plugins/discovery/config_test.go b/third_party/opa/v1/plugins/discovery/config_test.go new file mode 100644 index 000000000000..c06986bf6d9b --- /dev/null +++ b/third_party/opa/v1/plugins/discovery/config_test.go @@ -0,0 +1,185 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package discovery + +import ( + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/keys" +) + +func TestConfigValidation(t *testing.T) { + tests := []struct { + input string + services []string + wantErr bool + }{ + { + input: `{}`, + services: []string{"service1"}, + wantErr: true, + }, + { + input: `{"name": "a/b/c", "service": "service1"}`, + services: []string{"service2"}, + wantErr: true, + }, + { + input: `{"name": "a/b/c", "service": "service1"}`, + services: []string{"service1", "service2"}, + wantErr: false, + }, + { + input: `{"name": "a/b/c"}`, + services: []string{"service1", "service2"}, + wantErr: true, + }, + { + input: `{"name": "a/b/c"}`, + services: []string{}, + wantErr: true, + }, + { + input: `{"name": "a/b/c"}`, + services: []string{"service1"}, + wantErr: false, + }, + { + input: `{"name": "a/b/c", "prefix": "dummy", "decision": "query"}`, + services: []string{"service1"}, + wantErr: false, + }, + { + input: `{"name": "a/b/c", "decision": "query", "signing": {"keyid": "foo", "scope": "write"}}}`, + services: []string{"s1"}, + wantErr: false, + }, + { + input: `{"name": "a/b/c", "decision": "query", "signing": {"keyid": "bar", "scope": "write"}}}`, + services: []string{"s1"}, + wantErr: true, + }, + } + + keys := map[string]*keys.Config{"foo": {Key: "secret"}} + for i, test := range tests { + t.Run(fmt.Sprintf("TestConfigValidation_case_%d", i), func(t *testing.T) { + _, err := NewConfigBuilder().WithBytes([]byte(test.input)).WithServices(test.services).WithKeyConfigs(keys).Parse() + if err != nil && !test.wantErr { + t.Fatalf("unexpected error while parsing config: %s", err.Error()) + } + if err == nil && test.wantErr { + t.Fatal("expected error while parsing config, but got none") + } + }) + } +} + +func TestConfigDecision(t *testing.T) { + tests := []struct { + input string + decision string + }{ + { + input: `{"name": "a/b/c", "decision": "query"}`, + decision: "data.query", + }, + { + input: `{"name": "a/b/c"}`, + decision: "data.a.b.c", + }, + { + input: `{"resource": "discovery.tar.gz"}`, + decision: `data`, + }, + { + input: `{"resource": "discovery.tar.gz", "decision": "foo/bar"}`, + decision: "data.foo.bar", + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("TestConfigDecision_case_%d", i), func(t *testing.T) { + c, err := NewConfigBuilder().WithBytes([]byte(test.input)).WithServices([]string{"service1"}).Parse() + if err != nil { + t.Fatal("unexpected error while parsing config") + } + + if c.query != test.decision { + t.Fail() + } + }) + } +} + +func TestConfigService(t *testing.T) { + tests := []struct { + input string + services []string + service string + }{ + { + input: `{"name": "a/b/c"}`, + services: []string{"service1"}, + service: "service1", + }, + { + input: `{"name": "a/b/c", "service": "service1"}`, + services: []string{"service1", "service2"}, + service: "service1", + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("TestConfigService_case_%d", i), func(t *testing.T) { + c, err := NewConfigBuilder().WithBytes([]byte(test.input)).WithServices(test.services).Parse() + if err != nil { + t.Fatalf("unexpected error while parsing config: %s", err) + } + + if c.service != test.service { + t.Fail() + } + }) + } +} + +func TestConfigPath(t *testing.T) { + tests := []struct { + input string + path string + }{ + { + input: `{"name": "a/b/c", "prefix": "dummy"}`, + path: "dummy/a/b/c", + }, + { + input: `{"name": "a/b/c"}`, + path: "bundles/a/b/c", + }, + { + input: `{"name": "a/b/c/", "resource": "x/y/z"}`, + path: "x/y/z", + }, + { + input: `{"name": "a/b/c", "prefix": "/bundles2", resource: "x/y/z"}`, + path: "x/y/z", + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("TestConfigDecision_case_%d", i), func(t *testing.T) { + c, err := NewConfigBuilder().WithBytes([]byte(test.input)).WithServices([]string{"service1"}).Parse() + if err != nil { + t.Fatalf("unexpected error while parsing config: %s", err.Error()) + } + + if c.path != test.path { + t.Fail() + } + }) + } +} diff --git a/third_party/opa/v1/plugins/discovery/discovery.go b/third_party/opa/v1/plugins/discovery/discovery.go new file mode 100644 index 000000000000..15ea34d428c4 --- /dev/null +++ b/third_party/opa/v1/plugins/discovery/discovery.go @@ -0,0 +1,793 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package discovery implements configuration discovery. +package discovery + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "maps" + "os" + "path/filepath" + "reflect" + "strings" + "sync" + + bundleUtils "github.com/open-policy-agent/opa/internal/bundle" + cfg "github.com/open-policy-agent/opa/internal/config" + "github.com/open-policy-agent/opa/v1/ast" + bundleApi "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/config" + "github.com/open-policy-agent/opa/v1/download" + "github.com/open-policy-agent/opa/v1/hooks" + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/plugins/bundle" + "github.com/open-policy-agent/opa/v1/plugins/logs" + "github.com/open-policy-agent/opa/v1/plugins/status" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + // Name is the discovery plugin name that will be registered with the plugin manager. + Name = "discovery" + + // maxActivationRetry represents the maximum number of attempts + // to activate a persisted discovery bundle. The value chosen for + // maxActivationRetry ensures that too much time is not spent to activate + // a bundle that will never successfully activate. + maxActivationRetry = 10 +) + +// Discovery implements configuration discovery for OPA. When discovery is +// started it will periodically download a configuration bundle and try to +// reconfigure the OPA. +type Discovery struct { + manager *plugins.Manager + config *Config + factories map[string]plugins.Factory + downloader bundle.Loader // discovery bundle downloader + status *bundle.Status // discovery status + listenersMtx sync.Mutex // lock for listener map + listeners map[any]func(bundle.Status) // listeners for discovery update events + etag string // discovery bundle etag for caching purposes + metrics metrics.Metrics + readyOnce sync.Once + logger logging.Logger + bundlePersistPath string + hooks hooks.Hooks + bootConfig map[string]any + overriddenConfigKeys []string +} + +// Factories provides a set of factory functions to use for +// instantiating custom plugins. The passed map will be merged +// with what's already on the `Discovery` instance, overwriting +// existing keys on clashes. +func Factories(fs map[string]plugins.Factory) func(*Discovery) { + return func(d *Discovery) { + if d.factories == nil { + d.factories = make(map[string]plugins.Factory, len(fs)) + } + maps.Copy(d.factories, fs) + } +} + +// Metrics provides a metrics provider to pass to plugins. +func Metrics(m metrics.Metrics) func(*Discovery) { + return func(d *Discovery) { + d.metrics = m + } +} + +func Hooks(hs hooks.Hooks) func(*Discovery) { + return func(d *Discovery) { + d.hooks = hs + } +} + +func BootConfig(bootConfig map[string]any) func(*Discovery) { + return func(d *Discovery) { + d.bootConfig = bootConfig + } +} + +// New returns a new discovery plugin. +func New(manager *plugins.Manager, opts ...func(*Discovery)) (*Discovery, error) { + result := &Discovery{ + manager: manager, + } + + for _, f := range opts { + f(result) + } + + result.logger = manager.Logger().WithFields(map[string]any{"plugin": Name}) + + config, err := NewConfigBuilder().WithBytes(manager.Config.Discovery).WithServices(manager.Services()). + WithKeyConfigs(manager.PublicKeys()).Parse() + + if err != nil { + return nil, err + } else if config == nil { + if _, err := getPluginSet(result.factories, manager, manager.Config, result.metrics, result.logger, nil); err != nil { + return nil, err + } + return result, nil + } + + result.config = config + restClient := manager.Client(config.service) + if strings.ToLower(restClient.Config().Type) == "oci" { + ociStorePath := filepath.Join(os.TempDir(), "opa", "oci") // use temporary folder /tmp/opa/oci + if manager.Config.PersistenceDirectory != nil { + ociStorePath = filepath.Join(*manager.Config.PersistenceDirectory, "oci") + } + result.downloader = download.NewOCI(config.Config, restClient, config.path, ociStorePath). + WithCallback(result.oneShot). + WithBundleVerificationConfig(config.Signing). + WithBundlePersistence(config.Persist). + WithBundleParserOpts(manager.ParserOptions()) + } else { + d := download.New(config.Config, restClient, config.path). + WithCallback(result.oneShot). + WithBundleVerificationConfig(config.Signing). + WithBundlePersistence(config.Persist). + WithBundleParserOpts(manager.ParserOptions()) + result.downloader = d + } + result.status = &bundle.Status{ + Name: Name, + } + + result.logger = manager.Logger().WithFields(map[string]any{"plugin": Name}) + + manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateNotReady}) + return result, nil +} + +// Start starts the dynamic discovery process if configured. +func (c *Discovery) Start(ctx context.Context) error { + + bundlePersistPath, err := c.getBundlePersistPath() + if err != nil { + return err + } + c.bundlePersistPath = bundlePersistPath + + c.loadAndActivateBundleFromDisk(ctx) + + if c.downloader != nil { + c.downloader.Start(ctx) + } else { + // If there is no dynamic discovery then update the status to OK. + c.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateOK}) + } + return nil +} + +// Stop stops the dynamic discovery process if configured. +func (c *Discovery) Stop(ctx context.Context) { + if c.downloader != nil { + c.downloader.Stop(ctx) + } + + c.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateNotReady}) +} + +// Reconfigure is a no-op on discovery. +func (*Discovery) Reconfigure(context.Context, any) { +} + +// Lookup returns the discovery plugin registered with the manager. +func Lookup(manager *plugins.Manager) *Discovery { + if p := manager.Plugin(Name); p != nil { + return p.(*Discovery) + } + return nil +} + +func (c *Discovery) TriggerMode() *plugins.TriggerMode { + if c.config == nil { + return nil + } + return c.config.Trigger +} + +func (c *Discovery) Trigger(ctx context.Context) error { + if c.downloader == nil { + return nil + } + return c.downloader.Trigger(ctx) +} + +func (c *Discovery) RegisterListener(name any, f func(bundle.Status)) { + c.listenersMtx.Lock() + defer c.listenersMtx.Unlock() + + if c.listeners == nil { + c.listeners = map[any]func(bundle.Status){} + } + + c.listeners[name] = f +} + +// Unregister a listener to stop receiving status updates. +func (c *Discovery) Unregister(name any) { + c.listenersMtx.Lock() + defer c.listenersMtx.Unlock() + + delete(c.listeners, name) +} + +func (c *Discovery) getBundlePersistPath() (string, error) { + persistDir, err := c.manager.Config.GetPersistenceDirectory() + if err != nil { + return "", err + } + + return filepath.Join(persistDir, "bundles"), nil +} + +func (c *Discovery) loadAndActivateBundleFromDisk(ctx context.Context) { + + if c.config != nil && c.config.Persist { + b, err := c.loadBundleFromDisk() + if err != nil { + c.logger.Error("Failed to load discovery bundle from disk: %v", err) + c.status.SetError(err) + return + } + + if b == nil { + return + } + + for range maxActivationRetry { + + ps, err := c.processBundle(ctx, b) + if err != nil { + c.logger.Error("Discovery bundle processing error occurred: %v", err) + c.status.SetError(err) + continue + } + + for _, p := range ps.Start { + if err := p.Start(ctx); err != nil { + c.logger.Error("Failed to start configured plugins: %v", err) + c.status.SetError(err) + return + } + } + + for _, p := range ps.Reconfig { + p.Plugin.Reconfigure(ctx, p.Config) + } + + c.status.SetError(nil) + c.status.SetActivateSuccess(b.Manifest.Revision) + + // On the first activation success mark the plugin as being in OK state + c.readyOnce.Do(func() { + c.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateOK}) + }) + + c.logger.Debug("Discovery bundle loaded from disk and activated successfully.") + return + } + } +} + +func (c *Discovery) loadBundleFromDisk() (*bundleApi.Bundle, error) { + return bundleUtils.LoadBundleFromDiskForRegoVersion(c.manager.ParserOptions().RegoVersion, + c.bundlePersistPath, c.discoveryBundleDirName(), c.config.Signing) +} + +func (c *Discovery) saveBundleToDisk(raw io.Reader) error { + + bundleDir := filepath.Join(c.bundlePersistPath, c.discoveryBundleDirName()) + bundleFile := filepath.Join(bundleDir, "bundle.tar.gz") + + tmpFile, saveErr := saveCurrentBundleToDisk(bundleDir, raw) + if saveErr != nil { + c.logger.Error("Failed to save new discovery bundle to disk: %v", saveErr) + + if err := os.Remove(tmpFile); err != nil { + c.logger.Warn("Failed to remove temp file ('%s'): %v", tmpFile, err) + } + + if _, err := os.Stat(bundleFile); err == nil { + c.logger.Warn("Older version of activated discovery bundle persisted, ignoring error") + return nil + } + return saveErr + } + + return os.Rename(tmpFile, bundleFile) +} + +func saveCurrentBundleToDisk(path string, raw io.Reader) (string, error) { + return bundleUtils.SaveBundleToDisk(path, raw) +} + +func (c *Discovery) oneShot(ctx context.Context, u download.Update) { + + c.processUpdate(ctx, u) + + if p := status.Lookup(c.manager); p != nil { + p.UpdateDiscoveryStatus(*c.status) + } + + c.listenersMtx.Lock() + defer c.listenersMtx.Unlock() + + for _, f := range c.listeners { + f(*c.status) + } +} + +func (c *Discovery) processUpdate(ctx context.Context, u download.Update) { + c.status.SetRequest() + + if u.Error != nil { + c.logger.Error("Discovery download failed: %v", u.Error) + c.status.SetError(u.Error) + c.downloader.ClearCache() + return + } + + c.status.LastSuccessfulRequest = c.status.LastRequest + + if u.Bundle != nil { + c.status.Type = u.Bundle.Type() + c.status.LastSuccessfulDownload = c.status.LastSuccessfulRequest + c.status.SetBundleSize(u.Size) + + if err := c.reconfigure(ctx, u); err != nil { + c.logger.Error("Discovery reconfiguration error occurred: %v", err) + c.status.SetError(err) + c.downloader.ClearCache() + return + } + + if c.config != nil && c.config.Persist { + c.logger.Debug("Persisting discovery bundle to disk in progress.") + + err := c.saveBundleToDisk(u.Raw) + if err != nil { + c.logger.Error("Persisting discovery bundle to disk failed: %v", err) + c.status.SetError(err) + c.downloader.SetCache("") + return + } + c.logger.Debug("Discovery bundle persisted to disk successfully at path %v.", filepath.Join(c.bundlePersistPath, c.discoveryBundleDirName())) + } + + c.status.SetError(nil) + c.status.SetActivateSuccess(u.Bundle.Manifest.Revision) + + // include the local overrides in the status update + if len(c.overriddenConfigKeys) != 0 { + msg := fmt.Sprintf("Keys in the discovered configuration overridden by boot configuration: %v", strings.Join(c.overriddenConfigKeys, ", ")) + c.logger.Debug(msg) + c.status.Message = msg + } + c.overriddenConfigKeys = nil + + // On the first activation success mark the plugin as being in OK state + c.readyOnce.Do(func() { + c.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateOK}) + }) + + if u.ETag != "" { + c.logger.Info("Discovery update processed successfully. Etag updated to %v.", u.ETag) + } else { + c.logger.Info("Discovery update processed successfully.") + } + c.etag = u.ETag + return + } + + if u.ETag == c.etag { + c.logger.Debug("Discovery update skipped, server replied with not modified.") + c.status.SetError(nil) + return + } +} + +func (c *Discovery) reconfigure(ctx context.Context, u download.Update) error { + + ps, err := c.processBundle(ctx, u.Bundle) + if err != nil { + return err + } + + for _, p := range ps.Start { + if err := p.Start(ctx); err != nil { + return err + } + } + + for _, p := range ps.Reconfig { + p.Plugin.Reconfigure(ctx, p.Config) + } + + return nil +} + +func (c *Discovery) applyLocalPluginConfigOverride(conf *config.Config) (*config.Config, []string, error) { + raw, err := json.Marshal(conf) + if err != nil { + return nil, nil, err + } + + var newConfig map[string]any + err = util.Unmarshal(raw, &newConfig) + if err != nil { + return nil, nil, err + } + + _, overriddenKeys := mergeValuesAndListOverrides(newConfig, c.bootConfig, "") + + bs, err := json.Marshal(newConfig) + if err != nil { + return nil, nil, err + } + + parsedConf, err := config.ParseConfig(bs, c.manager.ID) + if err != nil { + return nil, nil, err + } + + return parsedConf, overriddenKeys, nil +} + +func (c *Discovery) processBundle(ctx context.Context, b *bundleApi.Bundle) (*pluginSet, error) { + + config, err := evaluateBundle(ctx, c.manager.ID, c.manager.Info, b, c.config.query) + if err != nil { + return nil, err + } + + c.hooks.Each(func(h hooks.Hook) { + if f, ok := h.(hooks.ConfigDiscoveryHook); ok { + if c, e := f.OnConfigDiscovery(ctx, config); e != nil { + err = errors.Join(err, e) + } else { + config = c + } + } + }) + if err != nil { + return nil, err + } + + // Note: We don't currently support changes to the discovery + // configuration. These changes are risky because errors would be + // unrecoverable (without keeping track of changes and rolling back...) + config.Discovery = c.manager.Config.Discovery + + // check for updates to the discovery service + opts := c.manager.DefaultServiceOpts(config) + opts.Logger = c.logger.WithFields(c.manager.Client(c.config.service).LoggerFields()) + + services, err := cfg.ParseServicesConfig(opts) + if err != nil { + return nil, err + } + + if client, ok := services[c.config.service]; ok { + dClient := c.manager.Client(c.config.service) + if !client.Config().Equal(dClient.Config()) { + return nil, errors.New("updates to the discovery service are not allowed") + } + } + + // check for updates to the keys provided in the boot config + keys, err := keys.ParseKeysConfig(config.Keys) + if err != nil { + return nil, err + } + + if c.config.Signing != nil { + for key, kc := range keys { + if curr, ok := c.config.Signing.PublicKeys[key]; ok { + if !curr.Equal(kc) { + return nil, errors.New("updates to keys specified in the boot configuration are not allowed") + } + } + } + } + + overriddenConfig, overriddenKeys, err := c.applyLocalPluginConfigOverride(config) + if err != nil { + return nil, err + } + + if err := c.manager.Reconfigure(overriddenConfig); err != nil { + return nil, err + } + + ps, err := getPluginSet(c.factories, c.manager, overriddenConfig, c.metrics, c.logger, c.config.Trigger) + if err != nil { + return nil, err + } + + c.overriddenConfigKeys = overriddenKeys + + return ps, nil +} + +// discoveryBundleDirName returns the name of the directory where the discovery bundle will be persisted. +// It wraps the deprecated config.Name and uses Name as a default. +func (c *Discovery) discoveryBundleDirName() string { + if c.config.Name != nil { + return *c.config.Name + } + return Name +} + +func evaluateBundle(ctx context.Context, id string, info *ast.Term, b *bundleApi.Bundle, query string) (*config.Config, error) { + + modules := b.ParsedModules("discovery") + + compiler := ast.NewCompiler() + + if regoVersion := b.RegoVersion(ast.DefaultRegoVersion); regoVersion != ast.RegoUndefined { + compiler = compiler.WithDefaultRegoVersion(regoVersion) + } + + if compiler.Compile(modules); compiler.Failed() { + return nil, compiler.Errors + } + + store := inmem.NewFromObjectWithOpts(b.Data, inmem.OptRoundTripOnWrite(false)) + + rego := rego.New( + rego.Query(query), + rego.Compiler(compiler), + rego.Store(store), + rego.Runtime(info), + ) + + rs, err := rego.Eval(ctx) + if err != nil { + return nil, err + } + + if len(rs) == 0 { + return nil, errors.New("undefined configuration") + } + + bs, err := json.Marshal(rs[0].Expressions[0].Value) + if err != nil { + return nil, err + } + + processedConf := cfg.SubEnvVars(string(bs)) + return config.ParseConfig([]byte(processedConf), id) +} + +type pluginSet struct { + Start []plugins.Plugin + Reconfig []pluginreconfig +} + +type pluginreconfig struct { + Config any + Plugin plugins.Plugin +} + +type pluginfactory struct { + name string + factory plugins.Factory + config any +} + +func getPluginSet(factories map[string]plugins.Factory, manager *plugins.Manager, config *config.Config, m metrics.Metrics, l logging.Logger, trigger *plugins.TriggerMode) (*pluginSet, error) { + + // Parse and validate plugin configurations. + pluginNames := []string{} + pluginFactories := []pluginfactory{} + + for k := range config.Plugins { + f, ok := factories[k] + if !ok { + return nil, fmt.Errorf("plugin %q not registered", k) + } + + c, err := f.Validate(manager, config.Plugins[k]) + if err != nil { + return nil, err + } + + pluginFactories = append(pluginFactories, pluginfactory{ + name: k, + factory: f, + config: c, + }) + + pluginNames = append(pluginNames, k) + } + + // Parse and validate bundle/logs/status configurations. + + // If `bundle` was configured use that, otherwise try the new `bundles` option + bundleConfig, err := bundle.ParseConfig(config.Bundle, manager.Services()) + if err != nil { + return nil, err + } + if bundleConfig == nil { + bundleConfig, err = bundle.NewConfigBuilder().WithBytes(config.Bundles).WithServices(manager.Services()). + WithKeyConfigs(manager.PublicKeys()).WithTriggerMode(trigger).Parse() + if err != nil { + return nil, err + } + } else { + manager.Logger().Warn("Deprecated 'bundle' configuration specified. Use 'bundles' instead. See https://www.openpolicyagent.org/docs/latest/configuration/#bundles") + } + + decisionLogsConfig, err := logs.NewConfigBuilder().WithBytes(config.DecisionLogs).WithServices(manager.Services()). + WithPlugins(pluginNames).WithTriggerMode(trigger).WithLogger(l).Parse() + if err != nil { + return nil, err + } + + statusConfig, err := status.NewConfigBuilder().WithBytes(config.Status).WithServices(manager.Services()). + WithPlugins(pluginNames).WithTriggerMode(trigger).Parse() + if err != nil { + return nil, err + } + + // Accumulate plugins to start or reconfigure. + starts := []plugins.Plugin{} + reconfigs := []pluginreconfig{} + + if bundleConfig != nil { + p, created := getBundlePlugin(manager, bundleConfig) + if created { + starts = append(starts, p) + } else if p != nil { + reconfigs = append(reconfigs, pluginreconfig{bundleConfig, p}) + } + } + + if decisionLogsConfig != nil { + p, created := getDecisionLogsPlugin(manager, decisionLogsConfig, m) + if created { + starts = append(starts, p) + } else if p != nil { + reconfigs = append(reconfigs, pluginreconfig{decisionLogsConfig, p}) + } + } + + if statusConfig != nil { + p, created := getStatusPlugin(manager, statusConfig, m) + if created { + starts = append(starts, p) + } else if p != nil { + reconfigs = append(reconfigs, pluginreconfig{statusConfig, p}) + } + } + + result := &pluginSet{starts, reconfigs} + + getCustomPlugins(manager, pluginFactories, result) + + return result, nil +} + +func getBundlePlugin(m *plugins.Manager, config *bundle.Config) (plugin *bundle.Plugin, created bool) { + plugin = bundle.Lookup(m) + if plugin == nil { + plugin = bundle.New(config, m) + m.Register(bundle.Name, plugin) + registerBundleStatusUpdates(m) + created = true + } + return plugin, created +} + +func getDecisionLogsPlugin(m *plugins.Manager, config *logs.Config, metrics metrics.Metrics) (plugin *logs.Plugin, created bool) { + plugin = logs.Lookup(m) + if plugin == nil { + plugin = logs.New(config, m).WithMetrics(metrics) + m.Register(logs.Name, plugin) + created = true + } + return plugin, created +} + +func getStatusPlugin(m *plugins.Manager, config *status.Config, metrics metrics.Metrics) (plugin *status.Plugin, created bool) { + + plugin = status.Lookup(m) + + if plugin == nil { + plugin = status.New(config, m).WithMetrics(metrics) + m.Register(status.Name, plugin) + registerBundleStatusUpdates(m) + created = true + } + + return plugin, created +} + +func getCustomPlugins(manager *plugins.Manager, factories []pluginfactory, result *pluginSet) { + for _, pf := range factories { + if plugin := manager.Plugin(pf.name); plugin != nil { + result.Reconfig = append(result.Reconfig, pluginreconfig{pf.config, plugin}) + } else { + plugin := pf.factory.New(manager, pf.config) + manager.Register(pf.name, plugin) + result.Start = append(result.Start, plugin) + } + } +} + +func registerBundleStatusUpdates(m *plugins.Manager) { + bp := bundle.Lookup(m) + sp := status.Lookup(m) + if bp == nil || sp == nil { + return + } + type pluginlistener string + + // Depending on how the plugin was configured we will want to use different listeners + // for backwards compatibility. + if !bp.Config().IsMultiBundle() { + bp.Register(pluginlistener(status.Name), sp.UpdateBundleStatus) + } else { + bp.RegisterBulkListener(pluginlistener(status.Name), sp.BulkUpdateBundleStatus) + } +} + +// mergeValuesAndListOverrides will merge source and destination map, preferring values from the source map. +// It will also return a list of keys in the destination map which were overridden by those in the source map +func mergeValuesAndListOverrides(dest map[string]any, src map[string]any, prefix string) (map[string]any, []string) { + overriddenKeys := []string{} + + for k, v := range src { + // If the key doesn't exist already, then just set the key to that value + if _, exists := dest[k]; !exists { + dest[k] = v + continue + } + + fullKey := k + if prefix != "" { + fullKey = fmt.Sprintf("%v.%v", prefix, k) + } + + nextMap, ok := v.(map[string]any) + // If it isn't another map, overwrite the value + if !ok { + if !reflect.DeepEqual(dest[k], v) { + overriddenKeys = append(overriddenKeys, fullKey) + } + dest[k] = v + continue + } + // Edge case: If the key exists in the destination, but isn't a map + destMap, isMap := dest[k].(map[string]any) + // If the source map has a map for this key, prefer it + if !isMap { + dest[k] = v + overriddenKeys = append(overriddenKeys, fullKey) + continue + } + // If we got to this point, it is a map in both, so merge them + merged, overridden := mergeValuesAndListOverrides(destMap, nextMap, fullKey) + dest[k] = merged + overriddenKeys = append(overriddenKeys, overridden...) + } + return dest, overriddenKeys +} diff --git a/third_party/opa/v1/plugins/discovery/discovery_test.go b/third_party/opa/v1/plugins/discovery/discovery_test.go new file mode 100644 index 000000000000..d150b5216ded --- /dev/null +++ b/third_party/opa/v1/plugins/discovery/discovery_test.go @@ -0,0 +1,4291 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: goconst // string duplication is for test readability. +package discovery + +import ( + "bytes" + "compress/gzip" + "context" + "encoding/json" + "errors" + "fmt" + "maps" + "net" + "net/http" + "net/http/httptest" + "os" + "path" + "path/filepath" + "reflect" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + bundleApi "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/download" + "github.com/open-policy-agent/opa/v1/logging/test" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + bundlePlugin "github.com/open-policy-agent/opa/v1/plugins/bundle" + "github.com/open-policy-agent/opa/v1/plugins/logs" + "github.com/open-policy-agent/opa/v1/plugins/status" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/server" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/version" +) + +const ( + snapshotBundleSize = 1024 +) + +func TestMain(m *testing.M) { + if version.Version == "" { + version.Version = "unit-test" + } + os.Exit(m.Run()) +} + +func TestEvaluateBundle(t *testing.T) { + + sampleModule := ` + package foo.bar + import rego.v1 + + bundle = { + "name": rt.name, + "service": "example" + } if { + rt := opa.runtime() + } + ` + + b := &bundleApi.Bundle{ + Manifest: bundleApi.Manifest{ + Revision: "quickbrownfaux", + }, + Data: map[string]any{ + "foo": map[string]any{ + "bar": map[string]any{ + "status": map[string]any{}, + }, + }, + }, + Modules: []bundleApi.ModuleFile{ + { + Path: `/example.rego`, + Raw: []byte(sampleModule), + Parsed: ast.MustParseModule(sampleModule), + }, + }, + } + + info := ast.MustParseTerm(`{"name": "test/bundle1"}`) + + config, err := evaluateBundle(context.Background(), "test-id", info, b, "data.foo.bar") + if err != nil { + t.Fatal(err) + } + + if config.Bundle == nil { + t.Fatal("Expected a bundle configuration") + } + + var parsedConfig bundlePlugin.Config + + if err := util.Unmarshal(config.Bundle, &parsedConfig); err != nil { + t.Fatal("Unexpected error:", err) + } + + expectedBundleConfig := bundlePlugin.Config{ + Name: "test/bundle1", + Service: "example", + } + + if !reflect.DeepEqual(expectedBundleConfig, parsedConfig) { + t.Fatalf("Expected bundle config %v, but got %v", expectedBundleConfig, parsedConfig) + } + +} + +func TestProcessBundle(t *testing.T) { + + ctx := context.Background() + + manager, err := plugins.New([]byte(`{ + "services": { + "default": { + "url": "http://localhost:8181" + } + }, + "discovery": {"name": "config"} + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + initialBundle := makeDataBundle(1, ` + { + "config": { + "bundle": {"name": "test1"}, + "status": {}, + "decision_logs": {} + } + } + `) + + disco, err := New(manager) + if err != nil { + t.Fatal(err) + } + + ps, err := disco.processBundle(ctx, initialBundle) + if err != nil { + t.Fatal(err) + } + + if len(ps.Start) != 3 || len(ps.Reconfig) != 0 { + t.Fatalf("Expected exactly three start events but got %v", ps) + } + + updatedBundle := makeDataBundle(1, ` + { + "config": { + "bundle": {"name": "test2"}, + "status": {"partition_name": "foo"}, + "decision_logs": {"partition_name": "bar"} + } + } + `) + + ps, err = disco.processBundle(ctx, updatedBundle) + if err != nil { + t.Fatal(err) + } + + if len(ps.Start) != 0 || len(ps.Reconfig) != 3 { + t.Fatalf("Expected exactly three start events but got %v", ps) + } + + updatedBundle = makeDataBundle(2, ` + { + "config": { + "bundle": {"service": "missing service name", "name": "test2"} + } + } + `) + + _, err = disco.processBundle(ctx, updatedBundle) + if err == nil { + t.Fatal("Expected error but got success") + } + +} + +func TestEnvVarSubstitution(t *testing.T) { + + ctx := context.Background() + + manager, err := plugins.New([]byte(`{ + "services": { + "default": { + "url": "http://localhost:8181" + } + }, + "discovery": {"name": "config"} + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + t.Setenv("ENV1", "test1") + initialBundle := makeDataBundle(1, ` + { + "config": { + "bundle": {"name": "${ENV1}"}, + "status": {}, + "decision_logs": {} + } + } + `) + + disco, err := New(manager) + if err != nil { + t.Fatal(err) + } + + ps, err := disco.processBundle(ctx, initialBundle) + if err != nil { + t.Fatal(err) + } + + if len(ps.Start) != 3 || len(ps.Reconfig) != 0 { + t.Fatalf("Expected exactly three start events but got %v", ps) + } + + actualConfig, err := manager.Config.ActiveConfig() + if err != nil { + t.Fatal(err) + } + assertConfig(t, actualConfig, fmt.Sprintf(`{ + "bundle": { + "name": "test1" + }, + "decision_logs": {}, + "default_authorization_decision": "/system/authz/allow", + "default_decision": "/system/main", + "discovery": { + "name": "config" + }, + "labels": { + "id": "test-id", + "version": %v + }, + "status": {} +}`, version.Version)) +} + +func TestProcessBundleV1Compatible(t *testing.T) { + ctx := context.Background() + popts := ast.ParserOptions{RegoVersion: ast.RegoV1} + + manager, err := plugins.New([]byte(`{ + "services": { + "default": { + "url": "http://localhost:8181" + } + }, + "discovery": {"name": "config"} + }`), "test-id", + inmem.New(), + plugins.WithParserOptions(popts)) + if err != nil { + t.Fatal(err) + } + + initialBundle := makeModuleBundle(1, `package config +bundle.name := "test1" +status := {} +decision_logs := {} if { 3 == 3 } +`, popts) + + disco, err := New(manager) + if err != nil { + t.Fatal(err) + } + + ps, err := disco.processBundle(ctx, initialBundle) + if err != nil { + t.Fatal(err) + } + + if len(ps.Start) != 3 || len(ps.Reconfig) != 0 { + t.Fatalf("Expected exactly three start events but got %v", ps) + } + + actualConfig, err := manager.Config.ActiveConfig() + if err != nil { + t.Fatal(err) + } + assertConfig(t, actualConfig, fmt.Sprintf(`{ + "bundle": { + "name": "test1" + }, + "decision_logs": {}, + "default_authorization_decision": "/system/authz/allow", + "default_decision": "/system/main", + "discovery": { + "name": "config" + }, + "labels": { + "id": "test-id", + "version": %v + }, + "status": {} +}`, version.Version)) + + // The bundle is parsed outside the discovery service, but is still compiled by it during processing. + // As such, it is impossible to pass it a module that doesn't pass the parsing step. + // We first pass it a valid v1.0 policy ... + updatedBundle := makeModuleBundle(1, `package config +bundle.name := "test2" if { 1 == 1 } +status.partition_name := "foo" if { 2 == 2 } +decision_logs.partition_name := "bar" if { 3 == 3 } +`, popts) + + ps, err = disco.processBundle(ctx, updatedBundle) + if err != nil { + t.Fatal(err) + } + + if len(ps.Start) != 0 || len(ps.Reconfig) != 3 { + t.Fatalf("Expected exactly three start events but got %v", ps) + } + + actualConfig, err = manager.Config.ActiveConfig() + if err != nil { + t.Fatal(err) + } + assertConfig(t, actualConfig, fmt.Sprintf(`{ + "bundle": { + "name": "test2" + }, + "decision_logs": { + "partition_name": "bar" + }, + "default_authorization_decision": "/system/authz/allow", + "default_decision": "/system/main", + "discovery": { + "name": "config" + }, + "labels": { + "id": "test-id", + "version": %v + }, + "status": { + "partition_name": "foo" + } +}`, version.Version)) + + // ... and then an invalid v1.0 policy, where we expect the compiler to complain about shadowed imports (which passes the parsing step). + updatedBundle = makeModuleBundle(1, `package config +import data.foo +import data.bar as foo + +bundle.name := "test2" if { 1 == 1 } +status.partition_name := "foo" if { 2 == 2 } +decision_logs.partition_name := "bar" if { 3 == 3 } +`, popts) + + _, err = disco.processBundle(ctx, updatedBundle) + if err == nil { + t.Fatal("Expected error but got none") + } + expErr := `rego_compile_error: import must not shadow import data.foo` + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error:\n\n%v\n\nbut got:\n\n%v", expErr, err) + } +} + +func TestProcessBundleWithActiveConfig(t *testing.T) { + + ctx := context.Background() + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999", + "credentials": {"bearer": {"token": "test"}} + } + }, + "keys": { + "local_key": { + "private_key": "local" + } + }, + "discovery": {"name": "config"}, + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + initialBundle := makeDataBundle(1, ` + { + "config": { + "services": { + "acmecorp": { + "url": "https://example.com/control-plane-api/v1", + "credentials": {"bearer": {"token": "test-acmecorp"}} + } + }, + "bundles": {"test-bundle": {"service": "localhost"}}, + "status": {"partition_name": "foo"}, + "decision_logs": {"partition_name": "bar"}, + "default_decision": "bar/baz", + "default_authorization_decision": "baz/qux", + "keys": { + "global_key": { + "scope": "read", + "key": "secret" + } + } + } + } + `) + + disco, err := New(manager) + if err != nil { + t.Fatal(err) + } + + _, err = disco.processBundle(ctx, initialBundle) + if err != nil { + t.Fatal(err) + } + + actual, err := manager.Config.ActiveConfig() + if err != nil { + t.Fatal(err) + } + + expectedConfig := fmt.Sprintf(`{ + "services": { + "acmecorp": { + "url": "https://example.com/control-plane-api/v1" + } + }, + "labels": { + "id": "test-id", + "version": %v, + "x": "y" + }, + "keys": { + "global_key": { + "scope": "read" + } + }, + "decision_logs": { + "partition_name": "bar" + }, + "status": { + "partition_name": "foo" + }, + "bundles": { + "test-bundle": { + "service": "localhost" + } + }, + "default_authorization_decision": "baz/qux", + "default_decision": "bar/baz", + "discovery": {"name": "config"} + }`, version.Version) + + assertConfig(t, actual, expectedConfig) + + initialBundle = makeDataBundle(2, ` + { + "config": { + "services": { + "opa.example.com": { + "url": "https://opa.example.com", + "credentials": {"bearer": {"token": "test-opa"}} + } + }, + "bundles": {"test-bundle-2": {"service": "opa.example.com"}}, + "decision_logs": {}, + "keys": { + "global_key_2": { + "scope": "write", + "key": "secret_2" + } + } + } + } + `) + + _, err = disco.processBundle(ctx, initialBundle) + if err != nil { + t.Fatal(err) + } + + actual, err = manager.Config.ActiveConfig() + if err != nil { + t.Fatal(err) + } + + expectedConfig2 := fmt.Sprintf(`{ + "services": { + "opa.example.com": { + "url": "https://opa.example.com" + } + }, + "labels": { + "id": "test-id", + "version": %v, + "x": "y" + }, + "keys": { + "global_key_2": { + "scope": "write" + } + }, + "decision_logs": {}, + "bundles": { + "test-bundle-2": { + "service": "opa.example.com" + } + }, + "default_authorization_decision": "/system/authz/allow", + "default_decision": "/system/main", + "discovery": {"name": "config"} + }`, version.Version) + + assertConfig(t, actual, expectedConfig2) +} + +func assertConfig(t *testing.T, actualConfig any, expectedConfig string) { + t.Helper() + + var expected map[string]any + if err := util.Unmarshal([]byte(expectedConfig), &expected); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(actualConfig, expected) { + t.Fatalf("expected config:\n\n%v\n\ngot:\n\n%v", expectedConfig, actualConfig) + } +} + +type testFactory struct { + p *reconfigureTestPlugin +} + +func (testFactory) Validate(*plugins.Manager, []byte) (any, error) { + return nil, nil +} + +func (f testFactory) New(*plugins.Manager, any) plugins.Plugin { + return f.p +} + +type reconfigureTestPlugin struct { + counts map[string]int +} + +func (r *reconfigureTestPlugin) Start(context.Context) error { + r.counts["start"]++ + return nil +} + +func (*reconfigureTestPlugin) Stop(context.Context) { +} + +func (r *reconfigureTestPlugin) Reconfigure(_ context.Context, _ any) { + r.counts["reconfig"]++ +} + +func TestStartWithBundlePersistence(t *testing.T) { + dir := t.TempDir() + + initialBundle := makeDataBundle(1, ` + { + "config": { + "labels": {"x": "label value changed"}, + "default_decision": "bar/baz", + "default_authorization_decision": "baz/qux", + "plugins": { + "test_plugin": {"a": "b"} + } + } + } + `) + + initialBundle.Manifest.Init() + + var buf bytes.Buffer + if err := bundleApi.NewWriter(&buf).Write(*initialBundle); err != nil { + t.Fatal("unexpected error:", err) + } + + bundleDir := filepath.Join(dir, "bundles", "config") + + err := os.MkdirAll(bundleDir, os.ModePerm) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + if err := os.WriteFile(filepath.Join(bundleDir, "bundle.tar.gz"), buf.Bytes(), 0644); err != nil { + t.Fatalf("unexpected error %v", err) + } + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config", "persist": true}, + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + manager.Config.PersistenceDirectory = &dir + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + err = disco.Start(context.Background()) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + ensurePluginState(t, disco, plugins.StateOK) + + // verify the test plugin was registered on the manager + if plugin := manager.Plugin("test_plugin"); plugin == nil { + t.Fatalf("expected \"test_plugin\" to be regsitered with the plugin manager") + } + + // verify the test plugin was started + count, ok := testPlugin.counts["start"] + if !ok { + t.Fatal("expected test plugin to have start counter") + } + + if count != 1 { + t.Fatalf("expected test plugin to have a start count of 1 but got %v", count) + } +} + +func TestOneShotWithBundlePersistence(t *testing.T) { + dir := t.TempDir() + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config", "persist": true}, + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + disco.bundlePersistPath = filepath.Join(dir, ".opa") + + ensurePluginState(t, disco, plugins.StateNotReady) + + // simulate a bundle download error with no bundle on disk + disco.oneShot(ctx, download.Update{Error: errors.New("unknown error")}) + + if disco.status.Message == "" { + t.Fatal("expected error but got none") + } + + ensurePluginState(t, disco, plugins.StateNotReady) + + // download a bundle and persist to disk. Then verify the bundle persisted to disk + initialBundle := makeDataBundle(1, ` + { + "config": { + "labels": {"x": "label value changed"}, + "default_decision": "bar/baz", + "default_authorization_decision": "baz/qux", + "plugins": { + "test_plugin": {"a": "b"} + } + } + } + `) + + initialBundle.Manifest.Init() + expBndl := initialBundle.Copy() + + var buf bytes.Buffer + if err := bundleApi.NewWriter(&buf).Write(*initialBundle); err != nil { + t.Fatal("unexpected error:", err) + } + + disco.oneShot(ctx, download.Update{Bundle: initialBundle, ETag: "etag-1", Raw: &buf}) + + ensurePluginState(t, disco, plugins.StateOK) + + result, err := disco.loadBundleFromDisk() + if err != nil { + t.Fatal("unexpected error:", err) + } + + if !result.Equal(expBndl) { + t.Fatalf("expected the downloaded bundle to be equal to the one loaded from disk: result=%v, exp=%v", result, expBndl) + } + + // verify the test plugin was registered on the manager + if plugin := manager.Plugin("test_plugin"); plugin == nil { + t.Fatalf("expected \"test_plugin\" to be regsitered with the plugin manager") + } + + // verify the test plugin was started + count, ok := testPlugin.counts["start"] + if !ok { + t.Fatal("expected test plugin to have start counter") + } + + if count != 1 { + t.Fatalf("expected test plugin to have a start count of 1 but got %v", count) + } +} + +func TestLoadAndActivateBundleFromDisk(t *testing.T) { + dir := t.TempDir() + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config", "persist": true}, + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + disco.bundlePersistPath = filepath.Join(dir, ".opa") + + ensurePluginState(t, disco, plugins.StateNotReady) + + // persist a bundle to disk and then load it + initialBundle := makeDataBundle(1, ` + { + "config": { + "labels": {"x": "label value changed"}, + "default_decision": "bar/baz", + "default_authorization_decision": "baz/qux", + "plugins": { + "test_plugin": {"a": "b"} + }, + "services": { + "acmecorp": { + "url": "http://localhost:8181" + } + }, + "bundles": { + "authz": { + "service": "acmecorp" + } + } + } + } + `) + + initialBundle.Manifest.Init() + + var buf bytes.Buffer + if err := bundleApi.NewWriter(&buf).Write(*initialBundle); err != nil { + t.Fatal("unexpected error:", err) + } + + err = disco.saveBundleToDisk(&buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + disco.loadAndActivateBundleFromDisk(ctx) + + ensurePluginState(t, disco, plugins.StateOK) + + // verify the test plugin was registered on the manager + if plugin := manager.Plugin("test_plugin"); plugin == nil { + t.Fatalf("expected \"test_plugin\" to be regsitered with the plugin manager") + } + + // verify the test plugin was started + count, ok := testPlugin.counts["start"] + if !ok { + t.Fatal("expected test plugin to have start counter") + } + + if count != 1 { + t.Fatalf("expected test plugin to have a start count of 1 but got %v", count) + } + + // verify the bundle plugin was registered on the manager + if plugin := bundlePlugin.Lookup(disco.manager); plugin == nil { + t.Fatalf("expected bundle plugin to be regsitered with the plugin manager") + } +} + +func TestLoadAndActivateSignedBundleFromDisk(t *testing.T) { + dir := t.TempDir() + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config", "persist": true}, + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + disco.bundlePersistPath = filepath.Join(dir, ".opa") + disco.config.Signing = bundleApi.NewVerificationConfig(map[string]*bundleApi.KeyConfig{"foo": {Key: "secret", Algorithm: "HS256"}}, "foo", "", nil) + + ensurePluginState(t, disco, plugins.StateNotReady) + + // persist a bundle to disk and then load it + initialBundle := makeDataBundle(1, ` + { + "config": { + "labels": {"x": "label value changed"}, + "default_decision": "bar/baz", + "default_authorization_decision": "baz/qux", + "plugins": { + "test_plugin": {"a": "b"} + }, + "services": { + "acmecorp": { + "url": "http://localhost:8181" + } + }, + "bundles": { + "authz": { + "service": "acmecorp" + } + } + } + } + `) + + initialBundle.Manifest.Init() + + if err := initialBundle.GenerateSignature(bundleApi.NewSigningConfig("secret", "HS256", ""), "foo", false); err != nil { + t.Fatal("Unexpected error:", err) + } + + var buf bytes.Buffer + if err := bundleApi.NewWriter(&buf).Write(*initialBundle); err != nil { + t.Fatal("unexpected error:", err) + } + + err = disco.saveBundleToDisk(&buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + disco.loadAndActivateBundleFromDisk(ctx) + + ensurePluginState(t, disco, plugins.StateOK) + + // verify the test plugin was registered on the manager + if plugin := manager.Plugin("test_plugin"); plugin == nil { + t.Fatalf("expected \"test_plugin\" to be regsitered with the plugin manager") + } + + // verify the test plugin was started + count, ok := testPlugin.counts["start"] + if !ok { + t.Fatal("expected test plugin to have start counter") + } + + if count != 1 { + t.Fatalf("expected test plugin to have a start count of 1 but got %v", count) + } + + // verify the bundle plugin was registered on the manager + if plugin := bundlePlugin.Lookup(disco.manager); plugin == nil { + t.Fatalf("expected bundle plugin to be regsitered with the plugin manager") + } +} + +func TestLoadAndActivateBundleFromDiskMaxAttempts(t *testing.T) { + dir := t.TempDir() + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config", "persist": true}, + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + disco.bundlePersistPath = filepath.Join(dir, ".opa") + + ensurePluginState(t, disco, plugins.StateNotReady) + + // persist a bundle to disk and then load it + // this bundle should never activate as the service discovery depends on is modified + initialBundle := makeDataBundle(1, ` + { + "config": { + "labels": {"x": "label value changed"}, + "default_decision": "bar/baz", + "default_authorization_decision": "baz/qux", + "plugins": { + "test_plugin": {"a": "b"} + }, + "services": { + "localhost": { + "url": "http://localhost:8181" + } + }, + "bundles": { + "authz": { + "service": "localhost" + } + } + } + } + `) + + initialBundle.Manifest.Init() + + var buf bytes.Buffer + if err := bundleApi.NewWriter(&buf).Write(*initialBundle); err != nil { + t.Fatal("unexpected error:", err) + } + + err = disco.saveBundleToDisk(&buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + disco.loadAndActivateBundleFromDisk(ctx) + + ensurePluginState(t, disco, plugins.StateNotReady) + + if len(manager.Plugins()) != 0 { + t.Fatal("expected no plugins to be registered with the plugin manager") + } +} + +func TestLoadAndActivateBundleFromDiskV1Compatible(t *testing.T) { + tests := []struct { + note string + v1Compatible bool + bundle string + }{ + { + note: "v0.x", + bundle: `package config +import future.keywords + +labels.x := "label value changed" +default_decision := "bar/baz" +default_authorization_decision := "baz/qux" +plugins.test_plugin := v if { + v := {"a": "b"} +} +services.acmecorp.url := v if { + v := "http://localhost:8181" +} +bundles.authz.service := v if { + v := "localhost" +} +`, + }, + { + note: "v1.0", + v1Compatible: true, + // no future.keywords import + bundle: `package config +labels.x := "label value changed" +default_decision := "bar/baz" +default_authorization_decision := "baz/qux" +plugins.test_plugin := v if { + v := {"a": "b"} +} +services.acmecorp.url := v if { + v := "http://localhost:8181" +} +bundles.authz.service := v if { + v := "localhost" +} +`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + regoVersion := ast.RegoV0 + if tc.v1Compatible { + regoVersion = ast.RegoV1 + } + popts := ast.ParserOptions{RegoVersion: regoVersion} + dir := t.TempDir() + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config", "persist": true}, + }`), "test-id", + inmem.New(), + plugins.WithParserOptions(popts)) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + disco.bundlePersistPath = filepath.Join(dir, ".opa") + + ensurePluginState(t, disco, plugins.StateNotReady) + + // persist a bundle to disk and then load it + initialBundle := makeModuleBundle(1, tc.bundle, popts) + + initialBundle.Manifest.Init() + + var buf bytes.Buffer + if err := bundleApi.NewWriter(&buf).Write(*initialBundle); err != nil { + t.Fatal("unexpected error:", err) + } + + err = disco.saveBundleToDisk(&buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + disco.loadAndActivateBundleFromDisk(ctx) + + ensurePluginState(t, disco, plugins.StateOK) + + // verify the test plugin was registered on the manager + if plugin := manager.Plugin("test_plugin"); plugin == nil { + t.Fatalf("expected \"test_plugin\" to be regsitered with the plugin manager") + } + + // verify the test plugin was started + count, ok := testPlugin.counts["start"] + if !ok { + t.Fatal("expected test plugin to have start counter") + } + + if count != 1 { + t.Fatalf("expected test plugin to have a start count of 1 but got %v", count) + } + + // verify the bundle plugin was registered on the manager + if plugin := bundlePlugin.Lookup(disco.manager); plugin == nil { + t.Fatalf("expected bundle plugin to be regsitered with the plugin manager") + } + }) + } +} + +func TestLoadAndActivateBundleFromDiskWithBundleRegoVersion(t *testing.T) { + tests := []struct { + note string + bundleRegoVersion int + modules map[string]versionedModule + }{ + { + note: "v0 bundle", + bundleRegoVersion: 0, + modules: map[string]versionedModule{ + "policy.rego": {-1, `package config + +labels.x := "label value changed" +default_decision := "bar/baz" +default_authorization_decision := "baz/qux" +plugins.test_plugin := v { + v := {"a": "b"} +} + +services.acmecorp.url := v { + v := "http://localhost:8181" +} + +bundles.authz.service := v { + v := "localhost" +}`}, + }, + }, + { + note: "v0 bundle, v1 per-file override", + bundleRegoVersion: 0, + modules: map[string]versionedModule{ + "policy1.rego": {-1, `package config + +labels.x := "label value changed" +default_decision := "bar/baz" +default_authorization_decision := "baz/qux" +plugins.test_plugin := v { + v := {"a": "b"} +}`}, + "policy2.rego": {1, `package config + +services.acmecorp.url := v if { + v := "http://localhost:8181" +} + +bundles.authz.service := v if { + v := "localhost" +}`}, + }, + }, + { + note: "v1 bundle", + bundleRegoVersion: 1, + // no future.keywords import + modules: map[string]versionedModule{ + "policy.rego": {-1, `package config +labels.x := "label value changed" +default_decision := "bar/baz" +default_authorization_decision := "baz/qux" +plugins.test_plugin := v if { + v := {"a": "b"} +} +services.acmecorp.url := v if { + v := "http://localhost:8181" +} +bundles.authz.service := v if { + v := "localhost" +}`}, + }, + }, + { + note: "v1 bundle, v0 per-file override", + bundleRegoVersion: 1, + modules: map[string]versionedModule{ + "policy1.rego": {0, `package config + +labels.x := "label value changed" +default_decision := "bar/baz" +default_authorization_decision := "baz/qux" +plugins.test_plugin := v { + v := {"a": "b"} +}`}, + "policy2.rego": {-1, `package config + +services.acmecorp.url := v if { + v := "http://localhost:8181" +} + +bundles.authz.service := v if { + v := "localhost" +}`}, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + dir := t.TempDir() + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config", "persist": true}, + }`), "test-id", + inmem.New()) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + disco.bundlePersistPath = filepath.Join(dir, ".opa") + + ensurePluginState(t, disco, plugins.StateNotReady) + + // persist a bundle to disk and then load it + initialBundle := makeBundleWithRegoVersion(1, tc.bundleRegoVersion, tc.modules) + + initialBundle.Manifest.Init() + + var buf bytes.Buffer + if err := bundleApi.NewWriter(&buf).Write(*initialBundle); err != nil { + t.Fatal("unexpected error:", err) + } + + err = disco.saveBundleToDisk(&buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + disco.loadAndActivateBundleFromDisk(ctx) + + ensurePluginState(t, disco, plugins.StateOK) + + // verify the test plugin was registered on the manager + if plugin := manager.Plugin("test_plugin"); plugin == nil { + t.Fatalf("expected \"test_plugin\" to be regsitered with the plugin manager") + } + + // verify the test plugin was started + count, ok := testPlugin.counts["start"] + if !ok { + t.Fatal("expected test plugin to have start counter") + } + + if count != 1 { + t.Fatalf("expected test plugin to have a start count of 1 but got %v", count) + } + + // verify the bundle plugin was registered on the manager + if plugin := bundlePlugin.Lookup(disco.manager); plugin == nil { + t.Fatalf("expected bundle plugin to be regsitered with the plugin manager") + } + }) + } +} + +func TestSaveBundleToDiskNew(t *testing.T) { + dir := t.TempDir() + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config", "persist": true}, + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + disco.bundlePersistPath = filepath.Join(dir, ".opa") + + initialBundle := makeDataBundle(1, ` + { + "config": { + "labels": {"x": "label value changed"}, + "default_decision": "bar/baz", + "default_authorization_decision": "baz/qux", + "plugins": { + "test_plugin": {"a": "b"} + } + } + } + `) + + initialBundle.Manifest.Init() + + var buf bytes.Buffer + if err := bundleApi.NewWriter(&buf).Write(*initialBundle); err != nil { + t.Fatal("unexpected error:", err) + } + + err = disco.saveBundleToDisk(&buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } +} + +func TestSaveBundleToDiskNewConfiguredPersistDir(t *testing.T) { + dir := t.TempDir() + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config", "persist": true}, + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + // configure persistence dir instead of using the default. Discover plugin should pick this up + manager.Config.PersistenceDirectory = &dir + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + err = disco.Start(context.Background()) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + initialBundle := makeDataBundle(1, ` + { + "config": { + "labels": {"x": "label value changed"}, + "default_decision": "bar/baz", + "default_authorization_decision": "baz/qux", + "plugins": { + "test_plugin": {"a": "b"} + } + } + } + `) + + initialBundle.Manifest.Init() + + var buf bytes.Buffer + if err := bundleApi.NewWriter(&buf).Write(*initialBundle); err != nil { + t.Fatal("unexpected error:", err) + } + + err = disco.saveBundleToDisk(&buf) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + expectBundlePath := filepath.Join(dir, "bundles", "config", "bundle.tar.gz") + _, err = os.Stat(expectBundlePath) + if err != nil { + t.Errorf("expected bundle persisted at path %v, %v", expectBundlePath, err) + } +} + +func TestReconfigure(t *testing.T) { + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config"}, + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + initialBundle := makeDataBundle(1, ` + { + "config": { + "labels": {"x": "label value changed", "y": "new label"}, + "default_decision": "bar/baz", + "default_authorization_decision": "baz/qux", + "plugins": { + "test_plugin": {"a": "b"} + } + } + } + `) + + disco.oneShot(ctx, download.Update{Bundle: initialBundle, Size: snapshotBundleSize}) + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } else if disco.status.Type != bundleApi.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", disco.status.Type) + } else if disco.status.Size != snapshotBundleSize { + t.Fatalf("expected snapshot bundle size %d but got %d", snapshotBundleSize, disco.status.Size) + } + + // Verify labels are unchanged but allow additions + exp := map[string]string{"x": "y", "y": "new label", "id": "test-id", "version": version.Version} + if !maps.Equal(manager.Labels(), exp) { + t.Errorf("Expected labels to be unchanged (%v) but got %v", exp, manager.Labels()) + } + + // Verify decision ids set + expDecision := ast.MustParseTerm("data.bar.baz") + expAuthzDecision := ast.MustParseTerm("data.baz.qux") + if !manager.Config.DefaultDecisionRef().Equal(expDecision.Value) { + t.Errorf("Expected default decision to be %v but got %v", expDecision, manager.Config.DefaultDecisionRef()) + } + if !manager.Config.DefaultAuthorizationDecisionRef().Equal(expAuthzDecision.Value) { + t.Errorf("Expected default authz decision to be %v but got %v", expAuthzDecision, manager.Config.DefaultAuthorizationDecisionRef()) + } + + // Verify plugins started + if !maps.Equal(testPlugin.counts, map[string]int{"start": 1}) { + t.Errorf("Expected exactly one plugin start but got %v", testPlugin) + } + + // Verify plugins reconfigured + updatedBundle := makeDataBundle(2, ` + { + "config": { + "labels": {"x": "label value changed", "z": "another added label" }, + "default_decision": "bar/baz", + "default_authorization_decision": "baz/qux", + "plugins": { + "test_plugin": {"a": "plugin parameter value changed"} + } + } + } + `) + + disco.oneShot(ctx, download.Update{Bundle: updatedBundle}) + + // Verify label additions are always on top of bootstrap config with multiple discovery documents + exp = map[string]string{"x": "y", "z": "another added label", "id": "test-id", "version": version.Version} + if !maps.Equal(manager.Labels(), exp) { + t.Errorf("Expected labels to be unchanged (%v) but got %v", exp, manager.Labels()) + } + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } else if disco.status.Type != bundleApi.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", disco.status.Type) + } + + if !maps.Equal(testPlugin.counts, map[string]int{"start": 1, "reconfig": 1}) { + t.Errorf("Expected one plugin start and one reconfig but got %v", testPlugin) + } +} + +func TestReconfigureV1Compatible(t *testing.T) { + popts := ast.ParserOptions{RegoVersion: ast.RegoV1} + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config"}, + }`), "test-id", + inmem.New(), + plugins.WithParserOptions(popts)) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + initialBundle := makeModuleBundle(1, `package config +labels := v if { + v := {"x": "label value changed", "y": "new label"} +} +default_decision := "bar/baz" +default_authorization_decision := "baz/qux" +plugins.test_plugin := v if { + v := {"a": "b"} +}`, popts) + + disco.oneShot(ctx, download.Update{Bundle: initialBundle, Size: snapshotBundleSize}) + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } else if disco.status.Type != bundleApi.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", disco.status.Type) + } else if disco.status.Size != snapshotBundleSize { + t.Fatalf("expected snapshot bundle size %d but got %d", snapshotBundleSize, disco.status.Size) + } + + // Verify labels are unchanged but allow additions + exp := map[string]string{"x": "y", "y": "new label", "id": "test-id", "version": version.Version} + if !maps.Equal(manager.Labels(), exp) { + t.Errorf("Expected labels to be unchanged (%v) but got %v", exp, manager.Labels()) + } + + // Verify decision ids set + expDecision := ast.MustParseTerm("data.bar.baz") + expAuthzDecision := ast.MustParseTerm("data.baz.qux") + if !manager.Config.DefaultDecisionRef().Equal(expDecision.Value) { + t.Errorf("Expected default decision to be %v but got %v", expDecision, manager.Config.DefaultDecisionRef()) + } + if !manager.Config.DefaultAuthorizationDecisionRef().Equal(expAuthzDecision.Value) { + t.Errorf("Expected default authz decision to be %v but got %v", expAuthzDecision, manager.Config.DefaultAuthorizationDecisionRef()) + } + + // Verify plugins started + if !maps.Equal(testPlugin.counts, map[string]int{"start": 1}) { + t.Errorf("Expected exactly one plugin start but got %v", testPlugin) + } + + // Verify plugins reconfigured + updatedBundle := makeModuleBundle(2, `package config +labels := v if { + v := {"x": "label value changed", "z": "another added label"} +} +default_decision := "bar/baz" +default_authorization_decision := "baz/qux" +plugins.test_plugin := v if { + v := {"a": "plugin parameter value changed"} +}`, popts) + + disco.oneShot(ctx, download.Update{Bundle: updatedBundle}) + + // Verify label additions are always on top of bootstrap config with multiple discovery documents + exp = map[string]string{"x": "y", "z": "another added label", "id": "test-id", "version": version.Version} + if !maps.Equal(manager.Labels(), exp) { + t.Errorf("Expected labels to be unchanged (%v) but got %v", exp, manager.Labels()) + } + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } else if disco.status.Type != bundleApi.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", disco.status.Type) + } + + if !maps.Equal(testPlugin.counts, map[string]int{"start": 1, "reconfig": 1}) { + t.Errorf("Expected one plugin start and one reconfig but got %v", testPlugin) + } + + regoV0Bundle := makeModuleBundleWithRegoVersion(2, `package config +labels := v { + v := {"a": "zero"} +} +default_decision := "bar/baz" +default_authorization_decision := "baz/qux"`, 0) + + disco.oneShot(ctx, download.Update{Bundle: regoV0Bundle}) + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } else if disco.status.Type != bundleApi.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", disco.status.Type) + } + + expLabel := "zero" + actLabel := manager.Labels()["a"] + if actLabel != expLabel { + t.Errorf(`Expected label "a" to be: %v, got: %v`, expLabel, actLabel) + } + + regoV1Bundle := makeModuleBundleWithRegoVersion(2, `package config +labels := v if { + v := {"a": "one"} +} +default_decision := "bar/baz" +default_authorization_decision := "baz/qux"`, 1) + + disco.oneShot(ctx, download.Update{Bundle: regoV1Bundle}) + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } else if disco.status.Type != bundleApi.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", disco.status.Type) + } + + expLabel = "one" + actLabel = manager.Labels()["a"] + if actLabel != expLabel { + t.Errorf(`Expected label "a" to be: %v, got: %v`, expLabel, actLabel) + } +} + +func TestReconfigureWithBundleRegoVersion(t *testing.T) { + popts := ast.ParserOptions{RegoVersion: ast.RegoV1} + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config"}, + }`), "test-id", + inmem.New(), + plugins.WithParserOptions(popts)) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + initialBundle := makeModuleBundleWithRegoVersion(1, `package config +labels := v if { + v := {"x": "label value changed", "y": "new label"} +} +default_decision := "bar/baz" +default_authorization_decision := "baz/qux" +plugins.test_plugin := v if { + v := {"a": "b"} +}`, 1) + + disco.oneShot(ctx, download.Update{Bundle: initialBundle, Size: snapshotBundleSize}) + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } else if disco.status.Type != bundleApi.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", disco.status.Type) + } else if disco.status.Size != snapshotBundleSize { + t.Fatalf("expected snapshot bundle size %d but got %d", snapshotBundleSize, disco.status.Size) + } + + // Verify labels are unchanged but allow additions + exp := map[string]string{"x": "y", "y": "new label", "id": "test-id", "version": version.Version} + if !maps.Equal(manager.Labels(), exp) { + t.Errorf("Expected labels to be unchanged (%v) but got %v", exp, manager.Labels()) + } + + // Verify decision ids set + expDecision := ast.MustParseTerm("data.bar.baz") + expAuthzDecision := ast.MustParseTerm("data.baz.qux") + if !manager.Config.DefaultDecisionRef().Equal(expDecision.Value) { + t.Errorf("Expected default decision to be %v but got %v", expDecision, manager.Config.DefaultDecisionRef()) + } + if !manager.Config.DefaultAuthorizationDecisionRef().Equal(expAuthzDecision.Value) { + t.Errorf("Expected default authz decision to be %v but got %v", expAuthzDecision, manager.Config.DefaultAuthorizationDecisionRef()) + } + + // Verify plugins started + if !maps.Equal(testPlugin.counts, map[string]int{"start": 1}) { + t.Errorf("Expected exactly one plugin start but got %v", testPlugin) + } + + // Verify plugins reconfigured + updatedBundle := makeModuleBundleWithRegoVersion(2, `package config +labels := v if { + v := {"x": "label value changed", "z": "another added label"} +} +default_decision := "bar/baz" +default_authorization_decision := "baz/qux" +plugins.test_plugin := v if { + v := {"a": "plugin parameter value changed"} +}`, 1) + + disco.oneShot(ctx, download.Update{Bundle: updatedBundle}) + + // Verify label additions are always on top of bootstrap config with multiple discovery documents + exp = map[string]string{"x": "y", "z": "another added label", "id": "test-id", "version": version.Version} + if !maps.Equal(manager.Labels(), exp) { + t.Errorf("Expected labels to be unchanged (%v) but got %v", exp, manager.Labels()) + } + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } else if disco.status.Type != bundleApi.SnapshotBundleType { + t.Fatalf("expected snapshot bundle but got %v", disco.status.Type) + } + + if !maps.Equal(testPlugin.counts, map[string]int{"start": 1, "reconfig": 1}) { + t.Errorf("Expected one plugin start and one reconfig but got %v", testPlugin) + } +} + +func TestReconfigureWithLocalOverride(t *testing.T) { + ctx := context.Background() + + bootConfigRaw := []byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config"}, + "default_decision": "/http/example/authz/allow", + "keys": { + "local_key": { + "key": "some_private_key", + "scope": "write" + } + }, + "decision_logs": {"console": true}, + "nd_builtin_cache": false, + "distributed_tracing": {"type": "grpc"}, + "caching": { + "inter_query_builtin_cache": {"max_size_bytes": 10000000, "forced_eviction_threshold_percentage": 90}, + "inter_query_builtin_value_cache": { + "named": { + "io_jwt": {"max_num_entries": 55}, + "graphql": {"max_num_entries": 10} + } + } + } + }`) + + manager, err := plugins.New(bootConfigRaw, "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + var bootConfig map[string]any + err = util.Unmarshal(bootConfigRaw, &bootConfig) + if err != nil { + t.Fatal(err) + } + + disco, err := New(manager, BootConfig(bootConfig)) + if err != nil { + t.Fatal(err) + } + + // new label added in service config and boot config overrides existing label + serviceBundle := makeDataBundle(1, ` + { + "config": { + "labels": {"x": "new_value", "y": "new label"} + } + } + `) + + disco.oneShot(ctx, download.Update{Bundle: serviceBundle}) + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } else if !strings.Contains(disco.status.Message, "labels.x") { + t.Fatal("expected key \"labels.x\" to be overridden") + } + + exp := map[string]string{"x": "y", "y": "new label", "id": "test-id", "version": version.Version} + if !maps.Equal(manager.Labels(), exp) { + t.Errorf("Expected labels (%v) but got %v", exp, manager.Labels()) + } + + // `default_authorization_decision` is not specified in the boot config. Hence, it will get a default value. + // We're specifying it in the service config so, it should take precedence. + serviceBundle = makeDataBundle(2, ` + { + "config": { + "default_authorization_decision": "/http/example/system/allow" + } + } + `) + + disco.oneShot(ctx, download.Update{Bundle: serviceBundle}) + + expAuthzRule := "/http/example/system/allow" + if *manager.Config.DefaultAuthorizationDecision != expAuthzRule { + t.Errorf("Expected default authorization decision %v but got %v", expAuthzRule, *manager.Config.DefaultAuthorizationDecision) + } + + // `default_decision` is specified in both boot and service config. The former should take precedence. + serviceBundle = makeDataBundle(3, ` + { + "config": { + "default_decision": "/http/example/authz/allow/something" + } + } + `) + + disco.oneShot(ctx, download.Update{Bundle: serviceBundle}) + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } else if !strings.Contains(disco.status.Message, "default_decision") { + t.Fatal("expected key \"default_decision\" to be overridden") + } + + expAuthzRule = "/http/example/authz/allow" + if *manager.Config.DefaultDecision != expAuthzRule { + t.Fatalf("Expected default decision %v but got %v", expAuthzRule, *manager.Config.DefaultDecision) + } + + // `nd_builtin_cache` is specified in both boot and service config. The former should take precedence. + serviceBundle = makeDataBundle(4, ` + { + "config": { + "nd_builtin_cache": true + } + } + `) + + disco.oneShot(ctx, download.Update{Bundle: serviceBundle}) + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } else if !strings.Contains(disco.status.Message, "nd_builtin_cache") { + t.Fatal("expected key \"nd_builtin_cache\" to be overridden") + } + + if manager.Config.NDBuiltinCache { + t.Fatal("Expected nd_builtin_cache value to be false") + } + + // `persistence_directory` not specified in boot config. The service config value should be used. + serviceBundle = makeDataBundle(5, ` + { + "config": { + "persistence_directory": "test" + } + } + `) + + disco.oneShot(ctx, download.Update{Bundle: serviceBundle}) + + if manager.Config.PersistenceDirectory == nil || *manager.Config.PersistenceDirectory != "test" { + t.Fatal("Unexpected update to persistence directory") + } + + // nested: overriding a value in an existing config + serviceBundle = makeDataBundle(6, ` + { + "config": { + "decision_logs": {"console": false, "reporting": {"max_delay_seconds": 15, "min_delay_seconds": 10}} + } + } + `) + + disco.oneShot(ctx, download.Update{Bundle: serviceBundle}) + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } else if !strings.Contains(disco.status.Message, "decision_logs.console") { + t.Fatal("expected key \"decision_logs.console\" to be overridden") + } + + dlPlugin := logs.Lookup(disco.manager) + config := dlPlugin.Config() + + actualMax := time.Duration(*config.Reporting.MaxDelaySeconds) / time.Nanosecond + expectedMax := time.Duration(15) * time.Second + + if actualMax != expectedMax { + t.Fatalf("Expected maximum polling interval: %v but got %v", expectedMax, actualMax) + } + + actualMin := time.Duration(*config.Reporting.MinDelaySeconds) / time.Nanosecond + expectedMin := time.Duration(10) * time.Second + + if actualMin != expectedMin { + t.Fatalf("Expected maximum polling interval: %v but got %v", expectedMin, actualMin) + } + + if !config.ConsoleLogs { + t.Fatal("Expected console decision logging to be enabled") + } + + // nested: adding a value in an existing config + // only `stale_entry_eviction_period_seconds` should be used from the service config as the boot config defines + // the other fields + serviceBundle = makeDataBundle(7, ` + { + "config": { + "caching": { + "inter_query_builtin_cache": {"max_size_bytes": 200, "stale_entry_eviction_period_seconds": 10, "forced_eviction_threshold_percentage": 200}, + "inter_query_builtin_value_cache": { + "named": { + "io_jwt": {"max_num_entries": 10}, + "graphql": {"max_num_entries": 11} + } + } + } + } + } + `) + + disco.oneShot(ctx, download.Update{Bundle: serviceBundle}) + + if disco.status == nil { + t.Fatal("Expected to find status, found nil") + } + + expectedOverriddenKeys := []string{ + "caching.inter_query_builtin_cache.max_size_bytes", + "caching.inter_query_builtin_cache.forced_eviction_threshold_percentage", + "caching.inter_query_builtin_value_cache.named.io_jwt.max_num_entries", + "caching.inter_query_builtin_value_cache.named.graphql.max_num_entries", + } + for _, k := range expectedOverriddenKeys { + if !strings.Contains(disco.status.Message, k) { + t.Fatalf("expected key \"%v\" to be overridden", k) + } + } + + cacheConf, err := cache.ParseCachingConfig(manager.Config.Caching) + if err != nil { + t.Fatal(err) + } + + maxSize := new(int64) + *maxSize = 10000000 + period := new(int64) + *period = 10 + threshold := new(int64) + *threshold = 90 + maxNumEntriesInterQueryValueCache := new(int) + *maxNumEntriesInterQueryValueCache = 0 + maxNumEntriesJWTValueCache := new(int) + *maxNumEntriesJWTValueCache = 55 + maxNumEntriesGraphQLValueCache := new(int) + *maxNumEntriesGraphQLValueCache = 10 + + expectedCacheConf := &cache.Config{ + InterQueryBuiltinCache: cache.InterQueryBuiltinCacheConfig{ + MaxSizeBytes: maxSize, + StaleEntryEvictionPeriodSeconds: period, + ForcedEvictionThresholdPercentage: threshold, + }, + InterQueryBuiltinValueCache: cache.InterQueryBuiltinValueCacheConfig{ + MaxNumEntries: maxNumEntriesInterQueryValueCache, + NamedCacheConfigs: map[string]*cache.NamedValueCacheConfig{ + "io_jwt": { + MaxNumEntries: maxNumEntriesJWTValueCache, + }, + "graphql": { + MaxNumEntries: maxNumEntriesGraphQLValueCache, + }, + }, + }, + } + + if !reflect.DeepEqual(cacheConf, expectedCacheConf) { + t.Fatalf("want %v got %v", expectedCacheConf, cacheConf) + } + + // no corresponding service config entry + serviceBundle = makeDataBundle(8, ` + { + "config": {} + } + `) + + disco.oneShot(ctx, download.Update{Bundle: serviceBundle}) + + var dtConfig map[string]any + err = util.Unmarshal(manager.Config.DistributedTracing, &dtConfig) + if err != nil { + t.Fatal(err) + } + + ty, ok := dtConfig["type"] + if !ok { + t.Fatal("Expected config for distributed tracing") + } + + if ty != "grpc" { + t.Fatalf("Expected distributed tracing \"grpc\" but got %v", ty) + } +} + +func TestMergeValuesAndListOverrides(t *testing.T) { + tests := []struct { + name string + dest map[string]any + src map[string]any + expected map[string]any + override []string + }{ + { + name: "Simple merge", + dest: map[string]any{ + "a": 1, + "b": 2, + }, + src: map[string]any{ + "c": 3, + "d": 4, + }, + expected: map[string]any{ + "a": 1, + "b": 2, + "c": 3, + "d": 4, + }, + override: []string{}, + }, + { + name: "Nested merge", + dest: map[string]any{ + "a": 1, + "b": map[string]any{ + "ba": 10, + }, + }, + src: map[string]any{ + "b": map[string]any{ + "bb": 20, + }, + "c": map[string]any{ + "ca": 30, + }, + }, + expected: map[string]any{ + "a": 1, + "b": map[string]any{ + "ba": 10, + "bb": 20, + }, + "c": map[string]any{ + "ca": 30, + }, + }, + override: []string{}, + }, + { + name: "Simple Non-map override -1", + dest: map[string]any{ + "a": []any{"bar"}, + "b": 2, + }, + src: map[string]any{ + "a": 3, + }, + expected: map[string]any{ + "a": 3, + "b": 2, + }, + override: []string{"a"}, + }, + { + name: "Simple Non-map override -2", + dest: map[string]any{ + "a": 3, + "b": 2, + }, + src: map[string]any{ + "a": []any{"bar"}, + }, + expected: map[string]any{ + "a": []any{"bar"}, + "b": 2, + }, + override: []string{"a"}, + }, + { + name: "Non-map override -1", + dest: map[string]any{ + "a": []any{"bar"}, + "b": 2, + }, + src: map[string]any{ + "a": []string{"foo"}, + }, + expected: map[string]any{ + "a": []string{"foo"}, + "b": 2, + }, + override: []string{"a"}, + }, + { + name: "Non-map override -2", + dest: map[string]any{ + "a": map[string]any{ + "aa": 10, + "ab": 20, + }, + "b": 2, + }, + src: map[string]any{ + "a": []any{"foo"}, + }, + expected: map[string]any{ + "a": []any{"foo"}, + "b": 2, + }, + override: []string{"a"}, + }, + { + name: "Simple overridden keys", + dest: map[string]any{ + "a": 1, + "b": 2, + }, + src: map[string]any{ + "b": 20, + "c": 3, + }, + expected: map[string]any{ + "a": 1, + "b": 20, + "c": 3, + }, + override: []string{"b"}, + }, + { + name: "Nested overridden keys", + dest: map[string]any{ + "a": 1, + "b": map[string]any{ + "ba": 10, + "bb": 20, + }, + }, + src: map[string]any{ + "b": map[string]any{ + "bb": 200, + "bc": 300, + }, + "c": map[string]any{ + "ca": 30, + }, + }, + expected: map[string]any{ + "a": 1, + "b": map[string]any{ + "ba": 10, + "bb": 200, + "bc": 300, + }, + "c": map[string]any{ + "ca": 30, + }, + }, + override: []string{"b.bb"}, + }, + { + name: "Multiple Nested overridden keys", + dest: map[string]any{ + "a": 1, + "b": map[string]any{ + "ba": 10, + "bb": 20, + }, + "c": map[string]any{ + "ca": 10, + "cb": 20, + "cc": 30, + }, + }, + src: map[string]any{ + "b": map[string]any{ + "bb": 200, + "bc": 300, + }, + "c": map[string]any{ + "ca": 300, + "cd": 400, + }, + }, + expected: map[string]any{ + "a": 1, + "b": map[string]any{ + "ba": 10, + "bb": 200, + "bc": 300, + }, + "c": map[string]any{ + "ca": 300, + "cb": 20, + "cc": 30, + "cd": 400, + }, + }, + override: []string{"b.bb", "c.ca"}, + }, + { + name: "Nested overridden keys - 2", + dest: map[string]any{ + "a": 1, + "b": map[string]any{ + "ba": map[string]any{"bba": "1"}, + }, + "c": 2, + }, + src: map[string]any{ + "b": map[string]any{ + "ba": map[string]any{"bba": "2"}, + }, + "c": map[string]any{ + "ca": 30, + }, + }, + expected: map[string]any{ + "a": 1, + "b": map[string]any{ + "ba": map[string]any{"bba": "2"}, + }, + "c": map[string]any{ + "ca": 30, + }, + }, + override: []string{"b.ba.bba", "c"}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + result, overriddenKeys := mergeValuesAndListOverrides(tc.dest, tc.src, "") + if !reflect.DeepEqual(result, tc.expected) { + t.Errorf("Expected result %v but got %v", tc.expected, result) + } + + if len(overriddenKeys) != len(tc.override) { + t.Fatal("Mismatch between expected and actual overridden keys") + } + + for _, k1 := range tc.override { + found := false + for _, k2 := range overriddenKeys { + if k1 == k2 { + found = true + } + } + + if !found { + t.Errorf("Expected overridden keys %v but got %v", tc.override, overriddenKeys) + } + } + }) + } +} + +func TestReconfigureWithUpdates(t *testing.T) { + + ctx := context.Background() + + bootConfigRaw := []byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config"}, + "keys": { + "global_key": { + "key": "secret", + "algorithm": "HS256", + "scope": "read" + }, + "local_key": { + "key": "some_private_key", + "scope": "write" + } + }, + "persistence_directory": "test" + }`) + + manager, err := plugins.New(bootConfigRaw, "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + var bootConfig map[string]any + err = util.Unmarshal(bootConfigRaw, &bootConfig) + if err != nil { + t.Fatal(err) + } + + disco, err := New(manager, BootConfig(bootConfig)) + if err != nil { + t.Fatal(err) + } + originalConfig := disco.config + + initialBundle := makeDataBundle(1, ` + { + "config": { + "bundle": {"name": "test1"}, + "status": {}, + "decision_logs": {} + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: initialBundle}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // update the discovery configuration and check + // the boot configuration is not overwritten + updatedBundle := makeDataBundle(2, ` + { + "config": { + "discovery": { + "name": "config", + "decision": "/foo/bar" + } + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if !reflect.DeepEqual(originalConfig, disco.config) { + t.Fatal("Discovery configuration updated") + } + + // no update to the discovery configuration and check no error generated + updatedBundle = makeDataBundle(3, ` + { + "config": { + "discovery": { + "name": "config" + } + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if !reflect.DeepEqual(originalConfig, disco.config) { + t.Fatal("Discovery configuration updated") + } + + // update the discovery service and check that error generated + updatedBundle = makeDataBundle(4, ` + { + "config": { + "services": { + "localhost": { + "url": "http://localhost:9999", + "credentials": {"bearer": {"token": "blah"}} + } + } + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err == nil { + t.Fatal("Expected error but got nil") + } + + expectedErrMsg := "updates to the discovery service are not allowed" + if err.Error() != expectedErrMsg { + t.Fatalf("Expected error message: %v but got: %v", expectedErrMsg, err.Error()) + } + + // no update to the discovery service and check no error generated + updatedBundle = makeDataBundle(5, ` + { + "config": { + "services": { + "localhost": { + "url": "http://localhost:9999" + } + } + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // add a new service and a new bundle + updatedBundle = makeDataBundle(6, ` + { + "config": { + "services": { + "acmecorp": { + "url": "http://localhost:8181" + } + }, + "bundles": { + "authz": { + "service": "acmecorp" + } + } + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if len(disco.manager.Services()) != 2 { + t.Fatalf("Expected two services but got %v\n", len(disco.manager.Services())) + } + + bPlugin := bundlePlugin.Lookup(disco.manager) + config := bPlugin.Config() + expected := "acmecorp" + if config.Bundles["authz"].Service != expected { + t.Fatalf("Expected service %v for bundle authz but got %v", expected, config.Bundles["authz"].Service) + } + + // update existing bundle's config and add a new bundle + updatedBundle = makeDataBundle(7, ` + { + "config": { + "bundles": { + "authz": { + "service": "localhost", + "resource": "foo/bar" + }, + "main": { + "resource": "baz/bar" + } + } + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + bPlugin = bundlePlugin.Lookup(disco.manager) + config = bPlugin.Config() + expectedSvc := "localhost" + if config.Bundles["authz"].Service != expectedSvc { + t.Fatalf("Expected service %v for bundle authz but got %v", expectedSvc, config.Bundles["authz"].Service) + } + + expectedRes := "foo/bar" + if config.Bundles["authz"].Resource != expectedRes { + t.Fatalf("Expected resource %v for bundle authz but got %v", expectedRes, config.Bundles["authz"].Resource) + } + + expectedSvcs := map[string]bool{"localhost": true, "acmecorp": true} + if _, ok := expectedSvcs[config.Bundles["main"].Service]; !ok { + t.Fatalf("Expected service for bundle main to be one of [%v, %v] but got %v", "localhost", "acmecorp", config.Bundles["main"].Service) + } + + // update existing (non-discovery)service's config + updatedBundle = makeDataBundle(8, ` + { + "config": { + "services": { + "acmecorp": { + "url": "http://localhost:8181", + "credentials": {"bearer": {"token": "blah"}} + } + }, + "bundles": { + "authz": { + "service": "localhost", + "resource": "foo/bar" + } + } + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // add a new key + updatedBundle = makeDataBundle(9, ` + { + "config": { + "keys": { + "new_global_key": { + "key": "secret", + "algorithm": "HS256", + "scope": "read" + } + } + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // update a key in the boot config + updatedBundle = makeDataBundle(10, ` + { + "config": { + "keys": { + "global_key": { + "key": "new_secret", + "algorithm": "HS256", + "scope": "read" + } + } + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err == nil { + t.Fatal("Expected error but got nil") + } + + errMsg := "updates to keys specified in the boot configuration are not allowed" + if err.Error() != errMsg { + t.Fatalf("Expected error message: %v but got: %v", errMsg, err.Error()) + } + + // no config change for a key in the boot config + updatedBundle = makeDataBundle(11, ` + { + "config": { + "keys": { + "global_key": { + "key": "secret", + "algorithm": "HS256", + "scope": "read" + } + } + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // update a key not in the boot config + updatedBundle = makeDataBundle(12, ` + { + "config": { + "keys": { + "new_global_key": { + "key": "secret", + "algorithm": "HS256", + "scope": "write" + } + } + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // check that omitting persistence_directory or discovery doesn't remove boot config + updatedBundle = makeDataBundle(13, ` + { + "config": {} + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if manager.Config.PersistenceDirectory == nil { + t.Fatal("Erased persistence directory configuration") + } + if manager.Config.Discovery == nil { + t.Fatal("Erased discovery plugin configuration") + } + + // update persistence directory in the service config and check that its boot config value is not overridden + updatedBundle = makeDataBundle(14, ` + { + "config": { + "persistence_directory": "my_bundles" + } + } + `) + + err = disco.reconfigure(ctx, download.Update{Bundle: updatedBundle}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if manager.Config.PersistenceDirectory == nil || *manager.Config.PersistenceDirectory == "my_bundles" { + t.Fatal("Unexpected update to persistence directory") + } +} + +func TestProcessBundleWithSigning(t *testing.T) { + + ctx := context.Background() + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config", "signing": {"keyid": "my_global_key"}}, + "keys": {"my_global_key": {"algorithm": "HS256", "key": "secret"}}, + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + disco, err := New(manager) + if err != nil { + t.Fatal(err) + } + + initialBundle := makeDataBundle(1, ` + { + "config": { + "bundle": {"name": "test1"}, + "status": {}, + "decision_logs": {}, + "keys": {"my_local_key": {"algorithm": "HS256", "key": "new_secret"}} + } + } + `) + + _, err = disco.processBundle(ctx, initialBundle) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +func TestProcessBundleWithNoSigningConfig(t *testing.T) { + ctx := context.Background() + + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config"} + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + disco, err := New(manager) + if err != nil { + t.Fatal(err) + } + + initialBundle := makeDataBundle(1, ` + { + "config": { + "bundles": {"test1": {"service": "localhost"}}, + "keys": {"my_local_key": {"algorithm": "HS256", "key": "new_secret"}} + } + } + `) + + _, err = disco.processBundle(ctx, initialBundle) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +type testServer struct { + t *testing.T + server *httptest.Server + updates chan status.UpdateRequestV1 +} + +func (ts *testServer) Start() { + ts.updates = make(chan status.UpdateRequestV1, 100) + ts.server = httptest.NewServer(http.HandlerFunc(ts.handle)) +} + +func (ts *testServer) Stop() { + ts.server.Close() +} + +func (ts *testServer) handle(w http.ResponseWriter, r *http.Request) { + + var update status.UpdateRequestV1 + + if err := json.NewDecoder(r.Body).Decode(&update); err != nil { + ts.t.Fatal(err) + } + + ts.updates <- update + + w.WriteHeader(200) +} + +func TestStatusUpdates(t *testing.T) { + + ts := testServer{t: t} + ts.Start() + defer ts.Stop() + + manager, err := plugins.New(fmt.Appendf(nil, `{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": %q + } + }, + "discovery": {"name": "config"}, + }`, ts.server.URL), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + disco, err := New(manager) + if err != nil { + t.Fatal(err) + } + + updates := make(chan status.UpdateRequestV1, 100) + + ctx := context.Background() + + // Enable status plugin which sends initial update. + disco.oneShot(ctx, download.Update{ETag: "etag-1", Bundle: makeDataBundle(1, `{ + "config": { + "status": {} + } + }`)}) + + // status plugin updates and bundle discovery status update, + updates <- <-ts.updates + updates <- <-ts.updates + updates <- <-ts.updates + + // Downloader error. + disco.oneShot(ctx, download.Update{Error: errors.New("unknown error")}) + + updates <- <-ts.updates + + // Clear error. + disco.oneShot(ctx, download.Update{ETag: "etag-2", Bundle: makeDataBundle(2, `{ + "config": { + "status": {} + } + }`)}) + + updates <- <-ts.updates + + // Configuration error. + disco.oneShot(ctx, download.Update{ETag: "etag-3", Bundle: makeDataBundle(3, `{ + "config": { + "status": {"service": "missing service"} + } + }`)}) + + updates <- <-ts.updates + + // Clear error (last successful reconfigure). + disco.oneShot(ctx, download.Update{ETag: "etag-2"}) + + updates <- <-ts.updates + + // Check that all updates were received and active revisions are expected. + expectedDiscoveryUpdates := []struct { + Code string + Revision string + }{ + { + Code: "", + Revision: "test-revision-1", + }, + { + Code: "bundle_error", + Revision: "test-revision-1", + }, + { + Code: "", + Revision: "test-revision-2", + }, + { + Code: "bundle_error", + Revision: "test-revision-2", + }, + { + Code: "", + Revision: "test-revision-2", + }, + } + + // nextExpectedDiscoveryUpdate, we look for each + nextExpectedDiscoveryUpdate := expectedDiscoveryUpdates[0] + expectedDiscoveryUpdates = expectedDiscoveryUpdates[1:] + + timeout, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + for { + select { + case update := <-updates: + if update.Discovery != nil { + matches := false + if update.Discovery.Code == nextExpectedDiscoveryUpdate.Code && + update.Discovery.ActiveRevision == nextExpectedDiscoveryUpdate.Revision { + matches = true + } + + if matches { + if len(expectedDiscoveryUpdates) == 0 { + return + } + nextExpectedDiscoveryUpdate = expectedDiscoveryUpdates[0] + expectedDiscoveryUpdates = expectedDiscoveryUpdates[1:] + } + } + case <-timeout.Done(): + cancel() + t.Fatalf("Waiting for following statuses timed out: %v", expectedDiscoveryUpdates) + } + } +} + +func TestStatusUpdatesFromPersistedBundlesDontDelayBoot(t *testing.T) { + dir := t.TempDir() + + // write the disco bundle to disk + discoBundle := bundleApi.Bundle{ + Data: map[string]any{ + "discovery": map[string]any{ + "bundles": map[string]any{ + "main": map[string]any{ + "persist": true, + "resource": "/bundle", + "service": "localhost", + }, + }, + "status": map[string]any{ + "service": "localhost", + }, + }, + }, + } + + discoBundleDir := filepath.Join(dir, "bundles", "config") + if err := os.MkdirAll(discoBundleDir, 0755); err != nil { + t.Fatal(err) + } + + discoBundleFile, err := os.Create(filepath.Join(discoBundleDir, "bundle.tar.gz")) + if err != nil { + t.Fatal(err) + + } + defer discoBundleFile.Close() + + err = bundleApi.NewWriter(discoBundleFile).Write(discoBundle) + if err != nil { + t.Fatal(err) + } + + // write an example data bundle ('main') to disk + mainBundle := bundleApi.Bundle{ + Data: map[string]any{ + "foo": "bar", + }, + } + + mainBundleDir := filepath.Join(dir, "bundles", "main") + if err := os.MkdirAll(mainBundleDir, 0755); err != nil { + t.Fatal(err) + } + + mainBundleFile, err := os.Create(filepath.Join(mainBundleDir, "bundle.tar.gz")) + if err != nil { + t.Fatal(err) + + } + defer mainBundleFile.Close() + + err = bundleApi.NewWriter(mainBundleFile).Write(mainBundle) + if err != nil { + t.Fatal(err) + } + + // Create a timing out listener for the referenced localhost service + // :0 will cause net to find an available port + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer listener.Close() + + manager, err := plugins.New(fmt.Appendf(nil, `{ + "persistence_directory": %q, + "services": { + "localhost": { + "url": "http://%s" + } + }, + "discovery": {"name": "config", "persist": true, "decision": "discovery"}, + }`, dir, listener.Addr().String()), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + // allow 2s of time to start before failing + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + // start Discovery instance, wait for it to complete Start() + booted := make(chan bool) + go func() { + disco, err := New(manager) + if err != nil { + t.Log(err) + return + } + err = disco.Start(ctx) + if err != nil { + t.Log(err) + return + } + booted <- true + }() + + select { + case <-booted: + for k, pi := range manager.PluginStatus() { + if pi.State != plugins.StateOK { + t.Errorf("Expected %s plugin to be in OK state but got %v", k, pi.State) + } + } + case <-ctx.Done(): + t.Errorf("Timed out waiting for disco to start") + } +} + +func TestStatusUpdatesTimestamp(t *testing.T) { + + ts := testServer{t: t} + ts.Start() + defer ts.Stop() + + manager, err := plugins.New(fmt.Appendf(nil, `{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": %q + } + }, + "discovery": {"name": "config"}, + }`, ts.server.URL), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + disco, err := New(manager) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + // simulate HTTP 200 response from downloader + disco.oneShot(ctx, download.Update{ETag: "etag-1", Bundle: makeDataBundle(1, `{ + "config": { + "status": {} + } + }`)}) + + if disco.status.LastSuccessfulDownload != disco.status.LastSuccessfulRequest || disco.status.LastSuccessfulDownload != disco.status.LastRequest { + t.Fatal("expected last successful request to be same as download and request") + } + + if disco.status.LastSuccessfulActivation.IsZero() { + t.Fatal("expected last successful activation to be non-zero") + } + + time.Sleep(time.Millisecond) + + // simulate HTTP 304 response from downloader + disco.oneShot(ctx, download.Update{ETag: "etag-1", Bundle: nil}) + if disco.status.LastSuccessfulDownload == disco.status.LastSuccessfulRequest || disco.status.LastSuccessfulDownload == disco.status.LastRequest { + t.Fatal("expected last successful download to differ from request and last request") + } + + // simulate HTTP 200 response from downloader + disco.oneShot(ctx, download.Update{ETag: "etag-2", Bundle: makeDataBundle(2, `{ + "config": { + "status": {} + } + }`)}) + + if disco.status.LastSuccessfulDownload != disco.status.LastSuccessfulRequest || disco.status.LastSuccessfulDownload != disco.status.LastRequest { + t.Fatal("expected last successful request to be same as download and request") + } + + if disco.status.LastSuccessfulActivation.IsZero() { + t.Fatal("expected last successful activation to be non-zero") + } + + // simulate error response from downloader + disco.oneShot(ctx, download.Update{Error: errors.New("unknown error")}) + + if disco.status.LastSuccessfulDownload != disco.status.LastSuccessfulRequest || disco.status.LastSuccessfulDownload == disco.status.LastRequest { + t.Fatal("expected last successful request to be same as download but different from request") + } +} + +func TestStatusMetricsForLogDrops(t *testing.T) { + + ctx := context.Background() + + testLogger := test.New() + + manager, err := plugins.New([]byte(`{ + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config"}, + }`), "test-id", inmem.New(), plugins.ConsoleLogger(testLogger)) + if err != nil { + t.Fatal(err) + } + + initialBundle := makeDataBundle(1, ` + { + "config": { + "status": {"console": true}, + "decision_logs": { + "service": "localhost", + "reporting": { + "max_decisions_per_second": 1 + } + } + } + } + `) + + disco, err := New(manager, Metrics(metrics.New())) + if err != nil { + t.Fatal(err) + } + + ps, err := disco.processBundle(ctx, initialBundle) + if err != nil { + t.Fatal(err) + } + + // start the decision log and status plugins + for _, p := range ps.Start { + if err := p.Start(ctx); err != nil { + t.Fatal(err) + } + } + + plugin := logs.Lookup(manager) + if plugin == nil { + t.Fatal("Expected decision log plugin registered on manager") + } + + var input any = map[string]any{"method": "GET"} + var result any = false + + event1 := &server.Info{ + DecisionID: "abc", + Path: "foo/bar", + Input: &input, + Results: &result, + RemoteAddr: "test-1", + } + + event2 := &server.Info{ + DecisionID: "def", + Path: "foo/baz", + Input: &input, + Results: &result, + RemoteAddr: "test-2", + } + + event3 := &server.Info{ + DecisionID: "ghi", + Path: "foo/aux", + Input: &input, + Results: &result, + RemoteAddr: "test-3", + } + + _ = plugin.Log(ctx, event1) // event 1 should be written into the decision log encoder + _ = plugin.Log(ctx, event2) // event 2 should not be written into the decision log encoder as rate limit exceeded + _ = plugin.Log(ctx, event3) // event 3 should not be written into the decision log encoder as rate limit exceeded + + // trigger a status update + disco.oneShot(ctx, download.Update{ETag: "etag-1", Bundle: makeDataBundle(1, `{ + "config": { + "bundles": {"test-bundle": {"service": "localhost"}} + } + }`)}) + + status.Lookup(manager).Stop(ctx) + + entries := testLogger.Entries() + if len(entries) == 0 { + t.Fatal("Expected log entries but got none") + } + + // Pick the last entry as it should have the drop count + e := entries[len(entries)-1] + + if _, ok := e.Fields["metrics"]; !ok { + t.Fatal("Expected metrics") + } + + builtInMet := e.Fields["metrics"].(map[string]any)[""] + dropCount := builtInMet.(map[string]any)["counter_decision_logs_dropped_rate_limit_exceeded"] + + actual, err := dropCount.(json.Number).Int64() + if err != nil { + t.Fatal(err) + } + + // Along with event 2 and event 3, event 1 could also get dropped. This happens when the decision log plugin + // tries to requeue event 1 after a failed upload attempt to a non-existent remote endpoint + if actual < 2 { + t.Fatal("Expected at least 2 events to be dropped") + } +} + +func makeDataBundle(n int, s string) *bundleApi.Bundle { + return &bundleApi.Bundle{ + Manifest: bundleApi.Manifest{Revision: fmt.Sprintf("test-revision-%v", n)}, + Data: util.MustUnmarshalJSON([]byte(s)).(map[string]any), + } +} + +func makeModuleBundle(n int, s string, popts ast.ParserOptions) *bundleApi.Bundle { + return &bundleApi.Bundle{ + Manifest: bundleApi.Manifest{Revision: fmt.Sprintf("test-revision-%v", n)}, + Modules: []bundleApi.ModuleFile{ + { + URL: `policy.rego`, + Path: `/policy.rego`, + Raw: []byte(s), + Parsed: ast.MustParseModuleWithOpts(s, popts), + }, + }, + Data: map[string]any{}, + } +} + +func makeModuleBundleWithRegoVersion(revision int, bundle string, regoVersion int) *bundleApi.Bundle { + popts := ast.ParserOptions{} + if regoVersion == 0 { + popts.RegoVersion = ast.RegoV0 + } else { + popts.RegoVersion = ast.RegoV1 + } + return &bundleApi.Bundle{ + Manifest: bundleApi.Manifest{ + Revision: fmt.Sprintf("test-revision-%v", revision), + RegoVersion: ®oVersion, + }, + Modules: []bundleApi.ModuleFile{ + { + URL: `policy.rego`, + Path: `/policy.rego`, + Raw: []byte(bundle), + Parsed: ast.MustParseModuleWithOpts(bundle, popts), + }, + }, + Data: map[string]any{}, + } +} + +type versionedModule struct { + version int + module string +} + +func makeBundleWithRegoVersion(revision int, bundleRegoVersion int, modules map[string]versionedModule) *bundleApi.Bundle { + b := bundleApi.Bundle{ + Manifest: bundleApi.Manifest{ + Revision: fmt.Sprintf("test-revision-%v", revision), + RegoVersion: &bundleRegoVersion, + FileRegoVersions: map[string]int{}, + }, + Data: map[string]any{}, + } + + for k, v := range modules { + p := path.Join("/", k) + popts := ast.ParserOptions{} + if v.version >= 0 { + b.Manifest.FileRegoVersions[p] = v.version + popts.RegoVersion = ast.RegoVersionFromInt(v.version) + } else { + popts.RegoVersion = ast.RegoVersionFromInt(bundleRegoVersion) + } + b.Modules = append(b.Modules, bundleApi.ModuleFile{ + URL: k, + Path: p, + Raw: []byte(v.module), + Parsed: ast.MustParseModuleWithOpts(v.module, popts), + }) + } + + return &b +} + +func getTestManager(t *testing.T, conf string) *plugins.Manager { + t.Helper() + store := inmem.New() + manager, err := plugins.New([]byte(conf), "test-instance-id", store) + if err != nil { + t.Fatalf("failed to create plugin manager: %s", err) + } + return manager +} + +func TestGetPluginSetWithMixedConfig(t *testing.T) { + conf := ` +services: + s1: + url: http://test1.com + s2: + url: http://test2.com + +bundles: + bundle-new: + service: s1 + +bundle: + name: bundle-classic + service: s2 +` + manager := getTestManager(t, conf) + trigger := plugins.TriggerManual + _, err := getPluginSet(nil, manager, manager.Config, nil, nil, &trigger) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + p := manager.Plugin(bundlePlugin.Name) + if p == nil { + t.Fatal("Unable to find bundle plugin on manager") + } + bp := p.(*bundlePlugin.Plugin) + + // make sure the older style `bundle` config takes precedence + if bp.Config().Name != "bundle-classic" { + t.Fatal("Expected bundle plugin config Name to be 'bundle-classic'") + } + + if len(bp.Config().Bundles) != 1 { + t.Fatal("Expected a single bundle configured") + } + + if bp.Config().Bundles["bundle-classic"].Service != "s2" { + t.Fatalf("Expected the classic bundle to be configured as bundles[0], got: %+v", bp.Config().Bundles) + } +} + +func TestGetPluginSetWithBundlesConfig(t *testing.T) { + conf := ` +services: + s1: + url: http://test1.com + +bundles: + bundle-new: + service: s1 +` + manager := getTestManager(t, conf) + trigger := plugins.TriggerManual + _, err := getPluginSet(nil, manager, manager.Config, nil, nil, &trigger) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + p := manager.Plugin(bundlePlugin.Name) + if p == nil { + t.Fatal("Unable to find bundle plugin on manager") + } + bp := p.(*bundlePlugin.Plugin) + + if len(bp.Config().Bundles) != 1 { + t.Fatal("Expected a single bundle configured") + } + + if bp.Config().Bundles["bundle-new"].Service != "s1" { + t.Fatalf("Expected the bundle to be configured as bundles[0], got: %+v", bp.Config().Bundles) + } +} + +func TestGetPluginSetWithBadManualTriggerBundlesConfig(t *testing.T) { + confGood := ` +services: + s1: + url: http://test1.com + +bundles: + bundle-new: + service: s1 +` + + confBad := ` +services: + s1: + url: http://test1.com + +bundles: + bundle-new: + service: s1 + trigger: periodic +` + + tests := map[string]struct { + conf string + wantErr bool + err error + }{ + "no_trigger_mode_mismatch": { + confGood, false, nil, + }, + "trigger_mode_mismatch": { + confBad, true, errors.New("invalid configuration for bundle \"bundle-new\": trigger mode mismatch: manual and periodic (hint: check discovery configuration)"), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + manager := getTestManager(t, tc.conf) + trigger := plugins.TriggerManual + _, err := getPluginSet(nil, manager, manager.Config, nil, nil, &trigger) + + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + } +} + +func TestGetPluginSetWithBadManualTriggerDecisionLogConfig(t *testing.T) { + + confGood := ` +services: + s1: + url: http://test1.com + +bundles: + bundle-new: + service: s1 + trigger: manual +decision_logs: + service: s1 +` + + confBad := ` +services: + s1: + url: http://test1.com + +bundles: + bundle-new: + service: s1 + trigger: manual +decision_logs: + service: s1 + reporting: + trigger: periodic +` + + tests := map[string]struct { + conf string + wantErr bool + err error + }{ + "no_trigger_mode_mismatch": { + confGood, false, nil, + }, + "trigger_mode_mismatch": { + confBad, true, errors.New("invalid decision_log config: trigger mode mismatch: manual and periodic (hint: check discovery configuration)"), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + manager := getTestManager(t, tc.conf) + trigger := plugins.TriggerManual + _, err := getPluginSet(nil, manager, manager.Config, nil, nil, &trigger) + + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + } +} + +func TestGetPluginSetWithBadManualTriggerStatusConfig(t *testing.T) { + confGood := ` +services: + s1: + url: http://test1.com + +bundles: + bundle-new: + service: s1 + trigger: manual +decision_logs: + service: s1 + reporting: + trigger: manual +status: + service: s1 +` + + confBad := ` +services: + s1: + url: http://test1.com + +bundles: + bundle-new: + service: s1 + trigger: manual +decision_logs: + service: s1 + reporting: + trigger: manual +status: + service: s1 + trigger: periodic +` + + tests := map[string]struct { + conf string + wantErr bool + err error + }{ + "no_trigger_mode_mismatch": { + confGood, false, nil, + }, + "trigger_mode_mismatch": { + confBad, true, errors.New("invalid status config: trigger mode mismatch: manual and periodic (hint: check discovery configuration)"), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + manager := getTestManager(t, tc.conf) + trigger := plugins.TriggerManual + _, err := getPluginSet(nil, manager, manager.Config, nil, nil, &trigger) + + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + } +} + +func TestInterQueryBuiltinCacheConfigUpdate(t *testing.T) { + var config1 *cache.Config + var config2 *cache.Config + manager, err := plugins.New([]byte(`{ + "discovery": {"name": "config"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + }`), "test-id", inmem.New()) + manager.RegisterCacheTrigger(func(c *cache.Config) { + if config1 == nil { + config1 = c + } else if config2 == nil { + config2 = c + } else { + t.Fatal("Expected cache trigger to only be called twice") + } + }) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + initialBundle := makeDataBundle(1, `{ + "config": { + "caching": { + "inter_query_builtin_cache": { + "max_size_bytes": 100 + } + } + } + }`) + + disco.oneShot(ctx, download.Update{Bundle: initialBundle}) + + // Verify interQueryBuiltinCacheConfig is triggered with initial config + if config1 == nil || *config1.InterQueryBuiltinCache.MaxSizeBytes != int64(100) { + t.Fatalf("Expected cache max size bytes to be 100 after initial discovery, got: %v", config1.InterQueryBuiltinCache.MaxSizeBytes) + } + + // Verify interQueryBuiltinCache is reconfigured + updatedBundle := makeDataBundle(2, `{ + "config": { + "caching": { + "inter_query_builtin_cache": { + "max_size_bytes": 200 + } + } + } + }`) + + disco.oneShot(ctx, download.Update{Bundle: updatedBundle}) + + if config2 == nil || *config2.InterQueryBuiltinCache.MaxSizeBytes != int64(200) { + t.Fatalf("Expected cache max size bytes to be 200 after discovery reconfigure, got: %v", config2.InterQueryBuiltinCache.MaxSizeBytes) + } +} + +func TestNDBuiltinCacheConfigUpdate(t *testing.T) { + type exampleConfig struct { + v bool + } + var config1 *exampleConfig + var config2 *exampleConfig + manager, err := plugins.New([]byte(`{ + "discovery": {"name": "config"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + }`), "test-id", inmem.New()) + manager.RegisterNDCacheTrigger(func(x bool) { + if config1 == nil { + config1 = &exampleConfig{v: x} + } else if config2 == nil { + config2 = &exampleConfig{v: x} + } else { + t.Fatal("Expected cache trigger to only be called twice") + } + }) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + initialBundle := makeDataBundle(1, `{ + "config": { + "nd_builtin_cache": true + } + }`) + + disco.oneShot(ctx, download.Update{Bundle: initialBundle}) + + // Verify NDBuiltinCache is triggered with initial config + if config1 == nil || config1.v != true { + t.Fatalf("Expected ND builtin cache to be enabled after initial discovery, got: %v", config1.v) + } + + // Verify NDBuiltinCache is reconfigured + updatedBundle := makeDataBundle(2, `{ + "config": { + "nd_builtin_cache": false + } + }`) + + disco.oneShot(ctx, download.Update{Bundle: updatedBundle}) + + if config2 == nil || config2.v != false { + t.Fatalf("Expected ND builtin cache to be disabled after discovery reconfigure, got: %v", config2.v) + } +} + +func TestPluginManualTriggerLifecycle(t *testing.T) { + ctx := context.Background() + m := metrics.New() + + fixture := newTestFixture(t) + defer fixture.stop() + + // run query + result, err := fixture.runQuery(ctx, "data.foo.bar", m) + if err != nil { + t.Fatal(err) + } + + if result != nil { + t.Fatalf("Expected nil result but got %v", result) + } + + // log result (there should not be a decision log plugin on the manager yet) + err = fixture.log(ctx, "data.foo.bar", m, &result) + if err != nil { + t.Fatal(err) + } + + // start the discovery plugin + if err := fixture.plugin.Start(ctx); err != nil { + t.Fatal(err) + } + + // trigger the discovery plugin + fixture.server.discoConfig = ` + { + "config": { + "bundles": { + "authz": { + "service": "example", + "trigger": "manual" + } + }, + "status": {"service": "example", "trigger": "manual"}, + "decision_logs": {"service": "example", "reporting": {"trigger": "manual"}} + } + }` + + fixture.server.dicsoBundleRev = 1 + + trigger := make(chan struct{}) + fixture.discoTrigger <- trigger + <-trigger + + // check if the discovery, bundle, decision log and status plugin are configured + expectedNum := 4 + if len(fixture.manager.Plugins()) != expectedNum { + t.Fatalf("Expected %v configured plugins but got %v", expectedNum, len(fixture.manager.Plugins())) + } + + // run query (since the bundle plugin is not triggered yet, there should not be any activated bundles) + result, err = fixture.runQuery(ctx, "data.foo.bar", m) + if err != nil { + t.Fatal(err) + } + + if result != nil { + t.Fatalf("Expected nil result but got %v", result) + } + + // log result + err = fixture.log(ctx, "data.foo.bar", m, &result) + if err != nil { + t.Fatal(err) + } + + // trigger the bundle plugin + fixture.server.bundleData = map[string]any{ + "foo": map[string]any{ + "bar": "hello", + }, + } + fixture.server.bundleRevision = "abc" + + trigger = make(chan struct{}) + fixture.bundleTrigger <- trigger + <-trigger + + // ensure the bundle was activated + txn := storage.NewTransactionOrDie(ctx, fixture.manager.Store) + names, err := bundleApi.ReadBundleNamesFromStore(ctx, fixture.manager.Store, txn) + if err != nil { + t.Fatal(err) + } + + expectedNum = 1 + if len(names) != expectedNum { + t.Fatalf("Expected %d bundles in store, found %d", expectedNum, len(names)) + } + + // stop the "read" transaction + fixture.manager.Store.Abort(ctx, txn) + + // run query + result, err = fixture.runQuery(ctx, "data.foo.bar", m) + if err != nil { + t.Fatal(err) + } + + expected := "hello" + if result != expected { + t.Fatalf("Expected result %v but got %v", expected, result) + } + + // log result + err = fixture.log(ctx, "data.foo.bar", m, &result) + if err != nil { + t.Fatal(err) + } + + // trigger the decision log plugin + trigger = make(chan struct{}) + fixture.decisionLogTrigger <- trigger + <-trigger + + expectedNum = 2 + if len(fixture.server.logEvent) != expectedNum { + t.Fatalf("Expected %d decision log events, found %d", expectedNum, len(fixture.server.logEvent)) + } + + // verify the result in the last log + if *fixture.server.logEvent[1].Result != expected { + t.Fatalf("Expected result %v but got %v", expected, result) + } + + // trigger the status plugin + trigger = make(chan struct{}) + fixture.statusTrigger <- trigger + <-trigger + + expectedNum = 1 + if len(fixture.server.statusEvent) != expectedNum { + t.Fatalf("Expected %d status updates, found %d", expectedNum, len(fixture.server.statusEvent)) + } + + // update the service bundle and trigger the bundle plugin again + fixture.testServiceBundleUpdateScenario(ctx, m) + + // reconfigure the service bundle config to go from manual to periodic polling. This should result in an error + // when the discovery plugin tries to reconfigure the bundle + fixture.server.discoConfig = ` + { + "config": { + "bundles": { + "authz": { + "service": "example", + "trigger": "periodic" + } + } + } + }` + fixture.server.dicsoBundleRev = 2 + + trigger = make(chan struct{}) + fixture.discoTrigger <- trigger + <-trigger + + // trigger the status plugin + trigger = make(chan struct{}) + fixture.statusTrigger <- trigger + <-trigger + + expectedNum = 3 + if len(fixture.server.statusEvent) != expectedNum { + t.Fatalf("Expected %d status updates, found %d", expectedNum, len(fixture.server.statusEvent)) + } + + // check for error in the last update corresponding to the bad service bundle config + disco, _ := fixture.server.statusEvent[2].(map[string]any) + errMsg := disco["discovery"].(map[string]any)["message"] + + expErrMsg := "invalid configuration for bundle \"authz\": trigger mode mismatch: manual and periodic (hint: check discovery configuration)" + if errMsg != expErrMsg { + t.Fatalf("Expected error %v but got %v", expErrMsg, errMsg) + } + + // reconfigure plugins via discovery and then trigger discovery + fixture.testDiscoReconfigurationScenario(ctx, m) +} + +func TestListeners(t *testing.T) { + manager, err := plugins.New([]byte(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": "http://localhost:9999" + } + }, + "discovery": {"name": "config", "persist": true}, + }`), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + testPlugin := &reconfigureTestPlugin{counts: map[string]int{}} + testFactory := testFactory{p: testPlugin} + + disco, err := New(manager, Factories(map[string]plugins.Factory{"test_plugin": testFactory})) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + ensurePluginState(t, disco, plugins.StateNotReady) + + var status *bundlePlugin.Status + disco.RegisterListener("testlistener", func(s bundlePlugin.Status) { + status = &s + }) + + // simulate a bundle download error + disco.oneShot(ctx, download.Update{Error: errors.New("unknown error")}) + + if status == nil { + t.Fatalf("Expected discovery listener to receive status but was nil") + } + + status = nil + disco.Unregister("testlistener") + + // simulate a bundle download error + disco.oneShot(ctx, download.Update{Error: errors.New("unknown error")}) + if status != nil { + t.Fatalf("Expected discovery listener to be removed but received %v", status) + } +} + +type testFixture struct { + manager *plugins.Manager + plugin *Discovery + discoTrigger chan chan struct{} + bundleTrigger chan chan struct{} + decisionLogTrigger chan chan struct{} + statusTrigger chan chan struct{} + stopCh chan chan struct{} + server *testFixtureServer +} + +func newTestFixture(t *testing.T) *testFixture { + ts := testFixtureServer{ + t: t, + statusEvent: []any{}, + logEvent: []logs.EventV1{}, + } + + ts.start() + + managerConfig := fmt.Appendf(nil, `{ + "labels": { + "app": "example-app" + }, + "discovery": {"name": "disco", "trigger": "manual", "decision": "config"}, + "services": [ + { + "name": "example", + "url": %q + } + ]}`, ts.server.URL) + + manager, err := plugins.New(managerConfig, "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + disco, err := New(manager) + if err != nil { + t.Fatal(err) + } + + manager.Register(Name, disco) + + tf := testFixture{ + manager: manager, + plugin: disco, + server: &ts, + discoTrigger: make(chan chan struct{}), + bundleTrigger: make(chan chan struct{}), + decisionLogTrigger: make(chan chan struct{}), + statusTrigger: make(chan chan struct{}), + stopCh: make(chan chan struct{}), + } + + go tf.loop(context.Background()) + + return &tf +} + +func (t *testFixture) loop(ctx context.Context) { + + for { + select { + case stop := <-t.stopCh: + close(stop) + return + case done := <-t.discoTrigger: + if p, ok := t.manager.Plugin(Name).(plugins.Triggerable); ok { + _ = p.Trigger(ctx) + } + close(done) + + case done := <-t.bundleTrigger: + if p, ok := t.manager.Plugin(bundlePlugin.Name).(plugins.Triggerable); ok { + _ = p.Trigger(ctx) + } + close(done) + + case done := <-t.decisionLogTrigger: + if p, ok := t.manager.Plugin(logs.Name).(plugins.Triggerable); ok { + _ = p.Trigger(ctx) + } + close(done) + case done := <-t.statusTrigger: + if p, ok := t.manager.Plugin(status.Name).(plugins.Triggerable); ok { + _ = p.Trigger(ctx) + } + close(done) + } + } +} + +func (t *testFixture) runQuery(ctx context.Context, query string, m metrics.Metrics) (any, error) { + r := rego.New( + rego.Query(query), + rego.Store(t.manager.Store), + rego.Metrics(m), + ) + + // Run evaluation. + rs, err := r.Eval(ctx) + if err != nil { + return nil, err + } + + if len(rs) == 0 { + return nil, nil + } + + return rs[0].Expressions[0].Value, nil +} + +func (t *testFixture) log(ctx context.Context, query string, m metrics.Metrics, result *any) error { + + record := server.Info{ + Timestamp: time.Now(), + Path: query, + Metrics: m, + Results: result, + } + + if logger := logs.Lookup(t.manager); logger != nil { + if err := logger.Log(ctx, &record); err != nil { + return fmt.Errorf("decision log: %w", err) + } + } + return nil +} + +func (t *testFixture) testServiceBundleUpdateScenario(ctx context.Context, m metrics.Metrics) { + t.server.bundleData = map[string]any{ + "foo": map[string]any{ + "bar": "world", + }, + } + t.server.bundleRevision = "def" + + trigger := make(chan struct{}) + t.bundleTrigger <- trigger + <-trigger + + // run query + result, err := t.runQuery(ctx, "data.foo.bar", m) + if err != nil { + t.server.t.Fatal(err) + } + + expected := "world" + if result != expected { + t.server.t.Fatalf("Expected result %v but got %v", expected, result) + } + + // log result + err = t.log(ctx, "data.foo.bar", m, &result) + if err != nil { + t.server.t.Fatal(err) + } + + // trigger the decision log plugin + trigger = make(chan struct{}) + t.decisionLogTrigger <- trigger + <-trigger + + expectedNum := 3 + if len(t.server.logEvent) != expectedNum { + t.server.t.Fatalf("Expected %d decision log events, found %d", expectedNum, len(t.server.logEvent)) + } + + // verify the result in the last log + if *t.server.logEvent[2].Result != expected { + t.server.t.Fatalf("Expected result %v but got %v", expected, result) + } + + // trigger the status plugin (there should be a pending update corresponding to the last service bundle activation) + trigger = make(chan struct{}) + t.statusTrigger <- trigger + <-trigger + + expectedNum = 2 + if len(t.server.statusEvent) != expectedNum { + t.server.t.Fatalf("Expected %d status updates, found %d", expectedNum, len(t.server.statusEvent)) + } + + // verify the updated bundle revision in the last status update + bundles, _ := t.server.statusEvent[1].(map[string]any) + actual := bundles["bundles"].(map[string]any)["authz"].(map[string]any)["active_revision"] + + if actual != t.server.bundleRevision { + t.server.t.Fatalf("Expected revision %v but got %v", t.server.bundleRevision, actual) + } +} + +func (t *testFixture) testDiscoReconfigurationScenario(ctx context.Context, m metrics.Metrics) { + t.server.discoConfig = ` + { + "config": { + "bundles": { + "authz": { + "service": "example", + "resource": "newbundles/authz", + "trigger": "manual" + } + }, + "status": {"service": "example", "trigger": "manual", "partition_name": "new"}, + "decision_logs": {"service": "example", "resource": "newlogs", "reporting": {"trigger": "manual"}} + } + }` + + t.server.dicsoBundleRev = 3 + + trigger := make(chan struct{}) + t.discoTrigger <- trigger + <-trigger + + // trigger the bundle plugin + t.server.bundleData = map[string]any{ + "bux": map[string]any{ + "qux": "hello again!", + }, + } + t.server.bundleRevision = "ghi" + + trigger = make(chan struct{}) + t.bundleTrigger <- trigger + <-trigger + + // run query + result, err := t.runQuery(ctx, "data.bux.qux", m) + if err != nil { + t.server.t.Fatal(err) + } + + expected := "hello again!" + if result != expected { + t.server.t.Fatalf("Expected result %v but got %v", expected, result) + } + + // trigger the status plugin (there should be pending updates corresponding to the last discovery and service bundle activation) + trigger = make(chan struct{}) + t.statusTrigger <- trigger + <-trigger + + expectedNum := 4 + if len(t.server.statusEvent) != expectedNum { + t.server.t.Fatalf("Expected %d status updates, found %d", expectedNum, len(t.server.statusEvent)) + } + + // verify the updated discovery and service bundle revisions in the last status update + bundles, _ := t.server.statusEvent[3].(map[string]any) + actual := bundles["bundles"].(map[string]any)["authz"].(map[string]any)["active_revision"] + + if actual != t.server.bundleRevision { + t.server.t.Fatalf("Expected revision %v but got %v", t.server.bundleRevision, actual) + } + + disco, _ := t.server.statusEvent[3].(map[string]any) + actual = disco["discovery"].(map[string]any)["active_revision"] + + expectedRev := fmt.Sprintf("test-revision-%v", t.server.dicsoBundleRev) + if actual != expectedRev { + t.server.t.Fatalf("Expected discovery bundle revision %v but got %v", expectedRev, actual) + } +} + +func (t *testFixture) stop() { + done := make(chan struct{}) + t.stopCh <- done + <-done + + t.server.stop() +} + +type testFixtureServer struct { + t *testing.T + server *httptest.Server + discoConfig string + dicsoBundleRev int + bundleData map[string]any + bundleRevision string + statusEvent []any + logEvent []logs.EventV1 +} + +func (t *testFixtureServer) handle(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/bundles/disco" { + // prepare a discovery bundle with some configured plugins + + b := makeDataBundle(t.dicsoBundleRev, t.discoConfig) + + err := bundleApi.NewWriter(w).Write(*b) + if err != nil { + t.t.Fatal(err) + } + } else if r.URL.Path == "/bundles/authz" || r.URL.Path == "/newbundles/authz" { + // prepare a regular bundle + + b := bundleApi.Bundle{ + Data: t.bundleData, + Manifest: bundleApi.Manifest{Revision: t.bundleRevision}, + } + + err := bundleApi.NewWriter(w).Write(b) + if err != nil { + t.t.Fatal(err) + } + } else if r.URL.Path == "/status" || r.URL.Path == "/status/new" { + + var event any + + if err := util.NewJSONDecoder(r.Body).Decode(&event); err != nil { + t.t.Fatal(err) + } + + t.statusEvent = append(t.statusEvent, event) + + } else if r.URL.Path == "/logs" || r.URL.Path == "/newlogs" { + gr, err := gzip.NewReader(r.Body) + if err != nil { + t.t.Fatal(err) + } + var events []logs.EventV1 + if err := json.NewDecoder(gr).Decode(&events); err != nil { + t.t.Fatal(err) + } + if err := gr.Close(); err != nil { + t.t.Fatal(err) + } + + t.logEvent = append(t.logEvent, events...) + + } else { + t.t.Fatalf("unknown path %v", r.URL.Path) + } + +} + +func (t *testFixtureServer) start() { + t.server = httptest.NewServer(http.HandlerFunc(t.handle)) +} + +func (t *testFixtureServer) stop() { + t.server.Close() +} + +func ensurePluginState(t *testing.T, d *Discovery, state plugins.State) { + t.Helper() + status, ok := d.manager.PluginStatus()[Name] + if !ok { + t.Fatalf("Expected to find state for %s, found nil", Name) + return + } + if status.State != state { + t.Fatalf("Unexpected status state found in plugin manager for %s:\n\n\tFound:%+v\n\n\tExpected: %s", Name, status.State, state) + } +} diff --git a/third_party/opa/v1/plugins/logs/README.md b/third_party/opa/v1/plugins/logs/README.md new file mode 100644 index 000000000000..292ff3a61071 --- /dev/null +++ b/third_party/opa/v1/plugins/logs/README.md @@ -0,0 +1,88 @@ +# Decision Log Plugin + +The decision log plugin is responsible for gathering decision events from multiple sources and upload them to a service. +[This plugin is highly configurable](https://www.openpolicyagent.org/docs/latest/configuration/#decision-logs), allowing +the user to decide when to upload, drop or proxy a logged event. Each configuration can be dynamically updated while OPA is running. + +Events are uploaded in gzip compressed JSON array's at a user defined interval. This can either be triggered periodically +or manually through the SDK. The size of the gzip compressed JSON array is limited by `upload_size_limit_bytes`. + +There are two buffer implementations that can be selected by setting `decision_logs.reporting.buffer_type`, defaults to `size` + +## Event Buffer + +* `decision_logs.reporting.buffer_type=event` + +As events are logged each event is encoded and saved in a buffer. When an upload is triggered, all the events currently +in the buffer are uploaded in chunks (limited by `upload_size_limit_bytes`). The oldest events will drop if the buffer +is full, the limit can be configured by changing `buffer_size_limit_events`. + +Pros: +* Compressing only on upload keeps the event and JSON array buffers separate so they don't have to be in sync. +* Individual events can be dropped quicker without having to decompress the events. +* Using a channel as a buffer allows events to be written to the buffer concurrently. + +Cons: +* Upload will be slower as the events need to be compressed. +* A buffer limit has to be set, unlimited size isn't allowed. + +```mermaid +--- +title: Event Upload Flow +--- +flowchart LR + 1["Producer 1"] -. event .-> Buffer + 2["Producer 2"] -. event .-> Buffer + 3["Producer 3"] -. event .-> Buffer + subgraph log [Log Plugin] + Buffer --> package + subgraph package [JSON Array] + A["[event, event, event, event ....]"] + end + end + package -. POST .-> service + classDef large font-size:20pt; + +``` + +## Size Buffer + +* `decision_logs.reporting.buffer_type=size` + +As events are logged they are encoded and compressed into a JSON Array before being added to the buffer. When an upload +is triggered the current buffer is emptied, uploading each JSON Array of events as chunks. By default, the buffer is an +unlimited size but if `buffer_size_limit_bytes` is configured the oldest events will be dropped. + +Pros: +* Uploads are quicker because each event is already encoded and compressed. +* The local memory in bytes of the buffer can be limited. + +Cons: +* Events can flow between the encoder and buffer requiring a heavy use of locks. +* Dropping an individual event requires decompressing an entire array of events. +* Adding events to the buffer is slower as compression happens on write. + +```mermaid +--- +title: Event Upload Flow +--- +flowchart LR + 1["Producer 1"] -. event .-> Encoder + 2["Producer 2"] -. event .-> Encoder + 3["Producer 3"] -. event .-> Encoder + subgraph log [Log Plugin] + Encoder --> package + subgraph package [JSON Array] + A["[event, event, ...]"] + end + subgraph Buffer [Buffer] + B["[[event, event, ...], [event, event, ...]]"] + end + package --> Buffer + Buffer --> Encoder + + end + Buffer -. POST .-> service + classDef large font-size:20pt; + +``` \ No newline at end of file diff --git a/third_party/opa/v1/plugins/logs/buffer.go b/third_party/opa/v1/plugins/logs/buffer.go new file mode 100644 index 000000000000..b6e331bd5398 --- /dev/null +++ b/third_party/opa/v1/plugins/logs/buffer.go @@ -0,0 +1,64 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package logs + +import ( + "container/list" +) + +// logBuffer implements a circular FIFO buffer for the plugin that caps memory +// usage at the configured limit. If the buffer size is exceeded, events from +// the front of the buffer are dropped. +type logBuffer struct { + usage int64 + limit int64 + l *list.List +} + +type logBufferElem struct { + bs []byte +} + +func newLogBuffer(limit int64) *logBuffer { + return &logBuffer{ + limit: limit, + usage: 0, + l: list.New(), + } +} + +func (lb *logBuffer) Push(bs []byte) (dropped int) { + size := int64(len(bs)) + + if lb.limit > 0 { + for elem := lb.l.Front(); elem != nil && (lb.usage+size > lb.limit); elem = elem.Next() { + drop := elem.Value.(logBufferElem).bs + lb.l.Remove(elem) + lb.usage -= int64(len(drop)) + dropped++ + } + } + + elem := logBufferElem{bs} + + lb.l.PushBack(elem) + lb.usage += size + return dropped +} + +func (lb *logBuffer) Pop() []byte { + elem := lb.l.Front() + if elem != nil { + e := elem.Value.(logBufferElem) + lb.usage -= int64(len(e.bs)) + lb.l.Remove(elem) + return e.bs + } + return nil +} + +func (lb *logBuffer) Len() int { + return lb.l.Len() +} diff --git a/third_party/opa/v1/plugins/logs/buffer_test.go b/third_party/opa/v1/plugins/logs/buffer_test.go new file mode 100644 index 000000000000..b580d94385a6 --- /dev/null +++ b/third_party/opa/v1/plugins/logs/buffer_test.go @@ -0,0 +1,56 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package logs + +import ( + "bytes" + "testing" +) + +func TestLogBuffer(t *testing.T) { + + buffer := newLogBuffer(int64(20)) // 20 byte limit for test purposes + + dropped := buffer.Push(make([]byte, 20)) + if dropped != 0 { + t.Fatal("Expected dropped to be zero") + } + + bs := buffer.Pop() + if len(bs) != 20 { + t.Fatal("Expected buffer size to be 20") + } + + bs = buffer.Pop() + if bs != nil { + t.Fatal("Expected buffer to be nil") + } + + dropped = buffer.Push(bytes.Repeat([]byte(`1`), 10)) + if dropped != 0 { + t.Fatal("Expected dropped to be zero") + } + + dropped = buffer.Push(bytes.Repeat([]byte(`2`), 10)) + if dropped != 0 { + t.Fatal("Expected dropped to be zero") + } + + dropped = buffer.Push(bytes.Repeat([]byte(`3`), 10)) + if dropped != 1 { + t.Fatal("Expected dropped to be 1") + } + + bs = buffer.Pop() + exp := bytes.Repeat([]byte(`2`), 10) + if !bytes.Equal(bs, exp) { + t.Fatalf("Expected %v but got %v", exp, bs) + } + + if buffer.usage != 10 { + t.Fatalf("Expected buffer usage to be 10 but got %v", buffer.usage) + } + +} diff --git a/third_party/opa/v1/plugins/logs/encoder.go b/third_party/opa/v1/plugins/logs/encoder.go new file mode 100644 index 000000000000..408643cbe6b2 --- /dev/null +++ b/third_party/opa/v1/plugins/logs/encoder.go @@ -0,0 +1,470 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package logs + +import ( + "bytes" + "compress/gzip" + "encoding/json" + "math" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" +) + +const ( + encCompressedLimitThreshold = 0.9 + uncompressedLimitBaseFactor = 2 + uncompressedLimitExponentScaleFactor = 0.2 + logNDBDropCounterName = "decision_logs_nd_builtin_cache_dropped" + encLogExUploadSizeLimitCounterName = "enc_log_exceeded_upload_size_limit_bytes" + encUncompressedLimitScaleUpCounterName = "enc_uncompressed_limit_scale_up" + encUncompressedLimitScaleDownCounterName = "enc_uncompressed_limit_scale_down" + encUncompressedLimitStableCounterName = "enc_uncompressed_limit_stable" + encSoftLimitScaleUpCounterName = "enc_soft_limit_scale_up" // deprecated, use uncompressed version instead + encSoftLimitScaleDownCounterName = "enc_soft_limit_scale_down" // deprecated, use uncompressed version instead + encSoftLimitStableCounterName = "enc_soft_limit_stable" // deprecated, use uncompressed version instead + encNumberOfEventsInChunkHistogramName = "enc_events_written_in_chunk" +) + +// chunkEncoder implements log buffer chunking and compression. +// Decision events are written to the encoder and the encoder outputs chunks that are fit to the configured limit. +type chunkEncoder struct { + // limit is the maximum compressed payload size (configured by upload_size_limit_bytes) + limit int64 + threshold int + // bytesWritten is used to track if anything has been written to the buffer + // using this avoids working around the fact that the gzip compression adds a header + bytesWritten int + eventsWritten int64 + buf *bytes.Buffer + w *gzip.Writer + metrics metrics.Metrics + logger logging.Logger + // lastDroppedNDSize is a known size of an individual event that would require the ND cache to be dropped + lastDroppedNDSize int64 + + // The uncompressedLimit is an adaptive limit that will attempt to guess the uncompressedLimit based on the utilization of the buffer on upload. + // This minimizes having to decompress all the events in case the limit is reached, needing to only do it if the guess is too large. + // Otherwise, you would need to compress the incoming event by itself to get an accurate size for comparison which would cause two compressions each write. + // This means that at first the chunks will contain fewer events until the uncompressedLimit can grow to a stable state. + uncompressedLimit int64 + uncompressedLimitScaleUpExponent float64 + uncompressedLimitScaleDownExponent float64 +} + +func newChunkEncoder(limit int64) *chunkEncoder { + enc := &chunkEncoder{ + limit: limit, + uncompressedLimit: limit, + threshold: int(float64(limit) * encCompressedLimitThreshold), + uncompressedLimitScaleUpExponent: 0, + uncompressedLimitScaleDownExponent: 0, + } + enc.initialize() + + return enc +} + +func (enc *chunkEncoder) Reconfigure(limit int64) { + enc.limit = limit + enc.uncompressedLimit = limit + enc.uncompressedLimitScaleUpExponent = 0 + enc.uncompressedLimitScaleDownExponent = 0 + enc.threshold = int(float64(limit) * encCompressedLimitThreshold) + enc.lastDroppedNDSize = 0 +} + +// WithUncompressedLimit keep the adaptive uncompressed limit throughout the lifecycle of the size buffer +// this ensures that the uncompressed limit can grow/shrink appropriately as new data comes in +func (enc *chunkEncoder) WithUncompressedLimit(uncompressedLimit int64, uncompressedLimitScaleDownExponent float64, uncompressedLimitScaleUpExponent float64) *chunkEncoder { + enc.uncompressedLimit = uncompressedLimit + enc.uncompressedLimitScaleUpExponent = uncompressedLimitScaleUpExponent + enc.uncompressedLimitScaleDownExponent = uncompressedLimitScaleDownExponent + return enc +} + +func (enc *chunkEncoder) WithMetrics(m metrics.Metrics) *chunkEncoder { + enc.metrics = m + return enc +} + +func (enc *chunkEncoder) WithLogger(logger logging.Logger) *chunkEncoder { + enc.logger = logger + return enc +} + +func (enc *chunkEncoder) scaleUp() { + enc.incrMetric(encUncompressedLimitScaleUpCounterName) + enc.incrMetric(encSoftLimitScaleUpCounterName) + + mul := int64(math.Pow(float64(uncompressedLimitBaseFactor), enc.uncompressedLimitScaleUpExponent+1)) + enc.uncompressedLimit *= mul + enc.uncompressedLimitScaleUpExponent += uncompressedLimitExponentScaleFactor +} + +// Encode attempts to write an encoded event to the current chunk. +// A chunk is returned when it reaches the uncompressed limit, the uncompressed limit is adjusted if the buffer was underutilized or exceeded. +// An event is only dropped if it exceeds the limit after being compressed with or without dropping the Non-deterministic Cache (NDBuiltinCache). +// An event stays in the buffer until either a new event reaches the uncompressed limit or by calling Flush. +func (enc *chunkEncoder) Encode(event EventV1, eventBytes []byte) ([][]byte, error) { + // the incoming event is too big without dropping the ND cache + if enc.lastDroppedNDSize != 0 && int64(len(eventBytes)) >= enc.lastDroppedNDSize { + if event.NDBuiltinCache == nil { + enc.incrMetric(logEncodingFailureCounterName) + if enc.logger != nil { + enc.logger.Error("Log encoding failed: received a decision event size (%d) that exceeded the upload_size_limit_bytes (%d). No ND cache to drop.", + len(eventBytes), enc.limit) + } + return nil, nil + } + + // re-encode the event with the ND cache removed + event.NDBuiltinCache = nil + if enc.logger != nil { + enc.logger.Error("ND builtins cache dropped from this event to fit under maximum upload size limits. Increase upload size limit or change usage of non-deterministic builtins.") + } + enc.incrMetric(logNDBDropCounterName) + + var err error + eventBytes, err = json.Marshal(&event) + if err != nil { + return nil, err + } + } + + if int64(len(eventBytes)+enc.bytesWritten+1) <= enc.uncompressedLimit { + return nil, enc.appendEvent(eventBytes) + } + + // Adjust the encoder's uncompressed limit based on the current amount of + // data written to the underlying buffer. The uncompressed limit decides when to return a chunk. + // The uncompressed limit is modified based on the below algorithm: + // 1) Scale Up: If the current chunk size is below 90% of the user-configured limit, exponentially increase + // the uncompressed limit. The exponential function is 2^x where x has a minimum value of 1. + // A chunk will be returned with what was already written, the buffer was underutilized but the next time it shouldn't be. + // The incoming event is written to the next chunk. + // 2) Scale Down: If the current chunk size exceeds the compressed limit, decrease the uncompressed limit and re-encode the + // decisions in the last chunk. + // 3) Equilibrium: If the chunk size is between 90% and 100% of the user-configured limit, maintain uncompressed limit value. + // A chunk will be returned with what was already written, the uncompressed limit is ideal. + // The incoming event is written to the next chunk. + + // The uncompressed size is too small (it starts equal to the compressed limit) + // Or this is a recursive call to Write trying to split the events into separate chunks + if enc.bytesWritten == 0 { + // If an event is too large, there are multiple things to try before dropping the event: + // 1. Try to fit the incoming event into the next chunk without losing ND cache + if err := enc.appendEvent(eventBytes); err != nil { + return nil, err + } + + result, err := enc.reset() + if err != nil { + return nil, err + } + + currentSize := len(result) + if currentSize < int(enc.limit) { + // success! the incoming chunk doesn't have to lose the ND cache and can go into a chunk by itself + // scale up the uncompressed limit using the uncompressed event size as a base + err = enc.appendEvent(eventBytes) + if err != nil { + return nil, err + } + enc.uncompressedLimit = int64(len(eventBytes)) + enc.scaleUp() + return nil, nil + } + + // The ND cache has to be dropped, record this size as a known maximum event size + if enc.lastDroppedNDSize == 0 || int64(len(eventBytes)) < enc.lastDroppedNDSize { + enc.lastDroppedNDSize = int64(len(eventBytes)) + } + + // 2. Drop the ND cache and see if the incoming event can fit within the current chunk without the cache (so we can maximize chunk size) + enc.initialize() + enc.incrMetric(encLogExUploadSizeLimitCounterName) + // If there's no ND builtins cache in the event, then we don't need to retry encoding anything. + if event.NDBuiltinCache == nil { + enc.incrMetric(logEncodingFailureCounterName) + if enc.logger != nil { + enc.logger.Error("Log encoding failed: received a decision event size (%d) that exceeded the upload_size_limit_bytes (%d). No ND cache to drop.", currentSize, enc.limit) + } + return nil, nil + } + // re-encode the event with the ND cache removed + event.NDBuiltinCache = nil + + eventBytes, err = json.Marshal(&event) + if err != nil { + return nil, err + } + err = enc.appendEvent(eventBytes) + if err != nil { + return nil, err + } + + result, err = enc.reset() + if err != nil { + return nil, err + } + + if len(result) > int(enc.limit) { + enc.incrMetric(logEncodingFailureCounterName) + if enc.logger != nil { + enc.logger.Error("Log encoding failed: received a decision event size (%d) that exceeded the upload_size_limit_bytes (%d) even after dropping the ND cache.", + len(eventBytes), enc.limit) + } + enc.initialize() // drop the event + return nil, nil + } + + // success! the incoming event without its ND cache fits into the current chunk + err = enc.appendEvent(eventBytes) + if err != nil { + return nil, err + } + if enc.logger != nil { + enc.logger.Error("ND builtins cache dropped from this event to fit under maximum upload size limits. Increase upload size limit or change usage of non-deterministic builtins.") + } + enc.incrMetric(logNDBDropCounterName) + // success! the incoming chunk lost the ND cache, but it wasn't dropped entirely + // scale up the uncompressed limit using the uncompressed event size as a base + if int64(len(eventBytes)) > enc.uncompressedLimit { + enc.uncompressedLimit = int64(len(eventBytes)) + } + enc.scaleUp() + return nil, nil + } + + enc.updateMetric(encNumberOfEventsInChunkHistogramName, enc.eventsWritten) + result, err := enc.reset() + if err != nil { + return nil, err + } + + // 1) Scale Up: If the current chunk size is below 90% of the user-configured limit, exponentially increase + // the uncompressed limit. The exponential function is 2^x where x has a minimum value of 1 + if len(result) < enc.threshold { + enc.scaleUp() + + results := [][]byte{result} + + r, err := enc.Encode(event, eventBytes) + if err != nil { + return results, err + } + + if r != nil { + results = append(results, r...) + } + + return results, nil + } + + // 3) Equilibrium: If the chunk size is between 90% and 100% of the user-configured limit, maintain uncompressed limit value. + if int(enc.limit) > len(result) && len(result) >= enc.threshold { + enc.incrMetric(encUncompressedLimitStableCounterName) + enc.incrMetric(encSoftLimitStableCounterName) + + enc.uncompressedLimitScaleDownExponent = enc.uncompressedLimitScaleUpExponent + + results := [][]byte{result} + + r, err := enc.Encode(event, eventBytes) + if err != nil { + return results, err + } + + if r != nil { + results = append(results, r...) + } + + return results, nil + } + + // 2) Scale Down: If the current chunk size exceeds the compressed limit, decrease the uncompressed limit and re-encode the + // decisions in the last chunk. + events, err := newChunkDecoder(result).decode() + if err != nil { + return nil, err + } + + // add the current event so that it can be reorganized as needed + events = append(events, event) + + return enc.scaleDown(events) +} + +func (enc *chunkEncoder) scaleDown(events []EventV1) ([][]byte, error) { + if enc.uncompressedLimit > enc.limit { + enc.incrMetric(encUncompressedLimitScaleDownCounterName) + enc.incrMetric(encSoftLimitScaleDownCounterName) + + if enc.uncompressedLimitScaleDownExponent < enc.uncompressedLimitScaleUpExponent { + enc.uncompressedLimitScaleDownExponent = enc.uncompressedLimitScaleUpExponent + } + + den := int64(math.Pow(float64(uncompressedLimitBaseFactor), enc.uncompressedLimitScaleDownExponent-enc.uncompressedLimitScaleUpExponent+1)) + enc.uncompressedLimit /= den + + if enc.uncompressedLimitScaleUpExponent > 0 { + enc.uncompressedLimitScaleUpExponent -= uncompressedLimitExponentScaleFactor + } + } + + // The uncompressed limit has grown too large the events need to be split up into multiple chunks + enc.initialize() + + // split the events into multiple chunks + var result [][]byte + for i := range events { + eventBytes, err := json.Marshal(&events[i]) + if err != nil { + return nil, err + } + + // recursive call to make sure the chunk created adheres to the uncompressed size limit + chunks, err := enc.Encode(events[i], eventBytes) + if err != nil { + return nil, err + } + + if chunks != nil { + result = append(result, chunks...) + } + } + + return result, nil +} + +func (enc *chunkEncoder) appendEvent(event []byte) error { + if len(event) == 0 { + return nil + } + + if enc.bytesWritten == 0 { + n, err := enc.w.Write([]byte(`[`)) + if err != nil { + return err + } + enc.bytesWritten += n + } else { + n, err := enc.w.Write([]byte(`,`)) + if err != nil { + return err + } + enc.bytesWritten += n + } + + n, err := enc.w.Write(event) + if err != nil { + return err + } + enc.bytesWritten += n + enc.eventsWritten++ + + return nil +} + +func (enc *chunkEncoder) writeClose() error { + if _, err := enc.w.Write([]byte(`]`)); err != nil { + return err + } + return enc.w.Close() +} + +// Flush closes the current buffer and returns all the events written in chunks limited by the compressed limit. +// If the uncompressed size has grown too much it could require multiple decoding calls to size it down. +func (enc *chunkEncoder) Flush() ([][]byte, error) { + if enc.bytesWritten == 0 { + return nil, nil + } + + defer enc.initialize() + + var result [][]byte + + // create chunks until the current buffer is smaller than the limit + for { + r, err := enc.reset() + if err != nil { + return nil, err + } + if len(r) < int(enc.limit) { + return append(result, r), nil + } + events, err := newChunkDecoder(r).decode() + if err != nil { + return nil, err + } + chunk, err := enc.scaleDown(events) + if err != nil { + return nil, err + } + if chunk != nil { + result = append(result, chunk...) + } + } +} + +func (enc *chunkEncoder) reset() ([]byte, error) { + if enc.bytesWritten == 0 { + return nil, nil + } + + defer enc.initialize() + + if err := enc.writeClose(); err != nil { + return nil, err + } + + return enc.buf.Bytes(), nil +} + +func (enc *chunkEncoder) initialize() { + enc.buf = new(bytes.Buffer) + enc.bytesWritten = 0 + enc.eventsWritten = 0 + enc.w = gzip.NewWriter(enc.buf) +} + +func (enc *chunkEncoder) incrMetric(name string) { + if enc.metrics != nil { + enc.metrics.Counter(name).Incr() + } +} + +func (enc *chunkEncoder) updateMetric(name string, value int64) { + if enc.metrics != nil { + enc.metrics.Histogram(name).Update(value) + } +} + +// chunkDecoder decodes the encoded chunks and outputs the log events +type chunkDecoder struct { + raw []byte +} + +func newChunkDecoder(raw []byte) *chunkDecoder { + return &chunkDecoder{ + raw: raw, + } +} + +func (dec *chunkDecoder) decode() ([]EventV1, error) { + gr, err := gzip.NewReader(bytes.NewReader(dec.raw)) + if err != nil { + return nil, err + } + + var events []EventV1 + if err := json.NewDecoder(gr).Decode(&events); err != nil { + return nil, err + } + + return events, gr.Close() +} diff --git a/third_party/opa/v1/plugins/logs/encoder_test.go b/third_party/opa/v1/plugins/logs/encoder_test.go new file mode 100644 index 000000000000..d7aea8c974f6 --- /dev/null +++ b/third_party/opa/v1/plugins/logs/encoder_test.go @@ -0,0 +1,505 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package logs + +import ( + "encoding/json" + "strconv" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/logging" + testLogger "github.com/open-policy-agent/opa/v1/logging/test" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func eventWithNDCache() EventV1 { + // Purposely oversize NDBCache entry will force dropping during Log(). + ndbCacheExample := ast.MustJSON(builtins.NDBCache{ + "test.custom_space_waster": ast.NewObject([2]*ast.Term{ + ast.ArrayTerm(), + ast.StringTerm(strings.Repeat("Wasted space... ", 200)), + }), + }.AsValue()) + var result any = false + var expInput any = map[string]any{"method": "GET"} + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + return EventV1{ + DecisionID: "abc", + Path: "foo/bar", + Input: &expInput, + Result: &result, + RequestedBy: "test", + Timestamp: ts, + NDBuiltinCache: &ndbCacheExample, + } +} + +func TestLastDroppedNDSize(t *testing.T) { + enc := newChunkEncoder(200).WithMetrics(metrics.New()) + + if enc.lastDroppedNDSize != 0 { + t.Errorf("expected 0 got %d", enc.lastDroppedNDSize) + } + + event := eventWithNDCache() + + eventBytes, err := json.Marshal(&event) + if err != nil { + t.Fatal(err) + } + chunk, err := enc.Encode(event, eventBytes) + if err != nil { + t.Fatal(err) + } + + var expectedChunks int + if len(chunk) != expectedChunks { + t.Errorf("expected %v chunks, got %d", expectedChunks, len(chunk)) + } + + expectedBytesWritten := 157 // size after dropping the ND cache + if enc.bytesWritten != expectedBytesWritten { + t.Errorf("Expected %d bytes written but got %d", expectedBytesWritten, enc.bytesWritten) + } + + expectedLastDroppedSize := int64(3414) // size before dropping the ND cache + if enc.lastDroppedNDSize != expectedLastDroppedSize { + t.Errorf("expected %v got %d", expectedLastDroppedSize, enc.lastDroppedNDSize) + } + + eventBytes, err = json.Marshal(&event) + if err != nil { + t.Fatal(err) + } + chunk, err = enc.Encode(event, eventBytes) + if err != nil { + t.Fatal(err) + } + + if len(chunk) != expectedChunks { + t.Errorf("expected %v chunks, got %d", expectedChunks, len(chunk)) + } + + expectedBytesWritten = 314 // size of two events written + if enc.bytesWritten != expectedBytesWritten { + t.Errorf("Expected %d bytes written but got %d", expectedBytesWritten, enc.bytesWritten) + } + + if enc.lastDroppedNDSize != expectedLastDroppedSize { + t.Errorf("expected %v got %d", expectedLastDroppedSize, enc.lastDroppedNDSize) + } +} + +func TestChunkMaxUploadSizeLimitNDBCacheDropping(t *testing.T) { + t.Parallel() + + // note this only tests the size buffer type so that the encoder is used immediately on log + tests := []struct { + name string + uploadSizeLimitBytes int64 + expectedDroppedNDCacheEvents uint64 + expectedBytesWritten int + expectedUncompressedLimit int64 + expectedEncodingFailures uint64 + }{ + { + name: "drop ND cache to fit", + uploadSizeLimitBytes: 200, + expectedDroppedNDCacheEvents: 1, + // written after dropping the ND cache, otherwise the size would have been 3472 + expectedBytesWritten: 157, + expectedUncompressedLimit: 400, + }, + { + name: "dropping the ND cache doesn't help, drop the event", + uploadSizeLimitBytes: 120, + expectedUncompressedLimit: 120, // never gets adjusted + expectedEncodingFailures: 1, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + enc := newChunkEncoder(tc.uploadSizeLimitBytes).WithMetrics(metrics.New()) + + event := eventWithNDCache() + + eventBytes, err := json.Marshal(&event) + if err != nil { + t.Fatal(err) + } + chunk, err := enc.Encode(event, eventBytes) + if err != nil { + t.Fatal(err) + } + + if enc.metrics.Counter(logEncodingFailureCounterName).Value().(uint64) != tc.expectedEncodingFailures { + t.Errorf("Expected %d dropped events but got %d", tc.expectedDroppedNDCacheEvents, enc.metrics.Counter(logNDBDropCounterName)) + } + + if enc.metrics.Counter(logNDBDropCounterName).Value().(uint64) != tc.expectedDroppedNDCacheEvents { + t.Errorf("Expected %d dropped events but got %d", tc.expectedDroppedNDCacheEvents, enc.metrics.Counter(logNDBDropCounterName)) + } + + if chunk != nil { + t.Errorf("expected nil result but got %v", chunk) + } + + if enc.bytesWritten != tc.expectedBytesWritten { + t.Errorf("Expected %d bytes written but got %d", tc.expectedBytesWritten, enc.bytesWritten) + } + + if enc.uncompressedLimit != tc.expectedUncompressedLimit { + t.Errorf("Expected %d uncompressed limit but got %d", tc.expectedUncompressedLimit, enc.uncompressedLimit) + } + }) + } +} + +func TestChunkEncoder(t *testing.T) { + t.Parallel() + + enc := newChunkEncoder(1000) + var result any = false + var expInput any = map[string]any{"method": "GET"} + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + event := EventV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + }, + Revision: "a", + DecisionID: "a", + Path: "foo/bar", + Input: &expInput, + Result: &result, + RequestedBy: "test", + Timestamp: ts, + } + + eventBytes, err := json.Marshal(&event) + if err != nil { + t.Fatal(err) + } + bs, err := enc.Encode(event, eventBytes) + if bs != nil || err != nil { + t.Fatalf("Unexpected error or chunk produced: err: %v", err) + } + + bs, err = enc.Flush() + if bs == nil || err != nil { + t.Fatalf("Unexpected error or NO chunk produced: err: %v", err) + } + + bs, err = enc.Flush() + if bs != nil || err != nil { + t.Fatalf("Unexpected error chunk produced: err: %v", err) + + } +} + +func TestChunkEncoderSizeLimit(t *testing.T) { + t.Parallel() + + enc := newChunkEncoder(90).WithMetrics(metrics.New()) + var result any = false + var expInput any = map[string]any{"method": "GET"} + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + t.Fatal(err) + } + + // test adding a small event that would fit in the minUploadSizeLimitBytes + smallestEvent := EventV1{ + DecisionID: "1", + } + + eventBytes, err := json.Marshal(&smallestEvent) + if err != nil { + t.Fatal(err) + } + chunks, err := enc.Encode(smallestEvent, eventBytes) + if err != nil { + t.Fatal(err) + } + if len(chunks) != 0 { + t.Errorf("Unexpected result: %v", result) + } + if err := enc.w.Flush(); err != nil { + t.Fatal(err) + } + // expect the event to be written because it fits the minimum event size + expectedBufferSize := 78 // the compressed size of an absurd small event + if enc.buf.Len() != expectedBufferSize { + t.Errorf("Expected %v buffer size but got: %v", expectedBufferSize, enc.buf.Len()) + } + expectedBytesWritten := 69 // the uncompressed size of the event + if enc.bytesWritten != expectedBytesWritten { + t.Errorf("Expected %v bytes written but got: %v", expectedBytesWritten, enc.bytesWritten) + } + expectedEventsWritten := int64(1) + if enc.eventsWritten != expectedEventsWritten { + t.Errorf("Expected %v events written but got: %v", expectedEventsWritten, enc.eventsWritten) + } + + // write an event that is too big + event := EventV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + }, + DecisionID: "123", + Path: "foo/bar", + Input: &expInput, + Result: &result, + RequestedBy: "test", + Timestamp: ts, + } + + logger := testLogger.New() + enc.WithLogger(logger) + eventBytes, err = json.Marshal(&event) + if err != nil { + t.Fatal(err) + } + chunks, err = enc.Encode(event, eventBytes) + if err != nil { + t.Fatal(err) + } + if len(chunks) != 1 { + t.Errorf("Unexpected result: %v", result) + } + // the incoming event doesn't fit, but the previous small event size is between 90-100% capacity so chunk is returned + // the incoming event is too large and will be dropped + actualStableCounter := enc.metrics.Counter(encUncompressedLimitStableCounterName).Value().(uint64) + expectedStableCounter := uint64(1) + if actualStableCounter != expectedStableCounter { + t.Errorf("Expected %d encoding failure but got: %d", expectedStableCounter, actualStableCounter) + } + if err := enc.w.Flush(); err != nil { + t.Fatal(err) + } + expectedBufferSize = 15 + if enc.buf.Len() != expectedBufferSize { + t.Errorf("Expected %v buffer size but got: %v", expectedBufferSize, enc.buf.Len()) + } + expectedBytesWritten = 0 + if enc.bytesWritten != expectedBytesWritten { + t.Errorf("Expected %v bytes written but got: %v", expectedBytesWritten, enc.bytesWritten) + } + expectedEventsWritten = 0 + if enc.eventsWritten != expectedEventsWritten { + t.Errorf("Expected %v events written but got: %v", expectedEventsWritten, enc.eventsWritten) + } + + entries := logger.Entries() + expectedEntries := 1 + if len(entries) != expectedEntries { + t.Fatalf("Expected %v log entry but got: %v", expectedEntries, len(entries)) + } + if entries[0].Level != logging.Error && + entries[0].Message != "Log encoding failed: received a decision event size (176) that exceeded the upload_size_limit_bytes (25). No ND cache to drop." { + t.Errorf("Unexpected log entry: %v", entries[0]) + } + if enc.metrics.Counter(encUncompressedLimitScaleDownCounterName).Value().(uint64) != 0 { + t.Fatalf("Expected zero uncompressed limit scale down: %v", enc.metrics.Counter(encUncompressedLimitScaleDownCounterName).Value().(uint64)) + } + if enc.metrics.Counter(encLogExUploadSizeLimitCounterName).Value().(uint64) != 1 { + t.Fatalf("Expected one upload size limit exceed but got: %v", enc.metrics.Counter(encLogExUploadSizeLimitCounterName).Value().(uint64)) + } + if enc.metrics.Counter(logEncodingFailureCounterName).Value().(uint64) != 1 { + t.Errorf("Expected one encoding failure but got: %v", enc.metrics.Counter(logEncodingFailureCounterName).Value().(uint64)) + } +} + +func TestChunkEncoderAdaptive(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + limit int64 + numEvents int + expectedUncompressedLimit int64 + expectedMaxEventsInChunk int64 + expectedScaleUpEvents uint64 + expectedScaleDownEvents uint64 + expectedEquiEvents uint64 + }{ + { + // only one event can fit, after one scale up the uncompressed limit falls within the 90-100% utilization + name: "an uncompressed limit that stabilizes immediately", + limit: 201, + numEvents: 1000, + expectedUncompressedLimit: 464, + expectedMaxEventsInChunk: 1, + expectedScaleUpEvents: 1, + expectedScaleDownEvents: 0, + expectedEquiEvents: 999, + }, + { + // 61 events can fit, but takes some guessing before it gets to the uncompressed limit 7200 + name: "an uncompressed limit that stabilizes after a few guesses", + limit: 400, + numEvents: 1000, + expectedUncompressedLimit: 7200, + expectedMaxEventsInChunk: 61, + expectedScaleUpEvents: 5, + expectedScaleDownEvents: 2, + expectedEquiEvents: 31, + }, + { + // it is possible to set a limit that the algorithm fails to stabilize, it is just guessing + name: "an uncompressed limit that doesn't stabilize", + limit: 1000, + numEvents: 1000, + expectedUncompressedLimit: 40500, + expectedMaxEventsInChunk: 341, + expectedScaleUpEvents: 14, + expectedScaleDownEvents: 11, + expectedEquiEvents: 0, + }, + { + // a different limit can stabilize + name: "larger limit that does stabilize", + limit: 3000, + numEvents: 2000, + expectedUncompressedLimit: 108000, + expectedMaxEventsInChunk: 908, + expectedScaleUpEvents: 5, + expectedScaleDownEvents: 1, + expectedEquiEvents: 3, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + enc := newChunkEncoder(tc.limit).WithMetrics(metrics.New()) + var result any = false + var expInput any = map[string]any{"method": "GET"} + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + var chunks [][]byte + for i := range tc.numEvents { + + bundles := map[string]BundleInfoV1{} + bundles["authz"] = BundleInfoV1{Revision: strconv.Itoa(i)} + + // uncompressed the size of an event is 232 bytes + // when compressed by itself this event is 186 bytes + event := EventV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + }, + Bundles: bundles, + DecisionID: strconv.Itoa(i), + Path: "foo/bar", + Input: &expInput, + Result: &result, + RequestedBy: "test", + Timestamp: ts, + } + + eventBytes, err := json.Marshal(&event) + if err != nil { + t.Fatal(err) + } + chunk, err := enc.Encode(event, eventBytes) + if err != nil { + t.Fatal(err) + } + if chunk != nil { + chunks = append(chunks, chunk...) + } + } + + if enc.uncompressedLimit != tc.expectedUncompressedLimit { + t.Errorf("Expected %v uncompressed limit but got %v", tc.expectedUncompressedLimit, enc.uncompressedLimit) + } + + h := enc.metrics.Histogram(encNumberOfEventsInChunkHistogramName).Value().(map[string]any) + if h["max"].(int64) != tc.expectedMaxEventsInChunk { + t.Errorf("Expected %v max events in a chunk, got %v", tc.expectedMaxEventsInChunk, h["max"].(int64)) + } + + // decode the chunks and check the number of events is equal to the encoded events + actualEvents := decodeChunks(t, chunks) + + // flush the encoder + for { + bs, err := enc.Flush() + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(bs) == 0 { + break + } + + actualEvents = append(actualEvents, decodeChunks(t, bs)...) + } + + if tc.numEvents != len(actualEvents) { + t.Fatalf("Expected %v events but got %v", tc.numEvents, len(actualEvents)) + } + + // make sure there aren't any missing IDs + for i := range actualEvents { + id, err := strconv.Atoi(actualEvents[i].DecisionID) + if err != nil { + t.Fatal(err) + } + if id != i { + t.Fatalf("Expected decision ID %d but got %d", i, id) + } + } + + actualScaleUpEvents := enc.metrics.Counter(encUncompressedLimitScaleUpCounterName).Value().(uint64) + actualScaleDownEvents := enc.metrics.Counter(encUncompressedLimitScaleDownCounterName).Value().(uint64) + actualEquiEvents := enc.metrics.Counter(encUncompressedLimitStableCounterName).Value().(uint64) + + if actualScaleUpEvents != tc.expectedScaleUpEvents { + t.Errorf("Expected scale up events %v but got %v", tc.expectedScaleUpEvents, actualScaleUpEvents) + } + + if actualScaleDownEvents != tc.expectedScaleDownEvents { + t.Errorf("Expected scale down events %v but got %v", tc.expectedScaleDownEvents, actualScaleDownEvents) + } + + if actualEquiEvents != tc.expectedEquiEvents { + t.Errorf("Expected equilibrium events %v but got %v", tc.expectedEquiEvents, actualEquiEvents) + } + }) + } +} + +func decodeChunks(t *testing.T, bs [][]byte) []EventV1 { + t.Helper() + + var events []EventV1 + for _, chunk := range bs { + e, err := newChunkDecoder(chunk).decode() + if err != nil { + t.Fatal(err) + } + events = append(events, e...) + } + return events +} diff --git a/third_party/opa/v1/plugins/logs/eventBuffer.go b/third_party/opa/v1/plugins/logs/eventBuffer.go new file mode 100644 index 000000000000..b7ac610b66ff --- /dev/null +++ b/third_party/opa/v1/plugins/logs/eventBuffer.go @@ -0,0 +1,178 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package logs + +import ( + "context" + "encoding/json" + "sync" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins/rest" + "github.com/open-policy-agent/opa/v1/util" +) + +type bufferItem struct { + *EventV1 // an individual event + chunk []byte // a ready to upload compressed JSON Array of events +} + +// eventBuffer stores and uploads a gzip compressed JSON array of EventV1 entries +type eventBuffer struct { + buffer chan *bufferItem // buffer stores JSON encoded EventV1 data + upload sync.Mutex // upload controls that uploads are done sequentially + client rest.Client // client is used to upload the data to the configured service + uploadPath string // uploadPath is the configured HTTP resource path for upload + enc *chunkEncoder // encoder appends events into the gzip compressed JSON array + metrics metrics.Metrics + logger logging.Logger +} + +func newEventBuffer(bufferSizeLimitEvents int64, client rest.Client, uploadPath string, uploadSizeLimitBytes int64) *eventBuffer { + return &eventBuffer{ + buffer: make(chan *bufferItem, bufferSizeLimitEvents), + client: client, + uploadPath: uploadPath, + enc: newChunkEncoder(uploadSizeLimitBytes), + } +} + +func (b *eventBuffer) WithMetrics(m metrics.Metrics) *eventBuffer { + b.metrics = m + b.enc.metrics = m + return b +} + +func (b *eventBuffer) WithLogger(l logging.Logger) *eventBuffer { + b.logger = l + return b +} + +func (b *eventBuffer) incrMetric(name string) { + if b.metrics != nil { + b.metrics.Counter(name).Incr() + } +} + +// Reconfigure updates the user configurable values +// This cannot be called concurrently, this could change the underlying channel. +// Plugin manages a lock to control this so that changes to both buffer types can be managed sequentially. +func (b *eventBuffer) Reconfigure(bufferSizeLimitEvents int64, client rest.Client, uploadPath string, uploadSizeLimitBytes int64) { + // prevent an upload from pushing events that failed to upload back into a closed buffer + b.upload.Lock() + defer b.upload.Unlock() + + b.client = client + b.uploadPath = uploadPath + + if int64(cap(b.buffer)) == bufferSizeLimitEvents { + return + } + + b.enc.Reconfigure(uploadSizeLimitBytes) + + close(b.buffer) + oldBuffer := b.buffer + b.buffer = make(chan *bufferItem, bufferSizeLimitEvents) + + for event := range oldBuffer { + b.push(event) + } +} + +// Push attempts to add a new event to the buffer, returning true if an event was dropped. +// This can be called concurrently. +func (b *eventBuffer) Push(event *EventV1) { + b.push(&bufferItem{EventV1: event}) +} + +func (b *eventBuffer) push(event *bufferItem) { + util.PushFIFO(b.buffer, event, b.metrics, logBufferEventDropCounterName) +} + +// Upload reads events from the buffer and uploads them to the configured client. +// All the events currently in the buffer are read and written to a gzip compressed JSON array to create a chunk of data. +// Each chunk is limited by the uploadSizeLimitBytes. +func (b *eventBuffer) Upload(ctx context.Context) error { + b.upload.Lock() + defer b.upload.Unlock() + + eventLen := len(b.buffer) + if eventLen == 0 { + return &bufferEmpty{} + } + + for range eventLen { + event := b.readEvent() + if event == nil { + break + } + + var result [][]byte + if event.chunk != nil { + result = [][]byte{event.chunk} + } else { + eventBytes, err := json.Marshal(&event) + if err != nil { + return err + } + + result, err = b.enc.Encode(*event.EventV1, eventBytes) + if err != nil { + b.incrMetric(logEncodingFailureCounterName) + if b.logger != nil { + b.logger.Error("encoding failure: %v, dropping event with decision ID: %v", err, event.DecisionID) + } + } + } + + if err := b.uploadChunks(ctx, result); err != nil { + return err + } + } + + // flush any chunks that didn't hit the upload limit + result, err := b.enc.Flush() + if err != nil { + b.incrMetric(logEncodingFailureCounterName) + if b.logger != nil { + b.logger.Error("encoding failure: %v", err) + } + return nil + } + + if err := b.uploadChunks(ctx, result); err != nil { + return err + } + + return nil +} + +// uploadChunks attempts to upload multiple chunks to the configured client. +// In case of failure all the events are added back to the buffer. +func (b *eventBuffer) uploadChunks(ctx context.Context, result [][]byte) error { + var finalErr error + for _, chunk := range result { + err := uploadChunk(ctx, b.client, b.uploadPath, chunk) + + // if an upload failed, requeue the chunk + if err != nil { + finalErr = err + b.push(&bufferItem{chunk: chunk}) + } + } + return finalErr +} + +// readEvent does a nonblocking read from the event buffer +func (b *eventBuffer) readEvent() *bufferItem { + select { + case event := <-b.buffer: + return event + default: + return nil + } +} diff --git a/third_party/opa/v1/plugins/logs/eventBuffer_test.go b/third_party/opa/v1/plugins/logs/eventBuffer_test.go new file mode 100644 index 000000000000..d53b4fa56e8d --- /dev/null +++ b/third_party/opa/v1/plugins/logs/eventBuffer_test.go @@ -0,0 +1,255 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package logs + +import ( + "compress/gzip" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "strconv" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins/rest" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func TestEventBuffer_Push(t *testing.T) { + t.Parallel() + + expectedIds := make(map[string]struct{}) + var expectedDropped uint64 + limit := int64(2) + b := newEventBuffer(limit, rest.Client{}, "", 0).WithMetrics(metrics.New()) + + id := "id1" + expectedIds[id] = struct{}{} + b.Push(newTestEvent(t, id, false)) + checkBufferState(t, limit, b, expectedDropped, expectedIds) + + id = "id2" + expectedIds[id] = struct{}{} + b.Push(newTestEvent(t, id, false)) + checkBufferState(t, limit, b, expectedDropped, expectedIds) + + id = "id3" + expectedIds[id] = struct{}{} + b.Push(newTestEvent(t, id, false)) + // Three events were pushed, but limit is 2 so the oldest even should have been dropped + delete(expectedIds, "id1") + expectedDropped++ + checkBufferState(t, limit, b, expectedDropped, expectedIds) + + if int64(len(b.buffer)) != limit { + t.Fatalf("buffer size mismatch, expected %d, got %d", limit, len(b.buffer)) + } + + // Increase the limit, forcing the buffer to change + limit = int64(3) + b.Reconfigure(limit, rest.Client{}, "", 0) + checkBufferState(t, limit, b, expectedDropped, expectedIds) + + id = "id4" + expectedIds[id] = struct{}{} + b.Push(newTestEvent(t, id, false)) + checkBufferState(t, limit, b, expectedDropped, expectedIds) + + id = "id5" + expectedIds[id] = struct{}{} + b.Push(newTestEvent(t, id, true)) + // Four events were pushed, but limit is 3 so the oldest even should have been dropped + expectedDropped++ + delete(expectedIds, "id2") + checkBufferState(t, limit, b, expectedDropped, expectedIds) + + limit = int64(1) + b.Reconfigure(limit, rest.Client{}, "", 0) + // Limit reconfigured from 3->1, dropping 2 more events. + expectedDropped = 4 + delete(expectedIds, "id3") + delete(expectedIds, "id4") + checkBufferState(t, limit, b, expectedDropped, expectedIds) + + // Nothing changed + b.Reconfigure(limit, rest.Client{}, "", 0) + checkBufferState(t, limit, b, expectedDropped, expectedIds) +} + +func checkBufferState(t *testing.T, limit int64, b *eventBuffer, expectedDropped uint64, expectedIds map[string]struct{}) { + t.Helper() + + dropped := b.metrics.Counter(logBufferEventDropCounterName).Value().(uint64) + if dropped != expectedDropped { + t.Fatalf("number of dropped event mismatch, expected %d, got %d", expectedDropped, dropped) + } + + if len(b.buffer) != len(expectedIds) { + t.Fatalf("buffer size mismatch, expected %d, got %d", len(expectedIds), len(b.buffer)) + } + + close(b.buffer) + newBuffer := make(chan *bufferItem, limit) + for event := range b.buffer { + if _, ok := expectedIds[event.DecisionID]; !ok { + t.Fatalf("received unexpected event %v", event) + } + newBuffer <- event + } + + b.buffer = newBuffer +} + +func TestEventBuffer_Upload(t *testing.T) { + t.Parallel() + + uploadPath := "/v1/test" + + tests := []struct { + name string + eventLimit int64 + numberOfEvents int + uploadSizeLimitBytes int64 + handleFunc func(w http.ResponseWriter, r *http.Request) + expectedError string + }{ + { + name: "Upload everything in the buffer", + eventLimit: 4, + numberOfEvents: 3, + uploadSizeLimitBytes: defaultUploadSizeLimitBytes, + handleFunc: func(w http.ResponseWriter, r *http.Request) { + events := decodeLogEvent(t, r.Body) + if len(events) != 3 { + t.Errorf("expected 3 events, got %d", len(events)) + } + + w.WriteHeader(http.StatusOK) + }, + }, + { + name: "Upload in chunks determined by upload size limit", + eventLimit: 4, + numberOfEvents: 4, + uploadSizeLimitBytes: 196, // Each test event is 195 bytes + handleFunc: func(w http.ResponseWriter, r *http.Request) { + events := decodeLogEvent(t, r.Body) + if len(events) != 2 { + t.Errorf("expected 2 events, got %d", len(events)) + } + w.WriteHeader(http.StatusOK) + }, + }, + { + name: "Get error from failed upload", + eventLimit: 1, + numberOfEvents: 1, + uploadSizeLimitBytes: defaultUploadSizeLimitBytes, + handleFunc: func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusBadRequest) + }, + expectedError: "log upload failed, server replied with HTTP 400 Bad Request", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + client, ts := setupTestServer(t, uploadPath, tc.handleFunc) + defer ts.Close() + e := newEventBuffer(tc.eventLimit, client, uploadPath, tc.uploadSizeLimitBytes).WithMetrics(metrics.New()).WithLogger(logging.NewNoOpLogger()) + + for i := range tc.numberOfEvents { + e.Push(newTestEvent(t, strconv.Itoa(i), true)) + } + + err := e.Upload(context.Background()) + if err != nil { + if tc.expectedError == "" || tc.expectedError != "" && err.Error() != tc.expectedError { + t.Fatal(err) + } + } + }) + } +} + +func newTestEvent(t *testing.T, id string, enableNDCache bool) *EventV1 { + var result any = false + var expInput any = map[string]any{"method": "GET"} + timestamp, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + t.Fatal(err) + } + e := EventV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + }, + DecisionID: id, + Path: "foo/bar", + Input: &expInput, + Result: &result, + RequestedBy: "test", + Timestamp: timestamp, + } + + if enableNDCache { + var ndbCacheExample = ast.MustJSON(builtins.NDBCache{ + "time.now_ns": ast.NewObject([2]*ast.Term{ + ast.ArrayTerm(), + ast.NumberTerm("1663803565571081429"), + }), + }.AsValue()) + e.NDBuiltinCache = &ndbCacheExample + } + + return &e +} + +func setupTestServer(t *testing.T, uploadPath string, handleFunc func(w http.ResponseWriter, r *http.Request)) (rest.Client, *httptest.Server) { + mux := http.NewServeMux() + ts := httptest.NewServer(mux) + + mux.HandleFunc(uploadPath, handleFunc) + + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + "response_header_timeout_seconds": 20, + }`, ts.URL) + ks := map[string]*keys.Config{} + client, err := rest.New([]byte(config), ks) + if err != nil { + t.Fatal(err) + } + + return client, ts +} + +func decodeLogEvent(t *testing.T, r io.Reader) []EventV1 { + t.Helper() + + gr, err := gzip.NewReader(r) + if err != nil { + t.Fatal(err) + } + + var events []EventV1 + if err := json.NewDecoder(gr).Decode(&events); err != nil { + t.Fatal(err) + } + + if err := gr.Close(); err != nil { + t.Fatal(err) + } + + return events +} diff --git a/third_party/opa/v1/plugins/logs/mask.go b/third_party/opa/v1/plugins/logs/mask.go new file mode 100644 index 000000000000..98a55329e433 --- /dev/null +++ b/third_party/opa/v1/plugins/logs/mask.go @@ -0,0 +1,423 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package logs + +import ( + "errors" + "fmt" + "net/url" + "slices" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/internal/deepcopy" +) + +type maskOP string + +const ( + maskOPRemove maskOP = "remove" + maskOPUpsert maskOP = "upsert" + + partInput = "input" + partResult = "result" + partNDBCache = "nd_builtin_cache" +) + +var errMaskInvalidObject = errors.New("mask upsert invalid object") + +type maskRule struct { + OP maskOP `json:"op"` + Path string `json:"path"` + Value any `json:"value"` + escapedParts []string + modifyFullObj bool + failUndefinedPath bool +} + +type maskRuleSet struct { + OnRuleError func(*maskRule, error) + Rules []*maskRule + resultCopied bool +} + +func (r maskRule) String() string { + return "/" + strings.Join(r.escapedParts, "/") +} + +type maskRuleOption func(*maskRule) error + +func newMaskRule(path string, opts ...maskRuleOption) (*maskRule, error) { + const ( + defaultOP = maskOPRemove + defaultFailUndefinedPath = false + ) + + if len(path) == 0 { + return nil, errors.New("mask must be non-empty") + } else if !strings.HasPrefix(path, "/") { + return nil, errors.New("mask must be slash-prefixed") + } + + parts := strings.Split(path[1:], "/") + + switch parts[0] { + case partInput, partResult, partNDBCache: // OK + default: + return nil, fmt.Errorf("mask prefix not allowed: %v", parts[0]) + } + + escapedParts := make([]string, len(parts)) + for i := range parts { + _, err := url.PathUnescape(parts[i]) + if err != nil { + return nil, err + } + + escapedParts[i] = url.PathEscape(parts[i]) + } + + var modifyFullObj bool + if len(escapedParts) == 1 { + modifyFullObj = true + } + + r := &maskRule{ + OP: defaultOP, + Path: path, + escapedParts: escapedParts, + failUndefinedPath: defaultFailUndefinedPath, + modifyFullObj: modifyFullObj, + } + + for _, opt := range opts { + if err := opt(r); err != nil { + return nil, err + } + } + return r, nil +} + +func withOP(op maskOP) maskRuleOption { + return func(r *maskRule) error { + switch op { + case maskOPRemove, maskOPUpsert: + r.OP = op + return nil + } + return fmt.Errorf("mask op is not supported: %s", op) + } +} + +func withValue(val any) maskRuleOption { + return func(r *maskRule) error { + r.Value = val + return nil + } +} + +func withFailUndefinedPath() maskRuleOption { + return func(r *maskRule) error { + r.failUndefinedPath = true + return nil + } +} + +func (r maskRule) Mask(event *EventV1) error { + var maskObj *any // pointer to event Input|Result|NDBCache object + var maskObjPtr **any // pointer to the event Input|Result|NDBCache pointer itself + + switch p := r.escapedParts[0]; p { + case partInput: + if event.Input == nil { + if r.failUndefinedPath { + return errMaskInvalidObject + } + return nil + } + maskObj = event.Input + maskObjPtr = &event.Input + case partResult: + if event.Result == nil { + if r.failUndefinedPath { + return errMaskInvalidObject + } + return nil + } + maskObj = event.Result + maskObjPtr = &event.Result + case partNDBCache: + if event.NDBuiltinCache == nil { + if r.failUndefinedPath { + return errMaskInvalidObject + } + return nil + } + maskObj = event.NDBuiltinCache + maskObjPtr = &event.NDBuiltinCache + default: + return fmt.Errorf("illegal path value: %s", p) + } + + switch r.OP { + case maskOPRemove: + if r.modifyFullObj { + *maskObjPtr = nil + } else { + err := r.removeValue(r.escapedParts[1:], *maskObj) + if err != nil { + if err == errMaskInvalidObject && r.failUndefinedPath { + return err + } + return nil + } + } + + event.Erased = append(event.Erased, r.String()) + case maskOPUpsert: + if r.modifyFullObj { + *maskObjPtr = &r.Value + } else { + inputObj, ok := (*maskObj).(map[string]any) + if !ok { + return nil + } + + if err := r.mkdirp(inputObj, r.escapedParts[1:len(r.escapedParts)], r.Value); err != nil { + if r.failUndefinedPath { + return err + } + + return nil + } + } + + event.Masked = append(event.Masked, r.String()) + default: + return fmt.Errorf("illegal mask op value: %s", r.OP) + } + + return nil +} + +func (maskRule) removeValue(p []string, node any) error { + if len(p) == 0 { + return nil + } + + // the key or index to be removed + targetKey := p[len(p)-1] + + // nodeParent stores the parent of the node to be modified during the + // removal, this is only needed when the node is a slice + var nodeParent any + // nodeKey stores the key of the node to be modified relative to the parent + var nodeKey string + + // Walk to the parent of the target to be removed, the nodeParent is cached + // support removing of slice values + for i := range len(p) - 1 { + switch v := node.(type) { + case map[string]any: + child, ok := v[p[i]] + if !ok { + return errMaskInvalidObject + } + nodeParent = v + nodeKey = p[i] + node = child + + case []any: + index, err := strconv.Atoi(p[i]) + if err != nil || index < 0 || index >= len(v) { + return errMaskInvalidObject + } + nodeParent = v + nodeKey = p[i] + node = v[index] + + default: + return errMaskInvalidObject + } + } + + switch v := node.(type) { + case map[string]any: + if _, ok := v[targetKey]; !ok { + return errMaskInvalidObject + } + + delete(v, targetKey) + + case []any: + // first, check the targetKey is a valid index + targetIndex, err := strconv.Atoi(targetKey) + if err != nil || targetIndex < 0 || targetIndex >= len(v) { + return errMaskInvalidObject + } + + switch nodeParent := nodeParent.(type) { + case []any: + // update the target's grandparent slice with a new slice + index, err := strconv.Atoi(nodeKey) + if err != nil { + return errMaskInvalidObject + } + + nodeParent[index] = slices.Delete(v, targetIndex, targetIndex+1) + + case map[string]any: + nodeParent[nodeKey] = slices.Delete(v, targetIndex, targetIndex+1) + + default: + return errMaskInvalidObject + } + + default: + return errMaskInvalidObject + } + + return nil +} + +func (maskRule) mkdirp(node any, path []string, value any) error { + if len(path) == 0 { + return nil + } + + for i := range len(path) - 1 { + switch v := node.(type) { + case map[string]any: + child, ok := v[path[i]] + if !ok { + child = map[string]any{} + v[path[i]] = child + } + + node = child + + case []any: + idx, err := strconv.Atoi(path[i]) + if err != nil || idx < 0 { + return errMaskInvalidObject + } + + for len(v) <= idx { + v = append(v, nil) + } + + node = v[idx] + + default: + return errMaskInvalidObject + } + } + + switch v := node.(type) { + case map[string]any: + v[path[len(path)-1]] = value + + case []any: + idx, err := strconv.Atoi(path[len(path)-1]) + if err != nil || idx < 0 || idx >= len(v) { + return errMaskInvalidObject + } + v[idx] = value + + default: + return errMaskInvalidObject + } + + return nil +} + +func newMaskRuleSet(rv any, onRuleError func(*maskRule, error)) (*maskRuleSet, error) { + mRuleSet := &maskRuleSet{ + OnRuleError: onRuleError, + } + rawRules, ok := rv.([]any) + if !ok { + return nil, fmt.Errorf("unexpected rule format %v (%[1]T)", rv) + } + + for _, iface := range rawRules { + switch v := iface.(type) { + + case string: + // preserve default behavior of remove when + // structured mask format is not provided + rule, err := newMaskRule(v) + if err != nil { + return nil, err + } + + mRuleSet.Rules = append(mRuleSet.Rules, rule) + + case map[string]any: + rule := &maskRule{} + op, set := getString(v, "op") + if set && op == "" { + return nil, fmt.Errorf("invalid \"op\" value: %v %[1]T", v["op"]) + } + rule.OP = maskOP(op) + + path, set := getString(v, "path") + if set && path == "" { + return nil, fmt.Errorf("invalid \"path\" value: %v %[1]T", v["path"]) + } + rule.Path = path + + rule.Value = v["value"] + + // use unmarshalled values to create new Mask Rule + rule, err := newMaskRule(rule.Path, withOP(rule.OP), withValue(rule.Value)) + // TODO add withFailUndefinedPath() option based on + // A) new syntax in user defined mask rule + // B) passed in/global configuration option + // rule precedence A>B + if err != nil { + return nil, err + } + + mRuleSet.Rules = append(mRuleSet.Rules, rule) + + default: + return nil, fmt.Errorf("invalid mask rule format encountered: %T", v) + } + } + + return mRuleSet, nil +} + +func (rs maskRuleSet) Mask(event *EventV1) { + for _, mRule := range rs.Rules { + // result must be deep copied if there are any mask rules + // targeting it, to avoid modifying the result sent + // to the consumer + if mRule.escapedParts[0] == partResult && event.Result != nil && !rs.resultCopied { + resultCopy := deepcopy.DeepCopy(*event.Result) + event.Result = &resultCopy + rs.resultCopied = true + } + err := mRule.Mask(event) + if err != nil { + rs.OnRuleError(mRule, err) + } + } +} + +// bool return means the field was set, if the string is still "", the +// value was invalid +func getString(x map[string]any, key string) (string, bool) { + y, ok := x[key] + if !ok { + return "", false + } + s, ok := y.(string) + if !ok { + return "", true + } + return s, true +} diff --git a/third_party/opa/v1/plugins/logs/mask_test.go b/third_party/opa/v1/plugins/logs/mask_test.go new file mode 100644 index 000000000000..5a2453303932 --- /dev/null +++ b/third_party/opa/v1/plugins/logs/mask_test.go @@ -0,0 +1,811 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. +package logs + +import ( + "bytes" + "encoding/json" + "errors" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/util" +) + +func TestNewMaskRule(t *testing.T) { + tests := []struct { + note string + input *maskRule + expErr error + expPtr *maskRule + }{ + { + note: "empty", + input: &maskRule{ + OP: maskOPRemove, + Path: "", + }, + expErr: errors.New("mask must be non-empty"), + }, + { + note: "missing slash", + input: &maskRule{ + OP: maskOPRemove, + Path: "foo", + }, + expErr: errors.New("mask must be slash-prefixed"), + }, + { + note: "no prefix", + input: &maskRule{ + OP: maskOPRemove, + Path: "/", + }, + expErr: errors.New("mask prefix not allowed"), + }, + { + note: "bad prefix key", + input: &maskRule{ + OP: maskOPRemove, + Path: "/labels/foo", + }, + expErr: errors.New("mask prefix not allowed"), + }, + { + note: "standard", + input: &maskRule{ + OP: maskOPRemove, + Path: "/input/a/b/c", + }, + expPtr: &maskRule{ + OP: maskOPRemove, + Path: "/input/a/b/c", + escapedParts: []string{"input", "a", "b", "c"}, + }, + }, + { + note: "fail with object path undefined", + input: &maskRule{ + OP: maskOPRemove, + Path: "/input/a/b/c", + failUndefinedPath: true, + }, + expPtr: &maskRule{ + OP: maskOPRemove, + Path: "/input/a/b/c", + failUndefinedPath: true, + escapedParts: []string{"input", "a", "b", "c"}, + }, + }, + { + note: "fail with invalid OP", + input: &maskRule{ + OP: maskOP("undefinedOP"), + Path: "/input/a/b/c", + }, + expErr: errors.New("mask op is not supported: undefinedOP"), + }, + { + note: "escaping", + input: &maskRule{ + OP: maskOPRemove, + Path: "/input/a/%2F%2F/b", + }, + expPtr: &maskRule{ + OP: maskOPRemove, + Path: "/input/a/%2F%2F/b", + escapedParts: []string{"input", "a", "%252F%252F", "b"}, + }, + }, + { + note: "bad escape", + input: &maskRule{ + OP: maskOPRemove, + Path: "/input/a/%F/b", + }, + expErr: errors.New("invalid URL escape"), + }, + { + note: "empty component", + input: &maskRule{ + OP: maskOPRemove, + Path: "/input//foo", + }, + expPtr: &maskRule{ + OP: maskOPRemove, + Path: "/input//foo", + escapedParts: []string{"input", "", "foo"}, + }, + }, + { + note: "result", + input: &maskRule{ + OP: maskOPRemove, + Path: "/result/a", + }, + expPtr: &maskRule{ + OP: maskOPRemove, + Path: "/result/a", + escapedParts: []string{"result", "a"}, + }, + }, + { + note: "root", + input: &maskRule{ + OP: maskOPRemove, + Path: "/input", + }, + expPtr: &maskRule{ + OP: maskOPRemove, + Path: "/input", + escapedParts: []string{"input"}, + modifyFullObj: true, + }, + }, + { + note: "unsupported mask op", + input: &maskRule{ + OP: maskOP("unsupported"), + Path: "/input", + }, + expErr: errors.New("mask op is not supported: unsupported"), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + result, err := newMaskRule(tc.input.Path, withOP(tc.input.OP), withValue(tc.input.Value)) + if tc.input.failUndefinedPath { + _ = withFailUndefinedPath()(result) + } + + if tc.expErr != nil { + if err == nil { + t.Fatalf("Expected error but got: %v", result) + } else if !strings.Contains(err.Error(), tc.expErr.Error()) { + t.Fatalf("Expected error: %v, but got error: %v", tc.expErr, err) + } + } else { + if err != nil { + t.Fatal("Unexpected error:", err) + } + if !reflect.DeepEqual(result, tc.expPtr) { + t.Fatalf("Expected %#+v but got %#+v", tc.expPtr, result) + } + } + }) + } +} + +func TestMaskRuleMask(t *testing.T) { + tests := []struct { + note string + ptr *maskRule + event string + exp string + expErr error + }{ + { + note: "erase input", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input", + }, + event: `{"input": {"a": 1}}`, + exp: `{"erased": ["/input"]}`, + }, + { + note: "upsert input", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input", + Value: struct { + RandoString string + }{RandoString: "foo"}, + }, + event: `{"input": {"a": 1}}`, + exp: `{"masked": ["/input"], "input": {"RandoString": "foo"}}`, + }, + { + note: "erase result", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/result", + }, + event: `{"result": "foo"}`, + exp: `{"erased": ["/result"]}`, + }, + { + note: "upsert result", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/result", + Value: "upserted", + }, + event: `{"result": "foo"}`, + exp: `{"masked": ["/result"], "result": "upserted"}`, + }, + { + note: "erase undefined input", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo", + }, + event: `{}`, + exp: `{}`, + }, + { + note: "erase undefined input: fail unknown object path on", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo", + failUndefinedPath: true, + }, + event: `{}`, + exp: `{}`, + expErr: errMaskInvalidObject, + }, + { + note: "upsert undefined input", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo", + }, + event: `{}`, + exp: `{}`, + }, + { + note: "upsert undefined input: fail unknown object path on", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo", + failUndefinedPath: true, + }, + event: `{}`, + exp: `{}`, + expErr: errMaskInvalidObject, + }, + { + note: "erase undefined result", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/result/foo", + }, + event: `{}`, + exp: `{}`, + }, + { + note: "erase undefined result: fail unknown object path on", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/result/foo", + failUndefinedPath: true, + }, + event: `{}`, + exp: `{}`, + expErr: errMaskInvalidObject, + }, + { + note: "upsert undefined result", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/result/foo", + }, + event: `{}`, + exp: `{}`, + }, + { + note: "upsert undefined result: fail unknown object path on", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/result/foo", + failUndefinedPath: true, + }, + event: `{}`, + exp: `{}`, + expErr: errMaskInvalidObject, + }, + { + note: "erase undefined node", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo", + }, + event: `{"input": {"bar": 1}}`, + exp: `{"input": {"bar": 1}}`, + }, + { + note: "erase undefined node: fail unknown object path on", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo", + failUndefinedPath: true, + }, + event: `{"input": {"bar": 1}}`, + exp: `{"input": {"bar": 1}}`, + expErr: errMaskInvalidObject, + }, + { + note: "upsert undefined node with nil value", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo", + }, + event: `{"input": {"bar": 1}}`, + exp: `{"input": {"bar": 1, "foo": null}, "masked": ["/input/foo"]}`, + }, + { + note: "upsert undefined node with nil value: fail unknown object path on", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo", + failUndefinedPath: true, + }, + event: `{"input": {"bar": 1}}`, + exp: `{"input": {"bar": 1, "foo": null}, "masked": ["/input/foo"]}`, + expErr: errMaskInvalidObject, + }, + { + note: "upsert undefined node with a value", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo", + Value: "upserted", + }, + event: `{"input": {"bar": 1}}`, + exp: `{"input": {"bar": 1, "foo": "upserted"}, "masked": ["/input/foo"]}`, + }, + { + note: "erase undefined node-2", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo/bar", + }, + event: `{"input": {"foo": 1}}`, + exp: `{"input": {"foo": 1}}`, + }, + { + note: "upsert unsupported nested object type (json.Number) #1", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo/bar", + }, + event: `{"input": {"foo": 1}}`, + exp: `{"input": {"foo": 1}}`, + }, + { + note: "upsert unsupported nested object type (string) #1", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo/bar", + }, + event: `{"input": {"foo": "bar"}}`, + exp: `{"input": {"foo": "bar"}}`, + }, + { + note: "erase: undefined object: missing key", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo/bar/baz", + }, + event: `{"input": {"foo": {}}}`, + exp: `{"input": {"foo": {}}}`, + }, + { + note: "upsert: undefined object: missing key, no value", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo/bar/baz", + }, + event: `{"input": {"foo": {}}}`, + exp: `{"input": {"foo": {"bar": {"baz": null}}}, "masked": ["/input/foo/bar/baz"]}`, + }, + { + note: "upsert: undefined object: missing key, provided value", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo/bar/baz", + Value: 100, + }, + event: `{"input": {"foo": {}}}`, + exp: `{"input": {"foo": {"bar": {"baz": 100}}}, "masked": ["/input/foo/bar/baz"]}`, + }, + { + note: "erase: undefined scalar", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo/bar/baz", + }, + event: `{"input": {"foo": 1}}`, + exp: `{"input": {"foo": 1}}`, + }, + { + note: "upsert: unsupported nested object type (json.Number) #2", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo/bar/baz", + }, + event: `{"input": {"foo": 1}}`, + exp: `{"input": {"foo": 1}}`, + }, + { + note: "erase: undefined array: non-int index", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo/bar/baz", // bar is invalid + }, + event: `{"input": {"foo": [{"baz": 1}]}}`, + exp: `{"input": {"foo": [{"baz": 1}]}}`, + }, + { + note: "upsert: unsupported type: []interface {}", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo/bar/baz", // foo is []interface + }, + event: `{"input": {"foo": [{"baz": 1}]}}`, + exp: `{"input": {"foo": [{"baz": 1}]}}`, + }, + { + note: "erase: undefined array: negative index", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo/-1/baz", + }, + event: `{"input": {"foo": [{"baz": 1}]}}`, + exp: `{"input": {"foo": [{"baz": 1}]}}`, + }, + { + note: "upsert: undefined array: negative index", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo/-1/baz", // foo is an []interface {} + }, + event: `{"input": {"foo": [{"baz": 1}]}}`, + exp: `{"input": {"foo": [{"baz": 1}]}}`, + }, + { + note: "erase: undefined array: index out of range", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo/1/baz", + }, + event: `{"input": {"foo": [{"baz": 1}]}}`, + exp: `{"input": {"foo": [{"baz": 1}]}}`, + }, + { + note: "upsert: unsupported nested object type (array) #1", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo/1/baz", // foo is an []interface {} + }, + event: `{"input": {"foo": [{"baz": 1}]}}`, + exp: `{"input": {"foo": [{"baz": 1}]}}`, + }, + { + note: "erase: array: remove element", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo/0", + }, + event: `{"input": {"foo": [1]}}`, + exp: `{"input": {"foo": []}, "erased": ["/input/foo/0"]}`, + }, + { + note: "erase: array: remove element with deeper nesting", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo/0/bar/1", + }, + event: `{"input": {"foo": [{"bar": [1, 2]}]}}`, + exp: `{"input": {"foo": [{"bar": [1]}]}, "erased": ["/input/foo/0/bar/1"]}`, + }, + { + note: "erase: array: remove element that does not exist", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo/0/bar/9", + }, + event: `{"input": {"foo": [{"bar": [1, 2]}]}}`, + exp: `{"input": {"foo": [{"bar": [1, 2]}]}}`, + }, + { + note: "upsert: array: upsert element", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo/0", + Value: 2, + }, + event: `{"input": {"foo": [1]}}`, + exp: `{"input": {"foo": [2]}, "masked": ["/input/foo/0"]}`, + }, + { + note: "upsert: array: upsert nested array element", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo/0/bar/0", + Value: 2, + }, + event: `{"input": {"foo": [{"bar": [1]}]}}`, + exp: `{"input": {"foo": [{"bar": [2]}]}, "masked": ["/input/foo/0/bar/0"]}`, + }, + { + note: "upsert: array: upsert element in 2d array", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo/0/0", + Value: 2, + }, + event: `{"input": {"foo": [[1]]}}`, + exp: `{"input": {"foo": [[2]]}, "masked": ["/input/foo/0/0"]}`, + }, + { + note: "upsert: array: upsert element that does not exist", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo/1", + Value: 2, + }, + event: `{"input": {"foo": [1]}}`, + exp: `{"input": {"foo": [1]}}`, + }, + { + note: "erase: object key", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo", + }, + event: `{"input": {"bar": 1, "foo": [{"baz": 1}]}}`, + exp: `{"input": {"bar": 1}, "erased": ["/input/foo"]}`, + }, + { + note: "upsert: object key", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/foo", + Value: []map[string]int{{"nabs": 1}}, + }, + event: `{"input": {"bar": 1, "foo": [{"baz": 1}]}}`, + exp: `{"input": {"bar": 1, "foo": [{"nabs": 1}]}, "masked": ["/input/foo"]}`, + }, + { + note: "erase: object key (multiple)", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/bar", + }, + event: `{"input": {"bar": 1}, "erased": ["/input/foo"]}`, + exp: `{"input": {}, "erased": ["/input/foo", "/input/bar"]}`, + }, + { + note: "erase: object key (nested array)", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/foo/0/bar", + }, + event: `{"input": {"foo": [{"bar": 1, "baz": 2}]}}`, + exp: `{"input": {"foo": [{"baz": 2}]}, "erased": ["/input/foo/0/bar"]}`, + }, + { + note: "erase input: special character in path", + ptr: &maskRule{ + OP: maskOPRemove, + Path: "/input/:path", + }, + event: `{"input": {"bar": 1, ":path": "token"}}`, + exp: `{"input": {"bar": 1}, "erased": ["/input/:path"]}`, + }, + { + note: "upsert input: special character in path", + ptr: &maskRule{ + OP: maskOPUpsert, + Path: "/input/:path", + Value: "upserted", + }, + event: `{"input": {"bar": 1, ":path": "token"}}`, + exp: `{"input": {"bar": 1, ":path": "upserted"}, "masked": ["/input/:path"]}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ptr, err := newMaskRule(tc.ptr.Path, withOP(tc.ptr.OP), withValue(tc.ptr.Value)) + if tc.ptr.failUndefinedPath { + _ = withFailUndefinedPath()(ptr) + } + + if err != nil { + panic(err) + } + + var exp EventV1 + if err := util.UnmarshalJSON([]byte(tc.exp), &exp); err != nil { + panic(err) + } + + var event EventV1 + if err := util.UnmarshalJSON([]byte(tc.event), &event); err != nil { + panic(err) + } + + err = ptr.Mask(&event) + if err != nil { + if tc.expErr == nil { + t.Fatalf("no expected error, but received '%s'", err.Error()) + } + if tc.expErr.Error() != err.Error() { + t.Fatalf("expected error '%s', got '%s'", tc.expErr.Error(), err.Error()) + } + + } + + // compare via json marshall to map tc input types + bs1, _ := json.MarshalIndent(exp, "", " ") + bs2, _ := json.MarshalIndent(event, "", " ") + if !bytes.Equal(bs1, bs2) { + t.Fatalf("Expected: %s\nGot: %s", string(bs1), string(bs2)) + } + }) + } +} + +func TestNewMaskRuleSet(t *testing.T) { + tests := []struct { + note string + value any + exp *maskRuleSet + err error + }{ + { + note: "invalid format: not []any", + value: map[string]int{"invalid": 1}, + err: errors.New("unexpected rule format map[invalid:1] (map[string]int)"), + }, + { + note: "invalid format: nested type not string or map[string]any", + value: []any{ + []int{1, 2}, + }, + err: errors.New("invalid mask rule format encountered: []int"), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + _, err := newMaskRuleSet(tc.value, func(_ *maskRule, _ error) {}) + if err != nil { + if exp, act := tc.err.Error(), err.Error(); exp != act { + t.Fatalf("Expected: %s\nGot: %s", exp, act) + } + } else if tc.err != nil { + t.Errorf("expected error %v, got nil", tc.err) + } + }) + } +} + +func TestMaskRuleSetMask(t *testing.T) { + tests := []struct { + note string + rules []*maskRule + event string + exp string + expErr error + }{ + { + note: "erase input", + rules: []*maskRule{ + { + OP: maskOPRemove, + Path: "/input", + }, + }, + event: `{"input": {"a": 1}}`, + exp: `{"erased": ["/input"]}`, + }, + { + note: "erase result", + rules: []*maskRule{ + { + OP: maskOPRemove, + Path: "/result", + }, + }, + event: `{"result": {"a": 1}}`, + exp: `{"erased": ["/result"]}`, + }, + { + note: "erase input and result nested", + rules: []*maskRule{ + { + OP: maskOPRemove, + Path: "/input/a/b", + }, + { + OP: maskOPRemove, + Path: "/result/c/d", + }, + }, + event: `{"input":{"a":{"b":"removeme","y":"stillhere"}},"result":{"c":{"d":"removeme","z":"stillhere"}}}`, + exp: `{"input":{"a":{"y":"stillhere"}},"result":{"c":{"z":"stillhere"}},"erased":["/input/a/b", "/result/c/d"]}`, + }, + { + note: "expected rule error", + rules: []*maskRule{ + { + OP: maskOPRemove, + Path: "/result", + failUndefinedPath: true, + }, + }, + event: `{"input":"foo"}`, + exp: `{"input":"foo"}`, + expErr: errMaskInvalidObject, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ptr := &maskRuleSet{} + var ruleErr error + if tc.expErr != nil { + ptr.OnRuleError = func(_ *maskRule, err error) { + ruleErr = err + } + } else { + ptr.OnRuleError = func(mRule *maskRule, err error) { + t.Fatalf("unexpected rule error, rule: %s, error: %s", mRule.String(), err.Error()) + } + } + for _, rule := range tc.rules { + var mRule *maskRule + var err error + if rule.failUndefinedPath { + mRule, err = newMaskRule(rule.Path, withOP(rule.OP), withValue(rule.Value), withFailUndefinedPath()) + } else { + mRule, err = newMaskRule(rule.Path, withOP(rule.OP), withValue(rule.Value)) + } + if err != nil { + panic(err) + } + ptr.Rules = append(ptr.Rules, mRule) + } + + var exp EventV1 + if err := util.UnmarshalJSON([]byte(tc.exp), &exp); err != nil { + panic(err) + } + + var event EventV1 + var origEvent EventV1 + if err := util.UnmarshalJSON([]byte(tc.event), &event); err != nil { + panic(err) + } + origEvent = event + + ptr.Mask(&event) + + // compare via json marshall to map tc input types + bs1, _ := json.MarshalIndent(exp, "", " ") + bs2, _ := json.MarshalIndent(event, "", " ") + if !bytes.Equal(bs1, bs2) { + t.Fatalf("Expected: %s\nGot: %s", string(bs1), string(bs2)) + } + + if origEvent.Result != nil && reflect.DeepEqual(origEvent.Result, event.Result) { + t.Fatal("Expected event.Result to be deep copied during masking, so that the event's original Result is not modified") + } + + if tc.expErr != nil { + if ruleErr == nil { + t.Fatalf("Expected: %s\nGot:%s", tc.expErr.Error(), "nil") + } + if tc.expErr != ruleErr { + t.Fatalf("Expected: %s\nGot:%s", tc.expErr.Error(), ruleErr.Error()) + } + } + }) + } +} diff --git a/third_party/opa/v1/plugins/logs/plugin.go b/third_party/opa/v1/plugins/logs/plugin.go new file mode 100644 index 000000000000..fd6909bc498f --- /dev/null +++ b/third_party/opa/v1/plugins/logs/plugin.go @@ -0,0 +1,1225 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package logs implements decision log buffering and uploading. +package logs + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "math" + "math/rand" + "net/url" + "reflect" + "slices" + "strings" + "sync" + "time" + + "golang.org/x/time/rate" + + "github.com/open-policy-agent/opa/internal/ref" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + lstat "github.com/open-policy-agent/opa/v1/plugins/logs/status" + "github.com/open-policy-agent/opa/v1/plugins/rest" + "github.com/open-policy-agent/opa/v1/plugins/status" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/server" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/util" +) + +// Logger defines the interface for decision logging plugins. +type Logger interface { + plugins.Plugin + + Log(context.Context, EventV1) error +} + +// EventV1 represents a decision log event. +// WARNING: The AST() function for EventV1 must be kept in sync with +// the struct. Any changes here MUST be reflected in the AST() +// implementation below. +type EventV1 struct { + Labels map[string]string `json:"labels"` + DecisionID string `json:"decision_id"` + BatchDecisionID string `json:"batch_decision_id,omitempty"` + TraceID string `json:"trace_id,omitempty"` + SpanID string `json:"span_id,omitempty"` + Revision string `json:"revision,omitempty"` // Deprecated: Use Bundles instead + Bundles map[string]BundleInfoV1 `json:"bundles,omitempty"` + Path string `json:"path,omitempty"` + Query string `json:"query,omitempty"` + Input *any `json:"input,omitempty"` + Result *any `json:"result,omitempty"` + IntermediateResults map[string]any `json:"intermediate_results,omitempty"` + MappedResult *any `json:"mapped_result,omitempty"` + NDBuiltinCache *any `json:"nd_builtin_cache,omitempty"` + Erased []string `json:"erased,omitempty"` + Masked []string `json:"masked,omitempty"` + Error error `json:"error,omitempty"` + RequestedBy string `json:"requested_by,omitempty"` + Timestamp time.Time `json:"timestamp"` + Metrics map[string]any `json:"metrics,omitempty"` + RequestID uint64 `json:"req_id,omitempty"` + RequestContext *RequestContext `json:"request_context,omitempty"` + Custom map[string]any `json:"custom,omitempty"` + + inputAST ast.Value +} + +// BundleInfoV1 describes a bundle associated with a decision log event. +type BundleInfoV1 struct { + Revision string `json:"revision,omitempty"` +} + +type RequestContext struct { + HTTPRequest *HTTPRequestContext `json:"http,omitempty"` +} + +type HTTPRequestContext struct { + Headers map[string][]string `json:"headers,omitempty"` +} + +// AST returns the BundleInfoV1 as an AST value +func (b *BundleInfoV1) AST() ast.Value { + result := ast.NewObject() + if len(b.Revision) > 0 { + result.Insert(ast.InternedTerm("revision"), ast.StringTerm(b.Revision)) + } + return result +} + +// AST returns the Rego AST representation for a given EventV1 object. +// This avoids having to round trip through JSON while applying a decision log +// mask policy to the event. +func (e *EventV1) AST() (ast.Value, error) { + var err error + event := ast.NewObject( + ast.Item(ast.InternedTerm("decision_id"), ast.StringTerm(e.DecisionID)), + ) + + if e.BatchDecisionID != "" { + event.Insert(ast.InternedTerm("batch_decision_id"), ast.StringTerm(e.BatchDecisionID)) + } + + if e.Labels != nil { + labelsObj := ast.NewObject() + for k, v := range e.Labels { + labelsObj.Insert(ast.StringTerm(k), ast.StringTerm(v)) + } + event.Insert(ast.InternedTerm("labels"), ast.NewTerm(labelsObj)) + } else { + event.Insert(ast.InternedTerm("labels"), ast.NullTerm()) + } + + if len(e.Revision) > 0 { + event.Insert(ast.InternedTerm("revision"), ast.StringTerm(e.Revision)) + } + + if len(e.Bundles) > 0 { + bundlesObj := ast.NewObject() + for k, v := range e.Bundles { + bundlesObj.Insert(ast.StringTerm(k), ast.NewTerm(v.AST())) + } + event.Insert(ast.InternedTerm("bundles"), ast.NewTerm(bundlesObj)) + } + + if len(e.Path) > 0 { + event.Insert(ast.InternedTerm("path"), ast.StringTerm(e.Path)) + } + + if len(e.Query) > 0 { + event.Insert(ast.InternedTerm("query"), ast.StringTerm(e.Query)) + } + + if e.inputAST != nil { + event.Insert(ast.InternedTerm("input"), ast.NewTerm(e.inputAST)) + } else if e.Input != nil { + e.inputAST, err = roundtripJSONToAST(e.Input) + if err != nil { + return nil, err + } + event.Insert(ast.InternedTerm("input"), ast.NewTerm(e.inputAST)) + } + + if e.Result != nil { + results, err := roundtripJSONToAST(e.Result) + if err != nil { + return nil, err + } + event.Insert(ast.InternedTerm("result"), ast.NewTerm(results)) + } + + if len(e.IntermediateResults) > 0 { + iresults, err := roundtripJSONToAST(e.IntermediateResults) + if err != nil { + return nil, err + } + event.Insert(ast.InternedTerm("intermediate_results"), ast.NewTerm(iresults)) + } + + if e.MappedResult != nil { + mResults, err := roundtripJSONToAST(e.MappedResult) + if err != nil { + return nil, err + } + event.Insert(ast.InternedTerm("mapped_result"), ast.NewTerm(mResults)) + } + + if e.NDBuiltinCache != nil { + ndbCache, err := roundtripJSONToAST(e.NDBuiltinCache) + if err != nil { + return nil, err + } + event.Insert(ast.InternedTerm("nd_builtin_cache"), ast.NewTerm(ndbCache)) + } + + if len(e.Erased) > 0 { + erased := make([]*ast.Term, len(e.Erased)) + for i, v := range e.Erased { + erased[i] = ast.StringTerm(v) + } + event.Insert(ast.InternedTerm("erased"), ast.ArrayTerm(erased...)) + } + + if len(e.Masked) > 0 { + masked := make([]*ast.Term, len(e.Masked)) + for i, v := range e.Masked { + masked[i] = ast.StringTerm(v) + } + event.Insert(ast.InternedTerm("masked"), ast.ArrayTerm(masked...)) + } + + if e.Error != nil { + evalErr, err := roundtripJSONToAST(e.Error) + if err != nil { + return nil, err + } + event.Insert(ast.InternedTerm("error"), ast.NewTerm(evalErr)) + } + + if len(e.RequestedBy) > 0 { + event.Insert(ast.InternedTerm("requested_by"), ast.StringTerm(e.RequestedBy)) + } + + // Use the timestamp JSON marshaller to ensure the format is the same as + // round tripping through JSON. + timeBytes, err := e.Timestamp.MarshalJSON() + if err != nil { + return nil, err + } + event.Insert(ast.InternedTerm("timestamp"), ast.StringTerm(strings.Trim(string(timeBytes), "\""))) + + if e.Metrics != nil { + m, err := ast.InterfaceToValue(e.Metrics) + if err != nil { + return nil, err + } + event.Insert(ast.InternedTerm("metrics"), ast.NewTerm(m)) + } + + if e.RequestID > 0 { + event.Insert(ast.InternedTerm("req_id"), ast.UIntNumberTerm(e.RequestID)) + } + + if len(e.Custom) > 0 { + custom, err := roundtripJSONToAST(e.Custom) + if err != nil { + return nil, err + } + event.Insert(ast.InternedTerm("custom"), ast.NewTerm(custom)) + } + + return event, nil +} + +func roundtripJSONToAST(x any) (ast.Value, error) { + rawPtr := util.Reference(x) + // roundtrip through json: this turns slices (e.g. []string, []bool) into + // []any, the only array type ast.InterfaceToValue can work with + if err := util.RoundTrip(rawPtr); err != nil { + return nil, err + } + + return ast.InterfaceToValue(*rawPtr) +} + +const ( + // min amount of time to wait following a failure + minRetryDelay = time.Millisecond * 100 + defaultMinDelaySeconds = int64(300) + defaultMaxDelaySeconds = int64(600) + defaultBufferSizeLimitEvents = int64(10000) + defaultUploadSizeLimitBytes = int64(32768) // 32KB limit + minUploadSizeLimitBytes = int64(90) // A single event with a decision ID (69 bytes) + empty gzip file (21 bytes) + maxUploadSizeLimitBytes = int64(4294967296) // about 4GB + defaultBufferSizeLimitBytes = int64(0) // unlimited + defaultMaskDecisionPath = "/system/log/mask" + defaultDropDecisionPath = "/system/log/drop" + logRateLimitExDropCounterName = "decision_logs_dropped_rate_limit_exceeded" + logBufferEventDropCounterName = "decision_logs_dropped_buffer_size_limit_exceeded" + logBufferSizeLimitExDropCounterName = "decision_logs_dropped_buffer_size_limit_bytes_exceeded" + logEncodingFailureCounterName = "decision_logs_encoding_failure" + defaultResourcePath = "/logs" + sizeBufferType = "size" + eventBufferType = "event" +) + +// ReportingConfig represents configuration for the plugin's reporting behaviour. +type ReportingConfig struct { + BufferType string `json:"buffer_type,omitempty"` // toggles how the buffer stores events, defaults to using bytes + BufferSizeLimitBytes *int64 `json:"buffer_size_limit_bytes,omitempty"` // max size of in-memory size buffer + BufferSizeLimitEvents *int64 `json:"buffer_size_limit_events,omitempty"` // max size of in-memory event channel buffer + UploadSizeLimitBytes *int64 `json:"upload_size_limit_bytes,omitempty"` // max size of upload payload + MinDelaySeconds *int64 `json:"min_delay_seconds,omitempty"` // min amount of time to wait between successful poll attempts + MaxDelaySeconds *int64 `json:"max_delay_seconds,omitempty"` // max amount of time to wait between poll attempts + MaxDecisionsPerSecond *float64 `json:"max_decisions_per_second,omitempty"` // max number of decision logs to buffer per second + Trigger *plugins.TriggerMode `json:"trigger,omitempty"` // trigger mode +} + +type RequestContextConfig struct { + HTTPRequest *HTTPRequestContextConfig `json:"http,omitempty"` +} + +type HTTPRequestContextConfig struct { + Headers []string `json:"headers,omitempty"` +} + +// Config represents the plugin configuration. +type Config struct { + Plugin *string `json:"plugin"` + Service string `json:"service"` + PartitionName string `json:"partition_name,omitempty"` + Reporting ReportingConfig `json:"reporting"` + RequestContext RequestContextConfig `json:"request_context"` + MaskDecision *string `json:"mask_decision"` + DropDecision *string `json:"drop_decision"` + ConsoleLogs bool `json:"console"` + Resource *string `json:"resource"` + NDBuiltinCache bool `json:"nd_builtin_cache,omitempty"` + maskDecisionRef ast.Ref + dropDecisionRef ast.Ref +} + +func (c *Config) validateAndInjectDefaults(services []string, pluginsList []string, trigger *plugins.TriggerMode, l logging.Logger) error { + + if c.Plugin != nil { + var found bool + if slices.Contains(pluginsList, *c.Plugin) { + found = true + } + if !found { + return fmt.Errorf("invalid plugin name %q in decision_logs", *c.Plugin) + } + } else if c.Service == "" && len(services) != 0 && !c.ConsoleLogs { + // For backwards compatibility allow defaulting to the first + // service listed, but only if console logging is disabled. If enabled + // we can't tell if the deployer wanted to use only console logs or + // both console logs and the default service option. + c.Service = services[0] + } else if c.Service != "" { + found := slices.Contains(services, c.Service) + + if !found { + return fmt.Errorf("invalid service name %q in decision_logs", c.Service) + } + } + + t, err := plugins.ValidateAndInjectDefaultsForTriggerMode(trigger, c.Reporting.Trigger) + if err != nil { + return fmt.Errorf("invalid decision_log config: %w", err) + } + c.Reporting.Trigger = t + + min := defaultMinDelaySeconds + max := defaultMaxDelaySeconds + + // reject bad min/max values + if c.Reporting.MaxDelaySeconds != nil && c.Reporting.MinDelaySeconds != nil { + if *c.Reporting.MaxDelaySeconds < *c.Reporting.MinDelaySeconds { + return errors.New("max reporting delay must be >= min reporting delay in decision_logs") + } + min = *c.Reporting.MinDelaySeconds + max = *c.Reporting.MaxDelaySeconds + } else if c.Reporting.MaxDelaySeconds == nil && c.Reporting.MinDelaySeconds != nil { + return errors.New("reporting configuration missing 'max_delay_seconds' in decision_logs") + } else if c.Reporting.MinDelaySeconds == nil && c.Reporting.MaxDelaySeconds != nil { + return errors.New("reporting configuration missing 'min_delay_seconds' in decision_logs") + } + + // scale to seconds + minSeconds := int64(time.Duration(min) * time.Second) + c.Reporting.MinDelaySeconds = &minSeconds + + maxSeconds := int64(time.Duration(max) * time.Second) + c.Reporting.MaxDelaySeconds = &maxSeconds + + // default the upload size limit + uploadLimit := defaultUploadSizeLimitBytes + if c.Reporting.UploadSizeLimitBytes != nil { + uploadLimit = *c.Reporting.UploadSizeLimitBytes + } + + switch { + case uploadLimit > maxUploadSizeLimitBytes: + maxUploadLimit := maxUploadSizeLimitBytes + c.Reporting.UploadSizeLimitBytes = &maxUploadLimit + if l != nil { + l.Warn("the configured `upload_size_limit_bytes` (%d) has been set to the maximum limit (%d)", uploadLimit, maxUploadLimit) + } + case uploadLimit < minUploadSizeLimitBytes: + minUploadLimit := minUploadSizeLimitBytes + c.Reporting.UploadSizeLimitBytes = &minUploadLimit + if l != nil { + l.Warn("the configured `upload_size_limit_bytes` (%d) has been set to the minimum limit (%d)", uploadLimit, minUploadLimit) + } + default: + c.Reporting.UploadSizeLimitBytes = &uploadLimit + } + + if c.Reporting.BufferType == "" { + c.Reporting.BufferType = sizeBufferType + } else if c.Reporting.BufferType != eventBufferType && c.Reporting.BufferType != sizeBufferType { + return fmt.Errorf("invalid buffer type %q, expected %q or %q", c.Reporting.BufferType, eventBufferType, sizeBufferType) + } + + if c.Reporting.BufferType == eventBufferType && c.Reporting.BufferSizeLimitBytes != nil { + return fmt.Errorf("invalid decision_log config, 'buffer_size_limit_bytes' isn't supported for the %v buffer type", eventBufferType) + } + if c.Reporting.BufferType == sizeBufferType && c.Reporting.BufferSizeLimitEvents != nil { + return fmt.Errorf("invalid decision_log config, 'buffer_size_limit_events' isn't supported for the %v buffer type", sizeBufferType) + } + + if c.Reporting.BufferSizeLimitBytes != nil && c.Reporting.MaxDecisionsPerSecond != nil { + return errors.New("invalid decision_log config, specify either 'buffer_size_limit_bytes' or 'max_decisions_per_second'") + } + + // default the buffer size limit + sizeBufferLimit := defaultBufferSizeLimitBytes + if c.Reporting.BufferSizeLimitBytes != nil { + if *c.Reporting.BufferSizeLimitBytes <= int64(0) { + return errors.New("invalid decision_log config, 'buffer_size_limit_bytes' must be higher than 0") + } + sizeBufferLimit = *c.Reporting.BufferSizeLimitBytes + } + c.Reporting.BufferSizeLimitBytes = &sizeBufferLimit + + eventBufferLimit := defaultBufferSizeLimitEvents + if c.Reporting.BufferSizeLimitEvents != nil { + if *c.Reporting.BufferSizeLimitEvents <= int64(0) { + return errors.New("invalid decision_log config, 'buffer_size_limit_entries' must be higher than 0") + } + eventBufferLimit = *c.Reporting.BufferSizeLimitEvents + } + c.Reporting.BufferSizeLimitEvents = &eventBufferLimit + + if c.MaskDecision == nil { + maskDecision := defaultMaskDecisionPath + c.MaskDecision = &maskDecision + } + + c.maskDecisionRef, err = ref.ParseDataPath(*c.MaskDecision) + if err != nil { + return fmt.Errorf("invalid mask_decision in decision_logs: %w", err) + } + + if c.DropDecision == nil { + dropDecision := defaultDropDecisionPath + c.DropDecision = &dropDecision + } + + c.dropDecisionRef, err = ref.ParseDataPath(*c.DropDecision) + if err != nil { + return fmt.Errorf("invalid drop_decision in decision_logs: %w", err) + } + + if c.PartitionName != "" { + resourcePath := fmt.Sprintf("/logs/%v", c.PartitionName) + c.Resource = &resourcePath + } else if c.Resource == nil { + resourcePath := defaultResourcePath + c.Resource = &resourcePath + } else { + if _, err := url.Parse(*c.Resource); err != nil { + return fmt.Errorf("invalid resource path %q: %w", *c.Resource, err) + } + } + + return nil +} + +// Plugin implements decision log buffering and uploading. +type Plugin struct { + manager *plugins.Manager + config Config + runningBuffer string + reconfigMtx sync.RWMutex // reconfigMtx blocks reads/writes on buffer reconfiguration + eventBuffer *eventBuffer + buffer *logBuffer + enc *chunkEncoder + mtx sync.Mutex + statusMtx sync.Mutex + stop chan chan struct{} + reconfig chan reconfigure + preparedMask prepareOnce + preparedDrop prepareOnce + limiter *rate.Limiter + metrics metrics.Metrics + logger logging.Logger + status *lstat.Status +} + +type prepareOnce struct { + once *sync.Once + preparedQuery *rego.PreparedEvalQuery + err error +} + +func newPrepareOnce() *prepareOnce { + return &prepareOnce{ + once: new(sync.Once), + } +} + +func (po *prepareOnce) drop() { + po.once = new(sync.Once) +} + +func (po *prepareOnce) prepareOnce(f func() (*rego.PreparedEvalQuery, error)) (*rego.PreparedEvalQuery, error) { + po.once.Do(func() { + po.preparedQuery, po.err = f() + }) + return po.preparedQuery, po.err +} + +type reconfigure struct { + config any + done chan struct{} +} + +// ParseConfig validates the config and injects default values. +func ParseConfig(config []byte, services []string, pluginList []string) (*Config, error) { + t := plugins.DefaultTriggerMode + return NewConfigBuilder(). + WithBytes(config). + WithServices(services). + WithPlugins(pluginList). + WithTriggerMode(&t). + Parse() +} + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder struct { + raw []byte + services []string + plugins []string + trigger *plugins.TriggerMode + logger logging.Logger +} + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the plugin config. +func NewConfigBuilder() *ConfigBuilder { + return &ConfigBuilder{} +} + +func (b *ConfigBuilder) WithLogger(l logging.Logger) *ConfigBuilder { + b.logger = l + return b +} + +// WithBytes sets the raw plugin config. +func (b *ConfigBuilder) WithBytes(config []byte) *ConfigBuilder { + b.raw = config + return b +} + +// WithServices sets the services that implement control plane APIs. +func (b *ConfigBuilder) WithServices(services []string) *ConfigBuilder { + b.services = services + return b +} + +// WithPlugins sets the list of named plugins for decision logging. +func (b *ConfigBuilder) WithPlugins(plugins []string) *ConfigBuilder { + b.plugins = plugins + return b +} + +// WithTriggerMode sets the plugin trigger mode. +func (b *ConfigBuilder) WithTriggerMode(trigger *plugins.TriggerMode) *ConfigBuilder { + b.trigger = trigger + return b +} + +// Parse validates the config and injects default values. +func (b *ConfigBuilder) Parse() (*Config, error) { + if b.raw == nil { + return nil, nil + } + + var parsedConfig Config + + if err := util.Unmarshal(b.raw, &parsedConfig); err != nil { + return nil, err + } + + if parsedConfig.Plugin == nil && parsedConfig.Service == "" && len(b.services) == 0 && !parsedConfig.ConsoleLogs { + // Nothing to validate or inject + return nil, nil + } + + if err := parsedConfig.validateAndInjectDefaults(b.services, b.plugins, b.trigger, b.logger); err != nil { + return nil, err + } + + return &parsedConfig, nil +} + +// New returns a new Plugin with the given config. +func New(parsedConfig *Config, manager *plugins.Manager) *Plugin { + + plugin := &Plugin{ + manager: manager, + config: *parsedConfig, + stop: make(chan chan struct{}), + enc: newChunkEncoder(*parsedConfig.Reporting.UploadSizeLimitBytes), + reconfig: make(chan reconfigure), + logger: manager.Logger().WithFields(map[string]any{"plugin": Name}), + status: &lstat.Status{}, + preparedDrop: *newPrepareOnce(), + preparedMask: *newPrepareOnce(), + } + + plugin.enc.WithLogger(plugin.logger) + + switch parsedConfig.Reporting.BufferType { + case eventBufferType: + plugin.eventBuffer = newEventBuffer( + *parsedConfig.Reporting.BufferSizeLimitEvents, + plugin.manager.Client(plugin.config.Service), + *parsedConfig.Resource, + *parsedConfig.Reporting.UploadSizeLimitBytes, + ).WithLogger(plugin.logger).WithMetrics(plugin.metrics) + plugin.runningBuffer = eventBufferType + case sizeBufferType: + plugin.buffer = newLogBuffer(*parsedConfig.Reporting.BufferSizeLimitBytes) + plugin.runningBuffer = sizeBufferType + } + + if parsedConfig.Reporting.MaxDecisionsPerSecond != nil { + limit := *parsedConfig.Reporting.MaxDecisionsPerSecond + plugin.limiter = rate.NewLimiter(rate.Limit(limit), int(math.Max(1, limit))) + } + + manager.RegisterCompilerTrigger(plugin.compilerUpdated) + + manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateNotReady}) + + return plugin +} + +// WithMetrics sets the global metrics provider to be used by the plugin. +func (p *Plugin) WithMetrics(m metrics.Metrics) *Plugin { + p.metrics = m + p.enc.WithMetrics(m) + return p +} + +// Name identifies the plugin on manager. +const Name = "decision_logs" + +// Lookup returns the decision logs plugin registered with the manager. +func Lookup(manager *plugins.Manager) *Plugin { + if p := manager.Plugin(Name); p != nil { + return p.(*Plugin) + } + return nil +} + +// Start starts the plugin. +func (p *Plugin) Start(_ context.Context) error { + p.logger.Info("Starting decision logger.") + go p.loop() + p.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateOK}) + return nil +} + +// Stop stops the plugin. +func (p *Plugin) Stop(ctx context.Context) { + p.logger.Info("Stopping decision logger.") + + if *p.config.Reporting.Trigger == plugins.TriggerPeriodic { + if _, ok := ctx.Deadline(); ok && p.config.Service != "" { + p.flushDecisions(ctx) + } + } + + done := make(chan struct{}) + p.stop <- done + <-done + p.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateNotReady}) +} + +// Config returns the plugin's current configuration +func (p *Plugin) Config() *Config { + return &p.config +} + +func (p *Plugin) flushDecisions(ctx context.Context) { + p.logger.Info("Flushing decision logs.") + + done := make(chan bool) + + go func(ctx context.Context, done chan bool) { + for ctx.Err() == nil { + if err := p.oneShot(ctx); err != nil && !errors.Is(err, &bufferEmpty{}) { + p.logger.Error("Error flushing decisions: %s", err) + // Wait some before retrying, but skip incrementing interval since we are shutting down + time.Sleep(1 * time.Second) + } else { + done <- true + return + } + } + }(ctx, done) + + select { + case <-done: + p.logger.Info("All decisions in buffer uploaded.") + case <-ctx.Done(): + switch ctx.Err() { + case context.DeadlineExceeded, context.Canceled: + p.logger.Error("Plugin stopped with decisions possibly still in buffer.") + } + } +} + +// Log appends a decision log event to the buffer for uploading. +func (p *Plugin) Log(ctx context.Context, decision *server.Info) error { + bundles := map[string]BundleInfoV1{} + for name, info := range decision.Bundles { + bundles[name] = BundleInfoV1{Revision: info.Revision} + } + + event := EventV1{ + Labels: p.manager.Labels(), + DecisionID: decision.DecisionID, + BatchDecisionID: decision.BatchDecisionID, + TraceID: decision.TraceID, + SpanID: decision.SpanID, + Revision: decision.Revision, + Bundles: bundles, + Path: decision.Path, + Query: decision.Query, + Input: decision.Input, + Result: decision.Results, + IntermediateResults: decision.IntermediateResults, + MappedResult: decision.MappedResults, + NDBuiltinCache: decision.NDBuiltinCache, + RequestedBy: decision.RemoteAddr, + Timestamp: decision.Timestamp, + RequestID: decision.RequestID, + inputAST: decision.InputAST, + Custom: decision.Custom, + } + + headers := map[string][]string{} + rctx := p.config.RequestContext + + if rctx.HTTPRequest != nil && len(rctx.HTTPRequest.Headers) > 0 && decision.HTTPRequestContext.Header != nil { + for _, h := range rctx.HTTPRequest.Headers { + values := decision.HTTPRequestContext.Header.Values(h) + if len(values) > 0 { + headers[h] = decision.HTTPRequestContext.Header.Values(h) + } + } + } + + if len(headers) > 0 { + event.RequestContext = &RequestContext{HTTPRequest: &HTTPRequestContext{Headers: headers}} + } + + input, err := event.AST() + if err != nil { + return err + } + + drop, err := p.dropEvent(ctx, decision.Txn, input) + if err != nil { + p.logger.Error("Log drop decision failed: %v.", err) + return nil + } + + if drop { + p.logger.Debug("Decision log event to path %v dropped", event.Path) + return nil + } + + if decision.Metrics != nil { + event.Metrics = decision.Metrics.All() + } + + if decision.Error != nil { + event.Error = decision.Error + } + + if err := p.maskEvent(ctx, decision.Txn, input, &event); err != nil { + // TODO(tsandall): see note below about error handling. + p.logger.Error("Log event masking failed: %v.", err) + return nil + } + + if p.config.ConsoleLogs { + if err := p.logEvent(event); err != nil { + p.logger.Error("Failed to log to console: %v.", err) + } + } + + if p.config.Service != "" { + p.encodeAndBufferEvent(event) + } + + if p.config.Plugin != nil { + proxy, ok := p.manager.Plugin(*p.config.Plugin).(Logger) + if !ok { + return errors.New("plugin does not implement Logger interface") + } + return proxy.Log(ctx, event) + } + + return nil +} + +// Reconfigure notifies the plugin with a new configuration. +func (p *Plugin) Reconfigure(_ context.Context, config any) { + + done := make(chan struct{}) + p.reconfig <- reconfigure{config: config, done: done} + + p.preparedMask.drop() + p.preparedDrop.drop() + + <-done +} + +// Trigger can be used to control when the plugin attempts to upload +// a new decision log in manual triggering mode. +func (p *Plugin) Trigger(ctx context.Context) error { + done := make(chan error) + + go func() { + if p.config.Service != "" { + err := p.doOneShot(ctx) + if err != nil { + if ctx.Err() == nil { + done <- err + } + } + } + close(done) + }() + + select { + case err := <-done: + return err + case <-ctx.Done(): + return ctx.Err() + } +} + +// compilerUpdated is called when a compiler trigger on the plugin manager +// fires. This indicates a new compiler instance is available. The decision +// logger needs to prepare a new masking query. +func (p *Plugin) compilerUpdated(storage.Transaction) { + p.preparedMask.drop() + p.preparedDrop.drop() +} + +func (p *Plugin) loop() { + ctx, cancel := context.WithCancel(context.Background()) + + var retry int + + for { + var waitC chan struct{} + + if *p.config.Reporting.Trigger == plugins.TriggerPeriodic && p.config.Service != "" { + err := p.doOneShot(ctx) + + var delay time.Duration + + if err == nil { + min := float64(*p.config.Reporting.MinDelaySeconds) + max := float64(*p.config.Reporting.MaxDelaySeconds) + delay = time.Duration(((max - min) * rand.Float64()) + min) + } else { + delay = util.DefaultBackoff(float64(minRetryDelay), float64(*p.config.Reporting.MaxDelaySeconds), retry) + } + + p.logger.Debug("Waiting %v before next upload/retry.", delay) + + waitC = make(chan struct{}) + go func() { + timer, timerCancel := util.TimerWithCancel(delay) + select { + case <-timer.C: + if err != nil { + retry++ + } else { + retry = 0 + } + close(waitC) + case <-ctx.Done(): + timerCancel() // explicitly cancel the timer. + } + }() + } + + select { + case <-waitC: + case update := <-p.reconfig: + p.reconfigure(ctx, update.config) + update.done <- struct{}{} + case done := <-p.stop: + cancel() + done <- struct{}{} + return + } + } +} + +type bufferEmpty struct{} + +func (*bufferEmpty) Error() string { + return "buffer is empty" +} + +func (p *Plugin) doOneShot(ctx context.Context) error { + err := p.oneShot(ctx) + + if err != nil { + if errors.Is(err, &bufferEmpty{}) { + p.logger.Debug("Log upload queue was empty.") + err = nil + } else { + p.logger.Error("%v.", err) + } + } else { + p.logger.Info("Logs uploaded successfully.") + } + + p.setStatus(err) + return err +} + +func (p *Plugin) oneShot(ctx context.Context) error { + if p.runningBuffer == eventBufferType { + return p.eventBuffer.Upload(ctx) + } + + // Make a local copy of the plugin's encoder and buffer and create + // a new encoder and buffer. This is needed as locking the buffer for + // the upload duration will block policy evaluation and result in + // increased latency for OPA clients + p.mtx.Lock() + oldChunkEnc := p.enc + oldBuffer := p.buffer + p.buffer = newLogBuffer(*p.config.Reporting.BufferSizeLimitBytes) + p.enc = newChunkEncoder(*p.config.Reporting.UploadSizeLimitBytes).WithMetrics(p.metrics).WithLogger(p.logger). + WithUncompressedLimit(oldChunkEnc.uncompressedLimit, oldChunkEnc.uncompressedLimitScaleDownExponent, oldChunkEnc.uncompressedLimitScaleUpExponent) + p.mtx.Unlock() + + // Along with uploading the compressed events in the buffer + // to the remote server, flush any pending compressed data to the + // underlying writer and add to the buffer. + chunk, err := oldChunkEnc.Flush() + if err != nil { + return err + } + + for _, ch := range chunk { + p.bufferChunk(oldBuffer, ch) + } + + if oldBuffer.Len() == 0 { + return &bufferEmpty{} + } + + for bs := oldBuffer.Pop(); bs != nil; bs = oldBuffer.Pop() { + if err == nil { + err = uploadChunk(ctx, p.manager.Client(p.config.Service), *p.config.Resource, bs) + } + if err != nil { + if p.limiter != nil { + events, decErr := newChunkDecoder(bs).decode() + if decErr != nil { + continue + } + + for i := range events { + p.encodeAndBufferEvent(events[i]) + } + } else { + // requeue the chunk + p.mtx.Lock() + p.bufferChunk(p.buffer, bs) + p.mtx.Unlock() + } + } + } + + return err +} + +func (p *Plugin) reconfigure(ctx context.Context, config any) { + newConfig := config.(*Config) + + if reflect.DeepEqual(p.config, *newConfig) { + p.logger.Debug("Decision log uploader configuration unchanged.") + return + } + + p.logger.Info("Decision log uploader configuration changed.") + p.config = *newConfig + + p.reconfigMtx.Lock() + defer p.reconfigMtx.Unlock() + + switch newConfig.Reporting.BufferType { + case eventBufferType: + if p.eventBuffer == nil { + p.eventBuffer = newEventBuffer( + *p.config.Reporting.BufferSizeLimitEvents, + p.manager.Client(p.config.Service), + *p.config.Resource, + *p.config.Reporting.UploadSizeLimitBytes).WithLogger(p.logger).WithMetrics(p.metrics) + } else { + p.eventBuffer.Reconfigure( + *p.config.Reporting.BufferSizeLimitEvents, + p.manager.Client(p.config.Service), + *p.config.Resource, + *p.config.Reporting.UploadSizeLimitBytes) + } + + if p.runningBuffer == sizeBufferType { + if err := p.oneShot(ctx); err != nil && !errors.Is(err, &bufferEmpty{}) { + p.setStatus(err) + } + } + + p.runningBuffer = eventBufferType + case sizeBufferType: + if p.runningBuffer == eventBufferType { + if err := p.eventBuffer.Upload(ctx); err != nil && !errors.Is(err, &bufferEmpty{}) { + p.setStatus(err) + } + } + + if p.buffer == nil { + p.buffer = newLogBuffer(*p.config.Reporting.BufferSizeLimitBytes) + } + + p.runningBuffer = sizeBufferType + } +} + +// NOTE(philipc): Because ND builtins caching can cause unbounded growth in +// decision log entry size, we do best-effort event encoding here, and when we +// run out of space, we drop the ND builtins cache, and try encoding again. +func (p *Plugin) encodeAndBufferEvent(event EventV1) { + if p.limiter != nil && !p.limiter.Allow() { + p.incrMetric(logRateLimitExDropCounterName) + p.logger.Error("Decision log dropped as rate limit exceeded. Reduce reporting interval or increase rate limit.") + return + } + + // only blocks when the buffer is being reconfigured + p.reconfigMtx.RLock() + defer p.reconfigMtx.RUnlock() + + if p.runningBuffer == eventBufferType { + p.eventBuffer.Push(&event) + return + } + + eventBytes, err := json.Marshal(&event) + if err != nil { + p.logger.Error("Decision log dropped due to error serializing event to JSON: %v", err) + return + } + + p.mtx.Lock() + defer p.mtx.Unlock() + result, err := p.enc.Encode(event, eventBytes) + if err != nil { + return + } + for _, chunk := range result { + p.bufferChunk(p.buffer, chunk) + } +} + +func (p *Plugin) bufferChunk(buffer *logBuffer, bs []byte) { + dropped := buffer.Push(bs) + if dropped > 0 { + p.incrMetric(logBufferEventDropCounterName) + p.incrMetric(logBufferSizeLimitExDropCounterName) + p.logger.Error("Dropped %v chunks from buffer. Reduce reporting interval or increase buffer size.", dropped) + } +} + +func (p *Plugin) maskEvent(ctx context.Context, txn storage.Transaction, input ast.Value, event *EventV1) error { + pq, err := p.preparedMask.prepareOnce(func() (*rego.PreparedEvalQuery, error) { + var pq rego.PreparedEvalQuery + + query := ast.NewBody(ast.NewExpr(ast.NewTerm(p.config.maskDecisionRef))) + + r := rego.New( + rego.ParsedQuery(query), + rego.Compiler(p.manager.GetCompiler()), + rego.Store(p.manager.Store), + rego.Transaction(txn), + rego.Runtime(p.manager.Info), + rego.EnablePrintStatements(p.manager.EnablePrintStatements()), + rego.PrintHook(p.manager.PrintHook()), + ) + + pq, err := r.PrepareForEval(context.Background()) + if err != nil { + return nil, err + } + return &pq, nil + }) + + if err != nil { + return err + } + + rs, err := pq.Eval( + ctx, + rego.EvalParsedInput(input), + rego.EvalTransaction(txn), + ) + + if err != nil { + return err + } else if len(rs) == 0 { + return nil + } + + mRuleSet, err := newMaskRuleSet( + rs[0].Expressions[0].Value, + func(mRule *maskRule, err error) { + p.logger.Error("mask rule skipped: %s: %s", mRule.String(), err.Error()) + }, + ) + if err != nil { + return err + } + + mRuleSet.Mask(event) + + return nil +} + +func (p *Plugin) dropEvent(ctx context.Context, txn storage.Transaction, input ast.Value) (bool, error) { + var err error + + pq, err := p.preparedDrop.prepareOnce(func() (*rego.PreparedEvalQuery, error) { + var pq rego.PreparedEvalQuery + + query := ast.NewBody(ast.NewExpr(ast.NewTerm(p.config.dropDecisionRef))) + r := rego.New( + rego.ParsedQuery(query), + rego.Compiler(p.manager.GetCompiler()), + rego.Store(p.manager.Store), + rego.Transaction(txn), + rego.Runtime(p.manager.Info), + rego.EnablePrintStatements(p.manager.EnablePrintStatements()), + rego.PrintHook(p.manager.PrintHook()), + ) + + pq, err := r.PrepareForEval(context.Background()) + if err != nil { + return nil, err + } + return &pq, nil + }) + + if err != nil { + return false, err + } + + rs, err := pq.Eval( + ctx, + rego.EvalParsedInput(input), + rego.EvalTransaction(txn), + ) + + if err != nil { + return false, err + } + + return rs.Allowed(), nil +} + +func uploadChunk(ctx context.Context, client rest.Client, uploadPath string, data []byte) error { + + resp, err := client. + WithHeader("Content-Type", "application/json"). + WithHeader("Content-Encoding", "gzip"). + WithBytes(data). + Do(ctx, "POST", uploadPath) + + if err != nil { + return fmt.Errorf("log upload failed: %w", err) + } + + defer util.Close(resp) + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return lstat.HTTPError{StatusCode: resp.StatusCode} + } + + return nil +} + +func (p *Plugin) logEvent(event EventV1) error { + eventBuf, err := json.Marshal(&event) + if err != nil { + return err + } + fields := map[string]any{} + err = util.UnmarshalJSON(eventBuf, &fields) + if err != nil { + return err + } + p.manager.ConsoleLogger().WithFields(fields).WithFields(map[string]any{ + "type": "openpolicyagent.org/decision_logs", + }).Info("Decision Log") + return nil +} + +func (p *Plugin) incrMetric(name string) { + if p.metrics != nil { + p.metrics.Counter(name).Incr() + } +} + +func (p *Plugin) setStatus(err error) { + p.statusMtx.Lock() + p.status.SetError(err) + oldStatus := p.status + p.statusMtx.Unlock() + + if s := status.Lookup(p.manager); s != nil { + s.UpdateDecisionLogsStatus(*oldStatus) + } +} diff --git a/third_party/opa/v1/plugins/logs/plugin_benchmark_test.go b/third_party/opa/v1/plugins/logs/plugin_benchmark_test.go new file mode 100644 index 000000000000..20dce0e609bf --- /dev/null +++ b/third_party/opa/v1/plugins/logs/plugin_benchmark_test.go @@ -0,0 +1,267 @@ +package logs + +import ( + "context" + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/util" +) + +const largeEvent = `{ + "_id": "15596749567705615560", + "decision_id": "0e67fda0-170b-454d-9f5e-29691073f97e", + "input": { + "apiVersion": "admission.k8s.io/v1beta1", + "kind": "AdmissionReview", + "request": { + "kind": { + "group": "", + "kind": "Pod", + "version": "v1" + }, + "namespace": "demo", + "object": { + "metadata": { + "creationTimestamp": "2019-06-04T19:02:35Z", + "labels": { + "run": "nginx" + }, + "name": "nginx", + "namespace": "demo", + "uid": "507e4c3c-86fb-11e9-b289-42010a8000b2" + }, + "spec": { + "containers": [ + { + "image": "nginx", + "imagePullPolicy": "Always", + "name": "nginx", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "default-token-5vjbc", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "priority": 0, + "restartPolicy": "Never", + "schedulerName": "default-scheduler", + "securityContext": {}, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "default-token-5vjbc", + "secret": { + "secretName": "default-token-5vjbc" + } + } + ] + }, + "status": { + "phase": "Pending", + "qosClass": "BestEffort" + } + }, + "oldObject": null, + "operation": "CREATE", + "resource": { + "group": "", + "resource": "pods", + "version": "v1" + }, + "userInfo": { + "groups": [ + "system:serviceaccounts", + "system:serviceaccounts:opa-system", + "system:authenticated" + ], + "username": "system:serviceaccount:opa-system:default" + } + } + }, + "labels": { + "id": "462a43bd-6a5f-4530-9386-30b0f4e0c8af", + "policy-type": "kubernetes/admission_control", + "system-type": "kubernetes", + "version": "0.10.5" + }, + "metrics": { + "timer_rego_module_compile_ns": 222, + "timer_rego_module_parse_ns": 313, + "timer_rego_query_compile_ns": 121360, + "timer_rego_query_eval_ns": 923279, + "timer_rego_query_parse_ns": 287152, + "timer_server_handler_ns": 2563846 + }, + "path": "admission_control/main", + "requested_by": "10.52.0.1:53848", + "result": { + "apiVersion": "admission.k8s.io/v1beta1", + "kind": "AdmissionReview", + "response": { + "allowed": false, + "status": { + "message": "Resource Pod/demo/nginx includes container image 'nginx' from prohibited registry" + } + } + }, + "revision": "jafsdkjfhaslkdfjlaksdjflaksjdflkajsdlkfjasldkfjlaksdjflkasdjflkasjdflkajsdflkjasdklfjalsdjf", + "timestamp": "2019-06-04T19:02:35.692Z" + }` + +func BenchmarkMaskingNop(b *testing.B) { + + ctx := context.Background() + store := inmem.New() + + manager, err := plugins.New(nil, "test", store) + if err != nil { + b.Fatal(err) + } else if err := manager.Start(ctx); err != nil { + b.Fatal(err) + } + + cfg := &Config{Service: "svc"} + t := plugins.DefaultTriggerMode + if err := cfg.validateAndInjectDefaults([]string{"svc"}, nil, &t, nil); err != nil { + b.Fatal(err) + } + plugin := New(cfg, manager) + + var event EventV1 + if err := util.UnmarshalJSON([]byte(largeEvent), &event); err != nil { + b.Fatal(err) + } + input, err := event.AST() + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + + for range b.N { + if err := plugin.maskEvent(ctx, nil, input, &event); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkMaskingRuleCountsNop(b *testing.B) { + numRules := []int{1, 10, 100, 1000} + + ctx := context.Background() + store := inmem.New() + + manager, err := plugins.New(nil, "test", store) + if err != nil { + b.Fatal(err) + } else if err := manager.Start(ctx); err != nil { + b.Fatal(err) + } + + cfg := &Config{Service: "svc"} + t := plugins.DefaultTriggerMode + if err := cfg.validateAndInjectDefaults([]string{"svc"}, nil, &t, nil); err != nil { + b.Fatal(err) + } + plugin := New(cfg, manager) + + var event EventV1 + if err := util.UnmarshalJSON([]byte(largeEvent), &event); err != nil { + b.Fatal(err) + } + input, err := event.AST() + if err != nil { + b.Fatal(err) + } + + for _, ruleCount := range numRules { + b.Run(fmt.Sprintf("%dRules", ruleCount), func(b *testing.B) { + b.ResetTimer() + for range b.N { + if err := plugin.maskEvent(ctx, nil, input, &event); err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkMaskingErase(b *testing.B) { + + ctx := context.Background() + store := inmem.New() + + err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + return store.UpsertPolicy(ctx, txn, "test.rego", []byte(` + package system.log + + mask contains "/input" if { + input.input.request.kind.kind == "Pod" + } + `)) + }) + if err != nil { + b.Fatal(err) + } + + manager, err := plugins.New(nil, "test", store) + if err != nil { + b.Fatal(err) + } else if err := manager.Start(ctx); err != nil { + b.Fatal(err) + } + + cfg := &Config{Service: "svc"} + t := plugins.DefaultTriggerMode + if err := cfg.validateAndInjectDefaults([]string{"svc"}, nil, &t, nil); err != nil { + b.Fatal(err) + } + plugin := New(cfg, manager) + var event EventV1 + if err := util.UnmarshalJSON([]byte(largeEvent), &event); err != nil { + b.Fatal(err) + } + input, err := event.AST() + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + + for range b.N { + if err := plugin.maskEvent(ctx, nil, input, &event); err != nil { + b.Fatal(err) + } + + if event.Input != nil { + b.Fatal("Expected input to be erased") + } + } +} diff --git a/third_party/opa/v1/plugins/logs/plugin_test.go b/third_party/opa/v1/plugins/logs/plugin_test.go new file mode 100644 index 000000000000..4a745a1f21f7 --- /dev/null +++ b/third_party/opa/v1/plugins/logs/plugin_test.go @@ -0,0 +1,3921 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build slow + +package logs + +import ( + "bytes" + "compress/gzip" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "maps" + "net/http" + "net/http/httptest" + "os" + "reflect" + "strconv" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/logging/test" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/plugins/bundle" + "github.com/open-policy-agent/opa/v1/plugins/status" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/server" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/print" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/version" +) + +func TestMain(m *testing.M) { + version.Version = "XY.Z" + os.Exit(m.Run()) +} + +type testPlugin struct { + events []EventV1 +} + +func (p *testPlugin) Start(context.Context) error { + return nil +} + +func (p *testPlugin) Stop(context.Context) { +} + +func (p *testPlugin) Reconfigure(context.Context, any) { +} + +func (p *testPlugin) Log(_ context.Context, event EventV1) error { + p.events = append(p.events, event) + return nil +} + +func TestPluginCustomBackend(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager, _ := plugins.New(nil, "test-instance-id", inmem.New()) + + backend := &testPlugin{} + manager.Register("test_plugin", backend) + + config, err := ParseConfig([]byte(`{"plugin": "test_plugin"}`), nil, []string{"test_plugin"}) + if err != nil { + t.Fatal(err) + } + + plugin := New(config, manager) + if err := plugin.Log(ctx, &server.Info{Revision: "A"}); err != nil { + t.Fatal(err) + } + if err := plugin.Log(ctx, &server.Info{Revision: "B"}); err != nil { + t.Fatal(err) + } + + if len(backend.events) != 2 || backend.events[0].Revision != "A" || backend.events[1].Revision != "B" { + t.Fatal("Unexpected events:", backend.events) + } + + // Server events with only `Revision` should not include bundles in the EventV1 struct + for _, e := range backend.events { + if len(e.Bundles) > 0 { + t.Errorf("Unexpected `bundles` in event") + } + } +} + +func TestLogCustomField(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager, _ := plugins.New(nil, "test-instance-id", inmem.New()) + + backend := &testPlugin{} + manager.Register("test_plugin", backend) + + config, err := ParseConfig([]byte(`{"plugin": "test_plugin"}`), nil, []string{"test_plugin"}) + if err != nil { + t.Fatal(err) + } + + plugin := New(config, manager) + if err := plugin.Log(ctx, &server.Info{Custom: map[string]any{"abc": "xyz"}}); err != nil { + t.Fatal(err) + } + if err := plugin.Log(ctx, &server.Info{Custom: map[string]any{"ok": 2025}}); err != nil { + t.Fatal(err) + } + + if exp, act := 2, len(backend.events); exp != act { + t.Fatalf("expected %d events, got %d", exp, act) + } + + exp := []map[string]any{ + {"abc": "xyz"}, + {"ok": 2025}, + } + act := []map[string]any{ + backend.events[0].Custom, + backend.events[1].Custom, + } + if diff := cmp.Diff(exp, act); diff != "" { + t.Errorf("unexpected logs (-want, +got):\n%s", diff) + } +} + +func TestPluginCustomBackendAndHTTPServiceAndConsole(t *testing.T) { + t.Parallel() + + ctx := context.Background() + backend := testPlugin{} + testLogger := test.New() + + fixture := newTestFixture(t, testFixtureOptions{ + ConsoleLogger: testLogger, + ExtraManagerConfig: map[string]any{ + "plugins": map[string]any{"test_plugin": struct{}{}}, + }, + ExtraConfig: map[string]any{ + "plugin": "test_plugin", + "console": true, + }, + ManagerInit: func(m *plugins.Manager) { + m.Register("test_plugin", &backend) + }, + }) + + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 1) + + for i := range 2 { + if err := fixture.plugin.Log(ctx, &server.Info{ + Revision: strconv.Itoa(i), + }); err != nil { + t.Fatal(err) + } + } + fixture.plugin.flushDecisions(ctx) + + err := fixture.plugin.oneShot(ctx) + fmt.Println(errors.Is(err, &bufferEmpty{})) + if err != nil && !errors.Is(err, &bufferEmpty{}) { + t.Fatal(err) + } + + // check service + var evs []EventV1 + select { + case evs = <-fixture.server.ch: + default: + } + + if exp, act := 2, len(evs); exp != act { + t.Errorf("Service: expected chunk len %v but got: %v", exp, act) + } + + // check plugin + if exp, act := 2, len(backend.events); exp != act { + t.Fatalf("Plugin: expected %d events, got %d", exp, act) + } + if exp, act := "0", backend.events[0].Revision; exp != act { + t.Errorf("Plugin: expected event 0 rev %s, got %s", exp, act) + } + if exp, act := "1", backend.events[1].Revision; exp != act { + t.Errorf("Plugin: expected event 1 rev %s, got %s", exp, act) + } + + // check console logger + if exp, act := 2, len(testLogger.Entries()); exp != act { + t.Fatalf("Console: expected %d events, got %d", exp, act) + } +} + +func TestPluginRequestContext(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager, _ := plugins.New(nil, "test-instance-id", inmem.New()) + + backend := &testPlugin{} + manager.Register("test_plugin", backend) + + h1 := http.Header{} + h1.Set("foo", "bar") + + h2 := http.Header{} + h2.Set("foo", "bar") + h2.Add("foo2", "bar") + h2.Add("foo2", "bar2") + + cases := []struct { + note string + config []byte + decisionInfo *server.Info + expected *RequestContext + }{ + { + note: "no request context config - no request context in decision info", + config: []byte(`{"plugin": "test_plugin"}`), + decisionInfo: &server.Info{Bundles: map[string]server.BundleInfo{"b1": {Revision: "A"}}}, + expected: nil, + }, + { + note: "request context in config (single header) - no request context in decision info", + config: []byte(`{"plugin": "test_plugin", "request_context": {"http": {"headers": ["foo"]}}}`), + decisionInfo: &server.Info{Bundles: map[string]server.BundleInfo{"b1": {Revision: "A"}}}, + expected: nil, + }, + { + note: "request context in config (single header) - request context in decision info (no header map)", + config: []byte(`{"plugin": "test_plugin", "request_context": {"http": {"headers": ["foo"]}}}`), + decisionInfo: &server.Info{Bundles: map[string]server.BundleInfo{"b1": {Revision: "A"}}, HTTPRequestContext: logging.HTTPRequestContext{Header: nil}}, + expected: nil, + }, + { + note: "request context in config (single header) - request context in decision info (with header map)", + config: []byte(`{"plugin": "test_plugin", "request_context": {"http": {"headers": ["foo"]}}}`), + decisionInfo: &server.Info{Bundles: map[string]server.BundleInfo{"b1": {Revision: "A"}}, HTTPRequestContext: logging.HTTPRequestContext{Header: h1}}, + expected: &RequestContext{HTTPRequest: &HTTPRequestContext{Headers: map[string][]string{"foo": {"bar"}}}}, + }, + { + note: "request context in config (multiple headers) - request context in decision info (with header map partial)", + config: []byte(`{"plugin": "test_plugin", "request_context": {"http": {"headers": ["foo", "foo2"]}}}`), + decisionInfo: &server.Info{Bundles: map[string]server.BundleInfo{"b1": {Revision: "A"}}, HTTPRequestContext: logging.HTTPRequestContext{Header: h1}}, + expected: &RequestContext{HTTPRequest: &HTTPRequestContext{Headers: map[string][]string{"foo": {"bar"}}}}, + }, + { + note: "request context in config (multiple headers) - request context in decision info (with header map full)", + config: []byte(`{"plugin": "test_plugin", "request_context": {"http": {"headers": ["foo", "foo2"]}}}`), + decisionInfo: &server.Info{Bundles: map[string]server.BundleInfo{"b1": {Revision: "A"}}, HTTPRequestContext: logging.HTTPRequestContext{Header: h2}}, + expected: &RequestContext{HTTPRequest: &HTTPRequestContext{Headers: map[string][]string{"foo": {"bar"}, "foo2": {"bar", "bar2"}}}}, + }, + { + note: "request context in config (single header) - request context in decision info (with header map full)", + config: []byte(`{"plugin": "test_plugin", "request_context": {"http": {"headers": ["foo"]}}}`), + decisionInfo: &server.Info{Bundles: map[string]server.BundleInfo{"b1": {Revision: "A"}}, HTTPRequestContext: logging.HTTPRequestContext{Header: h2}}, + expected: &RequestContext{HTTPRequest: &HTTPRequestContext{Headers: map[string][]string{"foo": {"bar"}}}}, + }, + { + note: "no request context in config - request context in decision info (with header map)", + config: []byte(`{"plugin": "test_plugin"}`), + decisionInfo: &server.Info{Bundles: map[string]server.BundleInfo{"b1": {Revision: "A"}}, HTTPRequestContext: logging.HTTPRequestContext{Header: h1}}, + expected: nil, + }, + { + note: "request context in config (no http) - request context in decision info (with header map)", + config: []byte(`{"plugin": "test_plugin", "request_context": {}}`), + decisionInfo: &server.Info{Bundles: map[string]server.BundleInfo{"b1": {Revision: "A"}}, HTTPRequestContext: logging.HTTPRequestContext{Header: h1}}, + expected: nil, + }, + { + note: "request context in config (no headers) - request context in decision info (with header map)", + config: []byte(`{"plugin": "test_plugin", "request_context": {"http": {}}}`), + decisionInfo: &server.Info{Bundles: map[string]server.BundleInfo{"b1": {Revision: "A"}}, HTTPRequestContext: logging.HTTPRequestContext{Header: h1}}, + expected: nil, + }, + { + note: "request context in config (empty headers list) - request context in decision info (with header map)", + config: []byte(`{"plugin": "test_plugin", "request_context": {"http": {"headers": []}}}`), + decisionInfo: &server.Info{Bundles: map[string]server.BundleInfo{"b1": {Revision: "A"}}, HTTPRequestContext: logging.HTTPRequestContext{Header: h1}}, + expected: nil, + }, + } + + for i, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + config, err := ParseConfig(tc.config, nil, []string{"test_plugin"}) + if err != nil { + t.Fatal(err) + } + + plugin := New(config, manager) + if err := plugin.Log(ctx, tc.decisionInfo); err != nil { + t.Fatal(err) + } + + if len(backend.events) == 0 { + t.Fatal("expected at least one event") + } + + if !reflect.DeepEqual(backend.events[i].RequestContext, tc.expected) { + t.Fatalf("unexpected request context, want %+v but got %+v", tc.expected, backend.events[0].RequestContext) + } + }) + } +} + +func TestPluginSingleBundle(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager, _ := plugins.New(nil, "test-instance-id", inmem.New()) + + backend := &testPlugin{} + manager.Register("test_plugin", backend) + + config, err := ParseConfig([]byte(`{"plugin": "test_plugin"}`), nil, []string{"test_plugin"}) + if err != nil { + t.Fatal(err) + } + + plugin := New(config, manager) + if err := plugin.Log(ctx, &server.Info{Bundles: map[string]server.BundleInfo{"b1": {Revision: "A"}}}); err != nil { + t.Fatal(err) + } + + // Server events with `Bundles` should *not* have `Revision` set + if len(backend.events) != 1 { + t.Fatalf("Unexpected number of events: %v", backend.events) + } + + if backend.events[0].Revision != "" || backend.events[0].Bundles["b1"].Revision != "A" { + t.Fatal("Unexpected events: ", backend.events) + } +} + +func TestPluginErrorNoResult(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager, _ := plugins.New(nil, "test-instance-id", inmem.New()) + + backend := &testPlugin{} + manager.Register("test_plugin", backend) + + config, err := ParseConfig([]byte(`{"plugin": "test_plugin"}`), nil, []string{"test_plugin"}) + if err != nil { + t.Fatal(err) + } + + plugin := New(config, manager) + if err := plugin.Log(ctx, &server.Info{Error: errors.New("some error")}); err != nil { + t.Fatal(err) + } + if err := plugin.Log(ctx, &server.Info{Error: ast.Errors{&ast.Error{Code: "some_error"}}}); err != nil { + t.Fatal(err) + } + + if len(backend.events) != 2 || backend.events[0].Error == nil || backend.events[1].Error == nil { + t.Fatal("Unexpected events:", backend.events) + } +} + +func TestPluginQueriesAndPaths(t *testing.T) { + t.Parallel() + + ctx := context.Background() + manager, _ := plugins.New(nil, "test-instance-id", inmem.New()) + + backend := &testPlugin{} + manager.Register("test_plugin", backend) + + config, err := ParseConfig([]byte(`{"plugin": "test_plugin"}`), nil, []string{"test_plugin"}) + if err != nil { + t.Fatal(err) + } + + plugin := New(config, manager) + if err := plugin.Log(ctx, &server.Info{Path: "/"}); err != nil { + t.Fatal(err) + } + if err := plugin.Log(ctx, &server.Info{Path: "/data"}); err != nil { // /v1/data/data case + t.Fatal(err) + } + if err := plugin.Log(ctx, &server.Info{Path: "/foo"}); err != nil { + t.Fatal(err) + } + if err := plugin.Log(ctx, &server.Info{Path: "foo"}); err != nil { + t.Fatal(err) + } + if err := plugin.Log(ctx, &server.Info{Path: "/foo/bar"}); err != nil { + t.Fatal(err) + } + if err := plugin.Log(ctx, &server.Info{Path: "a.b.c"}); err != nil { + t.Fatal(err) + } + if err := plugin.Log(ctx, &server.Info{Path: "/foo/a.b.c/bar"}); err != nil { + t.Fatal(err) + } + if err := plugin.Log(ctx, &server.Info{Query: "a = data.foo"}); err != nil { + t.Fatal(err) + } + + exp := []struct { + query string + path string + }{ + {path: "/"}, + {path: "/data"}, + {path: "/foo"}, + {path: "foo"}, + {path: "/foo/bar"}, + {path: "a.b.c"}, + {path: "/foo/a.b.c/bar"}, + {query: "a = data.foo"}, + } + + if len(exp) != len(backend.events) { + t.Fatalf("Expected %d events but got %v", len(exp), len(backend.events)) + } + + for i, e := range exp { + if e.query != backend.events[i].Query || e.path != backend.events[i].Path { + t.Fatalf("Unexpected event %d, want %+v but got %+v", i, e, backend.events[i]) + } + } +} + +func TestPluginStartSameInput(t *testing.T) { + t.Parallel() + ctx := context.Background() + + fixture := newTestFixture(t) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 3) + var result any = false + + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + testMetrics := getWellKnownMetrics() + + var input any = map[string]any{"method": "GET"} + + for i := range 400 { + if err := fixture.plugin.Log(ctx, &server.Info{ + Revision: strconv.Itoa(i), + DecisionID: strconv.Itoa(i), + Path: "tda/bar", + Input: &input, + Results: &result, + RemoteAddr: "test", + Timestamp: ts, + Metrics: testMetrics, + }); err != nil { + t.Fatal(err) + } + } + + err = fixture.plugin.oneShot(ctx) + if err != nil { + t.Fatal(err) + } + + chunk1 := <-fixture.server.ch + chunk2 := <-fixture.server.ch + chunk3 := <-fixture.server.ch + // first size is smallest as the adaptive uncompressed limit increases more events can be added + expLen1 := 122 + expLen2 := 243 + expLen3 := 35 + + if len(chunk1) != expLen1 || len(chunk2) != expLen2 || len(chunk3) != expLen3 { + t.Fatalf("Expected chunk lens %v, %v, and %v but got: %v, %v, and %v", expLen1, expLen2, expLen3, len(chunk1), len(chunk2), len(chunk3)) + } + + var expInput any = map[string]any{"method": "GET"} + + msAsFloat64 := map[string]any{} + for k, v := range testMetrics.All() { + msAsFloat64[k] = float64(v.(uint64)) + } + + exp := EventV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + Revision: "399", + DecisionID: "399", + Path: "tda/bar", + Input: &expInput, + Result: &result, + RequestedBy: "test", + Timestamp: ts, + Metrics: msAsFloat64, + } + + if !reflect.DeepEqual(chunk3[expLen3-1], exp) { + t.Fatalf("Expected %+v but got %+v", exp, chunk3[expLen3-1]) + } + + if fixture.plugin.status.Code != "" { + t.Fatal("expected no error in status update") + } +} + +func TestPluginStartChangingInputValues(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + fixture := newTestFixture(t) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 3) + var result any = false + + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + var input any + + for i := range 400 { + input = map[string]any{"method": getValueForMethod(i), "path": getValueForPath(i), "user": getValueForUser(i)} + + if err := fixture.plugin.Log(ctx, &server.Info{ + Revision: strconv.Itoa(i), + DecisionID: strconv.Itoa(i), + Path: "foo/bar", + Input: &input, + Results: &result, + RemoteAddr: "test", + Timestamp: ts, + }); err != nil { + t.Fatal(err) + } + } + + err = fixture.plugin.oneShot(ctx) + if err != nil { + t.Fatal(err) + } + + chunk1 := <-fixture.server.ch + chunk2 := <-fixture.server.ch + chunk3 := <-fixture.server.ch + expLen1 := 125 + expLen2 := 248 + expLen3 := 27 + + if len(chunk1) != expLen1 || len(chunk2) != expLen2 || len((chunk3)) != expLen3 { + t.Fatalf("Expected chunk lens %v, %v and %v but got: %v, %v and %v", expLen1, expLen2, expLen3, len(chunk1), len(chunk2), len(chunk3)) + } + + exp := EventV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + Revision: "399", + DecisionID: "399", + Path: "foo/bar", + Input: &input, + Result: &result, + RequestedBy: "test", + Timestamp: ts, + } + + if !reflect.DeepEqual(chunk3[expLen3-1], exp) { + t.Fatalf("Expected %+v but got %+v", exp, chunk3[expLen3-1]) + } +} + +func TestPluginStartChangingInputKeysAndValues(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + fixture := newTestFixture(t) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 2) + var result any = false + + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + var input any + + for i := range 250 { + input = generateInputMap(i) + + if err := fixture.plugin.Log(ctx, &server.Info{ + Revision: strconv.Itoa(i), + DecisionID: strconv.Itoa(i), + Path: "foo/bar", + Input: &input, + Results: &result, + RemoteAddr: "test", + Timestamp: ts, + }); err != nil { + t.Fatal(err) + } + } + + err = fixture.plugin.oneShot(ctx) + if err != nil { + t.Fatal(err) + } + + <-fixture.server.ch + chunk2 := <-fixture.server.ch + + exp := EventV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + Revision: "249", + DecisionID: "249", + Path: "foo/bar", + Input: &input, + Result: &result, + RequestedBy: "test", + Timestamp: ts, + } + + if !reflect.DeepEqual(chunk2[len(chunk2)-1], exp) { + t.Fatalf("Expected %+v but got %+v", exp, chunk2[len(chunk2)-1]) + } +} + +func TestPluginRequeue(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + reportingBufferType string + }{ + { + name: "using event buffer", + reportingBufferType: "event", + }, + { + name: "using size buffer", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + fixture := newTestFixture(t, testFixtureOptions{ + ReportingBufferType: tc.reportingBufferType, + }) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 1) + + var input any = map[string]any{"method": "GET"} + var result1 any = false + + if err := fixture.plugin.Log(ctx, &server.Info{ + DecisionID: "abc", + Path: "data.foo.bar", + Input: &input, + Results: &result1, + RemoteAddr: "test", + Timestamp: time.Now().UTC(), + }); err != nil { + t.Fatal(err) + } + + fixture.server.expCode = 500 + err := fixture.plugin.oneShot(ctx) + if err == nil { + t.Fatal("Expected error") + } + + events1 := <-fixture.server.ch + + fixture.server.expCode = 200 + + err = fixture.plugin.oneShot(ctx) + if err != nil { + t.Fatal(err) + } + + events2 := <-fixture.server.ch + + if !reflect.DeepEqual(events1, events2) { + t.Fatalf("Expected %v but got: %v", events1, events2) + } + + err = fixture.plugin.oneShot(ctx) + if err != nil && !errors.Is(err, &bufferEmpty{}) { + t.Fatalf("Unexpected error or upload, err: %v", err) + } + }) + } +} + +func logServerInfo(id string, input any, result any) *server.Info { + return &server.Info{ + DecisionID: id, + Path: "data.foo.bar", + Input: &input, + Results: &result, + RemoteAddr: "test", + Timestamp: time.Now().UTC(), + } +} + +func TestPluginRequeueBufferPreserved(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + fixture := newTestFixture(t, testFixtureOptions{ReportingUploadSizeLimitBytes: 300}) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 3) + + var input any = map[string]any{"method": "GET"} + var result1 any = false + + _ = fixture.plugin.Log(ctx, logServerInfo("abc", input, result1)) + _ = fixture.plugin.Log(ctx, logServerInfo("def", input, result1)) + _ = fixture.plugin.Log(ctx, logServerInfo("ghi", input, result1)) + + bufLen := fixture.plugin.buffer.Len() + if bufLen < 1 { + t.Fatal("Expected buffer length of at least 1") + } + + fixture.server.expCode = 500 + err := fixture.plugin.oneShot(ctx) + if err == nil { + t.Fatal("Expected error") + } + + <-fixture.server.ch + + if fixture.plugin.buffer.Len() < bufLen { + t.Fatal("Expected buffer to be preserved") + } +} + +func TestPluginRateLimitInt(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + bufferType string + }{ + { + name: "using event buffer", + bufferType: eventBufferType, + }, + { + name: "using size buffer", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + numDecisions := 1 // 1 decision per second + + fixture := newTestFixture(t, testFixtureOptions{ + ReportingMaxDecisionsPerSecond: float64(numDecisions), + ReportingUploadSizeLimitBytes: defaultUploadSizeLimitBytes, + ReportingBufferType: tc.bufferType, + }) + defer fixture.server.stop() + + var input any = map[string]any{"method": "GET"} + var result any = false + + eventSize := 217 + event1 := &server.Info{ + DecisionID: "abc", + Path: "foo/bar", + Input: &input, + Results: &result, + RemoteAddr: "test-1", + Timestamp: ts, + } + + event2 := &server.Info{ + DecisionID: "def", + Path: "foo/baz", + Input: &input, + Results: &result, + RemoteAddr: "test-2", + Timestamp: ts, + } + + _ = fixture.plugin.Log(ctx, event1) // event 1 should be written into the encoder + + expectedLen := 1 + currentLen := getBufferLen(t, fixture, eventSize) + if currentLen != expectedLen { + t.Fatalf("Expected %v events to be written but got %v", expectedLen, currentLen) + } + + _ = fixture.plugin.Log(ctx, event2) // event 2 should not be written into the encoder as rate limit exceeded + + currentLen = getBufferLen(t, fixture, eventSize) + if currentLen != expectedLen { + t.Fatalf("Expected %v events to be written but got %v", expectedLen, currentLen) + } + + time.Sleep(1 * time.Second) + _ = fixture.plugin.Log(ctx, event2) // event 2 should now be written into the encoder + + expectedLen = 2 + currentLen = getBufferLen(t, fixture, eventSize) + if currentLen != expectedLen { + t.Fatalf("Expected %v events to be written but got %v", expectedLen, currentLen) + } + + expectedEvent1 := EventV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + DecisionID: "abc", + Path: "foo/bar", + Input: &input, + Result: &result, + RequestedBy: "test-1", + Timestamp: ts, + } + expectedEvent2 := EventV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + DecisionID: "def", + Path: "foo/baz", + Input: &input, + Result: &result, + RequestedBy: "test-2", + Timestamp: ts, + } + + var bufferEvent1, bufferEvent2 EventV1 + switch fixture.plugin.runningBuffer { + case sizeBufferType: + chunk, err := fixture.plugin.enc.Flush() + if err != nil { + t.Fatal(err) + } + if len(chunk) != 1 { + t.Fatalf("Expected 1 chunk but got %v", len(chunk)) + } + events := decodeLogEvent(t, bytes.NewReader(chunk[0])) + if len(events) != 2 { + t.Fatalf("Expected 2 events but got %v", len(events)) + } + bufferEvent1 = events[0] + bufferEvent2 = events[1] + case eventBufferType: + bufferEvent1 = *(<-fixture.plugin.eventBuffer.buffer).EventV1 + bufferEvent1.Bundles = nil + + bufferEvent2 = *(<-fixture.plugin.eventBuffer.buffer).EventV1 + bufferEvent2.Bundles = nil + + } + bufferEvent1.inputAST = nil + if !reflect.DeepEqual(bufferEvent1, expectedEvent1) { + t.Fatalf("Expected %+v but got %+v", expectedEvent1, event1) + } + bufferEvent2.inputAST = nil + if !reflect.DeepEqual(bufferEvent2, expectedEvent2) { + t.Fatalf("Expected %+v but got %+v", expectedEvent1, event1) + } + }) + } +} + +// getBufferLen returns the buffer length for either the event or size buffer. +func getBufferLen(t *testing.T, fixture testFixture, eventSize int) int { + switch fixture.plugin.runningBuffer { + case eventBufferType: + return len(fixture.plugin.eventBuffer.buffer) + case sizeBufferType: + // events stay in the encoder until the upload limit is reached + return fixture.plugin.enc.bytesWritten / eventSize + default: + t.Fatal("unknown buffer type") + return 0 + } +} + +func TestPluginRateLimitFloat(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + bufferType string + }{ + { + name: "using event buffer", + bufferType: eventBufferType, + }, + { + name: "using size buffer", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + numDecisions := 0.5 // 0.5 decision per second i.e. 1 decision per 2 seconds + fixture := newTestFixture(t, testFixtureOptions{ + ReportingMaxDecisionsPerSecond: numDecisions, + ReportingUploadSizeLimitBytes: defaultUploadSizeLimitBytes, + ReportingBufferType: tc.bufferType, + }) + defer fixture.server.stop() + + var input any = map[string]any{"method": "GET"} + var result any = false + + eventSize := 217 + event1 := &server.Info{ + DecisionID: "abc", + Path: "foo/bar", + Input: &input, + Results: &result, + RemoteAddr: "test-1", + Timestamp: ts, + } + + event2 := &server.Info{ + DecisionID: "def", + Path: "foo/baz", + Input: &input, + Results: &result, + RemoteAddr: "test-2", + Timestamp: ts, + } + + // event 1 should be written into the encoder + if err := fixture.plugin.Log(ctx, event1); err != nil { + t.Fatal(err) + } + + expectedLen := 1 + currentLen := getBufferLen(t, fixture, eventSize) + if currentLen != expectedLen { + t.Fatalf("Expected %v events to be written but got %v", expectedLen, currentLen) + } + + // event 2 should not be written into the encoder as rate limit exceeded + if err := fixture.plugin.Log(ctx, event2); err != nil { + t.Fatal(err) + } + + currentLen = getBufferLen(t, fixture, eventSize) + if currentLen != expectedLen { + t.Fatalf("Expected %v events to be written but got %v", expectedLen, currentLen) + } + + time.Sleep(1 * time.Second) + // event 2 should not be written into the encoder as rate limit exceeded + if err := fixture.plugin.Log(ctx, event2); err != nil { + t.Fatal(err) + } + + currentLen = getBufferLen(t, fixture, eventSize) + if currentLen != expectedLen { + t.Fatalf("Expected %v events to be written but got %v", expectedLen, currentLen) + } + + time.Sleep(1 * time.Second) + _ = fixture.plugin.Log(ctx, event2) // event 2 should now be written into the encoder + + expectedLen = 2 + currentLen = getBufferLen(t, fixture, eventSize) + if currentLen != expectedLen { + t.Fatalf("Expected %v events to be written but got %v", expectedLen, currentLen) + } + + expectedEvent1 := EventV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + DecisionID: "abc", + Path: "foo/bar", + Input: &input, + Result: &result, + RequestedBy: "test-1", + Timestamp: ts, + } + expectedEvent2 := EventV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + DecisionID: "def", + Path: "foo/baz", + Input: &input, + Result: &result, + RequestedBy: "test-2", + Timestamp: ts, + } + + var bufferEvent1, bufferEvent2 EventV1 + switch fixture.plugin.runningBuffer { + case sizeBufferType: + chunk, err := fixture.plugin.enc.Flush() + if err != nil { + t.Fatal(err) + } + if len(chunk) != 1 { + t.Fatalf("Expected 1 chunk but got %v", len(chunk)) + } + events := decodeLogEvent(t, bytes.NewReader(chunk[0])) + if len(events) != 2 { + t.Fatalf("Expected 2 events but got %v", len(events)) + } + bufferEvent1 = events[0] + bufferEvent2 = events[1] + case eventBufferType: + bufferEvent1 = *(<-fixture.plugin.eventBuffer.buffer).EventV1 + bufferEvent1.Bundles = nil + + bufferEvent2 = *(<-fixture.plugin.eventBuffer.buffer).EventV1 + bufferEvent2.Bundles = nil + + } + bufferEvent1.inputAST = nil + if !reflect.DeepEqual(bufferEvent1, expectedEvent1) { + t.Fatalf("Expected %+v but got %+v", expectedEvent1, event1) + } + bufferEvent2.inputAST = nil + if !reflect.DeepEqual(bufferEvent2, expectedEvent2) { + t.Fatalf("Expected %+v but got %+v", expectedEvent1, event1) + } + }) + } +} + +func TestPluginStatusUpdateHTTPError(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + bufferType string + }{ + { + name: "using event buffer", + bufferType: eventBufferType, + }, + { + name: "using size buffer", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + fixture := newTestFixture(t, testFixtureOptions{ + ReportingUploadSizeLimitBytes: defaultUploadSizeLimitBytes, + ReportingBufferType: tc.bufferType, + }) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 3) + + input := map[string]any{"method": "GET"} + var result1 bool + + if err := fixture.plugin.Log(ctx, logServerInfo("abc", input, result1)); err != nil { + t.Fatal(err) + } + if err := fixture.plugin.Log(ctx, logServerInfo("def", input, result1)); err != nil { + t.Fatal(err) + } + if err := fixture.plugin.Log(ctx, logServerInfo("ghi", input, result1)); err != nil { + t.Fatal(err) + } + + eventSize := 218 + bufLen := getBufferLen(t, fixture, eventSize) + if bufLen != 3 { + t.Fatalf("Expected buffer length of 3 but got %v", bufLen) + } + + fixture.server.expCode = 500 + err := fixture.plugin.doOneShot(ctx) + if err == nil { + t.Fatal("Expected error") + } + + <-fixture.server.ch + + if fixture.plugin.status.HTTPCode != "500" { + t.Fatal("expected http_code to be 500 instead of ", fixture.plugin.status.HTTPCode) + } + + msg := "log upload failed, server replied with HTTP 500 Internal Server Error" + if fixture.plugin.status.Message != msg { + t.Fatalf("expected status message to be %v instead of %v", msg, fixture.plugin.status.Message) + } + }) + } +} + +func TestPluginStatusUpdateEncodingFailure(t *testing.T) { + t.Parallel() + + ctx := context.Background() + testLogger := test.New() + + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + fixture := newTestFixture(t, testFixtureOptions{ + ConsoleLogger: testLogger, + ReportingUploadSizeLimitBytes: 1, + }) + defer fixture.server.stop() + + m := metrics.New() + fixture.plugin.metrics = m + fixture.plugin.enc.metrics = m + + var input any = map[string]any{"method": "GET"} + var result any = false + + event := &server.Info{ + DecisionID: "abc", + Path: "foo/bar", + Input: &input, + Results: &result, + RemoteAddr: "test-1", + Timestamp: ts, + } + + err = fixture.plugin.Log(ctx, event) + if err != nil { + t.Fatal(err) + } + + fixture.plugin.mtx.Lock() + if fixture.plugin.enc.bytesWritten != 0 { + t.Fatal("Expected no event to be written into the encoder") + } + fixture.plugin.mtx.Unlock() + + // Create a status plugin that logs to console + pluginConfig := []byte(`{ + "console": true, + }`) + + config, _ := status.ParseConfig(pluginConfig, fixture.manager.Services(), nil) + p := status.New(config, fixture.manager).WithMetrics(fixture.plugin.metrics) + + fixture.manager.Register(status.Name, p) + if err := fixture.manager.Start(ctx); err != nil { + t.Fatal(err) + } + + // Trigger a status update + fixture.server.expCode = 200 + err = fixture.plugin.doOneShot(ctx) + if err != nil && !errors.Is(err, &bufferEmpty{}) { + t.Fatal("Unexpected error") + } + + // Give the logger / console some time to process and print the events + time.Sleep(10 * time.Millisecond) + p.Stop(ctx) + + entries := testLogger.Entries() + if len(entries) == 0 { + t.Fatal("Expected log entries but got none") + } + + // Pick the last entry as it should have the decision log metrics + e := entries[len(entries)-1] + + if _, ok := e.Fields["metrics"]; !ok { + t.Fatal("Expected metrics field in status update") + } + + fmt.Println(e.Fields["metrics"]) + + exp := map[string]any{"": map[string]any{"counter_decision_logs_encoding_failure": json.Number("1"), + "counter_enc_log_exceeded_upload_size_limit_bytes": json.Number("1")}} + + if !reflect.DeepEqual(e.Fields["metrics"], exp) { + t.Fatalf("Expected %v but got %v", exp, e.Fields["metrics"]) + } +} + +func TestPluginStatusUpdateBufferSizeExceeded(t *testing.T) { + t.Parallel() + + ctx := context.Background() + testLogger := test.New() + + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + fixture := newTestFixture(t, testFixtureOptions{ + ConsoleLogger: testLogger, + ReportingBufferSizeLimitBytes: 200, + ReportingUploadSizeLimitBytes: 300, + }) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 1) + + fixture.plugin.metrics = metrics.New() + + var input any = map[string]any{"method": "GET"} + var result any = false + + event1 := &server.Info{ + DecisionID: "abc", + Path: "foo/bar", + Input: &input, + Results: &result, + RemoteAddr: "test-1", + Timestamp: ts, + } + + event2 := &server.Info{ + DecisionID: "def", + Path: "foo/baz", + Input: &input, + Results: &result, + RemoteAddr: "test-2", + Timestamp: ts, + } + + event3 := &server.Info{ + DecisionID: "ghi", + Path: "foo/aux", + Input: &input, + Results: &result, + RemoteAddr: "test-3", + Timestamp: ts, + } + + // write event 1 and 2 into the encoder and check the chunk is inserted into the buffer + if err := fixture.plugin.Log(ctx, event1); err != nil { + t.Error(err) + } + + if err := fixture.plugin.Log(ctx, event2); err != nil { + t.Error(err) + } + + fixture.plugin.mtx.Lock() + + if fixture.plugin.enc.bytesWritten == 0 { + t.Fatal("Expected event to be written into the encoder") + } + + if fixture.plugin.buffer.Len() == 0 { + t.Fatal("Expected one chunk to be written into the buffer") + } + fixture.plugin.mtx.Unlock() + + // write event 3 into the encoder and then flush the encoder which will result in the event being + // written to the buffer. But given the buffer size it won't be able to hold this event and will + // drop the existing chunk + _ = fixture.plugin.Log(ctx, event3) + + // Create a status plugin that logs to console + pluginConfig := []byte(`{ + "console": true, + }`) + + config, _ := status.ParseConfig(pluginConfig, fixture.manager.Services(), nil) + p := status.New(config, fixture.manager).WithMetrics(fixture.plugin.metrics) + + fixture.manager.Register(status.Name, p) + if err := fixture.manager.Start(ctx); err != nil { + t.Fatal(err) + } + + // Trigger a status update + fixture.server.expCode = 200 + err = fixture.plugin.doOneShot(ctx) + if err != nil { + t.Fatal("Unexpected error") + } + + <-fixture.server.ch + + // Give the logger / console some time to process and print the events + time.Sleep(10 * time.Millisecond) + p.Stop(ctx) + + entries := testLogger.Entries() + if len(entries) == 0 { + t.Fatal("Expected log entries but got none") + } + + // Pick the last entry as it should have the decision log metrics + e := entries[len(entries)-1] + + if _, ok := e.Fields["metrics"]; !ok { + t.Fatal("Expected metrics field in status update") + } + + exp := map[string]any{"": map[string]any{ + "counter_decision_logs_dropped_buffer_size_limit_bytes_exceeded": json.Number("1"), + "counter_decision_logs_dropped_buffer_size_limit_exceeded": json.Number("1"), + }} + + if !reflect.DeepEqual(e.Fields["metrics"], exp) { + t.Fatalf("Expected %v but got %v", exp, e.Fields["metrics"]) + } +} + +func TestPluginStatusUpdateRateLimitExceeded(t *testing.T) { + t.Parallel() + + ctx := context.Background() + testLogger := test.New() + + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + numDecisions := 1 // 1 decision per second + fixture := newTestFixture(t, testFixtureOptions{ + ConsoleLogger: testLogger, + ReportingMaxDecisionsPerSecond: float64(numDecisions), + ReportingUploadSizeLimitBytes: 300, + }) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 1) + + fixture.plugin.metrics = metrics.New() + + var input any = map[string]any{"method": "GET"} + var result any = false + + event1 := &server.Info{ + DecisionID: "abc", + Path: "foo/bar", + Input: &input, + Results: &result, + RemoteAddr: "test-1", + Timestamp: ts, + } + + event2 := &server.Info{ + DecisionID: "def", + Path: "foo/baz", + Input: &input, + Results: &result, + RemoteAddr: "test-2", + Timestamp: ts, + } + + event3 := &server.Info{ + DecisionID: "ghi", + Path: "foo/aux", + Input: &input, + Results: &result, + RemoteAddr: "test-3", + Timestamp: ts, + } + + _ = fixture.plugin.Log(ctx, event1) // event 1 should be written into the encoder + + fixture.plugin.mtx.Lock() + if fixture.plugin.enc.bytesWritten == 0 { + t.Fatal("Expected event to be written into the encoder") + } + fixture.plugin.mtx.Unlock() + + // Create a status plugin that logs to console + pluginConfig := []byte(`{ + "console": true, + }`) + + config, _ := status.ParseConfig(pluginConfig, fixture.manager.Services(), nil) + p := status.New(config, fixture.manager).WithMetrics(fixture.plugin.metrics) + + fixture.manager.Register(status.Name, p) + if err := fixture.manager.Start(ctx); err != nil { + t.Fatal(err) + } + + _ = fixture.plugin.Log(ctx, event2) // event 2 should not be written into the encoder as rate limit exceeded + _ = fixture.plugin.Log(ctx, event3) // event 3 should not be written into the encoder as rate limit exceeded + + // Trigger a status update + fixture.server.expCode = 200 + err = fixture.plugin.doOneShot(ctx) + if err != nil { + t.Fatal("Unexpected error") + } + + <-fixture.server.ch + + // Give the logger / console some time to process and print the events + time.Sleep(10 * time.Millisecond) + p.Stop(ctx) + + entries := testLogger.Entries() + if len(entries) == 0 { + t.Fatal("Expected log entries but got none") + } + + // Pick the last entry as it should have the decision log metrics + e := entries[len(entries)-1] + + if _, ok := e.Fields["metrics"]; !ok { + t.Fatal("Expected metrics field in status update") + } + + exp := map[string]any{"": map[string]any{"counter_decision_logs_dropped_rate_limit_exceeded": json.Number("2")}} + + if !reflect.DeepEqual(e.Fields["metrics"], exp) { + t.Fatalf("Expected %v but got %v", exp, e.Fields["metrics"]) + } +} + +func TestPluginRateLimitRequeue(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + bufferType string + }{ + { + name: "using event buffer", + bufferType: eventBufferType, + }, + { + name: "using size buffer", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + numDecisions := 100 // 100 decisions per second + fixture := newTestFixture(t, testFixtureOptions{ + ReportingMaxDecisionsPerSecond: float64(numDecisions), + ReportingUploadSizeLimitBytes: defaultUploadSizeLimitBytes, + ReportingBufferType: tc.bufferType, + }) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 3) + + var input any = map[string]any{"method": "GET"} + var result1 any = false + + if err := fixture.plugin.Log(ctx, logServerInfo("abc", input, result1)); err != nil { + t.Fatal(err) + } + if err := fixture.plugin.Log(ctx, logServerInfo("def", input, result1)); err != nil { + t.Fatal(err) + } + if err := fixture.plugin.Log(ctx, logServerInfo("ghi", input, result1)); err != nil { + t.Fatal(err) + } + + eventSize := 218 + bufLen := getBufferLen(t, fixture, eventSize) + if bufLen != 3 { + t.Fatal("Expected buffer length of 3 but got ", bufLen) + } + + fixture.server.expCode = 500 + err := fixture.plugin.oneShot(ctx) + if err == nil { + t.Fatal("Expected error") + } + <-fixture.server.ch + + var event1, event2, event3 EventV1 + switch fixture.plugin.runningBuffer { + case eventBufferType: + // buffer will put a single event with the failed uploaded chunk back in the buffer + bufLen = getBufferLen(t, fixture, eventSize) + if bufLen != 1 { + t.Fatal("Expected buffer length of 3 but got ", bufLen) + } + + chunk := (<-fixture.plugin.eventBuffer.buffer).chunk + + events, err := newChunkDecoder(chunk).decode() + if err != nil { + t.Fatal(err) + } + event1 = events[0] + event2 = events[1] + event3 = events[2] + + case sizeBufferType: + // size buffer will put individual events with the failed uploaded chunk back in the buffer + bufLen = getBufferLen(t, fixture, eventSize) + if bufLen != 3 { + t.Fatal("Expected buffer length of 3 but got ", bufLen) + } + + chunks, err := fixture.plugin.enc.Flush() + if err != nil { + t.Fatal(err) + } + if len(chunks) != 1 { + t.Fatalf("Expected 1 chunk but got %v", len(chunks)) + } + events := decodeLogEvent(t, bytes.NewReader(chunks[0])) + event1 = events[0] + event2 = events[1] + event3 = events[2] + } + + exp := "abc" + if event1.DecisionID != exp { + t.Fatalf("Expected decision log event id %v but got %v", exp, event1.DecisionID) + } + + exp = "def" + if event2.DecisionID != exp { + t.Fatalf("Expected decision log event id %v but got %v", exp, event2.DecisionID) + } + + exp = "ghi" + if event3.DecisionID != exp { + t.Fatalf("Expected decision log event id %v but got %v", exp, event3.DecisionID) + } + + }) + } +} + +func TestPluginRateLimitDropCountStatus(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + bufferType string + }{ + { + name: "using event buffer", + bufferType: eventBufferType, + }, + { + name: "using size buffer", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + testLogger := test.New() + + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + t.Fatal(err) + } + + numDecisions := 1 // 1 decision per second + fixture := newTestFixture(t, testFixtureOptions{ + ConsoleLogger: testLogger, + ReportingMaxDecisionsPerSecond: float64(numDecisions), + ReportingUploadSizeLimitBytes: 300, + ReportingBufferType: tc.bufferType, + }) + defer fixture.server.stop() + + fixture.plugin.metrics = metrics.New() + + var input any = map[string]any{"method": "GET"} + var result any = false + + event1 := &server.Info{ + DecisionID: "abc", + Path: "foo/bar", + Input: &input, + Results: &result, + RemoteAddr: "test-1", + Timestamp: ts, + } + + event2 := &server.Info{ + DecisionID: "def", + Path: "foo/baz", + Input: &input, + Results: &result, + RemoteAddr: "test-2", + Timestamp: ts, + } + + event3 := &server.Info{ + DecisionID: "ghi", + Path: "foo/aux", + Input: &input, + Results: &result, + RemoteAddr: "test-3", + Timestamp: ts, + } + + if err := fixture.plugin.Log(ctx, event1); err != nil { + t.Fatal(err) + } + + eventSize := 217 + expectedLen := 1 + currentLen := getBufferLen(t, fixture, eventSize) + if currentLen != expectedLen { + t.Fatalf("Expected %v events to be written but got %v", expectedLen, currentLen) + } + + // Create a status plugin that logs to console + pluginConfig := []byte(`{ + "console": true, + }`) + + config, _ := status.ParseConfig(pluginConfig, fixture.manager.Services(), nil) + p := status.New(config, fixture.manager).WithMetrics(fixture.plugin.metrics) + + fixture.manager.Register(status.Name, p) + if err := fixture.manager.Start(ctx); err != nil { + t.Fatal(err) + } + + // event 2 should not be written into the encoder as rate limit exceeded + if err := fixture.plugin.Log(ctx, event2); err != nil { + t.Fatal(err) + } + // event 3 should not be written into the encoder as rate limit exceeded + if err := fixture.plugin.Log(ctx, event3); err != nil { + t.Fatal(err) + } + + // Trigger a status update + p.UpdateDiscoveryStatus(*testStatus()) + + // Give the logger / console some time to process and print the events + time.Sleep(10 * time.Millisecond) + p.Stop(ctx) + + entries := testLogger.Entries() + if len(entries) == 0 { + t.Fatal("Expected log entries but got none") + } + + // Pick the last entry as it should have the drop count + e := entries[len(entries)-1] + + if _, ok := e.Fields["metrics"]; !ok { + t.Fatal("Expected metrics") + } + + exp := map[string]any{"": map[string]any{"counter_decision_logs_dropped_rate_limit_exceeded": json.Number("2")}} + + if !reflect.DeepEqual(e.Fields["metrics"], exp) { + t.Fatalf("Expected %v but got %v", exp, e.Fields["metrics"]) + } + }) + } +} + +func TestPluginRateLimitBadConfig(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + config []byte + expectedErrMsg string + }{ + { + name: "invalid buffer_size_limit_bytes & max_decisions_per_second", + config: []byte(`{ + "console": true, + "reporting": { + "buffer_size_limit_bytes": 1, + "max_decisions_per_second": 1 + } + }`), + expectedErrMsg: "invalid decision_log config, specify either 'buffer_size_limit_bytes' or 'max_decisions_per_second'", + }, + { + name: "invalid buffer_size_limit_events used with size buffer", + config: []byte(`{ + "console": true, + "reporting": { + "buffer_size_limit_events": 1 + } + }`), + expectedErrMsg: "invalid decision_log config, 'buffer_size_limit_events' isn't supported for the size buffer type", + }, + { + name: "invalid buffer_size_limit_bytes used with event buffer", + config: []byte(`{ + "console": true, + "reporting": { + "buffer_type": "event", + "buffer_size_limit_bytes": 1 + } + }`), + expectedErrMsg: "invalid decision_log config, 'buffer_size_limit_bytes' isn't supported for the event buffer type", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + + manager, _ := plugins.New(nil, "test-instance-id", inmem.New()) + + _, err := ParseConfig(tc.config, manager.Services(), nil) + if err == nil { + t.Fatal("Expected error but got nil") + } + + if err.Error() != tc.expectedErrMsg { + t.Fatalf("Expected error message %v but got %v", tc.expectedErrMsg, err.Error()) + } + }) + } +} + +func TestPluginNoLogging(t *testing.T) { + t.Parallel() + + // Given no custom plugin, no service(s) and no console logging configured, + // this should not be an error, but neither do we need to initiate the plugin + cases := []struct { + note string + config []byte + }{ + { + note: "no plugin attributes", + config: []byte(`{}`), + }, + { + note: "empty plugin configuration", + config: []byte(`{"decision_logs": {}}`), + }, + { + note: "only disabled console logger", + config: []byte(`{"decision_logs": {"console": "false"}}`), + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + config, err := ParseConfig(tc.config, []string{}, nil) + if err != nil { + t.Errorf("expected no error: %v", err) + } + if config != nil { + t.Errorf("excected no config for a no-op logging plugin") + } + }) + } +} + +func TestPluginTriggerManual(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + reportingBufferType string + reportingBufferSizeLimitEvents int64 + }{ + { + name: "using event buffer", + reportingBufferType: "event", + }, + { + name: "using size buffer", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + fixture := newTestFixture(t, testFixtureOptions{ + ReportingBufferType: tc.reportingBufferType, + ReportingBufferSizeLimitEvents: tc.reportingBufferSizeLimitEvents, + }) + defer fixture.server.stop() + + fixture.server.server.Config.SetKeepAlivesEnabled(false) + + fixture.server.ch = make(chan []EventV1, 4) + tr := plugins.TriggerManual + fixture.plugin.config.Reporting.Trigger = &tr + + if err := fixture.plugin.Start(ctx); err != nil { + t.Fatal(err) + } + + testMetrics := getWellKnownMetrics() + msAsFloat64 := map[string]any{} + for k, v := range testMetrics.All() { + msAsFloat64[k] = float64(v.(uint64)) + } + + var input any = map[string]any{"method": "GET"} + var result any = false + + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + exp := EventV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + Path: "tda/bar", + Input: &input, + Result: &result, + RequestedBy: "test", + Timestamp: ts, + Metrics: msAsFloat64, + } + + for i := range 400 { + if err := fixture.plugin.Log(ctx, &server.Info{ + Revision: strconv.Itoa(i), + DecisionID: strconv.Itoa(i), + Path: "tda/bar", + Input: &input, + Results: &result, + RemoteAddr: "test", + Timestamp: ts, + Metrics: testMetrics, + }); err != nil { + t.Fatal(err) + } + + // trigger the decision log upload + go func(i int) { + fixture.plugin.Trigger(ctx) + }(i) + + fmt.Println("waiting") + chunk := <-fixture.server.ch + + expLen := 1 + if len(chunk) != 1 { + t.Fatalf("Expected chunk len %v but got: %v", expLen, len(chunk)) + } + + exp.Revision = strconv.Itoa(i) + exp.DecisionID = strconv.Itoa(i) + + if !reflect.DeepEqual(chunk[0], exp) { + t.Fatalf("Expected %+v but got %+v", exp, chunk[0]) + } + } + + fixture.plugin.Stop(ctx) + }) + } +} + +func TestPluginTriggerManualWithTimeout(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + time.Sleep(3 * time.Second) // this should cause the context deadline to exceed + })) + + // setup plugin pointing at fake server + managerConfig := fmt.Appendf(nil, `{ + "labels": { + "app": "example-app" + }, + "services": [ + { + "name": "example", + "url": %q + } + ]}`, s.URL) + + manager, err := plugins.New( + managerConfig, + "test-instance-id", + inmem.New(), + plugins.GracefulShutdownPeriod(10)) + if err != nil { + t.Fatal(err) + } + + pluginConfig := make(map[string]any) + + pluginConfig["service"] = "example" + pluginConfig["resource"] = "/" + + pluginConfigBytes, err := json.MarshalIndent(pluginConfig, "", " ") + if err != nil { + t.Fatal(err) + } + + config, _ := ParseConfig(pluginConfigBytes, manager.Services(), nil) + + tr := plugins.TriggerManual + config.Reporting.Trigger = &tr + + if s, ok := manager.PluginStatus()[Name]; ok { + t.Fatalf("Unexpected status found in plugin manager for %s: %+v", Name, s) + } + + p := New(config, manager) + + ensurePluginState(t, p, plugins.StateNotReady) + + if err := p.Start(ctx); err != nil { + t.Fatal(err) + } + + var input any = map[string]any{"method": "GET"} + var result any = false + + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + err = p.Log(ctx, &server.Info{ + DecisionID: "0", + Path: "tda/bar", + Input: &input, + Results: &result, + RemoteAddr: "test", + Timestamp: ts, + }) + if err != nil { + t.Fatal(err) + } + + done := make(chan struct{}) + go func() { + // this call should block till the context deadline exceeds + p.Trigger(ctx) + close(done) + }() + <-done + + if ctx.Err() == nil { + t.Fatal("Expected error but got nil") + } + + exp := "context deadline exceeded" + if ctx.Err().Error() != exp { + t.Fatalf("Expected error %v but got %v", exp, ctx.Err().Error()) + } + + p.Stop(ctx) +} + +func TestPluginGracefulShutdownFlushesDecisions(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + fixture := newTestFixture(t) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 8) + + if err := fixture.plugin.Start(ctx); err != nil { + t.Fatal(err) + } + + var input any + var result any = false + + logsSent := 200 + for i := range logsSent { + input = generateInputMap(i) + _ = fixture.plugin.Log(ctx, logServerInfo("abc", input, result)) + } + + fixture.server.expCode = 200 + + timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + fixture.plugin.Stop(timeoutCtx) + + close(fixture.server.ch) + logsReceived := 0 + for element := range fixture.server.ch { + logsReceived += len(element) + } + + if logsReceived != logsSent { + t.Fatalf("Expected %v, got %v", logsSent, logsReceived) + } +} + +func TestPluginTerminatesAfterGracefulShutdownPeriod(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + fixture := newTestFixture(t) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 1) + fixture.server.expCode = 500 + + if err := fixture.plugin.Start(ctx); err != nil { + t.Fatal(err) + } + + var result any = false + var input any = generateInputMap(0) + _ = fixture.plugin.Log(ctx, logServerInfo("abc", input, result)) + + timeoutCtx, cancel := context.WithTimeout(ctx, 1*time.Nanosecond) + defer cancel() + + fixture.plugin.Stop(timeoutCtx) + + // Ensure the plugin was stopped without flushing its whole buffer + if fixture.plugin.buffer.Len() == 0 && fixture.plugin.enc.buf.Len() == 0 { + t.Errorf("Expected the plugin to still have buffered messages") + } +} + +func TestPluginTerminatesAfterGracefulShutdownPeriodWithoutLogs(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + fixture := newTestFixture(t) + defer fixture.server.stop() + + if err := fixture.plugin.Start(ctx); err != nil { + t.Fatal(err) + } + + timeoutCtx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + fixture.plugin.Stop(timeoutCtx) + if timeoutCtx.Err() != nil { + t.Fatal("Stop did not exit before context expiration") + } +} + +func TestPluginReconfigure(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + currentBufferType string + newBufferType string + reportingBufferSizeLimitEvents int64 + }{ + { + name: "Reconfigure from event to size buffer", + currentBufferType: "event", + newBufferType: "size", + }, + { + name: "Reconfigure from size to event buffer", + currentBufferType: "size", + newBufferType: "event", + }, + { + name: "Reconfigure from size to size buffer", + }, + { + name: "Reconfigure from event to event buffer", + currentBufferType: "event", + newBufferType: "event", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + fixture := newTestFixture(t, testFixtureOptions{ + ReportingBufferType: tc.currentBufferType, + }) + defer fixture.server.stop() + + if err := fixture.plugin.Start(ctx); err != nil { + t.Fatal(err) + } + + ensurePluginState(t, fixture.plugin, plugins.StateOK) + + minDelay := 2 + maxDelay := 3 + + pluginConfig := fmt.Appendf(nil, `{ + "service": "example", + "reporting": { + "buffer_type": %v, + "min_delay_seconds": %v, + "max_delay_seconds": %v + } + }`, tc.newBufferType, minDelay, maxDelay) + + config, _ := ParseConfig(pluginConfig, fixture.manager.Services(), nil) + + fixture.plugin.Reconfigure(ctx, config) + ensurePluginState(t, fixture.plugin, plugins.StateOK) + + fixture.plugin.Stop(ctx) + ensurePluginState(t, fixture.plugin, plugins.StateNotReady) + + actualMin := time.Duration(*fixture.plugin.config.Reporting.MinDelaySeconds) / time.Nanosecond + expectedMin := time.Duration(minDelay) * time.Second + + if actualMin != expectedMin { + t.Fatalf("Expected minimum polling interval: %v but got %v", expectedMin, actualMin) + } + + actualMax := time.Duration(*fixture.plugin.config.Reporting.MaxDelaySeconds) / time.Nanosecond + expectedMax := time.Duration(maxDelay) * time.Second + + if actualMax != expectedMax { + t.Fatalf("Expected maximum polling interval: %v but got %v", expectedMax, actualMax) + } + }) + } +} + +func TestPluginReconfigureUploadSizeLimit(t *testing.T) { + t.Parallel() + + ctx := context.Background() + limit := int64(300) + + fixture := newTestFixture(t, testFixtureOptions{ + ReportingUploadSizeLimitBytes: limit, + }) + defer fixture.server.stop() + + if err := fixture.plugin.Start(ctx); err != nil { + t.Fatal(err) + } + + ensurePluginState(t, fixture.plugin, plugins.StateOK) + + fixture.plugin.mtx.Lock() + if fixture.plugin.enc.limit != limit { + t.Fatalf("Expected upload size limit %v but got %v", limit, fixture.plugin.enc.limit) + } + fixture.plugin.mtx.Unlock() + + newLimit := int64(600) + + pluginConfig := fmt.Appendf(nil, `{ + "service": "example", + "reporting": { + "upload_size_limit_bytes": %v, + } + }`, newLimit) + + config, _ := ParseConfig(pluginConfig, fixture.manager.Services(), nil) + + fixture.plugin.Reconfigure(ctx, config) + ensurePluginState(t, fixture.plugin, plugins.StateOK) + + fixture.plugin.Stop(ctx) + ensurePluginState(t, fixture.plugin, plugins.StateNotReady) + + fixture.plugin.mtx.Lock() + if fixture.plugin.enc.limit != newLimit { + t.Fatalf("Expected upload size limit %v but got %v", newLimit, fixture.plugin.enc.limit) + } + fixture.plugin.mtx.Unlock() +} + +type appendingPrintHook struct { + printed *[]string +} + +func (a appendingPrintHook) Print(_ print.Context, s string) error { + *a.printed = append(*a.printed, s) + return nil +} + +func TestPluginMasking(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + rawPolicy []byte + expErased []string + expMasked []string + expPrinted []string + errManager error + expErr error + input any + expected any + ndbcache any + ndbc_expected any + reconfigure bool + }{ + { + note: "simple erase (with body true)", + rawPolicy: []byte(` + package system.log + import rego.v1 + mask contains "/input/password" if { + input.input.is_sensitive + }`), + expErased: []string{"/input/password"}, + input: map[string]any{ + "is_sensitive": true, + "password": "secret", + }, + expected: map[string]any{ + "is_sensitive": true, + }, + }, + { + note: "simple erase (with body true, plugin reconfigured)", + rawPolicy: []byte(` + package system.log + import rego.v1 + mask contains "/input/password" if { + input.input.is_sensitive + }`), + expErased: []string{"/input/password"}, + input: map[string]any{ + "is_sensitive": true, + "password": "secret", + }, + expected: map[string]any{ + "is_sensitive": true, + }, + reconfigure: true, + }, + { + note: "simple upsert (with body true)", + rawPolicy: []byte(` + package system.log + import rego.v1 + mask contains {"op": "upsert", "path": "/input/password", "value": x} if { + input.input.password + x := "**REDACTED**" + }`), + expMasked: []string{"/input/password"}, + input: map[string]any{ + "is_sensitive": true, + "password": "mySecretPassword", + }, + expected: map[string]any{ + "is_sensitive": true, + "password": "**REDACTED**", + }, + }, + { + note: "remove even with value set in rule body", + rawPolicy: []byte(` + package system.log + import rego.v1 + mask contains {"op": "remove", "path": "/input/password", "value": x} if { + input.input.password + x := "**REDACTED**" + }`), + expErased: []string{"/input/password"}, + input: map[string]any{ + "is_sensitive": true, + "password": "mySecretPassword", + }, + expected: map[string]any{ + "is_sensitive": true, + }, + }, + { + note: "remove when value not defined", + rawPolicy: []byte(` + package system.log + import rego.v1 + mask contains {"op": "remove", "path": "/input/password"} if { + input.input.password + }`), + expErased: []string{"/input/password"}, + input: map[string]any{ + "is_sensitive": true, + "password": "mySecretPassword", + }, + expected: map[string]any{ + "is_sensitive": true, + }, + }, + { + note: "remove when value not defined in rule body", + rawPolicy: []byte(` + package system.log + import rego.v1 + mask contains {"op": "remove", "path": "/input/password", "value": x} if { + input.input.password + }`), + errManager: errors.New("1 error occurred: test.rego:4: rego_unsafe_var_error: var x is unsafe"), + }, + { + note: "simple erase - no match", + rawPolicy: []byte(` + package system.log + mask["/input/password"] { + input.input.is_sensitive + }`), + input: map[string]any{ + "is_not_sensitive": true, + "password": "secret", + }, + expected: map[string]any{ + "is_not_sensitive": true, + "password": "secret", + }, + }, + { + note: "complex upsert - object key", + rawPolicy: []byte(` + package system.log + import rego.v1 + mask contains {"op": "upsert", "path": "/input/foo", "value": x} if { + input.input.foo + x := [ + {"nabs": 1} + ] + }`), + input: map[string]any{ + "bar": 1, + "foo": []map[string]any{{"baz": 1}}, + }, + // Due to ast.JSON() parsing as part of rego.eval, internal mapped + // types from mask rule valuations (for numbers) will be json.Number. + // This affects explicitly providing the expected any value. + // + // See TestMaksRuleErase where tests are written to confirm json marshalled + // output is as expected. + expected: map[string]any{ + "bar": 1, + "foo": []any{map[string]any{"nabs": json.Number("1")}}, + }, + }, + { + note: "upsert failure: unsupported type []map[string]any", + rawPolicy: []byte(` + package system.log + mask[{"op": "upsert", "path": "/input/foo/boo", "value": x}] { + x := [ + {"nabs": 1} + ] + }`), + input: map[string]any{ + "bar": json.Number("1"), + "foo": []map[string]any{{"baz": json.Number("1")}}, + }, + expected: map[string]any{ + "bar": json.Number("1"), + "foo": []map[string]any{{"baz": json.Number("1")}}, + }, + }, + { + note: "mixed mode - complex #1", + rawPolicy: []byte(` + package system.log + + import rego.v1 + + mask contains "/input/password" if { + input.input.is_sensitive + } + + # invalidate JWT signature + mask contains {"op": "upsert", "path": "/input/jwt", "value": x} if { + input.input.jwt + + # split jwt string + parts := split(input.input.jwt, ".") + + # make sure we have 3 parts + count(parts) == 3 + + # replace signature + new := array.concat(array.slice(parts, 0, 2), [base64url.encode("**REDACTED**")]) + x = concat(".", new) + + } + + mask contains {"op": "upsert", "path": "/input/foo", "value": x} if { + input.input.foo + x := [ + {"changed": 1} + ] + }`), + input: map[string]any{ + "is_sensitive": true, + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.cThIIoDvwdueQB468K5xDc5633seEFoqwxjF_xSJyQQ", + "bar": 1, + "foo": []map[string]any{{"baz": 1}}, + "password": "mySecretPassword", + }, + expected: map[string]any{ + "is_sensitive": true, + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.KipSRURBQ1RFRCoq", + "bar": 1, + "foo": []any{map[string]any{"changed": json.Number("1")}}, + }, + }, + { + note: "print() works", + rawPolicy: []byte(` + package system.log + import rego.v1 + mask contains "/input/password" if { + print("Erasing /input/password") + input.input.is_sensitive + }`), + expErased: []string{"/input/password"}, + input: map[string]any{ + "is_sensitive": true, + "password": "secret", + }, + expected: map[string]any{ + "is_sensitive": true, + }, + expPrinted: []string{"Erasing /input/password"}, + }, + { + note: "simple upsert on nd_builtin_cache", + rawPolicy: []byte(` + package system.log + import rego.v1 + mask contains {"op": "upsert", "path": "/nd_builtin_cache/rand.intn", "value": x} if { + input.nd_builtin_cache["rand.intn"] + x := "**REDACTED**" + }`), + expMasked: []string{"/nd_builtin_cache/rand.intn"}, + ndbcache: map[string]any{ + // Simulate rand.intn("z", 15) call, with output of 7. + "rand.intn": map[string]any{"[\"z\",15]": json.Number("7")}, + }, + ndbc_expected: map[string]any{ + "rand.intn": "**REDACTED**", + }, + }, + { + note: "simple upsert on nd_builtin_cache with multiple entries", + rawPolicy: []byte(` + package system.log + import rego.v1 + mask contains {"op": "upsert", "path": "/nd_builtin_cache/rand.intn", "value": x} if { + input.nd_builtin_cache["rand.intn"] + x := "**REDACTED**" + } + + mask contains {"op": "upsert", "path": "/nd_builtin_cache/net.lookup_ip_addr", "value": y} if { + obj := input.nd_builtin_cache["net.lookup_ip_addr"] + y := object.union({k: "4.4.x.x" | obj[k]; startswith(k, "[\"4.4.")}, + {k: obj[k] | obj[k]; not startswith(k, "[\"4.4.")}) + } + `), + expMasked: []string{"/nd_builtin_cache/net.lookup_ip_addr", "/nd_builtin_cache/rand.intn"}, + ndbcache: map[string]any{ + // Simulate rand.intn("z", 15) call, with output of 7. + "rand.intn": map[string]any{"[\"z\",15]": json.Number("7")}, + "net.lookup_ip_addr": map[string]any{ + "[\"1.1.1.1\"]": "1.1.1.1", + "[\"2.2.2.2\"]": "2.2.2.2", + "[\"3.3.3.3\"]": "3.3.3.3", + "[\"4.4.4.4\"]": "4.4.4.4", + }, + }, + ndbc_expected: map[string]any{ + "rand.intn": "**REDACTED**", + "net.lookup_ip_addr": map[string]any{ + "[\"1.1.1.1\"]": "1.1.1.1", + "[\"2.2.2.2\"]": "2.2.2.2", + "[\"3.3.3.3\"]": "3.3.3.3", + "[\"4.4.4.4\"]": "4.4.x.x", + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + // Setup masking fixture. Populate store with simple masking policy. + ctx := context.Background() + store := inmem.New() + + err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + if err := store.UpsertPolicy(ctx, txn, "test.rego", tc.rawPolicy); err != nil { + return err + } + return nil + }) + if err != nil { + t.Fatal(err) + } + + var output []string + + // Create and start manager. Start is required so that stored policies + // get compiled and made available to the plugin. + manager, err := plugins.New( + nil, + "test", + store, + plugins.EnablePrintStatements(true), + plugins.PrintHook(appendingPrintHook{printed: &output}), + ) + if err != nil { + t.Fatal(err) + } else if err := manager.Start(ctx); err != nil { + if tc.errManager != nil { + if tc.errManager.Error() != err.Error() { + t.Fatalf("expected error %s, but got %s", tc.errManager.Error(), err.Error()) + } + return + } + } + + // Instantiate the plugin. + cfg := &Config{Service: "svc"} + trigger := plugins.DefaultTriggerMode + if err := cfg.validateAndInjectDefaults([]string{"svc"}, nil, &trigger, nil); err != nil { + t.Fatal(err) + } + + plugin := New(cfg, manager) + + if err := plugin.Start(ctx); err != nil { + t.Fatal(err) + } + + event := &EventV1{ + Input: &tc.input, + NDBuiltinCache: &tc.ndbcache, + } + input, err := event.AST() + if err != nil { + t.Fatal(err) + } + + if err := plugin.maskEvent(ctx, nil, input, event); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(tc.expected, *event.Input) { + t.Fatalf("Expected %#+v but got %#+v:", tc.expected, *event.Input) + } + + if !reflect.DeepEqual(tc.ndbc_expected, *event.NDBuiltinCache) { + t.Fatalf("Expected %#+v but got %#+v:", tc.ndbc_expected, *event.NDBuiltinCache) + } + + if len(tc.expErased) > 0 { + if !reflect.DeepEqual(tc.expErased, event.Erased) { + t.Fatalf("Expected erased %v set but got %v", tc.expErased, event.Erased) + } + } + + if len(tc.expMasked) > 0 { + if !reflect.DeepEqual(tc.expMasked, event.Masked) { + t.Fatalf("Expected masked %v set but got %v", tc.expMasked, event.Masked) + } + } + + if !reflect.DeepEqual(tc.expPrinted, output) { + t.Errorf("Expected output %v, got %v", tc.expPrinted, output) + } + + // if reconfigure in test is on + if tc.reconfigure { + // Reconfigure and ensure that mask is invalidated. + maskDecision := "dead/beef" + newConfig := &Config{Service: "svc", MaskDecision: &maskDecision} + if err := newConfig.validateAndInjectDefaults([]string{"svc"}, nil, &trigger, nil); err != nil { + t.Fatal(err) + } + + plugin.Reconfigure(ctx, newConfig) + + event = &EventV1{ + Input: &tc.input, + } + input, err := event.AST() + if err != nil { + t.Fatal(err) + } + + if err := plugin.maskEvent(ctx, nil, input, event); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(*event.Input, tc.input) { + t.Fatalf("Expected %v but got modified input %v", tc.input, event.Input) + } + + } + }) + } +} + +func TestPluginDrop(t *testing.T) { + t.Parallel() + + // Test cases + tests := []struct { + note string + rawPolicy []byte + event *EventV1 + expected bool + }{ + { + note: "simple drop", + rawPolicy: []byte(` + package system.log + import rego.v1 + drop if { + endswith(input.path, "bar") + }`), + event: &EventV1{Path: "foo/bar"}, + + expected: true, + }, + { + note: "no drop", + rawPolicy: []byte(` + package system.log + import rego.v1 + drop if { + endswith(input.path, "bar") + }`), + event: &EventV1{Path: "foo/foo"}, + expected: false, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + // Setup fixture. Populate store with simple drop policy. + ctx := context.Background() + store := inmem.New() + + //checks if raw policy is valid and stores policy in store + err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + if err := store.UpsertPolicy(ctx, txn, "test.rego", tc.rawPolicy); err != nil { + return err + } + return nil + }) + if err != nil { + t.Fatal(err) + } + + var output []string + + // Create and start manager. Start is required so that stored policies + // get compiled and made available to the plugin. + manager, err := plugins.New( + nil, + "test", + store, + plugins.EnablePrintStatements(true), + plugins.PrintHook(appendingPrintHook{printed: &output}), + ) + if err != nil { + t.Fatal(err) + } + if err := manager.Start(ctx); err != nil { + t.Fatal(err) + } + + // Instantiate the plugin. + cfg := &Config{Service: "svc"} + trigger := plugins.DefaultTriggerMode + if err := cfg.validateAndInjectDefaults([]string{"svc"}, nil, &trigger, nil); err != nil { + t.Fatal(err) + } + + plugin := New(cfg, manager) + + if err := plugin.Start(ctx); err != nil { + t.Fatal(err) + } + input, err := tc.event.AST() + if err != nil { + t.Fatal(err) + } + + drop, err := plugin.dropEvent(ctx, nil, input) + if err != nil { + t.Fatal(err) + } + + if tc.expected != drop { + t.Errorf("Plugin: Expected drop to be %v got %v", tc.expected, drop) + } + }) + } +} + +func TestPluginMaskErrorHandling(t *testing.T) { + t.Parallel() + + rawPolicy := []byte(` + package system.log + import rego.v1 + drop if { + endswith(input.path, "bar") + }`) + event := &EventV1{Path: "foo/bar"} + + // Setup fixture. Populate store with simple drop policy. + ctx := context.Background() + store := inmem.New() + + // checks if raw policy is valid and stores policy in store + err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + if err := store.UpsertPolicy(ctx, txn, "test.rego", rawPolicy); err != nil { + return err + } + return nil + }) + if err != nil { + t.Fatal(err) + } + + var output []string + + // Create and start manager. Start is required so that stored policies + // get compiled and made available to the plugin. + manager, err := plugins.New( + nil, + "test", + store, + plugins.EnablePrintStatements(true), + plugins.PrintHook(appendingPrintHook{printed: &output}), + ) + if err != nil { + t.Fatal(err) + } + if err := manager.Start(ctx); err != nil { + t.Fatal(err) + } + + // Instantiate the plugin. + cfg := &Config{Service: "svc"} + trigger := plugins.DefaultTriggerMode + if err := cfg.validateAndInjectDefaults([]string{"svc"}, nil, &trigger, nil); err != nil { + t.Fatal(err) + } + + plugin := New(cfg, manager) + + if err := plugin.Start(ctx); err != nil { + t.Fatal(err) + } + input, err := event.AST() + if err != nil { + t.Fatal(err) + } + + type badTransaction struct { + storage.Transaction + } + + expErr := "storage_invalid_txn_error: unexpected transaction type *logs.badTransaction" + err = plugin.maskEvent(ctx, &badTransaction{}, input, event) + if err.Error() != expErr { + t.Fatalf("Expected error %v got %v", expErr, err) + } + + // We expect the same error on a second call, even though the mask query failed to prepare and won't be prepared again. + err = plugin.maskEvent(ctx, nil, input, event) + if err.Error() != expErr { + t.Fatalf("Expected error %v got %v", expErr, err) + } +} + +func TestPluginDropErrorHandling(t *testing.T) { + t.Parallel() + + rawPolicy := []byte(` + package system.log + import rego.v1 + drop if { + endswith(input.path, "bar") + }`) + event := &EventV1{Path: "foo/bar"} + + // Setup fixture. Populate store with simple drop policy. + ctx := context.Background() + store := inmem.New() + + //checks if raw policy is valid and stores policy in store + err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + if err := store.UpsertPolicy(ctx, txn, "test.rego", rawPolicy); err != nil { + return err + } + return nil + }) + if err != nil { + t.Fatal(err) + } + + var output []string + + // Create and start manager. Start is required so that stored policies + // get compiled and made available to the plugin. + manager, err := plugins.New( + nil, + "test", + store, + plugins.EnablePrintStatements(true), + plugins.PrintHook(appendingPrintHook{printed: &output}), + ) + if err != nil { + t.Fatal(err) + } + if err := manager.Start(ctx); err != nil { + t.Fatal(err) + } + + // Instantiate the plugin. + cfg := &Config{Service: "svc"} + trigger := plugins.DefaultTriggerMode + if err := cfg.validateAndInjectDefaults([]string{"svc"}, nil, &trigger, nil); err != nil { + t.Fatal(err) + } + + plugin := New(cfg, manager) + + if err := plugin.Start(ctx); err != nil { + t.Fatal(err) + } + input, err := event.AST() + if err != nil { + t.Fatal(err) + } + + type badTransaction struct { + storage.Transaction + } + + expErr := "storage_invalid_txn_error: unexpected transaction type *logs.badTransaction" + _, err = plugin.dropEvent(ctx, &badTransaction{}, input) + if err.Error() != expErr { + t.Fatalf("Expected error %v got %v", expErr, err) + } + + // We expect the same error on a second call, even though the drop query failed to prepare and won't be prepared again. + _, err = plugin.dropEvent(ctx, nil, input) + if err.Error() != expErr { + t.Fatalf("Expected error %v got %v", expErr, err) + } +} + +type testFixtureOptions struct { + ConsoleLogger *test.Logger + ReportingBufferType string + ReportingBufferSizeLimitEvents int64 + ReportingUploadSizeLimitBytes int64 + ReportingMaxDecisionsPerSecond float64 + ReportingBufferSizeLimitBytes int64 + Resource *string + TestServerPath *string + PartitionName *string + ExtraConfig map[string]any + ExtraManagerConfig map[string]any + ManagerInit func(*plugins.Manager) +} + +type testFixture struct { + manager *plugins.Manager + consoleLogger *test.Logger + plugin *Plugin + server *testServer +} + +func newTestFixture(t *testing.T, opts ...testFixtureOptions) testFixture { + var options testFixtureOptions + if len(opts) > 0 { + options = opts[0] + } + + ts := testServer{ + t: t, + expCode: 200, + } + + ts.start() + + managerConfig := fmt.Appendf(nil, `{ + "labels": { + "app": "example-app" + }, + "services": [ + { + "name": "example", + "url": %q, + "credentials": { + "bearer": { + "scheme": "Bearer", + "token": "secret" + } + } + } + ]}`, ts.server.URL) + + mgrCfg := make(map[string]any) + err := json.Unmarshal(managerConfig, &mgrCfg) + if err != nil { + t.Fatal(err) + } + maps.Copy(mgrCfg, options.ExtraManagerConfig) + managerConfig, err = json.MarshalIndent(mgrCfg, "", " ") + if err != nil { + t.Fatal(err) + } + + manager, err := plugins.New( + managerConfig, + "test-instance-id", + inmem.New(), + plugins.GracefulShutdownPeriod(10), + plugins.ConsoleLogger(options.ConsoleLogger)) + if err != nil { + t.Fatal(err) + } + if init := options.ManagerInit; init != nil { + init(manager) + } + + pluginConfig := map[string]any{ + "service": "example", + } + + if options.Resource != nil { + pluginConfig["resource"] = *options.Resource + } + + if options.PartitionName != nil { + pluginConfig["partition_name"] = *options.PartitionName + } + + maps.Copy(pluginConfig, options.ExtraConfig) + + pluginConfigBytes, err := json.MarshalIndent(pluginConfig, "", " ") + if err != nil { + t.Fatal(err) + } + + config, err := ParseConfig(pluginConfigBytes, manager.Services(), manager.Plugins()) + if err != nil { + t.Fatal(err) + } + + if options.TestServerPath != nil { + ts.path = *options.TestServerPath + } + + if options.ReportingMaxDecisionsPerSecond != 0 { + config.Reporting.MaxDecisionsPerSecond = &options.ReportingMaxDecisionsPerSecond + } + + if options.ReportingUploadSizeLimitBytes != 0 { + config.Reporting.UploadSizeLimitBytes = &options.ReportingUploadSizeLimitBytes + } + + ts.uploadLimit = *config.Reporting.UploadSizeLimitBytes + + if options.ReportingBufferSizeLimitBytes != 0 { + config.Reporting.BufferSizeLimitBytes = &options.ReportingBufferSizeLimitBytes + } + + if options.ReportingBufferSizeLimitEvents != 0 { + config.Reporting.BufferSizeLimitEvents = &options.ReportingBufferSizeLimitEvents + } + + if options.ReportingBufferType != "" { + config.Reporting.BufferType = options.ReportingBufferType + } + + if s, ok := manager.PluginStatus()[Name]; ok { + t.Fatalf("Unexpected status found in plugin manager for %s: %+v", Name, s) + } + + p := New(config, manager) + + ensurePluginState(t, p, plugins.StateNotReady) + + return testFixture{ + manager: manager, + consoleLogger: options.ConsoleLogger, + plugin: p, + server: &ts, + } + +} + +func TestParseConfigUseDefaultServiceNoConsole(t *testing.T) { + t.Parallel() + + services := []string{ + "s0", + "s1", + "s3", + } + + loggerConfig := []byte(`{ + "console": false + }`) + + config, err := ParseConfig([]byte(loggerConfig), services, nil) + + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + + if config.Service != services[0] { + t.Errorf("Expected %s service in config, actual = '%s'", services[0], config.Service) + } +} + +func TestParseConfigDefaultServiceWithConsole(t *testing.T) { + t.Parallel() + + services := []string{ + "s0", + "s1", + "s3", + } + + loggerConfig := []byte(`{ + "console": true + }`) + + config, err := ParseConfig([]byte(loggerConfig), services, nil) + + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + + if config.Service != "" { + t.Errorf("Expected no service in config, actual = '%s'", config.Service) + } +} + +func TestParseConfigTriggerMode(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + config []byte + expected plugins.TriggerMode + wantErr bool + err error + }{ + { + note: "default trigger mode", + config: []byte(`{}`), + expected: plugins.DefaultTriggerMode, + }, + { + note: "manual trigger mode", + config: []byte(`{"reporting": {"trigger": "manual"}}`), + expected: plugins.TriggerManual, + }, + { + note: "trigger mode mismatch", + config: []byte(`{"reporting": {"trigger": "manual"}}`), + expected: plugins.TriggerPeriodic, + wantErr: true, + err: errors.New("invalid decision_log config: trigger mode mismatch: periodic and manual (hint: check discovery configuration)"), + }, + { + note: "bad trigger mode", + config: []byte(`{"reporting": {"trigger": "foo"}}`), + expected: "foo", + wantErr: true, + err: errors.New("invalid decision_log config: invalid trigger mode \"foo\" (want \"periodic\" or \"manual\")"), + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + + c, err := NewConfigBuilder().WithBytes(tc.config).WithServices([]string{"s0"}).WithTriggerMode(&tc.expected).Parse() + + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else { + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if *c.Reporting.Trigger != tc.expected { + t.Fatalf("Expected trigger mode %v but got %v", tc.expected, *c.Reporting.Trigger) + } + } + }) + } +} + +func TestEventV1ToAST(t *testing.T) { + t.Parallel() + + input := `{"foo": [{"bar": 1, "baz": {"2": 3.3333333, "4": null}}]}` + var goInput any = string(util.MustMarshalJSON(input)) + astInput, err := roundtripJSONToAST(goInput) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + var result any = map[string]any{ + "x": true, + } + + var bigEvent EventV1 + if err := util.UnmarshalJSON([]byte(largeEvent), &bigEvent); err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + var ndbCacheExample any = ast.MustJSON(builtins.NDBCache{ + "time.now_ns": ast.NewObject([2]*ast.Term{ + ast.ArrayTerm(), + ast.NumberTerm("1663803565571081429"), + }), + }.AsValue()) + + cases := []struct { + note string + event EventV1 + }{ + { + note: "empty event", + event: EventV1{}, + }, + { + note: "basic event no result", + event: EventV1{ + Labels: map[string]string{"foo": "1", "bar": "2"}, + DecisionID: "1234567890", + Path: "/http/authz/allow", + RequestedBy: "[::1]:59943", + Timestamp: time.Now(), + }, + }, + { + note: "event with error", + event: EventV1{ + Labels: map[string]string{}, + DecisionID: "1234567890", + Path: "/system/main", + Error: rego.Errors{&topdown.Error{ + Code: topdown.BuiltinErr, + Message: "Some error happened somewhere", + Location: ast.NewLocation([]byte("myfunc(x)"), "policy.rego", 22, 17), + }}, + RequestedBy: "[::1]:59943", + Timestamp: time.Now(), + }, + }, + { + note: "event with input and result", + event: EventV1{ + Labels: map[string]string{"foo": "1", "bar": "2"}, + DecisionID: "1234567890", + Input: &goInput, + Path: "/http/authz/allow", + RequestedBy: "[::1]:59943", + Result: &result, + Timestamp: time.Now(), + inputAST: astInput, + }, + }, + { + note: "event without ast input", + event: EventV1{ + Labels: map[string]string{"foo": "1", "bar": "2"}, + DecisionID: "1234567890", + Input: &goInput, + Path: "/http/authz/allow", + RequestedBy: "[::1]:59943", + Result: &result, + Timestamp: time.Now(), + }, + }, + { + note: "event with bundles", + event: EventV1{ + Labels: map[string]string{"foo": "1", "bar": "2"}, + DecisionID: "1234567890", + Bundles: map[string]BundleInfoV1{ + "b1": {"revision7"}, + "b2": {"0"}, + "b3": {}, + }, + Input: &goInput, + Path: "/http/authz/allow", + RequestedBy: "[::1]:59943", + Result: &result, + Timestamp: time.Now(), + inputAST: astInput, + }, + }, + { + note: "event with erased", + event: EventV1{ + Erased: []string{"input/password", "result/secret"}, + Labels: map[string]string{"foo": "1", "bar": "2"}, + DecisionID: "1234567890", + Bundles: map[string]BundleInfoV1{ + "b1": {"revision7"}, + "b2": {"0"}, + "b3": {}, + }, + Input: &goInput, + Path: "/http/authz/allow", + RequestedBy: "[::1]:59943", + Result: &result, + Timestamp: time.Now(), + inputAST: astInput, + }, + }, + { + note: "event with masked", + event: EventV1{ + Masked: []string{"input/password", "result/secret"}, + Labels: map[string]string{"foo": "1", "bar": "2"}, + DecisionID: "1234567890", + Bundles: map[string]BundleInfoV1{ + "b1": {"revision7"}, + "b2": {"0"}, + "b3": {}, + }, + Input: &goInput, + Path: "/http/authz/allow", + RequestedBy: "[::1]:59943", + Result: &result, + Timestamp: time.Now(), + inputAST: astInput, + }, + }, + { + note: "big event", + event: bigEvent, + }, + { + note: "event with nd_builtin_cache", + event: EventV1{ + Labels: map[string]string{"foo": "1", "bar": "2"}, + DecisionID: "1234567890", + Bundles: map[string]BundleInfoV1{ + "b1": {"revision7"}, + "b2": {"0"}, + "b3": {}, + }, + Input: &goInput, + Path: "/http/authz/allow", + RequestedBy: "[::1]:59943", + Result: &result, + Timestamp: time.Now(), + inputAST: astInput, + NDBuiltinCache: &ndbCacheExample, + }, + }, + { + note: "event with req id", + event: EventV1{ + Labels: map[string]string{"foo": "1", "bar": "2"}, + DecisionID: "1234567890", + Bundles: map[string]BundleInfoV1{ + "b1": {"revision7"}, + "b2": {"0"}, + "b3": {}, + }, + Input: &goInput, + Path: "/http/authz/allow", + RequestedBy: "[::1]:59943", + Result: &result, + Timestamp: time.Now(), + RequestID: 1, + inputAST: astInput, + }, + }, + { + note: "event with batch decision id", + event: EventV1{ + Labels: map[string]string{"foo": "1", "bar": "2"}, + DecisionID: "1234567890", + BatchDecisionID: "abcdefghij", + Bundles: map[string]BundleInfoV1{ + "b1": {"revision7"}, + "b2": {"0"}, + "b3": {}, + }, + Input: &goInput, + Path: "/http/authz/allow", + RequestedBy: "[::1]:59943", + Result: &result, + Timestamp: time.Now(), + RequestID: 1, + inputAST: astInput, + }, + }, + { + note: "event with intermediate results", + event: EventV1{ + Labels: map[string]string{"foo": "1", "bar": "2"}, + DecisionID: "1234567890", + Bundles: map[string]BundleInfoV1{ + "b1": {"revision7"}, + "b2": {"0"}, + "b3": {}, + }, + Input: &goInput, + Path: "/http/authz/allow", + RequestedBy: "[::1]:59943", + Result: &result, + IntermediateResults: map[string]any{"foo": "bar"}, + Timestamp: time.Now(), + RequestID: 1, + inputAST: astInput, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + + // Ensure that the custom AST() function gives the same + // result as round tripping through JSON + + expected, err := roundtripJSONToAST(tc.event) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + actual, err := tc.event.AST() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if expected.Compare(actual) != 0 { + t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n\n", expected, actual) + } + }) + } +} + +func TestPluginDefaultResourcePath(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + reportingBufferType string + reportingBufferSizeLimitEvents int64 + }{ + { + name: "using event buffer", + reportingBufferType: "event", + }, + { + name: "using size buffer", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + testServerPath := "/logs" + + fixture := newTestFixture(t, testFixtureOptions{ + TestServerPath: &testServerPath, + }) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 1) + + var input any = map[string]any{"method": "GET"} + var result1 any = false + + if err := fixture.plugin.Log(ctx, &server.Info{ + DecisionID: "abc", + Path: "data.foo.bar", + Input: &input, + Results: &result1, + RemoteAddr: "test", + Timestamp: time.Now().UTC(), + }); err != nil { + t.Fatal(err) + } + + if *fixture.plugin.config.Resource != defaultResourcePath { + t.Errorf("Expected the resource path to be the default %s, actual = '%s'", defaultResourcePath, *fixture.plugin.config.Resource) + } + + fixture.server.expCode = 200 + + err := fixture.plugin.oneShot(ctx) + if err != nil { + t.Fatal(err) + } + }) + } +} + +func TestPluginResourcePathAndPartitionName(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + reportingBufferType string + reportingBufferSizeLimitEvents int64 + }{ + { + name: "using event buffer", + reportingBufferType: "event", + }, + { + name: "using size buffer", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + resourcePath := "/resource/path" + partitionName := "partition" + expectedPath := fmt.Sprintf("/logs/%v", partitionName) + + fixture := newTestFixture(t, testFixtureOptions{ + Resource: &resourcePath, + TestServerPath: &expectedPath, + PartitionName: &partitionName, + }) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 1) + + var input any = map[string]any{"method": "GET"} + var result1 any = false + + if err := fixture.plugin.Log(ctx, &server.Info{ + DecisionID: "abc", + Path: "data.foo.bar", + Input: &input, + Results: &result1, + RemoteAddr: "test", + Timestamp: time.Now().UTC(), + }); err != nil { + t.Fatal(err) + } + + if *fixture.plugin.config.Resource != expectedPath { + t.Errorf("Expected resource to be %s, but got %s", expectedPath, *fixture.plugin.config.Resource) + } + + fixture.server.expCode = 200 + + err := fixture.plugin.oneShot(ctx) + if err != nil { + t.Fatal(err) + } + }) + } +} + +func TestPluginResourcePath(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + reportingBufferType string + reportingBufferSizeLimitEvents int64 + }{ + { + name: "using event buffer", + reportingBufferType: "event", + }, + { + name: "using size buffer", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + resourcePath := "/plugin/log/path" + testServerPath := "/plugin/log/path" + + fixture := newTestFixture(t, testFixtureOptions{ + ReportingBufferType: tc.reportingBufferType, + Resource: &resourcePath, + TestServerPath: &testServerPath, + }) + defer fixture.server.stop() + + fixture.server.ch = make(chan []EventV1, 1) + + var input any = map[string]any{"method": "GET"} + var result1 any = false + + if err := fixture.plugin.Log(ctx, &server.Info{ + DecisionID: "abc", + Path: "data.foo.bar", + Input: &input, + Results: &result1, + RemoteAddr: "test", + Timestamp: time.Now().UTC(), + }); err != nil { + t.Fatal(err) + } + + if *fixture.plugin.config.Resource != resourcePath { + t.Errorf("Expected resource to be %s, but got %s", resourcePath, *fixture.plugin.config.Resource) + } + + fixture.server.expCode = 200 + + err := fixture.plugin.oneShot(ctx) + if err != nil { + t.Fatal(err) + } + }) + } +} + +type testServer struct { + t *testing.T + expCode int + server *httptest.Server + ch chan []EventV1 + path string + uploadLimit int64 +} + +func (t *testServer) handle(w http.ResponseWriter, r *http.Request) { + t.t.Helper() + + b, err := io.ReadAll(r.Body) + if err != nil { + t.t.Fatal(err) + } + + if int64(len(b)) > t.uploadLimit { + t.t.Fatalf("upload limit exceeded expected less than %d but got %d", t.uploadLimit, int64(len(b))) + } + + gr, err := gzip.NewReader(bytes.NewReader(b)) + if err != nil { + t.t.Fatal(err) + } + var events []EventV1 + if err := json.NewDecoder(gr).Decode(&events); err != nil { + t.t.Fatal(err) + } + if err := gr.Close(); err != nil { + t.t.Fatal(err) + } + if t.path != "" && r.URL.Path != t.path { + t.t.Fatalf("expecting the request path %s to equal the configured path: %s", r.URL.Path, t.path) + } + + t.t.Logf("decision log test server received %d events at path %s", len(events), r.URL.Path) + t.ch <- events + w.WriteHeader(t.expCode) +} + +func (t *testServer) start() { + t.server = httptest.NewServer(http.HandlerFunc(t.handle)) +} + +// stop the testServer. This should only be done at the end of a test! +func (t *testServer) stop() { + // Drain any pending events to ensure the server can stop + for len(t.ch) > 0 { + <-t.ch + } + t.server.Close() +} + +func getValueForMethod(idx int) string { + methods := []string{"GET", "POST", "PUT", "DELETE", "PATCH"} + return methods[idx%len(methods)] +} + +func getValueForPath(idx int) string { + paths := []string{"/blah1", "/blah2", "/blah3", "/blah4"} + return paths[idx%len(paths)] +} + +func getValueForUser(idx int) string { + users := []string{"Alice", "Bob", "Charlie", "David", "Ed"} + return users[idx%len(users)] +} + +func generateInputMap(idx int) map[string]any { + var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + result := make(map[string]any) + + for range 20 { + n := idx % len(letters) + key := string(letters[n]) + result[key] = strconv.Itoa(idx) + } + return result + +} + +func getWellKnownMetrics() metrics.Metrics { + m := metrics.New() + m.Counter("test_counter").Incr() + return m +} + +func ensurePluginState(t *testing.T, p *Plugin, state plugins.State) { + t.Helper() + status, ok := p.manager.PluginStatus()[Name] + if !ok { + t.Fatalf("Expected to find state for %s, found nil", Name) + return + } + if status.State != state { + t.Fatalf("Unexpected status state found in plugin manager for %s:\n\n\tFound:%+v\n\n\tExpected: %s", Name, status.State, plugins.StateOK) + } +} + +func testStatus() *bundle.Status { + tDownload, _ := time.Parse(time.RFC3339Nano, "2018-01-01T00:00:00.0000000Z") + tActivate, _ := time.Parse(time.RFC3339Nano, "2018-01-01T00:00:01.0000000Z") + + return &bundle.Status{ + Name: "example/authz", + ActiveRevision: "quickbrawnfaux", + LastSuccessfulDownload: tDownload, + LastSuccessfulActivation: tActivate, + } +} + +func TestConfigUploadLimit(t *testing.T) { + tests := []struct { + name string + limit int64 + expectedLimit int64 + expectedLog string + expectedErr string + }{ + { + name: "exceed maximum limit", + limit: int64(8589934592), + expectedLimit: maxUploadSizeLimitBytes, + expectedLog: "the configured `upload_size_limit_bytes` (8589934592) has been set to the maximum limit (4294967296)", + }, + { + name: "nothing changes", + limit: 1000, + expectedLimit: 1000, + }, + { + name: "negative limit", + limit: -1, + expectedLimit: minUploadSizeLimitBytes, + expectedLog: "the configured `upload_size_limit_bytes` (-1) has been set to the minimum limit (90)", + }, + { + name: "equal to minimum", + limit: minUploadSizeLimitBytes, + expectedLimit: minUploadSizeLimitBytes, + }, + { + name: "equal to maximum", + limit: maxUploadSizeLimitBytes, + expectedLimit: maxUploadSizeLimitBytes, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + + testLogger := test.New() + + cfg := &Config{ + Service: "svc", + Reporting: ReportingConfig{ + UploadSizeLimitBytes: &tc.limit, + }, + } + trigger := plugins.DefaultTriggerMode + if err := cfg.validateAndInjectDefaults([]string{"svc"}, nil, &trigger, testLogger); err != nil { + if tc.expectedErr != "" { + if tc.expectedErr != err.Error() { + t.Fatalf("Expected error to be `%s` but got `%s`", tc.expectedErr, err.Error()) + } else { + return + } + } else { + t.Fatal(err) + } + } + + if *cfg.Reporting.UploadSizeLimitBytes != tc.expectedLimit { + t.Fatalf("Expected upload limit to be %d but got %d", tc.expectedLimit, cfg.Reporting.UploadSizeLimitBytes) + } + + if tc.expectedLog != "" { + e := testLogger.Entries() + if e[0].Message != tc.expectedLog { + t.Fatalf("Expected log to be %s but got %s", tc.expectedLog, e[0].Message) + } + } else { + if len(testLogger.Entries()) != 0 { + t.Fatalf("Expected log to be empty but got %s", testLogger.Entries()[0].Message) + } + } + }) + } +} + +func TestAdaptiveSoftLimitBetweenUpload(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + bufferType string + initialSoftLimit int64 + newSoftLimit int64 + }{ + { + name: "using event buffer", + bufferType: eventBufferType, + initialSoftLimit: 300, + newSoftLimit: 600, + }, + { + name: "using size buffer", + bufferType: sizeBufferType, + initialSoftLimit: 300, + newSoftLimit: 600, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + fixture := newTestFixture(t, testFixtureOptions{ + ReportingBufferType: tc.bufferType, + ReportingUploadSizeLimitBytes: tc.initialSoftLimit, + }) + defer fixture.server.stop() + defer fixture.plugin.Stop(ctx) + + s := currentSoftLimit(t, fixture.plugin, tc.bufferType) + if s != tc.initialSoftLimit { + t.Fatalf("expected %d, got %d", tc.initialSoftLimit, s) + } + + fixture.server.server.Config.SetKeepAlivesEnabled(false) + + fixture.server.ch = make(chan []EventV1, 4) + tr := plugins.TriggerManual + fixture.plugin.config.Reporting.Trigger = &tr + + if err := fixture.plugin.Start(ctx); err != nil { + t.Fatal(err) + } + + testMetrics := getWellKnownMetrics() + msAsFloat64 := map[string]any{} + for k, v := range testMetrics.All() { + msAsFloat64[k] = float64(v.(uint64)) + } + + var input any = map[string]any{"method": "GET"} + var result any = false + + ts, err := time.Parse(time.RFC3339Nano, "2018-01-01T12:00:00.123456Z") + if err != nil { + panic(err) + } + + event := &server.Info{ + Revision: strconv.Itoa(1), + DecisionID: strconv.Itoa(1), + Path: "tda/bar", + Input: &input, + Results: &result, + RemoteAddr: "test", + Timestamp: ts, + Metrics: testMetrics, + } + + if err := fixture.plugin.Log(ctx, event); err != nil { + t.Fatal(err) + } + + if err := fixture.plugin.Log(ctx, event); err != nil { + t.Fatal(err) + } + + // this will increase the soft limit + if err := fixture.plugin.oneShot(ctx); err != nil { + t.Fatal(err) + } + + s = currentSoftLimit(t, fixture.plugin, tc.bufferType) + if s != tc.newSoftLimit { + t.Fatalf("expected %d, got %d", tc.newSoftLimit, s) + } + + if err := fixture.plugin.Log(ctx, event); err != nil { + t.Fatal(err) + } + + if err := fixture.plugin.Log(ctx, event); err != nil { + t.Fatal(err) + } + + // the soft limit will stay the same and not be reset to the initial soft limit + if err := fixture.plugin.oneShot(ctx); err != nil { + t.Fatal(err) + } + + s = currentSoftLimit(t, fixture.plugin, tc.bufferType) + if s != tc.newSoftLimit { + t.Fatalf("expected %d, got %d", tc.newSoftLimit, s) + } + }) + } +} + +func currentSoftLimit(t *testing.T, plugin *Plugin, bufferType string) int64 { + t.Helper() + + switch bufferType { + case eventBufferType: + return plugin.eventBuffer.enc.uncompressedLimit + case sizeBufferType: + return plugin.enc.uncompressedLimit + default: + t.Fatal("Unknown buffer type") + } + + return 0 +} diff --git a/third_party/opa/v1/plugins/logs/status/status.go b/third_party/opa/v1/plugins/logs/status/status.go new file mode 100644 index 000000000000..75b21f2ae522 --- /dev/null +++ b/third_party/opa/v1/plugins/logs/status/status.go @@ -0,0 +1,63 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package status + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "reflect" + "strconv" + + "github.com/open-policy-agent/opa/v1/metrics" +) + +const ( + errCode = "decision_log_error" +) + +// Status represents the status of processing a decision log. +type Status struct { + Code string `json:"code,omitempty"` + Message string `json:"message,omitempty"` + HTTPCode json.Number `json:"http_code,omitempty"` + Metrics metrics.Metrics `json:"metrics,omitempty"` +} + +// SetError updates the status object to reflect a failure to upload or +// process a log. If err is nil, the error status is cleared. +func (s *Status) SetError(err error) { + var httpError HTTPError + + switch { + case err == nil: + s.Code = "" + s.HTTPCode = "" + s.Message = "" + + case errors.As(err, &httpError): + s.Code = errCode + s.HTTPCode = json.Number(strconv.Itoa(httpError.StatusCode)) + s.Message = err.Error() + + default: + s.Code = errCode + s.HTTPCode = "" + s.Message = err.Error() + } +} + +func (s *Status) Equal(other *Status) bool { + return reflect.DeepEqual(s, other) +} + +type HTTPError struct { + StatusCode int +} + +func (e HTTPError) Error() string { + return fmt.Sprintf("log upload failed, server replied with HTTP %v %v", e.StatusCode, http.StatusText(e.StatusCode)) +} diff --git a/third_party/opa/v1/plugins/plugins.go b/third_party/opa/v1/plugins/plugins.go new file mode 100644 index 000000000000..de914fb3a210 --- /dev/null +++ b/third_party/opa/v1/plugins/plugins.go @@ -0,0 +1,1175 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package plugins implements plugin management for the policy engine. +package plugins + +import ( + "context" + "errors" + "fmt" + "maps" + mr "math/rand" + "net/http" + "sync" + "time" + + "github.com/open-policy-agent/opa/internal/report" + "github.com/prometheus/client_golang/prometheus" + "go.opentelemetry.io/otel/sdk/trace" + + bundleUtils "github.com/open-policy-agent/opa/internal/bundle" + cfg "github.com/open-policy-agent/opa/internal/config" + initload "github.com/open-policy-agent/opa/internal/runtime/init" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/config" + "github.com/open-policy-agent/opa/v1/hooks" + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/plugins/rest" + "github.com/open-policy-agent/opa/v1/resolver/wasm" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/print" + "github.com/open-policy-agent/opa/v1/tracing" +) + +// Factory defines the interface OPA uses to instantiate your plugin. +// +// When OPA processes it's configuration it looks for factories that +// have been registered by calling runtime.RegisterPlugin. Factories +// are registered to a name which is used to key into the +// configuration blob. If your plugin has not been configured, your +// factory will not be invoked. +// +// plugins: +// my_plugin1: +// some_key: foo +// # my_plugin2: +// # some_key2: bar +// +// If OPA was started with the configuration above and received two +// calls to runtime.RegisterPlugins (one with NAME "my_plugin1" and +// one with NAME "my_plugin2"), it would only invoke the factory for +// for my_plugin1. +// +// OPA instantiates and reconfigures plugins in two steps. First, OPA +// will call Validate to check the configuration. Assuming the +// configuration is valid, your factory should return a configuration +// value that can be used to construct your plugin. Second, OPA will +// call New to instantiate your plugin providing the configuration +// value returned from the Validate call. +// +// Validate receives a slice of bytes representing plugin +// configuration and returns a configuration value that can be used to +// instantiate your plugin. The manager is provided to give access to +// the OPA's compiler, storage layer, and global configuration. Your +// Validate function will typically: +// +// 1. Deserialize the raw config bytes +// 2. Validate the deserialized config for semantic errors +// 3. Inject default values +// 4. Return a deserialized/parsed config +// +// New receives a valid configuration for your plugin and returns a +// plugin object. Your New function will typically: +// +// 1. Cast the config value to it's own type +// 2. Instantiate a plugin object +// 3. Return the plugin object +// 4. Update status via `plugins.Manager#UpdatePluginStatus` +// +// After a plugin has been created subsequent status updates can be +// send anytime the plugin enters a ready or error state. +type Factory interface { + Validate(manager *Manager, config []byte) (any, error) + New(manager *Manager, config any) Plugin +} + +// Plugin defines the interface OPA uses to manage your plugin. +// +// When OPA starts it will start all of the plugins it was configured +// to instantiate. Each time a new plugin is configured (via +// discovery), OPA will start it. You can use the Start call to spawn +// additional goroutines or perform initialization tasks. +// +// Currently OPA will not call Stop on plugins. +// +// When OPA receives new configuration for your plugin via discovery +// it will first Validate the configuration using your factory and +// then call Reconfigure. +type Plugin interface { + Start(ctx context.Context) error + Stop(ctx context.Context) + Reconfigure(ctx context.Context, config any) +} + +// Triggerable defines the interface plugins use for manual plugin triggers. +type Triggerable interface { + Trigger(context.Context) error +} + +// State defines the state that a Plugin instance is currently +// in with pre-defined states. +type State string + +const ( + // StateNotReady indicates that the Plugin is not in an error state, but isn't + // ready for normal operation yet. This should only happen at + // initialization time. + StateNotReady State = "NOT_READY" + + // StateOK signifies that the Plugin is operating normally. + StateOK State = "OK" + + // StateErr indicates that the Plugin is in an error state and should not + // be considered as functional. + StateErr State = "ERROR" + + // StateWarn indicates the Plugin is operating, but in a potentially dangerous or + // degraded state. It may be used to indicate manual remediation is needed, or to + // alert admins of some other noteworthy state. + StateWarn State = "WARN" +) + +// TriggerMode defines the trigger mode utilized by a Plugin for bundle download, +// log upload etc. +type TriggerMode string + +const ( + // TriggerPeriodic represents periodic polling mechanism + TriggerPeriodic TriggerMode = "periodic" + + // TriggerManual represents manual triggering mechanism + TriggerManual TriggerMode = "manual" + + // DefaultTriggerMode represents default trigger mechanism + DefaultTriggerMode TriggerMode = "periodic" +) + +// default interval between OPA report uploads +var defaultUploadIntervalSec = int64(3600) + +// Status has a Plugin's current status plus an optional Message. +type Status struct { + State State `json:"state"` + Message string `json:"message,omitempty"` +} + +func (s *Status) String() string { + return fmt.Sprintf("{%v %q}", s.State, s.Message) +} + +func (s *Status) Equal(other *Status) bool { + if s == nil || other == nil { + return s == nil && other == nil + } + + return s.State == other.State && s.Message == other.Message +} + +// StatusListener defines a handler to register for status updates. +type StatusListener func(status map[string]*Status) + +// Manager implements lifecycle management of plugins and gives plugins access +// to engine-wide components like storage. +type Manager struct { + Store storage.Store + Config *config.Config + Info *ast.Term + ID string + + compiler *ast.Compiler + compilerMux sync.RWMutex + wasmResolvers []*wasm.Resolver + wasmResolversMtx sync.RWMutex + services map[string]rest.Client + keys map[string]*keys.Config + plugins []namedplugin + registeredTriggers []func(storage.Transaction) + mtx sync.Mutex + pluginStatus map[string]*Status + pluginStatusListeners map[string]StatusListener + initBundles map[string]*bundle.Bundle + initFiles loader.Result + maxErrors int + initialized bool + interQueryBuiltinCacheConfig *cache.Config + gracefulShutdownPeriod int + registeredCacheTriggers []func(*cache.Config) + logger logging.Logger + consoleLogger logging.Logger + serverInitialized chan struct{} + serverInitializedOnce sync.Once + printHook print.Hook + enablePrintStatements bool + router *http.ServeMux + prometheusRegister prometheus.Registerer + tracerProvider *trace.TracerProvider + distributedTacingOpts tracing.Options + registeredNDCacheTriggers []func(bool) + registeredTelemetryGatherers map[string]report.Gatherer + bootstrapConfigLabels map[string]string + hooks hooks.Hooks + enableTelemetry bool + reporter report.Reporter + opaReportNotifyCh chan struct{} + stop chan chan struct{} + parserOptions ast.ParserOptions + extraRoutes map[string]ExtraRoute + extraMiddlewares []func(http.Handler) http.Handler + extraAuthorizerRoutes []func(string, []any) bool + bundleActivatorPlugin string +} + +type managerContextKey string +type managerWasmResolverKey string + +const managerCompilerContextKey = managerContextKey("compiler") +const managerWasmResolverContextKey = managerWasmResolverKey("wasmResolvers") + +// SetCompilerOnContext puts the compiler into the storage context. Calling this +// function before committing updated policies to storage allows the manager to +// skip parsing and compiling of modules. Instead, the manager will use the +// compiler that was stored on the context. +func SetCompilerOnContext(context *storage.Context, compiler *ast.Compiler) { + context.Put(managerCompilerContextKey, compiler) +} + +// GetCompilerOnContext gets the compiler cached on the storage context. +func GetCompilerOnContext(context *storage.Context) *ast.Compiler { + compiler, ok := context.Get(managerCompilerContextKey).(*ast.Compiler) + if !ok { + return nil + } + return compiler +} + +// SetWasmResolversOnContext puts a set of Wasm Resolvers into the storage +// context. Calling this function before committing updated wasm modules to +// storage allows the manager to skip initializing modules before using them. +// Instead, the manager will use the compiler that was stored on the context. +func SetWasmResolversOnContext(context *storage.Context, rs []*wasm.Resolver) { + context.Put(managerWasmResolverContextKey, rs) +} + +// getWasmResolversOnContext gets the resolvers cached on the storage context. +func getWasmResolversOnContext(context *storage.Context) []*wasm.Resolver { + resolvers, ok := context.Get(managerWasmResolverContextKey).([]*wasm.Resolver) + if !ok { + return nil + } + return resolvers +} + +func validateTriggerMode(mode TriggerMode) error { + switch mode { + case TriggerPeriodic, TriggerManual: + return nil + default: + return fmt.Errorf("invalid trigger mode %q (want %q or %q)", mode, TriggerPeriodic, TriggerManual) + } +} + +// ValidateAndInjectDefaultsForTriggerMode validates the trigger mode and injects default values +func ValidateAndInjectDefaultsForTriggerMode(a, b *TriggerMode) (*TriggerMode, error) { + + if a == nil && b != nil { + err := validateTriggerMode(*b) + if err != nil { + return nil, err + } + return b, nil + } else if a != nil && b == nil { + err := validateTriggerMode(*a) + if err != nil { + return nil, err + } + return a, nil + } else if a != nil && b != nil { + if *a != *b { + return nil, fmt.Errorf("trigger mode mismatch: %s and %s (hint: check discovery configuration)", *a, *b) + } + err := validateTriggerMode(*a) + if err != nil { + return nil, err + } + return a, nil + } + + t := DefaultTriggerMode + return &t, nil +} + +type namedplugin struct { + name string + plugin Plugin +} + +// Info sets the runtime information on the manager. The runtime information is +// propagated to opa.runtime() built-in function calls. +func Info(term *ast.Term) func(*Manager) { + return func(m *Manager) { + m.Info = term + } +} + +// InitBundles provides the initial set of bundles to load. +func InitBundles(b map[string]*bundle.Bundle) func(*Manager) { + return func(m *Manager) { + m.initBundles = b + } +} + +// InitFiles provides the initial set of other data/policy files to load. +func InitFiles(f loader.Result) func(*Manager) { + return func(m *Manager) { + m.initFiles = f + } +} + +// MaxErrors sets the error limit for the manager's shared compiler. +func MaxErrors(n int) func(*Manager) { + return func(m *Manager) { + m.maxErrors = n + } +} + +// GracefulShutdownPeriod passes the configured graceful shutdown period to plugins +func GracefulShutdownPeriod(gracefulShutdownPeriod int) func(*Manager) { + return func(m *Manager) { + m.gracefulShutdownPeriod = gracefulShutdownPeriod + } +} + +// Logger configures the passed logger on the plugin manager (useful to +// configure default fields) +func Logger(logger logging.Logger) func(*Manager) { + return func(m *Manager) { + m.logger = logger + } +} + +// ConsoleLogger sets the passed logger to be used by plugins that are +// configured with console logging enabled. +func ConsoleLogger(logger logging.Logger) func(*Manager) { + return func(m *Manager) { + m.consoleLogger = logger + } +} + +func EnablePrintStatements(yes bool) func(*Manager) { + return func(m *Manager) { + m.enablePrintStatements = yes + } +} + +func PrintHook(h print.Hook) func(*Manager) { + return func(m *Manager) { + m.printHook = h + } +} + +func WithRouter(r *http.ServeMux) func(*Manager) { + return func(m *Manager) { + m.router = r + } +} + +// WithPrometheusRegister sets the passed prometheus.Registerer to be used by plugins +func WithPrometheusRegister(prometheusRegister prometheus.Registerer) func(*Manager) { + return func(m *Manager) { + m.prometheusRegister = prometheusRegister + } +} + +// WithTracerProvider sets the passed *trace.TracerProvider to be used by plugins +func WithTracerProvider(tracerProvider *trace.TracerProvider) func(*Manager) { + return func(m *Manager) { + m.tracerProvider = tracerProvider + } +} + +// WithDistributedTracingOpts sets the options to be used by distributed tracing. +func WithDistributedTracingOpts(tr tracing.Options) func(*Manager) { + return func(m *Manager) { + m.distributedTacingOpts = tr + } +} + +// WithHooks allows passing hooks to the plugin manager. +func WithHooks(hs hooks.Hooks) func(*Manager) { + return func(m *Manager) { + m.hooks = hs + } +} + +// WithParserOptions sets the parser options to be used by the plugin manager. +func WithParserOptions(opts ast.ParserOptions) func(*Manager) { + return func(m *Manager) { + m.parserOptions = opts + } +} + +// WithEnableTelemetry controls whether OPA will send telemetry reports to an external service. +func WithEnableTelemetry(enableTelemetry bool) func(*Manager) { + return func(m *Manager) { + m.enableTelemetry = enableTelemetry + } +} + +// WithTelemetryGatherers allows registration of telemetry gatherers which enable injection of additional data in the +// telemetry report +func WithTelemetryGatherers(gs map[string]report.Gatherer) func(*Manager) { + return func(m *Manager) { + m.registeredTelemetryGatherers = gs + } +} + +// WithBundleActivatorPlugin sets the name of the activator plugin to load bundles into the store +func WithBundleActivatorPlugin(bundleActivatorPlugin string) func(*Manager) { + return func(m *Manager) { + m.bundleActivatorPlugin = bundleActivatorPlugin + } +} + +// New creates a new Manager using config. +func New(raw []byte, id string, store storage.Store, opts ...func(*Manager)) (*Manager, error) { + + parsedConfig, err := config.ParseConfig(raw, id) + if err != nil { + return nil, err + } + + m := &Manager{ + Store: store, + Config: parsedConfig, + ID: id, + pluginStatus: map[string]*Status{}, + pluginStatusListeners: map[string]StatusListener{}, + maxErrors: -1, + serverInitialized: make(chan struct{}), + bootstrapConfigLabels: parsedConfig.Labels, + extraRoutes: map[string]ExtraRoute{}, + } + + for _, f := range opts { + f(m) + } + + if m.parserOptions.RegoVersion == ast.RegoUndefined { + // Default to v1 if rego-version is not set through options + m.parserOptions.RegoVersion = ast.DefaultRegoVersion + } + + if m.logger == nil { + m.logger = logging.Get() + } + + if m.consoleLogger == nil { + m.consoleLogger = logging.New() + } + + m.hooks.Each(func(h hooks.Hook) { + if f, ok := h.(hooks.ConfigHook); ok { + if c, e := f.OnConfig(context.Background(), parsedConfig); e != nil { + err = errors.Join(err, e) + } else { + parsedConfig = c + } + } + }) + if err != nil { + return nil, err + } + + // do after options and overrides + m.keys, err = keys.ParseKeysConfig(parsedConfig.Keys) + if err != nil { + return nil, err + } + + m.interQueryBuiltinCacheConfig, err = cache.ParseCachingConfig(parsedConfig.Caching) + if err != nil { + return nil, err + } + + serviceOpts := m.DefaultServiceOpts(parsedConfig) + + m.services, err = cfg.ParseServicesConfig(serviceOpts) + if err != nil { + return nil, err + } + + if m.enableTelemetry { + reporter, err := report.New(report.Options{Logger: m.logger}) + if err != nil { + return nil, err + } + m.reporter = reporter + + m.reporter.RegisterGatherer("min_compatible_version", func(_ context.Context) (any, error) { + var minimumCompatibleVersion string + if c := m.GetCompiler(); c != nil && c.Required != nil { + minimumCompatibleVersion, _ = c.Required.MinimumCompatibleVersion() + } + return minimumCompatibleVersion, nil + }) + + // register any additional gatherers + for k, g := range m.registeredTelemetryGatherers { + m.reporter.RegisterGatherer(k, g) + } + } + + return m, nil +} + +// Init returns an error if the manager could not initialize itself. Init() should +// be called before Start(). Init() is idempotent. +func (m *Manager) Init(ctx context.Context) error { + + if m.initialized { + return nil + } + + params := storage.TransactionParams{ + Write: true, + Context: storage.NewContext(), + } + + if m.enableTelemetry { + m.opaReportNotifyCh = make(chan struct{}) + m.stop = make(chan chan struct{}) + go m.sendOPAUpdateLoop(ctx) + } + + err := storage.Txn(ctx, m.Store, params, func(txn storage.Transaction) error { + + result, err := initload.InsertAndCompile(ctx, initload.InsertAndCompileOptions{ + Store: m.Store, + Txn: txn, + Files: m.initFiles, + Bundles: m.initBundles, + MaxErrors: m.maxErrors, + EnablePrintStatements: m.enablePrintStatements, + ParserOptions: m.parserOptions, + BundleActivatorPlugin: m.bundleActivatorPlugin, + }) + + if err != nil { + return err + } + + SetCompilerOnContext(params.Context, result.Compiler) + + resolvers, err := bundleUtils.LoadWasmResolversFromStore(ctx, m.Store, txn, nil) + if err != nil { + return err + } + SetWasmResolversOnContext(params.Context, resolvers) + + _, err = m.Store.Register(ctx, txn, storage.TriggerConfig{OnCommit: m.onCommit}) + return err + }) + + if err != nil { + if m.stop != nil { + done := make(chan struct{}) + m.stop <- done + <-done + } + + return err + } + + m.initialized = true + return nil +} + +// Labels returns the set of labels from the configuration. +func (m *Manager) Labels() map[string]string { + m.mtx.Lock() + defer m.mtx.Unlock() + return m.Config.Labels +} + +// InterQueryBuiltinCacheConfig returns the configuration for the inter-query caches. +func (m *Manager) InterQueryBuiltinCacheConfig() *cache.Config { + m.mtx.Lock() + defer m.mtx.Unlock() + return m.interQueryBuiltinCacheConfig +} + +// Register adds a plugin to the manager. When the manager is started, all of +// the plugins will be started. +func (m *Manager) Register(name string, plugin Plugin) { + m.mtx.Lock() + defer m.mtx.Unlock() + m.plugins = append(m.plugins, namedplugin{ + name: name, + plugin: plugin, + }) + if _, ok := m.pluginStatus[name]; !ok { + m.pluginStatus[name] = &Status{State: StateNotReady} + } +} + +// Plugins returns the list of plugins registered with the manager. +func (m *Manager) Plugins() []string { + m.mtx.Lock() + defer m.mtx.Unlock() + result := make([]string, len(m.plugins)) + for i := range m.plugins { + result[i] = m.plugins[i].name + } + return result +} + +// Plugin returns the plugin registered with name or nil if name is not found. +func (m *Manager) Plugin(name string) Plugin { + m.mtx.Lock() + defer m.mtx.Unlock() + for i := range m.plugins { + if m.plugins[i].name == name { + return m.plugins[i].plugin + } + } + return nil +} + +// AuthPlugin returns the HTTPAuthPlugin registered with name or nil if name is not found. +func (m *Manager) AuthPlugin(name string) rest.HTTPAuthPlugin { + m.mtx.Lock() + defer m.mtx.Unlock() + for i := range m.plugins { + if m.plugins[i].name == name { + return m.plugins[i].plugin.(rest.HTTPAuthPlugin) + } + } + return nil +} + +// GetCompiler returns the manager's compiler. +func (m *Manager) GetCompiler() *ast.Compiler { + m.compilerMux.RLock() + defer m.compilerMux.RUnlock() + return m.compiler +} + +func (m *Manager) setCompiler(compiler *ast.Compiler) { + m.compilerMux.Lock() + defer m.compilerMux.Unlock() + m.compiler = compiler +} + +type ExtraRoute struct { + PromName string // name is for prometheus metrics + HandlerFunc http.HandlerFunc +} + +func (m *Manager) ExtraRoutes() map[string]ExtraRoute { + return m.extraRoutes +} + +func (m *Manager) ExtraMiddlewares() []func(http.Handler) http.Handler { + return m.extraMiddlewares +} + +func (m *Manager) ExtraAuthorizerRoutes() []func(string, []any) bool { + return m.extraAuthorizerRoutes +} + +// ExtraRoute registers an extra route to be served by the HTTP +// server later. Using this instead of directly registering routes +// with GetRouter() lets the server apply its handler wrapping for +// Prometheus and OpenTelemetry. +// Caution: This cannot be used to dynamically register and un- +// register HTTP handlers. It's meant as a late-stage set up helper, +// to be called from a plugin's init methods. +func (m *Manager) ExtraRoute(path, name string, hf http.HandlerFunc) { + if _, ok := m.extraRoutes[path]; ok { + panic("extra route already registered: " + path) + } + m.extraRoutes[path] = ExtraRoute{ + PromName: name, + HandlerFunc: hf, + } +} + +// ExtraMiddleware registers extra middlewares (`func(http.Handler) http.Handler`) +// to be injected into the HTTP handler chain in the server later. +// Caution: This cannot be used to dynamically register and un- +// register middlewares. It's meant as a late-stage set up helper, +// to be called from a plugin's init methods. +func (m *Manager) ExtraMiddleware(mw ...func(http.Handler) http.Handler) { + m.extraMiddlewares = append(m.extraMiddlewares, mw...) +} + +// ExtraAuthorizerRoute registers an extra URL path validator function for use +// in the server authorizer. These functions designate specific methods and URL +// prefixes or paths where the authorizer should allow request body parsing. +// Caution: This cannot be used to dynamically register and un- +// register path validator functions. It's meant as a late-stage +// set up helper, to be called from a plugin's init methods. +func (m *Manager) ExtraAuthorizerRoute(validatorFunc func(string, []any) bool) { + m.extraAuthorizerRoutes = append(m.extraAuthorizerRoutes, validatorFunc) +} + +// GetRouter returns the managers router if set +func (m *Manager) GetRouter() *http.ServeMux { + m.mtx.Lock() + defer m.mtx.Unlock() + return m.router +} + +// RegisterCompilerTrigger registers for change notifications when the compiler +// is changed. +func (m *Manager) RegisterCompilerTrigger(f func(storage.Transaction)) { + m.mtx.Lock() + defer m.mtx.Unlock() + m.registeredTriggers = append(m.registeredTriggers, f) +} + +// GetWasmResolvers returns the manager's set of Wasm Resolvers. +func (m *Manager) GetWasmResolvers() []*wasm.Resolver { + m.wasmResolversMtx.RLock() + defer m.wasmResolversMtx.RUnlock() + return m.wasmResolvers +} + +func (m *Manager) setWasmResolvers(rs []*wasm.Resolver) { + m.wasmResolversMtx.Lock() + defer m.wasmResolversMtx.Unlock() + m.wasmResolvers = rs +} + +// Start starts the manager. Init() should be called once before Start(). +func (m *Manager) Start(ctx context.Context) error { + + if m == nil { + return nil + } + + if !m.initialized { + if err := m.Init(ctx); err != nil { + return err + } + } + + var toStart []Plugin + + func() { + m.mtx.Lock() + defer m.mtx.Unlock() + toStart = make([]Plugin, len(m.plugins)) + for i := range m.plugins { + toStart[i] = m.plugins[i].plugin + } + }() + + for i := range toStart { + if err := toStart[i].Start(ctx); err != nil { + return err + } + } + + return nil +} + +// Stop stops the manager, stopping all the plugins registered with it. +// Any plugin that needs to perform cleanup should do so within the duration +// of the graceful shutdown period passed with the context as a timeout. +// Note that a graceful shutdown period configured with the Manager instance +// will override the timeout of the passed in context (if applicable). +func (m *Manager) Stop(ctx context.Context) { + var toStop []Plugin + + func() { + m.mtx.Lock() + defer m.mtx.Unlock() + toStop = make([]Plugin, len(m.plugins)) + for i := range m.plugins { + toStop[i] = m.plugins[i].plugin + } + }() + + var cancel context.CancelFunc + if m.gracefulShutdownPeriod > 0 { + ctx, cancel = context.WithTimeout(ctx, time.Duration(m.gracefulShutdownPeriod)*time.Second) + } else { + ctx, cancel = context.WithCancel(ctx) + } + defer cancel() + for i := range toStop { + toStop[i].Stop(ctx) + } + if c, ok := m.Store.(interface{ Close(context.Context) error }); ok { + if err := c.Close(ctx); err != nil { + m.logger.Error("Error closing store: %v", err) + } + } + + if m.stop != nil { + done := make(chan struct{}) + m.stop <- done + <-done + } +} + +func (m *Manager) DefaultServiceOpts(config *config.Config) cfg.ServiceOptions { + return cfg.ServiceOptions{ + Raw: config.Services, + AuthPlugin: m.AuthPlugin, + Logger: m.logger, + Keys: m.keys, + DistributedTacingOpts: m.distributedTacingOpts, + } +} + +// Reconfigure updates the configuration on the manager. +func (m *Manager) Reconfigure(config *config.Config) error { + opts := m.DefaultServiceOpts(config) + + keys, err := keys.ParseKeysConfig(config.Keys) + if err != nil { + return err + } + opts.Keys = keys + + services, err := cfg.ParseServicesConfig(opts) + if err != nil { + return err + } + + interQueryBuiltinCacheConfig, err := cache.ParseCachingConfig(config.Caching) + if err != nil { + return err + } + + m.mtx.Lock() + defer m.mtx.Unlock() + + // don't overwrite existing labels, only allow additions - always based on the boostrap config + if config.Labels == nil { + config.Labels = m.bootstrapConfigLabels + } else { + maps.Copy(config.Labels, m.bootstrapConfigLabels) + } + + // don't erase persistence directory + if config.PersistenceDirectory == nil { + config.PersistenceDirectory = m.Config.PersistenceDirectory + } + + m.Config = config + m.interQueryBuiltinCacheConfig = interQueryBuiltinCacheConfig + + maps.Copy(m.services, services) + maps.Copy(m.keys, keys) + + for _, trigger := range m.registeredCacheTriggers { + trigger(interQueryBuiltinCacheConfig) + } + + for _, trigger := range m.registeredNDCacheTriggers { + trigger(config.NDBuiltinCache) + } + + return nil +} + +// PluginStatus returns the current statuses of any plugins registered. +func (m *Manager) PluginStatus() map[string]*Status { + m.mtx.Lock() + defer m.mtx.Unlock() + + return m.copyPluginStatus() +} + +// RegisterPluginStatusListener registers a StatusListener to be +// called when plugin status updates occur. +func (m *Manager) RegisterPluginStatusListener(name string, listener StatusListener) { + m.mtx.Lock() + defer m.mtx.Unlock() + + m.pluginStatusListeners[name] = listener +} + +// UnregisterPluginStatusListener removes a StatusListener registered with the +// same name. +func (m *Manager) UnregisterPluginStatusListener(name string) { + m.mtx.Lock() + defer m.mtx.Unlock() + + delete(m.pluginStatusListeners, name) +} + +// UpdatePluginStatus updates a named plugins status. Any registered +// listeners will be called with a copy of the new state of all +// plugins. +func (m *Manager) UpdatePluginStatus(pluginName string, status *Status) { + + var toNotify map[string]StatusListener + var statuses map[string]*Status + + func() { + m.mtx.Lock() + defer m.mtx.Unlock() + m.pluginStatus[pluginName] = status + toNotify = make(map[string]StatusListener, len(m.pluginStatusListeners)) + maps.Copy(toNotify, m.pluginStatusListeners) + statuses = m.copyPluginStatus() + }() + + for _, l := range toNotify { + l(statuses) + } +} + +func (m *Manager) copyPluginStatus() map[string]*Status { + statusCpy := map[string]*Status{} + for k, v := range m.pluginStatus { + var cpy *Status + if v != nil { + cpy = &Status{ + State: v.State, + Message: v.Message, + } + } + statusCpy[k] = cpy + } + return statusCpy +} + +func (m *Manager) onCommit(ctx context.Context, txn storage.Transaction, event storage.TriggerEvent) { + + compiler := GetCompilerOnContext(event.Context) + + // If the context does not contain the compiler fallback to loading the + // compiler from the store. Currently the bundle plugin sets the + // compiler on the context but the server does not (nor would users + // implementing their own policy loading.) + if compiler == nil && event.PolicyChanged() { + compiler, _ = loadCompilerFromStore(ctx, m.Store, txn, m.enablePrintStatements, m.ParserOptions()) + } + + if compiler != nil { + m.setCompiler(compiler) + + if m.enableTelemetry && event.PolicyChanged() { + m.opaReportNotifyCh <- struct{}{} + } + + for _, f := range m.registeredTriggers { + f(txn) + } + } + + // Similar to the compiler, look for a set of resolvers on the transaction + // context. If they are not set we may need to reload from the store. + resolvers := getWasmResolversOnContext(event.Context) + if resolvers != nil { + m.setWasmResolvers(resolvers) + + } else if event.DataChanged() { + if requiresWasmResolverReload(event) { + resolvers, err := bundleUtils.LoadWasmResolversFromStore(ctx, m.Store, txn, nil) + if err != nil { + panic(err) + } + m.setWasmResolvers(resolvers) + } else { + err := m.updateWasmResolversData(ctx, event) + if err != nil { + panic(err) + } + } + } +} + +func loadCompilerFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, enablePrintStatements bool, popts ast.ParserOptions) (*ast.Compiler, error) { + policies, err := store.ListPolicies(ctx, txn) + if err != nil { + return nil, err + } + modules := map[string]*ast.Module{} + + for _, policy := range policies { + bs, err := store.GetPolicy(ctx, txn, policy) + if err != nil { + return nil, err + } + module, err := ast.ParseModuleWithOpts(policy, string(bs), popts) + if err != nil { + return nil, err + } + modules[policy] = module + } + + compiler := ast.NewCompiler(). + WithEnablePrintStatements(enablePrintStatements) + + if popts.RegoVersion != ast.RegoUndefined { + compiler = compiler.WithDefaultRegoVersion(popts.RegoVersion) + } + + compiler.Compile(modules) + return compiler, nil +} + +func requiresWasmResolverReload(event storage.TriggerEvent) bool { + // If the data changes touched the bundle path (which includes + // the wasm modules) we will reload them. Otherwise update + // data for each module already on the manager. + for _, dataEvent := range event.Data { + if dataEvent.Path.HasPrefix(bundle.BundlesBasePath) { + return true + } + } + return false +} + +func (m *Manager) updateWasmResolversData(ctx context.Context, event storage.TriggerEvent) error { + m.wasmResolversMtx.Lock() + defer m.wasmResolversMtx.Unlock() + + for _, resolver := range m.wasmResolvers { + for _, dataEvent := range event.Data { + var err error + if dataEvent.Removed { + err = resolver.RemoveDataPath(ctx, dataEvent.Path) + } else { + err = resolver.SetDataPath(ctx, dataEvent.Path, dataEvent.Data) + } + if err != nil { + return fmt.Errorf("failed to update wasm runtime data: %s", err) + } + } + } + return nil +} + +// PublicKeys returns a public keys that can be used for verifying signed bundles. +func (m *Manager) PublicKeys() map[string]*keys.Config { + m.mtx.Lock() + defer m.mtx.Unlock() + return m.keys +} + +// Client returns a client for communicating with a remote service. +func (m *Manager) Client(name string) rest.Client { + m.mtx.Lock() + defer m.mtx.Unlock() + return m.services[name] +} + +// Services returns a list of services that m can provide clients for. +func (m *Manager) Services() []string { + m.mtx.Lock() + defer m.mtx.Unlock() + s := make([]string, 0, len(m.services)) + for name := range m.services { + s = append(s, name) + } + return s +} + +// Logger gets the standard logger for this plugin manager. +func (m *Manager) Logger() logging.Logger { + return m.logger +} + +// ConsoleLogger gets the console logger for this plugin manager. +func (m *Manager) ConsoleLogger() logging.Logger { + return m.consoleLogger +} + +func (m *Manager) PrintHook() print.Hook { + return m.printHook +} + +func (m *Manager) EnablePrintStatements() bool { + return m.enablePrintStatements +} + +// ServerInitialized signals a channel indicating that the OPA +// server has finished initialization. +func (m *Manager) ServerInitialized() { + m.serverInitializedOnce.Do(func() { close(m.serverInitialized) }) +} + +// ServerInitializedChannel returns a receive-only channel that +// is closed when the OPA server has finished initialization. +// Be aware that the socket of the server listener may not be +// open by the time this channel is closed. There is a very +// small window where the socket may still be closed, due to +// a race condition. +func (m *Manager) ServerInitializedChannel() <-chan struct{} { + return m.serverInitialized +} + +// RegisterCacheTrigger accepts a func that receives new inter-query cache config generated by +// a reconfigure of the plugin manager, so that it can be propagated to existing inter-query caches. +func (m *Manager) RegisterCacheTrigger(trigger func(*cache.Config)) { + m.mtx.Lock() + defer m.mtx.Unlock() + m.registeredCacheTriggers = append(m.registeredCacheTriggers, trigger) +} + +// PrometheusRegister gets the prometheus.Registerer for this plugin manager. +func (m *Manager) PrometheusRegister() prometheus.Registerer { + return m.prometheusRegister +} + +// TracerProvider gets the *trace.TracerProvider for this plugin manager. +func (m *Manager) TracerProvider() *trace.TracerProvider { + return m.tracerProvider +} + +func (m *Manager) RegisterNDCacheTrigger(trigger func(bool)) { + m.mtx.Lock() + defer m.mtx.Unlock() + m.registeredNDCacheTriggers = append(m.registeredNDCacheTriggers, trigger) +} + +func (m *Manager) sendOPAUpdateLoop(ctx context.Context) { + ticker := time.NewTicker(time.Duration(int64(time.Second) * defaultUploadIntervalSec)) + mr.New(mr.NewSource(time.Now().UnixNano())) + + ctx, cancel := context.WithCancel(ctx) + + var opaReportNotify bool + + for { + select { + case <-m.opaReportNotifyCh: + opaReportNotify = true + case <-ticker.C: + ticker.Stop() + + if opaReportNotify { + opaReportNotify = false + _, err := m.reporter.SendReport(ctx) + if err != nil { + m.logger.WithFields(map[string]any{"err": err}).Debug("Unable to send OPA telemetry report.") + } + } + + newInterval := mr.Int63n(defaultUploadIntervalSec) + defaultUploadIntervalSec + ticker = time.NewTicker(time.Duration(int64(time.Second) * newInterval)) + case done := <-m.stop: + cancel() + ticker.Stop() + done <- struct{}{} + return + } + } +} + +func (m *Manager) ParserOptions() ast.ParserOptions { + return m.parserOptions +} diff --git a/third_party/opa/v1/plugins/plugins_test.go b/third_party/opa/v1/plugins/plugins_test.go new file mode 100644 index 000000000000..1109b57edddf --- /dev/null +++ b/third_party/opa/v1/plugins/plugins_test.go @@ -0,0 +1,513 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package plugins + +import ( + "context" + "errors" + "net/http" + "reflect" + "testing" + + internal_tracing "github.com/open-policy-agent/opa/internal/distributedtracing" + "github.com/open-policy-agent/opa/internal/storage/mock" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/logging/test" + "github.com/open-policy-agent/opa/v1/plugins/rest" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/topdown/cache" + prom "github.com/prometheus/client_golang/prometheus" +) + +func TestManagerCacheTriggers(t *testing.T) { + m, err := New([]byte{}, "test", inmem.New()) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + l1Called := false + m.RegisterCacheTrigger(func(*cache.Config) { + l1Called = true + }) + + if m.registeredCacheTriggers[0] == nil { + t.Fatal("First listener failed to register") + } + + l2Called := false + m.RegisterCacheTrigger(func(*cache.Config) { + l2Called = true + }) + + if m.registeredCacheTriggers[0] == nil || m.registeredCacheTriggers[1] == nil { + t.Fatal("Second listener failed to register") + } + + if l1Called == true || l2Called == true { + t.Fatal("Listeners should not be called yet") + } + + err = m.Reconfigure(m.Config) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if l1Called == false || l2Called == false { + t.Fatal("Listeners should hav been called") + } +} + +func TestManagerNDCacheTriggers(t *testing.T) { + m, err := New([]byte{}, "test", inmem.New()) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + l1Called := false + m.RegisterNDCacheTrigger(func(bool) { + l1Called = true + }) + + if m.registeredNDCacheTriggers[0] == nil { + t.Fatal("First listener failed to register") + } + + l2Called := false + m.RegisterNDCacheTrigger(func(bool) { + l2Called = true + }) + + if m.registeredNDCacheTriggers[0] == nil || m.registeredNDCacheTriggers[1] == nil { + t.Fatal("Second listener failed to register") + } + + if l1Called == true || l2Called == true { + t.Fatal("Listeners should not be called yet") + } + + err = m.Reconfigure(m.Config) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if l1Called == false || l2Called == false { + t.Fatal("Listeners should hav been called") + } +} + +func TestManagerPluginStatusListener(t *testing.T) { + m, err := New([]byte{}, "test", inmem.New()) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + // Start by registering a single listener and validate that it was registered correctly + var l1Status map[string]*Status + m.RegisterPluginStatusListener("l1", func(status map[string]*Status) { + l1Status = status + }) + if len(m.pluginStatusListeners) != 1 || m.pluginStatusListeners["l1"] == nil { + t.Fatalf("Expected a single listener named 'l1' got: %+v", m.pluginStatusListeners) + } + + // Register a second one, validate both are there + var l2Status map[string]*Status + m.RegisterPluginStatusListener("l2", func(status map[string]*Status) { + l2Status = status + }) + if len(m.pluginStatusListeners) != 2 || m.pluginStatusListeners["l2"] == nil { + t.Fatalf("Expected a two listeners named 'l1' and 'l2' got: %+v", m.pluginStatusListeners) + } + + // Ensure starting statuses are empty by default + currentStatus := m.PluginStatus() + if len(currentStatus) != 0 { + t.Fatalf("Expected 0 statuses in current plugin status map, got: %+v", currentStatus) + } + + // Push an update to a plugin, ensure current status is reflected and listeners were called + const message = "foo" + m.UpdatePluginStatus("p1", &Status{State: StateOK, Message: message}) + currentStatus = m.PluginStatus() + if len(currentStatus) != 1 || currentStatus["p1"].State != StateOK || currentStatus["p1"].Message != message { + t.Fatalf("Expected 1 statuses in current plugin status map with state OK and message 'foo', got: %+v", currentStatus) + } + if !reflect.DeepEqual(currentStatus, l1Status) || !reflect.DeepEqual(l1Status, l2Status) { + t.Fatalf("Unexpected status in updates:\n\n\texpecting: %+v\n\n\tgot: l1: %+v l2: %+v\n", currentStatus, l1Status, l2Status) + } + + // Unregister the first listener, ensure it is removed + m.UnregisterPluginStatusListener("l1") + if len(m.pluginStatusListeners) != 1 || m.pluginStatusListeners["l2"] == nil { + t.Fatalf("Expected a single listeners named 'l2' got: %+v", m.pluginStatusListeners) + } + + // Send another update, ensure the status is ok and the remaining listener is still called + m.UpdatePluginStatus("p2", &Status{State: StateErr}) + currentStatus = m.PluginStatus() + if len(currentStatus) != 2 || currentStatus["p1"].State != StateOK || currentStatus["p1"].Message != message || currentStatus["p2"].State != StateErr { + t.Fatalf("Unexpected current plugin status, got: %+v", currentStatus) + } + if !reflect.DeepEqual(currentStatus, l2Status) { + t.Fatalf("Unexpected status in updates:\n\n\texpecting: %+v\n\n\tgot: %+v\n", currentStatus, l2Status) + } + + // Unregister the last listener + m.UnregisterPluginStatusListener("l2") + if len(m.pluginStatusListeners) != 0 { + t.Fatalf("Expected zero listeners got: %+v", m.pluginStatusListeners) + } + + // Ensure updates can still be sent with no listeners + m.UpdatePluginStatus("p2", &Status{State: StateOK}) + currentStatus = m.PluginStatus() + if len(currentStatus) != 2 || currentStatus["p1"].State != StateOK || currentStatus["p1"].Message != message || currentStatus["p2"].State != StateOK { + t.Fatalf("Unexpected current plugin status, got: %+v", currentStatus) + } +} + +func TestPluginStatusUpdateOnStartAndStop(t *testing.T) { + m, err := New([]byte{}, "test", inmem.New()) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + m.Register("p1", &testPlugin{m}) + + err = m.Start(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + m.Stop(context.Background()) +} + +type testPlugin struct { + m *Manager +} + +func (p *testPlugin) Start(context.Context) error { + p.m.UpdatePluginStatus("p1", &Status{State: StateOK}) + return nil +} + +func (p *testPlugin) Stop(context.Context) { + p.m.UpdatePluginStatus("p1", &Status{State: StateNotReady}) +} + +func (p *testPlugin) Reconfigure(context.Context, any) { + p.m.UpdatePluginStatus("p1", &Status{State: StateNotReady}) +} + +func TestPluginManagerLazyInitBeforePluginStart(t *testing.T) { + + m, err := New([]byte(`{"plugins": {"someplugin": {"enabled": true}}}`), "test", inmem.New()) + if err != nil { + t.Fatal(err) + } + + mock := &mockForInitStartOrdering{Manager: m} + + m.Register("someplugin", mock) + + if err := m.Start(context.Background()); err != nil { + t.Fatal(err) + } + + if !mock.Started { + t.Fatal("expected plugin to be started") + } + +} + +func TestPluginManagerInitBeforePluginStart(t *testing.T) { + + m, err := New([]byte(`{"plugins": {"someplugin": {}}}`), "test", inmem.New()) + if err != nil { + t.Fatal(err) + } + + if err := m.Init(context.Background()); err != nil { + t.Fatal(err) + } + + mock := &mockForInitStartOrdering{Manager: m} + + m.Register("someplugin", mock) + + if err := m.Start(context.Background()); err != nil { + t.Fatal(err) + } + + if !mock.Started { + t.Fatal("expected plugin to be started") + } + +} + +func TestPluginManagerInitIdempotence(t *testing.T) { + + mockStore := mock.New() + + m, err := New([]byte(`{"plugins": {"someplugin": {}}}`), "test", mockStore) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + if err := m.Init(ctx); err != nil { + t.Fatal(err) + } + + exp := len(mockStore.Transactions) + + if err := m.Init(ctx); err != nil { + t.Fatal(err) + } + + if len(mockStore.Transactions) != exp { + t.Fatal("expected num txns to be:", exp, "but got:", len(mockStore.Transactions)) + } + +} + +func TestManagerWithCachingConfig(t *testing.T) { + m, err := New([]byte(`{"caching": {"inter_query_builtin_cache": {"max_size_bytes": 100}, "inter_query_builtin_value_cache": {"max_num_entries": 100}}}`), "test", inmem.New()) + if err != nil { + t.Fatal(err) + } + + expected, _ := cache.ParseCachingConfig(nil) + limit := int64(100) + expected.InterQueryBuiltinCache.MaxSizeBytes = &limit + maxNumEntriesInterQueryValueCache := int(100) + expected.InterQueryBuiltinValueCache.MaxNumEntries = &maxNumEntriesInterQueryValueCache + + if !reflect.DeepEqual(m.InterQueryBuiltinCacheConfig(), expected) { + t.Fatalf("want %+v got %+v", expected, m.interQueryBuiltinCacheConfig) + } + + // config error + _, err = New([]byte(`{"caching": {"inter_query_builtin_cache": {"max_size_bytes": "100"}}}`), "test", inmem.New()) + if err == nil { + t.Fatal("expected error but got nil") + } + + // config error + _, err = New([]byte(`{"caching": {"inter_query_builtin_value_cache": {"max_num_entries": "100"}}}`), "test", inmem.New()) + if err == nil { + t.Fatal("expected error but got nil") + } +} + +func TestManagerWithNDCachingConfig(t *testing.T) { + m, err := New([]byte(`{"nd_builtin_cache": true}`), "test", inmem.New()) + if err != nil { + t.Fatal(err) + } + + expected := true + if !m.Config.NDBuiltinCache == expected { + t.Fatalf("want %+v got %+v", expected, m.Config.NDBuiltinCache) + } + + // config error + _, err = New([]byte(`{"nd_builtin_cache": "x"}`), "test", inmem.New()) + if err == nil { + t.Fatal("expected error but got nil") + } +} + +type mockForInitStartOrdering struct { + Manager *Manager + Started bool +} + +func (m *mockForInitStartOrdering) Start(_ context.Context) error { + m.Started = true + if m.Manager.initialized { + return nil + } + return errors.New("expected manager to be initialized") +} + +func (*mockForInitStartOrdering) Stop(context.Context) {} +func (*mockForInitStartOrdering) Reconfigure(context.Context, any) {} + +func TestPluginManagerAuthPlugin(t *testing.T) { + m, err := New([]byte(`{"plugins": {"someplugin": {}}}`), "test", inmem.New()) + if err != nil { + t.Fatal(err) + } + + if err := m.Init(context.Background()); err != nil { + t.Fatal(err) + } + + mock := &myAuthPluginMock{} + + m.Register("someplugin", mock) + + authPlugin := m.AuthPlugin("someplugin") + + if authPlugin == nil { + t.Fatal("expected to receive HTTPAuthPlugin") + } + + switch authPlugin.(type) { + case *myAuthPluginMock: + return + default: + t.Fatal("expected HTTPAuthPlugin to be myAuthPluginMock") + } +} + +func TestPluginManagerLogger(t *testing.T) { + + logger := logging.Get().WithFields(map[string]any{"context": "myloggincontext"}) + + m, err := New([]byte(`{}`), "test", inmem.New(), Logger(logger)) + if err != nil { + t.Fatal(err) + } + + if m.Logger() != logger { + t.Fatal("Logger was not configured on plugin manager") + } +} + +func TestPluginManagerConsoleLogger(t *testing.T) { + consoleLogger := test.New() + + mgr, err := New([]byte(`{}`), "", inmem.New(), ConsoleLogger(consoleLogger)) + if err != nil { + t.Fatal(err) + } + + const fieldKey = "foo" + const fieldValue = "bar" + mgr.ConsoleLogger().WithFields(map[string]any{fieldKey: fieldValue}).Info("Some message") + + entries := consoleLogger.Entries() + + exp := []test.LogEntry{ + { + Level: logging.Info, + Fields: map[string]any{fieldKey: fieldValue}, + Message: "Some message", + }, + } + + if !reflect.DeepEqual(exp, entries) { + t.Fatalf("want %v but got %v", exp, entries) + } +} + +func TestPluginManagerPrometheusRegister(t *testing.T) { + register := prometheusRegisterMock{Collectors: map[prom.Collector]bool{}} + mgr, err := New([]byte(`{}`), "", inmem.New(), WithPrometheusRegister(register)) + if err != nil { + t.Fatal(err) + } + + counter := prom.NewCounter(prom.CounterOpts{}) + if err := mgr.PrometheusRegister().Register(counter); err != nil { + t.Fatal(err) + } + if register.Collectors[counter] != true { + t.Fatalf("Counter metric was not registered on prometheus") + } +} + +func TestPluginManagerTracerProvider(t *testing.T) { + _, tracerProvider, _, err := internal_tracing.Init(context.TODO(), []byte(`{ "distributed_tracing": { "type": "grpc" } }`), "test") + if err != nil { + t.Fatal(err) + } + m, err := New([]byte(`{}`), "test", inmem.New(), WithTracerProvider(tracerProvider)) + if err != nil { + t.Fatal(err) + } + + if m.TracerProvider() != tracerProvider { + t.Fatal("TracerProvider was not configured on plugin manager") + } +} +func TestPluginManagerServerInitialized(t *testing.T) { + // Verify that ServerInitializedChannel is closed when + // ServerInitialized is called. + m1, err := New([]byte{}, "test1", inmem.New()) + if err != nil { + t.Fatal(err) + } + initChannel1 := m1.ServerInitializedChannel() + m1.ServerInitialized() + // Verify that ServerInitialized is idempotent and will not panic + m1.ServerInitialized() + select { + case <-initChannel1: + break + default: + t.Fatal("expected ServerInitializedChannel to be closed") + } + + // Verify that ServerInitializedChannel is open when + // ServerInitialized is not called. + m2, err := New([]byte{}, "test2", inmem.New()) + if err != nil { + t.Fatal(err) + } + initChannel2 := m2.ServerInitializedChannel() + select { + case <-initChannel2: + t.Fatal("expected ServerInitializedChannel to be open and have no messages") + default: + break + } +} + +type myAuthPluginMock struct{} + +func (*myAuthPluginMock) NewClient(c rest.Config) (*http.Client, error) { + tlsConfig, err := rest.DefaultTLSConfig(c) + if err != nil { + return nil, err + } + return rest.DefaultRoundTripperClient( + tlsConfig, + 10, + ), nil +} +func (*myAuthPluginMock) Prepare(*http.Request) error { + return nil +} +func (*myAuthPluginMock) Start(context.Context) error { + return nil +} +func (*myAuthPluginMock) Stop(context.Context) { +} +func (*myAuthPluginMock) Reconfigure(context.Context, any) { +} + +type prometheusRegisterMock struct { + Collectors map[prom.Collector]bool +} + +func (p prometheusRegisterMock) Register(collector prom.Collector) error { + p.Collectors[collector] = true + return nil +} + +func (p prometheusRegisterMock) MustRegister(collector ...prom.Collector) { + for _, c := range collector { + p.Collectors[c] = true + } +} + +func (p prometheusRegisterMock) Unregister(collector prom.Collector) bool { + delete(p.Collectors, collector) + return true +} diff --git a/third_party/opa/v1/plugins/rest/auth.go b/third_party/opa/v1/plugins/rest/auth.go new file mode 100644 index 000000000000..9a8d58cc660d --- /dev/null +++ b/third_party/opa/v1/plugins/rest/auth.go @@ -0,0 +1,1166 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package rest + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/sha512" + "crypto/tls" + "crypto/x509" + "encoding/asn1" + "encoding/base64" + "encoding/hex" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "hash" + "io" + "maps" + "math/big" + "net/http" + "net/url" + "os" + "strings" + "time" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jws" + "github.com/open-policy-agent/opa/internal/jwx/jws/sign" + "github.com/open-policy-agent/opa/internal/providers/aws" + "github.com/open-policy-agent/opa/internal/uuid" + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/logging" +) + +const ( + // Default to s3 when the service for sigv4 signing is not specified for backwards compatibility + awsSigv4SigningDefaultService = "s3" + // Default to urn:ietf:params:oauth:client-assertion-type:jwt-bearer for ClientAssertionType when not specified + defaultClientAssertionType = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" +) + +// DefaultTLSConfig defines standard TLS configurations based on the Config +func DefaultTLSConfig(c Config) (*tls.Config, error) { + t := &tls.Config{} + url, err := url.Parse(c.URL) + if err != nil { + return nil, err + } + if url.Scheme == "https" { + t.InsecureSkipVerify = c.AllowInsecureTLS + } + + if c.TLS != nil && c.TLS.CACert != "" { + caCert, err := os.ReadFile(c.TLS.CACert) + if err != nil { + return nil, err + } + + var rootCAs *x509.CertPool + if c.TLS.SystemCARequired { + rootCAs, err = x509.SystemCertPool() + if err != nil { + return nil, err + } + } else { + rootCAs = x509.NewCertPool() + } + + ok := rootCAs.AppendCertsFromPEM(caCert) + if !ok { + return nil, errors.New("unable to parse and append CA certificate to certificate pool") + } + t.RootCAs = rootCAs + } + + return t, nil +} + +// DefaultRoundTripperClient is a reasonable set of defaults for HTTP auth plugins +func DefaultRoundTripperClient(t *tls.Config, timeout int64) *http.Client { + // Ensure we use a http.Transport with proper settings: the zero values are not + // a good choice, as they cause leaking connections: + // https://github.com/golang/go/issues/19620 + + // copy, we don't want to alter the default client's Transport + tr := http.DefaultTransport.(*http.Transport).Clone() + tr.ResponseHeaderTimeout = time.Duration(timeout) * time.Second + tr.TLSClientConfig = t + + c := *http.DefaultClient + c.Transport = tr + return &c +} + +// defaultAuthPlugin represents baseline 'no auth' behavior if no alternative plugin is specified for a service +type defaultAuthPlugin struct{} + +func (*defaultAuthPlugin) NewClient(c Config) (*http.Client, error) { + t, err := DefaultTLSConfig(c) + if err != nil { + return nil, err + } + return DefaultRoundTripperClient(t, *c.ResponseHeaderTimeoutSeconds), nil +} + +func (*defaultAuthPlugin) Prepare(*http.Request) error { + return nil +} + +type serverTLSConfig struct { + CACert string `json:"ca_cert,omitempty"` + SystemCARequired bool `json:"system_ca_required,omitempty"` +} + +// bearerAuthPlugin represents authentication via a bearer token in the HTTP Authorization header +type bearerAuthPlugin struct { + Token string `json:"token"` + TokenPath string `json:"token_path"` + Scheme string `json:"scheme,omitempty"` + + // encode is set to true for the OCIDownloader because + // it expects tokens in plain text but needs them in base64. + encode bool + logger logging.Logger +} + +func (ap *bearerAuthPlugin) NewClient(c Config) (*http.Client, error) { + t, err := DefaultTLSConfig(c) + + ap.logger = c.logger + + if err != nil { + return nil, err + } + + if ap.Token != "" && ap.TokenPath != "" { + return nil, errors.New("invalid config: specify a value for either the \"token\" or \"token_path\" field") + } + + if ap.Scheme == "" { + ap.Scheme = "Bearer" + } + + if c.Type == "oci" { + // Standard rest clients use the bearer token as it is defined in the Config + // but the OCIDownloader needs it encoded to base64 before using to sign a request. + ap.encode = true + } + + return DefaultRoundTripperClient(t, *c.ResponseHeaderTimeoutSeconds), nil +} + +func (ap *bearerAuthPlugin) Prepare(req *http.Request) error { + token := ap.Token + if ap.logger == nil { + ap.logger = logging.Get() + } + + if ap.TokenPath != "" { + bytes, err := os.ReadFile(ap.TokenPath) + if err != nil { + return err + } + token = strings.TrimSpace(string(bytes)) + } + + if ap.encode { + token = base64.StdEncoding.EncodeToString([]byte(token)) + } + + if req.Response != nil && (req.Response.StatusCode == http.StatusPermanentRedirect || req.Response.StatusCode == http.StatusTemporaryRedirect) { + ap.logger.Debug("not attaching authorization header as the response contains a redirect") + } else { + ap.logger.Debug("attaching authorization header") + req.Header.Add("Authorization", fmt.Sprintf("%v %v", ap.Scheme, token)) + } + return nil +} + +type tokenEndpointResponse struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + ExpiresIn int64 `json:"expires_in"` +} + +type awsKmsKeyConfig struct { + Name string `json:"name"` + Algorithm string `json:"algorithm"` +} + +type azureKeyVaultConfig struct { + Key string `json:"key"` + KeyVersion string `json:"key_version"` + Alg string `json:"key_algorithm"` + Vault string `json:"vault"` + URL *url.URL + APIVersion string `json:"api_version"` +} + +func convertSignatureToBase64(alg string, der []byte) (string, error) { + r, s, derErr := pointsFromDER(der) + if derErr != nil { + return "", fmt.Errorf("failed to read points from der %v", derErr) + } + + signatureData, err := convertPointsToBase64(alg, r.Bytes(), s.Bytes()) + if err != nil { + return "", err + } + return signatureData, nil +} + +func pointsFromDER(der []byte) (R, S *big.Int, err error) { //nolint:gocritic + R, S = &big.Int{}, &big.Int{} + data := asn1.RawValue{} + if _, err := asn1.Unmarshal(der, &data); err != nil { + return nil, nil, fmt.Errorf("failed to unmarshall the signature from DER format %v", err) + + } + // https://docs.aws.amazon.com/kms/latest/APIReference/API_Sign.html#API_Sign_ResponseSyntax + // https://datatracker.ietf.org/doc/html/rfc3279#section-2.2.3 + // The format of our DER string is 0x02 + rlen + r + 0x02 + slen + s + rLen := data.Bytes[1] // The entire length of R + offset of 2 for 0x02 and rlen + r := data.Bytes[2 : rLen+2] + // Ignore the next 0x02 and slen bytes and just take the start of S to the end of the byte array + s := data.Bytes[rLen+4:] + R.SetBytes(r) + S.SetBytes(s) + return +} + +func convertPointsToBase64(alg string, r, s []byte) (string, error) { + curveBits, err := retrieveCurveBits(alg) + if err != nil { + return "", err + } + keyBytes := curveBits / 8 + if curveBits%8 > 0 { + keyBytes++ + } + // We serialize the outputs (r and s) into big-endian byte arrays and pad + // them with zeros on the left to make sure the sizes work out. Both arrays + // must be keyBytes long, and the output must be 2*keyBytes long. + rBytesPadded := make([]byte, keyBytes) + copy(rBytesPadded[keyBytes-len(r):], r) + sBytesPadded := make([]byte, keyBytes) + copy(sBytesPadded[keyBytes-len(s):], s) + signatureEnc := append(rBytesPadded, sBytesPadded...) + + return base64.RawURLEncoding.EncodeToString(signatureEnc), nil +} + +func retrieveCurveBits(alg string) (int, error) { + var curveBits int + switch alg { + case "ECDSA_SHA_256": + curveBits = 256 + case "ECDSA_SHA_384": + curveBits = 384 + case "ECDSA_SHA_512": + curveBits = 512 + default: + return 0, fmt.Errorf("unsupported sign algorithm %s", alg) + } + return curveBits, nil +} + +func messageDigest(message []byte, alg string) ([]byte, error) { + var digest hash.Hash + + switch alg { + case "ECDSA_SHA_256", "ES256", "ES256K", "PS256", "RS256": + digest = sha256.New() + case "ECDSA_SHA_384", "ES384", "PS384", "RS384": + digest = sha512.New384() + case "ECDSA_SHA_512", "ES512", "PS512", "RS512": + digest = sha512.New() + default: + return []byte{}, fmt.Errorf("unsupported sign algorithm %s", alg) + } + + _, err := digest.Write(message) + if err != nil { + return nil, err + } + return digest.Sum(nil), nil +} + +// oauth2ClientCredentialsAuthPlugin represents authentication via a bearer token in the HTTP Authorization header +// obtained through the OAuth2 client credentials flow +type oauth2ClientCredentialsAuthPlugin struct { + GrantType string `json:"grant_type"` + TokenURL string `json:"token_url"` + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + SigningKeyID string `json:"signing_key"` + Thumbprint string `json:"thumbprint"` + Claims map[string]any `json:"additional_claims"` + IncludeJti bool `json:"include_jti_claim"` + Scopes []string `json:"scopes,omitempty"` + AdditionalHeaders map[string]string `json:"additional_headers,omitempty"` + AdditionalParameters map[string]string `json:"additional_parameters,omitempty"` + AWSKmsKey *awsKmsKeyConfig `json:"aws_kms,omitempty"` + AWSSigningPlugin *awsSigningAuthPlugin `json:"aws_signing,omitempty"` + AzureKeyVault *azureKeyVaultConfig `json:"azure_keyvault,omitempty"` + AzureSigningPlugin *azureSigningAuthPlugin `json:"azure_signing,omitempty"` + ClientAssertionType string `json:"client_assertion_type"` + ClientAssertion string `json:"client_assertion"` + ClientAssertionPath string `json:"client_assertion_path"` + + signingKey *keys.Config + signingKeyParsed any + tokenCache *oauth2Token + tlsSkipVerify bool + logger logging.Logger +} + +type oauth2Token struct { + Token string + ExpiresAt time.Time +} + +func (ap *oauth2ClientCredentialsAuthPlugin) createJWSParts(extClaims map[string]any) ([]byte, []byte, string, error) { + now := time.Now() + claims := map[string]any{ + "iat": now.Unix(), + "exp": now.Add(10 * time.Minute).Unix(), + } + maps.Copy(claims, extClaims) + + if len(ap.Scopes) > 0 { + claims["scope"] = strings.Join(ap.Scopes, " ") + } + + if ap.IncludeJti { + jti, err := uuid.New(rand.Reader) + if err != nil { + return nil, nil, "", err + } + claims["jti"] = jti + } + + payload, err := json.Marshal(claims) + if err != nil { + return nil, nil, "", err + } + + var jwsHeaders []byte + var signatureAlg string + switch { + case ap.AWSKmsKey == nil && ap.AzureKeyVault == nil: + signatureAlg = ap.signingKey.Algorithm + case ap.AWSKmsKey != nil && ap.AWSKmsKey.Algorithm != "": + signatureAlg, err = ap.mapKMSAlgToSign(ap.AWSKmsKey.Algorithm) + if err != nil { + return nil, nil, "", err + } + case ap.AzureKeyVault != nil && ap.AzureKeyVault.Alg != "": + signatureAlg = ap.AzureKeyVault.Alg + } + if ap.Thumbprint != "" { + bytes, err := hex.DecodeString(ap.Thumbprint) + if err != nil { + return nil, nil, "", err + } + x5t := base64.URLEncoding.EncodeToString(bytes) + jwsHeaders = fmt.Appendf(nil, `{"typ":"JWT","alg":"%s","x5t":"%s"}`, signatureAlg, x5t) + } else { + jwsHeaders = fmt.Appendf(nil, `{"typ":"JWT","alg":"%s"}`, signatureAlg) + } + + return jwsHeaders, payload, signatureAlg, nil +} + +func (ap *oauth2ClientCredentialsAuthPlugin) createAuthJWT(ctx context.Context, extClaims map[string]any, signingKey any) (*string, error) { + header, payload, alg, err := ap.createJWSParts(extClaims) + if err != nil { + return nil, err + } + + var clientAssertion []byte + switch { + case ap.AWSKmsKey != nil: + clientAssertion, err = ap.SignWithKMS(ctx, payload, header) + case ap.AzureKeyVault != nil: + clientAssertion, err = ap.SignWithKeyVault(ctx, payload, header) + default: + clientAssertion, err = jws.SignLiteral(payload, + jwa.SignatureAlgorithm(alg), + signingKey, + header, + rand.Reader) + } + if err != nil { + return nil, err + } + jwt := string(clientAssertion) + + return &jwt, nil +} + +func (*oauth2ClientCredentialsAuthPlugin) mapKMSAlgToSign(alg string) (string, error) { + switch alg { + case "ECDSA_SHA_256": + return "ES256", nil + case "ECDSA_SHA_384": + return "ES384", nil + case "ECDSA_SHA_512": + return "ES512", nil + default: + return "", fmt.Errorf("unsupported sign algorithm %s", alg) + } +} + +// SignWithKMS will sign the JWT in AWS using the key stored in the supplied kmsArn +func (ap *oauth2ClientCredentialsAuthPlugin) SignWithKMS(ctx context.Context, payload []byte, hdrBuf []byte) ([]byte, error) { + + encodedHdr := base64.RawURLEncoding.EncodeToString(hdrBuf) + encodedPayload := base64.RawURLEncoding.EncodeToString(payload) + input := encodedHdr + "." + encodedPayload + digest, err := messageDigest([]byte(input), ap.AWSKmsKey.Algorithm) + if err != nil { + return nil, err + } + if ap.AWSSigningPlugin != nil { + signature, err := ap.AWSSigningPlugin.SignDigest(ctx, digest, ap.AWSKmsKey.Name, ap.AWSKmsKey.Algorithm) + if err != nil { + return nil, err + } + der, err := base64.StdEncoding.DecodeString(signature) + if err != nil { + return nil, err + } + signatureData, err := convertSignatureToBase64(ap.AWSKmsKey.Algorithm, der) + if err != nil { + return nil, err + } + + signedAssertion := input + "." + signatureData + + return []byte(signedAssertion), nil + } + return nil, errors.New("missing AWS credentials, failed to sign the assertion with kms") +} + +func (ap *oauth2ClientCredentialsAuthPlugin) SignWithKeyVault(ctx context.Context, payload []byte, hdrBuf []byte) ([]byte, error) { + if ap.AzureSigningPlugin == nil { + return nil, errors.New("missing Azure credentials, failed to sign the assertion with KeyVault") + } + + encodedHdr := base64.RawURLEncoding.EncodeToString(hdrBuf) + encodedPayload := base64.RawURLEncoding.EncodeToString(payload) + input := encodedHdr + "." + encodedPayload + digest, err := messageDigest([]byte(input), ap.AzureSigningPlugin.keyVaultSignPlugin.config.Alg) + if err != nil { + fmt.Println("unsupported algorithm", ap.AzureSigningPlugin.keyVaultSignPlugin.config.Alg) + return nil, err + } + + signature, err := ap.AzureSigningPlugin.SignDigest(ctx, digest) + if err != nil { + return nil, err + } + + return []byte(input + "." + signature), nil +} + +func (ap *oauth2ClientCredentialsAuthPlugin) parseSigningKey(c Config) (err error) { + if ap.SigningKeyID == "" { + return errors.New("signing_key required for jwt_bearer grant type") + } + + if val, ok := c.keys[ap.SigningKeyID]; ok { + if val.PrivateKey == "" { + return errors.New("referenced signing_key does not include a private key") + } + ap.signingKey = val + } else { + return errors.New("signing_key refers to non-existent key") + } + + alg := jwa.SignatureAlgorithm(ap.signingKey.Algorithm) + ap.signingKeyParsed, err = sign.GetSigningKey(ap.signingKey.PrivateKey, alg) + if err != nil { + return err + } + + return nil +} + +func (ap *oauth2ClientCredentialsAuthPlugin) NewClient(c Config) (*http.Client, error) { + t, err := DefaultTLSConfig(c) + if err != nil { + return nil, err + } + + if ap.GrantType == "" { + // Use client_credentials as default to not break existing config + ap.GrantType = grantTypeClientCredentials + } else if ap.GrantType != grantTypeClientCredentials && ap.GrantType != grantTypeJwtBearer { + return nil, errors.New("grant_type must be either client_credentials or jwt_bearer") + } + + if ap.GrantType == grantTypeJwtBearer || (ap.GrantType == grantTypeClientCredentials && ap.SigningKeyID != "") { + if err = ap.parseSigningKey(c); err != nil { + return nil, err + } + } + + // Inherit skip verify from the "parent" settings. Should this be configurable on the credentials too? + ap.tlsSkipVerify = c.AllowInsecureTLS + + ap.logger = c.logger + + if !strings.HasPrefix(ap.TokenURL, "https://") { + return nil, errors.New("token_url required to use https scheme") + } + if ap.GrantType == grantTypeClientCredentials { + clientCredentialExists := make(map[string]bool) + clientCredentialExists["client_secret"] = ap.ClientSecret != "" + clientCredentialExists["signing_key"] = ap.SigningKeyID != "" + clientCredentialExists["aws_kms"] = ap.AWSKmsKey != nil + clientCredentialExists["azure_keyvault"] = ap.AzureKeyVault != nil + clientCredentialExists["client_assertion"] = ap.ClientAssertion != "" + clientCredentialExists["client_assertion_path"] = ap.ClientAssertionPath != "" + + var notEmptyVarCount int + + for _, credentialSet := range clientCredentialExists { + if credentialSet { + notEmptyVarCount++ + } + } + + if notEmptyVarCount == 0 { + return nil, errors.New("please provide one of client_secret, signing_key, aws_kms, azure_keyvault, client_assertion, or client_assertion_path required") + } + + if notEmptyVarCount > 1 { + return nil, errors.New("can only use one of client_secret, signing_key, aws_kms, azure_keyvault, client_assertion, or client_assertion_path") + } + + switch { + case clientCredentialExists["aws_kms"]: + if ap.AWSSigningPlugin == nil { + return nil, errors.New("aws_kms and aws_signing required") + } + // initialize the awsSigningAuthPlugin + _, err = ap.AWSSigningPlugin.NewClient(c) + if err != nil { + return nil, err + } + case clientCredentialExists["azure_keyvault"]: + _, err := ap.AzureSigningPlugin.NewClient(c) + if err != nil { + return nil, err + } + case clientCredentialExists["client_assertion"]: + if ap.ClientAssertionType == "" { + ap.ClientAssertionType = defaultClientAssertionType + } + if ap.ClientID == "" { + return nil, errors.New("client_id and client_assertion required") + } + case clientCredentialExists["client_assertion_path"]: + if ap.ClientAssertionType == "" { + ap.ClientAssertionType = defaultClientAssertionType + } + if ap.ClientID == "" { + return nil, errors.New("client_id and client_assertion_path required") + } + case clientCredentialExists["client_secret"] && ap.ClientID == "": + return nil, errors.New("client_id and client_secret required") + } + } + + return DefaultRoundTripperClient(t, *c.ResponseHeaderTimeoutSeconds), nil +} + +func (ap *oauth2ClientCredentialsAuthPlugin) createTokenReqBody(ctx context.Context) (url.Values, error) { + body := url.Values{} + + if len(ap.Scopes) > 0 { + body.Add("scope", strings.Join(ap.Scopes, " ")) + } + + for k, v := range ap.AdditionalParameters { + body.Set(k, v) + } + + if ap.GrantType == grantTypeJwtBearer { + authJWT, err := ap.createAuthJWT(ctx, ap.Claims, ap.signingKeyParsed) + if err != nil { + return nil, err + } + body.Add("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer") + body.Add("assertion", *authJWT) + return body, nil + } + + body.Add("grant_type", grantTypeClientCredentials) + + switch { + case ap.SigningKeyID != "" || ap.AWSKmsKey != nil || ap.AzureKeyVault != nil: + authJwt, err := ap.createAuthJWT(ctx, ap.Claims, ap.signingKeyParsed) + if err != nil { + return nil, err + } + body.Add("client_assertion_type", defaultClientAssertionType) + body.Add("client_assertion", *authJwt) + + if ap.ClientID != "" { + body.Add("client_id", ap.ClientID) + } + case ap.ClientAssertion != "": + if ap.ClientAssertionType == "" { + ap.ClientAssertionType = defaultClientAssertionType + } + if ap.ClientID != "" { + body.Add("client_id", ap.ClientID) + } + body.Add("client_assertion_type", ap.ClientAssertionType) + body.Add("client_assertion", ap.ClientAssertion) + + case ap.ClientAssertionPath != "": + if ap.ClientAssertionType == "" { + ap.ClientAssertionType = defaultClientAssertionType + } + bytes, err := os.ReadFile(ap.ClientAssertionPath) + if err != nil { + return nil, err + } + if ap.ClientID != "" { + body.Add("client_id", ap.ClientID) + } + body.Add("client_assertion_type", ap.ClientAssertionType) + body.Add("client_assertion", strings.TrimSpace(string(bytes))) + } + + return body, nil +} + +// requestToken tries to obtain an access token using either the client credentials flow +// https://tools.ietf.org/html/rfc6749#section-4.4 +// or the JWT authorization grant +// https://tools.ietf.org/html/rfc7523 +func (ap *oauth2ClientCredentialsAuthPlugin) requestToken(ctx context.Context) (*oauth2Token, error) { + body, err := ap.createTokenReqBody(ctx) + if err != nil { + return nil, err + } + + r, err := http.NewRequestWithContext(ctx, http.MethodPost, ap.TokenURL, strings.NewReader(body.Encode())) + if err != nil { + return nil, err + } + r.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + if ap.GrantType == grantTypeClientCredentials && ap.ClientSecret != "" { + r.SetBasicAuth(ap.ClientID, ap.ClientSecret) + } + + for k, v := range ap.AdditionalHeaders { + r.Header.Add(k, v) + } + + client := DefaultRoundTripperClient(&tls.Config{InsecureSkipVerify: ap.tlsSkipVerify}, 10) + response, err := client.Do(r) + if err != nil { + return nil, err + } + defer response.Body.Close() + + bodyRaw, err := io.ReadAll(response.Body) + if err != nil { + return nil, err + } + + if response.StatusCode != 200 { + return nil, fmt.Errorf("error in response from OAuth2 token endpoint: %v", string(bodyRaw)) + } + + var tokenResponse tokenEndpointResponse + err = json.Unmarshal(bodyRaw, &tokenResponse) + if err != nil { + return nil, err + } + + if !strings.EqualFold(tokenResponse.TokenType, "bearer") { + return nil, errors.New("unknown token type returned from token endpoint") + } + + return &oauth2Token{ + Token: strings.TrimSpace(tokenResponse.AccessToken), + ExpiresAt: time.Now().Add(time.Duration(tokenResponse.ExpiresIn) * time.Second), + }, nil +} + +func (ap *oauth2ClientCredentialsAuthPlugin) Prepare(req *http.Request) error { + minTokenLifetime := float64(10) + if ap.tokenCache == nil || time.Until(ap.tokenCache.ExpiresAt).Seconds() < minTokenLifetime { + ap.logger.Debug("Requesting token from token_url %v", ap.TokenURL) + token, err := ap.requestToken(req.Context()) + if err != nil { + return err + } + ap.tokenCache = token + } + + req.Header.Add("Authorization", fmt.Sprintf("Bearer %v", ap.tokenCache.Token)) + return nil +} + +// clientTLSAuthPlugin represents authentication via client certificate on a TLS connection +type clientTLSAuthPlugin struct { + Cert string `json:"cert"` + PrivateKey string `json:"private_key"` + PrivateKeyPassphrase string `json:"private_key_passphrase,omitempty"` + CACert string `json:"ca_cert,omitempty"` // Deprecated: Use `services[_].tls.ca_cert` instead + SystemCARequired bool `json:"system_ca_required,omitempty"` // Deprecated: Use `services[_].tls.system_ca_required` instead +} + +func (ap *clientTLSAuthPlugin) NewClient(c Config) (*http.Client, error) { + tlsConfig, err := DefaultTLSConfig(c) + if err != nil { + return nil, err + } + + if ap.Cert == "" { + return nil, errors.New("client certificate is needed when client TLS is enabled") + } + if ap.PrivateKey == "" { + return nil, errors.New("private key is needed when client TLS is enabled") + } + + var keyPEMBlock []byte + data, err := os.ReadFile(ap.PrivateKey) + if err != nil { + return nil, err + } + + block, _ := pem.Decode(data) + if block == nil { + return nil, errors.New("PEM data could not be found") + } + + // nolint: staticcheck // We don't want to forbid users from using this encryption. + if x509.IsEncryptedPEMBlock(block) { + if ap.PrivateKeyPassphrase == "" { + return nil, errors.New("client certificate passphrase is needed, because the certificate is password encrypted") + } + // nolint: staticcheck // We don't want to forbid users from using this encryption. + block, err := x509.DecryptPEMBlock(block, []byte(ap.PrivateKeyPassphrase)) + if err != nil { + return nil, err + } + key, err := x509.ParsePKCS8PrivateKey(block) + if err != nil { + key, err = x509.ParsePKCS1PrivateKey(block) + if err != nil { + return nil, fmt.Errorf("private key should be a PEM or plain PKCS1 or PKCS8; parse error: %v", err) + } + } + rsa, ok := key.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("private key is invalid") + } + keyPEMBlock = pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(rsa), + }, + ) + } else { + keyPEMBlock = data + } + + certPEMBlock, err := os.ReadFile(ap.Cert) + if err != nil { + return nil, err + } + + cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock) + if err != nil { + return nil, err + } + tlsConfig.Certificates = []tls.Certificate{cert} + + var client *http.Client + + if c.TLS != nil && c.TLS.CACert != "" { + client = DefaultRoundTripperClient(tlsConfig, *c.ResponseHeaderTimeoutSeconds) + } else { + if ap.CACert != "" { + c.logger.Warn("Deprecated 'services[_].credentials.client_tls.ca_cert' configuration specified. Use 'services[_].tls.ca_cert' instead. See https://www.openpolicyagent.org/docs/latest/configuration/#services") + caCert, err := os.ReadFile(ap.CACert) + if err != nil { + return nil, err + } + + var caCertPool *x509.CertPool + if ap.SystemCARequired { + caCertPool, err = x509.SystemCertPool() + if err != nil { + return nil, err + } + } else { + caCertPool = x509.NewCertPool() + } + + ok := caCertPool.AppendCertsFromPEM(caCert) + if !ok { + return nil, errors.New("unable to parse and append CA certificate to certificate pool") + } + tlsConfig.RootCAs = caCertPool + } + + client = DefaultRoundTripperClient(tlsConfig, *c.ResponseHeaderTimeoutSeconds) + } + + return client, nil +} + +func (*clientTLSAuthPlugin) Prepare(_ *http.Request) error { + return nil +} + +// awsSigningAuthPlugin represents authentication using AWS V4 HMAC signing in the Authorization header +type awsSigningAuthPlugin struct { + AWSEnvironmentCredentials *awsEnvironmentCredentialService `json:"environment_credentials,omitempty"` + AWSMetadataCredentials *awsMetadataCredentialService `json:"metadata_credentials,omitempty"` + AWSAssumeRoleCredentials *awsAssumeRoleCredentialService `json:"assume_role_credentials,omitempty"` + AWSWebIdentityCredentials *awsWebIdentityCredentialService `json:"web_identity_credentials,omitempty"` + AWSProfileCredentials *awsProfileCredentialService `json:"profile_credentials,omitempty"` + AWSSSOCredentials *awsSSOCredentialsService `json:"sso_credentials,omitempty"` + + AWSService string `json:"service,omitempty"` + AWSSignatureVersion string `json:"signature_version,omitempty"` + + host string + ecrAuthPlugin *ecrAuthPlugin + kmsSignPlugin *awsKMSSignPlugin + + logger logging.Logger +} + +type awsCredentialServiceChain struct { + awsCredentialServices []awsCredentialService + logger logging.Logger +} + +func (acs *awsCredentialServiceChain) addService(service awsCredentialService) { + acs.awsCredentialServices = append(acs.awsCredentialServices, service) +} + +type awsCredentialCheckErrors []*awsCredentialCheckError + +func (e awsCredentialCheckErrors) Error() string { + + if len(e) == 0 { + return "no error(s)" + } + + if len(e) == 1 { + return fmt.Sprintf("1 error occurred: %v", e[0].Error()) + } + + s := make([]string, len(e)) + for i, err := range e { + s[i] = err.Error() + } + + return fmt.Sprintf("%d errors occurred:\n%s", len(e), strings.Join(s, "\n")) +} + +type awsCredentialCheckError struct { + message string +} + +func newAWSCredentialError(message string) *awsCredentialCheckError { + return &awsCredentialCheckError{ + message: message, + } +} + +func (e *awsCredentialCheckError) Error() string { + return e.message +} + +func (acs *awsCredentialServiceChain) credentials(ctx context.Context) (aws.Credentials, error) { + var errs awsCredentialCheckErrors + + for _, service := range acs.awsCredentialServices { + credential, err := service.credentials(ctx) + if err != nil { + acs.logger.Debug("awsSigningAuthPlugin:%T failed: %v", service, err) + + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + return aws.Credentials{}, err + } + + errs = append(errs, newAWSCredentialError(err.Error())) + continue + } + + acs.logger.Debug("awsSigningAuthPlugin:%T successful", service) + return credential, nil + } + + return aws.Credentials{}, fmt.Errorf("all AWS credential providers failed: %v", errs) +} + +func (ap *awsSigningAuthPlugin) awsCredentialService() awsCredentialService { + chain := awsCredentialServiceChain{ + logger: ap.logger, + } + + /* + Here we maintain the order of addition to the chain inline with + the order of credential providers followed by default by the + AWS SDK. For example + + https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html + */ + + if ap.AWSEnvironmentCredentials != nil { + ap.AWSEnvironmentCredentials.logger = ap.logger + chain.addService(ap.AWSEnvironmentCredentials) + } + + if ap.AWSAssumeRoleCredentials != nil { + ap.AWSAssumeRoleCredentials.logger = ap.logger + chain.addService(ap.AWSAssumeRoleCredentials) + } + + if ap.AWSWebIdentityCredentials != nil { + ap.AWSWebIdentityCredentials.logger = ap.logger + chain.addService(ap.AWSWebIdentityCredentials) + } + + if ap.AWSProfileCredentials != nil { + ap.AWSProfileCredentials.logger = ap.logger + chain.addService(ap.AWSProfileCredentials) + } + + if ap.AWSMetadataCredentials != nil { + ap.AWSMetadataCredentials.logger = ap.logger + chain.addService(ap.AWSMetadataCredentials) + } + + if ap.AWSSSOCredentials != nil { + ap.AWSSSOCredentials.logger = ap.logger + chain.addService(ap.AWSSSOCredentials) + } + + return &chain +} + +func (ap *awsSigningAuthPlugin) NewClient(c Config) (*http.Client, error) { + t, err := DefaultTLSConfig(c) + if err != nil { + 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 + } + + if err := ap.validateAndSetDefaults(c.Type); err != nil { + return nil, err + } + + return DefaultRoundTripperClient(t, *c.ResponseHeaderTimeoutSeconds), nil +} + +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) + default: + creds, err := ap.awsCredentialService().credentials(req.Context()) + if err != nil { + return fmt.Errorf("failed to get aws credentials: %w", err) + } + + ap.logger.Debug("Signing request with AWS credentials.") + + return aws.SignRequest(req, ap.AWSService, creds, time.Now(), ap.AWSSignatureVersion) + } +} + +func (ap *awsSigningAuthPlugin) validateAndSetDefaults(serviceType string) error { + cfgs := map[bool]int{} + cfgs[ap.AWSEnvironmentCredentials != nil]++ + cfgs[ap.AWSMetadataCredentials != nil]++ + cfgs[ap.AWSAssumeRoleCredentials != nil]++ + cfgs[ap.AWSWebIdentityCredentials != nil]++ + cfgs[ap.AWSProfileCredentials != nil]++ + cfgs[ap.AWSSSOCredentials != nil]++ + + if cfgs[true] == 0 { + return errors.New("a AWS credential service must be specified when S3 signing is enabled") + } + + if ap.AWSMetadataCredentials != nil { + if ap.AWSMetadataCredentials.RegionName == "" { + return errors.New("at least aws_region must be specified for AWS metadata credential service") + } + } + + if ap.AWSAssumeRoleCredentials != nil { + if err := ap.AWSAssumeRoleCredentials.populateFromEnv(); err != nil { + return err + } + } + + if ap.AWSWebIdentityCredentials != nil { + if err := ap.AWSWebIdentityCredentials.populateFromEnv(); err != nil { + return err + } + } + + ap.AWSService = strings.ToLower(ap.AWSService) + + // Only allow ECR for OCI service types + if serviceType == "oci" { + if ap.AWSService == "" { + ap.AWSService = "ecr" + } + + if ap.AWSService != "ecr" { + return fmt.Errorf(`cannot use aws service %q with service type "oci"`, ap.AWSService) + } + + // We need to setup a special auth plugin for ECR. + ap.ecrAuthPlugin = newECRAuthPlugin(ap) + } else { + // Disallow ECR for non-OCI service types + if ap.AWSService == "ecr" { + return errors.New(`aws service "ecr" must be used with service type "oci"`) + } + if ap.AWSService == "kms" && ap.kmsSignPlugin == nil { + // We need a special plugin for KMS. + ap.kmsSignPlugin = newKMSSignPlugin(ap) + } + if ap.AWSService == "" { + ap.AWSService = awsSigv4SigningDefaultService + } + } + + if ap.AWSSignatureVersion == "" { + ap.AWSSignatureVersion = "4" + } + + return nil +} + +func (ap *awsSigningAuthPlugin) SignDigest(ctx context.Context, digest []byte, keyID string, signingAlgorithm string) (string, error) { + switch ap.AWSService { + case "kms": + return ap.kmsSignPlugin.SignDigest(ctx, digest, keyID, signingAlgorithm) + default: + return "", fmt.Errorf(`cannot use SignDigest with aws service %q`, ap.AWSService) + } +} + +type azureSigningAuthPlugin struct { + MIAuthPlugin *azureManagedIdentitiesAuthPlugin `json:"azure_managed_identity,omitempty"` + keyVaultSignPlugin *azureKeyVaultSignPlugin + keyVaultConfig *azureKeyVaultConfig + host string + Service string `json:"service"` + logger logging.Logger +} + +func (ap *azureSigningAuthPlugin) NewClient(c Config) (*http.Client, error) { + t, err := DefaultTLSConfig(c) + if err != nil { + return nil, err + } + + tknURL, err := url.Parse(c.URL) + if err != nil { + return nil, err + } + + ap.host = tknURL.Host + + if ap.logger == nil { + ap.logger = c.logger + } + + if c.Credentials.OAuth2.AzureKeyVault == nil { + return nil, errors.New("missing keyvault config") + } + ap.keyVaultConfig = c.Credentials.OAuth2.AzureKeyVault + + if err := ap.validateAndSetDefaults(); err != nil { + return nil, err + } + + return DefaultRoundTripperClient(t, *c.ResponseHeaderTimeoutSeconds), nil +} + +func (ap *azureSigningAuthPlugin) validateAndSetDefaults() error { + if ap.MIAuthPlugin == nil { + return errors.New("missing azure managed identity config") + } + ap.MIAuthPlugin.setDefaults() + + if ap.keyVaultSignPlugin != nil { + return nil + } + ap.keyVaultConfig.URL = &url.URL{ + Scheme: "https", + Host: ap.keyVaultConfig.Vault + ".vault.azure.net", + } + ap.keyVaultSignPlugin = newKeyVaultSignPlugin(ap.MIAuthPlugin, ap.keyVaultConfig) + ap.keyVaultSignPlugin.setDefaults() + ap.keyVaultConfig = &ap.keyVaultSignPlugin.config + + return nil +} + +func (ap *azureSigningAuthPlugin) Prepare(req *http.Request) error { + switch ap.Service { + case "keyvault": + tkn, err := ap.keyVaultSignPlugin.tokener() + if err != nil { + return err + } + req.Header.Add("Authorization", "Bearer "+tkn) + return nil + default: + return fmt.Errorf("azureSigningAuthPlugin.Prepare() with %s not supported", ap.Service) + } +} + +func (ap *azureSigningAuthPlugin) SignDigest(ctx context.Context, digest []byte) (string, error) { + switch ap.Service { + case "keyvault": + return ap.keyVaultSignPlugin.SignDigest(ctx, digest) + default: + return "", fmt.Errorf(`cannot use SignDigest with azure service %q`, ap.Service) + } +} diff --git a/third_party/opa/v1/plugins/rest/auth_test.go b/third_party/opa/v1/plugins/rest/auth_test.go new file mode 100644 index 000000000000..3c1122df2cb6 --- /dev/null +++ b/third_party/opa/v1/plugins/rest/auth_test.go @@ -0,0 +1,418 @@ +package rest + +import ( + "bytes" + "net/http" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/logging" +) + +func TestOCIWithAWSAuthSetsUpECRAuthPlugin(t *testing.T) { + conf := `{ + "type": "oci", + "credentials": { + "s3_signing": { + "environment_credentials": {} + } + } + }` + + client, err := New([]byte(conf), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("New() = %v", err) + } + + if _, err := client.config.Credentials.S3Signing.NewClient(client.config); err != nil { + t.Fatalf("S3Signing.NewClient() = %q", err) + } + + if client.config.Credentials.S3Signing.AWSService != "ecr" { + t.Errorf("S3Signing.AWSService = %v, want = %v", client.config.Credentials.S3Signing.AWSService, "ecr") + } + + if client.config.Credentials.S3Signing.ecrAuthPlugin == nil { + t.Errorf("S3Signing.ecrAuthPlugin isn't setup") + } +} + +func TestOCIWithAWSWrongService(t *testing.T) { + conf := `{ + "type": "oci", + "credentials": { + "s3_signing": { + "service": "ec2", + "environment_credentials": {} + } + } + }` + + client, err := New([]byte(conf), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("New() = %q", err) + } + + { + _, err := client.config.Credentials.S3Signing.NewClient(client.config) + if err == nil { + t.Fatalf("S3Signing.NewClient(): expected error") + } + + wantContains := "ec2" + if !strings.Contains(err.Error(), wantContains) { + t.Errorf("got: %q, should contain: %q", err.Error(), wantContains) + } + } +} + +func TestECRWithoutOCIFails(t *testing.T) { + conf := `{ + "credentials": { + "s3_signing": { + "service": "ecr", + "environment_credentials": {} + } + } + }` + + client, err := New([]byte(conf), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("New() = %q", err) + } + + { + _, err := client.config.Credentials.S3Signing.NewClient(client.config) + if err == nil { + t.Fatal("S3Signing.NewClient(): expected error") + } + + wantContains := "oci" + if !strings.Contains(err.Error(), wantContains) { + t.Fatalf("S3Signing.NewClient() = %q, should contain = %q", err, wantContains) + } + } +} + +func TestOauth2WithAWSKMS(t *testing.T) { + conf := `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": "client_credentials", + "aws_kms": { + "name": "arn:aws:kms:eu-west-1:account_no:key/key_id", + "algorithm": "ECDSA_SHA_256" + }, + "aws_signing": { + "service": "kms", + "environment_credentials": { + "aws_default_region": "eu-west-1" + } + }, + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }` + + client, err := New([]byte(conf), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("New() = %v", err) + } + + if _, err := client.config.Credentials.OAuth2.NewClient(client.config); err != nil { + t.Fatalf("OAuth2.NewClient() = %q", err) + } + + if client.config.Credentials.OAuth2.AWSKmsKey.Name != "arn:aws:kms:eu-west-1:account_no:key/key_id" { + t.Errorf("OAuth2.AWSKmsKey.Name = %v, want = %v", client.config.Credentials.OAuth2.AWSKmsKey.Name, "arn:aws:kms:eu-west-1:account_no:key/key_id") + } + + if client.config.Credentials.OAuth2.AWSSigningPlugin.kmsSignPlugin == nil { + t.Errorf("OAuth2.AWSSigningPlugin.kmsSignPlugin isn't setup") + } +} + +func TestOauthWithAzureKV(t *testing.T) { + cfg := `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": "client_credentials", + "azure_keyvault": { + "key": "tester-key", + "key_algorithm": "ES256", + "vault": "my-secret-kv" + }, + "azure_signing": { + "service": "keyvault", + azure_managed_identity: {} + }, + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }` + + client, err := New([]byte(cfg), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("New() = %v", err) + } + + if _, err := client.config.Credentials.OAuth2.NewClient(client.config); err != nil { + t.Fatalf("OAuth2.NewClient() = %q", err) + } + + if client.config.Credentials.OAuth2.AzureKeyVault.Key != "tester-key" { + t.Errorf("OAuth2.AzureKeyVault.Key = %v, want = %v", client.config.Credentials.OAuth2.AzureKeyVault.Key, "tester-key") + } + + if client.config.Credentials.OAuth2.AzureSigningPlugin.keyVaultSignPlugin == nil { + t.Errorf("OAuth2.AzureSigningPlugin.keyVaultSignPlugin isn't setup") + } +} + +func TestAssumeRoleWithNoSigningProvider(t *testing.T) { + conf := `{ + "name": "foo", + "url": "https://my-example-opa-bucket.s3.eu-north-1.amazonaws.com", + "credentials": { + "s3_signing": { + "service": "s3", + "assume_role_credentials": {} + } + } + }` + + client, err := New([]byte(conf), map[string]*keys.Config{}) + if err != nil { + t.Fatal(err) + } + + _, err = client.config.Credentials.S3Signing.NewClient(client.config) + if err == nil { + t.Fatal("expected error but got nil") + } + + expErrMsg := "a AWS signing plugin must be specified when AssumeRole credential provider is enabled" + if err.Error() != expErrMsg { + t.Fatalf("expected error: %v but got: %v", expErrMsg, err) + } +} + +func TestAssumeRoleWithUnsupportedSigningProvider(t *testing.T) { + conf := `{ + "name": "foo", + "url": "https://my-example-opa-bucket.s3.eu-north-1.amazonaws.com", + "credentials": { + "s3_signing": { + "service": "s3", + "assume_role_credentials": {"aws_signing": {"web_identity_credentials": {}}} + } + } + }` + + client, err := New([]byte(conf), map[string]*keys.Config{}) + if err != nil { + t.Fatal(err) + } + + _, err = client.config.Credentials.S3Signing.NewClient(client.config) + if err == nil { + t.Fatal("expected error but got nil") + } + + expErrMsg := "unsupported AWS signing plugin with AssumeRole credential provider" + if err.Error() != expErrMsg { + t.Fatalf("expected error: %v but got: %v", expErrMsg, err) + } +} + +func TestOauth2WithClientAssertion(t *testing.T) { + conf := `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": "client_credentials", + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + }, + "client_id": "123", + "client_assertion": "abc123" + } + } + }` + + client, err := New([]byte(conf), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("New() = %v", err) + } + + if _, err := client.config.Credentials.OAuth2.NewClient(client.config); err != nil { + t.Fatalf("OAuth2.NewClient() = %q", err) + } + + if client.config.Credentials.OAuth2.ClientAssertionType != defaultClientAssertionType { + t.Errorf("OAuth2.ClientAssertionType = %v, want = %v", client.config.Credentials.OAuth2.ClientAssertionType, defaultClientAssertionType) + } +} + +func TestOauth2WithClientAssertionOverrideAssertionType(t *testing.T) { + conf := `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": "client_credentials", + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + }, + "client_id": "123", + "client_assertion": "abc123", + "client_assertion_type": "urn:ietf:params:oauth:my-thing" + } + } + }` + + client, err := New([]byte(conf), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("New() = %v", err) + } + + if _, err := client.config.Credentials.OAuth2.NewClient(client.config); err != nil { + t.Fatalf("OAuth2.NewClient() = %q", err) + } + + if client.config.Credentials.OAuth2.ClientAssertionType != "urn:ietf:params:oauth:my-thing" { + t.Errorf("OAuth2.ClientAssertionType = %v, want = %v", client.config.Credentials.OAuth2.ClientAssertionType, "urn:ietf:params:oauth:my-thing") + } +} + +func TestOauth2WithClientAssertionPath(t *testing.T) { + conf := `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": "client_credentials", + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + }, + "client_id": "123", + "client_assertion_path": "/var/run/secrets/azure/tokens/azure-identity-token" + } + } + }` + + client, err := New([]byte(conf), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("New() = %v", err) + } + + if _, err := client.config.Credentials.OAuth2.NewClient(client.config); err != nil { + t.Fatalf("OAuth2.NewClient() = %q", err) + } + + if client.config.Credentials.OAuth2.ClientAssertionType != defaultClientAssertionType { + t.Errorf("OAuth2.ClientAssertionType = %v, want = %v", client.config.Credentials.OAuth2.ClientAssertionType, defaultClientAssertionType) + } +} + +func TestOauth2WithClientAssertionPathOverrideAssertionType(t *testing.T) { + conf := `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": "client_credentials", + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + }, + "client_id": "123", + "client_assertion_path": "/var/run/secrets/azure/tokens/azure-identity-token", + "client_assertion_type": "urn:ietf:params:oauth:my-thing" + } + } + }` + + client, err := New([]byte(conf), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("New() = %v", err) + } + + if _, err := client.config.Credentials.OAuth2.NewClient(client.config); err != nil { + t.Fatalf("OAuth2.NewClient() = %q", err) + } + + if client.config.Credentials.OAuth2.ClientAssertionType != "urn:ietf:params:oauth:my-thing" { + t.Errorf("OAuth2.ClientAssertionType = %v, want = %v", client.config.Credentials.OAuth2.ClientAssertionType, "urn:ietf:params:oauth:my-thing") + } +} + +func TestBearerTokenHeaderAttachement(t *testing.T) { + conf := `{ + "name": "foo", + "url": "http://localhost", + "type":"oci", + "credentials": { + "bearer": { + "token":"user:password", + }, + } + }` + client, err := New([]byte(conf), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("New() = %v", err) + } + var buf bytes.Buffer + client.logger.SetLevel(logging.Debug) + client.logger.(*logging.StandardLogger).SetOutput(&buf) + + _, err = client.config.Credentials.Bearer.NewClient(client.config) + if err != nil { + t.Fatalf("Bearer Auth Plugin new client should not error = %q", err) + } + + err = client.config.Credentials.Bearer.Prepare(&http.Request{Response: &http.Response{StatusCode: http.StatusTemporaryRedirect}}) + if err != nil { + t.Fatalf("Bearer Auth Plugin should not error on redirect = %q ", err) + } + if !strings.Contains(buf.String(), "not attaching authorization header as the response contains a redirect") { + t.Fatalf("log debug output does not contain the message to confirm that the authorization header was not attached") + } + + err = client.config.Credentials.Bearer.Prepare(&http.Request{Response: &http.Response{StatusCode: http.StatusTemporaryRedirect}}) + if err != nil { + t.Fatalf("Bearer Auth Plugin should not error on redirect = %q ", err) + } + if !strings.Contains(buf.String(), "not attaching authorization header as the response contains a redirect") { + t.Fatalf("log debug output does not contain the message to confirm that the authorization header was not attached") + } + + err = client.config.Credentials.Bearer.Prepare(&http.Request{Header: http.Header{}}) + if err != nil { + t.Fatalf("Bearer Auth Plugin should not error on redirect = %q ", err) + } + if !strings.Contains(buf.String(), "attaching authorization header") { + t.Fatalf("log debug output should show that the authorization header is attached") + } +} diff --git a/third_party/opa/v1/plugins/rest/aws.go b/third_party/opa/v1/plugins/rest/aws.go new file mode 100644 index 000000000000..45c708ab80d4 --- /dev/null +++ b/third_party/opa/v1/plugins/rest/aws.go @@ -0,0 +1,1088 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package rest + +import ( + "bytes" + "context" + "crypto/sha1" + "encoding/hex" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "net/http" + "net/url" + "os" + "path" + "path/filepath" + "strings" + "time" + + "github.com/go-ini/ini" + "github.com/open-policy-agent/opa/internal/providers/aws" + "github.com/open-policy-agent/opa/v1/logging" +) + +const ( + // ref. https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html + ec2DefaultCredServicePath = "http://169.254.169.254/latest/meta-data/iam/security-credentials/" + + // ref. https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html + ec2DefaultTokenPath = "http://169.254.169.254/latest/api/token" + + // ref. https://docs.aws.amazon.com/AmazonECS/latest/userguide/task-iam-roles.html + ecsDefaultCredServicePath = "http://169.254.170.2" + ecsRelativePathEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" + ecsFullPathEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI" + ecsAuthorizationTokenEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN" + ecsAuthorizationTokenFileEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE" + + // ref. https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html + stsDefaultDomain = "amazonaws.com" + stsDefaultPath = "https://sts.%s" + stsRegionPath = "https://sts.%s.%s" + + // ref. https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html + accessKeyEnvVar = "AWS_ACCESS_KEY_ID" + secretKeyEnvVar = "AWS_SECRET_ACCESS_KEY" + securityTokenEnvVar = "AWS_SECURITY_TOKEN" + sessionTokenEnvVar = "AWS_SESSION_TOKEN" + awsRegionEnvVar = "AWS_REGION" + awsDomainEnvVar = "AWS_DOMAIN" + awsRoleArnEnvVar = "AWS_ROLE_ARN" + awsWebIdentityTokenFileEnvVar = "AWS_WEB_IDENTITY_TOKEN_FILE" + awsCredentialsFileEnvVar = "AWS_SHARED_CREDENTIALS_FILE" + awsConfigFileEnvVar = "AWS_CONFIG_FILE" + awsProfileEnvVar = "AWS_PROFILE" + + // ref. https://docs.aws.amazon.com/sdkref/latest/guide/settings-global.html + accessKeyGlobalSetting = "aws_access_key_id" + secretKeyGlobalSetting = "aws_secret_access_key" + securityTokenGlobalSetting = "aws_session_token" +) + +// awsCredentialService represents the interface for AWS credential providers +type awsCredentialService interface { + credentials(context.Context) (aws.Credentials, error) +} + +// awsEnvironmentCredentialService represents an static environment-variable credential provider for AWS +type awsEnvironmentCredentialService struct { + logger logging.Logger +} + +func (*awsEnvironmentCredentialService) credentials(context.Context) (aws.Credentials, error) { + var creds aws.Credentials + creds.AccessKey = os.Getenv(accessKeyEnvVar) + if creds.AccessKey == "" { + return creds, errors.New("no " + accessKeyEnvVar + " set in environment") + } + creds.SecretKey = os.Getenv(secretKeyEnvVar) + if creds.SecretKey == "" { + return creds, errors.New("no " + secretKeyEnvVar + " set in environment") + } + creds.RegionName = os.Getenv(awsRegionEnvVar) + if creds.RegionName == "" { + return creds, errors.New("no " + awsRegionEnvVar + " set in environment") + } + // SessionToken is required if using temporary ENV credentials from assumed IAM role + // Missing SessionToken results with 403 s3 error. + creds.SessionToken = os.Getenv(sessionTokenEnvVar) + if creds.SessionToken == "" { + // In case of missing SessionToken try to get SecurityToken + // AWS switched to use SessionToken, but SecurityToken was left for backward compatibility + creds.SessionToken = os.Getenv(securityTokenEnvVar) + } + + return creds, nil +} + +type ssoSessionDetails struct { + StartUrl string `json:"startUrl"` + Region string `json:"region"` + Name string + AccountID string + RoleName string + AccessToken string `json:"accessToken"` + ExpiresAt time.Time `json:"expiresAt"` + RegistrationExpiresAt time.Time `json:"registrationExpiresAt"` + RefreshToken string `json:"refreshToken"` + ClientId string `json:"clientId"` + ClientSecret string `json:"clientSecret"` +} + +type awsSSOCredentialsService struct { + Path string `json:"path,omitempty"` + SSOCachePath string `json:"cache_path,omitempty"` + + Profile string `json:"profile,omitempty"` + + logger logging.Logger + + creds aws.Credentials + + credentialsExpiresAt time.Time + + session *ssoSessionDetails +} + +func (cs *awsSSOCredentialsService) configPath() (string, error) { + if len(cs.Path) != 0 { + return cs.Path, nil + } + + if cs.Path = os.Getenv(awsConfigFileEnvVar); len(cs.Path) != 0 { + return cs.Path, nil + } + + homeDir, err := os.UserHomeDir() + if err != nil { + return "", fmt.Errorf("user home directory not found: %w", err) + } + + cs.Path = filepath.Join(homeDir, ".aws", "config") + + return cs.Path, nil +} +func (cs *awsSSOCredentialsService) ssoCachePath() (string, error) { + if len(cs.SSOCachePath) != 0 { + return cs.SSOCachePath, nil + } + + homeDir, err := os.UserHomeDir() + if err != nil { + return "", fmt.Errorf("user home directory not found: %w", err) + } + + cs.Path = filepath.Join(homeDir, ".aws", "sso", "cache") + + return cs.Path, nil +} + +func (cs *awsSSOCredentialsService) cacheKeyFileName() (string, error) { + + val := cs.session.StartUrl + if cs.session.Name != "" { + val = cs.session.Name + } + + hash := sha1.New() + hash.Write([]byte(val)) + cacheKey := hex.EncodeToString(hash.Sum(nil)) + + return cacheKey + ".json", nil +} + +func (cs *awsSSOCredentialsService) loadSSOCredentials() error { + ssoCachePath, err := cs.ssoCachePath() + if err != nil { + return fmt.Errorf("failed to get sso cache path: %w", err) + } + + cacheKeyFile, err := cs.cacheKeyFileName() + if err != nil { + return err + } + + cacheFile := path.Join(ssoCachePath, cacheKeyFile) + cache, err := os.ReadFile(cacheFile) + if err != nil { + return fmt.Errorf("failed to load cache file: %v", err) + } + + if err := json.Unmarshal(cache, &cs.session); err != nil { + return fmt.Errorf("failed to unmarshal cache file: %v", err) + } + + return nil + +} + +func (cs *awsSSOCredentialsService) loadSession() error { + configPath, err := cs.configPath() + if err != nil { + return fmt.Errorf("failed to get config path: %w", err) + } + config, err := ini.Load(configPath) + if err != nil { + return fmt.Errorf("failed to load config file: %w", err) + } + + section, err := config.GetSection("profile " + cs.Profile) + + if err != nil { + return fmt.Errorf("failed to find profile %s", cs.Profile) + } + + accountID, err := section.GetKey("sso_account_id") + if err != nil { + return fmt.Errorf("failed to find sso_account_id key in profile %s", cs.Profile) + } + + region, err := section.GetKey("region") + if err != nil { + return fmt.Errorf("failed to find region key in profile %s", cs.Profile) + } + + roleName, err := section.GetKey("sso_role_name") + if err != nil { + return fmt.Errorf("failed to find sso_role_name key in profile %s", cs.Profile) + } + + ssoSession, err := section.GetKey("sso_session") + if err != nil { + return fmt.Errorf("failed to find sso_session key in profile %s", cs.Profile) + } + + sessionName := ssoSession.Value() + + session, err := config.GetSection("sso-session " + sessionName) + if err != nil { + return fmt.Errorf("failed to find sso-session %s", sessionName) + } + + startUrl, err := session.GetKey("sso_start_url") + if err != nil { + return fmt.Errorf("failed to find sso_start_url key in sso-session %s", sessionName) + } + + cs.session = &ssoSessionDetails{ + StartUrl: startUrl.Value(), + Name: sessionName, + AccountID: accountID.Value(), + Region: region.Value(), + RoleName: roleName.Value(), + } + + return nil +} + +func (cs *awsSSOCredentialsService) tryRefreshToken() error { + // Check if refresh token is empty + if cs.session.RefreshToken == "" { + return errors.New("refresh token is empty") + } + + // Use the refresh token to get a new access token + // using the clientId, clientSecret and refreshToken from the loaded token + // return the new token + // if error, return error + + type refreshTokenRequest struct { + ClientId string `json:"clientId"` + ClientSecret string `json:"clientSecret"` + RefreshToken string `json:"refreshToken"` + GrantType string `json:"grantType"` + } + + data := refreshTokenRequest{ + ClientId: cs.session.ClientId, + ClientSecret: cs.session.ClientSecret, + RefreshToken: cs.session.RefreshToken, + GrantType: "refresh_token", + } + + body, err := json.Marshal(data) + if err != nil { + return fmt.Errorf("failed to marshal refresh token request: %v", err) + } + + endpoint := fmt.Sprintf("https://oidc.%s.amazonaws.com/token", cs.session.Region) + r, err := http.NewRequest("POST", endpoint, bytes.NewReader(body)) + if err != nil { + return fmt.Errorf("failed to create new request: %v", err) + } + + r.Header.Add("Content-Type", "application/json") + c := &http.Client{} + resp, err := c.Do(r) + if err != nil { + return fmt.Errorf("failed to do request: %v", err) + } + defer resp.Body.Close() + + type refreshTokenResponse struct { + AccessToken string `json:"accessToken"` + ExpiresIn int `json:"expiresIn"` + RefreshToken string `json:"refreshToken"` + } + + refreshedToken := refreshTokenResponse{} + + if err := json.NewDecoder(resp.Body).Decode(&refreshedToken); err != nil { + return fmt.Errorf("failed to decode response: %v", err) + } + + cs.session.AccessToken = refreshedToken.AccessToken + cs.session.ExpiresAt = time.Now().Add(time.Duration(refreshedToken.ExpiresIn) * time.Second) + cs.session.RefreshToken = refreshedToken.RefreshToken + + return nil +} + +func (cs *awsSSOCredentialsService) refreshCredentials() error { + url := fmt.Sprintf("https://portal.sso.%s.amazonaws.com/federation/credentials?account_id=%s&role_name=%s", cs.session.Region, cs.session.AccountID, cs.session.RoleName) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + + req.Header.Set("Authorization", "Bearer "+cs.session.AccessToken) + req.Header.Set("Content-Type", "application/json") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + type roleCredentials struct { + AccessKeyId string `json:"accessKeyId"` + SecretAccessKey string `json:"secretAccessKey"` + SessionToken string `json:"sessionToken"` + Expiration int64 `json:"expiration"` + } + type getRoleCredentialsResponse struct { + RoleCredentials roleCredentials `json:"roleCredentials"` + } + + var result getRoleCredentialsResponse + + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to decode response: %v", err) + } + + cs.creds = aws.Credentials{ + AccessKey: result.RoleCredentials.AccessKeyId, + SecretKey: result.RoleCredentials.SecretAccessKey, + SessionToken: result.RoleCredentials.SessionToken, + RegionName: cs.session.Region, + } + + cs.credentialsExpiresAt = time.Unix(result.RoleCredentials.Expiration, 0) + + return nil +} + +func (cs *awsSSOCredentialsService) loadProfile() { + if cs.Profile != "" { + return + } + + cs.Profile = os.Getenv(awsProfileEnvVar) + + if cs.Profile == "" { + cs.Profile = "default" + } + +} + +func (cs *awsSSOCredentialsService) init() error { + cs.loadProfile() + + if err := cs.loadSession(); err != nil { + return fmt.Errorf("failed to load session: %w", err) + } + + if err := cs.loadSSOCredentials(); err != nil { + return fmt.Errorf("failed to load SSO credentials: %w", err) + } + + // this enforces fetching credentials + cs.credentialsExpiresAt = time.Unix(0, 0) + return nil +} + +func (cs *awsSSOCredentialsService) credentials(context.Context) (aws.Credentials, error) { + if cs.session == nil { + if err := cs.init(); err != nil { + return aws.Credentials{}, err + } + } + + if cs.credentialsExpiresAt.Before(time.Now().Add(5 * time.Minute)) { + // Check if the sso token we have is still valid, + // if not, try to refresh it + if cs.session.ExpiresAt.Before(time.Now()) { + // we try and get a new token if we can + if cs.session.RegistrationExpiresAt.Before(time.Now()) { + return aws.Credentials{}, errors.New("cannot refresh token, registration expired") + } + + if err := cs.tryRefreshToken(); err != nil { + return aws.Credentials{}, fmt.Errorf("failed to refresh token: %w", err) + } + } + + if err := cs.refreshCredentials(); err != nil { + return aws.Credentials{}, fmt.Errorf("failed to refresh credentials: %w", err) + } + } + + return cs.creds, nil +} + +// awsProfileCredentialService represents a credential provider for AWS that extracts credentials from the AWS +// credentials file +type awsProfileCredentialService struct { + + // Path to the credentials file. + // + // If empty will look for "AWS_SHARED_CREDENTIALS_FILE" env variable. If the + // env value is empty will default to current user's home directory. + // Linux/OSX: "$HOME/.aws/credentials" + // Windows: "%USERPROFILE%\.aws\credentials" + Path string `json:"path,omitempty"` + + // AWS Profile to extract credentials from the credentials file. If empty + // will default to environment variable "AWS_PROFILE" or "default" if + // environment variable is also not set. + Profile string `json:"profile,omitempty"` + + RegionName string `json:"aws_region"` + + logger logging.Logger +} + +func (cs *awsProfileCredentialService) credentials(context.Context) (aws.Credentials, error) { + var creds aws.Credentials + + filename, err := cs.path() + if err != nil { + return creds, err + } + + cfg, err := ini.Load(filename) + if err != nil { + return creds, fmt.Errorf("failed to read credentials file: %v", err) + } + + profile, err := cfg.GetSection(cs.profile()) + if err != nil { + return creds, fmt.Errorf("failed to get profile: %v", err) + } + + creds.AccessKey = profile.Key(accessKeyGlobalSetting).String() + if creds.AccessKey == "" { + return creds, fmt.Errorf("profile \"%v\" in credentials file %v does not contain \"%v\"", cs.Profile, cs.Path, accessKeyGlobalSetting) + } + + creds.SecretKey = profile.Key(secretKeyGlobalSetting).String() + if creds.SecretKey == "" { + return creds, fmt.Errorf("profile \"%v\" in credentials file %v does not contain \"%v\"", cs.Profile, cs.Path, secretKeyGlobalSetting) + } + + creds.SessionToken = profile.Key(securityTokenGlobalSetting).String() // default to empty string + + if cs.RegionName == "" { + if cs.RegionName = os.Getenv(awsRegionEnvVar); cs.RegionName == "" { + return creds, errors.New("no " + awsRegionEnvVar + " set in environment or configuration") + } + } + creds.RegionName = cs.RegionName + + return creds, nil +} + +func (cs *awsProfileCredentialService) path() (string, error) { + if len(cs.Path) != 0 { + return cs.Path, nil + } + + if cs.Path = os.Getenv(awsCredentialsFileEnvVar); len(cs.Path) != 0 { + return cs.Path, nil + } + + homeDir, err := os.UserHomeDir() + if err != nil { + return "", fmt.Errorf("user home directory not found: %w", err) + } + + cs.Path = filepath.Join(homeDir, ".aws", "credentials") + + return cs.Path, nil +} + +func (cs *awsProfileCredentialService) profile() string { + if cs.Profile != "" { + return cs.Profile + } + + cs.Profile = os.Getenv(awsProfileEnvVar) + + if cs.Profile == "" { + cs.Profile = "default" + } + + return cs.Profile +} + +// awsMetadataCredentialService represents an EC2 metadata service credential provider for AWS +type awsMetadataCredentialService struct { + RoleName string `json:"iam_role,omitempty"` + RegionName string `json:"aws_region"` + creds aws.Credentials + expiration time.Time + credServicePath string + tokenPath string + logger logging.Logger +} + +func (cs *awsMetadataCredentialService) urlForMetadataService() (string, error) { + // override default path for testing + if cs.credServicePath != "" { + return cs.credServicePath + cs.RoleName, nil + } + // otherwise, normal flow + // if a role name is provided, look up via the EC2 credential service + if cs.RoleName != "" { + return ec2DefaultCredServicePath + cs.RoleName, nil + } + // otherwise, check environment to see if it looks like we're in an ECS + // container (with implied role association) + if isECS() { + // first check if the relative env var exists; if so we use that otherwise we + // use the "full" var + if _, relativeExists := os.LookupEnv(ecsRelativePathEnvVar); relativeExists { + return ecsDefaultCredServicePath + os.Getenv(ecsRelativePathEnvVar), nil + } + return os.Getenv(ecsFullPathEnvVar), nil + } + // if there's no role name and we don't appear to have a path to the + // ECS container service, then the configuration is invalid + return "", errors.New("metadata endpoint cannot be determined from settings and environment") +} + +func (cs *awsMetadataCredentialService) tokenRequest(ctx context.Context) (*http.Request, error) { + tokenURL := ec2DefaultTokenPath + if cs.tokenPath != "" { + // override for testing + tokenURL = cs.tokenPath + } + req, err := http.NewRequestWithContext(ctx, http.MethodPut, tokenURL, nil) + if err != nil { + return nil, err + } + + // we are going to use the token in the immediate future, so a long TTL is not necessary + req.Header.Set("X-aws-ec2-metadata-token-ttl-seconds", "60") + return req, nil +} + +func (cs *awsMetadataCredentialService) refreshFromService(ctx context.Context) error { + // define the expected JSON payload from the EC2 credential service + // ref. https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html + type metadataPayload struct { + Code string + AccessKeyID string `json:"AccessKeyId"` + SecretAccessKey string + Token string + Expiration time.Time + } + + // Short circuit if a reasonable amount of time until credential expiration remains + const tokenExpirationMargin = 5 * time.Minute + + if time.Now().Add(tokenExpirationMargin).Before(cs.expiration) { + cs.logger.Debug("Credentials previously obtained from metadata service still valid.") + return nil + } + + cs.logger.Debug("Obtaining credentials from metadata service.") + metaDataURL, err := cs.urlForMetadataService() + if err != nil { + // configuration issue or missing ECS environment + return err + } + + // construct an HTTP client with a reasonably short timeout + client := &http.Client{Timeout: time.Second * 10} + req, err := http.NewRequestWithContext(ctx, http.MethodGet, metaDataURL, nil) + if err != nil { + return errors.New("unable to construct metadata HTTP request: " + err.Error()) + } + + // if using the AWS_CONTAINER_CREDENTIALS_FULL_URI variable, we need to associate the token + // to the request + if _, useFullPath := os.LookupEnv(ecsFullPathEnvVar); useFullPath { + var token string + tokenFilePath, tokenFilePathExists := os.LookupEnv(ecsAuthorizationTokenFileEnvVar) + + if tokenFilePathExists { + tokenBytes, err := os.ReadFile(tokenFilePath) + if err != nil { + return errors.New("failed to read ECS metadata authorization token from file: " + err.Error()) + } + token = string(tokenBytes) + // If token doesn't exist as a file check if it exists as an environment variable + } else { + var tokenExists bool + token, tokenExists = os.LookupEnv(ecsAuthorizationTokenEnvVar) + if !tokenExists { + return errors.New("unable to get ECS metadata authorization token") + } + } + req.Header.Set("Authorization", token) + } + + // if in the EC2 environment, we will use IMDSv2, which requires a session cookie from a + // PUT request on the token endpoint before it will give the credentials, this provides + // protection from SSRF attacks + if !isECS() { + tokenReq, err := cs.tokenRequest(ctx) + if err != nil { + return errors.New("unable to construct metadata token HTTP request: " + err.Error()) + } + body, err := aws.DoRequestWithClient(tokenReq, client, "metadata token", cs.logger) + if err != nil { + return err + } + // token is the body of response; add to header of metadata request + req.Header.Set("X-aws-ec2-metadata-token", string(body)) + } + + body, err := aws.DoRequestWithClient(req, client, "metadata", cs.logger) + if err != nil { + return err + } + + var payload metadataPayload + err = json.Unmarshal(body, &payload) + if err != nil { + return errors.New("failed to parse credential response from metadata service: " + err.Error()) + } + + // Only the EC2 endpoint returns the "Code" element which indicates whether the query was + // successful; the ECS endpoint does not! Some other fields are missing in the ECS payload + // but we do not depend on them. + if cs.RoleName != "" && payload.Code != "Success" { + return errors.New("metadata service query did not succeed: " + payload.Code) + } + + cs.expiration = payload.Expiration + cs.creds.AccessKey = payload.AccessKeyID + cs.creds.SecretKey = payload.SecretAccessKey + cs.creds.SessionToken = payload.Token + cs.creds.RegionName = cs.RegionName + + return nil +} + +func (cs *awsMetadataCredentialService) credentials(ctx context.Context) (aws.Credentials, error) { + err := cs.refreshFromService(ctx) + if err != nil { + return cs.creds, err + } + return cs.creds, nil +} + +// awsAssumeRoleCredentialService represents a STS credential service that uses active IAM credentials +// to obtain temporary security credentials generated by AWS STS via AssumeRole API operation +type awsAssumeRoleCredentialService struct { + RegionName string `json:"aws_region"` + RoleArn string `json:"iam_role_arn"` + SessionName string `json:"session_name"` + Domain string `json:"aws_domain"` + AWSSigningPlugin *awsSigningAuthPlugin `json:"aws_signing,omitempty"` + stsURL string + creds aws.Credentials + expiration time.Time + logger logging.Logger +} + +func (cs *awsAssumeRoleCredentialService) populateFromEnv() error { + if cs.AWSSigningPlugin == nil { + return errors.New("a AWS signing plugin must be specified when AssumeRole credential provider is enabled") + } + + switch { + case cs.AWSSigningPlugin.AWSEnvironmentCredentials != nil: + case cs.AWSSigningPlugin.AWSProfileCredentials != nil: + case cs.AWSSigningPlugin.AWSMetadataCredentials != nil: + default: + return errors.New("unsupported AWS signing plugin with AssumeRole credential provider") + } + + if cs.AWSSigningPlugin.AWSMetadataCredentials != nil { + if cs.AWSSigningPlugin.AWSMetadataCredentials.RegionName == "" { + if cs.AWSSigningPlugin.AWSMetadataCredentials.RegionName = os.Getenv(awsRegionEnvVar); cs.AWSSigningPlugin.AWSMetadataCredentials.RegionName == "" { + return errors.New("no " + awsRegionEnvVar + " set in environment or configuration") + } + } + } + + if cs.AWSSigningPlugin.AWSSignatureVersion == "" { + cs.AWSSigningPlugin.AWSSignatureVersion = "4" + } + + if cs.Domain == "" { + cs.Domain = os.Getenv(awsDomainEnvVar) + } + + if cs.RegionName == "" { + if cs.RegionName = os.Getenv(awsRegionEnvVar); cs.RegionName == "" { + return errors.New("no " + awsRegionEnvVar + " set in environment or configuration") + } + } + + if cs.RoleArn == "" { + if cs.RoleArn = os.Getenv(awsRoleArnEnvVar); cs.RoleArn == "" { + return errors.New("no " + awsRoleArnEnvVar + " set in environment or configuration") + } + } + + return nil +} + +func (cs *awsAssumeRoleCredentialService) signingCredentials(ctx context.Context) (aws.Credentials, error) { + if cs.AWSSigningPlugin.AWSEnvironmentCredentials != nil { + cs.AWSSigningPlugin.AWSEnvironmentCredentials.logger = cs.logger + return cs.AWSSigningPlugin.AWSEnvironmentCredentials.credentials(ctx) + } + + if cs.AWSSigningPlugin.AWSProfileCredentials != nil { + cs.AWSSigningPlugin.AWSProfileCredentials.logger = cs.logger + return cs.AWSSigningPlugin.AWSProfileCredentials.credentials(ctx) + } + + cs.AWSSigningPlugin.AWSMetadataCredentials.logger = cs.logger + return cs.AWSSigningPlugin.AWSMetadataCredentials.credentials(ctx) +} + +func (cs *awsAssumeRoleCredentialService) stsPath() string { + return getSTSPath(cs.Domain, cs.stsURL, cs.RegionName) +} + +func (cs *awsAssumeRoleCredentialService) refreshFromService(ctx context.Context) error { + // define the expected JSON payload from the EC2 credential service + // ref. https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html + type responsePayload struct { + Result struct { + Credentials struct { + SessionToken string + SecretAccessKey string + Expiration time.Time + AccessKeyID string `xml:"AccessKeyId"` + } + } `xml:"AssumeRoleResult"` + } + + // short circuit if a reasonable amount of time until credential expiration remains + if time.Now().Add(time.Minute * 5).Before(cs.expiration) { + cs.logger.Debug("Credentials previously obtained from sts service still valid.") + return nil + } + + cs.logger.Debug("Obtaining credentials from sts for role %s.", cs.RoleArn) + + var sessionName string + if cs.SessionName == "" { + sessionName = "open-policy-agent" + } else { + sessionName = cs.SessionName + } + + queryVals := url.Values{ + "Action": []string{"AssumeRole"}, + "RoleSessionName": []string{sessionName}, + "RoleArn": []string{cs.RoleArn}, + "Version": []string{"2011-06-15"}, + } + stsRequestURL, _ := url.Parse(cs.stsPath()) + + // construct an HTTP client with a reasonably short timeout + client := &http.Client{Timeout: time.Second * 10} + req, err := http.NewRequestWithContext(ctx, http.MethodPost, stsRequestURL.String(), strings.NewReader(queryVals.Encode())) + if err != nil { + return errors.New("unable to construct STS HTTP request: " + err.Error()) + } + + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + // Note: Calls to AWS STS AssumeRole must be signed using the access key ID + // and secret access key + signingCreds, err := cs.signingCredentials(ctx) + if err != nil { + return err + } + + err = aws.SignRequest(req, "sts", signingCreds, time.Now(), cs.AWSSigningPlugin.AWSSignatureVersion) + if err != nil { + return err + } + + body, err := aws.DoRequestWithClient(req, client, "STS", cs.logger) + if err != nil { + return err + } + + var payload responsePayload + err = xml.Unmarshal(body, &payload) + if err != nil { + return errors.New("failed to parse credential response from STS service: " + err.Error()) + } + + cs.expiration = payload.Result.Credentials.Expiration + cs.creds.AccessKey = payload.Result.Credentials.AccessKeyID + cs.creds.SecretKey = payload.Result.Credentials.SecretAccessKey + cs.creds.SessionToken = payload.Result.Credentials.SessionToken + cs.creds.RegionName = cs.RegionName + + return nil +} + +func (cs *awsAssumeRoleCredentialService) credentials(ctx context.Context) (aws.Credentials, error) { + err := cs.refreshFromService(ctx) + if err != nil { + return cs.creds, err + } + return cs.creds, nil +} + +// awsWebIdentityCredentialService represents an STS WebIdentity credential services +type awsWebIdentityCredentialService struct { + RoleArn string + WebIdentityTokenFile string + RegionName string `json:"aws_region"` + SessionName string `json:"session_name"` + Domain string `json:"aws_domain"` + stsURL string + creds aws.Credentials + expiration time.Time + logger logging.Logger +} + +func (cs *awsWebIdentityCredentialService) populateFromEnv() error { + cs.RoleArn = os.Getenv(awsRoleArnEnvVar) + if cs.RoleArn == "" { + return errors.New("no " + awsRoleArnEnvVar + " set in environment") + } + cs.WebIdentityTokenFile = os.Getenv(awsWebIdentityTokenFileEnvVar) + if cs.WebIdentityTokenFile == "" { + return errors.New("no " + awsWebIdentityTokenFileEnvVar + " set in environment") + } + + if cs.Domain == "" { + cs.Domain = os.Getenv(awsDomainEnvVar) + } + + if cs.RegionName == "" { + if cs.RegionName = os.Getenv(awsRegionEnvVar); cs.RegionName == "" { + return errors.New("no " + awsRegionEnvVar + " set in environment or configuration") + } + } + return nil +} + +func (cs *awsWebIdentityCredentialService) stsPath() string { + return getSTSPath(cs.Domain, cs.stsURL, cs.RegionName) +} + +func (cs *awsWebIdentityCredentialService) refreshFromService(ctx context.Context) error { + // define the expected JSON payload from the EC2 credential service + // ref. https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html + type responsePayload struct { + Result struct { + Credentials struct { + SessionToken string + SecretAccessKey string + Expiration time.Time + AccessKeyID string `xml:"AccessKeyId"` + } + } `xml:"AssumeRoleWithWebIdentityResult"` + } + + // short circuit if a reasonable amount of time until credential expiration remains + if time.Now().Add(time.Minute * 5).Before(cs.expiration) { + cs.logger.Debug("Credentials previously obtained from sts service still valid.") + return nil + } + + cs.logger.Debug("Obtaining credentials from sts for role %s.", cs.RoleArn) + + var sessionName string + if cs.SessionName == "" { + sessionName = "open-policy-agent" + } else { + sessionName = cs.SessionName + } + + tokenData, err := os.ReadFile(cs.WebIdentityTokenFile) + if err != nil { + return errors.New("unable to read web token for sts HTTP request: " + err.Error()) + } + + token := string(tokenData) + + queryVals := url.Values{ + "Action": []string{"AssumeRoleWithWebIdentity"}, + "RoleSessionName": []string{sessionName}, + "RoleArn": []string{cs.RoleArn}, + "WebIdentityToken": []string{token}, + "Version": []string{"2011-06-15"}, + } + stsRequestURL, _ := url.Parse(cs.stsPath()) + + // construct an HTTP client with a reasonably short timeout + client := &http.Client{Timeout: time.Second * 10} + req, err := http.NewRequestWithContext(ctx, http.MethodPost, stsRequestURL.String(), strings.NewReader(queryVals.Encode())) + if err != nil { + return errors.New("unable to construct STS HTTP request: " + err.Error()) + } + + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + body, err := aws.DoRequestWithClient(req, client, "STS", cs.logger) + if err != nil { + return err + } + + var payload responsePayload + err = xml.Unmarshal(body, &payload) + if err != nil { + return errors.New("failed to parse credential response from STS service: " + err.Error()) + } + + cs.expiration = payload.Result.Credentials.Expiration + cs.creds.AccessKey = payload.Result.Credentials.AccessKeyID + cs.creds.SecretKey = payload.Result.Credentials.SecretAccessKey + cs.creds.SessionToken = payload.Result.Credentials.SessionToken + cs.creds.RegionName = cs.RegionName + + return nil +} + +func (cs *awsWebIdentityCredentialService) credentials(ctx context.Context) (aws.Credentials, error) { + err := cs.refreshFromService(ctx) + if err != nil { + return cs.creds, err + } + return cs.creds, nil +} + +func isECS() bool { + // the special relative path URI is set by the container agent in the ECS environment only + _, isECSRelative := os.LookupEnv(ecsRelativePathEnvVar) + _, isECSFull := os.LookupEnv(ecsFullPathEnvVar) + return isECSRelative || isECSFull +} + +// ecrAuthPlugin authorizes requests to AWS ECR. +type ecrAuthPlugin struct { + token aws.ECRAuthorizationToken + + // awsAuthPlugin is used to sign ecr authorization token requests. + awsAuthPlugin *awsSigningAuthPlugin + + // ecr represents the service we request tokens from. + ecr ecr + + logger logging.Logger +} + +type ecr interface { + GetAuthorizationToken(context.Context, aws.Credentials, string) (aws.ECRAuthorizationToken, error) +} + +func newECRAuthPlugin(ap *awsSigningAuthPlugin) *ecrAuthPlugin { + return &ecrAuthPlugin{ + awsAuthPlugin: ap, + ecr: aws.NewECR(ap.logger), + logger: ap.logger, + } +} + +// Prepare should be called with any request to AWS ECR. +// It takes care of retrieving an ECR authorization token to sign +// the request with. +func (ap *ecrAuthPlugin) Prepare(r *http.Request) error { + if !ap.token.IsValid() { + ap.logger.Debug("Refreshing ECR auth token") + if err := ap.refreshAuthorizationToken(r.Context()); err != nil { + return err + } + } + + ap.logger.Debug("Signing request with ECR authorization token") + + r.Header.Set("Authorization", "Basic "+ap.token.AuthorizationToken) + return nil +} + +func (ap *ecrAuthPlugin) refreshAuthorizationToken(ctx context.Context) error { + creds, err := ap.awsAuthPlugin.awsCredentialService().credentials(ctx) + if err != nil { + return fmt.Errorf("failed to get aws credentials: %w", err) + } + + token, err := ap.ecr.GetAuthorizationToken(ctx, creds, ap.awsAuthPlugin.AWSSignatureVersion) + if err != nil { + return fmt.Errorf("ecr: failed to get authorization token: %w", err) + } + + ap.token = token + return nil +} + +// awsKMSSignPlugin signs digests using AWS KMS. +type awsKMSSignPlugin struct { + + // awsAuthPlugin is used to sign kms sign requests. + awsAuthPlugin *awsSigningAuthPlugin + + // kms represents the service for signing digests. + kms awskms + + logger logging.Logger +} + +type awskms interface { + SignDigest(ctx context.Context, digest []byte, keyID string, signingAlgorithm string, creds aws.Credentials, signatureVersion string) (string, error) +} + +func newKMSSignPlugin(ap *awsSigningAuthPlugin) *awsKMSSignPlugin { + return &awsKMSSignPlugin{ + awsAuthPlugin: ap, + kms: aws.NewKMS(ap.logger), + logger: ap.logger, + } +} + +func (ap *awsKMSSignPlugin) SignDigest(ctx context.Context, digest []byte, keyID string, signingAlgorithm string) (string, error) { + creds, err := ap.awsAuthPlugin.awsCredentialService().credentials(ctx) + if err != nil { + return "", fmt.Errorf("failed to get aws credentials: %w", err) + } + + signature, err := ap.kms.SignDigest(ctx, digest, keyID, signingAlgorithm, creds, ap.awsAuthPlugin.AWSSignatureVersion) + if err != nil { + return "", fmt.Errorf("kms: failed to sign digest: %w", err) + } + + return signature, nil +} + +func getSTSPath(stsDomain, stsURL, regionName string) string { + var domain string + if stsDomain != "" { + domain = strings.ToLower(stsDomain) + } else { + domain = stsDefaultDomain + } + + var stsPath string + switch { + case stsURL != "": + stsPath = stsURL + case regionName != "": + stsPath = fmt.Sprintf(stsRegionPath, strings.ToLower(regionName), domain) + default: + stsPath = fmt.Sprintf(stsDefaultPath, domain) + } + return stsPath +} diff --git a/third_party/opa/v1/plugins/rest/aws_test.go b/third_party/opa/v1/plugins/rest/aws_test.go new file mode 100644 index 000000000000..79ff9bdf41b6 --- /dev/null +++ b/third_party/opa/v1/plugins/rest/aws_test.go @@ -0,0 +1,2020 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package rest + +import ( + "bytes" + "context" + "crypto/rand" + "crypto/sha1" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "slices" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/internal/providers/aws" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/util/test" +) + +// this is usually private; but we need it here +type metadataPayload struct { + Code string + AccessKeyID string `json:"AccessKeyId"` + SecretAccessKey string + Token string + Expiration time.Time +} + +// quick and dirty assertions +func assertEq(expected string, actual string, t *testing.T) { + t.Helper() + if actual != expected { + t.Error("expected: ", expected, " but got: ", actual) + } +} +func assertIn(candidates []string, actual string, t *testing.T) { + t.Helper() + if slices.Contains(candidates, actual) { + return + } + t.Error("value: '", actual, "' not found in: ", candidates) +} + +func assertErr(expected string, actual error, t *testing.T) { + t.Helper() + if !strings.Contains(actual.Error(), expected) { + t.Errorf("Expected error to contain %s, got: %s", expected, actual.Error()) + } +} + +func TestEnvironmentCredentialService(t *testing.T) { + cs := &awsEnvironmentCredentialService{} + + // wrong path: some required environment is missing + _, err := cs.credentials(context.Background()) + assertErr("no AWS_ACCESS_KEY_ID set in environment", err, t) + + t.Setenv("AWS_ACCESS_KEY_ID", "MYAWSACCESSKEYGOESHERE") + _, err = cs.credentials(context.Background()) + assertErr("no AWS_SECRET_ACCESS_KEY set in environment", err, t) + + t.Setenv("AWS_SECRET_ACCESS_KEY", "MYAWSSECRETACCESSKEYGOESHERE") + _, err = cs.credentials(context.Background()) + assertErr("no AWS_REGION set in environment", err, t) + + t.Setenv("AWS_REGION", "us-east-1") + + expectedCreds := aws.Credentials{ + AccessKey: "MYAWSACCESSKEYGOESHERE", + SecretKey: "MYAWSSECRETACCESSKEYGOESHERE", + RegionName: "us-east-1", + SessionToken: ""} + + testCases := []struct { + tokenEnv string + tokenValue string + }{ + // happy path: all required environment is present + {"", ""}, + // happy path: all required environment is present including security token + {"AWS_SECURITY_TOKEN", "MYSECURITYTOKENGOESHERE"}, + // happy path: all required environment is present including session token that is preferred over security token + {"AWS_SESSION_TOKEN", "MYSESSIONTOKENGOESHERE"}, + } + + for _, testCase := range testCases { + if testCase.tokenEnv != "" { + t.Setenv(testCase.tokenEnv, testCase.tokenValue) + } + expectedCreds.SessionToken = testCase.tokenValue + + envCreds, err := cs.credentials(context.Background()) + if err != nil { + t.Error("unexpected error: " + err.Error()) + } + + if envCreds != expectedCreds { + t.Error("expected: ", expectedCreds, " but got: ", envCreds) + } + } +} + +func TestProfileCredentialService(t *testing.T) { + + defaultKey := "AKIAIOSFODNN7EXAMPLE" + defaultSecret := "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + defaultSessionToken := "AQoEXAMPLEH4aoAH0gNCAPy" + defaultRegion := "us-west-2" + + fooKey := "AKIAI44QH8DHBEXAMPLE" + fooSecret := "je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY" + fooRegion := "us-east-1" + + config := fmt.Sprintf(` +[default] +aws_access_key_id=%v +aws_secret_access_key=%v +aws_session_token=%v + +[foo] +aws_access_key_id=%v +aws_secret_access_key=%v +`, defaultKey, defaultSecret, defaultSessionToken, fooKey, fooSecret) + + files := map[string]string{ + "example.ini": config, + } + + test.WithTempFS(files, func(path string) { + cfgPath := filepath.Join(path, "example.ini") + cs := &awsProfileCredentialService{ + Path: cfgPath, + Profile: "foo", + RegionName: fooRegion, + } + creds, err := cs.credentials(context.Background()) + if err != nil { + t.Fatal(err) + } + + expected := aws.Credentials{ + AccessKey: fooKey, + SecretKey: fooSecret, + RegionName: fooRegion, + SessionToken: "", + } + + if expected != creds { + t.Fatalf("Expected credentials %v but got %v", expected, creds) + } + + // "default" profile + cs = &awsProfileCredentialService{ + Path: cfgPath, + Profile: "", + RegionName: defaultRegion, + } + + creds, err = cs.credentials(context.Background()) + if err != nil { + t.Fatal(err) + } + + expected = aws.Credentials{ + AccessKey: defaultKey, + SecretKey: defaultSecret, + RegionName: defaultRegion, + SessionToken: defaultSessionToken, + } + + if expected != creds { + t.Fatalf("Expected credentials %v but got %v", expected, creds) + } + }) +} + +func TestProfileCredentialServiceWithEnvVars(t *testing.T) { + defaultKey := "AKIAIOSFODNN7EXAMPLE" + defaultSecret := "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + defaultSessionToken := "AQoEXAMPLEH4aoAH0gNCAPy" + defaultRegion := "us-east-1" + profile := "profileName" + config := fmt.Sprintf(` +[%s] +aws_access_key_id=%s +aws_secret_access_key=%s +aws_session_token=%s +`, profile, defaultKey, defaultSecret, defaultSessionToken) + + files := map[string]string{ + "example.ini": config, + } + + test.WithTempFS(files, func(path string) { + cfgPath := filepath.Join(path, "example.ini") + + t.Setenv(awsCredentialsFileEnvVar, cfgPath) + t.Setenv(awsProfileEnvVar, profile) + t.Setenv(awsRegionEnvVar, defaultRegion) + + cs := &awsProfileCredentialService{} + creds, err := cs.credentials(context.Background()) + if err != nil { + t.Fatal(err) + } + + expected := aws.Credentials{ + AccessKey: defaultKey, + SecretKey: defaultSecret, + RegionName: defaultRegion, + SessionToken: defaultSessionToken, + } + + if expected != creds { + t.Fatalf("Expected credentials %v but got %v", expected, creds) + } + }) +} + +func TestProfileCredentialServiceWithDefaultPath(t *testing.T) { + defaultKey := "AKIAIOSFODNN7EXAMPLE" + defaultSecret := "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + defaultSessionToken := "AQoEXAMPLEH4aoAH0gNCAPy" + defaultRegion := "us-west-22" + + config := fmt.Sprintf(` +[default] +aws_access_key_id=%s +aws_secret_access_key=%s +aws_session_token=%s +`, defaultKey, defaultSecret, defaultSessionToken) + + files := map[string]string{} + + test.WithTempFS(files, func(path string) { + + t.Setenv("USERPROFILE", path) + t.Setenv("HOME", path) + + cfgDir := filepath.Join(path, ".aws") + err := os.MkdirAll(cfgDir, os.ModePerm) + if err != nil { + t.Fatal(err) + } + + if err := os.WriteFile(filepath.Join(cfgDir, "credentials"), []byte(config), 0600); err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + cs := &awsProfileCredentialService{RegionName: defaultRegion} + creds, err := cs.credentials(context.Background()) + if err != nil { + t.Fatal(err) + } + + expected := aws.Credentials{ + AccessKey: defaultKey, + SecretKey: defaultSecret, + RegionName: defaultRegion, + SessionToken: defaultSessionToken, + } + + if expected != creds { + t.Fatalf("Expected credentials %v but got %v", expected, creds) + } + }) +} + +func TestProfileCredentialServiceWithError(t *testing.T) { + configNoAccessKeyID := ` +[default] +aws_secret_access_key = secret +` + + configNoSecret := ` +[default] +aws_access_key_id=accessKey +` + tests := []struct { + note string + config string + err string + }{ + { + note: "no aws_access_key_id", + config: configNoAccessKeyID, + err: "does not contain \"aws_access_key_id\"", + }, + { + note: "no aws_secret_access_key", + config: configNoSecret, + err: "does not contain \"aws_secret_access_key\"", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + + files := map[string]string{ + "example.ini": tc.config, + } + + test.WithTempFS(files, func(path string) { + cfgPath := filepath.Join(path, "example.ini") + cs := &awsProfileCredentialService{ + Path: cfgPath, + } + _, err := cs.credentials(context.Background()) + if err == nil { + t.Fatal("Expected error but got nil") + } + if !strings.Contains(err.Error(), tc.err) { + t.Errorf("expected error to contain %v, got %v", tc.err, err.Error()) + } + }) + }) + } +} + +func TestMetadataCredentialService(t *testing.T) { + ts := ec2CredTestServer{} + ts.start() + defer ts.stop() + + // wrong path: cred service path not well formed + cs := awsMetadataCredentialService{ + RoleName: "my_iam_role", + RegionName: "us-east-1", + credServicePath: "this is not a URL", // malformed + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + _, err := cs.credentials(context.Background()) + assertErr("unsupported protocol scheme \"\"", err, t) + + // wrong path: no role set but no ECS URI in environment + os.Unsetenv(ecsRelativePathEnvVar) + cs = awsMetadataCredentialService{ + RegionName: "us-east-1", + logger: logging.Get(), + } + _, err = cs.credentials(context.Background()) + assertErr("metadata endpoint cannot be determined from settings and environment", err, t) + + // wrong path: missing token + t.Setenv(ecsFullPathEnvVar, "fullPath") + os.Unsetenv(ecsAuthorizationTokenEnvVar) + _, err = cs.credentials(context.Background()) + assertErr("unable to get ECS metadata authorization token", err, t) + os.Unsetenv(ecsFullPathEnvVar) + + test.WithTempFS(nil, func(path string) { + // wrong path: bad file token + t.Setenv(ecsFullPathEnvVar, "fullPath") + t.Setenv(ecsAuthorizationTokenFileEnvVar, filepath.Join(path, "bad-file")) + _, err = cs.credentials(context.Background()) + assertErr("failed to read ECS metadata authorization token from file", err, t) + os.Unsetenv(ecsFullPathEnvVar) + os.Unsetenv(ecsAuthorizationTokenFileEnvVar) + }) + + // wrong path: creds not found + cs = awsMetadataCredentialService{ + RoleName: "not_my_iam_role", // not present + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + _, err = cs.credentials(context.Background()) + assertErr("metadata HTTP request returned unexpected status: 404 Not Found", err, t) + + // wrong path: malformed JSON body + cs = awsMetadataCredentialService{ + RoleName: "my_bad_iam_role", // not good + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + _, err = cs.credentials(context.Background()) + assertErr("failed to parse credential response from metadata service: invalid character 'T' looking for beginning of value", err, t) + + // wrong path: token service error + cs = awsMetadataCredentialService{ + RoleName: "my_iam_role", + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/missing_token", + logger: logging.Get(), + } // will 404 + _, err = cs.credentials(context.Background()) + assertErr("metadata token HTTP request returned unexpected status: 404 Not Found", err, t) + + // wrong path: token service returns bad token + cs = awsMetadataCredentialService{ + RoleName: "my_iam_role", + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/bad_token", + logger: logging.Get(), + } // not good + _, err = cs.credentials(context.Background()) + assertErr("metadata HTTP request returned unexpected status: 401 Unauthorized", err, t) + + // wrong path: bad result code from EC2 metadata service + ts.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Failure", // this is bad + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 30)} + cs = awsMetadataCredentialService{ + RoleName: "my_iam_role", + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + _, err = cs.credentials(context.Background()) + assertErr("metadata service query did not succeed: Failure", err, t) + + // happy path: base case + ts.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Success", + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 300)} + cs = awsMetadataCredentialService{ + RoleName: "my_iam_role", + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + var creds aws.Credentials + creds, err = cs.credentials(context.Background()) + if err != nil { + // Cannot proceed with test if unable to fetch credentials. + t.Fatal(err) + } + + assertEq(creds.AccessKey, ts.payload.AccessKeyID, t) + assertEq(creds.SecretKey, ts.payload.SecretAccessKey, t) + assertEq(creds.RegionName, cs.RegionName, t) + assertEq(creds.SessionToken, ts.payload.Token, t) + + // happy path: verify credentials are cached based on expiry + ts.payload.AccessKeyID = "ICHANGEDTHISBUTWEWONTSEEIT" + creds, err = cs.credentials(context.Background()) + if err != nil { + // Cannot proceed with test if unable to fetch credentials. + t.Fatal(err) + } + + assertEq(creds.AccessKey, "MYAWSACCESSKEYGOESHERE", t) // the original value + assertEq(creds.SecretKey, ts.payload.SecretAccessKey, t) + assertEq(creds.RegionName, cs.RegionName, t) + assertEq(creds.SessionToken, ts.payload.Token, t) + + // happy path: with refresh + // first time through + cs = awsMetadataCredentialService{ + RoleName: "my_iam_role", + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + ts.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Success", + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 2)} // short time + + creds, err = cs.credentials(context.Background()) + if err != nil { + // Cannot proceed with test if unable to fetch credentials. + t.Fatal(err) + } + + assertEq(creds.AccessKey, ts.payload.AccessKeyID, t) + assertEq(creds.SecretKey, ts.payload.SecretAccessKey, t) + assertEq(creds.RegionName, cs.RegionName, t) + assertEq(creds.SessionToken, ts.payload.Token, t) + + // second time through, with changes + ts.payload.AccessKeyID = "ICHANGEDTHISANDWEWILLSEEIT" + creds, err = cs.credentials(context.Background()) + if err != nil { + // Cannot proceed with test if unable to fetch credentials. + t.Fatal(err) + } + + assertEq(creds.AccessKey, ts.payload.AccessKeyID, t) // the new value + assertEq(creds.SecretKey, ts.payload.SecretAccessKey, t) + assertEq(creds.RegionName, cs.RegionName, t) + assertEq(creds.SessionToken, ts.payload.Token, t) + + // happy path: credentials fetched from full path var + cs = awsMetadataCredentialService{ + RegionName: "us-east-1", + credServicePath: "", // not set as we want to test env var resolution + logger: logging.Get(), + } + ts.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Success", + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 2)} // short time + t.Setenv(ecsFullPathEnvVar, ts.server.URL+"/fullPath") + t.Setenv(ecsAuthorizationTokenEnvVar, "THIS_IS_A_GOOD_TOKEN") + + creds, err = cs.credentials(context.Background()) + if err != nil { + // Cannot proceed with test if unable to fetch credentials. + t.Fatal(err) + } + + assertEq(creds.AccessKey, ts.payload.AccessKeyID, t) + assertEq(creds.SecretKey, ts.payload.SecretAccessKey, t) + assertEq(creds.RegionName, cs.RegionName, t) + assertEq(creds.SessionToken, ts.payload.Token, t) + os.Unsetenv(ecsFullPathEnvVar) + os.Unsetenv(ecsAuthorizationTokenEnvVar) + + // happy path: credentials fetched from full path var using token from filesystem + files := map[string]string{ + "good_token_file": "THIS_IS_A_GOOD_TOKEN", + } + test.WithTempFS(files, func(path string) { + // happy path: credentials fetched from full path var + cs = awsMetadataCredentialService{ + RegionName: "us-east-1", + credServicePath: "", // not set as we want to test env var resolution + logger: logging.Get(), + } + ts.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Success", + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 2)} // short time + t.Setenv(ecsFullPathEnvVar, ts.server.URL+"/fullPath") + t.Setenv(ecsAuthorizationTokenFileEnvVar, filepath.Join(path, "good_token_file")) + creds, err = cs.credentials(context.Background()) + if err != nil { + // Cannot proceed with test if unable to fetch credentials. + t.Fatal(err) + } + + assertEq(creds.AccessKey, ts.payload.AccessKeyID, t) + assertEq(creds.SecretKey, ts.payload.SecretAccessKey, t) + assertEq(creds.RegionName, cs.RegionName, t) + assertEq(creds.SessionToken, ts.payload.Token, t) + os.Unsetenv(ecsFullPathEnvVar) + os.Unsetenv(ecsAuthorizationTokenFileEnvVar) + }) +} + +func TestMetadataServiceErrorHandled(t *testing.T) { + ts := ec2CredTestServer{} + ts.start() + defer ts.stop() + + // wrong path: handle errors from credential service + cs := &awsMetadataCredentialService{ + RoleName: "not_my_iam_role", // not present + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + + _, err := cs.credentials(context.Background()) + assertErr("metadata HTTP request returned unexpected status: 404 Not Found", err, t) +} + +func TestV4Signing(t *testing.T) { + ts := ec2CredTestServer{} + ts.start() + defer ts.stop() + + // happy path: sign correctly + cs := &awsMetadataCredentialService{ + RoleName: "my_iam_role", // not present + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + ts.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Success", + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 2)} + req, _ := http.NewRequest("GET", "https://mybucket.s3.amazonaws.com/bundle.tar.gz", strings.NewReader("")) + + // force a non-random source so that we can predict the v4a signing key and, thus, signature + aws.SetRandomSource(test.NewZeroReader()) + defer func() { aws.SetRandomSource(rand.Reader) }() + + tests := []struct { + sigVersion string + expectedAuthorization []string + }{ + { + sigVersion: "4", + expectedAuthorization: []string{ + "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/us-east-1/s3/aws4_request," + + "SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token," + + "Signature=d3f0561abae5e35d9ee2c15e678bb7acacc4b4743707a8f7fbcbfdb519078990", + }, + }, + { + sigVersion: "4a", + expectedAuthorization: []string{ + // this signature is for go 1.24+ + "AWS4-ECDSA-P256-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/s3/aws4_request, " + + "SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-region-set;x-amz-security-token, " + + "Signature=3045022061d172d81cb118e1b1fbc321aa83b622eadbe8cc602d27d7e107cbf9caf42c09022100b3b0d9c52a382eea199b260f7aa69c658d63f78bd459da46ed94f30f66553389", + // this signature is for go 1.20+, which changed crypto/ecdsa so signatures differ from go 1.18 + "AWS4-ECDSA-P256-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/s3/aws4_request, " + + "SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-region-set;x-amz-security-token, " + + "Signature=3046022100b5b0a90b1739a67315b53b5ac93164e2a511723f76e29bf5396b7e55cb5db75a0221008702a757055fe397997d279fabfd73d162e4cae38111e806e87f4500076f3de0", + }, + }, + } + + for _, test := range tests { + t.Run(test.sigVersion, func(t *testing.T) { + creds, err := cs.credentials(context.Background()) + if err != nil { + t.Fatal("unexpected error getting credentials") + } + + if err := aws.SignRequest(req, "s3", creds, time.Unix(1556129697, 0), test.sigVersion); err != nil { + t.Fatal("unexpected error during signing", err) + } + + // expect mandatory headers + assertEq("mybucket.s3.amazonaws.com", req.Header.Get("Host"), t) + assertIn(test.expectedAuthorization, req.Header.Get("Authorization"), t) + assertEq("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + req.Header.Get("X-Amz-Content-Sha256"), t) + assertEq("20190424T181457Z", req.Header.Get("X-Amz-Date"), t) + assertEq("MYAWSSECURITYTOKENGOESHERE", req.Header.Get("X-Amz-Security-Token"), t) + }) + } +} + +func TestV4SigningUnsignedPayload(t *testing.T) { + ts := ec2CredTestServer{} + ts.start() + defer ts.stop() + + // happy path: sign correctly + cs := &awsMetadataCredentialService{ + RoleName: "my_iam_role", // not present + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + ts.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Success", + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 2)} + + // force a non-random source so that we can predict the v4a signing key and, thus, signature + aws.SetRandomSource(test.NewZeroReader()) + defer func() { aws.SetRandomSource(rand.Reader) }() + + tests := []struct { + disablePayloadSigning bool + expectedAuthorization []string + expectedShaHeaderVal string + }{ + { + disablePayloadSigning: true, + expectedAuthorization: []string{ + "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/us-east-1/s3/aws4_request," + + "SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token," + + "Signature=3682e55f6d86d3372003b3d28c74aa960f076d91fce833b129ae76415a12e5e4", + }, + expectedShaHeaderVal: "UNSIGNED-PAYLOAD", + }, + { + disablePayloadSigning: false, + expectedAuthorization: []string{ + "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/us-east-1/s3/aws4_request," + + "SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token," + + "Signature=d3f0561abae5e35d9ee2c15e678bb7acacc4b4743707a8f7fbcbfdb519078990", + }, + expectedShaHeaderVal: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + }, + } + for _, test := range tests { + creds, err := cs.credentials(context.Background()) + if err != nil { + t.Fatal("unexpected error getting credentials") + } + req, _ := http.NewRequest("GET", "https://mybucket.s3.amazonaws.com/bundle.tar.gz", strings.NewReader("")) + var body []byte + if req.Body == nil { + body = []byte("") + } else { + body, _ = io.ReadAll(req.Body) + req.Body = io.NopCloser(bytes.NewReader(body)) + } + + authHeader, awsHeaders := aws.SignV4(req.Header, req.Method, req.URL, body, "s3", creds, time.Unix(1556129697, 0).UTC(), test.disablePayloadSigning) + req.Header.Set("Authorization", authHeader) + for k, v := range awsHeaders { + req.Header.Add(k, v) + } + + // expect mandatory headers + assertEq("mybucket.s3.amazonaws.com", req.Header.Get("Host"), t) + assertIn(test.expectedAuthorization, req.Header.Get("Authorization"), t) + assertEq(test.expectedShaHeaderVal, req.Header.Get("X-Amz-Content-Sha256"), t) + assertEq("20190424T181457Z", req.Header.Get("X-Amz-Date"), t) + assertEq("MYAWSSECURITYTOKENGOESHERE", req.Header.Get("X-Amz-Security-Token"), t) + } +} + +func TestV4SigningForApiGateway(t *testing.T) { + ts := ec2CredTestServer{} + ts.start() + defer ts.stop() + + cs := &awsMetadataCredentialService{ + RoleName: "my_iam_role", // not present + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + ts.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Success", + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 2)} + req, _ := http.NewRequest("POST", "https://myrestapi.execute-api.us-east-1.amazonaws.com/prod/logs", + strings.NewReader("{ \"payload\": 42 }")) + req.Header.Set("Content-Type", "application/json") + + creds, err := cs.credentials(context.Background()) + if err != nil { + t.Fatal("unexpected error getting credentials") + } + + if err := aws.SignRequest(req, "execute-api", creds, time.Unix(1556129697, 0), "4"); err != nil { + t.Fatal("unexpected error during signing") + } + + // expect mandatory headers + assertEq(req.Header.Get("Host"), "myrestapi.execute-api.us-east-1.amazonaws.com", t) + assertEq(req.Header.Get("Authorization"), + "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/us-east-1/execute-api/aws4_request,"+ + "SignedHeaders=content-type;host;x-amz-date;x-amz-security-token,"+ + "Signature=c8ee72cc45050b255bcbf19defc693f7cd788959b5380fa0985de6e865635339", t) + // no content sha should be set, since this is specific to s3 and glacier + assertEq(req.Header.Get("X-Amz-Content-Sha256"), "", t) + assertEq(req.Header.Get("X-Amz-Date"), "20190424T181457Z", t) + assertEq(req.Header.Get("X-Amz-Security-Token"), "MYAWSSECURITYTOKENGOESHERE", t) +} + +func TestV4SigningOmitsIgnoredHeaders(t *testing.T) { + ts := ec2CredTestServer{} + ts.start() + defer ts.stop() + + cs := &awsMetadataCredentialService{ + RoleName: "my_iam_role", // not present + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + ts.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Success", + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 2)} + req, _ := http.NewRequest("POST", "https://myrestapi.execute-api.us-east-1.amazonaws.com/prod/logs", + strings.NewReader("{ \"payload\": 42 }")) + req.Header.Set("Content-Type", "application/json") + + // These are headers that should never be included in the signed headers + req.Header.Set("User-Agent", "Unit Tests!") + req.Header.Set("Authorization", "Auth header will be overwritten, and shouldn't be signed") + req.Header.Set("X-Amzn-Trace-Id", "Some trace id") + + // force a non-random source so that we can predict the v4a signing key and, thus, signature + aws.SetRandomSource(test.NewZeroReader()) + defer func() { aws.SetRandomSource(rand.Reader) }() + + tests := []struct { + sigVersion string + expectedAuthorization []string + }{ + { + sigVersion: "4", + expectedAuthorization: []string{"AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/us-east-1/execute-api/aws4_request," + + "SignedHeaders=content-type;host;x-amz-date;x-amz-security-token," + + "Signature=c8ee72cc45050b255bcbf19defc693f7cd788959b5380fa0985de6e865635339", + }, + }, + { + sigVersion: "4a", + expectedAuthorization: []string{ + // this signature is for go 1.20+, which changed crypto/ecdsa so signatures differ from go 1.18 + "AWS4-ECDSA-P256-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/execute-api/aws4_request, " + + "SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-region-set;x-amz-security-token, " + + "Signature=30440220030e9ef5a174354265b33cb57e43ed15cf418d90954d1c6061d99bca709ff0bd02204f11c90715131161bc65040dd11bc761471ccd230888750a12dbeaaf40541c20", + // this signature is for go 1.24+ + "AWS4-ECDSA-P256-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/execute-api/aws4_request, " + + "SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-region-set;x-amz-security-token, " + + "Signature=30450220652e9b5e04bb50cc27a599a05e4755719c25a6a93c4da71784ea681e951c5e9b022100c9e090654a5077946478fcd35b4b60d34899961b604ab57e6dd13ebcb49fc672", + }, + }, + } + + for _, test := range tests { + t.Run(test.sigVersion, func(t *testing.T) { + creds, err := cs.credentials(context.Background()) + if err != nil { + t.Fatal("unexpected error getting credentials") + } + + if err := aws.SignRequest(req, "execute-api", creds, time.Unix(1556129697, 0), test.sigVersion); err != nil { + t.Fatal("unexpected error during signing") + } + + // Check the signed headers doesn't include user-agent, authorization or x-amz-trace-id + assertIn(test.expectedAuthorization, req.Header.Get("Authorization"), t) + // The headers omitted from signing should still be present in the request + assertEq(req.Header.Get("User-Agent"), "Unit Tests!", t) + assertEq(req.Header.Get("X-Amzn-Trace-Id"), "Some trace id", t) + }) + } + +} + +func TestV4SigningCustomPort(t *testing.T) { + ts := ec2CredTestServer{} + ts.start() + defer ts.stop() + + cs := &awsMetadataCredentialService{ + RoleName: "my_iam_role", // not present + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + ts.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Success", + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 2)} + req, _ := http.NewRequest("GET", "https://custom.s3.server:9000/bundle.tar.gz", strings.NewReader("")) + + creds, err := cs.credentials(context.Background()) + if err != nil { + t.Fatal("unexpected error getting credentials") + } + + if err := aws.SignRequest(req, "s3", creds, time.Unix(1556129697, 0), "4"); err != nil { + t.Fatal("unexpected error during signing") + } + + // expect mandatory headers + assertEq(req.Header.Get("Host"), "custom.s3.server:9000", t) + assertEq(req.Header.Get("Authorization"), + "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/us-east-1/s3/aws4_request,"+ + "SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token,"+ + "Signature=765b67c6b136f99d9b769171c9939fc444021f7d17e4fbe6e1ab8b1926713c2b", t) + assertEq(req.Header.Get("X-Amz-Content-Sha256"), + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", t) + assertEq(req.Header.Get("X-Amz-Date"), "20190424T181457Z", t) + assertEq(req.Header.Get("X-Amz-Security-Token"), "MYAWSSECURITYTOKENGOESHERE", t) +} + +func TestV4SigningDoesNotMutateBody(t *testing.T) { + ts := ec2CredTestServer{} + ts.start() + defer ts.stop() + + cs := &awsMetadataCredentialService{ + RoleName: "my_iam_role", // not present + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + ts.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Success", + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 2)} + + // force a non-random source so that we can predict the v4a signing key and, thus, signature + aws.SetRandomSource(test.NewZeroReader()) + defer func() { aws.SetRandomSource(rand.Reader) }() + + tests := []struct { + sigVersion string + }{ + {sigVersion: "4"}, + {sigVersion: "4a"}, + } + + for _, test := range tests { + req, _ := http.NewRequest("POST", "https://myrestapi.execute-api.us-east-1.amazonaws.com/prod/logs", + strings.NewReader("{ \"payload\": 42 }")) + + creds, err := cs.credentials(context.Background()) + if err != nil { + t.Fatal("unexpected error getting credentials") + } + + if err := aws.SignRequest(req, "execute-api", creds, time.Unix(1556129697, 0), test.sigVersion); err != nil { + t.Fatal("unexpected error during signing") + } + + // Read the body and check that it was not mutated + body, _ := io.ReadAll(req.Body) + assertEq(string(body), "{ \"payload\": 42 }", t) + } +} + +func TestV4SigningWithMultiValueHeaders(t *testing.T) { + ts := ec2CredTestServer{} + ts.start() + defer ts.stop() + + cs := &awsMetadataCredentialService{ + RoleName: "my_iam_role", // not present + RegionName: "us-east-1", + credServicePath: ts.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: ts.server.URL + "/latest/api/token", + logger: logging.Get(), + } + ts.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Success", + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 2)} + req, _ := http.NewRequest("POST", "https://myrestapi.execute-api.us-east-1.amazonaws.com/prod/logs", + strings.NewReader("{ \"payload\": 42 }")) + req.Header.Add("Accept", "text/plain") + req.Header.Add("Accept", "text/html") + + // force a non-random source so that we can predict the v4a signing key and, thus, signature + // The mock rand reader returns an endless stream of zeros + aws.SetRandomSource(test.NewZeroReader()) + defer func() { aws.SetRandomSource(rand.Reader) }() + + tests := []struct { + sigVersion string + expectedAuthorization []string + }{ + { + sigVersion: "4", + expectedAuthorization: []string{ + "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/us-east-1/execute-api/aws4_request," + + "SignedHeaders=accept;host;x-amz-date;x-amz-security-token," + + "Signature=0237b0c789cad36212f0efba70c02549e1f659ab9caaca16423930cc7236c046", + }, + }, + { + sigVersion: "4a", + expectedAuthorization: []string{ + // this signature is for go 1.20+, which changed crypto/ecdsa so signatures differ from go 1.18 + "AWS4-ECDSA-P256-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/execute-api/aws4_request, " + + "SignedHeaders=accept;content-length;host;x-amz-content-sha256;x-amz-date;x-amz-region-set;x-amz-security-token, " + + "Signature=3045022047fc8a4a842fdaf8ca538580d8a7ef13da72b06f3b2953b2cb105c6ad86a9a41022100bb54877f27a58cf73a812af45bf82c94dd9f25d41a65645cb35f012cd1591589", + // this signature is for go 1.24+ + "AWS4-ECDSA-P256-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/execute-api/aws4_request, " + + "SignedHeaders=accept;content-length;host;x-amz-content-sha256;x-amz-date;x-amz-region-set;x-amz-security-token, " + + "Signature=304502204f38b116e49b743307141797f04c8610ed035c54d06acbeb4f33ab48c8ec578e022100bd5995cb1eaecb5aa8c3062dcfcae7af62b6f32cc578cd42268165e259be46a0", + }, + }, + } + + for _, test := range tests { + t.Run(test.sigVersion, func(t *testing.T) { + creds, err := cs.credentials(context.Background()) + if err != nil { + t.Fatal("unexpected error getting credentials") + } + + if err := aws.SignRequest(req, "execute-api", creds, time.Unix(1556129697, 0), test.sigVersion); err != nil { + t.Fatal("unexpected error during signing") + } + + if len(req.Header.Values("Authorization")) != 1 { + t.Fatal("Authorization header is multi-valued. This will break AWS v4 signing.") + } + // Check the signed headers includes our multi-value 'accept' header + assertIn(test.expectedAuthorization, req.Header.Get("Authorization"), t) + // The multi-value headers are preserved + assertEq("text/plain", req.Header.Values("Accept")[0], t) + assertEq("text/html", req.Header.Values("Accept")[1], t) + }) + } +} + +// simulate EC2 metadata service +type ec2CredTestServer struct { + server *httptest.Server + payload metadataPayload // must set before use +} + +func (t *ec2CredTestServer) handle(w http.ResponseWriter, r *http.Request) { + goodPath := "/latest/meta-data/iam/security-credentials/my_iam_role" + badPath := "/latest/meta-data/iam/security-credentials/my_bad_iam_role" + goodPathFull := "/fullPath" + + goodTokenPath := "/latest/api/token" + badTokenPath := "/latest/api/bad_token" + + tokenValue := "THIS_IS_A_GOOD_TOKEN" + jsonBytes, _ := json.Marshal(t.payload) + + switch r.URL.Path { + case goodTokenPath: + // a valid token + w.WriteHeader(200) + _, _ = w.Write([]byte(tokenValue)) + case badTokenPath: + // an invalid token + w.WriteHeader(200) + _, _ = w.Write([]byte("THIS_IS_A_BAD_TOKEN")) + case goodPath: + // validate token... + if r.Header.Get("X-aws-ec2-metadata-token") == tokenValue { + // a metadata response that's well-formed + w.WriteHeader(200) + _, _ = w.Write(jsonBytes) + } else { + // an unauthorized response + w.WriteHeader(401) + } + case badPath: + // a metadata response that's not well-formed + w.WriteHeader(200) + _, _ = w.Write([]byte("This isn't a JSON payload")) + case goodPathFull: + // validate token... + if r.Header.Get("Authorization") == tokenValue { + w.WriteHeader(200) + _, _ = w.Write(jsonBytes) + } else { + // AWS returns a 404 if the token is wrong + w.WriteHeader(404) + } + default: + // something else that we won't be able to find + w.WriteHeader(404) + } +} + +func (t *ec2CredTestServer) start() { + t.server = httptest.NewServer(http.HandlerFunc(t.handle)) +} + +func (t *ec2CredTestServer) stop() { + t.server.Close() +} + +func TestWebIdentityCredentialService(t *testing.T) { + t.Setenv("AWS_REGION", "us-west-1") + + testAccessKey := "ASgeIAIOSFODNN7EXAMPLE" + ts := stsTestServer{ + t: t, + accessKey: testAccessKey, + assumeRoleWithWebIdentity: true, + } + ts.start() + defer ts.stop() + cs := awsWebIdentityCredentialService{ + stsURL: ts.server.URL, + logger: logging.Get(), + } + + files := map[string]string{ + "good_token_file": "good-token", + "bad_token_file": "bad-token", + } + + test.WithTempFS(files, func(path string) { + goodTokenFile := filepath.Join(path, "good_token_file") + badTokenFile := filepath.Join(path, "bad_token_file") + + // wrong path: no AWS_ROLE_ARN set + err := cs.populateFromEnv() + assertErr("no AWS_ROLE_ARN set in environment", err, t) + t.Setenv("AWS_ROLE_ARN", "role:arn") + + // wrong path: no AWS_WEB_IDENTITY_TOKEN_FILE set + err = cs.populateFromEnv() + assertErr("no AWS_WEB_IDENTITY_TOKEN_FILE set in environment", err, t) + t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", "/nonsense") + + // happy path: both env vars set + err = cs.populateFromEnv() + if err != nil { + t.Fatalf("Error while getting env vars: %s", err) + } + + // wrong path: refresh with invalid web token file + err = cs.refreshFromService(context.Background()) + assertErr("unable to read web token for sts HTTP request: open /nonsense: no such file or directory", err, t) + + // wrong path: refresh with "bad token" + t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", badTokenFile) + _ = cs.populateFromEnv() + err = cs.refreshFromService(context.Background()) + assertErr("STS HTTP request returned unexpected status: 401 Unauthorized", err, t) + + // happy path: refresh with "good token" + t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", goodTokenFile) + _ = cs.populateFromEnv() + err = cs.refreshFromService(context.Background()) + if err != nil { + t.Fatalf("Unexpected err: %s", err) + } + + // happy path: refresh and get credentials + creds, _ := cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + + // happy path: refresh with session and get credentials + cs.expiration = time.Now() + cs.SessionName = "TEST_SESSION" + creds, _ = cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + + // happy path: don't refresh, but get credentials + ts.accessKey = "OTHERKEY" + creds, _ = cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + + // happy/wrong path: refresh with "bad token" but return previous credentials + t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", badTokenFile) + _ = cs.populateFromEnv() + cs.expiration = time.Now() + creds, err = cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + assertErr("STS HTTP request returned unexpected status: 401 Unauthorized", err, t) + + // wrong path: refresh with "bad token" but return previous credentials + t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", goodTokenFile) + t.Setenv("AWS_ROLE_ARN", "BrokenRole") + _ = cs.populateFromEnv() + cs.expiration = time.Now() + creds, err = cs.credentials(context.Background()) + assertErr("failed to parse credential response from STS service: EOF", err, t) + }) +} + +func TestAssumeRoleCredentialServiceUsingWrongSigningProvider(t *testing.T) { + t.Setenv("AWS_REGION", "us-west-1") + + testAccessKey := "ASgeIAIOSFODNN7EXAMPLE" + ts := stsTestServer{ + t: t, + accessKey: testAccessKey, + assumeRoleWithWebIdentity: false, + } + ts.start() + defer ts.stop() + cs := awsAssumeRoleCredentialService{ + stsURL: ts.server.URL, + logger: logging.Get(), + } + + // wrong path: no AWS signing plugin + err := cs.populateFromEnv() + assertErr("a AWS signing plugin must be specified when AssumeRole credential provider is enabled", err, t) + + // wrong path: unsupported AWS signing plugin + cs.AWSSigningPlugin = &awsSigningAuthPlugin{AWSWebIdentityCredentials: &awsWebIdentityCredentialService{}} + err = cs.populateFromEnv() + assertErr("unsupported AWS signing plugin with AssumeRole credential provider", err, t) +} + +func TestAssumeRoleCredentialServiceUsingEnvCredentialsProvider(t *testing.T) { + t.Setenv("AWS_REGION", "us-west-1") + + testAccessKey := "ASgeIAIOSFODNN7EXAMPLE" + ts := stsTestServer{ + t: t, + accessKey: testAccessKey, + assumeRoleWithWebIdentity: false, + } + ts.start() + defer ts.stop() + cs := awsAssumeRoleCredentialService{ + stsURL: ts.server.URL, + logger: logging.Get(), + AWSSigningPlugin: &awsSigningAuthPlugin{AWSEnvironmentCredentials: &awsEnvironmentCredentialService{}}, + } + + // wrong path: no AWS IAM Role ARN set in environment or config + err := cs.populateFromEnv() + assertErr("no AWS_ROLE_ARN set in environment or configuration", err, t) + t.Setenv("AWS_ROLE_ARN", "role:arn") + + // happy path: set AWS IAM Role ARN as env var + err = cs.populateFromEnv() + if err != nil { + t.Fatalf("Error while getting env vars: %s", err) + } + + // happy path: set AWS IAM Role ARN in config + os.Unsetenv("AWS_ROLE_ARN") + cs.RoleArn = "role:arn" + + err = cs.populateFromEnv() + if err != nil { + t.Fatalf("Error while getting env vars: %s", err) + } + + // wrong path: refresh and get credentials but signing credentials not set via env variables + _, err = cs.credentials(context.Background()) + assertErr("no AWS_ACCESS_KEY_ID set in environment", err, t) + + t.Setenv("AWS_ACCESS_KEY_ID", "MYAWSACCESSKEYGOESHERE") + + _, err = cs.credentials(context.Background()) + assertErr("no AWS_SECRET_ACCESS_KEY set in environment", err, t) + + t.Setenv("AWS_SECRET_ACCESS_KEY", "MYAWSSECRETACCESSKEYGOESHERE") + + // happy path: refresh and get credentials + creds, _ := cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + + // happy path: refresh with session and get credentials + cs.expiration = time.Now() + cs.SessionName = "TEST_SESSION" + creds, _ = cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + + // happy path: don't refresh as credentials not expired so STS not called + // verify existing credentials haven't changed + ts.accessKey = "OTHERKEY" + creds, _ = cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + + // happy path: refresh expired credentials + // verify new credentials are set + cs.expiration = time.Now() + creds, _ = cs.credentials(context.Background()) + assertEq(creds.AccessKey, ts.accessKey, t) +} + +func TestAssumeRoleCredentialServiceUsingProfileCredentialsProvider(t *testing.T) { + t.Setenv("AWS_REGION", "us-west-1") + + testAccessKey := "ASgeIAIOSFODNN7EXAMPLE" + ts := stsTestServer{ + t: t, + accessKey: testAccessKey, + assumeRoleWithWebIdentity: false, + } + ts.start() + defer ts.stop() + + defaultKey := "MYAWSACCESSKEYGOESHERE" + defaultSecret := "MYAWSSECRETACCESSKEYGOESHERE" + defaultSessionToken := "AQoEXAMPLEH4aoAH0gNCAPy" + + config := fmt.Sprintf(` +[foo] +aws_access_key_id=%v +aws_secret_access_key=%v +aws_session_token=%v +`, defaultKey, defaultSecret, defaultSessionToken) + + files := map[string]string{ + "example.ini": config, + } + + test.WithTempFS(files, func(path string) { + cfgPath := filepath.Join(path, "example.ini") + + cs := awsAssumeRoleCredentialService{ + stsURL: ts.server.URL, + logger: logging.Get(), + AWSSigningPlugin: &awsSigningAuthPlugin{AWSProfileCredentials: &awsProfileCredentialService{Path: cfgPath, Profile: "foo"}}, + } + + // wrong path: no AWS IAM Role ARN set in environment or config + err := cs.populateFromEnv() + assertErr("no AWS_ROLE_ARN set in environment or configuration", err, t) + t.Setenv("AWS_ROLE_ARN", "role:arn") + + // happy path: set AWS IAM Role ARN as env var + err = cs.populateFromEnv() + if err != nil { + t.Fatalf("Error while getting env vars: %s", err) + } + + // happy path: set AWS IAM Role ARN in config + os.Unsetenv("AWS_ROLE_ARN") + cs.RoleArn = "role:arn" + + err = cs.populateFromEnv() + if err != nil { + t.Fatalf("Error while getting env vars: %s", err) + } + + // happy path: refresh and get credentials + creds, _ := cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + + // happy path: refresh with session and get credentials + cs.expiration = time.Now() + cs.SessionName = "TEST_SESSION" + creds, _ = cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + + // happy path: don't refresh as credentials not expired so STS not called + // verify existing credentials haven't changed + ts.accessKey = "OTHERKEY" + creds, _ = cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + + // happy path: refresh expired credentials + // verify new credentials are set + cs.expiration = time.Now() + creds, _ = cs.credentials(context.Background()) + assertEq(creds.AccessKey, ts.accessKey, t) + }) +} + +func TestAssumeRoleCredentialServiceUsingMetadataCredentialsProvider(t *testing.T) { + t.Setenv("AWS_REGION", "us-west-1") + + testAccessKey := "ASgeIAIOSFODNN7EXAMPLE" + ts := stsTestServer{ + t: t, + accessKey: testAccessKey, + assumeRoleWithWebIdentity: false, + } + ts.start() + defer ts.stop() + + tsMetadata := ec2CredTestServer{} + tsMetadata.payload = metadataPayload{ + AccessKeyID: "MYAWSACCESSKEYGOESHERE", + SecretAccessKey: "MYAWSSECRETACCESSKEYGOESHERE", + Code: "Success", + Token: "MYAWSSECURITYTOKENGOESHERE", + Expiration: time.Now().UTC().Add(time.Minute * 300)} + tsMetadata.start() + defer tsMetadata.stop() + + cs := awsAssumeRoleCredentialService{ + stsURL: ts.server.URL, + logger: logging.Get(), + AWSSigningPlugin: &awsSigningAuthPlugin{AWSMetadataCredentials: &awsMetadataCredentialService{RoleName: "my_iam_role", credServicePath: tsMetadata.server.URL + "/latest/meta-data/iam/security-credentials/", + tokenPath: tsMetadata.server.URL + "/latest/api/token"}}, + } + + // wrong path: no AWS IAM Role ARN set in environment or config + err := cs.populateFromEnv() + assertErr("no AWS_ROLE_ARN set in environment or configuration", err, t) + t.Setenv("AWS_ROLE_ARN", "role:arn") + + // happy path: set AWS IAM Role ARN as env var + err = cs.populateFromEnv() + if err != nil { + t.Fatalf("Error while getting env vars: %s", err) + } + + // happy path: set AWS IAM Role ARN in config + os.Unsetenv("AWS_ROLE_ARN") + cs.RoleArn = "role:arn" + + err = cs.populateFromEnv() + if err != nil { + t.Fatalf("Error while getting env vars: %s", err) + } + + // happy path: refresh and get credentials + creds, _ := cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + + // happy path: refresh with session and get credentials + cs.expiration = time.Now() + cs.SessionName = "TEST_SESSION" + creds, _ = cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + + // happy path: don't refresh as credentials not expired so STS not called + // verify existing credentials haven't changed + ts.accessKey = "OTHERKEY" + creds, _ = cs.credentials(context.Background()) + assertEq(creds.AccessKey, testAccessKey, t) + + // happy path: refresh expired credentials + // verify new credentials are set + cs.expiration = time.Now() + creds, _ = cs.credentials(context.Background()) + assertEq(creds.AccessKey, ts.accessKey, t) +} + +func TestStsPath(t *testing.T) { + cs := awsWebIdentityCredentialService{} + + defaultPath := fmt.Sprintf(stsDefaultPath, stsDefaultDomain) + assertEq(defaultPath, cs.stsPath(), t) + + cs.RegionName = "us-east-2" + assertEq("https://sts.us-east-2.amazonaws.com", cs.stsPath(), t) + + cs.Domain = "example.com" + assertEq("https://sts.us-east-2.example.com", cs.stsPath(), t) + + cs.stsURL = "http://test.com" + assertEq("http://test.com", cs.stsPath(), t) +} + +func TestStsPathFromEnv(t *testing.T) { + t.Setenv(awsRoleArnEnvVar, "role:arn") + t.Setenv(awsWebIdentityTokenFileEnvVar, "/nonsense") + + tests := []struct { + note string + env map[string]string + cs awsWebIdentityCredentialService + want string + }{ + { + note: "region set in config", + cs: awsWebIdentityCredentialService{ + RegionName: "us-east-2", + }, + want: "https://sts.us-east-2.amazonaws.com", + }, + { + note: "region set in env", + env: map[string]string{ + awsRegionEnvVar: "us-east-1", + }, + want: "https://sts.us-east-1.amazonaws.com", + }, + { + note: "region set in env and config (config wins)", + env: map[string]string{ + awsRegionEnvVar: "us-east-1", + }, + cs: awsWebIdentityCredentialService{ + RegionName: "us-east-2", + }, + want: "https://sts.us-east-2.amazonaws.com", + }, + { + note: "domain set in config", + cs: awsWebIdentityCredentialService{ + RegionName: "us-east-2", + Domain: "foo.example.com", + }, + want: "https://sts.us-east-2.foo.example.com", + }, + { + note: "domain set in env", + env: map[string]string{ + awsDomainEnvVar: "bar.example.com", + }, + cs: awsWebIdentityCredentialService{ + RegionName: "us-east-2", // Region must always be set + }, + want: "https://sts.us-east-2.bar.example.com", + }, + { + note: "domain set in env and config (config wins)", + env: map[string]string{ + awsDomainEnvVar: "bar.example.com", + }, + cs: awsWebIdentityCredentialService{ + RegionName: "us-east-2", // Region must always be set + Domain: "foo.example.com", + }, + want: "https://sts.us-east-2.foo.example.com", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + for k, v := range tc.env { + t.Setenv(k, v) + } + if err := tc.cs.populateFromEnv(); err != nil { + t.Fatalf("Unexpected err: %s", err) + } + assertEq(tc.want, tc.cs.stsPath(), t) + }) + } +} + +// simulate AWS Security Token Service (AWS STS) +type stsTestServer struct { + t *testing.T + server *httptest.Server + accessKey string + assumeRoleWithWebIdentity bool +} + +func (t *stsTestServer) handle(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" || r.Method != http.MethodPost { + w.WriteHeader(404) + return + } + + if err := r.ParseForm(); err != nil { + w.WriteHeader(400) + return + } + + if r.PostForm.Get("Action") != "AssumeRoleWithWebIdentity" && r.PostForm.Get("Action") != "AssumeRole" { + w.WriteHeader(400) + return + } + + if r.PostForm.Get("RoleArn") == "BrokenRole" { + w.WriteHeader(200) + _, _ = w.Write([]byte("{}")) + return + } + + if t.assumeRoleWithWebIdentity { + token := r.PostForm.Get("WebIdentityToken") + if token != "good-token" { + w.WriteHeader(401) + return + } + w.WriteHeader(200) + } + + sessionName := r.PostForm.Get("RoleSessionName") + + var xmlResponse string + + if t.assumeRoleWithWebIdentity { + // Taken from STS docs: https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html + xmlResponse = ` + + amzn1.account.AF6RHO7KZU5XRVQJGXK6HB56KR2A + client.5498841531868486423.1548@apps.example.com + + arn:aws:sts::123456789012:assumed-role/FederatedWebIdentityRole/%[1]s + AROACLKWSDQRAOEXAMPLE:%[1]s + + + AQoDYXdzEE0a8ANXXXXXXXXNO1ewxE5TijQyp+IEXAMPLE + wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY + %s + %s + + www.amazon.com + + + ad4156e9-bce1-11e2-82e6-6b6efEXAMPLE + + ` + } else { + // Taken from STS docs: https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html + xmlResponse = ` + +DevUser123 + + arn:aws:sts::123456789012:assumed-role/demo/John + ARO123EXAMPLE123:%[1]s + + + + AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQW + LWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGd + QrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU + 9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz + +scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== + + + wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY + + %s + %s + +8 + + +c6104cbe-af31-11e0-8154-cbc7ccf896c7 + +` + } + + _, _ = w.Write(fmt.Appendf(nil, xmlResponse, sessionName, time.Now().Add(time.Hour).Format(time.RFC3339), t.accessKey)) +} + +func (t *stsTestServer) start() { + t.server = httptest.NewServer(http.HandlerFunc(t.handle)) +} + +func (t *stsTestServer) stop() { + t.server.Close() +} + +func TestECRAuthPluginFailsWithoutAWSAuthPlugins(t *testing.T) { + ap := newECRAuthPlugin(&awsSigningAuthPlugin{ + logger: logging.NewNoOpLogger(), + }) + + req := httptest.NewRequest("", "http://somewhere.com", nil) + + err := ap.Prepare(req) + if err == nil { + t.Error("ecrAuthPlugin.Prepare(): expected and error") + } +} + +func TestECRAuthPluginRequestsAuthorizationToken(t *testing.T) { + // Environment credentials to sign the ecr get authorization token request + t.Setenv(accessKeyEnvVar, "blablabla") + t.Setenv(secretKeyEnvVar, "tatata") + t.Setenv(awsRegionEnvVar, "us-east-1") + t.Setenv(sessionTokenEnvVar, "lalala") + + awsAuthPlugin := awsSigningAuthPlugin{ + logger: logging.NewNoOpLogger(), + AWSEnvironmentCredentials: &awsEnvironmentCredentialService{}, + } + + ap := newECRAuthPlugin(&awsAuthPlugin) + ap.ecr = &ecrStub{token: aws.ECRAuthorizationToken{ + AuthorizationToken: "secret", + }} + + req := httptest.NewRequest("", "http://somewhere.com", nil) + + if err := ap.Prepare(req); err != nil { + t.Errorf("ecrAuthPlugin.Prepare() = %q", err) + } + + got := req.Header.Get("Authorization") + want := "Basic secret" + if got != want { + t.Errorf("req.Header.Get(\"Authorization\") = %q, want %q", got, want) + } +} + +func TestECRAuthPluginRequestsRedirection(t *testing.T) { + // Environment credentials to sign the ecr get authorization token request + t.Setenv(accessKeyEnvVar, "blablabla") + t.Setenv(secretKeyEnvVar, "tatata") + t.Setenv(awsRegionEnvVar, "us-east-1") + t.Setenv(sessionTokenEnvVar, "lalala") + + ap := awsSigningAuthPlugin{ + logger: logging.NewNoOpLogger(), + AWSEnvironmentCredentials: &awsEnvironmentCredentialService{}, + host: "somewhere.com", + AWSService: "ecr", + } + + apECR := newECRAuthPlugin(&ap) + apECR.ecr = &ecrStub{token: aws.ECRAuthorizationToken{ + AuthorizationToken: "secret", + }} + + ap.ecrAuthPlugin = apECR + + // Request to the host specified in the plugin configuration + req := httptest.NewRequest("", "http://somewhere.com", nil) + + if err := ap.Prepare(req); err != nil { + t.Errorf("ecrAuthPlugin.Prepare() = %q", err) + } + + got := req.Header.Get("Authorization") + want := "Basic secret" + if got != want { + t.Errorf("req.Header.Get(\"Authorization\") = %q, want %q", got, want) + } + + // Redirection to another host + req = httptest.NewRequest("", "http://somewhere-else.com", nil) + + if err := ap.Prepare(req); err != nil { + t.Errorf("ecrAuthPlugin.Prepare() = %q", err) + } + + got = req.Header.Get("Authorization") + want = "" + if got != want { + t.Errorf("req.Header.Get(\"Authorization\") = %q, want %q", got, want) + } +} + +type ecrStub struct { + token aws.ECRAuthorizationToken +} + +func (es *ecrStub) GetAuthorizationToken(context.Context, aws.Credentials, string) (aws.ECRAuthorizationToken, error) { + return es.token, nil +} + +func TestECRAuthPluginReusesCachedToken(t *testing.T) { + logger := logging.NewNoOpLogger() + ap := ecrAuthPlugin{ + token: aws.ECRAuthorizationToken{ + AuthorizationToken: "secret", + ExpiresAt: time.Now().Add(time.Hour), + }, + awsAuthPlugin: &awsSigningAuthPlugin{ + logger: logger, + }, + logger: logger, + } + + req := httptest.NewRequest("", "http://somewhere.com", nil) + + if err := ap.Prepare(req); err != nil { + t.Errorf("ecrAuthPlugin.Prepare() = %q", err) + } + + got := req.Header.Get("Authorization") + want := "Basic secret" + if got != want { + t.Errorf("req.Header.Get(\"Authorization\") = %q, want %q", got, want) + } +} + +func TestSSOCredentialService(t *testing.T) { + // Create a temporary directory for test files + tempDir := t.TempDir() + + // Create test config file + configPath := filepath.Join(tempDir, "config") + configContent := ` +[profile test-profile] +sso_account_id = 123456789012 +sso_role_name = TestRole +sso_session = test-session +region = us-east-1 + +[sso-session test-session] +sso_start_url = https://test.awsapps.com/start +sso_region = us-east-1 +` + if err := os.WriteFile(configPath, []byte(configContent), 0644); err != nil { + t.Fatalf("Failed to write config file: %v", err) + } + + // Create test cache file + cachePath := filepath.Join(tempDir, "sso", "cache") + if err := os.MkdirAll(cachePath, 0755); err != nil { + t.Fatalf("Failed to create cache dir: %v", err) + } + + // Calculate the cache file name using the session name + sessionName := "test-session" + hash := sha1.New() + hash.Write([]byte(sessionName)) + cacheKey := fmt.Sprintf("%x.json", hash.Sum(nil)) + cacheFile := filepath.Join(cachePath, cacheKey) + + // Set up test times + futureTime := time.Now().Add(24 * time.Hour) + expiredTime := time.Now().Add(-1 * time.Hour) + + cacheContent := fmt.Sprintf(`{ + "startUrl": "https://test.awsapps.com/start", + "region": "us-east-1", + "accessToken": "test-access-token", + "expiresAt": %q, + "registrationExpiresAt": %q, + "refreshToken": "test-refresh-token", + "clientId": "test-client-id", + "clientSecret": "test-client-secret" + }`, futureTime.Format(time.RFC3339), futureTime.Format(time.RFC3339)) + + if err := os.WriteFile(cacheFile, []byte(cacheContent), 0644); err != nil { + t.Fatalf("Failed to write cache file: %v", err) + } + + tests := []struct { + name string + configPath string + profile string + cacheFile string + modifySession func(*ssoSessionDetails) + modifyCreds func(*aws.Credentials) + expectError bool + errorContains string + setupTest func() + }{ + { + name: "missing config path", + configPath: "/nonexistent/path", + profile: "test-profile", + expectError: true, + errorContains: "failed to load config file", + }, + { + name: "invalid profile", + configPath: configPath, + profile: "nonexistent-profile", + expectError: true, + errorContains: "failed to find profile", + }, + { + name: "successful credentials", + configPath: configPath, + profile: "test-profile", + cacheFile: cacheFile, + modifyCreds: func(creds *aws.Credentials) { + creds.AccessKey = "test-access-key" + creds.SecretKey = "test-secret-key" + creds.SessionToken = "test-session-token" + creds.RegionName = "us-east-1" + }, + }, + { + name: "expired access token", + configPath: configPath, + profile: "test-profile", + cacheFile: cacheFile, + modifySession: func(session *ssoSessionDetails) { + session.ExpiresAt = expiredTime + session.RegistrationExpiresAt = futureTime + }, + modifyCreds: func(creds *aws.Credentials) { + creds.AccessKey = "test-access-key" + creds.SecretKey = "test-secret-key" + creds.SessionToken = "test-session-token" + creds.RegionName = "us-east-1" + }, + }, + { + name: "expired registration", + configPath: configPath, + profile: "test-profile", + cacheFile: cacheFile, + modifySession: func(session *ssoSessionDetails) { + session.ExpiresAt = expiredTime + session.RegistrationExpiresAt = expiredTime + }, + expectError: true, + errorContains: "cannot refresh token, registration expired", + }, + { + name: "missing refresh token", + configPath: configPath, + profile: "test-profile", + cacheFile: cacheFile, + expectError: true, + errorContains: "failed to refresh token", + setupTest: func() { + // Create a session with expired access token and missing refresh token + session := &ssoSessionDetails{ + StartUrl: "https://test.awsapps.com/start", + Region: "us-east-1", + AccessToken: "test-access-token", + ExpiresAt: expiredTime, + RegistrationExpiresAt: futureTime, + ClientId: "test-client-id", + ClientSecret: "test-client-secret", + } + modifiedContent, err := json.Marshal(session) + if err != nil { + t.Fatalf("Failed to marshal session: %v", err) + } + if err := os.WriteFile(cacheFile, modifiedContent, 0644); err != nil { + t.Fatalf("Failed to write modified cache file: %v", err) + } + }, + }, + { + name: "expired credentials", + configPath: configPath, + profile: "test-profile", + cacheFile: cacheFile, + modifyCreds: func(creds *aws.Credentials) { + creds.AccessKey = "test-access-key" + creds.SecretKey = "test-secret-key" + creds.SessionToken = "test-session-token" + creds.RegionName = "us-east-1" + }, + }, + { + name: "invalid cache file", + configPath: configPath, + profile: "test-profile", + cacheFile: cacheFile, + expectError: true, + errorContains: "failed to unmarshal cache file", + setupTest: func() { + // Write invalid JSON to cache file before running the test + if err := os.WriteFile(cacheFile, []byte("invalid json"), 0644); err != nil { + t.Fatalf("Failed to write invalid cache file: %v", err) + } + }, + }, + { + name: "missing cache file", + configPath: configPath, + profile: "test-profile", + cacheFile: filepath.Join(cachePath, "nonexistent.json"), + expectError: true, + errorContains: "failed to load cache file", + setupTest: func() { + // Remove the cache file if it exists + if err := os.Remove(cacheFile); err != nil && !os.IsNotExist(err) { + t.Fatalf("Failed to remove cache file: %v", err) + } + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Reset files to initial state before each test + if err := os.WriteFile(configPath, []byte(configContent), 0644); err != nil { + t.Fatalf("Failed to reset config file: %v", err) + } + if err := os.WriteFile(cacheFile, []byte(cacheContent), 0644); err != nil { + t.Fatalf("Failed to reset cache file: %v", err) + } + + // Run any test-specific setup + if tc.setupTest != nil { + tc.setupTest() + } + + // Create service with test configuration + service := &awsSSOCredentialsService{ + Path: tc.configPath, + SSOCachePath: filepath.Dir(tc.cacheFile), + Profile: tc.profile, + logger: logging.NewNoOpLogger(), + } + + // Modify session details if needed + if tc.modifySession != nil { + session := &ssoSessionDetails{} + if err := json.Unmarshal([]byte(cacheContent), session); err != nil { + t.Fatalf("Failed to unmarshal session: %v", err) + } + tc.modifySession(session) + modifiedContent, err := json.Marshal(session) + if err != nil { + t.Fatalf("Failed to marshal modified session: %v", err) + } + if err := os.WriteFile(tc.cacheFile, modifiedContent, 0644); err != nil { + t.Fatalf("Failed to write modified cache file: %v", err) + } + } + + // Get credentials + creds, err := service.credentials(context.Background()) + + // Verify results + if tc.expectError { + if err == nil { + t.Fatal("expected error but got none") + } + if !strings.Contains(err.Error(), tc.errorContains) { + t.Errorf("expected error to contain %q, got %q", tc.errorContains, err.Error()) + } + } else { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if tc.modifyCreds != nil { + tc.modifyCreds(&creds) + } + if creds.AccessKey == "" { + t.Error("expected non-empty access key") + } + if creds.SecretKey == "" { + t.Error("expected non-empty secret key") + } + if creds.SessionToken == "" { + t.Error("expected non-empty session token") + } + if creds.RegionName != "us-east-1" { + t.Errorf("expected region us-east-1, got %q", creds.RegionName) + } + } + }) + } +} diff --git a/third_party/opa/v1/plugins/rest/azure.go b/third_party/opa/v1/plugins/rest/azure.go new file mode 100644 index 000000000000..9f7a164327f0 --- /dev/null +++ b/third_party/opa/v1/plugins/rest/azure.go @@ -0,0 +1,287 @@ +package rest + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "os" + "time" +) + +var ( + azureIMDSEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token" + defaultAPIVersion = "2018-02-01" + defaultResource = "https://storage.azure.com/" + timeout = 5 * time.Second + defaultAPIVersionForAppServiceMsi = "2019-08-01" + defaultKeyVaultAPIVersion = "7.4" +) + +// azureManagedIdentitiesToken holds a token for managed identities for Azure resources +type azureManagedIdentitiesToken struct { + AccessToken string `json:"access_token"` + ExpiresIn string `json:"expires_in"` + ExpiresOn string `json:"expires_on"` + NotBefore string `json:"not_before"` + Resource string `json:"resource"` + TokenType string `json:"token_type"` +} + +// azureManagedIdentitiesError represents an error fetching an azureManagedIdentitiesToken +type azureManagedIdentitiesError struct { + Err string `json:"error"` + Description string `json:"error_description"` + Endpoint string + StatusCode int +} + +func (e *azureManagedIdentitiesError) Error() string { + return fmt.Sprintf("%v %s retrieving azure token from %s: %s", e.StatusCode, e.Err, e.Endpoint, e.Description) +} + +// azureManagedIdentitiesAuthPlugin uses an azureManagedIdentitiesToken.AccessToken for bearer authorization +type azureManagedIdentitiesAuthPlugin struct { + Endpoint string `json:"endpoint"` + APIVersion string `json:"api_version"` + Resource string `json:"resource"` + ObjectID string `json:"object_id"` + ClientID string `json:"client_id"` + MiResID string `json:"mi_res_id"` + UseAppServiceMsi bool `json:"use_app_service_msi,omitempty"` +} + +func (ap *azureManagedIdentitiesAuthPlugin) setDefaults() { + if ap.Endpoint == "" { + identityEndpoint := os.Getenv("IDENTITY_ENDPOINT") + if identityEndpoint != "" { + ap.UseAppServiceMsi = true + ap.Endpoint = identityEndpoint + } else { + ap.Endpoint = azureIMDSEndpoint + } + } + + if ap.Resource == "" { + ap.Resource = defaultResource + } + + if ap.APIVersion == "" { + if ap.UseAppServiceMsi { + ap.APIVersion = defaultAPIVersionForAppServiceMsi + } else { + ap.APIVersion = defaultAPIVersion + } + } + +} + +func (ap *azureManagedIdentitiesAuthPlugin) NewClient(c Config) (*http.Client, error) { + if c.Type == "oci" { + return nil, errors.New("azure managed identities auth: OCI service not supported") + } + ap.setDefaults() + t, err := DefaultTLSConfig(c) + if err != nil { + return nil, err + } + + return DefaultRoundTripperClient(t, *c.ResponseHeaderTimeoutSeconds), nil +} + +func (ap *azureManagedIdentitiesAuthPlugin) Prepare(req *http.Request) error { + token, err := azureManagedIdentitiesTokenRequest( + ap.Endpoint, ap.APIVersion, ap.Resource, + ap.ObjectID, ap.ClientID, ap.MiResID, + ap.UseAppServiceMsi, + ) + if err != nil { + return err + } + + req.Header.Add("Authorization", "Bearer "+token.AccessToken) + return nil +} + +// azureManagedIdentitiesTokenRequest fetches an azureManagedIdentitiesToken +func azureManagedIdentitiesTokenRequest( + endpoint, apiVersion, resource, objectID, clientID, miResID string, + useAppServiceMsi bool, +) (azureManagedIdentitiesToken, error) { + var token azureManagedIdentitiesToken + e := buildAzureManagedIdentitiesRequestPath(endpoint, apiVersion, resource, objectID, clientID, miResID) + + request, err := http.NewRequest("GET", e, nil) + if err != nil { + return token, err + } + if useAppServiceMsi { + identityHeader := os.Getenv("IDENTITY_HEADER") + if identityHeader == "" { + return token, errors.New("azure managed identities auth: IDENTITY_HEADER env var not found") + } + request.Header.Add("x-identity-header", identityHeader) + } else { + request.Header.Add("Metadata", "true") + } + + httpClient := http.Client{Timeout: timeout} + response, err := httpClient.Do(request) + if err != nil { + return token, err + } + defer response.Body.Close() + + data, err := io.ReadAll(response.Body) + if err != nil { + return token, err + } + + if s := response.StatusCode; s != http.StatusOK { + var azureError azureManagedIdentitiesError + err = json.Unmarshal(data, &azureError) + if err != nil { + return token, err + } + + azureError.Endpoint = e + azureError.StatusCode = s + return token, &azureError + } + + err = json.Unmarshal(data, &token) + if err != nil { + return token, err + } + return token, nil +} + +// buildAzureManagedIdentitiesRequestPath constructs the request URL for an Azure managed identities token request +func buildAzureManagedIdentitiesRequestPath( + endpoint, apiVersion, resource, objectID, clientID, miResID string, +) string { + params := url.Values{ + "api-version": []string{apiVersion}, + "resource": []string{resource}, + } + + if objectID != "" { + params.Add("object_id", objectID) + } + + if clientID != "" { + params.Add("client_id", clientID) + } + + if miResID != "" { + params.Add("mi_res_id", miResID) + } + + return endpoint + "?" + params.Encode() +} + +type azureKeyVaultSignPlugin struct { + config azureKeyVaultConfig + tokener func() (string, error) +} + +func newKeyVaultSignPlugin(ap *azureManagedIdentitiesAuthPlugin, cfg *azureKeyVaultConfig) *azureKeyVaultSignPlugin { + resp := &azureKeyVaultSignPlugin{ + tokener: func() (string, error) { + resp, err := azureManagedIdentitiesTokenRequest( + ap.Endpoint, + ap.APIVersion, + cfg.URL.String(), + ap.ObjectID, + ap.ClientID, + ap.MiResID, + ap.UseAppServiceMsi) + if err != nil { + return "", err + } + return resp.AccessToken, nil + }, + config: *cfg, + } + return resp +} + +func (akv *azureKeyVaultSignPlugin) setDefaults() { + if akv.config.APIVersion == "" { + akv.config.APIVersion = defaultKeyVaultAPIVersion + } +} + +type kvRequest struct { + Alg string `json:"alg"` + Value string `json:"value"` +} + +type kvResponse struct { + KID string `json:"kid"` + Value string `json:"value"` +} + +// SignDigest() uses the Microsoft keyvault rest api to sign a byte digest +// https://learn.microsoft.com/en-us/rest/api/keyvault/keys/sign/sign +func (ap *azureKeyVaultSignPlugin) SignDigest(ctx context.Context, digest []byte) (string, error) { + tkn, err := ap.tokener() + if err != nil { + return "", err + } + if ap.config.URL.Host == "" { + return "", errors.New("keyvault host not set") + } + + signingURL := ap.config.URL.JoinPath("keys", ap.config.Key, ap.config.KeyVersion, "sign") + q := signingURL.Query() + q.Set("api-version", ap.config.APIVersion) + signingURL.RawQuery = q.Encode() + reqBody, err := json.Marshal(kvRequest{ + Alg: ap.config.Alg, + Value: base64.StdEncoding.EncodeToString(digest)}) + if err != nil { + return "", err + } + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, signingURL.String(), bytes.NewBuffer(reqBody)) + if err != nil { + return "", err + } + + req.Header.Add("Authorization", "Bearer "+tkn) + req.Header.Add("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + + if resp.StatusCode != http.StatusOK { + if resp.Body != nil { + defer resp.Body.Close() + b, _ := io.ReadAll(resp.Body) + return "", fmt.Errorf("non 200 status code, got: %d. Body: %v", resp.StatusCode, string(b)) + } + return "", fmt.Errorf("non 200 status code from keyvault sign, got: %d", resp.StatusCode) + } + defer resp.Body.Close() + + respBytes, err := io.ReadAll(resp.Body) + if err != nil { + return "", errors.New("failed to read keyvault response body") + } + + var res kvResponse + err = json.Unmarshal(respBytes, &res) + if err != nil { + return "", fmt.Errorf("no valid keyvault response, got: %v", string(respBytes)) + } + + return res.Value, nil +} diff --git a/third_party/opa/v1/plugins/rest/azure_test.go b/third_party/opa/v1/plugins/rest/azure_test.go new file mode 100644 index 000000000000..3752d5b673c3 --- /dev/null +++ b/third_party/opa/v1/plugins/rest/azure_test.go @@ -0,0 +1,242 @@ +package rest + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "reflect" + "testing" + + "github.com/open-policy-agent/opa/v1/keys" +) + +func assertStringsEqual(t *testing.T, expected string, actual string, label string) { + t.Helper() + if actual != expected { + t.Errorf("%s: expected %s, got %s", label, expected, actual) + } +} + +func assertParamsEqual(t *testing.T, expected url.Values, actual url.Values, label string) { + t.Helper() + if !reflect.DeepEqual(expected, actual) { + t.Errorf("%s: expected %s, got %s", label, expected.Encode(), actual.Encode()) + } +} +func TestAzureManagedIdentitiesAuthPlugin_NewClient(t *testing.T) { + tests := []struct { + label string + endpoint string + apiVersion string + resource string + objectID string + clientID string + miResID string + }{ + { + "test all defaults", + "", "", "", "", "", "", + }, + { + "test no defaults", + "some_endpoint", "some_version", "some_resource", "some_oid", "some_cid", "some_miresid", + }, + } + + nonEmptyString := func(value string, defaultValue string) string { + if value == "" { + return defaultValue + } + return value + } + + for _, tt := range tests { + config := generateConfigString(tt.endpoint, tt.apiVersion, tt.resource, tt.objectID, tt.clientID, tt.miResID) + + client, err := New([]byte(config), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + ap := client.config.Credentials.AzureManagedIdentity + _, err = ap.NewClient(client.config) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // We test that default values are set correctly in the azureManagedIdentitiesAuthPlugin + // Note that there is significant overlap between TestAzureManagedIdentitiesAuthPlugin_NewClient and TestAzureManagedIdentitiesAuthPlugin + // This is because the latter cannot test default endpoint setting, which we do here + assertStringsEqual(t, nonEmptyString(tt.endpoint, azureIMDSEndpoint), ap.Endpoint, tt.label) + assertStringsEqual(t, nonEmptyString(tt.apiVersion, defaultAPIVersion), ap.APIVersion, tt.label) + assertStringsEqual(t, nonEmptyString(tt.resource, defaultResource), ap.Resource, tt.label) + assertStringsEqual(t, tt.objectID, ap.ObjectID, tt.label) + assertStringsEqual(t, tt.clientID, ap.ClientID, tt.label) + assertStringsEqual(t, tt.miResID, ap.MiResID, tt.label) + } +} + +func TestAzureManagedIdentitiesAuthPluginForAppService_NewClient(t *testing.T) { + tests := []struct { + label string + endpoint string + apiVersion string + resource string + objectID string + clientID string + miResID string + }{ + { + "test all defaults", + "", "", "", "", "", "", + }, + { + "test no defaults", + "some_endpoint", "some_version", "some_resource", "some_oid", "some_cid", "some_miresid", + }, + } + + nonEmptyString := func(value string, defaultValue string) string { + if value == "" { + return defaultValue + } + return value + } + + defaultIdentityEndpoint := "http://localhost:42356/msi/token" + defaultIdentityHeader := "IdentityHeader" + t.Setenv("IDENTITY_ENDPOINT", defaultIdentityEndpoint) + t.Setenv("IDENTITY_HEADER", defaultIdentityHeader) + + for _, tt := range tests { + config := generateConfigString(tt.endpoint, tt.apiVersion, tt.resource, tt.objectID, tt.clientID, tt.miResID) + + client, err := New([]byte(config), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + ap := client.config.Credentials.AzureManagedIdentity + _, err = ap.NewClient(client.config) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // We test that default values are set correctly in the azureManagedIdentitiesAuthPlugin + // Note that there is significant overlap between TestAzureManagedIdentitiesAuthPlugin_NewClient and TestAzureManagedIdentitiesAuthPlugin + // This is because the latter cannot test default endpoint setting, which we do here + assertStringsEqual(t, nonEmptyString(tt.endpoint, defaultIdentityEndpoint), ap.Endpoint, tt.label) + assertStringsEqual(t, nonEmptyString(tt.apiVersion, defaultAPIVersionForAppServiceMsi), ap.APIVersion, tt.label) + assertStringsEqual(t, nonEmptyString(tt.resource, defaultResource), ap.Resource, tt.label) + assertStringsEqual(t, tt.objectID, ap.ObjectID, tt.label) + assertStringsEqual(t, tt.clientID, ap.ClientID, tt.label) + assertStringsEqual(t, tt.miResID, ap.MiResID, tt.label) + } +} + +func TestAzureManagedIdentitiesAuthPlugin(t *testing.T) { + tests := []struct { + label string + apiVersion string + resource string + objectID string + clientID string + miResID string + expectedParams url.Values + }{ + { + "test all defaults", + "", "", "", "", "", + url.Values{ + "api-version": []string{"2018-02-01"}, + "resource": []string{"https://storage.azure.com/"}, + }, + }, + { + "test custom api version", + "2021-02-01", "", "", "", "", + url.Values{ + "api-version": []string{"2021-02-01"}, + "resource": []string{"https://storage.azure.com/"}, + }, + }, + { + "test custom resource", + "", "https://management.azure.com/", "", "", "", + url.Values{ + "api-version": []string{"2018-02-01"}, + "resource": []string{"https://management.azure.com/"}, + }, + }, + { + "test custom IDs", + "", "", "oid", "cid", "mrid", + url.Values{ + "api-version": []string{"2018-02-01"}, + "resource": []string{"https://storage.azure.com/"}, + "object_id": []string{"oid"}, + "client_id": []string{"cid"}, + "mi_res_id": []string{"mrid"}, + }, + }, + } + + for _, tt := range tests { + ts := azureManagedIdentitiesTestServer{ + t: t, + label: tt.label, + expectedParams: tt.expectedParams, + } + ts.start() + + config := generateConfigString(ts.server.URL, tt.apiVersion, tt.resource, tt.objectID, tt.clientID, tt.miResID) + + client, err := New([]byte(config), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + ctx := context.Background() + _, _ = client.Do(ctx, "GET", "test") + ts.stop() + } +} + +type azureManagedIdentitiesTestServer struct { + t *testing.T + server *httptest.Server + label string + expectedParams url.Values +} + +func (t *azureManagedIdentitiesTestServer) handle(_ http.ResponseWriter, r *http.Request) { + assertParamsEqual(t.t, t.expectedParams, r.URL.Query(), t.label) +} + +func (t *azureManagedIdentitiesTestServer) start() { + t.server = httptest.NewServer(http.HandlerFunc(t.handle)) +} + +func (t *azureManagedIdentitiesTestServer) stop() { + t.server.Close() +} + +func generateConfigString(endpoint, apiVersion, resource, objectID, clientID, miResID string) string { + return fmt.Sprintf(`{ + "name": "name", + "url": "url", + "allow_insecure_tls": true, + "credentials": { + "azure_managed_identity": { + "endpoint": "%s", + "api_version": "%s", + "resource": "%s", + "object_id": "%s", + "client_id": "%s", + "mi_res_id": "%s" + } + } + }`, endpoint, apiVersion, resource, objectID, clientID, miResID) +} diff --git a/third_party/opa/v1/plugins/rest/gcp.go b/third_party/opa/v1/plugins/rest/gcp.go new file mode 100644 index 000000000000..c717105c7f3e --- /dev/null +++ b/third_party/opa/v1/plugins/rest/gcp.go @@ -0,0 +1,173 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package rest + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strings" + "time" +) + +var ( + defaultGCPMetadataEndpoint = "http://metadata.google.internal" + defaultAccessTokenPath = "/computeMetadata/v1/instance/service-accounts/default/token" + defaultIdentityTokenPath = "/computeMetadata/v1/instance/service-accounts/default/identity" +) + +// AccessToken holds a GCP access token. +type AccessToken struct { + AccessToken string `json:"access_token"` + ExpiresIn int64 `json:"expires_in"` + TokenType string `json:"token_type"` +} + +type gcpMetadataError struct { + err error + endpoint string + statusCode int +} + +func (e *gcpMetadataError) Error() string { + return fmt.Sprintf("error retrieving gcp ID token from %s %d: %v", e.endpoint, e.statusCode, e.err) +} + +func (e *gcpMetadataError) Unwrap() error { return e.err } + +var ( + errGCPMetadataNotFound = errors.New("not found") + errGCPMetadataInvalidRequest = errors.New("invalid request") + errGCPMetadataUnexpected = errors.New("unexpected error") +) + +// gcpMetadataAuthPlugin represents authentication via GCP metadata service. +type gcpMetadataAuthPlugin struct { + AccessTokenPath string `json:"access_token_path"` + Audience string `json:"audience"` + Endpoint string `json:"endpoint"` + IdentityTokenPath string `json:"identity_token_path"` + Scopes []string `json:"scopes"` +} + +func (ap *gcpMetadataAuthPlugin) NewClient(c Config) (*http.Client, error) { + if ap.Audience == "" && len(ap.Scopes) == 0 { + return nil, errors.New("audience or scopes is required when gcp metadata is enabled") + } + + if ap.Audience != "" && len(ap.Scopes) > 0 { + return nil, errors.New("either audience or scopes can be set, not both, when gcp metadata is enabled") + } + + if ap.Endpoint == "" { + ap.Endpoint = defaultGCPMetadataEndpoint + } + + if ap.AccessTokenPath == "" { + ap.AccessTokenPath = defaultAccessTokenPath + } + + if ap.IdentityTokenPath == "" { + ap.IdentityTokenPath = defaultIdentityTokenPath + } + + t, err := DefaultTLSConfig(c) + if err != nil { + return nil, err + } + + return DefaultRoundTripperClient(t, *c.ResponseHeaderTimeoutSeconds), nil +} + +func (ap *gcpMetadataAuthPlugin) Prepare(req *http.Request) error { + var err error + var token string + + if ap.Audience != "" { + token, err = identityTokenFromMetadataService(ap.Endpoint, ap.IdentityTokenPath, ap.Audience) + if err != nil { + return fmt.Errorf("error retrieving identity token from gcp metadata service: %w", err) + } + } + + if len(ap.Scopes) != 0 { + token, err = accessTokenFromMetadataService(ap.Endpoint, ap.AccessTokenPath, ap.Scopes) + if err != nil { + return fmt.Errorf("error retrieving access token from gcp metadata service: %w", err) + } + } + + req.Header.Add("Authorization", fmt.Sprintf("Bearer %v", token)) + return nil +} + +// accessTokenFromMetadataService returns an access token based on the scopes. +func accessTokenFromMetadataService(endpoint, path string, scopes []string) (string, error) { + s := strings.Join(scopes, ",") + + e := fmt.Sprintf("%s%s?scopes=%s", endpoint, path, s) + + data, err := gcpMetadataServiceRequest(e) + if err != nil { + return "", err + } + + var accessToken AccessToken + err = json.Unmarshal(data, &accessToken) + if err != nil { + return "", err + } + + return accessToken.AccessToken, nil +} + +// identityTokenFromMetadataService returns an identity token based on the audience. +func identityTokenFromMetadataService(endpoint, path, audience string) (string, error) { + e := fmt.Sprintf("%s%s?audience=%s", endpoint, path, audience) + + data, err := gcpMetadataServiceRequest(e) + if err != nil { + return "", err + } + return string(data), nil +} + +func gcpMetadataServiceRequest(endpoint string) ([]byte, error) { + request, err := http.NewRequest("GET", endpoint, nil) + if err != nil { + return nil, err + } + + request.Header.Add("Metadata-Flavor", "Google") + + timeout := time.Duration(5) * time.Second + httpClient := http.Client{Timeout: timeout} + + response, err := httpClient.Do(request) + if err != nil { + return nil, err + } + defer response.Body.Close() + + switch s := response.StatusCode; s { + case 200: + break + case 400: + return nil, &gcpMetadataError{errGCPMetadataInvalidRequest, endpoint, s} + case 404: + return nil, &gcpMetadataError{errGCPMetadataNotFound, endpoint, s} + default: + return nil, &gcpMetadataError{errGCPMetadataUnexpected, endpoint, s} + } + + data, err := io.ReadAll(response.Body) + if err != nil { + return nil, err + } + + return data, nil +} diff --git a/third_party/opa/v1/plugins/rest/gcp_test.go b/third_party/opa/v1/plugins/rest/gcp_test.go new file mode 100644 index 000000000000..e69a438d75a5 --- /dev/null +++ b/third_party/opa/v1/plugins/rest/gcp_test.go @@ -0,0 +1,103 @@ +package rest + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/open-policy-agent/opa/v1/keys" +) + +func TestGCPMetadataAuthPlugin(t *testing.T) { + idToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.Et9HFtf9R3GEMA0IICOfFMVXY7kkTX1wr4qCyhIf58U" + + s := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})) + defer s.Close() + + ts := httptest.NewServer(http.Handler(&gcpMetadataHandler{idToken})) + defer ts.Close() + + config := fmt.Sprintf(`{ + "name": "foo", + "url": "%s", + "allow_insecure_tls": true, + "credentials": { + "gcp_metadata": { + "audience": "https://example.org", + "endpoint": "%s" + } + } + }`, s.URL, ts.URL) + client, err := New([]byte(config), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + ctx := context.Background() + _, err = client.Do(ctx, "GET", "test") + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } +} + +func TestIdentityTokenFromMetadataService(t *testing.T) { + token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.Et9HFtf9R3GEMA0IICOfFMVXY7kkTX1wr4qCyhIf58U" + + ts := httptest.NewServer(http.Handler(&gcpMetadataHandler{token})) + defer ts.Close() + + tests := []struct { + audience string + identityToken string + identityTokenPath string + err error + }{ + {"https://example.org", token, defaultIdentityTokenPath, nil}, + {"", "", defaultIdentityTokenPath, errGCPMetadataInvalidRequest}, + {"https://example.org", "", "/status/bad/request", errGCPMetadataInvalidRequest}, + {"https://example.org", "", "/status/not/found", errGCPMetadataNotFound}, + {"https://example.org", "", "/status/internal/server/error", errGCPMetadataUnexpected}, + } + + for _, tt := range tests { + token, err := identityTokenFromMetadataService(ts.URL, tt.identityTokenPath, tt.audience) + if !errors.Is(err, tt.err) { + t.Fatalf("Unexpected error, got %v, want %v", err, tt.err) + } + + if token != tt.identityToken { + t.Fatalf("Unexpected id token, got %v, want %v", token, tt.identityToken) + } + } +} + +type gcpMetadataHandler struct { + identityToken string +} + +func (h *gcpMetadataHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path + audience := r.URL.Query()["audience"][0] + + if audience == "" { + http.Error(w, "", http.StatusBadRequest) + return + } + + switch path { + case defaultIdentityTokenPath: + fmt.Fprint(w, h.identityToken) + case "/status/bad/request": + http.Error(w, "", http.StatusBadRequest) + case "/status/not/found": + http.Error(w, "", http.StatusNotFound) + case "/status/internal/server/error": + http.Error(w, "", http.StatusInternalServerError) + default: + http.Error(w, "", http.StatusNotFound) + } +} diff --git a/third_party/opa/v1/plugins/rest/rest.go b/third_party/opa/v1/plugins/rest/rest.go new file mode 100644 index 000000000000..f8be30af5e31 --- /dev/null +++ b/third_party/opa/v1/plugins/rest/rest.go @@ -0,0 +1,366 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package rest implements a REST client for communicating with remote services. +package rest + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "maps" + "net/http" + "net/http/httputil" + "reflect" + "strings" + + "github.com/open-policy-agent/opa/internal/version" + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/tracing" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + defaultResponseHeaderTimeoutSeconds = int64(10) + defaultResponseSizeLimitBytes = 1024 + + grantTypeClientCredentials = "client_credentials" + grantTypeJwtBearer = "jwt_bearer" +) + +var maskedHeaderKeys = map[string]struct{}{ + "Authorization": {}, + "X-Amz-Security-Token": {}, +} + +// An HTTPAuthPlugin represents a mechanism to construct and configure HTTP authentication for a REST service +type HTTPAuthPlugin interface { + // implementations can assume NewClient will be called before Prepare + NewClient(Config) (*http.Client, error) + Prepare(*http.Request) error +} + +// Config represents configuration for a REST client. +type Config struct { + Name string `json:"name"` + URL string `json:"url"` + Headers map[string]string `json:"headers"` + AllowInsecureTLS bool `json:"allow_insecure_tls,omitempty"` + ResponseHeaderTimeoutSeconds *int64 `json:"response_header_timeout_seconds,omitempty"` + TLS *serverTLSConfig `json:"tls,omitempty"` + Credentials struct { + Bearer *bearerAuthPlugin `json:"bearer,omitempty"` + OAuth2 *oauth2ClientCredentialsAuthPlugin `json:"oauth2,omitempty"` + ClientTLS *clientTLSAuthPlugin `json:"client_tls,omitempty"` + S3Signing *awsSigningAuthPlugin `json:"s3_signing,omitempty"` + GCPMetadata *gcpMetadataAuthPlugin `json:"gcp_metadata,omitempty"` + AzureManagedIdentity *azureManagedIdentitiesAuthPlugin `json:"azure_managed_identity,omitempty"` + Plugin *string `json:"plugin,omitempty"` + } `json:"credentials"` + Type string `json:"type,omitempty"` + keys map[string]*keys.Config + logger logging.Logger +} + +// Equal returns true if this client config is equal to the other. +func (c *Config) Equal(other *Config) bool { + otherWithoutLogger := *other + otherWithoutLogger.logger = c.logger + return reflect.DeepEqual(c, &otherWithoutLogger) +} + +// An AuthPluginLookupFunc can lookup auth plugins by their name. +type AuthPluginLookupFunc func(name string) HTTPAuthPlugin + +// AuthPlugin should be used to get an authentication method from the config. +func (c *Config) AuthPlugin(lookup AuthPluginLookupFunc) (HTTPAuthPlugin, error) { + var candidate HTTPAuthPlugin + if c.Credentials.Plugin != nil { + if lookup == nil { + // if no authPluginLookup function is passed we can't resolve the plugin + return nil, errors.New("missing auth plugin lookup function") + } + + candidate := lookup(*c.Credentials.Plugin) + if candidate == nil { + return nil, fmt.Errorf("auth plugin %q not found", *c.Credentials.Plugin) + } + + return candidate, nil + } + // reflection avoids need for this code to change as auth plugins are added + s := reflect.ValueOf(c.Credentials) + for i := range s.NumField() { + if s.Field(i).IsNil() { + continue + } + + if candidate != nil { + return nil, errors.New("a maximum one credential method must be specified") + } + + candidate = s.Field(i).Interface().(HTTPAuthPlugin) + } + + if candidate == nil { + return &defaultAuthPlugin{}, nil + } + return candidate, nil +} + +func (c *Config) authHTTPClient(lookup AuthPluginLookupFunc) (*http.Client, error) { + plugin, err := c.AuthPlugin(lookup) + if err != nil { + return nil, err + } + return plugin.NewClient(*c) +} + +func (c *Config) authPrepare(req *http.Request, lookup AuthPluginLookupFunc) error { + plugin, err := c.AuthPlugin(lookup) + if err != nil { + return err + } + return plugin.Prepare(req) +} + +// Client implements an HTTP/REST client for communicating with remote +// services. +type Client struct { + bytes *[]byte + json *any + config Config + headers map[string]string + authPluginLookup AuthPluginLookupFunc + logger logging.Logger + loggerFields map[string]any + distributedTacingOpts tracing.Options +} + +// Name returns an option that overrides the service name on the client. +func Name(s string) func(*Client) { + return func(c *Client) { + c.config.Name = s + } +} + +// AuthPluginLookup assigns a function to lookup an HTTPAuthPlugin to a new Client. +// It's intended to be used when creating a Client using New(). Usually this is passed +// the plugins.AuthPlugin func, which retrieves a registered HTTPAuthPlugin from the +// plugin manager. +func AuthPluginLookup(l AuthPluginLookupFunc) func(*Client) { + return func(c *Client) { + c.authPluginLookup = l + } +} + +// Logger assigns a logger to the client +func Logger(l logging.Logger) func(*Client) { + return func(c *Client) { + c.logger = l + } +} + +// DistributedTracingOpts sets the options to be used by distributed tracing. +func DistributedTracingOpts(tr tracing.Options) func(*Client) { + return func(c *Client) { + c.distributedTacingOpts = tr + } +} + +// New returns a new Client for config. +func New(config []byte, keys map[string]*keys.Config, opts ...func(*Client)) (Client, error) { + var parsedConfig Config + if err := util.Unmarshal(config, &parsedConfig); err != nil { + return Client{}, err + } + + parsedConfig.URL = strings.TrimRight(parsedConfig.URL, "/") + + if parsedConfig.ResponseHeaderTimeoutSeconds == nil { + timeout := defaultResponseHeaderTimeoutSeconds + parsedConfig.ResponseHeaderTimeoutSeconds = &timeout + } + + parsedConfig.keys = keys + + client := Client{ + config: parsedConfig, + } + + for _, f := range opts { + f(&client) + } + + if client.logger == nil { + client.logger = logging.Get() + } + + client.config.logger = client.logger + + return client, nil +} + +// AuthPluginLookup returns the lookup function to find a custom registered +// auth plugin by its name. +func (c Client) AuthPluginLookup() AuthPluginLookupFunc { + return c.authPluginLookup +} + +// Service returns the name of the service this Client is configured for. +func (c Client) Service() string { + return c.config.Name +} + +// Config returns this Client's configuration +func (c Client) Config() *Config { + return &c.config +} + +// SetResponseHeaderTimeout sets the "ResponseHeaderTimeout" in the http client's Transport +func (c Client) SetResponseHeaderTimeout(timeout *int64) Client { + c.config.ResponseHeaderTimeoutSeconds = timeout + return c +} + +// Logger returns the logger assigned to the Client +func (c Client) Logger() logging.Logger { + return c.logger +} + +// LoggerFields returns the fields used for log statements used by Client +func (c Client) LoggerFields() map[string]any { + return c.loggerFields +} + +// WithHeader returns a shallow copy of the client with a header to include the +// requests. +func (c Client) WithHeader(k, v string) Client { + if v == "" { + return c + } + if c.headers == nil { + c.headers = map[string]string{} + } + c.headers[k] = v + return c +} + +// WithJSON returns a shallow copy of the client with the JSON value set as the +// message body to include the requests. This function sets the Content-Type +// header. +func (c Client) WithJSON(body any) Client { + c = c.WithHeader("Content-Type", "application/json") + c.json = &body + return c +} + +// WithBytes returns a shallow copy of the client with the bytes set as the +// message body to include in the requests. +func (c Client) WithBytes(body []byte) Client { + c.bytes = &body + return c +} + +// Do executes a request using the client. +func (c Client) Do(ctx context.Context, method, path string) (*http.Response, error) { + + httpClient, err := c.config.authHTTPClient(c.authPluginLookup) + if err != nil { + return nil, err + } + + if len(c.distributedTacingOpts) > 0 { + httpClient.Transport = tracing.NewTransport(httpClient.Transport, c.distributedTacingOpts) + } + + path = strings.Trim(path, "/") + + var body io.Reader + + if c.bytes != nil { + body = bytes.NewReader(*c.bytes) + } else if c.json != nil { + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(*c.json); err != nil { + return nil, err + } + body = &buf + } + + url := c.config.URL + "/" + path + req, err := http.NewRequestWithContext(ctx, method, url, body) + if err != nil { + return nil, err + } + + headers := map[string]string{ + "User-Agent": version.UserAgent, + } + + // Copy custom headers from config. + maps.Copy(headers, c.config.Headers) + + // Overwrite with headers set directly on client. + maps.Copy(headers, c.headers) + + for key, value := range headers { + req.Header.Add(key, value) + } + + if err = c.config.authPrepare(req, c.authPluginLookup); err != nil { + return nil, err + } + + if c.logger.GetLevel() >= logging.Debug { + c.loggerFields = map[string]any{ + "method": method, + "url": url, + "headers": withMaskedHeaders(req.Header), + } + + c.logger.WithFields(c.loggerFields).Debug("Sending request.") + } + + resp, err := httpClient.Do(req) + + if resp != nil && c.logger.GetLevel() >= logging.Debug { + // Only log for debug purposes. If an error occurred, the caller should handle + // that. In the non-error case, the caller may not do anything. + c.loggerFields["status"] = resp.Status + c.loggerFields["headers"] = resp.Header + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + dump, err := httputil.DumpResponse(resp, true) + if err != nil { + return nil, err + } + + if len(dump) < defaultResponseSizeLimitBytes { + c.loggerFields["response"] = string(dump) + } else { + c.loggerFields["response"] = fmt.Sprintf("%v...", string(dump[:defaultResponseSizeLimitBytes])) + } + } + c.logger.WithFields(c.loggerFields).Debug("Received response.") + } + + return resp, err +} + +func withMaskedHeaders(headers http.Header) http.Header { + masked := make(http.Header) + for k, v := range headers { + if _, ok := maskedHeaderKeys[k]; ok { + masked.Set(k, "REDACTED") + } else { + masked[k] = v + } + } + return masked +} diff --git a/third_party/opa/v1/plugins/rest/rest_test.go b/third_party/opa/v1/plugins/rest/rest_test.go new file mode 100644 index 000000000000..94608e3cc5ac --- /dev/null +++ b/third_party/opa/v1/plugins/rest/rest_test.go @@ -0,0 +1,2874 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package rest + +import ( + "bytes" + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "io" + "math/big" + "net" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "reflect" + "slices" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jws" + "github.com/open-policy-agent/opa/internal/providers/aws" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/keys" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/tracing" + + "github.com/open-policy-agent/opa/internal/version" + "github.com/open-policy-agent/opa/v1/util/test" + + testlogger "github.com/open-policy-agent/opa/v1/logging/test" +) + +const keyID = "key1" + +func TestAuthPluginWithNoAuthPluginLookup(t *testing.T) { + t.Parallel() + + authPlugin := "anything" + cfg := Config{ + Credentials: struct { + Bearer *bearerAuthPlugin `json:"bearer,omitempty"` + OAuth2 *oauth2ClientCredentialsAuthPlugin `json:"oauth2,omitempty"` + ClientTLS *clientTLSAuthPlugin `json:"client_tls,omitempty"` + S3Signing *awsSigningAuthPlugin `json:"s3_signing,omitempty"` + GCPMetadata *gcpMetadataAuthPlugin `json:"gcp_metadata,omitempty"` + AzureManagedIdentity *azureManagedIdentitiesAuthPlugin `json:"azure_managed_identity,omitempty"` + Plugin *string `json:"plugin,omitempty"` + }{ + Plugin: &authPlugin, + }, + } + _, err := cfg.AuthPlugin(nil) + if err == nil { + t.Error("Expected error but got nil") + } + if want, have := "missing auth plugin lookup function", err.Error(); want != have { + t.Errorf("Unexpected error, want %q, have %q", want, have) + } +} + +// Note(philipc): Cannot run this test in parallel, due to the t.Setenv calls +// from one of its helper methods. +func TestNew(t *testing.T) { + tests := []struct { + name string + input string + wantErr bool + env map[string]string + }{ + { + name: "BadScheme", + input: `{ + "name": "foo", + "url": "bad scheme://authority", + }`, + wantErr: true, + }, + { + name: "ValidUrl", + input: `{ + "name": "foo", + "url", "http://localhost/some/path", + }`, + }, + { + name: "Token", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "bearer": { + "token": "secret", + } + } + }`, + }, + { + name: "TokenWithScheme", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "bearer": { + "scheme": "Acmecorp-Token", + "token": "secret" + } + } + }`, + }, + { + name: "MissingTlsOptions", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "client_tls": {} + } + }`, + wantErr: true, + }, + { + name: "IncompleteTlsOptions", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "client_tls": { + "cert": "cert.pem" + } + } + }`, + wantErr: true, + }, + { + name: "EmptyS3Options", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + } + } + }`, + wantErr: true, + }, + { + name: "ValidS3EnvCreds", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "environment_credentials": {} + } + } + }`, + }, + { + name: "ValidApiGatewayEnvCreds", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "service": "execute-api", + "environment_credentials": {} + } + } + }`, + }, + { + name: "ValidS3MetadataCredsWithRole", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "metadata_credentials": { + "aws_region": "us-east-1", + "iam_role": "my_iam_role" + } + } + } + }`, + }, + { + name: "ValidS3MetadataCreds", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "metadata_credentials": { + "aws_region": "us-east-1", + } + } + } + }`, + }, + { + name: "MissingS3MetadataCredOptions", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "metadata_credentials": {} + } + } + }`, + wantErr: true, + }, + { + name: "MultipleS3CredOptions/metadata+environment", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "metadata_credentials": { + "aws_region": "us-east-1", + "iam_role": "my_iam_role" + }, + "environment_credentials": {} + } + } + }`, + wantErr: false, + }, + { + name: "MultipleS3CredOptions/metadata+profile+environment+webidentity", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "profile_credentials": {}, + "environment_credentials": {}, + "web_identity_credentials": {}, + "metadata_credentials": { + "aws_region": "us-east-1", + "iam_role": "my_iam_role" + } + } + } + }`, + env: map[string]string{ + awsRoleArnEnvVar: "TEST", + awsWebIdentityTokenFileEnvVar: "TEST", + awsRegionEnvVar: "us-west-2", + }, + wantErr: false, + }, + { + name: "MultipleCredentialsOptions", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "environment_credentials": {} + }, + "bearer": { + "scheme": "Acmecorp-Token", + "token": "secret" + } + } + }`, + wantErr: true, + }, + { + name: "Oauth2NoTokenUrl", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "token_url": "" + } + + } + }`, + wantErr: true, + }, + { + name: "Oauth2MissingScopes", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "token_url": "https://localhost", + "client_id": "client_one", + "client_secret": "super_secret" + } + } + }`, + }, + { + name: "Oauth2MissingClientId", + input: `{ + "name": "foo", + "url": "https://localhost", + "credentials": { + "oauth2": { + "token_url": "https://localhost", + "client_id": "" + } + } + }`, + wantErr: true, + }, + { + name: "Oauth2MissingSecret", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "token_url": "https://localhost", + "client_id": "client_one" + } + } + }`, + wantErr: true, + }, + { + name: "Oauth2Creds", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "token_url": "https://localhost", + "client_id": "client_one", + "client_secret": "super_secret" + } + } + }`, + }, + { + name: "Oauth2GetCredScopes", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "token_url": "https://localhost", + "client_id": "client_one", + "client_secret": "super_secret", + "scopes": ["profile", "opa"] + } + } + }`, + }, + { + name: "Oauth2JwtBearerMissingSigningKey", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }`, grantTypeJwtBearer), + wantErr: true, + }, + { + name: "Oauth2JwtBearerSigningKeyWithoutCorrespondingKey", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "signing_key": "key2", + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }`, grantTypeJwtBearer), + wantErr: true, + }, + { + name: "Oauth2JwtBearerSigningKeyWithCorrespondingKey", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "signing_key": "key1", + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }`, grantTypeJwtBearer), + }, + { + name: "Oauth2JwtBearerSigningKeyPublicKeyReference", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "signing_key": "pub_key", + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }`, grantTypeJwtBearer), + wantErr: true, + }, + { + name: "Oauth2WrongGrantType", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": "authorization_code", + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }`, + wantErr: true, + }, + { + name: "Oauth2ClientCredentialsMissingCredentials", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }`, grantTypeClientCredentials), + wantErr: true, + }, + { + name: "Oauth2ClientCredentialsJwtNoAdditionalClaims", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "signing_key": "key1", + "token_url": "https://localhost", + "scopes": ["profile", "opa"] + } + } + }`, grantTypeClientCredentials), + }, + { + name: "Oauth2ClientCredentialsJwtThumbprint", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "signing_key": "key1", + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "thumbprint": "8F1BDDDE9982299E62749C20EDDBAAC57F619D04" + } + } + }`, grantTypeClientCredentials), + }, + { + name: "Oauth2ClientCredentialsTooManyCredentials", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "signing_key": "key1", + "client_id": "client-one", + "client_secret": "supersecret", + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }`, grantTypeClientCredentials), + wantErr: true, + }, + { + name: "Oauth2ClientCredentialsJWTAuthentication", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "signing_key": "key1", + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }`, grantTypeClientCredentials), + }, + { + name: "Oauth2ClientCredentialsJWTAuthentication_with_AWS_KMS", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "aws_kms": { + "name": "arn:aws:kms:eu-west-1:account_no:key/key_id", + "algorithm": "ECDSA_SHA_256" + }, + "aws_signing": { + "service": "kms", + "environment_credentials": { + "aws_default_region": "eu-west-1" + } + }, + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }`, grantTypeClientCredentials), + }, + { + name: "Oauth2ClientCredentialsJWTAuthentication_with_AWS_KMS_missing_credentials", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "aws_kms": { + "name": "arn:aws:kms:eu-west-1:account_no:key/key_id", + "algorithm": "ECDSA_SHA_256" + }, + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }`, grantTypeClientCredentials), + wantErr: true, + }, + { + name: "Oauth2ClientCredentialsJWTAuthentication with Azure KeyVault", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "azure_keyvault": { + "key": "tester-key", + "key_algorithm": "ES256", + "vault": "my-secret-kv" + }, + "azure_signing": { + "service": "keyvault", + "azure_managed_identity": {} + }, + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }`, grantTypeClientCredentials), + }, + { + name: "Oauth2ClientCredentialsJWTAuthentication with Azure KeyVault and missing managed identity", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "azure_keyvault": { + "key": "tester-key", + "key_algorithm": "ES256", + "vault": "my-secret-kv" + }, + "azure_signing": { + "service": "keyvault", + }, + "token_url": "https://localhost", + "scopes": ["profile", "opa"], + "additional_claims": { + "aud": "some audience" + } + } + } + }`, grantTypeClientCredentials), + wantErr: true, + }, + { + name: "S3WebIdentityMissingEnvVars", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "web_identity_credentials": {} + }, + } + }`, + wantErr: true, + }, + { + name: "S3WebIdentityCreds", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "web_identity_credentials": {} + }, + } + }`, + env: map[string]string{ + awsRoleArnEnvVar: "TEST", + awsWebIdentityTokenFileEnvVar: "TEST", + awsRegionEnvVar: "us-west-1", + }, + }, + { + name: "S3AssumeRoleMissingEnvVars", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "assume_role_credentials": {} + }, + } + }`, + wantErr: true, + }, + { + name: "S3AssumeRoleCredsMissingSigningPlugin", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "assume_role_credentials": {} + }, + } + }`, + env: map[string]string{ + awsRoleArnEnvVar: "TEST", + accessKeyEnvVar: "TEST", + secretKeyEnvVar: "TEST", + awsRegionEnvVar: "us-west-1", + }, + wantErr: true, + }, + { + name: "S3AssumeRoleCreds", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "s3_signing": { + "assume_role_credentials": {"aws_signing": {"environment_credentials": {}}} + }, + } + }`, + env: map[string]string{ + awsRoleArnEnvVar: "TEST", + accessKeyEnvVar: "TEST", + secretKeyEnvVar: "TEST", + awsRegionEnvVar: "us-west-1", + }, + }, + { + name: "ValidGCPMetadataIDTokenOptions", + input: `{ + "name": "foo", + "url": "https://localhost", + "credentials": { + "gcp_metadata": { + "audience": "https://localhost" + } + } + }`, + }, + { + name: "ValidGCPMetadataAccessTokenOptions", + input: `{ + "name": "foo", + "url": "https://localhost", + "credentials": { + "gcp_metadata": { + "scopes": ["storage.read_only"] + } + } + }`, + }, + { + name: "EmptyGCPMetadataOptions", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "gcp_metadata": { + } + } + }`, + wantErr: true, + }, + { + name: "EmptyGCPMetadataIDTokenAudienceOption", + input: `{ + "name": "foo", + "url": "https://localhost", + "credentials": { + "gcp_metadata": { + "audience": "" + } + } + }`, + wantErr: true, + }, + { + name: "EmptyGCPMetadataAccessTokenScopesOption", + input: `{ + "name": "foo", + "url": "https://localhost", + "credentials": { + "gcp_metadata": { + "scopes": [] + } + } + }`, + wantErr: true, + }, + { + name: "InvalidGCPMetadataOptions", + input: `{ + "name": "foo", + "url": "https://localhost", + "credentials": { + "gcp_metadata": { + "audience": "https://localhost", + "scopes": ["storage.read_only"] + } + } + }`, + wantErr: true, + }, + { + name: "Plugin", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "plugin": "my_plugin" + } + }`, + }, + { + name: "Unknown plugin", + input: `{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "plugin": "unknown_plugin" + } + }`, + wantErr: true, + }, + { + name: "Oauth2CredsClientAssertionPath", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "token_url": "https://localhost", + "client_id": "client_one", + "client_assertion_path": "/some/file", + "scopes": ["profile", "opa"] + } + } + }`, grantTypeClientCredentials), + }, + { + name: "Oauth2CredsClientAssertion", + input: fmt.Sprintf(`{ + "name": "foo", + "url": "http://localhost", + "credentials": { + "oauth2": { + "grant_type": %q, + "token_url": "https://localhost", + "client_id": "client_one", + "client_assertion": "assertive", + "scopes": ["profile", "opa"] + } + } + }`, grantTypeClientCredentials), + }, + } + + var results []Client + + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + keyPem := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + }) + pubKeyPem := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PUBLIC KEY", + Bytes: x509.MarshalPKCS1PublicKey(&key.PublicKey), + }) + + ks := map[string]*keys.Config{ + keyID: { + PrivateKey: string(keyPem), + Algorithm: "RS256", + }, + "pub_key": { + Key: string(pubKeyPem), + Algorithm: "RS256", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + for key, val := range tc.env { + t.Setenv(key, val) + } + + client, err := New([]byte(tc.input), ks, AuthPluginLookup(mockAuthPluginLookup)) + if err != nil { + // We never want an error here and cannot proceed if there is one. + t.Fatalf("Unexpected error: %v", err) + } + + plugin, err := client.config.AuthPlugin(mockAuthPluginLookup) + if err != nil { + if tc.wantErr { + return + } + t.Fatalf("Unexpected error: %v", err) + } + + _, err = plugin.NewClient(client.config) + if err != nil && !tc.wantErr { + t.Fatalf("Unexpected error: %v", err) + } else if err == nil && tc.wantErr { + t.Fatalf("Expected error for input %v", tc.input) + } + + if *client.config.ResponseHeaderTimeoutSeconds != defaultResponseHeaderTimeoutSeconds { + t.Fatalf("Expected default response header timeout but got %v seconds", *client.config.ResponseHeaderTimeoutSeconds) + } + + results = append(results, client) + }) + } + + if results[3].config.Credentials.Bearer.Scheme != "Acmecorp-Token" { + t.Fatalf("Expected custom token but got: %v", results[3].config.Credentials.Bearer.Scheme) + } +} + +func TestNewWithResponseHeaderTimeout(t *testing.T) { + t.Parallel() + + input := `{ + "name": "foo", + "url": "http://localhost", + "response_header_timeout_seconds": 20 + }` + + client, err := New([]byte(input), map[string]*keys.Config{}) + if err != nil { + t.Fatal("Unexpected error") + } + + if *client.config.ResponseHeaderTimeoutSeconds != 20 { + t.Fatalf("Expected response header timeout %v seconds but got %v seconds", 20, *client.config.ResponseHeaderTimeoutSeconds) + } +} + +func TestDoWithResponseHeaderTimeout(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + ctx := context.Background() + + tests := map[string]struct { + d time.Duration + responseHeaderTimeout string + wantErr bool + errMsg string + }{ + "response_headers_timeout_not_met": {1, "2", false, ""}, + "response_headers_timeout_met": {2, "1", true, "net/http: timeout awaiting response headers"}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + + baseURL, teardown := getTestServerWithTimeout(tc.d) + defer teardown() + + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + "response_header_timeout_seconds": %v, + }`, baseURL, tc.responseHeaderTimeout) + ks := map[string]*keys.Config{} + client, err := New([]byte(config), ks) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = client.Do(ctx, "GET", "/v1/test") + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if !strings.Contains(err.Error(), tc.errMsg) { + t.Fatalf("Expected error %v but got %v", tc.errMsg, err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + }) + } +} + +type tracemock struct { + called int +} + +func (m *tracemock) NewTransport(rt http.RoundTripper, _ tracing.Options) http.RoundTripper { + m.called++ + return rt +} + +func (*tracemock) NewHandler(http.Handler, string, tracing.Options) http.Handler { + panic("unreachable") +} + +func TestDoWithDistributedTracingOpts(t *testing.T) { + t.Parallel() + + ctx := context.Background() + mock := tracemock{} + tracing.RegisterHTTPTracing(&mock) + + body := "Some Bad Request was received" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintln(w, body) + })) + defer ts.Close() + + buf := bytes.Buffer{} + logger := logging.New() + logger.SetOutput(&buf) + logger.SetLevel(logging.Debug) + + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + }`, ts.URL) + ks := map[string]*keys.Config{} + client, err := New([]byte(config), ks, DistributedTracingOpts(tracing.Options{"testoption"})) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = client.Do(ctx, "GET", ts.URL) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if exp, act := 1, mock.called; exp != act { + t.Errorf("calls to NewTransport: expected %d, got %d", exp, act) + } +} + +func TestDoWithResponseInClientLog(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + body := "Some Bad Request was received" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintln(w, body) + })) + defer ts.Close() + + buf := bytes.Buffer{} + logger := logging.New() + logger.SetOutput(&buf) + logger.SetLevel(logging.Debug) + + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + }`, ts.URL) + ks := map[string]*keys.Config{} + client, err := New([]byte(config), ks, Logger(logger)) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = client.Do(ctx, "GET", ts.URL) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if !strings.Contains(buf.String(), body) { + t.Errorf("expected string %q not found in client logs", body) + } +} + +func TestDoWithTruncatedResponseInClientLog(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintln(w, strings.Repeat("Some Bad Request was received", 50)) + })) + defer ts.Close() + + buf := bytes.Buffer{} + logger := logging.New() + logger.SetOutput(&buf) + logger.SetLevel(logging.Debug) + + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + }`, ts.URL) + ks := map[string]*keys.Config{} + client, err := New([]byte(config), ks, Logger(logger)) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + _, err = client.Do(ctx, "GET", ts.URL) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + exp := "Some Bad Request was recei..." + if !strings.Contains(buf.String(), exp) { + t.Errorf("expected string %q not found in client logs", exp) + } +} + +func TestValidUrl(t *testing.T) { + t.Parallel() + + ts := testServer{ + t: t, + expMethod: "GET", + expPath: "/test", + } + ts.start() + defer ts.stop() + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + }`, ts.server.URL) + client, err := New([]byte(config), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + ctx := context.Background() + if _, err := client.Do(ctx, "GET", "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } +} + +func testBearerToken(t *testing.T, scheme, token string) { + ts := testServer{ + t: t, + expBearerScheme: scheme, + expBearerToken: token, + } + ts.start() + defer ts.stop() + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + "credentials": { + "bearer": { + "scheme": %q, + "token": %q + } + } + }`, ts.server.URL, scheme, token) + ks := map[string]*keys.Config{} + client, err := New([]byte(config), ks) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + ctx := context.Background() + if _, err := client.Do(ctx, "GET", "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } +} + +func TestBearerTokenDefaultScheme(t *testing.T) { + t.Parallel() + + testBearerToken(t, "", "secret") +} + +func TestBearerTokenCustomScheme(t *testing.T) { + t.Parallel() + + testBearerToken(t, "Acmecorp-Token", "secret") +} + +func TestBearerTokenPath(t *testing.T) { + t.Parallel() + + ts := testServer{ + t: t, + expBearerScheme: "", + expBearerToken: "secret", + expBearerTokenPath: true, + } + ts.start() + defer ts.stop() + + files := map[string]string{ + "token.txt": "secret", + } + + test.WithTempFS(files, func(path string) { + tokenPath := filepath.Join(path, "token.txt") + + client := newTestBearerClient(t, &ts, tokenPath) + + ctx := context.Background() + if _, err := client.Do(ctx, "GET", "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Stop server and update the token + ts.stop() + ts.expBearerToken = "newsecret" + ts.start() + + // check client cannot access the server + client = newTestBearerClient(t, &ts, tokenPath) + + if resp, err := client.Do(ctx, "GET", "test"); err == nil { + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if resp.StatusCode != http.StatusUnauthorized { + t.Fatalf("Expected http status %v but got %v", http.StatusUnauthorized, resp.StatusCode) + } + + expectedErrMsg := "Expected bearer token \"newsecret\", got authorization header \"Bearer secret\"" + + if string(bodyBytes) != expectedErrMsg { + t.Fatalf("Expected error message %v but got %v", expectedErrMsg, string(bodyBytes)) + } + } else { + t.Fatalf("Unexpected error: %v", err) + } + + // Update the token file and try again + if err := os.WriteFile(filepath.Join(path, "token.txt"), []byte("newsecret"), 0600); err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if _, err := client.Do(ctx, "GET", "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) +} + +func TestBearerWithCustomCACert(t *testing.T) { + t.Parallel() + + ts := testServer{ + t: t, + tls: true, + expBearerScheme: "", + expBearerToken: "secret", + expBearerTokenPath: true, + } + ts.start() + defer ts.stop() + + files := map[string]string{ + "token.txt": "secret", + "ca.pem": string(ts.rootCertPEM), + } + + test.WithTempFS(files, func(path string) { + tokenPath := filepath.Join(path, "token.txt") + ts.caCert = filepath.Join(path, "ca.pem") + + client := newTestBearerClient(t, &ts, tokenPath) + + ctx := context.Background() + if _, err := client.Do(ctx, "GET", "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) +} + +func TestBearerWithCustomCACertAndSystemCA(t *testing.T) { + t.Parallel() + + ts := testServer{ + t: t, + tls: true, + expBearerScheme: "", + expBearerToken: "secret", + expBearerTokenPath: true, + expectSystemCA: true, + } + ts.start() + defer ts.stop() + + files := map[string]string{ + "token.txt": "secret", + "ca.pem": string(ts.rootCertPEM), + } + + test.WithTempFS(files, func(path string) { + tokenPath := filepath.Join(path, "token.txt") + ts.caCert = filepath.Join(path, "ca.pem") + + client := newTestBearerClient(t, &ts, tokenPath) + + ctx := context.Background() + if _, err := client.Do(ctx, "GET", "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) +} + +func TestBearerTokenInvalidConfig(t *testing.T) { + t.Parallel() + + ts := testServer{ + t: t, + expBearerScheme: "", + expBearerToken: "secret", + } + ts.start() + defer ts.stop() + + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + "credentials": { + "bearer": { + "token_path": %q, + "token": %q + } + } + }`, ts.server.URL, "token.txt", "secret") + client, err := New([]byte(config), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + ctx := context.Background() + + _, err = client.Do(ctx, "GET", "test") + + if err == nil { + t.Fatalf("Expected error but got nil") + } + + if !strings.HasPrefix(err.Error(), "invalid config") { + t.Fatalf("Unexpected error message %v\n", err) + } +} + +func TestBearerTokenIsEncodedForOCI(t *testing.T) { + t.Parallel() + + config := `{ + "name": "foo", + "type": "oci", + "credentials": { + "bearer": { + "token": "secret", + "scheme": "Bearer" + } + } + }` + + client, err := New([]byte(config), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("New() = %q", err) + } + + if _, err := client.config.Credentials.Bearer.NewClient(client.config); err != nil { + t.Errorf("Bearer.NewClient() = %q", err) + } + + req := httptest.NewRequest("", "http://somewhere.com", nil) + if err := client.config.Credentials.Bearer.Prepare(req); err != nil { + t.Errorf("Bearer.Prepare() = %q", err) + } + + token := base64.StdEncoding.EncodeToString([]byte("secret")) + + want := "Bearer " + token + got := req.Header.Get("Authorization") + if got != want { + t.Errorf("req.Header.Get(\"Authorization\") = %q, want = %q", got, want) + } +} + +func newTestBearerClient(t *testing.T, ts *testServer, tokenPath string) *Client { + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + "tls": {"ca_cert": %q, system_ca_required: %v}, + "credentials": { + "bearer": { + "token_path": %q + } + } + }`, ts.server.URL, ts.caCert, ts.expectSystemCA, tokenPath) + client, err := New([]byte(config), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + return &client +} + +func TestClientCert(t *testing.T) { + t.Parallel() + + ts := testServer{ + t: t, + tls: true, + expectClientCert: true, + } + ts.start() + defer ts.stop() + + files := map[string]string{ + "client.pem": string(ts.clientCertPem), + "client.key": string(ts.clientCertKey), + } + + test.WithTempFS(files, func(path string) { + certPath := filepath.Join(path, "client.pem") + keyPath := filepath.Join(path, "client.key") + + client := newTestClient(t, &ts, certPath, keyPath) + + ctx := context.Background() + if _, err := client.Do(ctx, "GET", "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Scramble the keys in the server + ts.stop() + ts.start() + + // Ensure the keys don't work anymore, make a new client as the url will have changed + client = newTestClient(t, &ts, certPath, keyPath) + _, err := client.Do(ctx, "GET", "test") + expectedErrMsg := func(s string) bool { + switch { + case strings.Contains(s, "tls: unknown certificate authority"): + case strings.Contains(s, "tls: bad certificate"): + default: + return false + } + return true + } + if err == nil || !expectedErrMsg(err.Error()) { + t.Fatalf("Unexpected error %v", err) + } + + // Update the key files and try again.. + if err := os.WriteFile(filepath.Join(path, "client.pem"), ts.clientCertPem, 0600); err != nil { + t.Fatalf("Unexpected error: %s", err) + } + if err := os.WriteFile(filepath.Join(path, "client.key"), ts.clientCertKey, 0600); err != nil { + t.Fatalf("Unexpected error: %s", err) + } + if _, err := client.Do(ctx, "GET", "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) +} + +func TestClientCertPassword(t *testing.T) { + t.Parallel() + + ts := testServer{ + t: t, + tls: true, + expectClientCert: true, + clientCertPassword: "password", + } + ts.start() + defer ts.stop() + + files := map[string]string{ + "client.pem": string(ts.clientCertPem), + "client.key": string(ts.clientCertKey), + } + + test.WithTempFS(files, func(path string) { + certPath := filepath.Join(path, "client.pem") + keyPath := filepath.Join(path, "client.key") + + client := newTestClient(t, &ts, certPath, keyPath) + + ctx := context.Background() + if _, err := client.Do(ctx, "GET", "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) +} + +func TestClientTLSWithCustomCACert(t *testing.T) { + t.Parallel() + + ts := testServer{ + t: t, + tls: true, + expectClientCert: true, + } + ts.start() + defer ts.stop() + + files := map[string]string{ + "client.pem": string(ts.clientCertPem), + "client.key": string(ts.clientCertKey), + "ca.pem": string(ts.rootCertPEM), + } + + test.WithTempFS(files, func(path string) { + certPath := filepath.Join(path, "client.pem") + keyPath := filepath.Join(path, "client.key") + ts.caCert = filepath.Join(path, "ca.pem") + + client := newTestClient(t, &ts, certPath, keyPath) + + ctx := context.Background() + if _, err := client.Do(ctx, "GET", "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) +} + +func TestClientTLSWithCustomCACertAndSystemCA(t *testing.T) { + t.Parallel() + + ts := testServer{ + t: t, + tls: true, + expectClientCert: true, + expectSystemCA: true, + } + ts.start() + defer ts.stop() + + files := map[string]string{ + "client.pem": string(ts.clientCertPem), + "client.key": string(ts.clientCertKey), + "ca.pem": string(ts.rootCertPEM), + } + + test.WithTempFS(files, func(path string) { + certPath := filepath.Join(path, "client.pem") + keyPath := filepath.Join(path, "client.key") + ts.caCert = filepath.Join(path, "ca.pem") + + client := newTestClient(t, &ts, certPath, keyPath) + + ctx := context.Background() + if _, err := client.Do(ctx, "GET", "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) +} + +func TestOauth2ClientCredentials(t *testing.T) { + t.Parallel() + + tests := []struct { + ts *testServer + ots *oauth2TestServer + options testPluginCustomizer + wantErr bool + }{ + { + ts: &testServer{t: t, expBearerToken: "token_1"}, + ots: &oauth2TestServer{t: t}, + }, + { + ts: &testServer{t: t, expBearerToken: "token_1"}, + ots: &oauth2TestServer{t: t, tokenType: "unknown"}, + wantErr: true, + }, + { + ts: &testServer{t: t}, + ots: &oauth2TestServer{t: t}, + options: func(c *Config) { + c.Credentials.OAuth2.ClientSecret = "not_super_secret" + }, + wantErr: true, + }, + { + ts: &testServer{t: t}, + ots: &oauth2TestServer{t: t, expScope: &[]string{"read", "opa"}}, + options: func(c *Config) { + c.Credentials.OAuth2.Scopes = []string{"read", "opa"} + }, + }, + { + ts: &testServer{t: t}, + ots: &oauth2TestServer{t: t, expHeaders: map[string]string{"x-custom-header": "custom-value"}}, + options: func(c *Config) { + c.Credentials.OAuth2.AdditionalHeaders = map[string]string{"x-custom-header": "custom-value"} + }, + }, + { + ts: &testServer{t: t}, + ots: &oauth2TestServer{t: t, expBody: map[string]string{"custom_field": "custom-value"}}, + options: func(c *Config) { + c.Credentials.OAuth2.AdditionalParameters = map[string]string{"custom_field": "custom-value"} + }, + }, + } + + for _, tc := range tests { + func() { + tc.ts.start() + defer tc.ts.stop() + tc.ots.start() + defer tc.ots.stop() + + if tc.options == nil { + tc.options = func(_ *Config) {} + } + + client := newOauth2TestClient(t, tc.ts, tc.ots, tc.options) + ctx := context.Background() + _, err := client.Do(ctx, "GET", "test") + if err != nil && !tc.wantErr { + t.Fatalf("Unexpected error: %v", err) + } else if err == nil && tc.wantErr { + t.Fatalf("Expected error: %v", err) + } + }() + } +} + +func TestOauth2ClientCredentialsExpiringTokenIsRefreshed(t *testing.T) { + t.Parallel() + + ts := testServer{ + t: t, + expBearerToken: "token_1", + } + ts.start() + ots := oauth2TestServer{ + t: t, + // Issue tokens with a TTL below our considered minimum - this should force the client to fetch a new one the + // second time the credentials are used rather than reusing the token it has + tokenTTL: 9, + } + ots.start() + defer ots.stop() + + client := newOauth2TestClient(t, &ts, &ots) + ctx := context.Background() + _, err := client.Do(ctx, "GET", "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + ts.stop() + ts = testServer{ + t: t, + expBearerToken: "token_2", + } + ts.start() + defer ts.stop() + + client = newOauth2TestClient(t, &ts, &ots) + ctx = context.Background() + _, err = client.Do(ctx, "GET", "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +func TestOauth2ClientCredentialsNonExpiringTokenIsReused(t *testing.T) { + t.Parallel() + + ts := testServer{ + t: t, + expBearerToken: "token_1", + } + ts.start() + defer ts.stop() + + ots := oauth2TestServer{ + t: t, + tokenTTL: 300, + } + ots.start() + defer ots.stop() + + client := newOauth2TestClient(t, &ts, &ots) + ctx := context.Background() + _, err := client.Do(ctx, "GET", "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, err = client.Do(ctx, "GET", "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +func TestOauth2JwtBearerGrantType(t *testing.T) { + t.Parallel() + + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + keyPem := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}) + ks := map[string]*keys.Config{ + keyID: { + PrivateKey: string(keyPem), + Algorithm: "RS256", + }, + } + + ts := testServer{t: t, expBearerToken: "token_1"} + ts.start() + defer ts.stop() + + ots := oauth2TestServer{ + t: t, + tokenTTL: 300, + expGrantType: "urn:ietf:params:oauth:grant-type:jwt-bearer", + expScope: &[]string{"scope1", "scope2"}, + expJwtCredential: true, + expAlgorithm: jwa.RS256, + verificationKey: &key.PublicKey, + } + ots.start() + defer ots.stop() + + client := newOauth2JwtBearerTestClient(t, ks, &ts, &ots, func(c *Config) { + c.Credentials.OAuth2.SigningKeyID = keyID + }) + ctx := context.Background() + _, err = client.Do(ctx, "GET", "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +func TestOauth2JwtBearerGrantTypePKCS8EncodedPrivateKey(t *testing.T) { + t.Parallel() + + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + privateKey, err := x509.MarshalPKCS8PrivateKey(key) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + keyPem := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: privateKey}) + ks := map[string]*keys.Config{ + keyID: { + PrivateKey: string(keyPem), + Algorithm: "RS256", + }, + } + + ts := testServer{t: t, expBearerToken: "token_1"} + ts.start() + defer ts.stop() + + ots := oauth2TestServer{ + t: t, + tokenTTL: 300, + expGrantType: "urn:ietf:params:oauth:grant-type:jwt-bearer", + expScope: &[]string{"scope1", "scope2"}, + expJwtCredential: true, + expAlgorithm: jwa.RS256, + verificationKey: &key.PublicKey, + } + ots.start() + defer ots.stop() + + client := newOauth2JwtBearerTestClient(t, ks, &ts, &ots, func(c *Config) { + c.Credentials.OAuth2.SigningKeyID = keyID + }) + ctx := context.Background() + _, err = client.Do(ctx, "GET", "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +func TestOauth2JwtBearerGrantTypeEllipticCurveAlgorithm(t *testing.T) { + t.Parallel() + + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + privateKey, err := x509.MarshalECPrivateKey(key) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + keyPem := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: privateKey}) + ks := map[string]*keys.Config{ + keyID: { + PrivateKey: string(keyPem), + Algorithm: "ES256", + }, + } + + ts := testServer{t: t, expBearerToken: "token_1"} + ts.start() + defer ts.stop() + + ots := oauth2TestServer{ + t: t, + tokenTTL: 300, + expGrantType: "urn:ietf:params:oauth:grant-type:jwt-bearer", + expScope: &[]string{"scope1", "scope2"}, + expJwtCredential: true, + expAlgorithm: jwa.ES256, + verificationKey: &key.PublicKey, + } + ots.start() + defer ots.stop() + + client := newOauth2JwtBearerTestClient(t, ks, &ts, &ots, func(c *Config) { + c.Credentials.OAuth2.SigningKeyID = keyID + c.Credentials.OAuth2.IncludeJti = true + }) + ctx := context.Background() + _, err = client.Do(ctx, "GET", "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, err = client.Do(ctx, "GET", "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +func TestOauth2ClientCredentialsJwtAuthentication(t *testing.T) { + t.Parallel() + + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + keyPem := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}) + ks := map[string]*keys.Config{ + keyID: { + PrivateKey: string(keyPem), + Algorithm: "RS256", + }, + } + + ts := testServer{t: t, expBearerToken: "token_1"} + ts.start() + defer ts.stop() + + ots := oauth2TestServer{ + t: t, + tokenTTL: 300, + expGrantType: grantTypeClientCredentials, + expScope: &[]string{"scope1", "scope2"}, + expX5t: "jxvd3pmCKZ5idJwg7duqxX9hnQQ=", + expJwtCredential: true, + expAlgorithm: jwa.RS256, + verificationKey: &key.PublicKey, + } + ots.start() + defer ots.stop() + + client := newOauth2ClientCredentialsJwtAuthClient(t, ks, &ts, &ots, func(c *Config) { + c.Credentials.OAuth2.SigningKeyID = keyID + }) + ctx := context.Background() + _, err = client.Do(ctx, "GET", "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, err = client.Do(ctx, "GET", "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +// https://github.com/open-policy-agent/opa/issues/3255 +func TestS3SigningInstantiationInitializesLogger(t *testing.T) { + t.Parallel() + + config := `{ + "name": "foo", + "url": "https://bundles.example.com", + "credentials": { + "s3_signing": { + "environment_credentials": {} + } + } + }` + + authPlugin := &awsSigningAuthPlugin{ + AWSEnvironmentCredentials: &awsEnvironmentCredentialService{}, + } + client, err := New([]byte(config), map[string]*keys.Config{}, AuthPluginLookup(func(_ string) HTTPAuthPlugin { + return authPlugin + })) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + plugin := client.authPluginLookup("s3_signing") + if _, err = plugin.NewClient(client.config); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if authPlugin.logger == nil { + t.Errorf("Expected logger to be initialized") + } +} + +func TestS3SigningMultiCredentialProvider(t *testing.T) { + t.Parallel() + + credentialProviderCount := 4 + config := `{ + "name": "foo", + "url": "https://bundles.example.com", + "credentials": { + "s3_signing": { + "environment_credentials": {}, + "profile_credentials": {}, + "metadata_credentials": {}, + "web_identity_credentials": {} + } + } + }` + + client, err := New([]byte(config), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + awsPlugin := client.config.Credentials.S3Signing + if awsPlugin == nil { + t.Fatalf("Client config S3 signing credentials setup unexpected") + } + + awsCredentialServiceChain, ok := awsPlugin.awsCredentialService().(*awsCredentialServiceChain) + if !ok { + t.Fatalf("Unexpected AWS credential service:%v is not a chain", + reflect.TypeOf(awsCredentialServiceChain)) + } + + if len(awsCredentialServiceChain.awsCredentialServices) != credentialProviderCount { + t.Fatalf("Credential provider count mismatch %d != %d", credentialProviderCount, + len(awsCredentialServiceChain.awsCredentialServices)) + } + + expectedOrder := []awsCredentialService{ + &awsEnvironmentCredentialService{}, + &awsWebIdentityCredentialService{}, + &awsProfileCredentialService{}, + &awsMetadataCredentialService{}, + } + + if !reflect.DeepEqual(awsCredentialServiceChain.awsCredentialServices, + expectedOrder) { + t.Fatalf("Ordering is unexpected") + } +} + +func TestAWSCredentialServiceChain(t *testing.T) { + tests := []struct { + name string + input string + wantErr bool + env map[string]string + errMsg string + }{ + { + name: "Fallback to Environment Credential", + input: `{ + "name": "foo", + "url": "https://bundles.example.com", + "credentials": { + "s3_signing": { + "web_identity_credentials": {}, + "environment_credentials": {}, + "profile_credentials": {}, + "metadata_credentials": {} + } + } + }`, + wantErr: false, + env: map[string]string{ + accessKeyEnvVar: "a", + secretKeyEnvVar: "a", + awsRegionEnvVar: "us-east-1", + }, + }, + { + name: "No provider is successful", + input: `{ + "name": "foo", + "url": "https://bundles.example.com", + "credentials": { + "s3_signing": { + "web_identity_credentials": {}, + "environment_credentials": {}, + "profile_credentials": {}, + "metadata_credentials": {} + } + } + }`, + wantErr: true, + errMsg: "all AWS credential providers failed: 4 errors occurred", + env: map[string]string{}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + for key, val := range tc.env { + t.Setenv(key, val) + } + + t.Cleanup(func() { + for key := range tc.env { + _ = os.Unsetenv(key) + } + }) + + client, err := New([]byte(tc.input), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + awsPlugin := client.config.Credentials.S3Signing + if awsPlugin == nil { + t.Fatalf("Client config S3 signing credentials setup unexpected") + } + + req, err := http.NewRequest("GET", "/example/bundle.tar.gz", nil) + if err != nil { + t.Fatalf("Failed to create HTTP request: %v", err) + } + + awsPlugin.logger = client.logger + err = awsPlugin.Prepare(req) + + if tc.wantErr { + if err == nil { + t.Fatalf("Expected error for input %v", tc.input) + } + + if !strings.Contains(err.Error(), tc.errMsg) { + t.Fatalf("Expected error message %v but got %v", tc.errMsg, err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + } +} + +func TestDebugLoggingRequestMaskAuthorizationHeader(t *testing.T) { + t.Parallel() + + token := "secret" + plaintext := "plaintext" + ts := testServer{t: t, expBearerToken: token} + ts.start() + defer ts.stop() + + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + "credentials": { + "bearer": { + "token": %q + } + }, + "headers": { + "X-AMZ-SECURITY-TOKEN": %q, + "remains-unmasked": %q + } + }`, ts.server.URL, token, token, plaintext) + client, err := New([]byte(config), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + logger := testlogger.New() + logger.SetLevel(logging.Debug) + client.logger = logger + + ctx := context.Background() + if _, err := client.Do(ctx, "GET", "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + entries := logger.Entries() + if len(entries) != 2 { + t.Fatalf("Expected 2 log entries, got %d", len(entries)) + } + + requestEntry := entries[0] + headers := requestEntry.Fields["headers"].(http.Header) + for k := range headers { + v := headers.Get(k) + if _, ok := maskedHeaderKeys[k]; ok { + if v != "REDACTED" { + t.Errorf("Expected redacted %q header value, got %v", k, v) + } + } else if k == "Remains-Unmasked" && v != plaintext { + t.Errorf("Expected %q header to have value %q, got %v", k, plaintext, v) + } + } +} + +func newTestClient(t *testing.T, ts *testServer, certPath string, keypath string) *Client { + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + "allow_insecure_tls": true, + "tls": {"ca_cert": %q, system_ca_required: %v}, + "credentials": { + "client_tls": { + "cert": %q, + "private_key": %q + } + } + }`, ts.server.URL, ts.caCert, ts.expectSystemCA, certPath, keypath) + client, err := New([]byte(config), map[string]*keys.Config{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if ts.clientCertPassword != "" { + client.Config().Credentials.ClientTLS.PrivateKeyPassphrase = ts.clientCertPassword + } + + return &client +} + +type testPluginCustomizer func(c *Config) + +type testServer struct { + t *testing.T + server *httptest.Server + expPath string + expMethod string + expBearerToken string + expBearerScheme string + expBearerTokenPath bool + tls bool + clientCertPem []byte + clientCertKey []byte + clientCertPassword string + expectClientCert bool + rootCertPEM []byte + caCert string + expectSystemCA bool + serverCertPool *x509.CertPool + certificates []tls.Certificate +} + +type oauth2TestServer struct { + t *testing.T + server *httptest.Server + expGrantType string + expClientID string + expClientSecret string + expHeaders map[string]string + expBody map[string]string + expJwtCredential bool + expScope *[]string + expAlgorithm jwa.SignatureAlgorithm + expX5t string + expSignature string + tokenType string + tokenTTL int64 + invocations int32 + verificationKey any +} + +func newOauth2TestClient(t *testing.T, ts *testServer, ots *oauth2TestServer, options ...testPluginCustomizer) *Client { + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + "allow_insecure_tls": true, + "credentials": { + "oauth2": { + "token_url": "%v/token", + "client_id": "client_one", + "client_secret": "super_secret" + } + } + }`, ts.server.URL, ots.server.URL) + client, err := New([]byte(config), map[string]*bundle.KeyConfig{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + for _, option := range options { + option(client.Config()) + } + + return &client +} + +// Create client to test JWT authorization grant as described in https://tools.ietf.org/html/rfc7523 +func newOauth2JwtBearerTestClient(t *testing.T, keys map[string]*keys.Config, ts *testServer, ots *oauth2TestServer, options ...testPluginCustomizer) *Client { + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + "allow_insecure_tls": true, + "credentials": { + "oauth2": { + "token_url": "%v/token", + "grant_type": %q, + "scopes": ["scope1", "scope2"], + "additional_claims": { + "aud": "test-audience", + "iss": "client-one" + } + } + } + }`, ts.server.URL, ots.server.URL, grantTypeJwtBearer) + + client, err := New([]byte(config), keys) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + for _, option := range options { + option(client.Config()) + } + + return &client +} + +// Create client to test JWT client authentication as described in https://tools.ietf.org/html/rfc7523 +func newOauth2ClientCredentialsJwtAuthClient(t *testing.T, keys map[string]*keys.Config, ts *testServer, ots *oauth2TestServer, options ...testPluginCustomizer) *Client { + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + "allow_insecure_tls": true, + "credentials": { + "oauth2": { + "token_url": "%v/token", + "grant_type": %q, + "signing_key": "key1", + "client_id": "client-one", + "scopes": ["scope1", "scope2"], + "thumbprint": "8F1BDDDE9982299E62749C20EDDBAAC57F619D04", + "additional_claims": { + "aud": "test-audience", + "iss": "client-one" + } + } + } + }`, ts.server.URL, ots.server.URL, grantTypeClientCredentials) + + client, err := New([]byte(config), keys) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + for _, option := range options { + option(client.Config()) + } + + return &client +} + +func (t *oauth2TestServer) start() { + if t.tokenTTL == 0 { + t.tokenTTL = 3600 + } + if t.expScope == nil { + t.expScope = &[]string{} + } + if t.tokenType == "" { + t.tokenType = "bearer" + } + t.expClientID = "client_one" + t.expClientSecret = "super_secret" + + t.server = httptest.NewUnstartedServer(http.HandlerFunc(t.handle)) + + rootKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.t.Fatalf("generating random key: %v", err) + } + _, rootCertPem, err := createRootCert(rootKey) + if err != nil { + t.t.Fatalf("creating root cert: %v", err) + } + + serverCertPool := x509.NewCertPool() + serverCertPool.AppendCertsFromPEM(rootCertPem) + t.server.TLS = &tls.Config{ + RootCAs: serverCertPool, + } + t.server.StartTLS() +} + +func (t *oauth2TestServer) stop() { + t.server.Close() +} + +func (t *oauth2TestServer) handle(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + t.t.Fatalf("Expected method POST, got %v", r.Method) + } + if r.URL.Path != "/token" { + t.t.Fatalf("Expected path /token got %q", r.URL.Path) + } + + if err := r.ParseForm(); err != nil { + t.t.Fatal(err) + } + + if t.expGrantType == "" { + t.expGrantType = grantTypeClientCredentials + } + if r.Form["grant_type"][0] != t.expGrantType { + t.t.Fatalf("Expected grant_type=%v", t.expGrantType) + } + + for k, v := range t.expBody { + if r.Form[k][0] != v { + t.t.Fatalf("Expected header %s=%s got %s", k, v, r.Form[k][0]) + } + } + + for k, v := range t.expHeaders { + if r.Header.Get(k) != v { + t.t.Fatalf("Expected header %s=%s got %s", k, v, r.Header.Get(k)) + } + } + + if len(r.Form["scope"]) > 0 { + scope := strings.Split(r.Form["scope"][0], " ") + if !slices.Equal(*t.expScope, scope) { + t.t.Fatalf("Expected scope %v, got %v", *t.expScope, scope) + } + } else if t.expScope != nil && len(*t.expScope) > 0 { + t.t.Fatal("Expected scope to be provided") + } + + if !t.expJwtCredential { + authHeader := strings.TrimSpace(r.Header.Get("Authorization")) + split := strings.Split(authHeader, " ") + credentials := split[len(split)-1] + + decoded, err := base64.StdEncoding.DecodeString(credentials) + if err != nil { + t.t.Fatal(err) + } + + pair := strings.SplitN(string(decoded), ":", 2) + if len(pair) != 2 || pair[0] != t.expClientID || pair[1] != t.expClientSecret { + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(`{"error"": "invalid_client"}`)) + return + } + } else { + var token string + if t.expGrantType == "urn:ietf:params:oauth:grant-type:jwt-bearer" { + token = r.Form["assertion"][0] + } else { + token = r.Form["client_assertion"][0] + } + if t.expSignature != "" { + signature := strings.Split(token, ".")[2] + if t.expSignature != signature { + t.t.Errorf("Expected expSignature %v, got %v", t.expSignature, signature) + } + } else { + _, err := jws.Verify([]byte(token), t.expAlgorithm, t.verificationKey) + if err != nil { + t.t.Fatalf("Unexpected signature verification error %v", err) + } + } + if t.expX5t != "" { + headerRaw, _ := base64.RawURLEncoding.DecodeString(strings.Split(token, ".")[0]) + var headers map[string]string + _ = json.Unmarshal(headerRaw, &headers) + x5t := headers["x5t"] + + if t.expX5t != x5t { + t.t.Errorf("Expected expX5t %v, got %v", t.expX5t, x5t) + } + } + } + + t.invocations++ + token := fmt.Sprintf("token_%v", t.invocations) + + w.WriteHeader(http.StatusOK) + body := fmt.Sprintf(`{"token_type": "%v", "access_token": "%v", "expires_in": %v}`, t.tokenType, token, t.tokenTTL) + _, _ = w.Write([]byte(body)) +} + +func (t *testServer) handle(w http.ResponseWriter, r *http.Request) { + if t.expMethod != "" && t.expMethod != r.Method { + t.t.Fatalf("Expected method %v, got %v", t.expMethod, r.Method) + } + if t.expPath != "" && t.expPath != r.URL.Path { + t.t.Fatalf("Expected path %q, got %q", t.expPath, r.URL.Path) + } + if (t.expBearerToken != "" || t.expBearerScheme != "") && len(r.Header["Authorization"]) == 0 { + t.t.Fatal("Expected bearer token, but didn't get any") + } + if len(r.Header["Authorization"]) > 0 { + auth := r.Header["Authorization"][0] + if t.expBearerScheme != "" && !strings.HasPrefix(auth, t.expBearerScheme) { + errMsg := fmt.Sprintf("Expected bearer scheme %q, got authorization header %q", t.expBearerScheme, auth) + if t.expBearerTokenPath { + w.WriteHeader(http.StatusUnauthorized) + _, _ = w.Write([]byte(errMsg)) + return + } + t.t.Fatal(errMsg) + } + if t.expBearerToken != "" && !strings.HasSuffix(auth, t.expBearerToken) { + errMsg := fmt.Sprintf("Expected bearer token %q, got authorization header %q", t.expBearerToken, auth) + if t.expBearerTokenPath { + w.WriteHeader(http.StatusUnauthorized) + _, _ = w.Write([]byte(errMsg)) + return + } + t.t.Fatal(errMsg) + } + } + if t.expectClientCert { + if len(r.TLS.PeerCertificates) == 0 { + t.t.Fatal("Expected client certificate but didn't get any") + } + } + ua := r.Header.Get("user-Agent") + if ua != version.UserAgent { + t.t.Errorf("Unexpected User-Agent string: %s", ua) + } + + w.WriteHeader(200) +} + +func (t *testServer) generateClientKeys() { + // generate a new set of root key+cert objects + rootKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.t.Fatalf("generating random key: %v", err) + } + rootCert, rootCertPEM, err := createRootCert(rootKey) + if err != nil { + t.t.Fatalf("error creating cert: %v", err) + } + + keyPEMBlock := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rootKey)}) + cert, err := tls.X509KeyPair(rootCertPEM, keyPEMBlock) + if err != nil { + t.t.Fatalf("error creating tls.X509KeyPair: %v", err) + } + + // save a copy of the root certificate for clients to use + t.serverCertPool = x509.NewCertPool() + t.serverCertPool.AppendCertsFromPEM(rootCertPEM) + t.rootCertPEM = rootCertPEM + t.certificates = []tls.Certificate{cert} + + // create a key-pair for the client + clientKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.t.Fatalf("generating random key: %v", err) + } + + // create a template for the client + clientCertTmpl, err := certTemplate() + if err != nil { + t.t.Fatalf("creating cert template: %v", err) + } + clientCertTmpl.KeyUsage = x509.KeyUsageDigitalSignature + clientCertTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth} + + // the root cert signs the client cert + _, t.clientCertPem, err = createCert(clientCertTmpl, rootCert, &clientKey.PublicKey, rootKey) + if err != nil { + t.t.Fatalf("error creating cert: %v", err) + } + + var pemBlock *pem.Block + if t.clientCertPassword != "" { + // nolint: staticcheck // We don't want to forbid users from using this encryption. + pemBlock, err = x509.EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(clientKey), + []byte(t.clientCertPassword), x509.PEMCipherAES128) + if err != nil { + t.t.Fatalf("error encrypting pem block: %v", err) + } + } else { + pemBlock = &pem.Block{ + Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(clientKey), + } + } + + // encode and load the cert and private key for the client + t.clientCertKey = pem.EncodeToMemory(pemBlock) +} + +func (t *testServer) start() { + t.server = httptest.NewUnstartedServer(http.HandlerFunc(t.handle)) + + if t.tls { + t.generateClientKeys() + t.server.TLS = &tls.Config{ + ClientAuth: tls.VerifyClientCertIfGiven, + ClientCAs: t.serverCertPool, + Certificates: t.certificates, + } + t.server.StartTLS() + } else { + t.server.Start() + } +} + +func (t *testServer) stop() { + t.server.Close() +} + +// helper function to create a cert template with a serial number and other required fields +func certTemplate() (*x509.Certificate, error) { + // generate a random serial number (a real cert authority would have some logic behind this) + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return nil, errors.New("failed to generate serial number: " + err.Error()) + } + + tmpl := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{Organization: []string{"OPA"}}, + SignatureAlgorithm: x509.SHA256WithRSA, + NotBefore: time.Now(), + NotAfter: time.Now().Add(time.Hour), // valid for an hour + BasicConstraintsValid: true, + } + return &tmpl, nil +} + +func createCert(template, parent *x509.Certificate, pub any, parentPriv any) ( + cert *x509.Certificate, certPEM []byte, err error) { + + certDER, err := x509.CreateCertificate(rand.Reader, template, parent, pub, parentPriv) + if err != nil { + return + } + // parse the resulting certificate so we can use it again + cert, err = x509.ParseCertificate(certDER) + if err != nil { + return + } + // PEM encode the certificate (this is a standard TLS encoding) + b := pem.Block{Type: "CERTIFICATE", Bytes: certDER} + certPEM = pem.EncodeToMemory(&b) + return +} + +func createRootCert(rootKey *rsa.PrivateKey) (cert *x509.Certificate, certPEM []byte, err error) { + rootCertTmpl, err := certTemplate() + if err != nil { + return nil, nil, err + } + rootCertTmpl.IsCA = true + rootCertTmpl.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature + rootCertTmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} + rootCertTmpl.IPAddresses = []net.IP{net.ParseIP("127.0.0.1")} + + return createCert(rootCertTmpl, rootCertTmpl, &rootKey.PublicKey, rootKey) +} + +func getTestServerWithTimeout(d time.Duration) (baseURL string, teardownFn func()) { + mux := http.NewServeMux() + ts := httptest.NewServer(mux) + + mux.HandleFunc("/v1/test", func(w http.ResponseWriter, _ *http.Request) { + time.Sleep(d * time.Second) + w.WriteHeader(http.StatusOK) + }) + return ts.URL, ts.Close +} + +func mockAuthPluginLookup(name string) HTTPAuthPlugin { + if name == "my_plugin" { + return &myPluginMock{} + } + return nil +} + +type myPluginMock struct{} + +func (*myPluginMock) NewClient(c Config) (*http.Client, error) { + tlsConfig, err := DefaultTLSConfig(c) + if err != nil { + return nil, err + } + return DefaultRoundTripperClient( + tlsConfig, + defaultResponseHeaderTimeoutSeconds, + ), nil +} +func (*myPluginMock) Prepare(*http.Request) error { + return nil +} + +// Note(philipc): Cannot run this test in parallel, due to the t.Setenv calls +// from one of its helper methods. +func TestOauth2ClientCredentialsGrantTypeWithKms(t *testing.T) { + + // DER-encoded object from KMS as explained here: https://docs.aws.amazon.com/kms/latest/APIReference/API_Sign.html#API_Sign_ResponseSyntax + derEncodeSignature := []byte{48, 68, 2, 32, 84, 124, 17, 255, 68, 181, 189, 159, 77, 235, 242, 88, 85, 139, 84, 111, 204, 108, 235, 90, 128, 220, 247, 176, 215, 28, 188, 110, 19, 158, 137, 30, 2, 32, 88, 17, 176, 72, 157, 42, 1, 223, 69, 41, 225, 77, 121, 13, 117, 132, 146, 243, 45, 208, 207, 119, 233, 156, 96, 94, 192, 174, 136, 218, 206, 84} + // The signature representing the above object + jwtSignature := "VHwR_0S1vZ9N6_JYVYtUb8xs61qA3Pew1xy8bhOeiR5YEbBInSoB30Up4U15DXWEkvMt0M936ZxgXsCuiNrOVA" + + ts := testServer{t: t, expBearerToken: "token_1"} + ts.start() + defer ts.stop() + + ots := oauth2TestServer{ + t: t, + tokenTTL: 300, + expScope: &[]string{"scope1", "scope2"}, + expJwtCredential: true, + expAlgorithm: jwa.ES256, + expGrantType: grantTypeClientCredentials, + expSignature: jwtSignature, + } + ots.start() + defer ots.stop() + + kmsServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var signRequest = &aws.KMSSignRequest{} + if r.Body != nil { + bodyBytes, err := io.ReadAll(r.Body) + if err != nil { + t.Fatalf("failed to read kms sign request = %v", err) + } + defer r.Body.Close() + err = json.Unmarshal(bodyBytes, signRequest) + if err != nil { + t.Fatalf("failed to unmarshall kms sign request = %v", err) + } + } + responseFmt := `{"KeyId": "%s", "Signature": "%s", "SigningAlgorithm": "%s"}` + responsePayload := fmt.Sprintf(responseFmt, signRequest.KeyID, base64.StdEncoding.EncodeToString(derEncodeSignature), signRequest.SigningAlgorithm) + if _, err := io.WriteString(w, responsePayload); err != nil { + t.Fatalf("io.WriteString(w, payload) = %v", err) + } + })) + defer kmsServer.Close() + + logger := logging.New() + logger.SetLevel(logging.Debug) + + kms := aws.NewKMSWithURLClient(kmsServer.URL, kmsServer.Client(), logger) + client := newOauth2KmsClientCredentialsTestClient(t, &ts, &ots, kms) + ctx := context.Background() + _, err := client.Do(ctx, "GET", "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, err = client.Do(ctx, "GET", "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +// Create client to test ClientCredentials grant using KMS +func newOauth2KmsClientCredentialsTestClient(t *testing.T, ts *testServer, ots *oauth2TestServer, kms *aws.KMS) *Client { + config := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + "allow_insecure_tls": true, + "credentials": { + "oauth2": { + "token_url": "%v/token", + "grant_type": %q, + "scopes": ["scope1", "scope2"], + "additional_claims": { + "aud": "test-audience", + "iss": "client-one" + }, + "aws_kms": { + "name": "arn:aws:kms:eu-west-1:account_no:key/key_id", + "algorithm": "ECDSA_SHA_256" + }, + "aws_signing": { + "service": "kms", + "environment_credentials": { + "aws_default_region": "eu-west-1" + } + } + } + } + }`, ts.server.URL, ots.server.URL, grantTypeClientCredentials) + + // Setup variables for environment_credentials{} + t.Setenv(accessKeyEnvVar, accessKeyEnvVar) + t.Setenv(secretKeyEnvVar, secretKeyEnvVar) + t.Setenv(awsRegionEnvVar, awsRegionEnvVar) + + client, err := New([]byte(config), map[string]*keys.Config{}) + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if _, err := client.config.Credentials.OAuth2.NewClient(client.config); err != nil { + t.Fatalf("OAuth2.NewClient() = %q", err) + } + + if client.config.Credentials.OAuth2.AWSSigningPlugin.kmsSignPlugin == nil { + t.Errorf("OAuth2.AWSSigningPlugin.kmsSignPlugin isn't setup") + } + + // setup fake KMS signer + client.config.Credentials.OAuth2.AWSSigningPlugin.kmsSignPlugin.kms = kms + return &client +} + +func TestOauth2ClientCredentialsGrantTypeWithKeyVault(t *testing.T) { + sign := "KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30" + ts := testServer{t: t, expBearerToken: "token_1"} + ts.start() + defer ts.stop() + + ots := oauth2TestServer{ + t: t, + tokenTTL: 300, + expScope: &[]string{"scope1", "scope2"}, + expJwtCredential: true, + expAlgorithm: "ES256", + expGrantType: grantTypeClientCredentials, + expSignature: sign, + } + ots.start() + defer ots.stop() + + kvServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Body == nil { + t.Fatal("got keyvault sign request but body is missing") + } + defer r.Body.Close() + var signRequest kvRequest + bodyBytes, err := io.ReadAll(r.Body) + if err != nil { + t.Fatalf("failed to read keyvault sign request = %v", err) + } + + err = json.Unmarshal(bodyBytes, &signRequest) + if err != nil { + t.Fatalf("failed to unmarshal keyvault sign request = %v", err) + } + + resp, err := json.Marshal(kvResponse{KID: "some-KID", Value: sign}) + if err != nil { + t.Fatalf("json.Marshal(kvResponse{}) = %v", err) + } + w.WriteHeader(200) + _, err = w.Write(resp) + if err != nil { + t.Fatalf("w.Write(resp) = %v", err) + } + })) + defer kvServer.Close() + + tokenerServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + b, err := json.Marshal(azureManagedIdentitiesToken{AccessToken: "token1"}) + if err != nil { + t.Fatalf("json.Marshal(azureManagedIdentitiesToken{}) = %v", err) + } + w.WriteHeader(200) + _, err = w.Write(b) + if err != nil { + t.Fatalf("w.Write(b) = %v", err) + } + })) + + kvURL, err := url.Parse(kvServer.URL) + if err != nil { + t.Fatalf("url.Parse(kvServer.URL) = %v", err) + } + tokenerServerURL, err := url.Parse(tokenerServer.URL) + if err != nil { + t.Fatalf("url.Parse(tokenerServer.URL) = %v", err) + } + fakeCfg := azureKeyVaultConfig{ + URL: kvURL, + Alg: "ES256", + } + + fakePlugin := &azureSigningAuthPlugin{ + MIAuthPlugin: &azureManagedIdentitiesAuthPlugin{Endpoint: tokenerServerURL.String()}, + Service: "keyvault", + keyVaultConfig: &fakeCfg, + keyVaultSignPlugin: &azureKeyVaultSignPlugin{ + tokener: func() (string, error) { return "azure_tkn", nil }, + config: fakeCfg, + }, + } + + client := newOauth2AzureKVClient(t, &ts, &ots, tokenerServer, fakePlugin) + _, err = client.Do(context.Background(), http.MethodGet, "test") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } +} + +func newOauth2AzureKVClient(t *testing.T, ts *testServer, ots *oauth2TestServer, tkn *httptest.Server, azureSign *azureSigningAuthPlugin) *Client { + cfg := fmt.Sprintf(`{ + "name": "foo", + "url": %q, + "allow_insecure_tls": true, + "credentials": { + "oauth2": { + "token_url": "%v/token", + "grant_type": %q, + "azure_keyvault": { + "key": "tester-key", + "key_algorithm": "ES256", + "vault": "my-secret-kv" + }, + "azure_signing": { + "service": "keyvault", + "azure_managed_identity": { + "endpoint": "%v" + } + }, + "scopes": ["scope1", "scope2"], + "additional_claims": { + "aud": "test-audience", + "iss": "client-one" + } + } + } + }`, ts.server.URL, ots.server.URL, grantTypeClientCredentials, tkn.URL) + client, err := New([]byte(cfg), map[string]*keys.Config{}) + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if _, err := client.config.Credentials.OAuth2.NewClient(client.config); err != nil { + t.Fatalf("Oauth2.NewClient() = %q", err) + } + + if client.config.Credentials.OAuth2.AzureSigningPlugin.keyVaultSignPlugin == nil { + t.Errorf("Oauth2.AzureSigningPlugin.keyVaultSignPlugin isn't setup") + } + + // setup fake KV config and signer + client.config.Credentials.OAuth2.AzureKeyVault = azureSign.keyVaultConfig + client.config.Credentials.OAuth2.AzureSigningPlugin = azureSign + return &client +} diff --git a/third_party/opa/v1/plugins/server/decoding/config.go b/third_party/opa/v1/plugins/server/decoding/config.go new file mode 100644 index 000000000000..74e508817bf5 --- /dev/null +++ b/third_party/opa/v1/plugins/server/decoding/config.go @@ -0,0 +1,102 @@ +// Package decoding implements the configuration side of the upgraded gzip +// decompression framework. The original work only enabled gzip decoding for +// a few endpoints-- here we enable if for all of OPA. Additionally, we provide +// some new defensive configuration options: max_length, and gzip.max_length. +// These allow rejecting requests that indicate their contents are larger than +// the size limits. +// +// The request handling pipeline now looks roughly like this: +// +// Request -> MaxBytesReader(Config.MaxLength) -> ir.CopyN(dest, req, Gzip.MaxLength) +// +// The intent behind this design is to improve how OPA handles large and/or +// malicious requests, compressed or otherwise. The benefit of being a little +// more strict in what we allow is that we can now use "riskier", but +// dramatically more performant techniques, like preallocating content buffers +// for gzipped data. This also should help OPAs in limited memory situations. +package decoding + +import ( + "errors" + + "github.com/open-policy-agent/opa/v1/util" +) + +var ( + defaultMaxRequestLength = int64(268435456) // 256 MB + defaultGzipMaxContentLength = int64(536870912) // 512 MB +) + +// Config represents the configuration for the Server.Decoding settings +type Config struct { + MaxLength *int64 `json:"max_length,omitempty"` // maximum request size that will be read, regardless of compression. + Gzip *Gzip `json:"gzip,omitempty"` +} + +// Gzip represents the configuration for the Server.Decoding.Gzip settings +type Gzip struct { + MaxLength *int64 `json:"max_length,omitempty"` // Max number of bytes allowed to be read from the decompressor. +} + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder struct { + raw []byte +} + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the server config +func NewConfigBuilder() *ConfigBuilder { + return &ConfigBuilder{} +} + +// WithBytes sets the raw server config +func (b *ConfigBuilder) WithBytes(config []byte) *ConfigBuilder { + b.raw = config + return b +} + +// Parse returns a valid Config object with defaults injected. +func (b *ConfigBuilder) Parse() (*Config, error) { + if b.raw == nil { + defaultConfig := &Config{ + MaxLength: &defaultMaxRequestLength, + Gzip: &Gzip{ + MaxLength: &defaultGzipMaxContentLength, + }, + } + return defaultConfig, nil + } + + var result Config + + if err := util.Unmarshal(b.raw, &result); err != nil { + return nil, err + } + + return &result, result.validateAndInjectDefaults() +} + +// validateAndInjectDefaults populates defaults if the fields are nil, then +// validates the config values. +func (c *Config) validateAndInjectDefaults() error { + if c.MaxLength == nil { + c.MaxLength = &defaultMaxRequestLength + } + + if c.Gzip == nil { + c.Gzip = &Gzip{ + MaxLength: &defaultGzipMaxContentLength, + } + } + if c.Gzip.MaxLength == nil { + c.Gzip.MaxLength = &defaultGzipMaxContentLength + } + + if *c.MaxLength <= 0 { + return errors.New("invalid value for server.decoding.max_length field, should be a positive number") + } + if *c.Gzip.MaxLength <= 0 { + return errors.New("invalid value for server.decoding.gzip.max_length field, should be a positive number") + } + + return nil +} diff --git a/third_party/opa/v1/plugins/server/decoding/config_test.go b/third_party/opa/v1/plugins/server/decoding/config_test.go new file mode 100644 index 000000000000..f8751092f3b7 --- /dev/null +++ b/third_party/opa/v1/plugins/server/decoding/config_test.go @@ -0,0 +1,108 @@ +package decoding + +import ( + "fmt" + "testing" +) + +func TestConfigValidation(t *testing.T) { + tests := []struct { + input string + wantErr bool + }{ + { + input: `{}`, + wantErr: false, + }, + { + input: `{"gzip": {"max_length": "not-a-number"}}`, + wantErr: true, + }, + { + input: `{"gzip": {max_length": 42}}`, + wantErr: false, + }, + { + input: `{"gzip":{"max_length": "42"}}`, + wantErr: true, + }, + { + input: `{"gzip":{"max_length": 0}}`, + wantErr: true, + }, + { + input: `{"gzip":{"max_length": -10}}`, + wantErr: true, + }, + { + input: `{"gzip":{"random_key": 0}}`, + wantErr: false, + }, + { + input: `{"gzip": {"max_length": -10}}`, + wantErr: true, + }, + { + input: `{"max_length": "not-a-number"}`, + wantErr: true, + }, + { + input: `{"gzip":{}}`, + wantErr: false, + }, + { + input: `{"max_length": "not-a-number", "gzip":{}}`, + wantErr: true, + }, + { + input: `{"max_length": 42, "gzip":{"max_length": 42}}`, + wantErr: false, + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("TestConfigValidation_case_%d", i), func(t *testing.T) { + _, err := NewConfigBuilder().WithBytes([]byte(test.input)).Parse() + if err != nil && !test.wantErr { + t.Fatalf("Unexpected error: %s", err.Error()) + } + if err == nil && test.wantErr { + t.Fail() + } + }) + } +} + +func TestConfigValue(t *testing.T) { + tests := []struct { + input string + maxLengthExpectedValue int64 + gzipMaxLengthExpectedValue int64 + }{ + { + input: `{}`, + maxLengthExpectedValue: 268435456, + gzipMaxLengthExpectedValue: 536870912, + }, + { + input: `{"max_length": 5, "gzip":{"max_length": 42}}`, + maxLengthExpectedValue: 5, + gzipMaxLengthExpectedValue: 42, + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("TestConfigValue_case_%d", i), func(t *testing.T) { + config, err := NewConfigBuilder().WithBytes([]byte(test.input)).Parse() + if err != nil { + t.Fatalf("Error building configuration: %s", err.Error()) + } + if *config.MaxLength != test.maxLengthExpectedValue { + t.Fatalf("Unexpected config value for max_length (exp/actual): %d, %d", test.maxLengthExpectedValue, *config.MaxLength) + } + if *config.Gzip.MaxLength != test.gzipMaxLengthExpectedValue { + t.Fatalf("Unexpected config value for gzip.max_length (exp/actual): %d, %d", test.gzipMaxLengthExpectedValue, *config.Gzip.MaxLength) + } + }) + } +} diff --git a/third_party/opa/v1/plugins/server/encoding/config.go b/third_party/opa/v1/plugins/server/encoding/config.go new file mode 100644 index 000000000000..f2bb75a7e491 --- /dev/null +++ b/third_party/opa/v1/plugins/server/encoding/config.go @@ -0,0 +1,91 @@ +package encoding + +import ( + "compress/gzip" + "errors" + + "github.com/open-policy-agent/opa/v1/util" +) + +var defaultGzipMinLength = 1024 +var defaultGzipCompressionLevel = gzip.BestCompression + +// Config represents the configuration for the Server.Encoding settings +type Config struct { + Gzip *Gzip `json:"gzip,omitempty"` +} + +// Gzip represents the configuration for the Server.Encoding.Gzip settings +type Gzip struct { + MinLength *int `json:"min_length,omitempty"` // the minimum length of a response that will be gzipped + CompressionLevel *int `json:"compression_level,omitempty"` // the compression level for gzip +} + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder struct { + raw []byte +} + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the server config +func NewConfigBuilder() *ConfigBuilder { + return &ConfigBuilder{} +} + +// WithBytes sets the raw server config +func (b *ConfigBuilder) WithBytes(config []byte) *ConfigBuilder { + b.raw = config + return b +} + +// Parse returns a valid Config object with defaults injected. +func (b *ConfigBuilder) Parse() (*Config, error) { + if b.raw == nil { + defaultConfig := &Config{ + Gzip: &Gzip{ + MinLength: &defaultGzipMinLength, + CompressionLevel: &defaultGzipCompressionLevel, + }, + } + return defaultConfig, nil + } + + var result Config + + if err := util.Unmarshal(b.raw, &result); err != nil { + return nil, err + } + + return &result, result.validateAndInjectDefaults() +} + +func (c *Config) validateAndInjectDefaults() error { + if c.Gzip == nil { + c.Gzip = &Gzip{ + MinLength: &defaultGzipMinLength, + CompressionLevel: &defaultGzipCompressionLevel, + } + } + if c.Gzip.MinLength == nil { + c.Gzip.MinLength = &defaultGzipMinLength + } + + if c.Gzip.CompressionLevel == nil { + c.Gzip.CompressionLevel = &defaultGzipCompressionLevel + } + + if *c.Gzip.MinLength <= 0 { + return errors.New("invalid value for server.encoding.gzip.min_length field, should be a positive number") + } + + acceptedCompressionLevels := map[int]bool{ + gzip.NoCompression: true, + gzip.BestSpeed: true, + gzip.BestCompression: true, + } + _, compressionLevelAccepted := acceptedCompressionLevels[*c.Gzip.CompressionLevel] + if !compressionLevelAccepted { + return errors.New("invalid value for server.encoding.gzip.compression_level field, accepted values are 0, 1 or 9") + } + + return nil +} diff --git a/third_party/opa/v1/plugins/server/encoding/config_test.go b/third_party/opa/v1/plugins/server/encoding/config_test.go new file mode 100644 index 000000000000..d8aa7482a396 --- /dev/null +++ b/third_party/opa/v1/plugins/server/encoding/config_test.go @@ -0,0 +1,105 @@ +package encoding + +import ( + "fmt" + "testing" +) + +func TestConfigValidation(t *testing.T) { + tests := []struct { + input string + wantErr bool + }{ + { + input: `{}`, + wantErr: false, + }, + { + input: `{"gzip": {"min_length": "not-a-number"}}`, + wantErr: true, + }, + { + input: `{"gzip": {min_length": 42}}`, + wantErr: false, + }, + { + input: `{"gzip":{"min_length": "42"}}`, + wantErr: true, + }, + { + input: `{"gzip":{"min_length": 0}}`, + wantErr: true, + }, + { + input: `{"gzip":{"min_length": -10}}`, + wantErr: true, + }, + { + input: `{"gzip":{"random_key": 0}}`, + wantErr: false, + }, + { + input: `{"gzip": {"min_length": -10, "compression_level": 13}}`, + wantErr: true, + }, + { + input: `{"gzip":{"compression_level": "not-an-number"}}`, + wantErr: true, + }, + { + input: `{"gzip":{"compression_level": 1}}`, + wantErr: false, + }, + { + input: `{"gzip":{"compression_level": 13}}`, + wantErr: true, + }, + { + input: `{"gzip":{"min_length": 42, "compression_level": 9}}`, + wantErr: false, + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("TestConfigValidation_case_%d", i), func(t *testing.T) { + _, err := NewConfigBuilder().WithBytes([]byte(test.input)).Parse() + if err != nil && !test.wantErr { + t.Fail() + } + if err == nil && test.wantErr { + t.Fail() + } + }) + } +} + +func TestConfigValue(t *testing.T) { + tests := []struct { + input string + minLengthExpectedValue int + compressionLevelExpectedValue int + }{ + { + input: `{}`, + minLengthExpectedValue: 1024, + compressionLevelExpectedValue: 9, + }, + { + input: `{"gzip":{"min_length": 42, "compression_level": 1}}`, + minLengthExpectedValue: 42, + compressionLevelExpectedValue: 1, + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("TestConfigValue_case_%d", i), func(t *testing.T) { + config, err := NewConfigBuilder().WithBytes([]byte(test.input)).Parse() + if err != nil { + t.Fail() + } + if *config.Gzip.MinLength != test.minLengthExpectedValue || *config.Gzip.CompressionLevel != test.compressionLevelExpectedValue { + t.Fail() + } + }) + } +} diff --git a/third_party/opa/v1/plugins/server/metrics/config.go b/third_party/opa/v1/plugins/server/metrics/config.go new file mode 100644 index 000000000000..e3eff7edf8e3 --- /dev/null +++ b/third_party/opa/v1/plugins/server/metrics/config.go @@ -0,0 +1,93 @@ +package metrics + +import ( + "github.com/open-policy-agent/opa/v1/util" +) + +var defaultHTTPRequestBuckets = []float64{ + 1e-6, // 1 microsecond + 5e-6, + 1e-5, + 5e-5, + 1e-4, + 5e-4, + 1e-3, // 1 millisecond + 0.01, + 0.1, + 1, // 1 second +} + +// Config represents the configuration for the Server.Metrics settings +type Config struct { + Prom *Prom `json:"prom,omitempty"` +} + +// Prom represents the configuration for the Server.Metrics.Prom settings +type Prom struct { + HTTPRequestDurationSeconds *HTTPRequestDurationSeconds `json:"http_request_duration_seconds,omitempty"` +} + +// HTTPRequestDurationSeconds represents the configuration for the Server.Metrics.Prom.HTTPRequestDurationSeconds settings +type HTTPRequestDurationSeconds struct { + Buckets []float64 `json:"buckets,omitempty"` // the float64 array of buckets representing seconds or division of a second +} + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder struct { + raw []byte +} + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the server config +func NewConfigBuilder() *ConfigBuilder { + return &ConfigBuilder{} +} + +// WithBytes sets the raw server config +func (b *ConfigBuilder) WithBytes(config []byte) *ConfigBuilder { + b.raw = config + return b +} + +// Parse returns a valid Config object with defaults injected. +func (b *ConfigBuilder) Parse() (*Config, error) { + if b.raw == nil { + defaultConfig := &Config{ + Prom: &Prom{ + HTTPRequestDurationSeconds: &HTTPRequestDurationSeconds{ + Buckets: defaultHTTPRequestBuckets, + }, + }, + } + return defaultConfig, nil + } + + var result Config + + if err := util.Unmarshal(b.raw, &result); err != nil { + return nil, err + } + + return &result, result.validateAndInjectDefaults() +} + +func (c *Config) validateAndInjectDefaults() error { + if c.Prom == nil { + c.Prom = &Prom{ + HTTPRequestDurationSeconds: &HTTPRequestDurationSeconds{ + Buckets: defaultHTTPRequestBuckets, + }, + } + } + + if c.Prom.HTTPRequestDurationSeconds == nil { + c.Prom.HTTPRequestDurationSeconds = &HTTPRequestDurationSeconds{ + Buckets: defaultHTTPRequestBuckets, + } + } + + if c.Prom.HTTPRequestDurationSeconds.Buckets == nil { + c.Prom.HTTPRequestDurationSeconds.Buckets = defaultHTTPRequestBuckets + } + + return nil +} diff --git a/third_party/opa/v1/plugins/server/metrics/config_test.go b/third_party/opa/v1/plugins/server/metrics/config_test.go new file mode 100644 index 000000000000..c776d22adc35 --- /dev/null +++ b/third_party/opa/v1/plugins/server/metrics/config_test.go @@ -0,0 +1,129 @@ +package metrics + +import ( + "fmt" + "testing" +) + +func TestConfigValidation(t *testing.T) { + tests := []struct { + input string + wantErr bool + }{ + { + input: `{}`, + wantErr: false, + }, + { + input: `{"prom": {}}`, + wantErr: false, + }, + { + input: `{"prom": {"http_request_duration_seconds": {}}}`, + wantErr: false, + }, + { + input: `{"prom": {"http_request_duration_seconds": {"buckets": []}}}`, + wantErr: false, + }, + + { + input: `{"prom": {"http_request_duration_seconds": {"buckets": ["not-a-array"]}}}`, + wantErr: true, + }, + { + input: `{"prom": {"http_request_duration_seconds": {"buckets": [1]}}}`, + wantErr: false, + }, + { + input: `{"prom": {"http_request_duration_seconds": {"buckets": "1"}}}`, + wantErr: true, + }, + { + input: `{"prom": {"http_request_duration_seconds": {"buckets": [0.001, "1", "2"]}}}`, + wantErr: true, + }, + { + input: `{"prom": {"http_request_duration_seconds": {"buckets": ["one", "two", "three"]}}}`, + wantErr: true, + }, + { + input: `{"prom": {"http_request_duration_seconds": {"buckets": ["0.1", "0.2", "0.3", "4"]}}}`, + wantErr: true, + }, + { + input: `{"prom": {"random_key": 0}}`, + wantErr: false, + }, + { + input: `{"prom": {"http_request_duration_seconds": {"random_key": 0}}}`, + wantErr: false, + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("TestConfigValidation_case_%d", i), func(t *testing.T) { + _, err := NewConfigBuilder().WithBytes([]byte(test.input)).Parse() + if err != nil && !test.wantErr { + t.Fail() + } + if err == nil && test.wantErr { + t.Fail() + } + }) + } +} + +func TestConfigValue(t *testing.T) { + tests := []struct { + input string + expectedValue []float64 + }{ + { + input: `{}`, + expectedValue: defaultHTTPRequestBuckets, + }, + { + input: `{"prom": {}}`, + expectedValue: defaultHTTPRequestBuckets, + }, + { + input: `{"prom": {"http_request_duration_seconds": {}}}`, + expectedValue: defaultHTTPRequestBuckets, + }, + { + input: `{"prom": {"http_request_duration_seconds": {"buckets": []}}}`, + expectedValue: []float64{}, + }, + { + input: `{"prom": {"http_request_duration_seconds": {"buckets":[0.1, 0.2, 0.3, 4]}}}`, + expectedValue: []float64{0.1, 0.2, 0.3, 4}, + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("TestConfigValue_case_%d", i), func(t *testing.T) { + config, err := NewConfigBuilder().WithBytes([]byte(test.input)).Parse() + if err != nil { + t.Fail() + } + if !valuesAreEqual(config.Prom.HTTPRequestDurationSeconds.Buckets, test.expectedValue) { + t.Fail() + } + }) + } +} + +func valuesAreEqual(a []float64, b []float64) bool { + if len(a) != len(b) { + return false + } + + for i, v := range a { + if v != b[i] { + return false + } + } + + return true +} diff --git a/third_party/opa/v1/plugins/status/metrics.go b/third_party/opa/v1/plugins/status/metrics.go new file mode 100644 index 000000000000..9141ed31c2dc --- /dev/null +++ b/third_party/opa/v1/plugins/status/metrics.go @@ -0,0 +1,174 @@ +package status + +import ( + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/version" + "github.com/prometheus/client_golang/prometheus" +) + +var defaultBundleLoadStageBuckets = prometheus.ExponentialBuckets(1000, 2, 20) + +type PrometheusConfig struct { + Collectors *Collectors `json:"collectors,omitempty"` +} + +type Collectors struct { + BundleLoadDurationNanoseconds *BundleLoadDurationNanoseconds `json:"bundle_loading_duration_ns,omitempty"` +} + +func injectDefaultDurationBuckets(p *PrometheusConfig) *PrometheusConfig { + if p != nil && p.Collectors != nil && p.Collectors.BundleLoadDurationNanoseconds != nil && p.Collectors.BundleLoadDurationNanoseconds.Buckets != nil { + return p + } + + return &PrometheusConfig{ + Collectors: &Collectors{ + BundleLoadDurationNanoseconds: &BundleLoadDurationNanoseconds{ + Buckets: defaultBundleLoadStageBuckets, + }, + }, + } +} + +// collectors is a list of all collectors maintained by the status plugin. +// Note: when adding a new collector, make sure to also add it to this list, +// or it won't survive status plugin reconfigure events. +type collectors struct { + opaInfo prometheus.Gauge + pluginStatus *prometheus.GaugeVec + loaded *prometheus.CounterVec + failLoad *prometheus.CounterVec + lastRequest *prometheus.GaugeVec + lastSuccessfulActivation *prometheus.GaugeVec + lastSuccessfulDownload *prometheus.GaugeVec + lastSuccessfulRequest *prometheus.GaugeVec + bundleLoadDuration *prometheus.HistogramVec +} + +func newCollectors(prometheusConfig *PrometheusConfig) *collectors { + opaInfo := prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "opa_info", + Help: "Information about the OPA environment.", + ConstLabels: map[string]string{"version": version.Version}, + }, + ) + opaInfo.Set(1) // only publish once + + pluginStatus := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "plugin_status_gauge", + Help: "Gauge for the plugin by status.", + }, + []string{"name", "status"}, + ) + loaded := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "bundle_loaded_counter", + Help: "Counter for the bundle loaded.", + }, + []string{"name"}, + ) + failLoad := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "bundle_failed_load_counter", + Help: "Counter for the failed bundle load.", + }, + []string{"name", "code", "message"}, + ) + lastRequest := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "last_bundle_request", + Help: "Gauge for the last bundle request.", + }, + []string{"name"}, + ) + lastSuccessfulActivation := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "last_success_bundle_activation", + Help: "Gauge for the last success bundle activation.", + }, + []string{"name", "active_revision"}, + ) + lastSuccessfulDownload := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "last_success_bundle_download", + Help: "Gauge for the last success bundle download.", + }, + []string{"name"}, + ) + lastSuccessfulRequest := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "last_success_bundle_request", + Help: "Gauge for the last success bundle request.", + }, + []string{"name"}, + ) + + bundleLoadDuration := newBundleLoadDurationCollector(prometheusConfig) + + return &collectors{ + opaInfo: opaInfo, + pluginStatus: pluginStatus, + loaded: loaded, + failLoad: failLoad, + lastRequest: lastRequest, + lastSuccessfulActivation: lastSuccessfulActivation, + lastSuccessfulDownload: lastSuccessfulDownload, + lastSuccessfulRequest: lastSuccessfulRequest, + bundleLoadDuration: bundleLoadDuration, + } +} + +func newBundleLoadDurationCollector(prometheusConfig *PrometheusConfig) *prometheus.HistogramVec { + return prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "bundle_loading_duration_ns", + Help: "Histogram for the bundle loading duration by stage.", + Buckets: prometheusConfig.Collectors.BundleLoadDurationNanoseconds.Buckets, + }, []string{"name", "stage"}) +} + +func (c *collectors) RegisterAll(register prometheus.Registerer, logger logging.Logger) { + if register == nil { + return + } + for _, collector := range c.toList() { + if err := register.Register(collector); err != nil { + logger.Error("Status metric failed to register on prometheus :%v.", err) + } + } +} + +func (c *collectors) UnregisterAll(register prometheus.Registerer) { + if register == nil { + return + } + + for _, collector := range c.toList() { + register.Unregister(collector) + } +} + +func (c *collectors) ReregisterBundleLoadDuration(register prometheus.Registerer, config *PrometheusConfig, logger logging.Logger) { + logger.Debug("Re-register bundleLoadDuration collector") + register.Unregister(c.bundleLoadDuration) + c.bundleLoadDuration = newBundleLoadDurationCollector(config) + if err := register.Register(c.bundleLoadDuration); err != nil { + logger.Error("Status metric failed to register bundleLoadDuration collector on prometheus :%v.", err) + } +} + +// helper function +func (c *collectors) toList() []prometheus.Collector { + return []prometheus.Collector{ + c.opaInfo, + c.pluginStatus, + c.loaded, + c.failLoad, + c.lastRequest, + c.lastSuccessfulActivation, + c.lastSuccessfulDownload, + c.lastSuccessfulRequest, + c.bundleLoadDuration, + } +} diff --git a/third_party/opa/v1/plugins/status/plugin.go b/third_party/opa/v1/plugins/status/plugin.go new file mode 100644 index 000000000000..f509f096a0f7 --- /dev/null +++ b/third_party/opa/v1/plugins/status/plugin.go @@ -0,0 +1,612 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package status implements status reporting. +package status + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "maps" + "net/http" + "reflect" + "slices" + + lstat "github.com/open-policy-agent/opa/v1/plugins/logs/status" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/plugins/bundle" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + statusBufferLimit = int64(1) + statusBufferDropCounterName = "status_dropped_buffer_limit_exceeded" +) + +// Logger defines the interface for status plugins. +type Logger interface { + plugins.Plugin + + Log(context.Context, *UpdateRequestV1) error +} + +// UpdateRequestV1 represents the status update message that OPA sends to +// remote HTTP endpoints. +type UpdateRequestV1 struct { + Labels map[string]string `json:"labels"` + Bundle *bundle.Status `json:"bundle,omitempty"` // Deprecated: Use bulk `bundles` status updates instead + Bundles map[string]*bundle.Status `json:"bundles,omitempty"` + Discovery *bundle.Status `json:"discovery,omitempty"` + DecisionLogs *lstat.Status `json:"decision_logs,omitempty"` + Metrics map[string]any `json:"metrics,omitempty"` + Plugins map[string]*plugins.Status `json:"plugins,omitempty"` +} + +// Plugin implements status reporting. Updates can be triggered by the caller. +type Plugin struct { + manager *plugins.Manager + config Config + bundleCh chan bundle.Status // Deprecated: Use bulk bundle status updates instead + lastBundleStatus *bundle.Status // Deprecated: Use bulk bundle status updates instead + bulkBundleCh chan map[string]*bundle.Status + lastBundleStatuses map[string]*bundle.Status + discoCh chan bundle.Status + lastDiscoStatus *bundle.Status + pluginStatusCh chan map[string]*plugins.Status + decisionLogsCh chan lstat.Status + lastDecisionLogsStatus *lstat.Status + lastPluginStatuses map[string]*plugins.Status + queryCh chan chan *UpdateRequestV1 + stop chan chan struct{} + reconfig chan reconfigure + metrics metrics.Metrics + logger logging.Logger + trigger chan trigger + collectors *collectors +} + +// Config contains configuration for the plugin. +type Config struct { + Plugin *string `json:"plugin"` + Service string `json:"service"` + PartitionName string `json:"partition_name,omitempty"` + ConsoleLogs bool `json:"console"` + Prometheus bool `json:"prometheus"` + PrometheusConfig *PrometheusConfig `json:"prometheus_config,omitempty"` + Trigger *plugins.TriggerMode `json:"trigger,omitempty"` // trigger mode +} + +// BundleLoadDurationNanoseconds represents the configuration for the status.prometheus_config.bundle_loading_duration_ns settings +type BundleLoadDurationNanoseconds struct { + Buckets []float64 `json:"buckets,omitempty"` // the float64 array of buckets representing nanoseconds or multiple of nanoseconds +} + +type reconfigure struct { + config any + done chan struct{} +} + +type trigger struct { + ctx context.Context + done chan error +} + +func (c *Config) validateAndInjectDefaults(services []string, pluginsList []string, trigger *plugins.TriggerMode) error { + if c.Plugin != nil && !slices.Contains(pluginsList, *c.Plugin) { + return fmt.Errorf("invalid plugin name %q in status", *c.Plugin) + } else if c.Service == "" && len(services) != 0 && !(c.ConsoleLogs || c.Prometheus) { + // For backwards compatibility allow defaulting to the first + // service listed, but only if console logging is disabled. If enabled + // we can't tell if the deployer wanted to use only console logs or + // both console logs and the default service option. + c.Service = services[0] + } else if c.Service != "" && !slices.Contains(services, c.Service) { + return fmt.Errorf("invalid service name %q in status", c.Service) + } + + t, err := plugins.ValidateAndInjectDefaultsForTriggerMode(trigger, c.Trigger) + if err != nil { + return fmt.Errorf("invalid status config: %w", err) + } + c.Trigger = t + + c.PrometheusConfig = injectDefaultDurationBuckets(c.PrometheusConfig) + + return nil +} + +// ParseConfig validates the config and injects default values. +func ParseConfig(config []byte, services []string, pluginsList []string) (*Config, error) { + t := plugins.DefaultTriggerMode + return NewConfigBuilder().WithBytes(config).WithServices(services).WithPlugins(pluginsList).WithTriggerMode(&t).Parse() +} + +// ConfigBuilder assists in the construction of the plugin configuration. +type ConfigBuilder struct { + raw []byte + services []string + plugins []string + trigger *plugins.TriggerMode +} + +// NewConfigBuilder returns a new ConfigBuilder to build and parse the plugin config. +func NewConfigBuilder() *ConfigBuilder { + return &ConfigBuilder{} +} + +// WithBytes sets the raw plugin config. +func (b *ConfigBuilder) WithBytes(config []byte) *ConfigBuilder { + b.raw = config + return b +} + +// WithServices sets the services that implement control plane APIs. +func (b *ConfigBuilder) WithServices(services []string) *ConfigBuilder { + b.services = services + return b +} + +// WithPlugins sets the list of named plugins for status updates. +func (b *ConfigBuilder) WithPlugins(plugins []string) *ConfigBuilder { + b.plugins = plugins + return b +} + +// WithTriggerMode sets the plugin trigger mode. +func (b *ConfigBuilder) WithTriggerMode(trigger *plugins.TriggerMode) *ConfigBuilder { + b.trigger = trigger + return b +} + +// Parse validates the config and injects default values. +func (b *ConfigBuilder) Parse() (*Config, error) { + if b.raw == nil { + return nil, nil + } + + var parsedConfig Config + + if err := util.Unmarshal(b.raw, &parsedConfig); err != nil { + return nil, err + } + + if parsedConfig.Plugin == nil && parsedConfig.Service == "" && len(b.services) == 0 && !parsedConfig.ConsoleLogs && !parsedConfig.Prometheus { + // Nothing to validate or inject + return nil, nil + } + + if err := parsedConfig.validateAndInjectDefaults(b.services, b.plugins, b.trigger); err != nil { + return nil, err + } + + return &parsedConfig, nil +} + +// New returns a new Plugin with the given config. +func New(parsedConfig *Config, manager *plugins.Manager) *Plugin { + p := &Plugin{ + manager: manager, + config: *parsedConfig, + bundleCh: make(chan bundle.Status, statusBufferLimit), + bulkBundleCh: make(chan map[string]*bundle.Status, statusBufferLimit), + discoCh: make(chan bundle.Status), + decisionLogsCh: make(chan lstat.Status), + stop: make(chan chan struct{}), + reconfig: make(chan reconfigure), + // we use a buffered channel here to avoid blocking other plugins + // when updating statuses + pluginStatusCh: make(chan map[string]*plugins.Status, statusBufferLimit), + queryCh: make(chan chan *UpdateRequestV1), + logger: manager.Logger().WithFields(map[string]any{"plugin": Name}), + trigger: make(chan trigger), + collectors: newCollectors(parsedConfig.PrometheusConfig), + } + + p.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateNotReady}) + + return p +} + +// WithMetrics sets the global metrics provider to be used by the plugin. +func (p *Plugin) WithMetrics(m metrics.Metrics) *Plugin { + p.metrics = m + return p +} + +// Name identifies the plugin on manager. +const Name = "status" + +// Lookup returns the status plugin registered with the manager. +func Lookup(manager *plugins.Manager) *Plugin { + if p := manager.Plugin(Name); p != nil { + return p.(*Plugin) + } + return nil +} + +// Start starts the plugin. +func (p *Plugin) Start(ctx context.Context) error { + p.logger.Info("Starting status reporter.") + + go p.loop(ctx) + + // Setup a listener for plugin statuses, but only after starting the loop + // to prevent blocking threads pushing the plugin updates. + p.manager.RegisterPluginStatusListener(Name, p.UpdatePluginStatus) + + if p.config.Prometheus { + p.collectors.RegisterAll(p.manager.PrometheusRegister(), p.logger) + } + + // Set the status plugin's status to OK now that everything is registered and + // the loop is running. This will trigger an update on the listener with the + // current status of all the other plugins too. + p.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateOK}) + return nil +} + +// Stop stops the plugin. +func (p *Plugin) Stop(ctx context.Context) { + p.logger.Info("Stopping status reporter.") + + done := make(chan struct{}) + + // stop the status plugin loop and flush any pending status updates + go func() { + p.manager.UnregisterPluginStatusListener(Name) + d := make(chan struct{}) + p.stop <- d + <-d + p.flush(ctx) + done <- struct{}{} + }() + + // wait for status plugin to shut down gracefully or timeout + select { + case <-done: + p.manager.UpdatePluginStatus(Name, &plugins.Status{State: plugins.StateNotReady}) + case <-ctx.Done(): + switch ctx.Err() { + case context.DeadlineExceeded, context.Canceled: + p.logger.Error("Status Plugin stopped with statuses possibly not sent.") + } + } +} + +func (p *Plugin) flush(ctx context.Context) { + if !p.readBundleStatus() { + return + } + + err := p.oneShot(ctx) + if err != nil { + p.logger.Error("%v.", err) + } else { + p.logger.Info("Final status update sent successfully.") + } +} + +// UpdateBundleStatus notifies the plugin that the policy bundle was updated. +// Deprecated: Use BulkUpdateBundleStatus instead. +func (p *Plugin) UpdateBundleStatus(status bundle.Status) { + util.PushFIFO(p.bundleCh, status, p.metrics, statusBufferDropCounterName) +} + +// BulkUpdateBundleStatus notifies the plugin that the policy bundle was updated. +func (p *Plugin) BulkUpdateBundleStatus(status map[string]*bundle.Status) { + util.PushFIFO(p.bulkBundleCh, status, p.metrics, statusBufferDropCounterName) +} + +// UpdateDiscoveryStatus notifies the plugin that the discovery bundle was updated. +func (p *Plugin) UpdateDiscoveryStatus(status bundle.Status) { + p.discoCh <- status +} + +// UpdateDecisionLogsStatus notifies the plugin that status of a decision log upload event. +func (p *Plugin) UpdateDecisionLogsStatus(status lstat.Status) { + p.decisionLogsCh <- status +} + +// UpdatePluginStatus notifies the plugin that a plugin status was updated. +func (p *Plugin) UpdatePluginStatus(status map[string]*plugins.Status) { + p.pluginStatusCh <- status +} + +// Reconfigure notifies the plugin with a new configuration. +func (p *Plugin) Reconfigure(_ context.Context, config any) { + done := make(chan struct{}) + p.reconfig <- reconfigure{config: config, done: done} + <-done +} + +// Snapshot returns the current status. +func (p *Plugin) Snapshot() *UpdateRequestV1 { + ch := make(chan *UpdateRequestV1) + p.queryCh <- ch + s := <-ch + return s +} + +// Trigger can be used to control when the plugin attempts to upload +// status in manual triggering mode. +func (p *Plugin) Trigger(ctx context.Context) error { + done := make(chan error) + p.trigger <- trigger{ctx: ctx, done: done} + + select { + case err := <-done: + return err + case <-ctx.Done(): + return ctx.Err() + } +} + +func (p *Plugin) loop(ctx context.Context) { + ctx, cancel := context.WithCancel(ctx) + + for { + select { + case statuses := <-p.pluginStatusCh: + p.lastPluginStatuses = statuses + if *p.config.Trigger == plugins.TriggerPeriodic { + err := p.oneShot(ctx) + if err != nil { + p.logger.Error("%v.", err) + } else { + p.logger.Info("Status update sent successfully in response to plugin update.") + } + } + + case statuses := <-p.bulkBundleCh: + p.lastBundleStatuses = statuses + if *p.config.Trigger == plugins.TriggerPeriodic { + err := p.oneShot(ctx) + if err != nil { + p.logger.Error("%v.", err) + } else { + p.logger.Info("Status update sent successfully in response to bundle update.") + } + } + + case status := <-p.bundleCh: + p.lastBundleStatus = &status + err := p.oneShot(ctx) + if err != nil { + p.logger.Error("%v.", err) + } else { + p.logger.Info("Status update sent successfully in response to bundle update.") + } + case status := <-p.discoCh: + p.lastDiscoStatus = &status + if *p.config.Trigger == plugins.TriggerPeriodic { + err := p.oneShot(ctx) + if err != nil { + p.logger.Error("%v.", err) + } else { + p.logger.Info("Status update sent successfully in response to discovery update.") + } + } + case status := <-p.decisionLogsCh: + p.lastDecisionLogsStatus = &status + if *p.config.Trigger == plugins.TriggerPeriodic { + err := p.oneShot(ctx) + if err != nil { + p.logger.Error("%v.", err) + } else { + p.logger.Info("Status update sent successfully in response to decision log update.") + } + } + case update := <-p.reconfig: + p.reconfigure(update.config) + update.done <- struct{}{} + case respCh := <-p.queryCh: + p.readBundleStatus() + respCh <- p.snapshot() + case update := <-p.trigger: + // make sure the more recent status is registered + p.readBundleStatus() + err := p.oneShot(update.ctx) + if err != nil { + p.logger.Error("%v.", err) + if update.ctx.Err() == nil { + update.done <- err + } + } else { + p.logger.Info("Status update sent successfully in response to manual trigger.") + } + close(update.done) + case done := <-p.stop: + cancel() + done <- struct{}{} + return + } + } +} + +// readBundleStatus is a non-blocking read to make sure the latest status is received +func (p *Plugin) readBundleStatus() bool { + var changed bool + + select { + case status := <-p.pluginStatusCh: + p.lastPluginStatuses = status + changed = true + default: + } + + select { + case status := <-p.bulkBundleCh: + p.lastBundleStatuses = status + changed = true + default: + } + + select { + case status := <-p.bundleCh: + p.lastBundleStatus = &status + changed = true + default: + } + + select { + case status := <-p.discoCh: + p.lastDiscoStatus = &status + changed = true + default: + } + + select { + case status := <-p.decisionLogsCh: + p.lastDecisionLogsStatus = &status + changed = true + default: + } + + return changed +} + +func (p *Plugin) oneShot(ctx context.Context) error { + req := p.snapshot() + + if p.config.ConsoleLogs { + err := p.logUpdate(req) + if err != nil { + p.logger.Error("Failed to log to console: %v.", err) + } + } + + if p.config.Prometheus { + p.updatePrometheusMetrics(req) + } + + if p.config.Plugin != nil { + proxy, ok := p.manager.Plugin(*p.config.Plugin).(Logger) + if !ok { + return errors.New("plugin does not implement Logger interface") + } + return proxy.Log(ctx, req) + } + + if p.config.Service != "" { + resp, err := p.manager.Client(p.config.Service). + WithJSON(req). + Do(ctx, "POST", fmt.Sprintf("/status/%v", p.config.PartitionName)) + if err != nil { + return fmt.Errorf("status update failed: %w", err) + } + + defer util.Close(resp) + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return fmt.Errorf("status update failed, server replied with HTTP %v %v", resp.StatusCode, http.StatusText(resp.StatusCode)) + } + } + return nil +} + +func (p *Plugin) reconfigure(config any) { + newConfig := config.(*Config) + + if reflect.DeepEqual(p.config, *newConfig) { + p.logger.Debug("Status reporter configuration unchanged.") + return + } + + p.logger.Info("Status reporter configuration changed.") + + if newConfig.Prometheus && !p.config.Prometheus { + p.collectors.RegisterAll(p.manager.PrometheusRegister(), p.logger) + } else if !newConfig.Prometheus && p.config.Prometheus { + p.collectors.UnregisterAll(p.manager.PrometheusRegister()) + } else if newConfig.Prometheus && p.config.Prometheus { + if !reflect.DeepEqual(newConfig.PrometheusConfig, p.config.PrometheusConfig) { + p.collectors.ReregisterBundleLoadDuration(p.manager.PrometheusRegister(), newConfig.PrometheusConfig, p.logger) + } + } + + p.config = *newConfig +} + +func (p *Plugin) snapshot() *UpdateRequestV1 { + s := &UpdateRequestV1{ + Labels: p.manager.Labels(), + Discovery: p.lastDiscoStatus, + DecisionLogs: p.lastDecisionLogsStatus, + Bundle: p.lastBundleStatus, + Bundles: p.lastBundleStatuses, + Plugins: p.lastPluginStatuses, + } + + if p.metrics != nil { + s.Metrics = map[string]any{p.metrics.Info().Name: p.metrics.All()} + } + + return s +} + +func (p *Plugin) logUpdate(update *UpdateRequestV1) error { + eventBuf, err := json.Marshal(&update) + if err != nil { + return err + } + fields := map[string]any{} + err = util.UnmarshalJSON(eventBuf, &fields) + if err != nil { + return err + } + p.manager.ConsoleLogger().WithFields(fields).WithFields(map[string]any{ + "type": "openpolicyagent.org/status", + }).Info("Status Log") + return nil +} + +func (p *Plugin) updatePrometheusMetrics(u *UpdateRequestV1) { + p.collectors.pluginStatus.Reset() + for name, plugin := range u.Plugins { + p.collectors.pluginStatus.WithLabelValues(name, string(plugin.State)).Set(1) + } + p.collectors.lastSuccessfulActivation.Reset() + for _, bundle := range u.Bundles { + if bundle.Code == "" && !bundle.LastSuccessfulActivation.IsZero() { + p.collectors.loaded.WithLabelValues(bundle.Name).Inc() + } else { + p.collectors.failLoad.WithLabelValues(bundle.Name, bundle.Code, bundle.Message).Inc() + } + p.collectors.lastSuccessfulActivation.WithLabelValues(bundle.Name, bundle.ActiveRevision).Set(float64(bundle.LastSuccessfulActivation.UnixNano())) + p.collectors.lastSuccessfulDownload.WithLabelValues(bundle.Name).Set(float64(bundle.LastSuccessfulDownload.UnixNano())) + p.collectors.lastSuccessfulRequest.WithLabelValues(bundle.Name).Set(float64(bundle.LastSuccessfulRequest.UnixNano())) + p.collectors.lastRequest.WithLabelValues(bundle.Name).Set(float64(bundle.LastRequest.UnixNano())) + + if bundle.Metrics != nil { + for stage, metric := range bundle.Metrics.All() { + switch stage { + case "timer_bundle_request_ns", "timer_rego_data_parse_ns", "timer_rego_module_parse_ns", "timer_rego_module_compile_ns", "timer_rego_load_bundles_ns": + p.collectors.bundleLoadDuration.WithLabelValues(bundle.Name, stage).Observe(float64(metric.(int64))) + } + } + } + } +} + +func (u UpdateRequestV1) Equal(other UpdateRequestV1) bool { + return maps.Equal(u.Labels, other.Labels) && + maps.EqualFunc(u.Bundles, other.Bundles, (*bundle.Status).Equal) && + maps.EqualFunc(u.Plugins, other.Plugins, (*plugins.Status).Equal) && + u.Bundle.Equal(other.Bundle) && + u.Discovery.Equal(other.Discovery) && + u.DecisionLogs.Equal(other.DecisionLogs) && + nullSafeDeepEqual(u.Metrics, other.Metrics) +} + +func nullSafeDeepEqual(a, b any) bool { + if a == nil && b == nil { + return true + } + return a != nil && b != nil && reflect.DeepEqual(a, b) +} diff --git a/third_party/opa/v1/plugins/status/plugin_test.go b/third_party/opa/v1/plugins/status/plugin_test.go new file mode 100644 index 000000000000..dbaa26f36c9e --- /dev/null +++ b/third_party/opa/v1/plugins/status/plugin_test.go @@ -0,0 +1,1430 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package status + +import ( + "context" + "errors" + "fmt" + "maps" + "net/http" + "net/http/httptest" + "os" + "reflect" + "slices" + "strconv" + "strings" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" + + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/plugins/bundle" + lstat "github.com/open-policy-agent/opa/v1/plugins/logs/status" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" + "github.com/open-policy-agent/opa/v1/version" +) + +func TestMain(m *testing.M) { + if version.Version == "" { + version.Version = "unit-test" + } + os.Exit(m.Run()) +} + +func TestStatusUpdateBuffer(t *testing.T) { + + tests := []struct { + name string + numberOfStatusUpdates int + expectedStatusUpdates int + expectedNameDropped string + }{ + { + name: "add multiple events dropping the oldest", + numberOfStatusUpdates: 11, + expectedStatusUpdates: 1, + expectedNameDropped: "0", + }, + { + name: "don't drop anything", + numberOfStatusUpdates: 5, + expectedStatusUpdates: 1, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + fixture := newTestFixture(t, nil) + ctx := context.Background() + + err := fixture.plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer fixture.plugin.Stop(ctx) + + for i := range tc.numberOfStatusUpdates { + s := bundle.Status{ + Name: strconv.Itoa(i), + } + fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{ + "test": &s, + }) + } + + if len(fixture.plugin.bulkBundleCh) != tc.expectedStatusUpdates { + t.Fatalf("expected %d updates, got %d", tc.expectedStatusUpdates, len(fixture.plugin.bulkBundleCh)) + } + for _, v := range <-fixture.plugin.bulkBundleCh { + if v.Name == tc.expectedNameDropped { + t.Fatalf("expected %s dropped", tc.expectedNameDropped) + } + } + }) + } + +} + +func TestConfigValueParse(t *testing.T) { + tests := []struct { + note string + input string + expectedNoConfig bool + expectedValue []float64 + }{ + { + note: "empty config", + input: `{}`, + expectedNoConfig: true, + }, + { + note: "empty prometheus config", + input: `{"prometheus": false}`, + expectedNoConfig: true, + }, + { + note: "no specific prometheus config, expected default config buckets", + input: `{"prometheus": true}`, + expectedValue: defaultBundleLoadStageBuckets, + }, + { + note: "no specific prometheus config, expected default config buckets", + input: `{"prometheus": true, "prometheus_config": {}}`, + expectedValue: defaultBundleLoadStageBuckets, + }, + { + note: "no specific collectors config, expected default config buckets", + input: `{"prometheus": true, "prometheus_config": {"collectors": {}}}`, + expectedValue: defaultBundleLoadStageBuckets, + }, + { + note: "specified prometheus config, expected value same as config", + input: `{"prometheus": true, "prometheus_config": {"collectors": {"bundle_loading_duration_ns": {}}}}`, + expectedValue: defaultBundleLoadStageBuckets, + }, + { + note: "specified prometheus config, expected value same as config", + input: `{"prometheus": true, "prometheus_config": {"collectors": {"bundle_loading_duration_ns": {"buckets": []}}}}`, + expectedValue: []float64{}, + }, + { + note: "specified prometheus config, expected value same as config", + input: `{"prometheus": true, "prometheus_config": {"collectors": {"bundle_loading_duration_ns": {"buckets":[1, 1000, 1000_000, 1e8]}}}}`, + expectedValue: []float64{1, 1000, 1000_000, 1e8}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + config, err := ParseConfig([]byte(tc.input), []string{}, []string{"status"}) + if err != nil { + t.Errorf("expected no error: %v", err) + } + if tc.expectedNoConfig && config != nil { + t.Errorf("expected parsed config is nil, got %v", config) + } + if !tc.expectedNoConfig && !slices.Equal(config.PrometheusConfig.Collectors.BundleLoadDurationNanoseconds.Buckets, tc.expectedValue) { + t.Errorf("expected %v, got %v", tc.expectedValue, config.PrometheusConfig.Collectors.BundleLoadDurationNanoseconds.Buckets) + } + }) + } +} + +func TestPluginPrometheus(t *testing.T) { + fixture := newTestFixture(t, nil, func(c *Config) { + c.Prometheus = true + }) + fixture.server.ch = make(chan UpdateRequestV1) + defer fixture.server.stop() + + ctx := context.Background() + + err := fixture.plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer fixture.plugin.Stop(ctx) + <-fixture.server.ch + + status := testStatus() + + fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{"bundle": status}) + <-fixture.server.ch + + registerMock := fixture.manager.PrometheusRegister().(*prometheusRegisterMock) + + assertOpInformationGauge(t, registerMock) + + if registerMock.Collectors[fixture.plugin.collectors.pluginStatus] != true { + t.Fatalf("Plugin status metric was not registered on prometheus") + } + if registerMock.Collectors[fixture.plugin.collectors.loaded] != true { + t.Fatalf("Loaded metric was not registered on prometheus") + } + if registerMock.Collectors[fixture.plugin.collectors.failLoad] != true { + t.Fatalf("FailLoad metric was not registered on prometheus") + } + if registerMock.Collectors[fixture.plugin.collectors.lastRequest] != true { + t.Fatalf("Last request metric was not registered on prometheus") + } + if registerMock.Collectors[fixture.plugin.collectors.lastSuccessfulActivation] != true { + t.Fatalf("Last Successful Activation metric was not registered on prometheus") + } + if registerMock.Collectors[fixture.plugin.collectors.lastSuccessfulDownload] != true { + t.Fatalf("Last Successful Download metric was not registered on prometheus") + } + if registerMock.Collectors[fixture.plugin.collectors.lastSuccessfulRequest] != true { + t.Fatalf("Last Successful Request metric was not registered on prometheus") + } + if registerMock.Collectors[fixture.plugin.collectors.bundleLoadDuration] != true { + t.Fatalf("Bundle Load Duration metric was not registered on prometheus") + } + if len(registerMock.Collectors) != 9 { + t.Fatalf("Number of collectors expected (%v), got %v", 9, len(registerMock.Collectors)) + } + + lastRequestMetricResult := time.UnixMilli(int64(testutil.ToFloat64(fixture.plugin.collectors.lastRequest) / 1e6)) + if !lastRequestMetricResult.Equal(status.LastRequest) { + t.Fatalf("Last request expected (%v), got %v", status.LastRequest.UTC(), lastRequestMetricResult.UTC()) + } + + lastSuccessfulRequestMetricResult := time.UnixMilli(int64(testutil.ToFloat64(fixture.plugin.collectors.lastSuccessfulRequest) / 1e6)) + if !lastSuccessfulRequestMetricResult.Equal(status.LastSuccessfulRequest) { + t.Fatalf("Last request expected (%v), got %v", status.LastSuccessfulRequest.UTC(), lastSuccessfulRequestMetricResult.UTC()) + } + + lastSuccessfulDownloadMetricResult := time.UnixMilli(int64(testutil.ToFloat64(fixture.plugin.collectors.lastSuccessfulDownload) / 1e6)) + if !lastSuccessfulDownloadMetricResult.Equal(status.LastSuccessfulDownload) { + t.Fatalf("Last request expected (%v), got %v", status.LastSuccessfulDownload.UTC(), lastSuccessfulDownloadMetricResult.UTC()) + } + + lastSuccessfulActivationMetricResult := time.UnixMilli(int64(testutil.ToFloat64(fixture.plugin.collectors.lastSuccessfulActivation) / 1e6)) + if !lastSuccessfulActivationMetricResult.Equal(status.LastSuccessfulActivation) { + t.Fatalf("Last request expected (%v), got %v", status.LastSuccessfulActivation.UTC(), lastSuccessfulActivationMetricResult.UTC()) + } + + bundlesLoaded := testutil.CollectAndCount(fixture.plugin.collectors.loaded) + if bundlesLoaded != 1 { + t.Fatalf("Unexpected number of bundle loads (%v), got %v", 1, bundlesLoaded) + } + + bundlesFailedToLoad := testutil.CollectAndCount(fixture.plugin.collectors.failLoad) + if bundlesFailedToLoad != 0 { + t.Fatalf("Unexpected number of bundle fails load (%v), got %v", 0, bundlesFailedToLoad) + } + + pluginsStatus := testutil.CollectAndCount(fixture.plugin.collectors.pluginStatus) + if pluginsStatus != 1 { + t.Fatalf("Unexpected number of plugins (%v), got %v", 1, pluginsStatus) + } + + // Assert that metrics are purged when prometheus is disabled + prometheusDisabledConfig := newConfig(fixture.manager, func(c *Config) { + c.Prometheus = false + }) + fixture.plugin.Reconfigure(ctx, prometheusDisabledConfig) + eventually(t, func() bool { return fixture.plugin.config.Prometheus == false }) + + if len(registerMock.Collectors) != 0 { + t.Fatalf("Number of collectors expected (%v), got %v", 0, len(registerMock.Collectors)) + } + + // Assert that metrics are re-registered when prometheus is re-enabled + prometheusReenabledConfig := newConfig(fixture.manager, func(c *Config) { + c.Prometheus = true + }) + fixture.plugin.Reconfigure(ctx, prometheusReenabledConfig) + eventually(t, func() bool { return fixture.plugin.config.Prometheus == true }) + + if len(registerMock.Collectors) != 9 { + t.Fatalf("Number of collectors expected (%v), got %v", 9, len(registerMock.Collectors)) + } +} + +func eventually(t *testing.T, predicate func() bool) { + t.Helper() + if !test.Eventually(t, 1*time.Second, predicate) { + t.Fatal("check took too long") + } +} + +func assertOpInformationGauge(t *testing.T, registerMock *prometheusRegisterMock) { + gauges := filterGauges(registerMock) + if len(gauges) != 1 { + t.Fatalf("Expected one registered gauge on prometheus but got %v", len(gauges)) + } + + gauge := gauges[0] + + fqName := getName(gauge) + if fqName != "opa_info" { + t.Fatalf("Expected gauge to have name opa_info but was %s", fqName) + } + + labels := getConstLabels(gauge) + versionAct := labels["version"] + if versionAct != version.Version { + t.Fatalf("Expected gauge to have version label with value %s but was %s", version.Version, versionAct) + } +} + +func getName(gauge prometheus.Gauge) string { + desc := reflect.Indirect(reflect.ValueOf(gauge.Desc())) + fqName := desc.FieldByName("fqName").String() + return fqName +} + +func getConstLabels(gauge prometheus.Gauge) prometheus.Labels { + desc := reflect.Indirect(reflect.ValueOf(gauge.Desc())) + constLabelPairs := desc.FieldByName("constLabelPairs") + + // put all label pairs into a map for easier comparison. + labels := make(prometheus.Labels, constLabelPairs.Len()) + for i := range constLabelPairs.Len() { + name := constLabelPairs.Index(i).Elem().FieldByName("Name").Elem().String() + value := constLabelPairs.Index(i).Elem().FieldByName("Value").Elem().String() + labels[name] = value + } + return labels +} + +func filterGauges(registerMock *prometheusRegisterMock) []prometheus.Gauge { + fltd := make([]prometheus.Gauge, 0) + + for m := range registerMock.Collectors { + switch metric := m.(type) { + case prometheus.Gauge: + fltd = append(fltd, metric) + } + } + return fltd +} + +func TestMetricsBundleWithoutRevision(t *testing.T) { + fixture := newTestFixture(t, nil, func(c *Config) { + c.Prometheus = true + }) + fixture.server.ch = make(chan UpdateRequestV1) + defer fixture.server.stop() + + ctx := context.Background() + + err := fixture.plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer fixture.plugin.Stop(ctx) + <-fixture.server.ch + + status := testStatus() + status.ActiveRevision = "" + + fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{"bundle": status}) + <-fixture.server.ch + + bundlesLoaded := testutil.CollectAndCount(fixture.plugin.collectors.loaded) + if bundlesLoaded != 1 { + t.Fatalf("Unexpected number of bundle loads (%v), got %v", 1, bundlesLoaded) + } + + bundlesFailedToLoad := testutil.CollectAndCount(fixture.plugin.collectors.failLoad) + if bundlesFailedToLoad != 0 { + t.Fatalf("Unexpected number of bundle fails load (%v), got %v", 0, bundlesFailedToLoad) + } +} + +func TestPluginStart(t *testing.T) { + + fixture := newTestFixture(t, nil) + fixture.server.ch = make(chan UpdateRequestV1) + defer fixture.server.stop() + + ctx := context.Background() + + err := fixture.plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer fixture.plugin.Stop(ctx) + + // Start will trigger a status update when the plugin state switches + // from "not ready" to "ok". + result := <-fixture.server.ch + + exp := UpdateRequestV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + Plugins: map[string]*plugins.Status{ + "status": {State: plugins.StateOK}, + }, + } + + if !result.Equal(exp) { + t.Fatalf("Expected: %v but got: %v", exp, result) + } + + status := testStatus() + + fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{"test": status}) + result = <-fixture.server.ch + + exp.Bundles = map[string]*bundle.Status{"test": status} + + if !result.Equal(exp) { + t.Fatalf("Expected: %v but got: %v", exp, result) + } +} + +func TestPluginNoLogging(t *testing.T) { + // Given no custom plugin, no service(s) and no console logging configured, + // this should not be an error, but neither do we need to initiate the plugin + cases := []struct { + note string + config []byte + }{ + { + note: "no plugin attributes", + config: []byte(`{}`), + }, + { + note: "empty plugin configuration", + config: []byte(`{"status": {}}`), + }, + { + note: "only disabled console logger", + config: []byte(`{"status": {"console": "false"}}`), + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + config, err := ParseConfig(tc.config, []string{}, nil) + if err != nil { + t.Errorf("expected no error: %v", err) + } + if config != nil { + t.Errorf("excected no config for a no-op logging plugin") + } + }) + } +} + +func TestPluginStartTriggerManualStart(t *testing.T) { + + fixture := newTestFixture(t, nil) + fixture.server.ch = make(chan UpdateRequestV1) + defer fixture.server.stop() + + ctx := context.Background() + tr := plugins.TriggerManual + fixture.plugin.config.Trigger = &tr + + // Start will trigger a status update when the plugin state switches + // from "not ready" to "ok". This status update will be sent only after a manual trigger + err := fixture.plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer fixture.plugin.Stop(ctx) + + // trigger the status update + go func() { + _ = fixture.plugin.Trigger(ctx) + }() + + result := <-fixture.server.ch + + exp := UpdateRequestV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + } + + if !maps.Equal(result.Labels, exp.Labels) { + t.Fatalf("Expected: %v but got: %v", exp, result) + } +} + +func TestPluginStartTriggerManual(t *testing.T) { + fixture := newTestFixture(t, nil) + fixture.server.ch = make(chan UpdateRequestV1) + defer fixture.server.stop() + tr := plugins.TriggerManual + fixture.plugin.config.Trigger = &tr + + status := testStatus() + + fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{"test": status}) + + statuses := <-fixture.plugin.bulkBundleCh + fixture.plugin.lastBundleStatuses = statuses + + // trigger the status update + go func() { + _ = fixture.plugin.Trigger(context.Background()) + }() + + go func() { + update := <-fixture.plugin.trigger + err := fixture.plugin.oneShot(update.ctx) + if err != nil { + t.Error(err) + return + } + }() + + result := <-fixture.server.ch + + exp := UpdateRequestV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + } + exp.Bundles = map[string]*bundle.Status{"test": status} + + if !maps.EqualFunc(result.Bundles, exp.Bundles, (*bundle.Status).Equal) { + t.Fatalf("Expected: %v but got: %v", exp, result) + } +} + +func TestPluginStartTriggerManualMultiple(t *testing.T) { + + fixture := newTestFixture(t, nil) + fixture.server.ch = make(chan UpdateRequestV1) + defer fixture.server.stop() + + ctx := context.Background() + tr := plugins.TriggerManual + fixture.plugin.config.Trigger = &tr + + err := fixture.plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer fixture.plugin.Stop(ctx) + + status := testStatus() + fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{"test": status}) + fixture.plugin.UpdateDiscoveryStatus(*status) + + exp := UpdateRequestV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + Plugins: map[string]*plugins.Status{ + "status": {State: plugins.StateOK}, + }, + } + + // trigger the status update + go func() { + _ = fixture.plugin.Trigger(ctx) + }() + + result := <-fixture.server.ch + + exp.Bundles = map[string]*bundle.Status{"test": status} + exp.Discovery = status + + if !result.Equal(exp) { + t.Fatalf("Expected: %v but got: %v", exp, result) + } +} + +func TestPluginStartTriggerManualWithTimeout(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + s := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) { + time.Sleep(3 * time.Second) // this should cause the context deadline to exceed + })) + + managerConfig := fmt.Appendf(nil, `{ + "labels": { + "app": "example-app" + }, + "services": [ + { + "name": "example", + "url": %q + } + ]}`, s.URL) + + manager, err := plugins.New(managerConfig, "test-instance-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + pluginConfig := []byte(`{ + "service": "example", + }`) + + config, _ := ParseConfig(pluginConfig, manager.Services(), nil) + tr := plugins.TriggerManual + config.Trigger = &tr + + p := New(config, manager) + + // Start will trigger a status update when the plugin state switches + // from "not ready" to "ok". This status update will be sent only after a manual trigger + err = p.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer p.Stop(ctx) + + // trigger the status update + done := make(chan struct{}) + go func() { + // this call should block till the context deadline exceeds + _ = p.Trigger(ctx) + close(done) + }() + <-done + + if ctx.Err() == nil { + t.Fatal("Expected error but got nil") + } + + exp := "context deadline exceeded" + if ctx.Err().Error() != exp { + t.Fatalf("Expected error %v but got %v", exp, ctx.Err().Error()) + } +} + +func TestPluginStartTriggerManualWithError(t *testing.T) { + ctx := context.Background() + + managerConfig := []byte(`{ + "labels": { + "app": "example-app" + }, + "services": [ + { + "name": "example", + "url": "http://localhost:12345" + } + ]}`) + + manager, err := plugins.New(managerConfig, "test-instance-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + pluginConfig := []byte(`{ + "service": "example", + }`) + + config, _ := ParseConfig(pluginConfig, manager.Services(), nil) + tr := plugins.TriggerManual + config.Trigger = &tr + + p := New(config, manager) + + // Start will trigger a status update when the plugin state switches + // from "not ready" to "ok". This status update will be sent only after a manual trigger + err = p.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer p.Stop(ctx) + + // trigger the status update + // this call should result in an error from the bad service config + err = p.Trigger(ctx) + if err == nil { + t.Fatal("Expected error but got nil") + } + + exp := "connection refused" + if !strings.Contains(err.Error(), exp) { + t.Fatalf("Unexpected error message %v", err.Error()) + } +} + +func TestPluginStartBulkUpdate(t *testing.T) { + + fixture := newTestFixture(t, nil) + fixture.server.ch = make(chan UpdateRequestV1) + defer fixture.server.stop() + + ctx := context.Background() + + err := fixture.plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer fixture.plugin.Stop(ctx) + + // Start will trigger a status update when the plugin state switches + // from "not ready" to "ok". + <-fixture.server.ch // Discard first request. + + exp := UpdateRequestV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + Plugins: map[string]*plugins.Status{ + "status": {State: plugins.StateOK}, + }, + } + + status := testStatus() + + fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{status.Name: status}) + result := <-fixture.server.ch + + exp.Bundles = map[string]*bundle.Status{status.Name: status} + + if !result.Equal(exp) { + t.Fatalf("Expected: %v but got: %v", exp, result) + } +} + +func TestPluginStartBulkUpdateMultiple(t *testing.T) { + + fixture := newTestFixture(t, nil) + fixture.server.ch = make(chan UpdateRequestV1) + defer fixture.server.stop() + + ctx := context.Background() + + err := fixture.plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer fixture.plugin.Stop(ctx) + + // Ignore the plugin updating its status (tested elsewhere) + <-fixture.server.ch + + statuses := map[string]*bundle.Status{} + tDownload, _ := time.Parse(time.RFC3339Nano, "2018-01-01T00:00:00.0000000Z") + tActivate, _ := time.Parse(time.RFC3339Nano, "2018-01-01T00:00:01.0000000Z") + for i := range 20 { + name := fmt.Sprintf("test-bundle-%d", i) + statuses[name] = &bundle.Status{ + Name: name, + ActiveRevision: fmt.Sprintf("v%d", i), + LastSuccessfulDownload: tDownload, + LastSuccessfulActivation: tActivate, + } + } + + fixture.plugin.BulkUpdateBundleStatus(statuses) + result := <-fixture.server.ch + + expLabels := map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + } + + if !maps.Equal(result.Labels, expLabels) { + t.Fatalf("Unexpected status labels: %+v", result.Labels) + } + + if len(result.Bundles) != len(statuses) { + t.Fatalf("Expected %d statuses, got %d", len(statuses), len(result.Bundles)) + } + + for name, s := range statuses { + actualStatus := result.Bundles[name] + if actualStatus.Name != s.Name || + actualStatus.LastSuccessfulActivation != s.LastSuccessfulActivation || + actualStatus.LastSuccessfulDownload != s.LastSuccessfulDownload || + actualStatus.ActiveRevision != s.ActiveRevision { + t.Errorf("Bundle %s has unexpected status:\n\n %v\n\nExpected:\n%v\n\n", name, actualStatus, s) + } + } +} + +func TestPluginStartDiscovery(t *testing.T) { + + fixture := newTestFixture(t, nil) + fixture.server.ch = make(chan UpdateRequestV1) + defer fixture.server.stop() + + ctx := context.Background() + + err := fixture.plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer fixture.plugin.Stop(ctx) + + // Ignore the plugin updating its status (tested elsewhere) + <-fixture.server.ch + + status := testStatus() + + fixture.plugin.UpdateDiscoveryStatus(*status) + result := <-fixture.server.ch + + exp := UpdateRequestV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + Discovery: status, + Plugins: map[string]*plugins.Status{ + "status": {State: plugins.StateOK}, + }, + } + + if !result.Equal(exp) { + t.Fatalf("Expected: %+v but got: %+v", exp, result) + } +} + +func TestPluginStartDecisionLogs(t *testing.T) { + + fixture := newTestFixture(t, nil) + fixture.server.ch = make(chan UpdateRequestV1) + defer fixture.server.stop() + + ctx := context.Background() + + err := fixture.plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer fixture.plugin.Stop(ctx) + + // Ignore the plugin updating its status (tested elsewhere) + <-fixture.server.ch + + status := &lstat.Status{ + Code: "decision_log_error", + Message: "Upload Failed", + HTTPCode: "400", + } + + fixture.plugin.UpdateDecisionLogsStatus(*status) + result := <-fixture.server.ch + + exp := UpdateRequestV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + DecisionLogs: status, + Plugins: map[string]*plugins.Status{ + "status": {State: plugins.StateOK}, + }, + } + + if !result.Equal(exp) { + t.Fatalf("Expected: %+v but got: %+v", exp, result) + } +} + +func TestPluginBadAuth(t *testing.T) { + fixture := newTestFixture(t, nil) + ctx := context.Background() + fixture.server.expCode = 401 + defer fixture.server.stop() + fixture.plugin.lastBundleStatuses = map[string]*bundle.Status{} + err := fixture.plugin.oneShot(ctx) + if err == nil { + t.Fatal("Expected error") + } + if err.Error() != "status update failed, server replied with HTTP 401 Unauthorized" { + t.Fatalf("Unexpected error contents: %v", err) + } +} + +func TestPluginBadPath(t *testing.T) { + fixture := newTestFixture(t, nil) + ctx := context.Background() + fixture.server.expCode = 404 + defer fixture.server.stop() + fixture.plugin.lastBundleStatuses = map[string]*bundle.Status{} + err := fixture.plugin.oneShot(ctx) + if err == nil { + t.Fatal("Expected error") + } + if err.Error() != "status update failed, server replied with HTTP 404 Not Found" { + t.Fatalf("Unexpected error contents: %v", err) + } +} + +func TestPluginBadStatus(t *testing.T) { + fixture := newTestFixture(t, nil) + ctx := context.Background() + fixture.server.expCode = 500 + defer fixture.server.stop() + fixture.plugin.lastBundleStatuses = map[string]*bundle.Status{} + err := fixture.plugin.oneShot(ctx) + if err == nil { + t.Fatal("Expected error") + } + if err.Error() != "status update failed, server replied with HTTP 500 Internal Server Error" { + t.Fatalf("Unexpected error contents: %v", err) + } +} + +func TestPluginNonstandardStatus(t *testing.T) { + fixture := newTestFixture(t, nil) + ctx := context.Background() + fixture.server.expCode = 599 + defer fixture.server.stop() + fixture.plugin.lastBundleStatuses = map[string]*bundle.Status{} + err := fixture.plugin.oneShot(ctx) + if err == nil { + t.Fatal("Expected error") + } + if err.Error() != "status update failed, server replied with HTTP 599 " { + t.Fatalf("Unexpected error contents: %v", err) + } +} + +func TestPlugin2xxStatus(t *testing.T) { + fixture := newTestFixture(t, nil) + ctx := context.Background() + fixture.server.expCode = 204 + defer fixture.server.stop() + fixture.plugin.lastBundleStatuses = map[string]*bundle.Status{} + err := fixture.plugin.oneShot(ctx) + if err != nil { + t.Fatal("Expected no error") + } +} + +func TestPluginReconfigure(t *testing.T) { + ctx := context.Background() + fixture := newTestFixture(t, nil, func(c *Config) { + c.Prometheus = true + }) + defer fixture.server.stop() + + if err := fixture.plugin.Start(ctx); err != nil { + t.Fatal(err) + } + + // expect initial prometheus config is filled with default BundleLoadDurationNanoseconds bucket + initialPrometheusConfig := &PrometheusConfig{ + Collectors: &Collectors{ + BundleLoadDurationNanoseconds: &BundleLoadDurationNanoseconds{ + Buckets: defaultBundleLoadStageBuckets, + }, + }, + } + if !reflect.DeepEqual(fixture.plugin.config.PrometheusConfig, initialPrometheusConfig) { + t.Fatalf("Expected initial prometheus config %+v: test but got %+v", initialPrometheusConfig, fixture.plugin.config.PrometheusConfig) + } + + pluginConfig := []byte(`{ + "service": "example", + "partition_name": "test", + "prometheus": true, + "prometheus_config": { + "collectors": { + "bundle_loading_duration_ns": { + "buckets":[1, 1000, 1000_000, 1e8] + } + } + } + }`) + + config, _ := ParseConfig(pluginConfig, fixture.manager.Services(), nil) + + fixture.plugin.Reconfigure(ctx, config) + fixture.plugin.Stop(ctx) + + // after reconfigure, expect partition name and prometheus config are updated + if fixture.plugin.config.PartitionName != "test" { + t.Fatalf("Expected partition name: test but got %v", fixture.plugin.config.PartitionName) + } + + expectedPromConfig := &PrometheusConfig{ + Collectors: &Collectors{ + BundleLoadDurationNanoseconds: &BundleLoadDurationNanoseconds{ + Buckets: []float64{1, 1000, 1000_000, 1e8}, + }, + }, + } + if !reflect.DeepEqual(fixture.plugin.config.PrometheusConfig, expectedPromConfig) { + t.Fatalf("Expected prometheus config %+v: test but got %+v", expectedPromConfig, fixture.plugin.config.PrometheusConfig) + } +} + +func TestMetrics(t *testing.T) { + fixture := newTestFixture(t, metrics.New()) + fixture.server.ch = make(chan UpdateRequestV1) + defer fixture.server.stop() + + ctx := context.Background() + + err := fixture.plugin.Start(ctx) + if err != nil { + t.Fatal(err) + } + defer fixture.plugin.Stop(ctx) + + // Ignore the plugin updating its status (tested elsewhere) + <-fixture.server.ch + + status := testStatus() + + fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{"bundle": status}) + result := <-fixture.server.ch + + exp := map[string]any{"": map[string]any{}} + + if !reflect.DeepEqual(result.Metrics, exp) { + t.Fatalf("Expected %v but got %v", exp, result.Metrics) + } +} + +func TestParseConfigUseDefaultServiceNoConsole(t *testing.T) { + services := []string{ + "s0", + "s1", + "s3", + } + + loggerConfig := []byte(`{ + "console": false + }`) + + config, err := ParseConfig(loggerConfig, services, nil) + + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + + if config.Service != services[0] { + t.Errorf("Expected %s service in config, actual = '%s'", services[0], config.Service) + } +} + +func TestParseConfigDefaultServiceWithConsole(t *testing.T) { + services := []string{ + "s0", + "s1", + "s3", + } + + loggerConfig := []byte(`{ + "console": true + }`) + + config, err := ParseConfig(loggerConfig, services, nil) + + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + + if config.Service != "" { + t.Errorf("Expected no service in config, actual = '%s'", config.Service) + } +} + +func TestParseConfigTriggerMode(t *testing.T) { + cases := []struct { + note string + config []byte + expected plugins.TriggerMode + wantErr bool + err error + }{ + { + note: "default trigger mode", + config: []byte(`{}`), + expected: plugins.DefaultTriggerMode, + }, + { + note: "manual trigger mode", + config: []byte(`{"trigger": "manual"}`), + expected: plugins.TriggerManual, + }, + { + note: "trigger mode mismatch", + config: []byte(`{"trigger": "manual"}`), + expected: plugins.TriggerPeriodic, + wantErr: true, + err: errors.New("invalid status config: trigger mode mismatch: periodic and manual (hint: check discovery configuration)"), + }, + { + note: "bad trigger mode", + config: []byte(`{"trigger": "foo"}`), + expected: "foo", + wantErr: true, + err: errors.New("invalid status config: invalid trigger mode \"foo\" (want \"periodic\" or \"manual\")"), + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + c, err := NewConfigBuilder().WithBytes(tc.config).WithServices([]string{"s0"}).WithTriggerMode(&tc.expected).Parse() + + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else { + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if *c.Trigger != tc.expected { + t.Fatalf("Expected trigger mode %v but got %v", tc.expected, *c.Trigger) + } + } + }) + } +} + +type testFixture struct { + manager *plugins.Manager + plugin *Plugin + server *testServer +} + +type testPluginCustomizer func(c *Config) + +func newPlugin(t *testing.T, url string, m metrics.Metrics, options ...testPluginCustomizer) (*plugins.Manager, *Plugin) { + + managerConfig := fmt.Appendf(nil, `{ + "labels": { + "app": "example-app" + }, + "services": [ + { + "name": "example", + "url": %q, + "credentials": { + "bearer": { + "scheme": "Bearer", + "token": "secret" + } + } + } + ]}`, url) + + registerMock := &prometheusRegisterMock{ + Collectors: map[prometheus.Collector]bool{}, + } + manager, err := plugins.New(managerConfig, "test-instance-id", inmem.New(), plugins.WithPrometheusRegister(registerMock)) + if err != nil { + t.Fatal(err) + } + + config := newConfig(manager, options...) + + p := New(config, manager).WithMetrics(m) + + return manager, p +} + +func newTestFixture(t *testing.T, m metrics.Metrics, options ...testPluginCustomizer) testFixture { + + ts := testServer{ + t: t, + expCode: 200, + } + + ts.start() + + manager, p := newPlugin(t, ts.server.URL, m, options...) + + return testFixture{ + manager: manager, + plugin: p, + server: &ts, + } + +} + +func newConfig(manager *plugins.Manager, options ...testPluginCustomizer) *Config { + pluginConfig := []byte(`{ + "service": "example", + }`) + + config, _ := ParseConfig(pluginConfig, manager.Services(), nil) + for _, option := range options { + option(config) + } + + return config +} + +type testServer struct { + t *testing.T + expCode int + server *httptest.Server + ch chan UpdateRequestV1 +} + +func (t *testServer) handle(w http.ResponseWriter, r *http.Request) { + + status := UpdateRequestV1{} + + if err := util.NewJSONDecoder(r.Body).Decode(&status); err != nil { + t.t.Fatal(err) + } + + if t.ch != nil { + t.ch <- status + } + + w.WriteHeader(t.expCode) +} + +func (t *testServer) start() { + t.server = httptest.NewServer(http.HandlerFunc(t.handle)) +} + +func (t *testServer) stop() { + t.server.Close() +} + +func testStatus() *bundle.Status { + tDownload, _ := time.Parse(time.RFC3339Nano, "2018-01-01T00:00:00.0000000Z") + tActivate, _ := time.Parse(time.RFC3339Nano, "2018-01-01T00:00:01.0000000Z") + tSuccessfulRequest, _ := time.Parse(time.RFC3339Nano, "2018-01-01T00:00:02.0000000Z") + tRequest, _ := time.Parse(time.RFC3339Nano, "2018-01-01T00:00:03.0000000Z") + + status := bundle.Status{ + Name: "example/authz", + ActiveRevision: "quickbrawnfaux", + LastSuccessfulDownload: tDownload, + LastSuccessfulActivation: tActivate, + LastRequest: tRequest, + LastSuccessfulRequest: tSuccessfulRequest, + } + + return &status +} + +type testPlugin struct { + reqs []UpdateRequestV1 +} + +func (*testPlugin) Start(context.Context) error { + return nil +} + +func (*testPlugin) Stop(context.Context) { +} + +func (*testPlugin) Reconfigure(context.Context, any) { +} + +func (p *testPlugin) Log(_ context.Context, req *UpdateRequestV1) error { + p.reqs = append(p.reqs, *req) + return nil +} + +func TestPluginCustomBackend(t *testing.T) { + ctx := context.Background() + manager, _ := plugins.New(nil, "test-instance-id", inmem.New()) + + backend := &testPlugin{} + manager.Register("test_plugin", backend) + + config, err := ParseConfig([]byte(`{"plugin": "test_plugin"}`), nil, []string{"test_plugin"}) + if err != nil { + t.Fatal(err) + } + + plugin := New(config, manager) + err = plugin.oneShot(ctx) + if err != nil { + t.Fatal(err) + } + err = plugin.oneShot(ctx) + if err != nil { + t.Fatal(err) + } + + if len(backend.reqs) != 2 { + t.Fatalf("Unexpected number of reqs: expected 2, got %d: %v", len(backend.reqs), backend.reqs) + } +} + +type prometheusRegisterMock struct { + Collectors map[prometheus.Collector]bool +} + +func (p prometheusRegisterMock) Register(collector prometheus.Collector) error { + p.Collectors[collector] = true + return nil +} + +func (p prometheusRegisterMock) MustRegister(collector ...prometheus.Collector) { + for _, c := range collector { + p.Collectors[c] = true + } +} + +func (p prometheusRegisterMock) Unregister(collector prometheus.Collector) bool { + delete(p.Collectors, collector) + return true +} + +func TestPluginTerminatesAfterGracefulShutdownPeriodWithoutStatus(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + fixture := newTestFixture(t, nil) + defer fixture.server.stop() + + if err := fixture.plugin.Start(ctx); err != nil { + t.Fatal(err) + } + + timeoutCtx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + fixture.plugin.Stop(timeoutCtx) + if timeoutCtx.Err() != nil { + t.Fatal("Stop did not exit before context expiration") + } +} + +func TestPluginTerminatesAfterGracefulShutdownPeriodWithStatus(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + fixture := newTestFixture(t, nil) + fixture.server.ch = make(chan UpdateRequestV1, 1) + defer fixture.server.stop() + + // simplified status loop just to return done + // but doesn't read from the channels + go func() { + for done := range fixture.plugin.stop { + done <- struct{}{} + return + } + }() + + status := testStatus() + fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{status.Name: status}) + + timeoutCtx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + fixture.plugin.Stop(timeoutCtx) + if timeoutCtx.Err() != nil { + t.Fatal("Stop did not exit before context expiration") + } + + result := <-fixture.server.ch + + exp := UpdateRequestV1{ + Labels: map[string]string{ + "id": "test-instance-id", + "app": "example-app", + "version": version.Version, + }, + Bundles: map[string]*bundle.Status{status.Name: status}, + } + + if !result.Equal(exp) { + t.Fatalf("Expected: %v but got: %v", exp, result) + } +} + +func TestSlowServer(t *testing.T) { + t.Parallel() + + received := make(chan struct{}) + wait := make(chan struct{}) + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // notify test the server got the request + received <- struct{}{} + + // block until the test is ready to move on, so that multiple status updates can be sent + <-wait + })) + defer server.Close() + + _, plugin := newPlugin(t, server.URL, nil) + + // just start the loop, calling Start will also send a plugin status update that isn't needed for this test + go plugin.loop(context.Background()) + + status := bundle.Status{ + Name: "test", + } + plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{status.Name: &status}) + + // wait for server to get stuck + <-received + + if len(plugin.bulkBundleCh) != 0 { + t.Fatalf("Unexpected bulk bundle status: %v", plugin.bulkBundleCh) + } + + status = bundle.Status{ + Name: "I will be dropped", + } + plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{status.Name: &status}) + expectedStatusName := "I won't be dropped" + status = bundle.Status{ + Name: expectedStatusName, + } + plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{status.Name: &status}) + + currentStatus := <-plugin.bulkBundleCh + if len(currentStatus) > 1 { + t.Fatalf("Only one status expected but got: %v", currentStatus) + } + if _, ok := currentStatus[expectedStatusName]; !ok { + t.Fatalf("Expected status name not found: %v", currentStatus) + } + + // stop blocking server + wait <- struct{}{} +} diff --git a/third_party/opa/v1/profiler/profiler.go b/third_party/opa/v1/profiler/profiler.go new file mode 100644 index 000000000000..adc071598b4b --- /dev/null +++ b/third_party/opa/v1/profiler/profiler.go @@ -0,0 +1,395 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package profiler computes and reports on the time spent on expressions. +package profiler + +import ( + "sort" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/topdown" +) + +// Profiler computes and reports on the time spent on expressions. +type Profiler struct { + hits map[string]map[int]ExprStats + hitsByExprIndex map[string]map[int]map[int]ExprStats + activeTimer time.Time + prevExpr exprInfo +} + +// exprInfo stores information about an expression. +type exprInfo struct { + index int + location *ast.Location + op topdown.Op +} + +// New returns a new Profiler object. +func New() *Profiler { + return &Profiler{ + hits: map[string]map[int]ExprStats{}, + hitsByExprIndex: map[string]map[int]map[int]ExprStats{}, + } +} + +// Enabled returns true if profiler is enabled. +func (*Profiler) Enabled() bool { + return true +} + +// Config returns the standard Tracer configuration for the profiler +func (*Profiler) Config() topdown.TraceConfig { + return topdown.TraceConfig{ + PlugLocalVars: false, // Event variable metadata is not required for the Profiler + } +} + +// ReportByFile returns a profiler report for expressions grouped by the +// file name. For each file the results are sorted by increasing row number. +func (p *Profiler) ReportByFile() Report { + p.processLastExpr() + + report := Report{Files: map[string]*FileReport{}} + + for file, hits := range p.hits { + stats := []ExprStats{} + for row, stat := range hits { + if entry, ok := p.hitsByExprIndex[file][row]; ok { + stat.NumGenExpr = len(entry) + } + stats = append(stats, stat) + } + + sortStatsByRow(stats) + fr, ok := report.Files[file] + if !ok { + fr = &FileReport{} + report.Files[file] = fr + } + fr.Result = stats + } + + return report +} + +// ReportTopNResults returns the top N results based on the given +// criteria. If N <= 0, all the results based on the criteria are returned. +func (p *Profiler) ReportTopNResults(numResults int, criteria []string) []ExprStats { + p.processLastExpr() + + stats := []ExprStats{} + + for file, hits := range p.hits { + for row, stat := range hits { + if entry, ok := p.hitsByExprIndex[file][row]; ok { + stat.NumGenExpr = len(entry) + } + stats = append(stats, stat) + } + } + + // allowed criteria for sorting results + allowedCriteria := map[string]lessFunc{} + allowedCriteria["total_time_ns"] = func(stat1, stat2 *ExprStats) bool { + return stat1.ExprTimeNs > stat2.ExprTimeNs + } + allowedCriteria["num_eval"] = func(stat1, stat2 *ExprStats) bool { + return stat1.NumEval > stat2.NumEval + } + allowedCriteria["num_redo"] = func(stat1, stat2 *ExprStats) bool { + return stat1.NumRedo > stat2.NumRedo + } + allowedCriteria["num_gen_expr"] = func(stat1, stat2 *ExprStats) bool { + return stat1.NumGenExpr > stat2.NumGenExpr + } + allowedCriteria["file"] = func(stat1, stat2 *ExprStats) bool { + return stat1.Location.File > stat2.Location.File + } + allowedCriteria["line"] = func(stat1, stat2 *ExprStats) bool { + return stat1.Location.Row > stat2.Location.Row + } + + sortFuncs := []lessFunc{} + + for _, cr := range criteria { + if fn, ok := allowedCriteria[cr]; ok { + sortFuncs = append(sortFuncs, fn) + } + } + + // if no criteria return all the stats + if len(sortFuncs) == 0 { + return stats + } + + orderedBy(sortFuncs).Sort(stats) + + // if desired number of results to be returned is less than or + // equal to 0 or exceed total available results, + // return all the stats + if numResults <= 0 || numResults > len(stats) { + return stats + } + return stats[:numResults] + +} + +// Trace updates the profiler state. +// Deprecated: Use TraceEvent instead. +func (p *Profiler) Trace(event *topdown.Event) { + p.TraceEvent(*event) +} + +// TraceEvent updates the coverage state. +func (p *Profiler) TraceEvent(event topdown.Event) { + switch event.Op { + case topdown.EvalOp: + if expr, ok := event.Node.(*ast.Expr); ok && expr != nil { + p.processExpr(expr, event.Op) + } + case topdown.RedoOp: + if expr, ok := event.Node.(*ast.Expr); ok && expr != nil { + p.processExpr(expr, event.Op) + } + } +} + +func (p *Profiler) processExpr(expr *ast.Expr, eventType topdown.Op) { + if expr.Location == nil { + // add fake location to group expressions without a location + expr.Location = ast.NewLocation([]byte("???"), "", 0, 0) + } + + // set the active timer on the first expression + if p.activeTimer.IsZero() { + p.activeTimer = time.Now() + p.prevExpr = exprInfo{ + op: eventType, + location: expr.Location, + index: expr.Index, + } + return + } + + // record the profiler results for the previous expression + p.calculateHitsByExprIndex() + + file := p.prevExpr.location.File + hits, ok := p.hits[file] + if !ok { + hits = map[int]ExprStats{} + hits[p.prevExpr.location.Row] = getProfilerStats(p.prevExpr, p.activeTimer) + p.hits[file] = hits + } else { + pos := p.prevExpr.location.Row + pStats, ok := hits[pos] + if !ok { + hits[pos] = getProfilerStats(p.prevExpr, p.activeTimer) + } else { + pStats.ExprTimeNs += time.Since(p.activeTimer).Nanoseconds() + + switch p.prevExpr.op { + case topdown.EvalOp: + pStats.NumEval++ + case topdown.RedoOp: + pStats.NumRedo++ + } + hits[pos] = pStats + } + } + + // reset active timer and expression + p.activeTimer = time.Now() + p.prevExpr = exprInfo{ + op: eventType, + location: expr.Location, + index: expr.Index, + } +} + +func (p *Profiler) processLastExpr() { + expr := ast.Expr{ + Location: p.prevExpr.location, + Index: p.prevExpr.index, + } + p.processExpr(&expr, p.prevExpr.op) +} + +func (p *Profiler) calculateHitsByExprIndex() { + file := p.prevExpr.location.File + hitsUnique, ok := p.hitsByExprIndex[file] + + if !ok { + hitsUnique = map[int]map[int]ExprStats{} + hitsUnique[p.prevExpr.location.Row] = map[int]ExprStats{p.prevExpr.index: getProfilerStats(p.prevExpr, p.activeTimer)} + p.hitsByExprIndex[file] = hitsUnique + } else { + row := p.prevExpr.location.Row + idx := p.prevExpr.index + + pStats, ok := hitsUnique[row] + if !ok { + hitsUnique[row] = map[int]ExprStats{idx: getProfilerStats(p.prevExpr, p.activeTimer)} + } else { + pStatsIdx, ok := pStats[idx] + if !ok { + hitsUnique[row][idx] = getProfilerStats(p.prevExpr, p.activeTimer) + } else { + pStatsIdx.ExprTimeNs += time.Since(p.activeTimer).Nanoseconds() + + switch p.prevExpr.op { + case topdown.EvalOp: + pStatsIdx.NumEval++ + case topdown.RedoOp: + pStatsIdx.NumRedo++ + } + + hitsUnique[row][idx] = pStatsIdx + } + } + } +} + +func getProfilerStats(expr exprInfo, timer time.Time) ExprStats { + profilerStats := ExprStats{} + profilerStats.ExprTimeNs = time.Since(timer).Nanoseconds() + profilerStats.Location = expr.location + + switch expr.op { + case topdown.EvalOp: + profilerStats.NumEval = 1 + case topdown.RedoOp: + profilerStats.NumRedo = 1 + } + return profilerStats +} + +// ExprStats represents the result of profiling an expression. +type ExprStats struct { + ExprTimeNs int64 `json:"total_time_ns"` + NumEval int `json:"num_eval"` + NumRedo int `json:"num_redo"` + NumGenExpr int `json:"num_gen_expr"` + Location *ast.Location `json:"location"` +} + +// ExprStatsAggregated represents the result of profiling an expression +// by aggregating `n` profiles. +type ExprStatsAggregated struct { + ExprTimeNsStats any `json:"total_time_ns_stats"` + NumEval int `json:"num_eval"` + NumRedo int `json:"num_redo"` + NumGenExpr int `json:"num_gen_expr"` + Location *ast.Location `json:"location"` +} + +func aggregate(stats ...ExprStats) ExprStatsAggregated { + if len(stats) == 0 { + return ExprStatsAggregated{} + } + res := ExprStatsAggregated{ + NumEval: stats[0].NumEval, + NumRedo: stats[0].NumRedo, + NumGenExpr: stats[0].NumGenExpr, + Location: stats[0].Location, + } + timeNs := make([]int64, 0, len(stats)) + for _, s := range stats { + timeNs = append(timeNs, s.ExprTimeNs) + } + res.ExprTimeNsStats = metrics.Statistics(timeNs...) + return res +} + +func AggregateProfiles(profiles ...[]ExprStats) []ExprStatsAggregated { + if len(profiles) == 0 { + return []ExprStatsAggregated{} + } + res := make([]ExprStatsAggregated, len(profiles[0])) + for j := range len(profiles[0]) { + var s []ExprStats + for _, p := range profiles { + s = append(s, p[j]) + } + res[j] = aggregate(s...) + } + return res +} + +func sortStatsByRow(ps []ExprStats) { + sort.Slice(ps, func(i, j int) bool { + return ps[i].Location.Row < ps[j].Location.Row + }) +} + +// Report represents the profiler report for a set of files. +type Report struct { + Files map[string]*FileReport `json:"files"` +} + +// FileReport represents a profiler report for a single file. +type FileReport struct { + Result []ExprStats `json:"result"` +} + +// Helper interfaces and methods for sorting a slice of ExprStats structs +// based on multiple fields. + +type lessFunc func(p1, p2 *ExprStats) bool + +// multiSorter implements the Sort interface, sorting the changes within. +type multiSorter struct { + stats []ExprStats + less []lessFunc +} + +// Sort sorts the argument slice according to the less functions passed to OrderedBy. +func (ms *multiSorter) Sort(stats []ExprStats) { + ms.stats = stats + sort.Sort(ms) +} + +// orderedBy returns a Sorter that sorts using the less functions, in order. +func orderedBy(less []lessFunc) *multiSorter { + return &multiSorter{ + less: less, + } +} + +// Len is part of sort.Interface. +func (ms *multiSorter) Len() int { + return len(ms.stats) +} + +// Swap is part of sort.Interface. +func (ms *multiSorter) Swap(i, j int) { + ms.stats[i], ms.stats[j] = ms.stats[j], ms.stats[i] +} + +// Less is part of sort.Interface. It is implemented by looping along the +// less functions until it finds a comparison that discriminates between +// the two items. +func (ms *multiSorter) Less(i, j int) bool { + p, q := &ms.stats[i], &ms.stats[j] + // Try all but the last comparison. + var k int + // changing this here changes the semantics, likely because + // k outlives the range.. seems like a bug in the intrange linter + //nolint:intrange + for k = 0; k < len(ms.less)-1; k++ { + less := ms.less[k] + switch { + case less(p, q): + return true + case less(q, p): + return false + } + // p == q; try the next comparison. + } + return ms.less[k](p, q) +} diff --git a/third_party/opa/v1/profiler/profiler_bench_test.go b/third_party/opa/v1/profiler/profiler_bench_test.go new file mode 100644 index 000000000000..d32059a2a685 --- /dev/null +++ b/third_party/opa/v1/profiler/profiler_bench_test.go @@ -0,0 +1,73 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package profiler + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/rego" +) + +func BenchmarkProfilerBigLocalVar(b *testing.B) { + iterations := []int{1, 100, 1000} + vars := []int{1, 10} + + for _, iterationCount := range iterations { + for _, varCount := range vars { + name := fmt.Sprintf("%dVars%dIterations", varCount, iterationCount) + b.Run(name, func(b *testing.B) { + profiler := New() + module := generateModule(varCount, iterationCount) + + if _, err := ast.ParseModule("test.rego", module); err != nil { + b.Fatal(err) + } + + ctx := context.Background() + + pq, err := rego.New( + rego.Module("test.rego", module), + rego.Query("data.test.p"), + ).PrepareForEval(ctx) + + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + + for range b.N { + if _, err = pq.Eval(ctx, rego.EvalQueryTracer(profiler)); err != nil { + b.Fatal(err) + } + } + }) + } + } +} + +func generateModule(numVars int, dataSize int) string { + sb := strings.Builder{} + sb.WriteString(`package test + +p if { + x := a + v := x[i] +`) + for i := range numVars { + sb.WriteString(fmt.Sprintf("\tv%d := x[i+%d]\n", i, i)) + } + sb.WriteString("\tfalse\n}\n") + sb.WriteString("\na := [\n") + for i := range dataSize { + sb.WriteString(fmt.Sprintf("\t%d,\n", i)) + } + sb.WriteString("]\n") + return sb.String() +} diff --git a/third_party/opa/v1/profiler/profiler_test.go b/third_party/opa/v1/profiler/profiler_test.go new file mode 100644 index 000000000000..e8ea82b3c498 --- /dev/null +++ b/third_party/opa/v1/profiler/profiler_test.go @@ -0,0 +1,519 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: goconst // string duplication is for test readability. +package profiler + +import ( + "context" + _ "encoding/json" + "reflect" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/types" +) + +func TestProfilerLargeArray(t *testing.T) { + profiler := New() + module := `package test +import rego.v1 + +foo if { + p + bar + not baz + bee +} + +bee if { + nums = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i", "b", "c", "d", "e", "f", "g", "h", "i"] + num = nums[_] + contains(num, "test") +} + +bar if { + a := 1 + b := 2 + a != b +} + +baz if { + true + false + true +} + +p if { + a := 1 + b := 2 + c := 3 + x = a + b * c +} +` + + _, err := ast.ParseModule("test.rego", module) + if err != nil { + t.Fatal(err) + } + + eval := rego.New( + rego.Module("test.rego", module), + rego.Query("data.test.foo"), + rego.QueryTracer(profiler), + ) + + ctx := context.Background() + _, err = eval.Eval(ctx) + + if err != nil { + t.Fatal(err) + } + + report := profiler.ReportByFile() + + fr, ok := report.Files["test.rego"] + if !ok { + t.Fatal("Expected file report for test.rego") + } + + if len(fr.Result) != 16 { + t.Fatalf("Expected file report length to be 16 instead got %v", len(fr.Result)) + } + + expectedNumEval := []int{1, 1, 2, 1, 1, 1, 1633, 1, 1, 1, 1, 1, 1, 1, 1, 3} + expectedNumRedo := []int{1, 1, 0, 0, 1, 1633, 0, 1, 1, 1, 1, 0, 1, 1, 1, 3} + expectedRow := []int{5, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 30, 31, 32, 33} + expectedNumGenExpr := []int{1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3} + + for idx, actualExprStat := range fr.Result { + if actualExprStat.NumEval != expectedNumEval[idx] { + t.Fatalf("Index %v: Expected number of evals %v but got %v", idx, expectedNumEval[idx], actualExprStat.NumEval) + } + + if actualExprStat.NumRedo != expectedNumRedo[idx] { + t.Fatalf("Index %v: Expected number of redos %v but got %v", idx, expectedNumRedo[idx], actualExprStat.NumRedo) + } + + if actualExprStat.Location.Row != expectedRow[idx] { + t.Fatalf("Index %v: Expected row %v but got %v", idx, expectedRow[idx], actualExprStat.Location.Row) + } + + if actualExprStat.NumGenExpr != expectedNumGenExpr[idx] { + t.Fatalf("Index %v: Expected number of generated expressions %v but got %v", idx, expectedNumGenExpr[idx], actualExprStat.NumGenExpr) + } + } +} + +func TestProfileCheckExprDuration(t *testing.T) { + profiler := New() + + ast.RegisterBuiltin(&ast.Builtin{ + Name: "test.sleep", + Decl: types.NewFunction( + types.Args(types.S), + types.Nl, + ), + }) + + topdown.RegisterBuiltinFunc("test.sleep", func(_ topdown.BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + d, _ := time.ParseDuration(string(operands[0].Value.(ast.String))) + time.Sleep(d) + return iter(ast.NullTerm()) + }) + + module := `package test + import rego.v1 + + foo if { + test.sleep("100ms") + }` + + _, err := ast.ParseModule("test.rego", module) + if err != nil { + t.Fatal(err) + } + + eval := rego.New( + rego.Module("test.rego", module), + rego.Query("data.test.foo"), + rego.QueryTracer(profiler), + ) + + ctx := context.Background() + _, err = eval.Eval(ctx) + + if err != nil { + t.Fatal(err) + } + + report := profiler.ReportByFile() + + fr, ok := report.Files["test.rego"] + if !ok { + t.Fatal("Expected file report for test.rego") + } + + if len(fr.Result) != 1 { + t.Fatalf("Expected file report length to be 1 instead got %v", len(fr.Result)) + } + + if string(fr.Result[0].Location.Text) != "test.sleep(\"100ms\")" { + t.Fatalf("Expected text is test.sleep(\"100ms\") but got %v", string(fr.Result[0].Location.Text)) + } + + if fr.Result[0].ExprTimeNs <= 50*time.Millisecond.Nanoseconds() { + t.Fatalf("Expected eval time is at least 100 msec but got %v", fr.Result[0].ExprTimeNs) + } + +} + +func TestProfilerReportTopNResultsNoCriteria(t *testing.T) { + profiler := New() + module := `package test +import rego.v1 + +foo if { + bar + not baz + bee +} + +bee if { + nums = ["a", "b", "c", "d"] + num = nums[_] + contains(num, "test") +} + +bar if { + a := 1 + b := 2 + a != b +} + +baz if { + true + false + true +}` + + _, err := ast.ParseModule("test.rego", module) + if err != nil { + t.Fatal(err) + } + + eval := rego.New( + rego.Module("test.rego", module), + rego.Query("data.test.foo"), + rego.QueryTracer(profiler), + ) + + ctx := context.Background() + _, err = eval.Eval(ctx) + + if err != nil { + t.Fatal(err) + } + + stats := profiler.ReportTopNResults(0, []string{}) + + expectedResLen := 12 + if len(stats) != expectedResLen { + t.Fatalf("Expected %v stats instead got %v", expectedResLen, len(stats)) + } +} + +func TestProfilerReportTopNResultsOneCriteria(t *testing.T) { + profiler := New() + module := `package test +import rego.v1 + +foo if { + bar + not baz + bee +} + +bee if { + nums = ["a", "b", "c", "d"] + num = nums[_] + contains(num, "test") +} + +bar if { + a := 1 + b := 2 + a != b +} + +baz if { + true + false + true +}` + + _, err := ast.ParseModule("test.rego", module) + if err != nil { + t.Fatal(err) + } + + eval := rego.New( + rego.Module("test.rego", module), + rego.Query("data.test.foo"), + rego.QueryTracer(profiler), + ) + + ctx := context.Background() + _, err = eval.Eval(ctx) + + if err != nil { + t.Fatal(err) + } + + stats := profiler.ReportTopNResults(5, []string{"total_time_ns"}) + + expectedResLen := 5 + if len(stats) != expectedResLen { + t.Fatalf("Expected %v stats instead got %v", expectedResLen, len(stats)) + } + + for i := range len(stats) - 1 { + if stats[i].ExprTimeNs < stats[i+1].ExprTimeNs { + t.Fatalf("Results not sorted in decreasing order of evaluation times") + } + } +} + +func TestProfilerReportTopNResultsTwoCriteria(t *testing.T) { + profiler := New() + module := `package test +import rego.v1 + +foo if { + bar + not baz + bee +} + +bee if { + nums = ["a", "b", "c", "d"] + num = nums[_] + contains(num, "test") +} + +bar if { + a := 1 + b := 2 + a != b +} + +baz if { + true + false + true +}` + + _, err := ast.ParseModule("test.rego", module) + if err != nil { + t.Fatal(err) + } + + eval := rego.New( + rego.Module("test.rego", module), + rego.Query("data.test.foo"), + rego.QueryTracer(profiler), + ) + + ctx := context.Background() + _, err = eval.Eval(ctx) + + if err != nil { + t.Fatal(err) + } + + stats := profiler.ReportTopNResults(5, []string{"num_eval", "total_time_ns"}) + + expectedResLen := 5 + if len(stats) != expectedResLen { + t.Fatalf("Expected %v stats instead got %v", expectedResLen, len(stats)) + } + + var i int + for i = range len(stats) - 1 { + if stats[i].NumEval < stats[i+1].NumEval { + t.Fatalf("Results not sorted in decreasing order of number of evaluations") + } + + if stats[i].NumEval == stats[i+1].NumEval { + if stats[i].ExprTimeNs < stats[i+1].ExprTimeNs { + t.Fatalf("Results not sorted in decreasing order of evaluation times") + } + } + } +} + +func TestProfilerReportTopNResultsThreeCriteria(t *testing.T) { + profiler := New() + module := `package test +import rego.v1 + +foo if { + bar + not baz + bee +} + +bee if { + nums = ["a", "b", "c", "d"] + num = nums[_] + contains(num, "test") +} + +bar if { + a := 1 + b := 2 + a != b +} + +baz if { + true + false + true +}` + + _, err := ast.ParseModule("test.rego", module) + if err != nil { + t.Fatal(err) + } + + eval := rego.New( + rego.Module("test.rego", module), + rego.Query("data.test.foo"), + rego.QueryTracer(profiler), + ) + + ctx := context.Background() + _, err = eval.Eval(ctx) + + if err != nil { + t.Fatal(err) + } + + stats := profiler.ReportTopNResults(10, []string{"num_eval", "num_redo", "total_time_ns"}) + + expectedResLen := 10 + if len(stats) != expectedResLen { + t.Fatalf("Expected %v stats instead got %v", expectedResLen, len(stats)) + } + + var i int + for i = range len(stats) - 1 { + if stats[i].NumEval < stats[i+1].NumEval { + t.Fatalf("Results not sorted in decreasing order of number of evaluations") + } + + if stats[i].NumEval == stats[i+1].NumEval { + + if stats[i].NumRedo < stats[i+1].NumRedo { + t.Fatalf("Results not sorted in decreasing order of number of redos") + } + + if stats[i].NumRedo == stats[i+1].NumRedo { + if stats[i].ExprTimeNs < stats[i+1].ExprTimeNs { + t.Fatalf("Results not sorted in decreasing order of evaluation times") + } + } + } + } +} + +func TestProfilerWithPartialEval(t *testing.T) { + profiler := New() + + module := `package test +import rego.v1 + +default foo = false + +foo = true if { + op = allowed_operations[_] + input.method = op.method + input.resource = op.resource +} + +allowed_operations = [ + {"method": "PUT", "resource": "policy"}, +]` + + _, err := ast.ParseModule("test.rego", module) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + pq, err := rego.New( + rego.Module("test.rego", module), + rego.Query("data.test.foo"), + ).PrepareForEval(ctx, rego.WithPartialEval()) + if err != nil { + t.Fatal(err) + } + + _, err = pq.Eval(ctx, rego.EvalQueryTracer(profiler)) + if err != nil { + t.Fatal(err) + } + + report := profiler.ReportByFile() + + if len(report.Files) != 1 { + t.Fatalf("Expected file report length to be 1 instead got %v", len(report.Files)) + } + + fr := report.Files[""] + + if len(fr.Result) != 2 { + t.Fatalf("Expected 2 results for file but instead got %v", len(fr.Result)) + } + + expectedNumEval := []int{2, 1} + expectedNumRedo := []int{2, 1} + expectedNumGenExpr := []int{1, 1} + expectedLocation := []string{"???", "data.partial.__result__"} + + for idx, actualExprStat := range fr.Result { + if actualExprStat.NumEval != expectedNumEval[idx] { + t.Fatalf("Index %v: Expected number of evals %v but got %v", idx, expectedNumEval[idx], actualExprStat.NumEval) + } + + if actualExprStat.NumRedo != expectedNumRedo[idx] { + t.Fatalf("Index %v: Expected number of redos %v but got %v", idx, expectedNumRedo[idx], actualExprStat.NumRedo) + } + + if actualExprStat.NumGenExpr != expectedNumGenExpr[idx] { + t.Fatalf("Index %v: Expected number of generated expressions %v but got %v", idx, expectedNumGenExpr[idx], actualExprStat.NumGenExpr) + } + + if string(actualExprStat.Location.Text) != expectedLocation[idx] { + t.Fatalf("Index %v: Expected location %v but got %v", idx, expectedLocation[idx], string(actualExprStat.Location.Text)) + } + } +} + +func TestProfilerTraceConfig(t *testing.T) { + ct := topdown.QueryTracer(New()) + conf := ct.Config() + + expected := topdown.TraceConfig{ + PlugLocalVars: false, + } + + if !reflect.DeepEqual(expected, conf) { + t.Fatalf("Expected config: %+v, got %+v", expected, conf) + } +} diff --git a/third_party/opa/v1/refactor/refactor.go b/third_party/opa/v1/refactor/refactor.go new file mode 100644 index 000000000000..1d6c16556666 --- /dev/null +++ b/third_party/opa/v1/refactor/refactor.go @@ -0,0 +1,125 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package refactor implements different refactoring operations over Rego modules. +package refactor + +import ( + "fmt" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// Error defines the structure of errors returned by refactor. +type Error struct { + Message string `json:"message"` + Location *ast.Location `json:"location,omitempty"` +} + +func (e Error) Error() string { + + if e.Location != nil { + return e.Location.Format(e.Message) + } + + return e.Message +} + +// Refactor implements different refactoring operations over Rego modules eg. renaming packages. +type Refactor struct { +} + +// New returns a new Refactor object. +func New() *Refactor { + return &Refactor{} +} + +// MoveQuery holds the set of Rego modules whose package paths and other references are to be rewritten +// as per the mapping defined in SrcDstMapping. +// If validate is true, the moved modules will be compiled to ensure they are valid. +type MoveQuery struct { + Modules map[string]*ast.Module + SrcDstMapping map[string]string + validate bool +} + +// WithValidation controls whether to compile moved modules to ensure they are valid. +func (mq MoveQuery) WithValidation(v bool) MoveQuery { + mq.validate = v + return mq +} + +// MoveQueryResult defines the output of a move query and holds the rewritten modules with updated packages paths +// and references. +type MoveQueryResult struct { + Result map[string]*ast.Module `json:"result"` +} + +// validate validates moved modules by compiling them. +func (mqr *MoveQueryResult) validate() error { + compiler := ast.NewCompiler() + compiler.Compile(mqr.Result) + + if compiler.Failed() { + return compiler.Errors + } + return nil +} + +// Move rewrites Rego code by updating package paths and other references in q's modules as per +// the mapping specified in q. +func (*Refactor) Move(q MoveQuery) (*MoveQueryResult, error) { + + for _, module := range q.Modules { + t := ast.NewGenericTransformer(func(x any) (any, error) { + if s, ok := x.(ast.Ref); ok { + for k, v := range q.SrcDstMapping { + other, err := ast.ParseRef(k) + if err != nil { + return nil, err + } + + if s.HasPrefix(other) { + newRef, err := ast.ParseRef(v) + if err != nil { + return nil, err + } + return newRef.Concat(s[len(other):]), nil + } + + // check if a reference in the policy is a prefix of a source reference + // example: policy_reference = data.foo + // mapping: {"data.foo.bar": "data.baz"} + // In this scenario, we can relocate data.foo.bar but everything under data.foo + // (e.g., data.foo.baz, data.foo.qux, etc.) can't be relocated + r := s.ConstantPrefix() + if len(r) != 0 && other.HasPrefix(r) { + msg := fmt.Sprintf("cannot rewrite `%v`: constant prefix `%v` of `%v` is too short", s, r, s) + x := Error{Message: msg, Location: s[len(s)-1].Loc()} + return nil, x + } + } + } + return x, nil + }) + _, err := ast.Transform(t, module) + if err != nil { + switch err.(type) { + case Error: + return nil, err + default: + return nil, Error{Message: err.Error()} + } + } + } + + result := &MoveQueryResult{Result: q.Modules} + + if q.validate { + if err := result.validate(); err != nil { + return nil, err + } + } + return result, nil +} diff --git a/third_party/opa/v1/refactor/refactor_test.go b/third_party/opa/v1/refactor/refactor_test.go new file mode 100644 index 000000000000..8abf0ce790b9 --- /dev/null +++ b/third_party/opa/v1/refactor/refactor_test.go @@ -0,0 +1,397 @@ +package refactor + +import ( + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestMoveRenamePackage(t *testing.T) { + module := ast.MustParseModule(`package lib.foo +import rego.v1 + +default allow = false + +allow if { + input.message == "hello" +}`) + + modules := map[string]*ast.Module{ + "policy.rego": module, + } + + mappings := map[string]string{ + "data.lib.foo": "data.baz.bar", + } + + result, err := New().Move(MoveQuery{ + Modules: modules, + SrcDstMapping: mappings, + }) + if err != nil { + t.Fatal(err) + } + + actual := result.Result["policy.rego"] + + expected := ast.MustParseModule(`package baz.bar +import rego.v1 + +default allow = false + +allow if { + input.message == "hello" +}`) + + if !expected.Equal(actual) { + t.Fatalf("Expected module:\n%v\n\nGot:\n%v\n", expected, actual) + } +} + +func TestMoveRenamePackagePrefix(t *testing.T) { + module1 := ast.MustParseModule(`package lib.foo +import rego.v1 + +default allow = false + +allow if { + input.message == "hello" +}`) + + module2 := ast.MustParseModule(`package lib.bar +import rego.v1 + +allow if { + input.message == "world" +}`) + + modules := map[string]*ast.Module{ + "policy1.rego": module1, + "policy2.rego": module2, + } + + mappings := map[string]string{ + "data.lib": "data.hidden", + } + + result, err := New().Move(MoveQuery{ + Modules: modules, + SrcDstMapping: mappings, + }) + if err != nil { + t.Fatal(err) + } + + actual1 := result.Result["policy1.rego"] + actual2 := result.Result["policy2.rego"] + + expected1 := ast.MustParseModule(`package hidden.foo +import rego.v1 + +default allow = false + +allow if { + input.message == "hello" +}`) + + expected2 := ast.MustParseModule(`package hidden.bar +import rego.v1 + +allow if { + input.message == "world" +}`) + + if !expected1.Equal(actual1) { + t.Fatalf("Expected module:\n%v\n\nGot:\n%v\n", expected1, actual1) + } + + if !expected2.Equal(actual2) { + t.Fatalf("Expected module:\n%v\n\nGot:\n%v\n", expected2, actual2) + } +} + +func TestMovePrefixInjection(t *testing.T) { + module1 := ast.MustParseModule(`package a.b +import rego.v1 + +p if { data.x.q }`) + + module2 := ast.MustParseModule(`package x + +q = true`) + + modules := map[string]*ast.Module{ + "policy1.rego": module1, + "policy2.rego": module2, + } + + mappings := map[string]string{ + "data": "data.deadbeef", + } + + result, err := New().Move(MoveQuery{ + Modules: modules, + SrcDstMapping: mappings, + }) + if err != nil { + t.Fatal(err) + } + + actual1 := result.Result["policy1.rego"] + actual2 := result.Result["policy2.rego"] + + expected1 := ast.MustParseModule(`package deadbeef.a.b +import rego.v1 + +p if { + data.deadbeef.x.q +}`) + + expected2 := ast.MustParseModule(`package deadbeef.x + +q = true`) + + if !expected1.Equal(actual1) { + t.Fatalf("Expected module:\n%v\n\nGot:\n%v\n", expected1, actual1) + } + + if !expected2.Equal(actual2) { + t.Fatalf("Expected module:\n%v\n\nGot:\n%v\n", expected2, actual2) + } +} + +func TestMoveWithKeyword(t *testing.T) { + module1 := ast.MustParseModule(`package a.b +import rego.v1 +import data.x.q as r + +p if{ r with data.foo as 7 }`) + + module2 := ast.MustParseModule(`package x +import rego.v1 + +q if { data.foo == 7 }`) + + modules := map[string]*ast.Module{ + "policy1.rego": module1, + "policy2.rego": module2, + } + + mappings := map[string]string{ + "data": "data.deadbeef", + } + + result, err := New().Move(MoveQuery{ + Modules: modules, + SrcDstMapping: mappings, + }) + if err != nil { + t.Fatal(err) + } + + actual1 := result.Result["policy1.rego"] + actual2 := result.Result["policy2.rego"] + + expected1 := ast.MustParseModule(`package deadbeef.a.b +import rego.v1 +import data.deadbeef.x.q as r + +p if { + r with data.deadbeef.foo as 7 +}`) + + expected2 := ast.MustParseModule(`package deadbeef.x +import rego.v1 + +q if { + data.deadbeef.foo == 7 +}`) + + if !expected1.Equal(actual1) { + t.Fatalf("Expected module:\n%v\n\nGot:\n%v\n", expected1, actual1) + } + + if !expected2.Equal(actual2) { + t.Fatalf("Expected module:\n%v\n\nGot:\n%v\n", expected2, actual2) + } +} + +func TestMovePrefixTooShort(t *testing.T) { + module1 := ast.MustParseModule(`package foo.bar + +p = 7`) + + module2 := ast.MustParseModule(`package a + +p = data.foo`) + + modules := map[string]*ast.Module{ + "policy1.rego": module1, + "policy2.rego": module2, + } + + mappings := map[string]string{ + "data.foo.bar": "data.baz", + } + + _, err := New().Move(MoveQuery{ + Modules: modules, + SrcDstMapping: mappings, + }) + if err == nil { + t.Fatal("Expected error but got nil") + } + + errMsg := "cannot rewrite `data.foo`: constant prefix `data.foo` of `data.foo` is too short" + if !strings.Contains(err.Error(), errMsg) { + t.Fatalf("Expected error message %v but got %v", errMsg, err.Error()) + } +} + +func TestMovePrefixEmpty(t *testing.T) { + module1 := ast.MustParseModule(`package foo.bar.v1 +import rego.v1 + +helper_1 if { + to_number(split(input.baz, ".")[1]) >= 1 +} + +helper_2 if { + to_number(split(data.bar, ".")[1]) >= 1 +}`) + + modules := map[string]*ast.Module{ + "policy1.rego": module1, + } + + mappings := map[string]string{ + "data.foo": "data.hidden.name[\"hello:0.1\"]", + "data.bar": "data.hello", + } + + result, err := New().Move(MoveQuery{ + Modules: modules, + SrcDstMapping: mappings, + }) + if err != nil { + t.Fatal(err) + } + + actual := result.Result["policy1.rego"] + + expected := ast.MustParseModule(`package hidden.name["hello:0.1"].bar.v1 +import rego.v1 + +helper_1 if{ + to_number(split(input.baz, ".")[1]) >= 1 +} + +helper_2 if { + to_number(split(data.hello, ".")[1]) >= 1 +}`) + + if !expected.Equal(actual) { + t.Fatalf("Expected module:\n%v\n\nGot:\n%v\n", expected, actual) + } +} + +func TestMoveConflictingRulesNoValidation(t *testing.T) { + module1 := ast.MustParseModule(`package a.b +import rego.v1 + +p contains 1`) + + module2 := ast.MustParseModule(`package b + +p = 7`) + + modules := map[string]*ast.Module{ + "policy1.rego": module1, + "policy2.rego": module2, + } + + mappings := map[string]string{ + "data.a": "data", + } + + _, err := New().Move(MoveQuery{ + Modules: modules, + SrcDstMapping: mappings, + }) + if err != nil { + t.Fatal(err) + } +} + +func TestMoveConflictingRulesWithValidation(t *testing.T) { + module1 := ast.MustParseModule(`package a.b +import rego.v1 + +p contains 1`) + + module2 := ast.MustParseModule(`package b + +p = 7`) + + modules := map[string]*ast.Module{ + "policy1.rego": module1, + "policy2.rego": module2, + } + + mappings := map[string]string{ + "data.a": "data", + } + + _, err := New().Move(MoveQuery{ + Modules: modules, + SrcDstMapping: mappings, + }.WithValidation(true)) + if err == nil { + t.Fatal("Expected error but got nil") + } + + errMsg := "rego_type_error: conflicting rules data.b.p found" + if !strings.Contains(err.Error(), errMsg) { + t.Fatalf("Expected error message %v but got %v", errMsg, err.Error()) + } +} + +func TestMoveBadSourceMapping(t *testing.T) { + module := ast.MustParseModule(`package lib.foo +import rego.v1 + +default allow = false + +allow if { + input.message == "hello" +}`) + + modules := map[string]*ast.Module{ + "policy.rego": module, + } + + mappings := map[string]string{ + "data.lib.": "data.hidden", + } + + _, err := New().Move(MoveQuery{ + Modules: modules, + SrcDstMapping: mappings, + }) + if err == nil { + t.Fatal("Expected error but got nil") + } + + mappings = map[string]string{ + "data.lib": "data.hidden.", + } + + _, err = New().Move(MoveQuery{ + Modules: modules, + SrcDstMapping: mappings, + }) + if err == nil { + t.Fatal("Expected error but got nil") + } +} diff --git a/third_party/opa/v1/rego/errors.go b/third_party/opa/v1/rego/errors.go new file mode 100644 index 000000000000..dcc5e2679d12 --- /dev/null +++ b/third_party/opa/v1/rego/errors.go @@ -0,0 +1,24 @@ +package rego + +// HaltError is an error type to return from a custom function implementation +// that will abort the evaluation process (analogous to topdown.Halt). +type HaltError struct { + err error +} + +// Error delegates to the wrapped error +func (h *HaltError) Error() string { + return h.err.Error() +} + +// NewHaltError wraps an error such that the evaluation process will stop +// when it occurs. +func NewHaltError(err error) error { + return &HaltError{err: err} +} + +// ErrorDetails interface is satisfied by an error that provides further +// details. +type ErrorDetails interface { + Lines() []string +} diff --git a/third_party/opa/v1/rego/example_test.go b/third_party/opa/v1/rego/example_test.go new file mode 100644 index 000000000000..99d596dceed4 --- /dev/null +++ b/third_party/opa/v1/rego/example_test.go @@ -0,0 +1,1091 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint // example code +package rego_test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/storage/disk" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +func ExampleRego_Eval_simple() { + + ctx := context.Background() + + // Create very simple query that binds a single variable. + rego := rego.New(rego.Query("x = 1")) + + // Run evaluation. + rs, err := rego.Eval(ctx) + + // Inspect results. + fmt.Println("len:", len(rs)) + fmt.Println("bindings:", rs[0].Bindings) + fmt.Println("err:", err) + + // Output: + // + // len: 1 + // bindings: map[x:1] + // err: +} + +func ExampleRego_Eval_input() { + + ctx := context.Background() + + // Raw input data that will be used in evaluation. + raw := `{"users": [{"id": "bob"}, {"id": "alice"}]}` + d := json.NewDecoder(bytes.NewBufferString(raw)) + + // Numeric values must be represented using json.Number. + d.UseNumber() + + var input any + + if err := d.Decode(&input); err != nil { + panic(err) + } + + // Create a simple query over the input. + rego := rego.New( + rego.Query("input.users[idx].id = user_id"), + rego.Input(input)) + + //Run evaluation. + rs, err := rego.Eval(ctx) + + if err != nil { + // Handle error. + } + + // Inspect results. + fmt.Println("len:", len(rs)) + fmt.Println("bindings.idx:", rs[1].Bindings["idx"]) + fmt.Println("bindings.user_id:", rs[1].Bindings["user_id"]) + + // Output: + // + // len: 2 + // bindings.idx: 1 + // bindings.user_id: alice +} + +func ExampleRego_Eval_multipleBindings() { + + ctx := context.Background() + + // Create query that produces multiple bindings for variable. + rego := rego.New( + rego.Query(`a = ["ex", "am", "ple"]; x = a[_]; not p[x]`), + rego.Package(`example`), + rego.Module("example.rego", `package example + + import rego.v1 + + p contains "am" if { true } + `), + ) + + // Run evaluation. + rs, err := rego.Eval(ctx) + + // Inspect results. + fmt.Println("len:", len(rs)) + fmt.Println("err:", err) + for i := range rs { + fmt.Printf("bindings[\"x\"]: %v (i=%d)\n", rs[i].Bindings["x"], i) + } + + // Output: + // + // len: 2 + // err: + // bindings["x"]: ex (i=0) + // bindings["x"]: ple (i=1) +} + +func ExampleRego_Eval_singleDocument() { + + ctx := context.Background() + + // Create query that produces a single document. + rego := rego.New( + rego.Query("data.example.p"), + rego.Module("example.rego", + `package example + +import rego.v1 + +p = ["hello", "world"] if { true }`, + )) + + // Run evaluation. + rs, err := rego.Eval(ctx) + + // Inspect result. + fmt.Println("value:", rs[0].Expressions[0].Value) + fmt.Println("err:", err) + + // Output: + // + // value: [hello world] + // err: +} + +func ExampleRego_Eval_allowed() { + + ctx := context.Background() + + // Create query that returns a single boolean value. + rego := rego.New( + rego.Query("data.authz.allow"), + rego.Module("example.rego", + `package authz + +import rego.v1 + +default allow = false +allow if { + input.open == "sesame" +}`, + ), + rego.Input(map[string]any{"open": "sesame"}), + ) + + // Run evaluation. + rs, err := rego.Eval(ctx) + if err != nil { + panic(err) + } + + // Inspect result. + fmt.Println("allowed:", rs.Allowed()) + + // Output: + // + // allowed: true +} + +func ExampleRego_Eval_multipleDocuments() { + + ctx := context.Background() + + // Create query that produces multiple documents. + rego := rego.New( + rego.Query("data.example.p[x]"), + rego.Module("example.rego", + `package example + +import rego.v1 + +p = {"hello": "alice", "goodbye": "bob"} if { true }`, + )) + + // Run evaluation. + rs, err := rego.Eval(ctx) + + // Inspect results. + fmt.Println("len:", len(rs)) + fmt.Println("err:", err) + for i := range rs { + fmt.Printf("bindings[\"x\"]: %v (i=%d)\n", rs[i].Bindings["x"], i) + fmt.Printf("value: %v (i=%d)\n", rs[i].Expressions[0].Value, i) + } + + // Output: + // + // len: 2 + // err: + // bindings["x"]: goodbye (i=0) + // value: bob (i=0) + // bindings["x"]: hello (i=1) + // value: alice (i=1) +} + +func ExampleRego_Eval_compiler() { + + ctx := context.Background() + + // Define a simple policy. + module := ` + package example + + import rego.v1 + + default allow = false + + allow if { + input.identity = "admin" + } + + allow if { + input.method = "GET" + } + ` + + // Compile the module. The keys are used as identifiers in error messages. + compiler, err := ast.CompileModules(map[string]string{ + "example.rego": module, + }) + + // Create a new query that uses the compiled policy from above. + rego := rego.New( + rego.Query("data.example.allow"), + rego.Compiler(compiler), + rego.Input( + map[string]any{ + "identity": "bob", + "method": "GET", + }, + ), + ) + + // Run evaluation. + rs, err := rego.Eval(ctx) + + if err != nil { + // Handle error. + } + + // Inspect results. + fmt.Println("len:", len(rs)) + fmt.Println("value:", rs[0].Expressions[0].Value) + fmt.Println("allowed:", rs.Allowed()) // helper method + + // Output: + // + // len: 1 + // value: true + // allowed: true +} + +func ExampleRego_Eval_storage() { + + ctx := context.Background() + + data := `{ + "example": { + "users": [ + { + "name": "alice", + "likes": ["dogs", "clouds"] + }, + { + "name": "bob", + "likes": ["pizza", "cats"] + } + ] + } + }` + + var json map[string]any + + err := util.UnmarshalJSON([]byte(data), &json) + if err != nil { + // Handle error. + } + + // Manually create the storage layer. inmem.NewFromObject returns an + // in-memory store containing the supplied data. + store := inmem.NewFromObject(json) + + // Create new query that returns the value + rego := rego.New( + rego.Query("data.example.users[0].likes"), + rego.Store(store)) + + // Run evaluation. + rs, err := rego.Eval(ctx) + if err != nil { + // Handle error. + } + + // Inspect the result. + fmt.Println("value:", rs[0].Expressions[0].Value) + + // Output: + // + // value: [dogs clouds] +} + +func ExampleRego_Eval_persistent_storage() { + + ctx := context.Background() + + data := `{ + "example": { + "users": { + "alice": { + "likes": ["dogs", "clouds"] + }, + "bob": { + "likes": ["pizza", "cats"] + } + } + } + }` + + var json map[string]any + + err := util.UnmarshalJSON([]byte(data), &json) + if err != nil { + // Handle error. + } + + // Manually create a persistent storage-layer in a temporary directory. + rootDir, err := os.MkdirTemp("", "rego_example") + if err != nil { + panic(err) + } + + defer os.RemoveAll(rootDir) + + // Configure the store to partition data at `/example/users` so that each + // user's data is stored on a different row. Assuming the policy only reads + // data for a single user to process the policy query, OPA can avoid loading + // _all_ user data into memory this way. + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: rootDir, + Partitions: []storage.Path{{"example", "user"}}, + }) + if err != nil { + // Handle error. + } + + err = storage.WriteOne(ctx, store, storage.AddOp, storage.Path{}, json) + if err != nil { + // Handle error + } + + // Run a query that returns the value + rs, err := rego.New( + rego.Query(`data.example.users["alice"].likes`), + rego.Store(store)).Eval(ctx) + if err != nil { + // Handle error. + } + + // Inspect the result. + fmt.Println("value:", rs[0].Expressions[0].Value) + + // Re-open the store in the same directory. + store.Close(ctx) + + store2, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: rootDir, + Partitions: []storage.Path{{"example", "user"}}, + }) + if err != nil { + // Handle error. + } + + // Run the same query with a new store. + rs, err = rego.New( + rego.Query(`data.example.users["alice"].likes`), + rego.Store(store2)).Eval(ctx) + if err != nil { + // Handle error. + } + + // Inspect the result and observe the same result. + fmt.Println("value:", rs[0].Expressions[0].Value) + + // Output: + // + // value: [dogs clouds] + // value: [dogs clouds] +} + +func ExampleRego_Eval_transactions() { + + ctx := context.Background() + + // Create storage layer and load dummy data. + store := inmem.NewFromReader(bytes.NewBufferString(`{ + "favourites": { + "pizza": "cheese", + "colour": "violet" + } + }`)) + + // Open a write transaction on the store that will perform write operations. + txn, err := store.NewTransaction(ctx, storage.WriteParams) + if err != nil { + // Handle error. + } + + // Create rego query that uses the transaction created above. + inside := rego.New( + rego.Query("data.favourites.pizza"), + rego.Store(store), + rego.Transaction(txn), + ) + + // Create rego query that DOES NOT use the transaction created above. Under + // the hood, the rego package will create it's own transaction to + // ensure it evaluates over a consistent snapshot of the storage layer. + outside := rego.New( + rego.Query("data.favourites.pizza"), + rego.Store(store), + ) + + // Write change to storage layer inside the transaction. + err = store.Write(ctx, txn, storage.AddOp, storage.MustParsePath("/favourites/pizza"), "pepperoni") + if err != nil { + // Handle error. + } + + // Run evaluation INSIDE the transaction. + rs, err := inside.Eval(ctx) + if err != nil { + // Handle error. + } + + fmt.Println("value (inside txn):", rs[0].Expressions[0].Value) + + // Run evaluation OUTSIDE the transaction. + rs, err = outside.Eval(ctx) + if err != nil { + // Handle error. + } + + fmt.Println("value (outside txn):", rs[0].Expressions[0].Value) + + if err := store.Commit(ctx, txn); err != nil { + // Handle error. + } + + // Run evaluation AFTER the transaction commits. + rs, err = outside.Eval(ctx) + if err != nil { + // Handle error. + } + + fmt.Println("value (after txn):", rs[0].Expressions[0].Value) + + // Output: + // + // value (inside txn): pepperoni + // value (outside txn): cheese + // value (after txn): pepperoni +} + +func ExampleRego_Eval_errors() { + + ctx := context.Background() + + r := rego.New( + rego.Query("data.example.p"), + rego.Module("example_error.rego", + `package example +import rego.v1 + +p = true if { not q[x] } +q = {1, 2, 3} if { true }`, + )) + + _, err := r.Eval(ctx) + + switch err := err.(type) { + case ast.Errors: + for _, e := range err { + fmt.Println("code:", e.Code) + fmt.Println("row:", e.Location.Row) + fmt.Println("filename:", e.Location.File) + } + default: + // Some other error occurred. + } + + // Output: + // + // code: rego_unsafe_var_error + // row: 4 + // filename: example_error.rego +} + +func ExampleRego_PartialResult() { + + ctx := context.Background() + + // Define a role-based access control (RBAC) policy that decides whether to + // allow or deny requests. Requests are allowed if the user is bound to a + // role that grants permission to perform the operation on the resource. + module := ` + package example + + import rego.v1 + import data.bindings + import data.roles + + default allow = false + + allow if { + user_has_role[role_name] + role_has_permission[role_name] + } + + user_has_role contains role_name if { + b = bindings[_] + b.role = role_name + b.user = input.subject.user + } + + role_has_permission contains role_name if { + r = roles[_] + r.name = role_name + match_with_wildcard(r.operations, input.operation) + match_with_wildcard(r.resources, input.resource) + } + + match_with_wildcard(allowed, value) if { + allowed[_] = "*" + } + + match_with_wildcard(allowed, value) if { + allowed[_] = value + } + ` + + // Define dummy roles and role bindings for the example. In real-world + // scenarios, this data would be pushed or pulled into the service + // embedding OPA either from an external API or configuration file. + store := inmem.NewFromReader(bytes.NewBufferString(`{ + "roles": [ + { + "resources": ["documentA", "documentB"], + "operations": ["read"], + "name": "analyst" + }, + { + "resources": ["*"], + "operations": ["*"], + "name": "admin" + } + ], + "bindings": [ + { + "user": "bob", + "role": "admin" + }, + { + "user": "alice", + "role": "analyst" + } + ] + }`)) + + // Prepare and run partial evaluation on the query. The result of partial + // evaluation can be cached for performance. When the data or policy + // change, partial evaluation should be re-run. + r := rego.New( + rego.Query("data.example.allow"), + rego.Module("example.rego", module), + rego.Store(store), + ) + + pr, err := r.PartialResult(ctx) + if err != nil { + // Handle error. + } + + // Define example inputs (representing requests) that will be used to test + // the policy. + examples := []map[string]any{ + { + "resource": "documentA", + "operation": "write", + "subject": map[string]any{ + "user": "bob", + }, + }, + { + "resource": "documentB", + "operation": "write", + "subject": map[string]any{ + "user": "alice", + }, + }, + { + "resource": "documentB", + "operation": "read", + "subject": map[string]any{ + "user": "alice", + }, + }, + } + + for i := range examples { + + // Prepare and run normal evaluation from the result of partial + // evaluation. + r := pr.Rego( + rego.Input(examples[i]), + ) + + rs, err := r.Eval(ctx) + + if err != nil || len(rs) != 1 || len(rs[0].Expressions) != 1 { + // Handle erorr. + } else { + fmt.Printf("input %d allowed: %v\n", i+1, rs[0].Expressions[0].Value) + } + } + + // Output: + // + // input 1 allowed: true + // input 2 allowed: false + // input 3 allowed: true +} + +func ExampleRego_Partial() { + + ctx := context.Background() + + // Define a simple policy for example purposes. + module := `package test + + import rego.v1 + + allow if { + input.method = read_methods[_] + input.path = ["reviews", user] + input.user = user + } + + allow if { + input.method = read_methods[_] + input.path = ["reviews", _] + input.is_admin + } + + read_methods = ["GET"] + ` + + r := rego.New(rego.Query("data.test.allow == true"), rego.Module("example.rego", module)) + pq, err := r.Partial(ctx) + if err != nil { + // Handle error. + } + + // Inspect result. + for i := range pq.Queries { + fmt.Printf("Query #%d: %v\n", i+1, pq.Queries[i]) + } + + // Output: + // + // Query #1: "GET" = input.method; input.path = ["reviews", _]; input.is_admin + // Query #2: "GET" = input.method; input.path = ["reviews", user3]; user3 = input.user +} + +func ExampleRego_Eval_trace_simple() { + + ctx := context.Background() + + // Create very simple query that binds a single variable and enables tracing. + r := rego.New( + rego.Query("x = 1"), + rego.Trace(true), + ) + + // Run evaluation. + r.Eval(ctx) + + // Inspect results. + rego.PrintTraceWithLocation(os.Stdout, r) + + // Output: + // + // query:1 Enter x = 1 + // query:1 | Eval x = 1 + // query:1 | Unify x = 1 + // query:1 | Exit x = 1 + // query:1 Redo x = 1 + // query:1 | Redo x = 1 +} + +func ExampleRego_Eval_tracer() { + + ctx := context.Background() + + buf := topdown.NewBufferTracer() + + // Create very simple query that binds a single variable and provides a tracer. + rego := rego.New( + rego.Query("x = 1"), + rego.QueryTracer(buf), + ) + + // Run evaluation. + rego.Eval(ctx) + + // Inspect results. + topdown.PrettyTraceWithLocation(os.Stdout, *buf) + + // Output: + // + // query:1 Enter x = 1 + // query:1 | Eval x = 1 + // query:1 | Unify x = 1 + // query:1 | Exit x = 1 + // query:1 Redo x = 1 + // query:1 | Redo x = 1 +} + +func ExampleRego_PrepareForEval() { + ctx := context.Background() + + // Create a simple query + r := rego.New( + rego.Query("input.x == 1"), + ) + + // Prepare for evaluation + pq, err := r.PrepareForEval(ctx) + + if err != nil { + // Handle error. + } + + // Raw input data that will be used in the first evaluation + input := map[string]any{"x": 2} + + // Run the evaluation + rs, err := pq.Eval(ctx, rego.EvalInput(input)) + + if err != nil { + // Handle error. + } + + // Inspect results. + fmt.Println("initial result:", rs[0].Expressions[0]) + + // Update input + input["x"] = 1 + + // Run the evaluation with new input + rs, err = pq.Eval(ctx, rego.EvalInput(input)) + + if err != nil { + // Handle error. + } + + // Inspect results. + fmt.Println("updated result:", rs[0].Expressions[0]) + + // Output: + // + // initial result: false + // updated result: true +} + +func ExampleRego_PrepareForPartial() { + + ctx := context.Background() + + // Define a simple policy for example purposes. + module := `package test + + import rego.v1 + + allow if { + input.method = read_methods[_] + input.path = ["reviews", user] + input.user = user + } + + allow if { + input.method = read_methods[_] + input.path = ["reviews", _] + input.is_admin + } + + read_methods = ["GET"] + ` + + r := rego.New( + rego.Query("data.test.allow == true"), + rego.Module("example.rego", module), + ) + + pq, err := r.PrepareForPartial(ctx) + if err != nil { + // Handle error. + } + + pqs, err := pq.Partial(ctx) + if err != nil { + // Handle error. + } + + // Inspect result + fmt.Println("First evaluation") + for i := range pqs.Queries { + fmt.Printf("Query #%d: %v\n", i+1, pqs.Queries[i]) + } + + // Evaluate with specified input + exampleInput := map[string]string{ + "method": "GET", + } + + // Evaluate again with different input and unknowns + pqs, err = pq.Partial(ctx, + rego.EvalInput(exampleInput), + rego.EvalUnknowns([]string{"input.user", "input.is_admin", "input.path"}), + ) + if err != nil { + // Handle error. + } + + // Inspect result + fmt.Println("Second evaluation") + for i := range pqs.Queries { + fmt.Printf("Query #%d: %v\n", i+1, pqs.Queries[i]) + } + + // Output: + // + // First evaluation + // Query #1: "GET" = input.method; input.path = ["reviews", _]; input.is_admin + // Query #2: "GET" = input.method; input.path = ["reviews", user3]; user3 = input.user + // Second evaluation + // Query #1: input.path = ["reviews", _]; input.is_admin + // Query #2: input.path = ["reviews", user3]; user3 = input.user +} + +func ExampleRego_custom_functional_builtin() { + + r := rego.New( + // An example query that uses a custom function. + rego.Query(`x = trim_and_split("/foo/bar/baz/", "/")`), + + // A custom function that trims and splits strings on the same delimiter. + rego.Function2( + ®o.Function{ + Name: "trim_and_split", + Decl: types.NewFunction( + types.Args(types.S, types.S), // two string inputs + types.NewArray(nil, types.S), // variable-length string array output + ), + }, + func(_ rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error) { + + str, ok1 := a.Value.(ast.String) + delim, ok2 := b.Value.(ast.String) + + // The function is undefined for non-string inputs. Built-in + // functions should only return errors in unrecoverable cases. + if !ok1 || !ok2 { + return nil, nil + } + + result := strings.Split(strings.Trim(string(str), string(delim)), string(delim)) + + arr := make([]*ast.Term, len(result)) + for i := range result { + arr[i] = ast.StringTerm(result[i]) + } + + return ast.ArrayTerm(arr...), nil + }, + ), + ) + + rs, err := r.Eval(context.Background()) + if err != nil { + // handle error + } + + fmt.Println(rs[0].Bindings["x"]) + + // Output: + // + // [foo bar baz] +} + +func ExampleRego_custom_function_caching() { + i := 0 + + r := rego.New( + // An example query that uses a custom function. + rego.Query(`x = mycounter("foo"); y = mycounter("foo")`), + + // A custom function that uses caching. + rego.FunctionDyn( + ®o.Function{ + Name: "mycounter", + Memoize: true, + Decl: types.NewFunction( + types.Args(types.S), // one string input + types.N, // one number output + ), + }, + func(_ topdown.BuiltinContext, args []*ast.Term) (*ast.Term, error) { + i++ + return ast.IntNumberTerm(i), nil + }, + ), + ) + + rs, err := r.Eval(context.Background()) + if err != nil { + // handle error + } + + fmt.Println("x:", rs[0].Bindings["x"]) + fmt.Println("y:", rs[0].Bindings["y"]) + + // Output: + // + // x: 1 + // y: 1 +} + +func ExampleRego_custom_function_nondeterministic() { + ndbCache := builtins.NDBCache{} + r := rego.New( + // An example query that uses a custom function. + rego.Query(`x = myrandom()`), + + // A custom function that uses caching. + rego.FunctionDyn( + ®o.Function{ + Name: "myrandom", + Memoize: true, + Nondeterministic: true, + Decl: types.NewFunction( + nil, // one string input + types.N, // one number output + ), + }, + func(_ topdown.BuiltinContext, args []*ast.Term) (*ast.Term, error) { + i := 55555 + return ast.IntNumberTerm(i), nil + }, + ), + rego.NDBuiltinCache(ndbCache), + ) + + rs, err := r.Eval(context.Background()) + if err != nil { + // handle error + } + + // Check the binding, and what the NDBCache saw. + // This ensures the Nondeterministic flag propagated through correctly. + fmt.Println("x:", rs[0].Bindings["x"]) + fmt.Println("NDBCache:", ndbCache["myrandom"]) + + // Output: + // + // x: 55555 + // NDBCache: {[]: 55555} +} + +func ExampleRego_custom_function_global() { + + decl := ®o.Function{ + Name: "trim_and_split", + Decl: types.NewFunction( + types.Args(types.S, types.S), // two string inputs + types.NewArray(nil, types.S), // variable-length string array output + ), + } + + impl := func(_ rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error) { + + str, ok1 := a.Value.(ast.String) + delim, ok2 := b.Value.(ast.String) + + // The function is undefined for non-string inputs. Built-in + // functions should only return errors in unrecoverable cases. + if !ok1 || !ok2 { + return nil, nil + } + + result := strings.Split(strings.Trim(string(str), string(delim)), string(delim)) + + arr := make([]*ast.Term, len(result)) + for i := range result { + arr[i] = ast.StringTerm(result[i]) + } + + return ast.ArrayTerm(arr...), nil + } + + // The rego package exports helper functions for different arities and a + // special version of the function that accepts a dynamic number. + rego.RegisterBuiltin2(decl, impl) + + r := rego.New( + // An example query that uses a custom function. + rego.Query(`x = trim_and_split("/foo/bar/baz/", "/")`), + ) + + rs, err := r.Eval(context.Background()) + if err != nil { + // handle error + } + + fmt.Println(rs[0].Bindings["x"]) + + // Output: + // + // [foo bar baz] +} + +func ExampleRego_print_statements() { + + var buf bytes.Buffer + + r := rego.New( + rego.Query("data.example.rule_containing_print_call"), + rego.Module("example.rego", ` + package example + + import rego.v1 + + rule_containing_print_call if { + print("input.foo is:", input.foo, "and input.bar is:", input.bar) + } + `), + rego.Input(map[string]any{ + "foo": 7, + }), + rego.EnablePrintStatements(true), + rego.PrintHook(topdown.NewPrintHook(&buf)), + ) + + _, err := r.Eval(context.Background()) + if err != nil { + // handle error + } + + fmt.Println("buf:", buf.String()) + + // Output: + // + // buf: input.foo is: 7 and input.bar is: +} diff --git a/third_party/opa/v1/rego/plugins.go b/third_party/opa/v1/rego/plugins.go new file mode 100644 index 000000000000..55b5ed7803ab --- /dev/null +++ b/third_party/opa/v1/rego/plugins.go @@ -0,0 +1,43 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package rego + +import ( + "context" + "sync" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ir" +) + +var targetPlugins = map[string]TargetPlugin{} +var pluginMtx sync.Mutex + +type TargetPlugin interface { + IsTarget(string) bool + PrepareForEval(context.Context, *ir.Policy, ...PrepareOption) (TargetPluginEval, error) +} + +type TargetPluginEval interface { + Eval(context.Context, *EvalContext, ast.Value) (ast.Value, error) +} + +func (*Rego) targetPlugin(tgt string) TargetPlugin { + for _, p := range targetPlugins { + if p.IsTarget(tgt) { + return p + } + } + return nil +} + +func RegisterPlugin(name string, p TargetPlugin) { + pluginMtx.Lock() + defer pluginMtx.Unlock() + if _, ok := targetPlugins[name]; ok { + panic("plugin already registered " + name) + } + targetPlugins[name] = p +} diff --git a/third_party/opa/v1/rego/plugins_test.go b/third_party/opa/v1/rego/plugins_test.go new file mode 100644 index 000000000000..6dcb10124406 --- /dev/null +++ b/third_party/opa/v1/rego/plugins_test.go @@ -0,0 +1,219 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package rego + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ir" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/tracing" + "github.com/open-policy-agent/opa/v1/types" +) + +type testPlugin struct { + builtinFuncs map[string]*topdown.Builtin + state string +} + +func (*testPlugin) IsTarget(t string) bool { + return t == "foo" +} + +func (*testPlugin) PrepareForEval(_ context.Context, _ *ir.Policy, po ...PrepareOption) (TargetPluginEval, error) { + pc := &PrepareConfig{} + for _, o := range po { + o(pc) + } + return &testPlugin{ + builtinFuncs: pc.BuiltinFuncs(), + state: "newstate", + }, nil +} + +func (t *testPlugin) Eval(_ context.Context, _ *EvalContext, rt ast.Value) (ast.Value, error) { + if rt != nil { + return ast.NewSet(ast.NewTerm(ast.NewObject( + [2]*ast.Term{ast.StringTerm("^term1"), ast.ObjectTerm([2]*ast.Term{ast.StringTerm(t.state), ast.NewTerm(rt)})}, + ))), nil + } + return ast.NewSet(ast.NewTerm(ast.NewObject( + [2]*ast.Term{ast.StringTerm("^term1"), ast.StringTerm(t.state)}, + ))), nil +} + +// Warning(philipc): This test modifies package variables, which means it cannot +// be run safely in parallel with other tests. +func TestTargetViaPlugin(t *testing.T) { + tp := testPlugin{} + RegisterPlugin("rego.target.foo", &tp) + t.Cleanup(resetPlugins) + r := New( + Query("input"), + Input("original-input"), + Target("foo"), + Runtime(ast.StringTerm("runtime")), + ) + assertEval(t, r, `[[{"newstate": "runtime"}]]`) +} + +type defaultPlugin struct { + testPlugin +} + +func (*defaultPlugin) IsTarget(t string) bool { return t == "" || t == "foo" } + +// Warning(philipc): This test modifies package variables, which means it cannot +// be run safely in parallel with other tests. +func TestTargetViaDefaultPlugin(t *testing.T) { + t.Run("no target", func(t *testing.T) { + tp := defaultPlugin{testPlugin{}} + RegisterPlugin("rego.target.foo", &tp) + t.Cleanup(resetPlugins) + r := New( + Query("input"), + Input("original-input"), + ) + assertEval(t, r, `[["newstate"]]`) + }) + + t.Run("other target NOT overridden", func(t *testing.T) { + tp := defaultPlugin{testPlugin{}} + RegisterPlugin("rego.target.foo", &tp) + t.Cleanup(resetPlugins) + r := New( + Query("input"), + Input("original-input"), + Target("rego"), + ) + assertEval(t, r, `[["original-input"]]`) + }) +} + +// Warning(philipc): This test modifies package variables, which means it cannot +// be run safely in parallel with other tests. +func TestPluginPrepareOptions(t *testing.T) { + ctx := context.Background() + tp := testPlugin{} + RegisterPlugin("rego.target.foo", &tp) + t.Cleanup(resetPlugins) + + t.Run("passed to PrepareForEval", func(t *testing.T) { + r := New( + Query("input"), + Input("original-input"), + Target("foo"), + Runtime(ast.StringTerm("runtime")), + ) + bi := map[string]*topdown.Builtin{ + "count": { + Decl: ast.BuiltinMap["count"], + Func: topdown.GetBuiltin("count"), + }, + } + pq, err := r.PrepareForEval(ctx, WithBuiltinFuncs(bi)) + if err != nil { + t.Fatalf("PrepareForEval: %v", err) + } + assertPreparedEvalQueryEval(t, pq, nil, `[[{"newstate": "runtime"}]]`) + + // NOTE(sr): To assert what we want, we'll have to reach into the internals + // here. Typically, the _effect_ of the PrepareOptions passed to the plugin + // would be in the evalution done by the plugin. But our test plugin here does + // not really do anything. + internals := r.targetPrepState.(*testPlugin) + act, exp := internals.builtinFuncs, bi + if diff := cmp.Diff(exp, act, + cmpopts.IgnoreUnexported(ast.Builtin{}, types.Function{}), + cmpopts.IgnoreFields(topdown.Builtin{}, "Func")); diff != "" { + t.Errorf("unexpected result (-want, +got):\n%s", diff) + } + }) + + t.Run("passed to New", func(t *testing.T) { + cpy := ast.BuiltinMap["count"] + cpy.Description = "" + cpy.Categories = nil + bi := map[string]*topdown.Builtin{ + "count": { + Decl: cpy, + Func: topdown.GetBuiltin("count"), + }, + } + r := New( + Query("input"), + Input("original-input"), + Target("foo"), + Runtime(ast.StringTerm("runtime")), + Function1(&Function{ + Name: "count", + Decl: bi["count"].Decl.Decl, + }, func(BuiltinContext, *ast.Term) (*ast.Term, error) { return nil, nil }), + ) + assertEval(t, r, `[[{"newstate": "runtime"}]]`) + + // NOTE(sr): To assert what we want, we'll have to reach into the internals + // here. Typically, the _effect_ of the PrepareOptions passed to the plugin + // would be in the evalution done by the plugin. But our test plugin here does + // not really do anything. + internals := r.targetPrepState.(*testPlugin) + act, exp := internals.builtinFuncs, bi + if diff := cmp.Diff(exp, act, + cmpopts.IgnoreUnexported(ast.Builtin{}, types.Function{}), + cmpopts.IgnoreFields(topdown.Builtin{}, "Func")); diff != "" { + t.Errorf("unexpected result (-want, +got):\n%s", diff) + } + }) +} + +// Warning(philipc): This test modifies package variables, which means it cannot +// be run safely in parallel with other tests. +func TestDistributedTracingOptsOnEvalContext(t *testing.T) { + tp := testPluginDT{} + RegisterPlugin("rego.target.foo_dt", &tp) + t.Cleanup(resetPlugins) + r := New( + Query("input"), + Target("foo_dt"), + Runtime(ast.StringTerm("runtime")), + DistributedTracingOpts(tracing.NewOptions("hey")), + ) + assertEval(t, r, `[[{"x":"hey"}]]`) +} + +type testPluginDT struct{} + +func (*testPluginDT) IsTarget(t string) bool { + return t == "foo_dt" +} + +func (t *testPluginDT) PrepareForEval(context.Context, *ir.Policy, ...PrepareOption) (TargetPluginEval, error) { + return t, nil +} + +func (*testPluginDT) Eval(_ context.Context, ectx *EvalContext, _ ast.Value) (ast.Value, error) { + if l := len(ectx.TracingOpts()); l != 1 { + return nil, fmt.Errorf("expected ectx.TracingOpts of len 1, got %d", l) + } + return ast.NewSet(ast.NewTerm(ast.NewObject( + [2]*ast.Term{ + ast.StringTerm("^term1"), + ast.ObjectTerm( + [2]*ast.Term{ + ast.StringTerm("x"), + ast.StringTerm(fmt.Sprintf("%v", ectx.TracingOpts()[0])), + }), + }))), nil +} + +func resetPlugins() { + targetPlugins = map[string]TargetPlugin{} +} diff --git a/third_party/opa/v1/rego/prepare_test.go b/third_party/opa/v1/rego/prepare_test.go new file mode 100644 index 000000000000..cea0e1f975cf --- /dev/null +++ b/third_party/opa/v1/rego/prepare_test.go @@ -0,0 +1,41 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package rego_test + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/types" +) + +// NOTE(sr): These test are here because the only cases where PrepareOption are +// used is outside of the rego package. Testing them within the rego package +// would be less realistic. +func TestPrepareOption(t *testing.T) { + t.Parallel() + + t.Run("BuiltinFuncs", func(t *testing.T) { + bi := map[string]*topdown.Builtin{ + "count": { + Decl: ast.BuiltinMap["count"], + Func: topdown.GetBuiltin("count"), + }, + } + pc := ®o.PrepareConfig{} + rego.WithBuiltinFuncs(bi)(pc) + act, exp := pc.BuiltinFuncs(), bi + if diff := cmp.Diff(exp, act, + cmpopts.IgnoreUnexported(ast.Builtin{}, types.Function{}), + cmpopts.IgnoreFields(topdown.Builtin{}, "Func")); diff != "" { + t.Errorf("unexpected result (-want, +got):\n%s", diff) + } + }) +} diff --git a/third_party/opa/v1/rego/rego.go b/third_party/opa/v1/rego/rego.go new file mode 100644 index 000000000000..6c8c482b82cf --- /dev/null +++ b/third_party/opa/v1/rego/rego.go @@ -0,0 +1,3004 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package rego exposes high level APIs for evaluating Rego policies. +package rego + +import ( + "context" + "errors" + "fmt" + "io" + "maps" + "strings" + "time" + + bundleUtils "github.com/open-policy-agent/opa/internal/bundle" + "github.com/open-policy-agent/opa/internal/future" + "github.com/open-policy-agent/opa/internal/planner" + "github.com/open-policy-agent/opa/internal/rego/opa" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/ir" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/resolver" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/print" + "github.com/open-policy-agent/opa/v1/tracing" + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + defaultPartialNamespace = "partial" + wasmVarPrefix = "^" +) + +// nolint: deadcode,varcheck +const ( + targetWasm = "wasm" + targetRego = "rego" +) + +// CompileResult represents the result of compiling a Rego query, zero or more +// Rego modules, and arbitrary contextual data into an executable. +type CompileResult struct { + Bytes []byte `json:"bytes"` +} + +// PartialQueries contains the queries and support modules produced by partial +// evaluation. +type PartialQueries struct { + Queries []ast.Body `json:"queries,omitempty"` + Support []*ast.Module `json:"modules,omitempty"` +} + +// PartialResult represents the result of partial evaluation. The result can be +// used to generate a new query that can be run when inputs are known. +type PartialResult struct { + compiler *ast.Compiler + store storage.Store + body ast.Body + builtinDecls map[string]*ast.Builtin + builtinFuncs map[string]*topdown.Builtin +} + +// Rego returns an object that can be evaluated to produce a query result. +func (pr PartialResult) Rego(options ...func(*Rego)) *Rego { + options = append(options, Compiler(pr.compiler), Store(pr.store), ParsedQuery(pr.body)) + r := New(options...) + + // Propagate any custom builtins. + maps.Copy(r.builtinDecls, pr.builtinDecls) + maps.Copy(r.builtinFuncs, pr.builtinFuncs) + return r +} + +// preparedQuery is a wrapper around a Rego object which has pre-processed +// state stored on it. Once prepared there are a more limited number of actions +// that can be taken with it. It will, however, be able to evaluate faster since +// it will not have to re-parse or compile as much. +type preparedQuery struct { + r *Rego + cfg *PrepareConfig +} + +// EvalContext defines the set of options allowed to be set at evaluation +// time. Any other options will need to be set on a new Rego object. +type EvalContext struct { + hasInput bool + time time.Time + seed io.Reader + rawInput *any + parsedInput ast.Value + metrics metrics.Metrics + txn storage.Transaction + instrument bool + instrumentation *topdown.Instrumentation + partialNamespace string + queryTracers []topdown.QueryTracer + compiledQuery compiledQuery + unknowns []string + disableInlining []ast.Ref + nondeterministicBuiltins bool + parsedUnknowns []*ast.Term + indexing bool + earlyExit bool + interQueryBuiltinCache cache.InterQueryCache + interQueryBuiltinValueCache cache.InterQueryValueCache + ndBuiltinCache builtins.NDBCache + resolvers []refResolver + httpRoundTripper topdown.CustomizeRoundTripper + sortSets bool + copyMaps bool + printHook print.Hook + capabilities *ast.Capabilities + strictBuiltinErrors bool + virtualCache topdown.VirtualCache + baseCache topdown.BaseCache + tracing tracing.Options + externalCancel topdown.Cancel // Note(philip): If non-nil, the cancellation is handled outside of this package. +} + +func (e *EvalContext) RawInput() *any { + return e.rawInput +} + +func (e *EvalContext) ParsedInput() ast.Value { + return e.parsedInput +} + +func (e *EvalContext) Time() time.Time { + return e.time +} + +func (e *EvalContext) Seed() io.Reader { + return e.seed +} + +func (e *EvalContext) InterQueryBuiltinCache() cache.InterQueryCache { + return e.interQueryBuiltinCache +} + +func (e *EvalContext) InterQueryBuiltinValueCache() cache.InterQueryValueCache { + return e.interQueryBuiltinValueCache +} + +func (e *EvalContext) PrintHook() print.Hook { + return e.printHook +} + +func (e *EvalContext) Metrics() metrics.Metrics { + return e.metrics +} + +func (e *EvalContext) StrictBuiltinErrors() bool { + return e.strictBuiltinErrors +} + +func (e *EvalContext) NDBCache() builtins.NDBCache { + return e.ndBuiltinCache +} + +func (e *EvalContext) CompiledQuery() ast.Body { + return e.compiledQuery.query +} + +func (e *EvalContext) Capabilities() *ast.Capabilities { + return e.capabilities +} + +func (e *EvalContext) Transaction() storage.Transaction { + return e.txn +} + +func (e *EvalContext) TracingOpts() tracing.Options { + return e.tracing +} + +func (e *EvalContext) ExternalCancel() topdown.Cancel { + return e.externalCancel +} + +func (e *EvalContext) QueryTracers() []topdown.QueryTracer { + return e.queryTracers +} + +// EvalOption defines a function to set an option on an EvalConfig +type EvalOption func(*EvalContext) + +// EvalInput configures the input for a Prepared Query's evaluation +func EvalInput(input any) EvalOption { + return func(e *EvalContext) { + e.rawInput = &input + e.hasInput = true + } +} + +// EvalParsedInput configures the input for a Prepared Query's evaluation +func EvalParsedInput(input ast.Value) EvalOption { + return func(e *EvalContext) { + e.parsedInput = input + e.hasInput = true + } +} + +// EvalMetrics configures the metrics for a Prepared Query's evaluation +func EvalMetrics(metric metrics.Metrics) EvalOption { + return func(e *EvalContext) { + e.metrics = metric + } +} + +// EvalTransaction configures the Transaction for a Prepared Query's evaluation +func EvalTransaction(txn storage.Transaction) EvalOption { + return func(e *EvalContext) { + e.txn = txn + } +} + +// EvalInstrument enables or disables instrumenting for a Prepared Query's evaluation +func EvalInstrument(instrument bool) EvalOption { + return func(e *EvalContext) { + e.instrument = instrument + } +} + +// EvalTracer configures a tracer for a Prepared Query's evaluation +// Deprecated: Use EvalQueryTracer instead. +func EvalTracer(tracer topdown.Tracer) EvalOption { + return func(e *EvalContext) { + if tracer != nil { + e.queryTracers = append(e.queryTracers, topdown.WrapLegacyTracer(tracer)) + } + } +} + +// EvalQueryTracer configures a tracer for a Prepared Query's evaluation +func EvalQueryTracer(tracer topdown.QueryTracer) EvalOption { + return func(e *EvalContext) { + if tracer != nil { + e.queryTracers = append(e.queryTracers, tracer) + } + } +} + +// EvalPartialNamespace returns an argument that sets the namespace to use for +// partial evaluation results. The namespace must be a valid package path +// component. +func EvalPartialNamespace(ns string) EvalOption { + return func(e *EvalContext) { + e.partialNamespace = ns + } +} + +// EvalUnknowns returns an argument that sets the values to treat as +// unknown during partial evaluation. +func EvalUnknowns(unknowns []string) EvalOption { + return func(e *EvalContext) { + e.unknowns = unknowns + } +} + +// EvalDisableInlining returns an argument that adds a set of paths to exclude from +// partial evaluation inlining. +func EvalDisableInlining(paths []ast.Ref) EvalOption { + return func(e *EvalContext) { + e.disableInlining = paths + } +} + +// EvalParsedUnknowns returns an argument that sets the values to treat +// as unknown during partial evaluation. +func EvalParsedUnknowns(unknowns []*ast.Term) EvalOption { + return func(e *EvalContext) { + e.parsedUnknowns = unknowns + } +} + +// EvalRuleIndexing will disable indexing optimizations for the +// evaluation. This should only be used when tracing in debug mode. +func EvalRuleIndexing(enabled bool) EvalOption { + return func(e *EvalContext) { + e.indexing = enabled + } +} + +// EvalEarlyExit will disable 'early exit' optimizations for the +// evaluation. This should only be used when tracing in debug mode. +func EvalEarlyExit(enabled bool) EvalOption { + return func(e *EvalContext) { + e.earlyExit = enabled + } +} + +// EvalTime sets the wall clock time to use during policy evaluation. +// time.now_ns() calls will return this value. +func EvalTime(x time.Time) EvalOption { + return func(e *EvalContext) { + e.time = x + } +} + +// EvalSeed sets a reader that will seed randomization required by built-in functions. +// If a seed is not provided crypto/rand.Reader is used. +func EvalSeed(r io.Reader) EvalOption { + return func(e *EvalContext) { + e.seed = r + } +} + +// EvalInterQueryBuiltinCache sets the inter-query cache that built-in functions can utilize +// during evaluation. +func EvalInterQueryBuiltinCache(c cache.InterQueryCache) EvalOption { + return func(e *EvalContext) { + e.interQueryBuiltinCache = c + } +} + +// EvalInterQueryBuiltinValueCache sets the inter-query value cache that built-in functions can utilize +// during evaluation. +func EvalInterQueryBuiltinValueCache(c cache.InterQueryValueCache) EvalOption { + return func(e *EvalContext) { + e.interQueryBuiltinValueCache = c + } +} + +// EvalNDBuiltinCache sets the non-deterministic builtin cache that built-in functions can +// use during evaluation. +func EvalNDBuiltinCache(c builtins.NDBCache) EvalOption { + return func(e *EvalContext) { + e.ndBuiltinCache = c + } +} + +// EvalResolver sets a Resolver for a specified ref path for this evaluation. +func EvalResolver(ref ast.Ref, r resolver.Resolver) EvalOption { + return func(e *EvalContext) { + e.resolvers = append(e.resolvers, refResolver{ref, r}) + } +} + +// EvalHTTPRoundTripper allows customizing the http.RoundTripper for this evaluation. +func EvalHTTPRoundTripper(t topdown.CustomizeRoundTripper) EvalOption { + return func(e *EvalContext) { + e.httpRoundTripper = t + } +} + +// EvalSortSets causes the evaluator to sort sets before returning them as JSON arrays. +func EvalSortSets(yes bool) EvalOption { + return func(e *EvalContext) { + e.sortSets = yes + } +} + +// EvalCopyMaps causes the evaluator to copy `map[string]any`s before returning them. +func EvalCopyMaps(yes bool) EvalOption { + return func(e *EvalContext) { + e.copyMaps = yes + } +} + +// EvalPrintHook sets the object to use for handling print statement outputs. +func EvalPrintHook(ph print.Hook) EvalOption { + return func(e *EvalContext) { + e.printHook = ph + } +} + +// EvalVirtualCache sets the topdown.VirtualCache to use for evaluation. +// This is optional, and if not set, the default cache is used. +func EvalVirtualCache(vc topdown.VirtualCache) EvalOption { + return func(e *EvalContext) { + e.virtualCache = vc + } +} + +// EvalBaseCache sets the topdown.BaseCache to use for evaluation. +// This is optional, and if not set, the default cache is used. +func EvalBaseCache(bc topdown.BaseCache) EvalOption { + return func(e *EvalContext) { + e.baseCache = bc + } +} + +// EvalNondeterministicBuiltins causes non-deterministic builtins to be evalued +// during partial evaluation. This is needed to pull in external data, or validate +// a JWT, during PE, so that the result informs what queries are returned. +func EvalNondeterministicBuiltins(yes bool) EvalOption { + return func(e *EvalContext) { + e.nondeterministicBuiltins = yes + } +} + +// EvalExternalCancel sets an external topdown.Cancel for the interpreter to use +// for cancellation. This is useful for batch-evaluation of many rego queries. +func EvalExternalCancel(ec topdown.Cancel) EvalOption { + return func(e *EvalContext) { + e.externalCancel = ec + } +} + +func (pq preparedQuery) Modules() map[string]*ast.Module { + mods := make(map[string]*ast.Module) + + maps.Copy(mods, pq.r.parsedModules) + + for _, b := range pq.r.bundles { + for _, mod := range b.Modules { + mods[mod.Path] = mod.Parsed + } + } + + return mods +} + +// newEvalContext creates a new EvalContext overlaying any EvalOptions over top +// the Rego object on the preparedQuery. The returned function should be called +// once the evaluation is complete to close any transactions that might have +// been opened. +func (pq preparedQuery) newEvalContext(ctx context.Context, options []EvalOption) (*EvalContext, func(context.Context), error) { + ectx := &EvalContext{ + hasInput: false, + rawInput: nil, + parsedInput: nil, + metrics: nil, + txn: nil, + instrument: false, + instrumentation: nil, + partialNamespace: pq.r.partialNamespace, + queryTracers: nil, + unknowns: pq.r.unknowns, + parsedUnknowns: pq.r.parsedUnknowns, + nondeterministicBuiltins: pq.r.nondeterministicBuiltins, + compiledQuery: compiledQuery{}, + indexing: true, + earlyExit: true, + resolvers: pq.r.resolvers, + printHook: pq.r.printHook, + capabilities: pq.r.capabilities, + strictBuiltinErrors: pq.r.strictBuiltinErrors, + tracing: pq.r.distributedTracingOpts, + } + + for _, o := range options { + o(ectx) + } + + if ectx.metrics == nil { + ectx.metrics = metrics.New() + } + + if ectx.instrument { + ectx.instrumentation = topdown.NewInstrumentation(ectx.metrics) + } + + // Default to an empty "finish" function + finishFunc := func(context.Context) {} + + var err error + ectx.disableInlining, err = parseStringsToRefs(pq.r.disableInlining) + if err != nil { + return nil, finishFunc, err + } + + if ectx.txn == nil { + ectx.txn, err = pq.r.store.NewTransaction(ctx) + if err != nil { + return nil, finishFunc, err + } + finishFunc = func(ctx context.Context) { + pq.r.store.Abort(ctx, ectx.txn) + } + } + + // If we didn't get an input specified in the Eval options + // then fall back to the Rego object's input fields. + if !ectx.hasInput { + ectx.rawInput = pq.r.rawInput + ectx.parsedInput = pq.r.parsedInput + } + + if ectx.parsedInput == nil { + if ectx.rawInput == nil { + // Fall back to the original Rego objects input if none was specified + // Note that it could still be nil + ectx.rawInput = pq.r.rawInput + } + + if pq.r.targetPlugin(pq.r.target) == nil && // no plugin claims this target + pq.r.target != targetWasm { + ectx.parsedInput, err = pq.r.parseRawInput(ectx.rawInput, ectx.metrics) + if err != nil { + return nil, finishFunc, err + } + } + } + + return ectx, finishFunc, nil +} + +// PreparedEvalQuery holds the prepared Rego state that has been pre-processed +// for subsequent evaluations. +type PreparedEvalQuery struct { + preparedQuery +} + +// Eval evaluates this PartialResult's Rego object with additional eval options +// and returns a ResultSet. +// If options are provided they will override the original Rego options respective value. +// The original Rego object transaction will *not* be re-used. A new transaction will be opened +// if one is not provided with an EvalOption. +func (pq PreparedEvalQuery) Eval(ctx context.Context, options ...EvalOption) (ResultSet, error) { + ectx, finish, err := pq.newEvalContext(ctx, options) + if err != nil { + return nil, err + } + defer finish(ctx) + + ectx.compiledQuery = pq.r.compiledQueries[evalQueryType] + + return pq.r.eval(ctx, ectx) +} + +// PreparedPartialQuery holds the prepared Rego state that has been pre-processed +// for partial evaluations. +type PreparedPartialQuery struct { + preparedQuery +} + +// Partial runs partial evaluation on the prepared query and returns the result. +// The original Rego object transaction will *not* be re-used. A new transaction will be opened +// if one is not provided with an EvalOption. +func (pq PreparedPartialQuery) Partial(ctx context.Context, options ...EvalOption) (*PartialQueries, error) { + ectx, finish, err := pq.newEvalContext(ctx, options) + if err != nil { + return nil, err + } + defer finish(ctx) + + ectx.compiledQuery = pq.r.compiledQueries[partialQueryType] + + return pq.r.partial(ctx, ectx) +} + +// Errors represents a collection of errors returned when evaluating Rego. +type Errors []error + +func (errs Errors) Error() string { + if len(errs) == 0 { + return "no error" + } + if len(errs) == 1 { + return fmt.Sprintf("1 error occurred: %v", errs[0].Error()) + } + buf := []string{fmt.Sprintf("%v errors occurred", len(errs))} + for _, err := range errs { + buf = append(buf, err.Error()) + } + return strings.Join(buf, "\n") +} + +var errPartialEvaluationNotEffective = errors.New("partial evaluation not effective") + +// IsPartialEvaluationNotEffectiveErr returns true if err is an error returned by +// this package to indicate that partial evaluation was ineffective. +func IsPartialEvaluationNotEffectiveErr(err error) bool { + errs, ok := err.(Errors) + if !ok { + return false + } + return len(errs) == 1 && errs[0] == errPartialEvaluationNotEffective +} + +type compiledQuery struct { + query ast.Body + compiler ast.QueryCompiler +} + +type queryType int + +// Define a query type for each of the top level Rego +// API's that compile queries differently. +const ( + evalQueryType queryType = iota + partialResultQueryType + partialQueryType + compileQueryType +) + +type loadPaths struct { + paths []string + filter loader.Filter +} + +// Rego constructs a query and can be evaluated to obtain results. +type Rego struct { + query string + parsedQuery ast.Body + compiledQueries map[queryType]compiledQuery + pkg string + parsedPackage *ast.Package + imports []string + parsedImports []*ast.Import + rawInput *any + parsedInput ast.Value + unknowns []string + parsedUnknowns []*ast.Term + disableInlining []string + shallowInlining bool + nondeterministicBuiltins bool + skipPartialNamespace bool + partialNamespace string + modules []rawModule + parsedModules map[string]*ast.Module + compiler *ast.Compiler + store storage.Store + ownStore bool + ownStoreReadAst bool + txn storage.Transaction + metrics metrics.Metrics + queryTracers []topdown.QueryTracer + tracebuf *topdown.BufferTracer + trace bool + instrumentation *topdown.Instrumentation + instrument bool + capture map[*ast.Expr]ast.Var // map exprs to generated capture vars + termVarID int + dump io.Writer + runtime *ast.Term + time time.Time + seed io.Reader + capabilities *ast.Capabilities + builtinDecls map[string]*ast.Builtin + builtinFuncs map[string]*topdown.Builtin + unsafeBuiltins map[string]struct{} + loadPaths loadPaths + bundlePaths []string + bundles map[string]*bundle.Bundle + skipBundleVerification bool + bundleActivationPlugin string + enableBundleLazyLoadingMode bool + interQueryBuiltinCache cache.InterQueryCache + interQueryBuiltinValueCache cache.InterQueryValueCache + ndBuiltinCache builtins.NDBCache + strictBuiltinErrors bool + builtinErrorList *[]topdown.Error + resolvers []refResolver + schemaSet *ast.SchemaSet + target string // target type (wasm, rego, etc.) + opa opa.EvalEngine + generateJSON func(*ast.Term, *EvalContext) (any, error) + printHook print.Hook + enablePrintStatements bool + distributedTracingOpts tracing.Options + strict bool + targetPrepState TargetPluginEval + regoVersion ast.RegoVersion + compilerHook func(*ast.Compiler) + evalMode *ast.CompilerEvalMode +} + +func (r *Rego) RegoVersion() ast.RegoVersion { + return r.regoVersion +} + +// Function represents a built-in function that is callable in Rego. +type Function struct { + Name string + Description string + Decl *types.Function + Memoize bool + Nondeterministic bool +} + +// BuiltinContext contains additional attributes from the evaluator that +// built-in functions can use, e.g., the request context.Context, caches, etc. +type BuiltinContext = topdown.BuiltinContext + +type ( + // Builtin1 defines a built-in function that accepts 1 argument. + Builtin1 func(bctx BuiltinContext, op1 *ast.Term) (*ast.Term, error) + + // Builtin2 defines a built-in function that accepts 2 arguments. + Builtin2 func(bctx BuiltinContext, op1, op2 *ast.Term) (*ast.Term, error) + + // Builtin3 defines a built-in function that accepts 3 argument. + Builtin3 func(bctx BuiltinContext, op1, op2, op3 *ast.Term) (*ast.Term, error) + + // Builtin4 defines a built-in function that accepts 4 argument. + Builtin4 func(bctx BuiltinContext, op1, op2, op3, op4 *ast.Term) (*ast.Term, error) + + // BuiltinDyn defines a built-in function that accepts a list of arguments. + BuiltinDyn func(bctx BuiltinContext, terms []*ast.Term) (*ast.Term, error) +) + +// RegisterBuiltin1 adds a built-in function globally inside the OPA runtime. +func RegisterBuiltin1(decl *Function, impl Builtin1) { + ast.RegisterBuiltin(&ast.Builtin{ + Name: decl.Name, + Description: decl.Description, + Decl: decl.Decl, + Nondeterministic: decl.Nondeterministic, + }) + topdown.RegisterBuiltinFunc(decl.Name, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return impl(bctx, terms[0]) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// RegisterBuiltin2 adds a built-in function globally inside the OPA runtime. +func RegisterBuiltin2(decl *Function, impl Builtin2) { + ast.RegisterBuiltin(&ast.Builtin{ + Name: decl.Name, + Description: decl.Description, + Decl: decl.Decl, + Nondeterministic: decl.Nondeterministic, + }) + topdown.RegisterBuiltinFunc(decl.Name, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return impl(bctx, terms[0], terms[1]) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// RegisterBuiltin3 adds a built-in function globally inside the OPA runtime. +func RegisterBuiltin3(decl *Function, impl Builtin3) { + ast.RegisterBuiltin(&ast.Builtin{ + Name: decl.Name, + Description: decl.Description, + Decl: decl.Decl, + Nondeterministic: decl.Nondeterministic, + }) + topdown.RegisterBuiltinFunc(decl.Name, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return impl(bctx, terms[0], terms[1], terms[2]) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// RegisterBuiltin4 adds a built-in function globally inside the OPA runtime. +func RegisterBuiltin4(decl *Function, impl Builtin4) { + ast.RegisterBuiltin(&ast.Builtin{ + Name: decl.Name, + Description: decl.Description, + Decl: decl.Decl, + Nondeterministic: decl.Nondeterministic, + }) + topdown.RegisterBuiltinFunc(decl.Name, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return impl(bctx, terms[0], terms[1], terms[2], terms[3]) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// RegisterBuiltinDyn adds a built-in function globally inside the OPA runtime. +func RegisterBuiltinDyn(decl *Function, impl BuiltinDyn) { + ast.RegisterBuiltin(&ast.Builtin{ + Name: decl.Name, + Description: decl.Description, + Decl: decl.Decl, + Nondeterministic: decl.Nondeterministic, + }) + topdown.RegisterBuiltinFunc(decl.Name, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return impl(bctx, terms) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// Function1 returns an option that adds a built-in function to the Rego object. +func Function1(decl *Function, f Builtin1) func(*Rego) { + return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms[0]) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// Function2 returns an option that adds a built-in function to the Rego object. +func Function2(decl *Function, f Builtin2) func(*Rego) { + return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms[0], terms[1]) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// Function3 returns an option that adds a built-in function to the Rego object. +func Function3(decl *Function, f Builtin3) func(*Rego) { + return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms[0], terms[1], terms[2]) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// Function4 returns an option that adds a built-in function to the Rego object. +func Function4(decl *Function, f Builtin4) func(*Rego) { + return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms[0], terms[1], terms[2], terms[3]) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// FunctionDyn returns an option that adds a built-in function to the Rego object. +func FunctionDyn(decl *Function, f BuiltinDyn) func(*Rego) { + return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// FunctionDecl returns an option that adds a custom-built-in function +// __declaration__. NO implementation is provided. This is used for +// non-interpreter execution envs (e.g., Wasm). +func FunctionDecl(decl *Function) func(*Rego) { + return newDecl(decl) +} + +func newDecl(decl *Function) func(*Rego) { + return func(r *Rego) { + r.builtinDecls[decl.Name] = &ast.Builtin{ + Name: decl.Name, + Decl: decl.Decl, + } + } +} + +type memo struct { + term *ast.Term + err error +} + +type memokey string + +func memoize(decl *Function, bctx BuiltinContext, terms []*ast.Term, ifEmpty func() (*ast.Term, error)) (*ast.Term, error) { + + if !decl.Memoize { + return ifEmpty() + } + + // NOTE(tsandall): we assume memoization is applied to infrequent built-in + // calls that do things like fetch data from remote locations. As such, + // converting the terms to strings is acceptable for now. + var b strings.Builder + if _, err := b.WriteString(decl.Name); err != nil { + return nil, err + } + + // The term slice _may_ include an output term depending on how the caller + // referred to the built-in function. Only use the arguments as the cache + // key. Unification ensures we don't get false positive matches. + for i := range decl.Decl.Arity() { + if _, err := b.WriteString(terms[i].String()); err != nil { + return nil, err + } + } + + key := memokey(b.String()) + hit, ok := bctx.Cache.Get(key) + var m memo + if ok { + m = hit.(memo) + } else { + m.term, m.err = ifEmpty() + bctx.Cache.Put(key, m) + } + + return m.term, m.err +} + +// Dump returns an argument that sets the writer to dump debugging information to. +func Dump(w io.Writer) func(r *Rego) { + return func(r *Rego) { + r.dump = w + } +} + +// Query returns an argument that sets the Rego query. +func Query(q string) func(r *Rego) { + return func(r *Rego) { + r.query = q + } +} + +// ParsedQuery returns an argument that sets the Rego query. +func ParsedQuery(q ast.Body) func(r *Rego) { + return func(r *Rego) { + r.parsedQuery = q + } +} + +// Package returns an argument that sets the Rego package on the query's +// context. +func Package(p string) func(r *Rego) { + return func(r *Rego) { + r.pkg = p + } +} + +// ParsedPackage returns an argument that sets the Rego package on the query's +// context. +func ParsedPackage(pkg *ast.Package) func(r *Rego) { + return func(r *Rego) { + r.parsedPackage = pkg + } +} + +// Imports returns an argument that adds a Rego import to the query's context. +func Imports(p []string) func(r *Rego) { + return func(r *Rego) { + r.imports = append(r.imports, p...) + } +} + +// ParsedImports returns an argument that adds Rego imports to the query's +// context. +func ParsedImports(imp []*ast.Import) func(r *Rego) { + return func(r *Rego) { + r.parsedImports = append(r.parsedImports, imp...) + } +} + +// Input returns an argument that sets the Rego input document. Input should be +// a native Go value representing the input document. +func Input(x any) func(r *Rego) { + return func(r *Rego) { + r.rawInput = &x + } +} + +// ParsedInput returns an argument that sets the Rego input document. +func ParsedInput(x ast.Value) func(r *Rego) { + return func(r *Rego) { + r.parsedInput = x + } +} + +// Unknowns returns an argument that sets the values to treat as unknown during +// partial evaluation. +func Unknowns(unknowns []string) func(r *Rego) { + return func(r *Rego) { + r.unknowns = unknowns + } +} + +// ParsedUnknowns returns an argument that sets the values to treat as unknown +// during partial evaluation. +func ParsedUnknowns(unknowns []*ast.Term) func(r *Rego) { + return func(r *Rego) { + r.parsedUnknowns = unknowns + } +} + +// DisableInlining adds a set of paths to exclude from partial evaluation inlining. +func DisableInlining(paths []string) func(r *Rego) { + return func(r *Rego) { + r.disableInlining = paths + } +} + +// NondeterministicBuiltins causes non-deterministic builtins to be evalued during +// partial evaluation. This is needed to pull in external data, or validate a JWT, +// during PE, so that the result informs what queries are returned. +func NondeterministicBuiltins(yes bool) func(r *Rego) { + return func(r *Rego) { + r.nondeterministicBuiltins = yes + } +} + +// ShallowInlining prevents rules that depend on unknown values from being inlined. +// Rules that only depend on known values are inlined. +func ShallowInlining(yes bool) func(r *Rego) { + return func(r *Rego) { + r.shallowInlining = yes + } +} + +// SkipPartialNamespace disables namespacing of partial evalution results for support +// rules generated from policy. Synthetic support rules are still namespaced. +func SkipPartialNamespace(yes bool) func(r *Rego) { + return func(r *Rego) { + r.skipPartialNamespace = yes + } +} + +// PartialNamespace returns an argument that sets the namespace to use for +// partial evaluation results. The namespace must be a valid package path +// component. +func PartialNamespace(ns string) func(r *Rego) { + return func(r *Rego) { + r.partialNamespace = ns + } +} + +// Module returns an argument that adds a Rego module. +func Module(filename, input string) func(r *Rego) { + return func(r *Rego) { + r.modules = append(r.modules, rawModule{ + filename: filename, + module: input, + }) + } +} + +// ParsedModule returns an argument that adds a parsed Rego module. If a string +// module with the same filename name is added, it will override the parsed +// module. +func ParsedModule(module *ast.Module) func(*Rego) { + return func(r *Rego) { + var filename string + if module.Package.Location != nil { + filename = module.Package.Location.File + } else { + filename = fmt.Sprintf("module_%p.rego", module) + } + r.parsedModules[filename] = module + } +} + +// Load returns an argument that adds a filesystem path to load data +// and Rego modules from. Any file with a *.rego, *.yaml, or *.json +// extension will be loaded. The path can be either a directory or file, +// directories are loaded recursively. The optional ignore string patterns +// can be used to filter which files are used. +// The Load option can only be used once. +// Note: Loading files will require a write transaction on the store. +func Load(paths []string, filter loader.Filter) func(r *Rego) { + return func(r *Rego) { + r.loadPaths = loadPaths{paths, filter} + } +} + +// LoadBundle returns an argument that adds a filesystem path to load +// a bundle from. The path can be a compressed bundle file or a directory +// to be loaded as a bundle. +// Note: Loading bundles will require a write transaction on the store. +func LoadBundle(path string) func(r *Rego) { + return func(r *Rego) { + r.bundlePaths = append(r.bundlePaths, path) + } +} + +// ParsedBundle returns an argument that adds a bundle to be loaded. +func ParsedBundle(name string, b *bundle.Bundle) func(r *Rego) { + return func(r *Rego) { + r.bundles[name] = b + } +} + +// Compiler returns an argument that sets the Rego compiler. +func Compiler(c *ast.Compiler) func(r *Rego) { + return func(r *Rego) { + r.compiler = c + } +} + +// Store returns an argument that sets the policy engine's data storage layer. +// +// If using the Load, LoadBundle, or ParsedBundle options then a transaction +// must also be provided via the Transaction() option. After loading files +// or bundles the transaction should be aborted or committed. +func Store(s storage.Store) func(r *Rego) { + return func(r *Rego) { + r.store = s + } +} + +// StoreReadAST returns an argument that sets whether the store should eagerly convert data to AST values. +// +// Only applicable when no store has been set on the Rego object through the Store option. +func StoreReadAST(enabled bool) func(r *Rego) { + return func(r *Rego) { + r.ownStoreReadAst = enabled + } +} + +// Transaction returns an argument that sets the transaction to use for storage +// layer operations. +// +// Requires the store associated with the transaction to be provided via the +// Store() option. If using Load(), LoadBundle(), or ParsedBundle() options +// the transaction will likely require write params. +func Transaction(txn storage.Transaction) func(r *Rego) { + return func(r *Rego) { + r.txn = txn + } +} + +// Metrics returns an argument that sets the metrics collection. +func Metrics(m metrics.Metrics) func(r *Rego) { + return func(r *Rego) { + r.metrics = m + } +} + +// Instrument returns an argument that enables instrumentation for diagnosing +// performance issues. +func Instrument(yes bool) func(r *Rego) { + return func(r *Rego) { + r.instrument = yes + } +} + +// Trace returns an argument that enables tracing on r. +func Trace(yes bool) func(r *Rego) { + return func(r *Rego) { + r.trace = yes + } +} + +// Tracer returns an argument that adds a query tracer to r. +// Deprecated: Use QueryTracer instead. +func Tracer(t topdown.Tracer) func(r *Rego) { + return func(r *Rego) { + if t != nil { + r.queryTracers = append(r.queryTracers, topdown.WrapLegacyTracer(t)) + } + } +} + +// QueryTracer returns an argument that adds a query tracer to r. +func QueryTracer(t topdown.QueryTracer) func(r *Rego) { + return func(r *Rego) { + if t != nil { + r.queryTracers = append(r.queryTracers, t) + } + } +} + +// Runtime returns an argument that sets the runtime data to provide to the +// evaluation engine. +func Runtime(term *ast.Term) func(r *Rego) { + return func(r *Rego) { + r.runtime = term + } +} + +// Time sets the wall clock time to use during policy evaluation. Prepared queries +// do not inherit this parameter. Use EvalTime to set the wall clock time when +// executing a prepared query. +func Time(x time.Time) func(r *Rego) { + return func(r *Rego) { + r.time = x + } +} + +// Seed sets a reader that will seed randomization required by built-in functions. +// If a seed is not provided crypto/rand.Reader is used. +func Seed(r io.Reader) func(*Rego) { + return func(e *Rego) { + e.seed = r + } +} + +// PrintTrace is a helper function to write a human-readable version of the +// trace to the writer w. +func PrintTrace(w io.Writer, r *Rego) { + if r == nil || r.tracebuf == nil { + return + } + topdown.PrettyTrace(w, *r.tracebuf) +} + +// PrintTraceWithLocation is a helper function to write a human-readable version of the +// trace to the writer w. +func PrintTraceWithLocation(w io.Writer, r *Rego) { + if r == nil || r.tracebuf == nil { + return + } + topdown.PrettyTraceWithLocation(w, *r.tracebuf) +} + +// UnsafeBuiltins sets the built-in functions to treat as unsafe and not allow. +// This option is ignored for module compilation if the caller supplies the +// compiler. This option is always honored for query compilation. Provide an +// empty (non-nil) map to disable checks on queries. +func UnsafeBuiltins(unsafeBuiltins map[string]struct{}) func(r *Rego) { + return func(r *Rego) { + r.unsafeBuiltins = unsafeBuiltins + } +} + +// SkipBundleVerification skips verification of a signed bundle. +func SkipBundleVerification(yes bool) func(r *Rego) { + return func(r *Rego) { + r.skipBundleVerification = yes + } +} + +// BundleActivatorPlugin sets the name of the activator plugin used to load bundles into the store. +func BundleActivatorPlugin(name string) func(r *Rego) { + return func(r *Rego) { + r.bundleActivationPlugin = name + } +} + +// BundleLazyLoadingMode sets the bundle loading mode. If true, bundles will be +// read in lazy mode. In this mode, data files in the bundle will not be +// deserialized and the check to validate that the bundle data does not contain +// paths outside the bundle's roots will not be performed while reading the bundle. +func BundleLazyLoadingMode(yes bool) func(r *Rego) { + return func(r *Rego) { + r.enableBundleLazyLoadingMode = yes + } +} + +// InterQueryBuiltinCache sets the inter-query cache that built-in functions can utilize +// during evaluation. +func InterQueryBuiltinCache(c cache.InterQueryCache) func(r *Rego) { + return func(r *Rego) { + r.interQueryBuiltinCache = c + } +} + +// InterQueryBuiltinValueCache sets the inter-query value cache that built-in functions can utilize +// during evaluation. +func InterQueryBuiltinValueCache(c cache.InterQueryValueCache) func(r *Rego) { + return func(r *Rego) { + r.interQueryBuiltinValueCache = c + } +} + +// NDBuiltinCache sets the non-deterministic builtins cache. +func NDBuiltinCache(c builtins.NDBCache) func(r *Rego) { + return func(r *Rego) { + r.ndBuiltinCache = c + } +} + +// StrictBuiltinErrors tells the evaluator to treat all built-in function errors as fatal errors. +func StrictBuiltinErrors(yes bool) func(r *Rego) { + return func(r *Rego) { + r.strictBuiltinErrors = yes + } +} + +// BuiltinErrorList supplies an error slice to store built-in function errors. +func BuiltinErrorList(list *[]topdown.Error) func(r *Rego) { + return func(r *Rego) { + r.builtinErrorList = list + } +} + +// Resolver sets a Resolver for a specified ref path. +func Resolver(ref ast.Ref, r resolver.Resolver) func(r *Rego) { + return func(rego *Rego) { + rego.resolvers = append(rego.resolvers, refResolver{ref, r}) + } +} + +// Schemas sets the schemaSet +func Schemas(x *ast.SchemaSet) func(r *Rego) { + return func(r *Rego) { + r.schemaSet = x + } +} + +// Capabilities configures the underlying compiler's capabilities. +// This option is ignored for module compilation if the caller supplies the +// compiler. +func Capabilities(c *ast.Capabilities) func(r *Rego) { + return func(r *Rego) { + r.capabilities = c + } +} + +// Target sets the runtime to exercise. +func Target(t string) func(r *Rego) { + return func(r *Rego) { + r.target = t + } +} + +// GenerateJSON sets the AST to JSON converter for the results. +func GenerateJSON(f func(*ast.Term, *EvalContext) (any, error)) func(r *Rego) { + return func(r *Rego) { + r.generateJSON = f + } +} + +// PrintHook sets the object to use for handling print statement outputs. +func PrintHook(h print.Hook) func(r *Rego) { + return func(r *Rego) { + r.printHook = h + } +} + +// DistributedTracingOpts sets the options to be used by distributed tracing. +func DistributedTracingOpts(tr tracing.Options) func(r *Rego) { + return func(r *Rego) { + r.distributedTracingOpts = tr + } +} + +// EnablePrintStatements enables print() calls. If this option is not provided, +// print() calls will be erased from the policy. This option only applies to +// queries and policies that passed as raw strings, i.e., this function will not +// have any affect if the caller supplies the ast.Compiler instance. +func EnablePrintStatements(yes bool) func(r *Rego) { + return func(r *Rego) { + r.enablePrintStatements = yes + } +} + +// Strict enables or disables strict-mode in the compiler +func Strict(yes bool) func(r *Rego) { + return func(r *Rego) { + r.strict = yes + } +} + +func SetRegoVersion(version ast.RegoVersion) func(r *Rego) { + return func(r *Rego) { + r.regoVersion = version + } +} + +// CompilerHook sets a hook function that will be called after the compiler is initialized. +// This is only called if the compiler has not been provided already. +func CompilerHook(hook func(*ast.Compiler)) func(r *Rego) { + return func(r *Rego) { + r.compilerHook = hook + } +} + +// EvalMode lets you override the evaluation mode. +func EvalMode(mode ast.CompilerEvalMode) func(r *Rego) { + return func(r *Rego) { + r.evalMode = &mode + } +} + +// New returns a new Rego object. +func New(options ...func(r *Rego)) *Rego { + + r := &Rego{ + parsedModules: map[string]*ast.Module{}, + capture: map[*ast.Expr]ast.Var{}, + compiledQueries: map[queryType]compiledQuery{}, + builtinDecls: map[string]*ast.Builtin{}, + builtinFuncs: map[string]*topdown.Builtin{}, + bundles: map[string]*bundle.Bundle{}, + } + + for _, option := range options { + option(r) + } + + callHook := r.compiler == nil // call hook only if we created the compiler here + + if r.compiler == nil { + r.compiler = ast.NewCompiler(). + WithUnsafeBuiltins(r.unsafeBuiltins). + WithBuiltins(r.builtinDecls). + WithDebug(r.dump). + WithSchemas(r.schemaSet). + WithCapabilities(r.capabilities). + WithEnablePrintStatements(r.enablePrintStatements). + WithStrict(r.strict). + WithUseTypeCheckAnnotations(true) + + // topdown could be target "" or "rego", but both could be overridden by + // a target plugin (checked below) + if r.target == targetWasm { + r.compiler = r.compiler.WithEvalMode(ast.EvalModeIR) + } + + if r.regoVersion != ast.RegoUndefined { + r.compiler = r.compiler.WithDefaultRegoVersion(r.regoVersion) + } + } + + if r.store == nil { + if bundle.HasExtension() { + r.store = bundle.BundleExtStore() + } else { + r.store = inmem.NewWithOpts(inmem.OptReturnASTValuesOnRead(r.ownStoreReadAst)) + } + r.ownStore = true + } else { + r.ownStore = false + } + + if r.metrics == nil { + r.metrics = metrics.New() + } + + if r.instrument { + r.instrumentation = topdown.NewInstrumentation(r.metrics) + r.compiler.WithMetrics(r.metrics) + } + + if r.trace { + r.tracebuf = topdown.NewBufferTracer() + r.queryTracers = append(r.queryTracers, r.tracebuf) + } + + if r.partialNamespace == "" { + r.partialNamespace = defaultPartialNamespace + } + + if r.generateJSON == nil { + r.generateJSON = generateJSON + } + + if r.evalMode != nil { + r.compiler = r.compiler.WithEvalMode(*r.evalMode) + } + + if r.compilerHook != nil && callHook { + r.compilerHook(r.compiler) + } + + return r +} + +// Eval evaluates this Rego object and returns a ResultSet. +func (r *Rego) Eval(ctx context.Context) (ResultSet, error) { + var err error + var txnClose transactionCloser + r.txn, txnClose, err = r.getTxn(ctx) + if err != nil { + return nil, err + } + + pq, err := r.PrepareForEval(ctx) + if err != nil { + _ = txnClose(ctx, err) // Ignore error + return nil, err + } + + evalArgs := []EvalOption{ + EvalTransaction(r.txn), + EvalMetrics(r.metrics), + EvalInstrument(r.instrument), + EvalTime(r.time), + EvalInterQueryBuiltinCache(r.interQueryBuiltinCache), + EvalInterQueryBuiltinValueCache(r.interQueryBuiltinValueCache), + EvalSeed(r.seed), + } + + if r.ndBuiltinCache != nil { + evalArgs = append(evalArgs, EvalNDBuiltinCache(r.ndBuiltinCache)) + } + + for _, qt := range r.queryTracers { + evalArgs = append(evalArgs, EvalQueryTracer(qt)) + } + + for i := range r.resolvers { + evalArgs = append(evalArgs, EvalResolver(r.resolvers[i].ref, r.resolvers[i].r)) + } + + rs, err := pq.Eval(ctx, evalArgs...) + txnErr := txnClose(ctx, err) // Always call closer + if err == nil { + err = txnErr + } + return rs, err +} + +// PartialEval has been deprecated and renamed to PartialResult. +func (r *Rego) PartialEval(ctx context.Context) (PartialResult, error) { + return r.PartialResult(ctx) +} + +// PartialResult partially evaluates this Rego object and returns a PartialResult. +func (r *Rego) PartialResult(ctx context.Context) (PartialResult, error) { + var err error + var txnClose transactionCloser + r.txn, txnClose, err = r.getTxn(ctx) + if err != nil { + return PartialResult{}, err + } + + pq, err := r.PrepareForEval(ctx, WithPartialEval()) + txnErr := txnClose(ctx, err) // Always call closer + if err != nil { + return PartialResult{}, err + } + if txnErr != nil { + return PartialResult{}, txnErr + } + + pr := PartialResult{ + compiler: pq.r.compiler, + store: pq.r.store, + body: pq.r.parsedQuery, + builtinDecls: pq.r.builtinDecls, + builtinFuncs: pq.r.builtinFuncs, + } + + return pr, nil +} + +// Partial runs partial evaluation on r and returns the result. +func (r *Rego) Partial(ctx context.Context) (*PartialQueries, error) { + var err error + var txnClose transactionCloser + r.txn, txnClose, err = r.getTxn(ctx) + if err != nil { + return nil, err + } + + pq, err := r.PrepareForPartial(ctx) + if err != nil { + _ = txnClose(ctx, err) // Ignore error + return nil, err + } + + evalArgs := []EvalOption{ + EvalTransaction(r.txn), + EvalMetrics(r.metrics), + EvalInstrument(r.instrument), + EvalInterQueryBuiltinCache(r.interQueryBuiltinCache), + EvalInterQueryBuiltinValueCache(r.interQueryBuiltinValueCache), + } + + if r.ndBuiltinCache != nil { + evalArgs = append(evalArgs, EvalNDBuiltinCache(r.ndBuiltinCache)) + } + + for _, t := range r.queryTracers { + evalArgs = append(evalArgs, EvalQueryTracer(t)) + } + + for i := range r.resolvers { + evalArgs = append(evalArgs, EvalResolver(r.resolvers[i].ref, r.resolvers[i].r)) + } + + pqs, err := pq.Partial(ctx, evalArgs...) + txnErr := txnClose(ctx, err) // Always call closer + if err == nil { + err = txnErr + } + return pqs, err +} + +// CompileOption defines a function to set options on Compile calls. +type CompileOption func(*CompileContext) + +// CompileContext contains options for Compile calls. +type CompileContext struct { + partial bool +} + +// CompilePartial defines an option to control whether partial evaluation is run +// before the query is planned and compiled. +func CompilePartial(yes bool) CompileOption { + return func(cfg *CompileContext) { + cfg.partial = yes + } +} + +// Compile returns a compiled policy query. +func (r *Rego) Compile(ctx context.Context, opts ...CompileOption) (*CompileResult, error) { + + var cfg CompileContext + + for _, opt := range opts { + opt(&cfg) + } + + var queries []ast.Body + modules := make([]*ast.Module, 0, len(r.compiler.Modules)) + + if cfg.partial { + + pq, err := r.Partial(ctx) + if err != nil { + return nil, err + } + if r.dump != nil { + if len(pq.Queries) != 0 { + msg := fmt.Sprintf("QUERIES (%d total):", len(pq.Queries)) + fmt.Fprintln(r.dump, msg) + fmt.Fprintln(r.dump, strings.Repeat("-", len(msg))) + for i := range pq.Queries { + fmt.Println(pq.Queries[i]) + } + fmt.Fprintln(r.dump) + } + if len(pq.Support) != 0 { + msg := fmt.Sprintf("SUPPORT (%d total):", len(pq.Support)) + fmt.Fprintln(r.dump, msg) + fmt.Fprintln(r.dump, strings.Repeat("-", len(msg))) + for i := range pq.Support { + fmt.Println(pq.Support[i]) + } + fmt.Fprintln(r.dump) + } + } + + queries = pq.Queries + modules = pq.Support + + for _, module := range r.compiler.Modules { + modules = append(modules, module) + } + } else { + var err error + // If creating a new transaction it should be closed before calling the + // planner to avoid holding open the transaction longer than needed. + // + // TODO(tsandall): in future, planner could make use of store, in which + // case this will need to change. + var txnClose transactionCloser + r.txn, txnClose, err = r.getTxn(ctx) + if err != nil { + return nil, err + } + + err = r.prepare(ctx, compileQueryType, nil) + txnErr := txnClose(ctx, err) // Always call closer + if err != nil { + return nil, err + } + if txnErr != nil { + return nil, err + } + + for _, module := range r.compiler.Modules { + modules = append(modules, module) + } + + queries = []ast.Body{r.compiledQueries[compileQueryType].query} + } + + if tgt := r.targetPlugin(r.target); tgt != nil { + return nil, errors.New("unsupported for rego target plugins") + } + + return r.compileWasm(modules, queries, compileQueryType) // TODO(sr) control flow is funky here +} + +func (r *Rego) compileWasm(_ []*ast.Module, queries []ast.Body, qType queryType) (*CompileResult, error) { + return nil, errors.New("wasm target not supported") +} + +// PrepareOption defines a function to set an option to control +// the behavior of the Prepare call. +type PrepareOption func(*PrepareConfig) + +// PrepareConfig holds settings to control the behavior of the +// Prepare call. +type PrepareConfig struct { + doPartialEval bool + disableInlining *[]string + builtinFuncs map[string]*topdown.Builtin +} + +// WithPartialEval configures an option for PrepareForEval +// which will have it perform partial evaluation while preparing +// the query (similar to rego.Rego#PartialResult) +func WithPartialEval() PrepareOption { + return func(p *PrepareConfig) { + p.doPartialEval = true + } +} + +// WithNoInline adds a set of paths to exclude from partial evaluation inlining. +func WithNoInline(paths []string) PrepareOption { + return func(p *PrepareConfig) { + p.disableInlining = &paths + } +} + +// WithBuiltinFuncs carries the rego.Function{1,2,3} per-query function definitions +// to the target plugins. +func WithBuiltinFuncs(bis map[string]*topdown.Builtin) PrepareOption { + return func(p *PrepareConfig) { + if p.builtinFuncs == nil { + p.builtinFuncs = maps.Clone(bis) + } else { + maps.Copy(p.builtinFuncs, bis) + } + } +} + +// BuiltinFuncs allows retrieving the builtin funcs set via PrepareOption +// WithBuiltinFuncs. +func (p *PrepareConfig) BuiltinFuncs() map[string]*topdown.Builtin { + return p.builtinFuncs +} + +// PrepareForEval will parse inputs, modules, and query arguments in preparation +// of evaluating them. +func (r *Rego) PrepareForEval(ctx context.Context, opts ...PrepareOption) (PreparedEvalQuery, error) { + if !r.hasQuery() { + return PreparedEvalQuery{}, errors.New("cannot evaluate empty query") + } + + pCfg := &PrepareConfig{} + for _, o := range opts { + o(pCfg) + } + + var err error + var txnClose transactionCloser + r.txn, txnClose, err = r.getTxn(ctx) + if err != nil { + return PreparedEvalQuery{}, err + } + + // If the caller wanted to do partial evaluation as part of preparation + // do it now and use the new Rego object. + if pCfg.doPartialEval { + + pr, err := r.partialResult(ctx, pCfg) + if err != nil { + _ = txnClose(ctx, err) // Ignore error + return PreparedEvalQuery{}, err + } + + // Prepare the new query using the result of partial evaluation + pq, err := pr.Rego(Transaction(r.txn)).PrepareForEval(ctx) + txnErr := txnClose(ctx, err) + if err != nil { + return pq, err + } + return pq, txnErr + } + + err = r.prepare(ctx, evalQueryType, []extraStage{ + { + after: "ResolveRefs", + stage: ast.QueryCompilerStageDefinition{ + Name: "RewriteToCaptureValue", + MetricName: "query_compile_stage_rewrite_to_capture_value", + Stage: r.rewriteQueryToCaptureValue, + }, + }, + }) + if err != nil { + _ = txnClose(ctx, err) // Ignore error + return PreparedEvalQuery{}, err + } + + switch r.target { + case targetWasm: // TODO(sr): make wasm a target plugin, too + + if r.hasWasmModule() { + _ = txnClose(ctx, err) // Ignore error + return PreparedEvalQuery{}, errors.New("wasm target not supported") + } + + var modules []*ast.Module + for _, module := range r.compiler.Modules { + modules = append(modules, module) + } + + queries := []ast.Body{r.compiledQueries[evalQueryType].query} + + e, err := opa.LookupEngine(targetWasm) + if err != nil { + return PreparedEvalQuery{}, err + } + + // nolint: staticcheck // SA4006 false positive + cr, err := r.compileWasm(modules, queries, evalQueryType) + if err != nil { + _ = txnClose(ctx, err) // Ignore error + return PreparedEvalQuery{}, err + } + + // nolint: staticcheck // SA4006 false positive + data, err := r.store.Read(ctx, r.txn, storage.Path{}) + if err != nil { + _ = txnClose(ctx, err) // Ignore error + return PreparedEvalQuery{}, err + } + + o, err := e.New().WithPolicyBytes(cr.Bytes).WithDataJSON(data).Init() + if err != nil { + _ = txnClose(ctx, err) // Ignore error + return PreparedEvalQuery{}, err + } + r.opa = o + + case targetRego: // do nothing, don't lookup default plugin + default: // either a specific plugin target, or one that is default + if tgt := r.targetPlugin(r.target); tgt != nil { + queries := []ast.Body{r.compiledQueries[evalQueryType].query} + pol, err := r.planQuery(queries, evalQueryType) + if err != nil { + return PreparedEvalQuery{}, err + } + // always add the builtins provided via rego.FunctionN options + opts = append(opts, WithBuiltinFuncs(r.builtinFuncs)) + r.targetPrepState, err = tgt.PrepareForEval(ctx, pol, opts...) + if err != nil { + return PreparedEvalQuery{}, err + } + } + } + + txnErr := txnClose(ctx, err) // Always call closer + if txnErr != nil { + return PreparedEvalQuery{}, txnErr + } + + return PreparedEvalQuery{preparedQuery{r, pCfg}}, err +} + +// PrepareForPartial will parse inputs, modules, and query arguments in preparation +// of partially evaluating them. +func (r *Rego) PrepareForPartial(ctx context.Context, opts ...PrepareOption) (PreparedPartialQuery, error) { + if !r.hasQuery() { + return PreparedPartialQuery{}, errors.New("cannot evaluate empty query") + } + + pCfg := &PrepareConfig{} + for _, o := range opts { + o(pCfg) + } + + var err error + var txnClose transactionCloser + r.txn, txnClose, err = r.getTxn(ctx) + if err != nil { + return PreparedPartialQuery{}, err + } + + err = r.prepare(ctx, partialQueryType, []extraStage{ + { + after: "CheckSafety", + stage: ast.QueryCompilerStageDefinition{ + Name: "RewriteEquals", + MetricName: "query_compile_stage_rewrite_equals", + Stage: r.rewriteEqualsForPartialQueryCompile, + }, + }, + }) + txnErr := txnClose(ctx, err) // Always call closer + if err != nil { + return PreparedPartialQuery{}, err + } + if txnErr != nil { + return PreparedPartialQuery{}, txnErr + } + + return PreparedPartialQuery{preparedQuery{r, pCfg}}, err +} + +func (r *Rego) prepare(ctx context.Context, qType queryType, extras []extraStage) error { + var err error + + r.parsedInput, err = r.parseInput() + if err != nil { + return err + } + + err = r.loadFiles(ctx, r.txn, r.metrics) + if err != nil { + return err + } + + err = r.loadBundles(ctx, r.txn, r.metrics) + if err != nil { + return err + } + + err = r.parseModules(ctx, r.txn, r.metrics) + if err != nil { + return err + } + + // Compile the modules *before* the query, else functions + // defined in the module won't be found... + err = r.compileModules(ctx, r.txn, r.metrics) + if err != nil { + return err + } + + imports, err := r.prepareImports() + if err != nil { + return err + } + + queryImports := []*ast.Import{} + for _, imp := range imports { + path := imp.Path.Value.(ast.Ref) + if path.HasPrefix([]*ast.Term{ast.FutureRootDocument}) || path.HasPrefix([]*ast.Term{ast.RegoRootDocument}) { + queryImports = append(queryImports, imp) + } + } + + r.parsedQuery, err = r.parseQuery(queryImports, r.metrics) + if err != nil { + return err + } + + err = r.compileAndCacheQuery(qType, r.parsedQuery, imports, r.metrics, extras) + if err != nil { + return err + } + + return nil +} + +func (r *Rego) parseModules(ctx context.Context, txn storage.Transaction, m metrics.Metrics) error { + if len(r.modules) == 0 { + return nil + } + + ids, err := r.store.ListPolicies(ctx, txn) + if err != nil { + return err + } + + m.Timer(metrics.RegoModuleParse).Start() + defer m.Timer(metrics.RegoModuleParse).Stop() + var errs Errors + + popts := ast.ParserOptions{ + RegoVersion: r.regoVersion, + Capabilities: r.capabilities, + } + + // Parse any modules that are saved to the store, but only if + // another compile step is going to occur (ie. we have parsed modules + // that need to be compiled). + for _, id := range ids { + // if it is already on the compiler we're using + // then don't bother to re-parse it from source + if _, haveMod := r.compiler.Modules[id]; haveMod { + continue + } + + bs, err := r.store.GetPolicy(ctx, txn, id) + if err != nil { + return err + } + + parsed, err := ast.ParseModuleWithOpts(id, string(bs), popts) + if err != nil { + errs = append(errs, err) + } + + r.parsedModules[id] = parsed + } + + // Parse any passed in as arguments to the Rego object + for _, module := range r.modules { + p, err := module.ParseWithOpts(popts) + if err != nil { + switch errorWithType := err.(type) { + case ast.Errors: + for _, e := range errorWithType { + errs = append(errs, e) + } + default: + errs = append(errs, errorWithType) + } + } + r.parsedModules[module.filename] = p + } + + if len(errs) > 0 { + return errs + } + + return nil +} + +func (r *Rego) loadFiles(ctx context.Context, txn storage.Transaction, m metrics.Metrics) error { + if len(r.loadPaths.paths) == 0 { + return nil + } + + m.Timer(metrics.RegoLoadFiles).Start() + defer m.Timer(metrics.RegoLoadFiles).Stop() + + result, err := loader.NewFileLoader(). + WithMetrics(m). + WithProcessAnnotation(true). + WithBundleLazyLoadingMode(bundle.HasExtension()). + WithRegoVersion(r.regoVersion). + WithCapabilities(r.capabilities). + Filtered(r.loadPaths.paths, r.loadPaths.filter) + if err != nil { + return err + } + for name, mod := range result.Modules { + r.parsedModules[name] = mod.Parsed + } + + if len(result.Documents) > 0 { + err = r.store.Write(ctx, txn, storage.AddOp, storage.Path{}, result.Documents) + if err != nil { + return err + } + } + return nil +} + +func (r *Rego) loadBundles(_ context.Context, _ storage.Transaction, m metrics.Metrics) error { + if len(r.bundlePaths) == 0 { + return nil + } + + m.Timer(metrics.RegoLoadBundles).Start() + defer m.Timer(metrics.RegoLoadBundles).Stop() + + for _, path := range r.bundlePaths { + bndl, err := loader.NewFileLoader(). + WithMetrics(m). + WithProcessAnnotation(true). + WithBundleLazyLoadingMode(bundle.HasExtension()). + WithSkipBundleVerification(r.skipBundleVerification). + WithRegoVersion(r.regoVersion). + WithCapabilities(r.capabilities). + AsBundle(path) + if err != nil { + return fmt.Errorf("loading error: %s", err) + } + r.bundles[path] = bndl + } + return nil +} + +func (r *Rego) parseInput() (ast.Value, error) { + if r.parsedInput != nil { + return r.parsedInput, nil + } + return r.parseRawInput(r.rawInput, r.metrics) +} + +func (*Rego) parseRawInput(rawInput *any, m metrics.Metrics) (ast.Value, error) { + var input ast.Value + + if rawInput == nil { + return input, nil + } + + m.Timer(metrics.RegoInputParse).Start() + defer m.Timer(metrics.RegoInputParse).Stop() + + rawPtr := util.Reference(rawInput) + + // roundtrip through json: this turns slices (e.g. []string, []bool) into + // []any, the only array type ast.InterfaceToValue can work with + if err := util.RoundTrip(rawPtr); err != nil { + return nil, err + } + + return ast.InterfaceToValue(*rawPtr) +} + +func (r *Rego) parseQuery(queryImports []*ast.Import, m metrics.Metrics) (ast.Body, error) { + if r.parsedQuery != nil { + return r.parsedQuery, nil + } + + m.Timer(metrics.RegoQueryParse).Start() + defer m.Timer(metrics.RegoQueryParse).Stop() + + popts, err := future.ParserOptionsFromFutureImports(queryImports) + if err != nil { + return nil, err + } + popts.RegoVersion = r.regoVersion + popts, err = parserOptionsFromRegoVersionImport(queryImports, popts) + if err != nil { + return nil, err + } + popts.SkipRules = true + popts.Capabilities = r.capabilities + + return ast.ParseBodyWithOpts(r.query, popts) +} + +func parserOptionsFromRegoVersionImport(imports []*ast.Import, popts ast.ParserOptions) (ast.ParserOptions, error) { + for _, imp := range imports { + path := imp.Path.Value.(ast.Ref) + if ast.Compare(path, ast.RegoV1CompatibleRef) == 0 { + popts.RegoVersion = ast.RegoV1 + return popts, nil + } + } + return popts, nil +} + +func (r *Rego) compileModules(ctx context.Context, txn storage.Transaction, m metrics.Metrics) error { + + // Only compile again if there are new modules. + if len(r.bundles) > 0 || len(r.parsedModules) > 0 { + + // The bundle.Activate call will activate any bundles passed in + // (ie compile + handle data store changes), and include any of + // the additional modules passed in. If no bundles are provided + // it will only compile the passed in modules. + // Use this as the single-point of compiling everything only a + // single time. + opts := &bundle.ActivateOpts{ + Ctx: ctx, + Store: r.store, + Txn: txn, + Compiler: r.compilerForTxn(ctx, r.store, txn), + Metrics: m, + Bundles: r.bundles, + ExtraModules: r.parsedModules, + ParserOptions: ast.ParserOptions{RegoVersion: r.regoVersion}, + } + err := bundle.Activate(opts) + if err != nil { + return err + } + } + + // Ensure all configured resolvers from the store are loaded. Skip if any were explicitly provided. + if len(r.resolvers) == 0 { + resolvers, err := bundleUtils.LoadWasmResolversFromStore(ctx, r.store, txn, r.bundles) + if err != nil { + return err + } + + for _, rslvr := range resolvers { + for _, ep := range rslvr.Entrypoints() { + r.resolvers = append(r.resolvers, refResolver{ep, rslvr}) + } + } + } + return nil +} + +func (r *Rego) compileAndCacheQuery(qType queryType, query ast.Body, imports []*ast.Import, m metrics.Metrics, extras []extraStage) error { + m.Timer(metrics.RegoQueryCompile).Start() + defer m.Timer(metrics.RegoQueryCompile).Stop() + + cachedQuery, ok := r.compiledQueries[qType] + if ok && cachedQuery.query != nil && cachedQuery.compiler != nil { + return nil + } + + qc, compiled, err := r.compileQuery(query, imports, m, extras) + if err != nil { + return err + } + + // cache the query for future use + r.compiledQueries[qType] = compiledQuery{ + query: compiled, + compiler: qc, + } + return nil +} + +func (r *Rego) prepareImports() ([]*ast.Import, error) { + imports := r.parsedImports + + if len(r.imports) > 0 { + s := make([]string, len(r.imports)) + for i := range r.imports { + s[i] = fmt.Sprintf("import %v", r.imports[i]) + } + parsed, err := ast.ParseImports(strings.Join(s, "\n")) + if err != nil { + return nil, err + } + imports = append(imports, parsed...) + } + return imports, nil +} + +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 != "" { + var err error + pkg, err = ast.ParsePackage(fmt.Sprintf("package %v", r.pkg)) + if err != nil { + return nil, nil, err + } + } else { + pkg = r.parsedPackage + } + + qctx := ast.NewQueryContext(). + WithPackage(pkg). + WithImports(imports) + + qc := r.compiler.QueryCompiler(). + WithContext(qctx). + WithUnsafeBuiltins(r.unsafeBuiltins). + WithEnablePrintStatements(r.enablePrintStatements). + WithStrict(false) + + for _, extra := range extras { + qc = qc.WithStageAfter(extra.after, extra.stage) + } + + compiled, err := qc.Compile(query) + + return qc, compiled, err + +} + +func (r *Rego) eval(ctx context.Context, ectx *EvalContext) (ResultSet, error) { + switch { + case r.targetPrepState != nil: // target plugin flow + var val ast.Value + if r.runtime != nil { + val = r.runtime.Value + } + s, err := r.targetPrepState.Eval(ctx, ectx, val) + if err != nil { + return nil, err + } + return r.valueToQueryResult(s, ectx) + case r.target == targetWasm: + return r.evalWasm(ctx, ectx) + case r.target == targetRego: // continue + } + + q := topdown.NewQuery(ectx.compiledQuery.query). + WithQueryCompiler(ectx.compiledQuery.compiler). + WithCompiler(r.compiler). + WithStore(r.store). + WithTransaction(ectx.txn). + WithBuiltins(r.builtinFuncs). + WithMetrics(ectx.metrics). + WithInstrumentation(ectx.instrumentation). + WithRuntime(r.runtime). + WithIndexing(ectx.indexing). + WithEarlyExit(ectx.earlyExit). + WithInterQueryBuiltinCache(ectx.interQueryBuiltinCache). + WithInterQueryBuiltinValueCache(ectx.interQueryBuiltinValueCache). + WithStrictBuiltinErrors(r.strictBuiltinErrors). + WithBuiltinErrorList(r.builtinErrorList). + WithSeed(ectx.seed). + WithPrintHook(ectx.printHook). + WithDistributedTracingOpts(r.distributedTracingOpts). + WithVirtualCache(ectx.virtualCache). + WithBaseCache(ectx.baseCache) + + if !ectx.time.IsZero() { + q = q.WithTime(ectx.time) + } + + if ectx.ndBuiltinCache != nil { + q = q.WithNDBuiltinCache(ectx.ndBuiltinCache) + } + + for i := range ectx.queryTracers { + q = q.WithQueryTracer(ectx.queryTracers[i]) + } + + if ectx.parsedInput != nil { + q = q.WithInput(ast.NewTerm(ectx.parsedInput)) + } + + if ectx.httpRoundTripper != nil { + q = q.WithHTTPRoundTripper(ectx.httpRoundTripper) + } + + for i := range ectx.resolvers { + q = q.WithResolver(ectx.resolvers[i].ref, ectx.resolvers[i].r) + } + + // Cancel query if context is cancelled or deadline is reached. + if ectx.externalCancel == nil { + // Create a one-off goroutine to handle cancellation for this query. + c := topdown.NewCancel() + q = q.WithCancel(c) + exit := make(chan struct{}) + defer close(exit) + go waitForDone(ctx, exit, func() { + c.Cancel() + }) + } else { + // Query cancellation is being handled elsewhere. + q = q.WithCancel(ectx.externalCancel) + } + + var rs ResultSet + err := q.Iter(ctx, func(qr topdown.QueryResult) error { + result, err := r.generateResult(qr, ectx) + if err != nil { + return err + } + rs = append(rs, result) + return nil + }) + + if err != nil { + return nil, err + } + + if len(rs) == 0 { + return nil, nil + } + + return rs, nil +} + +func (r *Rego) evalWasm(ctx context.Context, ectx *EvalContext) (ResultSet, error) { + input := ectx.rawInput + if ectx.parsedInput != nil { + i := any(ectx.parsedInput) + input = &i + } + result, err := r.opa.Eval(ctx, opa.EvalOpts{ + Metrics: r.metrics, + Input: input, + Time: ectx.time, + Seed: ectx.seed, + InterQueryBuiltinCache: ectx.interQueryBuiltinCache, + NDBuiltinCache: ectx.ndBuiltinCache, + PrintHook: ectx.printHook, + Capabilities: ectx.capabilities, + }) + if err != nil { + return nil, err + } + + parsed, err := ast.ParseTerm(string(result.Result)) + if err != nil { + return nil, err + } + + return r.valueToQueryResult(parsed.Value, ectx) +} + +func (r *Rego) valueToQueryResult(res ast.Value, ectx *EvalContext) (ResultSet, error) { + resultSet, ok := res.(ast.Set) + if !ok { + return nil, errors.New("illegal result type") + } + + if resultSet.Len() == 0 { + return nil, nil + } + + var rs ResultSet + err := resultSet.Iter(func(term *ast.Term) error { + obj, ok := term.Value.(ast.Object) + if !ok { + return errors.New("illegal result type") + } + qr := topdown.QueryResult{} + obj.Foreach(func(k, v *ast.Term) { + kvt := ast.VarTerm(string(k.Value.(ast.String))) + qr[kvt.Value.(ast.Var)] = v + }) + result, err := r.generateResult(qr, ectx) + if err != nil { + return err + } + rs = append(rs, result) + return nil + }) + + return rs, err +} + +func (r *Rego) generateResult(qr topdown.QueryResult, ectx *EvalContext) (Result, error) { + + rewritten := ectx.compiledQuery.compiler.RewrittenVars() + + result := newResult() + for k, term := range qr { + if rw, ok := rewritten[k]; ok { + k = rw + } + if isTermVar(k) || isTermWasmVar(k) || k.IsGenerated() || k.IsWildcard() { + continue + } + + v, err := r.generateJSON(term, ectx) + if err != nil { + return result, err + } + + result.Bindings[string(k)] = v + } + + for _, expr := range ectx.compiledQuery.query { + if expr.Generated { + continue + } + + if k, ok := r.capture[expr]; ok { + v, err := r.generateJSON(qr[k], ectx) + if err != nil { + return result, err + } + result.Expressions = append(result.Expressions, newExpressionValue(expr, v)) + } else { + result.Expressions = append(result.Expressions, newExpressionValue(expr, true)) + } + + } + return result, nil +} + +func (r *Rego) partialResult(ctx context.Context, pCfg *PrepareConfig) (PartialResult, error) { + + err := r.prepare(ctx, partialResultQueryType, []extraStage{ + { + after: "ResolveRefs", + stage: ast.QueryCompilerStageDefinition{ + Name: "RewriteForPartialEval", + MetricName: "query_compile_stage_rewrite_for_partial_eval", + Stage: r.rewriteQueryForPartialEval, + }, + }, + }) + if err != nil { + return PartialResult{}, err + } + + ectx := &EvalContext{ + parsedInput: r.parsedInput, + metrics: r.metrics, + txn: r.txn, + partialNamespace: r.partialNamespace, + queryTracers: r.queryTracers, + compiledQuery: r.compiledQueries[partialResultQueryType], + instrumentation: r.instrumentation, + indexing: true, + resolvers: r.resolvers, + capabilities: r.capabilities, + strictBuiltinErrors: r.strictBuiltinErrors, + nondeterministicBuiltins: r.nondeterministicBuiltins, + } + + disableInlining := r.disableInlining + + if pCfg.disableInlining != nil { + disableInlining = *pCfg.disableInlining + } + + ectx.disableInlining, err = parseStringsToRefs(disableInlining) + if err != nil { + return PartialResult{}, err + } + + pq, err := r.partial(ctx, ectx) + if err != nil { + return PartialResult{}, err + } + + // Construct module for queries. + id := fmt.Sprintf("__partialresult__%s__", ectx.partialNamespace) + + module, err := ast.ParseModuleWithOpts(id, "package "+ectx.partialNamespace, + ast.ParserOptions{RegoVersion: r.regoVersion}) + if err != nil { + return PartialResult{}, errors.New("bad partial namespace") + } + + module.Rules = make([]*ast.Rule, len(pq.Queries)) + for i, body := range pq.Queries { + rule := &ast.Rule{ + Head: ast.NewHead(ast.Var("__result__"), nil, ast.Wildcard), + Body: body, + Module: module, + } + module.Rules[i] = rule + if checkPartialResultForRecursiveRefs(body, rule.Path()) { + return PartialResult{}, Errors{errPartialEvaluationNotEffective} + } + } + + // Update compiler with partial evaluation output. + r.compiler.Modules[id] = module + for i, module := range pq.Support { + r.compiler.Modules[fmt.Sprintf("__partialsupport__%s__%d__", ectx.partialNamespace, i)] = module + } + + r.metrics.Timer(metrics.RegoModuleCompile).Start() + r.compilerForTxn(ctx, r.store, r.txn).Compile(r.compiler.Modules) + r.metrics.Timer(metrics.RegoModuleCompile).Stop() + + if r.compiler.Failed() { + return PartialResult{}, r.compiler.Errors + } + + result := PartialResult{ + compiler: r.compiler, + store: r.store, + body: ast.MustParseBody(fmt.Sprintf("data.%v.__result__", ectx.partialNamespace)), + builtinDecls: r.builtinDecls, + builtinFuncs: r.builtinFuncs, + } + + return result, nil +} + +func (r *Rego) partial(ctx context.Context, ectx *EvalContext) (*PartialQueries, error) { + + var unknowns []*ast.Term + + switch { + case ectx.parsedUnknowns != nil: + unknowns = ectx.parsedUnknowns + case ectx.unknowns != nil: + unknowns = make([]*ast.Term, len(ectx.unknowns)) + for i := range ectx.unknowns { + var err error + unknowns[i], err = ast.ParseTerm(ectx.unknowns[i]) + if err != nil { + return nil, err + } + } + default: + // Use input document as unknown if caller has not specified any. + unknowns = []*ast.Term{ast.NewTerm(ast.InputRootRef)} + } + + q := topdown.NewQuery(ectx.compiledQuery.query). + WithQueryCompiler(ectx.compiledQuery.compiler). + WithCompiler(r.compiler). + WithStore(r.store). + WithTransaction(ectx.txn). + WithBuiltins(r.builtinFuncs). + WithMetrics(ectx.metrics). + WithInstrumentation(ectx.instrumentation). + WithUnknowns(unknowns). + WithDisableInlining(ectx.disableInlining). + WithNondeterministicBuiltins(ectx.nondeterministicBuiltins). + WithRuntime(r.runtime). + WithIndexing(ectx.indexing). + WithEarlyExit(ectx.earlyExit). + WithPartialNamespace(ectx.partialNamespace). + WithSkipPartialNamespace(r.skipPartialNamespace). + WithShallowInlining(r.shallowInlining). + WithInterQueryBuiltinCache(ectx.interQueryBuiltinCache). + WithInterQueryBuiltinValueCache(ectx.interQueryBuiltinValueCache). + WithStrictBuiltinErrors(ectx.strictBuiltinErrors). + WithSeed(ectx.seed). + WithPrintHook(ectx.printHook) + + if !ectx.time.IsZero() { + q = q.WithTime(ectx.time) + } + + if ectx.ndBuiltinCache != nil { + q = q.WithNDBuiltinCache(ectx.ndBuiltinCache) + } + + for i := range ectx.queryTracers { + q = q.WithQueryTracer(ectx.queryTracers[i]) + } + + if ectx.parsedInput != nil { + q = q.WithInput(ast.NewTerm(ectx.parsedInput)) + } + + for i := range ectx.resolvers { + q = q.WithResolver(ectx.resolvers[i].ref, ectx.resolvers[i].r) + } + + // Cancel query if context is cancelled or deadline is reached. + if ectx.externalCancel == nil { + // Create a one-off goroutine to handle cancellation for this query. + c := topdown.NewCancel() + q = q.WithCancel(c) + exit := make(chan struct{}) + defer close(exit) + go waitForDone(ctx, exit, func() { + c.Cancel() + }) + } else { + // Query cancellation is being handled elsewhere. + q = q.WithCancel(ectx.externalCancel) + } + + queries, support, err := q.PartialRun(ctx) + if err != nil { + return nil, err + } + + // If the target rego-version is v0, and the rego.v1 import is available, then we attempt to apply it to support modules. + if r.regoVersion == ast.RegoV0 && + (r.capabilities == nil || + r.capabilities.ContainsFeature(ast.FeatureRegoV1Import) || + r.capabilities.ContainsFeature(ast.FeatureRegoV1)) { + + for i, mod := range support { + // We can't apply the RegoV0CompatV1 version to the support module if it contains rules or vars that + // conflict with future keywords. + applyRegoVersion := true + + ast.WalkRules(mod, func(r *ast.Rule) bool { + name := r.Head.Name + if name == "" && len(r.Head.Reference) > 0 { + name = r.Head.Reference[0].Value.(ast.Var) + } + if ast.IsFutureKeywordForRegoVersion(name.String(), ast.RegoV0) { + applyRegoVersion = false + return true + } + return false + }) + + if applyRegoVersion { + ast.WalkVars(mod, func(v ast.Var) bool { + if ast.IsFutureKeywordForRegoVersion(v.String(), ast.RegoV0) { + applyRegoVersion = false + return true + } + return false + }) + } + + if applyRegoVersion { + support[i].SetRegoVersion(ast.RegoV0CompatV1) + } else { + support[i].SetRegoVersion(r.regoVersion) + } + } + } else { + // If the target rego-version is not v0, then we apply the target rego-version to the support modules. + for i := range support { + support[i].SetRegoVersion(r.regoVersion) + } + } + + pq := &PartialQueries{ + Queries: queries, + Support: support, + } + + return pq, nil +} + +func (r *Rego) rewriteQueryToCaptureValue(_ ast.QueryCompiler, query ast.Body) (ast.Body, error) { + + checkCapture := iteration(query) || len(query) > 1 + + for _, expr := range query { + + if expr.Negated { + continue + } + + if expr.IsAssignment() || expr.IsEquality() { + continue + } + + var capture *ast.Term + + // If the expression can be evaluated as a function, rewrite it to + // capture the return value. E.g., neq(1,2) becomes neq(1,2,x) but + // plus(1,2,x) does not get rewritten. + switch terms := expr.Terms.(type) { + case *ast.Term: + capture = r.generateTermVar() + expr.Terms = ast.Equality.Expr(terms, capture).Terms + r.capture[expr] = capture.Value.(ast.Var) + case []*ast.Term: + tpe := r.compiler.TypeEnv.Get(terms[0]) + if !types.Void(tpe) && types.Arity(tpe) == len(terms)-1 { + capture = r.generateTermVar() + expr.Terms = append(terms, capture) + r.capture[expr] = capture.Value.(ast.Var) + } + } + + if capture != nil && checkCapture { + cpy := expr.Copy() + cpy.Terms = capture + cpy.Generated = true + cpy.With = nil + query.Append(cpy) + } + } + + return query, nil +} + +func (*Rego) rewriteQueryForPartialEval(_ ast.QueryCompiler, query ast.Body) (ast.Body, error) { + if len(query) != 1 { + return nil, errors.New("partial evaluation requires single ref (not multiple expressions)") + } + + term, ok := query[0].Terms.(*ast.Term) + if !ok { + return nil, errors.New("partial evaluation requires ref (not expression)") + } + + ref, ok := term.Value.(ast.Ref) + if !ok { + return nil, fmt.Errorf("partial evaluation requires ref (not %v)", ast.ValueName(term.Value)) + } + + if !ref.IsGround() { + return nil, errors.New("partial evaluation requires ground ref") + } + + return ast.NewBody(ast.Equality.Expr(ast.Wildcard, term)), nil +} + +// rewriteEqualsForPartialQueryCompile will rewrite == to = in queries. Normally +// this wouldn't be done, except for handling queries with the `Partial` API +// where rewriting them can substantially simplify the result, and it is unlikely +// that the caller would need expression values. +func (*Rego) rewriteEqualsForPartialQueryCompile(_ ast.QueryCompiler, query ast.Body) (ast.Body, error) { + doubleEq := ast.Equal.Ref() + unifyOp := ast.Equality.Ref() + ast.WalkExprs(query, func(x *ast.Expr) bool { + if x.IsCall() { + operator := x.Operator() + if operator.Equal(doubleEq) && len(x.Operands()) == 2 { + x.SetOperator(ast.NewTerm(unifyOp)) + } + } + return false + }) + return query, nil +} + +func (r *Rego) generateTermVar() *ast.Term { + r.termVarID++ + prefix := ast.WildcardPrefix + if p := r.targetPlugin(r.target); p != nil { + prefix = wasmVarPrefix + } else if r.target == targetWasm { + prefix = wasmVarPrefix + } + return ast.VarTerm(fmt.Sprintf("%sterm%v", prefix, r.termVarID)) +} + +func (r Rego) hasQuery() bool { + return len(r.query) != 0 || len(r.parsedQuery) != 0 +} + +func (r Rego) hasWasmModule() bool { + for _, b := range r.bundles { + if len(b.WasmModules) > 0 { + return true + } + } + return false +} + +type transactionCloser func(ctx context.Context, err error) error + +// getTxn will conditionally create a read or write transaction suitable for +// the configured Rego object. The returned function should be used to close the txn +// regardless of status. +func (r *Rego) getTxn(ctx context.Context) (storage.Transaction, transactionCloser, error) { + + noopCloser := func(_ context.Context, _ error) error { + return nil // no-op default + } + + if r.txn != nil { + // Externally provided txn + return r.txn, noopCloser, nil + } + + // Create a new transaction.. + params := storage.TransactionParams{} + + // Bundles and data paths may require writing data files or manifests to storage + if len(r.bundles) > 0 || len(r.bundlePaths) > 0 || len(r.loadPaths.paths) > 0 { + + // If we were given a store we will *not* write to it, only do that on one + // which was created automatically on behalf of the user. + if !r.ownStore { + return nil, noopCloser, errors.New("unable to start write transaction when store was provided") + } + + params.Write = true + } + + txn, err := r.store.NewTransaction(ctx, params) + if err != nil { + return nil, noopCloser, err + } + + // Setup a closer function that will abort or commit as needed. + closer := func(ctx context.Context, txnErr error) error { + var err error + + if txnErr == nil && params.Write { + err = r.store.Commit(ctx, txn) + } else { + r.store.Abort(ctx, txn) + } + + // Clear the auto created transaction now that it is closed. + r.txn = nil + + return err + } + + return txn, closer, nil +} + +func (r *Rego) compilerForTxn(ctx context.Context, store storage.Store, txn storage.Transaction) *ast.Compiler { + // Update the compiler to have a valid path conflict check + // for the current context and transaction. + return r.compiler.WithPathConflictsCheck(storage.NonEmpty(ctx, store, txn)) +} + +func checkPartialResultForRecursiveRefs(body ast.Body, path ast.Ref) bool { + var stop bool + ast.WalkRefs(body, func(x ast.Ref) bool { + if !stop { + if path.HasPrefix(x) { + stop = true + } + } + return stop + }) + return stop +} + +func isTermVar(v ast.Var) bool { + return strings.HasPrefix(string(v), ast.WildcardPrefix+"term") +} + +func isTermWasmVar(v ast.Var) bool { + return strings.HasPrefix(string(v), wasmVarPrefix+"term") +} + +func waitForDone(ctx context.Context, exit chan struct{}, f func()) { + select { + case <-exit: + return + case <-ctx.Done(): + f() + return + } +} + +type rawModule struct { + filename string + module string +} + +func (m rawModule) Parse() (*ast.Module, error) { + return ast.ParseModule(m.filename, m.module) +} + +func (m rawModule) ParseWithOpts(opts ast.ParserOptions) (*ast.Module, error) { + return ast.ParseModuleWithOpts(m.filename, m.module, opts) +} + +type extraStage struct { + after string + stage ast.QueryCompilerStageDefinition +} + +type refResolver struct { + ref ast.Ref + r resolver.Resolver +} + +func iteration(x any) bool { + + var stopped bool + + vis := ast.NewGenericVisitor(func(x any) bool { + switch x := x.(type) { + case *ast.Term: + if ast.IsComprehension(x.Value) { + return true + } + case ast.Ref: + if !stopped { + if bi := ast.BuiltinMap[x.String()]; bi != nil { + if bi.Relation { + stopped = true + return stopped + } + } + for i := 1; i < len(x); i++ { + if _, ok := x[i].Value.(ast.Var); ok { + stopped = true + return stopped + } + } + } + return stopped + } + return stopped + }) + + vis.Walk(x) + + return stopped +} + +func parseStringsToRefs(s []string) ([]ast.Ref, error) { + if len(s) == 0 { + return nil, nil + } + + refs := make([]ast.Ref, len(s)) + for i := range refs { + var err error + refs[i], err = ast.ParseRef(s[i]) + if err != nil { + return nil, err + } + } + + return refs, nil +} + +// helper function to finish a built-in function call. If an error occurred, +// wrap the error and return it. Otherwise, invoke the iterator if the result +// was defined. +func finishFunction(name string, bctx topdown.BuiltinContext, result *ast.Term, err error, iter func(*ast.Term) error) error { + if err != nil { + var e *HaltError + sb := strings.Builder{} + if errors.As(err, &e) { + sb.Grow(len(name) + len(e.Error()) + 2) + sb.WriteString(name) + sb.WriteString(": ") + sb.WriteString(e.Error()) + tdErr := &topdown.Error{ + Code: topdown.BuiltinErr, + Message: sb.String(), + Location: bctx.Location, + } + return topdown.Halt{Err: tdErr.Wrap(e)} + } + sb.Grow(len(name) + len(err.Error()) + 2) + sb.WriteString(name) + sb.WriteString(": ") + sb.WriteString(err.Error()) + tdErr := &topdown.Error{ + Code: topdown.BuiltinErr, + Message: sb.String(), + Location: bctx.Location, + } + return tdErr.Wrap(err) + } + if result == nil { + return nil + } + return iter(result) +} + +// helper function to return an option that sets a custom built-in function. +func newFunction(decl *Function, f topdown.BuiltinFunc) func(*Rego) { + return func(r *Rego) { + r.builtinDecls[decl.Name] = &ast.Builtin{ + Name: decl.Name, + Decl: decl.Decl, + Nondeterministic: decl.Nondeterministic, + } + r.builtinFuncs[decl.Name] = &topdown.Builtin{ + Decl: r.builtinDecls[decl.Name], + Func: f, + } + } +} + +func generateJSON(term *ast.Term, ectx *EvalContext) (any, error) { + return ast.JSONWithOpt(term.Value, + ast.JSONOpt{ + SortSets: ectx.sortSets, + CopyMaps: ectx.copyMaps, + }) +} + +func (r *Rego) planQuery(queries []ast.Body, evalQueryType queryType) (*ir.Policy, error) { + modules := make([]*ast.Module, 0, len(r.compiler.Modules)) + for _, module := range r.compiler.Modules { + modules = append(modules, module) + } + + decls := make(map[string]*ast.Builtin, len(r.builtinDecls)+len(ast.BuiltinMap)) + maps.Copy(decls, ast.BuiltinMap) + maps.Copy(decls, r.builtinDecls) + + const queryName = "eval" // NOTE(tsandall): the query name is arbitrary + + p := planner.New(). + WithQueries([]planner.QuerySet{ + { + Name: queryName, + Queries: queries, + RewrittenVars: r.compiledQueries[evalQueryType].compiler.RewrittenVars(), + }, + }). + WithModules(modules). + WithBuiltinDecls(decls). + WithDebug(r.dump) + + policy, err := p.Plan() + if err != nil { + return nil, err + } + if r.dump != nil { + fmt.Fprintln(r.dump, "PLAN:") + fmt.Fprintln(r.dump, "-----") + err = ir.Pretty(r.dump, policy) + if err != nil { + return nil, err + } + fmt.Fprintln(r.dump) + } + return policy, nil +} diff --git a/third_party/opa/v1/rego/rego_bench_test.go b/third_party/opa/v1/rego/rego_bench_test.go new file mode 100644 index 000000000000..29e68e2414b8 --- /dev/null +++ b/third_party/opa/v1/rego/rego_bench_test.go @@ -0,0 +1,508 @@ +package rego + +import ( + "context" + "encoding/json" + "fmt" + "os" + "strconv" + "testing" + + "github.com/open-policy-agent/opa/internal/runtime" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func BenchmarkPartialObjectRuleCrossModule(b *testing.B) { + ctx := context.Background() + sizes := []int{10, 100, 1000} + + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + store := inmem.NewFromObject(map[string]any{}) + mods := test.PartialObjectBenchmarkCrossModule(n) + query := "data.test.foo" + + input := make(map[string]any) + for idx := range 4 { + input[fmt.Sprintf("test_input_%d", idx)] = "test_input_10" + } + inputAST, err := ast.InterfaceToValue(input) + if err != nil { + b.Fatal(err) + } + + compiler := ast.MustCompileModules(map[string]string{ + "test/foo.rego": mods[0], + "test/bar.rego": mods[1], + "test/baz.rego": mods[2], + }) + info, err := runtime.Term(runtime.Params{}) + if err != nil { + b.Fatal(err) + } + + pq, err := New( + Query(query), + Compiler(compiler), + Store(store), + Runtime(info), + ).PrepareForEval(ctx) + + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + + for range b.N { + _, err = pq.Eval( + ctx, + EvalParsedInput(inputAST), + EvalRuleIndexing(true), + EvalEarlyExit(true), + ) + + if err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkCustomFunctionInHotPath(b *testing.B) { + ctx := context.Background() + input := ast.MustParseTerm(mustReadFileAsString(b, "testdata/ast.json")) + module := ast.MustParseModule(`package test + + import rego.v1 + + r := count(refs) + + refs contains value if { + walk(input, [_, value]) + is_ref(value) + } + + is_ref(value) if value.type == "ref" + is_ref(value) if value[0].type == "ref"`) + + r := New(Query("data.test.r = x"), ParsedModule(module)) + + pq, err := r.PrepareForEval(ctx) + if err != nil { + b.Fatal(err) + } + + for range b.N { + res, err := pq.Eval(ctx, EvalParsedInput(input.Value)) + if err != nil { + b.Fatal(err) + } + + if res == nil { + b.Fatal("expected result") + } + + if res[0].Bindings["x"].(json.Number) != "402" { + b.Fatalf("expected 402, got %v", res[0].Bindings["x"]) + } + } +} + +// Benchmarks of the ACI test data from Regorus +// https://github.com/microsoft/regorus?tab=readme-ov-file#performance + +// BenchmarkAciTestBuildAndEval-10 37 30700209 ns/op 16437935 B/op 384211 allocs/op +// BenchmarkAciTestBuildAndEval-12 58 17566909 ns/op 15991409 B/op 304237 allocs/op +func BenchmarkAciTestBuildAndEval(b *testing.B) { + ctx := context.Background() + + for range b.N { + bundle, err := loader.NewFileLoader(). + WithRegoVersion(ast.RegoV0). + AsBundle("testdata/aci") + if err != nil { + b.Fatal(err) + } + + input := ast.MustParseTerm(mustReadFileAsString(b, "testdata/aci/input.json")) + + r := New(Query("data.framework.mount_overlay = x"), ParsedBundle("", bundle)) + + pq, err := r.PrepareForEval(ctx) + if err != nil { + b.Fatal(err) + } + + res, err := pq.Eval(ctx, EvalParsedInput(input.Value)) + if err != nil { + b.Fatal(err) + } + + _ = res + } +} + +// BenchmarkAciTestOnlyEval-10 12752 92188 ns/op 50005 B/op 1062 allocs/op +// BenchmarkAciTestOnlyEval-10 13521 86647 ns/op 47448 B/op 967 allocs/op // ref.CopyNonGround +// BenchmarkAciTestOnlyEval-12 21007 57551 ns/op 45323 B/op 920 allocs/op +func BenchmarkAciTestOnlyEval(b *testing.B) { + ctx := context.Background() + + bundle, err := loader.NewFileLoader(). + WithRegoVersion(ast.RegoV0). + AsBundle("testdata/aci") + if err != nil { + b.Fatal(err) + } + + input := ast.MustParseTerm(mustReadFileAsString(b, "testdata/aci/input.json")) + + r := New(Query("data.framework.mount_overlay = x"), ParsedBundle("", bundle)) + + pq, err := r.PrepareForEval(ctx) + if err != nil { + b.Fatal(err) + } + + for range b.N { + res, err := pq.Eval(ctx, EvalParsedInput(input.Value)) + if err != nil { + b.Fatal(err) + } + _ = res + } +} + +// BenchmarkArrayIteration-10 +// 15574 77121 ns/op 67249 B/op 1115 allocs/op // handleErr wrapping, not inlined +// 33862 35864 ns/op 5768 B/op 93 allocs/op // handleErr only on error, inlined +func BenchmarkArrayIteration(b *testing.B) { + ctx := context.Background() + + at := make([]*ast.Term, 512) + for i := range 511 { + at[i] = ast.StringTerm("a") + } + at[511] = ast.StringTerm("v") + + input := ast.NewObject(ast.Item(ast.StringTerm("foo"), ast.ArrayTerm(at...))) + module := ast.MustParseModule(`package test + + default r := false + + r if input.foo[_] == "v"`) + + r := New(Query("data.test.r = x"), ParsedModule(module)) + + pq, err := r.PrepareForEval(ctx) + if err != nil { + b.Fatal(err) + } + + for range b.N { + res, err := pq.Eval(ctx, EvalParsedInput(input)) + if err != nil { + b.Fatal(err) + } + + if res == nil { + b.Fatal("expected result") + } + + if res[0].Bindings["x"].(bool) != true { + b.Fatalf("expected true, got %v", res[0].Bindings["x"]) + } + } +} + +// BenchmarkSetIteration-10 +// 4800 272403 ns/op 80875 B/op 1193 allocs/op // handleErr wrapping, not inlined +// 4933 223234 ns/op 76772 B/op 681 allocs/op // handleErr only on error, not inlined +func BenchmarkSetIteration(b *testing.B) { + ctx := context.Background() + + at := make([]*ast.Term, 512) + for i := range 512 { + at[i] = ast.StringTerm(strconv.Itoa(i)) + } + + input := ast.NewObject(ast.Item(ast.StringTerm("foo"), ast.ArrayTerm(at...))) + module := ast.MustParseModule(`package test + + s := {x | x := input.foo[_]} + + default r := false + + r if s[_] == "not found"`) + + r := New(Query("data.test.r = x"), ParsedModule(module)) + + pq, err := r.PrepareForEval(ctx) + if err != nil { + b.Fatal(err) + } + + for range b.N { + res, err := pq.Eval(ctx, EvalParsedInput(input)) + if err != nil { + b.Fatal(err) + } + if res == nil { + b.Fatal("expected result") + } + if res[0].Bindings["x"].(bool) != false { + b.Fatalf("expected false, got %v", res[0].Bindings["x"]) + } + } +} + +// BenchmarkObjectIteration-10 +// 12067 99582 ns/op 72830 B/op 1126 allocs/op // handleErr wrapping, not inlined +// 15358 85080 ns/op 27752 B/op 615 allocs/op // handleErr only on error, not inlined +func BenchmarkObjectIteration(b *testing.B) { + ctx := context.Background() + + at := make([][2]*ast.Term, 512) + for i := range 512 { + at[i] = ast.Item(ast.StringTerm(strconv.Itoa(i)), ast.StringTerm(strconv.Itoa(i))) + } + + input := ast.NewObject(ast.Item(ast.StringTerm("foo"), ast.ObjectTerm(at...))) + module := ast.MustParseModule(`package test + + default r := false + + r if { + input.foo[_] == "512" + } + `) + + r := New(Query("data.test.r = x"), ParsedModule(module)) + + pq, err := r.PrepareForEval(ctx) + if err != nil { + b.Fatal(err) + } + + for range b.N { + res, err := pq.Eval(ctx, EvalParsedInput(input)) + if err != nil { + b.Fatal(err) + } + if res == nil { + b.Fatal("expected result") + } + if res[0].Bindings["x"].(bool) != false { + b.Fatalf("expected false, got %v", res[0].Bindings["x"]) + } + } +} + +// Comparing the cost of referencing not found data in Go vs. AST storage +// +// BenchmarkStoreRefNotFound/inmem-go-10 5208 212288 ns/op 160609 B/op 2936 allocs/op +// BenchmarkStoreRefNotFound/inmem-ast-10 13929 90053 ns/op 39614 B/op 1012 allocs/op +func BenchmarkStoreRefNotFound(b *testing.B) { + ctx := context.Background() + + things := make(map[string]map[string]string, 100) + for i := range 100 { + things[strconv.Itoa(i)] = map[string]string{"foo": "bar"} + } + + stores := map[string]storage.Store{ + "inmem-go": inmem.NewFromObject(map[string]any{"things": things}), + "inmem-ast": inmem.NewFromObjectWithASTRead(map[string]any{"things": things}), + } + policy := `package p + +r contains true if { + data.things[_].bar +} +` + for name, store := range stores { + b.Run(name, func(b *testing.B) { + r := New( + Query("data.p.r = x"), + Store(store), + ParsedModule(ast.MustParseModule(policy)), + GenerateJSON(func(*ast.Term, *EvalContext) (any, error) { + return nil, nil + }), + ) + + pq, err := r.PrepareForEval(ctx) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + + for range b.N { + res, err := pq.Eval(ctx) + if err != nil { + b.Fatal(err) + } + + _ = res + } + }) + } +} + +// 242.5 ns/op 168 B/op 7 allocs/op // original implementation +// 176.7 ns/op 96 B/op 4 allocs/op // sync.Pool in ptr.ValuePtr (saving 1 alloc/op per path part) +func BenchmarkStoreRead(b *testing.B) { + ctx := context.Background() + store := inmem.NewFromObjectWithASTRead(map[string]any{ + "foo": map[string]any{ + "bar": map[string]any{ + "baz": "qux", + }, + }, + }) + + txn, err := store.NewTransaction(ctx) + if err != nil { + b.Fatal(err) + } + + ref := ast.MustParseRef("data.foo.bar.baz") + + for range b.N { + // 1 alloc/op + path, err := storage.NewPathForRef(ref) + if err != nil { + b.Fatal(err) + } + + // 3 allocs/op (down from 6) + // turns each string in path into a StringTerm only to use it + // for a Get call in storage (ptr.ValuePtr) + v, err := store.Read(ctx, txn, path) + if err != nil { + b.Fatal(err) + } + + if v == nil { + b.Fatal("expected value") + } + } +} + +// 5730 ns/op 5737 B/op 93 allocs/op +// 5222 ns/op 5639 B/op 89 allocs/op // ref.CopyNonGround +// 2786 ns/op 5090 B/op 77 allocs/op // Lazy init improvements +func BenchmarkTrivialPolicy(b *testing.B) { + ctx := context.Background() + r := New( + ParsedQuery(ast.MustParseBody("data.p.r = x")), + ParsedModule(ast.MustParseModule(`package p + r := 1`)), + GenerateJSON(noOpGenerateJSON), + ) + + pq, err := r.PrepareForEval(ctx) + if err != nil { + b.Fatal(err) + } + + for range b.N { + if _, err := pq.Eval(ctx); err != nil { + b.Fatal(err) + } + } +} + +// 1851 ns/op 3376 B/op 53 allocs/op - main +// 1312 ns/op 2632 B/op 38 allocs/op - lazy init targetStack, functionMockStack, comprehensionCache +// ------------------------------------------------- and move newResolverTrie call from NewQuery to WithResolver +// ... +func BenchmarkTrivialQuery(b *testing.B) { + m := metrics.New() + r := New(ParsedQuery(ast.MustParseBody("1")), GenerateJSON(noOpGenerateJSON), Metrics(m)) + + ctx := context.Background() + + pq, err := r.PrepareForEval(ctx) + if err != nil { + b.Fatal(err) + } + + for range b.N { + if _, err := pq.Eval(ctx, EvalMetrics(m)); err != nil { + b.Fatal(err) + } + } +} + +func mustReadFileAsString(b *testing.B, path string) string { + b.Helper() + + bs, err := os.ReadFile(path) + if err != nil { + b.Fatal(err) + } + + return string(bs) +} + +func noOpGenerateJSON(*ast.Term, *EvalContext) (any, error) { + return nil, nil +} + +// 46168 ns/op 14627 B/op 496 allocs/op +// 25671 ns/op 11488 B/op 300 allocs/op +// ... +func BenchmarkGlobalVsLocalLookup(b *testing.B) { + ctx := context.Background() + + module := ast.MustParseModule(`package p +global := 100 + +global_ref if { + some i in numbers.range(1, 100) + i == global +} + +local_var if { + local := global + some i in numbers.range(1, 100) + i == local +}`) + + q1 := ast.MustParseBody("data.p.global_ref = true") + q2 := ast.MustParseBody("data.p.local_var = true") + + r1 := New(ParsedQuery(q1), ParsedModule(module), GenerateJSON(noOpGenerateJSON)) + r2 := New(ParsedQuery(q2), ParsedModule(module), GenerateJSON(noOpGenerateJSON)) + + pq1, err := r1.PrepareForEval(ctx) + if err != nil { + b.Fatal(err) + } + + pq2, err := r2.PrepareForEval(ctx) + if err != nil { + b.Fatal(err) + } + + names := []string{"global_ref", "local_var"} + + for i, pq := range []PreparedEvalQuery{pq1, pq2} { + b.Run(names[i], func(b *testing.B) { + for range b.N { + if _, err := pq.Eval(ctx); err != nil { + b.Fatal(err) + } + } + }) + } +} diff --git a/third_party/opa/v1/rego/rego_test.go b/third_party/opa/v1/rego/rego_test.go new file mode 100644 index 000000000000..4139752ad6d4 --- /dev/null +++ b/third_party/opa/v1/rego/rego_test.go @@ -0,0 +1,3451 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: goconst // string duplication is for test readability. +package rego + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "log" + "maps" + "net/http" + "net/http/httptest" + "path/filepath" + "reflect" + "slices" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/open-policy-agent/opa/internal/storage/mock" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ast/location" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestRegoEval_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expResult any + expErrs []string + }{ + { + note: "v0 module", // v0 in NOT the default version + module: `package test + +p[x] { + x = ["a", "b", "c"][_] +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "import rego.v1", + module: `package test +import rego.v1 + +p contains x if { + some x in ["a", "b", "c"] +}`, + expResult: []string{"a", "b", "c"}, + }, + { + note: "v1 module ", // v1 is the default version + module: `package test + +p contains x if { + some x in ["a", "b", "c"] +}`, + expResult: []string{"a", "b", "c"}, + }, + { + note: "v1 module, v1 compile-time violations", // v1 is the default version + module: `package test +import data.foo +import data.bar as foo + +p contains x if { + some x in ["a", "b", "c"] +}`, + expErrs: []string{ + "test.rego:3: rego_compile_error: import must not shadow import data.foo", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.module, + } + + test.WithTempFS(files, func(root string) { + ctx := context.Background() + + pq, err := New( + Load([]string{root}, nil), + Query("data.test.p"), + ).PrepareForEval(ctx) + + if tc.expErrs != nil { + if err == nil { + t.Fatalf("Expected error but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain %q but got: %v", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + rs, err := pq.Eval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(rs) != 1 { + t.Fatalf("Expected exactly one result but got: %v", rs) + } + + if reflect.DeepEqual(rs[0].Expressions[0].Value, tc.expResult) { + t.Fatalf("Expected %v but got: %v", tc.expResult, rs[0].Expressions[0].Value) + } + } + }) + }) + } +} + +func TestRegoEval_Capabilities(t *testing.T) { + tests := []struct { + note string + regoVersion ast.RegoVersion + capabilities *ast.Capabilities + module string + expResult any + expErrs []string + }{ + { + note: "v0 module, rego-v0, no capabilities", + regoVersion: ast.RegoV0, + module: `package test + +p[x] { + x = ["a", "b", "c"][_] +}`, + expResult: []string{"a", "b", "c"}, + }, + { + note: "v0 module, rego-v0, v0 capabilities", + regoVersion: ast.RegoV0, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + module: `package test + +p[x] { + x = ["a", "b", "c"][_] +}`, + expResult: []string{"a", "b", "c"}, + }, + { + note: "v0 module, rego-v0, v1 capabilities", + regoVersion: ast.RegoV0, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + module: `package test + +p[x] { + x = ["a", "b", "c"][_] +}`, + expResult: []string{"a", "b", "c"}, + }, + + { + note: "v0 module, rego-v1, no capabilities", + regoVersion: ast.RegoV1, + module: `package test + +p[x] { + x = ["a", "b", "c"][_] +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0 module, rego-v1, v0 capabilities", + regoVersion: ast.RegoV1, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + module: `package test + +p[x] { + x = ["a", "b", "c"][_] +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v0 module, rego-v1, v0 capabilities without rego_v1 feature", + regoVersion: ast.RegoV1, + capabilities: func() *ast.Capabilities { + caps := ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)) + + feats := make([]string, 0, len(caps.Features)) + for _, feat := range caps.Features { + if feat != ast.FeatureRegoV1 { + feats = append(feats, feat) + } + } + caps.Features = feats + + return caps + }(), + module: `package test + +p[x] { + x = ["a", "b", "c"][_] +}`, + expErrs: []string{ + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "v0 module, rego-v1, v1 capabilities", + regoVersion: ast.RegoV1, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + module: `package test + +p[x] { + x = ["a", "b", "c"][_] +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + + { + note: "v1 module, rego-v0, no capabilities", + regoVersion: ast.RegoV0, + module: `package test + +p contains x if { + some x in ["a", "b", "c"] +}`, + expErrs: []string{ + "test.rego:4: rego_parse_error: unexpected identifier token", + }, + }, + { + note: "v1 module, rego-v0, v0 capabilities", + regoVersion: ast.RegoV0, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + module: `package test + +p contains x if { + some x in ["a", "b", "c"] +}`, + expErrs: []string{ + "test.rego:4: rego_parse_error: unexpected identifier token", + }, + }, + { + note: "v1 module, rego-v0, v1 capabilities", + regoVersion: ast.RegoV0, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + module: `package test + +p contains x if { + some x in ["a", "b", "c"] +}`, + expErrs: []string{ + "test.rego:4: rego_parse_error: unexpected identifier token", + }, + }, + + { + note: "v1 module, rego-v1, no capabilities", + regoVersion: ast.RegoV1, + module: `package test + +p contains x if { + some x in ["a", "b", "c"] +}`, + expResult: []string{"a", "b", "c"}, + }, + { + note: "v1 module, rego-v1, v0 capabilities", + regoVersion: ast.RegoV1, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)), + module: `package test + +p contains x if { + some x in ["a", "b", "c"] +}`, + }, + { + note: "v1 module, rego-v1, v0 capabilities without rego_v1 feature", + regoVersion: ast.RegoV1, + capabilities: func() *ast.Capabilities { + caps := ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV0)) + + feats := make([]string, 0, len(caps.Features)) + for _, feat := range caps.Features { + if feat != ast.FeatureRegoV1 { + feats = append(feats, feat) + } + } + caps.Features = feats + + return caps + }(), + module: `package test + +p contains x if { + some x in ["a", "b", "c"] +}`, + expErrs: []string{ + "rego_parse_error: illegal capabilities: rego_v1 feature required for parsing v1 Rego", + }, + }, + { + note: "v1 module, rego-v1, v1 capabilities", + regoVersion: ast.RegoV1, + capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(ast.RegoV1)), + module: `package test + +p contains x if { + some x in ["a", "b", "c"] +}`, + expResult: []string{"a", "b", "c"}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.module, + } + + test.WithTempFS(files, func(root string) { + ctx := context.Background() + + pq, err := New( + SetRegoVersion(tc.regoVersion), + Capabilities(tc.capabilities), + Load([]string{root}, nil), + Query("data.test.p"), + ).PrepareForEval(ctx) + + if tc.expErrs != nil { + if err == nil { + t.Fatalf("Expected error but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%q\n\nbut got:\n\n%v", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + rs, err := pq.Eval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(rs) != 1 { + t.Fatalf("Expected exactly one result but got:\n\n%v", rs) + } + + if reflect.DeepEqual(rs[0].Expressions[0].Value, tc.expResult) { + t.Fatalf("Expected %v but got: %v", tc.expResult, rs[0].Expressions[0].Value) + } + } + }) + }) + } +} + +func assertEval(t *testing.T, r *Rego, expected string) { + t.Helper() + rs, err := r.Eval(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + assertResultSet(t, rs, expected) +} + +func assertPreparedEvalQueryEval(t *testing.T, pq PreparedEvalQuery, options []EvalOption, expected string) { + t.Helper() + rs, err := pq.Eval(context.Background(), options...) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + assertResultSet(t, rs, expected) +} + +func assertResultSet(t *testing.T, rs ResultSet, expected string) { + t.Helper() + result := []any{} + + for i := range rs { + values := []any{} + for j := range rs[i].Expressions { + values = append(values, rs[i].Expressions[j].Value) + } + result = append(result, values) + } + + if !reflect.DeepEqual(result, util.MustUnmarshalJSON([]byte(expected))) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", expected, result) + } +} + +func TestRegoEvalExpressionValue(t *testing.T) { + + module := `package test + + arr = [1,false,true] + f(x) = x + g(x, y) = x + y + h(x) = false` + + tests := []struct { + query string + expected string + }{ + { + query: "1", + expected: "[[1]]", + }, + { + query: "1+2", + expected: "[[3]]", + }, + { + query: "1+(2*3)", + expected: "[[7]]", + }, + { + query: "data.test.arr[0]", + expected: "[[1]]", + }, + { + query: "data.test.arr[1]", + expected: "[[false]]", + }, + { + query: "data.test.f(1)", + expected: "[[1]]", + }, + { + query: "data.test.f(1,x)", + expected: "[[true]]", + }, + { + query: "data.test.g(1,2)", + expected: "[[3]]", + }, + { + query: "data.test.g(1,2,x)", + expected: "[[true]]", + }, + { + query: "false", + expected: "[[false]]", + }, + { + query: "1 == 2", + expected: "[[false]]", + }, + { + query: "data.test.h(1)", + expected: "[[false]]", + }, + { + query: "data.test.g(1,2) != 3", + expected: "[[false]]", + }, + { + query: "data.test.arr[i]", + expected: "[[1], [true]]", + }, + { + query: "[x | data.test.arr[_] = x]", + expected: "[[[1, false, true]]]", + }, + { + query: "a = 1; b = 2; a > b", + expected: `[]`, + }, + } + + for _, tc := range tests { + t.Run(tc.query, func(t *testing.T) { + r := New( + Query(tc.query), + Module("", module), + ) + assertEval(t, r, tc.expected) + }) + } +} + +func TestRegoInputs(t *testing.T) { + tests := map[string]struct { + input any + expected string + }{ + "map": {map[string]bool{"foo": true}, `[[{"foo": true}]]`}, + "int": {1, `[[1]]`}, + "bool": {false, `[[false]]`}, + "struct": {struct { + Foo string `json:"baz"` + }{"bar"}, `[[{"baz":"bar"}]]`}, + "pointer to struct": {&struct { + Foo string `json:"baz"` + }{"bar"}, `[[{"baz":"bar"}]]`}, + "pointer to pointer to struct": { + func() any { + a := &struct { + Foo string `json:"baz"` + }{"bar"} + return &a + }(), `[[{"baz":"bar"}]]`}, + "slice": {[]string{"a", "b"}, `[[["a", "b"]]]`}, + "nil": {nil, `[[null]]`}, + "slice of interface": {[]any{"a", 2, true}, `[[["a", 2, true]]]`}, + } + + for desc, tc := range tests { + t.Run(desc, func(t *testing.T) { + r := New( + Query("input"), + Input(tc.input), + Schemas(nil), + ) + assertEval(t, r, tc.expected) + }) + } +} + +func TestRegoRewrittenVarsCapture(t *testing.T) { + + ctx := context.Background() + + r := New( + Query("a := 1; a != 0; a"), + ) + + rs, err := r.Eval(ctx) + if err != nil || len(rs) != 1 { + t.Fatalf("Unexpected result: %v (err: %v)", rs, err) + } + + if !reflect.DeepEqual(rs[0].Bindings["a"], json.Number("1")) { + t.Fatal("Expected a to be 1 but got:", rs[0].Bindings["a"]) + } + +} + +func TestRegoDoNotCaptureVoidCalls(t *testing.T) { + + ctx := context.Background() + + r := New(Query("print(1)")) + + rs, err := r.Eval(ctx) + if err != nil || len(rs) != 1 { + t.Fatal(err, "rs:", rs) + } + + if !rs[0].Expressions[0].Value.(bool) { + t.Fatal("expected expression value to be true") + } +} + +func TestRegoCancellation(t *testing.T) { + + ast.RegisterBuiltin(&ast.Builtin{ + Name: "test.sleep", + Decl: types.NewFunction( + types.Args(types.S), + types.Nl, + ), + }) + + topdown.RegisterBuiltinFunc("test.sleep", func(_ topdown.BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + d, _ := time.ParseDuration(string(operands[0].Value.(ast.String))) + time.Sleep(d) + return iter(ast.NullTerm()) + }) + + ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*10) + r := New(Query(`test.sleep("1s")`)) + rs, err := r.Eval(ctx) + cancel() + + if err == nil { + t.Fatalf("Expected cancellation error but got: %v", rs) + } + exp := topdown.Error{Code: topdown.CancelErr, Message: context.DeadlineExceeded.Error()} + if !errors.Is(err, &exp) { + t.Errorf("error: expected %v, got: %v", exp, err) + } +} + +func TestRegoCustomBuiltinHalt(t *testing.T) { + + funOpt := Function1( + &Function{ + Name: "halt_func", + Decl: types.NewFunction( + types.Args(types.S), + types.Nl, + ), + }, + func(BuiltinContext, *ast.Term) (*ast.Term, error) { + return nil, NewHaltError(errors.New("stop")) + }, + ) + r := New(Query(`halt_func("")`), funOpt) + rs, err := r.Eval(context.Background()) + if err == nil { + t.Fatalf("Expected halt error but got: %v", rs) + } + // exp is the error topdown returns after unwrapping the Halt + exp := topdown.Error{Code: topdown.BuiltinErr, Message: "halt_func: stop", + Location: location.NewLocation([]byte(`halt_func("")`), "", 1, 1)} + if !errors.Is(err, &exp) { + t.Fatalf("error: expected %v, got: %v", exp, err) + } +} + +func TestRegoMetrics(t *testing.T) { + m := metrics.New() + r := New(Query("foo = 1"), Module("foo.rego", "package x"), Metrics(m)) + ctx := context.Background() + _, err := r.Eval(ctx) + if err != nil { + t.Fatal(err) + } + + validateRegoMetrics(t, m, []string{ + "timer_rego_query_parse_ns", + "timer_rego_query_eval_ns", + "timer_rego_query_compile_ns", + "timer_rego_module_parse_ns", + "timer_rego_module_compile_ns", + }) +} + +func TestPreparedRegoMetrics(t *testing.T) { + m := metrics.New() + r := New(Query("foo = 1"), Module("foo.rego", "package x"), Metrics(m)) + ctx := context.Background() + pq, err := r.PrepareForEval(ctx) + if err != nil { + t.Fatal(err) + } + + _, err = pq.Eval(ctx, EvalMetrics(m)) + if err != nil { + t.Fatal(err) + } + + validateRegoMetrics(t, m, []string{ + "timer_rego_query_parse_ns", + "timer_rego_query_eval_ns", + "timer_rego_query_compile_ns", + "timer_rego_module_parse_ns", + "timer_rego_module_compile_ns", + }) +} + +func TestPreparedRegoMetricsPrepareOnly(t *testing.T) { + m := metrics.New() + r := New(Query("foo = 1"), Module("foo.rego", "package x"), Metrics(m)) + ctx := context.Background() + pq, err := r.PrepareForEval(ctx) + if err != nil { + t.Fatal(err) + } + + _, err = pq.Eval(ctx) // No EvalMetrics() passed in + if err != nil { + t.Fatal(err) + } + + validateRegoMetrics(t, m, []string{ + "timer_rego_query_parse_ns", + "timer_rego_query_compile_ns", + "timer_rego_module_parse_ns", + "timer_rego_module_compile_ns", + }) +} + +func TestPreparedRegoMetricsEvalOnly(t *testing.T) { + m := metrics.New() + r := New(Query("foo = 1"), Module("foo.rego", "package x")) // No Metrics() passed in + ctx := context.Background() + pq, err := r.PrepareForEval(ctx) + if err != nil { + t.Fatal(err) + } + + _, err = pq.Eval(ctx, EvalMetrics(m)) + if err != nil { + t.Fatal(err) + } + + validateRegoMetrics(t, m, []string{ + "timer_rego_query_eval_ns", + }) +} + +func validateRegoMetrics(t *testing.T, m metrics.Metrics, expectedFields []string) { + t.Helper() + + all := m.All() + + for _, name := range expectedFields { + value, ok := all[name] + if !ok { + t.Errorf("expected to find %v but did not", name) + } + if value.(int64) == 0 { + t.Errorf("expected metric %v to have some non-zero value, but found 0", name) + } + } +} + +func TestRegoInstrumentExtraEvalCompilerStage(t *testing.T) { + m := metrics.New() + r := New(Query("foo = 1"), Module("foo.rego", "package x"), Metrics(m), Instrument(true)) + ctx := context.Background() + _, err := r.Eval(ctx) + if err != nil { + t.Fatal(err) + } + + exp := []string{ + "timer_query_compile_stage_rewrite_to_capture_value_ns", + } + + all := m.All() + + for _, name := range exp { + if _, ok := all[name]; !ok { + t.Errorf("expected to find %v but did not", name) + } + } +} + +func TestPreparedRegoInstrumentExtraEvalCompilerStage(t *testing.T) { + m := metrics.New() + r := New(Query("foo = 1"), Module("foo.rego", "package x"), Metrics(m), Instrument(true)) + ctx := context.Background() + pq, err := r.PrepareForEval(ctx) + if err != nil { + t.Fatal(err) + } + + // No metrics flag is passed in, should not affect results for compiler stage + // but expect to turn off instrumentation for evaluation. + _, err = pq.Eval(ctx) + if err != nil { + t.Fatal(err) + } + + exp := []string{ + "timer_query_compile_stage_rewrite_to_capture_value_ns", + } + + nExp := []string{ + "timer_eval_op_plug_ns", // We should *not* see the eval timers + } + + all := m.All() + + for _, name := range exp { + if _, ok := all[name]; !ok { + t.Errorf("expected to find %v but did not", name) + } + } + + for _, name := range nExp { + if _, ok := all[name]; ok { + t.Errorf("did not expect to find %v", name) + } + } +} + +func TestRegoInstrumentExtraPartialCompilerStage(t *testing.T) { + m := metrics.New() + r := New(Query("foo = 1"), Module("foo.rego", "package x"), Metrics(m), Instrument(true)) + ctx := context.Background() + _, err := r.Partial(ctx) + if err != nil { + t.Fatal(err) + } + + exp := []string{ + "timer_query_compile_stage_rewrite_equals_ns", + } + + all := m.All() + + for _, name := range exp { + if _, ok := all[name]; !ok { + t.Errorf("Expected to find %v but did not", name) + } + } +} + +func TestRegoInstrumentExtraPartialResultCompilerStage(t *testing.T) { + m := metrics.New() + r := New(Query("input.x"), Module("foo.rego", "package x"), Metrics(m), Instrument(true)) + ctx := context.Background() + _, err := r.PartialResult(ctx) + if err != nil { + t.Fatal(err) + } + + exp := []string{ + "timer_query_compile_stage_rewrite_for_partial_eval_ns", + } + + all := m.All() + + for _, name := range exp { + if _, ok := all[name]; !ok { + t.Errorf("Expected to find '%v' in metrics\n\nActual:\n %+v", name, all) + } + } +} + +func TestPreparedRegoTracerNoPropagate(t *testing.T) { + tracer := topdown.NewBufferTracer() + mod := ` + package test + + p = { + input.x == 10 + } + ` + pq, err := New( + Query("data"), + Module("foo.rego", mod), + Tracer(tracer), + Input(map[string]any{"x": 10})).PrepareForEval(context.Background()) + if err != nil { + t.Fatalf("unexpected error %s", err) + } + + _, err = pq.Eval(context.Background()) // no EvalTracer option + if err != nil { + t.Fatalf("unexpected error %s", err) + } + + if len(*tracer) > 0 { + t.Fatal("expected 0 traces to be collected") + } +} + +func TestPreparedRegoQueryTracerNoPropagate(t *testing.T) { + tracer := topdown.NewBufferTracer() + mod := ` + package test + + p = { + input.x == 10 + } + ` + pq, err := New( + Query("data"), + Module("foo.rego", mod), + QueryTracer(tracer), + Input(map[string]any{"x": 10})).PrepareForEval(context.Background()) + if err != nil { + t.Fatalf("unexpected error %s", err) + } + + _, err = pq.Eval(context.Background()) // no EvalQueryTracer option + if err != nil { + t.Fatalf("unexpected error %s", err) + } + + if len(*tracer) > 0 { + t.Fatal("expected 0 traces to be collected") + } +} + +func TestRegoDisableIndexing(t *testing.T) { + tracer := topdown.NewBufferTracer() + mod := ` + package test + import rego.v1 + + p if { + input.x = 1 + } + + p if { + input.y = 1 + } + ` + pq, err := New( + Query("data"), + Module("foo.rego", mod), + ).PrepareForEval(context.Background()) + if err != nil { + t.Fatalf("unexpected error %s", err) + } + + _, err = pq.Eval( + context.Background(), + EvalQueryTracer(tracer), + EvalRuleIndexing(false), + EvalInput(map[string]any{"x": 10}), + ) + if err != nil { + t.Fatalf("unexpected error %s", err) + } + + var evalNodes []string + for _, e := range *tracer { + if e.Op == topdown.EvalOp { + evalNodes = append(evalNodes, string(e.Node.Loc().Text)) + } + } + + expectedEvalNodes := []string{ + "input.x = 1", + "input.y = 1", + } + + for _, expected := range expectedEvalNodes { + found := slices.Contains(evalNodes, expected) + if !found { + t.Fatalf("Missing expected eval node in trace: %q\nGot: %q\n", expected, evalNodes) + } + } +} + +func TestRegoDisableIndexingWithMatch(t *testing.T) { + tracer := topdown.NewBufferTracer() + mod := ` + package test + import rego.v1 + + p if { + input.x = 1 + } + + p if { + input.y = 1 + } + ` + pq, err := New( + Query("data"), + Module("foo.rego", mod), + ).PrepareForEval(context.Background()) + if err != nil { + t.Fatalf("unexpected error %s", err) + } + + rs, err := pq.Eval( + context.Background(), + EvalQueryTracer(tracer), + EvalRuleIndexing(false), + EvalInput(map[string]any{"x": 1}), + ) + if err != nil { + t.Fatalf("unexpected error %s", err) + } + + assertResultSet(t, rs, `[[{"test": {"p": true}}]]`) + + var evalNodes []string + for _, e := range *tracer { + if e.Op == topdown.EvalOp { + evalNodes = append(evalNodes, string(e.Node.Loc().Text)) + } + } + + expectedEvalNodes := []string{ + "input.x = 1", + "input.y = 1", + } + + for _, expected := range expectedEvalNodes { + found := slices.Contains(evalNodes, expected) + if !found { + t.Fatalf("Missing expected eval node in trace: %q\nGot: %q\n", expected, evalNodes) + } + } +} + +func TestRegoCatchPathConflicts(t *testing.T) { + r := New( + Query("data"), + Module("test.rego", "package x\np=1"), + Store(inmem.NewFromObject(map[string]any{ + "x": map[string]any{"p": 1}, + })), + ) + + ctx := context.Background() + _, err := r.Eval(ctx) + + if err == nil { + t.Fatal("expected error") + } +} + +func TestPartialRewriteEquals(t *testing.T) { + mod := ` + package test + import rego.v1 + + default p = false + p if { + input.x = 1 + } + ` + r := New( + Query("data.test.p == true"), + Module("test.rego", mod), + ) + + ctx := context.Background() + pq, err := r.Partial(ctx) + + if err != nil { + t.Fatalf("unexpected error from Rego.Partial(): %s", err.Error()) + } + + // Expect to not have any "support" in the resulting queries + if len(pq.Support) > 0 { + t.Errorf("expected to not have any Support in PartialQueries: %+v", pq) + } + + expectedQuery := "input.x = 1" + if len(pq.Queries) != 1 { + t.Errorf("expected 1 query but found %d: %+v", len(pq.Queries), pq) + } + if pq.Queries[0].String() != expectedQuery { + t.Errorf("unexpected query in result, expected='%s' found='%s'", + expectedQuery, pq.Queries[0].String()) + } +} + +// NOTE(sr): https://github.com/open-policy-agent/opa/issues/4345 +func TestPrepareAndEvalRaceConditions(t *testing.T) { + tests := []struct { + note string + module string + exp string + }{ + { + note: "object", + module: `package test + import rego.v1 + p contains {"x":"y"}`, + exp: `[[[{"x":"y"}]]]`, + }, + { + note: "set", + module: `package test + import rego.v1 + p contains {"x"}`, + exp: `[[[["x"]]]]`, + }, + { + note: "array", + module: `package test + import rego.v1 + p contains ["x"]`, + exp: `[[[["x"]]]]`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + r := New( + Query("data.test.p"), + Module("", tc.module), + Package("foo"), + ) + + pq, err := r.PrepareForEval(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + // run this 1000 times concurrently + var wg sync.WaitGroup + wg.Add(1000) + for range 1000 { + go func(t *testing.T) { + t.Helper() + assertPreparedEvalQueryEval(t, pq, []EvalOption{}, tc.exp) + wg.Done() + }(t) + } + wg.Wait() + }) + } +} + +func TestPrepareAndEvalNewInput(t *testing.T) { + module := ` + package test + x = input.y + ` + + r := New( + Query("data.test.x"), + Module("", module), + Package("foo"), + ) + + pq, err := r.PrepareForEval(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput(map[string]int{"y": 1}), + }, "[[1]]") +} + +func TestPrepareAndEvalNewMetrics(t *testing.T) { + module := ` + package test + x = input.y + ` + + originalMetrics := metrics.New() + + r := New( + Query("data.test.x"), + Module("", module), + Package("foo"), + Metrics(originalMetrics), + ) + + pq, err := r.PrepareForEval(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + if len(originalMetrics.All()) == 0 { + t.Errorf("Expected metrics stored on 'originalMetrics' after Prepare()") + } + + // Reset the original ones (for testing) + // and make a new one for the Eval + originalMetrics.Clear() + newMetrics := metrics.New() + + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput(map[string]int{"y": 1}), + EvalMetrics(newMetrics), + }, "[[1]]") + + if len(originalMetrics.All()) > 0 { + t.Errorf("Expected no metrics stored on original Rego object metrics but found: %s", + originalMetrics.All()) + } + + if len(newMetrics.All()) == 0 { + t.Errorf("Expected metrics stored on 'newMetrics' after Prepare()") + } +} + +func TestPrepareAndEvalTransaction(t *testing.T) { + module := ` + package test + x = data.foo.y + ` + ctx := context.Background() + store := mock.New() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + path, ok := storage.ParsePath("/foo") + if !ok { + t.Fatalf("Unexpected error parsing path") + } + + err := storage.MakeDir(ctx, store, txn, path) + if err != nil { + t.Fatalf("Unexpected error writing to store: %s", err.Error()) + } + + err = store.Write(ctx, txn, storage.AddOp, path, map[string]any{"y": 1}) + if err != nil { + t.Fatalf("Unexpected error writing to store: %s", err.Error()) + } + + r := New( + Query("data.test.x"), + Module("", module), + Store(store), + Transaction(txn), + ) + + pq, err := r.PrepareForEval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + // Base case, expect it to use the transaction provided + assertPreparedEvalQueryEval(t, pq, []EvalOption{EvalTransaction(txn)}, "[[1]]") + + mockTxn := store.GetTransaction(txn.ID()) + for _, read := range store.Reads { + if read.Transaction != mockTxn { + t.Errorf("Found read operation with an invalid transaction, expected: %d, found: %d", mockTxn.ID(), read.Transaction.ID()) + } + } + + store.AssertValid(t) + store.Reset() + + // Case with an update to the store and a new transaction + txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + err = store.Write(ctx, txn, storage.AddOp, path, map[string]any{"y": 2}) + if err != nil { + t.Fatalf("Unexpected error writing to store: %s", err.Error()) + } + + // Expect the new result from the updated value on this transaction + assertPreparedEvalQueryEval(t, pq, []EvalOption{EvalTransaction(txn)}, "[[2]]") + + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("Unexpected error committing to store: %s", err) + } + + newMockTxn := store.GetTransaction(txn.ID()) + for _, read := range store.Reads { + if read.Transaction != newMockTxn { + t.Errorf("Found read operation with an invalid transaction, expected: %d, found: %d", mockTxn.ID(), read.Transaction.ID()) + } + } + + store.AssertValid(t) + store.Reset() + + // Case with no transaction provided, should create a new one and see the latest value + txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + err = store.Write(ctx, txn, storage.AddOp, path, map[string]any{"y": 3}) + if err != nil { + t.Fatalf("Unexpected error writing to store: %s", err.Error()) + } + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("Unexpected error committing to store: %s", err) + } + + assertPreparedEvalQueryEval(t, pq, nil, "[[3]]") + + if len(store.Transactions) != 2 { + t.Fatalf("Expected only two transactions on store, found %d", len(store.Transactions)) + } + + autoTxn := store.Transactions[1] + for _, read := range store.Reads { + if read.Transaction != autoTxn { + t.Errorf("Found read operation with an invalid transaction, expected: %d, found: %d", autoTxn, read.Transaction.ID()) + } + } + store.AssertValid(t) + +} + +func TestPrepareAndEvalIdempotent(t *testing.T) { + module := ` + package test + x = input.y + ` + + r := New( + Query("data.test.x"), + Module("", module), + Package("foo"), + ) + + pq, err := r.PrepareForEval(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + // Expect evaluating the same thing >1 time gives the same + // results each time. + for range 5 { + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput(map[string]int{"y": 1}), + }, "[[1]]") + } +} + +func TestPrepareAndEvalOriginal(t *testing.T) { + module := ` + package test + x = input.y + ` + + r := New( + Query("data.test.x"), + Module("", module), + Package("foo"), + Input(map[string]int{"y": 2}), + ) + + pq, err := r.PrepareForEval(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput(map[string]int{"y": 1}), + }, "[[1]]") + + // Even after prepare and eval with different input + // expect that the original Rego object behaves + // as expected for Eval. + + assertEval(t, r, "[[2]]") +} + +func TestPrepareAndEvalOnlyOneErrorOccurredPrintOnce(t *testing.T) { + module := ` + package test + package test + x = input.y + ` + + r := New( + Query("data.test.x"), + Module("", module), + Package("foo"), + Input(map[string]int{"y": 2}), + ) + + _, err := r.PrepareForEval(context.Background()) + if err == nil { + t.Fatal("Expected error but got nil") + } + if strings.Count(err.Error(), "1 error occurred") > 1 { + t.Fatalf("Expected to print '1 error occurred' only once") + } +} + +func TestPrepareAndEvalNewPrintHook(t *testing.T) { + module := ` + package test + import rego.v1 + x if { print(input) } + ` + + r := New( + Query("data.test.x"), + Module("", module), + Package("foo"), + EnablePrintStatements(true), + ) + + pq, err := r.PrepareForEval(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + var buf0 bytes.Buffer + ph0 := topdown.NewPrintHook(&buf0) + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput("hello"), + EvalPrintHook(ph0), + }, "[[true]]") + + if exp, act := "hello\n", buf0.String(); exp != act { + t.Fatalf("print hook, expected %q, got %q", exp, act) + } + + // repeat + var buf1 bytes.Buffer + ph1 := topdown.NewPrintHook(&buf1) + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput("world"), + EvalPrintHook(ph1), + }, "[[true]]") + + if exp, act := "world\n", buf1.String(); exp != act { + t.Fatalf("print hook, expected %q, got %q", exp, act) + } +} + +func TestPrepareAndPartialResult(t *testing.T) { + module := ` + package test + x = input.y + ` + + r := New( + Query("data.test.x"), + Module("", module), + Package("foo"), + Input(map[string]int{"y": 2}), + ) + + ctx := context.Background() + + pq, err := r.PrepareForEval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput(map[string]int{"y": 1}), + }, "[[1]]") + + // Even after prepare and eval with different input + // expect that the original Rego object behaves + // as expected for PartialResult. + + partial, err := r.PartialResult(ctx) + if err != nil { + t.Fatal(err) + } + + r2 := partial.Rego( + Input(map[string]int{"y": 7}), + ) + assertEval(t, r2, "[[7]]") +} + +func TestPrepareWithPartialEval(t *testing.T) { + module := ` + package test + x = input.y + ` + + r := New( + Query("data.test.x"), + Module("", module), + Package("foo"), + ) + + ctx := context.Background() + + // Prepare the query and partially evaluate it + pq, err := r.PrepareForEval(ctx, WithPartialEval()) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput(map[string]int{"y": 1}), + }, "[[1]]") +} + +func TestPrepareAndPartial(t *testing.T) { + mod := ` + package test + import rego.v1 + + default p = false + p if { + input.x = 1 + } + ` + r := New( + Query("data.test.p == true"), + Module("test.rego", mod), + ) + + ctx := context.Background() + + pq, err := r.PrepareForEval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput(map[string]int{"x": 1}), + }, "[[true]]") + + // Even after prepare and eval with different input + // expect that the original Rego object behaves + // as expected for Partial. + + partialQuery, err := r.Partial(ctx) + if err != nil { + t.Fatal(err) + } + expectedQuery := "input.x = 1" + if len(partialQuery.Queries) != 1 { + t.Errorf("expected 1 query but found %d: %+v", len(partialQuery.Queries), pq) + } + if partialQuery.Queries[0].String() != expectedQuery { + t.Errorf("unexpected query in result, expected='%s' found='%s'", + expectedQuery, partialQuery.Queries[0].String()) + } +} + +func TestPartialWithRegoV1(t *testing.T) { + tests := []struct { + note string + module string + expQuery string + expSupport string + }{ + { + note: "No imports", + module: `package test + p[k] contains v if { + k := "foo" + v := input.v + }`, + expQuery: `data.partial.test.p = x`, + expSupport: `package partial.test.p + +foo contains __local1__1 if { __local1__1 = input.v }`, + }, + { + note: "rego.v1 imported", + module: `package test + import rego.v1 + p[k] contains v if { + k := "foo" + v := input.v + }`, + expQuery: `data.partial.test.p = x`, + expSupport: `package partial.test.p + +foo contains __local1__1 if { __local1__1 = input.v }`, + }, + { + note: "future.keywords imported", + module: `package test + import future.keywords + p[k] contains v if { + k := "foo" + v := input.v + }`, + expQuery: `data.partial.test.p = x`, + expSupport: `package partial.test.p + +foo contains __local1__1 if { __local1__1 = input.v }`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + r := New( + Query("data.test.p = x"), + Module("test.rego", tc.module), + SetRegoVersion(ast.RegoV1), + ) + + ctx := context.Background() + + partialQuery, err := r.Partial(ctx) + if err != nil { + t.Fatal(err) + } + + actualQuery := partialQuery.Queries[0].String() + if tc.expQuery != actualQuery { + t.Fatalf("Expected partial query to be:\n\n%s\n\nbut got:\n\n%s", tc.expQuery, actualQuery) + } + + actualSupport := partialQuery.Support[0].String() + if tc.expSupport != actualSupport { + t.Fatalf("Expected support module to be:\n\n%s\n\nbut got:\n\n%s", tc.expSupport, actualSupport) + } + }) + } +} + +func TestPartialNamespace(t *testing.T) { + + r := New( + PartialNamespace("foo"), + Query("data.test.p = x"), + SetRegoVersion(ast.RegoV1), + Module("test.rego", ` + package test + + default p = false + + p if { input.x = 1 } + `), + ) + + pq, err := r.Partial(context.Background()) + if err != nil { + t.Fatal(err) + } + + expQuery := ast.MustParseBody(`data.foo.test.p = x`) + + if len(pq.Queries) != 1 || !pq.Queries[0].Equal(expQuery) { + t.Fatalf("Expected exactly one query %v but got: %v", expQuery, pq.Queries) + } + + expSupport := ast.MustParseModuleWithOpts(` + package foo.test + + default p = false + + p if { input.x = 1 } + `, ast.ParserOptions{RegoVersion: ast.RegoV1}) + + if len(pq.Support) != 1 || !pq.Support[0].Equal(expSupport) { + t.Fatalf("Expected exactly one support:\n\n%v\n\nGot:\n\n%v", expSupport, pq.Support[0]) + } +} + +func TestPrepareAndCompile(t *testing.T) { + module := ` + package test + x = input.y + ` + + r := New( + Query("data.test.x"), + Module("", module), + Package("foo"), + ) + + ctx := context.Background() + + pq, err := r.PrepareForEval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput(map[string]int{"y": 1}), + }, "[[1]]") + + // Ensure that Compile still works after Prepare + // and its Eval has been called. + _, err = r.Compile(ctx) + if err != nil { + t.Errorf("Unexpected error when compiling: %s", err.Error()) + } +} + +func TestPartialResultWithInput(t *testing.T) { + mod := ` + package test + import rego.v1 + + default p = false + p if { + input.x == 1 + } + ` + r := New( + Query("data.test.p"), + Module("test.rego", mod), + ) + + ctx := context.Background() + pr, err := r.PartialResult(ctx) + + if err != nil { + t.Fatalf("unexpected error from Rego.PartialResult(): %s", err.Error()) + } + + r2 := pr.Rego( + Input(map[string]int{"x": 1}), + ) + + assertEval(t, r2, "[[true]]") +} + +func TestPartialResultWithNamespace(t *testing.T) { + mod := ` + package test + import rego.v1 + + p if { + true + } + ` + c := ast.NewCompiler() + r := New( + Query("data.test.p"), + Module("test.rego", mod), + PartialNamespace("test_ns1"), + Compiler(c), + ) + + ctx := context.Background() + pr, err := r.PartialResult(ctx) + + if err != nil { + t.Fatalf("unexpected error from Rego.PartialResult(): %s", err.Error()) + } + + expectedQuery := "data.test_ns1.__result__" + if pr.body.String() != expectedQuery { + t.Fatalf("Expected partial result query %s got %s", expectedQuery, pr.body) + } + + r2 := pr.Rego() + + assertEval(t, r2, "[[true]]") + + if len(c.Modules) != 2 { + t.Fatalf("Expected two modules on the compiler, got: %v", c.Modules) + } + + expectedModuleID := "__partialresult__test_ns1__" + if _, ok := c.Modules[expectedModuleID]; !ok { + t.Fatalf("Expected to find module %s in compiler Modules, got: %v", expectedModuleID, c.Modules) + } +} + +func TestPreparedPartialResultWithTracer(t *testing.T) { + mod := ` + package test + import rego.v1 + + default p = false + p if { + input.x = 1 + } + ` + r := New( + Query("data.test.p == true"), + Module("test.rego", mod), + ) + + tracer := topdown.NewBufferTracer() + + ctx := context.Background() + pq, err := r.PrepareForPartial(ctx) + if err != nil { + t.Fatalf("unexpected error from Rego.PrepareForPartial(): %s", err.Error()) + } + + pqs, err := pq.Partial(ctx, EvalTracer(tracer)) + if err != nil { + t.Fatalf("unexpected error from PreparedEvalQuery.Partial(): %s", err.Error()) + } + + expectedQuery := "input.x = 1" + if len(pqs.Queries) != 1 { + t.Errorf("expected 1 query but found %d: %+v", len(pqs.Queries), pqs) + } + if pqs.Queries[0].String() != expectedQuery { + t.Errorf("unexpected query in result, expected='%s' found='%s'", + expectedQuery, pqs.Queries[0].String()) + } + + if len(*tracer) == 0 { + t.Errorf("Expected buffer tracer to contain > 0 traces") + } +} + +func TestPreparedPartialResultWithQueryTracer(t *testing.T) { + mod := ` + package test + import rego.v1 + + default p = false + p if { + input.x = 1 + } + ` + r := New( + Query("data.test.p == true"), + Module("test.rego", mod), + ) + + tracer := topdown.NewBufferTracer() + + ctx := context.Background() + pq, err := r.PrepareForPartial(ctx) + if err != nil { + t.Fatalf("unexpected error from Rego.PrepareForPartial(): %s", err.Error()) + } + + pqs, err := pq.Partial(ctx, EvalQueryTracer(tracer)) + if err != nil { + t.Fatalf("unexpected error from PreparedEvalQuery.Partial(): %s", err.Error()) + } + + expectedQuery := "input.x = 1" + if len(pqs.Queries) != 1 { + t.Errorf("expected 1 query but found %d: %+v", len(pqs.Queries), pqs) + } + if pqs.Queries[0].String() != expectedQuery { + t.Errorf("unexpected query in result, expected='%s' found='%s'", + expectedQuery, pqs.Queries[0].String()) + } + + if len(*tracer) == 0 { + t.Errorf("Expected buffer tracer to contain > 0 traces") + } +} + +func TestPartialResultSetsValidConflictChecker(t *testing.T) { + mod := ` + package test + import rego.v1 + + p if { + true + } + ` + + c := ast.NewCompiler().WithPathConflictsCheck(func(_ []string) (bool, error) { + t.Fatal("Conflict check should not have been called") + return false, nil + }) + + r := New( + Query("data.test.p"), + Module("test.rego", mod), + PartialNamespace("test_ns1"), + Compiler(c), + ) + + ctx := context.Background() + pr, err := r.PartialResult(ctx) + + if err != nil { + t.Fatalf("unexpected error from Rego.PartialResult(): %s", err.Error()) + } + + r2 := pr.Rego() + + assertEval(t, r2, "[[true]]") +} + +func TestMissingLocation(t *testing.T) { + + // Create a query programmatically and evaluate it. The Location information + // is not set so the resulting expression value will not have it. + r := New(ParsedQuery(ast.NewBody(ast.NewExpr(ast.BooleanTerm(true))))) + rs, err := r.Eval(context.Background()) + + if err != nil { + t.Fatal(err) + } else if len(rs) == 0 || !rs[0].Expressions[0].Value.(bool) { + t.Fatal("Unexpected result set:", rs) + } + + if rs[0].Expressions[0].Location != nil { + t.Fatal("Expected location data to be unset.") + } +} + +func TestBundlePassing(t *testing.T) { + + opaBundle := bundle.Bundle{ + Modules: []bundle.ModuleFile{ + { + Path: "policy.rego", + Parsed: ast.MustParseModule(`package foo + allow = true`), + Raw: []byte(`package foo + allow = true`), + }, + }, + Manifest: bundle.Manifest{Revision: "test", Roots: &[]string{"/"}}, + } + + // Pass a bundle + r := New( + ParsedBundle("123", &opaBundle), + Query("x = data.foo.allow"), + ) + + res, err := r.Eval(context.Background()) + + if err != nil { + t.Fatal(err) + } + assertResultSet(t, res, `[[true]]`) +} + +func TestModulePassing(t *testing.T) { + + // This module will not be loaded since it has the same filename as the + // file2.rego below and the raw modules override parsed modules. + module1, err := ast.ParseModule("file2.rego", `package file2 + + p = "deadbeef" + `) + if err != nil { + t.Fatal(err) + } + + r := New( + Query("data"), + Module("file1.rego", `package file1 + + p = 1 + `), + Module("file2.rego", `package file2 + + p = 2`), + ParsedModule(module1), + ParsedModule(ast.MustParseModule(`package file4 + + p = 4`)), + ) + + rs, err := r.Eval(context.Background()) + if err != nil { + t.Fatal(err) + } + + exp := util.MustUnmarshalJSON([]byte(` + { + "file1": { + "p": 1 + }, + "file2": { + "p": 2 + }, + "file4": { + "p": 4 + } + } + `)) + + if !reflect.DeepEqual(rs[0].Expressions[0].Value, exp) { + t.Fatalf("Expected %v but got %v", exp, rs[0].Expressions[0].Value) + } +} + +func TestUnsafeBuiltins(t *testing.T) { + + ctx := context.Background() + + unsafeCountExpr := "unsafe built-in function calls in expression: count" + unsafeCountExprWith := `with keyword replacing built-in function: target must not be unsafe: "count"` + + t.Run("unsafe query", func(t *testing.T) { + r := New( + Query(`count([1, 2, 3])`), + UnsafeBuiltins(map[string]struct{}{"count": {}}), + ) + if _, err := r.Eval(ctx); err == nil || !strings.Contains(err.Error(), unsafeCountExpr) { + t.Fatalf("Expected unsafe built-in error but got %v", err) + } + }) + + t.Run("unsafe query, 'with' replacement", func(t *testing.T) { + r := New( + Query(`is_array([1, 2, 3]) with is_array as count`), + UnsafeBuiltins(map[string]struct{}{"count": {}}), + ) + if _, err := r.Eval(ctx); err == nil || !strings.Contains(err.Error(), unsafeCountExprWith) { + t.Fatalf("Expected unsafe built-in error but got %v", err) + } + }) + + t.Run("unsafe module", func(t *testing.T) { + r := New( + Query(`data.pkg.deny`), + Module("pkg.rego", `package pkg + import rego.v1 + deny if{ + count(input.requests) > 10 + } + `), + UnsafeBuiltins(map[string]struct{}{"count": {}}), + ) + if _, err := r.Eval(ctx); err == nil || !strings.Contains(err.Error(), unsafeCountExpr) { + t.Fatalf("Expected unsafe built-in error but got %v", err) + } + }) + + t.Run("unsafe module, 'with' replacement in query", func(t *testing.T) { + r := New( + Query(`data.pkg.deny with is_array as count`), + Module("pkg.rego", `package pkg + import rego.v1 + deny if { + is_array(input.requests) > 10 + } + `), + UnsafeBuiltins(map[string]struct{}{"count": {}}), + ) + if _, err := r.Eval(ctx); err == nil || !strings.Contains(err.Error(), unsafeCountExprWith) { + t.Fatalf("Expected unsafe built-in error but got %v", err) + } + }) + + t.Run("unsafe module, 'with' replacement in module", func(t *testing.T) { + r := New( + Query(`data.pkg.deny`), + Module("pkg.rego", `package pkg + import rego.v1 + deny if { + is_array(input.requests) > 10 with is_array as count + } + `), + UnsafeBuiltins(map[string]struct{}{"count": {}}), + ) + if _, err := r.Eval(ctx); err == nil || !strings.Contains(err.Error(), unsafeCountExprWith) { + t.Fatalf("Expected unsafe built-in error but got %v", err) + } + }) + + t.Run("inherit in query", func(t *testing.T) { + r := New( + Compiler(ast.NewCompiler().WithUnsafeBuiltins(map[string]struct{}{"count": {}})), + Query("count([])"), + ) + if _, err := r.Eval(ctx); err == nil || !strings.Contains(err.Error(), unsafeCountExpr) { + t.Fatalf("Expected unsafe built-in error but got %v", err) + } + }) + + t.Run("inherit in query, 'with' replacement", func(t *testing.T) { + r := New( + Compiler(ast.NewCompiler().WithUnsafeBuiltins(map[string]struct{}{"count": {}})), + Query("is_array([]) with is_array as count"), + ) + if _, err := r.Eval(ctx); err == nil || !strings.Contains(err.Error(), unsafeCountExprWith) { + t.Fatalf("Expected unsafe built-in error but got %v", err) + } + }) + + t.Run("override/disable in query", func(t *testing.T) { + r := New( + Compiler(ast.NewCompiler().WithUnsafeBuiltins(map[string]struct{}{"count": {}})), + UnsafeBuiltins(map[string]struct{}{}), + Query("count([])"), + ) + if _, err := r.Eval(ctx); err != nil { + t.Fatal(err) + } + }) + + t.Run("override/change in query", func(t *testing.T) { + r := New( + Compiler(ast.NewCompiler().WithUnsafeBuiltins(map[string]struct{}{"count": {}})), + UnsafeBuiltins(map[string]struct{}{"max": {}}), + Query("count([]); max([1,2])"), + ) + + _, err := r.Eval(ctx) + if err == nil || err.Error() != "1 error occurred: 1:12: rego_type_error: unsafe built-in function calls in expression: max" { + t.Fatalf("expected error for max but got: %v", err) + } + }) + + t.Run("ignore if given compiler", func(_ *testing.T) { + r := New( + Compiler(ast.NewCompiler()), + UnsafeBuiltins(map[string]struct{}{"count": {}}), + Query("data.test.p = 0"), + Module("test.rego", `package test + + p = count([])`), + ) + rs, err := r.Eval(context.Background()) + if err != nil || len(rs) != 1 { + log.Fatalf("Unexpected error or result. Result: %v. Error: %v", rs, err) + } + }) +} + +func TestPreparedQueryGetModules(t *testing.T) { + mods := map[string]string{ + "a.rego": "package a\np = 1", + "b.rego": "package b\nq = 1", + "c.rego": "package c\nr = 1", + } + + regoArgs := make([]func(r *Rego), 0, len(mods)+1) + + for name, mod := range mods { + regoArgs = append(regoArgs, Module(name, mod)) + } + + regoArgs = append(regoArgs, Query("data")) + + ctx := context.Background() + pq, err := New(regoArgs...).PrepareForEval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + actualMods := pq.Modules() + + if len(actualMods) != len(mods) { + t.Fatalf("Expected %d modules, got %d", len(mods), len(actualMods)) + } + + for name, actualMod := range actualMods { + expectedMod, found := mods[name] + if !found { + t.Fatalf("Unexpected module %s", name) + } + if actualMod.String() != ast.MustParseModule(expectedMod).String() { + t.Fatalf("Modules for %s do not match.\n\nExpected:\n%s\n\nActual:\n%s\n\n", + name, actualMod.String(), expectedMod) + } + } +} + +func TestRegoEvalWithFile(t *testing.T) { + files := map[string]string{ + "x/x.rego": "package x\np = 1", + "x/x.json": `{"y": "foo"}`, + } + + test.WithTempFS(files, func(path string) { + ctx := context.Background() + + pq, err := New( + Load([]string{path}, nil), + Query("data"), + ).PrepareForEval(ctx) + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + rs, err := pq.Eval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + assertResultSet(t, rs, `[[{"x":{"p":1,"y":"foo"}}]]`) + }) +} + +func TestRegoEvalWithBundle(t *testing.T) { + files := map[string]string{ + "x/x.rego": "package x\np = data.x.b", + "x/data.json": `{"b": "bar"}`, + "other/not-data.json": `{"ignored": "data"}`, + } + + test.WithTempFS(files, func(path string) { + ctx := context.Background() + + pq, err := New( + LoadBundle(path), + Query("data.x.p"), + ).PrepareForEval(ctx) + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + rs, err := pq.Eval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + assertResultSet(t, rs, `[["bar"]]`) + + mods := pq.Modules() + if exp, act := 1, len(mods); exp != act { + t.Fatalf("expected %d modules, found %d", exp, act) + } + for act := range mods { + if exp := filepath.Join(path, "x", "x.rego"); exp != act { + t.Errorf("expected module name %q, got %q", exp, act) + } + } + }) +} + +func TestRegoEvalWithBundleURL(t *testing.T) { + files := map[string]string{ + "x/x.rego": "package x\np = data.x.b", + } + + test.WithTempFS(files, func(path string) { + ctx := context.Background() + pq, err := New( + LoadBundle("file://"+path), + Query("data.x.p"), + ).PrepareForEval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + mods := pq.Modules() + if exp, act := 1, len(mods); exp != act { + t.Fatalf("expected %d modules, found %d", exp, act) + } + for act := range mods { + if exp := filepath.Join(path, "x", "x.rego"); exp != act { + t.Errorf("expected module name %q, got %q", exp, act) + } + } + }) +} + +func TestRegoEvalPoliciesInStore(t *testing.T) { + store := mock.New() + ctx := context.Background() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + err := store.UpsertPolicy(ctx, txn, "a.rego", []byte("package a\np=1")) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + err = store.Commit(ctx, txn) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + pq, err := New( + Store(store), + Module("b.rego", "package b\np = data.a.p"), + Query("data.b.p"), + ).PrepareForEval(ctx) + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + rs, err := pq.Eval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + assertResultSet(t, rs, `[[1]]`) +} + +func TestRegoEvalModulesOnCompiler(t *testing.T) { + compiler := ast.NewCompiler() + + compiler.Compile(map[string]*ast.Module{ + "a.rego": ast.MustParseModule("package a\np = 1"), + }) + + if len(compiler.Errors) > 0 { + t.Fatalf("Unexpected compile errors: %s", compiler.Errors) + } + + ctx := context.Background() + + pq, err := New( + Compiler(compiler), + Query("data.a.p"), + Schemas(nil), + ).PrepareForEval(ctx) + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + rs, err := pq.Eval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + assertResultSet(t, rs, `[[1]]`) +} + +func TestRegoEvalWithRegoV1(t *testing.T) { + tests := []struct { + note string + regoVersion ast.RegoVersion + policies map[string]string + query string + expectedResult string + expectedErr string + }{ + { + note: "Rego v0", + regoVersion: ast.RegoV0, + policies: map[string]string{ + "policy.rego": `package test + x[y] { y := 1 }`, + }, + expectedResult: `[[{"x": [1]}]]`, + }, + { + note: "Rego v0, forced v1 compatibility", + regoVersion: ast.RegoV0CompatV1, + policies: map[string]string{ + "policy.rego": `package test + import rego.v1 + x contains y if { y := 1 }`, + }, + expectedResult: `[[{"x": [1]}]]`, + }, + { + note: "Rego v0, forced v1 compatibility, invalid rule head", + regoVersion: ast.RegoV0CompatV1, + policies: map[string]string{ + "policy.rego": `package test + import future.keywords.contains + x contains y { y := 1 }`, + }, + expectedErr: "rego_parse_error: `if` keyword is required before rule body", + }, + { + note: "Rego v0, forced v1 compatibility, missing required imports", + regoVersion: ast.RegoV0CompatV1, + policies: map[string]string{ + "policy.rego": `package test + x contains y if { y := 1 }`, + }, + expectedErr: "rego_parse_error: var cannot be used for rule name", // FIXME: Improve error message + }, + { + note: "Rego v1", + regoVersion: ast.RegoV1, + policies: map[string]string{ + "policy.rego": `package test + x contains y if { y := 1 }`, + }, + expectedResult: `[[{"x": [1]}]]`, + }, + { + note: "Rego v1, invalid rule head", + regoVersion: ast.RegoV1, + policies: map[string]string{ + "policy.rego": `package test + x contains y { y := 1 }`, + }, + expectedErr: "rego_parse_error: `if` keyword is required before rule body", + }, + { + note: "Rego v1, multiple files", + regoVersion: ast.RegoV1, + policies: map[string]string{ + "one.rego": `package test + x contains v if { v := 1 }`, + "two.rego": `package test + import rego.v1 + y contains v if { v := 1 }`, + "three.rego": `package test + import future.keywords + z contains v if { v := 1 }`, + }, + expectedResult: `[[{"x": [1], "y": [1], "z": [1]}]]`, + }, + } + + setup := []struct { + name string + options func(path string, policies map[string]string, t *testing.T, ctx context.Context) []func(*Rego) + }{ + { + name: "File", + options: func(path string, _ map[string]string, _ *testing.T, _ context.Context) []func(*Rego) { + return []func(*Rego){ + Load([]string{path}, nil), + } + }, + }, + { + name: "Bundle", + options: func(path string, _ map[string]string, _ *testing.T, _ context.Context) []func(*Rego) { + return []func(*Rego){ + LoadBundle(path), + } + }, + }, + { + name: "Bundle URL", + options: func(path string, _ map[string]string, _ *testing.T, _ context.Context) []func(*Rego) { + return []func(*Rego){ + LoadBundle("file://" + path), + } + }, + }, + { + name: "Store", + options: func(_ string, policies map[string]string, t *testing.T, ctx context.Context) []func(*Rego) { + t.Helper() + store := mock.New() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + for name, policy := range policies { + err := store.UpsertPolicy(ctx, txn, name, []byte(policy)) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + } + + err := store.Commit(ctx, txn) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + return []func(*Rego){ + // This extra module is required for modules in the store to be parsed + Module("extra.rego", "package extra\np = 1"), + Store(store), + } + }, + }, + } + + for _, s := range setup { + for _, tc := range tests { + t.Run(fmt.Sprintf("%s: %s", s.name, tc.note), func(t *testing.T) { + test.WithTempFS(tc.policies, func(path string) { + ctx := context.Background() + + options := append(s.options(path, tc.policies, t, ctx), + Query("data.test"), + func(r *Rego) { + SetRegoVersion(tc.regoVersion)(r) + }, + ) + + pq, err := New( + options..., + ).PrepareForEval(ctx) + + if tc.expectedErr != "" { + if err == nil { + t.Fatal("Expected error, got none") + } + if !strings.Contains(err.Error(), tc.expectedErr) { + t.Fatalf("Expected error:\n\n%s\n\ngot:\n\n%s", err, tc.expectedErr) + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + rs, err := pq.Eval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if tc.expectedResult != "" { + assertResultSet(t, rs, tc.expectedResult) + } + } + }) + }) + } + } +} + +func TestRegoLoadFilesWithProvidedStore(t *testing.T) { + ctx := context.Background() + store := mock.New() + + files := map[string]string{ + "x.rego": "package x\np = data.x.b", + } + + test.WithTempFS(files, func(path string) { + pq, err := New( + Store(store), + Query("data"), + Load([]string{path}, nil), + ).PrepareForEval(ctx) + + if err == nil { + t.Fatal("Expected an error but err == nil") + } + + if pq.r != nil { + t.Fatalf("Expected pq.r == nil, got: %+v", pq) + } + }) +} + +func TestRegoLoadBundleWithProvidedStore(t *testing.T) { + ctx := context.Background() + store := mock.New() + + files := map[string]string{ + "x/x.rego": "package x\np = data.x.b", + } + + test.WithTempFS(files, func(path string) { + pq, err := New( + Store(store), + Query("data"), + LoadBundle(path), + ).PrepareForEval(ctx) + + if err == nil { + t.Fatal("Expected an error but err == nil") + } + + if pq.r != nil { + t.Fatalf("Expected pq.r == nil, got: %+v", pq) + } + }) +} + +func TestRegoCustomBuiltinPartialPropagate(t *testing.T) { + mod := `package test + import rego.v1 + + p if { + x = trim_and_split(input.foo, "/") + x == ["foo", "bar", "baz"] + } + ` + + originalRego := New( + Module("test.rego", mod), + Query(`data.test.p`), + Function2( + &Function{ + Name: "trim_and_split", + Decl: types.NewFunction( + types.Args(types.S, types.S), // two string inputs + types.NewArray(nil, types.S), // variable-length string array output + ), + }, + func(_ BuiltinContext, a, b *ast.Term) (*ast.Term, error) { + + str, ok1 := a.Value.(ast.String) + delim, ok2 := b.Value.(ast.String) + + // The function is undefined for non-string inputs. Built-in + // functions should only return errors in unrecoverable cases. + if !ok1 || !ok2 { + return nil, nil + } + + result := strings.Split(strings.Trim(string(str), string(delim)), string(delim)) + + arr := make([]*ast.Term, len(result)) + for i := range result { + arr[i] = ast.StringTerm(result[i]) + } + + return ast.ArrayTerm(arr...), nil + }, + ), + ) + + pr, err := originalRego.PartialResult(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + rs, err := pr.Rego( + Input(map[string]any{"foo": "/foo/bar/baz/"}), + ).Eval(context.Background()) + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + assertResultSet(t, rs, `[[true]]`) + +} + +func TestRegoPartialResultRecursiveRefs(t *testing.T) { + r := New(Query("data"), Module("test.rego", `package foo.bar + import rego.v1 + + default p = false + + p if { input.x = 1 }`)) + + _, err := r.PartialResult(context.Background()) + if err == nil { + t.Fatal("expected error") + } + + if !IsPartialEvaluationNotEffectiveErr(err) { + t.Fatal("expected ineffective partial eval error") + } + +} + +func TestSkipPartialNamespaceOption(t *testing.T) { + r := New(Query("data.test.p"), Module("example.rego", ` + package test + import rego.v1 + + default p = false + + p = true if { input } + `), SkipPartialNamespace(true)) + + pq, err := r.Partial(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(pq.Queries) != 1 || !pq.Queries[0].Equal(ast.MustParseBody("data.test.p")) { + t.Fatal("expected exactly one query and for reference to not have been rewritten but got:", pq.Queries) + } + + if len(pq.Support) != 1 || !pq.Support[0].Package.Equal(ast.MustParsePackage("package test")) { + t.Fatal("expected exactly one support and for package to be same as input but got:", pq.Support) + } +} + +func TestShallowInliningOption(t *testing.T) { + r := New(Query("data.test.p = true"), + SetRegoVersion(ast.RegoV1), + Module("example.rego", ` + package test + + p if { + q = true + } + + q if { + input.x = r + } + + r = 7 + `), + ShallowInlining(true)) + + pq, err := r.Partial(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(pq.Queries) != 1 || !pq.Queries[0].Equal(ast.MustParseBody("data.partial.test.p = true")) { + t.Fatal("expected exactly one query and ref to be rewritten but got:", pq.Queries) + } + + exp := ast.MustParseModuleWithOpts(` + package partial.test + + p if { data.partial.test.q = true } + q if { 7 = input.x } + `, ast.ParserOptions{RegoVersion: ast.RegoV1}) + + if len(pq.Support) != 1 || !pq.Support[0].Equal(exp) { + t.Fatal("expected module:", exp, "\n\ngot module:", pq.Support[0]) + } +} + +func TestRegoPartialResultSortedRules(t *testing.T) { + r := New(Query("data.test.p"), + SetRegoVersion(ast.RegoV1), + Module("example.rego", ` + package test + + default p = false + + p if { + r = (input.d * input.a) + input.c + r < s + } + + p if { + r = (input.d * input.b) + input.c + r < s + } + + s = 100 + `)) + + pq, err := r.Partial(context.Background()) + if err != nil { + t.Fatal(err) + } + + // Without sorting of support rules, the output of the above partial evaluation + // resulted in a random order of the support rules (in this case two different possible outputs) + exp := ast.MustParseModuleWithOpts( + `package partial.test + + default p = false + + p = true if { lt(plus(mul(input.d, input.a), input.c), 100) } + p = true if { lt(plus(mul(input.d, input.b), input.c), 100) } + `, + ast.ParserOptions{RegoVersion: ast.RegoV1}) + + if len(pq.Support) != 1 || !pq.Support[0].Equal(exp) { + t.Fatal("expected module:", exp, "\n\ngot module:", pq.Support[0]) + } + +} + +func TestPrepareWithEmptyModule(t *testing.T) { + _, err := New( + Query("d"), + Module("example.rego", ""), + ).PrepareForEval(context.Background()) + + expected := "1 error occurred: example.rego:0: rego_parse_error: empty module" + if err == nil || err.Error() != expected { + t.Fatalf("Expected error %s, got %s", expected, err) + } +} + +func TestPrepareWithWasmTargetNotSupported(t *testing.T) { + files := map[string]string{ + "x/x.rego": "package x\np = data.x.b", + "x/data.json": `{"b": "bar"}`, + "/policy.wasm": `modules-compiled-as-wasm-binary`, + } + + test.WithTempFS(files, func(path string) { + ctx := context.Background() + + _, err := New( + LoadBundle(path), + Query("data.x.p"), + Target("wasm"), + ).PrepareForEval(ctx) + + expected := "wasm target not supported" + if err == nil || err.Error() != expected { + t.Fatalf("Expected error %s, got %s", expected, err) + } + }) +} + +func TestEvalWithInterQueryCache(t *testing.T) { + newHeaders := map[string][]string{"Cache-Control": {"max-age=290304000, public"}} + + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + headers := w.Header() + + maps.Copy(headers, newHeaders) + + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"x": 1}`)) + })) + defer ts.Close() + query := fmt.Sprintf(`http.send({"method": "get", "url": "%s", "force_json_decode": true, "cache": true})`, ts.URL) + + // add an inter-query cache + config, _ := cache.ParseCachingConfig(nil) + interQueryCache := cache.NewInterQueryCache(config) + + ctx := context.Background() + _, err := New(Query(query), InterQueryBuiltinCache(interQueryCache)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + // eval again with same query + // this request should be served by the cache + _, err = New(Query(query), InterQueryBuiltinCache(interQueryCache)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + if len(requests) != 1 { + t.Fatal("Expected server to be called only once") + } +} + +func TestEvalWithInterQueryValueCache(t *testing.T) { + ctx := context.Background() + + // add an inter-query value cache + config, _ := cache.ParseCachingConfig(nil) + interQueryValueCache := cache.NewInterQueryValueCache(ctx, config) + + m := metrics.New() + + query := `regex.match("foo.*", "foobar")` + _, err := New(Query(query), InterQueryBuiltinValueCache(interQueryValueCache), Metrics(m)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + // eval again with same query + // this request should be served by the cache + _, err = New(Query(query), InterQueryBuiltinValueCache(interQueryValueCache), Metrics(m)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + if exp, act := uint64(1), m.Counter("rego_builtin_regex_interquery_value_cache_hits").Value(); exp != act { + t.Fatalf("expected %d cache hits, got %d", exp, act) + } + + query = `glob.match("*.example.com", ["."], "api.example.com")` + _, err = New(Query(query), InterQueryBuiltinValueCache(interQueryValueCache), Metrics(m)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + // eval again with same query + // this request should be served by the cache + _, err = New(Query(query), InterQueryBuiltinValueCache(interQueryValueCache), Metrics(m)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + _, err = New(Query(query), InterQueryBuiltinValueCache(interQueryValueCache), Metrics(m)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + if exp, act := uint64(2), m.Counter("rego_builtin_glob_interquery_value_cache_hits").Value(); exp != act { + t.Fatalf("expected %d cache hits, got %d", exp, act) + } +} + +// We use http.send to ensure the NDBuiltinCache is involved. +func TestEvalWithNDCache(t *testing.T) { + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + _, _ = w.Write([]byte(`{"x": 1}`)) + })) + defer ts.Close() + query := fmt.Sprintf(`http.send({"method": "get", "url": "%s", "force_json_decode": true})`, ts.URL) + + // Set up the ND cache, and put in some arbitrary constants for the first K/V pair. + arbitraryKey := ast.Number(strconv.Itoa(2015)) + arbitraryValue := ast.String("First commit year") + ndBC := builtins.NDBCache{} + ndBC.Put("arbitrary_experiment", arbitraryKey, arbitraryValue) + + // Query execution of http.send should add an entry to the NDBuiltinCache. + ctx := context.Background() + _, err := New(Query(query), NDBuiltinCache(ndBC)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + // Check and make sure we got exactly 2x items back in the ND builtin cache. + // NDBuiltinCache always has the structure: map[ast.String]map[ast.Array]ast.Value + if len(ndBC) != 2 { + t.Fatalf("Expected exactly 2 items in non-deterministic builtin cache. Found %d items.\n", len(ndBC)) + } + // Check the cached k/v types for the HTTP section of the cache. + if cachedResults, ok := ndBC["http.send"]; ok { + err := cachedResults.Iter(func(k, v *ast.Term) error { + if _, ok := k.Value.(*ast.Array); !ok { + t.Fatalf("http.send failed to store Object key in the ND builtins cache") + } + if _, ok := v.Value.(ast.Object); !ok { + t.Fatalf("http.send failed to store Object value in the ND builtins cache") + } + return nil + }) + if err != nil { + t.Fatal(err) + } + } + + // Ensure our original arbitrary data in the cache was preserved. + if v, ok := ndBC.Get("arbitrary_experiment", arbitraryKey); ok { + if v != arbitraryValue { + t.Fatalf("Non-deterministic builtins cache value was mangled. Expected: %v, got: %v\n", arbitraryValue, v) + } + } else { + t.Fatal("Non-deterministic builtins cache lookup failed.") + } +} + +func TestEvalWithPrebuiltNDCache(t *testing.T) { + query := "time.now_ns()" + ndBC := builtins.NDBCache{} + + // Populate the cache for time.now_ns with an arbitrary timestamp. + timeValue, err := time.Parse("2006-01-02T15:04:05Z", "2015-12-28T14:08:25Z") + if err != nil { + t.Fatal(err) + } + + // Timestamp ns value will be: 1451311705000000000 + ndBC.Put("time.now_ns", ast.NewArray(), ast.Number(json.Number(strconv.FormatInt(timeValue.UnixNano(), 10)))) + // time.now_ns should use the cached entry instead of the current time. + ctx := context.Background() + rs, err := New(Query(query), NDBuiltinCache(ndBC)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + // Check that we got the correct time value in the result set. + assertResultSet(t, rs, "[[1451311705000000000]]") +} + +func TestNDBCacheWithRuleBody(t *testing.T) { + ctx := context.Background() + ts := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})) + defer ts.Close() + + ndBC := builtins.NDBCache{} + query := "data.foo.p = x" + _, err := New( + Query(query), + NDBuiltinCache(ndBC), + Module("test.rego", fmt.Sprintf(`package foo +import rego.v1 +p if { + http.send({"url": "%s", "method":"get"}) +}`, ts.URL)), + ).Eval(ctx) + if err != nil { + t.Fatal(err) + } + _, ok := ndBC["http.send"] + if !ok { + t.Fatalf("expected http.send cache entry") + } +} + +// Catches issues around iteration with ND builtins. +func TestNDBCacheWithRuleBodyAndIteration(t *testing.T) { + ctx := context.Background() + ts := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { + })) + defer ts.Close() + + ndBC := builtins.NDBCache{} + query := "data.foo.results = x" + _, err := New( + Query(query), + NDBuiltinCache(ndBC), + Module("test.rego", fmt.Sprintf(`package foo + +import rego.v1 + +urls := [ + "%[1]s/headers", + "%[1]s/ip", + "%[1]s/user-agent" +] + +results contains response if { + some url in urls + response := http.send({ + "method": "GET", + "url": url + }) +}`, ts.URL)), + ).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + // Ensure that the cache exists, and has exactly 3 entries. + entries, ok := ndBC["http.send"] + if !ok { + t.Fatalf("expected http.send cache entry") + } + if entries.Len() != 3 { + t.Fatalf("expected 3 http.send cache entries, received:\n%v", ndBC) + } +} + +// This test ensures that the NDBCache correctly serializes/deserializes. +func TestNDBCacheMarshalUnmarshalJSON(t *testing.T) { + original := builtins.NDBCache{} + + // Populate the cache for time.now_ns with an arbitrary timestamp. + original.Put("time.now_ns", ast.NewArray(), ast.Number(json.Number(strconv.FormatInt(1451311705000000000, 10)))) + jOriginal, err := json.Marshal(original) + if err != nil { + t.Fatal(err) + } + + var other builtins.NDBCache + err = json.Unmarshal(jOriginal, &other) + if err != nil { + t.Fatal(err) + } + + jOther, err := json.Marshal(other) + if err != nil { + t.Fatal(err) + } + + // Check that the two NDBCache value's JSONified forms match exactly. + if !bytes.Equal(jOriginal, jOther) { + t.Fatalf("JSONified values of NDBCaches do not match; expected %s, got %s", string(jOriginal), string(jOther)) + } +} + +func TestStrictBuiltinErrors(t *testing.T) { + _, err := New(Query("1/0"), StrictBuiltinErrors(true)).Eval(context.Background()) + if err == nil { + t.Fatal("expected error") + } + topdownErr, ok := err.(*topdown.Error) + if !ok { + t.Fatal("expected topdown error but got:", err) + } + + if topdownErr.Code != topdown.BuiltinErr { + t.Fatal("expected builtin error code but got:", topdownErr.Code) + } + + if topdownErr.Message != "div: divide by zero" { + t.Fatal("expected divide by zero error but got:", topdownErr.Message) + } +} + +func TestBuiltinErrorList(t *testing.T) { + var buf []topdown.Error + + _, err := New(Query("1/0"), BuiltinErrorList(&buf)).Eval(context.Background()) + if err != nil { + t.Fatal("unexpected error") + } + + if len(buf) != 1 { + t.Fatal("expected 1 error in buffer") + } + + if buf[0].Error() != "1/0: eval_builtin_error: div: divide by zero" { + t.Fatal("expected divide by zero error but got:", buf[0].Error()) + } +} + +func TestTimeSeedingOptions(t *testing.T) { + + ctx := context.Background() + clock := time.Now() + + // Check expected time is returned. + rs, err := New(Query("time.now_ns(x)"), Time(clock)).Eval(ctx) + if err != nil { + t.Fatal(err) + } else if len(rs) != 1 || !reflect.DeepEqual(rs[0].Bindings["x"], int64ToJSONNumber(clock.UnixNano())) { + t.Fatal("unexpected wall clock value") + } + + // Check that time is not propagated to prepared query. + eval, err := New(Query("time.now_ns(x)"), Time(clock)).PrepareForEval(ctx) + if err != nil { + t.Fatal(err) + } + + rs2, err := eval.Eval(ctx) + if err != nil { + t.Fatal(err) + } else if len(rs2) != 1 || reflect.DeepEqual(rs[0].Bindings["x"], rs2[0].Bindings["x"]) { + t.Fatal("expected new wall clock value") + } + + // Check that prepared query returns provided time. + rs3, err := eval.Eval(ctx, EvalTime(clock)) + if err != nil { + t.Fatal(err) + } else if len(rs2) != 1 || !reflect.DeepEqual(rs[0].Bindings["x"], rs3[0].Bindings["x"]) { + t.Fatal("expected old wall clock value") + } + +} + +func int64ToJSONNumber(i int64) json.Number { + return json.Number(strconv.FormatInt(i, 10)) +} + +func TestPrepareAndCompileWithSchema(t *testing.T) { + module := ` + package test + x = input.y + ` + + schemaBytes := `{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://example.com/example.json", + "type": "object", + "title": "The root schema", + "description": "The root schema comprises the entire JSON document.", + "required": [], + "properties": { + "y": { + "$id": "#/properties/y", + "type": "integer", + "title": "The y schema", + "description": "An explanation about the purpose of this instance." + } + }, + "additionalProperties": false + }` + + var schema any + err := util.Unmarshal([]byte(schemaBytes), &schema) + if err != nil { + t.Fatal(err) + } + + schemaSet := ast.NewSchemaSet() + schemaSet.Put(ast.InputRootRef, schema) + + r := New( + Query("data.test.x"), + Module("", module), + Package("foo"), + Schemas(schemaSet), + ) + + ctx := context.Background() + + pq, err := r.PrepareForEval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput(map[string]int{"y": 1}), + }, "[[1]]") + + // Ensure that Compile still works after Prepare + // and its Eval has been called. + _, err = r.Compile(ctx) + if err != nil { + t.Errorf("Unexpected error when compiling: %s", err.Error()) + } +} + +func TestPrepareAndCompileWithRegoV1(t *testing.T) { + module := `package test +x contains v if { + v := input.y +}` + + r := New( + Query("data.test.x"), + Module("", module), + SetRegoVersion(ast.RegoV1), + ) + + ctx := context.Background() + + pq, err := r.PrepareForEval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } + + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput(map[string]int{"y": 1}), + }, "[[[1]]]") + + _, err = r.Compile(ctx) + if err != nil { + t.Errorf("Unexpected error when compiling: %s", err.Error()) + } +} + +func TestGenerateJSON(t *testing.T) { + r := New( + Query("input"), + Input("original-input"), + GenerateJSON(func(*ast.Term, *EvalContext) (any, error) { + return "converted-input", nil + }), + ) + assertEval(t, r, `[["converted-input"]]`) +} + +func TestRegoLazyObjDefault(t *testing.T) { + foo := map[string]any{"foo": "bar", "other": 1} + store := inmem.NewFromObjectWithOpts(map[string]any{ + "stored": foo, + }) + r := New( + Query("x = data.stored"), + Store(store), + ) + + ctx := context.Background() + rs, err := r.Eval(ctx) + if err != nil { + t.Fatal(err) + } + act, ok := rs[0].Bindings["x"] + if !ok { + t.Fatalf("expected binding for \"x\", got %v", rs[0].Bindings) + } + m, ok := act.(map[string]any) + if !ok { + t.Fatalf("expected %T, got %T: %[2]v", m, act) + } + m["fox"] = true + + if _, ok := foo["fox"]; ok { + t.Errorf("expected no change in foo, found one: %v", foo) + } +} + +func TestRegoLazyObjNoRoundTripOnWrite(t *testing.T) { + foo := map[string]any{"foo": "bar", "other": 1} + store := inmem.NewFromObjectWithOpts(map[string]any{ + "stored": foo, + }, inmem.OptRoundTripOnWrite(false)) + r := New( + Query("x = data.stored"), + Store(store), + ) + + ctx := context.Background() + rs, err := r.Eval(ctx) + if err != nil { + t.Fatal(err) + } + act, ok := rs[0].Bindings["x"] + if !ok { + t.Fatalf("expected binding for \"x\", got %v", rs[0].Bindings) + } + m, ok := act.(map[string]any) + if !ok { + t.Fatalf("expected %T, got %T: %[2]v", m, act) + } + m["fox"] = true + + if v, ok := foo["fox"]; !ok || !v.(bool) { + t.Errorf("expected change in foo, found none: %v", foo) + } +} + +func TestRegoLazyObjCopyMaps(t *testing.T) { + foo := map[string]any{"foo": "bar", "other": 1} + store := inmem.NewFromObjectWithOpts(map[string]any{ + "stored": foo, + }, inmem.OptRoundTripOnWrite(false)) + r := New( + Query("x = data.stored"), + Store(store), + ) + + ctx := context.Background() + pq, err := r.PrepareForEval(ctx) + if err != nil { + t.Fatal(err) + } + rs, err := pq.Eval(ctx, EvalCopyMaps(true)) + if err != nil { + t.Fatal(err) + } + act, ok := rs[0].Bindings["x"] + if !ok { + t.Fatalf("expected binding for \"x\", got %v", rs[0].Bindings) + } + m, ok := act.(map[string]any) + if !ok { + t.Fatalf("expected %T, got %T: %[2]v", m, act) + } + m["fox"] = true + + if _, ok := foo["fox"]; ok { + t.Errorf("expected no change in foo, found one: %v", foo) + } +} + +func TestDescriptionRegisterBuiltin1(t *testing.T) { + description := "custom-arity-1" + + decl := &Function{ + Name: "foo", + Description: description, + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), + } + + RegisterBuiltin1(decl, func(_ BuiltinContext, _ *ast.Term) (*ast.Term, error) { + return ast.StringTerm("bar"), nil + }) + defer unregisterBuiltin("foo") + + got := ast.Builtins[len(ast.Builtins)-1].Description + if got != description { + t.Fatalf("expected %q, got %q", description, got) + } +} + +func TestDescriptionRegisterBuiltin2(t *testing.T) { + description := "custom-arity-2" + + decl := &Function{ + Name: "foo", + Description: description, + Decl: types.NewFunction( + types.Args(types.S, types.S), + types.S, + ), + } + + RegisterBuiltin2(decl, func(_ BuiltinContext, _, _ *ast.Term) (*ast.Term, error) { + return ast.StringTerm("bar"), nil + }) + defer unregisterBuiltin("foo") + + got := ast.Builtins[len(ast.Builtins)-1].Description + if got != description { + t.Fatalf("expected %q, got %q", description, got) + } +} + +func TestDescriptionRegisterBuiltin3(t *testing.T) { + description := "custom-arity-3" + + decl := &Function{ + Name: "foo", + Description: description, + Decl: types.NewFunction( + types.Args(types.S, types.S, types.S), + types.S, + ), + } + + RegisterBuiltin3(decl, func(_ BuiltinContext, _, _, _ *ast.Term) (*ast.Term, error) { + return ast.StringTerm("bar"), nil + }) + defer unregisterBuiltin("foo") + + got := ast.Builtins[len(ast.Builtins)-1].Description + if got != description { + t.Fatalf("expected %q, got %q", description, got) + } +} + +func TestDescriptionRegisterBuiltin4(t *testing.T) { + description := "custom-arity-4" + + decl := &Function{ + Name: "foo", + Description: description, + Decl: types.NewFunction( + types.Args(types.S, types.S, types.S, types.S), + types.S, + ), + } + + RegisterBuiltin4(decl, func(_ BuiltinContext, _, _, _, _ *ast.Term) (*ast.Term, error) { + return ast.StringTerm("bar"), nil + }) + defer unregisterBuiltin("foo") + + got := ast.Builtins[len(ast.Builtins)-1].Description + if got != description { + t.Fatalf("expected %q, got %q", description, got) + } +} + +func TestDescriptionRegisterBuiltinDyn(t *testing.T) { + description := "custom-arity-dyn" + + decl := &Function{ + Name: "foo", + Description: description, + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), + } + + RegisterBuiltinDyn(decl, func(BuiltinContext, []*ast.Term) (*ast.Term, error) { + return ast.StringTerm("bar"), nil + }) + defer unregisterBuiltin("foo") + + got := ast.Builtins[len(ast.Builtins)-1].Description + if got != description { + t.Fatalf("expected %q, got %q", description, got) + } +} + +// unregisterBuiltin removes the builtin of the given name from ast.Builtins. This assists in +// cleaning up custom functions added as part of certain test cases. +func unregisterBuiltin(name string) { + ast.Builtins = slices.DeleteFunc(ast.Builtins, func(b *ast.Builtin) bool { return b.Name == name }) +} + +func TestCompilerContextViaRegoModuleBuiltin(t *testing.T) { + moduleSource := `package test + +result := test.module("policy.rego") +` + + t.Run("compiler not passed", func(t *testing.T) { + ctx := context.Background() + r := New( + Query("data.test.result"), + CompilerHook(func(c *ast.Compiler) { ctx = ast.WithCompiler(ctx, c) }), + Module("policy.rego", moduleSource), + Function1(&Function{ + Name: "test.module", + Decl: types.NewFunction(types.Args(types.S), types.S), + }, func(bctx BuiltinContext, a *ast.Term) (*ast.Term, error) { + moduleName, ok := a.Value.(ast.String) + if !ok { + return nil, fmt.Errorf("bad arg type: %T", a.Value) + } + + comp, ok := ast.CompilerFromContext(bctx.Context) + if !ok { + return nil, errors.New("no compiler on context") + } + + return ast.StringTerm(comp.Modules[string(moduleName)].String()), nil + }), + ) + + rs, err := r.Eval(ctx) + if err != nil { + t.Fatalf("rego Eval error: %v", err) + } + if len(rs) == 0 || len(rs[0].Expressions) == 0 { + t.Fatalf("No results") + } + got := rs[0].Expressions[0].Value + want := "package test\n\nresult := __local0__ if { true; test.module(\"policy.rego\", __local0__) }" + if got != want { + t.Errorf("Expected %q, got %q", want, got) + } + }) + + t.Run("compiler passed in", func(t *testing.T) { // when the compiler is passed, no hook is run + ctx := context.Background() + r := New( + Compiler(ast.NewCompiler()), + Query("data.test.result"), + CompilerHook(func(*ast.Compiler) { t.Fatal("unexpected hook call") }), + Module("policy.rego", "package test\nresult:=true"), + ) + rs, err := r.Eval(ctx) + if err != nil { + t.Fatalf("rego Eval error: %v", err) + } + if act, exp := rs.Allowed(), true; exp != act { + t.Errorf("expected %v, got %v", exp, act) + } + }) +} diff --git a/third_party/opa/v1/rego/rego_wasmtarget_test.go b/third_party/opa/v1/rego/rego_wasmtarget_test.go new file mode 100644 index 000000000000..bb6e090b789c --- /dev/null +++ b/third_party/opa/v1/rego/rego_wasmtarget_test.go @@ -0,0 +1,450 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build opa_wasm +// +build opa_wasm + +package rego + +import ( + "context" + "fmt" + "math/rand" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/util" + + "github.com/fortytw2/leaktest" + + sdk_errors "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/errors" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/util/test" + + _ "github.com/open-policy-agent/opa/v1/features/wasm" +) + +func TestPrepareAndEvalWithWasmTarget(t *testing.T) { + t.Parallel() + + mod := ` + package test + default p = false + p if { + input.x == 1 + } + ` + + ctx := context.Background() + + pq, err := New( + Query("data.test.p = x"), + Target("wasm"), + Module("a.rego", mod), + ).PrepareForEval(ctx) + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput(map[string]int{"x": 1}), + }, "[[true]]") + + pq, err = New( + Query("a = [1,2]; x = a[i]"), + Target("wasm"), + ).PrepareForEval(ctx) + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + assertPreparedEvalQueryEval(t, pq, []EvalOption{}, "[[true, true],[true, true]]") + + pq, err = New( + Query("foo(100)"), + Target("wasm"), + ).PrepareForEval(ctx) + + if err == nil { + t.Fatal("Expected error") + } +} + +func TestPrepareAndEvalWithWasmTargetModulesOnCompiler(t *testing.T) { + t.Parallel() + + mod := ` + package test + default p = false + p if { + input.x == data.x.p + } + ` + + compiler := ast.NewCompiler() + + compiler.Compile(map[string]*ast.Module{ + "a.rego": ast.MustParseModule(mod), + }) + + if len(compiler.Errors) > 0 { + t.Fatalf("Unexpected compile errors: %s", compiler.Errors) + } + + ctx := context.Background() + + pq, err := New( + Compiler(compiler), + Query("data.test.p"), + Target("wasm"), + Store(inmem.NewFromObject(map[string]any{ + "x": map[string]any{"p": 1}, + })), + ).PrepareForEval(ctx) + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalInput(map[string]int{"x": 1}), + }, "[[true]]") +} + +func TestWasmTimeOfDay(t *testing.T) { + t.Parallel() + + ctx := context.Background() + pq, err := New(Query("time.now_ns()"), Target("wasm")).PrepareForEval(ctx) + if err != nil { + t.Fatal(err) + } + + now := time.Unix(1615397269, 0) + + assertPreparedEvalQueryEval(t, pq, []EvalOption{ + EvalTime(now), + }, "[[1615397269000000000]]") +} + +func TestEvalWithContextTimeout(t *testing.T) { + t.Parallel() + test.Skip(t) + + ts := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { + timer, cancel := util.TimerWithCancel(5 * time.Second) + select { + case <-r.Context().Done(): + // Without this, our test execution would hang waiting for this server to have + // served all requests to the end -- unrelated to the behaviour in the client, + // so the test would still pass. + cancel() + return + case <-timer.C: + return + } + })) + defer ts.Close() + + // This is host function, i.e. it's not implemented natively in wasm, + // but calls the topdown function from the wasm instance's execution. + // Also, it uses the topdown.Cancel mechanism for cancellation. + cidrExpand := `package p +allow if { + net.cidr_expand("1.0.0.0/1") +}` + + // Also a host function, but uses context.Context for cancellation. + httpSend := fmt.Sprintf(`package p +allow if { + http.send({"method": "get", "url": "%s", "raise_error": true}) +}`, + ts.URL) + + // This is a natively-implemented (for the wasm target) function that + // takes long. + numbersRange := `package p +allow if { + numbers.range(1, 1e8)[_] == 1e8 +}` + + for _, tc := range []struct { + note, target, policy string + errorCheck func(error) bool + }{ + { + note: "net.cidr_expand", + target: "rego", + policy: cidrExpand, + errorCheck: topdown.IsCancel, + }, + { + note: "http.send", + target: "rego", + policy: httpSend, + errorCheck: topdown.IsCancel, + }, + { + note: "numbers.range", + target: "rego", + policy: numbersRange, + errorCheck: topdown.IsCancel, + }, + { + note: "net.cidr_expand", + target: "wasm", + policy: cidrExpand, + errorCheck: sdk_errors.IsCancel, + }, + { + note: "http.send", + target: "wasm", + policy: httpSend, + errorCheck: sdk_errors.IsCancel, + }, + { + note: "numbers.range", + target: "wasm", + policy: numbersRange, + errorCheck: sdk_errors.IsCancel, + }, + } { + t.Run(tc.target+"/"+tc.note, func(t *testing.T) { + defer leaktest.Check(t)() + before := time.Now() + ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) + defer cancel() + + pq, err := New( + Query("data.p.allow"), + Module("p.rego", tc.policy), + Target(tc.target), + StrictBuiltinErrors(true), // ignored for wasm target (always non-strict) + ).PrepareForEval(ctx) + if err != nil { + t.Fatal(err) + } + + _, err = pq.Eval(ctx) + if testing.Verbose() { + t.Log(err) + } + if !tc.errorCheck(err) { + t.Errorf("failed checking error, got %[1]v (%[1]T)", err) + } + if time.Since(before) > 2*time.Second { + // if the cancelled execution took so long, it wasn't really cancelled + t.Errorf("expected cancellation, but test ran %s", time.Since(before)) + } + }) + } +} + +func TestRandSeedingOptions(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + exp := "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8" + + for _, tgt := range []string{targetWasm, targetRego} { + tgt := tgt // copy for capturing loop variable (not needed in Go 1.22+) + t.Run(tgt, func(t *testing.T) { + t.Parallel() + + seed := rand.New(rand.NewSource(0)) + + // Check expected uuid is returned. + rs, err := New(Query(`uuid.rfc4122("", x)`), Seed(seed), Target(tgt)).Eval(ctx) + if err != nil { + t.Fatal(err) + } else if rs[0].Bindings["x"] != exp { + t.Fatalf("expected %q but got %q", exp, rs[0].Bindings["x"]) + } + + // Check that seed does not propagate to prepared query. + eval, err := New(Query(`uuid.rfc4122("", x)`), Seed(seed)).PrepareForEval(ctx) + if err != nil { + t.Fatal(err) + } + + rs2, err := eval.Eval(ctx) + if err != nil { + t.Fatal(err) + } else if rs2[0].Bindings["x"] == exp { + t.Fatal("expected new uuid") + } + + exp3 := "6e4ff95f-f662-45ee-a82a-bdf44a2d0b75" + + // Check that prepared query uses explicitly provided seed. + rs3, err := eval.Eval(ctx, EvalSeed(seed)) + if err != nil { + t.Fatal(err) + } else if rs3[0].Bindings["x"] != exp3 { + t.Fatalf("expected %q but got %q", exp, rs3[0].Bindings["x"]) + } + }) + } +} + +func TestCompatWithABIMinorVersion1(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + pq, err := New( + LoadBundle("testdata/bundle.tar.gz"), + Query("data.test.allow"), + ).PrepareForEval(ctx) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + rs, err := pq.Eval(ctx, EvalInput(map[string]any{"x": "x"})) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + assertResultSet(t, rs, `[[true]]`) +} + +func TestEvalWasmWithInterQueryCache(t *testing.T) { + t.Parallel() + + newHeaders := map[string][]string{"Cache-Control": {"max-age=290304000, public"}} + + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + headers := w.Header() + + for k, v := range newHeaders { + headers[k] = v + } + + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"x": 1}`)) + })) + defer ts.Close() + + query := fmt.Sprintf(`http.send({"method": "get", "url": "%s", "force_json_decode": true, "cache": true})`, ts.URL) + + // add an inter-query cache + config, _ := cache.ParseCachingConfig(nil) + interQueryCache := cache.NewInterQueryCache(config) + + ctx := context.Background() + _, err := New(Target("wasm"), Query(query), InterQueryBuiltinCache(interQueryCache)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + // eval again with same query + // this request should be served by the cache + _, err = New(Target("wasm"), Query(query), InterQueryBuiltinCache(interQueryCache)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + if len(requests) != 1 { + t.Fatal("Expected server to be called only once") + } +} + +func TestEvalWasmWithHTTPAllowNet(t *testing.T) { + t.Parallel() + + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"x": 1}`)) + })) + defer ts.Close() + + serverUrl, err := url.Parse(ts.URL) + if err != nil { + t.Fatal(err) + } + serverHost := strings.Split(serverUrl.Host, ":")[0] + + query := fmt.Sprintf(`http.send({"method": "get", "url": "%s", "force_json_decode": true, "cache": true})`, ts.URL) + capabilities := ast.CapabilitiesForThisVersion() + capabilities.AllowNet = []string{"example.com"} + + // add an inter-query cache + config, _ := cache.ParseCachingConfig(nil) + interQueryCache := cache.NewInterQueryCache(config) + + ctx := context.Background() + // StrictBuiltinErrors(true) has no effect when target is 'wasm' + // this request should be rejected by the allow_net allowlist + _, err = New(Target("wasm"), Query(query), InterQueryBuiltinCache(interQueryCache), Capabilities(capabilities)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + if len(requests) != 0 { + t.Fatal("Expected server to not be called") + } + + capabilities.AllowNet = []string{serverHost} + + // eval again with same query + // this request should not be rejected by the allow_net allowlist + _, err = New(Target("wasm"), Query(query), InterQueryBuiltinCache(interQueryCache), Capabilities(capabilities)).Eval(ctx) + if err != nil { + t.Fatal(err) + } + + if len(requests) != 1 { + t.Fatal("Expected server to never be called") + } +} + +// Warning(philipc): This test modifies package variables, which means it cannot +// be run safely in parallel with other tests. +func TestRegoTargetWasmAndTargetPluginDisablesIndexingTopdownStages(t *testing.T) { + tp := testPlugin{} + RegisterPlugin("rego.target.foo", &tp) + t.Cleanup(resetPlugins) + + for _, tgt := range []string{"wasm", "foo"} { + t.Run(tgt, func(t *testing.T) { + m := metrics.New() + r := New(Query("foo = 1"), Module("foo.rego", "package x"), Metrics(m), Instrument(true), Target(tgt)) + ctx := context.Background() + _, err := r.Eval(ctx) + if err != nil { + t.Fatal(err) + } + + expAbsent := []string{ + "timer_query_compile_stage_build_comprehension_index_ns", + "timer_compile_stage_rebuild_comprehension_indices_ns", + "timer_compile_stage_rebuild_indices_ns", + } + + all := m.All() + + for _, name := range expAbsent { + if _, ok := all[name]; ok { + t.Errorf("Expected NOT to find %v but did", name) + } + } + }) + } +} diff --git a/third_party/opa/v1/rego/resultset.go b/third_party/opa/v1/rego/resultset.go new file mode 100644 index 000000000000..983de2223e79 --- /dev/null +++ b/third_party/opa/v1/rego/resultset.go @@ -0,0 +1,90 @@ +package rego + +import ( + "fmt" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// ResultSet represents a collection of output from Rego evaluation. An empty +// result set represents an undefined query. +type ResultSet []Result + +// Vars represents a collection of variable bindings. The keys are the variable +// names and the values are the binding values. +type Vars map[string]any + +// WithoutWildcards returns a copy of v with wildcard variables removed. +func (v Vars) WithoutWildcards() Vars { + n := Vars{} + for k, v := range v { + if ast.Var(k).IsWildcard() || ast.Var(k).IsGenerated() { + continue + } + n[k] = v + } + return n +} + +// Result defines the output of Rego evaluation. +type Result struct { + Expressions []*ExpressionValue `json:"expressions"` + Bindings Vars `json:"bindings,omitempty"` +} + +func newResult() Result { + return Result{ + Bindings: Vars{}, + } +} + +// Location defines a position in a Rego query or module. +type Location struct { + Row int `json:"row"` + Col int `json:"col"` +} + +// ExpressionValue defines the value of an expression in a Rego query. +type ExpressionValue struct { + Value any `json:"value"` + Text string `json:"text"` + Location *Location `json:"location"` +} + +func newExpressionValue(expr *ast.Expr, value any) *ExpressionValue { + result := &ExpressionValue{ + Value: value, + } + if expr.Location != nil { + result.Text = string(expr.Location.Text) + result.Location = &Location{ + Row: expr.Location.Row, + Col: expr.Location.Col, + } + } + return result +} + +func (ev *ExpressionValue) String() string { + return fmt.Sprint(ev.Value) +} + +// Allowed is a helper method that'll return true if all of these conditions hold: +// - the result set only has one element +// - there is only one expression in the result set's only element +// - that expression has the value `true` +// - there are no bindings. +// +// If bindings are present, this will yield `false`: it would be a pitfall to +// return `true` for a query like `data.authz.allow = x`, which always has result +// set element with value true, but could also have a binding `x: false`. +func (rs ResultSet) Allowed() bool { + if len(rs) == 1 && len(rs[0].Bindings) == 0 { + if exprs := rs[0].Expressions; len(exprs) == 1 { + if b, ok := exprs[0].Value.(bool); ok { + return b + } + } + } + return false +} diff --git a/third_party/opa/v1/rego/resultset_test.go b/third_party/opa/v1/rego/resultset_test.go new file mode 100644 index 000000000000..371629485a85 --- /dev/null +++ b/third_party/opa/v1/rego/resultset_test.go @@ -0,0 +1,78 @@ +package rego_test + +import ( + "context" + "testing" + + "github.com/open-policy-agent/opa/v1/rego" +) + +func TestResultSetAllowed(t *testing.T) { + tests := []struct { + note string + module string + query string + expected bool + }{ + { + note: "simplest true", + module: `package authz +import rego.v1 +allow if { true } +`, + query: "data.authz.allow", + expected: true, + }, + { + note: "simplest false", + module: `package authz +default allow = false +`, + query: "data.authz.allow", + expected: false, + }, + { + note: "true value + bindings", + module: `package authz +import rego.v1 +allow if { true } +`, + query: "data.authz.allow = x", + expected: false, + }, + { + note: "object response, bound to var in query", + module: `package authz +import rego.v1 +resp = { "allow": true } if { true } +`, + query: "data.authz.resp = x", + expected: false, + }, + { + note: "object response, treated as false", + module: `package authz +import rego.v1 +resp = { "allow": true } if { true } +`, + query: "data.authz.resp", + expected: false, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + r := rego.New( + rego.Query(tc.query), + rego.Module("", tc.module), + ) + rs, err := r.Eval(context.Background()) + if err != nil { + t.Fatal(err) + } + if exp, act := tc.expected, rs.Allowed(); exp != act { + t.Errorf("expected %v, got %v", exp, act) + } + }) + } +} diff --git a/third_party/opa/v1/rego/testdata/aci/api.rego b/third_party/opa/v1/rego/testdata/aci/api.rego new file mode 100644 index 000000000000..fa3014448aae --- /dev/null +++ b/third_party/opa/v1/rego/testdata/aci/api.rego @@ -0,0 +1,26 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +package api + +version := "0.10.0" + +enforcement_points := { + "mount_device": {"introducedVersion": "0.1.0", "default_results": {"allowed": false}}, + "mount_overlay": {"introducedVersion": "0.1.0", "default_results": {"allowed": false}}, + "create_container": {"introducedVersion": "0.1.0", "default_results": {"allowed": false, "env_list": null, "allow_stdio_access": false}}, + "unmount_device": {"introducedVersion": "0.2.0", "default_results": {"allowed": true}}, + "unmount_overlay": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}}, + "exec_in_container": {"introducedVersion": "0.2.0", "default_results": {"allowed": true, "env_list": null}}, + "exec_external": {"introducedVersion": "0.3.0", "default_results": {"allowed": true, "env_list": null, "allow_stdio_access": false}}, + "shutdown_container": {"introducedVersion": "0.4.0", "default_results": {"allowed": true}}, + "signal_container_process": {"introducedVersion": "0.5.0", "default_results": {"allowed": true}}, + "plan9_mount": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}}, + "plan9_unmount": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}}, + "get_properties": {"introducedVersion": "0.7.0", "default_results": {"allowed": true}}, + "dump_stacks": {"introducedVersion": "0.7.0", "default_results": {"allowed": true}}, + "runtime_logging": {"introducedVersion": "0.8.0", "default_results": {"allowed": true}}, + "load_fragment": {"introducedVersion": "0.9.0", "default_results": {"allowed": false, "add_module": false}}, + "scratch_mount": {"introducedVersion": "0.10.0", "default_results": {"allowed": true}}, + "scratch_unmount": {"introducedVersion": "0.10.0", "default_results": {"allowed": true}}, +} diff --git a/third_party/opa/v1/rego/testdata/aci/data.json b/third_party/opa/v1/rego/testdata/aci/data.json new file mode 100644 index 000000000000..2b4b9f4c4a11 --- /dev/null +++ b/third_party/opa/v1/rego/testdata/aci/data.json @@ -0,0 +1,12 @@ +{ + "metadata": { + "devices": { + "/run/layers/p0-layer0": "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766", + "/run/layers/p0-layer1": "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c", + "/run/layers/p0-layer2": "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79", + "/run/layers/p0-layer3": "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156", + "/run/layers/p0-layer4": "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c", + "/run/layers/p0-layer5": "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a" + } + } +} diff --git a/third_party/opa/v1/rego/testdata/aci/framework.rego b/third_party/opa/v1/rego/testdata/aci/framework.rego new file mode 100644 index 000000000000..0a7f65b667b9 --- /dev/null +++ b/third_party/opa/v1/rego/testdata/aci/framework.rego @@ -0,0 +1,1831 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +package framework + +import future.keywords.every +import future.keywords.in + +version := "0.3.0" + +device_mounted(target) { + data.metadata.devices[target] +} + +default deviceHash_ok := false + +# test if a device hash exists as a layer in a policy container +deviceHash_ok { + layer := data.policy.containers[_].layers[_] + input.deviceHash == layer +} + +# test if a device hash exists as a layer in a fragment container +deviceHash_ok { + feed := data.metadata.issuers[_].feeds[_] + some fragment in feed + layer := fragment.containers[_].layers[_] + input.deviceHash == layer +} + +default mount_device := {"allowed": false} + +mount_device := {"metadata": [addDevice], "allowed": true} { + not device_mounted(input.target) + deviceHash_ok + addDevice := { + "name": "devices", + "action": "add", + "key": input.target, + "value": input.deviceHash, + } +} + +default unmount_device := {"allowed": false} + +unmount_device := {"metadata": [removeDevice], "allowed": true} { + device_mounted(input.unmountTarget) + removeDevice := { + "name": "devices", + "action": "remove", + "key": input.unmountTarget, + } +} + +layerPaths_ok(layers) { + length := count(layers) + count(input.layerPaths) == length + every i, path in input.layerPaths { + layers[(length - i) - 1] == data.metadata.devices[path] + } +} + +default overlay_exists := false + +overlay_exists { + data.metadata.matches[input.containerID] +} + +overlay_mounted(target) { + data.metadata.overlayTargets[target] +} + +default candidate_containers := [] + +candidate_containers := containers { + semver.compare(policy_framework_version, version) == 0 + + policy_containers := [c | c := data.policy.containers[_]] + fragment_containers := [c | + feed := data.metadata.issuers[_].feeds[_] + fragment := feed[_] + c := fragment.containers[_] + ] + + containers := array.concat(policy_containers, fragment_containers) +} + +candidate_containers := containers { + semver.compare(policy_framework_version, version) < 0 + + policy_containers := apply_defaults("container", data.policy.containers, policy_framework_version) + fragment_containers := [c | + feed := data.metadata.issuers[_].feeds[_] + fragment := feed[_] + c := fragment.containers[_] + ] + + containers := array.concat(policy_containers, fragment_containers) +} + +default mount_overlay := {"allowed": false} + +mount_overlay := {"metadata": [addMatches, addOverlayTarget], "allowed": true} { + not overlay_exists + + containers := [container | + container := candidate_containers[_] + layerPaths_ok(container.layers) + ] + + count(containers) > 0 + addMatches := { + "name": "matches", + "action": "add", + "key": input.containerID, + "value": containers, + } + + addOverlayTarget := { + "name": "overlayTargets", + "action": "add", + "key": input.target, + "value": true, + } +} + +default unmount_overlay := {"allowed": false} + +unmount_overlay := {"metadata": [removeOverlayTarget], "allowed": true} { + overlay_mounted(input.unmountTarget) + removeOverlayTarget := { + "name": "overlayTargets", + "action": "remove", + "key": input.unmountTarget, + } +} + +command_ok(command) { + count(input.argList) == count(command) + every i, arg in input.argList { + command[i] == arg + } +} + +env_ok(pattern, "string", value) { + pattern == value +} + +env_ok(pattern, "re2", value) { + regex.match(pattern, value) +} + +rule_ok(rule, env) { + not rule.required +} + +rule_ok(rule, env) { + rule.required + env_ok(rule.pattern, rule.strategy, env) +} + +envList_ok(env_rules, envList) { + every rule in env_rules { + some env in envList + rule_ok(rule, env) + } + + every env in envList { + some rule in env_rules + env_ok(rule.pattern, rule.strategy, env) + } +} + +valid_envs_subset(env_rules) := envs { + envs := {env | + some env in input.envList + some rule in env_rules + env_ok(rule.pattern, rule.strategy, env) + } +} + +valid_envs_for_all(items) := envs { + allow_environment_variable_dropping + + # for each item, find a subset of the environment rules + # that are valid + valid := [envs | + some item in items + envs := valid_envs_subset(item.env_rules) + ] + + # we want to select the most specific matches, which in this + # case consists of those matches which require dropping the + # fewest environment variables (i.e. the longest lists) + counts := [num_envs | + envs := valid[_] + num_envs := count(envs) + ] + max_count := max(counts) + + largest_env_sets := {envs | + some i + counts[i] == max_count + envs := valid[i] + } + + # if there is more than one set with the same size, we + # can only proceed if they are all the same, so we verify + # that the intersection is equal to the union. For a single + # set this is trivially true. + envs_i := intersection(largest_env_sets) + envs_u := union(largest_env_sets) + envs_i == envs_u + envs := envs_i +} + +valid_envs_for_all(items) := envs { + not allow_environment_variable_dropping + + # no dropping allowed, so we just return the input + envs := input.envList +} + +workingDirectory_ok(working_dir) { + input.workingDir == working_dir +} + +privileged_ok(elevation_allowed) { + not input.privileged +} + +privileged_ok(elevation_allowed) { + input.privileged + input.privileged == elevation_allowed +} + +noNewPrivileges_ok(no_new_privileges) { + no_new_privileges + input.noNewPrivileges +} + +noNewPrivileges_ok(no_new_privileges) { + no_new_privileges == false +} + +idName_ok(pattern, "any", value) { + true +} + +idName_ok(pattern, "id", value) { + pattern == value.id +} + +idName_ok(pattern, "name", value) { + pattern == value.name +} + +idName_ok(pattern, "re2", value) { + regex.match(pattern, value.name) +} + +user_ok(user) { + user.umask == input.umask + idName_ok(user.user_idname.pattern, user.user_idname.strategy, input.user) + every group in input.groups { + some group_idname in user.group_idnames + idName_ok(group_idname.pattern, group_idname.strategy, group) + } +} + +seccomp_ok(seccomp_profile_sha256) { + input.seccompProfileSHA256 == seccomp_profile_sha256 +} + +default container_started := false + +container_started { + data.metadata.started[input.containerID] +} + +default container_privileged := false + +container_privileged { + data.metadata.started[input.containerID].privileged +} + +capsList_ok(allowed_caps_list, requested_caps_list) { + count(allowed_caps_list) == count(requested_caps_list) + + every cap in requested_caps_list { + some allowed in allowed_caps_list + cap == allowed + } + + every allowed in allowed_caps_list { + some cap in requested_caps_list + allowed == cap + } +} + +filter_capsList_by_allowed(allowed_caps_list, requested_caps_list) := caps { + # find a subset of the capabilities that are valid + caps := {cap | + some cap in requested_caps_list + some allowed in allowed_caps_list + cap == allowed + } +} + +filter_capsList_for_single_container(allowed_caps) := caps { + bounding := filter_capsList_by_allowed(allowed_caps.bounding, input.capabilities.bounding) + effective := filter_capsList_by_allowed(allowed_caps.effective, input.capabilities.effective) + inheritable := filter_capsList_by_allowed(allowed_caps.inheritable, input.capabilities.inheritable) + permitted := filter_capsList_by_allowed(allowed_caps.permitted, input.capabilities.permitted) + ambient := filter_capsList_by_allowed(allowed_caps.ambient, input.capabilities.ambient) + + caps := { + "bounding": bounding, + "effective": effective, + "inheritable": inheritable, + "permitted": permitted, + "ambient": ambient + } +} + +largest_caps_sets_for_all(containers, privileged) := largest_caps_sets { + filtered := [caps | + container := containers[_] + capabilities := get_capabilities(container, privileged) + caps := filter_capsList_for_single_container(capabilities) + ] + + # we want to select the most specific matches, which in this + # case consists of those matches which require dropping the + # fewest capabilities (i.e. the longest lists) + counts := [num_caps | + caps := filtered[_] + num_caps := count(caps.bounding) + count(caps.effective) + + count(caps.inheritable) + count(caps.permitted) + + count(caps.ambient) + ] + max_count := max(counts) + + largest_caps_sets := [caps | + some i + counts[i] == max_count + caps := filtered[i] + ] +} + +all_caps_sets_are_equal(sets) := caps { + # if there is more than one set with the same size, we + # can only proceed if they are all the same, so we verify + # that the intersection is equal to the union. For a single + # set this is trivially true. + bounding_i := intersection({caps.bounding | caps := sets[_]}) + effective_i := intersection({caps.effective | caps := sets[_]}) + inheritable_i := intersection({caps.inheritable | caps := sets[_]}) + permitted_i := intersection({caps.permitted | caps := sets[_]}) + ambient_i := intersection({caps.ambient | caps := sets[_]}) + + bounding_u := union({caps.bounding | caps := sets[_]}) + effective_u := union({caps.effective | caps := sets[_]}) + inheritable_u := union({caps.inheritable | caps := sets[_]}) + permitted_u := union({caps.permitted | caps := sets[_]}) + ambient_u := union({caps.ambient | caps := sets[_]}) + + bounding_i == bounding_u + effective_i == effective_u + inheritable_i == inheritable_u + permitted_i == permitted_u + ambient_i == ambient_u + + caps := { + "bounding": bounding_i, + "effective": effective_i, + "inheritable": inheritable_i, + "permitted": permitted_i, + "ambient": ambient_i, + } +} + +valid_caps_for_all(containers, privileged) := caps { + allow_capability_dropping + + # find largest matching capabilities sets aka "the most specific" + largest_caps_sets := largest_caps_sets_for_all(containers, privileged) + + # if there is more than one set with the same size, we + # can only proceed if they are all the same + caps := all_caps_sets_are_equal(largest_caps_sets) +} + +valid_caps_for_all(containers, privileged) := caps { + not allow_capability_dropping + + # no dropping allowed, so we just return the input + caps := input.capabilities +} + +caps_ok(allowed_caps, requested_caps) { + capsList_ok(allowed_caps.bounding, requested_caps.bounding) + capsList_ok(allowed_caps.effective, requested_caps.effective) + capsList_ok(allowed_caps.inheritable, requested_caps.inheritable) + capsList_ok(allowed_caps.permitted, requested_caps.permitted) + capsList_ok(allowed_caps.ambient, requested_caps.ambient) +} + +get_capabilities(container, privileged) := capabilities { + container.capabilities != null + capabilities := container.capabilities +} + +default_privileged_capabilities := capabilities { + caps := {cap | cap := data.defaultPrivilegedCapabilities[_]} + capabilities := { + "bounding": caps, + "effective": caps, + "inheritable": caps, + "permitted": caps, + "ambient": set(), + } +} + +get_capabilities(container, true) := capabilities { + container.capabilities == null + container.allow_elevated + capabilities := default_privileged_capabilities +} + +default_unprivileged_capabilities := capabilities { + caps := {cap | cap := data.defaultUnprivilegedCapabilities[_]} + capabilities := { + "bounding": caps, + "effective": caps, + "inheritable": set(), + "permitted": caps, + "ambient": set(), + } +} + +get_capabilities(container, false) := capabilities { + container.capabilities == null + container.allow_elevated + capabilities := default_unprivileged_capabilities +} + +get_capabilities(container, privileged) := capabilities { + container.capabilities == null + not container.allow_elevated + capabilities := default_unprivileged_capabilities +} + +default create_container := {"allowed": false} + +create_container := {"metadata": [updateMatches, addStarted], + "env_list": env_list, + "caps_list": caps_list, + "allow_stdio_access": allow_stdio_access, + "allowed": true} { + not container_started + + # narrow the matches based upon command, working directory, and + # mount list + possible_after_initial_containers := [container | + container := data.metadata.matches[input.containerID][_] + # NB any change to these narrowing conditions should be reflected in + # the error handling, such that error messaging correctly reflects + # the narrowing process. + noNewPrivileges_ok(container.no_new_privileges) + user_ok(container.user) + privileged_ok(container.allow_elevated) + workingDirectory_ok(container.working_dir) + command_ok(container.command) + mountList_ok(container.mounts, container.allow_elevated) + seccomp_ok(container.seccomp_profile_sha256) + ] + + count(possible_after_initial_containers) > 0 + + # check to see if the environment variables match, dropping + # them if allowed (and necessary) + env_list := valid_envs_for_all(possible_after_initial_containers) + possible_after_env_containers := [container | + container := possible_after_initial_containers[_] + envList_ok(container.env_rules, env_list) + ] + + count(possible_after_env_containers) > 0 + + # check to see if the capabilities variables match, dropping + # them if allowed (and necessary) + caps_list := valid_caps_for_all(possible_after_env_containers, input.privileged) + possible_after_caps_containers := [container | + container := possible_after_env_containers[_] + caps_ok(get_capabilities(container, input.privileged), caps_list) + ] + + count(possible_after_caps_containers) > 0 + + # set final container list + containers := possible_after_caps_containers + + # we can't do narrowing based on allowing stdio access so at this point + # every container from the policy that might match this create request + # must have the same allow stdio value otherwise, we are in an undecidable + # state + allow_stdio_access := containers[0].allow_stdio_access + every c in containers { + c.allow_stdio_access == allow_stdio_access + } + + updateMatches := { + "name": "matches", + "action": "update", + "key": input.containerID, + "value": containers, + } + + addStarted := { + "name": "started", + "action": "add", + "key": input.containerID, + "value": { + "privileged": input.privileged, + }, + } +} + +mountSource_ok(constraint, source) { + startswith(constraint, data.sandboxPrefix) + newConstraint := replace(constraint, data.sandboxPrefix, input.sandboxDir) + regex.match(newConstraint, source) +} + +mountSource_ok(constraint, source) { + startswith(constraint, data.hugePagesPrefix) + newConstraint := replace(constraint, data.hugePagesPrefix, input.hugePagesDir) + regex.match(newConstraint, source) +} + +mountSource_ok(constraint, source) { + startswith(constraint, data.plan9Prefix) + some target, containerID in data.metadata.p9mounts + source == target + input.containerID == containerID +} + +mountSource_ok(constraint, source) { + constraint == source +} + +mountConstraint_ok(constraint, mount) { + mount.type == constraint.type + mountSource_ok(constraint.source, mount.source) + mount.destination != "" + mount.destination == constraint.destination + + # the following check is not required (as the following tests will prove this + # condition as well), however it will check whether those more expensive + # tests need to be performed. + count(mount.options) == count(constraint.options) + every option in mount.options { + some constraintOption in constraint.options + option == constraintOption + } + + every option in constraint.options { + some mountOption in mount.options + option == mountOption + } +} + +mount_ok(mounts, allow_elevated, mount) { + some constraint in mounts + mountConstraint_ok(constraint, mount) +} + +mount_ok(mounts, allow_elevated, mount) { + some constraint in data.defaultMounts + mountConstraint_ok(constraint, mount) +} + +mount_ok(mounts, allow_elevated, mount) { + allow_elevated + some constraint in data.privilegedMounts + mountConstraint_ok(constraint, mount) +} + +mountList_ok(mounts, allow_elevated) { + every mount in input.mounts { + mount_ok(mounts, allow_elevated, mount) + } +} + +default exec_in_container := {"allowed": false} + +exec_in_container := {"metadata": [updateMatches], + "env_list": env_list, + "caps_list": caps_list, + "allowed": true} { + container_started + + # narrow our matches based upon the process requested + possible_after_initial_containers := [container | + container := data.metadata.matches[input.containerID][_] + # NB any change to these narrowing conditions should be reflected in + # the error handling, such that error messaging correctly reflects + # the narrowing process. + workingDirectory_ok(container.working_dir) + noNewPrivileges_ok(container.no_new_privileges) + user_ok(container.user) + some process in container.exec_processes + command_ok(process.command) + ] + + count(possible_after_initial_containers) > 0 + + # check to see if the environment variables match, dropping + # them if allowed (and necessary) + env_list := valid_envs_for_all(possible_after_initial_containers) + possible_after_env_containers := [container | + container := possible_after_initial_containers[_] + envList_ok(container.env_rules, env_list) + ] + + count(possible_after_env_containers) > 0 + + # check to see if the capabilities variables match, dropping + # them if allowed (and necessary) + caps_list := valid_caps_for_all(possible_after_env_containers, container_privileged) + possible_after_caps_containers := [container | + container := possible_after_env_containers[_] + caps_ok(get_capabilities(container, container_privileged), caps_list) + ] + + count(possible_after_caps_containers) > 0 + + # set final container list + containers := possible_after_caps_containers + + updateMatches := { + "name": "matches", + "action": "update", + "key": input.containerID, + "value": containers, + } +} + +default shutdown_container := {"allowed": false} + +shutdown_container := {"metadata": [remove], "allowed": true} { + container_started + remove := { + "name": "matches", + "action": "remove", + "key": input.containerID, + } +} + +default signal_container_process := {"allowed": false} + +signal_container_process := {"metadata": [updateMatches], "allowed": true} { + container_started + input.isInitProcess + containers := [container | + container := data.metadata.matches[input.containerID][_] + signal_ok(container.signals) + ] + + count(containers) > 0 + updateMatches := { + "name": "matches", + "action": "update", + "key": input.containerID, + "value": containers, + } +} + +signal_container_process := {"metadata": [updateMatches], "allowed": true} { + container_started + not input.isInitProcess + containers := [container | + container := data.metadata.matches[input.containerID][_] + some process in container.exec_processes + command_ok(process.command) + signal_ok(process.signals) + ] + + count(containers) > 0 + updateMatches := { + "name": "matches", + "action": "update", + "key": input.containerID, + "value": containers, + } +} + +signal_ok(signals) { + some signal in signals + input.signal == signal +} + +plan9_mounted(target) { + data.metadata.p9mounts[target] +} + +default plan9_mount := {"allowed": false} + +plan9_mount := {"metadata": [addPlan9Target], "allowed": true} { + not plan9_mounted(input.target) + some containerID, _ in data.metadata.matches + pattern := concat("", [input.rootPrefix, "/", containerID, input.mountPathPrefix]) + regex.match(pattern, input.target) + addPlan9Target := { + "name": "p9mounts", + "action": "add", + "key": input.target, + "value": containerID, + } +} + +default plan9_unmount := {"allowed": false} + +plan9_unmount := {"metadata": [removePlan9Target], "allowed": true} { + plan9_mounted(input.unmountTarget) + removePlan9Target := { + "name": "p9mounts", + "action": "remove", + "key": input.unmountTarget, + } +} + + +default enforcement_point_info := {"available": false, "default_results": {"allow": false}, "unknown": true, "invalid": false, "version_missing": false} + +enforcement_point_info := {"available": false, "default_results": {"allow": false}, "unknown": false, "invalid": false, "version_missing": true} { + policy_api_version == null +} + +enforcement_point_info := {"available": available, "default_results": default_results, "unknown": false, "invalid": false, "version_missing": false} { + enforcement_point := data.api.enforcement_points[input.name] + semver.compare(data.api.version, enforcement_point.introducedVersion) >= 0 + available := semver.compare(policy_api_version, enforcement_point.introducedVersion) >= 0 + default_results := enforcement_point.default_results +} + +enforcement_point_info := {"available": false, "default_results": {"allow": false}, "unknown": false, "invalid": true, "version_missing": false} { + enforcement_point := data.api.enforcement_points[input.name] + semver.compare(data.api.version, enforcement_point.introducedVersion) < 0 +} + +default candidate_external_processes := [] + +candidate_external_processes := external_processes { + semver.compare(policy_framework_version, version) == 0 + + policy_external_processes := [e | e := data.policy.external_processes[_]] + fragment_external_processes := [e | + feed := data.metadata.issuers[_].feeds[_] + fragment := feed[_] + e := fragment.external_processes[_] + ] + + external_processes := array.concat(policy_external_processes, fragment_external_processes) +} + +candidate_external_processes := external_processes { + semver.compare(policy_framework_version, version) < 0 + + policy_external_processes := apply_defaults("external_process", data.policy.external_processes, policy_framework_version) + fragment_external_processes := [e | + feed := data.metadata.issuers[_].feeds[_] + fragment := feed[_] + e := fragment.external_processes[_] + ] + + external_processes := array.concat(policy_external_processes, fragment_external_processes) +} + +external_process_ok(process) { + command_ok(process.command) + envList_ok(process.env_rules, input.envList) + workingDirectory_ok(process.working_dir) +} + +default exec_external := {"allowed": false} + +exec_external := {"allowed": true, + "allow_stdio_access": allow_stdio_access, + "env_list": env_list} { + possible_processes := [process | + process := candidate_external_processes[_] + # NB any change to these narrowing conditions should be reflected in + # the error handling, such that error messaging correctly reflects + # the narrowing process. + workingDirectory_ok(process.working_dir) + command_ok(process.command) + ] + + count(possible_processes) > 0 + + # check to see if the environment variables match, dropping + # them if allowed (and necessary) + env_list := valid_envs_for_all(possible_processes) + processes := [process | + process := possible_processes[_] + envList_ok(process.env_rules, env_list) + ] + + count(processes) > 0 + + allow_stdio_access := processes[0].allow_stdio_access + every p in processes { + p.allow_stdio_access == allow_stdio_access + } +} + +default get_properties := {"allowed": false} + +get_properties := {"allowed": true} { + allow_properties_access +} + +default dump_stacks := {"allowed": false} + +dump_stacks := {"allowed": true} { + allow_dump_stacks +} + +default runtime_logging := {"allowed": false} + +runtime_logging := {"allowed": true} { + allow_runtime_logging +} + +default fragment_containers := [] + +fragment_containers := data[input.namespace].containers + +default fragment_fragments := [] + +fragment_fragments := data[input.namespace].fragments + +default fragment_external_processes := [] + +fragment_external_processes := data[input.namespace].external_processes + +apply_defaults(name, raw_values, framework_version) := values { + semver.compare(framework_version, version) == 0 + values := raw_values +} + +apply_defaults("container", raw_values, framework_version) := values { + semver.compare(framework_version, version) < 0 + values := [checked | + raw := raw_values[_] + checked := check_container(raw, framework_version) + ] +} + +apply_defaults("external_process", raw_values, framework_version) := values { + semver.compare(framework_version, version) < 0 + values := [checked | + raw := raw_values[_] + checked := check_external_process(raw, framework_version) + ] +} + +apply_defaults("fragment", raw_values, framework_version) := values { + semver.compare(framework_version, version) < 0 + values := [checked | + raw := raw_values[_] + checked := check_fragment(raw, framework_version) + ] +} + +default fragment_framework_version := null +fragment_framework_version := data[input.namespace].framework_version + +extract_fragment_includes(includes) := fragment { + framework_version := fragment_framework_version + objects := { + "containers": apply_defaults("container", fragment_containers, framework_version), + "fragments": apply_defaults("fragment", fragment_fragments, framework_version), + "external_processes": apply_defaults("external_process", fragment_external_processes, framework_version) + } + + fragment := { + include: objects[include] | include := includes[_] + } +} + +issuer_exists(iss) { + data.metadata.issuers[iss] +} + +feed_exists(iss, feed) { + data.metadata.issuers[iss].feeds[feed] +} + +update_issuer(includes) := issuer { + feed_exists(input.issuer, input.feed) + old_issuer := data.metadata.issuers[input.issuer] + old_fragments := old_issuer.feeds[input.feed] + new_issuer := {"feeds": {input.feed: array.concat([extract_fragment_includes(includes)], old_fragments)}} + + issuer := object.union(old_issuer, new_issuer) +} + +update_issuer(includes) := issuer { + not feed_exists(input.issuer, input.feed) + old_issuer := data.metadata.issuers[input.issuer] + new_issuer := {"feeds": {input.feed: [extract_fragment_includes(includes)]}} + + issuer := object.union(old_issuer, new_issuer) +} + +update_issuer(includes) := issuer { + not issuer_exists(input.issuer) + issuer := {"feeds": {input.feed: [extract_fragment_includes(includes)]}} +} + +default candidate_fragments := [] + +candidate_fragments := fragments { + semver.compare(policy_framework_version, version) == 0 + + policy_fragments := [f | f := data.policy.fragments[_]] + fragment_fragments := [f | + feed := data.metadata.issuers[_].feeds[_] + fragment := feed[_] + f := fragment.fragments[_] + ] + + fragments := array.concat(policy_fragments, fragment_fragments) +} + +candidate_fragments := fragments { + semver.compare(policy_framework_version, version) < 0 + + policy_fragments := apply_defaults("fragment", data.policy.fragments, policy_framework_version) + fragment_fragments := [f | + feed := data.metadata.issuers[_].feeds[_] + fragment := feed[_] + f := fragment.fragments[_] + ] + + fragments := array.concat(policy_fragments, fragment_fragments) +} + +default load_fragment := {"allowed": false} + +svn_ok(svn, minimum_svn) { + # deprecated + semver.is_valid(svn) + semver.is_valid(minimum_svn) + semver.compare(svn, minimum_svn) >= 0 +} + +svn_ok(svn, minimum_svn) { + to_number(svn) >= to_number(minimum_svn) +} + +fragment_ok(fragment) { + input.issuer == fragment.issuer + input.feed == fragment.feed + svn_ok(data[input.namespace].svn, fragment.minimum_svn) +} + +load_fragment := {"metadata": [updateIssuer], "add_module": add_module, "allowed": true} { + some fragment in candidate_fragments + fragment_ok(fragment) + + issuer := update_issuer(fragment.includes) + updateIssuer := { + "name": "issuers", + "action": "update", + "key": input.issuer, + "value": issuer, + } + + add_module := "namespace" in fragment.includes +} + +default scratch_mount := {"allowed": false} + +scratch_mounted(target) { + data.metadata.scratch_mounts[target] +} + +scratch_mount := {"metadata": [add_scratch_mount], "allowed": true} { + not scratch_mounted(input.target) + allow_unencrypted_scratch + add_scratch_mount := { + "name": "scratch_mounts", + "action": "add", + "key": input.target, + "value": {"encrypted": input.encrypted}, + } +} + +scratch_mount := {"metadata": [add_scratch_mount], "allowed": true} { + not scratch_mounted(input.target) + not allow_unencrypted_scratch + input.encrypted + add_scratch_mount := { + "name": "scratch_mounts", + "action": "add", + "key": input.target, + "value": {"encrypted": input.encrypted}, + } +} + +default scratch_unmount := {"allowed": false} + +scratch_unmount := {"metadata": [remove_scratch_mount], "allowed": true} { + scratch_mounted(input.unmountTarget) + remove_scratch_mount := { + "name": "scratch_mounts", + "action": "remove", + "key": input.unmountTarget, + } +} + +reason := { + "errors": errors, + "error_objects": error_objects +} + +################################################################ +# Error messages +################################################################ + +errors["deviceHash not found"] { + input.rule == "mount_device" + not deviceHash_ok +} + +errors["device already mounted at path"] { + input.rule == "mount_device" + device_mounted(input.target) +} + +errors["no device at path to unmount"] { + input.rule == "unmount_device" + not device_mounted(input.unmountTarget) +} + +errors["container already started"] { + input.rule == "create_container" + container_started +} + +errors["container not started"] { + input.rule in ["exec_in_container", "shutdown_container", "signal_container_process"] + not container_started +} + +errors["overlay has already been mounted"] { + input.rule == "mount_overlay" + overlay_exists +} + +default overlay_matches := false + +overlay_matches { + some container in candidate_containers + layerPaths_ok(container.layers) +} + +errors["no overlay at path to unmount"] { + input.rule == "unmount_overlay" + not overlay_mounted(input.unmountTarget) +} + +errors["no matching containers for overlay"] { + input.rule == "mount_overlay" + not overlay_matches +} + +default privileged_matches := false + +privileged_matches { + input.rule == "create_container" + some container in data.metadata.matches[input.containerID] + privileged_ok(container.allow_elevated) +} + +errors["privileged escalation not allowed"] { + input.rule in ["create_container"] + not privileged_matches +} + +default command_matches := false + +command_matches { + input.rule == "create_container" + some container in data.metadata.matches[input.containerID] + command_ok(container.command) +} + +command_matches { + input.rule == "exec_in_container" + some container in data.metadata.matches[input.containerID] + some process in container.exec_processes + command_ok(process.command) +} + +command_matches { + input.rule == "exec_external" + some process in candidate_external_processes + command_ok(process.command) +} + +errors["invalid command"] { + input.rule in ["create_container", "exec_in_container", "exec_external"] + not command_matches +} + +env_matches(env) { + input.rule in ["create_container", "exec_in_container"] + some container in data.metadata.matches[input.containerID] + some rule in container.env_rules + env_ok(rule.pattern, rule.strategy, env) +} + +env_matches(env) { + input.rule in ["exec_external"] + some process in candidate_external_processes + some rule in process.env_rules + env_ok(rule.pattern, rule.strategy, env) +} + +errors[envError] { + input.rule in ["create_container", "exec_in_container", "exec_external"] + bad_envs := [invalid | + env := input.envList[_] + not env_matches(env) + parts := split(env, "=") + invalid = parts[0] + ] + + count(bad_envs) > 0 + envError := concat(" ", ["invalid env list:", concat(",", bad_envs)]) +} + +env_rule_matches(rule) { + some env in input.envList + env_ok(rule.pattern, rule.strategy, env) +} + +errors["missing required environment variable"] { + input.rule == "create_container" + + not container_started + possible_containers := [container | + container := data.metadata.matches[input.containerID][_] + noNewPrivileges_ok(container.no_new_privileges) + user_ok(container.user) + privileged_ok(container.allow_elevated) + workingDirectory_ok(container.working_dir) + command_ok(container.command) + mountList_ok(container.mounts, container.allow_elevated) + ] + + count(possible_containers) > 0 + + containers := [container | + container := possible_containers[_] + missing_rules := {invalid | + invalid := {rule | + rule := container.env_rules[_] + rule.required + not env_rule_matches(rule) + } + count(invalid) > 0 + } + count(missing_rules) > 0 + ] + + count(containers) > 0 +} + +errors["missing required environment variable"] { + input.rule == "exec_in_container" + + container_started + possible_containers := [container | + container := data.metadata.matches[input.containerID][_] + noNewPrivileges_ok(container.no_new_privileges) + user_ok(container.user) + workingDirectory_ok(container.working_dir) + some process in container.exec_processes + command_ok(process.command) + ] + + count(possible_containers) > 0 + + containers := [container | + container := possible_containers[_] + missing_rules := {invalid | + invalid := {rule | + rule := container.env_rules[_] + rule.required + not env_rule_matches(rule) + } + count(invalid) > 0 + } + count(missing_rules) > 0 + ] + + count(containers) > 0 +} + +errors["missing required environment variable"] { + input.rule == "exec_external" + + possible_processes := [process | + process := candidate_external_processes[_] + workingDirectory_ok(process.working_dir) + command_ok(process.command) + ] + + count(possible_processes) > 0 + + processes := [process | + process := possible_processes[_] + missing_rules := {invalid | + invalid := {rule | + rule := process.env_rules[_] + rule.required + not env_rule_matches(rule) + } + count(invalid) > 0 + } + count(missing_rules) > 0 + ] + + count(processes) > 0 +} + +default workingDirectory_matches := false + +workingDirectory_matches { + input.rule in ["create_container", "exec_in_container"] + some container in data.metadata.matches[input.containerID] + workingDirectory_ok(container.working_dir) +} + +workingDirectory_matches { + input.rule == "exec_external" + some process in candidate_external_processes + workingDirectory_ok(process.working_dir) +} + +errors["invalid working directory"] { + input.rule in ["create_container", "exec_in_container", "exec_external"] + not workingDirectory_matches +} + +mount_matches(mount) { + some container in data.metadata.matches[input.containerID] + mount_ok(container.mounts, container.allow_elevated, mount) +} + +errors[mountError] { + input.rule == "create_container" + bad_mounts := [mount.destination | + mount := input.mounts[_] + not mount_matches(mount) + ] + + count(bad_mounts) > 0 + mountError := concat(" ", ["invalid mount list:", concat(",", bad_mounts)]) +} + +default signal_allowed := false + +signal_allowed { + some container in data.metadata.matches[input.containerID] + signal_ok(container.signals) +} + +signal_allowed { + some container in data.metadata.matches[input.containerID] + some process in container.exec_processes + command_ok(process.command) + signal_ok(process.signals) +} + +errors["target isn't allowed to receive the signal"] { + input.rule == "signal_container_process" + not signal_allowed +} + +errors["device already mounted at path"] { + input.rule == "plan9_mount" + plan9_mounted(input.target) +} + +errors["no device at path to unmount"] { + input.rule == "plan9_unmount" + not plan9_mounted(input.unmountTarget) +} + +default fragment_issuer_matches := false + +fragment_issuer_matches { + some fragment in candidate_fragments + fragment.issuer == input.issuer +} + +errors["invalid fragment issuer"] { + input.rule == "load_fragment" + not fragment_issuer_matches +} + +default fragment_feed_matches := false + +fragment_feed_matches { + some fragment in candidate_fragments + fragment.issuer == input.issuer + fragment.feed == input.feed +} + +fragment_feed_matches { + input.feed in data.metadata.issuers[input.issuer] +} + +errors["invalid fragment feed"] { + input.rule == "load_fragment" + fragment_issuer_matches + not fragment_feed_matches +} + +default fragment_version_is_valid := false + +fragment_version_is_valid { + some fragment in candidate_fragments + fragment.issuer == input.issuer + fragment.feed == input.feed + svn_ok(data[input.namespace].svn, fragment.minimum_svn) +} + +default svn_mismatch := false + +svn_mismatch { + some fragment in candidate_fragments + fragment.issuer == input.issuer + fragment.feed == input.feed + to_number(data[input.namespace].svn) + semver.is_valid(fragment.minimum_svn) +} + +svn_mismatch { + some fragment in candidate_fragments + fragment.issuer == input.issuer + fragment.feed == input.feed + semver.is_valid(data[input.namespace].svn) + to_number(fragment.minimum_svn) +} + +errors["fragment svn is below the specified minimum"] { + input.rule == "load_fragment" + fragment_feed_matches + not svn_mismatch + not fragment_version_is_valid +} + +errors["fragment svn and the specified minimum are different types"] { + input.rule == "load_fragment" + fragment_feed_matches + svn_mismatch +} + +errors["scratch already mounted at path"] { + input.rule == "scratch_mount" + scratch_mounted(input.target) +} + +errors["unencrypted scratch not allowed"] { + input.rule == "scratch_mount" + not allow_unencrypted_scratch + not input.encrypted +} + +errors["no scratch at path to unmount"] { + input.rule == "scratch_unmount" + not scratch_mounted(input.unmountTarget) +} + +errors[framework_version_error] { + policy_framework_version == null + framework_version_error := concat(" ", ["framework_version is missing. Current version:", version]) +} + +errors[framework_version_error] { + semver.compare(policy_framework_version, version) > 0 + framework_version_error := concat(" ", ["framework_version is ahead of the current version:", policy_framework_version, "is greater than", version]) +} + +errors[fragment_framework_version_error] { + input.namespace + fragment_framework_version == null + fragment_framework_version_error := concat(" ", ["fragment framework_version is missing. Current version:", version]) +} + +errors[fragment_framework_version_error] { + input.namespace + semver.compare(fragment_framework_version, version) > 0 + fragment_framework_version_error := concat(" ", ["fragment framework_version is ahead of the current version:", fragment_framework_version, "is greater than", version]) +} + +errors["containers only distinguishable by allow_stdio_access"] { + input.rule == "create_container" + + not container_started + possible_after_initial_containers := [container | + container := data.metadata.matches[input.containerID][_] + noNewPrivileges_ok(container.no_new_privileges) + user_ok(container.user) + privileged_ok(container.allow_elevated) + workingDirectory_ok(container.working_dir) + command_ok(container.command) + mountList_ok(container.mounts, container.allow_elevated) + seccomp_ok(container.seccomp_profile_sha256) + ] + + count(possible_after_initial_containers) > 0 + + # check to see if the environment variables match, dropping + # them if allowed (and necessary) + env_list := valid_envs_for_all(possible_after_initial_containers) + possible_after_env_containers := [container | + container := possible_after_initial_containers[_] + envList_ok(container.env_rules, env_list) + ] + + count(possible_after_env_containers) > 0 + + # check to see if the capabilities variables match, dropping + # them if allowed (and necessary) + caps_list := valid_caps_for_all(possible_after_env_containers, input.privileged) + possible_after_caps_containers := [container | + container := possible_after_env_containers[_] + caps_ok(get_capabilities(container, input.privileged), caps_list) + ] + + count(possible_after_caps_containers) > 0 + + # set final container list + containers := possible_after_caps_containers + + allow_stdio_access := containers[0].allow_stdio_access + some c in containers + c.allow_stdio_access != allow_stdio_access +} + +errors["external processes only distinguishable by allow_stdio_access"] { + input.rule == "exec_external" + + possible_processes := [process | + process := candidate_external_processes[_] + workingDirectory_ok(process.working_dir) + command_ok(process.command) + ] + + count(possible_processes) > 0 + + # check to see if the environment variables match, dropping + # them if allowed (and necessary) + env_list := valid_envs_for_all(possible_processes) + processes := [process | + process := possible_processes[_] + envList_ok(process.env_rules, env_list) + ] + + count(processes) > 0 + + allow_stdio_access := processes[0].allow_stdio_access + some p in processes + p.allow_stdio_access != allow_stdio_access +} + + +default noNewPrivileges_matches := false + +noNewPrivileges_matches { + input.rule == "create_container" + some container in data.metadata.matches[input.containerID] + noNewPrivileges_ok(container.no_new_privileges) +} + +noNewPrivileges_matches { + input.rule == "exec_in_container" + some container in data.metadata.matches[input.containerID] + some process in container.exec_processes + command_ok(process.command) + workingDirectory_ok(process.working_dir) + noNewPrivileges_ok(process.no_new_privileges) +} + +errors["invalid noNewPrivileges"] { + input.rule in ["create_container", "exec_in_container"] + not noNewPrivileges_matches +} + +default user_matches := false + +user_matches { + input.rule == "create_container" + some container in data.metadata.matches[input.containerID] + user_ok(container.user) +} + +user_matches { + input.rule == "exec_in_container" + some container in data.metadata.matches[input.containerID] + some process in container.exec_processes + command_ok(process.command) + workingDirectory_ok(process.working_dir) + user_ok(process.user) +} + +errors["invalid user"] { + input.rule in ["create_container", "exec_in_container"] + not user_matches +} + +errors["capabilities don't match"] { + input.rule == "create_container" + + not container_started + + possible_after_initial_containers := [container | + container := data.metadata.matches[input.containerID][_] + privileged_ok(container.allow_elevated) + noNewPrivileges_ok(container.no_new_privileges) + user_ok(container.user) + workingDirectory_ok(container.working_dir) + command_ok(container.command) + mountList_ok(container.mounts, container.allow_elevated) + seccomp_ok(container.seccomp_profile_sha256) + ] + + count(possible_after_initial_containers) > 0 + + # check to see if the environment variables match, dropping + # them if allowed (and necessary) + env_list := valid_envs_for_all(possible_after_initial_containers) + possible_after_env_containers := [container | + container := possible_after_initial_containers[_] + envList_ok(container.env_rules, env_list) + ] + + count(possible_after_env_containers) > 0 + + # check to see if the capabilities variables match, dropping + # them if allowed (and necessary) + caps_list := valid_caps_for_all(possible_after_env_containers, input.privileged) + possible_after_caps_containers := [container | + container := possible_after_env_containers[_] + caps_ok(get_capabilities(container, input.privileged), caps_list) + ] + + count(possible_after_caps_containers) == 0 +} + +errors["capabilities don't match"] { + input.rule == "exec_in_container" + + container_started + + possible_after_initial_containers := [container | + container := data.metadata.matches[input.containerID][_] + workingDirectory_ok(container.working_dir) + noNewPrivileges_ok(container.no_new_privileges) + user_ok(container.user) + some process in container.exec_processes + command_ok(process.command) + ] + + count(possible_after_initial_containers) > 0 + + # check to see if the environment variables match, dropping + # them if allowed (and necessary) + env_list := valid_envs_for_all(possible_after_initial_containers) + possible_after_env_containers := [container | + container := possible_after_initial_containers[_] + envList_ok(container.env_rules, env_list) + ] + + count(possible_after_env_containers) > 0 + + # check to see if the capabilities variables match, dropping + # them if allowed (and necessary) + caps_list := valid_caps_for_all(possible_after_env_containers, container_privileged) + possible_after_caps_containers := [container | + container := possible_after_env_containers[_] + caps_ok(get_capabilities(container, container_privileged), caps_list) + ] + + count(possible_after_caps_containers) == 0 +} + +# covers exec_in_container as well. it shouldn't be possible to ever get +# an exec_in_container as it "inherits" capabilities rules from create_container +errors["containers only distinguishable by capabilties"] { + input.rule == "create_container" + + allow_capability_dropping + not container_started + + # narrow the matches based upon command, working directory, and + # mount list + possible_after_initial_containers := [container | + container := data.metadata.matches[input.containerID][_] + # NB any change to these narrowing conditions should be reflected in + # the error handling, such that error messaging correctly reflects + # the narrowing process. + noNewPrivileges_ok(container.no_new_privileges) + user_ok(container.user) + privileged_ok(container.allow_elevated) + workingDirectory_ok(container.working_dir) + command_ok(container.command) + mountList_ok(container.mounts, container.allow_elevated) + ] + + count(possible_after_initial_containers) > 0 + + # check to see if the environment variables match, dropping + # them if allowed (and necessary) + env_list := valid_envs_for_all(possible_after_initial_containers) + possible_after_env_containers := [container | + container := possible_after_initial_containers[_] + envList_ok(container.env_rules, env_list) + ] + + count(possible_after_env_containers) > 0 + + largest := largest_caps_sets_for_all(possible_after_env_containers, input.privileged) + not all_caps_sets_are_equal(largest) +} + +default seccomp_matches := false + +seccomp_matches { + input.rule == "create_container" + some container in data.metadata.matches[input.containerID] + seccomp_ok(container.seccomp_profile_sha256) +} + +errors["invalid seccomp"] { + input.rule == "create_container" + not seccomp_matches +} + +default error_objects := null + +error_objects := containers { + input.rule == "create_container" + containers := data.metadata.matches[input.containerID] +} + +error_objects := processes { + input.rule == "exec_in_container" + processes := [process | + container := data.metadata.matches[input.containerID][_] + process := container.exec_processes[_] + ] +} + +error_objects := processes { + input.rule == "exec_external" + processes := candidate_external_processes +} + +error_objects := fragments { + input.rule == "load_fragment" + fragments := candidate_fragments +} + + +################################################################################ +# Logic for providing backwards compatibility for framework data objects +################################################################################ + + +check_container(raw_container, framework_version) := container { + semver.compare(framework_version, version) == 0 + container := raw_container +} + +check_container(raw_container, framework_version) := container { + semver.compare(framework_version, version) < 0 + container := { + # Base fields + "command": raw_container.command, + "env_rules": raw_container.env_rules, + "layers": raw_container.layers, + "mounts": raw_container.mounts, + "allow_elevated": raw_container.allow_elevated, + "working_dir": raw_container.working_dir, + "exec_processes": raw_container.exec_processes, + "signals": raw_container.signals, + "allow_stdio_access": raw_container.allow_stdio_access, + # Additional fields need to have default logic applied + "no_new_privileges": check_no_new_privileges(raw_container, framework_version), + "user": check_user(raw_container, framework_version), + "capabilities": check_capabilities(raw_container, framework_version), + "seccomp_profile_sha256": check_seccomp_profile_sha256(raw_container, framework_version), + } +} + +check_no_new_privileges(raw_container, framework_version) := no_new_privileges { + semver.compare(framework_version, "0.2.0") >= 0 + no_new_privileges := raw_container.no_new_privileges +} + +check_no_new_privileges(raw_container, framework_version) := no_new_privileges { + semver.compare(framework_version, "0.2.0") < 0 + no_new_privileges := false +} + +check_user(raw_container, framework_version) := user { + semver.compare(framework_version, "0.2.1") >= 0 + user := raw_container.user +} + +check_user(raw_container, framework_version) := user { + semver.compare(framework_version, "0.2.1") < 0 + user := { + "umask": "0022", + "user_idname": { + "pattern": "", + "strategy": "any" + }, + "group_idnames": [ + { + "pattern": "", + "strategy": "any" + } + ] + } +} + +check_capabilities(raw_container, framework_version) := capabilities { + semver.compare(framework_version, "0.2.2") >= 0 + capabilities := raw_container.capabilities +} + +check_capabilities(raw_container, framework_version) := capabilities { + semver.compare(framework_version, "0.2.2") < 0 + # we cannot determine a reasonable default at the time this is called, + # which is either during `mount_overlay` or `load_fragment`, and so + # we set it to `null`, which indicates that the capabilities should + # be determined dynamically when needed. + capabilities := null +} + +check_seccomp_profile_sha256(raw_container, framework_version) := seccomp_profile_sha256 { + semver.compare(framework_version, "0.2.3") >= 0 + seccomp_profile_sha256 := raw_container.seccomp_profile_sha256 +} + +check_seccomp_profile_sha256(raw_container, framework_version) := seccomp_profile_sha256 { + semver.compare(framework_version, "0.2.3") < 0 + seccomp_profile_sha256 := "" +} + +check_external_process(raw_process, framework_version) := process { + semver.compare(framework_version, version) == 0 + process := raw_process +} + +check_external_process(raw_process, framework_version) := process { + semver.compare(framework_version, version) < 0 + process := { + # Base fields + "command": raw_process.command, + "env_rules": raw_process.env_rules, + "working_dir": raw_process.working_dir, + "allow_stdio_access": raw_process.allow_stdio_access, + # Additional fields need to have default logic applied + } +} + +check_fragment(raw_fragment, framework_version) := fragment { + semver.compare(framework_version, version) == 0 + fragment := raw_fragment +} + +check_fragment(raw_fragment, framework_version) := fragment { + semver.compare(framework_version, version) < 0 + fragment := { + # Base fields + "issuer": raw_fragment.issuer, + "feed": raw_fragment.feed, + "minimum_svn": raw_fragment.minimum_svn, + "includes": raw_fragment.includes, + # Additional fields need to have default logic applied + } +} + +# base policy-level flags +allow_properties_access := data.policy.allow_properties_access +allow_dump_stacks := data.policy.allow_dump_stacks +allow_runtime_logging := data.policy.allow_runtime_logging +allow_environment_variable_dropping := data.policy.allow_environment_variable_dropping +allow_unencrypted_scratch := data.policy.allow_unencrypted_scratch + +# all flags not in the base set need to have default logic applied + +default allow_capability_dropping := false + +allow_capability_dropping := flag { + semver.compare(policy_framework_version, "0.2.2") >= 0 + flag := data.policy.allow_capability_dropping +} + +default policy_framework_version := null +default policy_api_version := null + +policy_framework_version := data.policy.framework_version +policy_api_version := data.policy.api_version + +# deprecated +policy_framework_version := data.policy.framework_svn +policy_api_version := data.policy.api_svn +fragment_framework_version := data[input.namespace].framework_svn diff --git a/third_party/opa/v1/rego/testdata/aci/input.json b/third_party/opa/v1/rego/testdata/aci/input.json new file mode 100644 index 000000000000..93c068b0336e --- /dev/null +++ b/third_party/opa/v1/rego/testdata/aci/input.json @@ -0,0 +1,12 @@ +{ + "containerID": "container0", + "layerPaths": [ + "/run/layers/p0-layer0", + "/run/layers/p0-layer1", + "/run/layers/p0-layer2", + "/run/layers/p0-layer3", + "/run/layers/p0-layer4", + "/run/layers/p0-layer5" + ], + "target": "/run/gcs/c/container0/rootfs" +} diff --git a/third_party/opa/v1/rego/testdata/aci/policy.rego b/third_party/opa/v1/rego/testdata/aci/policy.rego new file mode 100644 index 000000000000..068b5de01f17 --- /dev/null +++ b/third_party/opa/v1/rego/testdata/aci/policy.rego @@ -0,0 +1,89 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +package policy + +api_version := "0.10.0" +framework_version := "0.3.0" + +fragments := [ + {"issuer": "did:web:contoso.com", "feed": "contoso.azurecr.io/infra", "minimum_svn": "1", "includes": ["containers"]}, +] +containers := [ + { + "command": ["rustc","--help"], + "env_rules": [{"pattern": `PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true},{"pattern": `RUSTUP_HOME=/usr/local/rustup`, "strategy": "string", "required": true},{"pattern": `CARGO_HOME=/usr/local/cargo`, "strategy": "string", "required": true},{"pattern": `RUST_VERSION=1.52.1`, "strategy": "string", "required": true},{"pattern": `TERM=xterm`, "strategy": "string", "required": false},{"pattern": `PREFIX_.+=.+`, "strategy": "re2", "required": false}], + "layers": ["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"], + "mounts": [{"destination": "/container/path/one", "options": ["rbind","rshared","rw"], "source": "sandbox:///host/path/one", "type": "bind"},{"destination": "/container/path/two", "options": ["rbind","rshared","ro"], "source": "sandbox:///host/path/two", "type": "bind"}], + "exec_processes": [{"command": ["top"], "signals": []}], + "signals": [], + "user": { + "user_idname": {"pattern": ``, "strategy": "any"}, + "group_idnames": [{"pattern": ``, "strategy": "any"}], + "umask": "0022" + }, + "capabilities": { + "bounding": ["CAP_SYS_ADMIN"], + "effective": ["CAP_SYS_ADMIN"], + "inheritable": ["CAP_SYS_ADMIN"], + "permitted": ["CAP_SYS_ADMIN"], + "ambient": ["CAP_SYS_ADMIN"], + }, + "seccomp_profile_sha256": "", + "allow_elevated": true, + "working_dir": "/home/user", + "allow_stdio_access": false, + "no_new_privileges": true, + }, + { + "command": ["/pause"], + "env_rules": [{"pattern": `PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true},{"pattern": `TERM=xterm`, "strategy": "string", "required": false}], + "layers": ["16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415"], + "mounts": [], + "exec_processes": [], + "signals": [], + "user": { + "user_idname": {"pattern": ``, "strategy": "any"}, + "group_idnames": [{"pattern": ``, "strategy": "any"}], + "umask": "0022" + }, + "capabilities": null, + "seccomp_profile_sha256": "", + "allow_elevated": false, + "working_dir": "/", + "allow_stdio_access": false, + "no_new_privileges": true, + }, +] +external_processes := [ + {"command": ["bash"], "env_rules": [{"pattern": `PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true}], "working_dir": "/", "allow_stdio_access": false}, +] +allow_properties_access := false +allow_dump_stacks := false +allow_runtime_logging := false +allow_environment_variable_dropping := false +allow_unencrypted_scratch := false +allow_capability_dropping := true + + +mount_device := data.framework.mount_device +unmount_device := data.framework.unmount_device +mount_overlay := data.framework.mount_overlay +unmount_overlay := data.framework.unmount_overlay +create_container := data.framework.create_container +exec_in_container := data.framework.exec_in_container +exec_external := data.framework.exec_external +shutdown_container := data.framework.shutdown_container +signal_container_process := data.framework.signal_container_process +plan9_mount := data.framework.plan9_mount +plan9_unmount := data.framework.plan9_unmount +get_properties := data.framework.get_properties +dump_stacks := data.framework.dump_stacks +runtime_logging := data.framework.runtime_logging +load_fragment := data.framework.load_fragment +scratch_mount := data.framework.scratch_mount +scratch_unmount := data.framework.scratch_unmount +reason := { + "errors": data.framework.errors, + "error_objects": data.framework.error_objects, +} diff --git a/third_party/opa/v1/rego/testdata/ast.json b/third_party/opa/v1/rego/testdata/ast.json new file mode 100644 index 000000000000..b6364f2a1dde --- /dev/null +++ b/third_party/opa/v1/rego/testdata/ast.json @@ -0,0 +1,7647 @@ +{ + "package": { + "location": "1:1:1:8", + "path": [ + { + "type": "var", + "value": "data" + }, + { + "location": "1:9:1:14", + "type": "string", + "value": "regal" + }, + { + "location": "1:15:1:18", + "type": "string", + "value": "ast" + } + ], + "annotations": [ + { + "location": "55:1:58:85", + "scope": "rule", + "description": "find vars like input[x].foo[y] where x and y are vars\nnote: value.type == \"ref\" check must have been done before calling this function\n" + }, + { + "location": "81:1:84:64", + "scope": "rule", + "description": "traverses all nodes in provided terms (using `walk`), and returns an array with\nall variables declared in terms, i,e [x, y] or {x: y}, etc.\n" + }, + { + "location": "91:1:94:70", + "scope": "rule", + "description": "traverses all nodes in provided terms (using `walk`), and returns true if any variable\nis found in terms, with early exit (as opposed to find_term_vars)\n" + }, + { + "scope": "rule", + "description": "traverses all nodes under provided node (using `walk`), and returns an array with\nall variables declared via assignment (:=), `some`, `every` and in comprehensions\nDEPRECATED: uses ast.found.vars instead\n", + "location": "165:1:169:44" + }, + { + "location": "182:1:192:10", + "scope": "rule", + "description": "object containing all variables found in the input AST, keyed first by the index of\nthe rule where the variables were found (as a numeric string), and then the context\nof the variable, which will be one of:\n- term\n- assign\n- every\n- some\n- somein\n- ref\n" + }, + { + "location": "205:1:206:41", + "scope": "rule", + "description": "all refs foundd in module" + }, + { + "description": "all symbols foundd in module", + "location": "218:1:219:44", + "scope": "rule" + }, + { + "location": "229:1:230:50", + "scope": "rule", + "description": "all comprehensions found in module" + }, + { + "location": "242:1:247:28", + "scope": "rule", + "description": "finds all vars declared in `rule` *before* the `location` provided\nnote: this isn't 100% accurate, as it doesn't take into account `=`\nassignments / unification, but it's likely good enough since other rules\nrecommend against those\n" + }, + { + "location": "292:1:293:77", + "scope": "rule", + "description": "find *only* names in the local scope, and not e.g. rule names" + }, + { + "location": "306:1:309:82", + "scope": "rule", + "description": "similar to `find_vars_in_local_scope`, but returns all variable names in scope\nof the given location *and* the rule names present in the scope (i.e. module)\n" + }, + { + "location": "317:1:320:39", + "scope": "rule", + "description": "find all variables declared via `some` declarations (and *not* `some .. in`)\nin the scope of the given location\n" + }, + { + "location": "326:1:327:41", + "scope": "rule", + "description": "all expressions in module" + } + ] + }, + "imports": [ + { + "location": "3:1:3:7", + "path": { + "type": "ref", + "value": [ + { + "type": "var", + "value": "rego", + "location": "3:8:3:12" + }, + { + "location": "3:13:3:15", + "type": "string", + "value": "v1" + } + ], + "location": "3:8:3:15" + } + }, + { + "location": "5:1:5:7", + "path": { + "location": "5:8:5:23", + "type": "ref", + "value": [ + { + "location": "5:8:5:12", + "type": "var", + "value": "data" + }, + { + "location": "5:13:5:18", + "type": "string", + "value": "regal" + }, + { + "location": "5:19:5:23", + "type": "string", + "value": "util" + } + ] + } + } + ], + "rules": [ + { + "location": "7:1:11:2", + "head": { + "args": [ + { + "location": "7:19:7:22", + "type": "var", + "value": "obj" + } + ], + "assign": true, + "value": { + "location": "7:27:11:2", + "type": "arraycomprehension", + "value": { + "term": { + "type": "var", + "value": "value", + "location": "7:28:7:33" + }, + "body": [ + { + "location": "8:2:8:23", + "terms": [ + { + "location": "8:2:8:6", + "type": "ref", + "value": [ + { + "location": "8:2:8:6", + "type": "var", + "value": "walk" + } + ] + }, + { + "location": "8:7:8:10", + "type": "var", + "value": "obj" + }, + { + "value": [ + { + "location": "8:13:8:14", + "type": "var", + "value": "$0" + }, + { + "location": "8:16:8:21", + "type": "var", + "value": "value" + } + ], + "location": "8:12:8:22", + "type": "array" + } + ] + }, + { + "location": "9:2:9:21", + "terms": [ + { + "location": "9:13:9:15", + "type": "ref", + "value": [ + { + "location": "9:13:9:15", + "type": "var", + "value": "equal" + } + ] + }, + { + "value": [ + { + "location": "9:2:9:7", + "type": "var", + "value": "value" + }, + { + "location": "9:8:9:12", + "type": "string", + "value": "type" + } + ], + "location": "9:2:9:12", + "type": "ref" + }, + { + "location": "9:16:9:21", + "type": "string", + "value": "var" + } + ] + }, + { + "location": "10:2:10:33", + "terms": [ + { + "value": [ + { + "location": "10:28:10:30", + "type": "var", + "value": "equal" + } + ], + "location": "10:28:10:30", + "type": "ref" + }, + { + "location": "10:2:10:27", + "type": "call", + "value": [ + { + "location": "10:2:10:9", + "type": "ref", + "value": [ + { + "location": "10:2:10:9", + "type": "var", + "value": "indexof" + } + ] + }, + { + "value": [ + { + "location": "10:10:10:15", + "type": "var", + "value": "value" + }, + { + "location": "10:16:10:21", + "type": "string", + "value": "value" + } + ], + "location": "10:10:10:21", + "type": "ref" + }, + { + "location": "10:23:10:26", + "type": "string", + "value": "$" + } + ] + }, + { + "value": -1, + "location": "10:31:10:32", + "type": "number" + } + ] + } + ] + } + }, + "location": "7:1:11:2", + "ref": [ + { + "type": "var", + "value": "_find_nested_vars", + "location": "7:1:7:18" + } + ] + } + }, + { + "location": "16:1:19:2", + "head": { + "location": "16:1:16:32", + "ref": [ + { + "location": "16:1:16:18", + "type": "var", + "value": "_find_assign_vars" + } + ], + "args": [ + { + "location": "16:19:16:24", + "type": "var", + "value": "value" + } + ], + "assign": true, + "value": { + "location": "16:29:16:32", + "type": "var", + "value": "var" + } + }, + "body": [ + { + "location": "17:2:17:24", + "terms": [ + { + "location": "17:16:17:18", + "type": "ref", + "value": [ + { + "location": "17:16:17:18", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "17:2:17:15", + "type": "ref", + "value": [ + { + "location": "17:2:17:7", + "type": "var", + "value": "value" + }, + { + "location": "17:8:17:9", + "type": "number", + "value": 1 + }, + { + "location": "17:11:17:15", + "type": "string", + "value": "type" + } + ] + }, + { + "type": "string", + "value": "var", + "location": "17:19:17:24" + } + ] + }, + { + "location": "18:2:18:19", + "terms": [ + { + "location": "18:6:18:8", + "type": "ref", + "value": [ + { + "location": "18:6:18:8", + "type": "var", + "value": "assign" + } + ] + }, + { + "type": "var", + "value": "var", + "location": "18:2:18:5" + }, + { + "location": "18:9:18:19", + "type": "array", + "value": [ + { + "location": "18:10:18:18", + "type": "ref", + "value": [ + { + "location": "18:10:18:15", + "type": "var", + "value": "value" + }, + { + "location": "18:16:18:17", + "type": "number", + "value": 1 + } + ] + } + ] + } + ] + } + ] + }, + { + "location": "25:1:28:2", + "head": { + "ref": [ + { + "location": "25:1:25:18", + "type": "var", + "value": "_find_assign_vars" + } + ], + "args": [ + { + "value": "value", + "location": "25:19:25:24", + "type": "var" + } + ], + "assign": true, + "value": { + "location": "25:29:25:33", + "type": "var", + "value": "vars" + }, + "location": "25:1:25:33" + }, + "body": [ + { + "terms": [ + { + "type": "ref", + "value": [ + { + "location": "26:16:26:18", + "type": "var", + "value": "internal" + }, + { + "location": "26:16:26:18", + "type": "string", + "value": "member_2" + } + ], + "location": "26:16:26:18" + }, + { + "location": "26:2:26:15", + "type": "ref", + "value": [ + { + "location": "26:2:26:7", + "type": "var", + "value": "value" + }, + { + "value": 1, + "location": "26:8:26:9", + "type": "number" + }, + { + "location": "26:11:26:15", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "26:19:26:38", + "type": "set", + "value": [ + { + "type": "string", + "value": "array", + "location": "26:20:26:27" + }, + { + "type": "string", + "value": "object", + "location": "26:29:26:37" + } + ] + } + ], + "location": "26:2:26:38" + }, + { + "location": "27:2:27:37", + "terms": [ + { + "type": "ref", + "value": [ + { + "location": "27:7:27:9", + "type": "var", + "value": "assign" + } + ], + "location": "27:7:27:9" + }, + { + "location": "27:2:27:6", + "type": "var", + "value": "vars" + }, + { + "value": [ + { + "location": "27:10:27:27", + "type": "ref", + "value": [ + { + "location": "27:10:27:27", + "type": "var", + "value": "_find_nested_vars" + } + ] + }, + { + "location": "27:28:27:36", + "type": "ref", + "value": [ + { + "location": "27:28:27:33", + "type": "var", + "value": "value" + }, + { + "location": "27:34:27:35", + "type": "number", + "value": 1 + } + ] + } + ], + "location": "27:10:27:37", + "type": "call" + } + ] + } + ] + }, + { + "location": "31:1:34:2", + "head": { + "ref": [ + { + "location": "31:1:31:21", + "type": "var", + "value": "_find_some_decl_vars" + } + ], + "args": [ + { + "location": "31:22:31:27", + "type": "var", + "value": "value" + } + ], + "assign": true, + "value": { + "location": "31:32:34:2", + "type": "arraycomprehension", + "value": { + "body": [ + { + "location": "32:2:32:17", + "terms": { + "location": "32:2:32:6", + "symbols": [ + { + "type": "call", + "value": [ + { + "location": "32:9:32:11", + "type": "ref", + "value": [ + { + "location": "32:9:32:11", + "type": "var", + "value": "internal" + }, + { + "location": "32:9:32:11", + "type": "string", + "value": "member_2" + } + ] + }, + { + "location": "32:7:32:8", + "type": "var", + "value": "v" + }, + { + "location": "32:12:32:17", + "type": "var", + "value": "value" + } + ], + "location": "32:7:32:17" + } + ] + } + }, + { + "location": "33:2:33:17", + "terms": [ + { + "location": "33:9:33:11", + "type": "ref", + "value": [ + { + "location": "33:9:33:11", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "33:2:33:8", + "type": "ref", + "value": [ + { + "location": "33:2:33:3", + "type": "var", + "value": "v" + }, + { + "location": "33:4:33:8", + "type": "string", + "value": "type" + } + ] + }, + { + "value": "var", + "location": "33:12:33:17", + "type": "string" + } + ] + } + ], + "term": { + "location": "31:33:31:34", + "type": "var", + "value": "v" + } + } + }, + "location": "31:1:34:2" + } + }, + { + "location": "37:1:42:2", + "head": { + "ref": [ + { + "location": "37:1:37:24", + "type": "var", + "value": "_find_some_in_decl_vars" + } + ], + "args": [ + { + "location": "37:25:37:30", + "type": "var", + "value": "value" + } + ], + "assign": true, + "value": { + "value": "vars", + "location": "37:35:37:39", + "type": "var" + }, + "location": "37:1:37:39" + }, + "body": [ + { + "terms": [ + { + "location": "38:6:38:8", + "type": "ref", + "value": [ + { + "location": "38:6:38:8", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "38:2:38:5", + "type": "var", + "value": "arr" + }, + { + "location": "38:9:38:23", + "type": "ref", + "value": [ + { + "value": "value", + "location": "38:9:38:14", + "type": "var" + }, + { + "location": "38:15:38:16", + "type": "number", + "value": 0 + }, + { + "location": "38:18:38:23", + "type": "string", + "value": "value" + } + ] + } + ], + "location": "38:2:38:23" + }, + { + "terms": [ + { + "location": "39:13:39:15", + "type": "ref", + "value": [ + { + "location": "39:13:39:15", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "39:2:39:12", + "type": "call", + "value": [ + { + "location": "39:2:39:7", + "type": "ref", + "value": [ + { + "location": "39:2:39:7", + "type": "var", + "value": "count" + } + ] + }, + { + "location": "39:8:39:11", + "type": "var", + "value": "arr" + } + ] + }, + { + "location": "39:16:39:17", + "type": "number", + "value": 3 + } + ], + "location": "39:2:39:17" + }, + { + "location": "41:2:41:35", + "terms": [ + { + "location": "41:7:41:9", + "type": "ref", + "value": [ + { + "location": "41:7:41:9", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "41:2:41:6", + "type": "var", + "value": "vars" + }, + { + "location": "41:10:41:35", + "type": "call", + "value": [ + { + "location": "41:10:41:27", + "type": "ref", + "value": [ + { + "location": "41:10:41:27", + "type": "var", + "value": "_find_nested_vars" + } + ] + }, + { + "type": "ref", + "value": [ + { + "location": "41:28:41:31", + "type": "var", + "value": "arr" + }, + { + "location": "41:32:41:33", + "type": "number", + "value": 1 + } + ], + "location": "41:28:41:34" + } + ] + } + ] + } + ] + }, + { + "head": { + "args": [ + { + "location": "45:25:45:30", + "type": "var", + "value": "value" + } + ], + "assign": true, + "value": { + "location": "45:35:45:39", + "type": "var", + "value": "vars" + }, + "location": "45:1:45:39", + "ref": [ + { + "value": "_find_some_in_decl_vars", + "location": "45:1:45:24", + "type": "var" + } + ] + }, + "body": [ + { + "location": "46:2:46:23", + "terms": [ + { + "location": "46:6:46:8", + "type": "ref", + "value": [ + { + "location": "46:6:46:8", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "46:2:46:5", + "type": "var", + "value": "arr" + }, + { + "type": "ref", + "value": [ + { + "location": "46:9:46:14", + "type": "var", + "value": "value" + }, + { + "location": "46:15:46:16", + "type": "number", + "value": 0 + }, + { + "type": "string", + "value": "value", + "location": "46:18:46:23" + } + ], + "location": "46:9:46:23" + } + ] + }, + { + "location": "47:2:47:17", + "terms": [ + { + "location": "47:13:47:15", + "type": "ref", + "value": [ + { + "location": "47:13:47:15", + "type": "var", + "value": "equal" + } + ] + }, + { + "value": [ + { + "location": "47:2:47:7", + "type": "ref", + "value": [ + { + "location": "47:2:47:7", + "type": "var", + "value": "count" + } + ] + }, + { + "location": "47:8:47:11", + "type": "var", + "value": "arr" + } + ], + "location": "47:2:47:12", + "type": "call" + }, + { + "type": "number", + "value": 4, + "location": "47:16:47:17" + } + ] + }, + { + "location": "49:2:52:3", + "terms": [ + { + "location": "49:7:49:9", + "type": "ref", + "value": [ + { + "location": "49:7:49:9", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "49:2:49:6", + "type": "var", + "value": "vars" + }, + { + "type": "arraycomprehension", + "value": { + "term": { + "location": "49:11:49:12", + "type": "var", + "value": "v" + }, + "body": [ + { + "location": "50:3:50:19", + "terms": { + "location": "50:3:50:7", + "symbols": [ + { + "location": "50:8:50:19", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "50:10:50:12", + "type": "var", + "value": "internal" + }, + { + "location": "50:10:50:12", + "type": "string", + "value": "member_2" + } + ], + "location": "50:10:50:12" + }, + { + "value": "i", + "location": "50:8:50:9", + "type": "var" + }, + { + "value": [ + { + "location": "50:14:50:15", + "type": "number", + "value": 1 + }, + { + "type": "number", + "value": 2, + "location": "50:17:50:18" + } + ], + "location": "50:13:50:19", + "type": "array" + } + ] + } + ] + } + }, + { + "location": "51:3:51:38", + "terms": { + "location": "51:3:51:7", + "symbols": [ + { + "location": "51:8:51:38", + "type": "call", + "value": [ + { + "location": "51:10:51:12", + "type": "ref", + "value": [ + { + "type": "var", + "value": "internal", + "location": "51:10:51:12" + }, + { + "value": "member_2", + "location": "51:10:51:12", + "type": "string" + } + ] + }, + { + "location": "51:8:51:9", + "type": "var", + "value": "v" + }, + { + "type": "call", + "value": [ + { + "location": "51:13:51:30", + "type": "ref", + "value": [ + { + "location": "51:13:51:30", + "type": "var", + "value": "_find_nested_vars" + } + ] + }, + { + "location": "51:31:51:37", + "type": "ref", + "value": [ + { + "location": "51:31:51:34", + "type": "var", + "value": "arr" + }, + { + "location": "51:35:51:36", + "type": "var", + "value": "i" + } + ] + } + ], + "location": "51:13:51:38" + } + ] + } + ] + } + } + ] + }, + "location": "49:10:52:3" + } + ] + } + ], + "location": "45:1:53:2" + }, + { + "location": "59:1:64:2", + "annotations": [ + { + "location": "55:1:58:85", + "scope": "rule", + "description": "find vars like input[x].foo[y] where x and y are vars\nnote: value.type == \"ref\" check must have been done before calling this function\n" + } + ], + "head": { + "ref": [ + { + "location": "59:1:59:14", + "type": "var", + "value": "find_ref_vars" + } + ], + "args": [ + { + "location": "59:15:59:20", + "type": "var", + "value": "value" + } + ], + "assign": true, + "value": { + "location": "59:25:64:2", + "type": "arraycomprehension", + "value": { + "term": { + "location": "59:26:59:29", + "type": "var", + "value": "var" + }, + "body": [ + { + "location": "60:2:60:28", + "terms": { + "symbols": [ + { + "location": "60:7:60:28", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "60:14:60:16", + "type": "var", + "value": "internal" + }, + { + "location": "60:14:60:16", + "type": "string", + "value": "member_3" + } + ], + "location": "60:14:60:16" + }, + { + "location": "60:7:60:8", + "type": "var", + "value": "i" + }, + { + "type": "var", + "value": "var", + "location": "60:10:60:13" + }, + { + "value": [ + { + "type": "var", + "value": "value", + "location": "60:17:60:22" + }, + { + "type": "string", + "value": "value", + "location": "60:23:60:28" + } + ], + "location": "60:17:60:28", + "type": "ref" + } + ] + } + ], + "location": "60:2:60:6" + } + }, + { + "location": "62:2:62:7", + "terms": [ + { + "type": "ref", + "value": [ + { + "value": "gt", + "location": "62:4:62:5", + "type": "var" + } + ], + "location": "62:4:62:5" + }, + { + "location": "62:2:62:3", + "type": "var", + "value": "i" + }, + { + "location": "62:6:62:7", + "type": "number", + "value": 0 + } + ] + }, + { + "terms": [ + { + "location": "63:11:63:13", + "type": "ref", + "value": [ + { + "type": "var", + "value": "equal", + "location": "63:11:63:13" + } + ] + }, + { + "value": [ + { + "location": "63:2:63:5", + "type": "var", + "value": "var" + }, + { + "location": "63:6:63:10", + "type": "string", + "value": "type" + } + ], + "location": "63:2:63:10", + "type": "ref" + }, + { + "location": "63:14:63:19", + "type": "string", + "value": "var" + } + ], + "location": "63:2:63:19" + } + ] + } + }, + "location": "59:1:64:2" + } + }, + { + "body": [ + { + "location": "69:2:72:3", + "terms": [ + { + "value": [ + { + "location": "69:10:69:12", + "type": "var", + "value": "assign" + } + ], + "location": "69:10:69:12", + "type": "ref" + }, + { + "type": "var", + "value": "key_var", + "location": "69:2:69:9" + }, + { + "location": "69:13:72:3", + "type": "arraycomprehension", + "value": { + "body": [ + { + "location": "70:3:70:26", + "terms": [ + { + "location": "70:18:70:20", + "type": "ref", + "value": [ + { + "location": "70:18:70:20", + "type": "var", + "value": "equal" + } + ] + }, + { + "value": [ + { + "type": "var", + "value": "value", + "location": "70:3:70:8" + }, + { + "location": "70:9:70:12", + "type": "string", + "value": "key" + }, + { + "location": "70:13:70:17", + "type": "string", + "value": "type" + } + ], + "location": "70:3:70:17", + "type": "ref" + }, + { + "value": "var", + "location": "70:21:70:26", + "type": "string" + } + ] + }, + { + "location": "71:3:71:38", + "terms": [ + { + "location": "71:33:71:35", + "type": "ref", + "value": [ + { + "value": "equal", + "location": "71:33:71:35", + "type": "var" + } + ] + }, + { + "location": "71:3:71:32", + "type": "call", + "value": [ + { + "location": "71:3:71:10", + "type": "ref", + "value": [ + { + "location": "71:3:71:10", + "type": "var", + "value": "indexof" + } + ] + }, + { + "location": "71:11:71:26", + "type": "ref", + "value": [ + { + "value": "value", + "location": "71:11:71:16", + "type": "var" + }, + { + "location": "71:17:71:20", + "type": "string", + "value": "key" + }, + { + "location": "71:21:71:26", + "type": "string", + "value": "value" + } + ] + }, + { + "value": "$", + "location": "71:28:71:31", + "type": "string" + } + ] + }, + { + "location": "71:36:71:37", + "type": "number", + "value": -1 + } + ] + } + ], + "term": { + "location": "69:14:69:23", + "type": "ref", + "value": [ + { + "location": "69:14:69:19", + "type": "var", + "value": "value" + }, + { + "location": "69:20:69:23", + "type": "string", + "value": "key" + } + ] + } + } + } + ] + }, + { + "location": "73:2:76:3", + "terms": [ + { + "location": "73:10:73:12", + "type": "ref", + "value": [ + { + "location": "73:10:73:12", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "73:2:73:9", + "type": "var", + "value": "val_var" + }, + { + "value": { + "term": { + "location": "73:14:73:25", + "type": "ref", + "value": [ + { + "type": "var", + "value": "value", + "location": "73:14:73:19" + }, + { + "location": "73:20:73:25", + "type": "string", + "value": "value" + } + ] + }, + "body": [ + { + "location": "74:3:74:28", + "terms": [ + { + "location": "74:20:74:22", + "type": "ref", + "value": [ + { + "location": "74:20:74:22", + "type": "var", + "value": "equal" + } + ] + }, + { + "type": "ref", + "value": [ + { + "location": "74:3:74:8", + "type": "var", + "value": "value" + }, + { + "location": "74:9:74:14", + "type": "string", + "value": "value" + }, + { + "location": "74:15:74:19", + "type": "string", + "value": "type" + } + ], + "location": "74:3:74:19" + }, + { + "location": "74:23:74:28", + "type": "string", + "value": "var" + } + ] + }, + { + "location": "75:3:75:40", + "terms": [ + { + "location": "75:35:75:37", + "type": "ref", + "value": [ + { + "location": "75:35:75:37", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "75:3:75:34", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "75:3:75:10", + "type": "var", + "value": "indexof" + } + ], + "location": "75:3:75:10" + }, + { + "location": "75:11:75:28", + "type": "ref", + "value": [ + { + "location": "75:11:75:16", + "type": "var", + "value": "value" + }, + { + "location": "75:17:75:22", + "type": "string", + "value": "value" + }, + { + "location": "75:23:75:28", + "type": "string", + "value": "value" + } + ] + }, + { + "location": "75:30:75:33", + "type": "string", + "value": "$" + } + ] + }, + { + "location": "75:38:75:39", + "type": "number", + "value": -1 + } + ] + } + ] + }, + "location": "73:13:76:3", + "type": "arraycomprehension" + } + ] + }, + { + "location": "78:2:78:40", + "terms": [ + { + "type": "ref", + "value": [ + { + "value": "assign", + "location": "78:7:78:9", + "type": "var" + } + ], + "location": "78:7:78:9" + }, + { + "value": "vars", + "location": "78:2:78:6", + "type": "var" + }, + { + "location": "78:10:78:40", + "type": "call", + "value": [ + { + "location": "78:10:78:22", + "type": "ref", + "value": [ + { + "location": "78:10:78:15", + "type": "var", + "value": "array" + }, + { + "location": "78:16:78:22", + "type": "string", + "value": "concat" + } + ] + }, + { + "value": "key_var", + "location": "78:23:78:30", + "type": "var" + }, + { + "location": "78:32:78:39", + "type": "var", + "value": "val_var" + } + ] + } + ] + } + ], + "location": "68:1:79:2", + "head": { + "location": "68:1:68:32", + "ref": [ + { + "value": "_find_every_vars", + "location": "68:1:68:17", + "type": "var" + } + ], + "args": [ + { + "location": "68:18:68:23", + "type": "var", + "value": "value" + } + ], + "assign": true, + "value": { + "location": "68:28:68:32", + "type": "var", + "value": "vars" + } + } + }, + { + "location": "85:1:89:2", + "annotations": [ + { + "location": "81:1:84:64", + "scope": "rule", + "description": "traverses all nodes in provided terms (using `walk`), and returns an array with\nall variables declared in terms, i,e [x, y] or {x: y}, etc.\n" + } + ], + "head": { + "ref": [ + { + "location": "85:1:85:15", + "type": "var", + "value": "find_term_vars" + } + ], + "args": [ + { + "value": "terms", + "location": "85:16:85:21", + "type": "var" + } + ], + "assign": true, + "value": { + "location": "85:26:89:2", + "type": "arraycomprehension", + "value": { + "term": { + "location": "85:27:85:31", + "type": "var", + "value": "term" + }, + "body": [ + { + "location": "86:2:86:24", + "terms": [ + { + "value": [ + { + "location": "86:2:86:6", + "type": "var", + "value": "walk" + } + ], + "location": "86:2:86:6", + "type": "ref" + }, + { + "location": "86:7:86:12", + "type": "var", + "value": "terms" + }, + { + "location": "86:14:86:23", + "type": "array", + "value": [ + { + "location": "86:15:86:16", + "type": "var", + "value": "$1" + }, + { + "type": "var", + "value": "term", + "location": "86:18:86:22" + } + ] + } + ] + }, + { + "location": "88:2:88:20", + "terms": [ + { + "location": "88:12:88:14", + "type": "ref", + "value": [ + { + "type": "var", + "value": "equal", + "location": "88:12:88:14" + } + ] + }, + { + "location": "88:2:88:11", + "type": "ref", + "value": [ + { + "location": "88:2:88:6", + "type": "var", + "value": "term" + }, + { + "value": "type", + "location": "88:7:88:11", + "type": "string" + } + ] + }, + { + "location": "88:15:88:20", + "type": "string", + "value": "var" + } + ] + } + ] + } + }, + "location": "85:1:89:2" + } + }, + { + "head": { + "value": { + "type": "boolean", + "value": true + }, + "location": "95:1:95:20", + "ref": [ + { + "location": "95:1:95:13", + "type": "var", + "value": "has_term_var" + } + ], + "args": [ + { + "type": "var", + "value": "terms", + "location": "95:14:95:19" + } + ] + }, + "body": [ + { + "location": "96:2:96:24", + "terms": [ + { + "value": [ + { + "value": "walk", + "location": "96:2:96:6", + "type": "var" + } + ], + "location": "96:2:96:6", + "type": "ref" + }, + { + "location": "96:7:96:12", + "type": "var", + "value": "terms" + }, + { + "location": "96:14:96:23", + "type": "array", + "value": [ + { + "location": "96:15:96:16", + "type": "var", + "value": "$2" + }, + { + "location": "96:18:96:22", + "type": "var", + "value": "term" + } + ] + } + ] + }, + { + "location": "98:2:98:20", + "terms": [ + { + "location": "98:12:98:14", + "type": "ref", + "value": [ + { + "location": "98:12:98:14", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "98:2:98:11", + "type": "ref", + "value": [ + { + "location": "98:2:98:6", + "type": "var", + "value": "term" + }, + { + "location": "98:7:98:11", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "98:15:98:20", + "type": "string", + "value": "var" + } + ] + } + ], + "location": "95:1:99:2", + "annotations": [ + { + "description": "traverses all nodes in provided terms (using `walk`), and returns true if any variable\nis found in terms, with early exit (as opposed to find_term_vars)\n", + "location": "91:1:94:70", + "scope": "rule" + } + ] + }, + { + "location": "101:1:112:2", + "head": { + "value": { + "type": "object", + "value": [ + [ + { + "value": "term", + "location": "101:29:101:35", + "type": "string" + }, + { + "location": "101:37:101:87", + "type": "call", + "value": [ + { + "location": "101:37:101:51", + "type": "ref", + "value": [ + { + "location": "101:37:101:51", + "type": "var", + "value": "find_term_vars" + } + ] + }, + { + "type": "call", + "value": [ + { + "location": "101:52:101:69", + "type": "ref", + "value": [ + { + "location": "101:52:101:69", + "type": "var", + "value": "function_ret_args" + } + ] + }, + { + "location": "101:70:101:77", + "type": "var", + "value": "fn_name" + }, + { + "location": "101:79:101:84", + "type": "var", + "value": "value" + } + ], + "location": "101:52:101:86" + } + ] + } + ] + ], + "location": "101:28:101:87" + }, + "location": "101:1:101:87", + "ref": [ + { + "location": "101:1:101:11", + "type": "var", + "value": "_find_vars" + } + ], + "args": [ + { + "location": "101:12:101:17", + "type": "var", + "value": "value" + }, + { + "location": "101:19:101:23", + "type": "var", + "value": "last" + } + ], + "assign": true + }, + "body": [ + { + "terms": [ + { + "location": "102:7:102:9", + "type": "ref", + "value": [ + { + "location": "102:7:102:9", + "type": "var", + "value": "equal" + } + ] + }, + { + "type": "var", + "value": "last", + "location": "102:2:102:6" + }, + { + "location": "102:10:102:17", + "type": "string", + "value": "terms" + } + ], + "location": "102:2:102:17" + }, + { + "location": "103:2:103:24", + "terms": [ + { + "location": "103:16:103:18", + "type": "ref", + "value": [ + { + "location": "103:16:103:18", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "103:2:103:15", + "type": "ref", + "value": [ + { + "location": "103:2:103:7", + "type": "var", + "value": "value" + }, + { + "location": "103:8:103:9", + "type": "number", + "value": 0 + }, + { + "location": "103:11:103:15", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "103:19:103:24", + "type": "string", + "value": "ref" + } + ] + }, + { + "location": "104:2:104:33", + "terms": [ + { + "location": "104:25:104:27", + "type": "ref", + "value": [ + { + "location": "104:25:104:27", + "type": "var", + "value": "equal" + } + ] + }, + { + "type": "ref", + "value": [ + { + "location": "104:2:104:7", + "type": "var", + "value": "value" + }, + { + "location": "104:8:104:9", + "type": "number", + "value": 0 + }, + { + "location": "104:11:104:16", + "type": "string", + "value": "value" + }, + { + "location": "104:17:104:18", + "type": "number", + "value": 0 + }, + { + "location": "104:20:104:24", + "type": "string", + "value": "type" + } + ], + "location": "104:2:104:24" + }, + { + "location": "104:28:104:33", + "type": "string", + "value": "var" + } + ] + }, + { + "location": "105:2:105:37", + "terms": [ + { + "location": "105:26:105:28", + "type": "ref", + "value": [ + { + "location": "105:26:105:28", + "type": "var", + "value": "neq" + } + ] + }, + { + "location": "105:2:105:25", + "type": "ref", + "value": [ + { + "location": "105:2:105:7", + "type": "var", + "value": "value" + }, + { + "location": "105:8:105:9", + "type": "number", + "value": 0 + }, + { + "location": "105:11:105:16", + "type": "string", + "value": "value" + }, + { + "value": 0, + "location": "105:17:105:18", + "type": "number" + }, + { + "value": "value", + "location": "105:20:105:25", + "type": "string" + } + ] + }, + { + "type": "string", + "value": "assign", + "location": "105:29:105:37" + } + ] + }, + { + "location": "107:2:107:42", + "terms": [ + { + "location": "107:10:107:12", + "type": "ref", + "value": [ + { + "location": "107:10:107:12", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "107:2:107:9", + "type": "var", + "value": "fn_name" + }, + { + "location": "107:13:107:42", + "type": "call", + "value": [ + { + "location": "107:13:107:26", + "type": "ref", + "value": [ + { + "value": "ref_to_string", + "location": "107:13:107:26", + "type": "var" + } + ] + }, + { + "value": [ + { + "location": "107:27:107:32", + "type": "var", + "value": "value" + }, + { + "location": "107:33:107:34", + "type": "number", + "value": 0 + }, + { + "location": "107:36:107:41", + "type": "string", + "value": "value" + } + ], + "location": "107:27:107:41", + "type": "ref" + } + ] + } + ] + }, + { + "location": "109:2:109:28", + "negated": true, + "terms": [ + { + "location": "109:6:109:14", + "type": "ref", + "value": [ + { + "type": "var", + "value": "contains", + "location": "109:6:109:14" + } + ] + }, + { + "value": "fn_name", + "location": "109:15:109:22", + "type": "var" + }, + { + "location": "109:24:109:27", + "type": "string", + "value": "$" + } + ] + }, + { + "location": "110:2:110:31", + "terms": [ + { + "location": "110:10:110:12", + "type": "ref", + "value": [ + { + "location": "110:10:110:12", + "type": "var", + "value": "internal" + }, + { + "location": "110:10:110:12", + "type": "string", + "value": "member_2" + } + ] + }, + { + "location": "110:2:110:9", + "type": "var", + "value": "fn_name" + }, + { + "location": "110:13:110:31", + "type": "var", + "value": "all_function_names" + } + ] + }, + { + "location": "111:2:111:38", + "terms": [ + { + "location": "111:2:111:22", + "type": "ref", + "value": [ + { + "location": "111:2:111:22", + "type": "var", + "value": "function_ret_in_args" + } + ] + }, + { + "location": "111:23:111:30", + "type": "var", + "value": "fn_name" + }, + { + "location": "111:32:111:37", + "type": "var", + "value": "value" + } + ] + } + ] + }, + { + "location": "114:1:119:2", + "head": { + "assign": true, + "value": { + "location": "114:28:114:64", + "type": "object", + "value": [ + [ + { + "value": "assign", + "location": "114:29:114:37", + "type": "string" + }, + { + "location": "114:39:114:64", + "type": "call", + "value": [ + { + "location": "114:39:114:56", + "type": "ref", + "value": [ + { + "location": "114:39:114:56", + "type": "var", + "value": "_find_assign_vars" + } + ] + }, + { + "location": "114:57:114:62", + "type": "var", + "value": "value" + } + ] + } + ] + ] + }, + "location": "114:1:114:64", + "ref": [ + { + "location": "114:1:114:11", + "type": "var", + "value": "_find_vars" + } + ], + "args": [ + { + "value": "value", + "location": "114:12:114:17", + "type": "var" + }, + { + "location": "114:19:114:23", + "type": "var", + "value": "last" + } + ] + }, + "body": [ + { + "location": "115:2:115:17", + "terms": [ + { + "location": "115:7:115:9", + "type": "ref", + "value": [ + { + "location": "115:7:115:9", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "115:2:115:6", + "type": "var", + "value": "last" + }, + { + "value": "terms", + "location": "115:10:115:17", + "type": "string" + } + ] + }, + { + "location": "116:2:116:24", + "terms": [ + { + "location": "116:16:116:18", + "type": "ref", + "value": [ + { + "location": "116:16:116:18", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "116:2:116:15", + "type": "ref", + "value": [ + { + "value": "value", + "location": "116:2:116:7", + "type": "var" + }, + { + "location": "116:8:116:9", + "type": "number", + "value": 0 + }, + { + "location": "116:11:116:15", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "116:19:116:24", + "type": "string", + "value": "ref" + } + ] + }, + { + "location": "117:2:117:33", + "terms": [ + { + "location": "117:25:117:27", + "type": "ref", + "value": [ + { + "location": "117:25:117:27", + "type": "var", + "value": "equal" + } + ] + }, + { + "type": "ref", + "value": [ + { + "value": "value", + "location": "117:2:117:7", + "type": "var" + }, + { + "location": "117:8:117:9", + "type": "number", + "value": 0 + }, + { + "location": "117:11:117:16", + "type": "string", + "value": "value" + }, + { + "location": "117:17:117:18", + "type": "number", + "value": 0 + }, + { + "location": "117:20:117:24", + "type": "string", + "value": "type" + } + ], + "location": "117:2:117:24" + }, + { + "location": "117:28:117:33", + "type": "string", + "value": "var" + } + ] + }, + { + "location": "118:2:118:37", + "terms": [ + { + "type": "ref", + "value": [ + { + "location": "118:26:118:28", + "type": "var", + "value": "equal" + } + ], + "location": "118:26:118:28" + }, + { + "location": "118:2:118:25", + "type": "ref", + "value": [ + { + "location": "118:2:118:7", + "type": "var", + "value": "value" + }, + { + "location": "118:8:118:9", + "type": "number", + "value": 0 + }, + { + "location": "118:11:118:16", + "type": "string", + "value": "value" + }, + { + "location": "118:17:118:18", + "type": "number", + "value": 0 + }, + { + "location": "118:20:118:25", + "type": "string", + "value": "value" + } + ] + }, + { + "location": "118:29:118:37", + "type": "string", + "value": "assign" + } + ] + } + ] + }, + { + "location": "125:1:130:2", + "head": { + "value": { + "location": "125:28:125:64", + "type": "object", + "value": [ + [ + { + "location": "125:29:125:37", + "type": "string", + "value": "assign" + }, + { + "location": "125:39:125:64", + "type": "call", + "value": [ + { + "location": "125:39:125:56", + "type": "ref", + "value": [ + { + "location": "125:39:125:56", + "type": "var", + "value": "_find_assign_vars" + } + ] + }, + { + "location": "125:57:125:62", + "type": "var", + "value": "value" + } + ] + } + ] + ] + }, + "location": "125:1:125:64", + "ref": [ + { + "type": "var", + "value": "_find_vars", + "location": "125:1:125:11" + } + ], + "args": [ + { + "location": "125:12:125:17", + "type": "var", + "value": "value" + }, + { + "location": "125:19:125:23", + "type": "var", + "value": "last" + } + ], + "assign": true + }, + "body": [ + { + "location": "126:2:126:17", + "terms": [ + { + "location": "126:7:126:9", + "type": "ref", + "value": [ + { + "value": "equal", + "location": "126:7:126:9", + "type": "var" + } + ] + }, + { + "location": "126:2:126:6", + "type": "var", + "value": "last" + }, + { + "location": "126:10:126:17", + "type": "string", + "value": "terms" + } + ] + }, + { + "location": "127:2:127:24", + "terms": [ + { + "location": "127:16:127:18", + "type": "ref", + "value": [ + { + "location": "127:16:127:18", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "127:2:127:15", + "type": "ref", + "value": [ + { + "location": "127:2:127:7", + "type": "var", + "value": "value" + }, + { + "location": "127:8:127:9", + "type": "number", + "value": 0 + }, + { + "location": "127:11:127:15", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "127:19:127:24", + "type": "string", + "value": "ref" + } + ] + }, + { + "location": "128:2:128:33", + "terms": [ + { + "location": "128:25:128:27", + "type": "ref", + "value": [ + { + "location": "128:25:128:27", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "128:2:128:24", + "type": "ref", + "value": [ + { + "value": "value", + "location": "128:2:128:7", + "type": "var" + }, + { + "location": "128:8:128:9", + "type": "number", + "value": 0 + }, + { + "value": "value", + "location": "128:11:128:16", + "type": "string" + }, + { + "location": "128:17:128:18", + "type": "number", + "value": 0 + }, + { + "location": "128:20:128:24", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "128:28:128:33", + "type": "string", + "value": "var" + } + ] + }, + { + "location": "129:2:129:33", + "terms": [ + { + "location": "129:26:129:28", + "type": "ref", + "value": [ + { + "value": "equal", + "location": "129:26:129:28", + "type": "var" + } + ] + }, + { + "type": "ref", + "value": [ + { + "value": "value", + "location": "129:2:129:7", + "type": "var" + }, + { + "type": "number", + "value": 0, + "location": "129:8:129:9" + }, + { + "location": "129:11:129:16", + "type": "string", + "value": "value" + }, + { + "location": "129:17:129:18", + "type": "number", + "value": 0 + }, + { + "location": "129:20:129:25", + "type": "string", + "value": "value" + } + ], + "location": "129:2:129:25" + }, + { + "location": "129:29:129:33", + "type": "string", + "value": "eq" + } + ] + } + ] + }, + { + "location": "132:1:132:77", + "head": { + "args": [ + { + "location": "132:12:132:17", + "type": "var", + "value": "value" + }, + { + "location": "132:19:132:20", + "type": "var", + "value": "$3" + } + ], + "assign": true, + "value": { + "location": "132:25:132:54", + "type": "object", + "value": [ + [ + { + "location": "132:26:132:31", + "type": "string", + "value": "ref" + }, + { + "location": "132:33:132:54", + "type": "call", + "value": [ + { + "location": "132:33:132:46", + "type": "ref", + "value": [ + { + "value": "find_ref_vars", + "location": "132:33:132:46", + "type": "var" + } + ] + }, + { + "location": "132:47:132:52", + "type": "var", + "value": "value" + } + ] + } + ] + ] + }, + "location": "132:1:132:54", + "ref": [ + { + "location": "132:1:132:11", + "type": "var", + "value": "_find_vars" + } + ] + }, + "body": [ + { + "location": "132:58:132:77", + "terms": [ + { + "value": [ + { + "location": "132:69:132:71", + "type": "var", + "value": "equal" + } + ], + "location": "132:69:132:71", + "type": "ref" + }, + { + "location": "132:58:132:68", + "type": "ref", + "value": [ + { + "location": "132:58:132:63", + "type": "var", + "value": "value" + }, + { + "location": "132:64:132:68", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "132:72:132:77", + "type": "string", + "value": "ref" + } + ] + } + ] + }, + { + "location": "134:1:137:2", + "head": { + "location": "134:1:134:70", + "ref": [ + { + "location": "134:1:134:11", + "type": "var", + "value": "_find_vars" + } + ], + "args": [ + { + "location": "134:12:134:17", + "type": "var", + "value": "value" + }, + { + "value": "last", + "location": "134:19:134:23", + "type": "var" + } + ], + "assign": true, + "value": { + "location": "134:28:134:70", + "type": "object", + "value": [ + [ + { + "location": "134:29:134:37", + "type": "string", + "value": "somein" + }, + { + "location": "134:39:134:70", + "type": "call", + "value": [ + { + "location": "134:39:134:62", + "type": "ref", + "value": [ + { + "location": "134:39:134:62", + "type": "var", + "value": "_find_some_in_decl_vars" + } + ] + }, + { + "type": "var", + "value": "value", + "location": "134:63:134:68" + } + ] + } + ] + ] + } + }, + "body": [ + { + "location": "135:2:135:19", + "terms": [ + { + "location": "135:7:135:9", + "type": "ref", + "value": [ + { + "type": "var", + "value": "equal", + "location": "135:7:135:9" + } + ] + }, + { + "location": "135:2:135:6", + "type": "var", + "value": "last" + }, + { + "value": "symbols", + "location": "135:10:135:19", + "type": "string" + } + ] + }, + { + "location": "136:2:136:25", + "terms": [ + { + "location": "136:16:136:18", + "type": "ref", + "value": [ + { + "type": "var", + "value": "equal", + "location": "136:16:136:18" + } + ] + }, + { + "location": "136:2:136:15", + "type": "ref", + "value": [ + { + "location": "136:2:136:7", + "type": "var", + "value": "value" + }, + { + "location": "136:8:136:9", + "type": "number", + "value": 0 + }, + { + "location": "136:11:136:15", + "type": "string", + "value": "type" + } + ] + }, + { + "value": "call", + "location": "136:19:136:25", + "type": "string" + } + ] + } + ] + }, + { + "body": [ + { + "location": "140:2:140:19", + "terms": [ + { + "location": "140:7:140:9", + "type": "ref", + "value": [ + { + "location": "140:7:140:9", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "140:2:140:6", + "type": "var", + "value": "last" + }, + { + "location": "140:10:140:19", + "type": "string", + "value": "symbols" + } + ] + }, + { + "location": "141:2:141:25", + "terms": [ + { + "location": "141:16:141:18", + "type": "ref", + "value": [ + { + "location": "141:16:141:18", + "type": "var", + "value": "neq" + } + ] + }, + { + "location": "141:2:141:15", + "type": "ref", + "value": [ + { + "location": "141:2:141:7", + "type": "var", + "value": "value" + }, + { + "location": "141:8:141:9", + "type": "number", + "value": 0 + }, + { + "location": "141:11:141:15", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "141:19:141:25", + "type": "string", + "value": "call" + } + ] + } + ], + "location": "139:1:142:2", + "head": { + "ref": [ + { + "value": "_find_vars", + "location": "139:1:139:11", + "type": "var" + } + ], + "args": [ + { + "type": "var", + "value": "value", + "location": "139:12:139:17" + }, + { + "location": "139:19:139:23", + "type": "var", + "value": "last" + } + ], + "assign": true, + "value": { + "type": "object", + "value": [ + [ + { + "location": "139:29:139:35", + "type": "string", + "value": "some" + }, + { + "type": "call", + "value": [ + { + "location": "139:37:139:57", + "type": "ref", + "value": [ + { + "location": "139:37:139:57", + "type": "var", + "value": "_find_some_decl_vars" + } + ] + }, + { + "location": "139:58:139:63", + "type": "var", + "value": "value" + } + ], + "location": "139:37:139:65" + } + ] + ], + "location": "139:28:139:65" + }, + "location": "139:1:139:65" + } + }, + { + "location": "144:1:147:2", + "head": { + "assign": true, + "value": { + "location": "144:28:144:62", + "type": "object", + "value": [ + [ + { + "location": "144:29:144:36", + "type": "string", + "value": "every" + }, + { + "location": "144:38:144:62", + "type": "call", + "value": [ + { + "location": "144:38:144:54", + "type": "ref", + "value": [ + { + "location": "144:38:144:54", + "type": "var", + "value": "_find_every_vars" + } + ] + }, + { + "location": "144:55:144:60", + "type": "var", + "value": "value" + } + ] + } + ] + ] + }, + "location": "144:1:144:62", + "ref": [ + { + "value": "_find_vars", + "location": "144:1:144:11", + "type": "var" + } + ], + "args": [ + { + "type": "var", + "value": "value", + "location": "144:12:144:17" + }, + { + "value": "last", + "location": "144:19:144:23", + "type": "var" + } + ] + }, + "body": [ + { + "location": "145:2:145:17", + "terms": [ + { + "location": "145:7:145:9", + "type": "ref", + "value": [ + { + "location": "145:7:145:9", + "type": "var", + "value": "equal" + } + ] + }, + { + "type": "var", + "value": "last", + "location": "145:2:145:6" + }, + { + "location": "145:10:145:17", + "type": "string", + "value": "terms" + } + ] + }, + { + "location": "146:2:146:14", + "terms": { + "location": "146:2:146:14", + "type": "ref", + "value": [ + { + "location": "146:2:146:7", + "type": "var", + "value": "value" + }, + { + "value": "domain", + "location": "146:8:146:14", + "type": "string" + } + ] + } + } + ] + }, + { + "head": { + "args": [ + { + "location": "149:12:149:17", + "type": "var", + "value": "value" + }, + { + "location": "149:19:149:23", + "type": "var", + "value": "last" + } + ], + "assign": true, + "value": { + "location": "149:28:149:46", + "type": "object", + "value": [ + [ + { + "location": "149:29:149:35", + "type": "string", + "value": "args" + }, + { + "location": "149:37:149:45", + "type": "var", + "value": "arg_vars" + } + ] + ] + }, + "location": "149:1:149:46", + "ref": [ + { + "location": "149:1:149:11", + "type": "var", + "value": "_find_vars" + } + ] + }, + "body": [ + { + "terms": [ + { + "location": "150:7:150:9", + "type": "ref", + "value": [ + { + "location": "150:7:150:9", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "150:2:150:6", + "type": "var", + "value": "last" + }, + { + "value": "args", + "location": "150:10:150:16", + "type": "string" + } + ], + "location": "150:2:150:16" + }, + { + "location": "152:2:155:3", + "terms": [ + { + "location": "152:11:152:13", + "type": "ref", + "value": [ + { + "location": "152:11:152:13", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "152:2:152:10", + "type": "var", + "value": "arg_vars" + }, + { + "location": "152:14:155:3", + "type": "arraycomprehension", + "value": { + "term": { + "location": "152:15:152:18", + "type": "var", + "value": "arg" + }, + "body": [ + { + "location": "153:3:153:20", + "terms": { + "location": "153:3:153:7", + "symbols": [ + { + "location": "153:8:153:20", + "type": "call", + "value": [ + { + "location": "153:12:153:14", + "type": "ref", + "value": [ + { + "location": "153:12:153:14", + "type": "var", + "value": "internal" + }, + { + "location": "153:12:153:14", + "type": "string", + "value": "member_2" + } + ] + }, + { + "value": "arg", + "location": "153:8:153:11", + "type": "var" + }, + { + "value": "value", + "location": "153:15:153:20", + "type": "var" + } + ] + } + ] + } + }, + { + "location": "154:3:154:20", + "terms": [ + { + "location": "154:12:154:14", + "type": "ref", + "value": [ + { + "location": "154:12:154:14", + "type": "var", + "value": "equal" + } + ] + }, + { + "value": [ + { + "location": "154:3:154:6", + "type": "var", + "value": "arg" + }, + { + "location": "154:7:154:11", + "type": "string", + "value": "type" + } + ], + "location": "154:3:154:11", + "type": "ref" + }, + { + "location": "154:15:154:20", + "type": "string", + "value": "var" + } + ] + } + ] + } + } + ] + }, + { + "location": "157:2:157:21", + "terms": [ + { + "location": "157:18:157:19", + "type": "ref", + "value": [ + { + "location": "157:18:157:19", + "type": "var", + "value": "gt" + } + ] + }, + { + "location": "157:2:157:17", + "type": "call", + "value": [ + { + "value": [ + { + "location": "157:2:157:7", + "type": "var", + "value": "count" + } + ], + "location": "157:2:157:7", + "type": "ref" + }, + { + "location": "157:8:157:16", + "type": "var", + "value": "arg_vars" + } + ] + }, + { + "location": "157:20:157:21", + "type": "number", + "value": 0 + } + ] + } + ], + "location": "149:1:158:2" + }, + { + "location": "160:1:163:2", + "head": { + "assign": true, + "value": { + "location": "160:22:160:40", + "type": "call", + "value": [ + { + "value": [ + { + "location": "160:22:160:29", + "type": "var", + "value": "sprintf" + } + ], + "location": "160:22:160:29", + "type": "ref" + }, + { + "location": "160:30:160:34", + "type": "string", + "value": "%d" + }, + { + "location": "160:36:160:39", + "type": "array", + "value": [ + { + "location": "160:37:160:38", + "type": "var", + "value": "i" + } + ] + } + ] + }, + "location": "160:1:160:40", + "ref": [ + { + "location": "160:1:160:12", + "type": "var", + "value": "_rule_index" + } + ], + "args": [ + { + "type": "var", + "value": "rule", + "location": "160:13:160:17" + } + ] + }, + "body": [ + { + "location": "161:2:161:21", + "terms": { + "location": "161:2:161:6", + "symbols": [ + { + "location": "161:7:161:21", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "161:12:161:14", + "type": "var", + "value": "internal" + }, + { + "location": "161:12:161:14", + "type": "string", + "value": "member_3" + } + ], + "location": "161:12:161:14" + }, + { + "location": "161:7:161:8", + "type": "var", + "value": "i" + }, + { + "value": "r", + "location": "161:10:161:11", + "type": "var" + }, + { + "location": "161:15:161:21", + "type": "var", + "value": "_rules" + } + ] + } + ] + } + }, + { + "location": "162:2:162:11", + "terms": [ + { + "location": "162:4:162:6", + "type": "ref", + "value": [ + { + "location": "162:4:162:6", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "162:2:162:3", + "type": "var", + "value": "r" + }, + { + "value": "rule", + "location": "162:7:162:11", + "type": "var" + } + ] + } + ] + }, + { + "location": "170:1:174:2", + "annotations": [ + { + "scope": "rule", + "description": "traverses all nodes under provided node (using `walk`), and returns an array with\nall variables declared via assignment (:=), `some`, `every` and in comprehensions\nDEPRECATED: uses ast.found.vars instead\n", + "location": "165:1:169:44" + } + ], + "head": { + "assign": true, + "value": { + "value": { + "term": { + "type": "var", + "value": "var", + "location": "170:21:170:24" + }, + "body": [ + { + "location": "171:2:171:27", + "terms": [ + { + "location": "171:2:171:6", + "type": "ref", + "value": [ + { + "location": "171:2:171:6", + "type": "var", + "value": "walk" + } + ] + }, + { + "location": "171:7:171:11", + "type": "var", + "value": "node" + }, + { + "location": "171:13:171:26", + "type": "array", + "value": [ + { + "location": "171:14:171:18", + "type": "var", + "value": "path" + }, + { + "location": "171:20:171:25", + "type": "var", + "value": "value" + } + ] + } + ] + }, + { + "location": "173:2:173:50", + "terms": [ + { + "value": [ + { + "location": "173:6:173:8", + "type": "var", + "value": "assign" + } + ], + "location": "173:6:173:8", + "type": "ref" + }, + { + "location": "173:2:173:5", + "type": "var", + "value": "var" + }, + { + "location": "173:9:174:2", + "type": "ref", + "value": [ + { + "location": "173:9:173:44", + "type": "call", + "value": [ + { + "location": "173:9:173:19", + "type": "ref", + "value": [ + { + "location": "173:9:173:19", + "type": "var", + "value": "_find_vars" + } + ] + }, + { + "value": "value", + "location": "173:20:173:25", + "type": "var" + }, + { + "location": "173:27:173:44", + "type": "call", + "value": [ + { + "location": "173:27:173:37", + "type": "ref", + "value": [ + { + "location": "173:27:173:32", + "type": "var", + "value": "regal" + }, + { + "location": "173:33:173:37", + "type": "string", + "value": "last" + } + ] + }, + { + "location": "173:38:173:42", + "type": "var", + "value": "path" + } + ] + } + ] + }, + { + "location": "173:45:173:46", + "type": "var", + "value": "$4" + }, + { + "location": "173:48:173:49", + "type": "var", + "value": "$5" + } + ] + } + ] + } + ] + }, + "location": "170:20:174:2", + "type": "arraycomprehension" + }, + "location": "170:1:174:2", + "ref": [ + { + "location": "170:1:170:10", + "type": "var", + "value": "find_vars" + } + ], + "args": [ + { + "location": "170:11:170:15", + "type": "var", + "value": "node" + } + ] + } + }, + { + "location": "178:1:178:22", + "head": { + "location": "178:1:178:22", + "ref": [ + { + "location": "178:1:178:7", + "type": "var", + "value": "_rules" + } + ], + "assign": true, + "value": { + "location": "178:11:178:22", + "type": "ref", + "value": [ + { + "location": "178:11:178:16", + "type": "var", + "value": "input" + }, + { + "location": "178:17:178:22", + "type": "string", + "value": "rules" + } + ] + } + } + }, + { + "location": "180:1:180:79", + "head": { + "location": "180:1:180:60", + "ref": [ + { + "location": "180:1:180:7", + "type": "var", + "value": "_rules" + } + ], + "assign": true, + "value": { + "location": "180:11:180:60", + "type": "ref", + "value": [ + { + "type": "var", + "value": "data", + "location": "180:11:180:15" + }, + { + "value": "workspace", + "location": "180:16:180:25", + "type": "string" + }, + { + "location": "180:26:180:32", + "type": "string", + "value": "parsed" + }, + { + "location": "180:33:180:53", + "type": "ref", + "value": [ + { + "location": "180:33:180:38", + "type": "var", + "value": "input" + }, + { + "location": "180:39:180:44", + "type": "string", + "value": "regal" + }, + { + "type": "string", + "value": "file", + "location": "180:45:180:49" + }, + { + "type": "string", + "value": "uri", + "location": "180:50:180:53" + } + ] + }, + { + "location": "180:55:180:60", + "type": "string", + "value": "rules" + } + ] + } + }, + "body": [ + { + "location": "180:64:180:79", + "negated": true, + "terms": { + "location": "180:68:180:79", + "type": "ref", + "value": [ + { + "value": "input", + "location": "180:68:180:73", + "type": "var" + }, + { + "value": "rules", + "location": "180:74:180:79", + "type": "string" + } + ] + } + } + ] + }, + { + "head": { + "location": "193:1:193:45", + "ref": [ + { + "location": "193:1:193:6", + "type": "var", + "value": "found" + }, + { + "location": "193:7:193:11", + "type": "string", + "value": "vars" + }, + { + "location": "193:12:193:22", + "type": "var", + "value": "rule_index" + }, + { + "location": "193:24:193:31", + "type": "var", + "value": "context" + } + ], + "key": { + "location": "193:42:193:45", + "type": "var", + "value": "var" + } + }, + "body": [ + { + "location": "194:2:194:24", + "terms": { + "symbols": [ + { + "location": "194:7:194:24", + "type": "call", + "value": [ + { + "location": "194:15:194:17", + "type": "ref", + "value": [ + { + "location": "194:15:194:17", + "type": "var", + "value": "internal" + }, + { + "location": "194:15:194:17", + "type": "string", + "value": "member_3" + } + ] + }, + { + "location": "194:7:194:8", + "type": "var", + "value": "i" + }, + { + "location": "194:10:194:14", + "type": "var", + "value": "rule" + }, + { + "location": "194:18:194:24", + "type": "var", + "value": "_rules" + } + ] + } + ], + "location": "194:2:194:6" + } + }, + { + "location": "197:2:197:34", + "terms": [ + { + "value": [ + { + "value": "assign", + "location": "197:13:197:15", + "type": "var" + } + ], + "location": "197:13:197:15", + "type": "ref" + }, + { + "location": "197:2:197:12", + "type": "var", + "value": "rule_index" + }, + { + "location": "197:16:197:34", + "type": "call", + "value": [ + { + "location": "197:16:197:23", + "type": "ref", + "value": [ + { + "value": "sprintf", + "location": "197:16:197:23", + "type": "var" + } + ] + }, + { + "location": "197:24:197:28", + "type": "string", + "value": "%d" + }, + { + "location": "197:30:197:33", + "type": "array", + "value": [ + { + "value": "i", + "location": "197:31:197:32", + "type": "var" + } + ] + } + ] + } + ] + }, + { + "location": "199:2:199:27", + "terms": [ + { + "location": "199:2:199:6", + "type": "ref", + "value": [ + { + "location": "199:2:199:6", + "type": "var", + "value": "walk" + } + ] + }, + { + "value": "rule", + "location": "199:7:199:11", + "type": "var" + }, + { + "location": "199:13:199:26", + "type": "array", + "value": [ + { + "location": "199:14:199:18", + "type": "var", + "value": "path" + }, + { + "location": "199:20:199:25", + "type": "var", + "value": "value" + } + ] + } + ] + }, + { + "location": "201:2:201:59", + "terms": { + "location": "201:2:201:6", + "symbols": [ + { + "location": "201:7:201:59", + "type": "call", + "value": [ + { + "location": "201:21:201:23", + "type": "ref", + "value": [ + { + "location": "201:21:201:23", + "type": "var", + "value": "internal" + }, + { + "type": "string", + "value": "member_3", + "location": "201:21:201:23" + } + ] + }, + { + "location": "201:7:201:14", + "type": "var", + "value": "context" + }, + { + "location": "201:16:201:20", + "type": "var", + "value": "vars" + }, + { + "location": "201:24:201:59", + "type": "call", + "value": [ + { + "location": "201:24:201:34", + "type": "ref", + "value": [ + { + "location": "201:24:201:34", + "type": "var", + "value": "_find_vars" + } + ] + }, + { + "location": "201:35:201:40", + "type": "var", + "value": "value" + }, + { + "location": "201:42:201:59", + "type": "call", + "value": [ + { + "location": "201:42:201:52", + "type": "ref", + "value": [ + { + "location": "201:42:201:47", + "type": "var", + "value": "regal" + }, + { + "location": "201:48:201:52", + "type": "string", + "value": "last" + } + ] + }, + { + "type": "var", + "value": "path", + "location": "201:53:201:57" + } + ] + } + ] + } + ] + } + ] + } + }, + { + "location": "202:2:202:18", + "terms": { + "location": "202:2:202:6", + "symbols": [ + { + "location": "202:7:202:18", + "type": "call", + "value": [ + { + "location": "202:11:202:13", + "type": "ref", + "value": [ + { + "location": "202:11:202:13", + "type": "var", + "value": "internal" + }, + { + "location": "202:11:202:13", + "type": "string", + "value": "member_2" + } + ] + }, + { + "type": "var", + "value": "var", + "location": "202:7:202:10" + }, + { + "type": "var", + "value": "vars", + "location": "202:14:202:18" + } + ] + } + ] + } + } + ], + "location": "193:1:203:2", + "annotations": [ + { + "location": "182:1:192:10", + "scope": "rule", + "description": "object containing all variables found in the input AST, keyed first by the index of\nthe rule where the variables were found (as a numeric string), and then the context\nof the variable, which will be one of:\n- term\n- assign\n- every\n- some\n- somein\n- ref\n" + } + ] + }, + { + "location": "207:1:216:2", + "annotations": [ + { + "location": "205:1:206:41", + "scope": "rule", + "description": "all refs foundd in module" + } + ], + "head": { + "ref": [ + { + "value": "found", + "location": "207:1:207:6", + "type": "var" + }, + { + "location": "207:7:207:11", + "type": "string", + "value": "refs" + }, + { + "location": "207:12:207:22", + "type": "var", + "value": "rule_index" + } + ], + "key": { + "value": "value", + "location": "207:33:207:38", + "type": "var" + }, + "location": "207:1:207:38" + }, + "body": [ + { + "location": "208:2:208:24", + "terms": { + "location": "208:2:208:6", + "symbols": [ + { + "location": "208:7:208:24", + "type": "call", + "value": [ + { + "location": "208:15:208:17", + "type": "ref", + "value": [ + { + "location": "208:15:208:17", + "type": "var", + "value": "internal" + }, + { + "location": "208:15:208:17", + "type": "string", + "value": "member_3" + } + ] + }, + { + "location": "208:7:208:8", + "type": "var", + "value": "i" + }, + { + "location": "208:10:208:14", + "type": "var", + "value": "rule" + }, + { + "location": "208:18:208:24", + "type": "var", + "value": "_rules" + } + ] + } + ] + } + }, + { + "location": "211:2:211:34", + "terms": [ + { + "location": "211:13:211:15", + "type": "ref", + "value": [ + { + "location": "211:13:211:15", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "211:2:211:12", + "type": "var", + "value": "rule_index" + }, + { + "value": [ + { + "location": "211:16:211:23", + "type": "ref", + "value": [ + { + "location": "211:16:211:23", + "type": "var", + "value": "sprintf" + } + ] + }, + { + "location": "211:24:211:28", + "type": "string", + "value": "%d" + }, + { + "location": "211:30:211:33", + "type": "array", + "value": [ + { + "location": "211:31:211:32", + "type": "var", + "value": "i" + } + ] + } + ], + "location": "211:16:211:34", + "type": "call" + } + ] + }, + { + "location": "213:2:213:24", + "terms": [ + { + "location": "213:2:213:6", + "type": "ref", + "value": [ + { + "location": "213:2:213:6", + "type": "var", + "value": "walk" + } + ] + }, + { + "location": "213:7:213:11", + "type": "var", + "value": "rule" + }, + { + "location": "213:13:213:23", + "type": "array", + "value": [ + { + "location": "213:14:213:15", + "type": "var", + "value": "$6" + }, + { + "location": "213:17:213:22", + "type": "var", + "value": "value" + } + ] + } + ] + }, + { + "location": "215:2:215:15", + "terms": [ + { + "type": "ref", + "value": [ + { + "location": "215:2:215:8", + "type": "var", + "value": "is_ref" + } + ], + "location": "215:2:215:8" + }, + { + "location": "215:9:215:14", + "type": "var", + "value": "value" + } + ] + } + ] + }, + { + "annotations": [ + { + "location": "218:1:219:44", + "scope": "rule", + "description": "all symbols foundd in module" + } + ], + "head": { + "ref": [ + { + "location": "220:1:220:6", + "type": "var", + "value": "found" + }, + { + "location": "220:7:220:14", + "type": "string", + "value": "symbols" + }, + { + "value": "rule_index", + "location": "220:15:220:25", + "type": "var" + } + ], + "key": { + "location": "220:36:220:49", + "type": "ref", + "value": [ + { + "location": "220:36:220:41", + "type": "var", + "value": "value" + }, + { + "type": "string", + "value": "symbols", + "location": "220:42:220:49" + } + ] + }, + "location": "220:1:220:49" + }, + "body": [ + { + "location": "221:2:221:24", + "terms": { + "location": "221:2:221:6", + "symbols": [ + { + "location": "221:7:221:24", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "221:15:221:17", + "type": "var", + "value": "internal" + }, + { + "type": "string", + "value": "member_3", + "location": "221:15:221:17" + } + ], + "location": "221:15:221:17" + }, + { + "location": "221:7:221:8", + "type": "var", + "value": "i" + }, + { + "location": "221:10:221:14", + "type": "var", + "value": "rule" + }, + { + "location": "221:18:221:24", + "type": "var", + "value": "_rules" + } + ] + } + ] + } + }, + { + "location": "224:2:224:34", + "terms": [ + { + "location": "224:13:224:15", + "type": "ref", + "value": [ + { + "location": "224:13:224:15", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "224:2:224:12", + "type": "var", + "value": "rule_index" + }, + { + "location": "224:16:224:34", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "224:16:224:23", + "type": "var", + "value": "sprintf" + } + ], + "location": "224:16:224:23" + }, + { + "location": "224:24:224:28", + "type": "string", + "value": "%d" + }, + { + "location": "224:30:224:33", + "type": "array", + "value": [ + { + "location": "224:31:224:32", + "type": "var", + "value": "i" + } + ] + } + ] + } + ] + }, + { + "location": "226:2:226:24", + "terms": [ + { + "location": "226:2:226:6", + "type": "ref", + "value": [ + { + "type": "var", + "value": "walk", + "location": "226:2:226:6" + } + ] + }, + { + "location": "226:7:226:11", + "type": "var", + "value": "rule" + }, + { + "location": "226:13:226:23", + "type": "array", + "value": [ + { + "location": "226:14:226:15", + "type": "var", + "value": "$7" + }, + { + "value": "value", + "location": "226:17:226:22", + "type": "var" + } + ] + } + ] + } + ], + "location": "220:1:227:2" + }, + { + "location": "231:1:240:2", + "annotations": [ + { + "location": "229:1:230:50", + "scope": "rule", + "description": "all comprehensions found in module" + } + ], + "head": { + "ref": [ + { + "location": "231:1:231:6", + "type": "var", + "value": "found" + }, + { + "location": "231:7:231:21", + "type": "string", + "value": "comprehensions" + }, + { + "location": "231:22:231:32", + "type": "var", + "value": "rule_index" + } + ], + "key": { + "location": "231:43:231:48", + "type": "var", + "value": "value" + }, + "location": "231:1:231:48" + }, + "body": [ + { + "location": "232:2:232:24", + "terms": { + "location": "232:2:232:6", + "symbols": [ + { + "location": "232:7:232:24", + "type": "call", + "value": [ + { + "location": "232:15:232:17", + "type": "ref", + "value": [ + { + "type": "var", + "value": "internal", + "location": "232:15:232:17" + }, + { + "location": "232:15:232:17", + "type": "string", + "value": "member_3" + } + ] + }, + { + "location": "232:7:232:8", + "type": "var", + "value": "i" + }, + { + "type": "var", + "value": "rule", + "location": "232:10:232:14" + }, + { + "value": "_rules", + "location": "232:18:232:24", + "type": "var" + } + ] + } + ] + } + }, + { + "location": "235:2:235:34", + "terms": [ + { + "location": "235:13:235:15", + "type": "ref", + "value": [ + { + "location": "235:13:235:15", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "235:2:235:12", + "type": "var", + "value": "rule_index" + }, + { + "location": "235:16:235:34", + "type": "call", + "value": [ + { + "location": "235:16:235:23", + "type": "ref", + "value": [ + { + "location": "235:16:235:23", + "type": "var", + "value": "sprintf" + } + ] + }, + { + "location": "235:24:235:28", + "type": "string", + "value": "%d" + }, + { + "location": "235:30:235:33", + "type": "array", + "value": [ + { + "value": "i", + "location": "235:31:235:32", + "type": "var" + } + ] + } + ] + } + ] + }, + { + "location": "237:2:237:24", + "terms": [ + { + "location": "237:2:237:6", + "type": "ref", + "value": [ + { + "value": "walk", + "location": "237:2:237:6", + "type": "var" + } + ] + }, + { + "type": "var", + "value": "rule", + "location": "237:7:237:11" + }, + { + "value": [ + { + "value": "$8", + "location": "237:14:237:15", + "type": "var" + }, + { + "value": "value", + "location": "237:17:237:22", + "type": "var" + } + ], + "location": "237:13:237:23", + "type": "array" + } + ] + }, + { + "location": "239:2:239:81", + "terms": [ + { + "type": "ref", + "value": [ + { + "location": "239:13:239:15", + "type": "var", + "value": "internal" + }, + { + "location": "239:13:239:15", + "type": "string", + "value": "member_2" + } + ], + "location": "239:13:239:15" + }, + { + "location": "239:2:239:12", + "type": "ref", + "value": [ + { + "location": "239:2:239:7", + "type": "var", + "value": "value" + }, + { + "location": "239:8:239:12", + "type": "string", + "value": "type" + } + ] + }, + { + "value": [ + { + "type": "string", + "value": "arraycomprehension", + "location": "239:17:239:37" + }, + { + "value": "objectcomprehension", + "location": "239:39:239:60", + "type": "string" + }, + { + "location": "239:62:239:80", + "type": "string", + "value": "setcomprehension" + } + ], + "location": "239:16:239:81", + "type": "set" + } + ] + } + ] + }, + { + "head": { + "assign": true, + "value": { + "location": "248:45:253:2", + "type": "arraycomprehension", + "value": { + "term": { + "location": "248:46:248:49", + "type": "var", + "value": "var" + }, + "body": [ + { + "location": "249:2:249:44", + "terms": [ + { + "location": "249:6:249:8", + "type": "ref", + "value": [ + { + "type": "var", + "value": "assign", + "location": "249:6:249:8" + } + ] + }, + { + "value": "var", + "location": "249:2:249:5", + "type": "var" + }, + { + "location": "249:9:249:44", + "type": "ref", + "value": [ + { + "location": "249:9:249:14", + "type": "var", + "value": "found" + }, + { + "location": "249:15:249:19", + "type": "string", + "value": "vars" + }, + { + "type": "call", + "value": [ + { + "location": "249:20:249:31", + "type": "ref", + "value": [ + { + "location": "249:20:249:31", + "type": "var", + "value": "_rule_index" + } + ] + }, + { + "location": "249:32:249:36", + "type": "var", + "value": "rule" + } + ], + "location": "249:20:249:38" + }, + { + "location": "249:39:249:40", + "type": "var", + "value": "$9" + }, + { + "location": "249:42:249:43", + "type": "var", + "value": "$10" + } + ] + } + ] + }, + { + "terms": [ + { + "location": "251:6:251:17", + "type": "ref", + "value": [ + { + "location": "251:6:251:17", + "type": "var", + "value": "is_wildcard" + } + ] + }, + { + "type": "var", + "value": "var", + "location": "251:18:251:21" + } + ], + "location": "251:2:251:22", + "negated": true + }, + { + "location": "252:2:252:64", + "terms": [ + { + "location": "252:2:252:18", + "type": "ref", + "value": [ + { + "location": "252:2:252:18", + "type": "var", + "value": "_before_location" + } + ] + }, + { + "type": "var", + "value": "rule", + "location": "252:19:252:23" + }, + { + "location": "252:25:252:28", + "type": "var", + "value": "var" + }, + { + "location": "252:30:252:64", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "252:30:252:34", + "type": "var", + "value": "util" + }, + { + "location": "252:35:252:53", + "type": "string", + "value": "to_location_object" + } + ], + "location": "252:30:252:53" + }, + { + "location": "252:54:252:62", + "type": "var", + "value": "location" + } + ] + } + ] + } + ] + } + }, + "location": "248:1:253:2", + "ref": [ + { + "location": "248:1:248:25", + "type": "var", + "value": "find_vars_in_local_scope" + } + ], + "args": [ + { + "location": "248:26:248:30", + "type": "var", + "value": "rule" + }, + { + "type": "var", + "value": "location", + "location": "248:32:248:40" + } + ] + }, + "location": "248:1:253:2", + "annotations": [ + { + "location": "242:1:247:28", + "scope": "rule", + "description": "finds all vars declared in `rule` *before* the `location` provided\nnote: this isn't 100% accurate, as it doesn't take into account `=`\nassignments / unification, but it's likely good enough since other rules\nrecommend against those\n" + } + ] + }, + { + "location": "255:1:262:2", + "head": { + "args": [ + { + "location": "255:15:255:23", + "type": "var", + "value": "location" + } + ], + "assign": true, + "value": { + "location": "255:28:255:31", + "type": "var", + "value": "end" + }, + "location": "255:1:255:31", + "ref": [ + { + "location": "255:1:255:14", + "type": "var", + "value": "_end_location" + } + ] + }, + "body": [ + { + "terms": [ + { + "value": [ + { + "value": "assign", + "location": "256:6:256:8", + "type": "var" + } + ], + "location": "256:6:256:8", + "type": "ref" + }, + { + "location": "256:2:256:5", + "type": "var", + "value": "loc" + }, + { + "value": [ + { + "location": "256:9:256:32", + "type": "ref", + "value": [ + { + "location": "256:9:256:13", + "type": "var", + "value": "util" + }, + { + "location": "256:14:256:32", + "type": "string", + "value": "to_location_object" + } + ] + }, + { + "type": "var", + "value": "location", + "location": "256:33:256:41" + } + ], + "location": "256:9:256:42", + "type": "call" + } + ], + "location": "256:2:256:42" + }, + { + "location": "257:2:257:32", + "terms": [ + { + "location": "257:8:257:10", + "type": "ref", + "value": [ + { + "location": "257:8:257:10", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "257:2:257:7", + "type": "var", + "value": "lines" + }, + { + "location": "257:11:257:32", + "type": "call", + "value": [ + { + "location": "257:11:257:16", + "type": "ref", + "value": [ + { + "location": "257:11:257:16", + "type": "var", + "value": "split" + } + ] + }, + { + "location": "257:17:257:25", + "type": "ref", + "value": [ + { + "location": "257:17:257:20", + "type": "var", + "value": "loc" + }, + { + "location": "257:21:257:25", + "type": "string", + "value": "text" + } + ] + }, + { + "location": "257:27:257:31", + "type": "string", + "value": "\n" + } + ] + } + ] + }, + { + "location": "258:2:261:3", + "terms": [ + { + "value": [ + { + "type": "var", + "value": "assign", + "location": "258:6:258:8" + } + ], + "location": "258:6:258:8", + "type": "ref" + }, + { + "location": "258:2:258:5", + "type": "var", + "value": "end" + }, + { + "location": "258:9:261:3", + "type": "object", + "value": [ + [ + { + "location": "259:3:259:8", + "type": "string", + "value": "row" + }, + { + "value": [ + { + "value": [ + { + "location": "259:35:259:36", + "type": "var", + "value": "minus" + } + ], + "location": "259:35:259:36", + "type": "ref" + }, + { + "value": [ + { + "location": "259:19:259:20", + "type": "ref", + "value": [ + { + "location": "259:19:259:20", + "type": "var", + "value": "plus" + } + ] + }, + { + "location": "259:11:259:18", + "type": "ref", + "value": [ + { + "location": "259:11:259:14", + "type": "var", + "value": "loc" + }, + { + "location": "259:15:259:18", + "type": "string", + "value": "row" + } + ] + }, + { + "location": "259:21:259:34", + "type": "call", + "value": [ + { + "location": "259:21:259:26", + "type": "ref", + "value": [ + { + "location": "259:21:259:26", + "type": "var", + "value": "count" + } + ] + }, + { + "location": "259:27:259:32", + "type": "var", + "value": "lines" + } + ] + } + ], + "location": "259:11:259:35", + "type": "call" + }, + { + "type": "number", + "value": 1, + "location": "259:37:259:38" + } + ], + "location": "259:11:259:39", + "type": "call" + } + ], + [ + { + "type": "string", + "value": "col", + "location": "260:3:260:8" + }, + { + "location": "260:10:260:44", + "type": "call", + "value": [ + { + "location": "260:18:260:19", + "type": "ref", + "value": [ + { + "location": "260:18:260:19", + "type": "var", + "value": "plus" + } + ] + }, + { + "value": [ + { + "location": "260:10:260:13", + "type": "var", + "value": "loc" + }, + { + "location": "260:14:260:17", + "type": "string", + "value": "col" + } + ], + "location": "260:10:260:17", + "type": "ref" + }, + { + "location": "260:20:260:45", + "type": "call", + "value": [ + { + "location": "260:20:260:25", + "type": "ref", + "value": [ + { + "type": "var", + "value": "count", + "location": "260:20:260:25" + } + ] + }, + { + "location": "260:26:260:44", + "type": "call", + "value": [ + { + "location": "260:26:260:36", + "type": "ref", + "value": [ + { + "location": "260:26:260:31", + "type": "var", + "value": "regal" + }, + { + "location": "260:32:260:36", + "type": "string", + "value": "last" + } + ] + }, + { + "location": "260:37:260:42", + "type": "var", + "value": "lines" + } + ] + } + ] + } + ] + } + ] + ] + } + ] + } + ] + }, + { + "location": "266:1:278:2", + "head": { + "location": "266:1:266:36", + "ref": [ + { + "type": "var", + "value": "_before_location", + "location": "266:1:266:17" + } + ], + "args": [ + { + "location": "266:18:266:22", + "type": "var", + "value": "rule" + }, + { + "location": "266:24:266:25", + "type": "var", + "value": "$11" + }, + { + "location": "266:27:266:35", + "type": "var", + "value": "location" + } + ], + "value": { + "type": "boolean", + "value": true + } + }, + "body": [ + { + "location": "267:2:267:42", + "terms": [ + { + "value": [ + { + "location": "267:6:267:8", + "type": "var", + "value": "assign" + } + ], + "location": "267:6:267:8", + "type": "ref" + }, + { + "location": "267:2:267:5", + "type": "var", + "value": "loc" + }, + { + "location": "267:9:267:42", + "type": "call", + "value": [ + { + "location": "267:9:267:32", + "type": "ref", + "value": [ + { + "location": "267:9:267:13", + "type": "var", + "value": "util" + }, + { + "location": "267:14:267:32", + "type": "string", + "value": "to_location_object" + } + ] + }, + { + "location": "267:33:267:41", + "type": "var", + "value": "location" + } + ] + } + ] + }, + { + "location": "269:2:269:66", + "terms": [ + { + "location": "269:14:269:16", + "type": "ref", + "value": [ + { + "location": "269:14:269:16", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "269:2:269:13", + "type": "var", + "value": "value_start" + }, + { + "value": [ + { + "type": "ref", + "value": [ + { + "location": "269:17:269:21", + "type": "var", + "value": "util" + }, + { + "location": "269:22:269:40", + "type": "string", + "value": "to_location_object" + } + ], + "location": "269:17:269:40" + }, + { + "location": "269:41:269:65", + "type": "ref", + "value": [ + { + "location": "269:41:269:45", + "type": "var", + "value": "rule" + }, + { + "location": "269:46:269:50", + "type": "string", + "value": "head" + }, + { + "value": "value", + "location": "269:51:269:56", + "type": "string" + }, + { + "value": "location", + "location": "269:57:269:65", + "type": "string" + } + ] + } + ], + "location": "269:17:269:66", + "type": "call" + } + ] + }, + { + "location": "271:2:271:28", + "terms": [ + { + "value": [ + { + "value": "gte", + "location": "271:10:271:12", + "type": "var" + } + ], + "location": "271:10:271:12", + "type": "ref" + }, + { + "type": "ref", + "value": [ + { + "location": "271:2:271:5", + "type": "var", + "value": "loc" + }, + { + "location": "271:6:271:9", + "type": "string", + "value": "row" + } + ], + "location": "271:2:271:9" + }, + { + "location": "271:13:271:28", + "type": "ref", + "value": [ + { + "location": "271:13:271:24", + "type": "var", + "value": "value_start" + }, + { + "location": "271:25:271:28", + "type": "string", + "value": "row" + } + ] + } + ] + }, + { + "location": "272:2:272:28", + "terms": [ + { + "location": "272:10:272:12", + "type": "ref", + "value": [ + { + "value": "gte", + "location": "272:10:272:12", + "type": "var" + } + ] + }, + { + "location": "272:2:272:9", + "type": "ref", + "value": [ + { + "location": "272:2:272:5", + "type": "var", + "value": "loc" + }, + { + "location": "272:6:272:9", + "type": "string", + "value": "col" + } + ] + }, + { + "location": "272:13:272:28", + "type": "ref", + "value": [ + { + "location": "272:13:272:24", + "type": "var", + "value": "value_start" + }, + { + "location": "272:25:272:28", + "type": "string", + "value": "col" + } + ] + } + ] + }, + { + "location": "274:2:274:79", + "terms": [ + { + "location": "274:12:274:14", + "type": "ref", + "value": [ + { + "value": "assign", + "location": "274:12:274:14", + "type": "var" + } + ] + }, + { + "value": "value_end", + "location": "274:2:274:11", + "type": "var" + }, + { + "location": "274:15:274:79", + "type": "call", + "value": [ + { + "location": "274:15:274:28", + "type": "ref", + "value": [ + { + "type": "var", + "value": "_end_location", + "location": "274:15:274:28" + } + ] + }, + { + "location": "274:29:274:79", + "type": "call", + "value": [ + { + "location": "274:29:274:52", + "type": "ref", + "value": [ + { + "location": "274:29:274:33", + "type": "var", + "value": "util" + }, + { + "value": "to_location_object", + "location": "274:34:274:52", + "type": "string" + } + ] + }, + { + "location": "274:53:274:77", + "type": "ref", + "value": [ + { + "location": "274:53:274:57", + "type": "var", + "value": "rule" + }, + { + "location": "274:58:274:62", + "type": "string", + "value": "head" + }, + { + "location": "274:63:274:68", + "type": "string", + "value": "value" + }, + { + "location": "274:69:274:77", + "type": "string", + "value": "location" + } + ] + } + ] + } + ] + } + ] + }, + { + "location": "276:2:276:26", + "terms": [ + { + "location": "276:10:276:12", + "type": "ref", + "value": [ + { + "value": "lte", + "location": "276:10:276:12", + "type": "var" + } + ] + }, + { + "location": "276:2:276:9", + "type": "ref", + "value": [ + { + "location": "276:2:276:5", + "type": "var", + "value": "loc" + }, + { + "location": "276:6:276:9", + "type": "string", + "value": "row" + } + ] + }, + { + "location": "276:13:276:26", + "type": "ref", + "value": [ + { + "location": "276:13:276:22", + "type": "var", + "value": "value_end" + }, + { + "location": "276:23:276:26", + "type": "string", + "value": "row" + } + ] + } + ] + }, + { + "location": "277:2:277:26", + "terms": [ + { + "location": "277:10:277:12", + "type": "ref", + "value": [ + { + "location": "277:10:277:12", + "type": "var", + "value": "lte" + } + ] + }, + { + "location": "277:2:277:9", + "type": "ref", + "value": [ + { + "location": "277:2:277:5", + "type": "var", + "value": "loc" + }, + { + "location": "277:6:277:9", + "type": "string", + "value": "col" + } + ] + }, + { + "location": "277:13:277:26", + "type": "ref", + "value": [ + { + "value": "value_end", + "location": "277:13:277:22", + "type": "var" + }, + { + "location": "277:23:277:26", + "type": "string", + "value": "col" + } + ] + } + ] + } + ] + }, + { + "head": { + "ref": [ + { + "location": "280:1:280:17", + "type": "var", + "value": "_before_location" + } + ], + "args": [ + { + "location": "280:18:280:19", + "type": "var", + "value": "$12" + }, + { + "value": "var", + "location": "280:21:280:24", + "type": "var" + }, + { + "location": "280:26:280:34", + "type": "var", + "value": "location" + } + ], + "value": { + "type": "boolean", + "value": true + }, + "location": "280:1:280:35" + }, + "body": [ + { + "location": "281:2:281:83", + "terms": [ + { + "value": [ + { + "location": "281:44:281:45", + "type": "var", + "value": "lt" + } + ], + "location": "281:44:281:45", + "type": "ref" + }, + { + "value": [ + { + "location": "281:2:281:39", + "type": "call", + "value": [ + { + "value": [ + { + "type": "var", + "value": "util", + "location": "281:2:281:6" + }, + { + "type": "string", + "value": "to_location_object", + "location": "281:7:281:25" + } + ], + "location": "281:2:281:25", + "type": "ref" + }, + { + "value": [ + { + "location": "281:26:281:29", + "type": "var", + "value": "var" + }, + { + "location": "281:30:281:38", + "type": "string", + "value": "location" + } + ], + "location": "281:26:281:38", + "type": "ref" + } + ] + }, + { + "location": "281:40:281:43", + "type": "string", + "value": "row" + } + ], + "location": "281:2:281:45", + "type": "ref" + }, + { + "location": "281:46:282:2", + "type": "ref", + "value": [ + { + "location": "281:46:281:79", + "type": "call", + "value": [ + { + "location": "281:46:281:69", + "type": "ref", + "value": [ + { + "location": "281:46:281:50", + "type": "var", + "value": "util" + }, + { + "location": "281:51:281:69", + "type": "string", + "value": "to_location_object" + } + ] + }, + { + "location": "281:70:281:78", + "type": "var", + "value": "location" + } + ] + }, + { + "location": "281:80:281:83", + "type": "string", + "value": "row" + } + ] + } + ] + } + ], + "location": "280:1:282:2" + }, + { + "location": "284:1:290:2", + "head": { + "value": { + "type": "boolean", + "value": true + }, + "location": "284:1:284:35", + "ref": [ + { + "location": "284:1:284:17", + "type": "var", + "value": "_before_location" + } + ], + "args": [ + { + "location": "284:18:284:19", + "type": "var", + "value": "$13" + }, + { + "value": "var", + "location": "284:21:284:24", + "type": "var" + }, + { + "type": "var", + "value": "location", + "location": "284:26:284:34" + } + ] + }, + "body": [ + { + "terms": [ + { + "location": "285:10:285:12", + "type": "ref", + "value": [ + { + "location": "285:10:285:12", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "285:2:285:9", + "type": "var", + "value": "var_loc" + }, + { + "location": "285:13:285:50", + "type": "call", + "value": [ + { + "location": "285:13:285:36", + "type": "ref", + "value": [ + { + "location": "285:13:285:17", + "type": "var", + "value": "util" + }, + { + "location": "285:18:285:36", + "type": "string", + "value": "to_location_object" + } + ] + }, + { + "location": "285:37:285:49", + "type": "ref", + "value": [ + { + "location": "285:37:285:40", + "type": "var", + "value": "var" + }, + { + "location": "285:41:285:49", + "type": "string", + "value": "location" + } + ] + } + ] + } + ], + "location": "285:2:285:50" + }, + { + "location": "286:2:286:42", + "terms": [ + { + "value": [ + { + "location": "286:6:286:8", + "type": "var", + "value": "assign" + } + ], + "location": "286:6:286:8", + "type": "ref" + }, + { + "value": "loc", + "location": "286:2:286:5", + "type": "var" + }, + { + "location": "286:9:286:42", + "type": "call", + "value": [ + { + "location": "286:9:286:32", + "type": "ref", + "value": [ + { + "location": "286:9:286:13", + "type": "var", + "value": "util" + }, + { + "location": "286:14:286:32", + "type": "string", + "value": "to_location_object" + } + ] + }, + { + "location": "286:33:286:41", + "type": "var", + "value": "location" + } + ] + } + ] + }, + { + "location": "288:2:288:24", + "terms": [ + { + "location": "288:14:288:16", + "type": "ref", + "value": [ + { + "location": "288:14:288:16", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "288:2:288:13", + "type": "ref", + "value": [ + { + "value": "var_loc", + "location": "288:2:288:9", + "type": "var" + }, + { + "location": "288:10:288:13", + "type": "string", + "value": "row" + } + ] + }, + { + "location": "288:17:288:24", + "type": "ref", + "value": [ + { + "location": "288:17:288:20", + "type": "var", + "value": "loc" + }, + { + "location": "288:21:288:24", + "type": "string", + "value": "row" + } + ] + } + ] + }, + { + "location": "289:2:289:23", + "terms": [ + { + "type": "ref", + "value": [ + { + "value": "lt", + "location": "289:14:289:15", + "type": "var" + } + ], + "location": "289:14:289:15" + }, + { + "value": [ + { + "location": "289:2:289:9", + "type": "var", + "value": "var_loc" + }, + { + "location": "289:10:289:13", + "type": "string", + "value": "col" + } + ], + "location": "289:2:289:13", + "type": "ref" + }, + { + "value": [ + { + "location": "289:16:289:19", + "type": "var", + "value": "loc" + }, + { + "location": "289:20:289:23", + "type": "string", + "value": "col" + } + ], + "location": "289:16:289:23", + "type": "ref" + } + ] + } + ] + }, + { + "location": "294:1:299:2", + "annotations": [ + { + "location": "292:1:293:77", + "scope": "rule", + "description": "find *only* names in the local scope, and not e.g. rule names" + } + ], + "head": { + "location": "294:1:294:51", + "ref": [ + { + "location": "294:1:294:26", + "type": "var", + "value": "find_names_in_local_scope" + } + ], + "args": [ + { + "location": "294:27:294:31", + "type": "var", + "value": "rule" + }, + { + "location": "294:33:294:41", + "type": "var", + "value": "location" + } + ], + "assign": true, + "value": { + "type": "var", + "value": "names", + "location": "294:46:294:51" + } + }, + "body": [ + { + "location": "295:2:295:43", + "terms": [ + { + "location": "295:15:295:17", + "type": "ref", + "value": [ + { + "location": "295:15:295:17", + "type": "var", + "value": "assign" + } + ] + }, + { + "value": "fn_arg_names", + "location": "295:2:295:14", + "type": "var" + }, + { + "value": [ + { + "location": "295:18:295:37", + "type": "ref", + "value": [ + { + "type": "var", + "value": "_function_arg_names", + "location": "295:18:295:37" + } + ] + }, + { + "location": "295:38:295:42", + "type": "var", + "value": "rule" + } + ], + "location": "295:18:295:43", + "type": "call" + } + ] + }, + { + "location": "296:2:296:106", + "terms": [ + { + "location": "296:12:296:14", + "type": "ref", + "value": [ + { + "location": "296:12:296:14", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "296:2:296:11", + "type": "var", + "value": "var_names" + }, + { + "location": "296:15:296:106", + "type": "setcomprehension", + "value": { + "term": { + "value": [ + { + "type": "var", + "value": "var", + "location": "296:16:296:19" + }, + { + "type": "string", + "value": "value", + "location": "296:20:296:25" + } + ], + "location": "296:16:296:25", + "type": "ref" + }, + "body": [ + { + "location": "296:28:296:105", + "terms": { + "location": "296:28:296:32", + "symbols": [ + { + "location": "296:33:296:105", + "type": "call", + "value": [ + { + "location": "296:37:296:39", + "type": "ref", + "value": [ + { + "location": "296:37:296:39", + "type": "var", + "value": "internal" + }, + { + "location": "296:37:296:39", + "type": "string", + "value": "member_2" + } + ] + }, + { + "type": "var", + "value": "var", + "location": "296:33:296:36" + }, + { + "location": "296:40:296:106", + "type": "call", + "value": [ + { + "location": "296:40:296:64", + "type": "ref", + "value": [ + { + "location": "296:40:296:64", + "type": "var", + "value": "find_vars_in_local_scope" + } + ] + }, + { + "location": "296:65:296:69", + "type": "var", + "value": "rule" + }, + { + "location": "296:71:296:105", + "type": "call", + "value": [ + { + "location": "296:71:296:94", + "type": "ref", + "value": [ + { + "value": "util", + "location": "296:71:296:75", + "type": "var" + }, + { + "location": "296:76:296:94", + "type": "string", + "value": "to_location_object" + } + ] + }, + { + "location": "296:95:296:103", + "type": "var", + "value": "location" + } + ] + } + ] + } + ] + } + ] + } + } + ] + } + } + ] + }, + { + "location": "298:2:298:35", + "terms": [ + { + "location": "298:8:298:10", + "type": "ref", + "value": [ + { + "location": "298:8:298:10", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "298:2:298:7", + "type": "var", + "value": "names" + }, + { + "location": "298:11:298:35", + "type": "call", + "value": [ + { + "location": "298:24:298:25", + "type": "ref", + "value": [ + { + "location": "298:24:298:25", + "type": "var", + "value": "or" + } + ] + }, + { + "location": "298:11:298:23", + "type": "var", + "value": "fn_arg_names" + }, + { + "type": "var", + "value": "var_names", + "location": "298:26:298:35" + } + ] + } + ] + } + ] + }, + { + "location": "301:1:304:2", + "head": { + "value": { + "location": "301:30:304:2", + "type": "setcomprehension", + "value": { + "term": { + "value": [ + { + "location": "301:31:301:34", + "type": "var", + "value": "arg" + }, + { + "location": "301:35:301:40", + "type": "string", + "value": "value" + } + ], + "location": "301:31:301:40", + "type": "ref" + }, + "body": [ + { + "location": "302:2:302:28", + "terms": { + "location": "302:2:302:6", + "symbols": [ + { + "location": "302:7:302:28", + "type": "call", + "value": [ + { + "location": "302:11:302:13", + "type": "ref", + "value": [ + { + "type": "var", + "value": "internal", + "location": "302:11:302:13" + }, + { + "location": "302:11:302:13", + "type": "string", + "value": "member_2" + } + ] + }, + { + "location": "302:7:302:10", + "type": "var", + "value": "arg" + }, + { + "value": [ + { + "location": "302:14:302:18", + "type": "var", + "value": "rule" + }, + { + "location": "302:19:302:23", + "type": "string", + "value": "head" + }, + { + "value": "args", + "location": "302:24:302:28", + "type": "string" + } + ], + "location": "302:14:302:28", + "type": "ref" + } + ] + } + ] + } + }, + { + "location": "303:2:303:19", + "terms": [ + { + "location": "303:11:303:13", + "type": "ref", + "value": [ + { + "location": "303:11:303:13", + "type": "var", + "value": "equal" + } + ] + }, + { + "location": "303:2:303:10", + "type": "ref", + "value": [ + { + "value": "arg", + "location": "303:2:303:5", + "type": "var" + }, + { + "location": "303:6:303:10", + "type": "string", + "value": "type" + } + ] + }, + { + "location": "303:14:303:19", + "type": "string", + "value": "var" + } + ] + } + ] + } + }, + "location": "301:1:304:2", + "ref": [ + { + "location": "301:1:301:20", + "type": "var", + "value": "_function_arg_names" + } + ], + "args": [ + { + "location": "301:21:301:25", + "type": "var", + "value": "rule" + } + ], + "assign": true + } + }, + { + "location": "310:1:315:2", + "annotations": [ + { + "location": "306:1:309:82", + "scope": "rule", + "description": "similar to `find_vars_in_local_scope`, but returns all variable names in scope\nof the given location *and* the rule names present in the scope (i.e. module)\n" + } + ], + "head": { + "assign": true, + "value": { + "location": "310:40:310:45", + "type": "var", + "value": "names" + }, + "location": "310:1:310:45", + "ref": [ + { + "location": "310:1:310:20", + "type": "var", + "value": "find_names_in_scope" + } + ], + "args": [ + { + "location": "310:21:310:25", + "type": "var", + "value": "rule" + }, + { + "value": "location", + "location": "310:27:310:35", + "type": "var" + } + ] + }, + "body": [ + { + "location": "311:2:311:78", + "terms": [ + { + "location": "311:9:311:11", + "type": "ref", + "value": [ + { + "location": "311:9:311:11", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "311:2:311:8", + "type": "var", + "value": "locals" + }, + { + "location": "311:12:311:78", + "type": "call", + "value": [ + { + "location": "311:12:311:37", + "type": "ref", + "value": [ + { + "value": "find_names_in_local_scope", + "location": "311:12:311:37", + "type": "var" + } + ] + }, + { + "location": "311:38:311:42", + "type": "var", + "value": "rule" + }, + { + "location": "311:44:311:78", + "type": "call", + "value": [ + { + "location": "311:44:311:67", + "type": "ref", + "value": [ + { + "location": "311:44:311:48", + "type": "var", + "value": "util" + }, + { + "type": "string", + "value": "to_location_object", + "location": "311:49:311:67" + } + ] + }, + { + "location": "311:68:311:76", + "type": "var", + "value": "location" + } + ] + } + ] + } + ] + }, + { + "location": "314:2:314:55", + "terms": [ + { + "location": "314:8:314:10", + "type": "ref", + "value": [ + { + "location": "314:8:314:10", + "type": "var", + "value": "assign" + } + ] + }, + { + "location": "314:2:314:7", + "type": "var", + "value": "names" + }, + { + "location": "314:12:314:56", + "type": "call", + "value": [ + { + "location": "314:47:314:48", + "type": "ref", + "value": [ + { + "location": "314:47:314:48", + "type": "var", + "value": "or" + } + ] + }, + { + "location": "314:12:314:47", + "type": "call", + "value": [ + { + "location": "314:23:314:24", + "type": "ref", + "value": [ + { + "location": "314:23:314:24", + "type": "var", + "value": "or" + } + ] + }, + { + "location": "314:12:314:22", + "type": "var", + "value": "rule_names" + }, + { + "location": "314:25:314:45", + "type": "var", + "value": "imported_identifiers" + } + ] + }, + { + "type": "var", + "value": "locals", + "location": "314:49:314:55" + } + ] + } + ] + } + ] + }, + { + "location": "321:1:324:2", + "annotations": [ + { + "location": "317:1:320:39", + "scope": "rule", + "description": "find all variables declared via `some` declarations (and *not* `some .. in`)\nin the scope of the given location\n" + } + ], + "head": { + "assign": true, + "value": { + "location": "321:50:324:2", + "type": "setcomprehension", + "value": { + "term": { + "location": "321:51:321:65", + "type": "ref", + "value": [ + { + "location": "321:51:321:59", + "type": "var", + "value": "some_var" + }, + { + "location": "321:60:321:65", + "type": "string", + "value": "value" + } + ] + }, + "body": [ + { + "location": "322:2:322:56", + "terms": { + "symbols": [ + { + "location": "322:7:322:56", + "type": "call", + "value": [ + { + "value": [ + { + "location": "322:16:322:18", + "type": "var", + "value": "internal" + }, + { + "type": "string", + "value": "member_2", + "location": "322:16:322:18" + } + ], + "location": "322:16:322:18", + "type": "ref" + }, + { + "location": "322:7:322:15", + "type": "var", + "value": "some_var" + }, + { + "location": "322:19:322:56", + "type": "ref", + "value": [ + { + "location": "322:19:322:24", + "type": "var", + "value": "found" + }, + { + "location": "322:25:322:29", + "type": "string", + "value": "vars" + }, + { + "location": "322:30:322:48", + "type": "call", + "value": [ + { + "value": [ + { + "location": "322:30:322:41", + "type": "var", + "value": "_rule_index" + } + ], + "location": "322:30:322:41", + "type": "ref" + }, + { + "location": "322:42:322:46", + "type": "var", + "value": "rule" + } + ] + }, + { + "location": "322:49:322:55", + "type": "string", + "value": "some" + } + ] + } + ] + } + ], + "location": "322:2:322:6" + } + }, + { + "location": "323:2:323:44", + "terms": [ + { + "location": "323:2:323:18", + "type": "ref", + "value": [ + { + "location": "323:2:323:18", + "type": "var", + "value": "_before_location" + } + ] + }, + { + "location": "323:19:323:23", + "type": "var", + "value": "rule" + }, + { + "location": "323:25:323:33", + "type": "var", + "value": "some_var" + }, + { + "location": "323:35:323:43", + "type": "var", + "value": "location" + } + ] + } + ] + } + }, + "location": "321:1:324:2", + "ref": [ + { + "location": "321:1:321:30", + "type": "var", + "value": "find_some_decl_names_in_scope" + } + ], + "args": [ + { + "location": "321:31:321:35", + "type": "var", + "value": "rule" + }, + { + "location": "321:37:321:45", + "type": "var", + "value": "location" + } + ] + } + }, + { + "body": [ + { + "location": "329:2:329:38", + "terms": { + "location": "329:2:329:6", + "symbols": [ + { + "location": "329:7:329:38", + "type": "call", + "value": [ + { + "location": "329:24:329:26", + "type": "ref", + "value": [ + { + "location": "329:24:329:26", + "type": "var", + "value": "internal" + }, + { + "location": "329:24:329:26", + "type": "string", + "value": "member_3" + } + ] + }, + { + "location": "329:7:329:17", + "type": "var", + "value": "rule_index" + }, + { + "location": "329:19:329:23", + "type": "var", + "value": "rule" + }, + { + "location": "329:27:329:38", + "type": "ref", + "value": [ + { + "location": "329:27:329:32", + "type": "var", + "value": "input" + }, + { + "location": "329:33:329:38", + "type": "string", + "value": "rules" + } + ] + } + ] + } + ] + } + }, + { + "location": "330:2:330:36", + "terms": { + "symbols": [ + { + "location": "330:7:330:36", + "type": "call", + "value": [ + { + "type": "ref", + "value": [ + { + "location": "330:24:330:26", + "type": "var", + "value": "internal" + }, + { + "location": "330:24:330:26", + "type": "string", + "value": "member_3" + } + ], + "location": "330:24:330:26" + }, + { + "location": "330:7:330:17", + "type": "var", + "value": "expr_index" + }, + { + "value": "expr", + "location": "330:19:330:23", + "type": "var" + }, + { + "location": "330:27:330:36", + "type": "ref", + "value": [ + { + "type": "var", + "value": "rule", + "location": "330:27:330:31" + }, + { + "location": "330:32:330:36", + "type": "string", + "value": "body" + } + ] + } + ] + } + ], + "location": "330:2:330:6" + } + } + ], + "location": "328:1:331:2", + "annotations": [ + { + "location": "326:1:327:41", + "scope": "rule", + "description": "all expressions in module" + } + ], + "head": { + "location": "328:1:328:38", + "ref": [ + { + "location": "328:1:328:6", + "type": "var", + "value": "exprs" + }, + { + "location": "328:7:328:17", + "type": "var", + "value": "rule_index" + }, + { + "location": "328:19:328:29", + "type": "var", + "value": "expr_index" + } + ], + "assign": true, + "value": { + "location": "328:34:328:38", + "type": "var", + "value": "expr" + } + } + } + ], + "comments": [ + { + "text": "IHNpbXBsZSBhc3NpZ25tZW50LCBpLmUuIGB4IDo9IDEwMGAgcmV0dXJucyBgeGA=", + "location": "13:1:13:49" + }, + { + "location": "14:1:14:49", + "text": "IGFsd2F5cyByZXR1cm5zIGEgc2luZ2xlIHZhciwgYnV0IHdyYXBwZWQgaW4gYW4=" + }, + { + "location": "15:1:15:24", + "text": "IGFycmF5IGZvciBjb25zaXN0ZW5jeQ==" + }, + { + "location": "21:1:21:41", + "text": "ICdkZXN0cnVjdHVyaW5nJyBhcnJheSBhc3NpZ25tZW50LCBpLmUu" + }, + { + "location": "22:1:22:25", + "text": "IFthLCBiLCBjXSA6PSBbMSwgMiwgM10=" + }, + { + "location": "23:1:23:5", + "text": "IG9y" + }, + { + "location": "24:1:24:27", + "text": "IHthOiBifSA6PSB7ImZvbyI6ICJiYXIifQ==" + }, + { + "location": "30:1:30:56", + "text": "IHZhciBkZWNsYXJlZCB2aWEgYHNvbWVgLCBpLmUuIGBzb21lIHhgIG9yIGBzb21lIHgsIHlg" + }, + { + "location": "36:1:36:56", + "text": "IHNpbmdsZSB2YXIgZGVjbGFyZWQgdmlhIGBzb21lIGluYCwgaS5lLiBgc29tZSB4IGluIHlg" + }, + { + "text": "IHR3byB2YXJzIGRlY2xhcmVkIHZpYSBgc29tZSBpbmAsIGkuZS4gYHNvbWUgeCwgeSBpbiB6YA==", + "location": "44:1:44:57" + }, + { + "location": "55:1:58:85", + "text": "IE1FVEFEQVRB" + }, + { + "location": "56:1:56:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "57:1:57:58", + "text": "ICAgZmluZCB2YXJzIGxpa2UgaW5wdXRbeF0uZm9vW3ldIHdoZXJlIHggYW5kIHkgYXJlIHZhcnM=" + }, + { + "location": "58:1:58:85", + "text": "ICAgbm90ZTogdmFsdWUudHlwZSA9PSAicmVmIiBjaGVjayBtdXN0IGhhdmUgYmVlbiBkb25lIGJlZm9yZSBjYWxsaW5nIHRoaXMgZnVuY3Rpb24=" + }, + { + "location": "66:1:66:63", + "text": "IG9uZSBvciB0d28gdmFycyBkZWNsYXJlZCB2aWEgYGV2ZXJ5YCwgaS5lLiBgZXZlcnkgeCBpbiB5IHt9YA==" + }, + { + "location": "67:1:67:40", + "text": "IG9yIGBldmVyeWAsIGkuZS4gYGV2ZXJ5IHgsIHkgaW4geSB7fWA=" + }, + { + "location": "81:1:84:64", + "text": "IE1FVEFEQVRB" + }, + { + "location": "82:1:82:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "83:1:83:84", + "text": "ICAgdHJhdmVyc2VzIGFsbCBub2RlcyBpbiBwcm92aWRlZCB0ZXJtcyAodXNpbmcgYHdhbGtgKSwgYW5kIHJldHVybnMgYW4gYXJyYXkgd2l0aA==" + }, + { + "text": "ICAgYWxsIHZhcmlhYmxlcyBkZWNsYXJlZCBpbiB0ZXJtcywgaSxlIFt4LCB5XSBvciB7eDogeX0sIGV0Yy4=", + "location": "84:1:84:64" + }, + { + "location": "91:1:94:70", + "text": "IE1FVEFEQVRB" + }, + { + "location": "92:1:92:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "93:1:93:91", + "text": "ICAgdHJhdmVyc2VzIGFsbCBub2RlcyBpbiBwcm92aWRlZCB0ZXJtcyAodXNpbmcgYHdhbGtgKSwgYW5kIHJldHVybnMgdHJ1ZSBpZiBhbnkgdmFyaWFibGU=" + }, + { + "location": "94:1:94:70", + "text": "ICAgaXMgZm91bmQgaW4gdGVybXMsIHdpdGggZWFybHkgZXhpdCAoYXMgb3Bwb3NlZCB0byBmaW5kX3Rlcm1fdmFycyk=" + }, + { + "location": "110:32:110:65", + "text": "IHJlZ2FsIGlnbm9yZTpleHRlcm5hbC1yZWZlcmVuY2U=" + }, + { + "location": "121:1:121:77", + "text": "IGA9YCBpc24ndCBuZWNlc3NhcmlseSBhc3NpZ25tZW50LCBhbmQgb25seSBjb25zaWRlcmluZyB0aGUgdmFyaWFibGUgb24gdGhl" + }, + { + "location": "122:1:122:77", + "text": "IGxlZnQtaGFuZCBzaWRlIGlzIGVxdWFsbHkgZHViaW91cywgYnV0IHdlJ2xsIHRyZWF0IGB4ID0gMWAgYXMgYHggOj0gMWAgZm9y" + }, + { + "location": "123:1:123:79", + "text": "IHRoZSBwdXJwb3NlIG9mIHRoaXMgZnVuY3Rpb24gdW50aWwgd2UgaGF2ZSBhIG1vcmUgcm9idXN0IHdheSBvZiBkZWFsaW5nIHdpdGg=" + }, + { + "text": "IHVuaWZpY2F0aW9u", + "location": "124:1:124:14" + }, + { + "location": "161:22:161:55", + "text": "IHJlZ2FsIGlnbm9yZTpleHRlcm5hbC1yZWZlcmVuY2U=" + }, + { + "location": "165:1:169:44", + "text": "IE1FVEFEQVRB" + }, + { + "location": "166:1:166:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "167:1:167:86", + "text": "ICAgdHJhdmVyc2VzIGFsbCBub2RlcyB1bmRlciBwcm92aWRlZCBub2RlICh1c2luZyBgd2Fsa2ApLCBhbmQgcmV0dXJucyBhbiBhcnJheSB3aXRo" + }, + { + "location": "168:1:168:86", + "text": "ICAgYWxsIHZhcmlhYmxlcyBkZWNsYXJlZCB2aWEgYXNzaWdubWVudCAoOj0pLCBgc29tZWAsIGBldmVyeWAgYW5kIGluIGNvbXByZWhlbnNpb25z" + }, + { + "location": "169:1:169:44", + "text": "ICAgREVQUkVDQVRFRDogdXNlcyBhc3QuZm91bmQudmFycyBpbnN0ZWFk" + }, + { + "location": "176:1:176:85", + "text": "IGhhY2sgdG8gd29yayBhcm91bmQgdGhlIGRpZmZlcmVudCBpbnB1dCBtb2RlbHMgb2YgbGludGluZyB2cy4gdGhlIGxzcCBwYWNrYWdlLi4gd2U=" + }, + { + "location": "177:1:177:49", + "text": "IHNob3VsZCBwcm9iYWJseSBjb25zaWRlciBzb21ldGhpbmcgbW9yZSByb2J1c3Q=" + }, + { + "location": "182:1:192:10", + "text": "IE1FVEFEQVRBOg==" + }, + { + "text": "IGRlc2NyaXB0aW9uOiB8", + "location": "183:1:183:17" + }, + { + "location": "184:1:184:88", + "text": "ICAgb2JqZWN0IGNvbnRhaW5pbmcgYWxsIHZhcmlhYmxlcyBmb3VuZCBpbiB0aGUgaW5wdXQgQVNULCBrZXllZCBmaXJzdCBieSB0aGUgaW5kZXggb2Y=" + }, + { + "location": "185:1:185:88", + "text": "ICAgdGhlIHJ1bGUgd2hlcmUgdGhlIHZhcmlhYmxlcyB3ZXJlIGZvdW5kIChhcyBhIG51bWVyaWMgc3RyaW5nKSwgYW5kIHRoZW4gdGhlIGNvbnRleHQ=" + }, + { + "location": "186:1:186:43", + "text": "ICAgb2YgdGhlIHZhcmlhYmxlLCB3aGljaCB3aWxsIGJlIG9uZSBvZjo=" + }, + { + "location": "187:1:187:11", + "text": "ICAgLSB0ZXJt" + }, + { + "text": "ICAgLSBhc3NpZ24=", + "location": "188:1:188:13" + }, + { + "location": "189:1:189:12", + "text": "ICAgLSBldmVyeQ==" + }, + { + "location": "190:1:190:11", + "text": "ICAgLSBzb21l" + }, + { + "location": "191:1:191:13", + "text": "ICAgLSBzb21laW4=" + }, + { + "location": "192:1:192:10", + "text": "ICAgLSByZWY=" + }, + { + "location": "196:2:196:92", + "text": "IGNvbnZlcnRpbmcgdG8gc3RyaW5nIHVudGlsIGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuLXBvbGljeS1hZ2VudC9vcGEvaXNzdWVzLzY3MzYgaXMgZml4ZWQ=" + }, + { + "location": "205:1:206:41", + "text": "IE1FVEFEQVRB" + }, + { + "location": "206:1:206:41", + "text": "IGRlc2NyaXB0aW9uOiBhbGwgcmVmcyBmb3VuZGQgaW4gbW9kdWxl" + }, + { + "location": "210:2:210:92", + "text": "IGNvbnZlcnRpbmcgdG8gc3RyaW5nIHVudGlsIGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuLXBvbGljeS1hZ2VudC9vcGEvaXNzdWVzLzY3MzYgaXMgZml4ZWQ=" + }, + { + "location": "218:1:219:44", + "text": "IE1FVEFEQVRB" + }, + { + "location": "219:1:219:44", + "text": "IGRlc2NyaXB0aW9uOiBhbGwgc3ltYm9scyBmb3VuZGQgaW4gbW9kdWxl" + }, + { + "location": "223:2:223:92", + "text": "IGNvbnZlcnRpbmcgdG8gc3RyaW5nIHVudGlsIGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuLXBvbGljeS1hZ2VudC9vcGEvaXNzdWVzLzY3MzYgaXMgZml4ZWQ=" + }, + { + "location": "229:1:230:50", + "text": "IE1FVEFEQVRB" + }, + { + "location": "230:1:230:50", + "text": "IGRlc2NyaXB0aW9uOiBhbGwgY29tcHJlaGVuc2lvbnMgZm91bmQgaW4gbW9kdWxl" + }, + { + "location": "234:2:234:92", + "text": "IGNvbnZlcnRpbmcgdG8gc3RyaW5nIHVudGlsIGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuLXBvbGljeS1hZ2VudC9vcGEvaXNzdWVzLzY3MzYgaXMgZml4ZWQ=" + }, + { + "location": "242:1:247:28", + "text": "IE1FVEFEQVRB" + }, + { + "location": "243:1:243:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "244:1:244:71", + "text": "ICAgZmluZHMgYWxsIHZhcnMgZGVjbGFyZWQgaW4gYHJ1bGVgICpiZWZvcmUqIHRoZSBgbG9jYXRpb25gIHByb3ZpZGVk" + }, + { + "location": "245:1:245:72", + "text": "ICAgbm90ZTogdGhpcyBpc24ndCAxMDAlIGFjY3VyYXRlLCBhcyBpdCBkb2Vzbid0IHRha2UgaW50byBhY2NvdW50IGA9YA==" + }, + { + "location": "246:1:246:77", + "text": "ICAgYXNzaWdubWVudHMgLyB1bmlmaWNhdGlvbiwgYnV0IGl0J3MgbGlrZWx5IGdvb2QgZW5vdWdoIHNpbmNlIG90aGVyIHJ1bGVz" + }, + { + "location": "247:1:247:28", + "text": "ICAgcmVjb21tZW5kIGFnYWluc3QgdGhvc2U=" + }, + { + "location": "249:45:249:78", + "text": "IHJlZ2FsIGlnbm9yZTpleHRlcm5hbC1yZWZlcmVuY2U=" + }, + { + "location": "264:1:264:62", + "text": "IHNwZWNpYWwgY2FzZSDigJQgdGhlIHZhbHVlIGxvY2F0aW9uIG9mIHRoZSBydWxlIGhlYWQgInNlZXMi" + }, + { + "location": "265:1:265:48", + "text": "IGFsbCBsb2NhbCB2YXJpYWJsZXMgZGVjbGFyZWQgaW4gdGhlIHJ1bGUgYm9keQ==" + }, + { + "location": "292:1:293:77", + "text": "IE1FVEFEQVRB" + }, + { + "location": "293:1:293:77", + "text": "IGRlc2NyaXB0aW9uOiBmaW5kICpvbmx5KiBuYW1lcyBpbiB0aGUgbG9jYWwgc2NvcGUsIGFuZCBub3QgZS5nLiBydWxlIG5hbWVz" + }, + { + "location": "306:1:309:82", + "text": "IE1FVEFEQVRB" + }, + { + "location": "307:1:307:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "308:1:308:83", + "text": "ICAgc2ltaWxhciB0byBgZmluZF92YXJzX2luX2xvY2FsX3Njb3BlYCwgYnV0IHJldHVybnMgYWxsIHZhcmlhYmxlIG5hbWVzIGluIHNjb3Bl" + }, + { + "location": "309:1:309:82", + "text": "ICAgb2YgdGhlIGdpdmVuIGxvY2F0aW9uICphbmQqIHRoZSBydWxlIG5hbWVzIHByZXNlbnQgaW4gdGhlIHNjb3BlIChpLmUuIG1vZHVsZSk=" + }, + { + "location": "313:2:313:36", + "text": "IHBhcmVucyBiZWxvdyBhZGRlZCBieSBvcGEtZm10IDop" + }, + { + "location": "317:1:320:39", + "text": "IE1FVEFEQVRB" + }, + { + "location": "318:1:318:17", + "text": "IGRlc2NyaXB0aW9uOiB8" + }, + { + "location": "319:1:319:81", + "text": "ICAgZmluZCBhbGwgdmFyaWFibGVzIGRlY2xhcmVkIHZpYSBgc29tZWAgZGVjbGFyYXRpb25zIChhbmQgKm5vdCogYHNvbWUgLi4gaW5gKQ==" + }, + { + "location": "320:1:320:39", + "text": "ICAgaW4gdGhlIHNjb3BlIG9mIHRoZSBnaXZlbiBsb2NhdGlvbg==" + }, + { + "location": "322:57:322:90", + "text": "IHJlZ2FsIGlnbm9yZTpleHRlcm5hbC1yZWZlcmVuY2U=" + }, + { + "location": "326:1:327:41", + "text": "IE1FVEFEQVRB" + }, + { + "location": "327:1:327:41", + "text": "IGRlc2NyaXB0aW9uOiBhbGwgZXhwcmVzc2lvbnMgaW4gbW9kdWxl" + } + ], + "regal": { + "file": { + "name": "bundle/regal/ast/search.rego", + "lines": [ + "package regal.ast", + "", + "import rego.v1", + "", + "import data.regal.util", + "", + "_find_nested_vars(obj) := [value |", + "\twalk(obj, [_, value])", + "\tvalue.type == \"var\"", + "\tindexof(value.value, \"$\") == -1", + "]", + "", + "# simple assignment, i.e. `x := 100` returns `x`", + "# always returns a single var, but wrapped in an", + "# array for consistency", + "_find_assign_vars(value) := var if {", + "\tvalue[1].type == \"var\"", + "\tvar := [value[1]]", + "}", + "", + "# 'destructuring' array assignment, i.e.", + "# [a, b, c] := [1, 2, 3]", + "# or", + "# {a: b} := {\"foo\": \"bar\"}", + "_find_assign_vars(value) := vars if {", + "\tvalue[1].type in {\"array\", \"object\"}", + "\tvars := _find_nested_vars(value[1])", + "}", + "", + "# var declared via `some`, i.e. `some x` or `some x, y`", + "_find_some_decl_vars(value) := [v |", + "\tsome v in value", + "\tv.type == \"var\"", + "]", + "", + "# single var declared via `some in`, i.e. `some x in y`", + "_find_some_in_decl_vars(value) := vars if {", + "\tarr := value[0].value", + "\tcount(arr) == 3", + "", + "\tvars := _find_nested_vars(arr[1])", + "}", + "", + "# two vars declared via `some in`, i.e. `some x, y in z`", + "_find_some_in_decl_vars(value) := vars if {", + "\tarr := value[0].value", + "\tcount(arr) == 4", + "", + "\tvars := [v |", + "\t\tsome i in [1, 2]", + "\t\tsome v in _find_nested_vars(arr[i])", + "\t]", + "}", + "", + "# METADATA", + "# description: |", + "# find vars like input[x].foo[y] where x and y are vars", + "# note: value.type == \"ref\" check must have been done before calling this function", + "find_ref_vars(value) := [var |", + "\tsome i, var in value.value", + "", + "\ti > 0", + "\tvar.type == \"var\"", + "]", + "", + "# one or two vars declared via `every`, i.e. `every x in y {}`", + "# or `every`, i.e. `every x, y in y {}`", + "_find_every_vars(value) := vars if {", + "\tkey_var := [value.key |", + "\t\tvalue.key.type == \"var\"", + "\t\tindexof(value.key.value, \"$\") == -1", + "\t]", + "\tval_var := [value.value |", + "\t\tvalue.value.type == \"var\"", + "\t\tindexof(value.value.value, \"$\") == -1", + "\t]", + "", + "\tvars := array.concat(key_var, val_var)", + "}", + "", + "# METADATA", + "# description: |", + "# traverses all nodes in provided terms (using `walk`), and returns an array with", + "# all variables declared in terms, i,e [x, y] or {x: y}, etc.", + "find_term_vars(terms) := [term |", + "\twalk(terms, [_, term])", + "", + "\tterm.type == \"var\"", + "]", + "", + "# METADATA", + "# description: |", + "# traverses all nodes in provided terms (using `walk`), and returns true if any variable", + "# is found in terms, with early exit (as opposed to find_term_vars)", + "has_term_var(terms) if {", + "\twalk(terms, [_, term])", + "", + "\tterm.type == \"var\"", + "}", + "", + "_find_vars(value, last) := {\"term\": find_term_vars(function_ret_args(fn_name, value))} if {", + "\tlast == \"terms\"", + "\tvalue[0].type == \"ref\"", + "\tvalue[0].value[0].type == \"var\"", + "\tvalue[0].value[0].value != \"assign\"", + "", + "\tfn_name := ref_to_string(value[0].value)", + "", + "\tnot contains(fn_name, \"$\")", + "\tfn_name in all_function_names # regal ignore:external-reference", + "\tfunction_ret_in_args(fn_name, value)", + "}", + "", + "_find_vars(value, last) := {\"assign\": _find_assign_vars(value)} if {", + "\tlast == \"terms\"", + "\tvalue[0].type == \"ref\"", + "\tvalue[0].value[0].type == \"var\"", + "\tvalue[0].value[0].value == \"assign\"", + "}", + "", + "# `=` isn't necessarily assignment, and only considering the variable on the", + "# left-hand side is equally dubious, but we'll treat `x = 1` as `x := 1` for", + "# the purpose of this function until we have a more robust way of dealing with", + "# unification", + "_find_vars(value, last) := {\"assign\": _find_assign_vars(value)} if {", + "\tlast == \"terms\"", + "\tvalue[0].type == \"ref\"", + "\tvalue[0].value[0].type == \"var\"", + "\tvalue[0].value[0].value == \"eq\"", + "}", + "", + "_find_vars(value, _) := {\"ref\": find_ref_vars(value)} if value.type == \"ref\"", + "", + "_find_vars(value, last) := {\"somein\": _find_some_in_decl_vars(value)} if {", + "\tlast == \"symbols\"", + "\tvalue[0].type == \"call\"", + "}", + "", + "_find_vars(value, last) := {\"some\": _find_some_decl_vars(value)} if {", + "\tlast == \"symbols\"", + "\tvalue[0].type != \"call\"", + "}", + "", + "_find_vars(value, last) := {\"every\": _find_every_vars(value)} if {", + "\tlast == \"terms\"", + "\tvalue.domain", + "}", + "", + "_find_vars(value, last) := {\"args\": arg_vars} if {", + "\tlast == \"args\"", + "", + "\targ_vars := [arg |", + "\t\tsome arg in value", + "\t\targ.type == \"var\"", + "\t]", + "", + "\tcount(arg_vars) > 0", + "}", + "", + "_rule_index(rule) := sprintf(\"%d\", [i]) if {", + "\tsome i, r in _rules # regal ignore:external-reference", + "\tr == rule", + "}", + "", + "# METADATA", + "# description: |", + "# traverses all nodes under provided node (using `walk`), and returns an array with", + "# all variables declared via assignment (:=), `some`, `every` and in comprehensions", + "# DEPRECATED: uses ast.found.vars instead", + "find_vars(node) := [var |", + "\twalk(node, [path, value])", + "", + "\tvar := _find_vars(value, regal.last(path))[_][_]", + "]", + "", + "# hack to work around the different input models of linting vs. the lsp package.. we", + "# should probably consider something more robust", + "_rules := input.rules", + "", + "_rules := data.workspace.parsed[input.regal.file.uri].rules if not input.rules", + "", + "# METADATA:", + "# description: |", + "# object containing all variables found in the input AST, keyed first by the index of", + "# the rule where the variables were found (as a numeric string), and then the context", + "# of the variable, which will be one of:", + "# - term", + "# - assign", + "# - every", + "# - some", + "# - somein", + "# - ref", + "found.vars[rule_index][context] contains var if {", + "\tsome i, rule in _rules", + "", + "\t# converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed", + "\trule_index := sprintf(\"%d\", [i])", + "", + "\twalk(rule, [path, value])", + "", + "\tsome context, vars in _find_vars(value, regal.last(path))", + "\tsome var in vars", + "}", + "", + "# METADATA", + "# description: all refs foundd in module", + "found.refs[rule_index] contains value if {", + "\tsome i, rule in _rules", + "", + "\t# converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed", + "\trule_index := sprintf(\"%d\", [i])", + "", + "\twalk(rule, [_, value])", + "", + "\tis_ref(value)", + "}", + "", + "# METADATA", + "# description: all symbols foundd in module", + "found.symbols[rule_index] contains value.symbols if {", + "\tsome i, rule in _rules", + "", + "\t# converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed", + "\trule_index := sprintf(\"%d\", [i])", + "", + "\twalk(rule, [_, value])", + "}", + "", + "# METADATA", + "# description: all comprehensions found in module", + "found.comprehensions[rule_index] contains value if {", + "\tsome i, rule in _rules", + "", + "\t# converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed", + "\trule_index := sprintf(\"%d\", [i])", + "", + "\twalk(rule, [_, value])", + "", + "\tvalue.type in {\"arraycomprehension\", \"objectcomprehension\", \"setcomprehension\"}", + "}", + "", + "# METADATA", + "# description: |", + "# finds all vars declared in `rule` *before* the `location` provided", + "# note: this isn't 100% accurate, as it doesn't take into account `=`", + "# assignments / unification, but it's likely good enough since other rules", + "# recommend against those", + "find_vars_in_local_scope(rule, location) := [var |", + "\tvar := found.vars[_rule_index(rule)][_][_] # regal ignore:external-reference", + "", + "\tnot is_wildcard(var)", + "\t_before_location(rule, var, util.to_location_object(location))", + "]", + "", + "_end_location(location) := end if {", + "\tloc := util.to_location_object(location)", + "\tlines := split(loc.text, \"\\n\")", + "\tend := {", + "\t\t\"row\": (loc.row + count(lines)) - 1,", + "\t\t\"col\": loc.col + count(regal.last(lines)),", + "\t}", + "}", + "", + "# special case — the value location of the rule head \"sees\"", + "# all local variables declared in the rule body", + "_before_location(rule, _, location) if {", + "\tloc := util.to_location_object(location)", + "", + "\tvalue_start := util.to_location_object(rule.head.value.location)", + "", + "\tloc.row >= value_start.row", + "\tloc.col >= value_start.col", + "", + "\tvalue_end := _end_location(util.to_location_object(rule.head.value.location))", + "", + "\tloc.row <= value_end.row", + "\tloc.col <= value_end.col", + "}", + "", + "_before_location(_, var, location) if {", + "\tutil.to_location_object(var.location).row < util.to_location_object(location).row", + "}", + "", + "_before_location(_, var, location) if {", + "\tvar_loc := util.to_location_object(var.location)", + "\tloc := util.to_location_object(location)", + "", + "\tvar_loc.row == loc.row", + "\tvar_loc.col < loc.col", + "}", + "", + "# METADATA", + "# description: find *only* names in the local scope, and not e.g. rule names", + "find_names_in_local_scope(rule, location) := names if {", + "\tfn_arg_names := _function_arg_names(rule)", + "\tvar_names := {var.value | some var in find_vars_in_local_scope(rule, util.to_location_object(location))}", + "", + "\tnames := fn_arg_names | var_names", + "}", + "", + "_function_arg_names(rule) := {arg.value |", + "\tsome arg in rule.head.args", + "\targ.type == \"var\"", + "}", + "", + "# METADATA", + "# description: |", + "# similar to `find_vars_in_local_scope`, but returns all variable names in scope", + "# of the given location *and* the rule names present in the scope (i.e. module)", + "find_names_in_scope(rule, location) := names if {", + "\tlocals := find_names_in_local_scope(rule, util.to_location_object(location))", + "", + "\t# parens below added by opa-fmt :)", + "\tnames := (rule_names | imported_identifiers) | locals", + "}", + "", + "# METADATA", + "# description: |", + "# find all variables declared via `some` declarations (and *not* `some .. in`)", + "# in the scope of the given location", + "find_some_decl_names_in_scope(rule, location) := {some_var.value |", + "\tsome some_var in found.vars[_rule_index(rule)][\"some\"] # regal ignore:external-reference", + "\t_before_location(rule, some_var, location)", + "}", + "", + "# METADATA", + "# description: all expressions in module", + "exprs[rule_index][expr_index] := expr if {", + "\tsome rule_index, rule in input.rules", + "\tsome expr_index, expr in rule.body", + "}", + "" + ], + "abs": "/Users/anderseknert/git/styra/regal/bundle/regal/ast/search.rego" + }, + "environment": { + "path_separator": "/" + } + } +} diff --git a/third_party/opa/v1/repl/errors.go b/third_party/opa/v1/repl/errors.go new file mode 100644 index 000000000000..e5286a44389c --- /dev/null +++ b/third_party/opa/v1/repl/errors.go @@ -0,0 +1,38 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package repl + +import "fmt" + +// Error is the error type returned by the REPL. +type Error struct { + Code string + Message string +} + +func (err *Error) Error() string { + return fmt.Sprintf("code %v: %v", err.Code, err.Message) +} + +const ( + // BadArgsErr indicates bad arguments were provided to a built-in REPL + // command. + BadArgsErr string = "bad arguments" +) + +func newBadArgsErr(f string, a ...any) *Error { + return &Error{ + Code: BadArgsErr, + Message: fmt.Sprintf(f, a...), + } +} + +// stop is returned by the 'exit' command to indicate to the REPL that it should +// break and return. +type stop struct{} + +func (stop) Error() string { + return "" +} diff --git a/third_party/opa/v1/repl/example_test.go b/third_party/opa/v1/repl/example_test.go new file mode 100644 index 000000000000..fac9710f2c6d --- /dev/null +++ b/third_party/opa/v1/repl/example_test.go @@ -0,0 +1,60 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package repl_test + +import ( + "bytes" + "context" + "fmt" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/repl" + "github.com/open-policy-agent/opa/v1/storage/inmem" +) + +// nolint // example code +func ExampleREPL_OneShot() { + // Initialize context for the example. Normally the caller would obtain the + // context from an input parameter or instantiate their own. + ctx := context.Background() + + // Instantiate the policy engine's storage layer. + store := inmem.New() + + // Create a buffer that will receive REPL output. + var buf bytes.Buffer + + // Create a new REPL. + r := repl.New(store, "", &buf, "json", 0, ""). + WithRegoVersion(ast.RegoV1) + + // Define a rule inside the REPL. + r.OneShot(ctx, "p if { a = [1, 2, 3, 4]; a[_] > 3 }") + + // Query the rule defined above. + r.OneShot(ctx, "p") + + // Inspect the output. Defining rules does not produce output so we only expect + // output from the second line of input. + fmt.Println(buf.String()) + + // Output: + // { + // "result": [ + // { + // "expressions": [ + // { + // "value": true, + // "text": "p", + // "location": { + // "row": 1, + // "col": 1 + // } + // } + // ] + // } + // ] + // } +} diff --git a/third_party/opa/v1/repl/repl.go b/third_party/opa/v1/repl/repl.go new file mode 100644 index 000000000000..612cfa8b1cf3 --- /dev/null +++ b/third_party/opa/v1/repl/repl.go @@ -0,0 +1,1646 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package repl implements a Read-Eval-Print-Loop (REPL) for interacting with the policy engine. +// +// The REPL is typically used from the command line, however, it can also be used as a library. +// nolint: goconst // String reuse here doesn't make sense to deduplicate. +package repl + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "maps" + "os" + "strconv" + "strings" + "sync" + + "github.com/peterh/liner" + + "github.com/open-policy-agent/opa/internal/future" + pr "github.com/open-policy-agent/opa/internal/presentation" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/compile" + "github.com/open-policy-agent/opa/v1/format" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/profiler" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/lineage" + "github.com/open-policy-agent/opa/v1/version" +) + +// REPL represents an instance of the interactive shell. +type REPL struct { + output io.Writer + stderr io.Writer + store storage.Store + runtime *ast.Term + + modules map[string]*ast.Module + currentModuleID string + buffer []string + txn storage.Transaction + metrics metrics.Metrics + profiler bool + strictBuiltinErrors bool + capabilities *ast.Capabilities + regoVersion ast.RegoVersion + initBundles map[string]*bundle.Bundle + + // TODO(tsandall): replace this state with rule definitions + // inside the default module. + outputFormat string + explain explainMode + instrument bool + historyPath string + initPrompt string + bufferPrompt string + banner string + types bool + unknowns []*ast.Term + bufferDisabled bool + undefinedDisabled bool + errLimit int + prettyLimit int + report [][2]string + target string // target type (wasm, rego, etc.) + mtx sync.Mutex +} + +type explainMode string + +const ( + explainOff explainMode = "off" + explainFull explainMode = "full" + explainNotes explainMode = "notes" + explainFails explainMode = "fails" + explainDebug explainMode = "debug" +) + +func parseExplainMode(str string) (explainMode, error) { + validExplainModes := []string{ + string(explainOff), + string(explainFull), + string(explainNotes), + string(explainFails), + string(explainDebug), + } + + for _, mode := range validExplainModes { + if mode == str { + return explainMode(mode), nil + } + } + + return "", fmt.Errorf("invalid explain mode, expected one of: %s", strings.Join(validExplainModes, ", ")) +} + +const defaultPrettyLimit = 80 + +var allowedTargets = map[string]bool{compile.TargetRego: true, compile.TargetWasm: true} + +const exitPromptMessage = "Do you want to exit ([y]/n)? " + +// New returns a new instance of the REPL. +func New(store storage.Store, historyPath string, output io.Writer, outputFormat string, errLimit int, banner string) *REPL { + + return &REPL{ + output: output, + store: store, + modules: map[string]*ast.Module{}, + capabilities: ast.CapabilitiesForThisVersion(), + outputFormat: outputFormat, + explain: explainOff, + historyPath: historyPath, + initPrompt: "> ", + bufferPrompt: "| ", + banner: banner, + errLimit: errLimit, + prettyLimit: defaultPrettyLimit, + target: "", + regoVersion: ast.DefaultRegoVersion, + } +} + +func (r *REPL) WithCapabilities(capabilities *ast.Capabilities) *REPL { + r.capabilities = capabilities + return r +} + +func (r *REPL) WithInitBundles(b map[string]*bundle.Bundle) *REPL { + r.initBundles = b + return r +} + +func defaultModule() *ast.Module { + return ast.MustParseModule(`package repl`) +} + +func defaultPackage() *ast.Package { + return ast.MustParsePackage(`package repl`) +} + +func (r *REPL) getCurrentOrDefaultModule() *ast.Module { + if r.currentModuleID == "" { + return defaultModule() + } + return r.modules[r.currentModuleID] +} + +func (r *REPL) initModule(ctx context.Context) error { + if r.currentModuleID != "" { + return nil + } + return r.evalStatement(ctx, defaultPackage()) +} + +func (r *REPL) WithStderrWriter(w io.Writer) *REPL { + r.stderr = w + return nil +} + +// Loop will run until the user enters "exit", Ctrl+C, Ctrl+D, or an unexpected error occurs. +func (r *REPL) Loop(ctx context.Context) error { + + // Initialize the liner library. + line := liner.NewLiner() + defer line.Close() + line.SetCtrlCAborts(true) + line.SetMultiLineMode(true) + r.loadHistory(line) + + if len(r.banner) > 0 { + fmt.Fprintln(r.output, r.banner) + } + + line.SetCompleter(r.complete) + +loop: + for { + + input, err := line.Prompt(r.getPrompt()) + + // prompt on ctrl+d + if err == io.EOF { + goto exitPrompt + } + + // reset on ctrl+c + if err == liner.ErrPromptAborted { + continue + } + + // exit on unknown error + if err != nil { + fmt.Fprintln(r.output, "error (fatal):", err) + return err + } + + if err := r.OneShot(ctx, input); err != nil { + switch err := err.(type) { + case stop: + goto exit + default: + fmt.Fprintln(r.output, err) + } + } + + line.AppendHistory(input) + } + +exitPrompt: + fmt.Fprintln(r.output) + + for { + input, err := line.Prompt(exitPromptMessage) + + // exit on ctrl+d + if err == io.EOF { + break + } + + // reset on ctrl+c + if err == liner.ErrPromptAborted { + goto loop + } + + // exit on unknown error + if err != nil { + fmt.Fprintln(r.output, "error (fatal):", err) + return err + } + + switch strings.ToLower(input) { + case "", "y", "yes": + goto exit + case "n", "no": + goto loop + } + } + +exit: + r.saveHistory(line) + return nil +} + +// OneShot evaluates the line and prints the result. If an error occurs it is +// returned for the caller to display. +func (r *REPL) OneShot(ctx context.Context, line string) error { + + var err error + r.txn, err = r.store.NewTransaction(ctx) + if err != nil { + return err + } + + defer r.store.Abort(ctx, r.txn) + + if r.metrics != nil { + defer r.metrics.Clear() + } + + if len(r.buffer) == 0 { + if cmd := newCommand(line); cmd != nil { + switch cmd.op { + case "dump": + return r.cmdDump(ctx, cmd.args) + case "json": + return r.cmdFormat("json") + case "show": + return r.cmdShow(cmd.args) + case "unset": + return r.cmdUnset(ctx, cmd.args) + case "unset-package": + return r.cmdUnsetPackage(ctx, cmd.args) + case "pretty": + return r.cmdFormat("pretty") + case "pretty-limit": + return r.cmdPrettyLimit(cmd.args) + case "trace": + // If an argument is specified, e.g. `trace notes`, parse that + // argument and toggle that specific mode. If no argument is + // specified, toggle full explain mode since that is backwards- + // compatible. + if len(cmd.args) == 1 { + explainMode, err := parseExplainMode(cmd.args[0]) + if err != nil { + return err + } + return r.cmdTrace(explainMode) + } + return r.cmdTrace(explainFull) + case "notes": + return r.cmdTrace(explainNotes) + case "fails": + return r.cmdTrace(explainFails) + case "metrics": + return r.cmdMetrics() + case "instrument": + return r.cmdInstrument() + case "profile": + return r.cmdProfile() + case "types": + return r.cmdTypes() + case "unknown": + return r.cmdUnknown(cmd.args) + case "strict-builtin-errors": + return r.cmdStrictBuiltinErrors() + case "target": + return r.cmdTarget(cmd.args) + case "help": + return r.cmdHelp(cmd.args) + case "exit": + return r.cmdExit() + } + } + + r.buffer = append(r.buffer, line) + return r.evalBufferOne(ctx) + } + + r.buffer = append(r.buffer, line) + if len(line) == 0 { + return r.evalBufferMulti(ctx) + } + + return nil +} + +// DisableMultiLineBuffering causes the REPL to not buffer lines when a parse +// error occurs. Instead, the error will be returned to the caller. +func (r *REPL) DisableMultiLineBuffering(yes bool) *REPL { + r.bufferDisabled = yes + return r +} + +// DisableUndefinedOutput causes the REPL to not print any output when the query +// is undefined. +func (r *REPL) DisableUndefinedOutput(yes bool) *REPL { + r.undefinedDisabled = yes + return r +} + +// WithRuntime sets the runtime data to provide to the evaluation engine. +func (r *REPL) WithRuntime(term *ast.Term) *REPL { + r.runtime = term + return r +} + +// WithRegoVersion sets the Rego version to v. +func (r *REPL) WithRegoVersion(v ast.RegoVersion) *REPL { + r.regoVersion = v + return r +} + +// WithV1Compatible sets the Rego version to v1. +// Deprecated: Use WithRegoVersion instead. +func (r *REPL) WithV1Compatible(v1Compatible bool) *REPL { + if v1Compatible { + r.regoVersion = ast.RegoV1 + } else { + r.regoVersion = ast.DefaultRegoVersion + } + return r +} + +// SetOPAVersionReport sets the information about the latest OPA release. +func (r *REPL) SetOPAVersionReport(report [][2]string) { + r.mtx.Lock() + defer r.mtx.Unlock() + r.report = report +} + +func (r *REPL) complete(line string) []string { + c := []string{} + set := map[string]struct{}{} + ctx := context.Background() + txn, err := r.store.NewTransaction(ctx) + + if err != nil { + fmt.Fprintln(r.output, "error:", err) + return c + } + + defer r.store.Abort(ctx, txn) + + // add imports + for _, mod := range r.modules { + for _, imp := range future.FilterFutureImports(mod.Imports) { + path := imp.Name().String() + if strings.HasPrefix(path, line) { + set[path] = struct{}{} + } + } + } + + // add virtual docs defined in repl + for _, mod := range r.modules { + for _, rule := range mod.Rules { + path := rule.Path().String() + if strings.HasPrefix(path, line) { + set[path] = struct{}{} + } + } + } + + mods, err := r.loadModules(ctx, txn) + if err != nil { + fmt.Fprintln(r.output, "error:", err) + return c + } + + // add virtual docs defined by policies + for _, mod := range mods { + for _, rule := range mod.Rules { + path := rule.Path().String() + if strings.HasPrefix(path, line) { + set[path] = struct{}{} + } + } + } + + for path := range set { + c = append(c, path) + } + return c +} + +func (r *REPL) cmdDump(ctx context.Context, args []string) error { + if len(args) == 0 { + return r.cmdDumpOutput(ctx) + } + return r.cmdDumpPath(ctx, args[0]) +} + +func (r *REPL) cmdDumpOutput(ctx context.Context) error { + return dumpStorage(ctx, r.store, r.txn, r.output) +} + +func (r *REPL) cmdDumpPath(ctx context.Context, filename string) error { + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + return dumpStorage(ctx, r.store, r.txn, f) +} + +func (*REPL) cmdExit() error { + return stop{} +} + +func (r *REPL) cmdFormat(s string) error { + r.outputFormat = s + return nil +} + +func (r *REPL) cmdTarget(t []string) error { + if len(t) != 1 { + return newBadArgsErr("target : expects exactly one argument") + } + + if _, ok := allowedTargets[t[0]]; !ok { + return fmt.Errorf("invalid target \"%v\":must be one of {rego,wasm}", t[0]) + } + + r.target = t[0] + + r.checkTraceSupported() + return nil +} + +func (r *REPL) cmdPrettyLimit(s []string) error { + if len(s) != 1 { + return errors.New("usage: pretty-limit ") + } + i64, err := strconv.ParseInt(s[0], 10, 0) + if err != nil { + return err + } + r.prettyLimit = int(i64) + return nil +} + +func (r *REPL) cmdHelp(args []string) error { + if len(args) == 0 { + printHelp(r.output, r.initPrompt, r.report) + } else { + if desc, ok := topics[args[0]]; ok { + return desc.fn(r.output) + } + return fmt.Errorf("unknown topic '%v'", args[0]) + } + return nil +} + +func (r *REPL) cmdShow(args []string) error { + + if len(args) == 0 { + if r.currentModuleID == "" { + fmt.Fprintln(r.output, "no rules defined") + return nil + } + module := r.modules[r.currentModuleID] + bs, err := format.AstWithOpts(module, format.Opts{RegoVersion: module.RegoVersion()}) + if err != nil { + return err + } + fmt.Fprint(r.output, string(bs)) + return nil + } else if args[0] == "debug" { + debug := replDebugState{ + Explain: r.explain, + Metrics: r.metricsEnabled(), + Instrument: r.instrument, + Profile: r.profilerEnabled(), + StrictBuiltinErrors: r.strictBuiltinErrors, + } + b, err := json.MarshalIndent(debug, "", "\t") + if err != nil { + return fmt.Errorf("error: %v", err) + } + fmt.Fprintln(r.output, string(b)) + return nil + } + return fmt.Errorf("unknown option '%v'", args[0]) +} + +type replDebugState struct { + Explain explainMode `json:"explain"` + Metrics bool `json:"metrics"` + Instrument bool `json:"instrument"` + Profile bool `json:"profile"` + StrictBuiltinErrors bool `json:"strict-builtin-errors"` +} + +func (r *REPL) cmdTrace(mode explainMode) error { + if r.explain == mode { + r.explain = explainOff + } else { + r.explain = mode + } + + r.checkTraceSupported() + return nil +} + +func (r *REPL) checkTraceSupported() { + if r.explain != explainOff && r.target == compile.TargetWasm { + fmt.Fprintf(r.output, "warning: trace mode \"%v\" is not supported with wasm target\n", r.explain) + } +} + +func (r *REPL) metricsEnabled() bool { + return r.metrics != nil +} + +func (r *REPL) cmdMetrics() error { + if r.metrics == nil { + r.metrics = metrics.New() + } else { + r.metrics = nil + } + r.instrument = false + return nil +} + +func (r *REPL) cmdInstrument() error { + if r.instrument { + r.metrics = nil + r.instrument = false + } else { + r.metrics = metrics.New() + r.instrument = true + } + return nil +} + +func (r *REPL) profilerEnabled() bool { + return r.profiler +} + +func (r *REPL) cmdProfile() error { + if r.profiler { + r.profiler = false + } else { + r.profiler = true + } + return nil +} + +func (r *REPL) cmdStrictBuiltinErrors() error { + r.strictBuiltinErrors = !r.strictBuiltinErrors + return nil +} + +func (r *REPL) cmdTypes() error { + r.types = !r.types + return nil +} + +var errUnknownUsage = errors.New("usage: unknown [ [...]] (hint: try 'input')") + +func (r *REPL) cmdUnknown(s []string) error { + + if len(s) == 0 && len(r.unknowns) == 0 { + return errUnknownUsage + } + + unknowns := make([]*ast.Term, len(s)) + + for i := range unknowns { + + ref, err := ast.ParseRef(s[i]) + if err != nil { + return errUnknownUsage + } + + unknowns[i] = ast.NewTerm(ref) + } + + r.unknowns = unknowns + return nil +} + +func (r *REPL) cmdUnset(ctx context.Context, args []string) error { + if len(args) != 1 { + return newBadArgsErr("unset : expects exactly one argument") + } + + term, err := ast.ParseTerm(args[0]) + if err != nil { + return newBadArgsErr("argument must identify a rule") + } + + v, ok := term.Value.(ast.Var) + + if !ok { + ref, ok := term.Value.(ast.Ref) + if !ok || !ast.RootDocumentNames.Contains(ref[0]) { + return newBadArgsErr("arguments must identify a rule") + } + v = ref[0].Value.(ast.Var) + } + + unset, err := r.unsetRule(ctx, v) + if err != nil { + return err + } else if !unset { + fmt.Fprintln(r.output, "warning: no matching rules in current module") + } + + return nil +} + +func (r *REPL) cmdUnsetPackage(ctx context.Context, args []string) error { + if len(args) != 1 { + return newBadArgsErr("unset-package : expects exactly one argument") + } + + pkg, err := ast.ParsePackage("package " + args[0]) + if err != nil { + return newBadArgsErr("argument must identify a package") + } + + unset, err := r.unsetPackage(ctx, pkg) + if err != nil { + return err + } else if !unset { + fmt.Fprintln(r.output, "warning: no matching package") + } + + return nil +} + +func (r *REPL) unsetRule(ctx context.Context, name ast.Var) (bool, error) { + if r.currentModuleID == "" { + return false, nil + } + + mod := r.modules[r.currentModuleID] + rules := []*ast.Rule{} + + for _, r := range mod.Rules { + if !r.Head.Name.Equal(name) { + rules = append(rules, r) + } + } + + if len(rules) == len(mod.Rules) { + return false, nil + } + + cpy := mod.Copy() + cpy.Rules = rules + err := r.recompile(ctx, cpy) + if err != nil { + return false, err + } + + return true, nil +} + +func (r *REPL) unsetPackage(_ context.Context, pkg *ast.Package) (bool, error) { + path := pkg.Path.String() + _, ok := r.modules[path] + if ok { + delete(r.modules, path) + } else { + return false, nil + } + + // Change back to default module if current one is being removed + if r.currentModuleID == path { + r.currentModuleID = "" + } + + return true, nil +} + +func (r *REPL) timerStart(msg string) { + if r.metrics != nil { + r.metrics.Timer(msg).Start() + } +} + +func (r *REPL) timerStop(msg string) { + if r.metrics != nil { + r.metrics.Timer(msg).Stop() + } +} + +func (r *REPL) recompile(ctx context.Context, cpy *ast.Module) error { + policies, err := r.loadModules(ctx, r.txn) + if err != nil { + return err + } + + policies[r.currentModuleID] = cpy + + for id, mod := range r.modules { + if id != r.currentModuleID { + policies[id] = mod + } + } + + compiler := ast.NewCompiler(). + SetErrorLimit(r.errLimit). + WithEnablePrintStatements(true). + WithCapabilities(r.capabilities) + + if r.instrument { + compiler.WithMetrics(r.metrics) + } + + if compiler.Compile(policies); compiler.Failed() { + return compiler.Errors + } + + r.modules[r.currentModuleID] = cpy + return nil +} + +func (r *REPL) compileBody(_ context.Context, compiler *ast.Compiler, body ast.Body) (ast.Body, *ast.TypeEnv, error) { + r.timerStart(metrics.RegoQueryCompile) + defer r.timerStop(metrics.RegoQueryCompile) + + qctx := ast.NewQueryContext() + + if r.currentModuleID != "" { + qctx = qctx.WithPackage(r.modules[r.currentModuleID].Package). + WithImports(future.FilterFutureImports(r.modules[r.currentModuleID].Imports)) + } + + qc := compiler.QueryCompiler().WithContext(qctx).WithEnablePrintStatements(true) + body, err := qc.Compile(body) + return body, qc.TypeEnv(), err +} + +func (r *REPL) compileRule(ctx context.Context, rule *ast.Rule) error { + + var unset bool + + if r.regoVersion == ast.RegoV1 { + if errs := ast.CheckRegoV1(rule); errs != nil { + return errs + } + } + + if rule.Head.Assign { + var err error + unset, err = r.unsetRule(ctx, rule.Head.Name) + if err != nil { + return err + } + } + + r.timerStart(metrics.RegoModuleCompile) + defer r.timerStop(metrics.RegoModuleCompile) + + if err := r.initModule(ctx); err != nil { + return err + } + + mod := r.modules[r.currentModuleID] + prev := mod.Rules + mod.Rules = append(mod.Rules, rule) + ast.WalkRules(rule, func(r *ast.Rule) bool { + r.Module = mod + return false + }) + + policies, err := r.loadModules(ctx, r.txn) + if err != nil { + return err + } + + maps.Copy(policies, r.modules) + + compiler := ast.NewCompiler(). + SetErrorLimit(r.errLimit). + WithEnablePrintStatements(true). + WithCapabilities(r.capabilities) + + if r.instrument { + compiler.WithMetrics(r.metrics) + } + + if compiler.Compile(policies); compiler.Failed() { + mod.Rules = prev + return compiler.Errors + } + + switch r.outputFormat { + case "json": + default: + msg := "defined" + if unset { + msg = "re-defined" + } + fmt.Fprintf(r.output, "Rule '%v' %v in %v. Type 'show' to see rules.\n", rule.Head.Ref().String(), msg, mod.Package) + } + + return nil +} + +func (r *REPL) evalBufferOne(ctx context.Context) error { + + line := strings.Join(r.buffer, "\n") + + if len(strings.TrimSpace(line)) == 0 { + r.buffer = []string{} + return nil + } + + popts, err := r.parserOptions() + if err != nil { + return err + } + + // The user may enter lines with comments on the end or + // multiple lines with comments interspersed. In these cases + // the parser will return multiple statements. + r.timerStart(metrics.RegoQueryParse) + stmts, _, err := ast.ParseStatementsWithOpts("", line, popts) + r.timerStop(metrics.RegoQueryParse) + + if err != nil { + if r.bufferDisabled { + return err + } + return nil + } + + r.buffer = []string{} + + for _, stmt := range stmts { + if err := r.evalStatement(ctx, stmt); err != nil { + return err + } + } + + return nil +} + +func (r *REPL) evalBufferMulti(ctx context.Context) error { + + line := strings.Join(r.buffer, "\n") + r.buffer = []string{} + + if len(strings.TrimSpace(line)) == 0 { + return nil + } + + popts, err := r.parserOptions() + if err != nil { + return err + } + + r.timerStart(metrics.RegoQueryParse) + stmts, _, err := ast.ParseStatementsWithOpts("", line, popts) + r.timerStop(metrics.RegoQueryParse) + + if err != nil { + return err + } + + for _, stmt := range stmts { + if err := r.evalStatement(ctx, stmt); err != nil { + return err + } + } + + return nil +} + +func (r *REPL) parserOptions() (ast.ParserOptions, error) { + if r.regoVersion == ast.RegoV1 { + return ast.ParserOptions{RegoVersion: ast.RegoV1}, nil + } + if r.currentModuleID != "" { + opts, err := future.ParserOptionsFromFutureImports(r.modules[r.currentModuleID].Imports) + if err == nil { + for _, i := range r.modules[r.currentModuleID].Imports { + if ast.Compare(i.Path.Value, ast.RegoV1CompatibleRef) == 0 { + opts.RegoVersion = ast.RegoV1 + } + } + } + return opts, err + } + return ast.ParserOptions{RegoVersion: r.regoVersion}, nil +} + +func (r *REPL) loadCompiler(ctx context.Context) (*ast.Compiler, error) { + + r.timerStart(metrics.RegoModuleCompile) + defer r.timerStop(metrics.RegoModuleCompile) + + policies, err := r.loadModules(ctx, r.txn) + if err != nil { + return nil, err + } + + maps.Copy(policies, r.modules) + + compiler := ast.NewCompiler(). + SetErrorLimit(r.errLimit). + WithEnablePrintStatements(true). + WithCapabilities(r.capabilities) + + if r.instrument { + compiler.WithMetrics(r.metrics) + } + + if compiler.Compile(policies); compiler.Failed() { + return nil, compiler.Errors + } + + return compiler, nil +} + +// loadInput returns the input defined in the REPL. The REPL loads the +// input from the data.repl.input document. +func (r *REPL) loadInput(ctx context.Context, compiler *ast.Compiler) (ast.Value, error) { + + q := topdown.NewQuery(ast.MustParseBody("data.repl.input = x")). + WithCompiler(compiler). + WithStore(r.store). + WithTransaction(r.txn) + + qrs, err := q.Run(ctx) + if err != nil { + return nil, err + } + + if len(qrs) != 1 { + return nil, nil + } + + return qrs[0][ast.Var("x")].Value, nil +} + +func (r *REPL) evalStatement(ctx context.Context, stmt any) error { + switch stmt := stmt.(type) { + case ast.Body: + compiler, err := r.loadCompiler(ctx) + if err != nil { + return err + } + + input, err := r.loadInput(ctx, compiler) + if err != nil { + return err + } + + if ok, err := r.interpretAsRule(ctx, compiler, stmt); ok || err != nil { + return err + } + + compiledBody, typeEnv, err := r.compileBody(ctx, compiler, stmt) + if err != nil { + return err + } + + if len(r.unknowns) > 0 { + err = r.evalPartial(ctx, compiler, input, compiledBody) + } else { + err = r.evalBody(ctx, compiler, input, stmt) + if r.types { + r.printTypes(ctx, typeEnv, compiledBody) + } + } + + return err + case *ast.Rule: + return r.compileRule(ctx, stmt) + case *ast.Import: + return r.evalImport(ctx, stmt) + case *ast.Package: + return r.evalPackage(stmt) + } + return nil +} + +func (r *REPL) evalBody(ctx context.Context, compiler *ast.Compiler, input ast.Value, body ast.Body) error { + + var tracebuf *topdown.BufferTracer + var prof *profiler.Profiler + + args := []func(*rego.Rego){ + rego.Compiler(compiler), + rego.Store(r.store), + rego.Transaction(r.txn), + rego.ParsedImports(r.getCurrentOrDefaultModule().Imports), + rego.ParsedPackage(r.getCurrentOrDefaultModule().Package), + rego.ParsedQuery(body), + rego.ParsedInput(input), + rego.Metrics(r.metrics), + rego.Instrument(r.instrument), + rego.Runtime(r.runtime), + rego.StrictBuiltinErrors(r.strictBuiltinErrors), + rego.Target(r.target), + rego.EnablePrintStatements(true), + rego.PrintHook(topdown.NewPrintHook(r.stderrWriter())), + } + + if r.explain != explainOff { + tracebuf = topdown.NewBufferTracer() + args = append(args, rego.QueryTracer(tracebuf)) + } + + if r.profiler { + prof = profiler.New() + args = append(args, rego.QueryTracer(prof)) + } + + eval := rego.New(args...) + rs, err := eval.Eval(ctx) + + output := pr.Output{ + Errors: pr.NewOutputErrors(err), + Result: rs, + Metrics: r.metrics, + } + + if r.profiler { + output.Profile = prof.ReportTopNResults(-1, pr.DefaultProfileSortOrder) + } + + output = output.WithLimit(r.prettyLimit) + + switch r.explain { + case explainDebug: + output.Explanation = lineage.Debug(*tracebuf) + case explainFull: + output.Explanation = lineage.Full(*tracebuf) + case explainNotes: + output.Explanation = lineage.Notes(*tracebuf) + case explainFails: + output.Explanation = lineage.Fails(*tracebuf) + } + + switch r.outputFormat { + case "json": + return pr.JSON(r.output, output) + default: + return pr.Pretty(r.output, output) + } +} + +func (r *REPL) evalPartial(ctx context.Context, compiler *ast.Compiler, input ast.Value, body ast.Body) error { + + var buf *topdown.BufferTracer + + if r.explain != explainOff { + buf = topdown.NewBufferTracer() + } + + eval := rego.New( + rego.Compiler(compiler), + rego.Store(r.store), + rego.Transaction(r.txn), + rego.ParsedImports(r.getCurrentOrDefaultModule().Imports), + rego.ParsedPackage(r.getCurrentOrDefaultModule().Package), + rego.ParsedQuery(body), + rego.ParsedInput(input), + rego.Metrics(r.metrics), + rego.QueryTracer(buf), + rego.Instrument(r.instrument), + rego.ParsedUnknowns(r.unknowns), + rego.Runtime(r.runtime), + rego.StrictBuiltinErrors(r.strictBuiltinErrors), + rego.EnablePrintStatements(true), + rego.PrintHook(topdown.NewPrintHook(r.stderrWriter())), + ) + + pq, err := eval.Partial(ctx) + + output := pr.Output{ + Metrics: r.metrics, + Partial: pq, + Errors: pr.NewOutputErrors(err), + } + + switch r.explain { + case explainDebug: + output.Explanation = lineage.Debug(*buf) + case explainFull: + output.Explanation = lineage.Full(*buf) + case explainNotes: + output.Explanation = lineage.Notes(*buf) + case explainFails: + output.Explanation = lineage.Fails(*buf) + } + + switch r.outputFormat { + case "json": + return pr.JSON(r.output, output) + default: + return pr.Pretty(r.output, output) + } +} + +func (r *REPL) evalImport(ctx context.Context, i *ast.Import) error { + + if err := r.initModule(ctx); err != nil { + return err + } + + mod := r.modules[r.currentModuleID] + + for _, other := range mod.Imports { + if other.Equal(i) { + return nil + } + } + + mod.Imports = append(mod.Imports, i) + + return nil +} + +func (r *REPL) evalPackage(p *ast.Package) error { + moduleID := p.Path.String() + + if _, ok := r.modules[moduleID]; ok { + r.currentModuleID = moduleID + return nil + } + + m := ast.Module{ + Package: p, + } + m.SetRegoVersion(r.regoVersion) + r.modules[moduleID] = &m + + r.currentModuleID = moduleID + + return nil +} + +// interpretAsRule attempts to interpret the supplied query as a rule +// definition. If the query is a single := or = statement and it can be +// converted into a rule and compiled, then it will be interpreted as such. This +// allows users to define constants in the REPL. For example: +// +// > a = 1 +// > a +// 1 +// +// If the expression is a = statement, then an additional check on the left +// hand side occurs. For example: +// +// > b = 2 +// > b = 2 +// true # not redefined! +func (r *REPL) interpretAsRule(ctx context.Context, compiler *ast.Compiler, body ast.Body) (bool, error) { + + if len(body) != 1 { + return false, nil + } + + expr := body[0] + + if len(expr.Operands()) != 2 { + return false, nil + } + + if expr.IsAssignment() { + rule, err := ast.ParseCompleteDocRuleFromAssignmentExpr(r.getCurrentOrDefaultModule(), expr.Operand(0), expr.Operand(1)) + if err != nil { + return false, nil + } + // TODO(sr): support interactive ref head rule definitions + if len(rule.Head.Ref()) > 1 { + return false, nil + } + + if err := r.compileRule(ctx, rule); err != nil { + return false, err + } + return rule != nil, nil + } + + if !expr.IsEquality() { + return false, nil + } + + if isGlobalInModule(compiler, r.getCurrentOrDefaultModule(), body[0].Operand(0)) { + return false, nil + } + + rule, err := ast.ParseCompleteDocRuleFromEqExpr(r.getCurrentOrDefaultModule(), expr.Operand(0), expr.Operand(1)) + if err != nil { + return false, nil + } + // TODO(sr): support interactive ref head rule definitions + if len(rule.Head.Ref()) > 1 { + return false, nil + } + + if err := r.compileRule(ctx, rule); err != nil { + return false, err + } + return rule != nil, nil +} + +func (r *REPL) getPrompt() string { + if len(r.buffer) > 0 { + return r.bufferPrompt + } + return r.initPrompt +} + +func (r *REPL) loadHistory(prompt *liner.State) { + if f, err := os.Open(r.historyPath); err == nil { + _, _ = prompt.ReadHistory(f) // ignore error + f.Close() + } +} + +func (r *REPL) loadModules(ctx context.Context, txn storage.Transaction) (map[string]*ast.Module, error) { + modules := make(map[string]*ast.Module) + + if len(r.initBundles) > 0 { + for bundleName, b := range r.initBundles { + maps.Copy(modules, b.ParsedModules(bundleName)) + } + } + + ids, err := r.store.ListPolicies(ctx, txn) + if err != nil { + return nil, err + } + + for _, id := range ids { + // skip re-parsing + if _, haveMod := modules[id]; haveMod { + continue + } + + bs, err := r.store.GetPolicy(ctx, txn, id) + if err != nil { + return nil, err + } + + popts := ast.ParserOptions{ + RegoVersion: r.regoVersion, + } + + parsed, err := ast.ParseModuleWithOpts(id, string(bs), popts) + if err != nil { + return nil, err + } + + modules[id] = parsed + } + + return modules, nil +} + +func (r *REPL) printTypes(_ context.Context, typeEnv *ast.TypeEnv, body ast.Body) { + + ast.WalkRefs(body, func(ref ast.Ref) bool { + fmt.Fprintf(r.output, "# %v: %v\n", ref, typeEnv.Get(ref)) + return false + }) + + vis := ast.NewVarVisitor().WithParams(ast.VarVisitorParams{ + SkipRefHead: true, + }) + + vis.Walk(body) + + for v := range vis.Vars() { + fmt.Fprintf(r.output, "# %v: %v\n", v, typeEnv.Get(v)) + } +} + +func (r *REPL) saveHistory(prompt *liner.State) { + if f, err := os.Create(r.historyPath); err == nil { + _, _ = prompt.WriteHistory(f) // ignore error + f.Close() + } +} + +func (r *REPL) stderrWriter() io.Writer { + if r.stderr != nil { + return r.stderr + } + return os.Stderr +} + +type commandDesc struct { + name string + args []string + help string +} + +func (c commandDesc) syntax() string { + if len(c.args) > 0 { + return fmt.Sprintf("%v %v", c.name, strings.Join(c.args, " ")) + } + return c.name +} + +type exampleDesc struct { + example string + comment string +} + +var examples = [...]exampleDesc{ + {"data", "show all documents"}, + {"data[x] = _", "show all top level keys"}, + {"data.system.version", "drill into specific document"}, +} + +var extra = [...]commandDesc{ + {"", []string{}, "evaluate the statement"}, + {"package", []string{""}, "change active package"}, + {"import", []string{""}, "add import to active module"}, +} + +var builtin = [...]commandDesc{ + {"show", []string{""}, "show active module definition"}, + {"show debug", []string{""}, "show REPL settings"}, + {"unset", []string{""}, "unset rules in currently active module"}, + {"unset-package", []string{""}, "unset packages in currently active module"}, + {"json", []string{}, "set output format to JSON"}, + {"pretty", []string{}, "set output format to pretty"}, + {"pretty-limit", []string{}, "set pretty value output limit"}, + {"trace", []string{"[mode]"}, "toggle full trace or specific mode"}, + {"notes", []string{}, "toggle notes trace"}, + {"fails", []string{}, "toggle fails trace"}, + {"metrics", []string{}, "toggle metrics"}, + {"instrument", []string{}, "toggle instrumentation"}, + {"profile", []string{}, "toggle profiler and turns off trace"}, + {"types", []string{}, "toggle type information"}, + {"unknown", []string{"[ref-1 [ref-2 [...]]]"}, "toggle partial evaluation mode"}, + {"strict-builtin-errors", []string{}, "toggle strict built-in error mode"}, + {"dump", []string{"[path]"}, "dump raw data in storage"}, + {"help", []string{"[topic]"}, "print this message"}, + {"target", []string{"[mode]"}, "set the runtime to exercise {rego,wasm} (default rego)"}, + {"exit", []string{}, "exit out of shell (or ctrl+d)"}, + {"ctrl+l", []string{}, "clear the screen"}, +} + +type topicDesc struct { + fn func(io.Writer) error + comment string +} + +var topics = map[string]topicDesc{ + "input": {printHelpInput, "how to set input document"}, + "partial": {printHelpPartial, "how to use partial evaluation"}, +} + +type command struct { + op string + args []string +} + +func newCommand(line string) *command { + p := strings.Fields(strings.TrimSpace(line)) + if len(p) == 0 { + return nil + } + inputCommand := strings.ToLower(p[0]) + for i := range builtin { + if builtin[i].name == inputCommand { + return &command{ + op: builtin[i].name, + args: p[1:], + } + } + } + return nil +} + +func dumpStorage(ctx context.Context, store storage.Store, txn storage.Transaction, w io.Writer) error { + data, err := store.Read(ctx, txn, storage.Path{}) + if err != nil { + return err + } + e := json.NewEncoder(w) + return e.Encode(data) +} + +func isGlobalInModule(compiler *ast.Compiler, module *ast.Module, term *ast.Term) bool { + + var name ast.Var + + if ast.RootDocumentRefs.Contains(term) { + name = term.Value.(ast.Ref)[0].Value.(ast.Var) + } else if v, ok := term.Value.(ast.Var); ok { + name = v + } else { + return false + } + + for _, imp := range module.Imports { + if imp.Name().Compare(name) == 0 { + return true + } + } + + path := module.Package.Path.Copy().Append(ast.StringTerm(string(name))) + node := compiler.RuleTree + + for _, elem := range path { + node = node.Child(elem.Value) + if node == nil { + return false + } + } + + return len(node.Values) > 0 +} + +func printHelp(output io.Writer, initPrompt string, report [][2]string) { + printHelpExamples(output, initPrompt) + printHelpCommands(output) + if len(report) != 0 { + printOPAReleaseInfo(output, report) + } +} + +func printHelpExamples(output io.Writer, promptSymbol string) { + + fmt.Fprintln(output, "") + fmt.Fprintln(output, "Examples") + fmt.Fprintln(output, "========") + fmt.Fprintln(output, "") + + maxLength := 0 + for _, ex := range examples { + if len(ex.example) > maxLength { + maxLength = len(ex.example) + } + } + + f := fmt.Sprintf("%v%%-%dv # %%v\n", promptSymbol, maxLength+1) + + for _, ex := range examples { + fmt.Fprintf(output, f, ex.example, ex.comment) + } + + fmt.Fprintln(output, "") +} + +func printHelpCommands(output io.Writer) { + + all := append(extra[:], builtin[:]...) + + // Compute max length of all command and topic names. + names := []string{} + + for _, x := range all { + names = append(names, x.syntax()) + } + for x := range topics { + names = append(names, "help "+x) + } + + maxLength := 0 + + for _, name := range names { + length := len(name) + if length > maxLength { + maxLength = length + } + } + + f := fmt.Sprintf("%%%dv : %%v\n", maxLength) + + // Print out command help. + fmt.Fprintln(output, "Commands") + fmt.Fprintln(output, "========") + fmt.Fprintln(output, "") + + for _, c := range all { + fmt.Fprintf(output, f, c.syntax(), c.help) + } + + fmt.Fprintln(output, "") + + // Print out topic help. + fmt.Fprintln(output, "Additional Topics") + fmt.Fprintln(output, "=================") + fmt.Fprintln(output, "") + + for key, desc := range topics { + fmt.Fprintf(output, f, "help "+key, desc.comment) + } + + fmt.Fprintln(output, "") +} + +func printOPAReleaseInfo(output io.Writer, report [][2]string) { + + fmt.Fprintln(output, "Version Info") + fmt.Fprintln(output, "============") + fmt.Fprintln(output) + + maxLen := 0 + + for _, pair := range report { + if len(pair[0]) > maxLen { + maxLen = len(pair[0]) + } + } + + fmtStr := fmt.Sprintf("%%-%dv : %%v\n", maxLen) + + fmt.Fprintf(output, fmtStr, "Current Version", version.Version) + for _, pair := range report { + fmt.Fprintf(output, fmtStr, pair[0], pair[1]) + } + + fmt.Fprintln(output, "") +} + +func printHelpInput(output io.Writer) error { + + printHelpTitle(output, "Input") + + txt := strings.TrimSpace(` +Rego allows queries to refer to documents outside of the storage layer. These +documents must be provided as inputs to the query engine. In Rego, these values +are nested under the root "input" document. + +In the interactive shell, users can set the value for the "input" document by +defining documents under the repl.input package. + +For example: + + # Change to the repl.input package. + > package repl.input + + # Define a new document called "params". + > params = {"method": "POST", "path": "/some/path"} + + # Switch back to another package to test access to input. + > package opa.example + + # Import "params" defined above. + > import input.params + + # Import a future keyword. + > import future.keywords.in + > 1 in [0, 2, 1] + true + + # Define rule that refers to "params". + > is_post { params.method = "POST" } + + # Test evaluation. + > is_post + true`) + "\n" + + fmt.Fprintln(output, txt) + return nil +} + +func printHelpPartial(output io.Writer) error { + + printHelpTitle(output, "Partial Evaluation") + + txt := strings.TrimSpace(` +Rego queries can be partially evaluated with respect to the specific unknown +variables, inputs, or any document rooted under data. The result of partial +evaluation is a new set of queries that can be evaluated later. + +For example: + + > allowed_methods = ["GET", "HEAD"] + + # Enable partial evaluation. Treat input document as unknown. + > unknown input + + # Partially evaluate a query. + > method = allowed_methods[i]; input.method = method + input.method = "GET"; i = 0; method = "GET" + input.method = "HEAD"; i = 1; method = "HEAD" + + # Turn off partial evaluation by running the 'unknown' command with no arguments. + > unknown`) + "\n" + + fmt.Fprintln(output, txt) + return nil +} + +func printHelpTitle(output io.Writer, title string) { + fmt.Fprintln(output, "") + fmt.Fprintln(output, title) + fmt.Fprintln(output, strings.Repeat("=", len(title))) + fmt.Fprintln(output, "") +} diff --git a/third_party/opa/v1/repl/repl_test.go b/third_party/opa/v1/repl/repl_test.go new file mode 100644 index 000000000000..8294f6ff6309 --- /dev/null +++ b/third_party/opa/v1/repl/repl_test.go @@ -0,0 +1,3407 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: goconst // string duplication is for test readability. +package repl + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "reflect" + "sort" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/presentation" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestFunction(t *testing.T) { + store := newTestStore() + ctx := context.Background() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + mod1 := []byte(`package a.b.c +import rego.v1 + +foo(x) = y if { + split(x, ".", y) +} + +bar([x, y]) = z if { + trim(x, y, z) +} +`) + + mod2 := []byte(`package a.b.d +import rego.v1 + +baz(_) = y if { + data.a.b.c.foo("barfoobar.bar", x) + data.a.b.c.bar(x, y) +}`) + + if err := store.UpsertPolicy(ctx, txn, "mod1", mod1); err != nil { + panic(err) + } + + if err := store.UpsertPolicy(ctx, txn, "mod2", mod2); err != nil { + panic(err) + } + + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + + var buf bytes.Buffer + repl := newRepl(store, &buf) + + // We import rego.v1 to ensure we're compatible with both v0 and v1 as default rego-version. + if err := repl.OneShot(ctx, "import rego.v1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, "json"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "data.a.b.d.baz(null, x)"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + exp := util.MustUnmarshalJSON([]byte(`{"result": [{"expressions": [{"text":"data.a.b.d.baz(null, x)", "value": true, "location": {"row": 1, "col": 1}}], "bindings": {"x": "foo"}}]}`)) + result := util.MustUnmarshalJSON(buf.Bytes()) + if !reflect.DeepEqual(exp, result) { + t.Fatalf("expected data.a.b.d.baz(x) to be %v, got %v", exp, result) + } + + if err := repl.OneShot(ctx, "p(x) = y if { y = x+4 }"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + buf.Reset() + if err := repl.OneShot(ctx, "data.repl.p(5, y)"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + exp = util.MustUnmarshalJSON([]byte(`{ + "result": [ + { + "expressions": [ + { + "text": "data.repl.p(5, y)", + "value": true, + "location": { + "col": 1, + "row": 1 + } + } + ], + "bindings": { + "y": 9 + } + } + ] + }`)) + result = util.MustUnmarshalJSON(buf.Bytes()) + if !reflect.DeepEqual(exp, result) { + t.Fatalf("expected datrepl.p(x) to be %v, got %v", exp, result) + } + + if err := repl.OneShot(ctx, "f(1, x) = y if { y = x }"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "f(2, x) = y if { y = x*2 }"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + buf.Reset() + if err := repl.OneShot(ctx, "data.repl.f(1, 2, y)"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + exp = util.MustUnmarshalJSON([]byte(`{ + "result": [ + { + "expressions": [ + { + "text": "data.repl.f(1, 2, y)", + "location": { + "col": 1, + "row": 1 + }, + "value": true + } + ], + "bindings": { + "y": 2 + } + } + ] + }`)) + result = util.MustUnmarshalJSON(buf.Bytes()) + if !reflect.DeepEqual(exp, result) { + t.Fatalf("expected data.repl.f(1, 2, y) to be %v, got %v", exp, result) + } + buf.Reset() + if err := repl.OneShot(ctx, "data.repl.f(2, 2, y)"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + exp = util.MustUnmarshalJSON([]byte(`{ + "result": [ + { + "expressions": [ + { + "text": "data.repl.f(2, 2, y)", + "location": { + "col": 1, + "row": 1 + }, + "value": true + } + ], + "bindings": { + "y": 4 + } + } + ] + }`)) + result = util.MustUnmarshalJSON(buf.Bytes()) + if !reflect.DeepEqual(exp, result) { + t.Fatalf("expected data.repl.f(2, 2, y) to be %v, got %v", exp, result) + } +} + +func TestComplete(t *testing.T) { + ctx := context.Background() + store := newTestStore() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + mod1 := []byte(`package a.b.c +import rego.v1 + +p = 1 if { true } +q = 2 if { true } +q = 3 if { false }`) + + mod2 := []byte(`package a.b.d +import rego.v1 + +r = 3 if { true }`) + + if err := store.UpsertPolicy(ctx, txn, "mod1", mod1); err != nil { + panic(err) + } + + if err := store.UpsertPolicy(ctx, txn, "mod2", mod2); err != nil { + panic(err) + } + + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + + var buf bytes.Buffer + repl := newRepl(store, &buf) + if err := repl.OneShot(ctx, "s = 4"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buf.Reset() + + result := repl.complete("") + expected := []string{ + "data.a.b.c.p", + "data.a.b.c.q", + "data.a.b.d.r", + "data.repl.s", + } + + sort.Strings(result) + sort.Strings(expected) + + if !reflect.DeepEqual(result, expected) { + t.Fatalf("Expected %v but got: %v", expected, result) + } + + result = repl.complete("data.a.b") + expected = []string{ + "data.a.b.c.p", + "data.a.b.c.q", + "data.a.b.d.r", + } + + sort.Strings(result) + sort.Strings(expected) + + if !reflect.DeepEqual(result, expected) { + t.Fatalf("Expected %v but got: %v", expected, result) + } + + result = repl.complete("data.a.b.c.p[x]") + expected = []string{} + + if !reflect.DeepEqual(result, expected) { + t.Fatalf("Expected %v but got: %v", expected, result) + } + + if err := repl.OneShot(ctx, "import data.a.b.c.p as xyz"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "import data.a.b.d"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result = repl.complete("x") + expected = []string{ + "xyz", + } + + if !reflect.DeepEqual(result, expected) { + t.Fatalf("Expected %v but got: %v", expected, result) + } +} + +func TestDump(t *testing.T) { + ctx := context.Background() + input := `{"a": [1,2,3,4]}` + var data map[string]any + err := util.UnmarshalJSON([]byte(input), &data) + if err != nil { + panic(err) + } + store := inmem.NewFromObject(data) + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "dump"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "{\"a\":[1,2,3,4]}\n") +} + +func TestDumpPath(t *testing.T) { + ctx := context.Background() + input := `{"a": [1,2,3,4]}` + var data map[string]any + err := util.UnmarshalJSON([]byte(input), &data) + if err != nil { + panic(err) + } + store := inmem.NewFromObject(data) + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + file := filepath.Join(t.TempDir(), "tmpfile") + if err := repl.OneShot(ctx, "dump "+file); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if buffer.String() != "" { + t.Errorf("Expected no output but got: %v", buffer.String()) + } + + bs, err := os.ReadFile(file) + if err != nil { + t.Fatalf("Expected file read to succeed but got: %v", err) + } + + var result map[string]any + if err := util.UnmarshalJSON(bs, &result); err != nil { + t.Fatalf("Expected json unmarshal to succeed but got: %v", err) + } + + if !reflect.DeepEqual(data, result) { + t.Fatalf("Expected dumped json to equal %v but got: %v", data, result) + } +} + +func TestDumpPathCaseSensitive(t *testing.T) { + ctx := context.Background() + input := `{"a": [1,2,3,4]}` + var data map[string]any + err := util.UnmarshalJSON([]byte(input), &data) + if err != nil { + panic(err) + } + store := inmem.NewFromObject(data) + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + file := filepath.Join(t.TempDir(), "tmpfile") + if err := repl.OneShot(ctx, "dump "+file); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if buffer.String() != "" { + t.Errorf("Expected no output but got: %v", buffer.String()) + } + + bs, err := os.ReadFile(file) + if err != nil { + t.Fatalf("Expected file read to succeed but got: %v", err) + } + + var result map[string]any + if err := util.UnmarshalJSON(bs, &result); err != nil { + t.Fatalf("Expected json unmarshal to succeed but got: %v", err) + } + + if !reflect.DeepEqual(data, result) { + t.Fatalf("Expected dumped json to equal %v but got: %v", data, result) + } +} + +func TestHelp(t *testing.T) { + topics["deadbeef"] = topicDesc{ + fn: func(w io.Writer) error { + fmt.Fprintln(w, "blah blah blah") + return nil + }, + } + + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "help deadbeef"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected := "blah blah blah\n" + + if buffer.String() != expected { + t.Fatalf("Unexpected output from help topic: %v", buffer.String()) + } +} + +func TestHelpWithOPAVersionReport(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + // empty report + repl.SetOPAVersionReport(nil) + if err := repl.OneShot(ctx, "help"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if strings.Contains(buffer.String(), "Version Info") { + t.Fatalf("Unexpected output from help: \"%v\"", buffer.String()) + } + + buffer.Reset() + + repl.SetOPAVersionReport([][2]string{ + {"Latest Upstream Version", "0.19.2"}, + {"Download", "https://openpolicyagent.org/downloads/v0.19.2/opa_darwin_amd64"}, + {"Release Notes", "https://github.com/open-policy-agent/opa/releases/tag/v0.19.2"}, + }) + if err := repl.OneShot(ctx, "help"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + exp := `Latest Upstream Version : 0.19.2 +Download : https://openpolicyagent.org/downloads/v0.19.2/opa_darwin_amd64 +Release Notes : https://github.com/open-policy-agent/opa/releases/tag/v0.19.2` + + if !strings.Contains(buffer.String(), exp) { + t.Fatalf("Expected output from help to contain: \"%v\" but got \"%v\"", exp, buffer.String()) + } +} + +func TestShowDebug(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "show debug"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + var result replDebugState + + if err := util.Unmarshal(buffer.Bytes(), &result); err != nil { + t.Fatal(err) + } + + var exp replDebugState + exp.Explain = explainOff + + if !reflect.DeepEqual(result, exp) { + t.Fatalf("Expected %+v but got %+v", exp, result) + } + + buffer.Reset() + + if err := repl.OneShot(ctx, "trace"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "metrics"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "instrument"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "profile"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "show debug"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + exp.Explain = explainFull + exp.Metrics = true + exp.Instrument = true + exp.Profile = true + + if err := util.Unmarshal(buffer.Bytes(), &result); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(result, exp) { + t.Fatalf("Expected %+v but got %+v", exp, result) + } +} + +// The rego.v1 import will be stripped from the output if the default rego-version is v1, +// so we need two flavours of this test: v0, and v1. +func TestShowV0(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer).WithRegoVersion(ast.RegoV0) + + if err := repl.OneShot(ctx, `package repl_test`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + assertREPLText(t, buffer, "package repl_test\n") + buffer.Reset() + + if err := repl.OneShot(ctx, "import input.xyz"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected := `package repl_test + +import input.xyz` + "\n" + assertREPLText(t, buffer, expected) + buffer.Reset() + + if err := repl.OneShot(ctx, "import data.foo as bar"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected = `package repl_test + +import data.foo as bar +import input.xyz` + "\n" + assertREPLText(t, buffer, expected) + buffer.Reset() + + if err := repl.OneShot(ctx, `p[1] { true }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, `p[2] { true }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected = `package repl_test + +import data.foo as bar +import input.xyz + +p[1] + +p[2]` + "\n" + assertREPLText(t, buffer, expected) + buffer.Reset() + + if err := repl.OneShot(ctx, "package abc"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + assertREPLText(t, buffer, "package abc\n") + buffer.Reset() + + if err := repl.OneShot(ctx, "package repl_test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + assertREPLText(t, buffer, expected) + buffer.Reset() +} + +func TestShowV1(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer).WithRegoVersion(ast.RegoV1) + + if err := repl.OneShot(ctx, `package repl_test`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + assertREPLText(t, buffer, "package repl_test\n") + buffer.Reset() + + if err := repl.OneShot(ctx, "import input.xyz"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected := `package repl_test + +import input.xyz` + "\n" + assertREPLText(t, buffer, expected) + buffer.Reset() + + if err := repl.OneShot(ctx, "import data.foo as bar"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected = `package repl_test + +import data.foo as bar +import input.xyz` + "\n" + assertREPLText(t, buffer, expected) + buffer.Reset() + + if err := repl.OneShot(ctx, `p contains 1 if { true }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, `p contains 2 if { true }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected = `package repl_test + +import data.foo as bar +import input.xyz + +p contains 1 + +p contains 2` + "\n" + assertREPLText(t, buffer, expected) + buffer.Reset() + + if err := repl.OneShot(ctx, "package abc"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + assertREPLText(t, buffer, "package abc\n") + buffer.Reset() + + if err := repl.OneShot(ctx, "package repl_test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + assertREPLText(t, buffer, expected) + buffer.Reset() +} + +func TestTypes(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + if err := repl.OneShot(ctx, "types"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // We import rego.v1 to ensure we're compatible with both v0 and v1 as default rego-version. + if err := repl.OneShot(ctx, "import rego.v1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, `p[x] = y if { x := "a"; y := 1 }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `p[x]`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + output := strings.TrimSpace(buffer.String()) + + exp := []string{ + "# data.repl.p[x]: number", + "# x: string", + } + + for i := range exp { + if !strings.Contains(output, exp[i]) { + t.Fatalf("Expected output to contain %q but got: %v", exp[i], output) + } + } + +} + +func TestUnknown(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + if err := repl.OneShot(ctx, "xs = [1,2,3]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + + err := repl.OneShot(ctx, "unknown input") + if err != nil { + t.Fatal("Unexpected command error:", err) + } + + if err := repl.OneShot(ctx, "data.repl.xs[i] = x; input.x = x"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + output := strings.TrimSpace(buffer.String()) + expected := strings.TrimSpace(` ++---------+-------------+ +| Query 1 | input.x = 1 | +| | i = 0 | +| | x = 1 | ++---------+-------------+ +| Query 2 | input.x = 2 | +| | i = 1 | +| | x = 2 | ++---------+-------------+ +| Query 3 | input.x = 3 | +| | i = 2 | +| | x = 3 | ++---------+-------------+ +`) + + if output != expected { + t.Fatalf("Unexpected output. Expected:\n\n%v\n\nGot:\n\n%v", expected, output) + } +} +func TestUnknownMetrics(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + if err := repl.OneShot(ctx, "xs = [1,2,3]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + + err := repl.OneShot(ctx, "unknown input") + if err != nil { + t.Fatal("Unexpected command error:", err) + } + + if err := repl.OneShot(ctx, "metrics"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, "data.repl.xs[i] = x; input.x = x"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + output := strings.TrimSpace(buffer.String()) + expected := strings.TrimSpace(` ++---------+-------------+ +| Query 1 | input.x = 1 | +| | i = 0 | +| | x = 1 | ++---------+-------------+ +| Query 2 | input.x = 2 | +| | i = 1 | +| | x = 2 | ++---------+-------------+ +| Query 3 | input.x = 3 | +| | i = 2 | +| | x = 3 | ++---------+-------------+ +`) + + if !strings.HasPrefix(output, expected) { + t.Fatalf("Unexpected partial eval results. Expected:\n\n%v\n\nGot:\n\n%v", expected, output) + } + + if !strings.Contains(output, "timer_rego_partial_eval_ns") { + t.Fatal("Expected timer_rego_partial_eval_ns but got:\n\n", output) + } +} + +func TestUnknownJSON(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + if err := repl.OneShot(ctx, "xs = [1,2,3]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + + err := repl.OneShot(ctx, "unknown input") + if err != nil { + t.Fatal("Unexpected command error:", err) + } + + if err := repl.OneShot(ctx, "json"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "data.repl.xs[i] = x; input.x = x"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + var result presentation.Output + + if err := json.NewDecoder(&buffer).Decode(&result); err != nil { + t.Fatal(err) + } + + if len(result.Partial.Queries) != 3 { + t.Fatalf("Expected exactly 3 queries in partial evaluation output but got: %v", result) + } +} + +func TestUnknownInvalid(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + err := repl.OneShot(ctx, "unknown x-1") + if err == nil || !strings.Contains(err.Error(), "usage: unknown ") { + t.Fatal("expected error from setting bad unknown but got:", err) + } + + // Ensure that partial evaluation has not been enabled. + buffer.Reset() + if err := repl.OneShot(ctx, "1+2"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := strings.TrimSpace(buffer.String()) + if result != "3" { + t.Fatal("want true but got:", result) + } +} + +func TestUnset(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + var err error + + // We import rego.v1 to ensure we're compatible with both v0 and v1 as default rego-version. + if err := repl.OneShot(ctx, "import rego.v1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, "magic = 23"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "p = 3.14"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "unset p"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + err = repl.OneShot(ctx, "p") + + if _, ok := err.(ast.Errors); !ok { + t.Fatalf("Expected AST error but got: %v", err) + } + + buffer.Reset() + if err := repl.OneShot(ctx, "p = 3.14"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `p = 3 if { false }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "unset p"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + err = repl.OneShot(ctx, "p") + if _, ok := err.(ast.Errors); !ok { + t.Fatalf("Expected AST error but got err: %v, output: %v", err, buffer.String()) + } + + if err := repl.OneShot(ctx, "unset "); err == nil { + t.Fatalf("Expected unset error for bad syntax but got: %v", buffer.String()) + } + + if err := repl.OneShot(ctx, "unset 1=1"); err == nil { + t.Fatalf("Expected unset error for bad syntax but got: %v", buffer.String()) + } + + if err := repl.OneShot(ctx, `unset "p"`); err == nil { + t.Fatalf("Expected unset error for bad syntax but got: %v", buffer.String()) + } + + buffer.Reset() + if err := repl.OneShot(ctx, "p(x) = y if { y = x }"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "unset p"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + err = repl.OneShot(ctx, "data.repl.p(1, 2)") + if err == nil || err.Error() != `1 error occurred: 1:1: rego_type_error: undefined function data.repl.p` { + t.Fatalf("Expected eval error (undefined built-in) but got err: '%v'", err) + } + + buffer.Reset() + if err := repl.OneShot(ctx, "p(1, x) = y if { y = x }"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "p(2, x) = y if { y = x+1 }"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "unset p"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + err = repl.OneShot(ctx, "data.repl.p(1, 2, 3)") + if err == nil || err.Error() != `1 error occurred: 1:1: rego_type_error: undefined function data.repl.p` { + t.Fatalf("Expected eval error (undefined built-in) but got err: '%v'", err) + } + + buffer.Reset() + if err := repl.OneShot(ctx, `unset q`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if buffer.String() != "warning: no matching rules in current module\n" { + t.Fatalf("Expected unset error for missing rule but got: %v", buffer.String()) + } + + buffer.Reset() + if err := repl.OneShot(ctx, `unset q`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if buffer.String() != "warning: no matching rules in current module\n" { + t.Fatalf("Expected unset error for missing function but got: %v", buffer.String()) + } + + buffer.Reset() + if err := repl.OneShot(ctx, `magic`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if buffer.String() != "23\n" { + t.Fatalf("Expected magic to be defined but got: %v", buffer.String()) + } + + buffer.Reset() + if err := repl.OneShot(ctx, `package data.other`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `unset magic`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if buffer.String() != "warning: no matching rules in current module\n" { + t.Fatalf("Expected unset error for bad syntax but got: %v", buffer.String()) + } +} + +func TestUnsetInputDocument(t *testing.T) { + // input is only allowed to be overridden in rego v0, so we only assert the following when that's the active version. + + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer).WithRegoVersion(ast.RegoV0) + + if err := repl.OneShot(ctx, `input = {}`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, `unset input`); err != nil { + t.Fatalf("Expected unset to succeed for input: %v", err) + } + + buffer.Reset() + if err := repl.OneShot(ctx, `not input`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if buffer.String() != "true\n" { + t.Fatalf("Expected unset input to remove input document: %v", buffer.String()) + } +} + +func TestOneShotEmptyBufferOneExpr(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "data.a[i].b.c[j] = 2"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "+---+---+\n| i | j |\n+---+---+\n| 0 | 1 |\n+---+---+\n") + buffer.Reset() + if err := repl.OneShot(ctx, "data.a[i].b.c[j] = \"deadbeef\""); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "undefined\n") +} + +func TestOneShotEmptyBufferOneRule(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + // We import rego.v1 to ensure we're compatible with both v0 and v1 as default rego-version. + if err := repl.OneShot(ctx, "import rego.v1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, `p contains x if { data.a[i] = x }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "Rule 'p' defined in package repl. Type 'show' to see rules.\n") +} + +func TestOneShotRefHeadRulePrinted(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + repl.regoVersion = ast.RegoV1 + + if err := repl.OneShot(ctx, `foo.bar.baz if { true }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "Rule 'foo.bar.baz' defined in package repl. Type 'show' to see rules.\n") +} + +func TestOneShotBufferedExpr(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "data.a[i].b.c[j] = "); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "") + if err := repl.OneShot(ctx, "2"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "") + if err := repl.OneShot(ctx, ""); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "+---+---+\n| i | j |\n+---+---+\n| 0 | 1 |\n+---+---+\n") +} + +func TestOneShotBufferedRule(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + // We import rego.v1 to ensure we're compatible with both v0 and v1 as default rego-version. + if err := repl.OneShot(ctx, "import rego.v1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, "p contains x if { "); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "") + if err := repl.OneShot(ctx, "data.a[i].b.c[1]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "") + if err := repl.OneShot(ctx, " = "); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "") + if err := repl.OneShot(ctx, "x"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "") + if err := repl.OneShot(ctx, "}"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "") + if err := repl.OneShot(ctx, ""); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "Rule 'p' defined in package repl. Type 'show' to see rules.\n") + buffer.Reset() + if err := repl.OneShot(ctx, "p[2]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), "2\n") +} + +func TestOneShotJSON(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + repl.outputFormat = "json" + if err := repl.OneShot(ctx, "data.a[i] = x"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + var expected any + if err := util.UnmarshalJSON([]byte(`{ + "result": [ + { + "expressions": [ + { + "value": true, + "text": "data.a[i] = x", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "i": 0, + "x": { + "b": { + "c": [ + true, + 2, + false + ] + } + } + } + }, + { + "expressions": [ + { + "value": true, + "text": "data.a[i] = x", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "i": 1, + "x": { + "b": { + "c": [ + false, + true, + 1 + ] + } + } + } + } + ] + }`), &expected); err != nil { + panic(err) + } + + var result any + + if err := util.UnmarshalJSON(buffer.Bytes(), &result); err != nil { + t.Errorf("Unexpected output format: %v", err) + return + } + + if !reflect.DeepEqual(expected, result) { + t.Errorf("Expected %v but got: %v", expected, buffer.String()) + } +} + +func TestOneShot_DefaultRegoVersion(t *testing.T) { + type action struct { + line string + expOutput string + expErrs []string + } + + tests := []struct { + note string + actions []action + }{ + { + note: "v1 keywords used", + actions: []action{ + { + line: "a contains 2 if { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v1 keywords not used", + actions: []action{ + { + line: "a[2] { true }", + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + }, + }, + { + note: "v1 keywords imported", + actions: []action{ + { + line: "import future.keywords", + }, + { + line: "a contains 2 if { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v1 compile-time violation", + actions: []action{ + { + line: "b if { data := 1; data == 1 }", + expErrs: []string{ + "rego_compile_error: variables must not shadow data (use a different variable name)", + }, + }, + }, + }, + { + note: "rego.v1 imported", + actions: []action{ + { + line: "import rego.v1", + }, + { + line: "a contains 2 if { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v1 keywords", + actions: []action{ + { + line: "a contains 2 if { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + for _, action := range tc.actions { + err := repl.OneShot(ctx, action.line) + + if len(action.expErrs) != 0 { + if err == nil { + t.Fatalf("Expected error but got: %s", buffer.String()) + } + + for _, e := range action.expErrs { + if !strings.Contains(err.Error(), e) { + t.Fatalf("Expected error to contain:\n\n%q\n\nbut got:\n\n%v", e, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), action.expOutput) + } + } + }) + } +} + +func TestOneShot_RegoVersion(t *testing.T) { + type action struct { + line string + expOutput string + expErrs []string + } + tests := []struct { + note string + actions []action + regoVersion ast.RegoVersion + }{ + { + note: "v0, keywords used", + regoVersion: ast.RegoV0, + actions: []action{ + { + line: "a contains 2 if { true }", + expErrs: []string{"rego_unsafe_var_error: var a is unsafe"}, + }, + }, + }, + { + note: "v0, keywords not used", + regoVersion: ast.RegoV0, + actions: []action{ + { + line: "a[2] { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v0, keywords imported", + regoVersion: ast.RegoV0, + actions: []action{ + { + line: "import future.keywords", + }, + { + line: "a contains 2 if { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v0, rego.v1 imported", + regoVersion: ast.RegoV0, + actions: []action{ + { + line: "import rego.v1", + }, + { + line: "a contains 2 if { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v0, v1 compile-time violation", + regoVersion: ast.RegoV0, + actions: []action{ + { + line: "b { data := 1; data == 1 }", + expOutput: "Rule 'b' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v1, keywords not used", + regoVersion: ast.RegoV1, + actions: []action{ + { + line: "a[2] { true }", + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + }, + }, + { + note: "v1, keywords used, not imported", + regoVersion: ast.RegoV1, + actions: []action{ + { + line: "a contains 2 if { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v1, keywords used, keywords imported", + regoVersion: ast.RegoV1, + actions: []action{ + { + line: "import future.keywords", + }, + { + line: "a contains 2 if { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v1, keywords used, rego.v1 imported", + regoVersion: ast.RegoV1, + actions: []action{ + { + line: "import rego.v1", + }, + { + line: "a contains 2 if { true }", + expOutput: "Rule 'a' defined in package repl. Type 'show' to see rules.\n", + }, + }, + }, + { + note: "v1 compile-time violation", + regoVersion: ast.RegoV1, + actions: []action{ + { + line: "b if { data := 1; data == 1 }", + expErrs: []string{ + "rego_compile_error: variables must not shadow data (use a different variable name)", + }, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer). + WithRegoVersion(tc.regoVersion) + + for _, action := range tc.actions { + err := repl.OneShot(ctx, action.line) + + if len(action.expErrs) != 0 { + if err == nil { + t.Fatalf("Expected error but got: %s", buffer.String()) + } + + for _, e := range action.expErrs { + if !strings.Contains(err.Error(), e) { + t.Fatalf("Expected error to contain:\n\n%q\n\nbut got:\n\n%v", e, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expectOutput(t, buffer.String(), action.expOutput) + } + } + }) + } +} + +func TestStoredModule_RegoVersion(t *testing.T) { + tests := []struct { + note string + regoVersion ast.RegoVersion + module string + line string + expOutput string + expErrs []string + }{ + { + note: "v0 keywords not used", + regoVersion: ast.RegoV0, + module: `package example +p[2] { 1 == 1 }`, + line: "data.example.p", + expOutput: "[\n 2\n]\n", + }, + { + note: "v0, keywords not imported but used", + regoVersion: ast.RegoV0, + module: `package example +p contains 2 if { 1 == 1 }`, + line: "data.example.p", + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0, keywords imported", + regoVersion: ast.RegoV0, + module: `package example +import future.keywords +p contains 2 if { 1 == 1 }`, + line: "data.example.p", + expOutput: "[\n 2\n]\n", + }, + { + note: "v0, rego.v1 imported", + regoVersion: ast.RegoV0, + module: `package example +import rego.v1 +p contains 2 if { 1 == 1 }`, + line: "data.example.p", + expOutput: "[\n 2\n]\n", + }, + { + note: "v0, v1 compile-time violation", + regoVersion: ast.RegoV0, + module: `package example +p { data := 1; data == 1 }`, + line: "data.example.p", + expOutput: "true\n", + }, + { + note: "v1, keywords not used", + regoVersion: ast.RegoV1, + module: `package example +p[2] { 1 == 1 }`, + line: "data.example.p", + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1, keywords not imported", + regoVersion: ast.RegoV1, + module: `package example +p contains 2 if { 1 == 1 }`, + line: "data.example.p", + expOutput: "[\n 2\n]\n", + }, + { + note: "v1, keywords imported", + regoVersion: ast.RegoV1, + module: `package example +import future.keywords +p contains 2 if { 1 == 1 }`, + line: "data.example.p", + expOutput: "[\n 2\n]\n", + }, + { + note: "v1, rego.v1 imported", + regoVersion: ast.RegoV1, + module: `package example +import rego.v1 +p contains 2 if { 1 == 1 }`, + line: "data.example.p", + expOutput: "[\n 2\n]\n", + }, + { + note: "v1, v1 compile-time violation", + regoVersion: ast.RegoV1, + module: `package example +p if { data := 1; data == 1 }`, + line: "data.example.p", + expErrs: []string{ + "rego_compile_error: variables must not shadow data (use a different variable name)", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := newTestStore() + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + if err := store.UpsertPolicy(ctx, txn, "policy", []byte(tc.module)); err != nil { + t.Fatalf("Unexpected error upserting policy: %v", err) + } + + if err := store.Commit(ctx, txn); err != nil { + t.Fatalf("Unexpected error committing store transaction: %v", err) + } + + var buffer bytes.Buffer + repl := newRepl(store, &buffer). + WithRegoVersion(tc.regoVersion) + + err := repl.OneShot(ctx, tc.line) + + if len(tc.expErrs) != 0 { + if err == nil { + t.Fatalf("Expected error but got: %s", buffer.String()) + } + + for _, e := range tc.expErrs { + if !strings.Contains(err.Error(), e) { + t.Fatalf("Expected error to contain:\n\n%q\n\nbut got:\n\n%v", e, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expectOutput(t, buffer.String(), tc.expOutput) + } + }) + } +} + +func TestEvalData(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + testMod := []byte(`package ex +import rego.v1 + +p = [1, 2, 3] if { true }`) + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + if err := store.UpsertPolicy(ctx, txn, "test", testMod); err != nil { + panic(err) + } + + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + + if err := repl.OneShot(ctx, "data"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected := parseJSON(` + { + "a": [ + { + "b": { + "c": [ + true, + 2, + false + ] + } + }, + { + "b": { + "c": [ + false, + true, + 1 + ] + } + } + ], + "ex": { + "p": [ + 1, + 2, + 3 + ] + } + }`) + result := parseJSON(buffer.String()) + + // Strip REPL documents out as these change depending on build settings. + data := result.(map[string]any) + delete(data, "repl") + + if !reflect.DeepEqual(result, expected) { + t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, result) + } +} + +func TestEvalFalse(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "false"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := buffer.String() + if result != "false\n" { + t.Errorf("Expected result to be false but got: %v", result) + } +} + +func TestEvalConstantRule(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "pi = 3.14"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := buffer.String() + if result != "Rule 'pi' defined in package repl. Type 'show' to see rules.\n" { + t.Errorf("Expected rule to be defined but got: %v", result) + return + } + buffer.Reset() + if err := repl.OneShot(ctx, "pi"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result = buffer.String() + expected := "3.14\n" + if result != expected { + t.Errorf("Expected pi to evaluate to 3.14 but got: %v", result) + return + } + buffer.Reset() + err := repl.OneShot(ctx, "pi.deadbeef") + result = buffer.String() + expected = "undefined ref: data.repl.pi.deadbeef" + if err == nil { + t.Fatalf("Expected OneShot to return error %v but got: %v", expected, err) + } + if result != "" || !strings.Contains(err.Error(), expected) { + t.Fatalf("Expected pi.deadbeef to fail/error but got:\nresult: %q\nerr: %v", result, err) + } + buffer.Reset() + if err := repl.OneShot(ctx, "pi > 3"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result = buffer.String() + if result != "true\n" { + t.Errorf("Expected pi > 3 to be true but got: %v", result) + return + } +} + +func TestEvalBooleanFlags(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "flags = [true, true]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "flags[_]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := strings.TrimSpace(` +Rule 'flags' defined in package repl. Type 'show' to see rules. ++----------+ +| flags[_] | ++----------+ +| true | +| true | ++----------+`) + result := strings.TrimSpace(buffer.String()) + if result != expected { + t.Errorf("Expected a single column with boolean output but got:\n%v", result) + } + buffer.Reset() + + if err := repl.OneShot(ctx, `flags2 = [true, "x", 1]`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "flags2[_]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected = strings.TrimSpace(` +Rule 'flags2' defined in package repl. Type 'show' to see rules. ++-----------+ +| flags2[_] | ++-----------+ +| true | +| "x" | +| 1 | ++-----------+`) + result = strings.TrimSpace(buffer.String()) + if result != expected { + t.Errorf("Expected a single column with boolean output but got:\n%v", result) + } +} + +func TestEvalConstantRuleDefaultRootDoc(t *testing.T) { + // The 'input' document may only be shadowed in rego v0. + + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer). + WithRegoVersion(ast.RegoV0) + if err := repl.OneShot(ctx, "input = 1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, "input = 2"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + assertREPLText(t, buffer, "undefined\n") + buffer.Reset() + if err := repl.OneShot(ctx, "input = 1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + assertREPLText(t, buffer, "true\n") +} + +func TestEvalConstantRuleAssignment(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + + defined := "Rule 'x' defined in package repl. Type 'show' to see rules.\n" + redefined := "Rule 'x' re-defined in package repl. Type 'show' to see rules.\n" + + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "x = 1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + assertREPLText(t, buffer, defined) + buffer.Reset() + if err := repl.OneShot(ctx, "x := 2"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + assertREPLText(t, buffer, redefined) + buffer.Reset() + + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + assertREPLText(t, buffer, `package repl + +x := 2 +`) + buffer.Reset() + + if err := repl.OneShot(ctx, "x := 3"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + assertREPLText(t, buffer, redefined) + buffer.Reset() + if err := repl.OneShot(ctx, "x"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := buffer.String() + if result != "3\n" { + t.Fatalf("Expected 3 but got: %v", result) + } + + buffer.Reset() + if err := repl.OneShot(ctx, "x = 3"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result = buffer.String() + if result != "true\n" { + t.Fatalf("Expected true but got: %v", result) + } + + buffer.Reset() + err := repl.OneShot(ctx, "assign()") + if err == nil || !strings.Contains(err.Error(), "rego_type_error: assign: arity mismatch\n\thave: ()\n\twant: (any, any)") { + t.Fatal("Expected type check error but got:", err) + } +} +func TestEvalConstantRuleAssignmentInputDocument(t *testing.T) { + // input is only allowed to be overridden in rego v0, so we only assert the following when that's the active version. + + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer). + WithRegoVersion(ast.RegoV0) + + definedInput := "Rule 'input' defined in package repl. Type 'show' to see rules.\n" + redefinedInput := "Rule 'input' re-defined in package repl. Type 'show' to see rules.\n" + + buffer.Reset() + if err := repl.OneShot(ctx, "input = 0"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + assertREPLText(t, buffer, definedInput) + buffer.Reset() + if err := repl.OneShot(ctx, "input := 1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + assertREPLText(t, buffer, redefinedInput) + buffer.Reset() + if err := repl.OneShot(ctx, "input"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := buffer.String() + if result != "1\n" { + t.Fatalf("Expected 1 but got: %v", result) + } +} + +func TestEvalSingleTermMultiValue(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + repl.outputFormat = "json" + + input := `{ + "result": [ + { + "expressions": [ + { + "value": true, + "text": "data.a[i].b.c[_]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "i": 0 + } + }, + { + "expressions": [ + { + "value": 2, + "text": "data.a[i].b.c[_]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "i": 0 + } + }, + { + "expressions": [ + { + "value": true, + "text": "data.a[i].b.c[_]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "i": 1 + } + }, + { + "expressions": [ + { + "value": 1, + "text": "data.a[i].b.c[_]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "i": 1 + } + } + ] + }` + + var expected any + if err := util.UnmarshalJSON([]byte(input), &expected); err != nil { + panic(err) + } + + if err := repl.OneShot(ctx, "data.a[i].b.c[_]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + var result any + if err := util.UnmarshalJSON(buffer.Bytes(), &result); err != nil { + t.Errorf("Expected valid JSON document: %v: %v", err, buffer.String()) + return + } + + if !reflect.DeepEqual(expected, result) { + t.Errorf("Expected %v but got: %v", expected, buffer.String()) + return + } + + buffer.Reset() + + // We import rego.v1 to ensure we're compatible with both v0 and v1 as default rego-version. + if err := repl.OneShot(ctx, "import rego.v1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, "data.deadbeef[x]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + s := buffer.String() + if s != "{}\n" { + t.Errorf("Expected undefined from reference but got: %v", s) + return + } + + buffer.Reset() + + if err := repl.OneShot(ctx, `p contains x if { a = [1, 2, 3, 4]; a[_] = x }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, "p[x]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + input = ` + { + "result": [ + { + "expressions": [ + { + "value": 1, + "text": "p[x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 1 + } + }, + { + "expressions": [ + { + "value": 2, + "text": "p[x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 2 + } + }, + { + "expressions": [ + { + "value": 3, + "text": "p[x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 3 + } + }, + { + "expressions": [ + { + "value": 4, + "text": "p[x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 4 + } + } + ] + } + ` + + if err := util.UnmarshalJSON([]byte(input), &expected); err != nil { + panic(err) + } + + if err := util.UnmarshalJSON(buffer.Bytes(), &result); err != nil { + t.Errorf("Expected valid JSON document: %v: %v", err, buffer.String()) + return + } + + if !reflect.DeepEqual(expected, result) { + t.Errorf("Exepcted %v but got: %v", expected, buffer.String()) + } +} + +func TestEvalSingleTermMultiValueSetRef(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + repl.outputFormat = "json" + + // We import rego.v1 to ensure we're compatible with both v0 and v1 as default rego-version. + if err := repl.OneShot(ctx, "import rego.v1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, `p contains 1 if { true }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `p contains 2 if { true }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `q = {3, 4} if { true }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `r = [x, y] if { x = {5, 6}; y = [7, 8] }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, "p[x]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := parseJSON(`{ + "result": [ + { + "expressions": [ + { + "value": 1, + "text": "p[x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 1 + } + }, + { + "expressions": [ + { + "value": 2, + "text": "p[x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 2 + } + } + ] + }`) + result := parseJSON(buffer.String()) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("Expected %v but got: %v", expected, buffer.String()) + } + + buffer.Reset() + if err := repl.OneShot(ctx, "q[x]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected = parseJSON(`{ + "result": [ + { + "expressions": [ + { + "value": 3, + "text": "q[x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 3 + } + }, + { + "expressions": [ + { + "value": 4, + "text": "q[x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 4 + } + } + ] + }`) + result = parseJSON(buffer.String()) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("Expected %v but got: %v", expected, buffer.String()) + } + + // Example below shows behavior for ref that iterates an embedded set. The + // tricky part here is that r[_] may refer to multiple collection types. If + // we eventually have a way of distinguishing between the bindings added for + // refs to sets, then those bindings could be filtered out. For now this is + // acceptable, as it should be an edge case. + buffer.Reset() + if err := repl.OneShot(ctx, "r[_][x]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected = parseJSON(`{ + "result": [ + { + "expressions": [ + { + "value": 5, + "text": "r[_][x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 5 + } + }, + { + "expressions": [ + { + "value": 6, + "text": "r[_][x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 6 + } + }, + { + "expressions": [ + { + "value": 7, + "text": "r[_][x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 0 + } + }, + { + "expressions": [ + { + "value": 8, + "text": "r[_][x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 1 + } + } + ] + }`) + result = parseJSON(buffer.String()) + if !reflect.DeepEqual(result, expected) { + t.Fatalf("Expected %v but got: %v", expected, buffer.String()) + } +} + +func TestEvalRuleCompileError(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + // We import rego.v1 to ensure we're compatible with both v0 and v1 as default rego-version. + if err := repl.OneShot(ctx, "import rego.v1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + err := repl.OneShot(ctx, `p contains x if { true }`) + expected := "x is unsafe" + if err == nil { + t.Fatalf("Expected OneShot to return error %v but got: %v", expected, err) + } + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected error to contain %v but got: %v (err: %v)", expected, buffer.String(), err) + return + } + buffer.Reset() + err = repl.OneShot(ctx, `p = true if { true }`) + result := buffer.String() + if err != nil || result != "Rule 'p' defined in package repl. Type 'show' to see rules.\n" { + t.Errorf("Expected valid rule to compile (because state should be unaffected) but got: %v (err: %v)", result, err) + } +} + +func TestEvalBodyCompileError(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + repl.outputFormat = "json" + err := repl.OneShot(ctx, `x = 1; y > x`) + if _, ok := err.(ast.Errors); !ok { + t.Fatalf("Expected error message in output but got`: %v", buffer.String()) + } + buffer.Reset() + if err := repl.OneShot(ctx, `x = 1; y = 2; y > x`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := util.MustUnmarshalJSON(buffer.Bytes()) + exp := util.MustUnmarshalJSON([]byte(`{ + "result": [ + { + "expressions": [ + { + "value": true, + "text": "x = 1", + "location": { + "row": 1, + "col": 1 + } + }, + { + "value": true, + "text": "y = 2", + "location": { + "row": 1, + "col": 8 + } + }, + { + "value": true, + "text": "y \u003e x", + "location": { + "row": 1, + "col": 15 + } + } + ], + "bindings": { + "x": 1, + "y": 2 + } + } + ] + }`)) + if !reflect.DeepEqual(exp, result) { + t.Errorf(`Expected %v but got: %v"`, exp, buffer.String()) + return + } +} + +func TestEvalBodyContainingWildCards(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "data.a[_].b.c[_] = x"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := strings.TrimSpace(` ++-------+ +| x | ++-------+ +| true | +| 2 | +| false | +| false | +| true | +| 1 | ++-------+`) + result := strings.TrimSpace(buffer.String()) + if result != expected { + t.Errorf("Expected only a single column of output but got:\n%v", result) + } + +} + +func TestEvalBodyInput(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer). + WithRegoVersion(ast.RegoV0) + + if err := repl.OneShot(ctx, `package repl`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `input["foo.bar"] = "hello" { true }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `input["baz"] = data.a[0].b.c[2] { true }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `package test`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "import input.baz"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `p = true { input["foo.bar"] = "hello"; baz = false }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, "p"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result := buffer.String() + if result != "true\n" { + t.Fatalf("expected true but got: %v", result) + } +} + +func TestEvalBodyInputComplete(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer). + WithRegoVersion(ast.RegoV0) + + // Test that input can be defined completely: + // https://github.com/open-policy-agent/opa/issues/231 + if err := repl.OneShot(ctx, `package repl`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `input = 1`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, `input`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result := buffer.String() + if result != "1\n" { + t.Fatalf("Expected 1 but got: %v", result) + } + + buffer.Reset() + + // Test that input is as expected + if err := repl.OneShot(ctx, `package ex1`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `x = input`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, `x`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result = buffer.String() + if result != "1\n" { + t.Fatalf("Expected 1 but got: %v", result) + } + + // Test that local input replaces other inputs + if err := repl.OneShot(ctx, `package ex2`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `input = 2`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, `input`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result = buffer.String() + + if result != "2\n" { + t.Fatalf("Expected 2 but got: %v", result) + } + + buffer.Reset() + + // Test that original input is intact + if err := repl.OneShot(ctx, `package ex3`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `input`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result = buffer.String() + + if result != "1\n" { + t.Fatalf("Expected 1 but got: %v", result) + } + + // Test that deferencing undefined input results in undefined + buffer.Reset() + + repl = newRepl(store, &buffer) + if err := repl.OneShot(ctx, `input.p`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result = buffer.String() + if result != "undefined\n" { + t.Fatalf("Expected undefined but got: %v", result) + } + + buffer.Reset() + if err := repl.OneShot(ctx, `input.p = false`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result = buffer.String() + if result != "undefined\n" { + t.Fatalf("Expected undefined but got: %v", result) + } + +} + +func TestEvalBodyWith(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + // We import rego.v1 to ensure we're compatible with both v0 and v1 as default rego-version. + if err := repl.OneShot(ctx, "import rego.v1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, `p = true if { input.foo = "bar" }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, "p"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if buffer.String() != "undefined\n" { + t.Fatalf("Expected undefined but got: %v", buffer.String()) + } + + buffer.Reset() + + if err := repl.OneShot(ctx, `p with input.foo as "bar"`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result := buffer.String() + expected := "true\n" + + if result != expected { + t.Fatalf("Expected true but got: %v", result) + } +} + +func TestEvalBodyRewrittenBuiltin(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "json"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // We import rego.v1 to ensure we're compatible with both v0 and v1 as default rego-version. + if err := repl.OneShot(ctx, "import rego.v1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, `p contains x if { a[x]; a = [1,2,3,4] }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "p[x] > 1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := util.MustUnmarshalJSON(buffer.Bytes()) + expected := util.MustUnmarshalJSON([]byte(`{ + "result": [ + { + "expressions": [ + { + "value": true, + "text": "p[x] \u003e 1", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 2 + } + }, + { + "expressions": [ + { + "value": true, + "text": "p[x] \u003e 1", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 3 + } + } + ] + }`)) + if util.Compare(result, expected) != 0 { + t.Fatalf("Expected %v but got: %v", expected, buffer.String()) + } +} + +func TestEvalBodyRewrittenRef(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "json"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `i = 1`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `data.a[0].b.c[i]`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := util.MustUnmarshalJSON(buffer.Bytes()) + expected := util.MustUnmarshalJSON([]byte(`{ + "result": [ + { + "expressions": [ + { + "value": 2, + "text": "data.a[0].b.c[i]", + "location": { + "row": 1, + "col": 1 + } + } + ] + } + ] + }`)) + if util.Compare(result, expected) != 0 { + t.Fatalf("Expected %v but got: %v", expected, buffer.String()) + } + buffer.Reset() + if err := repl.OneShot(ctx, "p = {1,2,3}"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "p"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result = util.MustUnmarshalJSON(buffer.Bytes()) + expected = util.MustUnmarshalJSON([]byte(`{ + "result": [ + { + "expressions": [ + { + "value": [ + 1, + 2, + 3 + ], + "text": "p", + "location": { + "row": 1, + "col": 1 + } + } + ] + } + ] + }`)) + if util.Compare(result, expected) != 0 { + t.Fatalf("Expected %v but got: %v", expected, buffer.String()) + } + buffer.Reset() + if err := repl.OneShot(ctx, "p[x]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result = util.MustUnmarshalJSON(buffer.Bytes()) + expected = util.MustUnmarshalJSON([]byte(`{ + "result": [ + { + "expressions": [ + { + "value": 1, + "text": "p[x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 1 + } + }, + { + "expressions": [ + { + "value": 2, + "text": "p[x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 2 + } + }, + { + "expressions": [ + { + "value": 3, + "text": "p[x]", + "location": { + "row": 1, + "col": 1 + } + } + ], + "bindings": { + "x": 3 + } + } + ] + }`)) + if util.Compare(result, expected) != 0 { + t.Fatalf("Expected %v but got: %v", expected, buffer.String()) + } +} + +func TestEvalBodySomeDecl(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "json"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "some x; x = 1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + exp := util.MustUnmarshalJSON([]byte(`{ + "result": [ + { + "expressions": [ + { + "value": true, + "text": "x = 1", + "location": { + "row": 1, + "col": 9 + } + } + ], + "bindings": { + "x": 1 + } + } + ] + }`)) + result := util.MustUnmarshalJSON(buffer.Bytes()) + if util.Compare(result, exp) != 0 { + t.Fatalf("Expected %v but got: %v", exp, result) + } +} + +func TestEvalImport(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "import data.a"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if len(buffer.Bytes()) != 0 { + t.Errorf("Expected no output but got: %v", buffer.String()) + return + } + buffer.Reset() + if err := repl.OneShot(ctx, "a[0].b.c[0] = true"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := buffer.String() + expected := "true\n" + if result != expected { + t.Errorf("Expected expression to evaluate successfully but got: %v", result) + return + } + + // https://github.com/open-policy-agent/opa/issues/158 - re-run query to + // make sure import is not lost + buffer.Reset() + if err := repl.OneShot(ctx, "a[0].b.c[0] = true"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result = buffer.String() + expected = "true\n" + if result != expected { + t.Fatalf("Expected expression to evaluate successfully but got: %v", result) + } +} + +func TestEvalImportFutureKeywords(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer). + WithRegoVersion(ast.RegoV0) + + err := repl.OneShot(ctx, "1 in [1]") + if err == nil { + t.Fatal("Expected error got nil") + } + expected := "rego_unsafe_var_error: var in is unsafe (hint: `import future.keywords.in` to import a future keyword)" + if !strings.Contains(err.Error(), expected) { + t.Fatalf("Expected error to contain %q but got: %v", expected, err) + } + buffer.Reset() + + // future keywords import + if err := repl.OneShot(ctx, "import future.keywords"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if len(buffer.Bytes()) != 0 { + t.Errorf("Expected no output but got: %v", buffer.String()) + return + } + buffer.Reset() + if err := repl.OneShot(ctx, "1 in [1,2,3]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := buffer.String() + expected = "true\n" + if result != expected { + t.Errorf("Expected expression to evaluate successfully but got: %v", result) + return + } + buffer.Reset() + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + act := buffer.String() + exp := `package repl + +import future.keywords +` + if act != exp { + t.Errorf("expected %q, got: %q", exp, act) + return + } + + buffer.Reset() + if err := repl.OneShot(ctx, `package foo.bar`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "import future.keywords.in"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if len(buffer.Bytes()) != 0 { + t.Errorf("Expected no output but got: %v", buffer.String()) + return + } + if err := repl.OneShot(ctx, `p = true { 1 in [1,2,3] }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + // ignore "rule p defined" message + buffer.Reset() + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + act = buffer.String() + exp = `package foo.bar + +import future.keywords.in + +p { + 1 in [1, 2, 3] +} +` + if act != exp { + t.Errorf("expected %q, got: %q", exp, act) + return + } + buffer.Reset() + if err := repl.OneShot(ctx, "p"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result = buffer.String() + expected = "true\n" + if result != expected { + t.Errorf("Expected expression to evaluate successfully but got: %v", result) + return + } +} + +func TestEvalPackage(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, `package foo.bar`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // We import rego.v1 to ensure we're compatible with both v0 and v1 as default rego-version. + if err := repl.OneShot(ctx, "import rego.v1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, `p = true if { true }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `package baz.qux`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + err := repl.OneShot(ctx, "p") + expected := "p is unsafe" + if err == nil { + t.Fatalf("Expected OneShot to return error %v but got: %v", expected, err) + } + if !strings.Contains(err.Error(), expected) { + t.Fatalf("Expected unsafe variable error but got: %v", err) + } + if err := repl.OneShot(ctx, "import data.foo.bar.p"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, "p"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if buffer.String() != "true\n" { + t.Errorf("Expected expression to eval successfully but got: %v", buffer.String()) + return + } +} + +func TestMetrics(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "a = {[1,2], [3,4]}"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "metrics"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `[x | a[x]]`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if !strings.Contains(buffer.String(), "timer_rego_query_compile_ns") { + t.Fatal("Expected output to contain well known metric key but got:", buffer.String()) + } + + buffer.Reset() + if err := repl.OneShot(ctx, `[x | a[x]]`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if !strings.Contains(buffer.String(), "timer_rego_query_compile_ns") { + t.Fatal("Expected output to contain well known metric key but got:", buffer.String()) + } + + buffer.Reset() + if err := repl.OneShot(ctx, "metrics"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `[x | a[x]]`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected := `[ + [ + 1, + 2 + ], + [ + 3, + 4 + ] +] +` + + if expected != buffer.String() { + t.Fatalf("Expected output to be exactly:\n%v\n\nGot:\n\n%v\n", expected, buffer.String()) + } +} + +func TestProfile(t *testing.T) { + store := newTestStore() + ctx := context.Background() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + const numLines = 21 + + mod2 := []byte(`package rbac + import rego.v1 + + inp := { + "subject": "bob", + "resource": "foo123", + "action": "write", + } + bindings = [ + { + "user": "alice", + "roles": ["dev", "test"], + }, + { + "user": "bob", + "roles": ["test"], + }, +] + + roles := [ + { + "name": "dev", + "permissions": [ + {"resource": "foo123", "action": "write"}, + {"resource": "foo123", "action": "read"}, + ], + }, + { + "name": "test", + "permissions": [{"resource": "foo123", "action": "read"}], + }, +] + +default allow = false + + allow if { + user_has_role[role_name] + role_has_permission[role_name] + } + + user_has_role contains role_name if { + binding := bindings[_] + binding.user = inp.subject + role_name := binding.roles[_] + } + + role_has_permission contains role_name if { + role := roles[_] + role_name := role.name + perm := role.permissions[_] + perm.resource = inp.resource + perm.action = inp.action + }`) + + if err := store.UpsertPolicy(ctx, txn, "mod2", mod2); err != nil { + panic(err) + } + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "profile"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "data.rbac.allow"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + result := buffer.String() + lines := strings.Split(result, "\n") + if len(lines) != numLines { + t.Fatal("Expected 21 lines, got :", len(lines)) + } + buffer.Reset() +} + +func TestStrictBuiltinErrors(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + + repl := newRepl(store, &buffer) + + if err := repl.OneShot(ctx, "1/0"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result := buffer.String() + + if !strings.Contains(result, "undefined") { + t.Fatal("expected undefined") + } + + buffer.Reset() + + if err := repl.OneShot(ctx, "strict-builtin-errors"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "1/0"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result = buffer.String() + + if !strings.Contains(result, "divide by zero") { + t.Fatal("expected divide by zero error") + } +} + +func TestInstrument(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + + repl := newRepl(store, &buffer) + + // Turn on instrumentation w/o turning on metrics. + if err := repl.OneShot(ctx, "instrument"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "true"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result := buffer.String() + + if !strings.Contains(result, "histogram_eval_op_plug") { + t.Fatal("Expected plug histogram in output but got:", result) + } + + buffer.Reset() + + // Turn off instrumentation. + if err := repl.OneShot(ctx, "instrument"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "true"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result = buffer.String() + + if strings.Contains(result, "histogram_eval_op_plug") { + t.Fatal("Expected instrumentation to be turned off but got:", result) + } + + buffer.Reset() + + // Turn on metrics and then turn on instrumentation. + if err := repl.OneShot(ctx, "metrics"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "true"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result = buffer.String() + + if strings.Contains(result, "histogram_eval_op_plug") { + t.Fatal("Expected instrumentation to be turned off but got:", result) + } + + if !strings.Contains(result, "timer_rego_query_eval_ns") { + t.Fatal("Expected metrics to be turned on but got:", result) + } + + buffer.Reset() + + if err := repl.OneShot(ctx, "instrument"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "true"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + result = buffer.String() + + if !strings.Contains(result, "histogram_eval_op_plug") { + t.Fatal("Expected instrumentation to be turned on but got:", result) + } + + if !strings.Contains(result, "timer_rego_query_eval_ns") { + t.Fatal("Expected metrics to be turned on but got:", result) + } + +} + +func TestEvalTrace(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "trace"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := strings.TrimSpace(` +query:1 Enter data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1 +query:1 | Eval data.a[i].b.c[j] = x +query:1 | Eval data.a[k].b.c[x] = 1 +query:1 | Fail data.a[k].b.c[x] = 1 +query:1 | Redo data.a[i].b.c[j] = x +query:1 | Eval data.a[k].b.c[x] = 1 +query:1 | Exit data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1 +query:1 Redo data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1 +query:1 | Redo data.a[k].b.c[x] = 1 +query:1 | Redo data.a[i].b.c[j] = x +query:1 | Eval data.a[k].b.c[x] = 1 +query:1 | Fail data.a[k].b.c[x] = 1 +query:1 | Redo data.a[i].b.c[j] = x +query:1 | Eval data.a[k].b.c[x] = 1 +query:1 | Fail data.a[k].b.c[x] = 1 +query:1 | Redo data.a[i].b.c[j] = x +query:1 | Eval data.a[k].b.c[x] = 1 +query:1 | Fail data.a[k].b.c[x] = 1 +query:1 | Redo data.a[i].b.c[j] = x +query:1 | Eval data.a[k].b.c[x] = 1 +query:1 | Fail data.a[k].b.c[x] = 1 +query:1 | Redo data.a[i].b.c[j] = x ++---+---+---+---+ +| i | j | k | x | ++---+---+---+---+ +| 0 | 1 | 1 | 2 | ++---+---+---+---+`) + expected += "\n" + + if expected != buffer.String() { + t.Fatalf("Expected output to be exactly:\n%v\n\nGot:\n\n%v\n", expected, buffer.String()) + } +} + +func TestEvalNotes(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + // We import rego.v1 to ensure we're compatible with both v0 and v1 as default rego-version. + if err := repl.OneShot(ctx, "import rego.v1"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := repl.OneShot(ctx, `p if { a = [1,2,3]; a[i] = x; x > 1; trace(sprintf("x = %d", [x])) }`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "notes"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + buffer.Reset() + if err := repl.OneShot(ctx, "p"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + expected := strings.TrimSpace(`query:1 Enter data.repl.p = _ +query:1 | Enter data.repl.p +query:1 | | Note "x = 2" +true`) + expected += "\n" + if expected != buffer.String() { + t.Fatalf("Expected output to be exactly:\n%v\n\nGot:\n\n%v\n", expected, buffer.String()) + } +} + +func TestTruncatePrettyOutput(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + repl.prettyLimit = 1000 // crank up limit to test repl command + if err := repl.OneShot(ctx, "pretty-limit 80"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "data[x]"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + for _, line := range strings.Split(buffer.String(), "\n") { + // | "repl" | {"version": ... | + if len(line) > 96 { + t.Fatalf("Expected len(line) to be < 96 but got:\n\n%v", buffer) + } + } + buffer.Reset() + if err := repl.OneShot(ctx, "pretty-limit"); err == nil || !strings.Contains(err.Error(), "usage: pretty-limit ") { + t.Fatalf("Expected usage error but got: %v", err) + } +} + +func TestUnsetPackage(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + if err := repl.OneShot(ctx, "package a"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `unset-package 5`); err == nil { + t.Fatalf("Expected package-unset error for bad package but got: %v", buffer.String()) + } + + buffer.Reset() + + if err := repl.OneShot(ctx, "package a"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "unset-package b"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if buffer.String() != "warning: no matching package\n" { + t.Fatalf("Expected unset-package warning no matching package but got: %v", buffer.String()) + } + + buffer.Reset() + + if err := repl.OneShot(ctx, `package a`); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, `unset-package b`); err != nil { + t.Fatalf("Expected unset-package to succeed for input: %v", err) + } + + buffer.Reset() + + if err := repl.OneShot(ctx, "package a"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "unset-package a"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := repl.OneShot(ctx, "show"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if buffer.String() != "no rules defined\n" { + t.Fatalf("Expected unset-package to return to default but got: %v", buffer.String()) + } +} + +func TestCapabilities(t *testing.T) { + capabilities := ast.CapabilitiesForThisVersion() + allowedBuiltins := []*ast.Builtin{} + for _, builtin := range capabilities.Builtins { + if builtin.Name != "http.send" { + allowedBuiltins = append(allowedBuiltins, builtin) + } + } + capabilities.Builtins = allowedBuiltins + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer).WithCapabilities(capabilities) + if err := repl.OneShot(ctx, `http.send({"url": "http://example.com", "method": "GET"})`); err != nil { + if !strings.Contains(err.Error(), "undefined function http.send") { + t.Fatalf("Unexpected error: %v", err) + } + } else { + t.Fatalf("Expected error on http.send") + } +} + +func TestTraceArgument(t *testing.T) { + ctx := context.Background() + store := inmem.New() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + if err := repl.OneShot(ctx, "trace debug"); err != nil { + t.Fatal(err) + } + if err := repl.OneShot(ctx, "show debug"); err != nil { + t.Fatal(err) + } + output := buffer.String() + expected := `"explain": "debug"` + if !strings.Contains(output, expected) { + t.Fatalf("Expected output to contain %s but got %s", expected, output) + } +} + +func assertREPLText(t *testing.T, buf bytes.Buffer, expected string) { + t.Helper() + result := buf.String() + if result != expected { + t.Fatalf("Expected:\n%v\n\nString:\n\n%v\nGot:\n%v\n\nString:\n\n%v", []byte(expected), expected, []byte(result), result) + } +} + +func expectOutput(t *testing.T, output string, expected string) { + t.Helper() + if output != expected { + t.Errorf("Repl output: expected %#v but got %#v", expected, output) + } +} + +func newRepl(store storage.Store, buffer *bytes.Buffer) *REPL { + return New(store, "", buffer, "", 0, "") +} + +func newTestStore() storage.Store { + input := ` + { + "a": [ + { + "b": { + "c": [true,2,false] + } + }, + { + "b": { + "c": [false,true,1] + } + } + ] + } + ` + var data map[string]any + err := util.UnmarshalJSON([]byte(input), &data) + if err != nil { + panic(err) + } + return inmem.NewFromObject(data) +} + +func parseJSON(s string) any { + var v any + if err := util.UnmarshalJSON([]byte(s), &v); err != nil { + panic(err) + } + return v +} diff --git a/third_party/opa/v1/repl/repl_wasmtarget_test.go b/third_party/opa/v1/repl/repl_wasmtarget_test.go new file mode 100644 index 000000000000..02c92ddad296 --- /dev/null +++ b/third_party/opa/v1/repl/repl_wasmtarget_test.go @@ -0,0 +1,80 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build opa_wasm +// +build opa_wasm + +package repl + +import ( + "bytes" + "context" + "testing" + + _ "github.com/open-policy-agent/opa/v1/features/wasm" +) + +func TestReplWasmTarget(t *testing.T) { + ctx := context.Background() + store := newTestStore() + var buffer bytes.Buffer + repl := newRepl(store, &buffer) + + err := repl.OneShot(ctx, "target foo bar") + + expected := "code bad arguments: target : expects exactly one argument" + if err == nil || err.Error() != expected { + t.Fatalf("Expected error %s, got %s", expected, err) + } + + err = repl.OneShot(ctx, "target foo") + + expected = "invalid target \"foo\":must be one of {rego,wasm}" + if err == nil || err.Error() != expected { + t.Fatalf("Expected error %s, got %s", expected, err) + } + + buffer.Reset() + err = repl.OneShot(ctx, "target wasm") + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + repl.OneShot(ctx, `p = true if { input.foo = "bar" }`) + buffer.Reset() + repl.OneShot(ctx, "p") + + if buffer.String() != "undefined\n" { + t.Fatalf("Expected undefined but got: %v", buffer.String()) + } + + buffer.Reset() + repl.OneShot(ctx, `p with input as {"foo": "bar"}`) + + result := buffer.String() + expected = "true\n" + + if result != expected { + t.Fatalf("Expected true but got: %v", result) + } + + buffer.Reset() + repl.OneShot(ctx, `p with input.foo as "bar"`) + + result = buffer.String() + + if result != expected { + t.Fatalf("Expected true but got: %v", result) + } + + buffer.Reset() + repl.OneShot(ctx, `trace`) + + result = buffer.String() + + expected = "warning: trace mode \"full\" is not supported with wasm target\n" + if result != expected { + t.Fatalf("Expected true but got: %v", result) + } +} diff --git a/third_party/opa/v1/resolver/interface.go b/third_party/opa/v1/resolver/interface.go new file mode 100644 index 000000000000..1f04d21c01ba --- /dev/null +++ b/third_party/opa/v1/resolver/interface.go @@ -0,0 +1,29 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package resolver + +import ( + "context" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" +) + +// Resolver defines an external value resolver for OPA evaluations. +type Resolver interface { + Eval(context.Context, Input) (Result, error) +} + +// Input as provided to a Resolver instance when evaluating. +type Input struct { + Ref ast.Ref + Input *ast.Term + Metrics metrics.Metrics +} + +// Result of resolving a ref. +type Result struct { + Value ast.Value +} diff --git a/third_party/opa/v1/resolver/wasm/wasm.go b/third_party/opa/v1/resolver/wasm/wasm.go new file mode 100644 index 000000000000..884e4ca7cc3d --- /dev/null +++ b/third_party/opa/v1/resolver/wasm/wasm.go @@ -0,0 +1,174 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package wasm + +import ( + "context" + "errors" + "fmt" + "strconv" + + "github.com/open-policy-agent/opa/internal/rego/opa" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/resolver" +) + +// New creates a new Resolver instance which is using the Wasm module +// policy for the given entrypoint ref. +func New(entrypoints []ast.Ref, policy []byte, data any) (*Resolver, error) { + e, err := opa.LookupEngine("wasm") + if err != nil { + return nil, err + } + o, err := e.New(). + WithPolicyBytes(policy). + WithDataJSON(data). + Init() + if err != nil { + return nil, err + } + + // Construct a quick lookup table of ref -> entrypoint ID + // for handling evaluations. Only the entrypoints provided + // by the caller will be constructed, this may be a subset + // of entrypoints available in the Wasm module, however + // only the configured ones will be used when Eval() is + // called. + entrypointRefToID := ast.NewValueMap() + epIDs, err := o.Entrypoints(context.Background()) + if err != nil { + return nil, err + } + for path, id := range epIDs { + for _, ref := range entrypoints { + refPtr, err := ref.Ptr() + if err != nil { + return nil, err + } + if refPtr == path { + entrypointRefToID.Put(ref, ast.Number(strconv.Itoa(int(id)))) + } + } + } + + return &Resolver{ + entrypoints: entrypoints, + entrypointIDs: entrypointRefToID, + o: o, + }, nil +} + +// Resolver implements the resolver.Resolver interface +// using Wasm modules to perform an evaluation. +type Resolver struct { + entrypoints []ast.Ref + entrypointIDs *ast.ValueMap + o opa.EvalEngine +} + +// Entrypoints returns a list of entrypoints this resolver is configured to +// perform evaluations on. +func (r *Resolver) Entrypoints() []ast.Ref { + return r.entrypoints +} + +// Close shuts down the resolver. +func (r *Resolver) Close() { + r.o.Close() +} + +// Eval performs an evaluation using the provided input and the Wasm module +// associated with this Resolver instance. +func (r *Resolver) Eval(ctx context.Context, input resolver.Input) (resolver.Result, error) { + v := r.entrypointIDs.Get(input.Ref) + if v == nil { + return resolver.Result{}, fmt.Errorf("unknown entrypoint %s", input.Ref) + } + + numValue, ok := v.(ast.Number) + if !ok { + return resolver.Result{}, fmt.Errorf("internal error: invalid entrypoint id %s", numValue) + } + + epID, ok := numValue.Int() + if !ok { + return resolver.Result{}, fmt.Errorf("internal error: invalid entrypoint id %s", numValue) + } + + var in *any + if input.Input != nil { + var str any = []byte(input.Input.String()) + in = &str + } + + opts := opa.EvalOpts{ + Input: in, + Entrypoint: int32(epID), + Metrics: input.Metrics, + } + out, err := r.o.Eval(ctx, opts) + if err != nil { + return resolver.Result{}, err + } + + result, err := getResult(out) + if err != nil { + return resolver.Result{}, err + } + + return resolver.Result{Value: result}, nil +} + +// SetData will update the external data for the Wasm instance. +func (r *Resolver) SetData(ctx context.Context, data any) error { + return r.o.SetData(ctx, data) +} + +// SetDataPath will set the provided data on the wasm instance at the specified path. +func (r *Resolver) SetDataPath(ctx context.Context, path []string, data any) error { + return r.o.SetDataPath(ctx, path, data) +} + +// RemoveDataPath will remove any data at the specified path. +func (r *Resolver) RemoveDataPath(ctx context.Context, path []string) error { + return r.o.RemoveDataPath(ctx, path) +} + +func getResult(evalResult *opa.Result) (ast.Value, error) { + + parsed, err := ast.ParseTerm(string(evalResult.Result)) + if err != nil { + return nil, fmt.Errorf("failed to parse wasm result: %s", err) + } + + resultSet, ok := parsed.Value.(ast.Set) + if !ok { + return nil, errors.New("illegal result type") + } + + if resultSet.Len() == 0 { + return nil, nil + } + + if resultSet.Len() > 1 { + return nil, errors.New("illegal result type") + } + + var obj ast.Object + err = resultSet.Iter(func(term *ast.Term) error { + obj, ok = term.Value.(ast.Object) + if !ok || obj.Len() != 1 { + return errors.New("illegal result type") + } + return nil + }) + if err != nil { + return nil, err + } + + result := obj.Get(ast.InternedTerm("result")) + + return result.Value, nil +} diff --git a/third_party/opa/v1/runtime/check_user_linux.go b/third_party/opa/v1/runtime/check_user_linux.go new file mode 100644 index 000000000000..f3ed8b32398d --- /dev/null +++ b/third_party/opa/v1/runtime/check_user_linux.go @@ -0,0 +1,23 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package runtime + +import ( + "os/user" + + "github.com/open-policy-agent/opa/v1/logging" +) + +// checkUserPrivileges on Linux could be running in Docker, so we check if +// we're running in the official container image. +func checkUserPrivileges(logger logging.Logger) { + usr, err := user.Current() + if err != nil { + logger.Debug("Failed to determine uid/gid of process owner") + } else if usr.Uid == "0" || usr.Gid == "0" { + message := "OPA running with uid or gid 0. Running OPA with root privileges is not recommended." + logger.Warn(message) + } +} diff --git a/third_party/opa/v1/runtime/check_user_unix.go b/third_party/opa/v1/runtime/check_user_unix.go new file mode 100644 index 000000000000..7b398c195f17 --- /dev/null +++ b/third_party/opa/v1/runtime/check_user_unix.go @@ -0,0 +1,25 @@ +//go:build !linux && !windows +// +build !linux,!windows + +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package runtime + +import ( + "os/user" + + "github.com/open-policy-agent/opa/v1/logging" +) + +// checkUserPrivileges could not be running in Docker, so we only warn +// if run as uid/gid 0. +func checkUserPrivileges(logger logging.Logger) { + usr, err := user.Current() + if err != nil { + logger.Debug("Failed to determine uid/gid of process owner") + } else if usr.Uid == "0" || usr.Gid == "0" { + logger.Warn("OPA running with uid or gid 0. Running OPA with root privileges is not recommended.") + } +} diff --git a/third_party/opa/v1/runtime/check_user_windows.go b/third_party/opa/v1/runtime/check_user_windows.go new file mode 100644 index 000000000000..012b5f7e79b5 --- /dev/null +++ b/third_party/opa/v1/runtime/check_user_windows.go @@ -0,0 +1,14 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package runtime + +import ( + "github.com/open-policy-agent/opa/v1/logging" +) + +// checkUserPrivileges is a no-op in Windows to avoid lookups with +// Active Directory and the like, when we don't care for the output +// anyways. +func checkUserPrivileges(logging.Logger) {} diff --git a/third_party/opa/v1/runtime/doc.go b/third_party/opa/v1/runtime/doc.go new file mode 100644 index 000000000000..4e047af6a45d --- /dev/null +++ b/third_party/opa/v1/runtime/doc.go @@ -0,0 +1,6 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package runtime contains the entry point to the policy engine. +package runtime diff --git a/third_party/opa/v1/runtime/logging.go b/third_party/opa/v1/runtime/logging.go new file mode 100644 index 000000000000..9158b950a937 --- /dev/null +++ b/third_party/opa/v1/runtime/logging.go @@ -0,0 +1,274 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package runtime + +import ( + "bytes" + "compress/gzip" + "io" + "net/http" + "strings" + "sync/atomic" + "time" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/topdown/print" +) + +type loggingPrintHook struct { + logger logging.Logger +} + +func (h loggingPrintHook) Print(pctx print.Context, msg string) error { + // NOTE(tsandall): if the request context is not present then do not panic, + // just log the print message without the additional context. + var fields map[string]any + rctx, ok := logging.FromContext(pctx.Context) + if ok { + fields = rctx.Fields() + } else { + fields = make(map[string]any, 1) + } + fields["line"] = pctx.Location.String() + h.logger.WithFields(fields).Info(msg) + return nil +} + +// LoggingHandler returns an http.Handler that will print log messages +// containing the request information as well as response status and latency. +type LoggingHandler struct { + logger logging.Logger + inner http.Handler + requestID uint64 +} + +// NewLoggingHandler returns a new http.Handler. +func NewLoggingHandler(logger logging.Logger, inner http.Handler) http.Handler { + return &LoggingHandler{ + logger: logger, + inner: inner, + requestID: uint64(0), + } +} + +func (h *LoggingHandler) loggingEnabled(level logging.Level) bool { + return level <= h.logger.GetLevel() +} + +func (h *LoggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + cloneHeaders := r.Header.Clone() + + // set the HTTP headers via a dedicated key on the parent context irrespective of logging level + r = r.WithContext(logging.WithHTTPRequestContext(r.Context(), &logging.HTTPRequestContext{Header: cloneHeaders})) + + var rctx logging.RequestContext + rctx.ReqID = atomic.AddUint64(&h.requestID, uint64(1)) + + recorder := newRecorder(h.logger, w, r, rctx.ReqID, h.loggingEnabled(logging.Debug)) + t0 := time.Now() + + if h.loggingEnabled(logging.Info) { + + rctx.ClientAddr = r.RemoteAddr + rctx.ReqMethod = r.Method + rctx.ReqPath = r.URL.EscapedPath() + r = r.WithContext(logging.NewContext(r.Context(), &rctx)) + + var err error + fields := rctx.Fields() + + if h.loggingEnabled(logging.Debug) { + var bs []byte + if r.Body != nil { + bs, r.Body, err = readBody(r.Body) + } + if err == nil { + if gzipReceived(r.Header) { + // the request is compressed + var gzReader *gzip.Reader + var plainOutput []byte + reader := bytes.NewReader(bs) + gzReader, err = gzip.NewReader(reader) + if err == nil { + plainOutput, err = io.ReadAll(gzReader) + if err == nil { + defer gzReader.Close() + fields["req_body"] = string(plainOutput) + } + } + } else { + fields["req_body"] = string(bs) + } + } + + // err can be thrown on different statements + if err != nil { + fields["err"] = err + } + + fields["req_params"] = r.URL.Query() + } + + if err == nil { + h.logger.WithFields(fields).Info("Received request.") + } else { + h.logger.WithFields(fields).Error("Failed to read body.") + } + } + + h.inner.ServeHTTP(recorder, r) + + dt := time.Since(t0) + statusCode := 200 + if recorder.statusCode != 0 { + statusCode = recorder.statusCode + } + + if h.loggingEnabled(logging.Info) { + fields := map[string]any{ + "client_addr": rctx.ClientAddr, + "req_id": rctx.ReqID, + "req_method": rctx.ReqMethod, + "req_path": rctx.ReqPath, + "resp_status": statusCode, + "resp_bytes": recorder.bytesWritten, + "resp_duration": float64(dt.Nanoseconds()) / 1e6, + } + + if h.loggingEnabled(logging.Debug) { + switch { + case isPprofEndpoint(r): + // pprof always sends binary data (protobuf) + fields["resp_body"] = "[binary payload]" + + case gzipAccepted(r.Header) && isMetricsEndpoint(r): + // metrics endpoint does so when the client accepts it (e.g. prometheus) + fields["resp_body"] = "[compressed payload]" + + case gzipAccepted(r.Header) && gzipReceived(w.Header()) && (isDataEndpoint(r) || isCompileEndpoint(r)): + // data and compile endpoints might compress the response + gzReader, gzErr := gzip.NewReader(recorder.buf) + if gzErr == nil { + plainOutput, readErr := io.ReadAll(gzReader) + if readErr == nil { + defer gzReader.Close() + fields["resp_body"] = string(plainOutput) + } else { + h.logger.Error("Failed to decompressed the payload: %v", readErr.Error()) + } + } else { + h.logger.Error("Failed to read the compressed payload: %v", gzErr.Error()) + } + + default: + fields["resp_body"] = recorder.buf.String() + } + } + + h.logger.WithFields(fields).Info("Sent response.") + } +} + +func gzipAccepted(header http.Header) bool { + a := header.Get("Accept-Encoding") + parts := strings.Split(a, ",") + for _, part := range parts { + part = strings.TrimSpace(part) + if part == "gzip" || strings.HasPrefix(part, "gzip;") { + return true + } + } + return false +} + +func gzipReceived(header http.Header) bool { + a := header.Get("Content-Encoding") + parts := strings.Split(a, ",") + for _, part := range parts { + part = strings.TrimSpace(part) + if part == "gzip" || strings.HasPrefix(part, "gzip;") { + return true + } + } + return false +} + +func isPprofEndpoint(req *http.Request) bool { + return strings.HasPrefix(req.URL.Path, "/debug/pprof/") +} + +func isMetricsEndpoint(req *http.Request) bool { + return strings.HasPrefix(req.URL.Path, "/metrics") +} + +func isDataEndpoint(req *http.Request) bool { + return strings.HasPrefix(req.URL.Path, "/v1/data") || strings.HasPrefix(req.URL.Path, "/v0/data") +} + +func isCompileEndpoint(req *http.Request) bool { + return strings.HasPrefix(req.URL.Path, "/v1/compile") +} + +type recorder struct { + logger logging.Logger + inner http.ResponseWriter + req *http.Request + id uint64 + + buf *bytes.Buffer + bytesWritten int + statusCode int +} + +func newRecorder(logger logging.Logger, w http.ResponseWriter, r *http.Request, id uint64, buffer bool) *recorder { + var buf *bytes.Buffer + if buffer { + buf = new(bytes.Buffer) + } + return &recorder{ + logger: logger, + buf: buf, + inner: w, + req: r, + id: id, + } +} + +func (r *recorder) Header() http.Header { + return r.inner.Header() +} + +func (r *recorder) Write(bs []byte) (int, error) { + r.bytesWritten += len(bs) + if r.buf != nil { + r.buf.Write(bs) + } + return r.inner.Write(bs) +} + +func (r *recorder) WriteHeader(s int) { + r.statusCode = s + r.inner.WriteHeader(s) +} + +var _ http.Flusher = (*recorder)(nil) + +func (r *recorder) Flush() { + if f, ok := r.inner.(http.Flusher); ok { + f.Flush() + } +} + +func readBody(r io.ReadCloser) ([]byte, io.ReadCloser, error) { + if r == http.NoBody { + return nil, r, nil + } + var buf bytes.Buffer + if _, err := buf.ReadFrom(r); err != nil { + return nil, r, err + } + return buf.Bytes(), io.NopCloser(bytes.NewReader(buf.Bytes())), nil +} diff --git a/third_party/opa/v1/runtime/logging_test.go b/third_party/opa/v1/runtime/logging_test.go new file mode 100644 index 000000000000..fc5555dd6bef --- /dev/null +++ b/third_party/opa/v1/runtime/logging_test.go @@ -0,0 +1,375 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: goconst // string duplication is for test readability. +package runtime + +import ( + "bytes" + "compress/gzip" + "context" + "io" + "log" + "net/http" + "net/http/httptest" + "net/url" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/logging/test" + "github.com/open-policy-agent/opa/v1/server" +) + +func TestValidateGzipHeader(t *testing.T) { + + httpHeader := http.Header{} + httpHeader.Add("Accept", "*/*") + if result, expected := gzipAccepted(httpHeader), false; result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } + + httpHeader.Add("Accept-Encoding", "gzip") + if result, expected := gzipAccepted(httpHeader), true; result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } + + httpHeader.Set("Accept-Encoding", "gzip, deflate, br") + if result, expected := gzipAccepted(httpHeader), true; result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } + + httpHeader.Set("Accept-Encoding", "br;q=1.0, gzip;q=0.8, *;q=0.1") + if result, expected := gzipAccepted(httpHeader), true; result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } +} + +func TestValidateReceivedGzipHeader(t *testing.T) { + + httpHeader := http.Header{} + httpHeader.Set("Content-Encoding", "*/*") + if result, expected := gzipReceived(httpHeader), false; result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } + + httpHeader.Set("Content-Encoding", "gzip") + if result, expected := gzipReceived(httpHeader), true; result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } + + httpHeader.Set("Content-Encoding", "gzip, deflate, br") + if result, expected := gzipReceived(httpHeader), true; result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } + + httpHeader.Set("Content-Encoding", "br;q=1.0, gzip;q=0.8, *;q=0.1") + if result, expected := gzipReceived(httpHeader), true; result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } +} + +func TestValidatePprofUrl(t *testing.T) { + + req := http.Request{} + + req.URL = &url.URL{Path: "/metrics"} + if result, expected := isPprofEndpoint(&req), false; result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } + + req.URL = &url.URL{Path: "/debug/pprof/"} + if result, expected := isPprofEndpoint(&req), true; result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } +} + +func TestValidateMetricsUrl(t *testing.T) { + + req := http.Request{} + + req.URL = &url.URL{Path: "/metrics"} + if result, expected := isMetricsEndpoint(&req), true; result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } + + req.URL = &url.URL{Path: "/debug/pprof/"} + if result, expected := isMetricsEndpoint(&req), false; result != expected { + t.Errorf("Expected %v but got: %v", expected, result) + } +} + +func TestRequestErrorLoggingWithHTTPRequestContext(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + logger := test.New() + logger.SetLevel(logging.Error) + + params := NewParams() + params.Addrs = &[]string{"localhost:0"} + params.Logger = logger + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatal(err) + } + + initChannel := rt.Manager.ServerInitializedChannel() + go func() { + if err := rt.Serve(ctx); err != nil { + t.Error(err) + } + }() + <-initChannel + + rec := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/v1/data", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("foo", "bar") + req.Header.Set("foo2", "bar2") + req.Header.Add("foo2", "bar3") + + decisions := []*server.Info{} + + rt.server.WithDecisionLoggerWithErr(func(_ context.Context, info *server.Info) error { + decisions = append(decisions, info) + return nil + }) + + rt.server.Handler.ServeHTTP(rec, req) + if exp, act := http.StatusOK, rec.Result().StatusCode; exp != act { + t.Errorf("%s %s: expected HTTP %d, got %d", "GET", "/v1/data", exp, act) + } + + if len(decisions) != 1 { + t.Fatalf("Expected exactly one decision but got: %d", len(decisions)) + } + + expHeaders := http.Header{} + expHeaders.Set("foo", "bar") + expHeaders.Add("foo2", "bar2") + expHeaders.Add("foo2", "bar3") + + exp := logging.HTTPRequestContext{Header: expHeaders} + + if !reflect.DeepEqual(decisions[0].HTTPRequestContext, exp) { + t.Fatalf("Expected HTTP request context %v but got: %v", exp, decisions[0].HTTPRequestContext) + } +} + +func TestRequestLogging(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + logger := test.New() + logger.SetLevel(logging.Debug) + + // set the threshold to a low value so the server compresses the response when asked to + gzipMinLength := "server.encoding.gzip.min_length=5" + shutdownSeconds := 1 + params := NewParams() + params.Addrs = &[]string{"localhost:0"} + params.Logger = logger + params.PprofEnabled = true + params.GracefulShutdownPeriod = shutdownSeconds // arbitrary, must be non-zero + params.ConfigOverrides = []string{gzipMinLength} + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatal(err) + } + + initChannel := rt.Manager.ServerInitializedChannel() + go func() { + if err := rt.Serve(ctx); err != nil { + t.Error(err) + } + }() + <-initChannel + + // prepare the request bodies to be used + var dataEndpointBody = []byte(`{"input": {"data": "checkForMe"}}`) + var compileEndpointBody = []byte(`{"unknowns": ["input"], "query": "data.checkForMe = true"}`) + var dataEndpointCompressedBody = zipString(`{"input": {"data": "checkForMe"}}`) + var compileEndpointCompressedBody = zipString(`{"unknowns": ["input"], "query": "data.checkForMe = true"}`) + + tests := []struct { + path string + acceptEncoding string + expected string + expectedEncoding string + contentEncoding string + requestBody *[]byte + }{ + { + path: "/metrics", + acceptEncoding: "gzip", + expected: "[compressed payload]", + expectedEncoding: "gzip", + contentEncoding: "", + requestBody: nil, + }, + { + path: "/metrics", + acceptEncoding: "*/*", + expected: "HELP go_gc_duration_seconds A summary of the wall-time pause (stop-the-world) duration in garbage collection cycles.", + expectedEncoding: "", + contentEncoding: "", + requestBody: nil, + }, + { // the data handler on GET will compress the response if response is above server.encoding.gzip.min_length in size + path: "/v1/data", + acceptEncoding: "gzip", + expected: "{\"result\":{}}", + expectedEncoding: "gzip", + contentEncoding: "", + requestBody: nil, + }, + { // the data handler on POST can compress the response if response is above server.encoding.gzip.min_length in size + path: "/v1/data", + acceptEncoding: "gzip", + expected: "{\"result\":{}}", + expectedEncoding: "gzip", + contentEncoding: "", + requestBody: &dataEndpointBody, + }, + { // the data handler on POST can consume compressed request + path: "/v1/data", + acceptEncoding: "gzip", + expected: "{\"result\":{}}", + expectedEncoding: "gzip", + contentEncoding: "gzip", + requestBody: &dataEndpointCompressedBody, + }, + { // the compile handler will compress the response if response is above server.encoding.gzip.min_length in size + path: "/v1/compile", + acceptEncoding: "gzip", + expected: "{\"result\":{}}", + expectedEncoding: "gzip", + contentEncoding: "", + requestBody: &compileEndpointBody, + }, + { // the compile handler can consume compressed request + path: "/v1/compile", + acceptEncoding: "gzip", + expected: "{\"result\":{}}", + expectedEncoding: "gzip", + contentEncoding: "gzip", + requestBody: &compileEndpointCompressedBody, + }, + { // the handlers return plain data + path: "/v1/data", + acceptEncoding: "*/*", + expected: "{\"result\":{}}", + expectedEncoding: "", + contentEncoding: "", + requestBody: nil, + }, + { // accept-encoding does not matter for pprof: it's always protobuf + path: "/debug/pprof/cmdline", + acceptEncoding: "*/*", + expected: "[binary payload]", + expectedEncoding: "", + contentEncoding: "", + requestBody: nil, + }, + } + + // execute all the requests + for _, tc := range tests { + rec := httptest.NewRecorder() + method := "GET" + var body io.Reader + if tc.requestBody != nil { + method = "POST" + body = bytes.NewReader(*tc.requestBody) + } + req, err := http.NewRequest(method, tc.path, body) + + if err != nil { + t.Fatal(err) + } + req.Header.Set("Accept-Encoding", tc.acceptEncoding) + if tc.contentEncoding != "" { + req.Header.Set("Content-Encoding", tc.contentEncoding) + } + rt.server.Handler.ServeHTTP(rec, req) + if exp, act := http.StatusOK, rec.Result().StatusCode; exp != act { + t.Errorf("%s %s: expected HTTP %d, got %d", method, tc.path, exp, act) + } + contentEncoding := rec.Result().Header.Get("Content-Encoding") + if contentEncoding != tc.expectedEncoding { + t.Errorf("%s %s: expected content encoding %s, got %s", method, tc.path, tc.expectedEncoding, contentEncoding) + } + } + + cancel() + + // check the logs + ents := logger.Entries() + for j, tc := range tests { + i := uint64(j + 1) + foundResponse := false + foundRequest := false + for _, ent := range entriesForReq(ents, i) { + if ent.Message == "Sent response." { + act := ent.Fields["resp_body"].(string) + if !strings.Contains(act, tc.expected) { + t.Errorf("expected %q in resp_body field, got %q", tc.expected, act) + } + foundResponse = true + } + if tc.requestBody != nil && ent.Message == "Received request." { + if tc.requestBody != nil { + // the req_body is always uncompressed + act := ent.Fields["req_body"].(string) + if !strings.Contains(act, "checkForMe") { + t.Errorf("expected string %q in req_body field, got %q", "checkForMe", act) + } + foundRequest = true + } + } + } + if !foundResponse { + t.Errorf("Expected \"Sent response.\" log for request %d (path %s)", j, tc.path) + } + if tc.requestBody != nil && !foundRequest { + t.Errorf("Expected \"Received request.\" log for request %d (path %s)", j, tc.path) + } + + } + if t.Failed() { + t.Logf("logs: %v", ents) + } +} + +func entriesForReq(ents []test.LogEntry, n uint64) []test.LogEntry { + var ret []test.LogEntry + for _, e := range ents { + if r, ok := e.Fields["req_id"]; ok { + if i, ok := r.(uint64); ok { + if i == n { + ret = append(ret, e) + } + } + } + } + return ret +} +func zipString(input string) []byte { + var b bytes.Buffer + gz := gzip.NewWriter(&b) + if _, err := gz.Write([]byte(input)); err != nil { + log.Fatal(err) + } + if err := gz.Close(); err != nil { + log.Fatal(err) + } + return b.Bytes() +} diff --git a/third_party/opa/v1/runtime/plugins_test.go b/third_party/opa/v1/runtime/plugins_test.go new file mode 100644 index 000000000000..9626e56f49c0 --- /dev/null +++ b/third_party/opa/v1/runtime/plugins_test.go @@ -0,0 +1,192 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package runtime + +import ( + "context" + "errors" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +type Tester struct { + startErr error +} + +func (t *Tester) Start(_ context.Context) error { + return t.startErr +} + +func (*Tester) Stop(_ context.Context) {} + +func (*Tester) Reconfigure(_ context.Context, _ any) {} + +type Config struct { + ConfigErr bool `json:"configerr"` +} + +type Factory struct{} + +func (Factory) Validate(_ *plugins.Manager, config []byte) (any, error) { + + cfg := Config{} + + if err := util.Unmarshal(config, &cfg); err != nil { + return nil, err + } + + if cfg.ConfigErr { + return nil, errors.New("test error") + } + + return cfg, nil +} + +func (Factory) New(_ *plugins.Manager, _ any) plugins.Plugin { + return &Tester{} +} + +func TestRegisterPlugin(t *testing.T) { + + params := NewParams() + + fs := map[string]string{ + "/config.yaml": `{"plugins": {"test": {}}}`, + } + + test.WithTempFS(fs, func(testDirRoot string) { + + RegisterPlugin("test", Factory{}) + + params.ConfigFile = filepath.Join(testDirRoot, "config.yaml") + + rt, err := NewRuntime(context.Background(), params) + if err != nil { + t.Fatal(err.Error()) + } + + if err := rt.Manager.Start(context.Background()); err != nil { + t.Fatalf("Unable to initialize plugins: %v", err.Error()) + } + + p := rt.Manager.Plugin("test") + if p == nil { + t.Fatal("expected plugin to be registered") + } + + }) + +} + +func TestRegisterPluginNotStartedWithoutConfig(t *testing.T) { + + params := NewParams() + + fs := map[string]string{ + "/config.yaml": `{"plugins": {}}`, + } + + test.WithTempFS(fs, func(testDirRoot string) { + + RegisterPlugin("test", Factory{}) + + params.ConfigFile = filepath.Join(testDirRoot, "config.yaml") + + rt, err := NewRuntime(context.Background(), params) + if err != nil { + t.Fatal(err.Error()) + } + + if err := rt.Manager.Start(context.Background()); err != nil { + t.Fatalf("Unable to initialize plugins: %v", err.Error()) + } + + p := rt.Manager.Plugin("test") + if p != nil { + t.Fatal("expected plugin to be missing") + } + + }) + +} + +func TestRegisterPluginBadBootConfig(t *testing.T) { + + params := NewParams() + + fs := map[string]string{ + "/config.yaml": `{"plugins": {"test": {"configerr": true}}}`, + } + + test.WithTempFS(fs, func(testDirRoot string) { + + RegisterPlugin("test", Factory{}) + + params.ConfigFile = filepath.Join(testDirRoot, "config.yaml") + + _, err := NewRuntime(context.Background(), params) + if err == nil || !strings.Contains(err.Error(), "config error: test") { + t.Fatal("expected config error but got:", err) + } + + }) + +} + +func TestWaitPluginsReady(t *testing.T) { + fs := map[string]string{ + "/config.yaml": `{"plugins": {"test": {}}}`, + } + + test.WithTempFS(fs, func(testDirRoot string) { + + RegisterPlugin("test", Factory{}) + + params := NewParams() + params.ConfigFile = filepath.Join(testDirRoot, "config.yaml") + + rt, err := NewRuntime(context.Background(), params) + if err != nil { + t.Fatal(err.Error()) + } + + if err := rt.Manager.Start(context.Background()); err != nil { + t.Fatalf("Unable to initialize plugins: %v", err.Error()) + } + + rt.Manager.UpdatePluginStatus("test", &plugins.Status{ + State: plugins.StateNotReady, + }) + + go func() { + time.Sleep(100 * time.Millisecond) + + rt.Manager.UpdatePluginStatus("test", &plugins.Status{ + State: plugins.StateOK, + }) + rt.Manager.UpdatePluginStatus("discovery", &plugins.Status{ + State: plugins.StateOK, + }) + }() + + if err := rt.waitPluginsReady(1*time.Millisecond, 0); err != nil { + t.Fatalf("Expected no error when no timeout: %v", err) + } + + if err := rt.waitPluginsReady(1*time.Millisecond, 1*time.Millisecond); err == nil { + t.Fatal("Expected timeout error") + } + + if err := rt.waitPluginsReady(1*time.Millisecond, time.Second); err != nil { + t.Fatal(err) + } + }) +} diff --git a/third_party/opa/v1/runtime/runtime.go b/third_party/opa/v1/runtime/runtime.go new file mode 100644 index 000000000000..493c24d0eb05 --- /dev/null +++ b/third_party/opa/v1/runtime/runtime.go @@ -0,0 +1,1078 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package runtime + +import ( + "bytes" + "context" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io" + mr "math/rand" + "net/http" + "net/url" + "os" + "os/signal" + "strings" + "sync" + "syscall" + "time" + + "github.com/fsnotify/fsnotify" + prometheus_sdk "github.com/prometheus/client_golang/prometheus" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/propagation" + "go.uber.org/automaxprocs/maxprocs" + + "github.com/open-policy-agent/opa/internal/compiler" + "github.com/open-policy-agent/opa/internal/config" + internal_tracing "github.com/open-policy-agent/opa/internal/distributedtracing" + internal_logging "github.com/open-policy-agent/opa/internal/logging" + "github.com/open-policy-agent/opa/internal/pathwatcher" + "github.com/open-policy-agent/opa/internal/prometheus" + "github.com/open-policy-agent/opa/internal/ref" + "github.com/open-policy-agent/opa/internal/report" + "github.com/open-policy-agent/opa/internal/runtime" + initload "github.com/open-policy-agent/opa/internal/runtime/init" + "github.com/open-policy-agent/opa/internal/uuid" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + opa_config "github.com/open-policy-agent/opa/v1/config" + "github.com/open-policy-agent/opa/v1/hooks" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/plugins/discovery" + "github.com/open-policy-agent/opa/v1/plugins/logs" + metrics_config "github.com/open-policy-agent/opa/v1/plugins/server/metrics" + "github.com/open-policy-agent/opa/v1/repl" + "github.com/open-policy-agent/opa/v1/server" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/disk" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/tracing" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/version" +) + +var ( + registeredPlugins map[string]plugins.Factory + registeredPluginsMux sync.Mutex +) + +const ( + // default interval between OPA version report uploads after startup (1h) + defaultInitialUploadInterval = time.Hour + // upload interval when OPA has been running for 6+ hrs (6h) + defaultLaterUploadInterval = 6 * time.Hour +) + +// RegisterPlugin registers a plugin factory with the runtime +// package. When the runtime is created, the factories are used to parse +// plugin configuration and instantiate plugins. If no configuration is +// provided, plugins are not instantiated. This function is idempotent. +func RegisterPlugin(name string, factory plugins.Factory) { + registeredPluginsMux.Lock() + defer registeredPluginsMux.Unlock() + registeredPlugins[name] = factory +} + +// Params stores the configuration for an OPA instance. +type Params struct { + // Globally unique identifier for this OPA instance. If an ID is not specified, + // the runtime will generate one. + ID string + + // Addrs are the listening addresses that the OPA server will bind to. + Addrs *[]string + + // DiagnosticAddrs are the listening addresses that the OPA server will bind to + // for read-only diagnostic API's (/health, /metrics, etc) + DiagnosticAddrs *[]string + + // H2CEnabled flag controls whether OPA will allow H2C (HTTP/2 cleartext) on + // HTTP listeners. + H2CEnabled bool + + // Authentication is the type of authentication scheme to use. + Authentication server.AuthenticationScheme + + // Authorization is the type of authorization scheme to use. + Authorization server.AuthorizationScheme + + // Certificate is the certificate to use in server-mode. If the certificate + // is nil, the server will NOT use TLS. + Certificate *tls.Certificate + + // CertificateFile and CertificateKeyFile are the paths to the cert and its + // keyfile. It'll be used to periodically reload the files from disk if they + // have changed. The server will attempt to refresh every 5 minutes, unless + // a different CertificateRefresh time.Duration is provided + CertificateFile string + CertificateKeyFile string + CertificateRefresh time.Duration + + // CertPool holds the CA certs trusted by the OPA server. + CertPool *x509.CertPool + // CertPoolFile, if set permits the reloading of the CA cert pool from disk + CertPoolFile string + + // MinVersion contains the minimum TLS version that is acceptable. + // If zero, TLS 1.2 is currently taken as the minimum. + MinTLSVersion uint16 + + // HistoryPath is the filename to store the interactive shell user + // input history. + HistoryPath string + + // Output format controls how the REPL will print query results. + // Default: "pretty". + OutputFormat string + + // Paths contains filenames of base documents and policy modules to load on + // startup. Data files may be prefixed with ":" to indicate + // where the contained document should be loaded. + Paths []string + + // Optional filter that will be passed to the file loader. + Filter loader.Filter + + // BundleMode will enable treating the Paths provided as bundles rather than + // loading all data & policy files. + BundleMode bool + + // Watch flag controls whether OPA will watch the Paths files for changes. + // If this flag is true, OPA will watch the Paths files for changes and + // reload the storage layer each time they change. This is useful for + // interactive development. + Watch bool + + // ErrorLimit is the number of errors the compiler will allow to occur before + // exiting early. + ErrorLimit int + + // PprofEnabled flag controls whether pprof endpoints are enabled + PprofEnabled bool + + // DecisionIDFactory generates decision IDs to include in API responses + // sent by the server (in response to Data API queries.) + DecisionIDFactory func() string + + // Logging configures the logging behaviour. + Logging LoggingConfig + + // Logger sets the logger implementation to use for debug logs. + Logger logging.Logger + + // ConsoleLogger sets the logger implementation to use for console logs. + ConsoleLogger logging.Logger + + // ConfigFile refers to the OPA configuration to load on startup. + ConfigFile string + + // ConfigOverrides are overrides for the OPA configuration that are applied + // over top the config file They are in a list of key=value syntax that + // conform to the syntax defined in the `strval` package + ConfigOverrides []string + + // ConfigOverrideFiles Similar to `ConfigOverrides` except they are in the + // form of `key=path/to/file`where the file contains the value to be used. + ConfigOverrideFiles []string + + // Output is the output stream used when run as an interactive shell. This + // is mostly for test purposes. + Output io.Writer + + // GracefulShutdownPeriod is the time (in seconds) to wait for the http + // server to shutdown gracefully. + GracefulShutdownPeriod int + + // ShutdownWaitPeriod is the time (in seconds) to wait before initiating shutdown. + ShutdownWaitPeriod int + + // EnableVersionCheck flag controls whether OPA will report its version to an external service. + // If this flag is true, OPA will report its version to the external service + EnableVersionCheck bool + + // BundleVerificationConfig sets the key configuration used to verify a signed bundle + BundleVerificationConfig *bundle.VerificationConfig + + // SkipBundleVerification flag controls whether OPA will verify a signed bundle + SkipBundleVerification bool + + // BundleActivatorPlugin controls the name of the activator plugin used to load bundles into the store. + BundleActivatorPlugin string + + // BundleLazyLoadingMode flag controls whether OPA will load bundle contents in lazy mode. + BundleLazyLoadingMode bool + + // SkipKnownSchemaCheck flag controls whether OPA will perform type checking on known input schemas + SkipKnownSchemaCheck bool + + // ReadyTimeout flag controls if and for how long OPA server will wait (in seconds) for + // configured bundles and plugins to be activated/ready before listening for traffic. + // A value of 0 or less means no wait is exercised. + ReadyTimeout int + + // Router is the router to which handlers for the REST API are added. + // Router uses a first-matching-route-wins strategy, so no existing routes are overridden + // If it is nil, a new http.ServeMux will be created + Router *http.ServeMux + + // DiskStorage, if set, will make the runtime instantiate a disk-backed storage + // implementation (instead of the default, in-memory store). + // It can also be enabled via config, and this runtime field takes precedence. + DiskStorage *disk.Options + + // StoreBuilder allows passing a storage backend builder + StoreBuilder func(_ context.Context, _ logging.Logger, _ prometheus_sdk.Registerer, config []byte, id string) (storage.Store, error) + + DistributedTracingOpts tracing.Options + + // Check if default Addr is set or the user has changed it. + AddrSetByUser bool + + // UnixSocketPerm specifies the permission for the Unix domain socket if used to listen for connections + UnixSocketPerm *string + + // V0Compatible will enable OPA features and behaviors that were enabled by default in OPA v0.x releases. + // Takes precedence over V1Compatible. + V0Compatible bool + + // V1Compatible will enable OPA features and behaviors that will be enabled by default in a future OPA v1.0 release. + // This flag allows users to opt-in to the new behavior and helps transition to the future release upon which + // the new behavior will be enabled by default. + // If V0Compatible is set, V1Compatible will be ignored. + V1Compatible bool + + // CipherSuites specifies the list of enabled TLS 1.0–1.2 cipher suites + CipherSuites *[]uint16 + + // ReadAstValuesFromStore controls whether the storage layer should return AST values when reading from the store. + // This is an eager conversion, that comes with an upfront performance cost when updating the store (e.g. bundle updates). + // Evaluation performance is affected in that data doesn't need to be converted to AST during evaluation. + // Only applicable when using the default in-memory store, and not when used together with the DiskStorage option. + ReadAstValuesFromStore bool + + // ExtraDiscoveryOpts allows for passing options to the discovery plugin, as instantiated by the runtime. + ExtraDiscoveryOpts []func(*discovery.Discovery) + + // Hooks is our generic extension mechanism. + Hooks hooks.Hooks + + // NDBCacheEnabled allows enabling the non-deterministic builtin cache globally. + NDBCacheEnabled bool + + Brand string +} + +func (p *Params) regoVersion() ast.RegoVersion { + // v0 takes precedence over v1 + if p.V0Compatible { + return ast.RegoV0 + } + if p.V1Compatible { + return ast.RegoV1 + } + return ast.DefaultRegoVersion +} + +func (p *Params) parserOptions() ast.ParserOptions { + return ast.ParserOptions{RegoVersion: p.regoVersion()} +} + +// LoggingConfig stores the configuration for OPA's logging behaviour. +type LoggingConfig struct { + Level string + Format string + TimestampFormat string +} + +// NewParams returns a new Params object. +func NewParams() Params { + return Params{ + Output: os.Stdout, + BundleMode: false, + EnableVersionCheck: false, + Brand: "OPA", // default + } +} + +type ServerStatus int + +const ( + ServerNotStarted ServerStatus = iota + ServerWaitingForPlugins + ServerInitialized + ServerStopped +) + +// Runtime represents a single OPA instance. +type Runtime struct { + Params Params + Store storage.Store + Manager *plugins.Manager + + logger logging.Logger + server *server.Server + metrics *prometheus.Provider + reporter report.Reporter + traceExporter *otlptrace.Exporter + loadedPathsResult *initload.LoadPathsResult + + serverStatus ServerStatus + serverInitMtx sync.RWMutex + done chan struct{} + repl *repl.REPL +} + +// NewRuntime returns a new Runtime object initialized with params. Clients must +// call StartServer() or StartREPL() to start the runtime in either mode. +func NewRuntime(ctx context.Context, params Params) (*Runtime, error) { + if params.ID == "" { + var err error + params.ID, err = generateInstanceID() + if err != nil { + return nil, err + } + } + + level, err := internal_logging.GetLevel(params.Logging.Level) + if err != nil { + return nil, err + } + + // NOTE(tsandall): This is a temporary hack to ensure that log formatting + // and leveling is applied correctly. Currently there are a few places where + // the global logger is used as a fallback, however, that fallback _should_ + // never be used. This ensures that _if_ the fallback is used accidentally, + // that the logging configuration is applied. Once we remove all usage of + // the global logger and we remove the API that allows callers to access the + // global logger, we can remove this. + logging.Get().SetFormatter(internal_logging.GetFormatter(params.Logging.Format, params.Logging.TimestampFormat)) + logging.Get().SetLevel(level) + + var logger logging.Logger + + if params.Logger != nil { + logger = params.Logger + } else { + stdLogger := logging.New() + stdLogger.SetLevel(level) + stdLogger.SetFormatter(internal_logging.GetFormatter(params.Logging.Format, params.Logging.TimestampFormat)) + logger = stdLogger + } + + if err := params.Hooks.Validate(); err != nil { + return nil, err + } + + var filePaths []string + urlPathCount := 0 + for _, path := range params.Paths { + if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") { + urlPathCount++ + override, err := urlPathToConfigOverride(urlPathCount, path) + if err != nil { + return nil, err + } + params.ConfigOverrides = append(params.ConfigOverrides, override...) + } else { + filePaths = append(filePaths, path) + } + } + params.Paths = filePaths + + config, err := config.Load(params.ConfigFile, params.ConfigOverrides, params.ConfigOverrideFiles) + if err != nil { + return nil, fmt.Errorf("config error: %w", err) + } + + var reporter report.Reporter + if params.EnableVersionCheck { + var err error + reporter, err = report.New(report.Options{Logger: logger}) + if err != nil { + return nil, fmt.Errorf("config error: %w", err) + } + } + + regoVersion := params.regoVersion() + + loaded, err := initload.LoadPathsForRegoVersion(regoVersion, params.Paths, params.Filter, params.BundleMode, params.BundleVerificationConfig, params.SkipBundleVerification, params.BundleLazyLoadingMode, false, false, nil, nil) + if err != nil { + return nil, fmt.Errorf("load error: %w", err) + } + + isAuthorizationEnabled := params.Authorization != server.AuthorizationOff + + info, err := runtime.Term(runtime.Params{Config: config, IsAuthorizationEnabled: isAuthorizationEnabled, SkipKnownSchemaCheck: params.SkipKnownSchemaCheck}) + if err != nil { + return nil, err + } + + consoleLogger := params.ConsoleLogger + if consoleLogger == nil { + l := logging.New() + l.SetFormatter(internal_logging.GetFormatter(params.Logging.Format, params.Logging.TimestampFormat)) + consoleLogger = l + } + + if params.Router == nil { + params.Router = http.NewServeMux() + } + + metricsConfig, parseConfigErr := extractMetricsConfig(config, params) + if parseConfigErr != nil { + return nil, parseConfigErr + } + metrics := prometheus.New(metrics.New(), errorLogger(logger), metricsConfig.Prom.HTTPRequestDurationSeconds.Buckets) + + var store storage.Store + if params.DiskStorage == nil { + params.DiskStorage, err = disk.OptionsFromConfig(config, params.ID) + if err != nil { + return nil, fmt.Errorf("parse disk store configuration: %w", err) + } + } + + switch { + case params.DiskStorage != nil: + store, err = disk.New(ctx, logger, metrics, *params.DiskStorage) + if err != nil { + return nil, fmt.Errorf("initialize disk store: %w", err) + } + case params.StoreBuilder != nil: + store, err = params.StoreBuilder(ctx, logger, metrics, config, params.ID) + if err != nil { + return nil, fmt.Errorf("initialize store: %w", err) + } + default: + store = inmem.NewWithOpts(inmem.OptRoundTripOnWrite(false), + inmem.OptReturnASTValuesOnRead(params.ReadAstValuesFromStore)) + } + + traceExporter, tracerProvider, _, err := internal_tracing.Init(ctx, config, params.ID) + if err != nil { + return nil, fmt.Errorf("config error: %w", err) + } + if tracerProvider != nil { + params.DistributedTracingOpts = tracing.NewOptions( + otelhttp.WithTracerProvider(tracerProvider), + otelhttp.WithPropagators(propagation.TraceContext{}), + ) + } + + manager, err := plugins.New(config, + params.ID, + store, + plugins.Info(info), + plugins.InitBundles(loaded.Bundles), + plugins.InitFiles(loaded.Files), + plugins.MaxErrors(params.ErrorLimit), + plugins.GracefulShutdownPeriod(params.GracefulShutdownPeriod), + plugins.ConsoleLogger(consoleLogger), + plugins.Logger(logger), + plugins.EnablePrintStatements(logger.GetLevel() >= logging.Info), + plugins.PrintHook(loggingPrintHook{logger: logger}), + plugins.WithRouter(params.Router), + plugins.WithPrometheusRegister(metrics), + plugins.WithTracerProvider(tracerProvider), + plugins.WithEnableTelemetry(params.EnableVersionCheck), + plugins.WithParserOptions(params.parserOptions()), + plugins.WithDistributedTracingOpts(params.DistributedTracingOpts), + plugins.WithBundleActivatorPlugin(params.BundleActivatorPlugin), + plugins.WithHooks(params.Hooks), + ) + if err != nil { + return nil, fmt.Errorf("config error: %w", err) + } + + if err := manager.Init(ctx); err != nil { + return nil, fmt.Errorf("initialization error: %w", err) + } + + if isAuthorizationEnabled && !params.SkipKnownSchemaCheck { + if err := verifyAuthorizationPolicySchema(manager); err != nil { + return nil, fmt.Errorf("initialization error: %w", err) + } + } + + var bootConfig map[string]any + err = util.Unmarshal(config, &bootConfig) + if err != nil { + return nil, fmt.Errorf("config error: %w", err) + } + + opts := make([]func(*discovery.Discovery), 0, len(params.ExtraDiscoveryOpts)+3) + opts = append(opts, + discovery.Factories(registeredPlugins), + discovery.Metrics(metrics), + discovery.BootConfig(bootConfig), + ) + opts = append(opts, params.ExtraDiscoveryOpts...) + disco, err := discovery.New(manager, opts...) + if err != nil { + return nil, fmt.Errorf("config error: %w", err) + } + + manager.Register(discovery.Name, disco) + + rt := &Runtime{ + Store: manager.Store, + Params: params, + Manager: manager, + logger: logger, + metrics: metrics, + reporter: reporter, + serverStatus: ServerNotStarted, + traceExporter: traceExporter, + loadedPathsResult: loaded, + } + + return rt, nil +} + +// extractMetricsConfig returns the configuration for server metrics and parsing errors if any +func extractMetricsConfig(config []byte, params Params) (*metrics_config.Config, error) { + var opaParsedConfig, opaParsedConfigErr = opa_config.ParseConfig(config, params.ID) + if opaParsedConfigErr != nil { + return nil, opaParsedConfigErr + } + + var serverMetricsData []byte + if opaParsedConfig.Server != nil { + serverMetricsData = opaParsedConfig.Server.Metrics + } + + var configBuilder = metrics_config.NewConfigBuilder() + var metricsParsedConfig, metricsParsedConfigErr = configBuilder.WithBytes(serverMetricsData).Parse() + if metricsParsedConfigErr != nil { + return nil, fmt.Errorf("server metrics configuration parse error: %w", metricsParsedConfigErr) + } + + return metricsParsedConfig, nil +} + +func (rt *Runtime) setServerStatus(status ServerStatus) { + rt.serverInitMtx.Lock() + defer rt.serverInitMtx.Unlock() + rt.serverStatus = status +} + +func (rt *Runtime) ServerStatus() ServerStatus { + rt.serverInitMtx.RLock() + defer rt.serverInitMtx.RUnlock() + return rt.serverStatus +} + +// StartServer starts the runtime in server mode. This function will block the +// calling goroutine. +func (rt *Runtime) StartServer(ctx context.Context) { + err := rt.Serve(ctx) + if err != nil { + os.Exit(1) + } +} + +// Serve will start a new REST API server and listen for requests. This +// will block until either: an error occurs, the context is canceled, or +// a SIGTERM or SIGKILL signal is sent. +func (rt *Runtime) Serve(ctx context.Context) error { + if rt.Params.Addrs == nil { + return errors.New("at least one address must be configured in runtime parameters") + } + + serverInitializingMessage := "Initializing server." + if !rt.Params.AddrSetByUser && rt.Params.V0Compatible { + serverInitializingMessage += " OPA is running on a public (0.0.0.0) network interface. Unless you intend to expose OPA outside of the host, binding to the localhost interface (--addr localhost:8181) is recommended. See https://www.openpolicyagent.org/docs/latest/security/#interface-binding" + } + + if rt.Params.DiagnosticAddrs == nil { + rt.Params.DiagnosticAddrs = &[]string{} + } + + rt.logger.WithFields(map[string]any{ + "addrs": *rt.Params.Addrs, + "diagnostic-addrs": *rt.Params.DiagnosticAddrs, + }).Info(serverInitializingMessage) + + if rt.Params.Authorization == server.AuthorizationOff && rt.Params.Authentication == server.AuthenticationToken { + rt.logger.Error("Token authentication enabled without authorization. Authentication will be ineffective. See https://www.openpolicyagent.org/docs/latest/security/#authentication-and-authorization for more information.") + } + + checkUserPrivileges(rt.logger) + + // NOTE(tsandall): at some point, hopefully we can remove this because the + // Go runtime will just do the right thing. Until then, try to set + // GOMAXPROCS based on the CPU quota applied to the process. + undo, err := maxprocs.Set(maxprocs.Logger(func(f string, a ...any) { + rt.logger.Debug(f, a...) + })) + if err != nil { + rt.logger.WithFields(map[string]any{"err": err}).Debug("Failed to set GOMAXPROCS from CPU quota.") + } + + defer undo() + + if err := rt.Manager.Start(ctx); err != nil { + rt.logger.WithFields(map[string]any{"err": err}).Error("Failed to start plugins.") + return err + } + + defer rt.Manager.Stop(ctx) + + if rt.traceExporter != nil { + if err := rt.traceExporter.Start(ctx); err != nil { + rt.logger.WithFields(map[string]any{"err": err}).Error("Failed to start OpenTelemetry trace exporter.") + return err + } + } + + rt.server = server.New(). + WithRouter(rt.Params.Router). + WithStore(rt.Store). + WithManager(rt.Manager). + WithCompilerErrorLimit(rt.Params.ErrorLimit). + WithPprofEnabled(rt.Params.PprofEnabled). + WithAddresses(*rt.Params.Addrs). + WithH2CEnabled(rt.Params.H2CEnabled). + // always use the initial values for the certificate and ca pool, reloading behavior is configured below + WithCertificate(rt.Params.Certificate). + WithCertPool(rt.Params.CertPool). + WithAuthentication(rt.Params.Authentication). + WithAuthorization(rt.Params.Authorization). + WithDecisionIDFactory(rt.decisionIDFactory). + WithDecisionLoggerWithErr(rt.decisionLogger). + WithRuntime(rt.Manager.Info). + WithMetrics(rt.metrics). + WithMinTLSVersion(rt.Params.MinTLSVersion). + WithCipherSuites(rt.Params.CipherSuites). + WithDistributedTracingOpts(rt.Params.DistributedTracingOpts). + WithHooks(rt.Params.Hooks). + WithNDBCacheEnabled(rt.Params.NDBCacheEnabled) + + // If decision_logging plugin enabled, check to see if we opted in to the ND builtins cache. + if lp := logs.Lookup(rt.Manager); lp != nil { + rt.server = rt.server.WithNDBCacheEnabled(rt.Params.NDBCacheEnabled || rt.Manager.Config.NDBuiltinCacheEnabled()) + } + + if rt.Params.DiagnosticAddrs != nil { + rt.server = rt.server.WithDiagnosticAddresses(*rt.Params.DiagnosticAddrs) + } + + if rt.Params.UnixSocketPerm != nil { + rt.server = rt.server.WithUnixSocketPermission(rt.Params.UnixSocketPerm) + } + + // If a refresh period is set, then we will periodically reload the certificate and ca pool. Otherwise, we will only + // reload cert, key and ca pool files when they change on disk. + if rt.Params.CertificateRefresh > 0 { + rt.server = rt.server.WithCertRefresh(rt.Params.CertificateRefresh) + } + + // if either the cert or the ca pool file is set then these fields will be set on the server and reloaded when they + // change on disk. + if rt.Params.CertificateFile != "" || rt.Params.CertPoolFile != "" { + rt.server = rt.server.WithTLSConfig(&server.TLSConfig{ + CertFile: rt.Params.CertificateFile, + KeyFile: rt.Params.CertificateKeyFile, + CertPoolFile: rt.Params.CertPoolFile, + }) + } + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + rt.server, err = rt.server.Init(ctx) + if err != nil { + rt.logger.WithFields(map[string]any{"err": err}).Error("Unable to initialize server.") + return err + } + + if rt.Params.Watch { + if err := rt.startWatcher(ctx, rt.Params.Paths, rt.onReloadLogger); err != nil { + rt.logger.WithFields(map[string]any{"err": err}).Error("Unable to open watch.") + return err + } + } + + if rt.Params.EnableVersionCheck { + rt.done = make(chan struct{}) + go rt.checkOPAUpdateLoop(ctx, rt.done) + } + + defer func() { + if rt.done != nil { + rt.done <- struct{}{} + } + }() + + rt.server.Handler = NewLoggingHandler(rt.logger, rt.server.Handler) + rt.server.DiagnosticHandler = NewLoggingHandler(rt.logger, rt.server.DiagnosticHandler) + + rt.setServerStatus(ServerWaitingForPlugins) + + if err := rt.waitPluginsReady( + 100*time.Millisecond, + time.Second*time.Duration(rt.Params.ReadyTimeout)); err != nil { + rt.logger.WithFields(map[string]any{"err": err}).Error("Failed to wait for plugins activation.") + return err + } + + loops, err := rt.server.Listeners() + if err != nil { + rt.logger.WithFields(map[string]any{"err": err}).Error("Unable to create listeners.") + return err + } + + errc := make(chan error) + for _, loop := range loops { + go func(serverLoop func() error) { + errc <- serverLoop() + }(loop) + } + + // Buffer one element as os/signal uses non-blocking channel sends. + // This prevents potentially dropping the first element and failing to shut + // down gracefully. A buffer of 1 is sufficient as we're just looking for a + // one-time shutdown signal. + signalc := make(chan os.Signal, 1) + signal.Notify(signalc, syscall.SIGINT, syscall.SIGTERM) + + // Note that there is a small chance the socket of the server listener is still + // closed by the time this block is executed, due to the serverLoop above + // executing in a goroutine. + rt.setServerStatus(ServerInitialized) + rt.Manager.ServerInitialized() + + rt.logger.Debug("Server initialized.") + + defer rt.setServerStatus(ServerStopped) + + for { + select { + case <-ctx.Done(): + return rt.gracefulServerShutdown(rt.server) + case <-signalc: + return rt.gracefulServerShutdown(rt.server) + case err := <-errc: + rt.logger.WithFields(map[string]any{"err": err}).Error("Listener failed.") + os.Exit(1) //nolint:gocritic + } + } +} + +// Addrs returns a list of addresses that the runtime is listening on (when +// in server mode). Returns an empty list if it hasn't started listening. +func (rt *Runtime) Addrs() []string { + rt.serverInitMtx.RLock() + defer rt.serverInitMtx.RUnlock() + + if rt.serverStatus < ServerInitialized { + return nil + } + + return rt.server.Addrs() +} + +// DiagnosticAddrs returns a list of diagnostic addresses that the runtime is +// listening on (when in server mode). Returns an empty list if it hasn't +// started listening. +func (rt *Runtime) DiagnosticAddrs() []string { + if rt.server == nil { + return nil + } + + return rt.server.DiagnosticAddrs() +} + +// StartREPL starts the runtime in REPL mode. This function will block the calling goroutine. +func (rt *Runtime) StartREPL(ctx context.Context) error { + if err := rt.Manager.Start(ctx); err != nil { + fmt.Fprintln(rt.Params.Output, "error starting plugins:", err) + return err + } + + defer rt.Manager.Stop(ctx) + + banner := rt.getBanner() + repl := repl.New(rt.Store, rt.Params.HistoryPath, rt.Params.Output, rt.Params.OutputFormat, rt.Params.ErrorLimit, banner). + WithRuntime(rt.Manager.Info). + WithRegoVersion(rt.Params.regoVersion()). + WithInitBundles(rt.loadedPathsResult.Bundles) + + if rt.Params.Watch { + if err := rt.startWatcher(ctx, rt.Params.Paths, onReloadPrinter(rt.Params.Output)); err != nil { + fmt.Fprintln(rt.Params.Output, "error opening watch:", err) + return err + } + } + + if rt.Params.EnableVersionCheck { + go func() { + repl.SetOPAVersionReport(rt.checkOPAUpdate(ctx).Slice()) + }() + } + + rt.repl = repl + return repl.Loop(ctx) +} + +// SetDistributedTracingLogging configures the distributed tracing's ErrorHandler, +// and logger instances. +func (rt *Runtime) SetDistributedTracingLogging() { + internal_tracing.SetupLogging(rt.logger) +} + +func (rt *Runtime) checkOPAUpdate(ctx context.Context) *report.DataResponse { + resp, _ := rt.reporter.SendReport(ctx) + return resp +} + +func (rt *Runtime) checkOPAUpdateLoop(ctx context.Context, done chan struct{}) { + rt.checkOPAUpdateLoopDurations(ctx, done, defaultInitialUploadInterval, defaultLaterUploadInterval) +} + +func (rt *Runtime) checkOPAUpdateLoopDurations(ctx context.Context, done chan struct{}, initialDur, laterDur time.Duration) { + ticker := time.NewTicker(initialDur) + i := 0 + mr.New(mr.NewSource(time.Now().UnixNano())) // Seed the PRNG. + + for { + resp, err := rt.reporter.SendReport(ctx) + if err != nil { + rt.logger.WithFields(map[string]any{"err": err}).Debug("Unable to send %s version report.", rt.Params.Brand) + } else { + if resp.Latest.OPAUpToDate { + rt.logger.WithFields(map[string]any{ + "current_version": version.Version, + }).Debug("%s is up to date.", rt.Params.Brand) + } else { + rt.logger.WithFields(map[string]any{ + "download_opa": resp.Latest.Download, + "release_notes": resp.Latest.ReleaseNotes, + "current_version": version.Version, + "latest_version": strings.TrimPrefix(resp.Latest.LatestRelease, "v"), + }).Info("%s is out of date.", rt.Params.Brand) + } + } + select { + case <-ticker.C: + ticker.Stop() + i++ // count the attempts + + newInterval := time.Duration(mr.Int63n(int64(time.Hour / time.Second))) // spray, between 0 and 1 hr + if i < 6 { + newInterval += initialDur + } else { + newInterval += laterDur + } + ticker = time.NewTicker(newInterval) + case <-done: + ticker.Stop() + return + } + } +} + +func (rt *Runtime) decisionIDFactory() string { + if rt.Params.DecisionIDFactory != nil { + return rt.Params.DecisionIDFactory() + } + if logs.Lookup(rt.Manager) != nil { + return generateDecisionID() + } + return "" +} + +func (rt *Runtime) decisionLogger(ctx context.Context, event *server.Info) error { + plugin := logs.Lookup(rt.Manager) + if plugin == nil { + return nil + } + + return plugin.Log(ctx, event) +} + +func (rt *Runtime) startWatcher(ctx context.Context, paths []string, onReload func(time.Duration, error)) error { + watcher, err := rt.getWatcher(paths) + if err != nil { + return err + } + go rt.readWatcher(ctx, watcher, paths, onReload) + return nil +} + +func (rt *Runtime) readWatcher(ctx context.Context, watcher *fsnotify.Watcher, paths []string, onReload func(time.Duration, error)) { + for evt := range watcher.Events { + removalMask := fsnotify.Remove | fsnotify.Rename + mask := fsnotify.Create | fsnotify.Write | removalMask + if (evt.Op & mask) != 0 { + rt.logger.WithFields(map[string]any{ + "event": evt.String(), + }).Debug("Registered file event.") + t0 := time.Now() + removed := "" + if (evt.Op & removalMask) != 0 { + removed = evt.Name + } + err := rt.processWatcherUpdate(ctx, paths, removed) + onReload(time.Since(t0), err) + } + } +} + +func (rt *Runtime) processWatcherUpdate(ctx context.Context, paths []string, removed string) error { + return pathwatcher.ProcessWatcherUpdateForRegoVersion(ctx, rt.Manager.ParserOptions().RegoVersion, paths, removed, rt.Store, rt.Params.Filter, rt.Params.BundleMode, rt.Params.BundleLazyLoadingMode, func(ctx context.Context, txn storage.Transaction, loaded *initload.LoadPathsResult) error { + _, err := initload.InsertAndCompile(ctx, initload.InsertAndCompileOptions{ + Store: rt.Store, + Txn: txn, + Files: loaded.Files, + Bundles: loaded.Bundles, + MaxErrors: -1, + ParserOptions: rt.Manager.ParserOptions(), + }) + + return err + }) +} + +func (rt *Runtime) getBanner() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "%s %v (commit %v, built at %v)\n", rt.Params.Brand, version.Version, version.Vcs, version.Timestamp) + fmt.Fprintf(&buf, "\n") + fmt.Fprintf(&buf, "Run 'help' to see a list of commands and check for updates.\n") + return buf.String() +} + +func (rt *Runtime) gracefulServerShutdown(s *server.Server) error { + if rt.Params.ShutdownWaitPeriod > 0 { + rt.logger.Info("Waiting %vs before initiating shutdown...", rt.Params.ShutdownWaitPeriod) + time.Sleep(time.Duration(rt.Params.ShutdownWaitPeriod) * time.Second) + } + + rt.logger.Info("Shutting down...") + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(rt.Params.GracefulShutdownPeriod)*time.Second) + defer cancel() + err := s.Shutdown(ctx) + if err != nil { + rt.logger.WithFields(map[string]any{"err": err}).Error("Failed to shutdown server gracefully.") + return err + } + rt.logger.Info("Server shutdown.") + + if rt.traceExporter != nil { + err = rt.traceExporter.Shutdown(ctx) + if err != nil { + rt.logger.WithFields(map[string]any{"err": err}).Error("Failed to shutdown OpenTelemetry trace exporter gracefully.") + } + } + return nil +} + +func (rt *Runtime) waitPluginsReady(checkInterval, timeout time.Duration) error { + if timeout <= 0 { + return nil + } + + // check readiness of all plugins + pluginsReady := func() bool { + for _, status := range rt.Manager.PluginStatus() { + if status != nil && status.State != plugins.StateOK { + return false + } + } + return true + } + + rt.logger.Debug("Waiting for plugins activation (%v).", timeout) + + return util.WaitFunc(pluginsReady, checkInterval, timeout) +} + +func (rt *Runtime) onReloadLogger(d time.Duration, err error) { + rt.logger.WithFields(map[string]any{ + "duration": d, + "err": err, + }).Info("Processed file watch event.") +} + +func (rt *Runtime) getWatcher(rootPaths []string) (*fsnotify.Watcher, error) { + watcher, err := pathwatcher.CreatePathWatcher(rootPaths) + if err != nil { + return nil, err + } + + for _, path := range watcher.WatchList() { + rt.logger.WithFields(map[string]any{"path": path}).Debug("watching path") + } + + return watcher, nil +} + +func urlPathToConfigOverride(pathCount int, path string) ([]string, error) { + uri, err := url.Parse(path) + if err != nil { + return nil, err + } + baseURL := uri.Scheme + "://" + uri.Host + urlPath := uri.Path + if uri.RawQuery != "" { + urlPath += "?" + uri.RawQuery + } + + return []string{ + fmt.Sprintf("services.cli%d.url=%s", pathCount, baseURL), + fmt.Sprintf("bundles.cli%d.service=cli%d", pathCount, pathCount), + fmt.Sprintf("bundles.cli%d.resource=%s", pathCount, urlPath), + fmt.Sprintf("bundles.cli%d.persist=true", pathCount), + }, nil +} + +func errorLogger(logger logging.Logger) func(attrs map[string]any, f string, a ...any) { + return func(attrs map[string]any, f string, a ...any) { + logger.WithFields(attrs).Error(f, a...) + } +} + +func onReloadPrinter(output io.Writer) func(time.Duration, error) { + return func(d time.Duration, err error) { + if err != nil { + fmt.Fprintf(output, "\n# reload error (took %v): %v", d, err) + } else { + fmt.Fprintf(output, "\n# reloaded files (took %v)", d) + } + } +} + +func generateInstanceID() (string, error) { + return uuid.New(rand.Reader) +} + +func generateDecisionID() string { + id, err := uuid.New(rand.Reader) + if err != nil { + return "" + } + return id +} + +func verifyAuthorizationPolicySchema(m *plugins.Manager) error { + authorizationDecisionRef, err := ref.ParseDataPath(*m.Config.DefaultAuthorizationDecision) + if err != nil { + return err + } + + return compiler.VerifyAuthorizationPolicySchema(m.GetCompiler(), authorizationDecisionRef) +} + +func init() { + registeredPlugins = make(map[string]plugins.Factory) +} diff --git a/third_party/opa/v1/runtime/runtime_test.go b/third_party/opa/v1/runtime/runtime_test.go new file mode 100644 index 000000000000..acf4c50d29f2 --- /dev/null +++ b/third_party/opa/v1/runtime/runtime_test.go @@ -0,0 +1,2191 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package runtime + +import ( + "bufio" + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "maps" + "net/http" + "net/http/httptest" + "os" + "path" + "path/filepath" + "reflect" + "runtime" + "slices" + "strings" + "testing" + "time" + + prometheus_sdk "github.com/prometheus/client_golang/prometheus" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/config" + "github.com/open-policy-agent/opa/v1/hooks" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/plugins/discovery" + "github.com/open-policy-agent/opa/v1/server/authorizer" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/tracing" + + "github.com/open-policy-agent/opa/internal/report" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/logging" + testLog "github.com/open-policy-agent/opa/v1/logging/test" + sdktest "github.com/open-policy-agent/opa/v1/sdk/test" + "github.com/open-policy-agent/opa/v1/server" + "github.com/open-policy-agent/opa/v1/storage" + topdown_cache "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestRuntimeProcessWatchEvents(t *testing.T) { + tests := []struct { + note string + asBundle bool + readAst bool + }{ + { + note: "no bundle, read raw data", + }, + { + note: "no bundle, read ast", + readAst: true, + }, + { + note: "bundle, read raw data", + asBundle: true, + }, + { + note: "bundle, read ast", + asBundle: true, + readAst: true, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + testRuntimeProcessWatchEvents(t, tc.asBundle, tc.readAst) + }) + } +} + +func testRuntimeProcessWatchEvents(t *testing.T, asBundle bool, readAst bool) { + t.Helper() + + ctx := context.Background() + fs := map[string]string{ + "test/some/data.json": `{ + "hello": "world" + }`, + } + + test.WithTempFS(fs, func(rootDir string) { + // Prefix the directory intended to be watched with at least one + // directory to avoid permission issues on the local host. Otherwise we + // cannot always watch the tmp directory's parent. + rootDir = filepath.Join(rootDir, "test") + + params := NewParams() + params.Paths = []string{rootDir} + params.BundleMode = asBundle + params.ReadAstValuesFromStore = readAst + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatal(err) + } + + txn := storage.NewTransactionOrDie(ctx, rt.Store) + _, err = rt.Store.Read(ctx, txn, storage.MustParsePath("/system/version")) + if err != nil { + t.Fatal(err) + } + rt.Store.Abort(ctx, txn) + + var buf bytes.Buffer + + if err := rt.startWatcher(ctx, params.Paths, onReloadPrinter(&buf)); err != nil { + t.Fatalf("Unexpected watcher init error: %v", err) + } + + expected := map[string]any{ + "hello": "world-2", + } + + if err := os.WriteFile(path.Join(rootDir, "some/data.json"), util.MustMarshalJSON(expected), 0644); err != nil { + panic(err) + } + + t0 := time.Now() + path := storage.MustParsePath("/some") + + // In practice, reload takes ~100us on development machine. + maxWaitTime := time.Second * 1 + var val any + + for time.Since(t0) < maxWaitTime { + time.Sleep(1 * time.Millisecond) + txn := storage.NewTransactionOrDie(ctx, rt.Store) + var err error + val, err = rt.Store.Read(ctx, txn, path) + if err != nil { + panic(err) + } + + // Ensure the update didn't overwrite the system version information + _, err = rt.Store.Read(ctx, txn, storage.MustParsePath("/system/version")) + if err != nil { + t.Fatal(err) + } + + rt.Store.Abort(ctx, txn) + + if readAst { + exp, _ := ast.InterfaceToValue(expected) + if ast.Compare(val, exp) == 0 { + return // success + } + } else if reflect.DeepEqual(val, expected) { + return // success + } + + } + + t.Fatalf("Did not see expected change in %v, last value: %v, buf: %v", maxWaitTime, val, buf.String()) + }) +} + +func TestRuntimeProcessWatchEventPolicyError(t *testing.T) { + testRuntimeProcessWatchEventPolicyError(t, false) +} + +func TestRuntimeProcessWatchEventPolicyErrorWithBundle(t *testing.T) { + testRuntimeProcessWatchEventPolicyError(t, true) +} + +func testRuntimeProcessWatchEventPolicyError(t *testing.T, asBundle bool) { + ctx := context.Background() + + fs := map[string]string{ + "test/x.rego": `package test + + default x = 1 + `, + } + + test.WithTempFS(fs, func(rootDir string) { + // Prefix the directory intended to be watched with at least one + // directory to avoid permission issues on the local host. Otherwise we + // cannot always watch the tmp directory's parent. + rootDir = filepath.Join(rootDir, "test") + + params := NewParams() + params.Paths = []string{rootDir} + params.BundleMode = asBundle + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatal(err) + } + + err = storage.Txn(ctx, rt.Store, storage.WriteParams, func(txn storage.Transaction) error { + return rt.Store.UpsertPolicy(ctx, txn, "out-of-band.rego", []byte(`package foo`)) + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + ch := make(chan error) + + testFunc := func(_ time.Duration, err error) { + ch <- err + } + + if err := rt.startWatcher(ctx, params.Paths, testFunc); err != nil { + t.Fatalf("Unexpected watcher init error: %v", err) + } + + newModule := []byte(`package test + + default x = 2`) + + if err := os.WriteFile(path.Join(rootDir, "y.rego"), newModule, 0644); err != nil { + t.Fatal(err) + } + + // Wait for up to 1 second before considering test failed. On Linux we + // observe multiple events on write (e.g., create -> write) which + // triggers two errors instead of one, whereas on Darwin only a single + // event (e.g., create) is sent. Same as below. + maxWait := time.Second + timer := time.NewTimer(maxWait) + + // Expect type error. + func() { + for { + select { + case result := <-ch: + if errs, ok := result.(ast.Errors); ok { + if errs[0].Code == ast.TypeErr { + err = nil + return + } + } + err = result + case <-timer.C: + return + } + } + }() + + if err != nil { + t.Fatalf("Expected specific failure before %v. Last error: %v", maxWait, err) + } + + if err := os.Remove(path.Join(rootDir, "x.rego")); err != nil { + t.Fatal(err) + } + + timer = time.NewTimer(maxWait) + + // Expect no error. + func() { + for { + select { + case result := <-ch: + if result == nil { + err = nil + return + } + err = result + case <-timer.C: + return + } + } + }() + + if err != nil { + t.Fatalf("Expected result to succeed before %v. Last error: %v", maxWait, err) + } + + }) +} + +func TestRuntimeReplWithBundleBuiltWithV1Compatibility(t *testing.T) { + ctx := context.Background() + + test.WithTempFS(nil, func(rootDir string) { + p := filepath.Join(rootDir, "bundle.tar.gz") + + mod := `package test + p := 7 if 3 < 4 + ` + + files := [][2]string{ + {"/.manifest", `{"revision": "foo", "rego_version": 1}`}, + {"/x.rego", mod}, + } + + buf := archive.MustWriteTarGz(files) + bf, err := os.Create(p) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + output := test.BlockingWriter{} + + params := NewParams() + params.Output = &output + params.Paths = []string{p} + params.BundleMode = true + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatal(err) + } + + go func() { _ = rt.StartREPL(ctx) }() + + if !test.Eventually(t, 5*time.Second, func() bool { + return strings.Contains(output.String(), "Run 'help' to see a list of commands and check for updates.") + }) { + t.Fatal("Timed out waiting for REPL to start") + } + output.Reset() + + if err := rt.repl.OneShot(ctx, "data.test.p"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + actual := strings.TrimSpace(output.String()) + expected := "7" + + if actual != expected { + t.Fatalf("expected data.test.p to be %v, got %v", expected, actual) + } + }) +} + +func TestRuntimeReplProcessWatchV1Compatible(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + v1Compatible bool + policy string + expErrs []string + expOutput string + }{ + { + note: "v0, keywords not used", + v0Compatible: true, + policy: `package test +p[1] { + data.foo == "bar" +}`, + }, + { + note: "v0, keywords not imported", + v0Compatible: true, + policy: `package test +p contains 1 if { + data.foo == "bar" +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0, keywords imported", + v0Compatible: true, + policy: `package test +import future.keywords +p contains 1 if { + data.foo == "bar" +}`, + }, + { + note: "v0, rego.v1 imported", + v0Compatible: true, + policy: `package test +import rego.v1 +p contains 1 if { + data.foo == "bar" +}`, + }, + + { + note: "v1, keywords not used", + v1Compatible: true, + policy: `package test +p[1] { + data.foo == "bar" +}`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1, keywords not imported", + v1Compatible: true, + policy: `package test +p contains 1 if { + data.foo == "bar" +}`, + }, + { + note: "v1, keywords imported", + v1Compatible: true, + policy: `package test +import future.keywords +p contains 1 if { + data.foo == "bar" +}`, + }, + { + note: "v1, rego.v1 imported", + v1Compatible: true, + policy: `package test +import rego.v1 +p contains 1 if { + data.foo == "bar" +}`, + }, + } + + fs := map[string]string{ + "test/data.json": `{"foo": "bar"}`, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + test.WithTempFS(fs, func(rootDir string) { + // Prefix the directory intended to be watched with at least one + // directory to avoid permission issues on the local host. Otherwise, we + // cannot always watch the tmp directory's parent. + rootDir = filepath.Join(rootDir, "test") + + output := test.BlockingWriter{} + + params := NewParams() + params.Output = &output + params.Paths = []string{rootDir} + params.Watch = true + params.V0Compatible = tc.v0Compatible + params.V1Compatible = tc.v1Compatible + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatal(err) + } + + go func() { _ = rt.StartREPL(ctx) }() + + if !test.Eventually(t, 5*time.Second, func() bool { + return strings.Contains(output.String(), "Run 'help' to see a list of commands and check for updates.") + }) { + t.Fatal("Timed out waiting for REPL to start") + } + output.Reset() + + // write new policy to disk, to trigger the watcher + if err := os.WriteFile(path.Join(rootDir, "authz.rego"), []byte(tc.policy), 0644); err != nil { + t.Fatal(err) + } + + if tc.expErrs != nil { + if !test.Eventually(t, 5*time.Second, func() bool { + for _, expErr := range tc.expErrs { + if !strings.Contains(output.String(), expErr) { + return false + } + } + return true + }) { + t.Fatalf("Expected error(s):\n\n%v\n\ngot output:\n\n%s", tc.expErrs, output.String()) + } + } else { + if !test.Eventually(t, 5*time.Second, func() bool { + return strings.Contains(output.String(), "# reloaded files") + }) { + t.Fatal("Timed out waiting for watcher") + } + } + }) + }) + } +} + +func TestRuntimeServerProcessWatchV1Compatible(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + v1Compatible bool + policy string + expErrs []string + expOutput string + }{ + { + note: "v0, keywords not used", + v0Compatible: true, + policy: `package test +p[1] { + data.foo == "bar" +}`, + }, + { + note: "v0, keywords not imported", + v0Compatible: true, + policy: `package test +p contains 1 if { + data.foo == "bar" +}`, + expErrs: []string{ + "rego_parse_error: var cannot be used for rule name", + "rego_parse_error: number cannot be used for rule name", + }, + }, + { + note: "v0, keywords imported", + v0Compatible: true, + policy: `package test +import future.keywords +p contains 1 if { + data.foo == "bar" +}`, + }, + { + note: "v0, rego.v1 imported", + v0Compatible: true, + policy: `package test +import rego.v1 +p contains 1 if { + data.foo == "bar" +}`, + }, + { + note: "v1, keywords not used", + v1Compatible: true, + policy: `package test +p[1] { + data.foo == "bar" +}`, + expErrs: []string{ + "rego_parse_error: `if` keyword is required before rule body", + "rego_parse_error: `contains` keyword is required for partial set rules", + }, + }, + { + note: "v1, keywords not imported", + v1Compatible: true, + policy: `package test +p contains 1 if { + data.foo == "bar" +}`, + }, + { + note: "v1, keywords imported", + v1Compatible: true, + policy: `package test +import future.keywords +p contains 1 if { + data.foo == "bar" +}`, + }, + { + note: "v1, rego.v1 imported", + v1Compatible: true, + policy: `package test +import rego.v1 +p contains 1 if { + data.foo == "bar" +}`, + }, + } + + fs := map[string]string{ + "test/data.json": `{"foo": "bar"}`, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + test.WithTempFS(fs, func(rootDir string) { + // Prefix the directory intended to be watched with at least one + // directory to avoid permission issues on the local host. Otherwise, we + // cannot always watch the tmp directory's parent. + rootDir = filepath.Join(rootDir, "test") + + testLogger := testLog.New() + + params := NewParams() + params.Logger = testLogger + params.Addrs = &[]string{"localhost:0"} + params.AddrSetByUser = true + params.Paths = []string{rootDir} + params.Watch = true + params.V0Compatible = tc.v0Compatible + params.V1Compatible = tc.v1Compatible + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatal(err) + } + + go rt.StartServer(ctx) + + if !test.Eventually(t, 5*time.Second, func() bool { + found := false + for _, e := range testLogger.Entries() { + found = strings.Contains(e.Message, "Server initialized.") || found + } + return found + }) { + t.Fatal("Timed out waiting for server to start") + } + + // write new policy to disk, to trigger the watcher + if err := os.WriteFile(path.Join(rootDir, "authz.rego"), []byte(tc.policy), 0644); err != nil { + t.Fatal(err) + } + + if tc.expErrs != nil { + // wait for errors + if !test.Eventually(t, 5*time.Second, func() bool { + for _, expErr := range tc.expErrs { + found := false + for _, e := range testLogger.Entries() { + if errs, ok := e.Fields["err"].(loader.Errors); ok { + for _, err := range errs { + found = strings.Contains(err.Error(), expErr) || found + } + } + } + if !found { + return false + } + } + return true + }) { + t.Fatalf("Timed out waiting for watcher. Expected errors:\n\n%v\n\ngot output:\n\n%v", + tc.expErrs, testLogger.Entries()) + } + } else { + // wait for successful reload + if !test.Eventually(t, 5*time.Second, func() bool { + found := false + for _, e := range testLogger.Entries() { + found = strings.Contains(e.Message, "Processed file watch event.") || found + } + return found + }) { + t.Fatal("Timed out waiting for watcher") + } + } + }) + }) + } +} + +func TestCheckOPAUpdateBadURL(t *testing.T) { + testCheckOPAUpdate(t, "http://foo:8112", nil) +} + +func TestCheckOPAUpdateWithNewUpdate(t *testing.T) { + tag := "v100.0.0" + downloadLink := createDownloadLink(tag) + + resp := &report.GHResponse{ + TagName: tag, + Download: downloadLink, + ReleaseNotes: "https://github.com/open-policy-agent/opa/releases/tag/v100.0.0", + } + + // test server + baseURL, teardown := getTestServer(resp, http.StatusOK) + defer teardown() + + exp := &report.DataResponse{Latest: report.ReleaseDetails{ + Download: downloadLink, + ReleaseNotes: "https://github.com/open-policy-agent/opa/releases/tag/v100.0.0", + LatestRelease: tag, + }} + + testCheckOPAUpdate(t, baseURL, exp) +} + +func createDownloadLink(tag string) string { + // to support testing on all supported platforms + downloadLink := fmt.Sprintf("https://openpolicyagent.org/downloads/%s/opa_%v_%v", + tag, runtime.GOOS, runtime.GOARCH) + + if runtime.GOARCH == "arm64" { + downloadLink = fmt.Sprintf("%v_static", downloadLink) + } + + if strings.HasPrefix(runtime.GOOS, "win") { + downloadLink = fmt.Sprintf("%v.exe", downloadLink) + } + + return downloadLink +} + +func TestCheckOPAUpdateLoopBadURL(t *testing.T) { + testCheckOPAUpdateLoop(t, "http://foo:8112", "Unable to send OPA version report") +} + +func TestCheckOPAUpdateLoopNoUpdate(t *testing.T) { + srvResp := &report.GHResponse{ + TagName: "v1.0.0", + } + + // test server + baseURL, teardown := getTestServer(srvResp, http.StatusOK) + defer teardown() + + testCheckOPAUpdateLoop(t, baseURL, "OPA is up to date.") +} + +func TestCheckOPAUpdateLoopLaterRequests(t *testing.T) { + resp := &report.GHResponse{ + TagName: "v1.0.0", + } + + // test server + baseURL, teardown := getTestServer(resp, http.StatusOK) + defer teardown() + + t.Setenv("OPA_TELEMETRY_SERVICE_URL", baseURL) + + ctx := context.Background() + + logger := logging.New() + stdout := bytes.NewBuffer(nil) + logger.SetOutput(stdout) + logger.SetLevel(logging.Debug) + + rt := getTestRuntime(ctx, t, logger) + + done := make(chan struct{}) + go func() { + initial := time.Millisecond + later := 100 * time.Millisecond + rt.checkOPAUpdateLoopDurations(ctx, done, initial, later) + }() + time.Sleep(150 * time.Millisecond) + done <- struct{}{} + + // NOTE(sr): We'll assert that within 200ms, we have gotten less than + // 10 requests. This is a little less strict than we could be, to not + // make this test too sensitive to timing and noise test environments. + // However, it's strict enbough: If the "later" duration wasn't + // respected, we'd see a lot more requests. + needle := "OPA is up to date." + act := strings.Count(stdout.String(), needle) + exp := 7 + if act > exp+1 || act < exp { + t.Fatalf("Expected output to contain: %q >= 7 times, less than 8, got %d", needle, act) + } +} + +func TestCheckOPAUpdateLoopWithNewUpdate(t *testing.T) { + tag := "v100.0.0" + downloadLink := createDownloadLink(tag) + + resp := &report.GHResponse{ + TagName: tag, + Download: downloadLink, + ReleaseNotes: "https://github.com/open-policy-agent/opa/releases/tag/v100.0.0", + } + + // test server + baseURL, teardown := getTestServer(resp, http.StatusOK) + defer teardown() + + testCheckOPAUpdateLoop(t, baseURL, "OPA is out of date.") +} + +func TestRuntimeWithAuthzSchemaVerification(t *testing.T) { + ctx := context.Background() + + fs := map[string]string{ + "test/authz.rego": `package system.authz + import rego.v1 + + default allow := false + + allow if { + input.identity = "foo" + }`, + } + + test.WithTempFS(fs, func(rootDir string) { + rootDir = filepath.Join(rootDir, "test") + + params := NewParams() + params.Paths = []string{rootDir} + params.Authorization = server.AuthorizationBasic + + _, err := NewRuntime(ctx, params) + if err != nil { + t.Fatal(err) + } + + badModule := []byte(`package system.authz + import rego.v1 + + default allow := false + + allow if { + input.identty = "foo" + }`) + + if err := os.WriteFile(path.Join(rootDir, "authz.rego"), badModule, 0644); err != nil { + t.Fatal(err) + } + + _, err = NewRuntime(ctx, params) + if err == nil { + t.Fatal("Expected error but got nil") + } + + if !strings.Contains(err.Error(), "undefined ref: input.identty") { + t.Errorf("Expected error \"%v\" not found", "undefined ref: input.identty") + } + + // no verification checks + params.Authorization = server.AuthorizationOff + _, err = NewRuntime(ctx, params) + if err != nil { + t.Fatal(err) + } + }) +} + +func TestRuntimeWithAuthzSchemaVerificationTransitive(t *testing.T) { + ctx := context.Background() + + fs := map[string]string{ + "test/authz.rego": `package system.authz + import rego.v1 + + default allow := false + + is_secret := input.identty == "secret" + + # even though "is_secret" is called via 2 paths, there should be only one resulting error + # 1-step dependency + allow if { + is_secret + } + + # 2-step dependency + allow if { + allow2 + } + + allow2 if { + is_secret + }`, + } + + test.WithTempFS(fs, func(rootDir string) { + rootDir = filepath.Join(rootDir, "test") + + params := NewParams() + params.Paths = []string{rootDir} + params.Authorization = server.AuthorizationBasic + + _, err := NewRuntime(ctx, params) + if err == nil { + t.Fatal("Expected error but got nil") + } + + if !strings.Contains(err.Error(), "undefined ref: input.identty") { + t.Errorf("Expected error \"%v\" not found", "undefined ref: input.identty") + } + }) +} + +func TestCheckAuthIneffective(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Millisecond) + defer cancel() // NOTE(sr): The timeout will have been reached by the time `done` is closed. + + params := NewParams() + params.Authentication = server.AuthenticationToken + params.Authorization = server.AuthorizationOff + + logger := logging.New() + stdout := bytes.NewBuffer(nil) + logger.SetOutput(stdout) + + params.Logger = logger + params.Addrs = &[]string{"localhost:0"} + params.GracefulShutdownPeriod = 1 + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + done := make(chan struct{}) + go func() { + rt.StartServer(ctx) + close(done) + }() + <-done + + expected := "Token authentication enabled without authorization. Authentication will be ineffective. See https://www.openpolicyagent.org/docs/latest/security/#authentication-and-authorization for more information." + if !strings.Contains(stdout.String(), expected) { + t.Fatalf("Expected output to contain: \"%v\" but got \"%v\"", expected, stdout.String()) + } + +} + +func TestServerInitialized(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Millisecond) + defer cancel() // NOTE(sr): The timeout will have been reached by the time `done` is closed. + var output bytes.Buffer + + params := NewParams() + params.Output = &output + params.Addrs = &[]string{"localhost:0"} + params.GracefulShutdownPeriod = 1 + params.Logger = logging.NewNoOpLogger() + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + initChannel := rt.Manager.ServerInitializedChannel() + done := make(chan struct{}) + go func() { + rt.StartServer(ctx) + close(done) + }() + <-done + select { + case <-initChannel: + return + default: + t.Fatal("expected ServerInitializedChannel to be closed") + } +} + +func TestServerInitializedWithRegoV1(t *testing.T) { + tests := []struct { + note string + v0Compatible bool + v1Compatible bool + files map[string]string + expErr string + }{ + { + note: "Rego v0, keywords not imported", + v0Compatible: true, + files: map[string]string{ + "policy.rego": `package test + p if { + input.x == 1 + } + `, + }, + expErr: "rego_parse_error: var cannot be used for rule name", + }, + { + note: "Rego v0, rego.v1 imported", + v0Compatible: true, + files: map[string]string{ + "policy.rego": `package test + import rego.v1 + p if { + input.x == 1 + } + `, + }, + }, + { + note: "Rego v0, future.keywords imported", + v0Compatible: true, + files: map[string]string{ + "policy.rego": `package test + import future.keywords.if + p if { + input.x == 1 + } + `, + }, + }, + { + note: "Rego v0, no keywords used", + v0Compatible: true, + files: map[string]string{ + "policy.rego": `package test + p { + input.x == 1 + } + `, + }, + }, + { + note: "Rego v1, keywords not imported", + v1Compatible: true, + files: map[string]string{ + "policy.rego": `package test + p if { + input.x == 1 + } + `, + }, + }, + { + note: "Rego v1, rego.v1 imported", + v1Compatible: true, + files: map[string]string{ + "policy.rego": `package test + import rego.v1 + p if { + input.x == 1 + } + `, + }, + }, + { + note: "Rego v1, future.keywords imported", + v1Compatible: true, + files: map[string]string{ + "policy.rego": `package test + import future.keywords.if + p if { + input.x == 1 + } + `, + }, + }, + { + note: "Rego v1, no keywords used", + v1Compatible: true, + files: map[string]string{ + "policy.rego": `package test + p { + input.x == 1 + } + `, + }, + expErr: "rego_parse_error: `if` keyword is required before rule body", + }, + } + + bundle := []bool{false, true} + + for _, tc := range tests { + for _, b := range bundle { + t.Run(fmt.Sprintf("%s; bundle=%v", tc.note, b), func(t *testing.T) { + test.WithTempFS(tc.files, func(root string) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Millisecond) + defer cancel() + var output bytes.Buffer + + params := NewParams() + params.Output = &output + params.Paths = []string{root} + params.BundleMode = b + params.Addrs = &[]string{"localhost:0"} + params.GracefulShutdownPeriod = 1 + params.Logger = logging.NewNoOpLogger() + params.V0Compatible = tc.v0Compatible + params.V1Compatible = tc.v1Compatible + + rt, err := NewRuntime(ctx, params) + + if tc.expErr != "" { + if err == nil { + t.Fatal("Expected error but got nil") + } + if !strings.Contains(err.Error(), tc.expErr) { + t.Fatalf("Expected error:\n\n%v\n\ngot:\n\n%v", tc.expErr, err.Error()) + } + } else { + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + initChannel := rt.Manager.ServerInitializedChannel() + done := make(chan struct{}) + go func() { + rt.StartServer(ctx) + close(done) + }() + <-done + select { + case <-initChannel: + return + default: + t.Fatal("expected ServerInitializedChannel to be closed") + } + } + }) + }) + } + } +} + +func TestServerInitializedWithBundleRegoVersion(t *testing.T) { + tests := []struct { + note string + files map[string]string + expErr string + }{ + { + note: "v0.x bundle, keywords not imported", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test + p if { + input.x == 1 + } + `, + }, + expErr: "rego_parse_error: var cannot be used for rule name", + }, + { + note: "v0.x bundle, rego.v1 imported", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test + import rego.v1 + p if { + input.x == 1 + } + `, + }, + }, + { + note: "v0.x bundle, future.keywords imported", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test + import future.keywords.if + p if { + input.x == 1 + } + `, + }, + }, + { + note: "v0.x bundle, no keywords used", + files: map[string]string{ + ".manifest": `{"rego_version": 0}`, + "policy.rego": `package test + p { + input.x == 1 + } + `, + }, + }, + { + note: "v0 bundle, v1 per-file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } + }`, + "policy1.rego": `package test + p[1] { + input.x == 1 + } + `, + "policy2.rego": `package test + q contains 2 if { + input.x == 1 + } + `, + }, + }, + { + note: "v0 bundle, v1 per-file override (glob)", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/bar/*.rego": 1 + } + }`, + "foo/policy1.rego": `package test + p[1] { + input.x == 1 + } + `, + "bar/policy2.rego": `package test + q contains 2 if { + input.x == 1 + } + `, + }, + }, + { + note: "v0 bundle, v1 per-file override, incompatible", + files: map[string]string{ + ".manifest": `{ + "rego_version": 0, + "file_rego_versions": { + "/policy2.rego": 1 + } + }`, + "policy1.rego": `package test + p[1] { + input.x == 1 + } + `, + "policy2.rego": `package test + q[2] { + input.x == 1 + } + `, + }, + expErr: "rego_parse_error", + }, + + { + note: "v1.0 bundle, keywords not imported", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test + p if { + input.x == 1 + } + `, + }, + }, + { + note: "v1.0 bundle, rego.v1 imported", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test + import rego.v1 + p if { + input.x == 1 + } + `, + }, + }, + { + note: "v1.0 bundle, future.keywords imported", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test + import future.keywords.if + p if { + input.x == 1 + } + `, + }, + }, + { + note: "v1.0 bundle, no keywords used", + files: map[string]string{ + ".manifest": `{"rego_version": 1}`, + "policy.rego": `package test + p { + input.x == 1 + } + `, + }, + expErr: "rego_parse_error: `if` keyword is required before rule body", + }, + { + note: "v1 bundle, v0 per-file override", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } + }`, + "policy1.rego": `package test + p[1] { + input.x == 1 + } + `, + "policy2.rego": `package test + q contains 2 if { + input.x == 1 + } + `, + }, + }, + { + note: "v1 bundle, v0 per-file override (glob)", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/foo/*.rego": 0 + } + }`, + "foo/policy1.rego": `package test + p[1] { + input.x == 1 + } + `, + "bar/policy2.rego": `package test + q contains 2 if { + input.x == 1 + } + `, + }, + }, + { + note: "v1 bundle, v0 per-file override, incompatible", + files: map[string]string{ + ".manifest": `{ + "rego_version": 1, + "file_rego_versions": { + "/policy1.rego": 0 + } + }`, + "policy1.rego": `package test + p contains 1 if { + input.x == 1 + } + `, + "policy2.rego": `package test + q contains 2 if { + input.x == 1 + } + `, + }, + expErr: "rego_parse_error", + }, + } + + bundleTypeCases := []struct { + note string + tar bool + }{ + { + "bundle dir", false, + }, + { + "bundle tar", true, + }, + } + + for _, bundleType := range bundleTypeCases { + for _, tc := range tests { + t.Run(fmt.Sprintf("%s, %s", bundleType.note, tc.note), func(t *testing.T) { + files := map[string]string{} + if bundleType.tar { + files["bundle.tar.gz"] = "" + } else { + maps.Copy(files, tc.files) + } + + test.WithTempFS(files, func(root string) { + p := root + if bundleType.tar { + p = filepath.Join(root, "bundle.tar.gz") + files := make([][2]string, 0, len(tc.files)) + for k, v := range tc.files { + files = append(files, [2]string{k, v}) + } + buf := archive.MustWriteTarGz(files) + bf, err := os.Create(p) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + _, err = bf.Write(buf.Bytes()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Millisecond) + defer cancel() + var output bytes.Buffer + + params := NewParams() + params.Output = &output + params.Paths = []string{p} + params.BundleMode = true + params.Addrs = &[]string{"localhost:0"} + params.GracefulShutdownPeriod = 1 + params.Logger = logging.NewNoOpLogger() + + rt, err := NewRuntime(ctx, params) + + if tc.expErr != "" { + if err == nil { + t.Fatal("Expected error but got nil") + } + if !strings.Contains(err.Error(), tc.expErr) { + t.Fatalf("Expected error:\n\n%v\n\ngot:\n\n%v", tc.expErr, err.Error()) + } + } else { + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + initChannel := rt.Manager.ServerInitializedChannel() + done := make(chan struct{}) + go func() { + rt.StartServer(ctx) + close(done) + }() + <-done + select { + case <-initChannel: + return + default: + t.Fatal("expected ServerInitializedChannel to be closed") + } + } + }) + }) + } + } +} + +func TestGracefulTracerShutdown(t *testing.T) { + fs := map[string]string{ + "/config.yaml": `{"distributed_tracing": {"type": "grpc"}}`, + } + + test.WithTempFS(fs, func(testDirRoot string) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Millisecond) + defer cancel() // NOTE(sr): The timeout will have been reached by the time `done` is closed. + + logger := testLog.New() + + params := NewParams() + params.ConfigFile = filepath.Join(testDirRoot, "config.yaml") + params.Addrs = &[]string{"localhost:0"} + params.GracefulShutdownPeriod = 1 + params.Logger = logger + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if rt.traceExporter == nil { + t.Fatal("traceExporter should not be nil") + } + + done := make(chan struct{}) + go func() { + rt.StartServer(ctx) + close(done) + }() + <-done + + expected := "Failed to shutdown OpenTelemetry trace exporter gracefully." + if strings.Contains(logger.Entries()[0].Message, expected) { + t.Fatalf("Expected no output containing: \"%v\"", expected) + } + }) +} + +func TestUrlPathToConfigOverride(t *testing.T) { + params := NewParams() + params.Paths = []string{"https://www.example.com/bundles/bundle.tar.gz"} + ctx := context.Background() + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatal(err) + } + + var serviceConf map[string]any + if err = json.Unmarshal(rt.Manager.Config.Services, &serviceConf); err != nil { + t.Fatal(err) + } + + cliService, ok := serviceConf["cli1"].(map[string]any) + if !ok { + t.Fatal("excpected service configuration for 'cli1' service") + } + + if cliService["url"] != "https://www.example.com" { + t.Error("expected cli1 service url value: 'https://www.example.com'") + } + + var bundleConf map[string]any + if err = json.Unmarshal(rt.Manager.Config.Bundles, &bundleConf); err != nil { + t.Fatal(err) + } + + cliBundle, ok := bundleConf["cli1"].(map[string]any) + if !ok { + t.Fatal("excpected bundle configuration for 'cli1' bundle") + } + + if cliBundle["service"] != "cli1" { + t.Error("expected cli1 bundle service value: 'cli1'") + } + + if cliBundle["resource"] != "/bundles/bundle.tar.gz" { + t.Error("expected cli1 bundle resource value: 'bundles/bundle.tar.gz'") + } + + if cliBundle["persist"] != true { + t.Error("expected cli1 bundle persist value: true") + } +} + +func getTestServer(update any, statusCode int) (string, func()) { + mux := http.NewServeMux() + ts := httptest.NewServer(mux) + + mux.HandleFunc("/repos/open-policy-agent/opa/releases/latest", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(statusCode) + bs, _ := json.Marshal(update) + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(bs) // ignore error + }) + return ts.URL, ts.Close +} + +func testCheckOPAUpdate(t *testing.T, url string, expected *report.DataResponse) { + t.Helper() + t.Setenv("OPA_TELEMETRY_SERVICE_URL", url) + + ctx := context.Background() + rt := getTestRuntime(ctx, t, logging.NewNoOpLogger()) + result := rt.checkOPAUpdate(ctx) + + if !reflect.DeepEqual(result, expected) { + t.Fatalf("Expected output:\"%v\" but got: \"%v\"", expected, result) + } +} + +func testCheckOPAUpdateLoop(t *testing.T, url, expected string) { + t.Helper() + t.Setenv("OPA_TELEMETRY_SERVICE_URL", url) + + ctx := context.Background() + + logger := logging.New() + stdout := bytes.NewBuffer(nil) + logger.SetOutput(stdout) + logger.SetLevel(logging.Debug) + + rt := getTestRuntime(ctx, t, logger) + + done := make(chan struct{}) + go func() { + initial := time.Millisecond + later := initial + rt.checkOPAUpdateLoopDurations(ctx, done, initial, later) + }() + time.Sleep(2 * time.Millisecond) + done <- struct{}{} + + if !strings.Contains(stdout.String(), expected) { + t.Fatalf("Expected output to contain: \"%v\" but got \"%v\"", expected, stdout.String()) + } +} + +func getTestRuntime(ctx context.Context, t *testing.T, logger logging.Logger) *Runtime { + t.Helper() + + params := NewParams() + params.EnableVersionCheck = true + params.Logger = logger + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + return rt +} + +func TestAddrWarningMessage(t *testing.T) { + testCases := []struct { + name string + addrSetByUser bool + containsMsg bool + v0Compatible bool + }{ + {"NoWarningMessage", true, false, false}, + {"WarningMessage", false, true, true}, + {"V0Compatible", false, true, true}, + {"V0InCompatible", false, false, false}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Millisecond) + defer cancel() + + params := NewParams() + + logger := testLog.New() + logLevel := logging.Info + + params.Logger = logger + params.Addrs = &[]string{"localhost:8181"} + params.AddrSetByUser = tc.addrSetByUser + params.GracefulShutdownPeriod = 1 + params.V0Compatible = tc.v0Compatible + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + done := make(chan struct{}) + go func() { + rt.StartServer(ctx) + close(done) + }() + <-done + + warning := " OPA is running on a public (0.0.0.0) network interface. Unless you intend to expose OPA outside of the host, binding to the localhost interface (--addr localhost:8181) is recommended. See https://www.openpolicyagent.org/docs/latest/security/#interface-binding" + containsWarning := strings.Contains(logger.Entries()[0].Message, warning) + + if containsWarning != tc.containsMsg { + t.Fatal("Mismatch between OPA server displaying the interface warning message and user setting the server address") + } + + if logger.GetLevel() != logLevel { + t.Fatalf("Expected log level to be: \"%v\" but got \"%v\"", logLevel, logger.GetLevel()) + } + }) + } +} + +func TestRuntimeWithExplicitMetricConfiguration(t *testing.T) { + fs := map[string]string{ + "/config.yaml": `{"server": {"metrics": {"prom": {"http_request_duration_seconds": {"buckets": [0.1, 0.2, 0.3]}}}}}`, + } + + test.WithTempFS(fs, func(testDirRoot string) { + params := NewParams() + params.ConfigFile = filepath.Join(testDirRoot, "config.yaml") + + _, err := NewRuntime(context.Background(), params) + if err != nil { + t.Fatal(err.Error()) + } + }) +} + +func TestRuntimeWithExplicitBadMetricConfiguration(t *testing.T) { + fs := map[string]string{ + "/config.yaml": `{"server": {"metrics": {"prom": {"http_request_duration_seconds": {"buckets": "would-not-work"}}}}}`, + } + + test.WithTempFS(fs, func(testDirRoot string) { + params := NewParams() + params.ConfigFile = filepath.Join(testDirRoot, "config.yaml") + + _, err := NewRuntime(context.Background(), params) + if err == nil { + t.Fatalf("Expected error to be thrown on malformed metrics config") + } + + if !strings.HasPrefix(err.Error(), "server metrics configuration parse error") { + t.Fatalf("Expected specific error to be thrown on malformed metrics config") + } + }) +} + +func TestExtraDiscoveryOpts(t *testing.T) { + ctx := context.Background() + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/discovery.tar.gz", map[string]string{ + "main.rego": ` +package config + +plugins.foobar := {} +`, + }), + ) + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "discovery": { + "decision": "config", + "resource": "/bundles/discovery.tar.gz" + } + }`, server.URL()) + cfg := filepath.Join(t.TempDir(), "opa.json") + if err := os.WriteFile(cfg, []byte(config), 0x755); err != nil { + t.Fatalf("write config %s: %v", cfg, err) + } + + params := NewParams() + params.ConfigFile = cfg + params.Output = io.Discard + params.Addrs = &[]string{"localhost:0"} + params.GracefulShutdownPeriod = 1 + testLogger := testLog.New() + params.Logger = testLogger + params.ExtraDiscoveryOpts = []func(*discovery.Discovery){ + discovery.Factories(map[string]plugins.Factory{"foobar": &factory{}}), + } + + // To check that the ExtraDiscoveryOpts have had an effect, we'll start the + // runtime and trigger a discovery update. The config it'll receive has a + // plugin called "foobar", which it'll only know if the factories have been + // set properly. + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + disco := discovery.Lookup(rt.Manager) + if err := disco.Trigger(ctx); err != nil { + t.Errorf("trigger discovery: %v", err) + } + + if !test.Eventually(t, 5*time.Second, func() bool { + found := false + for _, e := range testLogger.Entries() { + t.Log(e.Message) + if e.Message == "Discovery update processed successfully." { + found = true + break + } + } + return found + }) { + t.Error("discovery failed, check logs") + } +} + +type factory struct{} + +func (f *factory) New(m *plugins.Manager, _ any) plugins.Plugin { + m.ExtraRoute("GET /v1/flusher", "v1/flusher", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte("hey\n")) + w.(http.Flusher).Flush() + time.Sleep(1 * time.Second) + _, _ = w.Write([]byte("there\n")) + }) + return f +} + +func (*factory) Validate(*plugins.Manager, []byte) (any, error) { + return nil, nil +} + +func (*factory) Start(context.Context) error { + return nil +} + +func (*factory) Stop(context.Context) { +} + +func (*factory) Reconfigure(context.Context, any) { +} + +// TestCustomHandlerFlusher ensures that a handler defined through a plugin +// can call Flush(), and that all the middlewares in between work in the +// expected way -- passing the Flush() along. It needs to be tested through +// the runtime package to cover all the layers of middlwares typically used +// in OPA run as server. +func TestCustomHandlerFlusher(t *testing.T) { + fact := &factory{} + ctx := context.Background() + spanExporter := tracetest.NewInMemoryExporter() + options := tracing.NewOptions( + otelhttp.WithTracerProvider(trace.NewTracerProvider(trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(spanExporter)))), + ) + + RegisterPlugin("test_custom_handler_flusher", fact) + + config := `plugins: + test_custom_handler_flusher: {} +` + cfg := filepath.Join(t.TempDir(), "opa.yml") + if err := os.WriteFile(cfg, []byte(config), 0x755); err != nil { + t.Fatalf("write config %s: %v", cfg, err) + } + + for _, tc := range []struct { + note string + otel tracing.Options + }{ + { + note: "with otel", + otel: options, + }, + { + note: "without otel", + }, + } { + t.Run(tc.note, func(t *testing.T) { + testLogger := testLog.New() + params := NewParams() + params.DistributedTracingOpts = tc.otel + params.Logger = testLogger + params.Addrs = &[]string{"localhost:0"} + params.ConfigFile = cfg + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + go rt.StartServer(ctx) + if !test.Eventually(t, 5*time.Second, func() bool { + found := false + for _, e := range testLogger.Entries() { + found = strings.Contains(e.Message, "Server initialized.") || found + } + return found + }) { + t.Fatal("Timed out waiting for server to start") + } + host := rt.Addrs()[0] + r, err := http.NewRequest(http.MethodGet, "http://"+host+"/v1/flusher", nil) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + start := time.Now() + resp, err := http.DefaultClient.Do(r) + if err != nil { + t.Fatal("expected no error, got", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("status %d (want 200)", resp.StatusCode) + } + + defer resp.Body.Close() + + reader := bufio.NewReader(resp.Body) + { + line, err := reader.ReadString('\n') + if err != nil && !strings.Contains(err.Error(), "timeout") { + t.Fatalf("unexpected error reading: %v", err) + } + flushed := "hey\n" + if line != flushed { + t.Errorf("expected flushed line %q, got %q", flushed, line) + } + if dur := time.Since(start); dur > 100*time.Millisecond { // we're very gracious here, giving it 100ms leeway + t.Error("first line came too late (flush hasn't happened)", dur.String()) + } + } + { + line, err := reader.ReadString('\n') + if err != nil && !strings.Contains(err.Error(), "timeout") { + t.Fatalf("unexpected error reading: %v", err) + } + rest := "there\n" + if line != rest { + t.Errorf("expected flushed line %q, got %q", rest, line) + } + } + + t.Log(time.Since(start).String()) + for _, e := range testLogger.Entries() { + t.Log(e.Message) + } + }) + } +} + +type configHook struct { + some string +} + +func (ch *configHook) OnConfig(_ context.Context, c *config.Config) (*config.Config, error) { + ch.some = string(c.Extra["some"]) + return c, nil +} + +func TestConfigHookAndNonReplacedEnvVars(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Millisecond) + defer cancel() // NOTE(sr): The timeout will have been reached by the time `done` is closed. + testLogger := testLog.New() + + hk := configHook{} + + cf := filepath.Join(t.TempDir(), "opa.yaml") + if err := os.WriteFile(cf, []byte("some: ${thing}\n"), 0755); err != nil { + t.Fatal(err) + } + + params := NewParams() + params.Logger = testLogger + params.Addrs = &[]string{"localhost:0"} + params.Hooks = hooks.New(&hk) + params.ConfigFile = cf + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + initChannel := rt.Manager.ServerInitializedChannel() + done := make(chan struct{}) + go func() { + rt.StartServer(ctx) + close(done) + }() + <-done + select { + case <-initChannel: + return + default: + t.Fatal("expected ServerInitializedChannel to be closed") + } + + if act, exp := hk.some, "${thing}"; exp != act { + t.Errorf("Expected %q, got %q", exp, act) + } + + for _, e := range testLogger.Entries() { + t.Log(e.Message) + } +} + +type iqcHook struct { + c topdown_cache.InterQueryCache +} + +func (j *iqcHook) OnInterQueryCache(_ context.Context, c topdown_cache.InterQueryCache) error { + j.c = c + return nil +} + +type iqvcHook struct { + c topdown_cache.InterQueryValueCache +} + +func (j *iqvcHook) OnInterQueryValueCache(_ context.Context, c topdown_cache.InterQueryValueCache) error { + j.c = c + return nil +} + +func TestCacheHooksOnServer(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Millisecond) + defer cancel() // NOTE(sr): The timeout will have been reached by the time `done` is closed. + testLogger := testLog.New() + + h1 := iqcHook{} + h2 := iqvcHook{} + + params := NewParams() + params.Logger = testLogger + params.Addrs = &[]string{"localhost:0"} + params.Hooks = hooks.New(&h1, &h2) + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + initChannel := rt.Manager.ServerInitializedChannel() + done := make(chan struct{}) + go func() { + rt.StartServer(ctx) + close(done) + }() + <-done + select { + case <-initChannel: + return + default: + t.Fatal("expected ServerInitializedChannel to be closed") + } + if h1.c == nil { + t.Errorf("expected non-nil inter-query cache") + } + if h2.c == nil { + t.Errorf("expected non-nil inter-query value cache") + } + + for _, e := range testLogger.Entries() { + t.Log(e.Message) + } +} + +type fakeStore struct { + storage.Store +} + +func (f *fakeStore) Read(ctx context.Context, txn storage.Transaction, p storage.Path) (any, error) { + if slices.Contains(p, "foo") { + return map[string]any{"fake": p}, nil + } + return f.Store.Read(ctx, txn, p) +} + +func TestCustomStoreBuilder(t *testing.T) { + ctx := context.Background() + testLogger := testLog.New() + params := NewParams() + params.Logger = testLogger + params.Addrs = &[]string{"localhost:0"} + params.StoreBuilder = func(_ context.Context, logger logging.Logger, registerer prometheus_sdk.Registerer, config []byte, id string) (storage.Store, error) { + switch { + case logger == nil: + t.Fatal("logger empty") + case registerer == nil: + t.Fatal("registerer empty") + case config == nil: + t.Fatal("config empty") + case id == "": + t.Fatal("id empty") + } + return &fakeStore{inmem.New()}, nil + } + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + go rt.StartServer(ctx) + if !test.Eventually(t, 5*time.Second, func() bool { + found := false + for _, e := range testLogger.Entries() { + found = strings.Contains(e.Message, "Server initialized.") || found + } + return found + }) { + t.Fatal("Timed out waiting for server to start") + } + host := rt.Addrs()[0] + r, err := http.NewRequest(http.MethodGet, "http://"+host+"/v1/data/foo/bar", nil) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + resp, err := http.DefaultClient.Do(r) + if err != nil { + t.Fatal("expected no error, got", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("status %d (want 200)", resp.StatusCode) + } + + defer resp.Body.Close() + var payload struct { + Result map[string]any + } + if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil { + t.Fatalf("decode response: %v", err) + } + if !reflect.DeepEqual(map[string]any{"fake": []any{"foo", "bar"}}, payload.Result) { + t.Errorf("unexpected result: %v", payload.Result) + } +} + +func TestExtraMiddleware(t *testing.T) { + ctx := context.Background() + testLogger := testLog.New() + params := NewParams() + params.Logger = testLogger + params.Addrs = &[]string{"localhost:0"} + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + rt.Manager.ExtraMiddleware(func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := context.WithValue(r.Context(), "foo", "bar") //nolint:staticcheck,SA1029 // this is a simple example + next.ServeHTTP(w, r.WithContext(ctx)) + }) + }) + rt.Manager.ExtraRoute("GET /exp/foo", "exp/foo", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, r.Context().Value("foo")) + })) + go rt.StartServer(ctx) + if !test.Eventually(t, 5*time.Second, func() bool { + found := false + for _, e := range testLogger.Entries() { + found = strings.Contains(e.Message, "Server initialized.") || found + } + return found + }) { + t.Fatal("Timed out waiting for server to start") + } + host := rt.Addrs()[0] + r, err := http.NewRequest(http.MethodGet, "http://"+host+"/exp/foo", nil) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + resp, err := http.DefaultClient.Do(r) + if err != nil { + t.Fatal("expected no error, got", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("status %d (want 200)", resp.StatusCode) + } + + defer resp.Body.Close() + buf, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + if act, exp := string(buf), "bar"; !reflect.DeepEqual(act, exp) { + t.Errorf("got %v (%[1]T), want %v (%[2]T)", act, exp) + } +} + +func TestExtraAuthorizerRoutes(t *testing.T) { + ctx := context.Background() + testLogger := testLog.New() + params := NewParams() + params.Logger = testLogger + params.Addrs = &[]string{"localhost:0"} + params.Authorization = server.AuthorizationBasic + authzPolicy := []byte(` +package system.authz + +default allow := false # Reject requests by default. + +# Authorizer will deny request if it cannot see the parsed request body. +allow if { + input.method == "POST" + input.path == ["exp", "foo"] + input.body.example == "A" +}`) + + rt, err := NewRuntime(ctx, params) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + // Add a simple authz policy for POST /exp/foo. + err = storage.Txn(ctx, rt.Store, storage.WriteParams, func(txn storage.Transaction) error { + return rt.Store.UpsertPolicy(ctx, txn, "authz.rego", authzPolicy) + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + // Use a basic "echo" handler here that will reflect back the request body. + // If the authorizer blocks the request body from being parsed, we won't see it here on the request context. + rt.Manager.ExtraRoute("POST /exp/foo", "exp/foo", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if body, ok := authorizer.GetBodyOnContext(r.Context()); ok { + bs := string(util.MustMarshalJSON(body)) + fmt.Fprint(w, bs) + } else { + t.Fatal("No request body found on request context.") + } + })) + // Add POST /exp/foo to authorizer's list of routes that should have request bodies. + rt.Manager.ExtraAuthorizerRoute(func(method string, path []any) bool { + s0 := path[0].(string) + s1 := path[1].(string) + return method == "POST" && s0 == "exp" && s1 == "foo" + }) + go rt.StartServer(ctx) + if !test.Eventually(t, 5*time.Second, func() bool { + found := false + for _, e := range testLogger.Entries() { + found = strings.Contains(e.Message, "Server initialized.") || found + } + return found + }) { + t.Fatal("Timed out waiting for server to start") + } + host := rt.Addrs()[0] + r, err := http.NewRequest(http.MethodPost, "http://"+host+"/exp/foo", strings.NewReader(`{"example": "A"}`)) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + resp, err := http.DefaultClient.Do(r) + if err != nil { + t.Fatal("expected no error, got", err) + } + // If we get a 401 Not Authorized here, it means the authz policy could not + // validate the contents of the parsed request body for some reason. + if resp.StatusCode != http.StatusOK { + t.Fatalf("status %d (want 200)", resp.StatusCode) + } + defer resp.Body.Close() + buf, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + if act, exp := string(buf), `{"example":"A"}`; !reflect.DeepEqual(act, exp) { + t.Errorf("got %v (%[1]T), want %v (%[2]T)", act, exp) + } +} diff --git a/third_party/opa/v1/schemas/authorizationPolicy.json b/third_party/opa/v1/schemas/authorizationPolicy.json new file mode 100644 index 000000000000..639a3c303f10 --- /dev/null +++ b/third_party/opa/v1/schemas/authorizationPolicy.json @@ -0,0 +1,43 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for the OPA Authorization Policy Input document", + "type": "object", + "properties": { + "identity": { + "type": "string" + }, + "client_certificates": { + "type": "array", + "items": { + "type": "object" + } + }, + "method": { + "type": "string" + }, + "path": { + "type": "array", + "items": { + "type": "string" + } + }, + "params": { + "type": "object" + }, + "headers": { + "type": "object" + }, + "body": { + "type": "object" + } + }, + "required": [ + "identity", + "client_certificates", + "method", + "path", + "params", + "headers", + "body" + ] +} diff --git a/third_party/opa/v1/schemas/schemas.go b/third_party/opa/v1/schemas/schemas.go new file mode 100644 index 000000000000..82f387aee811 --- /dev/null +++ b/third_party/opa/v1/schemas/schemas.go @@ -0,0 +1,15 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package schemas + +import ( + "embed" +) + +// FS contains the known schemas for OPA's Authorization Policy etc. +// "authorizationPolicy.json" contains the input schema for OPA's Authorization Policy +// +//go:embed *.json +var FS embed.FS diff --git a/third_party/opa/v1/schemas/schemas_test.go b/third_party/opa/v1/schemas/schemas_test.go new file mode 100644 index 000000000000..0bae109d899f --- /dev/null +++ b/third_party/opa/v1/schemas/schemas_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package schemas_test + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/schemas" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestSchemasEmbedded(t *testing.T) { + ents, err := schemas.FS.ReadDir(".") + if err != nil { + t.Fatal(err) + } + if len(ents) == 0 { + t.Error("expected schemas to be present") + } + for _, ent := range ents { + cont, err := schemas.FS.ReadFile(ent.Name()) + if err != nil { + t.Errorf("file %v: %v", ent.Name(), err) + } + var x any + err = util.UnmarshalJSON(cont, &x) + if err != nil { + t.Errorf("file %v: %v", ent.Name(), err) + } + } +} diff --git a/third_party/opa/v1/sdk/RawMapper.go b/third_party/opa/v1/sdk/RawMapper.go new file mode 100644 index 000000000000..9663eb7d45c0 --- /dev/null +++ b/third_party/opa/v1/sdk/RawMapper.go @@ -0,0 +1,17 @@ +package sdk + +import ( + "github.com/open-policy-agent/opa/v1/rego" +) + +type RawMapper struct { +} + +func (*RawMapper) MapResults(pq *rego.PartialQueries) (any, error) { + + return pq, nil +} + +func (*RawMapper) ResultToJSON(results any) (any, error) { + return results, nil +} diff --git a/third_party/opa/v1/sdk/opa.go b/third_party/opa/v1/sdk/opa.go new file mode 100644 index 000000000000..92d75d0eb6d7 --- /dev/null +++ b/third_party/opa/v1/sdk/opa.go @@ -0,0 +1,761 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package sdk contains a high-level API for embedding OPA inside of Go programs. +package sdk + +import ( + "bytes" + "cmp" + "context" + "crypto/rand" + "fmt" + "maps" + "sync" + "time" + + "github.com/open-policy-agent/opa/internal/ref" + "github.com/open-policy-agent/opa/internal/runtime" + "github.com/open-policy-agent/opa/internal/uuid" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/hooks" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/plugins/discovery" + "github.com/open-policy-agent/opa/v1/plugins/logs" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/server" + "github.com/open-policy-agent/opa/v1/server/types" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/print" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/version" +) + +// OPA represents an instance of the policy engine. OPA can be started with +// several options that control configuration, logging, and lifecycle. +type OPA struct { + id string + state *state + mtx sync.Mutex + logger logging.Logger + console logging.Logger + plugins map[string]plugins.Factory + store storage.Store + hooks hooks.Hooks + config []byte + regoVersion ast.RegoVersion + managerOpts []func(*plugins.Manager) +} + +type state struct { + manager *plugins.Manager + interQueryBuiltinCache cache.InterQueryCache + interQueryBuiltinValueCache cache.InterQueryValueCache + queryCache *queryCache +} + +// New returns a new OPA object. This function should minimally be called with +// options that specify an OPA configuration file. +func New(ctx context.Context, nopts Options) (*OPA, error) { + defaultOptsMtx.Lock() + opts := Options{ + Plugins: make(map[string]plugins.Factory, len(nopts.Plugins)+len(defaultOptions.Plugins)), + } + opts.ID = cmp.Or(nopts.ID, defaultOptions.ID) + opts.Config = cmp.Or(nopts.Config, defaultOptions.Config) + opts.ConsoleLogger = cmp.Or(nopts.ConsoleLogger, defaultOptions.ConsoleLogger) + + opts.V1Compatible = nopts.V1Compatible + opts.V0Compatible = nopts.V0Compatible + opts.RegoVersion = cmp.Or(nopts.RegoVersion, defaultOptions.RegoVersion) + + opts.ManagerOpts = append(opts.ManagerOpts, defaultOptions.ManagerOpts...) + opts.ManagerOpts = append(opts.ManagerOpts, nopts.ManagerOpts...) + + opts.Logger = cmp.Or(nopts.Logger, defaultOptions.Logger) + hs := make([]hooks.Hook, 0, opts.Hooks.Len()+nopts.Hooks.Len()) + opts.Hooks.Each(func(h hooks.Hook) { + hs = append(hs, h) + }) + nopts.Hooks.Each(func(h hooks.Hook) { + hs = append(hs, h) + }) + opts.Hooks = hooks.New(hs...) + + maps.Copy(opts.Plugins, defaultOptions.Plugins) + maps.Copy(opts.Plugins, nopts.Plugins) + + opts.Store = cmp.Or(nopts.Store, defaultOptions.Store) + opts.Ready = cmp.Or(nopts.Ready, defaultOptions.Ready) + defaultOptsMtx.Unlock() + + var err error + + id := opts.ID + if id == "" { + id, err = uuid.New(rand.Reader) + if err != nil { + return nil, err + } + } + + if err := opts.init(); err != nil { + return nil, err + } + + opa := &OPA{ + id: id, + store: opts.Store, + hooks: opts.Hooks, + state: &state{ + queryCache: newQueryCache(), + }, + } + + opa.config = opts.config + opa.logger = opts.Logger + opa.console = opts.ConsoleLogger + opa.plugins = opts.Plugins + opa.managerOpts = opts.ManagerOpts + + opa.regoVersion = opts.regoVersion() + + return opa, opa.configure(ctx, opa.config, opts.Ready, opts.block) +} + +// Plugin returns the named plugin. If the plugin does not exist, this function +// returns nil. +func (opa *OPA) Plugin(name string) plugins.Plugin { + opa.mtx.Lock() + defer opa.mtx.Unlock() + return opa.state.manager.Plugin(name) +} + +// Configure updates the configuration of the OPA in-place. This function should +// be called in response to configuration updates in the environment. This +// function is atomic. If the configuration update cannot be successfully +// applied, the old configuration will remain intact. +func (opa *OPA) Configure(ctx context.Context, opts ConfigOptions) error { + + if err := opts.init(); err != nil { + return err + } + + // NOTE(tsandall): In future we could be more intelligent about + // re-configuration and avoid expensive background processing. + opa.mtx.Lock() + equal := bytes.Equal(opts.config, opa.config) + opa.mtx.Unlock() + + if equal { + close(opts.Ready) + return nil + } + + return opa.configure(ctx, opts.config, opts.Ready, opts.block) +} + +func (opa *OPA) configure(ctx context.Context, bs []byte, ready chan struct{}, block bool) error { + info, err := runtime.Term(runtime.Params{Config: opa.config}) + if err != nil { + return err + } + + opts := []func(*plugins.Manager){ + plugins.Info(info), + plugins.Logger(opa.logger), + plugins.ConsoleLogger(opa.console), + plugins.WithParserOptions(ast.ParserOptions{RegoVersion: opa.regoVersion}), + plugins.EnablePrintStatements(opa.logger.GetLevel() >= logging.Info), + plugins.PrintHook(loggingPrintHook{logger: opa.logger}), + plugins.WithHooks(opa.hooks), + } + opts = append(opts, opa.managerOpts...) + + // Plumb in storage for external bundle activation plugin, if registered with bundle.RegisterStore, + // unless the user has passed their own store already. + var store storage.Store + switch { + case opa.store != nil: + store = opa.store + case bundle.BundleExtStore != nil: + store = bundle.BundleExtStore() + } + manager, err := plugins.New( + bs, + opa.id, + store, + opts..., + ) + if err != nil { + return err + } + + manager.RegisterCompilerTrigger(func(storage.Transaction) { + opa.mtx.Lock() + opa.state.queryCache.Clear() + opa.mtx.Unlock() + }) + + manager.RegisterPluginStatusListener("sdk", func(status map[string]*plugins.Status) { + + select { + case <-ready: + return + default: + } + // NOTE(tsandall): we do not include a special case for the discovery + // plugin. If the discovery plugin is the only plugin and it goes ready, + // then OPA will be considered ready. The discovery plugin only goes ready + // _after_ it has successfully processed a discovery bundle. During + // discovery bundle processing, other plugins will register so their states + // will be accounted for. If a discovery bundle did not enable any other + // plugins (bundles, etc.) the OPA will still be operational. + for _, s := range status { + if s.State != plugins.StateOK { + return + } + } + + close(ready) + }) + + var bootConfig map[string]any + err = util.Unmarshal(opa.config, &bootConfig) + if err != nil { + return err + } + + d, err := discovery.New(manager, + discovery.Factories(opa.plugins), + discovery.Hooks(opa.hooks), + discovery.BootConfig(bootConfig), + ) + if err != nil { + return err + } + + manager.Register(discovery.Name, d) + + if err := manager.Start(ctx); err != nil { + return err + } + + if block { + select { + case <-ctx.Done(): + return ctx.Err() + case <-ready: + } + } + + opa.mtx.Lock() + defer opa.mtx.Unlock() + + // NOTE(tsandall): there is no return value from Stop() and it could block + // on async operations (e.g., decision log uploading) so defer the call to + // another goroutine. + // + // TODO(tsandall): if we need to block on operations like decision log + // uploading, perhaps we could rely on a manual trigger. + previousManager := opa.state.manager + go func() { + if previousManager != nil { + previousManager.Stop(ctx) + } + }() + + opa.state.manager = manager + opa.state.queryCache.Clear() + opa.state.interQueryBuiltinCache = cache.NewInterQueryCacheWithContext(ctx, manager.InterQueryBuiltinCacheConfig()) + opa.state.interQueryBuiltinValueCache = cache.NewInterQueryValueCache(ctx, manager.InterQueryBuiltinCacheConfig()) + opa.config = bs + + return nil +} + +// Stop closes the OPA. The OPA cannot be restarted. +func (opa *OPA) Stop(ctx context.Context) { + + opa.mtx.Lock() + mgr := opa.state.manager + opa.mtx.Unlock() + + if mgr != nil { + mgr.Stop(ctx) + } +} + +// Decision returns a named decision. This function is threadsafe. +func (opa *OPA) Decision(ctx context.Context, options DecisionOptions) (*DecisionResult, error) { + + record := server.Info{ + Timestamp: options.Now, + Path: options.Path, + Input: &options.Input, + NDBuiltinCache: &options.NDBCache, + Metrics: options.Metrics, + DecisionID: options.DecisionID, + } + + // Only use non-deterministic builtins cache if it's available. + var ndbc builtins.NDBCache + if options.NDBCache != nil { + if v, ok := options.NDBCache.(builtins.NDBCache); ok { + ndbc = v + } + } + + result, err := opa.executeTransaction( + ctx, + &record, + func(s state, result *DecisionResult) { + result.Result, result.Provenance, record.InputAST, record.Bundles, record.Error = evaluate(ctx, evalArgs{ + runtime: s.manager.Info, + printHook: s.manager.PrintHook(), + compiler: s.manager.GetCompiler(), + store: s.manager.Store, + queryCache: s.queryCache, + interQueryCache: s.interQueryBuiltinCache, + interQueryBuiltinValueCache: s.interQueryBuiltinValueCache, + ndbcache: ndbc, + txn: record.Txn, + now: record.Timestamp, + path: record.Path, + input: *record.Input, + m: record.Metrics, + strictBuiltinErrors: options.StrictBuiltinErrors, + tracer: options.Tracer, + profiler: options.Profiler, + instrument: options.Instrument, + }) + if record.Error == nil { + record.Results = &result.Result + } + }, + ) + if err != nil { + return nil, err + } + + return result, record.Error +} + +// DecisionOptions contains parameters for query evaluation. +type DecisionOptions struct { + Now time.Time // specifies wallclock time used for time.now_ns(), decision log timestamp, etc. + Path string // specifies name of policy decision to evaluate (e.g., example/allow) + Input any // specifies value of the input document to evaluate policy with + NDBCache any // specifies the non-deterministic builtins cache to use for evaluation. + StrictBuiltinErrors bool // treat built-in function errors as fatal + Tracer topdown.QueryTracer // specifies the tracer to use for evaluation, optional + Metrics metrics.Metrics // specifies the metrics to use for preparing and evaluation, optional + Profiler topdown.QueryTracer // specifies the profiler to use, optional + Instrument bool // if true, instrumentation will be enabled + DecisionID string // the identifier for this decision; if not set, a globally unique identifier will be generated +} + +// DecisionResult contains the output of query evaluation. +type DecisionResult struct { + ID string // provides the identifier for this decision (which is included in the decision log.) + Result any // provides the output of query evaluation. + Provenance types.ProvenanceV1 // wraps the bundle build/version information +} + +func (opa *OPA) executeTransaction(ctx context.Context, record *server.Info, work func(state, *DecisionResult)) (*DecisionResult, error) { + if record.Metrics == nil { + record.Metrics = metrics.New() + } + record.Metrics.Timer(metrics.SDKDecisionEval).Start() + + if record.DecisionID == "" { + id, err := uuid.New(rand.Reader) + if err != nil { + return nil, err + } + record.DecisionID = id + } + + result := &DecisionResult{ID: record.DecisionID} + + opa.mtx.Lock() + s := *opa.state + opa.mtx.Unlock() + + if record.Timestamp.IsZero() { + record.Timestamp = time.Now().UTC() + } + + if record.Path == "" { + record.Path = *s.manager.Config.DefaultDecision + } + + record.Txn, record.Error = s.manager.Store.NewTransaction(ctx, storage.TransactionParams{}) + + if record.Error == nil { + defer s.manager.Store.Abort(ctx, record.Txn) + work(s, result) + } + + record.Metrics.Timer(metrics.SDKDecisionEval).Stop() + + if logger := logs.Lookup(s.manager); logger != nil { + // Decision log masking requires the event object to be a map[string]any, + // or a []any, and all internal objects referenced in the mask to be + // similarly generic. Convert the input AST back into a JSON-representation to + // ensure decision logging will work if the input Go type does not fit these requirements. + if record.InputAST != nil { + asJSON, err := ast.JSON(record.InputAST) + if err != nil { + return nil, err + } + *record.Input = asJSON + } + if err := logger.Log(ctx, record); err != nil { + return result, fmt.Errorf("decision log: %w", err) + } + } + return result, nil +} + +// Partial returns a named decision. This function is threadsafe. +// Note(philipc): The NDBCache is unused here, because non-deterministic +// builtins are not run during partial evaluation. +func (opa *OPA) Partial(ctx context.Context, options PartialOptions) (*PartialResult, error) { + + if options.Mapper == nil { + options.Mapper = &RawMapper{} + } + + record := server.Info{ + Timestamp: options.Now, + Input: &options.Input, + Query: options.Query, + Metrics: options.Metrics, + DecisionID: options.DecisionID, + } + + var provenance types.ProvenanceV1 + + var pq *rego.PartialQueries + decision, err := opa.executeTransaction( + ctx, + &record, + func(s state, result *DecisionResult) { + pq, provenance, record.InputAST, record.Bundles, record.Error = partial(ctx, partialEvalArgs{ + runtime: s.manager.Info, + printHook: s.manager.PrintHook(), + compiler: s.manager.GetCompiler(), + store: s.manager.Store, + txn: record.Txn, + now: record.Timestamp, + query: record.Query, + unknowns: options.Unknowns, + input: *record.Input, + m: record.Metrics, + strictBuiltinErrors: options.StrictBuiltinErrors, + tracer: options.Tracer, + profiler: options.Profiler, + instrument: options.Instrument, + }) + if record.Error == nil { + result.Result, record.Error = options.Mapper.MapResults(pq) + var pqAst any + if record.Error == nil { + var mappedResults any + mappedResults, record.Error = options.Mapper.ResultToJSON(result.Result) + record.MappedResults = &mappedResults + pqAst = pq + record.Results = &pqAst + } + } + }, + ) + if err != nil { + return nil, err + } + + return &PartialResult{ + ID: decision.ID, + Result: decision.Result, + AST: pq, + Provenance: provenance, + }, record.Error +} + +type PartialQueryMapper interface { + // The first interface being returned is the type that will be used for further processing + MapResults(pq *rego.PartialQueries) (any, error) + // This should be able to take the Result object from MapResults and return a type that can be logged as JSON + ResultToJSON(result any) (any, error) +} + +// PartialOptions contains parameters for partial query evaluation. +type PartialOptions struct { + Now time.Time // specifies wallclock time used for time.now_ns(), decision log timestamp, etc. + Input any // specifies value of the input document to evaluate policy with + Query string // specifies the query to be partially evaluated + Unknowns []string // specifies the unknown elements of the policy + Mapper PartialQueryMapper // specifies the mapper to use when processing results + StrictBuiltinErrors bool // treat built-in function errors as fatal + Tracer topdown.QueryTracer // specifies the tracer to use for evaluation, optional + Metrics metrics.Metrics // specifies the metrics to use for preparing and evaluation, optional + Profiler topdown.QueryTracer // specifies the profiler to use, optional + Instrument bool // if true, instrumentation will be enabled + DecisionID string // the identifier for this decision; if not set, a globally unique identifier will be generated +} + +type PartialResult struct { + ID string // decision ID + Result any // mapped result + AST *rego.PartialQueries // raw result + Provenance types.ProvenanceV1 // wraps the bundle build/version information +} + +// Error represents an internal error in the SDK. +type Error struct { + Code string `json:"code"` + Message string `json:"message,omitempty"` +} + +func (err *Error) Error() string { + return fmt.Sprintf("%v: %v", err.Code, err.Message) +} + +const ( + // UndefinedErr indicates that the queried decision was undefined. + UndefinedErr = "opa_undefined_error" +) + +func undefinedDecisionErr(path string) *Error { + return &Error{ + Code: UndefinedErr, + Message: fmt.Sprintf("%v decision was undefined", path), + } +} + +// IsUndefinedErr returns true of the err represents an undefined decision error. +func IsUndefinedErr(err error) bool { + actual, ok := err.(*Error) + return ok && actual.Code == UndefinedErr +} + +type evalArgs struct { + runtime *ast.Term + printHook print.Hook + compiler *ast.Compiler + store storage.Store + txn storage.Transaction + queryCache *queryCache + interQueryCache cache.InterQueryCache + interQueryBuiltinValueCache cache.InterQueryValueCache + now time.Time + path string + input any + ndbcache builtins.NDBCache + m metrics.Metrics + strictBuiltinErrors bool + tracer topdown.QueryTracer + profiler topdown.QueryTracer + instrument bool +} + +func evaluate(ctx context.Context, args evalArgs) (any, types.ProvenanceV1, ast.Value, map[string]server.BundleInfo, error) { + + provenance := types.ProvenanceV1{ + Version: version.Version, + Vcs: version.Vcs, + Timestamp: version.Timestamp, + Hostname: version.Hostname, + Bundles: make(map[string]types.ProvenanceBundleV1), + } + bundles, err := bundles(ctx, args.store, args.txn) + if err != nil { + return nil, provenance, nil, nil, err + } + for b, info := range bundles { + provenance.Bundles[b] = types.ProvenanceBundleV1{ + Revision: info.Revision, + } + } + + r, err := ref.ParseDataPath(args.path) + if err != nil { + return nil, provenance, nil, bundles, err + } + + pq, err := args.queryCache.Get(r.String(), func(query string) (*rego.PreparedEvalQuery, error) { + pq, err := rego.New( + rego.Time(args.now), + rego.Metrics(args.m), + rego.Query(query), + rego.Compiler(args.compiler), + rego.Store(args.store), + rego.Transaction(args.txn), + rego.PrintHook(args.printHook), + rego.StrictBuiltinErrors(args.strictBuiltinErrors), + rego.Instrument(args.instrument), + rego.Runtime(args.runtime)).PrepareForEval(ctx) + if err != nil { + return nil, err + } + return &pq, err + }) + if err != nil { + return nil, provenance, nil, bundles, err + } + + inputAST, err := ast.InterfaceToValue(args.input) + if err != nil { + return nil, provenance, nil, bundles, err + } + + rs, err := pq.Eval( + ctx, + rego.EvalTime(args.now), + rego.EvalParsedInput(inputAST), + rego.EvalTransaction(args.txn), + rego.EvalInterQueryBuiltinCache(args.interQueryCache), + rego.EvalInterQueryBuiltinValueCache(args.interQueryBuiltinValueCache), + rego.EvalNDBuiltinCache(args.ndbcache), + rego.EvalQueryTracer(args.tracer), + rego.EvalMetrics(args.m), + rego.EvalQueryTracer(args.profiler), + rego.EvalInstrument(args.instrument), + ) + if err != nil { + return nil, provenance, inputAST, bundles, err + } else if len(rs) == 0 { + return nil, provenance, inputAST, bundles, undefinedDecisionErr(args.path) + } + + return rs[0].Expressions[0].Value, provenance, inputAST, bundles, nil +} + +type partialEvalArgs struct { + runtime *ast.Term + compiler *ast.Compiler + printHook print.Hook + store storage.Store + txn storage.Transaction + unknowns []string + query string + now time.Time + input any + m metrics.Metrics + strictBuiltinErrors bool + tracer topdown.QueryTracer + profiler topdown.QueryTracer + instrument bool +} + +func partial(ctx context.Context, args partialEvalArgs) (*rego.PartialQueries, types.ProvenanceV1, ast.Value, map[string]server.BundleInfo, error) { + + provenance := types.ProvenanceV1{ + Version: version.Version, + Bundles: make(map[string]types.ProvenanceBundleV1), + } + + bundles, err := bundles(ctx, args.store, args.txn) + if err != nil { + return nil, provenance, nil, nil, err + } + for b, info := range bundles { + provenance.Bundles[b] = types.ProvenanceBundleV1{ + Revision: info.Revision, + } + } + + inputAST, err := ast.InterfaceToValue(args.input) + if err != nil { + return nil, provenance, nil, bundles, err + } + re := rego.New( + rego.Time(args.now), + rego.Metrics(args.m), + rego.Store(args.store), + rego.Compiler(args.compiler), + rego.Transaction(args.txn), + rego.Runtime(args.runtime), + rego.Input(args.input), + rego.Query(args.query), + rego.Unknowns(args.unknowns), + rego.PrintHook(args.printHook), + rego.StrictBuiltinErrors(args.strictBuiltinErrors), + rego.QueryTracer(args.tracer), + rego.QueryTracer(args.profiler), + rego.Instrument(args.instrument), + ) + + pq, err := re.Partial(ctx) + if err != nil { + return nil, provenance, nil, bundles, err + } + return pq, provenance, inputAST, bundles, err +} + +type queryCache struct { + sync.Mutex + cache map[string]*rego.PreparedEvalQuery +} + +func newQueryCache() *queryCache { + return &queryCache{cache: map[string]*rego.PreparedEvalQuery{}} +} + +func (qc *queryCache) Get(key string, orElse func(string) (*rego.PreparedEvalQuery, error)) (*rego.PreparedEvalQuery, error) { + qc.Lock() + defer qc.Unlock() + + result, ok := qc.cache[key] + if ok { + return result, nil + } + + result, err := orElse(key) + if err != nil { + return nil, err + } + + qc.cache[key] = result + return result, nil +} + +func (qc *queryCache) Clear() { + qc.Lock() + defer qc.Unlock() + + qc.cache = make(map[string]*rego.PreparedEvalQuery) +} + +func bundles(ctx context.Context, store storage.Store, txn storage.Transaction) (map[string]server.BundleInfo, error) { + bundles := map[string]server.BundleInfo{} + names, err := bundle.ReadBundleNamesFromStore(ctx, store, txn) + if err != nil && !storage.IsNotFound(err) { + return nil, fmt.Errorf("failed to read bundle names: %w", err) + } + for _, name := range names { + r, err := bundle.ReadBundleRevisionFromStore(ctx, store, txn, name) + if err != nil { + return nil, fmt.Errorf("failed to read bundle revisions: %w", err) + } + bundles[name] = server.BundleInfo{Revision: r} + } + return bundles, nil +} + +type loggingPrintHook struct { + logger logging.Logger +} + +func (h loggingPrintHook) Print(pctx print.Context, msg string) error { + h.logger.WithFields(map[string]any{"line": pctx.Location.String()}).Info(msg) + return nil +} diff --git a/third_party/opa/v1/sdk/opa_internal_test.go b/third_party/opa/v1/sdk/opa_internal_test.go new file mode 100644 index 000000000000..310c49f47535 --- /dev/null +++ b/third_party/opa/v1/sdk/opa_internal_test.go @@ -0,0 +1,58 @@ +package sdk + +import ( + "context" + "fmt" + "reflect" + "strings" + "testing" + + sdktest "github.com/open-policy-agent/opa/v1/sdk/test" +) + +func TestDefaultOptions(t *testing.T) { + ctx := context.Background() + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +loaded := true +`, + }), + ) + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opts := defaultOptions + opts.Config = strings.NewReader(config) + SetDefaultOptions(opts) + + t.Cleanup(func() { SetDefaultOptions(defaultOptions) }) + + opa, err := New(ctx, Options{}) + if err != nil { + t.Fatal(err) + } + defer opa.Stop(ctx) + + exp := true + + if result, err := opa.Decision(ctx, DecisionOptions{Path: "/system/loaded"}); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(result.Result, exp) { + t.Fatalf("expected %v but got %v", exp, result.Result) + } +} diff --git a/third_party/opa/v1/sdk/opa_test.go b/third_party/opa/v1/sdk/opa_test.go new file mode 100644 index 000000000000..7afcd8c34020 --- /dev/null +++ b/third_party/opa/v1/sdk/opa_test.go @@ -0,0 +1,3022 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package sdk_test + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + promdto "github.com/prometheus/client_model/go" + + "github.com/fortytw2/leaktest" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/config" + "github.com/open-policy-agent/opa/v1/hooks" + "github.com/open-policy-agent/opa/v1/logging" + loggingtest "github.com/open-policy-agent/opa/v1/logging/test" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/profiler" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/sdk" + sdktest "github.com/open-policy-agent/opa/v1/sdk/test" + "github.com/open-policy-agent/opa/v1/server/types" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/lineage" + "github.com/open-policy-agent/opa/v1/util/test" + "github.com/open-policy-agent/opa/v1/version" +) + +func TestDefaultRegoVersion(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.RawBundles(true), + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + // v1 module + "main.rego": ` +package system + +main if { + "a" in p +} + +p contains x if { + x = "a" +} + +str = "foo" + +loopback = input +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{}); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(bool); !ok || !decision { + t.Fatal("expected true but got:", decision, ok) + } + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{Path: "/system/str"}); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(string); !ok || decision != "foo" { + t.Fatal(`expected "foo" but got:`, decision) + } + + exp := map[string]any{"foo": "bar"} + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{Path: "/system/loopback", Input: map[string]any{"foo": "bar"}}); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(result.Result, exp) { + t.Fatalf("expected %v but got %v", exp, result.Result) + } +} + +// Plugin creates an empty plugin to test plugin initialization and shutdown +type plugin struct { + manager *plugins.Manager + shutdown time.Duration // to simulate a shutdown that takes some time +} + +type factory struct { + shutdown time.Duration +} + +func (p *plugin) Start(context.Context) error { + p.manager.UpdatePluginStatus("test_plugin", &plugins.Status{State: plugins.StateOK}) + return nil +} + +func (p *plugin) Stop(ctx context.Context) { + select { + case <-ctx.Done(): + case <-time.After(p.shutdown): + } +} + +func (*plugin) Reconfigure(context.Context, any) { +} + +func (f factory) New(manager *plugins.Manager, _ any) plugins.Plugin { + return &plugin{ + manager: manager, + shutdown: f.shutdown, + } +} + +func (factory) Validate(*plugins.Manager, []byte) (any, error) { + return nil, nil +} + +func TestPlugins(t *testing.T) { + ctx := context.Background() + config := `{ + "plugins": { + "test_plugin": {} + } + }` + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + Plugins: map[string]plugins.Factory{ + "test_plugin": factory{}, + }, + }) + if err != nil { + t.Fatal(err) + } + opa.Stop(ctx) +} + +func TestHookOnConfig(t *testing.T) { + ctx := context.Background() + + // We're setting up two hooks that smuggle in some new labels, and hold on + // to their config. + // NOTE: Hook ordering isn't guaranteed, so we cannot rely on their invocation + // in sequence. + th0 := &testhook{k: "foo", v: "baz"} + th1 := &testhook{k: "fox", v: "quz"} + opa, err := sdk.New(ctx, sdk.Options{ + ID: "sdk-id-0", + Config: strings.NewReader(`{}`), + Hooks: hooks.New(th0, th1), + }) + if err != nil { + t.Fatal(err) + } + defer opa.Stop(ctx) + + exp := &config.Config{ + Labels: map[string]string{ + "id": "sdk-id-0", + "version": version.Version, + "foo": "baz", + "fox": "quz", + }, + } + act := th1.c // doesn't matter which hook, they only mutate the config via its pointer + if diff := cmp.Diff(exp, act, cmpopts.IgnoreFields(config.Config{}, "DefaultDecision", "DefaultAuthorizationDecision")); diff != "" { + t.Errorf("unexpected config: (-want, +got):\n%s", diff) + } +} + +func TestHookOnConfigDiscovery(t *testing.T) { + ctx := context.Background() + th0 := &testhook{k: "foo", v: "baz"} + th1 := &testhook{k: "fox", v: "quz"} + disco := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/status" { + return // ignore status plugin POSTs + } + http.FileServer(http.Dir("testdata")).ServeHTTP(w, r) + })) + opa, err := sdk.New(ctx, sdk.Options{ + ID: "sdk-id-0", + Config: strings.NewReader(fmt.Sprintf(`{ +"discovery": {"service":"disco", "resource": "disco.tar.gz"}, +"services": [{"name":"disco", "url": "%[1]s"}] + }`, disco.URL)), + Hooks: hooks.New(th0, th1), + Logger: logging.New(), + Plugins: map[string]plugins.Factory{ + "test_plugin": factory{}, + }, + }) + if err != nil { + t.Fatal(err) + } + defer opa.Stop(ctx) + + exp := &config.Config{ + Labels: map[string]string{ + "id": "sdk-id-0", + "version": version.Version, + "foo": "baz", + "fox": "quz", + }, + Plugins: map[string]json.RawMessage{"test_plugin": json.RawMessage("{}")}, + Discovery: json.RawMessage(`{"service":"disco", "resource": "disco.tar.gz"}`), + } + act := th1.c // doesn't matter which hook, they only mutate the config via its pointer + if diff := cmp.Diff(exp, act, cmpopts.IgnoreFields(config.Config{}, "DefaultDecision", "DefaultAuthorizationDecision")); diff != "" { + t.Errorf("unexpected config: (-want, +got):\n%s", diff) + } +} + +type testhook struct { + k, v string + c *config.Config +} + +func (h *testhook) OnConfig(_ context.Context, c *config.Config) (*config.Config, error) { + c.Labels[h.k] = h.v + h.c = c + return c, nil +} + +func (h *testhook) OnConfigDiscovery(_ context.Context, c *config.Config) (*config.Config, error) { + c.Labels[h.k] = h.v + h.c = c + return c, nil +} + +func TestPluginPanic(t *testing.T) { + ctx := context.Background() + + opa, err := sdk.New(ctx, sdk.Options{}) + if err != nil { + t.Fatal(err) + } + opa.Stop(ctx) +} + +func TestSDKConfigurableID(t *testing.T) { + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = time.now_ns() +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL()) + + testLogger := loggingtest.New() + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + ConsoleLogger: testLogger, + ID: "164031de-e511-11ec-8fea-0242ac120002"}) + + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + if _, err := opa.Decision(ctx, sdk.DecisionOptions{ + Now: time.Unix(0, 1619868194450288000).UTC(), + }); err != nil { + t.Fatal(err) + } + + entries := testLogger.Entries() + + if entries[0].Fields["labels"].(map[string]any)["id"] != "164031de-e511-11ec-8fea-0242ac120002" { + t.Fatalf("expected %v but got %v", "164031de-e511-11ec-8fea-0242ac120002", entries[0].Fields["labels"].(map[string]any)["id"]) + } + +} + +func TestDecision(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = true + +str = "foo" + +loopback = input +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{}); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(bool); !ok || !decision { + t.Fatal("expected true but got:", decision, ok) + } + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{Path: "/system/str"}); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(string); !ok || decision != "foo" { + t.Fatal(`expected "foo" but got:`, decision) + } + + exp := map[string]any{"foo": "bar"} + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{Path: "/system/loopback", Input: map[string]any{"foo": "bar"}}); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(result.Result, exp) { + t.Fatalf("expected %v but got %v", exp, result.Result) + } +} + +func TestDecisionWithStrictBuiltinErrors(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package example +import rego.v1 + +erroring_function(number) = output if { + output := number / 0 +} + +allow if { + erroring_function(1) +} +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + _, err = opa.Decision(ctx, sdk.DecisionOptions{ + StrictBuiltinErrors: true, + Path: "/example/allow", + }) + if err == nil { + t.Fatal("expected error but got nil") + } + + actual, ok := err.(*topdown.Error) + if !ok || actual.Code != "eval_builtin_error" { + t.Fatalf("expected eval_builtin_error but got %v", actual) + } + + if exp, act := "div: divide by zero", actual.Message; exp != act { + t.Fatalf("expected %v but got %v", exp, act) + } +} + +func TestDecisionWithTrace(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system +import rego.v1 + +main if { + trace("foobar") + true +} + `, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + tracer := topdown.NewBufferTracer() + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{ + Path: "/system/main", + Tracer: tracer, + }); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(bool); !ok || !decision { + t.Fatal("expected true but got:", decision, ok) + } + + events := lineage.Notes(*(tracer)) + if exp, act := 3, len(events); exp != act { + t.Fatalf("expected %d events, got %d", exp, act) + } + + if exp, act := "Enter", string(events[0].Op); exp != act { + t.Errorf("expected %s event, got %s", exp, act) + } + if exp, act := "data.system.main", string(events[0].Location.Text); exp != act { + t.Errorf("expected location %q got %q", exp, act) + } + if exp, act := "Enter", string(events[1].Op); exp != act { + t.Errorf("expected %s event, got %v", exp, act) + } + if exp, act := "Note", string(events[2].Op); exp != act { + t.Errorf("expected %s event, got %v", exp, act) + } + if exp, act := "foobar", events[2].Message; exp != act { + t.Errorf("unexpected message, wanted %q, got %q", exp, act) + } +} + +func TestDecisionWithMetrics(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = true +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + m := metrics.New() + if result, err := opa.Decision(ctx, sdk.DecisionOptions{ + Metrics: m, + }); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(bool); !ok || !decision { + t.Fatal("expected true but got:", decision, ok) + } + + if exp, act := 4, len(m.All()); exp != act { + t.Fatalf("expected %d metrics, got %d", exp, act) + } + + expectedRecordedMetricGroups := map[string]bool{ + "timer_rego": false, + "timer_sdk": false, + } + for k := range m.All() { + for group, found := range expectedRecordedMetricGroups { + if found { + continue + } + if strings.HasPrefix(k, group) { + expectedRecordedMetricGroups[group] = true + } + } + } + for group, found := range expectedRecordedMetricGroups { + if !found { + t.Errorf("expected metric group %s not recorded", group) + } + } + +} + +func TestDecisionWithIntrumentationAndProfile(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = true +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + m := metrics.New() + p := profiler.New() + if result, err := opa.Decision(ctx, sdk.DecisionOptions{ + Metrics: m, + Profiler: p, + Instrument: true, + }); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(bool); !ok || !decision { + t.Fatal("expected true but got:", decision, ok) + } + + if exp, act := 25, len(m.All()); exp != act { + t.Fatalf("expected %d metrics, got %d", exp, act) + } + + expectedRecordedMetricGroups := map[string]bool{ + "counter_eval": false, + "histogram_eval": false, + "timer_query_compile": false, + "timer_eval": false, + "timer_rego": false, + "timer_sdk": false, + } + for k := range m.All() { + for group, found := range expectedRecordedMetricGroups { + if found { + continue + } + if strings.HasPrefix(k, group) { + expectedRecordedMetricGroups[group] = true + } + } + } + for group, found := range expectedRecordedMetricGroups { + if !found { + t.Errorf("expected metric group %s not recorded", group) + } + } + + stats := p.ReportTopNResults(10, []string{"line"}) + + if exp, act := 2, len(stats); exp != act { + t.Fatalf("expected %d stats, got %d", exp, act) + } + if exp, act := "true", string(stats[0].Location.Text); exp != act { + t.Errorf("expected location %q got %q", exp, act) + } + if exp, act := "data.system.main", string(stats[1].Location.Text); exp != act { + t.Errorf("expected location %q got %q", exp, act) + } + +} + +func TestDecisionWithProvenance(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = true +`, + ".manifest": `{"revision": "v1.0.0"}`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + result, err := opa.Decision(ctx, sdk.DecisionOptions{}) + if err != nil { + t.Fatal(err) + } + if decision, ok := result.Result.(bool); !ok || !decision { + t.Fatal("expected true but got:", decision, ok) + } + + expectedProvenance := types.ProvenanceV1{ + Version: version.Version, + Bundles: map[string]types.ProvenanceBundleV1{ + "test": { + Revision: "v1.0.0", + }, + }, + } + + if result.Provenance.Version == "" { + t.Error("expected non empty provenance version") + } + + if !reflect.DeepEqual(result.Provenance, expectedProvenance) { + t.Fatalf("expected %v but got %v", expectedProvenance, result.Provenance) + } + +} + +func TestDecisionWithBundleData(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = data.foo +`, + "data.json": `{"foo": "bar"}`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + result, err := opa.Decision(ctx, sdk.DecisionOptions{}) + if err != nil { + t.Fatal(err) + } + + exp := "bar" + if act, ok := result.Result.(string); !ok || act != exp { + t.Fatalf("expected %s but got %s", exp, act) + } + +} + +func TestDecisionWithConfigurableID(t *testing.T) { + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = time.now_ns() +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL()) + + testLogger := loggingtest.New() + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + ConsoleLogger: testLogger}) + + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + if _, err := opa.Decision(ctx, sdk.DecisionOptions{ + Now: time.Unix(0, 1619868194450288000).UTC(), + }); err != nil { + t.Fatal(err) + } + + if _, err := opa.Decision(ctx, sdk.DecisionOptions{ + Now: time.Unix(0, 1619868194450288000).UTC(), + DecisionID: "164031de-e511-11ec-8fea-0242ac120002", + }); err != nil { + t.Fatal(err) + } + + entries := testLogger.Entries() + + if exp, act := 2, len(entries); exp != act { + t.Fatalf("expected %d entries, got %d", exp, act) + } + + if entries[0].Fields["decision_id"] == "" { + t.Fatalf("expected not empty decision_id") + } + + if entries[1].Fields["decision_id"] != "164031de-e511-11ec-8fea-0242ac120002" { + t.Fatalf("expected %v but got %v", "164031de-e511-11ec-8fea-0242ac120002", entries[1].Fields["decision_id"]) + } +} + +func TestPartial(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package test +import rego.v1 + +allow if { + data.junk.x = input.y +} +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL()) + + testLogger := loggingtest.New() + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + ConsoleLogger: testLogger, + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + var result *sdk.PartialResult + if result, err = opa.Partial(ctx, sdk.PartialOptions{ + Input: map[string]int{"y": 2}, + Query: "data.test.allow = true", + Unknowns: []string{"data.junk.x"}, + Mapper: &sdk.RawMapper{}, + Now: time.Unix(0, 1619868194450288000).UTC(), + }); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(*rego.PartialQueries); !ok || decision.Queries[0].String() != "2 = data.junk.x" { + t.Fatal("expected &{[2 = data.junk.x] []} true but got:", decision, ok) + } + + entries := testLogger.Entries() + + if l := len(entries); l != 1 { + t.Fatalf("expected %v but got %v", 1, l) + } + + // just checking for existence, since it's a complex value + if entries[0].Fields["mapped_result"] == nil { + t.Fatalf("expected not nil value for mapped_result but got nil") + } + + if entries[0].Fields["result"] == nil { + t.Fatalf("expected not nil value for result but got nil") + } + + if entries[0].Fields["timestamp"] != "2021-05-01T11:23:14.450288Z" { + t.Fatalf("expected %v but got %v", "2021-05-01T11:23:14.450288Z", entries[0].Fields["timestamp"]) + } + +} + +func TestPartialWithStrictBuiltinErrors(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package example +import rego.v1 + +erroring_function(number) = output if { + output := number / 0 +} + +allow if { + erroring_function(1) +} +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL()) + + testLogger := loggingtest.New() + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + ConsoleLogger: testLogger, + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + _, err = opa.Partial(ctx, sdk.PartialOptions{ + Input: map[string]any{}, + Query: "data.example.allow", + Unknowns: []string{}, + Mapper: &sdk.RawMapper{}, + Now: time.Unix(0, 1619868194450288000).UTC(), + StrictBuiltinErrors: true, + }) + if err == nil { + t.Fatal("expected error but got nil") + } + + actual, ok := err.(*topdown.Error) + if !ok || actual.Code != "eval_builtin_error" { + t.Fatalf("expected eval_builtin_error but got %v", actual) + } + + if exp, act := "div: divide by zero", actual.Message; exp != act { + t.Fatalf("expected %v but got %v", exp, act) + } +} + +func TestPartialWithTrace(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system +import rego.v1 + +main if { + trace("foobar") +} +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL()) + + testLogger := loggingtest.New() + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + ConsoleLogger: testLogger, + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + tracer := topdown.NewBufferTracer() + _, err = opa.Partial(ctx, sdk.PartialOptions{ + Input: map[string]any{}, + Query: "data.system.main", + Unknowns: []string{}, + Mapper: &sdk.RawMapper{}, + Now: time.Unix(0, 1619868194450288000).UTC(), + Tracer: tracer, + }) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + events := lineage.Notes(*(tracer)) + + if exp, act := 3, len(events); exp != act { + t.Fatalf("expected %d events, got %d", exp, act) + } + + if exp, act := "Enter", string(events[0].Op); exp != act { + t.Errorf("expected %s event, got %s", exp, act) + } + if exp, act := "data.system.main", string(events[0].Location.Text); exp != act { + t.Errorf("expected location %q got %q", exp, act) + } + if exp, act := "Enter", string(events[1].Op); exp != act { + t.Errorf("expected %s event, got %v", exp, act) + } + if exp, act := "Note", string(events[2].Op); exp != act { + t.Errorf("expected %s event, got %v", exp, act) + } + if exp, act := "foobar", events[2].Message; exp != act { + t.Errorf("unexpected message, wanted %q, got %q", exp, act) + } +} + +func TestPartialWithMetrics(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package test +import rego.v1 + +allow if { + data.junk.x = input.y +} +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + m := metrics.New() + var result *sdk.PartialResult + if result, err = opa.Partial(ctx, sdk.PartialOptions{ + Input: map[string]int{"y": 2}, + Query: "data.test.allow = true", + Unknowns: []string{"data.junk.x"}, + Mapper: &sdk.RawMapper{}, + Now: time.Unix(0, 1619868194450288000).UTC(), + Metrics: m, + }); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(*rego.PartialQueries); !ok || decision.Queries[0].String() != "2 = data.junk.x" { + t.Fatal("expected &{[2 = data.junk.x] []} true but got:", decision, ok) + } + + if exp, act := 5, len(m.All()); exp != act { + t.Fatalf("expected %d metrics, got %d", exp, act) + } + + expectedRecordedMetricGroups := map[string]bool{ + "timer_rego": false, + "timer_sdk": false, + } + for k := range m.All() { + for group, found := range expectedRecordedMetricGroups { + if found { + continue + } + if strings.HasPrefix(k, group) { + expectedRecordedMetricGroups[group] = true + } + } + } + for group, found := range expectedRecordedMetricGroups { + if !found { + t.Errorf("expected metric group %s not recorded", group) + } + } + +} + +func TestPartialWithInstrumentationAndProfile(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package test +import rego.v1 + +allow if { + data.junk.x = input.y +} +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + m := metrics.New() + p := profiler.New() + var result *sdk.PartialResult + if result, err = opa.Partial(ctx, sdk.PartialOptions{ + Input: map[string]int{"y": 2}, + Query: "data.test.allow = true", + Unknowns: []string{"data.junk.x"}, + Mapper: &sdk.RawMapper{}, + Now: time.Unix(0, 1619868194450288000).UTC(), + Metrics: m, + Profiler: p, + Instrument: true, + }); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(*rego.PartialQueries); !ok || decision.Queries[0].String() != "2 = data.junk.x" { + t.Fatal("expected &{[2 = data.junk.x] []} true but got:", decision, ok) + } + + if exp, act := 32, len(m.All()); exp != act { + t.Fatalf("expected %d metrics, got %d", exp, act) + } + + expectedRecordedMetricGroups := map[string]bool{ + "histogram_eval": false, + "histogram_partial": false, + "timer_query_compile": false, + "timer_eval": false, + "timer_partial": false, + "timer_rego": false, + "timer_sdk": false, + } + + for k := range m.All() { + for group, found := range expectedRecordedMetricGroups { + if found { + continue + } + if strings.HasPrefix(k, group) { + expectedRecordedMetricGroups[group] = true + } + } + } + for group, found := range expectedRecordedMetricGroups { + if !found { + t.Errorf("expected metric group %s not recorded", group) + } + } + + stats := p.ReportTopNResults(10, []string{"line"}) + + if exp, act := 2, len(stats); exp != act { + t.Fatalf("expected %d stats, got %d", exp, act) + } + if exp, act := "data.junk.x = input.y", string(stats[0].Location.Text); exp != act { + t.Errorf("expected location %q got %q", exp, act) + } + if exp, act := "data.test.allow = true", string(stats[1].Location.Text); exp != act { + t.Errorf("expected location %q got %q", exp, act) + } + +} + +func TestPartialWithProvenance(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package test +import rego.v1 + +allow if { + data.junk.x = input.y +} +`, + ".manifest": `{"revision": "v1.0.0"}`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + var result *sdk.PartialResult + if result, err = opa.Partial(ctx, sdk.PartialOptions{ + Input: map[string]int{"y": 2}, + Query: "data.test.allow = true", + Unknowns: []string{"data.junk.x"}, + Mapper: &sdk.RawMapper{}, + Now: time.Unix(0, 1619868194450288000).UTC(), + }); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(*rego.PartialQueries); !ok || decision.Queries[0].String() != "2 = data.junk.x" { + t.Fatal("expected &{[2 = data.junk.x] []} true but got:", decision, ok) + } + + expectedProvenance := types.ProvenanceV1{ + Version: version.Version, + Vcs: version.Vcs, + Timestamp: version.Timestamp, + Hostname: version.Hostname, + Bundles: map[string]types.ProvenanceBundleV1{ + "test": { + Revision: "v1.0.0", + }, + }, + } + + if !reflect.DeepEqual(result.Provenance, expectedProvenance) { + t.Fatalf("expected %v but got %v", expectedProvenance, result.Provenance) + } + +} + +func TestPartialWithConfigurableID(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package test +import rego.v1 + +allow if { + data.junk.x = input.y +} +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL()) + + testLogger := loggingtest.New() + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + ConsoleLogger: testLogger, + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + if result, err := opa.Partial(ctx, sdk.PartialOptions{ + Input: map[string]int{"y": 2}, + Query: "data.test.allow = true", + Unknowns: []string{"data.junk.x"}, + Mapper: &sdk.RawMapper{}, + Now: time.Unix(0, 1619868194450288000).UTC(), + }); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(*rego.PartialQueries); !ok || decision.Queries[0].String() != "2 = data.junk.x" { + t.Fatal("expected &{[2 = data.junk.x] []} true but got:", decision, ok) + } + + if result, err := opa.Partial(ctx, sdk.PartialOptions{ + Input: map[string]int{"y": 2}, + Query: "data.test.allow = true", + Unknowns: []string{"data.junk.x"}, + Mapper: &sdk.RawMapper{}, + Now: time.Unix(0, 1619868194450288000).UTC(), + DecisionID: "164031de-e511-11ec-8fea-0242ac120002", + }); err != nil { + t.Fatal(err) + } else if decision, ok := result.Result.(*rego.PartialQueries); !ok || decision.Queries[0].String() != "2 = data.junk.x" { + t.Fatal("expected &{[2 = data.junk.x] []} true but got:", decision, ok) + } + + entries := testLogger.Entries() + + if exp, act := 2, len(entries); exp != act { + t.Fatalf("expected %d entries, got %d", exp, act) + } + + if entries[0].Fields["decision_id"] == "" { + t.Fatalf("expected not empty decision_id") + } + + if entries[1].Fields["decision_id"] != "164031de-e511-11ec-8fea-0242ac120002" { + t.Fatalf("expected %v but got %v", "164031de-e511-11ec-8fea-0242ac120002", entries[1].Fields["decision_id"]) + } +} + +func TestUndefinedError(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": "package system", + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + _, err = opa.Decision(ctx, sdk.DecisionOptions{}) + if err == nil { + t.Fatal("expected error") + } + + if actual, ok := err.(*sdk.Error); !ok || actual.Code != sdk.UndefinedErr { + t.Fatalf("expected undefined error but got %v", actual) + } + +} + +func TestDecisionLogging(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = time.now_ns() +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL()) + + testLogger := loggingtest.New() + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + ConsoleLogger: testLogger, + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + // Verify that timestamp matches time.now_ns() value. + if _, err := opa.Decision(ctx, sdk.DecisionOptions{ + Now: time.Unix(0, 1619868194450288000).UTC(), + }); err != nil { + t.Fatal(err) + } + + entries := testLogger.Entries() + exp := json.Number("1619868194450288000") + + if len(entries) != 1 || entries[0].Fields["result"] != exp || entries[0].Fields["timestamp"] != "2021-05-01T11:23:14.450288Z" { + t.Fatalf("expected %v but got %v", exp, entries[0].Fields["result"]) + } + + // Verify that timestamp matches time.now_ns() value. + if _, err := opa.Decision(ctx, sdk.DecisionOptions{ + Now: time.Unix(0, 1619868194450288000).UTC(), + }); err != nil { + t.Fatal(err) + } + +} + +func TestDecisionLoggingWithMasking(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = true + +str = "foo" + +loopback = input +`, + "log.rego": ` +package system.log +import rego.v1 + +mask contains "/input/secret" +mask contains "/input/top/secret" +mask contains "/input/dossier/1/highly" +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL()) + + testLogger := loggingtest.New() + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + ConsoleLogger: testLogger, + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + if _, err := opa.Decision(ctx, sdk.DecisionOptions{ + Input: map[string]any{ + "secret": "foo", + "top": map[string]string{ + "secret": "bar", + }, + "dossier": []map[string]any{ + { + "very": "private", + }, + { + "highly": "classified", + }, + }, + }, + }); err != nil { + t.Fatal(err) + } + + entries := testLogger.Entries() + + if len(entries) != 1 { + t.Fatalf("expected 1 entry but got %d", len(entries)) + } + + expectedErased := []any{ + "/input/dossier/1/highly", + "/input/secret", + "/input/top/secret", + } + erased := entries[0].Fields["erased"].([]any) + stringLess := func(a, b string) bool { + return a < b + } + if !cmp.Equal(expectedErased, erased, cmpopts.SortSlices(stringLess)) { + t.Errorf("Did not get expected result for erased field in decision log:\n%s", cmp.Diff(expectedErased, erased, cmpopts.SortSlices(stringLess))) + } + errMsg := `Expected masked field "%s" to be removed, but it was present.` + input := entries[0].Fields["input"].(map[string]any) + if _, ok := input["secret"]; ok { + t.Errorf(errMsg, "/input/secret") + } + + if _, ok := input["top"].(map[string]any)["secret"]; ok { + t.Errorf(errMsg, "/input/top/secret") + } + + if _, ok := input["dossier"].([]any)[1].(map[string]any)["highly"]; ok { + t.Errorf(errMsg, "/input/dossier/1/highly") + } + +} + +func TestDecisionLoggingWithNDBCache(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = time.now_ns() +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true, + "nd_builtin_cache": true + } + }`, server.URL()) + + testLogger := loggingtest.New() + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + ConsoleLogger: testLogger, + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + // Build ND builtins cache, and populate with an unused builtin. + ndbc := builtins.NDBCache{} + ndbc.Put("rand.intn", ast.NewArray(), ast.NewObject([2]*ast.Term{ast.StringTerm("z"), ast.IntNumberTerm(7)})) + + // Verify that timestamp matches time.now_ns() value. + if _, err := opa.Decision(ctx, sdk.DecisionOptions{ + Now: time.Unix(0, 1619868194450288000).UTC(), + NDBCache: ndbc, + }); err != nil { + t.Fatal(err) + } + + entries := testLogger.Entries() + + // Check the contents of the ND builtins cache. + if cache, ok := entries[0].Fields["nd_builtin_cache"]; ok { + // Ensure the original cache entry for rand.intn is still there. + if _, ok := cache.(map[string]any)["rand.intn"]; !ok { + t.Fatalf("ND builtins cache was not preserved during evaluation.") + } + // Ensure time.now_ns entry was picked up correctly. + if _, ok := cache.(map[string]any)["time.now_ns"]; !ok { + t.Fatalf("ND builtins cache did not observe time.now_ns call during evaluation.") + } + } else { + t.Fatalf("ND builtins cache missing.") + } + +} + +func TestQueryCaching(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = 7 +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "decision_logs": { + "console": true + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + // Execute two queries with metrics + m1 := metrics.New() + if _, err := opa.Decision(ctx, sdk.DecisionOptions{ + Metrics: m1, + }); err != nil { + t.Fatal(err) + } + + m2 := metrics.New() + if _, err := opa.Decision(ctx, sdk.DecisionOptions{ + Metrics: m2, + }); err != nil { + t.Fatal(err) + } + + // Expect only the metrics from the first query to contain preparation metrics + if _, ok := m1.All()["timer_rego_query_parse_ns"]; !ok { + t.Fatal("first query should have preparation metrics") + } + if _, ok := m2.All()["timer_rego_query_parse_ns"]; ok { + t.Fatal("second query should not have preparation metrics") + } + +} + +func TestDiscovery(t *testing.T) { + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/discovery.tar.gz", map[string]string{ + "bundles.rego": ` +package bundles + +test := {"resource": "/bundles/bundle.tar.gz"} + `, + }), + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = 7 + `, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "discovery": { + "resource": "/bundles/discovery.tar.gz" + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + exp := json.Number("7") + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{}); err != nil { + t.Fatal(err) + } else if result.Result != exp { + t.Fatalf("expected %v but got %v", exp, result.Result) + } + +} + +func TestDiscoveryBundleRegoV1(t *testing.T) { + tests := []struct { + note string + v1Compatible bool + discoveryBundle map[string]string + policyBundle map[string]string + expErr string + }{ + { + note: "0.x compatible, keywords not imported ind disco bundle", + discoveryBundle: map[string]string{ + "bundles.rego": ` +package bundles + +foo contains "bar" + +# This will be interpreted as two rules - 'test.resource' and 'if' - so we have foo above to force an error +test.resource := b if { + b := "/bundles/bundle.tar.gz" +}`, + }, + policyBundle: map[string]string{ + "main.rego": ` +package system + +import future.keywords + +main := v if { v := 7 }`, + }, + expErr: "rego_parse_error", + }, + + { + note: "0.x compatible, keywords not imported ind policy bundle", + discoveryBundle: map[string]string{ + "bundles.rego": ` +package bundles + +import future.keywords + +test.resource := b if { + b := "/bundles/bundle.tar.gz" +}`, + }, + policyBundle: map[string]string{ + "main.rego": ` +package system + +foo contains "bar" + +# This will be interpreted as two rules - 'main' and 'if' - so we have foo above to force an error +main := v if { v := 7 }`, + }, + expErr: "rego_parse_error", + }, + + { + note: "0.x compatible, keywords imported", + discoveryBundle: map[string]string{ + "bundles.rego": ` +package bundles + +import future.keywords + +test.resource := b if { + b := "/bundles/bundle.tar.gz" +}`, + }, + policyBundle: map[string]string{ + "main.rego": ` +package system + +import future.keywords + +main := v if { v := 7 }`, + }, + }, + { + note: "0.x compatible, rego.v1 imported", + discoveryBundle: map[string]string{ + "bundles.rego": ` +package bundles + +import rego.v1 + +test.resource := b if { + b := "/bundles/bundle.tar.gz" +}`, + }, + policyBundle: map[string]string{ + "main.rego": ` +package system + +import rego.v1 + +main := v if { v := 7 }`, + }, + }, + { + note: "1.0 compatible, keywords not imported", + v1Compatible: true, + // Discovery and policy bundles are rego-v1 compatible, but rego-v0 incompatible (if keyword used without import) + discoveryBundle: map[string]string{ + "bundles.rego": ` +package bundles + +test.resource := b if { + b := "/bundles/bundle.tar.gz" +}`, + }, + policyBundle: map[string]string{ + "main.rego": ` +package system + +main := v if { v := 7 }`, + }, + }, + { + note: "1.0 compatible, keywords imported", + v1Compatible: true, + discoveryBundle: map[string]string{ + "bundles.rego": ` +package bundles + +import future.keywords + +test.resource := b if { + b := "/bundles/bundle.tar.gz" +}`, + }, + policyBundle: map[string]string{ + "main.rego": ` +package system + +import future.keywords + +main := v if { v := 7 }`, + }, + }, + { + note: "1.0 compatible, rego.v1 imported", + v1Compatible: true, + discoveryBundle: map[string]string{ + "bundles.rego": ` +package bundles + +import rego.v1 + +test.resource := b if { + b := "/bundles/bundle.tar.gz" +}`, + }, + policyBundle: map[string]string{ + "main.rego": ` +package system + +import rego.v1 + +main := v if { v := 7 }`, + }, + }, + { + note: "1.0 compatible, keywords not used in discovery bundle", + v1Compatible: true, + discoveryBundle: map[string]string{ + "bundles.rego": ` +package bundles + +import rego.v1 + +test.resource := b { + b := "/bundles/bundle.tar.gz" +}`, + }, + expErr: "rego_parse_error", + }, + { + note: "1.0 compatible, keywords not used in policy bundle", + v1Compatible: true, + discoveryBundle: map[string]string{ + "bundles.rego": ` +package bundles + +import rego.v1 + +test.resource := b if { + b := "/bundles/bundle.tar.gz" +}`, + }, + policyBundle: map[string]string{ + "main.rego": ` +package system + +import rego.v1 + +main := v { v := 7 }`, + }, + expErr: "rego_parse_error", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + + serverOpts := []func(*sdktest.Server) error{ + sdktest.MockBundle("/bundles/discovery.tar.gz", tc.discoveryBundle), + sdktest.MockBundle("/bundles/bundle.tar.gz", tc.policyBundle), + sdktest.RawBundles(true), + } + server := sdktest.MustNewServer(serverOpts...) + defer server.Stop() + + c := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "discovery": { + "resource": "/bundles/discovery.tar.gz" + } + }`, server.URL()) + + var readyCh chan struct{} + var logger logging.Logger + if tc.expErr != "" { + logger = loggingtest.New() + logger.SetLevel(logging.Info) + readyCh = make(chan struct{}) + } else { + logger = logging.NewNoOpLogger() + } + + opa, err := sdk.New(ctx, sdk.Options{ + Logger: logger, + Ready: readyCh, + Config: strings.NewReader(c), + RegoVersion: ast.RegoV0, + V1Compatible: tc.v1Compatible, + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + if tc.expErr != "" { + l := logger.(*loggingtest.Logger) + if !test.Eventually(t, 5*time.Second, func() bool { + for _, e := range l.Entries() { + if strings.Contains(e.Message, tc.expErr) { + return true + } + } + return false + }) { + t.Fatalf("timed out waiting for logged error:\n\n%s\n\ngot\n\n%v:", tc.expErr, l.Entries()) + } + } else { + exp := json.Number("7") + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{}); err != nil { + t.Fatal(err) + } else if result.Result != exp { + t.Fatalf("expected %v but got %v", exp, result.Result) + } + } + }) + } +} + +func TestRegoV1WithConfiguredLocalBundle(t *testing.T) { + tests := []struct { + note string + v1Compatible bool + policy string + expErr string + }{ + { + note: "0.x compatible, keywords not imported", + policy: ` +package system + +l contains 7 + +main := v if { + v := l[0] +} +`, + expErr: "rego_parse_error", + }, + { + note: "0.x compatible, keywords imported", + policy: ` +package system + +import future.keywords + +main := 7 if { + true +} +`, + }, + { + note: "0.x compatible, rego.v1 imported", + policy: ` +package system + +import rego.v1 + +main := 7 if { + true +} +`, + }, + { + note: "1.0 compatible, keywords not imported", + v1Compatible: true, + policy: ` +package system + +main := 7 if { + true +} +`, + }, + { + note: "1.0 compatible, keywords imported", + v1Compatible: true, + policy: ` +package system + +import future.keywords + +main := 7 if { + true +} +`, + }, + { + note: "1.0 compatible, rego.v1 imported", + v1Compatible: true, + policy: ` +package system + +import rego.v1 + +main := 7 if { + true +} +`, + }, + { + note: "1.0 compatible, keywords not used", + v1Compatible: true, + policy: ` +package system + +main := 7 { + true +} +`, + expErr: "rego_parse_error", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(map[string]string{}, func(rootDir string) { + f, err := os.Create(filepath.Join(rootDir, "bundle.tar.gz")) + if err != nil { + t.Fatal(err) + } + buf := archive.MustWriteTarGz([][2]string{{"main.rego", tc.policy}}) + _, err = f.Write(buf.Bytes()) + if err != nil { + t.Fatal(err) + } + + c := fmt.Sprintf(`services: +bundles: + test: + resource: "file://%s/bundle.tar.gz"`, rootDir) + + var readyCh chan struct{} + logger := loggingtest.New() + logger.SetLevel(logging.Info) + if tc.expErr != "" { + readyCh = make(chan struct{}) + } + + ctx := context.Background() + opa, err := sdk.New(ctx, sdk.Options{ + Logger: logger, + Ready: readyCh, + Config: strings.NewReader(c), + RegoVersion: ast.RegoV0, + V1Compatible: tc.v1Compatible, + }) + if err != nil { + t.Fatal(err) + } + + if tc.expErr != "" { + if !test.Eventually(t, 5*time.Second, func() bool { + entries := logger.Entries() + for _, e := range entries { + if strings.Contains(e.Message, tc.expErr) { + return true + } + } + return false + }) { + t.Fatalf("timed out waiting for logged error:\n\n%s\n\ngot\n\n%v:", tc.expErr, logger.Entries()) + } + } else { + exp := json.Number("7") + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{}); err != nil { + t.Fatal(err) + } else if result.Result != exp { + t.Fatalf("expected %v but got %v", exp, result.Result) + } + } + }) + }) + } +} + +func TestAsync(t *testing.T) { + + ctx := context.Background() + + callerReadyCh := make(chan struct{}) + readyCh := make(chan struct{}) + + server := sdktest.MustNewServer( + sdktest.Ready(callerReadyCh), + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = 7 +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + Ready: readyCh, + }) + if err != nil { + t.Fatal(err) + } + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{}); !sdk.IsUndefinedErr(err) { + t.Fatal("expected undefined error but got", result, "err:", err) + } + + defer opa.Stop(ctx) + + // Signal the server to become ready. By controlling server readiness, we + // can avoid a race condition above when expecting an undefined decision. + close(callerReadyCh) + + <-readyCh + + exp := json.Number("7") + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{}); err != nil || !reflect.DeepEqual(result.Result, exp) { + t.Fatal("expected 7 but got", result, "err:", err) + } +} + +func TestCancelStartup(t *testing.T) { + + server := sdktest.MustNewServer() + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/doesnotexist.tar.gz" + } + } + }`, server.URL()) + + // Server will return 404 responses because bundle does not exist. OPA should timeout. + ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100) + defer cancel() + + _, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if !errors.Is(err, context.DeadlineExceeded) { + t.Fatalf("expected deadline exceeded error but got %v", err) + } +} + +// TestStopWithDeadline asserts that a graceful shutdown of the SDK is possible. +func TestStopWithDeadline(t *testing.T) { + + ctx := context.Background() + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(`{ + "plugins": { + "test_plugin": {} + } + }`), + Plugins: map[string]plugins.Factory{ + "test_plugin": factory{shutdown: time.Second}, + }, + }) + if err != nil { + t.Fatal(err) + } + + const timeout = 20 * time.Millisecond + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + before := time.Now() + opa.Stop(ctx) // 1s timeout is ignored + + dur := time.Since(before) + diff := dur - timeout + maxDelta := 500 * time.Millisecond + if diff > maxDelta || diff < -maxDelta { + t.Errorf("expected shutdown to have %v grace period, measured shutdown in %v (max delta %v)", timeout, dur, maxDelta) + } +} + +func TestConfigAsYAML(t *testing.T) { + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = 7 +`, + }), + ) + defer server.Stop() + + config := fmt.Sprintf(`services: + test: + url: %q +bundles: + test: + resource: "/bundles/bundle.tar.gz"`, server.URL()) + + ctx := context.Background() + _, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } +} + +func TestConfigure(t *testing.T) { + defer leaktest.Check(t)() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle1.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = 7 +`, + }), + sdktest.MockBundle("/bundles/bundle2.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = 8 +`, + }), + ) + defer server.Stop() + + // Startup new OPA with first config. + config1 := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle1.tar.gz" + } + } + }`, server.URL()) + + ctx := context.Background() + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config1), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + exp := json.Number("7") + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{}); err != nil { + t.Fatal(err) + } else if result.Result != exp { + t.Fatalf("expected %v but got %v", exp, result.Result) + } + + // Reconfigure with new config to make sure update is picked up. + config2 := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle2.tar.gz" + } + } + }`, server.URL()) + + err = opa.Configure(ctx, sdk.ConfigOptions{ + Config: strings.NewReader(config2), + }) + if err != nil { + t.Fatal(err) + } + + exp = json.Number("8") + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{}); err != nil { + t.Fatal(err) + } else if result.Result != exp { + t.Fatalf("expected %v but got %v", exp, result.Result) + } + + // Reconfigure w/ same config to verify that readiness channel is closed. + ch := make(chan struct{}) + err = opa.Configure(ctx, sdk.ConfigOptions{ + Config: strings.NewReader(config2), + Ready: ch, + }) + if err != nil { + t.Fatal(err) + } + + <-ch +} + +func TestOpaVersion(t *testing.T) { + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +opa_version := opa.runtime().version + `, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + exp := version.Version + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{Path: "/system/opa_version"}); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(result.Result, exp) { + t.Fatalf("expected %v but got %v", exp, result.Result) + } +} + +func TestOpaRuntimeConfig(t *testing.T) { + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +rt := opa.runtime() + +result := { + "service_url": rt.config.services.test.url, + "bundle_resource": rt.config.bundles.test.resource, + "test_label": rt.config.labels.test +} + `, + }), + ) + + defer server.Stop() + + testBundleResource := "/bundles/bundle.tar.gz" + testLabel := "a label" + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": %q + } + }, + "labels": { + "test": %q + } + }`, server.URL(), testBundleResource, testLabel) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + exp := map[string]any{ + "service_url": server.URL(), + "bundle_resource": testBundleResource, + "test_label": testLabel, + } + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{Path: "/system/result"}); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(result.Result, exp) { + t.Fatalf("expected %v but got %v", exp, result.Result) + } +} + +func TestOpaRuntimeEnvironmentVariableDefinedInOS(t *testing.T) { + t.Setenv("TOKEN_VERIFY_KEY", "B41BD5F462719C6D6118E673A2389") + + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system +import rego.v1 + +rt := opa.runtime() + +grant if { + authenticatedUser +} + +claims := payload if { + io.jwt.verify_hs256(input.token, opa.runtime().env.TOKEN_VERIFY_KEY) + [_, payload, _] := io.jwt.decode(input.token) +} + +authenticatedUser := a if { + claims + a := count(claims) > 0 +} + `, + }), + ) + + defer server.Stop() + + testBundleResource := "/bundles/bundle.tar.gz" + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": %q + } + }, + }`, server.URL(), testBundleResource) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + exp := true + + input := map[string]any{} + input["token"] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiQWxpY2lhIFNtaXRoc29uaWFuIiwicm9sZXMiOlsicmVhZGVyIiwid3JpdGVyIl0sInVzZXJuYW1lIjoiYWxpY2UifQ.md2KPJFH9OgBq-N0RonGdf5doGYRO_1miN8ugTSeTYc" + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{Path: "/system/grant", Input: input}); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(result.Result, exp) { + t.Fatalf("expected %v but got %v", exp, result.Result) + } +} + +func TestOpaRuntimeEnvironmentVariableDefinedInConfig(t *testing.T) { + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system +import rego.v1 + +rt := opa.runtime() + +grant if { + authenticatedUser +} + +claims := payload if { + io.jwt.verify_hs256(input.token, opa.runtime().config.env.TOKEN_VERIFY_KEY) + [_, payload, _] := io.jwt.decode(input.token) +} + +authenticatedUser := a if { + claims + a := count(claims) > 0 +} + `, + }), + ) + + defer server.Stop() + + testBundleResource := "/bundles/bundle.tar.gz" + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": %q + } + }, + "env": { + "TOKEN_VERIFY_KEY" : "B41BD5F462719C6D6118E673A2389" + } + }`, server.URL(), testBundleResource) + + opa, err := sdk.New(ctx, sdk.Options{ + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + exp := true + + input := map[string]any{} + input["token"] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiQWxpY2lhIFNtaXRoc29uaWFuIiwicm9sZXMiOlsicmVhZGVyIiwid3JpdGVyIl0sInVzZXJuYW1lIjoiYWxpY2UifQ.md2KPJFH9OgBq-N0RonGdf5doGYRO_1miN8ugTSeTYc" + + if result, err := opa.Decision(ctx, sdk.DecisionOptions{Path: "/system/grant", Input: input}); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(result.Result, exp) { + t.Fatalf("expected %v but got %v", exp, result.Result) + } +} + +func TestPrintStatements(t *testing.T) { + + ctx := context.Background() + + s := sdktest.MustNewServer( + sdktest.RawBundles(true), // non-raw bundles will be compiled server-side, which will change print location depending on parser rego-version (v1 drops rego.v1 import). + sdktest.MockBundle("/bundles/b.tar.gz", map[string]string{ + "x.rego": ` +package foo +import rego.v1 + +p if { print("XXX") } +`, + })) + + defer s.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/b.tar.gz" + } + } + }`, s.URL()) + + logger := loggingtest.New() + logger.SetLevel(logging.Info) + + opa, err := sdk.New(ctx, sdk.Options{ + Logger: logger, + Config: strings.NewReader(config), + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + if _, err := opa.Decision(ctx, sdk.DecisionOptions{Path: "/foo/p"}); err != nil { + t.Fatal(err) + } + + entries := logger.Entries() + if len(entries) == 0 { + t.Fatal("expected logs") + } + + e := entries[len(entries)-1] + + if e.Message != "XXX" || e.Fields["line"].(string) != "/x.rego:5" { + t.Fatal("expected print output but got:", e) + } +} + +func TestConfigurableManagerOpts(t *testing.T) { + ctx := context.Background() + + server := sdktest.MustNewServer( + sdktest.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "main.rego": ` +package system + +main = true + +str = "foo" + +loopback = input +`, + }), + ) + + defer server.Stop() + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/bundle.tar.gz" + } + }, + "status": { + "prometheus": true + } + }`, server.URL()) + + opa, err := sdk.New(ctx, sdk.Options{ + ID: "sdk-id-0", + Config: strings.NewReader(config), + ManagerOpts: []func(manager *plugins.Manager){ + plugins.WithPrometheusRegister(prometheus.DefaultRegisterer), + }, + }) + + defer opa.Stop(ctx) + + if err != nil { + t.Fatal(err) + } + + m, err := prometheus.DefaultGatherer.Gather() + if err != nil { + t.Fatal(err) + } + + registeredMetrics := toMetricMap(m) + + if registeredMetrics["opa_info"] == false { + t.Errorf("expected metric 'opa_info' to be registered but it was not") + } +} + +func toMetricMap(metrics []*promdto.MetricFamily) map[string]bool { + metricMap := make(map[string]bool, len(metrics)) + for _, m := range metrics { + metricMap[m.GetName()] = true + } + return metricMap +} + +func TestActivateV1Bundles(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100) + defer cancel() + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.FileServer(http.Dir("testdata")).ServeHTTP(w, r) + })) + + config := fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "v1bundle": { + "resource": "/v1bundle.tar.gz" + } + } + }`, server.URL) + + opa, err := sdk.New(ctx, sdk.Options{ + ID: "sdk-id-0", + Config: strings.NewReader(config), + Logger: logging.New(), + V1Compatible: true, + }) + if err != nil { + t.Fatal(err) + } + + defer opa.Stop(ctx) + + d, err := opa.Decision(context.Background(), sdk.DecisionOptions{ + Path: "v1bundle/authz", + Input: map[string]any{ + "role": "admin", + }, + }) + if err != nil { + t.Fatal(err) + } + + if d.Result != true { + t.Errorf("expected result to be true, got %v", d.Result) + } +} + +// TestWithOwnStoreVSExtStore asserts that in the SDK setup, a provided +// store always takes precedence over the extensions store. +func TestWithOwnStoreVSExtStore(t *testing.T) { + bundle.RegisterStoreFunc(inmem.New) + t.Cleanup(func() { bundle.RegisterStoreFunc(nil) }) + ctx := context.Background() + opts := sdk.Options{ + Store: inmem.New(), + } + store := opts.Store + if err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + return store.UpsertPolicy(ctx, txn, "pkg", []byte(`package pkg +p := true +`)) + }); err != nil { + panic(err) + } + + o, err := sdk.New(ctx, opts) + if err != nil { + panic(err) + } + + res, err := o.Decision(ctx, sdk.DecisionOptions{ + Path: "pkg/p", + }) + if err != nil { + t.Fatal(err) + } + exp := true + act := res.Result + if diff := cmp.Diff(exp, act); diff != "" { + t.Errorf("unexpected result (-want, +got):\n%s", diff) + } + +} diff --git a/third_party/opa/v1/sdk/options.go b/third_party/opa/v1/sdk/options.go new file mode 100644 index 000000000000..1814021e3a77 --- /dev/null +++ b/third_party/opa/v1/sdk/options.go @@ -0,0 +1,178 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package sdk + +import ( + "fmt" + "io" + "sync" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/sirupsen/logrus" + + "github.com/open-policy-agent/opa/v1/hooks" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" +) + +// DefaultOptions allows providing default `Options` to be used in sdk.New(). +var defaultOptions Options +var defaultOptsMtx sync.Mutex + +// SetDefaultOptions allows providing default `Options` to be used in sdk.New(). +// Note that due to the way booleans work, V1Compatible and V0Compatible is ignored in default options, +// use RegoVersion instead. +func SetDefaultOptions(o Options) { + defaultOptsMtx.Lock() + defaultOptions = o + defaultOptsMtx.Unlock() +} + +// Options contains parameters to setup and configure OPA. +type Options struct { + + // Config provides the OPA configuration for this instance. The config can + // be supplied as a YAML or JSON byte stream. See + // https://www.openpolicyagent.org/docs/latest/configuration/ for detailed + // description of the supported configuration. + Config io.Reader + + // Logger sets the logging implementation to use for standard logs emitted + // by OPA. By default, standard logging is disabled. + Logger logging.Logger + + // ConsoleLogger sets the logging implementation to use for emitting Status + // and Decision Logs to the console. By default, console logging is enabled. + ConsoleLogger logging.Logger + + // Ready sets a channel to notify when the OPA instance is ready. If this + // field is not set, the New() function will block until ready. The channel + // is closed to signal readiness. + Ready chan struct{} + + // Plugins provides a set of plugins.Factory instances that will be + // registered with the OPA SDK instance. + Plugins map[string]plugins.Factory + + // ID provides an option to set a static ID for the OPA system, avoiding + // the need to generate a random one at initialization. Setting a static ID + // is recommended, as it makes it easier to track the system over time. + ID string + + // Store sets the store to be used by the SDK instance. If nil, it'll use OPA's + // inmem store. + Store storage.Store + + // Hooks allows hooking into the internals of SDK operations (TODO(sr): find better words) + Hooks hooks.Hooks + + // V0Compatible enables v0 compatibility mode when set to true. + // This is an opt-in to OPA features and behaviors that were enabled by default in OPA v0.x. + // Takes precedence over V1Compatible. + V0Compatible bool + + // V1Compatible enables v1 compatibility mode when set to true. + // This is an opt-in to OPA features and behaviors that will be enabled by default in OPA v1.0 and later. + // See https://www.openpolicyagent.org/docs/latest/opa-1/ for more information. + // If V0Compatible is set to true, this field is ignored. + V1Compatible bool + + // RegoVersion sets the version of the Rego language to use. + // If V0Compatible or V1Compatible is set to true, this field is ignored. + RegoVersion ast.RegoVersion + + // ManagerOpts allows customization of the plugin manager. + // The given options get appended to the list of options already provided by the SDK and eventually + // overriding them. + ManagerOpts []func(manager *plugins.Manager) + + config []byte + block bool +} + +func (o *Options) regoVersion() ast.RegoVersion { + // v0 takes precedence over v1 + if o.V0Compatible { + return ast.RegoV0 + } + if o.V1Compatible { + return ast.RegoV1 + } + return o.RegoVersion +} + +func (o *Options) init() error { + + if o.Ready == nil { + o.Ready = make(chan struct{}) + o.block = true + } + + if o.Logger == nil { + o.Logger = logging.NewNoOpLogger() + } + + if o.ConsoleLogger == nil { + l := logging.New() + l.SetFormatter(&logrus.JSONFormatter{}) + o.ConsoleLogger = l + } + + if o.Config == nil { + o.config = []byte("{}") + } else { + bs, err := io.ReadAll(o.Config) + if err != nil { + return err + } + o.config = bs + } + + if o.Store == nil { + o.Store = inmem.New() + } + + if err := o.Hooks.Validate(); err != nil { + return fmt.Errorf("hooks: %w", err) + } + + return nil +} + +// ConfigOptions contains parameters to (re-)configure OPA. +type ConfigOptions struct { + + // Config provides the OPA configuration for this instance. The config can + // be supplied as a YAML or JSON byte stream. See + // https://www.openpolicyagent.org/docs/latest/configuration/ for detailed + // description of the supported configuration. + Config io.Reader + + // Ready sets a channel to notify when the OPA instance is ready. If this + // field is not set, the Configure() function will block until ready. The + // channel is closed to signal readiness. + Ready chan struct{} + + config []byte + block bool +} + +func (o *ConfigOptions) init() error { + + if o.Ready == nil { + o.Ready = make(chan struct{}) + o.block = true + } + + bs, err := io.ReadAll(o.Config) + if err != nil { + return err + } + + o.config = bs + return nil +} diff --git a/third_party/opa/v1/sdk/test/test.go b/third_party/opa/v1/sdk/test/test.go new file mode 100644 index 000000000000..1db9dfac8c1e --- /dev/null +++ b/third_party/opa/v1/sdk/test/test.go @@ -0,0 +1,492 @@ +package test + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/json" + "fmt" + "io" + "maps" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "time" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/compile" + + "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +// MockBundle sets a bundle named file on the test server containing the given +// policies. +func MockBundle(file string, policies map[string]string) func(*Server) error { + return func(s *Server) error { + if !strings.HasPrefix(file, "/bundles/") { + return fmt.Errorf("mock bundle filename must be prefixed with '/bundles/ but got %q", file) + } + s.bundles[file] = policies + return nil + } +} + +// MockOCIBundle prepares the server to allow serving "/v2" OCI responses from the supplied policies +// Ref parameter must be in the form of //: that will be used in detecting future calls +func MockOCIBundle(ref string, policies map[string]string) func(*Server) error { + return func(s *Server) error { + if !strings.Contains(ref, "/") { + return fmt.Errorf("mock oci bundle ref must contain 'org/repo' but got %q", ref) + } + return s.buildBundles(ref, policies) + } +} + +// Ready provides a channel that the server will use to gate readiness. The +// caller can provide this channel to prevent the server from becoming ready. +// The server will response with HTTP 500 responses until ready. The caller +// should close the channel to indicate readiness. +func Ready(ch chan struct{}) func(*Server) error { + return func(s *Server) error { + s.ready = ch + return nil + } +} + +// ParserOptions sets the ast.ParserOptions to use when parsing modules when preparing bundles. +func ParserOptions(popts ast.ParserOptions) func(*Server) error { + return func(s *Server) error { + s.parserOptions = popts + return nil + } +} + +// Server provides a mock HTTP server for testing the SDK and integrations. +type Server struct { + server *httptest.Server + ready chan struct{} + bundles map[string]map[string]string + rawBundles bool + parserOptions ast.ParserOptions +} + +// MustNewServer returns a new Server for test purposes or panics if an error occurs. +func MustNewServer(opts ...func(*Server) error) *Server { + s, err := NewServer(opts...) + if err != nil { + panic(err) + } + return s +} + +// NewServer returns a new Server for test purposes. +func NewServer(opts ...func(*Server) error) (*Server, error) { + s := &Server{ + bundles: map[string]map[string]string{}, + } + for _, opt := range opts { + if err := opt(s); err != nil { + return nil, err + } + } + if s.ready == nil { + s.ready = make(chan struct{}) + close(s.ready) + } + s.server = httptest.NewServer(http.HandlerFunc(s.handle)) + return s, nil +} + +func RawBundles(raw bool) func(*Server) error { + return func(s *Server) error { + s.rawBundles = raw + return nil + } +} + +func (s *Server) ParserOptions() ast.ParserOptions { + return s.parserOptions +} + +// WithTestBundle adds a bundle to the server at the specified endpoint. +func (s *Server) WithTestBundle(endpoint string, policies map[string]string) *Server { + s.bundles[endpoint] = policies + return s +} + +// Stop stops the test server. +func (s *Server) Stop() { + s.server.Close() +} + +// URL returns the base URL of the server. +func (s *Server) URL() string { + return s.server.URL +} + +// Builds the tarball from the supplied policies and prepares the layers in a temporary directory +func (s *Server) buildBundles(ref string, policies map[string]string) error { + // Prepare the modules to include in the bundle. Sort them so bundles are deterministic. + modules := make([]bundle.ModuleFile, 0, len(policies)) + for url, str := range policies { + module, err := ast.ParseModuleWithOpts(url, str, s.parserOptions) + if err != nil { + return fmt.Errorf("failed to parse module: %v", err) + } + modules = append(modules, bundle.ModuleFile{ + URL: url, + Parsed: module, + }) + } + sort.Slice(modules, func(i, j int) bool { + return modules[i].URL < modules[j].URL + }) + + // Compile the bundle out into a buffer + buf := bytes.NewBuffer(nil) + + // We need to explicitly set the global bundle rego-version, as an unassigned version will be + // interpreted as v0 on the receiving end, which will cause problems if modules are parsed/compiled + // as v1 on this end, which will drop 'rego.v1' and 'future.keywords' imports. + bundleManifest := bundle.Manifest{} + bundleManifest.SetRegoVersion(ast.DefaultRegoVersion) + bundleManifest.Init() + + err := compile.New().WithOutput(buf).WithBundle(&bundle.Bundle{ + Data: map[string]any{}, + Modules: modules, + Manifest: bundleManifest, + }).Build(context.Background()) + if err != nil { + return err + } + directoryName, err := os.MkdirTemp("", "oci-test-temp") + fmt.Println("Testing OCI temporary directory:", directoryName) + if err != nil { + return err + } + // Write buf tarball to layer + tarLayer := filepath.Join(directoryName, "tar.layer") + err = os.WriteFile(tarLayer, buf.Bytes(), 0655) + if err != nil { + return err + } + // Write empty config layer + configLayer := filepath.Join(directoryName, "config.layer") + err = os.WriteFile(configLayer, []byte("{}"), 0655) + if err != nil { + return err + } + // Calculate SHA and size and prepare manifest layer + tarSHA, err := getFileSHA(tarLayer) + if err != nil { + return err + } + + configSHA, err := getFileSHA(configLayer) + if err != nil { + return err + } + + var manifest ocispec.Manifest + manifest.SchemaVersion = 2 + manifest.Config = ocispec.Descriptor{ + MediaType: ocispec.MediaTypeImageConfig, + Digest: digest.Digest(fmt.Sprintf("sha256:%x", configSHA)), + Size: int64(2), // config size is set to 2 as an empty config is used + } + manifest.Layers = []ocispec.Descriptor{ + { + MediaType: ocispec.MediaTypeImageLayerGzip, + Digest: digest.Digest(fmt.Sprintf("sha256:%x", tarSHA)), + Size: int64(buf.Len()), + Annotations: map[string]string{ + ocispec.AnnotationTitle: ref, + ocispec.AnnotationCreated: time.Now().Format(time.RFC3339), + }, + }, + } + + manifestData, err := json.Marshal(manifest) + if err != nil { + return err + } + manifestLayer := filepath.Join(directoryName, "manifest.layer") + err = os.WriteFile(manifestLayer, manifestData, 0655) + if err != nil { + return err + } + + // Set ref layer paths to server bundles + s.bundles[ref] = map[string]string{ + "manifest": manifestLayer, + "config": configLayer, + "tar": tarLayer, + } + return nil +} + +func getFileSHA(filePath string) ([]byte, error) { + f, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer f.Close() + hash := sha256.New() + if _, err := io.Copy(hash, f); err != nil { + return nil, err + } + return hash.Sum(nil), nil +} + +func (s *Server) handle(w http.ResponseWriter, r *http.Request) { + + select { + case <-s.ready: + default: + w.WriteHeader(http.StatusInternalServerError) + return + } + + if strings.HasPrefix(r.URL.Path, "/v2") { + s.handleOCIBundles(w, r) + return + } + + if strings.HasPrefix(r.URL.Path, "/bundles") { + if s.rawBundles { + s.handleRawBundles(w, r) + } else { + s.handleBundles(w, r) + } + return + } + + w.WriteHeader(http.StatusInternalServerError) +} + +func (s *Server) handleOCIBundles(w http.ResponseWriter, r *http.Request) { + ref := "" // key used to detect layers from s.bundles + tag := "" // image tag used in request path verification + repo := "" // image repo used in request path verification + var buf bytes.Buffer + // get first key that matches request url pattern + for key := range s.bundles { + // extract tag + parsedRef := strings.Split(key, ":") + checkRef := strings.Split(parsedRef[0], "/") + // check if request path contains org and repository + if strings.Contains(r.URL.Path, checkRef[1]) && strings.Contains(r.URL.Path, checkRef[2]) { + ref = key + tag = parsedRef[1] + repo = checkRef[1] + "/" + checkRef[2] + break + } + } + if ref == "" || tag == "" || repo == "" { + w.WriteHeader(http.StatusBadRequest) + return + } + layers := s.bundles[ref] + fi, err := os.Stat(layers["manifest"]) + if err != nil { + w.WriteHeader(http.StatusFailedDependency) + return + } + manifestSize := fi.Size() + manifestSHA, err := getFileSHA(layers["manifest"]) + if err != nil { + w.WriteHeader(http.StatusFailedDependency) + return + } + fi, err = os.Stat(layers["config"]) + if err != nil { + w.WriteHeader(http.StatusFailedDependency) + return + } + configSize := fi.Size() + configSHA, err := getFileSHA(layers["config"]) + if err != nil { + w.WriteHeader(http.StatusFailedDependency) + return + } + fi, err = os.Stat(layers["tar"]) + if err != nil { + w.WriteHeader(http.StatusFailedDependency) + return + } + // get the size + tarSize := fi.Size() + tarSHA, err := getFileSHA(layers["tar"]) + if err != nil { + w.WriteHeader(http.StatusFailedDependency) + return + } + + if r.URL.Path == fmt.Sprintf("/v2/%s/manifests/%s", repo, tag) { + w.Header().Add("Content-Length", strconv.FormatInt(manifestSize, 10)) + w.Header().Add("Content-Type", "application/vnd.oci.image.manifest.v1+json") + w.Header().Add("Docker-Content-Digest", fmt.Sprintf("sha256:%x", manifestSHA)) + w.WriteHeader(http.StatusOK) + return + } + if r.URL.Path == fmt.Sprintf("/v2/%s/manifests/sha256:%x", repo, manifestSHA) { + w.Header().Add("Content-Length", strconv.FormatInt(manifestSize, 10)) + w.Header().Add("Content-Type", "application/vnd.oci.image.manifest.v1+json") + w.Header().Add("Docker-Content-Digest", fmt.Sprintf("sha256:%x", manifestSHA)) + w.WriteHeader(200) + bs, err := os.ReadFile(layers["manifest"]) + if err != nil { + w.WriteHeader(http.StatusNotFound) + return + } + buf.Write(bs) + _, err = w.Write(buf.Bytes()) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + } + return + } + if r.URL.Path == fmt.Sprintf("/v2/%s/blobs/sha256:%x", repo, configSHA) { + w.Header().Add("Content-Length", strconv.FormatInt(configSize, 10)) + w.Header().Add("Content-Type", "application/vnd.oci.image.manifest.v1+json") + w.Header().Add("Docker-Content-Digest", fmt.Sprintf("sha256:%x", configSHA)) + w.WriteHeader(200) + bs, err := os.ReadFile(layers["config"]) + if err != nil { + w.WriteHeader(http.StatusNotFound) + return + } + buf.Write(bs) + _, err = w.Write(buf.Bytes()) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + } + return + } + if r.URL.Path == fmt.Sprintf("/v2/%s/blobs/sha256:%x", repo, tarSHA) { + w.Header().Add("Content-Length", strconv.FormatInt(tarSize, 10)) + w.Header().Add("Content-Type", "application/vnd.oci.image.manifest.v1+json") + w.Header().Add("Docker-Content-Digest", fmt.Sprintf("sha256:%x", tarSHA)) + w.WriteHeader(200) + bs, err := os.ReadFile(layers["tar"]) + if err != nil { + w.WriteHeader(http.StatusNotFound) + return + } + buf.Write(bs) + _, err = w.Write(buf.Bytes()) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + } + return + } +} + +func (s *Server) handleBundles(w http.ResponseWriter, r *http.Request) { + + // Return 404 if bundle path does not exist. + b, ok := s.bundles[r.URL.Path] + if !ok { + w.WriteHeader(http.StatusNotFound) + return + } + + // Prepare a mapping to store bundle data + data := map[string]any{} + + // Prepare a manifest for use if a .manifest file exists. + var manifest bundle.Manifest + + // Prepare the modules to include in the bundle. Sort them so bundles are deterministic. + modules := make([]bundle.ModuleFile, 0, len(b)) + for url, str := range b { + switch { + case url == ".manifest": + err := json.Unmarshal([]byte(str), &manifest) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintf(w, "error unmarshaling .manifest file: %v", err) + return + } + case strings.HasSuffix(url, ".rego"): + module, err := ast.ParseModuleWithOpts(url, str, s.parserOptions) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(err.Error())) + return + } + modules = append(modules, bundle.ModuleFile{ + URL: url, + Parsed: module, + }) + case strings.HasSuffix(url, ".json"): + if strings.Contains(url, "/") { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintf(w, "nested data documents are not implemented in the dummy server: %s", url) + return + } + + var d map[string]any + + err := json.Unmarshal([]byte(str), &d) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintf(w, "error unmarshaling json file: %v", err) + return + } + + maps.Copy(data, d) + default: + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintf(w, "unexpected file in dummy bundle: %s", url) + return + } + } + sort.Slice(modules, func(i, j int) bool { + return modules[i].URL < modules[j].URL + }) + + // Compile the bundle out into a buffer + buf := bytes.NewBuffer(nil) + err := compile.New().WithOutput(buf).WithBundle(&bundle.Bundle{ + Data: data, + Modules: modules, + Manifest: manifest, + }).Build(r.Context()) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(err.Error())) + return + } + + // Write out the bundle + w.WriteHeader(http.StatusOK) + _, _ = io.Copy(w, buf) +} + +func (s *Server) handleRawBundles(w http.ResponseWriter, r *http.Request) { + // Return 404 if bundle path does not exist. + b, ok := s.bundles[r.URL.Path] + if !ok { + w.WriteHeader(http.StatusNotFound) + return + } + + files := make([][2]string, 0, len(b)) + for url, str := range b { + files = append(files, [2]string{url, str}) + } + buf := archive.MustWriteTarGz(files) + + // Write out the bundle + w.WriteHeader(http.StatusOK) + _, _ = io.Copy(w, buf) +} diff --git a/third_party/opa/v1/sdk/testdata/Makefile b/third_party/opa/v1/sdk/testdata/Makefile new file mode 100644 index 000000000000..1268401628e3 --- /dev/null +++ b/third_party/opa/v1/sdk/testdata/Makefile @@ -0,0 +1,7 @@ +disco.tar.gz: bundle/data.json + opa build bundle + mv bundle.tar.gz disco.tar.gz + +v1bundle.tar.gz: v1bundle/.manifest v1bundle/policy.rego + opa build --v1-compatible -o v1bundle.tar.gz -b v1bundle/ + diff --git a/third_party/opa/v1/sdk/testdata/bundle/data.json b/third_party/opa/v1/sdk/testdata/bundle/data.json new file mode 100644 index 000000000000..6ea528e494fd --- /dev/null +++ b/third_party/opa/v1/sdk/testdata/bundle/data.json @@ -0,0 +1,6 @@ +{ + "plugins": { + "test_plugin": { + } + } +} diff --git a/third_party/opa/v1/sdk/testdata/disco.tar.gz b/third_party/opa/v1/sdk/testdata/disco.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..4ffaffe18ef1da5ba760b2a36ab34dc2455605b3 GIT binary patch literal 122 zcmb2|=3oGW|Cjtdbv&<});f1Sd4Z$>2pAhMTxOfGrLl3NWfQNExrvE^aYBH>g1M6g z7Hrv}va#b}L(=XGw&pGlTZ0_^GON%xZixWh8 UngSh^8Gu&(|L?#R_z1)T0JmQ)4*&oF literal 0 HcmV?d00001 diff --git a/third_party/opa/v1/sdk/testdata/v1bundle.tar.gz b/third_party/opa/v1/sdk/testdata/v1bundle.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..9d7cce5ef2e707437eaeb9bb99c76520a6c98358 GIT binary patch literal 281 zcmV+!0p|W6iwFP!00000|LoPVPQx$|2H>ptDNgQ+k~3{OM9L5eMz(Hx#cIW_U0r^`lL!`S%`R|HWlFXliv)O165$A~S6%Q*SPQN^3hC*N|GQjah z`V<36pW(NA?SI@KF}$*KkC2>Eu@Mi^wni1bGkUqry;`{+?r|*tqMYsJe=1V`PotO1 zXSq_a`wbXtRVSk%kdYo=psJyhCMd=xaC@_8uA4=}CZH_&ZSSwWxLjvv$SY_qat5Rt2-5FydFpI|ExT?|MRPq|5F&aR~sF)v%KQ`l6&VU f@am5576QLd!A+7R`G?*D00960>5&Kr01yBG11^We literal 0 HcmV?d00001 diff --git a/third_party/opa/v1/sdk/testdata/v1bundle/.manifest b/third_party/opa/v1/sdk/testdata/v1bundle/.manifest new file mode 100644 index 000000000000..aa0f6db355f9 --- /dev/null +++ b/third_party/opa/v1/sdk/testdata/v1bundle/.manifest @@ -0,0 +1,3 @@ +{ + "roots": ["v1bundle"] +} \ No newline at end of file diff --git a/third_party/opa/v1/sdk/testdata/v1bundle/policy.rego b/third_party/opa/v1/sdk/testdata/v1bundle/policy.rego new file mode 100644 index 000000000000..fce2a458888d --- /dev/null +++ b/third_party/opa/v1/sdk/testdata/v1bundle/policy.rego @@ -0,0 +1,9 @@ +package v1bundle + +default authz := false + +# METADATA +# entrypoint: true +authz if { + input.role == "admin" +} diff --git a/third_party/opa/v1/server/authorizer/authorizer.go b/third_party/opa/v1/server/authorizer/authorizer.go new file mode 100644 index 000000000000..497b6065ac6c --- /dev/null +++ b/third_party/opa/v1/server/authorizer/authorizer.go @@ -0,0 +1,307 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package authorizer provides authorization handlers to the server. +package authorizer + +import ( + "context" + "net/http" + "net/url" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/server/identifier" + "github.com/open-policy-agent/opa/v1/server/types" + "github.com/open-policy-agent/opa/v1/server/writer" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/print" + "github.com/open-policy-agent/opa/v1/util" +) + +// Basic provides policy-based authorization over incoming requests. +type Basic struct { + inner http.Handler + compiler func() *ast.Compiler + store storage.Store + runtime *ast.Term + decision func() ast.Ref + printHook print.Hook + enablePrintStatements bool + interQueryCache cache.InterQueryCache + interQueryValueCache cache.InterQueryValueCache + urlPathExpectsBodyFunc []func(string, []any) bool +} + +// Runtime returns an argument that sets the runtime on the authorizer. +func Runtime(term *ast.Term) func(*Basic) { + return func(b *Basic) { + b.runtime = term + } +} + +// Decision returns an argument that sets the path of the authorization decision +// to query. +func Decision(ref func() ast.Ref) func(*Basic) { + return func(b *Basic) { + b.decision = ref + } +} + +// PrintHook sets the object to use for handling print statement outputs. +func PrintHook(printHook print.Hook) func(*Basic) { + return func(b *Basic) { + b.printHook = printHook + } +} + +// EnablePrintStatements enables print() calls. If this option is not provided, +// print() calls will be erased from the policy. This option only applies to +// queries and policies that passed as raw strings, i.e., this function will not +// have any affect if the caller supplies the ast.Compiler instance. +func EnablePrintStatements(yes bool) func(r *Basic) { + return func(b *Basic) { + b.enablePrintStatements = yes + } +} + +// InterQueryCache enables the inter-query cache on the authorizer +func InterQueryCache(interQueryCache cache.InterQueryCache) func(*Basic) { + return func(b *Basic) { + b.interQueryCache = interQueryCache + } +} + +// InterQueryValueCache enables the inter-query value cache on the authorizer +func InterQueryValueCache(interQueryValueCache cache.InterQueryValueCache) func(*Basic) { + return func(b *Basic) { + b.interQueryValueCache = interQueryValueCache + } +} + +// URLPathValidatorFuncs allows for extensions to the allowed paths an authorizer will accept. +func URLPathExpectsBodyFunc(urlPathExpectsBodyFunc []func(string, []any) bool) func(*Basic) { + return func(b *Basic) { + b.urlPathExpectsBodyFunc = urlPathExpectsBodyFunc + } +} + +// NewBasic returns a new Basic object. +func NewBasic(inner http.Handler, compiler func() *ast.Compiler, store storage.Store, opts ...func(*Basic)) http.Handler { + b := &Basic{ + inner: inner, + compiler: compiler, + store: store, + } + + for _, opt := range opts { + opt(b) + } + + return b +} + +func (b *Basic) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // TODO(tsandall): Pass AST value as input instead of Go value to avoid unnecessary + // conversions. + r, input, err := makeInput(r, b.urlPathExpectsBodyFunc) + if err != nil { + writer.ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err) + return + } + + rego := rego.New( + rego.Query(b.decision().String()), + rego.Compiler(b.compiler()), + rego.Store(b.store), + rego.Input(input), + rego.Runtime(b.runtime), + rego.EnablePrintStatements(b.enablePrintStatements), + rego.PrintHook(b.printHook), + rego.InterQueryBuiltinCache(b.interQueryCache), + rego.InterQueryBuiltinValueCache(b.interQueryValueCache), + ) + + rs, err := rego.Eval(r.Context()) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + if len(rs) == 0 { + // Authorizer was configured but no policy defined. This indicates an internal error or misconfiguration. + writer.Error(w, http.StatusInternalServerError, types.NewErrorV1(types.CodeInternal, types.MsgUnauthorizedUndefinedError)) + return + } + + switch allowed := rs[0].Expressions[0].Value.(type) { + case bool: + if allowed { + b.inner.ServeHTTP(w, r) + return + } + case map[string]any: + if decision, ok := allowed["allowed"]; ok { + if allow, ok := decision.(bool); ok && allow { + b.inner.ServeHTTP(w, r) + return + } + if reason, ok := allowed["reason"]; ok { + message, ok := reason.(string) + if ok { + writer.Error(w, http.StatusUnauthorized, types.NewErrorV1(types.CodeUnauthorized, message)) //nolint:govet + return + } + } + } else { + writer.Error(w, http.StatusInternalServerError, types.NewErrorV1(types.CodeInternal, types.MsgUndefinedError)) + return + } + } + writer.Error(w, http.StatusUnauthorized, types.NewErrorV1(types.CodeUnauthorized, types.MsgUnauthorizedError)) +} + +var emptyQuery = url.Values{} + +func makeInput(r *http.Request, extraPaths []func(string, []any) bool) (*http.Request, any, error) { + path, err := parsePath(r.URL.Path) + if err != nil { + return r, nil, err + } + + method := strings.ToUpper(r.Method) + + query := emptyQuery + if r.URL.RawQuery != "" { + query = r.URL.Query() + } + + var rawBody []byte + + if expectBody(r.Method, path) || checkExtraExpectedReqBodyPaths(extraPaths, r.Method, path) { + var err error + rawBody, err = util.ReadMaybeCompressedBody(r) + if err != nil { + return r, nil, err + } + } + + input := map[string]any{ + "path": path, + "method": method, + "params": query, + "headers": r.Header, + } + + if len(rawBody) > 0 { + var body any + if expectYAML(r) { + if err := util.Unmarshal(rawBody, &body); err != nil { + return r, nil, err + } + } else if err := util.UnmarshalJSON(rawBody, &body); err != nil { + return r, nil, err + } + + // We cache the parsed body on the context so the server does not have + // to parse the input document twice. + input["body"] = body + ctx := SetBodyOnContext(r.Context(), body) + r = r.WithContext(ctx) + } + + identity, ok := identifier.Identity(r) + if ok { + input["identity"] = identity + } + + clientCertificates, ok := identifier.ClientCertificates(r) + if ok { + input["client_certificates"] = clientCertificates + } + + return r, input, nil +} + +var dataAPIVersions = map[string]bool{ + "v0": true, + "v1": true, +} + +func expectBody(method string, path []any) bool { + if method == http.MethodPost { + if len(path) == 1 { + s := path[0].(string) + return s == "" + } else if len(path) >= 2 { + s1 := path[0].(string) + s2 := path[1].(string) + return dataAPIVersions[s1] && s2 == "data" + } + } + return false +} + +func checkExtraExpectedReqBodyPaths(validators []func(string, []any) bool, method string, path []any) bool { + for _, f := range validators { + if f(method, path) { + return true + } + } + return false +} + +func expectYAML(r *http.Request) bool { + // NOTE(tsandall): This check comes from the server's HTTP handler code. The docs + // are a bit more strict, but the authorizer should be consistent w/ the original + // server handler implementation. + return strings.Contains(r.Header.Get("Content-Type"), "yaml") +} + +func parsePath(path string) ([]any, error) { + if len(path) == 0 { + return []any{}, nil + } + parts := strings.Split(path[1:], "/") + for i := range parts { + var err error + parts[i], err = url.PathUnescape(parts[i]) + if err != nil { + return nil, err + } + } + sl := make([]any, len(parts)) + for i := range sl { + sl[i] = parts[i] + } + return sl, nil +} + +type authorizerCachedBody struct { + parsed any +} + +type authorizerCachedBodyKey string + +const ctxkey authorizerCachedBodyKey = "authorizerCachedBodyKey" + +// SetBodyOnContext adds the parsed input value to the context. This function is only +// exposed for test purposes. +func SetBodyOnContext(ctx context.Context, x any) context.Context { + return context.WithValue(ctx, ctxkey, authorizerCachedBody{ + parsed: x, + }) +} + +// GetBodyOnContext returns the parsed input from the request context if it exists. +// The authorizer saves the parsed input on the context when it runs. +func GetBodyOnContext(ctx context.Context) (any, bool) { + input, ok := ctx.Value(ctxkey).(authorizerCachedBody) + if !ok { + return nil, false + } + return input.parsed, true +} diff --git a/third_party/opa/v1/server/authorizer/authorizer_test.go b/third_party/opa/v1/server/authorizer/authorizer_test.go new file mode 100644 index 000000000000..643e4d02b4c2 --- /dev/null +++ b/third_party/opa/v1/server/authorizer/authorizer_test.go @@ -0,0 +1,572 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package authorizer + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/server/identifier" + "github.com/open-policy-agent/opa/v1/server/types" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/print" + "github.com/open-policy-agent/opa/v1/util" +) + +type mockHandler struct { +} + +type appendingPrintHook struct { + printed *[]string +} + +func (a appendingPrintHook) Print(_ print.Context, s string) error { + *a.printed = append(*a.printed, s) + return nil +} + +func (*mockHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(200) +} + +func TestBasic(t *testing.T) { + + // Policy for testing access to policies. + compiler := func() *ast.Compiler { + module := ` + package system.authz + + import data.system.tokens + + allow = resp if { + not undefined_case + } + + resp["allowed"] = allowed if { + not undefined_case + not wrong_object + } + + resp["reason"] = "custom reason" if { + input.path = ["reason"] + } + + resp["reason"] = 0 if { + input.path = ["reason", "wrong_type"] + } + + resp["foo"] = "bar" if { + wrong_object + } + + default allowed = false + + allowed = allow_inner if { + not undefined_case # undefined + not wrong_object # object response, wrong key + not input.path[0] = "reason" # custom reason + not conflict_error # eval errors + print("ok") + } + + undefined_case if { + input.path[0] = "undefined" + } + + wrong_object if { + input.path = ["reason", "wrong_object"] + } + + conflict_error if { + input.path[0] = "conflict_error" + {k: v | k = ["a", "a"][_]; [1, 2][v]} + } + + default allow_inner = false + + allow_inner if { + valid_method + valid_path + } + + valid_method if { + rights[_].access[_] = access_map[input.method] + } + + valid_path if { + rights[_].path = "*" + } + + valid_path if { + rights[_].path = input.path + } + + rights contains right if { + role = tokens[input.identity].roles[_] + right = all_rights[role][_] + } + + all_rights = { + "admin": [{ + "path": "*", + "access": ["read", "write"], + }], + "service_read_only_path": [ + { + "path": ["data", "some", "specific", "document"], + "access": ["read"], + }, + ], + "service_read_write_path": [ + { + "path": ["data", "some", "other", "document"], + "access": ["read", "write"], + }, + ], + } + + access_map = { + "GET": "read", + "HEAD": "read", + "PATCH": "write", + "POST": "write", + "PUT": "write", + "DELETE": "write", + } + ` + c := ast.NewCompiler().WithEnablePrintStatements(true) + c.Compile(map[string]*ast.Module{ + "test.rego": ast.MustParseModuleWithOpts(module, ast.ParserOptions{AllFutureKeywords: true}), + }) + if c.Failed() { + t.Fatalf("Unexpected error compiling test module: %v", c.Errors) + } + return c + } + + // Data used for testing authorizer access to storage. + data := util.MustUnmarshalJSON([]byte(` + { + "system": { + "tokens": { + "token0": { + "roles": ["admin"] + }, + "token1": { + "roles": ["service_read_only_path"] + }, + "token2": { + "roles": ["service_read_write_path"] + } + } + } + } + `)) + + store := inmem.NewFromObject(data.(map[string]any)) + + tests := []struct { + note string + identity string + method string + path string + expectedStatus int + expectedCode string + expectedMsg string + expectedPrint []string + }{ + {"root (ok)", "token0", http.MethodGet, "", http.StatusOK, "", "", []string{"ok"}}, + {"index.html (ok)", "token0", http.MethodGet, "/index.html", http.StatusOK, "", "", []string{"ok"}}, + {"undefined", "token0", http.MethodGet, "/undefined", http.StatusInternalServerError, types.CodeInternal, types.MsgUnauthorizedUndefinedError, []string{}}, + {"evaluation error", "token0", http.MethodGet, "/conflict_error", http.StatusInternalServerError, types.CodeInternal, types.MsgEvaluationError, []string{}}, + {"ok", "token1", http.MethodGet, "/data/some/specific/document", http.StatusOK, "", "", []string{"ok"}}, + {"ok (w/ query params)", "token1", http.MethodGet, "/data/some/specific/document?pretty=true", http.StatusOK, "", "", []string{"ok"}}, + {"unauthorized method", "token1", http.MethodPut, "/data/some/specific/document", http.StatusUnauthorized, types.CodeUnauthorized, types.MsgUnauthorizedError, []string{"ok"}}, + {"unauthorized path", "token2", http.MethodGet, "/data/some/doc/not/allowed", http.StatusUnauthorized, types.CodeUnauthorized, types.MsgUnauthorizedError, []string{"ok"}}, + {"unauthorized path (w/ query params)", "token2", http.MethodGet, "/data/some/doc/not/allowed?pretty=true", http.StatusUnauthorized, types.CodeUnauthorized, types.MsgUnauthorizedError, []string{"ok"}}, + {"custom reason", "token2", http.MethodGet, "/reason", http.StatusUnauthorized, types.CodeUnauthorized, "custom reason", []string{}}, + {"custom reason, wrong type", "token2", http.MethodGet, "/reason/wrong_type", http.StatusUnauthorized, types.CodeUnauthorized, types.MsgUnauthorizedError, []string{}}, + {"non-bool/obj response", "token2", http.MethodGet, "/reason/wrong_object", http.StatusInternalServerError, types.CodeInternal, types.MsgUndefinedError, []string{}}, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + + recorder := httptest.NewRecorder() + req, err := http.NewRequest(tc.method, "http://localhost:8181"+tc.path, nil) + if err != nil { + t.Fatalf("Unexpected error creating request for %v: %v", tc, err) + } + + if len(tc.identity) > 0 { + req = identifier.SetIdentity(req, tc.identity) + } + + var output []string + NewBasic( + &mockHandler{}, + compiler, + store, + EnablePrintStatements(true), + PrintHook(appendingPrintHook{printed: &output}), + Decision(func() ast.Ref { + return ast.MustParseRef("data.system.authz.allow") + }), + ).ServeHTTP(recorder, req) + + if recorder.Code != tc.expectedStatus { + t.Fatalf("Expected status code %v but got: %v", tc.expectedStatus, recorder) + } + + if !Equal(tc.expectedPrint, output) { + t.Errorf("Expected output %v, got %v", tc.expectedPrint, output) + } + + // Check code/message if response should be error. + if tc.expectedStatus != http.StatusOK { + var x any + if err := util.NewJSONDecoder(recorder.Body).Decode(&x); err != nil { + t.Fatalf("Expected JSON response but got: %v", recorder) + } + response := ast.MustInterfaceToValue(x) + code, err := response.Find(ast.RefTerm(ast.StringTerm("code")).Value.(ast.Ref)) + if err != nil { + t.Fatalf("Missing code in response: %v", recorder) + } else if code.Compare(ast.String(tc.expectedCode)) != 0 { + t.Fatalf("Expected code %v but got: %v", tc.expectedCode, recorder) + } + + msg, err := response.Find(ast.RefTerm(ast.StringTerm("message")).Value.(ast.Ref)) + if err != nil { + t.Fatalf("Missing message in response: %v", recorder) + } else if !strings.Contains(msg.String(), tc.expectedMsg) { + t.Fatalf("Expected msg to contain %v but got: %v", tc.expectedMsg, response) + } + } + }) + } +} + +func TestBasicEscapeError(t *testing.T) { + + recorder := httptest.NewRecorder() + req, err := http.NewRequest(http.MethodGet, "http://localhost:8181", nil) + if err != nil { + t.Fatal(err) + } + + req.URL.Path = `/invalid/path/foo%LALALA` + + store := inmem.New() + + NewBasic(&mockHandler{}, ast.NewCompiler, store).ServeHTTP(recorder, req) + + if recorder.Code != http.StatusBadRequest { + t.Fatalf("Expected bad request but got: %v", recorder) + } + + var response types.ErrorV1 + + if err := json.NewDecoder(recorder.Body).Decode(&response); err != nil { + t.Fatalf("Expected error response but got: %v", recorder) + } + + if response.Code != types.CodeInvalidParameter || + !strings.Contains(response.Message, "invalid URL") { + t.Fatalf("Expected invalid parameter and URL parse error but got: %v", recorder) + } +} + +func TestMakeInput(t *testing.T) { + path := "/foo/bar?pretty=true&explain=\"full\"" + req, err := http.NewRequest(http.MethodGet, "http://localhost:8181"+path, nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Add("x-custom", "foo") + req.Header.Add("X-custom", "bar") + req.Header.Add("x-custom-2", "baz") + req.Header.Add("custom-header-3?", "wat") + + query := req.URL.Query() + + // set query parameters + query.Set("pretty", "true") + query.Set("explain", "full") + req.URL.RawQuery = query.Encode() + + req = identifier.SetIdentity(req, "bob") + + _, result, err := makeInput(req, nil) + if err != nil { + t.Fatal(err) + } + + expectedResult := util.MustUnmarshalJSON([]byte(` + { + "path": ["foo","bar"], + "method": "GET", + "identity": "bob", + "headers": { + "X-Custom": ["foo", "bar"], + "X-Custom-2": ["baz"], + "custom-header-3?": ["wat"] + }, + "params": {"explain": ["full"], "pretty": ["true"]} + } + `)) + + if !bytes.Equal(util.MustMarshalJSON(expectedResult), util.MustMarshalJSON(result)) { + t.Fatalf("Expected %+v but got %+v", expectedResult, result) + } +} + +func TestMakeInputWithBody(t *testing.T) { + reqs := []struct { + method string + path string + headers map[string]string + body string + extraPaths []func(string, []any) bool + useYAML bool + assertBodyExists bool + assertBodyDoesNotExist bool + }{ + { + method: "POST", + path: "/", + body: `{"foo": "bar"}`, + assertBodyExists: true, + }, + { + method: "POST", + path: "/", + body: `foo: bar`, + useYAML: true, + assertBodyExists: true, + }, + { + method: "POST", + path: "/v0/data", + body: `{"foo": "bar"}`, + assertBodyExists: true, + }, + { + method: "POST", + path: "/v1/data", + body: `{"foo": "bar"}`, + assertBodyExists: true, + }, + { + method: "PUT", + path: "/v1/data", + body: `{"foo": "bar"}`, + assertBodyDoesNotExist: true, + }, + { + method: "PATCH", + path: "/v1/data", + body: `{"foo": "bar"}`, + assertBodyDoesNotExist: true, + }, + { + method: "GET", + path: "/v1/data", + assertBodyDoesNotExist: true, + }, + { + method: "PUT", + path: "/v1/policies/test", + body: "package test\np = 7", + assertBodyDoesNotExist: true, + }, + { + method: "PUT", + path: "/v1/example-plugin", + body: `{"example": "body must still be yaml or json"}`, + extraPaths: []func(string, []any) bool{func(method string, path []any) bool { + s1 := path[0].(string) + s2 := path[1].(string) + return dataAPIVersions[s1] && s2 == "example-plugin" + }}, + }, + } + + for _, tc := range reqs { + + t.Run(tc.method+"_"+tc.path, func(t *testing.T) { + + req, err := http.NewRequest(tc.method, "http://localhost:8181"+tc.path, bytes.NewBufferString(tc.body)) + if err != nil { + t.Fatal(err) + } + + if tc.useYAML { + req.Header.Set("Content-Type", "application/x-yaml") + } + + req, input, err := makeInput(req, tc.extraPaths) + if err != nil { + t.Fatal(err) + } + + if tc.assertBodyExists { + + var want any + + if tc.useYAML { + if err := util.Unmarshal([]byte(tc.body), &want); err != nil { + t.Fatal(err) + } + } else { + want = util.MustUnmarshalJSON([]byte(tc.body)) + } + + body := input.(map[string]any)["body"] + + if !reflect.DeepEqual(body, want) { + t.Fatalf("expected parsed bodies to be equal but got %v and want %v", body, want) + } + + body, ok := GetBodyOnContext(req.Context()) + if !ok || !reflect.DeepEqual(body, want) { + t.Fatalf("expected parsed body to be cached on context but got %v and want %v", body, want) + } + } + + if tc.assertBodyDoesNotExist { + _, ok := input.(map[string]any)["body"] + if ok { + t.Fatal("expected no parsed body in input") + } + _, ok = GetBodyOnContext(req.Context()) + if ok { + t.Fatal("expected no parsed body to be cached on context") + } + } + + }) + + } + +} + +func TestInterQueryCache(t *testing.T) { + + count := 0 + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + count++ + })) + + t.Cleanup(func() { + ts.Close() + }) + + compiler := func() *ast.Compiler { + module := fmt.Sprintf(` + package system.authz + import rego.v1 + + allow if { + http.send({ + "method": "GET", + "url": "%v", + "force_cache": true, + "force_cache_duration_seconds": 60 + }).status_code == 200 + } + `, ts.URL) + c := ast.NewCompiler() + c.Compile(map[string]*ast.Module{ + "test.rego": ast.MustParseModule(module), + }) + if c.Failed() { + t.Fatalf("Unexpected error compiling test module: %v", c.Errors) + } + return c + } + + recorder := httptest.NewRecorder() + req, err := http.NewRequest(http.MethodGet, "http://localhost:8181/v1/data", nil) + if err != nil { + t.Fatal(err) + } + + config, _ := cache.ParseCachingConfig(nil) + interQueryCache := cache.NewInterQueryCache(config) + + basic := NewBasic(&mockHandler{}, compiler, inmem.New(), InterQueryCache(interQueryCache), Decision(func() ast.Ref { + return ast.MustParseRef("data.system.authz.allow") + })) + + // Execute the policy twice + basic.ServeHTTP(recorder, req) + basic.ServeHTTP(recorder, req) + + // And make sure the test server was only hit once + if count != 1 { + t.Error("Expected http.send response to be cached") + } +} + +func TestInterQueryValueCache(t *testing.T) { + + compiler := func() *ast.Compiler { + module := ` + package system.authz + import rego.v1 + + allow if { + regex.match("foo.*", "foobar") + }` + c := ast.NewCompiler() + c.Compile(map[string]*ast.Module{ + "test.rego": ast.MustParseModule(module), + }) + if c.Failed() { + t.Fatalf("Unexpected error compiling test module: %v", c.Errors) + } + return c + } + + recorder := httptest.NewRecorder() + req, err := http.NewRequest(http.MethodGet, "http://localhost:8181/v1/data", nil) + if err != nil { + t.Fatal(err) + } + + config, _ := cache.ParseCachingConfig(nil) + interQueryValueCache := cache.NewInterQueryValueCache(context.Background(), config) + + basic := NewBasic(&mockHandler{}, compiler, inmem.New(), InterQueryValueCache(interQueryValueCache), Decision(func() ast.Ref { + return ast.MustParseRef("data.system.authz.allow") + })) + + // Execute the policy + basic.ServeHTTP(recorder, req) +} + +func Equal(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true +} diff --git a/third_party/opa/v1/server/buffer.go b/third_party/opa/v1/server/buffer.go new file mode 100644 index 000000000000..47ac5924b02d --- /dev/null +++ b/third_party/opa/v1/server/buffer.go @@ -0,0 +1,47 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package server + +import ( + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/topdown" +) + +// Info contains information describing a policy decision. +type Info struct { + Txn storage.Transaction + Revision string // Deprecated: Use `Bundles` instead + Bundles map[string]BundleInfo + DecisionID string + BatchDecisionID string + TraceID string + SpanID string + RemoteAddr string + HTTPRequestContext logging.HTTPRequestContext + Query string + Path string + Timestamp time.Time + Input *any + InputAST ast.Value + Results *any + IntermediateResults map[string]any + MappedResults *any + NDBuiltinCache *any + Error error + Metrics metrics.Metrics + Trace []*topdown.Event + RequestID uint64 + Custom map[string]any +} + +// BundleInfo contains information describing a bundle. +type BundleInfo struct { + Revision string +} diff --git a/third_party/opa/v1/server/cache.go b/third_party/opa/v1/server/cache.go new file mode 100644 index 000000000000..c56ac2e085c8 --- /dev/null +++ b/third_party/opa/v1/server/cache.go @@ -0,0 +1,53 @@ +package server + +import "sync" + +type cache struct { + data map[string]any + keylist []string + idx int + maxSize int + mtx sync.RWMutex +} + +func newCache(maxSize int) *cache { + return &cache{ + data: map[string]any{}, + keylist: []string{}, + maxSize: maxSize, + } +} + +func (c *cache) Get(k string) (any, bool) { + c.mtx.RLock() + v, ok := c.data[k] + c.mtx.RUnlock() + return v, ok +} + +func (c *cache) Insert(k string, v any) { + + // Short path if its already in the cache + _, ok := c.Get(k) + if ok { + return + } + + // Slow path, grab the write lock and insert + c.mtx.Lock() + _, ok = c.data[k] + if !ok { + c.data[k] = v + if len(c.keylist) < c.maxSize { + // Haven't reached max size yet, keep adding keys. + c.keylist = append(c.keylist, k) + } else { + // Start recycling spots in the key list and + // dropping cache entries for them. + delete(c.data, c.keylist[c.idx]) + c.keylist[c.idx] = k + c.idx = (c.idx + 1) % c.maxSize + } + } + c.mtx.Unlock() +} diff --git a/third_party/opa/v1/server/cache_test.go b/third_party/opa/v1/server/cache_test.go new file mode 100644 index 000000000000..06bd22ce12eb --- /dev/null +++ b/third_party/opa/v1/server/cache_test.go @@ -0,0 +1,79 @@ +package server + +import ( + "strconv" + "testing" +) + +func TestCacheBase(t *testing.T) { + c := newCache(5) + foo := struct{}{} + c.Insert("foo", foo) + ensureCacheKey(t, c, "foo", foo) +} + +func TestCacheLimit(t *testing.T) { + max := 10 + c := newCache(max) + + // Fill the cache with values + var i int + //nolint:intrange + for i = 0; i < max; i++ { + c.Insert(strconv.Itoa(i), i) + } + + // Ensure its at the max size + ensureCacheSize(t, c, max) + + // Ensure they are all stored.. + for j := range max { + ensureCacheKey(t, c, strconv.Itoa(j), j) + } + + // Continue filling the cache, expect old keys to be dropped + c.Insert(strconv.Itoa(i), i) + ensureCacheKey(t, c, strconv.Itoa(i), i) + + // Should still be at max size + ensureCacheSize(t, c, max) + + // Expect that "0" got dropped + _, ok := c.Get("0") + if ok { + t.Fatal("Expected key '0' to not be found") + } + + // Load the cache with many more than max + for ; i < max*20; i++ { + c.Insert(strconv.Itoa(i), i) + } + ensureCacheSize(t, c, max) + + // Ensure the last set of "max" number are available, and everything else is not + for j := range i { + k := strconv.Itoa(j) + if j >= (i - max) { + ensureCacheKey(t, c, k, j) + } else { + _, ok := c.Get(k) + if ok { + t.Fatalf("Expected key %s to not be found", k) + } + } + } +} + +func ensureCacheKey(t *testing.T, c *cache, k string, v any) { + t.Helper() + actual, ok := c.Get(k) + if !ok || v != actual { + t.Fatalf("expected to retrieve value %v for key %s, got %v ok==%t", v, k, actual, ok) + } +} + +func ensureCacheSize(t *testing.T, c *cache, size int) { + if len(c.data) != size && len(c.keylist) != size { + t.Fatalf("Unexpected cache size len(data)=%d len(keylist)=%d, expected %d", size, len(c.data), len(c.keylist)) + } +} diff --git a/third_party/opa/v1/server/certs.go b/third_party/opa/v1/server/certs.go new file mode 100644 index 000000000000..f0889b21cceb --- /dev/null +++ b/third_party/opa/v1/server/certs.go @@ -0,0 +1,213 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package server + +import ( + "bytes" + "crypto/sha256" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io" + "os" + "time" + + "github.com/fsnotify/fsnotify" + + "github.com/open-policy-agent/opa/internal/pathwatcher" + "github.com/open-policy-agent/opa/v1/logging" +) + +func (s *Server) getCertificate(_ *tls.ClientHelloInfo) (*tls.Certificate, error) { + s.tlsConfigMtx.RLock() + defer s.tlsConfigMtx.RUnlock() + return s.cert, nil +} + +// reloadTLSConfig reloads the TLS config if the cert, key files or cert pool contents have changed. +func (s *Server) reloadTLSConfig(logger logging.Logger) error { + s.tlsConfigMtx.Lock() + defer s.tlsConfigMtx.Unlock() + + // reloading of the certificate key pair and the CA pool are independent operations, + // though errors from either operation are aggregated. + var errs error + + // if the server has a cert configured, then we need to check the cert and key for changes. + if s.certFile != "" { + newCert, certFileHash, certKeyFileHash, updated, err := reloadCertificateKeyPair( + s.certFile, + s.certKeyFile, + s.certFileHash, + s.certKeyFileHash, + logger, + ) + if err != nil { + errs = errors.Join(errs, err) + } else if updated { + s.cert = newCert + s.certFileHash = certFileHash + s.certKeyFileHash = certKeyFileHash + + logger.Debug("Refreshed server certificate.") + } + } + + // if the server has a cert pool configured, also attempt to reload this + if s.certPoolFile != "" { + pool, certPoolFileHash, updated, err := reloadCertificatePool(s.certPoolFile, s.certPoolFileHash, logger) + if err != nil { + errs = errors.Join(errs, err) + } else if updated { + s.certPool = pool + s.certPoolFileHash = certPoolFileHash + logger.Debug("Refreshed server CA certificate pool.") + } + } + + return errs +} + +// reloadCertificatePool loads the CA cert pool from the given file and returns a new pool if the file has changed. +func reloadCertificatePool(certPoolFile string, certPoolFileHash []byte, _ logging.Logger) (*x509.CertPool, []byte, bool, error) { + certPoolHash, err := hash(certPoolFile) + if err != nil { + return nil, nil, false, fmt.Errorf("failed to hash CA cert pool file: %w", err) + } + + if bytes.Equal(certPoolFileHash, certPoolHash) { + return nil, nil, false, nil + } + caCertPEM, err := os.ReadFile(certPoolFile) + if err != nil { + return nil, nil, false, fmt.Errorf("failed to read CA cert pool file %q: %w", certPoolFile, err) + } + + pool := x509.NewCertPool() + if ok := pool.AppendCertsFromPEM(caCertPEM); !ok { + return nil, nil, false, fmt.Errorf("failed to load CA cert pool file %q", certPoolFile) + } + + return pool, certPoolHash, true, nil +} + +// reloadCertificateKeyPair loads the certificate and key from the given files and returns a new certificate if either +// file has changed. +func reloadCertificateKeyPair( + certFile, certKeyFile string, + certFileHash, certKeyFileHash []byte, + logger logging.Logger, +) (*tls.Certificate, []byte, []byte, bool, error) { + certHash, err := hash(certFile) + if err != nil { + return nil, nil, nil, false, fmt.Errorf("failed to hash server certificate file: %w", err) + } + + certKeyHash, err := hash(certKeyFile) + if err != nil { + return nil, nil, nil, false, fmt.Errorf("failed to hash server key file: %w", err) + } + + differentCert := !bytes.Equal(certFileHash, certHash) + differentKey := !bytes.Equal(certKeyFileHash, certKeyHash) + + if differentCert && !differentKey { + logger.Warn("Server certificate file changed but server key file did not change.") + } + if !differentCert && differentKey { + logger.Warn("Server key file changed but server certificate file did not change.") + } + + if !differentCert && !differentKey { + return nil, nil, nil, false, nil + } + + newCert, err := tls.LoadX509KeyPair(certFile, certKeyFile) + if err != nil { + return nil, nil, nil, false, fmt.Errorf("server certificate key pair was not updated, update failed: %w", err) + } + + return &newCert, certHash, certKeyHash, true, nil +} + +func (s *Server) certLoopPolling(logger logging.Logger) Loop { + return func() error { + for range time.NewTicker(s.certRefresh).C { + err := s.reloadTLSConfig(logger) + if err != nil { + logger.Error(fmt.Sprintf("Failed to reload TLS config: %s", err)) + } + } + + return nil + } +} + +func (s *Server) certLoopNotify(logger logging.Logger) Loop { + return func() error { + + var paths []string + + // if a cert file is set, then we want to watch the cert and key + if s.certFile != "" { + paths = append(paths, s.certFile, s.certKeyFile) + } + + // if a cert pool file is set, then we want to watch the cert pool. This might be set without the cert and key + // being set too. + if s.certPoolFile != "" { + paths = append(paths, s.certPoolFile) + } + + watcher, err := pathwatcher.CreatePathWatcher(paths) + if err != nil { + return fmt.Errorf("failed to create tls path watcher: %w", err) + } + + for evt := range watcher.Events { + removalMask := fsnotify.Remove | fsnotify.Rename + mask := fsnotify.Create | fsnotify.Write | removalMask + if (evt.Op & mask) == 0 { + continue + } + + // retry logic here handles cases where the files are still being written to as events are triggered. + retries := 0 + for { + err = s.reloadTLSConfig(s.manager.Logger()) + if err == nil { + logger.Info("TLS config reloaded") + break + } + + retries++ + if retries >= 5 { + logger.Error("Failed to reload TLS config after retrying: %s", err) + break + } + + time.Sleep(100 * time.Millisecond) + } + } + + return nil + } +} + +func hash(file string) ([]byte, error) { + f, err := os.Open(file) + if err != nil { + return nil, err + } + defer f.Close() + + h := sha256.New() + if _, err := io.Copy(h, f); err != nil { + return nil, err + } + + return h.Sum(nil), nil +} diff --git a/third_party/opa/v1/server/doc.go b/third_party/opa/v1/server/doc.go new file mode 100644 index 000000000000..378194cd4f19 --- /dev/null +++ b/third_party/opa/v1/server/doc.go @@ -0,0 +1,6 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package server contains the policy engine's server handlers. +package server diff --git a/third_party/opa/v1/server/features.go b/third_party/opa/v1/server/features.go new file mode 100644 index 000000000000..3b0153722087 --- /dev/null +++ b/third_party/opa/v1/server/features.go @@ -0,0 +1,10 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build opa_wasm +// +build opa_wasm + +package server + +import _ "github.com/open-policy-agent/opa/v1/features/wasm" diff --git a/third_party/opa/v1/server/handlers/compress.go b/third_party/opa/v1/server/handlers/compress.go new file mode 100644 index 000000000000..583dd1828e64 --- /dev/null +++ b/third_party/opa/v1/server/handlers/compress.go @@ -0,0 +1,194 @@ +package handlers + +import ( + "compress/gzip" + "fmt" + "io" + "net/http" + "strings" + "sync" +) + +const ( + acceptEncodingHeader = "Accept-Encoding" + contentEncodingHeader = "Content-Encoding" + contentLengthHeader = "Content-Length" + gzipEncodingValue = "gzip" +) + +// This handler applies only for data and compile endpoints, for selected HTTP methods +// +// If the client asked for a gzip response, this handler will buffer the response and +// wait until it reached a certain threshold. If the threshold is not hit, the uncompressed response is sent +// +// If a gzip response is not asked by the client, it'll send the uncompressed response +// +// The threshold and the gzip compression level can be modified from server's configuration + +func CompressHandler(handler http.Handler, gzipMinLength int, gzipCompressionLevel int) http.Handler { + initGzipPool(gzipCompressionLevel) + + return http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) { + enabledForEndpoint := isDataEndpoint(request) || isCompileEndpoint(request) + if !enabledForEndpoint { + handler.ServeHTTP(responseWriter, request) + return + } + + responseWriter.Header().Add("Vary", acceptEncodingHeader) + + if !gzipHeaderDetected(request.Header) { + handler.ServeHTTP(responseWriter, request) + return + } + + crw := &compressResponseWriter{ + ResponseWriter: responseWriter, + headerWritten: false, + minlength: gzipMinLength, + } + defer crw.Close() + handler.ServeHTTP(crw, request) + }) +} + +type compressResponseWriter struct { + gzipWriter *gzip.Writer + http.ResponseWriter + buffer []byte + statusCode int + headerWritten bool + minlength int +} + +var gzipPool *sync.Pool + +func initGzipPool(compressionLevel int) { + if gzipPool == nil { + gzipPool = &sync.Pool{ + New: func() any { + writer, _ := gzip.NewWriterLevel(io.Discard, compressionLevel) + return writer + }, + } + } +} + +func (w *compressResponseWriter) WriteHeader(statusCode int) { + // save the status code for later use + w.statusCode = statusCode +} + +func (w *compressResponseWriter) Write(bytes []byte) (int, error) { + if w.isGzipInitialized() { + return w.gzipWriter.Write(bytes) + } + + // accumulate the buffer + w.buffer = append(w.buffer, bytes...) + + // if the buffer is above threshold, use compression + if len(w.buffer) >= w.minlength { + err := w.doCompressedResponse() + if err != nil { + return 0, err + } + return len(bytes), nil + } + + // wait for more data + return len(bytes), nil +} + +func (w *compressResponseWriter) Flush() { + if w.isGzipInitialized() { + w.gzipWriter.Flush() + flusher, canFlush := w.ResponseWriter.(http.Flusher) + if canFlush { + flusher.Flush() + } + } +} + +func (w *compressResponseWriter) Close() error { + if !w.isGzipInitialized() { + // gzip didn't handle the response, send it plain + err := w.doUncompressedResponse() + if err != nil { + err = fmt.Errorf("error writing uncompressed data: %v", err.Error()) + } + return err + } + + err := w.gzipWriter.Close() + defer gzipPool.Put(w.gzipWriter) + w.gzipWriter = nil + return err +} + +func (w *compressResponseWriter) doCompressedResponse() error { + w.ResponseWriter.Header().Set(contentEncodingHeader, gzipEncodingValue) + w.Header().Del(contentLengthHeader) + w.writeHeader() + // there's nothing to write + if len(w.buffer) == 0 { + return nil + } + gzipWriter := gzipPool.Get().(*gzip.Writer) + gzipWriter.Reset(w.ResponseWriter) + w.gzipWriter = gzipWriter + _, err := w.gzipWriter.Write(w.buffer) + return err +} + +func (w *compressResponseWriter) doUncompressedResponse() error { + w.writeHeader() + // there's nothing to write + if w.buffer == nil { + return nil + } + _, err := w.ResponseWriter.Write(w.buffer) + w.buffer = nil + return err +} + +func (w *compressResponseWriter) isGzipInitialized() bool { + return w.gzipWriter != nil +} + +func (w *compressResponseWriter) writeHeader() { + if !w.headerWritten && w.statusCode != 0 { + w.ResponseWriter.WriteHeader(w.statusCode) + w.headerWritten = true + } +} + +func isDataEndpoint(req *http.Request) bool { + isPostOrGetMethod := isPostMethod(req) || isGetMethod(req) + isV1rV0 := strings.HasPrefix(req.URL.Path, "/v1/data") || strings.HasPrefix(req.URL.Path, "/v0/data") + return isPostOrGetMethod && isV1rV0 +} + +func isCompileEndpoint(req *http.Request) bool { + return isPostMethod(req) && strings.HasPrefix(req.URL.Path, "/v1/compile") +} + +func isPostMethod(req *http.Request) bool { + return req.Method == "POST" +} + +func isGetMethod(req *http.Request) bool { + return req.Method == "GET" +} + +func gzipHeaderDetected(header http.Header) bool { + a := header.Get("Accept-Encoding") + parts := strings.Split(a, ",") + for _, part := range parts { + part = strings.TrimSpace(part) + if part == gzipEncodingValue || strings.HasPrefix(part, gzipEncodingValue+";") { + return true + } + } + return false +} diff --git a/third_party/opa/v1/server/handlers/compress_test.go b/third_party/opa/v1/server/handlers/compress_test.go new file mode 100644 index 000000000000..525c97957b11 --- /dev/null +++ b/third_party/opa/v1/server/handlers/compress_test.go @@ -0,0 +1,182 @@ +package handlers + +import ( + "bytes" + "compress/gzip" + "io" + "log" + "net/http" + "net/http/httptest" + "net/url" + "testing" +) + +const ( + gzipEncoding = "gzip" + requestBody = "Hello World!\n" +) + +var defaultCompressionLevel = gzip.BestCompression + +type compressHandlerTestScenario struct { + path string + method string + acceptEncoding string + gzipMinSize int + expectedCompressedResponse bool +} + +func executeRequest(w *httptest.ResponseRecorder, testScenario compressHandlerTestScenario) { + CompressHandler(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + _, err := io.WriteString(w, requestBody) + if err != nil { + log.Fatalf("Error writing the request body: %v", err) + } + }), testScenario.gzipMinSize, defaultCompressionLevel).ServeHTTP(w, &http.Request{ + URL: &url.URL{Path: testScenario.path}, + Method: testScenario.method, + Header: http.Header{ + "Accept-Encoding": []string{testScenario.acceptEncoding}, + }, + }) +} + +func TestCompressHandlerWithGzipOnInScopeEndpoints(t *testing.T) { + tests := map[string]compressHandlerTestScenario{ + "v0PostDataCompressed": { + path: "/v0/data", + method: "POST", + acceptEncoding: gzipEncoding, + gzipMinSize: 1, + expectedCompressedResponse: true, + }, + "v1PostDataCompressed": { + path: "/v1/data", + method: "POST", + acceptEncoding: gzipEncoding, + gzipMinSize: 1, + expectedCompressedResponse: true, + }, + "v1PostCompileCompressed": { + path: "/v1/compile", + method: "POST", + acceptEncoding: gzipEncoding, + gzipMinSize: 1, + expectedCompressedResponse: true, + }, + "v0PostDataUncompressed": { + path: "/v0/data", + method: "POST", + acceptEncoding: gzipEncoding, + gzipMinSize: 1024, + expectedCompressedResponse: false, + }, + "v1PostDataUncompressed": { + path: "/v1/data", + method: "POST", + acceptEncoding: gzipEncoding, + gzipMinSize: 1024, + expectedCompressedResponse: false, + }, + "v1PostCompileUncompressed": { + path: "/v1/compile", + method: "POST", + acceptEncoding: gzipEncoding, + gzipMinSize: 1024, + expectedCompressedResponse: false, + }, + "v1PostCompileAcceptEncodingAll": { + path: "/v1/compile", + method: "POST", + acceptEncoding: "*/*", + gzipMinSize: 1024, + expectedCompressedResponse: false, + }, + } + for name, ts := range tests { + w := httptest.NewRecorder() + executeRequest(w, ts) + if w.Result().Header.Get("Vary") != "Accept-Encoding" { + t.Error("missing the Vary header") + } + contentEncodingValue := w.Result().Header.Get("Content-Encoding") + if ts.expectedCompressedResponse { + if contentEncodingValue != gzipEncoding { + t.Errorf("wrong content encoding, got %q want %q", contentEncodingValue, gzipEncoding) + } + expectedLength := len(zipString(requestBody)) + receivedLength := w.Body.Len() + if receivedLength != expectedLength { + t.Errorf("test: %s wrong len, got %d want %d", name, w.Body.Len(), expectedLength) + } + receivedBody := unzip(w.Body.Bytes()) + if receivedBody != requestBody { + t.Errorf("test: %s wrong body, got %v, want %v", name, receivedBody, requestBody) + } + } else { + if contentEncodingValue == gzipEncoding { + t.Errorf("wrong content encoding, got %q want %q", contentEncodingValue, "") + } + expectedLength := len(requestBody) + receivedLength := w.Body.Len() + if receivedLength != expectedLength { + t.Errorf("test: %s wrong len, got %d want %d", name, w.Body.Len(), expectedLength) + } + receivedBody := w.Body.String() + if receivedBody != requestBody { + t.Errorf("test: %s wrong body, got %v, want %v", name, receivedBody, requestBody) + } + } + } +} + +func TestHandlerOnEndpointsWithoutCompression(t *testing.T) { + testScenario := compressHandlerTestScenario{ + path: "/metrics", + method: "GET", + acceptEncoding: gzipEncoding, + gzipMinSize: 1, + } + w := httptest.NewRecorder() + executeRequest(w, testScenario) + contentEncodingValue := w.Result().Header.Get("Content-Encoding") + if contentEncodingValue != "" { + t.Errorf("wrong content encoding, got %q want %q", contentEncodingValue, gzipEncoding) + } + + expectedLength := len(requestBody) + receivedLength := w.Body.Len() + if receivedLength != expectedLength { + t.Errorf("wrong len, got %d want %d", w.Body.Len(), expectedLength) + } +} + +func zipString(input string) []byte { + var b bytes.Buffer + gz := gzip.NewWriter(&b) + if _, err := gz.Write([]byte(input)); err != nil { + log.Fatal(err) + } + if err := gz.Close(); err != nil { + log.Fatal(err) + } + return b.Bytes() +} + +func unzip(body []byte) string { + reader := bytes.NewReader(body) + gzReader, err := gzip.NewReader(reader) + if err != nil { + log.Fatalf("Unexpected gzip error: %v", err) + } + plainOutput, err := io.ReadAll(gzReader) + if err != nil { + log.Fatalf("Unexpected gzip error: %v", err) + } + err = gzReader.Close() + if err != nil { + log.Fatalf("Unexpected gzip close err: %v", err) + } + return string(plainOutput) +} diff --git a/third_party/opa/v1/server/handlers/decoding.go b/third_party/opa/v1/server/handlers/decoding.go new file mode 100644 index 000000000000..3dae717fd705 --- /dev/null +++ b/third_party/opa/v1/server/handlers/decoding.go @@ -0,0 +1,53 @@ +package handlers + +import ( + "net/http" + "strings" + + "github.com/open-policy-agent/opa/v1/server/types" + "github.com/open-policy-agent/opa/v1/server/writer" + util_decoding "github.com/open-policy-agent/opa/v1/util/decoding" +) + +// This handler provides hard limits on the size of the request body, for both +// the raw body content, and also for the decompressed size when gzip +// compression is used. +// +// The Content-Length restriction happens here in the handler, but the +// decompressed size limit is enforced later, in `util.ReadMaybeCompressedBody`. +// The handler passes the gzip size limits down to that function through the +// request context whenever gzip encoding is present. +func DecodingLimitsHandler(handler http.Handler, maxLength, gzipMaxLength int64) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Reject too-large requests before doing any further processing. + // Note(philipc): This does nothing in the case of "chunked" + // requests, since those should report a ContentLength of -1. + if r.ContentLength > maxLength { + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, types.MsgDecodingLimitError)) + return + } + // For requests where full size is not known in advance (such as chunked + // requests), pass server.decoding.max_length down, using the request + // context. + + // Note(philipc): Unknown request body size is signaled to the server + // handler by net/http setting the Request.ContentLength field to -1. We + // don't check for the `Transfer-Encoding: chunked` header explicitly, + // because net/http will strip it out from requests automatically. + // Ref: https://pkg.go.dev/net/http#Request + if r.ContentLength < 0 { + ctx := util_decoding.AddServerDecodingMaxLen(r.Context(), maxLength) + r = r.WithContext(ctx) + } + // Pass server.decoding.gzip.max_length down, using the request context. + if strings.Contains(r.Header.Get("Content-Encoding"), "gzip") { + ctx := util_decoding.AddServerDecodingGzipMaxLen(r.Context(), gzipMaxLength) + r = r.WithContext(ctx) + } + + // Copied over from the net/http package; enforces max body read limits. + r2 := *r + r2.Body = http.MaxBytesReader(w, r.Body, maxLength) + handler.ServeHTTP(w, &r2) + }) +} diff --git a/third_party/opa/v1/server/handlers/handlers.go b/third_party/opa/v1/server/handlers/handlers.go new file mode 100644 index 000000000000..8a798d4ea145 --- /dev/null +++ b/third_party/opa/v1/server/handlers/handlers.go @@ -0,0 +1,36 @@ +package handlers + +import ( + "net/http" + "strings" +) + +// HeadMethodNotAllowedHandler returns a handler that responds with +// 405 Method Not Allowed for HEAD requests. +func HeadMethodNotAllowedHandler(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodHead { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + handler.ServeHTTP(w, r) + }) +} + +// TrailingSlashRedirectHandler returns a handler that redirects requests +// with a trailing slash to the same URL without the trailing slash. +func TrailingSlashRedirectHandler(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" && strings.HasSuffix(r.URL.Path, "/") { + http.Redirect(w, r, strings.TrimSuffix(r.URL.Path, "/"), http.StatusMovedPermanently) + return + } + handler.ServeHTTP(w, r) + }) +} + +// DefaultHandler returns a handler that applies both the HeadMethodNotAllowedHandler +// and TrailingSlashRedirectHandler to the provided handler. +func DefaultHandler(handler http.Handler) http.Handler { + return HeadMethodNotAllowedHandler(TrailingSlashRedirectHandler(handler)) +} diff --git a/third_party/opa/v1/server/identifier/certs.go b/third_party/opa/v1/server/identifier/certs.go new file mode 100644 index 000000000000..17ad1ea80c0a --- /dev/null +++ b/third_party/opa/v1/server/identifier/certs.go @@ -0,0 +1,25 @@ +package identifier + +import ( + "context" + "crypto/x509" + "net/http" +) + +type clientCertificatesKey string + +const clientCertificates = clientCertificatesKey("org.openpolicyagent/client-certificates") + +// ClientCertificates returns the ClientCertificates of the caller associated with ctx. +func ClientCertificates(r *http.Request) ([]*x509.Certificate, bool) { + ctx := r.Context() + + certs, ok := ctx.Value(clientCertificates).([]*x509.Certificate) + + return certs, ok +} + +// SetClientCertificates returns a new http.Request with the ClientCertificates set to v. +func SetClientCertificates(r *http.Request, v []*x509.Certificate) *http.Request { + return r.WithContext(context.WithValue(r.Context(), clientCertificates, v)) +} diff --git a/third_party/opa/v1/server/identifier/identifier.go b/third_party/opa/v1/server/identifier/identifier.go new file mode 100644 index 000000000000..de0c7dd82f94 --- /dev/null +++ b/third_party/opa/v1/server/identifier/identifier.go @@ -0,0 +1,30 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package identifier provides handlers for associating identity information with incoming requests. +package identifier + +import ( + "context" + "net/http" +) + +type identityKey string + +const identity = identityKey("org.openpolicyagent/identity") + +// Identity returns the identity of the caller associated with ctx. +func Identity(r *http.Request) (string, bool) { + ctx := r.Context() + v, ok := ctx.Value(identity).(string) + if ok { + return v, true + } + return "", false +} + +// SetIdentity returns a new http.Request with the identity set to v. +func SetIdentity(r *http.Request, v string) *http.Request { + return r.WithContext(context.WithValue(r.Context(), identity, v)) +} diff --git a/third_party/opa/v1/server/identifier/mock_test.go b/third_party/opa/v1/server/identifier/mock_test.go new file mode 100644 index 000000000000..6b719047b85b --- /dev/null +++ b/third_party/opa/v1/server/identifier/mock_test.go @@ -0,0 +1,21 @@ +package identifier_test + +import ( + "crypto/x509" + "net/http" + + "github.com/open-policy-agent/opa/v1/server/identifier" +) + +type mockHandler struct { + identity string + identityDefined bool + + clientCertificates []*x509.Certificate + clientCertificatesDefined bool +} + +func (h *mockHandler) ServeHTTP(_ http.ResponseWriter, r *http.Request) { + h.identity, h.identityDefined = identifier.Identity(r) + h.clientCertificates, h.clientCertificatesDefined = identifier.ClientCertificates(r) +} diff --git a/third_party/opa/v1/server/identifier/testdata/.gitignore b/third_party/opa/v1/server/identifier/testdata/.gitignore new file mode 100644 index 000000000000..e8cd99200d0b --- /dev/null +++ b/third_party/opa/v1/server/identifier/testdata/.gitignore @@ -0,0 +1,5 @@ +*.srl +*.cnf +csr.pem +ca-key.pem +ca.pem diff --git a/third_party/opa/v1/server/identifier/testdata/gencerts.sh b/third_party/opa/v1/server/identifier/testdata/gencerts.sh new file mode 100755 index 000000000000..f2a0b5d0dd81 --- /dev/null +++ b/third_party/opa/v1/server/identifier/testdata/gencerts.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# taken from +# https://github.com/dexidp/dex/blob/2d1ac74ec0ca12ae4d36072525d976c1a596820a/examples/k8s/gencert.sh#L22 + +cat <req.cnf +[req] +req_extensions = v3_req +distinguished_name = req_distinguished_name + +[req_distinguished_name] + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = client.opa.example.com +EOF + +openssl genrsa -out ca-key.pem 2048 +openssl req -x509 -new -nodes -key ca-key.pem -days 1000 -out ca.pem -subj "/CN=my-ca" + +openssl genrsa -out key.pem 2048 +openssl req -new -key key.pem -out csr.pem -subj "/CN=my-client" -config req.cnf +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cn-cert.pem -days 1000 -extensions v3_req -extfile req.cnf + +openssl req -new -key key.pem -out csr.pem -subj "/O=Torchwood/OU=opa-client-01" -config req.cnf +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out ou-cert.pem -days 1000 -extensions v3_req -extfile req.cnf diff --git a/third_party/opa/v1/server/identifier/tls.go b/third_party/opa/v1/server/identifier/tls.go new file mode 100644 index 000000000000..d17c9f1314d2 --- /dev/null +++ b/third_party/opa/v1/server/identifier/tls.go @@ -0,0 +1,32 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package identifier + +import ( + "net/http" +) + +// TLSBased extracts the CN of the client's TLS ceritificate +type TLSBased struct { + inner http.Handler +} + +// NewTLSBased returns a new TLSBased object. +func NewTLSBased(inner http.Handler) *TLSBased { + return &TLSBased{ + inner: inner, + } +} + +func (h *TLSBased) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if tls := r.TLS; tls != nil { + if certs := tls.PeerCertificates; len(certs) > 0 { + r = SetIdentity(r, certs[0].Subject.ToRDNSequence().String()) + r = SetClientCertificates(r, certs) + } + } + + h.inner.ServeHTTP(w, r) +} diff --git a/third_party/opa/v1/server/identifier/tls_test.go b/third_party/opa/v1/server/identifier/tls_test.go new file mode 100644 index 000000000000..fed0a91615f7 --- /dev/null +++ b/third_party/opa/v1/server/identifier/tls_test.go @@ -0,0 +1,129 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package identifier_test + +import ( + "crypto/tls" + "crypto/x509" + "encoding/pem" + "net/http" + "net/http/httptest" + "testing" + + "github.com/open-policy-agent/opa/v1/server/identifier" +) + +// Note: In these tests, we don't worry about the server actually verifying the +// client's certs; that's done in a different place. We only request it, and +// check what the identifier does with it. + +func TestTLSBased(t *testing.T) { + mock := &mockHandler{} + handler := identifier.NewTLSBased(mock) + + tests := []struct { + desc string + cert string + key string + identityExpected string + identityDefined bool + clientCertificatesDefined bool + }{ + { + desc: "no cert", + }, + { + desc: "cert with CN=", + cert: "testdata/cn-cert.pem", + key: "testdata/key.pem", + identityExpected: "CN=my-client", + identityDefined: true, + clientCertificatesDefined: true, + }, + { + desc: "cert with long DN", + cert: "testdata/ou-cert.pem", + key: "testdata/key.pem", + identityExpected: "OU=opa-client-01,O=Torchwood", + identityDefined: true, + clientCertificatesDefined: true, + }, + { + desc: "SPIFFE cert", + cert: "testdata/spiffe-svid-cert.pem", + key: "testdata/spiffe-svid-key.pem", + identityExpected: "SERIALNUMBER=3064486355086639231,OU=Example Org Unit,O=Example Org,C=GB", + identityDefined: true, + clientCertificatesDefined: true, + }, + } + + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + var err error + + // Note: some re-use happens if this server is outside of the tests loop, + // causing weird overlaps. Let's keep setting up a fresh one in each + // iteration to be safe. + s := httptest.NewUnstartedServer(handler) + s.TLS = &tls.Config{ClientAuth: tls.RequestClientCert} + s.StartTLS() + defer s.Close() + c := s.Client() // trusts the httptest server's TLS cert + + var cert tls.Certificate + if tc.cert != "" && tc.key != "" { + cert, err = tls.LoadX509KeyPair(tc.cert, tc.key) + if err != nil { + t.Fatalf("read test cert/key (%s/%s): %s", tc.cert, tc.key, err) + } + c.Transport.(*http.Transport).TLSClientConfig.Certificates = []tls.Certificate{cert} + } + + _, err = c.Get(s.URL) + if err != nil { + t.Fatalf("unexpected error in GET %s: %s", s.URL, err) + } + if mock.identityDefined != tc.identityDefined { + t.Fatalf("Expected identityDefined to be %v but got: %v", tc.identityDefined, mock.identityDefined) + } + + if tc.identityDefined { + if mock.identity != tc.identityExpected { + t.Fatalf("Expected identity to be %s but got: %s", tc.identityExpected, mock.identity) + } + } + + if mock.clientCertificatesDefined != tc.clientCertificatesDefined { + t.Fatalf("Expected clientCertificatesDefined to be %v but got: %v", tc.clientCertificatesDefined, mock.clientCertificatesDefined) + } + + if tc.clientCertificatesDefined { + if len(mock.clientCertificates) != 1 { + t.Fatalf("Expected clientCertificates to have 1 cert but got: %d", len(mock.clientCertificates)) + } + + gotPemData := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: mock.clientCertificates[0].Raw, + }) + + parsedWantedCert, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + t.Fatalf("Error parsing expected cert: %s", err) + } + + wantPemData := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: parsedWantedCert.Raw, + }) + + if got, want := string(gotPemData), string(wantPemData); got != want { + t.Fatalf("Expected clientCertificates to be \n%s\n but got: \n%s\n", want, got) + } + } + }) + } +} diff --git a/third_party/opa/v1/server/identifier/token.go b/third_party/opa/v1/server/identifier/token.go new file mode 100644 index 000000000000..a5d57b38dbe3 --- /dev/null +++ b/third_party/opa/v1/server/identifier/token.go @@ -0,0 +1,33 @@ +package identifier + +import ( + "net/http" + "regexp" +) + +// TokenBased extracts Bearer tokens from the request. +type TokenBased struct { + inner http.Handler +} + +// NewTokenBased returns a new TokenBased object. +func NewTokenBased(inner http.Handler) *TokenBased { + return &TokenBased{ + inner: inner, + } +} + +var bearerTokenRegexp = regexp.MustCompile(`^Bearer\s+(\S+)$`) + +func (h *TokenBased) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + value := r.Header.Get("Authorization") + if len(value) > 0 { + match := bearerTokenRegexp.FindStringSubmatch(value) + if len(match) > 0 { + r = SetIdentity(r, match[1]) + } + } + + h.inner.ServeHTTP(w, r) +} diff --git a/third_party/opa/v1/server/identifier/token_test.go b/third_party/opa/v1/server/identifier/token_test.go new file mode 100644 index 000000000000..c8c535cddb7d --- /dev/null +++ b/third_party/opa/v1/server/identifier/token_test.go @@ -0,0 +1,63 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package identifier_test + +import ( + "net/http" + "testing" + + "github.com/open-policy-agent/opa/v1/server/identifier" +) + +func TestTokenBased(t *testing.T) { + + mock := &mockHandler{} + handler := identifier.NewTokenBased(mock) + + req, err := http.NewRequest(http.MethodGet, "/foo/bar/baz", nil) + if err != nil { + t.Fatalf("Unexpected error creating request: %v", err) + } + + tests := []struct { + value string + expected string + identityDefined bool + }{ + { + "", + "", + false, + }, + { + "Bearer this-is-the-token", + "this-is-the-token", + true, + }, + { + "Bearer this-is-the-token-with-spaces", + "this-is-the-token-with-spaces", + true, + }, + } + + for _, tc := range tests { + + if tc.value != "" { + req.Header.Set("Authorization", tc.value) + } + + handler.ServeHTTP(nil, req) + + if mock.identityDefined != tc.identityDefined { + t.Fatalf("Expected identityDefined to be %v but got: %v", tc.identityDefined, mock.identityDefined) + } + + if mock.identity != tc.expected { + t.Fatalf("Expected identity to be %s but got: %s", tc.expected, mock.identity) + } + } + +} diff --git a/third_party/opa/v1/server/server.go b/third_party/opa/v1/server/server.go new file mode 100644 index 000000000000..f9225ed85750 --- /dev/null +++ b/third_party/opa/v1/server/server.go @@ -0,0 +1,3184 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package server + +import ( + "bytes" + "context" + "crypto/tls" + "crypto/x509" + "encoding/json" + "errors" + "fmt" + "html/template" + "io" + "net" + "net/http" + "net/http/pprof" + "net/url" + "os" + "slices" + "strconv" + "strings" + "sync" + "time" + + "github.com/open-policy-agent/opa/v1/hooks" + serverDecodingPlugin "github.com/open-policy-agent/opa/v1/plugins/server/decoding" + serverEncodingPlugin "github.com/open-policy-agent/opa/v1/plugins/server/encoding" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + + "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" + + "github.com/open-policy-agent/opa/internal/json/patch" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + bundlePlugin "github.com/open-policy-agent/opa/v1/plugins/bundle" + "github.com/open-policy-agent/opa/v1/plugins/status" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/server/authorizer" + "github.com/open-policy-agent/opa/v1/server/handlers" + "github.com/open-policy-agent/opa/v1/server/identifier" + "github.com/open-policy-agent/opa/v1/server/types" + "github.com/open-policy-agent/opa/v1/server/writer" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + iCache "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/lineage" + "github.com/open-policy-agent/opa/v1/tracing" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/version" +) + +// AuthenticationScheme enumerates the supported authentication schemes. The +// authentication scheme determines how client identities are established. +type AuthenticationScheme int + +// Set of supported authentication schemes. +const ( + AuthenticationOff AuthenticationScheme = iota + AuthenticationToken + AuthenticationTLS +) + +// AuthorizationScheme enumerates the supported authorization schemes. The authorization +// scheme determines how access to OPA is controlled. +type AuthorizationScheme int + +// Set of supported authorization schemes. +const ( + AuthorizationOff AuthorizationScheme = iota + AuthorizationBasic +) + +const ( + defaultMinTLSVersion = tls.VersionTLS12 + + // Set of handlers for use in the "handler" dimension of the duration metric. + PromHandlerV0Data = "v0/data" + PromHandlerV1Data = "v1/data" + PromHandlerV1Query = "v1/query" + PromHandlerV1Policies = "v1/policies" + PromHandlerV1Compile = "v1/compile" + PromHandlerV1Config = "v1/config" + PromHandlerV1Status = "v1/status" + PromHandlerIndex = "index" + PromHandlerCatch = "catchall" + PromHandlerHealth = "health" + PromHandlerAPIAuthz = "authz" + + pqMaxCacheSize = 100 + + // OpenTelemetry attributes + otelDecisionIDAttr = "opa.decision_id" +) + +var ( + supportedTLSVersions = []uint16{tls.VersionTLS10, tls.VersionTLS11, tls.VersionTLS12, tls.VersionTLS13} + unsafeBuiltinsMap = map[string]struct{}{ast.HTTPSend.Name: {}} + intermediateResultsEnabled = os.Getenv("OPA_DECISIONS_INTERMEDIATE_RESULTS") != "" +) + +type IntermediateResultsContextKey struct{} + +// Server represents an instance of OPA running in server mode. +type Server struct { + Handler http.Handler + DiagnosticHandler http.Handler + + router *http.ServeMux + addrs []string + diagAddrs []string + h2cEnabled bool + authentication AuthenticationScheme + authorization AuthorizationScheme + cert *tls.Certificate + tlsConfigMtx sync.RWMutex + certFile string + certFileHash []byte + certKeyFile string + certKeyFileHash []byte + certRefresh time.Duration + certPool *x509.CertPool + certPoolFile string + certPoolFileHash []byte + minTLSVersion uint16 + mtx sync.RWMutex + partials map[string]rego.PartialResult + preparedEvalQueries *cache + store storage.Store + manager *plugins.Manager + decisionIDFactory func() string + logger func(context.Context, *Info) error + errLimit int + pprofEnabled bool + runtime *ast.Term + httpListeners []httpListener + metrics Metrics + defaultDecisionPath string + interQueryBuiltinCache iCache.InterQueryCache + interQueryBuiltinValueCache iCache.InterQueryValueCache + allPluginsOkOnce bool + distributedTracingOpts tracing.Options + ndbCacheEnabled bool + unixSocketPerm *string + cipherSuites *[]uint16 + hooks hooks.Hooks +} + +// Metrics defines the interface that the server requires for recording HTTP +// handler metrics. +type Metrics interface { + RegisterEndpoints(registrar func(path, method string, handler http.Handler)) + InstrumentHandler(handler http.Handler, label string) http.Handler +} + +// TLSConfig represents the TLS configuration for the server. +// This configuration is used to configure file watchers to reload each file as it +// changes on disk. +type TLSConfig struct { + // CertFile is the path to the server's serving certificate file. + CertFile string + + // KeyFile is the path to the server's key file, completing the key pair for the + // CertFile certificate. + KeyFile string + + // CertPoolFile is the path to the CA cert pool file. The contents of this file will be + // reloaded when the file changes on disk and used in as trusted client CAs in the TLS config + // for new connections to the server. + CertPoolFile string +} + +// Loop will contain all the calls from the server that we'll be listening on. +type Loop func() error + +// New returns a new Server. +func New() *Server { + s := Server{} + return &s +} + +// Init initializes the server. This function MUST be called before starting any loops +// from s.Listeners(). +func (s *Server) Init(ctx context.Context) (*Server, error) { + s.initRouters(ctx) + var err error + s.hooks.Each(func(h hooks.Hook) { + switch h := h.(type) { + case hooks.InterQueryCacheHook: + if e := h.OnInterQueryCache(ctx, s.interQueryBuiltinCache); e != nil { + err = errors.Join(err, e) + } + case hooks.InterQueryValueCacheHook: + if e := h.OnInterQueryValueCache(ctx, s.interQueryBuiltinValueCache); e != nil { + err = errors.Join(err, e) + } + } + }) + if err != nil { + return nil, err + } + + txn, err := s.store.NewTransaction(ctx, storage.WriteParams) + if err != nil { + return nil, err + } + + // Register triggers so that if runtime reloads the policies, the + // server sees the change. + config := storage.TriggerConfig{ + OnCommit: s.reload, + } + if _, err := s.store.Register(ctx, txn, config); err != nil { + s.store.Abort(ctx, txn) + return nil, err + } + + s.partials = map[string]rego.PartialResult{} + s.preparedEvalQueries = newCache(pqMaxCacheSize) + s.defaultDecisionPath = s.generateDefaultDecisionPath() + s.manager.RegisterNDCacheTrigger(s.updateNDCache) + + s.Handler = s.initHandlerAuthn(s.Handler) + + // compression handler + s.Handler, err = s.initHandlerCompression(s.Handler) + if err != nil { + return nil, err + } + s.DiagnosticHandler = s.initHandlerAuthn(s.DiagnosticHandler) + + s.Handler, err = s.initHandlerDecodingLimits(s.Handler) + if err != nil { + return nil, err + } + + return s, s.store.Commit(ctx, txn) +} + +// Shutdown will attempt to gracefully shutdown each of the http servers +// currently in use by the OPA Server. If any exceed the deadline specified +// by the context an error will be returned. +func (s *Server) Shutdown(ctx context.Context) error { + errChan := make(chan error) + for _, srvr := range s.httpListeners { + go func(s httpListener) { + errChan <- s.Shutdown(ctx) + }(srvr) + } + // wait until each server has finished shutting down + var errorList []error + for range s.httpListeners { + err := <-errChan + if err != nil { + errorList = append(errorList, err) + } + } + + if len(errorList) > 0 { + errMsg := "error while shutting down: " + for i, err := range errorList { + errMsg += fmt.Sprintf("(%d) %s. ", i, err.Error()) + } + return errors.New(errMsg) + } + return nil +} + +// WithAddresses sets the listening addresses that the server will bind to. +func (s *Server) WithAddresses(addrs []string) *Server { + s.addrs = addrs + return s +} + +// WithDiagnosticAddresses sets the listening addresses that the server will +// bind to and *only* serve read-only diagnostic API's. +func (s *Server) WithDiagnosticAddresses(addrs []string) *Server { + s.diagAddrs = addrs + return s +} + +// WithAuthentication sets authentication scheme to use on the server. +func (s *Server) WithAuthentication(scheme AuthenticationScheme) *Server { + s.authentication = scheme + return s +} + +// WithAuthorization sets authorization scheme to use on the server. +func (s *Server) WithAuthorization(scheme AuthorizationScheme) *Server { + s.authorization = scheme + return s +} + +// WithCertificate sets the server-side certificate that the server will use. +func (s *Server) WithCertificate(cert *tls.Certificate) *Server { + s.cert = cert + return s +} + +// WithCertificatePaths sets the server-side certificate and keyfile paths +// that the server will periodically check for changes, and reload if necessary. +func (s *Server) WithCertificatePaths(certFile, keyFile string, refresh time.Duration) *Server { + s.certFile = certFile + s.certKeyFile = keyFile + s.certRefresh = refresh + return s +} + +// WithCertPool sets the server-side cert pool that the server will use. +func (s *Server) WithCertPool(pool *x509.CertPool) *Server { + s.certPool = pool + return s +} + +// WithTLSConfig sets the TLS configuration used by the server. +func (s *Server) WithTLSConfig(tlsConfig *TLSConfig) *Server { + s.certFile = tlsConfig.CertFile + s.certKeyFile = tlsConfig.KeyFile + s.certPoolFile = tlsConfig.CertPoolFile + return s +} + +// WithCertRefresh sets the period on which certs, keys and cert pools are reloaded from disk. +func (s *Server) WithCertRefresh(refresh time.Duration) *Server { + s.certRefresh = refresh + return s +} + +// WithStore sets the storage used by the server. +func (s *Server) WithStore(store storage.Store) *Server { + s.store = store + return s +} + +// WithMetrics sets the metrics provider used by the server. +func (s *Server) WithMetrics(m Metrics) *Server { + s.metrics = m + return s +} + +// WithManager sets the plugins manager used by the server. +func (s *Server) WithManager(manager *plugins.Manager) *Server { + s.manager = manager + return s +} + +// WithCompilerErrorLimit sets the limit on the number of compiler errors the server will +// allow. +func (s *Server) WithCompilerErrorLimit(limit int) *Server { + s.errLimit = limit + return s +} + +// WithPprofEnabled sets whether pprof endpoints are enabled +func (s *Server) WithPprofEnabled(pprofEnabled bool) *Server { + s.pprofEnabled = pprofEnabled + return s +} + +// WithH2CEnabled sets whether h2c ("HTTP/2 cleartext") is enabled for the http listener +func (s *Server) WithH2CEnabled(enabled bool) *Server { + s.h2cEnabled = enabled + return s +} + +// WithDecisionLogger sets the decision logger used by the +// server. DEPRECATED. Use WithDecisionLoggerWithErr instead. +func (s *Server) WithDecisionLogger(logger func(context.Context, *Info)) *Server { + s.logger = func(ctx context.Context, info *Info) error { + logger(ctx, info) + return nil + } + return s +} + +// WithDecisionLoggerWithErr sets the decision logger used by the server. +func (s *Server) WithDecisionLoggerWithErr(logger func(context.Context, *Info) error) *Server { + s.logger = logger + return s +} + +// WithDecisionIDFactory sets a function on the server to generate decision IDs. +func (s *Server) WithDecisionIDFactory(f func() string) *Server { + s.decisionIDFactory = f + return s +} + +// WithRuntime sets the runtime data to provide to the evaluation engine. +func (s *Server) WithRuntime(term *ast.Term) *Server { + s.runtime = term + return s +} + +// WithRouter sets the mux.Router to attach OPA's HTTP API routes onto. If a +// router is not supplied, the server will create it's own. +func (s *Server) WithRouter(router *http.ServeMux) *Server { + s.router = router + return s +} + +func (s *Server) WithMinTLSVersion(minTLSVersion uint16) *Server { + if slices.Contains(supportedTLSVersions, minTLSVersion) { + s.minTLSVersion = minTLSVersion + } else { + s.minTLSVersion = defaultMinTLSVersion + } + return s +} + +// WithDistributedTracingOpts sets the options to be used by distributed tracing. +func (s *Server) WithDistributedTracingOpts(opts tracing.Options) *Server { + s.distributedTracingOpts = opts + return s +} + +// WithHooks allows passing hooks to the server. +func (s *Server) WithHooks(hs hooks.Hooks) *Server { + s.hooks = hs + return s +} + +// WithNDBCacheEnabled sets whether the ND builtins cache is to be used. +func (s *Server) WithNDBCacheEnabled(ndbCacheEnabled bool) *Server { + s.ndbCacheEnabled = ndbCacheEnabled + return s +} + +// WithCipherSuites sets the list of enabled TLS 1.0–1.2 cipher suites. +func (s *Server) WithCipherSuites(cipherSuites *[]uint16) *Server { + s.cipherSuites = cipherSuites + return s +} + +// WithUnixSocketPermission sets the permission for the Unix domain socket if used to listen for +// incoming connections. Applies to the sockets the server is listening on including diagnostic API's. +func (s *Server) WithUnixSocketPermission(unixSocketPerm *string) *Server { + s.unixSocketPerm = unixSocketPerm + return s +} + +// Listeners returns functions that listen and serve connections. +func (s *Server) Listeners() ([]Loop, error) { + loops := []Loop{} + + handlerBindings := map[httpListenerType]struct { + addrs []string + handler http.Handler + }{ + defaultListenerType: {s.addrs, s.Handler}, + diagnosticListenerType: {s.diagAddrs, s.DiagnosticHandler}, + } + + for t, binding := range handlerBindings { + for _, addr := range binding.addrs { + l, listener, err := s.getListener(addr, binding.handler, t) + if err != nil { + return nil, err + } + s.httpListeners = append(s.httpListeners, listener) + loops = append(loops, l...) + } + } + + return loops, nil +} + +// Addrs returns a list of addresses that the server is listening on. +// If the server hasn't been started it will not return an address. +func (s *Server) Addrs() []string { + return s.addrsForType(defaultListenerType) +} + +// DiagnosticAddrs returns a list of addresses that the server is listening on +// for the read-only diagnostic API's (eg /health, /metrics, etc) +// If the server hasn't been started it will not return an address. +func (s *Server) DiagnosticAddrs() []string { + return s.addrsForType(diagnosticListenerType) +} + +func (s *Server) addrsForType(t httpListenerType) []string { + var addrs []string + for _, l := range s.httpListeners { + a := l.Addr() + if a != "" && l.Type() == t { + addrs = append(addrs, a) + } + } + return addrs +} + +type tcpKeepAliveListener struct { + *net.TCPListener +} + +func (ln tcpKeepAliveListener) Accept() (net.Conn, error) { + tc, err := ln.AcceptTCP() + if err != nil { + return nil, err + } + err = tc.SetKeepAlive(true) + if err != nil { + return nil, err + } + err = tc.SetKeepAlivePeriod(3 * time.Minute) + if err != nil { + return nil, err + } + return tc, nil +} + +type httpListenerType int + +const ( + defaultListenerType httpListenerType = iota + diagnosticListenerType +) + +type httpListener interface { + Addr() string + ListenAndServe() error + ListenAndServeTLS(certFile, keyFile string) error + Shutdown(context.Context) error + Type() httpListenerType +} + +// baseHTTPListener is just a wrapper around http.Server +type baseHTTPListener struct { + s *http.Server + l net.Listener + t httpListenerType + addr string + addrMtx sync.RWMutex +} + +var _ httpListener = (*baseHTTPListener)(nil) + +func newHTTPListener(srvr *http.Server, t httpListenerType) httpListener { + return &baseHTTPListener{s: srvr, t: t} +} + +func newHTTPUnixSocketListener(srvr *http.Server, l net.Listener, t httpListenerType) httpListener { + return &baseHTTPListener{s: srvr, l: l, t: t} +} + +func (b *baseHTTPListener) ListenAndServe() error { + addr := b.s.Addr + if addr == "" { + addr = ":http" + } + var err error + b.l, err = net.Listen("tcp", addr) + if err != nil { + return err + } + + b.initAddr() + + return b.s.Serve(tcpKeepAliveListener{b.l.(*net.TCPListener)}) +} + +func (b *baseHTTPListener) initAddr() { + b.addrMtx.Lock() + if addr := b.l.(*net.TCPListener).Addr(); addr != nil { + b.addr = addr.String() + } + b.addrMtx.Unlock() +} + +func (b *baseHTTPListener) Addr() string { + b.addrMtx.Lock() + defer b.addrMtx.Unlock() + return b.addr +} + +func (b *baseHTTPListener) ListenAndServeTLS(certFile, keyFile string) error { + addr := b.s.Addr + if addr == "" { + addr = ":https" + } + + var err error + b.l, err = net.Listen("tcp", addr) + if err != nil { + return err + } + + b.initAddr() + + defer b.l.Close() + + return b.s.ServeTLS(tcpKeepAliveListener{b.l.(*net.TCPListener)}, certFile, keyFile) +} + +func (b *baseHTTPListener) Shutdown(ctx context.Context) error { + return b.s.Shutdown(ctx) +} + +func (b *baseHTTPListener) Type() httpListenerType { + return b.t +} + +func (s *Server) getListener(addr string, h http.Handler, t httpListenerType) ([]Loop, httpListener, error) { + parsedURL, err := parseURL(addr, s.cert != nil) + if err != nil { + return nil, nil, err + } + + var loops []Loop + var loop Loop + var listener httpListener + switch parsedURL.Scheme { + case "unix": + loop, listener, err = s.getListenerForUNIXSocket(parsedURL, h, t) + loops = []Loop{loop} + case "http": + loop, listener, err = s.getListenerForHTTPServer(parsedURL, h, t) + loops = []Loop{loop} + case "https": + loop, listener, err = s.getListenerForHTTPSServer(parsedURL, h, t) + logger := s.manager.Logger().WithFields(map[string]any{ + "cert-file": s.certFile, + "cert-key-file": s.certKeyFile, + }) + + // if a manual cert refresh period has been set, then use the polling behavior, + // otherwise use the fsnotify default behavior + if s.certRefresh > 0 { + loops = []Loop{loop, s.certLoopPolling(logger)} + } else if s.certFile != "" || s.certPoolFile != "" { + loops = []Loop{loop, s.certLoopNotify(logger)} + } + default: + err = fmt.Errorf("invalid url scheme %q", parsedURL.Scheme) + } + + return loops, listener, err +} + +func (s *Server) getListenerForHTTPServer(u *url.URL, h http.Handler, t httpListenerType) (Loop, httpListener, error) { + if s.h2cEnabled { + h2s := &http2.Server{} + h = h2c.NewHandler(h, h2s) + } + h1s := http.Server{ + Addr: u.Host, + Handler: h, + } + + l := newHTTPListener(&h1s, t) + + return l.ListenAndServe, l, nil +} + +func (s *Server) getListenerForHTTPSServer(u *url.URL, h http.Handler, t httpListenerType) (Loop, httpListener, error) { + + if s.cert == nil { + return nil, nil, errors.New("TLS certificate required but not supplied") + } + + tlsConfig := tls.Config{ + GetCertificate: s.getCertificate, + // GetConfigForClient is used to ensure that a fresh config is provided containing the latest cert pool. + // This is not required, but appears to be how connect time updates config should be done: + // https://github.com/golang/go/issues/16066#issuecomment-250606132 + GetConfigForClient: func(_ *tls.ClientHelloInfo) (*tls.Config, error) { + s.tlsConfigMtx.Lock() + defer s.tlsConfigMtx.Unlock() + + cfg := &tls.Config{ + GetCertificate: s.getCertificate, + ClientCAs: s.certPool, + } + + if s.authentication == AuthenticationTLS { + cfg.ClientAuth = tls.RequireAndVerifyClientCert + } + + if s.minTLSVersion != 0 { + cfg.MinVersion = s.minTLSVersion + } else { + cfg.MinVersion = defaultMinTLSVersion + } + + if s.cipherSuites != nil { + cfg.CipherSuites = *s.cipherSuites + } + + return cfg, nil + }, + } + + httpsServer := http.Server{ + Addr: u.Host, + Handler: h, + TLSConfig: &tlsConfig, + } + + l := newHTTPListener(&httpsServer, t) + + httpsLoop := func() error { return l.ListenAndServeTLS("", "") } + + return httpsLoop, l, nil +} + +func (s *Server) getListenerForUNIXSocket(u *url.URL, h http.Handler, t httpListenerType) (Loop, httpListener, error) { + socketPath := u.Host + u.Path + + // Recover @ prefix for abstract Unix sockets. + if strings.HasPrefix(u.String(), u.Scheme+"://@") { + socketPath = "@" + socketPath + } else { + // Remove domain socket file in case it already exists. + os.Remove(socketPath) + } + + domainSocketServer := http.Server{Handler: h} + unixListener, err := net.Listen("unix", socketPath) + if err != nil { + return nil, nil, err + } + + if s.unixSocketPerm != nil { + modeVal, err := strconv.ParseUint(*s.unixSocketPerm, 8, 32) + if err != nil { + return nil, nil, err + } + + if err := os.Chmod(socketPath, os.FileMode(modeVal)); err != nil { + return nil, nil, err + } + } + + l := newHTTPUnixSocketListener(&domainSocketServer, unixListener, t) + + domainSocketLoop := func() error { return domainSocketServer.Serve(unixListener) } + return domainSocketLoop, l, nil +} + +func (s *Server) initHandlerAuthn(handler http.Handler) http.Handler { + switch s.authentication { + case AuthenticationToken: + handler = identifier.NewTokenBased(handler) + case AuthenticationTLS: + handler = identifier.NewTLSBased(handler) + } + + return handler +} + +func (s *Server) initHandlerAuthz(handler http.Handler) http.Handler { + switch s.authorization { + case AuthorizationBasic: + handler = authorizer.NewBasic( + handler, + s.getCompiler, + s.store, + authorizer.Runtime(s.runtime), + authorizer.Decision(s.manager.Config.DefaultAuthorizationDecisionRef), + authorizer.PrintHook(s.manager.PrintHook()), + authorizer.EnablePrintStatements(s.manager.EnablePrintStatements()), + authorizer.InterQueryCache(s.interQueryBuiltinCache), + authorizer.InterQueryValueCache(s.interQueryBuiltinValueCache), + authorizer.URLPathExpectsBodyFunc(s.manager.ExtraAuthorizerRoutes())) + + if s.metrics != nil { + handler = s.instrumentHandler(handler.ServeHTTP, PromHandlerAPIAuthz) + } + } + + return handler +} + +// Enforces request body size limits on incoming requests. For gzipped requests, +// it passes the size limit down the body-reading method via the request +// context. +func (s *Server) initHandlerDecodingLimits(handler http.Handler) (http.Handler, error) { + var decodingRawConfig json.RawMessage + serverConfig := s.manager.Config.Server + if serverConfig != nil { + decodingRawConfig = serverConfig.Decoding + } + decodingConfig, err := serverDecodingPlugin.NewConfigBuilder().WithBytes(decodingRawConfig).Parse() + if err != nil { + return nil, err + } + decodingHandler := handlers.DecodingLimitsHandler(handler, *decodingConfig.MaxLength, *decodingConfig.Gzip.MaxLength) + + return decodingHandler, nil +} + +func (s *Server) initHandlerCompression(handler http.Handler) (http.Handler, error) { + var encodingRawConfig json.RawMessage + serverConfig := s.manager.Config.Server + if serverConfig != nil { + encodingRawConfig = serverConfig.Encoding + } + encodingConfig, err := serverEncodingPlugin.NewConfigBuilder().WithBytes(encodingRawConfig).Parse() + if err != nil { + return nil, err + } + compressHandler := handlers.CompressHandler(handler, *encodingConfig.Gzip.MinLength, *encodingConfig.Gzip.CompressionLevel) + + return compressHandler, nil +} + +func (s *Server) initRouters(ctx context.Context) { + mainRouter := s.router + if mainRouter == nil { + mainRouter = http.NewServeMux() + } + + diagRouter := http.NewServeMux() + + // authorizer, if configured, needs the iCache to be set up already + + cacheConfig := s.manager.InterQueryBuiltinCacheConfig() + + s.interQueryBuiltinCache = iCache.NewInterQueryCacheWithContext(ctx, cacheConfig) + s.interQueryBuiltinValueCache = iCache.NewInterQueryValueCache(ctx, cacheConfig) + + s.manager.RegisterCacheTrigger(s.updateCacheConfig) + + // Add authorization handler. This must come BEFORE authentication handler + // so that the latter can run first. + handlerAuthz := s.initHandlerAuthz(mainRouter) + + handlerAuthzDiag := s.initHandlerAuthz(diagRouter) + + // All routers get the same base configuration *and* diagnostic API's + for _, router := range []*http.ServeMux{mainRouter, diagRouter} { + if s.metrics != nil { + s.metrics.RegisterEndpoints(func(path, method string, handler http.Handler) { + router.Handle(fmt.Sprintf("%s %s", method, path), handler) + }) + } + + router.Handle("GET /health", s.instrumentHandler(s.unversionedGetHealth, PromHandlerHealth)) + // Use this route to evaluate health policy defined at system.health + // By convention, policy is typically defined at system.health.live and system.health.ready, and is + // evaluated by calling /health/live and /health/ready respectively. + router.Handle("GET /health/{path...}", s.instrumentHandler(s.unversionedGetHealthWithPolicy, PromHandlerHealth)) + } + + for p, r := range s.manager.ExtraRoutes() { + mainRouter.Handle(p, s.instrumentHandler(r.HandlerFunc, r.PromName)) + } + + if s.pprofEnabled { + mainRouter.HandleFunc("GET /debug/pprof/", pprof.Index) + mainRouter.Handle("GET /debug/pprof/allocs", pprof.Handler("allocs")) + mainRouter.Handle("GET /debug/pprof/block", pprof.Handler("block")) + mainRouter.Handle("GET /debug/pprof/heap", pprof.Handler("heap")) + mainRouter.Handle("GET /debug/pprof/mutex", pprof.Handler("mutex")) + mainRouter.HandleFunc("GET /debug/pprof/cmdline", pprof.Cmdline) + mainRouter.HandleFunc("GET /debug/pprof/profile", pprof.Profile) + mainRouter.HandleFunc("GET /debug/pprof/symbol", pprof.Symbol) + mainRouter.HandleFunc("GET /debug/pprof/trace", pprof.Trace) + } + + // Only the main mainRouter gets the OPA API's (data, policies, query, etc) + mainRouter.Handle("POST /v0/data/{path...}", s.instrumentHandler(s.v0DataPost, PromHandlerV0Data)) + mainRouter.Handle("POST /v0/data", s.instrumentHandler(s.v0DataPost, PromHandlerV0Data)) + mainRouter.Handle("DELETE /v1/data/{path...}", s.instrumentHandler(s.v1DataDelete, PromHandlerV1Data)) + mainRouter.Handle("PUT /v1/data/{path...}", s.instrumentHandler(s.v1DataPut, PromHandlerV1Data)) + mainRouter.Handle("PUT /v1/data", s.instrumentHandler(s.v1DataPut, PromHandlerV1Data)) + mainRouter.Handle("GET /v1/data/{path...}", s.instrumentHandler(s.v1DataGet, PromHandlerV1Data)) + mainRouter.Handle("GET /v1/data", s.instrumentHandler(s.v1DataGet, PromHandlerV1Data)) + mainRouter.Handle("PATCH /v1/data/{path...}", s.instrumentHandler(s.v1DataPatch, PromHandlerV1Data)) + mainRouter.Handle("PATCH /v1/data", s.instrumentHandler(s.v1DataPatch, PromHandlerV1Data)) + mainRouter.Handle("POST /v1/data/{path...}", s.instrumentHandler(s.v1DataPost, PromHandlerV1Data)) + mainRouter.Handle("POST /v1/data", s.instrumentHandler(s.v1DataPost, PromHandlerV1Data)) + mainRouter.Handle("GET /v1/policies", s.instrumentHandler(s.v1PoliciesList, PromHandlerV1Policies)) + mainRouter.Handle("DELETE /v1/policies/{path...}", s.instrumentHandler(s.v1PoliciesDelete, PromHandlerV1Policies)) + mainRouter.Handle("GET /v1/policies/{path...}", s.instrumentHandler(s.v1PoliciesGet, PromHandlerV1Policies)) + mainRouter.Handle("PUT /v1/policies/{path...}", s.instrumentHandler(s.v1PoliciesPut, PromHandlerV1Policies)) + mainRouter.Handle("GET /v1/query", s.instrumentHandler(s.v1QueryGet, PromHandlerV1Query)) + mainRouter.Handle("POST /v1/query", s.instrumentHandler(s.v1QueryPost, PromHandlerV1Query)) + mainRouter.Handle("POST /v1/compile", s.instrumentHandler(s.v1CompilePost, PromHandlerV1Compile)) + mainRouter.Handle("GET /v1/config", s.instrumentHandler(s.v1ConfigGet, PromHandlerV1Config)) + mainRouter.Handle("GET /v1/status", s.instrumentHandler(s.v1StatusGet, PromHandlerV1Status)) + mainRouter.Handle("POST /{$}", s.instrumentHandler(s.unversionedPost, PromHandlerIndex)) + mainRouter.Handle("GET /{$}", s.instrumentHandler(s.indexGet, PromHandlerIndex)) + + // These are catch all handlers that respond http.StatusMethodNotAllowed for resources that exist but the method is not allowed + mainRouter.Handle("/v0/data/{path...}", s.methodNotAllowedHandler()) + mainRouter.Handle("/v0/data", s.methodNotAllowedHandler()) + mainRouter.Handle("/v1/data/{path...}", s.methodNotAllowedHandler()) + mainRouter.Handle("/v1/data", s.methodNotAllowedHandler()) + mainRouter.Handle("/v1/policies", s.methodNotAllowedHandler()) + mainRouter.Handle("/v1/policies/{path...}", s.methodNotAllowedHandler()) + mainRouter.Handle("/v1/query/{path...}", s.methodNotAllowedHandler()) + mainRouter.Handle("/v1/query", s.methodNotAllowedHandler()) + + // Add authorization handler in the end so that it can run first + s.Handler = handlerAuthz + s.DiagnosticHandler = handlerAuthzDiag +} + +func createMiddleware(mw ...func(http.Handler) http.Handler) func(http.Handler) http.Handler { + return func(hnd http.Handler) http.Handler { + next := hnd + for k := len(mw) - 1; k >= 0; k-- { + next = mw[k](next) + } + return next + } +} + +func (s *Server) instrumentHandler(handler func(http.ResponseWriter, *http.Request), label string) http.Handler { + httpHandler := handlers.DefaultHandler(createMiddleware( + s.manager.ExtraMiddlewares()..., + )(http.HandlerFunc(handler))) + if len(s.distributedTracingOpts) > 0 { + httpHandler = tracing.NewHandler(httpHandler, label, s.distributedTracingOpts) + } + if s.metrics != nil { + return s.metrics.InstrumentHandler(httpHandler, label) + } + return httpHandler +} + +func (s *Server) methodNotAllowedHandler() http.Handler { + return s.instrumentHandler(writer.HTTPStatus(http.StatusMethodNotAllowed), PromHandlerCatch) +} + +func (s *Server) execQuery(ctx context.Context, br bundleRevisions, txn storage.Transaction, parsedQuery ast.Body, input ast.Value, rawInput *any, m metrics.Metrics, explainMode types.ExplainModeV1, includeMetrics, includeInstrumentation, pretty bool) (*types.QueryResponseV1, error) { + results := types.QueryResponseV1{} + ctx, logger := s.getDecisionLogger(ctx, br) + + var buf *topdown.BufferTracer + if explainMode != types.ExplainOffV1 { + buf = topdown.NewBufferTracer() + } + + var ndbCache builtins.NDBCache + if s.ndbCacheEnabled { + ndbCache = builtins.NDBCache{} + } + + opts := []func(*rego.Rego){ + rego.Store(s.store), + rego.Transaction(txn), + rego.Compiler(s.getCompiler()), + rego.ParsedQuery(parsedQuery), + rego.ParsedInput(input), + rego.Metrics(m), + rego.Instrument(includeInstrumentation), + rego.QueryTracer(buf), + rego.Runtime(s.runtime), + rego.UnsafeBuiltins(unsafeBuiltinsMap), + rego.InterQueryBuiltinCache(s.interQueryBuiltinCache), + rego.InterQueryBuiltinValueCache(s.interQueryBuiltinValueCache), + rego.PrintHook(s.manager.PrintHook()), + rego.EnablePrintStatements(s.manager.EnablePrintStatements()), + rego.DistributedTracingOpts(s.distributedTracingOpts), + rego.NDBuiltinCache(ndbCache), + } + + for _, r := range s.manager.GetWasmResolvers() { + for _, entrypoint := range r.Entrypoints() { + opts = append(opts, rego.Resolver(entrypoint, r)) + } + } + + rego := rego.New(opts...) + + output, err := rego.Eval(ctx) + if err != nil { + _ = logger.Log(ctx, txn, "", parsedQuery.String(), rawInput, input, nil, ndbCache, err, m) + return nil, err + } + + for _, result := range output { + results.Result = append(results.Result, result.Bindings.WithoutWildcards()) + } + + if includeMetrics || includeInstrumentation { + results.Metrics = m.All() + } + + if explainMode != types.ExplainOffV1 { + results.Explanation = s.getExplainResponse(explainMode, *buf, pretty) + } + + var x any = results.Result + if err := logger.Log(ctx, txn, "", parsedQuery.String(), rawInput, input, &x, ndbCache, nil, m); err != nil { + return nil, err + } + return &results, nil +} + +func (*Server) indexGet(w http.ResponseWriter, _ *http.Request) { + _ = indexHTML.Execute(w, struct { + Version string + BuildCommit string + BuildTimestamp string + BuildHostname string + }{ + Version: version.Version, + BuildCommit: version.Vcs, + BuildTimestamp: version.Timestamp, + BuildHostname: version.Hostname, + }) +} + +type bundleRevisions struct { + LegacyRevision string + Revisions map[string]string +} + +func getRevisions(ctx context.Context, store storage.Store, txn storage.Transaction) (bundleRevisions, error) { + + var err error + var br bundleRevisions + br.Revisions = map[string]string{} + + // Check if we still have a legacy bundle manifest in the store + br.LegacyRevision, err = bundle.LegacyReadRevisionFromStore(ctx, store, txn) + if err != nil && !storage.IsNotFound(err) { + return br, err + } + + // read all bundle revisions from storage (if any exist) + names, err := bundle.ReadBundleNamesFromStore(ctx, store, txn) + if err != nil && !storage.IsNotFound(err) { + return br, err + } + + for _, name := range names { + r, err := bundle.ReadBundleRevisionFromStore(ctx, store, txn, name) + if err != nil && !storage.IsNotFound(err) { + return br, err + } + br.Revisions[name] = r + } + + return br, nil +} + +func (s *Server) reload(context.Context, storage.Transaction, storage.TriggerEvent) { + + // NOTE(tsandall): We currently rely on the storage txn to provide + // critical sections in the server. + // + // If you modify this function to change any other state on the server, you must + // review the other places in the server where that state is accessed to avoid data + // races--the state must be accessed _after_ a txn has been opened. + + // reset some cached info + s.partials = map[string]rego.PartialResult{} + s.preparedEvalQueries = newCache(pqMaxCacheSize) + s.defaultDecisionPath = s.generateDefaultDecisionPath() +} + +func (s *Server) unversionedPost(w http.ResponseWriter, r *http.Request) { + s.v0QueryPath(w, r, "", true) +} + +func (s *Server) v0DataPost(w http.ResponseWriter, r *http.Request) { + s.v0QueryPath(w, r, escapedPathValue(r, "path"), false) +} + +func (s *Server) v0QueryPath(w http.ResponseWriter, r *http.Request, urlPath string, useDefaultDecisionPath bool) { + m := metrics.New() + m.Timer(metrics.ServerHandler).Start() + + decisionID := s.generateDecisionID() + ctx := logging.WithDecisionID(r.Context(), decisionID) + annotateSpan(ctx, decisionID) + + input, goInput, err := readInputV0(r) + if err != nil { + writer.ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, fmt.Errorf("unexpected parse error for input: %w", err)) + return + } + + // Prepare for query. + txn, err := s.store.NewTransaction(ctx) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + defer s.store.Abort(ctx, txn) + + br, err := getRevisions(ctx, s.store, txn) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + if useDefaultDecisionPath { + urlPath = s.generateDefaultDecisionPath() + } + + ctx, logger := s.getDecisionLogger(ctx, br) + + var ndbCache builtins.NDBCache + if s.ndbCacheEnabled { + ndbCache = builtins.NDBCache{} + } + + pqID := "v0QueryPath::" + urlPath + preparedQuery, ok := s.getCachedPreparedEvalQuery(pqID, m) + if !ok { + opts := []func(*rego.Rego){ + rego.Compiler(s.getCompiler()), + rego.Store(s.store), + } + + // Set resolvers on the base Rego object to avoid having them get + // re-initialized, and to propagate them to the prepared query. + for _, r := range s.manager.GetWasmResolvers() { + for _, entrypoint := range r.Entrypoints() { + opts = append(opts, rego.Resolver(entrypoint, r)) + } + } + + rego, err := s.makeRego(ctx, false, txn, input, urlPath, m, false, nil, opts) + if err != nil { + _ = logger.Log(ctx, txn, urlPath, "", goInput, input, nil, ndbCache, err, m) + writer.ErrorAuto(w, err) + return + } + + pq, err := rego.PrepareForEval(ctx) + if err != nil { + _ = logger.Log(ctx, txn, urlPath, "", goInput, input, nil, ndbCache, err, m) + writer.ErrorAuto(w, err) + return + } + preparedQuery = &pq + s.preparedEvalQueries.Insert(pqID, preparedQuery) + } + + evalOpts := []rego.EvalOption{ + rego.EvalTransaction(txn), + rego.EvalParsedInput(input), + rego.EvalMetrics(m), + rego.EvalInterQueryBuiltinCache(s.interQueryBuiltinCache), + rego.EvalInterQueryBuiltinValueCache(s.interQueryBuiltinValueCache), + rego.EvalNDBuiltinCache(ndbCache), + } + + rs, err := preparedQuery.Eval( + ctx, + evalOpts..., + ) + + m.Timer(metrics.ServerHandler).Stop() + + // Handle results. + if err != nil { + _ = logger.Log(ctx, txn, urlPath, "", goInput, input, nil, ndbCache, err, m) + writer.ErrorAuto(w, err) + return + } + + if len(rs) == 0 { + ref, err := stringPathToDataRef(urlPath) + if err != nil { + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, "invalid path: %v", err)) + return + } + + var messageType = types.MsgMissingError + if len(s.getCompiler().GetRulesForVirtualDocument(ref)) > 0 { + messageType = types.MsgFoundUndefinedError + } + errV1 := types.NewErrorV1(types.CodeUndefinedDocument, "%v: %v", messageType, ref) + if err := logger.Log(ctx, txn, urlPath, "", goInput, input, nil, ndbCache, errV1, m); err != nil { + writer.ErrorAuto(w, err) + return + } + + writer.Error(w, http.StatusNotFound, errV1) + return + } + err = logger.Log(ctx, txn, urlPath, "", goInput, input, &rs[0].Expressions[0].Value, ndbCache, nil, m) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + writer.JSONOK(w, rs[0].Expressions[0].Value, pretty(r)) +} + +func (s *Server) getCachedPreparedEvalQuery(key string, m metrics.Metrics) (*rego.PreparedEvalQuery, bool) { + pq, ok := s.preparedEvalQueries.Get(key) + counter := m.Counter(metrics.ServerQueryCacheHit) // Creates the counter on m if it doesn't exist, starts at 0 + if ok { + counter.Incr() // Increment counter on hit + return pq.(*rego.PreparedEvalQuery), true + } + return nil, false +} + +func (s *Server) canEval(ctx context.Context) bool { + // Create very simple query that binds a single variable. + opts := []func(*rego.Rego){ + rego.Compiler(s.getCompiler()), + rego.Store(s.store), + rego.Query("x = 1"), + } + + for _, r := range s.manager.GetWasmResolvers() { + for _, ep := range r.Entrypoints() { + opts = append(opts, rego.Resolver(ep, r)) + } + } + + eval := rego.New(opts...) + // Run evaluation. + rs, err := eval.Eval(ctx) + if err != nil { + return false + } + + v, ok := rs[0].Bindings["x"] + if ok { + jsonNumber, ok := v.(json.Number) + if ok && jsonNumber.String() == "1" { + return true + } + } + return false +} + +func (*Server) bundlesReady(pluginStatuses map[string]*plugins.Status) bool { + + // Look for a discovery plugin first, if it exists and isn't ready + // then don't bother with the others. + // Note: use "discovery" instead of `discovery.Name` to avoid import + // cycle problems.. + dpStatus, ok := pluginStatuses["discovery"] + if ok && dpStatus != nil && (dpStatus.State != plugins.StateOK) { + return false + } + + // The bundle plugin won't return "OK" until the first activation + // of each configured bundle. + bpStatus, ok := pluginStatuses[bundlePlugin.Name] + if ok && bpStatus != nil && (bpStatus.State != plugins.StateOK) { + return false + } + + return true +} + +func (s *Server) unversionedGetHealth(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + includeBundleStatus := getBoolParam(r.URL, types.ParamBundleActivationV1, true) || + getBoolParam(r.URL, types.ParamBundlesActivationV1, true) + includePluginStatus := getBoolParam(r.URL, types.ParamPluginsV1, true) + excludePlugin := getStringSliceParam(r.URL, types.ParamExcludePluginV1) + excludePluginMap := map[string]struct{}{} + for _, name := range excludePlugin { + excludePluginMap[name] = struct{}{} + } + + // Ensure the server can evaluate a simple query + if !s.canEval(ctx) { + writeHealthResponse(w, errors.New("unable to perform evaluation")) + return + } + + pluginStatuses := s.manager.PluginStatus() + + // Ensure that bundles (if configured, and requested to be included in the result) + // have been activated successfully. This will include discovery bundles as well as + // normal bundles that are configured. + if includeBundleStatus && !s.bundlesReady(pluginStatuses) { + // For backwards compatibility we don't return a payload with statuses for the bundle endpoint + writeHealthResponse(w, errors.New("one or more bundles are not activated")) + return + } + + if includePluginStatus { + // Ensure that all plugins (if requested to be included in the result) have an OK status. + hasErr := false + for name, status := range pluginStatuses { + if _, exclude := excludePluginMap[name]; exclude { + continue + } + if status != nil && status.State != plugins.StateOK { + hasErr = true + break + } + } + if hasErr { + writeHealthResponse(w, errors.New("one or more plugins are not up")) + return + } + } + writeHealthResponse(w, nil) +} + +func (s *Server) unversionedGetHealthWithPolicy(w http.ResponseWriter, r *http.Request) { + pluginStatus := s.manager.PluginStatus() + pluginState := map[string]string{} + + // optimistically assume all plugins are ok + allPluginsOk := true + + // build input document for health check query + input := func() map[string]any { + s.mtx.Lock() + defer s.mtx.Unlock() + + // iterate over plugin status to extract state + for name, status := range pluginStatus { + if status != nil { + pluginState[name] = string(status.State) + // if all plugins have not been in OK state yet, then check to see if plugin state is OKx + if !s.allPluginsOkOnce && status.State != plugins.StateOK { + allPluginsOk = false + } + } + } + // once all plugins are OK, set the allPluginsOkOnce flag to true, indicating that all + // plugins have achieved a "ready" state at least once on the server. + if allPluginsOk { + s.allPluginsOkOnce = true + } + + return map[string]any{ + "plugin_state": pluginState, + "plugins_ready": s.allPluginsOkOnce, + } + }() + + healthDataPath := "/system/health/" + escapedPathValue(r, "path") + + healthDataPathQuery, err := stringPathToQuery(healthDataPath) + if err != nil { + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, "invalid path: %v", err)) + return + } + + rego := rego.New( + rego.ParsedQuery(healthDataPathQuery), + rego.Compiler(s.getCompiler()), + rego.Store(s.store), + rego.Input(input), + rego.Runtime(s.runtime), + rego.PrintHook(s.manager.PrintHook()), + ) + + rs, err := rego.Eval(r.Context()) + if err != nil { + writeHealthResponse(w, err) + return + } + + if len(rs) == 0 { + writeHealthResponse(w, fmt.Errorf("health check (%v) was undefined", healthDataPathQuery)) + return + } + + result, ok := rs[0].Expressions[0].Value.(bool) + if ok && result { + writeHealthResponse(w, nil) + return + } + + writeHealthResponse(w, fmt.Errorf("health check (%v) returned unexpected value", healthDataPathQuery)) +} + +func writeHealthResponse(w http.ResponseWriter, err error) { + if err != nil { + writer.JSON(w, http.StatusInternalServerError, types.HealthResponseV1{Error: err.Error()}, false) + return + } + + writer.JSONOK(w, types.HealthResponseV1{}, false) +} + +func (s *Server) v1CompilePost(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + explainMode := getExplain(r.URL, types.ExplainOffV1) + includeInstrumentation := getBoolParam(r.URL, types.ParamInstrumentV1, true) + + m := metrics.New() + m.Timer(metrics.ServerHandler).Start() + m.Timer(metrics.RegoQueryParse).Start() + + // decompress the input if sent as zip + body, err := util.ReadMaybeCompressedBody(r) + if err != nil { + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, "could not decompress the body")) + return + } + + request, reqErr := readInputCompilePostV1(body, s.manager.ParserOptions()) + if reqErr != nil { + writer.Error(w, http.StatusBadRequest, reqErr) + return + } + + m.Timer(metrics.RegoQueryParse).Stop() + + c := storage.NewContext().WithMetrics(m) + txn, err := s.store.NewTransaction(ctx, storage.TransactionParams{Context: c}) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + defer s.store.Abort(ctx, txn) + + var buf *topdown.BufferTracer + if explainMode != types.ExplainOffV1 { + buf = topdown.NewBufferTracer() + } + + eval := rego.New( + rego.Compiler(s.getCompiler()), + rego.Store(s.store), + rego.Transaction(txn), + rego.ParsedQuery(request.Query), + rego.ParsedInput(request.Input), + rego.ParsedUnknowns(request.Unknowns), + rego.DisableInlining(request.Options.DisableInlining), + rego.NondeterministicBuiltins(request.Options.NondeterminsiticBuiltins), + rego.QueryTracer(buf), + rego.Instrument(includeInstrumentation), + rego.Metrics(m), + rego.Runtime(s.runtime), + rego.UnsafeBuiltins(unsafeBuiltinsMap), + rego.InterQueryBuiltinCache(s.interQueryBuiltinCache), + rego.InterQueryBuiltinValueCache(s.interQueryBuiltinValueCache), + rego.PrintHook(s.manager.PrintHook()), + ) + + pq, err := eval.Partial(ctx) + if err != nil { + switch err := err.(type) { + case ast.Errors: + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, types.MsgCompileModuleError).WithASTErrors(err)) + default: + writer.ErrorAuto(w, err) + } + return + } + + m.Timer(metrics.ServerHandler).Stop() + + result := types.CompileResponseV1{} + + if includeMetrics(r) || includeInstrumentation { + result.Metrics = m.All() + } + + if explainMode != types.ExplainOffV1 { + result.Explanation = s.getExplainResponse(explainMode, *buf, pretty(r)) + } + + var i any = types.PartialEvaluationResultV1{ + Queries: pq.Queries, + Support: pq.Support, + } + + result.Result = &i + + writer.JSONOK(w, result, pretty(r)) +} + +func (s *Server) v1DataGet(w http.ResponseWriter, r *http.Request) { + m := metrics.New() + + m.Timer(metrics.ServerHandler).Start() + + decisionID := s.generateDecisionID() + ctx := logging.WithDecisionID(r.Context(), decisionID) + annotateSpan(ctx, decisionID) + + urlPath := escapedPathValue(r, "path") + explainMode := getExplain(r.URL, types.ExplainOffV1) + includeInstrumentation := getBoolParam(r.URL, types.ParamInstrumentV1, true) + provenance := getBoolParam(r.URL, types.ParamProvenanceV1, true) + strictBuiltinErrors := getBoolParam(r.URL, types.ParamStrictBuiltinErrors, true) + + m.Timer(metrics.RegoInputParse).Start() + + inputs := r.URL.Query()[types.ParamInputV1] + + var input ast.Value + var goInput *any + + if len(inputs) > 0 { + var err error + input, goInput, err = readInputGetV1(inputs[len(inputs)-1]) + if err != nil { + writer.ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err) + return + } + } + + m.Timer(metrics.RegoInputParse).Stop() + + // Prepare for query. + c := storage.NewContext().WithMetrics(m) + txn, err := s.store.NewTransaction(ctx, storage.TransactionParams{Context: c}) + if err != nil { + writer.ErrorAuto(w, err) + return + } + defer s.store.Abort(ctx, txn) + + br, err := getRevisions(ctx, s.store, txn) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + ctx, logger := s.getDecisionLogger(ctx, br) + + var ndbCache builtins.NDBCache + if s.ndbCacheEnabled { + ndbCache = builtins.NDBCache{} + } + + var buf *topdown.BufferTracer + + if explainMode != types.ExplainOffV1 { + buf = topdown.NewBufferTracer() + } + + pqID := "v1DataGet::" + if strictBuiltinErrors { + pqID += "strict-builtin-errors::" + } + pqID += urlPath + preparedQuery, ok := s.getCachedPreparedEvalQuery(pqID, m) + if !ok { + opts := []func(*rego.Rego){ + rego.Compiler(s.getCompiler()), + rego.Store(s.store), + } + + for _, r := range s.manager.GetWasmResolvers() { + for _, entrypoint := range r.Entrypoints() { + opts = append(opts, rego.Resolver(entrypoint, r)) + } + } + + rego, err := s.makeRego(ctx, strictBuiltinErrors, txn, input, urlPath, m, includeInstrumentation, buf, opts) + if err != nil { + _ = logger.Log(ctx, txn, urlPath, "", goInput, input, nil, ndbCache, err, m) + writer.ErrorAuto(w, err) + return + } + + pq, err := rego.PrepareForEval(ctx) + if err != nil { + _ = logger.Log(ctx, txn, urlPath, "", goInput, input, nil, ndbCache, err, m) + writer.ErrorAuto(w, err) + return + } + preparedQuery = &pq + s.preparedEvalQueries.Insert(pqID, preparedQuery) + } + + evalOpts := []rego.EvalOption{ + rego.EvalTransaction(txn), + rego.EvalParsedInput(input), + rego.EvalMetrics(m), + rego.EvalQueryTracer(buf), + rego.EvalInterQueryBuiltinCache(s.interQueryBuiltinCache), + rego.EvalInterQueryBuiltinValueCache(s.interQueryBuiltinValueCache), + rego.EvalInstrument(includeInstrumentation), + rego.EvalNDBuiltinCache(ndbCache), + } + + rs, err := preparedQuery.Eval( + ctx, + evalOpts..., + ) + + m.Timer(metrics.ServerHandler).Stop() + + // Handle results. + if err != nil { + _ = logger.Log(ctx, txn, urlPath, "", goInput, input, nil, ndbCache, err, m) + writer.ErrorAuto(w, err) + return + } + + result := types.DataResponseV1{ + DecisionID: decisionID, + } + + if includeMetrics(r) || includeInstrumentation { + result.Metrics = m.All() + } + + if provenance { + result.Provenance = s.getProvenance(br) + } + + if len(rs) == 0 { + if explainMode == types.ExplainFullV1 { + result.Explanation, err = types.NewTraceV1(lineage.Full(*buf), pretty(r)) + if err != nil { + writer.ErrorAuto(w, err) + return + } + } + + if err := logger.Log(ctx, txn, urlPath, "", goInput, input, nil, ndbCache, nil, m); err != nil { + writer.ErrorAuto(w, err) + return + } + writer.JSONOK(w, result, pretty(r)) + return + } + + result.Result = &rs[0].Expressions[0].Value + + if explainMode != types.ExplainOffV1 { + result.Explanation = s.getExplainResponse(explainMode, *buf, pretty(r)) + } + + if err := logger.Log(ctx, txn, urlPath, "", goInput, input, result.Result, ndbCache, nil, m); err != nil { + writer.ErrorAuto(w, err) + return + } + writer.JSONOK(w, result, pretty(r)) +} + +func (s *Server) v1DataPatch(w http.ResponseWriter, r *http.Request) { + m := metrics.New() + m.Timer(metrics.ServerHandler).Start() + + ctx := r.Context() + var ops []types.PatchV1 + + m.Timer(metrics.RegoInputParse).Start() + if err := util.NewJSONDecoder(r.Body).Decode(&ops); err != nil { + writer.ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err) + return + } + m.Timer(metrics.RegoInputParse).Stop() + + patches, err := s.prepareV1PatchSlice(escapedPathValue(r, "path"), ops) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + params := storage.WriteParams + params.Context = storage.NewContext().WithMetrics(m) + txn, err := s.store.NewTransaction(ctx, params) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + for _, patch := range patches { + if err := s.checkPathScope(ctx, txn, patch.path); err != nil { + s.abortAuto(ctx, txn, w, err) + return + } + + if err := s.store.Write(ctx, txn, patch.op, patch.path, patch.value); err != nil { + s.abortAuto(ctx, txn, w, err) + return + } + } + + if err := ast.CheckPathConflicts(s.getCompiler(), storage.NonEmpty(ctx, s.store, txn)); len(err) > 0 { + s.store.Abort(ctx, txn) + writer.ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err) + return + } + + if err := s.store.Commit(ctx, txn); err != nil { + writer.ErrorAuto(w, err) + return + } + + m.Timer(metrics.ServerHandler).Stop() + + if includeMetrics(r) { + result := types.DataResponseV1{ + Metrics: m.All(), + } + writer.JSONOK(w, result, false) + return + } + + w.WriteHeader(http.StatusNoContent) +} + +func (s *Server) v1DataPost(w http.ResponseWriter, r *http.Request) { + m := s.getMetrics(r) + m.Timer(metrics.ServerHandler).Start() + + decisionID := s.generateDecisionID() + ctx := logging.WithDecisionID(r.Context(), decisionID) + annotateSpan(ctx, decisionID) + + m.Timer(metrics.RegoInputParse).Start() + + input, goInput, err := readInputPostV1(r) + if err != nil { + writer.ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err) + return + } + + m.Timer(metrics.RegoInputParse).Stop() + + txn, err := s.store.NewTransaction(ctx, storage.TransactionParams{Context: storage.NewContext().WithMetrics(m)}) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + defer s.store.Abort(ctx, txn) + + provenance := getBoolParam(r.URL, types.ParamProvenanceV1, true) + + var logger decisionLogger + var br bundleRevisions + + if s.logger != nil || provenance { + br, err = getRevisions(ctx, s.store, txn) + if err != nil { + writer.ErrorAuto(w, err) + return + } + if s.logger != nil { + ctx, logger = s.getDecisionLogger(ctx, br) + } + } + + var buf *topdown.BufferTracer + + explainMode := getExplain(r.URL, types.ExplainOffV1) + if explainMode != types.ExplainOffV1 { + buf = topdown.NewBufferTracer() + } + + var ndbCache builtins.NDBCache + if s.ndbCacheEnabled { + ndbCache = builtins.NDBCache{} + } + + urlPath := escapedPathValue(r, "path") + + strictBuiltinErrors := getBoolParam(r.URL, types.ParamStrictBuiltinErrors, true) + includeInstrumentation := getBoolParam(r.URL, types.ParamInstrumentV1, true) + + pqID := "v1DataPost::" + if strictBuiltinErrors { + pqID = "v1DataPost::strict-builtin-errors::" + } + pqID += urlPath + preparedQuery, ok := s.getCachedPreparedEvalQuery(pqID, m) + if !ok { + opts := []func(*rego.Rego){ + rego.Compiler(s.getCompiler()), + rego.Store(s.store), + } + + // Set resolvers on the base Rego object to avoid having them get + // re-initialized, and to propagate them to the prepared query. + for _, r := range s.manager.GetWasmResolvers() { + for _, entrypoint := range r.Entrypoints() { + opts = append(opts, rego.Resolver(entrypoint, r)) + } + } + + rego, err := s.makeRego(ctx, strictBuiltinErrors, txn, input, urlPath, m, includeInstrumentation, buf, opts) + if err != nil { + _ = logger.Log(ctx, txn, urlPath, "", goInput, input, nil, ndbCache, err, m) + writer.ErrorAuto(w, err) + return + } + + pq, err := rego.PrepareForEval(ctx) + if err != nil { + _ = logger.Log(ctx, txn, urlPath, "", goInput, input, nil, ndbCache, err, m) + writer.ErrorAuto(w, err) + return + } + preparedQuery = &pq + s.preparedEvalQueries.Insert(pqID, preparedQuery) + } + + rs, err := preparedQuery.Eval(ctx, + rego.EvalTransaction(txn), + rego.EvalParsedInput(input), + rego.EvalMetrics(m), + rego.EvalQueryTracer(buf), + rego.EvalInterQueryBuiltinCache(s.interQueryBuiltinCache), + rego.EvalInterQueryBuiltinValueCache(s.interQueryBuiltinValueCache), + rego.EvalInstrument(includeInstrumentation), + rego.EvalNDBuiltinCache(ndbCache), + ) + + m.Timer(metrics.ServerHandler).Stop() + + // Handle results. + if err != nil { + _ = logger.Log(ctx, txn, urlPath, "", goInput, input, nil, ndbCache, err, m) + writer.ErrorAuto(w, err) + return + } + + result := types.DataResponseV1{ + DecisionID: decisionID, + } + + if input == nil { + result.Warning = types.NewWarning(types.CodeAPIUsageWarn, types.MsgInputKeyMissing) + } + + includeMetrics := getBoolParam(r.URL, types.ParamMetricsV1, true) + if includeMetrics || includeInstrumentation { + result.Metrics = m.All() + } + + if provenance { + result.Provenance = s.getProvenance(br) + } + + if len(rs) == 0 { + if explainMode == types.ExplainFullV1 { + if result.Explanation, err = types.NewTraceV1(lineage.Full(*buf), pretty(r)); err != nil { + writer.ErrorAuto(w, err) + return + } + } + if err = logger.Log(ctx, txn, urlPath, "", goInput, input, nil, ndbCache, nil, m); err != nil { + writer.ErrorAuto(w, err) + return + } + writer.JSONOK(w, result, pretty(r)) + return + } + + result.Result = &rs[0].Expressions[0].Value + + if explainMode != types.ExplainOffV1 { + result.Explanation = s.getExplainResponse(explainMode, *buf, pretty(r)) + } + + if err := logger.Log(ctx, txn, urlPath, "", goInput, input, result.Result, ndbCache, nil, m); err != nil { + writer.ErrorAuto(w, err) + return + } + writer.JSONOK(w, result, pretty(r)) +} + +func escapedPathValue(r *http.Request, key string) string { + pathValue := r.PathValue(key) + escaped := r.URL.EscapedPath() + if !strings.Contains(escaped, "%") { + return pathValue + } + + i := strings.Index(r.URL.Path, pathValue) + if i == -1 || i > len(escaped) { + return pathValue + } + + return escaped[i:] +} + +func (s *Server) v1DataPut(w http.ResponseWriter, r *http.Request) { + m := metrics.New() + m.Timer(metrics.ServerHandler).Start() + + ctx := r.Context() + + m.Timer(metrics.RegoInputParse).Start() + var value any + if err := util.NewJSONDecoder(r.Body).Decode(&value); err != nil { + writer.ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err) + return + } + m.Timer(metrics.RegoInputParse).Stop() + + pv := escapedPathValue(r, "path") + + path, ok := storage.ParsePathEscaped("/" + strings.Trim(pv, "/")) + if !ok { + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, "bad path: %v", pv)) + return + } + + params := storage.WriteParams + params.Context = storage.NewContext().WithMetrics(m) + txn, err := s.store.NewTransaction(ctx, params) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + if err := s.checkPathScope(ctx, txn, path); err != nil { + s.abortAuto(ctx, txn, w, err) + return + } + + _, err = s.store.Read(ctx, txn, path) + if err != nil { + if !storage.IsNotFound(err) { + s.abortAuto(ctx, txn, w, err) + return + } + if len(path) > 0 { + if err := storage.MakeDir(ctx, s.store, txn, path[:len(path)-1]); err != nil { + s.abortAuto(ctx, txn, w, err) + return + } + } + } else if r.Header.Get("If-None-Match") == "*" { + s.store.Abort(ctx, txn) + w.WriteHeader(http.StatusNotModified) + return + } + + if err := s.store.Write(ctx, txn, storage.AddOp, path, value); err != nil { + s.abortAuto(ctx, txn, w, err) + return + } + + if err := ast.CheckPathConflicts(s.getCompiler(), storage.NonEmpty(ctx, s.store, txn)); len(err) > 0 { + s.store.Abort(ctx, txn) + writer.ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err) + return + } + + if err := s.store.Commit(ctx, txn); err != nil { + writer.ErrorAuto(w, err) + return + } + + m.Timer(metrics.ServerHandler).Stop() + + if includeMetrics(r) { + result := types.DataResponseV1{ + Metrics: m.All(), + } + writer.JSONOK(w, result, false) + return + } + + w.WriteHeader(http.StatusNoContent) +} + +func (s *Server) v1DataDelete(w http.ResponseWriter, r *http.Request) { + m := metrics.New() + m.Timer(metrics.ServerHandler).Start() + + ctx := r.Context() + + pv := escapedPathValue(r, "path") + path, ok := storage.ParsePathEscaped("/" + strings.Trim(pv, "/")) + if !ok { + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, "bad path: %v", pv)) + return + } + + params := storage.WriteParams + params.Context = storage.NewContext().WithMetrics(m) + txn, err := s.store.NewTransaction(ctx, params) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + if err := s.checkPathScope(ctx, txn, path); err != nil { + s.abortAuto(ctx, txn, w, err) + return + } + + _, err = s.store.Read(ctx, txn, path) + if err != nil { + s.abortAuto(ctx, txn, w, err) + return + } + + if err := s.store.Write(ctx, txn, storage.RemoveOp, path, nil); err != nil { + s.abortAuto(ctx, txn, w, err) + return + } + + if err := s.store.Commit(ctx, txn); err != nil { + writer.ErrorAuto(w, err) + return + } + + m.Timer(metrics.ServerHandler).Stop() + + if includeMetrics(r) { + result := types.DataResponseV1{ + Metrics: m.All(), + } + writer.JSONOK(w, result, false) + return + } + + w.WriteHeader(http.StatusNoContent) +} + +func (s *Server) v1PoliciesDelete(w http.ResponseWriter, r *http.Request) { + id := r.PathValue("path") + + m := metrics.New() + params := storage.WriteParams + params.Context = storage.NewContext().WithMetrics(m) + txn, err := s.store.NewTransaction(r.Context(), params) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + if err := s.checkPolicyIDScope(r.Context(), txn, id); err != nil { + s.abortAuto(r.Context(), txn, w, err) + return + } + + modules, err := s.loadModules(r.Context(), txn) + if err != nil { + s.abortAuto(r.Context(), txn, w, err) + return + } + + delete(modules, id) + + c := ast.NewCompiler().SetErrorLimit(s.errLimit) + + m.Timer(metrics.RegoModuleCompile).Start() + + if c.Compile(modules); c.Failed() { + s.abort(r.Context(), txn, func() { + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidOperation, types.MsgCompileModuleError).WithASTErrors(c.Errors)) + }) + return + } + + m.Timer(metrics.RegoModuleCompile).Stop() + + if err := s.store.DeletePolicy(r.Context(), txn, id); err != nil { + s.abortAuto(r.Context(), txn, w, err) + return + } + + if err := s.store.Commit(r.Context(), txn); err != nil { + writer.ErrorAuto(w, err) + return + } + + resp := types.PolicyDeleteResponseV1{} + if includeMetrics(r) { + resp.Metrics = m.All() + } + + writer.JSONOK(w, resp, pretty(r)) +} + +func (s *Server) v1PoliciesGet(w http.ResponseWriter, r *http.Request) { + txn, err := s.store.NewTransaction(r.Context()) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + defer s.store.Abort(r.Context(), txn) + + path := r.PathValue("path") + + bs, err := s.store.GetPolicy(r.Context(), txn, path) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + resp := types.PolicyGetResponseV1{ + Result: types.PolicyV1{ + ID: path, + Raw: string(bs), + AST: s.getCompiler().Modules[path], + }, + } + + writer.JSONOK(w, resp, pretty(r)) +} + +func (s *Server) v1PoliciesList(w http.ResponseWriter, r *http.Request) { + + ctx := r.Context() + + txn, err := s.store.NewTransaction(ctx) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + defer s.store.Abort(ctx, txn) + + policies := []types.PolicyV1{} + c := s.getCompiler() + + // Only return policies from the store, the compiler + // may contain additional partially compiled modules. + ids, err := s.store.ListPolicies(ctx, txn) + if err != nil { + writer.ErrorAuto(w, err) + return + } + for _, id := range ids { + bs, err := s.store.GetPolicy(ctx, txn, id) + if err != nil { + writer.ErrorAuto(w, err) + return + } + policy := types.PolicyV1{ + ID: id, + Raw: string(bs), + AST: c.Modules[id], + } + policies = append(policies, policy) + } + + writer.JSONOK(w, types.PolicyListResponseV1{Result: policies}, pretty(r)) +} + +func (s *Server) v1PoliciesPut(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + id := r.PathValue("path") + + includeMetrics := includeMetrics(r) + m := metrics.New() + + m.Timer("server_read_bytes").Start() + + buf, err := io.ReadAll(r.Body) + if err != nil { + writer.ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err) + return + } + + m.Timer("server_read_bytes").Stop() + + params := storage.WriteParams + params.Context = storage.NewContext().WithMetrics(m) + txn, err := s.store.NewTransaction(ctx, params) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + if err := s.checkPolicyIDScope(ctx, txn, id); err != nil && !storage.IsNotFound(err) { + s.abortAuto(ctx, txn, w, err) + return + } + + if bs, err := s.store.GetPolicy(ctx, txn, id); err != nil { + if !storage.IsNotFound(err) { + s.abortAuto(ctx, txn, w, err) + return + } + } else if bytes.Equal(buf, bs) { + s.store.Abort(ctx, txn) + resp := types.PolicyPutResponseV1{} + if includeMetrics { + resp.Metrics = m.All() + } + writer.JSONOK(w, resp, pretty(r)) + return + } + + m.Timer(metrics.RegoModuleParse).Start() + parsedMod, err := ast.ParseModuleWithOpts(id, string(buf), s.manager.ParserOptions()) + m.Timer(metrics.RegoModuleParse).Stop() + + if err != nil { + s.store.Abort(ctx, txn) + switch err := err.(type) { + case ast.Errors: + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, types.MsgCompileModuleError).WithASTErrors(err)) + default: + writer.ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err) + } + return + } + + if parsedMod == nil { + s.store.Abort(ctx, txn) + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, "empty module")) + return + } + + if err := s.checkPolicyPackageScope(ctx, txn, parsedMod.Package); err != nil { + s.abortAuto(ctx, txn, w, err) + return + } + + modules, err := s.loadModules(ctx, txn) + if err != nil { + s.abortAuto(ctx, txn, w, err) + return + } + + modules[id] = parsedMod + + c := ast.NewCompiler(). + SetErrorLimit(s.errLimit). + WithPathConflictsCheck(storage.NonEmpty(ctx, s.store, txn)). + WithEnablePrintStatements(s.manager.EnablePrintStatements()) + + m.Timer(metrics.RegoModuleCompile).Start() + + if c.Compile(modules); c.Failed() { + s.abort(ctx, txn, func() { + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, types.MsgCompileModuleError).WithASTErrors(c.Errors)) + }) + return + } + + m.Timer(metrics.RegoModuleCompile).Stop() + + if err := s.store.UpsertPolicy(ctx, txn, id, buf); err != nil { + s.abortAuto(ctx, txn, w, err) + return + } + + if err := s.store.Commit(ctx, txn); err != nil { + writer.ErrorAuto(w, err) + return + } + + resp := types.PolicyPutResponseV1{} + + if includeMetrics { + resp.Metrics = m.All() + } + + writer.JSONOK(w, resp, pretty(r)) +} + +func (s *Server) v1QueryGet(w http.ResponseWriter, r *http.Request) { + m := metrics.New() + + decisionID := s.generateDecisionID() + ctx := logging.WithDecisionID(r.Context(), decisionID) + annotateSpan(ctx, decisionID) + + values := r.URL.Query() + + qStrs := values[types.ParamQueryV1] + if len(qStrs) == 0 { + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, "missing parameter 'q'")) + return + } + qStr := qStrs[len(qStrs)-1] + + parsedQuery, err := validateQuery(qStr, s.manager.ParserOptions()) + if err != nil { + switch err := err.(type) { + case ast.Errors: + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, types.MsgParseQueryError).WithASTErrors(err)) + default: + writer.ErrorAuto(w, err) + } + return + } + + explainMode := getExplain(r.URL, types.ExplainOffV1) + includeInstrumentation := getBoolParam(r.URL, types.ParamInstrumentV1, true) + + params := storage.TransactionParams{Context: storage.NewContext().WithMetrics(m)} + txn, err := s.store.NewTransaction(ctx, params) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + defer s.store.Abort(ctx, txn) + + br, err := getRevisions(ctx, s.store, txn) + if err != nil { + writer.ErrorAuto(w, err) + return + } + pretty := pretty(r) + results, err := s.execQuery(ctx, br, txn, parsedQuery, nil, nil, m, explainMode, includeMetrics(r), includeInstrumentation, pretty) + if err != nil { + switch err := err.(type) { + case ast.Errors: + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, types.MsgCompileQueryError).WithASTErrors(err)) + default: + writer.ErrorAuto(w, err) + } + return + } + + writer.JSONOK(w, results, pretty) +} + +func (s *Server) v1QueryPost(w http.ResponseWriter, r *http.Request) { + m := metrics.New() + m.Timer(metrics.ServerHandler).Start() + + decisionID := s.generateDecisionID() + ctx := logging.WithDecisionID(r.Context(), decisionID) + annotateSpan(ctx, decisionID) + + var request types.QueryRequestV1 + err := util.NewJSONDecoder(r.Body).Decode(&request) + if err != nil { + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, "error(s) occurred while decoding request: %v", err.Error())) + return + } + qStr := request.Query + parsedQuery, err := validateQuery(qStr, s.manager.ParserOptions()) + if err != nil { + switch err := err.(type) { + case ast.Errors: + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, types.MsgParseQueryError).WithASTErrors(err)) + default: + writer.ErrorAuto(w, err) + } + return + } + + pretty := pretty(r) + explainMode := getExplain(r.URL, types.ExplainOffV1) + includeMetrics := includeMetrics(r) + includeInstrumentation := getBoolParam(r.URL, types.ParamInstrumentV1, true) + + var input ast.Value + + if request.Input != nil { + input, err = ast.InterfaceToValue(*request.Input) + if err != nil { + writer.ErrorAuto(w, err) + return + } + } + + params := storage.TransactionParams{Context: storage.NewContext().WithMetrics(m)} + txn, err := s.store.NewTransaction(ctx, params) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + defer s.store.Abort(ctx, txn) + + br, err := getRevisions(ctx, s.store, txn) + if err != nil { + writer.ErrorAuto(w, err) + return + } + + results, err := s.execQuery(ctx, br, txn, parsedQuery, input, request.Input, m, explainMode, includeMetrics, includeInstrumentation, pretty) + if err != nil { + switch err := err.(type) { + case ast.Errors: + writer.Error(w, http.StatusBadRequest, types.NewErrorV1(types.CodeInvalidParameter, types.MsgCompileQueryError).WithASTErrors(err)) + default: + writer.ErrorAuto(w, err) + } + return + } + + m.Timer(metrics.ServerHandler).Stop() + + if includeMetrics || includeInstrumentation { + results.Metrics = m.All() + } + + writer.JSONOK(w, results, pretty) +} + +func (s *Server) v1ConfigGet(w http.ResponseWriter, r *http.Request) { + result, err := s.manager.Config.ActiveConfig() + if err != nil { + writer.ErrorAuto(w, err) + return + } + writer.JSONOK(w, types.ConfigResponseV1{Result: &result}, pretty(r)) +} + +func (s *Server) v1StatusGet(w http.ResponseWriter, r *http.Request) { + p := status.Lookup(s.manager) + if p == nil { + writer.ErrorString(w, http.StatusInternalServerError, types.CodeInternal, errors.New("status plugin not enabled")) + return + } + + var st any = p.Snapshot() + writer.JSONOK(w, types.StatusResponseV1{Result: &st}, pretty(r)) +} + +func (s *Server) checkPolicyIDScope(ctx context.Context, txn storage.Transaction, id string) error { + + bs, err := s.store.GetPolicy(ctx, txn, id) + if err != nil { + return err + } + + module, err := ast.ParseModuleWithOpts(id, string(bs), s.manager.ParserOptions()) + if err != nil { + return err + } + + return s.checkPolicyPackageScope(ctx, txn, module.Package) +} + +func (s *Server) checkPolicyPackageScope(ctx context.Context, txn storage.Transaction, pkg *ast.Package) error { + + path, err := pkg.Path.Ptr() + if err != nil { + return err + } + + spath, ok := storage.ParsePathEscaped("/" + path) + if !ok { + return types.BadRequestErr("invalid package path: cannot determine scope") + } + + return s.checkPathScope(ctx, txn, spath) +} + +func (s *Server) getMetrics(r *http.Request) metrics.Metrics { + metricsInQuery := getBoolParam(r.URL, types.ParamMetricsV1, true) + instrumentationInQuery := getBoolParam(r.URL, types.ParamInstrumentV1, true) + + if s.logger == nil && !metricsInQuery && !instrumentationInQuery { + return metrics.NoOp() + } + + return metrics.New() +} + +func (s *Server) checkPathScope(ctx context.Context, txn storage.Transaction, path storage.Path) error { + + names, err := bundle.ReadBundleNamesFromStore(ctx, s.store, txn) + if err != nil { + if !storage.IsNotFound(err) { + return err + } + return nil + } + + bundleRoots := map[string][]string{} + for _, name := range names { + roots, err := bundle.ReadBundleRootsFromStore(ctx, s.store, txn, name) + if err != nil && !storage.IsNotFound(err) { + return err + } + bundleRoots[name] = roots + } + + spath := strings.Trim(path.String(), "/") + + if spath == "" && len(bundleRoots) > 0 { + return types.BadRequestErr("can't write to document root with bundle roots configured") + } + + spathParts := strings.Split(spath, "/") + + for name, roots := range bundleRoots { + if roots == nil { + return types.BadRequestErr(fmt.Sprintf("all paths owned by bundle %q", name)) + } + for _, root := range roots { + if root == "" { + return types.BadRequestErr(fmt.Sprintf("all paths owned by bundle %q", name)) + } + if isPathOwned(spathParts, strings.Split(root, "/")) { + return types.BadRequestErr(fmt.Sprintf("path %v is owned by bundle %q", spath, name)) + } + } + } + + return nil +} + +func (s *Server) getDecisionLogger(ctx context.Context, br bundleRevisions) (context.Context, decisionLogger) { + var logger decisionLogger + if intermediateResultsEnabled { + ctx = context.WithValue(ctx, IntermediateResultsContextKey{}, make(map[string]any)) + } + + // For backwards compatibility use `revision` as needed. + if s.hasLegacyBundle(br) { + logger.revision = br.LegacyRevision + } else { + logger.revisions = br.Revisions + } + logger.logger = s.logger + return ctx, logger +} + +func (*Server) getExplainResponse(explainMode types.ExplainModeV1, trace []*topdown.Event, pretty bool) (explanation types.TraceV1) { + switch explainMode { + case types.ExplainNotesV1: + var err error + explanation, err = types.NewTraceV1(lineage.Notes(trace), pretty) + if err != nil { + break + } + case types.ExplainFailsV1: + var err error + explanation, err = types.NewTraceV1(lineage.Fails(trace), pretty) + if err != nil { + break + } + case types.ExplainFullV1: + var err error + explanation, err = types.NewTraceV1(lineage.Full(trace), pretty) + if err != nil { + break + } + case types.ExplainDebugV1: + var err error + explanation, err = types.NewTraceV1(lineage.Debug(trace), pretty) + if err != nil { + break + } + } + return explanation +} + +func (s *Server) abort(ctx context.Context, txn storage.Transaction, finish func()) { + s.store.Abort(ctx, txn) + finish() +} + +func (s *Server) abortAuto(ctx context.Context, txn storage.Transaction, w http.ResponseWriter, err error) { + s.abort(ctx, txn, func() { writer.ErrorAuto(w, err) }) +} + +func (s *Server) loadModules(ctx context.Context, txn storage.Transaction) (map[string]*ast.Module, error) { + + ids, err := s.store.ListPolicies(ctx, txn) + if err != nil { + return nil, err + } + + modules := make(map[string]*ast.Module, len(ids)) + + for _, id := range ids { + bs, err := s.store.GetPolicy(ctx, txn, id) + if err != nil { + return nil, err + } + + parsed, err := ast.ParseModuleWithOpts(id, string(bs), s.manager.ParserOptions()) + if err != nil { + return nil, err + } + + modules[id] = parsed + } + + return modules, nil +} + +func (s *Server) getCompiler() *ast.Compiler { + return s.manager.GetCompiler() +} + +func (s *Server) makeRego(_ context.Context, + strictBuiltinErrors bool, + txn storage.Transaction, + input ast.Value, + urlPath string, + m metrics.Metrics, + instrument bool, + tracer topdown.QueryTracer, + opts []func(*rego.Rego), +) (*rego.Rego, error) { + query, err := stringPathToQuery(urlPath) + if err != nil { + return nil, types.NewErrorV1(types.CodeInvalidParameter, "invalid path: %v", err) + } + + opts = append( + opts, + rego.Transaction(txn), + rego.ParsedQuery(query), + rego.ParsedInput(input), + rego.Metrics(m), + rego.QueryTracer(tracer), + rego.Instrument(instrument), + rego.Runtime(s.runtime), + rego.UnsafeBuiltins(unsafeBuiltinsMap), + rego.StrictBuiltinErrors(strictBuiltinErrors), + rego.PrintHook(s.manager.PrintHook()), + rego.DistributedTracingOpts(s.distributedTracingOpts), + ) + + return rego.New(opts...), nil +} + +func stringPathToQuery(urlPath string) (ast.Body, error) { + ref, err := stringPathToDataRef(urlPath) + if err != nil { + return nil, err + } + + return parseRefQuery(ref.String()) +} + +// parseRefQuery parses a string into a query ast.Body. +// The resulting query must be comprised of a single ref, or an error will be returned. +func parseRefQuery(str string) (ast.Body, error) { + query, err := ast.ParseBody(str) + if err != nil { + return nil, errors.New("failed to parse query") + } + + // assert the query is exactly one statement + if l := len(query); l == 0 { + return nil, errors.New("no ref") + } else if l > 1 { + return nil, errors.New("complex query") + } + + // assert the single statement is a lone ref + expr := query[0] + switch t := expr.Terms.(type) { + case *ast.Term: + switch t.Value.(type) { + case ast.Ref: + return query, nil + } + } + + return nil, errors.New("complex query") +} + +func (*Server) prepareV1PatchSlice(root string, ops []types.PatchV1) (result []patchImpl, err error) { + + root = "/" + strings.Trim(root, "/") + + for _, op := range ops { + + impl := patchImpl{ + value: op.Value, + } + + // Map patch operation. + switch op.Op { + case "add": + impl.op = storage.AddOp + case "remove": + impl.op = storage.RemoveOp + case "replace": + impl.op = storage.ReplaceOp + default: + return nil, types.BadPatchOperationErr(op.Op) + } + + // Construct patch path. + path := strings.Trim(op.Path, "/") + if len(path) > 0 { + if root == "/" { + path = root + path + } else { + path = root + "/" + path + } + } else { + path = root + } + + var ok bool + impl.path, ok = patch.ParsePatchPathEscaped(path) + if !ok { + return nil, types.BadPatchPathErr(op.Path) + } + + result = append(result, impl) + } + + return result, nil +} + +func (s *Server) generateDecisionID() string { + if s.decisionIDFactory != nil { + return s.decisionIDFactory() + } + return "" +} + +func (s *Server) getProvenance(br bundleRevisions) *types.ProvenanceV1 { + + p := &types.ProvenanceV1{ + Version: version.Version, + Vcs: version.Vcs, + Timestamp: version.Timestamp, + Hostname: version.Hostname, + } + + // For backwards compatibility, if the bundles are using the old + // style config we need to fill in the older `Revision` field. + // Otherwise use the newer `Bundles` keyword. + if s.hasLegacyBundle(br) { + p.Revision = br.LegacyRevision + } else { + p.Bundles = map[string]types.ProvenanceBundleV1{} + for name, revision := range br.Revisions { + p.Bundles[name] = types.ProvenanceBundleV1{Revision: revision} + } + } + + return p +} + +func (s *Server) hasLegacyBundle(br bundleRevisions) bool { + bp := bundlePlugin.Lookup(s.manager) + return br.LegacyRevision != "" || (bp != nil && !bp.Config().IsMultiBundle()) +} + +func (s *Server) generateDefaultDecisionPath() string { + // Assume the path is safe to transition back to a url + p, _ := s.manager.Config.DefaultDecisionRef().Ptr() + return p +} + +func isPathOwned(path, root []string) bool { + for i := 0; i < len(path) && i < len(root); i++ { + if path[i] != root[i] { + return false + } + } + return true +} + +func (s *Server) updateCacheConfig(cacheConfig *iCache.Config) { + s.interQueryBuiltinCache.UpdateConfig(cacheConfig) + s.interQueryBuiltinValueCache.UpdateConfig(cacheConfig) +} + +func (s *Server) updateNDCache(enabled bool) { + s.mtx.Lock() + defer s.mtx.Unlock() + s.ndbCacheEnabled = enabled +} + +func stringPathToDataRef(s string) (ast.Ref, error) { + result := ast.Ref{ast.DefaultRootDocument} + r, err := stringPathToRef(s) + if err != nil { + return nil, err + } + return append(result, r...), nil +} + +func stringPathToRef(s string) (ast.Ref, error) { + r := ast.Ref{} + + if len(s) == 0 { + return r, nil + } + + p := strings.Split(s, "/") + for _, x := range p { + if x == "" { + continue + } + + if y, err := url.PathUnescape(x); err == nil { + x = y + } + + if strings.Contains(x, "\"") { + return nil, fmt.Errorf("invalid ref term '%s'", x) + } + + i, err := strconv.Atoi(x) + if err != nil { + r = append(r, ast.StringTerm(x)) + } else { + r = append(r, ast.IntNumberTerm(i)) + } + } + return r, nil +} + +func validateQuery(query string, opts ast.ParserOptions) (ast.Body, error) { + return ast.ParseBodyWithOpts(query, opts) +} + +func getBoolParam(url *url.URL, name string, ifEmpty bool) bool { + if url.RawQuery == "" { + return false + } + + p, ok := url.Query()[name] + if !ok { + return false + } + + // Query params w/o values are represented as slice (of len 1) with an + // empty string. + if len(p) == 1 && p[0] == "" { + return ifEmpty + } + + for _, x := range p { + if strings.EqualFold(x, "true") { + return true + } + } + + return false +} + +func getStringSliceParam(url *url.URL, name string) []string { + + p, ok := url.Query()[name] + if !ok { + return nil + } + + // Query params w/o values are represented as slice (of len 1) with an + // empty string. + if len(p) == 1 && p[0] == "" { + return nil + } + + return p +} + +func getExplain(url *url.URL, zero types.ExplainModeV1) types.ExplainModeV1 { + if url.RawQuery == "" { + return zero + } + + for _, x := range url.Query()[types.ParamExplainV1] { + switch x { + case string(types.ExplainNotesV1): + return types.ExplainNotesV1 + case string(types.ExplainFailsV1): + return types.ExplainFailsV1 + case string(types.ExplainFullV1): + return types.ExplainFullV1 + case string(types.ExplainDebugV1): + return types.ExplainDebugV1 + } + } + return zero +} + +func readInputV0(r *http.Request) (ast.Value, *any, error) { + + parsed, ok := authorizer.GetBodyOnContext(r.Context()) + if ok { + v, err := ast.InterfaceToValue(parsed) + return v, &parsed, err + } + + // decompress the input if sent as zip + bodyBytes, err := util.ReadMaybeCompressedBody(r) + if err != nil { + return nil, nil, fmt.Errorf("could not decompress the body: %w", err) + } + + var x any + + if strings.Contains(r.Header.Get("Content-Type"), "yaml") { + if len(bodyBytes) > 0 { + if err = util.Unmarshal(bodyBytes, &x); err != nil { + return nil, nil, fmt.Errorf("body contains malformed input document: %w", err) + } + } + } else { + dec := util.NewJSONDecoder(bytes.NewBuffer(bodyBytes)) + if err := dec.Decode(&x); err != nil && err != io.EOF { + return nil, nil, fmt.Errorf("body contains malformed input document: %w", err) + } + } + + v, err := ast.InterfaceToValue(x) + return v, &x, err +} + +func readInputGetV1(str string) (ast.Value, *any, error) { + var input any + if err := util.UnmarshalJSON([]byte(str), &input); err != nil { + return nil, nil, fmt.Errorf("parameter contains malformed input document: %w", err) + } + v, err := ast.InterfaceToValue(input) + return v, &input, err +} + +func readInputPostV1(r *http.Request) (ast.Value, *any, error) { + + parsed, ok := authorizer.GetBodyOnContext(r.Context()) + if ok { + if obj, ok := parsed.(map[string]any); ok { + if input, ok := obj["input"]; ok { + v, err := ast.InterfaceToValue(input) + return v, &input, err + } + } + return nil, nil, nil + } + + var request types.DataRequestV1 + + // decompress the input if sent as zip + bodyBytes, err := util.ReadMaybeCompressedBody(r) + if err != nil { + return nil, nil, fmt.Errorf("could not decompress the body: %w", err) + } + + ct := r.Header.Get("Content-Type") + // There is no standard for yaml mime-type so we just look for + // anything related + if strings.Contains(ct, "yaml") { + if len(bodyBytes) > 0 { + if err = util.Unmarshal(bodyBytes, &request); err != nil { + return nil, nil, fmt.Errorf("body contains malformed input document: %w", err) + } + } + } else { + dec := util.NewJSONDecoder(bytes.NewBuffer(bodyBytes)) + if err := dec.Decode(&request); err != nil && err != io.EOF { + return nil, nil, fmt.Errorf("body contains malformed input document: %w", err) + } + } + + if request.Input == nil { + return nil, nil, nil + } + + v, err := ast.InterfaceToValue(*request.Input) + return v, request.Input, err +} + +type compileRequest struct { + Query ast.Body + Input ast.Value + Unknowns []*ast.Term + Options compileRequestOptions +} + +type compileRequestOptions struct { + DisableInlining []string + NondeterminsiticBuiltins bool +} + +func readInputCompilePostV1(reqBytes []byte, queryParserOptions ast.ParserOptions) (*compileRequest, *types.ErrorV1) { + var request types.CompileRequestV1 + + err := util.NewJSONDecoder(bytes.NewBuffer(reqBytes)).Decode(&request) + if err != nil { + return nil, types.NewErrorV1(types.CodeInvalidParameter, "error(s) occurred while decoding request: %v", err.Error()) + } + + query, err := ast.ParseBodyWithOpts(request.Query, queryParserOptions) + if err != nil { + switch err := err.(type) { + case ast.Errors: + return nil, types.NewErrorV1(types.CodeInvalidParameter, types.MsgParseQueryError).WithASTErrors(err) + default: + return nil, types.NewErrorV1(types.CodeInvalidParameter, "%v: %v", types.MsgParseQueryError, err) + } + } else if len(query) == 0 { + return nil, types.NewErrorV1(types.CodeInvalidParameter, "missing required 'query' value") + } + + var input ast.Value + if request.Input != nil { + input, err = ast.InterfaceToValue(*request.Input) + if err != nil { + return nil, types.NewErrorV1(types.CodeInvalidParameter, "error(s) occurred while converting input: %v", err) + } + } + + var unknowns []*ast.Term + if request.Unknowns != nil { + unknowns = make([]*ast.Term, len(*request.Unknowns)) + for i, s := range *request.Unknowns { + unknowns[i], err = ast.ParseTerm(s) + if err != nil { + return nil, types.NewErrorV1(types.CodeInvalidParameter, "error(s) occurred while parsing unknowns: %v", err) + } + } + } + + return &compileRequest{ + Query: query, + Input: input, + Unknowns: unknowns, + Options: compileRequestOptions{ + DisableInlining: request.Options.DisableInlining, + NondeterminsiticBuiltins: request.Options.NondeterministicBuiltins, + }, + }, nil +} + +var indexHTML, _ = template.New("index").Parse(` + + + + + +

+ ________      ________    ________
+|\   __  \    |\   __  \  |\   __  \
+\ \  \|\  \   \ \  \|\  \ \ \  \|\  \
+ \ \  \\\  \   \ \   ____\ \ \   __  \
+  \ \  \\\  \   \ \  \___|  \ \  \ \  \
+   \ \_______\   \ \__\      \ \__\ \__\
+    \|_______|    \|__|       \|__|\|__|
+
+Open Policy Agent - An open source project to policy-enable your service.
+
+Version: {{ .Version }}
+Build Commit: {{ .BuildCommit }}
+Build Timestamp: {{ .BuildTimestamp }}
+Build Hostname: {{ .BuildHostname }}
+
+Query:
+
+
Input Data (JSON):
+
+
+
+ + +`) + +type decisionLogger struct { + revisions map[string]string + revision string // Deprecated: Use `revisions` instead. + logger func(context.Context, *Info) error +} + +func (l decisionLogger) Log( + ctx context.Context, + txn storage.Transaction, + path string, + query string, + goInput *any, + astInput ast.Value, + goResults *any, + ndbCache builtins.NDBCache, + err error, + m metrics.Metrics, +) error { + if l.logger == nil { + return nil + } + + bundles := map[string]BundleInfo{} + for name, rev := range l.revisions { + bundles[name] = BundleInfo{Revision: rev} + } + + rctx := logging.RequestContext{} + if r, ok := logging.FromContext(ctx); ok { + rctx = *r + } + decisionID, _ := logging.DecisionIDFromContext(ctx) + + var httpRctx logging.HTTPRequestContext + + httpRctxVal, _ := logging.HTTPRequestContextFromContext(ctx) + if httpRctxVal != nil { + httpRctx = *httpRctxVal + } + + info := &Info{ + Txn: txn, + Revision: l.revision, + Bundles: bundles, + Timestamp: time.Now().UTC(), + DecisionID: decisionID, + RemoteAddr: rctx.ClientAddr, + HTTPRequestContext: httpRctx, + Path: path, + Query: query, + Input: goInput, + InputAST: astInput, + Results: goResults, + Error: err, + Metrics: m, + RequestID: rctx.ReqID, + } + + if ndbCache != nil { + x, err := ast.JSON(ndbCache.AsValue()) + if err != nil { + return err + } + info.NDBuiltinCache = &x + } + + sctx := trace.SpanFromContext(ctx).SpanContext() + if sctx.IsValid() { + info.TraceID = sctx.TraceID().String() + info.SpanID = sctx.SpanID().String() + } + + if intermediateResultsEnabled { + if iresults, ok := ctx.Value(IntermediateResultsContextKey{}).(map[string]any); ok { + info.IntermediateResults = iresults + } + } + + if l.logger != nil { + if err := l.logger(ctx, info); err != nil { + return fmt.Errorf("decision_logs: %w", err) + } + } + + return nil +} + +type patchImpl struct { + path storage.Path + op storage.PatchOp + value any +} + +func parseURL(s string, useHTTPSByDefault bool) (*url.URL, error) { + if !strings.Contains(s, "://") { + scheme := "http://" + if useHTTPSByDefault { + scheme = "https://" + } + s = scheme + s + } + return url.Parse(s) +} + +func annotateSpan(ctx context.Context, decisionID string) { + if decisionID != "" { + trace.SpanFromContext(ctx).SetAttributes(attribute.String(otelDecisionIDAttr, decisionID)) + } +} + +func pretty(r *http.Request) bool { + return getBoolParam(r.URL, types.ParamPrettyV1, true) +} + +func includeMetrics(r *http.Request) bool { + return getBoolParam(r.URL, types.ParamMetricsV1, true) +} diff --git a/third_party/opa/v1/server/server_bench_test.go b/third_party/opa/v1/server/server_bench_test.go new file mode 100644 index 000000000000..419130be632b --- /dev/null +++ b/third_party/opa/v1/server/server_bench_test.go @@ -0,0 +1,72 @@ +package server + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/open-policy-agent/opa/v1/plugins" + "github.com/open-policy-agent/opa/v1/storage/inmem" +) + +func BenchmarkDataPostV1Request(b *testing.B) { + f := newBenchFixture(b) + err := f.v1(http.MethodPut, "/policies/test", `package test + +default hello := false + +hello if input.message == "world" +`, 200, "") + if err != nil { + b.Fatal(err) + } + + for i := range b.N { + req := newReqV1("POST", "/data/test", `{"input": {"message": "world"}}`) + + err := f.executeRequest(req, 200, "{\"result\":{\"hello\":true}}\n") + if err != nil { + b.Fatalf("Unexpected error from POST /data/test: %v, iteration: %d", err, i) + } + } +} + +func newBenchFixture(b *testing.B, opts ...any) *fixture { + ctx := context.Background() + server := New(). + WithAddresses([]string{"localhost:8182"}). + WithStore(inmem.New()) // potentially overridden via opts + + for _, opt := range opts { + if opt, ok := opt.(func(*Server)); ok { + opt(server) + } + } + + var mOpts []func(*plugins.Manager) + for _, opt := range opts { + if opt, ok := opt.(func(*plugins.Manager)); ok { + mOpts = append(mOpts, opt) + } + } + + m, err := plugins.New([]byte{}, "test", server.store, mOpts...) + if err != nil { + b.Fatal(err) + } + server = server.WithManager(m) + if err := m.Start(ctx); err != nil { + b.Fatal(err) + } + server, err = server.Init(ctx) + if err != nil { + b.Fatal(err) + } + recorder := httptest.NewRecorder() + + return &fixture{ + server: server, + recorder: recorder, + } +} diff --git a/third_party/opa/v1/server/server_test.go b/third_party/opa/v1/server/server_test.go new file mode 100644 index 000000000000..2c5346de5536 --- /dev/null +++ b/third_party/opa/v1/server/server_test.go @@ -0,0 +1,7042 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: goconst // string duplication is for test readability. +package server + +import ( + "bytes" + "compress/gzip" + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/binary" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "io" + "log" + "math" + "math/big" + "net" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "reflect" + "slices" + "strconv" + "strings" + "sync/atomic" + "testing" + "time" + + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + + "github.com/open-policy-agent/opa/internal/distributedtracing" + "github.com/open-policy-agent/opa/internal/prometheus" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/config" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/plugins" + pluginBundle "github.com/open-policy-agent/opa/v1/plugins/bundle" + pluginStatus "github.com/open-policy-agent/opa/v1/plugins/status" + "github.com/open-policy-agent/opa/v1/server/authorizer" + "github.com/open-policy-agent/opa/v1/server/identifier" + "github.com/open-policy-agent/opa/v1/server/types" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/disk" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" + "github.com/open-policy-agent/opa/v1/version" + prom "github.com/prometheus/client_golang/prometheus" +) + +type tr struct { + method string + path string + body string + code int + resp string +} + +func TestUnversionedGetHealth(t *testing.T) { + t.Parallel() + + f := newFixture(t) + req := newReqUnversioned(http.MethodGet, "/health", "") + validateDiagnosticRequest(t, f, req, 200, `{}`) +} + +func TestUnversionedGetHealthBundleNoBundleSet(t *testing.T) { + t.Parallel() + + f := newFixture(t) + req := newReqUnversioned(http.MethodGet, "/health?bundles=true", "") + validateDiagnosticRequest(t, f, req, 200, `{}`) +} + +func TestUnversionedGetHealthCheckOnlyBundlePlugin(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + // Initialize the server as if a bundle plugin was + // configured on the manager. + f.server.manager.UpdatePluginStatus("bundle", &plugins.Status{State: plugins.StateNotReady}) + + // The bundle hasn't been activated yet, expect the health check to fail + req := newReqUnversioned(http.MethodGet, "/health?bundles=true", "") + validateDiagnosticRequest(t, f, req, 500, `{"error":"one or more bundles are not activated"}`) + + // Set the bundle to be activated. + f.server.manager.UpdatePluginStatus("bundle", &plugins.Status{State: plugins.StateOK}) + + // The heath check should now respond as healthy + req = newReqUnversioned(http.MethodGet, "/health?bundles=true", "") + validateDiagnosticRequest(t, f, req, 200, `{}`) +} + +func TestUnversionedGetHealthCheckDiscoveryWithBundle(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + // Initialize the server as if a discovery bundle is configured + f.server.manager.UpdatePluginStatus("discovery", &plugins.Status{State: plugins.StateNotReady}) + + // The discovery bundle hasn't been activated yet, expect the health check to fail + req := newReqUnversioned(http.MethodGet, "/health?bundles=true", "") + validateDiagnosticRequest(t, f, req, 500, `{"error":"one or more bundles are not activated"}`) + + // Set the bundle to be not ready (plugin configured and created, but hasn't activated all bundles yet). + f.server.manager.UpdatePluginStatus("discovery", &plugins.Status{State: plugins.StateOK}) + f.server.manager.UpdatePluginStatus("bundle", &plugins.Status{State: plugins.StateNotReady}) + + // The discovery bundle is OK, but the newly configured bundle hasn't been activated yet, expect the health check to fail + req = newReqUnversioned(http.MethodGet, "/health?bundles=true", "") + validateDiagnosticRequest(t, f, req, 500, `{"error":"one or more bundles are not activated"}`) + + // Set the bundle to be activated. + f.server.manager.UpdatePluginStatus("bundle", &plugins.Status{State: plugins.StateOK}) + + // The heath check should now respond as healthy + req = newReqUnversioned(http.MethodGet, "/health?bundles=true", "") + validateDiagnosticRequest(t, f, req, 200, `{}`) +} + +func TestUnversionedGetHealthCheckBundleActivationSingleLegacy(t *testing.T) { + t.Parallel() + + // Initialize the server as if there is no bundle plugin + + f := newFixture(t) + + ctx := context.Background() + + // The server doesn't know about any bundles, so return a healthy status + req := newReqUnversioned(http.MethodGet, "/health?bundle=true", "") + validateDiagnosticRequest(t, f, req, 200, `{}`) + + err := storage.Txn(ctx, f.server.store, storage.WriteParams, func(txn storage.Transaction) error { + return bundle.LegacyWriteManifestToStore(ctx, f.server.store, txn, bundle.Manifest{ + Revision: "a", + }) + }) + + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + // The heath check still respond as healthy with a legacy bundle found in storage + req = newReqUnversioned(http.MethodGet, "/health?bundle=true", "") + validateDiagnosticRequest(t, f, req, 200, `{}`) +} + +func TestBundlesReady(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + status map[string]*plugins.Status + ready bool + }{ + { + note: "nil status", + status: nil, + ready: true, + }, + { + note: "empty status", + status: map[string]*plugins.Status{}, + ready: true, + }, + { + note: "discovery not ready - bundle missing", + status: map[string]*plugins.Status{ + "discovery": {State: plugins.StateNotReady}, + }, + ready: false, + }, + { + note: "discovery ok - bundle missing", + status: map[string]*plugins.Status{ + "discovery": {State: plugins.StateOK}, + }, + ready: true, // bundles aren't enabled, only discovery plugin configured + }, + { + note: "discovery missing - bundle not ready", + status: map[string]*plugins.Status{ + "bundle": {State: plugins.StateNotReady}, + }, + ready: false, + }, + { + note: "discovery missing - bundle ok", + status: map[string]*plugins.Status{ + "bundle": {State: plugins.StateOK}, + }, + ready: true, // discovery isn't enabled, only bundle plugin configured + }, + { + note: "discovery not ready - bundle not ready", + status: map[string]*plugins.Status{ + "discovery": {State: plugins.StateNotReady}, + "bundle": {State: plugins.StateNotReady}, + }, + ready: false, + }, + { + note: "discovery ok - bundle not ready", + status: map[string]*plugins.Status{ + "discovery": {State: plugins.StateOK}, + "bundle": {State: plugins.StateNotReady}, + }, + ready: false, + }, + { + note: "discovery not ready - bundle ok", + status: map[string]*plugins.Status{ + "discovery": {State: plugins.StateNotReady}, + "bundle": {State: plugins.StateOK}, + }, + ready: false, + }, + { + note: "discovery ok - bundle ok", + status: map[string]*plugins.Status{ + "discovery": {State: plugins.StateOK}, + "bundle": {State: plugins.StateOK}, + }, + ready: true, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + f := newFixture(t) + + actual := f.server.bundlesReady(tc.status) + if actual != tc.ready { + t.Errorf("Expected %t got %t", tc.ready, actual) + } + }) + } +} + +func TestUnversionedGetHealthCheckDiscoveryWithPlugins(t *testing.T) { + t.Parallel() + + // Use the same server through the cases, the status updates apply incrementally to it. + f := newFixture(t) + + cases := []struct { + note string + statusUpdates map[string]*plugins.Status + exp int + expBody string + }{ + { + note: "no plugins configured", + statusUpdates: nil, + exp: 200, + expBody: `{}`, + }, + { + note: "one plugin configured - not ready", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateNotReady}, + }, + exp: 500, + expBody: `{"error": "one or more plugins are not up"}`, + }, + { + note: "one plugin configured - ready", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + }, + exp: 200, + expBody: `{}`, + }, + { + note: "one plugin configured - error state", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateErr}, + }, + exp: 500, + expBody: `{"error": "one or more plugins are not up"}`, + }, + { + note: "one plugin configured - recovered from error", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + }, + exp: 200, + expBody: `{}`, + }, + { + note: "add second plugin - not ready", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + "p2": {State: plugins.StateNotReady}, + }, + exp: 500, + expBody: `{"error": "one or more plugins are not up"}`, + }, + { + note: "add third plugin - not ready", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + "p2": {State: plugins.StateNotReady}, + "p3": {State: plugins.StateNotReady}, + }, + exp: 500, + expBody: `{"error": "one or more plugins are not up"}`, + }, + { + note: "mixed states - not ready", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + "p2": {State: plugins.StateErr}, + "p3": {State: plugins.StateNotReady}, + }, + exp: 500, + expBody: `{"error": "one or more plugins are not up"}`, + }, + { + note: "mixed states - still not ready", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + "p2": {State: plugins.StateErr}, + "p3": {State: plugins.StateOK}, + }, + exp: 500, + expBody: `{"error": "one or more plugins are not up"}`, + }, + { + note: "all plugins ready", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + "p2": {State: plugins.StateOK}, + "p3": {State: plugins.StateOK}, + }, + exp: 200, + expBody: `{}`, + }, + { + note: "one plugins fails", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateErr}, + "p2": {State: plugins.StateOK}, + "p3": {State: plugins.StateOK}, + }, + exp: 500, + expBody: `{"error": "one or more plugins are not up"}`, + }, + { + note: "all plugins ready - recovery", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + "p2": {State: plugins.StateOK}, + "p3": {State: plugins.StateOK}, + }, + exp: 200, + expBody: `{}`, + }, + { + note: "nil plugin status", + statusUpdates: map[string]*plugins.Status{ + "p1": nil, + }, + exp: 200, + expBody: `{}`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + for name, status := range tc.statusUpdates { + f.server.manager.UpdatePluginStatus(name, status) + } + + req := newReqUnversioned(http.MethodGet, "/health?plugins", "") + validateDiagnosticRequest(t, f, req, tc.exp, tc.expBody) + }) + } +} + +func TestUnversionedGetHealthCheckDiscoveryWithPluginsAndExclude(t *testing.T) { + t.Parallel() + + // Use the same server through the cases, the status updates apply incrementally to it. + f := newFixture(t) + + cases := []struct { + note string + statusUpdates map[string]*plugins.Status + exp int + expBody string + }{ + { + note: "no plugins configured", + statusUpdates: nil, + exp: 200, + expBody: `{}`, + }, + { + note: "one plugin configured - not ready", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateNotReady}, + }, + exp: 500, + expBody: `{"error": "one or more plugins are not up"}`, + }, + { + note: "one plugin configured - ready", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + }, + exp: 200, + expBody: `{}`, + }, + { + note: "one plugin configured - error state", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateErr}, + }, + exp: 500, + expBody: `{"error": "one or more plugins are not up"}`, + }, + { + note: "one plugin configured - recovered from error", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + }, + exp: 200, + expBody: `{}`, + }, + { + note: "add excluded plugin - not ready", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + "p2": {State: plugins.StateNotReady}, + }, + exp: 200, + expBody: `{}`, + }, + { + note: "add another excluded plugin - not ready", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + "p2": {State: plugins.StateNotReady}, + "p3": {State: plugins.StateNotReady}, + }, + exp: 200, + expBody: `{}`, + }, + { + note: "excluded plugin - error", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + "p2": {State: plugins.StateErr}, + "p3": {State: plugins.StateErr}, + }, + exp: 200, + expBody: `{}`, + }, + { + note: "first plugin - error", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateErr}, + "p2": {State: plugins.StateErr}, + "p3": {State: plugins.StateErr}, + }, + exp: 500, + expBody: `{"error": "one or more plugins are not up"}`, + }, + { + note: "all plugins ready", + statusUpdates: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + "p2": {State: plugins.StateOK}, + "p3": {State: plugins.StateOK}, + }, + exp: 200, + expBody: `{}`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + for name, status := range tc.statusUpdates { + f.server.manager.UpdatePluginStatus(name, status) + } + + req := newReqUnversioned(http.MethodGet, "/health?plugins&exclude-plugin=p2&exclude-plugin=p3", "") + validateDiagnosticRequest(t, f, req, tc.exp, tc.expBody) + }) + } +} + +func TestUnversionedGetHealthCheckBundleAndPlugins(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + statuses map[string]*plugins.Status + exp int + expBody string + }{ + { + note: "no plugins configured", + statuses: nil, + exp: 200, + expBody: `{}`, + }, + { + note: "only bundle plugin configured - not ready", + statuses: map[string]*plugins.Status{ + "bundle": {State: plugins.StateNotReady}, + }, + exp: 500, + expBody: `{"error": "one or more bundles are not activated"}`, + }, + { + note: "only bundle plugin configured - ok", + statuses: map[string]*plugins.Status{ + "bundle": {State: plugins.StateOK}, + }, + exp: 200, + expBody: `{}`, + }, + { + note: "only custom plugin configured - not ready", + statuses: map[string]*plugins.Status{ + "p1": {State: plugins.StateNotReady}, + }, + exp: 500, + expBody: `{"error": "one or more plugins are not up"}`, + }, + { + note: "only custom plugin configured - ok", + statuses: map[string]*plugins.Status{ + "p1": {State: plugins.StateOK}, + }, + exp: 200, + expBody: `{}`, + }, + { + note: "both configured - bundle not ready", + statuses: map[string]*plugins.Status{ + "bundle": {State: plugins.StateNotReady}, + "p1": {State: plugins.StateOK}, + }, + exp: 500, + expBody: `{"error": "one or more bundles are not activated"}`, + }, + { + note: "both configured - custom plugin not ready", + statuses: map[string]*plugins.Status{ + "bundle": {State: plugins.StateOK}, + "p1": {State: plugins.StateNotReady}, + }, + exp: 500, + expBody: `{"error": "one or more plugins are not up"}`, + }, + { + note: "both configured - both ready", + statuses: map[string]*plugins.Status{ + "bundle": {State: plugins.StateOK}, + "p1": {State: plugins.StateOK}, + }, + exp: 200, + expBody: `{}`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + f := newFixture(t) + + for name, status := range tc.statuses { + f.server.manager.UpdatePluginStatus(name, status) + } + + req := newReqUnversioned(http.MethodGet, "/health?plugins&bundles", "") + validateDiagnosticRequest(t, f, req, tc.exp, tc.expBody) + }) + } +} + +func TestUnversionedGetHealthWithPolicyMissing(t *testing.T) { + t.Parallel() + + f := newFixture(t) + req := newReqUnversioned(http.MethodGet, "/health/live", "") + validateDiagnosticRequest(t, f, req, 500, `{"error":"health check (data.system.health.live) was undefined"}`) +} + +func TestUnversionedGetHealthWithPolicyUpdates(t *testing.T) { + t.Parallel() + + ctx := context.Background() + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + healthPolicy := `package system.health + + live := true + ` + + if err := store.UpsertPolicy(ctx, txn, "test", []byte(healthPolicy)); err != nil { + panic(err) + } + + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + + f := newFixtureWithStore(t, store) + req := newReqUnversioned(http.MethodGet, "/health/live", "") + validateDiagnosticRequest(t, f, req, 200, `{}`) + + // update health policy to set live to false + txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + healthPolicy = `package system.health + + live := false + ` + + if err := store.UpsertPolicy(ctx, txn, "test", []byte(healthPolicy)); err != nil { + panic(err) + } + + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + + req = newReqUnversioned(http.MethodGet, "/health/live", "") + validateDiagnosticRequest(t, f, req, 500, `{"error": "health check (data.system.health.live) returned unexpected value"}`) +} + +func TestUnversionedGetHealthWithPolicyUsingPlugins(t *testing.T) { + t.Parallel() + + ctx := context.Background() + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + healthPolicy := `package system.health + import rego.v1 + + default live = false + + live if { + input.plugin_state.bundle == "OK" + } + + default ready = false + + ready if { + input.plugins_ready + } + ` + + if err := store.UpsertPolicy(ctx, txn, "test", []byte(healthPolicy)); err != nil { + panic(err) + } + + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + + // plugins start out as not ready + f := newFixtureWithStore(t, store) + f.server.manager.UpdatePluginStatus("discovery", &plugins.Status{State: plugins.StateNotReady}) + f.server.manager.UpdatePluginStatus("bundle", &plugins.Status{State: plugins.StateNotReady}) + + // make sure live and ready are failing, as expected + liveReq := newReqUnversioned(http.MethodGet, "/health/live", "") + validateDiagnosticRequest(t, f, liveReq, 500, `{"error": "health check (data.system.health.live) returned unexpected value"}`) + + readyReq := newReqUnversioned(http.MethodGet, "/health/ready", "") + validateDiagnosticRequest(t, f, readyReq, 500, `{"error": "health check (data.system.health.ready) returned unexpected value"}`) + + // all plugins are reporting OK + f.server.manager.UpdatePluginStatus("discovery", &plugins.Status{State: plugins.StateOK}) + f.server.manager.UpdatePluginStatus("bundle", &plugins.Status{State: plugins.StateOK}) + + // make sure live and ready are now passing, as expected + liveReq = newReqUnversioned(http.MethodGet, "/health/live", "") + validateDiagnosticRequest(t, f, liveReq, 200, `{}`) + + readyReq = newReqUnversioned(http.MethodGet, "/health/ready", "") + validateDiagnosticRequest(t, f, readyReq, 200, `{}`) + + // bundle is now not ready again + f.server.manager.UpdatePluginStatus("bundle", &plugins.Status{State: plugins.StateNotReady}) + + // the live rule should fail, but the ready rule should still succeed, because plugins_ready stays true once set + liveReq = newReqUnversioned(http.MethodGet, "/health/live", "") + validateDiagnosticRequest(t, f, liveReq, 500, `{"error": "health check (data.system.health.live) returned unexpected value"}`) + + readyReq = newReqUnversioned(http.MethodGet, "/health/ready", "") + validateDiagnosticRequest(t, f, readyReq, 200, `{}`) +} + +func TestDataV0(t *testing.T) { + t.Parallel() + + testMod1 := `package test + import rego.v1 + + p = "hello" + + q = { + "foo": [1,2,3,4] + } if { + input.flag = true + } + ` + pretty := `{ + "p": "hello", + "q": { + "foo": [ + 1, + 2, + 3, + 4 + ] + } + }` + + f := newFixture(t) + + if err := f.v1(http.MethodPut, "/policies/test", testMod1, 200, ""); err != nil { + t.Fatalf("Unexpected error while creating policy: %v", err) + } + + if err := f.v0(http.MethodPost, "/data/test/p", "", 200, `"hello"`); err != nil { + t.Fatalf("Expected response hello but got: %v", err) + } + + if err := f.v0(http.MethodPost, "/data/test/q/foo", `{"flag": true}`, 200, `[1,2,3,4]`); err != nil { + t.Fatalf("Expected response [1,2,3,4] but got: %v", err) + } + + if err := f.v0(http.MethodPost, "/data/test?pretty=true", `{"flag": true}`, 200, pretty); err != nil { + t.Fatalf("Expected response %v but got: %v", pretty, err) + } + + req := newReqV0(http.MethodPost, "/data/test/q", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + if f.recorder.Code != 404 { + t.Fatalf("Expected HTTP 404 but got: %v", f.recorder) + } + + var resp types.ErrorV1 + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&resp); err != nil { + t.Fatalf("Unexpected error while deserializing response: %v", err) + } + + if resp.Code != types.CodeUndefinedDocument { + t.Fatalf("Expected undefiend code but got: %v", resp) + } +} + +// Tests that the responses for (theoretically) valid resources but with forbidden methods return the proper status code +func Test405StatusCodev1(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + reqs []tr + }{ + {"v1 data one level 405", []tr{ + {http.MethodHead, "/data/lvl1", "", 405, ""}, + {http.MethodConnect, "/data/lvl1", "", 405, ""}, + {http.MethodOptions, "/data/lvl1", "", 405, ""}, + {http.MethodTrace, "/data/lvl1", "", 405, ""}, + }}, + {"v1 data 405", []tr{ + {http.MethodHead, "/data", "", 405, ""}, + {http.MethodConnect, "/data", "", 405, ""}, + {http.MethodOptions, "/data", "", 405, ""}, + {http.MethodTrace, "/data", "", 405, ""}, + {http.MethodDelete, "/data", "", 405, ""}, + }}, + {"v1 policies 405", []tr{ + {http.MethodHead, "/policies", "", 405, ""}, + {http.MethodConnect, "/policies", "", 405, ""}, + {http.MethodDelete, "/policies", "", 405, ""}, + {http.MethodOptions, "/policies", "", 405, ""}, + {http.MethodTrace, "/policies", "", 405, ""}, + {http.MethodPost, "/policies", "", 405, ""}, + {http.MethodPut, "/policies", "", 405, ""}, + {http.MethodPatch, "/policies", "", 405, ""}, + }}, + {"v1 policies one level 405", []tr{ + {http.MethodHead, "/policies/lvl1", "", 405, ""}, + {http.MethodConnect, "/policies/lvl1", "", 405, ""}, + {http.MethodOptions, "/policies/lvl1", "", 405, ""}, + {http.MethodTrace, "/policies/lvl1", "", 405, ""}, + {http.MethodPost, "/policies/lvl1", "", 405, ""}, + }}, + {"v1 query one level 405", []tr{ + {http.MethodHead, "/query/lvl1", "", 405, ""}, + {http.MethodConnect, "/query/lvl1", "", 405, ""}, + {http.MethodDelete, "/query/lvl1", "", 405, ""}, + {http.MethodOptions, "/query/lvl1", "", 405, ""}, + {http.MethodTrace, "/query/lvl1", "", 405, ""}, + {http.MethodPost, "/query/lvl1", "", 405, ""}, + {http.MethodPut, "/query/lvl1", "", 405, ""}, + {http.MethodPatch, "/query/lvl1", "", 405, ""}, + }}, + {"v1 query 405", []tr{ + {http.MethodHead, "/query", "", 405, ""}, + {http.MethodConnect, "/query", "", 405, ""}, + {http.MethodDelete, "/query", "", 405, ""}, + {http.MethodOptions, "/query", "", 405, ""}, + {http.MethodTrace, "/query", "", 405, ""}, + {http.MethodPut, "/query", "", 405, ""}, + {http.MethodPatch, "/query", "", 405, ""}, + }}, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + executeRequests(t, tc.reqs) + }) + } +} + +// Tests that the responses for (theoretically) valid resources but with forbidden methods return the proper status code +func Test405StatusCodev0(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + reqs []tr + }{ + {"v0 data one levels 405", []tr{ + {http.MethodHead, "/data/lvl2", "", 405, ""}, + {http.MethodConnect, "/data/lvl2", "", 405, ""}, + {http.MethodDelete, "/data/lvl2", "", 405, ""}, + {http.MethodOptions, "/data/lvl2", "", 405, ""}, + {http.MethodTrace, "/data/lvl2", "", 405, ""}, + {http.MethodGet, "/data/lvl2", "", 405, ""}, + {http.MethodPatch, "/data/lvl2", "", 405, ""}, + {http.MethodPut, "/data/lvl2", "", 405, ""}, + }}, + {"v0 data 405", []tr{ + {http.MethodHead, "/data", "", 405, ""}, + {http.MethodConnect, "/data", "", 405, ""}, + {http.MethodDelete, "/data", "", 405, ""}, + {http.MethodOptions, "/data", "", 405, ""}, + {http.MethodTrace, "/data", "", 405, ""}, + {http.MethodGet, "/data", "", 405, ""}, + {http.MethodPatch, "/data", "", 405, ""}, + {http.MethodPut, "/data", "", 405, ""}, + }}, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + executeRequestsv0(t, tc.reqs) + }) + } +} + +func TestCompileV1(t *testing.T) { + t.Parallel() + + v0mod := `package test + + p { + input.x = 1 + } + + q { + data.a[i] = input.x + } + + default r = true + + r { input.x = 1 } + + custom_func(x) { data.a[i] == x } + + s { custom_func(input.x) } + ` + + v1mod := `package test + + p if { + input.x = 1 + } + + q if { + data.a[i] = input.x + } + + default r = true + + r if { input.x = 1 } + + custom_func(x) if { data.a[i] == x } + + s if { custom_func(input.x) } + ` + + v0v1mod := `package test + import rego.v1 + + p if { + input.x = 1 + } + + q if { + data.a[i] = input.x + } + + default r = true + + r if { input.x = 1 } + + custom_func(x) if { data.a[i] == x } + + s if { custom_func(input.x) } + ` + + expQuery := func(s string) string { + return fmt.Sprintf(`{"result": {"queries": [%v]}}`, string(util.MustMarshalJSON(ast.MustParseBody(s)))) + } + + expError := func(s string) string { + return fmt.Sprintf(`{ + "code": "invalid_parameter", + "errors": [ + %s + ], + "message": "error(s) occurred while compiling module(s)" + }`, s) + } + + expQueryAndSupport := func(q string, m string, rv ast.RegoVersion) string { + opts := ast.ParserOptions{RegoVersion: rv} + return fmt.Sprintf(`{"result": {"queries": [%v], "support": [%v]}}`, + string(util.MustMarshalJSON(ast.MustParseBodyWithOpts(q, opts))), + string(util.MustMarshalJSON(ast.MustParseModuleWithOpts(m, opts)))) + } + + tests := []struct { + note string + trs []tr + regoVersion ast.RegoVersion + }{ + { + note: "v1 keyword in query", + trs: []tr{ + {http.MethodPost, "/compile", `{ + "unknowns": ["input"], + "query": "42 in input.x" + }`, 200, expQuery("42 in input.x")}, + }, + }, + { + note: "v1 keyword in query (v0 rego-version)", + regoVersion: ast.RegoV0, + trs: []tr{ + {http.MethodPost, "/compile", `{ + "unknowns": ["input"], + "query": "42 in input.x" + }`, 400, expError(fmt.Sprintf(`{ + "code": "rego_unsafe_var_error", + "location": { + "col": 4, + "file": "", + "row": 1 + }, + "message": "%s" + }`, "var in is unsafe (hint: `import future.keywords.in` to import a future keyword)"))}, + }, + }, + { + note: "basic", + trs: []tr{ + {http.MethodPut, "/policies/test", v1mod, 200, ""}, + {http.MethodPost, "/compile", `{ + "unknowns": ["input"], + "query": "data.test.p = true" + }`, 200, expQuery("input.x = 1")}, + }, + }, + { + note: "subtree", + trs: []tr{ + {http.MethodPost, "/compile", `{ + "unknowns": ["input.x"], + "input": {"y": 1}, + "query": "input.x > input.y" + }`, 200, expQuery("input.x > 1")}, + }, + }, + { + note: "data", + trs: []tr{ + {http.MethodPut, "/policies/test", v1mod, 200, ""}, + {http.MethodPost, "/compile", `{ + "unknowns": ["data.a"], + "input": { + "x": 1 + }, + "query": "data.test.q = true" + }`, 200, expQuery("1 = data.a[i1]")}, + }, + }, + { + note: "escaped string", + trs: []tr{ + {http.MethodPost, "/compile", `{ + "query": "input[\"x\"] = 1" + }`, 200, expQuery("input.x = 1")}, + }, + }, + { + note: "support", + trs: []tr{ + {http.MethodPut, "/policies/test", v1mod, 200, ""}, + {http.MethodPost, "/compile", `{ + "query": "data.test.r = true" + }`, 200, expQueryAndSupport( + `data.partial.test.r = true`, + `package partial.test + + r if { input.x = 1 } + default r = true + `, + ast.DefaultRegoVersion)}, + }, + }, + { + note: "support (v0 rego-version)", + regoVersion: ast.RegoV0, + trs: []tr{ + {http.MethodPut, "/policies/test", v0mod, 200, ""}, + {http.MethodPost, "/compile", `{ + "query": "data.test.r = true" + }`, 200, expQueryAndSupport( + `data.partial.test.r = true`, + `package partial.test + + r { input.x = 1 } + default r = true + `, + ast.RegoV0)}, + }, + }, + { + note: "support (v1 rego-version)", + regoVersion: ast.RegoV1, + trs: []tr{ + {http.MethodPut, "/policies/test", v1mod, 200, ""}, + {http.MethodPost, "/compile", `{ + "query": "data.test.r = true" + }`, 200, expQueryAndSupport( + `data.partial.test.r = true`, + `package partial.test + + r if { input.x = 1 } + default r = true + `, + ast.RegoV1)}, + }, + }, + { + note: "support (import rego.v1)", + regoVersion: ast.RegoV0, + trs: []tr{ + {http.MethodPut, "/policies/test", v0v1mod, 200, ""}, + // NOTE: v0 support rules don't get the rego.v1 import applied + {http.MethodPost, "/compile", `{ + "query": "data.test.r = true" + }`, 200, expQueryAndSupport( + `data.partial.test.r = true`, + `package partial.test + + r { input.x = 1 } + default r = true + `, + ast.RegoV0)}, + }, + }, + { + note: "function without disableInlining", + trs: []tr{ + {http.MethodPut, "/policies/test", v1mod, 200, ""}, + {http.MethodPost, "/compile", `{ + "unknowns": ["data.a"], + "query": "data.test.s = true", + "input": { "x": 1 } + }`, 200, expQuery("data.a[i2] = 1")}, + }, + }, + { + note: "function with disableInlining", + trs: []tr{ + {http.MethodPut, "/policies/test", v1mod, 200, ""}, + {http.MethodPost, "/compile", `{ + "unknowns": ["data.a"], + "query": "data.test.s = true", + "options": { "disableInlining": ["data.test"] }, + "input": { "x": 1 } + }`, 200, expQueryAndSupport( + `data.partial.test.s = true`, + `package partial.test + + s if { data.partial.test.custom_func(1) } + custom_func(__local0__2) if { data.a[i2] = __local0__2 } + `, + ast.DefaultRegoVersion)}, + }, + }, + { + note: "empty unknowns", + trs: []tr{ + {http.MethodPost, "/compile", `{"query": "input.x > 1", "unknowns": []}`, 200, `{"result": {}}`}, + }, + }, + { + note: "never defined", + trs: []tr{ + {http.MethodPost, "/compile", `{"query": "1 = 2"}`, 200, `{"result": {}}`}, + }, + }, + { + note: "always defined", + trs: []tr{ + {http.MethodPost, "/compile", `{"query": "1 = 1"}`, 200, `{"result": {"queries": [[]]}}`}, + }, + }, + { + note: "error: bad request", + trs: []tr{{http.MethodPost, "/compile", `{"input": [{]}`, 400, ``}}, + }, + { + note: "error: empty query", + trs: []tr{{http.MethodPost, "/compile", `{}`, 400, ""}}, + }, + { + note: "error: bad query", + trs: []tr{{http.MethodPost, "/compile", `{"query": "x %!> 9"}`, 400, ""}}, + }, + { + note: "error: bad unknown", + trs: []tr{{http.MethodPost, "/compile", `{"unknowns": ["input."], "query": "true"}`, 400, ""}}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + if tc.regoVersion != ast.RegoUndefined { + executeRequests(t, tc.trs, variant{ + name: tc.regoVersion.String(), + opts: []any{plugins.WithParserOptions(ast.ParserOptions{RegoVersion: tc.regoVersion})}, + }) + } else { + executeRequests(t, tc.trs) + } + }) + } +} + +func TestCompileV1Observability(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + test.WithTempFS(nil, func(root string) { + disk, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{Dir: root}) + if err != nil { + t.Fatal(err) + } + defer disk.Close(ctx) + f := newFixtureWithStore(t, disk) + + err = f.v1(http.MethodPut, "/policies/test", `package test + import rego.v1 + + p if { input.x = 1 }`, 200, "") + if err != nil { + t.Fatal(err) + } + + compileReq := newReqV1(http.MethodPost, "/compile?metrics&explain=full", `{ + "query": "data.test.p = true" + }`) + + f.reset() + f.server.Handler.ServeHTTP(f.recorder, compileReq) + + var response types.CompileResponseV1 + if err := json.NewDecoder(f.recorder.Body).Decode(&response); err != nil { + t.Fatal(err) + } + + if len(response.Explanation) == 0 { + t.Fatal("Expected non-empty explanation") + } + + assertMetricsExist(t, response.Metrics, []string{ + "timer_rego_partial_eval_ns", + "timer_rego_query_compile_ns", + "timer_rego_query_parse_ns", + "timer_server_handler_ns", + "counter_disk_read_keys", + "timer_disk_read_ns", + }) + }) +} + +func TestCompileV1UnsafeBuiltin(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + query := `{"query": "http.send({\"method\": \"get\", \"url\": \"foo.com\"}, x)"}` + expResp := `{ + "code": "invalid_parameter", + "message": "error(s) occurred while compiling module(s)", + "errors": [ + { + "code": "rego_type_error", + "message": "unsafe built-in function calls in expression: http.send", + "location": { + "file": "", + "row": 1, + "col": 1 + } + } + ] +}` + + if err := f.v1(http.MethodPost, `/compile`, query, 400, expResp); err != nil { + t.Fatalf("Expected bad request but got %v", f.recorder) + } +} + +func TestDataV1Redirection(t *testing.T) { + t.Parallel() + + f := newFixture(t) + // Testing redirect at the root level + if err := f.v1(http.MethodPut, "/data/", `{"foo": [1,2,3]}`, 301, ""); err != nil { + t.Fatalf("Unexpected error from PUT: %v", err) + } + locHdr := f.recorder.Header().Get("Location") + if strings.Compare(locHdr, "/v1/data") != 0 { + t.Fatalf("Unexpected error Location header value: %v", locHdr) + } + RedirectedPath := strings.SplitAfter(locHdr, "/v1")[1] + if err := f.v1(http.MethodPut, RedirectedPath, `{"foo": [1,2,3]}`, 204, ""); err != nil { + t.Fatalf("Unexpected error from PUT: %v", err) + } + if err := f.v1(http.MethodGet, RedirectedPath, "", 200, `{"result": {"foo": [1,2,3]}}`); err != nil { + t.Fatalf("Unexpected error from GET: %v", err) + } + // Now we test redirection a few levels down + if err := f.v1(http.MethodPut, "/data/a/b/c/", `{"foo": [1,2,3]}`, 301, ""); err != nil { + t.Fatalf("Unexpected error from PUT: %v", err) + } + locHdrLv := f.recorder.Header().Get("Location") + if strings.Compare(locHdrLv, "/v1/data/a/b/c") != 0 { + t.Fatalf("Unexpected error Location header value: %v", locHdrLv) + } + RedirectedPathLvl := strings.SplitAfter(locHdrLv, "/v1")[1] + if err := f.v1(http.MethodPut, RedirectedPathLvl, `{"foo": [1,2,3]}`, 204, ""); err != nil { + t.Fatalf("Unexpected error from PUT: %v", err) + } + if err := f.v1(http.MethodGet, RedirectedPathLvl, "", 200, `{"result": {"foo": [1,2,3]}}`); err != nil { + t.Fatalf("Unexpected error from GET: %v", err) + } +} + +func TestDataV1(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + testMod1 := `package testmod + +import rego.v1 +import input.req1 +import input.req2 as reqx +import input.req3.attr1 + +p contains x if { q[x]; not r[x] } +q contains x if { data.x.y[i] = x } +r contains x if { data.x.z[i] = x } +g = true if { req1.a[0] = 1; reqx.b[i] = 1 } +h = true if { attr1[i] > 1 } +gt1 = true if { req1 > 1 } +arr = [1, 2, 3, 4] if { true } +undef = true if { false }` + + testMod2 := `package testmod +import rego.v1 + +p = [1, 2, 3, 4] if { true } +q = {"a": 1, "b": 2} if { true }` + + testMod4 := `package testmod +import rego.v1 + +p = true if { true } +p = false if { true }` + + testMod5 := `package testmod.empty.mod` + testMod6 := `package testmod.all.undefined +import rego.v1 + +p = true if { false }` + + tests := []struct { + note string + reqs []tr + }{ + {"add root", []tr{ + {http.MethodPatch, "/data/x", `[{"op": "add", "path": "/", "value": {"a": 1}}]`, 204, ""}, + {http.MethodGet, "/data/x/a", "", 200, `{"result": 1}`}, + }}, + {"append array", []tr{ + {http.MethodPatch, "/data/x", `[{"op": "add", "path": "/", "value": []}]`, 204, ""}, + {http.MethodPatch, "/data/x", `[{"op": "add", "path": "-", "value": {"a": 1}}]`, 204, ""}, + {http.MethodPatch, "/data/x", `[{"op": "add", "path": "-", "value": {"a": 2}}]`, 204, ""}, + {http.MethodGet, "/data/x/0/a", "", 200, `{"result": 1}`}, + {http.MethodGet, "/data/x/1/a", "", 200, `{"result": 2}`}, + }}, + {"append array one-shot", []tr{ + {http.MethodPatch, "/data/x", `[ + {"op": "add", "path": "/", "value": []}, + {"op": "add", "path": "-", "value": {"a": 1}}, + {"op": "add", "path": "-", "value": {"a": 2}} + ]`, 204, ""}, + {http.MethodGet, "/data/x/1/a", "", 200, `{"result": 2}`}, + }}, + {"insert array", []tr{ + {http.MethodPatch, "/data/x", `[{"op": "add", "path": "/", "value": { + "y": [ + {"z": [1,2,3]}, + {"z": [4,5,6]} + ] + }}]`, 204, ""}, + {http.MethodGet, "/data/x/y/1/z/2", "", 200, `{"result": 6}`}, + {http.MethodPatch, "/data/x/y/1", `[{"op": "add", "path": "/z/1", "value": 100}]`, 204, ""}, + {http.MethodGet, "/data/x/y/1/z", "", 200, `{"result": [4, 100, 5, 6]}`}, + }}, + {"patch root", []tr{ + {http.MethodPatch, "/data", `[ + { + "op": "add", + "path": "/", + "value": {"a": 1, "b": 2} + } + ]`, 204, ""}, + {http.MethodGet, "/data", "", 200, `{"result": {"a": 1, "b": 2}}`}, + }}, + {"patch root invalid", []tr{ + {http.MethodPatch, "/data", `[ + { + "op": "add", + "path": "/", + "value": [1,2,3] + } + ]`, 400, ""}, + }}, + {"patch invalid", []tr{ + {http.MethodPatch, "/data", `[ + { + "op": "remove", + "path": "/" + } + ]`, 400, ""}, + }}, + {"patch abort", []tr{ + {http.MethodPatch, "/data", `[ + {"op": "add", "path": "/foo", "value": "hello"}, + {"op": "add", "path": "/bar", "value": "world"}, + {"op": "add", "path": "/foo/bad", "value": "deadbeef"} + ]`, 404, ""}, + {http.MethodGet, "/data", "", 200, `{"result": {}}`}, + }}, + {"put root", []tr{ + {http.MethodPut, "/data", `{"foo": [1,2,3]}`, 204, ""}, + {http.MethodGet, "/data", "", 200, `{"result": {"foo": [1,2,3]}}`}, + }}, + {"put deep makedir", []tr{ + {http.MethodPut, "/data/a/b/c/d", `1`, 204, ""}, + {http.MethodGet, "/data/a/b/c", "", 200, `{"result": {"d": 1}}`}, + }}, + {"put deep makedir partial", []tr{ + {http.MethodPut, "/data/a/b", `{}`, 204, ""}, + {http.MethodPut, "/data/a/b/c/d", `0`, 204, ""}, + {http.MethodGet, "/data/a/b/c", "", 200, `{"result": {"d": 0}}`}, + }}, + {"put exists overwrite", []tr{ + {http.MethodPut, "/data/a/b/c", `"hello"`, 204, ""}, + {http.MethodPut, "/data/a/b", `"goodbye"`, 204, ""}, + {http.MethodGet, "/data/a", "", 200, `{"result": {"b": "goodbye"}}`}, + }}, + {"put base write conflict", []tr{ + {http.MethodPut, "/data/a/b", `[1,2,3,4]`, 204, ""}, + {http.MethodPut, "/data/a/b/c/d", "0", 404, `{ + "code": "resource_conflict", + "message": "storage_write_conflict_error: /a/b" + }`}, + }}, + {"put base/virtual conflict", []tr{ + {http.MethodPut, "/policies/testmod", "package x.y\np = 1\nq = 2", 200, ""}, + {http.MethodPut, "/data/x", `{"y": {"p": "xxx"}}`, 400, `{ + "code": "invalid_parameter", + "message": "1 error occurred: testmod:2: rego_compile_error: conflicting rule for data path x/y/p found" + }`}, + {http.MethodPut, "/data/x/y", `{"p": "xxx"}`, 400, ``}, + {http.MethodPut, "/data/x/y/p", `"xxx"`, 400, ``}, + {http.MethodPut, "/data/x/y/p/a", `1`, 400, ``}, + {http.MethodDelete, "/policies/testmod", "", 200, ""}, + {http.MethodPut, "/data/x/y/p/a", `1`, 204, ``}, + {http.MethodPut, "/policies/testmod", "package x.y\np = 1\nq = 2", 400, `{ + "code": "invalid_parameter", + "message": "error(s) occurred while compiling module(s)", + "errors": [ + { + "code": "rego_compile_error", + "message": "conflicting rule for data path x/y/p found", + "location": { + "file": "testmod", + "row": 2, + "col": 1 + } + } + ] + }`}, + }}, + {"get virtual", []tr{ + {http.MethodPut, "/policies/test", testMod1, 200, ""}, + {http.MethodPatch, "/data/x", `[{"op": "add", "path": "/", "value": {"y": [1,2,3,4], "z": [3,4,5,6]}}]`, 204, ""}, + {http.MethodGet, "/data/testmod/p", "", 200, `{"result": [1,2]}`}, + }}, + {"get with input", []tr{ + {http.MethodPut, "/policies/test", testMod1, 200, ""}, + {http.MethodGet, "/data/testmod/g?input=%7B%22req1%22%3A%7B%22a%22%3A%5B1%5D%7D%2C+%22req2%22%3A%7B%22b%22%3A%5B0%2C1%5D%7D%7D", "", 200, `{"result": true}`}, + }}, + {"get with input (missing input value)", []tr{ + {http.MethodPut, "/policies/test", testMod1, 200, ""}, + {http.MethodGet, "/data/testmod/g?input=%7B%22req1%22%3A%7B%22a%22%3A%5B1%5D%7D%7D", "", 200, "{}"}, // req2 not specified + }}, + {"get with input (namespaced)", []tr{ + {http.MethodPut, "/policies/test", testMod1, 200, ""}, + {http.MethodGet, "/data/testmod/h?input=%7B%22req3%22%3A%7B%22attr1%22%3A%5B4%2C3%2C2%2C1%5D%7D%7D", "", 200, `{"result": true}`}, + }}, + {"get with input (root)", []tr{ + {http.MethodPut, "/policies/test", testMod1, 200, ""}, + {http.MethodGet, `/data/testmod/gt1?input={"req1":2}`, "", 200, `{"result": true}`}, + }}, + {"get with input (bad format)", []tr{ + {http.MethodGet, "/data/deadbeef?input", "", 400, `{ + "code": "invalid_parameter", + "message": "parameter contains malformed input document: EOF" + }`}, + {http.MethodGet, "/data/deadbeef?input=", "", 400, `{ + "code": "invalid_parameter", + "message": "parameter contains malformed input document: EOF" + }`}, + {http.MethodGet, `/data/deadbeef?input="foo`, "", 400, `{ + "code": "invalid_parameter", + "message": "parameter contains malformed input document: unexpected EOF" + }`}, + }}, + {"get with input (path error)", []tr{ + {http.MethodGet, `/data/deadbeef?input={"foo:1}`, "", 400, `{ + "code": "invalid_parameter", + "message": "parameter contains malformed input document: unexpected EOF" + }`}, + }}, + {"get empty and undefined", []tr{ + {http.MethodPut, "/policies/test", testMod1, 200, ""}, + {http.MethodPut, "/policies/test2", testMod5, 200, ""}, + {http.MethodPut, "/policies/test3", testMod6, 200, ""}, + {http.MethodGet, "/data/testmod/undef", "", 200, "{}"}, + {http.MethodGet, "/data/doesnot/exist", "", 200, "{}"}, + {http.MethodGet, "/data/testmod/empty/mod", "", 200, `{ + "result": {} + }`}, + {http.MethodGet, "/data/testmod/all/undefined", "", 200, `{ + "result": {} + }`}, + }}, + {"get root", []tr{ + {http.MethodPut, "/policies/test", testMod2, 200, ""}, + {http.MethodPatch, "/data/x", `[{"op": "add", "path": "/", "value": [1,2,3,4]}]`, 204, ""}, + {http.MethodGet, "/data", "", 200, `{"result": {"testmod": {"p": [1,2,3,4], "q": {"a":1, "b": 2}}, "x": [1,2,3,4]}}`}, + }}, + {"post root", []tr{ + {http.MethodPost, "/data", "", 200, `{ + "result": {}, + "warning": { + "code": "api_usage_warning", + "message": "'input' key missing from the request" + } + }`}, + {http.MethodPut, "/policies/test", testMod2, 200, ""}, + {http.MethodPost, "/data", "", 200, `{ + "result": { + "testmod": { + "p": [1,2,3,4], + "q": {"b": 2, "a": 1} + } + }, + "warning": { + "code": "api_usage_warning", + "message": "'input' key missing from the request" + } + }`}, + }}, + {"post input", []tr{ + {http.MethodPut, "/policies/test", testMod1, 200, ""}, + {http.MethodPost, "/data/testmod/gt1", `{"input": {"req1": 2}}`, 200, `{"result": true}`}, + }}, + {"post malformed input", []tr{ + {http.MethodPost, "/data/deadbeef", `{"input": @}`, 400, `{ + "code": "invalid_parameter", + "message": "body contains malformed input document: invalid character '@' looking for beginning of value" + }`}, + }}, + {"post empty object", []tr{ + {http.MethodPost, "/data", `{}`, 200, `{ + "result": {}, + "warning": { + "code": "api_usage_warning", + "message": "'input' key missing from the request" + } + }`}, + }}, + {"evaluation conflict", []tr{ + {http.MethodPut, "/policies/test", testMod4, 200, ""}, + {http.MethodPost, "/data/testmod/p", "", 500, `{ + "code": "internal_error", + "errors": [ + { + "code": "eval_conflict_error", + "location": { + "col": 1, + "file": "test", + "row": 5 + }, + "message": "complete rules must not produce multiple outputs" + } + ], + "message": "error(s) occurred while evaluating query" + }`}, + }}, + {"query wildcards omitted", []tr{ + {http.MethodPatch, "/data/x", `[{"op": "add", "path": "/", "value": [1,2,3,4]}]`, 204, ""}, + {http.MethodGet, "/query?q=data.x[_]%20=%20x", "", 200, `{"result": [{"x": 1}, {"x": 2}, {"x": 3}, {"x": 4}]}`}, + }}, + {"query undefined", []tr{ + {http.MethodGet, "/query?q=a=1%3Bb=2%3Ba=b", "", 200, `{}`}, + }}, + {"query compiler error", []tr{ + {http.MethodGet, "/query?q=x", "", 400, ""}, + // Subsequent query should not fail. + {http.MethodGet, "/query?q=x=1", "", 200, `{"result": [{"x": 1}]}`}, + }}, + {"delete and check", []tr{ + {http.MethodDelete, "/data/a/b", "", 404, ""}, + {http.MethodPut, "/data/a/b/c/d", `1`, 204, ""}, + {http.MethodGet, "/data/a/b/c", "", 200, `{"result": {"d": 1}}`}, + {http.MethodDelete, "/data/a/b", "", 204, ""}, + {http.MethodGet, "/data/a/b/c/d", "", 200, `{}`}, + {http.MethodGet, "/data/a", "", 200, `{"result": {}}`}, + {http.MethodGet, "/data/a/b/c", "", 200, `{}`}, + }}, + {"escaped paths", []tr{ + {http.MethodPut, "/data/a%2Fb", `{"c/d": 1}`, 204, ""}, + {http.MethodGet, "/data", "", 200, `{"result": {"a/b": {"c/d": 1}}}`}, + {http.MethodGet, "/data/a%2Fb/c%2Fd", "", 200, `{"result": 1}`}, + {http.MethodGet, "/data/a/b", "", 200, `{}`}, + {http.MethodPost, "/data/a%2Fb/c%2Fd", "", 200, `{ + "result": 1, + "warning": { + "code": "api_usage_warning", + "message": "'input' key missing from the request" + } + }`}, + {http.MethodPost, "/data/a/b", "", 200, `{ + "warning": { + "code": "api_usage_warning", + "message": "'input' key missing from the request" + } + }`}, + {http.MethodPatch, "/data/a%2Fb", `[{"op": "add", "path": "/e%2Ff", "value": 2}]`, 204, ""}, + {http.MethodPost, "/data", "", 200, `{ + "result": { + "a/b": { + "c/d": 1, + "e/f": 2 + } + }, + "warning": { + "code": "api_usage_warning", + "message": "'input' key missing from the request" + } + }`}, + }}, + {"strict-builtin-errors", []tr{ + {http.MethodPut, "/policies/test", ` + package test + import rego.v1 + + default p = false + + p if { 1/0 } + `, 200, ""}, + {http.MethodGet, "/data/test/p", "", 200, `{"result": false}`}, + {http.MethodGet, "/data/test/p?strict-builtin-errors", "", 500, `{ + "code": "internal_error", + "message": "error(s) occurred while evaluating query", + "errors": [ + { + "code": "eval_builtin_error", + "message": "div: divide by zero", + "location": { + "file": "test", + "row": 7, + "col": 12 + } + } + ] + }`}, + {http.MethodPost, "/data/test/p", "", 200, `{ + "result": false, + "warning": { + "code": "api_usage_warning", + "message": "'input' key missing from the request" + } + }`}, + {http.MethodPost, "/data/test/p?strict-builtin-errors", "", 500, `{ + "code": "internal_error", + "message": "error(s) occurred while evaluating query", + "errors": [ + { + "code": "eval_builtin_error", + "message": "div: divide by zero", + "location": { + "file": "test", + "row": 7, + "col": 12 + } + } + ] + }`}, + }}, + {"post api usage warning", []tr{ + {http.MethodPost, "/data", "", 200, `{ + "result": {}, + "warning": { + "code": "api_usage_warning", + "message": "'input' key missing from the request" + } + }`}, + {http.MethodPost, "/data", `{"input": {}}`, 200, `{"result": {}}`}, + }}, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + test.WithTempFS(nil, func(root string) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + disk, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{Dir: root}) + if err != nil { + t.Fatal(err) + } + defer disk.Close(ctx) + executeRequests(t, tc.reqs, + variant{"inmem", nil}, + variant{"disk", []any{ + func(s *Server) { + s.WithStore(disk) + }, + }}, + ) + }) + }) + } +} + +func TestDataV1Metrics(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + test.WithTempFS(nil, func(root string) { + disk, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{Dir: root}) + if err != nil { + t.Fatal(err) + } + defer disk.Close(ctx) + + f := newFixtureWithStore(t, disk) + put := newReqV1(http.MethodPut, `/data?metrics`, `{"foo":"bar"}`) + f.server.Handler.ServeHTTP(f.recorder, put) + + if f.recorder.Code != 200 { + t.Fatalf("Expected success but got %v", f.recorder) + } + + var result types.DataResponseV1 + err = util.UnmarshalJSON(f.recorder.Body.Bytes(), &result) + if err != nil { + t.Fatalf("Unexpected error while unmarshalling result: %v", err) + } + + assertMetricsExist(t, result.Metrics, []string{ + "counter_disk_read_keys", + "counter_disk_deleted_keys", + "counter_disk_written_keys", + "counter_disk_read_bytes", + "timer_rego_input_parse_ns", + "timer_server_handler_ns", + "timer_disk_read_ns", + "timer_disk_write_ns", + "timer_disk_commit_ns", + }) + }) +} + +func TestConfigV1(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + c := []byte(`{"services": { + "acmecorp": { + "url": "https://example.com/control-plane-api/v1", + "credentials": {"bearer": {"token": "test"}} + } + }, + "labels": { + "region": "west" + }, + "keys": { + "global_key": { + "algorithm": HS256, + "key": "secret" + } + }}`) + + conf, err := config.ParseConfig(c, "foo") + if err != nil { + t.Fatal(err) + } + + f.server.manager.Config = conf + + expected := map[string]any{ + "result": map[string]any{ + "labels": map[string]any{"id": "foo", "version": version.Version, "region": "west"}, + "keys": map[string]any{"global_key": map[string]any{"algorithm": "HS256"}}, + "services": map[string]any{"acmecorp": map[string]any{"url": "https://example.com/control-plane-api/v1"}}, + "default_authorization_decision": "/system/authz/allow", + "default_decision": "/system/main", + }, + } + bs, err := json.Marshal(expected) + if err != nil { + t.Fatal(err) + } + + if err := f.v1(http.MethodGet, "/config", "", 200, string(bs)); err != nil { + t.Fatal(err) + } + + badServicesConfig := []byte(`{ + "services": { + "acmecorp": ["foo"] + } + }`) + + conf, err = config.ParseConfig(badServicesConfig, "foo") + if err != nil { + t.Fatal(err) + } + + f.server.manager.Config = conf + + if err := f.v1(http.MethodGet, "/config", "", 500, `{ + "code": "internal_error", + "message": "type assertion error"}`); err != nil { + t.Fatal(err) + } +} + +func TestDataYAML(t *testing.T) { + t.Parallel() + + testMod1 := `package testmod +import rego.v1 +import input.req1 +gt1 = true if { req1 > 1 }` + + inputYaml1 := ` +--- +input: + req1: 2` + + inputYaml2 := ` +--- +req1: 2` + + f := newFixture(t) + + if err := f.v1(http.MethodPut, "/policies/test", testMod1, 200, ""); err != nil { + t.Fatalf("Unexpected error from PUT /policies/test: %v", err) + } + + // First JSON and then later yaml to make sure both work + if err := f.v1(http.MethodPost, "/data/testmod/gt1", `{"input": {"req1": 2}}`, 200, `{"result": true}`); err != nil { + t.Fatalf("Unexpected error from PUT /policies/test: %v", err) + } + + req := newReqV1(http.MethodPost, "/data/testmod/gt1", inputYaml1) + req.Header.Set("Content-Type", "application/x-yaml") + if err := f.executeRequest(req, 200, `{"result": true}`); err != nil { + t.Fatalf("Unexpected error from POST with yaml: %v", err) + } + + req = newReqV0(http.MethodPost, "/data/testmod/gt1", inputYaml2) + req.Header.Set("Content-Type", "application/x-yaml") + if err := f.executeRequest(req, 200, `true`); err != nil { + t.Fatalf("Unexpected error from POST with yaml: %v", err) + } + + if err := f.v1(http.MethodPut, "/policies/test2", `package system +main = data.testmod.gt1`, 200, ""); err != nil { + t.Fatalf("Unexpected error from PUT /policies/test: %v", err) + } + + req = newReqUnversioned(http.MethodPost, "/", inputYaml2) + req.Header.Set("Content-Type", "application/x-yaml") + if err := f.executeRequest(req, 200, `true`); err != nil { + t.Fatalf("Unexpected error from POST with yaml: %v", err) + } + +} + +func TestDataPutV1IfNoneMatch(t *testing.T) { + t.Parallel() + + f := newFixture(t) + if err := f.v1(http.MethodPut, "/data/a/b/c", "0", 204, ""); err != nil { + t.Fatalf("Unexpected error from PUT /data/a/b/c: %v", err) + } + req := newReqV1(http.MethodPut, "/data/a/b/c", "1") + req.Header.Set("If-None-Match", "*") + if err := f.executeRequest(req, 304, ""); err != nil { + t.Fatalf("Unexpected error from PUT with If-None-Match=*: %v", err) + } +} + +// Ensure JSON payload is compressed with gzip. +func mustGZIPPayload(payload []byte) []byte { + var compressedPayload bytes.Buffer + gz := gzip.NewWriter(&compressedPayload) + if _, err := gz.Write(payload); err != nil { + panic(fmt.Errorf("Error writing to gzip writer: %w", err)) + } + if err := gz.Close(); err != nil { + panic(fmt.Errorf("Error closing gzip writer: %w", err)) + } + return compressedPayload.Bytes() +} + +// generateJSONBenchmarkData returns a map of `k` keys and `v` key/value pairs. +// Taken from topdown/topdown_bench_test.go +func generateJSONBenchmarkData(k, v int) map[string]any { + // create array of null values that can be iterated over + keys := make([]any, k) + for i := range keys { + keys[i] = nil + } + + // create large JSON object value (100,000 entries is about 2MB on disk) + values := map[string]any{} + for i := range v { + values[fmt.Sprintf("key%d", i)] = fmt.Sprintf("value%d", i) + } + + return map[string]any{ + "input": map[string]any{ + "keys": keys, + "values": values, + }, + } +} + +// Ref: https://github.com/open-policy-agent/opa/issues/6804 +func TestDataGetV1CompressedRequestWithAuthorizer(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + payload []byte + forcePayloadSizeField uint32 // Size to manually set the payload field for the gzip blob. + expRespHTTPStatus int + expErrorMsg string + }{ + { + note: "empty message", + payload: mustGZIPPayload([]byte{}), + expRespHTTPStatus: 401, + }, + { + note: "empty object", + payload: mustGZIPPayload([]byte(`{}`)), + expRespHTTPStatus: 401, + }, + { + note: "basic authz - fail", + payload: mustGZIPPayload([]byte(`{"user": "bob"}`)), + expRespHTTPStatus: 401, + }, + { + note: "basic authz - pass", + payload: mustGZIPPayload([]byte(`{"user": "alice"}`)), + expRespHTTPStatus: 200, + }, + { + note: "basic authz - malicious size field", + payload: mustGZIPPayload([]byte(`{"user": "alice"}`)), + expRespHTTPStatus: 400, + forcePayloadSizeField: 134217728, // 128 MB + expErrorMsg: "gzip: invalid checksum", + }, + { + note: "basic authz - huge zip", + payload: mustGZIPPayload(util.MustMarshalJSON(generateJSONBenchmarkData(100, 100))), + expRespHTTPStatus: 401, + }, + } + + for _, test := range tests { + t.Run(test.note, func(t *testing.T) { + ctx := context.Background() + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + authzPolicy := `package system.authz + +import rego.v1 + +default allow := false # Reject requests by default. + +allow if { + # Logic to authorize request goes here. + input.body.user == "alice" +} +` + + if err := store.UpsertPolicy(ctx, txn, "test", []byte(authzPolicy)); err != nil { + panic(err) + } + + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + + opts := [](func(*Server)){ + func(s *Server) { + s.WithStore(store) + }, + func(s *Server) { + s.WithAuthorization(AuthorizationBasic) + }, + } + + f := newFixtureWithConfig(t, fmt.Sprintf(`{"server":{"decision_logs": %t}}`, true), opts...) + + // Forcibly replace the size trailer field for the gzip blob. + // Byte order is little-endian, field is a uint32. + if test.forcePayloadSizeField != 0 { + binary.LittleEndian.PutUint32(test.payload[len(test.payload)-4:], test.forcePayloadSizeField) + } + + // execute the request + req := newReqV1(http.MethodPost, "/data/test", string(test.payload)) + req.Header.Set("Content-Encoding", "gzip") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + if f.recorder.Code != test.expRespHTTPStatus { + t.Fatalf("Unexpected HTTP status code, (exp,got): %d, %d", test.expRespHTTPStatus, f.recorder.Code) + } + if test.expErrorMsg != "" { + var serverErr types.ErrorV1 + if err := json.Unmarshal(f.recorder.Body.Bytes(), &serverErr); err != nil { + t.Fatalf("Could not deserialize error message: %s", err.Error()) + } + if serverErr.Message != test.expErrorMsg { + t.Fatalf("Expected error message to have message '%s', got message: '%s'", test.expErrorMsg, serverErr.Message) + } + } + }) + } +} + +// Tests to ensure the body size limits work, for compressed requests. +func TestDataPostV1CompressedDecodingLimits(t *testing.T) { + t.Parallel() + + defaultMaxLen := int64(1024) + defaultGzipMaxLen := int64(1024) + + tests := []struct { + note string + wantGzip bool + wantChunkedEncoding bool + payload []byte + forceContentLen int64 // Size to manually set the Content-Length header to. + forcePayloadSizeField uint32 // Size to manually set the payload field for the gzip blob. + expRespHTTPStatus int + expWarningMsg string + expErrorMsg string + maxLen int64 + gzipMaxLen int64 + }{ + { + note: "empty message", + payload: []byte{}, + expRespHTTPStatus: 200, + expWarningMsg: "'input' key missing from the request", + }, + { + note: "empty message, gzip", + wantGzip: true, + payload: mustGZIPPayload([]byte{}), + expRespHTTPStatus: 200, + expWarningMsg: "'input' key missing from the request", + }, + { + note: "empty message, malicious Content-Length", + payload: []byte{}, + forceContentLen: 2048, // Server should ignore this header entirely. + expRespHTTPStatus: 400, + expErrorMsg: "request body too large", + }, + { + note: "empty message, gzip, malicious Content-Length", + wantGzip: true, + payload: mustGZIPPayload([]byte{}), + forceContentLen: 2048, // Server should ignore this header entirely. + expRespHTTPStatus: 400, + expErrorMsg: "request body too large", + }, + { + note: "basic - malicious size field, expect reject on gzip payload length", + wantGzip: true, + payload: mustGZIPPayload([]byte(`{"input": {"user": "alice"}}`)), + expRespHTTPStatus: 400, + forcePayloadSizeField: 134217728, // 128 MB + expErrorMsg: "gzip payload too large", + gzipMaxLen: 1024, + }, + { + note: "basic - malicious size field, expect reject on gzip payload length, chunked encoding", + wantGzip: true, + wantChunkedEncoding: true, + payload: mustGZIPPayload([]byte(`{"input": {"user": "alice"}}`)), + expRespHTTPStatus: 400, + forcePayloadSizeField: 134217728, // 128 MB + expErrorMsg: "gzip payload too large", + gzipMaxLen: 1024, + }, + { + note: "basic, large payload", + payload: util.MustMarshalJSON(generateJSONBenchmarkData(100, 100)), + expRespHTTPStatus: 200, + maxLen: 134217728, + }, + { + note: "basic, large payload, expect reject on Content-Length", + payload: util.MustMarshalJSON(generateJSONBenchmarkData(100, 100)), + expRespHTTPStatus: 400, + maxLen: 512, + expErrorMsg: "request body too large", + }, + { + note: "basic, large payload, expect reject on Content-Length, chunked encoding", + wantChunkedEncoding: true, + payload: util.MustMarshalJSON(generateJSONBenchmarkData(100, 100)), + expRespHTTPStatus: 200, + maxLen: 134217728, + }, + { + note: "basic, gzip, large payload", + wantGzip: true, + payload: mustGZIPPayload(util.MustMarshalJSON(generateJSONBenchmarkData(100, 100))), + expRespHTTPStatus: 200, + maxLen: 1024, + gzipMaxLen: 134217728, + }, + { + note: "basic, gzip, large payload, expect reject on gzip payload length", + wantGzip: true, + payload: mustGZIPPayload(util.MustMarshalJSON(generateJSONBenchmarkData(100, 100))), + expRespHTTPStatus: 400, + maxLen: 1024, + gzipMaxLen: 10, + expErrorMsg: "gzip payload too large", + }, + } + + for _, test := range tests { + t.Run(test.note, func(t *testing.T) { + ctx := context.Background() + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + examplePolicy := `package example.authz + +import rego.v1 + +default allow := false # Reject requests by default. + +allow if { + # Logic to authorize request goes here. + input.body.user == "alice" +} +` + + if err := store.UpsertPolicy(ctx, txn, "test", []byte(examplePolicy)); err != nil { + panic(err) + } + + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + + opts := [](func(*Server)){ + func(s *Server) { + s.WithStore(store) + }, + } + + // Set defaults for max_length configs, if not specified in the test case. + if test.maxLen == 0 { + test.maxLen = defaultMaxLen + } + if test.gzipMaxLen == 0 { + test.gzipMaxLen = defaultGzipMaxLen + } + + f := newFixtureWithConfig(t, fmt.Sprintf(`{"server":{"decision_logs": %t, "decoding":{"max_length": %d, "gzip": {"max_length": %d}}}}`, true, test.maxLen, test.gzipMaxLen), opts...) + + // Forcibly replace the size trailer field for the gzip blob. + // Byte order is little-endian, field is a uint32. + if test.forcePayloadSizeField != 0 { + binary.LittleEndian.PutUint32(test.payload[len(test.payload)-4:], test.forcePayloadSizeField) + } + + // execute the request + req := newReqV1(http.MethodPost, "/data/test", string(test.payload)) + if test.wantGzip { + req.Header.Set("Content-Encoding", "gzip") + } + if test.wantChunkedEncoding { + req.ContentLength = -1 + req.TransferEncoding = []string{"chunked"} + req.Header.Set("Transfer-Encoding", "chunked") + } + if test.forceContentLen > 0 { + req.ContentLength = test.forceContentLen + req.Header.Set("Content-Length", strconv.FormatInt(test.forceContentLen, 10)) + } + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + if f.recorder.Code != test.expRespHTTPStatus { + t.Fatalf("Unexpected HTTP status code, (exp,got): %d, %d, response body: %s", test.expRespHTTPStatus, f.recorder.Code, f.recorder.Body.Bytes()) + } + if test.expErrorMsg != "" { + var serverErr types.ErrorV1 + if err := json.Unmarshal(f.recorder.Body.Bytes(), &serverErr); err != nil { + t.Fatalf("Could not deserialize error message: %s, message was: %s", err.Error(), f.recorder.Body.Bytes()) + } + if !strings.Contains(serverErr.Message, test.expErrorMsg) { + t.Fatalf("Expected error message to have message '%s', got message: '%s'", test.expErrorMsg, serverErr.Message) + } + } else { + var resp types.DataResponseV1 + if err := json.Unmarshal(f.recorder.Body.Bytes(), &resp); err != nil { + t.Fatalf("Could not deserialize response: %s, message was: %s", err.Error(), f.recorder.Body.Bytes()) + } + if test.expWarningMsg != "" { + if !strings.Contains(resp.Warning.Message, test.expWarningMsg) { + t.Fatalf("Expected warning message to have message '%s', got message: '%s'", test.expWarningMsg, resp.Warning.Message) + } + } else if resp.Warning != nil { + // Error on unexpected warnings. Something is wrong. + t.Fatalf("Unexpected warning: code: %s, message: %s", resp.Warning.Code, resp.Warning.Message) + } + } + }) + } +} + +func TestDataPostV0CompressedResponse(t *testing.T) { + t.Parallel() + + tests := []struct { + gzipMinLength int + compressedResponse bool + }{ + { + gzipMinLength: 3, + compressedResponse: true, + }, + { + gzipMinLength: 1400, + compressedResponse: false, + }, + } + + for _, test := range tests { + f := newFixtureWithConfig(t, fmt.Sprintf(`{"server":{"encoding":{"gzip":{"min_length": %d}}}}`, test.gzipMinLength)) + // create the policy + err := f.v1(http.MethodPut, "/policies/test", `package opa.examples +import rego.v1 +import input.example.flag +allow_request if { flag == true } +`, 200, "") + if err != nil { + t.Fatal(err) + } + + // execute the request + req := newReqV0(http.MethodPost, "/data/opa/examples/allow_request", `{"example": {"flag": true}}`) + req.Header.Set("Accept-Encoding", "gzip") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + // check for content encoding + expectedEncoding := "gzip" + if !test.compressedResponse { + expectedEncoding = "" + } + receivedEncodingHeaderValue := f.recorder.Header().Get("Content-Encoding") + if receivedEncodingHeaderValue != expectedEncoding { + t.Fatalf("Expected Content-Encoding %v but got: %v", expectedEncoding, receivedEncodingHeaderValue) + } + + var plainOutput []byte + if test.compressedResponse { + // unzip the response + gzReader, err := gzip.NewReader(f.recorder.Body) + if err != nil { + t.Fatalf("Unexpected gzip error: %v", err) + } + plainOutput, err = io.ReadAll(gzReader) + if err != nil { + t.Fatalf("Unexpected error on reading the response: %v", err) + } + } else { + plainOutput = f.recorder.Body.Bytes() + } + + expected := "true" + result := strings.TrimSuffix(string(plainOutput), "\n") + if plainOutput == nil || result != expected { + t.Fatalf("Expected %v but got: %v", expected, result) + } + } +} + +func TestDataPostV1CompressedResponse(t *testing.T) { + t.Parallel() + + tests := []struct { + gzipMinLength int + compressedResponse bool + }{ + { + gzipMinLength: 3, + compressedResponse: true, + }, + { + gzipMinLength: 1400, + compressedResponse: false, + }, + } + + for _, test := range tests { + f := newFixtureWithConfig(t, fmt.Sprintf(`{"server":{"encoding":{"gzip":{"min_length": %d}}}}`, test.gzipMinLength)) + // create the policy + err := f.v1(http.MethodPut, "/policies/test", `package test +import rego.v1 +default hello := false +hello if { + input.message == "world" +} +`, 200, "") + if err != nil { + t.Fatal(err) + } + + // execute the request + req := newReqV1(http.MethodPost, "/data/test", `{"input": {"message": "world"}}`) + req.Header.Set("Accept-Encoding", "gzip") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.DataResponseV1 + + // check for content encoding + expectedEncoding := "gzip" + if !test.compressedResponse { + expectedEncoding = "" + } + receivedEncodingHeaderValue := f.recorder.Header().Get("Content-Encoding") + if receivedEncodingHeaderValue != expectedEncoding { + t.Fatalf("Expected Content-Encoding %v but got: %v", expectedEncoding, receivedEncodingHeaderValue) + } + + if test.compressedResponse { + // unzip and unmarshall the response + gzReader, err := gzip.NewReader(f.recorder.Body) + if err != nil { + t.Fatalf("Unexpected gzip error: %v", err) + } + if err := util.NewJSONDecoder(gzReader).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + } else { + // unmarshall the response + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + } + + var expected any + if err := util.UnmarshalJSON([]byte(`{"hello": true}`), &expected); err != nil { + panic(err) + } + if result.Result == nil || !reflect.DeepEqual(*result.Result, expected) { + t.Fatalf("Expected %v but got: %v", expected, *result.Result) + } + } +} + +func TestCompileV1CompressedResponse(t *testing.T) { + t.Parallel() + + tests := []struct { + gzipMinLength int + compressedResponse bool + }{ + { + gzipMinLength: 3, + compressedResponse: true, + }, + { + gzipMinLength: 1400, + compressedResponse: false, + }, + } + + for _, test := range tests { + f := newFixtureWithConfig(t, fmt.Sprintf(`{"server":{"encoding":{"gzip":{"min_length": %d}}}}`, test.gzipMinLength)) + + // create the policy + mod := `package test + import rego.v1 + + p if { + input.x = 1 + } + + q if { + data.a[i] = input.x + } + + default r = true + + r if { input.x = 1 } + + custom_func(x) if { data.a[i] == x } + + s if { custom_func(input.x) } + ` + err := f.v1(http.MethodPut, "/policies/test", mod, 200, "") + if err != nil { + t.Fatal(err) + } + + // execute the request + req := newReqV1(http.MethodPost, "/compile", `{"unknowns": ["input"], "query": "data.test.p = true"}`) + req.Header.Set("Accept-Encoding", "gzip") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.CompileResponseV1 + + // check for content encoding + expectedEncoding := "gzip" + if !test.compressedResponse { + expectedEncoding = "" + } + receivedEncodingHeaderValue := f.recorder.Header().Get("Content-Encoding") + if receivedEncodingHeaderValue != expectedEncoding { + t.Fatalf("Expected Content-Encoding %v but got: %v", expectedEncoding, receivedEncodingHeaderValue) + } + + if test.compressedResponse { + // unzip and unmarshall the response + gzReader, err := gzip.NewReader(f.recorder.Body) + if err != nil { + t.Fatalf("Unexpected gzip error: %v", err) + } + if err := util.NewJSONDecoder(gzReader).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + } else { + // unmarshall the response + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + } + + var expected any + expectedStr := fmt.Sprintf(`{"queries": [%v]}`, string(util.MustMarshalJSON(ast.MustParseBody("input.x = 1")))) + if err := util.UnmarshalJSON([]byte(expectedStr), &expected); err != nil { + panic(err) + } + + if result.Result == nil || !reflect.DeepEqual(*result.Result, expected) { + t.Fatalf("Expected %v but got: %v", expected, *result.Result) + } + } +} + +func TestDataPostV0CompressedRequest(t *testing.T) { + t.Parallel() + + f := newFixture(t) + // create the policy + err := f.v1(http.MethodPut, "/policies/test", `package opa.examples +import rego.v1 +import input.example.flag +allow_request if { flag == true } +`, 200, "") + if err != nil { + t.Fatal(err) + } + + // execute the request + compressedBoy := zipString(`{"example": {"flag": true}}`) + req := newStreamedReqV0(http.MethodPost, "/data/opa/examples/allow_request", bytes.NewReader(compressedBoy)) + req.Header.Set("Content-Encoding", "gzip") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + expected := "true" + result := strings.TrimSuffix(f.recorder.Body.String(), "\n") + if result != expected { + t.Fatalf("Expected %v but got: %v", expected, result) + } +} + +func TestDataPostV1CompressedRequest(t *testing.T) { + t.Parallel() + + f := newFixture(t) + // create the policy + err := f.v1(http.MethodPut, "/policies/test", `package test +import rego.v1 +default hello := false +hello if { + input.message == "world" +} +`, 200, "") + if err != nil { + t.Fatal(err) + } + + // execute the request + compressedBoy := zipString(`{"input": {"message": "world"}}`) + req := newStreamedReqV1(http.MethodPost, "/data/test", bytes.NewReader(compressedBoy)) + req.Header.Set("Content-Encoding", "gzip") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.DataResponseV1 + + // unmarshall the response + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + var expected any + if err := util.UnmarshalJSON([]byte(`{"hello": true}`), &expected); err != nil { + panic(err) + } + if result.Result == nil || !reflect.DeepEqual(*result.Result, expected) { + t.Fatalf("Expected %v but got: %v", expected, *result.Result) + } +} + +func TestCompileV1CompressedRequest(t *testing.T) { + t.Parallel() + + f := newFixture(t) + // create the policy + mod := `package test + import rego.v1 + + p if { + input.x = 1 + } + + q if { + data.a[i] = input.x + } + + default r = true + + r if { input.x = 1 } + + custom_func(x) if { data.a[i] == x } + + s if { custom_func(input.x) } + ` + err := f.v1(http.MethodPut, "/policies/test", mod, 200, "") + if err != nil { + t.Fatal(err) + } + + // execute the request + compressedBoy := zipString(`{"unknowns": ["input"], "query": "data.test.p = true"}`) + req := newStreamedReqV1(http.MethodPost, "/compile", bytes.NewReader(compressedBoy)) + req.Header.Set("Content-Encoding", "gzip") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.CompileResponseV1 + + // unmarshall the response + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + var expected any + expectedStr := fmt.Sprintf(`{"queries": [%v]}`, string(util.MustMarshalJSON(ast.MustParseBody("input.x = 1")))) + if err := util.UnmarshalJSON([]byte(expectedStr), &expected); err != nil { + panic(err) + } + + if result.Result == nil || !reflect.DeepEqual(*result.Result, expected) { + t.Fatalf("Expected %v but got: %v", expected, *result.Result) + } +} + +func TestBundleScope(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + test.WithTempFS(nil, func(root string) { + disk, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{Dir: root}) + if err != nil { + t.Fatal(err) + } + defer disk.Close(ctx) + + for _, v := range []variant{ + {"inmem", nil}, + {"disk", []any{func(s *Server) { s.WithStore(disk) }}}, + } { + t.Run(v.name, func(t *testing.T) { + f := newFixture(t, v.opts...) + + txn := storage.NewTransactionOrDie(ctx, f.server.store, storage.WriteParams) + + if err := bundle.WriteManifestToStore(ctx, f.server.store, txn, "test-bundle", bundle.Manifest{ + Revision: "AAAAA", + Roots: &[]string{"a/b/c", "x/y", "foobar"}, + }); err != nil { + t.Fatal(err) + } + + if err := f.server.store.UpsertPolicy(ctx, txn, "someid", []byte(`package x.y.z`)); err != nil { + t.Fatal(err) + } + + if err := f.server.store.Commit(ctx, txn); err != nil { + t.Fatal(err) + } + + cases := []tr{ + { + method: "PUT", + path: "/data/a/b", + body: "1", + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "path a/b is owned by bundle \"test-bundle\""}`, + }, + { + method: "PUT", + path: "/data/a/b/c", + body: "1", + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "path a/b/c is owned by bundle \"test-bundle\""}`, + }, + { + method: "PUT", + path: "/data/a/b/c/d", + body: "1", + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "path a/b/c/d is owned by bundle \"test-bundle\""}`, + }, + { + method: "PUT", + path: "/data/a/b/d", + body: "1", + code: http.StatusNoContent, + }, + { + method: "PATCH", + path: "/data/a", + body: `[{"path": "/b/c", "op": "add", "value": 1}]`, + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "path a/b/c is owned by bundle \"test-bundle\""}`, + }, + { + method: "DELETE", + path: "/data/a", + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "path a is owned by bundle \"test-bundle\""}`, + }, + { + method: "PUT", + path: "/policies/test1", + body: `package a.b`, + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "path a/b is owned by bundle \"test-bundle\""}`, + }, + { + method: "PUT", + path: "/policies/someid", + body: `package other.path`, + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "path x/y/z is owned by bundle \"test-bundle\""}`, + }, + { + method: "DELETE", + path: "/policies/someid", + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "path x/y/z is owned by bundle \"test-bundle\""}`, + }, + { + method: "PUT", + path: "/data/foo/bar", + body: "1", + code: http.StatusNoContent, + }, + { + method: "PUT", + path: "/data/foo", + body: "1", + code: http.StatusNoContent, + }, + { + method: "PUT", + path: "/data", + body: `{"a": "b"}`, + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "can't write to document root with bundle roots configured"}`, + }, + } + + if err := f.v1TestRequests(cases); err != nil { + t.Fatal(err) + } + }) + } + }) +} + +func TestBundleScopeMultiBundle(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + f := newFixture(t) + + txn := storage.NewTransactionOrDie(ctx, f.server.store, storage.WriteParams) + + if err := bundle.WriteManifestToStore(ctx, f.server.store, txn, "test-bundle1", bundle.Manifest{ + Revision: "AAAAA", + Roots: &[]string{"a/b/c", "x/y"}, + }); err != nil { + t.Fatal(err) + } + + if err := bundle.WriteManifestToStore(ctx, f.server.store, txn, "test-bundle2", bundle.Manifest{ + Revision: "AAAAA", + Roots: &[]string{"a/b/d"}, + }); err != nil { + t.Fatal(err) + } + + if err := bundle.WriteManifestToStore(ctx, f.server.store, txn, "test-bundle3", bundle.Manifest{ + Revision: "AAAAA", + Roots: &[]string{"a/b/e", "a/b/f"}, + }); err != nil { + t.Fatal(err) + } + + if err := f.server.store.UpsertPolicy(ctx, txn, "someid", []byte(`package x.y.z`)); err != nil { + t.Fatal(err) + } + + if err := f.server.store.Commit(ctx, txn); err != nil { + t.Fatal(err) + } + + cases := []tr{ + { + method: "PUT", + path: "/data/x/y", + body: "1", + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "path x/y is owned by bundle \"test-bundle1\""}`, + }, + { + method: "PUT", + path: "/data/a/b/d", + body: "1", + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "path a/b/d is owned by bundle \"test-bundle2\""}`, + }, + { + method: "PUT", + path: "/data/foo/bar", + body: "1", + code: http.StatusNoContent, + }, + } + + if err := f.v1TestRequests(cases); err != nil { + t.Fatal(err) + } +} + +func TestBundleNoRoots(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + f := newFixture(t) + + txn := storage.NewTransactionOrDie(ctx, f.server.store, storage.WriteParams) + + if err := bundle.WriteManifestToStore(ctx, f.server.store, txn, "test-bundle", bundle.Manifest{ + Revision: "AAAAA", + // No Roots provided + }); err != nil { + t.Fatal(err) + } + + if err := f.server.store.UpsertPolicy(ctx, txn, "someid", []byte(`package x.y.z`)); err != nil { + t.Fatal(err) + } + + if err := f.server.store.Commit(ctx, txn); err != nil { + t.Fatal(err) + } + + cases := []tr{ + { + method: "PUT", + path: "/data/a/b", + body: "1", + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "all paths owned by bundle \"test-bundle\""}`, + }, + } + + if err := f.v1TestRequests(cases); err != nil { + t.Fatal(err) + } + + txn = storage.NewTransactionOrDie(ctx, f.server.store, storage.WriteParams) + + if err := bundle.WriteManifestToStore(ctx, f.server.store, txn, "test-bundle", bundle.Manifest{ + Revision: "AAAAA", + // Roots provided but contains empty string + Roots: &[]string{"", "does/not/matter"}, + }); err != nil { + t.Fatal(err) + } + + if err := f.server.store.UpsertPolicy(ctx, txn, "someid", []byte(`package x.y.z`)); err != nil { + t.Fatal(err) + } + + if err := f.server.store.Commit(ctx, txn); err != nil { + t.Fatal(err) + } + + cases = []tr{ + { + method: "PUT", + path: "/data/a/b", + body: "1", + code: http.StatusBadRequest, + resp: `{"code": "invalid_parameter", "message": "all paths owned by bundle \"test-bundle\""}`, + }, + } + + if err := f.v1TestRequests(cases); err != nil { + t.Fatal(err) + } +} + +func TestDataUpdate(t *testing.T) { + tests := []struct { + note string + readAst bool + }{ + { + note: "read raw data", + }, + { + note: "read ast data", + readAst: true, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + f := newFixtureWithStore(t, inmem.NewWithOpts(inmem.OptReturnASTValuesOnRead(tc.readAst))) + + // PUT data + + putData := `{"a":1,"b":2, "c": 3}` + err := f.v1(http.MethodPut, "/data/x", putData, 204, "") + if err != nil { + t.Fatal(err) + } + + req := newReqV1(http.MethodGet, "/data/x", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.DataResponseV1 + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + var expected any + if err := util.UnmarshalJSON([]byte(putData), &expected); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + if result.Result == nil || !reflect.DeepEqual(*result.Result, expected) { + t.Fatalf("Expected %v but got: %v", expected, *result.Result) + } + + // DELETE data + + if err := f.v1(http.MethodDelete, "/data/x/b", "", 204, ""); err != nil { + t.Fatal("Unexpected error:", err) + } + + req = newReqV1(http.MethodGet, "/data/x", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + if err := util.UnmarshalJSON([]byte(`{"a":1,"c": 3}`), &expected); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + if result.Result == nil || !reflect.DeepEqual(*result.Result, expected) { + t.Fatalf("Expected %v but got: %v", expected, *result.Result) + } + }) + } +} + +func TestDataGetExplainFull(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + err := f.v1(http.MethodPut, "/data/x", `{"a":1,"b":2}`, 204, "") + if err != nil { + t.Fatal(err) + } + + req := newReqV1(http.MethodGet, "/data/x?explain=full", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.DataResponseV1 + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + explain := mustUnmarshalTrace(result.Explanation) + nexpect := 5 + if len(explain) != nexpect { + t.Fatalf("Expected exactly %d events but got %d", nexpect, len(explain)) + } + + exitEvent := -1 + for i := 0; i < len(explain) && exitEvent < 0; i++ { + if explain[i].Op == "exit" { + exitEvent = i + } + } + if exitEvent < 0 { + t.Fatalf("Expected one exit node but found none") + } + + _, ok := explain[exitEvent].Node.(ast.Body) + if !ok { + t.Fatalf("Expected body for node but got: %v", explain[exitEvent].Node) + } + + if len(explain[exitEvent].Locals) != 1 { + t.Fatalf("Expected one binding but got: %v", explain[exitEvent].Locals) + } + + req = newReqV1(http.MethodGet, "/data/deadbeef?explain=full", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + result = types.DataResponseV1{} + + if f.recorder.Code != 200 { + t.Fatalf("Expected status code to be 200 but got: %v", f.recorder.Code) + } + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + explain = mustUnmarshalTrace(result.Explanation) + nexpect = 3 + if len(explain) != nexpect { + t.Fatalf("Expected exactly %d events but got %d", nexpect, len(explain)) + } + + lastEvent := len(explain) - 1 + if explain[lastEvent].Op != "fail" { + t.Fatalf("Expected last event to be 'fail' but got: %v", explain[lastEvent]) + } + + req = newReqV1(http.MethodGet, "/data/x?explain=full&pretty=true", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + result = types.DataResponseV1{} + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + exp := []any{ + `query:1 Enter data.x = _`, + `query:1 | Eval data.x = _`, + `query:1 | Exit data.x = _`, + `query:1 Redo data.x = _`, + `query:1 | Redo data.x = _`} + actual := util.MustUnmarshalJSON(result.Explanation).([]any) + if !reflect.DeepEqual(actual, exp) { + t.Fatalf(`Expected pretty explanation to be %v, got %v`, exp, actual) + } +} + +func TestDataPostWithActiveStoreWriteTxn(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + err := f.v1(http.MethodPut, "/policies/test", `package test +import rego.v1 + +p = [1, 2, 3, 4] if { true }`, 200, "") + if err != nil { + t.Fatal(err) + } + + // open write transaction on the store and execute a query. + // Then check the query is processed + ctx := context.Background() + _ = storage.NewTransactionOrDie(ctx, f.server.store, storage.WriteParams) + + req := newReqV1(http.MethodPost, "/data/test/p", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.DataResponseV1 + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + var expected any + + if err := util.UnmarshalJSON([]byte(`[1,2,3,4]`), &expected); err != nil { + panic(err) + } + + if result.Result == nil || !reflect.DeepEqual(*result.Result, expected) { + t.Fatalf("Expected %v but got: %v", expected, result.Result) + } +} + +func TestDataPostExplain(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + err := f.v1(http.MethodPut, "/policies/test", `package test +import rego.v1 + +p = [1, 2, 3, 4] if { true }`, 200, "") + if err != nil { + t.Fatal(err) + } + + req := newReqV1(http.MethodPost, "/data/test/p?explain=full", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.DataResponseV1 + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + explain := mustUnmarshalTrace(result.Explanation) + nexpect := 11 + + if len(explain) != nexpect { + t.Fatalf("Expected exactly %d events but got %d", nexpect, len(explain)) + } + + var expected any + + if err := util.UnmarshalJSON([]byte(`[1,2,3,4]`), &expected); err != nil { + panic(err) + } + + if result.Result == nil || !reflect.DeepEqual(*result.Result, expected) { + t.Fatalf("Expected %v but got: %v", expected, result.Result) + } + +} + +func TestDataPostExplainNotes(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + err := f.v1(http.MethodPut, "/policies/test", ` + package test + import rego.v1 + + p if { + data.a[i] = x; x > 1 + trace(sprintf("found x = %d", [x])) + }`, 200, "") + if err != nil { + t.Fatal(err) + } + + err = f.v1(http.MethodPut, "/data/a", `[1,2,3]`, 204, "") + if err != nil { + t.Fatal(err) + } + f.reset() + + req := newReqV1(http.MethodPost, "/data/test/p?explain=notes", "") + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.DataResponseV1 + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode err: %v", err) + } + + var trace types.TraceV1Raw + + if err := trace.UnmarshalJSON(result.Explanation); err != nil { + t.Fatal(err) + } + + if len(trace) != 3 || trace[2].Op != "note" { + t.Logf("Found %d events in trace", len(trace)) + for i := range trace { + t.Logf("Event #%d: %v\n", i, trace[i]) + } + t.Fatal("Unexpected trace") + } +} + +// Warning(philipc): This test modifies package variables in the version +// package, which means it cannot be run in parallel with other tests. +func TestDataProvenanceSingleBundle(t *testing.T) { + f := newFixture(t) + + // Dummy up since we are not using ld... + // Note: No bundle 'revision'... + version.Version = "0.10.7" + version.Vcs = "ac23eb45" + version.Timestamp = "today" + version.Hostname = "foo.bar.com" + + // Initialize as if a bundle plugin is running + bp := pluginBundle.New(&pluginBundle.Config{Name: "b1"}, f.server.manager) + f.server.manager.Register(pluginBundle.Name, bp) + + req := newReqV1(http.MethodPost, "/data?provenance", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.DataResponseV1 + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + if result.Provenance == nil { + t.Fatalf("Expected non-nil provenance: %v", result.Provenance) + } + + expectedProvenance := &types.ProvenanceV1{ + Version: version.Version, + Vcs: version.Vcs, + Timestamp: version.Timestamp, + Hostname: version.Hostname, + } + + if !reflect.DeepEqual(result.Provenance, expectedProvenance) { + t.Errorf("Unexpected provenance data: \n\n%+v\n\nExpected:\n%+v\n\n", result.Provenance, expectedProvenance) + } + + ctx := context.Background() + + // Update bundle revision and request again + err := storage.Txn(ctx, f.server.store, storage.WriteParams, func(txn storage.Transaction) error { + return bundle.LegacyWriteManifestToStore(ctx, f.server.store, txn, bundle.Manifest{Revision: "r1"}) + }) + if err != nil { + t.Fatal(err) + } + + req = newReqV1(http.MethodPost, "/data?provenance", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + result = types.DataResponseV1{} + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + if result.Provenance == nil { + t.Fatalf("Expected non-nil provenance: %v", result.Provenance) + } + + expectedProvenance.Revision = "r1" + if !reflect.DeepEqual(result.Provenance, expectedProvenance) { + t.Errorf("Unexpected provenance data: \n\n%+v\n\nExpected:\n%+v\n\n", result.Provenance, expectedProvenance) + } +} + +// Warning(philipc): This test modifies package variables in the version +// package, which means it cannot be run in parallel with other tests. +func TestDataProvenanceSingleFileBundle(t *testing.T) { + f := newFixture(t) + + // Dummy up since we are not using ld... + // Note: No bundle 'revision'... + version.Version = "0.10.7" + version.Vcs = "ac23eb45" + version.Timestamp = "today" + version.Hostname = "foo.bar.com" + + // No bundle plugin initialized, just a legacy revision set + ctx := context.Background() + + err := storage.Txn(ctx, f.server.store, storage.WriteParams, func(txn storage.Transaction) error { + return bundle.LegacyWriteManifestToStore(ctx, f.server.store, txn, bundle.Manifest{Revision: "r1"}) + }) + if err != nil { + t.Fatal(err) + } + + req := newReqV1(http.MethodPost, "/data?provenance", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + result := types.DataResponseV1{} + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + if result.Provenance == nil { + t.Fatalf("Expected non-nil provenance: %v", result.Provenance) + } + + expectedProvenance := &types.ProvenanceV1{ + Version: version.Version, + Vcs: version.Vcs, + Timestamp: version.Timestamp, + Hostname: version.Hostname, + } + + expectedProvenance.Revision = "r1" + if !reflect.DeepEqual(result.Provenance, expectedProvenance) { + t.Errorf("Unexpected provenance data: \n\n%+v\n\nExpected:\n%+v\n\n", result.Provenance, expectedProvenance) + } +} + +// Warning(philipc): This test modifies package variables in the version +// package, which means it cannot be run in parallel with other tests. +func TestDataProvenanceMultiBundle(t *testing.T) { + f := newFixture(t) + + // Dummy up since we are not using ld... + version.Version = "0.10.7" + version.Vcs = "ac23eb45" + version.Timestamp = "today" + version.Hostname = "foo.bar.com" + + // Initialize as if a bundle plugin is running with 2 bundles + bp := pluginBundle.New(&pluginBundle.Config{Bundles: map[string]*pluginBundle.Source{ + "b1": {Service: "s1", Resource: "bundle.tar.gz"}, + "b2": {Service: "s2", Resource: "bundle.tar.gz"}, + }}, f.server.manager) + f.server.manager.Register(pluginBundle.Name, bp) + + req := newReqV1(http.MethodPost, "/data?provenance", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.DataResponseV1 + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + if result.Provenance == nil { + t.Fatalf("Expected non-nil provenance: %v", result.Provenance) + } + + expectedProvenance := &types.ProvenanceV1{ + Version: version.Version, + Vcs: version.Vcs, + Timestamp: version.Timestamp, + Hostname: version.Hostname, + } + + if !reflect.DeepEqual(result.Provenance, expectedProvenance) { + t.Errorf("Unexpected provenance data: \n\n%+v\n\nExpected:\n%+v\n\n", result.Provenance, expectedProvenance) + } + + // Update bundle revision for a single bundle and make the request again + ctx := context.Background() + + err := storage.Txn(ctx, f.server.store, storage.WriteParams, func(txn storage.Transaction) error { + return bundle.WriteManifestToStore(ctx, f.server.store, txn, "b1", bundle.Manifest{Revision: "r1"}) + }) + if err != nil { + t.Fatal(err) + } + + req = newReqV1(http.MethodPost, "/data?provenance", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + result = types.DataResponseV1{} + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + if result.Provenance == nil { + t.Fatalf("Expected non-nil provenance: %v", result.Provenance) + } + + expectedProvenance.Bundles = map[string]types.ProvenanceBundleV1{ + "b1": {Revision: "r1"}, + } + + if !reflect.DeepEqual(result.Provenance, expectedProvenance) { + t.Errorf("Unexpected provenance data: \n\n%+v\n\nExpected:\n%+v\n\n", result.Provenance, expectedProvenance) + } + + // Update both and check again + err = storage.Txn(ctx, f.server.store, storage.WriteParams, func(txn storage.Transaction) error { + err := bundle.WriteManifestToStore(ctx, f.server.store, txn, "b1", bundle.Manifest{Revision: "r2"}) + if err != nil { + return err + } + return bundle.WriteManifestToStore(ctx, f.server.store, txn, "b2", bundle.Manifest{Revision: "r1"}) + }) + if err != nil { + t.Fatal(err) + } + + req = newReqV1(http.MethodPost, "/data?provenance", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + result = types.DataResponseV1{} + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + if result.Provenance == nil { + t.Fatalf("Expected non-nil provenance: %v", result.Provenance) + } + + expectedProvenance.Bundles = map[string]types.ProvenanceBundleV1{ + "b1": {Revision: "r2"}, + "b2": {Revision: "r1"}, + } + + if !reflect.DeepEqual(result.Provenance, expectedProvenance) { + t.Errorf("Unexpected provenance data: \n\n%+v\n\nExpected:\n%+v\n\n", result.Provenance, expectedProvenance) + } +} + +func TestDataMetricsEval(t *testing.T) { + t.Parallel() + + // These tests all use the /v1/data API with ?metrics appended. + // We're setting up the disk store because that injects a few extra metrics, + // which storage/inmem does not. + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + test.WithTempFS(nil, func(root string) { + disk, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{Dir: root}) + if err != nil { + t.Fatal(err) + } + defer disk.Close(ctx) + + f := newFixtureWithStore(t, disk) + + // Make a request to evaluate `data` + testDataMetrics(t, f, http.MethodPost, "/data?metrics", "", []string{ + "counter_server_query_cache_hit", + "counter_disk_read_keys", + "counter_disk_read_bytes", + "timer_rego_input_parse_ns", + "timer_rego_query_compile_ns", + "timer_rego_query_eval_ns", + "timer_server_handler_ns", + "timer_disk_read_ns", + "timer_rego_external_resolve_ns", + }) + + // Repeat previous request, expect to have hit the query cache + // so fewer timers should have been reported. + testDataMetrics(t, f, http.MethodPost, "/data?metrics", "", []string{ + "counter_server_query_cache_hit", + "counter_disk_read_keys", + "counter_disk_read_bytes", + "timer_disk_read_ns", + "timer_rego_external_resolve_ns", + "timer_rego_input_parse_ns", + "timer_rego_query_eval_ns", + "timer_server_handler_ns", + }) + + // Exercise the PUT, PATCH, and DELETE endpoints. + testDataMetrics(t, f, http.MethodPut, "/data/example?metrics", "{}", []string{ + "counter_disk_read_keys", + "counter_disk_written_keys", + "timer_disk_commit_ns", + "timer_disk_read_ns", + "timer_disk_write_ns", + "timer_rego_input_parse_ns", + "timer_server_handler_ns", + }) + + testDataMetrics(t, f, http.MethodPatch, "/data/example?metrics", "[]", []string{ + "timer_disk_commit_ns", + "timer_rego_input_parse_ns", + "timer_server_handler_ns", + }) + + testDataMetrics(t, f, http.MethodDelete, "/data/example?metrics", "{}", []string{ + "counter_disk_deleted_keys", + "counter_disk_read_keys", + "counter_disk_read_bytes", + "timer_disk_commit_ns", + "timer_disk_read_ns", + "timer_disk_write_ns", + "timer_server_handler_ns", + }) + }) +} + +func testDataMetrics(t *testing.T, f *fixture, method string, url string, payload string, expected []string) { + t.Helper() + f.reset() + req := newReqV1(method, url, payload) + f.server.Handler.ServeHTTP(f.recorder, req) + + var result types.DataResponseV1 + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + assertMetricsExist(t, result.Metrics, expected) +} + +func assertMetricsExist(t *testing.T, metrics types.MetricsV1, expected []string) { + t.Helper() + + for _, key := range expected { + v, ok := metrics[key] + if !ok { + t.Errorf("Missing expected metric: %s", key) + } else if v == nil { + t.Errorf("Expected non-nil value for metric: %s", key) + } + + } + + if len(expected) != len(metrics) { + t.Errorf("Expected %d metrics, got %d\n\n\tValues: %+v", len(expected), len(metrics), metrics) + } +} + +func TestV1Pretty(t *testing.T) { + t.Parallel() + + f := newFixture(t) + err := f.v1(http.MethodPatch, "/data/x", `[{"op": "add", "path":"/", "value": [1,2,3,4]}]`, 204, "") + if err != nil { + t.Fatal(err) + } + + req := newReqV1(http.MethodGet, "/data/x?pretty=true", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + lines := strings.Split(f.recorder.Body.String(), "\n") + if len(lines) != 9 { + t.Errorf("Expected 8 lines in output but got %d:\n%v", len(lines), lines) + } + + req = newReqV1(http.MethodGet, "/query?q=data.x[i]&pretty=true", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + lines = strings.Split(f.recorder.Body.String(), "\n") + if len(lines) != 17 { + t.Errorf("Expected 16 lines of output but got %d:\n%v", len(lines), lines) + } +} + +func TestPoliciesPutV1(t *testing.T) { + t.Parallel() + + v0Module := `package a.b.c + +import data.x.y as z +import data.p + +q[x] { p[x]; not r[x] } +r[x] { z[x] = 4 }` + + v1Module := `package a.b.c + +import data.x.y as z +import data.p + +q contains x if { p[x]; not r[x] } +r contains x if { z[x] = 4 }` + + tests := []struct { + note string + regoVersion ast.RegoVersion + module string + expErrs []string + }{ + { + note: "v0 server, v0 module", + regoVersion: ast.RegoV0, + module: v0Module, + }, + { + note: "v0 server, v1 module", + regoVersion: ast.RegoV0, + module: v1Module, + expErrs: []string{"var cannot be used for rule name"}, + }, + { + note: "v1 server, v1 module", + regoVersion: ast.RegoV1, + module: v1Module, + }, + { + note: "v1 server, v0 module", + regoVersion: ast.RegoV1, + module: v0Module, + expErrs: []string{ + "`if` keyword is required before rule body", + "`contains` keyword is required for partial set rules", + }, + }, + } + + for i, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + f := newFixture(t, plugins.WithParserOptions(ast.ParserOptions{ + RegoVersion: tc.regoVersion, + })) + req := newReqV1(http.MethodPut, fmt.Sprintf("/policies/%d", i), tc.module) + + f.server.Handler.ServeHTTP(f.recorder, req) + + var response map[string]any + if err := json.NewDecoder(f.recorder.Body).Decode(&response); err != nil { + t.Fatalf("Unexpected error while unmarshalling response: %v", err) + } + + if len(tc.expErrs) > 0 { + if f.recorder.Code != 400 { + t.Fatalf("Expected bad request but got %v", f.recorder) + } + + var errs []string + if errors, ok := response["errors"].([]any); ok { + for _, err := range errors { + errs = append(errs, err.(map[string]any)["message"].(string)) + } + } + + for _, expErr := range tc.expErrs { + found := false + for _, err := range errs { + if strings.Contains(err, expErr) { + found = true + break + } + } + if !found { + t.Fatalf("Expected error containing %q but got: %v", expErr, errs) + } + } + } else { + if f.recorder.Code != 200 { + t.Fatalf("Expected success but got %v", f.recorder) + } + + if len(response) != 0 { + t.Fatalf("Expected empty wrapper object") + } + } + }) + } +} + +func TestPoliciesPutV1Empty(t *testing.T) { + t.Parallel() + + f := newFixture(t) + req := newReqV1(http.MethodPut, "/policies/1", "") + + f.server.Handler.ServeHTTP(f.recorder, req) + + if f.recorder.Code != 400 { + t.Fatalf("Expected bad request but got %v", f.recorder) + } +} + +func TestPoliciesPutV1ParseError(t *testing.T) { + t.Parallel() + + f := newFixture(t) + req := newReqV1(http.MethodPut, "/policies/test", ` + package a.b.c + + p ;- true + `) + + f.server.Handler.ServeHTTP(f.recorder, req) + + if f.recorder.Code != 400 { + t.Fatalf("Expected bad request but got %v", f.recorder) + } + + response := map[string]any{} + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&response); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + if !reflect.DeepEqual(response["code"], types.CodeInvalidParameter) { + t.Fatalf("Expected code %v but got: %v", types.CodeInvalidParameter, response) + } + + v := ast.MustInterfaceToValue(response) + + name, err := v.Find(ast.MustParseRef("_.errors[0].location.file")[1:]) + if err != nil { + t.Fatalf("Expecfted to find name in errors but: %v", err) + } + + if name.Compare(ast.String("test")) != 0 { + t.Fatalf("Expected name ot equal test but got: %v", name) + } + + req = newReqV1(http.MethodPut, "/policies/test", ``) + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + if f.recorder.Code != 400 { + t.Fatalf("Expected bad request but got %v", f.recorder) + } + + req = newReqV1(http.MethodPut, "/policies/test", ` + package a.b.c + + p = true`) + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + if f.recorder.Code != 200 { + t.Fatalf("Expected ok but got %v", f.recorder) + } + +} + +func TestPoliciesPutV1CompileError(t *testing.T) { + t.Parallel() + + f := newFixture(t) + req := newReqV1(http.MethodPut, "/policies/test", `package a.b.c + +p[x] { q[x] } +q[x] { p[x] }`, + ) + + f.server.Handler.ServeHTTP(f.recorder, req) + + if f.recorder.Code != 400 { + t.Fatalf("Expected bad request but got %v", f.recorder) + } + + response := map[string]any{} + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&response); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + if !reflect.DeepEqual(response["code"], types.CodeInvalidParameter) { + t.Fatalf("Expected code %v but got: %v", types.CodeInvalidParameter, response) + } + + v := ast.MustInterfaceToValue(response) + + name, err := v.Find(ast.MustParseRef("_.errors[0].location.file")[1:]) + if err != nil { + t.Fatalf("Expecfted to find name in errors but: %v", err) + } + + if name.Compare(ast.String("test")) != 0 { + t.Fatalf("Expected name ot equal test but got: %v", name) + } +} + +func TestPoliciesPutV1Noop(t *testing.T) { + t.Parallel() + + f := newFixture(t) + err := f.v1("PUT", "/policies/test?metrics", `package foo`, 200, "") + if err != nil { + t.Fatal(err) + } + f.reset() + err = f.v1("PUT", "/policies/test?metrics", `package foo`, 200, "") + if err != nil { + t.Fatal(err) + } + + var resp types.PolicyPutResponseV1 + if err := json.NewDecoder(f.recorder.Body).Decode(&resp); err != nil { + t.Fatal(err) + } + + exp := []string{"timer_server_read_bytes_ns"} + + // Sort the metric keys and compare to expected value. We're assuming the + // server skips parsing if the bytes are equal. + result := util.KeysSorted(resp.Metrics) + + if !reflect.DeepEqual(exp, result) { + t.Fatalf("Expected %v but got %v", exp, result) + } + + f.reset() + + // Ensure subsequent update with changed policy parses the body. + err = f.v1("PUT", "/policies/test?metrics", "package foo\np = 1", 200, "") + if err != nil { + t.Fatal(err) + } + + var resp2 types.PolicyPutResponseV1 + if err := json.NewDecoder(f.recorder.Body).Decode(&resp2); err != nil { + t.Fatal(err) + } + + if _, ok := resp2.Metrics["timer_rego_module_parse_ns"]; !ok { + t.Fatalf("Expected parse module metric in response but got %v", resp2) + } + +} + +func TestPoliciesListV1(t *testing.T) { + t.Parallel() + + f := newFixture(t) + putPolicy(t, f, testMod) + + expected := []types.PolicyV1{ + newPolicy("1", testMod), + } + + assertListPolicy(t, f, expected) +} + +func putPolicy(t *testing.T, f *fixture, mod string) { + t.Helper() + put := newReqV1(http.MethodPut, "/policies/1", mod) + f.server.Handler.ServeHTTP(f.recorder, put) + if f.recorder.Code != 200 { + t.Fatalf("Expected success but got %v", f.recorder) + } + f.reset() +} + +func assertListPolicy(t *testing.T, f *fixture, expected []types.PolicyV1) { + t.Helper() + + list := newReqV1(http.MethodGet, "/policies", "") + f.server.Handler.ServeHTTP(f.recorder, list) + + if f.recorder.Code != 200 { + t.Fatalf("Expected success but got %v", f.recorder) + } + + // var policies []*PolicyV1 + var response types.PolicyListResponseV1 + + err := util.NewJSONDecoder(f.recorder.Body).Decode(&response) + if err != nil { + t.Fatalf("Expected policy list but got error: %v with response body:\n\n%v\n", err, f.recorder) + } + + if len(expected) != len(response.Result) { + t.Fatalf("Expected %d policies but got: %v", len(expected), response.Result) + } + for i := range expected { + if !expected[i].Equal(response.Result[i]) { + t.Fatalf("Expected policies to be equal. Expected:\n\n%v\n\nGot:\n\n%+v\n", expected, response.Result) + } + } + + f.reset() +} + +func TestPoliciesGetV1(t *testing.T) { + t.Parallel() + + f := newFixture(t) + put := newReqV1(http.MethodPut, "/policies/1", testMod) + f.server.Handler.ServeHTTP(f.recorder, put) + + if f.recorder.Code != 200 { + t.Fatalf("Expected success but got %v", f.recorder) + } + + f.reset() + get := newReqV1(http.MethodGet, "/policies/1", "") + + f.server.Handler.ServeHTTP(f.recorder, get) + + if f.recorder.Code != 200 { + t.Fatalf("Expected success but got %v", f.recorder) + } + + var response types.PolicyGetResponseV1 + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&response); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected := newPolicy("1", testMod) + + if !expected.Equal(response.Result) { + t.Errorf("Expected policies to be equal. Expected:\n\n%v\n\nGot:\n\n%v\n", expected, response.Result) + } +} + +func TestPoliciesDeleteV1(t *testing.T) { + t.Parallel() + + f := newFixture(t) + put := newReqV1(http.MethodPut, "/policies/1", testMod) + f.server.Handler.ServeHTTP(f.recorder, put) + + if f.recorder.Code != 200 { + t.Fatalf("Expected success but got %v", f.recorder) + } + + f.reset() + del := newReqV1(http.MethodDelete, "/policies/1", "") + + f.server.Handler.ServeHTTP(f.recorder, del) + + if f.recorder.Code != 200 { + t.Fatalf("Expected success but got %v", f.recorder) + } + + var response map[string]any + if err := json.NewDecoder(f.recorder.Body).Decode(&response); err != nil { + t.Fatalf("Unexpected unmarshal error: %v", err) + } + + if len(response) > 0 { + t.Fatalf("Expected empty response but got: %v", response) + } + + f.reset() + get := newReqV1(http.MethodGet, "/policies/1", "") + f.server.Handler.ServeHTTP(f.recorder, get) + if f.recorder.Code != 404 { + t.Fatalf("Expected not found but got %v", f.recorder) + } +} + +func TestPoliciesPathSlashes(t *testing.T) { + t.Parallel() + + f := newFixture(t) + if err := f.v1(http.MethodPut, "/policies/a/b/c.rego", testMod, 200, ""); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if err := f.v1(http.MethodGet, "/policies/a/b/c.rego", testMod, 200, ""); err != nil { + t.Fatalf("Unexpected error: %v", err) + } +} + +func TestPoliciesUrlEncoded(t *testing.T) { + t.Parallel() + + const expectedPolicyID = "/a policy/another-component" + var urlEscapedPolicyID = url.PathEscape(expectedPolicyID) + f := newFixture(t) + + // PUT policy with URL encoded ID + put := newReqV1(http.MethodPut, "/policies/"+urlEscapedPolicyID, testMod) + f.server.Handler.ServeHTTP(f.recorder, put) + + if f.recorder.Code != 200 { + t.Fatalf("Expected success but got %v", f.recorder) + } + + // end PUT policy with URL encoded ID + f.reset() + // GET policy with URL encoded ID + + get := newReqV1(http.MethodGet, "/policies/"+urlEscapedPolicyID, "") + f.server.Handler.ServeHTTP(f.recorder, get) + if f.recorder.Code != 200 { + t.Fatalf("Expected success but got %v", f.recorder) + } + var getResponse types.PolicyGetResponseV1 + if err := json.NewDecoder(f.recorder.Body).Decode(&getResponse); err != nil { + t.Fatalf("Unexpected unmarshal error: %v", err) + } + + if getResponse.Result.ID != expectedPolicyID { + t.Fatalf(`Expected policy ID to be "%s" but got "%s"`, expectedPolicyID, getResponse.Result.ID) + } + + // end GET policy with URL encoded ID + f.reset() + // DELETE policy with URL encoded ID + + deleteRequest := newReqV1(http.MethodDelete, "/policies/"+urlEscapedPolicyID, "") + f.server.Handler.ServeHTTP(f.recorder, deleteRequest) + if f.recorder.Code != 200 { + t.Fatalf("Expected success but got %v", f.recorder) + } +} + +func TestStatusV1(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + // Expect HTTP 500 before status plugin is registered + req := newReqV1(http.MethodGet, "/status", "") + f.server.Handler.ServeHTTP(f.recorder, req) + + if f.recorder.Result().StatusCode != http.StatusInternalServerError { + t.Fatal("expected internal error") + } + + // Expect HTTP 200 after status plus is registered + manual := plugins.TriggerManual + bs := pluginStatus.New(&pluginStatus.Config{ + Trigger: &manual, + PrometheusConfig: &pluginStatus.PrometheusConfig{ + Collectors: &pluginStatus.Collectors{ + BundleLoadDurationNanoseconds: &pluginStatus.BundleLoadDurationNanoseconds{ + Buckets: prom.ExponentialBuckets(1000, 2, 20), + }, + }, + }, + }, f.server.manager) + err := bs.Start(context.Background()) + if err != nil { + t.Fatal(err) + } + + f.server.manager.Register(pluginStatus.Name, bs) + + // Fetch the status info, wait for status plugin to be ok + t0 := time.Now() + ok := false + for !ok && time.Since(t0) < time.Second { + req = newReqV1(http.MethodGet, "/status", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + if f.recorder.Result().StatusCode != http.StatusOK { + t.Fatal("expected ok") + } + + var resp1 struct { + Result struct { + Plugins struct { + Status struct { + State string + } + } + } + } + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&resp1); err != nil { + t.Fatal(err) + } + if resp1.Result.Plugins.Status.State == "OK" { + ok = true + } else { + t.Log("expected plugin state for status to be 'OK' but got:", resp1) + } + } + + // Expect HTTP 200 and updated status after bundle update occurs + bs.BulkUpdateBundleStatus(map[string]*pluginBundle.Status{ + "test": { + Name: "test", + HTTPCode: "403", + }, + }) + + req = newReqV1(http.MethodGet, "/status", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + if f.recorder.Result().StatusCode != http.StatusOK { + t.Fatal("expected ok") + } + + var resp2 struct { + Result struct { + Bundles struct { + Test struct { + Name string + HTTPCode json.Number `json:"http_code"` + } + } + } + } + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&resp2); err != nil { + t.Fatal(err) + } + if resp2.Result.Bundles.Test.Name != "test" { + t.Fatal("expected bundle to exist in status response but got:", resp2) + } + if resp2.Result.Bundles.Test.HTTPCode != "403" { + t.Fatal("expected HTTPCode to equal 403 but got:", resp2) + } +} + +func TestStatusV1MetricsWithSystemAuthzPolicy(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + // Add the authz policy + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + authzPolicy := `package system.authz + import rego.v1 + + default allow = false + allow if { + input.path = ["v1", "status"] + }` + + if err := store.UpsertPolicy(ctx, txn, "test", []byte(authzPolicy)); err != nil { + t.Fatal(err) + } + + if err := store.Commit(ctx, txn); err != nil { + t.Fatal(err) + } + + // Add Prometheus Registerer to be used by plugins + inner := metrics.New() + + logger := func(logger logging.Logger) func(attrs map[string]any, f string, a ...any) { + return func(attrs map[string]any, f string, a ...any) { + logger.WithFields(attrs).Error(f, a...) + } + }(logging.NewNoOpLogger()) + + prom := prometheus.New(inner, logger, []float64{1e-6, 5e-6, 1e-5, 5e-5, 1e-4, 5e-4, 1e-3, 0.01, 0.1, 1}) + serverOpts := []any{func(s *Server) { s.WithAuthorization(AuthorizationBasic) }, func(s *Server) { s.WithMetrics(prom) }} + + f := newFixtureWithStore(t, store, serverOpts...) + + // Expect HTTP 500 before status plugin is registered + req := newReqV1(http.MethodGet, "/status", "") + f.server.Handler.ServeHTTP(f.recorder, req) + + if f.recorder.Result().StatusCode != http.StatusInternalServerError { + t.Fatal("expected internal error") + } + + // Register Status plugin + manual := plugins.TriggerManual + bs := pluginStatus.New(&pluginStatus.Config{ + Trigger: &manual, + Prometheus: true, + PrometheusConfig: &pluginStatus.PrometheusConfig{ + Collectors: &pluginStatus.Collectors{ + BundleLoadDurationNanoseconds: &pluginStatus.BundleLoadDurationNanoseconds{ + Buckets: []float64{1, 1000, 10_000, 1e8}, + }, + }, + }, + }, f.server.manager).WithMetrics(prom) + err := bs.Start(context.Background()) + if err != nil { + t.Fatal(err) + } + f.server.manager.Register(pluginStatus.Name, bs) + + // Fetch the status info, wait for status plugin to be ok + t0 := time.Now() + ok := false + for !ok && time.Since(t0) < time.Second { + req = newReqV1(http.MethodGet, "/status", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + if f.recorder.Result().StatusCode != http.StatusOK { + t.Fatal("expected ok") + } + + var resp1 struct { + Result struct { + Plugins struct { + Status struct { + State string + } + } + } + } + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&resp1); err != nil { + t.Fatal(err) + } + if resp1.Result.Plugins.Status.State == "OK" { + ok = true + } else { + t.Log("expected plugin state for status to be 'OK' but got:", resp1) + } + } + + // Make requests that should get denied + req = newReqV1(http.MethodGet, "/policies", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + if f.recorder.Result().StatusCode != http.StatusUnauthorized { + t.Fatalf("Expected success but got %v", f.recorder) + } + + req = newReqV1(http.MethodGet, "/data", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + if f.recorder.Result().StatusCode != http.StatusUnauthorized { + t.Fatalf("Expected success but got %v", f.recorder) + } + + // Check Prometheus status metrics in the Status API + + req = newReqV1(http.MethodGet, "/status", "") + f.reset() + f.server.Handler.ServeHTTP(f.recorder, req) + + if f.recorder.Result().StatusCode != http.StatusOK { + t.Fatal("expected ok") + } + + var resp struct { + Result struct { + Plugins struct { + Status struct { + State string + } + } + Metrics map[string]any + } + } + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&resp); err != nil { + t.Fatal(err) + } else if resp.Result.Plugins.Status.State != "OK" { + t.Fatal("expected plugin state for status to be 'OK' but got:", resp) + } + + met, ok := resp.Result.Metrics["prometheus"] + if !ok { + t.Fatal("expected prometheus metrics to be present in status") + } + + promMet, ok := met.(map[string]any) + if !ok { + t.Fatal("expected prometheus metrics to be a map") + } + + httpMet, ok := promMet["http_request_duration_seconds"].(map[string]any) + if !ok { + t.Fatal("expected http_request_duration_seconds metric to be a map") + } + + innerMet, ok := httpMet["metric"].([]any) + if !ok { + t.Fatal("expected http_request_duration_seconds histogram metric to be a list") + } + + expected := []any{map[string]any{"name": "code", "value": "401"}, + map[string]any{"name": "handler", "value": "authz"}, + map[string]any{"name": "method", "value": "get"}} + + found := false + for _, m := range innerMet { + item, ok := m.(map[string]any) + if ok { + if reflect.DeepEqual(item["label"].([]any), expected) { + found = true + break + } + } else { + t.Fatal("expected each http_request_duration_seconds histogram metric element to be a map") + } + } + + if !found { + t.Fatalf("expected to find metrics %v but found no match", expected) + } +} + +func TestQueryPostBasic(t *testing.T) { + t.Parallel() + + f := newFixture(t) + f.server, _ = New(). + WithAddresses([]string{"localhost:8182"}). + WithStore(f.server.store). + WithManager(f.server.manager). + Init(context.Background()) + + setup := []tr{ + {http.MethodPost, "/query", `{"query": "a=data.k.x with data.k as {\"x\" : 7}"}`, 200, `{"result":[{"a":7}]}`}, + {http.MethodPost, "/query", `{"query": "input=x", "input": 7}`, 200, `{"result":[{"x":7}]}`}, + {http.MethodPost, "/query", `{"query": "input=x", "input": @}`, 400, ``}, + } + + for _, tr := range setup { + req := newReqV1(tr.method, tr.path, tr.body) + req.RemoteAddr = "testaddr" + + if err := f.executeRequest(req, tr.code, tr.resp); err != nil { + t.Fatal(err) + } + } +} + +func TestDecisionIDs(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + ids := []string{} + ctr := 0 + + f.server = f.server.WithDecisionLoggerWithErr(func(_ context.Context, info *Info) error { + ids = append(ids, info.DecisionID) + return nil + }).WithDecisionIDFactory(func() string { + ctr++ + return strconv.Itoa(ctr) + }) + + if err := f.v1("GET", "/data/undefined", "", 200, `{"decision_id": "1"}`); err != nil { + t.Fatal(err) + } + + if err := f.v1("POST", "/data/undefined", "", 200, `{ + "decision_id": "2", + "warning": { + "code": "api_usage_warning", + "message": "'input' key missing from the request" + } + }`); err != nil { + t.Fatal(err) + } + + if err := f.v1("GET", "/data", "", 200, `{"decision_id": "3", "result": {}}`); err != nil { + t.Fatal(err) + } + + if err := f.v1("POST", "/data", "", 200, `{ + "decision_id": "4", + "result": {}, + "warning": { + "code": "api_usage_warning", + "message": "'input' key missing from the request" + } + }`); err != nil { + t.Fatal(err) + } + + exp := []string{"1", "2", "3", "4"} + + if !reflect.DeepEqual(ids, exp) { + t.Fatalf("Expected %v but got %v", exp, ids) + } +} + +func TestDecisionLoggingWithHTTPRequestContext(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + decisions := []*Info{} + + var nextID int + + f.server = f.server.WithDecisionIDFactory(func() string { + nextID++ + return strconv.Itoa(nextID) + }).WithDecisionLoggerWithErr(func(_ context.Context, info *Info) error { + decisions = append(decisions, info) + return nil + }) + + req := newReqV1("POST", "/data/nonexistent", `{"input": {"foo": 1}}`) + req.Header.Set("foo", "bar") + req.Header.Set("foo2", "bar2") + req.Header.Add("foo2", "bar3") + + httpRctx := logging.HTTPRequestContext{Header: req.Header.Clone()} + + req = req.WithContext(logging.WithHTTPRequestContext(req.Context(), &httpRctx)) + + if err := f.executeRequest(req, http.StatusOK, `{"decision_id": "1"}`); err != nil { + t.Fatal(err) + } + + if len(decisions) != 1 { + t.Fatalf("Expected exactly 1 decision but got: %d", len(decisions)) + } + + expHeaders := http.Header{} + expHeaders.Set("foo", "bar") + expHeaders.Add("foo2", "bar2") + expHeaders.Add("foo2", "bar3") + + exp := logging.HTTPRequestContext{Header: expHeaders} + + if !reflect.DeepEqual(decisions[0].HTTPRequestContext, exp) { + t.Fatalf("Expected HTTP request context %v but got: %v", exp, decisions[0].HTTPRequestContext) + } +} + +func TestDecisionLogging(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + decisions := []*Info{} + + var nextID int + + f.server = f.server.WithDecisionIDFactory(func() string { + nextID++ + return strconv.Itoa(nextID) + }).WithDecisionLoggerWithErr(func(_ context.Context, info *Info) error { + if info.Path == "fail_closed/decision_logger_err" { + return errors.New("some error") + } + decisions = append(decisions, info) + return nil + }) + + reqs := []struct { + raw *http.Request + v0 bool + method string + path string + body string + code int + response string + }{ + { + method: "PUT", + path: "/policies/test", + body: "package system\nmain=true", + response: "{}", + }, + { + method: "POST", + path: "/data", + response: `{ + "result": {}, + "warning": { + "code": "api_usage_warning", + "message": "'input' key missing from the request" + }, + "decision_id": "1" + }`, + }, + { + method: "GET", + path: "/data", + response: `{"result": {}, "decision_id": "2"}`, + }, + { + method: "POST", + path: "/data/nonexistent", + body: `{"input": {"foo": 1}}`, + response: `{"decision_id": "3"}`, + }, + { + method: "POST", + v0: true, + path: "/data", + response: `{}`, + }, + { + raw: newReqUnversioned("POST", "/", ""), + response: "true", + }, + { + method: "GET", + path: "/query?q=data=x", + response: `{"result": [{"x": {}}]}`, + }, + { + method: "POST", + path: "/query", + body: `{"query": "data=x"}`, + response: `{"result": [{"x": {}}]}`, + }, + { + method: "PUT", + path: "/policies/test2", + body: `package foo + import rego.v1 + p if { {k: v | k = ["a", "a"][_]; v = [1, 2][_]} }`, + response: `{}`, + }, + { + method: "PUT", + path: "/policies/test", + body: `package system + import rego.v1 + main if { data.foo.p }`, + response: `{}`, + }, + { + method: "POST", + path: "/data", + code: 500, + }, + { + method: "GET", + path: "/data", + code: 500, + }, + { + raw: newReqUnversioned("POST", "/", ""), + code: 500, + }, + { + method: "POST", + path: "/data/fail_closed/decision_logger_err", + code: 500, + }, + { + method: "POST", + v0: true, + path: "/data/test", + code: 404, + response: `{ + "code": "undefined_document", + "message": "document missing: data.test" + }`, + }, + } + + for _, r := range reqs { + code := r.code + if code == 0 { + code = http.StatusOK + } + if r.raw != nil { + if err := f.executeRequest(r.raw, code, r.response); err != nil { + t.Fatal(err) + } + } else if r.v0 { + if err := f.v0(r.method, r.path, r.body, code, r.response); err != nil { + t.Fatal(err) + } + } else { + if err := f.v1(r.method, r.path, r.body, code, r.response); err != nil { + t.Fatal(err) + } + } + } + + exp := []struct { + input string + path string + query string + wantErr bool + }{ + {path: ""}, + {path: ""}, + {path: "nonexistent", input: `{"foo": 1}`}, + {path: ""}, + {path: "system/main"}, + {query: "data = x"}, + {query: "data = x"}, + {path: "", wantErr: true}, + {path: "", wantErr: true}, + {path: "system/main", wantErr: true}, + {path: `test`, wantErr: true}, + } + + if len(decisions) != len(exp) { + t.Fatalf("Expected exactly %d decisions but got: %d", len(exp), len(decisions)) + } + + for i, d := range decisions { + if d.DecisionID == "" { + t.Fatalf("Expected decision ID on decision %d but got: %v", i, d) + } + if d.Metrics.Timer(metrics.ServerHandler).Value() == 0 { + t.Fatalf("Expected server handler timer to be started on decision %d but got %v", i, d) + } + if exp[i].path != d.Path || exp[i].query != d.Query { + t.Fatalf("Unexpected path or query on %d, want: %v but got: %v", i, exp[i], d) + } + if exp[i].wantErr && d.Error == nil || !exp[i].wantErr && d.Error != nil { + t.Fatalf("Unexpected error on %d, wantErr: %v, got: %v", i, exp[i].wantErr, d) + } + if exp[i].input != "" { + input := util.MustUnmarshalJSON([]byte(exp[i].input)) + if d.Input == nil || !reflect.DeepEqual(input, *d.Input) { + t.Fatalf("Unexpected input on %d, want: %v, but got: %v", i, exp[i], d) + } + } + } + +} + +func TestDecisionLogErrorMessage(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + f.server.WithDecisionLoggerWithErr(func(context.Context, *Info) error { + return errors.New("xxx") + }) + + if err := f.v1(http.MethodPost, "/data", "", 500, `{ + "code": "internal_error", + "message": "decision_logs: xxx" + }`); err != nil { + t.Fatal(err) + } +} + +func TestQueryV1(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + regoVersion ast.RegoVersion + query string + expErr bool + }{ + { + note: "v0", + regoVersion: ast.RegoV0, + query: "a=[1,2,3]%3Ba[i]=x", + }, + { + note: "v0, v1 keywords in query", + regoVersion: ast.RegoV0, + query: "a=[1,2,3]%3Bsome+i,+x+in+a", + expErr: true, + }, + { + note: "v1", + regoVersion: ast.RegoV1, + query: "a=[1,2,3]%3Bsome+i,+x+in+a", + }, + { + note: "default rego-version", // v1 + query: "a=[1,2,3]%3Bsome+i,+x+in+a", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + test.WithTempFS(nil, func(root string) { + disk, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{Dir: root}) + if err != nil { + t.Fatal(err) + } + defer disk.Close(ctx) + + var opts []any + if tc.regoVersion != ast.RegoUndefined { + opts = append(opts, plugins.WithParserOptions(ast.ParserOptions{RegoVersion: tc.regoVersion})) + } + + f := newFixtureWithStore(t, disk, opts...) + get := newReqV1(http.MethodGet, fmt.Sprintf(`/query?q=%s&metrics`, tc.query), "") + f.server.Handler.ServeHTTP(f.recorder, get) + + if tc.expErr { + if f.recorder.Code != 400 { + t.Fatalf("Expected error but got %v", f.recorder) + } + } else { + if f.recorder.Code != 200 { + t.Fatalf("Expected success but got %v", f.recorder) + } + + var expected types.QueryResponseV1 + err = util.UnmarshalJSON([]byte(`{ + "result": [{"a":[1,2,3],"i":0,"x":1},{"a":[1,2,3],"i":1,"x":2},{"a":[1,2,3],"i":2,"x":3}] + }`), &expected) + if err != nil { + panic(err) + } + + var result types.QueryResponseV1 + err = util.UnmarshalJSON(f.recorder.Body.Bytes(), &result) + if err != nil { + t.Fatalf("Unexpected error while unmarshalling result: %v", err) + } + + assertMetricsExist(t, result.Metrics, []string{ + "counter_disk_read_keys", + "timer_rego_query_compile_ns", + "timer_rego_query_eval_ns", + // "timer_server_handler_ns", // TODO(sr): we're not consistent about timing this? + "timer_disk_read_ns", + }) + + result.Metrics = nil + if !reflect.DeepEqual(result, expected) { + t.Fatalf("Expected:\n\n%v\n\nbut got:\n\n%v", expected, result) + } + } + }) + }) + } +} + +func TestBadQueryV1(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + expectedErr := `{ + "code": "invalid_parameter", + "message": "error(s) occurred while parsing query", + "errors": [ + { + "code": "rego_parse_error", + "message": "illegal token", + "location": { + "file": "", + "row": 1, + "col": 1 + }, + "details": { + "line": "^ -i", + "idx": 0 + } + } + ] +}` + + if err := f.v1(http.MethodGet, `/query?q=^ -i`, "", 400, expectedErr); err != nil { + recvErr := f.recorder.Body.String() + t.Fatalf(`Expected %v but got: %v`, expectedErr, recvErr) + } +} + +func TestQueryV1UnsafeBuiltin(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + query := `/query?q=http.send({"method": "get", "url": "foo.com"}, x)` + + expected := `{ + "code": "invalid_parameter", + "message": "error(s) occurred while compiling query", + "errors": [ + { + "code": "rego_type_error", + "message": "unsafe built-in function calls in expression: http.send", + "location": { + "file": "", + "row": 1, + "col": 1 + } + } + ] +}` + + if err := f.v1(http.MethodGet, query, "", 400, expected); err != nil { + t.Fatalf(`Expected %v but got: %v`, expected, f.recorder.Body.String()) + } +} + +func TestUnversionedPost(t *testing.T) { + t.Parallel() + + f := newFixture(t) + + post := func() *http.Request { + return newReqUnversioned(http.MethodPost, "/", ` + { + "foo": { + "bar": [1,2,3] + } + }`) + } + + f.server.Handler.ServeHTTP(f.recorder, post()) + + if f.recorder.Code != 404 { + t.Fatalf("Expected not found before policy added but got %v", f.recorder) + } + + expectedBody := `{ + "code": "undefined_document", + "message": "document missing: data.system.main" +} +` + if f.recorder.Body.String() != expectedBody { + t.Errorf("Expected %s got %s", expectedBody, f.recorder.Body.String()) + } + + module := ` + package system.main + import rego.v1 + + agg = x if { + sum(input.foo.bar, x) + } + ` + + if err := f.v1("PUT", "/policies/test", module, 200, ""); err != nil { + t.Fatal(err) + } + + f.reset() + f.server.Handler.ServeHTTP(f.recorder, post()) + + expected := "{\"agg\":6}\n" + if f.recorder.Code != 200 || f.recorder.Body.String() != expected { + t.Fatalf(`Expected HTTP 200 / %v but got: %v`, expected, f.recorder) + } + + module = ` + package system + import rego.v1 + + main if { + input.foo == "bar" + } + ` + + if err := f.v1("PUT", "/policies/test", module, 200, ""); err != nil { + t.Fatal(err) + } + + f.reset() + f.server.Handler.ServeHTTP(f.recorder, func() *http.Request { + return newReqUnversioned(http.MethodPost, "/", `{"input": {"foo": "bar"}}`) + }()) + + if f.recorder.Code != 404 { + t.Fatalf("Expected not found before policy added but got %v", f.recorder) + } + + expectedBody = `{ + "code": "undefined_document", + "message": "document undefined: data.system.main" +} +` + if f.recorder.Body.String() != expectedBody { + t.Errorf("Expected %s got %s", expectedBody, f.recorder.Body.String()) + } + + // update the default decision path + s := "http/authz" + f.server.manager.Config.DefaultDecision = &s + + f.reset() + f.server.Handler.ServeHTTP(f.recorder, post()) + + if f.recorder.Code != 404 { + t.Fatalf("Expected not found before policy added but got %v", f.recorder) + } + + expectedBody = `{ + "code": "undefined_document", + "message": "document missing: data.http.authz" +} +` + if f.recorder.Body.String() != expectedBody { + t.Fatalf("Expected %s got %s", expectedBody, f.recorder.Body.String()) + } + + module = ` + package http.authz + import rego.v1 + + agg = x if { + sum(input.foo.bar, x) + } + ` + + if err := f.v1("PUT", "/policies/test", module, 200, ""); err != nil { + t.Fatal(err) + } + + f.reset() + f.server.Handler.ServeHTTP(f.recorder, post()) + + expected = "{\"agg\":6}\n" + if f.recorder.Code != 200 || f.recorder.Body.String() != expected { + t.Fatalf(`Expected HTTP 200 / %v but got: %v`, expected, f.recorder) + } +} + +func TestQueryV1Explain(t *testing.T) { + t.Parallel() + + f := newFixture(t) + get := newReqV1(http.MethodGet, `/query?q=a=[1,2,3]%3Ba[i]=x&explain=debug`, "") + f.server.Handler.ServeHTTP(f.recorder, get) + + if f.recorder.Code != 200 { + t.Fatalf("Expected 200 but got: %v", f.recorder) + } + + var result types.QueryResponseV1 + + if err := util.NewJSONDecoder(f.recorder.Body).Decode(&result); err != nil { + t.Fatalf("Unexpected JSON decode error: %v", err) + } + + nexpect := 21 + explain := mustUnmarshalTrace(result.Explanation) + if len(explain) != nexpect { + t.Fatalf("Expected exactly %d trace events for full query but got %d", nexpect, len(explain)) + } +} + +func TestAuthorization(t *testing.T) { + t.Parallel() + + ctx := context.Background() + store := inmem.New() + m, err := plugins.New([]byte{}, "test", store) + if err != nil { + panic(err) + } + + if err := m.Start(ctx); err != nil { + panic(err) + } + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + authzPolicy := `package system.authz + + import rego.v1 + import input.identity + + default allow = false + + allow if { + identity = "bob" + } + ` + + if err := store.UpsertPolicy(ctx, txn, "test", []byte(authzPolicy)); err != nil { + panic(err) + } + + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + + server, err := New(). + WithAddresses([]string{"localhost:8182"}). + WithStore(store). + WithManager(m). + WithAuthorization(AuthorizationBasic). + Init(ctx) + + if err != nil { + panic(err) + } + + // Test that bob can do stuff. + req1, err := http.NewRequest(http.MethodGet, "http://localhost:8182/health", nil) + if err != nil { + panic(err) + } + + req1 = identifier.SetIdentity(req1, "bob") + + validateAuthorizedRequest(t, server, req1, http.StatusOK) + + // Test that alice can't do stuff. + req2, err := http.NewRequest(http.MethodGet, "http://localhost:8182/health", nil) + if err != nil { + panic(err) + } + + req2 = identifier.SetIdentity(req2, "alice") + + validateAuthorizedRequest(t, server, req2, http.StatusUnauthorized) + + // Reverse the policy. + update := identifier.SetIdentity(newReqV1(http.MethodPut, "/policies/test", ` + package system.authz + + import rego.v1 + import input.identity + + default allow = false + + allow if { + identity = "alice" + } + `), "bob") + + recorder := httptest.NewRecorder() + server.Handler.ServeHTTP(recorder, update) + if recorder.Code != http.StatusOK { + t.Fatalf("Expected policy update to succeed but got: %v", recorder) + } + + // Try alice again. + server.Handler.ServeHTTP(recorder, req2) + validateAuthorizedRequest(t, server, req2, http.StatusOK) + + // Try bob again. + server.Handler.ServeHTTP(recorder, req1) + validateAuthorizedRequest(t, server, req1, http.StatusUnauthorized) + + // Try to query for "data" as alice (allowed) + req3, err := http.NewRequest(http.MethodPost, "http://localhost:8182/v1/data", bytes.NewBufferString(`{"input": {"foo": "bar"}}`)) + if err != nil { + panic(err) + } + + req3 = identifier.SetIdentity(req3, "alice") + recorder = httptest.NewRecorder() + server.Handler.ServeHTTP(recorder, req3) + if recorder.Code != http.StatusOK { + t.Fatal("expected successful response for data") + } + + // Try to query for "data" as bob (denied) + req4, err := http.NewRequest(http.MethodPost, "http://localhost:8182/v1/data", bytes.NewBufferString(`{"input": {"foo": "bar"}}`)) + if err != nil { + panic(err) + } + + req4 = identifier.SetIdentity(req4, "bob") + recorder = httptest.NewRecorder() + server.Handler.ServeHTTP(recorder, req4) + if recorder.Code != http.StatusUnauthorized { + t.Fatal("expected unauthorized response for data") + } +} + +func TestAuthorizationUsesInterQueryCache(t *testing.T) { + t.Parallel() + + ctx := context.Background() + store := inmem.New() + m, err := plugins.New([]byte{}, "test", store) + if err != nil { + panic(err) + } + + if err := m.Start(ctx); err != nil { + panic(err) + } + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + var c uint64 + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + atomic.AddUint64(&c, 1) + fmt.Fprintf(w, `{"count": %d}`, c) + })) + + authzPolicy := fmt.Sprintf(`package system.authz +import rego.v1 + +default allow := false + +allow if { + resp := http.send({ + "method": "GET", "url": "%[1]s/foo", + "force_cache": true, + "force_json_decode": true, + "force_cache_duration_seconds": 60, + }) + + resp.body.count == 1 +} +`, ts.URL) + t.Log(authzPolicy) + + if err := store.UpsertPolicy(ctx, txn, "test", []byte(authzPolicy)); err != nil { + t.Fatal(err) + } + + if err := store.Commit(ctx, txn); err != nil { + t.Fatal(err) + } + + server, err := New(). + WithAddresses([]string{"localhost:8182"}). + WithStore(store). + WithManager(m). + WithAuthorization(AuthorizationBasic). + Init(ctx) + + if err != nil { + t.Fatal(err) + } + + for range 5 { + req1, err := http.NewRequest(http.MethodGet, "http://localhost:8182/health", nil) + if err != nil { + t.Fatal(err) + } + + validateAuthorizedRequest(t, server, req1, http.StatusOK) + } +} + +func validateAuthorizedRequest(t *testing.T, s *Server, req *http.Request, exp int) { + t.Helper() + + r := httptest.NewRecorder() + + // First check the main router + s.Handler.ServeHTTP(r, req) + if r.Code != exp { + t.Errorf("(Default Handler) Expected %v but got: %v", exp, r) + } + + r = httptest.NewRecorder() + + // Ensure that auth happens for the diagnostic handler as well + s.DiagnosticHandler.ServeHTTP(r, req) + if r.Code != exp { + t.Errorf("(Diagnostic Handler) Expected %v but got: %v", exp, r) + } +} + +func TestServerUsesAuthorizerParsedBody(t *testing.T) { + t.Parallel() + + // Construct a request w/ a different message body (this should never happen.) + req, err := http.NewRequest(http.MethodPost, "http://localhost:8182/v1/data/test/echo", bytes.NewBufferString(`{"foo": "bad"}`)) + if err != nil { + t.Fatal(err) + } + + // Set the authorizer's parsed input to the expected message body. + ctx := authorizer.SetBodyOnContext(req.Context(), map[string]any{ + "input": map[string]any{ + "foo": "good", + }, + }) + + // Check that v1 reader function behaves correctly. + inp, goInp, err := readInputPostV1(req.WithContext(ctx)) + if err != nil { + t.Fatal(err) + } + + exp := ast.MustParseTerm(`{"foo": "good"}`) + + if exp.Value.Compare(inp) != 0 { + t.Fatalf("expected %v but got %v", exp, inp) + } + + if exp.Value.Compare(ast.MustInterfaceToValue(*goInp)) != 0 { + t.Fatalf("expected %v but got %v", exp, *goInp) + } + + // Check that v0 reader function behaves correctly. + ctx = authorizer.SetBodyOnContext(req.Context(), map[string]any{ + "foo": "good", + }) + + inp, goInp, err = readInputV0(req.WithContext(ctx)) + if err != nil { + t.Fatal(err) + } + + if exp.Value.Compare(inp) != 0 { + t.Fatalf("expected %v but got %v", exp, inp) + } + + if exp.Value.Compare(ast.MustInterfaceToValue(*goInp)) != 0 { + t.Fatalf("expected %v but got %v", exp, *goInp) + } +} + +func TestServerReloadTrigger(t *testing.T) { + t.Parallel() + + f := newFixture(t) + store := f.server.store + ctx := context.Background() + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + if err := store.UpsertPolicy(ctx, txn, "test", []byte("package test\np = 1")); err != nil { + panic(err) + } + if err := f.v1(http.MethodGet, "/data/test", "", 200, `{}`); err != nil { + t.Fatalf("Unexpected error from server: %v", err) + } + + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + if err := f.v1(http.MethodGet, "/data/test", "", 200, `{"result": {"p": 1}}`); err != nil { + t.Fatalf("Unexpected error from server: %v", err) + } +} + +func TestServerClearsCompilerConflictCheck(t *testing.T) { + t.Parallel() + + f := newFixture(t) + store := f.server.store + ctx := context.Background() + + // Make a new transaction + params := storage.WriteParams + params.Context = storage.NewContext() + txn := storage.NewTransactionOrDie(ctx, store, params) + + // Fresh compiler we will swap on the manager + c := ast.NewCompiler() + + // Add the policy we want to use + c.Compile(map[string]*ast.Module{"test": ast.MustParseModule("package test\np=1")}) + if len(c.Errors) > 0 { + t.Fatalf("Unexpected compile errors: %v", c.Errors) + } + + // Add in a "bad" conflict check + c = c.WithPathConflictsCheck(func(_ []string) (bool, error) { + t.Fatal("Conflict check should not have been called") + return false, nil + }) + + // Set the compiler on the transaction context and commit to trigger listeners + plugins.SetCompilerOnContext(params.Context, c) + + if err := store.UpsertPolicy(ctx, txn, "test", []byte("package test\np = 1")); err != nil { + panic(err) + } + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + + // internal helpers should now give the new compiler back + if f.server.getCompiler() != c { + t.Fatalf("Expected to get the updated compiler") + } +} + +type queryBindingErrStore struct { + storage.WritesNotSupported + storage.PolicyNotSupported +} + +func (*queryBindingErrStore) Read(_ context.Context, _ storage.Transaction, _ storage.Path) (any, error) { + return nil, errors.New("expected error") +} + +func (*queryBindingErrStore) ListPolicies(_ context.Context, _ storage.Transaction) ([]string, error) { + return nil, nil +} + +func (queryBindingErrStore) NewTransaction(_ context.Context, _ ...storage.TransactionParams) (storage.Transaction, error) { + return nil, nil +} + +func (queryBindingErrStore) Commit(_ context.Context, _ storage.Transaction) error { + return nil +} + +func (queryBindingErrStore) Abort(_ context.Context, _ storage.Transaction) { + +} + +func (queryBindingErrStore) Truncate(context.Context, storage.Transaction, storage.TransactionParams, storage.Iterator) error { + return nil +} + +func (queryBindingErrStore) Register(context.Context, storage.Transaction, storage.TriggerConfig) (storage.TriggerHandle, error) { + return nil, nil +} + +func (queryBindingErrStore) Unregister(context.Context, storage.Transaction, string) { + +} + +func TestQueryBindingIterationError(t *testing.T) { + t.Parallel() + + ctx := context.Background() + mock := &queryBindingErrStore{} + m, err := plugins.New([]byte{}, "test", mock) + if err != nil { + panic(err) + } + + server, err := New().WithStore(mock).WithManager(m).WithAddresses([]string{":8182"}).Init(ctx) + if err != nil { + panic(err) + } + recorder := httptest.NewRecorder() + + f := &fixture{ + server: server, + recorder: recorder, + t: t, + } + + get := newReqV1(http.MethodGet, `/query?q=a=data.foo.bar`, "") + f.server.Handler.ServeHTTP(f.recorder, get) + + if f.recorder.Code != 500 { + t.Fatalf("Expected 500 error due to unknown storage error but got: %v", f.recorder) + } + + var resultErr types.ErrorV1 + + if jsonErr := json.NewDecoder(f.recorder.Body).Decode(&resultErr); jsonErr != nil { + t.Fatal(jsonErr) + } + + if resultErr.Code != types.CodeInternal || resultErr.Message != "expected error" { + t.Fatal("unexpected response:", resultErr) + } +} + +const ( + testMod = `package a.b.c + +import rego.v1 +import data.x.y as z +import data.p + +q contains x if { p[x]; not r[x] } +r contains x if { z[x] = 4 }` +) + +type fixture struct { + server *Server + recorder *httptest.ResponseRecorder + t *testing.T +} + +func newFixture(t *testing.T, opts ...any) *fixture { + ctx := context.Background() + server := New(). + WithAddresses([]string{"localhost:8182"}). + WithStore(inmem.New()) // potentially overridden via opts + + for _, opt := range opts { + if opt, ok := opt.(func(*Server)); ok { + opt(server) + } + } + + var mOpts []func(*plugins.Manager) + for _, opt := range opts { + if opt, ok := opt.(func(*plugins.Manager)); ok { + mOpts = append(mOpts, opt) + } + } + + m, err := plugins.New([]byte{}, "test", server.store, mOpts...) + if err != nil { + t.Fatal(err) + } + server = server.WithManager(m) + if err := m.Start(ctx); err != nil { + t.Fatal(err) + } + server, err = server.Init(ctx) + if err != nil { + t.Fatal(err) + } + recorder := httptest.NewRecorder() + + return &fixture{ + server: server, + recorder: recorder, + t: t, + } +} + +func newFixtureWithConfig(t *testing.T, config string, opts ...func(*Server)) *fixture { + ctx := context.Background() + server := New(). + WithAddresses([]string{"localhost:8182"}). + WithStore(inmem.New()) // potentially overridden via opts + for _, opt := range opts { + opt(server) + } + + m, err := plugins.New([]byte(config), "test", server.store) + if err != nil { + t.Fatal(err) + } + server = server.WithManager(m) + if err := m.Start(ctx); err != nil { + t.Fatal(err) + } + server, err = server.Init(ctx) + if err != nil { + t.Fatal(err) + } + recorder := httptest.NewRecorder() + + return &fixture{ + server: server, + recorder: recorder, + t: t, + } +} + +func newFixtureWithStore(t *testing.T, store storage.Store, opts ...any) *fixture { + ctx := context.Background() + + var mOpts []func(*plugins.Manager) + for _, opt := range opts { + if opt, ok := opt.(func(*plugins.Manager)); ok { + mOpts = append(mOpts, opt) + } + } + + m, err := plugins.New([]byte{}, "test", store, mOpts...) + if err != nil { + panic(err) + } + + if err := m.Start(ctx); err != nil { + panic(err) + } + + server := New(). + WithAddresses([]string{"localhost:8182"}). + WithStore(store). + WithManager(m) + + for _, opt := range opts { + if opt, ok := opt.(func(*Server)); ok { + opt(server) + } + } + + server, err = server.Init(ctx) + if err != nil { + panic(err) + } + recorder := httptest.NewRecorder() + + return &fixture{ + server: server, + recorder: recorder, + t: t, + } +} + +func (f *fixture) v1TestRequests(trs []tr) error { + for i, tr := range trs { + if err := f.v1(tr.method, tr.path, tr.body, tr.code, tr.resp); err != nil { + return fmt.Errorf("error on test request #%d: %w", i+1, err) + } + } + return nil +} + +func (f *fixture) v1(method string, path string, body string, code int, resp string) error { + // All v1 API's should 404 for the diagnostic handler + if err := f.executeDiagnosticRequest(newReqV1(method, path, body), 404, ""); err != nil { + return err + } + + return f.executeRequest(newReqV1(method, path, body), code, resp) +} + +func (f *fixture) v0(method string, path string, body string, code int, resp string) error { + // All v0 API's should 404 for the diagnostic handler + if err := f.executeDiagnosticRequest(newReqV0(method, path, body), 404, ""); err != nil { + return err + } + + return f.executeRequest(newReqV0(method, path, body), code, resp) +} + +func (f *fixture) executeRequestForHandler(h http.Handler, req *http.Request, code int, resp string) error { + f.reset() + h.ServeHTTP(f.recorder, req) + if f.recorder.Code != code { + return fmt.Errorf("Expected code %v from %v %v but got: %+v", code, req.Method, req.URL, f.recorder) + } + if resp != "" { + body := f.recorder.Body.String() + if resp == body { + // Early return on exact match as we can avoid the cost of uunmarshalling + // both the expected and actual response in that case. This is particularly + // useful for benchmarks where you only want to measure server-sider handling. + return nil + } + + var result any + if err := util.UnmarshalJSON(f.recorder.Body.Bytes(), &result); err != nil { + return fmt.Errorf("Expected JSON response from %v %v but got: %v", req.Method, req.URL, f.recorder) + } + var expected any + if err := util.UnmarshalJSON([]byte(resp), &expected); err != nil { + panic(err) + } + if !reflect.DeepEqual(result, expected) { + a, err := json.MarshalIndent(expected, "", " ") + if err != nil { + panic(err) + } + b, err := json.MarshalIndent(result, "", " ") + if err != nil { + panic(err) + } + return fmt.Errorf("Expected JSON response from %v %v to equal:\n\n%s\n\nGot:\n\n%s", req.Method, req.URL, a, b) + } + } + return nil +} + +func (f *fixture) executeRequest(req *http.Request, code int, resp string) error { + return f.executeRequestForHandler(f.server.Handler, req, code, resp) +} + +func (f *fixture) executeDiagnosticRequest(req *http.Request, code int, resp string) error { + return f.executeRequestForHandler(f.server.DiagnosticHandler, req, code, resp) +} + +func (f *fixture) reset() { + f.recorder = httptest.NewRecorder() +} + +type variant struct { + name string + opts []any +} + +func executeRequests(t *testing.T, reqs []tr, variants ...variant) { + t.Helper() + + if len(variants) == 0 { + f := newFixture(t) + for i, req := range reqs { + if err := f.v1(req.method, req.path, req.body, req.code, req.resp); err != nil { + t.Errorf("Unexpected response on request %d: %v", i+1, err) + } + } + } + + for _, v := range variants { + t.Run(v.name, func(t *testing.T) { + f := newFixture(t, v.opts...) + for i, req := range reqs { + if err := f.v1(req.method, req.path, req.body, req.code, req.resp); err != nil { + t.Errorf("Unexpected response on request %d: %v", i+1, err) + } + } + }) + } +} + +// Runs through an array of test cases against the v0 REST API tree +func executeRequestsv0(t *testing.T, reqs []tr) { + t.Helper() + f := newFixture(t) + for i, req := range reqs { + if err := f.v0(req.method, req.path, req.body, req.code, req.resp); err != nil { + t.Errorf("Unexpected response on request %d: %v", i+1, err) + } + } +} + +func validateDiagnosticRequest(t *testing.T, f *fixture, req *http.Request, code int, resp string) { + t.Helper() + // diagnostic requests need to be available on both the normal handler and diagnostic handler + if err := f.executeRequest(req, code, resp); err != nil { + t.Errorf("Unexpected error for request %v: %s", req, err) + } + if err := f.executeDiagnosticRequest(req, code, resp); err != nil { + t.Errorf("Unexpected error for request %v: %s", req, err) + } +} + +func newPolicy(id, s string) types.PolicyV1 { + compiler := ast.NewCompiler() + parsed := ast.MustParseModule(s) + if compiler.Compile(map[string]*ast.Module{"": parsed}); compiler.Failed() { + panic(compiler.Errors) + } + mod := compiler.Modules[""] + return types.PolicyV1{ID: id, AST: mod, Raw: s} +} + +func newReqV1(method string, path string, body string) *http.Request { + return newReq(1, method, path, body) +} + +func newReqV0(method string, path string, body string) *http.Request { + return newReq(0, method, path, body) +} + +func newReq(version int, method, path, body string) *http.Request { + return newReqUnversioned(method, fmt.Sprintf("/v%d", version)+path, body) +} + +func newReqUnversioned(method, path, body string) *http.Request { + req, err := http.NewRequest(method, path, strings.NewReader(body)) + if err != nil { + panic(err) + } + return req +} + +func newStreamedReqV0(method string, path string, body io.Reader) *http.Request { + return newStreamedReq(0, method, path, body) +} + +func newStreamedReqV1(method string, path string, body io.Reader) *http.Request { + return newStreamedReq(1, method, path, body) +} + +func newStreamedReq(version int, method string, path string, body io.Reader) *http.Request { + return newStreamedReqUnversioned(method, fmt.Sprintf("/v%d", version)+path, body) +} + +func newStreamedReqUnversioned(method string, path string, body io.Reader) *http.Request { + req, err := http.NewRequest(method, path, body) + if err != nil { + panic(err) + } + return req +} + +func mustUnmarshalTrace(t types.TraceV1) (trace types.TraceV1Raw) { + if err := json.Unmarshal(t, &trace); err != nil { + panic("not reached") + } + return trace +} + +func TestShutdown(t *testing.T) { + t.Parallel() + + f := newFixture(t, func(s *Server) { + s.WithDiagnosticAddresses([]string{":8443"}) + }) + loops, err := f.server.Listeners() + if err != nil { + t.Errorf("unexpected error: %s", err.Error()) + } + + errc := make(chan error) + for _, loop := range loops { + go func(serverLoop func() error) { + errc <- serverLoop() + }(loop) + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(5)*time.Second) + defer cancel() + err = f.server.Shutdown(ctx) + if err != nil { + t.Errorf("unexpected error shutting down server: %s", err.Error()) + } +} + +func TestShutdownError(t *testing.T) { + t.Parallel() + + f := newFixture(t, func(s *Server) { + s.WithDiagnosticAddresses([]string{":8443"}) + }) + + errMsg := "failed to shutdown" + + // Add a mock httpListener to the server + m := &mockHTTPListener{ + shutdownHook: func() error { + return errors.New(errMsg) + }, + } + f.server.httpListeners = []httpListener{m} + + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(5)*time.Second) + defer cancel() + err := f.server.Shutdown(ctx) + if err == nil { + t.Error("expected an error shutting down server but err==nil") + } else if !strings.Contains(err.Error(), errMsg) { + t.Errorf("unexpected error shutting down server: %s", err.Error()) + } +} + +func TestShutdownMultipleErrors(t *testing.T) { + t.Parallel() + + f := newFixture(t, func(s *Server) { + s.WithDiagnosticAddresses([]string{":8443"}) + }) + + shutdownErrs := []error{errors.New("err1"), nil, errors.New("err3")} + + // Add mock httpListeners to the server + for _, err := range shutdownErrs { + m := &mockHTTPListener{} + if err != nil { + retVal := errors.New(err.Error()) + m.shutdownHook = func() error { + return retVal + } + } + f.server.httpListeners = append(f.server.httpListeners, m) + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(5)*time.Second) + defer cancel() + err := f.server.Shutdown(ctx) + if err == nil { + t.Fatal("expected an error shutting down server but err==nil") + } + + for _, expectedErr := range shutdownErrs { + if expectedErr != nil && !strings.Contains(err.Error(), expectedErr.Error()) { + t.Errorf("expected error message to contain '%s', full message: '%s'", expectedErr.Error(), err.Error()) + } + } +} + +func TestAddrsNoListeners(t *testing.T) { + t.Parallel() + + s := New() + a := s.Addrs() + if len(a) != 0 { + t.Errorf("expected an empty list of addresses, got: %+v", a) + } +} + +func TestAddrsWithEmptyListenAddr(t *testing.T) { + t.Parallel() + + s := New() + s.httpListeners = []httpListener{&mockHTTPListener{}} + a := s.Addrs() + if len(a) != 0 { + t.Errorf("expected an empty list of addresses, got: %+v", a) + } +} + +func TestAddrsWithListenAddr(t *testing.T) { + t.Parallel() + + s := New() + s.httpListeners = []httpListener{&mockHTTPListener{addrs: ":8181"}} + a := s.Addrs() + if len(a) != 1 || a[0] != ":8181" { + t.Errorf("expected only an ':8181' address, got: %+v", a) + } +} + +func TestAddrsWithMixedListenerAddr(t *testing.T) { + t.Parallel() + + s := New() + addrs := []string{":8181", "", "unix:///var/tmp/foo.sock"} + expected := []string{":8181", "unix:///var/tmp/foo.sock"} + + s.httpListeners = []httpListener{} + for _, addr := range addrs { + s.httpListeners = append(s.httpListeners, &mockHTTPListener{addrs: addr, t: defaultListenerType}) + } + + a := s.Addrs() + if len(a) != 2 { + t.Errorf("expected 2 addresses, got: %+v", a) + } + + for _, expectedAddr := range expected { + if !slices.Contains(a, expectedAddr) { + t.Errorf("expected %q in address list, got: %+v", expectedAddr, a) + } + } +} + +func TestDiagnosticAddrsNoListeners(t *testing.T) { + t.Parallel() + + s := New() + a := s.DiagnosticAddrs() + if len(a) != 0 { + t.Errorf("expected an empty list of addresses, got: %+v", a) + } +} + +func TestDiagnosticAddrsWithEmptyListenAddr(t *testing.T) { + t.Parallel() + + s := New() + s.httpListeners = []httpListener{&mockHTTPListener{t: diagnosticListenerType}} + a := s.DiagnosticAddrs() + if len(a) != 0 { + t.Errorf("expected an empty list of addresses, got: %+v", a) + } +} + +func TestDiagnosticAddrsWithListenAddr(t *testing.T) { + t.Parallel() + + s := New() + s.httpListeners = []httpListener{&mockHTTPListener{addrs: ":8181", t: diagnosticListenerType}} + a := s.DiagnosticAddrs() + if len(a) != 1 || a[0] != ":8181" { + t.Errorf("expected only an ':8181' address, got: %+v", a) + } +} + +func TestDiagnosticAddrsWithMixedListenerAddr(t *testing.T) { + t.Parallel() + + s := New() + addrs := []string{":8181", "", "unix:///var/tmp/foo.sock"} + expected := []string{":8181", "unix:///var/tmp/foo.sock"} + + s.httpListeners = []httpListener{} + for _, addr := range addrs { + s.httpListeners = append(s.httpListeners, &mockHTTPListener{addrs: addr, t: diagnosticListenerType}) + } + + a := s.DiagnosticAddrs() + if len(a) != 2 { + t.Errorf("expected 2 addresses, got: %+v", a) + } + + for _, expectedAddr := range expected { + if !slices.Contains(a, expectedAddr) { + t.Errorf("expected %q in address list, got: %+v", expectedAddr, a) + } + } +} + +func TestMixedAddrTypes(t *testing.T) { + t.Parallel() + + s := New() + + s.httpListeners = []httpListener{} + + addrs := map[string]struct{}{"localhost:8181": {}, "localhost:1234": {}, "unix:///var/tmp/foo.sock": {}} + for addr := range addrs { + s.httpListeners = append(s.httpListeners, &mockHTTPListener{addrs: addr, t: defaultListenerType}) + } + + diagAddrs := map[string]struct{}{":8181": {}, "https://127.0.0.1": {}} + for addr := range diagAddrs { + s.httpListeners = append(s.httpListeners, &mockHTTPListener{addrs: addr, t: diagnosticListenerType}) + } + + actualAddrs := s.Addrs() + if len(actualAddrs) != len(addrs) { + t.Errorf("expected %d addresses, got: %+v", len(addrs), actualAddrs) + } + + for _, addr := range actualAddrs { + if _, ok := addrs[addr]; !ok { + t.Errorf("Unexpected address %v", addr) + } + } + + actualDiagAddrs := s.DiagnosticAddrs() + if len(actualDiagAddrs) != len(diagAddrs) { + t.Errorf("expected %d addresses, got: %+v", len(diagAddrs), actualDiagAddrs) + } + + for _, addr := range actualDiagAddrs { + if _, ok := diagAddrs[addr]; !ok { + t.Errorf("Unexpected diagnostic address %v", addr) + } + } +} + +func TestCustomRoute(t *testing.T) { + t.Parallel() + + router := http.NewServeMux() + router.HandleFunc("GET /customEndpoint", func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte(`{"myCustomResponse": true}`)) // ignore error + }) + f := newFixture(t, func(server *Server) { + server.WithRouter(router) + }) + + if err := f.v1(http.MethodGet, "/data", "", 200, `{"result":{}}`); err != nil { + t.Fatalf("Unexpected response for default server route: %v", err) + } + r, err := http.NewRequest(http.MethodGet, "/customEndpoint", nil) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + if err := f.executeRequest(r, http.StatusOK, `{"myCustomResponse": true}`); err != nil { + t.Fatalf("Request to custom endpoint failed: %s", err) + } +} + +func TestCustomRouteWithMetrics(t *testing.T) { + t.Parallel() + + // Add Prometheus Registerer to be used by plugins + inner := metrics.New() + prom := prometheus.New(inner, nil, []float64{1}) + f := newFixture(t, + func(m *plugins.Manager) { + m.ExtraRoute("GET /v1/foo", "v1/foo", func(w http.ResponseWriter, _ *http.Request) { + fmt.Fprintln(w, `{"foo": "bar"}`) + }) + }, + func(s *Server) { + s.WithMetrics(prom) + }) + + { // existing APIs still work fine + if err := f.v1(http.MethodGet, "/data", "", 200, `{"result":{}}`); err != nil { + t.Fatalf("Unexpected response for default server route: %v", err) + } + } + { // new endpoint is wired up + r, err := http.NewRequest(http.MethodGet, "/v1/foo", nil) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + if err := f.executeRequest(r, http.StatusOK, `{"foo": "bar"}`); err != nil { + t.Fatalf("Request to custom endpoint failed: %s", err) + } + } + { // metrics are recorded for special endpoint + r, err := http.NewRequest(http.MethodGet, "/metrics", nil) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + f.reset() + f.server.DiagnosticHandler.ServeHTTP(f.recorder, r) + resp := f.recorder.Result() + body, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatalf("failed to read response body: %v", err) + } + resp.Body.Close() + + for _, want := range []string{ + `http_request_duration_seconds_count{code="200",handler="v1/data",method="get"} 1`, // default http handler + `http_request_duration_seconds_count{code="200",handler="v1/foo",method="get"} 1`, // added handler + `http_request_duration_seconds_bucket{code="200",handler="v1/foo",method="get",le="1"} 1`, + `http_request_duration_seconds_bucket{code="200",handler="v1/foo",method="get",le="+Inf"} 1`, + } { + if !strings.Contains(string(body), want) { + t.Errorf("expected response to contain metric %q, but it did not.\nBody:\n%s", want, string(body)) + } + } + } +} + +func TestDiagnosticRoutes(t *testing.T) { + t.Parallel() + + cases := []struct { + path string + should404 bool + }{ + {"/health", false}, + {"/metrics", false}, + {"/debug/pprof/", true}, + {"/v0/data", true}, + {"/v0/data/foo", true}, + {"/v1/data/", true}, + {"/v1/data/foo", true}, + {"/v1/policies", true}, + {"/v1/policies/foo", true}, + {"/v1/query", true}, + {"/v1/compile", true}, + {"/", true}, + } + + f := newFixture(t, func(s *Server) { + s.WithPprofEnabled(true) + s.WithMetrics(new(mockMetricsProvider)) + }) + + for _, tc := range cases { + t.Run(tc.path, func(t *testing.T) { + req, err := http.NewRequest("GET", tc.path, nil) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + code := http.StatusOK + if tc.should404 { + code = http.StatusNotFound + } + f.reset() + f.server.DiagnosticHandler.ServeHTTP(f.recorder, req) + if f.recorder.Code != code { + t.Errorf("Expected code %v from %v %v but got: %+v", code, req.Method, req.URL, f.recorder) + } + }) + } + +} + +func TestDistributedTracingEnabled(t *testing.T) { + t.Parallel() + + c := []byte(`{"distributed_tracing": { + "type": "grpc" + }}`) + + ctx := context.Background() + _, _, _, err := distributedtracing.Init(ctx, c, "foo") + if err != nil { + t.Fatalf("Unexpected error initializing gRPC trace exporter %v", err) + } + + c = []byte(`{"distributed_tracing": { + "type": "http" + }}`) + + _, _, _, err = distributedtracing.Init(ctx, c, "foo") + if err != nil { + t.Fatalf("Unexpected error initializing HTTP trace exporter %v", err) + } +} + +func TestDistributedTracingResourceAttributes(t *testing.T) { + t.Parallel() + + attributes := map[attribute.Key]string{ + semconv.DeploymentEnvironmentKey: "prod", + semconv.ServiceNameKey: "my-service", + semconv.ServiceVersionKey: "1.0", + semconv.ServiceNamespaceKey: "my-namespace", + semconv.ServiceInstanceIDKey: "1", + } + + c := fmt.Appendf(nil, `{"distributed_tracing": { + "type": "grpc", + "service_name": "%s", + "resource": { + "service_namespace": "%s", + "service_version": "%s", + "service_instance_id": "%s", + "deployment_environment": "%s" + } + }}`, attributes[semconv.ServiceNameKey], + attributes[semconv.ServiceNamespaceKey], + attributes[semconv.ServiceVersionKey], + attributes[semconv.ServiceInstanceIDKey], + attributes[semconv.DeploymentEnvironmentKey]) + + ctx := context.Background() + _, traceProvider, resource, err := distributedtracing.Init(ctx, c, "foo") + if err != nil { + t.Fatalf("Unexpected error initializing trace exporter %v", err) + } + if traceProvider == nil { + t.Fatalf("Tracer provider was not initialized") + } + if resource == nil { + t.Fatalf("Resource was not initialized") + } + if len(resource.Attributes()) != 5 { + t.Fatalf("Unexpected resource attributes count. Expected: %v, Got: %v", 5, len(resource.Attributes())) + } + + for _, value := range resource.Attributes() { + if attribute.StringValue(attributes[value.Key]) != value.Value { + t.Fatalf("Unexpected resource attribute. Expected: %v, Got: %v", attributes[value.Key], value) + } + } + +} + +func TestCertPoolReloading(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + tempDir := t.TempDir() + + serverCertPath := filepath.Join(tempDir, "serverCert.pem") + serverCertKeyPath := filepath.Join(tempDir, "serverCertKey.pem") + clientCertPath := filepath.Join(tempDir, "clientCert.pem") + clientCertKeyPath := filepath.Join(tempDir, "clientCertKey.pem") + caCertPath := filepath.Join(tempDir, "ca.pem") + + san := net.ParseIP("127.0.0.1") + + // create the CA cert used in the cert pool and for signing server certs + caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + + caSerial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + t.Fatal(err) + } + caSubj := pkix.Name{ + CommonName: "CA", + SerialNumber: caSerial.String(), + } + caTemplate := &x509.Certificate{ + BasicConstraintsValid: true, + SignatureAlgorithm: x509.ECDSAWithSHA256, + PublicKeyAlgorithm: x509.ECDSA, + PublicKey: caKey.Public(), + SerialNumber: caSerial, + Issuer: caSubj, + Subject: caSubj, + NotBefore: time.Now(), + NotAfter: time.Now().Add(100 * time.Hour * 24 * 365), + KeyUsage: x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + IsCA: true, + DNSNames: nil, + EmailAddresses: nil, + IPAddresses: nil, + } + + caCertData, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, caKey.Public(), caKey) + if err != nil { + t.Fatal(err) + } + + caCertPEMEncoded := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: caCertData, + }) + + // we write an empty file for now + err = os.WriteFile(caCertPath, []byte{}, 0o600) + if err != nil { + t.Fatal(err) + } + + // create a cert and key for the server to load at startup + var serverCert tls.Certificate + + serverCertKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + serverCert.PrivateKey = serverCertKey + + serverCertSerial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + t.Fatal(err) + } + serverCertTemplate := &x509.Certificate{ + BasicConstraintsValid: true, + SignatureAlgorithm: x509.ECDSAWithSHA256, + PublicKeyAlgorithm: x509.ECDSA, + PublicKey: serverCertKey.Public(), + SerialNumber: serverCertSerial, + Issuer: caSubj, + Subject: pkix.Name{ + CommonName: "Server 1", + SerialNumber: serverCertSerial.String(), + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(99 * time.Hour * 24 * 365), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + IsCA: false, + DNSNames: nil, + EmailAddresses: nil, + IPAddresses: []net.IP{san}, + } + + serverCertData, err := x509.CreateCertificate(rand.Reader, serverCertTemplate, caTemplate, serverCertKey.Public(), caKey) + if err != nil { + t.Fatal(err) + } + + serverCertPEMEncoded := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: serverCertData, + }) + + serverCertKeyMarshalled, _ := x509.MarshalPKCS8PrivateKey(serverCert.PrivateKey) + serverCertKeyPEMEncoded := pem.EncodeToMemory(&pem.Block{ + Type: "PRIVATE KEY", + Bytes: serverCertKeyMarshalled, + }) + + err = os.WriteFile(serverCertPath, serverCertPEMEncoded, 0o600) + if err != nil { + t.Fatal(err) + } + + err = os.WriteFile(serverCertKeyPath, serverCertKeyPEMEncoded, 0o600) + if err != nil { + t.Fatal(err) + } + + // create a cert and key for the client to test client auth + var clientCert tls.Certificate + + clientCertKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + clientCert.PrivateKey = clientCertKey + + clientCertSerial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + t.Fatal(err) + } + clientCertTemplate := &x509.Certificate{ + BasicConstraintsValid: true, + SignatureAlgorithm: x509.ECDSAWithSHA256, + PublicKeyAlgorithm: x509.ECDSA, + PublicKey: clientCertKey.Public(), + SerialNumber: clientCertSerial, + Issuer: caSubj, + Subject: pkix.Name{ + CommonName: "Client", + SerialNumber: clientCertSerial.String(), + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(99 * time.Hour * 24 * 365), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + } + + clientCertData, err := x509.CreateCertificate(rand.Reader, clientCertTemplate, caTemplate, clientCertKey.Public(), caKey) + if err != nil { + t.Fatal(err) + } + + clientCertPEMEncoded := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: clientCertData, + }) + + clientCertKeyMarshalled, _ := x509.MarshalPKCS8PrivateKey(clientCert.PrivateKey) + clientCertKeyPEMEncoded := pem.EncodeToMemory(&pem.Block{ + Type: "PRIVATE KEY", + Bytes: clientCertKeyMarshalled, + }) + + err = os.WriteFile(clientCertPath, clientCertPEMEncoded, 0o600) + if err != nil { + t.Fatal(err) + } + + err = os.WriteFile(clientCertKeyPath, clientCertKeyPEMEncoded, 0o600) + if err != nil { + t.Fatal(err) + } + + // configure the server to use the certs + initialCertPool := x509.NewCertPool() + ok := initialCertPool.AppendCertsFromPEM(caCertPEMEncoded) + if !ok { + t.Fatal("failed to add CA cert to cert pool") + } + + initialCert, err := tls.LoadX509KeyPair(serverCertPath, serverCertKeyPath) + if err != nil { + t.Fatal(err) + } + + listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Unexpected error creating listener while finding free port: %s", err) + } + + serverAddress := listener.Addr().String() + err = listener.Close() + if err != nil { + t.Fatalf("Unexpected error closing listener to free port: %s", err) + } + + t.Log("server address:", serverAddress) + + server := New(). + WithAddresses([]string{serverAddress}). + WithStore(inmem.New()). + WithCertificate(&initialCert). + WithCertPool(x509.NewCertPool()). // empty cert pool + WithAuthentication(AuthenticationTLS). + WithTLSConfig( + &TLSConfig{ + CertFile: serverCertPath, + KeyFile: serverCertKeyPath, + CertPoolFile: caCertPath, // currently empty + }, + ) + + // start the server referencing the certs + m, err := plugins.New([]byte{}, "test", server.store) + if err != nil { + t.Fatal(err) + } + server = server.WithManager(m) + if err = m.Start(ctx); err != nil { + t.Fatal(err) + } + server, err = server.Init(ctx) + if err != nil { + t.Fatal(err) + } + + loops, err := server.Listeners() + if err != nil { + t.Fatal(err) + } + + for _, loop := range loops { + go func(serverLoop func() error) { + errc := make(chan error) + errc <- serverLoop() + err := <-errc + t.Errorf("Unexpected error from server loop: %s", err) + }(loop) + } + + // wait for the server to start + retries := 10 + for { + if retries == 0 { + t.Fatal("failed to start server before deadline") + } + _, err = tls.Dial("tcp", serverAddress, &tls.Config{RootCAs: initialCertPool}) + if err != nil { + retries-- + time.Sleep(300 * time.Millisecond) + continue + } + t.Log("server started") + break + } + + // make the first request and check that the server is not trusting the client cert + clientKeyPair, err := tls.LoadX509KeyPair(clientCertPath, clientCertKeyPath) + if err != nil { + t.Fatal(err) + } + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + RootCAs: initialCertPool, + Certificates: []tls.Certificate{clientKeyPair}, + }, + }, + } + + // make a request and check that the server doesn't trust the client cert yet since it has no CA cert + retries = 10 + expectedError := "remote error: tls" + for { + if retries == 0 { + t.Fatal("server didn't return expected error before deadline") + } + + req, err := http.NewRequest("GET", fmt.Sprintf("https://%s/v1/data", serverAddress), nil) + if err != nil { + t.Fatal(err) + } + + _, err = client.Do(req) + if !strings.Contains(err.Error(), expectedError) { + t.Log("retrying, expected error:", expectedError, "but got:", err) + retries-- + time.Sleep(300 * time.Millisecond) + continue + } + + break + } + + // update the cert pool file to include the CA cert + err = os.WriteFile(caCertPath, caCertPEMEncoded, 0o600) + if err != nil { + t.Fatal(err) + } + + // make a second request and check that the server now trusts the client cert + retries = 10 + for { + if retries == 0 { + t.Fatal("server didn't accept client cert before deadline") + } + + req, err := http.NewRequest("GET", fmt.Sprintf("https://%s/v1/data", serverAddress), nil) + if err != nil { + t.Fatal(err) + } + + _, err = client.Do(req) + if err != nil { + t.Log("server still doesn't trust client cert") + retries-- + time.Sleep(300 * time.Millisecond) + continue + } + + break + } + + // update the cert pool file to a new & different CA that hasn't signed the client cert + caKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + + caSerial, err = rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + t.Fatal(err) + } + caSubj = pkix.Name{ + CommonName: "CA 2", + SerialNumber: caSerial.String(), + } + + caTemplate = &x509.Certificate{ + BasicConstraintsValid: true, + SignatureAlgorithm: x509.ECDSAWithSHA256, + PublicKeyAlgorithm: x509.ECDSA, + PublicKey: caKey.Public(), + SerialNumber: caSerial, + Issuer: caSubj, + Subject: caSubj, + NotBefore: time.Now(), + NotAfter: time.Now().Add(100 * time.Hour * 24 * 365), + KeyUsage: x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + IsCA: true, + DNSNames: nil, + EmailAddresses: nil, + IPAddresses: nil, + } + + caCertData, err = x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, caKey.Public(), caKey) + if err != nil { + t.Fatal(err) + } + + caCertPEMEncoded = pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: caCertData, + }) + + err = os.WriteFile(caCertPath, caCertPEMEncoded, 0o600) + if err != nil { + t.Fatal(err) + } + + // make a final request and check that the server doesn't trust the client again + // since the loaded CA cert is different from the one that signed the client cert + retries = 10 + for { + if retries == 0 { + t.Fatal("server didn't accept client cert before deadline") + } + + req, err := http.NewRequest("GET", fmt.Sprintf("https://%s/v1/data", serverAddress), nil) + if err != nil { + t.Fatal(err) + } + + _, err = client.Do(req) + if err == nil { + t.Log("server still trusts client cert") + retries-- + time.Sleep(300 * time.Millisecond) + continue + } + + if !strings.Contains(err.Error(), "remote error: tls") { + t.Fatalf("expected unknown certificate authority error (server has different CA) but got: %s", err) + } + + break + } + + err = server.Shutdown(ctx) + if err != nil { + t.Fatalf("Unexpected error shutting down server: %s", err) + } + +} + +func TestCertReloading(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + ctx := context.Background() + + testCases := map[string]struct { + Server func( + addr string, + initialCert *tls.Certificate, + initialCertPool *x509.CertPool, + certFilePath, keyFilePath, caCertPath string, + ) *Server + }{ + "fs notified server": { + Server: func( + addr string, + initialCert *tls.Certificate, + initialCertPool *x509.CertPool, + certFilePath, keyFilePath, caCertPath string, + ) *Server { + return New(). + WithAddresses([]string{addr}). + WithStore(inmem.New()). + WithCertificate(initialCert). + WithCertPool(initialCertPool). + WithTLSConfig( + &TLSConfig{ + CertFile: certFilePath, + KeyFile: keyFilePath, + CertPoolFile: caCertPath, + }, + ) + }, + }, + "interval reloaded server": { + Server: func( + addr string, + initialCert *tls.Certificate, + initialCertPool *x509.CertPool, + certFilePath, keyFilePath, _ string, + ) *Server { + return New(). + WithAddresses([]string{addr}). + WithStore(inmem.New()). + WithCertificate(initialCert). + WithCertPool(initialCertPool). + WithCertificatePaths( + certFilePath, + keyFilePath, + 1*time.Second, + ) + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + + tempDir := t.TempDir() + + serverCert1Path := filepath.Join(tempDir, "serverCert1.pem") + serverCert1KeyPath := filepath.Join(tempDir, "serverCert1Key.pem") + serverCert2Path := filepath.Join(tempDir, "serverCert2.pem") + serverCert2KeyPath := filepath.Join(tempDir, "serverCert2Key.pem") + caCertPath := filepath.Join(tempDir, "ca.pem") + + t.Helper() + + san := net.ParseIP("127.0.0.1") + + // create the CA cert used in the cert pool and for signing server certs + caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + + caSerial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + t.Fatal(err) + } + caSubj := pkix.Name{ + CommonName: "CA", + SerialNumber: caSerial.String(), + } + caTemplate := &x509.Certificate{ + BasicConstraintsValid: true, + SignatureAlgorithm: x509.ECDSAWithSHA256, + PublicKeyAlgorithm: x509.ECDSA, + PublicKey: caKey.Public(), + SerialNumber: caSerial, + Issuer: caSubj, + Subject: caSubj, + NotBefore: time.Now(), + NotAfter: time.Now().Add(100 * time.Hour * 24 * 365), + KeyUsage: x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + IsCA: true, + } + + caCertData, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, caKey.Public(), caKey) + if err != nil { + t.Fatal(err) + } + + caCertPEMEncoded := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: caCertData, + }) + + err = os.WriteFile(caCertPath, caCertPEMEncoded, 0o600) + if err != nil { + t.Fatal(err) + } + + // create a cert and key for the server to load at startup + var serverCert1 tls.Certificate + + serverCert1Key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + serverCert1.PrivateKey = serverCert1Key + + serverCert1Serial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + t.Fatal(err) + } + serverCert1Template := &x509.Certificate{ + BasicConstraintsValid: true, + SignatureAlgorithm: x509.ECDSAWithSHA256, + PublicKeyAlgorithm: x509.ECDSA, + PublicKey: serverCert1Key.Public(), + SerialNumber: serverCert1Serial, + Issuer: caSubj, + Subject: pkix.Name{ + CommonName: "Server 1", + SerialNumber: serverCert1Serial.String(), + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(99 * time.Hour * 24 * 365), + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + IPAddresses: []net.IP{san}, + } + + serverCert1Data2, err := x509.CreateCertificate(rand.Reader, serverCert1Template, caTemplate, serverCert1Key.Public(), caKey) + if err != nil { + t.Fatal(err) + } + + serverCert1PEMEncoded := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: serverCert1Data2, + }) + + serverCert1KeyMarshalled, _ := x509.MarshalPKCS8PrivateKey(serverCert1.PrivateKey) + serverCert1KeyPEMEncoded := pem.EncodeToMemory(&pem.Block{ + Type: "PRIVATE KEY", + Bytes: serverCert1KeyMarshalled, + }) + + err = os.WriteFile(serverCert1Path, serverCert1PEMEncoded, 0o600) + if err != nil { + t.Fatal(err) + } + + err = os.WriteFile(serverCert1KeyPath, serverCert1KeyPEMEncoded, 0o600) + if err != nil { + t.Fatal(err) + } + + // create a cert to load after startup + var serverCert2 tls.Certificate + + serverCert2Key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + serverCert2.PrivateKey = serverCert2Key + + serverCert2Serial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + t.Fatal(err) + } + serverCert1Template = &x509.Certificate{ + BasicConstraintsValid: true, + SignatureAlgorithm: x509.ECDSAWithSHA256, + PublicKeyAlgorithm: x509.ECDSA, + PublicKey: serverCert2Key.Public(), + SerialNumber: serverCert2Serial, + Issuer: caSubj, + Subject: pkix.Name{ + CommonName: "Server 2", + SerialNumber: serverCert1Serial.String(), + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(99 * time.Hour * 24 * 365), + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + IPAddresses: []net.IP{san}, + } + + serverCert2Data2, err := x509.CreateCertificate(rand.Reader, serverCert1Template, caTemplate, serverCert2Key.Public(), caKey) + if err != nil { + t.Fatal(err) + } + serverCert2.Certificate = [][]byte{serverCert2Data2, caCertData} + + serverCert2PEMEncoded := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: serverCert2Data2, + }) + + serverCert2KeyMarshalled, _ := x509.MarshalPKCS8PrivateKey(serverCert2.PrivateKey) + serverCert2KeyPEMEncoded := pem.EncodeToMemory(&pem.Block{ + Type: "PRIVATE KEY", + Bytes: serverCert2KeyMarshalled, + }) + + err = os.WriteFile(serverCert2Path, serverCert2PEMEncoded, 0o600) + if err != nil { + t.Fatal(err) + } + + err = os.WriteFile(serverCert2KeyPath, serverCert2KeyPEMEncoded, 0o600) + if err != nil { + t.Fatal(err) + } + + certPool2 := x509.NewCertPool() + ok := certPool2.AppendCertsFromPEM(caCertPEMEncoded) + if !ok { + t.Fatal("failed to add CA cert to cert pool") + } + certPool, _, _, serverCert1Data, serverCert2Data := certPool2, &serverCert1, &serverCert2, serverCert1Data2, serverCert2Data2 + + initialCert, err := tls.LoadX509KeyPair(serverCert1Path, serverCert1KeyPath) + if err != nil { + t.Fatal(err) + } + + listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Unexpected error creating listener while finding free port: %s", err) + } + + serverAddress := listener.Addr().String() + err = listener.Close() + if err != nil { + t.Fatalf("Unexpected error closing listener to free port: %s", err) + } + + t.Log("server address:", serverAddress) + + server := tc.Server(serverAddress, &initialCert, certPool, serverCert1Path, serverCert1KeyPath, caCertPath) + + // start the server referencing the certs + m, err := plugins.New([]byte{}, "test", server.store) + if err != nil { + t.Fatal(err) + } + server = server.WithManager(m) + if err = m.Start(ctx); err != nil { + t.Fatal(err) + } + server, err = server.Init(ctx) + if err != nil { + t.Fatal(err) + } + + loops, err := server.Listeners() + if err != nil { + t.Fatal(err) + } + + for _, loop := range loops { + go func(serverLoop func() error) { + errc := make(chan error) + errc <- serverLoop() + err := <-errc + t.Errorf("Unexpected error from server loop: %s", err) + }(loop) + } + + // wait for the server to start + retries := 10 + for { + if retries == 0 { + t.Fatal("failed to start server before deadline") + } + _, err = tls.Dial("tcp", serverAddress, &tls.Config{RootCAs: certPool}) + if err != nil { + retries-- + time.Sleep(300 * time.Millisecond) + continue + } + t.Log("server started") + break + } + + // make the first connection, check that the server 1 cert is returned + retries = 10 + for { + if retries == 0 { + t.Fatal("failed to get serverCert1 before deadline") + } + conn, err := tls.Dial("tcp", serverAddress, &tls.Config{RootCAs: certPool}) + if err != nil { + t.Fatal(err) + } + err = conn.Close() + if err != nil { + t.Fatal(err) + } + + certs := conn.ConnectionState().PeerCertificates + if len(certs) != 1 { + t.Fatalf("expected 1 cert, got %d", len(certs)) + } + + servedCert := certs[0] + if !bytes.Equal(servedCert.Raw, serverCert1Data) { + retries-- + time.Sleep(300 * time.Millisecond) + t.Logf("expected serverCert1, got %s", servedCert.Subject) + continue + } + + break + } + + // update the cert and key files by moving the second cert into place instead + err = os.Rename(serverCert2Path, serverCert1Path) + if err != nil { + t.Fatal(err) + } + err = os.Rename(serverCert2KeyPath, serverCert1KeyPath) + if err != nil { + t.Fatal(err) + } + + // make another connection, check that the server 2 cert is returned + retries = 10 + for { + if retries == 0 { + t.Fatal("failed to get serverCert2 before deadline") + } + + conn, err := tls.Dial("tcp", serverAddress, &tls.Config{RootCAs: certPool}) + if err != nil { + t.Fatal(err) + } + err = conn.Close() + if err != nil { + t.Fatal(err) + } + certs := conn.ConnectionState().PeerCertificates + if len(certs) != 1 { + t.Fatalf("expected 1 cert, got %d", len(certs)) + } + + servedCert := certs[0] + if !bytes.Equal(servedCert.Raw, serverCert2Data) { + retries-- + time.Sleep(300 * time.Millisecond) + t.Logf("expected serverCert2, got %s", servedCert.Subject) + continue + } + + break + } + + // remove the certs on disk, and check that the server still serves the previous certs + err = os.Remove(serverCert1Path) + if err != nil { + t.Fatal(err) + } + err = os.Remove(serverCert1KeyPath) + if err != nil { + t.Fatal(err) + } + + // make a third connection, and check that the server 2 cert is still returned despite the certs being removed + retries = 10 + for { + if retries == 0 { + t.Fatal("failed to get serverCert2 before deadline") + } + + conn, err := tls.Dial("tcp", serverAddress, &tls.Config{RootCAs: certPool}) + if err != nil { + t.Fatal(err) + } + err = conn.Close() + if err != nil { + t.Fatal(err) + } + + certs := conn.ConnectionState().PeerCertificates + if len(certs) != 1 { + t.Fatalf("expected 1 cert, got %d", len(certs)) + } + + servedCert := certs[0] + if !bytes.Equal(servedCert.Raw, serverCert2Data) { + retries-- + time.Sleep(300 * time.Millisecond) + t.Logf("expected serverCert2, got %s", servedCert.Subject) + continue + } + + break + } + + err = server.Shutdown(ctx) + if err != nil { + t.Fatalf("Unexpected error shutting down server: %s", err) + } + }) + } + +} + +type mockHTTPHandler struct{} + +func (*mockHTTPHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) +} + +type mockMetricsProvider struct{} + +func (*mockMetricsProvider) RegisterEndpoints(registrar func(string, string, http.Handler)) { + registrar("/metrics", "GET", new(mockHTTPHandler)) +} + +func (*mockMetricsProvider) InstrumentHandler(handler http.Handler, _ string) http.Handler { + return handler +} + +type listenerHook func() error + +type mockHTTPListener struct { + shutdownHook listenerHook + addrs string + t httpListenerType +} + +func (m mockHTTPListener) Addr() string { + return m.addrs +} + +func (mockHTTPListener) ListenAndServe() error { + return errors.New("not implemented") +} + +func (mockHTTPListener) ListenAndServeTLS(string, string) error { + return errors.New("not implemented") +} + +func (m mockHTTPListener) Shutdown(context.Context) error { + var err error + if m.shutdownHook != nil { + err = m.shutdownHook() + } + return err +} + +func (m mockHTTPListener) Type() httpListenerType { + return m.t +} + +func zipString(input string) []byte { + var b bytes.Buffer + gz := gzip.NewWriter(&b) + if _, err := gz.Write([]byte(input)); err != nil { + log.Fatal(err) + } + if err := gz.Close(); err != nil { + log.Fatal(err) + } + return b.Bytes() +} + +func TestStringPathToDataRef(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + path string + expRef string + expErr string + }{ + {path: "foo", expRef: `data.foo`}, + {path: "foo/", expRef: `data.foo`}, + {path: "foo/bar", expRef: `data.foo.bar`}, + {path: "foo/bar/", expRef: `data.foo.bar`}, + {path: "foo/../bar", expRef: `data.foo[".."].bar`}, + + // Path injection attack + // url path: `foo%22%5D%3Bmalicious_call%28%29%3Bx%3D%5B%22` + // url decoded: `foo"];malicious_call();x=["` + // data ref .String(): `data.foo["\"];malicious_call();x=[\""]` + // Above attack is mitigated by rejecting any ref component containing string terminators (`"`). + { + note: "string terminals inside ref term", + path: "foo%22%5D%3Bmalicious_call%28%29%3Bx%3D%5B%22", // foo"];malicious_call();x=[" + expErr: `invalid ref term 'foo"];malicious_call();x=["'`, + }, + } + + for _, tc := range cases { + note := tc.note + if note == "" { + note = strings.ReplaceAll(tc.path, "/", "_") + } + + t.Run(note, func(t *testing.T) { + ref, err := stringPathToDataRef(tc.path) + + if tc.expRef != "" { + if err != nil { + t.Fatalf("Expected ref:\n\n%s\n\nbut got error:\n\n%s", tc.expRef, err) + } + if refStr := ref.String(); refStr != tc.expRef { + t.Fatalf("Expected ref:\n\n%s\n\nbut got:\n\n%s", tc.expRef, refStr) + } + } + + if tc.expErr != "" { + if ref != nil { + t.Fatalf("Expected error:\n\n%s\n\nbut got ref:\n\n%s", tc.expErr, ref.String()) + } + if errStr := err.Error(); errStr != tc.expErr { + t.Fatalf("Expected error:\n\n%s\n\nbut got ref:\n\n%s", tc.expErr, errStr) + } + } + }) + } +} + +func TestParseRefQuery(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + raw string + expBody ast.Body + expErr string + }{ + { + note: "unparseable", + raw: `}abc{`, + expErr: "failed to parse query", + }, + { + note: "empty", + raw: ``, + expErr: "no ref", + }, + { + note: "single ref", + raw: `data.foo.bar`, + expBody: ast.MustParseBody(`data.foo.bar`), + }, + { + note: "multiple refs,';' separated", + raw: `data.foo.bar;data.baz.qux`, + expErr: "complex query", + }, + { + note: "multiple refs,newline separated", + raw: `data.foo.bar +data.baz.qux`, + expErr: "complex query", + }, + { + note: "single ref + call", + raw: `data.foo.bar;data.baz.qux()`, + expErr: "complex query", + }, + { + note: "single ref + assignment", + raw: `data.foo.bar;x := 42`, + expErr: "complex query", + }, + { + note: "single call", + raw: `data.foo.bar()`, + expErr: "complex query", + }, + { + note: "single assignment", + raw: `x := 42`, + expErr: "complex query", + }, + { + note: "single unification", + raw: `x = 42`, + expErr: "complex query", + }, + { + note: "single equality", + raw: `x == 42`, + expErr: "complex query", + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + body, err := parseRefQuery(tc.raw) + + if tc.expBody != nil { + if err != nil { + t.Fatalf("Expected body:\n\n%s\n\nbut got error:\n\n%s", tc.expBody, err) + } + if body.String() != tc.expBody.String() { + t.Fatalf("Expected body:\n\n%s\n\nbut got:\n\n%s", tc.expBody, body.String()) + } + } + + if tc.expErr != "" { + if body != nil { + t.Fatalf("Expected error:\n\n%s\n\nbut got body:\n\n%s", tc.expErr, body.String()) + } + if errStr := err.Error(); errStr != tc.expErr { + t.Fatalf("Expected error:\n\n%s\n\nbut got body:\n\n%s", tc.expErr, errStr) + } + } + }) + } +} diff --git a/third_party/opa/v1/server/types/types.go b/third_party/opa/v1/server/types/types.go new file mode 100644 index 000000000000..47df8f876544 --- /dev/null +++ b/third_party/opa/v1/server/types/types.go @@ -0,0 +1,497 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package types contains request/response types and codes for the server. +package types + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/util" +) + +// Error codes returned by OPA's REST API. +const ( + CodeInternal = "internal_error" + CodeEvaluation = "evaluation_error" + CodeUnauthorized = "unauthorized" + CodeInvalidParameter = "invalid_parameter" + CodeInvalidOperation = "invalid_operation" + CodeResourceNotFound = "resource_not_found" + CodeResourceConflict = "resource_conflict" + CodeUndefinedDocument = "undefined_document" +) + +// ErrorV1 models an error response sent to the client. +type ErrorV1 struct { + Code string `json:"code"` + Message string `json:"message"` + Errors []error `json:"errors,omitempty"` +} + +// NewErrorV1 returns a new ErrorV1 object. +func NewErrorV1(code, f string, a ...any) *ErrorV1 { + return &ErrorV1{ + Code: code, + Message: fmt.Sprintf(f, a...), + } +} + +// This shall only used for debugging purpose. +func (e *ErrorV1) Error() string { + return fmt.Sprintf("%s: %s", e.Code, e.Message) +} + +// WithError updates e to include a detailed error. +func (e *ErrorV1) WithError(err error) *ErrorV1 { + e.Errors = append(e.Errors, err) + return e +} + +// WithASTErrors updates e to include detailed AST errors. +func (e *ErrorV1) WithASTErrors(errors []*ast.Error) *ErrorV1 { + e.Errors = make([]error, len(errors)) + for i := range e.Errors { + e.Errors[i] = errors[i] + } + return e +} + +// Bytes marshals e with indentation for readability. +func (e *ErrorV1) Bytes() []byte { + bs, _ := json.MarshalIndent(e, "", " ") + return bs +} + +// Messages included in error responses. +const ( + MsgCompileModuleError = "error(s) occurred while compiling module(s)" + MsgParseQueryError = "error(s) occurred while parsing query" + MsgCompileQueryError = "error(s) occurred while compiling query" + MsgEvaluationError = "error(s) occurred while evaluating query" + MsgUnauthorizedUndefinedError = "authorization policy missing or undefined" + MsgUnauthorizedError = "request rejected by administrative policy" + MsgUndefinedError = "document missing or undefined" + MsgMissingError = "document missing" + MsgFoundUndefinedError = "document undefined" + MsgPluginConfigError = "error(s) occurred while configuring plugin(s)" + MsgDecodingLimitError = "request body too large" + MsgDecodingGzipLimitError = "compressed request body too large" +) + +// PatchV1 models a single patch operation against a document. +type PatchV1 struct { + Op string `json:"op"` + Path string `json:"path"` + Value any `json:"value"` +} + +// PolicyListResponseV1 models the response message for the Policy API list operation. +type PolicyListResponseV1 struct { + Result []PolicyV1 `json:"result"` +} + +// PolicyGetResponseV1 models the response message for the Policy API get operation. +type PolicyGetResponseV1 struct { + Result PolicyV1 `json:"result"` +} + +// PolicyPutResponseV1 models the response message for the Policy API put operation. +type PolicyPutResponseV1 struct { + Metrics MetricsV1 `json:"metrics,omitempty"` +} + +// PolicyDeleteResponseV1 models the response message for the Policy API delete operation. +type PolicyDeleteResponseV1 struct { + Metrics MetricsV1 `json:"metrics,omitempty"` +} + +// PolicyV1 models a policy module in OPA. +type PolicyV1 struct { + ID string `json:"id"` + Raw string `json:"raw"` + AST *ast.Module `json:"ast"` +} + +// Equal returns true if p is equal to other. +func (p PolicyV1) Equal(other PolicyV1) bool { + return p.ID == other.ID && p.Raw == other.Raw && p.AST.Equal(other.AST) +} + +// ProvenanceV1 models a collection of build/version information. +type ProvenanceV1 struct { + Version string `json:"version"` + Vcs string `json:"build_commit"` + Timestamp string `json:"build_timestamp"` + Hostname string `json:"build_hostname"` + Revision string `json:"revision,omitempty"` // Deprecated: Prefer `Bundles` + Bundles map[string]ProvenanceBundleV1 `json:"bundles,omitempty"` +} + +// ProvenanceBundleV1 models a bundle at some point in time +type ProvenanceBundleV1 struct { + Revision string `json:"revision"` +} + +// DataRequestV1 models the request message for Data API POST operations. +type DataRequestV1 struct { + Input *any `json:"input"` +} + +// DataResponseV1 models the response message for Data API read operations. +type DataResponseV1 struct { + DecisionID string `json:"decision_id,omitempty"` + Provenance *ProvenanceV1 `json:"provenance,omitempty"` + Explanation TraceV1 `json:"explanation,omitempty"` + Metrics MetricsV1 `json:"metrics,omitempty"` + Result *any `json:"result,omitempty"` + Warning *Warning `json:"warning,omitempty"` +} + +// Warning models DataResponse warnings +type Warning struct { + Code string `json:"code,omitempty"` + Message string `json:"message,omitempty"` +} + +// Warning Codes +const CodeAPIUsageWarn = "api_usage_warning" + +// Warning Messages +const MsgInputKeyMissing = "'input' key missing from the request" + +// NewWarning returns a new Warning object +func NewWarning(code, message string) *Warning { + return &Warning{Code: code, Message: message} +} + +// MetricsV1 models a collection of performance metrics. +type MetricsV1 map[string]any + +// QueryResponseV1 models the response message for Query API operations. +type QueryResponseV1 struct { + Explanation TraceV1 `json:"explanation,omitempty"` + Metrics MetricsV1 `json:"metrics,omitempty"` + Result AdhocQueryResultSetV1 `json:"result,omitempty"` +} + +// AdhocQueryResultSetV1 models the result of a Query API query. +type AdhocQueryResultSetV1 []map[string]any + +// ExplainModeV1 defines supported values for the "explain" query parameter. +type ExplainModeV1 string + +// Explanation mode enumeration. +const ( + ExplainOffV1 ExplainModeV1 = "off" + ExplainFullV1 ExplainModeV1 = "full" + ExplainNotesV1 ExplainModeV1 = "notes" + ExplainFailsV1 ExplainModeV1 = "fails" + ExplainDebugV1 ExplainModeV1 = "debug" +) + +// TraceV1 models the trace result returned for queries that include the +// "explain" parameter. +type TraceV1 json.RawMessage + +// MarshalJSON unmarshals the TraceV1 to a JSON representation. +func (t TraceV1) MarshalJSON() ([]byte, error) { + return t, nil +} + +// UnmarshalJSON unmarshals the TraceV1 from a JSON representation. +func (t *TraceV1) UnmarshalJSON(b []byte) error { + *t = TraceV1(b) + return nil +} + +// TraceV1Raw models the trace result returned for queries that include the +// "explain" parameter. The trace is modelled as series of trace events that +// identify the expression, local term bindings, query hierarchy, etc. +type TraceV1Raw []TraceEventV1 + +// UnmarshalJSON unmarshals the TraceV1Raw from a JSON representation. +func (t *TraceV1Raw) UnmarshalJSON(b []byte) error { + var trace []TraceEventV1 + if err := json.Unmarshal(b, &trace); err != nil { + return err + } + *t = TraceV1Raw(trace) + return nil +} + +// TraceV1Pretty models the trace result returned for queries that include the "explain" +// parameter. The trace is modelled as a human readable array of strings representing the +// evaluation of the query. +type TraceV1Pretty []string + +// UnmarshalJSON unmarshals the TraceV1Pretty from a JSON representation. +func (t *TraceV1Pretty) UnmarshalJSON(b []byte) error { + var s []string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + *t = TraceV1Pretty(s) + return nil +} + +// NewTraceV1 returns a new TraceV1 object. +func NewTraceV1(trace []*topdown.Event, pretty bool) (result TraceV1, err error) { + if pretty { + return newPrettyTraceV1(trace) + } + return newRawTraceV1(trace) +} + +func newRawTraceV1(trace []*topdown.Event) (TraceV1, error) { + result := TraceV1Raw(make([]TraceEventV1, len(trace))) + for i := range trace { + result[i] = TraceEventV1{ + Op: strings.ToLower(string(trace[i].Op)), + QueryID: trace[i].QueryID, + ParentID: trace[i].ParentID, + Locals: NewBindingsV1(trace[i].Locals), + Message: trace[i].Message, + } + if trace[i].Node != nil { + result[i].Type = ast.TypeName(trace[i].Node) + result[i].Node = trace[i].Node + } + } + + b, err := json.Marshal(result) + if err != nil { + return nil, err + } + return TraceV1(json.RawMessage(b)), nil +} + +func newPrettyTraceV1(trace []*topdown.Event) (TraceV1, error) { + var buf bytes.Buffer + topdown.PrettyTraceWithLocation(&buf, trace) + + str := strings.Trim(buf.String(), "\n") + b, err := json.Marshal(strings.Split(str, "\n")) + if err != nil { + return nil, err + } + return TraceV1(json.RawMessage(b)), nil +} + +// TraceEventV1 represents a step in the query evaluation process. +type TraceEventV1 struct { + Op string `json:"op"` + QueryID uint64 `json:"query_id"` + ParentID uint64 `json:"parent_id"` + Type string `json:"type"` + Node any `json:"node"` + Locals BindingsV1 `json:"locals"` + Message string `json:"message,omitempty"` +} + +// UnmarshalJSON deserializes a TraceEventV1 object. The Node field is +// deserialized based on the type hint from the type property in the JSON +// object. +func (te *TraceEventV1) UnmarshalJSON(bs []byte) error { + + keys := map[string]json.RawMessage{} + + if err := util.UnmarshalJSON(bs, &keys); err != nil { + return err + } + + if err := util.UnmarshalJSON(keys["type"], &te.Type); err != nil { + return err + } + + if err := util.UnmarshalJSON(keys["op"], &te.Op); err != nil { + return err + } + + if err := util.UnmarshalJSON(keys["query_id"], &te.QueryID); err != nil { + return err + } + + if err := util.UnmarshalJSON(keys["parent_id"], &te.ParentID); err != nil { + return err + } + + switch te.Type { + case "body": + var body ast.Body + if err := util.UnmarshalJSON(keys["node"], &body); err != nil { + return err + } + te.Node = body + case "expr": + var expr ast.Expr + if err := util.UnmarshalJSON(keys["node"], &expr); err != nil { + return err + } + te.Node = &expr + case "rule": + var rule ast.Rule + if err := util.UnmarshalJSON(keys["node"], &rule); err != nil { + return err + } + te.Node = &rule + } + + return util.UnmarshalJSON(keys["locals"], &te.Locals) +} + +// BindingsV1 represents a set of term bindings. +type BindingsV1 []*BindingV1 + +// BindingV1 represents a single term binding. +type BindingV1 struct { + Key *ast.Term `json:"key"` + Value *ast.Term `json:"value"` +} + +// NewBindingsV1 returns a new BindingsV1 object. +func NewBindingsV1(locals *ast.ValueMap) (result []*BindingV1) { + result = make([]*BindingV1, 0, locals.Len()) + locals.Iter(func(key, value ast.Value) bool { + result = append(result, &BindingV1{ + Key: &ast.Term{Value: key}, + Value: &ast.Term{Value: value}, + }) + return false + }) + return result +} + +// CompileRequestV1 models the request message for Compile API operations. +type CompileRequestV1 struct { + Input *any `json:"input"` + Query string `json:"query"` + Unknowns *[]string `json:"unknowns"` + Options struct { + DisableInlining []string `json:"disableInlining,omitempty"` + NondeterministicBuiltins bool `json:"nondeterministicBuiltins"` + } `json:"options,omitempty"` +} + +// CompileResponseV1 models the response message for Compile API operations. +type CompileResponseV1 struct { + Result *any `json:"result,omitempty"` + Explanation TraceV1 `json:"explanation,omitempty"` + Metrics MetricsV1 `json:"metrics,omitempty"` +} + +// PartialEvaluationResultV1 represents the output of partial evaluation and is +// included in Compile API responses. +type PartialEvaluationResultV1 struct { + Queries []ast.Body `json:"queries,omitempty"` + Support []*ast.Module `json:"support,omitempty"` +} + +// QueryRequestV1 models the request message for Query API operations. +type QueryRequestV1 struct { + Query string `json:"query"` + Input *any `json:"input"` +} + +// ConfigResponseV1 models the response message for Config API operations. +type ConfigResponseV1 struct { + Result *any `json:"result,omitempty"` +} + +// StatusResponseV1 models the response message for Status API (pull) operations. +type StatusResponseV1 struct { + Result *any `json:"result,omitempty"` +} + +// HealthResponseV1 models the response message for Health API operations. +type HealthResponseV1 struct { + Error string `json:"error,omitempty"` +} + +const ( + // ParamQueryV1 defines the name of the HTTP URL parameter that specifies + // values for the request query. + ParamQueryV1 = "q" + + // ParamInputV1 defines the name of the HTTP URL parameter that specifies + // values for the "input" document. + ParamInputV1 = "input" + + // ParamPrettyV1 defines the name of the HTTP URL parameter that indicates + // the client wants to receive a pretty-printed version of the response. + ParamPrettyV1 = "pretty" + + // ParamExplainV1 defines the name of the HTTP URL parameter that indicates the + // client wants to receive explanations in addition to the result. + ParamExplainV1 = "explain" + + // ParamMetricsV1 defines the name of the HTTP URL parameter that indicates + // the client wants to receive performance metrics in addition to the + // result. + ParamMetricsV1 = "metrics" + + // ParamInstrumentV1 defines the name of the HTTP URL parameter that + // indicates the client wants to receive instrumentation data for + // diagnosing performance issues. + ParamInstrumentV1 = "instrument" + + // ParamProvenanceV1 defines the name of the HTTP URL parameter that indicates + // the client wants build and version information in addition to the result. + ParamProvenanceV1 = "provenance" + + // ParamBundleActivationV1 defines the name of the HTTP URL parameter that + // indicates the client wants to include bundle activation in the results + // of the health API. + // Deprecated: Use ParamBundlesActivationV1 instead. + ParamBundleActivationV1 = "bundle" + + // ParamBundlesActivationV1 defines the name of the HTTP URL parameter that + // indicates the client wants to include bundle activation in the results + // of the health API. + ParamBundlesActivationV1 = "bundles" + + // ParamPluginsV1 defines the name of the HTTP URL parameter that + // indicates the client wants to include bundle status in the results + // of the health API. + ParamPluginsV1 = "plugins" + + // ParamExcludePluginV1 defines the name of the HTTP URL parameter that + // indicates the client wants to exclude plugin status in the results + // of the health API for the specified plugin(s) + ParamExcludePluginV1 = "exclude-plugin" + + // ParamStrictBuiltinErrors names the HTTP URL parameter that indicates the client + // wants built-in function errors to be treated as fatal. + ParamStrictBuiltinErrors = "strict-builtin-errors" +) + +// BadRequestErr represents an error condition raised if the caller passes +// invalid parameters. +type BadRequestErr string + +// BadPatchOperationErr returns BadRequestErr indicating the patch operation was +// invalid. +func BadPatchOperationErr(op string) error { + return BadRequestErr(fmt.Sprintf("bad patch operation: %v", op)) +} + +// BadPatchPathErr returns BadRequestErr indicating the patch path was invalid. +func BadPatchPathErr(path string) error { + return BadRequestErr(fmt.Sprintf("bad patch path: %v", path)) +} + +func (err BadRequestErr) Error() string { + return string(err) +} + +// IsBadRequest returns true if err is a BadRequestErr. +func IsBadRequest(err error) bool { + _, ok := err.(BadRequestErr) + return ok +} diff --git a/third_party/opa/v1/server/writer/writer.go b/third_party/opa/v1/server/writer/writer.go new file mode 100644 index 000000000000..eb968adade2d --- /dev/null +++ b/third_party/opa/v1/server/writer/writer.go @@ -0,0 +1,101 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package writer contains utilities for writing responses in the server. +package writer + +import ( + "encoding/json" + "net/http" + + "github.com/open-policy-agent/opa/v1/server/types" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/topdown" +) + +// HTTPStatus is used to set a specific status code +// Adapted from https://stackoverflow.com/questions/27711154/what-response-code-to-return-on-a-non-supported-http-method-on-rest +func HTTPStatus(code int) http.HandlerFunc { + return func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(code) + } +} + +// ErrorAuto writes a response with status and code set automatically based on +// the type of err. +func ErrorAuto(w http.ResponseWriter, err error) { + switch { + case types.IsBadRequest(err): + ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err) + case storage.IsWriteConflictError(err): + ErrorString(w, http.StatusNotFound, types.CodeResourceConflict, err) + case topdown.IsError(err): + Error(w, http.StatusInternalServerError, types.NewErrorV1(types.CodeInternal, types.MsgEvaluationError).WithError(err)) + case storage.IsInvalidPatch(err): + ErrorString(w, http.StatusBadRequest, types.CodeInvalidParameter, err) + case storage.IsNotFound(err): + ErrorString(w, http.StatusNotFound, types.CodeResourceNotFound, err) + default: + ErrorString(w, http.StatusInternalServerError, types.CodeInternal, err) + } +} + +// ErrorString writes a response with specified status, code, and message set to +// the err's string representation. +func ErrorString(w http.ResponseWriter, status int, code string, err error) { + Error(w, status, types.NewErrorV1(code, err.Error())) //nolint:govet +} + +// Error writes a response with specified status and error response. +func Error(w http.ResponseWriter, status int, err *types.ErrorV1) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + _, _ = w.Write(append(err.Bytes(), byte('\n'))) +} + +// JSON writes a response with the specified status code and object. The object +// will be JSON serialized. +// Deprecated: This method is problematic when using a non-200 status `code`: if +// encoding the payload fails, it'll print "superfluous call to WriteHeader()" +// logs. +func JSON(w http.ResponseWriter, code int, v any, pretty bool) { + enc := json.NewEncoder(w) + if pretty { + enc.SetIndent("", " ") + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + + if err := enc.Encode(v); err != nil { + ErrorAuto(w, err) + return + } +} + +// JSONOK is a helper for status "200 OK" responses +func JSONOK(w http.ResponseWriter, v any, pretty bool) { + enc := json.NewEncoder(w) + if pretty { + enc.SetIndent("", " ") + } + + w.Header().Add("Content-Type", "application/json") + // If Encode() calls w.Write() for the first time, it'll set the HTTP status + // to 200 OK. + if err := enc.Encode(v); err != nil { + ErrorAuto(w, err) + return + } +} + +// Bytes writes a response with the specified status code and bytes. +// Deprecated: Unused in OPA, will be removed in the future. +func Bytes(w http.ResponseWriter, code int, bs []byte) { + w.WriteHeader(code) + if code == 204 { + return + } + _, _ = w.Write(bs) +} diff --git a/third_party/opa/v1/storage/disk/config.go b/third_party/opa/v1/storage/disk/config.go new file mode 100644 index 000000000000..f871d1cafde1 --- /dev/null +++ b/third_party/opa/v1/storage/disk/config.go @@ -0,0 +1,82 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk + +import ( + "errors" + "fmt" + "os" + + badger "github.com/dgraph-io/badger/v4" + "github.com/open-policy-agent/opa/v1/config" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/util" +) + +type cfg struct { + Dir string `json:"directory"` + AutoCreate bool `json:"auto_create"` + Partitions []string `json:"partitions"` + Badger string `json:"badger"` +} + +var ErrInvalidPartitionPath = errors.New("invalid storage path") + +// OptionsFromConfig parses the passed config, extracts the disk storage +// settings, validates it, and returns a *Options struct pointer on success. +func OptionsFromConfig(raw []byte, id string) (*Options, error) { + parsedConfig, err := config.ParseConfig(raw, id) + if err != nil { + return nil, err + } + + if parsedConfig.Storage == nil || len(parsedConfig.Storage.Disk) == 0 { + return nil, nil + } + + var c cfg + if err := util.Unmarshal(parsedConfig.Storage.Disk, &c); err != nil { + return nil, err + } + + if _, err := os.Stat(c.Dir); err != nil { + if os.IsNotExist(err) && c.AutoCreate { + err = os.MkdirAll(c.Dir, 0700) // overwrite err + } + if err != nil { + return nil, fmt.Errorf("directory %v invalid: %w", c.Dir, err) + } + } + + opts := Options{ + Dir: c.Dir, + Badger: c.Badger, + } + for _, path := range c.Partitions { + p, ok := storage.ParsePath(path) + if !ok { + return nil, fmt.Errorf("partition path '%v': %w", path, ErrInvalidPartitionPath) + } + opts.Partitions = append(opts.Partitions, p) + } + + return &opts, nil +} + +func badgerConfigFromOptions(opts Options) (badger.Options, error) { + // Set some things _after_ FromSuperFlag to prohibit overriding them + + dir, err := dataDir(opts.Dir) + if err != nil { + return badger.DefaultOptions(""), err + } + + return badger.DefaultOptions(""). + FromSuperFlag(opts.Badger). + WithDir(dir). + WithValueDir(dir). + WithDetectConflicts(false), // We only allow one write txn at a time; so conflicts cannot happen. + nil +} diff --git a/third_party/opa/v1/storage/disk/config_test.go b/third_party/opa/v1/storage/disk/config_test.go new file mode 100644 index 000000000000..e773fd9deb58 --- /dev/null +++ b/third_party/opa/v1/storage/disk/config_test.go @@ -0,0 +1,237 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk + +import ( + "context" + "errors" + "os" + "path/filepath" + "testing" + + "github.com/dgraph-io/badger/v4" + "github.com/open-policy-agent/opa/v1/logging" +) + +func TestNewFromConfig(t *testing.T) { + t.Parallel() + + tmpdir := t.TempDir() + + for _, tc := range []struct { + note string + config string + err error // gets unwrapped + nothing bool // returns no disk options? + }{ + { + note: "no storage section", + config: "", + nothing: true, + }, + { + note: "successful init, no partitions", + config: ` +storage: + disk: + directory: "` + tmpdir + `" +`, + }, + { + note: "successful init, valid partitions", + config: ` +storage: + disk: + directory: "` + tmpdir + `" + partitions: + - /foo/bar + - /baz +`, + }, + { + note: "partitions invalid", + config: ` +storage: + disk: + directory: "` + tmpdir + `" + partitions: + - /foo/bar + - baz +`, + err: ErrInvalidPartitionPath, + }, + { + note: "directory does not exist", + config: ` +storage: + disk: + directory: "` + tmpdir + `/foobar" +`, + err: os.ErrNotExist, + }, + { + note: "auto-create directory, does not exist", + config: ` +storage: + disk: + auto_create: true + directory: "` + tmpdir + `/foobar" +`, + }, + { + note: "auto-create directory, does already exist", // could be the second run + config: ` +storage: + disk: + auto_create: true + directory: "` + tmpdir + `" +`, + }, + } { + t.Run(tc.note, func(t *testing.T) { + d, err := OptionsFromConfig([]byte(tc.config), "id") + if !errors.Is(err, tc.err) { + t.Errorf("err: expected %v, got %v", tc.err, err) + } + if tc.nothing && d != nil { + t.Errorf("expected no disk options, got %v", d) + } + }) + } +} + +func TestDataDirPrefix(t *testing.T) { + t.Parallel() + + ctx := context.Background() + tmpdir := t.TempDir() + + d, err := New(ctx, logging.NewNoOpLogger(), nil, Options{ + Dir: tmpdir, + }) + if err != nil { + t.Fatal(err) + } + d.Close(ctx) + + dir := filepath.Join(tmpdir, "data") + if _, err := os.Stat(dir); err != nil { + t.Fatalf("stat %v: %v", dir, err) + } + files, err := os.ReadDir(tmpdir) + if err != nil { + t.Fatal(err) + } + + // We currently only expect a single directory here: "data" + for _, file := range files { + if file.Name() != "data" { + t.Errorf("unexpected file in dir: %v", file.Name()) + } + } +} + +func TestBadgerConfigFromOptions(t *testing.T) { + t.Parallel() + + type check func(*testing.T, badger.Options) + checks := func(c ...check) []check { + return c + } + valueDir := func(exp string) check { + return func(t *testing.T, o badger.Options) { + if act := o.ValueDir; act != exp { + t.Errorf("ValueDir: expected %v, got %v", exp, act) + } + } + } + dir := func(exp string) check { + return func(t *testing.T, o badger.Options) { + if act := o.Dir; act != exp { + t.Errorf("Dir: expected %v, got %v", exp, act) + } + } + } + conflict := func(exp bool) check { + return func(t *testing.T, o badger.Options) { + if act := o.DetectConflicts; act != exp { + t.Errorf("DetectConflicts: expected %v, got %v", exp, act) + } + } + } + nummemtables := func(exp int) check { + return func(t *testing.T, o badger.Options) { + if act := o.NumMemtables; act != exp { + t.Errorf("Dir: expected %v, got %v", exp, act) + } + } + } + numversionstokeep := func(exp int) check { + return func(t *testing.T, o badger.Options) { + if act := o.NumVersionsToKeep; act != exp { + t.Errorf("Dir: expected %v, got %v", exp, act) + } + } + } + + tests := []struct { + note string + opts Options + checks []check + }{ + { + "defaults", + Options{ + Dir: "foo", + }, + checks( + valueDir("foo/data"), + dir("foo/data"), + conflict(false), + ), + }, + { + "valuedir+dir override", + Options{ + Dir: "foo", + Badger: `valuedir="baz"; dir="quz"`, + }, + checks( + valueDir("foo/data"), + dir("foo/data"), + ), + }, + { + "conflict detection override", + Options{ + Dir: "foo", + Badger: `detectconflicts=true`, + }, + checks(conflict(false)), + }, + { + "two valid overrides", // NOTE(sr): This is just one example + Options{ + Dir: "foo", + Badger: `nummemtables=123; numversionstokeep=123`, + }, + checks( + nummemtables(123), + numversionstokeep(123), + ), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + act, _ := badgerConfigFromOptions(tc.opts) + for _, check := range tc.checks { + check(t, act) + } + }) + } +} diff --git a/third_party/opa/v1/storage/disk/disk.go b/third_party/opa/v1/storage/disk/disk.go new file mode 100644 index 000000000000..46152ca27d97 --- /dev/null +++ b/third_party/opa/v1/storage/disk/disk.go @@ -0,0 +1,1029 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package disk provides disk-based implementation of the storage.Store +// interface. +// +// The disk.Store implementation uses an embedded key-value store to persist +// policies and data. Policy modules are stored as raw byte strings with one +// module per key. Data is mapped to the underlying key-value store with the +// assistance of caller-supplied "partitions". Partitions allow the caller to +// control the portions of the /data namespace that are mapped to individual +// keys. Operations that span multiple keys (e.g., a read against the entirety +// of /data) are more expensive than reads that target a specific key because +// the storage layer has to reconstruct the object from individual key-value +// pairs and page all of the data into memory. By supplying partitions that +// align with lookups in the policies, callers can optimize policy evaluation. +// +// Partitions are specified as a set of storage paths (e.g., {/foo/bar} declares +// a single partition at /foo/bar). Each partition tells the store that values +// under the partition path should be mapped to individual keys. Values that +// fall outside of the partitions are stored at adjacent keys without further +// splitting. For example, given the partition set {/foo/bar}, /foo/bar/abcd and +// /foo/bar/efgh are be written to separate keys. All other values under /foo +// are not split any further (e.g., all values under /foo/baz would be written +// to a single key). Similarly, values that fall outside of partitions are +// stored under individual keys at the root (e.g., the full extent of the value +// at /qux would be stored under one key.) +// There is support for wildcards in partitions: {/foo/*} will cause /foo/bar/abc +// and /foo/buz/def to be written to separate keys. Multiple wildcards are +// supported (/tenants/*/users/*/bindings), and they can also appear at the end +// of a partition (/users/*). +// +// All keys written by the disk.Store implementation are prefixed as follows: +// +// /// +// +// The value represents the version of the schema understood by +// this version of OPA. Currently this is always set to 1. The +// value represents the version of the partition layout +// supplied by the caller. Currently this is always set to 1. Currently, the +// disk.Store implementation only supports _additive_ changes to the +// partitioning layout, i.e., new partitions can be added as long as they do not +// overlap with existing unpartitioned data. The value is either "data" +// or "policies" depending on the value being stored. +// +// The disk.Store implementation attempts to be compatible with the inmem.store +// implementation however there are some minor differences: +// +// * Writes that add partitioned values implicitly create an object hierarchy +// containing the value (e.g., `add /foo/bar/abcd` implicitly creates the +// structure `{"foo": {"bar": {"abcd": ...}}}`). This is unavoidable because of +// how nested /data values are mapped to key-value pairs. +// +// * Trigger events do not include a set of changed paths because the underlying +// key-value store does not make them available. +package disk + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path" + "path/filepath" + "slices" + "strings" + "sync" + "sync/atomic" + "time" + + badger "github.com/dgraph-io/badger/v4" + "github.com/prometheus/client_golang/prometheus" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/util" +) + +// TODO(tsandall): add support for migrations +// TODO(sr): validate partition patterns properly: if the partitions were {/foo/bar} +// before and the new ones are {/foo/*}, it should be OK. + +// a value log file be rewritten if half the space can be discarded +const valueLogGCDiscardRatio = 0.5 + +// Options contains parameters that configure the disk-based store. +type Options struct { + Dir string // specifies directory to store data inside of + Partitions []storage.Path // data prefixes that enable efficient layout + Badger string // badger-internal configurables +} + +// Store provides a disk-based implementation of the storage.Store interface. +type Store struct { + db *badger.DB // underlying key-value store + xid uint64 // next transaction id + rmu sync.RWMutex // reader-writer lock + wmu sync.Mutex // writer lock + pm *pathMapper // maps logical storage paths to underlying store keys + partitions *partitionTrie // data structure to support path mapping + triggers map[*handle]struct{} // registered triggers + gcTicker *time.Ticker // gc ticker + close chan struct{} // close-only channel for stopping the GC goroutine + backupDB *badger.DB // backup of the underlying key-value store +} + +const ( + // metadataKey is a special value in the store for tracking schema versions. + metadataKey = "metadata" + + // supportedSchemaVersion represents the version of the store supported by + // this OPA. + supportedSchemaVersion int64 = 1 + + // basePartitionVersion represents the version of the caller-supplied data + // layout (aka partitioning). + basePartitionVersion int64 = 1 + + // symlink to directory path where badger write its files to + symlinkKey = "active" +) + +type metadata struct { + SchemaVersion *int64 `json:"schema_version"` // OPA-controlled data schema version + PartitionVersion *int64 `json:"partition_version"` // caller-supplied data layout version + Partitions []storage.Path `json:"partitions"` // caller-supplied data layout +} + +// systemPartition is the partition we add automatically: no user-defined partition +// should apply to the /system path. +const systemPartition = "/system/*" + +// New returns a new disk-based store based on the provided options. +func New(ctx context.Context, logger logging.Logger, prom prometheus.Registerer, opts Options) (*Store, error) { + + partitions := make(pathSet, len(opts.Partitions)) + copy(partitions, opts.Partitions) + partitions = partitions.Sorted() + + if !partitions.IsDisjoint() { + return nil, &storage.Error{ + Code: storage.InternalErr, + Message: fmt.Sprintf("partitions are overlapped: %v", opts.Partitions), + } + } + + partitions = append(partitions, storage.MustParsePath(systemPartition)) + if !partitions.IsDisjoint() { + return nil, &storage.Error{ + Code: storage.InternalErr, + Message: fmt.Sprintf("system partitions are managed: %v", opts.Partitions), + } + } + + options, err := badgerConfigFromOptions(opts) + if err != nil { + return nil, wrapError(err) + } + + options = options.WithLogger(&wrap{logger}) + db, err := badger.Open(options) + if err != nil { + return nil, wrapError(err) + } + + if prom != nil { + if err := initPrometheus(prom); err != nil { + return nil, err + } + } + + store := &Store{ + db: db, + partitions: buildPartitionTrie(partitions), + triggers: map[*handle]struct{}{}, + close: make(chan struct{}), + gcTicker: time.NewTicker(time.Minute), + } + + go store.GC(logger) + + if err := db.Update(func(txn *badger.Txn) error { + return store.init(ctx, txn, partitions) + }); err != nil { + store.Close(ctx) + return nil, err + } + + return store, store.diagnostics(ctx, partitions, logger) +} + +func (db *Store) GC(logger logging.Logger) { + for { + select { + case <-db.close: + return + case <-db.gcTicker.C: + for err := error(nil); err == nil; err = db.db.RunValueLogGC(valueLogGCDiscardRatio) { + logger.Debug("RunValueLogGC: err=%v", err) + } + } + } +} + +// Close finishes the DB connection and allows other processes to acquire it. +func (db *Store) Close(context.Context) error { + db.gcTicker.Stop() + return wrapError(db.db.Close()) +} + +// If the log level is debug, we'll output the badger logs in their corresponding +// log levels; if it's not debug, we'll suppress all badger logs. +type wrap struct { + l logging.Logger +} + +func (w *wrap) debugDo(f func(string, ...any), fmt string, as ...any) { + if w.l.GetLevel() >= logging.Debug { + f("badger: "+fmt, as...) + } +} + +func (w *wrap) Debugf(f string, as ...any) { w.debugDo(w.l.Debug, f, as...) } +func (w *wrap) Infof(f string, as ...any) { w.debugDo(w.l.Info, f, as...) } +func (w *wrap) Warningf(f string, as ...any) { w.debugDo(w.l.Warn, f, as...) } +func (w *wrap) Errorf(f string, as ...any) { w.debugDo(w.l.Error, f, as...) } + +// NewTransaction implements the storage.Store interface. +func (db *Store) NewTransaction(_ context.Context, params ...storage.TransactionParams) (storage.Transaction, error) { + var write bool + var context *storage.Context + + if len(params) > 0 { + write = params[0].Write + context = params[0].Context + } + + xid := atomic.AddUint64(&db.xid, uint64(1)) + if write { + db.wmu.Lock() // only one concurrent write txn + } else { + db.rmu.RLock() + } + underlying := db.db.NewTransaction(write) + + return newTransaction(xid, write, underlying, context, db.pm, db.partitions, db), nil +} + +// Truncate implements the storage.Store interface. This method must be called within a transaction. +func (db *Store) Truncate(ctx context.Context, txn storage.Transaction, params storage.TransactionParams, it storage.Iterator) error { + + // backup the existing store + currentDB, err := db.backupAndLoadDB() + if err != nil { + return wrapError(err) + } + + db.backupDB = currentDB + + // commit in-flight txn on the existing store + uTxn, err := db.underlying(txn) + if err != nil { + return err + } + + _, err = uTxn.Commit(ctx) + if err != nil { + return wrapError(err) + } + + // write new bundle policy and data into the existing DB + underlying := db.db.NewTransaction(true) + xid := atomic.AddUint64(&db.xid, uint64(1)) + underlyingTxn := newTransaction(xid, true, underlying, params.Context, db.pm, db.partitions, db) + + // For backwards compatibility, check if `RootOverwrite` was configured. + if params.RootOverwrite || overwriteRoot(params.BasePaths) { + newPath, ok := storage.ParsePathEscaped("/") + if !ok { + return fmt.Errorf("storage path invalid: %v", newPath) + } + + sTxn, err := db.doTruncateData(ctx, underlyingTxn, db.db, params, newPath, map[string]any{}) + if err != nil { + return wrapError(err) + } + + if sTxn != nil { + underlyingTxn = sTxn + } + } + + for { + var update *storage.Update + + update, err = it.Next() + if err == io.EOF { + break + } + + if err != nil { + return wrapError(err) + } + + if update.IsPolicy { + err = underlyingTxn.UpsertPolicy(ctx, strings.TrimLeft(update.Path.String(), "/"), update.Value) + if err != nil { + if err != badger.ErrTxnTooBig { + return wrapError(err) + } + + _, err = underlyingTxn.Commit(ctx) + if err != nil { + return wrapError(err) + } + + underlying = db.db.NewTransaction(true) + xid = atomic.AddUint64(&db.xid, uint64(1)) + underlyingTxn = newTransaction(xid, true, underlying, params.Context, db.pm, db.partitions, db) + + if err = underlyingTxn.UpsertPolicy(ctx, strings.TrimLeft(update.Path.String(), "/"), update.Value); err != nil { + return wrapError(err) + } + } + } else { + if len(update.Path) > 0 { + sTxn, err := db.doTruncateData(ctx, underlyingTxn, db.db, params, update.Path, update.Value) + if err != nil { + return wrapError(err) + } + + if sTxn != nil { + underlyingTxn = sTxn + } + } else { + for _, root := range params.BasePaths { + newPath, ok := storage.ParsePathEscaped("/" + root) + if !ok { + return fmt.Errorf("storage path invalid: %v", newPath) + } + + value, ok, err := lookup(newPath, update.Value) + if err != nil { + return err + } + + if ok { + if len(newPath) > 0 { + if err := storage.MakeDir(ctx, db, underlyingTxn, newPath[:len(newPath)-1]); err != nil { + return err + } + } + + sTxn, err := db.doTruncateData(ctx, underlyingTxn, db.db, params, newPath, value) + if err != nil { + return wrapError(err) + } + + if sTxn != nil { + underlyingTxn = sTxn + } + } + } + } + } + } + + if err != nil && err != io.EOF { + return wrapError(err) + } + + // commit active transaction on existing store + _, err = underlyingTxn.Commit(ctx) + if err != nil { + return wrapError(err) + } + + // Open write txn on the existing store in-case there are more write operations. + // The caller will either commit or abort this transaction + uTxn.stale = false + uTxn.underlying = db.db.NewTransaction(true) + + return nil +} + +func (db *Store) doTruncateData(ctx context.Context, underlying *transaction, badgerdb *badger.DB, + params storage.TransactionParams, path storage.Path, value any) (*transaction, error) { + + err := underlying.Write(ctx, storage.AddOp, path, value) + if err != nil { + if err != badger.ErrTxnTooBig { + return nil, wrapError(err) + } + + _, err = underlying.Commit(ctx) + if err != nil { + return nil, wrapError(err) + } + + txn := badgerdb.NewTransaction(true) + xid := atomic.AddUint64(&db.xid, uint64(1)) + sTxn := newTransaction(xid, true, txn, params.Context, db.pm, db.partitions, db) + + if err = sTxn.Write(ctx, storage.AddOp, path, value); err != nil { + return nil, wrapError(err) + } + + return sTxn, nil + } + + return nil, nil +} + +func (db *Store) backupAndLoadDB() (*badger.DB, error) { + currDir := db.db.Opts().Dir + + // backup db + backupDir, err := os.MkdirTemp(path.Dir(currDir), "backup") + if err != nil { + return nil, wrapError(err) + } + + bak, err := os.CreateTemp(backupDir, "badgerbak") + if err != nil { + return nil, wrapError(err) + } + + _, err = db.db.Backup(bak, 0) + if err != nil { + return nil, wrapError(err) + } + + // restore db + newDBDir, err := os.MkdirTemp(path.Dir(currDir), "backup") + if err != nil { + return nil, wrapError(err) + } + + opts := db.db.Opts().WithDir(newDBDir).WithValueDir(newDBDir) + + // open new db + newDB, err := badger.Open(opts) + if err != nil { + return nil, wrapError(err) + } + + bak, err = os.Open(bak.Name()) + if err != nil { + return nil, err + } + defer bak.Close() + + err = newDB.Load(bak, 16) + if err != nil { + return nil, wrapError(err) + } + + return newDB, wrapError(os.RemoveAll(backupDir)) +} + +func (*Store) cleanup(oldDB *badger.DB) error { + err := oldDB.Close() + if err != nil { + return wrapError(err) + } + + return wrapError(os.RemoveAll(oldDB.Opts().Dir)) +} + +// Commit implements the storage.Store interface. +func (db *Store) Commit(ctx context.Context, txn storage.Transaction) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + if underlying.write { + db.rmu.Lock() // blocks until all readers are done + event, err := underlying.Commit(ctx) + if err != nil { + return err + } + write := false // read only txn + readOnly := db.db.NewTransaction(write) + xid := atomic.AddUint64(&db.xid, uint64(1)) + readTxn := newTransaction(xid, write, readOnly, nil, db.pm, db.partitions, db) + for h := range db.triggers { + h.cb(ctx, readTxn, event) + } + + // cleanup backup db + if db.backupDB != nil { + if err := db.cleanup(db.backupDB); err != nil { + panic(err) + } + db.backupDB = nil + } + + db.rmu.Unlock() + db.wmu.Unlock() + } else { // committing read txn + underlying.Abort(ctx) + db.rmu.RUnlock() + } + return nil +} + +// Abort implements the storage.Store interface. +func (db *Store) Abort(ctx context.Context, txn storage.Transaction) { + underlying, err := db.underlying(txn) + if err != nil { + panic(err) + } + underlying.Abort(ctx) + + if underlying.write { + + if db.backupDB != nil { + db.rmu.Lock() + + // update symlink to point to the backup db + symlink := filepath.Join(path.Dir(db.backupDB.Opts().Dir), symlinkKey) + // "active" -> "backupXXXX" is what we want, not + // "active" -> "DIR/backupXXX", since that won't work when using a relative directory + target := filepath.Base(db.backupDB.Opts().Dir) + + err = createSymlink(target, symlink) + if err != nil { + panic(err) + } + + // swap db + oldDb := db.db + db.db = db.backupDB + + // cleanup existing db + if err := db.cleanup(oldDb); err != nil { + panic(err) + } + + db.rmu.Unlock() + } + + db.wmu.Unlock() + } else { + db.rmu.RUnlock() + } +} + +// ListPolicies implements the storage.Policy interface. +func (db *Store) ListPolicies(ctx context.Context, txn storage.Transaction) ([]string, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + return underlying.ListPolicies(ctx) +} + +// GetPolicy implements the storage.Policy interface. +func (db *Store) GetPolicy(ctx context.Context, txn storage.Transaction, id string) ([]byte, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + return underlying.GetPolicy(ctx, id) +} + +// UpsertPolicy implements the storage.Policy interface. +func (db *Store) UpsertPolicy(ctx context.Context, txn storage.Transaction, id string, bs []byte) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + return underlying.UpsertPolicy(ctx, id, bs) +} + +// DeletePolicy implements the storage.Policy interface. +func (db *Store) DeletePolicy(ctx context.Context, txn storage.Transaction, id string) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + if _, err := underlying.GetPolicy(ctx, id); err != nil { + return err + } + return underlying.DeletePolicy(ctx, id) +} + +// Register implements the storage.Trigger interface. +func (db *Store) Register(_ context.Context, txn storage.Transaction, config storage.TriggerConfig) (storage.TriggerHandle, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + if !underlying.write { + return nil, &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "triggers must be registered with a write transaction", + } + } + h := &handle{db: db, cb: config.OnCommit} + db.triggers[h] = struct{}{} + return h, nil +} + +// Read implements the storage.Store interface. +func (db *Store) Read(ctx context.Context, txn storage.Transaction, path storage.Path) (any, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + return underlying.Read(ctx, path) +} + +// Write implements the storage.Store interface. +func (db *Store) Write(ctx context.Context, txn storage.Transaction, op storage.PatchOp, path storage.Path, value any) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + val := util.Reference(value) + if err := util.RoundTrip(val); err != nil { + return wrapError(err) + } + return underlying.Write(ctx, op, path, *val) +} + +func (db *Store) underlying(txn storage.Transaction) (*transaction, error) { + underlying, ok := txn.(*transaction) + if !ok { + return nil, &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: fmt.Sprintf("unexpected transaction type %T", txn), + } + } + if underlying.db != db { + return nil, &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "unknown transaction", + } + } + if underlying.stale { + return nil, &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "stale transaction", + } + } + return underlying, nil +} + +type handle struct { + db *Store + cb func(context.Context, storage.Transaction, storage.TriggerEvent) +} + +func (h *handle) Unregister(_ context.Context, txn storage.Transaction) { + underlying, err := h.db.underlying(txn) + if err != nil { + panic(err) + } + if !underlying.write { + panic(&storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "triggers must be unregistered with a write transaction", + }) + } + delete(h.db.triggers, h) +} + +func (*Store) loadMetadata(txn *badger.Txn, m *metadata) (bool, error) { + + item, err := txn.Get([]byte(metadataKey)) + if err != nil { + if err != badger.ErrKeyNotFound { + return false, wrapError(err) + } + return false, nil + } + + bs, err := item.ValueCopy(nil) + if err != nil { + return false, wrapError(err) + } + + err = util.NewJSONDecoder(bytes.NewBuffer(bs)).Decode(m) + if err != nil { + return false, wrapError(err) + } + + return true, nil +} + +func (*Store) setMetadata(txn *badger.Txn, m metadata) error { + + bs, err := json.Marshal(m) + if err != nil { + return wrapError(err) + } + + return wrapError(txn.Set([]byte(metadataKey), bs)) +} + +func (db *Store) init(ctx context.Context, txn *badger.Txn, partitions []storage.Path) error { + + // Load existing metadata structure from the DB. + var m metadata + found, err := db.loadMetadata(txn, &m) + if err != nil { + return err + } + + if found && *m.SchemaVersion != supportedSchemaVersion { + return &storage.Error{ + Code: storage.InternalErr, + Message: fmt.Sprintf("unsupported schema version: %v (want %v)", *m.SchemaVersion, supportedSchemaVersion), + } + } + + // Initialize path mapper for operations on the DB. + if found { + db.pm = newPathMapper(*m.SchemaVersion, *m.PartitionVersion) + } else { + db.pm = newPathMapper(supportedSchemaVersion, basePartitionVersion) + } + + schemaVersion := supportedSchemaVersion + partitionVersion := basePartitionVersion + + // If metadata does not exist, finish initialization. + if !found { + return db.setMetadata(txn, metadata{ + SchemaVersion: &schemaVersion, + PartitionVersion: &partitionVersion, + Partitions: partitions, + }) + } + + // Check for backwards incompatible changes to partition map. + if err := db.validatePartitions(ctx, txn, m, partitions); err != nil { + return err + } + + // Assert updated metadata. + return db.setMetadata(txn, metadata{ + SchemaVersion: &schemaVersion, + PartitionVersion: &partitionVersion, + Partitions: partitions, + }) +} + +func (db *Store) validatePartitions(_ context.Context, txn *badger.Txn, existing metadata, partitions []storage.Path) error { + + oldPathSet := pathSet(existing.Partitions) + newPathSet := pathSet(partitions) + removedPartitions := oldPathSet.Diff(newPathSet) + addedPartitions := newPathSet.Diff(oldPathSet) + + // It's OK to replace partitions with wildcard partitions that overlap them: + // REMOVED: /foo/bar + // ADDED: /foo/* + // and the like. + replaced := make(pathSet, 0) + replacements := make(pathSet, 0) + for _, removed := range removedPartitions { + for _, added := range addedPartitions { + if isMatchedBy(removed, added) { + replaced = append(replaced, removed) + replacements = append(replacements, added) + } + } + } + + rest := removedPartitions.Diff(replaced) + if len(rest) > 0 { + return &storage.Error{ + Code: storage.InternalErr, + Message: fmt.Sprintf("partitions are backwards incompatible (old: %v, new: %v, missing: %v)", oldPathSet, newPathSet, rest)} + } + + for _, path := range addedPartitions.Diff(replacements) { + if prefix, wildcard := hasWildcard(path); wildcard { + path = prefix + } + for i := len(path); i > 0; i-- { + key, err := db.pm.DataPath2Key(path[:i]) + if err != nil { + return err + } + _, err = txn.Get(key) + if err == nil { + return &storage.Error{ + Code: storage.InternalErr, + Message: fmt.Sprintf("partitions are backwards incompatible (existing data: %v)", path[:i]), + } + } else if err != badger.ErrKeyNotFound { + return wrapError(err) + } + } + } + + return nil +} + +// MakeDir makes Store a storage.MakeDirer, to avoid the superfluous MakeDir +// steps -- MakeDir is implicit in the disk storage's data layout, since +// +// {"foo": {"bar": {"baz": 10}}} +// +// writes value `10` to key `/foo/bar/baz`. +// +// Here, we only check if it's a write transaction, for consistency with +// other implementations, and do nothing. +func (db *Store) MakeDir(_ context.Context, txn storage.Transaction, _ storage.Path) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + if !underlying.write { + return &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "MakeDir must be called with a write transaction", + } + } + return nil +} + +// diagnostics prints relevant partition and database related information at +// debug level. +func (db *Store) diagnostics(ctx context.Context, partitions pathSet, logger logging.Logger) error { + if logger.GetLevel() < logging.Debug { + return nil + } + if len(partitions) == 1 { // '/system/*' is always present + logger.Warn("no partitions configured") + if err := db.logPrefixStatistics(ctx, storage.MustParsePath("/"), logger); err != nil { + return err + } + } + for _, partition := range partitions { + if err := db.logPrefixStatistics(ctx, partition, logger); err != nil { + return err + } + } + return nil +} + +func (db *Store) logPrefixStatistics(ctx context.Context, partition storage.Path, logger logging.Logger) error { + + if prefix, ok := hasWildcard(partition); ok { + return db.logPrefixStatisticsWildcardPartition(ctx, prefix, partition, logger) + } + + key, err := db.pm.DataPrefix2Key(partition) + if err != nil { + return err + } + + opt := badger.DefaultIteratorOptions + opt.PrefetchValues = false + opt.Prefix = key + + var count, size uint64 + if err := db.db.View(func(txn *badger.Txn) error { + it := txn.NewIterator(opt) + defer it.Close() + for it.Rewind(); it.Valid(); it.Next() { + if err := ctx.Err(); err != nil { + return err + } + count++ + size += uint64(it.Item().EstimatedSize()) // key length + value length + } + return nil + }); err != nil { + return err + } + logger.Debug("partition %s: key count: %d (estimated size %d bytes)", partition, count, size) + return nil +} + +func hasWildcard(path storage.Path) (storage.Path, bool) { + for i := range path { + if path[i] == pathWildcard { + return path[:i], true + } + } + return nil, false +} + +func (db *Store) logPrefixStatisticsWildcardPartition(ctx context.Context, prefix, partition storage.Path, logger logging.Logger) error { + // we iterate all keys, and count things according to their concrete partition + type diagInfo struct{ count, size uint64 } + diag := map[string]*diagInfo{} + + key, err := db.pm.DataPrefix2Key(prefix) + if err != nil { + return err + } + + opt := badger.DefaultIteratorOptions + opt.PrefetchValues = false + opt.Prefix = key + if err := db.db.View(func(txn *badger.Txn) error { + it := txn.NewIterator(opt) + defer it.Close() + for it.Rewind(); it.Valid(); it.Next() { + if err := ctx.Err(); err != nil { + return err + } + if part, ok := db.prefixInPattern(it.Item().Key(), partition); ok { + p := part.String() + if diag[p] == nil { + diag[p] = &diagInfo{} + } + diag[p].count++ + diag[p].size += uint64(it.Item().EstimatedSize()) // key length + value length + } + } + return nil + }); err != nil { + return err + } + if len(diag) == 0 { + logger.Debug("partition pattern %s: key count: 0 (estimated size 0 bytes)", toString(partition)) + } + for part, diag := range diag { + logger.Debug("partition %s (pattern %s): key count: %d (estimated size %d bytes)", part, toString(partition), diag.count, diag.size) + } + return nil +} + +func (db *Store) prefixInPattern(key []byte, partition storage.Path) (storage.Path, bool) { + var part storage.Path + path, err := db.pm.DataKey2Path(key) + if err != nil { + return nil, false + } + for i := range partition { + if path[i] != partition[i] && partition[i] != pathWildcard { + return nil, false + } + part = append(part, path[i]) + } + return part, true +} + +func toString(path storage.Path) string { + if len(path) == 0 { + return "/" + } + buf := strings.Builder{} + for _, p := range path { + fmt.Fprintf(&buf, "/%s", p) + } + return buf.String() +} + +// dataDir prefixes the configured storage location: what it returns is +// what we have badger write its files to. It is done to give us some +// wiggle room in the future should we need to put further files on the +// file system (like backups): we can then just use the opts.Dir. +func dataDir(dir string) (string, error) { + + symlink := filepath.Join(dir, symlinkKey) + if _, err := os.Lstat(symlink); err == nil { + return filepath.EvalSymlinks(symlink) + } + + return filepath.Join(dir, "data"), nil +} + +func createSymlink(target, symlink string) error { + var lerr error + + if _, lerr = os.Lstat(symlink); lerr == nil { + if err := os.Remove(symlink); err != nil { + return err + } + + if err := os.Symlink(target, symlink); err != nil { + return err + } + } else if errors.Is(lerr, os.ErrNotExist) { + if err := os.Symlink(target, symlink); err != nil { + return err + } + + return nil + } + + return lerr +} + +func lookup(path storage.Path, data []byte) (any, bool, error) { + var obj map[string]json.RawMessage + err := util.Unmarshal(data, &obj) + if err != nil { + return nil, false, err + } + + if len(path) == 0 { + return obj, true, nil + } + + for i := range len(path) - 1 { + value, ok := obj[path[i]] + if !ok { + return nil, false, nil + } + + var next map[string]json.RawMessage + err := util.Unmarshal(value, &next) + if err != nil { + return nil, false, err + } + + obj = next + } + + value, ok := obj[path[len(path)-1]] + return value, ok, nil +} + +func overwriteRoot(roots []string) bool { + return slices.Contains(roots, "") +} diff --git a/third_party/opa/v1/storage/disk/disk_test.go b/third_party/opa/v1/storage/disk/disk_test.go new file mode 100644 index 000000000000..eba381acf83f --- /dev/null +++ b/third_party/opa/v1/storage/disk/disk_test.go @@ -0,0 +1,1909 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" + + "github.com/open-policy-agent/opa/v1/bundle" + + badger "github.com/dgraph-io/badger/v4" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +type testRead struct { + path string + exp string +} + +type testWrite struct { + op storage.PatchOp + path string + value string +} + +type testWriteError struct { + op storage.PatchOp + path string + value string +} + +// testCount lets you assert the number of keys under a prefix. +// Note that we don't do exact matches, so the assertions should be +// as exact as possible: +// +// testCount{"/foo", 1} +// testCount{"/foo/bar", 1} +// +// both of these would be true for one element under key `/foo/bar`. +type testCount struct { + key string + count int +} + +func (tc *testCount) assert(t *testing.T, s *Store) { + t.Helper() + key, err := s.pm.DataPath2Key(storage.MustParsePath(tc.key)) + if err != nil { + t.Fatal(err) + } + + opt := badger.DefaultIteratorOptions + opt.PrefetchValues = false + opt.Prefix = key + + var count int + if err := s.db.View(func(txn *badger.Txn) error { + it := txn.NewIterator(opt) + defer it.Close() + for it.Rewind(); it.Valid(); it.Next() { + count++ + } + return nil + }); err != nil { + t.Fatal(err) + } + + if tc.count != count { + t.Errorf("key %v: expected %d keys, found %d", tc.key, tc.count, count) + } +} + +type testDump struct{} // for debugging purposes + +func (*testDump) do(t *testing.T, s *Store) { + t.Helper() + opt := badger.DefaultIteratorOptions + opt.PrefetchValues = true + + if err := s.db.View(func(txn *badger.Txn) error { + it := txn.NewIterator(opt) + defer it.Close() + for it.Rewind(); it.Valid(); it.Next() { + t.Logf("%v -> %v", string(it.Item().Key()), it.Item().ValueSize()) + } + return nil + }); err != nil { + t.Fatal(err) + } +} + +func TestPolicies(t *testing.T) { + t.Parallel() + + test.WithTempFS(map[string]string{}, func(dir string) { + ctx := context.Background() + s, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: nil}) + if err != nil { + t.Fatal(err) + } + defer s.Close(ctx) + + err = storage.Txn(ctx, s, storage.WriteParams, func(txn storage.Transaction) error { + ids, err := s.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) > 0 { + t.Fatal("unexpected policies found") + } + + _, err = s.GetPolicy(ctx, txn, "foo.rego") + if err == nil { + t.Fatal("expected error") + } + + err = s.DeletePolicy(ctx, txn, "foo.rego") + if err == nil { + t.Fatal("expected error") + } + + err = s.UpsertPolicy(ctx, txn, "foo.rego", []byte(`package foo`)) + if err != nil { + t.Fatal(err) + } + + ids, err = s.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) != 1 || ids[0] != "foo.rego" { + t.Fatalf("missing or unexpected policies found: %v", ids) + } + + bs, err := s.GetPolicy(ctx, txn, "foo.rego") + if err != nil || !bytes.Equal(bs, []byte("package foo")) { + t.Fatalf("unexpected error or bad result: err=%v, result=%v", err, string(bs)) + } + + err = s.DeletePolicy(ctx, txn, "foo.rego") + if err != nil { + t.Fatal(err) + } + + ids, err = s.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } else if len(ids) > 0 { + t.Fatal("unexpected policies found") + } + + return nil + }) + if err != nil { + t.Fatal(err) + } + }) +} + +func TestTruncateAbsoluteStoragePath(t *testing.T) { + t.Parallel() + + test.WithTempFS(map[string]string{}, func(dir string) { + runTruncateTest(t, dir) + }) +} + +func TestTruncateRelativeStoragePath(t *testing.T) { + t.Parallel() + + runTruncateTest(t, t.TempDir()) +} + +func runTruncateTest(t *testing.T, dir string) { + ctx := context.Background() + s, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: nil}) + if err != nil { + t.Fatal(err) + } + defer s.Close(ctx) + + txn := storage.NewTransactionOrDie(ctx, s, storage.WriteParams) + + var archiveFiles = map[string]string{ + "/a/b/c/data.json": "[1,2,3]", + "/a/b/d/data.json": `e: true`, + "/data.json": `{"x": {"y": true}, "a": {"b": {"z": true}}}`, + "/a/b/y/data.yaml": `foo: 1`, + "/policy.rego": "package foo\n p = 1", + "/roles/policy.rego": "package bar\n p = 1", + } + + files := make([][2]string, 0, len(archiveFiles)) + for name, content := range archiveFiles { + files = append(files, [2]string{name, content}) + } + + buf := archive.MustWriteTarGz(files) + b, err := bundle.NewReader(buf).WithLazyLoadingMode(true).Read() + if err != nil { + t.Fatal(err) + } + + iterator := bundle.NewIterator(b.Raw) + + params := storage.WriteParams + params.BasePaths = []string{""} + err = s.Truncate(ctx, txn, params, iterator) + if err != nil { + t.Fatalf("Unexpected truncate error: %v", err) + } + + if err := s.Commit(ctx, txn); err != nil { + t.Fatalf("Unexpected commit error: %v", err) + } + + // check symlink not created + symlink := filepath.Join(dir, symlinkKey) + _, err = os.Lstat(symlink) + if err == nil { + t.Fatal("Expected error but got nil") + } + + txn = storage.NewTransactionOrDie(ctx, s) + + actual, err := s.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatal(err) + } + + expected := ` + { + "a": { + "b": { + "c": [1,2,3], + "d": { + "e": true + }, + "y": { + "foo": 1 + }, + "z": true + } + }, + "x": { + "y": true + } + } + ` + jsn := util.MustUnmarshalJSON([]byte(expected)) + + if !reflect.DeepEqual(jsn, actual) { + t.Fatalf("Expected reader's read to be %v but got: %v", jsn, actual) + } + + s.Abort(ctx, txn) + + txn = storage.NewTransactionOrDie(ctx, s) + ids, err := s.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } + + expectedIDs := map[string]struct{}{"policy.rego": {}, "roles/policy.rego": {}} + + for _, id := range ids { + if _, ok := expectedIDs[id]; !ok { + t.Fatalf("Expected list policies to contain %v but got: %v", expectedIDs, id) + } + } + + bs, err := s.GetPolicy(ctx, txn, "policy.rego") + expectedBytes := []byte("package foo\n p = 1") + if err != nil || !bytes.Equal(expectedBytes, bs) { + t.Fatalf("Expected get policy to return %v but got: %v (err: %v)", expectedBytes, bs, err) + } + + bs, err = s.GetPolicy(ctx, txn, "roles/policy.rego") + expectedBytes = []byte("package bar\n p = 1") + if err != nil || !bytes.Equal(expectedBytes, bs) { + t.Fatalf("Expected get policy to return %v but got: %v (err: %v)", expectedBytes, bs, err) + } + + // Close and re-open store + if err := s.Close(ctx); err != nil { + t.Fatalf("store close: %v", err) + } + + if _, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: nil}); err != nil { + t.Fatalf("store re-open: %v", err) + } +} + +func TestTruncateMultipleTxn(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + test.WithTempFS(map[string]string{}, func(dir string) { + ctx := context.Background() + s, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: nil, Badger: "memtablesize=4000;valuethreshold=600"}) + if err != nil { + t.Fatal(err) + } + defer s.Close(ctx) + + txn := storage.NewTransactionOrDie(ctx, s, storage.WriteParams) + + archiveFiles := map[string]string{} + + for i := range 20 { + + path := fmt.Sprintf("users/user%d/data.json", i) + + obj := map[string][]byte{} + obj[fmt.Sprintf("key%d", i)] = bytes.Repeat([]byte("a"), 1<<20) // 1 MB. + + bs, err := json.Marshal(obj) + if err != nil { + t.Fatal(err) + } + + archiveFiles[path] = string(bs) + } + + // additional data file at root + archiveFiles["/data.json"] = `{"a": {"b": {"z": true}}}` + + files := make([][2]string, 0, len(archiveFiles)) + for name, content := range archiveFiles { + files = append(files, [2]string{name, content}) + } + + buf := archive.MustWriteTarGz(files) + b, err := bundle.NewReader(buf).WithLazyLoadingMode(true).Read() + if err != nil { + t.Fatal(err) + } + + iterator := bundle.NewIterator(b.Raw) + + params := storage.WriteParams + params.BasePaths = []string{""} + err = s.Truncate(ctx, txn, params, iterator) + if err != nil { + t.Fatalf("Unexpected truncate error: %v", err) + } + + if err := s.Commit(ctx, txn); err != nil { + t.Fatalf("Unexpected commit error: %v", err) + } + + txn = storage.NewTransactionOrDie(ctx, s) + + _, err = s.Read(ctx, txn, storage.MustParsePath("/users/user19")) + if err != nil { + t.Fatal(err) + } + + s.Abort(ctx, txn) + + txn = storage.NewTransactionOrDie(ctx, s) + + actual, err := s.Read(ctx, txn, storage.MustParsePath("/a")) + if err != nil { + t.Fatal(err) + } + + expected := ` + { + "b": { + "z": true + } + } + ` + jsn := util.MustUnmarshalJSON([]byte(expected)) + + if !reflect.DeepEqual(jsn, actual) { + t.Fatalf("Expected reader's read to be %v but got: %v", jsn, actual) + } + }) +} + +func TestDataPartitioningValidation(t *testing.T) { + t.Parallel() + + closeFn := func(ctx context.Context, s *Store) { + t.Helper() + if s == nil { + return + } + if err := s.Close(ctx); err != nil { + t.Fatal(err) + } + } + + test.WithTempFS(map[string]string{}, func(dir string) { + + ctx := context.Background() + + _, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo/bar"), + storage.MustParsePath("/foo/bar/baz"), + }}) + + if err == nil { + t.Fatal("expected error") + } else if sErr, ok := err.(*storage.Error); !ok { + t.Fatal("expected storage error but got:", err) + } else if sErr.Code != storage.InternalErr || sErr.Message != "partitions are overlapped: [/foo/bar /foo/bar/baz]" { + t.Fatal("unexpected code or message, got:", err) + } + + // set up two partitions + s, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo/bar"), + storage.MustParsePath("/foo/baz"), + }}) + if err != nil { + t.Fatal(err) + } + + closeFn(ctx, s) + + // init with same settings: nothing wrong + s, err = New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo/baz"), + storage.MustParsePath("/foo/bar"), + }}) + if err != nil { + t.Fatal(err) + } + + closeFn(ctx, s) + + // adding another partition + s, err = New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo/baz"), + storage.MustParsePath("/foo/bar"), + storage.MustParsePath("/foo/qux"), + }}) + if err != nil { + t.Fatal(err) + } + + // We're writing data under the partitions: this affects how + // some partition changes are treated: if they don't affect existing + // data, they are accepted. + err = storage.WriteOne(ctx, s, storage.AddOp, storage.MustParsePath("/foo/corge"), "x") + if err != nil { + t.Fatal(err) + } + + err = storage.WriteOne(ctx, s, storage.AddOp, storage.MustParsePath("/deadbeef"), "x") + if err != nil { + t.Fatal(err) + } + + closeFn(ctx, s) + + s, err = New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo/baz"), + storage.MustParsePath("/foo/bar"), + storage.MustParsePath("/foo/qux/corge"), + }}) + if err == nil || !strings.Contains(err.Error(), + "partitions are backwards incompatible (old: [/foo/bar /foo/baz /foo/qux /system/*], new: [/foo/bar /foo/baz /foo/qux/corge /system/*], missing: [/foo/qux])") { + t.Fatal(err) + } + + closeFn(ctx, s) + + s, err = New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo/baz"), + storage.MustParsePath("/foo/bar"), + storage.MustParsePath("/foo/qux"), + storage.MustParsePath("/foo/corge"), + }}) + if err == nil || !strings.Contains(err.Error(), "partitions are backwards incompatible (existing data: /foo/corge)") { + t.Fatal("expected to find existing key but got:", err) + } + + closeFn(ctx, s) + + s, err = New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo/baz"), + storage.MustParsePath("/foo/bar"), + storage.MustParsePath("/foo/qux"), + storage.MustParsePath("/foo/corge/grault"), + }}) + if err == nil || !strings.Contains(err.Error(), "partitions are backwards incompatible (existing data: /foo/corge)") { + t.Fatal("expected to find parent key but got:", err) + } + + closeFn(ctx, s) + + s, err = New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo/baz"), + storage.MustParsePath("/foo/bar"), + storage.MustParsePath("/foo/qux"), + storage.MustParsePath("/deadbeef"), + }}) + if err == nil || !strings.Contains(err.Error(), "partitions are backwards incompatible (existing data: /deadbeef)") { + t.Fatal("expected to find existing key but got:", err) + } + + closeFn(ctx, s) + + // switching to wildcard partition + s, err = New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo/*"), + }}) + if err != nil { + t.Fatal(err) + } + closeFn(ctx, s) + + // adding another partition + s, err = New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/fox/in/the/snow/*"), + storage.MustParsePath("/foo/*"), + }}) + if err != nil { + t.Fatal(err) + } + closeFn(ctx, s) + + // switching to a partition with multiple wildcards + s, err = New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/fox/in/*/*/*"), + storage.MustParsePath("/foo/*"), + }}) + if err != nil { + t.Fatal(err) + } + closeFn(ctx, s) + + // there is no going back + s, err = New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/fox/in/the/snow/*"), + storage.MustParsePath("/foo/*"), + }}) + if err == nil || !strings.Contains(err.Error(), + "partitions are backwards incompatible (old: [/foo/* /fox/in/*/*/* /system/*], new: [/foo/* /fox/in/the/snow/* /system/*], missing: [/fox/in/*/*/*])", + ) { + t.Fatal(err) + } + closeFn(ctx, s) + + // adding a wildcard partition requires no content on the non-wildcard prefix + // we open the db with previously used partitions, write another key, and + // re-open with an extra wildcard partition + // switching to a partition with multiple wildcards + s, err = New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/fox/in/*/*/*"), + storage.MustParsePath("/foo/*"), + }}) + if err != nil { + t.Fatal(err) + } + err = storage.WriteOne(ctx, s, storage.AddOp, storage.MustParsePath("/peanutbutter/jelly"), true) + if err != nil { + t.Fatal(err) + } + closeFn(ctx, s) + s, err = New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/fox/in/*/*/*"), + storage.MustParsePath("/peanutbutter/*"), + storage.MustParsePath("/foo/*"), + }}) + if err == nil || !strings.Contains(err.Error(), "partitions are backwards incompatible (existing data: /peanutbutter)") { + t.Fatal("expected to find existing key but got:", err) + } + closeFn(ctx, s) + }) +} + +func TestDataPartitioningSystemPartitions(t *testing.T) { + t.Parallel() + + ctx := context.Background() + dir := "unused" + + for _, part := range []string{ + "/system", + "/system/*", + "/system/a", + "/system/a/b", + } { + _, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath(part), + }}) + if err == nil || !strings.Contains(err.Error(), "system partitions are managed") { + t.Fatal(err) + } + } +} + +func TestDataPartitioningReadsAndWrites(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + partitions []string + sequence []any + }{ + { + note: "exact-match: add", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `"x"`, + }, + testRead{ + path: "/foo/bar", + exp: `"x"`, + }, + testCount{"/foo/bar", 1}, + }, + }, + { + note: "exact-match: add: multi-level", + partitions: []string{"/foo/bar"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar/baz", + value: `"x"`, + }, + testRead{ + path: "/foo/bar/baz", + exp: `"x"`, + }, + testCount{"/foo/bar/baz", 1}, + }, + }, + { + note: "exact-match: unpartitioned", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/deadbeef", + value: `"x"`, + }, + testRead{ + path: "/deadbeef", + exp: `"x"`, + }, + testCount{"/foo", 0}, + testCount{"/deadbeef", 1}, + }, + }, + { + note: "exact-match: remove", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `7`, + }, + testWrite{ + op: storage.AddOp, + path: "/foo/baz", + value: `8`, + }, + testWrite{ + op: storage.RemoveOp, + path: "/foo/bar", + }, + testRead{ + path: "/foo", + exp: `{"baz": 8}`, + }, + }, + }, + { + note: "read: sub-field", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `{"baz": 8}`, + }, + testRead{ + path: "/foo/bar/baz", + exp: `8`, + }, + testCount{"/foo/bar", 1}, + testCount{"/foo/bar/baz", 0}, + }, + }, + { + note: "read-modify-write: add", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `{"baz": 7}`, + }, + testWrite{ + op: storage.AddOp, + path: "/foo/bar/baz", + value: `8`, + }, + testRead{ + path: "/foo/bar", + exp: `{"baz": 8}`, + }, + }, + }, + { + note: "read-modify-write: add: unpartitioned", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/deadbeef", + value: `{"foo": 7}`, + }, + testWrite{ + op: storage.AddOp, + path: "/deadbeef/foo", + value: `8`, + }, + testRead{ + path: "/deadbeef", + exp: `{"foo": 8}`, + }, + }, + }, + { + note: "read-modify-write: add: array append", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `[]`, + }, + testWrite{ + op: storage.AddOp, + path: "/foo/bar/-", + value: `8`, + }, + testRead{ + path: "/foo/bar", + exp: `[8]`, + }, + }, + }, + { + note: "read-modify-write: add: array append (via last index)", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `[1]`, + }, + testWrite{ + op: storage.AddOp, + path: "/foo/bar/1", + value: `8`, + }, + testRead{ + path: "/foo/bar", + exp: `[1, 8]`, + }, + }, + }, + { + note: "read-modify-write: add: array insert", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `[7]`, + }, + testWrite{ + op: storage.AddOp, + path: "/foo/bar/0", + value: `8`, + }, + testRead{ + path: "/foo/bar", + exp: `[8, 7]`, + }, + }, + }, + { + note: "read-modify-write: replace", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `{"baz": 7}`, + }, + testWrite{ + op: storage.ReplaceOp, + path: "/foo/bar/baz", + value: `8`, + }, + testRead{ + path: "/foo/bar", + exp: `{"baz": 8}`, + }, + }, + }, + { + note: "read-modify-write: replace: array", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `[7]`, + }, + testWrite{ + op: storage.ReplaceOp, + path: "/foo/bar/0", + value: `8`, + }, + testRead{ + path: "/foo/bar", + exp: `[8]`, + }, + }, + }, + { + note: "read-modify-write: remove", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `{"baz": 7}`, + }, + testWrite{ + op: storage.RemoveOp, + path: "/foo/bar/baz", + }, + testRead{ + path: "/foo/bar", + exp: `{}`, + }, + }, + }, + { + note: "read-modify-write: remove: array", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `[7, 8]`, + }, + testWrite{ + op: storage.RemoveOp, + path: "/foo/bar/0", + }, + testRead{ + path: "/foo/bar", + exp: `[8]`, + }, + }, + }, + { + note: "read-modify-write: multi-level: map", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `{"baz": {"qux": {"corge": 7}}}`, + }, + testWrite{ + op: storage.ReplaceOp, + path: "/foo/bar/baz/qux/corge", + value: "8", + }, + testRead{ + path: "/foo/bar", + exp: `{"baz": {"qux": {"corge": 8}}}`, + }, + }, + }, + { + note: "read-modify-write: multi-level: array", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `{"baz": [{"qux": {"corge": 7}}]}`, + }, + testWrite{ + op: storage.ReplaceOp, + path: "/foo/bar/baz/0/qux/corge", + value: "8", + }, + testRead{ + path: "/foo/bar", + exp: `{"baz": [{"qux": {"corge": 8}}]}`, + }, + }, + }, + { + note: "prefix", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo", + value: `{"bar": 7, "baz": 8}`, + }, + + testCount{"/foo", 2}, + testRead{ + path: "/foo/bar", + exp: `7`, + }, + testRead{ + path: "/foo/baz", + exp: `8`, + }, + testRead{ + path: "/foo", + exp: `{"bar": 7, "baz": 8}`, + }, + }, + }, + { + note: "prefix: unpartitioned: root", + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/deadbeef", + value: `7`, + }, + testRead{ + path: "/", + exp: `{"deadbeef": 7}`, + }, + }, + }, + { + note: "prefix: unpartitioned: mixed", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/", + value: `{"foo": {"bar": 7, "baz": 8}, "deadbeef": 9}`, + }, + testRead{ + path: "/", + exp: `{"foo": {"bar": 7, "baz": 8}, "deadbeef": 9}`, + }, + testRead{ + path: "/foo/bar", + exp: `7`, + }, + testRead{ + path: "/foo/baz", + exp: `8`, + }, + testRead{ + path: "/foo", + exp: `{"bar": 7, "baz": 8}`, + }, + testRead{ + path: "/deadbeef", + exp: `9`, + }, + }, + }, + { + note: "prefix: overwrite", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/", + value: `{"foo": {"bar": 7, "baz": 8}, "deadbeef": 9}`, + }, + testWrite{ + op: storage.AddOp, + path: "/foo", + value: `{"qux": 10, "baz": 8}`, + }, + testRead{ + path: "/", + exp: `{"foo": {"qux": 10, "baz": 8}, "deadbeef": 9}`, + }, + }, + }, + { + note: "prefix: remove", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/", + value: `{ + "foo": { + "bar": 7, + "baz": 8 + }, + "deadbeef": 9 + }`, + }, + testWrite{ + op: storage.RemoveOp, + path: "/foo", + }, + testRead{ + path: "/", + exp: `{"deadbeef": 9}`, + }, + }, + }, + { + note: "issue-3711: string-to-number conversion", + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/", + value: `{ + "2": 7 + }`, + }, + testRead{ + path: "/2", + exp: `7`, + }, + }, + }, + { + note: "pattern partitions: middle wildcard: match", + partitions: []string{"/foo/*/bar"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/a/bar", + value: `{"baz": 7}`, + }, + testCount{"/foo/a/bar/baz", 1}, + testRead{"/foo/a/bar/baz", `7`}, + }, + }, + { + note: "pattern partitions: middle wildcard: no-match", + partitions: []string{"/foo/*/bar"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/b/baz", + value: `{"quz": 1}`, + }, + testCount{"/foo/b/baz", 1}, + testCount{"/foo/b/baz/quz", 0}, + testRead{"/foo/b/baz/quz", `1`}, + }, + }, + { + note: "pattern partitions: middle wildcard: partial match", + partitions: []string{"/foo/*/bar"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/b", + value: `{"bar": {"quz": 1}, "x": "y"}`, + }, + testCount{"/foo/b/bar/quz", 1}, + testRead{"/foo/b/bar/quz", `1`}, + testCount{"/foo/b/x", 1}, + testRead{"/foo/b/x", `"y"`}, + }, + }, + { + note: "pattern partitions: 2x middle wildcard: partial match", + partitions: []string{"/foo/*/*/bar"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/b/c", + value: `{"bar": {"quz": 1}, "x": "y"}`, + }, + testCount{"/foo/b/c/bar/quz", 1}, + testRead{"/foo/b/c/bar/quz", `1`}, + testCount{"/foo/b/c/x", 1}, + testRead{"/foo/b/c/x", `"y"`}, + }, + }, + { + note: "pattern partitions: wildcard at the end", + partitions: []string{"/users/*"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/users", + value: `{"alice": {"bar": {"quz": 1}, "x": "y"}}`, + }, + testWrite{ + op: storage.AddOp, + path: "/users/bob", + value: `{"baz": {"one": 1}, "y": "x"}`, + }, + testCount{"/users/alice/bar", 1}, + testRead{"/users/alice/bar/quz", `1`}, + testCount{"/users/alice/x", 1}, + testRead{"/users/alice/x", `"y"`}, + testCount{"/users/bob/baz", 1}, + testRead{"/users/bob/baz/one", `1`}, + testCount{"/users/bob/y", 1}, + testRead{"/users/bob/y", `"x"`}, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + test.WithTempFS(map[string]string{}, func(dir string) { + + partitions := make([]storage.Path, len(tc.partitions)) + for i := range partitions { + partitions[i] = storage.MustParsePath(tc.partitions[i]) + } + + ctx := context.Background() + s, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: partitions}) + if err != nil { + t.Fatal(err) + } + defer s.Close(ctx) + + for _, x := range tc.sequence { + switch x := x.(type) { + case testCount: + x.assert(t, s) + case testWrite: + executeTestWrite(ctx, t, s, x) + case testRead: + result, err := storage.ReadOne(ctx, s, storage.MustParsePath(x.path)) + if err != nil { + t.Fatal(err) + } + var exp any + if x.exp != "" { + exp = util.MustUnmarshalJSON([]byte(x.exp)) + } + if cmp := util.Compare(result, exp); cmp != 0 { + t.Fatalf("expected %v but got %v", x.exp, result) + } + case testDump: + x.do(t, s) + default: + panic("unexpected type") + } + } + }) + }) + } + +} + +func TestDataPartitioningReadNotFoundErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + partitions []string + sequence []any + }{ + { + note: "unpartitioned: key", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `"x"`, + }, + testRead{ + path: "/deadbeef", + }, + }, + }, + { + note: "unpartitioned: nested", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/deadbeef", + value: `{"x": 7}`, + }, + testRead{ + path: "/deadbeef/y", + }, + }, + }, + { + note: "unpartitioned: nested: 2-level", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/deadbeef", + value: `{"x": 7}`, + }, + testRead{ + path: "/deadbeef/x/y", + }, + }, + }, + { + note: "partitioned: key", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `"x"`, + }, + testRead{ + path: "/foo/baz", + }, + }, + }, + { + note: "partitioned: nested", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `{"x": 7}`, + }, + testRead{ + path: "/foo/bar/y", + }, + }, + }, + { + note: "partitioned: nested: 2-level", + partitions: []string{"/foo"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `{"x": 7}`, + }, + testRead{ + path: "/foo/bar/x/y", + }, + }, + }, + { + note: "partitioned: prefix", + partitions: []string{"/foo", "/bar"}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo/bar", + value: `{"x": 7}`, + }, + testRead{ + path: "/bar", + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + test.WithTempFS(map[string]string{}, func(dir string) { + + partitions := make([]storage.Path, len(tc.partitions)) + for i := range partitions { + partitions[i] = storage.MustParsePath(tc.partitions[i]) + } + + ctx := context.Background() + s, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: partitions}) + if err != nil { + t.Fatal(err) + } + defer s.Close(ctx) + + for _, x := range tc.sequence { + switch x := x.(type) { + case testWrite: + executeTestWrite(ctx, t, s, x) + case testRead: + _, err := storage.ReadOne(ctx, s, storage.MustParsePath(x.path)) + if err == nil { + t.Fatal("expected error") + } else if sErr, ok := err.(*storage.Error); !ok { + t.Fatal("expected storage error but got:", err) + } else if sErr.Code != storage.NotFoundErr { + t.Fatal("expected not found error but got:", err) + } + default: + panic("unexpected type") + } + } + }) + }) + } +} + +func TestDataPartitioningWriteNotFoundErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + partitions []string + sequence []any + }{ + { + note: "patch: remove: non-existent key", + partitions: []string{}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo", + value: `{}`, + }, + testWriteError{ + op: storage.RemoveOp, + path: "/foo/bar", + }, + }, + }, + { + note: "patch: replace: non-existent key", + partitions: []string{}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo", + value: `{}`, + }, + testWriteError{ + op: storage.ReplaceOp, + path: "/foo/bar", + value: `7`, + }, + }, + }, + { + note: "patch: scalar", + partitions: []string{}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo", + value: `{"bar": 7}`, + }, + testWriteError{ + op: storage.RemoveOp, + path: "/foo/bar/baz", + }, + }, + }, + { + note: "patch: array index", + partitions: []string{}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo", + value: `[1,2,3]`, + }, + testWriteError{ + op: storage.RemoveOp, + path: "/foo/7", + }, + }, + }, + { + note: "patch: array index: non-leaf", + partitions: []string{}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo", + value: `[{"bar": 7}, {"baz": 8}]`, + }, + testWriteError{ + op: storage.RemoveOp, + path: "/foo/7/bar", + }, + }, + }, + { + note: "patch: array: non-existent key", + partitions: []string{}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo", + value: `[{"bar": 7}, {"baz": 8}]`, + }, + testWriteError{ + op: storage.RemoveOp, + path: "/foo/1/bar", // index 1 contains baz not bar + }, + }, + }, + { + note: "patch: array: scalar", + partitions: []string{}, + sequence: []any{ + testWrite{ + op: storage.AddOp, + path: "/foo", + value: `7`, + }, + testWriteError{ + op: storage.RemoveOp, + path: "/foo/1/bar", + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + test.WithTempFS(map[string]string{}, func(dir string) { + + partitions := make([]storage.Path, len(tc.partitions)) + for i := range partitions { + partitions[i] = storage.MustParsePath(tc.partitions[i]) + } + + ctx := context.Background() + s, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: partitions}) + + if err != nil { + t.Fatal(err) + } + defer s.Close(ctx) + + for _, x := range tc.sequence { + switch x := x.(type) { + case testWrite: + executeTestWrite(ctx, t, s, x) + case testWriteError: + var val any + if x.value != "" { + val = util.MustUnmarshalJSON([]byte(x.value)) + } + err := storage.WriteOne(ctx, s, x.op, storage.MustParsePath(x.path), val) + if err == nil { + t.Fatal("expected error") + } else if sErr, ok := err.(*storage.Error); !ok { + t.Fatal("expected storage error but got:", err) + } else if sErr.Code != storage.NotFoundErr { + t.Fatal("expected not found error but got:", err) + } + default: + panic("unexpected type") + } + } + }) + }) + } +} + +func TestDataPartitioningWriteInvalidPatchError(t *testing.T) { + t.Parallel() + + for _, pt := range []string{"/*", "/foo"} { + t.Run(pt, func(t *testing.T) { + t.Parallel() + + test.WithTempFS(map[string]string{}, func(dir string) { + ctx := context.Background() + s, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo"), + }}) + if err != nil { + t.Fatal(err) + } + defer s.Close(ctx) + + err = storage.WriteOne(ctx, s, storage.AddOp, storage.MustParsePath("/foo"), util.MustUnmarshalJSON([]byte(`[1,2,3]`))) + if err == nil { + t.Fatal("expected error") + } else if sErr, ok := err.(*storage.Error); !ok { + t.Fatal("expected storage error but got:", err) + } else if sErr.Code != storage.InvalidPatchErr { + t.Fatal("expected invalid patch error but got:", err) + } + + err = storage.WriteOne(ctx, s, storage.AddOp, storage.MustParsePath("/"), util.MustUnmarshalJSON([]byte(`{"foo": [1,2,3]}`))) + if err == nil { + t.Fatal("expected error") + } else if sErr, ok := err.(*storage.Error); !ok { + t.Fatal("expected storage error but got:", err) + } else if sErr.Code != storage.InvalidPatchErr { + t.Fatal("expected invalid patch error but got:", err) + } + + err = storage.WriteOne(ctx, s, storage.AddOp, storage.MustParsePath("/"), util.MustUnmarshalJSON([]byte(`[1,2,3]`))) + if err == nil { + t.Fatal("expected error") + } else if sErr, ok := err.(*storage.Error); !ok { + t.Fatal("expected storage error but got:", err) + } else if sErr.Code != storage.InvalidPatchErr { + t.Fatal("expected invalid patch error but got:", err) + } + }) + }) + } +} + +func executeTestWrite(ctx context.Context, t *testing.T, s storage.Store, x testWrite) { + t.Helper() + var val any + if x.value != "" { + val = util.MustUnmarshalJSON([]byte(x.value)) + } + err := storage.WriteOne(ctx, s, x.op, storage.MustParsePath(x.path), val) + if err != nil { + t.Fatal(err) + } +} + +func TestDiskTriggers(t *testing.T) { + t.Parallel() + + test.WithTempFS(map[string]string{}, func(dir string) { + ctx := context.Background() + store, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo"), + }}) + if err != nil { + t.Fatal(err) + } + defer store.Close(ctx) + writeTxn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + readTxn := storage.NewTransactionOrDie(ctx, store) + + _, err = store.Register(ctx, readTxn, storage.TriggerConfig{ + OnCommit: func(context.Context, storage.Transaction, storage.TriggerEvent) {}, + }) + + if err == nil || !storage.IsInvalidTransaction(err) { + t.Fatalf("Expected transaction error: %v", err) + } + + store.Abort(ctx, readTxn) + + var event storage.TriggerEvent + modifiedPath := storage.MustParsePath("/a") + expectedValue := "hello" + + _, err = store.Register(ctx, writeTxn, storage.TriggerConfig{ + OnCommit: func(ctx context.Context, txn storage.Transaction, evt storage.TriggerEvent) { + result, err := store.Read(ctx, txn, modifiedPath) + if err != nil || !reflect.DeepEqual(result, expectedValue) { + t.Fatalf("Expected result to be hello for trigger read but got: %v (err: %v)", result, err) + } + event = evt + }, + }) + if err != nil { + t.Fatalf("Failed to register callback: %v", err) + } + + if err := store.Write(ctx, writeTxn, storage.ReplaceOp, modifiedPath, expectedValue); err != nil { + t.Fatalf("Unexpected write error: %v", err) + } + + id := "test" + data := []byte("package abc") + if err := store.UpsertPolicy(ctx, writeTxn, id, data); err != nil { + t.Fatalf("Unexpected upsert error: %v", err) + } + + if err := store.Commit(ctx, writeTxn); err != nil { + t.Fatalf("Unexpected commit error: %v", err) + } + + if event.IsZero() || !event.PolicyChanged() || !event.DataChanged() { + t.Fatalf("Expected policy and data change but got: %v", event) + } + + expData := storage.DataEvent{Path: modifiedPath, Data: expectedValue, Removed: false} + if d := event.Data[0]; !reflect.DeepEqual(expData, d) { + t.Fatalf("Expected data event %v, got %v", expData, d) + } + + expPolicy := storage.PolicyEvent{ID: id, Data: data, Removed: false} + if p := event.Policy[0]; !reflect.DeepEqual(expPolicy, p) { + t.Fatalf("Expected policy event %v, got %v", expPolicy, p) + } + }) +} + +func TestLookup(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + input []byte + path string + expected []byte + }{ + { + note: "empty path", + input: []byte(`{"hello": "world"}`), + path: "", + expected: []byte(`{"hello": "world"}`), + }, + { + note: "single path", + input: []byte(`{"a": {"b": {"c": "d"}}}`), + path: "a", + expected: []byte(`{"b": {"c": "d"}}`), + }, + { + note: "nested path-1", + input: []byte(`{"a": {"b": {"c": "d"}}}`), + path: "a/b", + expected: []byte(`{"c": "d"}`), + }, + { + note: "nested path-2", + input: []byte(`{"a": {"b": {"c": {"d": [1,2,3]}}}}`), + path: "a/b/c", + expected: []byte(`{"d": [1,2,3]}`), + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + path, ok := storage.ParsePathEscaped("/" + tc.path) + if !ok { + t.Fatalf("storage path invalid: %v", path) + } + + result, _, err := lookup(path, tc.input) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + switch v := result.(type) { + case map[string]json.RawMessage: + var obj map[string]json.RawMessage + err := util.Unmarshal(tc.expected, &obj) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if !reflect.DeepEqual(v, obj) { + t.Fatalf("Expected result %v, got %v", obj, result) + } + case json.RawMessage: + if !bytes.Equal(v, tc.expected) { + t.Fatalf("Expected result %v, got %v", tc.expected, result) + } + } + }) + } +} + +func TestDiskDiagnostics(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + t.Run("no partitions", func(t *testing.T) { + t.Parallel() + + test.WithTempFS(nil, func(dir string) { + buf := bytes.Buffer{} + logger := logging.New() + logger.SetOutput(&buf) + logger.SetLevel(logging.Debug) + store, err := New(ctx, logger, nil, Options{Dir: dir}) + if err != nil { + t.Fatal(err) + } + // store something, won't show up in the logs yet (they're calculated on startup only) + if err := storage.WriteOne(ctx, store, storage.AddOp, storage.MustParsePath("/foo"), util.MustUnmarshalJSON([]byte(`{"baz": 1000}`))); err != nil { + t.Fatal(err) + } + if err := storage.WriteOne(ctx, store, storage.AddOp, storage.MustParsePath("/bar"), util.MustUnmarshalJSON([]byte(`{"quz": 2000}`))); err != nil { + t.Fatal(err) + } + if err := store.Close(ctx); err != nil { + t.Fatal(err) + } + + expected := []string{ + `level=warning msg="no partitions configured"`, + `level=debug msg="partition /: key count: 0 (estimated size 0 bytes)"`, + } + for _, exp := range expected { + if !strings.Contains(buf.String(), exp) { + t.Errorf("expected string %q not found in logs", exp) + } + } + if t.Failed() { + t.Log("log output: ", buf.String()) + } + + // re-open + buf = bytes.Buffer{} + logger = logging.New() + logger.SetOutput(&buf) + logger.SetLevel(logging.Debug) + store, err = New(ctx, logger, nil, Options{Dir: dir}) + if err != nil { + t.Fatal(err) + } + if err := store.Close(ctx); err != nil { + t.Fatal(err) + } + + expected = []string{ + `level=debug msg="partition /: key count: 2 (estimated size 50 bytes)"`, + } + for _, exp := range expected { + if !strings.Contains(buf.String(), exp) { + t.Errorf("expected string %q not found in logs", exp) + } + } + if t.Failed() { + t.Log("log oputput: ", buf.String()) + } + }) + }) + + t.Run("two partitions", func(t *testing.T) { + t.Parallel() + + test.WithTempFS(nil, func(dir string) { + opts := Options{ + Dir: dir, + Partitions: []storage.Path{ + storage.MustParsePath("/foo"), + storage.MustParsePath("/bar"), + }} + buf := bytes.Buffer{} + logger := logging.New() + logger.SetOutput(&buf) + logger.SetLevel(logging.Debug) + store, err := New(ctx, logger, nil, opts) + if err != nil { + t.Fatal(err) + } + + // store something, won't show up in the logs yet (they're calculated on startup only) + if err := storage.WriteOne(ctx, store, storage.AddOp, storage.MustParsePath("/foo"), util.MustUnmarshalJSON([]byte(`{"baz": 1000}`))); err != nil { + t.Fatal(err) + } + + if err := store.Close(ctx); err != nil { + t.Fatal(err) + } + + expected := []string{ + `level=debug msg="partition /bar: key count: 0 (estimated size 0 bytes)"`, + `level=debug msg="partition /foo: key count: 0 (estimated size 0 bytes)"`, + } + for _, exp := range expected { + if !strings.Contains(buf.String(), exp) { + t.Errorf("expected string %q not found in logs", exp) + } + } + if t.Failed() { + t.Log("log oputput: ", buf.String()) + } + + // re-open + buf = bytes.Buffer{} + logger = logging.New() + logger.SetOutput(&buf) + logger.SetLevel(logging.Debug) + store, err = New(ctx, logger, nil, opts) + if err != nil { + t.Fatal(err) + } + if err := store.Close(ctx); err != nil { + t.Fatal(err) + } + + expected = []string{ + `level=debug msg="partition /bar: key count: 0 (estimated size 0 bytes)"`, + `level=debug msg="partition /foo: key count: 1 (estimated size 21 bytes)"`, + } + for _, exp := range expected { + if !strings.Contains(buf.String(), exp) { + t.Errorf("expected string %q not found in logs", exp) + } + } + if t.Failed() { + t.Log("log oputput: ", buf.String()) + } + }) + }) + + t.Run("patterned partitions", func(t *testing.T) { + t.Parallel() + + test.WithTempFS(nil, func(dir string) { + opts := Options{Dir: dir, + Partitions: []storage.Path{ + storage.MustParsePath("/foo/*/bar"), + storage.MustParsePath("/bar"), + }} + buf := bytes.Buffer{} + logger := logging.New() + logger.SetOutput(&buf) + logger.SetLevel(logging.Debug) + store, err := New(ctx, logger, nil, opts) + if err != nil { + t.Fatal(err) + } + + // store something, won't show up in the logs yet (they're calculated on startup only) + if err := storage.WriteOne(ctx, store, storage.AddOp, storage.MustParsePath("/foo/x/bar"), util.MustUnmarshalJSON([]byte(`{"baz": 1000}`))); err != nil { + t.Fatal(err) + } + if err := storage.WriteOne(ctx, store, storage.AddOp, storage.MustParsePath("/bar"), util.MustUnmarshalJSON([]byte(`{"quz": 1000}`))); err != nil { + t.Fatal(err) + } + + if err := store.Close(ctx); err != nil { + t.Fatal(err) + } + + expected := []string{ + `level=debug msg="partition /bar: key count: 0 (estimated size 0 bytes)"`, + `level=debug msg="partition pattern /foo/*/bar: key count: 0 (estimated size 0 bytes)"`, + } + for _, exp := range expected { + if !strings.Contains(buf.String(), exp) { + t.Errorf("expected string %q not found in logs", exp) + } + } + if t.Failed() { + t.Log("log oputput: ", buf.String()) + } + + // re-open + buf = bytes.Buffer{} + logger = logging.New() + logger.SetOutput(&buf) + logger.SetLevel(logging.Debug) + store, err = New(ctx, logger, nil, opts) + if err != nil { + t.Fatal(err) + } + if err := store.Close(ctx); err != nil { + t.Fatal(err) + } + + expected = []string{ + `level=debug msg="partition /bar: key count: 1 (estimated size 21 bytes)"`, + `level=debug msg="partition /foo/x/bar (pattern /foo/*/bar): key count: 1 (estimated size 27 bytes)"`, + } + for _, exp := range expected { + if !strings.Contains(buf.String(), exp) { + t.Errorf("expected string %q not found in logs", exp) + } + } + if t.Failed() { + t.Log("log oputput: ", buf.String()) + } + }) + }) +} diff --git a/third_party/opa/v1/storage/disk/errors.go b/third_party/opa/v1/storage/disk/errors.go new file mode 100644 index 000000000000..53102f7f04c5 --- /dev/null +++ b/third_party/opa/v1/storage/disk/errors.go @@ -0,0 +1,24 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk + +import ( + "github.com/open-policy-agent/opa/v1/storage" +) + +var errNotFound = &storage.Error{Code: storage.NotFoundErr} + +func wrapError(err error) error { + if err == nil { + return nil + } + if _, ok := err.(*storage.Error); ok { + return err + } + // NOTE(tsandall): we intentionally do not convert badger.ErrKeyNotFound to + // NotFoundErr code here because the former may not always need to be + // represented as a NotFoundErr (i.e., it may depend on the call-site.) + return &storage.Error{Code: storage.InternalErr, Message: err.Error()} +} diff --git a/third_party/opa/v1/storage/disk/example_test.go b/third_party/opa/v1/storage/disk/example_test.go new file mode 100644 index 000000000000..0e8f5d6c2805 --- /dev/null +++ b/third_party/opa/v1/storage/disk/example_test.go @@ -0,0 +1,86 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk_test + +import ( + "context" + "fmt" + "os" + + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/disk" + "github.com/open-policy-agent/opa/v1/util" +) + +func check(err error) { + if err != nil { + panic(err) + } +} + +func Example_store() { + + ctx := context.Background() + + // Create a temporary directory for store. + dir, err := os.MkdirTemp("", "opa_disk_example") + check(err) + + // Cleanup temporary directory after finishing. + defer os.RemoveAll(dir) + + // Create a new disk-based store. + store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + Partitions: []storage.Path{ + storage.MustParsePath("/authz/tenants"), + }, + }) + check(err) + + // Insert data into the store. The `storage.WriteOne` function automatically + // opens a write transaction, applies the operation, and commits the + // transaction in one-shot. + err = storage.WriteOne(ctx, store, storage.AddOp, storage.MustParsePath("/"), util.MustUnmarshalJSON([]byte(`{ + "authz": { + "tenants": { + "acmecorp.openpolicyagent.org": { + "tier": "gold" + }, + "globex.openpolicyagent.org" :{ + "tier": "silver" + } + } + } + }`))) + check(err) + + // Close the store so that it can be reopened. + err = store.Close(ctx) + check(err) + + // Re-create the disk-based store using the same options. + store2, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{ + Dir: dir, + Partitions: []storage.Path{ + storage.MustParsePath("/authz/tenants"), + }, + }) + check(err) + + // Read value persisted above and inspect the result. + value, err := storage.ReadOne(ctx, store2, storage.MustParsePath("/authz/tenants/acmecorp.openpolicyagent.org")) + check(err) + + err = store2.Close(ctx) + check(err) + + fmt.Println(value) + + // Output: + // + // map[tier:gold] +} diff --git a/third_party/opa/v1/storage/disk/metrics.go b/third_party/opa/v1/storage/disk/metrics.go new file mode 100644 index 000000000000..1991a4e63355 --- /dev/null +++ b/third_party/opa/v1/storage/disk/metrics.go @@ -0,0 +1,51 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +var ( + // storage read transactions never delete or write + keysReadPerStoreRead = newHist("keys_read_per_store_read_txn", "How many database reads had to occur for a storage read transaction") + bytesReadPerStoreRead = newHist("key_bytes_read_per_store_read_txn", "How many bytes of data were read for a storage read transaction") + + keysReadPerStoreWrite = newHist("keys_read_per_store_write_txn", "How many database reads had to occur for a storage write transaction") + keysWrittenPerStoreWrite = newHist("keys_written_per_store_write_txn", "How many database writes had to occur for a storage write transaction") + keysDeletedPerStoreWrite = newHist("keys_deleted_per_store_write_txn", "How many database writes had to occur for a storage write transaction") + bytesReadPerStoreWrite = newHist("key_bytes_read_per_store_write_txn", "How many bytes of data were read for a storage write transaction") +) + +func initPrometheus(reg prometheus.Registerer) error { + for _, hist := range []prometheus.Histogram{ + keysReadPerStoreRead, + bytesReadPerStoreRead, + keysReadPerStoreWrite, + keysWrittenPerStoreWrite, + keysDeletedPerStoreWrite, + bytesReadPerStoreWrite, + } { + if err := reg.Register(hist); err != nil { + return err + } + } + return nil +} + +func newHist(name, desc string) prometheus.Histogram { + return prometheus.NewHistogram(prometheus.HistogramOpts{ + Name: name, + Help: desc, + Buckets: prometheus.LinearBuckets(1, 1, 10), // TODO different buckets? exp? + }) +} + +func forwardMetric(m map[string]any, counter string, hist prometheus.Histogram) { + key := "counter_" + counter + if s, ok := m[key]; ok { + hist.Observe(float64(s.(uint64))) + } +} diff --git a/third_party/opa/v1/storage/disk/partition.go b/third_party/opa/v1/storage/disk/partition.go new file mode 100644 index 000000000000..d93bc33ef9bb --- /dev/null +++ b/third_party/opa/v1/storage/disk/partition.go @@ -0,0 +1,60 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk + +import ( + "github.com/open-policy-agent/opa/v1/storage" +) + +type partitionTrie struct { + partitions map[string]*partitionTrie +} + +func buildPartitionTrie(paths []storage.Path) *partitionTrie { + root := newPartitionTrie() + for i := range paths { + root.insert(paths[i]) + } + return root +} + +func newPartitionTrie() *partitionTrie { + return &partitionTrie{ + partitions: make(map[string]*partitionTrie), + } +} + +func (p *partitionTrie) Find(path storage.Path) (int, *partitionTrie) { + node := p + for i, x := range path { + next, ok := node.partitions[pathWildcard] + if ok { + node = next + continue + } + next, ok = node.partitions[x] + if !ok { + return i + 1, nil + } + node = next + } + return len(path), node +} + +func (p *partitionTrie) insert(path storage.Path) { + + if len(path) == 0 { + return + } + + head := path[0] + child, ok := p.partitions[head] + if !ok { + child = newPartitionTrie() + p.partitions[head] = child + } + + child.insert(path[1:]) +} diff --git a/third_party/opa/v1/storage/disk/partition_test.go b/third_party/opa/v1/storage/disk/partition_test.go new file mode 100644 index 000000000000..900fc9eb1106 --- /dev/null +++ b/third_party/opa/v1/storage/disk/partition_test.go @@ -0,0 +1,118 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk + +import ( + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/storage" +) + +func TestPartitionTrie(t *testing.T) { + t.Parallel() + + // Build simple trie + root := buildPartitionTrie([]storage.Path{ + storage.MustParsePath("/foo/bar"), + storage.MustParsePath("/foo/baz/qux"), + storage.MustParsePath("/corge"), + storage.MustParsePath("/tenants/*/bindings"), // wildcard in the middle + storage.MustParsePath("/users/*"), // wildcard at the end + }) + + // Assert on counts... + if exp, act := 4, len(root.partitions); exp != act { + t.Fatalf("expected root to contain %d partitions, got %d", exp, act) + } + + if len(root.partitions["foo"].partitions) != 2 { + t.Fatal("expected foo to contain two partitions") + } + + if len(root.partitions["foo"].partitions["baz"].partitions) != 1 { + t.Fatal("expected baz to contain one child") + } + + tests := []struct { + path string + wantIdx int + wantPtr *partitionTrie + }{ + { + path: "/", + wantIdx: 0, + wantPtr: root, + }, { + path: "/foo", + wantIdx: 1, + wantPtr: root.partitions["foo"], + }, { + path: "/foo/bar", + wantIdx: 2, + wantPtr: root.partitions["foo"].partitions["bar"], + }, { + path: "/foo/bar/baz", + wantIdx: 3, + wantPtr: nil, + }, { + path: "/foo/bar/baz/qux", + wantIdx: 3, + wantPtr: nil, + }, { + path: "/foo/baz", + wantIdx: 2, + wantPtr: root.partitions["foo"].partitions["baz"], + }, { + path: "/foo/baz/deadbeef", + wantIdx: 3, + wantPtr: nil, + }, { + path: "/foo/baz/qux", + wantIdx: 3, + wantPtr: root.partitions["foo"].partitions["baz"].partitions["qux"], + }, { + path: "/foo/baz/qux/deadbeef", + wantIdx: 4, + wantPtr: nil, + }, { + path: "/foo/corge", + wantIdx: 2, + wantPtr: nil, + }, { + path: "/deadbeef", + wantIdx: 1, + wantPtr: nil, + }, { + path: "/tenants/deadbeef/bindings/user01", + wantIdx: 4, + wantPtr: nil, + }, { + path: "/tenants/deadbeef/bindings", + wantIdx: 3, + wantPtr: root.partitions["tenants"].partitions["*"].partitions["bindings"], + }, { + path: "/tenants/deadbeef/foo", + wantIdx: 3, + wantPtr: nil, + }, { + path: "/users/deadbeef", + wantIdx: 2, + wantPtr: root.partitions["users"].partitions["*"], + }, + } + + for _, tc := range tests { + t.Run(strings.TrimPrefix(tc.path, "/"), func(t *testing.T) { + t.Parallel() + + gotIdx, gotPtr := root.Find(storage.MustParsePath(tc.path)) + if gotIdx != tc.wantIdx || gotPtr != tc.wantPtr { + t.Fatalf("expected (%d, %v) but got (%d, %v)", tc.wantIdx, tc.wantPtr, gotIdx, gotPtr) + } + }) + } + +} diff --git a/third_party/opa/v1/storage/disk/paths.go b/third_party/opa/v1/storage/disk/paths.go new file mode 100644 index 000000000000..799a676c935c --- /dev/null +++ b/third_party/opa/v1/storage/disk/paths.go @@ -0,0 +1,150 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk + +import ( + "fmt" + "slices" + "strings" + + "github.com/open-policy-agent/opa/v1/storage" +) + +const pathWildcard = "*" + +type pathMapper struct { + dataPrefix string + dataPrefixNoTrailingSlash string + policiesPrefix string +} + +func newPathMapper(schemaVersion, partitionVersion int64) *pathMapper { + var pm pathMapper + pm.dataPrefix = fmt.Sprintf("/%v/%v/data/", schemaVersion, partitionVersion) + pm.dataPrefixNoTrailingSlash = pm.dataPrefix[:len(pm.dataPrefix)-1] + pm.policiesPrefix = fmt.Sprintf("/%v/%v/policies/", schemaVersion, partitionVersion) + return &pm +} + +func (pm *pathMapper) PolicyKey2ID(key []byte) string { + return string(key[len(pm.policiesPrefix):]) +} + +func (pm *pathMapper) PolicyIDPrefix() []byte { + return []byte(pm.policiesPrefix) +} + +func (pm *pathMapper) PolicyID2Key(id string) []byte { + return []byte(pm.policiesPrefix + id) +} + +func (*pathMapper) DataKey2Path(key []byte) (storage.Path, error) { + p, ok := storage.ParsePathEscaped(string(key)) + if !ok { + return nil, &storage.Error{Code: storage.InternalErr, Message: fmt.Sprintf("corrupt key: %s", key)} + } + // skip /// + return p[3:], nil +} + +func (pm *pathMapper) DataPrefix2Key(path storage.Path) ([]byte, error) { + if len(path) == 0 { + return []byte(pm.dataPrefix), nil + } + return []byte(pm.dataPrefixNoTrailingSlash + path.String() + "/"), nil +} + +func (pm *pathMapper) DataPath2Key(path storage.Path) ([]byte, error) { + if len(path) == 0 { + return nil, &storage.Error{Code: storage.InternalErr, Message: "empty path"} + } + return []byte(pm.dataPrefixNoTrailingSlash + path.String()), nil +} + +type pathSet []storage.Path + +func (ps pathSet) String() string { + if len(ps) == 0 { + return "[]" + } + buf := strings.Builder{} + buf.WriteRune('[') + for j, p := range ps.Sorted() { + if j != 0 { + buf.WriteRune(' ') + } + buf.WriteString(toString(p)) + } + buf.WriteRune(']') + return buf.String() +} + +func (ps pathSet) IsDisjoint() bool { + for i := range ps { + for j := range ps { + if i != j { + if hasPrefixWithWildcard(ps[i], ps[j]) { + return false + } + } + } + } + return true +} + +// hasPrefixWithWildcard returns true if p starts with other; respecting +// wildcards +func hasPrefixWithWildcard(p, other storage.Path) bool { + if len(other) > len(p) { + return false + } + for i := range other { + if p[i] == pathWildcard || other[i] == pathWildcard { + continue + } + if p[i] != other[i] { + return false + } + } + return true +} + +// isMatchedBy returns true if p starts with other, or is matched by it +// respecting wildcards _in other_ -- not in p. +func isMatchedBy(p, other storage.Path) bool { + if len(other) != len(p) { + return false + } + for i := range other { + if other[i] == pathWildcard { + continue + } + if p[i] != other[i] { + return false + } + } + return true +} + +func (ps pathSet) Diff(other pathSet) pathSet { + diff := pathSet{} + for _, x := range ps { + if !other.Contains(x) { + diff = append(diff, x) + } + } + return diff +} + +func (ps pathSet) Contains(x storage.Path) bool { + return slices.ContainsFunc(ps, x.Equal) +} + +func (ps pathSet) Sorted() []storage.Path { + cpy := make(pathSet, len(ps)) + copy(cpy, ps) + slices.SortFunc(cpy, storage.Path.Compare) + return cpy +} diff --git a/third_party/opa/v1/storage/disk/paths_test.go b/third_party/opa/v1/storage/disk/paths_test.go new file mode 100644 index 000000000000..a808b173a7c2 --- /dev/null +++ b/third_party/opa/v1/storage/disk/paths_test.go @@ -0,0 +1,63 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/storage" +) + +func TestIsDisjoint(t *testing.T) { + t.Parallel() + + paths := func(ps ...string) pathSet { + ret := make([]storage.Path, len(ps)) + for i := range ps { + ret[i] = storage.MustParsePath(ps[i]) + } + return ret + } + + for _, tc := range []struct { + note string + ps pathSet + overlapped bool + }{ + { + note: "simple disjoint", + ps: paths("/foo", "/bar", "/baz"), + }, + { + note: "simple overlapping", + ps: paths("/foo", "/foo/bar"), + overlapped: true, + }, + { + note: "three overlapping", + ps: paths("/fox", "/foo/bar", "/foo"), + overlapped: true, + }, + { + note: "wildcard overlapping, last", + ps: paths("/foo", "/foo/*"), + overlapped: true, + }, + { + note: "wildcard overlapping, middle", + ps: paths("/foo/bar/baz", "/foo/*/baz"), + overlapped: true, + }, + } { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + act := tc.ps.IsDisjoint() + if !tc.overlapped != act { + t.Errorf("path set: %v, disjoint == %v, expected %v", tc.ps, act, !tc.overlapped) + } + }) + } +} diff --git a/third_party/opa/v1/storage/disk/txn.go b/third_party/opa/v1/storage/disk/txn.go new file mode 100644 index 000000000000..d3e76bad869f --- /dev/null +++ b/third_party/opa/v1/storage/disk/txn.go @@ -0,0 +1,606 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "slices" + "strconv" + + badger "github.com/dgraph-io/badger/v4" + + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/internal/errors" + "github.com/open-policy-agent/opa/v1/storage/internal/ptr" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + readValueBytesCounter = "disk_read_bytes" + readKeysCounter = "disk_read_keys" + writtenKeysCounter = "disk_written_keys" + deletedKeysCounter = "disk_deleted_keys" + + commitTimer = "disk_commit" + readTimer = "disk_read" + writeTimer = "disk_write" +) + +type transaction struct { + underlying *badger.Txn // handle for the underlying badger transaction + partitions *partitionTrie // index for partitioning structure in underlying store + pm *pathMapper // used for mapping between logical storage paths and actual storage keys + db *Store // handle for the database this transaction was created on + xid uint64 // unique id for this transaction + stale bool // bit to indicate if the transaction was already aborted/committed + write bool // bit to indicate if the transaction may perform writes + event storage.TriggerEvent // constructed as we go, supplied by the caller to be included in triggers + metrics metrics.Metrics // per-transaction metrics +} + +func newTransaction(xid uint64, write bool, underlying *badger.Txn, context *storage.Context, pm *pathMapper, trie *partitionTrie, db *Store) *transaction { + + // Even if the caller is not interested, these will contribute + // to the prometheus metrics on commit. + var m metrics.Metrics + if context != nil { + m = context.Metrics() + } + if m == nil { + m = metrics.New() + } + + return &transaction{ + underlying: underlying, + partitions: trie, + pm: pm, + db: db, + xid: xid, + stale: false, + write: write, + event: storage.TriggerEvent{ + Context: context, + }, + metrics: m, + } +} + +func (txn *transaction) ID() uint64 { + return txn.xid +} + +// Commit will commit the underlying transaction, and forward the per-transaction +// metrics into prometheus metrics. +// NOTE(sr): aborted transactions are not measured +func (txn *transaction) Commit(context.Context) (storage.TriggerEvent, error) { + txn.stale = true + txn.metrics.Timer(commitTimer).Start() + err := wrapError(txn.underlying.Commit()) + txn.metrics.Timer(commitTimer).Stop() + + if err != nil { + return txn.event, err + } + + m := txn.metrics.All() + if txn.write { + forwardMetric(m, readKeysCounter, keysReadPerStoreWrite) + forwardMetric(m, readKeysCounter, keysReadPerStoreWrite) + forwardMetric(m, writtenKeysCounter, keysWrittenPerStoreWrite) + forwardMetric(m, deletedKeysCounter, keysDeletedPerStoreWrite) + forwardMetric(m, readValueBytesCounter, bytesReadPerStoreWrite) + } else { + forwardMetric(m, readKeysCounter, keysReadPerStoreRead) + forwardMetric(m, readValueBytesCounter, bytesReadPerStoreRead) + } + return txn.event, nil +} + +func (txn *transaction) Abort(context.Context) { + txn.stale = true + txn.underlying.Discard() +} + +func (txn *transaction) Read(ctx context.Context, path storage.Path) (any, error) { + txn.metrics.Timer(readTimer).Start() + defer txn.metrics.Timer(readTimer).Stop() + + i, node := txn.partitions.Find(path) + + if node == nil { + key, err := txn.pm.DataPath2Key(path[:i]) + if err != nil { + return nil, err + } + + value, err := txn.readOne(key) + if err != nil { + return nil, err + } + + return ptr.Ptr(value, path[i:]) + } + + key, err := txn.pm.DataPrefix2Key(path[:i]) + if err != nil { + return nil, err + } + + return txn.readMultiple(ctx, i, key) +} + +func (txn *transaction) readMultiple(ctx context.Context, offset int, prefix []byte) (any, error) { + + result := map[string]any{} + + it := txn.underlying.NewIterator(badger.IteratorOptions{Prefix: prefix}) + defer it.Close() + + var keybuf, valbuf []byte + var count uint64 + + for it.Rewind(); it.Valid(); it.Next() { + if ctx.Err() != nil { + return nil, ctx.Err() + } + + count++ + + keybuf = it.Item().KeyCopy(keybuf) + path, err := txn.pm.DataKey2Path(keybuf) + if err != nil { + return nil, err + } + + valbuf, err = it.Item().ValueCopy(valbuf) + if err != nil { + return nil, wrapError(err) + } + txn.metrics.Counter(readValueBytesCounter).Add(uint64(len(valbuf))) + + var value any + if err := deserialize(valbuf, &value); err != nil { + return nil, err + } + + node := result + + for i := offset; i < len(path)-1; i++ { + child, ok := node[path[i]] + if !ok { + child = map[string]any{} + node[path[i]] = child + } + childObj, ok := child.(map[string]any) + if !ok { + return nil, &storage.Error{Code: storage.InternalErr, Message: fmt.Sprintf("corrupt key-value: %s", keybuf)} + } + node = childObj + } + + node[path[len(path)-1]] = value + } + + txn.metrics.Counter(readKeysCounter).Add(count) + + if len(result) == 0 { + return nil, errNotFound + } + + return result, nil +} + +func (txn *transaction) readOne(key []byte) (any, error) { + txn.metrics.Counter(readKeysCounter).Add(1) + + item, err := txn.underlying.Get(key) + if err != nil { + if err == badger.ErrKeyNotFound { + return nil, errNotFound + } + return nil, wrapError(err) + } + + var val any + + err = item.Value(func(bs []byte) error { + txn.metrics.Counter(readValueBytesCounter).Add(uint64(len(bs))) + return deserialize(bs, &val) + }) + + return val, wrapError(err) +} + +type update struct { + key []byte + value []byte + data any + delete bool +} + +func (txn *transaction) Write(_ context.Context, op storage.PatchOp, path storage.Path, value any) error { + txn.metrics.Timer(writeTimer).Start() + defer txn.metrics.Timer(writeTimer).Stop() + + updates, err := txn.partitionWrite(op, path, value) + if err != nil { + return err + } + + for _, u := range updates { + if u.delete { + if err := txn.underlying.Delete(u.key); err != nil { + return err + } + txn.metrics.Counter(deletedKeysCounter).Add(1) + } else { + if err := txn.underlying.Set(u.key, u.value); err != nil { + return err + } + txn.metrics.Counter(writtenKeysCounter).Add(1) + } + + txn.event.Data = append(txn.event.Data, storage.DataEvent{ + Path: path, // ? + Data: u.data, // nil if delete == true + Removed: u.delete, + }) + } + return nil +} + +func (txn *transaction) partitionWrite(op storage.PatchOp, path storage.Path, value any) ([]update, error) { + + if op == storage.RemoveOp && len(path) == 0 { + return nil, &storage.Error{ + Code: storage.InvalidPatchErr, + Message: "root cannot be removed", + } + } + + i, node := txn.partitions.Find(path) + + if node == nil { + if len(path) < i { + panic("unreachable") + } + + if len(path) == i { + return txn.partitionWriteOne(op, path, value) + } + + key, err := txn.pm.DataPath2Key(path[:i]) + if err != nil { + return nil, err + } + + curr, err := txn.readOne(key) + if err != nil && err != errNotFound { + return nil, err + } + + modified, err := patch(curr, op, path, i, value) + if err != nil { + return nil, err + } + + bs, err := serialize(modified) + if err != nil { + return nil, err + } + return []update{{key: key, value: bs, data: modified}}, nil + } + + key, err := txn.pm.DataPrefix2Key(path) + if err != nil { + return nil, err + } + + it := txn.underlying.NewIterator(badger.IteratorOptions{Prefix: key}) + defer it.Close() + + var updates []update + + for it.Rewind(); it.Valid(); it.Next() { + updates = append(updates, update{key: it.Item().KeyCopy(nil), delete: true}) + txn.metrics.Counter(readKeysCounter).Add(1) + } + + if op == storage.RemoveOp { + return updates, nil + } + + return txn.partitionWriteMultiple(node, path, value, updates) +} + +func (txn *transaction) partitionWriteMultiple(node *partitionTrie, path storage.Path, value any, result []update) ([]update, error) { + // NOTE(tsandall): value must be an object so that it can be partitioned; in + // the future, arrays could be supported but that requires investigation. + + switch v := value.(type) { + case map[string]any: + bs, err := serialize(v) + if err != nil { + return nil, err + } + return txn.doPartitionWriteMultiple(node, path, bs, result) + case map[string]json.RawMessage: + bs, err := serialize(v) + if err != nil { + return nil, err + } + return txn.doPartitionWriteMultiple(node, path, bs, result) + case json.RawMessage: + return txn.doPartitionWriteMultiple(node, path, v, result) + case []uint8: + return txn.doPartitionWriteMultiple(node, path, v, result) + } + + return nil, &storage.Error{Code: storage.InvalidPatchErr, Message: "value cannot be partitioned"} +} + +func (txn *transaction) doPartitionWriteMultiple(node *partitionTrie, path storage.Path, bs []byte, result []update) ([]update, error) { + var obj map[string]json.RawMessage + err := util.Unmarshal(bs, &obj) + if err != nil { + return nil, &storage.Error{Code: storage.InvalidPatchErr, Message: "value cannot be partitioned"} + } + + for k, v := range obj { + child := append(path, k) + next, ok := node.partitions[k] + if !ok { // try wildcard + next, ok = node.partitions[pathWildcard] + } + if ok { + var err error + result, err = txn.partitionWriteMultiple(next, child, v, result) + if err != nil { + return nil, err + } + continue + } + + key, err := txn.pm.DataPath2Key(child) + if err != nil { + return nil, err + } + bs, err := serialize(v) + if err != nil { + return nil, err + } + result = append(result, update{key: key, value: bs, data: v}) + } + + return result, nil +} + +func (txn *transaction) partitionWriteOne(op storage.PatchOp, path storage.Path, value any) ([]update, error) { + key, err := txn.pm.DataPath2Key(path) + if err != nil { + return nil, err + } + + if op == storage.RemoveOp { + return []update{{key: key, delete: true}}, nil + } + + val, err := serialize(value) + if err != nil { + return nil, err + } + + return []update{{key: key, value: val, data: value}}, nil +} + +func (txn *transaction) ListPolicies(ctx context.Context) ([]string, error) { + + var result []string + + it := txn.underlying.NewIterator(badger.IteratorOptions{ + Prefix: txn.pm.PolicyIDPrefix(), + }) + + defer it.Close() + + var key []byte + + for it.Rewind(); it.Valid(); it.Next() { + if ctx.Err() != nil { + return nil, ctx.Err() + } + txn.metrics.Counter(readKeysCounter).Add(1) + item := it.Item() + key = item.KeyCopy(key) + result = append(result, txn.pm.PolicyKey2ID(key)) + } + + return result, nil +} + +func (txn *transaction) GetPolicy(_ context.Context, id string) ([]byte, error) { + txn.metrics.Counter(readKeysCounter).Add(1) + item, err := txn.underlying.Get(txn.pm.PolicyID2Key(id)) + if err != nil { + if err == badger.ErrKeyNotFound { + return nil, errors.NewNotFoundErrorf("policy id %q", id) + } + return nil, err + } + bs, err := item.ValueCopy(nil) + txn.metrics.Counter(readValueBytesCounter).Add(uint64(len(bs))) + return bs, wrapError(err) +} + +func (txn *transaction) UpsertPolicy(_ context.Context, id string, bs []byte) error { + if err := txn.underlying.Set(txn.pm.PolicyID2Key(id), bs); err != nil { + return wrapError(err) + } + txn.metrics.Counter(writtenKeysCounter).Add(1) + txn.event.Policy = append(txn.event.Policy, storage.PolicyEvent{ + ID: id, + Data: bs, + }) + return nil +} + +func (txn *transaction) DeletePolicy(_ context.Context, id string) error { + if err := txn.underlying.Delete(txn.pm.PolicyID2Key(id)); err != nil { + return wrapError(err) + } + txn.metrics.Counter(deletedKeysCounter).Add(1) + txn.event.Policy = append(txn.event.Policy, storage.PolicyEvent{ + ID: id, + Removed: true, + }) + return nil +} + +func serialize(value any) ([]byte, error) { + val, ok := value.([]byte) + if ok { + return val, nil + } + + bs, err := json.Marshal(value) + return bs, wrapError(err) +} + +func deserialize(bs []byte, result any) error { + d := util.NewJSONDecoder(bytes.NewReader(bs)) + return wrapError(d.Decode(&result)) +} + +func patch(data any, op storage.PatchOp, path storage.Path, idx int, value any) (any, error) { + if idx == len(path) { + panic("unreachable") + } + + val := value + switch v := value.(type) { + case json.RawMessage: + var obj map[string]json.RawMessage + err := util.Unmarshal(v, &obj) + if err == nil { + val = obj + } else { + var obj any + err := util.Unmarshal(v, &obj) + if err != nil { + return nil, err + } + val = obj + } + case []uint8: + var obj map[string]json.RawMessage + err := util.Unmarshal(v, &obj) + if err == nil { + val = obj + } else { + var obj any + err := util.Unmarshal(v, &obj) + if err != nil { + return nil, err + } + val = obj + } + } + + // Base case: mutate the data value in-place. + if len(path) == idx+1 { // last element + switch x := data.(type) { + case map[string]any: + key := path[len(path)-1] + switch op { + case storage.RemoveOp: + if _, ok := x[key]; !ok { + return nil, errors.NewNotFoundError(path) + } + delete(x, key) + return x, nil + case storage.ReplaceOp: + if _, ok := x[key]; !ok { + return nil, errors.NewNotFoundError(path) + } + x[key] = val + return x, nil + case storage.AddOp: + x[key] = val + return x, nil + } + case []any: + switch op { + case storage.AddOp: + if path[idx] == "-" || path[idx] == strconv.Itoa(len(x)) { + return append(x, val), nil + } + i, err := ptr.ValidateArrayIndexForWrite(x, path[idx], idx, path) + if err != nil { + return nil, err + } + // insert at i + return append(x[:i], append([]any{val}, x[i:]...)...), nil + case storage.ReplaceOp: + i, err := ptr.ValidateArrayIndexForWrite(x, path[idx], idx, path) + if err != nil { + return nil, err + } + x[i] = val + return x, nil + case storage.RemoveOp: + i, err := ptr.ValidateArrayIndexForWrite(x, path[idx], idx, path) + if err != nil { + return nil, err + + } + return slices.Delete(x, i, i+1), nil // i is skipped + default: + panic("unreachable") + } + case nil: // data wasn't set before + return map[string]any{path[idx]: val}, nil + default: + return nil, errors.NewNotFoundError(path) + } + } + + // Recurse on the value located at the next part of the path. + key := path[idx] + + switch x := data.(type) { + case map[string]any: + modified, err := patch(x[key], op, path, idx+1, val) + if err != nil { + return nil, err + } + x[key] = modified + return x, nil + case []any: + i, err := ptr.ValidateArrayIndexForWrite(x, path[idx], idx+1, path) + if err != nil { + return nil, err + } + modified, err := patch(x[i], op, path, idx+1, val) + if err != nil { + return nil, err + } + x[i] = modified + return x, nil + case nil: // data isn't there yet + y := make(map[string]any, 1) + modified, err := patch(nil, op, path, idx+1, val) + if err != nil { + return nil, err + } + y[key] = modified + return y, nil + default: + return nil, errors.NewNotFoundError(path) + } +} diff --git a/third_party/opa/v1/storage/disk/txn_test.go b/third_party/opa/v1/storage/disk/txn_test.go new file mode 100644 index 000000000000..26d67655a63d --- /dev/null +++ b/third_party/opa/v1/storage/disk/txn_test.go @@ -0,0 +1,134 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package disk + +import ( + "context" + "errors" + "fmt" + "math/rand" + "testing" + + "github.com/dgraph-io/badger/v4" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func randomString(n int) string { + var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + + s := make([]rune, n) + for i := range s { + s[i] = letters[rand.Intn(len(letters))] + } + return string(s) +} + +func fixture(n int) map[string]any { + foo := map[string]string{} + for i := range n { + foo[fmt.Sprintf(`"%d%s"`, i, randomString(4))] = randomString(3) + } + return map[string]any{"foo": foo} +} + +func TestSetTxnIsTooBigToFitIntoOneRequestWhenUseDiskStoreReturnsError(t *testing.T) { + t.Parallel() + + test.WithTempFS(nil, func(dir string) { + ctx := context.Background() + s, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo"), + }}) + if err != nil { + t.Fatal(err) + } + + nbKeys := 140_000 // 135_000 is ok, but 140_000 not + jsonFixture := fixture(nbKeys) + err = storage.Txn(ctx, s, storage.WriteParams, func(txn storage.Transaction) error { + err := s.Write(ctx, txn, storage.AddOp, storage.MustParsePath("/"), jsonFixture) + if !errors.Is(err, badger.ErrTxnTooBig) { + t.Errorf("expected %v, got %v", badger.ErrTxnTooBig, err) + } + return err + }) + if !errors.Is(err, badger.ErrTxnTooBig) { + t.Errorf("expected %v, got %v", badger.ErrTxnTooBig, err) + } + + _, err = storage.ReadOne(ctx, s, storage.MustParsePath("/foo")) + var notFound *storage.Error + ok := errors.As(err, ¬Found) + if !ok { + t.Errorf("expected %T, got %v", notFound, err) + } + if exp, act := storage.NotFoundErr, notFound.Code; exp != act { + t.Errorf("expected code %v, got %v", exp, act) + } + }) + +} + +func TestDeleteTxnIsTooBigToFitIntoOneRequestWhenUseDiskStore(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + test.WithTempFS(nil, func(dir string) { + ctx := context.Background() + s, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: []storage.Path{ + storage.MustParsePath("/foo"), + }}) + if err != nil { + t.Fatal(err) + } + nbKeys := 200_000 + jsonFixture := fixture(nbKeys) + foo := jsonFixture["foo"].(map[string]string) + + // Write data in increments so we don't step over the too-large-txn limit + for k, v := range foo { + err := storage.WriteOne(ctx, s, storage.AddOp, storage.MustParsePath("/foo/"+k), v) + if err != nil { + t.Fatal(err) + } + } + + // check expected state + res, err := storage.ReadOne(ctx, s, storage.MustParsePath("/foo")) + if err != nil { + t.Fatal(err) + } + if exp, act := nbKeys, len(res.(map[string]any)); exp != act { + t.Fatalf("expected %d keys, read %d", exp, act) + } + + err = storage.Txn(ctx, s, storage.WriteParams, func(txn storage.Transaction) error { + err := s.Write(ctx, txn, storage.RemoveOp, storage.MustParsePath("/foo"), jsonFixture) + if !errors.Is(err, badger.ErrTxnTooBig) { + t.Errorf("expected %v, got %v", badger.ErrTxnTooBig, err) + } + return err + }) + if !errors.Is(err, badger.ErrTxnTooBig) { + t.Errorf("expected %v, got %v", badger.ErrTxnTooBig, err) + } + + // check expected state again + res, err = storage.ReadOne(ctx, s, storage.MustParsePath("/foo")) + if err != nil { + t.Fatal(err) + } + if exp, act := nbKeys, len(res.(map[string]any)); exp != act { + t.Fatalf("expected %d keys, read %d", exp, act) + } + + }) + +} diff --git a/third_party/opa/v1/storage/doc.go b/third_party/opa/v1/storage/doc.go new file mode 100644 index 000000000000..6fa2f86d98d4 --- /dev/null +++ b/third_party/opa/v1/storage/doc.go @@ -0,0 +1,6 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package storage exposes the policy engine's storage layer. +package storage diff --git a/third_party/opa/v1/storage/errors.go b/third_party/opa/v1/storage/errors.go new file mode 100644 index 000000000000..a3d1c007370a --- /dev/null +++ b/third_party/opa/v1/storage/errors.go @@ -0,0 +1,121 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + "fmt" +) + +const ( + // InternalErr indicates an unknown, internal error has occurred. + InternalErr = "storage_internal_error" + + // NotFoundErr indicates the path used in the storage operation does not + // locate a document. + NotFoundErr = "storage_not_found_error" + + // WriteConflictErr indicates a write on the path enocuntered a conflicting + // value inside the transaction. + WriteConflictErr = "storage_write_conflict_error" + + // InvalidPatchErr indicates an invalid patch/write was issued. The patch + // was rejected. + InvalidPatchErr = "storage_invalid_patch_error" + + // InvalidTransactionErr indicates an invalid operation was performed + // inside of the transaction. + InvalidTransactionErr = "storage_invalid_txn_error" + + // TriggersNotSupportedErr indicates the caller attempted to register a + // trigger against a store that does not support them. + TriggersNotSupportedErr = "storage_triggers_not_supported_error" + + // WritesNotSupportedErr indicate the caller attempted to perform a write + // against a store that does not support them. + WritesNotSupportedErr = "storage_writes_not_supported_error" + + // PolicyNotSupportedErr indicate the caller attempted to perform a policy + // management operation against a store that does not support them. + PolicyNotSupportedErr = "storage_policy_not_supported_error" +) + +// Error is the error type returned by the storage layer. +type Error struct { + Code string `json:"code"` + Message string `json:"message"` +} + +func (err *Error) Error() string { + if err.Message != "" { + return fmt.Sprintf("%v: %v", err.Code, err.Message) + } + return err.Code +} + +// IsNotFound returns true if this error is a NotFoundErr. +func IsNotFound(err error) bool { + if err, ok := err.(*Error); ok { + return err.Code == NotFoundErr + } + return false +} + +// IsWriteConflictError returns true if this error a WriteConflictErr. +func IsWriteConflictError(err error) bool { + switch err := err.(type) { + case *Error: + return err.Code == WriteConflictErr + } + return false +} + +// IsInvalidPatch returns true if this error is a InvalidPatchErr. +func IsInvalidPatch(err error) bool { + switch err := err.(type) { + case *Error: + return err.Code == InvalidPatchErr + } + return false +} + +// IsInvalidTransaction returns true if this error is a InvalidTransactionErr. +func IsInvalidTransaction(err error) bool { + switch err := err.(type) { + case *Error: + return err.Code == InvalidTransactionErr + } + return false +} + +// IsIndexingNotSupported is a stub for backwards-compatibility. +// +// Deprecated: We no longer return IndexingNotSupported errors, so it is +// unnecessary to check for them. +func IsIndexingNotSupported(error) bool { return false } + +func writeConflictError(path Path) *Error { + return &Error{ + Code: WriteConflictErr, + Message: path.String(), + } +} + +func triggersNotSupportedError() *Error { + return &Error{ + Code: TriggersNotSupportedErr, + } +} + +func writesNotSupportedError() *Error { + return &Error{ + Code: WritesNotSupportedErr, + } +} + +func policyNotSupportedError() *Error { + return &Error{ + Code: PolicyNotSupportedErr, + } +} diff --git a/third_party/opa/v1/storage/errors_test.go b/third_party/opa/v1/storage/errors_test.go new file mode 100644 index 000000000000..4bf8207cccb2 --- /dev/null +++ b/third_party/opa/v1/storage/errors_test.go @@ -0,0 +1,27 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import "testing" + +func TestIsNotFound(t *testing.T) { + err1 := &Error{ + Code: NotFoundErr, + Message: "", + } + + err2 := &Error{ + Code: InternalErr, + Message: "", + } + + if !IsNotFound(err1) { + t.Errorf("Expected err1 to be not found error") + } + + if IsNotFound(err2) { + t.Errorf("Did not expect err2 to be not found error") + } +} diff --git a/third_party/opa/v1/storage/inmem/ast.go b/third_party/opa/v1/storage/inmem/ast.go new file mode 100644 index 000000000000..941cbeef51d4 --- /dev/null +++ b/third_party/opa/v1/storage/inmem/ast.go @@ -0,0 +1,313 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package inmem + +import ( + "fmt" + "strconv" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/internal/errors" + "github.com/open-policy-agent/opa/v1/storage/internal/ptr" +) + +type updateAST struct { + path storage.Path // data path modified by update + remove bool // indicates whether update removes the value at path + value ast.Value // value to add/replace at path (ignored if remove is true) +} + +func (u *updateAST) Path() storage.Path { + return u.path +} + +func (u *updateAST) Remove() bool { + return u.remove +} + +func (u *updateAST) Set(v any) { + if v, ok := v.(ast.Value); ok { + u.value = v + } else { + panic("illegal value type") // FIXME: do conversion? + } +} + +func (u *updateAST) Value() any { + return u.value +} + +func (u *updateAST) Relative(path storage.Path) dataUpdate { + cpy := *u + cpy.path = cpy.path[len(path):] + return &cpy +} + +func (u *updateAST) Apply(v any) any { + if len(u.path) == 0 { + return u.value + } + + data, ok := v.(ast.Value) + if !ok { + panic(fmt.Errorf("illegal value type %T, expected ast.Value", v)) + } + + if u.remove { + newV, err := removeInAst(data, u.path) + if err != nil { + panic(err) + } + return newV + } + + // If we're not removing, we're replacing (adds are turned into replaces during updateAST creation). + newV, err := setInAst(data, u.path, u.value) + if err != nil { + panic(err) + } + return newV +} + +func newUpdateAST(data any, op storage.PatchOp, path storage.Path, idx int, value ast.Value) (*updateAST, error) { + + switch data.(type) { + case ast.Null, ast.Boolean, ast.Number, ast.String: + return nil, errors.NewNotFoundError(path) + } + + switch data := data.(type) { + case ast.Object: + return newUpdateObjectAST(data, op, path, idx, value) + + case *ast.Array: + return newUpdateArrayAST(data, op, path, idx, value) + } + + return nil, &storage.Error{ + Code: storage.InternalErr, + Message: "invalid data value encountered", + } +} + +func newUpdateArrayAST(data *ast.Array, op storage.PatchOp, path storage.Path, idx int, value ast.Value) (*updateAST, error) { + + if idx == len(path)-1 { + if path[idx] == "-" || path[idx] == strconv.Itoa(data.Len()) { + if op != storage.AddOp { + return nil, invalidPatchError("%v: invalid patch path", path) + } + + cpy := data.Append(ast.NewTerm(value)) + return &updateAST{path[:len(path)-1], false, cpy}, nil + } + + pos, err := ptr.ValidateASTArrayIndex(data, path[idx], path) + if err != nil { + return nil, err + } + + switch op { + case storage.AddOp: + var results []*ast.Term + for i := range data.Len() { + if i == pos { + results = append(results, ast.NewTerm(value)) + } + results = append(results, data.Elem(i)) + } + + return &updateAST{path[:len(path)-1], false, ast.NewArray(results...)}, nil + + case storage.RemoveOp: + var results []*ast.Term + for i := range data.Len() { + if i != pos { + results = append(results, data.Elem(i)) + } + } + return &updateAST{path[:len(path)-1], false, ast.NewArray(results...)}, nil + + default: + var results []*ast.Term + for i := range data.Len() { + if i == pos { + results = append(results, ast.NewTerm(value)) + } else { + results = append(results, data.Elem(i)) + } + } + + return &updateAST{path[:len(path)-1], false, ast.NewArray(results...)}, nil + } + } + + pos, err := ptr.ValidateASTArrayIndex(data, path[idx], path) + if err != nil { + return nil, err + } + + return newUpdateAST(data.Elem(pos).Value, op, path, idx+1, value) +} + +func newUpdateObjectAST(data ast.Object, op storage.PatchOp, path storage.Path, idx int, value ast.Value) (*updateAST, error) { + key := ast.InternedTerm(path[idx]) + val := data.Get(key) + + if idx == len(path)-1 { + switch op { + case storage.ReplaceOp, storage.RemoveOp: + if val == nil { + return nil, errors.NewNotFoundError(path) + } + } + return &updateAST{path, op == storage.RemoveOp, value}, nil + } + + if val != nil { + return newUpdateAST(val.Value, op, path, idx+1, value) + } + + return nil, errors.NewNotFoundError(path) +} + +func interfaceToValue(v any) (ast.Value, error) { + if v, ok := v.(ast.Value); ok { + return v, nil + } + return ast.InterfaceToValue(v) +} + +// setInAst updates the value in the AST at the given path with the given value. +// Values can only be replaced in arrays, not added. +// Values for new keys can be added to objects +func setInAst(data ast.Value, path storage.Path, value ast.Value) (ast.Value, error) { + if len(path) == 0 { + return data, nil + } + + switch data := data.(type) { + case ast.Object: + return setInAstObject(data, path, value) + case *ast.Array: + return setInAstArray(data, path, value) + default: + return nil, fmt.Errorf("illegal value type %T, expected ast.Object or ast.Array", data) + } +} + +func setInAstObject(obj ast.Object, path storage.Path, value ast.Value) (ast.Value, error) { + key := ast.InternedTerm(path[0]) + + if len(path) == 1 { + obj.Insert(key, ast.NewTerm(value)) + return obj, nil + } + + child := obj.Get(key) + newChild, err := setInAst(child.Value, path[1:], value) + if err != nil { + return nil, err + } + obj.Insert(key, ast.NewTerm(newChild)) + return obj, nil +} + +func setInAstArray(arr *ast.Array, path storage.Path, value ast.Value) (ast.Value, error) { + idx, err := strconv.Atoi(path[0]) + if err != nil { + return nil, fmt.Errorf("illegal array index %v: %v", path[0], err) + } + + if idx < 0 || idx >= arr.Len() { + return arr, nil + } + + if len(path) == 1 { + arr.Set(idx, ast.NewTerm(value)) + return arr, nil + } + + child := arr.Elem(idx) + newChild, err := setInAst(child.Value, path[1:], value) + if err != nil { + return nil, err + } + arr.Set(idx, ast.NewTerm(newChild)) + return arr, nil +} + +func removeInAst(value ast.Value, path storage.Path) (ast.Value, error) { + if len(path) == 0 { + return value, nil + } + + switch value := value.(type) { + case ast.Object: + return removeInAstObject(value, path) + case *ast.Array: + return removeInAstArray(value, path) + default: + return nil, fmt.Errorf("illegal value type %T, expected ast.Object or ast.Array", value) + } +} + +func removeInAstObject(obj ast.Object, path storage.Path) (ast.Value, error) { + key := ast.InternedTerm(path[0]) + + if len(path) == 1 { + var items [][2]*ast.Term + // Note: possibly expensive operation for large data. + obj.Foreach(func(k *ast.Term, v *ast.Term) { + if k.Equal(key) { + return + } + items = append(items, [2]*ast.Term{k, v}) + }) + return ast.NewObject(items...), nil + } + + if child := obj.Get(key); child != nil { + updatedChild, err := removeInAst(child.Value, path[1:]) + if err != nil { + return nil, err + } + obj.Insert(key, ast.NewTerm(updatedChild)) + } + + return obj, nil +} + +func removeInAstArray(arr *ast.Array, path storage.Path) (ast.Value, error) { + idx, err := strconv.Atoi(path[0]) + if err != nil { + // We expect the path to be valid at this point. + return arr, nil + } + + if idx < 0 || idx >= arr.Len() { + return arr, err + } + + if len(path) == 1 { + var elems []*ast.Term + // Note: possibly expensive operation for large data. + for i := range arr.Len() { + if i == idx { + continue + } + elems = append(elems, arr.Elem(i)) + } + return ast.NewArray(elems...), nil + } + + updatedChild, err := removeInAst(arr.Elem(idx).Value, path[1:]) + if err != nil { + return nil, err + } + arr.Set(idx, ast.NewTerm(updatedChild)) + return arr, nil +} diff --git a/third_party/opa/v1/storage/inmem/ast_test.go b/third_party/opa/v1/storage/inmem/ast_test.go new file mode 100644 index 000000000000..d442c53ddc35 --- /dev/null +++ b/third_party/opa/v1/storage/inmem/ast_test.go @@ -0,0 +1,200 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package inmem + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" +) + +func TestSetInAst(t *testing.T) { + tests := []struct { + note string + value string + path string + newValue string + expected string + }{ + { + note: "zero length path", + value: `{}`, + path: "/", + newValue: "42", + expected: "{}", + }, + { + note: "set object key", + value: `{"a": 1, "b": 2, "c": 3}`, + path: "/b", + newValue: "42", + expected: `{"a": 1, "b": 42, "c": 3}`, + }, + { + note: "set nested object key", + value: `{"a": {"b": 1, "c": 2, "d": 3}, "b": 4}`, + path: "/a/c", + newValue: "42", + expected: `{"a": {"b": 1, "c": 42, "d": 3}, "b": 4}`, + }, + // new keys can be added to objects + { + note: "add object key", + value: `{"a": 1, "b": 2, "c": 3}`, + path: "/d", + newValue: "42", + expected: `{"a": 1, "b": 2, "c": 3, "d": 42}`, + }, + { + note: "add nested object key", + value: `{"a": {"b": 1, "c": 2, "d": 3}, "b": 4}`, + path: "/a/e", + newValue: "42", + expected: `{"a": {"b": 1, "c": 2, "d": 3, "e": 42}, "b": 4}`, + }, + + { + note: "set array element", + value: `[1, 2, 3]`, + path: "/1", + newValue: "42", + expected: `[1, 42, 3]`, + }, + { + note: "set nested array element", + value: `[[1, 2], [3, 4], [5, 6]]`, + path: "/1/0", + newValue: "42", + expected: `[[1, 2], [42, 4], [5, 6]]`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + value := ast.MustParseTerm(tc.value).Value + path := storage.MustParsePath(tc.path) + newValue := ast.MustParseTerm(tc.newValue).Value + expected := ast.MustParseTerm(tc.expected).Value + + result, err := setInAst(value, path, newValue) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if expected.Compare(result) != 0 { + t.Fatalf("Expected:\n\n%v\n\nbut got:\n\n%v", expected, result) + } + + if result.Hash() != expected.Hash() { + t.Fatalf("Expected hash:\n\n%v\n\nbut got:\n\n%v", expected.Hash(), result.Hash()) + } + }) + } +} + +func TestRemoveInAst(t *testing.T) { + tests := []struct { + note string + value string + path string + expected string + }{ + { + note: "zero length path (no-op)", + value: `{"a": 1, "b": 2, "c": 3}`, + path: "/", + expected: `{"a": 1, "b": 2, "c": 3}`, + }, + { + note: "remove object key", + value: `{"a": 1, "b": 2, "c": 3}`, + path: "/b", + expected: `{"a": 1, "c": 3}`, + }, + { + note: "remove object key, no hit", + value: `{"a": 1, "b": 2, "c": 3}`, + path: "/d", + expected: `{"a": 1, "b": 2, "c": 3}`, + }, + { + note: "remove nested object key", + value: `{"a": {"b": 1, "c": 2, "d": 3}, "b": 4}`, + path: "/a/c", + expected: `{"a": {"b": 1, "d": 3}, "b": 4}`, + }, + { + note: "remove nested object key, no hit", + value: `{"a": {"b": 1, "c": 2, "d": 3}, "b": 4}`, + path: "/a/e", + expected: `{"a": {"b": 1, "c": 2, "d": 3}, "b": 4}`, + }, + + { + note: "remove array element", + value: `[1, 2, 3]`, + path: "/1", + expected: `[1, 3]`, + }, + { + note: "remove array element, no hit (over)", + value: `[1, 2, 3]`, + path: "/4", + expected: `[1, 2, 3]`, + }, + { + note: "remove array element, no hit (under)", + value: `[1, 2, 3]`, + path: "/-1", + expected: `[1, 2, 3]`, + }, + { + note: "remove nested array element", + value: `[[1, 2], [3, 4], [5, 6]]`, + path: "/1/0", + expected: `[[1, 2], [4], [5, 6]]`, + }, + { + note: "remove nested array element, no hit", + value: `[[1, 2], [3, 4], [5, 6]]`, + path: "/1/2", + expected: `[[1, 2], [3, 4], [5, 6]]`, + }, + { + note: "remove array element nested inside object", + value: `{"a": [1, 2, 3], "b": [4, 5, 6]}`, + path: "/a/1", + expected: `{"a": [1, 3], "b": [4, 5, 6]}`, + }, + { + note: "remove object key nested inside array", + value: `[{"a": 1, "b": 2}, {"a": 3, "b": 4}]`, + path: "/1/a", + expected: `[{"a": 1, "b": 2}, {"b": 4}]`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + value := ast.MustParseTerm(tc.value).Value + path := storage.MustParsePath(tc.path) + expected := ast.MustParseTerm(tc.expected).Value + + result, err := removeInAst(value, path) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if expected.Compare(result) != 0 { + t.Fatalf("Expected:\n\n%v\n\nbut got:\n\n%v", expected, result) + } + + if result.Hash() != expected.Hash() { + t.Fatalf("Expected hash:\n\n%v\n\nbut got:\n\n%v", expected.Hash(), result.Hash()) + } + }) + } +} diff --git a/third_party/opa/v1/storage/inmem/example_test.go b/third_party/opa/v1/storage/inmem/example_test.go new file mode 100644 index 000000000000..b366e0ca8bb8 --- /dev/null +++ b/third_party/opa/v1/storage/inmem/example_test.go @@ -0,0 +1,161 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//revive:disable:empty-block + +package inmem_test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" +) + +func Example_read() { + // Initialize context for the example. Normally the caller would obtain the + // context from an input parameter or instantiate their own. + ctx := context.Background() + + // Define some dummy data to initialize the built-in store with. + exampleInput := ` + { + "users": [ + { + "name": "alice", + "color": "red", + "likes": ["clouds", "ships"] + }, + { + "name": "burt", + "likes": ["cheese", "wine"] + } + ] + } + ` + + var data map[string]any + + // OPA uses Go's standard JSON library but assumes that numbers have been + // decoded as json.Number instead of float64. You MUST decode with UseNumber + // enabled. + decoder := json.NewDecoder(bytes.NewBufferString(exampleInput)) + decoder.UseNumber() + + if err := decoder.Decode(&data); err != nil { + // Handle error. + } + + // Instantiate the storage layer. + store := inmem.NewFromObject(data) + + txn, err := store.NewTransaction(ctx) + if err != nil { + // Handle error. + } + + // Cancel transaction because no writes are performed. + defer store.Abort(ctx, txn) + + // Read values out of storage. + v1, err1 := store.Read(ctx, txn, storage.MustParsePath("/users/1/likes/1")) + v2, err2 := store.Read(ctx, txn, storage.MustParsePath("/users/0/age")) + + // Inspect the return values. + fmt.Println("v1:", v1) + fmt.Println("err1:", err1) + fmt.Println("v2:", v2) + fmt.Println("err2:", err2) + fmt.Println("err2 is not found:", storage.IsNotFound(err2)) + + // Output: + // v1: wine + // err1: + // v2: + // err2: storage_not_found_error: /users/0/age: document does not exist + // err2 is not found: true +} + +func Example_write() { + // Initialize context for the example. Normally the caller would obtain the + // context from an input parameter or instantiate their own. + ctx := context.Background() + + // Define some dummy data to initialize the DataStore with. + exampleInput := ` + { + "users": [ + { + "name": "alice", + "color": "red", + "likes": ["clouds", "ships"] + }, + { + "name": "burt", + "likes": ["cheese", "wine"] + } + ] + } + ` + + var data map[string]any + + // OPA uses Go's standard JSON library but assumes that numbers have been + // decoded as json.Number instead of float64. You MUST decode with UseNumber + // enabled. + decoder := json.NewDecoder(bytes.NewBufferString(exampleInput)) + decoder.UseNumber() + + if err := decoder.Decode(&data); err != nil { + // Handle error. + } + + // Create the new store with the dummy data. + store := inmem.NewFromObject(data) + + // Define dummy data to add to the DataStore. + examplePatch := `{ + "longitude": 82.501389, + "latitude": -62.338889 + }` + + var patch any + + // See comment above regarding decoder usage. + decoder = json.NewDecoder(bytes.NewBufferString(examplePatch)) + decoder.UseNumber() + + if err := decoder.Decode(&patch); err != nil { + // Handle error. + } + + txn, err := store.NewTransaction(ctx, storage.WriteParams) + if err != nil { + // Handle error. + } + + // Write values into storage and read result. + err0 := store.Write(ctx, txn, storage.AddOp, storage.MustParsePath("/users/0/location"), patch) + v1, err1 := store.Read(ctx, txn, storage.MustParsePath("/users/0/location/latitude")) + err2 := store.Write(ctx, txn, storage.ReplaceOp, storage.MustParsePath("/users/1/color"), "red") + + // Inspect the return values. + fmt.Println("err0:", err0) + fmt.Println("v1:", v1) + fmt.Println("err1:", err1) + fmt.Println("err2:", err2) + + // Rollback transaction because write failed. + store.Abort(ctx, txn) + + // Output: + // err0: + // v1: -62.338889 + // err1: + // err2: storage_not_found_error: /users/1/color: document does not exist + +} diff --git a/third_party/opa/v1/storage/inmem/inmem.go b/third_party/opa/v1/storage/inmem/inmem.go new file mode 100644 index 000000000000..742d6c167f4b --- /dev/null +++ b/third_party/opa/v1/storage/inmem/inmem.go @@ -0,0 +1,460 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package inmem implements an in-memory version of the policy engine's storage +// layer. +// +// The in-memory store is used as the default storage layer implementation. The +// in-memory store supports multi-reader/single-writer concurrency with +// rollback. +// +// Callers should assume the in-memory store does not make copies of written +// data. Once data is written to the in-memory store, it should not be modified +// (outside of calling Store.Write). Furthermore, data read from the in-memory +// store should be treated as read-only. +package inmem + +import ( + "context" + "fmt" + "io" + "path/filepath" + "strings" + "sync" + "sync/atomic" + + "github.com/open-policy-agent/opa/internal/merge" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/util" +) + +// New returns an empty in-memory store. +func New() storage.Store { + return NewWithOpts() +} + +// NewWithOpts returns an empty in-memory store, with extra options passed. +func NewWithOpts(opts ...Opt) storage.Store { + s := &store{ + triggers: map[*handle]storage.TriggerConfig{}, + policies: map[string][]byte{}, + roundTripOnWrite: true, + returnASTValuesOnRead: false, + } + + for _, opt := range opts { + opt(s) + } + + if s.returnASTValuesOnRead { + s.data = ast.NewObject() + } else { + s.data = map[string]any{} + } + + return s +} + +// NewFromObject returns a new in-memory store from the supplied data object. +func NewFromObject(data map[string]any) storage.Store { + return NewFromObjectWithOpts(data) +} + +// NewFromObjectWithOpts returns a new in-memory store from the supplied data object, with the +// options passed. +func NewFromObjectWithOpts(data map[string]any, opts ...Opt) storage.Store { + db := NewWithOpts(opts...) + ctx := context.Background() + txn, err := db.NewTransaction(ctx, storage.WriteParams) + if err != nil { + panic(err) + } + if err := db.Write(ctx, txn, storage.AddOp, storage.Path{}, data); err != nil { + panic(err) + } + if err := db.Commit(ctx, txn); err != nil { + panic(err) + } + return db +} + +// NewFromReader returns a new in-memory store from a reader that produces a +// JSON serialized object. This function is for test purposes. +func NewFromReader(r io.Reader) storage.Store { + return NewFromReaderWithOpts(r) +} + +// NewFromReader returns a new in-memory store from a reader that produces a +// JSON serialized object, with extra options. This function is for test purposes. +func NewFromReaderWithOpts(r io.Reader, opts ...Opt) storage.Store { + d := util.NewJSONDecoder(r) + var data map[string]any + if err := d.Decode(&data); err != nil { + panic(err) + } + return NewFromObjectWithOpts(data, opts...) +} + +type store struct { + rmu sync.RWMutex // reader-writer lock + wmu sync.Mutex // writer lock + xid uint64 // last generated transaction id + data any // raw or AST data + policies map[string][]byte // raw policies + triggers map[*handle]storage.TriggerConfig // registered triggers + + // roundTripOnWrite, if true, means that every call to Write round trips the + // data through JSON before adding the data to the store. Defaults to true. + roundTripOnWrite bool + + // returnASTValuesOnRead, if true, means that the store will eagerly convert data to AST values, + // and return them on Read. + // FIXME: naming(?) + returnASTValuesOnRead bool +} + +type handle struct { + db *store +} + +func (db *store) NewTransaction(_ context.Context, params ...storage.TransactionParams) (storage.Transaction, error) { + var write bool + var ctx *storage.Context + if len(params) > 0 { + write = params[0].Write + ctx = params[0].Context + } + xid := atomic.AddUint64(&db.xid, uint64(1)) + if write { + db.wmu.Lock() + } else { + db.rmu.RLock() + } + return newTransaction(xid, write, ctx, db), nil +} + +// Truncate implements the storage.Store interface. This method must be called within a transaction. +func (db *store) Truncate(ctx context.Context, txn storage.Transaction, params storage.TransactionParams, it storage.Iterator) error { + var update *storage.Update + var err error + mergedData := map[string]any{} + + underlying, err := db.underlying(txn) + if err != nil { + return err + } + + for { + update, err = it.Next() + if err != nil { + break + } + + if update.IsPolicy { + err = underlying.UpsertPolicy(strings.TrimLeft(update.Path.String(), "/"), update.Value) + if err != nil { + return err + } + } else { + var value any + err = util.Unmarshal(update.Value, &value) + if err != nil { + return err + } + + var key []string + dirpath := strings.TrimLeft(update.Path.String(), "/") + if len(dirpath) > 0 { + key = strings.Split(dirpath, "/") + } + + if value != nil { + obj, err := mktree(key, value) + if err != nil { + return err + } + + merged, ok := merge.InterfaceMaps(mergedData, obj) + if !ok { + return fmt.Errorf("failed to insert data file from path %s", filepath.Join(key...)) + } + mergedData = merged + } + } + } + + // err is known not to be nil at this point, as it getting assigned + // a non-nil value is the only way the loop above can exit. + if err != io.EOF { + return err + } + + // For backwards compatibility, check if `RootOverwrite` was configured. + if params.RootOverwrite { + newPath, ok := storage.ParsePathEscaped("/") + if !ok { + return fmt.Errorf("storage path invalid: %v", newPath) + } + return underlying.Write(storage.AddOp, newPath, mergedData) + } + + for _, root := range params.BasePaths { + newPath, ok := storage.ParsePathEscaped("/" + root) + if !ok { + return fmt.Errorf("storage path invalid: %v", newPath) + } + + if value, ok := lookup(newPath, mergedData); ok { + if len(newPath) > 0 { + if err := storage.MakeDir(ctx, db, txn, newPath[:len(newPath)-1]); err != nil { + return err + } + } + if err := underlying.Write(storage.AddOp, newPath, value); err != nil { + return err + } + } + } + return nil +} + +func (db *store) Commit(ctx context.Context, txn storage.Transaction) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + if underlying.write { + db.rmu.Lock() + event := underlying.Commit() + db.runOnCommitTriggers(ctx, txn, event) + // Mark the transaction stale after executing triggers, so they can + // perform store operations if needed. + underlying.stale = true + db.rmu.Unlock() + db.wmu.Unlock() + } else { + db.rmu.RUnlock() + } + return nil +} + +func (db *store) Abort(_ context.Context, txn storage.Transaction) { + underlying, err := db.underlying(txn) + if err != nil { + panic(err) + } + underlying.stale = true + if underlying.write { + db.wmu.Unlock() + } else { + db.rmu.RUnlock() + } +} + +func (db *store) ListPolicies(_ context.Context, txn storage.Transaction) ([]string, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + return underlying.ListPolicies(), nil +} + +func (db *store) GetPolicy(_ context.Context, txn storage.Transaction, id string) ([]byte, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + return underlying.GetPolicy(id) +} + +func (db *store) UpsertPolicy(_ context.Context, txn storage.Transaction, id string, bs []byte) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + return underlying.UpsertPolicy(id, bs) +} + +func (db *store) DeletePolicy(_ context.Context, txn storage.Transaction, id string) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + if _, err := underlying.GetPolicy(id); err != nil { + return err + } + return underlying.DeletePolicy(id) +} + +func (db *store) Register(_ context.Context, txn storage.Transaction, config storage.TriggerConfig) (storage.TriggerHandle, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + if !underlying.write { + return nil, &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "triggers must be registered with a write transaction", + } + } + h := &handle{db} + db.triggers[h] = config + return h, nil +} + +func (db *store) Read(_ context.Context, txn storage.Transaction, path storage.Path) (any, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + + v, err := underlying.Read(path) + if err != nil { + return nil, err + } + + return v, nil +} + +func (db *store) Write(_ context.Context, txn storage.Transaction, op storage.PatchOp, path storage.Path, value any) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + val := util.Reference(value) + if db.roundTripOnWrite { + if err := util.RoundTrip(val); err != nil { + return err + } + } + return underlying.Write(op, path, *val) +} + +func (h *handle) Unregister(_ context.Context, txn storage.Transaction) { + underlying, err := h.db.underlying(txn) + if err != nil { + panic(err) + } + if !underlying.write { + panic(&storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "triggers must be unregistered with a write transaction", + }) + } + delete(h.db.triggers, h) +} + +func (db *store) runOnCommitTriggers(ctx context.Context, txn storage.Transaction, event storage.TriggerEvent) { + if db.returnASTValuesOnRead && len(db.triggers) > 0 { + // FIXME: Not very performant for large data. + + dataEvents := make([]storage.DataEvent, 0, len(event.Data)) + + for _, dataEvent := range event.Data { + if astData, ok := dataEvent.Data.(ast.Value); ok { + jsn, err := ast.ValueToInterface(astData, illegalResolver{}) + if err != nil { + panic(err) + } + dataEvents = append(dataEvents, storage.DataEvent{ + Path: dataEvent.Path, + Data: jsn, + Removed: dataEvent.Removed, + }) + } else { + dataEvents = append(dataEvents, dataEvent) + } + } + + event = storage.TriggerEvent{ + Policy: event.Policy, + Data: dataEvents, + Context: event.Context, + } + } + + for _, t := range db.triggers { + t.OnCommit(ctx, txn, event) + } +} + +type illegalResolver struct{} + +func (illegalResolver) Resolve(ref ast.Ref) (any, error) { + return nil, fmt.Errorf("illegal value: %v", ref) +} + +func (db *store) underlying(txn storage.Transaction) (*transaction, error) { + underlying, ok := txn.(*transaction) + if !ok { + return nil, &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: fmt.Sprintf("unexpected transaction type %T", txn), + } + } + if underlying.db != db { + return nil, &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "unknown transaction", + } + } + if underlying.stale { + return nil, &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "stale transaction", + } + } + return underlying, nil +} + +const rootMustBeObjectMsg = "root must be object" +const rootCannotBeRemovedMsg = "root cannot be removed" + +func invalidPatchError(f string, a ...any) *storage.Error { + return &storage.Error{ + Code: storage.InvalidPatchErr, + Message: fmt.Sprintf(f, a...), + } +} + +func mktree(path []string, value any) (map[string]any, error) { + if len(path) == 0 { + // For 0 length path the value is the full tree. + obj, ok := value.(map[string]any) + if !ok { + return nil, invalidPatchError(rootMustBeObjectMsg) + } + return obj, nil + } + + dir := map[string]any{} + for i := len(path) - 1; i > 0; i-- { + dir[path[i]] = value + value = dir + dir = map[string]any{} + } + dir[path[0]] = value + + return dir, nil +} + +func lookup(path storage.Path, data map[string]any) (any, bool) { + if len(path) == 0 { + return data, true + } + for i := range len(path) - 1 { + value, ok := data[path[i]] + if !ok { + return nil, false + } + obj, ok := value.(map[string]any) + if !ok { + return nil, false + } + data = obj + } + value, ok := data[path[len(path)-1]] + return value, ok +} diff --git a/third_party/opa/v1/storage/inmem/inmem_test.go b/third_party/opa/v1/storage/inmem/inmem_test.go new file mode 100644 index 000000000000..d385a1999d74 --- /dev/null +++ b/third_party/opa/v1/storage/inmem/inmem_test.go @@ -0,0 +1,1352 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package inmem + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "reflect" + "slices" + "testing" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + storageerrors "github.com/open-policy-agent/opa/v1/storage/internal/errors" + + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestInMemoryRead(t *testing.T) { + + data := loadSmallTestData() + + var tests = []struct { + path string + expected any + }{ + {"/a/0", json.Number("1")}, + {"/a/3", json.Number("4")}, + {"/b/v1", "hello"}, + {"/b/v2", "goodbye"}, + {"/c/0/x/1", false}, + {"/c/0/y/0", nil}, + {"/c/0/y/1", json.Number("3.14159")}, + {"/d/e/1", "baz"}, + {"/d/e", []any{"bar", "baz"}}, + {"/c/0/z", map[string]any{"p": true, "q": false}}, + {"/a/0/beef", storageerrors.NewNotFoundError(storage.MustParsePath("/a/0/beef"))}, + {"/d/100", storageerrors.NewNotFoundError(storage.MustParsePath("/d/100"))}, + {"/dead/beef", storageerrors.NewNotFoundError(storage.MustParsePath("/dead/beef"))}, + {"/a/str", storageerrors.NewNotFoundErrorWithHint(storage.MustParsePath("/a/str"), storageerrors.ArrayIndexTypeMsg)}, + {"/a/100", storageerrors.NewNotFoundErrorWithHint(storage.MustParsePath("/a/100"), storageerrors.OutOfRangeMsg)}, + {"/a/-1", storageerrors.NewNotFoundErrorWithHint(storage.MustParsePath("/a/-1"), storageerrors.OutOfRangeMsg)}, + } + + store := NewFromObject(data) + ctx := context.Background() + + for idx, tc := range tests { + result, err := storage.ReadOne(ctx, store, storage.MustParsePath(tc.path)) + switch e := tc.expected.(type) { + case error: + if err == nil { + t.Errorf("Test case %d: expected error for %v but got %v", idx+1, tc.path, result) + } else if !reflect.DeepEqual(err, tc.expected) { + t.Errorf("Test case %d: unexpected error for %v: %v, expected: %v", idx+1, tc.path, err, e) + } + default: + if err != nil { + t.Errorf("Test case %d: expected success for %v but got %v", idx+1, tc.path, err) + } + if !reflect.DeepEqual(result, tc.expected) { + t.Errorf("Test case %d: expected %f but got %f", idx+1, tc.expected, result) + } + } + } + +} + +func TestInMemoryReadAst(t *testing.T) { + + data := loadSmallTestData() + + var tests = []struct { + path string + expected any + }{ + {"/a/0", ast.Number("1")}, + {"/a/3", ast.Number("4")}, + {"/b/v1", ast.String("hello")}, + {"/b/v2", ast.String("goodbye")}, + {"/c/0/x/1", ast.Boolean(false)}, + {"/c/0/y/0", ast.Null{}}, + {"/c/0/y/1", ast.Number("3.14159")}, + {"/d/e/1", ast.String("baz")}, + {"/d/e", ast.NewArray(ast.StringTerm("bar"), ast.StringTerm("baz"))}, + {"/c/0/z", ast.NewObject(ast.Item(ast.StringTerm("p"), ast.BooleanTerm(true)), ast.Item(ast.StringTerm("q"), ast.BooleanTerm(false)))}, + {"/a/0/beef", storageerrors.NewNotFoundError(storage.MustParsePath("/a/0/beef"))}, + {"/d/100", storageerrors.NewNotFoundError(storage.MustParsePath("/d/100"))}, + {"/dead/beef", storageerrors.NewNotFoundError(storage.MustParsePath("/dead/beef"))}, + {"/a/str", storageerrors.NewNotFoundErrorWithHint(storage.MustParsePath("/a/str"), storageerrors.ArrayIndexTypeMsg)}, + {"/a/100", storageerrors.NewNotFoundErrorWithHint(storage.MustParsePath("/a/100"), storageerrors.OutOfRangeMsg)}, + {"/a/-1", storageerrors.NewNotFoundErrorWithHint(storage.MustParsePath("/a/-1"), storageerrors.OutOfRangeMsg)}, + } + + store := NewFromObjectWithOpts(data, OptReturnASTValuesOnRead(true)) + ctx := context.Background() + + for idx, tc := range tests { + result, err := storage.ReadOne(ctx, store, storage.MustParsePath(tc.path)) + switch e := tc.expected.(type) { + case error: + if err == nil { + t.Errorf("Test case %d: expected error for %v but got %v", idx+1, tc.path, result) + } else if !reflect.DeepEqual(err, tc.expected) { + t.Errorf("Test case %d: unexpected error for %v: %v, expected: %v", idx+1, tc.path, err, e) + } + default: + if err != nil { + t.Errorf("Test case %d: expected success for %v but got %v", idx+1, tc.path, err) + } + if ast.Compare(result, tc.expected) != 0 { + t.Errorf("Test case %d: expected %f but got %f", idx+1, tc.expected, result) + } + } + } +} + +func TestInMemoryWrite(t *testing.T) { + readValueType := []struct { + note string + ast bool + }{ + {"raw", false}, + {"ast", true}, + } + + for _, rvt := range readValueType { + t.Run(rvt.note, func(t *testing.T) { + tests := []struct { + note string + op string + path string + value string + expected error + getPath string + getExpected any + }{ + {"add root", "add", "/", `{"a": [1]}`, nil, "/", `{"a": [1]}`}, + {"add", "add", "/newroot", `{"a": [[1]]}`, nil, "/newroot", `{"a": [[1]]}`}, + {"add arr", "add", "/a/1", `"x"`, nil, "/a", `[1,"x",2,3,4]`}, + {"add arr/arr", "add", "/h/1/2", `"x"`, nil, "/h", `[[1,2,3], [2,3,"x",4]]`}, + {"add obj/arr", "add", "/d/e/1", `"x"`, nil, "/d", `{"e": ["bar", "x", "baz"]}`}, + {"add obj", "add", "/b/vNew", `"x"`, nil, "/b", `{"v1": "hello", "v2": "goodbye", "vNew": "x"}`}, + {"add obj (existing)", "add", "/b/v2", `"x"`, nil, "/b", `{"v1": "hello", "v2": "x"}`}, + + {"append arr", "add", "/a/-", `"x"`, nil, "/a", `[1,2,3,4,"x"]`}, + {"append arr-2", "add", "/a/4", `"x"`, nil, "/a", `[1,2,3,4,"x"]`}, + {"append obj/arr", "add", `/c/0/x/-`, `"x"`, nil, "/c/0/x", `[true,false,"foo","x"]`}, + {"append obj/arr-2", "add", `/c/0/x/3`, `"x"`, nil, "/c/0/x", `[true,false,"foo","x"]`}, + {"append arr/arr", "add", `/h/0/-`, `"x"`, nil, `/h/0/3`, `"x"`}, + {"append arr/arr-2", "add", `/h/0/3`, `"x"`, nil, `/h/0/3`, `"x"`}, + {"append err", "remove", "/c/0/x/-", "", invalidPatchError("/c/0/x/-: invalid patch path"), "", nil}, + {"append err-2", "replace", "/c/0/x/-", "", invalidPatchError("/c/0/x/-: invalid patch path"), "", nil}, + + {"remove", "remove", "/a", "", nil, "/a", storageerrors.NewNotFoundError(storage.MustParsePath("/a"))}, + {"remove arr", "remove", "/a/1", "", nil, "/a", "[1,3,4]"}, + {"remove obj/arr", "remove", "/c/0/x/1", "", nil, "/c/0/x", `[true,"foo"]`}, + {"remove arr/arr", "remove", "/h/0/1", "", nil, "/h/0", "[1,3]"}, + {"remove obj", "remove", "/b/v2", "", nil, "/b", `{"v1": "hello"}`}, + + {"replace root", "replace", "/", `{"a": [1]}`, nil, "/", `{"a": [1]}`}, + {"replace", "replace", "/a", "1", nil, "/a", "1"}, + {"replace obj", "replace", "/b/v1", "1", nil, "/b", `{"v1": 1, "v2": "goodbye"}`}, + {"replace array", "replace", "/a/1", "999", nil, "/a", "[1,999,3,4]"}, + + {"err: bad root type", "add", "/", "[1,2,3]", invalidPatchError(rootMustBeObjectMsg), "", nil}, + {"err: remove root", "remove", "/", "", invalidPatchError(rootCannotBeRemovedMsg), "", nil}, + {"err: add arr (non-integer)", "add", "/a/foo", "1", storageerrors.NewNotFoundErrorWithHint(storage.MustParsePath("/a/foo"), storageerrors.ArrayIndexTypeMsg), "", nil}, + {"err: add arr (non-integer)", "add", "/a/3.14", "1", storageerrors.NewNotFoundErrorWithHint(storage.MustParsePath("/a/3.14"), storageerrors.ArrayIndexTypeMsg), "", nil}, + {"err: add arr (out of range)", "add", "/a/5", "1", storageerrors.NewNotFoundErrorWithHint(storage.MustParsePath("/a/5"), storageerrors.OutOfRangeMsg), "", nil}, + {"err: add arr (out of range)", "add", "/a/-1", "1", storageerrors.NewNotFoundErrorWithHint(storage.MustParsePath("/a/-1"), storageerrors.OutOfRangeMsg), "", nil}, + {"err: add arr (missing root)", "add", "/dead/beef/0", "1", storageerrors.NewNotFoundError(storage.MustParsePath("/dead/beef/0")), "", nil}, + {"err: add non-coll", "add", "/a/1/2", "1", storageerrors.NewNotFoundError(storage.MustParsePath("/a/1/2")), "", nil}, + {"err: append (missing)", "add", `/dead/beef/-`, "1", storageerrors.NewNotFoundError(storage.MustParsePath("/dead/beef/-")), "", nil}, + {"err: append obj/arr", "add", `/c/0/deadbeef/-`, `"x"`, storageerrors.NewNotFoundError(storage.MustParsePath("/c/0/deadbeef/-")), "", nil}, + {"err: append arr/arr (out of range)", "add", `/h/9999/-`, `"x"`, storageerrors.NewNotFoundErrorWithHint(storage.MustParsePath("/h/9999/-"), storageerrors.OutOfRangeMsg), "", nil}, + {"err: append append+add", "add", `/a/-/b/-`, `"x"`, storageerrors.NewNotFoundErrorWithHint(storage.MustParsePath(`/a/-/b/-`), storageerrors.ArrayIndexTypeMsg), "", nil}, + {"err: append arr/arr (non-array)", "add", `/b/v1/-`, "1", storageerrors.NewNotFoundError(storage.MustParsePath("/b/v1/-")), "", nil}, + {"err: remove missing", "remove", "/dead/beef/0", "", storageerrors.NewNotFoundError(storage.MustParsePath("/dead/beef/0")), "", nil}, + {"err: remove obj (missing)", "remove", "/b/deadbeef", "", storageerrors.NewNotFoundError(storage.MustParsePath("/b/deadbeef")), "", nil}, + {"err: replace root (missing)", "replace", "/deadbeef", "1", storageerrors.NewNotFoundError(storage.MustParsePath("/deadbeef")), "", nil}, + {"err: replace missing", "replace", "/dead/beef/1", "1", storageerrors.NewNotFoundError(storage.MustParsePath("/dead/beef/1")), "", nil}, + } + + ctx := context.Background() + + for i, tc := range tests { + data := loadSmallTestData() + store := NewFromObjectWithOpts(data, OptReturnASTValuesOnRead(rvt.ast)) + + // Perform patch and check result + value := loadExpectedSortedResult(tc.value) + + var op storage.PatchOp + switch tc.op { + case "add": + op = storage.AddOp + case "remove": + op = storage.RemoveOp + case "replace": + op = storage.ReplaceOp + default: + panic(fmt.Sprintf("illegal value: %v", tc.op)) + } + + err := storage.WriteOne(ctx, store, op, storage.MustParsePath(tc.path), value) + if tc.expected == nil { + if err != nil { + t.Errorf("Test case %d (%v): unexpected patch error: %v", i+1, tc.note, err) + continue + } + } else { + if err == nil { + t.Errorf("Test case %d (%v): expected patch error, but got nil instead", i+1, tc.note) + continue + } + if err.Error() != tc.expected.Error() { + t.Errorf("Test case %d (%v): expected patch error %v but got: %v", i+1, tc.note, tc.expected, err) + continue + } + } + + if tc.getPath == "" { + continue + } + + // Perform get and verify result + result, err := storage.ReadOne(ctx, store, storage.MustParsePath(tc.getPath)) + switch expected := tc.getExpected.(type) { + case error: + if err == nil { + t.Errorf("Test case %d (%v): expected get error but got: %v", i+1, tc.note, result) + continue + } + if err.Error() != expected.Error() { + t.Errorf("Test case %d (%v): expected get error %v but got: %v", i+1, tc.note, expected, err) + continue + } + case string: + if err != nil { + t.Errorf("Test case %d (%v): unexpected get error: %v", i+1, tc.note, err) + continue + } + + if rvt.ast { + e := ast.MustParseTerm(expected) + + if ast.Compare(result, e.Value) != 0 { + t.Errorf("Test case %d (%v): expected get result %v but got: %v", i+1, tc.note, e, result) + } + } else { + e := loadExpectedResult(expected) + + if !reflect.DeepEqual(result, e) { + t.Errorf("Test case %d (%v): expected get result %v but got: %v", i+1, tc.note, e, result) + } + } + } + } + }) + } +} + +func TestInMemoryWriteOfStruct(t *testing.T) { + type B struct { + Bar int `json:"bar"` + } + + type A struct { + Foo *B `json:"foo"` + } + + cases := map[string]struct { + value any + expected string + }{ + "nested struct": {A{&B{10}}, `{"foo": {"bar": 10 } }`}, + "pointer to nested struct": {&A{&B{10}}, `{"foo": {"bar": 10 } }`}, + "pointer to pointer to nested struct": { + func() any { + a := &A{&B{10}} + return &a + }(), `{"foo": {"bar": 10 } }`}, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + store := New() + ctx := context.Background() + + err := storage.WriteOne(ctx, store, storage.AddOp, storage.MustParsePath("/x"), tc.value) + if err != nil { + t.Fatal(err) + } + + actual, err := storage.ReadOne(ctx, store, storage.MustParsePath("/x")) + if err != nil { + t.Fatal(err) + } + + expected := loadExpectedSortedResult(tc.expected) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, got %v", tc.expected, actual) + } + }) + } +} + +func TestInMemoryWriteOfStructAst(t *testing.T) { + type B struct { + Bar int `json:"bar"` + } + + type A struct { + Foo *B `json:"foo"` + } + + cases := map[string]struct { + value any + expected string + }{ + "nested struct": {A{&B{10}}, `{"foo": {"bar": 10 } }`}, + "pointer to nested struct": {&A{&B{10}}, `{"foo": {"bar": 10 } }`}, + "pointer to pointer to nested struct": { + func() any { + a := &A{&B{10}} + return &a + }(), `{"foo": {"bar": 10 } }`}, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + store := NewWithOpts(OptReturnASTValuesOnRead(true)) + ctx := context.Background() + + // Written non-AST values are expected to be converted to AST values + err := storage.WriteOne(ctx, store, storage.AddOp, storage.MustParsePath("/x"), tc.value) + if err != nil { + t.Fatal(err) + } + + actual, err := storage.ReadOne(ctx, store, storage.MustParsePath("/x")) + if err != nil { + t.Fatal(err) + } + + // We expect the result to be an AST value + expected := ast.MustParseTerm(tc.expected) + if ast.Compare(expected.Value, actual) != 0 { + t.Errorf("expected %v, got %v", tc.expected, actual) + } + }) + } +} + +func TestInMemoryTxnMultipleWrites(t *testing.T) { + + ctx := context.Background() + store := NewFromObject(loadSmallTestData()) + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + // Perform a sequence of writes and then verify the read results are the + // same for the writer during the transaction and the reader after the + // commit. + writes := []struct { + op storage.PatchOp + path string + value string + }{ + {storage.AddOp, "/a/-", "[]"}, + {storage.AddOp, "/a/4/-", "1"}, + {storage.AddOp, "/a/4/-", "2"}, + {storage.AddOp, "/a/4/2", "3"}, + {storage.AddOp, "/b/foo", "{}"}, + {storage.AddOp, "/b/foo/bar", "{}"}, + {storage.AddOp, "/b/foo/bar/baz", "1"}, + {storage.AddOp, "/arr", "[]"}, + {storage.AddOp, "/arr/-", "1"}, + {storage.AddOp, "/arr/0", "2"}, + {storage.AddOp, "/arr/2", "3"}, + {storage.AddOp, "/c/0/x/-", "0"}, + {storage.AddOp, "/_", "null"}, // introduce new txn.log head + {storage.AddOp, "/c/0", `"new c[0]"`}, + {storage.AddOp, "/c/1", `"new c[1]"`}, + {storage.AddOp, "/_head", "1"}, + {storage.AddOp, "/_head", "2"}, // invalidate the txn.log head + {storage.AddOp, "/d/f", `{"g": {"h": 0}}`}, + {storage.AddOp, "/d/f/g/i", `{"j": 1}`}, + } + + reads := []struct { + path string + expected string + }{ + {"/a", `[1,2,3,4,[1,2,3]]`}, + {"/b/foo", `{"bar": {"baz": 1}}`}, + {"/arr", `[2,1,3]`}, + {"/c/0", `"new c[0]"`}, + {"/c/1", `"new c[1]"`}, + {"/d/f", `{"g": {"h": 0, "i": {"j": 1}}}`}, + {"/d", `{"e": ["bar", "baz"], "f": {"g":{"h": 0, "i": {"j": 1}}}}`}, + {"/h/1/2", "4"}, + } + + for _, w := range writes { + var jsn any + if w.value != "" { + jsn = util.MustUnmarshalJSON([]byte(w.value)) + } + if err := store.Write(ctx, txn, w.op, storage.MustParsePath(w.path), jsn); err != nil { + t.Fatalf("Unexpected write error on %v: %v", w, err) + } + } + + for _, r := range reads { + jsn := util.MustUnmarshalJSON([]byte(r.expected)) + result, err := store.Read(ctx, txn, storage.MustParsePath(r.path)) + if err != nil || !reflect.DeepEqual(jsn, result) { + t.Fatalf("Expected writer's read %v to be %v but got: %v (err: %v)", r.path, jsn, result, err) + } + } + + if err := store.Commit(ctx, txn); err != nil { + t.Fatalf("Unexpected commit error: %v", err) + } + + txn = storage.NewTransactionOrDie(ctx, store) + + for _, r := range reads { + jsn := util.MustUnmarshalJSON([]byte(r.expected)) + result, err := store.Read(ctx, txn, storage.MustParsePath(r.path)) + if err != nil || !reflect.DeepEqual(jsn, result) { + t.Fatalf("Expected reader's read %v to be %v but got: %v (err: %v)", r.path, jsn, result, err) + } + } +} + +func TestInMemoryTxnMultipleWritesAst(t *testing.T) { + + ctx := context.Background() + store := NewFromObjectWithOpts(loadSmallTestData(), OptReturnASTValuesOnRead(true)) + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + // Perform a sequence of writes and then verify the read results are the + // same for the writer during the transaction and the reader after the + // commit. + writes := []struct { + op storage.PatchOp + path string + value string + }{ + {storage.AddOp, "/a/-", "[]"}, + {storage.AddOp, "/a/4/-", "1"}, + {storage.AddOp, "/a/4/-", "2"}, + {storage.AddOp, "/a/4/2", "3"}, + {storage.AddOp, "/b/foo", "{}"}, + {storage.AddOp, "/b/foo/bar", "{}"}, + {storage.AddOp, "/b/foo/bar/baz", "1"}, + {storage.AddOp, "/arr", "[]"}, + {storage.AddOp, "/arr/-", "1"}, + {storage.AddOp, "/arr/0", "2"}, + {storage.AddOp, "/arr/2", "3"}, + {storage.AddOp, "/c/0/x/-", "0"}, + {storage.AddOp, "/_", "null"}, // introduce new txn.log head + {storage.AddOp, "/c/0", `"new c[0]"`}, + {storage.AddOp, "/c/1", `"new c[1]"`}, + {storage.AddOp, "/_head", "1"}, + {storage.AddOp, "/_head", "2"}, // invalidate the txn.log head + {storage.AddOp, "/d/f", `{"g": {"h": 0}}`}, + {storage.AddOp, "/d/f/g/i", `{"j": 1}`}, + } + + reads := []struct { + path string + expected string + }{ + {"/a", `[1,2,3,4,[1,2,3]]`}, + {"/b/foo", `{"bar": {"baz": 1}}`}, + {"/arr", `[2,1,3]`}, + {"/c/0", `"new c[0]"`}, + {"/c/1", `"new c[1]"`}, + {"/d/f", `{"g": {"h": 0, "i": {"j": 1}}}`}, + {"/d", `{"e": ["bar", "baz"], "f": {"g":{"h": 0, "i": {"j": 1}}}}`}, + {"/h/1/2", "4"}, + } + + for _, w := range writes { + var jsn any + if w.value != "" { + jsn = util.MustUnmarshalJSON([]byte(w.value)) + } + if err := store.Write(ctx, txn, w.op, storage.MustParsePath(w.path), jsn); err != nil { + t.Fatalf("Unexpected write error on %v: %v", w, err) + } + } + + for _, r := range reads { + exp := ast.MustParseTerm(r.expected) + result, err := store.Read(ctx, txn, storage.MustParsePath(r.path)) + if err != nil || ast.Compare(exp.Value, result) != 0 { + t.Fatalf("Expected writer's read %v to be %v but got: %v (err: %v)", r.path, exp, result, err) + } + } + + if err := store.Commit(ctx, txn); err != nil { + t.Fatalf("Unexpected commit error: %v", err) + } + + txn = storage.NewTransactionOrDie(ctx, store) + + for _, r := range reads { + exp := ast.MustParseTerm(r.expected) + result, err := store.Read(ctx, txn, storage.MustParsePath(r.path)) + if err != nil || ast.Compare(exp.Value, result) != 0 { + t.Fatalf("Expected reader's read %v to be %v but got: %v (err: %v)", r.path, exp, result, err) + } + } +} + +func TestTruncateNoExistingPath(t *testing.T) { + cases := []struct { + note string + ast bool + }{ + {"raw", false}, + {"ast", true}, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := NewFromObjectWithOpts(map[string]any{}, OptReturnASTValuesOnRead(tc.ast)) + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + var archiveFiles = map[string]string{ + "/a/b/c/data.json": "[1,2,3]", + } + + files := make([][2]string, 0, len(archiveFiles)) + for name, content := range archiveFiles { + files = append(files, [2]string{name, content}) + } + + buf := archive.MustWriteTarGz(files) + b, err := bundle.NewReader(buf).WithLazyLoadingMode(true).Read() + if err != nil { + t.Fatal(err) + } + + iterator := bundle.NewIterator(b.Raw) + + params := storage.WriteParams + params.BasePaths = []string{""} + + err = store.Truncate(ctx, txn, params, iterator) + if err != nil { + t.Fatalf("Unexpected truncate error: %v", err) + } + + if err := store.Commit(ctx, txn); err != nil { + t.Fatalf("Unexpected commit error: %v", err) + } + + txn = storage.NewTransactionOrDie(ctx, store) + + actual, err := store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatal(err) + } + + expected := ` +{ + "a": { + "b": { + "c": [1,2,3] + } + } +} +` + if tc.ast { + exp := ast.MustParseTerm(expected) + + if ast.Compare(exp.Value, actual) != 0 { + t.Fatalf("Expected reader's read to be %v but got: %v", exp, actual) + } + } else { + jsn := util.MustUnmarshalJSON([]byte(expected)) + + if !reflect.DeepEqual(jsn, actual) { + t.Fatalf("Expected reader's read to be %v but got: %v", jsn, actual) + } + } + }) + } +} + +func TestTruncate(t *testing.T) { + ctx := context.Background() + store := NewFromObject(map[string]any{}) + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + var archiveFiles = map[string]string{ + "/a/b/c/data.json": "[1,2,3]", + "/a/b/d/data.json": "true", + "/data.json": `{"x": {"y": true}, "a": {"b": {"z": true}}}`, + "/a/b/y/data.yaml": `foo: 1`, + "/policy.rego": "package foo\n p = 1", + "/roles/policy.rego": "package bar\n p = 1", + } + + files := make([][2]string, 0, len(archiveFiles)) + for name, content := range archiveFiles { + files = append(files, [2]string{name, content}) + } + + buf := archive.MustWriteTarGz(files) + b, err := bundle.NewReader(buf).WithLazyLoadingMode(true).Read() + if err != nil { + t.Fatal(err) + } + + iterator := bundle.NewIterator(b.Raw) + + params := storage.WriteParams + params.BasePaths = []string{""} + + err = store.Truncate(ctx, txn, params, iterator) + if err != nil { + t.Fatalf("Unexpected truncate error: %v", err) + } + + if err := store.Commit(ctx, txn); err != nil { + t.Fatalf("Unexpected commit error: %v", err) + } + + txn = storage.NewTransactionOrDie(ctx, store) + + actual, err := store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatal(err) + } + + expected := ` +{ + "a": { + "b": { + "c": [1,2,3], + "d": true, + "y": { + "foo": 1 + }, + "z": true + } + }, + "x": { + "y": true + } +} +` + jsn := util.MustUnmarshalJSON([]byte(expected)) + + if !reflect.DeepEqual(jsn, actual) { + t.Fatalf("Expected reader's read to be %v but got: %v", jsn, actual) + } + + store.Abort(ctx, txn) + + txn = storage.NewTransactionOrDie(ctx, store) + ids, err := store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } + + expectedIDs := map[string]struct{}{"policy.rego": {}, "roles/policy.rego": {}} + + for _, id := range ids { + if _, ok := expectedIDs[id]; !ok { + t.Fatalf("Expected list policies to contain %v but got: %v", id, expectedIDs) + } + } + + bs, err := store.GetPolicy(ctx, txn, "policy.rego") + expectedBytes := []byte("package foo\n p = 1") + if err != nil || !bytes.Equal(expectedBytes, bs) { + t.Fatalf("Expected get policy to return %v but got: %v (err: %v)", expectedBytes, bs, err) + } + + bs, err = store.GetPolicy(ctx, txn, "roles/policy.rego") + expectedBytes = []byte("package bar\n p = 1") + if err != nil || !bytes.Equal(expectedBytes, bs) { + t.Fatalf("Expected get policy to return %v but got: %v (err: %v)", expectedBytes, bs, err) + } +} + +func TestTruncateAst(t *testing.T) { + ctx := context.Background() + store := NewFromObjectWithOpts(map[string]any{}, OptReturnASTValuesOnRead(true)) + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + var archiveFiles = map[string]string{ + "/a/b/c/data.json": "[1,2,3]", + "/a/b/d/data.json": "true", + "/data.json": `{"x": {"y": true}, "a": {"b": {"z": true}}}`, + "/a/b/y/data.yaml": `foo: 1`, + "/policy.rego": "package foo\n p = 1", + "/roles/policy.rego": "package bar\n p = 1", + } + + files := make([][2]string, 0, len(archiveFiles)) + for name, content := range archiveFiles { + files = append(files, [2]string{name, content}) + } + + buf := archive.MustWriteTarGz(files) + b, err := bundle.NewReader(buf).WithLazyLoadingMode(true).Read() + if err != nil { + t.Fatal(err) + } + + iterator := bundle.NewIterator(b.Raw) + + params := storage.WriteParams + params.BasePaths = []string{""} + + err = store.Truncate(ctx, txn, params, iterator) + if err != nil { + t.Fatalf("Unexpected truncate error: %v", err) + } + + if err := store.Commit(ctx, txn); err != nil { + t.Fatalf("Unexpected commit error: %v", err) + } + + txn = storage.NewTransactionOrDie(ctx, store) + + actual, err := store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatal(err) + } + + expected := ` +{ + "a": { + "b": { + "c": [1,2,3], + "d": true, + "y": { + "foo": 1 + }, + "z": true + } + }, + "x": { + "y": true + } +} +` + exp := ast.MustParseTerm(expected) + + if ast.Compare(exp.Value, actual) != 0 { + t.Fatalf("Expected reader's read to be %v but got: %v", exp, actual) + } + + store.Abort(ctx, txn) + + txn = storage.NewTransactionOrDie(ctx, store) + ids, err := store.ListPolicies(ctx, txn) + if err != nil { + t.Fatal(err) + } + + expectedIDs := map[string]struct{}{"policy.rego": {}, "roles/policy.rego": {}} + + for _, id := range ids { + if _, ok := expectedIDs[id]; !ok { + t.Fatalf("Expected list policies to contain %v but got: %v", id, expectedIDs) + } + } + + bs, err := store.GetPolicy(ctx, txn, "policy.rego") + expectedBytes := []byte("package foo\n p = 1") + if err != nil || !bytes.Equal(expectedBytes, bs) { + t.Fatalf("Expected get policy to return %v but got: %v (err: %v)", expectedBytes, bs, err) + } + + bs, err = store.GetPolicy(ctx, txn, "roles/policy.rego") + expectedBytes = []byte("package bar\n p = 1") + if err != nil || !bytes.Equal(expectedBytes, bs) { + t.Fatalf("Expected get policy to return %v but got: %v (err: %v)", expectedBytes, bs, err) + } +} + +func TestTruncateDataMergeError(t *testing.T) { + cases := []struct { + note string + ast bool + }{ + {"raw", false}, + {"ast", true}, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := NewFromObjectWithOpts(map[string]any{}, OptReturnASTValuesOnRead(tc.ast)) + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + var archiveFiles = map[string]string{ + "/a/b/data.json": `{"c": "foo"}`, + "/data.json": `{"a": {"b": {"c": "bar"}}}`, + } + + files := make([][2]string, 0, len(archiveFiles)) + for name, content := range archiveFiles { + files = append(files, [2]string{name, content}) + } + + buf := archive.MustWriteTarGz(files) + b, err := bundle.NewReader(buf).WithLazyLoadingMode(true).Read() + if err != nil { + t.Fatal(err) + } + + iterator := bundle.NewIterator(b.Raw) + + err = store.Truncate(ctx, txn, storage.WriteParams, iterator) + if err == nil { + t.Fatal("Expected truncate error but got nil") + } + + expected := "failed to insert data file from path a/b" + if err.Error() != expected { + t.Fatalf("Expected error %v but got %v", expected, err.Error()) + } + }) + } +} + +func TestTruncateBadRootWrite(t *testing.T) { + cases := []struct { + note string + ast bool + }{ + {"raw", false}, + {"ast", true}, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := NewFromObjectWithOpts(map[string]any{}, OptReturnASTValuesOnRead(tc.ast)) + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + var archiveFiles = map[string]string{ + "/a/b/d/data.json": "true", + "/data.json": "[1,2,3]", + "/roles/policy.rego": "package bar\n p = 1", + } + + files := make([][2]string, 0, len(archiveFiles)) + for name, content := range archiveFiles { + files = append(files, [2]string{name, content}) + } + + buf := archive.MustWriteTarGz(files) + b, err := bundle.NewReader(buf).WithLazyLoadingMode(true).Read() + if err != nil { + t.Fatal(err) + } + + iterator := bundle.NewIterator(b.Raw) + + err = store.Truncate(ctx, txn, storage.WriteParams, iterator) + if err == nil { + t.Fatal("Expected truncate error but got nil") + } + + expected := "storage_invalid_patch_error: root must be object" + if err.Error() != expected { + t.Fatalf("Expected error %v but got %v", expected, err.Error()) + } + }) + } +} + +func TestInMemoryTxnWriteFailures(t *testing.T) { + cases := []struct { + note string + ast bool + }{ + {"raw", false}, + {"ast", true}, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := NewFromObjectWithOpts(loadSmallTestData(), OptReturnASTValuesOnRead(tc.ast)) + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + writes := []struct { + op storage.PatchOp + path string + value string + errCode string + }{ + {storage.RemoveOp, "/c/0/y", "", ""}, + {storage.RemoveOp, "/c/0/y", "", storage.NotFoundErr}, + {storage.ReplaceOp, "/c/0/y/0", "", storage.NotFoundErr}, + {storage.AddOp, "/new", `{"foo": "bar"}`, ""}, + {storage.AddOp, "/a/0/beef", "", storage.NotFoundErr}, + {storage.AddOp, "/arr", `[1,2,3]`, ""}, + {storage.AddOp, "/arr/0/foo", "", storage.NotFoundErr}, + {storage.AddOp, "/arr/4", "", storage.NotFoundErr}, + } + + for _, w := range writes { + var jsn any + if w.value != "" { + jsn = util.MustUnmarshalJSON([]byte(w.value)) + } + err := store.Write(ctx, txn, w.op, storage.MustParsePath(w.path), jsn) + if (w.errCode == "" && err != nil) || (err == nil && w.errCode != "") { + t.Fatalf("Expected errCode %q but got: %v", w.errCode, err) + } + } + }) + } +} + +func TestInMemoryTxnReadFailures(t *testing.T) { + cases := []struct { + note string + ast bool + }{ + {"raw", false}, + {"ast", true}, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := NewFromObjectWithOpts(loadSmallTestData(), OptReturnASTValuesOnRead(tc.ast)) + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + if err := store.Write(ctx, txn, storage.RemoveOp, storage.MustParsePath("/c/0/y"), nil); err != nil { + t.Fatalf("Unexpected write error: %v", err) + } + + if result, err := store.Read(ctx, txn, storage.MustParsePath("/c/0/y/0")); !storage.IsNotFound(err) { + t.Fatalf("Expected NotFoundErr for /c/0/y/0 but got: %v (err: %v)", result, err) + } + + if result, err := store.Read(ctx, txn, storage.MustParsePath("/c/0/y")); !storage.IsNotFound(err) { + t.Fatalf("Expected NotFoundErr for /c/0/y but got: %v (err: %v)", result, err) + } + + if result, err := store.Read(ctx, txn, storage.MustParsePath("/a/0/beef")); !storage.IsNotFound(err) { + t.Fatalf("Expected NotFoundErr for /c/0/y but got: %v (err: %v)", result, err) + } + }) + } +} + +func TestInMemoryTxnBadWrite(t *testing.T) { + ctx := context.Background() + store := NewFromObject(loadSmallTestData()) + txn := storage.NewTransactionOrDie(ctx, store) + if err := store.Write(ctx, txn, storage.RemoveOp, storage.MustParsePath("/a"), nil); !storage.IsInvalidTransaction(err) { + t.Fatalf("Expected InvalidTransactionErr but got: %v", err) + } +} + +func TestInMemoryTxnPolicies(t *testing.T) { + + ctx := context.Background() + store := New() + + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + if err := store.UpsertPolicy(ctx, txn, "test", []byte("package test")); err != nil { + t.Fatalf("Unexpected error on policy insert: %v", err) + } + + if err := store.Commit(ctx, txn); err != nil { + t.Fatalf("Unexpected commit error: %v", err) + } + + txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + if err := store.UpsertPolicy(ctx, txn, "test", []byte("package test\nimport data.foo")); err != nil { + t.Fatalf("Unexpected error on policy insert/update: %v", err) + } + + ids, err := store.ListPolicies(ctx, txn) + expectedIDs := []string{"test"} + if err != nil || !slices.Equal(expectedIDs, ids) { + t.Fatalf("Expected list policies to return %v but got: %v (err: %v)", expectedIDs, ids, err) + } + + bs, err := store.GetPolicy(ctx, txn, "test") + expectedBytes := []byte("package test\nimport data.foo") + if err != nil || !bytes.Equal(expectedBytes, bs) { + t.Fatalf("Expected get policy to return %v but got: %v (err: %v)", expectedBytes, bs, err) + } + + if err := store.DeletePolicy(ctx, txn, "test"); err != nil { + t.Fatalf("Unexpected delete policy error: %v", err) + } + + if err := store.UpsertPolicy(ctx, txn, "test2", []byte("package test2")); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + ids, err = store.ListPolicies(ctx, txn) + expectedIDs = []string{"test2"} + if err != nil || !slices.Equal(expectedIDs, ids) { + t.Fatalf("Expected list policies to return %v but got: %v (err: %v)", expectedIDs, ids, err) + } + + bs, err = store.GetPolicy(ctx, txn, "test2") + expectedBytes = []byte("package test2") + if err != nil || !bytes.Equal(expectedBytes, bs) { + t.Fatalf("Expected get policy to return %v but got: %v (err: %v)", expectedBytes, bs, err) + } + + if exist, err := store.GetPolicy(ctx, txn, "test"); !storage.IsNotFound(err) { + t.Fatalf("Expected NotFoundErr for test but got: %v (err: %v)", exist, err) + } + + store.Abort(ctx, txn) + + txn = storage.NewTransactionOrDie(ctx, store) + ids, err = store.ListPolicies(ctx, txn) + expectedIDs = []string{"test"} + if err != nil || !slices.Equal(expectedIDs, ids) { + t.Fatalf("Expected list policies to return %v but got: %v (err: %v)", expectedIDs, ids, err) + } + + if exist, err := store.GetPolicy(ctx, txn, "test2"); !storage.IsNotFound(err) { + t.Fatalf("Expected NotFoundErr for test2 but got: %v (err: %v)", exist, err) + } + + if err := store.DeletePolicy(ctx, txn, "test"); !storage.IsInvalidTransaction(err) { + t.Fatalf("Expected InvalidTransactionErr for test but got: %v", err) + } + + store.Abort(ctx, txn) + + txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + + if err := store.DeletePolicy(ctx, txn, "test"); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if err := store.Commit(ctx, txn); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + txn = storage.NewTransactionOrDie(ctx, store) + + if ids, err := store.ListPolicies(ctx, txn); err != nil || len(ids) != 0 { + t.Fatalf("Expected list policies to be empty but got: %v (err: %v)", ids, err) + } + +} + +func TestInMemoryTriggers(t *testing.T) { + cases := []struct { + note string + ast bool + }{ + {"raw", false}, + {"ast", true}, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + store := NewFromObjectWithOpts(loadSmallTestData(), OptReturnASTValuesOnRead(tc.ast)) + writeTxn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + readTxn := storage.NewTransactionOrDie(ctx, store) + + _, err := store.Register(ctx, readTxn, storage.TriggerConfig{ + OnCommit: func(context.Context, storage.Transaction, storage.TriggerEvent) {}, + }) + + if err == nil || !storage.IsInvalidTransaction(err) { + t.Fatalf("Expected transaction error: %v", err) + } + + store.Abort(ctx, readTxn) + + var event storage.TriggerEvent + modifiedPath := storage.MustParsePath("/a") + expectedValue := "hello" + + _, err = store.Register(ctx, writeTxn, storage.TriggerConfig{ + OnCommit: func(ctx context.Context, txn storage.Transaction, evt storage.TriggerEvent) { + result, err := store.Read(ctx, txn, modifiedPath) + if tc.ast { + expAstValue := ast.String(expectedValue) + if err != nil || ast.Compare(expAstValue, result) != 0 { + t.Fatalf("Expected result to be %v for trigger read but got: %v (err: %v)", expectedValue, result, err) + } + } else if err != nil || !reflect.DeepEqual(result, expectedValue) { + t.Fatalf("Expected result to be %v for trigger read but got: %v (err: %v)", expectedValue, result, err) + } + event = evt + }, + }) + if err != nil { + t.Fatalf("Failed to register callback: %v", err) + } + + if err := store.Write(ctx, writeTxn, storage.ReplaceOp, modifiedPath, expectedValue); err != nil { + t.Fatalf("Unexpected write error: %v", err) + } + + id := "test" + data := []byte("package abc") + if err := store.UpsertPolicy(ctx, writeTxn, id, data); err != nil { + t.Fatalf("Unexpected upsert error: %v", err) + } + + if err := store.Commit(ctx, writeTxn); err != nil { + t.Fatalf("Unexpected commit error: %v", err) + } + + if event.IsZero() || !event.PolicyChanged() || !event.DataChanged() { + t.Fatalf("Expected policy and data change but got: %v", event) + } + + expData := storage.DataEvent{Path: modifiedPath, Data: expectedValue, Removed: false} + if d := event.Data[0]; !reflect.DeepEqual(expData, d) { + t.Fatalf("Expected data event %v, got %v", expData, d) + } + + expPolicy := storage.PolicyEvent{ID: id, Data: data, Removed: false} + if p := event.Policy[0]; !reflect.DeepEqual(expPolicy, p) { + t.Fatalf("Expected policy event %v, got %v", expPolicy, p) + } + }) + } +} + +func TestInMemoryTriggersUnregister(t *testing.T) { + ctx := context.Background() + store := NewFromObject(loadSmallTestData()) + writeTxn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + modifiedPath := storage.MustParsePath("/a") + expectedValue := "hello" + + var called bool + _, err := store.Register(ctx, writeTxn, storage.TriggerConfig{ + OnCommit: func(_ context.Context, _ storage.Transaction, evt storage.TriggerEvent) { + if !evt.IsZero() { + called = true + } + }, + }) + if err != nil { + t.Fatalf("Failed to register callback: %v", err) + } + + handle, err := store.Register(ctx, writeTxn, storage.TriggerConfig{ + OnCommit: func(_ context.Context, _ storage.Transaction, evt storage.TriggerEvent) { + if !evt.IsZero() { + t.Fatalf("Callback should have been unregistered") + } + }, + }) + if err != nil { + t.Fatalf("Failed to register callback: %v", err) + } + + if err := store.Commit(ctx, writeTxn); err != nil { + t.Fatalf("Unexpected commit error: %v", err) + } + + writeTxn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + if err := store.Write(ctx, writeTxn, storage.AddOp, modifiedPath, expectedValue); err != nil { + t.Fatalf("Failed to write to store: %v", err) + } + handle.Unregister(ctx, writeTxn) + + if err := store.Commit(ctx, writeTxn); err != nil { + t.Fatalf("Unexpected commit error: %v", err) + } + + if !called { + t.Fatal("Registered callback was not called") + } +} + +func TestInMemoryContext(t *testing.T) { + + ctx := context.Background() + store := New() + params := storage.WriteParams + params.Context = storage.NewContext() + params.Context.Put("foo", "bar") + + txn, err := store.NewTransaction(ctx, params) + if err != nil { + t.Fatal(err) + } + + _, err = store.Register(ctx, txn, storage.TriggerConfig{ + OnCommit: func(_ context.Context, _ storage.Transaction, event storage.TriggerEvent) { + if event.Context.Get("foo") != "bar" { + t.Fatalf("Expected foo/bar in context but got: %+v", event.Context) + } else if event.Context.Get("deadbeef") != nil { + t.Fatalf("Got unexpected deadbeef value in context: %+v", event.Context) + } + }, + }) + if err != nil { + t.Fatal(err) + } + + if err := store.Commit(ctx, txn); err != nil { + t.Fatal(err) + } + +} + +func loadExpectedResult(input string) any { + if len(input) == 0 { + return nil + } + var data any + if err := util.UnmarshalJSON([]byte(input), &data); err != nil { + panic(err) + } + return data +} + +func loadExpectedSortedResult(input string) any { + data := loadExpectedResult(input) + switch data := data.(type) { + case []any: + return data + default: + return data + } +} + +func loadSmallTestData() map[string]any { + var data map[string]any + err := util.UnmarshalJSON([]byte(`{ + "a": [1,2,3,4], + "b": { + "v1": "hello", + "v2": "goodbye" + }, + "c": [{ + "x": [true, false, "foo"], + "y": [null, 3.14159], + "z": {"p": true, "q": false} + }], + "d": { + "e": ["bar", "baz"] + }, + "g": { + "a": [1, 0, 0, 0], + "b": [0, 2, 0, 0], + "c": [0, 0, 0, 4] + }, + "h": [ + [1,2,3], + [2,3,4] + ] + }`), &data) + if err != nil { + panic(err) + } + return data +} + +func TestOptRoundTripOnWrite(t *testing.T) { + validObject := map[string]string{"foo": "bar"} + + // self-referential objects are not serializable to JSON. + invalidObject := map[string]any{} + invalidObject["foo"] = invalidObject + + tests := []struct { + name string + opts []Opt + obj any + wantErr bool + }{{ + name: "success on valid object no Opts", + opts: nil, + obj: validObject, + wantErr: false, + }, { + name: "success on valid object round trip enabled", + opts: []Opt{OptRoundTripOnWrite(true)}, + obj: validObject, + wantErr: false, + }, { + name: "success on valid object round trip disabled", + opts: []Opt{OptRoundTripOnWrite(false)}, + obj: validObject, + wantErr: false, + }, { + // Ensure the setting defaults to "true". + name: "failure on invalid object no Opts", + opts: nil, + obj: invalidObject, + wantErr: true, + }, { + name: "failure on invalid object round trip enabled", + opts: []Opt{OptRoundTripOnWrite(true)}, + obj: invalidObject, + wantErr: true, + }, { + // While this represents a bad use case, it's how we know the round-tripping + // has been disabled. + name: "success on invalid object round trip disabled", + opts: []Opt{OptRoundTripOnWrite(false)}, + obj: invalidObject, + wantErr: false, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + db := NewWithOpts(tt.opts...) + ctx := context.Background() + + txn, err := db.NewTransaction(ctx, storage.WriteParams) + if err != nil { + t.Fatal(err) + } + + err = db.Write(ctx, txn, storage.AddOp, []string{"data"}, tt.obj) + if tt.wantErr && err == nil { + t.Fatal("got Write error = nil, want error") + } else if !tt.wantErr && err != nil { + t.Fatalf("got Write error, want nil") + } + }) + } +} diff --git a/third_party/opa/v1/storage/inmem/opts.go b/third_party/opa/v1/storage/inmem/opts.go new file mode 100644 index 000000000000..2239fc73a369 --- /dev/null +++ b/third_party/opa/v1/storage/inmem/opts.go @@ -0,0 +1,37 @@ +package inmem + +// An Opt modifies store at instantiation. +type Opt func(*store) + +// OptRoundTripOnWrite sets whether incoming objects written to store are +// round-tripped through JSON to ensure they are serializable to JSON. +// +// Callers should disable this if they can guarantee all objects passed to +// Write() are serializable to JSON. Failing to do so may result in undefined +// behavior, including panics. +// +// Usually, when only storing objects in the inmem store that have been read +// via encoding/json, this is safe to disable, and comes with an improvement +// in performance and memory use. +// +// If setting to false, callers should deep-copy any objects passed to Write() +// unless they can guarantee the objects will not be mutated after being written, +// and that mutations happening to the objects after they have been passed into +// Write() don't affect their logic. +func OptRoundTripOnWrite(enabled bool) Opt { + return func(s *store) { + s.roundTripOnWrite = enabled + } +} + +// OptReturnASTValuesOnRead sets whether data values added to the store should be +// eagerly converted to AST values, which are then returned on read. +// +// When enabled, this feature does not sanity check data before converting it to AST values, +// which may result in panics if the data is not valid. Callers should ensure that passed data +// can be serialized to AST values; otherwise, it's recommended to also enable OptRoundTripOnWrite. +func OptReturnASTValuesOnRead(enabled bool) Opt { + return func(s *store) { + s.returnASTValuesOnRead = enabled + } +} diff --git a/third_party/opa/v1/storage/inmem/test/testutil.go b/third_party/opa/v1/storage/inmem/test/testutil.go new file mode 100644 index 000000000000..bcf740c52e92 --- /dev/null +++ b/third_party/opa/v1/storage/inmem/test/testutil.go @@ -0,0 +1,28 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package test + +import ( + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" +) + +// New returns an inmem store with some common options set: opt-out of write +// roundtripping. +func New() storage.Store { + return inmem.NewWithOpts(inmem.OptRoundTripOnWrite(false)) +} + +// NewFromObject returns an inmem store from the passed object, with some +// common options set: opt-out of write roundtripping. +func NewFromObject(x map[string]any) storage.Store { + return inmem.NewFromObjectWithOpts(x, inmem.OptRoundTripOnWrite(false)) +} + +// NewFromObjectWithASTRead returns an inmem store from the passed object, with +// round-trip on write disabled and AST values returned on read. +func NewFromObjectWithASTRead(x map[string]any) storage.Store { + return inmem.NewFromObjectWithOpts(x, inmem.OptRoundTripOnWrite(false), inmem.OptReturnASTValuesOnRead(true)) +} diff --git a/third_party/opa/v1/storage/inmem/txn.go b/third_party/opa/v1/storage/inmem/txn.go new file mode 100644 index 000000000000..28e68c20f2e8 --- /dev/null +++ b/third_party/opa/v1/storage/inmem/txn.go @@ -0,0 +1,483 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package inmem + +import ( + "container/list" + "encoding/json" + "strconv" + + "github.com/open-policy-agent/opa/internal/deepcopy" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/internal/errors" + "github.com/open-policy-agent/opa/v1/storage/internal/ptr" +) + +// transaction implements the low-level read/write operations on the in-memory +// store and contains the state required for pending transactions. +// +// For write transactions, the struct contains a logical set of updates +// performed by write operations in the transaction. Each write operation +// compacts the set such that two updates never overlap: +// +// - If new update path is a prefix of existing update path, existing update is +// removed, new update is added. +// +// - If existing update path is a prefix of new update path, existing update is +// modified. +// +// - Otherwise, new update is added. +// +// Read transactions do not require any special handling and simply passthrough +// to the underlying store. Read transactions do not support upgrade. +type transaction struct { + xid uint64 + write bool + stale bool + db *store + updates *list.List + policies map[string]policyUpdate + context *storage.Context +} + +type policyUpdate struct { + value []byte + remove bool +} + +func newTransaction(xid uint64, write bool, context *storage.Context, db *store) *transaction { + return &transaction{ + xid: xid, + write: write, + db: db, + policies: map[string]policyUpdate{}, + updates: list.New(), + context: context, + } +} + +func (txn *transaction) ID() uint64 { + return txn.xid +} + +func (txn *transaction) Write(op storage.PatchOp, path storage.Path, value any) error { + + if !txn.write { + return &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "data write during read transaction", + } + } + + if len(path) == 0 { + return txn.updateRoot(op, value) + } + + for curr := txn.updates.Front(); curr != nil; { + update := curr.Value.(dataUpdate) + + // Check if new update masks existing update exactly. In this case, the + // existing update can be removed and no other updates have to be + // visited (because no two updates overlap.) + if update.Path().Equal(path) { + if update.Remove() { + if op != storage.AddOp { + return errors.NewNotFoundError(path) + } + } + txn.updates.Remove(curr) + break + } + + // Check if new update masks existing update. In this case, the + // existing update has to be removed but other updates may overlap, so + // we must continue. + if update.Path().HasPrefix(path) { + remove := curr + curr = curr.Next() + txn.updates.Remove(remove) + continue + } + + // Check if new update modifies existing update. In this case, the + // existing update is mutated. + if path.HasPrefix(update.Path()) { + if update.Remove() { + return errors.NewNotFoundError(path) + } + suffix := path[len(update.Path()):] + newUpdate, err := txn.db.newUpdate(update.Value(), op, suffix, 0, value) + if err != nil { + return err + } + update.Set(newUpdate.Apply(update.Value())) + return nil + } + + curr = curr.Next() + } + + update, err := txn.db.newUpdate(txn.db.data, op, path, 0, value) + if err != nil { + return err + } + + txn.updates.PushFront(update) + return nil +} + +func (txn *transaction) updateRoot(op storage.PatchOp, value any) error { + if op == storage.RemoveOp { + return invalidPatchError(rootCannotBeRemovedMsg) + } + + var update any + if txn.db.returnASTValuesOnRead { + valueAST, err := interfaceToValue(value) + if err != nil { + return err + } + if _, ok := valueAST.(ast.Object); !ok { + return invalidPatchError(rootMustBeObjectMsg) + } + + update = &updateAST{ + path: storage.Path{}, + remove: false, + value: valueAST, + } + } else { + if _, ok := value.(map[string]any); !ok { + return invalidPatchError(rootMustBeObjectMsg) + } + + update = &updateRaw{ + path: storage.Path{}, + remove: false, + value: value, + } + } + + txn.updates.Init() + txn.updates.PushFront(update) + return nil +} + +func (txn *transaction) Commit() (result storage.TriggerEvent) { + result.Context = txn.context + for curr := txn.updates.Front(); curr != nil; curr = curr.Next() { + action := curr.Value.(dataUpdate) + txn.db.data = action.Apply(txn.db.data) + + result.Data = append(result.Data, storage.DataEvent{ + Path: action.Path(), + Data: action.Value(), + Removed: action.Remove(), + }) + } + for id, upd := range txn.policies { + if upd.remove { + delete(txn.db.policies, id) + } else { + txn.db.policies[id] = upd.value + } + + result.Policy = append(result.Policy, storage.PolicyEvent{ + ID: id, + Data: upd.value, + Removed: upd.remove, + }) + } + return result +} + +func pointer(v any, path storage.Path) (any, error) { + if v, ok := v.(ast.Value); ok { + return ptr.ValuePtr(v, path) + } + return ptr.Ptr(v, path) +} + +func deepcpy(v any) any { + if v, ok := v.(ast.Value); ok { + var cpy ast.Value + + switch data := v.(type) { + case ast.Object: + cpy = data.Copy() + case *ast.Array: + cpy = data.Copy() + } + + return cpy + } + return deepcopy.DeepCopy(v) +} + +func (txn *transaction) Read(path storage.Path) (any, error) { + + if !txn.write { + return pointer(txn.db.data, path) + } + + var merge []dataUpdate + + for curr := txn.updates.Front(); curr != nil; curr = curr.Next() { + + upd := curr.Value.(dataUpdate) + + if path.HasPrefix(upd.Path()) { + if upd.Remove() { + return nil, errors.NewNotFoundError(path) + } + return pointer(upd.Value(), path[len(upd.Path()):]) + } + + if upd.Path().HasPrefix(path) { + merge = append(merge, upd) + } + } + + data, err := pointer(txn.db.data, path) + + if err != nil { + return nil, err + } + + if len(merge) == 0 { + return data, nil + } + + cpy := deepcpy(data) + + for _, update := range merge { + cpy = update.Relative(path).Apply(cpy) + } + + return cpy, nil +} + +func (txn *transaction) ListPolicies() []string { + var ids []string + for id := range txn.db.policies { + if _, ok := txn.policies[id]; !ok { + ids = append(ids, id) + } + } + for id, update := range txn.policies { + if !update.remove { + ids = append(ids, id) + } + } + return ids +} + +func (txn *transaction) GetPolicy(id string) ([]byte, error) { + if update, ok := txn.policies[id]; ok { + if !update.remove { + return update.value, nil + } + return nil, errors.NewNotFoundErrorf("policy id %q", id) + } + if exist, ok := txn.db.policies[id]; ok { + return exist, nil + } + return nil, errors.NewNotFoundErrorf("policy id %q", id) +} + +func (txn *transaction) UpsertPolicy(id string, bs []byte) error { + if !txn.write { + return &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "policy write during read transaction", + } + } + txn.policies[id] = policyUpdate{bs, false} + return nil +} + +func (txn *transaction) DeletePolicy(id string) error { + if !txn.write { + return &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "policy write during read transaction", + } + } + txn.policies[id] = policyUpdate{nil, true} + return nil +} + +type dataUpdate interface { + Path() storage.Path + Remove() bool + Apply(any) any + Relative(path storage.Path) dataUpdate + Set(any) + Value() any +} + +// update contains state associated with an update to be applied to the +// in-memory data store. +type updateRaw struct { + path storage.Path // data path modified by update + remove bool // indicates whether update removes the value at path + value any // value to add/replace at path (ignored if remove is true) +} + +func (db *store) newUpdate(data any, op storage.PatchOp, path storage.Path, idx int, value any) (dataUpdate, error) { + if db.returnASTValuesOnRead { + astData, err := interfaceToValue(data) + if err != nil { + return nil, err + } + astValue, err := interfaceToValue(value) + if err != nil { + return nil, err + } + return newUpdateAST(astData, op, path, idx, astValue) + } + return newUpdateRaw(data, op, path, idx, value) +} + +func newUpdateRaw(data any, op storage.PatchOp, path storage.Path, idx int, value any) (dataUpdate, error) { + + switch data.(type) { + case nil, bool, json.Number, string: + return nil, errors.NewNotFoundError(path) + } + + switch data := data.(type) { + case map[string]any: + return newUpdateObject(data, op, path, idx, value) + + case []any: + return newUpdateArray(data, op, path, idx, value) + } + + return nil, &storage.Error{ + Code: storage.InternalErr, + Message: "invalid data value encountered", + } +} + +func newUpdateArray(data []any, op storage.PatchOp, path storage.Path, idx int, value any) (dataUpdate, error) { + + if idx == len(path)-1 { + if path[idx] == "-" || path[idx] == strconv.Itoa(len(data)) { + if op != storage.AddOp { + return nil, invalidPatchError("%v: invalid patch path", path) + } + cpy := make([]any, len(data)+1) + copy(cpy, data) + cpy[len(data)] = value + return &updateRaw{path[:len(path)-1], false, cpy}, nil + } + + pos, err := ptr.ValidateArrayIndex(data, path[idx], path) + if err != nil { + return nil, err + } + + switch op { + case storage.AddOp: + cpy := make([]any, len(data)+1) + copy(cpy[:pos], data[:pos]) + copy(cpy[pos+1:], data[pos:]) + cpy[pos] = value + return &updateRaw{path[:len(path)-1], false, cpy}, nil + + case storage.RemoveOp: + cpy := make([]any, len(data)-1) + copy(cpy[:pos], data[:pos]) + copy(cpy[pos:], data[pos+1:]) + return &updateRaw{path[:len(path)-1], false, cpy}, nil + + default: + cpy := make([]any, len(data)) + copy(cpy, data) + cpy[pos] = value + return &updateRaw{path[:len(path)-1], false, cpy}, nil + } + } + + pos, err := ptr.ValidateArrayIndex(data, path[idx], path) + if err != nil { + return nil, err + } + + return newUpdateRaw(data[pos], op, path, idx+1, value) +} + +func newUpdateObject(data map[string]any, op storage.PatchOp, path storage.Path, idx int, value any) (dataUpdate, error) { + + if idx == len(path)-1 { + switch op { + case storage.ReplaceOp, storage.RemoveOp: + if _, ok := data[path[idx]]; !ok { + return nil, errors.NewNotFoundError(path) + } + } + return &updateRaw{path, op == storage.RemoveOp, value}, nil + } + + if data, ok := data[path[idx]]; ok { + return newUpdateRaw(data, op, path, idx+1, value) + } + + return nil, errors.NewNotFoundError(path) +} + +func (u *updateRaw) Remove() bool { + return u.remove +} + +func (u *updateRaw) Path() storage.Path { + return u.path +} + +func (u *updateRaw) Apply(data any) any { + if len(u.path) == 0 { + return u.value + } + parent, err := ptr.Ptr(data, u.path[:len(u.path)-1]) + if err != nil { + panic(err) + } + key := u.path[len(u.path)-1] + if u.remove { + obj := parent.(map[string]any) + delete(obj, key) + return data + } + switch parent := parent.(type) { + case map[string]any: + if parent == nil { + parent = make(map[string]any, 1) + } + parent[key] = u.value + case []any: + idx, err := strconv.Atoi(key) + if err != nil { + panic(err) + } + parent[idx] = u.value + } + return data +} + +func (u *updateRaw) Set(v any) { + u.value = v +} + +func (u *updateRaw) Value() any { + return u.value +} + +func (u *updateRaw) Relative(path storage.Path) dataUpdate { + cpy := *u + cpy.path = cpy.path[len(path):] + return &cpy +} diff --git a/third_party/opa/v1/storage/interface.go b/third_party/opa/v1/storage/interface.go new file mode 100644 index 000000000000..a783caae090a --- /dev/null +++ b/third_party/opa/v1/storage/interface.go @@ -0,0 +1,252 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + "context" + + "github.com/open-policy-agent/opa/v1/metrics" +) + +// Transaction defines the interface that identifies a consistent snapshot over +// the policy engine's storage layer. +type Transaction interface { + ID() uint64 +} + +// Store defines the interface for the storage layer's backend. +type Store interface { + Trigger + Policy + + // NewTransaction is called create a new transaction in the store. + NewTransaction(context.Context, ...TransactionParams) (Transaction, error) + + // Read is called to fetch a document referred to by path. + Read(context.Context, Transaction, Path) (any, error) + + // Write is called to modify a document referred to by path. + Write(context.Context, Transaction, PatchOp, Path, any) error + + // Commit is called to finish the transaction. If Commit returns an error, the + // transaction must be automatically aborted by the Store implementation. + Commit(context.Context, Transaction) error + + // Truncate is called to make a copy of the underlying store, write documents in the new store + // by creating multiple transactions in the new store as needed and finally swapping + // over to the new storage instance. This method must be called within a transaction on the original store. + Truncate(context.Context, Transaction, TransactionParams, Iterator) error + + // Abort is called to cancel the transaction. + Abort(context.Context, Transaction) +} + +// MakeDirer defines the interface a Store could realize to override the +// generic MakeDir functionality in storage.MakeDir +type MakeDirer interface { + MakeDir(context.Context, Transaction, Path) error +} + +// NonEmptyer allows a store implemention to override NonEmpty()) +type NonEmptyer interface { + NonEmpty(context.Context, Transaction) func([]string) (bool, error) +} + +// TransactionParams describes a new transaction. +type TransactionParams struct { + + // BasePaths indicates the top-level paths where write operations will be performed in this transaction. + BasePaths []string + + // RootOverwrite is deprecated. Use BasePaths instead. + RootOverwrite bool + + // Write indicates if this transaction will perform any write operations. + Write bool + + // Context contains key/value pairs passed to triggers. + Context *Context +} + +// Context is a simple container for key/value pairs. +type Context struct { + values map[any]any +} + +// NewContext returns a new context object. +func NewContext() *Context { + return &Context{ + values: map[any]any{}, + } +} + +// Get returns the key value in the context. +func (ctx *Context) Get(key any) any { + if ctx == nil { + return nil + } + return ctx.values[key] +} + +// Put adds a key/value pair to the context. +func (ctx *Context) Put(key, value any) { + ctx.values[key] = value +} + +var metricsKey = struct{}{} + +// WithMetrics allows passing metrics via the Context. +// It puts the metrics object in the ctx, and returns the same +// ctx (not a copy) for convenience. +func (ctx *Context) WithMetrics(m metrics.Metrics) *Context { + ctx.values[metricsKey] = m + return ctx +} + +// Metrics() allows using a Context's metrics. Returns nil if metrics +// were not attached to the Context. +func (ctx *Context) Metrics() metrics.Metrics { + if m, ok := ctx.values[metricsKey]; ok { + if met, ok := m.(metrics.Metrics); ok { + return met + } + } + return nil +} + +// WriteParams specifies the TransactionParams for a write transaction. +var WriteParams = TransactionParams{ + Write: true, +} + +// PatchOp is the enumeration of supposed modifications. +type PatchOp int + +// Patch supports add, remove, and replace operations. +const ( + AddOp PatchOp = iota + RemoveOp = iota + ReplaceOp = iota +) + +// WritesNotSupported provides a default implementation of the write +// interface which may be used if the backend does not support writes. +type WritesNotSupported struct{} + +func (WritesNotSupported) Write(context.Context, Transaction, PatchOp, Path, any) error { + return writesNotSupportedError() +} + +// Policy defines the interface for policy module storage. +type Policy interface { + ListPolicies(context.Context, Transaction) ([]string, error) + GetPolicy(context.Context, Transaction, string) ([]byte, error) + UpsertPolicy(context.Context, Transaction, string, []byte) error + DeletePolicy(context.Context, Transaction, string) error +} + +// PolicyNotSupported provides a default implementation of the policy interface +// which may be used if the backend does not support policy storage. +type PolicyNotSupported struct{} + +// ListPolicies always returns a PolicyNotSupportedErr. +func (PolicyNotSupported) ListPolicies(context.Context, Transaction) ([]string, error) { + return nil, policyNotSupportedError() +} + +// GetPolicy always returns a PolicyNotSupportedErr. +func (PolicyNotSupported) GetPolicy(context.Context, Transaction, string) ([]byte, error) { + return nil, policyNotSupportedError() +} + +// UpsertPolicy always returns a PolicyNotSupportedErr. +func (PolicyNotSupported) UpsertPolicy(context.Context, Transaction, string, []byte) error { + return policyNotSupportedError() +} + +// DeletePolicy always returns a PolicyNotSupportedErr. +func (PolicyNotSupported) DeletePolicy(context.Context, Transaction, string) error { + return policyNotSupportedError() +} + +// PolicyEvent describes a change to a policy. +type PolicyEvent struct { + ID string + Data []byte + Removed bool +} + +// DataEvent describes a change to a base data document. +type DataEvent struct { + Path Path + Data any + Removed bool +} + +// TriggerEvent describes the changes that caused the trigger to be invoked. +type TriggerEvent struct { + Policy []PolicyEvent + Data []DataEvent + Context *Context +} + +// IsZero returns true if the TriggerEvent indicates no changes occurred. This +// function is primarily for test purposes. +func (e TriggerEvent) IsZero() bool { + return !e.PolicyChanged() && !e.DataChanged() +} + +// PolicyChanged returns true if the trigger was caused by a policy change. +func (e TriggerEvent) PolicyChanged() bool { + return len(e.Policy) > 0 +} + +// DataChanged returns true if the trigger was caused by a data change. +func (e TriggerEvent) DataChanged() bool { + return len(e.Data) > 0 +} + +// TriggerConfig contains the trigger registration configuration. +type TriggerConfig struct { + + // OnCommit is invoked when a transaction is successfully committed. The + // callback is invoked with a handle to the write transaction that + // successfully committed before other clients see the changes. + OnCommit func(context.Context, Transaction, TriggerEvent) +} + +// Trigger defines the interface that stores implement to register for change +// notifications when the store is changed. +type Trigger interface { + Register(context.Context, Transaction, TriggerConfig) (TriggerHandle, error) +} + +// TriggersNotSupported provides default implementations of the Trigger +// interface which may be used if the backend does not support triggers. +type TriggersNotSupported struct{} + +// Register always returns an error indicating triggers are not supported. +func (TriggersNotSupported) Register(context.Context, Transaction, TriggerConfig) (TriggerHandle, error) { + return nil, triggersNotSupportedError() +} + +// TriggerHandle defines the interface that can be used to unregister triggers that have +// been registered on a Store. +type TriggerHandle interface { + Unregister(context.Context, Transaction) +} + +// Iterator defines the interface that can be used to read files from a directory starting with +// files at the base of the directory, then sub-directories etc. +type Iterator interface { + Next() (*Update, error) +} + +// Update contains information about a file +type Update struct { + Path Path + Value []byte + IsPolicy bool +} diff --git a/third_party/opa/v1/storage/internal/errors/errors.go b/third_party/opa/v1/storage/internal/errors/errors.go new file mode 100644 index 000000000000..d13fff50fc39 --- /dev/null +++ b/third_party/opa/v1/storage/internal/errors/errors.go @@ -0,0 +1,43 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package errors contains reusable error-related code for the storage layer. +package errors + +import ( + "fmt" + + "github.com/open-policy-agent/opa/v1/storage" +) + +const ArrayIndexTypeMsg = "array index must be integer" +const DoesNotExistMsg = "document does not exist" +const OutOfRangeMsg = "array index out of range" + +func NewNotFoundError(path storage.Path) *storage.Error { + return NewNotFoundErrorWithHint(path, DoesNotExistMsg) +} + +func NewNotFoundErrorWithHint(path storage.Path, hint string) *storage.Error { + message := path.String() + ": " + hint + return &storage.Error{ + Code: storage.NotFoundErr, + Message: message, + } +} + +func NewNotFoundErrorf(f string, a ...any) *storage.Error { + msg := fmt.Sprintf(f, a...) + return &storage.Error{ + Code: storage.NotFoundErr, + Message: msg, + } +} + +func NewWriteConflictError(p storage.Path) *storage.Error { + return &storage.Error{ + Code: storage.WriteConflictErr, + Message: p.String(), + } +} diff --git a/third_party/opa/v1/storage/internal/errors/errors_test.go b/third_party/opa/v1/storage/internal/errors/errors_test.go new file mode 100644 index 000000000000..9e47d5d0fac3 --- /dev/null +++ b/third_party/opa/v1/storage/internal/errors/errors_test.go @@ -0,0 +1,22 @@ +package errors + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/storage" +) + +// 160.8 ns/op 96 B/op 5 allocs/op // using fmt.Sprintf +// 58.31 ns/op 8 B/op 1 allocs/op // using string concatenation +// ... +func BenchmarkNewNotFoundErrorWithHint(b *testing.B) { + path := storage.Path([]string{"a", "b", "c"}) + hint := "something something" + + for range b.N { + err := NewNotFoundErrorWithHint(path, hint) + if err == nil { + b.Fatal("expected error") + } + } +} diff --git a/third_party/opa/v1/storage/internal/ptr/ptr.go b/third_party/opa/v1/storage/internal/ptr/ptr.go new file mode 100644 index 000000000000..c5e380af04d0 --- /dev/null +++ b/third_party/opa/v1/storage/internal/ptr/ptr.go @@ -0,0 +1,120 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package ptr provides utilities for pointer operations using storage layer paths. +package ptr + +import ( + "strconv" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/internal/errors" +) + +func Ptr(data any, path storage.Path) (any, error) { + node := data + for i := range path { + key := path[i] + switch curr := node.(type) { + case map[string]any: + var ok bool + if node, ok = curr[key]; !ok { + return nil, errors.NewNotFoundError(path) + } + case []any: + pos, err := ValidateArrayIndex(curr, key, path) + if err != nil { + return nil, err + } + node = curr[pos] + default: + return nil, errors.NewNotFoundError(path) + } + } + + return node, nil +} + +func ValuePtr(data ast.Value, path storage.Path) (ast.Value, error) { + node := data + for i := range path { + key := path[i] + switch curr := node.(type) { + case ast.Object: + // This term is only created for the lookup, which is not.. ideal. + // By using the pool, we can at least avoid allocating the term itself, + // while still having to pay 1 allocation for the value. A better solution + // would be dynamically interned string terms. + keyTerm := ast.TermPtrPool.Get() + keyTerm.Value = ast.String(key) + + val := curr.Get(keyTerm) + ast.TermPtrPool.Put(keyTerm) + if val == nil { + return nil, errors.NewNotFoundError(path) + } + node = val.Value + case *ast.Array: + pos, err := ValidateASTArrayIndex(curr, key, path) + if err != nil { + return nil, err + } + node = curr.Elem(pos).Value + default: + return nil, errors.NewNotFoundError(path) + } + } + + return node, nil +} + +func ValidateArrayIndex(arr []any, s string, path storage.Path) (int, error) { + idx, ok := isInt(s) + if !ok { + return 0, errors.NewNotFoundErrorWithHint(path, errors.ArrayIndexTypeMsg) + } + return inRange(idx, arr, path) +} + +func ValidateASTArrayIndex(arr *ast.Array, s string, path storage.Path) (int, error) { + idx, ok := isInt(s) + if !ok { + return 0, errors.NewNotFoundErrorWithHint(path, errors.ArrayIndexTypeMsg) + } + return inRange(idx, arr, path) +} + +// ValidateArrayIndexForWrite also checks that `s` is a valid way to address an +// array element like `ValidateArrayIndex`, but returns a `resource_conflict` error +// if it is not. +func ValidateArrayIndexForWrite(arr []any, s string, i int, path storage.Path) (int, error) { + idx, ok := isInt(s) + if !ok { + return 0, errors.NewWriteConflictError(path[:i-1]) + } + return inRange(idx, arr, path) +} + +func isInt(s string) (int, bool) { + idx, err := strconv.Atoi(s) + return idx, err == nil +} + +func inRange(i int, arr any, path storage.Path) (int, error) { + + var arrLen int + + switch v := arr.(type) { + case []any: + arrLen = len(v) + case *ast.Array: + arrLen = v.Len() + } + + if i < 0 || i >= arrLen { + return 0, errors.NewNotFoundErrorWithHint(path, errors.OutOfRangeMsg) + } + return i, nil +} diff --git a/third_party/opa/v1/storage/path.go b/third_party/opa/v1/storage/path.go new file mode 100644 index 000000000000..f774d2eeda76 --- /dev/null +++ b/third_party/opa/v1/storage/path.go @@ -0,0 +1,162 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + "errors" + "fmt" + "net/url" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// Path refers to a document in storage. +type Path []string + +// ParsePath returns a new path for the given str. +func ParsePath(str string) (path Path, ok bool) { + if len(str) == 0 { + return nil, false + } + if str[0] != '/' { + return nil, false + } + if len(str) == 1 { + return Path{}, true + } + parts := strings.Split(str[1:], "/") + return parts, true +} + +// ParsePathEscaped returns a new path for the given escaped str. +func ParsePathEscaped(str string) (path Path, ok bool) { + path, ok = ParsePath(str) + if !ok { + return + } + for i := range path { + segment, err := url.PathUnescape(path[i]) + if err == nil { + path[i] = segment + } + } + return +} + +// NewPathForRef returns a new path for the given ref. +func NewPathForRef(ref ast.Ref) (path Path, err error) { + + if len(ref) == 0 { + return nil, errors.New("empty reference (indicates error in caller)") + } + + if len(ref) == 1 { + return Path{}, nil + } + + path = make(Path, 0, len(ref)-1) + + for _, term := range ref[1:] { + switch v := term.Value.(type) { + case ast.String: + path = append(path, string(v)) + case ast.Number: + path = append(path, v.String()) + case ast.Boolean, ast.Null: + return nil, &Error{ + Code: NotFoundErr, + Message: fmt.Sprintf("%v: does not exist", ref), + } + case *ast.Array, ast.Object, ast.Set: + return nil, fmt.Errorf("composites cannot be base document keys: %v", ref) + default: + return nil, fmt.Errorf("unresolved reference (indicates error in caller): %v", ref) + } + } + + return path, nil +} + +// Compare performs lexigraphical comparison on p and other and returns -1 if p +// is less than other, 0 if p is equal to other, or 1 if p is greater than +// other. +func (p Path) Compare(other Path) (cmp int) { + for i := range min(len(p), len(other)) { + if cmp := strings.Compare(p[i], other[i]); cmp != 0 { + return cmp + } + } + if len(p) < len(other) { + return -1 + } + if len(p) == len(other) { + return 0 + } + return 1 +} + +// Equal returns true if p is the same as other. +func (p Path) Equal(other Path) bool { + return p.Compare(other) == 0 +} + +// HasPrefix returns true if p starts with other. +func (p Path) HasPrefix(other Path) bool { + if len(other) > len(p) { + return false + } + for i := range other { + if p[i] != other[i] { + return false + } + } + return true +} + +// Ref returns a ref that represents p rooted at head. +func (p Path) Ref(head *ast.Term) (ref ast.Ref) { + ref = make(ast.Ref, len(p)+1) + ref[0] = head + for i := range p { + idx, err := strconv.ParseInt(p[i], 10, 64) + if err == nil { + ref[i+1] = ast.UIntNumberTerm(uint64(idx)) + } else { + ref[i+1] = ast.StringTerm(p[i]) + } + } + return ref +} + +func (p Path) String() string { + if len(p) == 0 { + return "/" + } + + l := 0 + for i := range p { + l += len(p[i]) + 1 + } + + sb := strings.Builder{} + sb.Grow(l) + for i := range p { + sb.WriteByte('/') + sb.WriteString(url.PathEscape(p[i])) + } + return sb.String() +} + +// MustParsePath returns a new Path for s. If s cannot be parsed, this function +// will panic. This is mostly for test purposes. +func MustParsePath(s string) Path { + path, ok := ParsePath(s) + if !ok { + panic(s) + } + return path +} diff --git a/third_party/opa/v1/storage/path_test.go b/third_party/opa/v1/storage/path_test.go new file mode 100644 index 000000000000..3a19e77374cd --- /dev/null +++ b/third_party/opa/v1/storage/path_test.go @@ -0,0 +1,208 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + "errors" + "math" + "testing" + + "fmt" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestNewPathForString(t *testing.T) { + + tests := []struct { + input string + result Path + ok bool + }{ + {"", nil, false}, + {"foo", nil, false}, + {"/", Path{}, true}, + {"/", nil, true}, + {"/foo", Path{"foo"}, true}, + {"/foo/bar", Path{"foo", "bar"}, true}, + } + + for _, tc := range tests { + result, ok := ParsePath(tc.input) + if (tc.ok != ok) || !tc.result.Equal(result) { + t.Errorf("For %v wanted (%v, %v) but got (%v, %v)", tc.input, tc.result, tc.ok, result, ok) + } + } +} + +func TestNewPathForRef(t *testing.T) { + tests := []struct { + input ast.Ref + result Path + err error + }{ + {ast.Ref{}, nil, errors.New("empty reference (indicates error in caller)")}, + {ast.MustParseRef("data.foo[x]"), nil, errors.New("unresolved reference (indicates error in caller): data.foo[x]")}, + {ast.MustParseRef("data.foo[true]"), nil, &Error{ + Code: NotFoundErr, + Message: fmt.Sprintf("%v: does not exist", ast.MustParseRef("data.foo[true]")), + }}, + {ast.MustParseRef("data.foo[[1, 2]]"), nil, fmt.Errorf("composites cannot be base document keys: %v", ast.MustParseRef("data.foo[[1, 2]]"))}, + {ast.MustParseRef("data.foo[{1, 2}]"), nil, fmt.Errorf("composites cannot be base document keys: %v", ast.MustParseRef("data.foo[{1, 2}]"))}, + {ast.MustParseRef(`data.foo[{"foo": 2}]`), nil, fmt.Errorf("composites cannot be base document keys: %v", ast.MustParseRef(`data.foo[{"foo": 2}]`))}, + + {ast.MustParseRef("data"), Path{}, nil}, + {ast.MustParseRef("data.foo"), Path{"foo"}, nil}, + {ast.MustParseRef("data.foo[1]"), Path{"foo", "1"}, nil}, + {ast.MustParseRef("data.foo.bar"), Path{"foo", "bar"}, nil}, + } + + for _, tc := range tests { + result, err := NewPathForRef(tc.input) + if tc.err != nil && tc.err.Error() != err.Error() { + t.Errorf("For %v expected %v but got %v", tc.input, tc.err, err) + } else if !result.Equal(tc.result) { + t.Errorf("For %v expected %v but got %v", tc.input, tc.result, result) + } + } +} + +func TestNewPathForStringEscaped(t *testing.T) { + + tests := []struct { + input string + result Path + ok bool + }{ + { + input: "/foo/bar", // no escaping + result: Path{"foo", "bar"}, + ok: true, + }, + { + input: "/foo%2Fbar/baz", // single escape + result: Path{"foo/bar", "baz"}, + ok: true, + }, + { + input: "/foo%2F%2Fbar/baz", // double escape + result: Path{"foo//bar", "baz"}, + ok: true, + }, + } + + for _, tc := range tests { + result, ok := ParsePathEscaped(tc.input) + if (tc.ok != ok) || !tc.result.Equal(result) { + t.Errorf("For %v wanted (%v, %v) but got (%v, %v)", tc.input, tc.result, tc.ok, result, ok) + } + } +} + +func TestPathCompare(t *testing.T) { + tests := []struct { + a Path + b Path + result int + }{ + {Path{}, Path{}, 0}, + {Path{}, Path{"x"}, -1}, + {Path{"x"}, Path{}, 1}, + {Path{"x"}, Path{"x"}, 0}, + {Path{"x"}, Path{"y"}, -1}, + {Path{"x"}, Path{"w"}, 1}, + {Path{"x"}, Path{"wz"}, 1}, + {Path{"x"}, Path{"xx"}, -1}, + {Path{"xx"}, Path{"x"}, 1}, + {Path{"xx"}, Path{"xx"}, 0}, + {Path{"xy"}, Path{"xx"}, 1}, + } + for _, tc := range tests { + result := tc.a.Compare(tc.b) + if result != tc.result { + t.Errorf("For %v.Compare(%v) expected %v but got %v", tc.a, tc.b, tc.result, result) + } + } +} + +func TestPathEqual(t *testing.T) { + tests := []struct { + a Path + b Path + result bool + }{ + {Path{}, Path{}, true}, + {Path{}, Path{"foo"}, false}, + {Path{"foo"}, Path{}, false}, + {Path{"foo", "bar"}, Path{"foo"}, false}, + {Path{"foo", "bar"}, Path{"foo", "bar"}, true}, + } + for _, tc := range tests { + result := tc.a.Equal(tc.b) + if result != tc.result { + t.Errorf("For %v.HasPrefix(%v) expected %v but got %v", tc.a, tc.b, tc.result, result) + } + } +} + +func TestPathHasPrefix(t *testing.T) { + tests := []struct { + a Path + b Path + result bool + }{ + {Path{}, Path{}, true}, + {Path{}, Path{"foo"}, false}, + {Path{"foo"}, Path{}, true}, + {Path{"foo"}, Path{"bar"}, false}, + {Path{"bar"}, Path{"foo"}, false}, + {Path{"foo", "bar"}, Path{"foo"}, true}, + {Path{"foo", "bar"}, Path{"foo", "bar"}, true}, + {Path{"foo", "bar"}, Path{"foo", "bar", "baz"}, false}, + {Path{"foo", "bar", "baz"}, Path{}, true}, + } + for _, tc := range tests { + result := tc.a.HasPrefix(tc.b) + if result != tc.result { + t.Errorf("For %v.HasPrefix(%v) expected %v but got %v", tc.a, tc.b, tc.result, result) + } + } +} + +func TestPathRef(t *testing.T) { + tests := []struct { + path string + head string + ref string + }{ + {"/", "data", "data"}, + {"/foo/bar", "data", "data.foo.bar"}, + {"/foo/bar/3", "data", "data.foo.bar[3]"}, + {fmt.Sprintf("/foo/bar/%d", math.MaxInt64), "data", fmt.Sprintf("data.foo.bar[%d]", math.MaxInt64)}, + } + for _, tc := range tests { + path := MustParsePath(tc.path) + head := ast.VarTerm(tc.head) + ref := ast.MustParseRef(tc.ref) + result := path.Ref(head) + if !result.Equal(ref) { + t.Errorf("Expected %v but got %v", ref, result) + } + } +} + +// 108.8 ns/op 80 B/op 3 allocs/op // original implementation concat + Join +// 68.60 ns/op 24 B/op 2 allocs/op // strings.Builder +// 50.28 ns/op 16 B/op 1 allocs/op // strings.Builder with pre-allocated buffer +func BenchmarkPathString(b *testing.B) { + path := Path{"foo", "bar", "baz"} + + for range b.N { + res := path.String() + if res != "/foo/bar/baz" { + b.Fatal("unexpected result:", res) + } + } +} diff --git a/third_party/opa/v1/storage/storage.go b/third_party/opa/v1/storage/storage.go new file mode 100644 index 000000000000..38d51be405e7 --- /dev/null +++ b/third_party/opa/v1/storage/storage.go @@ -0,0 +1,139 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + "context" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// NewTransactionOrDie is a helper function to create a new transaction. If the +// storage layer cannot create a new transaction, this function will panic. This +// function should only be used for tests. +func NewTransactionOrDie(ctx context.Context, store Store, params ...TransactionParams) Transaction { + txn, err := store.NewTransaction(ctx, params...) + if err != nil { + panic(err) + } + return txn +} + +// ReadOne is a convenience function to read a single value from the provided Store. It +// will create a new Transaction to perform the read with, and clean up after itself +// should an error occur. +func ReadOne(ctx context.Context, store Store, path Path) (any, error) { + txn, err := store.NewTransaction(ctx) + if err != nil { + return nil, err + } + defer store.Abort(ctx, txn) + + return store.Read(ctx, txn, path) +} + +// WriteOne is a convenience function to write a single value to the provided Store. It +// will create a new Transaction to perform the write with, and clean up after itself +// should an error occur. +func WriteOne(ctx context.Context, store Store, op PatchOp, path Path, value any) error { + txn, err := store.NewTransaction(ctx, WriteParams) + if err != nil { + return err + } + + if err := store.Write(ctx, txn, op, path, value); err != nil { + store.Abort(ctx, txn) + return err + } + + return store.Commit(ctx, txn) +} + +// MakeDir inserts an empty object at path. If the parent path does not exist, +// MakeDir will create it recursively. +func MakeDir(ctx context.Context, store Store, txn Transaction, path Path) error { + + // Allow the Store implementation to deal with this in its own way. + if md, ok := store.(MakeDirer); ok { + return md.MakeDir(ctx, txn, path) + } + + if len(path) == 0 { + return nil + } + + node, err := store.Read(ctx, txn, path) + if err != nil { + if !IsNotFound(err) { + return err + } + + if err := MakeDir(ctx, store, txn, path[:len(path)-1]); err != nil { + return err + } + + return store.Write(ctx, txn, AddOp, path, map[string]any{}) + } + + if _, ok := node.(map[string]any); ok { + return nil + } + + if _, ok := node.(ast.Object); ok { + return nil + } + + return writeConflictError(path) +} + +// Txn is a convenience function that executes f inside a new transaction +// opened on the store. If the function returns an error, the transaction is +// aborted and the error is returned. Otherwise, the transaction is committed +// and the result of the commit is returned. +func Txn(ctx context.Context, store Store, params TransactionParams, f func(Transaction) error) error { + + txn, err := store.NewTransaction(ctx, params) + if err != nil { + return err + } + + if err := f(txn); err != nil { + store.Abort(ctx, txn) + return err + } + + return store.Commit(ctx, txn) +} + +// NonEmpty returns a function that tests if a path is non-empty. A +// path is non-empty if a Read on the path returns a value or a Read +// on any of the path prefixes returns a non-object value. +func NonEmpty(ctx context.Context, store Store, txn Transaction) func([]string) (bool, error) { + if md, ok := store.(NonEmptyer); ok { + return md.NonEmpty(ctx, txn) + } + return func(path []string) (bool, error) { + if _, err := store.Read(ctx, txn, Path(path)); err == nil { + return true, nil + } else if !IsNotFound(err) { + return false, err + } + for i := len(path) - 1; i > 0; i-- { + val, err := store.Read(ctx, txn, Path(path[:i])) + if err != nil && !IsNotFound(err) { + return false, err + } else if err == nil { + if _, ok := val.(map[string]any); ok { + return false, nil + } + if _, ok := val.(ast.Object); ok { + return false, nil + } + return true, nil + } + } + return false, nil + } +} diff --git a/third_party/opa/v1/storage/storage_test.go b/third_party/opa/v1/storage/storage_test.go new file mode 100644 index 000000000000..597f1589beb1 --- /dev/null +++ b/third_party/opa/v1/storage/storage_test.go @@ -0,0 +1,104 @@ +package storage_test + +import ( + "bytes" + "context" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" +) + +func TestNonEmpty(t *testing.T) { + + cases := []struct { + content string + path string + exp bool + }{ + { + content: `{}`, + path: "a/b/c", + exp: false, + }, + { + content: `{"a": {}}`, + path: "a/b/c", + exp: false, + }, + { + content: `{"a": {"b": {}}}`, + path: "a/b/c", + exp: false, + }, + { + content: `{"a": {"b": {"c": {}}}}`, + path: "a/b/c", + exp: true, + }, + { + content: `{"a": {"b": "x"}}`, + path: "a/b/c", + exp: true, + }, + { + content: `{"a": "x"}`, + path: "a/b/c", + exp: true, + }, + } + + ctx := context.Background() + + for _, tc := range cases { + t.Run(tc.content, func(t *testing.T) { + store := inmem.NewFromReader(bytes.NewBufferString(tc.content)) + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + nonEmpty, err := storage.NonEmpty(ctx, store, txn)(strings.Split(tc.path, "/")) + if err != nil { + t.Fatal(err) + } + if nonEmpty != tc.exp { + t.Errorf("Expected %v for %v on %v but got %v", tc.exp, tc.path, tc.content, nonEmpty) + } + return nil + }) + if err != nil { + t.Error(err) + } + }) + } + +} + +type nonEmpty struct { + storage.Store +} + +func (*nonEmpty) NonEmpty(context.Context, storage.Transaction) func([]string) (bool, error) { + return func([]string) (bool, error) { + return true, nil + } +} + +func TestNonEmptyer(t *testing.T) { + ctx := context.Background() + ne := &nonEmpty{inmem.New()} + + for _, path := range []string{"a", "a/b/c"} { + err := storage.Txn(ctx, ne, storage.TransactionParams{}, func(txn storage.Transaction) error { + nonEmpty, err := storage.NonEmpty(ctx, ne, txn)(strings.Split(path, "/")) + if err != nil { + t.Fatal(err) + } + if nonEmpty != true { + t.Errorf("Expected true for %v but got false", path) + } + return nil + }) + if err != nil { + t.Error(err) + } + } +} diff --git a/third_party/opa/v1/test/authz/authz_bench_test.go b/third_party/opa/v1/test/authz/authz_bench_test.go new file mode 100644 index 000000000000..20fcdf3f9ca7 --- /dev/null +++ b/third_party/opa/v1/test/authz/authz_bench_test.go @@ -0,0 +1,108 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package authz + +import ( + "context" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/disk" + "github.com/open-policy-agent/opa/v1/storage/inmem" +) + +func BenchmarkAuthzForbidAuthn(b *testing.B) { + b.Run("inmem", func(b *testing.B) { + runAuthzBenchmark(b, ForbidIdentity, 10) + }) + b.Run("disk", func(b *testing.B) { + runAuthzBenchmark(b, ForbidIdentity, 10, true) + }) +} + +func BenchmarkAuthzForbidPath(b *testing.B) { + runAuthzBenchmark(b, ForbidPath, 10) +} + +func BenchmarkAuthzForbidMethod(b *testing.B) { + runAuthzBenchmark(b, ForbidMethod, 10) +} + +func BenchmarkAuthzAllow10Paths(b *testing.B) { + runAuthzBenchmark(b, Allow, 10) +} + +func BenchmarkAuthzAllow100Paths(b *testing.B) { + runAuthzBenchmark(b, Allow, 100) +} + +func BenchmarkAuthzAllow1000Paths(b *testing.B) { + runAuthzBenchmark(b, Allow, 1000) +} + +func runAuthzBenchmark(b *testing.B, mode InputMode, numPaths int, extras ...bool) { + profile := DataSetProfile{ + NumTokens: 1000, + NumPaths: numPaths, + } + + ctx := context.Background() + data := GenerateDataset(profile) + useDisk := len(extras) > 0 && extras[0] + + var store storage.Store + if useDisk { + var err error + if store, err = disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{Dir: b.TempDir()}); err != nil { + b.Fatal(err) + } + + if err = storage.WriteOne(ctx, store, storage.AddOp, storage.Path{}, data); err != nil { + b.Fatal(err) + } + } else { + store = inmem.NewFromObjectWithOpts(data) + } + + compiler := ast.NewCompiler() + if compiler.Compile(map[string]*ast.Module{"": ast.MustParseModule(Policy)}); compiler.Failed() { + b.Fatalf("Unexpected error(s): %v", compiler.Errors) + } + + r := rego.New( + rego.Compiler(compiler), + rego.Store(store), + rego.Transaction(storage.NewTransactionOrDie(ctx, store)), + rego.Query(AllowQuery), + ) + + pq, err := r.PrepareForEval(ctx) + if err != nil { + b.Fatalf("Unexpected error(s): %v", err) + } + + input, expected := GenerateInput(profile, mode) + + inputAST, err := ast.InterfaceToValue(input) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + + for range b.N { + rs, err := pq.Eval(ctx, rego.EvalParsedInput(inputAST)) + if err != nil { + b.Fatalf("Unexpected error(s): %v", err) + } + + if rs.Allowed() != expected { + b.Fatalf("Unexpected result: %v", rs) + } + } +} diff --git a/third_party/opa/v1/test/authz/authz_test.go b/third_party/opa/v1/test/authz/authz_test.go new file mode 100644 index 000000000000..aa56a83bb632 --- /dev/null +++ b/third_party/opa/v1/test/authz/authz_test.go @@ -0,0 +1,55 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package authz + +import ( + "context" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" +) + +func TestAuthz(t *testing.T) { + + profile := DataSetProfile{ + NumTokens: 1000, + NumPaths: 10, + } + + ctx := context.Background() + data := GenerateDataset(profile) + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + compiler := ast.NewCompiler() + module := ast.MustParseModule(Policy) + + compiler.Compile(map[string]*ast.Module{"": module}) + if compiler.Failed() { + t.Fatalf("Unexpected error(s): %v", compiler.Errors) + } + + input, expected := GenerateInput(profile, ForbidPath) + + r := rego.New( + rego.Compiler(compiler), + rego.Store(store), + rego.Transaction(txn), + rego.Input(input), + rego.Query(AllowQuery), + ) + + rs, err := r.Eval(ctx) + + if err != nil { + t.Fatalf("Unexpected error(s): %v", err) + } + + if rs.Allowed() != expected { + t.Fatalf("Unexpected result: want %v, got %v", expected, rs.Allowed()) + } +} diff --git a/third_party/opa/v1/test/authz/testing.go b/third_party/opa/v1/test/authz/testing.go new file mode 100644 index 000000000000..241c3a1f36f9 --- /dev/null +++ b/third_party/opa/v1/test/authz/testing.go @@ -0,0 +1,167 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package authz contains unit and benchmark tests for authz use-cases +// The public (non-test) APIs are meant to be used as helpers for +// other tests to build off of. +package authz + +import ( + "encoding/json" + "fmt" + + "github.com/open-policy-agent/opa/v1/util" +) + +// Policy is a test rego policy for a token based authz system +const Policy = `package policy.restauthz + +import data.restauthz.tokens + +default allow := false + +allow if { + token := tokens[input.token_id] + some authz in token.authz_profiles + regex.match(authz.path, input.path) + input.method in authz.methods +}` + +// AllowQuery is the test query that goes with the Policy +// defined in this package +const AllowQuery = "data.policy.restauthz.allow" + +// DataSetProfile defines how the test data should be generated +type DataSetProfile struct { + NumTokens int + NumPaths int +} + +// InputMode defines what type of inputs to generate for testings +type InputMode int + +// InputMode types supported by GenerateInput +const ( + ForbidIdentity = iota + ForbidPath = iota + ForbidMethod = iota + Allow = iota +) + +// GenerateInput will use a dataset profile and desired InputMode to generate inputs for testing +func GenerateInput(profile DataSetProfile, mode InputMode) (any, any) { + var input string + var allow bool + + switch mode { + case ForbidIdentity: + input = fmt.Sprintf(` + { + "token_id": "deadbeef", + "path": %q, + "method": "GET" + } + `, generateRequestPath(profile.NumPaths-1)) + case ForbidPath: + input = fmt.Sprintf(` + { + "token_id": %q, + "path": %q, + "method": "GET" + }`, generateTokenID(profile.NumTokens-1), "/api/v1/resourcetype-deadbeef/deadbeefresourceid") + case ForbidMethod: + input = fmt.Sprintf(` + { + "token_id": %q, + "path": %q, + "method": "DEADBEEF" + } + `, generateTokenID(profile.NumTokens-1), generateRequestPath(profile.NumPaths-1)) + default: + input = fmt.Sprintf(` + { + "token_id": %q, + "path": %q, + "method": "GET" + } + `, generateTokenID(profile.NumTokens-1), generateRequestPath(profile.NumPaths-1)) + allow = true + } + + return util.MustUnmarshalJSON([]byte(input)), allow +} + +// GenerateDataset will generate a dataset for the given DatasetProfile +func GenerateDataset(profile DataSetProfile) map[string]any { + return map[string]any{ + "restauthz": map[string]any{ + "tokens": generateTokensJSON(profile), + }, + } +} + +func generateTokensJSON(profile DataSetProfile) any { + tokens := generateTokens(profile) + bs, err := json.Marshal(tokens) + if err != nil { + panic(err) + } + return util.MustUnmarshalJSON(bs) +} + +type token struct { + ID string `json:"id"` + AuthzProfiles []authzProfile `json:"authz_profiles"` +} + +type authzProfile struct { + Path string `json:"path"` + Methods []string `json:"methods"` +} + +func generateTokens(profile DataSetProfile) map[string]token { + tokens := map[string]token{} + for i := range profile.NumTokens { + token := generateToken(profile, i) + tokens[token.ID] = token + } + return tokens +} + +func generateToken(profile DataSetProfile, i int) token { + return token{ + ID: generateTokenID(i), + AuthzProfiles: generateAuthzProfiles(profile), + } +} + +func generateAuthzProfiles(profile DataSetProfile) []authzProfile { + profiles := make([]authzProfile, profile.NumPaths) + for i := range profile.NumPaths { + profiles[i] = generateAuthzProfile(profile, i) + } + return profiles +} + +func generateAuthzProfile(_ DataSetProfile, i int) authzProfile { + return authzProfile{ + Path: generateAuthzPath(i), + Methods: []string{ + "POST", + "GET", + }, + } +} + +func generateTokenID(suffix int) string { + return fmt.Sprintf("token-%d", suffix) +} + +func generateAuthzPath(i int) string { + return fmt.Sprintf("/api/v1/resourcetype-%d/*", i) +} + +func generateRequestPath(i int) string { + return fmt.Sprintf("/api/v1/resourcetype-%d/somefakeresourceid000000111111", i) +} diff --git a/third_party/opa/v1/test/cases/cases.go b/third_party/opa/v1/test/cases/cases.go new file mode 100644 index 000000000000..a8cab718aeef --- /dev/null +++ b/third_party/opa/v1/test/cases/cases.go @@ -0,0 +1,101 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package cases contains utilities for evaluation test cases. +package cases + +import ( + "fmt" + "os" + "path/filepath" + "sort" + + "github.com/open-policy-agent/opa/v1/util" +) + +// Create v1 test cases from v0 test cases. +// //go:generate ../../build/gen-run-go.sh internal/fmtcases/main.go testdata/v0 0 testdata/v1 1 +//go:generate ../../build/gen-run-go.sh internal/fmtcases/main.go testdata/v1 0 testdata/v1_2 1 + +// Set represents a collection of test cases. +type Set struct { + Cases []TestCase `json:"cases"` +} + +// Sorted returns a sorted copy of s. +func (s Set) Sorted() Set { + cpy := make([]TestCase, len(s.Cases)) + copy(cpy, s.Cases) + sort.Slice(cpy, func(i, j int) bool { + return cpy[i].Note < cpy[j].Note + }) + return Set{Cases: cpy} +} + +// TestCase represents a single test case. +type TestCase struct { + Filename string `json:"-" yaml:"-"` // name of file that case was loaded from + Note string `json:"note" yaml:"note"` // globally unique identifier for this test case + Query string `json:"query" yaml:"query"` // policy query to execute + Modules []string `json:"modules,omitempty" yaml:"modules,omitempty"` // policies to test against + Data *map[string]any `json:"data,omitempty" yaml:"data,omitempty"` // data to test against + Input *any `json:"input,omitempty" yaml:"input,omitempty"` // parsed input data to use + InputTerm *string `json:"input_term,omitempty" yaml:"input_term,omitempty"` // raw input data (serialized as a string, overrides input) + WantDefined *bool `json:"want_defined,omitempty" yaml:"want_defined,omitempty"` // expect query result to be defined (or not) + WantResult *[]map[string]any `json:"want_result,omitempty" yaml:"want_result,omitempty"` // expect query result (overrides defined) + WantErrorCode *string `json:"want_error_code,omitempty" yaml:"want_error_code,omitempty"` // expect query error code (overrides result) + WantError *string `json:"want_error,omitempty" yaml:"want_error,omitempty"` // expect query error message (overrides error code) + SortBindings bool `json:"sort_bindings,omitempty" yaml:"sort_bindings,omitempty"` // indicates that binding values should be treated as sets + StrictError bool `json:"strict_error,omitempty" yaml:"strict_error,omitempty"` // indicates that the error depends on strict builtin error mode + Env map[string]string `json:"env,omitempty" yaml:"env,omitempty"` // environment variables to be set during the test +} + +// Load returns a set of built-in test cases. +func Load(path string) (Set, error) { + return loadRecursive(path) +} + +// MustLoad returns a set of built-in test cases or panics if an error occurs. +func MustLoad(path string) Set { + result, err := Load(path) + if err != nil { + panic(err) + } + return result +} + +func loadRecursive(dirpath string) (Set, error) { + + result := Set{} + + err := filepath.Walk(dirpath, func(path string, info os.FileInfo, err error) error { + + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + bs, err := os.ReadFile(path) + if err != nil { + return fmt.Errorf("%s: %w", path, err) + } + + var x Set + if err := util.Unmarshal(bs, &x); err != nil { + return fmt.Errorf("%s: %w", path, err) + } + + for i := range x.Cases { + x.Cases[i].Filename = path + } + + result.Cases = append(result.Cases, x.Cases...) + return nil + }) + + return result, err +} diff --git a/third_party/opa/v1/test/cases/internal/fmtcases/main.go b/third_party/opa/v1/test/cases/internal/fmtcases/main.go new file mode 100644 index 000000000000..23e1b94348bb --- /dev/null +++ b/third_party/opa/v1/test/cases/internal/fmtcases/main.go @@ -0,0 +1,124 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "strconv" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/format" + "github.com/open-policy-agent/opa/v1/test/cases" + "github.com/open-policy-agent/opa/v1/util" + "go.yaml.in/yaml/v3" +) + +func main() { + if len(os.Args) < 5 { + fmt.Println("Usage: main ") + os.Exit(1) + } + + s := os.Args[1] + sv, err := strconv.Atoi(os.Args[2]) + if err != nil { + fmt.Println("Version must be an integer") + os.Exit(1) + } + t := os.Args[3] + tv, err := strconv.Atoi(os.Args[4]) + if err != nil { + fmt.Println("Version must be an integer") + os.Exit(1) + } + sourceRegoVersion := ast.RegoVersionFromInt(sv) + targetRegoVersion := ast.RegoVersionFromInt(tv) + + fmt.Printf("Formatting test cases '%s'->'%s' to rego-version %s\n", s, t, targetRegoVersion) + + es, err := os.ReadDir(s) + if err != nil { + fmt.Println("Error reading source directory:", err) + os.Exit(1) + } + for _, e := range es { + if err := copyEntry(s, sourceRegoVersion, e, t, targetRegoVersion); err != nil { + fmt.Println("Error handling source entry:", err) + os.Exit(1) + } + } +} + +func copyEntry(sourceRoot string, sourceRegoVersion ast.RegoVersion, e os.DirEntry, targetRoot string, targetRegoVersion ast.RegoVersion) error { + i, err := e.Info() + if err != nil { + return err + } + + if i.IsDir() { + err = os.MkdirAll(filepath.Join(targetRoot, e.Name()), i.Mode()) + if err != nil { + return err + } + childSourceRoot := filepath.Join(sourceRoot, e.Name()) + childTargetRoot := filepath.Join(targetRoot, e.Name()) + es, err := os.ReadDir(childSourceRoot) + if err != nil { + return err + } + for _, c := range es { + if err := copyEntry(childSourceRoot, sourceRegoVersion, c, childTargetRoot, targetRegoVersion); err != nil { + return err + } + } + } else { + path := filepath.Join(sourceRoot, i.Name()) + bs, err := os.ReadFile(path) + if err != nil { + return err + } + + var testCases cases.Set + if err := util.Unmarshal(bs, &testCases); err != nil { + return err + } + + // Format test modules + for _, testCase := range testCases.Cases { //nolint:gocritic + for i, module := range testCase.Modules { + bs, err := format.SourceWithOpts(fmt.Sprintf("mod%d.rego", i), []byte(module), + format.Opts{ + ParserOptions: &ast.ParserOptions{ + RegoVersion: sourceRegoVersion, + }, + RegoVersion: targetRegoVersion, + }) + if err != nil { + fmt.Printf("Error formatting module %s %s:%d: %v\n", path, testCase.Note, i, err) + } else { + testCase.Modules[i] = string(bs) + } + } + } + + // Write formatted test cases to target directory + targetPath := filepath.Join(targetRoot, i.Name()) + var buf bytes.Buffer + enc := yaml.NewEncoder(&buf) + enc.SetIndent(2) + if err := enc.Encode(testCases); err != nil { + return err + } + + text := "---\n" + buf.String() + if err := os.WriteFile(targetPath, []byte(text), i.Mode()); err != nil { + return err + } + } + return nil +} diff --git a/third_party/opa/v1/test/cases/internal/keywordrefs/main.go b/third_party/opa/v1/test/cases/internal/keywordrefs/main.go new file mode 100644 index 000000000000..9d839135af08 --- /dev/null +++ b/third_party/opa/v1/test/cases/internal/keywordrefs/main.go @@ -0,0 +1,65 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" +) + +//go:generate go run main.go + +func main() { + templateFile := "test-keywords-in-ref_v0.yaml.template" + templateContent, err := os.ReadFile(templateFile) + if err != nil { + fmt.Printf("Error reading v0 template file: %v\n", err) + os.Exit(1) + } + + err = generate(ast.KeywordsV0[:], "../../testdata/v0/keywordrefs", string(templateContent)) + if err != nil { + fmt.Printf("Error:%v\n", err) + os.Exit(1) + } + + templateFile = "test-keywords-in-ref_v1.yaml.template" + templateContent, err = os.ReadFile(templateFile) + if err != nil { + fmt.Printf("Error reading v1 template file: %v\n", err) + os.Exit(1) + } + + err = generate(ast.KeywordsV1[:], "../../testdata/v1/keywordrefs", string(templateContent)) + if err != nil { + fmt.Printf("Error:%v\n", err) + os.Exit(1) + } +} + +func generate(keywords []string, root string, template string) error { + err := os.MkdirAll(root, os.ModePerm) + if err != nil { + return fmt.Errorf("error creating directory %s: %v\n", root, err) + } + + // Generate a YAML file for each keyword + for _, keyword := range keywords { + file := fmt.Sprintf("%s/test-keyword-%s.yaml", root, keyword) + + outputContent := strings.ReplaceAll(template, "%{KW}", keyword) + + err := os.WriteFile(file, []byte(outputContent), 0644) + if err != nil { + return fmt.Errorf("error writing file %s: %v\n", file, err) + } + fmt.Printf("Generated file: %s\n", file) + } + + return nil +} diff --git a/third_party/opa/v1/test/cases/internal/keywordrefs/test-keywords-in-ref_v0.yaml.template b/third_party/opa/v1/test/cases/internal/keywordrefs/test-keywords-in-ref_v0.yaml.template new file mode 100644 index 000000000000..8e1c06682719 --- /dev/null +++ b/third_party/opa/v1/test/cases/internal/keywordrefs/test-keywords-in-ref_v0.yaml.template @@ -0,0 +1,200 @@ +--- +cases: + - note: keywordrefs/%{KW} keyword in package + query: data.foo.p = x + modules: + - | + package foo.%{KW}.bar + + baz := 42 + - | + package foo + import data.foo.%{KW}.bar + + p { + bar.baz == 42 + data.foo.%{KW}.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/%{KW} keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.%{KW} + + bar := 42 + - | + package foo + import data.foo.%{KW} as my_if + + p { + my_if.bar == 42 + data.foo.%{KW}.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/%{KW} keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p { + %{KW}.foo == 1 + foo.%{KW} == 2 + } + + %{KW}.foo := 1 + + foo.%{KW} := 2 + want_result: + - x: true + - note: keywordrefs/%{KW} keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p { + %{KW}.foo.bar == 3 + foo.bar.%{KW} == 6 + } + + %{KW}.foo.bar := 1 { + input.x == 1 + } else := 2 { + input.x == 2 + } else := 3 + + foo.bar.%{KW} := 4 { + input.x == 1 + } else := 5 { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/%{KW} keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + import future.keywords.contains + + p { + %{KW}.foo.bar == {"a", "c"} + foo.bar.%{KW} == {"a", "c"} + } + + %{KW}.foo.bar contains "a" + + %{KW}.foo.bar contains "b" { + false + } + + %{KW}.foo.bar contains "c" { + true + } + + foo.bar.%{KW} contains "a" + + foo.bar.%{KW} contains "b" { + false + } + + foo.bar.%{KW} contains "c" { + true + } + want_result: + - x: true + - note: keywordrefs/%{KW} keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p { + %{KW}.foo == "a" + %{KW}.bar.one == "a" + %{KW}.bar.three == "c" + foo.%{KW} == "a" + bar.baz.%{KW} == "a" + } + + %{KW}.foo := "a" + + %{KW}.foo := "b" { + false + } + + %{KW}.foo := "c" { + false + } + + %{KW}.bar.one := "a" + + %{KW}.bar.two := "b" { + false + } + + %{KW}.bar.three := "c" { + true + } + + foo.%{KW} := "a" + + foo.%{KW} := "b" { + false + } + + foo.%{KW} := "c" { + false + } + + bar.baz.%{KW} := "a" + + bar.baz.%{KW} := "b" { + false + } + + bar.baz.%{KW} := "c" { + false + } + want_result: + - x: true + - note: keywordrefs/%{KW} keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p { + %{KW}.foo(1) == 1 + %{KW}.foo(11) == 42 + foo.%{KW}(1) == 1 + foo.%{KW}(11) == 42 + bar.%{KW}.baz(1) == 1 + bar.%{KW}.baz(11) == 42 + } + + default %{KW}.foo(_) := 42 + + %{KW}.foo(x) := x { + x < 10 + } + + default foo.%{KW}(_) := 42 + + foo.%{KW}(x) := x { + x < 10 + } + + default bar.%{KW}.baz(_) := 42 + + bar.%{KW}.baz(x) := x { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/internal/keywordrefs/test-keywords-in-ref_v1.yaml.template b/third_party/opa/v1/test/cases/internal/keywordrefs/test-keywords-in-ref_v1.yaml.template new file mode 100644 index 000000000000..ab93b75c652e --- /dev/null +++ b/third_party/opa/v1/test/cases/internal/keywordrefs/test-keywords-in-ref_v1.yaml.template @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/%{KW} keyword in package + query: data.foo.p = x + modules: + - | + package foo.%{KW}.bar + + baz := 42 + - | + package foo + import data.foo.%{KW}.bar + + p if { + bar.baz == 42 + data.foo.%{KW}.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/%{KW} keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.%{KW} + + bar := 42 + - | + package foo + import data.foo.%{KW} as my_if + + p if { + my_if.bar == 42 + data.foo.%{KW}.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/%{KW} keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + %{KW}.foo == 1 + foo.%{KW} == 2 + } + + %{KW}.foo := 1 + + foo.%{KW} := 2 + want_result: + - x: true + - note: keywordrefs/%{KW} keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + %{KW}.foo == 3 + foo.%{KW} == 6 + } + + %{KW}.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.%{KW} := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/%{KW} keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + %{KW}.foo == {"a", "c"} + foo.%{KW} == {"a", "c"} + } + + %{KW}.foo contains "a" + + %{KW}.foo contains "b" if { + false + } + + %{KW}.foo contains "c" if { + true + } + + foo.%{KW} contains "a" + + foo.%{KW} contains "b" if { + false + } + + foo.%{KW} contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/%{KW} keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + %{KW}.foo == "a" + %{KW}.bar.one == "a" + %{KW}.bar.three == "c" + foo.%{KW} == "a" + bar.baz.%{KW} == "a" + } + + %{KW}.foo := "a" + + %{KW}.foo := "b" if { + false + } + + %{KW}.foo := "c" if { + false + } + + %{KW}.bar.one := "a" + + %{KW}.bar.two := "b" if { + false + } + + %{KW}.bar.three := "c" if { + true + } + + foo.%{KW} := "a" + + foo.%{KW} := "b" if { + false + } + + foo.%{KW} := "c" if { + false + } + + bar.baz.%{KW} := "a" + + bar.baz.%{KW} := "b" if { + false + } + + bar.baz.%{KW} := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/%{KW} keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + %{KW}.foo(1) == 1 + %{KW}.foo(11) == 42 + foo.%{KW}(1) == 1 + foo.%{KW}(11) == 42 + bar.%{KW}.baz(1) == 1 + bar.%{KW}.baz(11) == 42 + } + + default %{KW}.foo(_) := 42 + + %{KW}.foo(x) := x if { + x < 10 + } + + default foo.%{KW}(_) := 42 + + foo.%{KW}(x) := x if { + x < 10 + } + + default bar.%{KW}.baz(_) := 42 + + bar.%{KW}.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0001.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0001.yaml new file mode 100644 index 000000000000..7e22622bce0f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0001.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.a + count(__local0__, x) + } + note: aggregates/count + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0002.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0002.yaml new file mode 100644 index 000000000000..d58eb5b3c8ae --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0002.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = [y | data.generated.q[y]] + count(__local0__, x) + } + + q[x] { + x = data.a[_] + } + note: aggregates/count virtual + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0003.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0003.yaml new file mode 100644 index 000000000000..83668cafa64b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0003.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[x] { + __local0__ = data.b + count(__local0__, x) + } + note: aggregates/count keys + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0004.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0004.yaml new file mode 100644 index 000000000000..3baad1540463 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0004.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[x] { + __local0__ = [k | data.generated.q[k] = _] + count(__local0__, x) + } + + q[k] = v { + data.b[k] = v + } + note: aggregates/count keys virtual + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0005.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0005.yaml new file mode 100644 index 000000000000..fa7d252f6449 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0005.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = x { + __local0__ = data.generated.q + count(__local0__, x) + } + + q[x] { + x = data.a[_] + } + note: aggregates/count set + query: data.generated.p = x + want_result: + - x: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0006.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0006.yaml new file mode 100644 index 000000000000..53193713b881 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0006.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + sum([1, 2, 3, 4], x) + } + note: aggregates/sum + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 10 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0007.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0007.yaml new file mode 100644 index 000000000000..3494862f544f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0007.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + sum({1, 2, 3, 4}, x) + } + note: aggregates/sum set + query: data.generated.p = x + want_result: + - x: 10 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0008.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0008.yaml new file mode 100644 index 000000000000..588169bbd7d6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0008.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = [y | data.generated.q[y]] + sum(__local0__, x) + } + + q[x] { + data.a[_] = x + } + note: aggregates/sum virtual + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 10 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0009.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0009.yaml new file mode 100644 index 000000000000..d97672a74566 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0009.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = x { + __local0__ = data.generated.q + sum(__local0__, x) + } + + q[x] { + data.a[_] = x + } + note: aggregates/sum virtual set + query: data.generated.p = x + want_result: + - x: 10 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0010.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0010.yaml new file mode 100644 index 000000000000..908319949509 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0010.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + sum([49649733057, 1], __local0__) + __local0__ = 49649733058 + } + note: aggregates/bug 2469 - precision + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0011.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0011.yaml new file mode 100644 index 000000000000..a31836687966 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0011.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + product([1, 2, 3, 4], 24) + } + note: aggregates/product + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0012.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0012.yaml new file mode 100644 index 000000000000..f3d21be82e1c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0012.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + product({1, 2, 3, 4}, x) + } + note: aggregates/product set + query: data.generated.p = x + want_result: + - x: 24 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0013.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0013.yaml new file mode 100644 index 000000000000..abebe691dc4f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0013.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + max([1, 2, 3, 4], x) + } + note: aggregates/max + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0014.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0014.yaml new file mode 100644 index 000000000000..c60ec94a65b8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0014.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + max({1, 2, 3, 4}, x) + } + note: aggregates/max set + query: data.generated.p = x + want_result: + - x: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0015.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0015.yaml new file mode 100644 index 000000000000..28c53191126f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0015.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = [y | data.generated.q[y]] + max(__local0__, x) + } + + q[x] { + data.a[_] = x + } + note: aggregates/max virtual + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0016.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0016.yaml new file mode 100644 index 000000000000..915139474337 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0016.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = x { + __local0__ = data.generated.q + max(__local0__, x) + } + + q[x] { + data.a[_] = x + } + note: aggregates/max virtual set + query: data.generated.p = x + want_result: + - x: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0017.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0017.yaml new file mode 100644 index 000000000000..1a4b2dfaf80a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0017.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + min([1, 2, 3, 4], x) + } + note: aggregates/min + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0018.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0018.yaml new file mode 100644 index 000000000000..116deefe6ba9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0018.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + min([1, 2, 1, 3, 4], x) + } + note: aggregates/min dups + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0019.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0019.yaml new file mode 100644 index 000000000000..bddb0410b63d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0019.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + min([3, 2, 1, 4, 6, -7, 10], x) + } + note: aggregates/min out-of-order + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - -7 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0020.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0020.yaml new file mode 100644 index 000000000000..73f404abca35 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0020.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + min({1, 2, 3, 4}, x) + } + note: aggregates/min set + query: data.generated.p = x + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0021.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0021.yaml new file mode 100644 index 000000000000..9d8cfb113e63 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0021.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = [y | data.generated.q[y]] + min(__local0__, x) + } + + q[x] { + data.a[_] = x + } + note: aggregates/min virtual + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0022.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0022.yaml new file mode 100644 index 000000000000..d3cfb4475eb7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0022.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = x { + __local0__ = data.generated.q + min(__local0__, x) + } + + q[x] { + data.a[_] = x + } + note: aggregates/min virtual set + query: data.generated.p = x + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0023.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0023.yaml new file mode 100644 index 000000000000..9be0ae2e9038 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0023.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + __local0__ = data.a[3] + max([1, 2, 3, 4], __local0__) + } + note: aggregates/reduce ref dest + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0024.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0024.yaml new file mode 100644 index 000000000000..d0ccc23fc2c0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0024.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + __local0__ = data.a[3] + not max([1, 2, 3, 4, 5], __local0__) + } + note: aggregates/reduce ref dest (2) + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0025.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0025.yaml new file mode 100644 index 000000000000..09eb5395e5fc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0025.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + sort([4, 3, 2, 1], x) + } + note: aggregates/sort + query: data.generated.p = x + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0026.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0026.yaml new file mode 100644 index 000000000000..bc799b328349 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0026.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + sort({1, 2, 3, 4}, x) + } + note: aggregates/sort set + query: data.generated.p = x + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0027.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0027.yaml new file mode 100644 index 000000000000..97162982d0ba --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0027.yaml @@ -0,0 +1,24 @@ +--- +cases: + - modules: + - | + package generated + + p = x { + count("abcde", x) + } + note: aggregates/count string + query: data.generated.p = x + want_result: + - x: 5 + - modules: + - | + package generated + + p = x { + count("åäö", x) + } + note: aggregates/count string + query: data.generated.p = x + want_result: + - x: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0028.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0028.yaml new file mode 100644 index 000000000000..b117e5aa176f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-0028.yaml @@ -0,0 +1,30 @@ +--- +cases: + - modules: + - | + package generated + + p = x { + count(input.foo, x) + } + note: aggregates/count error null + query: data.generated.p = x + input: + foo: null + strict_error: true + want_error_code: eval_type_error + want_error: "operand 1 must be one of {array, object, set, string} but got null" + - modules: + - | + package generated + + p = x { + count(input.foo, x) + } + note: aggregates/count error number + query: data.generated.p = x + input: + foo: 5 + strict_error: true + want_error_code: eval_type_error + want_error: "operand 1 must be one of {array, object, set, string} but got number" diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-bad-utf8-runes.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-bad-utf8-runes.yaml new file mode 100644 index 000000000000..ce9bf4db0ac3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-aggregates-bad-utf8-runes.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: + modules: + - | + package test + + p[x] { + x := count(base64.decode("2E84ZuPUd7zfvCZSNEchVpDEIj6PL7JfLpIqyxVG16k=")) + } + note: aggregates/count with invalid utf-8 chars (0xFFFD) + query: data.test.p = x + sort_bindings: true + want_result: + - x: + - 30 diff --git a/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-membership.yaml b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-membership.yaml new file mode 100644 index 000000000000..143dd9a2b369 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/aggregates/test-membership.yaml @@ -0,0 +1,540 @@ +--- +cases: + - data: {} + modules: + - | + package test + + p { + in := 1 == 2 + in == false + } + note: aggregates/member without the future import, 'in' can still be used as variable + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + 1 in {1} + } + note: aggregates/member simple, set + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + 1 in [1] + } + note: aggregates/member simple, array + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + 1 in {"foo": 1} + } + note: aggregates/member simple, object + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + "foo", 1 in {"foo": 1} + } + note: aggregates/member object with key + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + 1, "two" in ["one", "two", "three"] + } + note: aggregates/member array with index + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + 1, (2 in [2]) in [false, true] + } + note: aggregates/member array with index, nested + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + 0, 2 in [2] in [true] + } + note: aggregates/member array with index, nested, associativity without parens + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + "foo", 2 in {"foo": 2} in [true] + } + note: aggregates/member object with key, nested, associativity without parens + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + "foo", (2 in {"bar": 2}) in {"foo": true} + } + note: aggregates/member object with key, nested + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p = x { + x := 1 in {2} + } + note: aggregates/member simple false, set + query: data.test.p = x + want_result: + - x: false + - data: {} + modules: + - | + package test + import future.keywords.in + p = x { + x := 1 in [2] + } + note: aggregates/member simple false, array + query: data.test.p = x + want_result: + - x: false + - data: {} + modules: + - | + package test + import future.keywords.in + p = x { + x := 1 in {"foo": 2} + } + note: aggregates/member simple false, object + query: data.test.p = x + want_result: + - x: false + - data: {} + modules: + - | + package test + import future.keywords.in + p { + {1,2} in [{1,2}] in [true] + } + note: aggregates/member chained + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + x := "foo" + xs := ["foo", "bar"] + x in xs + } + note: aggregates/member with vars + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + not "foo" in ["fox"] + } + note: aggregates/member with not + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + 1+1 in [2] + } + note: aggregates/member operator precedence with other infix operator (+) + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + x := { 1, 1 in [2] } + x == { 1, false } + } + note: aggregates/member operator precedence in list (set) + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + x := { (1, 1 in [2]) } + x == { false } + } + note: aggregates/member operator precedence in list with parens (set) + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + x := [ 1, 1 in [2] ] + x == [ 1, false ] + } + note: aggregates/member operator precedence in list (array) + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + x := [ (1, 1 in [2]) ] + x == [ false ] + } + note: aggregates/member operator precedence in list with parens (array) + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + f(1, 1 in [2]) + } + f(_, _) = true + note: aggregates/member operator precedence in list (fun args) + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + f((1, 1 in [2])) + } + f(_) = true + note: aggregates/member operator precedence in list with parens (fun args) + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + {"foo": {"baz": 2000}} in [{"foo": {"baz": 2000}}] + } + note: aggregates/member composite containee + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p = x { + x := 1 in "foo" + } + note: aggregates/member non-collection string + query: data.test.p = x + want_result: + - x: false + - data: {} + modules: + - | + package test + import future.keywords.in + p = x { + x := "foo" in 1 + } + note: aggregates/member non-collection number + query: data.test.p = x + want_result: + - x: false + - data: {} + modules: + - | + package test + import future.keywords.in + p = x { + x := 1, "foo" in 1 + } + note: aggregates/member with key in non-collection (number) + query: data.test.p = x + want_result: + - x: false + - data: {} + modules: + - | + package test + import future.keywords.in + p[x] { + some x in [1,2,3] + } + note: aggregates/member+some simple, array + query: data.test.p = x + want_result: + - x: + - 1 + - 2 + - 3 + - data: {} + modules: + - | + package test + import future.keywords.in + p { + some "foo" in ["foo"] + } + note: aggregates/member+some ground value + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + some numbers.range(1,1) in [[1]] + } + note: aggregates/member+some containee is call + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p = x { + some {"foo": x} in [{"foo": 100}, {"what": "ever"}] + } + note: aggregates/member+some non-ground composite containee + query: data.test.p = x + want_result: + - x: 100 + - data: {} + modules: + - | + package test + import future.keywords.in + p = x { + some {"foo": x, "what": y} in [{"foo": 100, "what": "ever"}] + } + note: aggregates/member+some non-ground composite containee, multiple bindings + query: data.test.p = x + want_result: + - x: 100 + - data: {} + modules: + - | + package test + import future.keywords.in + p { + some {"foo": 100} in [{"foo": 100}] + } + note: aggregates/member+some ground composite containee + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + import future.keywords.in + p { + some {"foo": 0} in [{"foo": 100}] + } + note: aggregates/member+some ground composite containee (false) + query: data.test.p = x + want_result: [] + - data: {} + modules: + - | + package test + import future.keywords.in + p = x { + some "foo", x in {"foo": 100, "what": "ever"} + } + note: aggregates/member+some+key non-ground value + query: data.test.p = x + want_result: + - x: 100 + - data: {} + modules: + - | + package test + import future.keywords.in + p = x { + some x, "ever" in {"foo": 100, "what": "ever"} + } + note: aggregates/member+some+key non-ground key + query: data.test.p = x + want_result: + - x: what + - data: {} + modules: + - | + package test + import future.keywords.in + p[k] = v { + some k, v in {"foo": 100, "what": "ever"} + } + note: aggregates/member+some+key non-ground key+value + query: data.test.p = x + want_result: + - x: + foo: 100 + what: ever + - data: {} + modules: + - | + package test + import future.keywords.in + p = x { + some {"foo": x}, "ever" in {{"foo": 100}: "ever"} + } + note: aggregates/member+some+key non-ground, composite key + query: data.test.p = x + want_result: + - x: 100 + - data: + array: + - a: 1 + - b: 2 + - c: 3 + modules: + - | + package test + import future.keywords.in + p = x { + some {"a": x} in data.array + } + note: aggregates/member+some+ref + query: data.test.p = x + want_result: + - x: 1 + - data: + array: + - a: 1 + - b: 2 + - c: 3 + modules: + - | + package test + import future.keywords.in + p = [x, y] { + some y, {"c": x} in data.array + } + note: aggregates/member+some+key+ref + query: data.test.p = x + want_result: + - x: + - 3 + - 2 + - data: + object: + array: + - a: 1 + - b: 2 + - c: 3 + modules: + - | + package test + import future.keywords.in + p = [x, y, i] { + some i + some y, {"c": x} in data.object[i] + } + note: aggregates/member+some+key+ref with other variable + query: data.test.p = x + want_result: + - x: + - 3 + - 2 + - array + - data: {} + modules: + - | + package test + import future.keywords.in + p[[k, v]] { + some k, v in input with input.foo as "bar" + } + note: aggregates/member+some+with + query: data.test.p = x + want_result: + - x: + - - foo + - bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0027.yaml b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0027.yaml new file mode 100644 index 000000000000..09f141c0a259 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0027.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + all(set(), __local1__) + __local0__ = __local1__ + } + note: all/empty set + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0028.yaml b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0028.yaml new file mode 100644 index 000000000000..04648b485aa6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0028.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + all([], __local1__) + __local0__ = __local1__ + } + note: all/empty array + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0029.yaml b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0029.yaml new file mode 100644 index 000000000000..99c1fac7174e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0029.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + all({true}, __local1__) + __local0__ = __local1__ + } + note: all/set success + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0030.yaml b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0030.yaml new file mode 100644 index 000000000000..be00198e028e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0030.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + all([true, true, true], __local1__) + __local0__ = __local1__ + } + note: all/array success + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0031.yaml b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0031.yaml new file mode 100644 index 000000000000..939870f47a76 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0031.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + all({false, true}, __local1__) + __local0__ = __local1__ + } + note: all/set fail + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0032.yaml b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0032.yaml new file mode 100644 index 000000000000..a6413523af21 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0032.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + all([false, true, true], __local1__) + __local0__ = __local1__ + } + note: all/array fail + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0033.yaml b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0033.yaml new file mode 100644 index 000000000000..16571a7fd742 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/all/test-all-0033.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + all([{}, "", true, true, 123], __local1__) + __local0__ = __local1__ + } + note: all/other types + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0034.yaml b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0034.yaml new file mode 100644 index 000000000000..9d06a6b1c652 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0034.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + any(set(), __local1__) + __local0__ = __local1__ + } + note: any/empty set + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0035.yaml b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0035.yaml new file mode 100644 index 000000000000..a3c498b6c9e2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0035.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + any([], __local1__) + __local0__ = __local1__ + } + note: any/empty array + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0036.yaml b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0036.yaml new file mode 100644 index 000000000000..f7bbf9fa87d2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0036.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + any({false, true}, __local1__) + __local0__ = __local1__ + } + note: any/set success + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0037.yaml b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0037.yaml new file mode 100644 index 000000000000..eb7e988b08fd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0037.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + any([true, true, true, false, false], __local1__) + __local0__ = __local1__ + } + note: any/array success + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0038.yaml b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0038.yaml new file mode 100644 index 000000000000..d2bc6d147be8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0038.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + any({false}, __local1__) + __local0__ = __local1__ + } + note: any/set fail + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0039.yaml b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0039.yaml new file mode 100644 index 000000000000..911373244cbb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0039.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + any([false], __local1__) + __local0__ = __local1__ + } + note: any/array fail + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0040.yaml b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0040.yaml new file mode 100644 index 000000000000..70ff6f30b113 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/any/test-any-0040.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + any([true, {}, "false"], __local1__) + __local0__ = __local1__ + } + note: any/other types + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0810.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0810.yaml new file mode 100644 index 000000000000..a09b6150e93a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0810.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[y] { + data.a[i] = x + __local0__ = i + x + y = __local0__ + } + note: arithmetic/plus + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 3 + - 5 + - 7 diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0811.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0811.yaml new file mode 100644 index 000000000000..6b893fe07221 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0811.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[y] { + data.a[i] = x + __local0__ = i - x + y = __local0__ + } + note: arithmetic/minus + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - -1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0812.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0812.yaml new file mode 100644 index 000000000000..6048a046e0a5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0812.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[y] { + data.a[i] = x + __local0__ = i * x + y = __local0__ + } + note: arithmetic/multiply + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 0 + - 2 + - 6 + - 12 diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0813.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0813.yaml new file mode 100644 index 000000000000..95acfd9dd837 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0813.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package test + + p[z] { + data.a[i] = x + y = i / x + round(y, z) + } + note: arithmetic/divide+round + query: data.test.p = x + sort_bindings: true + want_result: + - x: + - 0 + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0814.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0814.yaml new file mode 100644 index 000000000000..870f901d255a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0814.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + a: + - 1 + modules: + - | + package generated + + p = y { + data.a[i] = x + __local0__ = x / i + y = __local0__ + } + note: arithmetic/divide+error + query: data.generated.p = x + strict_error: true + want_error: divide by zero + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0815.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0815.yaml new file mode 100644 index 000000000000..9a7ba6a596f4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0815.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + abs(-10, x) + x = 10 + } + note: arithmetic/abs + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0816.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0816.yaml new file mode 100644 index 000000000000..a5be44291583 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0816.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + __local0__ = 7 % 4 + x = __local0__ + } + note: arithmetic/remainder + query: data.generated.p = x + want_result: + - x: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0817.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0817.yaml new file mode 100644 index 000000000000..ef79e120c522 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0817.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + __local0__ = 7 % 0 + x = __local0__ + } + note: arithmetic/remainder+error + query: data.generated.p = x + want_error: modulo by zero + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0818.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0818.yaml new file mode 100644 index 000000000000..5e5af67226f8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0818.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package test + + p = x { + x = 1.1 % 1 + } + note: arithmetic/remainder+error+floating + query: data.test.p = x + want_error: modulo on floating-point number + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0819.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0819.yaml new file mode 100644 index 000000000000..e23511e2c3c5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0819.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + __local0__ = data.a[3] + abs(-4, __local0__) + } + note: arithmetic/arity 1 ref dest + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0820.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0820.yaml new file mode 100644 index 000000000000..589b2213f4ce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0820.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + __local0__ = data.a[3] + not abs(-5, __local0__) + } + note: arithmetic/arity 1 ref dest (2) + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0821.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0821.yaml new file mode 100644 index 000000000000..b126d9fdb261 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0821.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + __local0__ = 1 + 2 + data.a[2] = __local0__ + } + note: arithmetic/arity 2 ref dest + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0822.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0822.yaml new file mode 100644 index 000000000000..ef3fd180a4c3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0822.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + __local0__ = 2 + 3 + not data.a[2] = __local0__ + } + note: arithmetic/arity 2 ref dest (2) + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0823.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0823.yaml new file mode 100644 index 000000000000..375a3c4bf673 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0823.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + __local0__ = 49649733057 + 1 + __local0__ = 49649733058 + } + note: arithmetic/bug 2469 - precision + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0824.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0824.yaml new file mode 100644 index 000000000000..631bf44f6649 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0824.yaml @@ -0,0 +1,62 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + ceil(1.01, x) + } + note: ceil rounds up + query: data.generated.p = x + want_result: + - x: 2 + - data: {} + modules: + - | + package generated + + p = x { + ceil(1.5, x) + } + note: ceil rounds up (2) + query: data.generated.p = x + want_result: + - x: 2 + - data: {} + modules: + - | + package generated + + p = x { + ceil(2222.2222222222, x) + } + note: ceil rounds up (3) + query: data.generated.p = x + want_result: + - x: 2223 + - data: {} + modules: + - | + package generated + + p = x { + ceil(1, x) + } + note: ceil integer + query: data.generated.p = x + want_result: + - x: 1 + - data: {} + modules: + - | + package generated + + p = x { + ceil(-1.99999, x) + } + note: ceil negative + query: data.generated.p = x + want_result: + - x: -1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0825.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0825.yaml new file mode 100644 index 000000000000..6c010c6b29fe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-0825.yaml @@ -0,0 +1,62 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + floor(1.01, x) + } + note: floor rounds down + query: data.generated.p = x + want_result: + - x: 1 + - data: {} + modules: + - | + package generated + + p = x { + floor(1.5, x) + } + note: floor rounds down (2) + query: data.generated.p = x + want_result: + - x: 1 + - data: {} + modules: + - | + package generated + + p = x { + floor(99.99999, x) + } + note: floor rounds down (3) + query: data.generated.p = x + want_result: + - x: 99 + - data: {} + modules: + - | + package generated + + p = x { + floor(1, x) + } + note: floor integer + query: data.generated.p = x + want_result: + - x: 1 + - data: {} + modules: + - | + package generated + + p = x { + floor(-1.001, x) + } + note: floor negative + query: data.generated.p = x + want_result: + - x: -2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-ll-overflow.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-ll-overflow.yaml new file mode 100644 index 000000000000..d583374bfcaa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-ll-overflow.yaml @@ -0,0 +1,38 @@ +--- +cases: + - data: {} + modules: + - | + package test + + p { + 9223372036854775808 > 1 + } + note: ll overflow + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + + p { + 18446744073709551617 > 1 + } + note: ll overflow (2) + query: data.test.p = x + want_result: + - x: true + - data: {} + modules: + - | + package test + + p { + 99999999999999999999999999999999 > 1 + } + note: ll overflow (2) + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-minus-type-error.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-minus-type-error.yaml new file mode 100644 index 000000000000..ab447ba5ab2b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-arithmetic-minus-type-error.yaml @@ -0,0 +1,28 @@ +--- +cases: + - data: + modules: + - | + package test + + p { + {1} - 1 + } + note: arithmetic/minus/type error + query: data.test.p = x + want_error: operand 2 must be set but got number + want_error_code: eval_type_error + strict_error: true + - data: + modules: + - | + package test + + p { + 1 - {1} + } + note: arithmetic/minus/type error + query: data.test.p = x + want_error: operand 2 must be number but got set + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-big-int-0001.yaml b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-big-int-0001.yaml new file mode 100644 index 000000000000..f308ca219c67 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/arithmetic/test-big-int-0001.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: + modules: + - | + package test + + p { + 28857836529306024611913 != 28857836529306024611912 + } + note: arithmetic/big_int + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0041.yaml b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0041.yaml new file mode 100644 index 000000000000..4b26a268cfa8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0041.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + array.concat([1, 2], [3, 4], __local0__) + x = __local0__ + } + note: array/concat + query: data.generated.p = x + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0042.yaml b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0042.yaml new file mode 100644 index 000000000000..9d9f97063fad --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0042.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p = x { + __local1__ = data.b + array.concat(__local1__, [3, 4], __local0__) + x = __local0__ + } + note: "array/concat: err" + query: data.generated.p = x + strict_error: true + want_error: "array.concat: operand 1 must be array but got object" + want_error_code: eval_type_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0043.yaml b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0043.yaml new file mode 100644 index 000000000000..aafa84f73b2c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0043.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p = x { + __local1__ = data.b + array.concat([1, 2], __local1__, __local0__) + x = __local0__ + } + note: "array/concat: err rhs" + query: data.generated.p = x + strict_error: true + want_error: "array.concat: operand 2 must be array but got object" + want_error_code: eval_type_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0044.yaml b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0044.yaml new file mode 100644 index 000000000000..df62b7688ff1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0044.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + array.slice([1, 2, 3, 4, 5], 1, 3, __local0__) + x = __local0__ + } + note: array/slice + query: data.generated.p = x + want_result: + - x: + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0045.yaml b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0045.yaml new file mode 100644 index 000000000000..785af8cf1df1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0045.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + array.slice([1, 2, 3], 0, 0, __local0__) + x = __local0__ + } + note: "array/slice: empty slice" + query: data.generated.p = x + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0046.yaml b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0046.yaml new file mode 100644 index 000000000000..df6d608647ca --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0046.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + array.slice([1, 2, 3, 4, 5], -4, -1, __local0__) + x = __local0__ + } + note: "array/slice: negative indices" + query: data.generated.p = x + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0047.yaml b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0047.yaml new file mode 100644 index 000000000000..d3029e6e46b7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0047.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + array.slice([1, 2, 3, 4, 5], 4, 1, __local0__) + x = __local0__ + } + note: "array/slice: stopIndex < startIndex" + query: data.generated.p = x + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0048.yaml b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0048.yaml new file mode 100644 index 000000000000..79d00f9f0e55 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0048.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + array.slice([1, 2, 3, 4, 5], -1, 2, __local0__) + x = __local0__ + } + note: "array/slice: clamp startIndex" + query: data.generated.p = x + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0049.yaml b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0049.yaml new file mode 100644 index 000000000000..9dc43af4fbde --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0049.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + array.slice([1, 2, 3, 4, 5], 3, 6, __local0__) + x = __local0__ + } + note: "array/slice: clamp stopIndex" + query: data.generated.p = x + want_result: + - x: + - 4 + - 5 diff --git a/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0050.yaml b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0050.yaml new file mode 100644 index 000000000000..1f133943605f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0050.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + array.slice([], 1000, 2000, __local0__) + x = __local0__ + } + note: "array/slice: clamp both out of range" + query: data.generated.p = x + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0051.yaml b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0051.yaml new file mode 100644 index 000000000000..ba8a64e75279 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0051.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + array.slice([1, 2, 3], 1000, 2000, __local0__) + x = __local0__ + } + note: "array/slice: clamp both out of range non-empty" + query: data.generated.p = x + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0052.yaml b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0052.yaml new file mode 100644 index 000000000000..7f3f187f5626 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/array/test-array-0052.yaml @@ -0,0 +1,44 @@ +--- +cases: + - data: + foo: + - 1 + - 2 + - 3 + modules: + - | + package test + + p := array.reverse(data.foo) + note: array/reverse_123 + query: data.test.p = x + want_result: + - x: + - 3 + - 2 + - 1 + - data: + foo: [] + modules: + - | + package test + + p := array.reverse(data.foo) + note: array/reverse_empty + query: data.test.p = x + want_result: + - x: [] + - data: + foo: + bar: baz + baz: bar + modules: + - | + package test + + p := array.reverse(data.foo) + note: array/reverse_object_error + query: data.test.p = x + strict_error: true + want_error: "array.reverse: operand 1 must be array but got object" + want_error_code: eval_type_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/assignments/test-file-level-assignments.yaml b/third_party/opa/v1/test/cases/testdata/v0/assignments/test-file-level-assignments.yaml new file mode 100644 index 000000000000..1c3cac79df20 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/assignments/test-file-level-assignments.yaml @@ -0,0 +1,49 @@ +--- +cases: + - note: assignments/file-level/default_value + query: data.test = x + modules: + - | + package test + + default a := 1 + want_result: [{ "x": { "a": 1 } }] + - note: assignments/file-level/rule + query: data.test = x + modules: + - | + package test + + b := 2 + want_result: [{ "x": { "b": 2 } }] + - note: assignments/file-level/else_keyword + query: data.test = x + modules: + - | + package test + + c := 3 { + false + } else := 4 { + true + } + want_result: [{ "x": { "c": 4 } }] + - note: assignments/file-level/partial_rule + query: data.test = x + modules: + - | + package test + + d[msg] := 5 { + msg = [1, 2, 3][_] + } + want_result: [{ "x": { "d": { "1": 5, "2": 5, "3": 5 } } }] + - note: assignments/file-level/function_return_value + query: data.test = x + modules: + - | + package test + + e := f(6) + f(x) := x + want_result: [{ "x": { "e": 6 } }] diff --git a/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0929.yaml b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0929.yaml new file mode 100644 index 000000000000..f7a1d7ff9bf5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0929.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + base64.encode("hello", x) + } + note: base64builtins/encode-1 + query: data.generated.p = x + want_result: + - x: aGVsbG8= diff --git a/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0930.yaml b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0930.yaml new file mode 100644 index 000000000000..af04b1ea922f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0930.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + base64.encode("there", x) + } + note: base64builtins/encode-2 + query: data.generated.p = x + want_result: + - x: dGhlcmU= diff --git a/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0931.yaml b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0931.yaml new file mode 100644 index 000000000000..0536b4d0c0f0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0931.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + base64.decode("aGVsbG8=", x) + } + note: base64builtins/decode-1 + query: data.generated.p = x + want_result: + - x: hello diff --git a/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0932.yaml b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0932.yaml new file mode 100644 index 000000000000..41023aa5201c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0932.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + base64.decode("dGhlcmU=", x) + } + note: base64builtins/decode-2 + query: data.generated.p = x + want_result: + - x: there diff --git a/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0933.yaml b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0933.yaml new file mode 100644 index 000000000000..596b04919dad --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0933.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + base64.encode("subjects?_d", x) + } + note: base64builtins/encode-slash + query: data.generated.p = x + want_result: + - x: c3ViamVjdHM/X2Q= diff --git a/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0934.yaml b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0934.yaml new file mode 100644 index 000000000000..688e867557a8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0934.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + base64.decode("c3ViamVjdHM/X2Q=", x) + } + note: base64builtins/decode-slash + query: data.generated.p = x + want_result: + - x: subjects?_d diff --git a/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0935.yaml b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0935.yaml new file mode 100644 index 000000000000..0f8ce2e03bbb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/base64builtins/test-base64builtins-0935.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + base64.is_valid("aGVsbG8=", x) + } + note: base64builtins/is_valid-true + query: data.generated.p = x + want_result: + - x: true + - data: {} + modules: + - | + package generated + + p = x { + base64.is_valid("{'not':'base64'}", x) + } + note: base64builtins/is_valid-false + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/base64urlbuiltins/test-base64urlbuiltins-0935.yaml b/third_party/opa/v1/test/cases/testdata/v0/base64urlbuiltins/test-base64urlbuiltins-0935.yaml new file mode 100644 index 000000000000..99e5748249fe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/base64urlbuiltins/test-base64urlbuiltins-0935.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: base64urlbuiltins/encode-1 + modules: + - | + package generated + + p = x { + base64url.encode("hello", x) + } + data: + query: data.generated.p = x + want_result: + - x: aGVsbG8= + - note: base64urlbuiltins/encode-2 + modules: + - | + package generated + + p = x { + base64url.encode("there", x) + } + data: + query: data.generated.p = x + want_result: + - x: dGhlcmU= diff --git a/third_party/opa/v1/test/cases/testdata/v0/base64urlbuiltins/test-base64urlbuiltins-0937.yaml b/third_party/opa/v1/test/cases/testdata/v0/base64urlbuiltins/test-base64urlbuiltins-0937.yaml new file mode 100644 index 000000000000..02118ee4f81b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/base64urlbuiltins/test-base64urlbuiltins-0937.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: base64urlbuiltins/decode-1 padded string + modules: + - | + package generated + + p = x { + base64url.decode("aGVsbG8=", x) + } + data: + query: data.generated.p = x + want_result: + - x: hello + - note: base64urlbuiltins/decode-2 non-padded string + modules: + - | + package generated + + p = x { + base64url.decode("aGVsbG8", x) + } + data: + query: data.generated.p = x + want_result: + - x: hello diff --git a/third_party/opa/v1/test/cases/testdata/v0/base64urlbuiltins/test-base64urlbuiltins-0939.yaml b/third_party/opa/v1/test/cases/testdata/v0/base64urlbuiltins/test-base64urlbuiltins-0939.yaml new file mode 100644 index 000000000000..c7e8cb0959cd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/base64urlbuiltins/test-base64urlbuiltins-0939.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: base64urlbuiltins/encode-1 without padding + query: data.generated.p = x + modules: + - | + package generated + p = x { + base64url.encode_no_pad("hello", x) + } + data: + want_result: + - x: aGVsbG8 + - note: base64urlbuiltins/encode-2 without padding + query: data.generated.p = x + modules: + - | + package generated + p = x { + base64url.encode_no_pad("there", x) + } + data: + want_result: + - x: dGhlcmU diff --git a/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0695.yaml b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0695.yaml new file mode 100644 index 000000000000..04bb4d29d3a9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0695.yaml @@ -0,0 +1,189 @@ +--- +cases: + - data: + topdown: + a: + b: + c: + x: + - 100 + - 200 + y: false + z: + a: b + input_term: "{}" + modules: + - | + package enum_errors.caller + + p[x] = y { + data.enum_errors.a[x] = y + } + - | + package enum_errors.a.b.c + + p = x { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.a.b.c.undefined1 + + p { + false + } + + p { + false + } + + q { + false + } + - | + package topdown + + p[[x1, x2, x3, x4]] { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q[[x1, x2, x3]] { + data.topdown.a.b[x1][x2][0] = x3 + } + + r[[x1, x2]] { + data.topdown.a.b[x1] = x2 + } + + s = __local1__ { + true + __local1__ = data.topdown.no + } + + t = __local2__ { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u = __local3__ { + true + __local3__ = data.topdown.missing.input.value + } + + v = __local4__ { + true + __local4__ = data.topdown.g + } + + w = __local5__ { + true + __local5__ = data.topdown.set + } + + iterate_ground[x] { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown.no.base.doc + + p = true + - | + package topdown.a.b.c.empty + - | + package topdown.a.b.c.s + + w = {"f": 10, "g": 9.9} + - | + package topdown.set + + v[__local6__] { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown.missing.input.value + + p = __local7__ { + true + __local7__ = input.deadbeef + } + - | + package topdown.a.b.c + + p = [1, 2] + + q = [3, 4] + + r["a"] = 1 + + r["b"] = 2 + - | + package topdown.a.b.c.undefined2 + + p { + input.foo + } + - | + package topdown.virtual.constants + + p = 1 + + q = 2 + + r = 1 + - | + package topdown.conflicts + + k = "bar" + - | + package topdown.g.h + + p { + false + } + note: baseandvirtualdocs/base/virtual + query: data.topdown.p = x + sort_bindings: true + want_result: + - x: + - - c + - p + - 0 + - 1 + - - c + - p + - 1 + - 2 + - - c + - q + - 0 + - 3 + - - c + - q + - 1 + - 4 + - - c + - r + - a + - 1 + - - c + - r + - b + - 2 + - - c + - s + - w + - f: 10 + g: 9.9 + - - c + - x + - 0 + - 100 + - - c + - x + - 1 + - 200 + - - c + - z + - a + - b diff --git a/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0696.yaml b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0696.yaml new file mode 100644 index 000000000000..08a9eebf8a00 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0696.yaml @@ -0,0 +1,273 @@ +--- +cases: + - data: + topdown: + a: + b: + c: + x: + - 100 + - 200 + y: false + z: + a: b + input_term: "{}" + modules: + - | + package partial.topdown + + p[["c", "x", 0, x41]] { + data.topdown.a.b.c.x[0] = x41 + } + + p[["c", "x", 1, x41]] { + data.topdown.a.b.c.x[1] = x41 + } + + p[["c", "z", "a", x41]] { + data.topdown.a.b.c.z.a = x41 + } + + p[[ + "c", "p", 0, + 1, + ]] + + p[[ + "c", "p", 1, + 2, + ]] + + p[[ + "c", "q", 0, + 3, + ]] + + p[[ + "c", "q", 1, + 4, + ]] + + p[[ + "c", "r", + "a", 1, + ]] + + p[[ + "c", "r", + "b", 2, + ]] + + p[[ + "c", "s", "w", + {"f": 10, "g": 9.9}, + ]] + + p[[ + "c", "undefined2", "p", + true, + ]] { + input.foo + } + + p[["c", "x", 0, x41]] { + data.topdown.a.b.c.x[0] = x41 + } + + p[["c", "x", 1, x41]] { + data.topdown.a.b.c.x[1] = x41 + } + + p[["c", "z", "a", x41]] { + data.topdown.a.b.c.z.a = x41 + } + + p[[ + "c", "p", 0, + 1, + ]] + + p[[ + "c", "p", 1, + 2, + ]] + + p[[ + "c", "q", 0, + 3, + ]] + + p[[ + "c", "q", 1, + 4, + ]] + + p[[ + "c", "r", + "a", 1, + ]] + + p[[ + "c", "r", + "b", 2, + ]] + + p[[ + "c", "s", "w", + {"f": 10, "g": 9.9}, + ]] + + p[[ + "c", "undefined2", "p", + true, + ]] { + input.foo + } + - | + package topdown.a.b.c.empty + - | + package topdown.a.b.c + + p = [1, 2] + + q = [3, 4] + + r["a"] = 1 + + r["b"] = 2 + - | + package topdown.a.b.c.undefined2 + + p { + input.foo + } + - | + package topdown.virtual.constants + + p = 1 + + q = 2 + + r = 1 + - | + package topdown.no.base.doc + + p = true + - | + package topdown.a.b.c.s + + w = {"f": 10, "g": 9.9} + - | + package topdown.missing.input.value + + p = __local7__ { + true + __local7__ = input.deadbeef + } + - | + package topdown.conflicts + + k = "bar" + - | + package topdown.g.h + + p { + false + } + - | + package enum_errors.a.b.c + + p = x { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.a.b.c.undefined1 + + p { + false + } + + p { + false + } + + q { + false + } + - | + package topdown + + p[[x1, x2, x3, x4]] { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q[[x1, x2, x3]] { + data.topdown.a.b[x1][x2][0] = x3 + } + + r[[x1, x2]] { + data.topdown.a.b[x1] = x2 + } + + s = __local1__ { + true + __local1__ = data.topdown.no + } + + t = __local2__ { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u = __local3__ { + true + __local3__ = data.topdown.missing.input.value + } + + v = __local4__ { + true + __local4__ = data.topdown.g + } + + w = __local5__ { + true + __local5__ = data.topdown.set + } + + iterate_ground[x] { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown.set + + v[__local6__] { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown_test_partial + + __result__ = _result { + data.partial.topdown.p = _result + } + - | + package enum_errors.caller + + p[x] = y { + data.enum_errors.a[x] = y + } + note: "baseandvirtualdocs/base/virtual: ground key" + query: data.topdown.q = x + sort_bindings: true + want_result: + - x: + - - c + - p + - 1 + - - c + - q + - 3 + - - c + - x + - 100 diff --git a/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0697.yaml b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0697.yaml new file mode 100644 index 000000000000..810a6aa2dd5c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0697.yaml @@ -0,0 +1,293 @@ +--- +cases: + - data: + topdown: + a: + b: + c: + x: + - 100 + - 200 + y: false + z: + a: b + input_term: "{}" + modules: + - | + package enum_errors.a.b.c + + p = x { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.virtual.constants + + p = 1 + + q = 2 + + r = 1 + - | + package partial.topdown + + p[["c", "x", 0, x41]] { + data.topdown.a.b.c.x[0] = x41 + } + + p[["c", "x", 1, x41]] { + data.topdown.a.b.c.x[1] = x41 + } + + p[["c", "z", "a", x41]] { + data.topdown.a.b.c.z.a = x41 + } + + p[[ + "c", "p", 0, + 1, + ]] + + p[[ + "c", "p", 1, + 2, + ]] + + p[[ + "c", "q", 0, + 3, + ]] + + p[[ + "c", "q", 1, + 4, + ]] + + p[[ + "c", "r", + "a", 1, + ]] + + p[[ + "c", "r", + "b", 2, + ]] + + p[[ + "c", "s", "w", + {"f": 10, "g": 9.9}, + ]] + + p[[ + "c", "undefined2", "p", + true, + ]] { + input.foo + } + + p[["c", "x", 0, x41]] { + data.topdown.a.b.c.x[0] = x41 + } + + p[["c", "x", 1, x41]] { + data.topdown.a.b.c.x[1] = x41 + } + + p[["c", "z", "a", x41]] { + data.topdown.a.b.c.z.a = x41 + } + + p[[ + "c", "p", 0, + 1, + ]] + + p[[ + "c", "p", 1, + 2, + ]] + + p[[ + "c", "q", 0, + 3, + ]] + + p[[ + "c", "q", 1, + 4, + ]] + + p[[ + "c", "r", + "a", 1, + ]] + + p[[ + "c", "r", + "b", 2, + ]] + + p[[ + "c", "s", "w", + {"f": 10, "g": 9.9}, + ]] + + p[[ + "c", "undefined2", "p", + true, + ]] { + input.foo + } + - | + package topdown.missing.input.value + + p = __local7__ { + true + __local7__ = input.deadbeef + } + - | + package topdown.conflicts + + k = "bar" + - | + package topdown.g.h + + p { + false + } + - | + package topdown.a.b.c + + p = [1, 2] + + q = [3, 4] + + r["a"] = 1 + + r["b"] = 2 + - | + package topdown.a.b.c.undefined2 + + p { + input.foo + } + - | + package topdown.a.b.c.s + + w = {"f": 10, "g": 9.9} + - | + package topdown + + p[[x1, x2, x3, x4]] { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q[[x1, x2, x3]] { + data.topdown.a.b[x1][x2][0] = x3 + } + + r[[x1, x2]] { + data.topdown.a.b[x1] = x2 + } + + s = __local1__ { + true + __local1__ = data.topdown.no + } + + t = __local2__ { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u = __local3__ { + true + __local3__ = data.topdown.missing.input.value + } + + v = __local4__ { + true + __local4__ = data.topdown.g + } + + w = __local5__ { + true + __local5__ = data.topdown.set + } + + iterate_ground[x] { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown_test_partial + + __result__ = _result { + _result = {[ + "c", "p", + 1, + ], [ + "c", "q", + 3, + ], ["c", "x", 100]} + } + - | + package topdown.a.b.c.undefined1 + + p { + false + } + + p { + false + } + + q { + false + } + - | + package topdown.no.base.doc + + p = true + - | + package topdown.set + + v[__local6__] { + true + __local6__ = data.topdown.set.u[_] + } + - | + package enum_errors.caller + + p[x] = y { + data.enum_errors.a[x] = y + } + - | + package topdown.a.b.c.empty + note: "baseandvirtualdocs/base/virtual: prefix" + query: data.topdown.r = x + sort_bindings: true + want_result: + - x: + - - c + - empty: {} + p: + - 1 + - 2 + q: + - 3 + - 4 + r: + a: 1 + b: 2 + s: + w: + f: 10 + g: 9.9 + undefined1: {} + undefined2: {} + x: + - 100 + - 200 + y: false + z: + a: b diff --git a/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0698.yaml b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0698.yaml new file mode 100644 index 000000000000..6b20b55ffd04 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0698.yaml @@ -0,0 +1,170 @@ +--- +cases: + - data: + topdown: + set: + u: + - 1 + - 2 + - 3 + - 4 + input_term: "{}" + modules: + - | + package enum_errors.a.b.c + + p = x { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.missing.input.value + + p = __local7__ { + true + __local7__ = input.deadbeef + } + - | + package topdown.a.b.c.undefined1 + + p { + false + } + + p { + false + } + + q { + false + } + - | + package topdown.set + + v[__local6__] { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown.conflicts + + k = "bar" + - | + package topdown.g.h + + p { + false + } + - | + package topdown_test_partial + + __result__ = _result { + data.partial.topdown.r = _result + } + - | + package topdown.a.b.c.empty + - | + package partial.topdown + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + - | + package topdown.a.b.c + + p = [1, 2] + + q = [3, 4] + + r["a"] = 1 + + r["b"] = 2 + - | + package topdown.a.b.c.undefined2 + + p { + input.foo + } + - | + package topdown + + p[[x1, x2, x3, x4]] { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q[[x1, x2, x3]] { + data.topdown.a.b[x1][x2][0] = x3 + } + + r[[x1, x2]] { + data.topdown.a.b[x1] = x2 + } + + s = __local1__ { + true + __local1__ = data.topdown.no + } + + t = __local2__ { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u = __local3__ { + true + __local3__ = data.topdown.missing.input.value + } + + v = __local4__ { + true + __local4__ = data.topdown.g + } + + w = __local5__ { + true + __local5__ = data.topdown.set + } + + iterate_ground[x] { + data.topdown.virtual.constants[x] = 1 + } + - | + package enum_errors.caller + + p[x] = y { + data.enum_errors.a[x] = y + } + - | + package topdown.a.b.c.s + + w = {"f": 10, "g": 9.9} + - | + package topdown.no.base.doc + + p = true + - | + package topdown.virtual.constants + + p = 1 + + q = 2 + + r = 1 + note: "baseandvirtualdocs/base/virtual: set" + query: data.topdown.w = x + want_result: + - x: + u: + - 1 + - 2 + - 3 + - 4 + v: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0699.yaml b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0699.yaml new file mode 100644 index 000000000000..4be903f5bba1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0699.yaml @@ -0,0 +1,156 @@ +--- +cases: + - data: {} + input_term: "{}" + modules: + - | + package topdown.conflicts + + k = "bar" + - | + package topdown.no.base.doc + + p = true + - | + package topdown.set + + v[__local6__] { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown.a.b.c.empty + - | + package enum_errors.caller + + p[x] = y { + data.enum_errors.a[x] = y + } + - | + package topdown.a.b.c.undefined1 + + p { + false + } + + p { + false + } + + q { + false + } + - | + package topdown_test_partial + + __result__ = _result { + _result = {"u": [1, 2, 3, 4], "v": {1, 2, 3, 4}} + } + - | + package topdown.a.b.c + + p = [1, 2] + + q = [3, 4] + + r["a"] = 1 + + r["b"] = 2 + - | + package topdown.a.b.c.s + + w = {"f": 10, "g": 9.9} + - | + package topdown.a.b.c.undefined2 + + p { + input.foo + } + - | + package topdown.virtual.constants + + p = 1 + + q = 2 + + r = 1 + - | + package enum_errors.a.b.c + + p = x { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.missing.input.value + + p = __local7__ { + true + __local7__ = input.deadbeef + } + - | + package topdown.g.h + + p { + false + } + - | + package topdown + + p[[x1, x2, x3, x4]] { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q[[x1, x2, x3]] { + data.topdown.a.b[x1][x2][0] = x3 + } + + r[[x1, x2]] { + data.topdown.a.b[x1] = x2 + } + + s = __local1__ { + true + __local1__ = data.topdown.no + } + + t = __local2__ { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u = __local3__ { + true + __local3__ = data.topdown.missing.input.value + } + + v = __local4__ { + true + __local4__ = data.topdown.g + } + + w = __local5__ { + true + __local5__ = data.topdown.set + } + + iterate_ground[x] { + data.topdown.virtual.constants[x] = 1 + } + - | + package partial.topdown + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + note: "baseandvirtualdocs/base/virtual: no base" + query: data.topdown.s = x + want_result: + - x: + base: + doc: + p: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0700.yaml b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0700.yaml new file mode 100644 index 000000000000..57ac8e180aea --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0700.yaml @@ -0,0 +1,153 @@ +--- +cases: + - data: {} + input_term: "{}" + modules: + - | + package enum_errors.caller + + p[x] = y { + data.enum_errors.a[x] = y + } + - | + package topdown.a.b.c.undefined1 + + p { + false + } + + p { + false + } + + q { + false + } + - | + package topdown.a.b.c.empty + - | + package topdown.missing.input.value + + p = __local7__ { + true + __local7__ = input.deadbeef + } + - | + package topdown.a.b.c.undefined2 + + p { + input.foo + } + - | + package topdown.no.base.doc + + p = true + - | + package topdown.set + + v[__local6__] { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown.a.b.c + + p = [1, 2] + + q = [3, 4] + + r["a"] = 1 + + r["b"] = 2 + - | + package topdown_test_partial + + __result__ = _result { + _result = {"base": {"doc": {"p": true}}} + } + - | + package topdown + + p[[x1, x2, x3, x4]] { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q[[x1, x2, x3]] { + data.topdown.a.b[x1][x2][0] = x3 + } + + r[[x1, x2]] { + data.topdown.a.b[x1] = x2 + } + + s = __local1__ { + true + __local1__ = data.topdown.no + } + + t = __local2__ { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u = __local3__ { + true + __local3__ = data.topdown.missing.input.value + } + + v = __local4__ { + true + __local4__ = data.topdown.g + } + + w = __local5__ { + true + __local5__ = data.topdown.set + } + + iterate_ground[x] { + data.topdown.virtual.constants[x] = 1 + } + - | + package partial.topdown + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + - | + package topdown.virtual.constants + + p = 1 + + q = 2 + + r = 1 + - | + package enum_errors.a.b.c + + p = x { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.g.h + + p { + false + } + - | + package topdown.conflicts + + k = "bar" + - | + package topdown.a.b.c.s + + w = {"f": 10, "g": 9.9} + note: "baseandvirtualdocs/base/virtual: undefined" + query: data.topdown.t = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0701.yaml b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0701.yaml new file mode 100644 index 000000000000..d143df74398c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0701.yaml @@ -0,0 +1,165 @@ +--- +cases: + - data: + topdown: + g: + h: + k: + - 1 + - 2 + - 3 + input_term: "{}" + modules: + - | + package topdown.a.b.c.undefined1 + + p { + false + } + + p { + false + } + + q { + false + } + - | + package topdown.set + + v[__local6__] { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown.a.b.c + + p = [1, 2] + + q = [3, 4] + + r["a"] = 1 + + r["b"] = 2 + - | + package topdown_test_partial + + __result__ = _result { + _result = {} + } + - | + package enum_errors.a.b.c + + p = x { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.g.h + + p { + false + } + - | + package topdown.a.b.c.undefined2 + + p { + input.foo + } + - | + package topdown.no.base.doc + + p = true + - | + package topdown.a.b.c.s + + w = {"f": 10, "g": 9.9} + - | + package enum_errors.caller + + p[x] = y { + data.enum_errors.a[x] = y + } + - | + package topdown.virtual.constants + + p = 1 + + q = 2 + + r = 1 + - | + package topdown.a.b.c.empty + - | + package topdown.missing.input.value + + p = __local7__ { + true + __local7__ = input.deadbeef + } + - | + package topdown.conflicts + + k = "bar" + - | + package topdown + + p[[x1, x2, x3, x4]] { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q[[x1, x2, x3]] { + data.topdown.a.b[x1][x2][0] = x3 + } + + r[[x1, x2]] { + data.topdown.a.b[x1] = x2 + } + + s = __local1__ { + true + __local1__ = data.topdown.no + } + + t = __local2__ { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u = __local3__ { + true + __local3__ = data.topdown.missing.input.value + } + + v = __local4__ { + true + __local4__ = data.topdown.g + } + + w = __local5__ { + true + __local5__ = data.topdown.set + } + + iterate_ground[x] { + data.topdown.virtual.constants[x] = 1 + } + - | + package partial.topdown + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + note: "baseandvirtualdocs/base/virtual: undefined-2" + query: data.topdown.v = x + want_result: + - x: + h: + k: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0702.yaml b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0702.yaml new file mode 100644 index 000000000000..822a758eef6d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0702.yaml @@ -0,0 +1,153 @@ +--- +cases: + - data: {} + input_term: "{}" + modules: + - | + package topdown.no.base.doc + + p = true + - | + package enum_errors.caller + + p[x] = y { + data.enum_errors.a[x] = y + } + - | + package topdown.a.b.c.empty + - | + package topdown.conflicts + + k = "bar" + - | + package topdown + + p[[x1, x2, x3, x4]] { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q[[x1, x2, x3]] { + data.topdown.a.b[x1][x2][0] = x3 + } + + r[[x1, x2]] { + data.topdown.a.b[x1] = x2 + } + + s = __local1__ { + true + __local1__ = data.topdown.no + } + + t = __local2__ { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u = __local3__ { + true + __local3__ = data.topdown.missing.input.value + } + + v = __local4__ { + true + __local4__ = data.topdown.g + } + + w = __local5__ { + true + __local5__ = data.topdown.set + } + + iterate_ground[x] { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown.g.h + + p { + false + } + - | + package topdown.a.b.c.undefined1 + + p { + false + } + + p { + false + } + + q { + false + } + - | + package partial.topdown + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + - | + package topdown.virtual.constants + + p = 1 + + q = 2 + + r = 1 + - | + package topdown.set + + v[__local6__] { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown_test_partial + + __result__ = _result { + _result = {"h": {"k": [1, 2, 3]}} + } + - | + package enum_errors.a.b.c + + p = x { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.a.b.c.undefined2 + + p { + input.foo + } + - | + package topdown.a.b.c.s + + w = {"f": 10, "g": 9.9} + - | + package topdown.missing.input.value + + p = __local7__ { + true + __local7__ = input.deadbeef + } + - | + package topdown.a.b.c + + p = [1, 2] + + q = [3, 4] + + r["a"] = 1 + + r["b"] = 2 + note: "baseandvirtualdocs/base/virtual: missing input value" + query: data.topdown.u = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0703.yaml b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0703.yaml new file mode 100644 index 000000000000..1d8d87b54bfc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0703.yaml @@ -0,0 +1,156 @@ +--- +cases: + - data: {} + input_term: "{}" + modules: + - | + package topdown.a.b.c + + p = [1, 2] + + q = [3, 4] + + r["a"] = 1 + + r["b"] = 2 + - | + package topdown.a.b.c.s + + w = {"f": 10, "g": 9.9} + - | + package topdown.no.base.doc + + p = true + - | + package partial.topdown + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + - | + package enum_errors.a.b.c + + p = x { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.set + + v[__local6__] { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown.conflicts + + k = "bar" + - | + package topdown + + p[[x1, x2, x3, x4]] { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q[[x1, x2, x3]] { + data.topdown.a.b[x1][x2][0] = x3 + } + + r[[x1, x2]] { + data.topdown.a.b[x1] = x2 + } + + s = __local1__ { + true + __local1__ = data.topdown.no + } + + t = __local2__ { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u = __local3__ { + true + __local3__ = data.topdown.missing.input.value + } + + v = __local4__ { + true + __local4__ = data.topdown.g + } + + w = __local5__ { + true + __local5__ = data.topdown.set + } + + iterate_ground[x] { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown.a.b.c.undefined1 + + p { + false + } + + p { + false + } + + q { + false + } + - | + package topdown.virtual.constants + + p = 1 + + q = 2 + + r = 1 + - | + package topdown.missing.input.value + + p = __local7__ { + true + __local7__ = input.deadbeef + } + - | + package enum_errors.caller + + p[x] = y { + data.enum_errors.a[x] = y + } + - | + package topdown.a.b.c.empty + - | + package topdown_test_partial + + __result__ = _result { + data.topdown.missing.input.value = _result + } + - | + package topdown.g.h + + p { + false + } + - | + package topdown.a.b.c.undefined2 + + p { + input.foo + } + note: baseandvirtualdocs/iterate ground + query: data.topdown.iterate_ground = x + sort_bindings: true + want_result: + - x: + - p + - r diff --git a/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0704.yaml b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0704.yaml new file mode 100644 index 000000000000..014cbf616566 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0704.yaml @@ -0,0 +1,157 @@ +--- +cases: + - data: + topdown: + conflicts: + k: foo + input_term: "{}" + modules: + - | + package topdown_test_partial + + __result__ = _result { + _result = {"p", "r"} + } + - | + package topdown.missing.input.value + + p = __local7__ { + true + __local7__ = input.deadbeef + } + - | + package topdown.a.b.c.s + + w = {"f": 10, "g": 9.9} + - | + package topdown.a.b.c.undefined1 + + p { + false + } + + p { + false + } + + q { + false + } + - | + package topdown.set + + v[__local6__] { + true + __local6__ = data.topdown.set.u[_] + } + - | + package enum_errors.caller + + p[x] = y { + data.enum_errors.a[x] = y + } + - | + package topdown.conflicts + + k = "bar" + - | + package topdown + + p[[x1, x2, x3, x4]] { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q[[x1, x2, x3]] { + data.topdown.a.b[x1][x2][0] = x3 + } + + r[[x1, x2]] { + data.topdown.a.b[x1] = x2 + } + + s = __local1__ { + true + __local1__ = data.topdown.no + } + + t = __local2__ { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u = __local3__ { + true + __local3__ = data.topdown.missing.input.value + } + + v = __local4__ { + true + __local4__ = data.topdown.g + } + + w = __local5__ { + true + __local5__ = data.topdown.set + } + + iterate_ground[x] { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown.no.base.doc + + p = true + - | + package partial.topdown + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + - | + package enum_errors.a.b.c + + p = x { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.a.b.c + + p = [1, 2] + + q = [3, 4] + + r["a"] = 1 + + r["b"] = 2 + - | + package topdown.a.b.c.empty + - | + package topdown.a.b.c.undefined2 + + p { + input.foo + } + - | + package topdown.virtual.constants + + p = 1 + + q = 2 + + r = 1 + - | + package topdown.g.h + + p { + false + } + note: "baseandvirtualdocs/base/virtual: conflicts" + query: data.topdown.conflicts = x + want_result: + - x: + k: foo diff --git a/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0705.yaml b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0705.yaml new file mode 100644 index 000000000000..822a1e99dfbe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/baseandvirtualdocs/test-baseandvirtualdocs-0705.yaml @@ -0,0 +1,158 @@ +--- +cases: + - data: {} + input_term: "{}" + modules: + - | + package topdown.a.b.c.s + + w = {"f": 10, "g": 9.9} + - | + package topdown.a.b.c.undefined1 + + p { + false + } + + p { + false + } + + q { + false + } + - | + package topdown.set + + v[__local6__] { + true + __local6__ = data.topdown.set.u[_] + } + - | + package enum_errors.a.b.c + + p = x { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.a.b.c.empty + - | + package topdown.virtual.constants + + p = 1 + + q = 2 + + r = 1 + - | + package topdown.g.h + + p { + false + } + - | + package enum_errors.caller + + p[x] = y { + data.enum_errors.a[x] = y + } + - | + package partial.topdown + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + + r[["c", x21]] { + data.topdown.a.b.c = x21 + } + - | + package topdown.missing.input.value + + p = __local7__ { + true + __local7__ = input.deadbeef + } + - | + package topdown.conflicts + + k = "bar" + - | + package topdown.no.base.doc + + p = true + - | + package topdown.a.b.c.undefined2 + + p { + input.foo + } + - | + package topdown_test_partial + + __result__ = _result { + _result = {"k": "foo"} + } + - | + package topdown + + p[[x1, x2, x3, x4]] { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q[[x1, x2, x3]] { + data.topdown.a.b[x1][x2][0] = x3 + } + + r[[x1, x2]] { + data.topdown.a.b[x1] = x2 + } + + s = __local1__ { + true + __local1__ = data.topdown.no + } + + t = __local2__ { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u = __local3__ { + true + __local3__ = data.topdown.missing.input.value + } + + v = __local4__ { + true + __local4__ = data.topdown.g + } + + w = __local5__ { + true + __local5__ = data.topdown.set + } + + iterate_ground[x] { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown.a.b.c + + p = [1, 2] + + q = [3, 4] + + r["a"] = 1 + + r["b"] = 2 + note: baseandvirtualdocs/enumerate virtual errors + query: data.enum_errors.caller.p = x + strict_error: true + want_error: divide by zero + want_error_code: eval_builtin_error + want_result: + - x: + b: + c: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsand/test-bitsand-0055.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsand/test-bitsand-0055.yaml new file mode 100644 index 000000000000..6e2eb5e0030c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsand/test-bitsand-0055.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + bits.and(7, 9, __local1__) + __local0__ = __local1__ + } + note: bitsand/basic bitwise-and + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsand/test-bitsand-0056.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsand/test-bitsand-0056.yaml new file mode 100644 index 000000000000..e65f02800f89 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsand/test-bitsand-0056.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + bits.and(50, 0, __local1__) + __local0__ = __local1__ + } + note: bitsand/and with zero is and + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsand/test-bitsand-0057.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsand/test-bitsand-0057.yaml new file mode 100644 index 000000000000..a8bcee5a8e09 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsand/test-bitsand-0057.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + bits.and(7.2, 42, __local1__) + __local0__ = __local1__ + } + note: bitsand/lhs (float) error + query: data.generated.p = x + want_error: "bits.and: operand 1 must be integer number but got floating-point number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsnegate/test-bitsnegate-0058.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsnegate/test-bitsnegate-0058.yaml new file mode 100644 index 000000000000..b35153a128b5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsnegate/test-bitsnegate-0058.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + bits.negate(42, __local1__) + __local0__ = __local1__ + } + note: bitsnegate/basic bitwise-negate + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - -43 diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsnegate/test-bitsnegate-0059.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsnegate/test-bitsnegate-0059.yaml new file mode 100644 index 000000000000..1a4ee56b660f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsnegate/test-bitsnegate-0059.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + bits.negate(7.2, __local1__) + __local0__ = __local1__ + } + note: bitsnegate/float error + query: data.generated.p = x + want_error: + "bits.negate: operand 1 must be integer number but got floating-point + number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsor/test-bitsor-0052.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsor/test-bitsor-0052.yaml new file mode 100644 index 000000000000..e7b85b302e1e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsor/test-bitsor-0052.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + bits.or(7, 9, __local1__) + __local0__ = __local1__ + } + note: bitsor/basic bitwise-or + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 15 diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsor/test-bitsor-0053.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsor/test-bitsor-0053.yaml new file mode 100644 index 000000000000..9a0c805851c5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsor/test-bitsor-0053.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + bits.or(50, 0, __local1__) + __local0__ = __local1__ + } + note: bitsor/or with zero is value + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 50 diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsor/test-bitsor-0054.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsor/test-bitsor-0054.yaml new file mode 100644 index 000000000000..f6c068d7b45f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsor/test-bitsor-0054.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + bits.or(7.2, 42, __local1__) + __local0__ = __local1__ + } + note: bitsor/lhs (float) error + query: data.generated.p = x + want_error: "bits.or: operand 1 must be integer number but got floating-point number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0063.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0063.yaml new file mode 100644 index 000000000000..b2033d659152 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0063.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + bits.lsh(1, 3, __local1__) + __local0__ = __local1__ + } + note: bitsshiftleft/basic shift-left + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 8 diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0064.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0064.yaml new file mode 100644 index 000000000000..ce2bc41250a2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0064.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + bits.lsh(7.2, 42, __local1__) + __local0__ = __local1__ + } + note: bitsshiftleft/lhs (float) error + query: data.generated.p = x + want_error: "bits.lsh: operand 1 must be integer number but got floating-point number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0065.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0065.yaml new file mode 100644 index 000000000000..87d97a973b6c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0065.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + bits.lsh(7, -1, __local1__) + __local0__ = __local1__ + } + note: bitsshiftleft/rhs must be unsigned + query: data.generated.p = x + want_error: + "bits.lsh: operand 2 must be an unsigned integer number but got a negative + integer" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0066.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0066.yaml new file mode 100644 index 000000000000..01c00f03d226 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0066.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + bits.lsh(2147483647, 1, __local1__) + __local0__ = __local1__ + } + note: bitsshiftleft/shift of max int32 doesn't overflow + query: data.generated.p = x + want_result: + - x: 4294967294 diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0067.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0067.yaml new file mode 100644 index 000000000000..0bdccef4710a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftleft/test-bitsshiftleft-0067.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + bits.lsh(9223372036854775807, 1, __local1__) + __local0__ = __local1__ + } + note: bitsshiftleft/shift of max int64 doesn't overflow and is not lossy + query: data.generated.p = x + want_result: + - x: 18446744073709551614 diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsshiftright/test-bitsshiftright-0068.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftright/test-bitsshiftright-0068.yaml new file mode 100644 index 000000000000..4c178e912b53 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftright/test-bitsshiftright-0068.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + bits.rsh(8, 3, __local1__) + __local0__ = __local1__ + } + note: bitsshiftright/basic shift-right + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsshiftright/test-bitsshiftright-0069.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftright/test-bitsshiftright-0069.yaml new file mode 100644 index 000000000000..de6573529872 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftright/test-bitsshiftright-0069.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + bits.rsh(7.2, 42, __local1__) + __local0__ = __local1__ + } + note: bitsshiftright/lhs (float) error + query: data.generated.p = x + want_error: "bits.rsh: operand 1 must be integer number but got floating-point number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsshiftright/test-bitsshiftright-0070.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftright/test-bitsshiftright-0070.yaml new file mode 100644 index 000000000000..0e1a7e8b246a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsshiftright/test-bitsshiftright-0070.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + bits.rsh(7, -1, __local1__) + __local0__ = __local1__ + } + note: bitsshiftright/rhs must be unsigned + query: data.generated.p = x + want_error: + "bits.rsh: operand 2 must be an unsigned integer number but got a negative + integer" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsxor/test-bitsxor-0060.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsxor/test-bitsxor-0060.yaml new file mode 100644 index 000000000000..9a3f026b7db2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsxor/test-bitsxor-0060.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + bits.xor(42, 3, __local1__) + __local0__ = __local1__ + } + note: bitsxor/basic bitwise-xor + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 41 diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsxor/test-bitsxor-0061.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsxor/test-bitsxor-0061.yaml new file mode 100644 index 000000000000..c49c615a6fc0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsxor/test-bitsxor-0061.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + bits.xor(42, 42, __local1__) + __local0__ = __local1__ + } + note: bitsxor/xor same is 0 + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/bitsxor/test-bitsxor-0062.yaml b/third_party/opa/v1/test/cases/testdata/v0/bitsxor/test-bitsxor-0062.yaml new file mode 100644 index 000000000000..e5222a6603f6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/bitsxor/test-bitsxor-0062.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + bits.xor(7.2, 42, __local1__) + __local0__ = __local1__ + } + note: bitsxor/lhs (float) error + query: data.generated.p = x + want_error: "bits.xor: operand 1 must be integer number but got floating-point number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0077.yaml b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0077.yaml new file mode 100644 index 000000000000..4a9bb519e2cb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0077.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + cast_null(null, x) + } + note: casts/null valid + query: data.generated.p = x + want_result: + - x: null diff --git a/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0078.yaml b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0078.yaml new file mode 100644 index 000000000000..8494332fb41a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0078.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + cast_null({}, x) + } + note: casts/null invalid + query: data.generated.p = x + want_error: "" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0079.yaml b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0079.yaml new file mode 100644 index 000000000000..4e9c798d48a7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0079.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + op1: + - 1 + - 2 + - 3 + modules: + - | + package generated + + p = x { + cast_string(data.op1, x) + } + note: casts/string invalid + query: data.generated.p = x + strict_error: true + want_error: "" + want_error_code: eval_type_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0080.yaml b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0080.yaml new file mode 100644 index 000000000000..ace76e0a5fcd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0080.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + cast_boolean(false, x) + } + note: casts/boolean valid + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0081.yaml b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0081.yaml new file mode 100644 index 000000000000..5bb7f6bf58a7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0081.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + cast_boolean(1, x) + } + note: casts/boolean invalid + query: data.generated.p = x + want_error: "" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0082.yaml b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0082.yaml new file mode 100644 index 000000000000..0f5275d1b336 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0082.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + cast_object({}, x) + } + note: casts/obj valid + query: data.generated.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0083.yaml b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0083.yaml new file mode 100644 index 000000000000..8b38455496bc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0083.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + cast_object([1, 2, 3], x) + } + note: casts/obj invalid + query: data.generated.p = x + want_error: "" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0824.yaml b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0824.yaml new file mode 100644 index 000000000000..0ab2b4f36a51 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0824.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z, i, j] { + to_number("-42.0", x) + to_number(false, y) + to_number(100.1, z) + to_number(null, i) + to_number(true, j) + } + note: casts/to_number + query: data.generated.p = x + want_result: + - x: + - -42 + - 0 + - 100.1 + - 0 + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0825.yaml b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0825.yaml new file mode 100644 index 000000000000..81f4c54777b2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0825.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + __local0__ = data.a[2] + to_number("3", __local0__) + } + note: casts/to_number ref dest + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0826.yaml b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0826.yaml new file mode 100644 index 000000000000..2fb77999a280 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0826.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + __local0__ = data.a[2] + not to_number("-1", __local0__) + } + note: casts/to_number ref dest + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0827.yaml b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0827.yaml new file mode 100644 index 000000000000..70dca6c1368a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/casts/test-casts-0827.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p { + to_number("broken", x) + } + note: "casts/to_number: bad input" + query: data.generated.p = x + want_error: invalid syntax + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0608.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0608.yaml new file mode 100644 index 000000000000..9b57fa80aa62 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0608.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + 1 = 1 + data.a[i] = x + x = 2 + } + note: comparisonexpr/equals + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0609.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0609.yaml new file mode 100644 index 000000000000..03a4e2aabaf2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0609.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + 0 != 1 + data.a[i] = x + x != 2 + } + note: comparisonexpr/noteq + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0610.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0610.yaml new file mode 100644 index 000000000000..1ba8d8c067bf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0610.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + 1 > 0 + data.a[i] = x + x > 2 + } + note: comparisonexpr/gt + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0611.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0611.yaml new file mode 100644 index 000000000000..34dfc8504a6c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0611.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + 1 >= 1 + data.a[i] = x + x >= 4 + } + note: comparisonexpr/gteq + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0612.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0612.yaml new file mode 100644 index 000000000000..69c3793cd8f0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0612.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + -1 < 0 + data.a[i] = x + x < 5 + } + note: comparisonexpr/lt + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0613.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0613.yaml new file mode 100644 index 000000000000..b45c7a6cb803 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0613.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + -1 <= 0 + data.a[i] = x + x <= 1 + } + note: comparisonexpr/lteq + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0614.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0614.yaml new file mode 100644 index 000000000000..78252483de86 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0614.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + 0 = 1 + } + note: "comparisonexpr/undefined: equals" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0615.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0615.yaml new file mode 100644 index 000000000000..4a682af3e907 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0615.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + 0 != 0 + } + note: "comparisonexpr/undefined: noteq" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0616.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0616.yaml new file mode 100644 index 000000000000..f06dab3354cc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0616.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + 1 > 2 + } + note: "comparisonexpr/undefined: gt" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0617.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0617.yaml new file mode 100644 index 000000000000..34ffbc139bfb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0617.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + 1 >= 2 + } + note: "comparisonexpr/undefined: gteq" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0618.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0618.yaml new file mode 100644 index 000000000000..0fc6bbeeb8b8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0618.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + 1 < -1 + } + note: "comparisonexpr/undefined: lt" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0619.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0619.yaml new file mode 100644 index 000000000000..9480f3c2a1a4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0619.yaml @@ -0,0 +1,12 @@ +--- +cases: + - modules: + - | + package generated + + p { + 1 <= -1 + } + note: "comparisonexpr/undefined: lteq" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0620.yaml b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0620.yaml new file mode 100644 index 000000000000..0809feb2b97e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comparisonexpr/test-comparisonexpr-0620.yaml @@ -0,0 +1,48 @@ +--- +cases: + # Leaving out floats as map keys until this is resolved: + # https://github.com/open-policy-agent/opa/issues/4797 + - modules: + - | + package comparison + + p { + 1 == 1.0 + } + note: "comparisonexpr/numbers: int and float comparison" + query: data.comparison.p = x + want_result: + - x: true + - modules: + - | + package comparison + + p { + [1] == [1.0] + } + note: "comparisonexpr/numbers: int and float array comparison" + query: data.comparison.p = x + want_result: + - x: true + - modules: + - | + package comparison + + p { + {1: 1} == {1: 1.0} + } + note: "comparisonexpr/numbers: int and float object comparison" + query: data.comparison.p = x + want_result: + - x: true + - modules: + - | + package comparison + + p { + {"x": [1, 2, {"b": 3.0}]} == {"x": [1, 2, {"b": 3}]} + } + note: "comparisonexpr/numbers: int and float nested object comparison" + query: data.comparison.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0495.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0495.yaml new file mode 100644 index 000000000000..e5f0c9fc909d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0495.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = null { + false + } + note: completedoc/undefined + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0496.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0496.yaml new file mode 100644 index 000000000000..3a0d060dede8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0496.yaml @@ -0,0 +1,12 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = null + note: completedoc/null + query: data.generated.p = x + want_result: + - x: null diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0497.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0497.yaml new file mode 100644 index 000000000000..d831729fa486 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0497.yaml @@ -0,0 +1,12 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = true + note: "completedoc/bool: true" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0498.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0498.yaml new file mode 100644 index 000000000000..436fcf13c0f7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0498.yaml @@ -0,0 +1,12 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = false + note: "completedoc/bool: false" + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0499.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0499.yaml new file mode 100644 index 000000000000..4f68567974ae --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0499.yaml @@ -0,0 +1,12 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = 3 + note: "completedoc/number: 3" + query: data.generated.p = x + want_result: + - x: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0500.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0500.yaml new file mode 100644 index 000000000000..7109c5a0b64c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0500.yaml @@ -0,0 +1,12 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = 3.0 + note: "completedoc/number: 3.0" + query: data.generated.p = x + want_result: + - x: 3.0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0501.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0501.yaml new file mode 100644 index 000000000000..332cdd2d5fcf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0501.yaml @@ -0,0 +1,12 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = 66.66667 + note: "completedoc/number: 66.66667" + query: data.generated.p = x + want_result: + - x: 66.66667 diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0502.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0502.yaml new file mode 100644 index 000000000000..2687d5d4af69 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0502.yaml @@ -0,0 +1,12 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = "hello" + note: 'completedoc/string: "hello"' + query: data.generated.p = x + want_result: + - x: hello diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0503.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0503.yaml new file mode 100644 index 000000000000..ca19d3dd7355 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0503.yaml @@ -0,0 +1,12 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = "" + note: 'completedoc/string: ""' + query: data.generated.p = x + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0504.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0504.yaml new file mode 100644 index 000000000000..e806fcc8d3a4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0504.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [1, 2, 3, 4] + note: "completedoc/array: [1,2,3,4]" + query: data.generated.p = x + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0505.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0505.yaml new file mode 100644 index 000000000000..cbaf83f5cc97 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0505.yaml @@ -0,0 +1,12 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [] + note: "completedoc/array: []" + query: data.generated.p = x + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0506.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0506.yaml new file mode 100644 index 000000000000..81f9aa008b4a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0506.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = {"a": [1], "b": [2], "c": [3]} + note: 'completedoc/object/nested composites: {"a": [1], "b": [2], "c": [3]}' + query: data.generated.p = x + want_result: + - x: + a: + - 1 + b: + - 2 + c: + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0507.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0507.yaml new file mode 100644 index 000000000000..d022211dbb3d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0507.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = {1: 2, {3: 4}: 5} + note: "completedoc/object/non-string key:" + query: data.generated.p = x + want_result: + - x: + "1": 2 + '{"3":4}': 5 diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0508.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0508.yaml new file mode 100644 index 000000000000..619f4260d38c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0508.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = {{1, 2}, {2, 3}} + note: "completedoc/set/nested: {{1,2},{2,3}}" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 1 + - 2 + - - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0509.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0509.yaml new file mode 100644 index 000000000000..ed5e4da50284 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0509.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = {"a": [x, y]} { + x = 1 + y = 2 + } + note: completedoc/vars + query: data.generated.p = x + want_result: + - x: + a: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0510.yaml b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0510.yaml new file mode 100644 index 000000000000..92b0a6764aa5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/completedoc/test-completedoc-0510.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = {"a": [x, y]} { + xs = [1, 2] + ys = [1, 2] + x = xs[_] + y = ys[_] + } + note: completedoc/vars conflict + query: data.generated.p = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositebasedereference/test-compositebasedereference-1073.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositebasedereference/test-compositebasedereference-1073.yaml new file mode 100644 index 000000000000..9c74d9d12de5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositebasedereference/test-compositebasedereference-1073.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + not data.a[[0]] + } + note: compositebasedereference/array + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositebasedereference/test-compositebasedereference-1074.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositebasedereference/test-compositebasedereference-1074.yaml new file mode 100644 index 000000000000..4d961638e881 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositebasedereference/test-compositebasedereference-1074.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + not data.a[{"b": "c"}] + } + note: compositebasedereference/object + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositebasedereference/test-compositebasedereference-1075.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositebasedereference/test-compositebasedereference-1075.yaml new file mode 100644 index 000000000000..7ece55ec69a9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositebasedereference/test-compositebasedereference-1075.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + not data.a[["b"]] + } + note: compositebasedereference/set + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0743.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0743.yaml new file mode 100644 index 000000000000..9472ed2dcf39 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0743.yaml @@ -0,0 +1,39 @@ +--- +cases: + - data: {} + modules: + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + - | + package test + + p = __local0__ { + true + __local0__ = data.fixture.r[[1, 2]] + } + note: compositereferences/array + query: data.test.p = x + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0744.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0744.yaml new file mode 100644 index 000000000000..6af1c884ded6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0744.yaml @@ -0,0 +1,38 @@ +--- +cases: + - data: {} + modules: + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + - | + package test + + p = __local0__ { + true + __local0__ = data.fixture.r[{"foo": "bar"}] + } + note: compositereferences/object + query: data.test.p = x + want_result: + - x: + foo: bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0745.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0745.yaml new file mode 100644 index 000000000000..b186eb952009 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0745.yaml @@ -0,0 +1,39 @@ +--- +cases: + - data: {} + modules: + - | + package test + + p = __local0__ { + true + __local0__ = data.fixture.r[{1, 2}] + } + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + note: compositereferences/set + query: data.test.p = x + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0746.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0746.yaml new file mode 100644 index 000000000000..73a83528608a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0746.yaml @@ -0,0 +1,40 @@ +--- +cases: + - data: {} + modules: + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + - | + package test + + p = __local0__ { + true + __local0__ = [x | data.fixture.r[[1, x]]] + } + note: compositereferences/unify array + query: data.test.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0747.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0747.yaml new file mode 100644 index 000000000000..a37f9a3786d4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0747.yaml @@ -0,0 +1,38 @@ +--- +cases: + - data: {} + modules: + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + - | + package test + + p = __local0__ { + true + __local0__ = [x | data.fixture.r[{"foo": x}]] + } + note: compositereferences/unify object + query: data.test.p = x + want_result: + - x: + - bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0748.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0748.yaml new file mode 100644 index 000000000000..eb287a47114b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0748.yaml @@ -0,0 +1,40 @@ +--- +cases: + - data: {} + modules: + - | + package test + + p = __local0__ { + true + __local0__ = [x | data.fixture.p1[[x, 2]]] + } + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + note: compositereferences/unify partial ground array + query: data.test.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0749.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0749.yaml new file mode 100644 index 000000000000..423c21553156 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0749.yaml @@ -0,0 +1,47 @@ +--- +cases: + - data: {} + modules: + - | + package test + + p = __local0__ { + true + __local0__ = [[x, y] | data.fixture.s[[x, y]]] + } + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + note: compositereferences/complete doc unify + query: data.test.p = x + sort_bindings: true + want_result: + - x: + - - 1 + - 2 + - - 1 + - 3 + - - 2 + - 7 + - - - 1 + - 1 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0750.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0750.yaml new file mode 100644 index 000000000000..e66e7a16ce47 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0750.yaml @@ -0,0 +1,47 @@ +--- +cases: + - data: {} + modules: + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + - | + package test + + p = __local0__ { + true + __local0__ = [[x, y] | data.fixture.r[[x, y]]] + } + note: compositereferences/partial doc unify + query: data.test.p = x + sort_bindings: true + want_result: + - x: + - - 1 + - 2 + - - 1 + - 3 + - - 2 + - 7 + - - - 1 + - 1 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0751.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0751.yaml new file mode 100644 index 000000000000..6281f11be862 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0751.yaml @@ -0,0 +1,36 @@ +--- +cases: + - data: {} + modules: + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + - | + package test + + p { + data.fixture.empty[set()] + } + note: compositereferences/empty set + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0752.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0752.yaml new file mode 100644 index 000000000000..2124b6b99946 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0752.yaml @@ -0,0 +1,40 @@ +--- +cases: + - data: {} + modules: + - | + package test + + p = __local0__ { + true + __local1__ = data.fixture.foo.bar + __local0__ = data.fixture.r[[__local1__, 3]] + } + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + note: compositereferences/ref + query: data.test.p = x + want_result: + - x: + - 1 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0753.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0753.yaml new file mode 100644 index 000000000000..0a3dce7b2e26 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0753.yaml @@ -0,0 +1,41 @@ +--- +cases: + - data: {} + modules: + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + - | + package test + + p = __local0__ { + true + __local1__ = data.fixture.o.foo + __local2__ = data.fixture.foo[__local1__] + __local0__ = data.fixture.r[[__local2__, 3]] + } + note: compositereferences/nested ref + query: data.test.p = x + want_result: + - x: + - 1 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0754.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0754.yaml new file mode 100644 index 000000000000..8f3d3d4b7ac6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0754.yaml @@ -0,0 +1,41 @@ +--- +cases: + - data: {} + modules: + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + - | + package test + + p = __local0__ { + true + __local1__ = [x | y = [1, 1]; x = y[_]] + __local0__ = data.fixture.s[[__local1__, 4]] + } + note: compositereferences/comprehension + query: data.test.p = x + want_result: + - x: + - - 1 + - 1 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0755.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0755.yaml new file mode 100644 index 000000000000..16055cf9bef1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0755.yaml @@ -0,0 +1,36 @@ +--- +cases: + - data: {} + modules: + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + - | + package test + + p = __local0__ { + true + __local0__ = data.fixture.r[[1, 4]] + } + note: compositereferences/missing array + query: data.test.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0756.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0756.yaml new file mode 100644 index 000000000000..c9013813eef7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0756.yaml @@ -0,0 +1,36 @@ +--- +cases: + - data: {} + modules: + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + - | + package test + + p = __local0__ { + true + __local0__ = data.fixture.r[{"foo": "baz"}] + } + note: compositereferences/missing object value + query: data.test.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0757.yaml b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0757.yaml new file mode 100644 index 000000000000..d4ec3ea1994f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/compositereferences/test-compositereferences-0757.yaml @@ -0,0 +1,36 @@ +--- +cases: + - data: {} + modules: + - | + package fixture + + empty = {set()} + + s = {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r[x] { + data.fixture.s[x] + } + + a = [1, 2] + + o = {"foo": "bar"} + + foo = {"bar": 1} + + p1[[1, 2]] + + p1[[1, 3]] + + p1[[2, 2]] + - | + package test + + p = __local0__ { + true + __local0__ = data.fixture.r[{1, 3}] + } + note: compositereferences/missing set + query: data.test.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0781.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0781.yaml new file mode 100644 index 000000000000..600623841e3a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0781.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[i] { + xs = [x | x = data.a[_]] + __local0__ = xs[i] + __local0__ > 1 + } + note: comprehensions/array simple + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0782.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0782.yaml new file mode 100644 index 000000000000..27f217bcb39e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0782.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[i] { + ys = [y | x = [z | z = data.a[_]]; y = x[_]] + __local0__ = ys[i] + __local0__ > 1 + } + note: comprehensions/array nested + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0783.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0783.yaml new file mode 100644 index 000000000000..d020e4f4b227 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0783.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[i] { + __local0__ = [x | x = data.a[_]] + xs = [__local0__] + __local1__ = xs[0][i] + __local1__ > 1 + } + note: comprehensions/array embedded array + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0784.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0784.yaml new file mode 100644 index 000000000000..3b115acf483e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0784.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[i] { + __local0__ = [x | x = data.a[_]] + xs = {"a": __local0__} + __local1__ = xs.a[i] + __local1__ > 1 + } + note: comprehensions/array embedded object + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0785.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0785.yaml new file mode 100644 index 000000000000..c4e6bdb3859e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0785.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = xs { + __local0__ = [x | x = data.a[_]] + xs = {__local0__} + } + note: comprehensions/array embedded set + query: data.generated.p = x + want_result: + - x: + - - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0786.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0786.yaml new file mode 100644 index 000000000000..0176bbf05dfa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0786.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + y = 1 + x = [y | y = 1] + } + note: comprehensions/array closure + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0787.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0787.yaml new file mode 100644 index 000000000000..5d27ec64eaa9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0787.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + data.generated.q.a[2][i] = x + } + + q[k] = v { + k = "a" + v = [y | i = [z | z = data.a[_]]; i[_] = _; i = y] + } + note: comprehensions/array dereference embedded + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0788.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0788.yaml new file mode 100644 index 000000000000..8b1635867036 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0788.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[i] { + xs = {s: x | x = data.a[_]; format_int(x, 10, s)} + y = xs[i] + y > 1 + } + note: comprehensions/object simple + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - "2" + - "3" + - "4" diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0789.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0789.yaml new file mode 100644 index 000000000000..fb410962024b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0789.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + xs = {k: 1 | data.a[_] = k} + xs[x] + } + note: comprehensions/object non-string key + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0790.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0790.yaml new file mode 100644 index 000000000000..2bfbe7e24136 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0790.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p = r { + r = {x: y | z = {i: q | i = data.b[q]}; x = z[y]} + } + note: comprehensions/object nested + query: data.generated.p = x + want_result: + - x: + v1: hello + v2: goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0791.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0791.yaml new file mode 100644 index 000000000000..473db4274665 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0791.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[i] { + __local0__ = {s: x | x = data.a[_]; format_int(x, 10, s)} + xs = [__local0__] + __local1__ = xs[0][i] + __local1__ > 1 + } + note: comprehensions/object embedded array + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - "2" + - "3" + - "4" diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0792.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0792.yaml new file mode 100644 index 000000000000..d0977cc4b080 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0792.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[i] { + __local0__ = {s: x | x = data.a[_]; format_int(x, 10, s)} + xs = {"a": __local0__} + __local1__ = xs.a[i] + __local1__ > 1 + } + note: comprehensions/object embedded object + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - "2" + - "3" + - "4" diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0793.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0793.yaml new file mode 100644 index 000000000000..a31bcb15152a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0793.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = xs { + __local0__ = {s: x | x = data.a[_]; format_int(x, 10, s)} + xs = {__local0__} + } + note: comprehensions/object embedded set + query: data.generated.p = x + want_result: + - x: + - "1": 1 + "2": 2 + "3": 3 + "4": 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0794.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0794.yaml new file mode 100644 index 000000000000..f1c1699678d4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0794.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + y = 1 + x = {"foo": y | y = 1} + } + note: comprehensions/object closure + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - foo: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0795.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0795.yaml new file mode 100644 index 000000000000..5fbfdaa20138 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0795.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + arr = [4] + + p[x] { + data.generated.q.a = x + } + + q[k] = v { + k = "a" + v = {"bar": y | i = {"foo": z | z = data.generated.arr[_]}; i[_] = _; i = y} + } + note: comprehensions/object dereference embedded + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - bar: + foo: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0796.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0796.yaml new file mode 100644 index 000000000000..ba32f1bf4da1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0796.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + data.generated.q.a = x + } + + q[k] = v { + k = "a" + v = {"bar": y | i = {"foo": z | z = data.a[_]}; i[_] = _; i = y} + } + note: comprehensions/object conflict + query: data.generated.p = x + want_error: object keys must be unique + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0797.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0797.yaml new file mode 100644 index 000000000000..04dcb9ff90e1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0797.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = y { + y = {x | x = data.a[_]; x > 1} + } + note: comprehensions/set simple + query: data.generated.p = x + want_result: + - x: + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0798.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0798.yaml new file mode 100644 index 000000000000..1acabd6e02a5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0798.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[i] { + ys = {y | x = {z | z = data.a[_]}; y = x[_]} + __local0__ = ys[i] + __local0__ > 1 + } + note: comprehensions/set nested + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0799.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0799.yaml new file mode 100644 index 000000000000..c67b455ae133 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0799.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[i] { + __local0__ = {x | x = data.a[_]} + xs = [__local0__] + __local1__ = xs[0][i] + __local1__ > 1 + } + note: comprehensions/set embedded array + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0800.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0800.yaml new file mode 100644 index 000000000000..bd0e76e78941 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0800.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[i] { + __local0__ = {x | x = data.a[_]} + xs = {"a": __local0__} + __local1__ = xs.a[i] + __local1__ > 1 + } + note: comprehensions/set embedded object + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0801.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0801.yaml new file mode 100644 index 000000000000..09051dba7ec2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0801.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = xs { + __local0__ = {x | x = data.a[_]} + xs = {__local0__} + } + note: comprehensions/set embedded set + query: data.generated.p = x + want_result: + - x: + - - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0802.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0802.yaml new file mode 100644 index 000000000000..54c371af7fc4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0802.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + y = 1 + x = {y | y = 1} + } + note: comprehensions/set closure + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0803.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0803.yaml new file mode 100644 index 000000000000..587176bfc908 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-0803.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + data.generated.q.a = x + } + + q[k] = v { + k = "a" + v = {y | i = {z | z = data.a[_]}; i[_] = _; i = y} + } + note: comprehensions/set dereference embedded + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-and-vars.yaml b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-and-vars.yaml new file mode 100644 index 000000000000..03b74b319bae --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/comprehensions/test-comprehensions-and-vars.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package test + xs := {"a", "b", "c"} + p = x { + y := { x | xs[x] } + z := { x | xs[x] } + count(y) == count(z) + x := count(y) + } + note: comprehensions/var bindings have no effect outside + query: data.test.p = x + want_result: + - x: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/containskeyword/test-contains-future-keyword.yaml b/third_party/opa/v1/test/cases/testdata/v0/containskeyword/test-contains-future-keyword.yaml new file mode 100644 index 000000000000..22622779632a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/containskeyword/test-contains-future-keyword.yaml @@ -0,0 +1,68 @@ +--- +cases: + - data: + modules: + - | + package test + p { + contains("fireplace", "repl") + } + note: containskeyword/base case + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.contains + + p { + contains("fireplace", "repl") + } + note: containskeyword/with unused kw import + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.contains + + p contains "x" { + contains("fireplace", "repl") + } + note: containskeyword/with kw and builtin used + query: data.test.p = x + want_result: + - x: [x] + - data: + modules: + - | + package test + import future.keywords.contains + + p contains "x" + note: containskeyword/empty body + query: data.test.p = x + want_result: + - x: [x] + - data: + modules: + - | + package test + import future.keywords.contains + + p contains msg { + msg := "nono" + } + p contains msg { + msg := "nonono" + } + note: containskeyword/ordinary deny rule + query: data.test.p = x + want_result: + - x: + - nono + - nonono diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptohmacequal/test-cryptohmacequal.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptohmacequal/test-cryptohmacequal.yaml new file mode 100644 index 000000000000..48790ec984f4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptohmacequal/test-cryptohmacequal.yaml @@ -0,0 +1,84 @@ +--- +cases: + - note: cryptohmacequal/crypto.hmac.equal_md5 + query: data.test.p = x + modules: + - | + package test + + p[res] { + res := crypto.hmac.equal(input.mac1, input.mac2) + } + input: + { + "mac1": "31b6db9e5eb4addb42f1a6ca07367adc", + "mac2": "31b6db9e5eb4addb42f1a6ca07367adc", + } + want_result: + - x: + - true + - note: cryptohmacequal/crypto.hmac.equal_sha1 + query: data.test.p = x + modules: + - | + package test + + p[res] { + res := crypto.hmac.equal(input.mac1, input.mac2) + } + input: + { + "mac1": "85d155c55ed286a300bd1cf124de08d87e914f3a", + "mac2": "85d155c55ed286a300bd1cf124de08d87e914f3a", + } + want_result: + - x: + - true + - note: cryptohmacequal/crypto.hmac.equal_sha256 + query: data.test.p = x + modules: + - | + package test + + p[res] { + res := crypto.hmac.equal(input.mac1, input.mac2) + } + input: + { + "mac1": "147933218aaabc0b8b10a2b3a5c34684c8d94341bcf10a4736dc7270f7741851", + "mac2": "147933218aaabc0b8b10a2b3a5c34684c8d94341bcf10a4736dc7270f7741851", + } + want_result: + - x: + - true + - note: cryptohmacequal/crypto.hmac.equal_sha512 + query: data.test.p = x + modules: + - | + package test + + p[res] { + res := crypto.hmac.equal(input.mac1, input.mac2) + } + input: + { + "mac1": "24257d7210582a65c731ec55159c8184cc24c02489453e58587f71f44c23a2d61b4b72154a89d17b2d49448a8452ea066f4fc56a2bcead45c088572ffccdb3d8", + "mac2": "24257d7210582a65c731ec55159c8184cc24c02489453e58587f71f44c23a2d61b4b72154a89d17b2d49448a8452ea066f4fc56a2bcead45c088572ffccdb3d8", + } + want_result: + - x: + - true + - note: cryptohmacequal/crypto.hmac.equal_false + query: data.test.p = x + modules: + - | + package test + + p[res] { + res := crypto.hmac.equal(input.mac1, input.mac2) + } + input: + { "mac1": "31b6db9e5eb4addb42f1a6ca07367adc", "mac2": "31b6db9e5eb4addb" } + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptohmacmd5/test-cryptohmacmd5.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptohmacmd5/test-cryptohmacmd5.yaml new file mode 100644 index 000000000000..f26467d040aa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptohmacmd5/test-cryptohmacmd5.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: cryptohmacmd5/crypto.hmac.md5 + query: data.test.p = x + modules: + - | + package test + + p[mac] { + mac := crypto.hmac.md5(input.message, input.key) + } + input: { "message": "foo", "key": "bar" } + want_result: + - x: + - 31b6db9e5eb4addb42f1a6ca07367adc + - note: cryptohmacmd5/crypto.hmac.md5_unicode + query: data.test.p = x + modules: + - | + package test + + p[mac] { + mac := crypto.hmac.md5(input.message, input.key) + } + input: { "message": "åäöçß🥲♙Ω", "key": "秘密の" } + want_result: + - x: + - 20a8743c2157ac60b7e8b79c83651b8d + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptohmacsha1/test-cryptohmacsha1.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptohmacsha1/test-cryptohmacsha1.yaml new file mode 100644 index 000000000000..e30417e3651a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptohmacsha1/test-cryptohmacsha1.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: cryptohmacsha1/crypto.hmac.sha1 + query: data.test.p = x + modules: + - | + package test + + p[mac] { + mac := crypto.hmac.sha1(input.message, input.key) + } + input: { "message": "foo", "key": "bar" } + want_result: + - x: + - 85d155c55ed286a300bd1cf124de08d87e914f3a + - note: cryptohmacsha1/crypto.hmac.sha1_unicode + query: data.test.p = x + modules: + - | + package test + + p[mac] { + mac := crypto.hmac.sha1(input.message, input.key) + } + input: { "message": "åäöçß🥲♙Ω", "key": "秘密の" } + want_result: + - x: + - 81759c39013935fcf0de833d44c8018d7c1455dd + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptohmacsha256/test-cryptohmacsha256.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptohmacsha256/test-cryptohmacsha256.yaml new file mode 100644 index 000000000000..bf5b242838a3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptohmacsha256/test-cryptohmacsha256.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: cryptohmacsha256/crypto.hmac.sha256 + query: data.test.p = x + modules: + - | + package test + + p[mac] { + mac := crypto.hmac.sha256(input.message, input.key) + } + input: { "message": "foo", "key": "bar" } + want_result: + - x: + - 147933218aaabc0b8b10a2b3a5c34684c8d94341bcf10a4736dc7270f7741851 + - note: cryptohmacsha256/crypto.hmac.sha256_unicode + query: data.test.p = x + modules: + - | + package test + + p[mac] { + mac := crypto.hmac.sha256(input.message, input.key) + } + input: { "message": "åäöçß🥲♙Ω", "key": "秘密の" } + want_result: + - x: + - eb90daeb76d4b2571fbdaf94bbb240809faa8fed93ec0c260dd38c3fdf8d963a + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptohmacsha512/test-cryptohmacsha512.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptohmacsha512/test-cryptohmacsha512.yaml new file mode 100644 index 000000000000..22788ff58b92 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptohmacsha512/test-cryptohmacsha512.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: cryptohmacsha512/crypto.hmac.sha512 + query: data.test.p = x + modules: + - | + package test + + p[mac] { + mac := crypto.hmac.sha512(input.message, input.key) + } + input: { "message": "foo", "key": "bar" } + want_result: + - x: + - 24257d7210582a65c731ec55159c8184cc24c02489453e58587f71f44c23a2d61b4b72154a89d17b2d49448a8452ea066f4fc56a2bcead45c088572ffccdb3d8 + - note: cryptohmacsha512/crypto.hmac.sha512_unicode + query: data.test.p = x + modules: + - | + package test + + p[mac] { + mac := crypto.hmac.sha512(input.message, input.key) + } + input: { "message": "åäöçß🥲♙Ω", "key": "秘密の" } + want_result: + - x: + - 192f5afded233d6e21427aa26ed267ac118cfa2971013d91cbed530c0b208d78138b83dfe1d6cc3553d7bd518f22a481402c723028e1279d1ffbe8f11ea6b125 + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptomd5/test-cryptomd5-0130.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptomd5/test-cryptomd5-0130.yaml new file mode 100644 index 000000000000..b78ff0dbbabb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptomd5/test-cryptomd5-0130.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p[__local0__] { + crypto.md5("lorem ipsum", __local1__) + __local0__ = __local1__ + } + note: cryptomd5/crypto.md5 with string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 80a751fde577028640c419000e33eba6 diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptoparsersaprivatekeys/test-cryptoparsersaprivatekey-1.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptoparsersaprivatekeys/test-cryptoparsersaprivatekey-1.yaml new file mode 100644 index 000000000000..be6570115c0a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptoparsersaprivatekeys/test-cryptoparsersaprivatekey-1.yaml @@ -0,0 +1,36 @@ +--- +cases: + - data: + modules: + - | + package testing + + pem := "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA9D/bK4171aiTNUkrUCHKGMLSQooV+o3wdz2889h9iv0HhhBJ\nCAGU54K3duB8ofHpmYL50QodcR4RLw1vSkaI+FPdPDMyKxKj/YcmofJjz4kW+Iqw\nFbBcbMnKnEVzye+CyW9YYOTu0xWtcgen80zGp2opG0GZX86hBjjXJnjOdrJTk6x2\nNAiJIbjsQevysmj+2MyqVm8widxw0x+rGhTaCD+ZXWitN0a0WO1aaA8c/7i99I9z\nhe2peKvXzEtMaqYO9ptHcYmq2z0QWvZuJVMv5Yn0mScLWyh91R099IOtn6sNaMMs\nOyTpi7E/2IlVgI2uKGPEopKkMFV8Fl2YaAbo7wIDAQABAoIBAAyMZ08ygqU0dvOq\n4a3JPp/NCo5el8h6mFsX8eg5PCHy4/sQRSBDLIpEXfaei+iqDA1V/E2wDlksaUeY\nkhony4uui1Q3cSFjYMd6tRJm6JfV/DcisO88U1NHfsBOlSdPxdFhhhHcUSTJHVMZ\nb5iBXkdlnd0HnsCcVguCyhLw6/KPFyiA+NYRz68flxze7admyVp5C6i/HbMPq8Pr\nMilBUvOFtxuaGeJBAiavuzUe9I70dRwpe424tMvisSA8h7Xbm8BeN/PJHDV/2JrI\nURgQ563yQ5So/Qg8AgxXRkpgWM9zAh7r31PBO86vq/B4ZbON/TtWdcZVsAcVB4Pk\ntqc8JNkCgYEA+g8V+y92SETdcwUkbd5O9Fg5CkfdsALsBXVH6FunrCUV5HS9l5o6\nMMBbJ/08odW/bP5BmOa4A/Hbk9uG/UfQn2KQ3HCgPlxUEwQO07R1/FcQOe4xmyG6\nJpDgQ30viE1RtlCkceQWUeitCIqZsYu0i8sLZLWJH+V/07OB4G17ELMCgYEA+g1v\nhrlAFNhZvrIX/zcP3xF2pZ+AqkFXdL/tWQZkWAVToONn/LlXTH71C/TO2x+OaQRm\nqX1bA9Zhyjf1gYQN9RenjUswvggk0aY2Tk28wUqowMGSsjQHmZ20EphHNMWNJpdS\nfKFfrQIFKCnLlpQVNz+j3bLWZUnq+jPaYnJP7NUCgYEA48qcVo7c7Ga3aNEVZ3St\nbg90HrZq760pvqshDz13V+0MrWnfUFxxh/mi0KHy+uYRlMNllFkQ5p8LTP0dUlt6\nY8dReU6r20MWX6BBtX9eP7o8ENm4nL4zqnAtq609gKgWuMNrmkiSQJl6Dx7bdY5z\nsSkNPvfUa5cQRBTxSjXRdtsCgYBHrzpdwRXh4/Q2ew/uFnbyWCtPZ96W8IyF58+/\nSdnSchR7dzYEeY3RXEQb3V6/6tgEu0JDLLC+9OKr+kbjjlwB+3oJQ5kBoYwMnj3L\nTPXj4+dk+xl3BPt4yoEpI4amVkwU2CTJnemzy3R3AyReUq2SXSg5El/sQbifaeYd\neu/20QKBgH/5IZHGBKiRAe1ww2FzOpDtL8VXXTe3EAXKutfajrHTqPz9+lXknX/D\nUMosh264nYXYS29WqxhJVutbE9u8e0VpuY1qIN9/3R0WKfTLTMUFlZtbqTepvsy1\nW2UbK732I4Nfp0/mtUvOSdMZO8dxbSdEeMnw/Ec8QgxK9a1rRu9+\n-----END RSA PRIVATE KEY-----" + + p { + count(crypto.parse_private_keys(pem)) == 1 + } + note: cryptoparseprivatekey/valid + query: data.testing.p = x + want_result: + - x: true + - data: + modules: + - | + package testing + pem := "nope" + p := crypto.parse_private_keys(pem) + note: cryptoparseprivatekey/invalid + query: data.testing.p = x + want_result: + - x: [] + - data: + modules: + - | + package testing + pem := "" + p := crypto.parse_private_keys(pem) + note: cryptoparseprivatekey/invalid + query: data.testing.p = x + want_result: + - x: null diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptosha1/test-cryptosha1-0131.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptosha1/test-cryptosha1-0131.yaml new file mode 100644 index 000000000000..545720aa7845 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptosha1/test-cryptosha1-0131.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + crypto.sha1("lorem ipsum", __local1__) + __local0__ = __local1__ + } + note: cryptosha1/crypto.sha1 with string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - bfb7759a67daeb65410490b4d98bb9da7d1ea2ce diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptosha256/test-cryptosha256-0132.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptosha256/test-cryptosha256-0132.yaml new file mode 100644 index 000000000000..bb16510c24cc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptosha256/test-cryptosha256-0132.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + crypto.sha256("lorem ipsum", __local1__) + __local0__ = __local1__ + } + note: cryptosha256/crypto.sha256 with string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 5e2bf57d3f40c4b6df69daf1936cb766f832374b4fc0259a7cbff06e2f70f269 diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parseandverifycertificates/test-cryptox509parseandverifycertificates.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parseandverifycertificates/test-cryptox509parseandverifycertificates.yaml new file mode 100644 index 000000000000..c4eb8f45ff8a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parseandverifycertificates/test-cryptox509parseandverifycertificates.yaml @@ -0,0 +1,138 @@ +--- +cases: + - data: + modules: + - | + package test + + import future.keywords + + certs := `-----BEGIN CERTIFICATE----- + MIIBoDCCAUagAwIBAgIRAJXcMYZALXooNq/VV/grXhMwCgYIKoZIzj0EAwIwLjER + MA8GA1UEChMIT1BBIFRlc3QxGTAXBgNVBAMTEE9QQSBUZXN0IFJvb3QgQ0EwHhcN + MjEwNzAxMTc0MTUzWhcNMzEwNjI5MTc0MTUzWjAuMREwDwYDVQQKEwhPUEEgVGVz + dDEZMBcGA1UEAxMQT1BBIFRlc3QgUm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49 + AwEHA0IABFqhdZA5LjsJgzsBvhgzfayZFOk+C7PmGCi7xz6zOC3xWORJZSNOyZeJ + YzSKFmoMZkcFMfslTW1jp9fwe1xl3HWjRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNV + HRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTch60qxQvLl+AfDfcaXmjvT8GvpzAK + BggqhkjOPQQDAgNIADBFAiBqraIP0l2U0oNuH0+rf36hDks94wSB5EGlGH3lYNMR + ugIhANkbukX5hOP8pJDRWP/pYuv6MBnRY4BS8gpp9Vu31qOb + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIByDCCAW6gAwIBAgIQC0k4DPGrh9me73EJX5zntTAKBggqhkjOPQQDAjAuMREw + DwYDVQQKEwhPUEEgVGVzdDEZMBcGA1UEAxMQT1BBIFRlc3QgUm9vdCBDQTAeFw0y + MTA3MDExNzQxNTNaFw0zMTA2MjkxNzQxNTNaMDYxETAPBgNVBAoTCE9QQSBUZXN0 + MSEwHwYDVQQDExhPUEEgVGVzdCBJbnRlcm1lZGlhdGUgQ0EwWTATBgcqhkjOPQIB + BggqhkjOPQMBBwNCAARvXQa7fy476gDI81nqLYb2SnD459WxBmU0hk2bA3ZuNtI+ + H20KXz6ISmxH3MZ2WBm6rOy7y4Gn+WMCJuxzcl5jo2YwZDAOBgNVHQ8BAf8EBAMC + AQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuslZNjJl0V8I1Gj17IID + ALy/9WEwHwYDVR0jBBgwFoAU3IetKsULy5fgHw33Gl5o70/Br6cwCgYIKoZIzj0E + AwIDSAAwRQIgUwsYApW9Tsm6AstWswaKGie0srB4FUkUbfKwWmUI2JgCIQCBTySN + MF+EiQAMKyz/N9KUuXEckC356WvKcyJaYYcV0w== + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIB8zCCAZqgAwIBAgIRAID4gPKg7DDiuOfzUYFSXLAwCgYIKoZIzj0EAwIwNjER + MA8GA1UEChMIT1BBIFRlc3QxITAfBgNVBAMTGE9QQSBUZXN0IEludGVybWVkaWF0 + ZSBDQTAeFw0yMTA3MDUxNzQ5NTBaFw0zNjA3MDExNzQ5NDdaMCUxIzAhBgNVBAMT + Gm5vdGFyZWFsc2l0ZS5vcGEubG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D + AQcDQgAE1YSXZXeaGGL+XeYyoPi/QdA39Ds4fgxSHJTMh+js393kByPm2PNtFkem + tUii3KCRJw3SEh3z0JWr/9y4+ua2L6OBmTCBljAOBgNVHQ8BAf8EBAMCB4AwHQYD + VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRL0P0g17viZHo9 + CnXe3ZQJm48LXTAfBgNVHSMEGDAWgBS6yVk2MmXRXwjUaPXsggMAvL/1YTAlBgNV + HREEHjAcghpub3RhcmVhbHNpdGUub3BhLmxvY2FsaG9zdDAKBggqhkjOPQQDAgNH + ADBEAiAtmZewL94ijN0YwUGaJM9BXCaoTQPwkzugqjCj+K912QIgKKFvbPu4asrE + nwy7dzejHmQUcZ/aUNbc4VTbiv15ESk= + -----END CERTIFICATE----- + ` + + value := crypto.x509.parse_and_verify_certificates(certs) + + result := { + "valid": value[0], + "certs": [c| + some cert in value[1] + c := { + "CN": cert.Subject.CommonName, + "DNS": cert.DNSNames, + "URI": cert.URIStrings, + } + ], + } + + note: cryptox509parseandverifycertificates/base_case + query: data.test.result = x + want_result: + - x: + certs: + - CN: notarealsite.opa.localhost + DNS: + - notarealsite.opa.localhost + URI: + - CN: OPA Test Intermediate CA + DNS: + URI: + - CN: OPA Test Root CA + DNS: + URI: + valid: true + - data: + modules: + - | + package test + + import future.keywords + + certs := `-----BEGIN CERTIFICATE----- + MIIB1TCCAXugAwIBAgIIKIoxsnMwJJ4wCgYIKoZIzj0EAwIwPTELMAkGA1UEBhMC + R0IxEDAOBgNVBAoTB0V4YW1wbGUxHDAaBgNVBAUTEzI5MjEyMDE5NTA4MDk2NjI2 + MjIwIBcNMjMxMTI5MTc1NTQ2WhgPMjEyMzExMDUxNzU1NDZaMD0xCzAJBgNVBAYT + AkdCMRAwDgYDVQQKEwdFeGFtcGxlMRwwGgYDVQQFExMyOTIxMjAxOTUwODA5NjYy + NjIyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkvI9ddM0SuP9LvBWS1y64fuK + ELCjVF5W3FSKm3azKEkDi8Eq1I1UM80MgCjC5ChNNyM4+cmVUDrCkTl3SqRxa6Nj + MGEwDgYDVR0PAQH/BAQDAgIEMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFF7H + A8n3mXXnwUP0ypMJ9JwY5wasMB8GA1UdEQQYMBaGFHNwaWZmZTovL2V4YW1wbGUu + Y29tMAoGCCqGSM49BAMCA0gAMEUCIByB2l5RIWmaU8qcRv13qigbB9BV/F2raEk+ + pRQnsUcgAiEA9OvBpPKC/FBkI5vVvR7WgK5sGPna4+a0RkXxRQgN2jM= + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIB1jCCAXygAwIBAgIIV9914tIKKkMwCgYIKoZIzj0EAwIwPTELMAkGA1UEBhMC + R0IxEDAOBgNVBAoTB0V4YW1wbGUxHDAaBgNVBAUTEzI5MjEyMDE5NTA4MDk2NjI2 + MjIwIBcNMjMxMTI5MTc1NTQ2WhgPMjEyMjExMDUxNzU1NDZaMD0xCzAJBgNVBAYT + AkdCMRAwDgYDVQQKEwdFeGFtcGxlMRwwGgYDVQQFExM2MzMxOTA5MjE4MTUzMTQ2 + OTQ3MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMoy2UqvC8zL3sPfLNvG1nX5p + 6hhEyDjFtokORB4VkKiPXFryIFn8XHG0ipz6aKSwVMoDT2T/YXP/wWpVwPJCi6Nk + MGIwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD + ATAMBgNVHRMBAf8EAjAAMCMGA1UdEQQcMBqGGHNwaWZmZTovL2V4YW1wbGUuY29t + L29wYTAKBggqhkjOPQQDAgNIADBFAiBEmdSKGj2+9J5SQPIAmwdxpVTOxqmVQv2x + Vvita/AmowIhAOyX/alNJxL4iCfKUNwlC2lYxGhuWopWgB1Q32bQhTEh + -----END CERTIFICATE----- + ` + + value := crypto.x509.parse_and_verify_certificates(certs) + + result := { + "valid": value[0], + "certs": [c| + some cert in value[1] + c := { + "CN": cert.Subject.CommonName, + "DNS": cert.DNSNames, + "URI": cert.URIStrings, + } + ], + } + + note: cryptox509parseandverifycertificates/uri_strings + query: data.test.result = x + want_result: + - x: + certs: + - CN: "" + DNS: null + URI: + - spiffe://example.com/opa + - CN: "" + DNS: null + URI: + - spiffe://example.com + valid: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0125.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0125.yaml new file mode 100644 index 000000000000..d40684116340 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0125.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + csr = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ21EQ0NBWUFDQVFBd1V6RUxNQWtHQTFVRUJoTUNWVk14RkRBU0JnTlZCQU1NQzJWNFlXMXdiR1V1WTI5dApNUW93Q0FZRFZRUUhEQUVnTVFvd0NBWURWUVFLREFFZ01Rb3dDQVlEVlFRSURBRWdNUW93Q0FZRFZRUUxEQUVnCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBMlpkaG1zaERBVTBYYnhnTk1GQWsKeEdWQnNjaHdWb2s5dXBBU2ZVWDA4VFlqMFZrV0VxNitmemdOdmRQSnd6Nm1lUDlnL01hRmhPYW91Nmh1UEhmbwpTVTlKN1FiTW56Uktsc0VJTzNodEM1QUt3OXYyZldVZGpCQS92Q1dZdXU1aUc1ZTdtUHNXWjd1cGxuVGZSekM4ClJLK0srWXJtNEQ4NHE1bHR5NEMzS2tRc0FjU0xQZk9MMXMvYjJyV21KR0FoV3NSa2doTVk2V3dza3VYWXRINTkKRzl5VURHUUhoalprcHFlZFY0OUM4c0NwMU8vWVpvU0hncDdHK0JiaFRta05CRzY3OFZHREplTnB3SG96dnRjVQpyQVNGRFJ4WnhPdTFHRzE3L1FiVW9SNVVkOTNwaUtaU0U2UHVDU2VCcy9UQmFJc3ZwUGtudVhkOXI4WGovbVd5CmtRSURBUUFCb0FBd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFBeDJkaCtkMU1CaEwwaDJYZklxaDVEYy9lYWoKU0xadGFNTWlJY1h1cC96UTl2eENXSkZlSGYzczBJdXliMEhkMlZNZ1BSYU8ydWRkY2JZdFFlKzJnWUtrTzFMWApCdHdQcXcwWHAweUF2dDUxRzJvZmVCbCtFa0ptNjk3RlNtemg4eDJJZFFBSkMzWi9ROFdMVmh3NFg2WlVicnhqCjJnTjJmaVhjS0RKbGVkcUgxY2V4WVVvbnlLSDZubG4wbzQzUUtEOFlSZG9hNVFqb3Ixb0JkY3dSTTA0VDM4ak0KV1B3d2JZTjNrVE9Ea0tiaVFVVWxVeFZuNnFnZTlNTWt0c0lOWkc0eDY1QmIwaWxTdHExRWQwN2Y5NmVnbHNKaApZVE9VRnZpZDZVSkVEcEJzcjhyZFROSW1JQkhCdkkra1BHS2FqcW83Z0VNc3hFYkNkemFHUTNZZnNYWT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0t" + + p = __local1__ { + __local3__ = data.generated.csr + crypto.x509.parse_certificate_request(__local3__, __local2__) + __local0__ = __local2__ + __local1__ = __local0__.Subject.CommonName + } + note: cryptox509parsecertificaterequest/PEM, b64 + query: data.generated.p = x + want_result: + - x: example.com diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0126.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0126.yaml new file mode 100644 index 000000000000..3010198a28db --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0126.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + csr = "-----BEGIN CERTIFICATE REQUEST-----\nMIICmDCCAYACAQAwUzELMAkGA1UEBhMCVVMxFDASBgNVBAMMC2V4YW1wbGUuY29t\nMQowCAYDVQQHDAEgMQowCAYDVQQKDAEgMQowCAYDVQQIDAEgMQowCAYDVQQLDAEg\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2ZdhmshDAU0XbxgNMFAk\nxGVBschwVok9upASfUX08TYj0VkWEq6+fzgNvdPJwz6meP9g/MaFhOaou6huPHfo\nSU9J7QbMnzRKlsEIO3htC5AKw9v2fWUdjBA/vCWYuu5iG5e7mPsWZ7uplnTfRzC8\nRK+K+Yrm4D84q5lty4C3KkQsAcSLPfOL1s/b2rWmJGAhWsRkghMY6WwskuXYtH59\nG9yUDGQHhjZkpqedV49C8sCp1O/YZoSHgp7G+BbhTmkNBG678VGDJeNpwHozvtcU\nrASFDRxZxOu1GG17/QbUoR5Ud93piKZSE6PuCSeBs/TBaIsvpPknuXd9r8Xj/mWy\nkQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAAx2dh+d1MBhL0h2XfIqh5Dc/eaj\nSLZtaMMiIcXup/zQ9vxCWJFeHf3s0Iuyb0Hd2VMgPRaO2uddcbYtQe+2gYKkO1LX\nBtwPqw0Xp0yAvt51G2ofeBl+EkJm697FSmzh8x2IdQAJC3Z/Q8WLVhw4X6ZUbrxj\n2gN2fiXcKDJledqH1cexYUonyKH6nln0o43QKD8YRdoa5Qjor1oBdcwRM04T38jM\nWPwwbYN3kTODkKbiQUUlUxVn6qge9MMktsINZG4x65Bb0ilStq1Ed07f96eglsJh\nYTOUFvid6UJEDpBsr8rdTNImIBHBvI+kPGKajqo7gEMsxEbCdzaGQ3YfsXY=\n-----END CERTIFICATE REQUEST-----" + + p = __local1__ { + __local3__ = data.generated.csr + crypto.x509.parse_certificate_request(__local3__, __local2__) + __local0__ = __local2__ + __local1__ = __local0__.Subject.CommonName + } + note: cryptox509parsecertificaterequest/PEM, string + query: data.generated.p = x + want_result: + - x: example.com diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0127.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0127.yaml new file mode 100644 index 000000000000..996fbe682c98 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0127.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + csr = "MIICmDCCAYACAQAwUzELMAkGA1UEBhMCVVMxFDASBgNVBAMMC2V4YW1wbGUuY29tMQowCAYDVQQHDAEgMQowCAYDVQQKDAEgMQowCAYDVQQIDAEgMQowCAYDVQQLDAEgMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2ZdhmshDAU0XbxgNMFAkxGVBschwVok9upASfUX08TYj0VkWEq6+fzgNvdPJwz6meP9g/MaFhOaou6huPHfoSU9J7QbMnzRKlsEIO3htC5AKw9v2fWUdjBA/vCWYuu5iG5e7mPsWZ7uplnTfRzC8RK+K+Yrm4D84q5lty4C3KkQsAcSLPfOL1s/b2rWmJGAhWsRkghMY6WwskuXYtH59G9yUDGQHhjZkpqedV49C8sCp1O/YZoSHgp7G+BbhTmkNBG678VGDJeNpwHozvtcUrASFDRxZxOu1GG17/QbUoR5Ud93piKZSE6PuCSeBs/TBaIsvpPknuXd9r8Xj/mWykQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAAx2dh+d1MBhL0h2XfIqh5Dc/eajSLZtaMMiIcXup/zQ9vxCWJFeHf3s0Iuyb0Hd2VMgPRaO2uddcbYtQe+2gYKkO1LXBtwPqw0Xp0yAvt51G2ofeBl+EkJm697FSmzh8x2IdQAJC3Z/Q8WLVhw4X6ZUbrxj2gN2fiXcKDJledqH1cexYUonyKH6nln0o43QKD8YRdoa5Qjor1oBdcwRM04T38jMWPwwbYN3kTODkKbiQUUlUxVn6qge9MMktsINZG4x65Bb0ilStq1Ed07f96eglsJhYTOUFvid6UJEDpBsr8rdTNImIBHBvI+kPGKajqo7gEMsxEbCdzaGQ3YfsXY=" + + p = __local1__ { + __local3__ = data.generated.csr + crypto.x509.parse_certificate_request(__local3__, __local2__) + __local0__ = __local2__ + __local1__ = __local0__.Subject.CommonName + } + note: cryptox509parsecertificaterequest/DER, b64 + query: data.generated.p = x + want_result: + - x: example.com diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0128.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0128.yaml new file mode 100644 index 000000000000..9133d10cdb19 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0128.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + modules: + - | + package generated + + csr = "YmFkc3RyaW5n" + + p = __local1__ { + __local3__ = data.generated.csr + crypto.x509.parse_certificate_request(__local3__, __local2__) + __local0__ = __local2__ + __local1__ = __local0__.Subject.CommonName + } + note: cryptox509parsecertificaterequest/invalid DER or PEM data, b64 + query: data.generated.p = x + want_error: "asn1: structure error" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0129.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0129.yaml new file mode 100644 index 000000000000..e3759bae0997 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0129.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + modules: + - | + package generated + + csr = "foobar" + + p = __local1__ { + __local3__ = data.generated.csr + crypto.x509.parse_certificate_request(__local3__, __local2__) + __local0__ = __local2__ + __local1__ = __local0__.Subject.CommonName + } + note: cryptox509parsecertificaterequest/invalid DER or PEM data, string + query: data.generated.p = x + want_error: illegal base64 + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0117.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0117.yaml new file mode 100644 index 000000000000..9c98d2ad155a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0117.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + certs = "MIIDujCCAqKgAwIBAgIIE31FZVaPXTUwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRlcm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwMTI5MTMyNzQzWhcNMTQwNTI5MDAwMDAwWjBpMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEYMBYGA1UEAwwPbWFpbC5nb29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfRrObuSW5T7q5CnSEqefEmtH4CCv6+5EckuriNr1CjfVvqzwfAhopXkLrq45EQm8vkmf7W96XJhC7ZM0dYi1/qOCAU8wggFLMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAaBgNVHREEEzARgg9tYWlsLmdvb2dsZS5jb20wCwYDVR0PBAQDAgeAMGgGCCsGAQUFBwEBBFwwWjArBggrBgEFBQcwAoYfaHR0cDovL3BraS5nb29nbGUuY29tL0dJQUcyLmNydDArBggrBgEFBQcwAYYfaHR0cDovL2NsaWVudHMxLmdvb2dsZS5jb20vb2NzcDAdBgNVHQ4EFgQUiJxtimAuTfwb+aUtBn5UYKreKvMwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBRK3QYWG7z2aLV29YG2u2IaulqBLzAXBgNVHSAEEDAOMAwGCisGAQQB1nkCBQEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3BraS5nb29nbGUuY29tL0dJQUcyLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAH6RYHxHdcGpMpFE3oxDoFnP+gtuBCHan2yE2GRbJ2Cw8Lw0MmuKqHlf9RSeYfd3BXeKkj1qO6TVKwCh+0HdZk283TZZyzmEOyclm3UGFYe82P/iDFt+CeQ3NpmBg+GoaVCuWAARJN/KfglbLyyYygcQq0SgeDh8dRKUiaW3HQSoYvTvdTuqzwK4CXsr3b5/dAOY8uMuG/IAR3FgwTbZ1dtoWRvOTa8hYiU6A475WuZKyEHcwnGYe57u2I2KbMgcKjPniocj4QzgYsVAVKW3IwaOhyE+vPxsiUkvQHdO2fojCkY8jg70jxM+gu59tPDNbw3Uh/2Ij310FgTHsnGQMyA==" + + p = __local2__ { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + note: cryptox509parsecertificates/DER, single cert, b64 + query: data.generated.p = x + want_result: + - x: + - mail.google.com diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0118.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0118.yaml new file mode 100644 index 000000000000..10f6cce49530 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0118.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + certs = "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" + + p = __local2__ { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + note: cryptox509parsecertificates/DER, chain, b64 + query: data.generated.p = x + want_result: + - x: + - mail.google.com + - Thawte SGC CA diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0119.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0119.yaml new file mode 100644 index 000000000000..feeb69f5e48d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0119.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + certs = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlGZHpDQ0JGK2dBd0lCQWdJU0EzTnJpQUV1cy8rY3ZmbHZoVlFPVzV6VE1BMEdDU3FHU0liM0RRRUJDd1VBDQpNRW94Q3pBSkJnTlZCQVlUQWxWVE1SWXdGQVlEVlFRS0V3MU1aWFFuY3lCRmJtTnllWEIwTVNNd0lRWURWUVFEDQpFeHBNWlhRbmN5QkZibU55ZVhCMElFRjFkR2h2Y21sMGVTQllNekFlRncweU1EQTNNVEF4TmpBd016QmFGdzB5DQpNREV3TURneE5qQXdNekJhTUI0eEhEQWFCZ05WQkFNVEUyOXdaVzV3YjJ4cFkzbGhaMlZ1ZEM1dmNtY3dnZ0VpDQpNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUN5eThIWlhWVEoyVFNIWFlub0wrQ0tZcG80DQp3ejF3b3dVY2R0L1hCZ04wOGYzN054YU5rK1ZBajhHRDJzNnpob0hMU2h5WVMyUFZvc2Y3eHVtdnlHOTE0UExwDQpJSE85V21DYVpNcXdFeXZNTS9WRTlkQmtLZmFUbzc4QlQ2YVh5Sm1ua2pwZUZtQk9HczN1UDViVUFSajNPbm5yDQo3QW9zOWo0NXJncnl0cGVsWVRNbExpNmpWdEJ2NVJJWnVNb0oxNVcyNTJ0OGVJZ3NPcTU3YWQwQm9iZXl5NFR1DQpHaHZlUDBWM3ZVSnZJM2licUg1RTljV3pJMmY4VXRvaXJVTmYwSjN0Y25nOEpxU091dXpXRFlXclJEQXpRYkpZDQpxS3p2VkRjTitwdHFWN0daNkp1cUhoZHdnRGVxQk9zdmVEYnpBQXlZU1ZQSmpSV1llYThNeGxNN09YYnRBZ01CDQpBQUdqZ2dLQk1JSUNmVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUhBd0VHDQpDQ3NHQVFVRkJ3TUNNQXdHQTFVZEV3RUIvd1FDTUFBd0hRWURWUjBPQkJZRUZIRHdlYjZLcHJTdldydy92UjZrDQp3VFZwdWRQdE1COEdBMVVkSXdRWU1CYUFGS2hLYW1NRWZkMjY1dEU1dDZaRlplL3pxT3loTUc4R0NDc0dBUVVGDQpCd0VCQkdNd1lUQXVCZ2dyQmdFRkJRY3dBWVlpYUhSMGNEb3ZMMjlqYzNBdWFXNTBMWGd6TG14bGRITmxibU55DQplWEIwTG05eVp6QXZCZ2dyQmdFRkJRY3dBb1lqYUhSMGNEb3ZMMk5sY25RdWFXNTBMWGd6TG14bGRITmxibU55DQplWEIwTG05eVp5OHdOd1lEVlIwUkJEQXdMb0lUYjNCbGJuQnZiR2xqZVdGblpXNTBMbTl5WjRJWGQzZDNMbTl3DQpaVzV3YjJ4cFkzbGhaMlZ1ZEM1dmNtY3dUQVlEVlIwZ0JFVXdRekFJQmdabmdRd0JBZ0V3TndZTEt3WUJCQUdDDQozeE1CQVFFd0tEQW1CZ2dyQmdFRkJRY0NBUllhYUhSMGNEb3ZMMk53Y3k1c1pYUnpaVzVqY25sd2RDNXZjbWN3DQpnZ0VFQmdvckJnRUVBZFo1QWdRQ0JJSDFCSUh5QVBBQWRnQmVwM1A1MzFiQTU3VTJTSDNRU2VBeWVwR2FESVNoDQpFaEtFR0hXV2dYRkZXQUFBQVhNNXE5dkRBQUFFQXdCSE1FVUNJUUNSSHFncnRsMDdZNlRyeWZNbVFONlROS1JWDQptMUxUeTl2STNNaC9rcmJTUVFJZ1lnVkFLd1hSb1BSK0JOMXBjSmJKdjNBaXZiaDZFN0w5ODdyTVNFUWs1Vm9BDQpkZ0N5SGdYTWk2TE5paUJPaDJiNUs3bUtKU0JuYTlyNmNPZXlTVk10NzR1UVhnQUFBWE01cTl1dUFBQUVBd0JIDQpNRVVDSVFEZHJ1VHV0US9VY2hja3FZUSsycDltdXRuclNublFYYTh4TEE0MVlHelpIZ0lnWFhFVEZiR2ZuczJDDQo3WUo4Y0RvWVlBam1kek1nOGs3aEtYUUd1L0tzQWI0d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFHazlwNXl0DQpPYURJUFJQazVJbXBIMWY2ZjAxMG1VTFdQVjVQam42a3pNSFA5ejVuZE16KysxTk92SFY0R1ZCQ29ldUtxMWJwDQpGQ0QrSWdBOXBjSkFFWFEvdTRHcG1iQUtVWnptZk1JYjg5YVJnbkpwMG14OVk0QkJkNDVFeFVXczh3NGNmZ0ZaDQp5WlVlSHZXczFhbnBBY1IyRklacEFWTVFDYUlnak90MmRkUjF4djRhY0N3K21EL0I5b0tmR1pFVWd5SUFOdnBCDQpJRGFiZ2dMU3dGYTlPS0tYUkJWUkFhZm83T2FjMjFIUVU3RTNzWHBoYUhaR2ZuMkYyN2REL3FvcVVjTHFyNGxDDQpjN2xORTBZR3A2cithUG85VkxjSDJWMGxONHQrMVZiVkFyd0t6bnNOZGNRbndLQmV0Z3F2WnJnTGc0K3FqbzR5DQp1aXhKWTM4WFUvYjdiYVU9DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=" + + p = __local2__ { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + note: cryptox509parsecertificates/PEM, single cert, b64 + query: data.generated.p = x + want_result: + - x: + - openpolicyagent.org diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0120.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0120.yaml new file mode 100644 index 000000000000..c6877c780011 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0120.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + certs = "-----BEGIN CERTIFICATE-----\nMIIFdzCCBF+gAwIBAgISA3NriAEus/+cvflvhVQOW5zTMA0GCSqGSIb3DQEBCwUA\nMEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD\nExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0yMDA3MTAxNjAwMzBaFw0y\nMDEwMDgxNjAwMzBaMB4xHDAaBgNVBAMTE29wZW5wb2xpY3lhZ2VudC5vcmcwggEi\nMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCyy8HZXVTJ2TSHXYnoL+CKYpo4\nwz1wowUcdt/XBgN08f37NxaNk+VAj8GD2s6zhoHLShyYS2PVosf7xumvyG914PLp\nIHO9WmCaZMqwEyvMM/VE9dBkKfaTo78BT6aXyJmnkjpeFmBOGs3uP5bUARj3Onnr\n7Aos9j45rgrytpelYTMlLi6jVtBv5RIZuMoJ15W252t8eIgsOq57ad0Bobeyy4Tu\nGhveP0V3vUJvI3ibqH5E9cWzI2f8UtoirUNf0J3tcng8JqSOuuzWDYWrRDAzQbJY\nqKzvVDcN+ptqV7GZ6JuqHhdwgDeqBOsveDbzAAyYSVPJjRWYea8MxlM7OXbtAgMB\nAAGjggKBMIICfTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG\nCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFHDweb6KprSvWrw/vR6k\nwTVpudPtMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUF\nBwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNy\neXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNy\neXB0Lm9yZy8wNwYDVR0RBDAwLoITb3BlbnBvbGljeWFnZW50Lm9yZ4IXd3d3Lm9w\nZW5wb2xpY3lhZ2VudC5vcmcwTAYDVR0gBEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC\n3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcw\nggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdgBep3P531bA57U2SH3QSeAyepGaDISh\nEhKEGHWWgXFFWAAAAXM5q9vDAAAEAwBHMEUCIQCRHqgrtl07Y6TryfMmQN6TNKRV\nm1LTy9vI3Mh/krbSQQIgYgVAKwXRoPR+BN1pcJbJv3Aivbh6E7L987rMSEQk5VoA\ndgCyHgXMi6LNiiBOh2b5K7mKJSBna9r6cOeySVMt74uQXgAAAXM5q9uuAAAEAwBH\nMEUCIQDdruTutQ/UchckqYQ+2p9mutnrSnnQXa8xLA41YGzZHgIgXXETFbGfns2C\n7YJ8cDoYYAjmdzMg8k7hKXQGu/KsAb4wDQYJKoZIhvcNAQELBQADggEBAGk9p5yt\nOaDIPRPk5ImpH1f6f010mULWPV5Pjn6kzMHP9z5ndMz++1NOvHV4GVBCoeuKq1bp\nFCD+IgA9pcJAEXQ/u4GpmbAKUZzmfMIb89aRgnJp0mx9Y4BBd45ExUWs8w4cfgFZ\nyZUeHvWs1anpAcR2FIZpAVMQCaIgjOt2ddR1xv4acCw+mD/B9oKfGZEUgyIANvpB\nIDabggLSwFa9OKKXRBVRAafo7Oac21HQU7E3sXphaHZGfn2F27dD/qoqUcLqr4lC\nc7lNE0YGp6r+aPo9VLcH2V0lN4t+1VbVArwKznsNdcQnwKBetgqvZrgLg4+qjo4y\nuixJY38XU/b7baU=\n-----END CERTIFICATE-----" + + p = __local2__ { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + note: cryptox509parsecertificates/PEM, single cert, string + query: data.generated.p = x + want_result: + - x: + - openpolicyagent.org diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0121.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0121.yaml new file mode 100644 index 000000000000..4f7db4a78036 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0121.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + certs = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlGZHpDQ0JGK2dBd0lCQWdJU0EzTnJpQUV1cy8rY3ZmbHZoVlFPVzV6VE1BMEdDU3FHU0liM0RRRUJDd1VBTUVveEN6QUpCZ05WQkFZVEFsVlRNUll3RkFZRFZRUUtFdzFNWlhRbmN5QkZibU55ZVhCME1TTXdJUVlEVlFRREV4cE1aWFFuY3lCRmJtTnllWEIwSUVGMWRHaHZjbWwwZVNCWU16QWVGdzB5TURBM01UQXhOakF3TXpCYUZ3MHlNREV3TURneE5qQXdNekJhTUI0eEhEQWFCZ05WQkFNVEUyOXdaVzV3YjJ4cFkzbGhaMlZ1ZEM1dmNtY3dnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDeXk4SFpYVlRKMlRTSFhZbm9MK0NLWXBvNHd6MXdvd1VjZHQvWEJnTjA4ZjM3TnhhTmsrVkFqOEdEMnM2emhvSExTaHlZUzJQVm9zZjd4dW12eUc5MTRQTHBJSE85V21DYVpNcXdFeXZNTS9WRTlkQmtLZmFUbzc4QlQ2YVh5Sm1ua2pwZUZtQk9HczN1UDViVUFSajNPbm5yN0FvczlqNDVyZ3J5dHBlbFlUTWxMaTZqVnRCdjVSSVp1TW9KMTVXMjUydDhlSWdzT3E1N2FkMEJvYmV5eTRUdUdodmVQMFYzdlVKdkkzaWJxSDVFOWNXekkyZjhVdG9pclVOZjBKM3Rjbmc4SnFTT3V1eldEWVdyUkRBelFiSllxS3p2VkRjTitwdHFWN0daNkp1cUhoZHdnRGVxQk9zdmVEYnpBQXlZU1ZQSmpSV1llYThNeGxNN09YYnRBZ01CQUFHamdnS0JNSUlDZlRBT0JnTlZIUThCQWY4RUJBTUNCYUF3SFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01Bd0dBMVVkRXdFQi93UUNNQUF3SFFZRFZSME9CQllFRkhEd2ViNktwclN2V3J3L3ZSNmt3VFZwdWRQdE1COEdBMVVkSXdRWU1CYUFGS2hLYW1NRWZkMjY1dEU1dDZaRlplL3pxT3loTUc4R0NDc0dBUVVGQndFQkJHTXdZVEF1QmdnckJnRUZCUWN3QVlZaWFIUjBjRG92TDI5amMzQXVhVzUwTFhnekxteGxkSE5sYm1OeWVYQjBMbTl5WnpBdkJnZ3JCZ0VGQlFjd0FvWWphSFIwY0RvdkwyTmxjblF1YVc1MExYZ3pMbXhsZEhObGJtTnllWEIwTG05eVp5OHdOd1lEVlIwUkJEQXdMb0lUYjNCbGJuQnZiR2xqZVdGblpXNTBMbTl5WjRJWGQzZDNMbTl3Wlc1d2IyeHBZM2xoWjJWdWRDNXZjbWN3VEFZRFZSMGdCRVV3UXpBSUJnWm5nUXdCQWdFd053WUxLd1lCQkFHQzN4TUJBUUV3S0RBbUJnZ3JCZ0VGQlFjQ0FSWWFhSFIwY0RvdkwyTndjeTVzWlhSelpXNWpjbmx3ZEM1dmNtY3dnZ0VFQmdvckJnRUVBZFo1QWdRQ0JJSDFCSUh5QVBBQWRnQmVwM1A1MzFiQTU3VTJTSDNRU2VBeWVwR2FESVNoRWhLRUdIV1dnWEZGV0FBQUFYTTVxOXZEQUFBRUF3QkhNRVVDSVFDUkhxZ3J0bDA3WTZUcnlmTW1RTjZUTktSVm0xTFR5OXZJM01oL2tyYlNRUUlnWWdWQUt3WFJvUFIrQk4xcGNKYkp2M0FpdmJoNkU3TDk4N3JNU0VRazVWb0FkZ0N5SGdYTWk2TE5paUJPaDJiNUs3bUtKU0JuYTlyNmNPZXlTVk10NzR1UVhnQUFBWE01cTl1dUFBQUVBd0JITUVVQ0lRRGRydVR1dFEvVWNoY2txWVErMnA5bXV0bnJTbm5RWGE4eExBNDFZR3paSGdJZ1hYRVRGYkdmbnMyQzdZSjhjRG9ZWUFqbWR6TWc4azdoS1hRR3UvS3NBYjR3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQUdrOXA1eXRPYURJUFJQazVJbXBIMWY2ZjAxMG1VTFdQVjVQam42a3pNSFA5ejVuZE16KysxTk92SFY0R1ZCQ29ldUtxMWJwRkNEK0lnQTlwY0pBRVhRL3U0R3BtYkFLVVp6bWZNSWI4OWFSZ25KcDBteDlZNEJCZDQ1RXhVV3M4dzRjZmdGWnlaVWVIdldzMWFucEFjUjJGSVpwQVZNUUNhSWdqT3QyZGRSMXh2NGFjQ3crbUQvQjlvS2ZHWkVVZ3lJQU52cEJJRGFiZ2dMU3dGYTlPS0tYUkJWUkFhZm83T2FjMjFIUVU3RTNzWHBoYUhaR2ZuMkYyN2REL3FvcVVjTHFyNGxDYzdsTkUwWUdwNnIrYVBvOVZMY0gyVjBsTjR0KzFWYlZBcndLem5zTmRjUW53S0JldGdxdlpyZ0xnNCtxam80eXVpeEpZMzhYVS9iN2JhVT0NCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQ0KTUlJRWtqQ0NBM3FnQXdJQkFnSVFDZ0ZCUWdBQUFWT0ZjMm9MaGV5bkNEQU5CZ2txaGtpRzl3MEJBUXNGQURBL01TUXdJZ1lEVlFRS0V4dEVhV2RwZEdGc0lGTnBaMjVoZEhWeVpTQlVjblZ6ZENCRGJ5NHhGekFWQmdOVkJBTVREa1JUVkNCU2IyOTBJRU5CSUZnek1CNFhEVEUyTURNeE56RTJOREEwTmxvWERUSXhNRE14TnpFMk5EQTBObG93U2pFTE1Ba0dBMVVFQmhNQ1ZWTXhGakFVQmdOVkJBb1REVXhsZENkeklFVnVZM0o1Y0hReEl6QWhCZ05WQkFNVEdreGxkQ2R6SUVWdVkzSjVjSFFnUVhWMGFHOXlhWFI1SUZnek1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBbk5NTThGcmxMa2UzY2wwM2c3Tm9ZekRxMXpVbUdTWGh2YjQxOFhDU0w3ZTRTMEVGcTZtZU5RaFk3TEVxeEdpSEM2UGpkZVRtODZkaWNicDVnV0FmMTVHYW4vUFFlR2R4eUdrT2xaSFAvdWFaNldBOFNNeCt5azEzRWlTZFJ4dGE2N25zSGpjQUhKeXNlNmNGNnM1SzY3MUI1VGFZdWN2OWJUeVdhTjhqS2tLUURJWjBaOGgvcFpxNFVtRVVFejlsNllLSHk5djZEbGIyaG9uemhUK1hocSt3M0JydmF3MlZGbjNFSzZCbHNwa0VObldBYTZ4Szh4dVFTWGd2b3BaUEtpQWxLUVRHZE1EUU1jMlBNVGlWRnJxb003aEQ4YkVmd3pCL29ua3hFejB0TnZqai9QSXphcms1TWNXdnhJME5IV1FXTTZyNmhDbTIxQXZBMkgzRGt3SURBUUFCbzRJQmZUQ0NBWGt3RWdZRFZSMFRBUUgvQkFnd0JnRUIvd0lCQURBT0JnTlZIUThCQWY4RUJBTUNBWVl3ZndZSUt3WUJCUVVIQVFFRWN6QnhNRElHQ0NzR0FRVUZCekFCaGlab2RIUndPaTh2YVhOeVp5NTBjblZ6ZEdsa0xtOWpjM0F1YVdSbGJuUnlkWE4wTG1OdmJUQTdCZ2dyQmdFRkJRY3dBb1l2YUhSMGNEb3ZMMkZ3Y0hNdWFXUmxiblJ5ZFhOMExtTnZiUzl5YjI5MGN5OWtjM1J5YjI5MFkyRjRNeTV3TjJNd0h3WURWUjBqQkJnd0ZvQVV4S2V4cEhzc2NmcmI0VXVRZGYvRUZXQ0ZpUkF3VkFZRFZSMGdCRTB3U3pBSUJnWm5nUXdCQWdFd1B3WUxLd1lCQkFHQzN4TUJBUUV3TURBdUJnZ3JCZ0VGQlFjQ0FSWWlhSFIwY0RvdkwyTndjeTV5YjI5MExYZ3hMbXhsZEhObGJtTnllWEIwTG05eVp6QThCZ05WSFI4RU5UQXpNREdnTDZBdGhpdG9kSFJ3T2k4dlkzSnNMbWxrWlc1MGNuVnpkQzVqYjIwdlJGTlVVazlQVkVOQldETkRVa3d1WTNKc01CMEdBMVVkRGdRV0JCU29TbXBqQkgzZHV1YlJPYmVtUldYdjg2anNvVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBM1RQWEVmTmpXRGpkR0JYN0NWVytkbGE1Y0VpbGFVY25lOElrQ0pMeFdoOUtFaWszSkhSUkhHSm91TTJWY0dmbDk2UzhUaWhSelp2b3JvZWQ2dGk2V3FFQm10enczV29kYXRnK1Z5T2VwaDRFWXByLzF3WEt0eDgvd0FwSXZKU3d0bVZpNE1GVTVhTXFyU0RFNmVhNzNNajJ0Y015bzVqTWQ2am1lV1VISzhzby9qb1dVb0hPVWd3dVg0UG8xUVl6KzNkc3prRHFNcDRma2x4QndYUnNXMTBLWHpQTVRaK3NPUEF2ZXl4aW5kbWprVzhsR3krUXNSbEdQZlorRzZaNmg3bWplbTBZK2lXbGtZY1Y0UElXTDFpd0JpOHNhQ2JHUzVqTjJwOE0rWCtRN1VOS0VrUk9iM042S09xa3FtNTdUSDJIM2VESkFrU25oNi9ETkZ1MFFnPT0NCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQ0KTUlJRFNqQ0NBaktnQXdJQkFnSVFSSyt3Z05hako3cUpNRG1HTHZoQWF6QU5CZ2txaGtpRzl3MEJBUVVGQURBL01TUXdJZ1lEVlFRS0V4dEVhV2RwZEdGc0lGTnBaMjVoZEhWeVpTQlVjblZ6ZENCRGJ5NHhGekFWQmdOVkJBTVREa1JUVkNCU2IyOTBJRU5CSUZnek1CNFhEVEF3TURrek1ESXhNVEl4T1ZvWERUSXhNRGt6TURFME1ERXhOVm93UHpFa01DSUdBMVVFQ2hNYlJHbG5hWFJoYkNCVGFXZHVZWFIxY21VZ1ZISjFjM1FnUTI4dU1SY3dGUVlEVlFRREV3NUVVMVFnVW05dmRDQkRRU0JZTXpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTit2NlpkUUNJTlh0TXhpWmZhUWd1ekgweXhyTU1wYjdObkRmY2RBd1JnVWkrRG9NM1pKS3VNL0lVbVRyRTRPcno1SXkyWHUvTk1oRDJYU0t0a3lqNHpsOTNld0VudTFsY0NKbzZtNjdYTXVlZ3dHTW9PaWZvb1VNTTBSb09FcU9MbDVDakg5VUwyQVpkKzNVV09EeU9LSVllcExZWUhzVW11NW91SkxHaWlmU0tPZUROb0pqajRYTGg3ZElOOWJ4aXFLcXk2OWNLM0ZDeG9sa0hSeXhYdHFxelRXTUluLzVXZ1RlMVFMeU5hdTdGcWNraDQ5WkxPTXh0Ky95VUZ3N0JaeTFTYnNPRlU1UTlEOC9SaGNRUEdYNjlXYW00MGR1dG9sdWNiWTM4RVZBanFyMm03eFBpNzFYQWljUE5hRGFlUVFteGtxdGlsWDQrVTltNS93QWwwQ0F3RUFBYU5DTUVBd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBT0JnTlZIUThCQWY4RUJBTUNBUVl3SFFZRFZSME9CQllFRk1TbnNhUjdMSEg2MitGTGtIWC94QlZnaFlrUU1BMEdDU3FHU0liM0RRRUJCUVVBQTRJQkFRQ2pHaXliRndCY3FSN3VLR1kzT3IrRHh6OUx3d21nbFNCZDQ5bFpSTkkrRFQ2OWlrdWdkQi9PRUlLY2RCb2RmcGdhM2NzVFM3TWdST1NSNmN6OGZhWGJhdVgrNXYzZ1R0MjNBRHExY0Vtdjh1WHJBdkhSQW9zWnk1UTZYa2pFR0I1WUdWOGVBbHJ3RFBHeHJhbmNXWWFMYnVtUjlZYksrcmxtTTZwWlc4N2lweFp6UjhzcnpKbXdOMGpQNDFaTDljOFBESEl5aDhid1JMdFRjbTFEOVNaSW1sSm50MWlyL21kMmNYamJEYUpXRkJNNUpER0ZvcWdDV2pCSDRkMVFCN3dDQ1pBQTYyUmpZSnNXdklqSkV1YlNmWkdMK1QweWpXVzA2WHl4VjNicXhiWW9PYjhWWlJ6STluZVdhZ3FOZHd2WWtRc0VqZ2ZiS2JZSzdwMkNOVFVRDQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=" + + p = __local2__ { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + note: cryptox509parsecertificates/PEM, chain, b64 + query: data.generated.p = x + want_result: + - x: + - openpolicyagent.org + - Let's Encrypt Authority X3 + - DST Root CA X3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0122.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0122.yaml new file mode 100644 index 000000000000..e7e0d610b831 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0122.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + certs = "-----BEGIN CERTIFICATE-----\nMIIFdzCCBF+gAwIBAgISA3NriAEus/+cvflvhVQOW5zTMA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0yMDA3MTAxNjAwMzBaFw0yMDEwMDgxNjAwMzBaMB4xHDAaBgNVBAMTE29wZW5wb2xpY3lhZ2VudC5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCyy8HZXVTJ2TSHXYnoL+CKYpo4wz1wowUcdt/XBgN08f37NxaNk+VAj8GD2s6zhoHLShyYS2PVosf7xumvyG914PLpIHO9WmCaZMqwEyvMM/VE9dBkKfaTo78BT6aXyJmnkjpeFmBOGs3uP5bUARj3Onnr7Aos9j45rgrytpelYTMlLi6jVtBv5RIZuMoJ15W252t8eIgsOq57ad0Bobeyy4TuGhveP0V3vUJvI3ibqH5E9cWzI2f8UtoirUNf0J3tcng8JqSOuuzWDYWrRDAzQbJYqKzvVDcN+ptqV7GZ6JuqHhdwgDeqBOsveDbzAAyYSVPJjRWYea8MxlM7OXbtAgMBAAGjggKBMIICfTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFHDweb6KprSvWrw/vR6kwTVpudPtMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUFBwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNyeXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNyeXB0Lm9yZy8wNwYDVR0RBDAwLoITb3BlbnBvbGljeWFnZW50Lm9yZ4IXd3d3Lm9wZW5wb2xpY3lhZ2VudC5vcmcwTAYDVR0gBEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdgBep3P531bA57U2SH3QSeAyepGaDIShEhKEGHWWgXFFWAAAAXM5q9vDAAAEAwBHMEUCIQCRHqgrtl07Y6TryfMmQN6TNKRVm1LTy9vI3Mh/krbSQQIgYgVAKwXRoPR+BN1pcJbJv3Aivbh6E7L987rMSEQk5VoAdgCyHgXMi6LNiiBOh2b5K7mKJSBna9r6cOeySVMt74uQXgAAAXM5q9uuAAAEAwBHMEUCIQDdruTutQ/UchckqYQ+2p9mutnrSnnQXa8xLA41YGzZHgIgXXETFbGfns2C7YJ8cDoYYAjmdzMg8k7hKXQGu/KsAb4wDQYJKoZIhvcNAQELBQADggEBAGk9p5ytOaDIPRPk5ImpH1f6f010mULWPV5Pjn6kzMHP9z5ndMz++1NOvHV4GVBCoeuKq1bpFCD+IgA9pcJAEXQ/u4GpmbAKUZzmfMIb89aRgnJp0mx9Y4BBd45ExUWs8w4cfgFZyZUeHvWs1anpAcR2FIZpAVMQCaIgjOt2ddR1xv4acCw+mD/B9oKfGZEUgyIANvpBIDabggLSwFa9OKKXRBVRAafo7Oac21HQU7E3sXphaHZGfn2F27dD/qoqUcLqr4lCc7lNE0YGp6r+aPo9VLcH2V0lN4t+1VbVArwKznsNdcQnwKBetgqvZrgLg4+qjo4yuixJY38XU/b7baU=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0NlowSjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMTGkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EFq6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWAa6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIGCCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNvbTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9kc3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAwVAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcCARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwuY3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsFAAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJouM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwuX4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlGPfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n-----END CERTIFICATE-----" + + p = __local2__ { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + note: cryptox509parsecertificates/PEM, chain, string + query: data.generated.p = x + want_result: + - x: + - openpolicyagent.org + - Let's Encrypt Authority X3 + - DST Root CA X3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0123.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0123.yaml new file mode 100644 index 000000000000..865cfd9c04c0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0123.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + modules: + - | + package generated + + certs = "YmFkc3RyaW5n" + + p = __local2__ { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + note: cryptox509parsecertificates/invalid DER or PEM data, b64 + query: data.generated.p = x + want_error: "x509: malformed certificate" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0124.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0124.yaml new file mode 100644 index 000000000000..64898922ebab --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-0124.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + modules: + - | + package generated + + certs = "foobar" + + p = __local2__ { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + note: cryptox509parsecertificates/invalid DER or PEM data, string + query: data.generated.p = x + want_error: illegal base64 + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-raw-uris.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-raw-uris.yaml new file mode 100644 index 000000000000..30acb87711e3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsecertificates/test-cryptox509parsecertificates-raw-uris.yaml @@ -0,0 +1,27 @@ +--- +cases: + - data: + modules: + - | + package generated + + certs = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lJZGxpT1dVY1NXM3N3Q2dZSUtvWkl6ajBFQXdJd1BURUxNQWtHQTFVRUJoTUMKUjBJeEVEQU9CZ05WQkFvVEIwVjRZVzF3YkdVeEhEQWFCZ05WQkFVVEV6RTFPREV4TnpnNU56UTJPRFkxTmpneQpOalF3SUJjTk1qTXhNVEl3TVRZMU5USTRXaGdQTWpFeU1qRXdNamN4TmpVMU1qaGFNRDB4Q3pBSkJnTlZCQVlUCkFrZENNUkF3RGdZRFZRUUtFd2RGZUdGdGNHeGxNUnd3R2dZRFZRUUZFeE00TlRJM056SXlOREE0TlRJeE5qVXoKTVRFMU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRXp0UDNrQnNpQXY4UUF5eWxUalJZSFlWegpjWTB5YmpBdC9VbWpZb3Fxb0o4SEtIdXF1ckRaUmVwa05qUXdwV3pmZndZZ0xaNk42SisyVUlPdlZ0TDZEcU5rCk1HSXdEZ1lEVlIwUEFRSC9CQVFEQWdlQU1CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUNCZ2dyQmdFRkJRY0QKQVRBTUJnTlZIUk1CQWY4RUFqQUFNQ01HQTFVZEVRUWNNQnFHR0hOd2FXWm1aVG92TDJWNFlXMXdiR1V1WTI5dApMMjl3WVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQXlRNDhPd25lTHkzMjZqYitEUjd5RjJhcS94Wnl1cW9qCitUU3ZLVVB5NEU0Q0lRQ0VMUlp3K0dWTjhJR0drVGV4MGxxTDNxY21mWldJbm15VitrbnQ0d3p3L3c9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==" + + uri_strings = crypto.x509.parse_certificates(certs)[0].URIStrings + note: cryptox509parsecertificates/uri_strings + query: data.generated.uri_strings = x + want_result: + - x: + - spiffe://example.com/opa + - data: + modules: + - | + package generated + + certs = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNTakNDQWJPZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRMEZBREJDTVFzd0NRWURWUVFHRXdKMWN6RUwKTUFrR0ExVUVDQXdDUTBFeEVEQU9CZ05WQkFvTUIwVjRZVzF3YkdVeEZEQVNCZ05WQkFNTUMyVjRZVzF3YkdVdQpZMjl0TUI0WERUSXpNVEV5T1RFMk5ESXdPRm9YRFRJME1URXlPREUyTkRJd09Gb3dRakVMTUFrR0ExVUVCaE1DCmRYTXhDekFKQmdOVkJBZ01Ba05CTVJBd0RnWURWUVFLREFkRmVHRnRjR3hsTVJRd0VnWURWUVFEREF0bGVHRnQKY0d4bExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBempOT1puY05DL25MdEZQYwpUNnNlSzZ0ditTbU9GaGk3NVBKRm9sQ0dFZUFiTHJHTzhaUmR2cXY2OStTTk41MUhrNFBJUDUrejk4aHZIdWJQClVtcGdOMVdVM3FKK2tOVTJXL3poR3pLMTdIL2c3YjVJTjRHZmx3bXJlRWZscnRicnZKSGRlRkVhVGtmYnVld0YKVmZvLzRiaG0yYUthczNHcUo3RnlBNDVxeVUwQ0F3RUFBYU5RTUU0d0hRWURWUjBPQkJZRUZOUStDMUVOK0dSYwpGS1dyTll6ZWdnbFc3TE9lTUI4R0ExVWRJd1FZTUJhQUZOUStDMUVOK0dSY0ZLV3JOWXplZ2dsVzdMT2VNQXdHCkExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVOQlFBRGdZRUF1cXdVVWVXbTFJaURRbmphK1hqd1VCaXUKQXBYWEx5NlFZRG9jUkhoRHlJS3BKSWRJOXltNi9RcVhkOFQ2WGlYUTJDbFNJbGxBSTByQWJYNUZvYzNxaWRkUAp0d2huT1pxd3NZdFhUNy9XOHFLSm5FVnhZckJuRmZSdk9SU3ZlOEtwSDErVHQ2elZEYlJ5bENacTR1TTNrZXN2CmJabXlVdlM1bldBVm44ZmhFdUU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=" + + uri_strings = crypto.x509.parse_certificates(certs)[0].URIStrings + note: cryptox509parsecertificates/uri_strings_no_uris + query: data.generated.uri_strings = x + want_result: + - x: null diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsekeypair/test-cryptox509parsekeypairs-0118.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsekeypair/test-cryptox509parsekeypairs-0118.yaml new file mode 100644 index 000000000000..2e27c9be306f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsekeypair/test-cryptox509parsekeypairs-0118.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: cryptoX509ParseKeyPairs/PEM_encoded_string_cert_and_key + query: data.test.p = x + modules: + - | + package test + p := crypto.x509.parse_keypair(input.cert, input.key)["Certificate"][0] + input: + { + cert: "-----BEGIN CERTIFICATE-----\nMIIEszCCApsCFDPRm4sTNZqiH601E6E6pEaJaCKqMA0GCSqGSIb3DQEBCwUAMBYx\nFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIzMDUxMjEyMDIxNFoXDTIzMDYxMTEy\nMDIxNFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUA\nA4ICDwAwggIKAoICAQDTcNASD17ohP3V14LMUIkFwAeeeniSXy4Pl3EqIgVpt9ul\n2IZiTm4JOYSgQ681bjt1OdwCkBf2Cg62DUyHMMwwnbaznN/Y5piYlaS33XCvcNpM\nOWxwA4Z5Q0jNAshBExp8EzjEojbIUoeSncAP9jtOO/NArhLq+XodmONLqBG8KmZc\n2uiAeJ7ZjTpOckASYPYy7tUc4Ha3XUV0SoF/+0SJrXYOtpyMuMxwks6jMbpacmZn\njFIe8m2xH97s5inmHkzjBVKbpBQQHROP6A61VOrH9FrJRiACZ440zFJ1CGlXZau4\noJgs9S1YRDI4W2Ha1WIYLpUBEMYXVYyn4swTBj42HqpSmfppOoR2g1d/AjxpFVAk\nPLrict/nszhTIkEUE39vJh4HuytT49ss/cp4KokxBjLz7LmTUEzxJS8dB9714KzO\nhFRHnpap0onU7Yb+Yz8/21bZL4AEL1d2Jg48jRET1Mrl9GOnWjzo7JxioUsbLcOo\ny8Qcu2L87BQZqHCVpq6TcJKfPgh9t7y1AHvAfoXJdGSwimCZXuR4VEA3NJAHgPOw\nLypB+dukzseWLdN+e7YOYqyWfjJg4aQmCx+nr866/QlQ4mrEBTkBsDOO5tEsKugI\nredu+lL6z9QFdmeQpoC+s6JKnV29KHZXNDBZ1gnXOyA6lIBjjFjZqnkwT1ToHwID\nAQABMA0GCSqGSIb3DQEBCwUAA4ICAQC8v5BIEooDSe5Kpzwsh9QLK7Ip33v4VHo+\nq82DRjAXiosFoaJ1dg+hwXOUo5VPSOqWCPNYRHDabuX4oiZAGPA9O5lTKQ/PQS4y\nYIsD9XgeuDllBo3Y/uBwjoFFAD169fYMmZgGQ/PCdFS9nrYduPAIzNnsip+Z+XRK\nqLwm83H/nKKgVvbGprNcivG3c8H42CKQ7aqDkEGuSG9EVRnQOMaI7ILMx/oj33Mk\nuJ/21z4AHj7ads3dUF4syEBo5gWCAiBd+g1E/BkFnn+G+oOYo7c12dB+r6rbgrA0\nBk+bW0NvU2ApP/LRRGaJD4wW2UoAklL618+CRWNtXdT+WeKIPlrEdC/0yzU5t6sA\nII+PfvbeWQfL5CssbWhkTbJFt3JKTrIMc9cMjNcqeS+bAELuI16d4CZ5TlmZZeMd\n1WCv6R3YifkR9IogZhix20nPCF1mfo7Q0XAywkx8pPAC4n6uYStU9YobNAGlWXz+\nYbbJDf9r9au88Gu6xFf3eETKu2tJsQgVYBmXic85+FoV45qBI5YZvqiX9XZtWXw2\n391fW+NdK01Jc0tqtADubaF6D4ncnriufqcz+70O0p7CzPbTMtUsPFxcfqZ6x1KX\ngP1TgZM++PuYjHEyCFhHQBz9cQlxPdW4alaDYBnnKvLZXDOAXCWVWS6SXa5tkU3n\nk9i0HC1bhg==\n-----END CERTIFICATE-----\n", + key: "-----BEGIN PRIVATE KEY-----\nMIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDTcNASD17ohP3V\n14LMUIkFwAeeeniSXy4Pl3EqIgVpt9ul2IZiTm4JOYSgQ681bjt1OdwCkBf2Cg62\nDUyHMMwwnbaznN/Y5piYlaS33XCvcNpMOWxwA4Z5Q0jNAshBExp8EzjEojbIUoeS\nncAP9jtOO/NArhLq+XodmONLqBG8KmZc2uiAeJ7ZjTpOckASYPYy7tUc4Ha3XUV0\nSoF/+0SJrXYOtpyMuMxwks6jMbpacmZnjFIe8m2xH97s5inmHkzjBVKbpBQQHROP\n6A61VOrH9FrJRiACZ440zFJ1CGlXZau4oJgs9S1YRDI4W2Ha1WIYLpUBEMYXVYyn\n4swTBj42HqpSmfppOoR2g1d/AjxpFVAkPLrict/nszhTIkEUE39vJh4HuytT49ss\n/cp4KokxBjLz7LmTUEzxJS8dB9714KzOhFRHnpap0onU7Yb+Yz8/21bZL4AEL1d2\nJg48jRET1Mrl9GOnWjzo7JxioUsbLcOoy8Qcu2L87BQZqHCVpq6TcJKfPgh9t7y1\nAHvAfoXJdGSwimCZXuR4VEA3NJAHgPOwLypB+dukzseWLdN+e7YOYqyWfjJg4aQm\nCx+nr866/QlQ4mrEBTkBsDOO5tEsKugIredu+lL6z9QFdmeQpoC+s6JKnV29KHZX\nNDBZ1gnXOyA6lIBjjFjZqnkwT1ToHwIDAQABAoICAAiuKHSdWe2czBjz1IRTyBRK\n2mU4rOuBadAtDPHIXMWGzUclOPsfMihByr6TmMVORbWdzvjx9nHc3ta9fAdOywsx\n5lbAWXY7nUciWZVMy3wAW43mi5uboXEoAHyeIR9+y8cNOPblm+8kaDluLXzaRHwF\nPQrKOq+X11oQtUAdYcECUpp8SDBCA291+09OJHA8t87GfExHsMf9VcUc6+0XoSwv\nyVl4SLwEOCxk8oPDnl1pNegJXDO2CyfK4amDF3RBiTGGveny1foFX5C4W6Y5Grxj\nvThnHxhKLQ3g13/DfSOf4mldenHaDOcDQbaLldxYh0Lr4qUdDazWj8QyrOMZDgOy\nnNnj9zuaI3Hw+zaGm6SQ4pG/s+IujUxyc0yNjGE5R/PswXBkpIR23w5hY7xyr0Eo\n/Gb+jAumKxu/QA1GWY9DL/YKbG0sHJlG8BMzMls5YlWoOhcqw3DHJvLSpI4GlQba\nhC741lNpCrf5FsyDu4ZOVfihDgGq3WteuEZ5vNBgLpateuAmCNjmNSDa/bK1cqBh\nUlgPbFTEYBSklgt8oEEfhvn3ZNvxgRMCFu9USXRQeMIbB+BVQnarWOTYhKL5dqAh\nuYCQprMRuu4FowAFYb37jEBktVAuJDJE4qwqYkLdu2g35SjbUgIJUn3oNDT7sgHs\nSjlWT6005qL6dkjINWXZAoIBAQDXdtWq/jA3RswzQdLewjzaJR9wTbl8lV699ei6\n8HUW9fr55mHyVewRihKNKFF0XnSAKBEqosyZ7joOdMwon0XmCZfJrjxTca4zhBtl\nk1ku9mnRhck0ztc4OUKGyvb9oJ/PlN1CrU/hBwYVjnbWDVtufq/xGxeSzWx2zad/\nb824leawE4c9ozdp1cGtx0iwYAGjjA4BmCQIXiNcYa8qKU6SmHtD95pIbZUvqyOg\ndT8TRcvt4jySibZtpWRF1NczNsnPa9TV6vtJqlZmF+vB1XZVLW0n/CyhAX8pb6qG\ncImaViA4V6/I1VzCYR6FD+vPL8hv8G36rEpwAHLEHWUcvkmHAoIBAQD7ODKXb5XL\nt3L9hxcOYrOshqKUCaT+52/Qmnntb0foxs5CcqBOPerOOTJgEPfn0NW3Ij8zaInk\nNSSSZNJVG3jpWLzOTNUN/Cyep+Tkt50LyvMBa4AUUx3HtkDKqaL789A3WfQ8LJbO\n4WRPM9QNFLxh8uitHkVZY0FgI11eZunNv7kplYltrOGJIVGa3u7rLxLw/EeX7BLi\n1lnaQobVwrVWD1NU1J16PdGp3lKPk8J7y2fSj+UQW76ABopzFayO5nqi06ULMIDm\nwVweYnYOBvNqqdG28u3ob15WCzH4WFd/RYDorXD3Xs9P0LEf9pf/PZ7elU/PEDse\njHXUNSPIVjKpAoIBABqjAEtBXWiYAgqcKpuLW8aELFzP3wx90tadHgZuT6tlAX//\ncUBqSuLoNN7qixddzf1B9s1UjwLApsC+w7aJ6jREH1W5io+uUCDiRhjKnI3nvLFA\nXt1+bLDwsz7CvMIiJ1+cQbZKgsOJAMGNeTeBMzp3wvyFouZtKumNBxYEFmSpc3l1\nEJUYJnOZD3aSWnQjilBTsi+URXAbYze6g9MshCAvZZ3DcHlfwr+/4omltQSG7m0c\nOOzMxZbMiZbwdyJHta9E320KvcIfosrATk8KOrTRBtuYm1PUQYo32dcA9qHz38vX\nW03ywqLtKr68dySH/bmI+a+xuQobpBSGpcdl5uUCggEAIZBckgcCiHk2D9FgrzdY\nshA64HR5auUY91HsQGDBxsPpAs+1wz5ahLr3lAYwWPR52UHmF8Q7yBWhkT2PLHfD\nK8oDT7zMKlYqz/e2iShO/yhaVzI5pn2EWQ5skacgc3EbvIl0LCX48CME9+AA0M6Y\nbK27kIWe1laAgYu4CcjOLAMVhgzIk7KpX1zoPjzSxvE/IptSJWYRD+V7k8GXqi+d\ncqYRiB/v+kkQHhXqCey/6zI96M/41rqrNQeqr72RlHYOpHqKbnhIgIwM9rJI+47K\nLtIJhtvmFUvr2qscPgXvir2Kf4vMsAAmyo8jWxXjMOLWuv5P72ZHv8kcZQHEihua\nIQKCAQEArirbu/1dp/WTPdzhDjRRup8zLHXNzdm1WYCx6NSdouZyWpsqjcJEIKkj\nWSv68hF4wp647eKU3/a27lungqCAKFpKzr1DptvXIJ9iQ53mZ+dL9JYl9hmowtWY\nrrRVLmaroKOvSVgnFRmKm5IEOTmaZhE20F4l0tFa4gSdTgbMSkgO3jvlw5nxte0Q\nE22gLpwhfX+YpbOgMETZ/0aH/MIdwJeyYm3GAhwVMyRnQztz/P8U0irS3GaNeEXc\nLYpsx8F42L9P11OzEw+82DV2XzoHKZUYnGngXs6qByUeuGDTb0daDjOK5on6eNKL\nAj0c+XArCS87P4iflF9maJ3Tk/NfaA==\n-----END PRIVATE KEY-----\n", + } + want_result: + - x: "MIIEszCCApsCFDPRm4sTNZqiH601E6E6pEaJaCKqMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIzMDUxMjEyMDIxNFoXDTIzMDYxMTEyMDIxNFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDTcNASD17ohP3V14LMUIkFwAeeeniSXy4Pl3EqIgVpt9ul2IZiTm4JOYSgQ681bjt1OdwCkBf2Cg62DUyHMMwwnbaznN/Y5piYlaS33XCvcNpMOWxwA4Z5Q0jNAshBExp8EzjEojbIUoeSncAP9jtOO/NArhLq+XodmONLqBG8KmZc2uiAeJ7ZjTpOckASYPYy7tUc4Ha3XUV0SoF/+0SJrXYOtpyMuMxwks6jMbpacmZnjFIe8m2xH97s5inmHkzjBVKbpBQQHROP6A61VOrH9FrJRiACZ440zFJ1CGlXZau4oJgs9S1YRDI4W2Ha1WIYLpUBEMYXVYyn4swTBj42HqpSmfppOoR2g1d/AjxpFVAkPLrict/nszhTIkEUE39vJh4HuytT49ss/cp4KokxBjLz7LmTUEzxJS8dB9714KzOhFRHnpap0onU7Yb+Yz8/21bZL4AEL1d2Jg48jRET1Mrl9GOnWjzo7JxioUsbLcOoy8Qcu2L87BQZqHCVpq6TcJKfPgh9t7y1AHvAfoXJdGSwimCZXuR4VEA3NJAHgPOwLypB+dukzseWLdN+e7YOYqyWfjJg4aQmCx+nr866/QlQ4mrEBTkBsDOO5tEsKugIredu+lL6z9QFdmeQpoC+s6JKnV29KHZXNDBZ1gnXOyA6lIBjjFjZqnkwT1ToHwIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQC8v5BIEooDSe5Kpzwsh9QLK7Ip33v4VHo+q82DRjAXiosFoaJ1dg+hwXOUo5VPSOqWCPNYRHDabuX4oiZAGPA9O5lTKQ/PQS4yYIsD9XgeuDllBo3Y/uBwjoFFAD169fYMmZgGQ/PCdFS9nrYduPAIzNnsip+Z+XRKqLwm83H/nKKgVvbGprNcivG3c8H42CKQ7aqDkEGuSG9EVRnQOMaI7ILMx/oj33MkuJ/21z4AHj7ads3dUF4syEBo5gWCAiBd+g1E/BkFnn+G+oOYo7c12dB+r6rbgrA0Bk+bW0NvU2ApP/LRRGaJD4wW2UoAklL618+CRWNtXdT+WeKIPlrEdC/0yzU5t6sAII+PfvbeWQfL5CssbWhkTbJFt3JKTrIMc9cMjNcqeS+bAELuI16d4CZ5TlmZZeMd1WCv6R3YifkR9IogZhix20nPCF1mfo7Q0XAywkx8pPAC4n6uYStU9YobNAGlWXz+YbbJDf9r9au88Gu6xFf3eETKu2tJsQgVYBmXic85+FoV45qBI5YZvqiX9XZtWXw2391fW+NdK01Jc0tqtADubaF6D4ncnriufqcz+70O0p7CzPbTMtUsPFxcfqZ6x1KXgP1TgZM++PuYjHEyCFhHQBz9cQlxPdW4alaDYBnnKvLZXDOAXCWVWS6SXa5tkU3nk9i0HC1bhg==" diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsekeypair/test-cryptox509parsekeypairs-0119.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsekeypair/test-cryptox509parsekeypairs-0119.yaml new file mode 100644 index 000000000000..8b24e15dc26c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsekeypair/test-cryptox509parsekeypairs-0119.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: cryptoX509ParseKeyPairs/base64_encoded_string_cert_and_key + query: data.test.p = x + modules: + - | + package test + p := crypto.x509.parse_keypair(input.cert, input.key)["Certificate"][0] + input: + { + cert: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUY3ekNDQTllZ0F3SUJBZ0lVZFJIQStCMC9aZ2tuS1BsQjJmc3dFOThsekFjd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZWXhDekFKQmdOVkJBWVRBbGhZTVJJd0VBWURWUVFJREFsVGRHRjBaVTVoYldVeEVUQVBCZ05WQkFjTQpDRU5wZEhsT1lXMWxNUlF3RWdZRFZRUUtEQXREYjIxd1lXNTVUbUZ0WlRFYk1Ca0dBMVVFQ3d3U1EyOXRjR0Z1CmVWTmxZM1JwYjI1T1lXMWxNUjB3R3dZRFZRUUREQlJEYjIxdGIyNU9ZVzFsVDNKSWIzTjBibUZ0WlRBZUZ3MHkKTXpBMU1UQXhNalV4TWpoYUZ3MHpNekExTURjeE1qVXhNamhhTUlHR01Rc3dDUVlEVlFRR0V3SllXREVTTUJBRwpBMVVFQ0F3SlUzUmhkR1ZPWVcxbE1SRXdEd1lEVlFRSERBaERhWFI1VG1GdFpURVVNQklHQTFVRUNnd0xRMjl0CmNHRnVlVTVoYldVeEd6QVpCZ05WQkFzTUVrTnZiWEJoYm5sVFpXTjBhVzl1VG1GdFpURWRNQnNHQTFVRUF3d1UKUTI5dGJXOXVUbUZ0WlU5eVNHOXpkRzVoYldVd2dnSWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUNEd0F3Z2dJSwpBb0lDQVFDM045anZpY21LcEdkMVAzcFhlQStNb2lkZE5qSjV2TTEzS3FhVGMxMjltY21ISHRJcmpHbmhvTHJOClNMV2NMR0tOQmJzQk5pNWxvcTQxc0pvZ3ltTnhVc3hRUUVyWG41RGlFbkVRZjFCcStuU0d6SWRHbWJtSlZXcUgKdDR0U0lIZENJRWF4SERIV2tJb2tsUjR0OENnTkl2aEtxbEdHSmx2TGZQVlpnV0hqczlIQSs2K0czNXRwL1pnQQp2U0EvTnV0azVQOUFMT2pseXNpeVNpWDZtYVJQayt5ck5mcHF6QTljbEllVlFRQ1RTR0hSUGVPeGcxU1NxcEhoCk11a3VZMDFKSHdvdEJVUWxOS3VEV0J3ejEzRk91YVBjNzRnMHBzTlFXZUNJaTdJWjZjUFpXSmZiQ2tSSEIwZUYKcGFPR2NueUZGYWZBZk5ZWjF3Wjg3SDZVM1U0endyMElMeFl6NkRuOEI0Zklra29ZMTlQSEVtbjR0ckhtNXdiUgpuSlZQaWVmZkFaenFLL3pjYWlCRlZEZEdRQy9UeTNLNXo5K1ZGYkl6UFdJUjVTNndlV3RiQThoM2FhL0UvdEhTCkNZV3ZpKzRSd3FQeGZXUTZKZXZ4ZFdDMDhSZ3kxSVB6N3Zxa3ZEMGJnN1JmMDF2SVVvaE8zdmJvSDdKejJLMG8KdkVHaVJkY1BRZkJ4eS9SNk80ZXN0UHBUTEU5Q1NTa2NUQVFjaWZDcG4zT1U4L3Z1UkxERXk5QXQrbWFkK0c1VgpLaVNubzhENXlnbGpPSEVRSUNaTzBjRU5GNXVWY3JOTmhRWVMwSmQ3RjllVkNTTzhIVUNzbE1xRjJlZG5GQ3BTCmZyWUlRa3NSVGdlajRiWFV4bFRGM3ZmakVabjVpZFZzSWdqMnRWUW9OMGRybjhNQVNRSURBUUFCbzFNd1VUQWQKQmdOVkhRNEVGZ1FVbERYeVAySHIrVlFNMWFIbHpjTjliRGxNYkpzd0h3WURWUjBqQkJnd0ZvQVVsRFh5UDJIcgorVlFNMWFIbHpjTjliRGxNYkpzd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRc0ZBQU9DCkFnRUFDekdyZlhWWWhmQ210RlpOMFk2TktRN2cxSFZHSVg1VjNBbDRVK29vM3krOW40S1ZHa2dWOWpoL2RaOGsKbXpXbXBYT0dSZkQxdWZSY0Q5cW9YeStHSFJ3LzBibTJ3a1ZjTXZFc25NeCtEa3VoaVZPSWMwUFlETmo4N3VLMwp2TkZCTTM5dW8xZmlKOXlkOVNLUXNqQjk0bFZNYUYxMDg5d2V2UFlsNXFtYm9rdTJxdXJYM3V3NVZ2eXNIQUF3ClUyeTB4OHkvYzNqdzlNZksxWDBHWWhTY1owcFYzeTN4dGxxckhuTmpTUmFaM3BmUGw3NGFjMGJndEU5cThKdDUKbzNEMGdmWmkwOFJrdW5ua2dKMS9QZDVpYUgvWEdnVG56NXdTRUF6VFJCWEhLWVlvRnBGV0wwcnowa0pvamZDdwpSdXQ2T21uL0w2blFXL1I1endzcXZrQUR1by9LWFkyTjY5L1J4UHcwejhFcTZZTVR0OHBXOGU1ZHpSRlFUS2N2CktUdDJCNTB1VFVEUlBRbW92dkYzRmRqcllaYkhyRlhHUXIyaGV6bDU2QkRSSkJaTzhIQ0g0NEIxc3d6K3QySWYKb3V2MlBIZDA1V3ZDVHIvM0dFL2ZiVGpabFVWeDhaYTJUWXdWakhTUzl3OWtRQ1V2dzNXM25OdVF6TklHVGJzYQpEdlQzeVN0MjJkRDQwTVlrNHpOc3o4d1YwUVYwdmJ5enorMUZUSXJlVldJSXhpMHVveWdNaHVGTDdJem4xeHNBClQ4TWVsbHQzN20wM2V2QkxwUXdPWHF0N1JXWlNaMllydVQ2clpkZ2RBOXRlK2Y3VUxhYUNGRDJxYzloRFRLOGoKemh3Mk41cGNIMVpYZGZBRjNNUWo1VmErSElDRElaL3BtTmNkVlJnWWxWN0hkOWc9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + key: "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRZ0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1N3d2dna29BZ0VBQW9JQ0FRQzNOOWp2aWNtS3BHZDEKUDNwWGVBK01vaWRkTmpKNXZNMTNLcWFUYzEyOW1jbUhIdElyakduaG9Mck5TTFdjTEdLTkJic0JOaTVsb3E0MQpzSm9neW1OeFVzeFFRRXJYbjVEaUVuRVFmMUJxK25TR3pJZEdtYm1KVldxSHQ0dFNJSGRDSUVheEhESFdrSW9rCmxSNHQ4Q2dOSXZoS3FsR0dKbHZMZlBWWmdXSGpzOUhBKzYrRzM1dHAvWmdBdlNBL051dGs1UDlBTE9qbHlzaXkKU2lYNm1hUlBrK3lyTmZwcXpBOWNsSWVWUVFDVFNHSFJQZU94ZzFTU3FwSGhNdWt1WTAxSkh3b3RCVVFsTkt1RApXQnd6MTNGT3VhUGM3NGcwcHNOUVdlQ0lpN0laNmNQWldKZmJDa1JIQjBlRnBhT0djbnlGRmFmQWZOWVoxd1o4CjdINlUzVTR6d3IwSUx4WXo2RG44QjRmSWtrb1kxOVBIRW1uNHRySG01d2JSbkpWUGllZmZBWnpxSy96Y2FpQkYKVkRkR1FDL1R5M0s1ejkrVkZiSXpQV0lSNVM2d2VXdGJBOGgzYWEvRS90SFNDWVd2aSs0UndxUHhmV1E2SmV2eApkV0MwOFJneTFJUHo3dnFrdkQwYmc3UmYwMXZJVW9oTzN2Ym9IN0p6Mkswb3ZFR2lSZGNQUWZCeHkvUjZPNGVzCnRQcFRMRTlDU1NrY1RBUWNpZkNwbjNPVTgvdnVSTERFeTlBdCttYWQrRzVWS2lTbm84RDV5Z2xqT0hFUUlDWk8KMGNFTkY1dVZjck5OaFFZUzBKZDdGOWVWQ1NPOEhVQ3NsTXFGMmVkbkZDcFNmcllJUWtzUlRnZWo0YlhVeGxURgozdmZqRVpuNWlkVnNJZ2oydFZRb04wZHJuOE1BU1FJREFRQUJBb0lDQUJKaUttUm9neDRqM2xTYm5Lc3Jtd1hOCm5GMEVLOTdlcEpBTktiOFlQNExmYkNMZ1l3Nm5EVldzQXFwSDNoOFFMZ2cvMTdKb3JSR2FGOWcvd3N0QSsyYmEKdTdEZXJwUEJpVEJCMFBIcWtGZFhqM3NhQ1FXNnRXelQ4dmN3b2F4SklTWXpwbHd0ZTh1dlg0a0pwRWhRTlRpUwpObThKZFZvY1BiQW1kdGkyL0dzME52cmgxZ3dXb2htcHJnZCs4bjRkUk5Pd0RYTnpQaUFXYjNwQ0tkcmg4U1JoCjc0aURSei9SZjBZWENoNmQ4ZENWWGVrNGlFRGVzRXp5QythWWJPQ3dhb2dJY3dVdTV0WkQyV1M1b2NUSzNHM2QKZnhWVFBHdXFBdVZzU3pUd0xWdmZ3bnlyb0xzRDVmTnBoZEhoVzQzSkxYak9BakcwWk9nZFZPT1NlQ1g0S1prbwptcWc4b3Y1TksxclVRVGQwNng2bmg0elBXdEdaTDIwbE9EMTZvRWgzUlZoRFU5akQ1U25aZ2M1Mzg2bVkxeTVJClV0QTBUUnh3MzNIcjRWT1ExaXVCZzlrNTU2MzFIcGl0clBWZ1QvLzVzYzdjMjhpQ2lBRnVFZGg3SDF5cG5NNkgKc1pjL2prRTJBOFF1NnpKSTV2NzRoYkFaTmxONVMrcDU4ajhrV2pPYTd4bnZwYk5saXc1a2JDK2Ezb25ZSVNHZApTNVZITzVEVkVaOEY3aUtUTS8zTEFZdDZScG0vdEZJOUhFZVRGWHVCVEVsYTlsb0srUWdXbjF1QVVMeGtKUHFyCkFXZ0tzL0FYYWpxVyt0d3ZUM2M5cUFDaGFTakhjV3lxem05WGh6RDRwNTg2cnpDZ3M5VUNSSStudlEvOGFIMWoKNm9CdkRqbUxpem9WUEVkR2k1VkJBb0lCQVFEM2pRZThEdVIxVjYveS9HcXRNRVcyVXptdTl2d1R4VXRBSFZtawpob25PbFo0MDVEL1B3b0s1dXBORjV2QWtxNDN1b1M2OXVuQWxxbGpraVNmOTEzU3pYZEE4dXRRYlQzVFczUmxWCkZ2dXZlN3YvcWtRZFdScm5rdXJhcWtFbnhUMnlKOGdlaG1Qcmo1STMwVHVRVXpSdmFRc2hyYkIwV3NCcGcrRmIKdWJnV29wU21hVTY5aEJpSGxwWFYrcjJRUXIrVllHT3JLUkNSWDRhMGxtbEJxbDdnemVZSTA2d2RpamszelF5cgorcVltYjRkdEFqd3huQ09CRG9qbVJBM2hGTmpVbC8wNlVvMmZzUEM1cjduWlA2cURUMUU2MU5tVDhPZ3kyUWE1Cm90Uk4wS1E3bThGRXAwUDA4aHByOTRxVlZLaHB2NzMzcGMwQkhCQXZ6VG9RdTdKcEFvSUJBUUM5ZUxZZzVSS3cKMDZQSEVmRlhMTnZ1NU12Q2I0T2NHY1BHUElLaHEyWExTbFJSSElVSkdueFB2RVRrcm93YXhDVGVVc2g0WUFtUApZUjZxKzE2aHRwN1JBZ1RWNkxvZUpnQ2hDNVRvTWlkZkR2MmxEUnZneVpMWWtsU0VIcEpaSXRWWTI4NzFWTkRaClhFQWwwOGFGcHlENDNseXdNc3UxaHYzSnRMVGJVTDNZdGtBZnBQVFJBZ0ZaaFlxbVJUdTJKeXN5dXVoWWFVbS8KeEw3WDcvYmlJZ2FPemR1YkF0QXBFTUlEbnFOdVdKOUVBQit4a1c5VUIyTFpRZjBuOTh2bG40aUowaDhsb1V5RAphWElPbWpDZFJXeGFzeVRRQkNEZUR0eXgxbHNvb1R1U2JWU1FFSHVUeUgzVno2UHZBZC9xNGxwRW5hY0UvMjE5CjRXUEh6NnArcDJMaEFvSUJBQ28yQXhhZkYzZW16eHJJemN2Z1NsTFBtQ3RzZEFsUEFBamJ1RmhrbElVRVlDaTIKcnViWFRRRXNma1pTSGFxekVnMlpzR1dycjhuTVpVSDYzVFhja2txdmVYMlJnZTl5T2dNVlNtZUc5cjJ5aEprUQp5SEtVcWhESXJZRkJ2TUJ5VXBYWlVMZGJ4UmY2c0QwU1VXekhzMDQ0QkN6bStBcXZHdFlqSmI5RlNNMmJSV3VtCjAwVmZpK3M2MHl2Y2lJeGJ4VjFNUlZKL094TCt6ZkpuSDJXU0RvR1l1bHZROUMxSlQzNWpXWUROeVowT01YSjIKQ2h1UGUwSmJYeDZjaGgxV042N3doNzUxS3k4S3RkR0QxRlhtRkVZMXRTMHA5RHZVdlZOR1RHNUZCSnlNTWlUego1eDIwdzlLMW9hbTlXUVVqbldBQzBQcTBhK04vakljS0lKZVAyZGtDZ2dFQVhPZzhKcFV0UFJnS1R5czFOSklDCnBubjZrRFV1Uy9VMlVwYUpWODA3OVJ0VmpSQjNDNmU1SFVBc2FCWlBEVER4QXpPRXFjSXQ3ZWlwcVIzcG9WSnoKUGZuSGRUelJSc2RMdDZ4K0wvMm40S3p4STJYeUxaK3FLaGhXNlJJMG9SQzduUDdyMU5EcU9DdE1LVUJYTUdKcgpnSjFJeGYyaWRqamphV3o2NGpBTlo1NjJnczNZWGtTbGRNaE8zSWxHWm1OK2d6bXpoT2JjQ3ZUbXYrd2pHMitqCjE1S0tCTkMwVWU2dHRDaXQ2d1g1MHRaY3RDMmtjWWZOcU1yNjRBWmFMUmExVlI5N3RuQUpuTWF2N3drY25ZSFYKU0FSZ0lNQmxmWDI4S2xmNkMwcEVjK0M0Zm93V2pMamJPMlM5OWd6dFI3Z0dtMjdTMzFpQTBDRWRWSFU0SFRMbgpBUUtDQVFFQXNzajBmRThqa0ZpdnpRMmxTZTBEeGhwUXFHZVF1UGROYkJKVnNBcVdmRXRKQ0h2R0JYaTZBeEIrClFnN015RE14dnVPYXQ1TVNUQ01GYzNYdVFUdEtxdlF3SGxXMXZpTnJxckNzRHVRNTVDa1ZQTUxmazk4VlFoeHkKMnpUd20zOUd5a1ZpLy9CaU5LQzRCU2ZtZlN6cGdiYlJnSEI2Ny9zcGIwejBMbXUrWG5uL0ROemxxS1pvQUZFQgpwcDdzNkVmWGdJN1Npbm94dGxuRHM4eDdFM2dlcEtENVVWbnQzM3FZbUhCOVZ3WHpUZDBaSkhrY0x1b1VpanJjCko2cENYVTcrRlpPd3pJQi9IcFFPZ2pwdklqQ0pJaHRGeFZ5Z2JqdXVjRThRS2Naaml5VXNGTHZkQzVkZTFNZVQKMXJKalFFc2laeEgrUVBSODh0dUJ5VVZHMDAwbHBBPT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=", + } + want_result: + - x: "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" diff --git a/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsersaprivatekey/test-cryptox509parsersaprivatekey-1.yaml b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsersaprivatekey/test-cryptox509parsersaprivatekey-1.yaml new file mode 100644 index 000000000000..95684ea4aae1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/cryptox509parsersaprivatekey/test-cryptox509parsersaprivatekey-1.yaml @@ -0,0 +1,34 @@ +--- +cases: + - data: + modules: + - | + package testing + + pem := "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA9D/bK4171aiTNUkrUCHKGMLSQooV+o3wdz2889h9iv0HhhBJ\nCAGU54K3duB8ofHpmYL50QodcR4RLw1vSkaI+FPdPDMyKxKj/YcmofJjz4kW+Iqw\nFbBcbMnKnEVzye+CyW9YYOTu0xWtcgen80zGp2opG0GZX86hBjjXJnjOdrJTk6x2\nNAiJIbjsQevysmj+2MyqVm8widxw0x+rGhTaCD+ZXWitN0a0WO1aaA8c/7i99I9z\nhe2peKvXzEtMaqYO9ptHcYmq2z0QWvZuJVMv5Yn0mScLWyh91R099IOtn6sNaMMs\nOyTpi7E/2IlVgI2uKGPEopKkMFV8Fl2YaAbo7wIDAQABAoIBAAyMZ08ygqU0dvOq\n4a3JPp/NCo5el8h6mFsX8eg5PCHy4/sQRSBDLIpEXfaei+iqDA1V/E2wDlksaUeY\nkhony4uui1Q3cSFjYMd6tRJm6JfV/DcisO88U1NHfsBOlSdPxdFhhhHcUSTJHVMZ\nb5iBXkdlnd0HnsCcVguCyhLw6/KPFyiA+NYRz68flxze7admyVp5C6i/HbMPq8Pr\nMilBUvOFtxuaGeJBAiavuzUe9I70dRwpe424tMvisSA8h7Xbm8BeN/PJHDV/2JrI\nURgQ563yQ5So/Qg8AgxXRkpgWM9zAh7r31PBO86vq/B4ZbON/TtWdcZVsAcVB4Pk\ntqc8JNkCgYEA+g8V+y92SETdcwUkbd5O9Fg5CkfdsALsBXVH6FunrCUV5HS9l5o6\nMMBbJ/08odW/bP5BmOa4A/Hbk9uG/UfQn2KQ3HCgPlxUEwQO07R1/FcQOe4xmyG6\nJpDgQ30viE1RtlCkceQWUeitCIqZsYu0i8sLZLWJH+V/07OB4G17ELMCgYEA+g1v\nhrlAFNhZvrIX/zcP3xF2pZ+AqkFXdL/tWQZkWAVToONn/LlXTH71C/TO2x+OaQRm\nqX1bA9Zhyjf1gYQN9RenjUswvggk0aY2Tk28wUqowMGSsjQHmZ20EphHNMWNJpdS\nfKFfrQIFKCnLlpQVNz+j3bLWZUnq+jPaYnJP7NUCgYEA48qcVo7c7Ga3aNEVZ3St\nbg90HrZq760pvqshDz13V+0MrWnfUFxxh/mi0KHy+uYRlMNllFkQ5p8LTP0dUlt6\nY8dReU6r20MWX6BBtX9eP7o8ENm4nL4zqnAtq609gKgWuMNrmkiSQJl6Dx7bdY5z\nsSkNPvfUa5cQRBTxSjXRdtsCgYBHrzpdwRXh4/Q2ew/uFnbyWCtPZ96W8IyF58+/\nSdnSchR7dzYEeY3RXEQb3V6/6tgEu0JDLLC+9OKr+kbjjlwB+3oJQ5kBoYwMnj3L\nTPXj4+dk+xl3BPt4yoEpI4amVkwU2CTJnemzy3R3AyReUq2SXSg5El/sQbifaeYd\neu/20QKBgH/5IZHGBKiRAe1ww2FzOpDtL8VXXTe3EAXKutfajrHTqPz9+lXknX/D\nUMosh264nYXYS29WqxhJVutbE9u8e0VpuY1qIN9/3R0WKfTLTMUFlZtbqTepvsy1\nW2UbK732I4Nfp0/mtUvOSdMZO8dxbSdEeMnw/Ec8QgxK9a1rRu9+\n-----END RSA PRIVATE KEY-----" + + p := crypto.x509.parse_rsa_private_key(pem) + note: cryptox509parsersaprivatekey/valid + query: data.testing.p = x + want_result: + - x: + "d": "DIxnTzKCpTR286rhrck-n80Kjl6XyHqYWxfx6Dk8IfLj-xBFIEMsikRd9p6L6KoMDVX8TbAOWSxpR5iSGifLi66LVDdxIWNgx3q1Embol9X8NyKw7zxTU0d-wE6VJ0_F0WGGEdxRJMkdUxlvmIFeR2Wd3QeewJxWC4LKEvDr8o8XKID41hHPrx-XHN7tp2bJWnkLqL8dsw-rw-syKUFS84W3G5oZ4kECJq-7NR70jvR1HCl7jbi0y-KxIDyHtdubwF4388kcNX_YmshRGBDnrfJDlKj9CDwCDFdGSmBYz3MCHuvfU8E7zq-r8Hhls439O1Z1xlWwBxUHg-S2pzwk2Q" + "dp": "48qcVo7c7Ga3aNEVZ3Stbg90HrZq760pvqshDz13V-0MrWnfUFxxh_mi0KHy-uYRlMNllFkQ5p8LTP0dUlt6Y8dReU6r20MWX6BBtX9eP7o8ENm4nL4zqnAtq609gKgWuMNrmkiSQJl6Dx7bdY5zsSkNPvfUa5cQRBTxSjXRdts" + "dq": "R686XcEV4eP0NnsP7hZ28lgrT2felvCMhefPv0nZ0nIUe3c2BHmN0VxEG91ev-rYBLtCQyywvvTiq_pG445cAft6CUOZAaGMDJ49y0z14-PnZPsZdwT7eMqBKSOGplZMFNgkyZ3ps8t0dwMkXlKtkl0oORJf7EG4n2nmHXrv9tE" + "e": "AQAB" + "kty": "RSA" + "n": "9D_bK4171aiTNUkrUCHKGMLSQooV-o3wdz2889h9iv0HhhBJCAGU54K3duB8ofHpmYL50QodcR4RLw1vSkaI-FPdPDMyKxKj_YcmofJjz4kW-IqwFbBcbMnKnEVzye-CyW9YYOTu0xWtcgen80zGp2opG0GZX86hBjjXJnjOdrJTk6x2NAiJIbjsQevysmj-2MyqVm8widxw0x-rGhTaCD-ZXWitN0a0WO1aaA8c_7i99I9zhe2peKvXzEtMaqYO9ptHcYmq2z0QWvZuJVMv5Yn0mScLWyh91R099IOtn6sNaMMsOyTpi7E_2IlVgI2uKGPEopKkMFV8Fl2YaAbo7w" + "p": "-g8V-y92SETdcwUkbd5O9Fg5CkfdsALsBXVH6FunrCUV5HS9l5o6MMBbJ_08odW_bP5BmOa4A_Hbk9uG_UfQn2KQ3HCgPlxUEwQO07R1_FcQOe4xmyG6JpDgQ30viE1RtlCkceQWUeitCIqZsYu0i8sLZLWJH-V_07OB4G17ELM" + "q": "-g1vhrlAFNhZvrIX_zcP3xF2pZ-AqkFXdL_tWQZkWAVToONn_LlXTH71C_TO2x-OaQRmqX1bA9Zhyjf1gYQN9RenjUswvggk0aY2Tk28wUqowMGSsjQHmZ20EphHNMWNJpdSfKFfrQIFKCnLlpQVNz-j3bLWZUnq-jPaYnJP7NU" + "qi": "f_khkcYEqJEB7XDDYXM6kO0vxVddN7cQBcq619qOsdOo_P36VeSdf8NQyiyHbridhdhLb1arGElW61sT27x7RWm5jWog33_dHRYp9MtMxQWVm1upN6m-zLVbZRsrvfYjg1-nT-a1S85J0xk7x3FtJ0R4yfD8RzxCDEr1rWtG734" + - data: + modules: + - | + package testing + + p := crypto.x509.parse_rsa_private_key("invalid") + note: cryptox509parsersaprivatekey/invalid + query: data.testing.p = x + want_error: illegal base64 + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/dataderef/test-data-derefs.yaml b/third_party/opa/v1/test/cases/testdata/v0/dataderef/test-data-derefs.yaml new file mode 100644 index 000000000000..a73a3f1f66b0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/dataderef/test-data-derefs.yaml @@ -0,0 +1,35 @@ +--- +cases: + - data: + "2": bar + modules: + - | + package test + p := data[2] + note: data/toplevel integer + query: data.test.p = x + want_result: + - x: bar + - data: + nested: + "2": bar + modules: + - | + package test + p := data.nested[2] + note: data/nested integer + query: data.test.p = x + want_result: + - x: bar + - data: + nested: + "2": bar + modules: + - | + package test + p := obj[2] { + obj := data.nested + } + note: "data/negative case: nested integer" + query: data.test.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-default-functions.yaml b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-default-functions.yaml new file mode 100644 index 000000000000..34433df8abb5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-default-functions.yaml @@ -0,0 +1,40 @@ +--- +cases: + - note: defaultkeyword/function with var arg + modules: + - | + package test + + default f(_) := 100 + query: data.test = x + want_result: + - x: {} + - note: defaultkeyword/function with var arg, ref head + modules: + - | + package test + + default p.q.r.f(x) := 100 + query: data.test = x + want_result: + - x: + p: + q: + r: {} + - note: defaultkeyword/function with var arg, ref head query + modules: + - | + package test + + default p.q.r.f(x) := 100 + + p.q.r.f(x) = x { + x == 2 + } + + foo { + p.q.r.f(3) == 100 + } + query: data.test.foo = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0804.yaml b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0804.yaml new file mode 100644 index 000000000000..eb62a53b9e5c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0804.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = 1 { + false + } + + default p = 0 + + p = 2 { + false + } + note: defaultkeyword/undefined + query: data.generated.p = x + want_result: + - x: 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0805.yaml b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0805.yaml new file mode 100644 index 000000000000..d65e2688d805 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0805.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + default p = 0 + + p = 1 + + p = 2 { + false + } + note: defaultkeyword/defined + query: data.generated.p = x + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0806.yaml b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0806.yaml new file mode 100644 index 000000000000..94ca0304167d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0806.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = 1 + + default p = 0 + + p = 2 { + false + } + note: defaultkeyword/defined-ooo + query: data.generated.p = x + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0807.yaml b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0807.yaml new file mode 100644 index 000000000000..43dfcc935150 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0807.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = 1 { + false + } + + default p = [x | data.a[_] = x] + note: defaultkeyword/array comprehension + query: data.generated.p = x + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0808.yaml b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0808.yaml new file mode 100644 index 000000000000..cf47977fd7ce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0808.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + d: + e: + - bar + - baz + modules: + - | + package generated + + p = 1 { + false + } + + default p = {x: k | data.d[k][_] = x} + note: defaultkeyword/object comprehension + query: data.generated.p = x + want_result: + - x: + bar: e + baz: e diff --git a/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0809.yaml b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0809.yaml new file mode 100644 index 000000000000..4765d9b56d1f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/defaultkeyword/test-defaultkeyword-0809.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = 1 { + false + } + + default p = {x | data.a[_] = x} + note: defaultkeyword/set comprehension + query: data.generated.p = x + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0763.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0763.yaml new file mode 100644 index 000000000000..2f692380ef03 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0763.yaml @@ -0,0 +1,33 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[x] { + data.a[i] = x + } + + p[y] { + data.b[j] = y + } + note: "disjunction/incr: query set" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + - goodbye + - hello diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0764.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0764.yaml new file mode 100644 index 000000000000..ed8f7792aa02 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0764.yaml @@ -0,0 +1,27 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[100] + + p[x] { + data.a[x] + } + note: "disjunction/incr: query set constants" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 0 + - 1 + - 2 + - 3 + - 100 diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0765.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0765.yaml new file mode 100644 index 000000000000..c331b13dec2c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0765.yaml @@ -0,0 +1,48 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + g: + a: + - 1 + - 0 + - 0 + - 0 + b: + - 0 + - 2 + - 0 + - 0 + c: + - 0 + - 0 + - 0 + - 4 + modules: + - | + package generated + + p[k] = v { + data.b[v] = k + } + + p[k] = v { + data.a[i] = v + data.g[k][j] = v + } + note: "disjunction/incr: query object" + query: data.generated.p = x + want_result: + - x: + a: 1 + b: 2 + c: 4 + goodbye: v2 + hello: v1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0766.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0766.yaml new file mode 100644 index 000000000000..b0287f635a0b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0766.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p["a"] = 1 + + p["b"] = 2 + note: "disjunction/incr: query object constant key" + query: data.generated.p = x + want_result: + - x: + a: 1 + b: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0767.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0767.yaml new file mode 100644 index 000000000000..66003c39c954 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0767.yaml @@ -0,0 +1,37 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[x] { + data.generated.q[x] + } + + q[x] { + data.a[i] = x + } + + q[y] { + data.b[j] = y + } + note: "disjunction/incr: iter set" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + - goodbye + - hello diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0768.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0768.yaml new file mode 100644 index 000000000000..6b91c8844b95 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0768.yaml @@ -0,0 +1,38 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[x] { + data.generated.q = s + s[x] + } + + q[x] { + data.a[_0] = x + } + + q[y] { + data.b[_0] = y + } + note: "disjunction/incr: eval set" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + - goodbye + - hello diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0769.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0769.yaml new file mode 100644 index 000000000000..8c688b253926 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0769.yaml @@ -0,0 +1,52 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + g: + a: + - 1 + - 0 + - 0 + - 0 + b: + - 0 + - 2 + - 0 + - 0 + c: + - 0 + - 0 + - 0 + - 4 + modules: + - | + package generated + + p[k] = v { + data.generated.q[k] = v + } + + q[k] = v { + data.b[v] = k + } + + q[k] = v { + data.a[i] = v + data.g[k][j] = v + } + note: "disjunction/incr: eval object" + query: data.generated.p = x + want_result: + - x: + a: 1 + b: 2 + c: 4 + goodbye: v2 + hello: v1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0770.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0770.yaml new file mode 100644 index 000000000000..15ecbd1c1163 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0770.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[k] = v { + data.generated.q[k] = v + } + + q["a"] = 1 + + q["b"] = 2 + note: "disjunction/incr: eval object constant key" + query: data.generated.p = x + want_result: + - x: + a: 1 + b: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0771.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0771.yaml new file mode 100644 index 000000000000..4cbda39489cb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0771.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + false + } + + p { + false + } + note: "disjunction/complete: undefined" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0772.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0772.yaml new file mode 100644 index 000000000000..f6da5d0fb4d3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0772.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = true + + p = false { + false + } + + p = false + note: "disjunction/complete: error" + query: data.generated.p = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0773.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0773.yaml new file mode 100644 index 000000000000..b0e31418191c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0773.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = true + + p = true + note: "disjunction/complete: valid" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0774.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0774.yaml new file mode 100644 index 000000000000..01b18ac9c379 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0774.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = true + + p = false { + false + } + note: "disjunction/complete: valid-2" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0775.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0775.yaml new file mode 100644 index 000000000000..6b379e794a10 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0775.yaml @@ -0,0 +1,211 @@ +--- +cases: + - data: + modules: + - | + package generated + + p { + data.generated.q + } + + q = true + + q = false + note: "disjunction/complete: reference error" + query: data.generated.p = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error + - data: + modules: + - | + package generated + + p { + data.generated.q + } + + q = true { + false + } + else = true { + true + } + + q = true { + false + } + else = false { + true + } + note: "disjunction/complete: nested conflict, else" + query: data.generated.p = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error + - data: + modules: + - | + package test + + p { + x := data.test.q(1) + x == true + } + + q(_) = true + + q(_) = false + note: "disjunction/nested function with conflict" + query: data.test.p = x + want_error: functions must not produce multiple outputs for same inputs + want_error_code: eval_conflict_error + - data: + modules: + - | + package test + + p { + x := data.test.q(1) + } + + q(_) = true { + false + } + else = true { + true + } + + q(_) = true { + false + } + else = false { + true + } + note: "disjunction/nested function with conflict, else" + query: data.test.p = x + want_error: functions must not produce multiple outputs for same inputs + want_error_code: eval_conflict_error + - data: + modules: + - | + package test + + p { + q + } + xs = {1, 2} + q = xs[_] + note: "disjunction/complete: conflict involving early-exit complete rule (set enumeration)" + query: data.test.p = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error + - data: + modules: + - | + package test + + p { + q + } + xs = [1, 2] + q = xs[_] + note: "disjunction/complete: conflict involving early-exit complete rule (array enumeration)" + query: data.test.p = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error + - data: + modules: + - | + package test + + p { + q + } + xs = {"a": 1, "b": 2} + q = xs[_] + note: "disjunction/complete: conflict involving early-exit complete rule (object enumeration)" + query: data.test.p = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error + + - data: + modules: + - | + package test + + p { + q + } + xs = {1, 2} + q[y] := x { + x := xs[_] + y := 1 + } + note: "disjunction/complete: conflict involving early-exit partial rule" + query: data.test.p = x + want_error: object keys must be unique + want_error_code: eval_conflict_error + + - data: + modules: + - | + package test + + p { + q + } + xs = {1, 2} + q = false { + false + } else = xs[_] + note: "disjunction/complete: conflict involving early-exit complete rule, else" + query: data.test.p = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error + + - data: + modules: + - | + package test + + p { + q + } + xs = {1, 2} + q = xs[_] + note: "disjunction/complete: conflict involving early-exit complete rule, multiple" + query: data.test.p = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error + + - note: "disjunction/complete: conflict involving early-exit complete rule, data array enumeration" + data: + arr: [1, 2] + modules: + - | + package test + + p { + q + } + q := data.arr[_] + query: data.test.p = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error + + - note: "disjunction/complete: conflict involving early-exit complete rule, data object enumeration" + data: + obj: + a: 1 + b: 2 + modules: + - | + package test + + p { + q + } + q := data.obj[_] + query: data.test.p = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0776.yaml b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0776.yaml new file mode 100644 index 000000000000..6bcd8b44c081 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/disjunction/test-disjunction-0776.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q + } + + q = true + + q = true + note: "disjunction/complete: reference valid" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1054.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1054.yaml new file mode 100644 index 000000000000..eac0dedd05f6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1054.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + no_op { + true + } else = false { + true + } + note: elsekeyword/no-op + query: data.ex.no_op = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1055.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1055.yaml new file mode 100644 index 000000000000..7b41fd102d36 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1055.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + bool { + false + } else = true { + true + } + note: elsekeyword/trivial + query: data.ex.bool = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1056.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1056.yaml new file mode 100644 index 000000000000..9f88ef5b0f9c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1056.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + non_bool = null { + false + } else = [100] { + true + } + note: elsekeyword/trivial-non-bool + query: data.ex.non_bool = x + want_result: + - x: + - 100 diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1057.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1057.yaml new file mode 100644 index 000000000000..4258a4f293f6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1057.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + triple { + false + } else { + false + } else = "hello" { + true + } + note: elsekeyword/trivial-3 + query: data.ex.triple = x + want_result: + - x: hello diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1058.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1058.yaml new file mode 100644 index 000000000000..d9c506bc79d3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1058.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + modules: + - | + package ex + + vars { + false + } else = ["hello", x] { + data.b.v2 = x + } + note: elsekeyword/var-head + query: data.ex.vars = x + want_result: + - x: + - hello + - goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1059.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1059.yaml new file mode 100644 index 000000000000..3d27bd6939b8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1059.yaml @@ -0,0 +1,28 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + modules: + - | + package ex + + refs { + false + } else = __local6__ { + true + __local7__ = data.b.v2 + __local6__ = ["hello", __local7__] + } + note: elsekeyword/ref-head + query: data.ex.refs = x + want_result: + - x: + - hello + - goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1060.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1060.yaml new file mode 100644 index 000000000000..0d9d046b12c5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1060.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + multiple_defined = false { + false + } else { + true + } else = false { + true + } + note: elsekeyword/first-match + query: data.ex.multiple_defined = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1061.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1061.yaml new file mode 100644 index 000000000000..ef7c0e5b4fa3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1061.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + default default_1 = 1 + + default_1 { + false + } + + default_1 = 2 + note: elsekeyword/default-1 + query: data.ex.default_1 = x + want_result: + - x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1062.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1062.yaml new file mode 100644 index 000000000000..9c1f1bd4035f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1062.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + default default_2 = 2 + + default_2 { + false + } + + default_2 = 1 { + false + } + note: elsekeyword/default-2 + query: data.ex.default_2 = x + want_result: + - x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1063.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1063.yaml new file mode 100644 index 000000000000..84f0167b538c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1063.yaml @@ -0,0 +1,28 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + multiple_roots { + false + } else = 1 { + false + } else = 2 { + true + } else = 3 { + true + } + + multiple_roots = 2 + + multiple_roots = 3 { + false + } else = 2 { + true + } + note: elsekeyword/multiple-roots + query: data.ex.multiple_roots = x + want_result: + - x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1064.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1064.yaml new file mode 100644 index 000000000000..4ee575644e4c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1064.yaml @@ -0,0 +1,34 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package ex + + indexed { + data.a[0] = 0 + } else = 2 { + data.a[0] = 1 + } else = 3 { + data.a[0] = 1 + } + + indexed { + data.a[0] = 1 + data.a[2] = 2 + } else { + false + } else = 2 { + data.a[0] = x + x = 1 + data.a[2] = 3 + } + note: elsekeyword/indexed + query: data.ex.indexed = x + want_result: + - x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1065.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1065.yaml new file mode 100644 index 000000000000..510c16f1be98 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1065.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + conflict_1 { + false + } else = true { + true + } + + conflict_1 = false + note: elsekeyword/conflict-1 + query: data.ex.conflict_1 = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1066.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1066.yaml new file mode 100644 index 000000000000..18ad2cb82bcd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1066.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + modules: + - | + package ex + + conflict_2 { + false + } + else = false { + true + } + + conflict_2 { + false + } + else = true { + true + } + note: elsekeyword/conflict-2 + query: data.ex.conflict_2 = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error + # TODO(sr) strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1067.yaml b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1067.yaml new file mode 100644 index 000000000000..ac0fa9b9659c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/elsekeyword/test-elsekeyword-1067.yaml @@ -0,0 +1,27 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + fn_result = [x, y, z] { + data.ex.fn(101, true, x) + data.ex.fn(100, true, y) + data.ex.fn(100, false, z) + } + + fn(x, y) = "large" { + x > 100 + } else = "small" { + y = true + } else = "medium" { + true + } + note: elsekeyword/functions + query: data.ex.fn_result = x + want_result: + - x: + - large + - small + - medium diff --git a/third_party/opa/v1/test/cases/testdata/v0/embeddedvirtualdoc/test-embeddedvirtualdoc-0976.yaml b/third_party/opa/v1/test/cases/testdata/v0/embeddedvirtualdoc/test-embeddedvirtualdoc-0976.yaml new file mode 100644 index 000000000000..7d8cc72a85ae --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/embeddedvirtualdoc/test-embeddedvirtualdoc-0976.yaml @@ -0,0 +1,45 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + g: + a: + - 1 + - 0 + - 0 + - 0 + b: + - 0 + - 2 + - 0 + - 0 + c: + - 0 + - 0 + - 0 + - 4 + input_term: "{}" + modules: + - | + package b.c.d + + p[x] { + data.a[i] = x + data.b.c.d.q[x] + } + + q[x] { + data.g[j][k] = x + } + note: embeddedvirtualdoc/deep embedded vdoc + query: data.b.c.d.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0545.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0545.yaml new file mode 100644 index 000000000000..c78d0188260a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0545.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + true = false + } + note: "eqexpr/undefined: same type" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0546.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0546.yaml new file mode 100644 index 000000000000..3caa66c12cd4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0546.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + [1, 2, 3] = [1, 3, 2] + } + note: "eqexpr/undefined: array order" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0547.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0547.yaml new file mode 100644 index 000000000000..4c237455f983 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0547.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.a[3] = 9999 + } + note: "eqexpr/undefined: ref value" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0548.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0548.yaml new file mode 100644 index 000000000000..3ba7bb4bb2d4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0548.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.a[i] = 9999 + } + note: "eqexpr/undefined: ref values" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0549.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0549.yaml new file mode 100644 index 000000000000..403dc3bd162c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0549.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.a[3] = x + x = 3 + } + note: "eqexpr/undefined: ground var" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0550.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0550.yaml new file mode 100644 index 000000000000..4e87cb0c643c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0550.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + [1, x, x] = [1, 2, 3] + } + note: "eqexpr/undefined: array var 1" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0551.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0551.yaml new file mode 100644 index 000000000000..6d0e2ab8e756 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0551.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + [1, x, 3] = [1, 2, x] + } + note: "eqexpr/undefined: array var 2" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0552.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0552.yaml new file mode 100644 index 000000000000..bbc690d51c3c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0552.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + __local0__ = data.a + __local1__ = data.a + {"a": 1, "b": 2} = {"a": __local0__, "b": __local1__} + } + note: "eqexpr/undefined: object var 1" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0553.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0553.yaml new file mode 100644 index 000000000000..f7a0f0d4377d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0553.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + [[1, x], [3, x]] = [[1, 2], [3, 4]] + } + note: "eqexpr/undefined: array deep var 1" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0554.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0554.yaml new file mode 100644 index 000000000000..fdddbc253a88 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0554.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + [[1, x], [3, 4]] = [[1, 2], [x, 4]] + } + note: "eqexpr/undefined: array deep var 2" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0555.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0555.yaml new file mode 100644 index 000000000000..84c178961147 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0555.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + {1, 2, 3} = {1, 2, 4} + } + note: "eqexpr/undefined: set" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0556.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0556.yaml new file mode 100644 index 000000000000..fba0c1347600 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0556.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + true = true + } + note: "eqexpr/ground: bool" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0557.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0557.yaml new file mode 100644 index 000000000000..4604976eb6d7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0557.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + "string" = "string" + } + note: "eqexpr/ground: string" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0558.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0558.yaml new file mode 100644 index 000000000000..0af8cb0e35a0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0558.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + 17 = 17 + } + note: "eqexpr/ground: number" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0559.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0559.yaml new file mode 100644 index 000000000000..bfc505a2c26f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0559.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + null = null + } + note: "eqexpr/ground: null" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0560.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0560.yaml new file mode 100644 index 000000000000..b7202e40466c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0560.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + [1, 2, 3] = [1, 2, 3] + } + note: "eqexpr/ground: array" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0561.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0561.yaml new file mode 100644 index 000000000000..ffb64925f45e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0561.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + {1, 2, 3} = {1, 2, 3} + } + note: "eqexpr/ground: set" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0562.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0562.yaml new file mode 100644 index 000000000000..f47aebfa6439 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0562.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + {"a": [1, 2, 3], "b": false} = {"a": [1, 2, 3], "b": false} + } + note: "eqexpr/ground: object" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0563.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0563.yaml new file mode 100644 index 000000000000..fa9ec016b61e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0563.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.a[2] = 3 + } + note: "eqexpr/ground: ref 1" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0564.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0564.yaml new file mode 100644 index 000000000000..89585ea41703 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0564.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: + b: + v2: goodbye + modules: + - | + package generated + + p { + data.b.v2 = "goodbye" + } + note: "eqexpr/ground: ref 2" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0565.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0565.yaml new file mode 100644 index 000000000000..849f13857ea5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0565.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + d: + e: + - bar + - baz + modules: + - | + package generated + + p { + data.d.e = ["bar", "baz"] + } + note: "eqexpr/ground: ref 3" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0566.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0566.yaml new file mode 100644 index 000000000000..5c745009d5d3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0566.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p { + data.c[0].x[1] = data.c[0].z.q + } + note: "eqexpr/ground: ref 4" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0567.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0567.yaml new file mode 100644 index 000000000000..e4cb094e7446 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0567.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + z = 42 + y = z + x = y + } + note: "eqexpr/var: x=y=z" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 42 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0568.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0568.yaml new file mode 100644 index 000000000000..8c8fa3494257 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0568.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.a[3] = x + x = 4 + } + note: "eqexpr/var: ref value" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0569.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0569.yaml new file mode 100644 index 000000000000..9dcf251818ee --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0569.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.a[i] = x + x = 2 + } + note: "eqexpr/var: ref values" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0570.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0570.yaml new file mode 100644 index 000000000000..bef367e94d36 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0570.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.a[i] = 4 + x = 3 + } + note: "eqexpr/var: ref key" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0571.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0571.yaml new file mode 100644 index 000000000000..084142379eff --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0571.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.a[i] = x + i = 2 + } + note: "eqexpr/var: ref keys" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0572.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0572.yaml new file mode 100644 index 000000000000..f8a2f370801b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0572.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + i = 2 + data.a[i] = x + } + note: "eqexpr/var: ref ground var" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0573.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0573.yaml new file mode 100644 index 000000000000..9160fa7fc2a8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0573.yaml @@ -0,0 +1,31 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p[x] { + data.c[0].x[i] = data.c[0].z[j] + x = [i, j] + } + note: "eqexpr/var: ref ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 0 + - p + - - 1 + - q diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0574.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0574.yaml new file mode 100644 index 000000000000..434a60d7f86e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0574.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + [1, x, 3] = [1, 2, 3] + } + note: "eqexpr/pattern: array" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0575.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0575.yaml new file mode 100644 index 000000000000..ba1c38c3f3b4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0575.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + [[1, x], [3, 4]] = [[1, 2], [3, 4]] + } + note: "eqexpr/pattern: array 2" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0576.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0576.yaml new file mode 100644 index 000000000000..cd4f443e9e6d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0576.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + [2, x, 3] = [x, 2, 3] + } + note: "eqexpr/pattern: array same var" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0577.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0577.yaml new file mode 100644 index 000000000000..9a983e8fbdf8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0577.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[z] { + [1, x, y] = [1, 2, 3] + z = [x, y] + } + note: "eqexpr/pattern: array multiple vars" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0578.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0578.yaml new file mode 100644 index 000000000000..74b94f35c173 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0578.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[z] { + [1, x, 3] = [y, 2, 3] + z = [x, y] + } + note: "eqexpr/pattern: array multiple vars 2" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 2 + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0579.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0579.yaml new file mode 100644 index 000000000000..4d85c28259fc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0579.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.a[0] + __local1__ = data.a[1] + __local2__ = data.a[2] + __local3__ = data.a[3] + [1, 2, 3, x] = [__local0__, __local1__, __local2__, __local3__] + } + note: "eqexpr/pattern: array ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0580.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0580.yaml new file mode 100644 index 000000000000..f02ba8eb8547 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0580.yaml @@ -0,0 +1,28 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.a[0] + __local1__ = data.a[1] + __local2__ = data.a[2] + __local3__ = data.a[i] + [1, 2, 3, x] = [__local0__, __local1__, __local2__, __local3__] + } + note: "eqexpr/pattern: array non-ground ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0581.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0581.yaml new file mode 100644 index 000000000000..537a0f72b10d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0581.yaml @@ -0,0 +1,27 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p[x] { + [true, false, x] = data.c[i][j] + } + note: "eqexpr/pattern: array = ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - foo diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0582.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0582.yaml new file mode 100644 index 000000000000..da2c23f3f0c9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0582.yaml @@ -0,0 +1,27 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p[x] { + data.c[i][j] = [true, false, x] + } + note: "eqexpr/pattern: array = ref (reversed)" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - foo diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0583.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0583.yaml new file mode 100644 index 000000000000..73d7bf958b9f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0583.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[y] { + x = 3 + [1, 2, x] = y + } + note: "eqexpr/pattern: array = var" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0584.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0584.yaml new file mode 100644 index 000000000000..f5576b9f97b7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0584.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[y] { + {"x": y} = {"x": "y"} + } + note: "eqexpr/pattern: object val" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - "y" diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0585.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0585.yaml new file mode 100644 index 000000000000..e25be3e36d57 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0585.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + {"x": x, "y": x} = {"x": 1, "y": 1} + } + note: "eqexpr/pattern: object same var" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0586.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0586.yaml new file mode 100644 index 000000000000..45df7ab32b94 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0586.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[z] { + {"x": x, "y": y} = {"x": 1, "y": 2} + z = [x, y] + } + note: "eqexpr/pattern: object multiple vars" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0587.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0587.yaml new file mode 100644 index 000000000000..a7d1007e0492 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0587.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[z] { + {"x": x, "y": 2} = {"x": 1, "y": y} + z = [x, y] + } + note: "eqexpr/pattern: object multiple vars 2" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0588.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0588.yaml new file mode 100644 index 000000000000..6d10a703e9c6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0588.yaml @@ -0,0 +1,28 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p[x] { + __local0__ = data.c[0].x[0] + {"p": __local0__, "q": x} = data.c[i][j] + } + note: "eqexpr/pattern: object ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0589.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0589.yaml new file mode 100644 index 000000000000..3f9efdbe11b1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0589.yaml @@ -0,0 +1,30 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p[x] { + __local0__ = data.c[0].x[i] + {"a": 1, "b": x} = {"a": 1, "b": __local0__} + } + note: "eqexpr/pattern: object non-ground ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false + - true + - foo diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0590.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0590.yaml new file mode 100644 index 000000000000..ac5a24505f5f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0590.yaml @@ -0,0 +1,31 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p[x] { + {"p": y, "q": z} = data.c[i][j] + x = [i, j, y, z] + } + note: "eqexpr/pattern: object = ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 0 + - z + - true + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0591.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0591.yaml new file mode 100644 index 000000000000..d068c971e73b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0591.yaml @@ -0,0 +1,31 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p[x] { + data.c[i][j] = {"p": y, "q": z} + x = [i, j, y, z] + } + note: "eqexpr/pattern: object = ref (reversed)" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 0 + - z + - true + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0592.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0592.yaml new file mode 100644 index 000000000000..17538a8cb2e8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0592.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + y = 2 + {"a": 1, "b": y} = x + } + note: "eqexpr/pattern: object = var" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - a: 1 + b: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0593.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0593.yaml new file mode 100644 index 000000000000..a9f7fdf03c61 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0593.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + f: + - xs: + - 1 + ys: + - 2 + - xs: + - 2 + ys: + - 3 + modules: + - | + package generated + + p[ys] { + data.f[i] = {"xs": [2], "ys": ys} + } + note: "eqexpr/pattern: object/array nested" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0594.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0594.yaml new file mode 100644 index 000000000000..db0b05d05240 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0594.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: + f: + - xs: + - 1 + ys: + - 2 + - xs: + - 2 + ys: + - 3 + modules: + - | + package generated + + p[v] { + data.f[i] = {"xs": [x], "ys": [y]} + v = [x, y] + } + note: "eqexpr/pattern: object/array nested 2" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 1 + - 2 + - - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0595.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0595.yaml new file mode 100644 index 000000000000..6a742de34c24 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0595.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + x = 2 + {1, 3, x} = {1, 2, 3} + } + note: "eqexpr/unordered: sets" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0596.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0596.yaml new file mode 100644 index 000000000000..6fb6537d5b32 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0596.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: + modules: + - | + package generated + + p[x] { + x = "a" + {x: 1} = {"a": 1} + } + note: "eqexpr/unordered: object keys" + query: data.generated.p = x + want_result: + - x: + - a diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0597.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0597.yaml new file mode 100644 index 000000000000..31ac5a9877bc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0597.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + x = "a" + {"a": 1} = {x: 1} + } + note: "eqexpr/unordered: object keys (reverse)" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - a diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0598.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0598.yaml new file mode 100644 index 000000000000..baec9a2409a5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0598.yaml @@ -0,0 +1,34 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + g: + a: + - 1 + - 0 + - 0 + - 0 + b: + - 0 + - 2 + - 0 + - 0 + c: + - 0 + - 0 + - 0 + - 4 + modules: + - | + package generated + + p { + data.a[i] = data.g[i][j] + } + note: "eqexpr/indexing: intersection" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0599.yaml b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0599.yaml new file mode 100644 index 000000000000..72b51dda32a5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/eqexpr/test-eqexpr-0599.yaml @@ -0,0 +1,20 @@ +--- +cases: + - input_term: "1.0" + modules: + - | + package test + p { input == 1.0 } + note: "eqexpr/indexing: input is 1.0" + query: data.test.p = x + want_result: + - x: true + - input_term: "1" + modules: + - | + package test + p { input == 1.0 } + note: "eqexpr/indexing: input is 1" + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0525.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0525.yaml new file mode 100644 index 000000000000..095048effe68 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0525.yaml @@ -0,0 +1,12 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = true + note: evaltermexpr/true + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0526.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0526.yaml new file mode 100644 index 000000000000..ec784ba31f40 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0526.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + false + } + note: evaltermexpr/false + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0527.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0527.yaml new file mode 100644 index 000000000000..dad32781e2fe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0527.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + -3.14 + } + note: evaltermexpr/number non-zero + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0528.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0528.yaml new file mode 100644 index 000000000000..1d0e95005962 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0528.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + null + } + note: evaltermexpr/number zero + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0529.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0529.yaml new file mode 100644 index 000000000000..4e82e838e042 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0529.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + null + } + note: evaltermexpr/null + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0530.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0530.yaml new file mode 100644 index 000000000000..fb9ea30cba6e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0530.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + "abc" + } + note: evaltermexpr/string non-empty + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0531.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0531.yaml new file mode 100644 index 000000000000..d9c91ba910da --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0531.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + "" + } + note: evaltermexpr/string empty + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0532.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0532.yaml new file mode 100644 index 000000000000..fbb62ea48b02 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0532.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + [1, 2, 3] + } + note: evaltermexpr/array non-empty + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0533.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0533.yaml new file mode 100644 index 000000000000..103479786e59 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0533.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + [] + } + note: evaltermexpr/array empty + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0534.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0534.yaml new file mode 100644 index 000000000000..6c686f283168 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0534.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + {"a": 1} + } + note: evaltermexpr/object non-empty + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0535.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0535.yaml new file mode 100644 index 000000000000..0ac7bf97df76 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0535.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + {} + } + note: evaltermexpr/object empty + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0536.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0536.yaml new file mode 100644 index 000000000000..079eac4d9839 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0536.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + {1, 2, 3} + } + note: evaltermexpr/set non-empty + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0537.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0537.yaml new file mode 100644 index 000000000000..325698d0c3e8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0537.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + set() + } + note: evaltermexpr/set empty + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0538.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0538.yaml new file mode 100644 index 000000000000..a1bc5c8de7ee --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0538.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.a[i] + } + note: evaltermexpr/ref + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0539.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0539.yaml new file mode 100644 index 000000000000..478642cc7644 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0539.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.deadbeef[i] + } + note: evaltermexpr/ref undefined + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0540.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0540.yaml new file mode 100644 index 000000000000..cc42d1186757 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0540.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.a[true] + } + note: evaltermexpr/ref undefined (path) + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0541.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0541.yaml new file mode 100644 index 000000000000..2c5aabc8e3ac --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0541.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p { + data.c[0].x[1] + } + note: evaltermexpr/ref false + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0542.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0542.yaml new file mode 100644 index 000000000000..55fe5b53c69d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0542.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + [x | x = 1] + } + note: evaltermexpr/array comprehension + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0543.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0543.yaml new file mode 100644 index 000000000000..f2f272355800 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0543.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + [x | x = 1; x = 2] + } + note: evaltermexpr/array comprehension empty + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0544.yaml b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0544.yaml new file mode 100644 index 000000000000..b57f4241447b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/evaltermexpr/test-evaltermexpr-0544.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.a[i] = x + x + i + } + note: evaltermexpr/arbitrary position + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/every/every.yaml b/third_party/opa/v1/test/cases/testdata/v0/every/every.yaml new file mode 100644 index 000000000000..37e485c8f342 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/every/every.yaml @@ -0,0 +1,249 @@ +--- +cases: + - data: + modules: + - | + package test + import future.keywords.every + + p { + every x in [] { x != x } + } + note: every/empty domain (array) + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.every + + p { + every x in set() { x != x } + } + note: every/empty domain (set) + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.every + + p { + every x in {} { x != x } + } + note: every/empty domain (object) + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.every + + l[1] { + false + } + + p { + every x in l { x != x } + } + note: every/empty domain (partial rule ref) + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.every + + p { + every _ in input { true } + } + note: every/domain undefined (input) + query: data.test.p = x + want_result: [] + - data: + modules: + - | + package test + import future.keywords.every + + p { + every _ in data.foo { true } + } + note: every/domain undefined (data ref) + query: data.test.p = x + want_result: [] + - data: + modules: + - | + package test + import future.keywords.every + + p { + every x in numbers.range(1, 10) { x >= 1 } + } + note: every/domain is call + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.every + + p { + every k, v in [1, 2] { k+1 == v } + } + note: every/simple key/val + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.every + + p { + every k, v in {1, 2} { k == v } + } + note: every/simple key/val (set) + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.every + + l[1] { + true + } + + l[2] { + true + } + + p { + every k, v in l { k == v } + } + note: every/simple key/val (partial rule ref) + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.every + + p { + i := 10 + every k, v in [1, 2] { k+v != i } + } + note: every/outer bindings + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.every + + p { + every v in [1, 2] { v != 1 } + } + note: every/simple failure, first + query: data.test.p = x + want_result: [] + - data: + modules: + - | + package test + import future.keywords.every + + p { + every v in [1, 2] { v != 2 } + } + note: every/simple failure, last + query: data.test.p = x + want_result: [] + - data: + modules: + - | + package test + import future.keywords.every + + p { + every v in input { v == 1 } with input as [1, 1, 1] + } + note: "every/with: domain" + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.every + + p { + every v in [1, 2] { v in input } with input as [1, 2, 1, 0] + } + note: "every/with: body" + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + import future.keywords.every + + p[v] { + every v in [1, 2] { v < 3 } + v := 10 + v > 3 + } + note: "every/followed by another query" + query: data.test.p = x + want_result: + - x: [10] + - note: "every/array with calls" + modules: + - | + package test + import future.keywords.every + + p { + every v in [1 / 2, 3, 4 + 5] { v < 10 } + } + query: data.test.p = x + want_result: + - x: true + - note: "every/array with calls (fail)" + modules: + - | + package test + import future.keywords.every + + p { + every v in [1 / 2, 3, 4 + 5] { v < 9 } + } + + q { + not p + } + query: data.test.q = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/every/non_iterable_domain.yaml b/third_party/opa/v1/test/cases/testdata/v0/every/non_iterable_domain.yaml new file mode 100644 index 000000000000..6b0d57b7258d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/every/non_iterable_domain.yaml @@ -0,0 +1,151 @@ +--- +cases: + - note: "every/non-iter domain: int" + modules: + - | + package test + import future.keywords.every + + default p := 1 + + p := 2 { + every v in 42 { v > 1 } + } + query: data.test.p = x + want_result: + - x: 1 + - note: "every/non-iter domain: string" + modules: + - | + package test + import future.keywords.every + + default p := 1 + + p := 2 { + every v in "foobar" { v > 1 } + } + query: data.test.p = x + want_result: + - x: 1 + - note: "every/non-iter domain: bool" + modules: + - | + package test + import future.keywords.every + + default p := 1 + + p := 2 { + every v in true { v > 1 } + } + query: data.test.p = x + want_result: + - x: 1 + - note: "every/non-iter domain: null" + modules: + - | + package test + import future.keywords.every + + default p := 1 + + p := 2 { + every v in null { v > 1 } + } + query: data.test.p = x + want_result: + - x: 1 + - note: "every/non-iter domain: built-in call" + modules: + - | + package test + import future.keywords.every + + default p := 1 + + p := 2 { + every v in floor(13.37) { v > 1 } + } + query: data.test.p = x + want_result: + - x: 1 + - note: "every/non-iter domain: function call" + modules: + - | + package test + import future.keywords.every + + default p := 1 + + p := 2 { + every v in foo(1, 2) { v > 1 } + } + + foo(a, b) := a + b + query: data.test.p = x + want_result: + - x: 1 + - note: "every/non-iter domain: rule ref" + modules: + - | + package test + import future.keywords.every + + default p := 1 + + p := 2 { + every v in q { v > 1 } + } + + q := 1 + query: data.test.p = x + want_result: + - x: 1 + - note: "every/non-iter domain: data int" + modules: + - | + package test + import future.keywords.every + + default p := 1 + + p := 2 { + every v in data.iterate_me { v > 1 } + } + query: data.test.p = x + data: + iterate_me: 1 + want_result: + - x: 1 + - note: "every/non-iter domain: input int" + modules: + - | + package test + import future.keywords.every + + default p := 1 + + p := 2 { + every v in input.iterate_me { v > 1 } + } + query: data.test.p = x + input: + iterate_me: 1 + want_result: + - x: 1 + - note: "every/non-iter domain: input int (1st level)" + modules: + - | + package test + import future.keywords.every + + default p := 1 + + p := 2 { + every v in input { v > 1 } + } + query: data.test.p = x + input: 1 + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/every/textbook.yaml b/third_party/opa/v1/test/cases/testdata/v0/every/textbook.yaml new file mode 100644 index 000000000000..6cb8f1aac5b3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/every/textbook.yaml @@ -0,0 +1,149 @@ +--- +cases: + - data: + input: + containers: + - image: bitcoin-miner + - image: acmecorp.com/webapp + modules: + - | + package test + import future.keywords.every + + p { + every x in input.containers { + startswith(x.image, "acmecorp.com/") + } + } + note: every/example, fail + query: data.test.p = x + want_result: [] + - data: + input: + containers: + - image: acmecorp.com/bitcoin-miner + - image: acmecorp.com/webapp + modules: + - | + package test + import future.keywords.every + + p { + every x in input.containers { + startswith(x.image, "acmecorp.com/") + } + } + note: every/example, success + query: data.test.p = x + want_result: + - x: true + - data: + input: + containers: + - image: acmecorp.com/bitcoin-miner + - image: acmecorp.com/webapp + init_containers: + - image: acmecorp.com/bitcoin-miner + modules: + - | + package test + import future.keywords.every + + p { + containers := { c | c := input.containers[_] } + init_containers := { c | c := input.init_containers[_] } + every x in containers | init_containers { + startswith(x.image, "acmecorp.com/") + } + } + note: every/example with two sets + query: data.test.p = x + want_result: + - x: true + - data: + input: + containers: + - image: acmecorp.com/bitcoin-miner + - image: acmecorp.com/webapp + init_containers: + - image: bitcoin-miner + modules: + - | + package test + import future.keywords.every + + p { + containers := { c | c := input.containers[_] } + init_containers := { c | c := input.init_containers[_] } + every x in containers | init_containers { + startswith(x.image, "acmecorp.com/") + } + } + note: every/example with two sets (fail) + query: data.test.p = x + want_result: [] + - data: + input: + containers: + - image: hooli.com/bitcoin-miner + - image: acmecorp.net/webapp + - image: nginx + modules: + - | + package test + import future.keywords.every + + allowed_repos := {"hooli.com/", "acmecorp.net/"} + + p { + every c in input.containers { + some repo in allowed_repos + startswith(c.image, repo) + } + } + note: every/example every/some, fail + query: data.test.p = x + want_result: [] + - data: + input: + containers: + - image: hooli.com/bitcoin-miner + - image: acmecorp.net/webapp + - image: hooli.com/nginx + modules: + - | + package test + import future.keywords.every + + allowed_repos := {"hooli.com/", "acmecorp.net/"} + + p { + every c in input.containers { + some repo in allowed_repos + startswith(c.image, repo) + } + } + note: every/example every/some, success + query: data.test.p = x + want_result: + - x: true + - data: + input: + servers: + - ports: [80, 443] + - ports: [80] + modules: + - | + package test + import future.keywords.every + + deny { + some s in input.servers + every port in s.ports { + port != 443 + } + } + note: every/example some/every + query: data.test.deny = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/example/test-example-1070.yaml b/third_party/opa/v1/test/cases/testdata/v0/example/test-example-1070.yaml new file mode 100644 index 000000000000..3d4ab2c25ac2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/example/test-example-1070.yaml @@ -0,0 +1,88 @@ +--- +cases: + - data: + networks: + - id: n1 + public: false + - id: n2 + public: false + - id: n3 + public: true + ports: + - id: p1 + networks: + - n1 + - id: p2 + networks: + - n3 + - id: p3 + networks: + - n2 + servers: + - id: s1 + name: app + ports: + - p1 + - p2 + - p3 + protocols: + - https + - ssh + - id: s2 + name: db + ports: + - p3 + protocols: + - mysql + - id: s3 + name: cache + ports: + - p3 + protocols: + - memcache + - http + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http + input_term: "{}" + modules: + - | + package opa.example + + public_servers[server] { + server = data.servers[_] + server.ports[_] = data.ports[i].id + data.ports[i].networks[_] = data.networks[j].id + data.networks[j].public = true + } + + violations[server] { + server = data.servers[_] + server.protocols[_] = "http" + data.opa.example.public_servers[server] + } + note: example/public servers + query: data.opa.example.public_servers = x + sort_bindings: true + want_result: + - x: + - id: s1 + name: app + ports: + - p1 + - p2 + - p3 + protocols: + - https + - ssh + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http diff --git a/third_party/opa/v1/test/cases/testdata/v0/example/test-example-1071.yaml b/third_party/opa/v1/test/cases/testdata/v0/example/test-example-1071.yaml new file mode 100644 index 000000000000..c77736cb0ea8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/example/test-example-1071.yaml @@ -0,0 +1,85 @@ +--- +cases: + - data: + networks: + - id: n1 + public: false + - id: n2 + public: false + - id: n3 + public: true + ports: + - id: p1 + networks: + - n1 + - id: p2 + networks: + - n3 + - id: p3 + networks: + - n2 + servers: + - id: s1 + name: app + ports: + - p1 + - p2 + - p3 + protocols: + - https + - ssh + - id: s2 + name: db + ports: + - p3 + protocols: + - mysql + - id: s3 + name: cache + ports: + - p3 + protocols: + - memcache + - http + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http + input_term: "{}" + modules: + - | + package opa.example + + public_servers[server] { + server = data.servers[_] + server.ports[_] = data.ports[i].id + data.ports[i].networks[_] = data.networks[j].id + data.networks[j].public = true + } + + violations[server] { + server = data.servers[_] + server.protocols[_] = "http" + data.opa.example.public_servers[server] + } + - | + package topdown_test_partial + + __result__ = _result { + _result = {{"id": "s1", "name": "app", "ports": ["p1", "p2", "p3"], "protocols": ["https", "ssh"]}, {"id": "s4", "name": "dev", "ports": ["p1", "p2"], "protocols": ["http"]}} + } + note: example/violations + query: data.opa.example.violations = x + sort_bindings: true + want_result: + - x: + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http diff --git a/third_party/opa/v1/test/cases/testdata/v0/example/test-example-1072.yaml b/third_party/opa/v1/test/cases/testdata/v0/example/test-example-1072.yaml new file mode 100644 index 000000000000..8dcae794306d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/example/test-example-1072.yaml @@ -0,0 +1,102 @@ +--- +cases: + - data: + networks: + - id: n1 + public: false + - id: n2 + public: false + - id: n3 + public: true + ports: + - id: p1 + networks: + - n1 + - id: p2 + networks: + - n3 + - id: p3 + networks: + - n2 + servers: + - id: s1 + name: app + ports: + - p1 + - p2 + - p3 + protocols: + - https + - ssh + - id: s2 + name: db + ports: + - p3 + protocols: + - mysql + - id: s3 + name: cache + ports: + - p3 + protocols: + - memcache + - http + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http + input_term: "{}" + modules: + - | + package opa.example + + public_servers[server] { + server = data.servers[_] + server.ports[_] = data.ports[i].id + data.ports[i].networks[_] = data.networks[j].id + data.networks[j].public = true + } + + violations[server] { + server = data.servers[_] + server.protocols[_] = "http" + data.opa.example.public_servers[server] + } + - | + package topdown_test_partial + + __result__ = _result { + _result = {{"id": "s4", "name": "dev", "ports": ["p1", "p2"], "protocols": ["http"]}} + } + note: example/both + query: data.opa.example = x + want_result: + - x: + public_servers: + - id: s1 + name: app + ports: + - p1 + - p2 + - p3 + protocols: + - https + - ssh + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http + violations: + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http diff --git a/third_party/opa/v1/test/cases/testdata/v0/fix1863/test-fix1863-0706.yaml b/third_party/opa/v1/test/cases/testdata/v0/fix1863/test-fix1863-0706.yaml new file mode 100644 index 000000000000..878cb5ce1912 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/fix1863/test-fix1863-0706.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: {} + modules: + - | + package a.b + + # this module is empty + - | + package x + + p = __local0__ { # p should be defined (an empty object) + true + __local0__ = data.a.b + } + note: fix1863/is defined + query: data = x + want_result: + - x: + a: + b: {} + x: + p: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/fix1863/test-fix1863-0707.yaml b/third_party/opa/v1/test/cases/testdata/v0/fix1863/test-fix1863-0707.yaml new file mode 100644 index 000000000000..bedc344f738a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/fix1863/test-fix1863-0707.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package a.b + + # this module is empty + - | + package x + + p = __local0__ { # p should be defined (an empty object) + true + __local0__ = data.a.b + } + note: fix1863/is defined + query: data.x = x + want_result: + - x: + p: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/fix1863/test-fix1863-0708.yaml b/third_party/opa/v1/test/cases/testdata/v0/fix1863/test-fix1863-0708.yaml new file mode 100644 index 000000000000..4922e1065d22 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/fix1863/test-fix1863-0708.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: {} + modules: + - | + package a.b + + # this module is empty + - | + package x + + p = __local0__ { # p should be defined (an empty object) + true + __local0__ = data.a.b + } + - | + package topdown_test_partial + + __result__ = _result { + _result = {"p": {}} + } + note: fix1863/is defined + query: data.x.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-conflicts.yaml b/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-conflicts.yaml new file mode 100644 index 000000000000..893804baae69 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-conflicts.yaml @@ -0,0 +1,22 @@ +--- +cases: + - modules: + - | + package test + o = ["1", "2"] + f(x) := o[_] == x + p { f("1") } + note: "functionerrors/conflict: plain false and true result, first round" + query: data.test.p = x + want_error: functions must not produce multiple outputs for same inputs + want_error_code: eval_conflict_error + - modules: + - | + package test + o = ["1", "2"] + f(x) := o[_] == x + p { f("2") } + note: "functionerrors/conflict: plain false and true result, second round" + query: data.test.p = x + want_error: functions must not produce multiple outputs for same inputs + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-1012.yaml b/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-1012.yaml new file mode 100644 index 000000000000..90c862c5d922 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-1012.yaml @@ -0,0 +1,33 @@ +--- +cases: + - data: + modules: + - | + package test1 + + p(a) = y { + y = a[_] + } + + r = y { + data.test1.p([1, 2, 3], y) + } + note: functionerrors/function output conflict single + query: data.test1.r = x + want_error: functions must not produce multiple outputs for same inputs + want_error_code: eval_conflict_error + - data: + modules: + - | + package test + + f(_) = true + f(_) = false + + r { + data.test.f(1) + } + note: functionerrors/function output conflict, used as boolean + query: data.test.r = x + want_error: functions must not produce multiple outputs for same inputs + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-1013.yaml b/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-1013.yaml new file mode 100644 index 000000000000..b85f14932418 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-1013.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + modules: + - | + package test2 + + p(1, a) = y { + y = a + } + + p(2, b) = y { + y = b + 1 + } + + r = y { + data.test2.p(3, 0, y) + } + note: functionerrors/function input no match + query: data.test2.r = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-1014.yaml b/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-1014.yaml new file mode 100644 index 000000000000..fa2f42641dd3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-1014.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + modules: + - | + package test3 + + p(1, a) = y { + y = a + } + + p(x, y) = z { + z = x + } + + r = y { + data.test3.p(1, 0, y) + } + note: functionerrors/function output conflict multiple + query: data.test3.r = x + want_error: functions must not produce multiple outputs for same inputs + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-undefined-builtin-result.yaml b/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-undefined-builtin-result.yaml new file mode 100644 index 000000000000..6dd4764a2ff5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functionerrors/test-functionerrors-undefined-builtin-result.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: + modules: + - | + package test + foo = units.parse_bytes("1KB") + bar = units.parse_bytes("foo") # undefined + note: functionerrors/undefined builtin result + query: data.test = x + want_result: + - x: + foo: 1000 diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0990.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0990.yaml new file mode 100644 index 000000000000..5bed409bb2a6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0990.yaml @@ -0,0 +1,279 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + c: + - x: + - true + - false + - foo + "y": + - null + - 3.14159 + z: + p: true + q: false + d: + e: + - bar + - baz + f: + - xs: + - 1 + ys: + - 2 + - xs: + - 2 + ys: + - 3 + g: + a: + - 1 + - 0 + - 0 + - 0 + b: + - 0 + - 2 + - 0 + - 0 + c: + - 0 + - 0 + - 0 + - 4 + h: + - - 1 + - 2 + - 3 + - - 2 + - 3 + - 4 + l: + - a: bob + b: -1 + c: + - 1 + - 2 + - 3 + - 4 + - a: alice + b: 1 + c: + - 2 + - 3 + - 4 + - 5 + d: null + m: [] + numbers: + - "1" + - "2" + - "3" + - "4" + strings: + bar: 2 + baz: 3 + foo: 1 + three: 3 + modules: + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local20__) = __local20__ + - | + package test.l1.l2 + + p = true + + f(__local21__) = __local21__ + - | + package test.omit_result + + f(__local22__) = __local22__ + + p { + data.test.omit_result.f(1) + } + - | + package ex + + foo(__local0__) = y { + split(__local0__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local1__) = y { + data.ex.foo(__local1__, y) + } + + chain1(__local2__) = b { + data.ex.chain0(__local2__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local3__) = [a, b] { + split(__local3__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local4__) = false + + falsy_func_else(__local5__) { + __local5__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local7__, __local8__]) = [a, b] { + data.ex.foo(__local7__, a) + data.ex.foo(__local8__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local9__, "bar": __local10__}) = z { + data.ex.foo(__local9__, a) + data.test.foo(__local10__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local11__) { + __local11__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local12__) = y { + y = __local12__ + } + + multi(2, __local13__) = y { + __local24__ = 2 * __local13__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local14__) = y { + __local26__ = __local14__ * 10 + y = __local26__ + } + + multi("foo", __local15__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local16__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local17__) = y { + trim(__local17__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local18__) = y { + y = __local18__ + } + + multi("bar", __local19__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + note: functions/basic call + query: data.ex.bar.alice = x + want_result: + - x: + - al + - ce diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0991.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0991.yaml new file mode 100644 index 000000000000..3fd302de2ee0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0991.yaml @@ -0,0 +1,200 @@ +--- +cases: + - data: {} + modules: + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local70__) = __local70__ + - | + package test.l1.l2 + + p = true + + f(__local72__) = __local72__ + - | + package test.omit_result + + f(__local74__) = __local74__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = ["al", "ce"] + } + - | + package ex + + foo(__local46__) = y { + split(__local46__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local47__) = y { + data.ex.foo(__local47__, y) + } + + chain1(__local48__) = b { + data.ex.chain0(__local48__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local49__) = [a, b] { + split(__local49__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local50__) = false + + falsy_func_else(__local51__) { + __local51__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local53__, __local54__]) = [a, b] { + data.ex.foo(__local53__, a) + data.ex.foo(__local54__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local55__, "bar": __local56__}) = z { + data.ex.foo(__local55__, a) + data.test.foo(__local56__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local57__) { + __local57__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local58__) = y { + y = __local58__ + } + + multi(2, __local59__) = y { + __local24__ = 2 * __local59__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local60__) = y { + __local26__ = __local60__ * 10 + y = __local26__ + } + + multi("foo", __local61__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local62__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local66__) = y { + trim(__local66__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local67__) = y { + y = __local67__ + } + + multi("bar", __local68__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + note: functions/false result + query: data.ex.falsy_undefined = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0992.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0992.yaml new file mode 100644 index 000000000000..031d223d7c51 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0992.yaml @@ -0,0 +1,201 @@ +--- +cases: + - data: {} + modules: + - | + package topdown_test_partial + + __result__ = _result { + _result = ["al", "ce"] + } + - | + package ex + + foo(__local46__) = y { + split(__local46__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local47__) = y { + data.ex.foo(__local47__, y) + } + + chain1(__local48__) = b { + data.ex.chain0(__local48__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local49__) = [a, b] { + split(__local49__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local50__) = false + + falsy_func_else(__local51__) { + __local51__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local53__, __local54__]) = [a, b] { + data.ex.foo(__local53__, a) + data.ex.foo(__local54__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local55__, "bar": __local56__}) = z { + data.ex.foo(__local55__, a) + data.test.foo(__local56__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local57__) { + __local57__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local58__) = y { + y = __local58__ + } + + multi(2, __local59__) = y { + __local24__ = 2 * __local59__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local60__) = y { + __local26__ = __local60__ * 10 + y = __local26__ + } + + multi("foo", __local61__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local62__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local66__) = y { + trim(__local66__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local67__) = y { + y = __local67__ + } + + multi("bar", __local68__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local70__) = __local70__ + - | + package test.l1.l2 + + p = true + + f(__local72__) = __local72__ + - | + package test.omit_result + + f(__local74__) = __local74__ + + p { + data.test.omit_result.f(1) + } + note: functions/false result negation + query: data.ex.falsy_negation = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0993.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0993.yaml new file mode 100644 index 000000000000..2d0a73867ec6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0993.yaml @@ -0,0 +1,201 @@ +--- +cases: + - data: {} + modules: + - | + package topdown_test_partial + + __result__ = _result { + _result = true + } + - | + package ex + + foo(__local40__) = y { + split(__local40__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local41__) = y { + data.ex.foo(__local41__, y) + } + + chain1(__local42__) = b { + data.ex.chain0(__local42__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local43__) = [a, b] { + split(__local43__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local44__) = false + + falsy_func_else(__local45__) { + __local45__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local64__, __local65__]) = [a, b] { + data.ex.foo(__local64__, a) + data.ex.foo(__local65__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local69__, "bar": __local71__}) = z { + data.ex.foo(__local69__, a) + data.test.foo(__local71__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local73__) { + __local73__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local75__) = y { + y = __local75__ + } + + multi(2, __local76__) = y { + __local24__ = 2 * __local76__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local77__) = y { + __local26__ = __local77__ * 10 + y = __local26__ + } + + multi("foo", __local78__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local79__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local86__) = y { + trim(__local86__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local87__) = y { + y = __local87__ + } + + multi("bar", __local88__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local91__) = __local91__ + - | + package test.l1.l2 + + p = true + + f(__local94__) = __local94__ + - | + package test.omit_result + + f(__local97__) = __local97__ + + p { + data.test.omit_result.f(1) + } + note: functions/false else value + query: data.ex.falsy_else_value = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0994.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0994.yaml new file mode 100644 index 000000000000..82c100ff3d22 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0994.yaml @@ -0,0 +1,200 @@ +--- +cases: + - data: {} + modules: + - | + package topdown_test_partial + + __result__ = _result { + _result = false + } + - | + package ex + + foo(__local66__) = y { + split(__local66__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local67__) = y { + data.ex.foo(__local67__, y) + } + + chain1(__local68__) = b { + data.ex.chain0(__local68__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local70__) = [a, b] { + split(__local70__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local72__) = false + + falsy_func_else(__local74__) { + __local74__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local81__, __local82__]) = [a, b] { + data.ex.foo(__local81__, a) + data.ex.foo(__local82__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local83__, "bar": __local84__}) = z { + data.ex.foo(__local83__, a) + data.test.foo(__local84__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local85__) { + __local85__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local89__) = y { + y = __local89__ + } + + multi(2, __local90__) = y { + __local24__ = 2 * __local90__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local92__) = y { + __local26__ = __local92__ * 10 + y = __local26__ + } + + multi("foo", __local93__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local95__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local106__) = y { + trim(__local106__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local107__) = y { + y = __local107__ + } + + multi("bar", __local108__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local112__) = __local112__ + - | + package test.l1.l2 + + p = true + + f(__local116__) = __local116__ + - | + package test.omit_result + + f(__local120__) = __local120__ + + p { + data.test.omit_result.f(1) + } + note: functions/false else undefined + query: data.ex.falsy_else_undefined = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0995.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0995.yaml new file mode 100644 index 000000000000..8599d8b1bcb4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0995.yaml @@ -0,0 +1,201 @@ +--- +cases: + - data: {} + modules: + - | + package test.l1.l2 + + p = true + + f(__local116__) = __local116__ + - | + package test.omit_result + + f(__local120__) = __local120__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = false + } + - | + package ex + + foo(__local66__) = y { + split(__local66__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local67__) = y { + data.ex.foo(__local67__, y) + } + + chain1(__local68__) = b { + data.ex.chain0(__local68__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local70__) = [a, b] { + split(__local70__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local72__) = false + + falsy_func_else(__local74__) { + __local74__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local81__, __local82__]) = [a, b] { + data.ex.foo(__local81__, a) + data.ex.foo(__local82__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local83__, "bar": __local84__}) = z { + data.ex.foo(__local83__, a) + data.test.foo(__local84__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local85__) { + __local85__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local89__) = y { + y = __local89__ + } + + multi(2, __local90__) = y { + __local24__ = 2 * __local90__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local92__) = y { + __local26__ = __local92__ * 10 + y = __local26__ + } + + multi("foo", __local93__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local95__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local106__) = y { + trim(__local106__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local107__) = y { + y = __local107__ + } + + multi("bar", __local108__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local112__) = __local112__ + note: functions/false else negation + query: data.ex.falsy_else_negation = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0996.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0996.yaml new file mode 100644 index 000000000000..80dcbd65bda5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0996.yaml @@ -0,0 +1,203 @@ +--- +cases: + - data: {} + modules: + - | + package test + + foo(__local126__) = y { + trim(__local126__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local127__) = y { + y = __local127__ + } + + multi("bar", __local128__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local133__) = __local133__ + - | + package test.l1.l2 + + p = true + + f(__local138__) = __local138__ + - | + package test.omit_result + + f(__local143__) = __local143__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = true + } + - | + package ex + + foo(__local86__) = y { + split(__local86__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local87__) = y { + data.ex.foo(__local87__, y) + } + + chain1(__local88__) = b { + data.ex.chain0(__local88__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local91__) = [a, b] { + split(__local91__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local94__) = false + + falsy_func_else(__local96__) { + __local96__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local98__, __local99__]) = [a, b] { + data.ex.foo(__local98__, a) + data.ex.foo(__local99__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local100__, "bar": __local101__}) = z { + data.ex.foo(__local100__, a) + data.test.foo(__local101__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local102__) { + __local102__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local103__) = y { + y = __local103__ + } + + multi(2, __local104__) = y { + __local24__ = 2 * __local104__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local105__) = y { + __local26__ = __local105__ * 10 + y = __local26__ + } + + multi("foo", __local109__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local110__) = true + + always_true { + data.ex.always_true_fn(1) + } + note: functions/chained + query: data.ex.chain2 = x + want_result: + - x: + - foo + - bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0997.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0997.yaml new file mode 100644 index 000000000000..9635b572634b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0997.yaml @@ -0,0 +1,203 @@ +--- +cases: + - data: {} + modules: + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local154__) = __local154__ + - | + package test.l1.l2 + + p = true + + f(__local160__) = __local160__ + - | + package test.omit_result + + f(__local166__) = __local166__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = ["foo", "bar"] + } + - | + package ex + + foo(__local106__) = y { + split(__local106__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local107__) = y { + data.ex.foo(__local107__, y) + } + + chain1(__local108__) = b { + data.ex.chain0(__local108__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local111__) = [a, b] { + split(__local111__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local112__) = false + + falsy_func_else(__local113__) { + __local113__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local115__, __local116__]) = [a, b] { + data.ex.foo(__local115__, a) + data.ex.foo(__local116__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local117__, "bar": __local118__}) = z { + data.ex.foo(__local117__, a) + data.test.foo(__local118__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local119__) { + __local119__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local120__) = y { + y = __local120__ + } + + multi(2, __local121__) = y { + __local24__ = 2 * __local121__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local122__) = y { + __local26__ = __local122__ * 10 + y = __local26__ + } + + multi("foo", __local123__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local124__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local146__) = y { + trim(__local146__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local147__) = y { + y = __local147__ + } + + multi("bar", __local148__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + note: functions/cross package + query: data.test.cross = x + want_result: + - x: + - s f + - - ", my name " diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0998.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0998.yaml new file mode 100644 index 000000000000..305c49cb4355 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0998.yaml @@ -0,0 +1,204 @@ +--- +cases: + - data: {} + modules: + - | + package test.l1.l3 + + g(__local175__) = __local175__ + - | + package test.l1.l2 + + p = true + + f(__local182__) = __local182__ + - | + package test.omit_result + + f(__local189__) = __local189__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = ["s f", [", my name "]] + } + - | + package ex + + foo(__local125__) = y { + split(__local125__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local126__) = y { + data.ex.foo(__local126__, y) + } + + chain1(__local127__) = b { + data.ex.chain0(__local127__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local128__) = [a, b] { + split(__local128__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local129__) = false + + falsy_func_else(__local130__) { + __local130__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local132__, __local133__]) = [a, b] { + data.ex.foo(__local132__, a) + data.ex.foo(__local133__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local134__, "bar": __local135__}) = z { + data.ex.foo(__local134__, a) + data.test.foo(__local135__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local136__) { + __local136__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local137__) = y { + y = __local137__ + } + + multi(2, __local138__) = y { + __local24__ = 2 * __local138__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local139__) = y { + __local26__ = __local139__ * 10 + y = __local26__ + } + + multi("foo", __local140__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local141__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local165__) = y { + trim(__local165__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local167__) = y { + y = __local167__ + } + + multi("bar", __local168__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + note: functions/array params + query: data.ex.arraysrule = x + want_result: + - x: + - - h + - h + - - foo diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0999.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0999.yaml new file mode 100644 index 000000000000..da07f416680c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-0999.yaml @@ -0,0 +1,204 @@ +--- +cases: + - data: {} + modules: + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local196__) = __local196__ + - | + package test.l1.l2 + + p = true + + f(__local204__) = __local204__ + - | + package test.omit_result + + f(__local212__) = __local212__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = [["h", "h"], ["foo"]] + } + - | + package ex + + foo(__local142__) = y { + split(__local142__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local143__) = y { + data.ex.foo(__local143__, y) + } + + chain1(__local144__) = b { + data.ex.chain0(__local144__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local145__) = [a, b] { + split(__local145__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local146__) = false + + falsy_func_else(__local147__) { + __local147__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local149__, __local150__]) = [a, b] { + data.ex.foo(__local149__, a) + data.ex.foo(__local150__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local151__, "bar": __local152__}) = z { + data.ex.foo(__local151__, a) + data.test.foo(__local152__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local153__) { + __local153__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local154__) = y { + y = __local154__ + } + + multi(2, __local155__) = y { + __local24__ = 2 * __local155__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local156__) = y { + __local26__ = __local156__ * 10 + y = __local26__ + } + + multi("foo", __local157__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local158__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local185__) = y { + trim(__local185__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local186__) = y { + y = __local186__ + } + + multi("bar", __local187__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + note: functions/object params + query: data.ex.objectsrule = x + want_result: + - x: + - - h + - h + - i diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1000.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1000.yaml new file mode 100644 index 000000000000..6121da71a4d6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1000.yaml @@ -0,0 +1,201 @@ +--- +cases: + - data: {} + modules: + - | + package topdown_test_partial + + __result__ = _result { + _result = [["h", "h"], "i"] + } + - | + package ex + + foo(__local159__) = y { + split(__local159__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local160__) = y { + data.ex.foo(__local160__, y) + } + + chain1(__local161__) = b { + data.ex.chain0(__local161__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local162__) = [a, b] { + split(__local162__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local163__) = false + + falsy_func_else(__local164__) { + __local164__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local166__, __local167__]) = [a, b] { + data.ex.foo(__local166__, a) + data.ex.foo(__local167__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local168__, "bar": __local169__}) = z { + data.ex.foo(__local168__, a) + data.test.foo(__local169__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local170__) { + __local170__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local171__) = y { + y = __local171__ + } + + multi(2, __local172__) = y { + __local24__ = 2 * __local172__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local173__) = y { + __local26__ = __local173__ * 10 + y = __local26__ + } + + multi("foo", __local174__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local175__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local205__) = y { + trim(__local205__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local206__) = y { + y = __local206__ + } + + multi("bar", __local207__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local217__) = __local217__ + - | + package test.l1.l2 + + p = true + + f(__local226__) = __local226__ + - | + package test.omit_result + + f(__local235__) = __local235__ + + p { + data.test.omit_result.f(1) + } + note: functions/ref func output + query: data.ex.refoutput = x + want_result: + - x: h diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1001.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1001.yaml new file mode 100644 index 000000000000..8b14a25e9fad --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1001.yaml @@ -0,0 +1,201 @@ +--- +cases: + - data: {} + modules: + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local238__) = __local238__ + - | + package test.l1.l2 + + p = true + + f(__local248__) = __local248__ + - | + package test.omit_result + + f(__local258__) = __local258__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = "h" + } + - | + package ex + + foo(__local176__) = y { + split(__local176__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local177__) = y { + data.ex.foo(__local177__, y) + } + + chain1(__local178__) = b { + data.ex.chain0(__local178__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local179__) = [a, b] { + split(__local179__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local180__) = false + + falsy_func_else(__local181__) { + __local181__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local183__, __local184__]) = [a, b] { + data.ex.foo(__local183__, a) + data.ex.foo(__local184__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local185__, "bar": __local186__}) = z { + data.ex.foo(__local185__, a) + data.test.foo(__local186__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local187__) { + __local187__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local188__) = y { + y = __local188__ + } + + multi(2, __local189__) = y { + __local24__ = 2 * __local189__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local190__) = y { + __local26__ = __local190__ * 10 + y = __local26__ + } + + multi("foo", __local191__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local192__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local224__) = y { + trim(__local224__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local225__) = y { + y = __local225__ + } + + multi("bar", __local227__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + note: functions/always_true + query: data.ex.always_true = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1002.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1002.yaml new file mode 100644 index 000000000000..17ad085d9078 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1002.yaml @@ -0,0 +1,201 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + foo(__local193__) = y { + split(__local193__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local194__) = y { + data.ex.foo(__local194__, y) + } + + chain1(__local195__) = b { + data.ex.chain0(__local195__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local196__) = [a, b] { + split(__local196__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local197__) = false + + falsy_func_else(__local198__) { + __local198__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local200__, __local201__]) = [a, b] { + data.ex.foo(__local200__, a) + data.ex.foo(__local201__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local202__, "bar": __local203__}) = z { + data.ex.foo(__local202__, a) + data.test.foo(__local203__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local204__) { + __local204__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local205__) = y { + y = __local205__ + } + + multi(2, __local206__) = y { + __local24__ = 2 * __local206__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local207__) = y { + __local26__ = __local207__ * 10 + y = __local26__ + } + + multi("foo", __local208__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local209__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local244__) = y { + trim(__local244__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local245__) = y { + y = __local245__ + } + + multi("bar", __local246__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local259__) = __local259__ + - | + package test.l1.l2 + + p = true + + f(__local270__) = __local270__ + - | + package test.omit_result + + f(__local281__) = __local281__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = true + } + note: functions/same package call + query: data.test.samepkg = x + want_result: + - x: w do you do? diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1003.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1003.yaml new file mode 100644 index 000000000000..8826a37c7a2b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1003.yaml @@ -0,0 +1,201 @@ +--- +cases: + - data: {} + modules: + - | + package test.l1.l2 + + p = true + + f(__local292__) = __local292__ + - | + package test.omit_result + + f(__local304__) = __local304__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = "w do you do?" + } + - | + package ex + + foo(__local210__) = y { + split(__local210__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local211__) = y { + data.ex.foo(__local211__, y) + } + + chain1(__local212__) = b { + data.ex.chain0(__local212__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local213__) = [a, b] { + split(__local213__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local214__) = false + + falsy_func_else(__local215__) { + __local215__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local217__, __local218__]) = [a, b] { + data.ex.foo(__local217__, a) + data.ex.foo(__local218__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local219__, "bar": __local220__}) = z { + data.ex.foo(__local219__, a) + data.test.foo(__local220__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local221__) { + __local221__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local222__) = y { + y = __local222__ + } + + multi(2, __local223__) = y { + __local24__ = 2 * __local223__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local224__) = y { + __local26__ = __local224__ * 10 + y = __local26__ + } + + multi("foo", __local225__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local226__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local264__) = y { + trim(__local264__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local265__) = y { + y = __local265__ + } + + multi("bar", __local266__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local279__) = __local279__ + note: functions/void good + query: data.ex.voidGood = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1004.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1004.yaml new file mode 100644 index 000000000000..fa0f2a649418 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1004.yaml @@ -0,0 +1,200 @@ +--- +cases: + - data: {} + modules: + - | + package test.omit_result + + f(__local327__) = __local327__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = true + } + - | + package ex + + foo(__local227__) = y { + split(__local227__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local228__) = y { + data.ex.foo(__local228__, y) + } + + chain1(__local229__) = b { + data.ex.chain0(__local229__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local230__) = [a, b] { + split(__local230__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local231__) = false + + falsy_func_else(__local232__) { + __local232__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local234__, __local235__]) = [a, b] { + data.ex.foo(__local234__, a) + data.ex.foo(__local235__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local236__, "bar": __local237__}) = z { + data.ex.foo(__local236__, a) + data.test.foo(__local237__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local238__) { + __local238__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local239__) = y { + y = __local239__ + } + + multi(2, __local240__) = y { + __local24__ = 2 * __local240__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local241__) = y { + __local26__ = __local241__ * 10 + y = __local26__ + } + + multi("foo", __local242__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local243__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local284__) = y { + trim(__local284__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local285__) = y { + y = __local285__ + } + + multi("bar", __local286__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local300__) = __local300__ + - | + package test.l1.l2 + + p = true + + f(__local314__) = __local314__ + note: functions/void bad + query: data.ex.voidBad = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1005.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1005.yaml new file mode 100644 index 000000000000..b52f4b932e0a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1005.yaml @@ -0,0 +1,201 @@ +--- +cases: + - data: {} + modules: + - | + package test.l1.l3 + + g(__local300__) = __local300__ + - | + package test.l1.l2 + + p = true + + f(__local314__) = __local314__ + - | + package test.omit_result + + f(__local327__) = __local327__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = true + } + - | + package ex + + foo(__local227__) = y { + split(__local227__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local228__) = y { + data.ex.foo(__local228__, y) + } + + chain1(__local229__) = b { + data.ex.chain0(__local229__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local230__) = [a, b] { + split(__local230__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local231__) = false + + falsy_func_else(__local232__) { + __local232__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local234__, __local235__]) = [a, b] { + data.ex.foo(__local234__, a) + data.ex.foo(__local235__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local236__, "bar": __local237__}) = z { + data.ex.foo(__local236__, a) + data.test.foo(__local237__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local238__) { + __local238__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local239__) = y { + y = __local239__ + } + + multi(2, __local240__) = y { + __local24__ = 2 * __local240__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local241__) = y { + __local26__ = __local241__ * 10 + y = __local26__ + } + + multi("foo", __local242__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local243__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local284__) = y { + trim(__local284__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local285__) = y { + y = __local285__ + } + + multi("bar", __local286__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + note: functions/multi1 + query: data.ex.multi1 = x + want_result: + - x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1006.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1006.yaml new file mode 100644 index 000000000000..60d1c8f4e19d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1006.yaml @@ -0,0 +1,201 @@ +--- +cases: + - data: {} + modules: + - | + package test.omit_result + + f(__local350__) = __local350__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = 2 + } + - | + package ex + + foo(__local244__) = y { + split(__local244__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local245__) = y { + data.ex.foo(__local245__, y) + } + + chain1(__local246__) = b { + data.ex.chain0(__local246__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local247__) = [a, b] { + split(__local247__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local248__) = false + + falsy_func_else(__local249__) { + __local249__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local251__, __local252__]) = [a, b] { + data.ex.foo(__local251__, a) + data.ex.foo(__local252__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local253__, "bar": __local254__}) = z { + data.ex.foo(__local253__, a) + data.test.foo(__local254__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local255__) { + __local255__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local256__) = y { + y = __local256__ + } + + multi(2, __local257__) = y { + __local24__ = 2 * __local257__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local258__) = y { + __local26__ = __local258__ * 10 + y = __local26__ + } + + multi("foo", __local259__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local260__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local304__) = y { + trim(__local304__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local305__) = y { + y = __local305__ + } + + multi("bar", __local306__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local321__) = __local321__ + - | + package test.l1.l2 + + p = true + + f(__local336__) = __local336__ + note: functions/multi2 + query: data.ex.multi2 = x + want_result: + - x: 5 diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1007.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1007.yaml new file mode 100644 index 000000000000..9a44201a3502 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1007.yaml @@ -0,0 +1,201 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + foo(__local261__) = y { + split(__local261__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local262__) = y { + data.ex.foo(__local262__, y) + } + + chain1(__local263__) = b { + data.ex.chain0(__local263__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local264__) = [a, b] { + split(__local264__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local265__) = false + + falsy_func_else(__local266__) { + __local266__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local268__, __local269__]) = [a, b] { + data.ex.foo(__local268__, a) + data.ex.foo(__local269__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local270__, "bar": __local271__}) = z { + data.ex.foo(__local270__, a) + data.test.foo(__local271__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local272__) { + __local272__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local273__) = y { + y = __local273__ + } + + multi(2, __local274__) = y { + __local24__ = 2 * __local274__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local275__) = y { + __local26__ = __local275__ * 10 + y = __local26__ + } + + multi("foo", __local276__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local277__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local324__) = y { + trim(__local324__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local325__) = y { + y = __local325__ + } + + multi("bar", __local326__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local342__) = __local342__ + - | + package test.l1.l2 + + p = true + + f(__local358__) = __local358__ + - | + package test.omit_result + + f(__local373__) = __local373__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = 5 + } + note: functions/multi3 + query: data.ex.multi3 = x + want_result: + - x: 20 diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1008.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1008.yaml new file mode 100644 index 000000000000..762675876a29 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1008.yaml @@ -0,0 +1,201 @@ +--- +cases: + - data: {} + modules: + - | + package test.omit_result + + f(__local396__) = __local396__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = 20 + } + - | + package ex + + foo(__local278__) = y { + split(__local278__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local279__) = y { + data.ex.foo(__local279__, y) + } + + chain1(__local280__) = b { + data.ex.chain0(__local280__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local281__) = [a, b] { + split(__local281__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local282__) = false + + falsy_func_else(__local283__) { + __local283__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local285__, __local286__]) = [a, b] { + data.ex.foo(__local285__, a) + data.ex.foo(__local286__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local287__, "bar": __local288__}) = z { + data.ex.foo(__local287__, a) + data.test.foo(__local288__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local289__) { + __local289__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local290__) = y { + y = __local290__ + } + + multi(2, __local291__) = y { + __local24__ = 2 * __local291__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local292__) = y { + __local26__ = __local292__ * 10 + y = __local26__ + } + + multi("foo", __local293__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local294__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local344__) = y { + trim(__local344__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local345__) = y { + y = __local345__ + } + + multi("bar", __local346__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local363__) = __local363__ + - | + package test.l1.l2 + + p = true + + f(__local380__) = __local380__ + note: functions/multi4 + query: data.ex.multi4 = x + want_result: + - x: bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1009.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1009.yaml new file mode 100644 index 000000000000..4393a8f6da3e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1009.yaml @@ -0,0 +1,203 @@ +--- +cases: + - data: {} + modules: + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local384__) = __local384__ + - | + package test.l1.l2 + + p = true + + f(__local402__) = __local402__ + - | + package test.omit_result + + f(__local419__) = __local419__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = "bar" + } + - | + package ex + + foo(__local295__) = y { + split(__local295__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local296__) = y { + data.ex.foo(__local296__, y) + } + + chain1(__local297__) = b { + data.ex.chain0(__local297__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local298__) = [a, b] { + split(__local298__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local299__) = false + + falsy_func_else(__local300__) { + __local300__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local302__, __local303__]) = [a, b] { + data.ex.foo(__local302__, a) + data.ex.foo(__local303__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local304__, "bar": __local305__}) = z { + data.ex.foo(__local304__, a) + data.test.foo(__local305__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local306__) { + __local306__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local307__) = y { + y = __local307__ + } + + multi(2, __local308__) = y { + __local24__ = 2 * __local308__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local309__) = y { + __local26__ = __local309__ * 10 + y = __local26__ + } + + multi("foo", __local310__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local311__) = true + + always_true { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local364__) = y { + trim(__local364__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local365__) = y { + y = __local365__ + } + + multi("bar", __local366__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + note: functions/multi cross package + query: data.test.multi_cross_pkg = x + want_result: + - x: + - bar + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1010.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1010.yaml new file mode 100644 index 000000000000..131d66d21718 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1010.yaml @@ -0,0 +1,204 @@ +--- +cases: + - data: {} + modules: + - | + package test + + foo(__local383__) = y { + trim(__local383__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local385__) = y { + y = __local385__ + } + + multi("bar", __local386__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local405__) = __local405__ + - | + package test.l1.l2 + + p = true + + f(__local424__) = __local424__ + - | + package test.omit_result + + f(__local442__) = __local442__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = ["bar", 3] + } + - | + package ex + + foo(__local312__) = y { + split(__local312__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local313__) = y { + data.ex.foo(__local313__, y) + } + + chain1(__local314__) = b { + data.ex.chain0(__local314__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local315__) = [a, b] { + split(__local315__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local316__) = false + + falsy_func_else(__local317__) { + __local317__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local319__, __local320__]) = [a, b] { + data.ex.foo(__local319__, a) + data.ex.foo(__local320__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local321__, "bar": __local322__}) = z { + data.ex.foo(__local321__, a) + data.test.foo(__local322__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local323__) { + __local323__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local324__) = y { + y = __local324__ + } + + multi(2, __local325__) = y { + __local24__ = 2 * __local325__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local326__) = y { + __local26__ = __local326__ * 10 + y = __local26__ + } + + multi("foo", __local327__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local328__) = true + + always_true { + data.ex.always_true_fn(1) + } + note: functions/skip-functions + query: data.test.l1 = x + want_result: + - x: + l2: + p: true + l3: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1011.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1011.yaml new file mode 100644 index 000000000000..b44758171082 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-1011.yaml @@ -0,0 +1,201 @@ +--- +cases: + - data: {} + modules: + - | + package test + + foo(__local403__) = y { + trim(__local403__, "h o", y) + } + + cross = y { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local404__) = y { + y = __local404__ + } + + multi("bar", __local406__) = y { + y = "baz" + } + + multi_cross_pkg = [y, z] { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg = y { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local426__) = __local426__ + - | + package test.l1.l2 + + p = true + + f(__local446__) = __local446__ + - | + package test.omit_result + + f(__local465__) = __local465__ + + p { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ = _result { + _result = {"l2": {"p": true}, "l3": {}} + } + - | + package ex + + foo(__local329__) = y { + split(__local329__, "i", y) + } + + bar[x] = y { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local330__) = y { + data.ex.foo(__local330__, y) + } + + chain1(__local331__) = b { + data.ex.chain0(__local331__, b) + } + + chain2 = d { + data.ex.chain1("fooibar", d) + } + + cross(__local332__) = [a, b] { + split(__local332__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local333__) = false + + falsy_func_else(__local334__) { + __local334__ = 1 + } + + else = false { + true + } + + falsy_undefined { + data.ex.falsy_func(1) + } + + falsy_negation { + not data.ex.falsy_func(1) + } + + falsy_else_value = __local23__ { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined { + data.ex.falsy_func_else(2) + } + + falsy_else_negation { + not data.ex.falsy_func_else(2) + } + + arrays([__local336__, __local337__]) = [a, b] { + data.ex.foo(__local336__, a) + data.ex.foo(__local337__, b) + } + + arraysrule = y { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local338__, "bar": __local339__}) = z { + data.ex.foo(__local338__, a) + data.test.foo(__local339__, b) + z = [a, b] + } + + objectsrule = y { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput = y { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local340__) { + __local340__ = "foo" + } + + voidGood { + not data.ex.void("bar", true) + } + + voidBad { + data.ex.void("bar", true) + } + + multi(1, __local341__) = y { + y = __local341__ + } + + multi(2, __local342__) = y { + __local24__ = 2 * __local342__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local343__) = y { + __local26__ = __local343__ * 10 + y = __local26__ + } + + multi("foo", __local344__) = y { + y = "bar" + } + + multi1 = y { + data.ex.multi(1, 2, y) + } + + multi2 = y { + data.ex.multi(2, 2, y) + } + + multi3 = y { + data.ex.multi(3, 2, y) + } + + multi4 = y { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local345__) = true + + always_true { + data.ex.always_true_fn(1) + } + note: functions/omit result + query: data.test.omit_result.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-default.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-default.yaml new file mode 100644 index 000000000000..6a5b6de29afa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-default.yaml @@ -0,0 +1,100 @@ +--- +cases: + - data: + modules: + - | + package test + + default f(x) = 1 + + f(x) = x { + x > 0 + } + + p { + f(-1) == 1 + } + + note: functions/default + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + + default f(x) = 1 + + f(x) = x { + x > 0 + } + + p { + f(2) == 2 + } + + note: functions/non default + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + + default f(x) = 1 + + p { + f(2) == 1 + } + + note: functions/only default + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + + default f(_, _) = 1 + + f(x, y) = x { + x == y + } + + p { + f(2, 2) == 2 + } + + note: functions/wildcard args + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + + default f(x) = 1000 + + f(x) = x { + x > 0 + } + + p = xs { + xs := [y | x = [1, -2, 3][_]; y := f(x)] + } + + note: functions/comprehensions + query: data.test.p = x + want_result: + - x: + - 1 + - 1000 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-nested-with-early-exit.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-nested-with-early-exit.yaml new file mode 100644 index 000000000000..33c6b78487f6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-nested-with-early-exit.yaml @@ -0,0 +1,100 @@ +--- +cases: + - data: + modules: + - | + package generated + + p(_) { + data.generated.q + } + + q = true + + q = false + note: "functions/nested complete doc with conflict" + query: data.generated.p(1) = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error + - data: + modules: + - | + package generated + + p(_) { + data.generated.q + } + + q = true { + false + } + else = true { + true + } + + q = true { + false + } + else = false { + true + } + note: "functions/nested complete doc with conflict, else" + query: data.generated.p(1) = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error + - data: + modules: + - | + package generated + + p(x) { + y := data.generated.q(x) + } + + q(_) = true + + q(_) = false + note: "functions/nested function with conflict" + query: data.generated.p(1) = x + want_error: functions must not produce multiple outputs for same inputs + want_error_code: eval_conflict_error + - data: + modules: + - | + package generated + + p(x) { + y := data.generated.q(x) + } + + q(_) = true { + false + } + else = true { + true + } + + q(_) = true { + false + } + else = false { + true + } + note: "functions/nested function with conflict, else" + query: data.generated.p(1) = x + want_error: functions must not produce multiple outputs for same inputs + want_error_code: eval_conflict_error + - data: + modules: + - | + package test + + p(x) { + y := data.test.q(x) + } + xs = {1, 2} + q(_) = xs[_] + note: "functions/nested function with conflict, else, no extra return" + query: data.test.p(1) = x + want_error: functions must not produce multiple outputs for same inputs + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-unused-arg.yaml b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-unused-arg.yaml new file mode 100644 index 000000000000..147d02a8babf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/functions/test-functions-unused-arg.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: + modules: + - | + package p + + f(x) { + r = input.that_is_not_there + } + note: unused arg + query: data.p.f(1) + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0133.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0133.yaml new file mode 100644 index 000000000000..f4053a4f2b71 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0133.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("*.github.com", ["."], "api.github.com", x) + } + note: globmatch/glob match with . delimiter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0134.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0134.yaml new file mode 100644 index 000000000000..962bfc2c2761 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0134.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("api.**.com", ["."], "api.github.com", x) + } + note: globmatch/super glob match with . delimiter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0135.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0135.yaml new file mode 100644 index 000000000000..a98e321f11e8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0135.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("api.**.com", ["."], "api.cdn.github.com", x) + } + note: globmatch/super glob match with . delimiter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0136.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0136.yaml new file mode 100644 index 000000000000..97bc71eea5ad --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0136.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("*:github:com", [":"], "api:github:com", x) + } + note: "globmatch/glob match with : delimiter" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0137.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0137.yaml new file mode 100644 index 000000000000..c82e80feeed7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0137.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("*.github.com", ["."], "api.not-github.com", x) + } + note: globmatch/glob no match with . delimiter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0138.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0138.yaml new file mode 100644 index 000000000000..c09c714db9cf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0138.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("[abc]at", [], "cat", x) + } + note: globmatch/glob match with character-list matchers + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0139.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0139.yaml new file mode 100644 index 000000000000..4be4e8bd6900 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0139.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("[abc]at", [], "fat", x) + } + note: globmatch/glob no match with character-list matchers + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0140.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0140.yaml new file mode 100644 index 000000000000..c654f920420c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0140.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("[!abc]at", [], "fat", x) + } + note: globmatch/glob match with negated character-list matchers + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0141.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0141.yaml new file mode 100644 index 000000000000..f4947de4213e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0141.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("[!abc]at", [], "cat", x) + } + note: globmatch/glob no match with negated character-list matchers + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0142.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0142.yaml new file mode 100644 index 000000000000..f4f40cd16c57 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0142.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("[a-c]at", [], "bat", x) + } + note: globmatch/glob match with character-range matchers + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0143.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0143.yaml new file mode 100644 index 000000000000..49a904373f85 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0143.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("[a-c]at", [], "fat", x) + } + note: globmatch/glob no match with character-range matchers + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0144.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0144.yaml new file mode 100644 index 000000000000..64ae2f0e56a3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0144.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("[!a-c]at", [], "bat", x) + } + note: globmatch/glob no match with character-range matchers + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0145.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0145.yaml new file mode 100644 index 000000000000..502940c2c439 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0145.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("[!a-c]at", [], "fat", x) + } + note: globmatch/glob match with character-range matchers + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0146.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0146.yaml new file mode 100644 index 000000000000..368fa11f7e29 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0146.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("?at", [], "fat", x) + } + note: globmatch/glob match with single wild-card + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0147.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0147.yaml new file mode 100644 index 000000000000..69b9caf22c09 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0147.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("?at", [], "at", x) + } + note: globmatch/glob no match with single wild-card + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0148.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0148.yaml new file mode 100644 index 000000000000..09dfeed8a321 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0148.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("?at", ["f"], "bat", x) + } + note: globmatch/glob match with single wild-card and delimiter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0149.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0149.yaml new file mode 100644 index 000000000000..7f48b43b113e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0149.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("?at", ["f"], "fat", x) + } + note: globmatch/glob no match with single wild-card and delimiter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0150.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0150.yaml new file mode 100644 index 000000000000..64e2e47f4a6e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0150.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("{cat,bat,[fr]at}", [], "cat", x) + } + note: globmatch/glob match with pattern-alternatives list (cat) + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0151.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0151.yaml new file mode 100644 index 000000000000..af5154257b7c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0151.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("{cat,bat,[fr]at}", [], "bat", x) + } + note: globmatch/glob match with pattern-alternatives list (bat) + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0152.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0152.yaml new file mode 100644 index 000000000000..e727cfc7dd70 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0152.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("{cat,bat,[fr]at}", [], "fat", x) + } + note: globmatch/glob match with pattern-alternatives list (fat) + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0153.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0153.yaml new file mode 100644 index 000000000000..2da53f781a2d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0153.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("{cat,bat,[fr]at}", [], "rat", x) + } + note: globmatch/glob match with pattern-alternatives list (rat) + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0154.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0154.yaml new file mode 100644 index 000000000000..386975ac1868 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0154.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("{cat,bat,[fr]at}", [], "at", x) + } + note: globmatch/glob no match with pattern-alternatives list + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0155.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0155.yaml new file mode 100644 index 000000000000..750ccccd50c2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0155.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("*", ["."], "foo", x) + } + note: globmatch/glob match single with . delimiter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0156.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0156.yaml new file mode 100644 index 000000000000..7acd48bf1ba1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0156.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("*", [], "foo", x) + } + note: globmatch/glob match single with default delimiter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0157.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0157.yaml new file mode 100644 index 000000000000..e5d43d27bb54 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0157.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("*", ["."], "foo.bar", x) + } + note: globmatch/glob no match single with . delimiter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0158.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0158.yaml new file mode 100644 index 000000000000..8b88178389e0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0158.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("*", [], "foo.bar", x) + } + note: globmatch/glob no match single with default delimiter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0159.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0159.yaml new file mode 100644 index 000000000000..7ddb148c0b8c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-0159.yaml @@ -0,0 +1,30 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("*", null, "foo.bar", x) + } + note: globmatch/glob match single without default delimiter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true + - data: {} + modules: + - | + package generated + + p[x] { + glob.match("foo*", null, "foo.bar", x) + } + note: globmatch/glob match single without default delimiter, glob non-empty + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-issue-5273.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-issue-5273.yaml new file mode 100644 index 000000000000..5190232e73ac --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-issue-5273.yaml @@ -0,0 +1,16 @@ +--- +cases: + - modules: + - | + package test + + p[x] { + glob.match("*.github.com", ["."], "api.github.com", x) + glob.match("*.github.com", ["."], "api.github.com", x) + } + # See: https://github.com/open-policy-agent/opa/issues/5273 + note: globmatch/no deadlocks for glob match + query: data.test.p = x + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-issue-5283.yaml b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-issue-5283.yaml new file mode 100644 index 000000000000..8ddb191def5c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globmatch/test-globmatch-issue-5283.yaml @@ -0,0 +1,25 @@ +--- +# See https://github.com/open-policy-agent/opa/issues/528 +cases: + - modules: + - | + package test + p = x { + x := glob.match("*.github.com", ["."], input) + } + input: api.example.com + note: globmatch/captured negative results, variable + query: data.test.p = x + want_result: + - x: false + - modules: + - | + package test + p { + glob.match("*.github.com", ["."], input, false) + } + input: api.example.com + note: globmatch/captured negative result, constant + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globquotemeta/test-globquotemeta-0159.yaml b/third_party/opa/v1/test/cases/testdata/v0/globquotemeta/test-globquotemeta-0159.yaml new file mode 100644 index 000000000000..c2be6dafd26f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globquotemeta/test-globquotemeta-0159.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + glob.quote_meta("*.github.com", x) + } + note: globquotemeta/glob quote meta + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - \*.github.com diff --git a/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0865.yaml b/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0865.yaml new file mode 100644 index 000000000000..d34a49f829cc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0865.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + regex.globs_match("a.a.[0-9]+z", ".b.b2359825792*594823z") + } + note: globsmatch/regex.globs_match + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0866.yaml b/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0866.yaml new file mode 100644 index 000000000000..65793f0bd00b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0866.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + regex.globs_match("[a-z]+", "[0-9]*") + } + note: globsmatch/regex.globs_match + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0867.yaml b/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0867.yaml new file mode 100644 index 000000000000..9ef1374fb844 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0867.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p { + regex.globs_match("pqrs]", "[a-b]+") + } + note: "globsmatch/regex.globs_match: bad pattern err" + query: data.generated.p = x + want_error: + "input:pqrs], pos:5, set-close ']' with no preceding '[': the input + provided is invalid" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0868.yaml b/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0868.yaml new file mode 100644 index 000000000000..6cc7ad1e8b5d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0868.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + d: + e: + - bar + - baz + modules: + - | + package generated + + p[x] { + __local0__ = data.d.e[x] + regex.globs_match("b.*", __local0__) + } + note: "globsmatch/regex.globs_match: ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 0 + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0869.yaml b/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0869.yaml new file mode 100644 index 000000000000..ff70dc5ea936 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0869.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + regex.globs_match(`[a-z]+\[[0-9]+\]`, "foo\\[1\\]") + } + note: "globsmatch/regex.globs_match: raw" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0870.yaml b/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0870.yaml new file mode 100644 index 000000000000..a4ed53cb10fa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/globsmatch/test-globsmatch-0870.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + regex.globs_match(`[a-z]+\[[0-9]+\]`, "foo[\"bar\"]") + } + note: "globsmatch/regex.globs_match: raw: undefined" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-basic-ast.yaml b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-basic-ast.yaml new file mode 100644 index 000000000000..6faac60d52ef --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-basic-ast.yaml @@ -0,0 +1,308 @@ +--- +# These unit tests check that we get JSON AST structures with +# expected structure and content. If these ever break, policies +# depending on the GraphQL ASTs will too! +cases: + - data: + modules: + - | + package test + ast := { + "Operations": [ + { + "Name": "", + "Operation": "query", + "SelectionSet": [ + { + "Alias": "hero", + "Name": "hero", + "SelectionSet": [ + { + "Alias": "name", + "Name": "name" + } + ] + } + ] + } + ] + } + p { + graphql.parse_query("{hero {name}}") == ast + } + note: graphql_parse_query/success-basic-ast simple query + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `query hero ($episode: Episode!) { + hero (episode: $episode) { + id + name + friends { + id + name + friends { + id + name + appearsIn + } + appearsIn + } + appearsIn + } + }` + ast := { + "Operations": [ + { + "Name": "hero", + "Operation": "query", + "SelectionSet": [ + { + "Alias": "hero", + "Arguments": [ + { + "Name": "episode", + "Value": { + "Kind": 0, + "Raw": "episode" + } + } + ], + "Name": "hero", + "SelectionSet": [ + { + "Alias": "id", + "Name": "id" + }, + { + "Alias": "name", + "Name": "name" + }, + { + "Alias": "friends", + "Name": "friends", + "SelectionSet": [ + { + "Alias": "id", + "Name": "id" + }, + { + "Alias": "name", + "Name": "name" + }, + { + "Alias": "friends", + "Name": "friends", + "SelectionSet": [ + { + "Alias": "id", + "Name": "id" + }, + { + "Alias": "name", + "Name": "name" + }, + { + "Alias": "appearsIn", + "Name": "appearsIn" + } + ] + }, + { + "Alias": "appearsIn", + "Name": "appearsIn" + } + ] + }, + { + "Alias": "appearsIn", + "Name": "appearsIn" + } + ] + } + ], + "VariableDefinitions": [ + { + "Type": { + "NamedType": "Episode", + "NonNull": true + }, + "Used": false, + "Variable": "episode" + } + ] + } + ] + } + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-basic-ast with arguments + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `query HeroForEpisode($ep: Episode!) { + hero(episode: $ep) { + name + ... on Droid { + primaryFunction + } + ... on Human { + height + } + } + }` + ast := { + "Operations": [ + { + "Name": "HeroForEpisode", + "Operation": "query", + "SelectionSet": [ + { + "Alias": "hero", + "Arguments": [ + { + "Name": "episode", + "Value": { + "Kind": 0, + "Raw": "ep" + } + } + ], + "Name": "hero", + "SelectionSet": [ + { + "Alias": "name", + "Name": "name" + }, + { + "SelectionSet": [ + { + "Alias": "primaryFunction", + "Name": "primaryFunction" + } + ], + "TypeCondition": "Droid" + }, + { + "SelectionSet": [ + { + "Alias": "height", + "Name": "height" + } + ], + "TypeCondition": "Human" + } + ] + } + ], + "VariableDefinitions": [ + { + "Type": { + "NamedType": "Episode", + "NonNull": true + }, + "Used": false, + "Variable": "ep" + } + ] + } + ] + } + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-basic-ast inline fragments and type conditions + query: data.test.p = x + want_result: + - x: true + # Inline fragments + - data: + modules: + - | + package test + gql := `{ + search(text: "an") { + __typename + ... on Human { + name + } + ... on Droid { + name + } + ... on Starship { + name + } + } + }` + ast := { + "Operations": [ + { + "Name": "", + "Operation": "query", + "SelectionSet": [ + { + "Alias": "search", + "Arguments": [ + { + "Name": "text", + "Value": { + "Kind": 3, + "Raw": "an" + } + } + ], + "Name": "search", + "SelectionSet": [ + { + "Alias": "__typename", + "Name": "__typename" + }, + { + "SelectionSet": [ + { + "Alias": "name", + "Name": "name" + } + ], + "TypeCondition": "Human" + }, + { + "SelectionSet": [ + { + "Alias": "name", + "Name": "name" + } + ], + "TypeCondition": "Droid" + }, + { + "SelectionSet": [ + { + "Alias": "name", + "Name": "name" + } + ], + "TypeCondition": "Starship" + } + ] + } + ] + } + ] + } + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-basic-ast meta fields and introspection + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-is-valid.yaml b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-is-valid.yaml new file mode 100644 index 000000000000..2378614033b4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-is-valid.yaml @@ -0,0 +1,176 @@ +--- +# This suite of tests is adapted from the underlying GraphQL parser library's +# own test suite, as it provides a fairly comprehensive set of good/degenerate +# test cases, which we want to make sure to react correctly to. +# See: https://github.com/vektah/gqlparser/blob/master/validator/validator_test.yml +cases: + - data: + modules: + - | + package test + schema := ` + extend type User { + id: ID! + } + extend type Product { + upc: String! + } + union _Entity = Product | User + extend type Query { + entity: _Entity + } + ` + query := ` + { + entity { + ... on User { + id + } + } + } + ` + p { + graphql.is_valid(query, schema) + } + note: graphql_is_valid/success extending non-existent types + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + query := ` + query SomeOperation { + # Note: Not providing mandatory parameter: (myEnum: Locale!) + myAction { + id + } + } + ` + # We use the unification style from semver's is_valid tests here: + p = x { + x = graphql.is_valid(query, schema) + } + note: graphql_is_valid/success validation rules are independent case 1 + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + query := ` + # Note: there is default enum value in variables + query SomeOperation ($locale: Locale! = DE) { + myAction(myEnum: $locale) { + id + } + } + ` + p { + graphql.is_valid(query, schema) + } + note: graphql_is_valid/success validation rules are independent case 2 + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type DeprecatedType { + deprecatedField: String @deprecated + newField(deprecatedArg: Int): Boolean + } + enum DeprecatedEnum { + ALPHA @deprecated + } + ` + query := `` + p { + graphql.is_valid(query, schema) + } + note: graphql_is_valid/success deprecating types + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type Query { + bar: String! + } + ` + query := ` + query Foo($flag: Boolean!) { + ...Bar + } + fragment Bar on Query { + bar @include(if: $flag) + } + ` + p { + graphql.is_valid(query, schema) + } + note: graphql_is_valid/success no unused variables + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema_ast := graphql.parse_schema(` + type Employee { + id: String! + salary: Int! + } + + schema { + query: Query + } + + type Query { + employeeByID(id: String): Employee + } + `) + query_ast := graphql.parse_query(` + query { employeeByID(id: "alice") { salary }} + `) + p { + graphql.is_valid(query_ast, schema_ast) + } + note: graphql_is_valid/success - AST objects - Employee example + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse-and-verify.yaml b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse-and-verify.yaml new file mode 100644 index 000000000000..5918c6b4efb0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse-and-verify.yaml @@ -0,0 +1,186 @@ +--- +# This suite of tests is adapted from the underlying GraphQL parser library's +# own test suite, as it provides a fairly comprehensive set of good/degenerate +# test cases, which we want to make sure to react correctly to. +# See: https://github.com/vektah/gqlparser/blob/master/validator/validator_test.yml +cases: + - data: + modules: + - | + package test + schema := ` + extend type User { + id: ID! + } + extend type Product { + upc: String! + } + union _Entity = Product | User + extend type Query { + entity: _Entity + } + ` + query := ` + { + entity { + ... on User { + id + } + } + } + ` + q_ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "entity", "Name": "entity", "SelectionSet": [{"SelectionSet": [{"Alias": "id", "Name": "id"}], "TypeCondition": "User"}]}]}]} + p { + [valid, q_ast, _] = graphql.parse_and_verify(query, schema) + valid + } + note: graphql_parse_and_verify/success extending non-existent types + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + query := ` + query SomeOperation { + # Note: Not providing mandatory parameter: (myEnum: Locale!) + myAction { + id + } + } + ` + p { + [valid, q_ast, _] = graphql.parse_and_verify(query, schema) + not valid + } + note: graphql_parse_and_verify/success validation rules are independent case 1 + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type Query { + x: Int + } + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + query := ` + query SomeOperation ($locale: Locale! = DE) { + myAction(myEnum: $locale) { + id + } + } + ` + q_ast := {"Operations": [{"Name": "SomeOperation", "Operation": "query", "SelectionSet": [{"Alias": "myAction", "Arguments": [{"Name": "myEnum", "Value": {"Kind": 0, "Raw": "locale"}}], "Name": "myAction", "SelectionSet": [{"Alias": "id", "Name": "id"}]}], "VariableDefinitions": [{"DefaultValue": {"Kind": 7, "Raw": "DE"}, "Type": {"NamedType": "Locale", "NonNull": true}, "Used": false, "Variable": "locale"}]}]} + p { + [valid, q_ast, _] = graphql.parse_and_verify(query, schema) + valid + } + note: graphql_parse_and_verify/success validation rules are independent case 2 + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type DeprecatedType { + deprecatedField: String @deprecated + newField(deprecatedArg: Int): Boolean + } + enum DeprecatedEnum { + ALPHA @deprecated + } + ` + query := `` + p { + [valid, {}, _] = graphql.parse_and_verify(query, schema) + valid + } + note: graphql_parse_and_verify/success deprecating types + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type Query { + bar: String! + } + ` + query := ` + query Foo($flag: Boolean!) { + ...Bar + } + fragment Bar on Query { + bar @include(if: $flag) + } + ` + q_ast := {"Fragments": [{"Name": "Bar", "SelectionSet": [{"Alias": "bar", "Directives": [{"Arguments": [{"Name": "if", "Value": {"Kind": 0, "Raw": "flag"}}], "Location": "", "Name": "include"}], "Name": "bar"}], "TypeCondition": "Query"}], "Operations": [{"Name": "Foo", "Operation": "query", "SelectionSet": [{"Name": "Bar"}], "VariableDefinitions": [{"Type": {"NamedType": "Boolean", "NonNull": true}, "Used": false, "Variable": "flag"}]}]} + p { + [valid, q_ast, _] = graphql.parse_and_verify(query, schema) + valid + } + note: graphql_parse_and_verify/success no unused variables + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema_ast := graphql.parse_schema(` + type Employee { + id: String! + salary: Int! + } + + schema { + query: Query + } + + type Query { + employeeByID(id: String): Employee + } + `) + query_ast := graphql.parse_query(` + query { employeeByID(id: "alice") { salary }} + `) + p { + [valid, query_ast, schema_ast] = graphql.parse_and_verify(query_ast, schema_ast) + valid + } + note: graphql_parse_and_verify/success - AST objects - Employee example + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse-query.yaml b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse-query.yaml new file mode 100644 index 000000000000..6c25e0aa7dd8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse-query.yaml @@ -0,0 +1,479 @@ +--- +# This suite of tests is adapted from the underlying GraphQL parser library's +# own test suite, as it provides a fairly comprehensive set of good/degenerate +# test cases, which we want to make sure to react correctly to. +# See: https://github.com/vektah/gqlparser/blob/master/parser/query_test.yml +cases: + # parser provides useful errors: + - data: + modules: + - | + package test + p { + graphql.parse_query(`{`) + } + note: graphql_parse_query/failure-unclosed paren + query: data.test.p = x + want_error: "graphql.parse_query: Expected Name, found in GraphQL string at location 1:2" + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := ` + { ...MissingOn } + fragment MissingOn Type + ` + p { + graphql.parse_query(gql) + } + note: graphql_parse_query/failure-missing on in fragment + query: data.test.p = x + want_error: 'graphql.parse_query: Expected "on", found Name "Type" in GraphQL string at location 3:22' + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := `{ field: {} }` + p { + graphql.parse_query(gql) + } + note: graphql_parse_query/failure-missing name after alias + query: data.test.p = x + want_error: "graphql.parse_query: Expected Name, found { in GraphQL string at location 1:10" + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := `notanoperation Foo { field }` + p { + graphql.parse_query(gql) + } + note: graphql_parse_query/failure-not an operation + query: data.test.p = x + want_error: 'graphql.parse_query: Unexpected Name "notanoperation" in GraphQL string at location 1:1' + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := `...` + p { + graphql.parse_query(gql) + } + note: graphql_parse_query/failure-a wild splat appears + query: data.test.p = x + want_error: "graphql.parse_query: Unexpected ... in GraphQL string at location 1:1" + want_error_code: eval_builtin_error + strict_error: true + # variables: + - data: + modules: + - | + package test + gql := `{ field(complex: { a: { b: [ $var ] } }) }` + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "field", "Arguments": [{"Name": "complex", "Value": {"Children": [{"Name": "a", "Value": {"Children": [{"Name": "b", "Value": {"Children": [{"Name": "", "Value": {"Kind": 0, "Raw": "var"}}], "Kind": 8, "Raw": ""}}], "Kind": 9, "Raw": ""}}], "Kind": 9, "Raw": ""}}], "Name": "field"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-variables are allowed in args + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `query Foo($x: Complex = { a: { b: [ $var ] } }) { field }` + p { + graphql.parse_query(gql) + } + note: graphql_parse_query/failure-variables are not allowed in default args + query: data.test.p = x + want_error: "graphql.parse_query: Unexpected $ in GraphQL string at location 1:37" + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := `query ($withDirective: String @first @second, $withoutDirective: String) { f }` + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "f", "Name": "f"}], "VariableDefinitions": [{"Directives": [{"Location": "", "Name": "first"}, {"Location": "", "Name": "second"}], "Type": {"NamedType": "String", "NonNull": false}, "Used": false, "Variable": "withDirective"}, {"Type": {"NamedType": "String", "NonNull": false}, "Used": false, "Variable": "withoutDirective"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-variables can have directives + query: data.test.p = x + want_result: + - x: true + # fragments: + - data: + modules: + - | + package test + gql := `fragment on on on { on }` + p { + graphql.parse_query(gql) + } + note: graphql_parse_query/failure-fragment can not be named 'on' + query: data.test.p = x + want_error: 'graphql.parse_query: Unexpected Name "on" in GraphQL string at location 1:10' + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := `{ ...on }` + p { + graphql.parse_query(gql) + } + note: graphql_parse_query/failure-fragment can not spread fragments called 'on' + query: data.test.p = x + want_error: "graphql.parse_query: Expected Name, found } in GraphQL string at location 1:9" + want_error_code: eval_builtin_error + strict_error: true + # encoding: + - data: + modules: + - | + package test + gql := ` + # This comment has a ਊ multi-byte character. + { field(arg: "Has a ਊ multi-byte character.") } + ` + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "field", "Arguments": [{"Name": "arg", "Value": {"Kind": 3, "Raw": "Has a ਊ multi-byte character."}}], "Name": "field"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-encoding multibyte characters are supported + query: data.test.p = x + want_result: + - x: true + # keywords are allowed anywhere a name is: + - data: + modules: + - | + package test + gql := ` + query on { + ... a + ... on on { field } + } + fragment a on Type { + on(on: $on) + @on(on: on) + } + ` + ast := {"Fragments": [{"Name": "a", "SelectionSet": [{"Alias": "on", "Arguments": [{"Name": "on", "Value": {"Kind": 0, "Raw": "on"}}], "Directives": [{"Arguments": [{"Name": "on", "Value": {"Kind": 7, "Raw": "on"}}], "Location": "", "Name": "on"}], "Name": "on"}], "TypeCondition": "Type"}], "Operations": [{"Name": "on", "Operation": "query", "SelectionSet": [{"Name": "a"}, {"SelectionSet": [{"Alias": "field", "Name": "field"}], "TypeCondition": "on"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-keywords-allowed-where-names-are on + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := ` + query subscription { + ... subscription + ... on subscription { field } + } + fragment subscription on Type { + subscription(subscription: $subscription) + @subscription(subscription: subscription) + } + ` + ast := {"Fragments": [{"Name": "subscription", "SelectionSet": [{"Alias": "subscription", "Arguments": [{"Name": "subscription", "Value": {"Kind": 0, "Raw": "subscription"}}], "Directives": [{"Arguments": [{"Name": "subscription", "Value": {"Kind": 7, "Raw": "subscription"}}], "Location": "", "Name": "subscription"}], "Name": "subscription"}], "TypeCondition": "Type"}], "Operations": [{"Name": "subscription", "Operation": "query", "SelectionSet": [{"Name": "subscription"}, {"SelectionSet": [{"Alias": "field", "Name": "field"}], "TypeCondition": "subscription"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-keywords-allowed-where-names-are subscription + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := ` + query true { + ... true + ... on true { field } + } + fragment true on Type { + true(true: $true) + @true(true: true) + } + ` + ast := {"Fragments": [{"Name": "true", "SelectionSet": [{"Alias": "true", "Arguments": [{"Name": "true", "Value": {"Kind": 0, "Raw": "true"}}], "Directives": [{"Arguments": [{"Name": "true", "Value": {"Kind": 5, "Raw": "true"}}], "Location": "", "Name": "true"}], "Name": "true"}], "TypeCondition": "Type"}], "Operations": [{"Name": "true", "Operation": "query", "SelectionSet": [{"Name": "true"}, {"SelectionSet": [{"Alias": "field", "Name": "field"}], "TypeCondition": "true"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-keywords-allowed-where-names-are true + query: data.test.p = x + want_result: + - x: true + # operations: + - data: + modules: + - | + package test + gql := `mutation { mutationField }` + ast := {"Operations": [{"Name": "", "Operation": "mutation", "SelectionSet": [{"Alias": "mutationField", "Name": "mutationField"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-operations anonymous mutation + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `mutation Foo { mutationField }` + ast := {"Operations": [{"Name": "Foo", "Operation": "mutation", "SelectionSet": [{"Alias": "mutationField", "Name": "mutationField"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-operations named mutation + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `subscription { subscriptionField }` + ast := {"Operations": [{"Name": "", "Operation": "subscription", "SelectionSet": [{"Alias": "subscriptionField", "Name": "subscriptionField"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-operations anonymous subscription + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `subscription Foo { subscriptionField }` + ast := {"Operations": [{"Name": "Foo", "Operation": "subscription", "SelectionSet": [{"Alias": "subscriptionField", "Name": "subscriptionField"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-operations named subscription + query: data.test.p = x + want_result: + - x: true + # ast: + - data: + modules: + - | + package test + gql := ` + { + node(id: 4) { + id, + name + } + } + ` + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "node", "Arguments": [{"Name": "id", "Value": {"Kind": 1, "Raw": "4"}}], "Name": "node", "SelectionSet": [{"Alias": "id", "Name": "id"}, {"Alias": "name", "Name": "name"}]}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-ast simple query + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := ` + query { + node { + id + } + } + ` + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "node", "Name": "node", "SelectionSet": [{"Alias": "id", "Name": "id"}]}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-ast nameless query with no variables + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `fragment a($v: Boolean = false) on t { f(v: $v) }` + ast := {"Fragments": [{"Name": "a", "SelectionSet": [{"Alias": "f", "Arguments": [{"Name": "v", "Value": {"Kind": 0, "Raw": "v"}}], "Name": "f"}], "TypeCondition": "t", "VariableDefinition": [{"DefaultValue": {"Kind": 5, "Raw": "false"}, "Type": {"NamedType": "Boolean", "NonNull": false}, "Used": false, "Variable": "v"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-ast fragment defined variables + query: data.test.p = x + want_result: + - x: true + # values: + - data: + modules: + - | + package test + gql := `{ f(id: null) }` + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "f", "Arguments": [{"Name": "id", "Value": {"Kind": 6, "Raw": "null"}}], "Name": "f"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-values null + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `{ f(long: """long""", short: "short") }` + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "f", "Arguments": [{"Name": "long", "Value": {"Kind": 4, "Raw": "long"}}, {"Name": "short", "Value": {"Kind": 3, "Raw": "short"}}], "Name": "f"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-values strings + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `{ f(id: [1,2]) }` + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "f", "Arguments": [{"Name": "id", "Value": {"Children": [{"Name": "", "Value": {"Kind": 1, "Raw": "1"}}, {"Name": "", "Value": {"Kind": 1, "Raw": "2"}}], "Kind": 8, "Raw": ""}}], "Name": "f"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-values list + query: data.test.p = x + want_result: + - x: true + # types: + - data: + modules: + - | + package test + gql := `query ($string: String, $int: Int, $arr: [Arr], $notnull: [Arr!]!) { f }` + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "f", "Name": "f"}], "VariableDefinitions": [{"Type": {"NamedType": "String", "NonNull": false}, "Used": false, "Variable": "string"}, {"Type": {"NamedType": "Int", "NonNull": false}, "Used": false, "Variable": "int"}, {"Type": {"Elem": {"NamedType": "Arr", "NonNull": false}, "NamedType": "", "NonNull": false}, "Used": false, "Variable": "arr"}, {"Type": {"Elem": {"NamedType": "Arr", "NonNull": true}, "NamedType": "", "NonNull": true}, "Used": false, "Variable": "notnull"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-types common types + query: data.test.p = x + want_result: + - x: true + # large queries: + - data: + modules: + - | + package test + # Copyright (c) 2015-present, Facebook, Inc. + # + # This source code is licensed under the MIT license found in the + # LICENSE file in the root directory of this source tree. + gql := ` + query queryName($foo: ComplexType, $site: Site = MOBILE) { + whoever123is: node(id: [123, 456]) { + id , + ... on User @defer { + field2 { + id , + alias: field1(first:10, after:$foo,) @include(if: $foo) { + id, + ...frag + } + } + } + ... @skip(unless: $foo) { + id + } + ... { + id + } + } + } + mutation likeStory { + like(story: 123) @defer { + story { + id + } + } + } + subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) { + storyLikeSubscribe(input: $input) { + story { + likers { + count + } + likeSentence { + text + } + } + } + } + fragment frag on Friend { + foo(size: $size, bar: $b, obj: {key: "value", block: """ + block string uses \""" + """}) + } + { + unnamed(truthy: true, falsey: false, nullish: null), + query + } + ` + ast := {"Fragments": [{"Name": "frag", "SelectionSet": [{"Alias": "foo", "Arguments": [{"Name": "size", "Value": {"Kind": 0, "Raw": "size"}}, {"Name": "bar", "Value": {"Kind": 0, "Raw": "b"}}, {"Name": "obj", "Value": {"Children": [{"Name": "key", "Value": {"Kind": 3, "Raw": "value"}}, {"Name": "block", "Value": {"Kind": 4, "Raw": "block string uses \"\"\""}}], "Kind": 9, "Raw": ""}}], "Name": "foo"}], "TypeCondition": "Friend"}], "Operations": [{"Name": "queryName", "Operation": "query", "SelectionSet": [{"Alias": "whoever123is", "Arguments": [{"Name": "id", "Value": {"Children": [{"Name": "", "Value": {"Kind": 1, "Raw": "123"}}, {"Name": "", "Value": {"Kind": 1, "Raw": "456"}}], "Kind": 8, "Raw": ""}}], "Name": "node", "SelectionSet": [{"Alias": "id", "Name": "id"}, {"Directives": [{"Location": "", "Name": "defer"}], "SelectionSet": [{"Alias": "field2", "Name": "field2", "SelectionSet": [{"Alias": "id", "Name": "id"}, {"Alias": "alias", "Arguments": [{"Name": "first", "Value": {"Kind": 1, "Raw": "10"}}, {"Name": "after", "Value": {"Kind": 0, "Raw": "foo"}}], "Directives": [{"Arguments": [{"Name": "if", "Value": {"Kind": 0, "Raw": "foo"}}], "Location": "", "Name": "include"}], "Name": "field1", "SelectionSet": [{"Alias": "id", "Name": "id"}, {"Name": "frag"}]}]}], "TypeCondition": "User"}, {"Directives": [{"Arguments": [{"Name": "unless", "Value": {"Kind": 0, "Raw": "foo"}}], "Location": "", "Name": "skip"}], "SelectionSet": [{"Alias": "id", "Name": "id"}], "TypeCondition": ""}, {"SelectionSet": [{"Alias": "id", "Name": "id"}], "TypeCondition": ""}]}], "VariableDefinitions": [{"Type": {"NamedType": "ComplexType", "NonNull": false}, "Used": false, "Variable": "foo"}, {"DefaultValue": {"Kind": 7, "Raw": "MOBILE"}, "Type": {"NamedType": "Site", "NonNull": false}, "Used": false, "Variable": "site"}]}, {"Name": "likeStory", "Operation": "mutation", "SelectionSet": [{"Alias": "like", "Arguments": [{"Name": "story", "Value": {"Kind": 1, "Raw": "123"}}], "Directives": [{"Location": "", "Name": "defer"}], "Name": "like", "SelectionSet": [{"Alias": "story", "Name": "story", "SelectionSet": [{"Alias": "id", "Name": "id"}]}]}]}, {"Name": "StoryLikeSubscription", "Operation": "subscription", "SelectionSet": [{"Alias": "storyLikeSubscribe", "Arguments": [{"Name": "input", "Value": {"Kind": 0, "Raw": "input"}}], "Name": "storyLikeSubscribe", "SelectionSet": [{"Alias": "story", "Name": "story", "SelectionSet": [{"Alias": "likers", "Name": "likers", "SelectionSet": [{"Alias": "count", "Name": "count"}]}, {"Alias": "likeSentence", "Name": "likeSentence", "SelectionSet": [{"Alias": "text", "Name": "text"}]}]}]}], "VariableDefinitions": [{"Type": {"NamedType": "StoryLikeSubscribeInput", "NonNull": false}, "Used": false, "Variable": "input"}]}, {"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "unnamed", "Arguments": [{"Name": "truthy", "Value": {"Kind": 5, "Raw": "true"}}, {"Name": "falsey", "Value": {"Kind": 5, "Raw": "false"}}, {"Name": "nullish", "Value": {"Kind": 6, "Raw": "null"}}], "Name": "unnamed"}, {"Alias": "query", "Name": "query"}]}]} + p { + graphql.parse_query(gql) == ast + } + note: graphql_parse_query/success-large-queries kitchen sink + query: data.test.p = x + want_result: + - x: true + # fuzzer: + - data: + modules: + - | + package test + gql := `{__typename{...}}` + p { + graphql.parse_query(gql) + } + note: graphql_parse_query/failure-fuzzer 01 + query: data.test.p = x + want_error: "graphql.parse_query: Expected {, found } in GraphQL string at location 1:16" + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := `{...{__typename{...{}}}}` + p { + graphql.parse_query(gql) + } + note: graphql_parse_query/failure-fuzzer 02 + query: data.test.p = x + want_error: "graphql.parse_query: expected at least one definition, found } in GraphQL string at location 1:21" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse-schema.yaml b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse-schema.yaml new file mode 100644 index 000000000000..07ae43f368f2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse-schema.yaml @@ -0,0 +1,688 @@ +--- +# This suite of tests is adapted from the underlying GraphQL parser library's +# own test suite, as it provides a fairly comprehensive set of good/degenerate +# test cases, which we want to make sure to react correctly to. +# See: https://github.com/vektah/gqlparser/blob/master/parser/schema_test.yml +cases: + # object types: + - data: + modules: + - | + package test + gql := ` + type Hello { + world: String + } + ` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-object-types simple + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := ` + "Description" + type Hello { + world: String + } + ` + ast := {"Definitions": [{"BuiltIn": false, "Description": "Description", "Fields": [{"Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-object-types with description + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := ` + """ + Description + """ + type Hello { + world: String + } + type Query { + hello: Hello + } + ` + ast := {"Definitions":[{"BuiltIn":false,"Description":"Description","Fields":[{"Description":"","Name":"world","Type":{"NamedType":"String","NonNull":false}}],"Kind":"OBJECT","Name":"Hello"},{"BuiltIn":false,"Description":"","Fields":[{"Description":"","Name":"hello","Type":{"NamedType":"Hello","NonNull":false}}],"Kind":"OBJECT","Name":"Query"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-object-types with block description + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := ` + type Hello { + world(flag: Boolean): String + } + ` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Arguments": [{"Description": "", "Name": "flag", "Type": {"NamedType": "Boolean", "NonNull": false}}], "Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-object-types with field arg + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := ` + type Hello { + world(flag: Boolean = true): String + } + ` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Arguments": [{"DefaultValue": {"Kind": 5, "Raw": "true"}, "Description": "", "Name": "flag", "Type": {"NamedType": "Boolean", "NonNull": false}}], "Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-object-types with field arg and default value + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := ` + type Hello { + world(things: [String]): String + } + ` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Arguments": [{"Description": "", "Name": "things", "Type": {"Elem": {"NamedType": "String", "NonNull": false}, "NamedType": "", "NonNull": false}}], "Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-object-types with field list arg + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := ` + type Hello { + world(argOne: Boolean, argTwo: Int): String + } + ` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Arguments": [{"Description": "", "Name": "argOne", "Type": {"NamedType": "Boolean", "NonNull": false}}, {"Description": "", "Name": "argTwo", "Type": {"NamedType": "Int", "NonNull": false}}], "Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-object-types with two args + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `type Hello {}` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-object-types must define one or more fields + query: data.test.p = x + want_error: "graphql.parse_schema: expected at least one definition, found } in GraphQL string at location 1:13" + want_error_code: eval_builtin_error + strict_error: true + + # type extensions: + - data: + modules: + - | + package test + gql := ` + extend type Hello { + world: String + } + ` + ast := {"Extensions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-type-extensions Object extension + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `extend type Hello implements Greeting` + ast := {"Extensions": [{"BuiltIn": false, "Description": "", "Interfaces": ["Greeting"], "Kind": "OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-type-extensions without any fields + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := ` + extend type Hello implements Greeting + extend type Hello implements SecondGreeting + ` + ast := {"Extensions": [{"BuiltIn": false, "Description": "", "Interfaces": ["Greeting"], "Kind": "OBJECT", "Name": "Hello"}, {"BuiltIn": false, "Description": "", "Interfaces": ["SecondGreeting"], "Kind": "OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-type-extensions without fields twice + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `extend type Hello` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-type-extensions without anything errors + query: data.test.p = x + want_error: "graphql.parse_schema: Unexpected in GraphQL string at location 1:18" + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := ` + "Description" + extend type Hello { + world: String + } + ` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-type-extensions can have descriptions # hmm, this might not be spec compliant... + query: data.test.p = x + want_error: 'graphql.parse_schema: Unexpected String "Description" in GraphQL string at location 2:4' + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := ` + extend "Description" type Hello { + world: String + } + ` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-type-extensions can not have descriptions on types + query: data.test.p = x + want_error: 'graphql.parse_schema: Unexpected String "Description" in GraphQL string at location 2:11' + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := ` + extend scalar Foo @deprecated + extend type Foo @deprecated + extend interface Foo @deprecated + extend union Foo @deprecated + extend enum Foo @deprecated + extend input Foo @deprecated + ` + ast := {"Extensions": [{"BuiltIn": false, "Description": "", "Directives": [{"Location": "", "Name": "deprecated"}], "Kind": "SCALAR", "Name": "Foo"}, {"BuiltIn": false, "Description": "", "Directives": [{"Location": "", "Name": "deprecated"}], "Kind": "OBJECT", "Name": "Foo"}, {"BuiltIn": false, "Description": "", "Directives": [{"Location": "", "Name": "deprecated"}], "Kind": "INTERFACE", "Name": "Foo"}, {"BuiltIn": false, "Description": "", "Directives": [{"Location": "", "Name": "deprecated"}], "Kind": "UNION", "Name": "Foo"}, {"BuiltIn": false, "Description": "", "Directives": [{"Location": "", "Name": "deprecated"}], "Kind": "ENUM", "Name": "Foo"}, {"BuiltIn": false, "Description": "", "Directives": [{"Location": "", "Name": "deprecated"}], "Kind": "INPUT_OBJECT", "Name": "Foo"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-type-extensions all can have directives + query: data.test.p = x + want_result: + - x: true + # schema definition: + - data: + modules: + - | + package test + gql := ` + schema { + query: Query + } + ` + ast := {"Schema": [{"Description": "", "OperationTypes": [{"Operation": "query", "Type": "Query"}]}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-schema-definition simple + query: data.test.p = x + want_result: + - x: true + # schema extensions: + - data: + modules: + - | + package test + gql := ` + extend schema { + mutation: Mutation + } + ` + ast := {"SchemaExtension": [{"Description": "", "OperationTypes": [{"Operation": "mutation", "Type": "Mutation"}]}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-schema-extensions simple + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `extend schema @directive` + ast := {"SchemaExtension": [{"Description": "", "Directives": [{"Location": "", "Name": "directive"}]}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-schema-extensions directive only + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `extend schema` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-schema-extensions without anything errors + query: data.test.p = x + want_error: "graphql.parse_schema: Unexpected in GraphQL string at location 1:14" + want_error_code: eval_builtin_error + strict_error: true + # inheritance: + - data: + modules: + - | + package test + gql := `type Hello implements World { field: String }` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "field", "Type": {"NamedType": "String", "NonNull": false}}], "Interfaces": ["World"], "Kind": "OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-inheritance single + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `type Hello implements Wo & rld { field: String }` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "field", "Type": {"NamedType": "String", "NonNull": false}}], "Interfaces": ["Wo", "rld"], "Kind": "OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-inheritance multi + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `type Hello implements & Wo & rld { field: String }` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "field", "Type": {"NamedType": "String", "NonNull": false}}], "Interfaces": ["Wo", "rld"], "Kind": "OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-inheritance multi with leading amp + query: data.test.p = x + want_result: + - x: true + # enums: + - data: + modules: + - | + package test + gql := `enum Hello { WORLD }` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "EnumValues": [{"Description": "", "Name": "WORLD"}], "Kind": "ENUM", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-enums single value + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `enum Hello { WO, RLD }` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "EnumValues": [{"Description": "", "Name": "WO"}, {"Description": "", "Name": "RLD"}], "Kind": "ENUM", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/success-enums double value + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `enum Hello {}` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-enums must define one or more unique enum values + query: data.test.p = x + want_error: "graphql.parse_schema: expected at least one definition, found } in GraphQL string at location 1:13" + want_error_code: eval_builtin_error + strict_error: true + # interface: + - data: + modules: + - | + package test + gql := ` + interface Hello { + world: String + } + ` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "INTERFACE", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-interface simple + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `interface Hello {}` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-interface must define one or more fields + query: data.test.p = x + want_error: "graphql.parse_schema: expected at least one definition, found } in GraphQL string at location 1:18" + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := ` + interface IA { + id: ID! + } + interface IIA implements IA { + id: ID! + } + type A implements IIA { + id: ID! + } + ` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "id", "Type": {"NamedType": "ID", "NonNull": true}}], "Kind": "INTERFACE", "Name": "IA"}, {"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "id", "Type": {"NamedType": "ID", "NonNull": true}}], "Interfaces": ["IA"], "Kind": "INTERFACE", "Name": "IIA"}, {"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "id", "Type": {"NamedType": "ID", "NonNull": true}}], "Interfaces": ["IIA"], "Kind": "OBJECT", "Name": "A"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-interface may define intermediate interfaces + query: data.test.p = x + want_result: + - x: true + # unions: + - data: + modules: + - | + package test + gql := `union Hello = World` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Kind": "UNION", "Name": "Hello", "Types": ["World"]}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-unions simple + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `union Hello = Wo | Rld` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Kind": "UNION", "Name": "Hello", "Types": ["Wo", "Rld"]}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-unions with two types + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `union Hello = | Wo | Rld` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Kind": "UNION", "Name": "Hello", "Types": ["Wo", "Rld"]}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-unions with leading pipe + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `union Hello = || Wo | Rld` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-unions cant be empty + query: data.test.p = x + want_error: "graphql.parse_schema: Expected Name, found | in GraphQL string at location 1:16" + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := `union Hello = Wo || Rld` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-unions cant double pipe + query: data.test.p = x + want_error: "graphql.parse_schema: Expected Name, found | in GraphQL string at location 1:19" + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := `union Hello = | Wo | Rld |` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-unions cant have trailing pipe + query: data.test.p = x + want_error: "graphql.parse_schema: Expected Name, found in GraphQL string at location 1:27" + want_error_code: eval_builtin_error + strict_error: true + # scalar: + - data: + modules: + - | + package test + gql := `scalar Hello` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Kind": "SCALAR", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-scalar simple + query: data.test.p = x + want_result: + - x: true + # input object: + - data: + modules: + - | + package test + gql := ` + input Hello { + world: String + } + ` + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "INPUT_OBJECT", "Name": "Hello"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-input-object simple + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := ` + input Hello { + world(foo: Int): String + } + ` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-input-object can not have args + query: data.test.p = x + want_error: "graphql.parse_schema: Expected :, found ( in GraphQL string at location 3:10" + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := `input Hello {}` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-input-object must define one or more input fields + query: data.test.p = x + want_error: "graphql.parse_schema: expected at least one definition, found } in GraphQL string at location 1:14" + want_error_code: eval_builtin_error + strict_error: true + # directives: + - data: + modules: + - | + package test + gql := `directive @foo on FIELD` + ast := {"Directives": [{"Description": "", "IsRepeatable": false, "Locations": ["FIELD"], "Name": "foo"}]} + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/success-directives simple + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := ` + directive @onQuery on QUERY + directive @onMutation on MUTATION + directive @onSubscription on SUBSCRIPTION + directive @onField on FIELD + directive @onFragmentDefinition on FRAGMENT_DEFINITION + directive @onFragmentSpread on FRAGMENT_SPREAD + directive @onInlineFragment on INLINE_FRAGMENT + directive @onVariableDefinition on VARIABLE_DEFINITION + ` + ast := {"Directives": [{"Description": "", "IsRepeatable": false, "Locations": ["QUERY"], "Name": "onQuery"}, {"Description": "", "IsRepeatable": false, "Locations": ["MUTATION"], "Name": "onMutation"}, {"Description": "", "IsRepeatable": false, "Locations": ["SUBSCRIPTION"], "Name": "onSubscription"}, {"Description": "", "IsRepeatable": false, "Locations": ["FIELD"], "Name": "onField"}, {"Description": "", "IsRepeatable": false, "Locations": ["FRAGMENT_DEFINITION"], "Name": "onFragmentDefinition"}, {"Description": "", "IsRepeatable": false, "Locations": ["FRAGMENT_SPREAD"], "Name": "onFragmentSpread"}, {"Description": "", "IsRepeatable": false, "Locations": ["INLINE_FRAGMENT"], "Name": "onInlineFragment"}, {"Description": "", "IsRepeatable": false, "Locations": ["VARIABLE_DEFINITION"], "Name": "onVariableDefinition"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-directives executable + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `directive @foo repeatable on FIELD` + ast := {"Directives": [{"Description": "", "IsRepeatable": true, "Locations": ["FIELD"], "Name": "foo"}]} + p { + graphql.parse_schema(gql) == ast + } + note: graphql_parse_schema/success-directives repeatable + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + gql := `directive @foo on FIELD | INCORRECT_LOCATION` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-directives invalid location + query: data.test.p = x + want_error: 'graphql.parse_schema: Unexpected Name "INCORRECT_LOCATION" in GraphQL string at location 1:27' + want_error_code: eval_builtin_error + strict_error: true + # fuzzer: + - data: + modules: + - | + package test + gql := `type o{d(g:[` + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-fuzzer 1 + query: data.test.p = x + want_error: "graphql.parse_schema: Expected Name, found in GraphQL string at location 1:13" + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + gql := "\"\"\"\r" + p { + graphql.parse_schema(gql) + } + note: graphql_parse_schema/failure-fuzzer 2 + query: data.test.p = x + want_error: "graphql.parse_schema: Unexpected in GraphQL string at location 2:1" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse.yaml b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse.yaml new file mode 100644 index 000000000000..0a722f701a5a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-parse.yaml @@ -0,0 +1,181 @@ +--- +# This suite of tests is adapted from the underlying GraphQL parser library's +# own test suite, as it provides a fairly comprehensive set of good/degenerate +# test cases, which we want to make sure to react correctly to. +# See: https://github.com/vektah/gqlparser/blob/master/validator/validator_test.yml +cases: + - data: + modules: + - | + package test + schema := ` + extend type User { + id: ID! + } + extend type Product { + upc: String! + } + union _Entity = Product | User + extend type Query { + entity: _Entity + } + ` + query := ` + { + entity { + ... on User { + id + } + } + } + ` + q_ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "entity", "Name": "entity", "SelectionSet": [{"SelectionSet": [{"Alias": "id", "Name": "id"}], "TypeCondition": "User"}]}]}]} + p { + [q_ast, _] = graphql.parse(query, schema) + } + note: graphql_parse/success extending non-existent types + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + query := ` + query SomeOperation { + # Note: Not providing mandatory parameter: (myEnum: Locale!) + myAction { + id + } + } + ` + p { + graphql.parse(query, schema) + } + note: graphql_parse/failure validation rules are independent case 1 + query: data.test.p = x + want_error: 'graphql.parse: Field "myAction" argument "myEnum" of type "Locale!" is required, but it was not provided in GraphQL string at location 4:5' + want_error_code: eval_builtin_error + strict_error: true + - data: + modules: + - | + package test + schema := ` + type Query { + x: Int + } + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + query := ` + query SomeOperation ($locale: Locale! = DE) { + myAction(myEnum: $locale) { + id + } + } + ` + q_ast := {"Operations": [{"Name": "SomeOperation", "Operation": "query", "SelectionSet": [{"Alias": "myAction", "Arguments": [{"Name": "myEnum", "Value": {"Kind": 0, "Raw": "locale"}}], "Name": "myAction", "SelectionSet": [{"Alias": "id", "Name": "id"}]}], "VariableDefinitions": [{"DefaultValue": {"Kind": 7, "Raw": "DE"}, "Type": {"NamedType": "Locale", "NonNull": true}, "Used": false, "Variable": "locale"}]}]} + p { + [q_ast, _] = graphql.parse(query, schema) + } + note: graphql_parse/success validation rules are independent case 2 + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type DeprecatedType { + deprecatedField: String @deprecated + newField(deprecatedArg: Int): Boolean + } + enum DeprecatedEnum { + ALPHA @deprecated + } + ` + query := `` + p { + [{}, _] = graphql.parse(query, schema) + } + note: graphql_parse/success deprecating types + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type Query { + bar: String! + } + ` + query := ` + query Foo($flag: Boolean!) { + ...Bar + } + fragment Bar on Query { + bar @include(if: $flag) + } + ` + q_ast := {"Fragments": [{"Name": "Bar", "SelectionSet": [{"Alias": "bar", "Directives": [{"Arguments": [{"Name": "if", "Value": {"Kind": 0, "Raw": "flag"}}], "Location": "", "Name": "include"}], "Name": "bar"}], "TypeCondition": "Query"}], "Operations": [{"Name": "Foo", "Operation": "query", "SelectionSet": [{"Name": "Bar"}], "VariableDefinitions": [{"Type": {"NamedType": "Boolean", "NonNull": true}, "Used": false, "Variable": "flag"}]}]} + p { + [q_ast, _] = graphql.parse(query, schema) + } + note: graphql_parse/success no unused variables + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema_ast := graphql.parse_schema(` + type Employee { + id: String! + salary: Int! + } + + schema { + query: Query + } + + type Query { + employeeByID(id: String): Employee + } + `) + query_ast := graphql.parse_query(` + query { employeeByID(id: "alice") { salary }} + `) + p { + [query_ast, schema_ast] = graphql.parse(query_ast, schema_ast) + } + note: graphql_parse/success - AST objects - Employee example + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-schema-is-valid.yaml b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-schema-is-valid.yaml new file mode 100644 index 000000000000..2b2465a74802 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/graphql/test-graphql-schema-is-valid.yaml @@ -0,0 +1,777 @@ +--- +# This suite of tests is adapted from the underlying GraphQL parser library's +# own test suite, as it provides a fairly comprehensive set of good/degenerate +# test cases, which we want to make sure to react correctly to. +# See: https://github.com/vektah/gqlparser/blob/master/validator/validator_test.yml +cases: + - data: + modules: + - | + package test + schema := ` + extend type User { + id: ID! + } + extend type Product { + upc: String! + } + union _Entity = Product | User + extend type Query { + entity: _Entity + } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success extending non-existent types + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + # We use the unification style from semver's is_valid tests here: + p = x { + x = graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success validation rules are independent case 1 and 2 + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type DeprecatedType { + deprecatedField: String @deprecated + newField(deprecatedArg: Int): Boolean + } + enum DeprecatedEnum { + ALPHA @deprecated + } + ` + query := `` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success deprecating types + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type Query { + bar: String! + } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success no unused variables + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema_ast := graphql.parse_schema(` + type Employee { + id: String! + salary: Int! + } + + schema { + query: Query + } + + type Query { + employeeByID(id: String): Employee + } + `) + p { + graphql.schema_is_valid(schema_ast) + } + note: graphql_schema_is_valid/success - AST objects - Employee example + query: data.test.p = x + want_result: + - x: true + # tests derived from gqlparser/parser/schema_test.yml + # object extensions: + - data: + modules: + - | + package test + schema := ` + type Hello { + world: String + } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-object-extensions simple + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + "Description" + type Hello { + world: String + } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-object-extensions with description + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + """ + Description + """ + # Even with comments between them + type Hello { + world: String + } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-object-extensions with block description + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type Hello { + world(flag: Boolean): String + } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-object-extensions with field arg + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type Hello { + world(flag: Boolean = true): String + } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-object-extensions with field arg and default value + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type Hello { + world(things: [String]): String + } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-object-extensions with field list arg + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type Hello { + world(argOne: Boolean, argTwo: Int): String + } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-object-extensions with two args + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + type Hello {} + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-object-extensions must define one or more fields + query: data.test.p = x + want_result: + - x: false + # type extensions: + - data: + modules: + - | + package test + schema := ` + extend type Hello { + world: String + } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-type-extensions object extension + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + extend type Hello implements Greeting + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-type-extensions without any fields + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + extend type Hello implements Greeting + extend type Hello implements SecondGreeting + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-type-extensions without fields twice + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + extend type Hello + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-type-extensions without anything errors + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + "Description" + extend type Hello { + world: String + } + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-type-extensions can have descriptions + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + extend "Description" type Hello { + world: String + } + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-type-extensions can not have descriptions on types + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + extend scalar Foo @deprecated + extend type Foo @deprecated + extend interface Foo @deprecated + extend union Foo @deprecated + extend enum Foo @deprecated + extend input Foo @deprecated + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-type-extensions all can have directives + query: data.test.p = x + want_result: + - x: false + # schema definition: + - data: + modules: + - | + package test + schema := ` + schema { + query: Query + } + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-schema-definition simple + query: data.test.p = x + want_result: + - x: false + # schema extensions: + - data: + modules: + - | + package test + schema := ` + extend schema { + mutation: Mutation + } + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-schema-extensions simple + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + type Query { + x: Int + } + directive @directive(a: String = "b") on SCHEMA + extend schema @directive + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-schema-extensions directive only + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + extend schema + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-schema-extensions without anything errors + query: data.test.p = x + want_result: + - x: false + # inheritance: + - data: + modules: + - | + package test + schema := ` + type Hello implements World { field: String } + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-inheritance single + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + type Hello implements Wo & rld { field: String } + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-inheritance multi + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + type Hello implements & Wo & rld { field: String } + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-inheritance multi with leading amp + query: data.test.p = x + want_result: + - x: false + # enums: + - data: + modules: + - | + package test + schema := ` + enum Hello { WORLD } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-enums single value + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + enum Hello { WO, RLD } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-enums double value + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + enum Hello {} + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-enums must define one or more unique enum values + query: data.test.p = x + want_result: + - x: false + # interface: + - data: + modules: + - | + package test + schema := ` + interface Hello { + world: String + } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-interface simple + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + interface Hello {} + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-interface must define one or more fields + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + interface IA { + id: ID! + } + interface IIA implements IA { + id: ID! + } + type A implements IIA { + id: ID! + } + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-interface may define intermediate interfaces + query: data.test.p = x + want_result: + - x: false + # unions: + - data: + modules: + - | + package test + schema := ` + union Hello = World + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-unions simple + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + union Hello = Wo | Rld + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-unions with two types + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + union Hello = | Wo | Rld + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-unions with leading pipe + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + union Hello = || Wo | Rld + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-unions cant be empty + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + union Hello = Wo || Rld + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-unions cant double pipe + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + union Hello = | Wo | Rld | + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-unions cant have trailing pipe + query: data.test.p = x + want_result: + - x: false + # scalar: + - data: + modules: + - | + package test + schema := ` + scalar Hello + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-scalar simple + query: data.test.p = x + want_result: + - x: true + # input objects: + - data: + modules: + - | + package test + schema := ` + input Hello { + world: String + } + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-input-objects simple + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + input Hello { + world(foo: Int): String + } + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-input-objects can not have args + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + schema := ` + input Hello {} + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-input-objects must define one or more input fields + query: data.test.p = x + want_result: + - x: false + # directives: + - data: + modules: + - | + package test + schema := ` + directive @foo on FIELD + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-directives simple + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + directive @onQuery on QUERY + directive @onMutation on MUTATION + directive @onSubscription on SUBSCRIPTION + directive @onField on FIELD + directive @onFragmentDefinition on FRAGMENT_DEFINITION + directive @onFragmentSpread on FRAGMENT_SPREAD + directive @onInlineFragment on INLINE_FRAGMENT + directive @onVariableDefinition on VARIABLE_DEFINITION + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-directives executable + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + directive @foo repeatable on FIELD + ` + p { + graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/success-directives repeatable + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + schema := ` + directive @foo on FIELD | INCORRECT_LOCATION + ` + p = x { + x := graphql.schema_is_valid(schema) + } + note: graphql_schema_is_valid/failure-directives invalid location + query: data.test.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/helloworld/test-helloworld-1.yaml b/third_party/opa/v1/test/cases/testdata/v0/helloworld/test-helloworld-1.yaml new file mode 100644 index 000000000000..4d7d8009c7af --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/helloworld/test-helloworld-1.yaml @@ -0,0 +1,50 @@ +--- +# If you are adding new built-in functions to Rego, you must include test cases in this format. +# +# Each test file contains a set of test 'cases'. Each test case includes a globally unique +# name that identifies the test case ('note'), a policy 'query' to execute, and a set of +# expectations (e.g., 'want_result'). Test cases can also assert on error conditions (e.g., +# built-in function errors like divide-by-zero). +# +# * Test cases may include zero or more Rego modules that support the test case. +# * Test cases may set the value of the base documents loaded under 'data' and 'input'. +# +# The result set is unordered. Each element in the result set specifies variable assignments to +# expect from the query. The example below finds a single assignment of the number '7' to the +# variable 'x'. +# +# If you adding tests for a built-in function, prefix the note with the built-in function name. +# Use snake_case_for_the_note. +# +# Many of the test cases include a large blob of generic JSON data. This is an artifact +# of the source code where those cases were exported from. Do not copy the blob into new +# test cases. +# +# The OPA test suite (which is implemented using Go's standard testing framework) discovers tests +# added under ./topdown/testdata/v0/cases. For example, to run only the tests in this file: +# +# go test ./topdown -v -run 'TestRego/helloworld' +# +cases: + - data: + foo: bar + input: + baz: bar + modules: + - | + package test + + p = 7 { + data.foo == q + } + + q = input.baz + note: helloworld/test_case + query: data.test.p = x + want_result: + - x: 7 + - note: helloworld/another_test_for_builtin_error + query: 1 / 0 + strict_error: true + want_error: "div: divide by zero" + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/hexbuiltins/test-hexbuiltins-0939.yaml b/third_party/opa/v1/test/cases/testdata/v0/hexbuiltins/test-hexbuiltins-0939.yaml new file mode 100644 index 000000000000..f9668615b97d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/hexbuiltins/test-hexbuiltins-0939.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: hexbuiltins/hex_encode with string + modules: + - | + package generated + p = x { + hex.encode("lorem ipsum", x) + } + data: + query: data.generated.p = x + want_result: + - x: 6c6f72656d20697073756d diff --git a/third_party/opa/v1/test/cases/testdata/v0/hexbuiltins/test-hexbuiltins-0940.yaml b/third_party/opa/v1/test/cases/testdata/v0/hexbuiltins/test-hexbuiltins-0940.yaml new file mode 100644 index 000000000000..21baa02b412a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/hexbuiltins/test-hexbuiltins-0940.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: hexbuiltins/hex_decode with string + modules: + - | + package generated + p = x { + hex.decode("6c6f72656d20697073756d", x) + } + data: + query: data.generated.p = x + want_result: + - x: lorem ipsum diff --git a/third_party/opa/v1/test/cases/testdata/v0/hexbuiltins/test-hexbuiltins-0941.yaml b/third_party/opa/v1/test/cases/testdata/v0/hexbuiltins/test-hexbuiltins-0941.yaml new file mode 100644 index 000000000000..961a550fcb5f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/hexbuiltins/test-hexbuiltins-0941.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: hexbuiltins/hex_decode with invalid hex encoded string + modules: + - | + package generated + p = x { + hex.decode("fghijkl", x) + } + data: + query: data.generated.p = x + want_error_code: eval_builtin_error + want_error: "invalid byte: U+0067 'g'" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/indexing/array-any.yaml b/third_party/opa/v1/test/cases/testdata/v0/indexing/array-any.yaml new file mode 100644 index 000000000000..a50444469b20 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/indexing/array-any.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + input_term: "{}" + modules: + - | + package indexing + + f(val) { + [_, _] = val + } + + p { + f([1, ["foo", "bar"]]) + } + note: indexing on any and arrays + query: data.indexing.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0758.yaml b/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0758.yaml new file mode 100644 index 000000000000..5416454524c2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0758.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + __local0__ = [1, 2, 3] + __local0__[x] + } + note: indirectreferences/array + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 0 + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0759.yaml b/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0759.yaml new file mode 100644 index 000000000000..bf8780473721 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0759.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + split("foo.bar", ".", __local0__) + __local0__[0] = "foo" + } + note: indirectreferences/call + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0760.yaml b/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0760.yaml new file mode 100644 index 000000000000..3686aae73b67 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0760.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + split("foo.bar:qux", ".", __local0__) + __local2__ = __local0__[_] + split(__local2__, ":", __local1__) + __local1__[i] = x + } + note: indirectreferences/multiple call + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - bar + - foo + - qux diff --git a/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0761.yaml b/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0761.yaml new file mode 100644 index 000000000000..8d0b5af4bf4e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0761.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + fn(__local0__) = [__local0__] + + p[x] { + data.generated.fn(1, __local1__) + x = __local1__[0] + } + note: indirectreferences/user call + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0762.yaml b/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0762.yaml new file mode 100644 index 000000000000..e35ef76ceedd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/indirectreferences/test-indirectreferences-0762.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + fn(__local0__) = [__local0__] + + p[x] { + __local2__ = [y | data.generated.fn(1, __local1__); y = __local1__] + x = __local2__[_][_] + } + note: indirectreferences/user call in comprehension + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0977.yaml b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0977.yaml new file mode 100644 index 000000000000..f035b98756ed --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0977.yaml @@ -0,0 +1,65 @@ +--- +cases: + - data: {} + input_term: '{"foo": 1}' + modules: + - | + package z + + p { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q[x] { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r[x] { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s { + input.req3.a.b.x[0] = 1 + } + + t { + input.req4.a.b.x[0] = 1 + } + + u[x] { + input.req3.a.b[_] = x + x > 1 + } + + w = [[1, 2], [3, 4]] + + gt1 { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] = y { + data.numbers[_] = x + to_number(x, y) + } + + loopback = __local0__ { + true + __local0__ = input + } + + sets { + input.foo[{1}][1] = 1 + } + note: inputvalues/loopback + query: data.z.loopback = x + want_result: + - x: + foo: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0978.yaml b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0978.yaml new file mode 100644 index 000000000000..478d181cfb73 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0978.yaml @@ -0,0 +1,68 @@ +--- +cases: + - data: {} + modules: + - | + package z + + p { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q[x] { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r[x] { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s { + input.req3.a.b.x[0] = 1 + } + + t { + input.req4.a.b.x[0] = 1 + } + + u[x] { + input.req3.a.b[_] = x + x > 1 + } + + w = [[1, 2], [3, 4]] + + gt1 { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] = y { + data.numbers[_] = x + to_number(x, y) + } + + loopback = __local0__ { + true + __local0__ = input + } + + sets { + input.foo[{1}][1] = 1 + } + - | + package topdown_test_partial + + __result__ = _result { + _result = input + } + note: inputvalues/loopback undefined + query: data.z.loopback = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0979.yaml b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0979.yaml new file mode 100644 index 000000000000..31e3069a3ebe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0979.yaml @@ -0,0 +1,75 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + input_term: '{"req1": {"foo": 4}, "req2": {"bar": 4}}' + modules: + - | + package z + + p { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q[x] { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r[x] { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s { + input.req3.a.b.x[0] = 1 + } + + t { + input.req4.a.b.x[0] = 1 + } + + u[x] { + input.req3.a.b[_] = x + x > 1 + } + + w = [[1, 2], [3, 4]] + + gt1 { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] = y { + data.numbers[_] = x + to_number(x, y) + } + + loopback = __local0__ { + true + __local0__ = input + } + + sets { + input.foo[{1}][1] = 1 + } + - | + package topdown_test_partial + + __result__ = _result { + _result = input + } + note: inputvalues/simple + query: data.z.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0980.yaml b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0980.yaml new file mode 100644 index 000000000000..fd35216ec7c7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0980.yaml @@ -0,0 +1,110 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + input_term: '{"req1": {"foo": 4}}' + modules: + - | + package z + + p { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q[x] { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r[x] { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s { + input.req3.a.b.x[0] = 1 + } + + t { + input.req4.a.b.x[0] = 1 + } + + u[x] { + input.req3.a.b[_] = x + x > 1 + } + + w = [[1, 2], [3, 4]] + + gt1 { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] = y { + data.numbers[_] = x + to_number(x, y) + } + + loopback = __local0__ { + true + __local0__ = input + } + + sets { + input.foo[{1}][1] = 1 + } + - | + package topdown_test_partial + + __result__ = _result { + input.req1.foo = 1 + input.req2.bar = 1 + input.req1.foo = 1 + input.req2.bar = 1 + 1 = input.req2.bar + 1 = input.req1.foo + _result = true + } + + __result__ = _result { + input.req1.foo = 2 + input.req2.bar = 2 + input.req1.foo = 2 + input.req2.bar = 2 + 2 = input.req2.bar + 2 = input.req1.foo + _result = true + } + + __result__ = _result { + input.req1.foo = 3 + input.req2.bar = 3 + input.req1.foo = 3 + input.req2.bar = 3 + 3 = input.req2.bar + 3 = input.req1.foo + _result = true + } + + __result__ = _result { + input.req1.foo = 4 + input.req2.bar = 4 + input.req1.foo = 4 + input.req2.bar = 4 + 4 = input.req2.bar + 4 = input.req1.foo + _result = true + } + note: inputvalues/missing + query: data.z.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0981.yaml b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0981.yaml new file mode 100644 index 000000000000..50e7f83d5920 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0981.yaml @@ -0,0 +1,106 @@ +--- +cases: + - data: {} + input_term: '{"req3": {"a": {"b": {"x": [1, 2, 3, 4]}}}}' + modules: + - | + package z + + p { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q[x] { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r[x] { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s { + input.req3.a.b.x[0] = 1 + } + + t { + input.req4.a.b.x[0] = 1 + } + + u[x] { + input.req3.a.b[_] = x + x > 1 + } + + w = [[1, 2], [3, 4]] + + gt1 { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] = y { + data.numbers[_] = x + to_number(x, y) + } + + loopback = __local0__ { + true + __local0__ = input + } + + sets { + input.foo[{1}][1] = 1 + } + - | + package topdown_test_partial + + __result__ = _result { + input.req1.foo = 1 + input.req2.bar = 1 + input.req1.foo = 1 + input.req2.bar = 1 + 1 = input.req2.bar + 1 = input.req1.foo + _result = true + } + + __result__ = _result { + input.req1.foo = 2 + input.req2.bar = 2 + input.req1.foo = 2 + input.req2.bar = 2 + 2 = input.req2.bar + 2 = input.req1.foo + _result = true + } + + __result__ = _result { + input.req1.foo = 3 + input.req2.bar = 3 + input.req1.foo = 3 + input.req2.bar = 3 + 3 = input.req2.bar + 3 = input.req1.foo + _result = true + } + + __result__ = _result { + input.req1.foo = 4 + input.req2.bar = 4 + input.req1.foo = 4 + input.req2.bar = 4 + 4 = input.req2.bar + 4 = input.req1.foo + _result = true + } + note: inputvalues/namespaced + query: data.z.s = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0982.yaml b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0982.yaml new file mode 100644 index 000000000000..d37369cf365e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0982.yaml @@ -0,0 +1,71 @@ +--- +cases: + - data: {} + input_term: '{"req4": {"a": {"b": {"x": [1, 2, 3, 4]}}}}' + modules: + - | + package z + + p { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q[x] { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r[x] { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s { + input.req3.a.b.x[0] = 1 + } + + t { + input.req4.a.b.x[0] = 1 + } + + u[x] { + input.req3.a.b[_] = x + x > 1 + } + + w = [[1, 2], [3, 4]] + + gt1 { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] = y { + data.numbers[_] = x + to_number(x, y) + } + + loopback = __local0__ { + true + __local0__ = input + } + + sets { + input.foo[{1}][1] = 1 + } + - | + package topdown_test_partial + + __result__ = _result { + input.req3.a.b.x[0] = 1 + _result = true + } + note: inputvalues/namespaced with alias + query: data.z.t = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0983.yaml b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0983.yaml new file mode 100644 index 000000000000..d8a998d49866 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/inputvalues/test-inputvalues-0983.yaml @@ -0,0 +1,71 @@ +--- +cases: + - data: {} + input_term: '{"foo": {{1}}}' + modules: + - | + package z + + p { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q[x] { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r[x] { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s { + input.req3.a.b.x[0] = 1 + } + + t { + input.req4.a.b.x[0] = 1 + } + + u[x] { + input.req3.a.b[_] = x + x > 1 + } + + w = [[1, 2], [3, 4]] + + gt1 { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] = y { + data.numbers[_] = x + to_number(x, y) + } + + loopback = __local0__ { + true + __local0__ = input + } + + sets { + input.foo[{1}][1] = 1 + } + - | + package topdown_test_partial + + __result__ = _result { + input.req4.a.b.x[0] = 1 + _result = true + } + note: inputvalues/input set + query: data.z.sets = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0352.yaml b/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0352.yaml new file mode 100644 index 000000000000..af81cf49c657 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0352.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + intersection(set(), x) + } + note: intersection/intersection_0_sets + query: data.generated.p = x + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0353.yaml b/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0353.yaml new file mode 100644 index 000000000000..8ff4695bd95f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0353.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + intersection({set(), {1, 2}}, x) + } + note: intersection/intersection_2_sets + query: data.generated.p = x + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0354.yaml b/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0354.yaml new file mode 100644 index 000000000000..4b71cc1abf04 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0354.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + s1 = {1, 2, 3} + s2 = {2} + intersection({s1, s2}, x) + } + note: intersection/intersection_2_sets + query: data.generated.p = x + want_result: + - x: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0355.yaml b/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0355.yaml new file mode 100644 index 000000000000..16f7057bf438 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0355.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + s1 = {1, 2, 3} + s2 = {2, 3, 4} + s3 = {4, 5, 6} + intersection({s1, s2, s3}, x) + } + note: intersection/intersection_3_sets + query: data.generated.p = x + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0356.yaml b/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0356.yaml new file mode 100644 index 000000000000..23abbcba9010 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/intersection/test-intersection-0356.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + s1 = {"a", "b", "c", "d"} + s2 = {"b", "c", "d"} + s3 = {"c", "d"} + s4 = {"d"} + intersection({s1, s2, s3, s4}, x) + } + note: intersection/intersection_4_sets + query: data.generated.p = x + want_result: + - x: + - d diff --git a/third_party/opa/v1/test/cases/testdata/v0/invalidkeyerror/test-invalidkeyerror-0176.yaml b/third_party/opa/v1/test/cases/testdata/v0/invalidkeyerror/test-invalidkeyerror-0176.yaml new file mode 100644 index 000000000000..ce95a5233a2b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/invalidkeyerror/test-invalidkeyerror-0176.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + http.send({"bad_key": "bad_value", "method": "get", "url": "http://127.0.0.1:51113"}, x) + } + note: invalidkeyerror/invalid keys + query: data.generated.p = x + want_error: 'invalid request parameters(s): {"bad_key"}' + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/invalidkeyerror/test-invalidkeyerror-0177.yaml b/third_party/opa/v1/test/cases/testdata/v0/invalidkeyerror/test-invalidkeyerror-0177.yaml new file mode 100644 index 000000000000..7c1b79983b66 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/invalidkeyerror/test-invalidkeyerror-0177.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + http.send({"method": "get"}, x) + } + note: invalidkeyerror/missing keys + query: data.generated.p = x + want_error: 'missing required request parameters(s): {"url"}' + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-is-valid.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-is-valid.yaml new file mode 100644 index 000000000000..a0380799df57 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-is-valid.yaml @@ -0,0 +1,73 @@ +--- +cases: + - note: jsonbuiltins/json is_valid + query: data.generated.p = x + modules: + - | + package generated + + documents = [ + `plainstring`, + `{`, + `{"json": "ok"}`, + ] + + p = [x | doc = documents[_]; json.is_valid(doc, x)] + strict_error: true + want_result: + - x: + - false + - false + - true + + - note: jsonbuiltins/json is_valid not string + modules: + - | + package generated + + p = x { + json.is_valid(input.foo, x) + } + query: data.generated.p = x + input: { "foo": 1 } + strict_error: true + want_result: + - x: false + + - note: jsonbuiltins/yaml is_valid + query: data.generated.p = x + modules: + - | + package generated + + documents = [ + `foo: + - qux: bar + - baz: 2`, + `foo: + - qux: bar + - baz: {`, + `{"json": "ok"}`, + ] + + p = [x | doc = documents[_]; yaml.is_valid(doc, x)] + strict_error: true + want_result: + - x: + - true + - false + - true + + - note: jsonbuiltins/yaml is_valid not string + modules: + - | + package generated + + p = x { + yaml.is_valid(input.foo, x) + } + query: data.generated.p = x + input: { "foo": 1 } + strict_error: true + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-json-marshal-with-options.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-json-marshal-with-options.yaml new file mode 100644 index 000000000000..f4ad2b6dbe3e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-json-marshal-with-options.yaml @@ -0,0 +1,144 @@ +--- +cases: + - data: {} + modules: + - | + package test + + p = x { + x := json.marshal_with_options([1234500000 + 67890, 1e6 * 2, 1e109 / 1e100], {"indent": " "}) + } + note: jsonbuiltins/marshal_with_options-explicit-indent + query: data.test.p = x + want_result: + - x: |- + [ + 1234567890, + 2000000, + 1000000000 + ] + - data: {} + modules: + - | + package test + + p = x { + x := json.marshal_with_options([1234500000 + 67890, 1e6 * 2, 1e109 / 1e100], {}) + } + note: jsonbuiltins/marshal_with_options-empty-object + query: data.test.p = x + want_result: + - x: "[1234567890,2000000,1000000000]" + - data: {} + modules: + - | + package test + + p = x { + x := json.marshal_with_options([1234500000 + 67890, 1e6 * 2, 1e109 / 1e100], {"pretty": true}) + } + note: jsonbuiltins/marshal_with_options-defaults + query: data.test.p = x + want_result: + - x: |- + [ + 1234567890, + 2000000, + 1000000000 + ] + - data: {} + modules: + - | + package test + + p = x { + x := json.marshal_with_options([1234500000 + 67890, 1e6 * 2, 1e109 / 1e100], {"pretty": false, "prefix": "NO!", "indent": "BAD!"}) + } + note: jsonbuiltins/marshal_with_options-explicit-disable + query: data.test.p = x + want_result: + - x: "[1234567890,2000000,1000000000]" + - data: {} + modules: + - | + package test + + p = x { + x := json.marshal_with_options([1234500000 + 67890, 1e6 * 2, 1e109 / 1e100], {"prefix": "JSON => "}) + } + note: jsonbuiltins/marshal_with_options-prefix + query: data.test.p = x + want_result: + - x: |- + JSON => [ + JSON => 1234567890, + JSON => 2000000, + JSON => 1000000000 + JSON => ] + - data: {} + modules: + - | + package test + + p = x { + x := json.marshal_with_options({"foo": "bar", "bar": "baz"}, {"prefix": "JSON => "}) + } + note: jsonbuiltins/marshal_with_options-object + query: data.test.p = x + want_result: + - x: |- + JSON => { + JSON => "bar": "baz", + JSON => "foo": "bar" + JSON => } + - data: {} + modules: + - | + package test + + p = x { + x := json.marshal_with_options([], {"indent": " ", "prefix": "---"}) + } + note: jsonbuiltins/marshal_with_options-empty-array + query: data.test.p = x + want_result: + - x: |- + ---[] + - data: {} + modules: + - | + package test + + p = x { + x := json.marshal_with_options([[[[[[[]]]]]]], {"indent": " "}) + } + note: jsonbuiltins/marshal_with_options-deep-array + query: data.test.p = x + want_result: + - x: |- + [ + [ + [ + [ + [ + [ + [] + ] + ] + ] + ] + ] + ] + - data: {} + modules: + - | + package test + + p = x { + x := json.marshal_with_options([], {"indent": " ", "include_winning_lottery_numbers": true}) + } + note: jsonbuiltins/marshal_with_options-invalid-key + query: data.test.p = x + strict_error: true + want_error: object contained unknown key "include_winning_lottery_numbers" + want_error_code: eval_type_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0924.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0924.yaml new file mode 100644 index 000000000000..36cc4376e150 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0924.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + json.marshal([{"foo": {1, 2, 3}}], x) + } + note: jsonbuiltins/marshal + query: data.generated.p = x + want_result: + - x: '[{"foo":[1,2,3]}]' diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0925.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0925.yaml new file mode 100644 index 000000000000..682632cb2e8c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0925.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + json.unmarshal("[{\"foo\":[1,2,3]}]", x) + } + note: jsonbuiltins/unmarshal + query: data.generated.p = x + want_result: + - x: + - foo: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0926.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0926.yaml new file mode 100644 index 000000000000..1387c672c214 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0926.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = x { + __local0__ = data.a[0] + json.unmarshal(__local0__, x) + } + note: jsonbuiltins/unmarshal-non-string + query: data.generated.p = x + strict_error: true + want_error: operand 1 must be string but got number + want_error_code: eval_type_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0927.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0927.yaml new file mode 100644 index 000000000000..cb8f9e779eac --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0927.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = y { + yaml.marshal([{"foo": {1, 2, 3}}], x) + yaml.unmarshal(x, y) + } + note: jsonbuiltins/yaml round-trip + query: data.generated.p = x + want_result: + - x: + - foo: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0928.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0928.yaml new file mode 100644 index 000000000000..479f27afa5ff --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-jsonbuiltins-0928.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p { + yaml.unmarshal("[1,2,3", _) + } + note: jsonbuiltins/yaml unmarshal error + query: data.generated.p = x + want_error: "yaml: line 1: did not find" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-marshal-large-ints.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-marshal-large-ints.yaml new file mode 100644 index 000000000000..089b9c56eb54 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonbuiltins/test-marshal-large-ints.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package test + + p = x { + json.marshal([1234500000 + 67890, 1e6 * 2, 1e109 / 1e100], x) + } + note: jsonbuiltins/marshal large integers + query: data.test.p = x + want_result: + - x: "[1234567890,2000000,1000000000]" diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0218.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0218.yaml new file mode 100644 index 000000000000..0342b79f0e7b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0218.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a/b/c"}, __local1__) + __local0__ = __local1__ + } + note: jsonfilter/base + query: data.generated.p = x + want_result: + - x: + a: + b: + c: 7 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0219.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0219.yaml new file mode 100644 index 000000000000..3e3fde2e4fbb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0219.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a/b/c", "e"}, __local1__) + __local0__ = __local1__ + } + note: jsonfilter/multiple roots + query: data.generated.p = x + want_result: + - x: + a: + b: + c: 7 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0220.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0220.yaml new file mode 100644 index 000000000000..47d39a141bc3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0220.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, ["a/b/c", "e"], __local1__) + __local0__ = __local1__ + } + note: jsonfilter/multiple roots array + query: data.generated.p = x + want_result: + - x: + a: + b: + c: 7 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0221.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0221.yaml new file mode 100644 index 000000000000..8aaaec7998ec --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0221.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.filter({"a": {"b": {"c": 7, "d": 8}, "e": 9}}, {"a/b/c", "a/e"}, __local1__) + __local0__ = __local1__ + } + note: jsonfilter/shared roots + query: data.generated.p = x + want_result: + - x: + a: + b: + c: 7 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0222.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0222.yaml new file mode 100644 index 000000000000..2e29733c7b39 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0222.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.filter({"a": {"b": 7}}, {"a", "a/b"}, __local1__) + __local0__ = __local1__ + } + note: jsonfilter/conflict + query: data.generated.p = x + want_result: + - x: + a: + b: 7 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0223.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0223.yaml new file mode 100644 index 000000000000..a4b413d52d78 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0223.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.filter({"a": 7}, set(), __local1__) + __local0__ = __local1__ + } + note: jsonfilter/empty list + query: data.generated.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0224.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0224.yaml new file mode 100644 index 000000000000..4822ad4c06e1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0224.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.filter({}, {"a/b"}, __local1__) + __local0__ = __local1__ + } + note: jsonfilter/empty object + query: data.generated.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0225.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0225.yaml new file mode 100644 index 000000000000..120008ac79a7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0225.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.filter({"a": [{"b": 7, "c": 8}, {"d": 9}]}, {"a/0/b", "a/1"}, __local1__) + __local0__ = __local1__ + } + note: jsonfilter/arrays + query: data.generated.p = x + want_result: + - x: + a: + - b: 7 + - d: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0226.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0226.yaml new file mode 100644 index 000000000000..d546a4af53ed --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0226.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.filter({"a": [{"1": ["b", "c", "d"]}, {"x": "y"}]}, {"a/0/1/2"}, __local1__) + __local0__ = __local1__ + } + note: jsonfilter/object with number keys + query: data.generated.p = x + want_result: + - x: + a: + - "1": + - d diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0227.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0227.yaml new file mode 100644 index 000000000000..d05efd3c7f3e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0227.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {["a", "b", "c"], ["e"]}, __local1__) + __local0__ = __local1__ + } + note: jsonfilter/arrays of roots + query: data.generated.p = x + want_result: + - x: + a: + b: + c: 7 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0228.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0228.yaml new file mode 100644 index 000000000000..9ea433a925ba --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonfilter/test-jsonfilter-0228.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.filter({"a": {"b": {"c": 7, "d": 8, "x": 0}}, "e": 9}, {"a/b/d", ["a", "b", "c"]}, __local1__) + __local0__ = __local1__ + } + note: jsonfilter/mixed root types + query: data.generated.p = x + want_result: + - x: + a: + b: + c: 7 + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonfilteridempotent/test-jsonfilteridempotent-0229.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonfilteridempotent/test-jsonfilteridempotent-0229.yaml new file mode 100644 index 000000000000..f627c895faf6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonfilteridempotent/test-jsonfilteridempotent-0229.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + __local0__ = {"a": {"b": 2, "c": 3}} + json.filter(__local0__, {"a/b"}, __local1__) + __local1__ = {"a": {"b": 2}} + json.filter(__local0__, {"a/c"}, __local2__) + __local2__ = {"a": {"c": 3}} + __local0__ = {"a": {"b": 2, "c": 3}} + } + note: jsonfilteridempotent/TestBuiltinJSONFilterIdempotent + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonpatch/coverage.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonpatch/coverage.yaml new file mode 100644 index 000000000000..1d20201fdac2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonpatch/coverage.yaml @@ -0,0 +1,12 @@ +--- +cases: + # These tests cover mostly error scenarios. + - note: jsonpatch/set-failure add-to-bad-path + query: data.main.result = x + want_result: [] + modules: + - | + package main + doc = [1, 2, 3] + patch = [{"op": "add", "path": "1.2", "value": "foo"}] + result = r {r = json.patch(doc, patch)} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonpatch/json-patch-tests.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonpatch/json-patch-tests.yaml new file mode 100644 index 000000000000..0223a778808f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonpatch/json-patch-tests.yaml @@ -0,0 +1,776 @@ +--- +cases: + - note: jsonpatch/json_patch_tests + # In the main module, we construct an object of failed cases. Using this + # together with `want_result` works well since `go test` will show us + # which cases failed. + # + # This allows us to paste in JSON spec tests almost verbatim: see the modules + # at the end of this case. + query: data.main.failed_cases = x + want_result: + - x: {} + modules: + - | + package main + + # Grab all cases from the modules inside `data.json_patch_cases` and + # construct an object with readable index names. + all_cases[k] = t { + t := data.json_patch_cases[p].cases[i] + k := sprintf("%s/%d", [p, i]) + } + + # Go through `all_cases` and select the ones that pass. + passed_cases[k] = t { + t := all_cases[k] + t.expected == json.patch(t.doc, [p | p = t.patch[_]]) + } { + # Some cases ("test" ones in particular) don't have an expected value but + # are still tests that we want to run. + t := all_cases[k] + not t.expected + not t.error + _ = json.patch(t.doc, [p | p = t.patch[_]]) + } { + # These are cases that are expected to not return a result, for example + # there's an invalid index or a "test" fails. + t := all_cases[k] + _ = t.error + not json.patch(t.doc, [p | p = t.patch[_]]) + } { + # Some cases are specifically disabled for the OPA implementation. + t := all_cases[k] + t.opa_disabled == true + } + + # `failed_cases` is simply the cases that didn't pass. + failed_cases[k] = t { + t := all_cases[k] + not passed_cases[k] + } + + # Source: + - | + package json_patch_cases.json_spec_tests + cases = [ + { + "comment": "4.1. add with missing object", + "doc": { "q": { "bar": 2 } }, + "patch": [ {"op": "add", "path": "/a/b", "value": 1} ], + "error": + "path /a does not exist -- missing objects are not created recursively" + }, + + { + "comment": "A.1. Adding an Object Member", + "doc": { + "foo": "bar" + }, + "patch": [ + { "op": "add", "path": "/baz", "value": "qux" } + ], + "expected": { + "baz": "qux", + "foo": "bar" + } + }, + + { + "comment": "A.2. Adding an Array Element", + "doc": { + "foo": [ "bar", "baz" ] + }, + "patch": [ + { "op": "add", "path": "/foo/1", "value": "qux" } + ], + "expected": { + "foo": [ "bar", "qux", "baz" ] + } + }, + + { + "comment": "A.3. Removing an Object Member", + "doc": { + "baz": "qux", + "foo": "bar" + }, + "patch": [ + { "op": "remove", "path": "/baz" } + ], + "expected": { + "foo": "bar" + } + }, + + { + "comment": "A.4. Removing an Array Element", + "doc": { + "foo": [ "bar", "qux", "baz" ] + }, + "patch": [ + { "op": "remove", "path": "/foo/1" } + ], + "expected": { + "foo": [ "bar", "baz" ] + } + }, + + { + "comment": "A.5. Replacing a Value", + "doc": { + "baz": "qux", + "foo": "bar" + }, + "patch": [ + { "op": "replace", "path": "/baz", "value": "boo" } + ], + "expected": { + "baz": "boo", + "foo": "bar" + } + }, + + { + "comment": "A.6. Moving a Value", + "doc": { + "foo": { + "bar": "baz", + "waldo": "fred" + }, + "qux": { + "corge": "grault" + } + }, + "patch": [ + { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" } + ], + "expected": { + "foo": { + "bar": "baz" + }, + "qux": { + "corge": "grault", + "thud": "fred" + } + } + }, + + { + "comment": "A.7. Moving an Array Element", + "doc": { + "foo": [ "all", "grass", "cows", "eat" ] + }, + "patch": [ + { "op": "move", "from": "/foo/1", "path": "/foo/3" } + ], + "expected": { + "foo": [ "all", "cows", "eat", "grass" ] + } + + }, + + { + "comment": "A.8. Testing a Value: Success", + "doc": { + "baz": "qux", + "foo": [ "a", 2, "c" ] + }, + "patch": [ + { "op": "test", "path": "/baz", "value": "qux" }, + { "op": "test", "path": "/foo/1", "value": 2 } + ], + "expected": { + "baz": "qux", + "foo": [ "a", 2, "c" ] + } + }, + + { + "comment": "A.9. Testing a Value: Error", + "doc": { + "baz": "qux" + }, + "patch": [ + { "op": "test", "path": "/baz", "value": "bar" } + ], + "error": "string not equivalent" + }, + + { + "comment": "A.10. Adding a nested Member Object", + "doc": { + "foo": "bar" + }, + "patch": [ + { "op": "add", "path": "/child", "value": { "grandchild": { } } } + ], + "expected": { + "foo": "bar", + "child": { + "grandchild": { + } + } + } + }, + + { + "comment": "A.11. Ignoring Unrecognized Elements", + "doc": { + "foo":"bar" + }, + "patch": [ + { "op": "add", "path": "/baz", "value": "qux", "xyz": 123 } + ], + "expected": { + "foo":"bar", + "baz":"qux" + } + }, + + { + "comment": "A.12. Adding to a Non-existent Target", + "doc": { + "foo": "bar" + }, + "patch": [ + { "op": "add", "path": "/baz/bat", "value": "qux" } + ], + "error": "add to a non-existent target" + }, + + { + "comment": "A.13 Invalid JSON Patch Document", + "doc": { + "foo": "bar" + }, + "patch": [ + { "op": "add", "path": "/baz", "value": "qux", "op": "remove" } + ], + "error": "operation has two 'op' members", + "disabled": true + }, + + { + "comment": "A.14. ~ Escape Ordering", + "doc": { + "/": 9, + "~1": 10 + }, + "patch": [{"op": "test", "path": "/~01", "value": 10}], + "expected": { + "/": 9, + "~1": 10 + } + }, + + { + "comment": "A.15. Comparing Strings and Numbers", + "doc": { + "/": 9, + "~1": 10 + }, + "patch": [{"op": "test", "path": "/~01", "value": "10"}], + "error": "number is not equal to string" + }, + + { + "comment": "A.16. Adding an Array Value", + "doc": { + "foo": ["bar"] + }, + "patch": [{ "op": "add", "path": "/foo/-", "value": ["abc", "def"] }], + "expected": { + "foo": ["bar", ["abc", "def"]] + } + } + ] + + # Source: + - | + package json_patch_cases.json_tests + cases = [ + { "comment": "empty list, empty docs", + "doc": {}, + "patch": [], + "expected": {} }, + + { "comment": "empty patch list", + "doc": {"foo": 1}, + "patch": [], + "expected": {"foo": 1} }, + + { "comment": "rearrangements OK?", + "doc": {"foo": 1, "bar": 2}, + "patch": [], + "expected": {"bar":2, "foo": 1} }, + + { "comment": "rearrangements OK? How about one level down ... array", + "doc": [{"foo": 1, "bar": 2}], + "patch": [], + "expected": [{"bar":2, "foo": 1}] }, + + { "comment": "rearrangements OK? How about one level down...", + "doc": {"foo":{"foo": 1, "bar": 2}}, + "patch": [], + "expected": {"foo":{"bar":2, "foo": 1}} }, + + { "comment": "add replaces any existing field", + "doc": {"foo": null}, + "patch": [{"op": "add", "path": "/foo", "value":1}], + "expected": {"foo": 1} }, + + { "comment": "toplevel array", + "doc": [], + "patch": [{"op": "add", "path": "/0", "value": "foo"}], + "expected": ["foo"] }, + + { "comment": "toplevel array, no change", + "doc": ["foo"], + "patch": [], + "expected": ["foo"] }, + + { "comment": "toplevel object, numeric string", + "doc": {}, + "patch": [{"op": "add", "path": "/foo", "value": "1"}], + "expected": {"foo":"1"} }, + + { "comment": "toplevel object, integer", + "doc": {}, + "patch": [{"op": "add", "path": "/foo", "value": 1}], + "expected": {"foo":1} }, + + { "comment": "Toplevel scalar values OK?", + "doc": "foo", + "patch": [{"op": "replace", "path": "", "value": "bar"}], + "expected": "bar", + "disabled": true }, + + { "comment": "replace object document with array document?", + "doc": {}, + "patch": [{"op": "add", "path": "", "value": []}], + "expected": [] }, + + { "comment": "replace array document with object document?", + "doc": [], + "patch": [{"op": "add", "path": "", "value": {}}], + "expected": {} }, + + { "comment": "append to root array document?", + "doc": [], + "patch": [{"op": "add", "path": "/-", "value": "hi"}], + "expected": ["hi"] }, + + { "comment": "Add, / target", + "doc": {}, + "patch": [ {"op": "add", "path": "/", "value":1 } ], + "expected": {"":1} }, + + { "comment": "Add, /foo/ deep target (trailing slash)", + "doc": {"foo": {}}, + "patch": [ {"op": "add", "path": "/foo/", "value":1 } ], + "expected": {"foo":{"": 1}} }, + + { "comment": "Add composite value at top level", + "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/bar", "value": [1, 2]}], + "expected": {"foo": 1, "bar": [1, 2]} }, + + { "comment": "Add into composite value", + "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "add", "path": "/baz/0/foo", "value": "world"}], + "expected": {"foo": 1, "baz": [{"qux": "hello", "foo": "world"}]} }, + + { "doc": {"bar": [1, 2]}, + "patch": [{"op": "add", "path": "/bar/8", "value": "5"}], + "error": "Out of bounds (upper)" }, + + { "doc": {"bar": [1, 2]}, + "patch": [{"op": "add", "path": "/bar/-1", "value": "5"}], + "error": "Out of bounds (lower)" }, + + { "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/bar", "value": true}], + "expected": {"foo": 1, "bar": true} }, + + { "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/bar", "value": false}], + "expected": {"foo": 1, "bar": false} }, + + { "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/bar", "value": null}], + "expected": {"foo": 1, "bar": null} }, + + { "comment": "0 can be an array index or object element name", + "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/0", "value": "bar"}], + "expected": {"foo": 1, "0": "bar" } }, + + { "doc": ["foo"], + "patch": [{"op": "add", "path": "/1", "value": "bar"}], + "expected": ["foo", "bar"] }, + + { "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/1", "value": "bar"}], + "expected": ["foo", "bar", "sil"] }, + + { "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/0", "value": "bar"}], + "expected": ["bar", "foo", "sil"] }, + + { "comment": "push item to array via last index + 1", + "doc": ["foo", "sil"], + "patch": [{"op":"add", "path": "/2", "value": "bar"}], + "expected": ["foo", "sil", "bar"] }, + + { "comment": "add item to array at index > length should fail", + "doc": ["foo", "sil"], + "patch": [{"op":"add", "path": "/3", "value": "bar"}], + "error": "index is greater than number of items in array" }, + + { "comment": "test against implementation-specific numeric parsing", + "doc": {"1e0": "foo"}, + "patch": [{"op": "test", "path": "/1e0", "value": "foo"}], + "expected": {"1e0": "foo"} }, + + { "comment": "test with bad number should fail", + "doc": ["foo", "bar"], + "patch": [{"op": "test", "path": "/1e0", "value": "bar"}], + "error": "test op shouldn't get array element 1" }, + + { "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/bar", "value": 42}], + "error": "Object operation on array target" }, + + { "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/1", "value": ["bar", "baz"]}], + "expected": ["foo", ["bar", "baz"], "sil"], + "comment": "value in array add not flattened" }, + + { "doc": {"foo": 1, "bar": [1, 2, 3, 4]}, + "patch": [{"op": "remove", "path": "/bar"}], + "expected": {"foo": 1} }, + + { "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "remove", "path": "/baz/0/qux"}], + "expected": {"foo": 1, "baz": [{}]} }, + + { "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "replace", "path": "/foo", "value": [1, 2, 3, 4]}], + "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]} }, + + { "doc": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]}, + "patch": [{"op": "replace", "path": "/baz/0/qux", "value": "world"}], + "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "world"}]} }, + + { "doc": ["foo"], + "patch": [{"op": "replace", "path": "/0", "value": "bar"}], + "expected": ["bar"] }, + + { "doc": [""], + "patch": [{"op": "replace", "path": "/0", "value": 0}], + "expected": [0] }, + + { "doc": [""], + "patch": [{"op": "replace", "path": "/0", "value": true}], + "expected": [true] }, + + { "doc": [""], + "patch": [{"op": "replace", "path": "/0", "value": false}], + "expected": [false] }, + + { "doc": [""], + "patch": [{"op": "replace", "path": "/0", "value": null}], + "expected": [null] }, + + { "doc": ["foo", "sil"], + "patch": [{"op": "replace", "path": "/1", "value": ["bar", "baz"]}], + "expected": ["foo", ["bar", "baz"]], + "comment": "value in array replace not flattened" }, + + { "comment": "replace whole document", + "doc": {"foo": "bar"}, + "patch": [{"op": "replace", "path": "", "value": {"baz": "qux"}}], + "expected": {"baz": "qux"} }, + + { "comment": "test replace with missing parent key should fail", + "doc": {"bar": "baz"}, + "patch": [{"op": "replace", "path": "/foo/bar", "value": false}], + "error": "replace op should fail with missing parent key" }, + + { "comment": "spurious patch properties", + "doc": {"foo": 1}, + "patch": [{"op": "test", "path": "/foo", "value": 1, "spurious": 1}], + "expected": {"foo": 1} }, + + { "doc": {"foo": null}, + "patch": [{"op": "test", "path": "/foo", "value": null}], + "expected": {"foo": null}, + "comment": "null value should be valid obj property" }, + + { "doc": {"foo": null}, + "patch": [{"op": "replace", "path": "/foo", "value": "truthy"}], + "expected": {"foo": "truthy"}, + "comment": "null value should be valid obj property to be replaced with something truthy" }, + + { "doc": {"foo": null}, + "patch": [{"op": "move", "from": "/foo", "path": "/bar"}], + "expected": {"bar": null}, + "comment": "null value should be valid obj property to be moved" }, + + { "doc": {"foo": null}, + "patch": [{"op": "copy", "from": "/foo", "path": "/bar"}], + "expected": {"foo": null, "bar": null}, + "comment": "null value should be valid obj property to be copied" }, + + { "doc": {"foo": null}, + "patch": [{"op": "remove", "path": "/foo"}], + "expected": {}, + "comment": "null value should be valid obj property to be removed" }, + + { "doc": {"foo": "bar"}, + "patch": [{"op": "replace", "path": "/foo", "value": null}], + "expected": {"foo": null}, + "comment": "null value should still be valid obj property replace other value" }, + + { "doc": {"foo": {"foo": 1, "bar": 2}}, + "patch": [{"op": "test", "path": "/foo", "value": {"bar": 2, "foo": 1}}], + "expected": {"foo": {"foo": 1, "bar": 2}}, + "comment": "test should pass despite rearrangement" }, + + { "doc": {"foo": [{"foo": 1, "bar": 2}]}, + "patch": [{"op": "test", "path": "/foo", "value": [{"bar": 2, "foo": 1}]}], + "expected": {"foo": [{"foo": 1, "bar": 2}]}, + "comment": "test should pass despite (nested) rearrangement" }, + + { "doc": {"foo": {"bar": [1, 2, 5, 4]}}, + "patch": [{"op": "test", "path": "/foo", "value": {"bar": [1, 2, 5, 4]}}], + "expected": {"foo": {"bar": [1, 2, 5, 4]}}, + "comment": "test should pass - no error" }, + + { "doc": {"foo": {"bar": [1, 2, 5, 4]}}, + "patch": [{"op": "test", "path": "/foo", "value": [1, 2]}], + "error": "test op should fail" }, + + { "comment": "Whole document", + "doc": { "foo": 1 }, + "patch": [{"op": "test", "path": "", "value": {"foo": 1}}], + "disabled": true }, + + { "comment": "Empty-string element", + "doc": { "": 1 }, + "patch": [{"op": "test", "path": "/", "value": 1}], + "expected": { "": 1 } }, + + { "doc": { + "foo": ["bar", "baz"], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8 + }, + "patch": [{"op": "test", "path": "/foo", "value": ["bar", "baz"]}, + {"op": "test", "path": "/foo/0", "value": "bar"}, + {"op": "test", "path": "/", "value": 0}, + {"op": "test", "path": "/a~1b", "value": 1}, + {"op": "test", "path": "/c%d", "value": 2}, + {"op": "test", "path": "/e^f", "value": 3}, + {"op": "test", "path": "/g|h", "value": 4}, + {"op": "test", "path": "/i\\j", "value": 5}, + {"op": "test", "path": "/k\"l", "value": 6}, + {"op": "test", "path": "/ ", "value": 7}, + {"op": "test", "path": "/m~0n", "value": 8}], + "expected": { + "": 0, + " ": 7, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "foo": [ + "bar", + "baz" + ], + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + "m~n": 8 + } + }, + { "comment": "Move to same location has no effect", + "doc": {"foo": 1}, + "patch": [{"op": "move", "from": "/foo", "path": "/foo"}], + "expected": {"foo": 1} }, + + { "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "move", "from": "/foo", "path": "/bar"}], + "expected": {"baz": [{"qux": "hello"}], "bar": 1} }, + + { "doc": {"baz": [{"qux": "hello"}], "bar": 1}, + "patch": [{"op": "move", "from": "/baz/0/qux", "path": "/baz/1"}], + "expected": {"baz": [{}, "hello"], "bar": 1} }, + + { "doc": {"baz": [{"qux": "hello"}], "bar": 1}, + "patch": [{"op": "copy", "from": "/baz/0", "path": "/boo"}], + "expected": {"baz":[{"qux":"hello"}],"bar":1,"boo":{"qux":"hello"}} }, + + { "comment": "replacing the root of the document is possible with add", + "doc": {"foo": "bar"}, + "patch": [{"op": "add", "path": "", "value": {"baz": "qux"}}], + "expected": {"baz":"qux"}}, + + { "comment": "Adding to \"/-\" adds to the end of the array", + "doc": [ 1, 2 ], + "patch": [ { "op": "add", "path": "/-", "value": { "foo": [ "bar", "baz" ] } } ], + "expected": [ 1, 2, { "foo": [ "bar", "baz" ] } ]}, + + { "comment": "Adding to \"/-\" adds to the end of the array, even n levels down", + "doc": [ 1, 2, [ 3, [ 4, 5 ] ] ], + "patch": [ { "op": "add", "path": "/2/1/-", "value": { "foo": [ "bar", "baz" ] } } ], + "expected": [ 1, 2, [ 3, [ 4, 5, { "foo": [ "bar", "baz" ] } ] ] ]}, + + { "comment": "test remove with bad number should fail", + "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "remove", "path": "/baz/1e0/qux"}], + "error": "remove op shouldn't remove from array with bad number" }, + + { "comment": "test remove on array", + "doc": [1, 2, 3, 4], + "patch": [{"op": "remove", "path": "/0"}], + "expected": [2, 3, 4] }, + + { "comment": "test repeated removes", + "doc": [1, 2, 3, 4], + "patch": [{ "op": "remove", "path": "/1" }, + { "op": "remove", "path": "/2" }], + "expected": [1, 3] }, + + { "comment": "test remove with bad index should fail", + "doc": [1, 2, 3, 4], + "patch": [{"op": "remove", "path": "/1e0"}], + "error": "remove op shouldn't remove from array with bad number" }, + + { "comment": "test replace with bad number should fail", + "doc": [""], + "patch": [{"op": "replace", "path": "/1e0", "value": false}], + "error": "replace op shouldn't replace in array with bad number" }, + + { "comment": "test copy with bad number should fail", + "doc": {"baz": [1,2,3], "bar": 1}, + "patch": [{"op": "copy", "from": "/baz/1e0", "path": "/boo"}], + "error": "copy op shouldn't work with bad number" }, + + { "comment": "test move with bad number should fail", + "doc": {"foo": 1, "baz": [1,2,3,4]}, + "patch": [{"op": "move", "from": "/baz/1e0", "path": "/foo"}], + "error": "move op shouldn't work with bad number" }, + + { "comment": "test add with bad number should fail", + "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/1e0", "value": "bar"}], + "error": "add op shouldn't add to array with bad number" }, + + { "comment": "missing 'path' parameter", + "doc": {}, + "patch": [ { "op": "add", "value": "bar" } ], + "error": "missing 'path' parameter" }, + + { "comment": "'path' parameter with null value", + "doc": {}, + "patch": [ { "op": "add", "path": null, "value": "bar" } ], + "error": "null is not valid value for 'path'" }, + + { "comment": "invalid JSON Pointer token", + "opa_disabled": true, + "doc": {}, + "patch": [ { "op": "add", "path": "foo", "value": "bar" } ], + "error": "JSON Pointer should start with a slash" }, + + { "comment": "missing 'value' parameter to add", + "doc": [ 1 ], + "patch": [ { "op": "add", "path": "/-" } ], + "error": "missing 'value' parameter" }, + + { "comment": "missing 'value' parameter to replace", + "doc": [ 1 ], + "patch": [ { "op": "replace", "path": "/0" } ], + "error": "missing 'value' parameter" }, + + { "comment": "missing 'value' parameter to test", + "doc": [ null ], + "patch": [ { "op": "test", "path": "/0" } ], + "error": "missing 'value' parameter" }, + + { "comment": "missing value parameter to test - where undef is falsy", + "doc": [ false ], + "patch": [ { "op": "test", "path": "/0" } ], + "error": "missing 'value' parameter" }, + + { "comment": "missing from parameter to copy", + "doc": [ 1 ], + "patch": [ { "op": "copy", "path": "/-" } ], + "error": "missing 'from' parameter" }, + + { "comment": "missing from location to copy", + "doc": { "foo": 1 }, + "patch": [ { "op": "copy", "from": "/bar", "path": "/foo" } ], + "error": "missing 'from' location" }, + + { "comment": "missing from parameter to move", + "doc": { "foo": 1 }, + "patch": [ { "op": "move", "path": "" } ], + "error": "missing 'from' parameter" }, + + { "comment": "missing from location to move", + "doc": { "foo": 1 }, + "patch": [ { "op": "move", "from": "/bar", "path": "/foo" } ], + "error": "missing 'from' location" }, + + { "comment": "duplicate ops", + "opa_disabled": true, + "doc": { "foo": "bar" }, + "patch": [ { "op": "add", "path": "/baz", "value": "qux", + "op": "move", "from":"/foo" } ], + "error": "patch has two 'op' members", + "disabled": true }, + + { "comment": "unrecognized op should fail", + "doc": {"foo": 1}, + "patch": [{"op": "spam", "path": "/foo", "value": 1}], + "error": "Unrecognized op 'spam'" }, + + { "comment": "test with bad array number that has leading zeros", + "doc": ["foo", "bar"], + "patch": [{"op": "test", "path": "/00", "value": "foo"}], + "error": "test op should reject the array value, it has leading zeros" }, + + { "comment": "test with bad array number that has leading zeros", + "doc": ["foo", "bar"], + "patch": [{"op": "test", "path": "/01", "value": "bar"}], + "error": "test op should reject the array value, it has leading zeros" }, + + { "comment": "Removing nonexistent field", + "doc": {"foo" : "bar"}, + "patch": [{"op": "remove", "path": "/baz"}], + "error": "removing a nonexistent field should fail" }, + + { "comment": "Removing deep nonexistent path", + "doc": {"foo" : "bar"}, + "patch": [{"op": "remove", "path": "/missing1/missing2"}], + "error": "removing a nonexistent field should fail" }, + + { "comment": "Removing nonexistent index", + "doc": ["foo", "bar"], + "patch": [{"op": "remove", "path": "/2"}], + "error": "removing a nonexistent index should fail" }, + + { "comment": "Patch with different capitalisation than doc", + "doc": {"foo":"bar"}, + "patch": [{"op": "add", "path": "/FOO", "value": "BAR"}], + "expected": {"foo": "bar", "FOO": "BAR"} + } + ] diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonpatch/set.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonpatch/set.yaml new file mode 100644 index 000000000000..581222f9c734 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonpatch/set.yaml @@ -0,0 +1,68 @@ +--- +cases: + # Here are some tests that cover dealing with sets and json.patch, since that + # is not covered by the spec. + - note: jsonpatch/set-success basic-remove + query: data.main.result.foo = x + want_result: + - x: + - a + - c + modules: + - | + package main + doc = {"foo": {"a", "b", "c"}} + patch = [{"op": "remove", "path": "foo/b"}] + result = r {r = json.patch(doc, patch)} + sort_bindings: true + - note: jsonpatch/set-success basic-add + query: data.main.result.foo = x + want_result: + - x: + - a + - b + - c + - d + modules: + - | + package main + doc = {"foo": {"a", "b", "c"}} + patch = [{"op": "add", "path": "foo/d", "value": "d"}] + result = r {r = json.patch(doc, patch)} + sort_bindings: true + - note: jsonpatch/set-failure add-with-mismatched-key-value + query: data.main.result.foo = x + want_result: [] # value does not match key + modules: + - | + package main + doc = {"foo": {"a", "b", "c"}} + patch = [{"op": "add", "path": "foo/d", "value": "e"}] + result = r {r = json.patch(doc, patch)} + - note: jsonpatch/set-success basic-move + query: "data.main.result.foo = x; data.main.result.bar = z" + want_result: + - x: + - b + z: + - a + - c + - d + modules: + - | + package main + doc = {"foo": {"a", "b"}, "bar": {"c", "d"}} + patch = [{"op": "move", "from": "foo/a", "path": "bar/a"}] + result = r {r = json.patch(doc, patch)} + sort_bindings: true + - note: jsonpatch/set-success add-to-nested-array + query: data.main.result = x + want_result: + - x: + - [1, 2] + modules: + - | + package main + doc = {[1]} + patch = [{"op": "add", "path": [[1], 1], "value": 2}] + result = r {r = json.patch(doc, patch)} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0230.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0230.yaml new file mode 100644 index 000000000000..531c3761deee --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0230.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a/b/c"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/base + query: data.generated.p = x + want_result: + - x: + a: + b: + d: 8 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0231.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0231.yaml new file mode 100644 index 000000000000..e59227fdef72 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0231.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a/b/c", "e"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/multiple roots + query: data.generated.p = x + want_result: + - x: + a: + b: + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0232.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0232.yaml new file mode 100644 index 000000000000..2a80c2abddfa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0232.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, ["a/b/c", "e"], __local1__) + __local0__ = __local1__ + } + note: jsonremove/multiple roots array + query: data.generated.p = x + want_result: + - x: + a: + b: + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0233.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0233.yaml new file mode 100644 index 000000000000..befed97d08b0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0233.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({"a": {"b": {"c": 7, "d": 8}, "e": 9}}, {"a/b/c", "a/e"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/shared roots + query: data.generated.p = x + want_result: + - x: + a: + b: + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0234.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0234.yaml new file mode 100644 index 000000000000..5f415e9ca1e2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0234.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({"a": {"b": 7}, "c": 1}, {"a", "a/b"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/conflict + query: data.generated.p = x + want_result: + - x: + c: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0235.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0235.yaml new file mode 100644 index 000000000000..39a83a0e01f6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0235.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({"a": 7}, set(), __local1__) + __local0__ = __local1__ + } + note: jsonremove/empty list + query: data.generated.p = x + want_result: + - x: + a: 7 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0236.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0236.yaml new file mode 100644 index 000000000000..2e704822e422 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0236.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({}, {"a/b"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/empty object + query: data.generated.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0237.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0237.yaml new file mode 100644 index 000000000000..62a32bdbbdf3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0237.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({"a": {"b": 7}, "c": 1}, {"a", "c"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/delete all + query: data.generated.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0238.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0238.yaml new file mode 100644 index 000000000000..90709e62f798 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0238.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({"a": {"b": 7}, "c": 1}, {"a/b", "c"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/delete last in object + query: data.generated.p = x + want_result: + - x: + a: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0239.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0239.yaml new file mode 100644 index 000000000000..f8bf6f701e8e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0239.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({"a": [{"b": 7, "c": 8}, {"d": 9}]}, {"a/0/b", "a/1"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/arrays + query: data.generated.p = x + want_result: + - x: + a: + - c: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0240.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0240.yaml new file mode 100644 index 000000000000..1994e1e89bb0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0240.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({"a": [{"1": ["b", "c", "d"]}, {"x": "y"}]}, {"a/0/1/2"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/object with number keys + query: data.generated.p = x + want_result: + - x: + a: + - "1": + - b + - c + - x: "y" diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0241.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0241.yaml new file mode 100644 index 000000000000..ee6e155fc6cb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0241.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {["a", "b", "c"], ["e"]}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/arrays of roots + query: data.generated.p = x + want_result: + - x: + a: + b: + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0242.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0242.yaml new file mode 100644 index 000000000000..f5468fd595a7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0242.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + json.remove({"a": {"b": {"c": 7, "d": 8, "x": 0}}, "e": 9}, {"a/b/d", ["a", "b", "c"]}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/mixed root types + query: data.generated.p = x + want_result: + - x: + a: + b: + x: 0 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0243.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0243.yaml new file mode 100644 index 000000000000..24b2e2f812b5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0243.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + input_term: '{"x": "foo"}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + json.remove(__local2__, {"a/b/c"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/error invalid target type string input + query: data.generated.p = x + want_error: "json.remove: operand 1 must be object but got string" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0244.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0244.yaml new file mode 100644 index 000000000000..faea9a381eca --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0244.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": 22}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + json.remove(__local2__, {"a/b/c"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/error invalid target type number input + query: data.generated.p = x + want_error: "json.remove: operand 1 must be object but got number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0245.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0245.yaml new file mode 100644 index 000000000000..6a6e62ca49e8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0245.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": true}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + json.remove(__local2__, {"a/b/c"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/error invalid target type boolean input + query: data.generated.p = x + want_error: "json.remove: operand 1 must be object but got boolean" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0246.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0246.yaml new file mode 100644 index 000000000000..13624cc0e175 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0246.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": ["a", "b", "c"]}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + json.remove(__local2__, {"a/b/c"}, __local1__) + __local0__ = __local1__ + } + note: jsonremove/error invalid target type array input + query: data.generated.p = x + want_error: "json.remove: operand 1 must be object but got array" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0247.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0247.yaml new file mode 100644 index 000000000000..c44f30a3f59d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0247.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": "foo"}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + note: jsonremove/error invalid paths type string + query: data.generated.p = x + want_error: "json.remove: operand 2 must be one of {set, array} but got string" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0248.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0248.yaml new file mode 100644 index 000000000000..eb72c930f1b0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0248.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": 22}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + note: jsonremove/error invalid paths type number + query: data.generated.p = x + want_error: "json.remove: operand 2 must be one of {set, array} but got number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0249.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0249.yaml new file mode 100644 index 000000000000..fd585a06b586 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0249.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": true}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + note: jsonremove/error invalid paths type boolean + query: data.generated.p = x + want_error: "json.remove: operand 2 must be one of {set, array} but got boolean" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0250.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0250.yaml new file mode 100644 index 000000000000..f8c7fc6bbcb5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0250.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": {"y": 123}}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + note: jsonremove/error invalid paths type object + query: data.generated.p = x + want_error: "json.remove: operand 2 must be one of {set, array} but got object" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0251.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0251.yaml new file mode 100644 index 000000000000..eb1e8e262319 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0251.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + input_term: '{"x": {1, 2, 3, "a"}}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + note: jsonremove/error invalid paths type set with numbers + query: data.generated.p = x + want_error: + "json.remove: operand 2 must be one of {set, array} containing string + paths or array of path segments but got number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0252.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0252.yaml new file mode 100644 index 000000000000..a8fa2a6f9d4a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0252.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + input_term: '{"x": {"a", {"x": 1}, {"y": 2}}}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + note: jsonremove/error invalid paths type set with objects + query: data.generated.p = x + want_error: + "json.remove: operand 2 must be one of {set, array} containing string + paths or array of path segments but got object" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0253.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0253.yaml new file mode 100644 index 000000000000..d57b6cd0fe72 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0253.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + input_term: '{"x": ["a", 1, 2, 3]}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + note: jsonremove/error invalid paths type array with numbers + query: data.generated.p = x + want_error: + "json.remove: operand 2 must be one of {set, array} containing string + paths or array of path segments but got number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0254.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0254.yaml new file mode 100644 index 000000000000..4e6028bc1892 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremove/test-jsonremove-0254.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + input_term: '{"x": ["a", {"x": 1}, {"y": 2}]}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + note: jsonremove/error invalid paths type array with objects + query: data.generated.p = x + want_error: + "json.remove: operand 2 must be one of {set, array} containing string + paths or array of path segments but got object" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonremoveidempotent/test-jsonremoveidempotent-0255.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonremoveidempotent/test-jsonremoveidempotent-0255.yaml new file mode 100644 index 000000000000..38921eb84f62 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonremoveidempotent/test-jsonremoveidempotent-0255.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + __local0__ = {"a": {"b": 2, "c": 3}} + json.remove(__local0__, {"a"}, __local1__) + __local1__ = {} + json.remove(__local0__, {"a/b"}, __local2__) + __local2__ = {"a": {"c": 3}} + json.remove(__local0__, {"a/c"}, __local3__) + __local3__ = {"a": {"b": 2}} + __local0__ = {"a": {"b": 2, "c": 3}} + } + note: jsonremoveidempotent/TestBuiltinJSONRemoveIdempotent + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonschema/test-json-match_schema.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonschema/test-json-match_schema.yaml new file mode 100644 index 000000000000..c40ea051e2e8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonschema/test-json-match_schema.yaml @@ -0,0 +1,104 @@ +--- +cases: + - note: json_match_schema/success + modules: + - | + package test + + document := {"id": 5} + schema := { + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + } + p := json.match_schema(document, schema) + query: data.test.p = x + want_result: + - x: [true, []] + - note: json_match_schema/success string document + modules: + - | + package test + + document := `{"id": 5}` + schema := { + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + } + p := json.match_schema(document, schema) + query: data.test.p = x + want_result: + - x: [true, []] + - note: json_match_schema/success string schema + modules: + - | + package test + + document := {"id": 5} + schema := `{ + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + }` + p := json.match_schema(document, schema) + query: data.test.p = x + want_result: + - x: [true, []] + - note: json_match_schema/invalid document + modules: + - | + package test + + document := {"id": "foo"} + schema := { + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + } + p := json.match_schema(document, schema) + query: data.test.p = x + want_result: + - x: + [ + false, + [ + { + "desc": "Invalid type. Expected: integer, given: string", + "error": "id: Invalid type. Expected: integer, given: string", + "field": "id", + "type": "invalid_type", + }, + ], + ] + - note: json_match_schema/invalid schema + modules: + - | + package test + + document := {"id": "foo"} + schema := { + "properties": { + "id": { + "type": "unknown" + } + }, + "required": ["id"] + } + p := json.match_schema(document, schema) + query: data.test.p = x + strict_error: true + want_error: "json.match_schema: has a primitive type that is NOT VALID -- given: /unknown/ Expected valid values are:[array boolean integer number null object string]" + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jsonschema/test-json-verify_schema.yaml b/third_party/opa/v1/test/cases/testdata/v0/jsonschema/test-json-verify_schema.yaml new file mode 100644 index 000000000000..3475fcb16294 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jsonschema/test-json-verify_schema.yaml @@ -0,0 +1,50 @@ +--- +cases: + - note: json_verify_schema/valid schema string + modules: + - | + package test + + schema := `{"type": "boolean"}` + p := json.verify_schema(schema) + query: data.test.p = x + want_result: + - x: [true, null] + - note: json_verify_schema/valid schema object + modules: + - | + package test + + schema := {"type": "boolean"} + p := json.verify_schema(schema) + query: data.test.p = x + want_result: + - x: [true, null] + - note: json_verify_schema/invalid schema string + modules: + - | + package test + + schema := `{"type": "unknown_type"}` + p := json.verify_schema(schema) + query: data.test.p = x + want_result: + - x: + [ + false, + "jsonschema: has a primitive type that is NOT VALID -- given: /unknown_type/ Expected valid values are:[array boolean integer number null object string]", + ] + - note: json_verify_schema/invalid schema object + modules: + - | + package test + + schema := {"type": "unknown_type"} + p := json.verify_schema(schema) + query: data.test.p = x + want_result: + - x: + [ + false, + "jsonschema: has a primitive type that is NOT VALID -- given: /unknown_type/ Expected valid values are:[array boolean integer number null object string]", + ] diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0389.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0389.yaml new file mode 100644 index 000000000000..9b79f5742747 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0389.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0.XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + note: jwtbuiltins/simple + query: data.generated.p = x + want_result: + - x: + - alg: HS256 + typ: JWT + - iss: opa + sub: "0" + - 5e65682e81c8de9c4cb4c3bf59138d3122731940cff690e3cbc269d3fb5d4576 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0390.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0390.yaml new file mode 100644 index 000000000000..79fdce567b4c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0390.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuZXciOiJJIGFtIGEgdXNlciBjcmVhdGVkIGZpZWxkIiwiaXNzIjoib3BhIn0.6UmjsclVDGD9jcmX_F8RJzVgHtUZuLu2pxkF_UEQCrE", [x, y, z]) + } + note: jwtbuiltins/simple-non-registered + query: data.generated.p = x + want_result: + - x: + - alg: HS256 + typ: JWT + - iss: opa + new: I am a user created field + - e949a3b1c9550c60fd8dc997fc5f112735601ed519b8bbb6a71905fd41100ab1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0391.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0391.yaml new file mode 100644 index 000000000000..7392afc03fb1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0391.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImVuYyI6ImJsYWgifQ.eyJuZXciOiJJIGFtIGEgdXNlciBjcmVhdGVkIGZpZWxkIiwiaXNzIjoib3BhIn0.McGUb1e-UviZKy6UyQErNNQzEUgeV25Buwk7OHOa8U8", [x, y, z]) + } + note: jwtbuiltins/no-support-jwe + query: data.generated.p = x + strict_error: true + want_error: JWT is a JWE object, which is not supported + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0392.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0392.yaml new file mode 100644 index 000000000000..9f72ce0b4676 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0392.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + note: jwtbuiltins/no-periods + query: data.generated.p = x + strict_error: true + want_error: encoded JWT had no period separators + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0393.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0393.yaml new file mode 100644 index 000000000000..2589ef361918 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0393.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXV.CJ9eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + note: jwtbuiltins/wrong-period-count + query: data.generated.p = x + strict_error: true + want_error: encoded JWT must have 3 sections, found 2 + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0394.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0394.yaml new file mode 100644 index 000000000000..944f78e13f3b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0394.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode("eyJhbGciOiJIU^%zI1NiI+sInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0.XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + note: jwtbuiltins/bad-header-encoding + query: data.generated.p = x + strict_error: true + want_error: + "JWT header had invalid encoding: illegal base64 data at input byte + 13" + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0395.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0395.yaml new file mode 100644 index 000000000000..efcb53fa76d8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0395.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwia/XNzIjoib3BhIn0.XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + note: jwtbuiltins/bad-payload-encoding + query: data.generated.p = x + strict_error: true + want_error: + "JWT payload had invalid encoding: illegal base64 data at input byte + 17" + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0396.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0396.yaml new file mode 100644 index 000000000000..60d0b6e65b46 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0396.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0.XmVoLoHI3pxMtMO(_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + note: jwtbuiltins/bad-signature-encoding + query: data.generated.p = x + strict_error: true + want_error: + "JWT signature had invalid encoding: illegal base64 data at input byte + 15" + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0397.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0397.yaml new file mode 100644 index 000000000000..b3a5c3892254 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0397.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.ImV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUp6ZFdJaU9pSXdJaXdpYVhOeklqb2liM0JoSW4wLlhtVm9Mb0hJM3B4TXRNT19XUk9OTVNKekdVRFA5cERqeThKcDBfdGRSWFki.8W0qx4mLxslmZl7wEMUWBxH7tST3XsEuWXxesXqFnRI", [x, y, z]) + } + note: jwtbuiltins/nested + query: data.generated.p = x + want_result: + - x: + - alg: HS256 + typ: JWT + - iss: opa + sub: "0" + - 5e65682e81c8de9c4cb4c3bf59138d3122731940cff690e3cbc269d3fb5d4576 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0398.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0398.yaml new file mode 100644 index 000000000000..708956dd5ec1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0398.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.ImV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNJc0ltTjBlU0k2SWtwWFZDSjkuSW1WNVNtaGlSMk5wVDJsS1NWVjZTVEZPYVVselNXNVNOV05EU1RaSmEzQllWa05LT1M1bGVVcDZaRmRKYVU5cFNYZEphWGRwWVZoT2VrbHFiMmxpTTBKb1NXNHdMbGh0Vm05TWIwaEpNM0I0VFhSTlQxOVhVazlPVFZOS2VrZFZSRkE1Y0VScWVUaEtjREJmZEdSU1dGa2kuOFcwcXg0bUx4c2xtWmw3d0VNVVdCeEg3dFNUM1hzRXVXWHhlc1hxRm5SSSI.U8rwnGAJ-bJoGrAYKEzNtbJQWd3x1eW0Y25nLKHDCgo", [x, y, z]) + } + note: jwtbuiltins/double-nested + query: data.generated.p = x + want_result: + - x: + - alg: HS256 + typ: JWT + - iss: opa + sub: "0" + - 5e65682e81c8de9c4cb4c3bf59138d3122731940cff690e3cbc269d3fb5d4576 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0399.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0399.yaml new file mode 100644 index 000000000000..882a9e4a05f5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0399.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwiaXNzIjoib3BhIiwiZXh0Ijp7ImFiYyI6IjEyMyIsImNiYSI6WzEwLCIxMCJdfX0.IIxF-uJ6i4K5Dj71xNLnUeqB9jmujl6ujTInhii1PxE", [x, y, z]) + } + note: jwtbuiltins/complex-values + query: data.generated.p = x + want_result: + - x: + - alg: HS256 + typ: JWT + - ext: + abc: "123" + cba: + - 10 + - "10" + iss: opa + sub: "0" + - 208c45fae27a8b82b90e3ef5c4d2e751ea81f639ae8e5eae8d32278628b53f11 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0400.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0400.yaml new file mode 100644 index 000000000000..e687430c99f5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtbuiltins/test-jwtbuiltins-0400.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiAiMCIsImlzcyI6ICJub3Qgb3BhIiwgImlzcyI6ICJhbHNvIG5vdCBvcGEiLCAiaXNzIjogIm9wYSJ9.XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + note: jwtbuiltins/duplicate-keys + query: data.generated.p = x + want_result: + - x: + - alg: HS256 + typ: JWT + - iss: opa + sub: "0" + - 5e65682e81c8de9c4cb4c3bf59138d3122731940cff690e3cbc269d3fb5d4576 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0449.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0449.yaml new file mode 100644 index 000000000000..68de917b3169 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0449.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/ps256-unconstrained + query: data.generated.p = x + want_result: + - x: + - true + - alg: PS256 + typ: JWT + - iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0450.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0450.yaml new file mode 100644 index 000000000000..abe03c7404ee --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0450.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/ps256-key-wrong + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0451.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0451.yaml new file mode 100644 index 000000000000..44258d10eb63 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0451.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImV4cCI6IDMwMDB9.hqDP3AzshNhUZMI02U3nLPrj93QFrgs-74XFrF1Vry2bplrz-NKpdVdfTu8iY_bhmkWf2Om5DdwRZj2ZgpGahtnshnHaRq0RyqF-m3Y7oNj6JL_YMwgxsFIIHtBlagBqDU-gZK99iqSOSGqVhvxqX6gCqFgE7vnEGHeeDedtRM53coAJuwzy8rQV9m3TewoofPdPasGv-dBLQZ3qgmnibkSgb7SmFpjXBy8zL3xJXOZhAHYlgcmcEoFVaWlBguIcWA87WZlpCLYcdYTJzSZweC3QLUhZ4RLJW84-LMKp6xWLLPrp3OgnsduB2G9PYMmYw_qCkuY1KGwfH4PvCQbAzQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/rs256-key-wrong + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0452.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0452.yaml new file mode 100644 index 000000000000..ab604fc4be63 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0452.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "iss": "xxx"}, [x, y, z]) + } + note: jwtdecodeverify/ps256-iss-ok + query: data.generated.p = x + want_result: + - x: + - true + - alg: PS256 + typ: JWT + - iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0453.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0453.yaml new file mode 100644 index 000000000000..24f12b09a3ac --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0453.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "iss": "yyy"}, [x, y, z]) + } + note: jwtdecodeverify/ps256-iss-wrong + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0454.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0454.yaml new file mode 100644 index 000000000000..d8478180d56d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0454.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"alg": "PS256", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/ps256-alg-ok + query: data.generated.p = x + want_result: + - x: + - true + - alg: PS256 + typ: JWT + - iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0455.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0455.yaml new file mode 100644 index 000000000000..6108b3ac6918 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0455.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"alg": "RS256", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/ps256-alg-wrong + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0456.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0456.yaml new file mode 100644 index 000000000000..5f26819e8b6c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0456.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImV4cCI6IDMwMDB9.hqDP3AzshNhUZMI02U3nLPrj93QFrgs-74XFrF1Vry2bplrz-NKpdVdfTu8iY_bhmkWf2Om5DdwRZj2ZgpGahtnshnHaRq0RyqF-m3Y7oNj6JL_YMwgxsFIIHtBlagBqDU-gZK99iqSOSGqVhvxqX6gCqFgE7vnEGHeeDedtRM53coAJuwzy8rQV9m3TewoofPdPasGv-dBLQZ3qgmnibkSgb7SmFpjXBy8zL3xJXOZhAHYlgcmcEoFVaWlBguIcWA87WZlpCLYcdYTJzSZweC3QLUhZ4RLJW84-LMKp6xWLLPrp3OgnsduB2G9PYMmYw_qCkuY1KGwfH4PvCQbAzQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "time": 2000000000000}, [x, y, z]) + } + note: jwtdecodeverify/rs256-exp-ok + query: data.generated.p = x + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - exp: 3000 + iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0457.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0457.yaml new file mode 100644 index 000000000000..3648c5815743 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0457.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImV4cCI6IDMwMDB9.hqDP3AzshNhUZMI02U3nLPrj93QFrgs-74XFrF1Vry2bplrz-NKpdVdfTu8iY_bhmkWf2Om5DdwRZj2ZgpGahtnshnHaRq0RyqF-m3Y7oNj6JL_YMwgxsFIIHtBlagBqDU-gZK99iqSOSGqVhvxqX6gCqFgE7vnEGHeeDedtRM53coAJuwzy8rQV9m3TewoofPdPasGv-dBLQZ3qgmnibkSgb7SmFpjXBy8zL3xJXOZhAHYlgcmcEoFVaWlBguIcWA87WZlpCLYcdYTJzSZweC3QLUhZ4RLJW84-LMKp6xWLLPrp3OgnsduB2G9PYMmYw_qCkuY1KGwfH4PvCQbAzQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "time": 4000000000000}, [x, y, z]) + } + note: jwtdecodeverify/rs256-exp-expired + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0458.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0458.yaml new file mode 100644 index 000000000000..979bfc8e0a7e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0458.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImV4cCI6IDMwMDB9.hqDP3AzshNhUZMI02U3nLPrj93QFrgs-74XFrF1Vry2bplrz-NKpdVdfTu8iY_bhmkWf2Om5DdwRZj2ZgpGahtnshnHaRq0RyqF-m3Y7oNj6JL_YMwgxsFIIHtBlagBqDU-gZK99iqSOSGqVhvxqX6gCqFgE7vnEGHeeDedtRM53coAJuwzy8rQV9m3TewoofPdPasGv-dBLQZ3qgmnibkSgb7SmFpjXBy8zL3xJXOZhAHYlgcmcEoFVaWlBguIcWA87WZlpCLYcdYTJzSZweC3QLUhZ4RLJW84-LMKp6xWLLPrp3OgnsduB2G9PYMmYw_qCkuY1KGwfH4PvCQbAzQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/rs256-exp-now-expired + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0459.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0459.yaml new file mode 100644 index 000000000000..c20a5d9e4826 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0459.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + now := time.now_ns() + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImV4cCI6IDMwMDB9.hqDP3AzshNhUZMI02U3nLPrj93QFrgs-74XFrF1Vry2bplrz-NKpdVdfTu8iY_bhmkWf2Om5DdwRZj2ZgpGahtnshnHaRq0RyqF-m3Y7oNj6JL_YMwgxsFIIHtBlagBqDU-gZK99iqSOSGqVhvxqX6gCqFgE7vnEGHeeDedtRM53coAJuwzy8rQV9m3TewoofPdPasGv-dBLQZ3qgmnibkSgb7SmFpjXBy8zL3xJXOZhAHYlgcmcEoFVaWlBguIcWA87WZlpCLYcdYTJzSZweC3QLUhZ4RLJW84-LMKp6xWLLPrp3OgnsduB2G9PYMmYw_qCkuY1KGwfH4PvCQbAzQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "time": now}, [x, y, z]) + } + note: jwtdecodeverify/rs256-exp-now-explicit-expired + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0460.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0460.yaml new file mode 100644 index 000000000000..d60cc3143537 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0460.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJuYmYiOiAxMDAwLCAiaXNzIjogInh4eCJ9.cwwYDfJhU_ambPIpwBJwDek05miffoudprr41IAYsl0IKekb1ii2uEgwkNM-LJtVXHe9hsK3gANFyfqoJuCZIBvaNMx_3Z0BUdeBs4k1UwBiZCpuud0ofgHKURwvehNgqDvRfchq_-K_Agi2iRdl0oShgLjN-gVbBl8pRwUbQrvASlcsCpZIKUyOzXNtaIZEFh1z6ISDy8UHHOdoieKpN23swya7QAcEb0wXEEKMkkhiRd5QHgWLk37Lnw2K89mKcq4Om0CtV9nHrxxmpYGSMPojCy16Gjdg5-xKyJWvxCfb3YUBUVM4RWa7ICOPRJWPuHxu9pPYG63hb_qDU6NLsw", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "time": 2000000000000}, [x, y, z]) + } + note: jwtdecodeverify/rs256-nbf-ok + query: data.generated.p = x + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - iss: xxx + nbf: 1000 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0461.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0461.yaml new file mode 100644 index 000000000000..aa2f90c7f543 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0461.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJuYmYiOiAxMDAwLCAiaXNzIjogInh4eCJ9.cwwYDfJhU_ambPIpwBJwDek05miffoudprr41IAYsl0IKekb1ii2uEgwkNM-LJtVXHe9hsK3gANFyfqoJuCZIBvaNMx_3Z0BUdeBs4k1UwBiZCpuud0ofgHKURwvehNgqDvRfchq_-K_Agi2iRdl0oShgLjN-gVbBl8pRwUbQrvASlcsCpZIKUyOzXNtaIZEFh1z6ISDy8UHHOdoieKpN23swya7QAcEb0wXEEKMkkhiRd5QHgWLk37Lnw2K89mKcq4Om0CtV9nHrxxmpYGSMPojCy16Gjdg5-xKyJWvxCfb3YUBUVM4RWa7ICOPRJWPuHxu9pPYG63hb_qDU6NLsw", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/rs256-nbf-now-ok + query: data.generated.p = x + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - iss: xxx + nbf: 1000 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0462.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0462.yaml new file mode 100644 index 000000000000..1f1f05a44932 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0462.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJuYmYiOiAxMDAwLCAiaXNzIjogInh4eCJ9.cwwYDfJhU_ambPIpwBJwDek05miffoudprr41IAYsl0IKekb1ii2uEgwkNM-LJtVXHe9hsK3gANFyfqoJuCZIBvaNMx_3Z0BUdeBs4k1UwBiZCpuud0ofgHKURwvehNgqDvRfchq_-K_Agi2iRdl0oShgLjN-gVbBl8pRwUbQrvASlcsCpZIKUyOzXNtaIZEFh1z6ISDy8UHHOdoieKpN23swya7QAcEb0wXEEKMkkhiRd5QHgWLk37Lnw2K89mKcq4Om0CtV9nHrxxmpYGSMPojCy16Gjdg5-xKyJWvxCfb3YUBUVM4RWa7ICOPRJWPuHxu9pPYG63hb_qDU6NLsw", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "time": 500000000000}, [x, y, z]) + } + note: jwtdecodeverify/rs256-nbf-toosoon + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0463.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0463.yaml new file mode 100644 index 000000000000..30e013aea0cc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0463.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJ0eXAiOiAiSldUIiwgImtpZCI6ICJrMSJ9.eyJpc3MiOiAieHh4IiwgInN1YiI6ICJmcmVkIn0.J4J4FgUD_P5fviVVjgvQWJDg-5XYTP_tHCwB3kSlYVKv8vmnZRNh4ke68OxfMP96iM-LZswG2fNqe-_piGIMepF5rCe1iIWAuz3qqkxfS9YVF3hvwoXhjJT0yIgrDMl1lfW5_XipNshZoxddWK3B7dnVW74MFazEEFuefiQm3PdMUX8jWGsmfgPnqBIZTizErNhoIMuRvYaVM1wA2nfrpVGONxMTaw8T0NRwYIuZwubbnNQ1yLhI0y3dsZvQ_lrh9Khtk9fS1V3SRh7aa9AvferJ4T-48qn_V1m3sINPgoA-uLGyyu3k_GkXRYW1yGNC-MH4T2cwhj89WITbIhusgQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/rs256-alg-missing + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0464.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0464.yaml new file mode 100644 index 000000000000..f0ae9df7eaeb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0464.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJjcml0IjogWyJqdW5rIl0sICJraWQiOiAiazEiLCAiYWxnIjogIlJTMjU2IiwgInR5cCI6ICJKV1QiLCAianVuayI6ICJ4eHgifQ.eyJpc3MiOiAieHh4IiwgInN1YiI6ICJmcmVkIn0.YfoUpW5CgDBtxtBuOix3cdYJGT8cX9Mq7wOhIbjDK7eRQUsAmMY_0EQPh7bd7Yi1gLI3e11BKzguf2EHqAa1kbkHWwFniBO-RIi8q42v2uxC4lpEpIjfaaXB5XmsLfAXtYRqh0AObvbSho6VDXBP_Kn81nhIiE2yFbH14_jhRMSxDBs5ToSkXV-XJHw5bONP8NxPqEk9KF3ZJGzN7J_KoD6LjqfYai5K0eLNEIZh4C1WjTdmCKMR4K6ieZRQWZiSsnhSqLSQERir4n22G3QsdY7dOnCp-SS4VYu3V-PfsOSFMvQ-TTAN1geqMZ9A7k1CCLW0wxKBs-KCiYzmRTzwxA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/rs256-crit-junk + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0465.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0465.yaml new file mode 100644 index 000000000000..272aaf0fca31 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0465.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCIsICJjdHkiOiAiSldUIn0.ZXlKaGJHY2lPaUFpVWxNeU5UWWlMQ0FpZEhsd0lqb2dJa3BYVkNKOS5leUpwYzNNaU9pQWllSGg0SW4wLnJSUnJlUU9DYW9ZLW1Nazcyak5GZVk1YVlFUWhJZ0lFdFZkUTlYblltUUwyTHdfaDdNbkk0U0VPMVBwa0JIVEpyZnljbEplTHpfalJ2UGdJMlcxaDFCNGNaVDhDZ21pVXdxQXI5c0puZHlVQ1FtSWRrbm53WkI5cXAtX3BTdGRHWEo5WnAzeEo4NXotVEJpWlN0QUNUZFdlUklGSUU3VkxPa20tRmxZdzh5OTdnaUN4TmxUdWl3amxlTjMwZDhnWHUxNkZGQzJTSlhtRjZKbXYtNjJHbERhLW1CWFZ0bGJVSTVlWVUwaTdueTNyQjBYUVQxRkt4ZUZ3OF85N09FdV9jY3VLcl82ZHlHZVFHdnQ5Y3JJeEFBMWFZbDdmbVBrNkVhcjllTTNKaGVYMi00Wkx0d1FOY1RDT01YV0dIck1DaG5MWVc4WEFrTHJEbl9yRmxUaVMtZw.Xicc2sWCZ_Nithucsw9XD7YOKrirUdEnH3MyiPM-Ck3vEU2RsTBsfU2JPhfjp3phc0VOgsAXCzwU5PwyNyUo1490q8YSym-liMyO2Lk-hjH5fAxoizg9yD4II_lK6Wz_Tnpc0bBGDLdbuUhvgvO7yqo-leBQlsfRXOvw4VSPSEy8QPtbURtbnLpWY2jGBKz7vGI_o4qDJ3PicG0kyEiWZNh3wjeeCYRCWvXN8qh7Uk5EA-8J5vX651GqV-7gmaX1n-8DXamhaCQcE-p1cjSj04-X-_bJlQtmb-TT3bSyUPxgHVncvxNUby8jkUTzfi5MMbmIzWWkxI5YtJTdtmCkPQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/rsa256-nested + query: data.generated.p = x + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0466.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0466.yaml new file mode 100644 index 000000000000..d83ab9afd8b0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0466.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCIsICJjdHkiOiAiSldUIn0.ZXlKaGJHY2lPaUFpVWxNeU5UWWlMQ0FpZEhsd0lqb2dJa3BYVkNJc0lDSmpkSGtpT2lBaVNsZFVJbjAuWlhsS2FHSkhZMmxQYVVGcFZXeE5lVTVVV1dsTVEwRnBaRWhzZDBscWIyZEphM0JZVmtOS09TNWxlVXB3WXpOTmFVOXBRV2xsU0dnMFNXNHdMbkpTVW5KbFVVOURZVzlaTFcxTmF6Y3lhazVHWlZrMVlWbEZVV2hKWjBsRmRGWmtVVGxZYmxsdFVVd3lUSGRmYURkTmJrazBVMFZQTVZCd2EwSklWRXB5Wm5samJFcGxUSHBmYWxKMlVHZEpNbGN4YURGQ05HTmFWRGhEWjIxcFZYZHhRWEk1YzBwdVpIbFZRMUZ0U1dScmJtNTNXa0k1Y1hBdFgzQlRkR1JIV0VvNVduQXplRW80TlhvdFZFSnBXbE4wUVVOVVpGZGxVa2xHU1VVM1ZreFBhMjB0Um14WmR6aDVPVGRuYVVONFRteFVkV2wzYW14bFRqTXdaRGhuV0hVeE5rWkdRekpUU2xodFJqWktiWFl0TmpKSGJFUmhMVzFDV0ZaMGJHSlZTVFZsV1ZVd2FUZHVlVE55UWpCWVVWUXhSa3Q0WlVaM09GODVOMDlGZFY5alkzVkxjbDgyWkhsSFpWRkhkblE1WTNKSmVFRkJNV0ZaYkRkbWJWQnJOa1ZoY2psbFRUTkthR1ZZTWkwMFdreDBkMUZPWTFSRFQwMVlWMGRJY2sxRGFHNU1XVmM0V0VGclRISkVibDl5Um14VWFWTXRady5YaWNjMnNXQ1pfTml0aHVjc3c5WEQ3WU9LcmlyVWRFbkgzTXlpUE0tQ2szdkVVMlJzVEJzZlUySlBoZmpwM3BoYzBWT2dzQVhDendVNVB3eU55VW8xNDkwcThZU3ltLWxpTXlPMkxrLWhqSDVmQXhvaXpnOXlENElJX2xLNld6X1RucGMwYkJHRExkYnVVaHZndk83eXFvLWxlQlFsc2ZSWE92dzRWU1BTRXk4UVB0YlVSdGJuTHBXWTJqR0JLejd2R0lfbzRxREozUGljRzBreUVpV1pOaDN3amVlQ1lSQ1d2WE44cWg3VWs1RUEtOEo1dlg2NTFHcVYtN2dtYVgxbi04RFhhbWhhQ1FjRS1wMWNqU2owNC1YLV9iSmxRdG1iLVRUM2JTeVVQeGdIVm5jdnhOVWJ5OGprVVR6Zmk1TU1ibUl6V1dreEk1WXRKVGR0bUNrUFE.ODBVH_gooCLJxtPVr1MjJC1syG4MnVUFP9LkI9pSaj0QABV4vpfqrBshHn8zOPgUTDeHwbc01Qy96cQlTMQQb94YANmZyL1nzwmdR4piiGXMGSlcCNfDg1o8DK4msMSR-X-j2IkxBDB8rfeFSfLRMgDCjAF0JolW7qWmMD9tBmFNYAjly4vMwToOXosDmFLl5eqyohXDf-3Ohljm5kIjtyMWkt5S9EVuwlIXh2owK5l59c4-TH29gkuaZ3uU4LFPjD7XKUrlOQnEMuu2QD8LAqTyxbnY4JyzUWEvyTM1dVmGnFpLKCg9QBly__y1u2ffhvDsHyuCmEKAbhPE98YvFA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/rsa256-nested2 + query: data.generated.p = x + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0467.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0467.yaml new file mode 100644 index 000000000000..2b70143d12bb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0467.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiRVMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.JvbTLBF06FR70gb7lCbx_ojhp4bk9--B_aULgNlYM0fYf9OSawaqBQp2lwW6FADFtRJ2WFUk5g0zwVOUlnrlzw", {"cert": "-----BEGIN CERTIFICATE-----\nMIIBcDCCARagAwIBAgIJAMZmuGSIfvgzMAoGCCqGSM49BAMCMBMxETAPBgNVBAMM\nCHdoYXRldmVyMB4XDTE4MDgxMDE0Mjg1NFoXDTE4MDkwOTE0Mjg1NFowEzERMA8G\nA1UEAwwId2hhdGV2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATPwn3WCEXL\nmjp/bFniDwuwsfu7bASlPae2PyWhqGeWwe23Xlyx+tSqxlkXYe4pZ23BkAAscpGj\nyn5gXHExyDlKo1MwUTAdBgNVHQ4EFgQUElRjSoVgKjUqY5AXz2o74cLzzS8wHwYD\nVR0jBBgwFoAUElRjSoVgKjUqY5AXz2o74cLzzS8wDwYDVR0TAQH/BAUwAwEB/zAK\nBggqhkjOPQQDAgNIADBFAiEA4yQ/88ZrUX68c6kOe9G11u8NUaUzd8pLOtkKhniN\nOHoCIHmNX37JOqTcTzGn2u9+c8NlnvZ0uDvsd1BmKPaUmjmm\n-----END CERTIFICATE-----\n"}, [x, y, z]) + } + note: jwtdecodeverify/es256-unconstrained + query: data.generated.p = x + want_result: + - x: + - true + - alg: ES256 + typ: JWT + - iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0468.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0468.yaml new file mode 100644 index 000000000000..7ac851ab6186 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0468.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM", {"secret": "secret"}, [x, y, z]) + } + note: jwtdecodeverify/hs256-unconstrained + query: data.generated.p = x + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - azp: alice + hr: false + subordinates: [] + user: alice diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0469.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0469.yaml new file mode 100644 index 000000000000..5aa2b8a46bda --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0469.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM", {"secret": "the wrong key"}, [x, y, z]) + } + note: jwtdecodeverify/hs256-key-wrong + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0470.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0470.yaml new file mode 100644 index 000000000000..432ef22f449d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0470.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImF1ZCI6ICJmcmVkIn0.F-9m2Tx8r1tuQFirazsI4FK05bXX3uP4ut8M2FryJ07k3bQhy262fdwNDmuFcGx0NfL-c80agcwGoTzMWXkVEgZ2KTz0QSAdcdGk3ZWtUy-Mj2IilZ1dzkVvW8LsithYFTGcUtkelFDrJwtMQ0Kum7SXJpC_HCBk4PbftY0XD6jRgHLnQdeT9_J11L4sd19vCdpxxxm3_m_yvUV3ZynzB4vhQbS3CET4EClAVhi-m_gMh9mj85gY1ycIz6-FxWv8xM2Igm2SMeIdyJwAvEGnIauRS928P_OqVCZgCH2Pafnxtzy77Llpxy8XS0xu5PtPw3_azhg33GaXDCFsfz6GpA", {"aud": "fred", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/rs256-aud + query: data.generated.p = x + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - aud: fred + iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0471.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0471.yaml new file mode 100644 index 000000000000..eaef9a6458f6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0471.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImF1ZCI6IFsiZnJlZCIsICJib2IiXX0.k8jW7PUiMkQCKCjnSFBFFKPDO0RXwZgVkLUwUfi8sMdrrcKi12LC8wd5fLBn0YraFtMXWKdMweKf9ZC-K33h5TK7kkTVKOXctF50mleMlUn0Up_XjtdP1v-2WOfivUXcexN1o-hu0kH7sSQnielXIjC2EAleG6A54YUOZFBdzvd1PKHlsxA7x2iiL73uGeFlyxoaMki8E5tx7FY6JGF1RdhWCoIV5A5J8QnwI5EetduJQ505U65Pk7UApWYWu4l2DT7KCCJa5dJaBvCBemVxWaBhCQWtJKU2ZgOEkpiK7b_HsdeRBmpG9Oi1o5mt5ybC09VxSD-lEda_iJO_7i042A", {"aud": "bob", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/rs256-aud-list + query: data.generated.p = x + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - aud: + - fred + - bob + iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0472.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0472.yaml new file mode 100644 index 000000000000..89bff5bc9ce8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0472.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"aud": "cath", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/ps256-no-aud + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0473.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0473.yaml new file mode 100644 index 000000000000..c735b71c97be --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0473.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImF1ZCI6ICJmcmVkIn0.F-9m2Tx8r1tuQFirazsI4FK05bXX3uP4ut8M2FryJ07k3bQhy262fdwNDmuFcGx0NfL-c80agcwGoTzMWXkVEgZ2KTz0QSAdcdGk3ZWtUy-Mj2IilZ1dzkVvW8LsithYFTGcUtkelFDrJwtMQ0Kum7SXJpC_HCBk4PbftY0XD6jRgHLnQdeT9_J11L4sd19vCdpxxxm3_m_yvUV3ZynzB4vhQbS3CET4EClAVhi-m_gMh9mj85gY1ycIz6-FxWv8xM2Igm2SMeIdyJwAvEGnIauRS928P_OqVCZgCH2Pafnxtzy77Llpxy8XS0xu5PtPw3_azhg33GaXDCFsfz6GpA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/rs256-missing-aud + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0474.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0474.yaml new file mode 100644 index 000000000000..05924d3ca514 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0474.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImF1ZCI6ICJmcmVkIn0.F-9m2Tx8r1tuQFirazsI4FK05bXX3uP4ut8M2FryJ07k3bQhy262fdwNDmuFcGx0NfL-c80agcwGoTzMWXkVEgZ2KTz0QSAdcdGk3ZWtUy-Mj2IilZ1dzkVvW8LsithYFTGcUtkelFDrJwtMQ0Kum7SXJpC_HCBk4PbftY0XD6jRgHLnQdeT9_J11L4sd19vCdpxxxm3_m_yvUV3ZynzB4vhQbS3CET4EClAVhi-m_gMh9mj85gY1ycIz6-FxWv8xM2Igm2SMeIdyJwAvEGnIauRS928P_OqVCZgCH2Pafnxtzy77Llpxy8XS0xu5PtPw3_azhg33GaXDCFsfz6GpA", {"aud": "cath", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/rs256-wrong-aud + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0475.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0475.yaml new file mode 100644 index 000000000000..f4b46ad606e4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0475.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImF1ZCI6IFsiZnJlZCIsICJib2IiXX0.k8jW7PUiMkQCKCjnSFBFFKPDO0RXwZgVkLUwUfi8sMdrrcKi12LC8wd5fLBn0YraFtMXWKdMweKf9ZC-K33h5TK7kkTVKOXctF50mleMlUn0Up_XjtdP1v-2WOfivUXcexN1o-hu0kH7sSQnielXIjC2EAleG6A54YUOZFBdzvd1PKHlsxA7x2iiL73uGeFlyxoaMki8E5tx7FY6JGF1RdhWCoIV5A5J8QnwI5EetduJQ505U65Pk7UApWYWu4l2DT7KCCJa5dJaBvCBemVxWaBhCQWtJKU2ZgOEkpiK7b_HsdeRBmpG9Oi1o5mt5ybC09VxSD-lEda_iJO_7i042A", {"aud": "cath", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + note: jwtdecodeverify/rs256-wrong-aud-list + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0476.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0476.yaml new file mode 100644 index 000000000000..0f5c06a70921 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0476.yaml @@ -0,0 +1,44 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.ZcLZbBKpPFFz8YGD2jEbXzwHT7DWtqRVk1PTV-cAWUV8jr6f2a--Fw9SFR3vSbrtFif06AQ3aWY7PMM2AuxDjiUVGjItmHRz0sJBEijcE2QVkDN7MNK3Kk1fsM_hbEXzNCzChZpEkTZnLy9ijkJJFD0j6lBat4lO5Zc_LC2lXUftV_hU2aW9mQ7pLSgJjItzRymivnN0g-WUDq5IPK_M8b3yPy_N9iByj8B2FO0sC3TuOrXWbrYrX4ve4bAaSqOFOXiL5Z5BJfmmtT--xKdWDGJxnei8lbv7in7t223fVsUpsH-zmybp529Fya37BsaIlcgLrl38ghvoqy2sHu2wAA", { + "cert": `{ + "keys": [ + { + "kty": "EC", + "use": "sig", + "crv": "P-256", + "kid": "k1", + "x": "9Qq5S5VqMQoH-FOI4atcH6V3bua03C-5ZMZMG1rszwA", + "y": "LLbFxWkGBEBrTm1GMYZJy1OXCH1KLweJMCgIEPIsibU", + "alg": "ES256" + }, + { + "kty": "RSA", + "e": "AQAB", + "use": "enc", + "kid": "k2", + "alg": "RS256", + "n": "sGu-fYVE2nq2dPxJlqAMI0Z8G3FD0XcWDnD8mkfO1ddKRGuUQZmfj4gWeZGyIk3cnuoy7KJCEqa3daXc08QHuFZyfn0rH33t8_AFsvb0q0i7R2FK-Gdqs_E0-sGpYMsRJdZWfCioLkYjIHEuVnRbi3DEsWqe484rEGbKF60jNRgGC4b-8pz-E538ZkssWxcqHrYIj5bjGEU36onjS3M_yrTuNvzv_8wRioK4fbcwmGne9bDxu8LcoSReWpPn0CnUkWnfqroRcMJnC87ZuJagDW1ZWCmU3psdsVanmFFh0DP6z0fsA4h8G2n9-qp-LEKFaWwo3IWlOsIzU3MHdcEiGw" + } + ] + }`, + "time": 1574723450396363500, + }, [x, y, z]) + } + note: jwtdecodeverify/multiple-keys-one-valid + query: data.generated.p = x + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - admin: true + iat: 1516239022 + name: John Doe + sub: "1234567890" diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0477.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0477.yaml new file mode 100644 index 000000000000..b7b89fc6e6e7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0477.yaml @@ -0,0 +1,40 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.G051ZlKno4XdDz4pdPthPKH1cKlFqkREvx_dHhl6kwM", { + "cert": `{ + "keys": [ + { + "kty": "EC", + "use": "sig", + "crv": "P-256", + "kid": "k1", + "x": "9Qq5S5VqMQoH-FOI4atcH6V3bua03C-5ZMZMG1rszwA", + "y": "LLbFxWkGBEBrTm1GMYZJy1OXCH1KLweJMCgIEPIsibU", + "alg": "ES256" + }, + { + "kty": "RSA", + "e": "AQAB", + "use": "enc", + "kid": "k2", + "alg": "RS256", + "n": "sGu-fYVE2nq2dPxJlqAMI0Z8G3FD0XcWDnD8mkfO1ddKRGuUQZmfj4gWeZGyIk3cnuoy7KJCEqa3daXc08QHuFZyfn0rH33t8_AFsvb0q0i7R2FK-Gdqs_E0-sGpYMsRJdZWfCioLkYjIHEuVnRbi3DEsWqe484rEGbKF60jNRgGC4b-8pz-E538ZkssWxcqHrYIj5bjGEU36onjS3M_yrTuNvzv_8wRioK4fbcwmGne9bDxu8LcoSReWpPn0CnUkWnfqroRcMJnC87ZuJagDW1ZWCmU3psdsVanmFFh0DP6z0fsA4h8G2n9-qp-LEKFaWwo3IWlOsIzU3MHdcEiGw" + } + ] + }`, + "time": 1574723450396363500, + }, [x, y, z]) + } + note: jwtdecodeverify/multiple-keys-no-valid + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0478.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0478.yaml new file mode 100644 index 000000000000..c3e1b4ce5ac8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0478.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjEwMDAuMX0.8ab0xurlRs_glclA3Sm7OMQgwkQvE4HuLsfMOc4nVO8", {"secret": "secret", "time": 2000000000000.1}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-nbf + query: data.generated.p = x + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - iss: xxx + nbf: 1000.1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0479.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0479.yaml new file mode 100644 index 000000000000..287e301ea7bb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0479.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjMwMDAuMX0.khHsSae91zHwuaTIvszln3kyrOdPyUYiGSvCI0j2ie8", {"secret": "secret", "time": 2000000000000.1}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-nbf-not-valid + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0480.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0480.yaml new file mode 100644 index 000000000000..34e2bb0cb70a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0480.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjMwMDAuMiwiaXNzIjoieHh4In0.XUen7GtDmICV3O1ngsoO-tQrjrXtOgJI06oGW0nQSIM", {"secret": "secret", "time": 2000000000000.1}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-exp-valid + query: data.generated.p = x + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - exp: 3000.2 + iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0481.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0481.yaml new file mode 100644 index 000000000000..9b8c398af670 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0481.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjMwMDAuMiwiaXNzIjoieHh4In0.XUen7GtDmICV3O1ngsoO-tQrjrXtOgJI06oGW0nQSIM", {"secret": "secret", "time": 4000000000000.1}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-exp-expired + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0482.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0482.yaml new file mode 100644 index 000000000000..0ab99e8bfc4c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0482.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjEuNTg5Mzg1NzcwMTIzNGUrMDl9.lvrsV1nam-BZr0SomWwsr4dBfu6BDrR2FzQ1iS_Xnrw", {"secret": "secret", "time": 1589385770023400000}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-nbf-one-tenth-second-before + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0483.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0483.yaml new file mode 100644 index 000000000000..57b01650c833 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0483.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjEuNTg5Mzg1NzcwMTIzNGUrMDl9.lvrsV1nam-BZr0SomWwsr4dBfu6BDrR2FzQ1iS_Xnrw", {"secret": "secret", "time": 1589385770123400000}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-nbf-equal + query: data.generated.p = x + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - iss: xxx + nbf: 1.5893857701234e+09 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0484.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0484.yaml new file mode 100644 index 000000000000..22514f595b0e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0484.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjEuNTg5Mzg1NzcwMTIzNGUrMDl9.lvrsV1nam-BZr0SomWwsr4dBfu6BDrR2FzQ1iS_Xnrw", {"secret": "secret", "time": 1589385770124400000}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-one-millisecond-after-nbf + query: data.generated.p = x + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - iss: xxx + nbf: 1.5893857701234e+09 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0485.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0485.yaml new file mode 100644 index 000000000000..c24617a9d72e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0485.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjEuNTg5Mzg1NzcwMTIzNGUrMDl9.lvrsV1nam-BZr0SomWwsr4dBfu6BDrR2FzQ1iS_Xnrw", {"secret": "secret", "time": 1589385770223400000}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-one-tenth-second-after-nbf + query: data.generated.p = x + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - iss: xxx + nbf: 1.5893857701234e+09 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0486.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0486.yaml new file mode 100644 index 000000000000..7cf891aa4104 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0486.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjEuNTg5Mzg1NzcwMTIzNGUrMDl9.lvrsV1nam-BZr0SomWwsr4dBfu6BDrR2FzQ1iS_Xnrw", {"secret": "secret", "time": 1589385771123400000}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-one-second-after-nbf + query: data.generated.p = x + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - iss: xxx + nbf: 1.5893857701234e+09 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0487.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0487.yaml new file mode 100644 index 000000000000..4e374d380519 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0487.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuNTg5Mzg1NzcxMTIzNGUrMDksImlzcyI6Inh4eCJ9.PZ2z6VfHt9YdvHHUbilkTnw4R9TK3_V0LV1h-q0k9xg", {"secret": "secret", "time": 1589385770123400000}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-one-second-before-exp + query: data.generated.p = x + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - exp: 1.5893857711234e+09 + iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0488.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0488.yaml new file mode 100644 index 000000000000..15f7724bab53 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0488.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuNTg5Mzg1NzcxMTIzNGUrMDksImlzcyI6Inh4eCJ9.PZ2z6VfHt9YdvHHUbilkTnw4R9TK3_V0LV1h-q0k9xg", {"secret": "secret", "time": 1589385771023400000}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-one-tenth-second-before-exp + query: data.generated.p = x + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - exp: 1.5893857711234e+09 + iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0489.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0489.yaml new file mode 100644 index 000000000000..09b5057dfb10 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0489.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuNTg5Mzg1NzcxMTIzNGUrMDksImlzcyI6Inh4eCJ9.PZ2z6VfHt9YdvHHUbilkTnw4R9TK3_V0LV1h-q0k9xg", {"secret": "secret", "time": 1589385771123400000}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-equal-exp + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0490.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0490.yaml new file mode 100644 index 000000000000..44fba56a2db4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0490.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuNTg5Mzg1NzcxMTIzNGUrMDksImlzcyI6Inh4eCJ9.PZ2z6VfHt9YdvHHUbilkTnw4R9TK3_V0LV1h-q0k9xg", {"secret": "secret", "time": 1589385771223400000}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-one-tenth-second-after-exp + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0491.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0491.yaml new file mode 100644 index 000000000000..d382265f7b9e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-0491.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y, z] { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuNTg5Mzg1NzcxMTIzNGUrMDksImlzcyI6Inh4eCJ9.PZ2z6VfHt9YdvHHUbilkTnw4R9TK3_V0LV1h-q0k9xg", {"secret": "secret", "time": 1589385772123400000}, [x, y, z]) + } + note: jwtdecodeverify/hs256-float-one-second-after-exp + query: data.generated.p = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-invalid-exp-type.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-invalid-exp-type.yaml new file mode 100644 index 000000000000..133c0503537c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-invalid-exp-type.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: + modules: + - | + package generated + + token := io.jwt.encode_sign({"alg": "HS256"}, {"exp": null}, {"kty": "oct", "k": base64url.encode_no_pad("42")}) + + decoded := io.jwt.decode_verify(token, {"secret": "42"}) + + note: jwtdecodeverify/invalid-exp + query: data.generated.decoded = x + strict_error: true + want_error: exp value must be a number + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-invalid-nbf-type.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-invalid-nbf-type.yaml new file mode 100644 index 000000000000..57882944357c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-invalid-nbf-type.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: + modules: + - | + package generated + + token := io.jwt.encode_sign({"alg": "HS256"}, {"nbf": "string is not valid here"}, {"kty": "oct", "k": base64url.encode_no_pad("42")}) + + decoded := io.jwt.decode_verify(token, {"secret": "42"}) + + note: jwtdecodeverify/invalid-nbf + query: data.generated.decoded = x + strict_error: true + want_error: nbf value must be a number + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-missing-iss-while-required.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-missing-iss-while-required.yaml new file mode 100644 index 000000000000..3765c60f15c0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtdecodeverify/test-jwtdecodeverify-missing-iss-while-required.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + modules: + - | + package generated + + token := io.jwt.encode_sign({"alg": "HS256"}, {}, {"kty": "oct", "k": base64url.encode_no_pad("42")}) + + decoded := io.jwt.decode_verify(token, {"secret": "42", "iss": "xxx"}) + + note: jwtdecodeverify/missing-iss-while-required + query: data.generated.decoded = x + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-0492.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-0492.yaml new file mode 100644 index 000000000000..39f3e5e210bc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-0492.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.encode_sign({"alg": "HS256", "typ": "JWT"}, {"aud": ["bob", "saul"], "exp": 1300819380, "http://example.com/is_root": true, "iss": "joe", "privateParams": {"private_one": "one", "private_two": "two"}}, {"k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", "kty": "oct"}, x) + } + note: jwtencodesign/https://tools.ietf.org/html/rfc7515#appendix-A.1 + query: data.generated.p = x + want_result: + - x: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYm9iIiwic2F1bCJdLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UiLCJwcml2YXRlUGFyYW1zIjp7InByaXZhdGVfb25lIjoib25lIiwicHJpdmF0ZV90d28iOiJ0d28ifX0.-Or2eol8bzly-Ztb0v7_7UkcKBkN_aNNpK33HK0MeOY diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-0493.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-0493.yaml new file mode 100644 index 000000000000..a61216230ea1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-0493.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.encode_sign({"alg": "HS256", "typ": "JWT"}, {}, {"k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", "kty": "oct"}, x) + } + note: jwtencodesign/Empty JSON payload + query: data.generated.p = x + want_result: + - x: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.6cvao8lnOu6FAdK68jQFcDMXOmaWNwWiYhCgijd-AD8 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-0494.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-0494.yaml new file mode 100644 index 000000000000..37621b051f62 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-0494.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.encode_sign({"alg": "RS256"}, {"aud": ["bob", "saul"], "exp": 1300819380, "http://example.com/is_root": true, "iss": "joe", "privateParams": {"private_one": "one", "private_two": "two"}}, {"d": "Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ", "dp": "BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0", "dq": "h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU", "e": "AQAB", "kty": "RSA", "n": "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", "p": "4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc", "q": "uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc", "qi": "IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U"}, x) + } + note: jwtencodesign/https://tools.ietf.org/html/rfc7515#appendix-A.2 + query: data.generated.p = x + want_result: + - x: eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsiYm9iIiwic2F1bCJdLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UiLCJwcml2YXRlUGFyYW1zIjp7InByaXZhdGVfb25lIjoib25lIiwicHJpdmF0ZV90d28iOiJ0d28ifX0.kvCjo2z80h4YO3umCn3iayUXEnBGgw-Nk4-cYNPWagW4SAg34nZSonUt9Kpnc5h0s6LpJAnam1xuEezk-2VRPnu05foQdWTDKJAze-9vZ0wr0L_KyXLZaW0vVfehdGpPgJj_FqVfgnc-hXdL0RADfrKjApsrO7oHpv-Ii3u7oKwauzq5oQ2AXt4cp6ahu8VhOBGQiPXtV98Yw3U3NBetMX-I_qR6-UGsN6Z0pRO0bsdk1ANvMiHT6Y04x8nh4kzk5pfv71ACgnxrs1zxwg7YfYzm2tQXGceu7fwI9sqO_jz2c8RvMsg4u8q0js58F-gR1SVjmTyipnXv0tAGzpZnhg diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-integer-timestamps.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-integer-timestamps.yaml new file mode 100644 index 000000000000..63bc8bef7ba8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-integer-timestamps.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package test + + p = x { + now_ns := 1.678e15 + iat := now_ns / 1e6 + exp := iat + 300 + io.jwt.encode_sign({"alg": "HS256", "typ": "JWT"}, {"iat": iat, "exp": exp}, {"k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", "kty": "oct"}, x) + } + note: jwtencodesign/plain integer timestamps + query: data.test.p = x + want_result: + - x: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NzgwMDAzMDAsImlhdCI6MTY3ODAwMDAwMH0.ZNCOrxE5MNdrqzHmiQ7c3so0IvGqHddBZFWe3kBaQHg diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-set-data.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-set-data.yaml new file mode 100644 index 000000000000..16a1bb860ca5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesign/test-jwtencodesign-set-data.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package test + + p := io.jwt.encode_sign( + {"alg": "HS256", "typ": "JWT"}, + # aud is a set and should be converted to a list + {"aud": {"bob", "saul"}, "exp": 1300819380, "http://example.com/is_root": true, "iss": "joe", "privateParams": {"private_one": "one", "private_two": "two"}}, + {"k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", "kty": "oct"} + ) + query: data.test.p = x + note: set data + want_result: + - x: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYm9iIiwic2F1bCJdLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UiLCJwcml2YXRlUGFyYW1zIjp7InByaXZhdGVfb25lIjoib25lIiwicHJpdmF0ZV90d28iOiJ0d28ifX0.-Or2eol8bzly-Ztb0v7_7UkcKBkN_aNNpK33HK0MeOY diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0379.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0379.yaml new file mode 100644 index 000000000000..26b43819f487 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0379.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.encode_sign_raw("{\"typ:\"JWT\",\r\n \"alg\":\"HS256\"}", "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + note: jwtencodesignheadererrors/Unknown signature algorithm + query: data.generated.p = x + strict_error: true + want_error: invalid character + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0380.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0380.yaml new file mode 100644 index 000000000000..4a8b52565426 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0380.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.encode_sign_raw("{\"alg\":\"dummy\"}", "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + note: jwtencodesignheadererrors/unknown signature algorithm + query: data.generated.p = x + strict_error: true + want_error: unknown signature algorithm + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0381.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0381.yaml new file mode 100644 index 000000000000..2f19849a970e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0381.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.encode_sign_raw("{}", "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + note: jwtencodesignheadererrors/Empty JSON header Error + query: data.generated.p = x + strict_error: true + want_error: unsupported signature algorithm + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0382.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0382.yaml new file mode 100644 index 000000000000..cfc331d9f2ec --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0382.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.encode_sign_raw("", "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + note: jwtencodesignheadererrors/Empty headers input error + query: data.generated.p = x + strict_error: true + want_error: unexpected end of JSON input + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0383.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0383.yaml new file mode 100644 index 000000000000..4a6f43a3a892 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0383.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.encode_sign_raw("e", "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + note: jwtencodesignheadererrors/No JSON Error + query: data.generated.p = x + strict_error: true + want_error: invalid character + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0376.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0376.yaml new file mode 100644 index 000000000000..f1fc1f78749c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0376.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.encode_sign_raw("{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}", "", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + note: jwtencodesignpayloaderrors/No Payload + query: data.generated.p = x + strict_error: true + want_error: type is JWT but payload is not JSON + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0377.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0377.yaml new file mode 100644 index 000000000000..323bd5540868 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0377.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.encode_sign_raw("{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}", "{\"iss:\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + note: jwtencodesignpayloaderrors/Payload JSON Error + query: data.generated.p = x + strict_error: true + want_error: type is JWT but payload is not JSON + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0378.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0378.yaml new file mode 100644 index 000000000000..32077c1e2a53 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0378.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.encode_sign_raw("{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}", "e", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + note: jwtencodesignpayloaderrors/Non JSON Error + query: data.generated.p = x + strict_error: true + want_error: type is JWT but payload is not JSON + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0384.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0384.yaml new file mode 100644 index 000000000000..9579bdb99d38 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0384.yaml @@ -0,0 +1,12 @@ +--- +cases: + - data: {} + modules: + - "package generated\n\np = x {\n io.jwt.encode_sign_raw(`{\"typ\":\"JWT\",\r\n\ + \ \"alg\":\"HS256\"}`, `{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\"\ + :true}`, `{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\ + \n}`, x)\n}\n" + note: jwtencodesignraw/https://tools.ietf.org/html/rfc7515#appendix-A.1 + query: data.generated.p = x + want_result: + - x: eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0385.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0385.yaml new file mode 100644 index 000000000000..22f852f5dbd3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0385.yaml @@ -0,0 +1,11 @@ +--- +cases: + - data: {} + modules: + - "package generated\n\np = x {\n io.jwt.encode_sign_raw(`{\"typ\":\"text/plain\"\ + ,\r\n \"alg\":\"HS256\"}`, ``, `{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\ + \n}`, x)\n}\n" + note: jwtencodesignraw/No Payload but Media Type is Plain + query: data.generated.p = x + want_result: + - x: eyJ0eXAiOiJ0ZXh0L3BsYWluIiwNCiAiYWxnIjoiSFMyNTYifQ..sXoGQMWwM-SmX495-htA7kndgbkwz1PnqsDeY275gnI diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0386.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0386.yaml new file mode 100644 index 000000000000..e4502eb37da8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0386.yaml @@ -0,0 +1,11 @@ +--- +cases: + - data: {} + modules: + - "package generated\n\np = x {\n io.jwt.encode_sign_raw(`{\"typ\":\"text/plain\"\ + ,\r\n \"alg\":\"HS256\"}`, `e`, `{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\ + \n}`, x)\n}\n" + note: jwtencodesignraw/text/plain media type + query: data.generated.p = x + want_result: + - x: eyJ0eXAiOiJ0ZXh0L3BsYWluIiwNCiAiYWxnIjoiSFMyNTYifQ.ZQ.oO8Vnc4Jv7-J231a1bEcQrgXfKbNW-kEvVY7BP1v5rM diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0387.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0387.yaml new file mode 100644 index 000000000000..86cbacf6a431 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0387.yaml @@ -0,0 +1,11 @@ +--- +cases: + - data: {} + modules: + - "package generated\n\np = x {\n io.jwt.encode_sign_raw(`{\"typ\":\"JWT\",\r\n\ + \ \"alg\":\"HS256\"}`, `{}`, `{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\ + \n}`, x)\n}\n" + note: jwtencodesignraw/Empty JSON payload + query: data.generated.p = x + want_result: + - x: eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.e30.KAml6HRetE0sq22SYNh_CQExhf-X31ChYTfGwUBIWu8 diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0388.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0388.yaml new file mode 100644 index 000000000000..12095dfaf23c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtencodesignraw/test-jwtencodesignraw-0388.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - "package generated\n\np = x {\n io.jwt.encode_sign_raw(`{\"alg\":\"RS256\"}`,\ + \ `{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\"\ + :true}`, `{\n \"kty\":\"RSA\",\n \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ\"\ + ,\n \"e\":\"AQAB\",\n \"d\":\"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ\"\ + ,\n \"p\":\"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc\"\ + ,\n \"q\":\"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc\"\ + ,\n \"dp\":\"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0\"\ + ,\n \"dq\":\"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU\"\ + ,\n \"qi\":\"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U\"\ + \n }`, x)\n}\n" + note: jwtencodesignraw/https://tools.ietf.org/html/rfc7515#appendix-A.2 + query: data.generated.p = x + want_result: + - x: eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs256/test-jwtverifyhs256-0440.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs256/test-jwtverifyhs256-0440.yaml new file mode 100644 index 000000000000..e923fc49a2ca --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs256/test-jwtverifyhs256-0440.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_hs256("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM", "secret", x) + } + note: jwtverifyhs256/success + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs256/test-jwtverifyhs256-0441.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs256/test-jwtverifyhs256-0441.yaml new file mode 100644 index 000000000000..9eb60d4d079a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs256/test-jwtverifyhs256-0441.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_hs256("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.R0NDxM1gHTucWQKwayMDre2PbMNR9K9efmOfygDZWcE", "secret", x) + } + note: jwtverifyhs256/failure-bad token + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs256/test-jwtverifyhs256-0442.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs256/test-jwtverifyhs256-0442.yaml new file mode 100644 index 000000000000..00b8ac1fe487 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs256/test-jwtverifyhs256-0442.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_hs256("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0", "secret", x) + } + note: jwtverifyhs256/failure-invalid token + query: data.generated.p = x + strict_error: true + want_error: encoded JWT must have 3 sections, found 2 + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs384/test-jwtverifyhs384-0443.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs384/test-jwtverifyhs384-0443.yaml new file mode 100644 index 000000000000..924168e61b95 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs384/test-jwtverifyhs384-0443.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_hs384("eyJhbGciOiJIUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.g98lHYzuqINVppLMoEZT7jlpX0IBSo9zKGoN9DhQg7Ua3YjLXbJMjzESjIHXOGLB", "secret", x) + } + note: jwtverifyhs384/success + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs384/test-jwtverifyhs384-0444.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs384/test-jwtverifyhs384-0444.yaml new file mode 100644 index 000000000000..4b15ead88cea --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs384/test-jwtverifyhs384-0444.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_hs384("eyJhbGciOiJIUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.g98lHYzuqINVppLMoEZT7jlpX0IBSo9zKGoN9DhQg7Ua3YjLXbJMjzESjIHXOBAD", "secret", x) + } + note: jwtverifyhs384/failure-bad token + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs384/test-jwtverifyhs384-0445.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs384/test-jwtverifyhs384-0445.yaml new file mode 100644 index 000000000000..ade1fc5c3eb9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs384/test-jwtverifyhs384-0445.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_hs384("eyJhbGciOiJIUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0", "secret", x) + } + note: jwtverifyhs384/failure-invalid token + query: data.generated.p = x + strict_error: true + want_error: encoded JWT must have 3 sections, found 2 + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs512/test-jwtverifyhs512-0446.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs512/test-jwtverifyhs512-0446.yaml new file mode 100644 index 000000000000..bf0807d44f54 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs512/test-jwtverifyhs512-0446.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_hs512("eyJhbGciOiJIUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.F6-xviRhK2OLcJJHFivhQqMN_dgX5boDrwbVKkdo9flQQNk-AaKpH3uYycFvBEd_erVefcsri_PkL4fjLSZ7ZA", "secret", x) + } + note: jwtverifyhs512/success + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs512/test-jwtverifyhs512-0447.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs512/test-jwtverifyhs512-0447.yaml new file mode 100644 index 000000000000..61d1d39459d3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs512/test-jwtverifyhs512-0447.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_hs512("eyJhbGciOiJIUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.F6-xviRhK2OLcJJHFivhQqMN_dgX5boDrwbVKkdo9flQQNk-AaKpH3uYycFvBEd_erVefcsri_PkL4fjLSZBAD", "secret", x) + } + note: jwtverifyhs512/failure-bad token + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs512/test-jwtverifyhs512-0448.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs512/test-jwtverifyhs512-0448.yaml new file mode 100644 index 000000000000..8d81fd0a33c2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyhs512/test-jwtverifyhs512-0448.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_hs512("eyJhbGciOiJIUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0", "secret", x) + } + note: jwtverifyhs512/failure-invalid token + query: data.generated.p = x + strict_error: true + want_error: encoded JWT must have 3 sections, found 2 + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0401.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0401.yaml new file mode 100644 index 000000000000..a11f03a9ca03 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0401.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/success-cert + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0402.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0402.yaml new file mode 100644 index 000000000000..b864ff785eec --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0402.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7nJwME0QNM6g0Ou9SyljlcIY4cnBcs8oWVHe74bJ7JTgYmDOk2CA14RE3wJNkUKERP/cRdesKDA/BToJXJUroYvhjXxUYn+i3wK5vOGRY9WUtTF9paIIpIV4USUOwDh3ufhA9K3tyh+ZVsqn80em0Lj2ME0EgScuk6u0/UYjjNvcmnQl+uDmghG8xBZh7TZW2+aceMwlb4LJIP36VRhgjKQGIxg2rW8ROXgJaFbNRCbiOUUqlq9SUZuhHo8TNOARXXxp9R4Fq7Cl7ZbwWtNPwAtM1y+Z+iyu/i91m0YLlU2XBOGLu9IA8IZjPlbCnk/SygpV9NNwTY9DSQ0QfXcPTGlsbFwzRzTlhH25wEl3j+2Ub9w/NX7Yo+j/Ei9eGZ8cq0bcvEwDeIo98HeNZWrLUUArayRYvh8zutOlzqehw8waFk9AxpfEp9oWekSz8gZw9OL773EhnglYxxjkPHNzk66CufLuTEf6uE9NLE5HnlQMbiqBFirIyAWGKyU3v2tphKvcogxmzzWA51p0GY0lGZvlLNt2NrJv2oGecyl3BLqHnBi+rGAosa/8XgfQT8RIk7YR/tDPDmPfaqSIc0po+NcHYEH82Yv+gfKSK++1fyssGCsSRJs8PFMuPGgv62fFrE/EHSsHJaNWojSYce/Trxm2RaHhw/8O4oKcfrbaRf8CAwEAAQ==\n-----END PUBLIC KEY-----", x) + } + note: jwtverifyrsa/success-cert + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0403.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0403.yaml new file mode 100644 index 000000000000..c1a39b1fd9d5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0403.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", `{"kty":"RSA","e":"AQAB","kid":"4db88b6b-cda9-4242-b79e-51346edc313c","n":"7nJwME0QNM6g0Ou9SyljlcIY4cnBcs8oWVHe74bJ7JTgYmDOk2CA14RE3wJNkUKERP_cRdesKDA_BToJXJUroYvhjXxUYn-i3wK5vOGRY9WUtTF9paIIpIV4USUOwDh3ufhA9K3tyh-ZVsqn80em0Lj2ME0EgScuk6u0_UYjjNvcmnQl-uDmghG8xBZh7TZW2-aceMwlb4LJIP36VRhgjKQGIxg2rW8ROXgJaFbNRCbiOUUqlq9SUZuhHo8TNOARXXxp9R4Fq7Cl7ZbwWtNPwAtM1y-Z-iyu_i91m0YLlU2XBOGLu9IA8IZjPlbCnk_SygpV9NNwTY9DSQ0QfXcPTGlsbFwzRzTlhH25wEl3j-2Ub9w_NX7Yo-j_Ei9eGZ8cq0bcvEwDeIo98HeNZWrLUUArayRYvh8zutOlzqehw8waFk9AxpfEp9oWekSz8gZw9OL773EhnglYxxjkPHNzk66CufLuTEf6uE9NLE5HnlQMbiqBFirIyAWGKyU3v2tphKvcogxmzzWA51p0GY0lGZvlLNt2NrJv2oGecyl3BLqHnBi-rGAosa_8XgfQT8RIk7YR_tDPDmPfaqSIc0po-NcHYEH82Yv-gfKSK--1fyssGCsSRJs8PFMuPGgv62fFrE_EHSsHJaNWojSYce_Trxm2RaHhw_8O4oKcfrbaRf8"}`, x) + } + note: jwtverifyrsa/success-jwk + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0404.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0404.yaml new file mode 100644 index 000000000000..13a68c5bc39a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0404.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_ps256("eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJQUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiZm9vIjogImJhciJ9.i0F3MHWzOsBNLqjQzK1UVeQid9xPMowCoUsoM-C2BDxUY-FMKmCeJ1NJ4TGnS9HzFK1ftEvRnPT7EOxOkHPoCk1rz3feTFgtHtNzQqLM1IBTnz6aHHOrda_bKPHH9ZIYCRQUPXhpC90ivW_IJR-f7Z1WLrMXaJ71i1XteruENHrJJJDn0HedHG6N0VHugBHrak5k57cbE31utAdx83TEd8v2Y8wAkCJXKrdmTa-8419LNxW_yjkvoDD53n3X5CHhYkSymU77p0v6yWO38qDWeKJ-Fm_PrMAo72_rizDBj_yPa5LA3bT_EnsgZtC-sp8_SCDIH41bjiCGpRHhqgZmyw", "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/success-ps256-cert + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0405.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0405.yaml new file mode 100644 index 000000000000..2c273b1b05b2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0405.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_ps256("eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJQUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiZm9vIjogImJhciJ9.i0F3MHWzOsBNLqjQzK1UVeQid9xPMowCoUsoM-C2BDxUY-FMKmCeJ1NJ4TGnS9HzFK1ftEvRnPT7EOxOkHPoCk1rz3feTFgtHtNzQqLM1IBTnz6aHHOrda_bKPHH9ZIYCRQUPXhpC90ivW_IJR-f7Z1WLrMXaJ71i1XteruENHrJJJDn0HedHG6N0VHugBHrak5k57cbE31utAdx83TEd8v2Y8wAkCJXKrdmTa-8419LNxW_yjkvoDD53n3X5CHhYkSymU77p0v6yWO38qDWeKJ-Fm_PrMAo72_rizDBj_yPa5LA3bT_EnsgZtC-sp8_SCDIH41bjiCGpRHhqgZmyw", `{"kty":"RSA","e":"AQAB","kid":"bf688c97-bf51-49ba-b9d3-115195bb0eb8","n":"uJApsyzFv-Y85M5JjezHvMDw_spgVCI7BqpYhnzK3xXw1dnkz1bWXGA9yF6AeADlE-1yc1ozrAURTnFSihIgj414i3MC2_0FkNcdAbnX7d9q9_jdCkHda4HER0zzXCaHlgnzoAz6edUU800-h0LleLnfgg4UST-0DFTCIGpfTbs7OPSy2WgT1vP6xbB45CUOJA7o0q6XE-hdhWWN0plrDiYD-0Y1SpOQYXmHhSmr-WVeKeoh5_0zeEVab6TQYec_16ByEyepaZB0g6WyGkFE6aG1NrpvDd24s_h7BAJg_S2mtu1lKWEqYjOgwzEl5XQQyXbpnq1USb12ArX16rZdew"}`, x) + } + note: jwtverifyrsa/success-ps256-jwk + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0406.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0406.yaml new file mode 100644 index 000000000000..3db04d72e813 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0406.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_es256("eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJFUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiaXNzIjogInh4eCJ9.lArczfN-pIL8oUU-7PU83u-zfXougXBZj6drFeKFsPEoVhy9WAyiZlRshYqjTSXdaw8yw2L-ovt4zTUZb2PWMg", "-----BEGIN CERTIFICATE-----\nMIIBcDCCARagAwIBAgIJAMZmuGSIfvgzMAoGCCqGSM49BAMCMBMxETAPBgNVBAMM\nCHdoYXRldmVyMB4XDTE4MDgxMDE0Mjg1NFoXDTE4MDkwOTE0Mjg1NFowEzERMA8G\nA1UEAwwId2hhdGV2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATPwn3WCEXL\nmjp/bFniDwuwsfu7bASlPae2PyWhqGeWwe23Xlyx+tSqxlkXYe4pZ23BkAAscpGj\nyn5gXHExyDlKo1MwUTAdBgNVHQ4EFgQUElRjSoVgKjUqY5AXz2o74cLzzS8wHwYD\nVR0jBBgwFoAUElRjSoVgKjUqY5AXz2o74cLzzS8wDwYDVR0TAQH/BAUwAwEB/zAK\nBggqhkjOPQQDAgNIADBFAiEA4yQ/88ZrUX68c6kOe9G11u8NUaUzd8pLOtkKhniN\nOHoCIHmNX37JOqTcTzGn2u9+c8NlnvZ0uDvsd1BmKPaUmjmm\n-----END CERTIFICATE-----\n", x) + } + note: jwtverifyrsa/success-es256-cert + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0407.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0407.yaml new file mode 100644 index 000000000000..14019d18a98a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0407.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_es256("eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJFUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiaXNzIjogInh4eCJ9.lArczfN-pIL8oUU-7PU83u-zfXougXBZj6drFeKFsPEoVhy9WAyiZlRshYqjTSXdaw8yw2L-ovt4zTUZb2PWMg", `{"kty":"EC","crv":"P-256","x":"z8J91ghFy5o6f2xZ4g8LsLH7u2wEpT2ntj8loahnlsE","y":"7bdeXLH61KrGWRdh7ilnbcGQACxykaPKfmBccTHIOUo"}`, x) + } + note: jwtverifyrsa/success-es256-jwk + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0408.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0408.yaml new file mode 100644 index 000000000000..640d634b1314 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0408.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.Yt89BjaPCNgol478rYyH66-XgkHos02TsVwxLH3ZlvOoIVjbhYW8q1_MHehct1-yBf1UOX3g-lUrIjpoDtX1TfAESuaWTjYPixRvjfJ-Nn75JF8QuAl5PD27C6aJ4PjUPNfj0kwYBnNQ_oX-ZFb781xRi7qRDB6swE4eBUxzHqKUJBLaMM2r8k1-9iE3ERNeqTJUhV__p0aSyRj-i62rdZ4TC5nhxtWodiGP4e4GrYlXkdaKduK63cfdJF-kfZfTsoDs_xy84pZOkzlflxuNv9bNqd-3ISAdWe4gsEvWWJ8v70-QWkydnH8rhj95DaqoXrjfzbOgDpKtdxJC4daVPKvntykzrxKhZ9UtWzm3OvJSKeyWujFZlldiTfBLqNDgdi-Boj_VxO5Pdh-67lC3L-pBMm4BgUqf6rakBQvoH7AV6zD5CbFixh7DuqJ4eJHHItWzJwDctMrV3asm-uOE1E2B7GErGo3iX6S9Iun_kvRUp6kyvOaDq5VvXzQOKyLQIQyHGGs0aIV5cFI2IuO5Rt0uUj5mzPQrQWHgI4r6Mc5bzmq2QLxBQE8OJ1RFhRpsuoWQyDM8aRiMQIJe1g3x4dnxbJK4dYheYblKHFepScYqT1hllDp3oUNn89sIjQIhJTe8KFATu4K8ppluys7vhpE2a_tq8i5O0MFxWmsxN4Q", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/failure-bad token + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0409.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0409.yaml new file mode 100644 index 000000000000..598199da3f5a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0409.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_ps256("eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJQUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiZm9vIjogImJhciJ9.i0F3MHWzOsBNLqjQzK1UVeQid9xPMowCoUsoM-C2BDxUY-FMKmCeJ1NJ4TGnS9HzFK1ftEvRnPT7EOxOkHPoCk1rz3feTFgtHtNzQqLM1IBTnz6aHHOrda_bKPHH9ZIYCRQUPXhpC90ivW_IJR-f7Z1WLrMXaJ71i1XteruENHrJJJDn0HedHG6N0VHugBHrak5k57cbE31utAdx83TEd8v2Y8wAkCJXKrdmTa-8419LNxW_yjkvoDD53n3X5CHhYkSymU77p0v6yWO38qDWeKJ-Fm_PrMAo72_rizDBj_yPa5LA3bT_EnsgZtC-sp8_SCDIH41bjiCGpRHhqgZmyw", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/failure-wrong key + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0410.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0410.yaml new file mode 100644 index 000000000000..4630da949d1b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0410.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_ps256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/failure-wrong alg + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0411.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0411.yaml new file mode 100644 index 000000000000..f7ed3fba0bf8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0411.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/failure-invalid token + query: data.generated.p = x + strict_error: true + want_error: encoded JWT must have 3 sections, found 2 + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0412.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0412.yaml new file mode 100644 index 000000000000..0f66c978283c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0412.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", "-----BEGIN CERT-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERT-----", x) + } + note: jwtverifyrsa/failure-bad pem certificate block + query: data.generated.p = x + strict_error: true + want_error: failed to extract a Key from the PEM certificate + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0413.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0413.yaml new file mode 100644 index 000000000000..d61d76791d1a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0413.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----\nEXTRA", x) + } + note: jwtverifyrsa/failure-extra data after pem certificate block + query: data.generated.p = x + strict_error: true + want_error: extra data after a PEM certificate block + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0414.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0414.yaml new file mode 100644 index 000000000000..5a88f2048041 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0414.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", "-----BEGIN CERTIFICATE-----\ndeadiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/failure-bad pem certificate + query: data.generated.p = x + strict_error: true + want_error: failed to parse a PEM certificate + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0415.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0415.yaml new file mode 100644 index 000000000000..902a9b0fccf2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0415.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", `{"kty":"bogus key type","e":"AQAB","kid":"4db88b6b-cda9-4242-b79e-51346edc313c","n":"7nJwME0QNM6g0Ou9SyljlcIY4cnBcs8oWVHe74bJ7JTgYmDOk2CA14RE3wJNkUKERP_cRdesKDA_BToJXJUroYvhjXxUYn-i3wK5vOGRY9WUtTF9paIIpIV4USUOwDh3ufhA9K3tyh-ZVsqn80em0Lj2ME0EgScuk6u0_UYjjNvcmnQl-uDmghG8xBZh7TZW2-aceMwlb4LJIP36VRhgjKQGIxg2rW8ROXgJaFbNRCbiOUUqlq9SUZuhHo8TNOARXXxp9R4Fq7Cl7ZbwWtNPwAtM1y-Z-iyu_i91m0YLlU2XBOGLu9IA8IZjPlbCnk_SygpV9NNwTY9DSQ0QfXcPTGlsbFwzRzTlhH25wEl3j-2Ub9w_NX7Yo-j_Ei9eGZ8cq0bcvEwDeIo98HeNZWrLUUArayRYvh8zutOlzqehw8waFk9AxpfEp9oWekSz8gZw9OL773EhnglYxxjkPHNzk66CufLuTEf6uE9NLE5HnlQMbiqBFirIyAWGKyU3v2tphKvcogxmzzWA51p0GY0lGZvlLNt2NrJv2oGecyl3BLqHnBi-rGAosa_8XgfQT8RIk7YR_tDPDmPfaqSIc0po-NcHYEH82Yv-gfKSK--1fyssGCsSRJs8PFMuPGgv62fFrE_EHSsHJaNWojSYce_Trxm2RaHhw_8O4oKcfrbaRf8"}`, x) + } + note: jwtverifyrsa/failure-bad jwk key + query: data.generated.p = x + strict_error: true + want_error: failed to parse a JWK key (set) + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0416.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0416.yaml new file mode 100644 index 000000000000..da3915e23047 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0416.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs384("eyJhbGciOiJSUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.b__y2zjqMoD7iWbHeQ0lNpnche3ph5-AwrIQICLMQQGtEz9WMBteHydkC5g01bm3TBX1d04Z5IEOsuK6btAtWma04c5NYqaUyNEUJKYCFoY02uH0jGdGfL6R5Kkv0lkNvN0s3Nex9jMaVVgqx8bcrOU0uRBFT67sXcm11LHaB9BwKFslolzHClxgXy5RIZb4OFk_7Yk7xTC6PcvEWkkGR9uXBhfDEig5WqdwOWPeulimvARDw14U35rzeh9xpGAPjBKeE-y20fXAk0cSF1H69C-Qa1jDQheYIrAJ6XMYGNZWuay5-smmeefe67eweEt1q-AD1NFepqkmZX382DGuYQ", "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkSgAwIBAgIBLjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEV\nMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMD\nRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDg1MTAzWhcNMjAwNTA3\nMTA1MTAzWjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4w\nDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3Qw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeRmygX/fOOUu5Wm91PFNo\nsHDG1CzG9a1iKBjUeMgi9bXXScUfatPmsNlxb56uSi0RXUsvJmY/yxkIIhRyapxW\n49j2idAM3SGGL1nOZf/XdpDHYsAFFZ237HGb8DOEk/p3xCFv0tH/iQ+kLP36EM1+\ntn6BfUXdJnVyvkSK2iMNeRY7A4DMX7sGX39LXsVJiCokIC8E0QUFrSjvrAm9ejKE\ntPojydo4c3VUxLfmFuyMXoD3bfk1Jv5i2J5RjtomjgK6zNCvgYzpspiodHChkzlU\nX8yk2YqlAHX3XdJA94LaDE2kNXiOQnFkUb8GsP7hmEbwGtMUEQie+jfgKplxJ49B\nAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAM\nBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQC9f2/kxT7DnQ94ownhHvd6\nrzk1WirI90rFM2MxhfkaDrOHhSGZL9nDf6TIZ4qeFKZXthpKxpiZm2Oxmn+vUsik\nW6bYjq1nX0GCchQLaaFf9Jh1IOLwkfoBdX55tV8xUGHRWgDlCuGbqiixz+Bm0Kap\nkmbyJynVcoiKhdLyYm/YTn/pC32SJW666reQ+0qCAoxzLQowBetHjwDam9RsDEf4\n+JRDjYPutNXyJ5X8BaBA6PzHanzMG/7RFYcx/2YhXwVxdfPHku4ALJcddIGAGNx2\n5yte+HY0aEu+06J67eD9+4fU7NixRMKigk9KbjqpeWD+0be+VgX8Dot4jaISgI/3\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/success-cert + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0417.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0417.yaml new file mode 100644 index 000000000000..6d8d490fcda1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0417.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs384("eyJhbGciOiJSUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.b__y2zjqMoD7iWbHeQ0lNpnche3ph5-AwrIQICLMQQGtEz9WMBteHydkC5g01bm3TBX1d04Z5IEOsuK6btAtWma04c5NYqaUyNEUJKYCFoY02uH0jGdGfL6R5Kkv0lkNvN0s3Nex9jMaVVgqx8bcrOU0uRBFT67sXcm11LHaB9BwKFslolzHClxgXy5RIZb4OFk_7Yk7xTC6PcvEWkkGR9uXBhfDEig5WqdwOWPeulimvARDw14U35rzeh9xpGAPjBKeE-y20fXAk0cSF1H69C-Qa1jDQheYIrAJ6XMYGNZWuay5-smmeefe67eweEt1q-AD1NFepqkmZX382DGuYQ", "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3kZsoF/3zjlLuVpvdTxT\naLBwxtQsxvWtYigY1HjIIvW110nFH2rT5rDZcW+erkotEV1LLyZmP8sZCCIUcmqc\nVuPY9onQDN0hhi9ZzmX/13aQx2LABRWdt+xxm/AzhJP6d8Qhb9LR/4kPpCz9+hDN\nfrZ+gX1F3SZ1cr5EitojDXkWOwOAzF+7Bl9/S17FSYgqJCAvBNEFBa0o76wJvXoy\nhLT6I8naOHN1VMS35hbsjF6A9235NSb+YtieUY7aJo4CuszQr4GM6bKYqHRwoZM5\nVF/MpNmKpQB1913SQPeC2gxNpDV4jkJxZFG/BrD+4ZhG8BrTFBEInvo34CqZcSeP\nQQIDAQAB\n-----END PUBLIC KEY-----", x) + } + note: jwtverifyrsa/success-key + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0418.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0418.yaml new file mode 100644 index 000000000000..5f7ba6f98136 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0418.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs384("eyJhbGciOiJSUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.b__y2zjqMoD7iWbHeQ0lNpnche3ph5-AwrIQICLMQQGtEz9WMBteHydkC5g01bm3TBX1d04Z5IEOsuK6btAtWma04c5NYqaUyNEUJKYCFoY02uH0jGdGfL6R5Kkv0lkNvN0s3Nex9jMaVVgqx8bcrOU0uRBFT67sXcm11LHaB9BwKFslolzHClxgXy5RIZb4OFk_7Yk7xTC6PcvEWkkGR9uXBhfDEig5WqdwOWPeulimvARDw14U35rzeh9xpGAPjBKeE-y20fXAk0cSF1H69C-Qa1jDQheYIrAJ6XMYGNZWuay5-smmeefe67eweEt1q-AD1NFepqkmZX382DGuYQ", `{"kty":"RSA","n":"3kZsoF_3zjlLuVpvdTxTaLBwxtQsxvWtYigY1HjIIvW110nFH2rT5rDZcW-erkotEV1LLyZmP8sZCCIUcmqcVuPY9onQDN0hhi9ZzmX_13aQx2LABRWdt-xxm_AzhJP6d8Qhb9LR_4kPpCz9-hDNfrZ-gX1F3SZ1cr5EitojDXkWOwOAzF-7Bl9_S17FSYgqJCAvBNEFBa0o76wJvXoyhLT6I8naOHN1VMS35hbsjF6A9235NSb-YtieUY7aJo4CuszQr4GM6bKYqHRwoZM5VF_MpNmKpQB1913SQPeC2gxNpDV4jkJxZFG_BrD-4ZhG8BrTFBEInvo34CqZcSePQQ","e":"AQAB"}`, x) + } + note: jwtverifyrsa/success-jwk + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0419.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0419.yaml new file mode 100644 index 000000000000..15e7bfd7a630 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0419.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs384("eyJhbGciOiJSUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.b__y2zjqMoD7iWbHeQ0lNpnche3ph5-AwrIQICLMQQGtEz9WMBteHydkC5g01bm3TBX1d04Z5IEOsuK6btAtWma04c5NYqaUyNEUJKYCFoY02uH0jGdGfL6R5Kkv0lkNvN0s3Nex9jMaVVgqx8bcrOU0uRBFT67sXcm11LHaB9BwKFslolzHClxgXy5RIZb4OFk_7Yk7xTC6PcvEWkkGR9uXBhfDEig5WqdwOWPeulimvARDw14U35rzeh9xpGAPjBKeE-y20fXAk0cSF1H69C-Qa1jDQheYIrAJ6XMYGNZWuay5-smmeefe67eweEt1q-AD1NFepqkmZX382DGuYQ", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/failure-wrong key + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0420.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0420.yaml new file mode 100644 index 000000000000..f456f8f15735 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0420.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs512("eyJhbGciOiJSUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VSe3qK5Gp0Q0_5nRgMFu25yw74FIgX-kXPOemSi62l-AxeVdUw8rOpEFrSTCaVjd3mPfKb-B056a-gtrbpXK9sUQnFdqdsyt8gHK-umz5lVyWfoAgj51Ontv-9K_pRORD9wqKqdTLZjCxJ5tyKoO0gY3SwwqSqGrp85vUjvEcK3jbMKINGRUNnOokeSm7byUEJsfKVUbPboSX1TGyvjDOZxxSITj8-bzZZ3F21DJ23N2IiJN7FW8Xj-SYyphXo-ML50o5bjW9YlQ5BDk-RW1I4eE-KpsxhApPv_xIgE8d89PVtXFuoJtv0yLRaZ1q04Fl9KNoMyZrmr349yppn0JlQ", "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkSgAwIBAgIBLjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEV\nMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMD\nRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDg1MTAzWhcNMjAwNTA3\nMTA1MTAzWjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4w\nDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3Qw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeRmygX/fOOUu5Wm91PFNo\nsHDG1CzG9a1iKBjUeMgi9bXXScUfatPmsNlxb56uSi0RXUsvJmY/yxkIIhRyapxW\n49j2idAM3SGGL1nOZf/XdpDHYsAFFZ237HGb8DOEk/p3xCFv0tH/iQ+kLP36EM1+\ntn6BfUXdJnVyvkSK2iMNeRY7A4DMX7sGX39LXsVJiCokIC8E0QUFrSjvrAm9ejKE\ntPojydo4c3VUxLfmFuyMXoD3bfk1Jv5i2J5RjtomjgK6zNCvgYzpspiodHChkzlU\nX8yk2YqlAHX3XdJA94LaDE2kNXiOQnFkUb8GsP7hmEbwGtMUEQie+jfgKplxJ49B\nAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAM\nBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQC9f2/kxT7DnQ94ownhHvd6\nrzk1WirI90rFM2MxhfkaDrOHhSGZL9nDf6TIZ4qeFKZXthpKxpiZm2Oxmn+vUsik\nW6bYjq1nX0GCchQLaaFf9Jh1IOLwkfoBdX55tV8xUGHRWgDlCuGbqiixz+Bm0Kap\nkmbyJynVcoiKhdLyYm/YTn/pC32SJW666reQ+0qCAoxzLQowBetHjwDam9RsDEf4\n+JRDjYPutNXyJ5X8BaBA6PzHanzMG/7RFYcx/2YhXwVxdfPHku4ALJcddIGAGNx2\n5yte+HY0aEu+06J67eD9+4fU7NixRMKigk9KbjqpeWD+0be+VgX8Dot4jaISgI/3\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/success-cert + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0421.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0421.yaml new file mode 100644 index 000000000000..286f2f9d0cba --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0421.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs512("eyJhbGciOiJSUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VSe3qK5Gp0Q0_5nRgMFu25yw74FIgX-kXPOemSi62l-AxeVdUw8rOpEFrSTCaVjd3mPfKb-B056a-gtrbpXK9sUQnFdqdsyt8gHK-umz5lVyWfoAgj51Ontv-9K_pRORD9wqKqdTLZjCxJ5tyKoO0gY3SwwqSqGrp85vUjvEcK3jbMKINGRUNnOokeSm7byUEJsfKVUbPboSX1TGyvjDOZxxSITj8-bzZZ3F21DJ23N2IiJN7FW8Xj-SYyphXo-ML50o5bjW9YlQ5BDk-RW1I4eE-KpsxhApPv_xIgE8d89PVtXFuoJtv0yLRaZ1q04Fl9KNoMyZrmr349yppn0JlQ", "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3kZsoF/3zjlLuVpvdTxT\naLBwxtQsxvWtYigY1HjIIvW110nFH2rT5rDZcW+erkotEV1LLyZmP8sZCCIUcmqc\nVuPY9onQDN0hhi9ZzmX/13aQx2LABRWdt+xxm/AzhJP6d8Qhb9LR/4kPpCz9+hDN\nfrZ+gX1F3SZ1cr5EitojDXkWOwOAzF+7Bl9/S17FSYgqJCAvBNEFBa0o76wJvXoy\nhLT6I8naOHN1VMS35hbsjF6A9235NSb+YtieUY7aJo4CuszQr4GM6bKYqHRwoZM5\nVF/MpNmKpQB1913SQPeC2gxNpDV4jkJxZFG/BrD+4ZhG8BrTFBEInvo34CqZcSeP\nQQIDAQAB\n-----END PUBLIC KEY-----", x) + } + note: jwtverifyrsa/success-key + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0422.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0422.yaml new file mode 100644 index 000000000000..104887f3f52a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0422.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs512("eyJhbGciOiJSUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VSe3qK5Gp0Q0_5nRgMFu25yw74FIgX-kXPOemSi62l-AxeVdUw8rOpEFrSTCaVjd3mPfKb-B056a-gtrbpXK9sUQnFdqdsyt8gHK-umz5lVyWfoAgj51Ontv-9K_pRORD9wqKqdTLZjCxJ5tyKoO0gY3SwwqSqGrp85vUjvEcK3jbMKINGRUNnOokeSm7byUEJsfKVUbPboSX1TGyvjDOZxxSITj8-bzZZ3F21DJ23N2IiJN7FW8Xj-SYyphXo-ML50o5bjW9YlQ5BDk-RW1I4eE-KpsxhApPv_xIgE8d89PVtXFuoJtv0yLRaZ1q04Fl9KNoMyZrmr349yppn0JlQ", `{"kty":"RSA","n":"3kZsoF_3zjlLuVpvdTxTaLBwxtQsxvWtYigY1HjIIvW110nFH2rT5rDZcW-erkotEV1LLyZmP8sZCCIUcmqcVuPY9onQDN0hhi9ZzmX_13aQx2LABRWdt-xxm_AzhJP6d8Qhb9LR_4kPpCz9-hDNfrZ-gX1F3SZ1cr5EitojDXkWOwOAzF-7Bl9_S17FSYgqJCAvBNEFBa0o76wJvXoyhLT6I8naOHN1VMS35hbsjF6A9235NSb-YtieUY7aJo4CuszQr4GM6bKYqHRwoZM5VF_MpNmKpQB1913SQPeC2gxNpDV4jkJxZFG_BrD-4ZhG8BrTFBEInvo34CqZcSePQQ","e":"AQAB"}`, x) + } + note: jwtverifyrsa/success-jwk + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0423.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0423.yaml new file mode 100644 index 000000000000..4917bb1675ff --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0423.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_rs512("eyJhbGciOiJSUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VSe3qK5Gp0Q0_5nRgMFu25yw74FIgX-kXPOemSi62l-AxeVdUw8rOpEFrSTCaVjd3mPfKb-B056a-gtrbpXK9sUQnFdqdsyt8gHK-umz5lVyWfoAgj51Ontv-9K_pRORD9wqKqdTLZjCxJ5tyKoO0gY3SwwqSqGrp85vUjvEcK3jbMKINGRUNnOokeSm7byUEJsfKVUbPboSX1TGyvjDOZxxSITj8-bzZZ3F21DJ23N2IiJN7FW8Xj-SYyphXo-ML50o5bjW9YlQ5BDk-RW1I4eE-KpsxhApPv_xIgE8d89PVtXFuoJtv0yLRaZ1q04Fl9KNoMyZrmr349yppn0JlQ", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/failure-wrong key + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0424.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0424.yaml new file mode 100644 index 000000000000..24782bce3fcb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0424.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_ps384("eyJhbGciOiJQUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.EHPUvPr6uJOYqdza95WbM1SYD8atZHJEVRggpwOWnHGsjQBoEarJb8QgW7TY22OXwGw2HWluTiyT_MAz02NaHRzZv6AgrmxCLChMWkCHLwPxqjs0xSvVAMLzHHq2X2Bcujo9KORGudR7zKz8pOX5Mfnm7Z6OGtqPCPLaIdVJlddNsG6a571NOuVuDWbcg0omeRDANZpCZMJeAQN2M-4Q61ef6zcQHK1R-QqzBhw6HzMgqR1LRJ0xbrmD-L5o53JM3pV1e1juKNXVK3vWkDQRCQORFn1lyH5isfSsiiHW-x90sUC7TrU_cOji4MMmOCME6kkwxe57ZgpeXtdVTvldpw", "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkSgAwIBAgIBKjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEV\nMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMD\nRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDkxMjU2WhcNMjAwNTA3\nMTExMjU2WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4w\nDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3Qw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtyVWH2FE8cU8LRcArH4Tw\nDhBOFcmJF28LrvRObcbDYsae6rEwby0aRgSxjTEMgyGBjroBSl22wOSA93kwx4pu\npfXEqbwywn9FhyKBb/OXQSglPmwrpmzQtPJGzBHncL+PPjRhPfqimwf7ZIPKAAgI\nz9O6ppGhE/x4Ct444jthUIBZuG5cUXhgiPBQdIQ3K88QhgVwcufTZkNHj4iSDfhl\nVFDHVjXjd2B/yGODjyv0TyChV0YBNGjMv7YFLWmIFUFzK+6qNSxu4czPtRkyfwaV\n2GW/PBT5f8fc6fKgQZ6k6BLK6+pi0iPh5TUizyHtqtueWDbrJ+wfdJilQS8D+EGr\nAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAM\nBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQBgVAM50/0aBTBxESYKIKN4\nE+qbV6aE0C+wYJLet0EWPTxwmFZamq5LNEO/D6xyoY5WHY60EyHRMs0agSB/ATBX\n5ULdEwh9G0NjxqivCcoddQ1fuVS2PrrqNL7VlRnYbTpd8/Dh4qnyl5FltlyZ/29L\ny7BWOwlcBlZdhsfH8svNX4PUxjRD+jmnczCDi7XSOKT8htKUV2ih1c9JrWpIhCi/\nHzEXkaAxNdhBNdIsLQMo3qq9fkSgNZQk9/ecJNPeuJ/UYyr5Xa4PxIWl4U+P7yuI\n+Q3FSPmUbiVsSGqMhh6V/DN8M+T5/KiSB47gFOfxc2/RR5aw4HkSp3WxwbT9njbE\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/success-ps384-cert + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0425.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0425.yaml new file mode 100644 index 000000000000..be46c0d1ac77 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0425.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_ps384("eyJhbGciOiJQUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.EHPUvPr6uJOYqdza95WbM1SYD8atZHJEVRggpwOWnHGsjQBoEarJb8QgW7TY22OXwGw2HWluTiyT_MAz02NaHRzZv6AgrmxCLChMWkCHLwPxqjs0xSvVAMLzHHq2X2Bcujo9KORGudR7zKz8pOX5Mfnm7Z6OGtqPCPLaIdVJlddNsG6a571NOuVuDWbcg0omeRDANZpCZMJeAQN2M-4Q61ef6zcQHK1R-QqzBhw6HzMgqR1LRJ0xbrmD-L5o53JM3pV1e1juKNXVK3vWkDQRCQORFn1lyH5isfSsiiHW-x90sUC7TrU_cOji4MMmOCME6kkwxe57ZgpeXtdVTvldpw", "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7clVh9hRPHFPC0XAKx+E\n8A4QThXJiRdvC670Tm3Gw2LGnuqxMG8tGkYEsY0xDIMhgY66AUpdtsDkgPd5MMeK\nbqX1xKm8MsJ/RYcigW/zl0EoJT5sK6Zs0LTyRswR53C/jz40YT36opsH+2SDygAI\nCM/TuqaRoRP8eAreOOI7YVCAWbhuXFF4YIjwUHSENyvPEIYFcHLn02ZDR4+Ikg34\nZVRQx1Y143dgf8hjg48r9E8goVdGATRozL+2BS1piBVBcyvuqjUsbuHMz7UZMn8G\nldhlvzwU+X/H3OnyoEGepOgSyuvqYtIj4eU1Is8h7arbnlg26yfsH3SYpUEvA/hB\nqwIDAQAB\n-----END PUBLIC KEY-----", x) + } + note: jwtverifyrsa/success-ps384-key + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0426.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0426.yaml new file mode 100644 index 000000000000..88204040cc18 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0426.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_ps384("eyJhbGciOiJQUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.EHPUvPr6uJOYqdza95WbM1SYD8atZHJEVRggpwOWnHGsjQBoEarJb8QgW7TY22OXwGw2HWluTiyT_MAz02NaHRzZv6AgrmxCLChMWkCHLwPxqjs0xSvVAMLzHHq2X2Bcujo9KORGudR7zKz8pOX5Mfnm7Z6OGtqPCPLaIdVJlddNsG6a571NOuVuDWbcg0omeRDANZpCZMJeAQN2M-4Q61ef6zcQHK1R-QqzBhw6HzMgqR1LRJ0xbrmD-L5o53JM3pV1e1juKNXVK3vWkDQRCQORFn1lyH5isfSsiiHW-x90sUC7TrU_cOji4MMmOCME6kkwxe57ZgpeXtdVTvldpw", `{"kty":"RSA","n":"7clVh9hRPHFPC0XAKx-E8A4QThXJiRdvC670Tm3Gw2LGnuqxMG8tGkYEsY0xDIMhgY66AUpdtsDkgPd5MMeKbqX1xKm8MsJ_RYcigW_zl0EoJT5sK6Zs0LTyRswR53C_jz40YT36opsH-2SDygAICM_TuqaRoRP8eAreOOI7YVCAWbhuXFF4YIjwUHSENyvPEIYFcHLn02ZDR4-Ikg34ZVRQx1Y143dgf8hjg48r9E8goVdGATRozL-2BS1piBVBcyvuqjUsbuHMz7UZMn8GldhlvzwU-X_H3OnyoEGepOgSyuvqYtIj4eU1Is8h7arbnlg26yfsH3SYpUEvA_hBqw","e":"AQAB"}`, x) + } + note: jwtverifyrsa/success-ps384-jwk + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0427.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0427.yaml new file mode 100644 index 000000000000..4a0f4e0bab0e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0427.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_ps384("eyJhbGciOiJQUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.EHPUvPr6uJOYqdza95WbM1SYD8atZHJEVRggpwOWnHGsjQBoEarJb8QgW7TY22OXwGw2HWluTiyT_MAz02NaHRzZv6AgrmxCLChMWkCHLwPxqjs0xSvVAMLzHHq2X2Bcujo9KORGudR7zKz8pOX5Mfnm7Z6OGtqPCPLaIdVJlddNsG6a571NOuVuDWbcg0omeRDANZpCZMJeAQN2M-4Q61ef6zcQHK1R-QqzBhw6HzMgqR1LRJ0xbrmD-L5o53JM3pV1e1juKNXVK3vWkDQRCQORFn1lyH5isfSsiiHW-x90sUC7TrU_cOji4MMmOCME6kkwxe57ZgpeXtdVTvldpw", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/failure-ps384-wrong key + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0428.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0428.yaml new file mode 100644 index 000000000000..86dbeb05f1f6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0428.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_ps512("eyJhbGciOiJQUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VRlkPtiUq5MmBNgyuBqxv2_aX40STrWrBB2sSmGbxI78jVG_3hVoh7Mk-wUmFL389qpf05xNdn-gpMe-MSDUux7U7EuFspFZdYTUBo9wRvEBe4e1rHUCG00lVdYCG7eEgbAxM3cUhrHRwExBte30qBrFFUY9FgG-kJdYhgyh7VquMGuKgiS8CP_H0Gp1mIvTw6eEnSFAoKiryw9edUZ78pHELNn4y18YZvEndeNZh7f19LCtrB0G2bJUHGM4vPcwo2D-UAhEFBpSlnnqXDLSWOhUgLNLu0kZACXhT808KT6fdF6eFihdThmWN7_HUz2znjrjs71CqqDJgLhkGs8UvQ", "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkSgAwIBAgIBKjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEV\nMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMD\nRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDkxMjU2WhcNMjAwNTA3\nMTExMjU2WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4w\nDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3Qw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtyVWH2FE8cU8LRcArH4Tw\nDhBOFcmJF28LrvRObcbDYsae6rEwby0aRgSxjTEMgyGBjroBSl22wOSA93kwx4pu\npfXEqbwywn9FhyKBb/OXQSglPmwrpmzQtPJGzBHncL+PPjRhPfqimwf7ZIPKAAgI\nz9O6ppGhE/x4Ct444jthUIBZuG5cUXhgiPBQdIQ3K88QhgVwcufTZkNHj4iSDfhl\nVFDHVjXjd2B/yGODjyv0TyChV0YBNGjMv7YFLWmIFUFzK+6qNSxu4czPtRkyfwaV\n2GW/PBT5f8fc6fKgQZ6k6BLK6+pi0iPh5TUizyHtqtueWDbrJ+wfdJilQS8D+EGr\nAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAM\nBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQBgVAM50/0aBTBxESYKIKN4\nE+qbV6aE0C+wYJLet0EWPTxwmFZamq5LNEO/D6xyoY5WHY60EyHRMs0agSB/ATBX\n5ULdEwh9G0NjxqivCcoddQ1fuVS2PrrqNL7VlRnYbTpd8/Dh4qnyl5FltlyZ/29L\ny7BWOwlcBlZdhsfH8svNX4PUxjRD+jmnczCDi7XSOKT8htKUV2ih1c9JrWpIhCi/\nHzEXkaAxNdhBNdIsLQMo3qq9fkSgNZQk9/ecJNPeuJ/UYyr5Xa4PxIWl4U+P7yuI\n+Q3FSPmUbiVsSGqMhh6V/DN8M+T5/KiSB47gFOfxc2/RR5aw4HkSp3WxwbT9njbE\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/success-ps512-cert + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0429.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0429.yaml new file mode 100644 index 000000000000..72fe7f561920 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0429.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_ps512("eyJhbGciOiJQUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VRlkPtiUq5MmBNgyuBqxv2_aX40STrWrBB2sSmGbxI78jVG_3hVoh7Mk-wUmFL389qpf05xNdn-gpMe-MSDUux7U7EuFspFZdYTUBo9wRvEBe4e1rHUCG00lVdYCG7eEgbAxM3cUhrHRwExBte30qBrFFUY9FgG-kJdYhgyh7VquMGuKgiS8CP_H0Gp1mIvTw6eEnSFAoKiryw9edUZ78pHELNn4y18YZvEndeNZh7f19LCtrB0G2bJUHGM4vPcwo2D-UAhEFBpSlnnqXDLSWOhUgLNLu0kZACXhT808KT6fdF6eFihdThmWN7_HUz2znjrjs71CqqDJgLhkGs8UvQ", "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7clVh9hRPHFPC0XAKx+E\n8A4QThXJiRdvC670Tm3Gw2LGnuqxMG8tGkYEsY0xDIMhgY66AUpdtsDkgPd5MMeK\nbqX1xKm8MsJ/RYcigW/zl0EoJT5sK6Zs0LTyRswR53C/jz40YT36opsH+2SDygAI\nCM/TuqaRoRP8eAreOOI7YVCAWbhuXFF4YIjwUHSENyvPEIYFcHLn02ZDR4+Ikg34\nZVRQx1Y143dgf8hjg48r9E8goVdGATRozL+2BS1piBVBcyvuqjUsbuHMz7UZMn8G\nldhlvzwU+X/H3OnyoEGepOgSyuvqYtIj4eU1Is8h7arbnlg26yfsH3SYpUEvA/hB\nqwIDAQAB\n-----END PUBLIC KEY-----", x) + } + note: jwtverifyrsa/success-ps512-key + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0430.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0430.yaml new file mode 100644 index 000000000000..ac6bb7ba8ab3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0430.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_ps512("eyJhbGciOiJQUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VRlkPtiUq5MmBNgyuBqxv2_aX40STrWrBB2sSmGbxI78jVG_3hVoh7Mk-wUmFL389qpf05xNdn-gpMe-MSDUux7U7EuFspFZdYTUBo9wRvEBe4e1rHUCG00lVdYCG7eEgbAxM3cUhrHRwExBte30qBrFFUY9FgG-kJdYhgyh7VquMGuKgiS8CP_H0Gp1mIvTw6eEnSFAoKiryw9edUZ78pHELNn4y18YZvEndeNZh7f19LCtrB0G2bJUHGM4vPcwo2D-UAhEFBpSlnnqXDLSWOhUgLNLu0kZACXhT808KT6fdF6eFihdThmWN7_HUz2znjrjs71CqqDJgLhkGs8UvQ", `{"kty":"RSA","n":"7clVh9hRPHFPC0XAKx-E8A4QThXJiRdvC670Tm3Gw2LGnuqxMG8tGkYEsY0xDIMhgY66AUpdtsDkgPd5MMeKbqX1xKm8MsJ_RYcigW_zl0EoJT5sK6Zs0LTyRswR53C_jz40YT36opsH-2SDygAICM_TuqaRoRP8eAreOOI7YVCAWbhuXFF4YIjwUHSENyvPEIYFcHLn02ZDR4-Ikg34ZVRQx1Y143dgf8hjg48r9E8goVdGATRozL-2BS1piBVBcyvuqjUsbuHMz7UZMn8GldhlvzwU-X_H3OnyoEGepOgSyuvqYtIj4eU1Is8h7arbnlg26yfsH3SYpUEvA_hBqw","e":"AQAB"}`, x) + } + note: jwtverifyrsa/success-ps512-jwk + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0431.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0431.yaml new file mode 100644 index 000000000000..6a3296871f11 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0431.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_ps512("eyJhbGciOiJQUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VRlkPtiUq5MmBNgyuBqxv2_aX40STrWrBB2sSmGbxI78jVG_3hVoh7Mk-wUmFL389qpf05xNdn-gpMe-MSDUux7U7EuFspFZdYTUBo9wRvEBe4e1rHUCG00lVdYCG7eEgbAxM3cUhrHRwExBte30qBrFFUY9FgG-kJdYhgyh7VquMGuKgiS8CP_H0Gp1mIvTw6eEnSFAoKiryw9edUZ78pHELNn4y18YZvEndeNZh7f19LCtrB0G2bJUHGM4vPcwo2D-UAhEFBpSlnnqXDLSWOhUgLNLu0kZACXhT808KT6fdF6eFihdThmWN7_HUz2znjrjs71CqqDJgLhkGs8UvQ", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/failure-wrong key + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0432.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0432.yaml new file mode 100644 index 000000000000..fbd622969410 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0432.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_es384("eyJhbGciOiJFUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.w85PzWrIQbJBOROnah0pa8or2LsXWnj88bwG1R-zf5Mm20CaYGPKPTQEsU_y-dzaWyDV1Na7nfaGaH3Khcvj8yS-bidZ0OZVVFDk9oabX7ZYvAHo2pTAOfxc11TeOYSF", "-----BEGIN CERTIFICATE-----\nMIICDDCCAZOgAwIBAgIBIzAKBggqhkjOPQQDAzBWMQswCQYDVQQGEwJVUzEVMBMG\nA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2\nMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDk0MzU1WhcNMjAwNTA3MTE0\nMzU1WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYD\nVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwdjAQ\nBgcqhkjOPQIBBgUrgQQAIgNiAARjcwW7g9wx4ePsuwcVzDJCVo4f8I1C1X5US4B1\nrWN+5zFSJoGCKaPTXMDhAdS08D1G20AIRmA0AlVVXRxrZYZ+Y282O6s+EGsB5T1W\nMCnUFk2Sa+xZiGPApYz4zSGbNEqjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE\nDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMAoGCCqGSM49BAMDA2cAMGQCMGSG\nVjx3DZP71ZGNDBw+AVdhNU3pgJW8kNpqjta3HFLb6pzqNOsfOn1ZeIWciEcyEgIw\nTGxli48W1AJ2s7Pw+3wOA6f9HAmczJPaiZ9CY038UiT8mk+pND5FEdqLhT/5lMEz\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/success-es384-cert + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0433.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0433.yaml new file mode 100644 index 000000000000..c3bd6640ecfa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0433.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_es384("eyJhbGciOiJFUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.w85PzWrIQbJBOROnah0pa8or2LsXWnj88bwG1R-zf5Mm20CaYGPKPTQEsU_y-dzaWyDV1Na7nfaGaH3Khcvj8yS-bidZ0OZVVFDk9oabX7ZYvAHo2pTAOfxc11TeOYSF", "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEY3MFu4PcMeHj7LsHFcwyQlaOH/CNQtV+\nVEuAda1jfucxUiaBgimj01zA4QHUtPA9RttACEZgNAJVVV0ca2WGfmNvNjurPhBr\nAeU9VjAp1BZNkmvsWYhjwKWM+M0hmzRK\n-----END PUBLIC KEY-----", x) + } + note: jwtverifyrsa/success-es384-key + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0434.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0434.yaml new file mode 100644 index 000000000000..048f1266e020 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0434.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_es384("eyJhbGciOiJFUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.w85PzWrIQbJBOROnah0pa8or2LsXWnj88bwG1R-zf5Mm20CaYGPKPTQEsU_y-dzaWyDV1Na7nfaGaH3Khcvj8yS-bidZ0OZVVFDk9oabX7ZYvAHo2pTAOfxc11TeOYSF", `{"kty":"EC","crv":"P-384","x":"Y3MFu4PcMeHj7LsHFcwyQlaOH_CNQtV-VEuAda1jfucxUiaBgimj01zA4QHUtPA9","y":"RttACEZgNAJVVV0ca2WGfmNvNjurPhBrAeU9VjAp1BZNkmvsWYhjwKWM-M0hmzRK"}`, x) + } + note: jwtverifyrsa/success-es384-jwk + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0435.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0435.yaml new file mode 100644 index 000000000000..20a3b4e6b965 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0435.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_es384("eyJhbGciOiJFUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.w85PzWrIQbJBOROnah0pa8or2LsXWnj88bwG1R-zf5Mm20CaYGPKPTQEsU_y-dzaWyDV1Na7nfaGaH3Khcvj8yS-bidZ0OZVVFDk9oabX7ZYvAHo2pTAOfxc11TeOYSF", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/failure-wrong key + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0436.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0436.yaml new file mode 100644 index 000000000000..140501eb6ca8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0436.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_es512("eyJhbGciOiJFUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.AYpssEoEqq9We9aKsnRykpECAVEOBRJJu8UgDzoL-F8fmB2LPxpS4Gl7D-9wAO5AJt4-9YSsgOb5FLc20MrZN30AAFYopZf75T1pEJQFrdDmOKT45abbrorcR7G_AHDbhBdDNM_R6GojYFg_HPxHndof745Yq5Tfw9PpJc-9kSyk6kqO", "-----BEGIN CERTIFICATE-----\nMIICWDCCAbmgAwIBAgIBAjAKBggqhkjOPQQDBDBWMQswCQYDVQQGEwJVUzEVMBMG\nA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2\nMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MTA1NDM3WhcNMjAwNTA3MTI1\nNDM3WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYD\nVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwgZsw\nEAYHKoZIzj0CAQYFK4EEACMDgYYABAHLm3IMD/88vC/S1cCTyjrCjwHIGsjibFBw\nPBXt36YKCjUdS7jiJJR5YQVPypSv7gPaKKn1E8CqkfVdd3rrp1TocAEms4XvigtW\nZBZzffw9xyZCgmtQ2dTHsufi/5W/Yx8N3Uw+D2wl1LKcJraouo+qgamGfuou6WbA\noPEtdOg0+B4jF6M1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUF\nBwMBMAwGA1UdEwEB/wQCMAAwCgYIKoZIzj0EAwQDgYwAMIGIAkIAzAAYDqMghX3S\n8UbS8s5TPAztJy9oNXFra5V8pPlUdNFc2ov2LN++scW46wCb/cJUyEc58sY7xFuK\nI5sCOkv95N8CQgFXmu354LZJ31zIovuUA8druOPe3TDnxMGwEEm2Lt43JNuhzNyP\nhJYh9/QKfe2AiwrLXEG4VVOIXdjq7vexl87evg==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/success-es512-cert + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0437.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0437.yaml new file mode 100644 index 000000000000..00b67654245f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0437.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_es512("eyJhbGciOiJFUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.AYpssEoEqq9We9aKsnRykpECAVEOBRJJu8UgDzoL-F8fmB2LPxpS4Gl7D-9wAO5AJt4-9YSsgOb5FLc20MrZN30AAFYopZf75T1pEJQFrdDmOKT45abbrorcR7G_AHDbhBdDNM_R6GojYFg_HPxHndof745Yq5Tfw9PpJc-9kSyk6kqO", "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBy5tyDA//PLwv0tXAk8o6wo8ByBrI\n4mxQcDwV7d+mCgo1HUu44iSUeWEFT8qUr+4D2iip9RPAqpH1XXd666dU6HABJrOF\n74oLVmQWc338PccmQoJrUNnUx7Ln4v+Vv2MfDd1MPg9sJdSynCa2qLqPqoGphn7q\nLulmwKDxLXToNPgeIxc=\n-----END PUBLIC KEY-----", x) + } + note: jwtverifyrsa/success-es512-key + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0438.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0438.yaml new file mode 100644 index 000000000000..72635b0f7246 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0438.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_es512("eyJhbGciOiJFUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.AYpssEoEqq9We9aKsnRykpECAVEOBRJJu8UgDzoL-F8fmB2LPxpS4Gl7D-9wAO5AJt4-9YSsgOb5FLc20MrZN30AAFYopZf75T1pEJQFrdDmOKT45abbrorcR7G_AHDbhBdDNM_R6GojYFg_HPxHndof745Yq5Tfw9PpJc-9kSyk6kqO", `{"kty":"EC","crv":"P-521","x":"AcubcgwP_zy8L9LVwJPKOsKPAcgayOJsUHA8Fe3fpgoKNR1LuOIklHlhBU_KlK_uA9ooqfUTwKqR9V13euunVOhw","y":"ASazhe-KC1ZkFnN9_D3HJkKCa1DZ1Mey5-L_lb9jHw3dTD4PbCXUspwmtqi6j6qBqYZ-6i7pZsCg8S106DT4HiMX"}`, x) + } + note: jwtverifyrsa/success-es512-jwk + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0439.yaml b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0439.yaml new file mode 100644 index 000000000000..a588307d445b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/jwtverifyrsa/test-jwtverifyrsa-0439.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + io.jwt.verify_es512("eyJhbGciOiJFUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.AYpssEoEqq9We9aKsnRykpECAVEOBRJJu8UgDzoL-F8fmB2LPxpS4Gl7D-9wAO5AJt4-9YSsgOb5FLc20MrZN30AAFYopZf75T1pEJQFrdDmOKT45abbrorcR7G_AHDbhBdDNM_R6GojYFg_HPxHndof745Yq5Tfw9PpJc-9kSyk6kqO", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + note: jwtverifyrsa/failure-wrong key + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-as.yaml b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-as.yaml new file mode 100644 index 000000000000..793678e695c8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-as.yaml @@ -0,0 +1,200 @@ +--- +cases: + - note: keywordrefs/as keyword in package + query: data.foo.p = x + modules: + - | + package foo.as.bar + + baz := 42 + - | + package foo + import data.foo.as.bar + + p { + bar.baz == 42 + data.foo.as.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/as keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.as + + bar := 42 + - | + package foo + import data.foo.as as my_if + + p { + my_if.bar == 42 + data.foo.as.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/as keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p { + as.foo == 1 + foo.as == 2 + } + + as.foo := 1 + + foo.as := 2 + want_result: + - x: true + - note: keywordrefs/as keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p { + as.foo.bar == 3 + foo.bar.as == 6 + } + + as.foo.bar := 1 { + input.x == 1 + } else := 2 { + input.x == 2 + } else := 3 + + foo.bar.as := 4 { + input.x == 1 + } else := 5 { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/as keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + import future.keywords.contains + + p { + as.foo.bar == {"a", "c"} + foo.bar.as == {"a", "c"} + } + + as.foo.bar contains "a" + + as.foo.bar contains "b" { + false + } + + as.foo.bar contains "c" { + true + } + + foo.bar.as contains "a" + + foo.bar.as contains "b" { + false + } + + foo.bar.as contains "c" { + true + } + want_result: + - x: true + - note: keywordrefs/as keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p { + as.foo == "a" + as.bar.one == "a" + as.bar.three == "c" + foo.as == "a" + bar.baz.as == "a" + } + + as.foo := "a" + + as.foo := "b" { + false + } + + as.foo := "c" { + false + } + + as.bar.one := "a" + + as.bar.two := "b" { + false + } + + as.bar.three := "c" { + true + } + + foo.as := "a" + + foo.as := "b" { + false + } + + foo.as := "c" { + false + } + + bar.baz.as := "a" + + bar.baz.as := "b" { + false + } + + bar.baz.as := "c" { + false + } + want_result: + - x: true + - note: keywordrefs/as keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p { + as.foo(1) == 1 + as.foo(11) == 42 + foo.as(1) == 1 + foo.as(11) == 42 + bar.as.baz(1) == 1 + bar.as.baz(11) == 42 + } + + default as.foo(_) := 42 + + as.foo(x) := x { + x < 10 + } + + default foo.as(_) := 42 + + foo.as(x) := x { + x < 10 + } + + default bar.as.baz(_) := 42 + + bar.as.baz(x) := x { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-default.yaml b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-default.yaml new file mode 100644 index 000000000000..ccd11896c05f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-default.yaml @@ -0,0 +1,200 @@ +--- +cases: + - note: keywordrefs/default keyword in package + query: data.foo.p = x + modules: + - | + package foo.default.bar + + baz := 42 + - | + package foo + import data.foo.default.bar + + p { + bar.baz == 42 + data.foo.default.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/default keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.default + + bar := 42 + - | + package foo + import data.foo.default as my_if + + p { + my_if.bar == 42 + data.foo.default.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/default keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p { + default.foo == 1 + foo.default == 2 + } + + default.foo := 1 + + foo.default := 2 + want_result: + - x: true + - note: keywordrefs/default keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p { + default.foo.bar == 3 + foo.bar.default == 6 + } + + default.foo.bar := 1 { + input.x == 1 + } else := 2 { + input.x == 2 + } else := 3 + + foo.bar.default := 4 { + input.x == 1 + } else := 5 { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/default keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + import future.keywords.contains + + p { + default.foo.bar == {"a", "c"} + foo.bar.default == {"a", "c"} + } + + default.foo.bar contains "a" + + default.foo.bar contains "b" { + false + } + + default.foo.bar contains "c" { + true + } + + foo.bar.default contains "a" + + foo.bar.default contains "b" { + false + } + + foo.bar.default contains "c" { + true + } + want_result: + - x: true + - note: keywordrefs/default keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p { + default.foo == "a" + default.bar.one == "a" + default.bar.three == "c" + foo.default == "a" + bar.baz.default == "a" + } + + default.foo := "a" + + default.foo := "b" { + false + } + + default.foo := "c" { + false + } + + default.bar.one := "a" + + default.bar.two := "b" { + false + } + + default.bar.three := "c" { + true + } + + foo.default := "a" + + foo.default := "b" { + false + } + + foo.default := "c" { + false + } + + bar.baz.default := "a" + + bar.baz.default := "b" { + false + } + + bar.baz.default := "c" { + false + } + want_result: + - x: true + - note: keywordrefs/default keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p { + default.foo(1) == 1 + default.foo(11) == 42 + foo.default(1) == 1 + foo.default(11) == 42 + bar.default.baz(1) == 1 + bar.default.baz(11) == 42 + } + + default default.foo(_) := 42 + + default.foo(x) := x { + x < 10 + } + + default foo.default(_) := 42 + + foo.default(x) := x { + x < 10 + } + + default bar.default.baz(_) := 42 + + bar.default.baz(x) := x { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-else.yaml b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-else.yaml new file mode 100644 index 000000000000..dbd2b133b091 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-else.yaml @@ -0,0 +1,200 @@ +--- +cases: + - note: keywordrefs/else keyword in package + query: data.foo.p = x + modules: + - | + package foo.else.bar + + baz := 42 + - | + package foo + import data.foo.else.bar + + p { + bar.baz == 42 + data.foo.else.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/else keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.else + + bar := 42 + - | + package foo + import data.foo.else as my_if + + p { + my_if.bar == 42 + data.foo.else.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/else keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p { + else.foo == 1 + foo.else == 2 + } + + else.foo := 1 + + foo.else := 2 + want_result: + - x: true + - note: keywordrefs/else keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p { + else.foo.bar == 3 + foo.bar.else == 6 + } + + else.foo.bar := 1 { + input.x == 1 + } else := 2 { + input.x == 2 + } else := 3 + + foo.bar.else := 4 { + input.x == 1 + } else := 5 { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/else keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + import future.keywords.contains + + p { + else.foo.bar == {"a", "c"} + foo.bar.else == {"a", "c"} + } + + else.foo.bar contains "a" + + else.foo.bar contains "b" { + false + } + + else.foo.bar contains "c" { + true + } + + foo.bar.else contains "a" + + foo.bar.else contains "b" { + false + } + + foo.bar.else contains "c" { + true + } + want_result: + - x: true + - note: keywordrefs/else keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p { + else.foo == "a" + else.bar.one == "a" + else.bar.three == "c" + foo.else == "a" + bar.baz.else == "a" + } + + else.foo := "a" + + else.foo := "b" { + false + } + + else.foo := "c" { + false + } + + else.bar.one := "a" + + else.bar.two := "b" { + false + } + + else.bar.three := "c" { + true + } + + foo.else := "a" + + foo.else := "b" { + false + } + + foo.else := "c" { + false + } + + bar.baz.else := "a" + + bar.baz.else := "b" { + false + } + + bar.baz.else := "c" { + false + } + want_result: + - x: true + - note: keywordrefs/else keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p { + else.foo(1) == 1 + else.foo(11) == 42 + foo.else(1) == 1 + foo.else(11) == 42 + bar.else.baz(1) == 1 + bar.else.baz(11) == 42 + } + + default else.foo(_) := 42 + + else.foo(x) := x { + x < 10 + } + + default foo.else(_) := 42 + + foo.else(x) := x { + x < 10 + } + + default bar.else.baz(_) := 42 + + bar.else.baz(x) := x { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-false.yaml b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-false.yaml new file mode 100644 index 000000000000..01646dc01b0f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-false.yaml @@ -0,0 +1,200 @@ +--- +cases: + - note: keywordrefs/false keyword in package + query: data.foo.p = x + modules: + - | + package foo.false.bar + + baz := 42 + - | + package foo + import data.foo.false.bar + + p { + bar.baz == 42 + data.foo.false.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/false keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.false + + bar := 42 + - | + package foo + import data.foo.false as my_if + + p { + my_if.bar == 42 + data.foo.false.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/false keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p { + false.foo == 1 + foo.false == 2 + } + + false.foo := 1 + + foo.false := 2 + want_result: + - x: true + - note: keywordrefs/false keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p { + false.foo.bar == 3 + foo.bar.false == 6 + } + + false.foo.bar := 1 { + input.x == 1 + } else := 2 { + input.x == 2 + } else := 3 + + foo.bar.false := 4 { + input.x == 1 + } else := 5 { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/false keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + import future.keywords.contains + + p { + false.foo.bar == {"a", "c"} + foo.bar.false == {"a", "c"} + } + + false.foo.bar contains "a" + + false.foo.bar contains "b" { + false + } + + false.foo.bar contains "c" { + true + } + + foo.bar.false contains "a" + + foo.bar.false contains "b" { + false + } + + foo.bar.false contains "c" { + true + } + want_result: + - x: true + - note: keywordrefs/false keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p { + false.foo == "a" + false.bar.one == "a" + false.bar.three == "c" + foo.false == "a" + bar.baz.false == "a" + } + + false.foo := "a" + + false.foo := "b" { + false + } + + false.foo := "c" { + false + } + + false.bar.one := "a" + + false.bar.two := "b" { + false + } + + false.bar.three := "c" { + true + } + + foo.false := "a" + + foo.false := "b" { + false + } + + foo.false := "c" { + false + } + + bar.baz.false := "a" + + bar.baz.false := "b" { + false + } + + bar.baz.false := "c" { + false + } + want_result: + - x: true + - note: keywordrefs/false keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p { + false.foo(1) == 1 + false.foo(11) == 42 + foo.false(1) == 1 + foo.false(11) == 42 + bar.false.baz(1) == 1 + bar.false.baz(11) == 42 + } + + default false.foo(_) := 42 + + false.foo(x) := x { + x < 10 + } + + default foo.false(_) := 42 + + foo.false(x) := x { + x < 10 + } + + default bar.false.baz(_) := 42 + + bar.false.baz(x) := x { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-import.yaml b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-import.yaml new file mode 100644 index 000000000000..c53f91296c9f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-import.yaml @@ -0,0 +1,200 @@ +--- +cases: + - note: keywordrefs/import keyword in package + query: data.foo.p = x + modules: + - | + package foo.import.bar + + baz := 42 + - | + package foo + import data.foo.import.bar + + p { + bar.baz == 42 + data.foo.import.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/import keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.import + + bar := 42 + - | + package foo + import data.foo.import as my_if + + p { + my_if.bar == 42 + data.foo.import.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/import keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p { + import.foo == 1 + foo.import == 2 + } + + import.foo := 1 + + foo.import := 2 + want_result: + - x: true + - note: keywordrefs/import keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p { + import.foo.bar == 3 + foo.bar.import == 6 + } + + import.foo.bar := 1 { + input.x == 1 + } else := 2 { + input.x == 2 + } else := 3 + + foo.bar.import := 4 { + input.x == 1 + } else := 5 { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/import keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + import future.keywords.contains + + p { + import.foo.bar == {"a", "c"} + foo.bar.import == {"a", "c"} + } + + import.foo.bar contains "a" + + import.foo.bar contains "b" { + false + } + + import.foo.bar contains "c" { + true + } + + foo.bar.import contains "a" + + foo.bar.import contains "b" { + false + } + + foo.bar.import contains "c" { + true + } + want_result: + - x: true + - note: keywordrefs/import keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p { + import.foo == "a" + import.bar.one == "a" + import.bar.three == "c" + foo.import == "a" + bar.baz.import == "a" + } + + import.foo := "a" + + import.foo := "b" { + false + } + + import.foo := "c" { + false + } + + import.bar.one := "a" + + import.bar.two := "b" { + false + } + + import.bar.three := "c" { + true + } + + foo.import := "a" + + foo.import := "b" { + false + } + + foo.import := "c" { + false + } + + bar.baz.import := "a" + + bar.baz.import := "b" { + false + } + + bar.baz.import := "c" { + false + } + want_result: + - x: true + - note: keywordrefs/import keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p { + import.foo(1) == 1 + import.foo(11) == 42 + foo.import(1) == 1 + foo.import(11) == 42 + bar.import.baz(1) == 1 + bar.import.baz(11) == 42 + } + + default import.foo(_) := 42 + + import.foo(x) := x { + x < 10 + } + + default foo.import(_) := 42 + + foo.import(x) := x { + x < 10 + } + + default bar.import.baz(_) := 42 + + bar.import.baz(x) := x { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-not.yaml b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-not.yaml new file mode 100644 index 000000000000..83bbd5132e65 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-not.yaml @@ -0,0 +1,200 @@ +--- +cases: + - note: keywordrefs/not keyword in package + query: data.foo.p = x + modules: + - | + package foo.not.bar + + baz := 42 + - | + package foo + import data.foo.not.bar + + p { + bar.baz == 42 + data.foo.not.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/not keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.not + + bar := 42 + - | + package foo + import data.foo.not as my_if + + p { + my_if.bar == 42 + data.foo.not.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/not keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p { + not.foo == 1 + foo.not == 2 + } + + not.foo := 1 + + foo.not := 2 + want_result: + - x: true + - note: keywordrefs/not keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p { + not.foo.bar == 3 + foo.bar.not == 6 + } + + not.foo.bar := 1 { + input.x == 1 + } else := 2 { + input.x == 2 + } else := 3 + + foo.bar.not := 4 { + input.x == 1 + } else := 5 { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/not keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + import future.keywords.contains + + p { + not.foo.bar == {"a", "c"} + foo.bar.not == {"a", "c"} + } + + not.foo.bar contains "a" + + not.foo.bar contains "b" { + false + } + + not.foo.bar contains "c" { + true + } + + foo.bar.not contains "a" + + foo.bar.not contains "b" { + false + } + + foo.bar.not contains "c" { + true + } + want_result: + - x: true + - note: keywordrefs/not keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p { + not.foo == "a" + not.bar.one == "a" + not.bar.three == "c" + foo.not == "a" + bar.baz.not == "a" + } + + not.foo := "a" + + not.foo := "b" { + false + } + + not.foo := "c" { + false + } + + not.bar.one := "a" + + not.bar.two := "b" { + false + } + + not.bar.three := "c" { + true + } + + foo.not := "a" + + foo.not := "b" { + false + } + + foo.not := "c" { + false + } + + bar.baz.not := "a" + + bar.baz.not := "b" { + false + } + + bar.baz.not := "c" { + false + } + want_result: + - x: true + - note: keywordrefs/not keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p { + not.foo(1) == 1 + not.foo(11) == 42 + foo.not(1) == 1 + foo.not(11) == 42 + bar.not.baz(1) == 1 + bar.not.baz(11) == 42 + } + + default not.foo(_) := 42 + + not.foo(x) := x { + x < 10 + } + + default foo.not(_) := 42 + + foo.not(x) := x { + x < 10 + } + + default bar.not.baz(_) := 42 + + bar.not.baz(x) := x { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-null.yaml b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-null.yaml new file mode 100644 index 000000000000..e4fa1f7a19d4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-null.yaml @@ -0,0 +1,200 @@ +--- +cases: + - note: keywordrefs/null keyword in package + query: data.foo.p = x + modules: + - | + package foo.null.bar + + baz := 42 + - | + package foo + import data.foo.null.bar + + p { + bar.baz == 42 + data.foo.null.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/null keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.null + + bar := 42 + - | + package foo + import data.foo.null as my_if + + p { + my_if.bar == 42 + data.foo.null.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/null keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p { + null.foo == 1 + foo.null == 2 + } + + null.foo := 1 + + foo.null := 2 + want_result: + - x: true + - note: keywordrefs/null keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p { + null.foo.bar == 3 + foo.bar.null == 6 + } + + null.foo.bar := 1 { + input.x == 1 + } else := 2 { + input.x == 2 + } else := 3 + + foo.bar.null := 4 { + input.x == 1 + } else := 5 { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/null keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + import future.keywords.contains + + p { + null.foo.bar == {"a", "c"} + foo.bar.null == {"a", "c"} + } + + null.foo.bar contains "a" + + null.foo.bar contains "b" { + false + } + + null.foo.bar contains "c" { + true + } + + foo.bar.null contains "a" + + foo.bar.null contains "b" { + false + } + + foo.bar.null contains "c" { + true + } + want_result: + - x: true + - note: keywordrefs/null keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p { + null.foo == "a" + null.bar.one == "a" + null.bar.three == "c" + foo.null == "a" + bar.baz.null == "a" + } + + null.foo := "a" + + null.foo := "b" { + false + } + + null.foo := "c" { + false + } + + null.bar.one := "a" + + null.bar.two := "b" { + false + } + + null.bar.three := "c" { + true + } + + foo.null := "a" + + foo.null := "b" { + false + } + + foo.null := "c" { + false + } + + bar.baz.null := "a" + + bar.baz.null := "b" { + false + } + + bar.baz.null := "c" { + false + } + want_result: + - x: true + - note: keywordrefs/null keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p { + null.foo(1) == 1 + null.foo(11) == 42 + foo.null(1) == 1 + foo.null(11) == 42 + bar.null.baz(1) == 1 + bar.null.baz(11) == 42 + } + + default null.foo(_) := 42 + + null.foo(x) := x { + x < 10 + } + + default foo.null(_) := 42 + + foo.null(x) := x { + x < 10 + } + + default bar.null.baz(_) := 42 + + bar.null.baz(x) := x { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-package.yaml b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-package.yaml new file mode 100644 index 000000000000..f68903aa6a85 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-package.yaml @@ -0,0 +1,200 @@ +--- +cases: + - note: keywordrefs/package keyword in package + query: data.foo.p = x + modules: + - | + package foo.package.bar + + baz := 42 + - | + package foo + import data.foo.package.bar + + p { + bar.baz == 42 + data.foo.package.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/package keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.package + + bar := 42 + - | + package foo + import data.foo.package as my_if + + p { + my_if.bar == 42 + data.foo.package.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/package keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p { + package.foo == 1 + foo.package == 2 + } + + package.foo := 1 + + foo.package := 2 + want_result: + - x: true + - note: keywordrefs/package keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p { + package.foo.bar == 3 + foo.bar.package == 6 + } + + package.foo.bar := 1 { + input.x == 1 + } else := 2 { + input.x == 2 + } else := 3 + + foo.bar.package := 4 { + input.x == 1 + } else := 5 { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/package keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + import future.keywords.contains + + p { + package.foo.bar == {"a", "c"} + foo.bar.package == {"a", "c"} + } + + package.foo.bar contains "a" + + package.foo.bar contains "b" { + false + } + + package.foo.bar contains "c" { + true + } + + foo.bar.package contains "a" + + foo.bar.package contains "b" { + false + } + + foo.bar.package contains "c" { + true + } + want_result: + - x: true + - note: keywordrefs/package keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p { + package.foo == "a" + package.bar.one == "a" + package.bar.three == "c" + foo.package == "a" + bar.baz.package == "a" + } + + package.foo := "a" + + package.foo := "b" { + false + } + + package.foo := "c" { + false + } + + package.bar.one := "a" + + package.bar.two := "b" { + false + } + + package.bar.three := "c" { + true + } + + foo.package := "a" + + foo.package := "b" { + false + } + + foo.package := "c" { + false + } + + bar.baz.package := "a" + + bar.baz.package := "b" { + false + } + + bar.baz.package := "c" { + false + } + want_result: + - x: true + - note: keywordrefs/package keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p { + package.foo(1) == 1 + package.foo(11) == 42 + foo.package(1) == 1 + foo.package(11) == 42 + bar.package.baz(1) == 1 + bar.package.baz(11) == 42 + } + + default package.foo(_) := 42 + + package.foo(x) := x { + x < 10 + } + + default foo.package(_) := 42 + + foo.package(x) := x { + x < 10 + } + + default bar.package.baz(_) := 42 + + bar.package.baz(x) := x { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-some.yaml b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-some.yaml new file mode 100644 index 000000000000..afe9a61db14a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-some.yaml @@ -0,0 +1,200 @@ +--- +cases: + - note: keywordrefs/some keyword in package + query: data.foo.p = x + modules: + - | + package foo.some.bar + + baz := 42 + - | + package foo + import data.foo.some.bar + + p { + bar.baz == 42 + data.foo.some.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/some keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.some + + bar := 42 + - | + package foo + import data.foo.some as my_if + + p { + my_if.bar == 42 + data.foo.some.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/some keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p { + some.foo == 1 + foo.some == 2 + } + + some.foo := 1 + + foo.some := 2 + want_result: + - x: true + - note: keywordrefs/some keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p { + some.foo.bar == 3 + foo.bar.some == 6 + } + + some.foo.bar := 1 { + input.x == 1 + } else := 2 { + input.x == 2 + } else := 3 + + foo.bar.some := 4 { + input.x == 1 + } else := 5 { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/some keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + import future.keywords.contains + + p { + some.foo.bar == {"a", "c"} + foo.bar.some == {"a", "c"} + } + + some.foo.bar contains "a" + + some.foo.bar contains "b" { + false + } + + some.foo.bar contains "c" { + true + } + + foo.bar.some contains "a" + + foo.bar.some contains "b" { + false + } + + foo.bar.some contains "c" { + true + } + want_result: + - x: true + - note: keywordrefs/some keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p { + some.foo == "a" + some.bar.one == "a" + some.bar.three == "c" + foo.some == "a" + bar.baz.some == "a" + } + + some.foo := "a" + + some.foo := "b" { + false + } + + some.foo := "c" { + false + } + + some.bar.one := "a" + + some.bar.two := "b" { + false + } + + some.bar.three := "c" { + true + } + + foo.some := "a" + + foo.some := "b" { + false + } + + foo.some := "c" { + false + } + + bar.baz.some := "a" + + bar.baz.some := "b" { + false + } + + bar.baz.some := "c" { + false + } + want_result: + - x: true + - note: keywordrefs/some keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p { + some.foo(1) == 1 + some.foo(11) == 42 + foo.some(1) == 1 + foo.some(11) == 42 + bar.some.baz(1) == 1 + bar.some.baz(11) == 42 + } + + default some.foo(_) := 42 + + some.foo(x) := x { + x < 10 + } + + default foo.some(_) := 42 + + foo.some(x) := x { + x < 10 + } + + default bar.some.baz(_) := 42 + + bar.some.baz(x) := x { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-true.yaml b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-true.yaml new file mode 100644 index 000000000000..0f463a13c26b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-true.yaml @@ -0,0 +1,200 @@ +--- +cases: + - note: keywordrefs/true keyword in package + query: data.foo.p = x + modules: + - | + package foo.true.bar + + baz := 42 + - | + package foo + import data.foo.true.bar + + p { + bar.baz == 42 + data.foo.true.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/true keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.true + + bar := 42 + - | + package foo + import data.foo.true as my_if + + p { + my_if.bar == 42 + data.foo.true.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/true keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p { + true.foo == 1 + foo.true == 2 + } + + true.foo := 1 + + foo.true := 2 + want_result: + - x: true + - note: keywordrefs/true keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p { + true.foo.bar == 3 + foo.bar.true == 6 + } + + true.foo.bar := 1 { + input.x == 1 + } else := 2 { + input.x == 2 + } else := 3 + + foo.bar.true := 4 { + input.x == 1 + } else := 5 { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/true keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + import future.keywords.contains + + p { + true.foo.bar == {"a", "c"} + foo.bar.true == {"a", "c"} + } + + true.foo.bar contains "a" + + true.foo.bar contains "b" { + false + } + + true.foo.bar contains "c" { + true + } + + foo.bar.true contains "a" + + foo.bar.true contains "b" { + false + } + + foo.bar.true contains "c" { + true + } + want_result: + - x: true + - note: keywordrefs/true keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p { + true.foo == "a" + true.bar.one == "a" + true.bar.three == "c" + foo.true == "a" + bar.baz.true == "a" + } + + true.foo := "a" + + true.foo := "b" { + false + } + + true.foo := "c" { + false + } + + true.bar.one := "a" + + true.bar.two := "b" { + false + } + + true.bar.three := "c" { + true + } + + foo.true := "a" + + foo.true := "b" { + false + } + + foo.true := "c" { + false + } + + bar.baz.true := "a" + + bar.baz.true := "b" { + false + } + + bar.baz.true := "c" { + false + } + want_result: + - x: true + - note: keywordrefs/true keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p { + true.foo(1) == 1 + true.foo(11) == 42 + foo.true(1) == 1 + foo.true(11) == 42 + bar.true.baz(1) == 1 + bar.true.baz(11) == 42 + } + + default true.foo(_) := 42 + + true.foo(x) := x { + x < 10 + } + + default foo.true(_) := 42 + + foo.true(x) := x { + x < 10 + } + + default bar.true.baz(_) := 42 + + bar.true.baz(x) := x { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-with.yaml b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-with.yaml new file mode 100644 index 000000000000..f4b2c9dfb360 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/keywordrefs/test-keyword-with.yaml @@ -0,0 +1,200 @@ +--- +cases: + - note: keywordrefs/with keyword in package + query: data.foo.p = x + modules: + - | + package foo.with.bar + + baz := 42 + - | + package foo + import data.foo.with.bar + + p { + bar.baz == 42 + data.foo.with.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/with keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.with + + bar := 42 + - | + package foo + import data.foo.with as my_if + + p { + my_if.bar == 42 + data.foo.with.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/with keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p { + with.foo == 1 + foo.with == 2 + } + + with.foo := 1 + + foo.with := 2 + want_result: + - x: true + - note: keywordrefs/with keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p { + with.foo.bar == 3 + foo.bar.with == 6 + } + + with.foo.bar := 1 { + input.x == 1 + } else := 2 { + input.x == 2 + } else := 3 + + foo.bar.with := 4 { + input.x == 1 + } else := 5 { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/with keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + import future.keywords.contains + + p { + with.foo.bar == {"a", "c"} + foo.bar.with == {"a", "c"} + } + + with.foo.bar contains "a" + + with.foo.bar contains "b" { + false + } + + with.foo.bar contains "c" { + true + } + + foo.bar.with contains "a" + + foo.bar.with contains "b" { + false + } + + foo.bar.with contains "c" { + true + } + want_result: + - x: true + - note: keywordrefs/with keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p { + with.foo == "a" + with.bar.one == "a" + with.bar.three == "c" + foo.with == "a" + bar.baz.with == "a" + } + + with.foo := "a" + + with.foo := "b" { + false + } + + with.foo := "c" { + false + } + + with.bar.one := "a" + + with.bar.two := "b" { + false + } + + with.bar.three := "c" { + true + } + + foo.with := "a" + + foo.with := "b" { + false + } + + foo.with := "c" { + false + } + + bar.baz.with := "a" + + bar.baz.with := "b" { + false + } + + bar.baz.with := "c" { + false + } + want_result: + - x: true + - note: keywordrefs/with keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p { + with.foo(1) == 1 + with.foo(11) == 42 + foo.with(1) == 1 + foo.with(11) == 42 + bar.with.baz(1) == 1 + bar.with.baz(11) == 42 + } + + default with.foo(_) := 42 + + with.foo(x) := x { + x < 10 + } + + default foo.with(_) := 42 + + foo.with(x) := x { + x < 10 + } + + default bar.with.baz(_) := 42 + + bar.with.baz(x) := x { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0777.yaml b/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0777.yaml new file mode 100644 index 000000000000..b200316e3bf6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0777.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: + modules: + - | + package generated + + p { + not true = false + } + note: "negation/neg: constants" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0778.yaml b/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0778.yaml new file mode 100644 index 000000000000..83c744131e96 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0778.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: + modules: + - | + package generated + + p { + not true = true + } + note: "negation/neg: constants" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0779.yaml b/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0779.yaml new file mode 100644 index 000000000000..7de7db024c9d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0779.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + not data.generated.q.v0 + } + + q[x] { + data.b[x] = v + } + note: "negation/neg: set contains" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0780.yaml b/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0780.yaml new file mode 100644 index 000000000000..cd60e3e5dc77 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-0780.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p { + not data.generated.q.v2 + } + + q[x] { + data.b[x] = v + } + note: "negation/neg: set contains undefined" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-data-ref-with-var.yaml b/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-data-ref-with-var.yaml new file mode 100644 index 000000000000..e70c3c5cd777 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/negation/test-negation-data-ref-with-var.yaml @@ -0,0 +1,73 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package test + + p = y { + y := "v0" + not data.test.q[y] + } + + q[x] { + data.b[x] = v + } + note: "negation/pos: ref with variable" + query: data.test.p = x + want_result: + - x: v0 + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package test + + p = y { + y := "v1" + not data.test.q[y] + } + + q[x] { + data.b[x] = v + } + note: "negation/neg: ref with variable" + query: data.test.p = x + want_result: [] + - data: + bar: + q: 8 + modules: + - | + package foo + p { + k := "p" + not data.bar[k] + } + - | + package bar + p = 7 + note: "negation/neg: ref with variable (hit) and virtual doc (miss))" + query: data.foo.p = x + want_result: [] + - data: + bar: + q: 8 + modules: + - | + package foo + p { + k := "q" + not data.bar[k] + } + - | + package bar + p = 7 + note: "negation/neg: ref with variable (miss) and virtual doc (hit)" + query: data.foo.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0709.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0709.yaml new file mode 100644 index 000000000000..3ca45780322f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0709.yaml @@ -0,0 +1,27 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + h: + - - 1 + - 2 + - 3 + - - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + __local0__ = data.h[0][0] + data.a[__local0__] = 2 + } + note: nestedreferences/ground ref + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0710.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0710.yaml new file mode 100644 index 000000000000..d33317045fa1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0710.yaml @@ -0,0 +1,31 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + h: + - - 1 + - 2 + - 3 + - - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.h[i][j] + x = data.a[__local0__] + } + note: nestedreferences/non-ground ref + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0711.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0711.yaml new file mode 100644 index 000000000000..bc9828ad2d84 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0711.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.a[i] + __local1__ = data.a[__local0__] + x = data.a[__local1__] + } + note: nestedreferences/two deep + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0712.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0712.yaml new file mode 100644 index 000000000000..226ca4a65b13 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0712.yaml @@ -0,0 +1,31 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + h: + - - 1 + - 2 + - 3 + - - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.a[j] + __local1__ = data.h[i][__local0__] + x = data.a[__local1__] + } + note: nestedreferences/two deep + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0713.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0713.yaml new file mode 100644 index 000000000000..4a965f159fbf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0713.yaml @@ -0,0 +1,30 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + h: + - - 1 + - 2 + - 3 + - - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.a[i] + __local1__ = data.h[i][__local0__] + x = data.a[__local1__] + } + note: nestedreferences/two deep repeated var + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0714.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0714.yaml new file mode 100644 index 000000000000..9d90c08dc0d9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0714.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + three: 3 + modules: + - | + package generated + + p { + __local0__ = data.three + 4 = data.a[__local0__] + } + note: nestedreferences/no suffix + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0715.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0715.yaml new file mode 100644 index 000000000000..ac5bad25c824 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0715.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[y] { + x = [1, 2, 3] + __local0__ = x[_] + y = data.a[__local0__] + } + note: nestedreferences/var ref + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0716.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0716.yaml new file mode 100644 index 000000000000..b49bf4801307 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0716.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + __local0__ = data.three.deadbeef + data.a[__local0__] = x + } + note: nestedreferences/undefined + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0717.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0717.yaml new file mode 100644 index 000000000000..774c11bbc109 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0717.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.generated.q[_] + x = data.a[__local0__] + } + + q = [2, 3] + note: "nestedreferences/vdoc ref: complete" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0718.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0718.yaml new file mode 100644 index 000000000000..532d0e5b00d8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0718.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.generated.q[1] + x = data.a[__local0__] + } + + q = [2, 3] + note: "nestedreferences/vdoc ref: complete: ground" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0719.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0719.yaml new file mode 100644 index 000000000000..f1f5791d6076 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0719.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + __local0__ = data.generated.q + 2 = data.a[__local0__] + } + + q = 1 + note: "nestedreferences/vdoc ref: complete: no suffix" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0720.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0720.yaml new file mode 100644 index 000000000000..dda27a31e368 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0720.yaml @@ -0,0 +1,28 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.generated.q[_] + x = data.a[__local0__] + } + + q[k] = v { + o = {"a": 2, "b": 3, "c": 100} + o[k] = v + } + note: "nestedreferences/vdoc ref: partial object" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0721.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0721.yaml new file mode 100644 index 000000000000..295926fb037e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0721.yaml @@ -0,0 +1,27 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.generated.q.b + x = data.a[__local0__] + } + + q[k] = v { + o = {"a": 2, "b": 3, "c": 100} + o[k] = v + } + note: "nestedreferences/vdoc ref: partial object: ground" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0722.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0722.yaml new file mode 100644 index 000000000000..760062154cbd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0722.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[x] { + __local0__ = data.b[_] + __local1__ = data.generated.q[__local0__] + x = data.a[__local1__] + } + + q = {"hello": 1, "goodbye": 3, "deadbeef": 1000} + note: "nestedreferences/vdoc ref: complete: nested bdoc ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0723.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0723.yaml new file mode 100644 index 000000000000..c097fd7a9506 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0723.yaml @@ -0,0 +1,32 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[x] { + __local0__ = data.b[_] + __local1__ = data.generated.q[__local0__] + x = data.a[__local1__] + } + + q[k] = v { + o = {"deadbeef": 1000, "goodbye": 3, "hello": 1} + o[k] = v + } + note: "nestedreferences/vdoc ref: partial object: nested bdoc ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0724.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0724.yaml new file mode 100644 index 000000000000..330845630e38 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0724.yaml @@ -0,0 +1,36 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + d: + e: + - bar + - baz + strings: + bar: 2 + baz: 3 + foo: 1 + modules: + - | + package generated + + p[x] { + __local0__ = data.d.e[_] + __local1__ = data.generated.q[__local0__] + x = data.a[__local1__] + } + + q[k] = v { + data.strings[k] = v + } + note: "nestedreferences/vdoc ref: partial object: nested bdoc ref-2" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0725.yaml b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0725.yaml new file mode 100644 index 000000000000..f9e9512fbf1d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/nestedreferences/test-nestedreferences-0725.yaml @@ -0,0 +1,31 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.a[_] + __local1__ = data.a[_] + __local2__ = data.generated.r[__local1__] + x = data.generated.q[__local0__].v[__local2__] + } + + q = [{"v": {}}, {"v": [0, 0, 1, 2]}, {"v": [0, 0, 3, 4]}, {"v": [0, 0]}, {}] + + r = [1, 2, 3, 4] + note: "nestedreferences/vdoc ref: multiple" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0092.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0092.yaml new file mode 100644 index 000000000000..6ee6b21855e0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0092.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_contains("10.0.0.0/8", "10.1.0.0/24", x) + } + note: netcidrcontains/cidr contains subnet + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0093.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0093.yaml new file mode 100644 index 000000000000..0cac1c7bd914 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0093.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_contains("172.17.0.0/24", "172.17.0.0/16", x) + } + note: netcidrcontains/cidr does not contain subnet partial + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0094.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0094.yaml new file mode 100644 index 000000000000..27095c4cd700 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0094.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_contains("10.0.0.0/8", "192.168.1.0/24", x) + } + note: netcidrcontains/cidr does not contain subnet + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0095.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0095.yaml new file mode 100644 index 000000000000..2c3de79e43f9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0095.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_contains("10.0.0.0/8", "10.1.1.1/32", x) + } + note: netcidrcontains/cidr contains single ip subnet + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0096.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0096.yaml new file mode 100644 index 000000000000..c1b98c0ec952 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0096.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_contains("2001:4860:4860::8888/32", "2001:4860:4860:1234::8888/40", x) + } + note: netcidrcontains/cidr contains subnet ipv6 + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0097.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0097.yaml new file mode 100644 index 000000000000..f844a441f734 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0097.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_contains("2001:4860:4860::8888/32", "2001:4860:4860:1234:5678:1234:5678:8888/128", x) + } + note: netcidrcontains/cidr contains single ip subnet ipv6 + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0098.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0098.yaml new file mode 100644 index 000000000000..c131bcf76a88 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0098.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_contains("2001:4860::/96", "2001:4860::/32", x) + } + note: netcidrcontains/cidr does not contain subnet partial ipv6 + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0099.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0099.yaml new file mode 100644 index 000000000000..fc2944582835 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0099.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_contains("2001:4860::/32", "fd1e:5bfe:8af3:9ddc::/64", x) + } + note: netcidrcontains/cidr does not contain subnet ipv6 + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0100.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0100.yaml new file mode 100644 index 000000000000..2204199ebbb9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0100.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + net.cidr_contains("not-a-cidr", "192.168.1.67", x) + } + note: netcidrcontains/cidr subnet overlap malformed cidr a + query: data.generated.p = x + want_error: "" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0101.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0101.yaml new file mode 100644 index 000000000000..350863263759 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0101.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + net.cidr_contains("192.168.1.0/28", "not-a-cidr", x) + } + note: netcidrcontains/cidr subnet overlap malformed cidr b + query: data.generated.p = x + want_error: "" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0102.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0102.yaml new file mode 100644 index 000000000000..5462780563d6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0102.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_contains("10.0.0.0/8", "10.1.2.3", x) + } + note: netcidrcontains/cidr contains ip + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0103.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0103.yaml new file mode 100644 index 000000000000..a47c1f7b523d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontains/test-netcidrcontains-0103.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_contains("10.0.0.0/8", "192.168.1.1", x) + } + note: netcidrcontains/cidr does not contain ip + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0104.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0104.yaml new file mode 100644 index 000000000000..3bf479a18683 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0104.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + net.cidr_contains_matches("1.1.1.0/24", "1.1.1.1", __local1__) + __local0__ = __local1__ + } + note: netcidrcontainsmatches/strings + query: data.generated.p = x + want_result: + - x: + - - 1.1.1.0/24 + - 1.1.1.1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0105.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0105.yaml new file mode 100644 index 000000000000..a61b154ce8dc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0105.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + net.cidr_contains_matches(["1.1.2.0/24", "1.1.1.0/24"], ["1.1.1.1", "1.1.2.1"], __local1__) + __local0__ = __local1__ + } + note: netcidrcontainsmatches/arrays + query: data.generated.p = x + want_result: + - x: + - - 0 + - 1 + - - 1 + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0106.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0106.yaml new file mode 100644 index 000000000000..4ded4a2e84eb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0106.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + net.cidr_contains_matches([["1.1.2.0/24", 1], "1.1.1.0/24"], ["1.1.1.1", "1.1.2.1"], __local1__) + __local0__ = __local1__ + } + note: netcidrcontainsmatches/arrays of tuples + query: data.generated.p = x + want_result: + - x: + - - 0 + - 1 + - - 1 + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0107.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0107.yaml new file mode 100644 index 000000000000..37be52de0d2f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0107.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + modules: + - | + package generated + + p = __local0__ { + __local2__ = data.a[0] + net.cidr_contains_matches(["1.1.2.0/24", "1.1.1.0/24"], ["1.1.1.1", __local2__], __local1__) + __local0__ = __local1__ + } + note: netcidrcontainsmatches/bad array + query: data.generated.p = x + strict_error: true + want_error: + "net.cidr_contains_matches: operand 2: element must be string or non-empty + array" + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0108.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0108.yaml new file mode 100644 index 000000000000..131a78e2434a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0108.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + net.cidr_contains_matches({"1.1.1.0/24", "1.1.2.0/24"}, {"1.1.1.1", "1.1.2.1"}, __local1__) + __local0__ = __local1__ + } + note: netcidrcontainsmatches/sets of strings + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 1.1.1.0/24 + - 1.1.1.1 + - - 1.1.2.0/24 + - 1.1.2.1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0109.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0109.yaml new file mode 100644 index 000000000000..43dc73e07cd9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0109.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + net.cidr_contains_matches({["1.1.1.0/24", "bar"], ["1.1.2.0/24", "foo"]}, {["1.1.1.1", "baz"], ["1.1.2.1", "qux"]}, __local1__) + __local0__ = __local1__ + } + note: netcidrcontainsmatches/sets of tuples + query: data.generated.p = x + want_result: + - x: + - - - 1.1.1.0/24 + - bar + - - 1.1.1.1 + - baz + - - - 1.1.2.0/24 + - foo + - - 1.1.2.1 + - qux diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0110.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0110.yaml new file mode 100644 index 000000000000..ec9552220078 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0110.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + modules: + - | + package generated + + p = __local0__ { + __local2__ = data.a[0] + net.cidr_contains_matches({["1.1.1.0/24", "bar"], ["1.1.2.0/24", "foo"]}, {__local2__, ["1.1.2.1", "qux"]}, __local1__) + __local0__ = __local1__ + } + note: netcidrcontainsmatches/bad set + query: data.generated.p = x + strict_error: true + want_error: + "net.cidr_contains_matches: operand 2: element must be string or non-empty + array" + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0111.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0111.yaml new file mode 100644 index 000000000000..95b6c349f707 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0111.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = __local0__ { + net.cidr_contains_matches({["1.1.1.0/24", "bar"], ["1.1.2.0/24", "foo"]}, {[], ["1.1.2.1", "qux"]}, __local1__) + __local0__ = __local1__ + } + note: netcidrcontainsmatches/bad set tuple element + query: data.generated.p = x + want_error: + "net.cidr_contains_matches: operand 2: element must be string or non-empty + array" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0112.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0112.yaml new file mode 100644 index 000000000000..f4c473200d98 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrcontainsmatches/test-netcidrcontainsmatches-0112.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + net.cidr_contains_matches({"k1": "1.1.1.1/24", "k2": ["1.1.1.2/24", 1]}, "1.1.1.128", __local1__) + __local0__ = __local1__ + } + note: netcidrcontainsmatches/objects + query: data.generated.p = x + want_result: + - x: + - - k1 + - 1.1.1.128 + - - k2 + - 1.1.1.128 diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0113.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0113.yaml new file mode 100644 index 000000000000..0597cc115554 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0113.yaml @@ -0,0 +1,18 @@ +--- +cases: + - modules: + - | + package generated + + p = x { + net.cidr_expand("192.168.1.1/30", x) + } + note: netcidrexpand/cidr includes host and broadcast + query: data.generated.p = x + want_result: + - x: + - 192.168.1.0 + - 192.168.1.1 + - 192.168.1.2 + - 192.168.1.3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0114.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0114.yaml new file mode 100644 index 000000000000..4be56570433c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0114.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + net.cidr_expand("172.16.100.255/30", x) + } + note: netcidrexpand/cidr last octet all 1s + query: data.generated.p = x + want_result: + - x: + - 172.16.100.252 + - 172.16.100.253 + - 172.16.100.254 + - 172.16.100.255 diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0115.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0115.yaml new file mode 100644 index 000000000000..4d5f88f94b0a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0115.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + net.cidr_expand("192.168.1.1/32", x) + } + note: netcidrexpand/cidr all bits + query: data.generated.p = x + want_result: + - x: + - 192.168.1.1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0116.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0116.yaml new file mode 100644 index 000000000000..ed1bd98eef93 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrexpand/test-netcidrexpand-0116.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + net.cidr_expand("192.168.1.1/33", x) + } + note: netcidrexpand/cidr invalid mask + query: data.generated.p = x + want_error: "net.cidr_expand: invalid CIDR address: 192.168.1.1/33" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0086.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0086.yaml new file mode 100644 index 000000000000..7c6dfe027a51 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0086.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_intersects("192.168.1.0/25", "192.168.1.64/25", x) + } + note: netcidrintersects/cidr subnet overlaps + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0087.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0087.yaml new file mode 100644 index 000000000000..096615d45ad8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0087.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_intersects("192.168.1.0/24", "192.168.2.0/24", x) + } + note: netcidrintersects/cidr subnet does not overlap + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0088.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0088.yaml new file mode 100644 index 000000000000..552ff2b8a3da --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0088.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_intersects("fd1e:5bfe:8af3:9ddc::/64", "fd1e:5bfe:8af3:9ddc:1111::/72", x) + } + note: netcidrintersects/cidr ipv6 subnet overlaps + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0089.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0089.yaml new file mode 100644 index 000000000000..11b1ccee44c5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0089.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_intersects("fd1e:5bfe:8af3:9ddc::/64", "2001:4860:4860::8888/32", x) + } + note: netcidrintersects/cidr ipv6 subnet does not overlap + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0090.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0090.yaml new file mode 100644 index 000000000000..7fa1ad7d4020 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0090.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + net.cidr_intersects("not-a-cidr", "192.168.1.0/24", x) + } + note: netcidrintersects/cidr subnet overlap malformed cidr a + query: data.generated.p = x + want_error: "" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0091.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0091.yaml new file mode 100644 index 000000000000..191e776d911f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrintersects/test-netcidrintersects-0091.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + net.cidr_intersects("192.168.1.0/28", "not-a-cidr", x) + } + note: netcidrintersects/cidr subnet overlap malformed cidr b + query: data.generated.p = x + want_error: "" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrisvalid/test_netcidrisvalid-0001.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrisvalid/test_netcidrisvalid-0001.yaml new file mode 100644 index 000000000000..538c51808acb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrisvalid/test_netcidrisvalid-0001.yaml @@ -0,0 +1,67 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + net.cidr_is_valid("192.168.1.0/24", __local0__) + x = __local0__ + } + note: valid ipv4 cidr + query: data.generated.p = x + want_result: + - x: true + - data: {} + modules: + - | + package generated + + p = x { + net.cidr_is_valid("", __local0__) + x = __local0__ + } + note: empty cidr + query: data.generated.p = x + want_result: + - x: false + - data: {} + modules: + - | + package generated + + p = x { + net.cidr_is_valid("there goes a string", __local0__) + x = __local0__ + } + note: random string + query: data.generated.p = x + want_result: + - x: false + - data: {} + modules: + - | + package generated + + p = x { + net.cidr_is_valid("192.168.1.2", __local0__) + x = __local0__ + } + note: valid ipv4 address + query: data.generated.p = x + want_result: + - x: false + - data: {} + modules: + - | + package generated + + p = x { + net.cidr_is_valid("2002::1234:abcd:ffff:c0a8:101/64", __local0__) + x = __local0__ + } + note: valid ipv6 cidr + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrmerge/test-ipv6-with-and-without-prefix.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrmerge/test-ipv6-with-and-without-prefix.yaml new file mode 100644 index 000000000000..a24acbc4eff8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrmerge/test-ipv6-with-and-without-prefix.yaml @@ -0,0 +1,60 @@ +--- +cases: + - note: netcidrmerge/cidr ipv6 with prefix + modules: + - | + package test + + p = x { + net.cidr_merge(["2601:600:8a80:207e:a57d:7567:e2c9:e7b3/128"], x) + } + query: data.test.p = x + want_result: + - x: + - "2601:600:8a80:207e:a57d:7567:e2c9:e7b3/128" + - note: netcidrmerge/cidr ipv6 with prefix, same twice + modules: + - | + package test + + p = x { + net.cidr_merge(["2601:600:8a80:207e:a57d:7567:e2c9:e7b3/128", "2601:600:8a80:207e:a57d:7567:e2c9:e7b3/128"], x) + } + query: data.test.p = x + want_result: + - x: + - "2601:600:8a80:207e:a57d:7567:e2c9:e7b3/128" + - note: netcidrmerge/cidr ipv6 with prefix, two different prefixes + modules: + - | + package test + + p = x { + net.cidr_merge(["2601:600:8a80:207e:a57d:7567:e2c9:e7b3/64", "2601:600:8a80:207e:a57d:7567:e2c9:e7b3/128"], x) + } + query: data.test.p = x + want_result: + - x: + - "2601:600:8a80:207e::/64" + - note: netcidrmerge/cidr ipv6 without prefix + modules: + - | + package test + + p = x { + net.cidr_merge(["2601:600:8a80:207e:a57d:7567:e2c9:e7b3"], x) + } + query: data.test.p = x + strict_error: true + want_error: "eval_builtin_error: net.cidr_merge: IPv6 invalid: needs prefix length" + - note: netcidrmerge/cidr ipv6 without prefix, same twice + modules: + - | + package test + + p = x { + net.cidr_merge(["2601:600:8a80:207e:a57d:7567:e2c9:e7b3", "2601:600:8a80:207e:a57d:7567:e2c9:e7b3"], x) + } + query: data.test.p = x + strict_error: true + want_error: "eval_builtin_error: net.cidr_merge: IPv6 invalid: needs prefix length" diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidrmerge/test-netcidrmerge0117.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidrmerge/test-netcidrmerge0117.yaml new file mode 100644 index 000000000000..6ad7d4cd0452 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidrmerge/test-netcidrmerge0117.yaml @@ -0,0 +1,214 @@ +--- +cases: + - note: netcidrmerge/cidr single subnet + modules: + - | + package test + + p = x { + net.cidr_merge(["192.0.128.0/24"], x) + } + query: data.test.p = x + want_result: + - x: + - 192.0.128.0/24 + - note: netcidrmerge/cidr duplicate + modules: + - | + package test + + p = x { + net.cidr_merge(["192.0.128.0/24", "192.0.128.0/24"], x) + } + query: data.test.p = x + want_result: + - x: + - 192.0.128.0/24 + - note: netcidrmerge/cidr IPv4 zero address + modules: + - | + package test + + p = x { + net.cidr_merge(["192.0.128.0/24", "0.0.0.0/0"], x) + } + query: data.test.p = x + want_result: + - x: + - 0.0.0.0/0 + - note: netcidrmerge/cidr merge subnets case 1 + modules: + - | + package test + + p = x { + net.cidr_merge(["192.0.128.0/24", "192.0.129.0/24"], x) + } + query: data.test.p = x + want_result: + - x: + - 192.0.128.0/23 + - note: netcidrmerge/cidr merge subnets case 2 + modules: + - | + package test + + p = x { + net.cidr_merge(["192.0.2.112/30", "192.0.2.116/31", "192.0.2.118/31"], x) + } + query: data.test.p = x + want_result: + - x: + - 192.0.2.112/29 + - note: netcidrmerge/cidr no overlap case 1 + modules: + - | + package test + + p = x { + net.cidr_merge(["192.0.129.0/24", "192.0.130.0/24"], x) + } + query: data.test.p = x + want_result: + - x: + - 192.0.129.0/24 + - 192.0.130.0/24 + - note: netcidrmerge/cidr no overlap case 2 + modules: + - | + package test + + p = x { + net.cidr_merge(["192.0.2.112/30", "192.0.2.116/32", "192.0.2.118/31"], x) + } + query: data.test.p = x + sort_bindings: true + want_result: + - x: + - 192.0.2.112/30 + - 192.0.2.116/32 + - 192.0.2.118/31 + - note: netcidrmerge/cidr mix case 1 + modules: + - | + package test + + p = x { + net.cidr_merge(["192.0.2.112/31", "192.0.2.116/31", "192.0.2.118/31"], x) + } + query: data.test.p = x + want_result: + - x: + - 192.0.2.112/31 + - 192.0.2.116/30 + - note: netcidrmerge/cidr mix case 2 + modules: + - | + package test + + p = x { + net.cidr_merge(["192.0.1.254/31", "192.0.2.0/28", "192.0.2.16/28", "192.0.2.32/28", "192.0.2.48/28", "192.0.2.64/28", "192.0.2.80/28", "192.0.2.96/28", "192.0.2.112/28", "192.0.2.128/28", "192.0.2.144/28", "192.0.2.160/28", "192.0.2.176/28", "192.0.2.192/28", "192.0.2.208/28", "192.0.2.224/28", "192.0.2.240/28", "192.0.3.0/28"], x) + } + query: data.test.p = x + want_result: + - x: + - 192.0.1.254/31 + - 192.0.2.0/24 + - 192.0.3.0/28 + - note: netcidrmerge/cidr IPv6 zero address case 1 + modules: + - | + package test + + p = x { + net.cidr_merge(["::/0", "fe80::1/128"], x) + } + query: data.test.p = x + want_result: + - x: + - ::/0 + - note: netcidrmerge/cidr IPv6 zero address case 2 + modules: + - | + package test + + p = x { + net.cidr_merge(["::/0", "::192.0.2.0/124", "ff00::101/128"], x) + } + query: data.test.p = x + want_result: + - x: + - ::/0 + - note: netcidrmerge/cidr IPv4 and IPv6 + modules: + - | + package test + + p = x { + net.cidr_merge(["fe80::/120", "192.0.2.0/24", "192.0.3.0/24", "192.0.4.0/25", "192.0.4.128/25"], x) + } + query: data.test.p = x + sort_bindings: true + want_result: + - x: + - 192.0.2.0/23 + - 192.0.4.0/24 + - fe80::/120 + - note: netcidrmerge/cidr empty + modules: + - | + package test + + p = x { + net.cidr_merge([], x) + } + query: data.test.p = x + want_result: + - x: [] + - note: netcidrmerge/cidr merge ip and subnets + modules: + - | + package test + + p = x { + net.cidr_merge(["192.0.2.112", "192.0.2.116/31", "192.0.2.118/31"], x) + } + query: data.test.p = x + want_result: + - x: + - 192.0.2.0/24 + - note: netcidrmerge/cidr merge ip addresses + modules: + - | + package test + + p = x { + net.cidr_merge(["192.0.128.0", "192.0.129.0"], x) + } + query: data.test.p = x + want_result: + - x: + - 192.0.128.0/23 + - note: netcidrmerge/cidr merge subnets set + modules: + - | + package test + + p = x { + net.cidr_merge({"192.0.2.112/30", "192.0.2.116/31", "192.0.2.118/31"}, x) + } + query: data.test.p = x + want_result: + - x: + - 192.0.2.112/29 + - note: netcidrmerge/cidr invalid IP + modules: + - | + package test + + p = x { + net.cidr_merge(["foo"], x) + } + query: data.test.p = x + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidroverlap/test-netcidroverlap-0084.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidroverlap/test-netcidroverlap-0084.yaml new file mode 100644 index 000000000000..af11ef7d2777 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidroverlap/test-netcidroverlap-0084.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_overlap("192.168.1.0/24", "192.168.1.67", x) + } + note: netcidroverlap/cidr match + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/netcidroverlap/test-netcidroverlap-0085.yaml b/third_party/opa/v1/test/cases/testdata/v0/netcidroverlap/test-netcidroverlap-0085.yaml new file mode 100644 index 000000000000..2af70eca45f2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netcidroverlap/test-netcidroverlap-0085.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + net.cidr_overlap("192.168.1.0/28", "192.168.1.67", x) + } + note: netcidroverlap/cidr mismatch + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/netlookupipaddr/test-netlookupipaddr.yaml b/third_party/opa/v1/test/cases/testdata/v0/netlookupipaddr/test-netlookupipaddr.yaml new file mode 100644 index 000000000000..109016e6b4ce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/netlookupipaddr/test-netlookupipaddr.yaml @@ -0,0 +1,46 @@ +--- +cases: + - data: + modules: + - | + package test + + p = x { + x := net.lookup_ip_addr("10.0.0.0") + } + note: net.lookup_ip_addr/simple ip4 returns that ip4 + query: data.test.p = x + want_result: + - x: + - 10.0.0.0 + - data: + modules: + - | + package test + + p = x { + x := net.lookup_ip_addr("::") + } + note: net.lookup_ip_addr/simple ip6 returns that ip6 + query: data.test.p = x + want_result: + - x: + - "::" + - data: + modules: + - | + package test + # one of these should be the case on any system + p { + net.lookup_ip_addr("localhost") == {"127.0.0.1"} + } + p { + net.lookup_ip_addr("localhost") == {"127.0.0.1", "::1"} + } + p { + net.lookup_ip_addr("localhost") == {"::1"} + } + note: net.lookup_ip_addr/localhost + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0256.yaml b/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0256.yaml new file mode 100644 index 000000000000..1dcda3964f5b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0256.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + numbers.range(0, 0, __local1__) + __local0__ = __local1__ + } + note: numbersrange/one + query: data.generated.p = x + want_result: + - x: + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0257.yaml b/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0257.yaml new file mode 100644 index 000000000000..41f41ab3d963 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0257.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + numbers.range(-2, 3, __local1__) + __local0__ = __local1__ + } + note: numbersrange/ascending + query: data.generated.p = x + want_result: + - x: + - -2 + - -1 + - 0 + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0258.yaml b/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0258.yaml new file mode 100644 index 000000000000..1e8dd378ba85 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0258.yaml @@ -0,0 +1,38 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + numbers.range(2, -3, __local1__) + __local0__ = __local1__ + } + note: numbersrange/descending + query: data.generated.p = x + want_result: + - x: + - 2 + - 1 + - 0 + - -1 + - -2 + - -3 + - note: numbersrange/descending (cheap optimization) + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ { + numbers.range(5, 2, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 5 + - 4 + - 3 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0259.yaml b/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0259.yaml new file mode 100644 index 000000000000..75d8e3575dca --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0259.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + numbers.range(49649733057, 49649733060, [49649733057, 49649733058, 49649733059, 49649733060]) + } + note: numbersrange/precision + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0260.yaml b/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0260.yaml new file mode 100644 index 000000000000..f70832efef72 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0260.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p { + numbers.range(3.14, 4) + } + note: "numbersrange/error: floating-point number pos 1" + query: data.generated.p = x + want_error: + "numbers.range: operand 1 must be integer number but got floating-point + number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0261.yaml b/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0261.yaml new file mode 100644 index 000000000000..dcd265edd200 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/numbersrange/test-numbersrange-0261.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + modules: + - | + package generated + + p { + numbers.range(3, 3.14) + } + note: "numbersrange/error: floating-point number pos 2" + query: data.generated.p = x + want_error: + "numbers.range: operand 2 must be integer number but got floating-point + number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/numbersrangestep/test-numbersrangestep.yaml b/third_party/opa/v1/test/cases/testdata/v0/numbersrangestep/test-numbersrangestep.yaml new file mode 100644 index 000000000000..dc05e621a5bd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/numbersrangestep/test-numbersrangestep.yaml @@ -0,0 +1,103 @@ +--- +cases: + - note: numbersrangestep/ascending + query: data.test.p = x + modules: + - | + package test + + p = num { + num := numbers.range_step(0, 10, 2) + } + want_result: + - x: + - 0 + - 2 + - 4 + - 6 + - 8 + - 10 + - note: numbersrangestep/descending + query: data.test.p = x + modules: + - | + package test + + p = num { + num := numbers.range_step(0, -10, 2) + } + want_result: + - x: + - 0 + - -2 + - -4 + - -6 + - -8 + - -10 + - note: numbersrangestep/descending (cheap optimization) + query: data.test.p = x + modules: + - | + package test + + p = num { + num := numbers.range_step(10, 3, 2) + } + want_result: + - x: + - 10 + - 8 + - 6 + - 4 + - note: numbersrangestep/negative + query: data.test.p = x + modules: + - | + package test + + p = num { + num := numbers.range_step(0, 10, -2) + } + want_error: "numbers.range_step: step must be a positive number above zero" + want_error_code: eval_builtin_error + strict_error: true + - note: numbersrangestep/memoryexample + query: data.test.p = x + modules: + - | + package test + + p = num { + num := numbers.range_step(1024, 4096, 1024) + } + want_result: + - x: + - 1024 + - 2048 + - 3072 + - 4096 + - note: numbersrangestep/equal + query: data.test.p = x + modules: + - | + package test + + p = num { + num := numbers.range_step(2, 2, 2) + } + want_result: + - x: + - 2 + - note: numbersrangestep/notinrange + query: data.test.p = x + modules: + - | + package test + + p = num { + num := numbers.range_step(2, 5, 2) + } + want_result: + - x: + - 2 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0300.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0300.yaml new file mode 100644 index 000000000000..dd737c967019 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0300.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectfilter/base + query: data.generated.p = x + want_result: + - x: + a: + b: + c: 7 + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0301.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0301.yaml new file mode 100644 index 000000000000..04c8e48979b8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0301.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.filter({"a": 1, "b": 2, "c": 3, "e": 9}, {"a", "e"}, __local1__) + __local0__ = __local1__ + } + note: objectfilter/multiple roots set + query: data.generated.p = x + want_result: + - x: + a: 1 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0302.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0302.yaml new file mode 100644 index 000000000000..7515d84ef54f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0302.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.filter({"a": 1, "b": 2, "c": 3, "e": 9}, ["a", "e"], __local1__) + __local0__ = __local1__ + } + note: objectfilter/multiple roots array + query: data.generated.p = x + want_result: + - x: + a: 1 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0303.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0303.yaml new file mode 100644 index 000000000000..b4dd9edcbb27 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0303.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.filter({"a": 1, "b": 2, "c": 3, "e": 9}, {"a": "foo", "e": ""}, __local1__) + __local0__ = __local1__ + } + note: objectfilter/multiple roots object + query: data.generated.p = x + want_result: + - x: + a: 1 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0304.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0304.yaml new file mode 100644 index 000000000000..a4a8480ff5f0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0304.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectfilter/duplicate roots + query: data.generated.p = x + want_result: + - x: + a: + b: + c: 7 + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0305.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0305.yaml new file mode 100644 index 000000000000..29cc394221b7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0305.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.filter({"a": 7}, set(), __local1__) + __local0__ = __local1__ + } + note: objectfilter/empty roots set + query: data.generated.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0306.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0306.yaml new file mode 100644 index 000000000000..5725f8a802f3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0306.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.filter({"a": 7}, [], __local1__) + __local0__ = __local1__ + } + note: objectfilter/empty roots array + query: data.generated.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0307.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0307.yaml new file mode 100644 index 000000000000..b1c3d735aa50 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0307.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.filter({"a": 7}, {}, __local1__) + __local0__ = __local1__ + } + note: objectfilter/empty roots object + query: data.generated.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0308.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0308.yaml new file mode 100644 index 000000000000..ea31c1a8cbba --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0308.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.filter({}, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectfilter/empty object + query: data.generated.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0309.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0309.yaml new file mode 100644 index 000000000000..3d732c8e0c20 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0309.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + input_term: '{"x": ["a"]}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.filter(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectfilter/error invalid object param type array input + query: data.generated.p = x + want_error: "object.filter: operand 1 must be object but got array" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0310.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0310.yaml new file mode 100644 index 000000000000..89dc5011b469 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0310.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + input_term: '{"x": false}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.filter(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectfilter/error invalid object param type bool input + query: data.generated.p = x + want_error: "object.filter: operand 1 must be object but got boolean" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0311.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0311.yaml new file mode 100644 index 000000000000..8d3ad7c2ce72 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0311.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + input_term: '{"x": 123}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.filter(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectfilter/error invalid object param type number input + query: data.generated.p = x + want_error: "object.filter: operand 1 must be object but got number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0312.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0312.yaml new file mode 100644 index 000000000000..a7c24b79b98d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0312.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + input_term: '{"x": "foo"}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.filter(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectfilter/error invalid object param type string input + query: data.generated.p = x + want_error: "object.filter: operand 1 must be object but got string" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0313.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0313.yaml new file mode 100644 index 000000000000..260486505dfd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0313.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + input_term: '{"x": null}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.filter(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectfilter/error invalid object param type nil input + query: data.generated.p = x + want_error: "object.filter: operand 1 must be object but got null" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0314.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0314.yaml new file mode 100644 index 000000000000..4ac47b9966f9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0314.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + input_term: '{"x": "foo"}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.filter({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + note: objectfilter/error invalid key param type string input + query: data.generated.p = x + want_error: "object.filter: operand 2 must be one of {object, set, array} but got string" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0315.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0315.yaml new file mode 100644 index 000000000000..7596501272d6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0315.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + input_term: '{"x": true}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.filter({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + note: objectfilter/error invalid key param type boolean input + query: data.generated.p = x + want_error: "object.filter: operand 2 must be one of {object, set, array} but got boolean" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0316.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0316.yaml new file mode 100644 index 000000000000..737b288a11db --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0316.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + input_term: '{"x": 22}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.filter({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + note: objectfilter/error invalid key param type number input + query: data.generated.p = x + want_error: "object.filter: operand 2 must be one of {object, set, array} but got number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0317.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0317.yaml new file mode 100644 index 000000000000..27ca4fec0251 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilter/test-objectfilter-0317.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + input_term: '{"x": null}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.filter({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + note: objectfilter/error invalid key param type nil input + query: data.generated.p = x + want_error: "object.filter: operand 2 must be one of {object, set, array} but got null" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilteridempotent/test-objectfilteridempotent-0319.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilteridempotent/test-objectfilteridempotent-0319.yaml new file mode 100644 index 000000000000..72bb2775932d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilteridempotent/test-objectfilteridempotent-0319.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + __local0__ = {"a": 1, "b": 2, "c": 3} + object.filter(__local0__, {"a"}, __local1__) + __local1__ = {"a": 1} + object.filter(__local0__, {"b"}, __local2__) + __local2__ = {"b": 2} + object.filter(__local0__, {"c"}, __local3__) + __local3__ = {"c": 3} + __local0__ = {"a": 1, "b": 2, "c": 3} + } + note: objectfilteridempotent/TestBuiltinObjectFilterIdempotent + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectfilternonstringkey/test-objectfilternonstringkey-0318.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectfilternonstringkey/test-objectfilternonstringkey-0318.yaml new file mode 100644 index 000000000000..0a8144665b87 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectfilternonstringkey/test-objectfilternonstringkey-0318.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + object.filter({"a": 1, [[7]]: 2}, {[[7]]}, __local1__) + __local0__ = __local1__ + __local0__ = {[[7]]: 2} + } + note: objectfilternonstringkey/non string root + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0262.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0262.yaml new file mode 100644 index 000000000000..b7a003193b27 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0262.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.get({"a": "b"}, "a", "c", __local1__) + __local0__ = __local1__ + } + note: objectget/basic case . found + query: data.generated.p = x + want_result: + - x: b diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0263.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0263.yaml new file mode 100644 index 000000000000..a543d65fdbf8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0263.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.get({"a": "b"}, "c", "c", __local1__) + __local0__ = __local1__ + } + note: objectget/basic case . not found + query: data.generated.p = x + want_result: + - x: c diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0264.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0264.yaml new file mode 100644 index 000000000000..fc7d01844d56 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0264.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.get({1: 2}, 1, 3, __local1__) + __local0__ = __local1__ + } + note: objectget/integer key found + query: data.generated.p = x + want_result: + - x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0265.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0265.yaml new file mode 100644 index 000000000000..2758272c2bd0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0265.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.get({1: 2}, 2, 3, __local1__) + __local0__ = __local1__ + } + note: objectget/integer key . not found + query: data.generated.p = x + want_result: + - x: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0266.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0266.yaml new file mode 100644 index 000000000000..bc60839a5bbe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0266.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.get({"a": {"b": "c"}}, "a", true, __local1__) + __local0__ = __local1__ + } + note: objectget/complex value . found + query: data.generated.p = x + want_result: + - x: + b: c diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0267.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0267.yaml new file mode 100644 index 000000000000..41efdac6afec --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-0267.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.get({"a": {"b": "c"}}, "b", true, __local1__) + __local0__ = __local1__ + } + note: objectget/complex value . not found + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-path.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-path.yaml new file mode 100644 index 000000000000..cc3e818c48c8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectget/test-objectget-path.yaml @@ -0,0 +1,125 @@ +--- +cases: + - note: objectget/empty_path_returns_object + query: data.test.p = x + modules: + - | + package test + + p = x { + x := object.get({"a": 1}, [], 2) + } + want_result: [{ x: { a: 1 } }] + + - note: objectget/path_with_single_element + query: data.test.p = x + modules: + - | + package test + + p = x { + x := object.get({ "a": 1 }, ["a"], 2) + } + want_result: [{ x: 1 }] + + - note: objectget/path_with_two_elements + query: data.test.p = x + modules: + - | + package test + + p = x { + x := object.get({ "a": { "b": 1 } }, ["a", "b"], 2) + } + want_result: [{ x: 1 }] + + - note: objectget/path_with_three_elements + query: data.test.p = x + modules: + - | + package test + + p = x { + x := object.get({ "a": { "b": { "c": 1 } } }, ["a", "b", "c"], 2) + } + want_result: [{ x: 1 }] + + - note: objectget/path_with_single_element_no_result + query: data.test.p = x + modules: + - | + package test + + p = x { + x := object.get({ "a": 1 }, ["b"], 2) + } + want_result: [{ x: 2 }] + + - note: objectget/path_with_two_elements_no_result + query: data.test.p = x + modules: + - | + package test + + p = x { + x := object.get({ "a": { "b": 1 } }, ["b", "a"], 2) + } + want_result: [{ x: 2 }] + + - note: objectget/path_with_three_elements_no_result + query: data.test.p = x + modules: + - | + package test + + p = x { + x := object.get({ "a": { "b": { "c": 1 } } }, ["a", "b", "a"], 2) + } + want_result: [{ x: 2 }] + + - note: objectget/path_with_non_string_keys + query: data.test.p = x + modules: + - | + package test + + p = x { + x := object.get({ 1: { "b": { [1,2,3]: 1 } } }, [1, "b", [1,2,3]], 2) + } + want_result: [{ x: 1 }] + + - note: objectget/get_intermediate_non_object + query: data.test.p = x + modules: + - | + package test + + p = x { + x := object.get({ "a": { "b": [1,2,3] } }, ["a", "b", "a"], 2) + } + want_result: [{ x: 2 }] + + - note: objectget/get_intermediate_array + query: data.test.p = x + modules: + - | + package test + + p = x { + x := object.get({ "a": { "b": [{"c": 1}] } }, ["a", "b", 0, "c"], 2) + } + want_result: [{ x: 1 }] + + - note: objectget/get_for_non_object + query: data.test.p = x + input_term: '{"obj":"object"}' + modules: + - | + package test + + p = x { + x := object.get(input.obj, ["a"], 2) + } + want_error: "object.get: operand 1 must be object but got string" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectkeys/test-objectkeys.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectkeys/test-objectkeys.yaml new file mode 100644 index 000000000000..7e3690fb744f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectkeys/test-objectkeys.yaml @@ -0,0 +1,70 @@ +--- +cases: + - note: objectkeys/string_keys_found + query: data.test.p = x + modules: + - | + package test + p := object.keys({"a": 1, "b": 2}) + want_result: + - x: + - a + - b + - note: objectkeys/number_keys_found + query: data.test.p = x + modules: + - | + package test + p := object.keys({1: 1, 2: 2}) + want_result: + - x: + - 1 + - 2 + - note: objectkeys/object_keys_found + query: data.test.p = x + modules: + - | + package test + p := object.keys({{"a": 1}: 1, {"b": 2}: 2}) + want_result: + - x: + - a: 1 + - b: 2 + - note: objectkeys/set_keys_found + query: data.test.p = x + modules: + - | + package test + p := object.keys({{"a"}: 1, {"b"}: 2}) + want_result: + - x: + - [a] + - [b] + - note: objectkeys/array_keys_found + query: data.test.p = x + modules: + - | + package test + p := object.keys({["a"]: 1, ["b"]: 2}) + want_result: + - x: + - [a] + - [b] + - note: objectkeys/empty_result + query: data.test.p = x + modules: + - | + package test + p := object.keys({}) + want_result: + - x: [] + - note: objectkeys/error_on_non_object + query: data.test.p = x + input_term: '{"obj":"object"}' + modules: + - | + package test + p := object.keys(input.obj) + want_error: "object.keys: operand 1 must be object but got string" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0279.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0279.yaml new file mode 100644 index 000000000000..fa48ad1ab030 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0279.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.remove({"a": 1, "b": {"c": 3}}, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectremove/base + query: data.generated.p = x + want_result: + - x: + b: + c: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0280.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0280.yaml new file mode 100644 index 000000000000..63af32e60e50 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0280.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.remove({"a": 1, "b": {"c": 3}, "d": 4}, {"b", "d"}, __local1__) + __local0__ = __local1__ + } + note: objectremove/multiple keys set + query: data.generated.p = x + want_result: + - x: + a: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0281.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0281.yaml new file mode 100644 index 000000000000..816eee487ace --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0281.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.remove({"a": 1, "b": {"c": 3}, "d": 4}, ["d", "b"], __local1__) + __local0__ = __local1__ + } + note: objectremove/multiple keys array + query: data.generated.p = x + want_result: + - x: + a: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0282.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0282.yaml new file mode 100644 index 000000000000..2ebae4d05aa6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0282.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.remove({"a": 1, "b": {"c": 3}, "d": 4}, {"b": 1, "d": ""}, __local1__) + __local0__ = __local1__ + } + note: objectremove/multiple keys object + query: data.generated.p = x + want_result: + - x: + a: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0283.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0283.yaml new file mode 100644 index 000000000000..d0d23a5fb592 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0283.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.remove({"a": {"b": {"c": 2}}, "x": 123}, {"a": {"b": {"foo": "bar"}}}, __local1__) + __local0__ = __local1__ + } + note: objectremove/multiple keys object nested + query: data.generated.p = x + want_result: + - x: + x: 123 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0284.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0284.yaml new file mode 100644 index 000000000000..6ea53b6bb392 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0284.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.remove({}, {"a", "b"}, __local1__) + __local0__ = __local1__ + } + note: objectremove/empty object + query: data.generated.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0285.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0285.yaml new file mode 100644 index 000000000000..169074b5fdb4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0285.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.remove({"a": 1, "b": {"c": 3}}, set(), __local1__) + __local0__ = __local1__ + } + note: objectremove/empty keys set + query: data.generated.p = x + want_result: + - x: + a: 1 + b: + c: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0286.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0286.yaml new file mode 100644 index 000000000000..414daaec8b62 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0286.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.remove({"a": 1, "b": {"c": 3}}, [], __local1__) + __local0__ = __local1__ + } + note: objectremove/empty keys array + query: data.generated.p = x + want_result: + - x: + a: 1 + b: + c: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0287.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0287.yaml new file mode 100644 index 000000000000..dc11314ebed8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0287.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.remove({"a": 1, "b": {"c": 3}}, {}, __local1__) + __local0__ = __local1__ + } + note: objectremove/empty keys obj + query: data.generated.p = x + want_result: + - x: + a: 1 + b: + c: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0288.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0288.yaml new file mode 100644 index 000000000000..e7112889a27c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0288.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.remove({"a": 1, "b": {"c": 3}}, {"z"}, __local1__) + __local0__ = __local1__ + } + note: objectremove/key doesnt exist + query: data.generated.p = x + want_result: + - x: + a: 1 + b: + c: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0289.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0289.yaml new file mode 100644 index 000000000000..0a5a867075e2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0289.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": ["a"]}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.remove(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectremove/error invalid object param type array input + query: data.generated.p = x + want_error: "object.remove: operand 1 must be object but got array" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0290.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0290.yaml new file mode 100644 index 000000000000..0889c2d60421 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0290.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": false}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.remove(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectremove/error invalid object param type bool input + query: data.generated.p = x + want_error: "object.remove: operand 1 must be object but got boolean" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0291.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0291.yaml new file mode 100644 index 000000000000..10c15329cbd0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0291.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": 123}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.remove(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectremove/error invalid object param type number input + query: data.generated.p = x + want_error: "object.remove: operand 1 must be object but got number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0292.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0292.yaml new file mode 100644 index 000000000000..13ce4aa3bebb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0292.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": "foo"}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.remove(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectremove/error invalid object param type string input + query: data.generated.p = x + want_error: "object.remove: operand 1 must be object but got string" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0293.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0293.yaml new file mode 100644 index 000000000000..425176b2c2f7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0293.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": null}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.remove(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + note: objectremove/error invalid object param type nil input + query: data.generated.p = x + want_error: "object.remove: operand 1 must be object but got null" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0294.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0294.yaml new file mode 100644 index 000000000000..7575b3616053 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0294.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": "foo"}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.remove({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + note: objectremove/error invalid key param type string input + query: data.generated.p = x + want_error: "object.remove: operand 2 must be one of {object, set, array} but got string" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0295.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0295.yaml new file mode 100644 index 000000000000..8c993f4fc749 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0295.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": true}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.remove({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + note: objectremove/error invalid key param type boolean input + query: data.generated.p = x + want_error: "object.remove: operand 2 must be one of {object, set, array} but got boolean" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0296.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0296.yaml new file mode 100644 index 000000000000..b7980c1f2f6e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0296.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": 22}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.remove({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + note: objectremove/error invalid key param type number input + query: data.generated.p = x + want_error: "object.remove: operand 2 must be one of {object, set, array} but got number" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0297.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0297.yaml new file mode 100644 index 000000000000..2ce18711a118 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremove/test-objectremove-0297.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"x": null}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.x + object.remove({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + note: objectremove/error invalid key param type nil input + query: data.generated.p = x + want_error: "object.remove: operand 2 must be one of {object, set, array} but got null" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremoveidempotent/test-objectremoveidempotent-0298.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremoveidempotent/test-objectremoveidempotent-0298.yaml new file mode 100644 index 000000000000..465fbffeb6be --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremoveidempotent/test-objectremoveidempotent-0298.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + __local0__ = {"a": 1, "b": 2, "c": 3} + object.remove(__local0__, {"a"}, __local1__) + __local1__ = {"b": 2, "c": 3} + object.remove(__local0__, {"b"}, __local2__) + __local2__ = {"a": 1, "c": 3} + object.remove(__local0__, {"c"}, __local3__) + __local3__ = {"a": 1, "b": 2} + __local0__ = {"a": 1, "b": 2, "c": 3} + } + note: objectremoveidempotent/TestBuiltinObjectRemoveIdempotent + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectremovenonstringkey/test-objectremovenonstringkey-0299.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectremovenonstringkey/test-objectremovenonstringkey-0299.yaml new file mode 100644 index 000000000000..d4b5bd4d6230 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectremovenonstringkey/test-objectremovenonstringkey-0299.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + object.remove({"a": 1, [[7]]: 2}, {[[7]]}, __local1__) + __local0__ = __local1__ + __local0__ = {"a": 1} + } + note: objectremovenonstringkey/non string root + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0268.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0268.yaml new file mode 100644 index 000000000000..353c73a28c76 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0268.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.union({}, {}, __local1__) + __local0__ = __local1__ + } + note: objectunion/both empty + query: data.generated.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0269.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0269.yaml new file mode 100644 index 000000000000..2b17e71440f6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0269.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.union({}, {"a": 1}, __local1__) + __local0__ = __local1__ + } + note: objectunion/left empty + query: data.generated.p = x + want_result: + - x: + a: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0270.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0270.yaml new file mode 100644 index 000000000000..8a6e211a0d0d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0270.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.union({"a": 1}, {}, __local1__) + __local0__ = __local1__ + } + note: objectunion/right empty + query: data.generated.p = x + want_result: + - x: + a: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0271.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0271.yaml new file mode 100644 index 000000000000..7aeccb7ca3d6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0271.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.union({"a": 1}, {"b": 2}, __local1__) + __local0__ = __local1__ + } + note: objectunion/base + query: data.generated.p = x + want_result: + - x: + a: 1 + b: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0272.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0272.yaml new file mode 100644 index 000000000000..d1f2ef336853 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0272.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.union({"a": {"b": {"c": 1}}}, {"b": 2}, __local1__) + __local0__ = __local1__ + } + note: objectunion/nested + query: data.generated.p = x + want_result: + - x: + a: + b: + c: 1 + b: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0273.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0273.yaml new file mode 100644 index 000000000000..879386f3c93c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0273.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.union({"b": 2}, {"a": {"b": {"c": 1}}}, __local1__) + __local0__ = __local1__ + } + note: objectunion/nested reverse + query: data.generated.p = x + want_result: + - x: + a: + b: + c: 1 + b: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0274.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0274.yaml new file mode 100644 index 000000000000..edff4d7eae50 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0274.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.union({"a": 1}, {"a": 2}, __local1__) + __local0__ = __local1__ + } + note: objectunion/conflict simple + query: data.generated.p = x + want_result: + - x: + a: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0275.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0275.yaml new file mode 100644 index 000000000000..b3f310f262d6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0275.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.union({"a": 1}, {"a": {"b": {"c": 1}}, "d": 7}, __local1__) + __local0__ = __local1__ + } + note: objectunion/conflict nested and extra field + query: data.generated.p = x + want_result: + - x: + a: + b: + c: 1 + d: 7 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0276.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0276.yaml new file mode 100644 index 000000000000..87342d38b18e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0276.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + object.union({"a": {"b": {"c": 1}}, "e": 1}, {"a": {"b": "foo", "b1": "bar"}, "d": 7, "e": 17}, __local1__) + __local0__ = __local1__ + } + note: objectunion/conflict multiple + query: data.generated.p = x + want_result: + - x: + a: + b: foo + b1: bar + d: 7 + e: 17 diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0277.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0277.yaml new file mode 100644 index 000000000000..4913eee75308 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0277.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"a": [1, 2, 3]}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.a + object.union(__local2__, {"b": 2}, __local1__) + __local0__ = __local1__ + } + note: objectunion/error wrong lhs type input + query: data.generated.p = x + want_error: "object.union: operand 1 must be object but got array" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0278.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0278.yaml new file mode 100644 index 000000000000..27c306f770d3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectunion/test-objectunion-0278.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"b": [1, 2, 3]}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.b + object.union({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + note: objectunion/error wrong rhs type input + query: data.generated.p = x + want_error: "object.union: operand 2 must be object but got array" + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/objectunionn/test-objectunionn-0001.yaml b/third_party/opa/v1/test/cases/testdata/v0/objectunionn/test-objectunionn-0001.yaml new file mode 100644 index 000000000000..7c1992eacfd7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/objectunionn/test-objectunionn-0001.yaml @@ -0,0 +1,80 @@ +--- +cases: + - note: objectunionn/empty array + modules: + - | + package test + + p := object.union_n([{}]) + + query: data.test.p = x + want_result: + - x: {} + + - note: objectunionn/single item array + modules: + - | + package test + + p := object.union_n([{"foo": "bar"}]) + + query: data.test.p = x + want_result: + - x: { "foo": "bar" } + + - note: objectunionn/merge objects + modules: + - | + package test + + x := object.union_n([{"foo": "bar"}, {"x": "y"}]) + + query: data.test.x = x + want_result: + - x: { "foo": "bar", "x": "y" } + + - note: objectunionn/merge objects conflict + modules: + - | + package test + + x := object.union_n([{"foo": "bar"}, {"foo": "baz"}]) + + query: data.test.x = x + want_result: + - x: { "foo": "baz" } + + - note: objectunionn/merge objects extended + modules: + - | + package test + + x := object.union_n([{ + "a": 1, + "b": 2, + "c": 3, + }, { + "foo": "baz", + "a": "a", + "b": 2, + "d": 4, + }, { + "a": "final A!", + "e": 5.0, + }]) + + query: data.test.x = x + want_result: + - x: { "foo": "baz", "a": "final A!", "b": 2, "c": 3, "d": 4, "e": 5.0 } + + - note: "# 5073 regression test" + input_term: '[{"foo": 1}, {"bar": 2}, "baz"]' + modules: + - | + package test + + x := object.union_n(input) + + query: data.test.x = x + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0984.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0984.yaml new file mode 100644 index 000000000000..36410e374a13 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0984.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + foo["bar"] = 0 + + foo["baz"] = 1 + + foo["*"] = [1, 2, 3] { + input.foo = 7 + } + + bar["x"] + + bar["y"] + + bar["*"] { + input.foo = 7 + } + note: partialdocconstants/obj-1 + query: data.ex.foo.bar = x + want_result: + - x: 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0985.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0985.yaml new file mode 100644 index 000000000000..8437cfc05eeb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0985.yaml @@ -0,0 +1,34 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + foo["bar"] = 0 + + foo["baz"] = 1 + + foo["*"] = [1, 2, 3] { + input.foo = 7 + } + + bar["x"] + + bar["y"] + + bar["*"] { + input.foo = 7 + } + - | + package topdown_test_partial + + __result__ = _result { + _result = 0 + } + note: partialdocconstants/obj + query: data.ex.foo = x + want_result: + - x: + bar: 0 + baz: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0986.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0986.yaml new file mode 100644 index 000000000000..56786212cd2d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0986.yaml @@ -0,0 +1,49 @@ +--- +cases: + - data: {} + input_term: '{"foo": 7}' + modules: + - | + package partial.ex + + foo["bar"] = 0 + + foo["baz"] = 1 + + foo["*"] = [1, 2, 3] { + input.foo = 7 + } + - | + package ex + + foo["bar"] = 0 + + foo["baz"] = 1 + + foo["*"] = [1, 2, 3] { + input.foo = 7 + } + + bar["x"] + + bar["y"] + + bar["*"] { + input.foo = 7 + } + - | + package topdown_test_partial + + __result__ = _result { + data.partial.ex.foo = _result + } + note: partialdocconstants/obj-all + query: data.ex.foo = x + want_result: + - x: + "*": + - 1 + - 2 + - 3 + bar: 0 + baz: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0987.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0987.yaml new file mode 100644 index 000000000000..7ff82cd75cd8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0987.yaml @@ -0,0 +1,42 @@ +--- +cases: + - data: {} + modules: + - | + package topdown_test_partial + + __result__ = _result { + data.partial.ex.foo = _result + } + - | + package partial.ex + + foo["bar"] = 0 + + foo["baz"] = 1 + + foo["*"] = [1, 2, 3] { + input.foo = 7 + } + - | + package ex + + foo["bar"] = 0 + + foo["baz"] = 1 + + foo["*"] = [1, 2, 3] { + input.foo = 7 + } + + bar["x"] + + bar["y"] + + bar["*"] { + input.foo = 7 + } + note: partialdocconstants/set-1 + query: data.ex.bar.x = x + want_result: + - x: x diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0988.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0988.yaml new file mode 100644 index 000000000000..61084d01574d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0988.yaml @@ -0,0 +1,45 @@ +--- +cases: + - data: {} + modules: + - | + package ex + + foo["bar"] = 0 + + foo["baz"] = 1 + + foo["*"] = [1, 2, 3] { + input.foo = 7 + } + + bar["x"] + + bar["y"] + + bar["*"] { + input.foo = 7 + } + - | + package topdown_test_partial + + __result__ = _result { + _result = "x" + } + - | + package partial.ex + + foo["bar"] = 0 + + foo["baz"] = 1 + + foo["*"] = [1, 2, 3] { + input.foo = 7 + } + note: partialdocconstants/set + query: data.ex.bar = x + sort_bindings: true + want_result: + - x: + - x + - "y" diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0989.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0989.yaml new file mode 100644 index 000000000000..542fe2829bb8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialdocconstants/test-partialdocconstants-0989.yaml @@ -0,0 +1,47 @@ +--- +cases: + - data: {} + input_term: '{"foo": 7}' + modules: + - | + package ex + + foo["bar"] = 0 + + foo["baz"] = 1 + + foo["*"] = [1, 2, 3] { + input.foo = 7 + } + + bar["x"] + + bar["y"] + + bar["*"] { + input.foo = 7 + } + - | + package topdown_test_partial + + __result__ = _result { + data.partial.ex.bar = _result + } + - | + package partial.ex + + bar["x"] + + bar["y"] + + bar["*"] { + input.foo = 7 + } + note: partialdocconstants/set-all + query: data.ex.bar = x + sort_bindings: true + want_result: + - x: + - "*" + - x + - "y" diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialiter/test-partialiter-001.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialiter/test-partialiter-001.yaml new file mode 100644 index 000000000000..dc6f3b2b21e4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialiter/test-partialiter-001.yaml @@ -0,0 +1,40 @@ +--- +cases: + - modules: + - | + package test + + p := count([x | q[x]]) + + q[1] + q[1] + q[2] + note: partialiter/sets unique + query: data.test.p = x + want_result: + - x: 2 + - modules: + - | + package test + + p := count([x | q[x]]) + + q[1] = 1 + q[1] = 1 + q[2] = 1 + note: partialiter/objects unique + query: data.test.p = x + want_result: + - x: 2 + - modules: + - | + package test + + p := count([x | q[x]]) + + q[1] = 1 + q[1] = 2 + q[2] = 1 + note: partialiter/objects conflict + query: data.test.p = x + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0519.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0519.yaml new file mode 100644 index 000000000000..c3f54b7ffc89 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0519.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[k] = v { + data.b[k] = v + } + note: partialobjectdoc/identity + query: data.generated.p = x + want_result: + - x: + v1: hello + v2: goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0520.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0520.yaml new file mode 100644 index 000000000000..6aaf69edb418 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0520.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + d: + e: + - bar + - baz + modules: + - | + package generated + + p[k] = v { + data.d[k] = v + } + note: partialobjectdoc/composites + query: data.generated.p = x + want_result: + - x: + e: + - bar + - baz diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0521.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0521.yaml new file mode 100644 index 000000000000..58e630d6900e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0521.yaml @@ -0,0 +1,39 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + g: + a: + - 1 + - 0 + - 0 + - 0 + b: + - 0 + - 2 + - 0 + - 0 + c: + - 0 + - 0 + - 0 + - 4 + modules: + - | + package generated + + p[k] = v { + data.a[i] = v + data.g[k][i] = v + } + note: partialobjectdoc/body/join var + query: data.generated.p = x + want_result: + - x: + a: 1 + b: 2 + c: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0522.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0522.yaml new file mode 100644 index 000000000000..47693beedd5c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0522.yaml @@ -0,0 +1,41 @@ +--- +cases: + - data: + g: + a: + - 1 + - 0 + - 0 + - 0 + b: + - 0 + - 2 + - 0 + - 0 + c: + - 0 + - 0 + - 0 + - 4 + modules: + - | + package generated + + p[k] = [v1, {"v2": v2}] { + data.g[k] = x + x[v1] = v2 + v2 != 0 + } + note: partialobjectdoc/composite value + query: data.generated.p = x + want_result: + - x: + a: + - 0 + - v2: 1 + b: + - 1 + - v2: 2 + c: + - 3 + - v2: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0523.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0523.yaml new file mode 100644 index 000000000000..8fc3ca348b37 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0523.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[k] = 1 { + ks = ["a", "b", "c", "a"] + ks[_] = k + } + note: partialobjectdoc/same key/value pair + query: data.generated.p = x + want_result: + - x: + a: 1 + b: 1 + c: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0524.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0524.yaml new file mode 100644 index 000000000000..89b77cafb874 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-0524.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[k] = 1 { + ks = [1, {}, null] + ks[_] = k + } + note: partialobjectdoc/non-string key + query: data.generated.p = x + want_result: + - x: + "1": 1 + "null": 1 + "{}": 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-ref.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-ref.yaml new file mode 100644 index 000000000000..762fd4a594e0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-partialobjectdoc-ref.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: partialobjectdoc/ref + modules: + - | + package generated + + p.q[k] = v { + k := ["foo", "bar"][v] + } + + p.baz := 2 + + q { + x := "bar" + y := "q" + p[y][x] == 1 + } + + query: data.generated.q = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-wasm-cases.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-wasm-cases.yaml new file mode 100644 index 000000000000..95d8e4f07e2b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialobjectdoc/test-wasm-cases.yaml @@ -0,0 +1,106 @@ +--- +# NOTE(sr): These test cases stem from cases we run against the wasm +# module but since the ref-heads change made them fail -- because of +# changes to the typing of partial object rules (which are now single- +# valued rules), they're added to the topdown tests, too. +cases: + - note: wasm/additive + query: "data.x.q = x" + modules: + - | + package x + p["a"] = 1 + p["b"] = 2 + + q { + p == {"a": 1, "b": 2} + } + want_result: + - x: true + - note: wasm/additive (negative) + query: "data.x.p = input" + input: { "a": 1, "b": 2 } + modules: + - | + package x + p["a"] = 1 + p["b"] = 2 + p["c"] = 3 + want_result: [] + - note: wasm/input + query: "data.x.q = x" + modules: + - | + package x + p["a"] = 1 { input.x = 1 } + p["b"] = 2 { input.y = 2 } + + q { + p == {"a": 1, "b": 2} + } + input: { "x": 1, "y": 2 } + want_result: + - x: true + - note: wasm/input (negative) + query: "data.x.q = x" + data: + z: + a: 1 + b: 2 + modules: + - | + package x + p["a"] = 1 { input.x = 1 } + p["b"] = 2 { input.y = 2 } + p["c"] = 3 { input.z = 3 } + + q { + p == data.z + } + want_result: + - x: true + input: { "x": 1, "y": 2 } + - note: wasm/composites + query: "data.x.q = x" + modules: + - | + package x + p[x] = [y] { x = "a"; y = 1 } + p[x] = [y] { x = "b"; y = 2 } + + q { p == {"a": [1], "b": [2]} } + want_result: + - x: true + - note: wasm/conflict error + query: "data.x.q = x" + data: + z: + a: 1 + modules: + - | + package x + p["x"] = 1 + p["x"] = 2 + q { + p == data.z + } + want_error: "test-0.rego:3: eval_conflict_error: complete rules must not produce multiple outputs" + want_error_code: eval_conflict_error + - note: wasm/object dereference + query: "data.x.q = x" + modules: + - | + package x + p["a"] = {"b": 1} + q { + p.a.b = 1 + } + want_result: + - x: true + - note: wasm/object dereference (negative) + query: data.x.p.a.b = 1 + modules: + - | + package x + p["a"] = {"b": 2} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-issue-3369.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-issue-3369.yaml new file mode 100644 index 000000000000..a728f1da3902 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-issue-3369.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + modules: + - | + package x + + p[a] { + a := q + } + + q[b] { + b := 1 + } + note: partialsetdoc/unexpected 'var requires evaluation' + query: data.x.p[x] = z + want_result: + - x: + - 1 + z: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-issue-3376.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-issue-3376.yaml new file mode 100644 index 000000000000..23eb6ba07149 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-issue-3376.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + modules: + - | + package foo + p { + q[i][j] = v # this fails! + } + q[x] { + x = r + } + r[x] { + x = [1] + } + note: partialsetdoc/iteration + query: data.foo.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-issue-3819.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-issue-3819.yaml new file mode 100644 index 000000000000..77872ffa01a8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-issue-3819.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + apples: 2 + bananas: 1 + clementines: 3 + modules: + - | + package foo + p[x] { + data.a[x] # iterates data.a + q[_] + } + q[x] { + x = data.a # inserts data.a into a set, sorts the object keys + } + note: "partialsetdoc: object sort while iter" + query: data.foo.p = x + sort_bindings: true + want_result: + - x: + - apples + - bananas + - clementines diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0511.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0511.yaml new file mode 100644 index 000000000000..e6c6a34171ff --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0511.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + data.a[i] = x + } + note: partialsetdoc/array values + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0512.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0512.yaml new file mode 100644 index 000000000000..8e3ba2fb1375 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0512.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + data.a[x] = _ + } + note: partialsetdoc/array indices + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 0 + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0513.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0513.yaml new file mode 100644 index 000000000000..484a001af59f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0513.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[x] { + data.b[x] = _ + } + note: partialsetdoc/object keys + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - v1 + - v2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0514.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0514.yaml new file mode 100644 index 000000000000..3dc7d1c10f48 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0514.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[x] { + data.b[i] = x + } + note: partialsetdoc/object values + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - goodbye + - hello diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0515.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0515.yaml new file mode 100644 index 000000000000..c3fd9a9d5120 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0515.yaml @@ -0,0 +1,32 @@ +--- +cases: + - data: + f: + - xs: + - 1 + ys: + - 2 + - xs: + - 2 + ys: + - 3 + modules: + - | + package generated + + p[x] { + data.f[i] = x + } + note: partialsetdoc/nested composites + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - xs: + - 1 + ys: + - 2 + - xs: + - 2 + ys: + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0516.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0516.yaml new file mode 100644 index 000000000000..28845a1950bc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0516.yaml @@ -0,0 +1,31 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p[x] { + data.c[i][j][k] = x + } + note: partialsetdoc/deep ref/heterogeneous + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - null + - false + - true + - 3.14159 + - foo diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0517.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0517.yaml new file mode 100644 index 000000000000..0349c6e5ca7f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0517.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.a[i] + x = [i, __local0__] + } + note: partialsetdoc/composite var value + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 0 + - 1 + - - 1 + - 2 + - - 2 + - 3 + - - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0518.yaml b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0518.yaml new file mode 100644 index 000000000000..7fad23f21637 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/partialsetdoc/test-partialsetdoc-0518.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[[x, {"y": y}]] { + x = 1 + y = 2 + } + note: partialsetdoc/composite key + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 1 + - "y": 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/planner-ir/test-array-ir-unify.yaml b/third_party/opa/v1/test/cases/testdata/v0/planner-ir/test-array-ir-unify.yaml new file mode 100644 index 000000000000..9c14ab1bdaaf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/planner-ir/test-array-ir-unify.yaml @@ -0,0 +1,47 @@ +--- +cases: + - note: ir/unification array and array comprehension + query: "data.test.p = x" + modules: + - | + package test + p = foo { + [foo] = [x | x := 1] + } + want_result: + - x: 1 + - note: ir/unification array comprehension and array + query: "data.test.p = x" + modules: + - | + package test + p = foo { + [x | x := 1] = [foo] + } + want_result: + - x: 1 + - note: ir/fixpoint key/value (negative) + query: "data.test.p = x" + input: + foos: ["foo"] + modules: + - | + package test + p { + some foo + foo == input.foos[foo] + } + want_result: [] + - note: ir/fixpoint key/value + query: "data.test.p = x" + input: + foos: [2, 1, 0] + modules: + - | + package test + p = foo { + some foo + foo == input.foos[foo] + } + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/planner-ir/test-call-dynamic.yaml b/third_party/opa/v1/test/cases/testdata/v0/planner-ir/test-call-dynamic.yaml new file mode 100644 index 000000000000..e0b7a6796329 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/planner-ir/test-call-dynamic.yaml @@ -0,0 +1,94 @@ +--- +cases: + - note: ir/call_dynamic in comprehension + modules: + - | + package test + p := x { + b := input + x := { y | y := data.a[b][_] } + } + - | + package a + b := {"foo", "bar"} + - | + package a + c := {"x", "y" } + query: data.test.p = x + sort_bindings: true + input: "b" + want_result: + - x: + - bar + - foo + - note: ir/no call-dynamic with mixed partial rules + modules: + - | + package test + import future.keywords.if + p := data.a.b[c] if c := "c" + - | + package a + import future.keywords.if + b[c] := "C" if c := "c" + b["d"] := "D" + query: data.test.p = x + want_result: + - x: C + - note: ir/call-dynamic with mixed partial rules + modules: + - | + package test + import future.keywords.if + p := a[b].c if b := "b" + a.b.c := "C" + a.x.d := "D" + query: data.test.p = x + want_result: + - x: C + - note: ir/no call-dynamic with mixed partial rules, ref heads + modules: + - | + package test + import future.keywords.if + p := a.b[c] if c := "c" + a.b[c] := "C" if c := "c" + a.b.d := "D" + query: data.test.p = x + want_result: + - x: C + - note: ir/call-dynamic with mixed partial rules, ref heads + modules: + - | + package test + import future.keywords.if + p := a[b][c] if { b := "b"; c := "c" } + a.b.c := "C" + a.x.d := "D" + query: data.test.p = x + want_result: + - x: C + - note: ir/call-dynamic with ref heads, issue 5839 + modules: + - | + package test + import future.keywords.if + p := a[b][c].allow if { b := "b"; c := "c" } + a.b.c.allow if true + a.x.d.allow if true + a.y[x] := "E" if x := "x" + query: data.test.p = x + want_result: + - x: true + - note: ir/call-dynamic with ref heads, issue 5839, penultimate + modules: + - | + package test + import future.keywords.if + p := a[b][c] if { b := "b"; c := "c" } + a.b.c if true + a.x.d if true + a.y := "E" + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/providers-aws/aws-sign_req-errors.yaml b/third_party/opa/v1/test/cases/testdata/v0/providers-aws/aws-sign_req-errors.yaml new file mode 100644 index 000000000000..f1de0d8b5c8f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/providers-aws/aws-sign_req-errors.yaml @@ -0,0 +1,175 @@ +--- +cases: + # http request object errors: + - data: + modules: + - | + package test + req := {} + aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_session_token": "MYAWSSECURITYTOKENGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + } + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=0047a7016c81e5c7f29cd522f97e911e04fc472fced0aee7916cbc287ad6c8e3", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "http://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/failure-simple-missing http request keys + query: data.test.p = x + want_error_code: eval_type_error + want_error: 'providers.aws.sign_req: operand 1 missing required request parameters(s): {"method", "url"}' + strict_error: true + - data: + modules: + - | + package test + req := {"method": set(), "url": set()} + aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_session_token": "MYAWSSECURITYTOKENGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + } + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=0047a7016c81e5c7f29cd522f97e911e04fc472fced0aee7916cbc287ad6c8e3", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "http://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/failure-simple-invalid type http request keys + query: data.test.p = x + want_error_code: eval_type_error + want_error: 'providers.aws.sign_req: operand 1 invalid values for required request parameters(s): {"method", "url"}' + strict_error: true + # aws config object errors: + - data: + modules: + - | + package test + req := {"method": "get", "url": "http://example.com"} + aws_config := {"example": "example"} + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=0047a7016c81e5c7f29cd522f97e911e04fc472fced0aee7916cbc287ad6c8e3", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "http://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/failure-simple-missing aws keys + query: data.test.p = x + want_error_code: eval_type_error + want_error: 'providers.aws.sign_req: operand 2 missing required AWS config parameters(s): {"aws_access_key", "aws_region", "aws_secret_access_key", "aws_service"}' + strict_error: true + - data: + modules: + - | + package test + req := {"method": "get", "url": "http://example.com"} + aws_config := { + "aws_access_key": 1, + "aws_secret_access_key": 2, + "aws_session_token": 3, + "aws_service": 4, + "aws_region": 5, + } + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=0047a7016c81e5c7f29cd522f97e911e04fc472fced0aee7916cbc287ad6c8e3", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "http://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/failure-simple-invalid type aws keys + query: data.test.p = x + want_error_code: eval_type_error + want_error: 'providers.aws.sign_req: operand 2 invalid values for required AWS config parameters(s): {"aws_access_key", "aws_region", "aws_secret_access_key", "aws_service"}' + strict_error: true + # timestamp errors: + - data: + modules: + - | + package test + req := {"method": "get", "url": "http://example.com"} + aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_session_token": "MYAWSSECURITYTOKENGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + } + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=0047a7016c81e5c7f29cd522f97e911e04fc472fced0aee7916cbc287ad6c8e3", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "http://example.com" + } + p { + providers.aws.sign_req(req, aws_config, -1e9) == expected + } + note: providers-aws-sign_req/failure-simple-bad timestamp + query: data.test.p = x + want_error_code: eval_type_error + want_error: "providers.aws.sign_req: operand 3 could not convert time_ns value into a unix timestamp" + strict_error: true + # boolean type error for disable_payload_signing key + - data: + modules: + - | + package test + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar"}} + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": "false"} + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=8f1dc7c9b9978356a0d0989fd26a95307f4f8a4aa264d8220647b7097d839952", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "https://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/failure-simple-bad-type-payload-signing-config + query: data.test.p = x + want_error_code: eval_type_error + want_error: "providers.aws.sign_req: operand 2 invalid value for 'disable_payload_signing' in AWS config" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/providers-aws/aws-sign_req.yaml b/third_party/opa/v1/test/cases/testdata/v0/providers-aws/aws-sign_req.yaml new file mode 100644 index 000000000000..5622e3ed752c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/providers-aws/aws-sign_req.yaml @@ -0,0 +1,310 @@ +--- +cases: + - data: + modules: + - | + package test + req := {"method": "get", "url": "http://example.com"} + aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + } + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=0047a7016c81e5c7f29cd522f97e911e04fc472fced0aee7916cbc287ad6c8e3", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "http://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/success-simple-no body + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + req := {"method": "get", "url": "http://example.com", "headers": {"foo": "bar"}} + aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + } + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=8f1dc7c9b9978356a0d0989fd26a95307f4f8a4aa264d8220647b7097d839952", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z", + "foo": "bar" + }, + "method": "get", + "url": "http://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/success-simple-with headers no body + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + req := {"method": "get", "url": "http://example.com"} + aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_session_token": "MYAWSSECURITYTOKENGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + } + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token,Signature=23c31bda8a74630c0a94f6b82a3511e7e74728df98e6f54ed0840488dbbdc8d1", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z", + "x-amz-security-token": "MYAWSSECURITYTOKENGOESHERE" + }, + "method": "get", + "url": "http://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/success-simple-no body-with session token + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + req := {"method": "get", "url": "http://example.com", "body": {"example": {1, 2, 3, 4}}} + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1"} + expected := { + "body": {"example": {1, 2, 3, 4}}, + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=5fb06ab1cfd74c8fcb3af95b8cce696708cf6155d971dac10f254d79799c6e88", + "host": "example.com", + "x-amz-content-sha256": "bacff6243c850423883052ac3c336fd645994442933408dfd3f9e858e69bda07", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "http://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/success-simple-body + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + req := {"method": "get", "url": "http://example.com", "raw_body": "{\"example\": {1, 2, 3, 4}}"} + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1"} + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=5bf26e169cb8b02330dba39d53932532438fc856c2f10537689a09ed807c7195", + "host": "example.com", + "x-amz-content-sha256": "22906461e2a98a3e780d0fd260e341bed5e544661e97c5936fc7f3af11aaad8b", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "raw_body": "{\"example\": {1, 2, 3, 4}}", + "url": "http://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/success-simple-raw_body + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + req := {"method": "get", "url": "http://example.com", "body": {"example": {1, 2, 3, 4}}, "raw_body": "{\"example\": {1, 2, 3, 4}}"} + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1"} + expected := { + "body": {"example": {1, 2, 3, 4}}, + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=5bf26e169cb8b02330dba39d53932532438fc856c2f10537689a09ed807c7195", + "host": "example.com", + "x-amz-content-sha256": "22906461e2a98a3e780d0fd260e341bed5e544661e97c5936fc7f3af11aaad8b", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "raw_body": "{\"example\": {1, 2, 3, 4}}", + "url": "http://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/success-simple-body-and-raw_body + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar"}} + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": false} + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=8f1dc7c9b9978356a0d0989fd26a95307f4f8a4aa264d8220647b7097d839952", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "https://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/success-simple-with-headers-no-body-with-payload-signing + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar"}} + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": true} + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=c6e6db654e92172f71e18c714c123711de069ac6fd7df1348e5b28fd05532028", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "UNSIGNED-PAYLOAD", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "https://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/success-simple-with-headers-no-body-no-payload-signing + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar"}, "body": {"example": {1, 2, 3, 4}}} + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": false} + expected := { + "body": {"example": {1, 2, 3, 4}}, + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=f6703a8727ec0a32f81b225a002f622e511e8a7d2ca8914894fcbb7a71d8d9d8", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "bacff6243c850423883052ac3c336fd645994442933408dfd3f9e858e69bda07", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "https://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/success-simple-with-headers-with-body-with-payload-signing + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar"}, "body": {"example": {1, 2, 3, 4}}} + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": true} + expected := { + "body": {"example": {1, 2, 3, 4}}, + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=c6e6db654e92172f71e18c714c123711de069ac6fd7df1348e5b28fd05532028", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "UNSIGNED-PAYLOAD", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "https://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/success-simple-with-headers-with-body-no-payload-signing + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar", "x-amz-content-sha256": "existing-value"}, "body": {"example": {1, 2, 3, 4}}} + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": false} + expected := { + "body": {"example": {1, 2, 3, 4}}, + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=42bcac7fc6d170e09be72fdf38b62e59361265bc59d5f9156f0d6faf889e98ba", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "bacff6243c850423883052ac3c336fd645994442933408dfd3f9e858e69bda07", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "https://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/success-simple-with-existing-sha-header-with-body-with-payload-signing + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar", "x-amz-content-sha256": "existing-value"}, "body": {"example": {1, 2, 3, 4}}} + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": true} + expected := { + "body": {"example": {1, 2, 3, 4}}, + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=edcd3ca3fd3acdfbcecad1a1d8062dbc6562d90352e294c336f9727397f9c383", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "UNSIGNED-PAYLOAD", + "x-amz-date": "20151228T140825Z" + }, + "method": "get", + "url": "https://example.com" + } + p { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + note: providers-aws-sign_req/success-simple-with-existing-sha-header-with-body-no-payload-signing + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/rand/test-rand.intn.yaml b/third_party/opa/v1/test/cases/testdata/v0/rand/test-rand.intn.yaml new file mode 100644 index 000000000000..7b0c7412ea9d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/rand/test-rand.intn.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: + modules: + - | + package test + + p = count(rands) { + rands := { rand.intn("key", 100) | numbers.range(1,100)[_] } + } + note: rand.intn/consistent values for same arguments + query: data.test.p = x + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0322.yaml b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0322.yaml new file mode 100644 index 000000000000..cca19a1fdac7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0322.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + input_term: "{}" + modules: + - | + package generated + + p = __local0__ { + graph.reachable({}, {"a"}, __local1__) + __local0__ = __local1__ + } + note: reachable/empty + query: data.generated.p = x + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0323.yaml b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0323.yaml new file mode 100644 index 000000000000..12f28bdabb53 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0323.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: {} + input_term: "{}" + modules: + - | + package generated + + p = __local0__ { + graph.reachable({ + "a": {"b"}, + "b": {"c"}, + "c": {"a"}, + }, {"a"}, __local1__) + sort(__local1__, __local2__) + __local0__ = __local2__ + } + note: reachable/cycle + query: data.generated.p = x + want_result: + - x: + - a + - b + - c diff --git a/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0324.yaml b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0324.yaml new file mode 100644 index 000000000000..651a5834645c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0324.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: {} + input_term: "{}" + modules: + - | + package generated + + p = __local0__ { + graph.reachable({ + "a": {"b", "c"}, + "b": {"d"}, + "c": {"d"}, + "d": set(), + "e": {"f"}, + "f": {"e"}, + "x": {"x"}, + }, {"b", "e"}, __local1__) + sort(__local1__, __local2__) + __local0__ = __local2__ + } + note: reachable/components + query: data.generated.p = x + want_result: + - x: + - b + - d + - e + - f diff --git a/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0325.yaml b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0325.yaml new file mode 100644 index 000000000000..669ce870d1f7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0325.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: {} + input_term: "{}" + modules: + - | + package generated + + p = __local0__ { + graph.reachable({ + "a": ["b"], + "b": ["c"], + "c": ["a"], + }, ["a"], __local1__) + sort(__local1__, __local2__) + __local0__ = __local2__ + } + note: reachable/arrays + query: data.generated.p = x + want_result: + - x: + - a + - b + - c diff --git a/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0326.yaml b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0326.yaml new file mode 100644 index 000000000000..e8b6ccbc14ad --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0326.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"graph": 1, "initial": [1]}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.graph + __local3__ = input.initial + graph.reachable(__local2__, __local3__, __local1__) + __local0__ = __local1__ + } + note: reachable/malformed 1 + query: data.generated.p = x + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0327.yaml b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0327.yaml new file mode 100644 index 000000000000..bbfc9fa1fbe7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0327.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + input_term: '{"graph": {"a": null}, "initial": ["a"]}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.graph + __local3__ = input.initial + graph.reachable(__local2__, __local3__, __local1__) + __local0__ = __local1__ + } + note: reachable/malformed 2 + query: data.generated.p = x + want_result: + - x: + - a diff --git a/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0328.yaml b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0328.yaml new file mode 100644 index 000000000000..5b02e5b29f3a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-0328.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '{"graph": {"a": []}, "initial": "a"}' + modules: + - | + package generated + + p = __local0__ { + __local2__ = input.graph + __local3__ = input.initial + graph.reachable(__local2__, __local3__, __local1__) + __local0__ = __local1__ + } + note: reachable/malformed 3 + query: data.generated.p = x + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-paths-0422.yaml b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-paths-0422.yaml new file mode 100644 index 000000000000..063d67870e85 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-paths-0422.yaml @@ -0,0 +1,191 @@ +--- +cases: + - data: {} + modules: + - | + package reachable + + p = result { + graph.reachable_paths({}, {"a"}, result) + } + note: reachable_paths/empty + query: data.reachable.p = x + want_result: + - x: [] + - data: {} + input_term: '{ + "graph": { + "a": {"b"}, + "b": {"c"}, + "c": {"a"}, + }, + "initial": {"a"} + }' + modules: + - | + package reachable + + p = result { + graph.reachable_paths(input.graph, input.initial, result) + } + note: reachable_paths/cycle + query: data.reachable.p = x + want_result: + - x: + - - a + - b + - c + - data: {} + input_term: '{ + "graph": { + "a": {"b", "c"}, + "b": {"d"}, + "c": {"d"}, + "d": set(), + "e": {"f"}, + "f": {"e"}, + "x": {"x"}, + }, + "initial": { + "b", "e" + } + }' + modules: + - | + package reachable + + p = result { + graph.reachable_paths(input.graph, input.initial, result) + } + note: reachable_paths/components + query: data.reachable.p = x + want_result: + - x: + - - b + - d + - - e + - f + - data: {} + input_term: '{ + "graph": { + "a": ["b"], + "b": ["c"], + "c": ["a"], + }, + "initial": ["a"] + }' + modules: + - | + package reachable + + p = result { + graph.reachable_paths(input.graph, input.initial, result) + } + note: reachable_paths/arrays + query: data.reachable.p = x + want_result: + - x: + - - a + - b + - c + - data: {} + input_term: '{ + "graph": 1, + "initial": [1] + }' + modules: + - | + package reachable + + p = result { + graph.reachable_paths(input.graph, input.initial, result) + } + note: reachable_paths/malformed 1 + query: data.reachable.p = x + want_error_code: eval_type_error + strict_error: true + - data: {} + input_term: '{ + "graph": { + "a": null + }, + "initial": ["a"] + }' + modules: + - | + package reachable + + p = result { + graph.reachable_paths(input.graph, input.initial, result) + } + note: reachable_paths/malformed 2 + query: data.reachable.p = x + want_result: + - x: + - - a + - data: {} + input_term: '{ + "graph": { + "a": [] + }, + "initial": "a" + }' + modules: + - | + package reachable + + p = result { + graph.reachable_paths(input.graph, input.initial, result) + } + note: reachable_paths/malformed 3 + query: data.reachable.p = x + want_error_code: eval_type_error + strict_error: true + - data: {} + input_term: '{ + "graph": { + "a": ["b", "c"], + "b": ["c"], + "c": [], + }, + "initial": {"a"} + }' + modules: + - | + package reachable + + p = result { + graph.reachable_paths(input.graph, input.initial, result) + } + note: reachable_paths/multiple_paths + query: data.reachable.p = x + want_result: + - x: + - - a + - b + - c + - - a + - c + + - data: {} + input_term: '{ + "graph": { + "a": ["b"], + "b": ["nonexistent"], + }, + "initial": {"a", "b"} + }' + modules: + - | + package reachable + + p = result { + graph.reachable_paths(input.graph, input.initial, result) + } + note: reachable_paths/invalid_end + query: data.reachable.p = x + want_result: + - x: + - - a + - b + - - b diff --git a/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-paths-1022.yaml b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-paths-1022.yaml new file mode 100644 index 000000000000..a409cb5892f7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/reachable/test-reachable-paths-1022.yaml @@ -0,0 +1,129 @@ +--- +cases: + - data: {} + input_term: '{ + "graph": { + "one": ["two","five"], + "two": ["four"], + "three": [""], + "four": ["three"], + "five": ["seven","six"], + "six": ["nine"], + "seven": ["eight"], + "eight": [""], + "nine": [""], + }, + "initial": {"one"} + }' + modules: + - | + package reachable + + p = result { + graph.reachable_paths(input.graph, input.initial, result) + } + note: reachable_paths/cycle_1022_1 + query: data.reachable.p = x + want_result: + - x: + - - one + - five + - seven + - eight + + - - one + - five + - six + - nine + + - - one + - two + - four + - three + + - data: {} + input_term: '{ + "graph": { + "one": {"two","five"}, + "two": {"four"}, + "three": {""}, + "four": {"three"}, + "five": {"seven","six"}, + "six": {"nine"}, + "seven": {"eight"}, + "eight": {""}, + "nine": {""}, + }, + "initial": {"one"} + }' + modules: + - | + package reachable + + p = result { + graph.reachable_paths(input.graph, input.initial, result) + } + note: reachable_paths/cycle_1022_2 + query: data.reachable.p = x + want_result: + - x: + - - one + - five + - seven + - eight + + - - one + - five + - six + - nine + + - - one + - two + - four + - three + + - data: {} + input_term: '{ + "graph": { + "one": ["two","five"], + "two": ["four"], + "three": [""], + "four": ["three"], + "five": ["seven","six"], + "six": ["nine","seven"], + "seven": ["eight"], + "eight": ["three"], + "nine": [""], + }, + "initial": {"one"} + }' + modules: + - | + package reachable + + p = result { + graph.reachable_paths(input.graph, input.initial, result) + } + note: reachable_paths/cycle_1022_3 + query: data.reachable.p = x + want_result: + - x: + - - one + - five + - seven + - eight + - three + + - - one + - five + - six + + - - one + - five + - six + - nine + + - - one + - two + - four + - three diff --git a/third_party/opa/v1/test/cases/testdata/v0/refheads/test-generic-refs.yaml b/third_party/opa/v1/test/cases/testdata/v0/refheads/test-generic-refs.yaml new file mode 100644 index 000000000000..1fa91806d804 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/refheads/test-generic-refs.yaml @@ -0,0 +1,256 @@ +--- +cases: + - note: "refheads/general, single var" + modules: + - | + package test + + p[q].r := i { q := ["a", "b", "c"][i] } + query: data.test.p = x + want_result: + - x: + a: + r: 0 + b: + r: 1 + c: + r: 2 + - note: "refheads/general, multiple vars" + modules: + - | + package test + + p[q][r] { q := ["a", "b", "c"][r] } + query: data.test.p = x + want_result: + - x: + a: + 0: true + b: + 1: true + c: + 2: true + - note: "refheads/general, deep query" + modules: + - | + package test + + p[q][r] { q := ["a", "b", "c"][r] } + query: data.test.p.b = x + want_result: + - x: + 1: true + - note: "refheads/general, overlapping rule, no conflict" + modules: + - | + package test + + p[q].r := i { q := ["a", "b", "c"][i] } + p.a.r := 0 + query: data.test.p = x + want_result: + - x: + a: + r: 0 + b: + r: 1 + c: + r: 2 + - note: "refheads/general, overlapping rule, different dynamic depths, no conflict" + modules: + - | + package test + + p[q].r.s := i { q := ["a", "b", "c"][i] } + p.d.r := 3 + p.e := 4 + query: data.test.p = x + want_result: + - x: + a: + r: + s: 0 + b: + r: + s: 1 + c: + r: + s: 2 + d: + r: 3 + e: 4 + - note: "refheads/general, self-conflict" + modules: + - | + package test + + p[q].r.s := i { q := ["a", "b", "c", "b"][i] } + query: data.test.p = x + want_error_code: eval_conflict_error + - note: "refheads/general, overlapping rule, conflict" + modules: + - | + package test + + p[q].r.s := i { q := ["a", "b", "c"][i] } + p.a.r := 42 + query: data.test.p = x + want_error_code: eval_conflict_error + - note: "refheads/general, overlapping rule, deep override inside other rule object value, conflict" + modules: + - | + package test + + p[q].r := v { + q := "q"; + v := { + "s": { + "t" : 1 + } + } + } + + p.q.r.s.t := 42 + query: data.test.p = x + want_error_code: eval_conflict_error + - note: "refheads/general, overlapping rule, deep injection into other rule object value, conflict" + modules: + - | + package test + + p[q].r := v { + q := "q"; + v := { + "s": { + "t" : 1 + } + } + } + + p.q.r.s.u := 42 + query: data.test.p = x + want_error_code: eval_conflict_error + - note: "refheads/general, set leaf (shallow ref)" + modules: + - | + package test + import future.keywords + + p[q] contains r { + x := ["a", "b", "c"] + q := x[_] + r := x[_] + q != r + } + + p.b contains "foo" + query: data.test.p = x + want_result: + - x: + a: ["b", "c"] + b: ["a", "c", "foo"] + c: ["a", "b"] + - note: "refheads/general, set leaf (other rule defines dynamic ref portion)" + modules: + - | + package test + import future.keywords + + p.q contains r { + r := ["a", "b", "c"][_] + } + + p[q] := r { q := "foo"; r := "bar" } + query: data.test.p = x + want_result: + - x: + q: ["a", "b", "c"] + foo: bar + - note: "refheads/general, set leaf" + modules: + - | + package test + import future.keywords + + p[q].r contains s { + x := ["a", "b", "c"] + q := x[_] + s := x[_] + q != s + } + + p.b.r contains "foo" + query: data.test.p = x + want_result: + - x: + a: + r: ["b", "c"] + b: + r: ["a", "c", "foo"] + c: + r: ["a", "b"] + - note: "refheads/general, set leaf, deep query" + modules: + - | + package test + import future.keywords + + p[q].r contains s { + x := ["a", "b", "c"] + q := x[_] + s := x[_] + q != s + } + + p.b.r contains "foo" + query: data.test.p.b.r.c = x + want_result: + - x: "c" + - note: "refheads/general, input var" + modules: + - | + package test + + p[input.x].r := "foo" + query: data.test.p = x + input: + x: "bar" + want_result: + - x: + bar: + r: "foo" + - note: "refheads/general, external non-ground var" + modules: + - | + package test + + a := [x | x := input.x[_]] + b := input.y + + p[a[b]].r[s] := i { + s := a[i] + } + query: data.test.p = x + input: + x: ["foo", "bar", "baz"] + "y": 1 + want_result: + - x: + bar: + r: + foo: 0 + bar: 1 + baz: 2 + - note: "refheads/general, multiple result-set entries" + modules: + - | + package test + + p.q[r].s := 1 { r := "foo" } + p.q[r].s := 2 { r := "bar" } + query: data.test.p.q[i].s = x + want_result: + - i: foo + x: 1 + - i: bar + x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/refheads/test-refs-as-rule-heads.yaml b/third_party/opa/v1/test/cases/testdata/v0/refheads/test-refs-as-rule-heads.yaml new file mode 100644 index 000000000000..0660a74180d7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/refheads/test-refs-as-rule-heads.yaml @@ -0,0 +1,360 @@ +--- +cases: + - modules: + - | + package test + + p.q.r = 1 + p.q.s = 2 + note: "refheads/single-value" + query: data.test.p = x + want_result: + - x: + q: + r: 1 + s: 2 + - modules: + - | + package test + + p.q.r = 1 + p.q[s] = 2 { s := "s" } + note: "refheads/single-value, with var" + query: data.test.p = x + want_result: + - x: + q: + r: 1 + s: 2 + - note: "refheads/single-value, with var, conflict" + modules: + - | + package test + + p.q.r = 1 + p.q[s] = 2 { s := "r" } + query: data.test.p.q = x + want_error_code: eval_conflict_error + want_error: object keys must be unique + - modules: + - | + package test + + a.b.c.p { true } + note: "refheads/complete: direct query" + query: data.test.a.b.c.p = x + want_result: + - x: true + - modules: + - | + package test + + q = 0 + note: "refheads/complete: direct query q" + query: data.test.q = x + want_result: + - x: 0 + - modules: + - | + package test + + a.b.c.p { true } + note: "refheads/complete: full package extent" + query: data.test = x + want_result: + - x: + a: + b: + c: + p: true + - modules: + - | + package test + + a.b.c.p = 1 + q = 0 + a.b.d = 3 + + p { + q == 0 + a.b.c.p == 1 + a.b.d == 3 + } + note: refheads/complete+mixed + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + a.b[x] = y { x := "c"; y := "d" } + note: refheads/single-value rule + query: data.test.a = x + want_result: + - x: + b: + c: d + - modules: + - | + package test + import future.keywords + + a.b contains x if some x in [1,2,3] + note: refheads/multi-value + query: data.test.a = x + want_result: + - x: + b: [1, 2, 3] + # NOTE(sr): This isn't supported yet + # - modules: + # - | + # package test + # import future.keywords + + # a.b[c] contains x if { c := "c"; some x in [1,2,3] } + # note: refheads/multi-value with var in ref + # query: data.test.a = x + # want_result: + # - x: + # b: + # c: [1, 2, 3] + - modules: + - | + package test + import future.keywords + + a.b[x] = i if some i, x in [1, 2, 3] + note: "refheads/single-value: previously partial object" + query: data.test.a.b = x + want_result: + - x: + 1: 0 + 2: 1 + 3: 2 + - modules: + - | + package test + import future.keywords + + a.b.c.d contains 1 if true + - | + package test.a + import future.keywords + + b.c.d contains 2 if true + note: "refheads/multi-value: same rule" + query: data.test.a = x + want_result: + - x: + b: + c: + d: [1, 2] + - modules: + - | + package test + + default a.b.c := "d" + note: refheads/single-value default rule + query: data.test.a = x + want_result: + - x: + b: + c: d + - modules: + - | + package test + import future.keywords + + q[7] = 8 if true + a[x] if q[x] + note: refheads/single-value example + query: data.test.a = x + want_result: + - x: + 7: true + - modules: + - | + package test + import future.keywords + + q[7] = 8 if false + a[x] if q[x] + note: refheads/single-value example, false + query: data.test.a = x + want_result: + - x: {} + - modules: + - | + package test + import future.keywords + + a.b.c = "d" if true + a.b.e = "f" if true + a.b.g contains x if some x in numbers.range(1, 3) + a.b.h[x] = 1 if x := "one" + note: refheads/mixed example, multiple rules + query: data.test.a.b = x + want_result: + - x: + c: d + e: f + g: + - 1 + - 2 + - 3 + h: + one: 1 + - modules: + - | + package example + import future.keywords + + apps_by_hostname[hostname] := app if { + some i + server := sites[_].servers[_] + hostname := server.hostname + apps[i].servers[_] == server.name + app := apps[i].name + } + + sites := [ + { + "region": "east", + "name": "prod", + "servers": [ + { + "name": "web-0", + "hostname": "hydrogen" + }, + { + "name": "web-1", + "hostname": "helium" + }, + { + "name": "db-0", + "hostname": "lithium" + } + ] + }, + { + "region": "west", + "name": "smoke", + "servers": [ + { + "name": "web-1000", + "hostname": "beryllium" + }, + { + "name": "web-1001", + "hostname": "boron" + }, + { + "name": "db-1000", + "hostname": "carbon" + } + ] + }, + { + "region": "west", + "name": "dev", + "servers": [ + { + "name": "web-dev", + "hostname": "nitrogen" + }, + { + "name": "db-dev", + "hostname": "oxygen" + } + ] + } + ] + + apps := [ + { + "name": "web", + "servers": ["web-0", "web-1", "web-1000", "web-1001", "web-dev"] + }, + { + "name": "mysql", + "servers": ["db-0", "db-1000"] + }, + { + "name": "mongodb", + "servers": ["db-dev"] + } + ] + + containers := [ + { + "image": "redis", + "ipaddress": "10.0.0.1", + "name": "big_stallman" + }, + { + "image": "nginx", + "ipaddress": "10.0.0.2", + "name": "cranky_euclid" + } + ] + note: refheads/website-example/partial-obj + query: data.example.apps_by_hostname.helium = x + want_result: + - x: web + - modules: + - | + package example + import future.keywords + + public_network contains net.id if { + some net in input.networks + net.public + } + note: refheads/website-example/partial-set + query: data.example.public_network = x + input: + networks: + - id: n1 + public: true + - id: n2 + public: false + want_result: + - x: + - n1 + - modules: + - | + package test + import future.keywords + + p[a][b][c][d][e] if { + some a in numbers.range(1, 5) + some b in numbers.range(1, 5) + some c in numbers.range(1, 5) + some d in numbers.range(1, 5) + some e in numbers.range(1, 5) + a+b+c+d+e == 24 + } + note: refheads/many-vars + query: data.test.p = x + want_result: + - x: + 4: + 5: + 5: + 5: + 5: true + 5: + 4: + 5: + 5: + 5: true + 5: + 4: + 5: + 5: true + 5: + 4: + 5: true + 5: + 4: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/refheads/test-regressions.yaml b/third_party/opa/v1/test/cases/testdata/v0/refheads/test-regressions.yaml new file mode 100644 index 000000000000..b73c0c52f26b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/refheads/test-regressions.yaml @@ -0,0 +1,155 @@ +--- +# NOTE(sr): These tests are not really related to ref heads, but collection +# regressions found when introducing ref heads. +cases: + - note: regression/ref-not-hashable + modules: + - | + package test + + ms[m.z] = m { + m := input.xs[y] + } + input: + xs: + something: + z: a + query: data.test = x + want_result: + - x: + ms: + a: + z: a + - note: regression/function refs and package extent + modules: + - | + package test + import future.keywords.if + + foo.bar(x) = x+1 + x := foo.bar(2) + query: data.test = x + want_result: + - x: + foo: {} + x: 3 + + - note: regression/rule refs and package extent + modules: + - | + package test + import future.keywords.if + + buz.quz = 3 if input == 3 + x := y { + y := buz.quz with input as 3 + } + query: data.test = x + want_result: + - x: + buz: {} + x: 3 + - note: regression/rule refs and package extents, multiple modules + modules: + - | + package test + import future.keywords.if + + x := y { + y := data.test.buz.quz with input as 3 + } + - | + package test.buz + import future.keywords.if + + quz = 3 if input == 3 + query: data.test = x + want_result: + - x: + buz: {} + x: 3 + - note: regression/type checking with ref rules + modules: + - | + package test + all[0] = [2] + level := 1 + + p := y { y := all[level-1][_] } + query: data.test.p = x + want_result: + - x: 2 + - note: regression/type checking with ref rules, number + modules: + - | + package test + p[0] = 1 + query: "data.test.p[0] = x" + want_result: + - x: 1 + - note: regression/type checking with ref rules, bool + modules: + - | + package test + p[true] = 1 + query: "data.test.p[true] = x" + want_result: + - x: 1 + - note: regression/full extent with partial object rule with empty indexer lookup result + modules: + - | + package test + p[x] = 2 { + x := input # we'll get 0 rules for data.test.p + false + } + query: data.test = x + want_result: + - x: + p: {} + - note: regression/obj in ref head query + modules: + - | + package test + p[{"a": "b"}] = true + query: data.test.p[{"a":"b"}] = x + want_result: + - x: true + - note: regression/full extent with non-string (number) last term + modules: + - | + package test + p[0] = true + query: data.test = x + want_result: + - x: + p: + "0": true # stringified key + - note: regression/full extent with non-string last term, comparison + modules: + - | + package test + p[0] = 1 + q { p[0] == 1 } + query: data.test.q = x + want_result: + - x: true + - note: regression/full extent with non-string (boolean) last term + modules: + - | + package test + p[true] = true + query: data.test = x + want_result: + - x: + p: + "true": true # stringified key + - note: regression/full extent with non-string last term, comparison + modules: + - | + package test + p[true] = false + q { p[true] == false } + query: data.test.q = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexfind/test-regexfind-0334.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexfind/test-regexfind-0334.yaml new file mode 100644 index 000000000000..0dd9d44c29a9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexfind/test-regexfind-0334.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + regex.find_n("a.", "paranormal", -1, __local0__) + x = __local0__ + } + note: regexfind/finds all match values + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - ar + - an + - al diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexfind/test-regexfind-0335.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexfind/test-regexfind-0335.yaml new file mode 100644 index 000000000000..290c29e2e295 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexfind/test-regexfind-0335.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + regex.find_n("a.", "paranormal", 2, __local0__) + x = __local0__ + } + note: regexfind/finds specified number of match values + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - ar + - an diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexfind/test-regexfind-0336.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexfind/test-regexfind-0336.yaml new file mode 100644 index 000000000000..8fb5473996a2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexfind/test-regexfind-0336.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + regex.find_n("bork", "paranormal", -1, __local0__) + x = __local0__ + } + note: regexfind/finds no matching values + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0337.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0337.yaml new file mode 100644 index 000000000000..747bc986e0ce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0337.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + regex.find_all_string_submatch_n("a(x*)b", "-", -1, __local0__) + x = __local0__ + } + note: regexfindallstringsubmatch/finds no matches + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0338.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0338.yaml new file mode 100644 index 000000000000..24cb6c78f080 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0338.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + regex.find_all_string_submatch_n("a(x*)b", "-ab-", -1, __local0__) + x = __local0__ + } + note: regexfindallstringsubmatch/single match without captures + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - - ab + - "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0339.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0339.yaml new file mode 100644 index 000000000000..ef04858cdd25 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0339.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + regex.find_all_string_submatch_n("a(x*)b", "-axxb-", -1, __local0__) + x = __local0__ + } + note: regexfindallstringsubmatch/single match with a capture + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - - axxb + - xx diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0340.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0340.yaml new file mode 100644 index 000000000000..84ec46154b0f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0340.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + regex.find_all_string_submatch_n("a(x*)b", "-ab-axb-", -1, __local0__) + x = __local0__ + } + note: regexfindallstringsubmatch/multiple matches with captures-1 + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - - ab + - "" + - - axb + - x diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0341.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0341.yaml new file mode 100644 index 000000000000..fb4e2099cb55 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0341.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + regex.find_all_string_submatch_n("a(x*)b", "-axxb-ab-", -1, __local0__) + x = __local0__ + } + note: regexfindallstringsubmatch/multiple matches with captures-2 + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - - axxb + - xx + - - ab + - "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0342.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0342.yaml new file mode 100644 index 000000000000..efb20173c693 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0342.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + regex.find_all_string_submatch_n("[^aouiye]([aouiye])([^aouiye])?", "somestri", -1, __local0__) + x = __local0__ + } + note: regexfindallstringsubmatch/multiple patterns, matches, and captures + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - - som + - o + - m + - - ri + - i + - "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0343.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0343.yaml new file mode 100644 index 000000000000..ef735669923e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0343.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: + modules: + - | + package generated + + p[x] { + regex.find_all_string_submatch_n("[^aouiye]([aouiye])([^aouiye])?", "somestri", 1, __local0__) + x = __local0__ + } + note: regexfindallstringsubmatch/multiple patterns, matches, and captures with specified number of matches + query: data.generated.p = x + want_result: + - x: + - - - som + - o + - m diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-large-input.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-large-input.yaml new file mode 100644 index 000000000000..87a33746d26d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexfindallstringsubmatch/test-regexfindallstringsubmatch-large-input.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: + input: + long: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabc" + modules: + - | + package test + import future.keywords + + p contains m if some m in regex.find_all_string_submatch_n("^.*$", input.long, -1) + note: regexfindallstringsubmatch/large input + query: data.test.p = x + want_result: + - x: + - ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabc"] diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexisvalid/test-regexisvalid-0329.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexisvalid/test-regexisvalid-0329.yaml new file mode 100644 index 000000000000..b08c124257cc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexisvalid/test-regexisvalid-0329.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: + num: 10 + modules: + - | + package generated + + p = x { + __local0__ = data.num + regex.is_valid(__local0__, x) + } + note: regexisvalid/bad operand type + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexisvalid/test-regexisvalid-0330.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexisvalid/test-regexisvalid-0330.yaml new file mode 100644 index 000000000000..bcb5973e9cf1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexisvalid/test-regexisvalid-0330.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + regex.is_valid(`++`, x) + } + note: regexisvalid/bad pattern + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexisvalid/test-regexisvalid-0331.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexisvalid/test-regexisvalid-0331.yaml new file mode 100644 index 000000000000..dd495baf6f67 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexisvalid/test-regexisvalid-0331.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + regex.is_valid(`.+`, x) + } + note: regexisvalid/good pattern + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0855.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0855.yaml new file mode 100644 index 000000000000..b66b7c775dc9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0855.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + re_match("^[a-z]+\\[[0-9]+\\]$", "foo[1]") + } + note: regexmatch/re_match + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0856.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0856.yaml new file mode 100644 index 000000000000..c508256785c0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0856.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + re_match("^[a-z]+\\[[0-9]+\\]$", "foo[\"bar\"]") + } + note: "regexmatch/re_match: undefined" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0857.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0857.yaml new file mode 100644 index 000000000000..4afde0c3b1f2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0857.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p { + re_match("][", "foo[\"bar\"]") + } + note: "regexmatch/re_match: bad pattern err" + query: data.generated.p = x + want_error: "re_match: error parsing regexp: missing closing ]: `[`" + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0858.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0858.yaml new file mode 100644 index 000000000000..aba669066c08 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0858.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + d: + e: + - bar + - baz + modules: + - | + package generated + + p[x] { + __local0__ = data.d.e[x] + re_match("^b.*$", __local0__) + } + note: "regexmatch/re_match: ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 0 + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0859.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0859.yaml new file mode 100644 index 000000000000..9777e2cadcf3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0859.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + re_match(`^[a-z]+\[[0-9]+\]$`, "foo[1]") + } + note: "regexmatch/re_match: raw" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0860.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0860.yaml new file mode 100644 index 000000000000..db3c524e2abb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0860.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + re_match(`^[a-z]+\[[0-9]+\]$`, "foo[\"bar\"]") + } + note: "regexmatch/re_match: raw: undefined" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0861.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0861.yaml new file mode 100644 index 000000000000..c73fcd6de18b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexmatch/test-regexmatch-0861.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + regex.match("^[a-z]+\\[[0-9]+\\]$", "foo[1]") + } + note: regexmatch/regex.match + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexmatchtemplate/test-regexmatchtemplate-0332.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexmatchtemplate/test-regexmatchtemplate-0332.yaml new file mode 100644 index 000000000000..f68d79c9fa28 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexmatchtemplate/test-regexmatchtemplate-0332.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + regex.template_match("urn:foo:{.*}", "urn:foo:bar:baz", "{", "}", x) + } + note: regexmatchtemplate/matches wildcard with {} + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexmatchtemplate/test-regexmatchtemplate-0333.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexmatchtemplate/test-regexmatchtemplate-0333.yaml new file mode 100644 index 000000000000..0fcbc15d540a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexmatchtemplate/test-regexmatchtemplate-0333.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + regex.template_match("urn:foo:<.*>", "urn:foo:bar:baz", "<", ">", x) + } + note: regexmatchtemplate/matches wildcard with <> + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexreplace/test-regexreplace-0001.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexreplace/test-regexreplace-0001.yaml new file mode 100644 index 000000000000..acd197da2e1f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexreplace/test-regexreplace-0001.yaml @@ -0,0 +1,40 @@ +--- +cases: + - data: {} + modules: + - | + package test + + p = x { + s := "-wy-wxxy-" + x := regex.replace(s, "w(x*)y", "0") + } + note: "regex.replace: test pattern match and replace" + query: data.test.p = x + want_result: + - x: -0-0- + - data: {} + modules: + - | + package test + + p = x { + s := "foo" + x := regex.replace(s, "(foo)", "$1$1") + } + note: "regex.replace: work with groups" + query: data.test.p = x + want_result: + - x: foofoo + - data: {} + modules: + - | + package test + + p = x { + s := "foo" + x := regex.replace(s, "[", "$1") + } + note: "regex.replace: bad regex pattern: Syntax error" + query: data.test.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexsplit/test-regexsplit-0862.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexsplit/test-regexsplit-0862.yaml new file mode 100644 index 000000000000..4aca4ce69d38 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexsplit/test-regexsplit-0862.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + regex.split("^[a-z]+\\[[0-9]+\\]$", "", [x]) + } + note: "regexsplit/regex.split: empty string" + query: data.generated.p = x + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexsplit/test-regexsplit-0863.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexsplit/test-regexsplit-0863.yaml new file mode 100644 index 000000000000..919b480958a4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexsplit/test-regexsplit-0863.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [v, w, x, y] { + regex.split("a", "banana", [v, w, x, y]) + } + note: "regexsplit/regex.split: non-repeat pattern" + query: data.generated.p = x + want_result: + - x: + - b + - "n" + - "n" + - "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/regexsplit/test-regexsplit-0864.yaml b/third_party/opa/v1/test/cases/testdata/v0/regexsplit/test-regexsplit-0864.yaml new file mode 100644 index 000000000000..d8e4b50894d5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regexsplit/test-regexsplit-0864.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [v, w] { + regex.split("z+", "pizza", [v, w]) + } + note: "regexsplit/regex.split: repeat pattern" + query: data.generated.p = x + want_result: + - x: + - pi + - a diff --git a/third_party/opa/v1/test/cases/testdata/v0/regometadatachain/test-regometadatachain-1.yaml b/third_party/opa/v1/test/cases/testdata/v0/regometadatachain/test-regometadatachain-1.yaml new file mode 100644 index 000000000000..c8f98ff86984 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regometadatachain/test-regometadatachain-1.yaml @@ -0,0 +1,99 @@ +--- +cases: + - data: + note: regometadatachain/simple + modules: + - | + # METADATA + # description: The Rego test suite + package testing + + # METADATA + # title: Testing annotations + # authors: + # - The OPA contributors + p := x { + x := rego.metadata.chain() + } + query: data.testing.p = x + want_result: + - x: + - annotations: + authors: + - name: The OPA contributors + scope: rule + title: Testing annotations + path: + - testing + - p + - annotations: + description: The Rego test suite + scope: package + path: + - testing + - data: + note: regometadatachain/rule mixed scope + modules: + - | + # METADATA + # description: The Rego test suite + package testing + + # METADATA + # scope: document + # title: Testing annotations + # authors: + # - The OPA contributors + p := "foo" { + false + } + + # METADATA + # title: Another annotation + p := x { + x := rego.metadata.chain() + } + query: data.testing.p = x + want_result: + - x: + - annotations: + scope: rule + title: Another annotation + path: + - testing + - p + - annotations: + authors: + - name: The OPA contributors + scope: document + title: Testing annotations + path: + - testing + - p + - annotations: + description: The Rego test suite + scope: package + path: + - testing + - data: + note: regometadatachain/package spanning modules + modules: + - | + # METADATA + # description: A set of package annotations seen across multiple modules + package testing + - | + package testing + + p := rego.metadata.chain() + query: data.testing.p = x + want_result: + - x: + - path: + - testing + - p + - path: + - testing + annotations: + scope: package + description: A set of package annotations seen across multiple modules diff --git a/third_party/opa/v1/test/cases/testdata/v0/regometadatarule/test-regometadatarule-1.yaml b/third_party/opa/v1/test/cases/testdata/v0/regometadatarule/test-regometadatarule-1.yaml new file mode 100644 index 000000000000..ce5444fd02b4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regometadatarule/test-regometadatarule-1.yaml @@ -0,0 +1,50 @@ +--- +cases: + - data: + modules: + - | + # METADATA + # description: The Rego test suite + package testing + + # METADATA + # title: Testing annotations + # authors: + # - The OPA contributors + p := x { + x := rego.metadata.rule() + } + note: regometadatarule/simple + query: data.testing.p = x + want_result: + - x: + title: Testing annotations + authors: + - name: The OPA contributors + scope: rule + - data: + modules: + - | + # METADATA + # description: The Rego test suite + package testing + + # METADATA + # title: Testing annotations + # authors: + # - The OPA contributors + p := "foo" { + false + } + + # METADATA + # title: Another annotation + p := x { + x := rego.metadata.rule() + } + note: regometadatarule/rule scope only + query: data.testing.p = x + want_result: + - x: + title: Another annotation + scope: rule diff --git a/third_party/opa/v1/test/cases/testdata/v0/regoparsemodule/test-regoparsemodule-0320.yaml b/third_party/opa/v1/test/cases/testdata/v0/regoparsemodule/test-regoparsemodule-0320.yaml new file mode 100644 index 000000000000..2ad66da1b79d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regoparsemodule/test-regoparsemodule-0320.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: + ok: |- + package foo.bar + + import rego.v1 + import data.a + + p if { a = true } + modules: + - | + package generated + + p = x { + __local0__ = data.ok + rego.parse_module("x.rego", __local0__, module) + x = module["package"].path[1].value + } + note: regoparsemodule/ok + query: data.generated.p = x + want_result: + - x: foo diff --git a/third_party/opa/v1/test/cases/testdata/v0/regoparsemodule/test-regoparsemodule-0321.yaml b/third_party/opa/v1/test/cases/testdata/v0/regoparsemodule/test-regoparsemodule-0321.yaml new file mode 100644 index 000000000000..0920c2dee4cc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/regoparsemodule/test-regoparsemodule-0321.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: + err: package foo. + modules: + - | + package generated + + p = x { + __local0__ = data.err + rego.parse_module("x.rego", __local0__, x) + } + note: regoparsemodule/error + query: data.generated.p = x + strict_error: true + want_error: "rego_parse_error: unexpected eof token: expected ident" + want_error_code: eval_builtin_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/rendertemplate/rendertemplate.yaml b/third_party/opa/v1/test/cases/testdata/v0/rendertemplate/rendertemplate.yaml new file mode 100644 index 000000000000..76fbdf52a39a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/rendertemplate/rendertemplate.yaml @@ -0,0 +1,49 @@ +--- +cases: + - note: rendertemplate/simple + query: data.test.p = x + modules: + - | + package test + + template_string = `{{.test}}` + template_vars = {`test`: `hello world`} + p = strings.render_template(template_string, template_vars) + want_result: + - x: "hello world" + + - note: rendertemplate/simpleint + query: data.test.p = x + modules: + - | + package test + + template_string = `{{.test}}` + template_vars = {`test`: 2023} + p = strings.render_template(template_string, template_vars) + want_result: + - x: "2023" + + - note: rendertemplate/complex + query: data.test.p = x + modules: + - | + package test + + template_string = `{{range $i, $name := .hellonames}}{{if $i}},{{end}}hello {{$name}}{{end}}` + template_vars = {`hellonames`: [`rohan`, `john doe`]} + p = strings.render_template(template_string, template_vars) + want_result: + - x: "hello rohan,hello john doe" + + - note: rendertemplate/missingkey + query: data.test.p = x + modules: + - | + package test + + template_string = `{{.testvarnotprovided}}` + template_vars = {`test`: `hello world`} + p = strings.render_template(template_string, template_vars) + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/replacen/test-replacen-0374.yaml b/third_party/opa/v1/test/cases/testdata/v0/replacen/test-replacen-0374.yaml new file mode 100644 index 000000000000..541eeffd3f58 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/replacen/test-replacen-0374.yaml @@ -0,0 +1,41 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + strings.replace_n({"<": "<", ">": ">"}, "This is HTML!", __local0__) + x = __local0__ + } + note: replacen/replace multiple patterns + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - This is <b>HTML</b>! + - data: {} + modules: + - | + package test + + p = strings.replace_n({"f": "x", "foo": "xxx"}, "foobar") + note: replacen/replace multiple patterns/overlapping + query: data.test.p = x + want_result: + - x: xoobar + - data: {} + modules: + - | + package test + + p = x { + x := strings.replace_n({ k: v | k := ["f", "foo"][i]; v := ["x", "xxx"][i]}, "foo") + y := strings.replace_n({ k: v | k := ["foo", "f"][i]; v := ["xxx", "x"][i]}, "foo") + x == y + } + note: replacen/replace multiple patterns/overlapping/insertion order does not matter + query: data.test.p = x + want_result: + - x: xoo diff --git a/third_party/opa/v1/test/cases/testdata/v0/replacen/test-replacen-0375.yaml b/third_party/opa/v1/test/cases/testdata/v0/replacen/test-replacen-0375.yaml new file mode 100644 index 000000000000..4dacd53549de --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/replacen/test-replacen-0375.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + strings.replace_n({"old1": "new1", "old2": "new2"}, "Everything is new1, new2", __local0__) + x = __local0__ + } + note: replacen/find no patterns + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - Everything is new1, new2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/replacen/test-replacen-bad-operands.yaml b/third_party/opa/v1/test/cases/testdata/v0/replacen/test-replacen-bad-operands.yaml new file mode 100644 index 000000000000..d25c90c40cb0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/replacen/test-replacen-bad-operands.yaml @@ -0,0 +1,38 @@ +--- +cases: + - data: null + modules: + - | + package test + + p = strings.replace_n({2: "x" | true}, "foo") + note: replacen/bad pattern object operand/non-string key + query: data.test.p = x + strict_error: true + want_error: "strings.replace_n: operand 1 non-string key found in pattern object" + want_error_code: eval_type_error + - data: + pattern: + f: 100 + modules: + - | + package test + + p = strings.replace_n(data.pattern, "foo") + note: replacen/bad pattern object operand/non-string value + query: data.test.p = x + strict_error: true + want_error: "strings.replace_n: operand 1 non-string value found in pattern object" + want_error_code: eval_type_error + - data: + string: 100 + modules: + - | + package test + + p = strings.replace_n({"foo":"baz"}, data.string) + note: replacen/bad pattern object operand/non-string value + query: data.test.p = x + strict_error: true + want_error: "strings.replace_n: operand 2 must be string but got number" + want_error_code: eval_type_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0344.yaml b/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0344.yaml new file mode 100644 index 000000000000..262248206191 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0344.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + semver.compare("1.0.0", "2.0.0", __local0__) + x = __local0__ + } + note: semvercompare/a < b + query: data.generated.p = x + want_result: + - x: -1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0345.yaml b/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0345.yaml new file mode 100644 index 000000000000..ded41df4ce01 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0345.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + semver.compare("2.0.0", "1.0.0", __local0__) + x = __local0__ + } + note: semvercompare/a > b + query: data.generated.p = x + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0346.yaml b/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0346.yaml new file mode 100644 index 000000000000..2b6d2b8dc120 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0346.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + semver.compare("1.0.0", "1.0.0", __local0__) + x = __local0__ + } + note: semvercompare/a == b + query: data.generated.p = x + want_result: + - x: 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0347.yaml b/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0347.yaml new file mode 100644 index 000000000000..a376df34d0c2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0347.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + semver.compare("1", "1.0.0", __local0__) + x = __local0__ + } + note: semvercompare/invalid version a + query: data.generated.p = x + want_error: 'semver.compare: operand 1: string "1" is not a valid SemVer' + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0348.yaml b/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0348.yaml new file mode 100644 index 000000000000..410b3f583740 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/semvercompare/test-semvercompare-0348.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + semver.compare("1.0.0", "1", __local0__) + x = __local0__ + } + note: semvercompare/invalid version b + query: data.generated.p = x + want_error: 'semver.compare: operand 2: string "1" is not a valid SemVer' + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/semverisvalid/test-semverisvalid-0349.yaml b/third_party/opa/v1/test/cases/testdata/v0/semverisvalid/test-semverisvalid-0349.yaml new file mode 100644 index 000000000000..8ead3d6509c4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/semverisvalid/test-semverisvalid-0349.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + semver.is_valid("1.0.0", __local0__) + x = __local0__ + } + note: semverisvalid/valid + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/semverisvalid/test-semverisvalid-0350.yaml b/third_party/opa/v1/test/cases/testdata/v0/semverisvalid/test-semverisvalid-0350.yaml new file mode 100644 index 000000000000..9a5a12338871 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/semverisvalid/test-semverisvalid-0350.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + semver.is_valid("1", __local0__) + x = __local0__ + } + note: semverisvalid/invalid version + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/semverisvalid/test-semverisvalid-0351.yaml b/third_party/opa/v1/test/cases/testdata/v0/semverisvalid/test-semverisvalid-0351.yaml new file mode 100644 index 000000000000..7770dc0c9108 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/semverisvalid/test-semverisvalid-0351.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + semver.is_valid(1, __local0__) + x = __local0__ + } + note: semverisvalid/invalid type + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0871.yaml b/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0871.yaml new file mode 100644 index 000000000000..4a1e14bdfff2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0871.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + s1 = {1, 2, 3, 4} + s2 = {1, 3} + __local0__ = s1 - s2 + x = __local0__ + } + note: sets/set_diff + query: data.generated.p = x + want_result: + - x: + - 2 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0872.yaml b/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0872.yaml new file mode 100644 index 000000000000..3f638e4967f2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0872.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = x { + __local0__ = data.a[0] + __local1__ = data.a[1] + __local2__ = data.a[2] + s1 = {__local0__, __local1__, __local2__} + __local3__ = data.a[0] + s2 = {2, __local3__} + set_diff(s1, s2, x) + } + note: "sets/set_diff: refs" + query: data.generated.p = x + want_result: + - x: + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0873.yaml b/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0873.yaml new file mode 100644 index 000000000000..811a4b364b3e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0873.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + __local0__ = {1, 2, 3} - {2, 3} + {1} = __local0__ + } + note: "sets/set_diff: ground output" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0874.yaml b/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0874.yaml new file mode 100644 index 000000000000..fd5474efd164 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0874.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + __local1__ = data.generated.s1 + __local2__ = data.generated.s2 + __local0__ = __local1__ - __local2__ + x = __local0__ + } + + s1[1] + + s1[2] + + s1["c"] + + s2 = {"c", 1} + note: "sets/set_diff: virt docs" + query: data.generated.p = x + want_result: + - x: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0875.yaml b/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0875.yaml new file mode 100644 index 000000000000..2651145c7e9c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0875.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = x { + __local1__ = data.a[1] + __local2__ = data.a[2] + __local3__ = data.a[2] + __local0__ = {3, __local1__, __local2__} & {3, 4, __local3__} + x = __local0__ + } + note: sets/intersect + query: data.generated.p = x + want_result: + - x: + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0876.yaml b/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0876.yaml new file mode 100644 index 000000000000..5c8596403cd0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/sets/test-sets-0876.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + __local1__ = data.a[1] + __local2__ = data.a[2] + __local3__ = data.a[2] + __local0__ = {3, __local1__, __local2__} | {3, 4, __local3__} + {2, 3, 4} = __local0__ + } + note: sets/union + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/sprintf/test-sprintf.yaml b/third_party/opa/v1/test/cases/testdata/v0/sprintf/test-sprintf.yaml new file mode 100644 index 000000000000..08399d6e465f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/sprintf/test-sprintf.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + modules: + - | + package test + + p = x { + x = sprintf("%s", [123456789123456789123]) + } + note: sprintf/big_int + query: data.test.p = x + want_result: + - x: "123456789123456789123" + - data: + modules: + - | + package test + + p = x { + x = sprintf("%s", [1208925819614629174706175]) + } + note: sprintf/big_int/max_cert_serial_number + query: data.test.p = x + want_result: + - x: "1208925819614629174706175" diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-anyprefixmatch.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-anyprefixmatch.yaml new file mode 100644 index 000000000000..371a3002aa0f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-anyprefixmatch.yaml @@ -0,0 +1,341 @@ +--- +cases: + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: ["a/", "d/"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: ["f/", "d/"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: ["a/b/", "a/"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: ["d/", "e/f/g"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + strings: ["aa/bb/cc", "aa/bb/dd", "ee/ff/gg"] + prefixes: ["aa/b", "e/f"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + strings: ["aa/bb/cc", "aa/bb/dd", "ee/ff/gg"] + prefixes: ["a/b", "e/f"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: ["aa/bb/cc", "aa/bb/dd", "ee/ff/gg"] + prefixes: ["b/cc"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: [] + prefixes: ["a/b", "e/f"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: [] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: [] + prefixes: [] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefix: "a/" + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefix) + } + note: strings/any_prefix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefix: "d/" + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefix) + } + note: strings/any_prefix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + string: "a/b/c" + prefixes: ["a/", "d/"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.string, input.prefixes) + } + note: strings/any_prefix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + string: "a/b/c" + prefixes: ["g/", "d/"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.string, input.prefixes) + } + note: strings/any_prefix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: ["a/", "d/"] + modules: + - | + package test + + strings_set[str] { + str := input.strings[_] + } + + prefixes_set[prefix] { + prefix := input.prefixes[_] + } + + p { + strings.any_prefix_match(strings_set, prefixes_set) + } + note: strings/any_prefix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: ["f/", "d/"] + modules: + - | + package test + + strings_set[str] { + str := input.strings[_] + } + + prefixes_set[prefix] { + prefix := input.prefixes[_] + } + + p { + strings.any_prefix_match(strings_set, prefixes_set) + } + note: strings/any_prefix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: [1, 2, 3] + prefixes: ["f/", "d/"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/type_error_nostrict + query: data.test.p = x + want_result: [] + - input: + strings: 1 + prefixes: ["f/", "d/"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/type_error_nostrict + query: data.test.p = x + want_result: [] + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: [1, 2] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/type_error_nostrict + query: data.test.p = x + want_result: [] + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: 1 + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/type_error_nostrict + query: data.test.p = x + want_result: [] + - input: + strings: [1, 2, 3] + prefixes: ["f/", "d/"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/type_error_strict + query: data.test.p = x + strict_error: true + want_error: "test-0.rego:4: eval_type_error: strings.any_prefix_match: operand 1 must be array of strings but got array containing number" + - input: + strings: 1 + prefixes: ["f/", "d/"] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/type_error_strict + query: data.test.p = x + strict_error: true + want_error: "test-0.rego:4: eval_type_error: strings.any_prefix_match: operand 1 must be one of {string, set, array} but got number" + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: [1, 2] + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/type_error_strict + query: data.test.p = x + strict_error: true + want_error: "test-0.rego:4: eval_type_error: strings.any_prefix_match: operand 2 must be array of strings but got array containing number" + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: 1 + modules: + - | + package test + + p { + strings.any_prefix_match(input.strings, input.prefixes) + } + note: strings/any_prefix_match/type_error_strict + query: data.test.p = x + strict_error: true + want_error: "test-0.rego:4: eval_type_error: strings.any_prefix_match: operand 2 must be one of {string, set, array} but got number" diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-anysuffixmatch.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-anysuffixmatch.yaml new file mode 100644 index 000000000000..685952a7b796 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-anysuffixmatch.yaml @@ -0,0 +1,341 @@ +--- +cases: + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + suffixes: ["/c", "/a"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.suffixes) + } + note: strings/any_suffix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + suffixes: ["/f", "/a"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.suffixes) + } + note: strings/any_suffix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + suffixes: ["/b/c", "/d"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.suffixes) + } + note: strings/any_suffix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + suffixes: ["/a", "e/f/g"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.suffixes) + } + note: strings/any_suffix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + strings: ["aa/bb/cc", "aa/bb/dd", "ee/ff/gg"] + suffixes: ["b/cc", "f/g"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.suffixes) + } + note: strings/any_suffix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + strings: ["aa/bb/cc", "aa/bb/dd", "ee/ff/gg"] + suffixes: ["b/c", "f/g"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.suffixes) + } + note: strings/any_suffix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: ["aa/bb/cc", "aa/bb/dd", "ee/ff/gg"] + suffixes: ["aa/b"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.suffixes) + } + note: strings/any_suffix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: [] + suffixes: ["a/b", "e/f"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.suffixes) + } + note: strings/any_suffix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + suffixes: [] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.suffixes) + } + note: strings/any_suffix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: [] + suffixes: [] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.suffixes) + } + note: strings/any_suffix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + suffix: "/c" + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.suffix) + } + note: strings/any_suffix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + suffix: "/h" + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.suffix) + } + note: strings/any_suffix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + string: "a/b/c" + suffixes: ["/c", "/a"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.string, input.suffixes) + } + note: strings/any_suffix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + string: "a/b/g" + suffixes: ["/c", "/a"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.string, input.suffixes) + } + note: strings/any_suffix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + suffixes: ["/c", "/a"] + modules: + - | + package test + + strings_set[str] { + str := input.strings[_] + } + + suffixes_set[suffix] { + suffix := input.suffixes[_] + } + + p { + strings.any_suffix_match(strings_set, suffixes_set) + } + note: strings/any_suffix_match/match + query: data.test.p = x + want_result: + - x: true + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + suffixes: ["/f", "/a"] + modules: + - | + package test + + strings_set[str] { + str := input.strings[_] + } + + suffixes_set[suffix] { + suffix := input.suffixes[_] + } + + p { + strings.any_suffix_match(strings_set, suffixes_set) + } + note: strings/any_suffix_match/nomatch + query: data.test.p = x + want_result: [] + - input: + strings: [1, 2, 3] + prefixes: ["f/", "d/"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.prefixes) + } + note: strings/any_suffix_match/type_error_nostrict + query: data.test.p = x + want_result: [] + - input: + strings: 1 + prefixes: ["f/", "d/"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.prefixes) + } + note: strings/any_suffix_match/type_error_nostrict + query: data.test.p = x + want_result: [] + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: [1, 2] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.prefixes) + } + note: strings/any_suffix_match/type_error_nostrict + query: data.test.p = x + want_result: [] + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: 1 + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.prefixes) + } + note: strings/any_suffix_match/type_error_nostrict + query: data.test.p = x + want_result: [] + - input: + strings: [1, 2, 3] + prefixes: ["f/", "d/"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.prefixes) + } + note: strings/any_suffix_match/type_error_strict + query: data.test.p = x + strict_error: true + want_error: "test-0.rego:4: eval_type_error: strings.any_suffix_match: operand 1 must be array of strings but got array containing number" + - input: + strings: 1 + prefixes: ["f/", "d/"] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.prefixes) + } + note: strings/any_suffix_match/type_error_strict + query: data.test.p = x + strict_error: true + want_error: "test-0.rego:4: eval_type_error: strings.any_suffix_match: operand 1 must be one of {string, set, array} but got number" + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: [1, 2] + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.prefixes) + } + note: strings/any_suffix_match/type_error_strict + query: data.test.p = x + strict_error: true + want_error: "test-0.rego:4: eval_type_error: strings.any_suffix_match: operand 2 must be array of strings but got array containing number" + - input: + strings: ["a/b/c", "a/b/d", "e/f/g"] + prefixes: 1 + modules: + - | + package test + + p { + strings.any_suffix_match(input.strings, input.prefixes) + } + note: strings/any_suffix_match/type_error_strict + query: data.test.p = x + strict_error: true + want_error: "test-0.rego:4: eval_type_error: strings.any_suffix_match: operand 2 must be one of {string, set, array} but got number" diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0877.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0877.yaml new file mode 100644 index 000000000000..66e2c265ae4d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0877.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + format_int(15.5, 16, x) + } + note: strings/format_int + query: data.generated.p = x + want_result: + - x: f diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0878.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0878.yaml new file mode 100644 index 000000000000..8bfbfa1f0bd3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0878.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + format_int(15.5, 16, "10000") + } + note: "strings/format_int: undefined" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0879.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0879.yaml new file mode 100644 index 000000000000..e900b88469df --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0879.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + numbers: + - "1" + - "2" + - "3" + - "4" + modules: + - | + package generated + + p { + __local0__ = data.numbers[2] + format_int(3.1, 10, __local0__) + } + note: "strings/format_int: ref dest" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0880.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0880.yaml new file mode 100644 index 000000000000..f78dfc76716a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0880.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + numbers: + - "1" + - "2" + - "3" + - "4" + modules: + - | + package generated + + p { + __local0__ = data.numbers[2] + not format_int(4.1, 10, __local0__) + } + note: "strings/format_int: ref dest (2)" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0881.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0881.yaml new file mode 100644 index 000000000000..451a21e2b32c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0881.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p { + format_int(4.1, 199, x) + } + note: "strings/format_int: err: bad base" + query: data.generated.p = x + want_error: operand 2 must be one of {2, 8, 10, 16} + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0882.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0882.yaml new file mode 100644 index 000000000000..2c9975b8e68b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0882.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + concat("/", ["", "foo", "bar", "0", "baz"], x) + } + note: strings/concat + query: data.generated.p = x + want_result: + - x: /foo/bar/0/baz diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0883.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0883.yaml new file mode 100644 index 000000000000..2fa6aa595462 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0883.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + modules: + - | + package test + + # Sets are unordered, so the output is not guaranteed. + # These are theoretically possible: + possibilities = { + "1,2,3", + "2,3,1", + "3,1,2", + "3,2,1", + "2,1,3", + "1,3,2" + } + + p { + x := concat(",", {"1", "2", "3"}) + possibilities[x] + } + note: "strings/concat: set" + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0884.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0884.yaml new file mode 100644 index 000000000000..fe3af4d795bc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0884.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + concat("/", ["a", "b"], "deadbeef") + } + note: "strings/concat: undefined" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0885.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0885.yaml new file mode 100644 index 000000000000..081368771916 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0885.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p { + __local0__ = data.c[0].x[2] + concat("", ["f", "o", "o"], __local0__) + } + note: "strings/concat: ref dest" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0886.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0886.yaml new file mode 100644 index 000000000000..ccb5bb3e7903 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0886.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p { + __local0__ = data.c[0].x[2] + not concat("", ["b", "a", "r"], __local0__) + } + note: "strings/concat: ref dest (2)" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0887.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0887.yaml new file mode 100644 index 000000000000..db5af7373e0f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0887.yaml @@ -0,0 +1,24 @@ +--- +cases: + - modules: + - | + package test + + p = x { + indexof("abcdefgh", "cde", x) + } + note: strings/indexof + query: data.test.p = x + want_result: + - x: 2 + - modules: + - | + package test + + p = x { + indexof("abcabcabcdefgh", "cde", x) + } + note: strings/indexof + query: data.test.p = x + want_result: + - x: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0888.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0888.yaml new file mode 100644 index 000000000000..1cb25c24c53e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0888.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + indexof("abcdefgh", "xyz", x) + } + note: "strings/indexof: not found" + query: data.generated.p = x + want_result: + - x: -1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0889.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0889.yaml new file mode 100644 index 000000000000..02ef5f72376a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0889.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + substring("abcdefgh", 2, 3, x) + } + note: strings/substring + query: data.generated.p = x + want_result: + - x: cde + - modules: + - "package generated\n\np = x {\n substring(\"\xE5\xE4\xF6\", 0, 2, x)\n}\n" + note: "strings/substring: unicode" + query: data.generated.p = x + want_result: + - x: "\xE5\xE4" diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0890.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0890.yaml new file mode 100644 index 000000000000..69a587ca54c0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0890.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + substring("abcdefgh", 2, -1, x) + } + note: "strings/substring: remainder" + query: data.generated.p = x + want_result: + - x: cdefgh + - modules: + - "package generated\n\np = x {\n substring(\"a\xE5\xE4\xF6\", 2, -1, x)\n}\n" + note: "strings/substring: remainder unicode" + query: data.generated.p = x + want_result: + - x: "\xE4\xF6" diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0891.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0891.yaml new file mode 100644 index 000000000000..dd7cae3726fe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0891.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + substring("abcdefgh", 2, 10000, x) + } + note: "strings/substring: too long" + query: data.generated.p = x + want_result: + - x: cdefgh + - modules: + - "package generated\n\np = x {\n substring(\"abcdefgh\xE5\xE4\xF6\", 2, 10000,\ + \ x)\n}\n" + note: "strings/substring: too long unicode" + query: data.generated.p = x + want_result: + - x: "cdefgh\xE5\xE4\xF6" diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0892.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0892.yaml new file mode 100644 index 000000000000..accf4468d543 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0892.yaml @@ -0,0 +1,27 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + substring("aaa", -1, -1, x) + } + note: "strings/substring: offset negative" + query: data.generated.p = x + want_error: negative offset + want_error_code: eval_builtin_error + strict_error: true + - modules: + - | + package generated + + p = x { + substring("åäö", -1, -1, x) + } + note: "strings/substring: offset negative unicode" + query: data.generated.p = x + want_error: negative offset + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0893.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0893.yaml new file mode 100644 index 000000000000..0ae9a49ceaac --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0893.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + substring("aaa", 3, -1, x) + } + note: "strings/substring: offset too long" + query: data.generated.p = x + want_result: + - x: "" + - modules: + - "package generated\n\np = x {\n substring(\"\xE5\xE4\xF6\", 3, -1, x)\n}\n" + note: "strings/substring: offset too long unicode" + query: data.generated.p = x + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0894.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0894.yaml new file mode 100644 index 000000000000..3f0f9cbbb5bf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0894.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + substring("aaa", 4, -1, x) + } + note: "strings/substring: offset too long 2" + query: data.generated.p = x + want_result: + - x: "" + - modules: + - "package generated\n\np = x {\n substring(\"\xE5\xE4\xF6\", 4, -1, x)\n}\n" + note: "strings/substring: offset too long 2 unicode" + query: data.generated.p = x + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0895.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0895.yaml new file mode 100644 index 000000000000..e84071c6b061 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0895.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + contains("abcdefgh", "defg") + } + note: strings/contains + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0896.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0896.yaml new file mode 100644 index 000000000000..d52eb2a43cf8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0896.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + contains("abcdefgh", "ac") + } + note: "strings/contains: undefined" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0897.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0897.yaml new file mode 100644 index 000000000000..a4e46f56bd38 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0897.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + startswith("abcdefgh", "abcd") + } + note: strings/startswith + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0898.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0898.yaml new file mode 100644 index 000000000000..6af7044b8814 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0898.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + startswith("abcdefgh", "bcd") + } + note: "strings/startswith: undefined" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0899.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0899.yaml new file mode 100644 index 000000000000..71555c61d75a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0899.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + endswith("abcdefgh", "fgh") + } + note: strings/endswith + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0900.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0900.yaml new file mode 100644 index 000000000000..82c88c389e96 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0900.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + endswith("abcdefgh", "fg") + } + note: "strings/endswith: undefined" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0901.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0901.yaml new file mode 100644 index 000000000000..4dc6a9d54b3e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0901.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + lower("AbCdEf", x) + } + note: strings/lower + query: data.generated.p = x + want_result: + - x: abcdef diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0902.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0902.yaml new file mode 100644 index 000000000000..ccfa3232da6f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0902.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + upper("AbCdEf", x) + } + note: strings/upper + query: data.generated.p = x + want_result: + - x: ABCDEF diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0903.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0903.yaml new file mode 100644 index 000000000000..8b7f8e21aba0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0903.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + split("", ".", [x]) + } + note: "strings/split: empty string" + query: data.generated.p = x + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0904.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0904.yaml new file mode 100644 index 000000000000..05eaf64cfecd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0904.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + split("foo", ".", [x]) + } + note: "strings/split: one" + query: data.generated.p = x + want_result: + - x: foo diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0905.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0905.yaml new file mode 100644 index 000000000000..c30b15a65e62 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0905.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y] { + split("foo.bar.baz", ".", [x, "bar", y]) + } + note: "strings/split: many" + query: data.generated.p = x + want_result: + - x: + - foo + - baz diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0906.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0906.yaml new file mode 100644 index 000000000000..b3c2a46107b6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0906.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + replace("", "hi", "bye", x) + } + note: "strings/replace: empty string" + query: data.generated.p = x + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0907.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0907.yaml new file mode 100644 index 000000000000..1806abf55fdf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0907.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + replace("foo.bar", ".", ",", x) + } + note: "strings/replace: one" + query: data.generated.p = x + want_result: + - x: foo,bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0908.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0908.yaml new file mode 100644 index 000000000000..01b2d7e0c573 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0908.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + replace("foo.bar.baz", ".", ",", x) + } + note: "strings/replace: many" + query: data.generated.p = x + want_result: + - x: foo,bar,baz diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0909.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0909.yaml new file mode 100644 index 000000000000..29ea900f53a4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0909.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + replace("foo...bar", "..", ",,", x) + } + note: "strings/replace: overlap" + query: data.generated.p = x + want_result: + - x: foo,,.bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0910.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0910.yaml new file mode 100644 index 000000000000..1dca8c4f6188 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0910.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + trim("", ".", x) + } + note: "strings/trim: empty string" + query: data.generated.p = x + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0911.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0911.yaml new file mode 100644 index 000000000000..b46dc79681b2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0911.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + trim("foo.bar...", ".", x) + } + note: "strings/trim: end" + query: data.generated.p = x + want_result: + - x: foo.bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0912.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0912.yaml new file mode 100644 index 000000000000..67a481b1c8f1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0912.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + trim("...foo.bar", ".", x) + } + note: "strings/trim: start" + query: data.generated.p = x + want_result: + - x: foo.bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0913.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0913.yaml new file mode 100644 index 000000000000..61207db8c06d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0913.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + trim("...foo.bar...", ".", x) + } + note: "strings/trim: both" + query: data.generated.p = x + want_result: + - x: foo.bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0914.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0914.yaml new file mode 100644 index 000000000000..d94d770e9b83 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0914.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + trim("...foo.bar...", ".fr", x) + } + note: "strings/trim: multi-cutset" + query: data.generated.p = x + want_result: + - x: oo.ba diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0915.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0915.yaml new file mode 100644 index 000000000000..69c47d17059f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0915.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + trim("...foo.bar...", ".o", x) + } + note: "strings/trim: multi-cutset-none" + query: data.generated.p = x + want_result: + - x: foo.bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0916.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0916.yaml new file mode 100644 index 000000000000..03a215e11f5b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0916.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + sprintf("hi", [], x) + } + note: "strings/sprintf: none" + query: data.generated.p = x + want_result: + - x: hi diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0917.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0917.yaml new file mode 100644 index 000000000000..30e408964bf3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0917.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + sprintf("hi %s", ["there"], x) + } + note: "strings/sprintf: string" + query: data.generated.p = x + want_result: + - x: hi there diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0918.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0918.yaml new file mode 100644 index 000000000000..53f20e80c22d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0918.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + sprintf("hi %02d", [5], x) + } + note: "strings/sprintf: int" + query: data.generated.p = x + want_result: + - x: hi 05 diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0919.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0919.yaml new file mode 100644 index 000000000000..71601f96e95d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0919.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + sprintf("hi %02X.%02X", [127, 1], x) + } + note: "strings/sprintf: hex" + query: data.generated.p = x + want_result: + - x: hi 7F.01 diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0920.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0920.yaml new file mode 100644 index 000000000000..915a11ce2ed3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0920.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + sprintf("hi %.2f", [3.1415], x) + } + note: "strings/sprintf: float" + query: data.generated.p = x + want_result: + - x: hi 3.14 diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0921.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0921.yaml new file mode 100644 index 000000000000..8b495ea41ee9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0921.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + sprintf("hi %v", [2e308], x) + } + note: "strings/sprintf: float too big" + query: data.generated.p = x + want_result: + - x: hi 2e308 diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0922.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0922.yaml new file mode 100644 index 000000000000..389a652f3aa9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0922.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + sprintf("hi %s", [true], x) + } + note: "strings/sprintf: bool" + query: data.generated.p = x + want_result: + - x: hi true diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0923.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0923.yaml new file mode 100644 index 000000000000..95587edb5318 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0923.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + sprintf("hi %v", [["there", 5, 3.14]], x) + } + note: "strings/sprintf: composite" + query: data.generated.p = x + want_result: + - x: hi ["there", 5, 3.14] diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0924.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0924.yaml new file mode 100644 index 000000000000..5048165cc09f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0924.yaml @@ -0,0 +1,71 @@ +--- +cases: + - data: + foo: bar + modules: + - | + package test + + p := strings.reverse(data.foo) + note: strings/reverse_bar + query: data.test.p = x + want_result: + - x: rab + - data: + foo: "2\uFE0F\u20E3" + modules: + - | + package test + + p := strings.reverse(data.foo) + note: strings/reverse_unicode_multi_char_emojii + query: data.test.p = x + want_result: + - x: "\u20E3\uFE0F2" + - data: + foo: "1\U0001F600\U0001D6FE" + modules: + - | + package test + + p := strings.reverse(data.foo) + note: strings/reverse_unicode + query: data.test.p = x + want_result: + - x: "\U0001D6FE\U0001F6001" + - data: + foo: "" + modules: + - | + package test + + p := strings.reverse(data.foo) + note: strings/reverse_empty + query: data.test.p = x + want_result: + - x: "" + - data: + foo: 123 + modules: + - | + package test + + p := strings.reverse(data.foo) + note: strings/reverse_number_error + query: data.test.p = x + strict_error: true + want_error: "reverse: operand 1 must be string but got number" + want_error_code: eval_type_error + - data: + foo: + bar: baz + modules: + - | + package test + + p := strings.reverse(data.foo) + note: strings/reverse_object_error + query: data.test.p = x + strict_error: true + want_error: "reverse: operand 1 must be string but got object" + want_error_code: eval_type_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0925.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0925.yaml new file mode 100644 index 000000000000..79e9754acdf4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0925.yaml @@ -0,0 +1,42 @@ +--- +cases: + - note: "strings/indexof_n_single_match" + query: data.test.p = x + modules: + - | + package test + p := indexof_n("dogcat", "cat") + want_result: + - x: [3] + - note: "strings/indexof_n_multiple_matches" + query: data.test.p = x + modules: + - | + package test + p := indexof_n("dogcatdogcat", "cat") + want_result: + - x: [3, 9] + - note: "strings/indexof_n_no_match" + query: data.test.p = x + modules: + - | + package test + p := indexof_n("dogcat", "rabbit") + want_result: + - x: [] + - note: "strings/indexof_n_unicode_matches" + query: data.test.p = x + modules: + - | + package test + p := indexof_n("😇😀😇😀😇😀", "😀") + want_result: + - x: [1, 3, 5] + - note: "strings/indexof_n_unicode_no_match" + query: data.test.p = x + modules: + - | + package test + p := indexof_n("😇😀😇😀😇😀", "😂") + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0926.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0926.yaml new file mode 100644 index 000000000000..6de11683456a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-0926.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: "strings/count_single_word_match" + query: data.test.p = x + modules: + - | + package test + p := strings.count("cheese", "e") + want_result: + - x: 3 + - note: "strings/count_multiple_separate_matches" + query: data.test.p = x + modules: + - | + package test + p := strings.count("hello hello hello world", "hello") + want_result: + - x: 3 + - note: "strings/count_n_no_match" + query: data.test.p = x + modules: + - | + package test + p := strings.count("dummy", "x") + want_result: + - x: 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-indexof-unicode.yaml b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-indexof-unicode.yaml new file mode 100644 index 000000000000..ba3218d1f33f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/strings/test-strings-indexof-unicode.yaml @@ -0,0 +1,57 @@ +--- +cases: + - modules: + - | + package test + + p = x { + indexof("μx", "x", x) + } + note: "strings/indexof: unicode char" + query: data.test.p = x + want_result: + - x: 1 + - modules: + - | + package test + + p = x { + indexof("μ", "μμ", x) + } + note: "strings/indexof: unicode chars not found" + query: data.test.p = x + want_result: + - x: -1 + - modules: + - | + package test + + p = x { + indexof("skön var våren", "vår", x) + } + note: "strings/indexof: unicode string" + query: data.test.p = x + want_result: + - x: 9 + - modules: + - | + package test + + p = x { + indexof("🍧🍨🧁🍰🍮", "🍮", x) + } + note: "strings/indexof: unicode string emoji" + query: data.test.p = x + want_result: + - x: 4 + - modules: + - | + package test + + p = x { + indexof("🍧🍨🧁🍰🍮", "🍧🍨🧁🍰🍮", x) + } + note: "strings/indexof: unicode string emojis" + query: data.test.p = x + want_result: + - x: 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/subset/test-subset.yaml b/third_party/opa/v1/test/cases/testdata/v0/subset/test-subset.yaml new file mode 100644 index 000000000000..9aec6f01d4da --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/subset/test-subset.yaml @@ -0,0 +1,437 @@ +--- +cases: + - data: + modules: + - | + package test + + A := { + "a": 5, + "b": 7, + "c": 15 + } + + B := { + "a": 5, + } + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + test_result { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + + note: subset/simple object subset 1 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := { + "a": 5, + "b": 7, + "c": 15 + } + + B := { + "a": 5, + "b": 10, + } + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + test_result { + not AsubB # B is not a subset of A + not BsubA # A is not a subset of B + } + + note: subset/simple object subset 2 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := { + "a": 5, + "b": 7, + "c": 15, + "nested": { + "x": 10, + "y": 15, + "z": 20 + } + } + + B := { + "a": 5, + "nested": { + "x": 10, + "y": 15, + "z": 20 + } + } + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + test_result { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + + note: subset/nested object subset 1 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := { + "a": 5, + "b": 7, + "c": 15, + "nested": { + "x": 10, + "y": 15, + "z": 20 + } + } + + # The subset operation applies recursively to nested objects + + B := { + "a": 5, + "nested": { + "x": 10, + "y": 15, + } + } + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + test_result { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + + note: subset/nested object subset 2 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := { + "a": 5, + "b": 7, + "c": 15, + "nested": { + "x": 10, + "y": 15, + "z": 20, + "set1": {1,2,3,4}, + "arr1": [6,7,8,9] + } + } + + # The subset operation applies recursively to nested objects + + B := { + "a": 5, + "nested": { + "x": 10, + "y": 15, + "set1": {4, 1}, + "arr1": [6,7] + } + } + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + test_result { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + + note: subset/nested object subset 3 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := {1, 2, 3} + B := {3, 2} + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + test_result { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + + note: subset/sets 1 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := {1, 2, 3, {"a", "b", "c"}} + B := {3, 2, {"a", "b", "c"}} + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + test_result { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + + note: subset/nested sets 1 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := {1, 2, 3, {"a", "b", "c"}} + B := {3, 2, {"a", "b"}} + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result { + not AsubB # B is not a subset of A + not BsubA # A is not a subset of B + } + + note: subset/nested sets 2 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := [1,2,3,4,5,6] + B := [3,4,5] + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + + note: subset/arrays 1 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := [1,2,3,4,5,6] + B := [1,2,3] + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + + note: subset/arrays 2 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := [1,2,3,4,5,6] + B := [4,5,6] + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + + note: subset/arrays 3 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := [1,2,3,4,5,6] + B := [1,2,3,4,5,6] + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result { + AsubB # B is a subset of A + BsubA # A is a subset of B + } + + note: subset/arrays 4 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := [1,2,3,4,5,6] + B := {4,3,2} + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + test_result { + AsubB # B is a subset of A + not BsubA # It is invalid operands + } + + note: subset/array and set 1 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := [1,2,3,4,5,6] + B := {9,8,7} + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + test_result { + not AsubB # B is not a subset of A + not BsubA # It is invalid operands + } + + note: subset/array and set 2 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := [1,2,3,4,5,6] + B := [4,5,6,8] + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result { + not AsubB # B is not a subset of A + not BsubA # A is not a subset of B + } + + note: subset/arrays 5 + query: data.test.test_result = x + want_result: + - x: true + + - data: + modules: + - | + package test + + A := [1,2,3,4,5,6] + B := [8,9,10] + + AsubB := object.subset(A, B) + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result { + not AsubB # B is not a subset of A + not BsubA # A is not a subset of B + } + + note: subset/arrays 6 + query: data.test.test_result = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0947.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0947.yaml new file mode 100644 index 000000000000..acf7f4eb12b0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0947.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + time.now_ns(t0) + test.sleep("10ms") + time.now_ns(t1) + t0 == t1 + } + note: time/time caching + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0948.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0948.yaml new file mode 100644 index 000000000000..b451a4470726 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0948.yaml @@ -0,0 +1,77 @@ +--- +cases: + - note: time/parse_nanos + modules: + - | + package generated + + p[case_id] = ns { + case := input.cases[case_id] + time.parse_ns(case.layout, case.value, ns) + } + query: data.generated.p = x + input: { + cases: + { + 1: + { + layout: "2006-01-02T15:04:05Z07:00", + value: "2017-06-02T19:00:00-07:00", + }, + ? 2 # RFC3339 + : { + layout: "2006-01-02T15:04:05Z07:00", + value: "1677-09-21T00:12:43.145224192-00:00", + }, + ? 3 # Earliest valid time + : { + layout: "2006-01-02T15:04:05Z07:00", + value: "2262-04-11T23:47:16.854775807-00:00", + }, + ? 4 # Latest valid time + : { + layout: "01/02 03:04:05PM '06 -0700", + value: "06/02 07:00:00PM '17 -0700", + }, + ? 5 # Layout + : { + layout: "02 Jan 06 15:04 -0700", + value: "02 Jun 17 19:00 -0700", + }, + 6: { layout: "RFC822Z", value: "02 Jun 17 19:00 -0700" }, + }, + } # RFC822Z # format constant + want_result: + - x: + { + 1: 1496455200000000000, + 2: -9223372036854775808, + 3: 9223372036854775807, + 4: 1496455200000000000, + 5: 1496455200000000000, + 6: 1496455200000000000, + } + - note: time/parse_nanos_too_small + modules: + - | + package generated + + p = ns { + time.parse_ns("2006-01-02T15:04:05Z07:00", "1677-09-21T00:12:43.145224191-00:00", ns) + } + query: data.generated.p = x + strict_error: true + want_error_code: eval_builtin_error + want_error: "time outside of valid range" + - note: time/parse_nanos_too_large + modules: + - | + package generated + + p = ns { + time.parse_ns("2006-01-02T15:04:05Z07:00", "2262-04-11T23:47:16.854775808-00:00", ns) + } + query: data.generated.p = x + strict_error: true + want_error_code: eval_builtin_error + want_error: "time outside of valid range" diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0949.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0949.yaml new file mode 100644 index 000000000000..252e27526c9a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0949.yaml @@ -0,0 +1,50 @@ +--- +cases: + - note: time/parse_rfc3339_nanos + modules: + - | + package generated + + p[t] = ns { + t = input.cases[_] + time.parse_rfc3339_ns(t, ns) + } + query: data.generated.p = x + input: { cases: [ + "1677-09-21T00:12:43.145224192-00:00", # Earliest valid time + "1970-01-01T00:00:00-00:00", + "2017-06-02T19:00:00-07:00", + "2262-04-11T23:47:16.854775807-00:00", # Latest valid time + ] } + want_result: + - x: + { + "1677-09-21T00:12:43.145224192-00:00": -9223372036854775808, + "1970-01-01T00:00:00-00:00": 0, + "2017-06-02T19:00:00-07:00": 1496455200000000000, + "2262-04-11T23:47:16.854775807-00:00": 9223372036854775807, + } + - note: time/parse_rfc3339_nanos_too_small + modules: + - | + package generated + + p = ns { + time.parse_rfc3339_ns("1677-09-21T00:12:43.145224191-00:00", ns) + } + query: data.generated.p = x + strict_error: true + want_error_code: eval_builtin_error + want_error: "time outside of valid range" + - note: time/parse_rfc3339_nanos_too_large + modules: + - | + package generated + + p = ns { + time.parse_rfc3339_ns("2262-04-11T23:47:16.854775808-00:00", ns) + } + query: data.generated.p = x + strict_error: true + want_error_code: eval_builtin_error + want_error: "time outside of valid range" diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0950.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0950.yaml new file mode 100644 index 000000000000..30ea7b5ed7fb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0950.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = ns { + time.parse_duration_ns("100ms", ns) + } + note: time/parse duration nanos + query: data.generated.p = x + want_result: + - x: 100000000 diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0951.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0951.yaml new file mode 100644 index 000000000000..22d8e8492a20 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0951.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [__local0__, __local1__, __local2__] { + __local3__ = 1517814000 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.date(__local5__, __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + note: time/date + query: data.generated.p = x + want_result: + - x: + - 2018 + - 2 + - 5 diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0952.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0952.yaml new file mode 100644 index 000000000000..f4b20fd5f6e9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0952.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [__local0__, __local1__, __local2__] { + __local3__ = 1517814000 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.date([__local5__, "America/Los_Angeles"], __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + note: time/date with LA tz + query: data.generated.p = x + want_result: + - x: + - 2018 + - 2 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0953.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0953.yaml new file mode 100644 index 000000000000..e17c1d5b0831 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0953.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [__local0__, __local1__, __local2__] { + __local3__ = 1517832000 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.date([__local5__, ""], __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + note: time/date with empty tz + query: data.generated.p = x + want_result: + - x: + - 2018 + - 2 + - 5 diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0954.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0954.yaml new file mode 100644 index 000000000000..7343dac41dc7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0954.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [__local0__, __local1__, __local2__] { + __local3__ = 1582977600 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.date(__local5__, __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + note: time/date leap day + query: data.generated.p = x + want_result: + - x: + - 2020 + - 2 + - 29 diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0955.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0955.yaml new file mode 100644 index 000000000000..f271b2bab544 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0955.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [__local0__, __local1__, __local2__] { + __local3__ = 1582977600 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + __local6__ = __local5__ * 1000 + time.date(__local6__, __local7__) + [__local0__, __local1__, __local2__] = __local7__ + } + note: time/date too big + query: data.generated.p = x + want_error: timestamp too big + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0956.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0956.yaml new file mode 100644 index 000000000000..3023344f929f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0956.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [__local0__, __local1__, __local2__] { + __local3__ = 1517832000 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.clock(__local5__, __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + note: time/clock + query: data.generated.p = x + want_result: + - x: + - 12 + - 0 + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0957.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0957.yaml new file mode 100644 index 000000000000..17521f02a7d3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0957.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [__local0__, __local1__, __local2__] { + __local3__ = 1517832000 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.clock([__local5__, "America/New_York"], __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + note: time/clock with NY tz + query: data.generated.p = x + want_result: + - x: + - 7 + - 0 + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0958.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0958.yaml new file mode 100644 index 000000000000..df749d8e438f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0958.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [__local0__, __local1__, __local2__] { + __local3__ = 1582977600 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.clock(__local5__, __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + note: time/clock leap day + query: data.generated.p = x + want_result: + - x: + - 12 + - 0 + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0959.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0959.yaml new file mode 100644 index 000000000000..5d44268d9352 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0959.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [__local0__, __local1__, __local2__] { + __local3__ = 1582977600 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + __local6__ = __local5__ * 1000 + time.clock(__local6__, __local7__) + [__local0__, __local1__, __local2__] = __local7__ + } + note: time/clock too big + query: data.generated.p = x + want_error: timestamp too big + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0960.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0960.yaml new file mode 100644 index 000000000000..b9a3993fdd92 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0960.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + time.weekday(1517832000000000000, __local1__) + __local0__ = __local1__ + } + note: time/weekday + query: data.generated.p = x + want_result: + - x: Monday diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0961.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0961.yaml new file mode 100644 index 000000000000..1d9ea4c6ea8e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0961.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + time.weekday(1517918400000000000, __local1__) + __local0__ = __local1__ + } + note: time/weekday + query: data.generated.p = x + want_result: + - x: Tuesday diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0962.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0962.yaml new file mode 100644 index 000000000000..bb0159e7512b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0962.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + time.weekday(1518004800000000000, __local1__) + __local0__ = __local1__ + } + note: time/weekday + query: data.generated.p = x + want_result: + - x: Wednesday diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0963.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0963.yaml new file mode 100644 index 000000000000..9c05729728be --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0963.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + time.weekday(1518091200000000000, __local1__) + __local0__ = __local1__ + } + note: time/weekday + query: data.generated.p = x + want_result: + - x: Thursday diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0964.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0964.yaml new file mode 100644 index 000000000000..96882aebd4df --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0964.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + time.weekday(1518177600000000000, __local1__) + __local0__ = __local1__ + } + note: time/weekday + query: data.generated.p = x + want_result: + - x: Friday diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0965.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0965.yaml new file mode 100644 index 000000000000..d2ed4ea7ab15 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0965.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + time.weekday(1518264000000000000, __local1__) + __local0__ = __local1__ + } + note: time/weekday + query: data.generated.p = x + want_result: + - x: Saturday diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0966.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0966.yaml new file mode 100644 index 000000000000..efce11d37bcd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0966.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = __local0__ { + time.weekday(1518350400000000000, __local1__) + __local0__ = __local1__ + } + note: time/weekday + query: data.generated.p = x + want_result: + - x: Sunday diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0967.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0967.yaml new file mode 100644 index 000000000000..72b503a3a41e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0967.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = __local0__ { + __local1__ = 1582977600 * 1000 + __local2__ = __local1__ * 1000 + __local3__ = __local2__ * 1000 + __local4__ = __local3__ * 1000 + time.weekday(__local4__, __local5__) + __local0__ = __local5__ + } + note: time/weekday too big + query: data.generated.p = x + want_error: timestamp too big + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0968.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0968.yaml new file mode 100644 index 000000000000..303d772f541c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0968.yaml @@ -0,0 +1,27 @@ +--- +cases: + - note: time/add_date year month day + modules: + - | + package generated + + p = __local0__ { + time.add_date(1585852421593912000, 3, 9, 12, __local1__) + __local0__ = __local1__ + } + + query: data.generated.p = x + want_result: + - x: 1705257221593912000 + - note: time/add_date too large result + modules: + - | + package generated + + p = ns { + time.add_date(0, 2262, 1, 1, ns) + } + query: data.generated.p = x + strict_error: true + want_error_code: eval_builtin_error + want_error: "time outside of valid range" diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0969.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0969.yaml new file mode 100644 index 000000000000..7822966384eb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0969.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: time/add_date negative values + modules: + - | + package generated + + p = __local0__ { + time.add_date(1585852421593912000, -1, -1, -1, __local1__) + __local0__ = __local1__ + } + query: data.generated.p = x + want_result: + - x: 1551465221593912000 + - note: time/add_date too small result + modules: + - | + package generated + + p = ns { + time.add_date(-9223372036854775808, 0, 0, -1, ns) + } + query: data.generated.p = x + strict_error: true + want_error_code: eval_builtin_error + want_error: "time outside of valid range" diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0970.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0970.yaml new file mode 100644 index 000000000000..daa2cb4913fb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0970.yaml @@ -0,0 +1,42 @@ +--- +cases: + - data: + modules: + - | + package test + + layout := "2006-01-02" + + a = minute_second { + minute_second := time.diff(time.now_ns()+61*1000*1000*1000, time.now_ns()) + } + + b = different_tz { + different_tz := time.diff([time.now_ns()+60*1000*1000*1000, "UTC"], [time.now_ns(), "Asia/Shanghai"]) + } + + c = leap_year { + leap_year := time.diff(time.parse_ns(layout, "2020-02-02"), time.parse_ns(layout, "2020-03-01")) + } + + d = not_leap_year { + not_leap_year := time.diff(time.parse_ns(layout, "2021-02-02"), time.parse_ns(layout, "2021-03-01")) + } + + e = leap_year_one_day { + leap_year_one_day := time.diff(time.parse_ns(layout, "2004-02-29"), time.parse_ns(layout, "2005-03-01")) + } + + note: time/diff + query: > + data.test.a = minute_second; + data.test.b = different_tz; + data.test.c = leap_year; + data.test.d = not_leap_year; + data.test.e = leap_year_one_day + want_result: + - minute_second: [0, 0, 0, 0, 1, 1] + different_tz: [0, 0, 0, 0, 1, 0] + leap_year: [0, 0, 28, 0, 0, 0] + not_leap_year: [0, 0, 27, 0, 0, 0] + leap_year_one_day: [1, 0, 1, 0, 0, 0] diff --git a/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0971.yaml b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0971.yaml new file mode 100644 index 000000000000..08cf568d7ffc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/time/test-time-0971.yaml @@ -0,0 +1,40 @@ +--- +cases: + - data: + modules: + - | + package test + + time_ns := 1670006453141828752 + + a := time.format(time_ns) + + b := time.format([time_ns, "Asia/Kolkata"]) + + c := time.format([time_ns,"Asia/Kolkata","Mon Jan 02 15:04:05 -0700 2006"]) + + d := time.format([time_ns,"Asia/Kolkata","RFC1123Z"]) + + note: time/format + query: > + data.test.a = no_timezone; + data.test.b = with_timezone; + data.test.c = with_layout; + data.test.d = with_constant + want_result: + - no_timezone: "2022-12-02T18:40:53.141828752Z" + with_timezone: "2022-12-03T00:10:53.141828752+05:30" + with_layout: "Sat Dec 03 00:10:53 +0530 2022" + with_constant: "Sat, 03 Dec 2022 00:10:53 +0530" + - data: + modules: + - | + package generated + + p := time.format(1582977600 * 10e12) + + note: time/format too big + query: data.generated.p = x + want_error: timestamp too big + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/toarray/test-toarray-0071.yaml b/third_party/opa/v1/test/cases/testdata/v0/toarray/test-toarray-0071.yaml new file mode 100644 index 000000000000..03dd253f826a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/toarray/test-toarray-0071.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + cast_array([1, 2, 3], x) + } + note: toarray/array input + query: data.generated.p = x + want_result: + - x: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/toarray/test-toarray-0072.yaml b/third_party/opa/v1/test/cases/testdata/v0/toarray/test-toarray-0072.yaml new file mode 100644 index 000000000000..6526f855ae71 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/toarray/test-toarray-0072.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + cast_array({1, 2, 3}, x) + } + note: toarray/set input + query: data.generated.p = x + want_result: + - x: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/toarray/test-toarray-0073.yaml b/third_party/opa/v1/test/cases/testdata/v0/toarray/test-toarray-0073.yaml new file mode 100644 index 000000000000..449c3899ab03 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/toarray/test-toarray-0073.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + cast_array("hello", x) + } + note: toarray/bad type + query: data.generated.p = x + want_error: operand 1 must be one of {array, set} + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/topdowndynamicdispatch/test-topdowndynamicdispatch-1068.yaml b/third_party/opa/v1/test/cases/testdata/v0/topdowndynamicdispatch/test-topdowndynamicdispatch-1068.yaml new file mode 100644 index 000000000000..f64690f99a2f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/topdowndynamicdispatch/test-topdowndynamicdispatch-1068.yaml @@ -0,0 +1,33 @@ +--- +cases: + - data: {} + input_term: "{}" + modules: + - | + package animals + + dog = "woof" + + cat = "meow" + - | + package dynamic + + sound = __local0__ { + true + __local1__ = data.dynamic.animal + __local0__ = data.animals[__local1__] + } + + animal = "dog" { + 2 > 1 + } + note: topdowndynamicdispatch/dynamic dispatch + query: data = x + want_result: + - x: + animals: + cat: meow + dog: woof + dynamic: + animal: dog + sound: woof diff --git a/third_party/opa/v1/test/cases/testdata/v0/toset/test-toset-0074.yaml b/third_party/opa/v1/test/cases/testdata/v0/toset/test-toset-0074.yaml new file mode 100644 index 000000000000..3424ac3e88d6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/toset/test-toset-0074.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + cast_set([1, 1, 1], x) + } + note: toset/array input + query: data.generated.p = x + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/toset/test-toset-0075.yaml b/third_party/opa/v1/test/cases/testdata/v0/toset/test-toset-0075.yaml new file mode 100644 index 000000000000..3cb52e6cfa33 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/toset/test-toset-0075.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + cast_set({1, 2, 3}, x) + } + note: toset/set input + query: data.generated.p = x + want_result: + - x: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/toset/test-toset-0076.yaml b/third_party/opa/v1/test/cases/testdata/v0/toset/test-toset-0076.yaml new file mode 100644 index 000000000000..d8c3404ea88b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/toset/test-toset-0076.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + cast_set("hello", x) + } + note: toset/bad type + query: data.generated.p = x + want_error: operand 1 must be one of {array, set} + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/trim/test-trim-0362.yaml b/third_party/opa/v1/test/cases/testdata/v0/trim/test-trim-0362.yaml new file mode 100644 index 000000000000..d275ab36366a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/trim/test-trim-0362.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + trim("¡¡¡foo, bar!!!", "!¡", __local1__) + __local0__ = __local1__ + } + note: trim/trims '!¡' from string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - foo, bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/trim/test-trim-0363.yaml b/third_party/opa/v1/test/cases/testdata/v0/trim/test-trim-0363.yaml new file mode 100644 index 000000000000..76a6a970c276 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/trim/test-trim-0363.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + trim("¡¡¡foo, bar!!!", "i", __local1__) + __local0__ = __local1__ + } + note: trim/trims nothing from string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - ¡¡¡foo, bar!!! diff --git a/third_party/opa/v1/test/cases/testdata/v0/trimleft/test-trimleft-0364.yaml b/third_party/opa/v1/test/cases/testdata/v0/trimleft/test-trimleft-0364.yaml new file mode 100644 index 000000000000..27a80bd5c9dc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/trimleft/test-trimleft-0364.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + trim_left("¡¡¡foo, bar!!!", "!¡", __local1__) + __local0__ = __local1__ + } + note: trimleft/trims leading '!¡' from string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - foo, bar!!! diff --git a/third_party/opa/v1/test/cases/testdata/v0/trimleft/test-trimleft-0365.yaml b/third_party/opa/v1/test/cases/testdata/v0/trimleft/test-trimleft-0365.yaml new file mode 100644 index 000000000000..94be42eb34de --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/trimleft/test-trimleft-0365.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + trim_left("!!!foo, bar¡¡¡", "¡", __local1__) + __local0__ = __local1__ + } + note: trimleft/trims nothing from string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - "!!!foo, bar¡¡¡" diff --git a/third_party/opa/v1/test/cases/testdata/v0/trimprefix/test-trimprefix-0366.yaml b/third_party/opa/v1/test/cases/testdata/v0/trimprefix/test-trimprefix-0366.yaml new file mode 100644 index 000000000000..8e7258e9904e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/trimprefix/test-trimprefix-0366.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + trim_prefix("¡¡¡foo, bar!!!", "¡¡¡foo", __local1__) + __local0__ = __local1__ + } + note: trimprefix/trims prefix '!¡' from string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - ", bar!!!" diff --git a/third_party/opa/v1/test/cases/testdata/v0/trimprefix/test-trimprefix-0367.yaml b/third_party/opa/v1/test/cases/testdata/v0/trimprefix/test-trimprefix-0367.yaml new file mode 100644 index 000000000000..f94442ce24c6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/trimprefix/test-trimprefix-0367.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + trim_prefix("¡¡¡foo, bar!!!", "¡¡¡bar", __local1__) + __local0__ = __local1__ + } + note: trimprefix/trims nothing from string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - ¡¡¡foo, bar!!! diff --git a/third_party/opa/v1/test/cases/testdata/v0/trimright/test-trimright-0368.yaml b/third_party/opa/v1/test/cases/testdata/v0/trimright/test-trimright-0368.yaml new file mode 100644 index 000000000000..2b1da704111b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/trimright/test-trimright-0368.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + trim_right("¡¡¡foo, bar!!!", "!¡", __local1__) + __local0__ = __local1__ + } + note: trimright/trims trailing '!¡' from string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - ¡¡¡foo, bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/trimright/test-trimright-0369.yaml b/third_party/opa/v1/test/cases/testdata/v0/trimright/test-trimright-0369.yaml new file mode 100644 index 000000000000..e1d7f34b91d4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/trimright/test-trimright-0369.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + trim_right("!!!foo, bar¡¡¡", "!", __local1__) + __local0__ = __local1__ + } + note: trimright/trims nothing from string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - "!!!foo, bar¡¡¡" diff --git a/third_party/opa/v1/test/cases/testdata/v0/trimspace/test-trimspace-0372.yaml b/third_party/opa/v1/test/cases/testdata/v0/trimspace/test-trimspace-0372.yaml new file mode 100644 index 000000000000..3e81ee801d98 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/trimspace/test-trimspace-0372.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + trim_space(" \t\n foo, bar \n\t\r\n", __local1__) + __local0__ = __local1__ + } + note: trimspace/trims all leading and trailing white space from string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - foo, bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/trimspace/test-trimspace-0373.yaml b/third_party/opa/v1/test/cases/testdata/v0/trimspace/test-trimspace-0373.yaml new file mode 100644 index 000000000000..fcaf5fe18156 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/trimspace/test-trimspace-0373.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + trim_space("foo, bar", __local1__) + __local0__ = __local1__ + } + note: trimspace/trims nothing from string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - foo, bar diff --git a/third_party/opa/v1/test/cases/testdata/v0/trimsuffix/test-trimsuffix-0370.yaml b/third_party/opa/v1/test/cases/testdata/v0/trimsuffix/test-trimsuffix-0370.yaml new file mode 100644 index 000000000000..58f6db6769fd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/trimsuffix/test-trimsuffix-0370.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + trim_suffix("¡¡¡foo, bar!!!", ", bar!!!", __local1__) + __local0__ = __local1__ + } + note: trimsuffix/trims suffix '!¡' from string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - ¡¡¡foo diff --git a/third_party/opa/v1/test/cases/testdata/v0/trimsuffix/test-trimsuffix-0371.yaml b/third_party/opa/v1/test/cases/testdata/v0/trimsuffix/test-trimsuffix-0371.yaml new file mode 100644 index 000000000000..394b553e38a1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/trimsuffix/test-trimsuffix-0371.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[__local0__] { + trim_suffix("¡¡¡foo, bar!!!", ", foo!!!", __local1__) + __local0__ = __local1__ + } + note: trimsuffix/trims nothing from string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - ¡¡¡foo, bar!!! diff --git a/third_party/opa/v1/test/cases/testdata/v0/type/test-regressions.yaml b/third_party/opa/v1/test/cases/testdata/v0/type/test-regressions.yaml new file mode 100644 index 000000000000..77d97bd6d5b0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/type/test-regressions.yaml @@ -0,0 +1,89 @@ +--- +cases: + - note: regression/partial-object override, different key type, query + modules: + - | + package test + + p[k] := v { + v := ["a", "b", "c"][k] + } + + p.foo := "bar" + query: data.test.p.foo = x + want_result: + - x: "bar" + - note: regression/partial-object override, different key type, referenced in other rule + modules: + - | + package test + + p[k] := v { + v := ["a", "b", "c"][k] + } + + p.foo := "bar" + + q[x] { + x := p[_] + x == "bar" + } + query: data.test.q = x + want_result: + - x: ["bar"] + - note: regression/dynamic object to static object comparison (https://github.com/open-policy-agent/opa/issues/6138) + modules: + - | + package test + + import future.keywords + + l := ["a", "b", "c"] + + obj[k] := v { + v := ["a", "b", "c"][k] + k < 3 + } + + obj[k] := v { + v := input.m[k] + } + + obj.foo := "bar" { input.foo } + + obj.baz := true { input.baz } + + compare { + # Comparison with static object that doesn't contain "optional" key. + obj == { + 0: "a", + 1: "b", + 2: "c", + } + + obj == { + 0: "a", + 1: "b", + 2: "c", + "foo": "bar" + } with input.foo as true + + obj == { + 0: "a", + 1: "b", + 2: "c", + "baz": true + } with input.baz as true + + obj == { + 0: "a", + 1: "b", + 2: "c", + 3: "d", + 4: "e", + 100: "f" + } with input.m as {3: "d", 4: "e", 100: "f"} + } + query: data.test.compare = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0828.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0828.yaml new file mode 100644 index 000000000000..7823c9304811 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0828.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + is_number(-42.0, x) + is_number(0, y) + is_number(100.1, z) + } + note: typebuiltin/is_number + query: data.generated.p = x + want_result: + - x: + - true + - true + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0829.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0829.yaml new file mode 100644 index 000000000000..55242c416b4b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0829.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + is_number(null, x) + } + note: typebuiltin/is_number + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0830.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0830.yaml new file mode 100644 index 000000000000..d79efb559b83 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0830.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + is_number(false, x) + } + note: typebuiltin/is_number + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0831.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0831.yaml new file mode 100644 index 000000000000..5cb125303dc9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0831.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + arr = [true, 1] + arr[_] = x + is_number(x) + } + note: typebuiltin/is_number + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0832.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0832.yaml new file mode 100644 index 000000000000..e9ae8d92a9cf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0832.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y, z] { + is_string("Hello", x) + is_string("There", y) + is_string("OPA", z) + } + note: typebuiltin/is_string + query: data.generated.p = x + want_result: + - x: + - true + - true + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0833.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0833.yaml new file mode 100644 index 000000000000..4abe521530c2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0833.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + is_string(null, x) + } + note: typebuiltin/is_string + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0834.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0834.yaml new file mode 100644 index 000000000000..7d61ebd145a0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0834.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + is_string(false, x) + } + note: typebuiltin/is_string + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0835.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0835.yaml new file mode 100644 index 000000000000..83b6e6da0909 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0835.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + arr = [true, 1, "Hey"] + arr[_] = x + is_string(x) + } + note: typebuiltin/is_string + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - Hey diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0836.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0836.yaml new file mode 100644 index 000000000000..71e7ee78b141 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0836.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y] { + is_boolean(true, x) + is_boolean(false, y) + } + note: typebuiltin/is_boolean + query: data.generated.p = x + want_result: + - x: + - true + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0837.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0837.yaml new file mode 100644 index 000000000000..452c2ca34502 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0837.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + is_boolean(null, x) + } + note: typebuiltin/is_boolean + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0838.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0838.yaml new file mode 100644 index 000000000000..eadf9b15b11f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0838.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + is_boolean("Hello", x) + } + note: typebuiltin/is_boolean + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0839.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0839.yaml new file mode 100644 index 000000000000..4d2323e8bd0e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0839.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + arr = [false, 1, "Hey"] + arr[_] = x + is_boolean(x) + } + note: typebuiltin/is_boolean + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0840.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0840.yaml new file mode 100644 index 000000000000..0fae54a2f4a6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0840.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y] { + is_array([1, 2, 3], x) + is_array(["a", "b"], y) + } + note: typebuiltin/is_array + query: data.generated.p = x + want_result: + - x: + - true + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0841.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0841.yaml new file mode 100644 index 000000000000..2d9ef5c18dd2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0841.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + is_array({1, 2, 3}, x) + } + note: typebuiltin/is_array + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0842.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0842.yaml new file mode 100644 index 000000000000..27f21391592d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0842.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = [x, y] { + is_set({1, 2, 3}, x) + is_set({"a", "b"}, y) + } + note: typebuiltin/is_set + query: data.generated.p = x + want_result: + - x: + - true + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0843.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0843.yaml new file mode 100644 index 000000000000..11e27d5a0b55 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0843.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + is_set([1, 2, 3], x) + } + note: typebuiltin/is_set + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0844.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0844.yaml new file mode 100644 index 000000000000..d740632ef3cd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0844.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + __local0__ = {"foo": yy | yy = 1} + is_object(__local0__, x) + } + note: typebuiltin/is_object + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0845.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0845.yaml new file mode 100644 index 000000000000..4a6381cf7820 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0845.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + is_object("foo", x) + } + note: typebuiltin/is_object + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0846.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0846.yaml new file mode 100644 index 000000000000..03e2acae0b80 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0846.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + is_null(null, x) + } + note: typebuiltin/is_null + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0847.yaml b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0847.yaml new file mode 100644 index 000000000000..95ffa8e0f785 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typebuiltin/test-typebuiltin-0847.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + is_null(true, x) + } + note: typebuiltin/is_null + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0848.yaml b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0848.yaml new file mode 100644 index 000000000000..55d2eb4bcbd5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0848.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + type_name(null, x) + } + note: typenamebuiltin/type_name + query: data.generated.p = x + want_result: + - x: "null" diff --git a/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0849.yaml b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0849.yaml new file mode 100644 index 000000000000..73682f0ded44 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0849.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + type_name(true, x) + } + note: typenamebuiltin/type_name + query: data.generated.p = x + want_result: + - x: boolean diff --git a/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0850.yaml b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0850.yaml new file mode 100644 index 000000000000..4c32c99ccd99 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0850.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + type_name(100, x) + } + note: typenamebuiltin/type_name + query: data.generated.p = x + want_result: + - x: number diff --git a/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0851.yaml b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0851.yaml new file mode 100644 index 000000000000..af920fb96d7f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0851.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + type_name("Hello", x) + } + note: typenamebuiltin/type_name + query: data.generated.p = x + want_result: + - x: string diff --git a/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0852.yaml b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0852.yaml new file mode 100644 index 000000000000..1edfe5259433 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0852.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + type_name([1, 2, 3], x) + } + note: typenamebuiltin/type_name + query: data.generated.p = x + want_result: + - x: array diff --git a/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0853.yaml b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0853.yaml new file mode 100644 index 000000000000..0c8f172d0393 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0853.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + type_name({1, 2, 3}, x) + } + note: typenamebuiltin/type_name + query: data.generated.p = x + want_result: + - x: set diff --git a/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0854.yaml b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0854.yaml new file mode 100644 index 000000000000..6dc5e2712f3a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/typenamebuiltin/test-typenamebuiltin-0854.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + __local0__ = {"foo": yy | yy = 1} + type_name(__local0__, x) + } + note: typenamebuiltin/type_name + query: data.generated.p = x + want_result: + - x: object diff --git a/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0599.yaml b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0599.yaml new file mode 100644 index 000000000000..5b8de1989f78 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0599.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + arr = [[1, [2]], [1, null], [2, [2]]] + [x, [2]] = arr[_] + } + note: undos/array-type + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0600.yaml b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0600.yaml new file mode 100644 index 000000000000..4a97cad5a2d6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0600.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + arr = [[1, 2], [1, null], [2, 2]] + arr[_] = [x, 2] + } + note: undos/arrays-element + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0601.yaml b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0601.yaml new file mode 100644 index 000000000000..d9e822e9b157 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0601.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + arr = [[1, [2]], [1, []], [2, [2]]] + arr[_] = [x, [2]] + } + note: undos/arrays-length + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0602.yaml b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0602.yaml new file mode 100644 index 000000000000..3bf69c75361e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0602.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + arr_ref: + - 1 + - null + modules: + - | + package generated + + p[x] { + __local0__ = data.arr_ref + arr = [[1, 2], __local0__, [2, 2]] + arr[_] = [x, 2] + } + note: undos/array-ref-element + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0603.yaml b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0603.yaml new file mode 100644 index 000000000000..128e9e5c28db --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0603.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + obj = {"a": {"x": 1, "y": {"v": 2}}, "b": {"x": 1, "y": null}, "c": {"x": 2, "y": {"v": 2}}} + {"x": x, "y": {"v": 2}} = obj[_] + } + note: undos/object-type + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0604.yaml b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0604.yaml new file mode 100644 index 000000000000..80e3661cd584 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0604.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + obj = {"a": {"x": 1, "y": 2}, "b": {"x": 1, "y": null}, "c": {"x": 2, "y": 2}} + obj[_] = {"x": x, "y": 2} + } + note: undos/objects-element + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0605.yaml b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0605.yaml new file mode 100644 index 000000000000..9c239728d933 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0605.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + obj = {"a": {"x": 1, "y": {"v": 2}}, "b": {"x": 1, "y": {}}, "c": {"x": 2, "y": {"v": 2}}} + obj[_] = {"x": x, "y": {"v": 2}} + } + note: undos/objects-length + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0606.yaml b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0606.yaml new file mode 100644 index 000000000000..50515989f89e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0606.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + obj_ref: + x: 1 + y: null + modules: + - | + package generated + + p[x] { + __local0__ = data.obj_ref + obj = {"a": {"x": 1, "y": 2}, "b": __local0__, "c": {"x": 2, "y": 2}} + obj[_] = {"x": x, "y": 2} + } + note: undos/object-ref-element + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0607.yaml b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0607.yaml new file mode 100644 index 000000000000..a429db135cf4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/undos/test-undos-0607.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + obj_ref_missing_key: + x: 3 + z: 2 + modules: + - | + package generated + + p[x] { + __local0__ = data.obj_ref_missing_key + obj = {"a": {"x": 1, "y": 2}, "b": __local0__, "c": {"x": 2, "y": 2}} + obj[_] = {"x": x, "y": 2} + } + note: undos/object-ref-missing-key + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0357.yaml b/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0357.yaml new file mode 100644 index 000000000000..35edfaeb0aeb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0357.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + union(set(), x) + } + note: union/union_0_sets + query: data.generated.p = x + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0358.yaml b/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0358.yaml new file mode 100644 index 000000000000..3a4a2ed50ec6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0358.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + union({set(), {1, 2}}, x) + } + note: union/union_2_sets + query: data.generated.p = x + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0359.yaml b/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0359.yaml new file mode 100644 index 000000000000..946779ce443f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0359.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + s1 = {1, 2, 3} + s2 = {2} + union({s1, s2}, x) + } + note: union/union_2_sets + query: data.generated.p = x + want_result: + - x: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0360.yaml b/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0360.yaml new file mode 100644 index 000000000000..df788fb4fd1a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0360.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + s1 = {1, 2, 3} + s2 = {2, 3, 4} + s3 = {4, 5, 6} + union({s1, s2, s3}, x) + } + note: union/union_3_sets + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 diff --git a/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0361.yaml b/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0361.yaml new file mode 100644 index 000000000000..31f63e20048f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/union/test-union-0361.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + s1 = {"a", "b", "c", "d"} + s2 = {"b", "c", "d"} + s3 = {"c", "d"} + s4 = {"d"} + union({s1, s2, s3, s4}, x) + } + note: union/union_4_sets + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - a + - b + - c + - d diff --git a/third_party/opa/v1/test/cases/testdata/v0/units/test-issue-4856.yaml b/third_party/opa/v1/test/cases/testdata/v0/units/test-issue-4856.yaml new file mode 100644 index 000000000000..c76138fef29a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/units/test-issue-4856.yaml @@ -0,0 +1,35 @@ +--- +cases: + - data: + modules: + - | + package test + p { + units.parse("500m") == 0.5 + } + note: units_parse/exact comparison - regression case 1 + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse("0.0005K") == 0.5 + } + note: units_parse/exact comparison - regression case 2 + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse("0.0000005M") == 0.5 + } + note: units_parse/exact comparison - regression case 3 + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-bytes-comparisons.yaml b/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-bytes-comparisons.yaml new file mode 100644 index 000000000000..59df4dc487a1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-bytes-comparisons.yaml @@ -0,0 +1,112 @@ +--- +cases: + - data: + modules: + - | + package test + p { + units.parse_bytes("8kb") > units.parse_bytes("7kb") + } + note: units_parse_bytes/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse_bytes("8gb") > units.parse_bytes("8mb") + } + note: units_parse_bytes/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse_bytes("1234kb") < units.parse_bytes("1gb") + } + note: units_parse_bytes/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse_bytes("1024") == units.parse_bytes("1KiB") + } + note: units_parse_bytes/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse_bytes("2MiB") == units.parse_bytes("2097152") + } + note: units_parse_bytes/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse_bytes("3MiB") > units.parse_bytes("3MB") + } + note: units_parse_bytes/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse_bytes("2MiB") == units.parse_bytes("2Mi") + } + note: units_parse_bytes/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse_bytes("4Mi") > units.parse_bytes("4M") + } + note: units_parse_bytes/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse_bytes("4.1Mi") > units.parse_bytes("4Mi") + } + note: units_parse_bytes/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse_bytes("128Gi") == units.parse_bytes("137438953472") + } + note: units_parse_bytes/comparison + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-bytes-errors.yaml b/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-bytes-errors.yaml new file mode 100644 index 000000000000..095017096f11 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-bytes-errors.yaml @@ -0,0 +1,86 @@ +--- +cases: + - data: + modules: + - | + package test + p { + units.parse_bytes("") + } + note: units_parse_bytes/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: no byte amount provided" + strict_error: true + - data: + modules: + - | + package test + p { + units.parse_bytes("GB") + } + note: units_parse_bytes/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: no byte amount provided" + strict_error: true + - data: + modules: + - | + package test + p { + units.parse_bytes("foo") + } + note: units_parse_bytes/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: no byte amount provided" + strict_error: true + - data: + modules: + - | + package test + p { + units.parse_bytes("0.0.0") + } + note: units_parse_bytes/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: could not parse byte amount to a number" + strict_error: true + - data: + modules: + - | + package test + p { + units.parse_bytes(".5.2") + } + note: units_parse_bytes/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: could not parse byte amount to a number" + strict_error: true + - data: + modules: + - | + package test + p { + units.parse_bytes("100 kb") + } + note: units_parse_bytes/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: spaces not allowed in resource strings" + strict_error: true + - data: + modules: + - | + package test + p { + units.parse_bytes(" 327MiB ") + } + note: units_parse_bytes/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: spaces not allowed in resource strings" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-bytes.yaml b/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-bytes.yaml new file mode 100644 index 000000000000..d04dbc480f5e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-bytes.yaml @@ -0,0 +1,456 @@ +--- +cases: + - data: + modules: + - | + package test + p { + units.parse_bytes("\"100TIB\"") == 109951162777600 + } + note: units_parse_bytes/removes quotes and lowercases string + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse_bytes("0") == 0 + } + note: units_parse_bytes/zero + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("0.0") == 0 + } + note: units_parse_bytes/zero float + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes(".0") == 0 + } + note: units_parse_bytes/zero bare float + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("12345") == 12345 + } + note: units_parse_bytes/raw number + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("10KB") == 10000 + } + note: units_parse_bytes/10 kilobytes uppercase + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("10KIB") == 10240 + } + note: units_parse_bytes/10 KiB uppercase + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("10kb") == 10000 + } + note: units_parse_bytes/10 KB lowercase + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("10Kib") == 10240 + } + note: units_parse_bytes/10 KiB mixed case + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("200mb") == 200000000 + } + note: units_parse_bytes/200 megabytes as mb + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("300GiB") == 322122547200 + } + note: units_parse_bytes/300 GiB + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("1.1KB") == 1100 + } + note: units_parse_bytes/1.1 KB floating point + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("1.1KiB") == 1126 + } + note: units_parse_bytes/1.1 KiB floating point rounded + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes(".5KB") == 500 + } + note: units_parse_bytes/.5 KB bare floating point + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100k") == 100000 + } + note: units_parse_bytes/100 kilobytes as k + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100kb") == 100000 + } + note: units_parse_bytes/100 kilobytes as kb + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100ki") == 102400 + } + note: units_parse_bytes/100 kibibytes as ki + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100kib") == 102400 + } + note: units_parse_bytes/100 kibibytes as kib + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100m") == 100000000 + } + note: units_parse_bytes/100 megabytes as m + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100mb") == 100000000 + } + note: units_parse_bytes/100 megabytes as mb + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100mi") == 104857600 + } + note: units_parse_bytes/100 mebibytes as mi + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100mib") == 104857600 + } + note: units_parse_bytes/100 mebibytes as mib + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100g") == 100000000000 + } + note: units_parse_bytes/100 gigabytes as g + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100gb") == 100000000000 + } + note: units_parse_bytes/100 gigabytes as gb + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100gi") == 107374182400 + } + note: units_parse_bytes/100 gibibytes as gi + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100gib") == 107374182400 + } + note: units_parse_bytes/100 gibibytes as gib + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100t") == 100000000000000 + } + note: units_parse_bytes/100 terabytes as t + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100tb") == 100000000000000 + } + note: units_parse_bytes/100 terabytes as tb + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100ti") == 109951162777600 + } + note: units_parse_bytes/100 tebibytes as ti + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100tib") == 109951162777600 + } + note: units_parse_bytes/100 tebibytes as tib + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100p") == 100000000000000000 + } + note: units_parse_bytes/100 petabytes as p + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100pb") == 100000000000000000 + } + note: units_parse_bytes/100 petabytes as pb + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100pi") == 112589990684262400 + } + note: units_parse_bytes/100 pebibytes as pi + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("100pib") == 112589990684262400 + } + note: units_parse_bytes/100 pebibytes as pib + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("10e") == 10000000000000000000 + } + note: units_parse_bytes/10 etabytes as e + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("10eb") == 10000000000000000000 + } + note: units_parse_bytes/10 etabytes as eb + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("10ei") == 11529215046068469760 + } + note: units_parse_bytes/10 ebibytes as ei + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse_bytes("10eib") == 11529215046068469760 + } + note: units_parse_bytes/10 ebibytes as eib + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-units-comparisons.yaml b/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-units-comparisons.yaml new file mode 100644 index 000000000000..0aeb8676c597 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-units-comparisons.yaml @@ -0,0 +1,112 @@ +--- +cases: + - data: + modules: + - | + package test + p { + units.parse("8k") > units.parse("7k") + } + note: units_parse/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse("8g") > units.parse("8m") + } + note: units_parse/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse("1234k") < units.parse("1g") + } + note: units_parse/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse("1024") == units.parse("1Ki") + } + note: units_parse/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse("2Mi") == units.parse("2097152") + } + note: units_parse/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse("3Mi") > units.parse("3M") + } + note: units_parse/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse("2Mi") == units.parse("2Mi") + } + note: units_parse/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse("4Mi") > units.parse("4M") + } + note: units_parse/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse("4.1Mi") > units.parse("4Mi") + } + note: units_parse/comparison + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse("128Gi") == units.parse("137438953472") + } + note: units_parse/comparison + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-units-errors.yaml b/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-units-errors.yaml new file mode 100644 index 000000000000..b599f027a7c7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-units-errors.yaml @@ -0,0 +1,86 @@ +--- +cases: + - data: + modules: + - | + package test + p { + units.parse("") + } + note: units_parse/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse: no amount provided" + strict_error: true + - data: + modules: + - | + package test + p { + units.parse("G") + } + note: units_parse/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse: no amount provided" + strict_error: true + - data: + modules: + - | + package test + p { + units.parse("foo") + } + note: units_parse/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse: no amount provided" + strict_error: true + - data: + modules: + - | + package test + p { + units.parse("0.0.0") + } + note: units_parse/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse: could not parse amount to a number" + strict_error: true + - data: + modules: + - | + package test + p { + units.parse(".5.2") + } + note: units_parse/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse: could not parse amount to a number" + strict_error: true + - data: + modules: + - | + package test + p { + units.parse("100 k") + } + note: units_parse/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse: spaces not allowed in resource strings" + strict_error: true + - data: + modules: + - | + package test + p { + units.parse(" 327Mi ") + } + note: units_parse/failure + query: data.test.p = x + want_error_code: eval_builtin_error + want_error: "units.parse: spaces not allowed in resource strings" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-units.yaml b/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-units.yaml new file mode 100644 index 000000000000..bb91a0abb8bd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/units/test-parse-units.yaml @@ -0,0 +1,432 @@ +--- +cases: + - data: + modules: + - | + package test + p { + units.parse("\"100TI\"") == 109951162777600 + } + note: units_parse/removes quotes and lowercases string + query: data.test.p = x + want_result: + - x: true + - data: + modules: + - | + package test + p { + units.parse("0") == 0 + } + note: units_parse/zero + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("0.0") == 0 + } + note: units_parse/zero float + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse(".0") == 0 + } + note: units_parse/zero bare float + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("12345") == 12345 + } + note: units_parse/raw number + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("10K") == 10000 + } + note: units_parse/10 kilo uppercase + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("10KI") == 10240 + } + note: units_parse/10 Ki uppercase + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("10k") == 10000 + } + note: units_parse/10 K lowercase + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("10Ki") == 10240 + } + note: units_parse/10 Ki mixed case + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("200M") == 200000000 + } + note: units_parse/200 mega + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("300Gi") == 322122547200 + } + note: units_parse/300 Gi + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("1.1K") == 1100 + } + note: units_parse/1.1 K floating point + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("1.1Ki") == 1126.4 + } + note: units_parse/1.1 Ki floating point, not rounded + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse(".5K") == 500 + } + note: units_parse/.5 K bare floating point + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100k") == 100000 + } + note: units_parse/100 kilo as k + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100K") == 100000 + } + note: units_parse/100 kilo as K + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100ki") == 102400 + } + note: units_parse/100 kibi as ki + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100Ki") == 102400 + } + note: units_parse/100 kibi as Ki + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + round(units.parse("100m") * 1000) == 100 + } + note: units_parse/100 milli as m + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100M") == 100000000 + } + note: units_parse/100 mega as M + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100mi") == 104857600 + } + note: units_parse/100 mebi as mi + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100Mi") == 104857600 + } + note: units_parse/100 mebi as Mi + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100g") == 100000000000 + } + note: units_parse/100 giga as g + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100gi") == 107374182400 + } + note: units_parse/100 gibi as gi + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100t") == 100000000000000 + } + note: units_parse/100 tera as t + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100T") == 100000000000000 + } + note: units_parse/100 tera as T + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100ti") == 109951162777600 + } + note: units_parse/100 tebi as ti + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100Ti") == 109951162777600 + } + note: units_parse/100 tebi as Ti + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100p") == 100000000000000000 + } + note: units_parse/100 peta as p + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100P") == 100000000000000000 + } + note: units_parse/100 peta as P + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100pi") == 112589990684262400 + } + note: units_parse/100 pebi as pi + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("100Pi") == 112589990684262400 + } + note: units_parse/100 pebi as Pi + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("10e") == 10000000000000000000 + } + note: units_parse/10 eta as e + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("10E") == 10000000000000000000 + } + note: units_parse/10 eta as E + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("10ei") == 11529215046068469760 + } + note: units_parse/10 ebi as ei + query: data.test.p = x + want_result: + - x: true + + - data: + modules: + - | + package test + p { + units.parse("10Ei") == 11529215046068469760 + } + note: units_parse/10 ebi as Ei + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/units/test-units-precision.yaml b/third_party/opa/v1/test/cases/testdata/v0/units/test-units-precision.yaml new file mode 100644 index 000000000000..c42933adb494 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/units/test-units-precision.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: + modules: + - | + package test + p { + json.marshal(units.parse("1G")) == json.marshal(1000000000) + } + note: units_parse/no decimal places for integers + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0939.yaml b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0939.yaml new file mode 100644 index 000000000000..c5bf7503d429 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0939.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + urlquery.encode("a=b+1", x) + } + note: urlbuiltins/encode + query: data.generated.p = x + want_result: + - x: a%3Db%2B1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0940.yaml b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0940.yaml new file mode 100644 index 000000000000..ceb68a18b1b1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0940.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + urlquery.encode("", x) + } + note: urlbuiltins/encode empty + query: data.generated.p = x + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0941.yaml b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0941.yaml new file mode 100644 index 000000000000..bdf2505f9210 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0941.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + urlquery.decode("a%3Db%2B1", x) + } + note: urlbuiltins/decode + query: data.generated.p = x + want_result: + - x: a=b+1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0942.yaml b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0942.yaml new file mode 100644 index 000000000000..14bd63b2c3c6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0942.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + urlquery.encode_object({}, x) + } + note: urlbuiltins/encode_object empty + query: data.generated.p = x + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0943.yaml b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0943.yaml new file mode 100644 index 000000000000..afd0275bf51f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0943.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + urlquery.encode_object({"a": "b", "c": "d"}, x) + } + note: urlbuiltins/encode_object strings + query: data.generated.p = x + want_result: + - x: a=b&c=d diff --git a/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0944.yaml b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0944.yaml new file mode 100644 index 000000000000..69d022db7e05 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0944.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + urlquery.encode_object({"a": "c=b+1"}, x) + } + note: urlbuiltins/encode_object escape + query: data.generated.p = x + want_result: + - x: a=c%3Db%2B1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0945.yaml b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0945.yaml new file mode 100644 index 000000000000..0db7ed8e93b9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0945.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + urlquery.encode_object({"a": ["b+1", "c+2"]}, x) + } + note: urlbuiltins/encode_object array + query: data.generated.p = x + want_result: + - x: a=b%2B1&a=c%2B2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0946.yaml b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0946.yaml new file mode 100644 index 000000000000..9214fe4e7863 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-0946.yaml @@ -0,0 +1,14 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + urlquery.encode_object({"a": {"b+1"}}, x) + } + note: urlbuiltins/encode_object set + query: data.generated.p = x + want_result: + - x: a=b%2B1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-1076.yaml b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-1076.yaml new file mode 100644 index 000000000000..e604b3a8abae --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/urlbuiltins/test-urlbuiltins-1076.yaml @@ -0,0 +1,35 @@ +--- +cases: + - modules: + - | + package decode_object + + p = x { + x = urlquery.decode_object("a=value_a1&b=value_b&a=value_a2") + } + note: urlbuiltins/decode_object multiple + query: data.decode_object.p = x + want_result: + - x: { "a": ["value_a1", "value_a2"], "b": ["value_b"] } + - modules: + - | + package decode_object + + p = x { + x = urlquery.decode_object("a=value_a1&b") + } + note: urlbuiltins/decode_object empty parameter + query: data.decode_object.p = x + want_result: + - x: { "a": ["value_a1"], "b": [""] } + - modules: + - | + package decode_object + + p = x { + x = urlquery.decode_object("") + } + note: urlbuiltins/decode_object empty string + query: data.decode_object.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/uuid/test-uuid-input-formats.yaml b/third_party/opa/v1/test/cases/testdata/v0/uuid/test-uuid-input-formats.yaml new file mode 100644 index 000000000000..4f1a43107d6e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/uuid/test-uuid-input-formats.yaml @@ -0,0 +1,52 @@ +--- +cases: + - note: uuid-parse/positive-v4-braces + data: {} + modules: + - | + package test + + p = uuid.parse(input.userid) + + input: { "userid": "{00000000-0000-4000-8000-000000000000}" } + query: data.test.p = x + want_result: + - x: + version: 4 + variant: "RFC4122" + + - note: uuid-parse/positive-v2-urn + data: {} + modules: + - | + package test + + p = uuid.parse(input.userid) + + input: { "userid": "urn:uuid:000003e8-48b9-21ee-b200-325096b39f47" } + query: data.test.p = x + want_result: + - x: + version: 2 + variant: "RFC4122" + nodeid: "32-50-96-b3-9f-47" + macvariables: "local:unicast" + time: 1693566990121469600 + clocksequence: 12800 + domain: "Person" + id: 1000 + + - note: uuid-parse/positive-v3-no-dashes + data: {} + modules: + - | + package test + + p = uuid.parse(input.userid) + + input: { "userid": "6bea8ef2d3d33cd184e09bab06a52ece" } + query: data.test.p = x + want_result: + - x: + version: 3 + variant: "RFC4122" diff --git a/third_party/opa/v1/test/cases/testdata/v0/uuid/test-uuid-parse-rule.yaml b/third_party/opa/v1/test/cases/testdata/v0/uuid/test-uuid-parse-rule.yaml new file mode 100644 index 000000000000..a5b99268497e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/uuid/test-uuid-parse-rule.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: uuid-parse-rule/positive + data: {} + modules: + - | + package test + + validuser { + is_string(input.userid) + parsed_uuid := uuid.parse(input.userid) + parsed_uuid.variant == "RFC4122" + parsed_uuid.version == 4 + } + + input: { "userid": "00000000-0000-4000-8000-000000000000" } + query: data.test.validuser = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/uuid/test-uuid-parse.yaml b/third_party/opa/v1/test/cases/testdata/v0/uuid/test-uuid-parse.yaml new file mode 100644 index 000000000000..69513416ad9c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/uuid/test-uuid-parse.yaml @@ -0,0 +1,64 @@ +--- +cases: + - note: uuid-parse/positive-v4 + data: {} + modules: + - | + package test + + p = uuid.parse(input.userid) + + input: { "userid": "00000000-0000-4000-8000-000000000000" } + query: data.test.p = x + want_result: + - x: + version: 4 + variant: "RFC4122" + + - note: uuid-parse/positive-v2 + data: {} + modules: + - | + package test + + p = uuid.parse(input.userid) + + input: { "userid": "000003e8-48b9-21ee-b200-325096b39f47" } + query: data.test.p = x + want_result: + - x: + version: 2 + variant: "RFC4122" + nodeid: "32-50-96-b3-9f-47" + macvariables: "local:unicast" + time: 1693566990121469600 + clocksequence: 12800 + domain: "Person" + id: 1000 + + - note: uuid-parse/positive-v3 + data: {} + modules: + - | + package test + + p = uuid.parse(input.userid) + + input: { "userid": "38074da4-0b00-388d-9c3c-362de965547a" } + query: data.test.p = x + want_result: + - x: + version: 3 + variant: "RFC4122" + + - note: uuid-parse/negative + data: {} + modules: + - | + package test + + p = uuid.parse(input.userid) + + input: { "userid": "123" } + query: data.test.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0726.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0726.yaml new file mode 100644 index 000000000000..40fbe01d6ef5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0726.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + v = [[1, 2], [2, 3], [3, 4]] + x = v[2][1] + } + note: varreferences/ground + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0727.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0727.yaml new file mode 100644 index 000000000000..cdf30a40d156 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0727.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + v = [[1, 2], [2, 3], [3, 4]] + x = v[i][j] + } + note: varreferences/non-ground + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0728.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0728.yaml new file mode 100644 index 000000000000..0f492bc5dcd6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0728.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] = y { + v = [{"a": 1, "b": 2}, {"c": 3, "z": [4]}] + y = v[i][x][j] + } + note: varreferences/mixed + query: data.generated.p = x + want_result: + - x: + z: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0729.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0729.yaml new file mode 100644 index 000000000000..ae187e244115 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0729.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p[x] { + v = data.c[i][j] + x = v[k] + x = true + } + note: varreferences/ref binding + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0730.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0730.yaml new file mode 100644 index 000000000000..9da7b570a37c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0730.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = x { + q = data.a + q[0] = x + q[0] + } + note: varreferences/existing ref binding + query: data.generated.p = x + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0731.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0731.yaml new file mode 100644 index 000000000000..587f84edbe8a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0731.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + v = [1, 2, 3] + __local0__ = v[i] + x = [{"a": __local0__}] + } + note: varreferences/embedded + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - a: 1 + - - a: 2 + - - a: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0732.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0732.yaml new file mode 100644 index 000000000000..f93f15c3f3ad --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0732.yaml @@ -0,0 +1,34 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p[x] { + v = data.c[i][j] + __local0__ = v[0] + __local1__ = v[1] + w = [__local0__, __local1__] + x = w[y] + } + note: varreferences/embedded ref binding + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - null + - false + - true + - 3.14159 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0733.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0733.yaml new file mode 100644 index 000000000000..3368978008e9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0733.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + i = [1, 2, 3, 4] + j = [1, 2, 999] + j[k] = y + i[y] = x + } + note: "varreferences/array: ground var" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0734.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0734.yaml new file mode 100644 index 000000000000..33f71efd462e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0734.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[y] { + i = [1, 2, 3, 4] + x = data.a[_] + i[x] = y + } + note: "varreferences/array: ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0735.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0735.yaml new file mode 100644 index 000000000000..ed5fd9910196 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0735.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + i = {"a": 1, "b": 2, "c": 3} + j = ["a", "c", "deadbeef"] + j[k] = y + i[y] = x + } + note: "varreferences/object: ground var" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0736.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0736.yaml new file mode 100644 index 000000000000..ce58da4b12c5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0736.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + numbers: + - "1" + - "2" + - "3" + - "4" + modules: + - | + package generated + + p[y] { + i = {"1": 1, "2": 2, "4": 4} + x = data.numbers[_] + i[x] = y + } + note: "varreferences/object: ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0737.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0737.yaml new file mode 100644 index 000000000000..854d2c5a19a8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0737.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + i = {1, 2, 3, 4} + j = {1, 2, 99} + j[x] + i[x] + } + note: "varreferences/set: ground var" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0738.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0738.yaml new file mode 100644 index 000000000000..ce396b2db1cf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0738.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + i = {1, 2, 3, 4} + x = data.a[_] + i[x] + } + note: "varreferences/set: ref" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0739.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0739.yaml new file mode 100644 index 000000000000..215f0155be90 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0739.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + v = {[1, 999], [3, 4]} + __local0__ = data.a[2] + pair = [__local0__, 4] + v[pair] + } + note: "varreferences/set: lookup: base docs" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0740.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0740.yaml new file mode 100644 index 000000000000..d220f36a4f9f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0740.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + x = [{}, {[1, 2], [3, 4]}] + y = [3, 4] + x[i][y] + } + note: "varreferences/set: lookup: embedded" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0741.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0741.yaml new file mode 100644 index 000000000000..922814c0d4be --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0741.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[[i, z, r]] { + x = [{}, {[1, 2], [3, 4]}] + y = [3, 4] + x[i][y][z] = r + } + note: "varreferences/set: lookup: dereference" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 1 + - 0 + - 3 + - - 1 + - 1 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0742.yaml b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0742.yaml new file mode 100644 index 000000000000..ca156cfcf2bd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/varreferences/test-varreferences-0742.yaml @@ -0,0 +1,15 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + somevar = [1, 2, 3] + somevar[i] = 2 + } + note: varreferences/avoids indexer + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0620.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0620.yaml new file mode 100644 index 000000000000..61815ebb7d25 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0620.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.generated.q[1] + } + + q[x] { + data.a[i] = x + } + note: "virtualdocs/input: set 1" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0621.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0621.yaml new file mode 100644 index 000000000000..f25b9db93fa9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0621.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + data.generated.q[1] = x + } + + q[x] { + data.a[i] = x + } + note: "virtualdocs/input: set 2" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0622.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0622.yaml new file mode 100644 index 000000000000..54d5e81d2bf8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0622.yaml @@ -0,0 +1,27 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.generated.q[2] + x = {"b": [__local0__]} + } + + q[x] { + data.a[i] = x + } + note: "virtualdocs/input: set embedded" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - b: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0623.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0623.yaml new file mode 100644 index 000000000000..caa1d9f217ba --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0623.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q[1000] + } + + q[x] { + data.a[x] = y + } + note: "virtualdocs/input: set undefined" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0624.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0624.yaml new file mode 100644 index 000000000000..fc3967c3b899 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0624.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p = y { + x = [1] + data.generated.q[x][0] = y + } + + q[[x]] { + data.a[_] = x + } + note: "virtualdocs/input: set dereference" + query: data.generated.p = x + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0625.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0625.yaml new file mode 100644 index 000000000000..0558386a944b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0625.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + x = 1 + data.generated.q[x] + } + + q[y] { + data.a[y] = i + } + note: "virtualdocs/input: set ground var" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0626.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0626.yaml new file mode 100644 index 000000000000..cfbeedd3550c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0626.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + z = [[1, 2], 2] + data.generated.q[z] + } + + q[[x, y]] { + y = 2 + x = [1, y] + } + note: "virtualdocs/input: set ground composite (1)" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0627.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0627.yaml new file mode 100644 index 000000000000..4120ee4fefcb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0627.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + y = 2 + z = [[1, y], y] + data.generated.q[z] + } + + q[[x, y]] { + y = 2 + x = [1, y] + } + note: "virtualdocs/input: set ground composite (2)" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0628.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0628.yaml new file mode 100644 index 000000000000..4f46f4a32385 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0628.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + y = 2 + x = [1, y] + z = [x, y] + data.generated.q[z] + } + + q[[x, y]] { + y = 2 + x = [1, y] + } + note: "virtualdocs/input: set ground composite (3)" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0629.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0629.yaml new file mode 100644 index 000000000000..41c5c294e167 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0629.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[u] { + y = 2 + data.generated.q[z] + z = [x, y] + x = [1, u] + } + + q[[x, y]] { + y = 2 + x = [1, y] + } + note: "virtualdocs/input: set partially ground composite" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0630.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0630.yaml new file mode 100644 index 000000000000..eaba9f2c2c66 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0630.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.generated.q[1] = 2 + } + + q[i] = x { + data.a[i] = x + } + note: "virtualdocs/input: object 1" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0631.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0631.yaml new file mode 100644 index 000000000000..68e1587a1301 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0631.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.generated.q[1] = 0 + } + + q[x] = i { + data.a[i] = x + } + note: "virtualdocs/input: object 2" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0632.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0632.yaml new file mode 100644 index 000000000000..b3d602df86b2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0632.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.generated.q[3] + __local1__ = data.generated.q[2] + x = [1, __local0__, __local1__] + } + + q[i] = x { + data.a[i] = x + } + note: "virtualdocs/input: object embedded 1" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 1 + - 4 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0633.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0633.yaml new file mode 100644 index 000000000000..859a2fb08cff --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0633.yaml @@ -0,0 +1,30 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.generated.q[3] + __local1__ = data.generated.q[2] + x = {"a": [__local0__], "b": [__local1__]} + } + + q[i] = x { + data.a[i] = x + } + note: "virtualdocs/input: object embedded 2" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - a: + - 4 + b: + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0634.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0634.yaml new file mode 100644 index 000000000000..20fa16d6da32 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0634.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.generated.q[1] = 9999 + } + + q[i] = x { + data.a[i] = x + } + note: "virtualdocs/input: object undefined val" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0635.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0635.yaml new file mode 100644 index 000000000000..41a4d1e94618 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0635.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.generated.q[9999] = 2 + } + + q[i] = x { + data.a[i] = x + } + note: "virtualdocs/input: object undefined key 1" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0636.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0636.yaml new file mode 100644 index 000000000000..6294c228f955 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0636.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.generated.q.foo = 2 + } + + q[i] = x { + data.a[i] = x + } + note: "virtualdocs/input: object undefined key 2" + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0637.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0637.yaml new file mode 100644 index 000000000000..32cc3fe3718e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0637.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p { + data.generated.q[0].x[1] = false + } + + q[i] = x { + x = data.c[i] + } + note: "virtualdocs/input: object dereference ground" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0638.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0638.yaml new file mode 100644 index 000000000000..8889f7f9a28e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0638.yaml @@ -0,0 +1,30 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[v] { + x = "a" + data.generated.q[x][y] = v + } + + q[k] = v { + k = "a" + v = data.a + } + note: "virtualdocs/input: object dereference ground 2" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0639.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0639.yaml new file mode 100644 index 000000000000..51e0bb1c6fe7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0639.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p { + data.generated.q[0][x][y] = false + } + + q[i] = x { + x = data.c[i] + } + note: "virtualdocs/input: object defererence non-ground" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0640.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0640.yaml new file mode 100644 index 000000000000..c144be730867 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0640.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[y] { + x = "b" + data.generated.q[x] = y + } + + q[k] = v { + x = {"a": 1, "b": 2} + x[k] = v + } + note: "virtualdocs/input: object ground var key" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0641.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0641.yaml new file mode 100644 index 000000000000..2b152db5dd0f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0641.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[y] { + x = 1 + data.generated.q[x] = y + } + + q[k] = v { + x = {1: 3, 2: 1} + x[k] = v + } + note: "virtualdocs/input: object non-string key" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0642.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0642.yaml new file mode 100644 index 000000000000..e6dacd483365 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0642.yaml @@ -0,0 +1,28 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] = y { + data.generated.r[z] = y + data.generated.q[x] = z + } + + r[k] = v { + x = {"a": 1, "b": 2, "c": 3, "d": 4} + x[k] = v + } + + q[y] = x { + z = {"a": "a", "b": "b", "d": "d"} + z[y] = x + } + note: "virtualdocs/input: variable binding substitution" + query: data.generated.p = x + want_result: + - x: + a: 1 + b: 2 + d: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0643.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0643.yaml new file mode 100644 index 000000000000..21c2faef254f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0643.yaml @@ -0,0 +1,28 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + data.generated.q[x] + } + + q[y] { + data.a[i] = y + } + note: "virtualdocs/output: set" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0644.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0644.yaml new file mode 100644 index 000000000000..94681f47cfe2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0644.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + d: + e: + - bar + - baz + modules: + - | + package generated + + p[i] { + __local0__ = data.generated.q[i] + {i: [i]} = {i: [__local0__]} + } + + q[x] { + data.d.e[i] = x + } + note: "virtualdocs/output: set embedded" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - bar + - baz diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0645.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0645.yaml new file mode 100644 index 000000000000..e872bb1fda21 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0645.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.generated.q[x] + } + + q[y] { + i = 1 + j = 2 + y = [i, j] + } + note: "virtualdocs/output: set var binding" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0646.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0646.yaml new file mode 100644 index 000000000000..2fc206003db7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0646.yaml @@ -0,0 +1,28 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[y] { + data.generated.q[x][0] = y + } + + q[[x]] { + data.a[_] = x + } + note: "virtualdocs/output: set dereference" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0647.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0647.yaml new file mode 100644 index 000000000000..8a269a68c1ed --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0647.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[y] { + data.generated.q[i][j][k][x] = y + } + + q[{{[1], [2]}, {[3], [4]}}] + note: "virtualdocs/output: set dereference deep" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0648.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0648.yaml new file mode 100644 index 000000000000..0b8e77198c51 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0648.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: + modules: + - | + package generated + + p[x] { + data.generated.q[x] + } + + q = {0, "", false, null, [], {}, set()} + note: "virtualdocs/output: set falsy values" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - null + - 0 + - "" + - [] + - [] + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0649.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0649.yaml new file mode 100644 index 000000000000..a159ed73fbc6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0649.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + data.generated.q[x] = 4 + } + + q[i] = x { + data.a[i] = x + } + note: "virtualdocs/output: object key" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0650.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0650.yaml new file mode 100644 index 000000000000..bb956822edb1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0650.yaml @@ -0,0 +1,27 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + data.generated.q[x] = 1 + } + + q[k] = 1 { + data.a[_] = k + k < 3 + } + note: "virtualdocs/output: object non-string key" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0651.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0651.yaml new file mode 100644 index 000000000000..9eb05c688da8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0651.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[x] = y { + data.generated.q[x] = y + } + + q[k] = v { + data.b[k] = v + } + note: "virtualdocs/output: object value" + query: data.generated.p = x + want_result: + - x: + v1: hello + v2: goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0652.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0652.yaml new file mode 100644 index 000000000000..d340c96f3eba --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0652.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[k] = v { + __local0__ = data.generated.q[k] + {k: [__local0__]} = {k: [v]} + } + + q[x] = y { + data.b[x] = y + } + note: "virtualdocs/output: object embedded" + query: data.generated.p = x + want_result: + - x: + v1: hello + v2: goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0653.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0653.yaml new file mode 100644 index 000000000000..958917d5921e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0653.yaml @@ -0,0 +1,31 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p[i] { + data.generated.q[i].x[1] = false + } + + q[i] = x { + x = data.c[i] + } + note: "virtualdocs/output: object dereference ground" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0654.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0654.yaml new file mode 100644 index 000000000000..b954e5271d15 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0654.yaml @@ -0,0 +1,37 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p[r] { + data.generated.q[x][y][z] = false + r = [x, y, z] + } + + q[i] = x { + x = data.c[i] + } + note: "virtualdocs/output: object defererence non-ground" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 0 + - x + - 1 + - - 0 + - z + - q diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0655.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0655.yaml new file mode 100644 index 000000000000..f082835db525 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0655.yaml @@ -0,0 +1,44 @@ +--- +cases: + - data: + d: + e: + - bar + - baz + l: + - a: bob + b: -1 + c: + - 1 + - 2 + - 3 + - 4 + - a: alice + b: 1 + c: + - 2 + - 3 + - 4 + - 5 + d: null + modules: + - | + package generated + + p[x] { + data.generated.q[_0][0].c[_1] = x + } + + q[k] = v { + data.d.e[_0] = k + v = [r | r = data.l[_1]] + } + note: "virtualdocs/output: object dereference array of refs" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0656.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0656.yaml new file mode 100644 index 000000000000..651375468301 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0656.yaml @@ -0,0 +1,45 @@ +--- +cases: + - data: + d: + e: + - bar + - baz + l: + - a: bob + b: -1 + c: + - 1 + - 2 + - 3 + - 4 + - a: alice + b: 1 + c: + - 2 + - 3 + - 4 + - 5 + d: null + modules: + - | + package generated + + p[x] { + data.generated.q[_0].x[0].c[_1] = x + } + + q[k] = v { + data.d.e[_0] = k + __local0__ = [r | r = data.l[_1]] + v = {"x": __local0__} + } + note: "virtualdocs/output: object dereference array of refs within object" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0657.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0657.yaml new file mode 100644 index 000000000000..906641434183 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0657.yaml @@ -0,0 +1,39 @@ +--- +cases: + - data: + d: + e: + - bar + - baz + l: + - a: bob + b: -1 + c: + - 1 + - 2 + - 3 + - 4 + - a: alice + b: 1 + c: + - 2 + - 3 + - 4 + - 5 + d: null + modules: + - | + package generated + + p { + data.generated.q.bar[1].alice[0] = 1 + } + + q[k] = v { + data.d.e[_] = k + v = [x | __local0__ = data.l[_].a; x = {__local0__: [1]}] + } + note: "virtualdocs/output: object dereference object with key refs" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0658.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0658.yaml new file mode 100644 index 000000000000..4e8f7476d606 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0658.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[z] { + data.generated.q[x] = y + z = [x, y] + } + + q[k] = v { + x = "a" + y = "b" + k = "foo" + v = [x, y] + } + note: "virtualdocs/output: object var binding" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - foo + - - a + - b diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0659.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0659.yaml new file mode 100644 index 000000000000..cbf65a729848 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0659.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[z] { + data.generated.q[x] = y + z = [x, y] + } + + q[k] = v { + x = "a" + v = "foo" + y = x + k = y + } + note: "virtualdocs/output: object key var binding" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - a + - foo diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0660.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0660.yaml new file mode 100644 index 000000000000..0943a37a3a96 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0660.yaml @@ -0,0 +1,27 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[[x, y]] { + data.generated.q[x] = 1 + data.generated.q[y] = x + } + + q[x] = i { + data.a[i] = x + } + note: "virtualdocs/object: self-join" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0661.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0661.yaml new file mode 100644 index 000000000000..4410fb9694b5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0661.yaml @@ -0,0 +1,27 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.generated.q[x] = data.generated.r[x] + } + + q[x] = y { + z = {"a": 1, "b": 2, "d": 4} + z[x] = y + } + + r[k] = v { + x = {"a": 1, "b": 2, "c": 4, "d": 3} + x[k] = v + } + note: "virtualdocs/i/o: objects" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - a + - b diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0662.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0662.yaml new file mode 100644 index 000000000000..50be7ae3177f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0662.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[y] { + data.generated.q[x] + data.generated.r[x] = y + } + + q[x] { + z = ["a", "b", "c", "d"] + z[y] = x + } + + r[k] = v { + x = {"a": 1, "b": 2, "d": 4} + x[k] = v + } + note: "virtualdocs/i/o: undefined keys" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0663.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0663.yaml new file mode 100644 index 000000000000..e0d1f9cab5fd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0663.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q[1] = 2 + } + + q = [1, 2, 3, 4] + note: "virtualdocs/input: complete array" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0664.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0664.yaml new file mode 100644 index 000000000000..9d5630a6b5ae --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0664.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q.b = 2 + } + + q = {"a": 1, "b": 2} + note: "virtualdocs/input: complete object" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0665.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0665.yaml new file mode 100644 index 000000000000..26a8e9376bf7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0665.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q[3] + } + + q = {1, 2, 3, 4} + note: "virtualdocs/input: complete set" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0666.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0666.yaml new file mode 100644 index 000000000000..1be9378a8753 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0666.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q[1][1] = 3 + } + + q = [[0, 1], [2, 3]] + note: "virtualdocs/input: complete array dereference ground" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0667.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0667.yaml new file mode 100644 index 000000000000..065279f36473 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0667.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q.b[1] = 4 + } + + q = {"a": [1, 2], "b": [3, 4]} + note: "virtualdocs/input: complete object dereference ground" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0668.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0668.yaml new file mode 100644 index 000000000000..8bbcd807115e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0668.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + z = [1, 2] + z[i] = y + data.generated.q[y] = x + } + + q = [1, 2, 3, 4] + note: "virtualdocs/input: complete array ground index" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0669.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0669.yaml new file mode 100644 index 000000000000..4c12e0b49173 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0669.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + z = ["b", "c"] + z[i] = y + data.generated.q[y] = x + } + + q = {"a": 1, "b": 2, "c": 3, "d": 4} + note: "virtualdocs/input: complete object ground key" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0670.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0670.yaml new file mode 100644 index 000000000000..ec9bdcd3f7ea --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0670.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q[1][1] = 2 + } + + q = [{"x": x, "y": y}, z] { + x = 1 + y = 2 + z = [1, 2, 3] + } + note: "virtualdocs/input: complete vars" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0671.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0671.yaml new file mode 100644 index 000000000000..afd4c9d3759f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0671.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.generated.q[i] = e + x = [i, e] + } + + q = [1, 2, 3, 4] + note: "virtualdocs/output: complete array" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 0 + - 1 + - - 1 + - 2 + - - 2 + - 3 + - - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0672.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0672.yaml new file mode 100644 index 000000000000..698ce4b471be --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0672.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.generated.q[i] = e + x = [i, e] + } + + q = {"a": 1, "b": 2} + note: "virtualdocs/output: complete object" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - a + - 1 + - - b + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0673.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0673.yaml new file mode 100644 index 000000000000..711d289282b5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0673.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.generated.q[x] + } + + q = {1, 2, 3, 4} + note: "virtualdocs/output: complete set" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0674.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0674.yaml new file mode 100644 index 000000000000..24f5460540d3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0674.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[r] { + data.generated.q[i][j] = 2 + r = [i, j] + } + + q = [[1, 2], [3, 2]] + note: "virtualdocs/output: complete array dereference non-ground" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 0 + - 1 + - - 1 + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0675.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0675.yaml new file mode 100644 index 000000000000..696c88d0485a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0675.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[r] { + data.generated.q[x][y] = 2 + r = [x, y] + } + + q = {"a": {"x": 1}, "b": {"y": 2}, "c": {"z": 2}} + note: "virtualdocs/output: complete object defererence non-ground" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - b + - "y" + - - c + - z diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0676.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0676.yaml new file mode 100644 index 000000000000..02353a8aeb9f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0676.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.generated.q[_][_] = x + } + + q = [{"x": x, "y": y}, z] { + x = 1 + y = 2 + z = [1, 2, 3] + } + note: "virtualdocs/output: complete vars" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0677.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0677.yaml new file mode 100644 index 000000000000..66b62e9dac12 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0677.yaml @@ -0,0 +1,16 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q + } + + q = true + note: "virtualdocs/no suffix: complete" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0678.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0678.yaml new file mode 100644 index 000000000000..b3892619aecb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0678.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q + } + + q = x { + x = true + } + note: "virtualdocs/no suffix: complete vars" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0679.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0679.yaml new file mode 100644 index 000000000000..8bb2097292ca --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0679.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q + } + + q = false + + q = true + note: "virtualdocs/no suffix: complete incr (error)" + query: data.generated.p = x + want_error: complete rules must not produce multiple outputs + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0680.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0680.yaml new file mode 100644 index 000000000000..5cb88dc3d8fa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0680.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + not data.generated.q + } + + q { + false + } + + q = false + note: "virtualdocs/no suffix: complete incr" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0681.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0681.yaml new file mode 100644 index 000000000000..74c5592fb331 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0681.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[x] = y { + data.generated.q = o + o[x] = y + } + + q[x] = y { + data.b[x] = y + } + note: "virtualdocs/no suffix: object" + query: data.generated.p = x + want_result: + - x: + v1: hello + v2: goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0682.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0682.yaml new file mode 100644 index 000000000000..3dcc4da14615 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0682.yaml @@ -0,0 +1,34 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + d: + e: + - bar + - baz + modules: + - | + package generated + + p[x] = y { + data.generated.q = o + o[x] = y + } + + q[x] = y { + data.b[x] = y + } + + q[x1] = y1 { + data.d.e[y1] = x1 + } + note: "virtualdocs/no suffix: object incr" + query: data.generated.p = x + want_result: + - x: + bar: 0 + baz: 1 + v1: hello + v2: goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0683.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0683.yaml new file mode 100644 index 000000000000..9a0a2c95d4c0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0683.yaml @@ -0,0 +1,34 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q = x + x[i] = 4 + } + + q[k] = v { + data.generated.r = x + x[k] = v + } + + r[k] = v { + data.generated.s = x + x[k] = v + } + + r[k] = v { + data.generated.t = x + x[v] = k + } + + s = {"a": 1, "b": 2, "c": 4} + + t = ["d", "e", "g"] + note: "virtualdocs/no suffix: chained" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0684.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0684.yaml new file mode 100644 index 000000000000..24a57e243887 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0684.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.generated.q = x + } + + q[k] = v { + i = "a" + j = 1 + v = [i, j] + k = i + } + note: "virtualdocs/no suffix: object var binding" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - a: + - a + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0685.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0685.yaml new file mode 100644 index 000000000000..958992837c8d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0685.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.generated.q = x + } + + q[k] = {"v": v} { + i = "a" + j = 1 + v = [i, j] + k = i + } + note: "virtualdocs/no suffix: object composite value" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - a: + v: + - a + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0686.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0686.yaml new file mode 100644 index 000000000000..fe6167b87d13 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0686.yaml @@ -0,0 +1,30 @@ +--- +cases: + - data: + c: + - x: + - true + - false + - foo + y: + - null + - 3.14159 + z: + p: true + q: false + modules: + - | + package generated + + p { + data.generated.q + data.generated.q + } + + q = x { + x = data.c[0].z.p + } + note: "virtualdocs/no suffix: bound ref with long prefix (#238)" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0687.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0687.yaml new file mode 100644 index 000000000000..92f353aafd55 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0687.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] = y { + xs = ["a", "b", "c", "a"] + x = xs[i] + y = data.a[i] + } + note: "virtualdocs/no suffix: object conflict (error)" + query: data.generated.p = x + want_error: object keys must be unique + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0688.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0688.yaml new file mode 100644 index 000000000000..6e0072ea123a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0688.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + data.generated.q = s + s[x] + } + + q[x] { + data.a[i] = x + } + note: "virtualdocs/no suffix: set" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0689.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0689.yaml new file mode 100644 index 000000000000..03738a524947 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0689.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[1] { + data.a[0] = 100 + } + note: virtualdocs/empty partial set + query: data.generated.p = x + sort_bindings: true + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0690.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0690.yaml new file mode 100644 index 000000000000..c207922c3915 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0690.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p["x"] = 1 { + data.a[0] = 100 + } + note: virtualdocs/empty partial object + query: data.generated.p = x + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0691.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0691.yaml new file mode 100644 index 000000000000..641c14883a03 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0691.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + data.generated.q.a.b = x + } + + q = {x: {y: 1}} { + x = "a" + y = "b" + } + note: "virtualdocs/input: non-ground object keys" + query: data.generated.p = x + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0692.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0692.yaml new file mode 100644 index 000000000000..bc3fa9836741 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0692.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.generated.q.c + } + + q = {x, "b", z} { + x = "a" + z = "c" + } + note: "virtualdocs/input: non-ground set elements" + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0693.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0693.yaml new file mode 100644 index 000000000000..ad04d52831cc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0693.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.generated.q[i][j] = x + } + + q = {x: {x1: 1}, y: {y1: 2}} { + x = "a" + y = "b" + x1 = "a1" + y1 = "b1" + } + note: "virtualdocs/output: non-ground object keys" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0694.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0694.yaml new file mode 100644 index 000000000000..c45ec1ee98ff --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-0694.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.generated.q[x] + } + + q = {x, "b", z} { + x = "a" + z = "c" + } + note: "virtualdocs/output: non-ground set elements" + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - a + - b + - c diff --git a/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-undefined.yaml b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-undefined.yaml new file mode 100644 index 000000000000..75e415b62fee --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/virtualdocs/test-virtualdocs-undefined.yaml @@ -0,0 +1,46 @@ +--- +cases: + - data: + modules: + - | + package test + + p { + [1, 2, input] + } else = false { true } + note: "virtualdocs/undefined: in array literal" + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + + p { + {1, 2, input} + } else = false { true } + note: "virtualdocs/undefined: in set literal" + query: data.test.p = x + want_result: + - x: false + - data: + modules: + - | + package test + + p = {1 | input} + note: "virtualdocs/undefined: in set coprehension body" + query: data.test.p = x + want_result: + - x: [] + - data: + modules: + - | + package test + + p = [1 | input] + note: "virtualdocs/undefined: in array coprehension body" + query: data.test.p = x + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0970.yaml b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0970.yaml new file mode 100644 index 000000000000..76ce923a7f21 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0970.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.a[0] + walk(__local0__, x) + } + note: walkbuiltin/scalar + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - [] + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0971.yaml b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0971.yaml new file mode 100644 index 000000000000..3ee65fa1a65a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0971.yaml @@ -0,0 +1,34 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + __local0__ = data.a + walk(__local0__, x) + } + note: walkbuiltin/arrays + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - [] + - - 1 + - 2 + - 3 + - 4 + - - - 0 + - 1 + - - - 1 + - 2 + - - - 2 + - 3 + - - - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0972.yaml b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0972.yaml new file mode 100644 index 000000000000..ef9387c1320f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0972.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p[x] { + __local0__ = data.b + walk(__local0__, x) + } + note: walkbuiltin/objects + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - [] + - v1: hello + v2: goodbye + - - - v1 + - hello + - - - v2 + - goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0973.yaml b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0973.yaml new file mode 100644 index 000000000000..f90ec921f2fa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0973.yaml @@ -0,0 +1,43 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + __local0__ = data.generated.q + walk(__local0__, x) + } + + q = {{1, 2, 3}} + note: walkbuiltin/sets + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - [] + - - - 1 + - 2 + - 3 + - - - - 1 + - 2 + - 3 + - - 1 + - 2 + - 3 + - - - - 1 + - 2 + - 3 + - 1 + - 1 + - - - - 1 + - 2 + - 3 + - 2 + - 2 + - - - - 1 + - 2 + - 3 + - 3 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0974.yaml b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0974.yaml new file mode 100644 index 000000000000..6bba86182205 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0974.yaml @@ -0,0 +1,30 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[[k, x]] { + __local0__ = data.generated.q + walk(__local0__, [k, x]) + __local1__ = k[1] + contains(__local1__, "oo") + } + + q = [{ + "foo": 1, + "bar": 2, + "bazoo": 3, + }] + note: walkbuiltin/match and filter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - - 0 + - bazoo + - 3 + - - - 0 + - foo + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0975.yaml b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0975.yaml new file mode 100644 index 000000000000..3af4e2293662 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-0975.yaml @@ -0,0 +1,33 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[[k1, k2, x]] { + __local0__ = data.generated.q + walk(__local0__, [["a", k1, "b", k2], x]) + } + + q = {"a": [ + {"b": {"foo": 1, "bar": 2}}, + {"b": {"baz": 3, "qux": 4}}, + ]} + note: walkbuiltin/partially ground path + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - 0 + - bar + - 2 + - - 0 + - foo + - 1 + - - 1 + - baz + - 3 + - - 1 + - qux + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-wildcard-path.yaml b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-wildcard-path.yaml new file mode 100644 index 000000000000..33ab09c18697 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/walkbuiltin/test-walkbuiltin-wildcard-path.yaml @@ -0,0 +1,29 @@ +--- +cases: + - modules: + - | + package testing + + obj := { + "bar": "baz", + "qux": [ + 1, + {"p": "rego", "q": "rules"}, + {1, 2, 3, {"a": "b", "c": {"d", "e", 1}}} + ] + } + + with_path[value] { + walk(obj, [path, value]) + } + + without_path[value] { + walk(obj, [_, value]) + } + + same_values := with_path == without_path + + note: "walkbuiltin/wildcard-path same values as when path provided" + query: x = data.testing.same_values + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-and-ndbcache-issue.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-and-ndbcache-issue.yaml new file mode 100644 index 000000000000..bee7996659ff --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-and-ndbcache-issue.yaml @@ -0,0 +1,17 @@ +--- +cases: + - modules: + - | + package rules + + p { + time.now_ns(now) + } + + q { p with data.x as 7 } + note: "with: ndb_cache-issue" + query: data.rules = x + want_result: + - x: + p: true + q: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-builtin-mock.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-builtin-mock.yaml new file mode 100644 index 000000000000..9e002905ca13 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-builtin-mock.yaml @@ -0,0 +1,452 @@ +--- +cases: + - modules: + - | + package test + + f() = 1 + p = y { + y = time.now_ns() with time.now_ns as f + } + note: "withkeyword/builtin: direct call, arity 0" + query: data.test.p = x + want_result: + - x: 1 + - modules: + - | + package test + + f(_) = 1 + p = y { + y = count([1,2,3]) with count as f + } + note: "withkeyword/builtin: direct call, arity 1" + query: data.test.p = x + want_result: + - x: 1 + - modules: + - | + package test + + f(_) = 1 + p { + q with count as f + } + q { + count([1,2,3]) == 1 + } + note: "withkeyword/builtin: indirect call, arity 1" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + f() = 1 + p { + q with time.now_ns as f + } + q { + time.now_ns() == 1 + } + note: "withkeyword/builtin: indirect call, arity 0" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + f() = 1 + p { + q with time.now_ns as f + not q + } + q { + time.now_ns() == 1 + } + note: "withkeyword/builtin: indirect call, arity 0, rule queried with and without mock" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + f() = 1 + p { + q with time.now_ns as f + } + q { + time.now_ns() == 1 + } + note: "withkeyword/builtin: indirect call, arity 0, query package" + query: data.test = x + want_result: + - x: + f: 1 + p: true + - modules: + - | + package test + import future.keywords.in + + pass_resp = {"body": {"roles": ["admin"]}} + deny_resp = {"body": {"roles": []}} + mock_http_send(req) = pass_resp { + req.body.name == "alice" + } else = deny_resp + + test_allow { + allow with http.send as mock_http_send with input.name as "alice" + } + + allow { + "admin" in http.send({"method": "GET", "body": input}).body.roles + } + note: "withkeyword/builtin: http.send example" + query: data.test.test_allow = x + want_result: + - x: true + - modules: + - | + package test + import future.keywords.in + + pass_resp = {"body": {"jwt": "myjot"}} + deny_resp = {"body"} + mock_http_send(req) = pass_resp { + req.body.name == "alice" + } else = deny_resp + + mock_decode_verify("myjot", _) = [true, {}, {"owner": "alice"}] + + test_allow { + allow + with data.verification.cert as "cert" + with input.name as "alice" + with http.send as mock_http_send + with io.jwt.decode_verify as mock_decode_verify + } + + allow { + payload.owner == input.name + } + + claims[k] = v { + resp := http.send({"method": "GET", "body": input}).body + some k, v in resp + } + payload = p { + some p + [true, _, p] = io.jwt.decode_verify(claims.jwt, { "cert": data.verification.cert, "iss": "issuer"}) + } + note: "withkeyword/builtin: nested, multiple mocks" + query: data.test.test_allow = x + want_result: + - x: true + - modules: + - | + package test + + f() = 1 + p { + q with time.now_ns as f + } + q { + valid_time(1) + } + valid_time(x) { time.now_ns() == x } + note: "withkeyword/builtin: indirect call through function" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + f() = 1 + p { + q with time.now_ns as f + not q + } + q { + valid_time(1) + } + valid_time(x) { time.now_ns() == x } + note: "withkeyword/builtin: indirect call through function, rule with and without mock" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + f() = 1 + p { + q with time.now_ns as f + } + q { + valid_time(1) + } + valid_time(x) { time.now_ns() == x } + note: "withkeyword/builtin: indirect call through function, query package" + query: data.test = x + want_result: + - x: + f: 1 + p: true + - modules: + - | + package test + import future.keywords.in + + mock_count("one") = 1 + mock_count(x) = count(x) { + x != "one" + } + + numbers := {"one", "two", "tree"} + p = s { + s := { count(n) | some n in numbers } with count as mock_count + } + note: "withkeyword/builtin: mock function calls original" + query: data.test.p = x + want_result: + - x: + - 1 + - 3 + - 4 + - modules: + - | + package test + import future.keywords.in + + mock_concat("one", _) = ["one"] + mock_concat("one", x) = x + + numbers := ["one", "one"] + p = s { + s := { concat(n, [n]) | some n in numbers } with concat as mock_concat + } + note: "withkeyword/builtin: mock function returns same result for both rule defs" + query: data.test.p = x + want_result: + - x: [["one"]] + - modules: + - | + package test + import future.keywords.in + + mock_concat("one", _) = ["one"] + mock_concat("one", x) = x + + count_four(4) = 4 + count_four(x) = count(x) + + numbers := {"one", "two", "tree"} + q = s { + s := { concat(n, [n]) | some n in numbers } with concat as mock_concat + r + } + r { + count(input.four) == 4 + } + p = y { + y := q + with concat as mock_concat + with count as count_four + with input.four as 4 + } + note: "withkeyword/builtin: nested, mock function calls original" + query: data.test.p = x + want_result: + - x: [["one"]] + - modules: + - | + package test + + f(_) = 1 + g(x) = count(x) # replaced with f by inner "with" + + q = y { + y = count([1,2,3]) with count as f + } + p = y { + y = q with count as g + } + note: "withkeyword/builtin: multiple with" + query: data.test.p = x + want_result: + - x: 1 + - modules: + - | + package test + + f(x) = object.union_n(x) { { x: i | x := ["a", "a"][i]} } # never called, runtime error + g(x) = count(x) + p { + q with count as f + } + q { + r with object.union_n as g + } + r { + object.union_n([{}]) + } + note: "withkeyword/builtin: mock will not call other mock" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + f(x) = object.union_n(x) # b1 + g(x) = 123 { + count(x) # b2 + s with array.reverse as h + } + h(_) = ["replaced"] + p { q with object.union_n as f } + q { r with count as g } + r { x := [{"foo": 4}, {"baz": 5}]; count(x) == 123; object.union_n(x) == {"foo": 4, "baz": 5} } + s { x := [{}]; array.reverse(x) == ["replaced"] } + note: "withkeyword/builtin: nested scope handling" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + p = y { + y = time.now_ns() with time.now_ns as 12300 + } + note: "withkeyword/builtin-value: arity-0, captured output" + query: data.test.p = x + want_result: + - x: 12300 + - modules: + - | + package test + + p { + time.now_ns() with time.now_ns as false + } + note: "withkeyword/builtin-value: arity-0, false" + query: data.test.p = x + want_result: [] + - modules: + - | + package test + + p { + time.now_ns() with time.now_ns as true + } + note: "withkeyword/builtin-value: arity-0" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + p { + x := true + time.now_ns() with time.now_ns as x + } + note: "withkeyword/builtin-value: arity-0, var" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + p { + count([]) == 1 with count as 1 + } + note: "withkeyword/builtin-value: arity-1" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + p { + count([], 1) with count as 1 + } + note: "withkeyword/builtin-value: arity-1, captured" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + p { + count(input) == 1 with count as 1 + } + note: "withkeyword/builtin-value: arity-1, input must still be defined" + query: data.test.p = x + want_result: [] + - modules: + - | + package test + + p = x { + x = time.now_ns() with time.now_ns as opa.runtime + } + note: "withkeyword/builtin-builtin: arity 0" + query: data.test.p = x + want_result: + - x: {} + - modules: + - | + package test + + p = x { + x = count([{}, {"foo": 3}]) with count as object.union_n + } + note: "withkeyword/builtin-builtin: arity 1, replacement is compound" + query: data.test.p = x + want_result: + - x: + foo: 3 + - modules: + - | + package test + + p = x { + x = object.union_n([{}, {"foo": 3}]) with object.union_n as count + } + note: "withkeyword/builtin-builtin: arity 1, replacement is simple" + query: data.test.p = x + want_result: + - x: 2 + - modules: + - | + package test + p { + count([1,2,3]) == 1 with count as [1|true][0] + } + note: "withkeyword/builtin: direct call, arity 1, replacement is value that needs eval (array comprehension)" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + p { + q with count as [1|true][0] + } + q { + count([1,2,3]) == 1 + } + note: "withkeyword/builtin: indirect call, arity 1, replacement is value that needs eval (array comprehension)" + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-function-mock.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-function-mock.yaml new file mode 100644 index 000000000000..b439ace9cb6b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-function-mock.yaml @@ -0,0 +1,128 @@ +--- +cases: + - modules: + - | + package test + f(_) = 2 + p = y { + y = f(true) with f as 1 + } + note: "withkeyword/function: direct call, value replacement, arity 1" # NOTE(sr): arity-0 functions fail typechecking + query: data.test.p = x + want_result: + - x: 1 + - modules: + - | + package test + f(_) = 2 + g(_) = 1 + p = y { + y = f(true) with f as g + } + note: "withkeyword/function: direct call, function replacement, arity 1" + query: data.test.p = x + want_result: + - x: 1 + - modules: + - | + package test + f(_) = 2 + g(_) = 1 + p { + f(true, 1) with f as g + } + note: "withkeyword/function: direct call, function replacement, arity 1, result captured" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + f(_) = 2 + p = y { + y = f([1]) with f as count + } + note: "withkeyword/function: direct call, built-in replacement, arity 1" + query: data.test.p = x + want_result: + - x: 1 + - modules: + - | + package test + f(_) = 2 + p { + f([1], 1) with f as count + } + note: "withkeyword/function: direct call, built-in replacement, arity 1, result captured" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + f1(x) = object.union_n(x) + f2(x) = count(x) + f3(x) = array.reverse(x) + f(x) = f1(x) + g(x) = 123 { + f2(x) + s with f3 as h + } + h(_) = ["replaced"] + p { q with f1 as f } + q { r with f2 as g } + r { x := [{"foo": 4}, {"baz": 5}]; f2(x) == 123; f1(x) == {"foo": 4, "baz": 5} } + s { x := [{}]; f3(x) == ["replaced"] } + note: "withkeyword/function: nested scope handling" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + + f(x) = 2 + g(x) = f(x) + p = y { y := f(1) with f as g } + note: "withkeyword/function: simple scope handling (no recursion here)" + query: data.test.p = x + want_result: + - x: 2 + - modules: + - | + package test + + f(_) = 1 { + input.x = "x" + } + p = y { y := f(1) with f as 2 } + note: "withkeyword/function: rule indexing irrelevant" + query: data.test.p = x + want_result: + - x: 2 + - modules: + - | + package test + f(_) = 1 + p { + f([1,2,3]) == 1 with f as [1|true][0] + } + note: "withkeyword/function: direct call, arity 1, replacement is value that needs eval (array comprehension)" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + f(_) = 1 + p { + q with f as [1|true][0] + } + q { + f([1,2,3]) == 1 + } + note: "withkeyword/function: indirect call, arity 1, replacement is value that needs eval (array comprehension)" + query: data.test.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-function-mocks-issue-5299.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-function-mocks-issue-5299.yaml new file mode 100644 index 000000000000..25e1c6280e48 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-with-function-mocks-issue-5299.yaml @@ -0,0 +1,60 @@ +--- +cases: + - modules: + - | + package test + f(_) = 2 + f0 := 1 # a rule + p = y { + y = f(true) with f as f0 + } + note: "withkeyword/function: direct call, rule replacement" + query: data.test.p = x + want_result: + - x: 1 + - modules: + - | + package test + f(_) = 2 + f0 := 1 # a rule + p { + f(true, 1) with f as f0 + } + note: "withkeyword/function: captured result, rule replacement" + query: data.test.p = x + want_result: + - x: true + - modules: + - | + package test + f := 1 # a rule + p = y { + y = time.now_ns() with time.now_ns as f + } + note: "withkeyword/builtin: direct call, arity 0, rule replacement" + query: data.test.p = x + want_result: + - x: 1 + - modules: + - | + package test + f := 1 # a rule + p = y { + y = count([1,2,3]) with count as f + } + note: "withkeyword/builtin: direct call, arity 1, rule replacement" + query: data.test.p = x + want_result: + - x: 1 + - modules: + - | + package test + f := 1 # a rule + g(x) := count(x) + p = y { + y = g([1,2,3]) with count as f + } + note: "withkeyword/builtin: indirect call, arity 1, rule replacement" + query: data.test.p = x + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1015.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1015.yaml new file mode 100644 index 000000000000..51fa863e6bb3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1015.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.ex.loopback with input as true + data.ex.loopback = false with input as false + } + - | + package ex + + loopback = __local0__ { + true + __local0__ = input + } + note: withkeyword/with + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1016.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1016.yaml new file mode 100644 index 000000000000..0125853c4695 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1016.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + not data.ex.loopback with input as false + data.ex.loopback with input as true + } + - | + package ex + + loopback = __local0__ { + true + __local0__ = input + } + note: withkeyword/with not + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1017.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1017.yaml new file mode 100644 index 000000000000..5f05c2f5aac0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1017.yaml @@ -0,0 +1,24 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.ex.composite[x] with input.foo as [1, 2, 3, 4] + } + - | + package ex + + composite[x] { + input.foo[_] = x + x > 2 + } + note: withkeyword/with composite + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1018.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1018.yaml new file mode 100644 index 000000000000..5490426e7c1a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1018.yaml @@ -0,0 +1,26 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = x { + foo = "hello" + bar = "world" + x = data.ex.vars with input.foo as foo with input.bar as bar + } + - | + package ex + + vars = x { + y = input.bar + z = input.foo + x = {"bar": y, "foo": z} + } + note: withkeyword/with vars + query: data.generated.p = x + want_result: + - x: + bar: world + foo: hello diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1019.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1019.yaml new file mode 100644 index 000000000000..77342d8a2bec --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1019.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = x { + x := data.ex.loopback with input.foo as "x" with input.foo.bar as "y" + } + - | + package ex + + loopback = y { + true + y = input + } + note: withkeyword/with conflict + query: data.generated.p = x + want_result: + - x: + foo: + bar: "y" diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1020.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1020.yaml new file mode 100644 index 000000000000..800bb7cb6669 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1020.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: {} + input_term: '{"a": {"d": 3}, "e": 4}' + modules: + - | + package generated + + r = __local0__ { + true + __local0__ = input + } + + q = x { + data.generated.r = x with input.a.c as 2 + } + + p = x { + data.generated.q = x with input.a.b as 1 + } + note: withkeyword/with stack + query: data.generated.p = x + want_result: + - x: + a: + b: 1 + c: 2 + d: 3 + e: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1021.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1021.yaml new file mode 100644 index 000000000000..75cd5d50d447 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1021.yaml @@ -0,0 +1,30 @@ +--- +cases: + - data: {} + input_term: '{"a": {"d": 3}, "e": 4}' + modules: + - | + package generated + + r = __local0__ { + true + __local0__ = input + } + + q = x { + not false with input as {} + data.generated.r = x with input.a.c as 2 + } + + p = x { + data.generated.q = x with input.a.b as 1 + } + note: withkeyword/with not stack + query: data.generated.p = x + want_result: + - x: + a: + b: 1 + c: 2 + d: 3 + e: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1022.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1022.yaml new file mode 100644 index 000000000000..492636d2a4c3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1022.yaml @@ -0,0 +1,36 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + r = __local0__ { + true + __local0__ = data.test + } + + q = x { + data.generated.r = x with data.test.a.c as 2 + } + + p = x { + data.generated.q = x with data.test.a.b as 1 + } + - | + package test.a + + d = 3 + - | + package test + + e = 4 + note: withkeyword/with stack (data) + query: data.generated.p = x + want_result: + - x: + a: + b: 1 + c: 2 + d: 3 + e: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1023.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1023.yaml new file mode 100644 index 000000000000..53859095b1b2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1023.yaml @@ -0,0 +1,45 @@ +--- +cases: + - data: {} + modules: + - | + package test.a + + d = 3 + - | + package test + + e = 4 + - | + package generated + + r = __local0__ { + true + __local0__ = data.test + } + + n1 { + data.test.a.z = 7 + } + + n { + not data.generated.n1 + } + + q = x { + not data.generated.n with data.test.a.z as 7 + data.generated.r = x with data.test.a.c as 2 + } + + p = x { + data.generated.q = x with data.test.a.b as 1 + } + note: withkeyword/with not stack (data) + query: data.generated.p = x + want_result: + - x: + a: + b: 1 + c: 2 + d: 3 + e: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1024.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1024.yaml new file mode 100644 index 000000000000..b6e7c6afbcb5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1024.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + input_term: '{"a": {"b": 1, "c": 2}}' + modules: + - | + package generated + + q = __local0__ { + true + __local0__ = input + } + + p = x { + data.generated.q = x with input.a as {"d": 3} + } + note: withkeyword/with stack overwrites + query: data.generated.p = x + want_result: + - x: + a: + d: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1025.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1025.yaml new file mode 100644 index 000000000000..33f2ef270dfe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1025.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + q = __local0__ { + true + __local0__ = data.test + } + + p = x { + data.generated.q = x with data.test.a as {"d": 3} + } + - | + package test + + a = {"b": 1, "c": 2} + note: withkeyword/with stack overwrites (data) + query: data.generated.p = x + want_result: + - x: + a: + d: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1026.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1026.yaml new file mode 100644 index 000000000000..23c77804592c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1026.yaml @@ -0,0 +1,30 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p[x] { + data.a[_] = x + not data.ex.input_eq with input.x as x + } + - | + package ex + + input_eq { + input.x = 1 + } + note: withkeyword/with invalidate + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1027.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1027.yaml new file mode 100644 index 000000000000..2d0bceb3f773 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1027.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + input_term: '"b"' + modules: + - | + package generated + + p = [x, y] { + x = input with input as "a" + y = input + } + note: withkeyword/with invalidate input stack + query: data.generated.p = x + want_result: + - x: + - a + - b diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1028.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1028.yaml new file mode 100644 index 000000000000..59506e68316e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1028.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: {} + input_term: '"c"' + modules: + - | + package generated + + q[x] { + input[_] = x + } + + p[[x, y]] { + data.generated.q[x] with input as ["a", "b"] + y = input + } + note: withkeyword/with invalidate input stack iteration + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - a + - c + - - b + - c diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1029.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1029.yaml new file mode 100644 index 000000000000..e79ab0ceb547 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1029.yaml @@ -0,0 +1,28 @@ +--- +cases: + - data: {} + input_term: "2" + modules: + - | + package generated + + q = "a" { + input = x + x = 1 + } + + q = "b" { + input = x + x = 2 + } + + p = [x, y] { + data.generated.q = x with input as 1 + data.generated.q = y + } + note: withkeyword/with invalidate virtual cache + query: data.generated.p = x + want_result: + - x: + - a + - b diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1030.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1030.yaml new file mode 100644 index 000000000000..5a27e992b39d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1030.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + q = "b" + + p = [x, y] { + data.generated.q = x with data.generated.q as "a" + data.generated.q = y + } + note: withkeyword/with invalidate data stack + query: data.generated.p = x + want_result: + - x: + - a + - b diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1031.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1031.yaml new file mode 100644 index 000000000000..7817bd197305 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1031.yaml @@ -0,0 +1,22 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + q["c"] + + p[[x, y]] { + data.generated.q[x] with data.generated.q as {"a", "b"} + y = data.generated.q + } + note: withkeyword/with invalidate data stack iteration + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - a + - - c + - - b + - - c diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1032.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1032.yaml new file mode 100644 index 000000000000..9d3e7143197b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1032.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.ex.allow_basic = true with data.a as "testdata" + } + - | + package ex + + allow_basic { + data.a = "testdata" + } + note: withkeyword/with basic data + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1033.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1033.yaml new file mode 100644 index 000000000000..6e3e36980db3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1033.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p { + data.ex.allow_merge_1 = true with data.b.v2 as "world" + } + - | + package ex + + allow_merge_1 { + data.b = {"v1": "hello", "v2": "world"} + } + note: withkeyword/with map data overwrite + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1034.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1034.yaml new file mode 100644 index 000000000000..d795c5915f45 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1034.yaml @@ -0,0 +1,28 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + modules: + - | + package generated + + p { + data.ex.allow_merge_2 = true with data.b.v2 as "world" with data.b.v3 as "again" + } + - | + package ex + + allow_merge_2 { + data.b = {"v1": "hello", "v2": "world", "v3": "again"} + } + note: withkeyword/with map data new key + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1035.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1035.yaml new file mode 100644 index 000000000000..fc857fd3c49c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1035.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + default p = false + p { + data.ex.allow_basic = true with data.a.b as 5 + } + - | + package ex + + allow_basic { + data.a = "testdata" + } + note: withkeyword/with data conflict + query: data.generated.p = x + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1036.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1036.yaml new file mode 100644 index 000000000000..d9b5149535f7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1036.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.a.b[x] = 1 with data.a.b as {"c": 1, "d": 2, "e": 1} + } + note: withkeyword/with base doc exact value + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - c + - e diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1037.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1037.yaml new file mode 100644 index 000000000000..f646b04d92da --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1037.yaml @@ -0,0 +1,18 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.a.b[x] with data.a.b as {"c": 1, "d": 2, "e": 1} + } + note: withkeyword/with base doc any index + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - c + - d + - e diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1038.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1038.yaml new file mode 100644 index 000000000000..e4e11d5dc60b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1038.yaml @@ -0,0 +1,13 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.a.b.c with data.a.b as 1 + } + note: withkeyword/undefined_1 + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1039.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1039.yaml new file mode 100644 index 000000000000..470a349d89bd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1039.yaml @@ -0,0 +1,29 @@ +--- +cases: + - data: + l: + - a: bob + b: -1 + c: + - 1 + - 2 + - 3 + - 4 + - a: alice + b: 1 + c: + - 2 + - 3 + - 4 + - 5 + d: null + modules: + - | + package generated + + p { + data.l.a with data.l as 1 + } + note: withkeyword/undefined_2 + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1040.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1040.yaml new file mode 100644 index 000000000000..c9c7a0206a02 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1040.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.ex.virtual = x with data.a.b as {"c": 1, "d": 2, "e": 1} + } + - | + package ex + + virtual[x] { + data.a.b[x] = 1 + } + note: withkeyword/with virtual doc exact value + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - c + - e diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1041.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1041.yaml new file mode 100644 index 000000000000..b57b0757f429 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1041.yaml @@ -0,0 +1,23 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p[x] { + data.ex.virtual[x] with data.a.b as {"c": 1, "d": 2, "e": 1} + } + - | + package ex + + virtual[x] { + data.a.b[x] = 1 + } + note: withkeyword/with virtual doc any index + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - c + - e diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1042.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1042.yaml new file mode 100644 index 000000000000..d124f0278d60 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1042.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = y { + y = data.ex.virtual.c with data.a.b as {"c": 1, "d": 2, "e": 1} + } + - | + package ex + + virtual[x] { + data.a.b[x] = 1 + } + note: withkeyword/with virtual doc specific index + query: data.generated.p = x + want_result: + - x: c diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1043.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1043.yaml new file mode 100644 index 000000000000..ac0e568acc30 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1043.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + not data.ex.virtual.d with data.a.b as {"c": 1, "d": 2, "e": 1} + } + - | + package ex + + virtual[x] { + data.a.b[x] = 1 + } + note: withkeyword/with virtual doc not specific index + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1044.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1044.yaml new file mode 100644 index 000000000000..e4dc1df85e7d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1044.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p = y { + y = data.ex.mock_var with data.ex.mock_var as {"c": 1, "d": 2} + } + - | + package ex + + mock_var = {"a": 0, "b": 0} + note: withkeyword/with mock var + query: data.generated.p = x + want_result: + - x: + c: 1 + d: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1045.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1045.yaml new file mode 100644 index 000000000000..45770d6d9c6d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1045.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.ex.mock_rule with data.ex.mock_rule as true + } + - | + package ex + + mock_rule = false { + 1 = 2 + } + note: withkeyword/with mock rule + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1046.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1046.yaml new file mode 100644 index 000000000000..f8a0db353ff9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1046.yaml @@ -0,0 +1,39 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.ex.allow with data.label.b.c as [1, 2, 3] + } + - | + package ex + + allow1 { + data.label.b.c = [1, 2, 3] + } + + allow2 { + data.label.b.c[x] = 2 + } + + allow3 { + data.label.b[x] = 1 + } + + allow4 { + data.label.b.c.d[x] = 1 + } + + allow { + data.ex.allow1 + data.ex.allow2 + not data.ex.allow3 + not data.ex.allow4 + } + note: withkeyword/with rule chain + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1047.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1047.yaml new file mode 100644 index 000000000000..ae612b6af0e0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1047.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + q[1] + + q[2] + + p[x] { + data.generated.q[x] with data.generated.q as {3, 4} + } + note: withkeyword/with mock iteration on sets + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1048.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1048.yaml new file mode 100644 index 000000000000..96d5f58dec71 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1048.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + q["a"] = 1 + + q["b"] = 2 + + p[x] = y { + data.generated.q[x] = y with data.generated.q as {"a": 3, "c": 4} + } + note: withkeyword/with mock iteration on objects + query: data.generated.p = x + want_result: + - x: + a: 3 + c: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1049.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1049.yaml new file mode 100644 index 000000000000..a5f9910fddb8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1049.yaml @@ -0,0 +1,21 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + q[1] + + q[2] + + p[x] { + data.generated.q[_] = x with data.generated.q as [3, 4] + } + note: withkeyword/with mock iteration on arrays + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1050.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1050.yaml new file mode 100644 index 000000000000..72e6e1ac8488 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1050.yaml @@ -0,0 +1,19 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.ex.input_eq with data.foo as 1 + } + - | + package ex + + input_eq { + input.x = 1 + } + note: withkeyword/bug 1083 + query: data.generated.p = x + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1051.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1051.yaml new file mode 100644 index 000000000000..a9982485d7d2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1051.yaml @@ -0,0 +1,25 @@ +--- +cases: + - data: + a: + - 1 + - 2 + - 3 + - 4 + modules: + - | + package generated + + p { + data.ex.data_eq with input as {} + } + - | + package ex + + data_eq { + data.a = x + } + note: withkeyword/bug 1100 + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1052.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1052.yaml new file mode 100644 index 000000000000..f7e0bd1acbe5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1052.yaml @@ -0,0 +1,20 @@ +--- +cases: + - data: {} + modules: + - | + package generated + + p { + data.ex.setl[1] with data.foo as {1} + } + - | + package ex + + setl[x] { + data.foo[x] + } + note: withkeyword/set lookup + query: data.generated.p = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1053.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1053.yaml new file mode 100644 index 000000000000..5e4b0423f9ce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1053.yaml @@ -0,0 +1,30 @@ +--- +cases: + - data: + modules: + - | + package generated + + p = [x, y] { + x = data.ex.s with input as {"a": "b", "c": "b"} + y = data.ex.s with input as {"a": "b"} + } + - | + package ex + + s[x] { + x = {v: ks | + v = input[i] + ks = {k | v = input[k]} + } + } + note: withkeyword/invalidate comprehension cache + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - - b: + - a + - - b: + - a + - c diff --git a/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1054.yaml b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1054.yaml new file mode 100644 index 000000000000..e0f7d09ceb99 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v0/withkeyword/test-withkeyword-1054.yaml @@ -0,0 +1,77 @@ +--- +cases: + - data: + modules: + - | + package test + + allow { + a := {"x": 0} + + input.x == 0 with input as a + } + note: withkeyword/rewrite declared variables in with value + query: data.test.allow = x + want_result: + - x: true + - data: + modules: + - | + package test + + allow { + a := {"x": 0} + + input.x == 1 with input as object.union(a, {"x": 1}) + input.x == -1 with input as object.union(a, {"x": -1}) + + input.x == 2 with input as object.union(a, object.union(a, {"x": 2})) + input.x == -2 with input as object.union(a, object.union(a, {"x": -2})) + } + note: withkeyword/rewrite declared variables nested in function call in with value + query: data.test.allow = x + want_result: + - x: true + - data: + modules: + - | + package test + + allow { + a := 1 + input[0] == 1 with input as [a] + input[0][0] == 1 with input as [[a]] + } + note: withkeyword/rewrite declared variables nested in array in with value + query: data.test.allow = x + want_result: + - x: true + - data: + modules: + - | + package test + + allow { + a := 1 + input.a == 1 with input as {"a": a} + input.nested.a == 1 with input as {"nested": {"a": a}} + } + note: withkeyword/rewrite declared variables nested in object in with value + query: data.test.allow = x + want_result: + - x: true + - data: + modules: + - | + package test + + allow { + a := 1 + b := 2 + + input.min == 1 with input as object.union({"min": 0}, {"min": min([a, b])}) + } + note: withkeyword/rewrite declared variables nested in function/array/object in with value + query: data.test.allow = x + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0001.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0001.yaml new file mode 100644 index 000000000000..d09e25510f57 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0001.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: aggregates/count + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.a + count(__local0__, x) + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0002.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0002.yaml new file mode 100644 index 000000000000..b9607e571911 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0002.yaml @@ -0,0 +1,27 @@ +--- + +cases: + - note: aggregates/count virtual + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = [y | data.generated.q[y]] + count(__local0__, x) + } + + q contains x if { + x = data.a[_] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0003.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0003.yaml new file mode 100644 index 000000000000..32f1f426b274 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0003.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: aggregates/count keys + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.b + count(__local0__, x) + } + data: + b: + v1: hello + v2: goodbye + want_result: + - x: + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0004.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0004.yaml new file mode 100644 index 000000000000..408434f03301 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0004.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: aggregates/count keys virtual + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = [k | data.generated.q[k] = _] + count(__local0__, x) + } + + q[k] := v if { + data.b[k] = v + } + data: + b: + v1: hello + v2: goodbye + want_result: + - x: + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0005.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0005.yaml new file mode 100644 index 000000000000..2d04de47989b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0005.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: aggregates/count set + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local0__ = data.generated.q + count(__local0__, x) + } + + q contains x if { + x = data.a[_] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0006.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0006.yaml new file mode 100644 index 000000000000..0dfe6291aec2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0006.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: aggregates/sum + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + sum([1, 2, 3, 4], x) + } + data: { } + want_result: + - x: + - 10 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0007.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0007.yaml new file mode 100644 index 000000000000..682abf2b2b17 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0007.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: aggregates/sum set + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + sum({1, 2, 3, 4}, x) + } + data: { } + want_result: + - x: 10 diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0008.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0008.yaml new file mode 100644 index 000000000000..0bc234aa7441 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0008.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: aggregates/sum virtual + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = [y | data.generated.q[y]] + sum(__local0__, x) + } + + q contains x if { + data.a[_] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 10 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0009.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0009.yaml new file mode 100644 index 000000000000..164924487327 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0009.yaml @@ -0,0 +1,25 @@ +--- + +cases: + - note: aggregates/sum virtual set + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local0__ = data.generated.q + sum(__local0__, x) + } + + q contains x if { + data.a[_] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: 10 diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0010.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0010.yaml new file mode 100644 index 000000000000..a87fbeb03c4d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0010.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: aggregates/bug 2469 - precision + query: data.generated.p = x + modules: + - | + package generated + + p if { + sum([49649733057, 1], __local0__) + __local0__ = 49649733058 + } + data: { } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0011.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0011.yaml new file mode 100644 index 000000000000..b07ec4154541 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0011.yaml @@ -0,0 +1,16 @@ +--- + + +cases: + - note: aggregates/product + query: data.generated.p = x + modules: + - | + package generated + + p if { + product([1, 2, 3, 4], 24) + } + data: { } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0012.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0012.yaml new file mode 100644 index 000000000000..7fd3a743f677 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0012.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: aggregates/product set + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + product({1, 2, 3, 4}, x) + } + data: { } + want_result: + - x: 24 diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0013.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0013.yaml new file mode 100644 index 000000000000..16bc667c2cc5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0013.yaml @@ -0,0 +1,17 @@ +--- + +cases: + - note: aggregates/max + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + max([1, 2, 3, 4], x) + } + data: { } + want_result: + - x: + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0014.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0014.yaml new file mode 100644 index 000000000000..ccd567458269 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0014.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: aggregates/max set + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + max({1, 2, 3, 4}, x) + } + data: { } + want_result: + - x: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0015.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0015.yaml new file mode 100644 index 000000000000..2a2cdde7ff15 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0015.yaml @@ -0,0 +1,27 @@ +--- + +cases: + - note: aggregates/max virtual + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = [y | data.generated.q[y]] + max(__local0__, x) + } + + q contains x if { + data.a[_] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0016.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0016.yaml new file mode 100644 index 000000000000..a24ed31eeb81 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0016.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: aggregates/max virtual set + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local0__ = data.generated.q + max(__local0__, x) + } + + q contains x if { + data.a[_] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0017.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0017.yaml new file mode 100644 index 000000000000..516cb7b56310 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0017.yaml @@ -0,0 +1,17 @@ +--- + +cases: + - note: aggregates/min + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + min([1, 2, 3, 4], x) + } + data: { } + want_result: + - x: + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0018.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0018.yaml new file mode 100644 index 000000000000..92347fb72925 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0018.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: aggregates/min dups + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + min([1, 2, 1, 3, 4], x) + } + data: { } + want_result: + - x: + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0019.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0019.yaml new file mode 100644 index 000000000000..520dc14a7f45 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0019.yaml @@ -0,0 +1,17 @@ +--- + +cases: + - note: aggregates/min out-of-order + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + min([3, 2, 1, 4, 6, -7, 10], x) + } + data: { } + want_result: + - x: + - -7 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0020.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0020.yaml new file mode 100644 index 000000000000..437d0fe2c6c4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0020.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: aggregates/min set + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + min({1, 2, 3, 4}, x) + } + data: { } + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0021.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0021.yaml new file mode 100644 index 000000000000..0d7f61b8761d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0021.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: aggregates/min virtual + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = [y | data.generated.q[y]] + min(__local0__, x) + } + + q contains x if { + data.a[_] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0022.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0022.yaml new file mode 100644 index 000000000000..4eb211f4b95c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0022.yaml @@ -0,0 +1,25 @@ +--- + +cases: + - note: aggregates/min virtual set + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local0__ = data.generated.q + min(__local0__, x) + } + + q contains x if { + data.a[_] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0023.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0023.yaml new file mode 100644 index 000000000000..3a4b3b4ab6e5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0023.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: aggregates/reduce ref dest + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.a[3] + max([1, 2, 3, 4], __local0__) + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0024.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0024.yaml new file mode 100644 index 000000000000..d3a39476051e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0024.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: aggregates/reduce ref dest (2) + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.a[3] + not max([1, 2, 3, 4, 5], __local0__) + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0025.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0025.yaml new file mode 100644 index 000000000000..014d37c29c73 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0025.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: aggregates/sort + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + sort([4, 3, 2, 1], x) + } + data: { } + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0026.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0026.yaml new file mode 100644 index 000000000000..6f1e0c0acdb4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0026.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: aggregates/sort set + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + sort({1, 2, 3, 4}, x) + } + data: { } + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0027.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0027.yaml new file mode 100644 index 000000000000..dec6bda21b60 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0027.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: aggregates/count string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + count("abcde", x) + } + want_result: + - x: 5 + - note: aggregates/count string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + count("åäö", x) + } + want_result: + - x: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0028.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0028.yaml new file mode 100644 index 000000000000..9767d6e81407 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-0028.yaml @@ -0,0 +1,30 @@ +--- +cases: + - note: aggregates/count error null + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + count(input.foo, x) + } + input: + foo: null + want_error_code: eval_type_error + want_error: operand 1 must be one of {array, object, set, string} but got null + strict_error: true + - note: aggregates/count error number + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + count(input.foo, x) + } + input: + foo: 5 + want_error_code: eval_type_error + want_error: operand 1 must be one of {array, object, set, string} but got number + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-bad-utf8-runes.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-bad-utf8-runes.yaml new file mode 100644 index 000000000000..6d712957c127 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-aggregates-bad-utf8-runes.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: aggregates/count with invalid utf-8 chars (0xFFFD) + query: data.test.p = x + modules: + - | + package test + + p contains x if { + x := count(base64.decode("2E84ZuPUd7zfvCZSNEchVpDEIj6PL7JfLpIqyxVG16k=")) + } + want_result: + - x: + - 30 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-membership.yaml b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-membership.yaml new file mode 100644 index 000000000000..e689b57f2f22 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/aggregates/test-membership.yaml @@ -0,0 +1,529 @@ +--- +cases: + - note: aggregates/member simple, set + query: data.test.p = x + modules: + - | + package test + + p if { + 1 in {1} + } + data: { } + want_result: + - x: true + - note: aggregates/member simple, array + query: data.test.p = x + modules: + - | + package test + + p if { + 1 in [1] + } + data: { } + want_result: + - x: true + - note: aggregates/member simple, object + query: data.test.p = x + modules: + - | + package test + + p if { + 1 in {"foo": 1} + } + data: { } + want_result: + - x: true + - note: aggregates/member object with key + query: data.test.p = x + modules: + - | + package test + + p if { + "foo", 1 in {"foo": 1} + } + data: { } + want_result: + - x: true + - note: aggregates/member array with index + query: data.test.p = x + modules: + - | + package test + + p if { + 1, "two" in ["one", "two", "three"] + } + data: { } + want_result: + - x: true + - note: aggregates/member array with index, nested + query: data.test.p = x + modules: + - | + package test + + p if { + 1, 2 in [2] in [false, true] + } + data: { } + want_result: + - x: true + - note: aggregates/member array with index, nested, associativity without parens + query: data.test.p = x + modules: + - | + package test + + p if { + (0, 2 in [2]) in [true] + } + data: { } + want_result: + - x: true + - note: aggregates/member object with key, nested, associativity without parens + query: data.test.p = x + modules: + - | + package test + + p if { + ("foo", 2 in {"foo": 2}) in [true] + } + data: { } + want_result: + - x: true + - note: aggregates/member object with key, nested + query: data.test.p = x + modules: + - | + package test + + p if { + "foo", (2 in {"bar": 2}) in {"foo": true} + } + data: { } + want_result: + - x: true + - note: aggregates/member simple false, set + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := 1 in {2} + } + data: { } + want_result: + - x: false + - note: aggregates/member simple false, array + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := 1 in [2] + } + data: { } + want_result: + - x: false + - note: aggregates/member simple false, object + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := 1 in {"foo": 2} + } + data: { } + want_result: + - x: false + - note: aggregates/member chained + query: data.test.p = x + modules: + - | + package test + + p if { + {1, 2} in [{1, 2}] in [true] + } + data: { } + want_result: + - x: true + - note: aggregates/member with vars + query: data.test.p = x + modules: + - | + package test + + p if { + x := "foo" + xs := ["foo", "bar"] + x in xs + } + data: { } + want_result: + - x: true + - note: aggregates/member with not + query: data.test.p = x + modules: + - | + package test + + p if { + not "foo" in ["fox"] + } + data: { } + want_result: + - x: true + - note: aggregates/member operator precedence with other infix operator (+) + query: data.test.p = x + modules: + - | + package test + + p if { + (1 + 1) in [2] + } + data: { } + want_result: + - x: true + - note: aggregates/member operator precedence in list (set) + query: data.test.p = x + modules: + - | + package test + + p if { + x := {1, 1 in [2]} + x == {1, false} + } + data: { } + want_result: + - x: true + - note: aggregates/member operator precedence in list with parens (set) + query: data.test.p = x + modules: + - | + package test + + p if { + x := {(1, 1 in [2])} + x == {false} + } + data: { } + want_result: + - x: true + - note: aggregates/member operator precedence in list (array) + query: data.test.p = x + modules: + - | + package test + + p if { + x := [1, 1 in [2]] + x == [1, false] + } + data: { } + want_result: + - x: true + - note: aggregates/member operator precedence in list with parens (array) + query: data.test.p = x + modules: + - | + package test + + p if { + x := [(1, 1 in [2])] + x == [false] + } + data: { } + want_result: + - x: true + - note: aggregates/member operator precedence in list (fun args) + query: data.test.p = x + modules: + - | + package test + + p if { + f(1, 1 in [2]) + } + + f(_, _) := true + data: { } + want_result: + - x: true + - note: aggregates/member operator precedence in list with parens (fun args) + query: data.test.p = x + modules: + - | + package test + + p if { + f((1, 1 in [2])) + } + + f(_) := true + data: { } + want_result: + - x: true + - note: aggregates/member composite containee + query: data.test.p = x + modules: + - | + package test + + p if { + {"foo": {"baz": 2000}} in [{"foo": {"baz": 2000}}] + } + data: { } + want_result: + - x: true + - note: aggregates/member non-collection string + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := 1 in "foo" + } + data: { } + want_result: + - x: false + - note: aggregates/member non-collection number + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := "foo" in 1 + } + data: { } + want_result: + - x: false + - note: aggregates/member with key in non-collection (number) + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := (1, "foo" in 1) + } + data: { } + want_result: + - x: false + - note: aggregates/member+some simple, array + query: data.test.p = x + modules: + - | + package test + + p contains x if { + some x in [1, 2, 3] + } + data: { } + want_result: + - x: + - 1 + - 2 + - 3 + - note: aggregates/member+some ground value + query: data.test.p = x + modules: + - | + package test + + p if { + some "foo" in ["foo"] + } + data: { } + want_result: + - x: true + - note: aggregates/member+some containee is call + query: data.test.p = x + modules: + - | + package test + + p if { + some numbers.range(1, 1) in [[1]] + } + data: { } + want_result: + - x: true + - note: aggregates/member+some non-ground composite containee + query: data.test.p = x + modules: + - | + package test + + p := x if { + some {"foo": x} in [{"foo": 100}, {"what": "ever"}] + } + data: { } + want_result: + - x: 100 + - note: aggregates/member+some non-ground composite containee, multiple bindings + query: data.test.p = x + modules: + - | + package test + + p := x if { + some {"foo": x, "what": y} in [{"foo": 100, "what": "ever"}] + } + data: { } + want_result: + - x: 100 + - note: aggregates/member+some ground composite containee + query: data.test.p = x + modules: + - | + package test + + p if { + some {"foo": 100} in [{"foo": 100}] + } + data: { } + want_result: + - x: true + - note: aggregates/member+some ground composite containee (false) + query: data.test.p = x + modules: + - | + package test + + p if { + some {"foo": 0} in [{"foo": 100}] + } + data: { } + want_result: [] + - note: aggregates/member+some+key non-ground value + query: data.test.p = x + modules: + - | + package test + + p := x if { + some "foo", x in {"foo": 100, "what": "ever"} + } + data: { } + want_result: + - x: 100 + - note: aggregates/member+some+key non-ground key + query: data.test.p = x + modules: + - | + package test + + p := x if { + some x, "ever" in {"foo": 100, "what": "ever"} + } + data: { } + want_result: + - x: what + - note: aggregates/member+some+key non-ground key+value + query: data.test.p = x + modules: + - | + package test + + p[k] := v if { + some k, v in {"foo": 100, "what": "ever"} + } + data: { } + want_result: + - x: + foo: 100 + what: ever + - note: aggregates/member+some+key non-ground, composite key + query: data.test.p = x + modules: + - | + package test + + p := x if { + some {"foo": x}, "ever" in {{"foo": 100}: "ever"} + } + data: { } + want_result: + - x: 100 + - note: aggregates/member+some+ref + query: data.test.p = x + modules: + - | + package test + + p := x if { + some {"a": x} in data.array + } + data: + array: + - a: 1 + - b: 2 + - c: 3 + want_result: + - x: 1 + - note: aggregates/member+some+key+ref + query: data.test.p = x + modules: + - | + package test + + p := [x, y] if { + some y, {"c": x} in data.array + } + data: + array: + - a: 1 + - b: 2 + - c: 3 + want_result: + - x: + - 3 + - 2 + - note: aggregates/member+some+key+ref with other variable + query: data.test.p = x + modules: + - | + package test + + p := [x, y, i] if { + some i + some y, {"c": x} in data.object[i] + } + data: + object: + array: + - a: 1 + - b: 2 + - c: 3 + want_result: + - x: + - 3 + - 2 + - array + - note: aggregates/member+some+with + query: data.test.p = x + modules: + - | + package test + + p contains [k, v] if { + some k, v in input with input.foo as "bar" + } + data: { } + want_result: + - x: + - - foo + - bar diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0810.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0810.yaml new file mode 100644 index 000000000000..94392246b6bb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0810.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: arithmetic/plus + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + data.a[i] = x + __local0__ = i + x + y = __local0__ + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 3 + - 5 + - 7 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0811.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0811.yaml new file mode 100644 index 000000000000..ab16f61fc389 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0811.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: arithmetic/minus + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + data.a[i] = x + __local0__ = i - x + y = __local0__ + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - -1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0812.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0812.yaml new file mode 100644 index 000000000000..ea574b53d2d0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0812.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: arithmetic/multiply + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + data.a[i] = x + __local0__ = i * x + y = __local0__ + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 0 + - 2 + - 6 + - 12 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0813.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0813.yaml new file mode 100644 index 000000000000..154e80a33443 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0813.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: arithmetic/divide+round + query: data.test.p = x + modules: + - | + package test + + p contains z if { + data.a[i] = x + y = i / x + round(y, z) + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 0 + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0814.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0814.yaml new file mode 100644 index 000000000000..35b5ec77c0f9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0814.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: arithmetic/divide+error + query: data.generated.p = x + modules: + - | + package generated + + p := y if { + data.a[i] = x + __local0__ = x / i + y = __local0__ + } + data: + a: + - 1 + want_error_code: eval_builtin_error + want_error: divide by zero + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0815.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0815.yaml new file mode 100644 index 000000000000..3dd71c70c94e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0815.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: arithmetic/abs + query: data.generated.p = x + modules: + - | + package generated + + p if { + abs(-10, x) + x = 10 + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0816.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0816.yaml new file mode 100644 index 000000000000..c75a745babce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0816.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: arithmetic/remainder + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local0__ = 7 % 4 + x = __local0__ + } + data: {} + want_result: + - x: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0817.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0817.yaml new file mode 100644 index 000000000000..85db4533792a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0817.yaml @@ -0,0 +1,16 @@ +--- + +cases: + - note: arithmetic/remainder+error + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local0__ = 7 % 0 + x = __local0__ + } + want_error_code: eval_builtin_error + want_error: modulo by zero + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0818.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0818.yaml new file mode 100644 index 000000000000..ecbea390c4ef --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0818.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: arithmetic/remainder+error+floating + query: data.test.p = x + modules: + - | + package test + + p := x if { + x = 1.1 % 1 + } + want_error_code: eval_builtin_error + want_error: modulo on floating-point number + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0819.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0819.yaml new file mode 100644 index 000000000000..415de68e2163 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0819.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: arithmetic/arity 1 ref dest + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.a[3] + abs(-4, __local0__) + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0820.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0820.yaml new file mode 100644 index 000000000000..71c51e6f0788 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0820.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: arithmetic/arity 1 ref dest (2) + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.a[3] + not abs(-5, __local0__) + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0821.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0821.yaml new file mode 100644 index 000000000000..d5ec3de50b0a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0821.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: arithmetic/arity 2 ref dest + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = 1 + 2 + data.a[2] = __local0__ + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0822.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0822.yaml new file mode 100644 index 000000000000..17dc995506d9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0822.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: arithmetic/arity 2 ref dest (2) + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = 2 + 3 + not data.a[2] = __local0__ + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0823.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0823.yaml new file mode 100644 index 000000000000..18980bd39956 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0823.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: arithmetic/bug 2469 - precision + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = 49649733057 + 1 + __local0__ = 49649733058 + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0824.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0824.yaml new file mode 100644 index 000000000000..94de2845c91e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0824.yaml @@ -0,0 +1,62 @@ +--- +cases: + - note: ceil rounds up + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + ceil(1.01, x) + } + data: {} + want_result: + - x: 2 + - note: ceil rounds up (2) + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + ceil(1.5, x) + } + data: {} + want_result: + - x: 2 + - note: ceil rounds up (3) + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + ceil(2222.2222222222, x) + } + data: {} + want_result: + - x: 2223 + - note: ceil integer + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + ceil(1, x) + } + data: {} + want_result: + - x: 1 + - note: ceil negative + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + ceil(-1.99999, x) + } + data: {} + want_result: + - x: -1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0825.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0825.yaml new file mode 100644 index 000000000000..a84bd3d01068 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-0825.yaml @@ -0,0 +1,62 @@ +--- +cases: + - note: floor rounds down + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + floor(1.01, x) + } + data: {} + want_result: + - x: 1 + - note: floor rounds down (2) + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + floor(1.5, x) + } + data: {} + want_result: + - x: 1 + - note: floor rounds down (3) + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + floor(99.99999, x) + } + data: {} + want_result: + - x: 99 + - note: floor integer + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + floor(1, x) + } + data: {} + want_result: + - x: 1 + - note: floor negative + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + floor(-1.001, x) + } + data: {} + want_result: + - x: -2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-minus-type-error.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-minus-type-error.yaml new file mode 100644 index 000000000000..63c8bb450d17 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-arithmetic-minus-type-error.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: arithmetic/minus/type error + query: data.test.p = x + modules: + - | + package test + + p if { + {1} - 1 + } + want_error_code: eval_type_error + want_error: operand 2 must be set but got number + strict_error: true + - note: arithmetic/minus/type error + query: data.test.p = x + modules: + - | + package test + + p if { + 1 - {1} + } + want_error_code: eval_type_error + want_error: operand 2 must be number but got set + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-big-int-0001.yaml b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-big-int-0001.yaml new file mode 100644 index 000000000000..abc9bd7ddaff --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/arithmetic/test-big-int-0001.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: arithmetic/big_int + query: data.test.p = x + modules: + - | + package test + + p if { + 28857836529306024611913 != 28857836529306024611912 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0041.yaml b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0041.yaml new file mode 100644 index 000000000000..5423f5e0db7f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0041.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: array/concat + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + array.concat([1, 2], [3, 4], __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0042.yaml b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0042.yaml new file mode 100644 index 000000000000..4da80c8aa95b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0042.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "array/concat: err" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local1__ = data.b + array.concat(__local1__, [3, 4], __local0__) + x = __local0__ + } + data: + b: + v1: hello + v2: goodbye + want_error_code: eval_type_error + want_error: "array.concat: operand 1 must be array but got object" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0043.yaml b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0043.yaml new file mode 100644 index 000000000000..a66a4b132c75 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0043.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "array/concat: err rhs" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local1__ = data.b + array.concat([1, 2], __local1__, __local0__) + x = __local0__ + } + data: + b: + v1: hello + v2: goodbye + want_error_code: eval_type_error + want_error: "array.concat: operand 2 must be array but got object" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0044.yaml b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0044.yaml new file mode 100644 index 000000000000..7e581e84abae --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0044.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: array/slice + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + array.slice([1, 2, 3, 4, 5], 1, 3, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0045.yaml b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0045.yaml new file mode 100644 index 000000000000..c581ec392faa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0045.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: "array/slice: empty slice" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + array.slice([1, 2, 3], 0, 0, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0046.yaml b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0046.yaml new file mode 100644 index 000000000000..db119420bd10 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0046.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: "array/slice: negative indices" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + array.slice([1, 2, 3, 4, 5], -4, -1, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0047.yaml b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0047.yaml new file mode 100644 index 000000000000..b94bb71ff28e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0047.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: "array/slice: stopIndex < startIndex" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + array.slice([1, 2, 3, 4, 5], 4, 1, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0048.yaml b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0048.yaml new file mode 100644 index 000000000000..c296a232a128 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0048.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: "array/slice: clamp startIndex" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + array.slice([1, 2, 3, 4, 5], -1, 2, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0049.yaml b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0049.yaml new file mode 100644 index 000000000000..c2e1a8a989db --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0049.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: "array/slice: clamp stopIndex" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + array.slice([1, 2, 3, 4, 5], 3, 6, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - 4 + - 5 diff --git a/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0050.yaml b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0050.yaml new file mode 100644 index 000000000000..0831f4a128ab --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0050.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: "array/slice: clamp both out of range" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + array.slice([], 1000, 2000, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0051.yaml b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0051.yaml new file mode 100644 index 000000000000..273432003bcb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0051.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: "array/slice: clamp both out of range non-empty" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + array.slice([1, 2, 3], 1000, 2000, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0052.yaml b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0052.yaml new file mode 100644 index 000000000000..1c7f4838648f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/array/test-array-0052.yaml @@ -0,0 +1,44 @@ +--- +cases: + - note: array/reverse_123 + query: data.test.p = x + modules: + - | + package test + + p := array.reverse(data.foo) + data: + foo: + - 1 + - 2 + - 3 + want_result: + - x: + - 3 + - 2 + - 1 + - note: array/reverse_empty + query: data.test.p = x + modules: + - | + package test + + p := array.reverse(data.foo) + data: + foo: [] + want_result: + - x: [] + - note: array/reverse_object_error + query: data.test.p = x + modules: + - | + package test + + p := array.reverse(data.foo) + data: + foo: + bar: baz + baz: bar + want_error_code: eval_type_error + want_error: 'array.reverse: operand 1 must be array but got object' + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/assignments/test-file-level-assignments.yaml b/third_party/opa/v1/test/cases/testdata/v1/assignments/test-file-level-assignments.yaml new file mode 100644 index 000000000000..6d53bb194594 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/assignments/test-file-level-assignments.yaml @@ -0,0 +1,61 @@ +--- +cases: + - note: assignments/file-level/default_value + query: data.test = x + modules: + - | + package test + + default a := 1 + want_result: + - x: + a: 1 + - note: assignments/file-level/rule + query: data.test = x + modules: + - | + package test + + b := 2 + want_result: + - x: + b: 2 + - note: assignments/file-level/else_keyword + query: data.test = x + modules: + - | + package test + + c := 3 if { + false + } else := 4 + want_result: + - x: + c: 4 + - note: assignments/file-level/partial_rule + query: data.test = x + modules: + - | + package test + + d[msg] := 5 if { + msg = [1, 2, 3][_] + } + want_result: + - x: + d: + "1": 5 + "2": 5 + "3": 5 + - note: assignments/file-level/function_return_value + query: data.test = x + modules: + - | + package test + + e := f(6) + + f(x) := x + want_result: + - x: + e: 6 diff --git a/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0929.yaml b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0929.yaml new file mode 100644 index 000000000000..5e91437e5571 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0929.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: base64builtins/encode-1 + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64.encode("hello", x) + } + data: {} + want_result: + - x: aGVsbG8= diff --git a/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0930.yaml b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0930.yaml new file mode 100644 index 000000000000..005563867b76 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0930.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: base64builtins/encode-2 + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64.encode("there", x) + } + data: {} + want_result: + - x: dGhlcmU= diff --git a/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0931.yaml b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0931.yaml new file mode 100644 index 000000000000..f2bb7338b1b8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0931.yaml @@ -0,0 +1,15 @@ +--- + +cases: + - note: base64builtins/decode-1 + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64.decode("aGVsbG8=", x) + } + data: {} + want_result: + - x: hello diff --git a/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0932.yaml b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0932.yaml new file mode 100644 index 000000000000..4745a3fd5b47 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0932.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: base64builtins/decode-2 + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64.decode("dGhlcmU=", x) + } + data: {} + want_result: + - x: there diff --git a/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0933.yaml b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0933.yaml new file mode 100644 index 000000000000..219ed80be451 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0933.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: base64builtins/encode-slash + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64.encode("subjects?_d", x) + } + data: {} + want_result: + - x: c3ViamVjdHM/X2Q= diff --git a/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0934.yaml b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0934.yaml new file mode 100644 index 000000000000..86ff675c9389 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0934.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: base64builtins/decode-slash + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64.decode("c3ViamVjdHM/X2Q=", x) + } + data: {} + want_result: + - x: subjects?_d diff --git a/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0935.yaml b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0935.yaml new file mode 100644 index 000000000000..c407078c50dc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/base64builtins/test-base64builtins-0935.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: base64builtins/is_valid-true + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64.is_valid("aGVsbG8=", x) + } + data: {} + want_result: + - x: true + - note: base64builtins/is_valid-false + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64.is_valid("{'not':'base64'}", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/base64urlbuiltins/test-base64urlbuiltins-0935.yaml b/third_party/opa/v1/test/cases/testdata/v1/base64urlbuiltins/test-base64urlbuiltins-0935.yaml new file mode 100644 index 000000000000..8d28b677fdd1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/base64urlbuiltins/test-base64urlbuiltins-0935.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: base64urlbuiltins/encode-1 + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64url.encode("hello", x) + } + want_result: + - x: aGVsbG8= + - note: base64urlbuiltins/encode-2 + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64url.encode("there", x) + } + want_result: + - x: dGhlcmU= diff --git a/third_party/opa/v1/test/cases/testdata/v1/base64urlbuiltins/test-base64urlbuiltins-0937.yaml b/third_party/opa/v1/test/cases/testdata/v1/base64urlbuiltins/test-base64urlbuiltins-0937.yaml new file mode 100644 index 000000000000..aedd20be3d86 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/base64urlbuiltins/test-base64urlbuiltins-0937.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: base64urlbuiltins/decode-1 padded string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64url.decode("aGVsbG8=", x) + } + want_result: + - x: hello + - note: base64urlbuiltins/decode-2 non-padded string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64url.decode("aGVsbG8", x) + } + want_result: + - x: hello diff --git a/third_party/opa/v1/test/cases/testdata/v1/base64urlbuiltins/test-base64urlbuiltins-0939.yaml b/third_party/opa/v1/test/cases/testdata/v1/base64urlbuiltins/test-base64urlbuiltins-0939.yaml new file mode 100644 index 000000000000..d1700f82c3fe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/base64urlbuiltins/test-base64urlbuiltins-0939.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: base64urlbuiltins/encode-1 without padding + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64url.encode_no_pad("hello", x) + } + want_result: + - x: aGVsbG8 + - note: base64urlbuiltins/encode-2 without padding + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + base64url.encode_no_pad("there", x) + } + want_result: + - x: dGhlcmU diff --git a/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0695.yaml b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0695.yaml new file mode 100644 index 000000000000..600c8ab8e401 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0695.yaml @@ -0,0 +1,189 @@ +--- +cases: + - note: baseandvirtualdocs/base/virtual + query: data.topdown.p = x + modules: + - | + package enum_errors.caller + + p[x] := y if { + data.enum_errors.a[x] = y + } + - | + package enum_errors.a.b.c + + p := x if { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.a.b.c.undefined1 + + p if { + false + } + + p if { + false + } + + q if { + false + } + - | + package topdown + + p contains [x1, x2, x3, x4] if { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q contains [x1, x2, x3] if { + data.topdown.a.b[x1][x2][0] = x3 + } + + r contains [x1, x2] if { + data.topdown.a.b[x1] = x2 + } + + s := __local1__ if { + true + __local1__ = data.topdown.no + } + + t := __local2__ if { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u := __local3__ if { + true + __local3__ = data.topdown.missing.input.value + } + + v := __local4__ if { + true + __local4__ = data.topdown.g + } + + w := __local5__ if { + true + __local5__ = data.topdown.set + } + + iterate_ground contains x if { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown.no.base.doc + + p := true + - | + package topdown.a.b.c.empty + - | + package topdown.a.b.c.s + + w := {"f": 10, "g": 9.9} + - | + package topdown.set + + v contains __local6__ if { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown.missing.input.value + + p := __local7__ if { + true + __local7__ = input.deadbeef + } + - | + package topdown.a.b.c + + p := [1, 2] + + q := [3, 4] + + r["a"] := 1 + + r["b"] := 2 + - | + package topdown.a.b.c.undefined2 + + p if { + input.foo + } + - | + package topdown.virtual.constants + + p := 1 + + q := 2 + + r := 1 + - | + package topdown.conflicts + + k := "bar" + - | + package topdown.g.h + + p if { + false + } + data: + topdown: + a: + b: + c: + "true": false + x: + - 100 + - 200 + z: + a: b + input_term: "{}" + want_result: + - x: + - - c + - p + - 0 + - 1 + - - c + - p + - 1 + - 2 + - - c + - q + - 0 + - 3 + - - c + - q + - 1 + - 4 + - - c + - r + - a + - 1 + - - c + - r + - b + - 2 + - - c + - s + - w + - f: 10 + g: 9.9 + - - c + - x + - 0 + - 100 + - - c + - x + - 1 + - 200 + - - c + - z + - a + - b + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0696.yaml b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0696.yaml new file mode 100644 index 000000000000..e14e45bf9332 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0696.yaml @@ -0,0 +1,273 @@ +--- +cases: + - note: "baseandvirtualdocs/base/virtual: ground key" + query: data.topdown.q = x + modules: + - | + package partial.topdown + + p contains ["c", "x", 0, x41] if { + data.topdown.a.b.c.x[0] = x41 + } + + p contains ["c", "x", 1, x41] if { + data.topdown.a.b.c.x[1] = x41 + } + + p contains ["c", "z", "a", x41] if { + data.topdown.a.b.c.z.a = x41 + } + + p contains [ + "c", "p", 0, + 1, + ] + + p contains [ + "c", "p", 1, + 2, + ] + + p contains [ + "c", "q", 0, + 3, + ] + + p contains [ + "c", "q", 1, + 4, + ] + + p contains [ + "c", "r", + "a", 1, + ] + + p contains [ + "c", "r", + "b", 2, + ] + + p contains [ + "c", "s", "w", + {"f": 10, "g": 9.9}, + ] + + p contains [ + "c", "undefined2", "p", + true, + ] if { + input.foo + } + + p contains ["c", "x", 0, x41] if { + data.topdown.a.b.c.x[0] = x41 + } + + p contains ["c", "x", 1, x41] if { + data.topdown.a.b.c.x[1] = x41 + } + + p contains ["c", "z", "a", x41] if { + data.topdown.a.b.c.z.a = x41 + } + + p contains [ + "c", "p", 0, + 1, + ] + + p contains [ + "c", "p", 1, + 2, + ] + + p contains [ + "c", "q", 0, + 3, + ] + + p contains [ + "c", "q", 1, + 4, + ] + + p contains [ + "c", "r", + "a", 1, + ] + + p contains [ + "c", "r", + "b", 2, + ] + + p contains [ + "c", "s", "w", + {"f": 10, "g": 9.9}, + ] + + p contains [ + "c", "undefined2", "p", + true, + ] if { + input.foo + } + - | + package topdown.a.b.c.empty + - | + package topdown.a.b.c + + p := [1, 2] + + q := [3, 4] + + r["a"] := 1 + + r["b"] := 2 + - | + package topdown.a.b.c.undefined2 + + p if { + input.foo + } + - | + package topdown.virtual.constants + + p := 1 + + q := 2 + + r := 1 + - | + package topdown.no.base.doc + + p := true + - | + package topdown.a.b.c.s + + w := {"f": 10, "g": 9.9} + - | + package topdown.missing.input.value + + p := __local7__ if { + true + __local7__ = input.deadbeef + } + - | + package topdown.conflicts + + k := "bar" + - | + package topdown.g.h + + p if { + false + } + - | + package enum_errors.a.b.c + + p := x if { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.a.b.c.undefined1 + + p if { + false + } + + p if { + false + } + + q if { + false + } + - | + package topdown + + p contains [x1, x2, x3, x4] if { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q contains [x1, x2, x3] if { + data.topdown.a.b[x1][x2][0] = x3 + } + + r contains [x1, x2] if { + data.topdown.a.b[x1] = x2 + } + + s := __local1__ if { + true + __local1__ = data.topdown.no + } + + t := __local2__ if { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u := __local3__ if { + true + __local3__ = data.topdown.missing.input.value + } + + v := __local4__ if { + true + __local4__ = data.topdown.g + } + + w := __local5__ if { + true + __local5__ = data.topdown.set + } + + iterate_ground contains x if { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown.set + + v contains __local6__ if { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown_test_partial + + __result__ := _result if { + data.partial.topdown.p = _result + } + - | + package enum_errors.caller + + p[x] := y if { + data.enum_errors.a[x] = y + } + data: + topdown: + a: + b: + c: + "true": false + x: + - 100 + - 200 + z: + a: b + input_term: "{}" + want_result: + - x: + - - c + - p + - 1 + - - c + - q + - 3 + - - c + - x + - 100 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0697.yaml b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0697.yaml new file mode 100644 index 000000000000..e02b6b657bb4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0697.yaml @@ -0,0 +1,297 @@ +--- +cases: + - note: "baseandvirtualdocs/base/virtual: prefix" + query: data.topdown.r = x + modules: + - | + package enum_errors.a.b.c + + p := x if { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.virtual.constants + + p := 1 + + q := 2 + + r := 1 + - | + package partial.topdown + + p contains ["c", "x", 0, x41] if { + data.topdown.a.b.c.x[0] = x41 + } + + p contains ["c", "x", 1, x41] if { + data.topdown.a.b.c.x[1] = x41 + } + + p contains ["c", "z", "a", x41] if { + data.topdown.a.b.c.z.a = x41 + } + + p contains [ + "c", "p", 0, + 1, + ] + + p contains [ + "c", "p", 1, + 2, + ] + + p contains [ + "c", "q", 0, + 3, + ] + + p contains [ + "c", "q", 1, + 4, + ] + + p contains [ + "c", "r", + "a", 1, + ] + + p contains [ + "c", "r", + "b", 2, + ] + + p contains [ + "c", "s", "w", + {"f": 10, "g": 9.9}, + ] + + p contains [ + "c", "undefined2", "p", + true, + ] if { + input.foo + } + + p contains ["c", "x", 0, x41] if { + data.topdown.a.b.c.x[0] = x41 + } + + p contains ["c", "x", 1, x41] if { + data.topdown.a.b.c.x[1] = x41 + } + + p contains ["c", "z", "a", x41] if { + data.topdown.a.b.c.z.a = x41 + } + + p contains [ + "c", "p", 0, + 1, + ] + + p contains [ + "c", "p", 1, + 2, + ] + + p contains [ + "c", "q", 0, + 3, + ] + + p contains [ + "c", "q", 1, + 4, + ] + + p contains [ + "c", "r", + "a", 1, + ] + + p contains [ + "c", "r", + "b", 2, + ] + + p contains [ + "c", "s", "w", + {"f": 10, "g": 9.9}, + ] + + p contains [ + "c", "undefined2", "p", + true, + ] if { + input.foo + } + - | + package topdown.missing.input.value + + p := __local7__ if { + true + __local7__ = input.deadbeef + } + - | + package topdown.conflicts + + k := "bar" + - | + package topdown.g.h + + p if { + false + } + - | + package topdown.a.b.c + + p := [1, 2] + + q := [3, 4] + + r["a"] := 1 + + r["b"] := 2 + - | + package topdown.a.b.c.undefined2 + + p if { + input.foo + } + - | + package topdown.a.b.c.s + + w := {"f": 10, "g": 9.9} + - | + package topdown + + p contains [x1, x2, x3, x4] if { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q contains [x1, x2, x3] if { + data.topdown.a.b[x1][x2][0] = x3 + } + + r contains [x1, x2] if { + data.topdown.a.b[x1] = x2 + } + + s := __local1__ if { + true + __local1__ = data.topdown.no + } + + t := __local2__ if { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u := __local3__ if { + true + __local3__ = data.topdown.missing.input.value + } + + v := __local4__ if { + true + __local4__ = data.topdown.g + } + + w := __local5__ if { + true + __local5__ = data.topdown.set + } + + iterate_ground contains x if { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = { + [ + "c", "p", + 1, + ], + [ + "c", "q", + 3, + ], + ["c", "x", 100], + } + } + - | + package topdown.a.b.c.undefined1 + + p if { + false + } + + p if { + false + } + + q if { + false + } + - | + package topdown.no.base.doc + + p := true + - | + package topdown.set + + v contains __local6__ if { + true + __local6__ = data.topdown.set.u[_] + } + - | + package enum_errors.caller + + p[x] := y if { + data.enum_errors.a[x] = y + } + - | + package topdown.a.b.c.empty + data: + topdown: + a: + b: + c: + "true": false + x: + - 100 + - 200 + z: + a: b + input_term: "{}" + want_result: + - x: + - - c + - empty: {} + p: + - 1 + - 2 + q: + - 3 + - 4 + r: + a: 1 + b: 2 + s: + w: + f: 10 + g: 9.9 + "true": false + undefined1: {} + undefined2: {} + x: + - 100 + - 200 + z: + a: b + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0698.yaml b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0698.yaml new file mode 100644 index 000000000000..26ea87229a23 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0698.yaml @@ -0,0 +1,170 @@ +--- +cases: + - note: "baseandvirtualdocs/base/virtual: set" + query: data.topdown.w = x + modules: + - | + package enum_errors.a.b.c + + p := x if { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.missing.input.value + + p := __local7__ if { + true + __local7__ = input.deadbeef + } + - | + package topdown.a.b.c.undefined1 + + p if { + false + } + + p if { + false + } + + q if { + false + } + - | + package topdown.set + + v contains __local6__ if { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown.conflicts + + k := "bar" + - | + package topdown.g.h + + p if { + false + } + - | + package topdown_test_partial + + __result__ := _result if { + data.partial.topdown.r = _result + } + - | + package topdown.a.b.c.empty + - | + package partial.topdown + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + - | + package topdown.a.b.c + + p := [1, 2] + + q := [3, 4] + + r["a"] := 1 + + r["b"] := 2 + - | + package topdown.a.b.c.undefined2 + + p if { + input.foo + } + - | + package topdown + + p contains [x1, x2, x3, x4] if { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q contains [x1, x2, x3] if { + data.topdown.a.b[x1][x2][0] = x3 + } + + r contains [x1, x2] if { + data.topdown.a.b[x1] = x2 + } + + s := __local1__ if { + true + __local1__ = data.topdown.no + } + + t := __local2__ if { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u := __local3__ if { + true + __local3__ = data.topdown.missing.input.value + } + + v := __local4__ if { + true + __local4__ = data.topdown.g + } + + w := __local5__ if { + true + __local5__ = data.topdown.set + } + + iterate_ground contains x if { + data.topdown.virtual.constants[x] = 1 + } + - | + package enum_errors.caller + + p[x] := y if { + data.enum_errors.a[x] = y + } + - | + package topdown.a.b.c.s + + w := {"f": 10, "g": 9.9} + - | + package topdown.no.base.doc + + p := true + - | + package topdown.virtual.constants + + p := 1 + + q := 2 + + r := 1 + data: + topdown: + set: + u: + - "1" + - "2" + - "3" + - "4" + input_term: "{}" + want_result: + - x: + u: + - "1" + - "2" + - "3" + - "4" + v: + - "1" + - "2" + - "3" + - "4" diff --git a/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0699.yaml b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0699.yaml new file mode 100644 index 000000000000..e74f493cd3e9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0699.yaml @@ -0,0 +1,156 @@ +--- +cases: + - note: "baseandvirtualdocs/base/virtual: no base" + query: data.topdown.s = x + modules: + - | + package topdown.conflicts + + k := "bar" + - | + package topdown.no.base.doc + + p := true + - | + package topdown.set + + v contains __local6__ if { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown.a.b.c.empty + - | + package enum_errors.caller + + p[x] := y if { + data.enum_errors.a[x] = y + } + - | + package topdown.a.b.c.undefined1 + + p if { + false + } + + p if { + false + } + + q if { + false + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = {"u": [1, 2, 3, 4], "v": {1, 2, 3, 4}} + } + - | + package topdown.a.b.c + + p := [1, 2] + + q := [3, 4] + + r["a"] := 1 + + r["b"] := 2 + - | + package topdown.a.b.c.s + + w := {"f": 10, "g": 9.9} + - | + package topdown.a.b.c.undefined2 + + p if { + input.foo + } + - | + package topdown.virtual.constants + + p := 1 + + q := 2 + + r := 1 + - | + package enum_errors.a.b.c + + p := x if { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.missing.input.value + + p := __local7__ if { + true + __local7__ = input.deadbeef + } + - | + package topdown.g.h + + p if { + false + } + - | + package topdown + + p contains [x1, x2, x3, x4] if { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q contains [x1, x2, x3] if { + data.topdown.a.b[x1][x2][0] = x3 + } + + r contains [x1, x2] if { + data.topdown.a.b[x1] = x2 + } + + s := __local1__ if { + true + __local1__ = data.topdown.no + } + + t := __local2__ if { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u := __local3__ if { + true + __local3__ = data.topdown.missing.input.value + } + + v := __local4__ if { + true + __local4__ = data.topdown.g + } + + w := __local5__ if { + true + __local5__ = data.topdown.set + } + + iterate_ground contains x if { + data.topdown.virtual.constants[x] = 1 + } + - | + package partial.topdown + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + data: {} + input_term: "{}" + want_result: + - x: + base: + doc: + p: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0700.yaml b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0700.yaml new file mode 100644 index 000000000000..f19a8b104976 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0700.yaml @@ -0,0 +1,153 @@ +--- +cases: + - note: "baseandvirtualdocs/base/virtual: undefined" + query: data.topdown.t = x + modules: + - | + package enum_errors.caller + + p[x] := y if { + data.enum_errors.a[x] = y + } + - | + package topdown.a.b.c.undefined1 + + p if { + false + } + + p if { + false + } + + q if { + false + } + - | + package topdown.a.b.c.empty + - | + package topdown.missing.input.value + + p := __local7__ if { + true + __local7__ = input.deadbeef + } + - | + package topdown.a.b.c.undefined2 + + p if { + input.foo + } + - | + package topdown.no.base.doc + + p := true + - | + package topdown.set + + v contains __local6__ if { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown.a.b.c + + p := [1, 2] + + q := [3, 4] + + r["a"] := 1 + + r["b"] := 2 + - | + package topdown_test_partial + + __result__ := _result if { + _result = {"base": {"doc": {"p": true}}} + } + - | + package topdown + + p contains [x1, x2, x3, x4] if { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q contains [x1, x2, x3] if { + data.topdown.a.b[x1][x2][0] = x3 + } + + r contains [x1, x2] if { + data.topdown.a.b[x1] = x2 + } + + s := __local1__ if { + true + __local1__ = data.topdown.no + } + + t := __local2__ if { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u := __local3__ if { + true + __local3__ = data.topdown.missing.input.value + } + + v := __local4__ if { + true + __local4__ = data.topdown.g + } + + w := __local5__ if { + true + __local5__ = data.topdown.set + } + + iterate_ground contains x if { + data.topdown.virtual.constants[x] = 1 + } + - | + package partial.topdown + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + - | + package topdown.virtual.constants + + p := 1 + + q := 2 + + r := 1 + - | + package enum_errors.a.b.c + + p := x if { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.g.h + + p if { + false + } + - | + package topdown.conflicts + + k := "bar" + - | + package topdown.a.b.c.s + + w := {"f": 10, "g": 9.9} + data: {} + input_term: "{}" + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0701.yaml b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0701.yaml new file mode 100644 index 000000000000..06dd0f7761da --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0701.yaml @@ -0,0 +1,165 @@ +--- +cases: + - note: "baseandvirtualdocs/base/virtual: undefined-2" + query: data.topdown.v = x + modules: + - | + package topdown.a.b.c.undefined1 + + p if { + false + } + + p if { + false + } + + q if { + false + } + - | + package topdown.set + + v contains __local6__ if { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown.a.b.c + + p := [1, 2] + + q := [3, 4] + + r["a"] := 1 + + r["b"] := 2 + - | + package topdown_test_partial + + __result__ := _result if { + _result = {} + } + - | + package enum_errors.a.b.c + + p := x if { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.g.h + + p if { + false + } + - | + package topdown.a.b.c.undefined2 + + p if { + input.foo + } + - | + package topdown.no.base.doc + + p := true + - | + package topdown.a.b.c.s + + w := {"f": 10, "g": 9.9} + - | + package enum_errors.caller + + p[x] := y if { + data.enum_errors.a[x] = y + } + - | + package topdown.virtual.constants + + p := 1 + + q := 2 + + r := 1 + - | + package topdown.a.b.c.empty + - | + package topdown.missing.input.value + + p := __local7__ if { + true + __local7__ = input.deadbeef + } + - | + package topdown.conflicts + + k := "bar" + - | + package topdown + + p contains [x1, x2, x3, x4] if { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q contains [x1, x2, x3] if { + data.topdown.a.b[x1][x2][0] = x3 + } + + r contains [x1, x2] if { + data.topdown.a.b[x1] = x2 + } + + s := __local1__ if { + true + __local1__ = data.topdown.no + } + + t := __local2__ if { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u := __local3__ if { + true + __local3__ = data.topdown.missing.input.value + } + + v := __local4__ if { + true + __local4__ = data.topdown.g + } + + w := __local5__ if { + true + __local5__ = data.topdown.set + } + + iterate_ground contains x if { + data.topdown.virtual.constants[x] = 1 + } + - | + package partial.topdown + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + data: + topdown: + g: + h: + k: + - "1" + - "2" + - "3" + input_term: "{}" + want_result: + - x: + h: + k: + - "1" + - "2" + - "3" diff --git a/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0702.yaml b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0702.yaml new file mode 100644 index 000000000000..7b38fdcca971 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0702.yaml @@ -0,0 +1,153 @@ +--- +cases: + - note: "baseandvirtualdocs/base/virtual: missing input value" + query: data.topdown.u = x + modules: + - | + package topdown.no.base.doc + + p := true + - | + package enum_errors.caller + + p[x] := y if { + data.enum_errors.a[x] = y + } + - | + package topdown.a.b.c.empty + - | + package topdown.conflicts + + k := "bar" + - | + package topdown + + p contains [x1, x2, x3, x4] if { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q contains [x1, x2, x3] if { + data.topdown.a.b[x1][x2][0] = x3 + } + + r contains [x1, x2] if { + data.topdown.a.b[x1] = x2 + } + + s := __local1__ if { + true + __local1__ = data.topdown.no + } + + t := __local2__ if { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u := __local3__ if { + true + __local3__ = data.topdown.missing.input.value + } + + v := __local4__ if { + true + __local4__ = data.topdown.g + } + + w := __local5__ if { + true + __local5__ = data.topdown.set + } + + iterate_ground contains x if { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown.g.h + + p if { + false + } + - | + package topdown.a.b.c.undefined1 + + p if { + false + } + + p if { + false + } + + q if { + false + } + - | + package partial.topdown + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + - | + package topdown.virtual.constants + + p := 1 + + q := 2 + + r := 1 + - | + package topdown.set + + v contains __local6__ if { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = {"h": {"k": [1, 2, 3]}} + } + - | + package enum_errors.a.b.c + + p := x if { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.a.b.c.undefined2 + + p if { + input.foo + } + - | + package topdown.a.b.c.s + + w := {"f": 10, "g": 9.9} + - | + package topdown.missing.input.value + + p := __local7__ if { + true + __local7__ = input.deadbeef + } + - | + package topdown.a.b.c + + p := [1, 2] + + q := [3, 4] + + r["a"] := 1 + + r["b"] := 2 + data: {} + input_term: "{}" + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0703.yaml b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0703.yaml new file mode 100644 index 000000000000..75cbe2fab265 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0703.yaml @@ -0,0 +1,156 @@ +--- +cases: + - note: baseandvirtualdocs/iterate ground + query: data.topdown.iterate_ground = x + modules: + - | + package topdown.a.b.c + + p := [1, 2] + + q := [3, 4] + + r["a"] := 1 + + r["b"] := 2 + - | + package topdown.a.b.c.s + + w := {"f": 10, "g": 9.9} + - | + package topdown.no.base.doc + + p := true + - | + package partial.topdown + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + - | + package enum_errors.a.b.c + + p := x if { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.set + + v contains __local6__ if { + true + __local6__ = data.topdown.set.u[_] + } + - | + package topdown.conflicts + + k := "bar" + - | + package topdown + + p contains [x1, x2, x3, x4] if { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q contains [x1, x2, x3] if { + data.topdown.a.b[x1][x2][0] = x3 + } + + r contains [x1, x2] if { + data.topdown.a.b[x1] = x2 + } + + s := __local1__ if { + true + __local1__ = data.topdown.no + } + + t := __local2__ if { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u := __local3__ if { + true + __local3__ = data.topdown.missing.input.value + } + + v := __local4__ if { + true + __local4__ = data.topdown.g + } + + w := __local5__ if { + true + __local5__ = data.topdown.set + } + + iterate_ground contains x if { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown.a.b.c.undefined1 + + p if { + false + } + + p if { + false + } + + q if { + false + } + - | + package topdown.virtual.constants + + p := 1 + + q := 2 + + r := 1 + - | + package topdown.missing.input.value + + p := __local7__ if { + true + __local7__ = input.deadbeef + } + - | + package enum_errors.caller + + p[x] := y if { + data.enum_errors.a[x] = y + } + - | + package topdown.a.b.c.empty + - | + package topdown_test_partial + + __result__ := _result if { + data.topdown.missing.input.value = _result + } + - | + package topdown.g.h + + p if { + false + } + - | + package topdown.a.b.c.undefined2 + + p if { + input.foo + } + data: {} + input_term: "{}" + want_result: + - x: + - p + - r + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0704.yaml b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0704.yaml new file mode 100644 index 000000000000..e6f52a0671dc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0704.yaml @@ -0,0 +1,157 @@ +--- +cases: + - note: "baseandvirtualdocs/base/virtual: conflicts" + query: data.topdown.conflicts = x + modules: + - | + package topdown_test_partial + + __result__ := _result if { + _result = {"p", "r"} + } + - | + package topdown.missing.input.value + + p := __local7__ if { + true + __local7__ = input.deadbeef + } + - | + package topdown.a.b.c.s + + w := {"f": 10, "g": 9.9} + - | + package topdown.a.b.c.undefined1 + + p if { + false + } + + p if { + false + } + + q if { + false + } + - | + package topdown.set + + v contains __local6__ if { + true + __local6__ = data.topdown.set.u[_] + } + - | + package enum_errors.caller + + p[x] := y if { + data.enum_errors.a[x] = y + } + - | + package topdown.conflicts + + k := "bar" + - | + package topdown + + p contains [x1, x2, x3, x4] if { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q contains [x1, x2, x3] if { + data.topdown.a.b[x1][x2][0] = x3 + } + + r contains [x1, x2] if { + data.topdown.a.b[x1] = x2 + } + + s := __local1__ if { + true + __local1__ = data.topdown.no + } + + t := __local2__ if { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u := __local3__ if { + true + __local3__ = data.topdown.missing.input.value + } + + v := __local4__ if { + true + __local4__ = data.topdown.g + } + + w := __local5__ if { + true + __local5__ = data.topdown.set + } + + iterate_ground contains x if { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown.no.base.doc + + p := true + - | + package partial.topdown + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + - | + package enum_errors.a.b.c + + p := x if { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.a.b.c + + p := [1, 2] + + q := [3, 4] + + r["a"] := 1 + + r["b"] := 2 + - | + package topdown.a.b.c.empty + - | + package topdown.a.b.c.undefined2 + + p if { + input.foo + } + - | + package topdown.virtual.constants + + p := 1 + + q := 2 + + r := 1 + - | + package topdown.g.h + + p if { + false + } + data: + topdown: + conflicts: + k: foo + input_term: "{}" + want_result: + - x: + k: foo diff --git a/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0705.yaml b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0705.yaml new file mode 100644 index 000000000000..6174f57401c5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/baseandvirtualdocs/test-baseandvirtualdocs-0705.yaml @@ -0,0 +1,158 @@ +--- +cases: + - note: baseandvirtualdocs/enumerate virtual errors + query: data.enum_errors.caller.p = x + modules: + - | + package topdown.a.b.c.s + + w := {"f": 10, "g": 9.9} + - | + package topdown.a.b.c.undefined1 + + p if { + false + } + + p if { + false + } + + q if { + false + } + - | + package topdown.set + + v contains __local6__ if { + true + __local6__ = data.topdown.set.u[_] + } + - | + package enum_errors.a.b.c + + p := x if { + __local0__ = 1 / 0 + x = __local0__ + } + - | + package topdown.a.b.c.empty + - | + package topdown.virtual.constants + + p := 1 + + q := 2 + + r := 1 + - | + package topdown.g.h + + p if { + false + } + - | + package enum_errors.caller + + p[x] := y if { + data.enum_errors.a[x] = y + } + - | + package partial.topdown + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + + r contains ["c", x21] if { + data.topdown.a.b.c = x21 + } + - | + package topdown.missing.input.value + + p := __local7__ if { + true + __local7__ = input.deadbeef + } + - | + package topdown.conflicts + + k := "bar" + - | + package topdown.no.base.doc + + p := true + - | + package topdown.a.b.c.undefined2 + + p if { + input.foo + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = {"k": "foo"} + } + - | + package topdown + + p contains [x1, x2, x3, x4] if { + data.topdown.a.b[x1][x2][x3] = x4 + } + + q contains [x1, x2, x3] if { + data.topdown.a.b[x1][x2][0] = x3 + } + + r contains [x1, x2] if { + data.topdown.a.b[x1] = x2 + } + + s := __local1__ if { + true + __local1__ = data.topdown.no + } + + t := __local2__ if { + true + __local2__ = data.topdown.a.b.c.undefined1 + } + + u := __local3__ if { + true + __local3__ = data.topdown.missing.input.value + } + + v := __local4__ if { + true + __local4__ = data.topdown.g + } + + w := __local5__ if { + true + __local5__ = data.topdown.set + } + + iterate_ground contains x if { + data.topdown.virtual.constants[x] = 1 + } + - | + package topdown.a.b.c + + p := [1, 2] + + q := [3, 4] + + r["a"] := 1 + + r["b"] := 2 + data: {} + input_term: "{}" + want_result: + - x: + b: + c: {} + want_error_code: eval_builtin_error + want_error: divide by zero + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsand/test-bitsand-0055.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsand/test-bitsand-0055.yaml new file mode 100644 index 000000000000..44401593d2a1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsand/test-bitsand-0055.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: bitsand/basic bitwise-and + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + bits.and(7, 9, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsand/test-bitsand-0056.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsand/test-bitsand-0056.yaml new file mode 100644 index 000000000000..042f4a3fe0ab --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsand/test-bitsand-0056.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: bitsand/and with zero is and + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + bits.and(50, 0, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 0 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsand/test-bitsand-0057.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsand/test-bitsand-0057.yaml new file mode 100644 index 000000000000..11adb54f4042 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsand/test-bitsand-0057.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: bitsand/lhs (float) error + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + bits.and(7.2, 42, __local1__) + __local0__ = __local1__ + } + data: {} + want_error_code: eval_type_error + want_error: "bits.and: operand 1 must be integer number but got floating-point number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsnegate/test-bitsnegate-0058.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsnegate/test-bitsnegate-0058.yaml new file mode 100644 index 000000000000..2a32b537b632 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsnegate/test-bitsnegate-0058.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: bitsnegate/basic bitwise-negate + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + bits.negate(42, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - -43 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsnegate/test-bitsnegate-0059.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsnegate/test-bitsnegate-0059.yaml new file mode 100644 index 000000000000..3e64f43b96ff --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsnegate/test-bitsnegate-0059.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: bitsnegate/float error + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + bits.negate(7.2, __local1__) + __local0__ = __local1__ + } + data: {} + want_error_code: eval_type_error + want_error: "bits.negate: operand 1 must be integer number but got floating-point number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsor/test-bitsor-0052.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsor/test-bitsor-0052.yaml new file mode 100644 index 000000000000..eba90637a059 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsor/test-bitsor-0052.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: bitsor/basic bitwise-or + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + bits.or(7, 9, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 15 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsor/test-bitsor-0053.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsor/test-bitsor-0053.yaml new file mode 100644 index 000000000000..3b964408a568 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsor/test-bitsor-0053.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: bitsor/or with zero is value + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + bits.or(50, 0, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 50 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsor/test-bitsor-0054.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsor/test-bitsor-0054.yaml new file mode 100644 index 000000000000..9bc9594fd8aa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsor/test-bitsor-0054.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: bitsor/lhs (float) error + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + bits.or(7.2, 42, __local1__) + __local0__ = __local1__ + } + data: {} + want_error_code: eval_type_error + want_error: "bits.or: operand 1 must be integer number but got floating-point number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0063.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0063.yaml new file mode 100644 index 000000000000..b9665f9cdfb4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0063.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: bitsshiftleft/basic shift-left + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + bits.lsh(1, 3, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 8 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0064.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0064.yaml new file mode 100644 index 000000000000..afee762923bc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0064.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: bitsshiftleft/lhs (float) error + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + bits.lsh(7.2, 42, __local1__) + __local0__ = __local1__ + } + data: {} + want_error_code: eval_type_error + want_error: "bits.lsh: operand 1 must be integer number but got floating-point number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0065.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0065.yaml new file mode 100644 index 000000000000..dd14c3100de9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0065.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: bitsshiftleft/rhs must be unsigned + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + bits.lsh(7, -1, __local1__) + __local0__ = __local1__ + } + data: {} + want_error_code: eval_type_error + want_error: "bits.lsh: operand 2 must be an unsigned integer number but got a negative integer" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0066.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0066.yaml new file mode 100644 index 000000000000..0127fe286503 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0066.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: bitsshiftleft/shift of max int32 doesn't overflow + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + bits.lsh(2147483647, 1, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: 4294967294 diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0067.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0067.yaml new file mode 100644 index 000000000000..d5ff6cf154c3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftleft/test-bitsshiftleft-0067.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: bitsshiftleft/shift of max int64 doesn't overflow and is not lossy + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + bits.lsh(9223372036854775807, 1, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: 18446744073709551614 diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsshiftright/test-bitsshiftright-0068.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftright/test-bitsshiftright-0068.yaml new file mode 100644 index 000000000000..f2978752d37d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftright/test-bitsshiftright-0068.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: bitsshiftright/basic shift-right + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + bits.rsh(8, 3, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsshiftright/test-bitsshiftright-0069.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftright/test-bitsshiftright-0069.yaml new file mode 100644 index 000000000000..b090df0369ab --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftright/test-bitsshiftright-0069.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: bitsshiftright/lhs (float) error + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + bits.rsh(7.2, 42, __local1__) + __local0__ = __local1__ + } + data: {} + want_error_code: eval_type_error + want_error: "bits.rsh: operand 1 must be integer number but got floating-point number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsshiftright/test-bitsshiftright-0070.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftright/test-bitsshiftright-0070.yaml new file mode 100644 index 000000000000..6d423aa3bc92 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsshiftright/test-bitsshiftright-0070.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: bitsshiftright/rhs must be unsigned + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + bits.rsh(7, -1, __local1__) + __local0__ = __local1__ + } + data: {} + want_error_code: eval_type_error + want_error: "bits.rsh: operand 2 must be an unsigned integer number but got a negative integer" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsxor/test-bitsxor-0060.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsxor/test-bitsxor-0060.yaml new file mode 100644 index 000000000000..d4a0538cc607 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsxor/test-bitsxor-0060.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: bitsxor/basic bitwise-xor + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + bits.xor(42, 3, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 41 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsxor/test-bitsxor-0061.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsxor/test-bitsxor-0061.yaml new file mode 100644 index 000000000000..59bf382eac44 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsxor/test-bitsxor-0061.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: bitsxor/xor same is 0 + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + bits.xor(42, 42, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 0 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/bitsxor/test-bitsxor-0062.yaml b/third_party/opa/v1/test/cases/testdata/v1/bitsxor/test-bitsxor-0062.yaml new file mode 100644 index 000000000000..678f4d284754 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/bitsxor/test-bitsxor-0062.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: bitsxor/lhs (float) error + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + bits.xor(7.2, 42, __local1__) + __local0__ = __local1__ + } + data: {} + want_error_code: eval_type_error + want_error: "bits.xor: operand 1 must be integer number but got floating-point number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0824.yaml b/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0824.yaml new file mode 100644 index 000000000000..1827cc9c8a4c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0824.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: casts/to_number + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z, i, j] if { + to_number("-42.0", x) + to_number(false, y) + to_number(100.1, z) + to_number(null, i) + to_number(true, j) + } + data: {} + want_result: + - x: + - -42 + - 0 + - 100.1 + - 0 + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0825.yaml b/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0825.yaml new file mode 100644 index 000000000000..1cea208ec26f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0825.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: casts/to_number ref dest + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.a[2] + to_number("3", __local0__) + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0826.yaml b/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0826.yaml new file mode 100644 index 000000000000..d98b9ecdec16 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0826.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: casts/to_number ref dest + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.a[2] + not to_number("-1", __local0__) + } + data: + a: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0827.yaml b/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0827.yaml new file mode 100644 index 000000000000..14dfe55cf4c1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0827.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "casts/to_number: bad input" + query: data.generated.p = x + modules: + - | + package generated + + p if { + to_number("broken", x) + } + want_error_code: eval_builtin_error + want_error: invalid syntax + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0828.yaml b/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0828.yaml new file mode 100644 index 000000000000..cc666f416f64 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/casts/test-casts-0828.yaml @@ -0,0 +1,90 @@ +--- +cases: + - note: "casts/to_number: nan input" + query: data.generated.p = b + modules: + - | + package generated + + p if { + to_number("nan", b) + } + want_error_code: eval_type_error + strict_error: true + - note: "casts/to_number: inf input" + query: data.generated.p = b + modules: + - | + package generated + + p if { + to_number("inf", b) + } + want_error_code: eval_type_error + strict_error: true + - note: "casts/to_number: -inf input" + query: data.generated.p = b + modules: + - | + package generated + + p if { + to_number("-inf", b) + } + want_error_code: eval_type_error + strict_error: true + - note: "casts/to_number: Infinity input" + query: data.generated.p = b + modules: + - | + package generated + + p if { + to_number("Infinity", b) + } + want_error_code: eval_type_error + strict_error: true + - note: "casts/to_number: -Infinity input" + query: data.generated.p = b + modules: + - | + package generated + + p if { + to_number("-Infinity", b) + } + want_error_code: eval_type_error + strict_error: true + - note: "casts/to_number: -nan input" + query: data.generated.p = b + modules: + - | + package generated + + p if { + to_number("-nan", b) + } + want_error_code: eval_type_error + strict_error: true + - note: "casts/to_number: -NaN input" + query: data.generated.p = b + modules: + - | + package generated + + p if { + to_number("-NaN", b) + } + want_error_code: eval_type_error + strict_error: true + - note: "casts/to_number: iNf input" + query: data.generated.p = b + modules: + - | + package generated + + p if { + to_number("iNf", b) + } + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0608.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0608.yaml new file mode 100644 index 000000000000..b1c92d05efa1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0608.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: comparisonexpr/equals + query: data.generated.p = x + modules: + - | + package generated + + p if { + 1 = 1 + data.a[i] = x + x = 2 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0609.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0609.yaml new file mode 100644 index 000000000000..ba181c23883b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0609.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: comparisonexpr/noteq + query: data.generated.p = x + modules: + - | + package generated + + p if { + 0 != 1 + data.a[i] = x + x != 2 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0610.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0610.yaml new file mode 100644 index 000000000000..e0ae018fb59c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0610.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: comparisonexpr/gt + query: data.generated.p = x + modules: + - | + package generated + + p if { + 1 > 0 + data.a[i] = x + x > 2 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0611.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0611.yaml new file mode 100644 index 000000000000..29b6ccd4d236 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0611.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: comparisonexpr/gteq + query: data.generated.p = x + modules: + - | + package generated + + p if { + 1 >= 1 + data.a[i] = x + x >= 4 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0612.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0612.yaml new file mode 100644 index 000000000000..fe98c8889535 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0612.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: comparisonexpr/lt + query: data.generated.p = x + modules: + - | + package generated + + p if { + -1 < 0 + data.a[i] = x + x < 5 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0613.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0613.yaml new file mode 100644 index 000000000000..e65c0c4b6528 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0613.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: comparisonexpr/lteq + query: data.generated.p = x + modules: + - | + package generated + + p if { + -1 <= 0 + data.a[i] = x + x <= 1 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0614.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0614.yaml new file mode 100644 index 000000000000..b17fa7e98a0b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0614.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "comparisonexpr/undefined: equals" + query: data.generated.p = x + modules: + - | + package generated + + p if { + 0 = 1 + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0615.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0615.yaml new file mode 100644 index 000000000000..e90d8c0d3965 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0615.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "comparisonexpr/undefined: noteq" + query: data.generated.p = x + modules: + - | + package generated + + p if { + 0 != 0 + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0616.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0616.yaml new file mode 100644 index 000000000000..0d3b7f804c72 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0616.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "comparisonexpr/undefined: gt" + query: data.generated.p = x + modules: + - | + package generated + + p if { + 1 > 2 + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0617.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0617.yaml new file mode 100644 index 000000000000..ef688daf15db --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0617.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "comparisonexpr/undefined: gteq" + query: data.generated.p = x + modules: + - | + package generated + + p if { + 1 >= 2 + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0618.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0618.yaml new file mode 100644 index 000000000000..69710d1816bb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0618.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "comparisonexpr/undefined: lt" + query: data.generated.p = x + modules: + - | + package generated + + p if { + 1 < -1 + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0619.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0619.yaml new file mode 100644 index 000000000000..2af90705533b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0619.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: "comparisonexpr/undefined: lteq" + query: data.generated.p = x + modules: + - | + package generated + + p if { + 1 <= -1 + } + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0620.yaml b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0620.yaml new file mode 100644 index 000000000000..8f9bcb561563 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comparisonexpr/test-comparisonexpr-0620.yaml @@ -0,0 +1,46 @@ +--- +cases: + - note: "comparisonexpr/numbers: int and float comparison" + query: data.comparison.p = x + modules: + - | + package comparison + + p if { + 1 == 1.0 + } + want_result: + - x: true + - note: "comparisonexpr/numbers: int and float array comparison" + query: data.comparison.p = x + modules: + - | + package comparison + + p if { + [1] == [1.0] + } + want_result: + - x: true + - note: "comparisonexpr/numbers: int and float object comparison" + query: data.comparison.p = x + modules: + - | + package comparison + + p if { + {1: 1} == {1: 1.0} + } + want_result: + - x: true + - note: "comparisonexpr/numbers: int and float nested object comparison" + query: data.comparison.p = x + modules: + - | + package comparison + + p if { + {"x": [1, 2, {"b": 3.0}]} == {"x": [1, 2, {"b": 3}]} + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0495.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0495.yaml new file mode 100644 index 000000000000..563a23adb2fe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0495.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: completedoc/undefined + query: data.generated.p = x + modules: + - | + package generated + + p := null if { + false + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0496.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0496.yaml new file mode 100644 index 000000000000..40bead12f7bf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0496.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: completedoc/null + query: data.generated.p = x + modules: + - | + package generated + + p := null + data: {} + want_result: + - x: null diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0497.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0497.yaml new file mode 100644 index 000000000000..9d080c890703 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0497.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: "completedoc/bool: true" + query: data.generated.p = x + modules: + - | + package generated + + p := true + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0498.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0498.yaml new file mode 100644 index 000000000000..606918434350 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0498.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: "completedoc/bool: false" + query: data.generated.p = x + modules: + - | + package generated + + p := false + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0499.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0499.yaml new file mode 100644 index 000000000000..01b46339c7cf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0499.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: "completedoc/number: 3" + query: data.generated.p = x + modules: + - | + package generated + + p := 3 + data: {} + want_result: + - x: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0500.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0500.yaml new file mode 100644 index 000000000000..507e1b84d6c2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0500.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: "completedoc/number: 3.0" + query: data.generated.p = x + modules: + - | + package generated + + p := 3.0 + data: {} + want_result: + - x: 3.0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0501.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0501.yaml new file mode 100644 index 000000000000..b29df723e6fd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0501.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: "completedoc/number: 66.66667" + query: data.generated.p = x + modules: + - | + package generated + + p := 66.66667 + data: {} + want_result: + - x: 66.66667 diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0502.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0502.yaml new file mode 100644 index 000000000000..de7fe7245d5a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0502.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: 'completedoc/string: "hello"' + query: data.generated.p = x + modules: + - | + package generated + + p := "hello" + data: {} + want_result: + - x: hello diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0503.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0503.yaml new file mode 100644 index 000000000000..14a33d097496 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0503.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: 'completedoc/string: ""' + query: data.generated.p = x + modules: + - | + package generated + + p := "" + data: {} + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0504.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0504.yaml new file mode 100644 index 000000000000..f2d4ae099b8b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0504.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "completedoc/array: [1,2,3,4]" + query: data.generated.p = x + modules: + - | + package generated + + p := [1, 2, 3, 4] + data: {} + want_result: + - x: + - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0505.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0505.yaml new file mode 100644 index 000000000000..721ed2407d22 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0505.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: "completedoc/array: []" + query: data.generated.p = x + modules: + - | + package generated + + p := [] + data: {} + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0506.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0506.yaml new file mode 100644 index 000000000000..9db7533aa41b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0506.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: 'completedoc/object/nested composites: {"a": [1], "b": [2], "c": [3]}' + query: data.generated.p = x + modules: + - | + package generated + + p := {"a": [1], "b": [2], "c": [3]} + data: {} + want_result: + - x: + a: + - 1 + b: + - 2 + c: + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0507.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0507.yaml new file mode 100644 index 000000000000..b8eb8d4c05c5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0507.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "completedoc/object/non-string key:" + query: data.generated.p = x + modules: + - | + package generated + + p := {1: 2, {3: 4}: 5} + data: {} + want_result: + - x: + '{"3":4}': 5 + "1": 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0508.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0508.yaml new file mode 100644 index 000000000000..cb3577f0f1a7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0508.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: "completedoc/set/nested: {{1,2},{2,3}}" + query: data.generated.p = x + modules: + - | + package generated + + p := {{1, 2}, {2, 3}} + data: {} + want_result: + - x: + - - 1 + - 2 + - - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0509.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0509.yaml new file mode 100644 index 000000000000..8d87b235e871 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0509.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: completedoc/vars + query: data.generated.p = x + modules: + - | + package generated + + p := {"a": [x, y]} if { + x = 1 + y = 2 + } + data: {} + want_result: + - x: + a: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0510.yaml b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0510.yaml new file mode 100644 index 000000000000..6c6c55cade1d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/completedoc/test-completedoc-0510.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: completedoc/vars conflict + query: data.generated.p = x + modules: + - | + package generated + + p := {"a": [x, y]} if { + xs = [1, 2] + ys = [1, 2] + x = xs[_] + y = ys[_] + } + data: {} + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositebasedereference/test-compositebasedereference-1073.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositebasedereference/test-compositebasedereference-1073.yaml new file mode 100644 index 000000000000..a9373cf2a89e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositebasedereference/test-compositebasedereference-1073.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: compositebasedereference/array + query: data.generated.p = x + modules: + - | + package generated + + p if { + not data.a[[0]] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositebasedereference/test-compositebasedereference-1074.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositebasedereference/test-compositebasedereference-1074.yaml new file mode 100644 index 000000000000..cb0f03e9a054 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositebasedereference/test-compositebasedereference-1074.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: compositebasedereference/object + query: data.generated.p = x + modules: + - | + package generated + + p if { + not data.a[{"b": "c"}] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositebasedereference/test-compositebasedereference-1075.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositebasedereference/test-compositebasedereference-1075.yaml new file mode 100644 index 000000000000..f079be0ce847 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositebasedereference/test-compositebasedereference-1075.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: compositebasedereference/set + query: data.generated.p = x + modules: + - | + package generated + + p if { + not data.a[["b"]] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0743.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0743.yaml new file mode 100644 index 000000000000..834e7e14fa4f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0743.yaml @@ -0,0 +1,39 @@ +--- +cases: + - note: compositereferences/array + query: data.test.p = x + modules: + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + - | + package test + + p := __local0__ if { + true + __local0__ = data.fixture.r[[1, 2]] + } + data: {} + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0744.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0744.yaml new file mode 100644 index 000000000000..9183d25284eb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0744.yaml @@ -0,0 +1,38 @@ +--- +cases: + - note: compositereferences/object + query: data.test.p = x + modules: + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + - | + package test + + p := __local0__ if { + true + __local0__ = data.fixture.r[{"foo": "bar"}] + } + data: {} + want_result: + - x: + foo: bar diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0745.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0745.yaml new file mode 100644 index 000000000000..cb1947d612a5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0745.yaml @@ -0,0 +1,39 @@ +--- +cases: + - note: compositereferences/set + query: data.test.p = x + modules: + - | + package test + + p := __local0__ if { + true + __local0__ = data.fixture.r[{1, 2}] + } + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + data: {} + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0746.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0746.yaml new file mode 100644 index 000000000000..47dda90a97c9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0746.yaml @@ -0,0 +1,40 @@ +--- +cases: + - note: compositereferences/unify array + query: data.test.p = x + modules: + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + - | + package test + + p := __local0__ if { + true + __local0__ = [x | data.fixture.r[[1, x]]] + } + data: {} + want_result: + - x: + - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0747.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0747.yaml new file mode 100644 index 000000000000..32ad78739b15 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0747.yaml @@ -0,0 +1,38 @@ +--- +cases: + - note: compositereferences/unify object + query: data.test.p = x + modules: + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + - | + package test + + p := __local0__ if { + true + __local0__ = [x | data.fixture.r[{"foo": x}]] + } + data: {} + want_result: + - x: + - bar diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0748.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0748.yaml new file mode 100644 index 000000000000..567285d38ddd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0748.yaml @@ -0,0 +1,40 @@ +--- +cases: + - note: compositereferences/unify partial ground array + query: data.test.p = x + modules: + - | + package test + + p := __local0__ if { + true + __local0__ = [x | data.fixture.p1[[x, 2]]] + } + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + data: {} + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0749.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0749.yaml new file mode 100644 index 000000000000..a04e75e3d1f1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0749.yaml @@ -0,0 +1,47 @@ +--- +cases: + - note: compositereferences/complete doc unify + query: data.test.p = x + modules: + - | + package test + + p := __local0__ if { + true + __local0__ = [[x, y] | data.fixture.s[[x, y]]] + } + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + data: {} + want_result: + - x: + - - 1 + - 2 + - - 1 + - 3 + - - 2 + - 7 + - - - 1 + - 1 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0750.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0750.yaml new file mode 100644 index 000000000000..8d7ecc4f4752 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0750.yaml @@ -0,0 +1,47 @@ +--- +cases: + - note: compositereferences/partial doc unify + query: data.test.p = x + modules: + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + - | + package test + + p := __local0__ if { + true + __local0__ = [[x, y] | data.fixture.r[[x, y]]] + } + data: {} + want_result: + - x: + - - 1 + - 2 + - - 1 + - 3 + - - 2 + - 7 + - - - 1 + - 1 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0751.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0751.yaml new file mode 100644 index 000000000000..abf5080c9769 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0751.yaml @@ -0,0 +1,36 @@ +--- +cases: + - note: compositereferences/empty set + query: data.test.p = x + modules: + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + - | + package test + + p if { + data.fixture.empty[set()] + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0752.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0752.yaml new file mode 100644 index 000000000000..d5861830de56 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0752.yaml @@ -0,0 +1,40 @@ +--- +cases: + - note: compositereferences/ref + query: data.test.p = x + modules: + - | + package test + + p := __local0__ if { + true + __local1__ = data.fixture.foo.bar + __local0__ = data.fixture.r[[__local1__, 3]] + } + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + data: {} + want_result: + - x: + - 1 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0753.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0753.yaml new file mode 100644 index 000000000000..aa13a695296a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0753.yaml @@ -0,0 +1,41 @@ +--- +cases: + - note: compositereferences/nested ref + query: data.test.p = x + modules: + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + - | + package test + + p := __local0__ if { + true + __local1__ = data.fixture.o.foo + __local2__ = data.fixture.foo[__local1__] + __local0__ = data.fixture.r[[__local2__, 3]] + } + data: {} + want_result: + - x: + - 1 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0754.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0754.yaml new file mode 100644 index 000000000000..7ca5cc96eef9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0754.yaml @@ -0,0 +1,41 @@ +--- +cases: + - note: compositereferences/comprehension + query: data.test.p = x + modules: + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + - | + package test + + p := __local0__ if { + true + __local1__ = [x | y = [1, 1]; x = y[_]] + __local0__ = data.fixture.s[[__local1__, 4]] + } + data: {} + want_result: + - x: + - - 1 + - 1 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0755.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0755.yaml new file mode 100644 index 000000000000..919e7495382f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0755.yaml @@ -0,0 +1,36 @@ +--- +cases: + - note: compositereferences/missing array + query: data.test.p = x + modules: + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + - | + package test + + p := __local0__ if { + true + __local0__ = data.fixture.r[[1, 4]] + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0756.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0756.yaml new file mode 100644 index 000000000000..54a6d1d98360 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0756.yaml @@ -0,0 +1,36 @@ +--- +cases: + - note: compositereferences/missing object value + query: data.test.p = x + modules: + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + - | + package test + + p := __local0__ if { + true + __local0__ = data.fixture.r[{"foo": "baz"}] + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0757.yaml b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0757.yaml new file mode 100644 index 000000000000..c3a704e76eb1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/compositereferences/test-compositereferences-0757.yaml @@ -0,0 +1,36 @@ +--- +cases: + - note: compositereferences/missing set + query: data.test.p = x + modules: + - | + package fixture + + empty := {set()} + + s := {[1, 2], [1, 3], {"foo": "bar"}, {1, 2}, [2, 7], [[1, 1], 4]} + + r contains x if { + data.fixture.s[x] + } + + a := [1, 2] + + o := {"foo": "bar"} + + foo := {"bar": 1} + + p1 contains [1, 2] + + p1 contains [1, 3] + + p1 contains [2, 2] + - | + package test + + p := __local0__ if { + true + __local0__ = data.fixture.r[{1, 3}] + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0781.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0781.yaml new file mode 100644 index 000000000000..8967bc4c7190 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0781.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: comprehensions/array simple + query: data.generated.p = x + modules: + - | + package generated + + p contains i if { + xs = [x | x = data.a[_]] + __local0__ = xs[i] + __local0__ > 1 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0782.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0782.yaml new file mode 100644 index 000000000000..01e7899bece2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0782.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: comprehensions/array nested + query: data.generated.p = x + modules: + - | + package generated + + p contains i if { + ys = [y | x = [z | z = data.a[_]]; y = x[_]] + __local0__ = ys[i] + __local0__ > 1 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0783.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0783.yaml new file mode 100644 index 000000000000..5ebce67414fb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0783.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: comprehensions/array embedded array + query: data.generated.p = x + modules: + - | + package generated + + p contains i if { + __local0__ = [x | x = data.a[_]] + xs = [__local0__] + __local1__ = xs[0][i] + __local1__ > 1 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0784.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0784.yaml new file mode 100644 index 000000000000..0290005771f0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0784.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: comprehensions/array embedded object + query: data.generated.p = x + modules: + - | + package generated + + p contains i if { + __local0__ = [x | x = data.a[_]] + xs = {"a": __local0__} + __local1__ = xs.a[i] + __local1__ > 1 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0785.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0785.yaml new file mode 100644 index 000000000000..67eff3296817 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0785.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: comprehensions/array embedded set + query: data.generated.p = x + modules: + - | + package generated + + p := xs if { + __local0__ = [x | x = data.a[_]] + xs = {__local0__} + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0786.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0786.yaml new file mode 100644 index 000000000000..108e51560f87 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0786.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: comprehensions/array closure + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + y = 1 + x = [y | y = 1] + } + data: {} + want_result: + - x: + - - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0787.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0787.yaml new file mode 100644 index 000000000000..197df2edc07c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0787.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: comprehensions/array dereference embedded + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q.a[2][i] = x + } + + q[k] := v if { + k = "a" + v = [y | i = [z | z = data.a[_]]; i[_] = _; i = y] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0788.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0788.yaml new file mode 100644 index 000000000000..fbfabcdf936b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0788.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: comprehensions/object simple + query: data.generated.p = x + modules: + - | + package generated + + p contains i if { + xs = {s: x | x = data.a[_]; format_int(x, 10, s)} + y = xs[i] + y > 1 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - "2" + - "3" + - "4" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0789.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0789.yaml new file mode 100644 index 000000000000..335133850a0e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0789.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: comprehensions/object non-string key + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + xs = {k: 1 | data.a[_] = k} + xs[x] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0790.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0790.yaml new file mode 100644 index 000000000000..8e60bd03f239 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0790.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: comprehensions/object nested + query: data.generated.p = x + modules: + - | + package generated + + p := r if { + r = {x: y | z = {i: q | i = data.b[q]}; x = z[y]} + } + data: + b: + v1: hello + v2: goodbye + want_result: + - x: + v1: hello + v2: goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0791.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0791.yaml new file mode 100644 index 000000000000..e0cab8efac5a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0791.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: comprehensions/object embedded array + query: data.generated.p = x + modules: + - | + package generated + + p contains i if { + __local0__ = {s: x | x = data.a[_]; format_int(x, 10, s)} + xs = [__local0__] + __local1__ = xs[0][i] + __local1__ > 1 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - "2" + - "3" + - "4" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0792.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0792.yaml new file mode 100644 index 000000000000..003b5a1d76b3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0792.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: comprehensions/object embedded object + query: data.generated.p = x + modules: + - | + package generated + + p contains i if { + __local0__ = {s: x | x = data.a[_]; format_int(x, 10, s)} + xs = {"a": __local0__} + __local1__ = xs.a[i] + __local1__ > 1 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - "2" + - "3" + - "4" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0793.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0793.yaml new file mode 100644 index 000000000000..7a4729cc7a98 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0793.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: comprehensions/object embedded set + query: data.generated.p = x + modules: + - | + package generated + + p := xs if { + __local0__ = {s: x | x = data.a[_]; format_int(x, 10, s)} + xs = {__local0__} + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - "1": 1 + "2": 2 + "3": 3 + "4": 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0794.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0794.yaml new file mode 100644 index 000000000000..5d4ba82a7159 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0794.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: comprehensions/object closure + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + y = 1 + x = {"foo": y | y = 1} + } + data: {} + want_result: + - x: + - foo: 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0795.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0795.yaml new file mode 100644 index 000000000000..8548cface4c8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0795.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: comprehensions/object dereference embedded + query: data.generated.p = x + modules: + - | + package generated + + arr := [4] + + p contains x if { + data.generated.q.a = x + } + + q[k] := v if { + k = "a" + v = {"bar": y | i = {"foo": z | z = data.generated.arr[_]}; i[_] = _; i = y} + } + data: {} + want_result: + - x: + - bar: + foo: 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0796.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0796.yaml new file mode 100644 index 000000000000..28cbe4aac118 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0796.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: comprehensions/object conflict + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q.a = x + } + + q[k] := v if { + k = "a" + v = {"bar": y | i = {"foo": z | z = data.a[_]}; i[_] = _; i = y} + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_error_code: eval_conflict_error + want_error: object keys must be unique diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0797.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0797.yaml new file mode 100644 index 000000000000..3b190130d365 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0797.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: comprehensions/set simple + query: data.generated.p = x + modules: + - | + package generated + + p := y if { + y = {x | x = data.a[_]; x > 1} + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0798.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0798.yaml new file mode 100644 index 000000000000..7a4a5a372c2e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0798.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: comprehensions/set nested + query: data.generated.p = x + modules: + - | + package generated + + p contains i if { + ys = {y | x = {z | z = data.a[_]}; y = x[_]} + __local0__ = ys[i] + __local0__ > 1 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0799.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0799.yaml new file mode 100644 index 000000000000..7a1feda8432f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0799.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: comprehensions/set embedded array + query: data.generated.p = x + modules: + - | + package generated + + p contains i if { + __local0__ = {x | x = data.a[_]} + xs = [__local0__] + __local1__ = xs[0][i] + __local1__ > 1 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0800.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0800.yaml new file mode 100644 index 000000000000..274b3c476084 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0800.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: comprehensions/set embedded object + query: data.generated.p = x + modules: + - | + package generated + + p contains i if { + __local0__ = {x | x = data.a[_]} + xs = {"a": __local0__} + __local1__ = xs.a[i] + __local1__ > 1 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0801.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0801.yaml new file mode 100644 index 000000000000..c38a06607de5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0801.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: comprehensions/set embedded set + query: data.generated.p = x + modules: + - | + package generated + + p := xs if { + __local0__ = {x | x = data.a[_]} + xs = {__local0__} + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - - 1 + - 2 + - 3 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0802.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0802.yaml new file mode 100644 index 000000000000..3d3dfa5c7819 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0802.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: comprehensions/set closure + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + y = 1 + x = {y | y = 1} + } + data: {} + want_result: + - x: + - - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0803.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0803.yaml new file mode 100644 index 000000000000..a1a63b332185 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-0803.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: comprehensions/set dereference embedded + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q.a = x + } + + q[k] := v if { + k = "a" + v = {y | i = {z | z = data.a[_]}; i[_] = _; i = y} + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - - - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-and-vars.yaml b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-and-vars.yaml new file mode 100644 index 000000000000..3deaed1b0f15 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/comprehensions/test-comprehensions-and-vars.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: comprehensions/var bindings have no effect outside + query: data.test.p = x + modules: + - | + package test + + xs := {"a", "b", "c"} + + p := x if { + y := {x | xs[x]} + z := {x | xs[x]} + count(y) == count(z) + x := count(y) + } + want_result: + - x: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/containskeyword/test-contains-future-keyword.yaml b/third_party/opa/v1/test/cases/testdata/v1/containskeyword/test-contains-future-keyword.yaml new file mode 100644 index 000000000000..f990959d346f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/containskeyword/test-contains-future-keyword.yaml @@ -0,0 +1,63 @@ +--- +cases: + - note: containskeyword/base case + query: data.test.p = x + modules: + - | + package test + + p if { + contains("fireplace", "repl") + } + want_result: + - x: true + - note: containskeyword/with unused kw import + query: data.test.p = x + modules: + - | + package test + + p if { + contains("fireplace", "repl") + } + want_result: + - x: true + - note: containskeyword/with kw and builtin used + query: data.test.p = x + modules: + - | + package test + + p contains "x" if { + contains("fireplace", "repl") + } + want_result: + - x: + - x + - note: containskeyword/empty body + query: data.test.p = x + modules: + - | + package test + + p contains "x" + want_result: + - x: + - x + - note: containskeyword/ordinary deny rule + query: data.test.p = x + modules: + - | + package test + + p contains msg if { + msg := "nono" + } + + p contains msg if { + msg := "nonono" + } + want_result: + - x: + - nono + - nonono diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptohmacequal/test-cryptohmacequal.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptohmacequal/test-cryptohmacequal.yaml new file mode 100644 index 000000000000..cdc939de1869 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptohmacequal/test-cryptohmacequal.yaml @@ -0,0 +1,77 @@ +--- +cases: + - note: cryptohmacequal/crypto.hmac.equal_md5 + query: data.test.p = x + modules: + - | + package test + + p contains res if { + res := crypto.hmac.equal(input.mac1, input.mac2) + } + input: + mac1: 31b6db9e5eb4addb42f1a6ca07367adc + mac2: 31b6db9e5eb4addb42f1a6ca07367adc + want_result: + - x: + - true + - note: cryptohmacequal/crypto.hmac.equal_sha1 + query: data.test.p = x + modules: + - | + package test + + p contains res if { + res := crypto.hmac.equal(input.mac1, input.mac2) + } + input: + mac1: 85d155c55ed286a300bd1cf124de08d87e914f3a + mac2: 85d155c55ed286a300bd1cf124de08d87e914f3a + want_result: + - x: + - true + - note: cryptohmacequal/crypto.hmac.equal_sha256 + query: data.test.p = x + modules: + - | + package test + + p contains res if { + res := crypto.hmac.equal(input.mac1, input.mac2) + } + input: + mac1: 147933218aaabc0b8b10a2b3a5c34684c8d94341bcf10a4736dc7270f7741851 + mac2: 147933218aaabc0b8b10a2b3a5c34684c8d94341bcf10a4736dc7270f7741851 + want_result: + - x: + - true + - note: cryptohmacequal/crypto.hmac.equal_sha512 + query: data.test.p = x + modules: + - | + package test + + p contains res if { + res := crypto.hmac.equal(input.mac1, input.mac2) + } + input: + mac1: 24257d7210582a65c731ec55159c8184cc24c02489453e58587f71f44c23a2d61b4b72154a89d17b2d49448a8452ea066f4fc56a2bcead45c088572ffccdb3d8 + mac2: 24257d7210582a65c731ec55159c8184cc24c02489453e58587f71f44c23a2d61b4b72154a89d17b2d49448a8452ea066f4fc56a2bcead45c088572ffccdb3d8 + want_result: + - x: + - true + - note: cryptohmacequal/crypto.hmac.equal_false + query: data.test.p = x + modules: + - | + package test + + p contains res if { + res := crypto.hmac.equal(input.mac1, input.mac2) + } + input: + mac1: 31b6db9e5eb4addb42f1a6ca07367adc + mac2: 31b6db9e5eb4addb + want_result: + - x: + - false diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptohmacmd5/test-cryptohmacmd5.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptohmacmd5/test-cryptohmacmd5.yaml new file mode 100644 index 000000000000..d8bab904bcfc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptohmacmd5/test-cryptohmacmd5.yaml @@ -0,0 +1,33 @@ +--- +cases: + - note: cryptohmacmd5/crypto.hmac.md5 + query: data.test.p = x + modules: + - | + package test + + p contains mac if { + mac := crypto.hmac.md5(input.message, input.key) + } + input: + key: bar + message: foo + want_result: + - x: + - 31b6db9e5eb4addb42f1a6ca07367adc + - note: cryptohmacmd5/crypto.hmac.md5_unicode + query: data.test.p = x + modules: + - | + package test + + p contains mac if { + mac := crypto.hmac.md5(input.message, input.key) + } + input: + key: 秘密の + message: "åäöçß\U0001F972♙Ω" + want_result: + - x: + - 20a8743c2157ac60b7e8b79c83651b8d + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptohmacsha1/test-cryptohmacsha1.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptohmacsha1/test-cryptohmacsha1.yaml new file mode 100644 index 000000000000..173540e8af4b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptohmacsha1/test-cryptohmacsha1.yaml @@ -0,0 +1,33 @@ +--- +cases: + - note: cryptohmacsha1/crypto.hmac.sha1 + query: data.test.p = x + modules: + - | + package test + + p contains mac if { + mac := crypto.hmac.sha1(input.message, input.key) + } + input: + key: bar + message: foo + want_result: + - x: + - 85d155c55ed286a300bd1cf124de08d87e914f3a + - note: cryptohmacsha1/crypto.hmac.sha1_unicode + query: data.test.p = x + modules: + - | + package test + + p contains mac if { + mac := crypto.hmac.sha1(input.message, input.key) + } + input: + key: 秘密の + message: "åäöçß\U0001F972♙Ω" + want_result: + - x: + - 81759c39013935fcf0de833d44c8018d7c1455dd + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptohmacsha256/test-cryptohmacsha256.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptohmacsha256/test-cryptohmacsha256.yaml new file mode 100644 index 000000000000..790827498c57 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptohmacsha256/test-cryptohmacsha256.yaml @@ -0,0 +1,33 @@ +--- +cases: + - note: cryptohmacsha256/crypto.hmac.sha256 + query: data.test.p = x + modules: + - | + package test + + p contains mac if { + mac := crypto.hmac.sha256(input.message, input.key) + } + input: + key: bar + message: foo + want_result: + - x: + - 147933218aaabc0b8b10a2b3a5c34684c8d94341bcf10a4736dc7270f7741851 + - note: cryptohmacsha256/crypto.hmac.sha256_unicode + query: data.test.p = x + modules: + - | + package test + + p contains mac if { + mac := crypto.hmac.sha256(input.message, input.key) + } + input: + key: 秘密の + message: "åäöçß\U0001F972♙Ω" + want_result: + - x: + - eb90daeb76d4b2571fbdaf94bbb240809faa8fed93ec0c260dd38c3fdf8d963a + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptohmacsha512/test-cryptohmacsha512.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptohmacsha512/test-cryptohmacsha512.yaml new file mode 100644 index 000000000000..3968e68bc8d2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptohmacsha512/test-cryptohmacsha512.yaml @@ -0,0 +1,33 @@ +--- +cases: + - note: cryptohmacsha512/crypto.hmac.sha512 + query: data.test.p = x + modules: + - | + package test + + p contains mac if { + mac := crypto.hmac.sha512(input.message, input.key) + } + input: + key: bar + message: foo + want_result: + - x: + - 24257d7210582a65c731ec55159c8184cc24c02489453e58587f71f44c23a2d61b4b72154a89d17b2d49448a8452ea066f4fc56a2bcead45c088572ffccdb3d8 + - note: cryptohmacsha512/crypto.hmac.sha512_unicode + query: data.test.p = x + modules: + - | + package test + + p contains mac if { + mac := crypto.hmac.sha512(input.message, input.key) + } + input: + key: 秘密の + message: "åäöçß\U0001F972♙Ω" + want_result: + - x: + - 192f5afded233d6e21427aa26ed267ac118cfa2971013d91cbed530c0b208d78138b83dfe1d6cc3553d7bd518f22a481402c723028e1279d1ffbe8f11ea6b125 + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptomd5/test-cryptomd5-0130.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptomd5/test-cryptomd5-0130.yaml new file mode 100644 index 000000000000..74ba18c46834 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptomd5/test-cryptomd5-0130.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: cryptomd5/crypto.md5 with string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + crypto.md5("lorem ipsum", __local1__) + __local0__ = __local1__ + } + want_result: + - x: + - 80a751fde577028640c419000e33eba6 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptoparsersaprivatekeys/test-cryptoparsersaprivatekey-1.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptoparsersaprivatekeys/test-cryptoparsersaprivatekey-1.yaml new file mode 100644 index 000000000000..9b3a036e109f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptoparsersaprivatekeys/test-cryptoparsersaprivatekey-1.yaml @@ -0,0 +1,37 @@ +--- +cases: + - note: cryptoparseprivatekey/valid + query: data.testing.p = x + modules: + - | + package testing + + pem := "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA9D/bK4171aiTNUkrUCHKGMLSQooV+o3wdz2889h9iv0HhhBJ\nCAGU54K3duB8ofHpmYL50QodcR4RLw1vSkaI+FPdPDMyKxKj/YcmofJjz4kW+Iqw\nFbBcbMnKnEVzye+CyW9YYOTu0xWtcgen80zGp2opG0GZX86hBjjXJnjOdrJTk6x2\nNAiJIbjsQevysmj+2MyqVm8widxw0x+rGhTaCD+ZXWitN0a0WO1aaA8c/7i99I9z\nhe2peKvXzEtMaqYO9ptHcYmq2z0QWvZuJVMv5Yn0mScLWyh91R099IOtn6sNaMMs\nOyTpi7E/2IlVgI2uKGPEopKkMFV8Fl2YaAbo7wIDAQABAoIBAAyMZ08ygqU0dvOq\n4a3JPp/NCo5el8h6mFsX8eg5PCHy4/sQRSBDLIpEXfaei+iqDA1V/E2wDlksaUeY\nkhony4uui1Q3cSFjYMd6tRJm6JfV/DcisO88U1NHfsBOlSdPxdFhhhHcUSTJHVMZ\nb5iBXkdlnd0HnsCcVguCyhLw6/KPFyiA+NYRz68flxze7admyVp5C6i/HbMPq8Pr\nMilBUvOFtxuaGeJBAiavuzUe9I70dRwpe424tMvisSA8h7Xbm8BeN/PJHDV/2JrI\nURgQ563yQ5So/Qg8AgxXRkpgWM9zAh7r31PBO86vq/B4ZbON/TtWdcZVsAcVB4Pk\ntqc8JNkCgYEA+g8V+y92SETdcwUkbd5O9Fg5CkfdsALsBXVH6FunrCUV5HS9l5o6\nMMBbJ/08odW/bP5BmOa4A/Hbk9uG/UfQn2KQ3HCgPlxUEwQO07R1/FcQOe4xmyG6\nJpDgQ30viE1RtlCkceQWUeitCIqZsYu0i8sLZLWJH+V/07OB4G17ELMCgYEA+g1v\nhrlAFNhZvrIX/zcP3xF2pZ+AqkFXdL/tWQZkWAVToONn/LlXTH71C/TO2x+OaQRm\nqX1bA9Zhyjf1gYQN9RenjUswvggk0aY2Tk28wUqowMGSsjQHmZ20EphHNMWNJpdS\nfKFfrQIFKCnLlpQVNz+j3bLWZUnq+jPaYnJP7NUCgYEA48qcVo7c7Ga3aNEVZ3St\nbg90HrZq760pvqshDz13V+0MrWnfUFxxh/mi0KHy+uYRlMNllFkQ5p8LTP0dUlt6\nY8dReU6r20MWX6BBtX9eP7o8ENm4nL4zqnAtq609gKgWuMNrmkiSQJl6Dx7bdY5z\nsSkNPvfUa5cQRBTxSjXRdtsCgYBHrzpdwRXh4/Q2ew/uFnbyWCtPZ96W8IyF58+/\nSdnSchR7dzYEeY3RXEQb3V6/6tgEu0JDLLC+9OKr+kbjjlwB+3oJQ5kBoYwMnj3L\nTPXj4+dk+xl3BPt4yoEpI4amVkwU2CTJnemzy3R3AyReUq2SXSg5El/sQbifaeYd\neu/20QKBgH/5IZHGBKiRAe1ww2FzOpDtL8VXXTe3EAXKutfajrHTqPz9+lXknX/D\nUMosh264nYXYS29WqxhJVutbE9u8e0VpuY1qIN9/3R0WKfTLTMUFlZtbqTepvsy1\nW2UbK732I4Nfp0/mtUvOSdMZO8dxbSdEeMnw/Ec8QgxK9a1rRu9+\n-----END RSA PRIVATE KEY-----" + + p if { + count(crypto.parse_private_keys(pem)) == 1 + } + want_result: + - x: true + - note: cryptoparseprivatekey/invalid + query: data.testing.p = x + modules: + - | + package testing + + pem := "nope" + + p := crypto.parse_private_keys(pem) + want_result: + - x: [] + - note: cryptoparseprivatekey/invalid + query: data.testing.p = x + modules: + - | + package testing + + pem := "" + + p := crypto.parse_private_keys(pem) + want_result: + - x: null diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptosha1/test-cryptosha1-0131.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptosha1/test-cryptosha1-0131.yaml new file mode 100644 index 000000000000..6ab451d7b321 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptosha1/test-cryptosha1-0131.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: cryptosha1/crypto.sha1 with string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + crypto.sha1("lorem ipsum", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - bfb7759a67daeb65410490b4d98bb9da7d1ea2ce + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptosha256/test-cryptosha256-0132.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptosha256/test-cryptosha256-0132.yaml new file mode 100644 index 000000000000..29713088795c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptosha256/test-cryptosha256-0132.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: cryptosha256/crypto.sha256 with string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + crypto.sha256("lorem ipsum", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 5e2bf57d3f40c4b6df69daf1936cb766f832374b4fc0259a7cbff06e2f70f269 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parseandverifycertificates/test-cryptox509parseandverifycertificates.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parseandverifycertificates/test-cryptox509parseandverifycertificates.yaml new file mode 100644 index 000000000000..19d0378ddd41 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parseandverifycertificates/test-cryptox509parseandverifycertificates.yaml @@ -0,0 +1,130 @@ +--- +cases: + - note: cryptox509parseandverifycertificates/base_case + query: data.test.result = x + modules: + - | + package test + + certs := `-----BEGIN CERTIFICATE----- + MIIBoDCCAUagAwIBAgIRAJXcMYZALXooNq/VV/grXhMwCgYIKoZIzj0EAwIwLjER + MA8GA1UEChMIT1BBIFRlc3QxGTAXBgNVBAMTEE9QQSBUZXN0IFJvb3QgQ0EwHhcN + MjEwNzAxMTc0MTUzWhcNMzEwNjI5MTc0MTUzWjAuMREwDwYDVQQKEwhPUEEgVGVz + dDEZMBcGA1UEAxMQT1BBIFRlc3QgUm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49 + AwEHA0IABFqhdZA5LjsJgzsBvhgzfayZFOk+C7PmGCi7xz6zOC3xWORJZSNOyZeJ + YzSKFmoMZkcFMfslTW1jp9fwe1xl3HWjRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNV + HRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTch60qxQvLl+AfDfcaXmjvT8GvpzAK + BggqhkjOPQQDAgNIADBFAiBqraIP0l2U0oNuH0+rf36hDks94wSB5EGlGH3lYNMR + ugIhANkbukX5hOP8pJDRWP/pYuv6MBnRY4BS8gpp9Vu31qOb + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIByDCCAW6gAwIBAgIQC0k4DPGrh9me73EJX5zntTAKBggqhkjOPQQDAjAuMREw + DwYDVQQKEwhPUEEgVGVzdDEZMBcGA1UEAxMQT1BBIFRlc3QgUm9vdCBDQTAeFw0y + MTA3MDExNzQxNTNaFw0zMTA2MjkxNzQxNTNaMDYxETAPBgNVBAoTCE9QQSBUZXN0 + MSEwHwYDVQQDExhPUEEgVGVzdCBJbnRlcm1lZGlhdGUgQ0EwWTATBgcqhkjOPQIB + BggqhkjOPQMBBwNCAARvXQa7fy476gDI81nqLYb2SnD459WxBmU0hk2bA3ZuNtI+ + H20KXz6ISmxH3MZ2WBm6rOy7y4Gn+WMCJuxzcl5jo2YwZDAOBgNVHQ8BAf8EBAMC + AQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuslZNjJl0V8I1Gj17IID + ALy/9WEwHwYDVR0jBBgwFoAU3IetKsULy5fgHw33Gl5o70/Br6cwCgYIKoZIzj0E + AwIDSAAwRQIgUwsYApW9Tsm6AstWswaKGie0srB4FUkUbfKwWmUI2JgCIQCBTySN + MF+EiQAMKyz/N9KUuXEckC356WvKcyJaYYcV0w== + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIB8zCCAZqgAwIBAgIRAID4gPKg7DDiuOfzUYFSXLAwCgYIKoZIzj0EAwIwNjER + MA8GA1UEChMIT1BBIFRlc3QxITAfBgNVBAMTGE9QQSBUZXN0IEludGVybWVkaWF0 + ZSBDQTAeFw0yMTA3MDUxNzQ5NTBaFw0zNjA3MDExNzQ5NDdaMCUxIzAhBgNVBAMT + Gm5vdGFyZWFsc2l0ZS5vcGEubG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D + AQcDQgAE1YSXZXeaGGL+XeYyoPi/QdA39Ds4fgxSHJTMh+js393kByPm2PNtFkem + tUii3KCRJw3SEh3z0JWr/9y4+ua2L6OBmTCBljAOBgNVHQ8BAf8EBAMCB4AwHQYD + VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRL0P0g17viZHo9 + CnXe3ZQJm48LXTAfBgNVHSMEGDAWgBS6yVk2MmXRXwjUaPXsggMAvL/1YTAlBgNV + HREEHjAcghpub3RhcmVhbHNpdGUub3BhLmxvY2FsaG9zdDAKBggqhkjOPQQDAgNH + ADBEAiAtmZewL94ijN0YwUGaJM9BXCaoTQPwkzugqjCj+K912QIgKKFvbPu4asrE + nwy7dzejHmQUcZ/aUNbc4VTbiv15ESk= + -----END CERTIFICATE----- + ` + + value := crypto.x509.parse_and_verify_certificates(certs) + + result := { + "valid": value[0], + "certs": [c | + some cert in value[1] + c := { + "CN": cert.Subject.CommonName, + "DNS": cert.DNSNames, + "URI": cert.URIStrings, + } + ], + } + want_result: + - x: + certs: + - CN: notarealsite.opa.localhost + DNS: + - notarealsite.opa.localhost + URI: null + - CN: OPA Test Intermediate CA + DNS: null + URI: null + - CN: OPA Test Root CA + DNS: null + URI: null + valid: true + - note: cryptox509parseandverifycertificates/uri_strings + query: data.test.result = x + modules: + - | + package test + + certs := `-----BEGIN CERTIFICATE----- + MIIB1TCCAXugAwIBAgIIKIoxsnMwJJ4wCgYIKoZIzj0EAwIwPTELMAkGA1UEBhMC + R0IxEDAOBgNVBAoTB0V4YW1wbGUxHDAaBgNVBAUTEzI5MjEyMDE5NTA4MDk2NjI2 + MjIwIBcNMjMxMTI5MTc1NTQ2WhgPMjEyMzExMDUxNzU1NDZaMD0xCzAJBgNVBAYT + AkdCMRAwDgYDVQQKEwdFeGFtcGxlMRwwGgYDVQQFExMyOTIxMjAxOTUwODA5NjYy + NjIyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkvI9ddM0SuP9LvBWS1y64fuK + ELCjVF5W3FSKm3azKEkDi8Eq1I1UM80MgCjC5ChNNyM4+cmVUDrCkTl3SqRxa6Nj + MGEwDgYDVR0PAQH/BAQDAgIEMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFF7H + A8n3mXXnwUP0ypMJ9JwY5wasMB8GA1UdEQQYMBaGFHNwaWZmZTovL2V4YW1wbGUu + Y29tMAoGCCqGSM49BAMCA0gAMEUCIByB2l5RIWmaU8qcRv13qigbB9BV/F2raEk+ + pRQnsUcgAiEA9OvBpPKC/FBkI5vVvR7WgK5sGPna4+a0RkXxRQgN2jM= + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIB1jCCAXygAwIBAgIIV9914tIKKkMwCgYIKoZIzj0EAwIwPTELMAkGA1UEBhMC + R0IxEDAOBgNVBAoTB0V4YW1wbGUxHDAaBgNVBAUTEzI5MjEyMDE5NTA4MDk2NjI2 + MjIwIBcNMjMxMTI5MTc1NTQ2WhgPMjEyMjExMDUxNzU1NDZaMD0xCzAJBgNVBAYT + AkdCMRAwDgYDVQQKEwdFeGFtcGxlMRwwGgYDVQQFExM2MzMxOTA5MjE4MTUzMTQ2 + OTQ3MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMoy2UqvC8zL3sPfLNvG1nX5p + 6hhEyDjFtokORB4VkKiPXFryIFn8XHG0ipz6aKSwVMoDT2T/YXP/wWpVwPJCi6Nk + MGIwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD + ATAMBgNVHRMBAf8EAjAAMCMGA1UdEQQcMBqGGHNwaWZmZTovL2V4YW1wbGUuY29t + L29wYTAKBggqhkjOPQQDAgNIADBFAiBEmdSKGj2+9J5SQPIAmwdxpVTOxqmVQv2x + Vvita/AmowIhAOyX/alNJxL4iCfKUNwlC2lYxGhuWopWgB1Q32bQhTEh + -----END CERTIFICATE----- + ` + + value := crypto.x509.parse_and_verify_certificates(certs) + + result := { + "valid": value[0], + "certs": [c | + some cert in value[1] + c := { + "CN": cert.Subject.CommonName, + "DNS": cert.DNSNames, + "URI": cert.URIStrings, + } + ], + } + want_result: + - x: + certs: + - CN: "" + DNS: null + URI: + - spiffe://example.com/opa + - CN: "" + DNS: null + URI: + - spiffe://example.com + valid: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0125.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0125.yaml new file mode 100644 index 000000000000..922b866ad820 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0125.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: cryptox509parsecertificaterequest/PEM, b64 + query: data.generated.p = x + modules: + - | + package generated + + csr := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ21EQ0NBWUFDQVFBd1V6RUxNQWtHQTFVRUJoTUNWVk14RkRBU0JnTlZCQU1NQzJWNFlXMXdiR1V1WTI5dApNUW93Q0FZRFZRUUhEQUVnTVFvd0NBWURWUVFLREFFZ01Rb3dDQVlEVlFRSURBRWdNUW93Q0FZRFZRUUxEQUVnCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBMlpkaG1zaERBVTBYYnhnTk1GQWsKeEdWQnNjaHdWb2s5dXBBU2ZVWDA4VFlqMFZrV0VxNitmemdOdmRQSnd6Nm1lUDlnL01hRmhPYW91Nmh1UEhmbwpTVTlKN1FiTW56Uktsc0VJTzNodEM1QUt3OXYyZldVZGpCQS92Q1dZdXU1aUc1ZTdtUHNXWjd1cGxuVGZSekM4ClJLK0srWXJtNEQ4NHE1bHR5NEMzS2tRc0FjU0xQZk9MMXMvYjJyV21KR0FoV3NSa2doTVk2V3dza3VYWXRINTkKRzl5VURHUUhoalprcHFlZFY0OUM4c0NwMU8vWVpvU0hncDdHK0JiaFRta05CRzY3OFZHREplTnB3SG96dnRjVQpyQVNGRFJ4WnhPdTFHRzE3L1FiVW9SNVVkOTNwaUtaU0U2UHVDU2VCcy9UQmFJc3ZwUGtudVhkOXI4WGovbVd5CmtRSURBUUFCb0FBd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFBeDJkaCtkMU1CaEwwaDJYZklxaDVEYy9lYWoKU0xadGFNTWlJY1h1cC96UTl2eENXSkZlSGYzczBJdXliMEhkMlZNZ1BSYU8ydWRkY2JZdFFlKzJnWUtrTzFMWApCdHdQcXcwWHAweUF2dDUxRzJvZmVCbCtFa0ptNjk3RlNtemg4eDJJZFFBSkMzWi9ROFdMVmh3NFg2WlVicnhqCjJnTjJmaVhjS0RKbGVkcUgxY2V4WVVvbnlLSDZubG4wbzQzUUtEOFlSZG9hNVFqb3Ixb0JkY3dSTTA0VDM4ak0KV1B3d2JZTjNrVE9Ea0tiaVFVVWxVeFZuNnFnZTlNTWt0c0lOWkc0eDY1QmIwaWxTdHExRWQwN2Y5NmVnbHNKaApZVE9VRnZpZDZVSkVEcEJzcjhyZFROSW1JQkhCdkkra1BHS2FqcW83Z0VNc3hFYkNkemFHUTNZZnNYWT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0t" + + p := __local1__ if { + __local3__ = data.generated.csr + crypto.x509.parse_certificate_request(__local3__, __local2__) + __local0__ = __local2__ + __local1__ = __local0__.Subject.CommonName + } + data: {} + want_result: + - x: example.com diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0126.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0126.yaml new file mode 100644 index 000000000000..b71eeabb7106 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0126.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: cryptox509parsecertificaterequest/PEM, string + query: data.generated.p = x + modules: + - | + package generated + + csr := "-----BEGIN CERTIFICATE REQUEST-----\nMIICmDCCAYACAQAwUzELMAkGA1UEBhMCVVMxFDASBgNVBAMMC2V4YW1wbGUuY29t\nMQowCAYDVQQHDAEgMQowCAYDVQQKDAEgMQowCAYDVQQIDAEgMQowCAYDVQQLDAEg\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2ZdhmshDAU0XbxgNMFAk\nxGVBschwVok9upASfUX08TYj0VkWEq6+fzgNvdPJwz6meP9g/MaFhOaou6huPHfo\nSU9J7QbMnzRKlsEIO3htC5AKw9v2fWUdjBA/vCWYuu5iG5e7mPsWZ7uplnTfRzC8\nRK+K+Yrm4D84q5lty4C3KkQsAcSLPfOL1s/b2rWmJGAhWsRkghMY6WwskuXYtH59\nG9yUDGQHhjZkpqedV49C8sCp1O/YZoSHgp7G+BbhTmkNBG678VGDJeNpwHozvtcU\nrASFDRxZxOu1GG17/QbUoR5Ud93piKZSE6PuCSeBs/TBaIsvpPknuXd9r8Xj/mWy\nkQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAAx2dh+d1MBhL0h2XfIqh5Dc/eaj\nSLZtaMMiIcXup/zQ9vxCWJFeHf3s0Iuyb0Hd2VMgPRaO2uddcbYtQe+2gYKkO1LX\nBtwPqw0Xp0yAvt51G2ofeBl+EkJm697FSmzh8x2IdQAJC3Z/Q8WLVhw4X6ZUbrxj\n2gN2fiXcKDJledqH1cexYUonyKH6nln0o43QKD8YRdoa5Qjor1oBdcwRM04T38jM\nWPwwbYN3kTODkKbiQUUlUxVn6qge9MMktsINZG4x65Bb0ilStq1Ed07f96eglsJh\nYTOUFvid6UJEDpBsr8rdTNImIBHBvI+kPGKajqo7gEMsxEbCdzaGQ3YfsXY=\n-----END CERTIFICATE REQUEST-----" + + p := __local1__ if { + __local3__ = data.generated.csr + crypto.x509.parse_certificate_request(__local3__, __local2__) + __local0__ = __local2__ + __local1__ = __local0__.Subject.CommonName + } + data: {} + want_result: + - x: example.com diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0127.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0127.yaml new file mode 100644 index 000000000000..1aaa0ba5e89c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0127.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: cryptox509parsecertificaterequest/DER, b64 + query: data.generated.p = x + modules: + - | + package generated + + csr := "MIICmDCCAYACAQAwUzELMAkGA1UEBhMCVVMxFDASBgNVBAMMC2V4YW1wbGUuY29tMQowCAYDVQQHDAEgMQowCAYDVQQKDAEgMQowCAYDVQQIDAEgMQowCAYDVQQLDAEgMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2ZdhmshDAU0XbxgNMFAkxGVBschwVok9upASfUX08TYj0VkWEq6+fzgNvdPJwz6meP9g/MaFhOaou6huPHfoSU9J7QbMnzRKlsEIO3htC5AKw9v2fWUdjBA/vCWYuu5iG5e7mPsWZ7uplnTfRzC8RK+K+Yrm4D84q5lty4C3KkQsAcSLPfOL1s/b2rWmJGAhWsRkghMY6WwskuXYtH59G9yUDGQHhjZkpqedV49C8sCp1O/YZoSHgp7G+BbhTmkNBG678VGDJeNpwHozvtcUrASFDRxZxOu1GG17/QbUoR5Ud93piKZSE6PuCSeBs/TBaIsvpPknuXd9r8Xj/mWykQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAAx2dh+d1MBhL0h2XfIqh5Dc/eajSLZtaMMiIcXup/zQ9vxCWJFeHf3s0Iuyb0Hd2VMgPRaO2uddcbYtQe+2gYKkO1LXBtwPqw0Xp0yAvt51G2ofeBl+EkJm697FSmzh8x2IdQAJC3Z/Q8WLVhw4X6ZUbrxj2gN2fiXcKDJledqH1cexYUonyKH6nln0o43QKD8YRdoa5Qjor1oBdcwRM04T38jMWPwwbYN3kTODkKbiQUUlUxVn6qge9MMktsINZG4x65Bb0ilStq1Ed07f96eglsJhYTOUFvid6UJEDpBsr8rdTNImIBHBvI+kPGKajqo7gEMsxEbCdzaGQ3YfsXY=" + + p := __local1__ if { + __local3__ = data.generated.csr + crypto.x509.parse_certificate_request(__local3__, __local2__) + __local0__ = __local2__ + __local1__ = __local0__.Subject.CommonName + } + data: {} + want_result: + - x: example.com diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0128.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0128.yaml new file mode 100644 index 000000000000..e1c203e9aa5b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0128.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: cryptox509parsecertificaterequest/invalid DER or PEM data, b64 + query: data.generated.p = x + modules: + - | + package generated + + csr := "YmFkc3RyaW5n" + + p := __local1__ if { + __local3__ = data.generated.csr + crypto.x509.parse_certificate_request(__local3__, __local2__) + __local0__ = __local2__ + __local1__ = __local0__.Subject.CommonName + } + want_error_code: eval_builtin_error + want_error: "asn1: structure error" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0129.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0129.yaml new file mode 100644 index 000000000000..3515b00efcc1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificaterequest/test-cryptox509parsecertificaterequest-0129.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: cryptox509parsecertificaterequest/invalid DER or PEM data, string + query: data.generated.p = x + modules: + - | + package generated + + csr := "foobar" + + p := __local1__ if { + __local3__ = data.generated.csr + crypto.x509.parse_certificate_request(__local3__, __local2__) + __local0__ = __local2__ + __local1__ = __local0__.Subject.CommonName + } + want_error_code: eval_builtin_error + want_error: illegal base64 + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0117.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0117.yaml new file mode 100644 index 000000000000..bda5bf897ac1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0117.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: cryptox509parsecertificates/DER, single cert, b64 + query: data.generated.p = x + modules: + - | + package generated + + certs := "MIIDujCCAqKgAwIBAgIIE31FZVaPXTUwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRlcm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwMTI5MTMyNzQzWhcNMTQwNTI5MDAwMDAwWjBpMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEYMBYGA1UEAwwPbWFpbC5nb29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfRrObuSW5T7q5CnSEqefEmtH4CCv6+5EckuriNr1CjfVvqzwfAhopXkLrq45EQm8vkmf7W96XJhC7ZM0dYi1/qOCAU8wggFLMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAaBgNVHREEEzARgg9tYWlsLmdvb2dsZS5jb20wCwYDVR0PBAQDAgeAMGgGCCsGAQUFBwEBBFwwWjArBggrBgEFBQcwAoYfaHR0cDovL3BraS5nb29nbGUuY29tL0dJQUcyLmNydDArBggrBgEFBQcwAYYfaHR0cDovL2NsaWVudHMxLmdvb2dsZS5jb20vb2NzcDAdBgNVHQ4EFgQUiJxtimAuTfwb+aUtBn5UYKreKvMwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBRK3QYWG7z2aLV29YG2u2IaulqBLzAXBgNVHSAEEDAOMAwGCisGAQQB1nkCBQEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3BraS5nb29nbGUuY29tL0dJQUcyLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAH6RYHxHdcGpMpFE3oxDoFnP+gtuBCHan2yE2GRbJ2Cw8Lw0MmuKqHlf9RSeYfd3BXeKkj1qO6TVKwCh+0HdZk283TZZyzmEOyclm3UGFYe82P/iDFt+CeQ3NpmBg+GoaVCuWAARJN/KfglbLyyYygcQq0SgeDh8dRKUiaW3HQSoYvTvdTuqzwK4CXsr3b5/dAOY8uMuG/IAR3FgwTbZ1dtoWRvOTa8hYiU6A475WuZKyEHcwnGYe57u2I2KbMgcKjPniocj4QzgYsVAVKW3IwaOhyE+vPxsiUkvQHdO2fojCkY8jg70jxM+gu59tPDNbw3Uh/2Ij310FgTHsnGQMyA==" + + p := __local2__ if { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + data: {} + want_result: + - x: + - mail.google.com diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0118.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0118.yaml new file mode 100644 index 000000000000..069d7db1761e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0118.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: cryptox509parsecertificates/DER, chain, b64 + query: data.generated.p = x + modules: + - | + package generated + + certs := "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" + + p := __local2__ if { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + data: {} + want_result: + - x: + - mail.google.com + - Thawte SGC CA diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0119.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0119.yaml new file mode 100644 index 000000000000..8d37932f11ce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0119.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: cryptox509parsecertificates/PEM, single cert, b64 + query: data.generated.p = x + modules: + - | + package generated + + certs := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlGZHpDQ0JGK2dBd0lCQWdJU0EzTnJpQUV1cy8rY3ZmbHZoVlFPVzV6VE1BMEdDU3FHU0liM0RRRUJDd1VBDQpNRW94Q3pBSkJnTlZCQVlUQWxWVE1SWXdGQVlEVlFRS0V3MU1aWFFuY3lCRmJtTnllWEIwTVNNd0lRWURWUVFEDQpFeHBNWlhRbmN5QkZibU55ZVhCMElFRjFkR2h2Y21sMGVTQllNekFlRncweU1EQTNNVEF4TmpBd016QmFGdzB5DQpNREV3TURneE5qQXdNekJhTUI0eEhEQWFCZ05WQkFNVEUyOXdaVzV3YjJ4cFkzbGhaMlZ1ZEM1dmNtY3dnZ0VpDQpNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUN5eThIWlhWVEoyVFNIWFlub0wrQ0tZcG80DQp3ejF3b3dVY2R0L1hCZ04wOGYzN054YU5rK1ZBajhHRDJzNnpob0hMU2h5WVMyUFZvc2Y3eHVtdnlHOTE0UExwDQpJSE85V21DYVpNcXdFeXZNTS9WRTlkQmtLZmFUbzc4QlQ2YVh5Sm1ua2pwZUZtQk9HczN1UDViVUFSajNPbm5yDQo3QW9zOWo0NXJncnl0cGVsWVRNbExpNmpWdEJ2NVJJWnVNb0oxNVcyNTJ0OGVJZ3NPcTU3YWQwQm9iZXl5NFR1DQpHaHZlUDBWM3ZVSnZJM2licUg1RTljV3pJMmY4VXRvaXJVTmYwSjN0Y25nOEpxU091dXpXRFlXclJEQXpRYkpZDQpxS3p2VkRjTitwdHFWN0daNkp1cUhoZHdnRGVxQk9zdmVEYnpBQXlZU1ZQSmpSV1llYThNeGxNN09YYnRBZ01CDQpBQUdqZ2dLQk1JSUNmVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUhBd0VHDQpDQ3NHQVFVRkJ3TUNNQXdHQTFVZEV3RUIvd1FDTUFBd0hRWURWUjBPQkJZRUZIRHdlYjZLcHJTdldydy92UjZrDQp3VFZwdWRQdE1COEdBMVVkSXdRWU1CYUFGS2hLYW1NRWZkMjY1dEU1dDZaRlplL3pxT3loTUc4R0NDc0dBUVVGDQpCd0VCQkdNd1lUQXVCZ2dyQmdFRkJRY3dBWVlpYUhSMGNEb3ZMMjlqYzNBdWFXNTBMWGd6TG14bGRITmxibU55DQplWEIwTG05eVp6QXZCZ2dyQmdFRkJRY3dBb1lqYUhSMGNEb3ZMMk5sY25RdWFXNTBMWGd6TG14bGRITmxibU55DQplWEIwTG05eVp5OHdOd1lEVlIwUkJEQXdMb0lUYjNCbGJuQnZiR2xqZVdGblpXNTBMbTl5WjRJWGQzZDNMbTl3DQpaVzV3YjJ4cFkzbGhaMlZ1ZEM1dmNtY3dUQVlEVlIwZ0JFVXdRekFJQmdabmdRd0JBZ0V3TndZTEt3WUJCQUdDDQozeE1CQVFFd0tEQW1CZ2dyQmdFRkJRY0NBUllhYUhSMGNEb3ZMMk53Y3k1c1pYUnpaVzVqY25sd2RDNXZjbWN3DQpnZ0VFQmdvckJnRUVBZFo1QWdRQ0JJSDFCSUh5QVBBQWRnQmVwM1A1MzFiQTU3VTJTSDNRU2VBeWVwR2FESVNoDQpFaEtFR0hXV2dYRkZXQUFBQVhNNXE5dkRBQUFFQXdCSE1FVUNJUUNSSHFncnRsMDdZNlRyeWZNbVFONlROS1JWDQptMUxUeTl2STNNaC9rcmJTUVFJZ1lnVkFLd1hSb1BSK0JOMXBjSmJKdjNBaXZiaDZFN0w5ODdyTVNFUWs1Vm9BDQpkZ0N5SGdYTWk2TE5paUJPaDJiNUs3bUtKU0JuYTlyNmNPZXlTVk10NzR1UVhnQUFBWE01cTl1dUFBQUVBd0JIDQpNRVVDSVFEZHJ1VHV0US9VY2hja3FZUSsycDltdXRuclNublFYYTh4TEE0MVlHelpIZ0lnWFhFVEZiR2ZuczJDDQo3WUo4Y0RvWVlBam1kek1nOGs3aEtYUUd1L0tzQWI0d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFHazlwNXl0DQpPYURJUFJQazVJbXBIMWY2ZjAxMG1VTFdQVjVQam42a3pNSFA5ejVuZE16KysxTk92SFY0R1ZCQ29ldUtxMWJwDQpGQ0QrSWdBOXBjSkFFWFEvdTRHcG1iQUtVWnptZk1JYjg5YVJnbkpwMG14OVk0QkJkNDVFeFVXczh3NGNmZ0ZaDQp5WlVlSHZXczFhbnBBY1IyRklacEFWTVFDYUlnak90MmRkUjF4djRhY0N3K21EL0I5b0tmR1pFVWd5SUFOdnBCDQpJRGFiZ2dMU3dGYTlPS0tYUkJWUkFhZm83T2FjMjFIUVU3RTNzWHBoYUhaR2ZuMkYyN2REL3FvcVVjTHFyNGxDDQpjN2xORTBZR3A2cithUG85VkxjSDJWMGxONHQrMVZiVkFyd0t6bnNOZGNRbndLQmV0Z3F2WnJnTGc0K3FqbzR5DQp1aXhKWTM4WFUvYjdiYVU9DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=" + + p := __local2__ if { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + data: {} + want_result: + - x: + - openpolicyagent.org diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0120.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0120.yaml new file mode 100644 index 000000000000..7b6a8d208624 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0120.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: cryptox509parsecertificates/PEM, single cert, string + query: data.generated.p = x + modules: + - | + package generated + + certs := "-----BEGIN CERTIFICATE-----\nMIIFdzCCBF+gAwIBAgISA3NriAEus/+cvflvhVQOW5zTMA0GCSqGSIb3DQEBCwUA\nMEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD\nExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0yMDA3MTAxNjAwMzBaFw0y\nMDEwMDgxNjAwMzBaMB4xHDAaBgNVBAMTE29wZW5wb2xpY3lhZ2VudC5vcmcwggEi\nMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCyy8HZXVTJ2TSHXYnoL+CKYpo4\nwz1wowUcdt/XBgN08f37NxaNk+VAj8GD2s6zhoHLShyYS2PVosf7xumvyG914PLp\nIHO9WmCaZMqwEyvMM/VE9dBkKfaTo78BT6aXyJmnkjpeFmBOGs3uP5bUARj3Onnr\n7Aos9j45rgrytpelYTMlLi6jVtBv5RIZuMoJ15W252t8eIgsOq57ad0Bobeyy4Tu\nGhveP0V3vUJvI3ibqH5E9cWzI2f8UtoirUNf0J3tcng8JqSOuuzWDYWrRDAzQbJY\nqKzvVDcN+ptqV7GZ6JuqHhdwgDeqBOsveDbzAAyYSVPJjRWYea8MxlM7OXbtAgMB\nAAGjggKBMIICfTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG\nCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFHDweb6KprSvWrw/vR6k\nwTVpudPtMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUF\nBwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNy\neXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNy\neXB0Lm9yZy8wNwYDVR0RBDAwLoITb3BlbnBvbGljeWFnZW50Lm9yZ4IXd3d3Lm9w\nZW5wb2xpY3lhZ2VudC5vcmcwTAYDVR0gBEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC\n3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcw\nggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdgBep3P531bA57U2SH3QSeAyepGaDISh\nEhKEGHWWgXFFWAAAAXM5q9vDAAAEAwBHMEUCIQCRHqgrtl07Y6TryfMmQN6TNKRV\nm1LTy9vI3Mh/krbSQQIgYgVAKwXRoPR+BN1pcJbJv3Aivbh6E7L987rMSEQk5VoA\ndgCyHgXMi6LNiiBOh2b5K7mKJSBna9r6cOeySVMt74uQXgAAAXM5q9uuAAAEAwBH\nMEUCIQDdruTutQ/UchckqYQ+2p9mutnrSnnQXa8xLA41YGzZHgIgXXETFbGfns2C\n7YJ8cDoYYAjmdzMg8k7hKXQGu/KsAb4wDQYJKoZIhvcNAQELBQADggEBAGk9p5yt\nOaDIPRPk5ImpH1f6f010mULWPV5Pjn6kzMHP9z5ndMz++1NOvHV4GVBCoeuKq1bp\nFCD+IgA9pcJAEXQ/u4GpmbAKUZzmfMIb89aRgnJp0mx9Y4BBd45ExUWs8w4cfgFZ\nyZUeHvWs1anpAcR2FIZpAVMQCaIgjOt2ddR1xv4acCw+mD/B9oKfGZEUgyIANvpB\nIDabggLSwFa9OKKXRBVRAafo7Oac21HQU7E3sXphaHZGfn2F27dD/qoqUcLqr4lC\nc7lNE0YGp6r+aPo9VLcH2V0lN4t+1VbVArwKznsNdcQnwKBetgqvZrgLg4+qjo4y\nuixJY38XU/b7baU=\n-----END CERTIFICATE-----" + + p := __local2__ if { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + data: {} + want_result: + - x: + - openpolicyagent.org diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0121.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0121.yaml new file mode 100644 index 000000000000..86feb423fd78 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0121.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: cryptox509parsecertificates/PEM, chain, b64 + query: data.generated.p = x + modules: + - | + package generated + + certs := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlGZHpDQ0JGK2dBd0lCQWdJU0EzTnJpQUV1cy8rY3ZmbHZoVlFPVzV6VE1BMEdDU3FHU0liM0RRRUJDd1VBTUVveEN6QUpCZ05WQkFZVEFsVlRNUll3RkFZRFZRUUtFdzFNWlhRbmN5QkZibU55ZVhCME1TTXdJUVlEVlFRREV4cE1aWFFuY3lCRmJtTnllWEIwSUVGMWRHaHZjbWwwZVNCWU16QWVGdzB5TURBM01UQXhOakF3TXpCYUZ3MHlNREV3TURneE5qQXdNekJhTUI0eEhEQWFCZ05WQkFNVEUyOXdaVzV3YjJ4cFkzbGhaMlZ1ZEM1dmNtY3dnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDeXk4SFpYVlRKMlRTSFhZbm9MK0NLWXBvNHd6MXdvd1VjZHQvWEJnTjA4ZjM3TnhhTmsrVkFqOEdEMnM2emhvSExTaHlZUzJQVm9zZjd4dW12eUc5MTRQTHBJSE85V21DYVpNcXdFeXZNTS9WRTlkQmtLZmFUbzc4QlQ2YVh5Sm1ua2pwZUZtQk9HczN1UDViVUFSajNPbm5yN0FvczlqNDVyZ3J5dHBlbFlUTWxMaTZqVnRCdjVSSVp1TW9KMTVXMjUydDhlSWdzT3E1N2FkMEJvYmV5eTRUdUdodmVQMFYzdlVKdkkzaWJxSDVFOWNXekkyZjhVdG9pclVOZjBKM3Rjbmc4SnFTT3V1eldEWVdyUkRBelFiSllxS3p2VkRjTitwdHFWN0daNkp1cUhoZHdnRGVxQk9zdmVEYnpBQXlZU1ZQSmpSV1llYThNeGxNN09YYnRBZ01CQUFHamdnS0JNSUlDZlRBT0JnTlZIUThCQWY4RUJBTUNCYUF3SFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01Bd0dBMVVkRXdFQi93UUNNQUF3SFFZRFZSME9CQllFRkhEd2ViNktwclN2V3J3L3ZSNmt3VFZwdWRQdE1COEdBMVVkSXdRWU1CYUFGS2hLYW1NRWZkMjY1dEU1dDZaRlplL3pxT3loTUc4R0NDc0dBUVVGQndFQkJHTXdZVEF1QmdnckJnRUZCUWN3QVlZaWFIUjBjRG92TDI5amMzQXVhVzUwTFhnekxteGxkSE5sYm1OeWVYQjBMbTl5WnpBdkJnZ3JCZ0VGQlFjd0FvWWphSFIwY0RvdkwyTmxjblF1YVc1MExYZ3pMbXhsZEhObGJtTnllWEIwTG05eVp5OHdOd1lEVlIwUkJEQXdMb0lUYjNCbGJuQnZiR2xqZVdGblpXNTBMbTl5WjRJWGQzZDNMbTl3Wlc1d2IyeHBZM2xoWjJWdWRDNXZjbWN3VEFZRFZSMGdCRVV3UXpBSUJnWm5nUXdCQWdFd053WUxLd1lCQkFHQzN4TUJBUUV3S0RBbUJnZ3JCZ0VGQlFjQ0FSWWFhSFIwY0RvdkwyTndjeTVzWlhSelpXNWpjbmx3ZEM1dmNtY3dnZ0VFQmdvckJnRUVBZFo1QWdRQ0JJSDFCSUh5QVBBQWRnQmVwM1A1MzFiQTU3VTJTSDNRU2VBeWVwR2FESVNoRWhLRUdIV1dnWEZGV0FBQUFYTTVxOXZEQUFBRUF3QkhNRVVDSVFDUkhxZ3J0bDA3WTZUcnlmTW1RTjZUTktSVm0xTFR5OXZJM01oL2tyYlNRUUlnWWdWQUt3WFJvUFIrQk4xcGNKYkp2M0FpdmJoNkU3TDk4N3JNU0VRazVWb0FkZ0N5SGdYTWk2TE5paUJPaDJiNUs3bUtKU0JuYTlyNmNPZXlTVk10NzR1UVhnQUFBWE01cTl1dUFBQUVBd0JITUVVQ0lRRGRydVR1dFEvVWNoY2txWVErMnA5bXV0bnJTbm5RWGE4eExBNDFZR3paSGdJZ1hYRVRGYkdmbnMyQzdZSjhjRG9ZWUFqbWR6TWc4azdoS1hRR3UvS3NBYjR3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQUdrOXA1eXRPYURJUFJQazVJbXBIMWY2ZjAxMG1VTFdQVjVQam42a3pNSFA5ejVuZE16KysxTk92SFY0R1ZCQ29ldUtxMWJwRkNEK0lnQTlwY0pBRVhRL3U0R3BtYkFLVVp6bWZNSWI4OWFSZ25KcDBteDlZNEJCZDQ1RXhVV3M4dzRjZmdGWnlaVWVIdldzMWFucEFjUjJGSVpwQVZNUUNhSWdqT3QyZGRSMXh2NGFjQ3crbUQvQjlvS2ZHWkVVZ3lJQU52cEJJRGFiZ2dMU3dGYTlPS0tYUkJWUkFhZm83T2FjMjFIUVU3RTNzWHBoYUhaR2ZuMkYyN2REL3FvcVVjTHFyNGxDYzdsTkUwWUdwNnIrYVBvOVZMY0gyVjBsTjR0KzFWYlZBcndLem5zTmRjUW53S0JldGdxdlpyZ0xnNCtxam80eXVpeEpZMzhYVS9iN2JhVT0NCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQ0KTUlJRWtqQ0NBM3FnQXdJQkFnSVFDZ0ZCUWdBQUFWT0ZjMm9MaGV5bkNEQU5CZ2txaGtpRzl3MEJBUXNGQURBL01TUXdJZ1lEVlFRS0V4dEVhV2RwZEdGc0lGTnBaMjVoZEhWeVpTQlVjblZ6ZENCRGJ5NHhGekFWQmdOVkJBTVREa1JUVkNCU2IyOTBJRU5CSUZnek1CNFhEVEUyTURNeE56RTJOREEwTmxvWERUSXhNRE14TnpFMk5EQTBObG93U2pFTE1Ba0dBMVVFQmhNQ1ZWTXhGakFVQmdOVkJBb1REVXhsZENkeklFVnVZM0o1Y0hReEl6QWhCZ05WQkFNVEdreGxkQ2R6SUVWdVkzSjVjSFFnUVhWMGFHOXlhWFI1SUZnek1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBbk5NTThGcmxMa2UzY2wwM2c3Tm9ZekRxMXpVbUdTWGh2YjQxOFhDU0w3ZTRTMEVGcTZtZU5RaFk3TEVxeEdpSEM2UGpkZVRtODZkaWNicDVnV0FmMTVHYW4vUFFlR2R4eUdrT2xaSFAvdWFaNldBOFNNeCt5azEzRWlTZFJ4dGE2N25zSGpjQUhKeXNlNmNGNnM1SzY3MUI1VGFZdWN2OWJUeVdhTjhqS2tLUURJWjBaOGgvcFpxNFVtRVVFejlsNllLSHk5djZEbGIyaG9uemhUK1hocSt3M0JydmF3MlZGbjNFSzZCbHNwa0VObldBYTZ4Szh4dVFTWGd2b3BaUEtpQWxLUVRHZE1EUU1jMlBNVGlWRnJxb003aEQ4YkVmd3pCL29ua3hFejB0TnZqai9QSXphcms1TWNXdnhJME5IV1FXTTZyNmhDbTIxQXZBMkgzRGt3SURBUUFCbzRJQmZUQ0NBWGt3RWdZRFZSMFRBUUgvQkFnd0JnRUIvd0lCQURBT0JnTlZIUThCQWY4RUJBTUNBWVl3ZndZSUt3WUJCUVVIQVFFRWN6QnhNRElHQ0NzR0FRVUZCekFCaGlab2RIUndPaTh2YVhOeVp5NTBjblZ6ZEdsa0xtOWpjM0F1YVdSbGJuUnlkWE4wTG1OdmJUQTdCZ2dyQmdFRkJRY3dBb1l2YUhSMGNEb3ZMMkZ3Y0hNdWFXUmxiblJ5ZFhOMExtTnZiUzl5YjI5MGN5OWtjM1J5YjI5MFkyRjRNeTV3TjJNd0h3WURWUjBqQkJnd0ZvQVV4S2V4cEhzc2NmcmI0VXVRZGYvRUZXQ0ZpUkF3VkFZRFZSMGdCRTB3U3pBSUJnWm5nUXdCQWdFd1B3WUxLd1lCQkFHQzN4TUJBUUV3TURBdUJnZ3JCZ0VGQlFjQ0FSWWlhSFIwY0RvdkwyTndjeTV5YjI5MExYZ3hMbXhsZEhObGJtTnllWEIwTG05eVp6QThCZ05WSFI4RU5UQXpNREdnTDZBdGhpdG9kSFJ3T2k4dlkzSnNMbWxrWlc1MGNuVnpkQzVqYjIwdlJGTlVVazlQVkVOQldETkRVa3d1WTNKc01CMEdBMVVkRGdRV0JCU29TbXBqQkgzZHV1YlJPYmVtUldYdjg2anNvVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBM1RQWEVmTmpXRGpkR0JYN0NWVytkbGE1Y0VpbGFVY25lOElrQ0pMeFdoOUtFaWszSkhSUkhHSm91TTJWY0dmbDk2UzhUaWhSelp2b3JvZWQ2dGk2V3FFQm10enczV29kYXRnK1Z5T2VwaDRFWXByLzF3WEt0eDgvd0FwSXZKU3d0bVZpNE1GVTVhTXFyU0RFNmVhNzNNajJ0Y015bzVqTWQ2am1lV1VISzhzby9qb1dVb0hPVWd3dVg0UG8xUVl6KzNkc3prRHFNcDRma2x4QndYUnNXMTBLWHpQTVRaK3NPUEF2ZXl4aW5kbWprVzhsR3krUXNSbEdQZlorRzZaNmg3bWplbTBZK2lXbGtZY1Y0UElXTDFpd0JpOHNhQ2JHUzVqTjJwOE0rWCtRN1VOS0VrUk9iM042S09xa3FtNTdUSDJIM2VESkFrU25oNi9ETkZ1MFFnPT0NCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQ0KTUlJRFNqQ0NBaktnQXdJQkFnSVFSSyt3Z05hako3cUpNRG1HTHZoQWF6QU5CZ2txaGtpRzl3MEJBUVVGQURBL01TUXdJZ1lEVlFRS0V4dEVhV2RwZEdGc0lGTnBaMjVoZEhWeVpTQlVjblZ6ZENCRGJ5NHhGekFWQmdOVkJBTVREa1JUVkNCU2IyOTBJRU5CSUZnek1CNFhEVEF3TURrek1ESXhNVEl4T1ZvWERUSXhNRGt6TURFME1ERXhOVm93UHpFa01DSUdBMVVFQ2hNYlJHbG5hWFJoYkNCVGFXZHVZWFIxY21VZ1ZISjFjM1FnUTI4dU1SY3dGUVlEVlFRREV3NUVVMVFnVW05dmRDQkRRU0JZTXpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTit2NlpkUUNJTlh0TXhpWmZhUWd1ekgweXhyTU1wYjdObkRmY2RBd1JnVWkrRG9NM1pKS3VNL0lVbVRyRTRPcno1SXkyWHUvTk1oRDJYU0t0a3lqNHpsOTNld0VudTFsY0NKbzZtNjdYTXVlZ3dHTW9PaWZvb1VNTTBSb09FcU9MbDVDakg5VUwyQVpkKzNVV09EeU9LSVllcExZWUhzVW11NW91SkxHaWlmU0tPZUROb0pqajRYTGg3ZElOOWJ4aXFLcXk2OWNLM0ZDeG9sa0hSeXhYdHFxelRXTUluLzVXZ1RlMVFMeU5hdTdGcWNraDQ5WkxPTXh0Ky95VUZ3N0JaeTFTYnNPRlU1UTlEOC9SaGNRUEdYNjlXYW00MGR1dG9sdWNiWTM4RVZBanFyMm03eFBpNzFYQWljUE5hRGFlUVFteGtxdGlsWDQrVTltNS93QWwwQ0F3RUFBYU5DTUVBd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBT0JnTlZIUThCQWY4RUJBTUNBUVl3SFFZRFZSME9CQllFRk1TbnNhUjdMSEg2MitGTGtIWC94QlZnaFlrUU1BMEdDU3FHU0liM0RRRUJCUVVBQTRJQkFRQ2pHaXliRndCY3FSN3VLR1kzT3IrRHh6OUx3d21nbFNCZDQ5bFpSTkkrRFQ2OWlrdWdkQi9PRUlLY2RCb2RmcGdhM2NzVFM3TWdST1NSNmN6OGZhWGJhdVgrNXYzZ1R0MjNBRHExY0Vtdjh1WHJBdkhSQW9zWnk1UTZYa2pFR0I1WUdWOGVBbHJ3RFBHeHJhbmNXWWFMYnVtUjlZYksrcmxtTTZwWlc4N2lweFp6UjhzcnpKbXdOMGpQNDFaTDljOFBESEl5aDhid1JMdFRjbTFEOVNaSW1sSm50MWlyL21kMmNYamJEYUpXRkJNNUpER0ZvcWdDV2pCSDRkMVFCN3dDQ1pBQTYyUmpZSnNXdklqSkV1YlNmWkdMK1QweWpXVzA2WHl4VjNicXhiWW9PYjhWWlJ6STluZVdhZ3FOZHd2WWtRc0VqZ2ZiS2JZSzdwMkNOVFVRDQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=" + + p := __local2__ if { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + data: {} + want_result: + - x: + - openpolicyagent.org + - Let's Encrypt Authority X3 + - DST Root CA X3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0122.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0122.yaml new file mode 100644 index 000000000000..f6d03c690c14 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0122.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: cryptox509parsecertificates/PEM, chain, string + query: data.generated.p = x + modules: + - | + package generated + + certs := "-----BEGIN CERTIFICATE-----\nMIIFdzCCBF+gAwIBAgISA3NriAEus/+cvflvhVQOW5zTMA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0yMDA3MTAxNjAwMzBaFw0yMDEwMDgxNjAwMzBaMB4xHDAaBgNVBAMTE29wZW5wb2xpY3lhZ2VudC5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCyy8HZXVTJ2TSHXYnoL+CKYpo4wz1wowUcdt/XBgN08f37NxaNk+VAj8GD2s6zhoHLShyYS2PVosf7xumvyG914PLpIHO9WmCaZMqwEyvMM/VE9dBkKfaTo78BT6aXyJmnkjpeFmBOGs3uP5bUARj3Onnr7Aos9j45rgrytpelYTMlLi6jVtBv5RIZuMoJ15W252t8eIgsOq57ad0Bobeyy4TuGhveP0V3vUJvI3ibqH5E9cWzI2f8UtoirUNf0J3tcng8JqSOuuzWDYWrRDAzQbJYqKzvVDcN+ptqV7GZ6JuqHhdwgDeqBOsveDbzAAyYSVPJjRWYea8MxlM7OXbtAgMBAAGjggKBMIICfTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFHDweb6KprSvWrw/vR6kwTVpudPtMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUFBwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNyeXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNyeXB0Lm9yZy8wNwYDVR0RBDAwLoITb3BlbnBvbGljeWFnZW50Lm9yZ4IXd3d3Lm9wZW5wb2xpY3lhZ2VudC5vcmcwTAYDVR0gBEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdgBep3P531bA57U2SH3QSeAyepGaDIShEhKEGHWWgXFFWAAAAXM5q9vDAAAEAwBHMEUCIQCRHqgrtl07Y6TryfMmQN6TNKRVm1LTy9vI3Mh/krbSQQIgYgVAKwXRoPR+BN1pcJbJv3Aivbh6E7L987rMSEQk5VoAdgCyHgXMi6LNiiBOh2b5K7mKJSBna9r6cOeySVMt74uQXgAAAXM5q9uuAAAEAwBHMEUCIQDdruTutQ/UchckqYQ+2p9mutnrSnnQXa8xLA41YGzZHgIgXXETFbGfns2C7YJ8cDoYYAjmdzMg8k7hKXQGu/KsAb4wDQYJKoZIhvcNAQELBQADggEBAGk9p5ytOaDIPRPk5ImpH1f6f010mULWPV5Pjn6kzMHP9z5ndMz++1NOvHV4GVBCoeuKq1bpFCD+IgA9pcJAEXQ/u4GpmbAKUZzmfMIb89aRgnJp0mx9Y4BBd45ExUWs8w4cfgFZyZUeHvWs1anpAcR2FIZpAVMQCaIgjOt2ddR1xv4acCw+mD/B9oKfGZEUgyIANvpBIDabggLSwFa9OKKXRBVRAafo7Oac21HQU7E3sXphaHZGfn2F27dD/qoqUcLqr4lCc7lNE0YGp6r+aPo9VLcH2V0lN4t+1VbVArwKznsNdcQnwKBetgqvZrgLg4+qjo4yuixJY38XU/b7baU=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0NlowSjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMTGkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EFq6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWAa6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIGCCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNvbTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9kc3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAwVAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcCARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwuY3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsFAAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJouM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwuX4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlGPfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n-----END CERTIFICATE-----" + + p := __local2__ if { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + data: {} + want_result: + - x: + - openpolicyagent.org + - Let's Encrypt Authority X3 + - DST Root CA X3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0123.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0123.yaml new file mode 100644 index 000000000000..87a7435231bc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0123.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: cryptox509parsecertificates/invalid DER or PEM data, b64 + query: data.generated.p = x + modules: + - | + package generated + + certs := "YmFkc3RyaW5n" + + p := __local2__ if { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + want_error_code: eval_builtin_error + want_error: "x509: malformed certificate" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0124.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0124.yaml new file mode 100644 index 000000000000..74741d0f8005 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-0124.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: cryptox509parsecertificates/invalid DER or PEM data, string + query: data.generated.p = x + modules: + - | + package generated + + certs := "foobar" + + p := __local2__ if { + __local4__ = data.generated.certs + crypto.x509.parse_certificates(__local4__, __local3__) + __local0__ = __local3__ + __local2__ = [__local1__ | __local1__ = __local0__[_].Subject.CommonName] + } + want_error_code: eval_builtin_error + want_error: illegal base64 + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-raw-uris.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-raw-uris.yaml new file mode 100644 index 000000000000..33a1666690e4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsecertificates/test-cryptox509parsecertificates-raw-uris.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: cryptox509parsecertificates/uri_strings + query: data.generated.uri_strings = x + modules: + - | + package generated + + certs := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUIxekNDQVh5Z0F3SUJBZ0lJZGxpT1dVY1NXM3N3Q2dZSUtvWkl6ajBFQXdJd1BURUxNQWtHQTFVRUJoTUMKUjBJeEVEQU9CZ05WQkFvVEIwVjRZVzF3YkdVeEhEQWFCZ05WQkFVVEV6RTFPREV4TnpnNU56UTJPRFkxTmpneQpOalF3SUJjTk1qTXhNVEl3TVRZMU5USTRXaGdQTWpFeU1qRXdNamN4TmpVMU1qaGFNRDB4Q3pBSkJnTlZCQVlUCkFrZENNUkF3RGdZRFZRUUtFd2RGZUdGdGNHeGxNUnd3R2dZRFZRUUZFeE00TlRJM056SXlOREE0TlRJeE5qVXoKTVRFMU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRXp0UDNrQnNpQXY4UUF5eWxUalJZSFlWegpjWTB5YmpBdC9VbWpZb3Fxb0o4SEtIdXF1ckRaUmVwa05qUXdwV3pmZndZZ0xaNk42SisyVUlPdlZ0TDZEcU5rCk1HSXdEZ1lEVlIwUEFRSC9CQVFEQWdlQU1CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUNCZ2dyQmdFRkJRY0QKQVRBTUJnTlZIUk1CQWY4RUFqQUFNQ01HQTFVZEVRUWNNQnFHR0hOd2FXWm1aVG92TDJWNFlXMXdiR1V1WTI5dApMMjl3WVRBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQXlRNDhPd25lTHkzMjZqYitEUjd5RjJhcS94Wnl1cW9qCitUU3ZLVVB5NEU0Q0lRQ0VMUlp3K0dWTjhJR0drVGV4MGxxTDNxY21mWldJbm15VitrbnQ0d3p3L3c9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==" + + uri_strings := crypto.x509.parse_certificates(certs)[0].URIStrings + want_result: + - x: + - spiffe://example.com/opa + - note: cryptox509parsecertificates/uri_strings_no_uris + query: data.generated.uri_strings = x + modules: + - | + package generated + + certs := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNTakNDQWJPZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRMEZBREJDTVFzd0NRWURWUVFHRXdKMWN6RUwKTUFrR0ExVUVDQXdDUTBFeEVEQU9CZ05WQkFvTUIwVjRZVzF3YkdVeEZEQVNCZ05WQkFNTUMyVjRZVzF3YkdVdQpZMjl0TUI0WERUSXpNVEV5T1RFMk5ESXdPRm9YRFRJME1URXlPREUyTkRJd09Gb3dRakVMTUFrR0ExVUVCaE1DCmRYTXhDekFKQmdOVkJBZ01Ba05CTVJBd0RnWURWUVFLREFkRmVHRnRjR3hsTVJRd0VnWURWUVFEREF0bGVHRnQKY0d4bExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBempOT1puY05DL25MdEZQYwpUNnNlSzZ0ditTbU9GaGk3NVBKRm9sQ0dFZUFiTHJHTzhaUmR2cXY2OStTTk41MUhrNFBJUDUrejk4aHZIdWJQClVtcGdOMVdVM3FKK2tOVTJXL3poR3pLMTdIL2c3YjVJTjRHZmx3bXJlRWZscnRicnZKSGRlRkVhVGtmYnVld0YKVmZvLzRiaG0yYUthczNHcUo3RnlBNDVxeVUwQ0F3RUFBYU5RTUU0d0hRWURWUjBPQkJZRUZOUStDMUVOK0dSYwpGS1dyTll6ZWdnbFc3TE9lTUI4R0ExVWRJd1FZTUJhQUZOUStDMUVOK0dSY0ZLV3JOWXplZ2dsVzdMT2VNQXdHCkExVWRFd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVOQlFBRGdZRUF1cXdVVWVXbTFJaURRbmphK1hqd1VCaXUKQXBYWEx5NlFZRG9jUkhoRHlJS3BKSWRJOXltNi9RcVhkOFQ2WGlYUTJDbFNJbGxBSTByQWJYNUZvYzNxaWRkUAp0d2huT1pxd3NZdFhUNy9XOHFLSm5FVnhZckJuRmZSdk9SU3ZlOEtwSDErVHQ2elZEYlJ5bENacTR1TTNrZXN2CmJabXlVdlM1bldBVm44ZmhFdUU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=" + + uri_strings := crypto.x509.parse_certificates(certs)[0].URIStrings + want_result: + - x: null diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsekeypair/test-cryptox509parsekeypairs-0118.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsekeypair/test-cryptox509parsekeypairs-0118.yaml new file mode 100644 index 000000000000..6fca33c7761d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsekeypair/test-cryptox509parsekeypairs-0118.yaml @@ -0,0 +1,94 @@ +--- +cases: + - note: cryptoX509ParseKeyPairs/PEM_encoded_string_cert_and_key + query: data.test.p = x + modules: + - | + package test + + p := crypto.x509.parse_keypair(input.cert, input.key).Certificate[0] + input: + cert: | + -----BEGIN CERTIFICATE----- + MIIEszCCApsCFDPRm4sTNZqiH601E6E6pEaJaCKqMA0GCSqGSIb3DQEBCwUAMBYx + FDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIzMDUxMjEyMDIxNFoXDTIzMDYxMTEy + MDIxNFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUA + A4ICDwAwggIKAoICAQDTcNASD17ohP3V14LMUIkFwAeeeniSXy4Pl3EqIgVpt9ul + 2IZiTm4JOYSgQ681bjt1OdwCkBf2Cg62DUyHMMwwnbaznN/Y5piYlaS33XCvcNpM + OWxwA4Z5Q0jNAshBExp8EzjEojbIUoeSncAP9jtOO/NArhLq+XodmONLqBG8KmZc + 2uiAeJ7ZjTpOckASYPYy7tUc4Ha3XUV0SoF/+0SJrXYOtpyMuMxwks6jMbpacmZn + jFIe8m2xH97s5inmHkzjBVKbpBQQHROP6A61VOrH9FrJRiACZ440zFJ1CGlXZau4 + oJgs9S1YRDI4W2Ha1WIYLpUBEMYXVYyn4swTBj42HqpSmfppOoR2g1d/AjxpFVAk + PLrict/nszhTIkEUE39vJh4HuytT49ss/cp4KokxBjLz7LmTUEzxJS8dB9714KzO + hFRHnpap0onU7Yb+Yz8/21bZL4AEL1d2Jg48jRET1Mrl9GOnWjzo7JxioUsbLcOo + y8Qcu2L87BQZqHCVpq6TcJKfPgh9t7y1AHvAfoXJdGSwimCZXuR4VEA3NJAHgPOw + LypB+dukzseWLdN+e7YOYqyWfjJg4aQmCx+nr866/QlQ4mrEBTkBsDOO5tEsKugI + redu+lL6z9QFdmeQpoC+s6JKnV29KHZXNDBZ1gnXOyA6lIBjjFjZqnkwT1ToHwID + AQABMA0GCSqGSIb3DQEBCwUAA4ICAQC8v5BIEooDSe5Kpzwsh9QLK7Ip33v4VHo+ + q82DRjAXiosFoaJ1dg+hwXOUo5VPSOqWCPNYRHDabuX4oiZAGPA9O5lTKQ/PQS4y + YIsD9XgeuDllBo3Y/uBwjoFFAD169fYMmZgGQ/PCdFS9nrYduPAIzNnsip+Z+XRK + qLwm83H/nKKgVvbGprNcivG3c8H42CKQ7aqDkEGuSG9EVRnQOMaI7ILMx/oj33Mk + uJ/21z4AHj7ads3dUF4syEBo5gWCAiBd+g1E/BkFnn+G+oOYo7c12dB+r6rbgrA0 + Bk+bW0NvU2ApP/LRRGaJD4wW2UoAklL618+CRWNtXdT+WeKIPlrEdC/0yzU5t6sA + II+PfvbeWQfL5CssbWhkTbJFt3JKTrIMc9cMjNcqeS+bAELuI16d4CZ5TlmZZeMd + 1WCv6R3YifkR9IogZhix20nPCF1mfo7Q0XAywkx8pPAC4n6uYStU9YobNAGlWXz+ + YbbJDf9r9au88Gu6xFf3eETKu2tJsQgVYBmXic85+FoV45qBI5YZvqiX9XZtWXw2 + 391fW+NdK01Jc0tqtADubaF6D4ncnriufqcz+70O0p7CzPbTMtUsPFxcfqZ6x1KX + gP1TgZM++PuYjHEyCFhHQBz9cQlxPdW4alaDYBnnKvLZXDOAXCWVWS6SXa5tkU3n + k9i0HC1bhg== + -----END CERTIFICATE----- + key: | + -----BEGIN PRIVATE KEY----- + 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 + Aj0c+XArCS87P4iflF9maJ3Tk/NfaA== + -----END PRIVATE KEY----- + want_result: + - x: MIIEszCCApsCFDPRm4sTNZqiH601E6E6pEaJaCKqMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIzMDUxMjEyMDIxNFoXDTIzMDYxMTEyMDIxNFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDTcNASD17ohP3V14LMUIkFwAeeeniSXy4Pl3EqIgVpt9ul2IZiTm4JOYSgQ681bjt1OdwCkBf2Cg62DUyHMMwwnbaznN/Y5piYlaS33XCvcNpMOWxwA4Z5Q0jNAshBExp8EzjEojbIUoeSncAP9jtOO/NArhLq+XodmONLqBG8KmZc2uiAeJ7ZjTpOckASYPYy7tUc4Ha3XUV0SoF/+0SJrXYOtpyMuMxwks6jMbpacmZnjFIe8m2xH97s5inmHkzjBVKbpBQQHROP6A61VOrH9FrJRiACZ440zFJ1CGlXZau4oJgs9S1YRDI4W2Ha1WIYLpUBEMYXVYyn4swTBj42HqpSmfppOoR2g1d/AjxpFVAkPLrict/nszhTIkEUE39vJh4HuytT49ss/cp4KokxBjLz7LmTUEzxJS8dB9714KzOhFRHnpap0onU7Yb+Yz8/21bZL4AEL1d2Jg48jRET1Mrl9GOnWjzo7JxioUsbLcOoy8Qcu2L87BQZqHCVpq6TcJKfPgh9t7y1AHvAfoXJdGSwimCZXuR4VEA3NJAHgPOwLypB+dukzseWLdN+e7YOYqyWfjJg4aQmCx+nr866/QlQ4mrEBTkBsDOO5tEsKugIredu+lL6z9QFdmeQpoC+s6JKnV29KHZXNDBZ1gnXOyA6lIBjjFjZqnkwT1ToHwIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQC8v5BIEooDSe5Kpzwsh9QLK7Ip33v4VHo+q82DRjAXiosFoaJ1dg+hwXOUo5VPSOqWCPNYRHDabuX4oiZAGPA9O5lTKQ/PQS4yYIsD9XgeuDllBo3Y/uBwjoFFAD169fYMmZgGQ/PCdFS9nrYduPAIzNnsip+Z+XRKqLwm83H/nKKgVvbGprNcivG3c8H42CKQ7aqDkEGuSG9EVRnQOMaI7ILMx/oj33MkuJ/21z4AHj7ads3dUF4syEBo5gWCAiBd+g1E/BkFnn+G+oOYo7c12dB+r6rbgrA0Bk+bW0NvU2ApP/LRRGaJD4wW2UoAklL618+CRWNtXdT+WeKIPlrEdC/0yzU5t6sAII+PfvbeWQfL5CssbWhkTbJFt3JKTrIMc9cMjNcqeS+bAELuI16d4CZ5TlmZZeMd1WCv6R3YifkR9IogZhix20nPCF1mfo7Q0XAywkx8pPAC4n6uYStU9YobNAGlWXz+YbbJDf9r9au88Gu6xFf3eETKu2tJsQgVYBmXic85+FoV45qBI5YZvqiX9XZtWXw2391fW+NdK01Jc0tqtADubaF6D4ncnriufqcz+70O0p7CzPbTMtUsPFxcfqZ6x1KXgP1TgZM++PuYjHEyCFhHQBz9cQlxPdW4alaDYBnnKvLZXDOAXCWVWS6SXa5tkU3nk9i0HC1bhg== diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsekeypair/test-cryptox509parsekeypairs-0119.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsekeypair/test-cryptox509parsekeypairs-0119.yaml new file mode 100644 index 000000000000..b8cda9631a48 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsekeypair/test-cryptox509parsekeypairs-0119.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: cryptoX509ParseKeyPairs/base64_encoded_string_cert_and_key + query: data.test.p = x + modules: + - | + package test + + p := crypto.x509.parse_keypair(input.cert, input.key).Certificate[0] + input: + cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUY3ekNDQTllZ0F3SUJBZ0lVZFJIQStCMC9aZ2tuS1BsQjJmc3dFOThsekFjd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZWXhDekFKQmdOVkJBWVRBbGhZTVJJd0VBWURWUVFJREFsVGRHRjBaVTVoYldVeEVUQVBCZ05WQkFjTQpDRU5wZEhsT1lXMWxNUlF3RWdZRFZRUUtEQXREYjIxd1lXNTVUbUZ0WlRFYk1Ca0dBMVVFQ3d3U1EyOXRjR0Z1CmVWTmxZM1JwYjI1T1lXMWxNUjB3R3dZRFZRUUREQlJEYjIxdGIyNU9ZVzFsVDNKSWIzTjBibUZ0WlRBZUZ3MHkKTXpBMU1UQXhNalV4TWpoYUZ3MHpNekExTURjeE1qVXhNamhhTUlHR01Rc3dDUVlEVlFRR0V3SllXREVTTUJBRwpBMVVFQ0F3SlUzUmhkR1ZPWVcxbE1SRXdEd1lEVlFRSERBaERhWFI1VG1GdFpURVVNQklHQTFVRUNnd0xRMjl0CmNHRnVlVTVoYldVeEd6QVpCZ05WQkFzTUVrTnZiWEJoYm5sVFpXTjBhVzl1VG1GdFpURWRNQnNHQTFVRUF3d1UKUTI5dGJXOXVUbUZ0WlU5eVNHOXpkRzVoYldVd2dnSWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUNEd0F3Z2dJSwpBb0lDQVFDM045anZpY21LcEdkMVAzcFhlQStNb2lkZE5qSjV2TTEzS3FhVGMxMjltY21ISHRJcmpHbmhvTHJOClNMV2NMR0tOQmJzQk5pNWxvcTQxc0pvZ3ltTnhVc3hRUUVyWG41RGlFbkVRZjFCcStuU0d6SWRHbWJtSlZXcUgKdDR0U0lIZENJRWF4SERIV2tJb2tsUjR0OENnTkl2aEtxbEdHSmx2TGZQVlpnV0hqczlIQSs2K0czNXRwL1pnQQp2U0EvTnV0azVQOUFMT2pseXNpeVNpWDZtYVJQayt5ck5mcHF6QTljbEllVlFRQ1RTR0hSUGVPeGcxU1NxcEhoCk11a3VZMDFKSHdvdEJVUWxOS3VEV0J3ejEzRk91YVBjNzRnMHBzTlFXZUNJaTdJWjZjUFpXSmZiQ2tSSEIwZUYKcGFPR2NueUZGYWZBZk5ZWjF3Wjg3SDZVM1U0endyMElMeFl6NkRuOEI0Zklra29ZMTlQSEVtbjR0ckhtNXdiUgpuSlZQaWVmZkFaenFLL3pjYWlCRlZEZEdRQy9UeTNLNXo5K1ZGYkl6UFdJUjVTNndlV3RiQThoM2FhL0UvdEhTCkNZV3ZpKzRSd3FQeGZXUTZKZXZ4ZFdDMDhSZ3kxSVB6N3Zxa3ZEMGJnN1JmMDF2SVVvaE8zdmJvSDdKejJLMG8KdkVHaVJkY1BRZkJ4eS9SNk80ZXN0UHBUTEU5Q1NTa2NUQVFjaWZDcG4zT1U4L3Z1UkxERXk5QXQrbWFkK0c1VgpLaVNubzhENXlnbGpPSEVRSUNaTzBjRU5GNXVWY3JOTmhRWVMwSmQ3RjllVkNTTzhIVUNzbE1xRjJlZG5GQ3BTCmZyWUlRa3NSVGdlajRiWFV4bFRGM3ZmakVabjVpZFZzSWdqMnRWUW9OMGRybjhNQVNRSURBUUFCbzFNd1VUQWQKQmdOVkhRNEVGZ1FVbERYeVAySHIrVlFNMWFIbHpjTjliRGxNYkpzd0h3WURWUjBqQkJnd0ZvQVVsRFh5UDJIcgorVlFNMWFIbHpjTjliRGxNYkpzd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRc0ZBQU9DCkFnRUFDekdyZlhWWWhmQ210RlpOMFk2TktRN2cxSFZHSVg1VjNBbDRVK29vM3krOW40S1ZHa2dWOWpoL2RaOGsKbXpXbXBYT0dSZkQxdWZSY0Q5cW9YeStHSFJ3LzBibTJ3a1ZjTXZFc25NeCtEa3VoaVZPSWMwUFlETmo4N3VLMwp2TkZCTTM5dW8xZmlKOXlkOVNLUXNqQjk0bFZNYUYxMDg5d2V2UFlsNXFtYm9rdTJxdXJYM3V3NVZ2eXNIQUF3ClUyeTB4OHkvYzNqdzlNZksxWDBHWWhTY1owcFYzeTN4dGxxckhuTmpTUmFaM3BmUGw3NGFjMGJndEU5cThKdDUKbzNEMGdmWmkwOFJrdW5ua2dKMS9QZDVpYUgvWEdnVG56NXdTRUF6VFJCWEhLWVlvRnBGV0wwcnowa0pvamZDdwpSdXQ2T21uL0w2blFXL1I1endzcXZrQUR1by9LWFkyTjY5L1J4UHcwejhFcTZZTVR0OHBXOGU1ZHpSRlFUS2N2CktUdDJCNTB1VFVEUlBRbW92dkYzRmRqcllaYkhyRlhHUXIyaGV6bDU2QkRSSkJaTzhIQ0g0NEIxc3d6K3QySWYKb3V2MlBIZDA1V3ZDVHIvM0dFL2ZiVGpabFVWeDhaYTJUWXdWakhTUzl3OWtRQ1V2dzNXM25OdVF6TklHVGJzYQpEdlQzeVN0MjJkRDQwTVlrNHpOc3o4d1YwUVYwdmJ5enorMUZUSXJlVldJSXhpMHVveWdNaHVGTDdJem4xeHNBClQ4TWVsbHQzN20wM2V2QkxwUXdPWHF0N1JXWlNaMllydVQ2clpkZ2RBOXRlK2Y3VUxhYUNGRDJxYzloRFRLOGoKemh3Mk41cGNIMVpYZGZBRjNNUWo1VmErSElDRElaL3BtTmNkVlJnWWxWN0hkOWc9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRZ0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1N3d2dna29BZ0VBQW9JQ0FRQzNOOWp2aWNtS3BHZDEKUDNwWGVBK01vaWRkTmpKNXZNMTNLcWFUYzEyOW1jbUhIdElyakduaG9Mck5TTFdjTEdLTkJic0JOaTVsb3E0MQpzSm9neW1OeFVzeFFRRXJYbjVEaUVuRVFmMUJxK25TR3pJZEdtYm1KVldxSHQ0dFNJSGRDSUVheEhESFdrSW9rCmxSNHQ4Q2dOSXZoS3FsR0dKbHZMZlBWWmdXSGpzOUhBKzYrRzM1dHAvWmdBdlNBL051dGs1UDlBTE9qbHlzaXkKU2lYNm1hUlBrK3lyTmZwcXpBOWNsSWVWUVFDVFNHSFJQZU94ZzFTU3FwSGhNdWt1WTAxSkh3b3RCVVFsTkt1RApXQnd6MTNGT3VhUGM3NGcwcHNOUVdlQ0lpN0laNmNQWldKZmJDa1JIQjBlRnBhT0djbnlGRmFmQWZOWVoxd1o4CjdINlUzVTR6d3IwSUx4WXo2RG44QjRmSWtrb1kxOVBIRW1uNHRySG01d2JSbkpWUGllZmZBWnpxSy96Y2FpQkYKVkRkR1FDL1R5M0s1ejkrVkZiSXpQV0lSNVM2d2VXdGJBOGgzYWEvRS90SFNDWVd2aSs0UndxUHhmV1E2SmV2eApkV0MwOFJneTFJUHo3dnFrdkQwYmc3UmYwMXZJVW9oTzN2Ym9IN0p6Mkswb3ZFR2lSZGNQUWZCeHkvUjZPNGVzCnRQcFRMRTlDU1NrY1RBUWNpZkNwbjNPVTgvdnVSTERFeTlBdCttYWQrRzVWS2lTbm84RDV5Z2xqT0hFUUlDWk8KMGNFTkY1dVZjck5OaFFZUzBKZDdGOWVWQ1NPOEhVQ3NsTXFGMmVkbkZDcFNmcllJUWtzUlRnZWo0YlhVeGxURgozdmZqRVpuNWlkVnNJZ2oydFZRb04wZHJuOE1BU1FJREFRQUJBb0lDQUJKaUttUm9neDRqM2xTYm5Lc3Jtd1hOCm5GMEVLOTdlcEpBTktiOFlQNExmYkNMZ1l3Nm5EVldzQXFwSDNoOFFMZ2cvMTdKb3JSR2FGOWcvd3N0QSsyYmEKdTdEZXJwUEJpVEJCMFBIcWtGZFhqM3NhQ1FXNnRXelQ4dmN3b2F4SklTWXpwbHd0ZTh1dlg0a0pwRWhRTlRpUwpObThKZFZvY1BiQW1kdGkyL0dzME52cmgxZ3dXb2htcHJnZCs4bjRkUk5Pd0RYTnpQaUFXYjNwQ0tkcmg4U1JoCjc0aURSei9SZjBZWENoNmQ4ZENWWGVrNGlFRGVzRXp5QythWWJPQ3dhb2dJY3dVdTV0WkQyV1M1b2NUSzNHM2QKZnhWVFBHdXFBdVZzU3pUd0xWdmZ3bnlyb0xzRDVmTnBoZEhoVzQzSkxYak9BakcwWk9nZFZPT1NlQ1g0S1prbwptcWc4b3Y1TksxclVRVGQwNng2bmg0elBXdEdaTDIwbE9EMTZvRWgzUlZoRFU5akQ1U25aZ2M1Mzg2bVkxeTVJClV0QTBUUnh3MzNIcjRWT1ExaXVCZzlrNTU2MzFIcGl0clBWZ1QvLzVzYzdjMjhpQ2lBRnVFZGg3SDF5cG5NNkgKc1pjL2prRTJBOFF1NnpKSTV2NzRoYkFaTmxONVMrcDU4ajhrV2pPYTd4bnZwYk5saXc1a2JDK2Ezb25ZSVNHZApTNVZITzVEVkVaOEY3aUtUTS8zTEFZdDZScG0vdEZJOUhFZVRGWHVCVEVsYTlsb0srUWdXbjF1QVVMeGtKUHFyCkFXZ0tzL0FYYWpxVyt0d3ZUM2M5cUFDaGFTakhjV3lxem05WGh6RDRwNTg2cnpDZ3M5VUNSSStudlEvOGFIMWoKNm9CdkRqbUxpem9WUEVkR2k1VkJBb0lCQVFEM2pRZThEdVIxVjYveS9HcXRNRVcyVXptdTl2d1R4VXRBSFZtawpob25PbFo0MDVEL1B3b0s1dXBORjV2QWtxNDN1b1M2OXVuQWxxbGpraVNmOTEzU3pYZEE4dXRRYlQzVFczUmxWCkZ2dXZlN3YvcWtRZFdScm5rdXJhcWtFbnhUMnlKOGdlaG1Qcmo1STMwVHVRVXpSdmFRc2hyYkIwV3NCcGcrRmIKdWJnV29wU21hVTY5aEJpSGxwWFYrcjJRUXIrVllHT3JLUkNSWDRhMGxtbEJxbDdnemVZSTA2d2RpamszelF5cgorcVltYjRkdEFqd3huQ09CRG9qbVJBM2hGTmpVbC8wNlVvMmZzUEM1cjduWlA2cURUMUU2MU5tVDhPZ3kyUWE1Cm90Uk4wS1E3bThGRXAwUDA4aHByOTRxVlZLaHB2NzMzcGMwQkhCQXZ6VG9RdTdKcEFvSUJBUUM5ZUxZZzVSS3cKMDZQSEVmRlhMTnZ1NU12Q2I0T2NHY1BHUElLaHEyWExTbFJSSElVSkdueFB2RVRrcm93YXhDVGVVc2g0WUFtUApZUjZxKzE2aHRwN1JBZ1RWNkxvZUpnQ2hDNVRvTWlkZkR2MmxEUnZneVpMWWtsU0VIcEpaSXRWWTI4NzFWTkRaClhFQWwwOGFGcHlENDNseXdNc3UxaHYzSnRMVGJVTDNZdGtBZnBQVFJBZ0ZaaFlxbVJUdTJKeXN5dXVoWWFVbS8KeEw3WDcvYmlJZ2FPemR1YkF0QXBFTUlEbnFOdVdKOUVBQit4a1c5VUIyTFpRZjBuOTh2bG40aUowaDhsb1V5RAphWElPbWpDZFJXeGFzeVRRQkNEZUR0eXgxbHNvb1R1U2JWU1FFSHVUeUgzVno2UHZBZC9xNGxwRW5hY0UvMjE5CjRXUEh6NnArcDJMaEFvSUJBQ28yQXhhZkYzZW16eHJJemN2Z1NsTFBtQ3RzZEFsUEFBamJ1RmhrbElVRVlDaTIKcnViWFRRRXNma1pTSGFxekVnMlpzR1dycjhuTVpVSDYzVFhja2txdmVYMlJnZTl5T2dNVlNtZUc5cjJ5aEprUQp5SEtVcWhESXJZRkJ2TUJ5VXBYWlVMZGJ4UmY2c0QwU1VXekhzMDQ0QkN6bStBcXZHdFlqSmI5RlNNMmJSV3VtCjAwVmZpK3M2MHl2Y2lJeGJ4VjFNUlZKL094TCt6ZkpuSDJXU0RvR1l1bHZROUMxSlQzNWpXWUROeVowT01YSjIKQ2h1UGUwSmJYeDZjaGgxV042N3doNzUxS3k4S3RkR0QxRlhtRkVZMXRTMHA5RHZVdlZOR1RHNUZCSnlNTWlUego1eDIwdzlLMW9hbTlXUVVqbldBQzBQcTBhK04vakljS0lKZVAyZGtDZ2dFQVhPZzhKcFV0UFJnS1R5czFOSklDCnBubjZrRFV1Uy9VMlVwYUpWODA3OVJ0VmpSQjNDNmU1SFVBc2FCWlBEVER4QXpPRXFjSXQ3ZWlwcVIzcG9WSnoKUGZuSGRUelJSc2RMdDZ4K0wvMm40S3p4STJYeUxaK3FLaGhXNlJJMG9SQzduUDdyMU5EcU9DdE1LVUJYTUdKcgpnSjFJeGYyaWRqamphV3o2NGpBTlo1NjJnczNZWGtTbGRNaE8zSWxHWm1OK2d6bXpoT2JjQ3ZUbXYrd2pHMitqCjE1S0tCTkMwVWU2dHRDaXQ2d1g1MHRaY3RDMmtjWWZOcU1yNjRBWmFMUmExVlI5N3RuQUpuTWF2N3drY25ZSFYKU0FSZ0lNQmxmWDI4S2xmNkMwcEVjK0M0Zm93V2pMamJPMlM5OWd6dFI3Z0dtMjdTMzFpQTBDRWRWSFU0SFRMbgpBUUtDQVFFQXNzajBmRThqa0ZpdnpRMmxTZTBEeGhwUXFHZVF1UGROYkJKVnNBcVdmRXRKQ0h2R0JYaTZBeEIrClFnN015RE14dnVPYXQ1TVNUQ01GYzNYdVFUdEtxdlF3SGxXMXZpTnJxckNzRHVRNTVDa1ZQTUxmazk4VlFoeHkKMnpUd20zOUd5a1ZpLy9CaU5LQzRCU2ZtZlN6cGdiYlJnSEI2Ny9zcGIwejBMbXUrWG5uL0ROemxxS1pvQUZFQgpwcDdzNkVmWGdJN1Npbm94dGxuRHM4eDdFM2dlcEtENVVWbnQzM3FZbUhCOVZ3WHpUZDBaSkhrY0x1b1VpanJjCko2cENYVTcrRlpPd3pJQi9IcFFPZ2pwdklqQ0pJaHRGeFZ5Z2JqdXVjRThRS2Naaml5VXNGTHZkQzVkZTFNZVQKMXJKalFFc2laeEgrUVBSODh0dUJ5VVZHMDAwbHBBPT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo= + want_result: + - x: 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 diff --git a/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsersaprivatekey/test-cryptox509parsersaprivatekey-1.yaml b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsersaprivatekey/test-cryptox509parsersaprivatekey-1.yaml new file mode 100644 index 000000000000..4d6fda45025e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/cryptox509parsersaprivatekey/test-cryptox509parsersaprivatekey-1.yaml @@ -0,0 +1,32 @@ +--- +cases: + - note: cryptox509parsersaprivatekey/valid + query: data.testing.p = x + modules: + - | + package testing + + pem := "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA9D/bK4171aiTNUkrUCHKGMLSQooV+o3wdz2889h9iv0HhhBJ\nCAGU54K3duB8ofHpmYL50QodcR4RLw1vSkaI+FPdPDMyKxKj/YcmofJjz4kW+Iqw\nFbBcbMnKnEVzye+CyW9YYOTu0xWtcgen80zGp2opG0GZX86hBjjXJnjOdrJTk6x2\nNAiJIbjsQevysmj+2MyqVm8widxw0x+rGhTaCD+ZXWitN0a0WO1aaA8c/7i99I9z\nhe2peKvXzEtMaqYO9ptHcYmq2z0QWvZuJVMv5Yn0mScLWyh91R099IOtn6sNaMMs\nOyTpi7E/2IlVgI2uKGPEopKkMFV8Fl2YaAbo7wIDAQABAoIBAAyMZ08ygqU0dvOq\n4a3JPp/NCo5el8h6mFsX8eg5PCHy4/sQRSBDLIpEXfaei+iqDA1V/E2wDlksaUeY\nkhony4uui1Q3cSFjYMd6tRJm6JfV/DcisO88U1NHfsBOlSdPxdFhhhHcUSTJHVMZ\nb5iBXkdlnd0HnsCcVguCyhLw6/KPFyiA+NYRz68flxze7admyVp5C6i/HbMPq8Pr\nMilBUvOFtxuaGeJBAiavuzUe9I70dRwpe424tMvisSA8h7Xbm8BeN/PJHDV/2JrI\nURgQ563yQ5So/Qg8AgxXRkpgWM9zAh7r31PBO86vq/B4ZbON/TtWdcZVsAcVB4Pk\ntqc8JNkCgYEA+g8V+y92SETdcwUkbd5O9Fg5CkfdsALsBXVH6FunrCUV5HS9l5o6\nMMBbJ/08odW/bP5BmOa4A/Hbk9uG/UfQn2KQ3HCgPlxUEwQO07R1/FcQOe4xmyG6\nJpDgQ30viE1RtlCkceQWUeitCIqZsYu0i8sLZLWJH+V/07OB4G17ELMCgYEA+g1v\nhrlAFNhZvrIX/zcP3xF2pZ+AqkFXdL/tWQZkWAVToONn/LlXTH71C/TO2x+OaQRm\nqX1bA9Zhyjf1gYQN9RenjUswvggk0aY2Tk28wUqowMGSsjQHmZ20EphHNMWNJpdS\nfKFfrQIFKCnLlpQVNz+j3bLWZUnq+jPaYnJP7NUCgYEA48qcVo7c7Ga3aNEVZ3St\nbg90HrZq760pvqshDz13V+0MrWnfUFxxh/mi0KHy+uYRlMNllFkQ5p8LTP0dUlt6\nY8dReU6r20MWX6BBtX9eP7o8ENm4nL4zqnAtq609gKgWuMNrmkiSQJl6Dx7bdY5z\nsSkNPvfUa5cQRBTxSjXRdtsCgYBHrzpdwRXh4/Q2ew/uFnbyWCtPZ96W8IyF58+/\nSdnSchR7dzYEeY3RXEQb3V6/6tgEu0JDLLC+9OKr+kbjjlwB+3oJQ5kBoYwMnj3L\nTPXj4+dk+xl3BPt4yoEpI4amVkwU2CTJnemzy3R3AyReUq2SXSg5El/sQbifaeYd\neu/20QKBgH/5IZHGBKiRAe1ww2FzOpDtL8VXXTe3EAXKutfajrHTqPz9+lXknX/D\nUMosh264nYXYS29WqxhJVutbE9u8e0VpuY1qIN9/3R0WKfTLTMUFlZtbqTepvsy1\nW2UbK732I4Nfp0/mtUvOSdMZO8dxbSdEeMnw/Ec8QgxK9a1rRu9+\n-----END RSA PRIVATE KEY-----" + + p := crypto.x509.parse_rsa_private_key(pem) + want_result: + - x: + d: DIxnTzKCpTR286rhrck-n80Kjl6XyHqYWxfx6Dk8IfLj-xBFIEMsikRd9p6L6KoMDVX8TbAOWSxpR5iSGifLi66LVDdxIWNgx3q1Embol9X8NyKw7zxTU0d-wE6VJ0_F0WGGEdxRJMkdUxlvmIFeR2Wd3QeewJxWC4LKEvDr8o8XKID41hHPrx-XHN7tp2bJWnkLqL8dsw-rw-syKUFS84W3G5oZ4kECJq-7NR70jvR1HCl7jbi0y-KxIDyHtdubwF4388kcNX_YmshRGBDnrfJDlKj9CDwCDFdGSmBYz3MCHuvfU8E7zq-r8Hhls439O1Z1xlWwBxUHg-S2pzwk2Q + dp: 48qcVo7c7Ga3aNEVZ3Stbg90HrZq760pvqshDz13V-0MrWnfUFxxh_mi0KHy-uYRlMNllFkQ5p8LTP0dUlt6Y8dReU6r20MWX6BBtX9eP7o8ENm4nL4zqnAtq609gKgWuMNrmkiSQJl6Dx7bdY5zsSkNPvfUa5cQRBTxSjXRdts + dq: R686XcEV4eP0NnsP7hZ28lgrT2felvCMhefPv0nZ0nIUe3c2BHmN0VxEG91ev-rYBLtCQyywvvTiq_pG445cAft6CUOZAaGMDJ49y0z14-PnZPsZdwT7eMqBKSOGplZMFNgkyZ3ps8t0dwMkXlKtkl0oORJf7EG4n2nmHXrv9tE + e: AQAB + kty: RSA + "n": 9D_bK4171aiTNUkrUCHKGMLSQooV-o3wdz2889h9iv0HhhBJCAGU54K3duB8ofHpmYL50QodcR4RLw1vSkaI-FPdPDMyKxKj_YcmofJjz4kW-IqwFbBcbMnKnEVzye-CyW9YYOTu0xWtcgen80zGp2opG0GZX86hBjjXJnjOdrJTk6x2NAiJIbjsQevysmj-2MyqVm8widxw0x-rGhTaCD-ZXWitN0a0WO1aaA8c_7i99I9zhe2peKvXzEtMaqYO9ptHcYmq2z0QWvZuJVMv5Yn0mScLWyh91R099IOtn6sNaMMsOyTpi7E_2IlVgI2uKGPEopKkMFV8Fl2YaAbo7w + p: -g8V-y92SETdcwUkbd5O9Fg5CkfdsALsBXVH6FunrCUV5HS9l5o6MMBbJ_08odW_bP5BmOa4A_Hbk9uG_UfQn2KQ3HCgPlxUEwQO07R1_FcQOe4xmyG6JpDgQ30viE1RtlCkceQWUeitCIqZsYu0i8sLZLWJH-V_07OB4G17ELM + q: -g1vhrlAFNhZvrIX_zcP3xF2pZ-AqkFXdL_tWQZkWAVToONn_LlXTH71C_TO2x-OaQRmqX1bA9Zhyjf1gYQN9RenjUswvggk0aY2Tk28wUqowMGSsjQHmZ20EphHNMWNJpdSfKFfrQIFKCnLlpQVNz-j3bLWZUnq-jPaYnJP7NU + qi: f_khkcYEqJEB7XDDYXM6kO0vxVddN7cQBcq619qOsdOo_P36VeSdf8NQyiyHbridhdhLb1arGElW61sT27x7RWm5jWog33_dHRYp9MtMxQWVm1upN6m-zLVbZRsrvfYjg1-nT-a1S85J0xk7x3FtJ0R4yfD8RzxCDEr1rWtG734 + - note: cryptox509parsersaprivatekey/invalid + query: data.testing.p = x + modules: + - | + package testing + + p := crypto.x509.parse_rsa_private_key("invalid") + want_error_code: eval_builtin_error + want_error: illegal base64 + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/dataderef/test-data-derefs.yaml b/third_party/opa/v1/test/cases/testdata/v1/dataderef/test-data-derefs.yaml new file mode 100644 index 000000000000..c933bb5105ad --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/dataderef/test-data-derefs.yaml @@ -0,0 +1,38 @@ +--- +cases: + - note: data/toplevel integer + query: data.test.p = x + modules: + - | + package test + + p := data[2] + data: + "2": bar + want_result: + - x: bar + - note: data/nested integer + query: data.test.p = x + modules: + - | + package test + + p := data.nested[2] + data: + nested: + "2": bar + want_result: + - x: bar + - note: "data/negative case: nested integer" + query: data.test.p = x + modules: + - | + package test + + p := obj[2] if { + obj := data.nested + } + data: + nested: + "2": bar + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-default-functions.yaml b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-default-functions.yaml new file mode 100644 index 000000000000..e83cd26fc28b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-default-functions.yaml @@ -0,0 +1,40 @@ +--- +cases: + - note: defaultkeyword/function with var arg + query: data.test = x + modules: + - | + package test + + default f(_) := 100 + want_result: + - x: {} + - note: defaultkeyword/function with var arg, ref head + query: data.test = x + modules: + - | + package test + + default p.q.r.f(x) := 100 + want_result: + - x: + p: + q: + r: {} + - note: defaultkeyword/function with var arg, ref head query + query: data.test.foo = x + modules: + - | + package test + + default p.q.r.f(x) := 100 + + p.q.r.f(x) := x if { + x == 2 + } + + foo if { + p.q.r.f(3) == 100 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0804.yaml b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0804.yaml new file mode 100644 index 000000000000..c0ac23fc3194 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0804.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: defaultkeyword/undefined + query: data.generated.p = x + modules: + - | + package generated + + p := 1 if { + false + } + + default p := 0 + + p := 2 if { + false + } + data: {} + want_result: + - x: 0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0805.yaml b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0805.yaml new file mode 100644 index 000000000000..b27dc0bda3d5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0805.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: defaultkeyword/defined + query: data.generated.p = x + modules: + - | + package generated + + default p := 0 + + p := 1 + + p := 2 if { + false + } + data: {} + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0806.yaml b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0806.yaml new file mode 100644 index 000000000000..986a7ec41dd4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0806.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: defaultkeyword/defined-ooo + query: data.generated.p = x + modules: + - | + package generated + + p := 1 + + default p := 0 + + p := 2 if { + false + } + data: {} + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0807.yaml b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0807.yaml new file mode 100644 index 000000000000..16e9ba515095 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0807.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: defaultkeyword/array comprehension + query: data.generated.p = x + modules: + - | + package generated + + p := 1 if { + false + } + + default p := [x | data.a[_] = x] + data: + a: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: + - "1" + - "2" + - "3" + - "4" diff --git a/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0808.yaml b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0808.yaml new file mode 100644 index 000000000000..d748fe7f7f07 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0808.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: defaultkeyword/object comprehension + query: data.generated.p = x + modules: + - | + package generated + + p := 1 if { + false + } + + default p := {x: k | data.d[k][_] = x} + data: + d: + e: + - bar + - baz + want_result: + - x: + bar: e + baz: e diff --git a/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0809.yaml b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0809.yaml new file mode 100644 index 000000000000..6b3ca5d98e4c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/defaultkeyword/test-defaultkeyword-0809.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: defaultkeyword/set comprehension + query: data.generated.p = x + modules: + - | + package generated + + p := 1 if { + false + } + + default p := {x | data.a[_] = x} + data: + a: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: + - "1" + - "2" + - "3" + - "4" diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0763.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0763.yaml new file mode 100644 index 000000000000..68390291022f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0763.yaml @@ -0,0 +1,33 @@ +--- +cases: + - note: "disjunction/incr: query set" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.a[i] = x + } + + p contains y if { + data.b[j] = y + } + data: + a: + - "1" + - "2" + - "3" + - "4" + b: + v1: hello + v2: goodbye + want_result: + - x: + - "1" + - "2" + - "3" + - "4" + - goodbye + - hello + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0764.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0764.yaml new file mode 100644 index 000000000000..db6ef1d8b60b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0764.yaml @@ -0,0 +1,27 @@ +--- +cases: + - note: "disjunction/incr: query set constants" + query: data.generated.p = x + modules: + - | + package generated + + p contains 100 + + p contains x if { + data.a[x] + } + data: + a: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: + - 0 + - 1 + - 2 + - 3 + - 100 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0765.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0765.yaml new file mode 100644 index 000000000000..8ec92959638e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0765.yaml @@ -0,0 +1,48 @@ +--- +cases: + - note: "disjunction/incr: query object" + query: data.generated.p = x + modules: + - | + package generated + + p[k] := v if { + data.b[v] = k + } + + p[k] := v if { + data.a[i] = v + data.g[k][j] = v + } + data: + a: + - "1" + - "2" + - "3" + - "4" + b: + v1: hello + v2: goodbye + g: + a: + - "1" + - "0" + - "0" + - "0" + b: + - "0" + - "2" + - "0" + - "0" + c: + - "0" + - "0" + - "0" + - "4" + want_result: + - x: + a: "1" + b: "2" + c: "4" + goodbye: v2 + hello: v1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0766.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0766.yaml new file mode 100644 index 000000000000..622eab91aea0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0766.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "disjunction/incr: query object constant key" + query: data.generated.p = x + modules: + - | + package generated + + p["a"] := 1 + + p["b"] := 2 + data: {} + want_result: + - x: + a: 1 + b: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0767.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0767.yaml new file mode 100644 index 000000000000..fb7743cdbca0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0767.yaml @@ -0,0 +1,37 @@ +--- +cases: + - note: "disjunction/incr: iter set" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[x] + } + + q contains x if { + data.a[i] = x + } + + q contains y if { + data.b[j] = y + } + data: + a: + - "1" + - "2" + - "3" + - "4" + b: + v1: hello + v2: goodbye + want_result: + - x: + - "1" + - "2" + - "3" + - "4" + - goodbye + - hello + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0768.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0768.yaml new file mode 100644 index 000000000000..58d12c1e66bb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0768.yaml @@ -0,0 +1,38 @@ +--- +cases: + - note: "disjunction/incr: eval set" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q = s + s[x] + } + + q contains x if { + data.a[_0] = x + } + + q contains y if { + data.b[_0] = y + } + data: + a: + - "1" + - "2" + - "3" + - "4" + b: + v1: hello + v2: goodbye + want_result: + - x: + - "1" + - "2" + - "3" + - "4" + - goodbye + - hello + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0769.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0769.yaml new file mode 100644 index 000000000000..98a5df67243d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0769.yaml @@ -0,0 +1,52 @@ +--- +cases: + - note: "disjunction/incr: eval object" + query: data.generated.p = x + modules: + - | + package generated + + p[k] := v if { + data.generated.q[k] = v + } + + q[k] := v if { + data.b[v] = k + } + + q[k] := v if { + data.a[i] = v + data.g[k][j] = v + } + data: + a: + - "1" + - "2" + - "3" + - "4" + b: + v1: hello + v2: goodbye + g: + a: + - "1" + - "0" + - "0" + - "0" + b: + - "0" + - "2" + - "0" + - "0" + c: + - "0" + - "0" + - "0" + - "4" + want_result: + - x: + a: "1" + b: "2" + c: "4" + goodbye: v2 + hello: v1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0770.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0770.yaml new file mode 100644 index 000000000000..4bd7b5fdad98 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0770.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "disjunction/incr: eval object constant key" + query: data.generated.p = x + modules: + - | + package generated + + p[k] := v if { + data.generated.q[k] = v + } + + q["a"] := 1 + + q["b"] := 2 + data: {} + want_result: + - x: + a: 1 + b: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0771.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0771.yaml new file mode 100644 index 000000000000..6d23b2a0d86c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0771.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: "disjunction/complete: undefined" + query: data.generated.p = x + modules: + - | + package generated + + p if { + false + } + + p if { + false + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0772.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0772.yaml new file mode 100644 index 000000000000..f7ad1950ebbb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0772.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "disjunction/complete: error" + query: data.generated.p = x + modules: + - | + package generated + + p := true + + p := false if { + false + } + + p := false + data: {} + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0773.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0773.yaml new file mode 100644 index 000000000000..c2fd5603798a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0773.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "disjunction/complete: valid" + query: data.generated.p = x + modules: + - | + package generated + + p := true + + p := true + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0774.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0774.yaml new file mode 100644 index 000000000000..6aead68e84c0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0774.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "disjunction/complete: valid-2" + query: data.generated.p = x + modules: + - | + package generated + + p := true + + p := false if { + false + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0775.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0775.yaml new file mode 100644 index 000000000000..da8bf2cd7a07 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0775.yaml @@ -0,0 +1,208 @@ +--- +cases: + - note: "disjunction/complete: reference error" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q + } + + q := true + + q := false + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs + - note: "disjunction/complete: nested conflict, else" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q + } + + q if { + false + } + + else := true + + q if { + false + } + + else := false + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs + - note: disjunction/nested function with conflict + query: data.test.p = x + modules: + - | + package test + + p if { + x := data.test.q(1) + x == true + } + + q(_) := true + + q(_) := false + want_error_code: eval_conflict_error + want_error: functions must not produce multiple outputs for same inputs + - note: disjunction/nested function with conflict, else + query: data.test.p = x + modules: + - | + package test + + p if { + x := data.test.q(1) + } + + q(_) if { + false + } + + else := true + + q(_) if { + false + } + + else := false + want_error_code: eval_conflict_error + want_error: functions must not produce multiple outputs for same inputs + - note: "disjunction/complete: conflict involving early-exit complete rule (set enumeration)" + query: data.test.p = x + modules: + - | + package test + + p if { + q + } + + xs := {1, 2} + + q := xs[_] + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs + - note: "disjunction/complete: conflict involving early-exit complete rule (array enumeration)" + query: data.test.p = x + modules: + - | + package test + + p if { + q + } + + xs := [1, 2] + + q := xs[_] + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs + - note: "disjunction/complete: conflict involving early-exit complete rule (object enumeration)" + query: data.test.p = x + modules: + - | + package test + + p if { + q + } + + xs := {"a": 1, "b": 2} + + q := xs[_] + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs + - note: "disjunction/complete: conflict involving early-exit partial rule" + query: data.test.p = x + modules: + - | + package test + + p if { + q + } + + xs := {1, 2} + + q[y] := x if { + x := xs[_] + y := 1 + } + want_error_code: eval_conflict_error + want_error: object keys must be unique + - note: "disjunction/complete: conflict involving early-exit complete rule, else" + query: data.test.p = x + modules: + - | + package test + + p if { + q + } + + xs := {1, 2} + + q := false if { + false + } else := xs[_] + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs + - note: "disjunction/complete: conflict involving early-exit complete rule, multiple" + query: data.test.p = x + modules: + - | + package test + + p if { + q + } + + xs := {1, 2} + + q := xs[_] + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs + - note: "disjunction/complete: conflict involving early-exit complete rule, data array enumeration" + query: data.test.p = x + modules: + - | + package test + + p if { + q + } + + q := data.arr[_] + data: + arr: + - "1" + - "2" + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs + - note: "disjunction/complete: conflict involving early-exit complete rule, data object enumeration" + query: data.test.p = x + modules: + - | + package test + + p if { + q + } + + q := data.obj[_] + data: + obj: + a: "1" + b: "2" + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs diff --git a/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0776.yaml b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0776.yaml new file mode 100644 index 000000000000..1c80ebd7fa66 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/disjunction/test-disjunction-0776.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "disjunction/complete: reference valid" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q + } + + q := true + + q := true + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1054.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1054.yaml new file mode 100644 index 000000000000..480d1a1f97da --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1054.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: elsekeyword/no-op + query: data.ex.no_op = x + modules: + - | + package ex + + no_op if { + true + } else := false + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1055.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1055.yaml new file mode 100644 index 000000000000..9ff0d3165688 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1055.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: elsekeyword/trivial + query: data.ex.bool = x + modules: + - | + package ex + + bool if { + false + } else := true + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1056.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1056.yaml new file mode 100644 index 000000000000..1e0b1dceb1b3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1056.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: elsekeyword/trivial-non-bool + query: data.ex.non_bool = x + modules: + - | + package ex + + non_bool := null if { + false + } else := [100] + data: {} + want_result: + - x: + - 100 diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1057.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1057.yaml new file mode 100644 index 000000000000..19b889d785eb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1057.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: elsekeyword/trivial-3 + query: data.ex.triple = x + modules: + - | + package ex + + triple if { + false + } else if { + false + } else := "hello" + data: {} + want_result: + - x: hello diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1058.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1058.yaml new file mode 100644 index 000000000000..84ceb34b0c5f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1058.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: elsekeyword/var-head + query: data.ex.vars = x + modules: + - | + package ex + + vars if { + false + } else := ["hello", x] if { + data.b.v2 = x + } + data: + a: + - "1" + - "2" + - "3" + - "4" + b: + v1: hello + v2: goodbye + want_result: + - x: + - hello + - goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1059.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1059.yaml new file mode 100644 index 000000000000..af20775e34d6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1059.yaml @@ -0,0 +1,28 @@ +--- +cases: + - note: elsekeyword/ref-head + query: data.ex.refs = x + modules: + - | + package ex + + refs if { + false + } else := __local6__ if { + true + __local7__ = data.b.v2 + __local6__ = ["hello", __local7__] + } + data: + a: + - "1" + - "2" + - "3" + - "4" + b: + v1: hello + v2: goodbye + want_result: + - x: + - hello + - goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1060.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1060.yaml new file mode 100644 index 000000000000..d1bca9bea7a0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1060.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: elsekeyword/first-match + query: data.ex.multiple_defined = x + modules: + - | + package ex + + multiple_defined := false if { + false + } else if { + true + } else := false + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1061.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1061.yaml new file mode 100644 index 000000000000..05fce710a2e9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1061.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: elsekeyword/default-1 + query: data.ex.default_1 = x + modules: + - | + package ex + + default default_1 := 1 + + default_1 if { + false + } + + default_1 := 2 + data: {} + want_result: + - x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1062.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1062.yaml new file mode 100644 index 000000000000..9506a812900b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1062.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: elsekeyword/default-2 + query: data.ex.default_2 = x + modules: + - | + package ex + + default default_2 := 2 + + default_2 if { + false + } + + default_2 := 1 if { + false + } + data: {} + want_result: + - x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1063.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1063.yaml new file mode 100644 index 000000000000..c846e888e68c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1063.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: elsekeyword/multiple-roots + query: data.ex.multiple_roots = x + modules: + - | + package ex + + multiple_roots if { + false + } else := 1 if { + false + } else := 2 if { + true + } else := 3 + + multiple_roots := 2 + + multiple_roots := 3 if { + false + } else := 2 + data: {} + want_result: + - x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1064.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1064.yaml new file mode 100644 index 000000000000..5c4e2e9268ef --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1064.yaml @@ -0,0 +1,34 @@ +--- +cases: + - note: elsekeyword/indexed + query: data.ex.indexed = x + modules: + - | + package ex + + indexed if { + data.a[0] = 0 + } else := 2 if { + data.a[0] = 1 + } else := 3 if { + data.a[0] = 1 + } + + indexed if { + data.a[0] = 1 + data.a[2] = 2 + } else if { + false + } else := 2 if { + data.a[0] = x + x = 1 + data.a[2] = 3 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1065.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1065.yaml new file mode 100644 index 000000000000..4184394b9fa9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1065.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: elsekeyword/conflict-1 + query: data.ex.conflict_1 = x + modules: + - | + package ex + + conflict_1 if { + false + } else := true + + conflict_1 := false + data: {} + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1066.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1066.yaml new file mode 100644 index 000000000000..5c715eb0a653 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1066.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: elsekeyword/conflict-2 + query: data.ex.conflict_2 = x + modules: + - | + package ex + + conflict_2 if { + false + } + + else := false + + conflict_2 if { + false + } + + else := true + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs diff --git a/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1067.yaml b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1067.yaml new file mode 100644 index 000000000000..dc222ac7163d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/elsekeyword/test-elsekeyword-1067.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: elsekeyword/functions + query: data.ex.fn_result = x + modules: + - | + package ex + + fn_result := [x, y, z] if { + data.ex.fn(101, true, x) + data.ex.fn(100, true, y) + data.ex.fn(100, false, z) + } + + fn(x, y) := "large" if { + x > 100 + } else := "small" if { + y = true + } else := "medium" + data: {} + want_result: + - x: + - large + - small + - medium diff --git a/third_party/opa/v1/test/cases/testdata/v1/embeddedvirtualdoc/test-embeddedvirtualdoc-0976.yaml b/third_party/opa/v1/test/cases/testdata/v1/embeddedvirtualdoc/test-embeddedvirtualdoc-0976.yaml new file mode 100644 index 000000000000..a935e4ffc27d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/embeddedvirtualdoc/test-embeddedvirtualdoc-0976.yaml @@ -0,0 +1,45 @@ +--- +cases: + - note: embeddedvirtualdoc/deep embedded vdoc + query: data.b.c.d.p = x + modules: + - | + package b.c.d + + p contains x if { + data.a[i] = x + data.b.c.d.q[x] + } + + q contains x if { + data.g[j][k] = x + } + data: + a: + - "1" + - "2" + - "3" + - "4" + g: + a: + - "1" + - "0" + - "0" + - "0" + b: + - "0" + - "2" + - "0" + - "0" + c: + - "0" + - "0" + - "0" + - "4" + input_term: "{}" + want_result: + - x: + - "1" + - "2" + - "4" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0545.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0545.yaml new file mode 100644 index 000000000000..fc69e14ccfe7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0545.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "eqexpr/undefined: same type" + query: data.generated.p = x + modules: + - | + package generated + + p if { + true = false + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0546.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0546.yaml new file mode 100644 index 000000000000..970b2acd406e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0546.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "eqexpr/undefined: array order" + query: data.generated.p = x + modules: + - | + package generated + + p if { + [1, 2, 3] = [1, 3, 2] + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0547.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0547.yaml new file mode 100644 index 000000000000..7c6f967058b6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0547.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "eqexpr/undefined: ref value" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a[3] = 9999 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0548.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0548.yaml new file mode 100644 index 000000000000..6b55a7db37ee --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0548.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "eqexpr/undefined: ref values" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a[i] = 9999 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0549.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0549.yaml new file mode 100644 index 000000000000..c14d06dd03d7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0549.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: "eqexpr/undefined: ground var" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a[3] = x + x = 3 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0550.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0550.yaml new file mode 100644 index 000000000000..2cb17de7502c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0550.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "eqexpr/undefined: array var 1" + query: data.generated.p = x + modules: + - | + package generated + + p if { + [1, x, x] = [1, 2, 3] + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0551.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0551.yaml new file mode 100644 index 000000000000..e89495ee88fc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0551.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "eqexpr/undefined: array var 2" + query: data.generated.p = x + modules: + - | + package generated + + p if { + [1, x, 3] = [1, 2, x] + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0552.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0552.yaml new file mode 100644 index 000000000000..40815a366d7b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0552.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "eqexpr/undefined: object var 1" + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.a + __local1__ = data.a + {"a": 1, "b": 2} = {"a": __local0__, "b": __local1__} + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0553.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0553.yaml new file mode 100644 index 000000000000..a25ad359ff68 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0553.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "eqexpr/undefined: array deep var 1" + query: data.generated.p = x + modules: + - | + package generated + + p if { + [[1, x], [3, x]] = [[1, 2], [3, 4]] + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0554.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0554.yaml new file mode 100644 index 000000000000..2313d80278a4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0554.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "eqexpr/undefined: array deep var 2" + query: data.generated.p = x + modules: + - | + package generated + + p if { + [[1, x], [3, 4]] = [[1, 2], [x, 4]] + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0555.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0555.yaml new file mode 100644 index 000000000000..7c3f2c19af51 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0555.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "eqexpr/undefined: set" + query: data.generated.p = x + modules: + - | + package generated + + p if { + {1, 2, 3} = {1, 2, 4} + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0556.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0556.yaml new file mode 100644 index 000000000000..86ffb11a0958 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0556.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "eqexpr/ground: bool" + query: data.generated.p = x + modules: + - | + package generated + + p if { + true = true + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0557.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0557.yaml new file mode 100644 index 000000000000..a91013e6493a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0557.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "eqexpr/ground: string" + query: data.generated.p = x + modules: + - | + package generated + + p if { + "string" = "string" + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0558.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0558.yaml new file mode 100644 index 000000000000..7ef36aabfcfb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0558.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "eqexpr/ground: number" + query: data.generated.p = x + modules: + - | + package generated + + p if { + 17 = 17 + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0559.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0559.yaml new file mode 100644 index 000000000000..b4ea68dfce06 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0559.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "eqexpr/ground: null" + query: data.generated.p = x + modules: + - | + package generated + + p if { + null = null + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0560.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0560.yaml new file mode 100644 index 000000000000..5d358f1826e6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0560.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "eqexpr/ground: array" + query: data.generated.p = x + modules: + - | + package generated + + p if { + [1, 2, 3] = [1, 2, 3] + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0561.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0561.yaml new file mode 100644 index 000000000000..6238c8436f78 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0561.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "eqexpr/ground: set" + query: data.generated.p = x + modules: + - | + package generated + + p if { + {1, 2, 3} = {1, 2, 3} + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0562.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0562.yaml new file mode 100644 index 000000000000..6ed349f7ec8b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0562.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "eqexpr/ground: object" + query: data.generated.p = x + modules: + - | + package generated + + p if { + {"a": [1, 2, 3], "b": false} = {"a": [1, 2, 3], "b": false} + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0563.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0563.yaml new file mode 100644 index 000000000000..a249db241440 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0563.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: "eqexpr/ground: ref 1" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a[2] = 3 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0564.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0564.yaml new file mode 100644 index 000000000000..88ec1f234c28 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0564.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "eqexpr/ground: ref 2" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.b.v2 = "goodbye" + } + data: + b: + v2: goodbye + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0565.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0565.yaml new file mode 100644 index 000000000000..7fcd4d71f4e9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0565.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "eqexpr/ground: ref 3" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.d.e = ["bar", "baz"] + } + data: + d: + e: + - bar + - baz + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0566.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0566.yaml new file mode 100644 index 000000000000..8efa46908b12 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0566.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "eqexpr/ground: ref 4" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.c[0].x[1] = data.c[0].z.q + } + data: + c: + - "true": + - null + - 3.1415 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0567.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0567.yaml new file mode 100644 index 000000000000..bdf596246826 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0567.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "eqexpr/var: x=y=z" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + z = 42 + y = z + x = y + } + data: {} + want_result: + - x: + - 42 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0568.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0568.yaml new file mode 100644 index 000000000000..47e5879be516 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0568.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "eqexpr/var: ref value" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a[3] = x + x = 4 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0569.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0569.yaml new file mode 100644 index 000000000000..6ad291f51607 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0569.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "eqexpr/var: ref values" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a[i] = x + x = 2 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0570.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0570.yaml new file mode 100644 index 000000000000..aed93ececc78 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0570.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "eqexpr/var: ref key" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a[i] = 4 + x = 3 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0571.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0571.yaml new file mode 100644 index 000000000000..8da9c1abb16a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0571.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "eqexpr/var: ref keys" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a[i] = x + i = 2 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0572.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0572.yaml new file mode 100644 index 000000000000..7333bae4047a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0572.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "eqexpr/var: ref ground var" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + i = 2 + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0573.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0573.yaml new file mode 100644 index 000000000000..c52be467f87e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0573.yaml @@ -0,0 +1,31 @@ +--- +cases: + - note: "eqexpr/var: ref ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.c[0].x[i] = data.c[0].z[j] + x = [i, j] + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: + - - 0 + - p + - - 1 + - q + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0574.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0574.yaml new file mode 100644 index 000000000000..013929d7301e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0574.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "eqexpr/pattern: array" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + [1, x, 3] = [1, 2, 3] + } + data: {} + want_result: + - x: + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0575.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0575.yaml new file mode 100644 index 000000000000..92b7a71fdb0f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0575.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "eqexpr/pattern: array 2" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + [[1, x], [3, 4]] = [[1, 2], [3, 4]] + } + data: {} + want_result: + - x: + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0576.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0576.yaml new file mode 100644 index 000000000000..d09d77062f54 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0576.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "eqexpr/pattern: array same var" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + [2, x, 3] = [x, 2, 3] + } + data: {} + want_result: + - x: + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0577.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0577.yaml new file mode 100644 index 000000000000..2f3898957751 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0577.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "eqexpr/pattern: array multiple vars" + query: data.generated.p = x + modules: + - | + package generated + + p contains z if { + [1, x, y] = [1, 2, 3] + z = [x, y] + } + data: {} + want_result: + - x: + - - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0578.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0578.yaml new file mode 100644 index 000000000000..f356cb151715 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0578.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "eqexpr/pattern: array multiple vars 2" + query: data.generated.p = x + modules: + - | + package generated + + p contains z if { + [1, x, 3] = [y, 2, 3] + z = [x, y] + } + data: {} + want_result: + - x: + - - 2 + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0579.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0579.yaml new file mode 100644 index 000000000000..79a9969d9cef --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0579.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "eqexpr/pattern: array ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.a[0] + __local1__ = data.a[1] + __local2__ = data.a[2] + __local3__ = data.a[3] + [1, 2, 3, x] = [__local0__, __local1__, __local2__, __local3__] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0580.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0580.yaml new file mode 100644 index 000000000000..0ec923f5d9b8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0580.yaml @@ -0,0 +1,28 @@ +--- +cases: + - note: "eqexpr/pattern: array non-ground ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.a[0] + __local1__ = data.a[1] + __local2__ = data.a[2] + __local3__ = data.a[i] + [1, 2, 3, x] = [__local0__, __local1__, __local2__, __local3__] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0581.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0581.yaml new file mode 100644 index 000000000000..9c317f728235 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0581.yaml @@ -0,0 +1,27 @@ +--- +cases: + - note: "eqexpr/pattern: array = ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + [true, false, x] = data.c[i][j] + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: + - foo + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0582.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0582.yaml new file mode 100644 index 000000000000..f31dd6108856 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0582.yaml @@ -0,0 +1,27 @@ +--- +cases: + - note: "eqexpr/pattern: array = ref (reversed)" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.c[i][j] = [true, false, x] + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: + - foo + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0583.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0583.yaml new file mode 100644 index 000000000000..7dd7b7c60fc8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0583.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: "eqexpr/pattern: array = var" + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + x = 3 + [1, 2, x] = y + } + data: {} + want_result: + - x: + - - 1 + - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0584.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0584.yaml new file mode 100644 index 000000000000..cef49899119e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0584.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "eqexpr/pattern: object val" + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + {"x": y} = {"x": "y"} + } + data: {} + want_result: + - x: + - "y" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0585.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0585.yaml new file mode 100644 index 000000000000..a08ba102da09 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0585.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "eqexpr/pattern: object same var" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + {"x": x, "y": x} = {"x": 1, "y": 1} + } + data: {} + want_result: + - x: + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0586.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0586.yaml new file mode 100644 index 000000000000..e259e72f5b3a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0586.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "eqexpr/pattern: object multiple vars" + query: data.generated.p = x + modules: + - | + package generated + + p contains z if { + {"x": x, "y": y} = {"x": 1, "y": 2} + z = [x, y] + } + data: {} + want_result: + - x: + - - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0587.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0587.yaml new file mode 100644 index 000000000000..3b83c43eccde --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0587.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "eqexpr/pattern: object multiple vars 2" + query: data.generated.p = x + modules: + - | + package generated + + p contains z if { + {"x": x, "y": 2} = {"x": 1, "y": y} + z = [x, y] + } + data: {} + want_result: + - x: + - - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0588.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0588.yaml new file mode 100644 index 000000000000..e98e38713754 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0588.yaml @@ -0,0 +1,28 @@ +--- +cases: + - note: "eqexpr/pattern: object ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.c[0].x[0] + {"p": __local0__, "q": x} = data.c[i][j] + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0589.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0589.yaml new file mode 100644 index 000000000000..788f21f9bee6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0589.yaml @@ -0,0 +1,30 @@ +--- +cases: + - note: "eqexpr/pattern: object non-ground ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.c[0].x[i] + {"a": 1, "b": x} = {"a": 1, "b": __local0__} + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: + - false + - true + - foo + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0590.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0590.yaml new file mode 100644 index 000000000000..071bd6be8510 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0590.yaml @@ -0,0 +1,31 @@ +--- +cases: + - note: "eqexpr/pattern: object = ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + {"p": y, "q": z} = data.c[i][j] + x = [i, j, y, z] + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: + - - 0 + - z + - true + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0591.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0591.yaml new file mode 100644 index 000000000000..193006963e95 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0591.yaml @@ -0,0 +1,31 @@ +--- +cases: + - note: "eqexpr/pattern: object = ref (reversed)" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.c[i][j] = {"p": y, "q": z} + x = [i, j, y, z] + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: + - - 0 + - z + - true + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0592.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0592.yaml new file mode 100644 index 000000000000..b1dcecc87fd1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0592.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "eqexpr/pattern: object = var" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + y = 2 + {"a": 1, "b": y} = x + } + data: {} + want_result: + - x: + - a: 1 + b: 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0593.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0593.yaml new file mode 100644 index 000000000000..f35e87295e06 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0593.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "eqexpr/pattern: object/array nested" + query: data.generated.p = x + modules: + - | + package generated + + p contains ys if { + data.f[i] = {"xs": [2], "ys": ys} + } + data: + f: + - xs: + - 1 + ys: + - 2 + - xs: + - 2 + ys: + - 3 + want_result: + - x: + - - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0594.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0594.yaml new file mode 100644 index 000000000000..1e0171348e5c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0594.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: "eqexpr/pattern: object/array nested 2" + query: data.generated.p = x + modules: + - | + package generated + + p contains v if { + data.f[i] = {"xs": [x], "ys": [y]} + v = [x, y] + } + data: + f: + - xs: + - 1 + ys: + - 2 + - xs: + - 2 + ys: + - 3 + want_result: + - x: + - - 1 + - 2 + - - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0595.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0595.yaml new file mode 100644 index 000000000000..ec6ee0dab683 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0595.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: "eqexpr/unordered: sets" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + x = 2 + {1, 3, x} = {1, 2, 3} + } + data: {} + want_result: + - x: + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0596.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0596.yaml new file mode 100644 index 000000000000..a9a85518f2a1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0596.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: "eqexpr/unordered: object keys" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + x = "a" + {x: 1} = {"a": 1} + } + want_result: + - x: + - a diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0597.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0597.yaml new file mode 100644 index 000000000000..1c7f29da7ce4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0597.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: "eqexpr/unordered: object keys (reverse)" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + x = "a" + {"a": 1} = {x: 1} + } + data: {} + want_result: + - x: + - a + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0598.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0598.yaml new file mode 100644 index 000000000000..93fcced60fa2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0598.yaml @@ -0,0 +1,34 @@ +--- +cases: + - note: "eqexpr/indexing: intersection" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a[i] = data.g[i][j] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + g: + a: + - 1 + - 0 + - 0 + - 0 + b: + - 0 + - 2 + - 0 + - 0 + c: + - 0 + - 0 + - 0 + - 4 + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0599.yaml b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0599.yaml new file mode 100644 index 000000000000..e637b9dd23be --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/eqexpr/test-eqexpr-0599.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "eqexpr/indexing: input is 1.0" + query: data.test.p = x + modules: + - | + package test + + p if input == 1.0 + input_term: "1.0" + want_result: + - x: true + - note: "eqexpr/indexing: input is 1" + query: data.test.p = x + modules: + - | + package test + + p if input == 1.0 + input_term: "1" + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0525.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0525.yaml new file mode 100644 index 000000000000..6438e1130dce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0525.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: evaltermexpr/true + query: data.generated.p = x + modules: + - | + package generated + + p := true + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0526.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0526.yaml new file mode 100644 index 000000000000..7d226a279278 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0526.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: evaltermexpr/false + query: data.generated.p = x + modules: + - | + package generated + + p if { + false + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0527.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0527.yaml new file mode 100644 index 000000000000..3a4ebd2b70c8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0527.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/number non-zero + query: data.generated.p = x + modules: + - | + package generated + + p if { + -3.14 + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0528.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0528.yaml new file mode 100644 index 000000000000..33b41ae3ffa8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0528.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/number zero + query: data.generated.p = x + modules: + - | + package generated + + p if { + null + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0529.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0529.yaml new file mode 100644 index 000000000000..bed461cac56a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0529.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/null + query: data.generated.p = x + modules: + - | + package generated + + p if { + null + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0530.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0530.yaml new file mode 100644 index 000000000000..4d80cdbae667 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0530.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/string non-empty + query: data.generated.p = x + modules: + - | + package generated + + p if { + "abc" + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0531.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0531.yaml new file mode 100644 index 000000000000..2febd007d944 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0531.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/string empty + query: data.generated.p = x + modules: + - | + package generated + + p if { + "" + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0532.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0532.yaml new file mode 100644 index 000000000000..c3abc40479df --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0532.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/array non-empty + query: data.generated.p = x + modules: + - | + package generated + + p if { + [1, 2, 3] + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0533.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0533.yaml new file mode 100644 index 000000000000..e81e8fd8b786 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0533.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/array empty + query: data.generated.p = x + modules: + - | + package generated + + p if { + [] + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0534.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0534.yaml new file mode 100644 index 000000000000..470d9c5a1e07 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0534.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/object non-empty + query: data.generated.p = x + modules: + - | + package generated + + p if { + {"a": 1} + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0535.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0535.yaml new file mode 100644 index 000000000000..a00c217293c6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0535.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/object empty + query: data.generated.p = x + modules: + - | + package generated + + p if { + {} + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0536.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0536.yaml new file mode 100644 index 000000000000..57d63af16098 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0536.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/set non-empty + query: data.generated.p = x + modules: + - | + package generated + + p if { + {1, 2, 3} + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0537.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0537.yaml new file mode 100644 index 000000000000..ae7b07e6bdab --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0537.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/set empty + query: data.generated.p = x + modules: + - | + package generated + + p if { + set() + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0538.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0538.yaml new file mode 100644 index 000000000000..af41407d7aa7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0538.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: evaltermexpr/ref + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a[i] + } + data: + a: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0539.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0539.yaml new file mode 100644 index 000000000000..37081d880e39 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0539.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: evaltermexpr/ref undefined + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.deadbeef[i] + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0540.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0540.yaml new file mode 100644 index 000000000000..6bcb307c6658 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0540.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: evaltermexpr/ref undefined (path) + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a[true] + } + data: + a: + - "1" + - "2" + - "3" + - "4" + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0541.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0541.yaml new file mode 100644 index 000000000000..734b22393f17 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0541.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: evaltermexpr/ref false + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.c[0].x[1] + } + data: + c: + - "true": + - null + - "3.14159" + x: + - true + - false + - foo + z: + p: true + q: false + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0542.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0542.yaml new file mode 100644 index 000000000000..9f936c76d8f4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0542.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/array comprehension + query: data.generated.p = x + modules: + - | + package generated + + p if { + [x | x = 1] + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0543.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0543.yaml new file mode 100644 index 000000000000..d272fdaa01eb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0543.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: evaltermexpr/array comprehension empty + query: data.generated.p = x + modules: + - | + package generated + + p if { + [x | x = 1; x = 2] + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0544.yaml b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0544.yaml new file mode 100644 index 000000000000..5726b0cc92d2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/evaltermexpr/test-evaltermexpr-0544.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: evaltermexpr/arbitrary position + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a[i] = x + x + i + } + data: + a: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/every/every.yaml b/third_party/opa/v1/test/cases/testdata/v1/every/every.yaml new file mode 100644 index 000000000000..db4debaadb76 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/every/every.yaml @@ -0,0 +1,212 @@ +--- +cases: + - note: every/empty domain (array) + query: data.test.p = x + modules: + - | + package test + + p if { + every x in [] { x != x } + } + want_result: + - x: true + - note: every/empty domain (set) + query: data.test.p = x + modules: + - | + package test + + p if { + every x in set() { x != x } + } + want_result: + - x: true + - note: every/empty domain (object) + query: data.test.p = x + modules: + - | + package test + + p if { + every x in {} { x != x } + } + want_result: + - x: true + - note: every/empty domain (partial rule ref) + query: data.test.p = x + modules: + - | + package test + + l contains 1 if { + false + } + + p if { + every x in l { x != x } + } + want_result: + - x: true + - note: every/domain undefined (input) + query: data.test.p = x + modules: + - | + package test + + p if { + every _ in input { true } + } + want_result: [] + - note: every/domain undefined (data ref) + query: data.test.p = x + modules: + - | + package test + + p if { + every _ in data.foo { true } + } + want_result: [] + - note: every/domain is call + query: data.test.p = x + modules: + - | + package test + + p if { + every x in numbers.range(1, 10) { x >= 1 } + } + want_result: + - x: true + - note: every/simple key/val + query: data.test.p = x + modules: + - | + package test + + p if { + every k, v in [1, 2] { k + 1 == v } + } + want_result: + - x: true + - note: every/simple key/val (set) + query: data.test.p = x + modules: + - | + package test + + p if { + every k, v in {1, 2} { k == v } + } + want_result: + - x: true + - note: every/simple key/val (partial rule ref) + query: data.test.p = x + modules: + - | + package test + + l contains 1 + + l contains 2 + + p if { + every k, v in l { k == v } + } + want_result: + - x: true + - note: every/outer bindings + query: data.test.p = x + modules: + - | + package test + + p if { + i := 10 + every k, v in [1, 2] { k + v != i } + } + want_result: + - x: true + - note: every/simple failure, first + query: data.test.p = x + modules: + - | + package test + + p if { + every v in [1, 2] { v != 1 } + } + want_result: [] + - note: every/simple failure, last + query: data.test.p = x + modules: + - | + package test + + p if { + every v in [1, 2] { v != 2 } + } + want_result: [] + - note: "every/with: domain" + query: data.test.p = x + modules: + - | + package test + + p if { + every v in input { v == 1 } with input as [1, 1, 1] + } + want_result: + - x: true + - note: "every/with: body" + query: data.test.p = x + modules: + - | + package test + + p if { + every v in [1, 2] { v in input } with input as [1, 2, 1, 0] + } + want_result: + - x: true + - note: every/followed by another query + query: data.test.p = x + modules: + - | + package test + + p contains v if { + every v in [1, 2] { v < 3 } + v := 10 + v > 3 + } + want_result: + - x: + - 10 + - note: every/array with calls + query: data.test.p = x + modules: + - | + package test + + p if { + every v in [1 / 2, 3, 4 + 5] { v < 10 } + } + want_result: + - x: true + - note: every/array with calls (fail) + query: data.test.q = x + modules: + - | + package test + + p if { + every v in [1 / 2, 3, 4 + 5] { v < 9 } + } + + q if { + not p + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/every/non_iterable_domain.yaml b/third_party/opa/v1/test/cases/testdata/v1/every/non_iterable_domain.yaml new file mode 100644 index 000000000000..3a1cf51e804e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/every/non_iterable_domain.yaml @@ -0,0 +1,141 @@ +--- +cases: + - note: "every/non-iter domain: int" + query: data.test.p = x + modules: + - | + package test + + default p := 1 + + p := 2 if { + every v in 42 { v > 1 } + } + want_result: + - x: 1 + - note: "every/non-iter domain: string" + query: data.test.p = x + modules: + - | + package test + + default p := 1 + + p := 2 if { + every v in "foobar" { v > 1 } + } + want_result: + - x: 1 + - note: "every/non-iter domain: bool" + query: data.test.p = x + modules: + - | + package test + + default p := 1 + + p := 2 if { + every v in true { v > 1 } + } + want_result: + - x: 1 + - note: "every/non-iter domain: null" + query: data.test.p = x + modules: + - | + package test + + default p := 1 + + p := 2 if { + every v in null { v > 1 } + } + want_result: + - x: 1 + - note: "every/non-iter domain: built-in call" + query: data.test.p = x + modules: + - | + package test + + default p := 1 + + p := 2 if { + every v in floor(13.37) { v > 1 } + } + want_result: + - x: 1 + - note: "every/non-iter domain: function call" + query: data.test.p = x + modules: + - | + package test + + default p := 1 + + p := 2 if { + every v in foo(1, 2) { v > 1 } + } + + foo(a, b) := a + b + want_result: + - x: 1 + - note: "every/non-iter domain: rule ref" + query: data.test.p = x + modules: + - | + package test + + default p := 1 + + p := 2 if { + every v in q { v > 1 } + } + + q := 1 + want_result: + - x: 1 + - note: "every/non-iter domain: data int" + query: data.test.p = x + modules: + - | + package test + + default p := 1 + + p := 2 if { + every v in data.iterate_me { v > 1 } + } + data: + iterate_me: 1 + want_result: + - x: 1 + - note: "every/non-iter domain: input int" + query: data.test.p = x + modules: + - | + package test + + default p := 1 + + p := 2 if { + every v in input.iterate_me { v > 1 } + } + input: + iterate_me: 1 + want_result: + - x: 1 + - note: "every/non-iter domain: input int (1st level)" + query: data.test.p = x + modules: + - | + package test + + default p := 1 + + p := 2 if { + every v in input { v > 1 } + } + input: 1 + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/every/textbook.yaml b/third_party/opa/v1/test/cases/testdata/v1/every/textbook.yaml new file mode 100644 index 000000000000..b67ebdb14e67 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/every/textbook.yaml @@ -0,0 +1,138 @@ +--- +cases: + - note: every/example, fail + query: data.test.p = x + modules: + - | + package test + + p if { + every x in input.containers { + startswith(x.image, "acmecorp.com/") + } + } + input: + containers: + - image: bitcoin-miner + - image: acmecorp.com/webapp + want_result: [] + - note: every/example, success + query: data.test.p = x + modules: + - | + package test + + p if { + every x in input.containers { + startswith(x.image, "acmecorp.com/") + } + } + input: + containers: + - image: acmecorp.com/bitcoin-miner + - image: acmecorp.com/webapp + want_result: + - x: true + - note: every/example with two sets + query: data.test.p = x + modules: + - | + package test + + p if { + containers := {c | c := input.containers[_]} + init_containers := {c | c := input.init_containers[_]} + every x in containers | init_containers { + startswith(x.image, "acmecorp.com/") + } + } + input: + containers: + - image: acmecorp.com/bitcoin-miner + - image: acmecorp.com/webapp + init_containers: + - image: acmecorp.com/bitcoin-miner + want_result: + - x: true + - note: every/example with two sets (fail) + query: data.test.p = x + modules: + - | + package test + + p if { + containers := {c | c := input.containers[_]} + init_containers := {c | c := input.init_containers[_]} + every x in containers | init_containers { + startswith(x.image, "acmecorp.com/") + } + } + input: + containers: + - image: acmecorp.com/bitcoin-miner + - image: acmecorp.com/webapp + init_containers: + - image: bitcoin-miner + want_result: [] + - note: every/example every/some, fail + query: data.test.p = x + modules: + - | + package test + + allowed_repos := {"hooli.com/", "acmecorp.net/"} + + p if { + every c in input.containers { + some repo in allowed_repos + startswith(c.image, repo) + } + } + input: + containers: + - image: hooli.com/bitcoin-miner + - image: acmecorp.net/webapp + - image: nginx + want_result: [] + - note: every/example every/some, success + query: data.test.p = x + modules: + - | + package test + + allowed_repos := {"hooli.com/", "acmecorp.net/"} + + p if { + every c in input.containers { + some repo in allowed_repos + startswith(c.image, repo) + } + } + input: + containers: + - image: hooli.com/bitcoin-miner + - image: acmecorp.net/webapp + - image: hooli.com/nginx + want_result: + - x: true + - note: every/example some/every + query: data.test.deny = x + modules: + - | + package test + + deny if { + some s in input.servers + every port in s.ports { + port != 443 + } + } + input: + servers: + - ports: + - "80" + - "443" + - ports: + - "80" + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/example/test-example-1070.yaml b/third_party/opa/v1/test/cases/testdata/v1/example/test-example-1070.yaml new file mode 100644 index 000000000000..5aedf7794b2b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/example/test-example-1070.yaml @@ -0,0 +1,88 @@ +--- +cases: + - note: example/public servers + query: data.opa.example.public_servers = x + modules: + - | + package opa.example + + public_servers contains server if { + server = data.servers[_] + server.ports[_] = data.ports[i].id + data.ports[i].networks[_] = data.networks[j].id + data.networks[j].public = true + } + + violations contains server if { + server = data.servers[_] + server.protocols[_] = "http" + data.opa.example.public_servers[server] + } + data: + networks: + - id: n1 + public: false + - id: n2 + public: false + - id: n3 + public: true + ports: + - id: p1 + networks: + - n1 + - id: p2 + networks: + - n3 + - id: p3 + networks: + - n2 + servers: + - id: s1 + name: app + ports: + - p1 + - p2 + - p3 + protocols: + - https + - ssh + - id: s2 + name: db + ports: + - p3 + protocols: + - mysql + - id: s3 + name: cache + ports: + - p3 + protocols: + - memcache + - http + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http + input_term: "{}" + want_result: + - x: + - id: s1 + name: app + ports: + - p1 + - p2 + - p3 + protocols: + - https + - ssh + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/example/test-example-1071.yaml b/third_party/opa/v1/test/cases/testdata/v1/example/test-example-1071.yaml new file mode 100644 index 000000000000..7af9c040b6a6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/example/test-example-1071.yaml @@ -0,0 +1,85 @@ +--- +cases: + - note: example/violations + query: data.opa.example.violations = x + modules: + - | + package opa.example + + public_servers contains server if { + server = data.servers[_] + server.ports[_] = data.ports[i].id + data.ports[i].networks[_] = data.networks[j].id + data.networks[j].public = true + } + + violations contains server if { + server = data.servers[_] + server.protocols[_] = "http" + data.opa.example.public_servers[server] + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = {{"id": "s1", "name": "app", "ports": ["p1", "p2", "p3"], "protocols": ["https", "ssh"]}, {"id": "s4", "name": "dev", "ports": ["p1", "p2"], "protocols": ["http"]}} + } + data: + networks: + - id: n1 + public: false + - id: n2 + public: false + - id: n3 + public: true + ports: + - id: p1 + networks: + - n1 + - id: p2 + networks: + - n3 + - id: p3 + networks: + - n2 + servers: + - id: s1 + name: app + ports: + - p1 + - p2 + - p3 + protocols: + - https + - ssh + - id: s2 + name: db + ports: + - p3 + protocols: + - mysql + - id: s3 + name: cache + ports: + - p3 + protocols: + - memcache + - http + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http + input_term: "{}" + want_result: + - x: + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/example/test-example-1072.yaml b/third_party/opa/v1/test/cases/testdata/v1/example/test-example-1072.yaml new file mode 100644 index 000000000000..52670d0d0591 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/example/test-example-1072.yaml @@ -0,0 +1,102 @@ +--- +cases: + - note: example/both + query: data.opa.example = x + modules: + - | + package opa.example + + public_servers contains server if { + server = data.servers[_] + server.ports[_] = data.ports[i].id + data.ports[i].networks[_] = data.networks[j].id + data.networks[j].public = true + } + + violations contains server if { + server = data.servers[_] + server.protocols[_] = "http" + data.opa.example.public_servers[server] + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = {{"id": "s4", "name": "dev", "ports": ["p1", "p2"], "protocols": ["http"]}} + } + data: + networks: + - id: n1 + public: false + - id: n2 + public: false + - id: n3 + public: true + ports: + - id: p1 + networks: + - n1 + - id: p2 + networks: + - n3 + - id: p3 + networks: + - n2 + servers: + - id: s1 + name: app + ports: + - p1 + - p2 + - p3 + protocols: + - https + - ssh + - id: s2 + name: db + ports: + - p3 + protocols: + - mysql + - id: s3 + name: cache + ports: + - p3 + protocols: + - memcache + - http + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http + input_term: "{}" + want_result: + - x: + public_servers: + - id: s1 + name: app + ports: + - p1 + - p2 + - p3 + protocols: + - https + - ssh + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http + violations: + - id: s4 + name: dev + ports: + - p1 + - p2 + protocols: + - http diff --git a/third_party/opa/v1/test/cases/testdata/v1/fix1863/test-fix1863-0706.yaml b/third_party/opa/v1/test/cases/testdata/v1/fix1863/test-fix1863-0706.yaml new file mode 100644 index 000000000000..f0cb1b3e3132 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/fix1863/test-fix1863-0706.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: fix1863/is defined + query: data = x + modules: + - | + package a.b + + # this module is empty + - | + package x + + p := __local0__ if { # p should be defined (an empty object) + true + __local0__ = data.a.b + } + data: {} + want_result: + - x: + a: + b: {} + x: + p: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/fix1863/test-fix1863-0707.yaml b/third_party/opa/v1/test/cases/testdata/v1/fix1863/test-fix1863-0707.yaml new file mode 100644 index 000000000000..1ca629e84634 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/fix1863/test-fix1863-0707.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: fix1863/is defined + query: data.x = x + modules: + - | + package a.b + + # this module is empty + - | + package x + + p := __local0__ if { # p should be defined (an empty object) + true + __local0__ = data.a.b + } + data: {} + want_result: + - x: + p: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/fix1863/test-fix1863-0708.yaml b/third_party/opa/v1/test/cases/testdata/v1/fix1863/test-fix1863-0708.yaml new file mode 100644 index 000000000000..4177b561e056 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/fix1863/test-fix1863-0708.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: fix1863/is defined + query: data.x.p = x + modules: + - | + package a.b + + # this module is empty + - | + package x + + p := __local0__ if { # p should be defined (an empty object) + true + __local0__ = data.a.b + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = {"p": {}} + } + data: {} + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-conflicts.yaml b/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-conflicts.yaml new file mode 100644 index 000000000000..a55af724714d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-conflicts.yaml @@ -0,0 +1,28 @@ +--- +cases: + - note: "functionerrors/conflict: plain false and true result, first round" + query: data.test.p = x + modules: + - | + package test + + o := ["1", "2"] + + f(x) := o[_] == x + + p if f("1") + want_error_code: eval_conflict_error + want_error: functions must not produce multiple outputs for same inputs + - note: "functionerrors/conflict: plain false and true result, second round" + query: data.test.p = x + modules: + - | + package test + + o := ["1", "2"] + + f(x) := o[_] == x + + p if f("2") + want_error_code: eval_conflict_error + want_error: functions must not produce multiple outputs for same inputs diff --git a/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-1012.yaml b/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-1012.yaml new file mode 100644 index 000000000000..48d4247b0fcb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-1012.yaml @@ -0,0 +1,32 @@ +--- +cases: + - note: functionerrors/function output conflict single + query: data.test1.r = x + modules: + - | + package test1 + + p(a) := y if { + y = a[_] + } + + r := y if { + data.test1.p([1, 2, 3], y) + } + want_error_code: eval_conflict_error + want_error: functions must not produce multiple outputs for same inputs + - note: functionerrors/function output conflict, used as boolean + query: data.test.r = x + modules: + - | + package test + + f(_) := true + + f(_) := false + + r if { + data.test.f(1) + } + want_error_code: eval_conflict_error + want_error: functions must not produce multiple outputs for same inputs diff --git a/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-1013.yaml b/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-1013.yaml new file mode 100644 index 000000000000..4229fb789bcb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-1013.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: functionerrors/function input no match + query: data.test2.r = x + modules: + - | + package test2 + + p(1, a) := y if { + y = a + } + + p(2, b) := y if { + y = b + 1 + } + + r := y if { + data.test2.p(3, 0, y) + } + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-1014.yaml b/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-1014.yaml new file mode 100644 index 000000000000..41b9e089ea8a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-1014.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: functionerrors/function output conflict multiple + query: data.test3.r = x + modules: + - | + package test3 + + p(1, a) := y if { + y = a + } + + p(x, y) := z if { + z = x + } + + r := y if { + data.test3.p(1, 0, y) + } + want_error_code: eval_conflict_error + want_error: functions must not produce multiple outputs for same inputs diff --git a/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-undefined-builtin-result.yaml b/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-undefined-builtin-result.yaml new file mode 100644 index 000000000000..2d3429a9f3f9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functionerrors/test-functionerrors-undefined-builtin-result.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: functionerrors/undefined builtin result + query: data.test = x + modules: + - | + package test + + foo := units.parse_bytes("1KB") + + bar := units.parse_bytes("foo") # undefined + want_result: + - x: + foo: 1000 diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0990.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0990.yaml new file mode 100644 index 000000000000..b2a873052034 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0990.yaml @@ -0,0 +1,277 @@ +--- +cases: + - note: functions/basic call + query: data.ex.bar.alice = x + modules: + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local20__) := __local20__ + - | + package test.l1.l2 + + p := true + + f(__local21__) := __local21__ + - | + package test.omit_result + + f(__local22__) := __local22__ + + p if { + data.test.omit_result.f(1) + } + - | + package ex + + foo(__local0__) := y if { + split(__local0__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local1__) := y if { + data.ex.foo(__local1__, y) + } + + chain1(__local2__) := b if { + data.ex.chain0(__local2__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local3__) := [a, b] if { + split(__local3__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local4__) := false + + falsy_func_else(__local5__) if { + __local5__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local7__, __local8__]) := [a, b] if { + data.ex.foo(__local7__, a) + data.ex.foo(__local8__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local9__, "bar": __local10__}) := z if { + data.ex.foo(__local9__, a) + data.test.foo(__local10__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local11__) if { + __local11__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local12__) := y if { + y = __local12__ + } + + multi(2, __local13__) := y if { + __local24__ = 2 * __local13__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local14__) := y if { + __local26__ = __local14__ * 10 + y = __local26__ + } + + multi("foo", __local15__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local16__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local17__) := y if { + trim(__local17__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local18__) := y if { + y = __local18__ + } + + multi("bar", __local19__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + data: + a: + - "1" + - "2" + - "3" + - "4" + b: + v1: hello + v2: goodbye + c: + - x: + - true + - false + - foo + "y": + - null + - "3.14159" + z: + p: true + q: false + d: + e: + - bar + - baz + f: + - xs: + - "1" + ys: + - "2" + - xs: + - "2" + ys: + - "3" + g: + a: + - "1" + - "0" + - "0" + - "0" + b: + - "0" + - "2" + - "0" + - "0" + c: + - "0" + - "0" + - "0" + - "4" + h: + - - "1" + - "2" + - "3" + - - "2" + - "3" + - "4" + l: + - a: bob + b: "-1" + c: + - "1" + - "2" + - "3" + - "4" + - a: alice + b: "1" + c: + - "2" + - "3" + - "4" + - "5" + d: null + m: [] + numbers: + - "1" + - "2" + - "3" + - "4" + strings: + bar: "2" + baz: "3" + foo: "1" + three: "3" + want_result: + - x: + - al + - ce diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0991.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0991.yaml new file mode 100644 index 000000000000..40287aed9553 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0991.yaml @@ -0,0 +1,198 @@ +--- +cases: + - note: functions/false result + query: data.ex.falsy_undefined = x + modules: + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local70__) := __local70__ + - | + package test.l1.l2 + + p := true + + f(__local72__) := __local72__ + - | + package test.omit_result + + f(__local74__) := __local74__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = ["al", "ce"] + } + - | + package ex + + foo(__local46__) := y if { + split(__local46__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local47__) := y if { + data.ex.foo(__local47__, y) + } + + chain1(__local48__) := b if { + data.ex.chain0(__local48__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local49__) := [a, b] if { + split(__local49__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local50__) := false + + falsy_func_else(__local51__) if { + __local51__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local53__, __local54__]) := [a, b] if { + data.ex.foo(__local53__, a) + data.ex.foo(__local54__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local55__, "bar": __local56__}) := z if { + data.ex.foo(__local55__, a) + data.test.foo(__local56__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local57__) if { + __local57__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local58__) := y if { + y = __local58__ + } + + multi(2, __local59__) := y if { + __local24__ = 2 * __local59__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local60__) := y if { + __local26__ = __local60__ * 10 + y = __local26__ + } + + multi("foo", __local61__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local62__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local66__) := y if { + trim(__local66__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local67__) := y if { + y = __local67__ + } + + multi("bar", __local68__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0992.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0992.yaml new file mode 100644 index 000000000000..8ef25d9a6d12 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0992.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: functions/false result negation + query: data.ex.falsy_negation = x + modules: + - | + package topdown_test_partial + + __result__ := _result if { + _result = ["al", "ce"] + } + - | + package ex + + foo(__local46__) := y if { + split(__local46__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local47__) := y if { + data.ex.foo(__local47__, y) + } + + chain1(__local48__) := b if { + data.ex.chain0(__local48__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local49__) := [a, b] if { + split(__local49__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local50__) := false + + falsy_func_else(__local51__) if { + __local51__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local53__, __local54__]) := [a, b] if { + data.ex.foo(__local53__, a) + data.ex.foo(__local54__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local55__, "bar": __local56__}) := z if { + data.ex.foo(__local55__, a) + data.test.foo(__local56__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local57__) if { + __local57__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local58__) := y if { + y = __local58__ + } + + multi(2, __local59__) := y if { + __local24__ = 2 * __local59__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local60__) := y if { + __local26__ = __local60__ * 10 + y = __local26__ + } + + multi("foo", __local61__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local62__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local66__) := y if { + trim(__local66__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local67__) := y if { + y = __local67__ + } + + multi("bar", __local68__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local70__) := __local70__ + - | + package test.l1.l2 + + p := true + + f(__local72__) := __local72__ + - | + package test.omit_result + + f(__local74__) := __local74__ + + p if { + data.test.omit_result.f(1) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0993.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0993.yaml new file mode 100644 index 000000000000..3b4f412c9cb4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0993.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: functions/false else value + query: data.ex.falsy_else_value = x + modules: + - | + package topdown_test_partial + + __result__ := _result if { + _result = true + } + - | + package ex + + foo(__local40__) := y if { + split(__local40__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local41__) := y if { + data.ex.foo(__local41__, y) + } + + chain1(__local42__) := b if { + data.ex.chain0(__local42__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local43__) := [a, b] if { + split(__local43__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local44__) := false + + falsy_func_else(__local45__) if { + __local45__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local64__, __local65__]) := [a, b] if { + data.ex.foo(__local64__, a) + data.ex.foo(__local65__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local69__, "bar": __local71__}) := z if { + data.ex.foo(__local69__, a) + data.test.foo(__local71__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local73__) if { + __local73__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local75__) := y if { + y = __local75__ + } + + multi(2, __local76__) := y if { + __local24__ = 2 * __local76__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local77__) := y if { + __local26__ = __local77__ * 10 + y = __local26__ + } + + multi("foo", __local78__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local79__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local86__) := y if { + trim(__local86__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local87__) := y if { + y = __local87__ + } + + multi("bar", __local88__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local91__) := __local91__ + - | + package test.l1.l2 + + p := true + + f(__local94__) := __local94__ + - | + package test.omit_result + + f(__local97__) := __local97__ + + p if { + data.test.omit_result.f(1) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0994.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0994.yaml new file mode 100644 index 000000000000..8f3346c54320 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0994.yaml @@ -0,0 +1,198 @@ +--- +cases: + - note: functions/false else undefined + query: data.ex.falsy_else_undefined = x + modules: + - | + package topdown_test_partial + + __result__ := _result if { + _result = false + } + - | + package ex + + foo(__local66__) := y if { + split(__local66__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local67__) := y if { + data.ex.foo(__local67__, y) + } + + chain1(__local68__) := b if { + data.ex.chain0(__local68__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local70__) := [a, b] if { + split(__local70__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local72__) := false + + falsy_func_else(__local74__) if { + __local74__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local81__, __local82__]) := [a, b] if { + data.ex.foo(__local81__, a) + data.ex.foo(__local82__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local83__, "bar": __local84__}) := z if { + data.ex.foo(__local83__, a) + data.test.foo(__local84__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local85__) if { + __local85__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local89__) := y if { + y = __local89__ + } + + multi(2, __local90__) := y if { + __local24__ = 2 * __local90__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local92__) := y if { + __local26__ = __local92__ * 10 + y = __local26__ + } + + multi("foo", __local93__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local95__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local106__) := y if { + trim(__local106__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local107__) := y if { + y = __local107__ + } + + multi("bar", __local108__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local112__) := __local112__ + - | + package test.l1.l2 + + p := true + + f(__local116__) := __local116__ + - | + package test.omit_result + + f(__local120__) := __local120__ + + p if { + data.test.omit_result.f(1) + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0995.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0995.yaml new file mode 100644 index 000000000000..27a53095572a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0995.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: functions/false else negation + query: data.ex.falsy_else_negation = x + modules: + - | + package test.l1.l2 + + p := true + + f(__local116__) := __local116__ + - | + package test.omit_result + + f(__local120__) := __local120__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = false + } + - | + package ex + + foo(__local66__) := y if { + split(__local66__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local67__) := y if { + data.ex.foo(__local67__, y) + } + + chain1(__local68__) := b if { + data.ex.chain0(__local68__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local70__) := [a, b] if { + split(__local70__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local72__) := false + + falsy_func_else(__local74__) if { + __local74__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local81__, __local82__]) := [a, b] if { + data.ex.foo(__local81__, a) + data.ex.foo(__local82__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local83__, "bar": __local84__}) := z if { + data.ex.foo(__local83__, a) + data.test.foo(__local84__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local85__) if { + __local85__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local89__) := y if { + y = __local89__ + } + + multi(2, __local90__) := y if { + __local24__ = 2 * __local90__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local92__) := y if { + __local26__ = __local92__ * 10 + y = __local26__ + } + + multi("foo", __local93__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local95__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local106__) := y if { + trim(__local106__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local107__) := y if { + y = __local107__ + } + + multi("bar", __local108__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local112__) := __local112__ + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0996.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0996.yaml new file mode 100644 index 000000000000..bd8e46838924 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0996.yaml @@ -0,0 +1,201 @@ +--- +cases: + - note: functions/chained + query: data.ex.chain2 = x + modules: + - | + package test + + foo(__local126__) := y if { + trim(__local126__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local127__) := y if { + y = __local127__ + } + + multi("bar", __local128__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local133__) := __local133__ + - | + package test.l1.l2 + + p := true + + f(__local138__) := __local138__ + - | + package test.omit_result + + f(__local143__) := __local143__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = true + } + - | + package ex + + foo(__local86__) := y if { + split(__local86__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local87__) := y if { + data.ex.foo(__local87__, y) + } + + chain1(__local88__) := b if { + data.ex.chain0(__local88__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local91__) := [a, b] if { + split(__local91__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local94__) := false + + falsy_func_else(__local96__) if { + __local96__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local98__, __local99__]) := [a, b] if { + data.ex.foo(__local98__, a) + data.ex.foo(__local99__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local100__, "bar": __local101__}) := z if { + data.ex.foo(__local100__, a) + data.test.foo(__local101__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local102__) if { + __local102__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local103__) := y if { + y = __local103__ + } + + multi(2, __local104__) := y if { + __local24__ = 2 * __local104__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local105__) := y if { + __local26__ = __local105__ * 10 + y = __local26__ + } + + multi("foo", __local109__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local110__) := true + + always_true if { + data.ex.always_true_fn(1) + } + data: {} + want_result: + - x: + - foo + - bar diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0997.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0997.yaml new file mode 100644 index 000000000000..cee39d032ba3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0997.yaml @@ -0,0 +1,201 @@ +--- +cases: + - note: functions/cross package + query: data.test.cross = x + modules: + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local154__) := __local154__ + - | + package test.l1.l2 + + p := true + + f(__local160__) := __local160__ + - | + package test.omit_result + + f(__local166__) := __local166__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = ["foo", "bar"] + } + - | + package ex + + foo(__local106__) := y if { + split(__local106__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local107__) := y if { + data.ex.foo(__local107__, y) + } + + chain1(__local108__) := b if { + data.ex.chain0(__local108__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local111__) := [a, b] if { + split(__local111__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local112__) := false + + falsy_func_else(__local113__) if { + __local113__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local115__, __local116__]) := [a, b] if { + data.ex.foo(__local115__, a) + data.ex.foo(__local116__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local117__, "bar": __local118__}) := z if { + data.ex.foo(__local117__, a) + data.test.foo(__local118__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local119__) if { + __local119__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local120__) := y if { + y = __local120__ + } + + multi(2, __local121__) := y if { + __local24__ = 2 * __local121__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local122__) := y if { + __local26__ = __local122__ * 10 + y = __local26__ + } + + multi("foo", __local123__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local124__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local146__) := y if { + trim(__local146__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local147__) := y if { + y = __local147__ + } + + multi("bar", __local148__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + data: {} + want_result: + - x: + - s f + - - ", my name " diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0998.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0998.yaml new file mode 100644 index 000000000000..c9a263cd1fa3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0998.yaml @@ -0,0 +1,202 @@ +--- +cases: + - note: functions/array params + query: data.ex.arraysrule = x + modules: + - | + package test.l1.l3 + + g(__local175__) := __local175__ + - | + package test.l1.l2 + + p := true + + f(__local182__) := __local182__ + - | + package test.omit_result + + f(__local189__) := __local189__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = ["s f", [", my name "]] + } + - | + package ex + + foo(__local125__) := y if { + split(__local125__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local126__) := y if { + data.ex.foo(__local126__, y) + } + + chain1(__local127__) := b if { + data.ex.chain0(__local127__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local128__) := [a, b] if { + split(__local128__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local129__) := false + + falsy_func_else(__local130__) if { + __local130__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local132__, __local133__]) := [a, b] if { + data.ex.foo(__local132__, a) + data.ex.foo(__local133__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local134__, "bar": __local135__}) := z if { + data.ex.foo(__local134__, a) + data.test.foo(__local135__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local136__) if { + __local136__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local137__) := y if { + y = __local137__ + } + + multi(2, __local138__) := y if { + __local24__ = 2 * __local138__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local139__) := y if { + __local26__ = __local139__ * 10 + y = __local26__ + } + + multi("foo", __local140__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local141__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local165__) := y if { + trim(__local165__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local167__) := y if { + y = __local167__ + } + + multi("bar", __local168__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + data: {} + want_result: + - x: + - - h + - h + - - foo diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0999.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0999.yaml new file mode 100644 index 000000000000..4aae7becbf96 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-0999.yaml @@ -0,0 +1,202 @@ +--- +cases: + - note: functions/object params + query: data.ex.objectsrule = x + modules: + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local196__) := __local196__ + - | + package test.l1.l2 + + p := true + + f(__local204__) := __local204__ + - | + package test.omit_result + + f(__local212__) := __local212__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = [["h", "h"], ["foo"]] + } + - | + package ex + + foo(__local142__) := y if { + split(__local142__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local143__) := y if { + data.ex.foo(__local143__, y) + } + + chain1(__local144__) := b if { + data.ex.chain0(__local144__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local145__) := [a, b] if { + split(__local145__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local146__) := false + + falsy_func_else(__local147__) if { + __local147__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local149__, __local150__]) := [a, b] if { + data.ex.foo(__local149__, a) + data.ex.foo(__local150__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local151__, "bar": __local152__}) := z if { + data.ex.foo(__local151__, a) + data.test.foo(__local152__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local153__) if { + __local153__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local154__) := y if { + y = __local154__ + } + + multi(2, __local155__) := y if { + __local24__ = 2 * __local155__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local156__) := y if { + __local26__ = __local156__ * 10 + y = __local26__ + } + + multi("foo", __local157__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local158__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local185__) := y if { + trim(__local185__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local186__) := y if { + y = __local186__ + } + + multi("bar", __local187__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + data: {} + want_result: + - x: + - - h + - h + - i diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1000.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1000.yaml new file mode 100644 index 000000000000..cbd77584d5b5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1000.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: functions/ref func output + query: data.ex.refoutput = x + modules: + - | + package topdown_test_partial + + __result__ := _result if { + _result = [["h", "h"], "i"] + } + - | + package ex + + foo(__local159__) := y if { + split(__local159__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local160__) := y if { + data.ex.foo(__local160__, y) + } + + chain1(__local161__) := b if { + data.ex.chain0(__local161__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local162__) := [a, b] if { + split(__local162__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local163__) := false + + falsy_func_else(__local164__) if { + __local164__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local166__, __local167__]) := [a, b] if { + data.ex.foo(__local166__, a) + data.ex.foo(__local167__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local168__, "bar": __local169__}) := z if { + data.ex.foo(__local168__, a) + data.test.foo(__local169__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local170__) if { + __local170__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local171__) := y if { + y = __local171__ + } + + multi(2, __local172__) := y if { + __local24__ = 2 * __local172__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local173__) := y if { + __local26__ = __local173__ * 10 + y = __local26__ + } + + multi("foo", __local174__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local175__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local205__) := y if { + trim(__local205__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local206__) := y if { + y = __local206__ + } + + multi("bar", __local207__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local217__) := __local217__ + - | + package test.l1.l2 + + p := true + + f(__local226__) := __local226__ + - | + package test.omit_result + + f(__local235__) := __local235__ + + p if { + data.test.omit_result.f(1) + } + data: {} + want_result: + - x: h diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1001.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1001.yaml new file mode 100644 index 000000000000..e3ce870cbbd2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1001.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: functions/always_true + query: data.ex.always_true = x + modules: + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local238__) := __local238__ + - | + package test.l1.l2 + + p := true + + f(__local248__) := __local248__ + - | + package test.omit_result + + f(__local258__) := __local258__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = "h" + } + - | + package ex + + foo(__local176__) := y if { + split(__local176__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local177__) := y if { + data.ex.foo(__local177__, y) + } + + chain1(__local178__) := b if { + data.ex.chain0(__local178__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local179__) := [a, b] if { + split(__local179__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local180__) := false + + falsy_func_else(__local181__) if { + __local181__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local183__, __local184__]) := [a, b] if { + data.ex.foo(__local183__, a) + data.ex.foo(__local184__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local185__, "bar": __local186__}) := z if { + data.ex.foo(__local185__, a) + data.test.foo(__local186__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local187__) if { + __local187__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local188__) := y if { + y = __local188__ + } + + multi(2, __local189__) := y if { + __local24__ = 2 * __local189__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local190__) := y if { + __local26__ = __local190__ * 10 + y = __local26__ + } + + multi("foo", __local191__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local192__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local224__) := y if { + trim(__local224__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local225__) := y if { + y = __local225__ + } + + multi("bar", __local227__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1002.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1002.yaml new file mode 100644 index 000000000000..d838b0607b75 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1002.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: functions/same package call + query: data.test.samepkg = x + modules: + - | + package ex + + foo(__local193__) := y if { + split(__local193__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local194__) := y if { + data.ex.foo(__local194__, y) + } + + chain1(__local195__) := b if { + data.ex.chain0(__local195__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local196__) := [a, b] if { + split(__local196__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local197__) := false + + falsy_func_else(__local198__) if { + __local198__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local200__, __local201__]) := [a, b] if { + data.ex.foo(__local200__, a) + data.ex.foo(__local201__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local202__, "bar": __local203__}) := z if { + data.ex.foo(__local202__, a) + data.test.foo(__local203__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local204__) if { + __local204__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local205__) := y if { + y = __local205__ + } + + multi(2, __local206__) := y if { + __local24__ = 2 * __local206__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local207__) := y if { + __local26__ = __local207__ * 10 + y = __local26__ + } + + multi("foo", __local208__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local209__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local244__) := y if { + trim(__local244__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local245__) := y if { + y = __local245__ + } + + multi("bar", __local246__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local259__) := __local259__ + - | + package test.l1.l2 + + p := true + + f(__local270__) := __local270__ + - | + package test.omit_result + + f(__local281__) := __local281__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = true + } + data: {} + want_result: + - x: w do you do? diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1003.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1003.yaml new file mode 100644 index 000000000000..549eb6578828 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1003.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: functions/void good + query: data.ex.voidGood = x + modules: + - | + package test.l1.l2 + + p := true + + f(__local292__) := __local292__ + - | + package test.omit_result + + f(__local304__) := __local304__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = "w do you do?" + } + - | + package ex + + foo(__local210__) := y if { + split(__local210__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local211__) := y if { + data.ex.foo(__local211__, y) + } + + chain1(__local212__) := b if { + data.ex.chain0(__local212__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local213__) := [a, b] if { + split(__local213__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local214__) := false + + falsy_func_else(__local215__) if { + __local215__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local217__, __local218__]) := [a, b] if { + data.ex.foo(__local217__, a) + data.ex.foo(__local218__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local219__, "bar": __local220__}) := z if { + data.ex.foo(__local219__, a) + data.test.foo(__local220__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local221__) if { + __local221__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local222__) := y if { + y = __local222__ + } + + multi(2, __local223__) := y if { + __local24__ = 2 * __local223__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local224__) := y if { + __local26__ = __local224__ * 10 + y = __local26__ + } + + multi("foo", __local225__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local226__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local264__) := y if { + trim(__local264__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local265__) := y if { + y = __local265__ + } + + multi("bar", __local266__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local279__) := __local279__ + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1004.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1004.yaml new file mode 100644 index 000000000000..c28ee4cf6b12 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1004.yaml @@ -0,0 +1,198 @@ +--- +cases: + - note: functions/void bad + query: data.ex.voidBad = x + modules: + - | + package test.omit_result + + f(__local327__) := __local327__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = true + } + - | + package ex + + foo(__local227__) := y if { + split(__local227__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local228__) := y if { + data.ex.foo(__local228__, y) + } + + chain1(__local229__) := b if { + data.ex.chain0(__local229__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local230__) := [a, b] if { + split(__local230__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local231__) := false + + falsy_func_else(__local232__) if { + __local232__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local234__, __local235__]) := [a, b] if { + data.ex.foo(__local234__, a) + data.ex.foo(__local235__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local236__, "bar": __local237__}) := z if { + data.ex.foo(__local236__, a) + data.test.foo(__local237__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local238__) if { + __local238__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local239__) := y if { + y = __local239__ + } + + multi(2, __local240__) := y if { + __local24__ = 2 * __local240__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local241__) := y if { + __local26__ = __local241__ * 10 + y = __local26__ + } + + multi("foo", __local242__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local243__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local284__) := y if { + trim(__local284__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local285__) := y if { + y = __local285__ + } + + multi("bar", __local286__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local300__) := __local300__ + - | + package test.l1.l2 + + p := true + + f(__local314__) := __local314__ + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1005.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1005.yaml new file mode 100644 index 000000000000..2e9750203754 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1005.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: functions/multi1 + query: data.ex.multi1 = x + modules: + - | + package test.l1.l3 + + g(__local300__) := __local300__ + - | + package test.l1.l2 + + p := true + + f(__local314__) := __local314__ + - | + package test.omit_result + + f(__local327__) := __local327__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = true + } + - | + package ex + + foo(__local227__) := y if { + split(__local227__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local228__) := y if { + data.ex.foo(__local228__, y) + } + + chain1(__local229__) := b if { + data.ex.chain0(__local229__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local230__) := [a, b] if { + split(__local230__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local231__) := false + + falsy_func_else(__local232__) if { + __local232__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local234__, __local235__]) := [a, b] if { + data.ex.foo(__local234__, a) + data.ex.foo(__local235__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local236__, "bar": __local237__}) := z if { + data.ex.foo(__local236__, a) + data.test.foo(__local237__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local238__) if { + __local238__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local239__) := y if { + y = __local239__ + } + + multi(2, __local240__) := y if { + __local24__ = 2 * __local240__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local241__) := y if { + __local26__ = __local241__ * 10 + y = __local26__ + } + + multi("foo", __local242__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local243__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local284__) := y if { + trim(__local284__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local285__) := y if { + y = __local285__ + } + + multi("bar", __local286__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + data: {} + want_result: + - x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1006.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1006.yaml new file mode 100644 index 000000000000..9875aef6aba9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1006.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: functions/multi2 + query: data.ex.multi2 = x + modules: + - | + package test.omit_result + + f(__local350__) := __local350__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = 2 + } + - | + package ex + + foo(__local244__) := y if { + split(__local244__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local245__) := y if { + data.ex.foo(__local245__, y) + } + + chain1(__local246__) := b if { + data.ex.chain0(__local246__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local247__) := [a, b] if { + split(__local247__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local248__) := false + + falsy_func_else(__local249__) if { + __local249__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local251__, __local252__]) := [a, b] if { + data.ex.foo(__local251__, a) + data.ex.foo(__local252__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local253__, "bar": __local254__}) := z if { + data.ex.foo(__local253__, a) + data.test.foo(__local254__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local255__) if { + __local255__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local256__) := y if { + y = __local256__ + } + + multi(2, __local257__) := y if { + __local24__ = 2 * __local257__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local258__) := y if { + __local26__ = __local258__ * 10 + y = __local26__ + } + + multi("foo", __local259__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local260__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local304__) := y if { + trim(__local304__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local305__) := y if { + y = __local305__ + } + + multi("bar", __local306__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local321__) := __local321__ + - | + package test.l1.l2 + + p := true + + f(__local336__) := __local336__ + data: {} + want_result: + - x: 5 diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1007.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1007.yaml new file mode 100644 index 000000000000..8770d5e2b654 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1007.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: functions/multi3 + query: data.ex.multi3 = x + modules: + - | + package ex + + foo(__local261__) := y if { + split(__local261__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local262__) := y if { + data.ex.foo(__local262__, y) + } + + chain1(__local263__) := b if { + data.ex.chain0(__local263__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local264__) := [a, b] if { + split(__local264__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local265__) := false + + falsy_func_else(__local266__) if { + __local266__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local268__, __local269__]) := [a, b] if { + data.ex.foo(__local268__, a) + data.ex.foo(__local269__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local270__, "bar": __local271__}) := z if { + data.ex.foo(__local270__, a) + data.test.foo(__local271__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local272__) if { + __local272__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local273__) := y if { + y = __local273__ + } + + multi(2, __local274__) := y if { + __local24__ = 2 * __local274__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local275__) := y if { + __local26__ = __local275__ * 10 + y = __local26__ + } + + multi("foo", __local276__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local277__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local324__) := y if { + trim(__local324__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local325__) := y if { + y = __local325__ + } + + multi("bar", __local326__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local342__) := __local342__ + - | + package test.l1.l2 + + p := true + + f(__local358__) := __local358__ + - | + package test.omit_result + + f(__local373__) := __local373__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = 5 + } + data: {} + want_result: + - x: 20 diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1008.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1008.yaml new file mode 100644 index 000000000000..b32596825748 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1008.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: functions/multi4 + query: data.ex.multi4 = x + modules: + - | + package test.omit_result + + f(__local396__) := __local396__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = 20 + } + - | + package ex + + foo(__local278__) := y if { + split(__local278__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local279__) := y if { + data.ex.foo(__local279__, y) + } + + chain1(__local280__) := b if { + data.ex.chain0(__local280__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local281__) := [a, b] if { + split(__local281__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local282__) := false + + falsy_func_else(__local283__) if { + __local283__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local285__, __local286__]) := [a, b] if { + data.ex.foo(__local285__, a) + data.ex.foo(__local286__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local287__, "bar": __local288__}) := z if { + data.ex.foo(__local287__, a) + data.test.foo(__local288__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local289__) if { + __local289__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local290__) := y if { + y = __local290__ + } + + multi(2, __local291__) := y if { + __local24__ = 2 * __local291__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local292__) := y if { + __local26__ = __local292__ * 10 + y = __local26__ + } + + multi("foo", __local293__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local294__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local344__) := y if { + trim(__local344__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local345__) := y if { + y = __local345__ + } + + multi("bar", __local346__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local363__) := __local363__ + - | + package test.l1.l2 + + p := true + + f(__local380__) := __local380__ + data: {} + want_result: + - x: bar diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1009.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1009.yaml new file mode 100644 index 000000000000..eabd13daf0ab --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1009.yaml @@ -0,0 +1,201 @@ +--- +cases: + - note: functions/multi cross package + query: data.test.multi_cross_pkg = x + modules: + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local384__) := __local384__ + - | + package test.l1.l2 + + p := true + + f(__local402__) := __local402__ + - | + package test.omit_result + + f(__local419__) := __local419__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = "bar" + } + - | + package ex + + foo(__local295__) := y if { + split(__local295__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local296__) := y if { + data.ex.foo(__local296__, y) + } + + chain1(__local297__) := b if { + data.ex.chain0(__local297__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local298__) := [a, b] if { + split(__local298__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local299__) := false + + falsy_func_else(__local300__) if { + __local300__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local302__, __local303__]) := [a, b] if { + data.ex.foo(__local302__, a) + data.ex.foo(__local303__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local304__, "bar": __local305__}) := z if { + data.ex.foo(__local304__, a) + data.test.foo(__local305__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local306__) if { + __local306__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local307__) := y if { + y = __local307__ + } + + multi(2, __local308__) := y if { + __local24__ = 2 * __local308__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local309__) := y if { + __local26__ = __local309__ * 10 + y = __local26__ + } + + multi("foo", __local310__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local311__) := true + + always_true if { + data.ex.always_true_fn(1) + } + - | + package test + + foo(__local364__) := y if { + trim(__local364__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local365__) := y if { + y = __local365__ + } + + multi("bar", __local366__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + data: {} + want_result: + - x: + - bar + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1010.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1010.yaml new file mode 100644 index 000000000000..40db1f7d98eb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1010.yaml @@ -0,0 +1,202 @@ +--- +cases: + - note: functions/skip-functions + query: data.test.l1 = x + modules: + - | + package test + + foo(__local383__) := y if { + trim(__local383__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local385__) := y if { + y = __local385__ + } + + multi("bar", __local386__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local405__) := __local405__ + - | + package test.l1.l2 + + p := true + + f(__local424__) := __local424__ + - | + package test.omit_result + + f(__local442__) := __local442__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = ["bar", 3] + } + - | + package ex + + foo(__local312__) := y if { + split(__local312__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local313__) := y if { + data.ex.foo(__local313__, y) + } + + chain1(__local314__) := b if { + data.ex.chain0(__local314__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local315__) := [a, b] if { + split(__local315__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local316__) := false + + falsy_func_else(__local317__) if { + __local317__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local319__, __local320__]) := [a, b] if { + data.ex.foo(__local319__, a) + data.ex.foo(__local320__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local321__, "bar": __local322__}) := z if { + data.ex.foo(__local321__, a) + data.test.foo(__local322__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local323__) if { + __local323__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local324__) := y if { + y = __local324__ + } + + multi(2, __local325__) := y if { + __local24__ = 2 * __local325__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local326__) := y if { + __local26__ = __local326__ * 10 + y = __local26__ + } + + multi("foo", __local327__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local328__) := true + + always_true if { + data.ex.always_true_fn(1) + } + data: {} + want_result: + - x: + l2: + p: true + l3: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1011.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1011.yaml new file mode 100644 index 000000000000..eec09745af1f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-1011.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: functions/omit result + query: data.test.omit_result.p = x + modules: + - | + package test + + foo(__local403__) := y if { + trim(__local403__, "h o", y) + } + + cross := y if { + data.ex.cross("hi, my name is foo", y) + } + + multi("foo", __local404__) := y if { + y = __local404__ + } + + multi("bar", __local406__) := y if { + y = "baz" + } + + multi_cross_pkg := [y, z] if { + data.test.multi("foo", "bar", y) + data.ex.multi(2, 1, z) + } + - | + package test + + samepkg := y if { + data.test.foo("how do you do?", y) + } + - | + package test.l1.l3 + + g(__local426__) := __local426__ + - | + package test.l1.l2 + + p := true + + f(__local446__) := __local446__ + - | + package test.omit_result + + f(__local465__) := __local465__ + + p if { + data.test.omit_result.f(1) + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = {"l2": {"p": true}, "l3": {}} + } + - | + package ex + + foo(__local329__) := y if { + split(__local329__, "i", y) + } + + bar[x] := y if { + data.l[_].a = x + data.ex.foo(x, y) + } + + chain0(__local330__) := y if { + data.ex.foo(__local330__, y) + } + + chain1(__local331__) := b if { + data.ex.chain0(__local331__, b) + } + + chain2 := d if { + data.ex.chain1("fooibar", d) + } + + cross(__local332__) := [a, b] if { + split(__local332__, "i", y) + __local27__ = y[1] + data.ex.foo(__local27__, b) + __local28__ = y[2] + data.test.foo(__local28__, a) + } + + falsy_func(__local333__) := false + + falsy_func_else(__local334__) if { + __local334__ = 1 + } + + else := false + + falsy_undefined if { + data.ex.falsy_func(1) + } + + falsy_negation if { + not data.ex.falsy_func(1) + } + + falsy_else_value := __local23__ if { + true + data.ex.falsy_func_else(2, __local23__) + } + + falsy_else_undefined if { + data.ex.falsy_func_else(2) + } + + falsy_else_negation if { + not data.ex.falsy_func_else(2) + } + + arrays([__local336__, __local337__]) := [a, b] if { + data.ex.foo(__local336__, a) + data.ex.foo(__local337__, b) + } + + arraysrule := y if { + data.ex.arrays(["hih", "foo"], y) + } + + objects({"foo": __local338__, "bar": __local339__}) := z if { + data.ex.foo(__local338__, a) + data.test.foo(__local339__, b) + z = [a, b] + } + + objectsrule := y if { + data.ex.objects({"bar": "hi ho", "foo": "hih"}, y) + } + + refoutput := y if { + data.ex.foo("hih", z) + y = z[1] + } + + void(__local340__) if { + __local340__ = "foo" + } + + voidGood if { + not data.ex.void("bar", true) + } + + voidBad if { + data.ex.void("bar", true) + } + + multi(1, __local341__) := y if { + y = __local341__ + } + + multi(2, __local342__) := y if { + __local24__ = 2 * __local342__ + a = __local24__ + __local25__ = a + 1 + y = __local25__ + } + + multi(3, __local343__) := y if { + __local26__ = __local343__ * 10 + y = __local26__ + } + + multi("foo", __local344__) := y if { + y = "bar" + } + + multi1 := y if { + data.ex.multi(1, 2, y) + } + + multi2 := y if { + data.ex.multi(2, 2, y) + } + + multi3 := y if { + data.ex.multi(3, 2, y) + } + + multi4 := y if { + data.ex.multi("foo", 2, y) + } + + always_true_fn(__local345__) := true + + always_true if { + data.ex.always_true_fn(1) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-default.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-default.yaml new file mode 100644 index 000000000000..d107e0d312ec --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-default.yaml @@ -0,0 +1,86 @@ +--- +cases: + - note: functions/default + query: data.test.p = x + modules: + - | + package test + + default f(x) := 1 + + f(x) := x if { + x > 0 + } + + p if { + f(-1) == 1 + } + want_result: + - x: true + - note: functions/non default + query: data.test.p = x + modules: + - | + package test + + default f(x) := 1 + + f(x) := x if { + x > 0 + } + + p if { + f(2) == 2 + } + want_result: + - x: true + - note: functions/only default + query: data.test.p = x + modules: + - | + package test + + default f(x) := 1 + + p if { + f(2) == 1 + } + want_result: + - x: true + - note: functions/wildcard args + query: data.test.p = x + modules: + - | + package test + + default f(_, _) := 1 + + f(x, y) := x if { + x == y + } + + p if { + f(2, 2) == 2 + } + want_result: + - x: true + - note: functions/comprehensions + query: data.test.p = x + modules: + - | + package test + + default f(x) := 1000 + + f(x) := x if { + x > 0 + } + + p := xs if { + xs := [y | x = [1, -2, 3][_]; y := f(x)] + } + want_result: + - x: + - 1 + - 1000 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-nested-with-early-exit.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-nested-with-early-exit.yaml new file mode 100644 index 000000000000..2dc626d16dd5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-nested-with-early-exit.yaml @@ -0,0 +1,93 @@ +--- +cases: + - note: functions/nested complete doc with conflict + query: data.generated.p(1) = x + modules: + - | + package generated + + p(_) if { + data.generated.q + } + + q := true + + q := false + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs + - note: functions/nested complete doc with conflict, else + query: data.generated.p(1) = x + modules: + - | + package generated + + p(_) if { + data.generated.q + } + + q if { + false + } + + else := true + + q if { + false + } + + else := false + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs + - note: functions/nested function with conflict + query: data.generated.p(1) = x + modules: + - | + package generated + + p(x) if { + y := data.generated.q(x) + } + + q(_) := true + + q(_) := false + want_error_code: eval_conflict_error + want_error: functions must not produce multiple outputs for same inputs + - note: functions/nested function with conflict, else + query: data.generated.p(1) = x + modules: + - | + package generated + + p(x) if { + y := data.generated.q(x) + } + + q(_) if { + false + } + + else := true + + q(_) if { + false + } + + else := false + want_error_code: eval_conflict_error + want_error: functions must not produce multiple outputs for same inputs + - note: functions/nested function with conflict, else, no extra return + query: data.test.p(1) = x + modules: + - | + package test + + p(x) if { + y := data.test.q(x) + } + + xs := {1, 2} + + q(_) := xs[_] + want_error_code: eval_conflict_error + want_error: functions must not produce multiple outputs for same inputs diff --git a/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-unused-arg.yaml b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-unused-arg.yaml new file mode 100644 index 000000000000..9c1a1c37d020 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/functions/test-functions-unused-arg.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: unused arg + query: data.p.f(1) + modules: + - | + package p + + f(x) if { + r = input.that_is_not_there + } + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0133.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0133.yaml new file mode 100644 index 000000000000..ada2793a758f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0133.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match with . delimiter + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("*.github.com", ["."], "api.github.com", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0134.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0134.yaml new file mode 100644 index 000000000000..1e03e58a6740 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0134.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/super glob match with . delimiter + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("api.**.com", ["."], "api.github.com", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0135.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0135.yaml new file mode 100644 index 000000000000..4405758543cc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0135.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/super glob match with . delimiter + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("api.**.com", ["."], "api.cdn.github.com", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0136.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0136.yaml new file mode 100644 index 000000000000..3ec3dc208c2f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0136.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "globmatch/glob match with : delimiter" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("*:github:com", [":"], "api:github:com", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0137.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0137.yaml new file mode 100644 index 000000000000..83ee861ca7b8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0137.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob no match with . delimiter + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("*.github.com", ["."], "api.not-github.com", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0138.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0138.yaml new file mode 100644 index 000000000000..22219ea93446 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0138.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match with character-list matchers + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("[abc]at", [], "cat", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0139.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0139.yaml new file mode 100644 index 000000000000..67e9ea30d23e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0139.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob no match with character-list matchers + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("[abc]at", [], "fat", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0140.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0140.yaml new file mode 100644 index 000000000000..6ba476338c37 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0140.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match with negated character-list matchers + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("[!abc]at", [], "fat", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0141.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0141.yaml new file mode 100644 index 000000000000..7f66f522ea64 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0141.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob no match with negated character-list matchers + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("[!abc]at", [], "cat", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0142.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0142.yaml new file mode 100644 index 000000000000..d1af45bf6355 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0142.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match with character-range matchers + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("[a-c]at", [], "bat", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0143.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0143.yaml new file mode 100644 index 000000000000..c1b2176174f2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0143.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob no match with character-range matchers + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("[a-c]at", [], "fat", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0144.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0144.yaml new file mode 100644 index 000000000000..45523ec6bf58 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0144.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob no match with character-range matchers + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("[!a-c]at", [], "bat", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0145.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0145.yaml new file mode 100644 index 000000000000..1f528cf1a2ed --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0145.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match with character-range matchers + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("[!a-c]at", [], "fat", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0146.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0146.yaml new file mode 100644 index 000000000000..d0684df605ad --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0146.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match with single wild-card + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("?at", [], "fat", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0147.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0147.yaml new file mode 100644 index 000000000000..fbe01053c156 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0147.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob no match with single wild-card + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("?at", [], "at", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0148.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0148.yaml new file mode 100644 index 000000000000..d2c665104582 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0148.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match with single wild-card and delimiter + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("?at", ["f"], "bat", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0149.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0149.yaml new file mode 100644 index 000000000000..07681d53fe90 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0149.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob no match with single wild-card and delimiter + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("?at", ["f"], "fat", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0150.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0150.yaml new file mode 100644 index 000000000000..5c9580ad9eb6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0150.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match with pattern-alternatives list (cat) + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("{cat,bat,[fr]at}", [], "cat", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0151.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0151.yaml new file mode 100644 index 000000000000..c6fc7d304252 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0151.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match with pattern-alternatives list (bat) + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("{cat,bat,[fr]at}", [], "bat", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0152.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0152.yaml new file mode 100644 index 000000000000..08ac93bd4bf3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0152.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match with pattern-alternatives list (fat) + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("{cat,bat,[fr]at}", [], "fat", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0153.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0153.yaml new file mode 100644 index 000000000000..acd06f916ff7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0153.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match with pattern-alternatives list (rat) + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("{cat,bat,[fr]at}", [], "rat", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0154.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0154.yaml new file mode 100644 index 000000000000..d60382cf6bee --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0154.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob no match with pattern-alternatives list + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("{cat,bat,[fr]at}", [], "at", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0155.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0155.yaml new file mode 100644 index 000000000000..ab4b7bc66ffd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0155.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match single with . delimiter + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("*", ["."], "foo", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0156.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0156.yaml new file mode 100644 index 000000000000..ac40685705b4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0156.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob match single with default delimiter + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("*", [], "foo", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0157.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0157.yaml new file mode 100644 index 000000000000..6de5c59dbeb8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0157.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob no match single with . delimiter + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("*", ["."], "foo.bar", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0158.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0158.yaml new file mode 100644 index 000000000000..16a4268826be --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0158.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globmatch/glob no match single with default delimiter + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("*", [], "foo.bar", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0159.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0159.yaml new file mode 100644 index 000000000000..92bc23d20454 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-0159.yaml @@ -0,0 +1,30 @@ +--- +cases: + - note: globmatch/glob match single without default delimiter + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("*", null, "foo.bar", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true + - note: globmatch/glob match single without default delimiter, glob non-empty + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.match("foo*", null, "foo.bar", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-issue-5273.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-issue-5273.yaml new file mode 100644 index 000000000000..c101c3fbf481 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-issue-5273.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: globmatch/no deadlocks for glob match + query: data.test.p = x + modules: + - | + package test + + p contains x if { + glob.match("*.github.com", ["."], "api.github.com", x) + glob.match("*.github.com", ["."], "api.github.com", x) + } + want_result: + - x: + - true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-issue-5283.yaml b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-issue-5283.yaml new file mode 100644 index 000000000000..c94d2e8888e3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globmatch/test-globmatch-issue-5283.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: globmatch/captured negative results, variable + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := glob.match("*.github.com", ["."], input) + } + input: api.example.com + want_result: + - x: false + - note: globmatch/captured negative result, constant + query: data.test.p = x + modules: + - | + package test + + p if { + glob.match("*.github.com", ["."], input, false) + } + input: api.example.com + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globquotemeta/test-globquotemeta-0159.yaml b/third_party/opa/v1/test/cases/testdata/v1/globquotemeta/test-globquotemeta-0159.yaml new file mode 100644 index 000000000000..09ea2ee3824b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globquotemeta/test-globquotemeta-0159.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: globquotemeta/glob quote meta + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + glob.quote_meta("*.github.com", x) + } + data: {} + want_result: + - x: + - \*.github.com + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0865.yaml b/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0865.yaml new file mode 100644 index 000000000000..4bfaff40acd3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0865.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: globsmatch/regex.globs_match + query: data.generated.p = x + modules: + - | + package generated + + p if { + regex.globs_match("a.a.[0-9]+z", ".b.b2359825792*594823z") + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0866.yaml b/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0866.yaml new file mode 100644 index 000000000000..52b515d15ba8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0866.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: globsmatch/regex.globs_match + query: data.generated.p = x + modules: + - | + package generated + + p if { + regex.globs_match("[a-z]+", "[0-9]*") + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0867.yaml b/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0867.yaml new file mode 100644 index 000000000000..3f11405b8633 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0867.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "globsmatch/regex.globs_match: bad pattern err" + query: data.generated.p = x + modules: + - | + package generated + + p if { + regex.globs_match("pqrs]", "[a-b]+") + } + want_error_code: eval_builtin_error + want_error: "input:pqrs], pos:5, set-close ']' with no preceding '[': the input provided is invalid" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0868.yaml b/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0868.yaml new file mode 100644 index 000000000000..cdc7196168d0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0868.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "globsmatch/regex.globs_match: ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.d.e[x] + regex.globs_match("b.*", __local0__) + } + data: + d: + e: + - bar + - baz + want_result: + - x: + - 0 + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0869.yaml b/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0869.yaml new file mode 100644 index 000000000000..001519e209f1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0869.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "globsmatch/regex.globs_match: raw" + query: data.generated.p = x + modules: + - | + package generated + + p if { + regex.globs_match(`[a-z]+\[[0-9]+\]`, "foo\\[1\\]") + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0870.yaml b/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0870.yaml new file mode 100644 index 000000000000..3f4ed0c01661 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/globsmatch/test-globsmatch-0870.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "globsmatch/regex.globs_match: raw: undefined" + query: data.generated.p = x + modules: + - | + package generated + + p if { + regex.globs_match(`[a-z]+\[[0-9]+\]`, "foo[\"bar\"]") + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-basic-ast.yaml b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-basic-ast.yaml new file mode 100644 index 000000000000..6f2ecc677fdf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-basic-ast.yaml @@ -0,0 +1,265 @@ +--- +cases: + - note: graphql_parse_query/success-basic-ast simple query + query: data.test.p = x + modules: + - | + package test + + ast := {"Operations": [{ + "Name": "", + "Operation": "query", + "SelectionSet": [{ + "Alias": "hero", + "Name": "hero", + "SelectionSet": [{ + "Alias": "name", + "Name": "name", + }], + }], + }]} + + p if { + graphql.parse_query("{hero {name}}") == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-basic-ast with arguments + query: data.test.p = x + modules: + - | + package test + + gql := `query hero ($episode: Episode!) { + hero (episode: $episode) { + id + name + friends { + id + name + friends { + id + name + appearsIn + } + appearsIn + } + appearsIn + } + }` + + ast := {"Operations": [{ + "Name": "hero", + "Operation": "query", + "SelectionSet": [{ + "Alias": "hero", + "Arguments": [{ + "Name": "episode", + "Value": { + "Kind": 0, + "Raw": "episode", + }, + }], + "Name": "hero", + "SelectionSet": [ + { + "Alias": "id", + "Name": "id", + }, + { + "Alias": "name", + "Name": "name", + }, + { + "Alias": "friends", + "Name": "friends", + "SelectionSet": [ + { + "Alias": "id", + "Name": "id", + }, + { + "Alias": "name", + "Name": "name", + }, + { + "Alias": "friends", + "Name": "friends", + "SelectionSet": [ + { + "Alias": "id", + "Name": "id", + }, + { + "Alias": "name", + "Name": "name", + }, + { + "Alias": "appearsIn", + "Name": "appearsIn", + }, + ], + }, + { + "Alias": "appearsIn", + "Name": "appearsIn", + }, + ], + }, + { + "Alias": "appearsIn", + "Name": "appearsIn", + }, + ], + }], + "VariableDefinitions": [{ + "Type": { + "NamedType": "Episode", + "NonNull": true, + }, + "Used": false, + "Variable": "episode", + }], + }]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-basic-ast inline fragments and type conditions + query: data.test.p = x + modules: + - | + package test + + gql := `query HeroForEpisode($ep: Episode!) { + hero(episode: $ep) { + name + ... on Droid { + primaryFunction + } + ... on Human { + height + } + } + }` + + ast := {"Operations": [{ + "Name": "HeroForEpisode", + "Operation": "query", + "SelectionSet": [{ + "Alias": "hero", + "Arguments": [{ + "Name": "episode", + "Value": { + "Kind": 0, + "Raw": "ep", + }, + }], + "Name": "hero", + "SelectionSet": [ + { + "Alias": "name", + "Name": "name", + }, + { + "SelectionSet": [{ + "Alias": "primaryFunction", + "Name": "primaryFunction", + }], + "TypeCondition": "Droid", + }, + { + "SelectionSet": [{ + "Alias": "height", + "Name": "height", + }], + "TypeCondition": "Human", + }, + ], + }], + "VariableDefinitions": [{ + "Type": { + "NamedType": "Episode", + "NonNull": true, + }, + "Used": false, + "Variable": "ep", + }], + }]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-basic-ast meta fields and introspection + query: data.test.p = x + modules: + - | + package test + + gql := `{ + search(text: "an") { + __typename + ... on Human { + name + } + ... on Droid { + name + } + ... on Starship { + name + } + } + }` + + ast := {"Operations": [{ + "Name": "", + "Operation": "query", + "SelectionSet": [{ + "Alias": "search", + "Arguments": [{ + "Name": "text", + "Value": { + "Kind": 3, + "Raw": "an", + }, + }], + "Name": "search", + "SelectionSet": [ + { + "Alias": "__typename", + "Name": "__typename", + }, + { + "SelectionSet": [{ + "Alias": "name", + "Name": "name", + }], + "TypeCondition": "Human", + }, + { + "SelectionSet": [{ + "Alias": "name", + "Name": "name", + }], + "TypeCondition": "Droid", + }, + { + "SelectionSet": [{ + "Alias": "name", + "Name": "name", + }], + "TypeCondition": "Starship", + }, + ], + }], + }]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-is-valid.yaml b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-is-valid.yaml new file mode 100644 index 000000000000..787620d4731f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-is-valid.yaml @@ -0,0 +1,279 @@ +--- +cases: + - note: graphql_is_valid/success extending non-existent types + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type User { + id: ID! + } + extend type Product { + upc: String! + } + union _Entity = Product | User + extend type Query { + entity: _Entity + } + ` + + query := ` + { + entity { + ... on User { + id + } + } + } + ` + + p if { + graphql.is_valid(query, schema) + } + want_result: + - x: true + - note: graphql_is_valid/success validation rules are independent case 1 + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + + query := ` + query SomeOperation { + # Note: Not providing mandatory parameter: (myEnum: Locale!) + myAction { + id + } + } + ` + + # We use the unification style from semver's is_valid tests here: + p := x if { + x = graphql.is_valid(query, schema) + } + want_result: + - x: false + - note: graphql_is_valid/success validation rules are independent case 2 + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + + query := ` + # Note: there is default enum value in variables + query SomeOperation ($locale: Locale! = DE) { + myAction(myEnum: $locale) { + id + } + } + ` + + p if { + graphql.is_valid(query, schema) + } + want_result: + - x: true + - note: graphql_is_valid/success deprecating types + query: data.test.p = x + modules: + - | + package test + + schema := ` + type DeprecatedType { + deprecatedField: String @deprecated + newField(deprecatedArg: Int): Boolean + } + enum DeprecatedEnum { + ALPHA @deprecated + } + ` + + query := `` + + p if { + graphql.is_valid(query, schema) + } + want_result: + - x: true + - note: graphql_is_valid/success no unused variables + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Query { + bar: String! + } + ` + + query := ` + query Foo($flag: Boolean!) { + ...Bar + } + fragment Bar on Query { + bar @include(if: $flag) + } + ` + + p if { + graphql.is_valid(query, schema) + } + want_result: + - x: true + - note: graphql_is_valid/success - AST objects - Employee example + query: data.test.p = x + modules: + - | + package test + + schema_ast := graphql.parse_schema(` + type Employee { + id: String! + salary: Int! + } + + schema { + query: Query + } + + type Query { + employeeByID(id: String): Employee + } + `) + + query_ast := graphql.parse_query(` + query { employeeByID(id: "alice") { salary }} + `) + + p if { + graphql.is_valid(query_ast, schema_ast) + } + want_result: + - x: true + - note: graphql_is_valid/undefined argument + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Query { + bar: String! + } + type Mutation { + x: String! + } + ` + + query := `mutation{x(a:b)}` + + p if { + not graphql.is_valid(query, schema) + } + want_result: + - x: true + + - note: graphql_is_valid/undefined argument schema ast + query: data.test.p = x + modules: + - | + package test + + schema_ast := { + "Definitions": [ + { + "BuiltIn": false, + "Description": "", + "Fields": [ + { + "Description": "", + "Name": "bar", + "Type": { + "NamedType": "String", + "NonNull": true + } + } + ], + "Kind": "OBJECT", + "Name": "Query" + }, + { + "BuiltIn": false, + "Description": "", + "Fields": [ + { + "Description": "", + "Name": "x", + "Type": { + "NamedType": "String", + "NonNull": true + } + } + ], + "Kind": "OBJECT", + "Name": "Mutation" + } + ] + } + + query_ast := { + "Operations": [ + { + "Name": "", + "Operation": "mutation", + "SelectionSet": [ + { + "Alias": "x", + "Arguments": [ + { + "Name": "a", + "Value": { + "Kind": 7, + "Raw": "b" + } + } + ], + "Name": "x" + } + ] + } + ] + } + + p if { + not graphql.is_valid(query_ast, schema_ast) + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse-and-verify.yaml b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse-and-verify.yaml new file mode 100644 index 000000000000..71cecc8de560 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse-and-verify.yaml @@ -0,0 +1,197 @@ +--- +cases: + - note: graphql_parse_and_verify/success extending non-existent types + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type User { + id: ID! + } + extend type Product { + upc: String! + } + union _Entity = Product | User + extend type Query { + entity: _Entity + } + ` + + query := ` + { + entity { + ... on User { + id + } + } + } + ` + + q_ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "entity", "Name": "entity", "SelectionSet": [{"SelectionSet": [{"Alias": "id", "Name": "id"}], "TypeCondition": "User"}]}]}]} + + p if { + [valid, q_ast, _] = graphql.parse_and_verify(query, schema) + valid + } + want_result: + - x: true + - note: graphql_parse_and_verify/success validation rules are independent case 1 + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + + query := ` + query SomeOperation { + # Note: Not providing mandatory parameter: (myEnum: Locale!) + myAction { + id + } + } + ` + + p if { + [valid, q_ast, _] = graphql.parse_and_verify(query, schema) + not valid + } + want_result: + - x: true + - note: graphql_parse_and_verify/success validation rules are independent case 2 + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Query { + x: Int + } + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + + query := ` + query SomeOperation ($locale: Locale! = DE) { + myAction(myEnum: $locale) { + id + } + } + ` + + q_ast := {"Operations": [{"Name": "SomeOperation", "Operation": "query", "SelectionSet": [{"Alias": "myAction", "Arguments": [{"Name": "myEnum", "Value": {"Kind": 0, "Raw": "locale"}}], "Name": "myAction", "SelectionSet": [{"Alias": "id", "Name": "id"}]}], "VariableDefinitions": [{"DefaultValue": {"Kind": 7, "Raw": "DE"}, "Type": {"NamedType": "Locale", "NonNull": true}, "Used": false, "Variable": "locale"}]}]} + + p if { + [valid, q_ast, _] = graphql.parse_and_verify(query, schema) + valid + } + want_result: + - x: true + - note: graphql_parse_and_verify/success deprecating types + query: data.test.p = x + modules: + - | + package test + + schema := ` + type DeprecatedType { + deprecatedField: String @deprecated + newField(deprecatedArg: Int): Boolean + } + enum DeprecatedEnum { + ALPHA @deprecated + } + ` + + query := `` + + p if { + [valid, {}, _] = graphql.parse_and_verify(query, schema) + valid + } + want_result: + - x: true + - note: graphql_parse_and_verify/success no unused variables + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Query { + bar: String! + } + ` + + query := ` + query Foo($flag: Boolean!) { + ...Bar + } + fragment Bar on Query { + bar @include(if: $flag) + } + ` + + q_ast := {"Fragments": [{"Name": "Bar", "SelectionSet": [{"Alias": "bar", "Directives": [{"Arguments": [{"Name": "if", "Value": {"Kind": 0, "Raw": "flag"}}], "Location": "", "Name": "include"}], "Name": "bar"}], "TypeCondition": "Query"}], "Operations": [{"Name": "Foo", "Operation": "query", "SelectionSet": [{"Name": "Bar"}], "VariableDefinitions": [{"Type": {"NamedType": "Boolean", "NonNull": true}, "Used": false, "Variable": "flag"}]}]} + + p if { + [valid, q_ast, _] = graphql.parse_and_verify(query, schema) + valid + } + want_result: + - x: true + - note: graphql_parse_and_verify/success - AST objects - Employee example + query: data.test.p = x + modules: + - | + package test + + schema_ast := graphql.parse_schema(` + type Employee { + id: String! + salary: Int! + } + + schema { + query: Query + } + + type Query { + employeeByID(id: String): Employee + } + `) + + query_ast := graphql.parse_query(` + query { employeeByID(id: "alice") { salary }} + `) + + p if { + [valid, query_ast, schema_ast] = graphql.parse_and_verify(query_ast, schema_ast) + valid + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse-query.yaml b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse-query.yaml new file mode 100644 index 000000000000..52ff5d0a791b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse-query.yaml @@ -0,0 +1,509 @@ +--- +cases: + - note: graphql_parse_query/failure-unclosed paren + query: data.test.p = x + modules: + - | + package test + + p if { + graphql.parse_query(`{`) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_query: Expected Name, found in GraphQL string at location 1:2" + strict_error: true + - note: graphql_parse_query/failure-missing on in fragment + query: data.test.p = x + modules: + - | + package test + + gql := ` + { ...MissingOn } + fragment MissingOn Type + ` + + p if { + graphql.parse_query(gql) + } + want_error_code: eval_builtin_error + want_error: 'graphql.parse_query: Expected "on", found Name "Type" in GraphQL string at location 3:22' + strict_error: true + - note: graphql_parse_query/failure-missing name after alias + query: data.test.p = x + modules: + - | + package test + + gql := `{ field: {} }` + + p if { + graphql.parse_query(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_query: Expected Name, found { in GraphQL string at location 1:10" + strict_error: true + - note: graphql_parse_query/failure-not an operation + query: data.test.p = x + modules: + - | + package test + + gql := `notanoperation Foo { field }` + + p if { + graphql.parse_query(gql) + } + want_error_code: eval_builtin_error + want_error: 'graphql.parse_query: Unexpected Name "notanoperation" in GraphQL string at location 1:1' + strict_error: true + - note: graphql_parse_query/failure-a wild splat appears + query: data.test.p = x + modules: + - | + package test + + gql := `...` + + p if { + graphql.parse_query(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_query: Unexpected ... in GraphQL string at location 1:1" + strict_error: true + - note: graphql_parse_query/success-variables are allowed in args + query: data.test.p = x + modules: + - | + package test + + gql := `{ field(complex: { a: { b: [ $var ] } }) }` + + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "field", "Arguments": [{"Name": "complex", "Value": {"Children": [{"Name": "a", "Value": {"Children": [{"Name": "b", "Value": {"Children": [{"Name": "", "Value": {"Kind": 0, "Raw": "var"}}], "Kind": 8, "Raw": ""}}], "Kind": 9, "Raw": ""}}], "Kind": 9, "Raw": ""}}], "Name": "field"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/failure-variables are not allowed in default args + query: data.test.p = x + modules: + - | + package test + + gql := `query Foo($x: Complex = { a: { b: [ $var ] } }) { field }` + + p if { + graphql.parse_query(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_query: Unexpected $ in GraphQL string at location 1:37" + strict_error: true + - note: graphql_parse_query/success-variables can have directives + query: data.test.p = x + modules: + - | + package test + + gql := `query ($withDirective: String @first @second, $withoutDirective: String) { f }` + + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "f", "Name": "f"}], "VariableDefinitions": [{"Directives": [{"Location": "", "Name": "first"}, {"Location": "", "Name": "second"}], "Type": {"NamedType": "String", "NonNull": false}, "Used": false, "Variable": "withDirective"}, {"Type": {"NamedType": "String", "NonNull": false}, "Used": false, "Variable": "withoutDirective"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/failure-fragment can not be named 'on' + query: data.test.p = x + modules: + - | + package test + + gql := `fragment on on on { on }` + + p if { + graphql.parse_query(gql) + } + want_error_code: eval_builtin_error + want_error: 'graphql.parse_query: Unexpected Name "on" in GraphQL string at location 1:10' + strict_error: true + - note: graphql_parse_query/failure-fragment can not spread fragments called 'on' + query: data.test.p = x + modules: + - | + package test + + gql := `{ ...on }` + + p if { + graphql.parse_query(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_query: Expected Name, found } in GraphQL string at location 1:9" + strict_error: true + - note: graphql_parse_query/success-encoding multibyte characters are supported + query: data.test.p = x + modules: + - | + package test + + gql := ` + # This comment has a ਊ multi-byte character. + { field(arg: "Has a ਊ multi-byte character.") } + ` + + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "field", "Arguments": [{"Name": "arg", "Value": {"Kind": 3, "Raw": "Has a ਊ multi-byte character."}}], "Name": "field"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-keywords-allowed-where-names-are on + query: data.test.p = x + modules: + - | + package test + + gql := ` + query on { + ... a + ... on on { field } + } + fragment a on Type { + on(on: $on) + @on(on: on) + } + ` + + ast := {"Fragments": [{"Name": "a", "SelectionSet": [{"Alias": "on", "Arguments": [{"Name": "on", "Value": {"Kind": 0, "Raw": "on"}}], "Directives": [{"Arguments": [{"Name": "on", "Value": {"Kind": 7, "Raw": "on"}}], "Location": "", "Name": "on"}], "Name": "on"}], "TypeCondition": "Type"}], "Operations": [{"Name": "on", "Operation": "query", "SelectionSet": [{"Name": "a"}, {"SelectionSet": [{"Alias": "field", "Name": "field"}], "TypeCondition": "on"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-keywords-allowed-where-names-are subscription + query: data.test.p = x + modules: + - | + package test + + gql := ` + query subscription { + ... subscription + ... on subscription { field } + } + fragment subscription on Type { + subscription(subscription: $subscription) + @subscription(subscription: subscription) + } + ` + + ast := {"Fragments": [{"Name": "subscription", "SelectionSet": [{"Alias": "subscription", "Arguments": [{"Name": "subscription", "Value": {"Kind": 0, "Raw": "subscription"}}], "Directives": [{"Arguments": [{"Name": "subscription", "Value": {"Kind": 7, "Raw": "subscription"}}], "Location": "", "Name": "subscription"}], "Name": "subscription"}], "TypeCondition": "Type"}], "Operations": [{"Name": "subscription", "Operation": "query", "SelectionSet": [{"Name": "subscription"}, {"SelectionSet": [{"Alias": "field", "Name": "field"}], "TypeCondition": "subscription"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-keywords-allowed-where-names-are true + query: data.test.p = x + modules: + - | + package test + + gql := ` + query true { + ... true + ... on true { field } + } + fragment true on Type { + true(true: $true) + @true(true: true) + } + ` + + ast := {"Fragments": [{"Name": "true", "SelectionSet": [{"Alias": "true", "Arguments": [{"Name": "true", "Value": {"Kind": 0, "Raw": "true"}}], "Directives": [{"Arguments": [{"Name": "true", "Value": {"Kind": 5, "Raw": "true"}}], "Location": "", "Name": "true"}], "Name": "true"}], "TypeCondition": "Type"}], "Operations": [{"Name": "true", "Operation": "query", "SelectionSet": [{"Name": "true"}, {"SelectionSet": [{"Alias": "field", "Name": "field"}], "TypeCondition": "true"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-operations anonymous mutation + query: data.test.p = x + modules: + - | + package test + + gql := `mutation { mutationField }` + + ast := {"Operations": [{"Name": "", "Operation": "mutation", "SelectionSet": [{"Alias": "mutationField", "Name": "mutationField"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-operations named mutation + query: data.test.p = x + modules: + - | + package test + + gql := `mutation Foo { mutationField }` + + ast := {"Operations": [{"Name": "Foo", "Operation": "mutation", "SelectionSet": [{"Alias": "mutationField", "Name": "mutationField"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-operations anonymous subscription + query: data.test.p = x + modules: + - | + package test + + gql := `subscription { subscriptionField }` + + ast := {"Operations": [{"Name": "", "Operation": "subscription", "SelectionSet": [{"Alias": "subscriptionField", "Name": "subscriptionField"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-operations named subscription + query: data.test.p = x + modules: + - | + package test + + gql := `subscription Foo { subscriptionField }` + + ast := {"Operations": [{"Name": "Foo", "Operation": "subscription", "SelectionSet": [{"Alias": "subscriptionField", "Name": "subscriptionField"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-ast simple query + query: data.test.p = x + modules: + - | + package test + + gql := ` + { + node(id: 4) { + id, + name + } + } + ` + + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "node", "Arguments": [{"Name": "id", "Value": {"Kind": 1, "Raw": "4"}}], "Name": "node", "SelectionSet": [{"Alias": "id", "Name": "id"}, {"Alias": "name", "Name": "name"}]}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-ast nameless query with no variables + query: data.test.p = x + modules: + - | + package test + + gql := ` + query { + node { + id + } + } + ` + + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "node", "Name": "node", "SelectionSet": [{"Alias": "id", "Name": "id"}]}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-ast fragment defined variables + query: data.test.p = x + modules: + - | + package test + + gql := `fragment a($v: Boolean = false) on t { f(v: $v) }` + + ast := {"Fragments": [{"Name": "a", "SelectionSet": [{"Alias": "f", "Arguments": [{"Name": "v", "Value": {"Kind": 0, "Raw": "v"}}], "Name": "f"}], "TypeCondition": "t", "VariableDefinition": [{"DefaultValue": {"Kind": 5, "Raw": "false"}, "Type": {"NamedType": "Boolean", "NonNull": false}, "Used": false, "Variable": "v"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-values null + query: data.test.p = x + modules: + - | + package test + + gql := `{ f(id: null) }` + + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "f", "Arguments": [{"Name": "id", "Value": {"Kind": 6, "Raw": "null"}}], "Name": "f"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-values strings + query: data.test.p = x + modules: + - | + package test + + gql := `{ f(long: """long""", short: "short") }` + + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "f", "Arguments": [{"Name": "long", "Value": {"Kind": 4, "Raw": "long"}}, {"Name": "short", "Value": {"Kind": 3, "Raw": "short"}}], "Name": "f"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-values list + query: data.test.p = x + modules: + - | + package test + + gql := `{ f(id: [1,2]) }` + + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "f", "Arguments": [{"Name": "id", "Value": {"Children": [{"Name": "", "Value": {"Kind": 1, "Raw": "1"}}, {"Name": "", "Value": {"Kind": 1, "Raw": "2"}}], "Kind": 8, "Raw": ""}}], "Name": "f"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-types common types + query: data.test.p = x + modules: + - | + package test + + gql := `query ($string: String, $int: Int, $arr: [Arr], $notnull: [Arr!]!) { f }` + + ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "f", "Name": "f"}], "VariableDefinitions": [{"Type": {"NamedType": "String", "NonNull": false}, "Used": false, "Variable": "string"}, {"Type": {"NamedType": "Int", "NonNull": false}, "Used": false, "Variable": "int"}, {"Type": {"Elem": {"NamedType": "Arr", "NonNull": false}, "NamedType": "", "NonNull": false}, "Used": false, "Variable": "arr"}, {"Type": {"Elem": {"NamedType": "Arr", "NonNull": true}, "NamedType": "", "NonNull": true}, "Used": false, "Variable": "notnull"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/success-large-queries kitchen sink + query: data.test.p = x + modules: + - | + package test + + # Copyright (c) 2015-present, Facebook, Inc. + # + # This source code is licensed under the MIT license found in the + # LICENSE file in the root directory of this source tree. + gql := ` + query queryName($foo: ComplexType, $site: Site = MOBILE) { + whoever123is: node(id: [123, 456]) { + id , + ... on User @defer { + field2 { + id , + alias: field1(first:10, after:$foo,) @include(if: $foo) { + id, + ...frag + } + } + } + ... @skip(unless: $foo) { + id + } + ... { + id + } + } + } + mutation likeStory { + like(story: 123) @defer { + story { + id + } + } + } + subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) { + storyLikeSubscribe(input: $input) { + story { + likers { + count + } + likeSentence { + text + } + } + } + } + fragment frag on Friend { + foo(size: $size, bar: $b, obj: {key: "value", block: """ + block string uses \""" + """}) + } + { + unnamed(truthy: true, falsey: false, nullish: null), + query + } + ` + + ast := {"Fragments": [{"Name": "frag", "SelectionSet": [{"Alias": "foo", "Arguments": [{"Name": "size", "Value": {"Kind": 0, "Raw": "size"}}, {"Name": "bar", "Value": {"Kind": 0, "Raw": "b"}}, {"Name": "obj", "Value": {"Children": [{"Name": "key", "Value": {"Kind": 3, "Raw": "value"}}, {"Name": "block", "Value": {"Kind": 4, "Raw": "block string uses \"\"\""}}], "Kind": 9, "Raw": ""}}], "Name": "foo"}], "TypeCondition": "Friend"}], "Operations": [{"Name": "queryName", "Operation": "query", "SelectionSet": [{"Alias": "whoever123is", "Arguments": [{"Name": "id", "Value": {"Children": [{"Name": "", "Value": {"Kind": 1, "Raw": "123"}}, {"Name": "", "Value": {"Kind": 1, "Raw": "456"}}], "Kind": 8, "Raw": ""}}], "Name": "node", "SelectionSet": [{"Alias": "id", "Name": "id"}, {"Directives": [{"Location": "", "Name": "defer"}], "SelectionSet": [{"Alias": "field2", "Name": "field2", "SelectionSet": [{"Alias": "id", "Name": "id"}, {"Alias": "alias", "Arguments": [{"Name": "first", "Value": {"Kind": 1, "Raw": "10"}}, {"Name": "after", "Value": {"Kind": 0, "Raw": "foo"}}], "Directives": [{"Arguments": [{"Name": "if", "Value": {"Kind": 0, "Raw": "foo"}}], "Location": "", "Name": "include"}], "Name": "field1", "SelectionSet": [{"Alias": "id", "Name": "id"}, {"Name": "frag"}]}]}], "TypeCondition": "User"}, {"Directives": [{"Arguments": [{"Name": "unless", "Value": {"Kind": 0, "Raw": "foo"}}], "Location": "", "Name": "skip"}], "SelectionSet": [{"Alias": "id", "Name": "id"}], "TypeCondition": ""}, {"SelectionSet": [{"Alias": "id", "Name": "id"}], "TypeCondition": ""}]}], "VariableDefinitions": [{"Type": {"NamedType": "ComplexType", "NonNull": false}, "Used": false, "Variable": "foo"}, {"DefaultValue": {"Kind": 7, "Raw": "MOBILE"}, "Type": {"NamedType": "Site", "NonNull": false}, "Used": false, "Variable": "site"}]}, {"Name": "likeStory", "Operation": "mutation", "SelectionSet": [{"Alias": "like", "Arguments": [{"Name": "story", "Value": {"Kind": 1, "Raw": "123"}}], "Directives": [{"Location": "", "Name": "defer"}], "Name": "like", "SelectionSet": [{"Alias": "story", "Name": "story", "SelectionSet": [{"Alias": "id", "Name": "id"}]}]}]}, {"Name": "StoryLikeSubscription", "Operation": "subscription", "SelectionSet": [{"Alias": "storyLikeSubscribe", "Arguments": [{"Name": "input", "Value": {"Kind": 0, "Raw": "input"}}], "Name": "storyLikeSubscribe", "SelectionSet": [{"Alias": "story", "Name": "story", "SelectionSet": [{"Alias": "likers", "Name": "likers", "SelectionSet": [{"Alias": "count", "Name": "count"}]}, {"Alias": "likeSentence", "Name": "likeSentence", "SelectionSet": [{"Alias": "text", "Name": "text"}]}]}]}], "VariableDefinitions": [{"Type": {"NamedType": "StoryLikeSubscribeInput", "NonNull": false}, "Used": false, "Variable": "input"}]}, {"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "unnamed", "Arguments": [{"Name": "truthy", "Value": {"Kind": 5, "Raw": "true"}}, {"Name": "falsey", "Value": {"Kind": 5, "Raw": "false"}}, {"Name": "nullish", "Value": {"Kind": 6, "Raw": "null"}}], "Name": "unnamed"}, {"Alias": "query", "Name": "query"}]}]} + + p if { + graphql.parse_query(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_query/failure-fuzzer 01 + query: data.test.p = x + modules: + - | + package test + + gql := `{__typename{...}}` + + p if { + graphql.parse_query(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_query: Expected {, found } in GraphQL string at location 1:16" + strict_error: true + - note: graphql_parse_query/failure-fuzzer 02 + query: data.test.p = x + modules: + - | + package test + + gql := `{...{__typename{...{}}}}` + + p if { + graphql.parse_query(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_query: expected at least one definition, found } in GraphQL string at location 1:21" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse-schema.yaml b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse-schema.yaml new file mode 100644 index 000000000000..05f75783e469 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse-schema.yaml @@ -0,0 +1,744 @@ +--- +cases: + - note: graphql_parse_schema/success-object-types simple + query: data.test.p = x + modules: + - | + package test + + gql := ` + type Hello { + world: String + } + ` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-object-types with description + query: data.test.p = x + modules: + - | + package test + + gql := ` + "Description" + type Hello { + world: String + } + ` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "Description", "Fields": [{"Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-object-types with block description + query: data.test.p = x + modules: + - | + package test + + gql := ` + """ + Description + """ + type Hello { + world: String + } + type Query { + hello: Hello + } + ` + + ast := {"Definitions":[{"BuiltIn":false,"Description":"Description","Fields":[{"Description":"","Name":"world","Type":{"NamedType":"String","NonNull":false}}],"Kind":"OBJECT","Name":"Hello"},{"BuiltIn":false,"Description":"","Fields":[{"Description":"","Name":"hello","Type":{"NamedType":"Hello","NonNull":false}}],"Kind":"OBJECT","Name":"Query"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-object-types with field arg + query: data.test.p = x + modules: + - | + package test + + gql := ` + type Hello { + world(flag: Boolean): String + } + ` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Arguments": [{"Description": "", "Name": "flag", "Type": {"NamedType": "Boolean", "NonNull": false}}], "Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-object-types with field arg and default value + query: data.test.p = x + modules: + - | + package test + + gql := ` + type Hello { + world(flag: Boolean = true): String + } + ` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Arguments": [{"DefaultValue": {"Kind": 5, "Raw": "true"}, "Description": "", "Name": "flag", "Type": {"NamedType": "Boolean", "NonNull": false}}], "Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-object-types with field list arg + query: data.test.p = x + modules: + - | + package test + + gql := ` + type Hello { + world(things: [String]): String + } + ` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Arguments": [{"Description": "", "Name": "things", "Type": {"Elem": {"NamedType": "String", "NonNull": false}, "NamedType": "", "NonNull": false}}], "Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-object-types with two args + query: data.test.p = x + modules: + - | + package test + + gql := ` + type Hello { + world(argOne: Boolean, argTwo: Int): String + } + ` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Arguments": [{"Description": "", "Name": "argOne", "Type": {"NamedType": "Boolean", "NonNull": false}}, {"Description": "", "Name": "argTwo", "Type": {"NamedType": "Int", "NonNull": false}}], "Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/failure-object-types must define one or more fields + query: data.test.p = x + modules: + - | + package test + + gql := `type Hello {}` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_schema: expected at least one definition, found } in GraphQL string at location 1:13" + strict_error: true + - note: graphql_parse_schema/success-type-extensions Object extension + query: data.test.p = x + modules: + - | + package test + + gql := ` + extend type Hello { + world: String + } + ` + + ast := {"Extensions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-type-extensions without any fields + query: data.test.p = x + modules: + - | + package test + + gql := `extend type Hello implements Greeting` + + ast := {"Extensions": [{"BuiltIn": false, "Description": "", "Interfaces": ["Greeting"], "Kind": "OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-type-extensions without fields twice + query: data.test.p = x + modules: + - | + package test + + gql := ` + extend type Hello implements Greeting + extend type Hello implements SecondGreeting + ` + + ast := {"Extensions": [{"BuiltIn": false, "Description": "", "Interfaces": ["Greeting"], "Kind": "OBJECT", "Name": "Hello"}, {"BuiltIn": false, "Description": "", "Interfaces": ["SecondGreeting"], "Kind": "OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/failure-type-extensions without anything errors + query: data.test.p = x + modules: + - | + package test + + gql := `extend type Hello` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_schema: Unexpected in GraphQL string at location 1:18" + strict_error: true + - note: graphql_parse_schema/failure-type-extensions can have descriptions + query: data.test.p = x + modules: + - | + package test + + gql := ` + "Description" + extend type Hello { + world: String + } + ` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: 'graphql.parse_schema: Unexpected String "Description" in GraphQL string at location 2:4' + strict_error: true + - note: graphql_parse_schema/failure-type-extensions can not have descriptions on types + query: data.test.p = x + modules: + - | + package test + + gql := ` + extend "Description" type Hello { + world: String + } + ` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: 'graphql.parse_schema: Unexpected String "Description" in GraphQL string at location 2:11' + strict_error: true + - note: graphql_parse_schema/success-type-extensions all can have directives + query: data.test.p = x + modules: + - | + package test + + gql := ` + extend scalar Foo @deprecated + extend type Foo @deprecated + extend interface Foo @deprecated + extend union Foo @deprecated + extend enum Foo @deprecated + extend input Foo @deprecated + ` + + ast := {"Extensions": [{"BuiltIn": false, "Description": "", "Directives": [{"Location": "", "Name": "deprecated"}], "Kind": "SCALAR", "Name": "Foo"}, {"BuiltIn": false, "Description": "", "Directives": [{"Location": "", "Name": "deprecated"}], "Kind": "OBJECT", "Name": "Foo"}, {"BuiltIn": false, "Description": "", "Directives": [{"Location": "", "Name": "deprecated"}], "Kind": "INTERFACE", "Name": "Foo"}, {"BuiltIn": false, "Description": "", "Directives": [{"Location": "", "Name": "deprecated"}], "Kind": "UNION", "Name": "Foo"}, {"BuiltIn": false, "Description": "", "Directives": [{"Location": "", "Name": "deprecated"}], "Kind": "ENUM", "Name": "Foo"}, {"BuiltIn": false, "Description": "", "Directives": [{"Location": "", "Name": "deprecated"}], "Kind": "INPUT_OBJECT", "Name": "Foo"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-schema-definition simple + query: data.test.p = x + modules: + - | + package test + + gql := ` + schema { + query: Query + } + ` + + ast := {"Schema": [{"Description": "", "OperationTypes": [{"Operation": "query", "Type": "Query"}]}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-schema-extensions simple + query: data.test.p = x + modules: + - | + package test + + gql := ` + extend schema { + mutation: Mutation + } + ` + + ast := {"SchemaExtension": [{"Description": "", "OperationTypes": [{"Operation": "mutation", "Type": "Mutation"}]}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-schema-extensions directive only + query: data.test.p = x + modules: + - | + package test + + gql := `extend schema @directive` + + ast := {"SchemaExtension": [{"Description": "", "Directives": [{"Location": "", "Name": "directive"}]}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/failure-schema-extensions without anything errors + query: data.test.p = x + modules: + - | + package test + + gql := `extend schema` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_schema: Unexpected in GraphQL string at location 1:14" + strict_error: true + - note: graphql_parse_schema/success-inheritance single + query: data.test.p = x + modules: + - | + package test + + gql := `type Hello implements World { field: String }` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "field", "Type": {"NamedType": "String", "NonNull": false}}], "Interfaces": ["World"], "Kind": "OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-inheritance multi + query: data.test.p = x + modules: + - | + package test + + gql := `type Hello implements Wo & rld { field: String }` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "field", "Type": {"NamedType": "String", "NonNull": false}}], "Interfaces": ["Wo", "rld"], "Kind": "OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-inheritance multi with leading amp + query: data.test.p = x + modules: + - | + package test + + gql := `type Hello implements & Wo & rld { field: String }` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "field", "Type": {"NamedType": "String", "NonNull": false}}], "Interfaces": ["Wo", "rld"], "Kind": "OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-enums single value + query: data.test.p = x + modules: + - | + package test + + gql := `enum Hello { WORLD }` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "EnumValues": [{"Description": "", "Name": "WORLD"}], "Kind": "ENUM", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-enums double value + query: data.test.p = x + modules: + - | + package test + + gql := `enum Hello { WO, RLD }` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "EnumValues": [{"Description": "", "Name": "WO"}, {"Description": "", "Name": "RLD"}], "Kind": "ENUM", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) + } + want_result: + - x: true + - note: graphql_parse_schema/failure-enums must define one or more unique enum values + query: data.test.p = x + modules: + - | + package test + + gql := `enum Hello {}` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_schema: expected at least one definition, found } in GraphQL string at location 1:13" + strict_error: true + - note: graphql_parse_schema/success-interface simple + query: data.test.p = x + modules: + - | + package test + + gql := ` + interface Hello { + world: String + } + ` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "INTERFACE", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/failure-interface must define one or more fields + query: data.test.p = x + modules: + - | + package test + + gql := `interface Hello {}` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_schema: expected at least one definition, found } in GraphQL string at location 1:18" + strict_error: true + - note: graphql_parse_schema/success-interface may define intermediate interfaces + query: data.test.p = x + modules: + - | + package test + + gql := ` + interface IA { + id: ID! + } + interface IIA implements IA { + id: ID! + } + type A implements IIA { + id: ID! + } + ` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "id", "Type": {"NamedType": "ID", "NonNull": true}}], "Kind": "INTERFACE", "Name": "IA"}, {"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "id", "Type": {"NamedType": "ID", "NonNull": true}}], "Interfaces": ["IA"], "Kind": "INTERFACE", "Name": "IIA"}, {"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "id", "Type": {"NamedType": "ID", "NonNull": true}}], "Interfaces": ["IIA"], "Kind": "OBJECT", "Name": "A"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-unions simple + query: data.test.p = x + modules: + - | + package test + + gql := `union Hello = World` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Kind": "UNION", "Name": "Hello", "Types": ["World"]}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-unions with two types + query: data.test.p = x + modules: + - | + package test + + gql := `union Hello = Wo | Rld` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Kind": "UNION", "Name": "Hello", "Types": ["Wo", "Rld"]}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-unions with leading pipe + query: data.test.p = x + modules: + - | + package test + + gql := `union Hello = | Wo | Rld` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Kind": "UNION", "Name": "Hello", "Types": ["Wo", "Rld"]}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/failure-unions cant be empty + query: data.test.p = x + modules: + - | + package test + + gql := `union Hello = || Wo | Rld` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_schema: Expected Name, found | in GraphQL string at location 1:16" + strict_error: true + - note: graphql_parse_schema/failure-unions cant double pipe + query: data.test.p = x + modules: + - | + package test + + gql := `union Hello = Wo || Rld` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_schema: Expected Name, found | in GraphQL string at location 1:19" + strict_error: true + - note: graphql_parse_schema/failure-unions cant have trailing pipe + query: data.test.p = x + modules: + - | + package test + + gql := `union Hello = | Wo | Rld |` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_schema: Expected Name, found in GraphQL string at location 1:27" + strict_error: true + - note: graphql_parse_schema/success-scalar simple + query: data.test.p = x + modules: + - | + package test + + gql := `scalar Hello` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Kind": "SCALAR", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-input-object simple + query: data.test.p = x + modules: + - | + package test + + gql := ` + input Hello { + world: String + } + ` + + ast := {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "INPUT_OBJECT", "Name": "Hello"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/failure-input-object can not have args + query: data.test.p = x + modules: + - | + package test + + gql := ` + input Hello { + world(foo: Int): String + } + ` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_schema: Expected :, found ( in GraphQL string at location 3:10" + strict_error: true + - note: graphql_parse_schema/failure-input-object must define one or more input fields + query: data.test.p = x + modules: + - | + package test + + gql := `input Hello {}` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_schema: expected at least one definition, found } in GraphQL string at location 1:14" + strict_error: true + - note: graphql_parse_schema/success-directives simple + query: data.test.p = x + modules: + - | + package test + + gql := `directive @foo on FIELD` + + ast := {"Directives": [{"Description": "", "IsRepeatable": false, "Locations": ["FIELD"], "Name": "foo"}]} + + p if { + graphql.parse_schema(gql) + } + want_result: + - x: true + - note: graphql_parse_schema/success-directives executable + query: data.test.p = x + modules: + - | + package test + + gql := ` + directive @onQuery on QUERY + directive @onMutation on MUTATION + directive @onSubscription on SUBSCRIPTION + directive @onField on FIELD + directive @onFragmentDefinition on FRAGMENT_DEFINITION + directive @onFragmentSpread on FRAGMENT_SPREAD + directive @onInlineFragment on INLINE_FRAGMENT + directive @onVariableDefinition on VARIABLE_DEFINITION + ` + + ast := {"Directives": [{"Description": "", "IsRepeatable": false, "Locations": ["QUERY"], "Name": "onQuery"}, {"Description": "", "IsRepeatable": false, "Locations": ["MUTATION"], "Name": "onMutation"}, {"Description": "", "IsRepeatable": false, "Locations": ["SUBSCRIPTION"], "Name": "onSubscription"}, {"Description": "", "IsRepeatable": false, "Locations": ["FIELD"], "Name": "onField"}, {"Description": "", "IsRepeatable": false, "Locations": ["FRAGMENT_DEFINITION"], "Name": "onFragmentDefinition"}, {"Description": "", "IsRepeatable": false, "Locations": ["FRAGMENT_SPREAD"], "Name": "onFragmentSpread"}, {"Description": "", "IsRepeatable": false, "Locations": ["INLINE_FRAGMENT"], "Name": "onInlineFragment"}, {"Description": "", "IsRepeatable": false, "Locations": ["VARIABLE_DEFINITION"], "Name": "onVariableDefinition"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/success-directives repeatable + query: data.test.p = x + modules: + - | + package test + + gql := `directive @foo repeatable on FIELD` + + ast := {"Directives": [{"Description": "", "IsRepeatable": true, "Locations": ["FIELD"], "Name": "foo"}]} + + p if { + graphql.parse_schema(gql) == ast + } + want_result: + - x: true + - note: graphql_parse_schema/failure-directives invalid location + query: data.test.p = x + modules: + - | + package test + + gql := `directive @foo on FIELD | INCORRECT_LOCATION` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: 'graphql.parse_schema: Unexpected Name "INCORRECT_LOCATION" in GraphQL string at location 1:27' + strict_error: true + - note: graphql_parse_schema/failure-fuzzer 1 + query: data.test.p = x + modules: + - | + package test + + gql := `type o{d(g:[` + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_schema: Expected Name, found in GraphQL string at location 1:13" + strict_error: true + - note: graphql_parse_schema/failure-fuzzer 2 + query: data.test.p = x + modules: + - | + package test + + gql := "\"\"\"\r" + + p if { + graphql.parse_schema(gql) + } + want_error_code: eval_builtin_error + want_error: "graphql.parse_schema: Unexpected in GraphQL string at location 2:1" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse.yaml b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse.yaml new file mode 100644 index 000000000000..9647366e2492 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-parse.yaml @@ -0,0 +1,192 @@ +--- +cases: + - note: graphql_parse/success extending non-existent types + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type User { + id: ID! + } + extend type Product { + upc: String! + } + union _Entity = Product | User + extend type Query { + entity: _Entity + } + ` + + query := ` + { + entity { + ... on User { + id + } + } + } + ` + + q_ast := {"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "entity", "Name": "entity", "SelectionSet": [{"SelectionSet": [{"Alias": "id", "Name": "id"}], "TypeCondition": "User"}]}]}]} + + p if { + [q_ast, _] = graphql.parse(query, schema) + } + want_result: + - x: true + - note: graphql_parse/failure validation rules are independent case 1 + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + + query := ` + query SomeOperation { + # Note: Not providing mandatory parameter: (myEnum: Locale!) + myAction { + id + } + } + ` + + p if { + graphql.parse(query, schema) + } + want_error_code: eval_builtin_error + want_error: 'graphql.parse: Field "myAction" argument "myEnum" of type "Locale!" is required, but it was not provided in GraphQL string at location 4:5' + strict_error: true + - note: graphql_parse/success validation rules are independent case 2 + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Query { + x: Int + } + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + + query := ` + query SomeOperation ($locale: Locale! = DE) { + myAction(myEnum: $locale) { + id + } + } + ` + + q_ast := {"Operations": [{"Name": "SomeOperation", "Operation": "query", "SelectionSet": [{"Alias": "myAction", "Arguments": [{"Name": "myEnum", "Value": {"Kind": 0, "Raw": "locale"}}], "Name": "myAction", "SelectionSet": [{"Alias": "id", "Name": "id"}]}], "VariableDefinitions": [{"DefaultValue": {"Kind": 7, "Raw": "DE"}, "Type": {"NamedType": "Locale", "NonNull": true}, "Used": false, "Variable": "locale"}]}]} + + p if { + [q_ast, _] = graphql.parse(query, schema) + } + want_result: + - x: true + - note: graphql_parse/success deprecating types + query: data.test.p = x + modules: + - | + package test + + schema := ` + type DeprecatedType { + deprecatedField: String @deprecated + newField(deprecatedArg: Int): Boolean + } + enum DeprecatedEnum { + ALPHA @deprecated + } + ` + + query := `` + + p if { + [{}, _] = graphql.parse(query, schema) + } + want_result: + - x: true + - note: graphql_parse/success no unused variables + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Query { + bar: String! + } + ` + + query := ` + query Foo($flag: Boolean!) { + ...Bar + } + fragment Bar on Query { + bar @include(if: $flag) + } + ` + + q_ast := {"Fragments": [{"Name": "Bar", "SelectionSet": [{"Alias": "bar", "Directives": [{"Arguments": [{"Name": "if", "Value": {"Kind": 0, "Raw": "flag"}}], "Location": "", "Name": "include"}], "Name": "bar"}], "TypeCondition": "Query"}], "Operations": [{"Name": "Foo", "Operation": "query", "SelectionSet": [{"Name": "Bar"}], "VariableDefinitions": [{"Type": {"NamedType": "Boolean", "NonNull": true}, "Used": false, "Variable": "flag"}]}]} + + p if { + [q_ast, _] = graphql.parse(query, schema) + } + want_result: + - x: true + - note: graphql_parse/success - AST objects - Employee example + query: data.test.p = x + modules: + - | + package test + + schema_ast := graphql.parse_schema(` + type Employee { + id: String! + salary: Int! + } + + schema { + query: Query + } + + type Query { + employeeByID(id: String): Employee + } + `) + + query_ast := graphql.parse_query(` + query { employeeByID(id: "alice") { salary }} + `) + + p if { + [query_ast, schema_ast] = graphql.parse(query_ast, schema_ast) + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-schema-is-valid.yaml b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-schema-is-valid.yaml new file mode 100644 index 000000000000..c074382aad12 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/graphql/test-graphql-schema-is-valid.yaml @@ -0,0 +1,838 @@ +--- +cases: + - note: graphql_schema_is_valid/success extending non-existent types + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type User { + id: ID! + } + extend type Product { + upc: String! + } + union _Entity = Product | User + extend type Query { + entity: _Entity + } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success validation rules are independent case 1 and 2 + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type Query { + myAction(myEnum: Locale!): SomeResult! + } + type SomeResult { + id: String + } + enum Locale { + EN + LT + DE + } + ` + + # We use the unification style from semver's is_valid tests here: + p := x if { + x = graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success deprecating types + query: data.test.p = x + modules: + - | + package test + + schema := ` + type DeprecatedType { + deprecatedField: String @deprecated + newField(deprecatedArg: Int): Boolean + } + enum DeprecatedEnum { + ALPHA @deprecated + } + ` + + query := `` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success no unused variables + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Query { + bar: String! + } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success - AST objects - Employee example + query: data.test.p = x + modules: + - | + package test + + schema_ast := graphql.parse_schema(` + type Employee { + id: String! + salary: Int! + } + + schema { + query: Query + } + + type Query { + employeeByID(id: String): Employee + } + `) + + p if { + graphql.schema_is_valid(schema_ast) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success-object-extensions simple + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Hello { + world: String + } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success-object-extensions with description + query: data.test.p = x + modules: + - | + package test + + schema := ` + "Description" + type Hello { + world: String + } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success-object-extensions with block description + query: data.test.p = x + modules: + - | + package test + + schema := ` + """ + Description + """ + # Even with comments between them + type Hello { + world: String + } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success-object-extensions with field arg + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Hello { + world(flag: Boolean): String + } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success-object-extensions with field arg and default value + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Hello { + world(flag: Boolean = true): String + } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success-object-extensions with field list arg + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Hello { + world(things: [String]): String + } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success-object-extensions with two args + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Hello { + world(argOne: Boolean, argTwo: Int): String + } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/failure-object-extensions must define one or more fields + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Hello {} + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-type-extensions object extension + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type Hello { + world: String + } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success-type-extensions without any fields + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type Hello implements Greeting + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-type-extensions without fields twice + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type Hello implements Greeting + extend type Hello implements SecondGreeting + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/failure-type-extensions without anything errors + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend type Hello + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/failure-type-extensions can have descriptions + query: data.test.p = x + modules: + - | + package test + + schema := ` + "Description" + extend type Hello { + world: String + } + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/failure-type-extensions can not have descriptions on types + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend "Description" type Hello { + world: String + } + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-type-extensions all can have directives + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend scalar Foo @deprecated + extend type Foo @deprecated + extend interface Foo @deprecated + extend union Foo @deprecated + extend enum Foo @deprecated + extend input Foo @deprecated + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-schema-definition simple + query: data.test.p = x + modules: + - | + package test + + schema := ` + schema { + query: Query + } + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-schema-extensions simple + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend schema { + mutation: Mutation + } + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-schema-extensions directive only + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Query { + x: Int + } + directive @directive(a: String = "b") on SCHEMA + extend schema @directive + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/failure-schema-extensions without anything errors + query: data.test.p = x + modules: + - | + package test + + schema := ` + extend schema + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/failure-inheritance single + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Hello implements World { field: String } + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/failure-inheritance multi + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Hello implements Wo & rld { field: String } + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-inheritance multi with leading amp + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Hello implements & Wo & rld { field: String } + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-enums single value + query: data.test.p = x + modules: + - | + package test + + schema := ` + enum Hello { WORLD } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success-enums double value + query: data.test.p = x + modules: + - | + package test + + schema := ` + enum Hello { WO, RLD } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/failure-enums must define one or more unique enum values + query: data.test.p = x + modules: + - | + package test + + schema := ` + enum Hello {} + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-interface simple + query: data.test.p = x + modules: + - | + package test + + schema := ` + interface Hello { + world: String + } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/failure-interface must define one or more fields + query: data.test.p = x + modules: + - | + package test + + schema := ` + interface Hello {} + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/failure-interface may define intermediate interfaces + query: data.test.p = x + modules: + - | + package test + + schema := ` + interface IA { + id: ID! + } + interface IIA implements IA { + id: ID! + } + type A implements IIA { + id: ID! + } + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-unions simple + query: data.test.p = x + modules: + - | + package test + + schema := ` + union Hello = World + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-unions with two types + query: data.test.p = x + modules: + - | + package test + + schema := ` + union Hello = Wo | Rld + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-unions with leading pipe + query: data.test.p = x + modules: + - | + package test + + schema := ` + union Hello = | Wo | Rld + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/failure-unions cant be empty + query: data.test.p = x + modules: + - | + package test + + schema := ` + union Hello = || Wo | Rld + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/failure-unions cant double pipe + query: data.test.p = x + modules: + - | + package test + + schema := ` + union Hello = Wo || Rld + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/failure-unions cant have trailing pipe + query: data.test.p = x + modules: + - | + package test + + schema := ` + union Hello = | Wo | Rld | + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-scalar simple + query: data.test.p = x + modules: + - | + package test + + schema := ` + scalar Hello + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success-input-objects simple + query: data.test.p = x + modules: + - | + package test + + schema := ` + input Hello { + world: String + } + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/failure-input-objects can not have args + query: data.test.p = x + modules: + - | + package test + + schema := ` + input Hello { + world(foo: Int): String + } + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/failure-input-objects must define one or more input fields + query: data.test.p = x + modules: + - | + package test + + schema := ` + input Hello {} + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/success-directives simple + query: data.test.p = x + modules: + - | + package test + + schema := ` + directive @foo on FIELD + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success-directives executable + query: data.test.p = x + modules: + - | + package test + + schema := ` + directive @onQuery on QUERY + directive @onMutation on MUTATION + directive @onSubscription on SUBSCRIPTION + directive @onField on FIELD + directive @onFragmentDefinition on FRAGMENT_DEFINITION + directive @onFragmentSpread on FRAGMENT_SPREAD + directive @onInlineFragment on INLINE_FRAGMENT + directive @onVariableDefinition on VARIABLE_DEFINITION + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/success-directives repeatable + query: data.test.p = x + modules: + - | + package test + + schema := ` + directive @foo repeatable on FIELD + ` + + p if { + graphql.schema_is_valid(schema) + } + want_result: + - x: true + - note: graphql_schema_is_valid/failure-directives invalid location + query: data.test.p = x + modules: + - | + package test + + schema := ` + directive @foo on FIELD | INCORRECT_LOCATION + ` + + p := x if { + x := graphql.schema_is_valid(schema) + } + want_result: + - x: false + - note: graphql_schema_is_valid/schema failure with object in input object + query: data.test.p = x + modules: + - | + package test + + schema := ` + type Mutation { + createPupil(pupil: PupilInput!): Pupil + } + type Query { + pupil(firstname: String): Pupil + } + type Pupil { + friends: [Pupil!]! + } + input PupilInput { + friends: [Pupil] + } + schema { + query: Query + mutation: Mutation + }` + + p := x if { + x := graphql.schema_is_valid(graphql.parse_schema(schema)) + } + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/helloworld/test-helloworld-1.yaml b/third_party/opa/v1/test/cases/testdata/v1/helloworld/test-helloworld-1.yaml new file mode 100644 index 000000000000..fa878e19dc34 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/helloworld/test-helloworld-1.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: helloworld/test_case + query: data.test.p = x + modules: + - | + package test + + p := 7 if { + data.foo == q + } + + q := input.baz + data: + foo: bar + input: + baz: bar + want_result: + - x: 7 + - note: helloworld/another_test_for_builtin_error + query: 1 / 0 + want_error_code: eval_builtin_error + want_error: "div: divide by zero" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/hexbuiltins/test-hexbuiltins-0939.yaml b/third_party/opa/v1/test/cases/testdata/v1/hexbuiltins/test-hexbuiltins-0939.yaml new file mode 100644 index 000000000000..e3ca5f4ca838 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/hexbuiltins/test-hexbuiltins-0939.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: hexbuiltins/hex_encode with string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + hex.encode("lorem ipsum", x) + } + want_result: + - x: 6c6f72656d20697073756d diff --git a/third_party/opa/v1/test/cases/testdata/v1/hexbuiltins/test-hexbuiltins-0940.yaml b/third_party/opa/v1/test/cases/testdata/v1/hexbuiltins/test-hexbuiltins-0940.yaml new file mode 100644 index 000000000000..f302e4292da8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/hexbuiltins/test-hexbuiltins-0940.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: hexbuiltins/hex_decode with string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + hex.decode("6c6f72656d20697073756d", x) + } + want_result: + - x: lorem ipsum diff --git a/third_party/opa/v1/test/cases/testdata/v1/hexbuiltins/test-hexbuiltins-0941.yaml b/third_party/opa/v1/test/cases/testdata/v1/hexbuiltins/test-hexbuiltins-0941.yaml new file mode 100644 index 000000000000..10f850b8311c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/hexbuiltins/test-hexbuiltins-0941.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: hexbuiltins/hex_decode with invalid hex encoded string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + hex.decode("fghijkl", x) + } + want_error_code: eval_builtin_error + want_error: "invalid byte: U+0067 'g'" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/indexing/array-any.yaml b/third_party/opa/v1/test/cases/testdata/v1/indexing/array-any.yaml new file mode 100644 index 000000000000..e2314b777e50 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/indexing/array-any.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: indexing on any and arrays + query: data.indexing.p = x + modules: + - | + package indexing + + f(val) if { + [_, _] = val + } + + p if { + f([1, ["foo", "bar"]]) + } + data: {} + input_term: "{}" + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0758.yaml b/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0758.yaml new file mode 100644 index 000000000000..06ab694a5748 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0758.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: indirectreferences/array + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = [1, 2, 3] + __local0__[x] + } + data: {} + want_result: + - x: + - 0 + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0759.yaml b/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0759.yaml new file mode 100644 index 000000000000..c2f16f3fbcb2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0759.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: indirectreferences/call + query: data.generated.p = x + modules: + - | + package generated + + p if { + split("foo.bar", ".", __local0__) + __local0__[0] = "foo" + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0760.yaml b/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0760.yaml new file mode 100644 index 000000000000..862d4b978b73 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0760.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: indirectreferences/multiple call + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + split("foo.bar:qux", ".", __local0__) + __local2__ = __local0__[_] + split(__local2__, ":", __local1__) + __local1__[i] = x + } + data: {} + want_result: + - x: + - bar + - foo + - qux + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0761.yaml b/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0761.yaml new file mode 100644 index 000000000000..5246a106ace0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0761.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: indirectreferences/user call + query: data.generated.p = x + modules: + - | + package generated + + fn(__local0__) := [__local0__] + + p contains x if { + data.generated.fn(1, __local1__) + x = __local1__[0] + } + data: {} + want_result: + - x: + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0762.yaml b/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0762.yaml new file mode 100644 index 000000000000..f0354176e83a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/indirectreferences/test-indirectreferences-0762.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: indirectreferences/user call in comprehension + query: data.generated.p = x + modules: + - | + package generated + + fn(__local0__) := [__local0__] + + p contains x if { + __local2__ = [y | data.generated.fn(1, __local1__); y = __local1__] + x = __local2__[_][_] + } + data: {} + want_result: + - x: + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0977.yaml b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0977.yaml new file mode 100644 index 000000000000..7fa3f70a3601 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0977.yaml @@ -0,0 +1,65 @@ +--- +cases: + - note: inputvalues/loopback + query: data.z.loopback = x + modules: + - | + package z + + p if { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q contains x if { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r contains x if { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s if { + input.req3.a.b.x[0] = 1 + } + + t if { + input.req4.a.b.x[0] = 1 + } + + u contains x if { + input.req3.a.b[_] = x + x > 1 + } + + w := [[1, 2], [3, 4]] + + gt1 if { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] := y if { + data.numbers[_] = x + to_number(x, y) + } + + loopback := __local0__ if { + true + __local0__ = input + } + + sets if { + input.foo[{1}][1] = 1 + } + data: {} + input_term: '{"foo": 1}' + want_result: + - x: + foo: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0978.yaml b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0978.yaml new file mode 100644 index 000000000000..48b16e15ac54 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0978.yaml @@ -0,0 +1,68 @@ +--- +cases: + - note: inputvalues/loopback undefined + query: data.z.loopback = x + modules: + - | + package z + + p if { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q contains x if { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r contains x if { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s if { + input.req3.a.b.x[0] = 1 + } + + t if { + input.req4.a.b.x[0] = 1 + } + + u contains x if { + input.req3.a.b[_] = x + x > 1 + } + + w := [[1, 2], [3, 4]] + + gt1 if { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] := y if { + data.numbers[_] = x + to_number(x, y) + } + + loopback := __local0__ if { + true + __local0__ = input + } + + sets if { + input.foo[{1}][1] = 1 + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = input + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0979.yaml b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0979.yaml new file mode 100644 index 000000000000..dc45ab60962c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0979.yaml @@ -0,0 +1,75 @@ +--- +cases: + - note: inputvalues/simple + query: data.z.p = x + modules: + - | + package z + + p if { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q contains x if { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r contains x if { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s if { + input.req3.a.b.x[0] = 1 + } + + t if { + input.req4.a.b.x[0] = 1 + } + + u contains x if { + input.req3.a.b[_] = x + x > 1 + } + + w := [[1, 2], [3, 4]] + + gt1 if { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] := y if { + data.numbers[_] = x + to_number(x, y) + } + + loopback := __local0__ if { + true + __local0__ = input + } + + sets if { + input.foo[{1}][1] = 1 + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = input + } + data: + a: + - 1 + - 2 + - 3 + - 4 + input_term: '{"req1": {"foo": 4}, "req2": {"bar": 4}}' + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0980.yaml b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0980.yaml new file mode 100644 index 000000000000..ef7e4e19beb2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0980.yaml @@ -0,0 +1,110 @@ +--- +cases: + - note: inputvalues/missing + query: data.z.p = x + modules: + - | + package z + + p if { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q contains x if { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r contains x if { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s if { + input.req3.a.b.x[0] = 1 + } + + t if { + input.req4.a.b.x[0] = 1 + } + + u contains x if { + input.req3.a.b[_] = x + x > 1 + } + + w := [[1, 2], [3, 4]] + + gt1 if { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] := y if { + data.numbers[_] = x + to_number(x, y) + } + + loopback := __local0__ if { + true + __local0__ = input + } + + sets if { + input.foo[{1}][1] = 1 + } + - | + package topdown_test_partial + + __result__ := _result if { + input.req1.foo = 1 + input.req2.bar = 1 + input.req1.foo = 1 + input.req2.bar = 1 + 1 = input.req2.bar + 1 = input.req1.foo + _result = true + } + + __result__ := _result if { + input.req1.foo = 2 + input.req2.bar = 2 + input.req1.foo = 2 + input.req2.bar = 2 + 2 = input.req2.bar + 2 = input.req1.foo + _result = true + } + + __result__ := _result if { + input.req1.foo = 3 + input.req2.bar = 3 + input.req1.foo = 3 + input.req2.bar = 3 + 3 = input.req2.bar + 3 = input.req1.foo + _result = true + } + + __result__ := _result if { + input.req1.foo = 4 + input.req2.bar = 4 + input.req1.foo = 4 + input.req2.bar = 4 + 4 = input.req2.bar + 4 = input.req1.foo + _result = true + } + data: + a: + - 1 + - 2 + - 3 + - 4 + input_term: '{"req1": {"foo": 4}}' + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0981.yaml b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0981.yaml new file mode 100644 index 000000000000..bbdfd20877df --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0981.yaml @@ -0,0 +1,106 @@ +--- +cases: + - note: inputvalues/namespaced + query: data.z.s = x + modules: + - | + package z + + p if { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q contains x if { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r contains x if { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s if { + input.req3.a.b.x[0] = 1 + } + + t if { + input.req4.a.b.x[0] = 1 + } + + u contains x if { + input.req3.a.b[_] = x + x > 1 + } + + w := [[1, 2], [3, 4]] + + gt1 if { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] := y if { + data.numbers[_] = x + to_number(x, y) + } + + loopback := __local0__ if { + true + __local0__ = input + } + + sets if { + input.foo[{1}][1] = 1 + } + - | + package topdown_test_partial + + __result__ := _result if { + input.req1.foo = 1 + input.req2.bar = 1 + input.req1.foo = 1 + input.req2.bar = 1 + 1 = input.req2.bar + 1 = input.req1.foo + _result = true + } + + __result__ := _result if { + input.req1.foo = 2 + input.req2.bar = 2 + input.req1.foo = 2 + input.req2.bar = 2 + 2 = input.req2.bar + 2 = input.req1.foo + _result = true + } + + __result__ := _result if { + input.req1.foo = 3 + input.req2.bar = 3 + input.req1.foo = 3 + input.req2.bar = 3 + 3 = input.req2.bar + 3 = input.req1.foo + _result = true + } + + __result__ := _result if { + input.req1.foo = 4 + input.req2.bar = 4 + input.req1.foo = 4 + input.req2.bar = 4 + 4 = input.req2.bar + 4 = input.req1.foo + _result = true + } + data: {} + input_term: '{"req3": {"a": {"b": {"x": [1, 2, 3, 4]}}}}' + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0982.yaml b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0982.yaml new file mode 100644 index 000000000000..983b7cd3f3b7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0982.yaml @@ -0,0 +1,71 @@ +--- +cases: + - note: inputvalues/namespaced with alias + query: data.z.t = x + modules: + - | + package z + + p if { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q contains x if { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r contains x if { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s if { + input.req3.a.b.x[0] = 1 + } + + t if { + input.req4.a.b.x[0] = 1 + } + + u contains x if { + input.req3.a.b[_] = x + x > 1 + } + + w := [[1, 2], [3, 4]] + + gt1 if { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] := y if { + data.numbers[_] = x + to_number(x, y) + } + + loopback := __local0__ if { + true + __local0__ = input + } + + sets if { + input.foo[{1}][1] = 1 + } + - | + package topdown_test_partial + + __result__ := _result if { + input.req3.a.b.x[0] = 1 + _result = true + } + data: {} + input_term: '{"req4": {"a": {"b": {"x": [1, 2, 3, 4]}}}}' + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0983.yaml b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0983.yaml new file mode 100644 index 000000000000..7ed59cf1c9f0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/inputvalues/test-inputvalues-0983.yaml @@ -0,0 +1,71 @@ +--- +cases: + - note: inputvalues/input set + query: data.z.sets = x + modules: + - | + package z + + p if { + data.a[i] = x + input.req1.foo = x + input.req2.bar = x + data.z.q[x] + } + + q contains x if { + input.req1.foo = x + input.req2.bar = x + data.z.r[x] + } + + r contains x if { + __local1__ = input.req2.bar + __local2__ = input.req1.foo + {"bar": [x], "foo": __local1__} = {"bar": [__local2__], "foo": x} + } + + s if { + input.req3.a.b.x[0] = 1 + } + + t if { + input.req4.a.b.x[0] = 1 + } + + u contains x if { + input.req3.a.b[_] = x + x > 1 + } + + w := [[1, 2], [3, 4]] + + gt1 if { + __local3__ = input.req1 + __local3__ > 1 + } + + keys[x] := y if { + data.numbers[_] = x + to_number(x, y) + } + + loopback := __local0__ if { + true + __local0__ = input + } + + sets if { + input.foo[{1}][1] = 1 + } + - | + package topdown_test_partial + + __result__ := _result if { + input.req4.a.b.x[0] = 1 + _result = true + } + data: {} + input_term: '{"foo": {{1}}}' + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0352.yaml b/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0352.yaml new file mode 100644 index 000000000000..c24a9ae543f8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0352.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: intersection/intersection_0_sets + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + intersection(set(), x) + } + data: {} + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0353.yaml b/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0353.yaml new file mode 100644 index 000000000000..7d3761b8c66a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0353.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: intersection/intersection_2_sets + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + intersection({set(), {1, 2}}, x) + } + data: {} + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0354.yaml b/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0354.yaml new file mode 100644 index 000000000000..54006c48e421 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0354.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: intersection/intersection_2_sets + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + s1 = {1, 2, 3} + s2 = {2} + intersection({s1, s2}, x) + } + data: {} + want_result: + - x: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0355.yaml b/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0355.yaml new file mode 100644 index 000000000000..6917ad885aad --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0355.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: intersection/intersection_3_sets + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + s1 = {1, 2, 3} + s2 = {2, 3, 4} + s3 = {4, 5, 6} + intersection({s1, s2, s3}, x) + } + data: {} + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0356.yaml b/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0356.yaml new file mode 100644 index 000000000000..1ccf04753b95 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/intersection/test-intersection-0356.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: intersection/intersection_4_sets + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + s1 = {"a", "b", "c", "d"} + s2 = {"b", "c", "d"} + s3 = {"c", "d"} + s4 = {"d"} + intersection({s1, s2, s3, s4}, x) + } + data: {} + want_result: + - x: + - d diff --git a/third_party/opa/v1/test/cases/testdata/v1/invalidkeyerror/test-invalidkeyerror-0176.yaml b/third_party/opa/v1/test/cases/testdata/v1/invalidkeyerror/test-invalidkeyerror-0176.yaml new file mode 100644 index 000000000000..d6992bf0b6cc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/invalidkeyerror/test-invalidkeyerror-0176.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: invalidkeyerror/invalid keys + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + http.send({"bad_key": "bad_value", "method": "get", "url": "http://127.0.0.1:51113"}, x) + } + want_error_code: eval_type_error + want_error: 'invalid request parameters(s): {"bad_key"}' + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/invalidkeyerror/test-invalidkeyerror-0177.yaml b/third_party/opa/v1/test/cases/testdata/v1/invalidkeyerror/test-invalidkeyerror-0177.yaml new file mode 100644 index 000000000000..40790f32c4c3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/invalidkeyerror/test-invalidkeyerror-0177.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: invalidkeyerror/missing keys + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + http.send({"method": "get"}, x) + } + want_error_code: eval_type_error + want_error: 'missing required request parameters(s): {"url"}' + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-is-valid.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-is-valid.yaml new file mode 100644 index 000000000000..ff20809694f3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-is-valid.yaml @@ -0,0 +1,72 @@ +--- +cases: + - note: jsonbuiltins/json is_valid + query: data.generated.p = x + modules: + - | + package generated + + documents := [ + `plainstring`, + `{`, + `{"json": "ok"}`, + ] + + p := [x | doc = documents[_]; json.is_valid(doc, x)] + want_result: + - x: + - false + - false + - true + strict_error: true + - note: jsonbuiltins/json is_valid not string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + json.is_valid(input.foo, x) + } + input: + foo: 1 + want_result: + - x: false + strict_error: true + - note: jsonbuiltins/yaml is_valid + query: data.generated.p = x + modules: + - | + package generated + + documents := [ + `foo: + - qux: bar + - baz: 2`, + `foo: + - qux: bar + - baz: {`, + `{"json": "ok"}`, + ] + + p := [x | doc = documents[_]; yaml.is_valid(doc, x)] + want_result: + - x: + - true + - false + - true + strict_error: true + - note: jsonbuiltins/yaml is_valid not string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + yaml.is_valid(input.foo, x) + } + input: + foo: 1 + want_result: + - x: false + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-json-marshal-with-options.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-json-marshal-with-options.yaml new file mode 100644 index 000000000000..fe433b72aa51 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-json-marshal-with-options.yaml @@ -0,0 +1,143 @@ +--- +cases: + - note: jsonbuiltins/marshal_with_options-explicit-indent + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := json.marshal_with_options([1234500000 + 67890, 1e6 * 2, 1e109 / 1e100], {"indent": " "}) + } + data: {} + want_result: + - x: |- + [ + 1234567890, + 2000000, + 1000000000 + ] + - note: jsonbuiltins/marshal_with_options-empty-object + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := json.marshal_with_options([1234500000 + 67890, 1e6 * 2, 1e109 / 1e100], {}) + } + data: {} + want_result: + - x: "[1234567890,2000000,1000000000]" + - note: jsonbuiltins/marshal_with_options-defaults + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := json.marshal_with_options([1234500000 + 67890, 1e6 * 2, 1e109 / 1e100], {"pretty": true}) + } + data: {} + want_result: + - x: |- + [ + 1234567890, + 2000000, + 1000000000 + ] + - note: jsonbuiltins/marshal_with_options-explicit-disable + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := json.marshal_with_options([1234500000 + 67890, 1e6 * 2, 1e109 / 1e100], {"pretty": false, "prefix": "NO!", "indent": "BAD!"}) + } + data: {} + want_result: + - x: "[1234567890,2000000,1000000000]" + - note: jsonbuiltins/marshal_with_options-prefix + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := json.marshal_with_options([1234500000 + 67890, 1e6 * 2, 1e109 / 1e100], {"prefix": "JSON => "}) + } + data: {} + want_result: + - x: |- + JSON => [ + JSON => 1234567890, + JSON => 2000000, + JSON => 1000000000 + JSON => ] + - note: jsonbuiltins/marshal_with_options-object + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := json.marshal_with_options({"foo": "bar", "bar": "baz"}, {"prefix": "JSON => "}) + } + data: {} + want_result: + - x: |- + JSON => { + JSON => "bar": "baz", + JSON => "foo": "bar" + JSON => } + - note: jsonbuiltins/marshal_with_options-empty-array + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := json.marshal_with_options([], {"indent": " ", "prefix": "---"}) + } + data: {} + want_result: + - x: "---[]" + - note: jsonbuiltins/marshal_with_options-deep-array + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := json.marshal_with_options([[[[[[[]]]]]]], {"indent": " "}) + } + data: {} + want_result: + - x: |- + [ + [ + [ + [ + [ + [ + [] + ] + ] + ] + ] + ] + ] + - note: jsonbuiltins/marshal_with_options-invalid-key + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := json.marshal_with_options([], {"indent": " ", "include_winning_lottery_numbers": true}) + } + data: {} + want_error_code: eval_type_error + want_error: object contained unknown key "include_winning_lottery_numbers" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0924.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0924.yaml new file mode 100644 index 000000000000..c667516c86b5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0924.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jsonbuiltins/marshal + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + json.marshal([{"foo": {1, 2, 3}}], x) + } + data: {} + want_result: + - x: '[{"foo":[1,2,3]}]' diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0925.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0925.yaml new file mode 100644 index 000000000000..1295672c8361 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0925.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonbuiltins/unmarshal + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + json.unmarshal("[{\"foo\":[1,2,3]}]", x) + } + data: {} + want_result: + - x: + - foo: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0926.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0926.yaml new file mode 100644 index 000000000000..8349aee49bca --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0926.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: jsonbuiltins/unmarshal-non-string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local0__ = data.a[0] + json.unmarshal(__local0__, x) + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_error_code: eval_type_error + want_error: operand 1 must be string but got number + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0927.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0927.yaml new file mode 100644 index 000000000000..923cecba1630 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0927.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jsonbuiltins/yaml round-trip + query: data.generated.p = x + modules: + - | + package generated + + p := y if { + yaml.marshal([{"foo": {1, 2, 3}}], x) + yaml.unmarshal(x, y) + } + data: {} + want_result: + - x: + - foo: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0928.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0928.yaml new file mode 100644 index 000000000000..a594a5c7bc2a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-jsonbuiltins-0928.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jsonbuiltins/yaml unmarshal error + query: data.generated.p = x + modules: + - | + package generated + + p if { + yaml.unmarshal("[1,2,3", _) + } + want_error_code: eval_builtin_error + want_error: "yaml: line 1: did not find" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-marshal-large-ints.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-marshal-large-ints.yaml new file mode 100644 index 000000000000..bdc608cba704 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonbuiltins/test-marshal-large-ints.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jsonbuiltins/marshal large integers + query: data.test.p = x + modules: + - | + package test + + p := x if { + json.marshal([1234500000 + 67890, 1e6 * 2, 1e109 / 1e100], x) + } + data: {} + want_result: + - x: "[1234567890,2000000,1000000000]" diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0218.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0218.yaml new file mode 100644 index 000000000000..799fc619da03 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0218.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonfilter/base + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a/b/c"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + c: 7 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0219.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0219.yaml new file mode 100644 index 000000000000..bb623a83de75 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0219.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jsonfilter/multiple roots + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a/b/c", "e"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + c: 7 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0220.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0220.yaml new file mode 100644 index 000000000000..2868060bc091 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0220.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jsonfilter/multiple roots array + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, ["a/b/c", "e"], __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + c: 7 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0221.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0221.yaml new file mode 100644 index 000000000000..96583556e1ec --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0221.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jsonfilter/shared roots + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.filter({"a": {"b": {"c": 7, "d": 8}, "e": 9}}, {"a/b/c", "a/e"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + c: 7 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0222.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0222.yaml new file mode 100644 index 000000000000..2ee5f94f5e66 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0222.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: jsonfilter/conflict + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.filter({"a": {"b": 7}}, {"a", "a/b"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: 7 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0223.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0223.yaml new file mode 100644 index 000000000000..a84c8477eb43 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0223.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jsonfilter/empty list + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.filter({"a": 7}, set(), __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0224.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0224.yaml new file mode 100644 index 000000000000..cfc7b3c0f720 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0224.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jsonfilter/empty object + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.filter({}, {"a/b"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0225.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0225.yaml new file mode 100644 index 000000000000..b309a64fb741 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0225.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonfilter/arrays + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.filter({"a": [{"b": 7, "c": 8}, {"d": 9}]}, {"a/0/b", "a/1"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + - b: 7 + - d: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0226.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0226.yaml new file mode 100644 index 000000000000..26ff7700efb4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0226.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonfilter/object with number keys + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.filter({"a": [{"1": ["b", "c", "d"]}, {"x": "y"}]}, {"a/0/1/2"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + - "1": + - d diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0227.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0227.yaml new file mode 100644 index 000000000000..f72035abfa7a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0227.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jsonfilter/arrays of roots + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {["a", "b", "c"], ["e"]}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + c: 7 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0228.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0228.yaml new file mode 100644 index 000000000000..910ab939329e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonfilter/test-jsonfilter-0228.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jsonfilter/mixed root types + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.filter({"a": {"b": {"c": 7, "d": 8, "x": 0}}, "e": 9}, {"a/b/d", ["a", "b", "c"]}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + c: 7 + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonfilteridempotent/test-jsonfilteridempotent-0229.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonfilteridempotent/test-jsonfilteridempotent-0229.yaml new file mode 100644 index 000000000000..e86833e7b81b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonfilteridempotent/test-jsonfilteridempotent-0229.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jsonfilteridempotent/TestBuiltinJSONFilterIdempotent + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = {"a": {"b": 2, "c": 3}} + json.filter(__local0__, {"a/b"}, __local1__) + __local1__ = {"a": {"b": 2}} + json.filter(__local0__, {"a/c"}, __local2__) + __local2__ = {"a": {"c": 3}} + __local0__ = {"a": {"b": 2, "c": 3}} + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonpatch/coverage.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonpatch/coverage.yaml new file mode 100644 index 000000000000..37ddba404645 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonpatch/coverage.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jsonpatch/set-failure add-to-bad-path + query: data.main.result = x + modules: + - | + package main + + doc := [1, 2, 3] + + patch := [{"op": "add", "path": "1.2", "value": "foo"}] + + result := r if r = json.patch(doc, patch) + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonpatch/json-patch-tests.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonpatch/json-patch-tests.yaml new file mode 100644 index 000000000000..5f4893a67b91 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonpatch/json-patch-tests.yaml @@ -0,0 +1,794 @@ +--- +cases: + - note: jsonpatch/json_patch_tests + query: data.main.failed_cases = x + modules: + - | + package main + + # Grab all cases from the modules inside `data.json_patch_cases` and + # construct an object with readable index names. + all_cases[k] := t if { + t := data.json_patch_cases[p].cases[i] + k := sprintf("%s/%d", [p, i]) + } + + # Go through `all_cases` and select the ones that pass. + passed_cases[k] := t if { + t := all_cases[k] + t.expected == json.patch(t.doc, [p | p = t.patch[_]]) + } + + passed_cases[k] := t if { + # Some cases ("test" ones in particular) don't have an expected value but + # are still tests that we want to run. + t := all_cases[k] + not t.expected + not t.error + _ = json.patch(t.doc, [p | p = t.patch[_]]) + } + + passed_cases[k] := t if { + # These are cases that are expected to not return a result, for example + # there's an invalid index or a "test" fails. + t := all_cases[k] + _ = t.error + not json.patch(t.doc, [p | p = t.patch[_]]) + } + + passed_cases[k] := t if { + # Some cases are specifically disabled for the OPA implementation. + t := all_cases[k] + t.opa_disabled == true + } + + # `failed_cases` is simply the cases that didn't pass. + failed_cases[k] := t if { + t := all_cases[k] + not passed_cases[k] + } + - | + package json_patch_cases.json_spec_tests + + cases := [ + { + "comment": "4.1. add with missing object", + "doc": {"q": {"bar": 2}}, + "patch": [{"op": "add", "path": "/a/b", "value": 1}], + "error": "path /a does not exist -- missing objects are not created recursively", + }, + { + "comment": "A.1. Adding an Object Member", + "doc": {"foo": "bar"}, + "patch": [{"op": "add", "path": "/baz", "value": "qux"}], + "expected": { + "baz": "qux", + "foo": "bar", + }, + }, + { + "comment": "A.2. Adding an Array Element", + "doc": {"foo": ["bar", "baz"]}, + "patch": [{"op": "add", "path": "/foo/1", "value": "qux"}], + "expected": {"foo": ["bar", "qux", "baz"]}, + }, + { + "comment": "A.3. Removing an Object Member", + "doc": { + "baz": "qux", + "foo": "bar", + }, + "patch": [{"op": "remove", "path": "/baz"}], + "expected": {"foo": "bar"}, + }, + { + "comment": "A.4. Removing an Array Element", + "doc": {"foo": ["bar", "qux", "baz"]}, + "patch": [{"op": "remove", "path": "/foo/1"}], + "expected": {"foo": ["bar", "baz"]}, + }, + { + "comment": "A.5. Replacing a Value", + "doc": { + "baz": "qux", + "foo": "bar", + }, + "patch": [{"op": "replace", "path": "/baz", "value": "boo"}], + "expected": { + "baz": "boo", + "foo": "bar", + }, + }, + { + "comment": "A.6. Moving a Value", + "doc": { + "foo": { + "bar": "baz", + "waldo": "fred", + }, + "qux": {"corge": "grault"}, + }, + "patch": [{"op": "move", "from": "/foo/waldo", "path": "/qux/thud"}], + "expected": { + "foo": {"bar": "baz"}, + "qux": { + "corge": "grault", + "thud": "fred", + }, + }, + }, + { + "comment": "A.7. Moving an Array Element", + "doc": {"foo": ["all", "grass", "cows", "eat"]}, + "patch": [{"op": "move", "from": "/foo/1", "path": "/foo/3"}], + "expected": {"foo": ["all", "cows", "eat", "grass"]}, + }, + { + "comment": "A.8. Testing a Value: Success", + "doc": { + "baz": "qux", + "foo": ["a", 2, "c"], + }, + "patch": [ + {"op": "test", "path": "/baz", "value": "qux"}, + {"op": "test", "path": "/foo/1", "value": 2}, + ], + "expected": { + "baz": "qux", + "foo": ["a", 2, "c"], + }, + }, + { + "comment": "A.9. Testing a Value: Error", + "doc": {"baz": "qux"}, + "patch": [{"op": "test", "path": "/baz", "value": "bar"}], + "error": "string not equivalent", + }, + { + "comment": "A.10. Adding a nested Member Object", + "doc": {"foo": "bar"}, + "patch": [{"op": "add", "path": "/child", "value": {"grandchild": {}}}], + "expected": { + "foo": "bar", + "child": {"grandchild": {}}, + }, + }, + { + "comment": "A.11. Ignoring Unrecognized Elements", + "doc": {"foo": "bar"}, + "patch": [{"op": "add", "path": "/baz", "value": "qux", "xyz": 123}], + "expected": { + "foo": "bar", + "baz": "qux", + }, + }, + { + "comment": "A.12. Adding to a Non-existent Target", + "doc": {"foo": "bar"}, + "patch": [{"op": "add", "path": "/baz/bat", "value": "qux"}], + "error": "add to a non-existent target", + }, + { + "comment": "A.13 Invalid JSON Patch Document", + "doc": {"foo": "bar"}, + "patch": [{"op": "remove", "path": "/baz", "value": "qux"}], + "error": "operation has two 'op' members", + "disabled": true, + }, + { + "comment": "A.14. ~ Escape Ordering", + "doc": { + "/": 9, + "~1": 10, + }, + "patch": [{"op": "test", "path": "/~01", "value": 10}], + "expected": { + "/": 9, + "~1": 10, + }, + }, + { + "comment": "A.15. Comparing Strings and Numbers", + "doc": { + "/": 9, + "~1": 10, + }, + "patch": [{"op": "test", "path": "/~01", "value": "10"}], + "error": "number is not equal to string", + }, + { + "comment": "A.16. Adding an Array Value", + "doc": {"foo": ["bar"]}, + "patch": [{"op": "add", "path": "/foo/-", "value": ["abc", "def"]}], + "expected": {"foo": ["bar", ["abc", "def"]]}, + }, + ] + - | + package json_patch_cases.json_tests + + cases := [ + { + "comment": "empty list, empty docs", + "doc": {}, + "patch": [], + "expected": {}, + }, + { + "comment": "empty patch list", + "doc": {"foo": 1}, + "patch": [], + "expected": {"foo": 1}, + }, + { + "comment": "rearrangements OK?", + "doc": {"foo": 1, "bar": 2}, + "patch": [], + "expected": {"bar": 2, "foo": 1}, + }, + { + "comment": "rearrangements OK? How about one level down ... array", + "doc": [{"foo": 1, "bar": 2}], + "patch": [], + "expected": [{"bar": 2, "foo": 1}], + }, + { + "comment": "rearrangements OK? How about one level down...", + "doc": {"foo": {"foo": 1, "bar": 2}}, + "patch": [], + "expected": {"foo": {"bar": 2, "foo": 1}}, + }, + { + "comment": "add replaces any existing field", + "doc": {"foo": null}, + "patch": [{"op": "add", "path": "/foo", "value": 1}], + "expected": {"foo": 1}, + }, + { + "comment": "toplevel array", + "doc": [], + "patch": [{"op": "add", "path": "/0", "value": "foo"}], + "expected": ["foo"], + }, + { + "comment": "toplevel array, no change", + "doc": ["foo"], + "patch": [], + "expected": ["foo"], + }, + { + "comment": "toplevel object, numeric string", + "doc": {}, + "patch": [{"op": "add", "path": "/foo", "value": "1"}], + "expected": {"foo": "1"}, + }, + { + "comment": "toplevel object, integer", + "doc": {}, + "patch": [{"op": "add", "path": "/foo", "value": 1}], + "expected": {"foo": 1}, + }, + { + "comment": "Toplevel scalar values OK?", + "doc": "foo", + "patch": [{"op": "replace", "path": "", "value": "bar"}], + "expected": "bar", + "disabled": true, + }, + { + "comment": "replace object document with array document?", + "doc": {}, + "patch": [{"op": "add", "path": "", "value": []}], + "expected": [], + }, + { + "comment": "replace array document with object document?", + "doc": [], + "patch": [{"op": "add", "path": "", "value": {}}], + "expected": {}, + }, + { + "comment": "append to root array document?", + "doc": [], + "patch": [{"op": "add", "path": "/-", "value": "hi"}], + "expected": ["hi"], + }, + { + "comment": "Add, / target", + "doc": {}, + "patch": [{"op": "add", "path": "/", "value": 1}], + "expected": {"": 1}, + }, + { + "comment": "Add, /foo/ deep target (trailing slash)", + "doc": {"foo": {}}, + "patch": [{"op": "add", "path": "/foo/", "value": 1}], + "expected": {"foo": {"": 1}}, + }, + { + "comment": "Add composite value at top level", + "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/bar", "value": [1, 2]}], + "expected": {"foo": 1, "bar": [1, 2]}, + }, + { + "comment": "Add into composite value", + "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "add", "path": "/baz/0/foo", "value": "world"}], + "expected": {"foo": 1, "baz": [{"qux": "hello", "foo": "world"}]}, + }, + { + "doc": {"bar": [1, 2]}, + "patch": [{"op": "add", "path": "/bar/8", "value": "5"}], + "error": "Out of bounds (upper)", + }, + { + "doc": {"bar": [1, 2]}, + "patch": [{"op": "add", "path": "/bar/-1", "value": "5"}], + "error": "Out of bounds (lower)", + }, + { + "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/bar", "value": true}], + "expected": {"foo": 1, "bar": true}, + }, + { + "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/bar", "value": false}], + "expected": {"foo": 1, "bar": false}, + }, + { + "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/bar", "value": null}], + "expected": {"foo": 1, "bar": null}, + }, + { + "comment": "0 can be an array index or object element name", + "doc": {"foo": 1}, + "patch": [{"op": "add", "path": "/0", "value": "bar"}], + "expected": {"foo": 1, "0": "bar"}, + }, + { + "doc": ["foo"], + "patch": [{"op": "add", "path": "/1", "value": "bar"}], + "expected": ["foo", "bar"], + }, + { + "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/1", "value": "bar"}], + "expected": ["foo", "bar", "sil"], + }, + { + "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/0", "value": "bar"}], + "expected": ["bar", "foo", "sil"], + }, + { + "comment": "push item to array via last index + 1", + "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/2", "value": "bar"}], + "expected": ["foo", "sil", "bar"], + }, + { + "comment": "add item to array at index > length should fail", + "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/3", "value": "bar"}], + "error": "index is greater than number of items in array", + }, + { + "comment": "test against implementation-specific numeric parsing", + "doc": {"1e0": "foo"}, + "patch": [{"op": "test", "path": "/1e0", "value": "foo"}], + "expected": {"1e0": "foo"}, + }, + { + "comment": "test with bad number should fail", + "doc": ["foo", "bar"], + "patch": [{"op": "test", "path": "/1e0", "value": "bar"}], + "error": "test op shouldn't get array element 1", + }, + { + "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/bar", "value": 42}], + "error": "Object operation on array target", + }, + { + "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/1", "value": ["bar", "baz"]}], + "expected": ["foo", ["bar", "baz"], "sil"], + "comment": "value in array add not flattened", + }, + { + "doc": {"foo": 1, "bar": [1, 2, 3, 4]}, + "patch": [{"op": "remove", "path": "/bar"}], + "expected": {"foo": 1}, + }, + { + "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "remove", "path": "/baz/0/qux"}], + "expected": {"foo": 1, "baz": [{}]}, + }, + { + "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "replace", "path": "/foo", "value": [1, 2, 3, 4]}], + "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]}, + }, + { + "doc": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]}, + "patch": [{"op": "replace", "path": "/baz/0/qux", "value": "world"}], + "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "world"}]}, + }, + { + "doc": ["foo"], + "patch": [{"op": "replace", "path": "/0", "value": "bar"}], + "expected": ["bar"], + }, + { + "doc": [""], + "patch": [{"op": "replace", "path": "/0", "value": 0}], + "expected": [0], + }, + { + "doc": [""], + "patch": [{"op": "replace", "path": "/0", "value": true}], + "expected": [true], + }, + { + "doc": [""], + "patch": [{"op": "replace", "path": "/0", "value": false}], + "expected": [false], + }, + { + "doc": [""], + "patch": [{"op": "replace", "path": "/0", "value": null}], + "expected": [null], + }, + { + "doc": ["foo", "sil"], + "patch": [{"op": "replace", "path": "/1", "value": ["bar", "baz"]}], + "expected": ["foo", ["bar", "baz"]], + "comment": "value in array replace not flattened", + }, + { + "comment": "replace whole document", + "doc": {"foo": "bar"}, + "patch": [{"op": "replace", "path": "", "value": {"baz": "qux"}}], + "expected": {"baz": "qux"}, + }, + { + "comment": "test replace with missing parent key should fail", + "doc": {"bar": "baz"}, + "patch": [{"op": "replace", "path": "/foo/bar", "value": false}], + "error": "replace op should fail with missing parent key", + }, + { + "comment": "spurious patch properties", + "doc": {"foo": 1}, + "patch": [{"op": "test", "path": "/foo", "value": 1, "spurious": 1}], + "expected": {"foo": 1}, + }, + { + "doc": {"foo": null}, + "patch": [{"op": "test", "path": "/foo", "value": null}], + "expected": {"foo": null}, + "comment": "null value should be valid obj property", + }, + { + "doc": {"foo": null}, + "patch": [{"op": "replace", "path": "/foo", "value": "truthy"}], + "expected": {"foo": "truthy"}, + "comment": "null value should be valid obj property to be replaced with something truthy", + }, + { + "doc": {"foo": null}, + "patch": [{"op": "move", "from": "/foo", "path": "/bar"}], + "expected": {"bar": null}, + "comment": "null value should be valid obj property to be moved", + }, + { + "doc": {"foo": null}, + "patch": [{"op": "copy", "from": "/foo", "path": "/bar"}], + "expected": {"foo": null, "bar": null}, + "comment": "null value should be valid obj property to be copied", + }, + { + "doc": {"foo": null}, + "patch": [{"op": "remove", "path": "/foo"}], + "expected": {}, + "comment": "null value should be valid obj property to be removed", + }, + { + "doc": {"foo": "bar"}, + "patch": [{"op": "replace", "path": "/foo", "value": null}], + "expected": {"foo": null}, + "comment": "null value should still be valid obj property replace other value", + }, + { + "doc": {"foo": {"foo": 1, "bar": 2}}, + "patch": [{"op": "test", "path": "/foo", "value": {"bar": 2, "foo": 1}}], + "expected": {"foo": {"foo": 1, "bar": 2}}, + "comment": "test should pass despite rearrangement", + }, + { + "doc": {"foo": [{"foo": 1, "bar": 2}]}, + "patch": [{"op": "test", "path": "/foo", "value": [{"bar": 2, "foo": 1}]}], + "expected": {"foo": [{"foo": 1, "bar": 2}]}, + "comment": "test should pass despite (nested) rearrangement", + }, + { + "doc": {"foo": {"bar": [1, 2, 5, 4]}}, + "patch": [{"op": "test", "path": "/foo", "value": {"bar": [1, 2, 5, 4]}}], + "expected": {"foo": {"bar": [1, 2, 5, 4]}}, + "comment": "test should pass - no error", + }, + { + "doc": {"foo": {"bar": [1, 2, 5, 4]}}, + "patch": [{"op": "test", "path": "/foo", "value": [1, 2]}], + "error": "test op should fail", + }, + { + "comment": "Whole document", + "doc": {"foo": 1}, + "patch": [{"op": "test", "path": "", "value": {"foo": 1}}], + "disabled": true, + }, + { + "comment": "Empty-string element", + "doc": {"": 1}, + "patch": [{"op": "test", "path": "/", "value": 1}], + "expected": {"": 1}, + }, + { + "doc": { + "foo": ["bar", "baz"], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8, + }, + "patch": [ + {"op": "test", "path": "/foo", "value": ["bar", "baz"]}, + {"op": "test", "path": "/foo/0", "value": "bar"}, + {"op": "test", "path": "/", "value": 0}, + {"op": "test", "path": "/a~1b", "value": 1}, + {"op": "test", "path": "/c%d", "value": 2}, + {"op": "test", "path": "/e^f", "value": 3}, + {"op": "test", "path": "/g|h", "value": 4}, + {"op": "test", "path": "/i\\j", "value": 5}, + {"op": "test", "path": "/k\"l", "value": 6}, + {"op": "test", "path": "/ ", "value": 7}, + {"op": "test", "path": "/m~0n", "value": 8}, + ], + "expected": { + "": 0, + " ": 7, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "foo": [ + "bar", + "baz", + ], + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + "m~n": 8, + }, + }, + { + "comment": "Move to same location has no effect", + "doc": {"foo": 1}, + "patch": [{"op": "move", "from": "/foo", "path": "/foo"}], + "expected": {"foo": 1}, + }, + { + "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "move", "from": "/foo", "path": "/bar"}], + "expected": {"baz": [{"qux": "hello"}], "bar": 1}, + }, + { + "doc": {"baz": [{"qux": "hello"}], "bar": 1}, + "patch": [{"op": "move", "from": "/baz/0/qux", "path": "/baz/1"}], + "expected": {"baz": [{}, "hello"], "bar": 1}, + }, + { + "doc": {"baz": [{"qux": "hello"}], "bar": 1}, + "patch": [{"op": "copy", "from": "/baz/0", "path": "/boo"}], + "expected": {"baz": [{"qux": "hello"}], "bar": 1, "boo": {"qux": "hello"}}, + }, + { + "comment": "replacing the root of the document is possible with add", + "doc": {"foo": "bar"}, + "patch": [{"op": "add", "path": "", "value": {"baz": "qux"}}], + "expected": {"baz": "qux"}, + }, + { + "comment": "Adding to \"/-\" adds to the end of the array", + "doc": [1, 2], + "patch": [{"op": "add", "path": "/-", "value": {"foo": ["bar", "baz"]}}], + "expected": [1, 2, {"foo": ["bar", "baz"]}], + }, + { + "comment": "Adding to \"/-\" adds to the end of the array, even n levels down", + "doc": [1, 2, [3, [4, 5]]], + "patch": [{"op": "add", "path": "/2/1/-", "value": {"foo": ["bar", "baz"]}}], + "expected": [1, 2, [3, [4, 5, {"foo": ["bar", "baz"]}]]], + }, + { + "comment": "test remove with bad number should fail", + "doc": {"foo": 1, "baz": [{"qux": "hello"}]}, + "patch": [{"op": "remove", "path": "/baz/1e0/qux"}], + "error": "remove op shouldn't remove from array with bad number", + }, + { + "comment": "test remove on array", + "doc": [1, 2, 3, 4], + "patch": [{"op": "remove", "path": "/0"}], + "expected": [2, 3, 4], + }, + { + "comment": "test repeated removes", + "doc": [1, 2, 3, 4], + "patch": [ + {"op": "remove", "path": "/1"}, + {"op": "remove", "path": "/2"}, + ], + "expected": [1, 3], + }, + { + "comment": "test remove with bad index should fail", + "doc": [1, 2, 3, 4], + "patch": [{"op": "remove", "path": "/1e0"}], + "error": "remove op shouldn't remove from array with bad number", + }, + { + "comment": "test replace with bad number should fail", + "doc": [""], + "patch": [{"op": "replace", "path": "/1e0", "value": false}], + "error": "replace op shouldn't replace in array with bad number", + }, + { + "comment": "test copy with bad number should fail", + "doc": {"baz": [1, 2, 3], "bar": 1}, + "patch": [{"op": "copy", "from": "/baz/1e0", "path": "/boo"}], + "error": "copy op shouldn't work with bad number", + }, + { + "comment": "test move with bad number should fail", + "doc": {"foo": 1, "baz": [1, 2, 3, 4]}, + "patch": [{"op": "move", "from": "/baz/1e0", "path": "/foo"}], + "error": "move op shouldn't work with bad number", + }, + { + "comment": "test add with bad number should fail", + "doc": ["foo", "sil"], + "patch": [{"op": "add", "path": "/1e0", "value": "bar"}], + "error": "add op shouldn't add to array with bad number", + }, + { + "comment": "missing 'path' parameter", + "doc": {}, + "patch": [{"op": "add", "value": "bar"}], + "error": "missing 'path' parameter", + }, + { + "comment": "'path' parameter with null value", + "doc": {}, + "patch": [{"op": "add", "path": null, "value": "bar"}], + "error": "null is not valid value for 'path'", + }, + { + "comment": "invalid JSON Pointer token", + "opa_disabled": true, + "doc": {}, + "patch": [{"op": "add", "path": "foo", "value": "bar"}], + "error": "JSON Pointer should start with a slash", + }, + { + "comment": "missing 'value' parameter to add", + "doc": [1], + "patch": [{"op": "add", "path": "/-"}], + "error": "missing 'value' parameter", + }, + { + "comment": "missing 'value' parameter to replace", + "doc": [1], + "patch": [{"op": "replace", "path": "/0"}], + "error": "missing 'value' parameter", + }, + { + "comment": "missing 'value' parameter to test", + "doc": [null], + "patch": [{"op": "test", "path": "/0"}], + "error": "missing 'value' parameter", + }, + { + "comment": "missing value parameter to test - where undef is falsy", + "doc": [false], + "patch": [{"op": "test", "path": "/0"}], + "error": "missing 'value' parameter", + }, + { + "comment": "missing from parameter to copy", + "doc": [1], + "patch": [{"op": "copy", "path": "/-"}], + "error": "missing 'from' parameter", + }, + { + "comment": "missing from location to copy", + "doc": {"foo": 1}, + "patch": [{"op": "copy", "from": "/bar", "path": "/foo"}], + "error": "missing 'from' location", + }, + { + "comment": "missing from parameter to move", + "doc": {"foo": 1}, + "patch": [{"op": "move", "path": ""}], + "error": "missing 'from' parameter", + }, + { + "comment": "missing from location to move", + "doc": {"foo": 1}, + "patch": [{"op": "move", "from": "/bar", "path": "/foo"}], + "error": "missing 'from' location", + }, + { + "comment": "duplicate ops", + "opa_disabled": true, + "doc": {"foo": "bar"}, + "patch": [{ + "op": "move", "path": "/baz", "value": "qux", + "from": "/foo", + }], + "error": "patch has two 'op' members", + "disabled": true, + }, + { + "comment": "unrecognized op should fail", + "doc": {"foo": 1}, + "patch": [{"op": "spam", "path": "/foo", "value": 1}], + "error": "Unrecognized op 'spam'", + }, + { + "comment": "test with bad array number that has leading zeros", + "doc": ["foo", "bar"], + "patch": [{"op": "test", "path": "/00", "value": "foo"}], + "error": "test op should reject the array value, it has leading zeros", + }, + { + "comment": "test with bad array number that has leading zeros", + "doc": ["foo", "bar"], + "patch": [{"op": "test", "path": "/01", "value": "bar"}], + "error": "test op should reject the array value, it has leading zeros", + }, + { + "comment": "Removing nonexistent field", + "doc": {"foo": "bar"}, + "patch": [{"op": "remove", "path": "/baz"}], + "error": "removing a nonexistent field should fail", + }, + { + "comment": "Removing deep nonexistent path", + "doc": {"foo": "bar"}, + "patch": [{"op": "remove", "path": "/missing1/missing2"}], + "error": "removing a nonexistent field should fail", + }, + { + "comment": "Removing nonexistent index", + "doc": ["foo", "bar"], + "patch": [{"op": "remove", "path": "/2"}], + "error": "removing a nonexistent index should fail", + }, + { + "comment": "Patch with different capitalisation than doc", + "doc": {"foo": "bar"}, + "patch": [{"op": "add", "path": "/FOO", "value": "BAR"}], + "expected": {"foo": "bar", "FOO": "BAR"}, + }, + ] + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonpatch/set.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonpatch/set.yaml new file mode 100644 index 000000000000..fd31a4fc88fc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonpatch/set.yaml @@ -0,0 +1,82 @@ +--- +cases: + - note: jsonpatch/set-success basic-remove + query: data.main.result.foo = x + modules: + - | + package main + + doc := {"foo": {"a", "b", "c"}} + + patch := [{"op": "remove", "path": "foo/b"}] + + result := r if r = json.patch(doc, patch) + want_result: + - x: + - a + - c + sort_bindings: true + - note: jsonpatch/set-success basic-add + query: data.main.result.foo = x + modules: + - | + package main + + doc := {"foo": {"a", "b", "c"}} + + patch := [{"op": "add", "path": "foo/d", "value": "d"}] + + result := r if r = json.patch(doc, patch) + want_result: + - x: + - a + - b + - c + - d + sort_bindings: true + - note: jsonpatch/set-failure add-with-mismatched-key-value + query: data.main.result.foo = x + modules: + - | + package main + + doc := {"foo": {"a", "b", "c"}} + + patch := [{"op": "add", "path": "foo/d", "value": "e"}] + + result := r if r = json.patch(doc, patch) + want_result: [] + - note: jsonpatch/set-success basic-move + query: data.main.result.foo = x; data.main.result.bar = z + modules: + - | + package main + + doc := {"foo": {"a", "b"}, "bar": {"c", "d"}} + + patch := [{"op": "move", "from": "foo/a", "path": "bar/a"}] + + result := r if r = json.patch(doc, patch) + want_result: + - x: + - b + z: + - a + - c + - d + sort_bindings: true + - note: jsonpatch/set-success add-to-nested-array + query: data.main.result = x + modules: + - | + package main + + doc := {[1]} + + patch := [{"op": "add", "path": [[1], 1], "value": 2}] + + result := r if r = json.patch(doc, patch) + want_result: + - x: + - - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0230.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0230.yaml new file mode 100644 index 000000000000..664784a775aa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0230.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jsonremove/base + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a/b/c"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + d: 8 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0231.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0231.yaml new file mode 100644 index 000000000000..39659f897574 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0231.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/multiple roots + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a/b/c", "e"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0232.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0232.yaml new file mode 100644 index 000000000000..2394d269648b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0232.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/multiple roots array + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, ["a/b/c", "e"], __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0233.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0233.yaml new file mode 100644 index 000000000000..b14c7e5235ec --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0233.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/shared roots + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({"a": {"b": {"c": 7, "d": 8}, "e": 9}}, {"a/b/c", "a/e"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0234.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0234.yaml new file mode 100644 index 000000000000..efe32238722e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0234.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jsonremove/conflict + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({"a": {"b": 7}, "c": 1}, {"a", "a/b"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + c: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0235.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0235.yaml new file mode 100644 index 000000000000..7cfbbbe05f0c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0235.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jsonremove/empty list + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({"a": 7}, set(), __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 7 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0236.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0236.yaml new file mode 100644 index 000000000000..56fc375a89e3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0236.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jsonremove/empty object + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({}, {"a/b"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0237.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0237.yaml new file mode 100644 index 000000000000..5be9f78aa435 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0237.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jsonremove/delete all + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({"a": {"b": 7}, "c": 1}, {"a", "c"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0238.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0238.yaml new file mode 100644 index 000000000000..2c21e040722f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0238.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jsonremove/delete last in object + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({"a": {"b": 7}, "c": 1}, {"a/b", "c"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0239.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0239.yaml new file mode 100644 index 000000000000..f976e46f2f83 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0239.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: jsonremove/arrays + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({"a": [{"b": 7, "c": 8}, {"d": 9}]}, {"a/0/b", "a/1"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + - c: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0240.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0240.yaml new file mode 100644 index 000000000000..d62ec2855df0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0240.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: jsonremove/object with number keys + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({"a": [{"1": ["b", "c", "d"]}, {"x": "y"}]}, {"a/0/1/2"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + - "1": + - b + - c + - x: "y" diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0241.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0241.yaml new file mode 100644 index 000000000000..0039fab5e182 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0241.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/arrays of roots + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {["a", "b", "c"], ["e"]}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0242.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0242.yaml new file mode 100644 index 000000000000..021b817a63c2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0242.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jsonremove/mixed root types + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + json.remove({"a": {"b": {"c": 7, "d": 8, "x": 0}}, "e": 9}, {"a/b/d", ["a", "b", "c"]}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + x: 0 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0243.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0243.yaml new file mode 100644 index 000000000000..9634b64fa9f9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0243.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: jsonremove/error invalid target type string input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + json.remove(__local2__, {"a/b/c"}, __local1__) + __local0__ = __local1__ + } + input_term: '{"x": "foo"}' + want_error_code: eval_type_error + want_error: "json.remove: operand 1 must be object but got string" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0244.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0244.yaml new file mode 100644 index 000000000000..f8dbb53119b1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0244.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/error invalid target type number input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + json.remove(__local2__, {"a/b/c"}, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": 22}' + want_error_code: eval_type_error + want_error: "json.remove: operand 1 must be object but got number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0245.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0245.yaml new file mode 100644 index 000000000000..53f6ee0323e7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0245.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/error invalid target type boolean input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + json.remove(__local2__, {"a/b/c"}, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": true}' + want_error_code: eval_type_error + want_error: "json.remove: operand 1 must be object but got boolean" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0246.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0246.yaml new file mode 100644 index 000000000000..bf89312415fd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0246.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/error invalid target type array input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + json.remove(__local2__, {"a/b/c"}, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": ["a", "b", "c"]}' + want_error_code: eval_type_error + want_error: "json.remove: operand 1 must be object but got array" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0247.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0247.yaml new file mode 100644 index 000000000000..816fcd62760d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0247.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/error invalid paths type string + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": "foo"}' + want_error_code: eval_type_error + want_error: "json.remove: operand 2 must be one of {set, array} but got string" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0248.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0248.yaml new file mode 100644 index 000000000000..a3c8d0fd65df --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0248.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/error invalid paths type number + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": 22}' + want_error_code: eval_type_error + want_error: "json.remove: operand 2 must be one of {set, array} but got number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0249.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0249.yaml new file mode 100644 index 000000000000..7d39e367e71d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0249.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/error invalid paths type boolean + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": true}' + want_error_code: eval_type_error + want_error: "json.remove: operand 2 must be one of {set, array} but got boolean" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0250.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0250.yaml new file mode 100644 index 000000000000..be6a11d46734 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0250.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/error invalid paths type object + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": {"y": 123}}' + want_error_code: eval_type_error + want_error: "json.remove: operand 2 must be one of {set, array} but got object" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0251.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0251.yaml new file mode 100644 index 000000000000..e4ba83853b72 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0251.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/error invalid paths type set with numbers + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": {1, 2, 3, "a"}}' + want_error_code: eval_type_error + want_error: "json.remove: operand 2 must be one of {set, array} containing string paths or array of path segments but got number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0252.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0252.yaml new file mode 100644 index 000000000000..1c6a5d89badd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0252.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/error invalid paths type set with objects + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": {"a", {"x": 1}, {"y": 2}}}' + want_error_code: eval_type_error + want_error: "json.remove: operand 2 must be one of {set, array} containing string paths or array of path segments but got object" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0253.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0253.yaml new file mode 100644 index 000000000000..1fb25029decf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0253.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: jsonremove/error invalid paths type array with numbers + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + input_term: '{"x": ["a", 1, 2, 3]}' + want_error_code: eval_type_error + want_error: "json.remove: operand 2 must be one of {set, array} containing string paths or array of path segments but got number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0254.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0254.yaml new file mode 100644 index 000000000000..0c95f20f841f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremove/test-jsonremove-0254.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jsonremove/error invalid paths type array with objects + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + json.remove({"a": {"b": {"c": 123}}}, __local2__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": ["a", {"x": 1}, {"y": 2}]}' + want_error_code: eval_type_error + want_error: "json.remove: operand 2 must be one of {set, array} containing string paths or array of path segments but got object" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonremoveidempotent/test-jsonremoveidempotent-0255.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonremoveidempotent/test-jsonremoveidempotent-0255.yaml new file mode 100644 index 000000000000..779cdfd40b3c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonremoveidempotent/test-jsonremoveidempotent-0255.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: jsonremoveidempotent/TestBuiltinJSONRemoveIdempotent + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = {"a": {"b": 2, "c": 3}} + json.remove(__local0__, {"a"}, __local1__) + __local1__ = {} + json.remove(__local0__, {"a/b"}, __local2__) + __local2__ = {"a": {"c": 3}} + json.remove(__local0__, {"a/c"}, __local3__) + __local3__ = {"a": {"b": 2}} + __local0__ = {"a": {"b": 2, "c": 3}} + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonschema/test-json-match_schema.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonschema/test-json-match_schema.yaml new file mode 100644 index 000000000000..b65a4cd7869f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonschema/test-json-match_schema.yaml @@ -0,0 +1,98 @@ +--- +cases: + - note: json_match_schema/success + query: data.test.p = x + modules: + - | + package test + + document := {"id": 5} + + schema := { + "properties": {"id": {"type": "integer"}}, + "required": ["id"], + } + + p := json.match_schema(document, schema) + want_result: + - x: + - true + - [] + - note: json_match_schema/success string document + query: data.test.p = x + modules: + - | + package test + + document := `{"id": 5}` + + schema := { + "properties": {"id": {"type": "integer"}}, + "required": ["id"], + } + + p := json.match_schema(document, schema) + want_result: + - x: + - true + - [] + - note: json_match_schema/success string schema + query: data.test.p = x + modules: + - | + package test + + document := {"id": 5} + + schema := `{ + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + }` + + p := json.match_schema(document, schema) + want_result: + - x: + - true + - [] + - note: json_match_schema/invalid document + query: data.test.p = x + modules: + - | + package test + + document := {"id": "foo"} + + schema := { + "properties": {"id": {"type": "integer"}}, + "required": ["id"], + } + + p := json.match_schema(document, schema) + want_result: + - x: + - false + - - desc: "Invalid type. Expected: integer, given: string" + error: "id: Invalid type. Expected: integer, given: string" + field: id + type: invalid_type + - note: json_match_schema/invalid schema + query: data.test.p = x + modules: + - | + package test + + document := {"id": "foo"} + + schema := { + "properties": {"id": {"type": "unknown"}}, + "required": ["id"], + } + + p := json.match_schema(document, schema) + want_error_code: eval_builtin_error + want_error: "json.match_schema: has a primitive type that is NOT VALID -- given: /unknown/ Expected valid values are:[array boolean integer number null object string]" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jsonschema/test-json-verify_schema.yaml b/third_party/opa/v1/test/cases/testdata/v1/jsonschema/test-json-verify_schema.yaml new file mode 100644 index 000000000000..8729a2d168ee --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jsonschema/test-json-verify_schema.yaml @@ -0,0 +1,54 @@ +--- +cases: + - note: json_verify_schema/valid schema string + query: data.test.p = x + modules: + - | + package test + + schema := `{"type": "boolean"}` + + p := json.verify_schema(schema) + want_result: + - x: + - true + - null + - note: json_verify_schema/valid schema object + query: data.test.p = x + modules: + - | + package test + + schema := {"type": "boolean"} + + p := json.verify_schema(schema) + want_result: + - x: + - true + - null + - note: json_verify_schema/invalid schema string + query: data.test.p = x + modules: + - | + package test + + schema := `{"type": "unknown_type"}` + + p := json.verify_schema(schema) + want_result: + - x: + - false + - "jsonschema: has a primitive type that is NOT VALID -- given: /unknown_type/ Expected valid values are:[array boolean integer number null object string]" + - note: json_verify_schema/invalid schema object + query: data.test.p = x + modules: + - | + package test + + schema := {"type": "unknown_type"} + + p := json.verify_schema(schema) + want_result: + - x: + - false + - "jsonschema: has a primitive type that is NOT VALID -- given: /unknown_type/ Expected valid values are:[array boolean integer number null object string]" diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0389.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0389.yaml new file mode 100644 index 000000000000..b0acdef1302f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0389.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jwtbuiltins/simple + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0.XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + data: {} + want_result: + - x: + - alg: HS256 + typ: JWT + - iss: opa + sub: "0" + - 5e65682e81c8de9c4cb4c3bf59138d3122731940cff690e3cbc269d3fb5d4576 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0390.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0390.yaml new file mode 100644 index 000000000000..21ead7b7f5fc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0390.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jwtbuiltins/simple-non-registered + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuZXciOiJJIGFtIGEgdXNlciBjcmVhdGVkIGZpZWxkIiwiaXNzIjoib3BhIn0.6UmjsclVDGD9jcmX_F8RJzVgHtUZuLu2pxkF_UEQCrE", [x, y, z]) + } + data: {} + want_result: + - x: + - alg: HS256 + typ: JWT + - iss: opa + new: I am a user created field + - e949a3b1c9550c60fd8dc997fc5f112735601ed519b8bbb6a71905fd41100ab1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0391.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0391.yaml new file mode 100644 index 000000000000..70e09d009f00 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0391.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtbuiltins/no-support-jwe + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImVuYyI6ImJsYWgifQ.eyJuZXciOiJJIGFtIGEgdXNlciBjcmVhdGVkIGZpZWxkIiwiaXNzIjoib3BhIn0.McGUb1e-UviZKy6UyQErNNQzEUgeV25Buwk7OHOa8U8", [x, y, z]) + } + data: {} + want_error_code: eval_builtin_error + want_error: JWT is a JWE object, which is not supported + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0392.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0392.yaml new file mode 100644 index 000000000000..43701c15645e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0392.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtbuiltins/no-periods + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + data: {} + want_error_code: eval_builtin_error + want_error: encoded JWT had no period separators + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0393.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0393.yaml new file mode 100644 index 000000000000..f902ad1aafb1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0393.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtbuiltins/wrong-period-count + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXV.CJ9eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + data: {} + want_error_code: eval_builtin_error + want_error: encoded JWT must have 3 sections, found 2 + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0394.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0394.yaml new file mode 100644 index 000000000000..6a410cff6d97 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0394.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtbuiltins/bad-header-encoding + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode("eyJhbGciOiJIU^%zI1NiI+sInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0.XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + data: {} + want_error_code: eval_builtin_error + want_error: "JWT header had invalid encoding: illegal base64 data at input byte 13" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0395.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0395.yaml new file mode 100644 index 000000000000..738653e32edc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0395.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtbuiltins/bad-payload-encoding + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwia/XNzIjoib3BhIn0.XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + data: {} + want_error_code: eval_builtin_error + want_error: "JWT payload had invalid encoding: illegal base64 data at input byte 17" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0396.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0396.yaml new file mode 100644 index 000000000000..fcd66300d03f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0396.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtbuiltins/bad-signature-encoding + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwiaXNzIjoib3BhIn0.XmVoLoHI3pxMtMO(_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + data: {} + want_error_code: eval_builtin_error + want_error: "JWT signature had invalid encoding: illegal base64 data at input byte 15" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0397.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0397.yaml new file mode 100644 index 000000000000..8f447f12d45b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0397.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jwtbuiltins/nested + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.ImV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUp6ZFdJaU9pSXdJaXdpYVhOeklqb2liM0JoSW4wLlhtVm9Mb0hJM3B4TXRNT19XUk9OTVNKekdVRFA5cERqeThKcDBfdGRSWFki.8W0qx4mLxslmZl7wEMUWBxH7tST3XsEuWXxesXqFnRI", [x, y, z]) + } + data: {} + want_result: + - x: + - alg: HS256 + typ: JWT + - iss: opa + sub: "0" + - 5e65682e81c8de9c4cb4c3bf59138d3122731940cff690e3cbc269d3fb5d4576 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0398.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0398.yaml new file mode 100644 index 000000000000..331240d61064 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0398.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jwtbuiltins/double-nested + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.ImV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNJc0ltTjBlU0k2SWtwWFZDSjkuSW1WNVNtaGlSMk5wVDJsS1NWVjZTVEZPYVVselNXNVNOV05EU1RaSmEzQllWa05LT1M1bGVVcDZaRmRKYVU5cFNYZEphWGRwWVZoT2VrbHFiMmxpTTBKb1NXNHdMbGh0Vm05TWIwaEpNM0I0VFhSTlQxOVhVazlPVFZOS2VrZFZSRkE1Y0VScWVUaEtjREJmZEdSU1dGa2kuOFcwcXg0bUx4c2xtWmw3d0VNVVdCeEg3dFNUM1hzRXVXWHhlc1hxRm5SSSI.U8rwnGAJ-bJoGrAYKEzNtbJQWd3x1eW0Y25nLKHDCgo", [x, y, z]) + } + data: {} + want_result: + - x: + - alg: HS256 + typ: JWT + - iss: opa + sub: "0" + - 5e65682e81c8de9c4cb4c3bf59138d3122731940cff690e3cbc269d3fb5d4576 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0399.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0399.yaml new file mode 100644 index 000000000000..c9cbc26f905e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0399.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: jwtbuiltins/complex-values + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwIiwiaXNzIjoib3BhIiwiZXh0Ijp7ImFiYyI6IjEyMyIsImNiYSI6WzEwLCIxMCJdfX0.IIxF-uJ6i4K5Dj71xNLnUeqB9jmujl6ujTInhii1PxE", [x, y, z]) + } + data: {} + want_result: + - x: + - alg: HS256 + typ: JWT + - ext: + abc: "123" + cba: + - 10 + - "10" + iss: opa + sub: "0" + - 208c45fae27a8b82b90e3ef5c4d2e751ea81f639ae8e5eae8d32278628b53f11 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0400.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0400.yaml new file mode 100644 index 000000000000..674376056566 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtbuiltins/test-jwtbuiltins-0400.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: jwtbuiltins/duplicate-keys + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiAiMCIsImlzcyI6ICJub3Qgb3BhIiwgImlzcyI6ICJhbHNvIG5vdCBvcGEiLCAiaXNzIjogIm9wYSJ9.XmVoLoHI3pxMtMO_WRONMSJzGUDP9pDjy8Jp0_tdRXY", [x, y, z]) + } + data: {} + want_result: + - x: + - alg: HS256 + typ: JWT + - iss: opa + sub: "0" + - 5e65682e81c8de9c4cb4c3bf59138d3122731940cff690e3cbc269d3fb5d4576 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0449.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0449.yaml new file mode 100644 index 000000000000..2d14730c0900 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0449.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: jwtdecodeverify/ps256-unconstrained + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - true + - alg: PS256 + typ: JWT + - iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0450.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0450.yaml new file mode 100644 index 000000000000..afe149f3252b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0450.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/ps256-key-wrong + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0451.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0451.yaml new file mode 100644 index 000000000000..26d986034774 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0451.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/rs256-key-wrong + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImV4cCI6IDMwMDB9.hqDP3AzshNhUZMI02U3nLPrj93QFrgs-74XFrF1Vry2bplrz-NKpdVdfTu8iY_bhmkWf2Om5DdwRZj2ZgpGahtnshnHaRq0RyqF-m3Y7oNj6JL_YMwgxsFIIHtBlagBqDU-gZK99iqSOSGqVhvxqX6gCqFgE7vnEGHeeDedtRM53coAJuwzy8rQV9m3TewoofPdPasGv-dBLQZ3qgmnibkSgb7SmFpjXBy8zL3xJXOZhAHYlgcmcEoFVaWlBguIcWA87WZlpCLYcdYTJzSZweC3QLUhZ4RLJW84-LMKp6xWLLPrp3OgnsduB2G9PYMmYw_qCkuY1KGwfH4PvCQbAzQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0452.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0452.yaml new file mode 100644 index 000000000000..684a8f6ad069 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0452.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: jwtdecodeverify/ps256-iss-ok + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "iss": "xxx"}, [x, y, z]) + } + want_result: + - x: + - true + - alg: PS256 + typ: JWT + - iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0453.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0453.yaml new file mode 100644 index 000000000000..bfdbb1b27577 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0453.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/ps256-iss-wrong + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "iss": "yyy"}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0454.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0454.yaml new file mode 100644 index 000000000000..47b1916ca6fc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0454.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: jwtdecodeverify/ps256-alg-ok + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"alg": "PS256", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - true + - alg: PS256 + typ: JWT + - iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0455.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0455.yaml new file mode 100644 index 000000000000..d9f9b815f0e2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0455.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/ps256-alg-wrong + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"alg": "RS256", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0456.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0456.yaml new file mode 100644 index 000000000000..c44bf505f86b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0456.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jwtdecodeverify/rs256-exp-ok + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImV4cCI6IDMwMDB9.hqDP3AzshNhUZMI02U3nLPrj93QFrgs-74XFrF1Vry2bplrz-NKpdVdfTu8iY_bhmkWf2Om5DdwRZj2ZgpGahtnshnHaRq0RyqF-m3Y7oNj6JL_YMwgxsFIIHtBlagBqDU-gZK99iqSOSGqVhvxqX6gCqFgE7vnEGHeeDedtRM53coAJuwzy8rQV9m3TewoofPdPasGv-dBLQZ3qgmnibkSgb7SmFpjXBy8zL3xJXOZhAHYlgcmcEoFVaWlBguIcWA87WZlpCLYcdYTJzSZweC3QLUhZ4RLJW84-LMKp6xWLLPrp3OgnsduB2G9PYMmYw_qCkuY1KGwfH4PvCQbAzQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "time": 2000000000000}, [x, y, z]) + } + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - exp: 3000 + iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0457.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0457.yaml new file mode 100644 index 000000000000..8b30730547a2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0457.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/rs256-exp-expired + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImV4cCI6IDMwMDB9.hqDP3AzshNhUZMI02U3nLPrj93QFrgs-74XFrF1Vry2bplrz-NKpdVdfTu8iY_bhmkWf2Om5DdwRZj2ZgpGahtnshnHaRq0RyqF-m3Y7oNj6JL_YMwgxsFIIHtBlagBqDU-gZK99iqSOSGqVhvxqX6gCqFgE7vnEGHeeDedtRM53coAJuwzy8rQV9m3TewoofPdPasGv-dBLQZ3qgmnibkSgb7SmFpjXBy8zL3xJXOZhAHYlgcmcEoFVaWlBguIcWA87WZlpCLYcdYTJzSZweC3QLUhZ4RLJW84-LMKp6xWLLPrp3OgnsduB2G9PYMmYw_qCkuY1KGwfH4PvCQbAzQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "time": 4000000000000}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0458.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0458.yaml new file mode 100644 index 000000000000..cc7d3711f34c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0458.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/rs256-exp-now-expired + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImV4cCI6IDMwMDB9.hqDP3AzshNhUZMI02U3nLPrj93QFrgs-74XFrF1Vry2bplrz-NKpdVdfTu8iY_bhmkWf2Om5DdwRZj2ZgpGahtnshnHaRq0RyqF-m3Y7oNj6JL_YMwgxsFIIHtBlagBqDU-gZK99iqSOSGqVhvxqX6gCqFgE7vnEGHeeDedtRM53coAJuwzy8rQV9m3TewoofPdPasGv-dBLQZ3qgmnibkSgb7SmFpjXBy8zL3xJXOZhAHYlgcmcEoFVaWlBguIcWA87WZlpCLYcdYTJzSZweC3QLUhZ4RLJW84-LMKp6xWLLPrp3OgnsduB2G9PYMmYw_qCkuY1KGwfH4PvCQbAzQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0459.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0459.yaml new file mode 100644 index 000000000000..4a6fa9a2b2e8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0459.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: jwtdecodeverify/rs256-exp-now-explicit-expired + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + now := time.now_ns() + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImV4cCI6IDMwMDB9.hqDP3AzshNhUZMI02U3nLPrj93QFrgs-74XFrF1Vry2bplrz-NKpdVdfTu8iY_bhmkWf2Om5DdwRZj2ZgpGahtnshnHaRq0RyqF-m3Y7oNj6JL_YMwgxsFIIHtBlagBqDU-gZK99iqSOSGqVhvxqX6gCqFgE7vnEGHeeDedtRM53coAJuwzy8rQV9m3TewoofPdPasGv-dBLQZ3qgmnibkSgb7SmFpjXBy8zL3xJXOZhAHYlgcmcEoFVaWlBguIcWA87WZlpCLYcdYTJzSZweC3QLUhZ4RLJW84-LMKp6xWLLPrp3OgnsduB2G9PYMmYw_qCkuY1KGwfH4PvCQbAzQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "time": now}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0460.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0460.yaml new file mode 100644 index 000000000000..613807292cb2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0460.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jwtdecodeverify/rs256-nbf-ok + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJuYmYiOiAxMDAwLCAiaXNzIjogInh4eCJ9.cwwYDfJhU_ambPIpwBJwDek05miffoudprr41IAYsl0IKekb1ii2uEgwkNM-LJtVXHe9hsK3gANFyfqoJuCZIBvaNMx_3Z0BUdeBs4k1UwBiZCpuud0ofgHKURwvehNgqDvRfchq_-K_Agi2iRdl0oShgLjN-gVbBl8pRwUbQrvASlcsCpZIKUyOzXNtaIZEFh1z6ISDy8UHHOdoieKpN23swya7QAcEb0wXEEKMkkhiRd5QHgWLk37Lnw2K89mKcq4Om0CtV9nHrxxmpYGSMPojCy16Gjdg5-xKyJWvxCfb3YUBUVM4RWa7ICOPRJWPuHxu9pPYG63hb_qDU6NLsw", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "time": 2000000000000}, [x, y, z]) + } + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - iss: xxx + nbf: 1000 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0461.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0461.yaml new file mode 100644 index 000000000000..00251cc5ea7a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0461.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jwtdecodeverify/rs256-nbf-now-ok + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJuYmYiOiAxMDAwLCAiaXNzIjogInh4eCJ9.cwwYDfJhU_ambPIpwBJwDek05miffoudprr41IAYsl0IKekb1ii2uEgwkNM-LJtVXHe9hsK3gANFyfqoJuCZIBvaNMx_3Z0BUdeBs4k1UwBiZCpuud0ofgHKURwvehNgqDvRfchq_-K_Agi2iRdl0oShgLjN-gVbBl8pRwUbQrvASlcsCpZIKUyOzXNtaIZEFh1z6ISDy8UHHOdoieKpN23swya7QAcEb0wXEEKMkkhiRd5QHgWLk37Lnw2K89mKcq4Om0CtV9nHrxxmpYGSMPojCy16Gjdg5-xKyJWvxCfb3YUBUVM4RWa7ICOPRJWPuHxu9pPYG63hb_qDU6NLsw", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - iss: xxx + nbf: 1000 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0462.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0462.yaml new file mode 100644 index 000000000000..8ed612c9a29f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0462.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/rs256-nbf-toosoon + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJuYmYiOiAxMDAwLCAiaXNzIjogInh4eCJ9.cwwYDfJhU_ambPIpwBJwDek05miffoudprr41IAYsl0IKekb1ii2uEgwkNM-LJtVXHe9hsK3gANFyfqoJuCZIBvaNMx_3Z0BUdeBs4k1UwBiZCpuud0ofgHKURwvehNgqDvRfchq_-K_Agi2iRdl0oShgLjN-gVbBl8pRwUbQrvASlcsCpZIKUyOzXNtaIZEFh1z6ISDy8UHHOdoieKpN23swya7QAcEb0wXEEKMkkhiRd5QHgWLk37Lnw2K89mKcq4Om0CtV9nHrxxmpYGSMPojCy16Gjdg5-xKyJWvxCfb3YUBUVM4RWa7ICOPRJWPuHxu9pPYG63hb_qDU6NLsw", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", "time": 500000000000}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0463.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0463.yaml new file mode 100644 index 000000000000..10fa3395f026 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0463.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/rs256-alg-missing + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJ0eXAiOiAiSldUIiwgImtpZCI6ICJrMSJ9.eyJpc3MiOiAieHh4IiwgInN1YiI6ICJmcmVkIn0.J4J4FgUD_P5fviVVjgvQWJDg-5XYTP_tHCwB3kSlYVKv8vmnZRNh4ke68OxfMP96iM-LZswG2fNqe-_piGIMepF5rCe1iIWAuz3qqkxfS9YVF3hvwoXhjJT0yIgrDMl1lfW5_XipNshZoxddWK3B7dnVW74MFazEEFuefiQm3PdMUX8jWGsmfgPnqBIZTizErNhoIMuRvYaVM1wA2nfrpVGONxMTaw8T0NRwYIuZwubbnNQ1yLhI0y3dsZvQ_lrh9Khtk9fS1V3SRh7aa9AvferJ4T-48qn_V1m3sINPgoA-uLGyyu3k_GkXRYW1yGNC-MH4T2cwhj89WITbIhusgQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0464.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0464.yaml new file mode 100644 index 000000000000..bb843efc36c9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0464.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/rs256-crit-junk + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJjcml0IjogWyJqdW5rIl0sICJraWQiOiAiazEiLCAiYWxnIjogIlJTMjU2IiwgInR5cCI6ICJKV1QiLCAianVuayI6ICJ4eHgifQ.eyJpc3MiOiAieHh4IiwgInN1YiI6ICJmcmVkIn0.YfoUpW5CgDBtxtBuOix3cdYJGT8cX9Mq7wOhIbjDK7eRQUsAmMY_0EQPh7bd7Yi1gLI3e11BKzguf2EHqAa1kbkHWwFniBO-RIi8q42v2uxC4lpEpIjfaaXB5XmsLfAXtYRqh0AObvbSho6VDXBP_Kn81nhIiE2yFbH14_jhRMSxDBs5ToSkXV-XJHw5bONP8NxPqEk9KF3ZJGzN7J_KoD6LjqfYai5K0eLNEIZh4C1WjTdmCKMR4K6ieZRQWZiSsnhSqLSQERir4n22G3QsdY7dOnCp-SS4VYu3V-PfsOSFMvQ-TTAN1geqMZ9A7k1CCLW0wxKBs-KCiYzmRTzwxA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0465.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0465.yaml new file mode 100644 index 000000000000..47670c7d6c00 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0465.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: jwtdecodeverify/rsa256-nested + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCIsICJjdHkiOiAiSldUIn0.ZXlKaGJHY2lPaUFpVWxNeU5UWWlMQ0FpZEhsd0lqb2dJa3BYVkNKOS5leUpwYzNNaU9pQWllSGg0SW4wLnJSUnJlUU9DYW9ZLW1Nazcyak5GZVk1YVlFUWhJZ0lFdFZkUTlYblltUUwyTHdfaDdNbkk0U0VPMVBwa0JIVEpyZnljbEplTHpfalJ2UGdJMlcxaDFCNGNaVDhDZ21pVXdxQXI5c0puZHlVQ1FtSWRrbm53WkI5cXAtX3BTdGRHWEo5WnAzeEo4NXotVEJpWlN0QUNUZFdlUklGSUU3VkxPa20tRmxZdzh5OTdnaUN4TmxUdWl3amxlTjMwZDhnWHUxNkZGQzJTSlhtRjZKbXYtNjJHbERhLW1CWFZ0bGJVSTVlWVUwaTdueTNyQjBYUVQxRkt4ZUZ3OF85N09FdV9jY3VLcl82ZHlHZVFHdnQ5Y3JJeEFBMWFZbDdmbVBrNkVhcjllTTNKaGVYMi00Wkx0d1FOY1RDT01YV0dIck1DaG5MWVc4WEFrTHJEbl9yRmxUaVMtZw.Xicc2sWCZ_Nithucsw9XD7YOKrirUdEnH3MyiPM-Ck3vEU2RsTBsfU2JPhfjp3phc0VOgsAXCzwU5PwyNyUo1490q8YSym-liMyO2Lk-hjH5fAxoizg9yD4II_lK6Wz_Tnpc0bBGDLdbuUhvgvO7yqo-leBQlsfRXOvw4VSPSEy8QPtbURtbnLpWY2jGBKz7vGI_o4qDJ3PicG0kyEiWZNh3wjeeCYRCWvXN8qh7Uk5EA-8J5vX651GqV-7gmaX1n-8DXamhaCQcE-p1cjSj04-X-_bJlQtmb-TT3bSyUPxgHVncvxNUby8jkUTzfi5MMbmIzWWkxI5YtJTdtmCkPQ", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0466.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0466.yaml new file mode 100644 index 000000000000..cacd11e7a636 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0466.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: jwtdecodeverify/rsa256-nested2 + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCIsICJjdHkiOiAiSldUIn0.ZXlKaGJHY2lPaUFpVWxNeU5UWWlMQ0FpZEhsd0lqb2dJa3BYVkNJc0lDSmpkSGtpT2lBaVNsZFVJbjAuWlhsS2FHSkhZMmxQYVVGcFZXeE5lVTVVV1dsTVEwRnBaRWhzZDBscWIyZEphM0JZVmtOS09TNWxlVXB3WXpOTmFVOXBRV2xsU0dnMFNXNHdMbkpTVW5KbFVVOURZVzlaTFcxTmF6Y3lhazVHWlZrMVlWbEZVV2hKWjBsRmRGWmtVVGxZYmxsdFVVd3lUSGRmYURkTmJrazBVMFZQTVZCd2EwSklWRXB5Wm5samJFcGxUSHBmYWxKMlVHZEpNbGN4YURGQ05HTmFWRGhEWjIxcFZYZHhRWEk1YzBwdVpIbFZRMUZ0U1dScmJtNTNXa0k1Y1hBdFgzQlRkR1JIV0VvNVduQXplRW80TlhvdFZFSnBXbE4wUVVOVVpGZGxVa2xHU1VVM1ZreFBhMjB0Um14WmR6aDVPVGRuYVVONFRteFVkV2wzYW14bFRqTXdaRGhuV0hVeE5rWkdRekpUU2xodFJqWktiWFl0TmpKSGJFUmhMVzFDV0ZaMGJHSlZTVFZsV1ZVd2FUZHVlVE55UWpCWVVWUXhSa3Q0WlVaM09GODVOMDlGZFY5alkzVkxjbDgyWkhsSFpWRkhkblE1WTNKSmVFRkJNV0ZaYkRkbWJWQnJOa1ZoY2psbFRUTkthR1ZZTWkwMFdreDBkMUZPWTFSRFQwMVlWMGRJY2sxRGFHNU1XVmM0V0VGclRISkVibDl5Um14VWFWTXRady5YaWNjMnNXQ1pfTml0aHVjc3c5WEQ3WU9LcmlyVWRFbkgzTXlpUE0tQ2szdkVVMlJzVEJzZlUySlBoZmpwM3BoYzBWT2dzQVhDendVNVB3eU55VW8xNDkwcThZU3ltLWxpTXlPMkxrLWhqSDVmQXhvaXpnOXlENElJX2xLNld6X1RucGMwYkJHRExkYnVVaHZndk83eXFvLWxlQlFsc2ZSWE92dzRWU1BTRXk4UVB0YlVSdGJuTHBXWTJqR0JLejd2R0lfbzRxREozUGljRzBreUVpV1pOaDN3amVlQ1lSQ1d2WE44cWg3VWs1RUEtOEo1dlg2NTFHcVYtN2dtYVgxbi04RFhhbWhhQ1FjRS1wMWNqU2owNC1YLV9iSmxRdG1iLVRUM2JTeVVQeGdIVm5jdnhOVWJ5OGprVVR6Zmk1TU1ibUl6V1dreEk1WXRKVGR0bUNrUFE.ODBVH_gooCLJxtPVr1MjJC1syG4MnVUFP9LkI9pSaj0QABV4vpfqrBshHn8zOPgUTDeHwbc01Qy96cQlTMQQb94YANmZyL1nzwmdR4piiGXMGSlcCNfDg1o8DK4msMSR-X-j2IkxBDB8rfeFSfLRMgDCjAF0JolW7qWmMD9tBmFNYAjly4vMwToOXosDmFLl5eqyohXDf-3Ohljm5kIjtyMWkt5S9EVuwlIXh2owK5l59c4-TH29gkuaZ3uU4LFPjD7XKUrlOQnEMuu2QD8LAqTyxbnY4JyzUWEvyTM1dVmGnFpLKCg9QBly__y1u2ffhvDsHyuCmEKAbhPE98YvFA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0467.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0467.yaml new file mode 100644 index 000000000000..0291e940e4cd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0467.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: jwtdecodeverify/es256-unconstrained + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiRVMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.JvbTLBF06FR70gb7lCbx_ojhp4bk9--B_aULgNlYM0fYf9OSawaqBQp2lwW6FADFtRJ2WFUk5g0zwVOUlnrlzw", {"cert": "-----BEGIN CERTIFICATE-----\nMIIBcDCCARagAwIBAgIJAMZmuGSIfvgzMAoGCCqGSM49BAMCMBMxETAPBgNVBAMM\nCHdoYXRldmVyMB4XDTE4MDgxMDE0Mjg1NFoXDTE4MDkwOTE0Mjg1NFowEzERMA8G\nA1UEAwwId2hhdGV2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATPwn3WCEXL\nmjp/bFniDwuwsfu7bASlPae2PyWhqGeWwe23Xlyx+tSqxlkXYe4pZ23BkAAscpGj\nyn5gXHExyDlKo1MwUTAdBgNVHQ4EFgQUElRjSoVgKjUqY5AXz2o74cLzzS8wHwYD\nVR0jBBgwFoAUElRjSoVgKjUqY5AXz2o74cLzzS8wDwYDVR0TAQH/BAUwAwEB/zAK\nBggqhkjOPQQDAgNIADBFAiEA4yQ/88ZrUX68c6kOe9G11u8NUaUzd8pLOtkKhniN\nOHoCIHmNX37JOqTcTzGn2u9+c8NlnvZ0uDvsd1BmKPaUmjmm\n-----END CERTIFICATE-----\n"}, [x, y, z]) + } + want_result: + - x: + - true + - alg: ES256 + typ: JWT + - iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0468.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0468.yaml new file mode 100644 index 000000000000..c8d7281d54f2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0468.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: jwtdecodeverify/hs256-unconstrained + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM", {"secret": "secret"}, [x, y, z]) + } + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - azp: alice + hr: false + subordinates: [] + user: alice diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0469.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0469.yaml new file mode 100644 index 000000000000..b9965b4b01d0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0469.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/hs256-key-wrong + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM", {"secret": "the wrong key"}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0470.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0470.yaml new file mode 100644 index 000000000000..89e88327fe74 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0470.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jwtdecodeverify/rs256-aud + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImF1ZCI6ICJmcmVkIn0.F-9m2Tx8r1tuQFirazsI4FK05bXX3uP4ut8M2FryJ07k3bQhy262fdwNDmuFcGx0NfL-c80agcwGoTzMWXkVEgZ2KTz0QSAdcdGk3ZWtUy-Mj2IilZ1dzkVvW8LsithYFTGcUtkelFDrJwtMQ0Kum7SXJpC_HCBk4PbftY0XD6jRgHLnQdeT9_J11L4sd19vCdpxxxm3_m_yvUV3ZynzB4vhQbS3CET4EClAVhi-m_gMh9mj85gY1ycIz6-FxWv8xM2Igm2SMeIdyJwAvEGnIauRS928P_OqVCZgCH2Pafnxtzy77Llpxy8XS0xu5PtPw3_azhg33GaXDCFsfz6GpA", {"aud": "fred", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - aud: fred + iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0471.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0471.yaml new file mode 100644 index 000000000000..cd39ceb6d4b4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0471.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: jwtdecodeverify/rs256-aud-list + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImF1ZCI6IFsiZnJlZCIsICJib2IiXX0.k8jW7PUiMkQCKCjnSFBFFKPDO0RXwZgVkLUwUfi8sMdrrcKi12LC8wd5fLBn0YraFtMXWKdMweKf9ZC-K33h5TK7kkTVKOXctF50mleMlUn0Up_XjtdP1v-2WOfivUXcexN1o-hu0kH7sSQnielXIjC2EAleG6A54YUOZFBdzvd1PKHlsxA7x2iiL73uGeFlyxoaMki8E5tx7FY6JGF1RdhWCoIV5A5J8QnwI5EetduJQ505U65Pk7UApWYWu4l2DT7KCCJa5dJaBvCBemVxWaBhCQWtJKU2ZgOEkpiK7b_HsdeRBmpG9Oi1o5mt5ybC09VxSD-lEda_iJO_7i042A", {"aud": "bob", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - aud: + - fred + - bob + iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0472.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0472.yaml new file mode 100644 index 000000000000..b570b936d910 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0472.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/ps256-no-aud + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4In0.iCePYnD1U13oBe_6ylhmojmkY_VZNYXqVszAej8RImMGv51OEqARmYFkRZYTiYCiVFober7vcDq_stOj1uAJCuttygGW_dpHiN-3EWsU2E2vCnXlygWe0ud38pOC-OVyEFbXxO9-m51vnS-3VmBjEO8G1UE8bLFXTeFOGkUIj9dqlefJSWh5wa8XA3g9mj0jqpuJi-7QgEIeVHk-JzhGpoFqI2f-Df_agVvc2x4V-6fJmj7wV2IsaFPRi36mVQmg8S-dkxu4AlaeCILhyNZl8ewjBHHBjJFRwzcy88L00mzdO51ZxEYsBdQav3ux2sc6vjT9PvvjAwzcthQxEoEaNA", {"aud": "cath", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0473.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0473.yaml new file mode 100644 index 000000000000..1b859b0e1b70 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0473.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/rs256-missing-aud + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImF1ZCI6ICJmcmVkIn0.F-9m2Tx8r1tuQFirazsI4FK05bXX3uP4ut8M2FryJ07k3bQhy262fdwNDmuFcGx0NfL-c80agcwGoTzMWXkVEgZ2KTz0QSAdcdGk3ZWtUy-Mj2IilZ1dzkVvW8LsithYFTGcUtkelFDrJwtMQ0Kum7SXJpC_HCBk4PbftY0XD6jRgHLnQdeT9_J11L4sd19vCdpxxxm3_m_yvUV3ZynzB4vhQbS3CET4EClAVhi-m_gMh9mj85gY1ycIz6-FxWv8xM2Igm2SMeIdyJwAvEGnIauRS928P_OqVCZgCH2Pafnxtzy77Llpxy8XS0xu5PtPw3_azhg33GaXDCFsfz6GpA", {"cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0474.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0474.yaml new file mode 100644 index 000000000000..74a55f961349 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0474.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/rs256-wrong-aud + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImF1ZCI6ICJmcmVkIn0.F-9m2Tx8r1tuQFirazsI4FK05bXX3uP4ut8M2FryJ07k3bQhy262fdwNDmuFcGx0NfL-c80agcwGoTzMWXkVEgZ2KTz0QSAdcdGk3ZWtUy-Mj2IilZ1dzkVvW8LsithYFTGcUtkelFDrJwtMQ0Kum7SXJpC_HCBk4PbftY0XD6jRgHLnQdeT9_J11L4sd19vCdpxxxm3_m_yvUV3ZynzB4vhQbS3CET4EClAVhi-m_gMh9mj85gY1ycIz6-FxWv8xM2Igm2SMeIdyJwAvEGnIauRS928P_OqVCZgCH2Pafnxtzy77Llpxy8XS0xu5PtPw3_azhg33GaXDCFsfz6GpA", {"aud": "cath", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0475.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0475.yaml new file mode 100644 index 000000000000..ab1462698087 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0475.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/rs256-wrong-aud-list + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAieHh4IiwgImF1ZCI6IFsiZnJlZCIsICJib2IiXX0.k8jW7PUiMkQCKCjnSFBFFKPDO0RXwZgVkLUwUfi8sMdrrcKi12LC8wd5fLBn0YraFtMXWKdMweKf9ZC-K33h5TK7kkTVKOXctF50mleMlUn0Up_XjtdP1v-2WOfivUXcexN1o-hu0kH7sSQnielXIjC2EAleG6A54YUOZFBdzvd1PKHlsxA7x2iiL73uGeFlyxoaMki8E5tx7FY6JGF1RdhWCoIV5A5J8QnwI5EetduJQ505U65Pk7UApWYWu4l2DT7KCCJa5dJaBvCBemVxWaBhCQWtJKU2ZgOEkpiK7b_HsdeRBmpG9Oi1o5mt5ybC09VxSD-lEda_iJO_7i042A", {"aud": "cath", "cert": "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----"}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0476.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0476.yaml new file mode 100644 index 000000000000..7a984f4b5d9c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0476.yaml @@ -0,0 +1,46 @@ +--- +cases: + - note: jwtdecodeverify/multiple-keys-one-valid + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify( + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.ZcLZbBKpPFFz8YGD2jEbXzwHT7DWtqRVk1PTV-cAWUV8jr6f2a--Fw9SFR3vSbrtFif06AQ3aWY7PMM2AuxDjiUVGjItmHRz0sJBEijcE2QVkDN7MNK3Kk1fsM_hbEXzNCzChZpEkTZnLy9ijkJJFD0j6lBat4lO5Zc_LC2lXUftV_hU2aW9mQ7pLSgJjItzRymivnN0g-WUDq5IPK_M8b3yPy_N9iByj8B2FO0sC3TuOrXWbrYrX4ve4bAaSqOFOXiL5Z5BJfmmtT--xKdWDGJxnei8lbv7in7t223fVsUpsH-zmybp529Fya37BsaIlcgLrl38ghvoqy2sHu2wAA", { + "cert": `{ + "keys": [ + { + "kty": "EC", + "use": "sig", + "crv": "P-256", + "kid": "k1", + "x": "9Qq5S5VqMQoH-FOI4atcH6V3bua03C-5ZMZMG1rszwA", + "y": "LLbFxWkGBEBrTm1GMYZJy1OXCH1KLweJMCgIEPIsibU", + "alg": "ES256" + }, + { + "kty": "RSA", + "e": "AQAB", + "use": "enc", + "kid": "k2", + "alg": "RS256", + "n": "sGu-fYVE2nq2dPxJlqAMI0Z8G3FD0XcWDnD8mkfO1ddKRGuUQZmfj4gWeZGyIk3cnuoy7KJCEqa3daXc08QHuFZyfn0rH33t8_AFsvb0q0i7R2FK-Gdqs_E0-sGpYMsRJdZWfCioLkYjIHEuVnRbi3DEsWqe484rEGbKF60jNRgGC4b-8pz-E538ZkssWxcqHrYIj5bjGEU36onjS3M_yrTuNvzv_8wRioK4fbcwmGne9bDxu8LcoSReWpPn0CnUkWnfqroRcMJnC87ZuJagDW1ZWCmU3psdsVanmFFh0DP6z0fsA4h8G2n9-qp-LEKFaWwo3IWlOsIzU3MHdcEiGw" + } + ] + }`, + "time": 1574723450396363500, + }, + [x, y, z], + ) + } + want_result: + - x: + - true + - alg: RS256 + typ: JWT + - admin: true + iat: 1516239022 + name: John Doe + sub: "1234567890" diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0477.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0477.yaml new file mode 100644 index 000000000000..1b9fa99da878 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0477.yaml @@ -0,0 +1,42 @@ +--- +cases: + - note: jwtdecodeverify/multiple-keys-no-valid + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify( + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.G051ZlKno4XdDz4pdPthPKH1cKlFqkREvx_dHhl6kwM", { + "cert": `{ + "keys": [ + { + "kty": "EC", + "use": "sig", + "crv": "P-256", + "kid": "k1", + "x": "9Qq5S5VqMQoH-FOI4atcH6V3bua03C-5ZMZMG1rszwA", + "y": "LLbFxWkGBEBrTm1GMYZJy1OXCH1KLweJMCgIEPIsibU", + "alg": "ES256" + }, + { + "kty": "RSA", + "e": "AQAB", + "use": "enc", + "kid": "k2", + "alg": "RS256", + "n": "sGu-fYVE2nq2dPxJlqAMI0Z8G3FD0XcWDnD8mkfO1ddKRGuUQZmfj4gWeZGyIk3cnuoy7KJCEqa3daXc08QHuFZyfn0rH33t8_AFsvb0q0i7R2FK-Gdqs_E0-sGpYMsRJdZWfCioLkYjIHEuVnRbi3DEsWqe484rEGbKF60jNRgGC4b-8pz-E538ZkssWxcqHrYIj5bjGEU36onjS3M_yrTuNvzv_8wRioK4fbcwmGne9bDxu8LcoSReWpPn0CnUkWnfqroRcMJnC87ZuJagDW1ZWCmU3psdsVanmFFh0DP6z0fsA4h8G2n9-qp-LEKFaWwo3IWlOsIzU3MHdcEiGw" + } + ] + }`, + "time": 1574723450396363500, + }, + [x, y, z], + ) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0478.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0478.yaml new file mode 100644 index 000000000000..85b5b861be11 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0478.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-nbf + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjEwMDAuMX0.8ab0xurlRs_glclA3Sm7OMQgwkQvE4HuLsfMOc4nVO8", {"secret": "secret", "time": 2000000000000.1}, [x, y, z]) + } + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - iss: xxx + nbf: 1000.1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0479.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0479.yaml new file mode 100644 index 000000000000..c70e54fbd7dc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0479.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-nbf-not-valid + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjMwMDAuMX0.khHsSae91zHwuaTIvszln3kyrOdPyUYiGSvCI0j2ie8", {"secret": "secret", "time": 2000000000000.1}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0480.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0480.yaml new file mode 100644 index 000000000000..63cddc9f56ad --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0480.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-exp-valid + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjMwMDAuMiwiaXNzIjoieHh4In0.XUen7GtDmICV3O1ngsoO-tQrjrXtOgJI06oGW0nQSIM", {"secret": "secret", "time": 2000000000000.1}, [x, y, z]) + } + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - exp: 3000.2 + iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0481.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0481.yaml new file mode 100644 index 000000000000..c82eba813e06 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0481.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-exp-expired + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjMwMDAuMiwiaXNzIjoieHh4In0.XUen7GtDmICV3O1ngsoO-tQrjrXtOgJI06oGW0nQSIM", {"secret": "secret", "time": 4000000000000.1}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0482.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0482.yaml new file mode 100644 index 000000000000..7e15d3928e56 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0482.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-nbf-one-tenth-second-before + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjEuNTg5Mzg1NzcwMTIzNGUrMDl9.lvrsV1nam-BZr0SomWwsr4dBfu6BDrR2FzQ1iS_Xnrw", {"secret": "secret", "time": 1589385770023400000}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0483.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0483.yaml new file mode 100644 index 000000000000..756cfd44aeda --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0483.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-nbf-equal + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjEuNTg5Mzg1NzcwMTIzNGUrMDl9.lvrsV1nam-BZr0SomWwsr4dBfu6BDrR2FzQ1iS_Xnrw", {"secret": "secret", "time": 1589385770123400000}, [x, y, z]) + } + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - iss: xxx + nbf: 1589385770.1234 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0484.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0484.yaml new file mode 100644 index 000000000000..78a70a838380 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0484.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-one-millisecond-after-nbf + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjEuNTg5Mzg1NzcwMTIzNGUrMDl9.lvrsV1nam-BZr0SomWwsr4dBfu6BDrR2FzQ1iS_Xnrw", {"secret": "secret", "time": 1589385770124400000}, [x, y, z]) + } + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - iss: xxx + nbf: 1589385770.1234 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0485.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0485.yaml new file mode 100644 index 000000000000..db4c3b3a59db --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0485.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-one-tenth-second-after-nbf + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjEuNTg5Mzg1NzcwMTIzNGUrMDl9.lvrsV1nam-BZr0SomWwsr4dBfu6BDrR2FzQ1iS_Xnrw", {"secret": "secret", "time": 1589385770223400000}, [x, y, z]) + } + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - iss: xxx + nbf: 1589385770.1234 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0486.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0486.yaml new file mode 100644 index 000000000000..74fa244c2c6e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0486.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-one-second-after-nbf + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4eHgiLCJuYmYiOjEuNTg5Mzg1NzcwMTIzNGUrMDl9.lvrsV1nam-BZr0SomWwsr4dBfu6BDrR2FzQ1iS_Xnrw", {"secret": "secret", "time": 1589385771123400000}, [x, y, z]) + } + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - iss: xxx + nbf: 1589385770.1234 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0487.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0487.yaml new file mode 100644 index 000000000000..e7774c7dcdc2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0487.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-one-second-before-exp + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuNTg5Mzg1NzcxMTIzNGUrMDksImlzcyI6Inh4eCJ9.PZ2z6VfHt9YdvHHUbilkTnw4R9TK3_V0LV1h-q0k9xg", {"secret": "secret", "time": 1589385770123400000}, [x, y, z]) + } + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - exp: 1589385771.1234 + iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0488.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0488.yaml new file mode 100644 index 000000000000..69c47501f97e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0488.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-one-tenth-second-before-exp + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuNTg5Mzg1NzcxMTIzNGUrMDksImlzcyI6Inh4eCJ9.PZ2z6VfHt9YdvHHUbilkTnw4R9TK3_V0LV1h-q0k9xg", {"secret": "secret", "time": 1589385771023400000}, [x, y, z]) + } + want_result: + - x: + - true + - alg: HS256 + typ: JWT + - exp: 1589385771.1234 + iss: xxx diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0489.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0489.yaml new file mode 100644 index 000000000000..80a417f9c327 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0489.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-equal-exp + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuNTg5Mzg1NzcxMTIzNGUrMDksImlzcyI6Inh4eCJ9.PZ2z6VfHt9YdvHHUbilkTnw4R9TK3_V0LV1h-q0k9xg", {"secret": "secret", "time": 1589385771123400000}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0490.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0490.yaml new file mode 100644 index 000000000000..9a750281d8c8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0490.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-one-tenth-second-after-exp + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuNTg5Mzg1NzcxMTIzNGUrMDksImlzcyI6Inh4eCJ9.PZ2z6VfHt9YdvHHUbilkTnw4R9TK3_V0LV1h-q0k9xg", {"secret": "secret", "time": 1589385771223400000}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0491.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0491.yaml new file mode 100644 index 000000000000..17ca6c824d9a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-0491.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/hs256-float-one-second-after-exp + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + io.jwt.decode_verify("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuNTg5Mzg1NzcxMTIzNGUrMDksImlzcyI6Inh4eCJ9.PZ2z6VfHt9YdvHHUbilkTnw4R9TK3_V0LV1h-q0k9xg", {"secret": "secret", "time": 1589385772123400000}, [x, y, z]) + } + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-invalid-exp-type.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-invalid-exp-type.yaml new file mode 100644 index 000000000000..2e1421383a53 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-invalid-exp-type.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtdecodeverify/invalid-exp + query: data.generated.decoded = x + modules: + - | + package generated + + token := io.jwt.encode_sign({"alg": "HS256"}, {"exp": null}, {"kty": "oct", "k": base64url.encode_no_pad("42")}) + + decoded := io.jwt.decode_verify(token, {"secret": "42"}) + want_error_code: eval_builtin_error + want_error: exp value must be a number + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-invalid-nbf-type.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-invalid-nbf-type.yaml new file mode 100644 index 000000000000..4fdc5c0d3aaf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-invalid-nbf-type.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtdecodeverify/invalid-nbf + query: data.generated.decoded = x + modules: + - | + package generated + + token := io.jwt.encode_sign({"alg": "HS256"}, {"nbf": "string is not valid here"}, {"kty": "oct", "k": base64url.encode_no_pad("42")}) + + decoded := io.jwt.decode_verify(token, {"secret": "42"}) + want_error_code: eval_builtin_error + want_error: nbf value must be a number + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-missing-iss-while-required.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-missing-iss-while-required.yaml new file mode 100644 index 000000000000..8be54fa55e89 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtdecodeverify/test-jwtdecodeverify-missing-iss-while-required.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: jwtdecodeverify/missing-iss-while-required + query: data.generated.decoded = x + modules: + - | + package generated + + token := io.jwt.encode_sign({"alg": "HS256"}, {}, {"kty": "oct", "k": base64url.encode_no_pad("42")}) + + decoded := io.jwt.decode_verify(token, {"secret": "42", "iss": "xxx"}) + want_result: + - x: + - false + - {} + - {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-0492.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-0492.yaml new file mode 100644 index 000000000000..66d7f6425694 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-0492.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtencodesign/https://tools.ietf.org/html/rfc7515#appendix-A.1 + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.encode_sign({"alg": "HS256", "typ": "JWT"}, {"aud": ["bob", "saul"], "exp": 1300819380, "http://example.com/is_root": true, "iss": "joe", "privateParams": {"private_one": "one", "private_two": "two"}}, {"k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", "kty": "oct"}, x) + } + data: {} + want_result: + - x: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYm9iIiwic2F1bCJdLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UiLCJwcml2YXRlUGFyYW1zIjp7InByaXZhdGVfb25lIjoib25lIiwicHJpdmF0ZV90d28iOiJ0d28ifX0.-Or2eol8bzly-Ztb0v7_7UkcKBkN_aNNpK33HK0MeOY diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-0493.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-0493.yaml new file mode 100644 index 000000000000..984d95d465df --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-0493.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtencodesign/Empty JSON payload + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.encode_sign({"alg": "HS256", "typ": "JWT"}, {}, {"k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", "kty": "oct"}, x) + } + data: {} + want_result: + - x: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.6cvao8lnOu6FAdK68jQFcDMXOmaWNwWiYhCgijd-AD8 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-0494.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-0494.yaml new file mode 100644 index 000000000000..1522aebb352f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-0494.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtencodesign/https://tools.ietf.org/html/rfc7515#appendix-A.2 + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.encode_sign({"alg": "RS256"}, {"aud": ["bob", "saul"], "exp": 1300819380, "http://example.com/is_root": true, "iss": "joe", "privateParams": {"private_one": "one", "private_two": "two"}}, {"d": "Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ", "dp": "BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0", "dq": "h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU", "e": "AQAB", "kty": "RSA", "n": "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", "p": "4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc", "q": "uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc", "qi": "IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U"}, x) + } + data: {} + want_result: + - x: eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsiYm9iIiwic2F1bCJdLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UiLCJwcml2YXRlUGFyYW1zIjp7InByaXZhdGVfb25lIjoib25lIiwicHJpdmF0ZV90d28iOiJ0d28ifX0.kvCjo2z80h4YO3umCn3iayUXEnBGgw-Nk4-cYNPWagW4SAg34nZSonUt9Kpnc5h0s6LpJAnam1xuEezk-2VRPnu05foQdWTDKJAze-9vZ0wr0L_KyXLZaW0vVfehdGpPgJj_FqVfgnc-hXdL0RADfrKjApsrO7oHpv-Ii3u7oKwauzq5oQ2AXt4cp6ahu8VhOBGQiPXtV98Yw3U3NBetMX-I_qR6-UGsN6Z0pRO0bsdk1ANvMiHT6Y04x8nh4kzk5pfv71ACgnxrs1zxwg7YfYzm2tQXGceu7fwI9sqO_jz2c8RvMsg4u8q0js58F-gR1SVjmTyipnXv0tAGzpZnhg diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-integer-timestamps.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-integer-timestamps.yaml new file mode 100644 index 000000000000..3532106e7d4e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-integer-timestamps.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: jwtencodesign/plain integer timestamps + query: data.test.p = x + modules: + - | + package test + + p := x if { + now_ns := 1.678e15 + iat := now_ns / 1e6 + exp := iat + 300 + io.jwt.encode_sign({"alg": "HS256", "typ": "JWT"}, {"iat": iat, "exp": exp}, {"k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", "kty": "oct"}, x) + } + data: {} + want_result: + - x: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NzgwMDAzMDAsImlhdCI6MTY3ODAwMDAwMH0.ZNCOrxE5MNdrqzHmiQ7c3so0IvGqHddBZFWe3kBaQHg diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-set-data.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-set-data.yaml new file mode 100644 index 000000000000..16a1bb860ca5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesign/test-jwtencodesign-set-data.yaml @@ -0,0 +1,17 @@ +--- +cases: + - data: {} + modules: + - | + package test + + p := io.jwt.encode_sign( + {"alg": "HS256", "typ": "JWT"}, + # aud is a set and should be converted to a list + {"aud": {"bob", "saul"}, "exp": 1300819380, "http://example.com/is_root": true, "iss": "joe", "privateParams": {"private_one": "one", "private_two": "two"}}, + {"k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", "kty": "oct"} + ) + query: data.test.p = x + note: set data + want_result: + - x: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYm9iIiwic2F1bCJdLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJpc3MiOiJqb2UiLCJwcml2YXRlUGFyYW1zIjp7InByaXZhdGVfb25lIjoib25lIiwicHJpdmF0ZV90d28iOiJ0d28ifX0.-Or2eol8bzly-Ztb0v7_7UkcKBkN_aNNpK33HK0MeOY diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0379.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0379.yaml new file mode 100644 index 000000000000..7ca1b1a95a30 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0379.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtencodesignheadererrors/Unknown signature algorithm + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.encode_sign_raw("{\"typ:\"JWT\",\r\n \"alg\":\"HS256\"}", "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: invalid character + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0380.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0380.yaml new file mode 100644 index 000000000000..a791126e8efc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0380.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtencodesignheadererrors/unknown signature algorithm + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.encode_sign_raw("{\"alg\":\"dummy\"}", "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: unknown signature algorithm + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0381.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0381.yaml new file mode 100644 index 000000000000..a72212ee8323 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0381.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtencodesignheadererrors/Empty JSON header Error + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.encode_sign_raw("{}", "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: unsupported signature algorithm + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0382.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0382.yaml new file mode 100644 index 000000000000..623d0d249673 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0382.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtencodesignheadererrors/Empty headers input error + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.encode_sign_raw("", "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: unexpected end of JSON input + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0383.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0383.yaml new file mode 100644 index 000000000000..5f6d9aa6acca --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignheadererrors/test-jwtencodesignheadererrors-0383.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtencodesignheadererrors/No JSON Error + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.encode_sign_raw("e", "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: invalid character + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0376.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0376.yaml new file mode 100644 index 000000000000..b6c3ef9ce44e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0376.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtencodesignpayloaderrors/No Payload + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.encode_sign_raw("{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}", "", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: type is JWT but payload is not JSON + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0377.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0377.yaml new file mode 100644 index 000000000000..b496bfb0c385 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0377.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtencodesignpayloaderrors/Payload JSON Error + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.encode_sign_raw("{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}", "{\"iss:\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: type is JWT but payload is not JSON + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0378.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0378.yaml new file mode 100644 index 000000000000..8232fef32945 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignpayloaderrors/test-jwtencodesignpayloaderrors-0378.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtencodesignpayloaderrors/Non JSON Error + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.encode_sign_raw("{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}", "e", "{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: type is JWT but payload is not JSON + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0384.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0384.yaml new file mode 100644 index 000000000000..2d6aa72bbb2a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0384.yaml @@ -0,0 +1,9 @@ +--- +cases: + - note: jwtencodesignraw/https://tools.ietf.org/html/rfc7515#appendix-A.1 + query: data.generated.p = x + modules: + - "package generated\n\np := x if {\n\tio.jwt.encode_sign_raw(\n\t\t`{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}`,\n\t\t`{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}`,\n\t\t`{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}`,\n\t\tx,\n\t)\n}\n" + data: {} + want_result: + - x: eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0385.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0385.yaml new file mode 100644 index 000000000000..ccbad926a534 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0385.yaml @@ -0,0 +1,9 @@ +--- +cases: + - note: jwtencodesignraw/No Payload but Media Type is Plain + query: data.generated.p = x + modules: + - "package generated\n\np := x if {\n\tio.jwt.encode_sign_raw(\n\t\t`{\"typ\":\"text/plain\",\r\n \"alg\":\"HS256\"}`,\n\t\t``, `{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}`,\n\t\tx,\n\t)\n}\n" + data: {} + want_result: + - x: eyJ0eXAiOiJ0ZXh0L3BsYWluIiwNCiAiYWxnIjoiSFMyNTYifQ..sXoGQMWwM-SmX495-htA7kndgbkwz1PnqsDeY275gnI diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0386.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0386.yaml new file mode 100644 index 000000000000..5de5a453bca0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0386.yaml @@ -0,0 +1,9 @@ +--- +cases: + - note: jwtencodesignraw/text/plain media type + query: data.generated.p = x + modules: + - "package generated\n\np := x if {\n\tio.jwt.encode_sign_raw(\n\t\t`{\"typ\":\"text/plain\",\r\n \"alg\":\"HS256\"}`,\n\t\t`e`, `{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}`,\n\t\tx,\n\t)\n}\n" + data: {} + want_result: + - x: eyJ0eXAiOiJ0ZXh0L3BsYWluIiwNCiAiYWxnIjoiSFMyNTYifQ.ZQ.oO8Vnc4Jv7-J231a1bEcQrgXfKbNW-kEvVY7BP1v5rM diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0387.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0387.yaml new file mode 100644 index 000000000000..d2382fe5421f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0387.yaml @@ -0,0 +1,9 @@ +--- +cases: + - note: jwtencodesignraw/Empty JSON payload + query: data.generated.p = x + modules: + - "package generated\n\np := x if {\n\tio.jwt.encode_sign_raw(\n\t\t`{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}`,\n\t\t`{}`, `{\n\"kty\":\"oct\",\n\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"\n}`,\n\t\tx,\n\t)\n}\n" + data: {} + want_result: + - x: eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.e30.KAml6HRetE0sq22SYNh_CQExhf-X31ChYTfGwUBIWu8 diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0388.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0388.yaml new file mode 100644 index 000000000000..53e4a902be29 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtencodesignraw/test-jwtencodesignraw-0388.yaml @@ -0,0 +1,9 @@ +--- +cases: + - note: jwtencodesignraw/https://tools.ietf.org/html/rfc7515#appendix-A.2 + query: data.generated.p = x + modules: + - "package generated\n\np := x if {\n\tio.jwt.encode_sign_raw(\n\t\t`{\"alg\":\"RS256\"}`, `{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n \"http://example.com/is_root\":true}`,\n\t\t`{\n \"kty\":\"RSA\",\n \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ\",\n \"e\":\"AQAB\",\n \"d\":\"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ\",\n \"p\":\"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc\",\n \"q\":\"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc\",\n \"dp\":\"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0\",\n \"dq\":\"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU\",\n \"qi\":\"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U\"\n }`,\n\t\tx,\n\t)\n}\n" + data: {} + want_result: + - x: eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs256/test-jwtverifyhs256-0440.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs256/test-jwtverifyhs256-0440.yaml new file mode 100644 index 000000000000..fec96f2afd4b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs256/test-jwtverifyhs256-0440.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyhs256/success + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_hs256("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM", "secret", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs256/test-jwtverifyhs256-0441.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs256/test-jwtverifyhs256-0441.yaml new file mode 100644 index 000000000000..44bce3ad905e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs256/test-jwtverifyhs256-0441.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyhs256/failure-bad token + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_hs256("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.R0NDxM1gHTucWQKwayMDre2PbMNR9K9efmOfygDZWcE", "secret", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs256/test-jwtverifyhs256-0442.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs256/test-jwtverifyhs256-0442.yaml new file mode 100644 index 000000000000..2964ea3ec6c7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs256/test-jwtverifyhs256-0442.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtverifyhs256/failure-invalid token + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_hs256("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0", "secret", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: encoded JWT must have 3 sections, found 2 + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs384/test-jwtverifyhs384-0443.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs384/test-jwtverifyhs384-0443.yaml new file mode 100644 index 000000000000..99adbbf53d30 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs384/test-jwtverifyhs384-0443.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyhs384/success + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_hs384("eyJhbGciOiJIUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.g98lHYzuqINVppLMoEZT7jlpX0IBSo9zKGoN9DhQg7Ua3YjLXbJMjzESjIHXOGLB", "secret", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs384/test-jwtverifyhs384-0444.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs384/test-jwtverifyhs384-0444.yaml new file mode 100644 index 000000000000..e81c93310114 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs384/test-jwtverifyhs384-0444.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyhs384/failure-bad token + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_hs384("eyJhbGciOiJIUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.g98lHYzuqINVppLMoEZT7jlpX0IBSo9zKGoN9DhQg7Ua3YjLXbJMjzESjIHXOBAD", "secret", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs384/test-jwtverifyhs384-0445.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs384/test-jwtverifyhs384-0445.yaml new file mode 100644 index 000000000000..f4e65dd5ceab --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs384/test-jwtverifyhs384-0445.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtverifyhs384/failure-invalid token + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_hs384("eyJhbGciOiJIUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0", "secret", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: encoded JWT must have 3 sections, found 2 + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs512/test-jwtverifyhs512-0446.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs512/test-jwtverifyhs512-0446.yaml new file mode 100644 index 000000000000..2cc7d069ea25 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs512/test-jwtverifyhs512-0446.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyhs512/success + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_hs512("eyJhbGciOiJIUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.F6-xviRhK2OLcJJHFivhQqMN_dgX5boDrwbVKkdo9flQQNk-AaKpH3uYycFvBEd_erVefcsri_PkL4fjLSZ7ZA", "secret", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs512/test-jwtverifyhs512-0447.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs512/test-jwtverifyhs512-0447.yaml new file mode 100644 index 000000000000..5cd7528db161 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs512/test-jwtverifyhs512-0447.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyhs512/failure-bad token + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_hs512("eyJhbGciOiJIUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.F6-xviRhK2OLcJJHFivhQqMN_dgX5boDrwbVKkdo9flQQNk-AaKpH3uYycFvBEd_erVefcsri_PkL4fjLSZBAD", "secret", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs512/test-jwtverifyhs512-0448.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs512/test-jwtverifyhs512-0448.yaml new file mode 100644 index 000000000000..f401a49d10f4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyhs512/test-jwtverifyhs512-0448.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtverifyhs512/failure-invalid token + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_hs512("eyJhbGciOiJIUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0", "secret", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: encoded JWT must have 3 sections, found 2 + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0401.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0401.yaml new file mode 100644 index 000000000000..4e6de79c4f45 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0401.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-cert + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0402.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0402.yaml new file mode 100644 index 000000000000..467626dbac2b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0402.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-cert + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7nJwME0QNM6g0Ou9SyljlcIY4cnBcs8oWVHe74bJ7JTgYmDOk2CA14RE3wJNkUKERP/cRdesKDA/BToJXJUroYvhjXxUYn+i3wK5vOGRY9WUtTF9paIIpIV4USUOwDh3ufhA9K3tyh+ZVsqn80em0Lj2ME0EgScuk6u0/UYjjNvcmnQl+uDmghG8xBZh7TZW2+aceMwlb4LJIP36VRhgjKQGIxg2rW8ROXgJaFbNRCbiOUUqlq9SUZuhHo8TNOARXXxp9R4Fq7Cl7ZbwWtNPwAtM1y+Z+iyu/i91m0YLlU2XBOGLu9IA8IZjPlbCnk/SygpV9NNwTY9DSQ0QfXcPTGlsbFwzRzTlhH25wEl3j+2Ub9w/NX7Yo+j/Ei9eGZ8cq0bcvEwDeIo98HeNZWrLUUArayRYvh8zutOlzqehw8waFk9AxpfEp9oWekSz8gZw9OL773EhnglYxxjkPHNzk66CufLuTEf6uE9NLE5HnlQMbiqBFirIyAWGKyU3v2tphKvcogxmzzWA51p0GY0lGZvlLNt2NrJv2oGecyl3BLqHnBi+rGAosa/8XgfQT8RIk7YR/tDPDmPfaqSIc0po+NcHYEH82Yv+gfKSK++1fyssGCsSRJs8PFMuPGgv62fFrE/EHSsHJaNWojSYce/Trxm2RaHhw/8O4oKcfrbaRf8CAwEAAQ==\n-----END PUBLIC KEY-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0403.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0403.yaml new file mode 100644 index 000000000000..69b837fb6c05 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0403.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-jwk + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", `{"kty":"RSA","e":"AQAB","kid":"4db88b6b-cda9-4242-b79e-51346edc313c","n":"7nJwME0QNM6g0Ou9SyljlcIY4cnBcs8oWVHe74bJ7JTgYmDOk2CA14RE3wJNkUKERP_cRdesKDA_BToJXJUroYvhjXxUYn-i3wK5vOGRY9WUtTF9paIIpIV4USUOwDh3ufhA9K3tyh-ZVsqn80em0Lj2ME0EgScuk6u0_UYjjNvcmnQl-uDmghG8xBZh7TZW2-aceMwlb4LJIP36VRhgjKQGIxg2rW8ROXgJaFbNRCbiOUUqlq9SUZuhHo8TNOARXXxp9R4Fq7Cl7ZbwWtNPwAtM1y-Z-iyu_i91m0YLlU2XBOGLu9IA8IZjPlbCnk_SygpV9NNwTY9DSQ0QfXcPTGlsbFwzRzTlhH25wEl3j-2Ub9w_NX7Yo-j_Ei9eGZ8cq0bcvEwDeIo98HeNZWrLUUArayRYvh8zutOlzqehw8waFk9AxpfEp9oWekSz8gZw9OL773EhnglYxxjkPHNzk66CufLuTEf6uE9NLE5HnlQMbiqBFirIyAWGKyU3v2tphKvcogxmzzWA51p0GY0lGZvlLNt2NrJv2oGecyl3BLqHnBi-rGAosa_8XgfQT8RIk7YR_tDPDmPfaqSIc0po-NcHYEH82Yv-gfKSK--1fyssGCsSRJs8PFMuPGgv62fFrE_EHSsHJaNWojSYce_Trxm2RaHhw_8O4oKcfrbaRf8"}`, x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0404.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0404.yaml new file mode 100644 index 000000000000..873d6f1f9e3e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0404.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-ps256-cert + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_ps256("eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJQUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiZm9vIjogImJhciJ9.i0F3MHWzOsBNLqjQzK1UVeQid9xPMowCoUsoM-C2BDxUY-FMKmCeJ1NJ4TGnS9HzFK1ftEvRnPT7EOxOkHPoCk1rz3feTFgtHtNzQqLM1IBTnz6aHHOrda_bKPHH9ZIYCRQUPXhpC90ivW_IJR-f7Z1WLrMXaJ71i1XteruENHrJJJDn0HedHG6N0VHugBHrak5k57cbE31utAdx83TEd8v2Y8wAkCJXKrdmTa-8419LNxW_yjkvoDD53n3X5CHhYkSymU77p0v6yWO38qDWeKJ-Fm_PrMAo72_rizDBj_yPa5LA3bT_EnsgZtC-sp8_SCDIH41bjiCGpRHhqgZmyw", "-----BEGIN CERTIFICATE-----\nMIIC/DCCAeSgAwIBAgIJAJRvYDU3ei3EMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCHdoYXRldmVyMB4XDTE4MDgxMDEwMzgxNloXDTE4MDkwOTEwMzgxNlowEzER\nMA8GA1UEAwwId2hhdGV2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC4kCmzLMW/5jzkzkmN7Me8wPD+ymBUIjsGqliGfMrfFfDV2eTPVtZcYD3IXoB4\nAOUT7XJzWjOsBRFOcVKKEiCPjXiLcwLb/QWQ1x0Budft32r3+N0KQd1rgcRHTPNc\nJoeWCfOgDPp51RTzTT6HQuV4ud+CDhRJP7QMVMIgal9Nuzs49LLZaBPW8/rFsHjk\nJQ4kDujSrpcT6F2FZY3SmWsOJgP7RjVKk5BheYeFKav5ZV4p6iHn/TN4RVpvpNBh\n5z/XoHITJ6lpkHSDpbIaQUTpobU2um8N3biz+HsEAmD9Laa27WUpYSpiM6DDMSXl\ndBDJdumerVRJvXYCtfXqtl17AgMBAAGjUzBRMB0GA1UdDgQWBBRz74MkVzT2K52/\nFJC4mTa9coM/DTAfBgNVHSMEGDAWgBRz74MkVzT2K52/FJC4mTa9coM/DTAPBgNV\nHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAD1ZE4IaIAetqGG+vt9oz1\nIx0j4EPok0ONyhhmiSsF6rSv8zlNWweVf5y6Z+AoTNY1Fym0T7dbpbqIox0EdKV3\nFLzniWOjznupbnqfXwHX/g1UAZSyt3akSatVhvNpGlnd7efTIAiNinX/TkzIjhZ7\nihMIZCGykT1P0ys1OaeEf57wAzviatD4pEMTIW0OOqY8bdRGhuJR1kKUZ/2Nm8Ln\ny7E0y8uODVbH9cAwGyzWB/QFc+bffNgi9uJaPQQc5Zxwpu9utlqyzFvXgV7MBYUK\nEYSLyxp4g4e5aujtLugaC8H6n9vP1mEBr/+T8HGynBZHNTKlDhhL9qDbpkkNB6/w\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0405.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0405.yaml new file mode 100644 index 000000000000..9b3f8413227d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0405.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-ps256-jwk + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_ps256("eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJQUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiZm9vIjogImJhciJ9.i0F3MHWzOsBNLqjQzK1UVeQid9xPMowCoUsoM-C2BDxUY-FMKmCeJ1NJ4TGnS9HzFK1ftEvRnPT7EOxOkHPoCk1rz3feTFgtHtNzQqLM1IBTnz6aHHOrda_bKPHH9ZIYCRQUPXhpC90ivW_IJR-f7Z1WLrMXaJ71i1XteruENHrJJJDn0HedHG6N0VHugBHrak5k57cbE31utAdx83TEd8v2Y8wAkCJXKrdmTa-8419LNxW_yjkvoDD53n3X5CHhYkSymU77p0v6yWO38qDWeKJ-Fm_PrMAo72_rizDBj_yPa5LA3bT_EnsgZtC-sp8_SCDIH41bjiCGpRHhqgZmyw", `{"kty":"RSA","e":"AQAB","kid":"bf688c97-bf51-49ba-b9d3-115195bb0eb8","n":"uJApsyzFv-Y85M5JjezHvMDw_spgVCI7BqpYhnzK3xXw1dnkz1bWXGA9yF6AeADlE-1yc1ozrAURTnFSihIgj414i3MC2_0FkNcdAbnX7d9q9_jdCkHda4HER0zzXCaHlgnzoAz6edUU800-h0LleLnfgg4UST-0DFTCIGpfTbs7OPSy2WgT1vP6xbB45CUOJA7o0q6XE-hdhWWN0plrDiYD-0Y1SpOQYXmHhSmr-WVeKeoh5_0zeEVab6TQYec_16ByEyepaZB0g6WyGkFE6aG1NrpvDd24s_h7BAJg_S2mtu1lKWEqYjOgwzEl5XQQyXbpnq1USb12ArX16rZdew"}`, x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0406.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0406.yaml new file mode 100644 index 000000000000..192feb2698f6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0406.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-es256-cert + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_es256("eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJFUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiaXNzIjogInh4eCJ9.lArczfN-pIL8oUU-7PU83u-zfXougXBZj6drFeKFsPEoVhy9WAyiZlRshYqjTSXdaw8yw2L-ovt4zTUZb2PWMg", "-----BEGIN CERTIFICATE-----\nMIIBcDCCARagAwIBAgIJAMZmuGSIfvgzMAoGCCqGSM49BAMCMBMxETAPBgNVBAMM\nCHdoYXRldmVyMB4XDTE4MDgxMDE0Mjg1NFoXDTE4MDkwOTE0Mjg1NFowEzERMA8G\nA1UEAwwId2hhdGV2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATPwn3WCEXL\nmjp/bFniDwuwsfu7bASlPae2PyWhqGeWwe23Xlyx+tSqxlkXYe4pZ23BkAAscpGj\nyn5gXHExyDlKo1MwUTAdBgNVHQ4EFgQUElRjSoVgKjUqY5AXz2o74cLzzS8wHwYD\nVR0jBBgwFoAUElRjSoVgKjUqY5AXz2o74cLzzS8wDwYDVR0TAQH/BAUwAwEB/zAK\nBggqhkjOPQQDAgNIADBFAiEA4yQ/88ZrUX68c6kOe9G11u8NUaUzd8pLOtkKhniN\nOHoCIHmNX37JOqTcTzGn2u9+c8NlnvZ0uDvsd1BmKPaUmjmm\n-----END CERTIFICATE-----\n", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0407.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0407.yaml new file mode 100644 index 000000000000..62c6a129c824 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0407.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-es256-jwk + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_es256("eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJFUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiaXNzIjogInh4eCJ9.lArczfN-pIL8oUU-7PU83u-zfXougXBZj6drFeKFsPEoVhy9WAyiZlRshYqjTSXdaw8yw2L-ovt4zTUZb2PWMg", `{"kty":"EC","crv":"P-256","x":"z8J91ghFy5o6f2xZ4g8LsLH7u2wEpT2ntj8loahnlsE","y":"7bdeXLH61KrGWRdh7ilnbcGQACxykaPKfmBccTHIOUo"}`, x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0408.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0408.yaml new file mode 100644 index 000000000000..abc2c411e4b8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0408.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/failure-bad token + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.Yt89BjaPCNgol478rYyH66-XgkHos02TsVwxLH3ZlvOoIVjbhYW8q1_MHehct1-yBf1UOX3g-lUrIjpoDtX1TfAESuaWTjYPixRvjfJ-Nn75JF8QuAl5PD27C6aJ4PjUPNfj0kwYBnNQ_oX-ZFb781xRi7qRDB6swE4eBUxzHqKUJBLaMM2r8k1-9iE3ERNeqTJUhV__p0aSyRj-i62rdZ4TC5nhxtWodiGP4e4GrYlXkdaKduK63cfdJF-kfZfTsoDs_xy84pZOkzlflxuNv9bNqd-3ISAdWe4gsEvWWJ8v70-QWkydnH8rhj95DaqoXrjfzbOgDpKtdxJC4daVPKvntykzrxKhZ9UtWzm3OvJSKeyWujFZlldiTfBLqNDgdi-Boj_VxO5Pdh-67lC3L-pBMm4BgUqf6rakBQvoH7AV6zD5CbFixh7DuqJ4eJHHItWzJwDctMrV3asm-uOE1E2B7GErGo3iX6S9Iun_kvRUp6kyvOaDq5VvXzQOKyLQIQyHGGs0aIV5cFI2IuO5Rt0uUj5mzPQrQWHgI4r6Mc5bzmq2QLxBQE8OJ1RFhRpsuoWQyDM8aRiMQIJe1g3x4dnxbJK4dYheYblKHFepScYqT1hllDp3oUNn89sIjQIhJTe8KFATu4K8ppluys7vhpE2a_tq8i5O0MFxWmsxN4Q", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0409.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0409.yaml new file mode 100644 index 000000000000..1d39eb187d7a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0409.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/failure-wrong key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_ps256("eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJQUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiZm9vIjogImJhciJ9.i0F3MHWzOsBNLqjQzK1UVeQid9xPMowCoUsoM-C2BDxUY-FMKmCeJ1NJ4TGnS9HzFK1ftEvRnPT7EOxOkHPoCk1rz3feTFgtHtNzQqLM1IBTnz6aHHOrda_bKPHH9ZIYCRQUPXhpC90ivW_IJR-f7Z1WLrMXaJ71i1XteruENHrJJJDn0HedHG6N0VHugBHrak5k57cbE31utAdx83TEd8v2Y8wAkCJXKrdmTa-8419LNxW_yjkvoDD53n3X5CHhYkSymU77p0v6yWO38qDWeKJ-Fm_PrMAo72_rizDBj_yPa5LA3bT_EnsgZtC-sp8_SCDIH41bjiCGpRHhqgZmyw", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0410.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0410.yaml new file mode 100644 index 000000000000..8f4a047c00da --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0410.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/failure-wrong alg + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_ps256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0411.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0411.yaml new file mode 100644 index 000000000000..db0c1e6f1bff --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0411.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtverifyrsa/failure-invalid token + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: encoded JWT must have 3 sections, found 2 + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0412.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0412.yaml new file mode 100644 index 000000000000..bedd20f0e8b0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0412.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtverifyrsa/failure-bad pem certificate block + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", "-----BEGIN CERT-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERT-----", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: failed to extract a Key from the PEM certificate + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0413.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0413.yaml new file mode 100644 index 000000000000..c26543d77a72 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0413.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtverifyrsa/failure-extra data after pem certificate block + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----\nEXTRA", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: extra data after a PEM certificate block + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0414.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0414.yaml new file mode 100644 index 000000000000..e04f9f94c458 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0414.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtverifyrsa/failure-bad pem certificate + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", "-----BEGIN CERTIFICATE-----\ndeadiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + data: {} + want_error_code: eval_builtin_error + want_error: failed to parse a PEM certificate + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0415.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0415.yaml new file mode 100644 index 000000000000..198ea070dece --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0415.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: jwtverifyrsa/failure-bad jwk key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs256("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g", `{"kty":"bogus key type","e":"AQAB","kid":"4db88b6b-cda9-4242-b79e-51346edc313c","n":"7nJwME0QNM6g0Ou9SyljlcIY4cnBcs8oWVHe74bJ7JTgYmDOk2CA14RE3wJNkUKERP_cRdesKDA_BToJXJUroYvhjXxUYn-i3wK5vOGRY9WUtTF9paIIpIV4USUOwDh3ufhA9K3tyh-ZVsqn80em0Lj2ME0EgScuk6u0_UYjjNvcmnQl-uDmghG8xBZh7TZW2-aceMwlb4LJIP36VRhgjKQGIxg2rW8ROXgJaFbNRCbiOUUqlq9SUZuhHo8TNOARXXxp9R4Fq7Cl7ZbwWtNPwAtM1y-Z-iyu_i91m0YLlU2XBOGLu9IA8IZjPlbCnk_SygpV9NNwTY9DSQ0QfXcPTGlsbFwzRzTlhH25wEl3j-2Ub9w_NX7Yo-j_Ei9eGZ8cq0bcvEwDeIo98HeNZWrLUUArayRYvh8zutOlzqehw8waFk9AxpfEp9oWekSz8gZw9OL773EhnglYxxjkPHNzk66CufLuTEf6uE9NLE5HnlQMbiqBFirIyAWGKyU3v2tphKvcogxmzzWA51p0GY0lGZvlLNt2NrJv2oGecyl3BLqHnBi-rGAosa_8XgfQT8RIk7YR_tDPDmPfaqSIc0po-NcHYEH82Yv-gfKSK--1fyssGCsSRJs8PFMuPGgv62fFrE_EHSsHJaNWojSYce_Trxm2RaHhw_8O4oKcfrbaRf8"}`, x) + } + data: {} + want_error_code: eval_builtin_error + want_error: failed to parse a JWK key (set) + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0416.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0416.yaml new file mode 100644 index 000000000000..e5cce64393df --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0416.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-cert + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs384("eyJhbGciOiJSUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.b__y2zjqMoD7iWbHeQ0lNpnche3ph5-AwrIQICLMQQGtEz9WMBteHydkC5g01bm3TBX1d04Z5IEOsuK6btAtWma04c5NYqaUyNEUJKYCFoY02uH0jGdGfL6R5Kkv0lkNvN0s3Nex9jMaVVgqx8bcrOU0uRBFT67sXcm11LHaB9BwKFslolzHClxgXy5RIZb4OFk_7Yk7xTC6PcvEWkkGR9uXBhfDEig5WqdwOWPeulimvARDw14U35rzeh9xpGAPjBKeE-y20fXAk0cSF1H69C-Qa1jDQheYIrAJ6XMYGNZWuay5-smmeefe67eweEt1q-AD1NFepqkmZX382DGuYQ", "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkSgAwIBAgIBLjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEV\nMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMD\nRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDg1MTAzWhcNMjAwNTA3\nMTA1MTAzWjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4w\nDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3Qw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeRmygX/fOOUu5Wm91PFNo\nsHDG1CzG9a1iKBjUeMgi9bXXScUfatPmsNlxb56uSi0RXUsvJmY/yxkIIhRyapxW\n49j2idAM3SGGL1nOZf/XdpDHYsAFFZ237HGb8DOEk/p3xCFv0tH/iQ+kLP36EM1+\ntn6BfUXdJnVyvkSK2iMNeRY7A4DMX7sGX39LXsVJiCokIC8E0QUFrSjvrAm9ejKE\ntPojydo4c3VUxLfmFuyMXoD3bfk1Jv5i2J5RjtomjgK6zNCvgYzpspiodHChkzlU\nX8yk2YqlAHX3XdJA94LaDE2kNXiOQnFkUb8GsP7hmEbwGtMUEQie+jfgKplxJ49B\nAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAM\nBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQC9f2/kxT7DnQ94ownhHvd6\nrzk1WirI90rFM2MxhfkaDrOHhSGZL9nDf6TIZ4qeFKZXthpKxpiZm2Oxmn+vUsik\nW6bYjq1nX0GCchQLaaFf9Jh1IOLwkfoBdX55tV8xUGHRWgDlCuGbqiixz+Bm0Kap\nkmbyJynVcoiKhdLyYm/YTn/pC32SJW666reQ+0qCAoxzLQowBetHjwDam9RsDEf4\n+JRDjYPutNXyJ5X8BaBA6PzHanzMG/7RFYcx/2YhXwVxdfPHku4ALJcddIGAGNx2\n5yte+HY0aEu+06J67eD9+4fU7NixRMKigk9KbjqpeWD+0be+VgX8Dot4jaISgI/3\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0417.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0417.yaml new file mode 100644 index 000000000000..778517bdaa60 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0417.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs384("eyJhbGciOiJSUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.b__y2zjqMoD7iWbHeQ0lNpnche3ph5-AwrIQICLMQQGtEz9WMBteHydkC5g01bm3TBX1d04Z5IEOsuK6btAtWma04c5NYqaUyNEUJKYCFoY02uH0jGdGfL6R5Kkv0lkNvN0s3Nex9jMaVVgqx8bcrOU0uRBFT67sXcm11LHaB9BwKFslolzHClxgXy5RIZb4OFk_7Yk7xTC6PcvEWkkGR9uXBhfDEig5WqdwOWPeulimvARDw14U35rzeh9xpGAPjBKeE-y20fXAk0cSF1H69C-Qa1jDQheYIrAJ6XMYGNZWuay5-smmeefe67eweEt1q-AD1NFepqkmZX382DGuYQ", "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3kZsoF/3zjlLuVpvdTxT\naLBwxtQsxvWtYigY1HjIIvW110nFH2rT5rDZcW+erkotEV1LLyZmP8sZCCIUcmqc\nVuPY9onQDN0hhi9ZzmX/13aQx2LABRWdt+xxm/AzhJP6d8Qhb9LR/4kPpCz9+hDN\nfrZ+gX1F3SZ1cr5EitojDXkWOwOAzF+7Bl9/S17FSYgqJCAvBNEFBa0o76wJvXoy\nhLT6I8naOHN1VMS35hbsjF6A9235NSb+YtieUY7aJo4CuszQr4GM6bKYqHRwoZM5\nVF/MpNmKpQB1913SQPeC2gxNpDV4jkJxZFG/BrD+4ZhG8BrTFBEInvo34CqZcSeP\nQQIDAQAB\n-----END PUBLIC KEY-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0418.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0418.yaml new file mode 100644 index 000000000000..6107dadda5b4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0418.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-jwk + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs384("eyJhbGciOiJSUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.b__y2zjqMoD7iWbHeQ0lNpnche3ph5-AwrIQICLMQQGtEz9WMBteHydkC5g01bm3TBX1d04Z5IEOsuK6btAtWma04c5NYqaUyNEUJKYCFoY02uH0jGdGfL6R5Kkv0lkNvN0s3Nex9jMaVVgqx8bcrOU0uRBFT67sXcm11LHaB9BwKFslolzHClxgXy5RIZb4OFk_7Yk7xTC6PcvEWkkGR9uXBhfDEig5WqdwOWPeulimvARDw14U35rzeh9xpGAPjBKeE-y20fXAk0cSF1H69C-Qa1jDQheYIrAJ6XMYGNZWuay5-smmeefe67eweEt1q-AD1NFepqkmZX382DGuYQ", `{"kty":"RSA","n":"3kZsoF_3zjlLuVpvdTxTaLBwxtQsxvWtYigY1HjIIvW110nFH2rT5rDZcW-erkotEV1LLyZmP8sZCCIUcmqcVuPY9onQDN0hhi9ZzmX_13aQx2LABRWdt-xxm_AzhJP6d8Qhb9LR_4kPpCz9-hDNfrZ-gX1F3SZ1cr5EitojDXkWOwOAzF-7Bl9_S17FSYgqJCAvBNEFBa0o76wJvXoyhLT6I8naOHN1VMS35hbsjF6A9235NSb-YtieUY7aJo4CuszQr4GM6bKYqHRwoZM5VF_MpNmKpQB1913SQPeC2gxNpDV4jkJxZFG_BrD-4ZhG8BrTFBEInvo34CqZcSePQQ","e":"AQAB"}`, x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0419.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0419.yaml new file mode 100644 index 000000000000..801ca3e44186 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0419.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/failure-wrong key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs384("eyJhbGciOiJSUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.b__y2zjqMoD7iWbHeQ0lNpnche3ph5-AwrIQICLMQQGtEz9WMBteHydkC5g01bm3TBX1d04Z5IEOsuK6btAtWma04c5NYqaUyNEUJKYCFoY02uH0jGdGfL6R5Kkv0lkNvN0s3Nex9jMaVVgqx8bcrOU0uRBFT67sXcm11LHaB9BwKFslolzHClxgXy5RIZb4OFk_7Yk7xTC6PcvEWkkGR9uXBhfDEig5WqdwOWPeulimvARDw14U35rzeh9xpGAPjBKeE-y20fXAk0cSF1H69C-Qa1jDQheYIrAJ6XMYGNZWuay5-smmeefe67eweEt1q-AD1NFepqkmZX382DGuYQ", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0420.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0420.yaml new file mode 100644 index 000000000000..1c1dc3fc5fe9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0420.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-cert + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs512("eyJhbGciOiJSUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VSe3qK5Gp0Q0_5nRgMFu25yw74FIgX-kXPOemSi62l-AxeVdUw8rOpEFrSTCaVjd3mPfKb-B056a-gtrbpXK9sUQnFdqdsyt8gHK-umz5lVyWfoAgj51Ontv-9K_pRORD9wqKqdTLZjCxJ5tyKoO0gY3SwwqSqGrp85vUjvEcK3jbMKINGRUNnOokeSm7byUEJsfKVUbPboSX1TGyvjDOZxxSITj8-bzZZ3F21DJ23N2IiJN7FW8Xj-SYyphXo-ML50o5bjW9YlQ5BDk-RW1I4eE-KpsxhApPv_xIgE8d89PVtXFuoJtv0yLRaZ1q04Fl9KNoMyZrmr349yppn0JlQ", "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkSgAwIBAgIBLjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEV\nMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMD\nRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDg1MTAzWhcNMjAwNTA3\nMTA1MTAzWjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4w\nDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3Qw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeRmygX/fOOUu5Wm91PFNo\nsHDG1CzG9a1iKBjUeMgi9bXXScUfatPmsNlxb56uSi0RXUsvJmY/yxkIIhRyapxW\n49j2idAM3SGGL1nOZf/XdpDHYsAFFZ237HGb8DOEk/p3xCFv0tH/iQ+kLP36EM1+\ntn6BfUXdJnVyvkSK2iMNeRY7A4DMX7sGX39LXsVJiCokIC8E0QUFrSjvrAm9ejKE\ntPojydo4c3VUxLfmFuyMXoD3bfk1Jv5i2J5RjtomjgK6zNCvgYzpspiodHChkzlU\nX8yk2YqlAHX3XdJA94LaDE2kNXiOQnFkUb8GsP7hmEbwGtMUEQie+jfgKplxJ49B\nAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAM\nBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQC9f2/kxT7DnQ94ownhHvd6\nrzk1WirI90rFM2MxhfkaDrOHhSGZL9nDf6TIZ4qeFKZXthpKxpiZm2Oxmn+vUsik\nW6bYjq1nX0GCchQLaaFf9Jh1IOLwkfoBdX55tV8xUGHRWgDlCuGbqiixz+Bm0Kap\nkmbyJynVcoiKhdLyYm/YTn/pC32SJW666reQ+0qCAoxzLQowBetHjwDam9RsDEf4\n+JRDjYPutNXyJ5X8BaBA6PzHanzMG/7RFYcx/2YhXwVxdfPHku4ALJcddIGAGNx2\n5yte+HY0aEu+06J67eD9+4fU7NixRMKigk9KbjqpeWD+0be+VgX8Dot4jaISgI/3\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0421.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0421.yaml new file mode 100644 index 000000000000..322e64c233a1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0421.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs512("eyJhbGciOiJSUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VSe3qK5Gp0Q0_5nRgMFu25yw74FIgX-kXPOemSi62l-AxeVdUw8rOpEFrSTCaVjd3mPfKb-B056a-gtrbpXK9sUQnFdqdsyt8gHK-umz5lVyWfoAgj51Ontv-9K_pRORD9wqKqdTLZjCxJ5tyKoO0gY3SwwqSqGrp85vUjvEcK3jbMKINGRUNnOokeSm7byUEJsfKVUbPboSX1TGyvjDOZxxSITj8-bzZZ3F21DJ23N2IiJN7FW8Xj-SYyphXo-ML50o5bjW9YlQ5BDk-RW1I4eE-KpsxhApPv_xIgE8d89PVtXFuoJtv0yLRaZ1q04Fl9KNoMyZrmr349yppn0JlQ", "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3kZsoF/3zjlLuVpvdTxT\naLBwxtQsxvWtYigY1HjIIvW110nFH2rT5rDZcW+erkotEV1LLyZmP8sZCCIUcmqc\nVuPY9onQDN0hhi9ZzmX/13aQx2LABRWdt+xxm/AzhJP6d8Qhb9LR/4kPpCz9+hDN\nfrZ+gX1F3SZ1cr5EitojDXkWOwOAzF+7Bl9/S17FSYgqJCAvBNEFBa0o76wJvXoy\nhLT6I8naOHN1VMS35hbsjF6A9235NSb+YtieUY7aJo4CuszQr4GM6bKYqHRwoZM5\nVF/MpNmKpQB1913SQPeC2gxNpDV4jkJxZFG/BrD+4ZhG8BrTFBEInvo34CqZcSeP\nQQIDAQAB\n-----END PUBLIC KEY-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0422.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0422.yaml new file mode 100644 index 000000000000..f400680207d5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0422.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-jwk + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs512("eyJhbGciOiJSUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VSe3qK5Gp0Q0_5nRgMFu25yw74FIgX-kXPOemSi62l-AxeVdUw8rOpEFrSTCaVjd3mPfKb-B056a-gtrbpXK9sUQnFdqdsyt8gHK-umz5lVyWfoAgj51Ontv-9K_pRORD9wqKqdTLZjCxJ5tyKoO0gY3SwwqSqGrp85vUjvEcK3jbMKINGRUNnOokeSm7byUEJsfKVUbPboSX1TGyvjDOZxxSITj8-bzZZ3F21DJ23N2IiJN7FW8Xj-SYyphXo-ML50o5bjW9YlQ5BDk-RW1I4eE-KpsxhApPv_xIgE8d89PVtXFuoJtv0yLRaZ1q04Fl9KNoMyZrmr349yppn0JlQ", `{"kty":"RSA","n":"3kZsoF_3zjlLuVpvdTxTaLBwxtQsxvWtYigY1HjIIvW110nFH2rT5rDZcW-erkotEV1LLyZmP8sZCCIUcmqcVuPY9onQDN0hhi9ZzmX_13aQx2LABRWdt-xxm_AzhJP6d8Qhb9LR_4kPpCz9-hDNfrZ-gX1F3SZ1cr5EitojDXkWOwOAzF-7Bl9_S17FSYgqJCAvBNEFBa0o76wJvXoyhLT6I8naOHN1VMS35hbsjF6A9235NSb-YtieUY7aJo4CuszQr4GM6bKYqHRwoZM5VF_MpNmKpQB1913SQPeC2gxNpDV4jkJxZFG_BrD-4ZhG8BrTFBEInvo34CqZcSePQQ","e":"AQAB"}`, x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0423.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0423.yaml new file mode 100644 index 000000000000..1d9659f64550 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0423.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/failure-wrong key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_rs512("eyJhbGciOiJSUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VSe3qK5Gp0Q0_5nRgMFu25yw74FIgX-kXPOemSi62l-AxeVdUw8rOpEFrSTCaVjd3mPfKb-B056a-gtrbpXK9sUQnFdqdsyt8gHK-umz5lVyWfoAgj51Ontv-9K_pRORD9wqKqdTLZjCxJ5tyKoO0gY3SwwqSqGrp85vUjvEcK3jbMKINGRUNnOokeSm7byUEJsfKVUbPboSX1TGyvjDOZxxSITj8-bzZZ3F21DJ23N2IiJN7FW8Xj-SYyphXo-ML50o5bjW9YlQ5BDk-RW1I4eE-KpsxhApPv_xIgE8d89PVtXFuoJtv0yLRaZ1q04Fl9KNoMyZrmr349yppn0JlQ", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0424.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0424.yaml new file mode 100644 index 000000000000..01c2830c5f2b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0424.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-ps384-cert + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_ps384("eyJhbGciOiJQUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.EHPUvPr6uJOYqdza95WbM1SYD8atZHJEVRggpwOWnHGsjQBoEarJb8QgW7TY22OXwGw2HWluTiyT_MAz02NaHRzZv6AgrmxCLChMWkCHLwPxqjs0xSvVAMLzHHq2X2Bcujo9KORGudR7zKz8pOX5Mfnm7Z6OGtqPCPLaIdVJlddNsG6a571NOuVuDWbcg0omeRDANZpCZMJeAQN2M-4Q61ef6zcQHK1R-QqzBhw6HzMgqR1LRJ0xbrmD-L5o53JM3pV1e1juKNXVK3vWkDQRCQORFn1lyH5isfSsiiHW-x90sUC7TrU_cOji4MMmOCME6kkwxe57ZgpeXtdVTvldpw", "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkSgAwIBAgIBKjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEV\nMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMD\nRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDkxMjU2WhcNMjAwNTA3\nMTExMjU2WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4w\nDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3Qw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtyVWH2FE8cU8LRcArH4Tw\nDhBOFcmJF28LrvRObcbDYsae6rEwby0aRgSxjTEMgyGBjroBSl22wOSA93kwx4pu\npfXEqbwywn9FhyKBb/OXQSglPmwrpmzQtPJGzBHncL+PPjRhPfqimwf7ZIPKAAgI\nz9O6ppGhE/x4Ct444jthUIBZuG5cUXhgiPBQdIQ3K88QhgVwcufTZkNHj4iSDfhl\nVFDHVjXjd2B/yGODjyv0TyChV0YBNGjMv7YFLWmIFUFzK+6qNSxu4czPtRkyfwaV\n2GW/PBT5f8fc6fKgQZ6k6BLK6+pi0iPh5TUizyHtqtueWDbrJ+wfdJilQS8D+EGr\nAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAM\nBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQBgVAM50/0aBTBxESYKIKN4\nE+qbV6aE0C+wYJLet0EWPTxwmFZamq5LNEO/D6xyoY5WHY60EyHRMs0agSB/ATBX\n5ULdEwh9G0NjxqivCcoddQ1fuVS2PrrqNL7VlRnYbTpd8/Dh4qnyl5FltlyZ/29L\ny7BWOwlcBlZdhsfH8svNX4PUxjRD+jmnczCDi7XSOKT8htKUV2ih1c9JrWpIhCi/\nHzEXkaAxNdhBNdIsLQMo3qq9fkSgNZQk9/ecJNPeuJ/UYyr5Xa4PxIWl4U+P7yuI\n+Q3FSPmUbiVsSGqMhh6V/DN8M+T5/KiSB47gFOfxc2/RR5aw4HkSp3WxwbT9njbE\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0425.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0425.yaml new file mode 100644 index 000000000000..ebb6907cf06e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0425.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-ps384-key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_ps384("eyJhbGciOiJQUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.EHPUvPr6uJOYqdza95WbM1SYD8atZHJEVRggpwOWnHGsjQBoEarJb8QgW7TY22OXwGw2HWluTiyT_MAz02NaHRzZv6AgrmxCLChMWkCHLwPxqjs0xSvVAMLzHHq2X2Bcujo9KORGudR7zKz8pOX5Mfnm7Z6OGtqPCPLaIdVJlddNsG6a571NOuVuDWbcg0omeRDANZpCZMJeAQN2M-4Q61ef6zcQHK1R-QqzBhw6HzMgqR1LRJ0xbrmD-L5o53JM3pV1e1juKNXVK3vWkDQRCQORFn1lyH5isfSsiiHW-x90sUC7TrU_cOji4MMmOCME6kkwxe57ZgpeXtdVTvldpw", "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7clVh9hRPHFPC0XAKx+E\n8A4QThXJiRdvC670Tm3Gw2LGnuqxMG8tGkYEsY0xDIMhgY66AUpdtsDkgPd5MMeK\nbqX1xKm8MsJ/RYcigW/zl0EoJT5sK6Zs0LTyRswR53C/jz40YT36opsH+2SDygAI\nCM/TuqaRoRP8eAreOOI7YVCAWbhuXFF4YIjwUHSENyvPEIYFcHLn02ZDR4+Ikg34\nZVRQx1Y143dgf8hjg48r9E8goVdGATRozL+2BS1piBVBcyvuqjUsbuHMz7UZMn8G\nldhlvzwU+X/H3OnyoEGepOgSyuvqYtIj4eU1Is8h7arbnlg26yfsH3SYpUEvA/hB\nqwIDAQAB\n-----END PUBLIC KEY-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0426.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0426.yaml new file mode 100644 index 000000000000..34d6057a9d77 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0426.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-ps384-jwk + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_ps384("eyJhbGciOiJQUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.EHPUvPr6uJOYqdza95WbM1SYD8atZHJEVRggpwOWnHGsjQBoEarJb8QgW7TY22OXwGw2HWluTiyT_MAz02NaHRzZv6AgrmxCLChMWkCHLwPxqjs0xSvVAMLzHHq2X2Bcujo9KORGudR7zKz8pOX5Mfnm7Z6OGtqPCPLaIdVJlddNsG6a571NOuVuDWbcg0omeRDANZpCZMJeAQN2M-4Q61ef6zcQHK1R-QqzBhw6HzMgqR1LRJ0xbrmD-L5o53JM3pV1e1juKNXVK3vWkDQRCQORFn1lyH5isfSsiiHW-x90sUC7TrU_cOji4MMmOCME6kkwxe57ZgpeXtdVTvldpw", `{"kty":"RSA","n":"7clVh9hRPHFPC0XAKx-E8A4QThXJiRdvC670Tm3Gw2LGnuqxMG8tGkYEsY0xDIMhgY66AUpdtsDkgPd5MMeKbqX1xKm8MsJ_RYcigW_zl0EoJT5sK6Zs0LTyRswR53C_jz40YT36opsH-2SDygAICM_TuqaRoRP8eAreOOI7YVCAWbhuXFF4YIjwUHSENyvPEIYFcHLn02ZDR4-Ikg34ZVRQx1Y143dgf8hjg48r9E8goVdGATRozL-2BS1piBVBcyvuqjUsbuHMz7UZMn8GldhlvzwU-X_H3OnyoEGepOgSyuvqYtIj4eU1Is8h7arbnlg26yfsH3SYpUEvA_hBqw","e":"AQAB"}`, x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0427.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0427.yaml new file mode 100644 index 000000000000..16ebe52403b8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0427.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/failure-ps384-wrong key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_ps384("eyJhbGciOiJQUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.EHPUvPr6uJOYqdza95WbM1SYD8atZHJEVRggpwOWnHGsjQBoEarJb8QgW7TY22OXwGw2HWluTiyT_MAz02NaHRzZv6AgrmxCLChMWkCHLwPxqjs0xSvVAMLzHHq2X2Bcujo9KORGudR7zKz8pOX5Mfnm7Z6OGtqPCPLaIdVJlddNsG6a571NOuVuDWbcg0omeRDANZpCZMJeAQN2M-4Q61ef6zcQHK1R-QqzBhw6HzMgqR1LRJ0xbrmD-L5o53JM3pV1e1juKNXVK3vWkDQRCQORFn1lyH5isfSsiiHW-x90sUC7TrU_cOji4MMmOCME6kkwxe57ZgpeXtdVTvldpw", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0428.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0428.yaml new file mode 100644 index 000000000000..11f7c6b80e6b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0428.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-ps512-cert + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_ps512("eyJhbGciOiJQUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VRlkPtiUq5MmBNgyuBqxv2_aX40STrWrBB2sSmGbxI78jVG_3hVoh7Mk-wUmFL389qpf05xNdn-gpMe-MSDUux7U7EuFspFZdYTUBo9wRvEBe4e1rHUCG00lVdYCG7eEgbAxM3cUhrHRwExBte30qBrFFUY9FgG-kJdYhgyh7VquMGuKgiS8CP_H0Gp1mIvTw6eEnSFAoKiryw9edUZ78pHELNn4y18YZvEndeNZh7f19LCtrB0G2bJUHGM4vPcwo2D-UAhEFBpSlnnqXDLSWOhUgLNLu0kZACXhT808KT6fdF6eFihdThmWN7_HUz2znjrjs71CqqDJgLhkGs8UvQ", "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkSgAwIBAgIBKjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEV\nMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMD\nRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDkxMjU2WhcNMjAwNTA3\nMTExMjU2WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4w\nDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3Qw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtyVWH2FE8cU8LRcArH4Tw\nDhBOFcmJF28LrvRObcbDYsae6rEwby0aRgSxjTEMgyGBjroBSl22wOSA93kwx4pu\npfXEqbwywn9FhyKBb/OXQSglPmwrpmzQtPJGzBHncL+PPjRhPfqimwf7ZIPKAAgI\nz9O6ppGhE/x4Ct444jthUIBZuG5cUXhgiPBQdIQ3K88QhgVwcufTZkNHj4iSDfhl\nVFDHVjXjd2B/yGODjyv0TyChV0YBNGjMv7YFLWmIFUFzK+6qNSxu4czPtRkyfwaV\n2GW/PBT5f8fc6fKgQZ6k6BLK6+pi0iPh5TUizyHtqtueWDbrJ+wfdJilQS8D+EGr\nAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAM\nBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQBgVAM50/0aBTBxESYKIKN4\nE+qbV6aE0C+wYJLet0EWPTxwmFZamq5LNEO/D6xyoY5WHY60EyHRMs0agSB/ATBX\n5ULdEwh9G0NjxqivCcoddQ1fuVS2PrrqNL7VlRnYbTpd8/Dh4qnyl5FltlyZ/29L\ny7BWOwlcBlZdhsfH8svNX4PUxjRD+jmnczCDi7XSOKT8htKUV2ih1c9JrWpIhCi/\nHzEXkaAxNdhBNdIsLQMo3qq9fkSgNZQk9/ecJNPeuJ/UYyr5Xa4PxIWl4U+P7yuI\n+Q3FSPmUbiVsSGqMhh6V/DN8M+T5/KiSB47gFOfxc2/RR5aw4HkSp3WxwbT9njbE\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0429.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0429.yaml new file mode 100644 index 000000000000..8b4cee3a6d1e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0429.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-ps512-key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_ps512("eyJhbGciOiJQUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VRlkPtiUq5MmBNgyuBqxv2_aX40STrWrBB2sSmGbxI78jVG_3hVoh7Mk-wUmFL389qpf05xNdn-gpMe-MSDUux7U7EuFspFZdYTUBo9wRvEBe4e1rHUCG00lVdYCG7eEgbAxM3cUhrHRwExBte30qBrFFUY9FgG-kJdYhgyh7VquMGuKgiS8CP_H0Gp1mIvTw6eEnSFAoKiryw9edUZ78pHELNn4y18YZvEndeNZh7f19LCtrB0G2bJUHGM4vPcwo2D-UAhEFBpSlnnqXDLSWOhUgLNLu0kZACXhT808KT6fdF6eFihdThmWN7_HUz2znjrjs71CqqDJgLhkGs8UvQ", "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7clVh9hRPHFPC0XAKx+E\n8A4QThXJiRdvC670Tm3Gw2LGnuqxMG8tGkYEsY0xDIMhgY66AUpdtsDkgPd5MMeK\nbqX1xKm8MsJ/RYcigW/zl0EoJT5sK6Zs0LTyRswR53C/jz40YT36opsH+2SDygAI\nCM/TuqaRoRP8eAreOOI7YVCAWbhuXFF4YIjwUHSENyvPEIYFcHLn02ZDR4+Ikg34\nZVRQx1Y143dgf8hjg48r9E8goVdGATRozL+2BS1piBVBcyvuqjUsbuHMz7UZMn8G\nldhlvzwU+X/H3OnyoEGepOgSyuvqYtIj4eU1Is8h7arbnlg26yfsH3SYpUEvA/hB\nqwIDAQAB\n-----END PUBLIC KEY-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0430.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0430.yaml new file mode 100644 index 000000000000..c1e471f52568 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0430.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-ps512-jwk + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_ps512("eyJhbGciOiJQUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VRlkPtiUq5MmBNgyuBqxv2_aX40STrWrBB2sSmGbxI78jVG_3hVoh7Mk-wUmFL389qpf05xNdn-gpMe-MSDUux7U7EuFspFZdYTUBo9wRvEBe4e1rHUCG00lVdYCG7eEgbAxM3cUhrHRwExBte30qBrFFUY9FgG-kJdYhgyh7VquMGuKgiS8CP_H0Gp1mIvTw6eEnSFAoKiryw9edUZ78pHELNn4y18YZvEndeNZh7f19LCtrB0G2bJUHGM4vPcwo2D-UAhEFBpSlnnqXDLSWOhUgLNLu0kZACXhT808KT6fdF6eFihdThmWN7_HUz2znjrjs71CqqDJgLhkGs8UvQ", `{"kty":"RSA","n":"7clVh9hRPHFPC0XAKx-E8A4QThXJiRdvC670Tm3Gw2LGnuqxMG8tGkYEsY0xDIMhgY66AUpdtsDkgPd5MMeKbqX1xKm8MsJ_RYcigW_zl0EoJT5sK6Zs0LTyRswR53C_jz40YT36opsH-2SDygAICM_TuqaRoRP8eAreOOI7YVCAWbhuXFF4YIjwUHSENyvPEIYFcHLn02ZDR4-Ikg34ZVRQx1Y143dgf8hjg48r9E8goVdGATRozL-2BS1piBVBcyvuqjUsbuHMz7UZMn8GldhlvzwU-X_H3OnyoEGepOgSyuvqYtIj4eU1Is8h7arbnlg26yfsH3SYpUEvA_hBqw","e":"AQAB"}`, x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0431.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0431.yaml new file mode 100644 index 000000000000..41bd41484bfd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0431.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/failure-wrong key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_ps512("eyJhbGciOiJQUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VRlkPtiUq5MmBNgyuBqxv2_aX40STrWrBB2sSmGbxI78jVG_3hVoh7Mk-wUmFL389qpf05xNdn-gpMe-MSDUux7U7EuFspFZdYTUBo9wRvEBe4e1rHUCG00lVdYCG7eEgbAxM3cUhrHRwExBte30qBrFFUY9FgG-kJdYhgyh7VquMGuKgiS8CP_H0Gp1mIvTw6eEnSFAoKiryw9edUZ78pHELNn4y18YZvEndeNZh7f19LCtrB0G2bJUHGM4vPcwo2D-UAhEFBpSlnnqXDLSWOhUgLNLu0kZACXhT808KT6fdF6eFihdThmWN7_HUz2znjrjs71CqqDJgLhkGs8UvQ", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0432.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0432.yaml new file mode 100644 index 000000000000..729991d0a131 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0432.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-es384-cert + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_es384("eyJhbGciOiJFUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.w85PzWrIQbJBOROnah0pa8or2LsXWnj88bwG1R-zf5Mm20CaYGPKPTQEsU_y-dzaWyDV1Na7nfaGaH3Khcvj8yS-bidZ0OZVVFDk9oabX7ZYvAHo2pTAOfxc11TeOYSF", "-----BEGIN CERTIFICATE-----\nMIICDDCCAZOgAwIBAgIBIzAKBggqhkjOPQQDAzBWMQswCQYDVQQGEwJVUzEVMBMG\nA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2\nMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDk0MzU1WhcNMjAwNTA3MTE0\nMzU1WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYD\nVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwdjAQ\nBgcqhkjOPQIBBgUrgQQAIgNiAARjcwW7g9wx4ePsuwcVzDJCVo4f8I1C1X5US4B1\nrWN+5zFSJoGCKaPTXMDhAdS08D1G20AIRmA0AlVVXRxrZYZ+Y282O6s+EGsB5T1W\nMCnUFk2Sa+xZiGPApYz4zSGbNEqjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE\nDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMAoGCCqGSM49BAMDA2cAMGQCMGSG\nVjx3DZP71ZGNDBw+AVdhNU3pgJW8kNpqjta3HFLb6pzqNOsfOn1ZeIWciEcyEgIw\nTGxli48W1AJ2s7Pw+3wOA6f9HAmczJPaiZ9CY038UiT8mk+pND5FEdqLhT/5lMEz\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0433.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0433.yaml new file mode 100644 index 000000000000..d0ab506488bb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0433.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-es384-key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_es384("eyJhbGciOiJFUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.w85PzWrIQbJBOROnah0pa8or2LsXWnj88bwG1R-zf5Mm20CaYGPKPTQEsU_y-dzaWyDV1Na7nfaGaH3Khcvj8yS-bidZ0OZVVFDk9oabX7ZYvAHo2pTAOfxc11TeOYSF", "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEY3MFu4PcMeHj7LsHFcwyQlaOH/CNQtV+\nVEuAda1jfucxUiaBgimj01zA4QHUtPA9RttACEZgNAJVVV0ca2WGfmNvNjurPhBr\nAeU9VjAp1BZNkmvsWYhjwKWM+M0hmzRK\n-----END PUBLIC KEY-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0434.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0434.yaml new file mode 100644 index 000000000000..1ad15ce54a39 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0434.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-es384-jwk + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_es384("eyJhbGciOiJFUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.w85PzWrIQbJBOROnah0pa8or2LsXWnj88bwG1R-zf5Mm20CaYGPKPTQEsU_y-dzaWyDV1Na7nfaGaH3Khcvj8yS-bidZ0OZVVFDk9oabX7ZYvAHo2pTAOfxc11TeOYSF", `{"kty":"EC","crv":"P-384","x":"Y3MFu4PcMeHj7LsHFcwyQlaOH_CNQtV-VEuAda1jfucxUiaBgimj01zA4QHUtPA9","y":"RttACEZgNAJVVV0ca2WGfmNvNjurPhBrAeU9VjAp1BZNkmvsWYhjwKWM-M0hmzRK"}`, x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0435.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0435.yaml new file mode 100644 index 000000000000..9b999b66e51a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0435.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/failure-wrong key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_es384("eyJhbGciOiJFUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.w85PzWrIQbJBOROnah0pa8or2LsXWnj88bwG1R-zf5Mm20CaYGPKPTQEsU_y-dzaWyDV1Na7nfaGaH3Khcvj8yS-bidZ0OZVVFDk9oabX7ZYvAHo2pTAOfxc11TeOYSF", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0436.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0436.yaml new file mode 100644 index 000000000000..7b5422d54f0b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0436.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-es512-cert + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_es512("eyJhbGciOiJFUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.AYpssEoEqq9We9aKsnRykpECAVEOBRJJu8UgDzoL-F8fmB2LPxpS4Gl7D-9wAO5AJt4-9YSsgOb5FLc20MrZN30AAFYopZf75T1pEJQFrdDmOKT45abbrorcR7G_AHDbhBdDNM_R6GojYFg_HPxHndof745Yq5Tfw9PpJc-9kSyk6kqO", "-----BEGIN CERTIFICATE-----\nMIICWDCCAbmgAwIBAgIBAjAKBggqhkjOPQQDBDBWMQswCQYDVQQGEwJVUzEVMBMG\nA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2\nMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MTA1NDM3WhcNMjAwNTA3MTI1\nNDM3WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYD\nVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwgZsw\nEAYHKoZIzj0CAQYFK4EEACMDgYYABAHLm3IMD/88vC/S1cCTyjrCjwHIGsjibFBw\nPBXt36YKCjUdS7jiJJR5YQVPypSv7gPaKKn1E8CqkfVdd3rrp1TocAEms4XvigtW\nZBZzffw9xyZCgmtQ2dTHsufi/5W/Yx8N3Uw+D2wl1LKcJraouo+qgamGfuou6WbA\noPEtdOg0+B4jF6M1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUF\nBwMBMAwGA1UdEwEB/wQCMAAwCgYIKoZIzj0EAwQDgYwAMIGIAkIAzAAYDqMghX3S\n8UbS8s5TPAztJy9oNXFra5V8pPlUdNFc2ov2LN++scW46wCb/cJUyEc58sY7xFuK\nI5sCOkv95N8CQgFXmu354LZJ31zIovuUA8druOPe3TDnxMGwEEm2Lt43JNuhzNyP\nhJYh9/QKfe2AiwrLXEG4VVOIXdjq7vexl87evg==\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0437.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0437.yaml new file mode 100644 index 000000000000..a17f618216e4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0437.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-es512-key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_es512("eyJhbGciOiJFUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.AYpssEoEqq9We9aKsnRykpECAVEOBRJJu8UgDzoL-F8fmB2LPxpS4Gl7D-9wAO5AJt4-9YSsgOb5FLc20MrZN30AAFYopZf75T1pEJQFrdDmOKT45abbrorcR7G_AHDbhBdDNM_R6GojYFg_HPxHndof745Yq5Tfw9PpJc-9kSyk6kqO", "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBy5tyDA//PLwv0tXAk8o6wo8ByBrI\n4mxQcDwV7d+mCgo1HUu44iSUeWEFT8qUr+4D2iip9RPAqpH1XXd666dU6HABJrOF\n74oLVmQWc338PccmQoJrUNnUx7Ln4v+Vv2MfDd1MPg9sJdSynCa2qLqPqoGphn7q\nLulmwKDxLXToNPgeIxc=\n-----END PUBLIC KEY-----", x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0438.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0438.yaml new file mode 100644 index 000000000000..1dd652b744de --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0438.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/success-es512-jwk + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_es512("eyJhbGciOiJFUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.AYpssEoEqq9We9aKsnRykpECAVEOBRJJu8UgDzoL-F8fmB2LPxpS4Gl7D-9wAO5AJt4-9YSsgOb5FLc20MrZN30AAFYopZf75T1pEJQFrdDmOKT45abbrorcR7G_AHDbhBdDNM_R6GojYFg_HPxHndof745Yq5Tfw9PpJc-9kSyk6kqO", `{"kty":"EC","crv":"P-521","x":"AcubcgwP_zy8L9LVwJPKOsKPAcgayOJsUHA8Fe3fpgoKNR1LuOIklHlhBU_KlK_uA9ooqfUTwKqR9V13euunVOhw","y":"ASazhe-KC1ZkFnN9_D3HJkKCa1DZ1Mey5-L_lb9jHw3dTD4PbCXUspwmtqi6j6qBqYZ-6i7pZsCg8S106DT4HiMX"}`, x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0439.yaml b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0439.yaml new file mode 100644 index 000000000000..1a0250372be0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/jwtverifyrsa/test-jwtverifyrsa-0439.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: jwtverifyrsa/failure-wrong key + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + io.jwt.verify_es512("eyJhbGciOiJFUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.AYpssEoEqq9We9aKsnRykpECAVEOBRJJu8UgDzoL-F8fmB2LPxpS4Gl7D-9wAO5AJt4-9YSsgOb5FLc20MrZN30AAFYopZf75T1pEJQFrdDmOKT45abbrorcR7G_AHDbhBdDNM_R6GojYFg_HPxHndof745Yq5Tfw9PpJc-9kSyk6kqO", "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-as.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-as.yaml new file mode 100644 index 000000000000..bf320584d24c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-as.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/as keyword in package + query: data.foo.p = x + modules: + - | + package foo.as.bar + + baz := 42 + - | + package foo + import data.foo.as.bar + + p if { + bar.baz == 42 + data.foo.as.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/as keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.as + + bar := 42 + - | + package foo + import data.foo.as as my_if + + p if { + my_if.bar == 42 + data.foo.as.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/as keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + as.foo == 1 + foo.as == 2 + } + + as.foo := 1 + + foo.as := 2 + want_result: + - x: true + - note: keywordrefs/as keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + as.foo == 3 + foo.as == 6 + } + + as.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.as := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/as keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + as.foo == {"a", "c"} + foo.as == {"a", "c"} + } + + as.foo contains "a" + + as.foo contains "b" if { + false + } + + as.foo contains "c" if { + true + } + + foo.as contains "a" + + foo.as contains "b" if { + false + } + + foo.as contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/as keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + as.foo == "a" + as.bar.one == "a" + as.bar.three == "c" + foo.as == "a" + bar.baz.as == "a" + } + + as.foo := "a" + + as.foo := "b" if { + false + } + + as.foo := "c" if { + false + } + + as.bar.one := "a" + + as.bar.two := "b" if { + false + } + + as.bar.three := "c" if { + true + } + + foo.as := "a" + + foo.as := "b" if { + false + } + + foo.as := "c" if { + false + } + + bar.baz.as := "a" + + bar.baz.as := "b" if { + false + } + + bar.baz.as := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/as keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + as.foo(1) == 1 + as.foo(11) == 42 + foo.as(1) == 1 + foo.as(11) == 42 + bar.as.baz(1) == 1 + bar.as.baz(11) == 42 + } + + default as.foo(_) := 42 + + as.foo(x) := x if { + x < 10 + } + + default foo.as(_) := 42 + + foo.as(x) := x if { + x < 10 + } + + default bar.as.baz(_) := 42 + + bar.as.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-contains.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-contains.yaml new file mode 100644 index 000000000000..9bb9bb9a9500 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-contains.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/contains keyword in package + query: data.foo.p = x + modules: + - | + package foo.contains.bar + + baz := 42 + - | + package foo + import data.foo.contains.bar + + p if { + bar.baz == 42 + data.foo.contains.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/contains keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.contains + + bar := 42 + - | + package foo + import data.foo.contains as my_if + + p if { + my_if.bar == 42 + data.foo.contains.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/contains keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + contains.foo == 1 + foo.contains == 2 + } + + contains.foo := 1 + + foo.contains := 2 + want_result: + - x: true + - note: keywordrefs/contains keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + contains.foo == 3 + foo.contains == 6 + } + + contains.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.contains := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/contains keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + contains.foo == {"a", "c"} + foo.contains == {"a", "c"} + } + + contains.foo contains "a" + + contains.foo contains "b" if { + false + } + + contains.foo contains "c" if { + true + } + + foo.contains contains "a" + + foo.contains contains "b" if { + false + } + + foo.contains contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/contains keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + contains.foo == "a" + contains.bar.one == "a" + contains.bar.three == "c" + foo.contains == "a" + bar.baz.contains == "a" + } + + contains.foo := "a" + + contains.foo := "b" if { + false + } + + contains.foo := "c" if { + false + } + + contains.bar.one := "a" + + contains.bar.two := "b" if { + false + } + + contains.bar.three := "c" if { + true + } + + foo.contains := "a" + + foo.contains := "b" if { + false + } + + foo.contains := "c" if { + false + } + + bar.baz.contains := "a" + + bar.baz.contains := "b" if { + false + } + + bar.baz.contains := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/contains keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + contains.foo(1) == 1 + contains.foo(11) == 42 + foo.contains(1) == 1 + foo.contains(11) == 42 + bar.contains.baz(1) == 1 + bar.contains.baz(11) == 42 + } + + default contains.foo(_) := 42 + + contains.foo(x) := x if { + x < 10 + } + + default foo.contains(_) := 42 + + foo.contains(x) := x if { + x < 10 + } + + default bar.contains.baz(_) := 42 + + bar.contains.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-default.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-default.yaml new file mode 100644 index 000000000000..36af5cce962a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-default.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/default keyword in package + query: data.foo.p = x + modules: + - | + package foo.default.bar + + baz := 42 + - | + package foo + import data.foo.default.bar + + p if { + bar.baz == 42 + data.foo.default.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/default keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.default + + bar := 42 + - | + package foo + import data.foo.default as my_if + + p if { + my_if.bar == 42 + data.foo.default.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/default keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + default.foo == 1 + foo.default == 2 + } + + default.foo := 1 + + foo.default := 2 + want_result: + - x: true + - note: keywordrefs/default keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + default.foo == 3 + foo.default == 6 + } + + default.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.default := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/default keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + default.foo == {"a", "c"} + foo.default == {"a", "c"} + } + + default.foo contains "a" + + default.foo contains "b" if { + false + } + + default.foo contains "c" if { + true + } + + foo.default contains "a" + + foo.default contains "b" if { + false + } + + foo.default contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/default keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + default.foo == "a" + default.bar.one == "a" + default.bar.three == "c" + foo.default == "a" + bar.baz.default == "a" + } + + default.foo := "a" + + default.foo := "b" if { + false + } + + default.foo := "c" if { + false + } + + default.bar.one := "a" + + default.bar.two := "b" if { + false + } + + default.bar.three := "c" if { + true + } + + foo.default := "a" + + foo.default := "b" if { + false + } + + foo.default := "c" if { + false + } + + bar.baz.default := "a" + + bar.baz.default := "b" if { + false + } + + bar.baz.default := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/default keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + default.foo(1) == 1 + default.foo(11) == 42 + foo.default(1) == 1 + foo.default(11) == 42 + bar.default.baz(1) == 1 + bar.default.baz(11) == 42 + } + + default default.foo(_) := 42 + + default.foo(x) := x if { + x < 10 + } + + default foo.default(_) := 42 + + foo.default(x) := x if { + x < 10 + } + + default bar.default.baz(_) := 42 + + bar.default.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-else.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-else.yaml new file mode 100644 index 000000000000..b4fce6422aa7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-else.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/else keyword in package + query: data.foo.p = x + modules: + - | + package foo.else.bar + + baz := 42 + - | + package foo + import data.foo.else.bar + + p if { + bar.baz == 42 + data.foo.else.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/else keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.else + + bar := 42 + - | + package foo + import data.foo.else as my_if + + p if { + my_if.bar == 42 + data.foo.else.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/else keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + else.foo == 1 + foo.else == 2 + } + + else.foo := 1 + + foo.else := 2 + want_result: + - x: true + - note: keywordrefs/else keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + else.foo == 3 + foo.else == 6 + } + + else.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.else := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/else keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + else.foo == {"a", "c"} + foo.else == {"a", "c"} + } + + else.foo contains "a" + + else.foo contains "b" if { + false + } + + else.foo contains "c" if { + true + } + + foo.else contains "a" + + foo.else contains "b" if { + false + } + + foo.else contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/else keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + else.foo == "a" + else.bar.one == "a" + else.bar.three == "c" + foo.else == "a" + bar.baz.else == "a" + } + + else.foo := "a" + + else.foo := "b" if { + false + } + + else.foo := "c" if { + false + } + + else.bar.one := "a" + + else.bar.two := "b" if { + false + } + + else.bar.three := "c" if { + true + } + + foo.else := "a" + + foo.else := "b" if { + false + } + + foo.else := "c" if { + false + } + + bar.baz.else := "a" + + bar.baz.else := "b" if { + false + } + + bar.baz.else := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/else keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + else.foo(1) == 1 + else.foo(11) == 42 + foo.else(1) == 1 + foo.else(11) == 42 + bar.else.baz(1) == 1 + bar.else.baz(11) == 42 + } + + default else.foo(_) := 42 + + else.foo(x) := x if { + x < 10 + } + + default foo.else(_) := 42 + + foo.else(x) := x if { + x < 10 + } + + default bar.else.baz(_) := 42 + + bar.else.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-every.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-every.yaml new file mode 100644 index 000000000000..ecb199a85381 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-every.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/every keyword in package + query: data.foo.p = x + modules: + - | + package foo.every.bar + + baz := 42 + - | + package foo + import data.foo.every.bar + + p if { + bar.baz == 42 + data.foo.every.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/every keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.every + + bar := 42 + - | + package foo + import data.foo.every as my_if + + p if { + my_if.bar == 42 + data.foo.every.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/every keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + every.foo == 1 + foo.every == 2 + } + + every.foo := 1 + + foo.every := 2 + want_result: + - x: true + - note: keywordrefs/every keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + every.foo == 3 + foo.every == 6 + } + + every.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.every := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/every keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + every.foo == {"a", "c"} + foo.every == {"a", "c"} + } + + every.foo contains "a" + + every.foo contains "b" if { + false + } + + every.foo contains "c" if { + true + } + + foo.every contains "a" + + foo.every contains "b" if { + false + } + + foo.every contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/every keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + every.foo == "a" + every.bar.one == "a" + every.bar.three == "c" + foo.every == "a" + bar.baz.every == "a" + } + + every.foo := "a" + + every.foo := "b" if { + false + } + + every.foo := "c" if { + false + } + + every.bar.one := "a" + + every.bar.two := "b" if { + false + } + + every.bar.three := "c" if { + true + } + + foo.every := "a" + + foo.every := "b" if { + false + } + + foo.every := "c" if { + false + } + + bar.baz.every := "a" + + bar.baz.every := "b" if { + false + } + + bar.baz.every := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/every keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + every.foo(1) == 1 + every.foo(11) == 42 + foo.every(1) == 1 + foo.every(11) == 42 + bar.every.baz(1) == 1 + bar.every.baz(11) == 42 + } + + default every.foo(_) := 42 + + every.foo(x) := x if { + x < 10 + } + + default foo.every(_) := 42 + + foo.every(x) := x if { + x < 10 + } + + default bar.every.baz(_) := 42 + + bar.every.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-false.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-false.yaml new file mode 100644 index 000000000000..caea38d664ea --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-false.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/false keyword in package + query: data.foo.p = x + modules: + - | + package foo.false.bar + + baz := 42 + - | + package foo + import data.foo.false.bar + + p if { + bar.baz == 42 + data.foo.false.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/false keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.false + + bar := 42 + - | + package foo + import data.foo.false as my_if + + p if { + my_if.bar == 42 + data.foo.false.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/false keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + false.foo == 1 + foo.false == 2 + } + + false.foo := 1 + + foo.false := 2 + want_result: + - x: true + - note: keywordrefs/false keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + false.foo == 3 + foo.false == 6 + } + + false.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.false := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/false keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + false.foo == {"a", "c"} + foo.false == {"a", "c"} + } + + false.foo contains "a" + + false.foo contains "b" if { + false + } + + false.foo contains "c" if { + true + } + + foo.false contains "a" + + foo.false contains "b" if { + false + } + + foo.false contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/false keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + false.foo == "a" + false.bar.one == "a" + false.bar.three == "c" + foo.false == "a" + bar.baz.false == "a" + } + + false.foo := "a" + + false.foo := "b" if { + false + } + + false.foo := "c" if { + false + } + + false.bar.one := "a" + + false.bar.two := "b" if { + false + } + + false.bar.three := "c" if { + true + } + + foo.false := "a" + + foo.false := "b" if { + false + } + + foo.false := "c" if { + false + } + + bar.baz.false := "a" + + bar.baz.false := "b" if { + false + } + + bar.baz.false := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/false keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + false.foo(1) == 1 + false.foo(11) == 42 + foo.false(1) == 1 + foo.false(11) == 42 + bar.false.baz(1) == 1 + bar.false.baz(11) == 42 + } + + default false.foo(_) := 42 + + false.foo(x) := x if { + x < 10 + } + + default foo.false(_) := 42 + + foo.false(x) := x if { + x < 10 + } + + default bar.false.baz(_) := 42 + + bar.false.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-if.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-if.yaml new file mode 100644 index 000000000000..44fd4bdc51d4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-if.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/if keyword in package + query: data.foo.p = x + modules: + - | + package foo.if.bar + + baz := 42 + - | + package foo + import data.foo.if.bar + + p if { + bar.baz == 42 + data.foo.if.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/if keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.if + + bar := 42 + - | + package foo + import data.foo.if as my_if + + p if { + my_if.bar == 42 + data.foo.if.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/if keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + if.foo == 1 + foo.if == 2 + } + + if.foo := 1 + + foo.if := 2 + want_result: + - x: true + - note: keywordrefs/if keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + if.foo == 3 + foo.if == 6 + } + + if.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.if := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/if keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + if.foo == {"a", "c"} + foo.if == {"a", "c"} + } + + if.foo contains "a" + + if.foo contains "b" if { + false + } + + if.foo contains "c" if { + true + } + + foo.if contains "a" + + foo.if contains "b" if { + false + } + + foo.if contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/if keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + if.foo == "a" + if.bar.one == "a" + if.bar.three == "c" + foo.if == "a" + bar.baz.if == "a" + } + + if.foo := "a" + + if.foo := "b" if { + false + } + + if.foo := "c" if { + false + } + + if.bar.one := "a" + + if.bar.two := "b" if { + false + } + + if.bar.three := "c" if { + true + } + + foo.if := "a" + + foo.if := "b" if { + false + } + + foo.if := "c" if { + false + } + + bar.baz.if := "a" + + bar.baz.if := "b" if { + false + } + + bar.baz.if := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/if keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + if.foo(1) == 1 + if.foo(11) == 42 + foo.if(1) == 1 + foo.if(11) == 42 + bar.if.baz(1) == 1 + bar.if.baz(11) == 42 + } + + default if.foo(_) := 42 + + if.foo(x) := x if { + x < 10 + } + + default foo.if(_) := 42 + + foo.if(x) := x if { + x < 10 + } + + default bar.if.baz(_) := 42 + + bar.if.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-import.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-import.yaml new file mode 100644 index 000000000000..f245091bca70 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-import.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/import keyword in package + query: data.foo.p = x + modules: + - | + package foo.import.bar + + baz := 42 + - | + package foo + import data.foo.import.bar + + p if { + bar.baz == 42 + data.foo.import.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/import keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.import + + bar := 42 + - | + package foo + import data.foo.import as my_if + + p if { + my_if.bar == 42 + data.foo.import.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/import keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + import.foo == 1 + foo.import == 2 + } + + import.foo := 1 + + foo.import := 2 + want_result: + - x: true + - note: keywordrefs/import keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + import.foo == 3 + foo.import == 6 + } + + import.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.import := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/import keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + import.foo == {"a", "c"} + foo.import == {"a", "c"} + } + + import.foo contains "a" + + import.foo contains "b" if { + false + } + + import.foo contains "c" if { + true + } + + foo.import contains "a" + + foo.import contains "b" if { + false + } + + foo.import contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/import keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + import.foo == "a" + import.bar.one == "a" + import.bar.three == "c" + foo.import == "a" + bar.baz.import == "a" + } + + import.foo := "a" + + import.foo := "b" if { + false + } + + import.foo := "c" if { + false + } + + import.bar.one := "a" + + import.bar.two := "b" if { + false + } + + import.bar.three := "c" if { + true + } + + foo.import := "a" + + foo.import := "b" if { + false + } + + foo.import := "c" if { + false + } + + bar.baz.import := "a" + + bar.baz.import := "b" if { + false + } + + bar.baz.import := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/import keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + import.foo(1) == 1 + import.foo(11) == 42 + foo.import(1) == 1 + foo.import(11) == 42 + bar.import.baz(1) == 1 + bar.import.baz(11) == 42 + } + + default import.foo(_) := 42 + + import.foo(x) := x if { + x < 10 + } + + default foo.import(_) := 42 + + foo.import(x) := x if { + x < 10 + } + + default bar.import.baz(_) := 42 + + bar.import.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-in.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-in.yaml new file mode 100644 index 000000000000..fa6ee3b30f4c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-in.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/in keyword in package + query: data.foo.p = x + modules: + - | + package foo.in.bar + + baz := 42 + - | + package foo + import data.foo.in.bar + + p if { + bar.baz == 42 + data.foo.in.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/in keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.in + + bar := 42 + - | + package foo + import data.foo.in as my_if + + p if { + my_if.bar == 42 + data.foo.in.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/in keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + in.foo == 1 + foo.in == 2 + } + + in.foo := 1 + + foo.in := 2 + want_result: + - x: true + - note: keywordrefs/in keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + in.foo == 3 + foo.in == 6 + } + + in.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.in := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/in keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + in.foo == {"a", "c"} + foo.in == {"a", "c"} + } + + in.foo contains "a" + + in.foo contains "b" if { + false + } + + in.foo contains "c" if { + true + } + + foo.in contains "a" + + foo.in contains "b" if { + false + } + + foo.in contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/in keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + in.foo == "a" + in.bar.one == "a" + in.bar.three == "c" + foo.in == "a" + bar.baz.in == "a" + } + + in.foo := "a" + + in.foo := "b" if { + false + } + + in.foo := "c" if { + false + } + + in.bar.one := "a" + + in.bar.two := "b" if { + false + } + + in.bar.three := "c" if { + true + } + + foo.in := "a" + + foo.in := "b" if { + false + } + + foo.in := "c" if { + false + } + + bar.baz.in := "a" + + bar.baz.in := "b" if { + false + } + + bar.baz.in := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/in keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + in.foo(1) == 1 + in.foo(11) == 42 + foo.in(1) == 1 + foo.in(11) == 42 + bar.in.baz(1) == 1 + bar.in.baz(11) == 42 + } + + default in.foo(_) := 42 + + in.foo(x) := x if { + x < 10 + } + + default foo.in(_) := 42 + + foo.in(x) := x if { + x < 10 + } + + default bar.in.baz(_) := 42 + + bar.in.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-not.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-not.yaml new file mode 100644 index 000000000000..d5b024273787 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-not.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/not keyword in package + query: data.foo.p = x + modules: + - | + package foo.not.bar + + baz := 42 + - | + package foo + import data.foo.not.bar + + p if { + bar.baz == 42 + data.foo.not.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/not keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.not + + bar := 42 + - | + package foo + import data.foo.not as my_if + + p if { + my_if.bar == 42 + data.foo.not.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/not keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + not.foo == 1 + foo.not == 2 + } + + not.foo := 1 + + foo.not := 2 + want_result: + - x: true + - note: keywordrefs/not keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + not.foo == 3 + foo.not == 6 + } + + not.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.not := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/not keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + not.foo == {"a", "c"} + foo.not == {"a", "c"} + } + + not.foo contains "a" + + not.foo contains "b" if { + false + } + + not.foo contains "c" if { + true + } + + foo.not contains "a" + + foo.not contains "b" if { + false + } + + foo.not contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/not keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + not.foo == "a" + not.bar.one == "a" + not.bar.three == "c" + foo.not == "a" + bar.baz.not == "a" + } + + not.foo := "a" + + not.foo := "b" if { + false + } + + not.foo := "c" if { + false + } + + not.bar.one := "a" + + not.bar.two := "b" if { + false + } + + not.bar.three := "c" if { + true + } + + foo.not := "a" + + foo.not := "b" if { + false + } + + foo.not := "c" if { + false + } + + bar.baz.not := "a" + + bar.baz.not := "b" if { + false + } + + bar.baz.not := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/not keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + not.foo(1) == 1 + not.foo(11) == 42 + foo.not(1) == 1 + foo.not(11) == 42 + bar.not.baz(1) == 1 + bar.not.baz(11) == 42 + } + + default not.foo(_) := 42 + + not.foo(x) := x if { + x < 10 + } + + default foo.not(_) := 42 + + foo.not(x) := x if { + x < 10 + } + + default bar.not.baz(_) := 42 + + bar.not.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-null.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-null.yaml new file mode 100644 index 000000000000..25e1bef034d2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-null.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/null keyword in package + query: data.foo.p = x + modules: + - | + package foo.null.bar + + baz := 42 + - | + package foo + import data.foo.null.bar + + p if { + bar.baz == 42 + data.foo.null.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/null keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.null + + bar := 42 + - | + package foo + import data.foo.null as my_if + + p if { + my_if.bar == 42 + data.foo.null.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/null keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + null.foo == 1 + foo.null == 2 + } + + null.foo := 1 + + foo.null := 2 + want_result: + - x: true + - note: keywordrefs/null keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + null.foo == 3 + foo.null == 6 + } + + null.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.null := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/null keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + null.foo == {"a", "c"} + foo.null == {"a", "c"} + } + + null.foo contains "a" + + null.foo contains "b" if { + false + } + + null.foo contains "c" if { + true + } + + foo.null contains "a" + + foo.null contains "b" if { + false + } + + foo.null contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/null keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + null.foo == "a" + null.bar.one == "a" + null.bar.three == "c" + foo.null == "a" + bar.baz.null == "a" + } + + null.foo := "a" + + null.foo := "b" if { + false + } + + null.foo := "c" if { + false + } + + null.bar.one := "a" + + null.bar.two := "b" if { + false + } + + null.bar.three := "c" if { + true + } + + foo.null := "a" + + foo.null := "b" if { + false + } + + foo.null := "c" if { + false + } + + bar.baz.null := "a" + + bar.baz.null := "b" if { + false + } + + bar.baz.null := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/null keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + null.foo(1) == 1 + null.foo(11) == 42 + foo.null(1) == 1 + foo.null(11) == 42 + bar.null.baz(1) == 1 + bar.null.baz(11) == 42 + } + + default null.foo(_) := 42 + + null.foo(x) := x if { + x < 10 + } + + default foo.null(_) := 42 + + foo.null(x) := x if { + x < 10 + } + + default bar.null.baz(_) := 42 + + bar.null.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-package.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-package.yaml new file mode 100644 index 000000000000..c13c49559627 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-package.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/package keyword in package + query: data.foo.p = x + modules: + - | + package foo.package.bar + + baz := 42 + - | + package foo + import data.foo.package.bar + + p if { + bar.baz == 42 + data.foo.package.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/package keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.package + + bar := 42 + - | + package foo + import data.foo.package as my_if + + p if { + my_if.bar == 42 + data.foo.package.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/package keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + package.foo == 1 + foo.package == 2 + } + + package.foo := 1 + + foo.package := 2 + want_result: + - x: true + - note: keywordrefs/package keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + package.foo == 3 + foo.package == 6 + } + + package.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.package := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/package keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + package.foo == {"a", "c"} + foo.package == {"a", "c"} + } + + package.foo contains "a" + + package.foo contains "b" if { + false + } + + package.foo contains "c" if { + true + } + + foo.package contains "a" + + foo.package contains "b" if { + false + } + + foo.package contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/package keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + package.foo == "a" + package.bar.one == "a" + package.bar.three == "c" + foo.package == "a" + bar.baz.package == "a" + } + + package.foo := "a" + + package.foo := "b" if { + false + } + + package.foo := "c" if { + false + } + + package.bar.one := "a" + + package.bar.two := "b" if { + false + } + + package.bar.three := "c" if { + true + } + + foo.package := "a" + + foo.package := "b" if { + false + } + + foo.package := "c" if { + false + } + + bar.baz.package := "a" + + bar.baz.package := "b" if { + false + } + + bar.baz.package := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/package keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + package.foo(1) == 1 + package.foo(11) == 42 + foo.package(1) == 1 + foo.package(11) == 42 + bar.package.baz(1) == 1 + bar.package.baz(11) == 42 + } + + default package.foo(_) := 42 + + package.foo(x) := x if { + x < 10 + } + + default foo.package(_) := 42 + + foo.package(x) := x if { + x < 10 + } + + default bar.package.baz(_) := 42 + + bar.package.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-some.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-some.yaml new file mode 100644 index 000000000000..6dc436a2bb6c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-some.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/some keyword in package + query: data.foo.p = x + modules: + - | + package foo.some.bar + + baz := 42 + - | + package foo + import data.foo.some.bar + + p if { + bar.baz == 42 + data.foo.some.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/some keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.some + + bar := 42 + - | + package foo + import data.foo.some as my_if + + p if { + my_if.bar == 42 + data.foo.some.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/some keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + some.foo == 1 + foo.some == 2 + } + + some.foo := 1 + + foo.some := 2 + want_result: + - x: true + - note: keywordrefs/some keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + some.foo == 3 + foo.some == 6 + } + + some.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.some := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/some keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + some.foo == {"a", "c"} + foo.some == {"a", "c"} + } + + some.foo contains "a" + + some.foo contains "b" if { + false + } + + some.foo contains "c" if { + true + } + + foo.some contains "a" + + foo.some contains "b" if { + false + } + + foo.some contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/some keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + some.foo == "a" + some.bar.one == "a" + some.bar.three == "c" + foo.some == "a" + bar.baz.some == "a" + } + + some.foo := "a" + + some.foo := "b" if { + false + } + + some.foo := "c" if { + false + } + + some.bar.one := "a" + + some.bar.two := "b" if { + false + } + + some.bar.three := "c" if { + true + } + + foo.some := "a" + + foo.some := "b" if { + false + } + + foo.some := "c" if { + false + } + + bar.baz.some := "a" + + bar.baz.some := "b" if { + false + } + + bar.baz.some := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/some keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + some.foo(1) == 1 + some.foo(11) == 42 + foo.some(1) == 1 + foo.some(11) == 42 + bar.some.baz(1) == 1 + bar.some.baz(11) == 42 + } + + default some.foo(_) := 42 + + some.foo(x) := x if { + x < 10 + } + + default foo.some(_) := 42 + + foo.some(x) := x if { + x < 10 + } + + default bar.some.baz(_) := 42 + + bar.some.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-true.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-true.yaml new file mode 100644 index 000000000000..e1c98fcfe04b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-true.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/true keyword in package + query: data.foo.p = x + modules: + - | + package foo.true.bar + + baz := 42 + - | + package foo + import data.foo.true.bar + + p if { + bar.baz == 42 + data.foo.true.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/true keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.true + + bar := 42 + - | + package foo + import data.foo.true as my_if + + p if { + my_if.bar == 42 + data.foo.true.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/true keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + true.foo == 1 + foo.true == 2 + } + + true.foo := 1 + + foo.true := 2 + want_result: + - x: true + - note: keywordrefs/true keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + true.foo == 3 + foo.true == 6 + } + + true.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.true := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/true keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + true.foo == {"a", "c"} + foo.true == {"a", "c"} + } + + true.foo contains "a" + + true.foo contains "b" if { + false + } + + true.foo contains "c" if { + true + } + + foo.true contains "a" + + foo.true contains "b" if { + false + } + + foo.true contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/true keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + true.foo == "a" + true.bar.one == "a" + true.bar.three == "c" + foo.true == "a" + bar.baz.true == "a" + } + + true.foo := "a" + + true.foo := "b" if { + false + } + + true.foo := "c" if { + false + } + + true.bar.one := "a" + + true.bar.two := "b" if { + false + } + + true.bar.three := "c" if { + true + } + + foo.true := "a" + + foo.true := "b" if { + false + } + + foo.true := "c" if { + false + } + + bar.baz.true := "a" + + bar.baz.true := "b" if { + false + } + + bar.baz.true := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/true keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + true.foo(1) == 1 + true.foo(11) == 42 + foo.true(1) == 1 + foo.true(11) == 42 + bar.true.baz(1) == 1 + bar.true.baz(11) == 42 + } + + default true.foo(_) := 42 + + true.foo(x) := x if { + x < 10 + } + + default foo.true(_) := 42 + + foo.true(x) := x if { + x < 10 + } + + default bar.true.baz(_) := 42 + + bar.true.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-with.yaml b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-with.yaml new file mode 100644 index 000000000000..2039bfabeee0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/keywordrefs/test-keyword-with.yaml @@ -0,0 +1,199 @@ +--- +cases: + - note: keywordrefs/with keyword in package + query: data.foo.p = x + modules: + - | + package foo.with.bar + + baz := 42 + - | + package foo + import data.foo.with.bar + + p if { + bar.baz == 42 + data.foo.with.bar.baz == 42 + } + want_result: + - x: true + - note: keywordrefs/with keyword in package, import alias + query: data.foo.p = x + modules: + - | + package foo.with + + bar := 42 + - | + package foo + import data.foo.with as my_if + + p if { + my_if.bar == 42 + data.foo.with.bar == 42 + } + want_result: + - x: true + - note: keywordrefs/with keyword rule refhead + query: data.test.p = x + modules: + - | + package test + + p if { + with.foo == 1 + foo.with == 2 + } + + with.foo := 1 + + foo.with := 2 + want_result: + - x: true + - note: keywordrefs/with keyword rule refhead, else bodies + query: data.test.p = x + input: + x: 3 + modules: + - | + package test + + p if { + with.foo == 3 + foo.with == 6 + } + + with.foo := 1 if { + input.x == 1 + } else := 2 if { + input.x == 2 + } else := 3 + + foo.with := 4 if { + input.x == 1 + } else := 5 if { + input.x == 2 + } else := 6 + want_result: + - x: true + - note: keywordrefs/with keyword rule refhead, partial set + query: data.test.p = x + modules: + - | + package test + + p if { + with.foo == {"a", "c"} + foo.with == {"a", "c"} + } + + with.foo contains "a" + + with.foo contains "b" if { + false + } + + with.foo contains "c" if { + true + } + + foo.with contains "a" + + foo.with contains "b" if { + false + } + + foo.with contains "c" if { + true + } + want_result: + - x: true + - note: keywordrefs/with keyword rule refhead, partial object + query: data.test.p = x + modules: + - | + package test + + p if { + with.foo == "a" + with.bar.one == "a" + with.bar.three == "c" + foo.with == "a" + bar.baz.with == "a" + } + + with.foo := "a" + + with.foo := "b" if { + false + } + + with.foo := "c" if { + false + } + + with.bar.one := "a" + + with.bar.two := "b" if { + false + } + + with.bar.three := "c" if { + true + } + + foo.with := "a" + + foo.with := "b" if { + false + } + + foo.with := "c" if { + false + } + + bar.baz.with := "a" + + bar.baz.with := "b" if { + false + } + + bar.baz.with := "c" if { + false + } + want_result: + - x: true + - note: keywordrefs/with keyword function refhead + query: data.test.p = x + modules: + - | + package test + + p if { + with.foo(1) == 1 + with.foo(11) == 42 + foo.with(1) == 1 + foo.with(11) == 42 + bar.with.baz(1) == 1 + bar.with.baz(11) == 42 + } + + default with.foo(_) := 42 + + with.foo(x) := x if { + x < 10 + } + + default foo.with(_) := 42 + + foo.with(x) := x if { + x < 10 + } + + default bar.with.baz(_) := 42 + + bar.with.baz(x) := x if { + x < 10 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0777.yaml b/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0777.yaml new file mode 100644 index 000000000000..9718a919dc76 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0777.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "negation/neg: constants" + query: data.generated.p = x + modules: + - | + package generated + + p if { + not true = false + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0778.yaml b/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0778.yaml new file mode 100644 index 000000000000..8d11640258cc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0778.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: "negation/neg: constants" + query: data.generated.p = x + modules: + - | + package generated + + p if { + not true = true + } + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0779.yaml b/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0779.yaml new file mode 100644 index 000000000000..b0b9ac1de183 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0779.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "negation/neg: set contains" + query: data.generated.p = x + modules: + - | + package generated + + p if { + not data.generated.q.v0 + } + + q contains x if { + data.b[x] = v + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0780.yaml b/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0780.yaml new file mode 100644 index 000000000000..8a95c5a31abd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-0780.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "negation/neg: set contains undefined" + query: data.generated.p = x + modules: + - | + package generated + + p if { + not data.generated.q.v2 + } + + q contains x if { + data.b[x] = v + } + data: + b: + v1: hello + v2: goodbye + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-data-ref-with-var.yaml b/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-data-ref-with-var.yaml new file mode 100644 index 000000000000..6d8b84134f76 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/negation/test-negation-data-ref-with-var.yaml @@ -0,0 +1,77 @@ +--- +cases: + - note: "negation/pos: ref with variable" + query: data.test.p = x + modules: + - | + package test + + p := y if { + y := "v0" + not data.test.q[y] + } + + q contains x if { + data.b[x] = v + } + data: + b: + v1: hello + v2: goodbye + want_result: + - x: v0 + - note: "negation/neg: ref with variable" + query: data.test.p = x + modules: + - | + package test + + p := y if { + y := "v1" + not data.test.q[y] + } + + q contains x if { + data.b[x] = v + } + data: + b: + v1: hello + v2: goodbye + want_result: [] + - note: "negation/neg: ref with variable (hit) and virtual doc (miss))" + query: data.foo.p = x + modules: + - | + package foo + + p if { + k := "p" + not data.bar[k] + } + - | + package bar + + p := 7 + data: + bar: + q: "8" + want_result: [] + - note: "negation/neg: ref with variable (miss) and virtual doc (hit)" + query: data.foo.p = x + modules: + - | + package foo + + p if { + k := "q" + not data.bar[k] + } + - | + package bar + + p := 7 + data: + bar: + q: "8" + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0709.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0709.yaml new file mode 100644 index 000000000000..66d01f66f26d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0709.yaml @@ -0,0 +1,27 @@ +--- +cases: + - note: nestedreferences/ground ref + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.h[0][0] + data.a[__local0__] = 2 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + h: + - - 1 + - 2 + - 3 + - - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0710.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0710.yaml new file mode 100644 index 000000000000..a3d457ca1394 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0710.yaml @@ -0,0 +1,31 @@ +--- +cases: + - note: nestedreferences/non-ground ref + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.h[i][j] + x = data.a[__local0__] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + h: + - - 1 + - 2 + - 3 + - - 2 + - 3 + - 4 + want_result: + - x: + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0711.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0711.yaml new file mode 100644 index 000000000000..cad3979a45cb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0711.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: nestedreferences/two deep + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.a[i] + __local1__ = data.a[__local0__] + x = data.a[__local1__] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0712.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0712.yaml new file mode 100644 index 000000000000..ca8b63d35e31 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0712.yaml @@ -0,0 +1,31 @@ +--- +cases: + - note: nestedreferences/two deep + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.a[j] + __local1__ = data.h[i][__local0__] + x = data.a[__local1__] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + h: + - - 1 + - 2 + - 3 + - - 2 + - 3 + - 4 + want_result: + - x: + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0713.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0713.yaml new file mode 100644 index 000000000000..39f3beeec865 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0713.yaml @@ -0,0 +1,30 @@ +--- +cases: + - note: nestedreferences/two deep repeated var + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.a[i] + __local1__ = data.h[i][__local0__] + x = data.a[__local1__] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + h: + - - 1 + - 2 + - 3 + - - 2 + - 3 + - 4 + want_result: + - x: + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0714.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0714.yaml new file mode 100644 index 000000000000..6e524be18cb7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0714.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: nestedreferences/no suffix + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.three + 4 = data.a[__local0__] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + three: 3 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0715.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0715.yaml new file mode 100644 index 000000000000..add7b14d8c34 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0715.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: nestedreferences/var ref + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + x = [1, 2, 3] + __local0__ = x[_] + y = data.a[__local0__] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0716.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0716.yaml new file mode 100644 index 000000000000..dcbf0a5d9e34 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0716.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: nestedreferences/undefined + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.three.deadbeef + data.a[__local0__] = x + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0717.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0717.yaml new file mode 100644 index 000000000000..a2ce744cdbc3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0717.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "nestedreferences/vdoc ref: complete" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.generated.q[_] + x = data.a[__local0__] + } + + q := [2, 3] + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0718.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0718.yaml new file mode 100644 index 000000000000..7ece8347b819 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0718.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: "nestedreferences/vdoc ref: complete: ground" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.generated.q[1] + x = data.a[__local0__] + } + + q := [2, 3] + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0719.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0719.yaml new file mode 100644 index 000000000000..0b2978180f94 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0719.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "nestedreferences/vdoc ref: complete: no suffix" + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.generated.q + 2 = data.a[__local0__] + } + + q := 1 + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0720.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0720.yaml new file mode 100644 index 000000000000..edb60b1065ed --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0720.yaml @@ -0,0 +1,28 @@ +--- +cases: + - note: "nestedreferences/vdoc ref: partial object" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.generated.q[_] + x = data.a[__local0__] + } + + q[k] := v if { + o = {"a": 2, "b": 3, "c": 100} + o[k] = v + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0721.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0721.yaml new file mode 100644 index 000000000000..065b553fc1fb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0721.yaml @@ -0,0 +1,27 @@ +--- +cases: + - note: "nestedreferences/vdoc ref: partial object: ground" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.generated.q.b + x = data.a[__local0__] + } + + q[k] := v if { + o = {"a": 2, "b": 3, "c": 100} + o[k] = v + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0722.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0722.yaml new file mode 100644 index 000000000000..46786baecbc9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0722.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: "nestedreferences/vdoc ref: complete: nested bdoc ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.b[_] + __local1__ = data.generated.q[__local0__] + x = data.a[__local1__] + } + + q := {"hello": 1, "goodbye": 3, "deadbeef": 1000} + data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + want_result: + - x: + - 2 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0723.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0723.yaml new file mode 100644 index 000000000000..94faec7301d1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0723.yaml @@ -0,0 +1,32 @@ +--- +cases: + - note: "nestedreferences/vdoc ref: partial object: nested bdoc ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.b[_] + __local1__ = data.generated.q[__local0__] + x = data.a[__local1__] + } + + q[k] := v if { + o = {"deadbeef": 1000, "goodbye": 3, "hello": 1} + o[k] = v + } + data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + want_result: + - x: + - 2 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0724.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0724.yaml new file mode 100644 index 000000000000..8902267730e7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0724.yaml @@ -0,0 +1,36 @@ +--- +cases: + - note: "nestedreferences/vdoc ref: partial object: nested bdoc ref-2" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.d.e[_] + __local1__ = data.generated.q[__local0__] + x = data.a[__local1__] + } + + q[k] := v if { + data.strings[k] = v + } + data: + a: + - 1 + - 2 + - 3 + - 4 + d: + e: + - bar + - baz + strings: + bar: 2 + baz: 3 + foo: 1 + want_result: + - x: + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0725.yaml b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0725.yaml new file mode 100644 index 000000000000..1ed42b35e4c6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/nestedreferences/test-nestedreferences-0725.yaml @@ -0,0 +1,31 @@ +--- +cases: + - note: "nestedreferences/vdoc ref: multiple" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.a[_] + __local1__ = data.a[_] + __local2__ = data.generated.r[__local1__] + x = data.generated.q[__local0__].v[__local2__] + } + + q := [{"v": {}}, {"v": [0, 0, 1, 2]}, {"v": [0, 0, 3, 4]}, {"v": [0, 0]}, {}] + + r := [1, 2, 3, 4] + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0092.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0092.yaml new file mode 100644 index 000000000000..c521f7cb07ca --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0092.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrcontains/cidr contains subnet + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_contains("10.0.0.0/8", "10.1.0.0/24", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0093.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0093.yaml new file mode 100644 index 000000000000..3f42c897cc9e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0093.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrcontains/cidr does not contain subnet partial + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_contains("172.17.0.0/24", "172.17.0.0/16", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0094.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0094.yaml new file mode 100644 index 000000000000..5c0a89531fcd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0094.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrcontains/cidr does not contain subnet + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_contains("10.0.0.0/8", "192.168.1.0/24", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0095.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0095.yaml new file mode 100644 index 000000000000..b553db4c8628 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0095.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrcontains/cidr contains single ip subnet + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_contains("10.0.0.0/8", "10.1.1.1/32", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0096.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0096.yaml new file mode 100644 index 000000000000..d16fe69b98fb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0096.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrcontains/cidr contains subnet ipv6 + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_contains("2001:4860:4860::8888/32", "2001:4860:4860:1234::8888/40", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0097.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0097.yaml new file mode 100644 index 000000000000..b51e8753e24f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0097.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrcontains/cidr contains single ip subnet ipv6 + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_contains("2001:4860:4860::8888/32", "2001:4860:4860:1234:5678:1234:5678:8888/128", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0098.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0098.yaml new file mode 100644 index 000000000000..9e9fe3cd05da --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0098.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrcontains/cidr does not contain subnet partial ipv6 + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_contains("2001:4860::/96", "2001:4860::/32", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0099.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0099.yaml new file mode 100644 index 000000000000..e8a8027ee0fe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0099.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrcontains/cidr does not contain subnet ipv6 + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_contains("2001:4860::/32", "fd1e:5bfe:8af3:9ddc::/64", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0100.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0100.yaml new file mode 100644 index 000000000000..9ccbdd232d25 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0100.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: netcidrcontains/cidr subnet overlap malformed cidr a + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_contains("not-a-cidr", "192.168.1.67", x) + } + want_error_code: eval_builtin_error + want_error: "" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0101.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0101.yaml new file mode 100644 index 000000000000..6ae4fa23ecc9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0101.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: netcidrcontains/cidr subnet overlap malformed cidr b + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_contains("192.168.1.0/28", "not-a-cidr", x) + } + want_error_code: eval_builtin_error + want_error: "" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0102.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0102.yaml new file mode 100644 index 000000000000..263902619704 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0102.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrcontains/cidr contains ip + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_contains("10.0.0.0/8", "10.1.2.3", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0103.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0103.yaml new file mode 100644 index 000000000000..401e225a4daf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontains/test-netcidrcontains-0103.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrcontains/cidr does not contain ip + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_contains("10.0.0.0/8", "192.168.1.1", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0104.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0104.yaml new file mode 100644 index 000000000000..bc827762a3ef --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0104.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: netcidrcontainsmatches/strings + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + net.cidr_contains_matches("1.1.1.0/24", "1.1.1.1", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - - 1.1.1.0/24 + - 1.1.1.1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0105.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0105.yaml new file mode 100644 index 000000000000..d0c8b91ff9a6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0105.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: netcidrcontainsmatches/arrays + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + net.cidr_contains_matches(["1.1.2.0/24", "1.1.1.0/24"], ["1.1.1.1", "1.1.2.1"], __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - - 0 + - 1 + - - 1 + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0106.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0106.yaml new file mode 100644 index 000000000000..82ab4012bdbe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0106.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: netcidrcontainsmatches/arrays of tuples + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + net.cidr_contains_matches([["1.1.2.0/24", 1], "1.1.1.0/24"], ["1.1.1.1", "1.1.2.1"], __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - - 0 + - 1 + - - 1 + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0107.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0107.yaml new file mode 100644 index 000000000000..ed6e534fda50 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0107.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: netcidrcontainsmatches/bad array + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = data.a[0] + net.cidr_contains_matches(["1.1.2.0/24", "1.1.1.0/24"], ["1.1.1.1", __local2__], __local1__) + __local0__ = __local1__ + } + data: + a: + - 1 + want_error_code: eval_builtin_error + want_error: "net.cidr_contains_matches: operand 2: element must be string or non-empty array" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0108.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0108.yaml new file mode 100644 index 000000000000..de6268460cfa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0108.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: netcidrcontainsmatches/sets of strings + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + net.cidr_contains_matches({"1.1.1.0/24", "1.1.2.0/24"}, {"1.1.1.1", "1.1.2.1"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - - 1.1.1.0/24 + - 1.1.1.1 + - - 1.1.2.0/24 + - 1.1.2.1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0109.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0109.yaml new file mode 100644 index 000000000000..e2b994bd94a8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0109.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: netcidrcontainsmatches/sets of tuples + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + net.cidr_contains_matches({["1.1.1.0/24", "bar"], ["1.1.2.0/24", "foo"]}, {["1.1.1.1", "baz"], ["1.1.2.1", "qux"]}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - - - 1.1.1.0/24 + - bar + - - 1.1.1.1 + - baz + - - - 1.1.2.0/24 + - foo + - - 1.1.2.1 + - qux diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0110.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0110.yaml new file mode 100644 index 000000000000..0a2201c3cee8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0110.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: netcidrcontainsmatches/bad set + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = data.a[0] + net.cidr_contains_matches({["1.1.1.0/24", "bar"], ["1.1.2.0/24", "foo"]}, {__local2__, ["1.1.2.1", "qux"]}, __local1__) + __local0__ = __local1__ + } + data: + a: + - 1 + want_error_code: eval_builtin_error + want_error: "net.cidr_contains_matches: operand 2: element must be string or non-empty array" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0111.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0111.yaml new file mode 100644 index 000000000000..ecd685db2363 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0111.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: netcidrcontainsmatches/bad set tuple element + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + net.cidr_contains_matches({["1.1.1.0/24", "bar"], ["1.1.2.0/24", "foo"]}, {[], ["1.1.2.1", "qux"]}, __local1__) + __local0__ = __local1__ + } + want_error_code: eval_builtin_error + want_error: "net.cidr_contains_matches: operand 2: element must be string or non-empty array" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0112.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0112.yaml new file mode 100644 index 000000000000..ad01b43c95a1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrcontainsmatches/test-netcidrcontainsmatches-0112.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: netcidrcontainsmatches/objects + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + net.cidr_contains_matches({"k1": "1.1.1.1/24", "k2": ["1.1.1.2/24", 1]}, "1.1.1.128", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - - k1 + - 1.1.1.128 + - - k2 + - 1.1.1.128 diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0113.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0113.yaml new file mode 100644 index 000000000000..9c2850078cf1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0113.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: netcidrexpand/cidr includes host and broadcast + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_expand("192.168.1.1/30", x) + } + want_result: + - x: + - 192.168.1.0 + - 192.168.1.1 + - 192.168.1.2 + - 192.168.1.3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0114.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0114.yaml new file mode 100644 index 000000000000..b24e633299be --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0114.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: netcidrexpand/cidr last octet all 1s + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_expand("172.16.100.255/30", x) + } + data: {} + want_result: + - x: + - 172.16.100.252 + - 172.16.100.253 + - 172.16.100.254 + - 172.16.100.255 diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0115.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0115.yaml new file mode 100644 index 000000000000..d9a953bff427 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0115.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: netcidrexpand/cidr all bits + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_expand("192.168.1.1/32", x) + } + data: {} + want_result: + - x: + - 192.168.1.1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0116.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0116.yaml new file mode 100644 index 000000000000..7ce60477319e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrexpand/test-netcidrexpand-0116.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: netcidrexpand/cidr invalid mask + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_expand("192.168.1.1/33", x) + } + want_error_code: eval_builtin_error + want_error: "net.cidr_expand: invalid CIDR address: 192.168.1.1/33" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0086.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0086.yaml new file mode 100644 index 000000000000..c9f2fe76ead5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0086.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrintersects/cidr subnet overlaps + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_intersects("192.168.1.0/25", "192.168.1.64/25", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0087.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0087.yaml new file mode 100644 index 000000000000..be0a2c7a7e6a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0087.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrintersects/cidr subnet does not overlap + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_intersects("192.168.1.0/24", "192.168.2.0/24", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0088.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0088.yaml new file mode 100644 index 000000000000..70d3345caf65 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0088.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrintersects/cidr ipv6 subnet overlaps + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_intersects("fd1e:5bfe:8af3:9ddc::/64", "fd1e:5bfe:8af3:9ddc:1111::/72", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0089.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0089.yaml new file mode 100644 index 000000000000..774a4b708cc4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0089.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: netcidrintersects/cidr ipv6 subnet does not overlap + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + net.cidr_intersects("fd1e:5bfe:8af3:9ddc::/64", "2001:4860:4860::8888/32", x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0090.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0090.yaml new file mode 100644 index 000000000000..d6836346bd13 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0090.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: netcidrintersects/cidr subnet overlap malformed cidr a + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_intersects("not-a-cidr", "192.168.1.0/24", x) + } + want_error_code: eval_builtin_error + want_error: "" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0091.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0091.yaml new file mode 100644 index 000000000000..56d3eaea102e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrintersects/test-netcidrintersects-0091.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: netcidrintersects/cidr subnet overlap malformed cidr b + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_intersects("192.168.1.0/28", "not-a-cidr", x) + } + want_error_code: eval_builtin_error + want_error: "" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrisvalid/test_netcidrisvalid-0001.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrisvalid/test_netcidrisvalid-0001.yaml new file mode 100644 index 000000000000..a031ce81614c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrisvalid/test_netcidrisvalid-0001.yaml @@ -0,0 +1,67 @@ +--- +cases: + - note: valid ipv4 cidr + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_is_valid("192.168.1.0/24", __local0__) + x = __local0__ + } + data: {} + want_result: + - x: true + - note: empty cidr + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_is_valid("", __local0__) + x = __local0__ + } + data: {} + want_result: + - x: false + - note: random string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_is_valid("there goes a string", __local0__) + x = __local0__ + } + data: {} + want_result: + - x: false + - note: valid ipv4 address + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_is_valid("192.168.1.2", __local0__) + x = __local0__ + } + data: {} + want_result: + - x: false + - note: valid ipv6 cidr + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + net.cidr_is_valid("2002::1234:abcd:ffff:c0a8:101/64", __local0__) + x = __local0__ + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrmerge/test-ipv6-with-and-without-prefix.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrmerge/test-ipv6-with-and-without-prefix.yaml new file mode 100644 index 000000000000..08701f3826e4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrmerge/test-ipv6-with-and-without-prefix.yaml @@ -0,0 +1,60 @@ +--- +cases: + - note: netcidrmerge/cidr ipv6 with prefix + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["2601:600:8a80:207e:a57d:7567:e2c9:e7b3/128"], x) + } + want_result: + - x: + - 2601:600:8a80:207e:a57d:7567:e2c9:e7b3/128 + - note: netcidrmerge/cidr ipv6 with prefix, same twice + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["2601:600:8a80:207e:a57d:7567:e2c9:e7b3/128", "2601:600:8a80:207e:a57d:7567:e2c9:e7b3/128"], x) + } + want_result: + - x: + - 2601:600:8a80:207e:a57d:7567:e2c9:e7b3/128 + - note: netcidrmerge/cidr ipv6 with prefix, two different prefixes + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["2601:600:8a80:207e:a57d:7567:e2c9:e7b3/64", "2601:600:8a80:207e:a57d:7567:e2c9:e7b3/128"], x) + } + want_result: + - x: + - 2601:600:8a80:207e::/64 + - note: netcidrmerge/cidr ipv6 without prefix + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["2601:600:8a80:207e:a57d:7567:e2c9:e7b3"], x) + } + want_error: "eval_builtin_error: net.cidr_merge: IPv6 invalid: needs prefix length" + strict_error: true + - note: netcidrmerge/cidr ipv6 without prefix, same twice + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["2601:600:8a80:207e:a57d:7567:e2c9:e7b3", "2601:600:8a80:207e:a57d:7567:e2c9:e7b3"], x) + } + want_error: "eval_builtin_error: net.cidr_merge: IPv6 invalid: needs prefix length" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netcidrmerge/test-netcidrmerge0117.yaml b/third_party/opa/v1/test/cases/testdata/v1/netcidrmerge/test-netcidrmerge0117.yaml new file mode 100644 index 000000000000..242e6afe4d87 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netcidrmerge/test-netcidrmerge0117.yaml @@ -0,0 +1,214 @@ +--- +cases: + - note: netcidrmerge/cidr single subnet + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["192.0.128.0/24"], x) + } + want_result: + - x: + - 192.0.128.0/24 + - note: netcidrmerge/cidr duplicate + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["192.0.128.0/24", "192.0.128.0/24"], x) + } + want_result: + - x: + - 192.0.128.0/24 + - note: netcidrmerge/cidr IPv4 zero address + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["192.0.128.0/24", "0.0.0.0/0"], x) + } + want_result: + - x: + - 0.0.0.0/0 + - note: netcidrmerge/cidr merge subnets case 1 + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["192.0.128.0/24", "192.0.129.0/24"], x) + } + want_result: + - x: + - 192.0.128.0/23 + - note: netcidrmerge/cidr merge subnets case 2 + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["192.0.2.112/30", "192.0.2.116/31", "192.0.2.118/31"], x) + } + want_result: + - x: + - 192.0.2.112/29 + - note: netcidrmerge/cidr no overlap case 1 + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["192.0.129.0/24", "192.0.130.0/24"], x) + } + want_result: + - x: + - 192.0.129.0/24 + - 192.0.130.0/24 + - note: netcidrmerge/cidr no overlap case 2 + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["192.0.2.112/30", "192.0.2.116/32", "192.0.2.118/31"], x) + } + want_result: + - x: + - 192.0.2.112/30 + - 192.0.2.116/32 + - 192.0.2.118/31 + sort_bindings: true + - note: netcidrmerge/cidr mix case 1 + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["192.0.2.112/31", "192.0.2.116/31", "192.0.2.118/31"], x) + } + want_result: + - x: + - 192.0.2.112/31 + - 192.0.2.116/30 + - note: netcidrmerge/cidr mix case 2 + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["192.0.1.254/31", "192.0.2.0/28", "192.0.2.16/28", "192.0.2.32/28", "192.0.2.48/28", "192.0.2.64/28", "192.0.2.80/28", "192.0.2.96/28", "192.0.2.112/28", "192.0.2.128/28", "192.0.2.144/28", "192.0.2.160/28", "192.0.2.176/28", "192.0.2.192/28", "192.0.2.208/28", "192.0.2.224/28", "192.0.2.240/28", "192.0.3.0/28"], x) + } + want_result: + - x: + - 192.0.1.254/31 + - 192.0.2.0/24 + - 192.0.3.0/28 + - note: netcidrmerge/cidr IPv6 zero address case 1 + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["::/0", "fe80::1/128"], x) + } + want_result: + - x: + - ::/0 + - note: netcidrmerge/cidr IPv6 zero address case 2 + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["::/0", "::192.0.2.0/124", "ff00::101/128"], x) + } + want_result: + - x: + - ::/0 + - note: netcidrmerge/cidr IPv4 and IPv6 + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["fe80::/120", "192.0.2.0/24", "192.0.3.0/24", "192.0.4.0/25", "192.0.4.128/25"], x) + } + want_result: + - x: + - 192.0.2.0/23 + - 192.0.4.0/24 + - fe80::/120 + sort_bindings: true + - note: netcidrmerge/cidr empty + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge([], x) + } + want_result: + - x: [] + - note: netcidrmerge/cidr merge ip and subnets + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["192.0.2.112", "192.0.2.116/31", "192.0.2.118/31"], x) + } + want_result: + - x: + - 192.0.2.0/24 + - note: netcidrmerge/cidr merge ip addresses + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["192.0.128.0", "192.0.129.0"], x) + } + want_result: + - x: + - 192.0.128.0/23 + - note: netcidrmerge/cidr merge subnets set + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge({"192.0.2.112/30", "192.0.2.116/31", "192.0.2.118/31"}, x) + } + want_result: + - x: + - 192.0.2.112/29 + - note: netcidrmerge/cidr invalid IP + query: data.test.p = x + modules: + - | + package test + + p := x if { + net.cidr_merge(["foo"], x) + } + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/netlookupipaddr/test-netlookupipaddr.yaml b/third_party/opa/v1/test/cases/testdata/v1/netlookupipaddr/test-netlookupipaddr.yaml new file mode 100644 index 000000000000..5001662993b4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/netlookupipaddr/test-netlookupipaddr.yaml @@ -0,0 +1,46 @@ +--- +cases: + - note: net.lookup_ip_addr/simple ip4 returns that ip4 + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := net.lookup_ip_addr("10.0.0.0") + } + want_result: + - x: + - 10.0.0.0 + - note: net.lookup_ip_addr/simple ip6 returns that ip6 + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := net.lookup_ip_addr("::") + } + want_result: + - x: + - "::" + - note: net.lookup_ip_addr/localhost + query: data.test.p = x + modules: + - | + package test + + # one of these should be the case on any system + p if { + net.lookup_ip_addr("localhost") == {"127.0.0.1"} + } + + p if { + net.lookup_ip_addr("localhost") == {"127.0.0.1", "::1"} + } + + p if { + net.lookup_ip_addr("localhost") == {"::1"} + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0256.yaml b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0256.yaml new file mode 100644 index 000000000000..e9d5a85507d6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0256.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: numbersrange/one + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + numbers.range(0, 0, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0257.yaml b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0257.yaml new file mode 100644 index 000000000000..c27a6ca6c0d2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0257.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: numbersrange/ascending + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + numbers.range(-2, 3, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - -2 + - -1 + - 0 + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0258.yaml b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0258.yaml new file mode 100644 index 000000000000..15f1d2d2c221 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0258.yaml @@ -0,0 +1,38 @@ +--- +cases: + - note: numbersrange/descending + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + numbers.range(2, -3, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 2 + - 1 + - 0 + - -1 + - -2 + - -3 + - note: numbersrange/descending (cheap optimization) + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + numbers.range(5, 2, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - 5 + - 4 + - 3 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0259.yaml b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0259.yaml new file mode 100644 index 000000000000..edffc64bbdf8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0259.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: numbersrange/precision + query: data.generated.p = x + modules: + - | + package generated + + p if { + numbers.range(49649733057, 49649733060, [49649733057, 49649733058, 49649733059, 49649733060]) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0260.yaml b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0260.yaml new file mode 100644 index 000000000000..279c299c1778 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0260.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "numbersrange/error: floating-point number pos 1" + query: data.generated.p = x + modules: + - | + package generated + + p if { + numbers.range(3.14, 4) + } + want_error_code: eval_type_error + want_error: "numbers.range: operand 1 must be integer number but got floating-point number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0261.yaml b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0261.yaml new file mode 100644 index 000000000000..d97a3b48cae0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-0261.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "numbersrange/error: floating-point number pos 2" + query: data.generated.p = x + modules: + - | + package generated + + p if { + numbers.range(3, 3.14) + } + want_error_code: eval_type_error + want_error: "numbers.range: operand 2 must be integer number but got floating-point number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-issue-7269.yaml b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-issue-7269.yaml new file mode 100644 index 000000000000..a31be16a5f7b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/numbersrange/test-numbersrange-issue-7269.yaml @@ -0,0 +1,12 @@ +--- +cases: + - note: numbersrange/issue 7269 + query: data.test.p = x + modules: + - | + package test + + p if numbers.range(0, 1) + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/numbersrangestep/test-numbersrangestep.yaml b/third_party/opa/v1/test/cases/testdata/v1/numbersrangestep/test-numbersrangestep.yaml new file mode 100644 index 000000000000..4851274d0a3d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/numbersrangestep/test-numbersrangestep.yaml @@ -0,0 +1,103 @@ +--- +cases: + - note: numbersrangestep/ascending + query: data.test.p = x + modules: + - | + package test + + p := num if { + num := numbers.range_step(0, 10, 2) + } + want_result: + - x: + - 0 + - 2 + - 4 + - 6 + - 8 + - 10 + - note: numbersrangestep/descending + query: data.test.p = x + modules: + - | + package test + + p := num if { + num := numbers.range_step(0, -10, 2) + } + want_result: + - x: + - 0 + - -2 + - -4 + - -6 + - -8 + - -10 + - note: numbersrangestep/descending (cheap optimization) + query: data.test.p = x + modules: + - | + package test + + p = num if { + num := numbers.range_step(10, 3, 2) + } + want_result: + - x: + - 10 + - 8 + - 6 + - 4 + - note: numbersrangestep/negative + query: data.test.p = x + modules: + - | + package test + + p := num if { + num := numbers.range_step(0, 10, -2) + } + want_error_code: eval_builtin_error + want_error: "numbers.range_step: step must be a positive number above zero" + strict_error: true + - note: numbersrangestep/memoryexample + query: data.test.p = x + modules: + - | + package test + + p := num if { + num := numbers.range_step(1024, 4096, 1024) + } + want_result: + - x: + - 1024 + - 2048 + - 3072 + - 4096 + - note: numbersrangestep/equal + query: data.test.p = x + modules: + - | + package test + + p := num if { + num := numbers.range_step(2, 2, 2) + } + want_result: + - x: + - 2 + - note: numbersrangestep/notinrange + query: data.test.p = x + modules: + - | + package test + + p := num if { + num := numbers.range_step(2, 5, 2) + } + want_result: + - x: + - 2 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0300.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0300.yaml new file mode 100644 index 000000000000..91a47851dae1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0300.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: objectfilter/base + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + c: 7 + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0301.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0301.yaml new file mode 100644 index 000000000000..6d4cf072bd22 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0301.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectfilter/multiple roots set + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.filter({"a": 1, "b": 2, "c": 3, "e": 9}, {"a", "e"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0302.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0302.yaml new file mode 100644 index 000000000000..ce6eac427f98 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0302.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectfilter/multiple roots array + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.filter({"a": 1, "b": 2, "c": 3, "e": 9}, ["a", "e"], __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0303.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0303.yaml new file mode 100644 index 000000000000..95a302c4b351 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0303.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectfilter/multiple roots object + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.filter({"a": 1, "b": 2, "c": 3, "e": 9}, {"a": "foo", "e": ""}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 + e: 9 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0304.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0304.yaml new file mode 100644 index 000000000000..5f59992ba957 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0304.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: objectfilter/duplicate roots + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + c: 7 + d: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0305.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0305.yaml new file mode 100644 index 000000000000..c7d3c2b3216b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0305.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: objectfilter/empty roots set + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.filter({"a": 7}, set(), __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0306.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0306.yaml new file mode 100644 index 000000000000..42c46eff3e7f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0306.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: objectfilter/empty roots array + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.filter({"a": 7}, [], __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0307.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0307.yaml new file mode 100644 index 000000000000..7b417917895c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0307.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: objectfilter/empty roots object + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.filter({"a": 7}, {}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0308.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0308.yaml new file mode 100644 index 000000000000..a7d050c23616 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0308.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: objectfilter/empty object + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.filter({}, {"a"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0309.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0309.yaml new file mode 100644 index 000000000000..40f558566c78 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0309.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectfilter/error invalid object param type array input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.filter(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + input_term: '{"x": ["a"]}' + want_error_code: eval_type_error + want_error: "object.filter: operand 1 must be object but got array" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0310.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0310.yaml new file mode 100644 index 000000000000..a7cd45449f2b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0310.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectfilter/error invalid object param type bool input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.filter(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + input_term: '{"x": false}' + want_error_code: eval_type_error + want_error: "object.filter: operand 1 must be object but got boolean" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0311.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0311.yaml new file mode 100644 index 000000000000..5d5825c80ee4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0311.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectfilter/error invalid object param type number input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.filter(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + input_term: '{"x": 123}' + want_error_code: eval_type_error + want_error: "object.filter: operand 1 must be object but got number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0312.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0312.yaml new file mode 100644 index 000000000000..37d3b5120535 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0312.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectfilter/error invalid object param type string input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.filter(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + input_term: '{"x": "foo"}' + want_error_code: eval_type_error + want_error: "object.filter: operand 1 must be object but got string" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0313.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0313.yaml new file mode 100644 index 000000000000..47c7034efebb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0313.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectfilter/error invalid object param type nil input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.filter(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + input_term: '{"x": null}' + want_error_code: eval_type_error + want_error: "object.filter: operand 1 must be object but got null" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0314.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0314.yaml new file mode 100644 index 000000000000..3df1ae7833d2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0314.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectfilter/error invalid key param type string input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.filter({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + input_term: '{"x": "foo"}' + want_error_code: eval_type_error + want_error: "object.filter: operand 2 must be one of {object, set, array} but got string" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0315.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0315.yaml new file mode 100644 index 000000000000..a03937f4e332 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0315.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectfilter/error invalid key param type boolean input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.filter({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + input_term: '{"x": true}' + want_error_code: eval_type_error + want_error: "object.filter: operand 2 must be one of {object, set, array} but got boolean" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0316.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0316.yaml new file mode 100644 index 000000000000..06a776bc0e46 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0316.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectfilter/error invalid key param type number input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.filter({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + input_term: '{"x": 22}' + want_error_code: eval_type_error + want_error: "object.filter: operand 2 must be one of {object, set, array} but got number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0317.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0317.yaml new file mode 100644 index 000000000000..bf790a6eabe9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilter/test-objectfilter-0317.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectfilter/error invalid key param type nil input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.filter({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + input_term: '{"x": null}' + want_error_code: eval_type_error + want_error: "object.filter: operand 2 must be one of {object, set, array} but got null" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilteridempotent/test-objectfilteridempotent-0319.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilteridempotent/test-objectfilteridempotent-0319.yaml new file mode 100644 index 000000000000..b835a26ac54f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilteridempotent/test-objectfilteridempotent-0319.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: objectfilteridempotent/TestBuiltinObjectFilterIdempotent + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = {"a": 1, "b": 2, "c": 3} + object.filter(__local0__, {"a"}, __local1__) + __local1__ = {"a": 1} + object.filter(__local0__, {"b"}, __local2__) + __local2__ = {"b": 2} + object.filter(__local0__, {"c"}, __local3__) + __local3__ = {"c": 3} + __local0__ = {"a": 1, "b": 2, "c": 3} + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectfilternonstringkey/test-objectfilternonstringkey-0318.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectfilternonstringkey/test-objectfilternonstringkey-0318.yaml new file mode 100644 index 000000000000..ee6a5f921d58 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectfilternonstringkey/test-objectfilternonstringkey-0318.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: objectfilternonstringkey/non string root + query: data.generated.p = x + modules: + - | + package generated + + p if { + object.filter({"a": 1, [[7]]: 2}, {[[7]]}, __local1__) + __local0__ = __local1__ + __local0__ = {[[7]]: 2} + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0262.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0262.yaml new file mode 100644 index 000000000000..3869e62a0df8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0262.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: objectget/basic case . found + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.get({"a": "b"}, "a", "c", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: b diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0263.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0263.yaml new file mode 100644 index 000000000000..13b13bd0f532 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0263.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: objectget/basic case . not found + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.get({"a": "b"}, "c", "c", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: c diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0264.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0264.yaml new file mode 100644 index 000000000000..88baa9958e07 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0264.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: objectget/integer key found + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.get({1: 2}, 1, 3, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0265.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0265.yaml new file mode 100644 index 000000000000..4b55030ffd2c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0265.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: objectget/integer key . not found + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.get({1: 2}, 2, 3, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0266.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0266.yaml new file mode 100644 index 000000000000..b2b16b2129bd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0266.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: objectget/complex value . found + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.get({"a": {"b": "c"}}, "a", true, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + b: c diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0267.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0267.yaml new file mode 100644 index 000000000000..9fc05f39adb3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-0267.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: objectget/complex value . not found + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.get({"a": {"b": "c"}}, "b", true, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-path.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-path.yaml new file mode 100644 index 000000000000..27e0ef5430d3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectget/test-objectget-path.yaml @@ -0,0 +1,126 @@ +--- +cases: + - note: objectget/empty_path_returns_object + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := object.get({"a": 1}, [], 2) + } + want_result: + - x: + a: 1 + - note: objectget/path_with_single_element + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := object.get({"a": 1}, ["a"], 2) + } + want_result: + - x: 1 + - note: objectget/path_with_two_elements + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := object.get({"a": {"b": 1}}, ["a", "b"], 2) + } + want_result: + - x: 1 + - note: objectget/path_with_three_elements + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := object.get({"a": {"b": {"c": 1}}}, ["a", "b", "c"], 2) + } + want_result: + - x: 1 + - note: objectget/path_with_single_element_no_result + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := object.get({"a": 1}, ["b"], 2) + } + want_result: + - x: 2 + - note: objectget/path_with_two_elements_no_result + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := object.get({"a": {"b": 1}}, ["b", "a"], 2) + } + want_result: + - x: 2 + - note: objectget/path_with_three_elements_no_result + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := object.get({"a": {"b": {"c": 1}}}, ["a", "b", "a"], 2) + } + want_result: + - x: 2 + - note: objectget/path_with_non_string_keys + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := object.get({1: {"b": {[1, 2, 3]: 1}}}, [1, "b", [1, 2, 3]], 2) + } + want_result: + - x: 1 + - note: objectget/get_intermediate_non_object + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := object.get({"a": {"b": [1, 2, 3]}}, ["a", "b", "a"], 2) + } + want_result: + - x: 2 + - note: objectget/get_intermediate_array + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := object.get({"a": {"b": [{"c": 1}]}}, ["a", "b", 0, "c"], 2) + } + want_result: + - x: 1 + - note: objectget/get_for_non_object + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := object.get(input.obj, ["a"], 2) + } + input_term: '{"obj":"object"}' + want_error_code: eval_type_error + want_error: "object.get: operand 1 must be object but got string" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectkeys/test-objectkeys.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectkeys/test-objectkeys.yaml new file mode 100644 index 000000000000..6d3c65d3c35c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectkeys/test-objectkeys.yaml @@ -0,0 +1,77 @@ +--- +cases: + - note: objectkeys/string_keys_found + query: data.test.p = x + modules: + - | + package test + + p := object.keys({"a": 1, "b": 2}) + want_result: + - x: + - a + - b + - note: objectkeys/number_keys_found + query: data.test.p = x + modules: + - | + package test + + p := object.keys({1: 1, 2: 2}) + want_result: + - x: + - 1 + - 2 + - note: objectkeys/object_keys_found + query: data.test.p = x + modules: + - | + package test + + p := object.keys({{"a": 1}: 1, {"b": 2}: 2}) + want_result: + - x: + - a: 1 + - b: 2 + - note: objectkeys/set_keys_found + query: data.test.p = x + modules: + - | + package test + + p := object.keys({{"a"}: 1, {"b"}: 2}) + want_result: + - x: + - - a + - - b + - note: objectkeys/array_keys_found + query: data.test.p = x + modules: + - | + package test + + p := object.keys({["a"]: 1, ["b"]: 2}) + want_result: + - x: + - - a + - - b + - note: objectkeys/empty_result + query: data.test.p = x + modules: + - | + package test + + p := object.keys({}) + want_result: + - x: [] + - note: objectkeys/error_on_non_object + query: data.test.p = x + modules: + - | + package test + + p := object.keys(input.obj) + input_term: '{"obj":"object"}' + want_error_code: eval_type_error + want_error: "object.keys: operand 1 must be object but got string" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0279.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0279.yaml new file mode 100644 index 000000000000..eebee73e0e8e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0279.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectremove/base + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.remove({"a": 1, "b": {"c": 3}}, {"a"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + b: + c: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0280.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0280.yaml new file mode 100644 index 000000000000..3f1704f27d25 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0280.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: objectremove/multiple keys set + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.remove({"a": 1, "b": {"c": 3}, "d": 4}, {"b", "d"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0281.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0281.yaml new file mode 100644 index 000000000000..c3f89f0ec28f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0281.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: objectremove/multiple keys array + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.remove({"a": 1, "b": {"c": 3}, "d": 4}, ["d", "b"], __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0282.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0282.yaml new file mode 100644 index 000000000000..a9fa6557c3b3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0282.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: objectremove/multiple keys object + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.remove({"a": 1, "b": {"c": 3}, "d": 4}, {"b": 1, "d": ""}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0283.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0283.yaml new file mode 100644 index 000000000000..2d9047ffed8a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0283.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: objectremove/multiple keys object nested + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.remove({"a": {"b": {"c": 2}}, "x": 123}, {"a": {"b": {"foo": "bar"}}}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + x: 123 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0284.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0284.yaml new file mode 100644 index 000000000000..5f19b69d4919 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0284.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: objectremove/empty object + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.remove({}, {"a", "b"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0285.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0285.yaml new file mode 100644 index 000000000000..87745acaee0c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0285.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/empty keys set + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.remove({"a": 1, "b": {"c": 3}}, set(), __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 + b: + c: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0286.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0286.yaml new file mode 100644 index 000000000000..53ee22afd4f2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0286.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/empty keys array + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.remove({"a": 1, "b": {"c": 3}}, [], __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 + b: + c: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0287.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0287.yaml new file mode 100644 index 000000000000..066f2215a2a2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0287.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/empty keys obj + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.remove({"a": 1, "b": {"c": 3}}, {}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 + b: + c: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0288.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0288.yaml new file mode 100644 index 000000000000..5416527902be --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0288.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/key doesnt exist + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.remove({"a": 1, "b": {"c": 3}}, {"z"}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 + b: + c: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0289.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0289.yaml new file mode 100644 index 000000000000..ac14e8fcf683 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0289.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/error invalid object param type array input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.remove(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": ["a"]}' + want_error_code: eval_type_error + want_error: "object.remove: operand 1 must be object but got array" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0290.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0290.yaml new file mode 100644 index 000000000000..288147a7ac0d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0290.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/error invalid object param type bool input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.remove(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": false}' + want_error_code: eval_type_error + want_error: "object.remove: operand 1 must be object but got boolean" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0291.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0291.yaml new file mode 100644 index 000000000000..bd530591643e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0291.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/error invalid object param type number input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.remove(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": 123}' + want_error_code: eval_type_error + want_error: "object.remove: operand 1 must be object but got number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0292.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0292.yaml new file mode 100644 index 000000000000..cbecf44444e4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0292.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/error invalid object param type string input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.remove(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": "foo"}' + want_error_code: eval_type_error + want_error: "object.remove: operand 1 must be object but got string" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0293.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0293.yaml new file mode 100644 index 000000000000..12bf8f35eeac --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0293.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/error invalid object param type nil input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.remove(__local2__, {"a"}, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": null}' + want_error_code: eval_type_error + want_error: "object.remove: operand 1 must be object but got null" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0294.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0294.yaml new file mode 100644 index 000000000000..d8a0351749cb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0294.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/error invalid key param type string input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.remove({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": "foo"}' + want_error_code: eval_type_error + want_error: "object.remove: operand 2 must be one of {object, set, array} but got string" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0295.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0295.yaml new file mode 100644 index 000000000000..fdf7ae13fe85 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0295.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/error invalid key param type boolean input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.remove({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": true}' + want_error_code: eval_type_error + want_error: "object.remove: operand 2 must be one of {object, set, array} but got boolean" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0296.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0296.yaml new file mode 100644 index 000000000000..8ac4f70e35c6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0296.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/error invalid key param type number input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.remove({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": 22}' + want_error_code: eval_type_error + want_error: "object.remove: operand 2 must be one of {object, set, array} but got number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0297.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0297.yaml new file mode 100644 index 000000000000..18f2da266be6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremove/test-objectremove-0297.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectremove/error invalid key param type nil input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.x + object.remove({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"x": null}' + want_error_code: eval_type_error + want_error: "object.remove: operand 2 must be one of {object, set, array} but got null" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremoveidempotent/test-objectremoveidempotent-0298.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremoveidempotent/test-objectremoveidempotent-0298.yaml new file mode 100644 index 000000000000..1ff19ea3d765 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremoveidempotent/test-objectremoveidempotent-0298.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: objectremoveidempotent/TestBuiltinObjectRemoveIdempotent + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = {"a": 1, "b": 2, "c": 3} + object.remove(__local0__, {"a"}, __local1__) + __local1__ = {"b": 2, "c": 3} + object.remove(__local0__, {"b"}, __local2__) + __local2__ = {"a": 1, "c": 3} + object.remove(__local0__, {"c"}, __local3__) + __local3__ = {"a": 1, "b": 2} + __local0__ = {"a": 1, "b": 2, "c": 3} + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectremovenonstringkey/test-objectremovenonstringkey-0299.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectremovenonstringkey/test-objectremovenonstringkey-0299.yaml new file mode 100644 index 000000000000..833c4570534e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectremovenonstringkey/test-objectremovenonstringkey-0299.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: objectremovenonstringkey/non string root + query: data.generated.p = x + modules: + - | + package generated + + p if { + object.remove({"a": 1, [[7]]: 2}, {[[7]]}, __local1__) + __local0__ = __local1__ + __local0__ = {"a": 1} + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0268.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0268.yaml new file mode 100644 index 000000000000..dc815e185798 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0268.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: objectunion/both empty + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.union({}, {}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0269.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0269.yaml new file mode 100644 index 000000000000..ee45d1fad25f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0269.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: objectunion/left empty + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.union({}, {"a": 1}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0270.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0270.yaml new file mode 100644 index 000000000000..2a34805cb185 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0270.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: objectunion/right empty + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.union({"a": 1}, {}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0271.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0271.yaml new file mode 100644 index 000000000000..9d031d5352b5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0271.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: objectunion/base + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.union({"a": 1}, {"b": 2}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 1 + b: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0272.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0272.yaml new file mode 100644 index 000000000000..2a751078e25f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0272.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: objectunion/nested + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.union({"a": {"b": {"c": 1}}}, {"b": 2}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + c: 1 + b: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0273.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0273.yaml new file mode 100644 index 000000000000..bb87dd051536 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0273.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: objectunion/nested reverse + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.union({"b": 2}, {"a": {"b": {"c": 1}}}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + c: 1 + b: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0274.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0274.yaml new file mode 100644 index 000000000000..3b6177f7078c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0274.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: objectunion/conflict simple + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.union({"a": 1}, {"a": 2}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0275.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0275.yaml new file mode 100644 index 000000000000..774928abbe70 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0275.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: objectunion/conflict nested and extra field + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.union({"a": 1}, {"a": {"b": {"c": 1}}, "d": 7}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: + c: 1 + d: 7 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0276.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0276.yaml new file mode 100644 index 000000000000..e4fa7ef5d875 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0276.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: objectunion/conflict multiple + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + object.union({"a": {"b": {"c": 1}}, "e": 1}, {"a": {"b": "foo", "b1": "bar"}, "d": 7, "e": 17}, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + a: + b: foo + b1: bar + d: 7 + e: 17 diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0277.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0277.yaml new file mode 100644 index 000000000000..430fc6988637 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0277.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectunion/error wrong lhs type input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.a + object.union(__local2__, {"b": 2}, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"a": [1, 2, 3]}' + want_error_code: eval_type_error + want_error: "object.union: operand 1 must be object but got array" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0278.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0278.yaml new file mode 100644 index 000000000000..661bb80699b1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectunion/test-objectunion-0278.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: objectunion/error wrong rhs type input + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.b + object.union({"a": 1}, __local2__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"b": [1, 2, 3]}' + want_error_code: eval_type_error + want_error: "object.union: operand 2 must be object but got array" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/objectunionn/test-objectunionn-0001.yaml b/third_party/opa/v1/test/cases/testdata/v1/objectunionn/test-objectunionn-0001.yaml new file mode 100644 index 000000000000..5de72493cc26 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/objectunionn/test-objectunionn-0001.yaml @@ -0,0 +1,83 @@ +--- +cases: + - note: objectunionn/empty array + query: data.test.p = x + modules: + - | + package test + + p := object.union_n([{}]) + want_result: + - x: {} + - note: objectunionn/single item array + query: data.test.p = x + modules: + - | + package test + + p := object.union_n([{"foo": "bar"}]) + want_result: + - x: + foo: bar + - note: objectunionn/merge objects + query: data.test.x = x + modules: + - | + package test + + x := object.union_n([{"foo": "bar"}, {"x": "y"}]) + want_result: + - x: + foo: bar + x: "y" + - note: objectunionn/merge objects conflict + query: data.test.x = x + modules: + - | + package test + + x := object.union_n([{"foo": "bar"}, {"foo": "baz"}]) + want_result: + - x: + foo: baz + - note: objectunionn/merge objects extended + query: data.test.x = x + modules: + - | + package test + + x := object.union_n([ + { + "a": 1, + "b": 2, + "c": 3, + }, + { + "foo": "baz", + "a": "a", + "b": 2, + "d": 4, + }, + { + "a": "final A!", + "e": 5.0, + }, + ]) + want_result: + - x: + a: final A! + b: 2 + c: 3 + d: 4 + e: 5 + foo: baz + - note: "# 5073 regression test" + query: data.test.x = x + modules: + - | + package test + + x := object.union_n(input) + input_term: '[{"foo": 1}, {"bar": 2}, "baz"]' + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0984.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0984.yaml new file mode 100644 index 000000000000..ddc726919465 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0984.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: partialdocconstants/obj-1 + query: data.ex.foo.bar = x + modules: + - | + package ex + + foo["bar"] := 0 + + foo["baz"] := 1 + + foo["*"] := [1, 2, 3] if { + input.foo = 7 + } + + bar contains "x" + + bar contains "y" + + bar contains "*" if { + input.foo = 7 + } + data: {} + want_result: + - x: 0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0985.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0985.yaml new file mode 100644 index 000000000000..7e12a24b53fe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0985.yaml @@ -0,0 +1,34 @@ +--- +cases: + - note: partialdocconstants/obj + query: data.ex.foo = x + modules: + - | + package ex + + foo["bar"] := 0 + + foo["baz"] := 1 + + foo["*"] := [1, 2, 3] if { + input.foo = 7 + } + + bar contains "x" + + bar contains "y" + + bar contains "*" if { + input.foo = 7 + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = 0 + } + data: {} + want_result: + - x: + bar: 0 + baz: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0986.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0986.yaml new file mode 100644 index 000000000000..a0354cfde63f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0986.yaml @@ -0,0 +1,49 @@ +--- +cases: + - note: partialdocconstants/obj-all + query: data.ex.foo = x + modules: + - | + package partial.ex + + foo["bar"] := 0 + + foo["baz"] := 1 + + foo["*"] := [1, 2, 3] if { + input.foo = 7 + } + - | + package ex + + foo["bar"] := 0 + + foo["baz"] := 1 + + foo["*"] := [1, 2, 3] if { + input.foo = 7 + } + + bar contains "x" + + bar contains "y" + + bar contains "*" if { + input.foo = 7 + } + - | + package topdown_test_partial + + __result__ := _result if { + data.partial.ex.foo = _result + } + data: {} + input_term: '{"foo": 7}' + want_result: + - x: + "*": + - 1 + - 2 + - 3 + bar: 0 + baz: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0987.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0987.yaml new file mode 100644 index 000000000000..4c6a89af3d74 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0987.yaml @@ -0,0 +1,42 @@ +--- +cases: + - note: partialdocconstants/set-1 + query: data.ex.bar.x = x + modules: + - | + package topdown_test_partial + + __result__ := _result if { + data.partial.ex.foo = _result + } + - | + package partial.ex + + foo["bar"] := 0 + + foo["baz"] := 1 + + foo["*"] := [1, 2, 3] if { + input.foo = 7 + } + - | + package ex + + foo["bar"] := 0 + + foo["baz"] := 1 + + foo["*"] := [1, 2, 3] if { + input.foo = 7 + } + + bar contains "x" + + bar contains "y" + + bar contains "*" if { + input.foo = 7 + } + data: {} + want_result: + - x: x diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0988.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0988.yaml new file mode 100644 index 000000000000..4a34e82fda46 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0988.yaml @@ -0,0 +1,45 @@ +--- +cases: + - note: partialdocconstants/set + query: data.ex.bar = x + modules: + - | + package ex + + foo["bar"] := 0 + + foo["baz"] := 1 + + foo["*"] := [1, 2, 3] if { + input.foo = 7 + } + + bar contains "x" + + bar contains "y" + + bar contains "*" if { + input.foo = 7 + } + - | + package topdown_test_partial + + __result__ := _result if { + _result = "x" + } + - | + package partial.ex + + foo["bar"] := 0 + + foo["baz"] := 1 + + foo["*"] := [1, 2, 3] if { + input.foo = 7 + } + data: {} + want_result: + - x: + - x + - "y" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0989.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0989.yaml new file mode 100644 index 000000000000..b467c447a3c8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialdocconstants/test-partialdocconstants-0989.yaml @@ -0,0 +1,47 @@ +--- +cases: + - note: partialdocconstants/set-all + query: data.ex.bar = x + modules: + - | + package ex + + foo["bar"] := 0 + + foo["baz"] := 1 + + foo["*"] := [1, 2, 3] if { + input.foo = 7 + } + + bar contains "x" + + bar contains "y" + + bar contains "*" if { + input.foo = 7 + } + - | + package topdown_test_partial + + __result__ := _result if { + data.partial.ex.bar = _result + } + - | + package partial.ex + + bar contains "x" + + bar contains "y" + + bar contains "*" if { + input.foo = 7 + } + data: {} + input_term: '{"foo": 7}' + want_result: + - x: + - "*" + - x + - "y" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialiter/test-partialiter-001.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialiter/test-partialiter-001.yaml new file mode 100644 index 000000000000..a2ae4125ab12 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialiter/test-partialiter-001.yaml @@ -0,0 +1,46 @@ +--- +cases: + - note: partialiter/sets unique + query: data.test.p = x + modules: + - | + package test + + p := count([x | q[x]]) + + q contains 1 + + q contains 1 + + q contains 2 + want_result: + - x: 2 + - note: partialiter/objects unique + query: data.test.p = x + modules: + - | + package test + + p := count([x | q[x]]) + + q[1] := 1 + + q[1] := 1 + + q[2] := 1 + want_result: + - x: 2 + - note: partialiter/objects conflict + query: data.test.p = x + modules: + - | + package test + + p := count([x | q[x]]) + + q[1] := 1 + + q[1] := 2 + + q[2] := 1 + want_error_code: eval_conflict_error diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0519.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0519.yaml new file mode 100644 index 000000000000..6a29ecce77e6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0519.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: partialobjectdoc/identity + query: data.generated.p = x + modules: + - | + package generated + + p[k] := v if { + data.b[k] = v + } + data: + b: + v1: hello + v2: goodbye + want_result: + - x: + v1: hello + v2: goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0520.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0520.yaml new file mode 100644 index 000000000000..68c2ff2feed5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0520.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: partialobjectdoc/composites + query: data.generated.p = x + modules: + - | + package generated + + p[k] := v if { + data.d[k] = v + } + data: + d: + e: + - bar + - baz + want_result: + - x: + e: + - bar + - baz diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0521.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0521.yaml new file mode 100644 index 000000000000..888e7467f23c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0521.yaml @@ -0,0 +1,39 @@ +--- +cases: + - note: partialobjectdoc/body/join var + query: data.generated.p = x + modules: + - | + package generated + + p[k] := v if { + data.a[i] = v + data.g[k][i] = v + } + data: + a: + - "1" + - "2" + - "3" + - "4" + g: + a: + - "1" + - "0" + - "0" + - "0" + b: + - "0" + - "2" + - "0" + - "0" + c: + - "0" + - "0" + - "0" + - "4" + want_result: + - x: + a: "1" + b: "2" + c: "4" diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0522.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0522.yaml new file mode 100644 index 000000000000..98108d5e7601 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0522.yaml @@ -0,0 +1,41 @@ +--- +cases: + - note: partialobjectdoc/composite value + query: data.generated.p = x + modules: + - | + package generated + + p[k] := [v1, {"v2": v2}] if { + data.g[k] = x + x[v1] = v2 + v2 != 0 + } + data: + g: + a: + - 1 + - 0 + - 0 + - 0 + b: + - 0 + - 2 + - 0 + - 0 + c: + - 0 + - 0 + - 0 + - 4 + want_result: + - x: + a: + - 0 + - v2: 1 + b: + - 1 + - v2: 2 + c: + - 3 + - v2: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0523.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0523.yaml new file mode 100644 index 000000000000..dd40b46e206e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0523.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: partialobjectdoc/same key/value pair + query: data.generated.p = x + modules: + - | + package generated + + p[k] := 1 if { + ks = ["a", "b", "c", "a"] + ks[_] = k + } + data: {} + want_result: + - x: + a: 1 + b: 1 + c: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0524.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0524.yaml new file mode 100644 index 000000000000..d2ab9a52edcc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-0524.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: partialobjectdoc/non-string key + query: data.generated.p = x + modules: + - | + package generated + + p[k] := 1 if { + ks = [1, {}, null] + ks[_] = k + } + data: {} + want_result: + - x: + "{}": 1 + "1": 1 + "null": 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-ref.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-ref.yaml new file mode 100644 index 000000000000..60508f2ff332 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-partialobjectdoc-ref.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: partialobjectdoc/ref + query: data.generated.q = x + modules: + - | + package generated + + p.q[k] := v if { + k := ["foo", "bar"][v] + } + + p.baz := 2 + + q if { + x := "bar" + y := "q" + p[y][x] == 1 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-wasm-cases.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-wasm-cases.yaml new file mode 100644 index 000000000000..8b8a7e2ae660 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialobjectdoc/test-wasm-cases.yaml @@ -0,0 +1,132 @@ +--- +cases: + - note: wasm/additive + query: data.x.q = x + modules: + - | + package x + + p["a"] := 1 + + p["b"] := 2 + + q if { + p == {"a": 1, "b": 2} + } + want_result: + - x: true + - note: wasm/additive (negative) + query: data.x.p = input + modules: + - | + package x + + p["a"] := 1 + + p["b"] := 2 + + p["c"] := 3 + input: + a: 1 + b: 2 + want_result: [] + - note: wasm/input + query: data.x.q = x + modules: + - | + package x + + p["a"] := 1 if input.x = 1 + + p["b"] := 2 if input.y = 2 + + q if { + p == {"a": 1, "b": 2} + } + input: + x: 1 + "y": 2 + want_result: + - x: true + - note: wasm/input (negative) + query: data.x.q = x + modules: + - | + package x + + p["a"] := 1 if input.x = 1 + + p["b"] := 2 if input.y = 2 + + p["c"] := 3 if input.z = 3 + + q if { + p == data.z + } + data: + z: + a: 1 + b: 2 + input: + x: 1 + "y": 2 + want_result: + - x: true + - note: wasm/composites + query: data.x.q = x + modules: + - | + package x + + p[x] := [y] if { + x = "a" + y = 1 + } + + p[x] := [y] if { + x = "b" + y = 2 + } + + q if p == {"a": [1], "b": [2]} + want_result: + - x: true + - note: wasm/conflict error + query: data.x.q = x + modules: + - | + package x + + p["x"] := 1 + + p["x"] := 2 + + q if { + p == data.z + } + data: + z: + a: 1 + want_error_code: eval_conflict_error + want_error: "eval_conflict_error: complete rules must not produce multiple outputs" + - note: wasm/object dereference + query: data.x.q = x + modules: + - | + package x + + p["a"] := {"b": 1} + + q if { + p.a.b = 1 + } + want_result: + - x: true + - note: wasm/object dereference (negative) + query: data.x.p.a.b = 1 + modules: + - | + package x + + p["a"] := {"b": 2} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-issue-3369.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-issue-3369.yaml new file mode 100644 index 000000000000..948929ff853c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-issue-3369.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: partialsetdoc/unexpected 'var requires evaluation' + query: data.x.p[x] = z + modules: + - | + package x + + p contains a if { + a := q + } + + q contains b if { + b := 1 + } + want_result: + - x: + - 1 + z: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-issue-3376.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-issue-3376.yaml new file mode 100644 index 000000000000..95ca0a22b205 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-issue-3376.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: partialsetdoc/iteration + query: data.foo.p = x + modules: + - | + package foo + + p if { + q[i][j] = v # this fails! + } + + q contains x if { + x = r + } + + r contains x if { + x = [1] + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-issue-3819.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-issue-3819.yaml new file mode 100644 index 000000000000..3f4154d3afed --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-issue-3819.yaml @@ -0,0 +1,27 @@ +--- +cases: + - note: "partialsetdoc: object sort while iter" + query: data.foo.p = x + modules: + - | + package foo + + p contains x if { + data.a[x] # iterates data.a + q[_] + } + + q contains x if { + x = data.a # inserts data.a into a set, sorts the object keys + } + data: + a: + apples: "2" + bananas: "1" + clementines: "3" + want_result: + - x: + - apples + - bananas + - clementines + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0511.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0511.yaml new file mode 100644 index 000000000000..f3bfba8d2bf2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0511.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: partialsetdoc/array values + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.a[i] = x + } + data: + a: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: + - "1" + - "2" + - "3" + - "4" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0512.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0512.yaml new file mode 100644 index 000000000000..d0dde42c9423 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0512.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: partialsetdoc/array indices + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.a[x] = _ + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 0 + - 1 + - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0513.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0513.yaml new file mode 100644 index 000000000000..ec750442e976 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0513.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: partialsetdoc/object keys + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.b[x] = _ + } + data: + b: + v1: hello + v2: goodbye + want_result: + - x: + - v1 + - v2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0514.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0514.yaml new file mode 100644 index 000000000000..f51fee98d595 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0514.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: partialsetdoc/object values + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.b[i] = x + } + data: + b: + v1: hello + v2: goodbye + want_result: + - x: + - goodbye + - hello + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0515.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0515.yaml new file mode 100644 index 000000000000..5cf42b7e8c12 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0515.yaml @@ -0,0 +1,32 @@ +--- +cases: + - note: partialsetdoc/nested composites + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.f[i] = x + } + data: + f: + - xs: + - "1" + ys: + - "2" + - xs: + - "2" + ys: + - "3" + want_result: + - x: + - xs: + - "1" + ys: + - "2" + - xs: + - "2" + ys: + - "3" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0516.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0516.yaml new file mode 100644 index 000000000000..988435128276 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0516.yaml @@ -0,0 +1,31 @@ +--- +cases: + - note: partialsetdoc/deep ref/heterogeneous + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.c[i][j][k] = x + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: + - null + - false + - true + - 3.14159 + - foo + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0517.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0517.yaml new file mode 100644 index 000000000000..ca97c5f5bdb6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0517.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: partialsetdoc/composite var value + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.a[i] + x = [i, __local0__] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - - 0 + - 1 + - - 1 + - 2 + - - 2 + - 3 + - - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0518.yaml b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0518.yaml new file mode 100644 index 000000000000..b109420ce3fd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/partialsetdoc/test-partialsetdoc-0518.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: partialsetdoc/composite key + query: data.generated.p = x + modules: + - | + package generated + + p contains [x, {"y": y}] if { + x = 1 + y = 2 + } + data: {} + want_result: + - x: + - - 1 + - "y": 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/planner-ir/test-array-ir-unify.yaml b/third_party/opa/v1/test/cases/testdata/v1/planner-ir/test-array-ir-unify.yaml new file mode 100644 index 000000000000..52d7c8709ee3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/planner-ir/test-array-ir-unify.yaml @@ -0,0 +1,55 @@ +--- +cases: + - note: ir/unification array and array comprehension + query: data.test.p = x + modules: + - | + package test + + p := foo if { + [foo] = [x | x := 1] + } + want_result: + - x: 1 + - note: ir/unification array comprehension and array + query: data.test.p = x + modules: + - | + package test + + p := foo if { + [x | x := 1] = [foo] + } + want_result: + - x: 1 + - note: ir/fixpoint key/value (negative) + query: data.test.p = x + modules: + - | + package test + + p if { + some foo + foo == input.foos[foo] + } + input: + foos: + - foo + want_result: [] + - note: ir/fixpoint key/value + query: data.test.p = x + modules: + - | + package test + + p := foo if { + some foo + foo == input.foos[foo] + } + input: + foos: + - 2 + - 1 + - 0 + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/planner-ir/test-call-dynamic.yaml b/third_party/opa/v1/test/cases/testdata/v1/planner-ir/test-call-dynamic.yaml new file mode 100644 index 000000000000..0c0b1835f385 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/planner-ir/test-call-dynamic.yaml @@ -0,0 +1,168 @@ +--- +cases: + - note: ir/call_dynamic in comprehension + query: data.test.p = x + modules: + - | + package test + + p := x if { + b := input + x := {y | y := data.a[b][_]} + } + - | + package a + + b := {"foo", "bar"} + - | + package a + + c := {"x", "y"} + input: b + want_result: + - x: + - bar + - foo + sort_bindings: true + - note: ir/no call-dynamic with mixed partial rules + query: data.test.p = x + modules: + - | + package test + + p := data.a.b[c] if c := "c" + - | + package a + + b[c] := "C" if c := "c" + + b["d"] := "D" + want_result: + - x: C + - note: ir/call-dynamic with mixed partial rules + query: data.test.p = x + modules: + - | + package test + + p := a[b].c if b := "b" + + a.b.c := "C" + + a.x.d := "D" + want_result: + - x: C + - note: ir/no call-dynamic with mixed partial rules, ref heads + query: data.test.p = x + modules: + - | + package test + + p := a.b[c] if c := "c" + + a.b[c] := "C" if c := "c" + + a.b.d := "D" + want_result: + - x: C + - note: ir/call-dynamic with mixed partial rules, ref heads + query: data.test.p = x + modules: + - | + package test + + p := a[b][c] if { + b := "b" + c := "c" + } + + a.b.c := "C" + + a.x.d := "D" + want_result: + - x: C + - note: ir/call-dynamic with ref heads, issue 5839 + query: data.test.p = x + modules: + - | + package test + + p := a[b][c].allow if { + b := "b" + c := "c" + } + + a.b.c.allow := true + + a.x.d.allow := true + + a.y[x] := "E" if x := "x" + want_result: + - x: true + - note: ir/call-dynamic with ref heads, issue 5839, penultimate + query: data.test.p = x + modules: + - | + package test + + p := a[b][c] if { + b := "b" + c := "c" + } + + a.b.c := true + + a.x.d := true + + a.y := "E" + want_result: + - x: true + - note: ir/call-dynamic with ref heads and unrelated rule (issue 7399) + query: data.test.p = x + modules: + - | + package test + p := data[first].allow.see.something_else if first := "primary" + - | + package primary + allow[action].something if action := "show" + allow.see.something_else if true + - | + package unrelated + allow.other.stuff if true + want_result: + - x: true + - note: ir/call-dynamic not used with ref heads and mixed-length rules (issue 7399) + query: data.test.q = x + modules: + - | + package test + + p.allow[action][resource] if { action := "list"; resource := "fruit" } + + p.unrelated.eat.veggies if true + + q := p[input.rule][input.action][input.resource] + input: + rule: allow + action: list + resource: fruit + want_result: + - x: true + - note: ir/call-dynamic not used with ref heads and mixed-length rules (issue 7399), second rule + query: data.test.q = x + modules: + - | + package test + + p.allow[action][resource] if { action := "list"; resource := "fruit" } + + p.unrelated.eat.veggies if true + + q := p[input.rule][input.action][input.resource] + input: + rule: unrelated + action: eat + resource: veggies + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/providers-aws/aws-sign_req-errors.yaml b/third_party/opa/v1/test/cases/testdata/v1/providers-aws/aws-sign_req-errors.yaml new file mode 100644 index 000000000000..a57f8ca033ac --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/providers-aws/aws-sign_req-errors.yaml @@ -0,0 +1,189 @@ +--- +cases: + - note: providers-aws-sign_req/failure-simple-missing http request keys + query: data.test.p = x + modules: + - | + package test + + req := {} + + aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_session_token": "MYAWSSECURITYTOKENGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + } + + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=0047a7016c81e5c7f29cd522f97e911e04fc472fced0aee7916cbc287ad6c8e3", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "http://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_error_code: eval_type_error + want_error: 'providers.aws.sign_req: operand 1 missing required request parameters(s): {"method", "url"}' + strict_error: true + - note: providers-aws-sign_req/failure-simple-invalid type http request keys + query: data.test.p = x + modules: + - | + package test + + req := {"method": set(), "url": set()} + + aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_session_token": "MYAWSSECURITYTOKENGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + } + + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=0047a7016c81e5c7f29cd522f97e911e04fc472fced0aee7916cbc287ad6c8e3", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "http://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_error_code: eval_type_error + want_error: 'providers.aws.sign_req: operand 1 invalid values for required request parameters(s): {"method", "url"}' + strict_error: true + - note: providers-aws-sign_req/failure-simple-missing aws keys + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "http://example.com"} + + aws_config := {"example": "example"} + + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=0047a7016c81e5c7f29cd522f97e911e04fc472fced0aee7916cbc287ad6c8e3", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "http://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_error_code: eval_type_error + want_error: 'providers.aws.sign_req: operand 2 missing required AWS config parameters(s): {"aws_access_key", "aws_region", "aws_secret_access_key", "aws_service"}' + strict_error: true + - note: providers-aws-sign_req/failure-simple-invalid type aws keys + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "http://example.com"} + + aws_config := { + "aws_access_key": 1, + "aws_secret_access_key": 2, + "aws_session_token": 3, + "aws_service": 4, + "aws_region": 5, + } + + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=0047a7016c81e5c7f29cd522f97e911e04fc472fced0aee7916cbc287ad6c8e3", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "http://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_error_code: eval_type_error + want_error: 'providers.aws.sign_req: operand 2 invalid values for required AWS config parameters(s): {"aws_access_key", "aws_region", "aws_secret_access_key", "aws_service"}' + strict_error: true + - note: providers-aws-sign_req/failure-simple-bad timestamp + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "http://example.com"} + + aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_session_token": "MYAWSSECURITYTOKENGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + } + + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=0047a7016c81e5c7f29cd522f97e911e04fc472fced0aee7916cbc287ad6c8e3", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "http://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, -1e9) == expected + } + want_error_code: eval_type_error + want_error: "providers.aws.sign_req: operand 3 could not convert time_ns value into a unix timestamp" + strict_error: true + - note: providers-aws-sign_req/failure-simple-bad-type-payload-signing-config + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar"}} + + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": "false"} + + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=8f1dc7c9b9978356a0d0989fd26a95307f4f8a4aa264d8220647b7097d839952", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "https://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_error_code: eval_type_error + want_error: "providers.aws.sign_req: operand 2 invalid value for 'disable_payload_signing' in AWS config" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/providers-aws/aws-sign_req.yaml b/third_party/opa/v1/test/cases/testdata/v1/providers-aws/aws-sign_req.yaml new file mode 100644 index 000000000000..fce2e110039c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/providers-aws/aws-sign_req.yaml @@ -0,0 +1,346 @@ +--- +cases: + - note: providers-aws-sign_req/success-simple-no body + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "http://example.com"} + + aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + } + + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=0047a7016c81e5c7f29cd522f97e911e04fc472fced0aee7916cbc287ad6c8e3", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "http://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_result: + - x: true + - note: providers-aws-sign_req/success-simple-with headers no body + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "http://example.com", "headers": {"foo": "bar"}} + + aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + } + + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=8f1dc7c9b9978356a0d0989fd26a95307f4f8a4aa264d8220647b7097d839952", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z", + "foo": "bar", + }, + "method": "get", + "url": "http://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_result: + - x: true + - note: providers-aws-sign_req/success-simple-no body-with session token + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "http://example.com"} + + aws_config := { + "aws_access_key": "MYAWSACCESSKEYGOESHERE", + "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", + "aws_session_token": "MYAWSSECURITYTOKENGOESHERE", + "aws_service": "s3", + "aws_region": "us-east-1", + } + + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token,Signature=23c31bda8a74630c0a94f6b82a3511e7e74728df98e6f54ed0840488dbbdc8d1", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z", + "x-amz-security-token": "MYAWSSECURITYTOKENGOESHERE", + }, + "method": "get", + "url": "http://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_result: + - x: true + - note: providers-aws-sign_req/success-simple-body + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "http://example.com", "body": {"example": {1, 2, 3, 4}}} + + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1"} + + expected := { + "body": {"example": {1, 2, 3, 4}}, + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=5fb06ab1cfd74c8fcb3af95b8cce696708cf6155d971dac10f254d79799c6e88", + "host": "example.com", + "x-amz-content-sha256": "bacff6243c850423883052ac3c336fd645994442933408dfd3f9e858e69bda07", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "http://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_result: + - x: true + - note: providers-aws-sign_req/success-simple-raw_body + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "http://example.com", "raw_body": "{\"example\": {1, 2, 3, 4}}"} + + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1"} + + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=5bf26e169cb8b02330dba39d53932532438fc856c2f10537689a09ed807c7195", + "host": "example.com", + "x-amz-content-sha256": "22906461e2a98a3e780d0fd260e341bed5e544661e97c5936fc7f3af11aaad8b", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "raw_body": "{\"example\": {1, 2, 3, 4}}", + "url": "http://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_result: + - x: true + - note: providers-aws-sign_req/success-simple-body-and-raw_body + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "http://example.com", "body": {"example": {1, 2, 3, 4}}, "raw_body": "{\"example\": {1, 2, 3, 4}}"} + + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1"} + + expected := { + "body": {"example": {1, 2, 3, 4}}, + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=5bf26e169cb8b02330dba39d53932532438fc856c2f10537689a09ed807c7195", + "host": "example.com", + "x-amz-content-sha256": "22906461e2a98a3e780d0fd260e341bed5e544661e97c5936fc7f3af11aaad8b", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "raw_body": "{\"example\": {1, 2, 3, 4}}", + "url": "http://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_result: + - x: true + - note: providers-aws-sign_req/success-simple-with-headers-no-body-with-payload-signing + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar"}} + + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": false} + + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=8f1dc7c9b9978356a0d0989fd26a95307f4f8a4aa264d8220647b7097d839952", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "https://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_result: + - x: true + - note: providers-aws-sign_req/success-simple-with-headers-no-body-no-payload-signing + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar"}} + + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": true} + + expected := { + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=c6e6db654e92172f71e18c714c123711de069ac6fd7df1348e5b28fd05532028", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "UNSIGNED-PAYLOAD", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "https://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_result: + - x: true + - note: providers-aws-sign_req/success-simple-with-headers-with-body-with-payload-signing + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar"}, "body": {"example": {1, 2, 3, 4}}} + + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": false} + + expected := { + "body": {"example": {1, 2, 3, 4}}, + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=f6703a8727ec0a32f81b225a002f622e511e8a7d2ca8914894fcbb7a71d8d9d8", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "bacff6243c850423883052ac3c336fd645994442933408dfd3f9e858e69bda07", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "https://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_result: + - x: true + - note: providers-aws-sign_req/success-simple-with-headers-with-body-no-payload-signing + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar"}, "body": {"example": {1, 2, 3, 4}}} + + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": true} + + expected := { + "body": {"example": {1, 2, 3, 4}}, + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=c6e6db654e92172f71e18c714c123711de069ac6fd7df1348e5b28fd05532028", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "UNSIGNED-PAYLOAD", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "https://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_result: + - x: true + - note: providers-aws-sign_req/success-simple-with-existing-sha-header-with-body-with-payload-signing + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar", "x-amz-content-sha256": "existing-value"}, "body": {"example": {1, 2, 3, 4}}} + + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": false} + + expected := { + "body": {"example": {1, 2, 3, 4}}, + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=42bcac7fc6d170e09be72fdf38b62e59361265bc59d5f9156f0d6faf889e98ba", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "bacff6243c850423883052ac3c336fd645994442933408dfd3f9e858e69bda07", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "https://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_result: + - x: true + - note: providers-aws-sign_req/success-simple-with-existing-sha-header-with-body-no-payload-signing + query: data.test.p = x + modules: + - | + package test + + req := {"method": "get", "url": "https://example.com", "headers": {"foo": "bar", "x-amz-content-sha256": "existing-value"}, "body": {"example": {1, 2, 3, 4}}} + + aws_config := {"aws_access_key": "MYAWSACCESSKEYGOESHERE", "aws_secret_access_key": "MYAWSSECRETACCESSKEYGOESHERE", "aws_service": "s3", "aws_region": "us-east-1", "disable_payload_signing": true} + + expected := { + "body": {"example": {1, 2, 3, 4}}, + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20151228/us-east-1/s3/aws4_request,SignedHeaders=foo;host;x-amz-content-sha256;x-amz-date,Signature=edcd3ca3fd3acdfbcecad1a1d8062dbc6562d90352e294c336f9727397f9c383", + "foo": "bar", + "host": "example.com", + "x-amz-content-sha256": "UNSIGNED-PAYLOAD", + "x-amz-date": "20151228T140825Z", + }, + "method": "get", + "url": "https://example.com", + } + + p if { + providers.aws.sign_req(req, aws_config, 1451311705000000000) == expected + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/rand/test-rand.intn.yaml b/third_party/opa/v1/test/cases/testdata/v1/rand/test-rand.intn.yaml new file mode 100644 index 000000000000..e99fbb95bf09 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/rand/test-rand.intn.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: rand.intn/consistent values for same arguments + query: data.test.p = x + modules: + - | + package test + + p := count(rands) if { + rands := {rand.intn("key", 100) | numbers.range(1, 100)[_]} + } + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0322.yaml b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0322.yaml new file mode 100644 index 000000000000..df499e3169d2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0322.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: reachable/empty + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + graph.reachable({}, {"a"}, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: "{}" + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0323.yaml b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0323.yaml new file mode 100644 index 000000000000..274ad0fd597e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0323.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: reachable/cycle + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + graph.reachable({ + "a": {"b"}, + "b": {"c"}, + "c": {"a"}, + }, {"a"}, __local1__) + sort(__local1__, __local2__) + __local0__ = __local2__ + } + data: {} + input_term: "{}" + want_result: + - x: + - a + - b + - c diff --git a/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0324.yaml b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0324.yaml new file mode 100644 index 000000000000..6ca4c53fee3d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0324.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: reachable/components + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + graph.reachable({ + "a": {"b", "c"}, + "b": {"d"}, + "c": {"d"}, + "d": set(), + "e": {"f"}, + "f": {"e"}, + "x": {"x"}, + }, {"b", "e"}, __local1__) + sort(__local1__, __local2__) + __local0__ = __local2__ + } + data: {} + input_term: "{}" + want_result: + - x: + - b + - d + - e + - f diff --git a/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0325.yaml b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0325.yaml new file mode 100644 index 000000000000..96a7a7311594 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0325.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: reachable/arrays + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + graph.reachable({ + "a": ["b"], + "b": ["c"], + "c": ["a"], + }, ["a"], __local1__) + sort(__local1__, __local2__) + __local0__ = __local2__ + } + data: {} + input_term: "{}" + want_result: + - x: + - a + - b + - c diff --git a/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0326.yaml b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0326.yaml new file mode 100644 index 000000000000..80b657d508fa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0326.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: reachable/malformed 1 + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.graph + __local3__ = input.initial + graph.reachable(__local2__, __local3__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"graph": 1, "initial": [1]}' + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0327.yaml b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0327.yaml new file mode 100644 index 000000000000..9328f09d3b97 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0327.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: reachable/malformed 2 + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.graph + __local3__ = input.initial + graph.reachable(__local2__, __local3__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"graph": {"a": null}, "initial": ["a"]}' + want_result: + - x: + - a diff --git a/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0328.yaml b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0328.yaml new file mode 100644 index 000000000000..490c80f3fb90 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-0328.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: reachable/malformed 3 + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local2__ = input.graph + __local3__ = input.initial + graph.reachable(__local2__, __local3__, __local1__) + __local0__ = __local1__ + } + data: {} + input_term: '{"graph": {"a": []}, "initial": "a"}' + want_error_code: eval_type_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-paths-0422.yaml b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-paths-0422.yaml new file mode 100644 index 000000000000..193703bb96ad --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-paths-0422.yaml @@ -0,0 +1,137 @@ +--- +cases: + - note: reachable_paths/empty + query: data.reachable.p = x + modules: + - | + package reachable + + p := result if { + graph.reachable_paths({}, {"a"}, result) + } + data: {} + want_result: + - x: [] + - note: reachable_paths/cycle + query: data.reachable.p = x + modules: + - | + package reachable + + p := result if { + graph.reachable_paths(input.graph, input.initial, result) + } + data: {} + input_term: '{ "graph": { "a": {"b"}, "b": {"c"}, "c": {"a"}, }, "initial": {"a"} }' + want_result: + - x: + - - a + - b + - c + - note: reachable_paths/components + query: data.reachable.p = x + modules: + - | + package reachable + + p := result if { + graph.reachable_paths(input.graph, input.initial, result) + } + data: {} + input_term: '{ "graph": { "a": {"b", "c"}, "b": {"d"}, "c": {"d"}, "d": set(), "e": {"f"}, "f": {"e"}, "x": {"x"}, }, "initial": { "b", "e" } }' + want_result: + - x: + - - b + - d + - - e + - f + - note: reachable_paths/arrays + query: data.reachable.p = x + modules: + - | + package reachable + + p := result if { + graph.reachable_paths(input.graph, input.initial, result) + } + data: {} + input_term: '{ "graph": { "a": ["b"], "b": ["c"], "c": ["a"], }, "initial": ["a"] }' + want_result: + - x: + - - a + - b + - c + - note: reachable_paths/malformed 1 + query: data.reachable.p = x + modules: + - | + package reachable + + p := result if { + graph.reachable_paths(input.graph, input.initial, result) + } + data: {} + input_term: '{ "graph": 1, "initial": [1] }' + want_error_code: eval_type_error + strict_error: true + - note: reachable_paths/malformed 2 + query: data.reachable.p = x + modules: + - | + package reachable + + p := result if { + graph.reachable_paths(input.graph, input.initial, result) + } + data: {} + input_term: '{ "graph": { "a": null }, "initial": ["a"] }' + want_result: + - x: + - - a + - note: reachable_paths/malformed 3 + query: data.reachable.p = x + modules: + - | + package reachable + + p := result if { + graph.reachable_paths(input.graph, input.initial, result) + } + data: {} + input_term: '{ "graph": { "a": [] }, "initial": "a" }' + want_error_code: eval_type_error + strict_error: true + - note: reachable_paths/multiple_paths + query: data.reachable.p = x + modules: + - | + package reachable + + p := result if { + graph.reachable_paths(input.graph, input.initial, result) + } + data: {} + input_term: '{ "graph": { "a": ["b", "c"], "b": ["c"], "c": [], }, "initial": {"a"} }' + want_result: + - x: + - - a + - b + - c + - - a + - c + - note: reachable_paths/invalid_end + query: data.reachable.p = x + modules: + - | + package reachable + + p := result if { + graph.reachable_paths(input.graph, input.initial, result) + } + data: {} + input_term: '{ "graph": { "a": ["b"], "b": ["nonexistent"], }, "initial": {"a", "b"} }' + want_result: + - x: + - - a + - b + - - b diff --git a/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-paths-1022.yaml b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-paths-1022.yaml new file mode 100644 index 000000000000..9eb8f359bf7c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/reachable/test-reachable-paths-1022.yaml @@ -0,0 +1,81 @@ +--- +cases: + - note: reachable_paths/cycle_1022_1 + query: data.reachable.p = x + modules: + - | + package reachable + + p := result if { + graph.reachable_paths(input.graph, input.initial, result) + } + data: {} + input_term: '{ "graph": { "one": ["two","five"], "two": ["four"], "three": [""], "four": ["three"], "five": ["seven","six"], "six": ["nine"], "seven": ["eight"], "eight": [""], "nine": [""], }, "initial": {"one"} }' + want_result: + - x: + - - one + - five + - seven + - eight + - - one + - five + - six + - nine + - - one + - two + - four + - three + - note: reachable_paths/cycle_1022_2 + query: data.reachable.p = x + modules: + - | + package reachable + + p := result if { + graph.reachable_paths(input.graph, input.initial, result) + } + data: {} + input_term: '{ "graph": { "one": {"two","five"}, "two": {"four"}, "three": {""}, "four": {"three"}, "five": {"seven","six"}, "six": {"nine"}, "seven": {"eight"}, "eight": {""}, "nine": {""}, }, "initial": {"one"} }' + want_result: + - x: + - - one + - five + - seven + - eight + - - one + - five + - six + - nine + - - one + - two + - four + - three + - note: reachable_paths/cycle_1022_3 + query: data.reachable.p = x + modules: + - | + package reachable + + p := result if { + graph.reachable_paths(input.graph, input.initial, result) + } + data: {} + input_term: '{ "graph": { "one": ["two","five"], "two": ["four"], "three": [""], "four": ["three"], "five": ["seven","six"], "six": ["nine","seven"], "seven": ["eight"], "eight": ["three"], "nine": [""], }, "initial": {"one"} }' + want_result: + - x: + - - one + - five + - seven + - eight + - three + - - one + - five + - six + - - one + - five + - six + - nine + - - one + - two + - four + - three diff --git a/third_party/opa/v1/test/cases/testdata/v1/refheads/test-generic-refs.yaml b/third_party/opa/v1/test/cases/testdata/v1/refheads/test-generic-refs.yaml new file mode 100644 index 000000000000..d3780d22f549 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/refheads/test-generic-refs.yaml @@ -0,0 +1,273 @@ +--- +cases: + - note: refheads/general, single var + query: data.test.p = x + modules: + - | + package test + + p[q].r := i if q := ["a", "b", "c"][i] + want_result: + - x: + a: + r: 0 + b: + r: 1 + c: + r: 2 + - note: refheads/general, multiple vars + query: data.test.p = x + modules: + - | + package test + + p[q][r] if q := ["a", "b", "c"][r] + want_result: + - x: + a: + "0": true + b: + "1": true + c: + "2": true + - note: refheads/general, deep query + query: data.test.p.b = x + modules: + - | + package test + + p[q][r] if q := ["a", "b", "c"][r] + want_result: + - x: + "1": true + - note: refheads/general, overlapping rule, no conflict + query: data.test.p = x + modules: + - | + package test + + p[q].r := i if q := ["a", "b", "c"][i] + + p.a.r := 0 + want_result: + - x: + a: + r: 0 + b: + r: 1 + c: + r: 2 + - note: refheads/general, overlapping rule, different dynamic depths, no conflict + query: data.test.p = x + modules: + - | + package test + + p[q].r.s := i if q := ["a", "b", "c"][i] + + p.d.r := 3 + + p.e := 4 + want_result: + - x: + a: + r: + s: 0 + b: + r: + s: 1 + c: + r: + s: 2 + d: + r: 3 + e: 4 + - note: refheads/general, self-conflict + query: data.test.p = x + modules: + - | + package test + + p[q].r.s := i if q := ["a", "b", "c", "b"][i] + want_error_code: eval_conflict_error + - note: refheads/general, overlapping rule, conflict + query: data.test.p = x + modules: + - | + package test + + p[q].r.s := i if q := ["a", "b", "c"][i] + + p.a.r := 42 + want_error_code: eval_conflict_error + - note: refheads/general, overlapping rule, deep override inside other rule object value, conflict + query: data.test.p = x + modules: + - | + package test + + p[q].r := v if { + q := "q" + v := {"s": {"t": 1}} + } + + p.q.r.s.t := 42 + want_error_code: eval_conflict_error + - note: refheads/general, overlapping rule, deep injection into other rule object value, conflict + query: data.test.p = x + modules: + - | + package test + + p[q].r := v if { + q := "q" + v := {"s": {"t": 1}} + } + + p.q.r.s.u := 42 + want_error_code: eval_conflict_error + - note: refheads/general, set leaf (shallow ref) + query: data.test.p = x + modules: + - | + package test + + p[q] contains r if { + x := ["a", "b", "c"] + q := x[_] + r := x[_] + q != r + } + + p.b contains "foo" + want_result: + - x: + a: + - b + - c + b: + - a + - c + - foo + c: + - a + - b + - note: refheads/general, set leaf (other rule defines dynamic ref portion) + query: data.test.p = x + modules: + - | + package test + + p.q contains r if { + r := ["a", "b", "c"][_] + } + + p[q] := r if { + q := "foo" + r := "bar" + } + want_result: + - x: + foo: bar + q: + - a + - b + - c + - note: refheads/general, set leaf + query: data.test.p = x + modules: + - | + package test + + p[q].r contains s if { + x := ["a", "b", "c"] + q := x[_] + s := x[_] + q != s + } + + p.b.r contains "foo" + want_result: + - x: + a: + r: + - b + - c + b: + r: + - a + - c + - foo + c: + r: + - a + - b + - note: refheads/general, set leaf, deep query + query: data.test.p.b.r.c = x + modules: + - | + package test + + p[q].r contains s if { + x := ["a", "b", "c"] + q := x[_] + s := x[_] + q != s + } + + p.b.r contains "foo" + want_result: + - x: c + - note: refheads/general, input var + query: data.test.p = x + modules: + - | + package test + + p[input.x].r := "foo" + input: + x: bar + want_result: + - x: + bar: + r: foo + - note: refheads/general, external non-ground var + query: data.test.p = x + modules: + - | + package test + + a := [x | x := input.x[_]] + + b := input.y + + p[a[b]].r[s] := i if { + s := a[i] + } + input: + x: + - foo + - bar + - baz + "y": 1 + want_result: + - x: + bar: + r: + bar: 1 + baz: 2 + foo: 0 + - note: refheads/general, multiple result-set entries + query: data.test.p.q[i].s = x + modules: + - | + package test + + p.q[r].s := 1 if r := "foo" + + p.q[r].s := 2 if r := "bar" + want_result: + - i: foo + x: 1 + - i: bar + x: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/refheads/test-refs-as-rule-heads.yaml b/third_party/opa/v1/test/cases/testdata/v1/refheads/test-refs-as-rule-heads.yaml new file mode 100644 index 000000000000..fe7d7950a984 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/refheads/test-refs-as-rule-heads.yaml @@ -0,0 +1,355 @@ +--- +cases: + - note: refheads/single-value + query: data.test.p = x + modules: + - | + package test + + p.q.r := 1 + + p.q.s := 2 + want_result: + - x: + q: + r: 1 + s: 2 + - note: refheads/single-value, with var + query: data.test.p = x + modules: + - | + package test + + p.q.r := 1 + + p.q[s] := 2 if s := "s" + want_result: + - x: + q: + r: 1 + s: 2 + - note: refheads/single-value, with var, conflict + query: data.test.p.q = x + modules: + - | + package test + + p.q.r := 1 + + p.q[s] := 2 if s := "r" + want_error_code: eval_conflict_error + want_error: object keys must be unique + - note: "refheads/complete: direct query" + query: data.test.a.b.c.p = x + modules: + - | + package test + + a.b.c.p := true + want_result: + - x: true + - note: "refheads/complete: direct query q" + query: data.test.q = x + modules: + - | + package test + + q := 0 + want_result: + - x: 0 + - note: "refheads/complete: full package extent" + query: data.test = x + modules: + - | + package test + + a.b.c.p := true + want_result: + - x: + a: + b: + c: + p: true + - note: refheads/complete+mixed + query: data.test.p = x + modules: + - | + package test + + a.b.c.p := 1 + + q := 0 + + a.b.d := 3 + + p if { + q == 0 + a.b.c.p == 1 + a.b.d == 3 + } + want_result: + - x: true + - note: refheads/single-value rule + query: data.test.a = x + modules: + - | + package test + + a.b[x] := y if { + x := "c" + y := "d" + } + want_result: + - x: + b: + c: d + - note: refheads/multi-value + query: data.test.a = x + modules: + - | + package test + + a.b contains x if some x in [1, 2, 3] + want_result: + - x: + b: + - 1 + - 2 + - 3 + - note: "refheads/single-value: previously partial object" + query: data.test.a.b = x + modules: + - | + package test + + a.b[x] := i if some i, x in [1, 2, 3] + want_result: + - x: + "1": 0 + "2": 1 + "3": 2 + - note: "refheads/multi-value: same rule" + query: data.test.a = x + modules: + - | + package test + + a.b.c.d contains 1 + - | + package test.a + + b.c.d contains 2 + want_result: + - x: + b: + c: + d: + - 1 + - 2 + - note: refheads/single-value default rule + query: data.test.a = x + modules: + - | + package test + + default a.b.c := "d" + want_result: + - x: + b: + c: d + - note: refheads/single-value example + query: data.test.a = x + modules: + - | + package test + + q[7] := 8 + + a[x] if q[x] + want_result: + - x: + "7": true + - note: refheads/single-value example, false + query: data.test.a = x + modules: + - | + package test + + q[7] := 8 if false + + a[x] if q[x] + want_result: + - x: {} + - note: refheads/mixed example, multiple rules + query: data.test.a.b = x + modules: + - | + package test + + a.b.c := "d" + + a.b.e := "f" + + a.b.g contains x if some x in numbers.range(1, 3) + + a.b.h[x] := 1 if x := "one" + want_result: + - x: + c: d + e: f + g: + - 1 + - 2 + - 3 + h: + one: 1 + - note: refheads/website-example/partial-obj + query: data.example.apps_by_hostname.helium = x + modules: + - | + package example + + apps_by_hostname[hostname] := app if { + some i + server := sites[_].servers[_] + hostname := server.hostname + apps[i].servers[_] == server.name + app := apps[i].name + } + + sites := [ + { + "region": "east", + "name": "prod", + "servers": [ + { + "name": "web-0", + "hostname": "hydrogen", + }, + { + "name": "web-1", + "hostname": "helium", + }, + { + "name": "db-0", + "hostname": "lithium", + }, + ], + }, + { + "region": "west", + "name": "smoke", + "servers": [ + { + "name": "web-1000", + "hostname": "beryllium", + }, + { + "name": "web-1001", + "hostname": "boron", + }, + { + "name": "db-1000", + "hostname": "carbon", + }, + ], + }, + { + "region": "west", + "name": "dev", + "servers": [ + { + "name": "web-dev", + "hostname": "nitrogen", + }, + { + "name": "db-dev", + "hostname": "oxygen", + }, + ], + }, + ] + + apps := [ + { + "name": "web", + "servers": ["web-0", "web-1", "web-1000", "web-1001", "web-dev"], + }, + { + "name": "mysql", + "servers": ["db-0", "db-1000"], + }, + { + "name": "mongodb", + "servers": ["db-dev"], + }, + ] + + containers := [ + { + "image": "redis", + "ipaddress": "10.0.0.1", + "name": "big_stallman", + }, + { + "image": "nginx", + "ipaddress": "10.0.0.2", + "name": "cranky_euclid", + }, + ] + want_result: + - x: web + - note: refheads/website-example/partial-set + query: data.example.public_network = x + modules: + - | + package example + + public_network contains net.id if { + some net in input.networks + net.public + } + input: + networks: + - id: n1 + public: true + - id: n2 + public: false + want_result: + - x: + - n1 + - note: refheads/many-vars + query: data.test.p = x + modules: + - | + package test + + p[a][b][c][d][e] if { + some a in numbers.range(1, 5) + some b in numbers.range(1, 5) + some c in numbers.range(1, 5) + some d in numbers.range(1, 5) + some e in numbers.range(1, 5) + (((a + b) + c) + d) + e == 24 + } + want_result: + - x: + "4": + "5": + "5": + "5": + "5": true + "5": + "4": + "5": + "5": + "5": true + "5": + "4": + "5": + "5": true + "5": + "4": + "5": true + "5": + "4": true diff --git a/third_party/opa/v1/test/cases/testdata/v1/refheads/test-regressions.yaml b/third_party/opa/v1/test/cases/testdata/v1/refheads/test-regressions.yaml new file mode 100644 index 000000000000..a0c1731fd137 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/refheads/test-regressions.yaml @@ -0,0 +1,180 @@ +--- +cases: + - note: regression/ref-not-hashable + query: data.test = x + modules: + - | + package test + + ms[m.z] := m if { + m := input.xs[y] + } + input: + xs: + something: + z: a + want_result: + - x: + ms: + a: + z: a + - note: regression/function refs and package extent + query: data.test = x + modules: + - | + package test + + foo["bar"](x) := x + 1 + + x := foo.bar(2) + want_result: + - x: + foo: {} + x: 3 + - note: regression/rule refs and package extent + query: data.test = x + modules: + - | + package test + + buz["quz"] := 3 if input == 3 + + x := y if { + y := buz.quz with input as 3 + } + want_result: + - x: + buz: {} + x: 3 + - note: regression/rule refs and package extents, multiple modules + query: data.test = x + modules: + - | + package test + + x := y if { + y := data.test.buz.quz with input as 3 + } + - | + package test.buz + + quz := 3 if input == 3 + want_result: + - x: + buz: {} + x: 3 + - note: regression/type checking with ref rules + query: data.test.p = x + modules: + - | + package test + + all[0] := [2] + + level := 1 + + p := y if y := all[level - 1][_] + want_result: + - x: 2 + - note: regression/type checking with ref rules, number + query: data.test.p[0] = x + modules: + - | + package test + + p[0] := 1 + want_result: + - x: 1 + - note: regression/type checking with ref rules, bool + query: data.test.p[true] = x + modules: + - | + package test + + p[true] := 1 + want_result: + - x: 1 + - note: regression/full extent with partial object rule with empty indexer lookup result + query: data.test = x + modules: + - | + package test + + p[x] := 2 if { + x := input # we'll get 0 rules for data.test.p + false + } + want_result: + - x: + p: {} + - note: regression/obj in ref head query + query: data.test.p[{"a":"b"}] = x + modules: + - | + package test + + p[{"a": "b"}] := true + want_result: + - x: true + - note: regression/full extent with non-string (number) last term + query: data.test = x + modules: + - | + package test + + p[0] := true + want_result: + - x: + p: + "0": true + - note: regression/full extent with non-string last term, comparison + query: data.test.q = x + modules: + - | + package test + + p[0] := 1 + + q if p[0] == 1 + want_result: + - x: true + - note: regression/full extent with non-string (boolean) last term + query: data.test = x + modules: + - | + package test + + p[true] := true + want_result: + - x: + p: + "true": true + - note: regression/full extent with non-string last term, comparison + query: data.test.q = x + modules: + - | + package test + + p[true] := false + + q if p[true] == false + want_result: + - x: true + - note: regression/virtual-cache key scope + query: data.test.main = x + modules: + - | + package test + + obj.sub[x][x] contains x if some x in ["one", "two"] + + obj[x][x] contains x if x := "whatever" + + main contains x if { + [1 | obj.sub[_].one[_]] + x := obj.sub[_][_][_] + } + want_result: + - x: + - "one" + - "two" diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexfind/test-regexfind-0334.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexfind/test-regexfind-0334.yaml new file mode 100644 index 000000000000..1497cd27e3c8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexfind/test-regexfind-0334.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: regexfind/finds all match values + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + regex.find_n("a.", "paranormal", -1, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - - ar + - an + - al + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexfind/test-regexfind-0335.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexfind/test-regexfind-0335.yaml new file mode 100644 index 000000000000..9ee8298083b4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexfind/test-regexfind-0335.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: regexfind/finds specified number of match values + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + regex.find_n("a.", "paranormal", 2, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - - ar + - an + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexfind/test-regexfind-0336.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexfind/test-regexfind-0336.yaml new file mode 100644 index 000000000000..d365e70c6c2b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexfind/test-regexfind-0336.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: regexfind/finds no matching values + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + regex.find_n("bork", "paranormal", -1, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - [] + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0337.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0337.yaml new file mode 100644 index 000000000000..cf6d9213a605 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0337.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: regexfindallstringsubmatch/finds no matches + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + regex.find_all_string_submatch_n("a(x*)b", "-", -1, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - [] + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0338.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0338.yaml new file mode 100644 index 000000000000..3788ad9a87c9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0338.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: regexfindallstringsubmatch/single match without captures + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + regex.find_all_string_submatch_n("a(x*)b", "-ab-", -1, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - - - ab + - "" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0339.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0339.yaml new file mode 100644 index 000000000000..d9b40e97cffe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0339.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: regexfindallstringsubmatch/single match with a capture + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + regex.find_all_string_submatch_n("a(x*)b", "-axxb-", -1, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - - - axxb + - xx + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0340.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0340.yaml new file mode 100644 index 000000000000..23828ae47a3a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0340.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: regexfindallstringsubmatch/multiple matches with captures-1 + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + regex.find_all_string_submatch_n("a(x*)b", "-ab-axb-", -1, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - - - ab + - "" + - - axb + - x + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0341.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0341.yaml new file mode 100644 index 000000000000..516f432b423f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0341.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: regexfindallstringsubmatch/multiple matches with captures-2 + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + regex.find_all_string_submatch_n("a(x*)b", "-axxb-ab-", -1, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - - - axxb + - xx + - - ab + - "" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0342.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0342.yaml new file mode 100644 index 000000000000..183a94ed85d4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0342.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: regexfindallstringsubmatch/multiple patterns, matches, and captures + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + regex.find_all_string_submatch_n("[^aouiye]([aouiye])([^aouiye])?", "somestri", -1, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - - - som + - o + - m + - - ri + - i + - "" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0343.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0343.yaml new file mode 100644 index 000000000000..5f69d0657454 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-0343.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: regexfindallstringsubmatch/multiple patterns, matches, and captures with specified number of matches + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + regex.find_all_string_submatch_n("[^aouiye]([aouiye])([^aouiye])?", "somestri", 1, __local0__) + x = __local0__ + } + want_result: + - x: + - - - som + - o + - m diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-large-input.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-large-input.yaml new file mode 100644 index 000000000000..de3220b56e77 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexfindallstringsubmatch/test-regexfindallstringsubmatch-large-input.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: regexfindallstringsubmatch/large input + query: data.test.p = x + modules: + - | + package test + + p contains m if some m in regex.find_all_string_submatch_n("^.*$", input.long, -1) + input: + long: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabc + want_result: + - x: + - - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabc diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexisvalid/test-regexisvalid-0329.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexisvalid/test-regexisvalid-0329.yaml new file mode 100644 index 000000000000..7687d8fae776 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexisvalid/test-regexisvalid-0329.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: regexisvalid/bad operand type + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local0__ = data.num + regex.is_valid(__local0__, x) + } + data: + num: 10 + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexisvalid/test-regexisvalid-0330.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexisvalid/test-regexisvalid-0330.yaml new file mode 100644 index 000000000000..1e347e74b6e0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexisvalid/test-regexisvalid-0330.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: regexisvalid/bad pattern + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + regex.is_valid(`++`, x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexisvalid/test-regexisvalid-0331.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexisvalid/test-regexisvalid-0331.yaml new file mode 100644 index 000000000000..dfe3eed8c16b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexisvalid/test-regexisvalid-0331.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: regexisvalid/good pattern + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + regex.is_valid(`.+`, x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexmatch/test-regexmatch-0861.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexmatch/test-regexmatch-0861.yaml new file mode 100644 index 000000000000..532cc9891cd7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexmatch/test-regexmatch-0861.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: regexmatch/regex.match + query: data.generated.p = x + modules: + - | + package generated + + p if { + regex.match("^[a-z]+\\[[0-9]+\\]$", "foo[1]") + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexmatchtemplate/test-regexmatchtemplate-0332.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexmatchtemplate/test-regexmatchtemplate-0332.yaml new file mode 100644 index 000000000000..f8864a3dc7c3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexmatchtemplate/test-regexmatchtemplate-0332.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: regexmatchtemplate/matches wildcard with {} + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + regex.template_match("urn:foo:{.*}", "urn:foo:bar:baz", "{", "}", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexmatchtemplate/test-regexmatchtemplate-0333.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexmatchtemplate/test-regexmatchtemplate-0333.yaml new file mode 100644 index 000000000000..fde6994f0e43 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexmatchtemplate/test-regexmatchtemplate-0333.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: regexmatchtemplate/matches wildcard with <> + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + regex.template_match("urn:foo:<.*>", "urn:foo:bar:baz", "<", ">", x) + } + data: {} + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexreplace/test-regexreplace-0001.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexreplace/test-regexreplace-0001.yaml new file mode 100644 index 000000000000..f7744b6a2884 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexreplace/test-regexreplace-0001.yaml @@ -0,0 +1,40 @@ +--- +cases: + - note: "regex.replace: test pattern match and replace" + query: data.test.p = x + modules: + - | + package test + + p := x if { + s := "-wy-wxxy-" + x := regex.replace(s, "w(x*)y", "0") + } + data: {} + want_result: + - x: -0-0- + - note: "regex.replace: work with groups" + query: data.test.p = x + modules: + - | + package test + + p := x if { + s := "foo" + x := regex.replace(s, "(foo)", "$1$1") + } + data: {} + want_result: + - x: foofoo + - note: "regex.replace: bad regex pattern: Syntax error" + query: data.test.p = x + modules: + - | + package test + + p := x if { + s := "foo" + x := regex.replace(s, "[", "$1") + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexsplit/test-regexsplit-0862.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexsplit/test-regexsplit-0862.yaml new file mode 100644 index 000000000000..7babc99c9414 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexsplit/test-regexsplit-0862.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "regexsplit/regex.split: empty string" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + regex.split("^[a-z]+\\[[0-9]+\\]$", "", [x]) + } + data: {} + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexsplit/test-regexsplit-0863.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexsplit/test-regexsplit-0863.yaml new file mode 100644 index 000000000000..5e10dbc7d4a5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexsplit/test-regexsplit-0863.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "regexsplit/regex.split: non-repeat pattern" + query: data.generated.p = x + modules: + - | + package generated + + p := [v, w, x, y] if { + regex.split("a", "banana", [v, w, x, y]) + } + data: {} + want_result: + - x: + - b + - "n" + - "n" + - "" diff --git a/third_party/opa/v1/test/cases/testdata/v1/regexsplit/test-regexsplit-0864.yaml b/third_party/opa/v1/test/cases/testdata/v1/regexsplit/test-regexsplit-0864.yaml new file mode 100644 index 000000000000..58adc9c58e25 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regexsplit/test-regexsplit-0864.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "regexsplit/regex.split: repeat pattern" + query: data.generated.p = x + modules: + - | + package generated + + p := [v, w] if { + regex.split("z+", "pizza", [v, w]) + } + data: {} + want_result: + - x: + - pi + - a diff --git a/third_party/opa/v1/test/cases/testdata/v1/regometadatachain/test-regometadatachain-1.yaml b/third_party/opa/v1/test/cases/testdata/v1/regometadatachain/test-regometadatachain-1.yaml new file mode 100644 index 000000000000..69424c4caa14 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regometadatachain/test-regometadatachain-1.yaml @@ -0,0 +1,96 @@ +--- +cases: + - note: regometadatachain/simple + query: data.testing.p = x + modules: + - | + # METADATA + # description: The Rego test suite + package testing + + # METADATA + # title: Testing annotations + # authors: + # - The OPA contributors + p := x if { + x := rego.metadata.chain() + } + want_result: + - x: + - annotations: + authors: + - name: The OPA contributors + scope: rule + title: Testing annotations + path: + - testing + - p + - annotations: + description: The Rego test suite + scope: package + path: + - testing + - note: regometadatachain/rule mixed scope + query: data.testing.p = x + modules: + - | + # METADATA + # description: The Rego test suite + package testing + + # METADATA + # scope: document + # title: Testing annotations + # authors: + # - The OPA contributors + p := "foo" if { + false + } + + # METADATA + # title: Another annotation + p := x if { + x := rego.metadata.chain() + } + want_result: + - x: + - annotations: + scope: rule + title: Another annotation + path: + - testing + - p + - annotations: + authors: + - name: The OPA contributors + scope: document + title: Testing annotations + path: + - testing + - p + - annotations: + description: The Rego test suite + scope: package + path: + - testing + - note: regometadatachain/package spanning modules + query: data.testing.p = x + modules: + - | + # METADATA + # description: A set of package annotations seen across multiple modules + package testing + - | + package testing + + p := rego.metadata.chain() + want_result: + - x: + - path: + - testing + - p + - annotations: + description: A set of package annotations seen across multiple modules + scope: package + path: + - testing diff --git a/third_party/opa/v1/test/cases/testdata/v1/regometadatarule/test-regometadatarule-1.yaml b/third_party/opa/v1/test/cases/testdata/v1/regometadatarule/test-regometadatarule-1.yaml new file mode 100644 index 000000000000..4c75d4daab1c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regometadatarule/test-regometadatarule-1.yaml @@ -0,0 +1,48 @@ +--- +cases: + - note: regometadatarule/simple + query: data.testing.p = x + modules: + - | + # METADATA + # description: The Rego test suite + package testing + + # METADATA + # title: Testing annotations + # authors: + # - The OPA contributors + p := x if { + x := rego.metadata.rule() + } + want_result: + - x: + authors: + - name: The OPA contributors + scope: rule + title: Testing annotations + - note: regometadatarule/rule scope only + query: data.testing.p = x + modules: + - | + # METADATA + # description: The Rego test suite + package testing + + # METADATA + # title: Testing annotations + # authors: + # - The OPA contributors + p := "foo" if { + false + } + + # METADATA + # title: Another annotation + p := x if { + x := rego.metadata.rule() + } + want_result: + - x: + scope: rule + title: Another annotation diff --git a/third_party/opa/v1/test/cases/testdata/v1/regoparsemodule/test-regoparsemodule-0320.yaml b/third_party/opa/v1/test/cases/testdata/v1/regoparsemodule/test-regoparsemodule-0320.yaml new file mode 100644 index 000000000000..90724f113b4f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regoparsemodule/test-regoparsemodule-0320.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: regoparsemodule/ok + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local0__ = data.ok + rego.parse_module("x.rego", __local0__, module) + x = module["package"].path[1].value + } + data: + ok: |- + package foo.bar + + import rego.v1 + import data.a + + p if { a = true } + want_result: + - x: foo diff --git a/third_party/opa/v1/test/cases/testdata/v1/regoparsemodule/test-regoparsemodule-0321.yaml b/third_party/opa/v1/test/cases/testdata/v1/regoparsemodule/test-regoparsemodule-0321.yaml new file mode 100644 index 000000000000..15fcf005be53 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/regoparsemodule/test-regoparsemodule-0321.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: regoparsemodule/error + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local0__ = data.err + rego.parse_module("x.rego", __local0__, x) + } + data: + err: package foo. + want_error_code: eval_builtin_error + want_error: "rego_parse_error: unexpected eof token: expected ident" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/rendertemplate/rendertemplate.yaml b/third_party/opa/v1/test/cases/testdata/v1/rendertemplate/rendertemplate.yaml new file mode 100644 index 000000000000..54b0f2bf4a21 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/rendertemplate/rendertemplate.yaml @@ -0,0 +1,54 @@ +--- +cases: + - note: rendertemplate/simple + query: data.test.p = x + modules: + - | + package test + + template_string := `{{.test}}` + + template_vars := {`test`: `hello world`} + + p := strings.render_template(template_string, template_vars) + want_result: + - x: hello world + - note: rendertemplate/simpleint + query: data.test.p = x + modules: + - | + package test + + template_string := `{{.test}}` + + template_vars := {`test`: 2023} + + p := strings.render_template(template_string, template_vars) + want_result: + - x: "2023" + - note: rendertemplate/complex + query: data.test.p = x + modules: + - | + package test + + template_string := `{{range $i, $name := .hellonames}}{{if $i}},{{end}}hello {{$name}}{{end}}` + + template_vars := {`hellonames`: [`rohan`, `john doe`]} + + p := strings.render_template(template_string, template_vars) + want_result: + - x: hello rohan,hello john doe + - note: rendertemplate/missingkey + query: data.test.p = x + modules: + - | + package test + + template_string := `{{.testvarnotprovided}}` + + template_vars := {`test`: `hello world`} + + p := strings.render_template(template_string, template_vars) + want_error_code: eval_builtin_error + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/replacen/test-replacen-0374.yaml b/third_party/opa/v1/test/cases/testdata/v1/replacen/test-replacen-0374.yaml new file mode 100644 index 000000000000..f035fc1d3231 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/replacen/test-replacen-0374.yaml @@ -0,0 +1,41 @@ +--- +cases: + - note: replacen/replace multiple patterns + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + strings.replace_n({"<": "<", ">": ">"}, "This is HTML!", __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - This is <b>HTML</b>! + sort_bindings: true + - note: replacen/replace multiple patterns/overlapping + query: data.test.p = x + modules: + - | + package test + + p := strings.replace_n({"f": "x", "foo": "xxx"}, "foobar") + data: {} + want_result: + - x: xoobar + - note: replacen/replace multiple patterns/overlapping/insertion order does not matter + query: data.test.p = x + modules: + - | + package test + + p := x if { + x := strings.replace_n({k: v | k := ["f", "foo"][i]; v := ["x", "xxx"][i]}, "foo") + y := strings.replace_n({k: v | k := ["foo", "f"][i]; v := ["xxx", "x"][i]}, "foo") + x == y + } + data: {} + want_result: + - x: xoo diff --git a/third_party/opa/v1/test/cases/testdata/v1/replacen/test-replacen-0375.yaml b/third_party/opa/v1/test/cases/testdata/v1/replacen/test-replacen-0375.yaml new file mode 100644 index 000000000000..a22db22fc201 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/replacen/test-replacen-0375.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: replacen/find no patterns + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + strings.replace_n({"old1": "new1", "old2": "new2"}, "Everything is new1, new2", __local0__) + x = __local0__ + } + data: {} + want_result: + - x: + - Everything is new1, new2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/replacen/test-replacen-bad-operands.yaml b/third_party/opa/v1/test/cases/testdata/v1/replacen/test-replacen-bad-operands.yaml new file mode 100644 index 000000000000..264922675970 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/replacen/test-replacen-bad-operands.yaml @@ -0,0 +1,37 @@ +--- +cases: + - note: replacen/bad pattern object operand/non-string key + query: data.test.p = x + modules: + - | + package test + + p := strings.replace_n({2: "x" | true}, "foo") + want_error_code: eval_type_error + want_error: "strings.replace_n: operand 1 non-string key found in pattern object" + strict_error: true + - note: replacen/bad pattern object operand/non-string value + query: data.test.p = x + modules: + - | + package test + + p := strings.replace_n(data.pattern, "foo") + data: + pattern: + f: 100 + want_error_code: eval_type_error + want_error: "strings.replace_n: operand 1 non-string value found in pattern object" + strict_error: true + - note: replacen/bad pattern object operand/non-string value + query: data.test.p = x + modules: + - | + package test + + p := strings.replace_n({"foo": "baz"}, data.string) + data: + string: 100 + want_error_code: eval_type_error + want_error: "strings.replace_n: operand 2 must be string but got number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0344.yaml b/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0344.yaml new file mode 100644 index 000000000000..5e2ef4839b67 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0344.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: semvercompare/a < b + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + semver.compare("1.0.0", "2.0.0", __local0__) + x = __local0__ + } + data: {} + want_result: + - x: -1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0345.yaml b/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0345.yaml new file mode 100644 index 000000000000..8eb6f9b44125 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0345.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: semvercompare/a > b + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + semver.compare("2.0.0", "1.0.0", __local0__) + x = __local0__ + } + data: {} + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0346.yaml b/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0346.yaml new file mode 100644 index 000000000000..6ab3ad2adee7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0346.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: semvercompare/a == b + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + semver.compare("1.0.0", "1.0.0", __local0__) + x = __local0__ + } + data: {} + want_result: + - x: 0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0347.yaml b/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0347.yaml new file mode 100644 index 000000000000..742beda19946 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0347.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: semvercompare/invalid version a + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + semver.compare("1", "1.0.0", __local0__) + x = __local0__ + } + want_error_code: eval_builtin_error + want_error: 'semver.compare: operand 1: string "1" is not a valid SemVer' + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0348.yaml b/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0348.yaml new file mode 100644 index 000000000000..0f0c011251b2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/semvercompare/test-semvercompare-0348.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: semvercompare/invalid version b + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + semver.compare("1.0.0", "1", __local0__) + x = __local0__ + } + data: {} + want_error_code: eval_builtin_error + want_error: 'semver.compare: operand 2: string "1" is not a valid SemVer' + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/semverisvalid/test-semverisvalid-0349.yaml b/third_party/opa/v1/test/cases/testdata/v1/semverisvalid/test-semverisvalid-0349.yaml new file mode 100644 index 000000000000..67322b9a0fc5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/semverisvalid/test-semverisvalid-0349.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: semverisvalid/valid + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + semver.is_valid("1.0.0", __local0__) + x = __local0__ + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/semverisvalid/test-semverisvalid-0350.yaml b/third_party/opa/v1/test/cases/testdata/v1/semverisvalid/test-semverisvalid-0350.yaml new file mode 100644 index 000000000000..ff23972419e9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/semverisvalid/test-semverisvalid-0350.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: semverisvalid/invalid version + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + semver.is_valid("1", __local0__) + x = __local0__ + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/semverisvalid/test-semverisvalid-0351.yaml b/third_party/opa/v1/test/cases/testdata/v1/semverisvalid/test-semverisvalid-0351.yaml new file mode 100644 index 000000000000..b618638dff41 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/semverisvalid/test-semverisvalid-0351.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: semverisvalid/invalid type + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + semver.is_valid(1, __local0__) + x = __local0__ + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0871.yaml b/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0871.yaml new file mode 100644 index 000000000000..0105f3c626df --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0871.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: sets/set_diff + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + s1 = {1, 2, 3, 4} + s2 = {1, 3} + __local0__ = s1 - s2 + x = __local0__ + } + data: {} + want_result: + - x: + - 2 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0872.yaml b/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0872.yaml new file mode 100644 index 000000000000..4f8c090a28a1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0872.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: "sets/set_diff: refs" + query: data.generated.p = x + modules: + - | + package generated + + p = x if { + __local0__ := data.a[0] + __local1__ := data.a[1] + __local2__ := data.a[2] + s1 := {__local0__, __local1__, __local2__} + __local3__ := data.a[0] + s2 := {2, __local3__} + x := s1 - s2 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0873.yaml b/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0873.yaml new file mode 100644 index 000000000000..718b5d593c8d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0873.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: "sets/set_diff: ground output" + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = {1, 2, 3} - {2, 3} + {1} = __local0__ + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0874.yaml b/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0874.yaml new file mode 100644 index 000000000000..493ad8369381 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0874.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: "sets/set_diff: virt docs" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local1__ = data.generated.s1 + __local2__ = data.generated.s2 + __local0__ = __local1__ - __local2__ + x = __local0__ + } + + s1 contains 1 + + s1 contains 2 + + s1 contains "c" + + s2 := {"c", 1} + data: {} + want_result: + - x: + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0875.yaml b/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0875.yaml new file mode 100644 index 000000000000..8c81727d59f8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0875.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: sets/intersect + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local1__ = data.a[1] + __local2__ = data.a[2] + __local3__ = data.a[2] + __local0__ = {3, __local1__, __local2__} & {3, 4, __local3__} + x = __local0__ + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0876.yaml b/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0876.yaml new file mode 100644 index 000000000000..b5a31cbe7282 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/sets/test-sets-0876.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: sets/union + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local1__ = data.a[1] + __local2__ = data.a[2] + __local3__ = data.a[2] + __local0__ = {3, __local1__, __local2__} | {3, 4, __local3__} + {2, 3, 4} = __local0__ + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/sprintf/test-sprintf.yaml b/third_party/opa/v1/test/cases/testdata/v1/sprintf/test-sprintf.yaml new file mode 100644 index 000000000000..f5a57b436fa3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/sprintf/test-sprintf.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: sprintf/big_int + query: data.test.p = x + modules: + - | + package test + + p := x if { + x = sprintf("%s", [123456789123456789123]) + } + want_result: + - x: "123456789123456789123" + - note: sprintf/big_int/max_cert_serial_number + query: data.test.p = x + modules: + - | + package test + + p := x if { + x = sprintf("%s", [1208925819614629174706175]) + } + want_result: + - x: "1208925819614629174706175" diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-anyprefixmatch.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-anyprefixmatch.yaml new file mode 100644 index 000000000000..217bc093f701 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-anyprefixmatch.yaml @@ -0,0 +1,430 @@ +--- +cases: + - note: strings/any_prefix_match/match + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - a/ + - d/ + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: + - x: true + - note: strings/any_prefix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - f/ + - d/ + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: [] + - note: strings/any_prefix_match/match + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - a/b/ + - a/ + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: + - x: true + - note: strings/any_prefix_match/match + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - d/ + - e/f/g + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: + - x: true + - note: strings/any_prefix_match/match + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - aa/b + - e/f + strings: + - aa/bb/cc + - aa/bb/dd + - ee/ff/gg + want_result: + - x: true + - note: strings/any_prefix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - a/b + - e/f + strings: + - aa/bb/cc + - aa/bb/dd + - ee/ff/gg + want_result: [] + - note: strings/any_prefix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - b/cc + strings: + - aa/bb/cc + - aa/bb/dd + - ee/ff/gg + want_result: [] + - note: strings/any_prefix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - a/b + - e/f + strings: [] + want_result: [] + - note: strings/any_prefix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: [] + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: [] + - note: strings/any_prefix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: [] + strings: [] + want_result: [] + - note: strings/any_prefix_match/match + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefix) + } + input: + prefix: a/ + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: + - x: true + - note: strings/any_prefix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefix) + } + input: + prefix: d/ + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: [] + - note: strings/any_prefix_match/match + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.string, input.prefixes) + } + input: + prefixes: + - a/ + - d/ + string: a/b/c + want_result: + - x: true + - note: strings/any_prefix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.string, input.prefixes) + } + input: + prefixes: + - g/ + - d/ + string: a/b/c + want_result: [] + - note: strings/any_prefix_match/match + query: data.test.p = x + modules: + - | + package test + + strings_set contains str if { + str := input.strings[_] + } + + prefixes_set contains prefix if { + prefix := input.prefixes[_] + } + + p if { + strings.any_prefix_match(strings_set, prefixes_set) + } + input: + prefixes: + - a/ + - d/ + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: + - x: true + - note: strings/any_prefix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + strings_set contains str if { + str := input.strings[_] + } + + prefixes_set contains prefix if { + prefix := input.prefixes[_] + } + + p if { + strings.any_prefix_match(strings_set, prefixes_set) + } + input: + prefixes: + - f/ + - d/ + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: [] + - note: strings/any_prefix_match/type_error_nostrict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - f/ + - d/ + strings: + - "1" + - "2" + - "3" + want_result: [] + - note: strings/any_prefix_match/type_error_nostrict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - f/ + - d/ + strings: 1 + want_result: [] + - note: strings/any_prefix_match/type_error_nostrict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - "1" + - "2" + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: [] + - note: strings/any_prefix_match/type_error_nostrict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: 1 + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: [] + - note: strings/any_prefix_match/type_error_strict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - f/ + - d/ + strings: + - 1 + - 2 + - 3 + want_error: "test-0.rego:4: eval_type_error: strings.any_prefix_match: operand 1 must be array of strings but got array containing number" + strict_error: true + - note: strings/any_prefix_match/type_error_strict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - f/ + - d/ + strings: 1 + want_error: "test-0.rego:4: eval_type_error: strings.any_prefix_match: operand 1 must be one of {string, set, array} but got number" + strict_error: true + - note: strings/any_prefix_match/type_error_strict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: + - 1 + - 2 + strings: + - a/b/c + - a/b/d + - e/f/g + want_error: "test-0.rego:4: eval_type_error: strings.any_prefix_match: operand 2 must be array of strings but got array containing number" + strict_error: true + - note: strings/any_prefix_match/type_error_strict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_prefix_match(input.strings, input.prefixes) + } + input: + prefixes: 1 + strings: + - a/b/c + - a/b/d + - e/f/g + want_error: "test-0.rego:4: eval_type_error: strings.any_prefix_match: operand 2 must be one of {string, set, array} but got number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-anysuffixmatch.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-anysuffixmatch.yaml new file mode 100644 index 000000000000..1db3eb321bca --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-anysuffixmatch.yaml @@ -0,0 +1,430 @@ +--- +cases: + - note: strings/any_suffix_match/match + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.suffixes) + } + input: + strings: + - a/b/c + - a/b/d + - e/f/g + suffixes: + - /c + - /a + want_result: + - x: true + - note: strings/any_suffix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.suffixes) + } + input: + strings: + - a/b/c + - a/b/d + - e/f/g + suffixes: + - /f + - /a + want_result: [] + - note: strings/any_suffix_match/match + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.suffixes) + } + input: + strings: + - a/b/c + - a/b/d + - e/f/g + suffixes: + - /b/c + - /d + want_result: + - x: true + - note: strings/any_suffix_match/match + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.suffixes) + } + input: + strings: + - a/b/c + - a/b/d + - e/f/g + suffixes: + - /a + - e/f/g + want_result: + - x: true + - note: strings/any_suffix_match/match + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.suffixes) + } + input: + strings: + - aa/bb/cc + - aa/bb/dd + - ee/ff/gg + suffixes: + - b/cc + - f/g + want_result: + - x: true + - note: strings/any_suffix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.suffixes) + } + input: + strings: + - aa/bb/cc + - aa/bb/dd + - ee/ff/gg + suffixes: + - b/c + - f/g + want_result: [] + - note: strings/any_suffix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.suffixes) + } + input: + strings: + - aa/bb/cc + - aa/bb/dd + - ee/ff/gg + suffixes: + - aa/b + want_result: [] + - note: strings/any_suffix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.suffixes) + } + input: + strings: [] + suffixes: + - a/b + - e/f + want_result: [] + - note: strings/any_suffix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.suffixes) + } + input: + strings: + - a/b/c + - a/b/d + - e/f/g + suffixes: [] + want_result: [] + - note: strings/any_suffix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.suffixes) + } + input: + strings: [] + suffixes: [] + want_result: [] + - note: strings/any_suffix_match/match + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.suffix) + } + input: + strings: + - a/b/c + - a/b/d + - e/f/g + suffix: /c + want_result: + - x: true + - note: strings/any_suffix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.suffix) + } + input: + strings: + - a/b/c + - a/b/d + - e/f/g + suffix: /h + want_result: [] + - note: strings/any_suffix_match/match + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.string, input.suffixes) + } + input: + string: a/b/c + suffixes: + - /c + - /a + want_result: + - x: true + - note: strings/any_suffix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.string, input.suffixes) + } + input: + string: a/b/g + suffixes: + - /c + - /a + want_result: [] + - note: strings/any_suffix_match/match + query: data.test.p = x + modules: + - | + package test + + strings_set contains str if { + str := input.strings[_] + } + + suffixes_set contains suffix if { + suffix := input.suffixes[_] + } + + p if { + strings.any_suffix_match(strings_set, suffixes_set) + } + input: + strings: + - a/b/c + - a/b/d + - e/f/g + suffixes: + - /c + - /a + want_result: + - x: true + - note: strings/any_suffix_match/nomatch + query: data.test.p = x + modules: + - | + package test + + strings_set contains str if { + str := input.strings[_] + } + + suffixes_set contains suffix if { + suffix := input.suffixes[_] + } + + p if { + strings.any_suffix_match(strings_set, suffixes_set) + } + input: + strings: + - a/b/c + - a/b/d + - e/f/g + suffixes: + - /f + - /a + want_result: [] + - note: strings/any_suffix_match/type_error_nostrict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.prefixes) + } + input: + prefixes: + - f/ + - d/ + strings: + - 1 + - 2 + - 3 + want_result: [] + - note: strings/any_suffix_match/type_error_nostrict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.prefixes) + } + input: + prefixes: + - f/ + - d/ + strings: 1 + want_result: [] + - note: strings/any_suffix_match/type_error_nostrict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.prefixes) + } + input: + prefixes: + - 1 + - 2 + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: [] + - note: strings/any_suffix_match/type_error_nostrict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.prefixes) + } + input: + prefixes: 1 + strings: + - a/b/c + - a/b/d + - e/f/g + want_result: [] + - note: strings/any_suffix_match/type_error_strict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.prefixes) + } + input: + prefixes: + - f/ + - d/ + strings: + - 1 + - 2 + - 3 + want_error: "test-0.rego:4: eval_type_error: strings.any_suffix_match: operand 1 must be array of strings but got array containing number" + strict_error: true + - note: strings/any_suffix_match/type_error_strict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.prefixes) + } + input: + prefixes: + - f/ + - d/ + strings: 1 + want_error: "test-0.rego:4: eval_type_error: strings.any_suffix_match: operand 1 must be one of {string, set, array} but got number" + strict_error: true + - note: strings/any_suffix_match/type_error_strict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.prefixes) + } + input: + prefixes: + - 1 + - 2 + strings: + - a/b/c + - a/b/d + - e/f/g + want_error: "test-0.rego:4: eval_type_error: strings.any_suffix_match: operand 2 must be array of strings but got array containing number" + strict_error: true + - note: strings/any_suffix_match/type_error_strict + query: data.test.p = x + modules: + - | + package test + + p if { + strings.any_suffix_match(input.strings, input.prefixes) + } + input: + prefixes: 1 + strings: + - a/b/c + - a/b/d + - e/f/g + want_error: "test-0.rego:4: eval_type_error: strings.any_suffix_match: operand 2 must be one of {string, set, array} but got number" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0877.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0877.yaml new file mode 100644 index 000000000000..3098c7093738 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0877.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: strings/format_int + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + format_int(15.5, 16, x) + } + data: {} + want_result: + - x: f diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0878.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0878.yaml new file mode 100644 index 000000000000..39f79c038b39 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0878.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "strings/format_int: undefined" + query: data.generated.p = x + modules: + - | + package generated + + p if { + format_int(15.5, 16, "10000") + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0879.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0879.yaml new file mode 100644 index 000000000000..a4a88e8f0fc9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0879.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "strings/format_int: ref dest" + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.numbers[2] + format_int(3.1, 10, __local0__) + } + data: + numbers: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0880.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0880.yaml new file mode 100644 index 000000000000..b07a2aaa3e4c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0880.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "strings/format_int: ref dest (2)" + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.numbers[2] + not format_int(4.1, 10, __local0__) + } + data: + numbers: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0881.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0881.yaml new file mode 100644 index 000000000000..c8a320c5c7ab --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0881.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/format_int: err: bad base" + query: data.generated.p = x + modules: + - | + package generated + + p if { + format_int(4.1, 199, x) + } + want_error_code: eval_type_error + want_error: operand 2 must be one of {2, 8, 10, 16} + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0882.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0882.yaml new file mode 100644 index 000000000000..22194f38cc5f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0882.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: strings/concat + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + concat("/", ["", "foo", "bar", "0", "baz"], x) + } + data: {} + want_result: + - x: /foo/bar/0/baz diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0883.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0883.yaml new file mode 100644 index 000000000000..0bbaefa5aa08 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0883.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "strings/concat: set" + query: data.test.p = x + modules: + - | + package test + + # Sets are unordered, so the output is not guaranteed. + # These are theoretically possible: + possibilities := { + "1,2,3", + "2,3,1", + "3,1,2", + "3,2,1", + "2,1,3", + "1,3,2", + } + + p if { + x := concat(",", {"1", "2", "3"}) + possibilities[x] + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0884.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0884.yaml new file mode 100644 index 000000000000..75fac3d25786 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0884.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "strings/concat: undefined" + query: data.generated.p = x + modules: + - | + package generated + + p if { + concat("/", ["a", "b"], "deadbeef") + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0885.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0885.yaml new file mode 100644 index 000000000000..484e37ea00b8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0885.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: "strings/concat: ref dest" + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.c[0].x[2] + concat("", ["f", "o", "o"], __local0__) + } + data: + c: + - "true": + - null + - "3.14159" + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0886.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0886.yaml new file mode 100644 index 000000000000..1a915fa615a4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0886.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: "strings/concat: ref dest (2)" + query: data.generated.p = x + modules: + - | + package generated + + p if { + __local0__ = data.c[0].x[2] + not concat("", ["b", "a", "r"], __local0__) + } + data: + c: + - "true": + - null + - "3.14159" + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0887.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0887.yaml new file mode 100644 index 000000000000..a4f982d33870 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0887.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: strings/indexof + query: data.test.p = x + modules: + - | + package test + + p := x if { + indexof("abcdefgh", "cde", x) + } + want_result: + - x: 2 + - note: strings/indexof + query: data.test.p = x + modules: + - | + package test + + p := x if { + indexof("abcabcabcdefgh", "cde", x) + } + want_result: + - x: 8 diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0888.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0888.yaml new file mode 100644 index 000000000000..464a7c326b6d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0888.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/indexof: not found" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + indexof("abcdefgh", "xyz", x) + } + data: {} + want_result: + - x: -1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0889.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0889.yaml new file mode 100644 index 000000000000..39e7844e69bc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0889.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: strings/substring + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + substring("abcdefgh", 2, 3, x) + } + data: {} + want_result: + - x: cde + - note: "strings/substring: unicode" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + substring("åäö", 0, 2, x) + } + want_result: + - x: åä diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0890.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0890.yaml new file mode 100644 index 000000000000..2a81bd9cf0a2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0890.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "strings/substring: remainder" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + substring("abcdefgh", 2, -1, x) + } + data: {} + want_result: + - x: cdefgh + - note: "strings/substring: remainder unicode" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + substring("aåäö", 2, -1, x) + } + want_result: + - x: äö diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0891.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0891.yaml new file mode 100644 index 000000000000..feacbbf49cf9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0891.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "strings/substring: too long" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + substring("abcdefgh", 2, 10000, x) + } + data: {} + want_result: + - x: cdefgh + - note: "strings/substring: too long unicode" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + substring("abcdefghåäö", 2, 10000, x) + } + want_result: + - x: cdefghåäö diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0892.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0892.yaml new file mode 100644 index 000000000000..fa4b69d32c65 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0892.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: "strings/substring: offset negative" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + substring("aaa", -1, -1, x) + } + want_error_code: eval_builtin_error + want_error: negative offset + strict_error: true + - note: "strings/substring: offset negative unicode" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + substring("åäö", -1, -1, x) + } + want_error_code: eval_builtin_error + want_error: negative offset + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0893.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0893.yaml new file mode 100644 index 000000000000..a138064b299c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0893.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "strings/substring: offset too long" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + substring("aaa", 3, -1, x) + } + data: {} + want_result: + - x: "" + - note: "strings/substring: offset too long unicode" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + substring("åäö", 3, -1, x) + } + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0894.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0894.yaml new file mode 100644 index 000000000000..4d357481e841 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0894.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "strings/substring: offset too long 2" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + substring("aaa", 4, -1, x) + } + data: {} + want_result: + - x: "" + - note: "strings/substring: offset too long 2 unicode" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + substring("åäö", 4, -1, x) + } + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0895.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0895.yaml new file mode 100644 index 000000000000..035c44ae5bd1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0895.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: strings/contains + query: data.generated.p = x + modules: + - | + package generated + + p if { + contains("abcdefgh", "defg") + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0896.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0896.yaml new file mode 100644 index 000000000000..a7499f950c35 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0896.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "strings/contains: undefined" + query: data.generated.p = x + modules: + - | + package generated + + p if { + contains("abcdefgh", "ac") + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0897.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0897.yaml new file mode 100644 index 000000000000..ddba86ec3e4b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0897.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: strings/startswith + query: data.generated.p = x + modules: + - | + package generated + + p if { + startswith("abcdefgh", "abcd") + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0898.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0898.yaml new file mode 100644 index 000000000000..d42d12b86f1f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0898.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "strings/startswith: undefined" + query: data.generated.p = x + modules: + - | + package generated + + p if { + startswith("abcdefgh", "bcd") + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0899.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0899.yaml new file mode 100644 index 000000000000..e4b6d3af6c0c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0899.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: strings/endswith + query: data.generated.p = x + modules: + - | + package generated + + p if { + endswith("abcdefgh", "fgh") + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0900.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0900.yaml new file mode 100644 index 000000000000..49245edfbf88 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0900.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: "strings/endswith: undefined" + query: data.generated.p = x + modules: + - | + package generated + + p if { + endswith("abcdefgh", "fg") + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0901.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0901.yaml new file mode 100644 index 000000000000..4b72b0a8bf0c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0901.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: strings/lower + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + lower("AbCdEf", x) + } + data: {} + want_result: + - x: abcdef diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0902.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0902.yaml new file mode 100644 index 000000000000..6d29195f745e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0902.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: strings/upper + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + upper("AbCdEf", x) + } + data: {} + want_result: + - x: ABCDEF diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0903.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0903.yaml new file mode 100644 index 000000000000..4bd0d3e44ce5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0903.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/split: empty string" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + split("", ".", [x]) + } + data: {} + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0904.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0904.yaml new file mode 100644 index 000000000000..4a0d68544d80 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0904.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/split: one" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + split("foo", ".", [x]) + } + data: {} + want_result: + - x: foo diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0905.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0905.yaml new file mode 100644 index 000000000000..9e928b0720e6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0905.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "strings/split: many" + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y] if { + split("foo.bar.baz", ".", [x, "bar", y]) + } + data: {} + want_result: + - x: + - foo + - baz diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0906.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0906.yaml new file mode 100644 index 000000000000..de222313d884 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0906.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/replace: empty string" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + replace("", "hi", "bye", x) + } + data: {} + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0907.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0907.yaml new file mode 100644 index 000000000000..5f0f72dcb29b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0907.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/replace: one" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + replace("foo.bar", ".", ",", x) + } + data: {} + want_result: + - x: foo,bar diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0908.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0908.yaml new file mode 100644 index 000000000000..e2b57e52026a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0908.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/replace: many" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + replace("foo.bar.baz", ".", ",", x) + } + data: {} + want_result: + - x: foo,bar,baz diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0909.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0909.yaml new file mode 100644 index 000000000000..4be2cb147f9b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0909.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/replace: overlap" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + replace("foo...bar", "..", ",,", x) + } + data: {} + want_result: + - x: foo,,.bar diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0910.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0910.yaml new file mode 100644 index 000000000000..04151043a9d3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0910.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/trim: empty string" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + trim("", ".", x) + } + data: {} + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0911.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0911.yaml new file mode 100644 index 000000000000..35bd22c17cda --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0911.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/trim: end" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + trim("foo.bar...", ".", x) + } + data: {} + want_result: + - x: foo.bar diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0912.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0912.yaml new file mode 100644 index 000000000000..b377cad5b1d4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0912.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/trim: start" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + trim("...foo.bar", ".", x) + } + data: {} + want_result: + - x: foo.bar diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0913.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0913.yaml new file mode 100644 index 000000000000..adabba912d9e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0913.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/trim: both" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + trim("...foo.bar...", ".", x) + } + data: {} + want_result: + - x: foo.bar diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0914.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0914.yaml new file mode 100644 index 000000000000..a2e9b81182b8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0914.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/trim: multi-cutset" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + trim("...foo.bar...", ".fr", x) + } + data: {} + want_result: + - x: oo.ba diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0915.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0915.yaml new file mode 100644 index 000000000000..af40b661e4bd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0915.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/trim: multi-cutset-none" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + trim("...foo.bar...", ".o", x) + } + data: {} + want_result: + - x: foo.bar diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0916.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0916.yaml new file mode 100644 index 000000000000..1ad9853462c8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0916.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/sprintf: none" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + sprintf("hi", [], x) + } + data: {} + want_result: + - x: hi diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0917.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0917.yaml new file mode 100644 index 000000000000..06b857da8119 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0917.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/sprintf: string" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + sprintf("hi %s", ["there"], x) + } + data: {} + want_result: + - x: hi there diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0918.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0918.yaml new file mode 100644 index 000000000000..37e51f0c1a74 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0918.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/sprintf: int" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + sprintf("hi %02d", [5], x) + } + data: {} + want_result: + - x: hi 05 diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0919.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0919.yaml new file mode 100644 index 000000000000..82c48aba0f34 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0919.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/sprintf: hex" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + sprintf("hi %02X.%02X", [127, 1], x) + } + data: {} + want_result: + - x: hi 7F.01 diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0920.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0920.yaml new file mode 100644 index 000000000000..89c10bea1f3f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0920.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/sprintf: float" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + sprintf("hi %.2f", [3.1415], x) + } + data: {} + want_result: + - x: hi 3.14 diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0921.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0921.yaml new file mode 100644 index 000000000000..b4d5414d32e5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0921.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/sprintf: float too big" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + sprintf("hi %v", [2e308], x) + } + data: {} + want_result: + - x: hi 2e308 diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0922.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0922.yaml new file mode 100644 index 000000000000..1a5edc45e75b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0922.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/sprintf: bool" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + sprintf("hi %s", [true], x) + } + data: {} + want_result: + - x: hi true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0923.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0923.yaml new file mode 100644 index 000000000000..056fa96002ae --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0923.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: "strings/sprintf: composite" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + sprintf("hi %v", [["there", 5, 3.14]], x) + } + data: {} + want_result: + - x: hi ["there", 5, 3.14] diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0924.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0924.yaml new file mode 100644 index 000000000000..e8d6de78aac4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0924.yaml @@ -0,0 +1,71 @@ +--- +cases: + - note: strings/reverse_bar + query: data.test.p = x + modules: + - | + package test + + p := strings.reverse(data.foo) + data: + foo: bar + want_result: + - x: rab + - note: strings/reverse_unicode_multi_char_emojii + query: data.test.p = x + modules: + - | + package test + + p := strings.reverse(data.foo) + data: + foo: 2️⃣ + want_result: + - x: ⃣️2 + - note: strings/reverse_unicode + query: data.test.p = x + modules: + - | + package test + + p := strings.reverse(data.foo) + data: + foo: "1\U0001F600\U0001D6FE" + want_result: + - x: "\U0001D6FE\U0001F6001" + - note: strings/reverse_empty + query: data.test.p = x + modules: + - | + package test + + p := strings.reverse(data.foo) + data: + foo: "" + want_result: + - x: "" + - note: strings/reverse_number_error + query: data.test.p = x + modules: + - | + package test + + p := strings.reverse(data.foo) + data: + foo: 123 + want_error_code: eval_type_error + want_error: "reverse: operand 1 must be string but got number" + strict_error: true + - note: strings/reverse_object_error + query: data.test.p = x + modules: + - | + package test + + p := strings.reverse(data.foo) + data: + foo: + bar: baz + want_error_code: eval_type_error + want_error: "reverse: operand 1 must be string but got object" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0925.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0925.yaml new file mode 100644 index 000000000000..f78e75baf481 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0925.yaml @@ -0,0 +1,47 @@ +--- +cases: + - note: strings/indexof_n_single_match + query: data.test.p = x + modules: + - | + package test + + p := indexof_n("dogcat", "cat") + want_result: + - x: + - 3 + - note: strings/indexof_n_multiple_matches + query: data.test.p = x + modules: + - | + package test + + p := indexof_n("dogcatdogcat", "cat") + want_result: + - x: + - 3 + - 9 + - note: strings/indexof_n_no_match + query: data.test.p = x + modules: + - | + package test + + p := indexof_n("dogcat", "rabbit") + want_result: + - x: [] + - note: strings/indexof_n_unicode_matches + query: data.test.p = x + modules: + - "package test\n\np := indexof_n(\"\U0001F607\U0001F600\U0001F607\U0001F600\U0001F607\U0001F600\", \"\U0001F600\")\n" + want_result: + - x: + - 1 + - 3 + - 5 + - note: strings/indexof_n_unicode_no_match + query: data.test.p = x + modules: + - "package test\n\np := indexof_n(\"\U0001F607\U0001F600\U0001F607\U0001F600\U0001F607\U0001F600\", \"\U0001F602\")\n" + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0926.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0926.yaml new file mode 100644 index 000000000000..b6eded1f577d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-0926.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: strings/count_single_word_match + query: data.test.p = x + modules: + - | + package test + + p := strings.count("cheese", "e") + want_result: + - x: 3 + - note: strings/count_multiple_separate_matches + query: data.test.p = x + modules: + - | + package test + + p := strings.count("hello hello hello world", "hello") + want_result: + - x: 3 + - note: strings/count_n_no_match + query: data.test.p = x + modules: + - | + package test + + p := strings.count("dummy", "x") + want_result: + - x: 0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-indexof-unicode.yaml b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-indexof-unicode.yaml new file mode 100644 index 000000000000..d5b572a9907c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/strings/test-strings-indexof-unicode.yaml @@ -0,0 +1,47 @@ +--- +cases: + - note: "strings/indexof: unicode char" + query: data.test.p = x + modules: + - | + package test + + p := x if { + indexof("μx", "x", x) + } + want_result: + - x: 1 + - note: "strings/indexof: unicode chars not found" + query: data.test.p = x + modules: + - | + package test + + p := x if { + indexof("μ", "μμ", x) + } + want_result: + - x: -1 + - note: "strings/indexof: unicode string" + query: data.test.p = x + modules: + - | + package test + + p := x if { + indexof("skön var våren", "vår", x) + } + want_result: + - x: 9 + - note: "strings/indexof: unicode string emoji" + query: data.test.p = x + modules: + - "package test\n\np := x if {\n\tindexof(\"\U0001F367\U0001F368\U0001F9C1\U0001F370\U0001F36E\", \"\U0001F36E\", x)\n}\n" + want_result: + - x: 4 + - note: "strings/indexof: unicode string emojis" + query: data.test.p = x + modules: + - "package test\n\np := x if {\n\tindexof(\"\U0001F367\U0001F368\U0001F9C1\U0001F370\U0001F36E\", \"\U0001F367\U0001F368\U0001F9C1\U0001F370\U0001F36E\", x)\n}\n" + want_result: + - x: 0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/subset/test-subset.yaml b/third_party/opa/v1/test/cases/testdata/v1/subset/test-subset.yaml new file mode 100644 index 000000000000..ad2bb75baa93 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/subset/test-subset.yaml @@ -0,0 +1,415 @@ +--- +cases: + - note: subset/simple object subset 1 + query: data.test.test_result = x + modules: + - | + package test + + A := { + "a": 5, + "b": 7, + "c": 15, + } + + B := {"a": 5} + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + test_result if { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true + - note: subset/simple object subset 2 + query: data.test.test_result = x + modules: + - | + package test + + A := { + "a": 5, + "b": 7, + "c": 15, + } + + B := { + "a": 5, + "b": 10, + } + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + test_result if { + not AsubB # B is not a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true + - note: subset/nested object subset 1 + query: data.test.test_result = x + modules: + - | + package test + + A := { + "a": 5, + "b": 7, + "c": 15, + "nested": { + "x": 10, + "y": 15, + "z": 20, + }, + } + + B := { + "a": 5, + "nested": { + "x": 10, + "y": 15, + "z": 20, + }, + } + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + test_result if { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true + - note: subset/nested object subset 2 + query: data.test.test_result = x + modules: + - | + package test + + A := { + "a": 5, + "b": 7, + "c": 15, + "nested": { + "x": 10, + "y": 15, + "z": 20, + }, + } + + # The subset operation applies recursively to nested objects + + B := { + "a": 5, + "nested": { + "x": 10, + "y": 15, + }, + } + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + test_result if { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true + - note: subset/nested object subset 3 + query: data.test.test_result = x + modules: + - | + package test + + A := { + "a": 5, + "b": 7, + "c": 15, + "nested": { + "x": 10, + "y": 15, + "z": 20, + "set1": {1, 2, 3, 4}, + "arr1": [6, 7, 8, 9], + }, + } + + # The subset operation applies recursively to nested objects + + B := { + "a": 5, + "nested": { + "x": 10, + "y": 15, + "set1": {4, 1}, + "arr1": [6, 7], + }, + } + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + test_result if { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true + - note: subset/sets 1 + query: data.test.test_result = x + modules: + - | + package test + + A := {1, 2, 3} + + B := {3, 2} + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + test_result if { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true + - note: subset/nested sets 1 + query: data.test.test_result = x + modules: + - | + package test + + A := {1, 2, 3, {"a", "b", "c"}} + + B := {3, 2, {"a", "b", "c"}} + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + test_result if { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true + - note: subset/nested sets 2 + query: data.test.test_result = x + modules: + - | + package test + + A := {1, 2, 3, {"a", "b", "c"}} + + B := {3, 2, {"a", "b"}} + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result if { + not AsubB # B is not a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true + - note: subset/arrays 1 + query: data.test.test_result = x + modules: + - | + package test + + A := [1, 2, 3, 4, 5, 6] + + B := [3, 4, 5] + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result if { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true + - note: subset/arrays 2 + query: data.test.test_result = x + modules: + - | + package test + + A := [1, 2, 3, 4, 5, 6] + + B := [1, 2, 3] + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result if { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true + - note: subset/arrays 3 + query: data.test.test_result = x + modules: + - | + package test + + A := [1, 2, 3, 4, 5, 6] + + B := [4, 5, 6] + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result if { + AsubB # B is a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true + - note: subset/arrays 4 + query: data.test.test_result = x + modules: + - | + package test + + A := [1, 2, 3, 4, 5, 6] + + B := [1, 2, 3, 4, 5, 6] + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result if { + AsubB # B is a subset of A + BsubA # A is a subset of B + } + want_result: + - x: true + - note: subset/array and set 1 + query: data.test.test_result = x + modules: + - | + package test + + A := [1, 2, 3, 4, 5, 6] + + B := {4, 3, 2} + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + test_result if { + AsubB # B is a subset of A + not BsubA # It is invalid operands + } + want_result: + - x: true + - note: subset/array and set 2 + query: data.test.test_result = x + modules: + - | + package test + + A := [1, 2, 3, 4, 5, 6] + + B := {9, 8, 7} + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + test_result if { + not AsubB # B is not a subset of A + not BsubA # It is invalid operands + } + want_result: + - x: true + - note: subset/arrays 5 + query: data.test.test_result = x + modules: + - | + package test + + A := [1, 2, 3, 4, 5, 6] + + B := [4, 5, 6, 8] + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result if { + not AsubB # B is not a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true + - note: subset/arrays 6 + query: data.test.test_result = x + modules: + - | + package test + + A := [1, 2, 3, 4, 5, 6] + + B := [8, 9, 10] + + AsubB := object.subset(A, B) + + BsubA := object.subset(B, A) + + # Notice that there isn't really a well-defined way to "match" the two + # nested sets to one another, so we B is not a subset of A in this + # case. + + test_result if { + not AsubB # B is not a subset of A + not BsubA # A is not a subset of B + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0947.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0947.yaml new file mode 100644 index 000000000000..52ea1594cd5b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0947.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: time/time caching + query: data.generated.p = x + modules: + - | + package generated + + p if { + time.now_ns(t0) + test.sleep("10ms") + time.now_ns(t1) + t0 == t1 + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0948.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0948.yaml new file mode 100644 index 000000000000..680cf0833cca --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0948.yaml @@ -0,0 +1,64 @@ +--- +cases: + - note: time/parse_nanos + query: data.generated.p = x + modules: + - | + package generated + + p[case_id] := ns if { + case := input.cases[case_id] + time.parse_ns(case.layout, case.value, ns) + } + input: + cases: + "1": + layout: 2006-01-02T15:04:05Z07:00 + value: "2017-06-02T19:00:00-07:00" + "2": + layout: 2006-01-02T15:04:05Z07:00 + value: "1677-09-21T00:12:43.145224192-00:00" + "3": + layout: 2006-01-02T15:04:05Z07:00 + value: "2262-04-11T23:47:16.854775807-00:00" + "4": + layout: 01/02 03:04:05PM '06 -0700 + value: 06/02 07:00:00PM '17 -0700 + "5": + layout: 02 Jan 06 15:04 -0700 + value: 02 Jun 17 19:00 -0700 + "6": + layout: RFC822Z + value: 02 Jun 17 19:00 -0700 + want_result: + - x: + "1": 1496455200000000000 + "2": -9223372036854775808 + "3": 9223372036854775807 + "4": 1496455200000000000 + "5": 1496455200000000000 + "6": 1496455200000000000 + - note: time/parse_nanos_too_small + query: data.generated.p = x + modules: + - | + package generated + + p := ns if { + time.parse_ns("2006-01-02T15:04:05Z07:00", "1677-09-21T00:12:43.145224191-00:00", ns) + } + want_error_code: eval_builtin_error + want_error: time outside of valid range + strict_error: true + - note: time/parse_nanos_too_large + query: data.generated.p = x + modules: + - | + package generated + + p := ns if { + time.parse_ns("2006-01-02T15:04:05Z07:00", "2262-04-11T23:47:16.854775808-00:00", ns) + } + want_error_code: eval_builtin_error + want_error: time outside of valid range + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0949.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0949.yaml new file mode 100644 index 000000000000..c4d97607c543 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0949.yaml @@ -0,0 +1,48 @@ +--- +cases: + - note: time/parse_rfc3339_nanos + query: data.generated.p = x + modules: + - | + package generated + + p[t] := ns if { + t = input.cases[_] + time.parse_rfc3339_ns(t, ns) + } + input: + cases: + - "1677-09-21T00:12:43.145224192-00:00" + - "1970-01-01T00:00:00-00:00" + - "2017-06-02T19:00:00-07:00" + - "2262-04-11T23:47:16.854775807-00:00" + want_result: + - x: + "1677-09-21T00:12:43.145224192-00:00": -9223372036854775808 + "1970-01-01T00:00:00-00:00": 0 + "2017-06-02T19:00:00-07:00": 1496455200000000000 + "2262-04-11T23:47:16.854775807-00:00": 9223372036854775807 + - note: time/parse_rfc3339_nanos_too_small + query: data.generated.p = x + modules: + - | + package generated + + p := ns if { + time.parse_rfc3339_ns("1677-09-21T00:12:43.145224191-00:00", ns) + } + want_error_code: eval_builtin_error + want_error: time outside of valid range + strict_error: true + - note: time/parse_rfc3339_nanos_too_large + query: data.generated.p = x + modules: + - | + package generated + + p := ns if { + time.parse_rfc3339_ns("2262-04-11T23:47:16.854775808-00:00", ns) + } + want_error_code: eval_builtin_error + want_error: time outside of valid range + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0950.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0950.yaml new file mode 100644 index 000000000000..1ca6fd4dd004 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0950.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: time/parse duration nanos + query: data.generated.p = x + modules: + - | + package generated + + p := ns if { + time.parse_duration_ns("100ms", ns) + } + data: {} + want_result: + - x: 100000000 diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0951.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0951.yaml new file mode 100644 index 000000000000..43e386942416 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0951.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: time/date + query: data.generated.p = x + modules: + - | + package generated + + p := [__local0__, __local1__, __local2__] if { + __local3__ = 1517814000 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.date(__local5__, __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + data: {} + want_result: + - x: + - 2018 + - 2 + - 5 diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0952.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0952.yaml new file mode 100644 index 000000000000..61582c1e0035 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0952.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: time/date with LA tz + query: data.generated.p = x + modules: + - | + package generated + + p := [__local0__, __local1__, __local2__] if { + __local3__ = 1517814000 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.date([__local5__, "America/Los_Angeles"], __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + data: {} + want_result: + - x: + - 2018 + - 2 + - 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0953.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0953.yaml new file mode 100644 index 000000000000..c0a1af1ccabb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0953.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: time/date with empty tz + query: data.generated.p = x + modules: + - | + package generated + + p := [__local0__, __local1__, __local2__] if { + __local3__ = 1517832000 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.date([__local5__, ""], __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + data: {} + want_result: + - x: + - 2018 + - 2 + - 5 diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0954.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0954.yaml new file mode 100644 index 000000000000..d6f7441c3420 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0954.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: time/date leap day + query: data.generated.p = x + modules: + - | + package generated + + p := [__local0__, __local1__, __local2__] if { + __local3__ = 1582977600 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.date(__local5__, __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + data: {} + want_result: + - x: + - 2020 + - 2 + - 29 diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0955.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0955.yaml new file mode 100644 index 000000000000..718e4b20b35f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0955.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: time/date too big + query: data.generated.p = x + modules: + - | + package generated + + p := [__local0__, __local1__, __local2__] if { + __local3__ = 1582977600 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + __local6__ = __local5__ * 1000 + time.date(__local6__, __local7__) + [__local0__, __local1__, __local2__] = __local7__ + } + want_error_code: eval_builtin_error + want_error: timestamp too big + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0956.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0956.yaml new file mode 100644 index 000000000000..64afe9c6afce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0956.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: time/clock + query: data.generated.p = x + modules: + - | + package generated + + p := [__local0__, __local1__, __local2__] if { + __local3__ = 1517832000 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.clock(__local5__, __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + data: {} + want_result: + - x: + - 12 + - 0 + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0957.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0957.yaml new file mode 100644 index 000000000000..53738280d80a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0957.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: time/clock with NY tz + query: data.generated.p = x + modules: + - | + package generated + + p := [__local0__, __local1__, __local2__] if { + __local3__ = 1517832000 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.clock([__local5__, "America/New_York"], __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + data: {} + want_result: + - x: + - 7 + - 0 + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0958.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0958.yaml new file mode 100644 index 000000000000..f316c5047a3d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0958.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: time/clock leap day + query: data.generated.p = x + modules: + - | + package generated + + p := [__local0__, __local1__, __local2__] if { + __local3__ = 1582977600 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + time.clock(__local5__, __local6__) + [__local0__, __local1__, __local2__] = __local6__ + } + data: {} + want_result: + - x: + - 12 + - 0 + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0959.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0959.yaml new file mode 100644 index 000000000000..35be109ac94d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0959.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: time/clock too big + query: data.generated.p = x + modules: + - | + package generated + + p := [__local0__, __local1__, __local2__] if { + __local3__ = 1582977600 * 1000 + __local4__ = __local3__ * 1000 + __local5__ = __local4__ * 1000 + __local6__ = __local5__ * 1000 + time.clock(__local6__, __local7__) + [__local0__, __local1__, __local2__] = __local7__ + } + want_error_code: eval_builtin_error + want_error: timestamp too big + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0960.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0960.yaml new file mode 100644 index 000000000000..11b4606cf424 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0960.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: time/weekday + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + time.weekday(1517832000000000000, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: Monday diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0961.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0961.yaml new file mode 100644 index 000000000000..aaed084d8e82 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0961.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: time/weekday + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + time.weekday(1517918400000000000, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: Tuesday diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0962.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0962.yaml new file mode 100644 index 000000000000..2db7c0b6b594 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0962.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: time/weekday + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + time.weekday(1518004800000000000, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: Wednesday diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0963.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0963.yaml new file mode 100644 index 000000000000..8c3d47db651a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0963.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: time/weekday + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + time.weekday(1518091200000000000, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: Thursday diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0964.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0964.yaml new file mode 100644 index 000000000000..23bac44513a3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0964.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: time/weekday + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + time.weekday(1518177600000000000, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: Friday diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0965.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0965.yaml new file mode 100644 index 000000000000..fad30d94fe84 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0965.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: time/weekday + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + time.weekday(1518264000000000000, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: Saturday diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0966.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0966.yaml new file mode 100644 index 000000000000..657c757dacb4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0966.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: time/weekday + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + time.weekday(1518350400000000000, __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: Sunday diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0967.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0967.yaml new file mode 100644 index 000000000000..2b1670bde81a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0967.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: time/weekday too big + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + __local1__ = 1582977600 * 1000 + __local2__ = __local1__ * 1000 + __local3__ = __local2__ * 1000 + __local4__ = __local3__ * 1000 + time.weekday(__local4__, __local5__) + __local0__ = __local5__ + } + want_error_code: eval_builtin_error + want_error: timestamp too big + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0968.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0968.yaml new file mode 100644 index 000000000000..13dd21a1f534 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0968.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: time/add_date year month day + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + time.add_date(1585852421593912000, 3, 9, 12, __local1__) + __local0__ = __local1__ + } + want_result: + - x: 1705257221593912000 + - note: time/add_date too large result + query: data.generated.p = x + modules: + - | + package generated + + p := ns if { + time.add_date(0, 2262, 1, 1, ns) + } + want_error_code: eval_builtin_error + want_error: time outside of valid range + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0969.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0969.yaml new file mode 100644 index 000000000000..8ca4b62a13ba --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0969.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: time/add_date negative values + query: data.generated.p = x + modules: + - | + package generated + + p := __local0__ if { + time.add_date(1585852421593912000, -1, -1, -1, __local1__) + __local0__ = __local1__ + } + want_result: + - x: 1551465221593912000 + - note: time/add_date too small result + query: data.generated.p = x + modules: + - | + package generated + + p := ns if { + time.add_date(-9223372036854775808, 0, 0, -1, ns) + } + want_error_code: eval_builtin_error + want_error: time outside of valid range + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0970.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0970.yaml new file mode 100644 index 000000000000..661fb62c0ad9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0970.yaml @@ -0,0 +1,66 @@ +--- +cases: + - note: time/diff + query: | + data.test.a = minute_second; data.test.b = different_tz; data.test.c = leap_year; data.test.d = not_leap_year; data.test.e = leap_year_one_day + modules: + - | + package test + + layout := "2006-01-02" + + a := minute_second if { + minute_second := time.diff(time.now_ns() + (((61 * 1000) * 1000) * 1000), time.now_ns()) + } + + b := different_tz if { + different_tz := time.diff([time.now_ns() + (((60 * 1000) * 1000) * 1000), "UTC"], [time.now_ns(), "Asia/Shanghai"]) + } + + c := leap_year if { + leap_year := time.diff(time.parse_ns(layout, "2020-02-02"), time.parse_ns(layout, "2020-03-01")) + } + + d := not_leap_year if { + not_leap_year := time.diff(time.parse_ns(layout, "2021-02-02"), time.parse_ns(layout, "2021-03-01")) + } + + e := leap_year_one_day if { + leap_year_one_day := time.diff(time.parse_ns(layout, "2004-02-29"), time.parse_ns(layout, "2005-03-01")) + } + want_result: + - different_tz: + - 0 + - 0 + - 0 + - 0 + - 1 + - 0 + leap_year: + - 0 + - 0 + - 28 + - 0 + - 0 + - 0 + leap_year_one_day: + - 1 + - 0 + - 1 + - 0 + - 0 + - 0 + minute_second: + - 0 + - 0 + - 0 + - 0 + - 1 + - 1 + not_leap_year: + - 0 + - 0 + - 27 + - 0 + - 0 + - 0 diff --git a/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0971.yaml b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0971.yaml new file mode 100644 index 000000000000..3b6f39dba1ce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/time/test-time-0971.yaml @@ -0,0 +1,33 @@ +--- +cases: + - note: time/format + query: | + data.test.a = no_timezone; data.test.b = with_timezone; data.test.c = with_layout; data.test.d = with_constant + modules: + - | + package test + + time_ns := 1670006453141828752 + + a := time.format(time_ns) + + b := time.format([time_ns, "Asia/Kolkata"]) + + c := time.format([time_ns, "Asia/Kolkata", "Mon Jan 02 15:04:05 -0700 2006"]) + + d := time.format([time_ns, "Asia/Kolkata", "RFC1123Z"]) + want_result: + - no_timezone: "2022-12-02T18:40:53.141828752Z" + with_constant: Sat, 03 Dec 2022 00:10:53 +0530 + with_layout: Sat Dec 03 00:10:53 +0530 2022 + with_timezone: "2022-12-03T00:10:53.141828752+05:30" + - note: time/format too big + query: data.generated.p = x + modules: + - | + package generated + + p := time.format(1582977600 * 10e12) + want_error_code: eval_builtin_error + want_error: timestamp too big + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/topdowndynamicdispatch/test-topdowndynamicdispatch-1068.yaml b/third_party/opa/v1/test/cases/testdata/v1/topdowndynamicdispatch/test-topdowndynamicdispatch-1068.yaml new file mode 100644 index 000000000000..45479ddf4956 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/topdowndynamicdispatch/test-topdowndynamicdispatch-1068.yaml @@ -0,0 +1,33 @@ +--- +cases: + - note: topdowndynamicdispatch/dynamic dispatch + query: data = x + modules: + - | + package animals + + dog := "woof" + + cat := "meow" + - | + package dynamic + + sound := __local0__ if { + true + __local1__ = data.dynamic.animal + __local0__ = data.animals[__local1__] + } + + animal := "dog" if { + 2 > 1 + } + data: {} + input_term: "{}" + want_result: + - x: + animals: + cat: meow + dog: woof + dynamic: + animal: dog + sound: woof diff --git a/third_party/opa/v1/test/cases/testdata/v1/trim/test-trim-0362.yaml b/third_party/opa/v1/test/cases/testdata/v1/trim/test-trim-0362.yaml new file mode 100644 index 000000000000..b8917249dc30 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/trim/test-trim-0362.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: trim/trims '!¡' from string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + trim("¡¡¡foo, bar!!!", "!¡", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - foo, bar + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/trim/test-trim-0363.yaml b/third_party/opa/v1/test/cases/testdata/v1/trim/test-trim-0363.yaml new file mode 100644 index 000000000000..12b96ae13bce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/trim/test-trim-0363.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: trim/trims nothing from string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + trim("¡¡¡foo, bar!!!", "i", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - ¡¡¡foo, bar!!! + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/trimleft/test-trimleft-0364.yaml b/third_party/opa/v1/test/cases/testdata/v1/trimleft/test-trimleft-0364.yaml new file mode 100644 index 000000000000..b32527f3f200 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/trimleft/test-trimleft-0364.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: trimleft/trims leading '!¡' from string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + trim_left("¡¡¡foo, bar!!!", "!¡", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - foo, bar!!! + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/trimleft/test-trimleft-0365.yaml b/third_party/opa/v1/test/cases/testdata/v1/trimleft/test-trimleft-0365.yaml new file mode 100644 index 000000000000..e27ed4faf4d7 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/trimleft/test-trimleft-0365.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: trimleft/trims nothing from string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + trim_left("!!!foo, bar¡¡¡", "¡", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - "!!!foo, bar¡¡¡" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/trimprefix/test-trimprefix-0366.yaml b/third_party/opa/v1/test/cases/testdata/v1/trimprefix/test-trimprefix-0366.yaml new file mode 100644 index 000000000000..fd45c3427d19 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/trimprefix/test-trimprefix-0366.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: trimprefix/trims prefix '!¡' from string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + trim_prefix("¡¡¡foo, bar!!!", "¡¡¡foo", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - ", bar!!!" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/trimprefix/test-trimprefix-0367.yaml b/third_party/opa/v1/test/cases/testdata/v1/trimprefix/test-trimprefix-0367.yaml new file mode 100644 index 000000000000..9f042f326558 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/trimprefix/test-trimprefix-0367.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: trimprefix/trims nothing from string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + trim_prefix("¡¡¡foo, bar!!!", "¡¡¡bar", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - ¡¡¡foo, bar!!! + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/trimright/test-trimright-0368.yaml b/third_party/opa/v1/test/cases/testdata/v1/trimright/test-trimright-0368.yaml new file mode 100644 index 000000000000..9a4073c06cac --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/trimright/test-trimright-0368.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: trimright/trims trailing '!¡' from string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + trim_right("¡¡¡foo, bar!!!", "!¡", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - ¡¡¡foo, bar + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/trimright/test-trimright-0369.yaml b/third_party/opa/v1/test/cases/testdata/v1/trimright/test-trimright-0369.yaml new file mode 100644 index 000000000000..f07883b832ce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/trimright/test-trimright-0369.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: trimright/trims nothing from string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + trim_right("!!!foo, bar¡¡¡", "!", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - "!!!foo, bar¡¡¡" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/trimspace/test-trimspace-0372.yaml b/third_party/opa/v1/test/cases/testdata/v1/trimspace/test-trimspace-0372.yaml new file mode 100644 index 000000000000..ec0e54a084b9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/trimspace/test-trimspace-0372.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: trimspace/trims all leading and trailing white space from string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + trim_space(" \t\n foo, bar \n\t\r\n", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - foo, bar + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/trimspace/test-trimspace-0373.yaml b/third_party/opa/v1/test/cases/testdata/v1/trimspace/test-trimspace-0373.yaml new file mode 100644 index 000000000000..e225ef94070d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/trimspace/test-trimspace-0373.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: trimspace/trims nothing from string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + trim_space("foo, bar", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - foo, bar + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/trimsuffix/test-trimsuffix-0370.yaml b/third_party/opa/v1/test/cases/testdata/v1/trimsuffix/test-trimsuffix-0370.yaml new file mode 100644 index 000000000000..4ba62adcce44 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/trimsuffix/test-trimsuffix-0370.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: trimsuffix/trims suffix '!¡' from string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + trim_suffix("¡¡¡foo, bar!!!", ", bar!!!", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - ¡¡¡foo + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/trimsuffix/test-trimsuffix-0371.yaml b/third_party/opa/v1/test/cases/testdata/v1/trimsuffix/test-trimsuffix-0371.yaml new file mode 100644 index 000000000000..a8bb8ee8cea5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/trimsuffix/test-trimsuffix-0371.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: trimsuffix/trims nothing from string + query: data.generated.p = x + modules: + - | + package generated + + p contains __local0__ if { + trim_suffix("¡¡¡foo, bar!!!", ", foo!!!", __local1__) + __local0__ = __local1__ + } + data: {} + want_result: + - x: + - ¡¡¡foo, bar!!! + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/type/test-regressions.yaml b/third_party/opa/v1/test/cases/testdata/v1/type/test-regressions.yaml new file mode 100644 index 000000000000..0416d91a609a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/type/test-regressions.yaml @@ -0,0 +1,88 @@ +--- +cases: + - note: regression/partial-object override, different key type, query + query: data.test.p.foo = x + modules: + - | + package test + + p[k] := v if { + v := ["a", "b", "c"][k] + } + + p["foo"] := "bar" + want_result: + - x: bar + - note: regression/partial-object override, different key type, referenced in other rule + query: data.test.q = x + modules: + - | + package test + + p[k] := v if { + v := ["a", "b", "c"][k] + } + + p["foo"] := "bar" + + q contains x if { + x := p[_] + x == "bar" + } + want_result: + - x: + - bar + - note: regression/dynamic object to static object comparison (https://github.com/open-policy-agent/opa/issues/6138) + query: data.test.compare = x + modules: + - | + package test + + l := ["a", "b", "c"] + + obj[k] := v if { + v := ["a", "b", "c"][k] + k < 3 + } + + obj[k] := v if { + v := input.m[k] + } + + obj["foo"] := "bar" if input.foo + + obj["baz"] := true if input.baz + + compare if { + # Comparison with static object that doesn't contain "optional" key. + obj == { + 0: "a", + 1: "b", + 2: "c", + } + + obj == { + 0: "a", + 1: "b", + 2: "c", + "foo": "bar", + } with input.foo as true + + obj == { + 0: "a", + 1: "b", + 2: "c", + "baz": true, + } with input.baz as true + + obj == { + 0: "a", + 1: "b", + 2: "c", + 3: "d", + 4: "e", + 100: "f", + } with input.m as {3: "d", 4: "e", 100: "f"} + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0828.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0828.yaml new file mode 100644 index 000000000000..ab314c9654fe --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0828.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: typebuiltin/is_number + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + is_number(-42.0, x) + is_number(0, y) + is_number(100.1, z) + } + data: {} + want_result: + - x: + - true + - true + - true diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0829.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0829.yaml new file mode 100644 index 000000000000..cadd8056b63c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0829.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typebuiltin/is_number + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + is_number(null, x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0830.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0830.yaml new file mode 100644 index 000000000000..4cca992518ed --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0830.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typebuiltin/is_number + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + is_number(false, x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0831.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0831.yaml new file mode 100644 index 000000000000..09b347f55a35 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0831.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: typebuiltin/is_number + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + arr = [true, 1] + arr[_] = x + is_number(x) + } + data: {} + want_result: + - x: + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0832.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0832.yaml new file mode 100644 index 000000000000..a853c0af1fdb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0832.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: typebuiltin/is_string + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y, z] if { + is_string("Hello", x) + is_string("There", y) + is_string("OPA", z) + } + data: {} + want_result: + - x: + - true + - true + - true diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0833.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0833.yaml new file mode 100644 index 000000000000..2f45cceb0f37 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0833.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typebuiltin/is_string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + is_string(null, x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0834.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0834.yaml new file mode 100644 index 000000000000..67743f809520 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0834.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typebuiltin/is_string + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + is_string(false, x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0835.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0835.yaml new file mode 100644 index 000000000000..d5b4e64e82c5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0835.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: typebuiltin/is_string + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + arr = [true, 1, "Hey"] + arr[_] = x + is_string(x) + } + data: {} + want_result: + - x: + - Hey + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0836.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0836.yaml new file mode 100644 index 000000000000..07a46abfc3b4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0836.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: typebuiltin/is_boolean + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y] if { + is_boolean(true, x) + is_boolean(false, y) + } + data: {} + want_result: + - x: + - true + - true diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0837.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0837.yaml new file mode 100644 index 000000000000..04a2c8694e58 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0837.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typebuiltin/is_boolean + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + is_boolean(null, x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0838.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0838.yaml new file mode 100644 index 000000000000..a14295358361 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0838.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typebuiltin/is_boolean + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + is_boolean("Hello", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0839.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0839.yaml new file mode 100644 index 000000000000..29ce0c6969fd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0839.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: typebuiltin/is_boolean + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + arr = [false, 1, "Hey"] + arr[_] = x + is_boolean(x) + } + data: {} + want_result: + - x: + - false + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0840.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0840.yaml new file mode 100644 index 000000000000..311db3c4f9b3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0840.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: typebuiltin/is_array + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y] if { + is_array([1, 2, 3], x) + is_array(["a", "b"], y) + } + data: {} + want_result: + - x: + - true + - true diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0841.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0841.yaml new file mode 100644 index 000000000000..2f708ff5d565 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0841.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typebuiltin/is_array + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + is_array({1, 2, 3}, x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0842.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0842.yaml new file mode 100644 index 000000000000..f0d536d1d578 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0842.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: typebuiltin/is_set + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y] if { + is_set({1, 2, 3}, x) + is_set({"a", "b"}, y) + } + data: {} + want_result: + - x: + - true + - true diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0843.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0843.yaml new file mode 100644 index 000000000000..98d55ffdf310 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0843.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typebuiltin/is_set + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + is_set([1, 2, 3], x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0844.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0844.yaml new file mode 100644 index 000000000000..ab8662e8788c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0844.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: typebuiltin/is_object + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local0__ = {"foo": yy | yy = 1} + is_object(__local0__, x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0845.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0845.yaml new file mode 100644 index 000000000000..8e38c0145bf2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0845.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typebuiltin/is_object + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + is_object("foo", x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0846.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0846.yaml new file mode 100644 index 000000000000..beb09cb1f0e2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0846.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typebuiltin/is_null + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + is_null(null, x) + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0847.yaml b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0847.yaml new file mode 100644 index 000000000000..c67cf8c5c58c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typebuiltin/test-typebuiltin-0847.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typebuiltin/is_null + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + is_null(true, x) + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0848.yaml b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0848.yaml new file mode 100644 index 000000000000..58627a6fe9b4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0848.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typenamebuiltin/type_name + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + type_name(null, x) + } + data: {} + want_result: + - x: "null" diff --git a/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0849.yaml b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0849.yaml new file mode 100644 index 000000000000..f10fb02f7946 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0849.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typenamebuiltin/type_name + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + type_name(true, x) + } + data: {} + want_result: + - x: boolean diff --git a/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0850.yaml b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0850.yaml new file mode 100644 index 000000000000..9b833dd1f434 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0850.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typenamebuiltin/type_name + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + type_name(100, x) + } + data: {} + want_result: + - x: number diff --git a/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0851.yaml b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0851.yaml new file mode 100644 index 000000000000..c26b1370eaf8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0851.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typenamebuiltin/type_name + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + type_name("Hello", x) + } + data: {} + want_result: + - x: string diff --git a/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0852.yaml b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0852.yaml new file mode 100644 index 000000000000..cfbdcc26c866 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0852.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typenamebuiltin/type_name + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + type_name([1, 2, 3], x) + } + data: {} + want_result: + - x: array diff --git a/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0853.yaml b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0853.yaml new file mode 100644 index 000000000000..06fbb5e5dd98 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0853.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: typenamebuiltin/type_name + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + type_name({1, 2, 3}, x) + } + data: {} + want_result: + - x: set diff --git a/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0854.yaml b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0854.yaml new file mode 100644 index 000000000000..0ba48d03a8c2 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/typenamebuiltin/test-typenamebuiltin-0854.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: typenamebuiltin/type_name + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + __local0__ = {"foo": yy | yy = 1} + type_name(__local0__, x) + } + data: {} + want_result: + - x: object diff --git a/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0599.yaml b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0599.yaml new file mode 100644 index 000000000000..b248d3b4f06a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0599.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: undos/array-type + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + arr = [[1, [2]], [1, null], [2, [2]]] + [x, [2]] = arr[_] + } + data: {} + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0600.yaml b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0600.yaml new file mode 100644 index 000000000000..03cefce27417 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0600.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: undos/arrays-element + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + arr = [[1, 2], [1, null], [2, 2]] + arr[_] = [x, 2] + } + data: {} + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0601.yaml b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0601.yaml new file mode 100644 index 000000000000..38f2d202a4f1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0601.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: undos/arrays-length + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + arr = [[1, [2]], [1, []], [2, [2]]] + arr[_] = [x, [2]] + } + data: {} + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0602.yaml b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0602.yaml new file mode 100644 index 000000000000..f7d700dff95b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0602.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: undos/array-ref-element + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.arr_ref + arr = [[1, 2], __local0__, [2, 2]] + arr[_] = [x, 2] + } + data: + arr_ref: + - 1 + - null + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0603.yaml b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0603.yaml new file mode 100644 index 000000000000..66b6f061888d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0603.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: undos/object-type + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + obj = {"a": {"x": 1, "y": {"v": 2}}, "b": {"x": 1, "y": null}, "c": {"x": 2, "y": {"v": 2}}} + {"x": x, "y": {"v": 2}} = obj[_] + } + data: {} + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0604.yaml b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0604.yaml new file mode 100644 index 000000000000..d6b8503f9761 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0604.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: undos/objects-element + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + obj = {"a": {"x": 1, "y": 2}, "b": {"x": 1, "y": null}, "c": {"x": 2, "y": 2}} + obj[_] = {"x": x, "y": 2} + } + data: {} + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0605.yaml b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0605.yaml new file mode 100644 index 000000000000..99eb6ef9d676 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0605.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: undos/objects-length + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + obj = {"a": {"x": 1, "y": {"v": 2}}, "b": {"x": 1, "y": {}}, "c": {"x": 2, "y": {"v": 2}}} + obj[_] = {"x": x, "y": {"v": 2}} + } + data: {} + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0606.yaml b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0606.yaml new file mode 100644 index 000000000000..55cbf7a85448 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0606.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: undos/object-ref-element + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.obj_ref + obj = {"a": {"x": 1, "y": 2}, "b": __local0__, "c": {"x": 2, "y": 2}} + obj[_] = {"x": x, "y": 2} + } + data: + obj_ref: + "true": null + x: 1 + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0607.yaml b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0607.yaml new file mode 100644 index 000000000000..b69eedbb3b66 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/undos/test-undos-0607.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: undos/object-ref-missing-key + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.obj_ref_missing_key + obj = {"a": {"x": 1, "y": 2}, "b": __local0__, "c": {"x": 2, "y": 2}} + obj[_] = {"x": x, "y": 2} + } + data: + obj_ref_missing_key: + x: 3 + z: 2 + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0357.yaml b/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0357.yaml new file mode 100644 index 000000000000..0b462842ff13 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0357.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: union/union_0_sets + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + union(set(), x) + } + data: {} + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0358.yaml b/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0358.yaml new file mode 100644 index 000000000000..27b1d37264c9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0358.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: union/union_2_sets + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + union({set(), {1, 2}}, x) + } + data: {} + want_result: + - x: + - 1 + - 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0359.yaml b/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0359.yaml new file mode 100644 index 000000000000..d1deb10fbbd4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0359.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: union/union_2_sets + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + s1 = {1, 2, 3} + s2 = {2} + union({s1, s2}, x) + } + data: {} + want_result: + - x: + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0360.yaml b/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0360.yaml new file mode 100644 index 000000000000..e1441747c625 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0360.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: union/union_3_sets + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + s1 = {1, 2, 3} + s2 = {2, 3, 4} + s3 = {4, 5, 6} + union({s1, s2, s3}, x) + } + data: {} + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0361.yaml b/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0361.yaml new file mode 100644 index 000000000000..2bb4c1590160 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/union/test-union-0361.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: union/union_4_sets + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + s1 = {"a", "b", "c", "d"} + s2 = {"b", "c", "d"} + s3 = {"c", "d"} + s4 = {"d"} + union({s1, s2, s3, s4}, x) + } + data: {} + want_result: + - x: + - a + - b + - c + - d + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/units/test-issue-4856.yaml b/third_party/opa/v1/test/cases/testdata/v1/units/test-issue-4856.yaml new file mode 100644 index 000000000000..a7e6a4f4b6c5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/units/test-issue-4856.yaml @@ -0,0 +1,35 @@ +--- +cases: + - note: units_parse/exact comparison - regression case 1 + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("500m") == 0.5 + } + want_result: + - x: true + - note: units_parse/exact comparison - regression case 2 + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("0.0005K") == 0.5 + } + want_result: + - x: true + - note: units_parse/exact comparison - regression case 3 + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("0.0000005M") == 0.5 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-bytes-comparisons.yaml b/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-bytes-comparisons.yaml new file mode 100644 index 000000000000..2e4aaa4f4900 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-bytes-comparisons.yaml @@ -0,0 +1,112 @@ +--- +cases: + - note: units_parse_bytes/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("8kb") > units.parse_bytes("7kb") + } + want_result: + - x: true + - note: units_parse_bytes/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("8gb") > units.parse_bytes("8mb") + } + want_result: + - x: true + - note: units_parse_bytes/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("1234kb") < units.parse_bytes("1gb") + } + want_result: + - x: true + - note: units_parse_bytes/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("1024") == units.parse_bytes("1KiB") + } + want_result: + - x: true + - note: units_parse_bytes/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("2MiB") == units.parse_bytes("2097152") + } + want_result: + - x: true + - note: units_parse_bytes/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("3MiB") > units.parse_bytes("3MB") + } + want_result: + - x: true + - note: units_parse_bytes/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("2MiB") == units.parse_bytes("2Mi") + } + want_result: + - x: true + - note: units_parse_bytes/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("4Mi") > units.parse_bytes("4M") + } + want_result: + - x: true + - note: units_parse_bytes/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("4.1Mi") > units.parse_bytes("4Mi") + } + want_result: + - x: true + - note: units_parse_bytes/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("128Gi") == units.parse_bytes("137438953472") + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-bytes-errors.yaml b/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-bytes-errors.yaml new file mode 100644 index 000000000000..5afa52139755 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-bytes-errors.yaml @@ -0,0 +1,86 @@ +--- +cases: + - note: units_parse_bytes/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("") + } + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: no byte amount provided" + strict_error: true + - note: units_parse_bytes/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("GB") + } + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: no byte amount provided" + strict_error: true + - note: units_parse_bytes/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("foo") + } + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: no byte amount provided" + strict_error: true + - note: units_parse_bytes/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("0.0.0") + } + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: could not parse byte amount to a number" + strict_error: true + - note: units_parse_bytes/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes(".5.2") + } + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: could not parse byte amount to a number" + strict_error: true + - note: units_parse_bytes/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100 kb") + } + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: spaces not allowed in resource strings" + strict_error: true + - note: units_parse_bytes/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes(" 327MiB ") + } + want_error_code: eval_builtin_error + want_error: "units.parse_bytes: spaces not allowed in resource strings" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-bytes.yaml b/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-bytes.yaml new file mode 100644 index 000000000000..b11f06b0c16c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-bytes.yaml @@ -0,0 +1,530 @@ +--- +cases: + - note: units_parse_bytes/removes quotes and lowercases string + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("\"100TIB\"") == 109951162777600 + } + want_result: + - x: true + - note: units_parse_bytes/zero + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("0") == 0 + } + want_result: + - x: true + - note: units_parse_bytes/zero float + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("0.0") == 0 + } + want_result: + - x: true + - note: units_parse_bytes/zero bare float + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes(".0") == 0 + } + want_result: + - x: true + - note: units_parse_bytes/raw number + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("12345") == 12345 + } + want_result: + - x: true + - note: units_parse_bytes/10 kilobytes uppercase + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("10KB") == 10000 + } + want_result: + - x: true + - note: units_parse_bytes/10 KiB uppercase + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("10KIB") == 10240 + } + want_result: + - x: true + - note: units_parse_bytes/10 KB lowercase + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("10kb") == 10000 + } + want_result: + - x: true + - note: units_parse_bytes/10 KiB mixed case + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("10Kib") == 10240 + } + want_result: + - x: true + - note: units_parse_bytes/200 megabytes as mb + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("200mb") == 200000000 + } + want_result: + - x: true + - note: units_parse_bytes/300 GiB + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("300GiB") == 322122547200 + } + want_result: + - x: true + - note: units_parse_bytes/1.1 KB floating point + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("1.1KB") == 1100 + } + want_result: + - x: true + - note: units_parse_bytes/1.1 KiB floating point rounded + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("1.1KiB") == 1126 + } + want_result: + - x: true + - note: units_parse_bytes/.5 KB bare floating point + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes(".5KB") == 500 + } + want_result: + - x: true + - note: units_parse_bytes/100 kilobytes as k + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100k") == 100000 + } + want_result: + - x: true + - note: units_parse_bytes/100 kilobytes as kb + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100kb") == 100000 + } + want_result: + - x: true + - note: units_parse_bytes/100 kibibytes as ki + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100ki") == 102400 + } + want_result: + - x: true + - note: units_parse_bytes/100 kibibytes as kib + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100kib") == 102400 + } + want_result: + - x: true + - note: units_parse_bytes/100 megabytes as m + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100m") == 100000000 + } + want_result: + - x: true + - note: units_parse_bytes/100 megabytes as mb + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100mb") == 100000000 + } + want_result: + - x: true + - note: units_parse_bytes/100 mebibytes as mi + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100mi") == 104857600 + } + want_result: + - x: true + - note: units_parse_bytes/100 mebibytes as mib + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100mib") == 104857600 + } + want_result: + - x: true + - note: units_parse_bytes/100 gigabytes as g + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100g") == 100000000000 + } + want_result: + - x: true + - note: units_parse_bytes/100 gigabytes as gb + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100gb") == 100000000000 + } + want_result: + - x: true + - note: units_parse_bytes/100 gibibytes as gi + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100gi") == 107374182400 + } + want_result: + - x: true + - note: units_parse_bytes/100 gibibytes as gib + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100gib") == 107374182400 + } + want_result: + - x: true + - note: units_parse_bytes/100 terabytes as t + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100t") == 100000000000000 + } + want_result: + - x: true + - note: units_parse_bytes/100 terabytes as tb + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100tb") == 100000000000000 + } + want_result: + - x: true + - note: units_parse_bytes/100 tebibytes as ti + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100ti") == 109951162777600 + } + want_result: + - x: true + - note: units_parse_bytes/100 tebibytes as tib + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100tib") == 109951162777600 + } + want_result: + - x: true + - note: units_parse_bytes/100 petabytes as p + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100p") == 100000000000000000 + } + want_result: + - x: true + - note: units_parse_bytes/100 petabytes as pb + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100pb") == 100000000000000000 + } + want_result: + - x: true + - note: units_parse_bytes/100 pebibytes as pi + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100pi") == 112589990684262400 + } + want_result: + - x: true + - note: units_parse_bytes/100 pebibytes as pib + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("100pib") == 112589990684262400 + } + want_result: + - x: true + - note: units_parse_bytes/10 etabytes as e + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("10e") == 10000000000000000000 + } + want_result: + - x: true + - note: units_parse_bytes/10 etabytes as eb + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("10eb") == 10000000000000000000 + } + want_result: + - x: true + - note: units_parse_bytes/10 ebibytes as ei + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("10ei") == 11529215046068469760 + } + want_result: + - x: true + - note: units_parse_bytes/10 ebibytes as eib + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("10eib") == 11529215046068469760 + } + want_result: + - x: true + - note: units_parse_bytes/scientific notation with KB + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("1e3KB") == 1000000 + } + want_result: + - x: true + - note: units_parse_bytes/uppercase scientific notation with MiB + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("3.2E2MiB") == 335544320 + } + want_result: + - x: true + - note: units_parse_bytes/mixed case scientific notation with GiB + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("4.5e1GiB") == 48318382080 + } + want_result: + - x: true + - note: units_parse_bytes/scientific notation without unit + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("5e6") == 5000000 + } + want_result: + - x: true + - note: units_parse_bytes/scientific notation with negative exponent and KB + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("1e-2KB") == 10 + } + want_result: + - x: true + - note: units_parse_bytes/uppercase scientific notation with GB + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("6.4E1GB") == 64000000000 + } + want_result: + - x: true + - note: units_parse_bytes/mixed case scientific notation with TiB + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("7.5e1TiB") == 82463372083200 + } + want_result: + - x: true + - note: units_parse_bytes/scientific notation with lowercase MiB + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("5e2MiB") == 524288000 + } + want_result: + - x: true + - note: units_parse_bytes/invalid format with only E or e + query: data.test.p = x + modules: + - | + package test + + p if { + not units.parse_bytes("5E") == 5 + } + want_result: + - x: true + - note: units_parse_bytes/scientific notation with lowercase scientific notation and GiB + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse_bytes("2e2GiB") == 214748364800 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-units-comparisons.yaml b/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-units-comparisons.yaml new file mode 100644 index 000000000000..d5b59766308e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-units-comparisons.yaml @@ -0,0 +1,112 @@ +--- +cases: + - note: units_parse/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("8k") > units.parse("7k") + } + want_result: + - x: true + - note: units_parse/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("8g") > units.parse("8m") + } + want_result: + - x: true + - note: units_parse/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("1234k") < units.parse("1g") + } + want_result: + - x: true + - note: units_parse/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("1024") == units.parse("1Ki") + } + want_result: + - x: true + - note: units_parse/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("2Mi") == units.parse("2097152") + } + want_result: + - x: true + - note: units_parse/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("3Mi") > units.parse("3M") + } + want_result: + - x: true + - note: units_parse/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("2Mi") == units.parse("2Mi") + } + want_result: + - x: true + - note: units_parse/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("4Mi") > units.parse("4M") + } + want_result: + - x: true + - note: units_parse/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("4.1Mi") > units.parse("4Mi") + } + want_result: + - x: true + - note: units_parse/comparison + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("128Gi") == units.parse("137438953472") + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-units-errors.yaml b/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-units-errors.yaml new file mode 100644 index 000000000000..4ee238f44903 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-units-errors.yaml @@ -0,0 +1,86 @@ +--- +cases: + - note: units_parse/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("") + } + want_error_code: eval_builtin_error + want_error: "units.parse: no amount provided" + strict_error: true + - note: units_parse/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("G") + } + want_error_code: eval_builtin_error + want_error: "units.parse: no amount provided" + strict_error: true + - note: units_parse/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("foo") + } + want_error_code: eval_builtin_error + want_error: "units.parse: no amount provided" + strict_error: true + - note: units_parse/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("0.0.0") + } + want_error_code: eval_builtin_error + want_error: "units.parse: could not parse amount to a number" + strict_error: true + - note: units_parse/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse(".5.2") + } + want_error_code: eval_builtin_error + want_error: "units.parse: could not parse amount to a number" + strict_error: true + - note: units_parse/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100 k") + } + want_error_code: eval_builtin_error + want_error: "units.parse: spaces not allowed in resource strings" + strict_error: true + - note: units_parse/failure + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse(" 327Mi ") + } + want_error_code: eval_builtin_error + want_error: "units.parse: spaces not allowed in resource strings" + strict_error: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-units.yaml b/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-units.yaml new file mode 100644 index 000000000000..e091d56185e8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/units/test-parse-units.yaml @@ -0,0 +1,530 @@ +--- +cases: + - note: units_parse/removes quotes and lowercases string + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("\"100TI\"") == 109951162777600 + } + want_result: + - x: true + - note: units_parse/zero + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("0") == 0 + } + want_result: + - x: true + - note: units_parse/zero float + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("0.0") == 0 + } + want_result: + - x: true + - note: units_parse/zero bare float + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse(".0") == 0 + } + want_result: + - x: true + - note: units_parse/raw number + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("12345") == 12345 + } + want_result: + - x: true + - note: units_parse/10 kilo uppercase + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("10K") == 10000 + } + want_result: + - x: true + - note: units_parse/10 Ki uppercase + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("10KI") == 10240 + } + want_result: + - x: true + - note: units_parse/10 K lowercase + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("10k") == 10000 + } + want_result: + - x: true + - note: units_parse/10 Ki mixed case + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("10Ki") == 10240 + } + want_result: + - x: true + - note: units_parse/200 mega + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("200M") == 200000000 + } + want_result: + - x: true + - note: units_parse/300 Gi + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("300Gi") == 322122547200 + } + want_result: + - x: true + - note: units_parse/1.1 K floating point + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("1.1K") == 1100 + } + want_result: + - x: true + - note: units_parse/1.1 Ki floating point, not rounded + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("1.1Ki") == 1126.4 + } + want_result: + - x: true + - note: units_parse/.5 K bare floating point + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse(".5K") == 500 + } + want_result: + - x: true + - note: units_parse/100 kilo as k + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100k") == 100000 + } + want_result: + - x: true + - note: units_parse/100 kilo as K + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100K") == 100000 + } + want_result: + - x: true + - note: units_parse/100 kibi as ki + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100ki") == 102400 + } + want_result: + - x: true + - note: units_parse/100 kibi as Ki + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100Ki") == 102400 + } + want_result: + - x: true + - note: units_parse/100 milli as m + query: data.test.p = x + modules: + - | + package test + + p if { + round(units.parse("100m") * 1000) == 100 + } + want_result: + - x: true + - note: units_parse/100 mega as M + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100M") == 100000000 + } + want_result: + - x: true + - note: units_parse/100 mebi as mi + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100mi") == 104857600 + } + want_result: + - x: true + - note: units_parse/100 mebi as Mi + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100Mi") == 104857600 + } + want_result: + - x: true + - note: units_parse/100 giga as g + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100g") == 100000000000 + } + want_result: + - x: true + - note: units_parse/100 gibi as gi + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100gi") == 107374182400 + } + want_result: + - x: true + - note: units_parse/100 tera as t + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100t") == 100000000000000 + } + want_result: + - x: true + - note: units_parse/100 tera as T + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100T") == 100000000000000 + } + want_result: + - x: true + - note: units_parse/100 tebi as ti + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100ti") == 109951162777600 + } + want_result: + - x: true + - note: units_parse/100 tebi as Ti + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100Ti") == 109951162777600 + } + want_result: + - x: true + - note: units_parse/100 peta as p + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100p") == 100000000000000000 + } + want_result: + - x: true + - note: units_parse/100 peta as P + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100P") == 100000000000000000 + } + want_result: + - x: true + - note: units_parse/100 pebi as pi + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100pi") == 112589990684262400 + } + want_result: + - x: true + - note: units_parse/100 pebi as Pi + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("100Pi") == 112589990684262400 + } + want_result: + - x: true + - note: units_parse/10 eta as e + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("10e") == 10000000000000000000 + } + want_result: + - x: true + - note: units_parse/10 eta as E + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("10E") == 10000000000000000000 + } + want_result: + - x: true + - note: units_parse/10 ebi as ei + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("10ei") == 11529215046068469760 + } + want_result: + - x: true + - note: units_parse/10 ebi as Ei + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("10Ei") == 11529215046068469760 + } + want_result: + - x: true + - note: units_parse/1e10 lowercase scientific notation without unit + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("1e10") == 10000000000 + } + want_result: + - x: true + - note: units_parse/3.2E4 uppercase scientific notation without unit + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("3.2E4") == 32000 + } + want_result: + - x: true + - note: units_parse/2.5e3 mixed case scientific notation with K + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("2.5e3K") == 2500000 + } + want_result: + - x: true + - note: units_parse/1e3M lowercase scientific notation with M + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("1e3M") == 1000000000 + } + want_result: + - x: true + - note: units_parse/4E2G uppercase scientific notation with G + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("4E2G") == 400000000000 + } + want_result: + - x: true + - note: units_parse/5e1Gi mixed case scientific notation with Gi + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("5e1Gi") == 53687091200 + } + want_result: + - x: true + - note: units_parse/1e-2 lowercase scientific notation with negative exponent + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("1e-2") == 0.01 + } + want_result: + - x: true + - note: units_parse/7.8E-1 uppercase scientific notation with negative exponent + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("7.8E-1") == 0.78 + } + want_result: + - x: true + - note: units_parse/6e3Mi mixed case scientific notation with binary Mi + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("6e3Mi") == 6291456000 + } + want_result: + - x: true + - note: units_parse/invalid notation with single E + query: data.test.p = x + modules: + - | + package test + + p if { + not units.parse("5E") == 5 + } + want_result: + - x: true + - note: units_parse/number without exponent or unit + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("42") == 42 + } + want_result: + - x: true + - note: units_parse/negative number in scientific notation with unit + query: data.test.p = x + modules: + - | + package test + + p if { + units.parse("-3.5E2m") == -0.35 + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/units/test-units-precision.yaml b/third_party/opa/v1/test/cases/testdata/v1/units/test-units-precision.yaml new file mode 100644 index 000000000000..7e659c3e0aba --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/units/test-units-precision.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: units_parse/no decimal places for integers + query: data.test.p = x + modules: + - | + package test + + p if { + json.marshal(units.parse("1G")) == json.marshal(1000000000) + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0939.yaml b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0939.yaml new file mode 100644 index 000000000000..effe05be8f63 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0939.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: urlbuiltins/encode + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + urlquery.encode("a=b+1", x) + } + data: {} + want_result: + - x: a%3Db%2B1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0940.yaml b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0940.yaml new file mode 100644 index 000000000000..9198f718a640 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0940.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: urlbuiltins/encode empty + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + urlquery.encode("", x) + } + data: {} + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0941.yaml b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0941.yaml new file mode 100644 index 000000000000..553e8b070fed --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0941.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: urlbuiltins/decode + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + urlquery.decode("a%3Db%2B1", x) + } + data: {} + want_result: + - x: a=b+1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0942.yaml b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0942.yaml new file mode 100644 index 000000000000..1823fd126533 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0942.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: urlbuiltins/encode_object empty + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + urlquery.encode_object({}, x) + } + data: {} + want_result: + - x: "" diff --git a/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0943.yaml b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0943.yaml new file mode 100644 index 000000000000..5b45027c1ed5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0943.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: urlbuiltins/encode_object strings + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + urlquery.encode_object({"a": "b", "c": "d"}, x) + } + data: {} + want_result: + - x: a=b&c=d diff --git a/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0944.yaml b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0944.yaml new file mode 100644 index 000000000000..58e7e2501465 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0944.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: urlbuiltins/encode_object escape + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + urlquery.encode_object({"a": "c=b+1"}, x) + } + data: {} + want_result: + - x: a=c%3Db%2B1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0945.yaml b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0945.yaml new file mode 100644 index 000000000000..00801a9f32d3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0945.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: urlbuiltins/encode_object array + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + urlquery.encode_object({"a": ["b+1", "c+2"]}, x) + } + data: {} + want_result: + - x: a=b%2B1&a=c%2B2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0946.yaml b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0946.yaml new file mode 100644 index 000000000000..4c36acff765f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-0946.yaml @@ -0,0 +1,14 @@ +--- +cases: + - note: urlbuiltins/encode_object set + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + urlquery.encode_object({"a": {"b+1"}}, x) + } + data: {} + want_result: + - x: a=b%2B1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-1076.yaml b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-1076.yaml new file mode 100644 index 000000000000..71ae4713e747 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/urlbuiltins/test-urlbuiltins-1076.yaml @@ -0,0 +1,44 @@ +--- +cases: + - note: urlbuiltins/decode_object multiple + query: data.decode_object.p = x + modules: + - | + package decode_object + + p := x if { + x = urlquery.decode_object("a=value_a1&b=value_b&a=value_a2") + } + want_result: + - x: + a: + - value_a1 + - value_a2 + b: + - value_b + - note: urlbuiltins/decode_object empty parameter + query: data.decode_object.p = x + modules: + - | + package decode_object + + p := x if { + x = urlquery.decode_object("a=value_a1&b") + } + want_result: + - x: + a: + - value_a1 + b: + - "" + - note: urlbuiltins/decode_object empty string + query: data.decode_object.p = x + modules: + - | + package decode_object + + p := x if { + x = urlquery.decode_object("") + } + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/uuid/test-uuid-input-formats.yaml b/third_party/opa/v1/test/cases/testdata/v1/uuid/test-uuid-input-formats.yaml new file mode 100644 index 000000000000..fc148568fc46 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/uuid/test-uuid-input-formats.yaml @@ -0,0 +1,50 @@ +--- +cases: + - note: uuid-parse/positive-v4-braces + query: data.test.p = x + modules: + - | + package test + + p := uuid.parse(input.userid) + data: {} + input: + userid: "{00000000-0000-4000-8000-000000000000}" + want_result: + - x: + variant: RFC4122 + version: 4 + - note: uuid-parse/positive-v2-urn + query: data.test.p = x + modules: + - | + package test + + p := uuid.parse(input.userid) + data: {} + input: + userid: urn:uuid:000003e8-48b9-21ee-b200-325096b39f47 + want_result: + - x: + clocksequence: 12800 + domain: Person + id: 1000 + macvariables: local:unicast + nodeid: 32-50-96-b3-9f-47 + time: 1693566990121469600 + variant: RFC4122 + version: 2 + - note: uuid-parse/positive-v3-no-dashes + query: data.test.p = x + modules: + - | + package test + + p := uuid.parse(input.userid) + data: {} + input: + userid: 6bea8ef2d3d33cd184e09bab06a52ece + want_result: + - x: + variant: RFC4122 + version: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/uuid/test-uuid-parse-rule.yaml b/third_party/opa/v1/test/cases/testdata/v1/uuid/test-uuid-parse-rule.yaml new file mode 100644 index 000000000000..e7b22fa21b62 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/uuid/test-uuid-parse-rule.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: uuid-parse-rule/positive + query: data.test.validuser = x + modules: + - | + package test + + validuser if { + is_string(input.userid) + parsed_uuid := uuid.parse(input.userid) + parsed_uuid.variant == "RFC4122" + parsed_uuid.version == 4 + } + data: {} + input: + userid: 00000000-0000-4000-8000-000000000000 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/uuid/test-uuid-parse.yaml b/third_party/opa/v1/test/cases/testdata/v1/uuid/test-uuid-parse.yaml new file mode 100644 index 000000000000..5b426fc0339f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/uuid/test-uuid-parse.yaml @@ -0,0 +1,61 @@ +--- +cases: + - note: uuid-parse/positive-v4 + query: data.test.p = x + modules: + - | + package test + + p := uuid.parse(input.userid) + data: {} + input: + userid: 00000000-0000-4000-8000-000000000000 + want_result: + - x: + variant: RFC4122 + version: 4 + - note: uuid-parse/positive-v2 + query: data.test.p = x + modules: + - | + package test + + p := uuid.parse(input.userid) + data: {} + input: + userid: 000003e8-48b9-21ee-b200-325096b39f47 + want_result: + - x: + clocksequence: 12800 + domain: Person + id: 1000 + macvariables: local:unicast + nodeid: 32-50-96-b3-9f-47 + time: 1693566990121469600 + variant: RFC4122 + version: 2 + - note: uuid-parse/positive-v3 + query: data.test.p = x + modules: + - | + package test + + p := uuid.parse(input.userid) + data: {} + input: + userid: 38074da4-0b00-388d-9c3c-362de965547a + want_result: + - x: + variant: RFC4122 + version: 3 + - note: uuid-parse/negative + query: data.test.p = x + modules: + - | + package test + + p := uuid.parse(input.userid) + data: {} + input: + userid: 123 + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0726.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0726.yaml new file mode 100644 index 000000000000..89d0d9973595 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0726.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: varreferences/ground + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + v = [[1, 2], [2, 3], [3, 4]] + x = v[2][1] + } + data: {} + want_result: + - x: + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0727.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0727.yaml new file mode 100644 index 000000000000..874ac9285e39 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0727.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: varreferences/non-ground + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + v = [[1, 2], [2, 3], [3, 4]] + x = v[i][j] + } + data: {} + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0728.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0728.yaml new file mode 100644 index 000000000000..dc302aa3c7af --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0728.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: varreferences/mixed + query: data.generated.p = x + modules: + - | + package generated + + p[x] := y if { + v = [{"a": 1, "b": 2}, {"c": 3, "z": [4]}] + y = v[i][x][j] + } + data: {} + want_result: + - x: + z: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0729.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0729.yaml new file mode 100644 index 000000000000..917c803b0f78 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0729.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: varreferences/ref binding + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + v = data.c[i][j] + x = v[k] + x = true + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: + - true + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0730.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0730.yaml new file mode 100644 index 000000000000..6815d2c621e8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0730.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: varreferences/existing ref binding + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + q = data.a + q[0] = x + q[0] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0731.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0731.yaml new file mode 100644 index 000000000000..f2155413282e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0731.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: varreferences/embedded + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + v = [1, 2, 3] + __local0__ = v[i] + x = [{"a": __local0__}] + } + data: {} + want_result: + - x: + - - a: 1 + - - a: 2 + - - a: 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0732.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0732.yaml new file mode 100644 index 000000000000..2b855b6fd0fd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0732.yaml @@ -0,0 +1,34 @@ +--- +cases: + - note: varreferences/embedded ref binding + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + v = data.c[i][j] + __local0__ = v[0] + __local1__ = v[1] + w = [__local0__, __local1__] + x = w[y] + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: + - null + - false + - true + - 3.14159 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0733.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0733.yaml new file mode 100644 index 000000000000..98c3b38105b5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0733.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "varreferences/array: ground var" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + i = [1, 2, 3, 4] + j = [1, 2, 999] + j[k] = y + i[y] = x + } + data: {} + want_result: + - x: + - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0734.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0734.yaml new file mode 100644 index 000000000000..226fcbd24bae --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0734.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "varreferences/array: ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + i = [1, 2, 3, 4] + x = data.a[_] + i[x] = y + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0735.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0735.yaml new file mode 100644 index 000000000000..73ab01fc0389 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0735.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "varreferences/object: ground var" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + i = {"a": 1, "b": 2, "c": 3} + j = ["a", "c", "deadbeef"] + j[k] = y + i[y] = x + } + data: {} + want_result: + - x: + - 1 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0736.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0736.yaml new file mode 100644 index 000000000000..438855478e5e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0736.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "varreferences/object: ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + i = {"1": 1, "2": 2, "4": 4} + x = data.numbers[_] + i[x] = y + } + data: + numbers: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: + - 1 + - 2 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0737.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0737.yaml new file mode 100644 index 000000000000..09c40b1cfadf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0737.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "varreferences/set: ground var" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + i = {1, 2, 3, 4} + j = {1, 2, 99} + j[x] + i[x] + } + data: {} + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0738.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0738.yaml new file mode 100644 index 000000000000..81f4c17c253f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0738.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: "varreferences/set: ref" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + i = {1, 2, 3, 4} + x = data.a[_] + i[x] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0739.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0739.yaml new file mode 100644 index 000000000000..5eb8770c03d4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0739.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "varreferences/set: lookup: base docs" + query: data.generated.p = x + modules: + - | + package generated + + p if { + v = {[1, 999], [3, 4]} + __local0__ = data.a[2] + pair = [__local0__, 4] + v[pair] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0740.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0740.yaml new file mode 100644 index 000000000000..55ff5fd6dccf --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0740.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "varreferences/set: lookup: embedded" + query: data.generated.p = x + modules: + - | + package generated + + p if { + x = [{}, {[1, 2], [3, 4]}] + y = [3, 4] + x[i][y] + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0741.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0741.yaml new file mode 100644 index 000000000000..fb00750d6426 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0741.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: "varreferences/set: lookup: dereference" + query: data.generated.p = x + modules: + - | + package generated + + p contains [i, z, r] if { + x = [{}, {[1, 2], [3, 4]}] + y = [3, 4] + x[i][y][z] = r + } + data: {} + want_result: + - x: + - - 1 + - 0 + - 3 + - - 1 + - 1 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0742.yaml b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0742.yaml new file mode 100644 index 000000000000..56fb83376a01 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/varreferences/test-varreferences-0742.yaml @@ -0,0 +1,15 @@ +--- +cases: + - note: varreferences/avoids indexer + query: data.generated.p = x + modules: + - | + package generated + + p if { + somevar = [1, 2, 3] + somevar[i] = 2 + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0620.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0620.yaml new file mode 100644 index 000000000000..0f58a9b30a09 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0620.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: "virtualdocs/input: set 1" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q[1] + } + + q contains x if { + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0621.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0621.yaml new file mode 100644 index 000000000000..00e2d8807691 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0621.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "virtualdocs/input: set 2" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[1] = x + } + + q contains x if { + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0622.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0622.yaml new file mode 100644 index 000000000000..e660c545a5dd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0622.yaml @@ -0,0 +1,27 @@ +--- +cases: + - note: "virtualdocs/input: set embedded" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.generated.q[2] + x = {"b": [__local0__]} + } + + q contains x if { + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - b: + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0623.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0623.yaml new file mode 100644 index 000000000000..2056325024af --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0623.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: "virtualdocs/input: set undefined" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q[1000] + } + + q contains x if { + data.a[x] = y + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0624.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0624.yaml new file mode 100644 index 000000000000..6858b7c404c0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0624.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: "virtualdocs/input: set dereference" + query: data.generated.p = x + modules: + - | + package generated + + p := y if { + x = [1] + data.generated.q[x][0] = y + } + + q contains [x] if { + data.a[_] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0625.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0625.yaml new file mode 100644 index 000000000000..3e6e21617121 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0625.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: "virtualdocs/input: set ground var" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + x = 1 + data.generated.q[x] + } + + q contains y if { + data.a[y] = i + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0626.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0626.yaml new file mode 100644 index 000000000000..7e28df7e9d4f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0626.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "virtualdocs/input: set ground composite (1)" + query: data.generated.p = x + modules: + - | + package generated + + p if { + z = [[1, 2], 2] + data.generated.q[z] + } + + q contains [x, y] if { + y = 2 + x = [1, y] + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0627.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0627.yaml new file mode 100644 index 000000000000..4952572bb2d3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0627.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: "virtualdocs/input: set ground composite (2)" + query: data.generated.p = x + modules: + - | + package generated + + p if { + y = 2 + z = [[1, y], y] + data.generated.q[z] + } + + q contains [x, y] if { + y = 2 + x = [1, y] + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0628.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0628.yaml new file mode 100644 index 000000000000..efe1e54c8a9c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0628.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "virtualdocs/input: set ground composite (3)" + query: data.generated.p = x + modules: + - | + package generated + + p if { + y = 2 + x = [1, y] + z = [x, y] + data.generated.q[z] + } + + q contains [x, y] if { + y = 2 + x = [1, y] + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0629.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0629.yaml new file mode 100644 index 000000000000..5692534529c3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0629.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: "virtualdocs/input: set partially ground composite" + query: data.generated.p = x + modules: + - | + package generated + + p contains u if { + y = 2 + data.generated.q[z] + z = [x, y] + x = [1, u] + } + + q contains [x, y] if { + y = 2 + x = [1, y] + } + data: {} + want_result: + - x: + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0630.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0630.yaml new file mode 100644 index 000000000000..b3c76d8ad472 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0630.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: "virtualdocs/input: object 1" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q[1] = 2 + } + + q[i] := x if { + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0631.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0631.yaml new file mode 100644 index 000000000000..7fef0d4693da --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0631.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: "virtualdocs/input: object 2" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q[1] = 0 + } + + q[x] := i if { + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0632.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0632.yaml new file mode 100644 index 000000000000..6ed8cd515b5e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0632.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: "virtualdocs/input: object embedded 1" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.generated.q[3] + __local1__ = data.generated.q[2] + x = [1, __local0__, __local1__] + } + + q[i] := x if { + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - - 1 + - 4 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0633.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0633.yaml new file mode 100644 index 000000000000..96b8a8ca8e2b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0633.yaml @@ -0,0 +1,30 @@ +--- +cases: + - note: "virtualdocs/input: object embedded 2" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.generated.q[3] + __local1__ = data.generated.q[2] + x = {"a": [__local0__], "b": [__local1__]} + } + + q[i] := x if { + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - a: + - 4 + b: + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0634.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0634.yaml new file mode 100644 index 000000000000..f9d0e886ef8f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0634.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "virtualdocs/input: object undefined val" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q[1] = 9999 + } + + q[i] := x if { + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0635.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0635.yaml new file mode 100644 index 000000000000..f2146ea2e3e9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0635.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "virtualdocs/input: object undefined key 1" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q[9999] = 2 + } + + q[i] := x if { + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0636.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0636.yaml new file mode 100644 index 000000000000..011d7f28529e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0636.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "virtualdocs/input: object undefined key 2" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q.foo = 2 + } + + q[i] := x if { + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0637.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0637.yaml new file mode 100644 index 000000000000..a9c4071a276f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0637.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: "virtualdocs/input: object dereference ground" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q[0].x[1] = false + } + + q[i] := x if { + x = data.c[i] + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0638.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0638.yaml new file mode 100644 index 000000000000..e861fa50dfdd --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0638.yaml @@ -0,0 +1,30 @@ +--- +cases: + - note: "virtualdocs/input: object dereference ground 2" + query: data.generated.p = x + modules: + - | + package generated + + p contains v if { + x = "a" + data.generated.q[x][y] = v + } + + q[k] := v if { + k = "a" + v = data.a + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0639.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0639.yaml new file mode 100644 index 000000000000..95d4c2316d76 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0639.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: "virtualdocs/input: object defererence non-ground" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q[0][x][y] = false + } + + q[i] := x if { + x = data.c[i] + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0640.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0640.yaml new file mode 100644 index 000000000000..f98904ba917c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0640.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "virtualdocs/input: object ground var key" + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + x = "b" + data.generated.q[x] = y + } + + q[k] := v if { + x = {"a": 1, "b": 2} + x[k] = v + } + data: {} + want_result: + - x: + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0641.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0641.yaml new file mode 100644 index 000000000000..114e78f6c5ec --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0641.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "virtualdocs/input: object non-string key" + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + x = 1 + data.generated.q[x] = y + } + + q[k] := v if { + x = {1: 3, 2: 1} + x[k] = v + } + data: {} + want_result: + - x: + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0642.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0642.yaml new file mode 100644 index 000000000000..0568e54a3b3b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0642.yaml @@ -0,0 +1,28 @@ +--- +cases: + - note: "virtualdocs/input: variable binding substitution" + query: data.generated.p = x + modules: + - | + package generated + + p[x] := y if { + data.generated.r[z] = y + data.generated.q[x] = z + } + + r[k] := v if { + x = {"a": 1, "b": 2, "c": 3, "d": 4} + x[k] = v + } + + q[y] := x if { + z = {"a": "a", "b": "b", "d": "d"} + z[y] = x + } + data: {} + want_result: + - x: + a: 1 + b: 2 + d: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0643.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0643.yaml new file mode 100644 index 000000000000..31f24015262d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0643.yaml @@ -0,0 +1,28 @@ +--- +cases: + - note: "virtualdocs/output: set" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[x] + } + + q contains y if { + data.a[i] = y + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0644.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0644.yaml new file mode 100644 index 000000000000..8b8649293b23 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0644.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: "virtualdocs/output: set embedded" + query: data.generated.p = x + modules: + - | + package generated + + p contains i if { + __local0__ = data.generated.q[i] + {i: [i]} = {i: [__local0__]} + } + + q contains x if { + data.d.e[i] = x + } + data: + d: + e: + - bar + - baz + want_result: + - x: + - bar + - baz + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0645.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0645.yaml new file mode 100644 index 000000000000..ebcc0320b31d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0645.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: "virtualdocs/output: set var binding" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[x] + } + + q contains y if { + i = 1 + j = 2 + y = [i, j] + } + data: {} + want_result: + - x: + - - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0646.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0646.yaml new file mode 100644 index 000000000000..f84cbedc85db --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0646.yaml @@ -0,0 +1,28 @@ +--- +cases: + - note: "virtualdocs/output: set dereference" + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + data.generated.q[x][0] = y + } + + q contains [x] if { + data.a[_] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0647.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0647.yaml new file mode 100644 index 000000000000..c9e42b2bfc0e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0647.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: "virtualdocs/output: set dereference deep" + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + data.generated.q[i][j][k][x] = y + } + + q contains {{[1], [2]}, {[3], [4]}} + data: {} + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0648.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0648.yaml new file mode 100644 index 000000000000..ac3cc9d0d2e9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0648.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "virtualdocs/output: set falsy values" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[x] + } + + q := {0, "", false, null, [], {}, set()} + want_result: + - x: + - null + - 0 + - "" + - [] + - [] + - {} + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0649.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0649.yaml new file mode 100644 index 000000000000..553b99e3ca7f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0649.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "virtualdocs/output: object key" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[x] = 4 + } + + q[i] := x if { + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0650.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0650.yaml new file mode 100644 index 000000000000..9fcec87ce972 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0650.yaml @@ -0,0 +1,27 @@ +--- +cases: + - note: "virtualdocs/output: object non-string key" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[x] = 1 + } + + q[k] := 1 if { + data.a[_] = k + k < 3 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0651.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0651.yaml new file mode 100644 index 000000000000..89c7676c11b9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0651.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: "virtualdocs/output: object value" + query: data.generated.p = x + modules: + - | + package generated + + p[x] := y if { + data.generated.q[x] = y + } + + q[k] := v if { + data.b[k] = v + } + data: + b: + v1: hello + v2: goodbye + want_result: + - x: + v1: hello + v2: goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0652.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0652.yaml new file mode 100644 index 000000000000..d159b6fddf09 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0652.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: "virtualdocs/output: object embedded" + query: data.generated.p = x + modules: + - | + package generated + + p[k] := v if { + __local0__ = data.generated.q[k] + {k: [__local0__]} = {k: [v]} + } + + q[x] := y if { + data.b[x] = y + } + data: + b: + v1: hello + v2: goodbye + want_result: + - x: + v1: hello + v2: goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0653.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0653.yaml new file mode 100644 index 000000000000..7d9620d085ab --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0653.yaml @@ -0,0 +1,31 @@ +--- +cases: + - note: "virtualdocs/output: object dereference ground" + query: data.generated.p = x + modules: + - | + package generated + + p contains i if { + data.generated.q[i].x[1] = false + } + + q[i] := x if { + x = data.c[i] + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: + - 0 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0654.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0654.yaml new file mode 100644 index 000000000000..35a8c866c995 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0654.yaml @@ -0,0 +1,37 @@ +--- +cases: + - note: "virtualdocs/output: object defererence non-ground" + query: data.generated.p = x + modules: + - | + package generated + + p contains r if { + data.generated.q[x][y][z] = false + r = [x, y, z] + } + + q[i] := x if { + x = data.c[i] + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: + - - 0 + - x + - 1 + - - 0 + - z + - q + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0655.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0655.yaml new file mode 100644 index 000000000000..36f3fbb0801c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0655.yaml @@ -0,0 +1,44 @@ +--- +cases: + - note: "virtualdocs/output: object dereference array of refs" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[_0][0].c[_1] = x + } + + q[k] := v if { + data.d.e[_0] = k + v = [r | r = data.l[_1]] + } + data: + d: + e: + - bar + - baz + l: + - a: bob + b: -1 + c: + - 1 + - 2 + - 3 + - 4 + - a: alice + b: 1 + c: + - 2 + - 3 + - 4 + - 5 + d: null + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0656.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0656.yaml new file mode 100644 index 000000000000..fc221cd46e87 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0656.yaml @@ -0,0 +1,45 @@ +--- +cases: + - note: "virtualdocs/output: object dereference array of refs within object" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[_0].x[0].c[_1] = x + } + + q[k] := v if { + data.d.e[_0] = k + __local0__ = [r | r = data.l[_1]] + v = {"x": __local0__} + } + data: + d: + e: + - bar + - baz + l: + - a: bob + b: -1 + c: + - 1 + - 2 + - 3 + - 4 + - a: alice + b: 1 + c: + - 2 + - 3 + - 4 + - 5 + d: null + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0657.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0657.yaml new file mode 100644 index 000000000000..ca63d716f5db --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0657.yaml @@ -0,0 +1,39 @@ +--- +cases: + - note: "virtualdocs/output: object dereference object with key refs" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q.bar[1].alice[0] = 1 + } + + q[k] := v if { + data.d.e[_] = k + v = [x | __local0__ = data.l[_].a; x = {__local0__: [1]}] + } + data: + d: + e: + - bar + - baz + l: + - a: bob + b: -1 + c: + - 1 + - 2 + - 3 + - 4 + - a: alice + b: 1 + c: + - 2 + - 3 + - 4 + - 5 + d: null + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0658.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0658.yaml new file mode 100644 index 000000000000..3d942f9851bc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0658.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: "virtualdocs/output: object var binding" + query: data.generated.p = x + modules: + - | + package generated + + p contains z if { + data.generated.q[x] = y + z = [x, y] + } + + q[k] := v if { + x = "a" + y = "b" + k = "foo" + v = [x, y] + } + data: {} + want_result: + - x: + - - foo + - - a + - b + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0659.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0659.yaml new file mode 100644 index 000000000000..e603b07554b1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0659.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "virtualdocs/output: object key var binding" + query: data.generated.p = x + modules: + - | + package generated + + p contains z if { + data.generated.q[x] = y + z = [x, y] + } + + q[k] := v if { + x = "a" + v = "foo" + y = x + k = y + } + data: {} + want_result: + - x: + - - a + - foo + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0660.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0660.yaml new file mode 100644 index 000000000000..9ce81865282d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0660.yaml @@ -0,0 +1,27 @@ +--- +cases: + - note: "virtualdocs/object: self-join" + query: data.generated.p = x + modules: + - | + package generated + + p contains [x, y] if { + data.generated.q[x] = 1 + data.generated.q[y] = x + } + + q[x] := i if { + data.a[i] = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0661.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0661.yaml new file mode 100644 index 000000000000..37272d1664ed --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0661.yaml @@ -0,0 +1,27 @@ +--- +cases: + - note: "virtualdocs/i/o: objects" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[x] = data.generated.r[x] + } + + q[x] := y if { + z = {"a": 1, "b": 2, "d": 4} + z[x] = y + } + + r[k] := v if { + x = {"a": 1, "b": 2, "c": 4, "d": 3} + x[k] = v + } + data: {} + want_result: + - x: + - a + - b + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0662.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0662.yaml new file mode 100644 index 000000000000..1016fe709daa --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0662.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: "virtualdocs/i/o: undefined keys" + query: data.generated.p = x + modules: + - | + package generated + + p contains y if { + data.generated.q[x] + data.generated.r[x] = y + } + + q contains x if { + z = ["a", "b", "c", "d"] + z[y] = x + } + + r[k] := v if { + x = {"a": 1, "b": 2, "d": 4} + x[k] = v + } + data: {} + want_result: + - x: + - 1 + - 2 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0663.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0663.yaml new file mode 100644 index 000000000000..ef159666e61f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0663.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "virtualdocs/input: complete array" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q[1] = 2 + } + + q := [1, 2, 3, 4] + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0664.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0664.yaml new file mode 100644 index 000000000000..6c865b912a7a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0664.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "virtualdocs/input: complete object" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q.b = 2 + } + + q := {"a": 1, "b": 2} + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0665.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0665.yaml new file mode 100644 index 000000000000..1c6a2d625246 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0665.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "virtualdocs/input: complete set" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q[3] + } + + q := {1, 2, 3, 4} + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0666.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0666.yaml new file mode 100644 index 000000000000..f1c92d4647a0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0666.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "virtualdocs/input: complete array dereference ground" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q[1][1] = 3 + } + + q := [[0, 1], [2, 3]] + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0667.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0667.yaml new file mode 100644 index 000000000000..7d88784080ce --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0667.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "virtualdocs/input: complete object dereference ground" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q.b[1] = 4 + } + + q := {"a": [1, 2], "b": [3, 4]} + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0668.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0668.yaml new file mode 100644 index 000000000000..097d4b3b40a3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0668.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: "virtualdocs/input: complete array ground index" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + z = [1, 2] + z[i] = y + data.generated.q[y] = x + } + + q := [1, 2, 3, 4] + data: {} + want_result: + - x: + - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0669.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0669.yaml new file mode 100644 index 000000000000..cbaa38534252 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0669.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: "virtualdocs/input: complete object ground key" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + z = ["b", "c"] + z[i] = y + data.generated.q[y] = x + } + + q := {"a": 1, "b": 2, "c": 3, "d": 4} + data: {} + want_result: + - x: + - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0670.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0670.yaml new file mode 100644 index 000000000000..6bedaa0d8109 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0670.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "virtualdocs/input: complete vars" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q[1][1] = 2 + } + + q := [{"x": x, "y": y}, z] if { + x = 1 + y = 2 + z = [1, 2, 3] + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0671.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0671.yaml new file mode 100644 index 000000000000..715647e11b35 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0671.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: "virtualdocs/output: complete array" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[i] = e + x = [i, e] + } + + q := [1, 2, 3, 4] + data: {} + want_result: + - x: + - - 0 + - 1 + - - 1 + - 2 + - - 2 + - 3 + - - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0672.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0672.yaml new file mode 100644 index 000000000000..e3e1fe11ac1c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0672.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "virtualdocs/output: complete object" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[i] = e + x = [i, e] + } + + q := {"a": 1, "b": 2} + data: {} + want_result: + - x: + - - a + - 1 + - - b + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0673.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0673.yaml new file mode 100644 index 000000000000..c21a3db483c8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0673.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: "virtualdocs/output: complete set" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[x] + } + + q := {1, 2, 3, 4} + data: {} + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0674.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0674.yaml new file mode 100644 index 000000000000..50fa6311f084 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0674.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "virtualdocs/output: complete array dereference non-ground" + query: data.generated.p = x + modules: + - | + package generated + + p contains r if { + data.generated.q[i][j] = 2 + r = [i, j] + } + + q := [[1, 2], [3, 2]] + data: {} + want_result: + - x: + - - 0 + - 1 + - - 1 + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0675.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0675.yaml new file mode 100644 index 000000000000..1f005103ed60 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0675.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: "virtualdocs/output: complete object defererence non-ground" + query: data.generated.p = x + modules: + - | + package generated + + p contains r if { + data.generated.q[x][y] = 2 + r = [x, y] + } + + q := {"a": {"x": 1}, "b": {"y": 2}, "c": {"z": 2}} + data: {} + want_result: + - x: + - - b + - "y" + - - c + - z + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0676.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0676.yaml new file mode 100644 index 000000000000..08a6aa10e02e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0676.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: "virtualdocs/output: complete vars" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[_][_] = x + } + + q := [{"x": x, "y": y}, z] if { + x = 1 + y = 2 + z = [1, 2, 3] + } + data: {} + want_result: + - x: + - 1 + - 2 + - 3 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0677.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0677.yaml new file mode 100644 index 000000000000..3df4a0f7314c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0677.yaml @@ -0,0 +1,16 @@ +--- +cases: + - note: "virtualdocs/no suffix: complete" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q + } + + q := true + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0678.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0678.yaml new file mode 100644 index 000000000000..ef82dea634e9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0678.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "virtualdocs/no suffix: complete vars" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q + } + + q := x if { + x = true + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0679.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0679.yaml new file mode 100644 index 000000000000..a74a863ed9fc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0679.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: "virtualdocs/no suffix: complete incr (error)" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q + } + + q := false + + q := true + data: {} + want_error_code: eval_conflict_error + want_error: complete rules must not produce multiple outputs diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0680.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0680.yaml new file mode 100644 index 000000000000..d1942b910eae --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0680.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: "virtualdocs/no suffix: complete incr" + query: data.generated.p = x + modules: + - | + package generated + + p if { + not data.generated.q + } + + q if { + false + } + + q := false + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0681.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0681.yaml new file mode 100644 index 000000000000..0c93c61f6f61 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0681.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: "virtualdocs/no suffix: object" + query: data.generated.p = x + modules: + - | + package generated + + p[x] := y if { + data.generated.q = o + o[x] = y + } + + q[x] := y if { + data.b[x] = y + } + data: + b: + v1: hello + v2: goodbye + want_result: + - x: + v1: hello + v2: goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0682.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0682.yaml new file mode 100644 index 000000000000..adf98bb6e7c0 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0682.yaml @@ -0,0 +1,34 @@ +--- +cases: + - note: "virtualdocs/no suffix: object incr" + query: data.generated.p = x + modules: + - | + package generated + + p[x] := y if { + data.generated.q = o + o[x] = y + } + + q[x] := y if { + data.b[x] = y + } + + q[x1] := y1 if { + data.d.e[y1] = x1 + } + data: + b: + v1: hello + v2: goodbye + d: + e: + - bar + - baz + want_result: + - x: + bar: 0 + baz: 1 + v1: hello + v2: goodbye diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0683.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0683.yaml new file mode 100644 index 000000000000..3ed4882fba91 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0683.yaml @@ -0,0 +1,34 @@ +--- +cases: + - note: "virtualdocs/no suffix: chained" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q = x + x[i] = 4 + } + + q[k] := v if { + data.generated.r = x + x[k] = v + } + + r[k] := v if { + data.generated.s = x + x[k] = v + } + + r[k] := v if { + data.generated.t = x + x[v] = k + } + + s := {"a": 1, "b": 2, "c": 4} + + t := ["d", "e", "g"] + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0684.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0684.yaml new file mode 100644 index 000000000000..e497324154ab --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0684.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: "virtualdocs/no suffix: object var binding" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q = x + } + + q[k] := v if { + i = "a" + j = 1 + v = [i, j] + k = i + } + data: {} + want_result: + - x: + - a: + - a + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0685.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0685.yaml new file mode 100644 index 000000000000..bd62ac7dfd6c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0685.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: "virtualdocs/no suffix: object composite value" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q = x + } + + q[k] := {"v": v} if { + i = "a" + j = 1 + v = [i, j] + k = i + } + data: {} + want_result: + - x: + - a: + v: + - a + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0686.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0686.yaml new file mode 100644 index 000000000000..648e1754b350 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0686.yaml @@ -0,0 +1,30 @@ +--- +cases: + - note: "virtualdocs/no suffix: bound ref with long prefix (#238)" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q + data.generated.q + } + + q := x if { + x = data.c[0].z.p + } + data: + c: + - "true": + - null + - 3.14159 + x: + - true + - false + - foo + z: + p: true + q: false + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0687.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0687.yaml new file mode 100644 index 000000000000..198f662edb20 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0687.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: "virtualdocs/no suffix: object conflict (error)" + query: data.generated.p = x + modules: + - | + package generated + + p[x] := y if { + xs = ["a", "b", "c", "a"] + x = xs[i] + y = data.a[i] + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_error_code: eval_conflict_error + want_error: object keys must be unique diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0688.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0688.yaml new file mode 100644 index 000000000000..03b4a4b2008c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0688.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: "virtualdocs/no suffix: set" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q = s + s[x] + } + + q contains x if { + data.a[i] = x + } + data: + a: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: + - "1" + - "2" + - "3" + - "4" + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0689.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0689.yaml new file mode 100644 index 000000000000..5a1a6dced34a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0689.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: virtualdocs/empty partial set + query: data.generated.p = x + modules: + - | + package generated + + p contains 1 if { + data.a[0] = 100 + } + data: + a: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: [] + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0690.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0690.yaml new file mode 100644 index 000000000000..eee3762aea0b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0690.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: virtualdocs/empty partial object + query: data.generated.p = x + modules: + - | + package generated + + p["x"] := 1 if { + data.a[0] = 100 + } + data: + a: + - "1" + - "2" + - "3" + - "4" + want_result: + - x: {} diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0691.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0691.yaml new file mode 100644 index 000000000000..32d80f839c67 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0691.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: "virtualdocs/input: non-ground object keys" + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + data.generated.q.a.b = x + } + + q := {x: {y: 1}} if { + x = "a" + y = "b" + } + data: {} + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0692.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0692.yaml new file mode 100644 index 000000000000..c9df3aea228f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0692.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: "virtualdocs/input: non-ground set elements" + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.generated.q.c + } + + q := {x, "b", z} if { + x = "a" + z = "c" + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0693.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0693.yaml new file mode 100644 index 000000000000..cf06cc10a388 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0693.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: "virtualdocs/output: non-ground object keys" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[i][j] = x + } + + q := {x: {x1: 1}, y: {y1: 2}} if { + x = "a" + y = "b" + x1 = "a1" + y1 = "b1" + } + data: {} + want_result: + - x: + - 1 + - 2 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0694.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0694.yaml new file mode 100644 index 000000000000..0b0dfa41ff2d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-0694.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: "virtualdocs/output: non-ground set elements" + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.generated.q[x] + } + + q := {x, "b", z} if { + x = "a" + z = "c" + } + data: {} + want_result: + - x: + - a + - b + - c + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-undefined.yaml b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-undefined.yaml new file mode 100644 index 000000000000..af96544a3689 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/virtualdocs/test-virtualdocs-undefined.yaml @@ -0,0 +1,42 @@ +--- +cases: + - note: "virtualdocs/undefined: in array literal" + query: data.test.p = x + modules: + - | + package test + + p if { + [1, 2, input] + } else := false + want_result: + - x: false + - note: "virtualdocs/undefined: in set literal" + query: data.test.p = x + modules: + - | + package test + + p if { + {1, 2, input} + } else := false + want_result: + - x: false + - note: "virtualdocs/undefined: in set coprehension body" + query: data.test.p = x + modules: + - | + package test + + p := {1 | input} + want_result: + - x: [] + - note: "virtualdocs/undefined: in array coprehension body" + query: data.test.p = x + modules: + - | + package test + + p := [1 | input] + want_result: + - x: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0970.yaml b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0970.yaml new file mode 100644 index 000000000000..6cb45a85516e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0970.yaml @@ -0,0 +1,41 @@ +--- +cases: + - note: walkbuiltin/scalar + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.a[0] + walk(__local0__, x) + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - - [] + - 1 + sort_bindings: true + - note: walkbuiltin/scalar no path + query: data.test.p = x + modules: + - | + package test + + p contains x if { + walk(data.a[0], [_, x]) + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0971.yaml b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0971.yaml new file mode 100644 index 000000000000..cd1a0328415b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0971.yaml @@ -0,0 +1,58 @@ +--- +cases: + - note: walkbuiltin/arrays + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.a + walk(__local0__, x) + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - - [] + - - 1 + - 2 + - 3 + - 4 + - - - 0 + - 1 + - - - 1 + - 2 + - - - 2 + - 3 + - - - 3 + - 4 + sort_bindings: true + - note: walkbuiltin/arrays no path + query: data.test.p = x + modules: + - | + package test + + p := [x | walk(data.a, [_, x])] + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 1 + - 2 + - 3 + - 4 + - - 1 + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0972.yaml b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0972.yaml new file mode 100644 index 000000000000..71ec37ecd32b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0972.yaml @@ -0,0 +1,44 @@ +--- +cases: + - note: walkbuiltin/objects + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.b + walk(__local0__, x) + } + data: + b: + v1: hello + v2: goodbye + want_result: + - x: + - - [] + - v1: hello + v2: goodbye + - - - v1 + - hello + - - - v2 + - goodbye + sort_bindings: true + - note: walkbuiltin/objects no path + query: data.generated.p = x + modules: + - | + package generated + + p := [x | walk(data.b, [_, x])] + data: + b: + v1: hello + v2: goodbye + want_result: + - x: + - goodbye + - hello + - v1: hello + v2: goodbye + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0973.yaml b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0973.yaml new file mode 100644 index 000000000000..d89607cff301 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0973.yaml @@ -0,0 +1,58 @@ +--- +cases: + - note: walkbuiltin/sets + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + __local0__ = data.generated.q + walk(__local0__, x) + } + + q := {{1, 2, 3}} + data: {} + want_result: + - x: + - - [] + - - - 1 + - 2 + - 3 + - - - - 1 + - 2 + - 3 + - - 1 + - 2 + - 3 + - - - - 1 + - 2 + - 3 + - 1 + - 1 + - - - - 1 + - 2 + - 3 + - 2 + - 2 + - - - - 1 + - 2 + - 3 + - 3 + - 3 + sort_bindings: true + - note: walkbuiltin/sets no path + query: data.test.p = x + modules: + - | + package test + + p := [x | walk({{1, 2, 3}}, [_, x])] + data: {} + want_result: + - x: + - [[1, 2, 3]] + - [1, 2, 3] + - 1 + - 2 + - 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0974.yaml b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0974.yaml new file mode 100644 index 000000000000..752f5726216b --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0974.yaml @@ -0,0 +1,28 @@ +--- +cases: + - note: walkbuiltin/match and filter + query: data.test.p = x + modules: + - | + package test + + p contains [k, x] if { + walk(q, [k, x]) + contains(k[1], "oo") + } + + q := [{ + "foo": 1, + "bar": 2, + "bazoo": 3, + }] + data: {} + want_result: + - x: + - - - 0 + - bazoo + - 3 + - - - 0 + - foo + - 1 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0975.yaml b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0975.yaml new file mode 100644 index 000000000000..5079bc2ce92c --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-0975.yaml @@ -0,0 +1,33 @@ +--- +cases: + - note: walkbuiltin/partially ground path + query: data.generated.p = x + modules: + - | + package generated + + p contains [k1, k2, x] if { + __local0__ = data.generated.q + walk(__local0__, [["a", k1, "b", k2], x]) + } + + q := {"a": [ + {"b": {"foo": 1, "bar": 2}}, + {"b": {"baz": 3, "qux": 4}}, + ]} + data: {} + want_result: + - x: + - - 0 + - bar + - 2 + - - 0 + - foo + - 1 + - - 1 + - baz + - 3 + - - 1 + - qux + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-issue-7656.yaml b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-issue-7656.yaml new file mode 100644 index 000000000000..1ec0bc1e8893 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-issue-7656.yaml @@ -0,0 +1,26 @@ +--- +cases: + # verify fix for issue 7656 + - note: walkbuiltin/array path not overwritten + query: data.generated.p = x + modules: + - | + package generated + + x := {"a": [{"b": [{"c": [ + {"aa": "AA"}, + {"bb": "BB"}, + {"cc": "CC"}, + {"dd": "DDD"}, + ]}]}]} + + p := {path: value | + [path, value] := walk(x) + count(value) == 2 + } + data: {} + want_result: + - x: + "[\"a\",0,\"b\",0,\"c\",0,\"aa\"]": "AA" + "[\"a\",0,\"b\",0,\"c\",1,\"bb\"]": "BB" + "[\"a\",0,\"b\",0,\"c\",2,\"cc\"]": "CC" diff --git a/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-wildcard-path.yaml b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-wildcard-path.yaml new file mode 100644 index 000000000000..2e2e45238a30 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/walkbuiltin/test-walkbuiltin-wildcard-path.yaml @@ -0,0 +1,28 @@ +--- +cases: + - note: walkbuiltin/wildcard-path same values as when path provided + query: x = data.testing.same_values + modules: + - | + package testing + + obj := { + "bar": "baz", + "qux": [ + 1, + {"p": "rego", "q": "rules"}, + {1, 2, 3, {"a": "b", "c": {"d", "e", 1}}}, + ], + } + + with_path contains value if { + walk(obj, [path, value]) + } + + without_path contains value if { + walk(obj, [_, value]) + } + + same_values := with_path == without_path + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-and-ndbcache-issue.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-and-ndbcache-issue.yaml new file mode 100644 index 000000000000..c407bd364c5f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-and-ndbcache-issue.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: "with: ndb_cache-issue" + query: data.rules = x + modules: + - | + package rules + + p if { + time.now_ns(now) + } + + q if p with data.x as 7 + want_result: + - x: + p: true + q: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-builtin-mock.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-builtin-mock.yaml new file mode 100644 index 000000000000..d89892521894 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-builtin-mock.yaml @@ -0,0 +1,534 @@ +--- +cases: + - note: "withkeyword/builtin: direct call, arity 0" + query: data.test.p = x + modules: + - | + package test + + f := 1 + + p := y if { + y = time.now_ns() with time.now_ns as f + } + want_result: + - x: 1 + - note: "withkeyword/builtin: direct call, arity 1" + query: data.test.p = x + modules: + - | + package test + + f(_) := 1 + + p := y if { + y = count([1, 2, 3]) with count as f + } + want_result: + - x: 1 + - note: "withkeyword/builtin: indirect call, arity 1" + query: data.test.p = x + modules: + - | + package test + + f(_) := 1 + + p if { + q with count as f + } + + q if { + count([1, 2, 3]) == 1 + } + want_result: + - x: true + - note: "withkeyword/builtin: indirect call, arity 0" + query: data.test.p = x + modules: + - | + package test + + f := 1 + + p if { + q with time.now_ns as f + } + + q if { + time.now_ns() == 1 + } + want_result: + - x: true + - note: "withkeyword/builtin: indirect call, arity 0, rule queried with and without mock" + query: data.test.p = x + modules: + - | + package test + + f := 1 + + p if { + q with time.now_ns as f + not q + } + + q if { + time.now_ns() == 1 + } + want_result: + - x: true + - note: "withkeyword/builtin: indirect call, arity 0, query package" + query: data.test = x + modules: + - | + package test + + f := 1 + + p if { + q with time.now_ns as f + } + + q if { + time.now_ns() == 1 + } + want_result: + - x: + f: 1 + p: true + - note: "withkeyword/builtin: http.send example" + query: data.test.test_allow = x + modules: + - | + package test + + pass_resp := {"body": {"roles": ["admin"]}} + + deny_resp := {"body": {"roles": []}} + + mock_http_send(req) := pass_resp if { + req.body.name == "alice" + } else := deny_resp + + test_allow if { + allow with http.send as mock_http_send with input.name as "alice" + } + + allow if { + "admin" in http.send({"method": "GET", "body": input}).body.roles + } + want_result: + - x: true + - note: "withkeyword/builtin: nested, multiple mocks" + query: data.test.test_allow = x + modules: + - | + package test + + pass_resp := {"body": {"jwt": "myjot"}} + + deny_resp := {"body"} + + mock_http_send(req) := pass_resp if { + req.body.name == "alice" + } else := deny_resp + + mock_decode_verify("myjot", _) := [true, {}, {"owner": "alice"}] + + test_allow if { + allow with data.verification.cert as "cert" + with input.name as "alice" + with http.send as mock_http_send + with io.jwt.decode_verify as mock_decode_verify + } + + allow if { + payload.owner == input.name + } + + claims[k] := v if { + resp := http.send({"method": "GET", "body": input}).body + some k, v in resp + } + + payload := p if { + some p + [true, _, p] = io.jwt.decode_verify(claims.jwt, {"cert": data.verification.cert, "iss": "issuer"}) + } + want_result: + - x: true + - note: "withkeyword/builtin: indirect call through function" + query: data.test.p = x + modules: + - | + package test + + f := 1 + + p if { + q with time.now_ns as f + } + + q if { + valid_time(1) + } + + valid_time(x) if time.now_ns() == x + want_result: + - x: true + - note: "withkeyword/builtin: indirect call through function, rule with and without mock" + query: data.test.p = x + modules: + - | + package test + + f := 1 + + p if { + q with time.now_ns as f + not q + } + + q if { + valid_time(1) + } + + valid_time(x) if time.now_ns() == x + want_result: + - x: true + - note: "withkeyword/builtin: indirect call through function, query package" + query: data.test = x + modules: + - | + package test + + f := 1 + + p if { + q with time.now_ns as f + } + + q if { + valid_time(1) + } + + valid_time(x) if time.now_ns() == x + want_result: + - x: + f: 1 + p: true + - note: "withkeyword/builtin: mock function calls original" + query: data.test.p = x + modules: + - | + package test + + mock_count("one") := 1 + + mock_count(x) := count(x) if { + x != "one" + } + + numbers := {"one", "two", "tree"} + + p := s if { + s := {count(n) | some n in numbers} with count as mock_count + } + want_result: + - x: + - 1 + - 3 + - 4 + - note: "withkeyword/builtin: mock function returns same result for both rule defs" + query: data.test.p = x + modules: + - | + package test + + mock_concat("one", _) := ["one"] + + mock_concat("one", x) := x + + numbers := ["one", "one"] + + p := s if { + s := {concat(n, [n]) | some n in numbers} with concat as mock_concat + } + want_result: + - x: + - - one + - note: "withkeyword/builtin: nested, mock function calls original" + query: data.test.p = x + modules: + - | + package test + + mock_concat("one", _) := ["one"] + + mock_concat("one", x) := x + + count_four(4) := 4 + + count_four(x) := count(x) + + numbers := {"one", "two", "tree"} + + q := s if { + s := {concat(n, [n]) | some n in numbers} with concat as mock_concat + r + } + + r if { + count(input.four) == 4 + } + + p := y if { + y := q with concat as mock_concat + with count as count_four + with input.four as 4 + } + want_result: + - x: + - - one + - note: "withkeyword/builtin: multiple with" + query: data.test.p = x + modules: + - | + package test + + f(_) := 1 + + g(x) := count(x) # replaced with f by inner "with" + + q := y if { + y = count([1, 2, 3]) with count as f + } + + p := y if { + y = q with count as g + } + want_result: + - x: 1 + - note: "withkeyword/builtin: mock will not call other mock" + query: data.test.p = x + modules: + - | + package test + + f(x) := object.union_n(x) if {x: i | x := ["a", "a"][i]} # never called, runtime error + + g(x) := count(x) + + p if { + q with count as f + } + + q if { + r with object.union_n as g + } + + r if { + object.union_n([{}]) + } + want_result: + - x: true + - note: "withkeyword/builtin: nested scope handling" + query: data.test.p = x + modules: + - | + package test + + f(x) := object.union_n(x) # b1 + + g(x) := 123 if { + count(x) # b2 + s with array.reverse as h + } + + h(_) := ["replaced"] + + p if q with object.union_n as f + + q if r with count as g + + r if { + x := [{"foo": 4}, {"baz": 5}] + count(x) == 123 + object.union_n(x) == {"foo": 4, "baz": 5} + } + + s if { + x := [{}] + array.reverse(x) == ["replaced"] + } + want_result: + - x: true + - note: "withkeyword/builtin-value: arity-0, captured output" + query: data.test.p = x + modules: + - | + package test + + p := y if { + y = time.now_ns() with time.now_ns as 12300 + } + want_result: + - x: 12300 + - note: "withkeyword/builtin-value: arity-0, false" + query: data.test.p = x + modules: + - | + package test + + p if { + time.now_ns() with time.now_ns as false + } + want_result: [] + - note: "withkeyword/builtin-value: arity-0" + query: data.test.p = x + modules: + - | + package test + + p if { + time.now_ns() with time.now_ns as true + } + want_result: + - x: true + - note: "withkeyword/builtin-value: arity-0, var" + query: data.test.p = x + modules: + - | + package test + + p if { + x := true + time.now_ns() with time.now_ns as x + } + want_result: + - x: true + - note: "withkeyword/builtin-value: arity-1" + query: data.test.p = x + modules: + - | + package test + + p if { + count([]) == 1 with count as 1 + } + want_result: + - x: true + - note: "withkeyword/builtin-value: arity-1, captured" + query: data.test.p = x + modules: + - | + package test + + p if { + count([], 1) with count as 1 + } + want_result: + - x: true + - note: "withkeyword/builtin-value: arity-1, input must still be defined" + query: data.test.p = x + modules: + - | + package test + + p if { + count(input) == 1 with count as 1 + } + want_result: [] + - note: "withkeyword/builtin-builtin: arity 0" + query: data.test.p = x + modules: + - | + package test + + p := x if { + x = time.now_ns() with time.now_ns as opa.runtime + } + want_result: + - x: {} + - note: "withkeyword/builtin-builtin: arity 1, replacement is compound" + query: data.test.p = x + modules: + - | + package test + + p := x if { + x = count([{}, {"foo": 3}]) with count as object.union_n + } + want_result: + - x: + foo: 3 + - note: "withkeyword/builtin-builtin: arity 1, replacement is simple" + query: data.test.p = x + modules: + - | + package test + + p := x if { + x = object.union_n([{}, {"foo": 3}]) with object.union_n as count + } + want_result: + - x: 2 + - note: "withkeyword/builtin: direct call, arity 1, replacement is value that needs eval (array comprehension)" + query: data.test.p = x + modules: + - | + package test + + p if { + count([1, 2, 3]) == 1 with count as [1 | true][0] + } + want_result: + - x: true + - note: "withkeyword/builtin: indirect call, arity 1, replacement is value that needs eval (array comprehension)" + query: data.test.p = x + modules: + - | + package test + + p if { + q with count as [1 | true][0] + } + + q if count([1, 2, 3]) == 1 + want_result: + - x: true + - note: "withkeyword/builtin: indirect call, arity 1, replacement is local variable via scan" + modules: + - | + package test + p if { + some v in numbers.range(1, 10) + q with count as v + } + q if r + r if count("foo") == 10 + query: data.test.p = x + want_result: + - x: true + - note: "withkeyword/builtin: indirect call, arity 1, multiple, nested replacements" + modules: + - | + package test + p if { + v := 3 + m with count as v + } + m if { + v := "300" + sprintf("x", []) == "x" # unreplaced + n with sprintf as v + } + n if { + count(input) == 3 + sprintf("", input) == "300" + } + query: data.test.p = x + input: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-function-mock.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-function-mock.yaml new file mode 100644 index 000000000000..b9417e266230 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-function-mock.yaml @@ -0,0 +1,215 @@ +--- +cases: + - note: "withkeyword/function: direct call, value replacement, arity 1" + query: data.test.p = x + modules: + - | + package test + + f(_) := 2 + + p := y if { + y = f(true) with f as 1 + } + want_result: + - x: 1 + - note: "withkeyword/function: direct call, function replacement, arity 1" + query: data.test.p = x + modules: + - | + package test + + f(_) := 2 + + g(_) := 1 + + p := y if { + y = f(true) with f as g + } + want_result: + - x: 1 + - note: "withkeyword/function: direct call, function replacement, arity 1, result captured" + query: data.test.p = x + modules: + - | + package test + + f(_) := 2 + + g(_) := 1 + + p if { + f(true, 1) with f as g + } + want_result: + - x: true + - note: "withkeyword/function: direct call, built-in replacement, arity 1" + query: data.test.p = x + modules: + - | + package test + + f(_) := 2 + + p := y if { + y = f([1]) with f as count + } + want_result: + - x: 1 + - note: "withkeyword/function: direct call, built-in replacement, arity 1, result captured" + query: data.test.p = x + modules: + - | + package test + + f(_) := 2 + + p if { + f([1], 1) with f as count + } + want_result: + - x: true + - note: "withkeyword/function: nested scope handling" + query: data.test.p = x + modules: + - | + package test + + f1(x) := object.union_n(x) + + f2(x) := count(x) + + f3(x) := array.reverse(x) + + f(x) := f1(x) + + g(x) := 123 if { + f2(x) + s with f3 as h + } + + h(_) := ["replaced"] + + p if q with f1 as f + + q if r with f2 as g + + r if { + x := [{"foo": 4}, {"baz": 5}] + f2(x) == 123 + f1(x) == {"foo": 4, "baz": 5} + } + + s if { + x := [{}] + f3(x) == ["replaced"] + } + want_result: + - x: true + - note: "withkeyword/function: simple scope handling (no recursion here)" + query: data.test.p = x + modules: + - | + package test + + f(x) := 2 + + g(x) := f(x) + + p := y if y := f(1) with f as g + want_result: + - x: 2 + - note: "withkeyword/function: rule indexing irrelevant" + query: data.test.p = x + modules: + - | + package test + + f(_) := 1 if { + input.x = "x" + } + + p := y if y := f(1) with f as 2 + want_result: + - x: 2 + - note: "withkeyword/function: direct call, arity 1, replacement is value that needs eval (array comprehension)" + query: data.test.p = x + modules: + - | + package test + + f(_) := 1 + + p if { + f([1, 2, 3]) == 1 with f as [1 | true][0] + } + want_result: + - x: true + - note: "withkeyword/function: indirect call, arity 1, replacement is value that needs eval (array comprehension)" + query: data.test.p = x + modules: + - | + package test + + f(_) := 1 + + p if { + q with f as [1 | true][0] + } + + q if { + f([1, 2, 3]) == 1 + } + want_result: + - x: true + - note: "withkeyword/function: indirect call, arity 1, replacement is local variable" + modules: + - | + package test + f(1) = 2 + p if { + my_var := 1 + q with f as my_var + } + q if r + r if f(1) == 1 + query: data.test.p = x + want_result: + - x: true + - note: "withkeyword/function: indirect call, arity 1, replacement is local variable via scan" + modules: + - | + package test + f(1) = 2 + p if { + some v in numbers.range(1, 10) + q with f as v + } + q if r + r if f(1) == 10 + query: data.test.p = x + want_result: + - x: true + - note: "withkeyword/function: indirect call, arity 1, multiple, nested replacements" + modules: + - | + package test + f(_) := 1 + g(_) := 0 + p if { + v := 3 + m with f as v + } + m if { + v := 300 + g(10) == 0 # unreplaced + n with g as v + } + n if { + f(input) == 3 + g(input) == 300 + } + query: data.test.p = x + input: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-function-mocks-issue-5299.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-function-mocks-issue-5299.yaml new file mode 100644 index 000000000000..dc73f64e28bb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-with-function-mocks-issue-5299.yaml @@ -0,0 +1,73 @@ +--- +cases: + - note: "withkeyword/function: direct call, rule replacement" + query: data.test.p = x + modules: + - | + package test + + f(_) := 2 + + f0 := 1 # a rule + + p := y if { + y = f(true) with f as f0 + } + want_result: + - x: 1 + - note: "withkeyword/function: captured result, rule replacement" + query: data.test.p = x + modules: + - | + package test + + f(_) := 2 + + f0 := 1 # a rule + + p if { + f(true, 1) with f as f0 + } + want_result: + - x: true + - note: "withkeyword/builtin: direct call, arity 0, rule replacement" + query: data.test.p = x + modules: + - | + package test + + f := 1 # a rule + + p := y if { + y = time.now_ns() with time.now_ns as f + } + want_result: + - x: 1 + - note: "withkeyword/builtin: direct call, arity 1, rule replacement" + query: data.test.p = x + modules: + - | + package test + + f := 1 # a rule + + p := y if { + y = count([1, 2, 3]) with count as f + } + want_result: + - x: 1 + - note: "withkeyword/builtin: indirect call, arity 1, rule replacement" + query: data.test.p = x + modules: + - | + package test + + f := 1 # a rule + + g(x) := count(x) + + p := y if { + y = g([1, 2, 3]) with count as f + } + want_result: + - x: 1 diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1015.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1015.yaml new file mode 100644 index 000000000000..9d3d2681955e --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1015.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: withkeyword/with + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.ex.loopback with input as true + data.ex.loopback = false with input as false + } + - | + package ex + + loopback := __local0__ if { + true + __local0__ = input + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1016.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1016.yaml new file mode 100644 index 000000000000..4f513530592f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1016.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: withkeyword/with not + query: data.generated.p = x + modules: + - | + package generated + + p if { + not data.ex.loopback with input as false + data.ex.loopback with input as true + } + - | + package ex + + loopback := __local0__ if { + true + __local0__ = input + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1017.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1017.yaml new file mode 100644 index 000000000000..8493951be125 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1017.yaml @@ -0,0 +1,24 @@ +--- +cases: + - note: withkeyword/with composite + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.ex.composite[x] with input.foo as [1, 2, 3, 4] + } + - | + package ex + + composite contains x if { + input.foo[_] = x + x > 2 + } + data: {} + want_result: + - x: + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1018.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1018.yaml new file mode 100644 index 000000000000..8457528a134f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1018.yaml @@ -0,0 +1,26 @@ +--- +cases: + - note: withkeyword/with vars + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + foo = "hello" + bar = "world" + x = data.ex.vars with input.foo as foo with input.bar as bar + } + - | + package ex + + vars := x if { + y = input.bar + z = input.foo + x = {"bar": y, "foo": z} + } + data: {} + want_result: + - x: + bar: world + foo: hello diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1019.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1019.yaml new file mode 100644 index 000000000000..5c62c269c8d9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1019.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: withkeyword/with conflict + query: data.generated.p = x + modules: + - | + package generated + + p := x if { + x := data.ex.loopback with input.foo as "x" with input.foo.bar as "y" + } + - | + package ex + + loopback := y if { + true + y = input + } + want_result: + - x: + foo: + bar: "y" diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1020.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1020.yaml new file mode 100644 index 000000000000..ab7ddd9d219d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1020.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: withkeyword/with stack + query: data.generated.p = x + modules: + - | + package generated + + r := __local0__ if { + true + __local0__ = input + } + + q := x if { + data.generated.r = x with input.a.c as 2 + } + + p := x if { + data.generated.q = x with input.a.b as 1 + } + data: {} + input_term: '{"a": {"d": 3}, "e": 4}' + want_result: + - x: + a: + b: 1 + c: 2 + d: 3 + e: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1021.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1021.yaml new file mode 100644 index 000000000000..a87210c61271 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1021.yaml @@ -0,0 +1,30 @@ +--- +cases: + - note: withkeyword/with not stack + query: data.generated.p = x + modules: + - | + package generated + + r := __local0__ if { + true + __local0__ = input + } + + q := x if { + not false with input as {} + data.generated.r = x with input.a.c as 2 + } + + p := x if { + data.generated.q = x with input.a.b as 1 + } + data: {} + input_term: '{"a": {"d": 3}, "e": 4}' + want_result: + - x: + a: + b: 1 + c: 2 + d: 3 + e: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1022.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1022.yaml new file mode 100644 index 000000000000..b508d8ca9d9a --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1022.yaml @@ -0,0 +1,36 @@ +--- +cases: + - note: withkeyword/with stack (data) + query: data.generated.p = x + modules: + - | + package generated + + r := __local0__ if { + true + __local0__ = data.test + } + + q := x if { + data.generated.r = x with data.test.a.c as 2 + } + + p := x if { + data.generated.q = x with data.test.a.b as 1 + } + - | + package test.a + + d := 3 + - | + package test + + e := 4 + data: {} + want_result: + - x: + a: + b: 1 + c: 2 + d: 3 + e: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1023.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1023.yaml new file mode 100644 index 000000000000..bcf2cc632b85 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1023.yaml @@ -0,0 +1,45 @@ +--- +cases: + - note: withkeyword/with not stack (data) + query: data.generated.p = x + modules: + - | + package test.a + + d := 3 + - | + package test + + e := 4 + - | + package generated + + r := __local0__ if { + true + __local0__ = data.test + } + + n1 if { + data.test.a.z = 7 + } + + n if { + not data.generated.n1 + } + + q := x if { + not data.generated.n with data.test.a.z as 7 + data.generated.r = x with data.test.a.c as 2 + } + + p := x if { + data.generated.q = x with data.test.a.b as 1 + } + data: {} + want_result: + - x: + a: + b: 1 + c: 2 + d: 3 + e: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1024.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1024.yaml new file mode 100644 index 000000000000..acea151ea2ea --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1024.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: withkeyword/with stack overwrites + query: data.generated.p = x + modules: + - | + package generated + + q := __local0__ if { + true + __local0__ = input + } + + p := x if { + data.generated.q = x with input.a as {"d": 3} + } + data: {} + input_term: '{"a": {"b": 1, "c": 2}}' + want_result: + - x: + a: + d: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1025.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1025.yaml new file mode 100644 index 000000000000..74c323d62e01 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1025.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: withkeyword/with stack overwrites (data) + query: data.generated.p = x + modules: + - | + package generated + + q := __local0__ if { + true + __local0__ = data.test + } + + p := x if { + data.generated.q = x with data.test.a as {"d": 3} + } + - | + package test + + a := {"b": 1, "c": 2} + data: {} + want_result: + - x: + a: + d: 3 diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1026.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1026.yaml new file mode 100644 index 000000000000..5d0ff2a9ceeb --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1026.yaml @@ -0,0 +1,30 @@ +--- +cases: + - note: withkeyword/with invalidate + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.a[_] = x + not data.ex.input_eq with input.x as x + } + - | + package ex + + input_eq if { + input.x = 1 + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: + - 2 + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1027.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1027.yaml new file mode 100644 index 000000000000..71e76f68f696 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1027.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: withkeyword/with invalidate input stack + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y] if { + x = input with input as "a" + y = input + } + data: {} + input_term: '"b"' + want_result: + - x: + - a + - b diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1028.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1028.yaml new file mode 100644 index 000000000000..df5c48721a97 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1028.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: withkeyword/with invalidate input stack iteration + query: data.generated.p = x + modules: + - | + package generated + + q contains x if { + input[_] = x + } + + p contains [x, y] if { + data.generated.q[x] with input as ["a", "b"] + y = input + } + data: {} + input_term: '"c"' + want_result: + - x: + - - a + - c + - - b + - c + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1029.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1029.yaml new file mode 100644 index 000000000000..5d867567fdd3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1029.yaml @@ -0,0 +1,28 @@ +--- +cases: + - note: withkeyword/with invalidate virtual cache + query: data.generated.p = x + modules: + - | + package generated + + q := "a" if { + input = x + x = 1 + } + + q := "b" if { + input = x + x = 2 + } + + p := [x, y] if { + data.generated.q = x with input as 1 + data.generated.q = y + } + data: {} + input_term: "2" + want_result: + - x: + - a + - b diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1030.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1030.yaml new file mode 100644 index 000000000000..851d3119c673 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1030.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: withkeyword/with invalidate data stack + query: data.generated.p = x + modules: + - | + package generated + + q := "b" + + p := [x, y] if { + data.generated.q = x with data.generated.q as "a" + data.generated.q = y + } + data: {} + want_result: + - x: + - a + - b diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1031.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1031.yaml new file mode 100644 index 000000000000..f9023ed8b6c6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1031.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: withkeyword/with invalidate data stack iteration + query: data.generated.p = x + modules: + - | + package generated + + q contains "c" + + p contains [x, y] if { + data.generated.q[x] with data.generated.q as {"a", "b"} + y = data.generated.q + } + data: {} + want_result: + - x: + - - a + - - c + - - b + - - c + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1032.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1032.yaml new file mode 100644 index 000000000000..f0b78a7431dc --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1032.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: withkeyword/with basic data + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.ex.allow_basic = true with data.a as "testdata" + } + - | + package ex + + allow_basic if { + data.a = "testdata" + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1033.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1033.yaml new file mode 100644 index 000000000000..3952f289a78d --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1033.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: withkeyword/with map data overwrite + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.ex.allow_merge_1 = true with data.b.v2 as "world" + } + - | + package ex + + allow_merge_1 if { + data.b = {"v1": "hello", "v2": "world"} + } + data: + b: + v1: hello + v2: goodbye + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1034.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1034.yaml new file mode 100644 index 000000000000..7daf1ef9f138 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1034.yaml @@ -0,0 +1,28 @@ +--- +cases: + - note: withkeyword/with map data new key + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.ex.allow_merge_2 = true with data.b.v2 as "world" with data.b.v3 as "again" + } + - | + package ex + + allow_merge_2 if { + data.b = {"v1": "hello", "v2": "world", "v3": "again"} + } + data: + a: + - 1 + - 2 + - 3 + - 4 + b: + v1: hello + v2: goodbye + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1035.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1035.yaml new file mode 100644 index 000000000000..c25980cc66d9 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1035.yaml @@ -0,0 +1,22 @@ +--- +cases: + - note: withkeyword/with data conflict + query: data.generated.p = x + modules: + - | + package generated + + default p := false + + p if { + data.ex.allow_basic = true with data.a.b as 5 + } + - | + package ex + + allow_basic if { + data.a = "testdata" + } + data: {} + want_result: + - x: false diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1036.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1036.yaml new file mode 100644 index 000000000000..d044986877c5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1036.yaml @@ -0,0 +1,17 @@ +--- +cases: + - note: withkeyword/with base doc exact value + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.a.b[x] = 1 with data.a.b as {"c": 1, "d": 2, "e": 1} + } + data: {} + want_result: + - x: + - c + - e + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1037.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1037.yaml new file mode 100644 index 000000000000..99495f4d1bef --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1037.yaml @@ -0,0 +1,18 @@ +--- +cases: + - note: withkeyword/with base doc any index + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.a.b[x] with data.a.b as {"c": 1, "d": 2, "e": 1} + } + data: {} + want_result: + - x: + - c + - d + - e + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1038.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1038.yaml new file mode 100644 index 000000000000..8f02e9344d87 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1038.yaml @@ -0,0 +1,13 @@ +--- +cases: + - note: withkeyword/undefined_1 + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.a.b.c with data.a.b as 1 + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1039.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1039.yaml new file mode 100644 index 000000000000..cb8110bf3925 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1039.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: withkeyword/undefined_2 + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.l.a with data.l as 1 + } + data: + l: + - a: bob + b: -1 + c: + - 1 + - 2 + - 3 + - 4 + - a: alice + b: 1 + c: + - 2 + - 3 + - 4 + - 5 + d: null + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1040.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1040.yaml new file mode 100644 index 000000000000..7769d894dbc1 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1040.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: withkeyword/with virtual doc exact value + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.ex.virtual = x with data.a.b as {"c": 1, "d": 2, "e": 1} + } + - | + package ex + + virtual contains x if { + data.a.b[x] = 1 + } + data: {} + want_result: + - x: + - - c + - e + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1041.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1041.yaml new file mode 100644 index 000000000000..804c3b178fd6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1041.yaml @@ -0,0 +1,23 @@ +--- +cases: + - note: withkeyword/with virtual doc any index + query: data.generated.p = x + modules: + - | + package generated + + p contains x if { + data.ex.virtual[x] with data.a.b as {"c": 1, "d": 2, "e": 1} + } + - | + package ex + + virtual contains x if { + data.a.b[x] = 1 + } + data: {} + want_result: + - x: + - c + - e + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1042.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1042.yaml new file mode 100644 index 000000000000..89c7eab772a5 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1042.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: withkeyword/with virtual doc specific index + query: data.generated.p = x + modules: + - | + package generated + + p := y if { + y = data.ex.virtual.c with data.a.b as {"c": 1, "d": 2, "e": 1} + } + - | + package ex + + virtual contains x if { + data.a.b[x] = 1 + } + data: {} + want_result: + - x: c diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1043.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1043.yaml new file mode 100644 index 000000000000..4c8ac9179028 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1043.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: withkeyword/with virtual doc not specific index + query: data.generated.p = x + modules: + - | + package generated + + p if { + not data.ex.virtual.d with data.a.b as {"c": 1, "d": 2, "e": 1} + } + - | + package ex + + virtual contains x if { + data.a.b[x] = 1 + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1044.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1044.yaml new file mode 100644 index 000000000000..6067dd64b929 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1044.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: withkeyword/with mock var + query: data.generated.p = x + modules: + - | + package generated + + p := y if { + y = data.ex.mock_var with data.ex.mock_var as {"c": 1, "d": 2} + } + - | + package ex + + mock_var := {"a": 0, "b": 0} + data: {} + want_result: + - x: + c: 1 + d: 2 diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1045.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1045.yaml new file mode 100644 index 000000000000..1853af891aa8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1045.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: withkeyword/with mock rule + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.ex.mock_rule with data.ex.mock_rule as true + } + - | + package ex + + mock_rule := false if { + 1 = 2 + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1046.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1046.yaml new file mode 100644 index 000000000000..be9446cd6c2f --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1046.yaml @@ -0,0 +1,39 @@ +--- +cases: + - note: withkeyword/with rule chain + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.ex.allow with data.label.b.c as [1, 2, 3] + } + - | + package ex + + allow1 if { + data.label.b.c = [1, 2, 3] + } + + allow2 if { + data.label.b.c[x] = 2 + } + + allow3 if { + data.label.b[x] = 1 + } + + allow4 if { + data.label.b.c.d[x] = 1 + } + + allow if { + data.ex.allow1 + data.ex.allow2 + not data.ex.allow3 + not data.ex.allow4 + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1047.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1047.yaml new file mode 100644 index 000000000000..3215271bfdac --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1047.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: withkeyword/with mock iteration on sets + query: data.generated.p = x + modules: + - | + package generated + + q contains 1 + + q contains 2 + + p contains x if { + data.generated.q[x] with data.generated.q as {3, 4} + } + data: {} + want_result: + - x: + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1048.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1048.yaml new file mode 100644 index 000000000000..3bec30d0c9e4 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1048.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: withkeyword/with mock iteration on objects + query: data.generated.p = x + modules: + - | + package generated + + q["a"] := 1 + + q["b"] := 2 + + p[x] := y if { + data.generated.q[x] = y with data.generated.q as {"a": 3, "c": 4} + } + data: {} + want_result: + - x: + a: 3 + c: 4 diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1049.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1049.yaml new file mode 100644 index 000000000000..39c68bd2e6d8 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1049.yaml @@ -0,0 +1,21 @@ +--- +cases: + - note: withkeyword/with mock iteration on arrays + query: data.generated.p = x + modules: + - | + package generated + + q contains 1 + + q contains 2 + + p contains x if { + data.generated.q[_] = x with data.generated.q as [3, 4] + } + data: {} + want_result: + - x: + - 3 + - 4 + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1050.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1050.yaml new file mode 100644 index 000000000000..c97e766aba15 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1050.yaml @@ -0,0 +1,19 @@ +--- +cases: + - note: withkeyword/bug 1083 + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.ex.input_eq with data.foo as 1 + } + - | + package ex + + input_eq if { + input.x = 1 + } + data: {} + want_result: [] diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1051.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1051.yaml new file mode 100644 index 000000000000..3a600aec38f3 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1051.yaml @@ -0,0 +1,25 @@ +--- +cases: + - note: withkeyword/bug 1100 + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.ex.data_eq with input as {} + } + - | + package ex + + data_eq if { + data.a = x + } + data: + a: + - 1 + - 2 + - 3 + - 4 + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1052.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1052.yaml new file mode 100644 index 000000000000..12203814d8e6 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1052.yaml @@ -0,0 +1,20 @@ +--- +cases: + - note: withkeyword/set lookup + query: data.generated.p = x + modules: + - | + package generated + + p if { + data.ex.setl[1] with data.foo as {1} + } + - | + package ex + + setl contains x if { + data.foo[x] + } + data: {} + want_result: + - x: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1053.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1053.yaml new file mode 100644 index 000000000000..8df0dd0d0136 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1053.yaml @@ -0,0 +1,29 @@ +--- +cases: + - note: withkeyword/invalidate comprehension cache + query: data.generated.p = x + modules: + - | + package generated + + p := [x, y] if { + x = data.ex.s with input as {"a": "b", "c": "b"} + y = data.ex.s with input as {"a": "b"} + } + - | + package ex + + s contains x if { + x = {v: ks | + v = input[i] + ks = {k | v = input[k]} + } + } + want_result: + - x: + - - b: + - a + - - b: + - a + - c + sort_bindings: true diff --git a/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1054.yaml b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1054.yaml new file mode 100644 index 000000000000..98d115fe5129 --- /dev/null +++ b/third_party/opa/v1/test/cases/testdata/v1/withkeyword/test-withkeyword-1054.yaml @@ -0,0 +1,72 @@ +--- +cases: + - note: withkeyword/rewrite declared variables in with value + query: data.test.allow = x + modules: + - | + package test + + allow if { + a := {"x": 0} + + input.x == 0 with input as a + } + want_result: + - x: true + - note: withkeyword/rewrite declared variables nested in function call in with value + query: data.test.allow = x + modules: + - | + package test + + allow if { + a := {"x": 0} + + input.x == 1 with input as object.union(a, {"x": 1}) + input.x == -1 with input as object.union(a, {"x": -1}) + + input.x == 2 with input as object.union(a, object.union(a, {"x": 2})) + input.x == -2 with input as object.union(a, object.union(a, {"x": -2})) + } + want_result: + - x: true + - note: withkeyword/rewrite declared variables nested in array in with value + query: data.test.allow = x + modules: + - | + package test + + allow if { + a := 1 + input[0] == 1 with input as [a] + input[0][0] == 1 with input as [[a]] + } + want_result: + - x: true + - note: withkeyword/rewrite declared variables nested in object in with value + query: data.test.allow = x + modules: + - | + package test + + allow if { + a := 1 + input.a == 1 with input as {"a": a} + input.nested.a == 1 with input as {"nested": {"a": a}} + } + want_result: + - x: true + - note: withkeyword/rewrite declared variables nested in function/array/object in with value + query: data.test.allow = x + modules: + - | + package test + + allow if { + a := 1 + b := 2 + + input.min == 1 with input as object.union({"min": 0}, {"min": min([a, b])}) + } + want_result: + - x: true diff --git a/third_party/opa/v1/test/cli/smoke/.gitignore b/third_party/opa/v1/test/cli/smoke/.gitignore new file mode 100644 index 000000000000..35c5bbe55310 --- /dev/null +++ b/third_party/opa/v1/test/cli/smoke/.gitignore @@ -0,0 +1 @@ +!bundle.tar.gz diff --git a/third_party/opa/v1/test/cli/smoke/.manifest b/third_party/opa/v1/test/cli/smoke/.manifest new file mode 100644 index 000000000000..17a3a0e9a661 --- /dev/null +++ b/third_party/opa/v1/test/cli/smoke/.manifest @@ -0,0 +1,3 @@ +{ + "roots": ["test", "namespace", "x"] +} \ No newline at end of file diff --git a/third_party/opa/v1/test/cli/smoke/data.yaml b/third_party/opa/v1/test/cli/smoke/data.yaml new file mode 100644 index 000000000000..14bb2664dd37 --- /dev/null +++ b/third_party/opa/v1/test/cli/smoke/data.yaml @@ -0,0 +1,3 @@ +x: + foo: + bar: true diff --git a/third_party/opa/v1/test/cli/smoke/golden-bundle.tar.gz b/third_party/opa/v1/test/cli/smoke/golden-bundle.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..c48dac1da7b64dd6d82f24b0592ef1bb8f648349 GIT binary patch literal 179 zcmV;k08IZMiwFP!00000|LoL34#FT11yI(Uf)mhBn(BFor43d~14B$>yt~njrY>9< zY?{p547>Rxe5%%x)yEV@Q49d=fak>L1W>=7Az%y|BNWO-ddZ1w6!>}9@^Tw-y~(vB zuoYG066xsrP;ST9{_mS*|Bb1+|Nk)A!(e--9?w8Zf1W}l)n&@j>A}sH5Zk1ECuMXg hpGf&m`EkmmXFK!H!5CxPbq@dl|NrGb`O^Rl002a-Satva literal 0 HcmV?d00001 diff --git a/third_party/opa/v1/test/cli/smoke/input.json b/third_party/opa/v1/test/cli/smoke/input.json new file mode 100644 index 000000000000..7f9c93371cdc --- /dev/null +++ b/third_party/opa/v1/test/cli/smoke/input.json @@ -0,0 +1 @@ +{"yay": true} diff --git a/third_party/opa/v1/test/cli/smoke/namespace/data.json b/third_party/opa/v1/test/cli/smoke/namespace/data.json new file mode 100644 index 000000000000..e63d37b65a8a --- /dev/null +++ b/third_party/opa/v1/test/cli/smoke/namespace/data.json @@ -0,0 +1,3 @@ +{ + "foo": "bar" +} diff --git a/third_party/opa/v1/test/cli/smoke/test.rego b/third_party/opa/v1/test/cli/smoke/test.rego new file mode 100644 index 000000000000..33dfa774a545 --- /dev/null +++ b/third_party/opa/v1/test/cli/smoke/test.rego @@ -0,0 +1,8 @@ +package test +import future.keywords.if + +# METADATA +# entrypoint: true +result if foo[input.yay] + +foo := data.x.foo diff --git a/third_party/opa/v1/test/e2e/authz/authz_bench_integration_test.go b/third_party/opa/v1/test/e2e/authz/authz_bench_integration_test.go new file mode 100644 index 000000000000..8b3592d1ecad --- /dev/null +++ b/third_party/opa/v1/test/e2e/authz/authz_bench_integration_test.go @@ -0,0 +1,129 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. +package authz + +import ( + "bytes" + "encoding/json" + "flag" + "io" + "os" + "strings" + "testing" + + testAuthz "github.com/open-policy-agent/opa/v1/test/authz" + "github.com/open-policy-agent/opa/v1/test/e2e" + "github.com/open-policy-agent/opa/v1/util" +) + +var testRuntime *e2e.TestRuntime + +func TestMain(m *testing.M) { + flag.Parse() + + testServerParams := e2e.NewAPIServerTestParams() + disk, cleanup := diskStorage() + testServerParams.DiskStorage = disk + var err error + testRuntime, err = e2e.NewTestRuntime(testServerParams) + if err != nil { + os.Exit(1) + } + + errc := testRuntime.RunTests(m) + if cleanup != nil { + if err := cleanup(); err != nil { + panic(err) + } + } + os.Exit(errc) +} + +func BenchmarkRESTAuthzForbidAuthn(b *testing.B) { + runAuthzBenchmark(b, testAuthz.ForbidIdentity, 10) +} + +func BenchmarkRESTAuthzForbidPath(b *testing.B) { + runAuthzBenchmark(b, testAuthz.ForbidPath, 10) +} + +func BenchmarkRESTAuthzForbidMethod(b *testing.B) { + runAuthzBenchmark(b, testAuthz.ForbidMethod, 10) +} + +func BenchmarkRESTAuthzAllow10Paths(b *testing.B) { + runAuthzBenchmark(b, testAuthz.Allow, 10) +} + +func BenchmarkRESTAuthzAllow100Paths(b *testing.B) { + runAuthzBenchmark(b, testAuthz.Allow, 100) +} + +func BenchmarkRESTAuthzAllow1000Paths(b *testing.B) { + runAuthzBenchmark(b, testAuthz.Allow, 1000) +} + +func runAuthzBenchmark(b *testing.B, mode testAuthz.InputMode, numPaths int) { + // Generate test data and push it into the server + profile := testAuthz.DataSetProfile{ + NumTokens: 1000, + NumPaths: numPaths, + } + data := testAuthz.GenerateDataset(profile) + err := testRuntime.UploadData(bytes.NewReader(util.MustMarshalJSON(data))) + if err != nil { + b.Fatal(err) + } + + // Push the test policy + err = testRuntime.UploadPolicy("restauthz", strings.NewReader(testAuthz.Policy)) + if err != nil { + b.Fatal(err) + } + + queryPath := strings.ReplaceAll(testAuthz.AllowQuery, ".", "/") + url := testRuntime.URL() + "/v1/" + queryPath + + input, expected := testAuthz.GenerateInput(profile, mode) + inputPayload := util.MustMarshalJSON(map[string]any{ + "input": input, + }) + inputReader := bytes.NewReader(inputPayload) + + b.ResetTimer() + + for range b.N { + + // The benchmark will include the time it takes to make the request, + // receive a response, and do any normal client error checking on + // the response. The benchmark is for the OPA server, not how + // long it takes the golang client to unpack the response body. + b.StartTimer() + resp, err := testRuntime.GetDataWithRawInput(url, inputReader) + if err != nil { + b.Fatal(err) + } + b.StopTimer() + + body, err := io.ReadAll(resp) + if err != nil { + b.Fatalf("unexpected error reading response body: %s", err) + } + resp.Close() + + parsedBody := struct { + Result bool `json:"result"` + }{} + + err = json.Unmarshal(body, &parsedBody) + if err != nil { + b.Fatalf("Failed to parse body: \n\nActual: %s\n\nExpected: {\"result\": BOOL}\n\nerr = %s ", string(body), err) + } + if parsedBody.Result != expected { + b.Fatalf("Unexpected result: %v", parsedBody.Result) + } + + inputReader.Reset(inputPayload) + } +} diff --git a/third_party/opa/v1/test/e2e/authz/disk.go b/third_party/opa/v1/test/e2e/authz/disk.go new file mode 100644 index 000000000000..0917afb262cb --- /dev/null +++ b/third_party/opa/v1/test/e2e/authz/disk.go @@ -0,0 +1,24 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build bench_disk +// +build bench_disk + +// nolint: deadcode,unused // build tags confuse these linters +package authz + +import ( + "os" + + "github.com/open-policy-agent/opa/v1/storage/disk" +) + +func diskStorage() (*disk.Options, func() error) { + dir, err := os.MkdirTemp("", "disk-store") + if err != nil { + panic(err) + } + + return &disk.Options{Dir: dir, Partitions: nil}, func() error { return os.RemoveAll(dir) } +} diff --git a/third_party/opa/v1/test/e2e/authz/nodisk.go b/third_party/opa/v1/test/e2e/authz/nodisk.go new file mode 100644 index 000000000000..50109ea596c6 --- /dev/null +++ b/third_party/opa/v1/test/e2e/authz/nodisk.go @@ -0,0 +1,15 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build !bench_disk +// +build !bench_disk + +// nolint: deadcode,unused // build tags confuse these linters +package authz + +import "github.com/open-policy-agent/opa/v1/storage/disk" + +func diskStorage() (*disk.Options, func() error) { + return nil, nil +} diff --git a/third_party/opa/v1/test/e2e/certrefresh/certrefresh_test.go b/third_party/opa/v1/test/e2e/certrefresh/certrefresh_test.go new file mode 100644 index 000000000000..f4c777aa777e --- /dev/null +++ b/third_party/opa/v1/test/e2e/certrefresh/certrefresh_test.go @@ -0,0 +1,191 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package certrefresh + +import ( + "crypto/tls" + "crypto/x509" + "flag" + "fmt" + "io" + "net/http" + "net/url" + "os" + "path/filepath" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/test/e2e" +) + +var testRuntime *e2e.TestRuntime +var pool *x509.CertPool + +// print error to stderr, exit 1 +func fatal(err any) { + fmt.Fprintf(os.Stderr, "%s\n", err) + os.Exit(1) +} + +const ( + certFile0 = "testdata/server-cert.pem" + certKeyFile0 = "testdata/server-key.pem" + serial0 = "481849676048721749484276160748693385016044597443" + certFile1 = "testdata/server-cert-new.pem" + certKeyFile1 = "testdata/server-key-new.pem" + serial1 = "481849676048721749484276160748693385016044597444" +) + +var certFile, certKeyFile string + +func TestMain(m *testing.M) { + flag.Parse() + caCertPEM, err := os.ReadFile("testdata/ca.pem") + if err != nil { + fatal(err) + } + pool = x509.NewCertPool() + if ok := pool.AppendCertsFromPEM(caCertPEM); !ok { + fatal("failed to parse CA cert") + } + + tmp, err := os.MkdirTemp("", "e2e_certrefresh") + if err != nil { + fatal(err) + } + defer os.RemoveAll(tmp) + + certFile = filepath.Join(tmp, "server-cert.pem") + if err := cpy(certFile0, certFile); err != nil { + fatal(err) + } + + certKeyFile = filepath.Join(tmp, "server-key.pem") + if err := cpy(certKeyFile0, certKeyFile); err != nil { + fatal(err) + } + + cert, err := tls.LoadX509KeyPair(certFile, certKeyFile) + if err != nil { + fatal(err) + } + + testServerParams := e2e.NewAPIServerTestParams() + testServerParams.Addrs = &[]string{"https://127.0.0.1:0"} + testServerParams.CertPool = pool + testServerParams.Certificate = &cert + testServerParams.CertificateFile = certFile + testServerParams.CertificateKeyFile = certKeyFile + testServerParams.CertificateRefresh = time.Millisecond + + testRuntime, err = e2e.NewTestRuntime(testServerParams) + if err != nil { + fatal(err) + } + + // We need a client with proper TLS setup, otherwise the health check + // that loops to determine if the server is ready will fail. + testRuntime.Client = newClient() + + os.Exit(testRuntime.RunTests(m)) +} + +func TestCertificateRotation(t *testing.T) { + wait := 20 * time.Millisecond // file reload happens every millisecond + + // before rotation + cert := getCert(t) + if exp, act := serial0, cert.SerialNumber.String(); exp != act { + t.Fatalf("expected signature %s, got %s", exp, act) + } + + // replace file on disk + replaceCerts(t, certFile1, certKeyFile1) + time.Sleep(wait) + + // after rotation + cert = getCert(t) + if exp, act := serial1, cert.SerialNumber.String(); exp != act { + t.Fatalf("expected signature %s, got %s", exp, act) + } + + // replace file with nothing + replaceCerts(t, os.DevNull, os.DevNull) + time.Sleep(wait) + + // second cert still used + cert = getCert(t) + if exp, act := serial1, cert.SerialNumber.String(); exp != act { + t.Fatalf("expected signature %s, got %s", exp, act) + } + + // go back to first cert + replaceCerts(t, certFile0, certKeyFile0) + time.Sleep(wait) + cert = getCert(t) + if exp, act := serial0, cert.SerialNumber.String(); exp != act { + t.Fatalf("expected signature %s, got %s", exp, act) + } +} + +func newClient() *http.Client { + c := *http.DefaultClient + tr := http.DefaultTransport.(*http.Transport).Clone() + tr.TLSClientConfig = &tls.Config{ + RootCAs: pool, + } + c.Transport = tr + return &c +} + +func cpy(from, to string) error { + src, err := os.Open(from) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.Create(to) + if err != nil { + return err + } + defer dst.Close() + + _, err = io.Copy(dst, src) + if err != nil { + return err + } + + // Ensure that our writes get committed to disk, even on slower systems. + return dst.Sync() +} + +func getCert(t *testing.T) *x509.Certificate { + t.Helper() + u, err := url.Parse(testRuntime.URL()) + if err != nil { + t.Fatal(err) + } + c := newClient() + cfg := c.Transport.(*http.Transport).TLSClientConfig + conn, err := tls.Dial("tcp", u.Host, cfg) + if err != nil { + t.Fatalf("dial: %v", err) + } + defer conn.Close() + + return conn.ConnectionState().PeerCertificates[0] +} + +func replaceCerts(t *testing.T, cert, key string) { + t.Helper() + + if err := cpy(cert, certFile); err != nil { + t.Fatal(err) + } + if err := cpy(key, certKeyFile); err != nil { + t.Fatal(err) + } +} diff --git a/third_party/opa/v1/test/e2e/certrefresh/testdata/.gitignore b/third_party/opa/v1/test/e2e/certrefresh/testdata/.gitignore new file mode 100644 index 000000000000..e7fe15b29bb4 --- /dev/null +++ b/third_party/opa/v1/test/e2e/certrefresh/testdata/.gitignore @@ -0,0 +1,4 @@ +*.srl +*.cnf +csr.pem +ca-key.pem diff --git a/third_party/opa/v1/test/e2e/certrefresh/testdata/gencerts.sh b/third_party/opa/v1/test/e2e/certrefresh/testdata/gencerts.sh new file mode 100755 index 000000000000..c6df4a869042 --- /dev/null +++ b/third_party/opa/v1/test/e2e/certrefresh/testdata/gencerts.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# adapted from +# https://github.com/dexidp/dex/blob/2d1ac74ec0ca12ae4d36072525d976c1a596820a/examples/k8s/gencert.sh#L22 + +cat <req.cnf +[req] +req_extensions = v3_req +distinguished_name = req_distinguished_name + +[req_distinguished_name] + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = opa.example.com +IP.1 = 127.0.0.1 +EOF + +openssl genrsa -out ca-key.pem 2048 +openssl req -x509 -new -nodes -key ca-key.pem -days 3650 -out ca.pem -subj "/CN=my-ca" + +openssl genrsa -out server-key.pem 2048 +openssl req -new -key server-key.pem -out csr.pem -subj "/CN=my-server" -config req.cnf +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 3650 -extensions v3_req -extfile req.cnf + +openssl genrsa -out server-key-new.pem 2048 +openssl req -new -key server-key-new.pem -out csr.pem -subj "/CN=my-server" -config req.cnf +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert-new.pem -days 3650 -extensions v3_req -extfile req.cnf diff --git a/third_party/opa/v1/test/e2e/concurrency/concurrency_test.go b/third_party/opa/v1/test/e2e/concurrency/concurrency_test.go new file mode 100644 index 000000000000..13d74bb3fcd9 --- /dev/null +++ b/third_party/opa/v1/test/e2e/concurrency/concurrency_test.go @@ -0,0 +1,123 @@ +package concurrency + +import ( + "flag" + "os" + "runtime" + "strings" + "sync" + "testing" + + "github.com/open-policy-agent/opa/v1/server/types" + "github.com/open-policy-agent/opa/v1/storage/disk" + "github.com/open-policy-agent/opa/v1/test/e2e" +) + +var testRuntime *e2e.TestRuntime + +func TestMain(m *testing.M) { + flag.Parse() + testServerParams := e2e.NewAPIServerTestParams() + + dir, err := os.MkdirTemp("", "disk-store") + if err != nil { + panic(err) + } + defer func() { os.RemoveAll(dir) }() + + for _, opts := range []*disk.Options{ + nil, + {Dir: dir, Partitions: nil}, + } { + var err error + testServerParams.DiskStorage = opts + testRuntime, err = e2e.NewTestRuntime(testServerParams) + if err != nil { + panic(err) + } + if ec := testRuntime.RunTests(m); ec != 0 { + os.Exit(ec) + } + } +} + +func TestConcurrencyGetV1Data(t *testing.T) { + + policy := ` + package test + p = true + ` + + err := testRuntime.UploadPolicy(t.Name(), strings.NewReader(policy)) + if err != nil { + t.Fatal(err) + } + + var wg sync.WaitGroup + num := runtime.NumCPU() + wg.Add(num) + + for range num { + go func() { + defer wg.Done() + for range 1000 { + dr := struct { + Result bool `json:"result"` + }{} + if err := testRuntime.GetDataWithInputTyped("test/p", nil, &dr); err != nil { + t.Error(err) + return + } + if !dr.Result { + t.Errorf("Unexpected response: %+v", dr) + return + } + } + }() + } + + wg.Wait() +} + +func TestConcurrencyCompile(t *testing.T) { + + policy := ` + package test + import rego.v1 + + f(_) + p if { + not q + } + q if { + not f(input.foo) + } + ` + + err := testRuntime.UploadPolicy(t.Name(), strings.NewReader(policy)) + if err != nil { + t.Fatal(err) + } + + req := types.CompileRequestV1{ + Query: "data.test.p", + } + + var wg sync.WaitGroup + num := runtime.NumCPU() + wg.Add(num) + + for range num { + go func() { + defer wg.Done() + for range 1000 { + if _, err := testRuntime.CompileRequest(req); err != nil { + t.Error(err) + return + } + } + }() + } + + wg.Wait() +} diff --git a/third_party/opa/v1/test/e2e/diagnostics/diagnostics_test.go b/third_party/opa/v1/test/e2e/diagnostics/diagnostics_test.go new file mode 100644 index 000000000000..d657a4b66d24 --- /dev/null +++ b/third_party/opa/v1/test/e2e/diagnostics/diagnostics_test.go @@ -0,0 +1,101 @@ +package diagnostics + +import ( + "flag" + "fmt" + "net/http" + "os" + "testing" + + "github.com/open-policy-agent/opa/v1/test/e2e" +) + +var testRuntime *e2e.TestRuntime + +func TestMain(m *testing.M) { + flag.Parse() + testServerParams := e2e.NewAPIServerTestParams() + testServerParams.Addrs = &[]string{"localhost:0"} + testServerParams.DiagnosticAddrs = &[]string{"localhost:0"} + + var err error + testRuntime, err = e2e.NewTestRuntimeWithOpts(e2e.TestRuntimeOpts{}, testServerParams) + if err != nil { + os.Exit(1) + } + + os.Exit(testRuntime.RunTests(m)) +} + +func TestServerWithDiagnosticAddrHealthCheck(t *testing.T) { + if err := testRuntime.HealthCheck(diagURL(t)); err != nil { + t.Fatal(err) + } + + // Ensure the "main" listener is still OK + if err := testRuntime.HealthCheck(testRuntime.URL()); err != nil { + t.Fatal(err) + } +} + +func TestServerWithDiagnosticAddrProtectedAPIs(t *testing.T) { + cases := []string{ + "/", + "/v0/data", + "/v0/data/foo", + "/v1/data", + "/v1/data/foo", + "/v1/policies", + "/v1/policies/foo", + "/v1/query", + "/v1/compile", + } + + baseURL := diagURL(t) + + methods := []string{ + http.MethodGet, + http.MethodPost, + http.MethodPut, + http.MethodTrace, + http.MethodPatch, + http.MethodConnect, + http.MethodDelete, + http.MethodOptions, + http.MethodHead, + } + + for _, tc := range cases { + url := baseURL + tc + for _, method := range methods { + t.Run(fmt.Sprintf("%s %s", method, tc), func(t *testing.T) { + assert404(t, method, url) + }) + } + } +} + +func diagURL(t *testing.T) string { + t.Helper() + addr := testRuntime.Runtime.DiagnosticAddrs()[0] + diagURL, err := testRuntime.AddrToURL(addr) + if err != nil { + t.Error("Unexpected error: ", err) + } + return diagURL +} + +func assert404(t *testing.T, method string, url string) { + t.Helper() + req, err := http.NewRequest(method, url, nil) + if err != nil { + t.Errorf("Unexpected error creating request: %s", err) + } + resp, err := testRuntime.Client.Do(req) + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + if resp.StatusCode != http.StatusNotFound { + t.Errorf("Unexpected response, expected 404, got: %d %s", resp.StatusCode, resp.Status) + } +} diff --git a/third_party/opa/v1/test/e2e/distributedtracing/distributedtracing_test.go b/third_party/opa/v1/test/e2e/distributedtracing/distributedtracing_test.go new file mode 100644 index 000000000000..660c46b89780 --- /dev/null +++ b/third_party/opa/v1/test/e2e/distributedtracing/distributedtracing_test.go @@ -0,0 +1,915 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package distributedtracing + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "os" + "strconv" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/logging/test" + "github.com/open-policy-agent/opa/v1/plugins/bundle" + "github.com/open-policy-agent/opa/v1/plugins/discovery" + "github.com/open-policy-agent/opa/v1/plugins/logs" + "github.com/open-policy-agent/opa/v1/plugins/status" + "github.com/open-policy-agent/opa/v1/runtime" + opasdktest "github.com/open-policy-agent/opa/v1/sdk/test" + "github.com/open-policy-agent/opa/v1/server" + "github.com/open-policy-agent/opa/v1/test/e2e" + "github.com/open-policy-agent/opa/v1/tracing" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" +) + +var testRuntime *e2e.TestRuntime +var spanExporter *tracetest.InMemoryExporter + +func TestMain(m *testing.M) { + spanExporter = tracetest.NewInMemoryExporter() + options := tracing.NewOptions( + otelhttp.WithTracerProvider(trace.NewTracerProvider(trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(spanExporter)))), + ) + + flag.Parse() + testServerParams := e2e.NewAPIServerTestParams() + testServerParams.DistributedTracingOpts = options + testServerParams.Addrs = &[]string{"localhost:0"} + + var err error + testRuntime, err = e2e.NewTestRuntime(testServerParams) + + if err != nil { + os.Exit(1) + } + + os.Exit(testRuntime.RunTests(m)) +} + +// TestServerSpan exemplarily asserts that the server handlers emit OpenTelemetry spans +// with the correct attributes. It does NOT exercise all handlers, but contains one test +// with a GET and one with a POST. +func TestServerSpan(t *testing.T) { + spanExporter.Reset() + + t.Run("POST v0/data", func(t *testing.T) { + t.Cleanup(spanExporter.Reset) + + mr, err := http.Post(testRuntime.URL()+"/v0/data", "application/json", nil) + if err != nil { + t.Fatal(err) + } + defer mr.Body.Close() + + spans := spanExporter.GetSpans() + if got, expected := len(spans), 1; got != expected { + t.Fatalf("got %d span(s), expected %d", got, expected) + } + if !spans[0].SpanContext.IsValid() { + t.Fatalf("invalid span created: %#v", spans[0].SpanContext) + } + if got, expected := spans[0].Name, "v0/data"; got != expected { + t.Fatalf("Expected span name to be %q but got %q", expected, got) + } + if got, expected := spans[0].SpanKind.String(), "server"; got != expected { + t.Fatalf("Expected span kind to be %q but got %q", expected, got) + } + + u, err := url.Parse(testRuntime.URL()) + if err != nil { + t.Fatal(err) + } + port, err := strconv.Atoi(u.Port()) + if err != nil { + t.Fatal(err) + } + expected := []any{ + attribute.String("server.address", u.Hostname()), + attribute.Int("server.port", port), + attribute.String("network.protocol.version", "1.1"), + attribute.String("network.peer.address", "127.0.0.1"), + attribute.Key("network.peer.port"), + attribute.String("http.request.method", "POST"), + attribute.String("url.scheme", "http"), + attribute.String("url.path", "/v0/data"), + attribute.Int("http.response.status_code", 200), + attribute.Int("http.response.body.size", 3), + attribute.String("user_agent.original", "Go-http-client/1.1"), + } + + compareSpanAttributes(t, expected, attribute.NewSet(spans[0].Attributes...)) + }) + + t.Run("GET v1/data", func(t *testing.T) { + t.Cleanup(spanExporter.Reset) + + mr, err := http.Get(testRuntime.URL() + "/v1/data") + if err != nil { + t.Fatal(err) + } + defer mr.Body.Close() + + spans := spanExporter.GetSpans() + if got, expected := len(spans), 1; got != expected { + t.Fatalf("got %d span(s), expected %d", got, expected) + } + if !spans[0].SpanContext.IsValid() { + t.Fatalf("invalid span created: %#v", spans[0].SpanContext) + } + if got, expected := spans[0].Name, "v1/data"; got != expected { + t.Fatalf("Expected span name to be %q but got %q", expected, got) + } + if got, expected := spans[0].SpanKind.String(), "server"; got != expected { + t.Fatalf("Expected span kind to be %q but got %q", expected, got) + } + + u, err := url.Parse(testRuntime.URL()) + if err != nil { + t.Fatal(err) + } + port, err := strconv.Atoi(u.Port()) + if err != nil { + t.Fatal(err) + } + expected := []any{ + attribute.String("server.address", u.Hostname()), + attribute.Int("server.port", port), + attribute.String("network.protocol.version", "1.1"), + attribute.String("network.peer.address", "127.0.0.1"), + attribute.Key("network.peer.port"), + attribute.String("http.request.method", "GET"), + attribute.String("url.scheme", "http"), + attribute.String("url.path", "/v1/data"), + attribute.Int("http.response.status_code", 200), + attribute.Int("http.response.body.size", 67), + attribute.String("user_agent.original", "Go-http-client/1.1"), + } + compareSpanAttributes(t, expected, attribute.NewSet(spans[0].Attributes...)) + }) +} + +func TestServerSpanWithDecisionLogging(t *testing.T) { + // setup + spanExp := tracetest.NewInMemoryExporter() + options := tracing.NewOptions( + otelhttp.WithTracerProvider(trace.NewTracerProvider(trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(spanExp)))), + ) + + testServerParams := e2e.NewAPIServerTestParams() + testServerParams.ConfigOverrides = []string{ + "decision_logs.console=true", + } + + // Ensure decisions are logged regardless of regular log level + testServerParams.Logging = runtime.LoggingConfig{Level: "error"} + consoleLogger := test.New() + testServerParams.ConsoleLogger = consoleLogger + + testServerParams.DistributedTracingOpts = options + + e2e.WithRuntime(t, e2e.TestRuntimeOpts{}, testServerParams, func(rt *e2e.TestRuntime) { + + spanExp.Reset() + rt.ConsoleLogger = consoleLogger + + mr, err := http.Post(rt.URL()+"/v1/data", "application/json", nil) + if err != nil { + t.Fatal(err) + } + defer mr.Body.Close() + + if mr.StatusCode != http.StatusOK { + t.Fatalf("expected status %v but got %v", http.StatusOK, mr.StatusCode) + } + + spans := spanExp.GetSpans() + if got, expected := len(spans), 1; got != expected { + t.Fatalf("got %d span(s), expected %d", got, expected) + } + if !spans[0].SpanContext.IsValid() { + t.Fatalf("invalid span created: %#v", spans[0].SpanContext) + } + + if got, expected := spans[0].SpanKind.String(), "server"; got != expected { + t.Fatalf("Expected span kind to be %q but got %q", expected, got) + } + + var entry test.LogEntry + var found bool + + for _, entry = range rt.ConsoleLogger.Entries() { + if entry.Message == "Decision Log" { + found = true + } + } + + if !found { + t.Fatalf("Did not find 'Decision Log' event in captured log entries") + } + + // Check for some important fields + expectedFields := map[string]*struct { + found bool + match func(*testing.T, string) + }{ + "labels": {}, + "decision_id": {}, + "trace_id": {}, + "span_id": {}, + "result": {}, + "timestamp": {}, + "type": {match: func(t *testing.T, actual string) { + if actual != "openpolicyagent.org/decision_logs" { + t.Fatalf("Expected field 'type' to be 'openpolicyagent.org/decision_logs'") + } + }}, + } + + // Ensure expected fields exist + for fieldName, rawField := range entry.Fields { + if fd, ok := expectedFields[fieldName]; ok { + if fieldValue, ok := rawField.(string); ok && fd.match != nil { + fd.match(t, fieldValue) + } + fd.found = true + } + } + + for field, fd := range expectedFields { + if !fd.found { + t.Errorf("Missing expected field in decision log: %s\n\nEntry: %+v\n\n", field, entry) + } + } + }) +} + +// TestClientSpan asserts that for all handlers that end up evaluating policies, the +// http.send calls will emit the proper spans related to the incoming requests. +// +// NOTE(sr): `{GET,POST} v1/query` are omitted, http.send is forbidden for ad-hoc queries +func TestClientSpan(t *testing.T) { + type resp struct { + DecisionID string `json:"decision_id"` + } + + policy := ` + package test + + response := http.send({"method": "get", "url": "%s/health"}) + ` + + policy = fmt.Sprintf(policy, testRuntime.URL()) + err := testRuntime.UploadPolicy(t.Name(), strings.NewReader(policy)) + if err != nil { + t.Fatal(err) + } + spanExporter.Reset() + + t.Run("POST v0/data", func(t *testing.T) { + t.Cleanup(spanExporter.Reset) + + mr, err := http.Post(testRuntime.URL()+"/v0/data/test", "application/json", nil) + if err != nil { + t.Fatal(err) + } + defer mr.Body.Close() + + spans := spanExporter.GetSpans() + + // Ordered by span emission, which is the reverse of the processing + // code flow: + // 3 = GET /health (HTTP server handler) + // + http.send (HTTP client instrumentation) + // + GET /v1/data/test (HTTP server handler) + if got, expected := len(spans), 3; got != expected { + t.Fatalf("got %d span(s), expected %d", got, expected) + } + if !spans[1].SpanContext.IsValid() { + t.Fatalf("invalid span created: %#v", spans[1].SpanContext) + } + if got, expected := spans[1].SpanKind.String(), "client"; got != expected { + t.Fatalf("Expected span kind to be %q but got %q", expected, got) + } + + parentSpanID := spans[2].SpanContext.SpanID() + if got, expected := spans[1].Parent.SpanID(), parentSpanID; got != expected { + t.Errorf("expected span to be child of %v, got parent %v", expected, got) + } + + expected := []any{ + attribute.String("http.request.method", "GET"), + attribute.String("url.full", testRuntime.URL()+"/health"), + attribute.Int("http.response.status_code", 200), + attribute.String("server.address", "127.0.0.1"), + attribute.Key("server.port"), + attribute.String("network.protocol.version", "1.1"), + } + compareSpanAttributes(t, expected, attribute.NewSet(spans[1].Attributes...)) + }) + + t.Run("GET v1/data", func(t *testing.T) { + t.Cleanup(spanExporter.Reset) + + mr, err := http.Get(testRuntime.URL() + "/v1/data/test") + if err != nil { + t.Fatal(err) + } + defer mr.Body.Close() + var r resp + if err := json.NewDecoder(mr.Body).Decode(&r); err != nil { + t.Fatal(err) + } + if r.DecisionID == "" { + t.Fatal("expected decision id") + } + + spans := spanExporter.GetSpans() + if got, expected := len(spans), 3; got != expected { + t.Fatalf("got %d span(s), expected %d", got, expected) + } + if !spans[1].SpanContext.IsValid() { + t.Fatalf("invalid span created: %#v", spans[1].SpanContext) + } + if got, expected := spans[1].SpanKind.String(), "client"; got != expected { + t.Fatalf("Expected span kind to be %q but got %q", expected, got) + } + + parentSpanID := spans[2].SpanContext.SpanID() + if got, expected := spans[1].Parent.SpanID(), parentSpanID; got != expected { + t.Errorf("expected span to be child of %v, got parent %v", expected, got) + } + + expected := []any{ + attribute.String("http.request.method", "GET"), + attribute.String("url.full", testRuntime.URL()+"/health"), + attribute.Int("http.response.status_code", 200), + attribute.String("server.address", "127.0.0.1"), + attribute.Key("server.port"), + attribute.String("network.protocol.version", "1.1"), + } + compareSpanAttributes(t, expected, attribute.NewSet(spans[1].Attributes...)) + + // The (parent) server span carries the decision ID + expected = []any{ + attribute.String("opa.decision_id", r.DecisionID), + } + compareSpanAttributes(t, expected, attribute.NewSet(spans[2].Attributes...)) + }) + + t.Run("POST v1/data", func(t *testing.T) { + t.Cleanup(spanExporter.Reset) + + payload := strings.NewReader(`{"input": "meow"}`) + mr, err := http.Post(testRuntime.URL()+"/v1/data/test", "application/json", payload) + if err != nil { + t.Fatal(err) + } + defer mr.Body.Close() + var r resp + if err := json.NewDecoder(mr.Body).Decode(&r); err != nil { + t.Fatal(err) + } + if r.DecisionID == "" { + t.Fatal("expected decision id") + } + + spans := spanExporter.GetSpans() + if got, expected := len(spans), 3; got != expected { + t.Fatalf("got %d span(s), expected %d", got, expected) + } + if !spans[1].SpanContext.IsValid() { + t.Fatalf("invalid span created: %#v", spans[1].SpanContext) + } + if got, expected := spans[1].SpanKind.String(), "client"; got != expected { + t.Fatalf("Expected span kind to be %q but got %q", expected, got) + } + + parentSpanID := spans[2].SpanContext.SpanID() + if got, expected := spans[1].Parent.SpanID(), parentSpanID; got != expected { + t.Errorf("expected span to be child of %v, got parent %v", expected, got) + } + + expected := []any{ + attribute.String("http.request.method", "GET"), + attribute.String("url.full", testRuntime.URL()+"/health"), + attribute.Int("http.response.status_code", 200), + attribute.String("server.address", "127.0.0.1"), + attribute.Key("server.port"), + attribute.String("network.protocol.version", "1.1"), + } + compareSpanAttributes(t, expected, attribute.NewSet(spans[1].Attributes...)) + + // The (parent) server span carries the decision ID + expected = []any{ + attribute.String("opa.decision_id", r.DecisionID), + } + compareSpanAttributes(t, expected, attribute.NewSet(spans[2].Attributes...)) + }) + + t.Run("POST /", func(t *testing.T) { + t.Cleanup(spanExporter.Reset) + + main := fmt.Sprintf(` + package system.main + + response := http.send({"method": "get", "url": "%s/health"}) + `, testRuntime.URL()) + err := testRuntime.UploadPolicy("system.main", strings.NewReader(main)) + if err != nil { + t.Fatal(err) + } + spanExporter.Reset() + + mr, err := http.Post(testRuntime.URL()+"/", "application/json", nil) + if err != nil { + t.Fatal(err) + } + defer mr.Body.Close() + + spans := spanExporter.GetSpans() + if got, expected := len(spans), 3; got != expected { + t.Fatalf("got %d span(s), expected %d", got, expected) + } + if !spans[1].SpanContext.IsValid() { + t.Fatalf("invalid span created: %#v", spans[1].SpanContext) + } + if got, expected := spans[1].SpanKind.String(), "client"; got != expected { + t.Fatalf("Expected span kind to be %q but got %q", expected, got) + } + + parentSpanID := spans[2].SpanContext.SpanID() + if got, expected := spans[1].Parent.SpanID(), parentSpanID; got != expected { + t.Errorf("expected span to be child of %v, got parent %v", expected, got) + } + + expected := []any{ + attribute.String("http.request.method", "GET"), + attribute.String("url.full", testRuntime.URL()+"/health"), + attribute.Int("http.response.status_code", 200), + attribute.String("server.address", "127.0.0.1"), + attribute.Key("server.port"), + attribute.String("network.protocol.version", "1.1"), + } + compareSpanAttributes(t, expected, attribute.NewSet(spans[1].Attributes...)) + }) +} + +func TestClientSpanWithDecisionLogging(t *testing.T) { + // setup + spanExp := tracetest.NewInMemoryExporter() + options := tracing.NewOptions( + otelhttp.WithTracerProvider(trace.NewTracerProvider(trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(spanExp)))), + ) + + testServerParams := e2e.NewAPIServerTestParams() + testServerParams.ConfigOverrides = []string{ + "decision_logs.console=true", + } + + // Ensure decisions are logged regardless of regular log level + testServerParams.Logging = runtime.LoggingConfig{Level: "error"} + consoleLogger := test.New() + testServerParams.ConsoleLogger = consoleLogger + + testServerParams.DistributedTracingOpts = options + + e2e.WithRuntime(t, e2e.TestRuntimeOpts{}, testServerParams, func(rt *e2e.TestRuntime) { + + spanExp.Reset() + rt.ConsoleLogger = consoleLogger + + policy := ` + package test + + response := http.send({"method": "get", "url": "%s/health"}) + ` + + policy = fmt.Sprintf(policy, testRuntime.URL()) + err := rt.UploadPolicy(t.Name(), strings.NewReader(policy)) + if err != nil { + t.Fatal(err) + } + + mr, err := http.Post(rt.URL()+"/v1/data/test", "application/json", nil) + if err != nil { + t.Fatal(err) + } + defer mr.Body.Close() + + if mr.StatusCode != http.StatusOK { + t.Fatalf("expected status %v but got %v", http.StatusOK, mr.StatusCode) + } + + spans := spanExp.GetSpans() + // Ordered by span emission, which is the reverse of the processing + // code flow: + // 3 = GET /health (HTTP server handler) + // + http.send (HTTP client instrumentation) + // + GET /v1/data/test (HTTP server handler) + if got, expected := len(spans), 3; got != expected { + t.Fatalf("got %d span(s), expected %d", got, expected) + } + + if !spans[1].SpanContext.IsValid() { + t.Fatalf("invalid span created: %#v", spans[1].SpanContext) + } + if got, expected := spans[1].SpanKind.String(), "client"; got != expected { + t.Fatalf("Expected span kind to be %q but got %q", expected, got) + } + + parentTraceID := spans[2].SpanContext.TraceID() + parentSpanID := spans[2].SpanContext.SpanID() + if got, expected := spans[1].Parent.SpanID(), parentSpanID; got != expected { + t.Errorf("expected span to be child of %v, got parent %v", expected, got) + } + + var entry test.LogEntry + var found bool + + for _, entry = range rt.ConsoleLogger.Entries() { + if entry.Message == "Decision Log" { + found = true + } + } + + if !found { + t.Fatalf("Did not find 'Decision Log' event in captured log entries") + } + + // Check for some important fields + expectedFields := map[string]*struct { + found bool + match func(*testing.T, string) + }{ + "labels": {}, + "decision_id": {}, + "trace_id": {match: func(t *testing.T, actual string) { + if actual != parentTraceID.String() { + t.Fatalf("Expected field 'trace_id' to be %v", parentTraceID.String()) + } + }}, + "span_id": {match: func(t *testing.T, actual string) { + if actual != parentSpanID.String() { + t.Fatalf("Expected field 'span_id' to be %v", parentSpanID.String()) + } + }}, + "result": {}, + "timestamp": {}, + "type": {match: func(t *testing.T, actual string) { + if actual != "openpolicyagent.org/decision_logs" { + t.Fatalf("Expected field 'type' to be 'openpolicyagent.org/decision_logs'") + } + }}, + } + + // Ensure expected fields exist + for fieldName, rawField := range entry.Fields { + if fd, ok := expectedFields[fieldName]; ok { + if fieldValue, ok := rawField.(string); ok && fd.match != nil { + fd.match(t, fieldValue) + } + fd.found = true + } + } + + for field, fd := range expectedFields { + if !fd.found { + t.Errorf("Missing expected field in decision log: %s\n\nEntry: %+v\n\n", field, entry) + } + } + }) +} + +func TestServerSpanWithSystemAuthzPolicy(t *testing.T) { + + // setup + spanExp := tracetest.NewInMemoryExporter() + options := tracing.NewOptions( + otelhttp.WithTracerProvider(trace.NewTracerProvider(trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(spanExp)))), + ) + + authzPolicy := []byte(`package system.authz +import rego.v1 +default allow = false +allow if { + input.path = ["health"] +}`) + + tmpfile, err := os.CreateTemp(t.TempDir(), "authz.*.rego") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpfile.Name()) + + if _, err := tmpfile.Write(authzPolicy); err != nil { + t.Fatal(err) + } + if err := tmpfile.Close(); err != nil { + t.Fatal(err) + } + + testServerParams := e2e.NewAPIServerTestParams() + testServerParams.DistributedTracingOpts = options + testServerParams.Authorization = server.AuthorizationBasic + testServerParams.Paths = []string{"system.authz:" + tmpfile.Name()} + + e2e.WithRuntime(t, e2e.TestRuntimeOpts{}, testServerParams, func(rt *e2e.TestRuntime) { + + spanExp.Reset() + + mr, err := http.Post(rt.URL()+"/v1/data", "application/json", nil) + if err != nil { + t.Fatal(err) + } + defer mr.Body.Close() + + if mr.StatusCode != http.StatusUnauthorized { + t.Fatalf("expected status %v but got %v", http.StatusUnauthorized, mr.StatusCode) + } + + spans := spanExp.GetSpans() + if got, expected := len(spans), 1; got != expected { + t.Fatalf("got %d span(s), expected %d", got, expected) + } + if !spans[0].SpanContext.IsValid() { + t.Fatalf("invalid span created: %#v", spans[0].SpanContext) + } + if got, expected := spans[0].Name, server.PromHandlerAPIAuthz; got != expected { + t.Fatalf("Expected span name to be %q but got %q", expected, got) + } + if got, expected := spans[0].SpanKind.String(), "server"; got != expected { + t.Fatalf("Expected span kind to be %q but got %q", expected, got) + } + + u := mr.Request.URL + port, err := strconv.Atoi(u.Port()) + if err != nil { + t.Fatal(err) + } + + expected := []any{ + attribute.String("server.address", u.Hostname()), + attribute.Int("server.port", port), + attribute.String("network.protocol.version", "1.1"), + attribute.String("network.peer.address", "127.0.0.1"), + attribute.Key("network.peer.port"), + attribute.String("http.request.method", "POST"), + attribute.String("url.scheme", "http"), + attribute.String("url.path", "/v1/data"), + attribute.Int("http.response.status_code", 401), + attribute.Int("http.response.body.size", 87), + attribute.String("user_agent.original", "Go-http-client/1.1"), + } + compareSpanAttributes(t, expected, attribute.NewSet(spans[0].Attributes...)) + + }) +} + +func TestControlPlaneSpans(t *testing.T) { + // setup + spanExp := tracetest.NewInMemoryExporter() + options := tracing.NewOptions( + otelhttp.WithTracerProvider(trace.NewTracerProvider(trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(spanExp)))), + ) + + opaControlPlane := opasdktest.MustNewServer( + opasdktest.MockBundle("/bundles/test", map[string]string{ + "main.rego": ` + package main + + default allow = false + `, + }), + opasdktest.MockBundle("/bundles/discovery", map[string]string{ + "data.json": ` + {"discovery":{"bundles":{"bundles/test":{"persist":false,"resource":"bundles/test","service":"bundleregistry", "trigger":"manual"}}}} + `, + }), + ) + defer opaControlPlane.Stop() + + controlPlaneURL, err := url.Parse(opaControlPlane.URL()) + if err != nil { + t.Fatal(err) + } + + controlPlanePort, err := strconv.Atoi(controlPlaneURL.Port()) + if err != nil { + t.Fatal(err) + } + + ts := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) { + })) + defer ts.Close() + + statusURL, err := url.Parse(ts.URL) + if err != nil { + t.Fatal(err) + } + + statusPort, err := strconv.Atoi(statusURL.Port()) + if err != nil { + t.Fatal(err) + } + + testServerParams := e2e.NewAPIServerTestParams() + testServerParams.ConfigOverrides = []string{ + "services.bundleregistry.url=" + opaControlPlane.URL(), + "services.observability.url=" + ts.URL, + "discovery.name=discovery", + "discovery.resource=/bundles/discovery", + "discovery.service=bundleregistry", + "discovery.trigger=manual", + "status.service=observability", + "status.trigger=manual", + "decision_logs.service=bundleregistry", + "decision_logs.reporting.trigger=manual", + } + + testServerParams.DistributedTracingOpts = options + testServerParams.ReadyTimeout = 5 + testServerParams.Logging = runtime.LoggingConfig{Level: "debug"} + + manualTriggers := func(rt *e2e.TestRuntime) error { + err := discovery.Lookup(rt.Runtime.Manager).Trigger(rt.Ctx) + if err != nil { + return err + } + err = bundle.Lookup(rt.Runtime.Manager).Trigger(rt.Ctx) + if err != nil { + return err + } + return status.Lookup(rt.Runtime.Manager).Trigger(rt.Ctx) + } + + e2e.WithRuntime(t, e2e.TestRuntimeOpts{PostServeActions: manualTriggers}, testServerParams, func(rt *e2e.TestRuntime) { + // We expect 3 spans: + // 1. GET /bundles/discovery (client) + // 2. GET /bundles/test (client) + // 3. POST /status (client) + // 4. health check (server) + + spans := spanExp.GetSpans() + if got, expected := len(spans), 4; got != expected { + t.Fatalf("got %d span(s), expected %d", got, expected) + } + for _, span := range spans { + if !span.SpanContext.IsValid() { + t.Fatalf("invalid span created: %#v", span.SpanContext) + } + } + + for idx := range 3 { + if got, expected := spans[idx].SpanKind.String(), "client"; got != expected { + t.Fatalf("Expected span kind to be %q but got %q", expected, got) + } + } + + u := controlPlaneURL + port := controlPlanePort + + expected := []any{ + attribute.String("server.address", u.Hostname()), + attribute.Int("server.port", port), + attribute.String("http.request.method", "GET"), + attribute.String("url.full", u.String()+"/bundles/discovery"), + attribute.Int("http.response.status_code", 200), + attribute.String("network.protocol.version", "1.1"), + } + compareSpanAttributes(t, expected, attribute.NewSet(spans[0].Attributes...)) + + expected = []any{ + attribute.String("server.address", u.Hostname()), + attribute.Int("server.port", port), + attribute.String("http.request.method", "GET"), + attribute.String("url.full", u.String()+"/bundles/test"), + attribute.Int("http.response.status_code", 200), + attribute.String("network.protocol.version", "1.1"), + } + compareSpanAttributes(t, expected, attribute.NewSet(spans[1].Attributes...)) + + expected = []any{ + attribute.String("server.address", statusURL.Hostname()), + attribute.Int("server.port", statusPort), + attribute.String("http.request.method", "POST"), + attribute.String("url.full", statusURL.String()+"/status"), + attribute.String("network.protocol.version", "1.1"), + } + compareSpanAttributes(t, expected, attribute.NewSet(spans[2].Attributes...)) + + spanExp.Reset() + + mr, err := http.Post(rt.URL()+"/v1/data/main", "application/json", nil) + if err != nil { + t.Fatal(err) + } + defer mr.Body.Close() + + _ = logs.Lookup(rt.Runtime.Manager).Trigger(context.Background()) + + spans = spanExp.GetSpans() + // Expect 2 spans: + // 1. POST /v1/data/main (server) + // 2. POST /v1/logs (client) + + if got, expected := len(spans), 2; got != expected { + t.Fatalf("got %d span(s), expected %d", got, expected) + } + for _, span := range spans { + if !span.SpanContext.IsValid() { + t.Fatalf("invalid span created: %#v", span.SpanContext) + } + } + if got, expected := spans[0].Name, "v1/data"; got != expected { + t.Fatalf("Expected span name to be %q but got %q", expected, got) + } + if got, expected := spans[0].SpanKind.String(), "server"; got != expected { + t.Fatalf("Expected span kind to be %q but got %q", expected, got) + } + if got, expected := spans[1].Name, "HTTP POST"; got != expected { + t.Fatalf("Expected span name to be %q but got %q", expected, got) + } + if got, expected := spans[1].SpanKind.String(), "client"; got != expected { + t.Fatalf("Expected span kind to be %q but got %q", expected, got) + } + + u, err = url.Parse(rt.URL()) + if err != nil { + t.Fatal(err) + } + port, err = strconv.Atoi(u.Port()) + if err != nil { + t.Fatal(err) + } + + expected = []any{ + attribute.String("server.address", u.Hostname()), + attribute.Int("server.port", port), + attribute.String("network.protocol.version", "1.1"), + attribute.String("network.peer.address", "127.0.0.1"), + attribute.Key("network.peer.port"), + attribute.String("http.request.method", "POST"), + attribute.String("url.scheme", "http"), + attribute.String("url.path", "/v1/data/main"), + attribute.Int("http.response.status_code", 200), + attribute.Int("http.response.body.size", 168), + attribute.String("user_agent.original", "Go-http-client/1.1"), + } + + compareSpanAttributes(t, expected, attribute.NewSet(spans[0].Attributes...)) + + expected = []any{ + attribute.String("server.address", controlPlaneURL.Hostname()), + attribute.Int("server.port", controlPlanePort), + attribute.String("http.request.method", "POST"), + attribute.String("url.full", controlPlaneURL.String()+"/logs"), + attribute.Int("http.response.status_code", 500), + attribute.String("error.type", "500"), + attribute.String("network.protocol.version", "1.1"), + } + + compareSpanAttributes(t, expected, attribute.NewSet(spans[1].Attributes...)) + }) +} + +func compareSpanAttributes(t *testing.T, expectedAttributes []any, spanAttributes attribute.Set) { + t.Helper() + ok := true + for _, exp := range expectedAttributes { + var expKey attribute.Key + var expValue *attribute.Value + + switch exp := exp.(type) { + case attribute.KeyValue: + expKey = exp.Key + expValue = &exp.Value + case attribute.Key: + expKey = exp + } + + value, exists := spanAttributes.Value(expKey) + if !exists { + t.Errorf("Expected span attributes to contain %q key", expKey) + ok = false + } else if expValue != nil && value != *expValue { + t.Errorf("Expected %q attribute to be %s but got %s", expKey, expValue.Emit(), value.Emit()) + ok = false + } + } + + if !ok { + txt, _ := spanAttributes.MarshalJSON() + t.Fatalf("Span attributes mismatch.\n\nGot:\n\n%s", txt) + } +} diff --git a/third_party/opa/v1/test/e2e/h2c/h2c_test.go b/third_party/opa/v1/test/e2e/h2c/h2c_test.go new file mode 100644 index 000000000000..11364c4d178e --- /dev/null +++ b/third_party/opa/v1/test/e2e/h2c/h2c_test.go @@ -0,0 +1,68 @@ +package h2c_test + +import ( + "crypto/tls" + "flag" + "net" + "net/http" + "os" + "testing" + + "golang.org/x/net/http2" + + "github.com/open-policy-agent/opa/v1/test/e2e" +) + +var testRuntime *e2e.TestRuntime + +func TestMain(m *testing.M) { + flag.Parse() + testServerParams := e2e.NewAPIServerTestParams() + testServerParams.Addrs = &[]string{"localhost:0"} + testServerParams.DiagnosticAddrs = &[]string{"localhost:0"} + testServerParams.H2CEnabled = true + + var err error + testRuntime, err = e2e.NewTestRuntime(testServerParams) + if err != nil { + os.Exit(1) + } + + os.Exit(testRuntime.RunTests(m)) +} + +func TestH2CHTTPListeners(t *testing.T) { + // h2c-enabled client + client := http.Client{ + Transport: &http2.Transport{ + AllowHTTP: true, + DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) { + return net.Dial(network, addr) + }, + }, + } + + addrs := append(testRuntime.Runtime.Addrs(), testRuntime.Runtime.DiagnosticAddrs()...) + + if expected, actual := 2, len(addrs); expected != actual { + t.Fatalf("expected %d addresses, found %d", expected, actual) + } + + for _, addr := range addrs { + u := "http://" + addr + "/health" + + resp, err := client.Get(u) + if err != nil { + t.Fatalf("failed to GET %s: %s", u, err) + } + + if expected, actual := http.StatusOK, resp.StatusCode; expected != actual { + t.Errorf("resp status: expected %d, got %d", expected, actual) + } + if expected, actual := 2, resp.ProtoMajor; expected != actual { + t.Errorf("resp.ProtoMajor: expected %d, got %d", expected, actual) + } + + resp.Body.Close() + } +} diff --git a/third_party/opa/v1/test/e2e/http/http_test.go b/third_party/opa/v1/test/e2e/http/http_test.go new file mode 100644 index 000000000000..952cf2f0dc12 --- /dev/null +++ b/third_party/opa/v1/test/e2e/http/http_test.go @@ -0,0 +1,156 @@ +package http_test + +import ( + "crypto/md5" + "encoding/json" + "flag" + "fmt" + "maps" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/test/e2e" +) + +var testRuntime *e2e.TestRuntime + +func TestMain(m *testing.M) { + flag.Parse() + + var err error + testRuntime, err = e2e.NewTestRuntime(e2e.NewAPIServerTestParams()) + if err != nil { + os.Exit(1) + } + + os.Exit(testRuntime.RunTests(m)) +} + +func TestHttpSendInterQueryForceCache(t *testing.T) { + tests := []struct { + note string + respHeaders map[string][]string + noForce bool + }{ + { + note: "http.send GET no force_cache", + respHeaders: map[string][]string{}, + noForce: true, + }, + { + note: "http.send GET force_cache default headers", + respHeaders: map[string][]string{}, + }, + { + note: "http.send GET force_cache no Date header", + respHeaders: map[string][]string{"Date": nil}, + }, + { + note: "http.send GET force_cache Date ignored", + respHeaders: map[string][]string{"Date": {"Wed, 31 Dec 2005 07:28:00 GMT"}}, + }, + { + note: "http.send GET force_cache Expires ignored", + respHeaders: map[string][]string{"Expires": {"Wed, 31 Dec 2005 07:28:00 GMT"}}, + }, + { + note: "http.send GET force_cache Cache-Control no-cache ignored", + respHeaders: map[string][]string{"Cache-Control": {"no-store"}}, + }, + { + note: "http.send GET force_cache Cache-Control max-age ignored", + respHeaders: map[string][]string{"Cache-Control": {"no-store", "max-age=0"}}, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + counter := 0 + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + counter++ + w.Header()["Content-Type"] = []string{"application/json"} + maps.Copy(w.Header(), tc.respHeaders) + w.WriteHeader(http.StatusOK) + _, err := w.Write(fmt.Appendf(nil, `{"c": %d}`, counter)) + if err != nil { + t.Fatal(err) + } + })) + defer ts.Close() + + var module string + if tc.noForce { + module = ` + package test + + response := http.send({ + "method": "get", + "url": "%s" + }).body + ` + } else { + module = ` + package test + + response := http.send({ + "method": "get", + "url": "%s", + "force_cache": true, + "force_cache_duration_seconds": 60 + }).body + ` + } + + // Since we are using the same request data for all tests (and only the response headers differ), we need + // to ensure that each test has some distinct identifier appended to the URL, or else the same cache key + // would be used for all of the tests! + url := ts.URL + "?test=" + fmt.Sprintf("%x", md5.Sum([]byte(tc.note))) + + if err := testRuntime.UploadPolicy("test", strings.NewReader(fmt.Sprintf(module, url))); err != nil { + t.Fatal(err) + } + + parsedBody := struct { + Result map[string]int `json:"result"` + }{} + expect := map[string]int{"c": 1} + + resultJSON, err := testRuntime.GetDataWithInput("test/response", map[string]string{}) + if err != nil { + t.Fatal(err) + } + if err = json.Unmarshal(resultJSON, &parsedBody); err != nil { + t.Fatal(err) + } + if !maps.Equal(parsedBody.Result, expect) { + t.Errorf("Expected response %v, got %v", expect, parsedBody.Result) + } + + // Repeat once more to see if the result is cached between queries + + if tc.noForce { + expect = map[string]int{"c": 2} + } + + resultJSON, err = testRuntime.GetDataWithInput("test/response", map[string]string{}) + if err != nil { + t.Fatal(err) + } + if err = json.Unmarshal(resultJSON, &parsedBody); err != nil { + t.Fatal(err) + } + if !maps.Equal(parsedBody.Result, expect) { + t.Errorf("Expected response %v, got %v", expect, parsedBody.Result) + } + + // Cleanup + + if err := testRuntime.DeletePolicy("test"); err != nil { + t.Fatal(err) + } + }) + } +} diff --git a/third_party/opa/v1/test/e2e/logs/console/console_decision_logger_benchmark_test.go b/third_party/opa/v1/test/e2e/logs/console/console_decision_logger_benchmark_test.go new file mode 100644 index 000000000000..1a6eab4a2649 --- /dev/null +++ b/third_party/opa/v1/test/e2e/logs/console/console_decision_logger_benchmark_test.go @@ -0,0 +1,18 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build noisy +// +build noisy + +package console + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/test/e2e/logs" +) + +func BenchmarkRESTConsoleDecisionLogger(b *testing.B) { + logs.RunDecisionLoggerBenchmark(b, testRuntime) +} diff --git a/third_party/opa/v1/test/e2e/logs/console/console_decision_logger_test.go b/third_party/opa/v1/test/e2e/logs/console/console_decision_logger_test.go new file mode 100644 index 000000000000..a2832204b254 --- /dev/null +++ b/third_party/opa/v1/test/e2e/logs/console/console_decision_logger_test.go @@ -0,0 +1,133 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package console + +import ( + "encoding/json" + "flag" + "os" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/logging/test" + "github.com/open-policy-agent/opa/v1/runtime" + "github.com/open-policy-agent/opa/v1/test/e2e" +) + +var testRuntime *e2e.TestRuntime + +func TestMain(m *testing.M) { + flag.Parse() + testServerParams := e2e.NewAPIServerTestParams() + + testServerParams.ConfigOverrides = []string{ + "decision_logs.console=true", + } + // Ensure decisions are logged regardless of regular log level + testServerParams.Logging = runtime.LoggingConfig{Level: "error"} + consoleLogger := test.New() + testServerParams.ConsoleLogger = consoleLogger + + var err error + testRuntime, err = e2e.NewTestRuntime(testServerParams) + if err != nil { + os.Exit(1) + } + + testRuntime.ConsoleLogger = consoleLogger + os.Exit(testRuntime.RunTests(m)) +} + +func TestConsoleDecisionLogWithInput(t *testing.T) { + + // Setup a test hook on the console logger (what the console decision logger uses) + + policy := ` + package test + import rego.v1 + + default allow = false + + allow if { + input.x == 1 + } + ` + + err := testRuntime.UploadPolicy(t.Name(), strings.NewReader(policy)) + if err != nil { + t.Fatal(err) + } + + input := map[string]int{ + "x": 1, + } + + expected := true + + resultJSON, err := testRuntime.GetDataWithInput("test/allow", input) + if err != nil { + t.Fatal(err) + } + + parsedBody := struct { + Result bool `json:"result"` + }{} + + err = json.Unmarshal(resultJSON, &parsedBody) + if err != nil { + t.Fatalf("Failed to parse body: \n\nActual: %s\n\nExpected: {\"result\": BOOL}\n\nerr = %s ", string(resultJSON), err) + } + + if parsedBody.Result != expected { + t.Fatalf("Unexpected result: %v", parsedBody.Result) + } + + // Check for some important fields + expectedFields := map[string]*struct { + found bool + match func(*testing.T, string) + }{ + "labels": {}, + "decision_id": {}, + "path": {}, + "input": {}, + "result": {}, + "timestamp": {}, + "type": {match: func(t *testing.T, actual string) { + if actual != "openpolicyagent.org/decision_logs" { + t.Fatalf("Expected field 'type' to be 'openpolicyagent.org/decision_logs'") + } + }}, + } + + var entry test.LogEntry + var found bool + + for _, entry = range testRuntime.ConsoleLogger.Entries() { + if entry.Message == "Decision Log" { + found = true + } + } + + if !found { + t.Fatalf("Did not find 'Decision Log' event in captured log entries") + } + + // Ensure expected fields exist + for fieldName, rawField := range entry.Fields { + if fd, ok := expectedFields[fieldName]; ok { + if fieldValue, ok := rawField.(string); ok && fd.match != nil { + fd.match(t, fieldValue) + } + fd.found = true + } + } + + for field, fd := range expectedFields { + if !fd.found { + t.Errorf("Missing expected field in decision log: %s\n\nEntry: %+v\n\n", field, entry) + } + } +} diff --git a/third_party/opa/v1/test/e2e/logs/remote/remote_decision_logger_benchmark_test.go b/third_party/opa/v1/test/e2e/logs/remote/remote_decision_logger_benchmark_test.go new file mode 100644 index 000000000000..83245e6e1a32 --- /dev/null +++ b/third_party/opa/v1/test/e2e/logs/remote/remote_decision_logger_benchmark_test.go @@ -0,0 +1,159 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build noisy +// +build noisy + +package remote + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io" + "os" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/test/authz" + testAuthz "github.com/open-policy-agent/opa/v1/test/authz" + "github.com/open-policy-agent/opa/v1/test/e2e" + testLogs "github.com/open-policy-agent/opa/v1/test/e2e/logs" + "github.com/open-policy-agent/opa/v1/util" +) + +var testRuntime *e2e.TestRuntime + +func TestMain(m *testing.M) { + flag.Parse() + + testLogServer := testLogs.TestLogServer{} + testLogServer.Start() + defer testLogServer.Stop() + + testServerParams := e2e.NewAPIServerTestParams() + + testServerParams.ConfigOverrides = []string{ + "decision_logs.console=false", + "decision_logs.service=logger", + "services.logger.url=" + testLogServer.URL(), + } + + var err error + testRuntime, err = e2e.NewTestRuntime(testServerParams) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + os.Exit(testRuntime.RunTests(m)) +} + +func BenchmarkRESTDecisionLogAuthzForbidAuthn(b *testing.B) { + runAuthzBenchmark(b, testAuthz.ForbidIdentity, 10) +} + +func BenchmarkRESTDecisionLogAuthzForbidPath(b *testing.B) { + runAuthzBenchmark(b, testAuthz.ForbidPath, 10) +} + +func BenchmarkRESTDecisionLogAuthzForbidMethod(b *testing.B) { + runAuthzBenchmark(b, testAuthz.ForbidMethod, 10) +} + +func BenchmarkRESTDecisionLogAuthzAllow10Paths(b *testing.B) { + runAuthzBenchmark(b, testAuthz.Allow, 10) +} + +func BenchmarkRESTDecisionLogAuthzAllow100Paths(b *testing.B) { + runAuthzBenchmark(b, testAuthz.Allow, 100) +} + +func BenchmarkRESTDecisionLogAuthzAllow1000Paths(b *testing.B) { + runAuthzBenchmark(b, testAuthz.Allow, 1000) +} + +func runAuthzBenchmark(b *testing.B, mode testAuthz.InputMode, numPaths int) { + // Generate test data and push it into the server + profile := testAuthz.DataSetProfile{ + NumTokens: 1000, + NumPaths: numPaths, + } + data := testAuthz.GenerateDataset(profile) + err := testRuntime.UploadData(bytes.NewReader(util.MustMarshalJSON(data))) + if err != nil { + b.Fatal(err) + } + + // Push the test policy + err = testRuntime.UploadPolicy("restauthz", strings.NewReader(testAuthz.Policy)) + if err != nil { + b.Fatal(err) + } + + queryPath := strings.Replace(authz.AllowQuery, ".", "/", -1) + url := testRuntime.URL() + "/v1/" + queryPath + + input, expected := testAuthz.GenerateInput(profile, mode) + inputPayload := util.MustMarshalJSON(map[string]any{ + "input": input, + }) + inputReader := bytes.NewReader(inputPayload) + + b.ResetTimer() + + for range b.N { + // The benchmark will include the time it takes to make the request, + // receive a response, and do any normal client error checking on + // the response. The benchmark is for the OPA server, not how + // long it takes the golang client to unpack the response body. + b.StartTimer() + resp, err := testRuntime.GetDataWithRawInput(url, inputReader) + b.StopTimer() + + body, err := io.ReadAll(resp) + if err != nil { + b.Fatalf("unexpected error reading response body: %s", err) + } + resp.Close() + + parsedBody := struct { + Result bool `json:"result"` + }{} + + err = json.Unmarshal(body, &parsedBody) + if err != nil { + b.Fatalf("Failed to parse body: \n\nActual: %s\n\nExpected: {\"result\": BOOL}\n\nerr = %s ", string(body), err) + } + if parsedBody.Result != expected { + b.Fatalf("Unexpected result: %v", parsedBody.Result) + } + + inputReader.Reset(inputPayload) + } +} + +func BenchmarkRESTRemoteDecisionLogger(b *testing.B) { + testLogs.RunDecisionLoggerBenchmark(b, testRuntime) +} + +func BenchmarkRESTRemoteDecisionLoggerMaskApplied(b *testing.B) { + maskPolicy := `package system.log + +mask["/input/password"] { + true +} + +mask[{"op": "upsert", "path": "/input/ssn", "value": x}] { + last4 := split(input.input.ssn, "-")[2] + x := sprintf("***-**-%s", [last4]) +} +` + err := testRuntime.UploadPolicy("mask", strings.NewReader(maskPolicy)) + if err != nil { + b.Fatal(err) + } + testLogs.RunDecisionLoggerBenchmark(b, testRuntime) +} diff --git a/third_party/opa/v1/test/e2e/logs/utils.go b/third_party/opa/v1/test/e2e/logs/utils.go new file mode 100644 index 000000000000..b3451ed657d4 --- /dev/null +++ b/third_party/opa/v1/test/e2e/logs/utils.go @@ -0,0 +1,134 @@ +package logs + +import ( + "context" + "encoding/json" + "fmt" + "net" + "net/http" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/test/e2e" +) + +// RunDecisionLoggerBenchmark runs a benchmark for decision logs with a +// pre-configured test runtime +func RunDecisionLoggerBenchmark(b *testing.B, rt *e2e.TestRuntime) { + ruleCounts := []int{1, 10, 100, 1000} + rulesHitCounts := []int{0, 1, 10, 100, 1000} + + for _, hitCount := range rulesHitCounts { + for _, ruleCount := range ruleCounts { + if hitCount > ruleCount { + continue + } + name := fmt.Sprintf("%dx%d", ruleCount, hitCount) + policy := GeneratePolicy(ruleCount, hitCount) + + // Push the test policy + err := rt.UploadPolicy("test", strings.NewReader(policy)) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + + b.Run(name, func(b *testing.B) { + input := map[string]any{ + "hit": true, + "password": "$up3r$Ecr3t", + "ssn": "123-45-6789", + } + b.ResetTimer() + + for range b.N { + b.StartTimer() + + bodyJSON, err := rt.GetDataWithInput("data/test/rule", input) + if err != nil { + b.Fatal(err) + } + + b.StopTimer() + + parsedBody := struct { + Result bool `json:"result"` + }{} + + err = json.Unmarshal(bodyJSON, &parsedBody) + if err != nil { + b.Fatalf("Failed to parse body: \n\nActual: %s\n\nExpected: {\"result\": BOOL}\n\nerr = %s ", string(bodyJSON), err) + } + expected := hitCount != 0 + if parsedBody.Result != expected { + b.Fatalf("Unexpected result: %v", parsedBody.Result) + } + } + }) + } + } +} + +// GeneratePolicy generates a policy for use in Decision Log e2e tests. The +// `ruleCounts` determine how many total rules to generate, and the `ruleHits` +// are the number of them that will be evaluated. This is keyed off of +// the `input.hit` boolean value. +func GeneratePolicy(ruleCounts int, ruleHits int) string { + pb := strings.Builder{} + pb.WriteString("package test\n") + hits := 0 + for range ruleCounts { + pb.WriteString("rule if {") + if hits < ruleHits { + pb.WriteString("input.hit = true") + hits++ + } else { + pb.WriteString("input.hit = false") + } + pb.WriteString("}\n") + } + return pb.String() +} + +// TestLogServer implements the decision log endpoint for e2e testing. +type TestLogServer struct { + server *http.Server + listener net.Listener +} + +// URL string representation for the current server. Requires that the server +// has already been started. +func (t *TestLogServer) URL() string { + return "http://" + t.listener.Addr().String() +} + +// Start the test server listening on a random port. +func (t *TestLogServer) Start() { + var err error + t.listener, err = net.Listen("tcp", ":0") + if err != nil { + panic(err) + } + t.server = &http.Server{} + t.server.SetKeepAlivesEnabled(false) + go func() { + err = t.server.Serve(t.listener) + if err != http.ErrServerClosed { + panic(err) + } + }() +} + +// Stop the test server. There is a 5 second graceful shutdown period and then +// it will be forcefully stopped. +func (t *TestLogServer) Stop() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(5)) + _ = t.server.Shutdown(ctx) + cancel() + err := t.server.Close() + if err != nil { + panic(err) + } +} diff --git a/third_party/opa/v1/test/e2e/metrics/metrics_test.go b/third_party/opa/v1/test/e2e/metrics/metrics_test.go new file mode 100644 index 000000000000..b4d1ac61c960 --- /dev/null +++ b/third_party/opa/v1/test/e2e/metrics/metrics_test.go @@ -0,0 +1,249 @@ +package metrics + +import ( + "flag" + "io" + "net/http" + "os" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/server/types" + "github.com/open-policy-agent/opa/v1/test/e2e" +) + +var testRuntime *e2e.TestRuntime + +func TestMain(m *testing.M) { + flag.Parse() + testServerParams := e2e.NewAPIServerTestParams() + + var err error + testRuntime, err = e2e.NewTestRuntime(testServerParams) + if err != nil { + os.Exit(1) + } + + os.Exit(testRuntime.RunTests(m)) +} + +func TestMetricsEndpoint(t *testing.T) { + + policy := ` + package test + p = true + ` + + err := testRuntime.UploadPolicy(t.Name(), strings.NewReader(policy)) + if err != nil { + t.Fatal(err) + } + + dr := struct { + Result bool `json:"result"` + }{} + + if err := testRuntime.GetDataWithInputTyped("test/p", nil, &dr); err != nil { + t.Fatal(err) + } + + if !dr.Result { + t.Fatalf("Unexpected response: %+v", dr) + } + + mr, err := http.Get(testRuntime.URL() + "/metrics") + if err != nil { + t.Fatal(err) + } + + defer mr.Body.Close() + + bs, err := io.ReadAll(mr.Body) + if err != nil { + t.Fatal(err) + } + + str := string(bs) + + expected := []string{ + `http_request_duration_seconds_count{code="200",handler="v1/policies",method="put"} 1`, + `http_request_duration_seconds_count{code="200",handler="v1/data",method="post"} 1`, + } + + for _, exp := range expected { + if !strings.Contains(str, exp) { + t.Fatalf("Expected to find %q but got:\n\n%v", exp, str) + } + } +} + +type response struct { + Result bool `json:"result"` + Metrics map[string]any `json:"metrics"` +} + +func TestRequestWithInstrumentationV1DataAPI(t *testing.T) { + + policy := ` + package test + p = true + q = true + ` + + err := testRuntime.UploadPolicy(t.Name(), strings.NewReader(policy)) + if err != nil { + t.Fatal(err) + } + + var resp response + if err := testRuntime.GetDataWithInputTyped("test/p?instrument", nil, &resp); err != nil { + t.Fatal(err) + } + + if !resp.Result { + t.Fatalf("Unexpected response: %+v", resp) + } + + assertDataInstrumentationMetricsInMap(t, true, resp.Metrics) + + // run another request, this should re-use the compiled query + var resp2 response + if err := testRuntime.GetDataWithInputTyped("test/p?instrument", nil, &resp2); err != nil { + t.Fatal(err) + } + + if !resp2.Result { + t.Fatalf("Unexpected response: %+v", resp2) + } + + assertDataInstrumentationMetricsInMap(t, false, resp2.Metrics) + + // GET data endpoint + var resp3 response + if err := testRuntime.GetDataWithInputTyped("test/q?instrument", nil, &resp3); err != nil { + t.Fatal(err) + } + + if !resp.Result { + t.Fatalf("Unexpected response: %+v", resp3) + } + + assertDataInstrumentationMetricsInMap(t, true, resp3.Metrics) + + // 2nd GET data endpoint + var resp4 response + if err := testRuntime.GetDataWithInputTyped("test/q?instrument", nil, &resp4); err != nil { + t.Fatal(err) + } + + if !resp.Result { + t.Fatalf("Unexpected response: %+v", resp4) + } + + assertDataInstrumentationMetricsInMap(t, false, resp4.Metrics) +} + +func TestRequestWithInstrumentationV1CompileAPI(t *testing.T) { + + policy := ` + package test + import rego.v1 + p if {input.x >= data.y} + ` + + err := testRuntime.UploadPolicy(t.Name(), strings.NewReader(policy)) + if err != nil { + t.Fatal(err) + } + + var i any = "{\"x\": 4}" + req := types.CompileRequestV1{ + Query: "data.test.p == true", + Input: &i, + Unknowns: &[]string{"data.y"}, + } + + resp, err := testRuntime.CompileRequestWithInstrumentation(req) + if err != nil { + t.Fatal(err) + } + + assertCompileInstrumentationMetricsInMap(t, true, resp.Metrics) +} + +func assertCompileInstrumentationMetricsInMap(t *testing.T, _ bool, metrics map[string]any) { + expectedKeys := []string{ + "histogram_eval_op_plug", + "timer_eval_op_plug_ns", + "timer_server_handler_ns", + + "timer_rego_query_parse_ns", + "timer_rego_query_compile_ns", + "timer_query_compile_stage_build_comprehension_index_ns", + "timer_query_compile_stage_check_safety_ns", + "timer_query_compile_stage_check_types_ns", + "timer_query_compile_stage_check_undefined_funcs_ns", + "timer_query_compile_stage_check_unsafe_builtins_ns", + "timer_query_compile_stage_resolve_refs_ns", + "timer_query_compile_stage_rewrite_comprehension_terms_ns", + "timer_query_compile_stage_rewrite_dynamic_terms_ns", + "timer_query_compile_stage_rewrite_expr_terms_ns", + "timer_query_compile_stage_rewrite_local_vars_ns", + "timer_query_compile_stage_rewrite_with_values_ns", + } + for _, key := range expectedKeys { + if metrics[key] == nil { + t.Errorf("Expected to find key %q in metrics response", key) + } + } + if t.Failed() { + t.Logf("metrics response: %v\n", metrics) + } +} + +func assertDataInstrumentationMetricsInMap(t *testing.T, includeCompile bool, metrics map[string]any) { + expectedKeys := []string{ + "counter_server_query_cache_hit", + "counter_eval_op_virtual_cache_miss", + "histogram_eval_op_plug", + "timer_eval_op_plug_ns", + "timer_rego_input_parse_ns", + "timer_rego_query_eval_ns", + "timer_server_handler_ns", + } + compileStageKeys := []string{ + "timer_rego_query_compile_ns", + "timer_query_compile_stage_build_comprehension_index_ns", + "timer_query_compile_stage_check_safety_ns", + "timer_query_compile_stage_check_types_ns", + "timer_query_compile_stage_check_undefined_funcs_ns", + "timer_query_compile_stage_check_unsafe_builtins_ns", + "timer_query_compile_stage_resolve_refs_ns", + "timer_query_compile_stage_rewrite_comprehension_terms_ns", + "timer_query_compile_stage_rewrite_dynamic_terms_ns", + "timer_query_compile_stage_rewrite_expr_terms_ns", + "timer_query_compile_stage_rewrite_local_vars_ns", + "timer_query_compile_stage_rewrite_to_capture_value_ns", + "timer_query_compile_stage_rewrite_with_values_ns", + } + + if includeCompile { + expectedKeys = append(expectedKeys, compileStageKeys...) + } + + for _, key := range expectedKeys { + if metrics[key] == nil { + t.Errorf("Expected to find key %q in metrics response", key) + } + } + if !includeCompile { + for _, key := range compileStageKeys { + if metrics[key] != nil { + t.Errorf("Expected NOT to find key %q in metrics response", key) + } + } + } + if t.Failed() { + t.Logf("metrics response: %v\n", metrics) + } +} diff --git a/third_party/opa/v1/test/e2e/oci/oci_test.go b/third_party/opa/v1/test/e2e/oci/oci_test.go new file mode 100644 index 000000000000..ed5db5709876 --- /dev/null +++ b/third_party/opa/v1/test/e2e/oci/oci_test.go @@ -0,0 +1,123 @@ +package test + +import ( + "bytes" + "strings" + "sync" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/logging" + test_sdk "github.com/open-policy-agent/opa/v1/sdk/test" + "github.com/open-policy-agent/opa/v1/test/e2e" +) + +type SafeBuffer struct { + b bytes.Buffer + m sync.Mutex +} + +func (b *SafeBuffer) Read(p []byte) (n int, err error) { + b.m.Lock() + defer b.m.Unlock() + return b.b.Read(p) +} +func (b *SafeBuffer) Write(p []byte) (n int, err error) { + b.m.Lock() + defer b.m.Unlock() + return b.b.Write(p) +} +func (b *SafeBuffer) String() string { + b.m.Lock() + defer b.m.Unlock() + return b.b.String() +} + +func TestEnablePrintStatementsForBundles(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + ref := "registry.io/someorg/somerepo:tag" + server := test_sdk.MustNewServer( + test_sdk.MockOCIBundle(ref, map[string]string{ + "post.rego": ` + package peoplefinder.POST.api.users + + import future.keywords.if + import input.user.properties as user_props + + default allowed = false + + allowed if { + user_props.department == "Operations" + user_props.title == "IT Manager" + } + `, + })) + params := e2e.NewAPIServerTestParams() + + buf := SafeBuffer{} + + logger := logging.New() + logger.SetLevel(logging.Debug) // set to debug to see the bundle download skip message + logger.SetOutput(&buf) + params.Logger = logger + + params.ConfigOverrides = []string{ + "services.test.url=" + server.URL(), + "services.test.type=oci", + "bundles.test.resource=" + ref, + "bundles.test.polling.min_delay_seconds=1", + "bundles.test.polling.max_delay_seconds=3", + } + + // Test runtime uses the local OCI image layers stored in download testdata that contain the + // rego policies based on the https://github.com/aserto-dev/policy-peoplefinder-abac template + e2e.WithRuntime(t, e2e.TestRuntimeOpts{WaitForBundles: true}, params, func(rt *e2e.TestRuntime) { + var readBuf []byte + type Props struct { + Department string `json:"department"` + Title string `json:"title"` + } + type Attributes struct { + Properties Props `json:"properties"` + } + type Input struct { + User Attributes `json:"user"` + } + + inputAllowed := Input{User: Attributes{Properties: Props{Department: "Operations", Title: "IT Manager"}}} + + inputNotAllowed := Input{User: Attributes{Properties: Props{Department: "IT", Title: "Engineer"}}} + + readBuf, err := rt.GetDataWithInput("peoplefinder/POST/api/users/allowed", inputAllowed) + if err != nil { + t.Fatal("failed to get data from runtime") + } + + response := string(readBuf) + if !strings.Contains(response, "true") { + t.Fatalf("expected true but got: %s", response) + } + readBuf, err = rt.GetDataWithInput("peoplefinder/POST/api/users/allowed", inputNotAllowed) + if err != nil { + t.Fatal("failed to get data from runtime") + } + if !strings.Contains(string(readBuf), "false") { + t.Fatalf("expected true but got: %s", response) + } + + time.Sleep(3 * time.Second) // wait for the downloader pooling mechanism to kick in + expContains := "Bundle loaded and activated successfully" + skipContains := "Bundle load skipped, server replied with not modified." + + if !strings.Contains(buf.String(), expContains) { + t.Fatalf("expected logs to contain %q but got: %v", expContains, buf.String()) + } + time.Sleep(3 * time.Second) // wait a couple of seconds for the second trigger to kick in + if !strings.Contains(buf.String(), skipContains) { + t.Fatalf("expected logs to contain %q but got: %v", skipContains, buf.String()) + } + }) +} diff --git a/third_party/opa/v1/test/e2e/print/print_test.go b/third_party/opa/v1/test/e2e/print/print_test.go new file mode 100644 index 000000000000..9a1951127309 --- /dev/null +++ b/third_party/opa/v1/test/e2e/print/print_test.go @@ -0,0 +1,149 @@ +package test + +import ( + "bytes" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/logging" + test_sdk "github.com/open-policy-agent/opa/v1/sdk/test" + "github.com/open-policy-agent/opa/v1/test/e2e" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestEnablePrintStatementsForFilesystemPolicies(t *testing.T) { + + files := map[string]string{ + "/test.rego": ` + package test + import rego.v1 + + p if { + print("hello world") + } + `, + } + + test.WithTempFS(files, func(dir string) { + + params := e2e.NewAPIServerTestParams() + params.Paths = []string{dir} + + buf := bytes.NewBuffer(nil) + + logger := logging.New() + logger.SetOutput(buf) + params.Logger = logger + + e2e.WithRuntime(t, e2e.TestRuntimeOpts{}, params, func(rt *e2e.TestRuntime) { + + var dr struct { + Result bool `json:"result"` + } + + if err := rt.GetDataWithInputTyped("test/p", nil, &dr); err != nil { + t.Fatal(err) + } else if !dr.Result { + t.Fatal("expected true") + } + + expContains := "hello world" + + if !strings.Contains(buf.String(), expContains) { + t.Fatalf("expected logs to contain %q but got: %v", expContains, buf.String()) + } + }) + }) + +} + +func TestEnablePrintStatementsForHTTPAPIPushedPolicies(t *testing.T) { + policy := ` + package test + import rego.v1 + + p if { + print("hello world") + } + ` + + params := e2e.NewAPIServerTestParams() + + buf := bytes.NewBuffer(nil) + + logger := logging.New() + logger.SetOutput(buf) + params.Logger = logger + + e2e.WithRuntime(t, e2e.TestRuntimeOpts{}, params, func(rt *e2e.TestRuntime) { + + if err := rt.UploadPolicy("test.rego", bytes.NewBufferString(policy)); err != nil { + t.Fatal(err) + } + + var dr struct { + Result bool `json:"result"` + } + + if err := rt.GetDataWithInputTyped("test/p", nil, &dr); err != nil { + t.Fatal(err) + } else if !dr.Result { + t.Fatal("expected true") + } + + expContains := "hello world" + + if !strings.Contains(buf.String(), expContains) { + t.Fatalf("expected logs to contain %q but got: %v", expContains, buf.String()) + } + }) + +} + +func TestEnablePrintStatementsForBundles(t *testing.T) { + + server := test_sdk.MustNewServer( + test_sdk.RawBundles(true), + test_sdk.MockBundle("/bundles/bundle.tar.gz", map[string]string{ + "test.rego": ` + package test + import rego.v1 + + p if { + print("hello world") + } + `, + })) + + params := e2e.NewAPIServerTestParams() + + buf := bytes.NewBuffer(nil) + + logger := logging.New() + logger.SetOutput(buf) + params.Logger = logger + + params.ConfigOverrides = []string{ + "services.test.url=" + server.URL(), + "bundles.test.resource=/bundles/bundle.tar.gz", + } + + e2e.WithRuntime(t, e2e.TestRuntimeOpts{WaitForBundles: true}, params, func(rt *e2e.TestRuntime) { + + var dr struct { + Result bool `json:"result"` + } + + if err := rt.GetDataWithInputTyped("test/p", nil, &dr); err != nil { + t.Fatal(err) + } else if !dr.Result { + t.Fatal("expected true") + } + + expContains := "hello world" + + if !strings.Contains(buf.String(), expContains) { + t.Fatalf("expected logs to contain %q but got: %v", expContains, buf.String()) + } + }) +} diff --git a/third_party/opa/v1/test/e2e/shutdown/shutdown_test.go b/third_party/opa/v1/test/e2e/shutdown/shutdown_test.go new file mode 100644 index 000000000000..66f47a839601 --- /dev/null +++ b/third_party/opa/v1/test/e2e/shutdown/shutdown_test.go @@ -0,0 +1,51 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. +package shutdown + +import ( + "flag" + "os" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/test/e2e" +) + +var testRuntime *e2e.TestRuntime + +func TestMain(m *testing.M) { + flag.Parse() + testServerParams := e2e.NewAPIServerTestParams() + + testServerParams.GracefulShutdownPeriod = 1 + testServerParams.ShutdownWaitPeriod = 2 + + var err error + testRuntime, err = e2e.NewTestRuntime(testServerParams) + if err != nil { + os.Exit(1) + } + + os.Exit(testRuntime.RunTests(m)) +} + +func TestShutdownWaitPeriod(t *testing.T) { + proc, err := os.FindProcess(os.Getpid()) + if err != nil { + t.Fatal(err) + } + + err = proc.Signal(os.Interrupt) + if err != nil { + t.Fatal(err) + } + + time.Sleep(1500 * time.Millisecond) + + // Ensure that OPA is still running + err = testRuntime.HealthCheck(testRuntime.URL()) + if err != nil { + t.Fatalf("Expected health endpoint to be up but got:\n\n%v", err) + } +} diff --git a/third_party/opa/v1/test/e2e/testing.go b/third_party/opa/v1/test/e2e/testing.go new file mode 100644 index 000000000000..73612ca1ba61 --- /dev/null +++ b/third_party/opa/v1/test/e2e/testing.go @@ -0,0 +1,510 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package e2e + +import ( + "bytes" + "context" + "crypto/rand" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "os" + "strings" + "sync" + "testing" + "time" + + "github.com/open-policy-agent/opa/internal/uuid" + "github.com/open-policy-agent/opa/v1/logging" + "github.com/open-policy-agent/opa/v1/logging/test" + "github.com/open-policy-agent/opa/v1/runtime" + "github.com/open-policy-agent/opa/v1/server/types" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + defaultAddr = "localhost:0" // default listening address for server, use a random open port +) + +// NewAPIServerTestParams creates a new set of runtime.Params with enough +// default values filled in to start the server. Options can/should +// be customized for the test case. +func NewAPIServerTestParams() runtime.Params { + params := runtime.NewParams() + + // Add in some defaults + params.Addrs = &[]string{defaultAddr} + + params.Logging = runtime.LoggingConfig{ + Level: "debug", + Format: "json-pretty", + } + + // unless overridden, don't log from tests + params.Logger = logging.NewNoOpLogger() + + params.GracefulShutdownPeriod = 10 // seconds + + params.DecisionIDFactory = func() string { + id, err := uuid.New(rand.Reader) + if err != nil { + return "" + } + return id + } + return params +} + +// TestRuntime holds metadata and provides helper methods +// to interact with the runtime being tested. +type TestRuntime struct { + Params runtime.Params + Runtime *runtime.Runtime + Ctx context.Context + Cancel context.CancelFunc + Client *http.Client + ConsoleLogger *test.Logger + url string + urlMtx *sync.Mutex + waitForBundles bool +} + +// NewTestRuntime returns a new TestRuntime. +func NewTestRuntime(params runtime.Params) (*TestRuntime, error) { + return NewTestRuntimeWithOpts(TestRuntimeOpts{}, params) +} + +// NewTestRuntimeWithOpts returns a new TestRuntime. +func NewTestRuntimeWithOpts(opts TestRuntimeOpts, params runtime.Params) (*TestRuntime, error) { + + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + + rt, err := runtime.NewRuntime(ctx, params) + if err != nil { + cancel() + return nil, fmt.Errorf("create new runtime: %w", err) + } + + return &TestRuntime{ + Params: params, + Runtime: rt, + Ctx: ctx, + Cancel: cancel, + Client: &http.Client{}, + urlMtx: new(sync.Mutex), + waitForBundles: opts.WaitForBundles, + }, nil +} + +// WrapRuntime creates a new TestRuntime by wrapping an existing runtime +func WrapRuntime(ctx context.Context, cancel context.CancelFunc, rt *runtime.Runtime) *TestRuntime { + return &TestRuntime{ + Params: rt.Params, + Runtime: rt, + Ctx: ctx, + Cancel: cancel, + Client: &http.Client{}, + urlMtx: new(sync.Mutex), + } +} + +// RunAPIServerTests will start the OPA runtime serving with a given +// configuration. This is essentially a wrapper for `m.Run()` that +// handles starting and stopping the local API server. The return +// value is what should be used as the code in `os.Exit` in the +// `TestMain` function. +// Deprecated: Use RunTests instead +func (t *TestRuntime) RunAPIServerTests(m *testing.M) int { + return t.runTests(m, true) +} + +// RunAPIServerBenchmarks will start the OPA runtime and do +// `m.Run()` similar to how RunAPIServerTests works. This +// will suppress logging output on stdout to prevent the tests +// from being overly verbose. If log output is desired set +// the `test.v` flag. +// Deprecated: Use RunTests instead +func (t *TestRuntime) RunAPIServerBenchmarks(m *testing.M) int { + return t.runTests(m, !testing.Verbose()) +} + +// RunTests will start the OPA runtime serving with a given +// configuration. This is essentially a wrapper for `m.Run()` that +// handles starting and stopping the local API server. The return +// value is what should be used as the code in `os.Exit` in the +// `TestMain` function. +func (t *TestRuntime) RunTests(m *testing.M) int { + return t.runTests(m, !testing.Verbose()) +} + +// URL will return the URL that the server is listening on. If +// the server hasn't started listening this will return an empty string. +// It is not expected for the URL to change throughout the lifetime of the +// TestRuntime. Runtimes configured with >1 address will only get the +// first URL. +func (t *TestRuntime) URL() string { + if t.url != "" { + // fast path once it has been computed + return t.url + } + + t.urlMtx.Lock() + defer t.urlMtx.Unlock() + + // check again in the lock, it might have changed on us.. + if t.url != "" { + return t.url + } + + addrs := t.Runtime.Addrs() + if len(addrs) == 0 { + return "" + } + // Just pick the first one, if a test was configured with >1 they + // will need to determine the URLs themselves. + addr := addrs[0] + + parsed, err := t.AddrToURL(addr) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + t.url = parsed + + return t.url +} + +// AddrToURL generates a full URL from an address, as configured on the runtime. +// This can include fully qualified urls, just host/ip, with port, or only port +// (eg, "localhost", ":8181", "http://foo", etc). If the runtime is configured +// with HTTPS certs it will generate an appropriate URL. +func (t *TestRuntime) AddrToURL(addr string) (string, error) { + if strings.HasPrefix(addr, ":") { + addr = "localhost" + addr + } + + if !strings.Contains(addr, "://") { + scheme := "http://" + if t.Params.Certificate != nil { + scheme = "https://" + } + addr = scheme + addr + } + + parsed, err := url.Parse(addr) + if err != nil { + return "", fmt.Errorf("failed to parse listening address of server: %s", err) + } + + return parsed.String(), nil +} + +func (t *TestRuntime) runTests(m *testing.M, suppressLogs bool) int { + // Start serving API requests in the background + done := make(chan error) + go func() { + // Suppress the stdlogger in the server + if suppressLogs { + logging.Get().SetOutput(io.Discard) + } + err := t.Runtime.Serve(t.Ctx) + done <- err + }() + + // Turns out this thread gets a different stdlogger + // so we need to set the output on it here too. + if suppressLogs { + logging.Get().SetOutput(io.Discard) + } + + // wait for the server to be ready + err := t.WaitForServer() + if err != nil { + return 1 + } + + // Actually run the unit tests/benchmarks + errc := m.Run() + + // Wait for the API server to stop + t.Cancel() + err = <-done + + if err != nil && errc == 0 { + // even if the tests passed return an error code if + // the server encountered an error + errc = 1 + } + + return errc +} + +// TestRuntimeOpts contains parameters for the test runtime. +type TestRuntimeOpts struct { + WaitForBundles bool // indicates if readiness check should depend on bundle activation + PostServeActions func(rt *TestRuntime) error +} + +// WithRuntime invokes f with a new TestRuntime after waiting for server +// readiness. This function can be called inside of each test that requires a +// runtime as opposed to RunTests which can only be called once. +func WithRuntime(t *testing.T, opts TestRuntimeOpts, params runtime.Params, f func(rt *TestRuntime)) { + + t.Helper() + + rt, err := NewTestRuntimeWithOpts(opts, params) + if err != nil { + t.Fatal(err) + } + + done := make(chan error) + go func() { + err := rt.Runtime.Serve(rt.Ctx) + done <- err + }() + + err = rt.WaitForServerStatus(runtime.ServerWaitingForPlugins) + if err != nil { + t.Fatal(err) + } + + if opts.PostServeActions != nil { + err = opts.PostServeActions(rt) + if err != nil { + t.Fatal(err) + } + } + + err = rt.WaitForServer() + if err != nil { + t.Fatal(err) + } + + f(rt) + rt.Cancel() + err = <-done + + if err != nil { + t.Fatal(err) + } +} + +// WaitForServer will block until the server is running and passes a health check. +func (t *TestRuntime) WaitForServer() error { + delay := time.Duration(100) * time.Millisecond + retries := 100 // 10 seconds before we give up + for range retries { + // First make sure it has started listening and we have an address + if t.URL() != "" { + // Then make sure it has started serving + err := t.HealthCheck(t.URL()) + if err == nil { + logging.Get().Info("Test server ready and listening on: %s", t.URL()) + return nil + } + } + time.Sleep(delay) + } + return errors.New("API Server not ready in time") +} + +func (t *TestRuntime) WaitForServerStatus(status runtime.ServerStatus) error { + delay := time.Duration(100) * time.Millisecond + retries := 100 // 10 seconds before we give up + for range retries { + if t.Runtime.ServerStatus() >= status { + return nil + } + time.Sleep(delay) + } + return fmt.Errorf("API Server did not reach status %d in time", status) +} + +// DeletePolicy will delete the given policy in the runtime via the v1 policy API +func (t *TestRuntime) DeletePolicy(name string) error { + req, err := http.NewRequest("DELETE", t.URL()+"/v1/policies/"+name, nil) + if err != nil { + return err + } + resp, err := t.Client.Do(req) + if err != nil { + return fmt.Errorf("failed to DELETE the test policy: %s", err) + } + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected response: %d %s", resp.StatusCode, resp.Status) + } + return nil +} + +// UploadPolicy will upload the given policy to the runtime via the v1 policy API +func (t *TestRuntime) UploadPolicy(name string, policy io.Reader) error { + req, err := http.NewRequest("PUT", t.URL()+"/v1/policies/"+name, policy) + if err != nil { + return fmt.Errorf("Unexpected error creating request: %s", err) + } + resp, err := t.Client.Do(req) + if err != nil { + return fmt.Errorf("Failed to PUT the test policy: %s", err) + } + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Unexpected response: %d %s", resp.StatusCode, resp.Status) + } + return nil +} + +// UploadData will upload the given data to the runtime via the v1 data API +func (t *TestRuntime) UploadData(data io.Reader) error { + return t.UploadDataToPath("/", data) +} + +// UploadDataToPath will upload the given data to the runtime via the v1 data API +func (t *TestRuntime) UploadDataToPath(path string, data io.Reader) error { + client := &http.Client{} + + urlPath := strings.TrimSuffix("/v1/data"+path, "/") + + req, err := http.NewRequest("PUT", t.URL()+urlPath, data) + if err != nil { + return fmt.Errorf("unexpected error creating request: %s", err) + } + + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("failed to PUT data: %s", err) + } + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("unexpected response: %d %s", resp.StatusCode, resp.Status) + } + return nil +} + +// GetDataWithInput will use the v1 data API and POST with the given input. The returned +// value is the full response body. +func (t *TestRuntime) GetDataWithInput(path string, input any) ([]byte, error) { + inputPayload := util.MustMarshalJSON(map[string]any{ + "input": input, + }) + + path = strings.TrimPrefix(path, "/") + if !strings.HasPrefix(path, "data") { + path = "data/" + path + } + + resp, err := t.GetDataWithRawInput(t.URL()+"/v1/"+path, bytes.NewReader(inputPayload)) + if err != nil { + return nil, err + } + + body, err := io.ReadAll(resp) + if err != nil { + return nil, fmt.Errorf("unexpected error reading response body: %s", err) + } + resp.Close() + return body, nil +} + +// GetDataWithRawInput will use the v1 data API and POST with the given input. The returned +// value is the full response body. +func (t *TestRuntime) GetDataWithRawInput(url string, input io.Reader) (io.ReadCloser, error) { + return t.request("POST", url, input) +} + +// GetData will use the v1 data API and GET without input. The returned value is the full +// response body. +func (t *TestRuntime) GetData(url string) (io.ReadCloser, error) { + return t.request("GET", url, nil) +} + +// CompileRequestWithInstrumentation will use the v1 compile API and POST with the given request and instrumentation enabled. +func (t *TestRuntime) CompileRequestWithInstrumentation(req types.CompileRequestV1) (*types.CompileResponseV1, error) { + return t.compileRequest(req, true) +} + +// CompileRequest will use the v1 compile API and POST with the given request. +func (t *TestRuntime) CompileRequest(req types.CompileRequestV1) (*types.CompileResponseV1, error) { + return t.compileRequest(req, false) +} + +func (t *TestRuntime) compileRequest(req types.CompileRequestV1, instrument bool) (*types.CompileResponseV1, error) { + inputPayload := util.MustMarshalJSON(req) + + url := t.URL() + "/v1/compile" + if instrument { + url += "?instrument" + } + resp, err := t.request("POST", url, bytes.NewReader(inputPayload)) + if err != nil { + return nil, err + } + + body, err := io.ReadAll(resp) + if err != nil { + return nil, fmt.Errorf("unexpected error reading response body: %s", err) + } + resp.Close() + + var typedResp types.CompileResponseV1 + err = json.Unmarshal(body, &typedResp) + if err != nil { + return nil, err + } + + return &typedResp, nil +} + +func (*TestRuntime) request(method, url string, input io.Reader) (io.ReadCloser, error) { + req, err := http.NewRequest(method, url, input) + if err != nil { + return nil, fmt.Errorf("unexpected error: %w", err) + } + req.Header.Set("content-type", "application/json") + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, fmt.Errorf("unexpected error: %w", err) + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected response status: %s", resp.Status) + } + return resp.Body, nil +} + +// GetDataWithInputTyped returns an unmarshalled response from GetDataWithInput. +func (t *TestRuntime) GetDataWithInputTyped(path string, input any, response any) error { + + bs, err := t.GetDataWithInput(path, input) + if err != nil { + return err + } + + return json.Unmarshal(bs, response) +} + +// HealthCheck will query /health and return an error if the server is not healthy +func (t *TestRuntime) HealthCheck(url string) error { + + url += "/health" + if t.waitForBundles { + url += "?bundles" + } + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return fmt.Errorf("unexpected error creating request: %s", err) + } + resp, err := t.Client.Do(req) + if err != nil { + return fmt.Errorf("unexpected error: %s", err) + } + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected response: %d %s", resp.StatusCode, resp.Status) + } + return nil +} diff --git a/third_party/opa/v1/test/e2e/tls/testdata/.gitignore b/third_party/opa/v1/test/e2e/tls/testdata/.gitignore new file mode 100644 index 000000000000..e7fe15b29bb4 --- /dev/null +++ b/third_party/opa/v1/test/e2e/tls/testdata/.gitignore @@ -0,0 +1,4 @@ +*.srl +*.cnf +csr.pem +ca-key.pem diff --git a/third_party/opa/v1/test/e2e/tls/testdata/gencerts.sh b/third_party/opa/v1/test/e2e/tls/testdata/gencerts.sh new file mode 100755 index 000000000000..c59cdedc9bd6 --- /dev/null +++ b/third_party/opa/v1/test/e2e/tls/testdata/gencerts.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# taken from +# https://github.com/dexidp/dex/blob/2d1ac74ec0ca12ae4d36072525d976c1a596820a/examples/k8s/gencert.sh#L22 + +cat <req.cnf +[req] +req_extensions = v3_req +distinguished_name = req_distinguished_name + +[req_distinguished_name] + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = opa.example.com +IP.1 = 127.0.0.1 +EOF + +openssl genrsa -out ca-key.pem 2048 +openssl req -x509 -new -nodes -key ca-key.pem -days 3650 -out ca.pem -subj "/CN=my-ca" + +openssl genrsa -out client-key.pem 2048 +openssl req -new -key client-key.pem -out csr.pem -subj "/CN=my-client" +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 3650 + +openssl genrsa -out client-key-2.pem 2048 +openssl req -new -key client-key-2.pem -out csr.pem -subj "/CN=my-client-2" +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert-2.pem -days 3650 + +openssl genrsa -out server-key.pem 2048 +openssl req -new -key server-key.pem -out csr.pem -subj "/CN=my-server" -config req.cnf +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 3650 -extensions v3_req -extfile req.cnf diff --git a/third_party/opa/v1/test/e2e/tls/tls_test.go b/third_party/opa/v1/test/e2e/tls/tls_test.go new file mode 100644 index 000000000000..380b6dc19c86 --- /dev/null +++ b/third_party/opa/v1/test/e2e/tls/tls_test.go @@ -0,0 +1,259 @@ +package tls + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "flag" + "fmt" + "net/http" + "net/url" + "os" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/server" + "github.com/open-policy-agent/opa/v1/test/e2e" +) + +var testRuntime *e2e.TestRuntime +var pool *x509.CertPool + +var minTLSVersions = map[string]uint16{ + "1.0": tls.VersionTLS10, + "1.1": tls.VersionTLS11, + "1.2": tls.VersionTLS12, + "1.3": tls.VersionTLS13, +} + +// print error to stderr, exit 1 +func fatal(err any) { + fmt.Fprintf(os.Stderr, "%s\n", err) + os.Exit(1) +} + +func TestMain(m *testing.M) { + minTLSVersion := flag.String("min-tls-version", "1.2", "minimum TLS Version") + TLSVersion := minTLSVersions[*minTLSVersion] + flag.Parse() + + caCertPEM, err := os.ReadFile("testdata/ca.pem") + if err != nil { + fatal(err) + } + pool = x509.NewCertPool() + if ok := pool.AppendCertsFromPEM(caCertPEM); !ok { + fatal("failed to parse CA cert") + } + certFile := "testdata/server-cert.pem" + certKeyFile := "testdata/server-key.pem" + cert, err := tls.LoadX509KeyPair(certFile, certKeyFile) + if err != nil { + fatal(err) + } + + // We need the policy to be present already, otherwise authorization + // for the health endpoint is going to fail on server startup. + authzPolicy := []byte(`package system.authz +import rego.v1 +import input.identity +default allow = false +allow if { + identity = "CN=my-client" +}`) + + tmpfile, err := os.CreateTemp("", "authz.*.rego") + if err != nil { + fatal(err) + } + defer os.Remove(tmpfile.Name()) + + if _, err := tmpfile.Write(authzPolicy); err != nil { + fatal(err) + } + if err := tmpfile.Close(); err != nil { + fatal(err) + } + + testServerParams := e2e.NewAPIServerTestParams() + testServerParams.Addrs = &[]string{"https://127.0.0.1:0"} + testServerParams.CertPool = pool + testServerParams.Certificate = &cert + testServerParams.CertificateFile = certFile + testServerParams.CertificateKeyFile = certKeyFile + testServerParams.CertificateRefresh = time.Millisecond + testServerParams.Authentication = server.AuthenticationTLS + testServerParams.Authorization = server.AuthorizationBasic + testServerParams.Paths = []string{"system.authz:" + tmpfile.Name()} + if TLSVersion != 0 { + testServerParams.MinTLSVersion = TLSVersion + } + + // RSA cipher suite given server's key is RSA + testServerParams.CipherSuites = &[]uint16{tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA} + + testRuntime, err = e2e.NewTestRuntime(testServerParams) + if err != nil { + fatal(err) + } + + // We need a client with proper TLS setup, otherwise the health check + // that loops to determine if the server is ready will fail. + testRuntime.Client = newClient(0, pool, nil, "testdata/client-cert.pem", "testdata/client-key.pem") + + os.Exit(testRuntime.RunTests(m)) +} + +func TestCipherSuites(t *testing.T) { + endpoint := testRuntime.URL() + t.Run("Cipher suite supported by both client and server", func(t *testing.T) { + + c := newClient(tls.VersionTLS12, pool, &[]uint16{tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, "testdata/client-cert.pem", "testdata/client-key.pem") + _, err := c.Get(endpoint) + if err != nil { + t.Fatal(err) + } + }) + + t.Run("No cipher suite supported by both client and server", func(t *testing.T) { + + // Since server's key is RSA, client specifying an ECDSA cipher suite should result in an error + c := newClient(tls.VersionTLS12, pool, &[]uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, "testdata/client-cert.pem", "testdata/client-key.pem") + _, err := c.Get(endpoint) + if err == nil { + t.Error("expected err - no cipher suite supported by both client and server, got nil") + } + + expErr := "tls: handshake failure" + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("unexpected error message %v", err) + } + }) +} + +func TestMinTLSVersion(t *testing.T) { + endpoint := testRuntime.URL() + t.Run("TLS version not supported by server", func(t *testing.T) { + + c := newClient(tls.VersionTLS10, pool, nil, "testdata/client-cert.pem", "testdata/client-key.pem") + _, err := c.Get(endpoint) + + if err == nil { + t.Error("expected err - protocol version not supported, got nil") + } + + }) + t.Run("TLS Version supported by server", func(t *testing.T) { + + c := newClient(tls.VersionTLS12, pool, nil, "testdata/client-cert.pem", "testdata/client-key.pem") + resp, err := c.Get(endpoint) + if err != nil { + t.Fatalf("GET: %v", err) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + t.Errorf("expected status 200, got %s", resp.Status) + } + }) +} + +func TestNotDefaultTLSVersion(t *testing.T) { + + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + os.Args = []string{"cmd", "--min-tls-version", "1.3"} + endpoint := testRuntime.URL() + t.Run("server started with min TLS Version 1.3, client connecting with not supported TLS version", func(t *testing.T) { + + c := newClient(tls.VersionTLS10, pool, nil, "testdata/client-cert.pem", "testdata/client-key.pem") + _, err := c.Get(endpoint) + + if err == nil { + t.Error("expected err - protocol version not supported, got nil") + } + var exp *url.Error + if !errors.As(err, &exp) { + t.Errorf("expected err type %[1]T, got %[2]T: %[2]v", exp, err) + } + }) + + t.Run("server started with min TLS Version 1.3, client connecting supported TLS version", func(t *testing.T) { + + c := newClient(tls.VersionTLS13, pool, nil, "testdata/client-cert.pem", "testdata/client-key.pem") + resp, err := c.Get(endpoint) + if err != nil { + t.Fatalf("GET: %v", err) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + t.Errorf("expected status 200, got %s", resp.Status) + } + }) + +} + +func TestAuthenticationTLS(t *testing.T) { + endpoint := testRuntime.URL() + "/v1/data/foo" + + // Note: This test is redundant. When the testRuntime starts the server, it + // already queries the health endpoint using a properly authenticated, and + // authorized, http client. + t.Run("happy path", func(t *testing.T) { + c := newClient(0, pool, nil, "testdata/client-cert.pem", "testdata/client-key.pem") + resp, err := c.Get(endpoint) + if err != nil { + t.Fatalf("GET: %v", err) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + t.Errorf("expected status 200, got %s", resp.Status) + } + }) + + t.Run("authn successful, authz failed", func(t *testing.T) { + c := newClient(0, pool, nil, "testdata/client-cert-2.pem", "testdata/client-key-2.pem") + resp, err := c.Get(endpoint) + if err != nil { + t.Fatalf("GET: %v", err) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusUnauthorized { + t.Errorf("expected status 401, got %s", resp.Status) + } + }) + + t.Run("client trusts server, but doesn't provide client cert", func(t *testing.T) { + c := newClient(0, pool, nil) + _, err := c.Get(endpoint) + if _, ok := err.(*url.Error); !ok { + t.Errorf("expected *url.Error, got %T: %v", err, err) + } + }) +} + +func newClient(maxTLSVersion uint16, pool *x509.CertPool, cipherSuites *[]uint16, clientKeyPair ...string) *http.Client { + c := *http.DefaultClient + tr := http.DefaultTransport.(*http.Transport).Clone() + tr.TLSClientConfig = &tls.Config{ + RootCAs: pool, + } + + if len(clientKeyPair) == 2 { + clientCert, err := tls.LoadX509KeyPair(clientKeyPair[0], clientKeyPair[1]) + if err != nil { + panic(err) + } + tr.TLSClientConfig.Certificates = []tls.Certificate{clientCert} + } + if maxTLSVersion != 0 { + tr.TLSClientConfig.MaxVersion = maxTLSVersion + } + + if cipherSuites != nil { + tr.TLSClientConfig.CipherSuites = *cipherSuites + } + + c.Transport = tr + return &c +} diff --git a/third_party/opa/v1/test/e2e/wasm/authz/authz_bench_integration_test.go b/third_party/opa/v1/test/e2e/wasm/authz/authz_bench_integration_test.go new file mode 100644 index 000000000000..500a1b6614a7 --- /dev/null +++ b/third_party/opa/v1/test/e2e/wasm/authz/authz_bench_integration_test.go @@ -0,0 +1,186 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build opa_wasm +// +build opa_wasm + +package authz + +import ( + "bytes" + "context" + "encoding/json" + "flag" + "io" + "os" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/compile" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/test/authz" + testAuthz "github.com/open-policy-agent/opa/v1/test/authz" + "github.com/open-policy-agent/opa/v1/test/e2e" + "github.com/open-policy-agent/opa/v1/util" + "github.com/open-policy-agent/opa/v1/util/test" +) + +var testRuntime *e2e.TestRuntime + +var queryPath = strings.Replace(authz.AllowQuery, ".", "/", -1) + +func TestMain(m *testing.M) { + flag.Parse() + + testServerParams := e2e.NewAPIServerTestParams() + var cleanup func() error + testServerParams.DiskStorage, cleanup = diskStorage() + var err error + testRuntime, err = e2e.NewTestRuntime(testServerParams) + if err != nil { + panic(err) + } + + files := map[string]string{ + "policy.rego": testAuthz.Policy, + ".manifest": `{"roots": ["/policy/restauthz"]}`, + } + + bundleBuf := bytes.Buffer{} + ctx := context.Background() + + // TODO: Is there an option to do this more easily? + // Would be cool to be able to pass already loaded data/files into the compile stuff + test.WithTempFS(files, func(rootDir string) { + err = compile.New(). + WithAsBundle(true). + WithEntrypoints(strings.TrimPrefix(queryPath, "data/")). // Entrypoints shouldn't be "data" prefixed + WithOutput(&bundleBuf). + WithPaths(rootDir). + WithTarget(compile.TargetWasm). + Build(ctx) + + if err != nil { + panic(err) + } + }) + + testBundle, err := bundle.NewCustomReader(bundle.NewTarballLoader(&bundleBuf)).Read() + if err != nil { + panic(err) + } + + // Sneak the bundle in... + err = storage.Txn(ctx, testRuntime.Runtime.Store, storage.WriteParams, func(txn storage.Transaction) error { + compiler := ast.NewCompiler().WithPathConflictsCheck(storage.NonEmpty(ctx, testRuntime.Runtime.Store, txn)) + m := metrics.New() + + activation := &bundle.ActivateOpts{ + Ctx: ctx, + Store: testRuntime.Runtime.Store, + Txn: txn, + Compiler: compiler, + Metrics: m, + Bundles: map[string]*bundle.Bundle{"bundle1": &testBundle}, + } + + return bundle.Activate(activation) + }) + if err != nil { + panic(err) + } + + errc := testRuntime.RunTests(m) + if errc == 0 && cleanup != nil { + if err := cleanup(); err != nil { + panic(err) + } + } + os.Exit(errc) +} + +func BenchmarkRESTAuthzForbidAuthn(b *testing.B) { + runAuthzBenchmark(b, testAuthz.ForbidIdentity, 10) +} + +func BenchmarkRESTAuthzForbidPath(b *testing.B) { + runAuthzBenchmark(b, testAuthz.ForbidPath, 10) +} + +func BenchmarkRESTAuthzForbidMethod(b *testing.B) { + runAuthzBenchmark(b, testAuthz.ForbidMethod, 10) +} + +func BenchmarkRESTAuthzAllow10Paths(b *testing.B) { + runAuthzBenchmark(b, testAuthz.Allow, 10) +} + +func BenchmarkRESTAuthzAllow100Paths(b *testing.B) { + runAuthzBenchmark(b, testAuthz.Allow, 100) +} + +// TODO: Re-enable when performance issues have been addressed. +// func BenchmarkRESTAuthzAllow1000Paths(b *testing.B) { +// runAuthzBenchmark(b, testAuthz.Allow, 1000) +// } + +func runAuthzBenchmark(b *testing.B, mode testAuthz.InputMode, numPaths int) { + // Generate test data and create a new bundle from it + profile := testAuthz.DataSetProfile{ + NumTokens: 1000, + NumPaths: numPaths, + } + data := testAuthz.GenerateDataset(profile) + + err := testRuntime.UploadDataToPath("/restauthz", bytes.NewReader(util.MustMarshalJSON(data["restauthz"]))) + if err != nil { + b.Fatal(err) + } + + url := testRuntime.URL() + "/v1/" + queryPath + + input, expected := testAuthz.GenerateInput(profile, mode) + inputPayload := util.MustMarshalJSON(map[string]any{ + "input": input, + }) + inputReader := bytes.NewReader(inputPayload) + + b.ResetTimer() + + for range b.N { + // The benchmark will include the time it takes to make the request, + // receive a response, and do any normal client error checking on + // the response. The benchmark is for the OPA server, not how + // long it takes the golang client to unpack the response body. + b.StartTimer() + resp, err := testRuntime.GetDataWithRawInput(url, inputReader) + if err != nil { + b.Fatal(err) + } + b.StopTimer() + + body, err := io.ReadAll(resp) + if err != nil { + b.Fatalf("unexpected error reading response body: %s", err) + } + resp.Close() + + parsedBody := struct { + Result bool `json:"result"` + }{} + + err = json.Unmarshal(body, &parsedBody) + if err != nil { + b.Fatalf("Failed to parse body: \n\nActual: %s\n\nExpected: {\"result\": BOOL}\n\nerr = %s ", string(body), err) + } + if parsedBody.Result != expected { + b.Fatalf("Unexpected result: %v", parsedBody.Result) + } + + inputReader.Reset(inputPayload) + } +} diff --git a/third_party/opa/v1/test/e2e/wasm/authz/disk.go b/third_party/opa/v1/test/e2e/wasm/authz/disk.go new file mode 100644 index 000000000000..124a23407b40 --- /dev/null +++ b/third_party/opa/v1/test/e2e/wasm/authz/disk.go @@ -0,0 +1,24 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build bench_disk +// +build bench_disk + +// nolint: deadcode,unused // build tags confuse these linters +package authz + +import ( + "os" + + "github.com/open-policy-agent/opa/v1/storage/disk" +) + +func diskStorage() (*disk.Options, func() error) { + dir, err := os.CreateTemp("", "disk-store") + if err != nil { + panic(err) + } + + return &disk.Options{Dir: dir, Partitions: nil}, func() error { return os.RemoveAll(dir) } +} diff --git a/third_party/opa/v1/test/e2e/wasm/authz/nodisk.go b/third_party/opa/v1/test/e2e/wasm/authz/nodisk.go new file mode 100644 index 000000000000..50109ea596c6 --- /dev/null +++ b/third_party/opa/v1/test/e2e/wasm/authz/nodisk.go @@ -0,0 +1,15 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build !bench_disk +// +build !bench_disk + +// nolint: deadcode,unused // build tags confuse these linters +package authz + +import "github.com/open-policy-agent/opa/v1/storage/disk" + +func diskStorage() (*disk.Options, func() error) { + return nil, nil +} diff --git a/third_party/opa/v1/test/scheduler/scheduler_bench_test.go b/third_party/opa/v1/test/scheduler/scheduler_bench_test.go new file mode 100644 index 000000000000..cf6264daa544 --- /dev/null +++ b/third_party/opa/v1/test/scheduler/scheduler_bench_test.go @@ -0,0 +1,357 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package scheduler + +import ( + "bytes" + "fmt" + "testing" + "text/template" + + "context" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/util" +) + +// FIXME(tsandall): scheduling policy depends heavily on data indexing to +// provide adequate performance. Data indexing has been removed until it can be +// performed during compilation. Once data indexing is restored, the large +// benchmarks can be re-enabled. + +func BenchmarkScheduler10x30(b *testing.B) { + runSchedulerBenchmark(b, 10, 30) +} + +type benchmarkParams struct { + store storage.Store + compiler *ast.Compiler + input any +} + +func runSchedulerBenchmark(b *testing.B, nodes int, pods int) { + ctx := context.Background() + params := setupBenchmark(nodes, pods) + b.ResetTimer() + for range b.N { + rego := rego.New( + rego.Compiler(params.compiler), + rego.Store(params.store), + rego.Input(params.input), + rego.Query("data.opa.test.scheduler.fit"), + ) + rs, err := rego.Eval(ctx) + if err != nil { + b.Fatal("unexpected error:", err) + } + ws := rs[0].Expressions[0].Value.(map[string]any) + if len(ws) != nodes { + b.Fatal("unexpected query result:", rs) + } + for n, w := range ws { + if fmt.Sprint(w) != "5.0138888888888888886" { + b.Fatalf("unexpected weight for: %v: %v\n\nDumping all weights:\n\n%v\n", n, w, rs) + } + } + } +} + +func setupBenchmark(nodes int, pods int) benchmarkParams { + + // policy compilation + c := ast.NewCompiler() + modules := map[string]*ast.Module{ + "test": ast.MustParseModule(policy), + } + + if c.Compile(modules); c.Failed() { + panic(c.Errors) + } + + // storage setup + store := inmem.New() + + // parameter setup + ctx := context.Background() + input := util.MustUnmarshalJSON([]byte(requestedPod)) + + // data setup + txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) + setupNodes(ctx, store, txn, nodes) + setupRCs(ctx, store, txn, 1) + setupPods(ctx, store, txn, pods, nodes) + if err := store.Commit(ctx, txn); err != nil { + panic(err) + } + + return benchmarkParams{ + store: store, + compiler: c, + input: input, + } +} + +type nodeTemplateInput struct { + Name string +} + +type podTemplateInput struct { + Name string + NodeName string +} + +func setupNodes(ctx context.Context, store storage.Store, txn storage.Transaction, n int) { + tmpl, err := template.New("node").Parse(nodeTemplate) + if err != nil { + panic(err) + } + if err := store.Write(ctx, txn, storage.AddOp, storage.MustParsePath("/nodes"), map[string]any{}); err != nil { + panic(err) + } + for i := range n { + input := nodeTemplateInput{ + Name: fmt.Sprintf("node%v", i), + } + v := runTemplate(tmpl, input) + path := storage.MustParsePath(fmt.Sprintf("/nodes/%v", input.Name)) + if err := store.Write(ctx, txn, storage.AddOp, path, v); err != nil { + panic(err) + } + } +} + +func setupRCs(ctx context.Context, store storage.Store, txn storage.Transaction, n int) { + tmpl, err := template.New("rc").Parse(nodeTemplate) + if err != nil { + panic(err) + } + path := storage.MustParsePath("/replicationcontrollers") + if err := store.Write(ctx, txn, storage.AddOp, path, map[string]any{}); err != nil { + panic(err) + } + for i := range n { + input := nodeTemplateInput{ + Name: fmt.Sprintf("rc%v", i), + } + v := runTemplate(tmpl, input) + path = storage.MustParsePath(fmt.Sprintf("/replicationcontrollers/%v", input.Name)) + if err := store.Write(ctx, txn, storage.AddOp, path, v); err != nil { + panic(err) + } + } +} + +func setupPods(ctx context.Context, store storage.Store, txn storage.Transaction, n int, numNodes int) { + tmpl, err := template.New("pod").Parse(podTemplate) + if err != nil { + panic(err) + } + path := storage.MustParsePath("/pods") + if err := store.Write(ctx, txn, storage.AddOp, path, map[string]any{}); err != nil { + panic(err) + } + for i := range n { + input := podTemplateInput{ + Name: fmt.Sprintf("pod%v", i), + NodeName: fmt.Sprintf("node%v", i%numNodes), + } + v := runTemplate(tmpl, input) + path = storage.MustParsePath(fmt.Sprintf("/pods/%v", input.Name)) + if err := store.Write(ctx, txn, storage.AddOp, path, v); err != nil { + panic(err) + } + } +} + +func runTemplate(tmpl *template.Template, input any) any { + var buf bytes.Buffer + if err := tmpl.Execute(&buf, input); err != nil { + panic(err) + } + var v any + if err := util.UnmarshalJSON(buf.Bytes(), &v); err != nil { + panic(err) + } + return v +} + +const ( + nodeTemplate = ` + {"status": { + "capacity": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": "1", + "memory": "3840Mi" + }, + "addresses": [ + { + "type": "LegacyHostIP", + "address": "172.17.0.5" + }, + { + "type": "InternalIP", + "address": "172.17.0.5" + } + ], + "nodeInfo": { + "kernelVersion": "", + "kubeletVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "containerRuntimeVersion": "docker://1.8.1", + "machineID": "", + "kubeProxyVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "bootID": "", + "osImage": "", + "architecture": "amd64", + "systemUUID": "", + "operatingSystem": "linux" + }, + "allocatable": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": 1000, + "memory": 4026531840 + }, + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "conditions": [ + { + "status": "False", + "lastTransitionTime": "2016-07-08T19:09:41Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available", + "type": "OutOfDisk" + }, + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:03:29Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletHasSufficientMemory", + "message": "kubelet has sufficient memory available", + "type": "MemoryPressure" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T19:09:41Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status", + "type": "Ready" + } + ] + }, + "kind": "Node", + "spec": { + "externalID": "172.17.0.5" + }, + "apiVersion": "v1", + "metadata": { + "uid": "{{ .Name }}", + "labels": { + "kubernetes.io/hostname": "172.17.0.5", + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64" + }, + "resourceVersion": "96999", + "creationTimestamp": "2016-07-08T16:03:29Z", + "selfLink": "/api/v1/nodes/172.17.0.5", + "name": "{{ .Name }}" + } + }` + + podTemplate = ` + { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:05Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-nm3wu_kubemark_e4b7acdc-4614-11e6-bd6d-0800275521ee_b63ce19a" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:04Z", + "hostIP": "172.17.0.10", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:04Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:06Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:04Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "{{ .NodeName }}", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "{{ .Name }}", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96837", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-nm3wu", + "uid": "{{ .Name }}" + } + } + ` +) diff --git a/third_party/opa/v1/test/scheduler/scheduler_test.go b/third_party/opa/v1/test/scheduler/scheduler_test.go new file mode 100644 index 000000000000..0d096b96fa3d --- /dev/null +++ b/third_party/opa/v1/test/scheduler/scheduler_test.go @@ -0,0 +1,483 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package scheduler + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "context" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestScheduler(t *testing.T) { + ctx := context.Background() + rego := setup(ctx, t, "data_10nodes_30pods.json") + + rs, err := rego.Eval(ctx) + + if err != nil { + t.Fatal("unexpected error:", err) + } + ws := rs[0].Expressions[0].Value.(map[string]any) + if len(ws) != 10 { + t.Fatal("unexpected query result:", rs) + } + for n, w := range ws { + if fmt.Sprint(w) != "5.0138888888888888886" { + t.Fatalf("unexpected weight for: %v: %v\n\nDumping all weights:\n\n%v\n", n, w, rs) + } + } +} + +func setup(_ context.Context, t *testing.T, filename string) *rego.Rego { + + // policy compilation + c := ast.NewCompiler() + modules := map[string]*ast.Module{ + "test": ast.MustParseModule(policy), + } + + if c.Compile(modules); c.Failed() { + t.Fatal("unexpected error:", c.Errors) + } + + // storage setup + store := loadDataStore(filename) + + // parameter setup + input := util.MustUnmarshalJSON([]byte(requestedPod)) + + return rego.New( + rego.Compiler(c), + rego.Store(store), + rego.Input(input), + rego.Query("data.opa.test.scheduler.fit"), + ) +} + +func loadDataStore(filename string) storage.Store { + f, err := os.Open(getFilename(filename)) + if err != nil { + panic(err) + } + defer f.Close() + return inmem.NewFromReader(f) +} + +func getFilename(filename string) string { + return filepath.Join("testdata", filename) +} + +const ( + requestedPod = `{"pod": { + "status": { + "phase": "Pending" + }, + "kind": "Pod", + "spec": { + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx-mdj4s", + "resourceVersion": "102515", + "generateName": "nginx-", + "namespace": "kubemark", + "labels": { + "app": "nginx30" + }, + "creationTimestamp": "2016-07-09T22:01:27Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx\",\"uid\":\"af24f2bf-4620-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"102514\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx-mdj4s", + "uid": "af25a765-4620-11e6-bd6d-0800275521ee" + } +}}` + + policy = ` +package opa.test.scheduler + +import rego.v1 +import data.nodes +import data.pods +import data.pvs +import data.pvcs +import data.services +import data.replicationcontrollers as rcs +import input.pod as req + +# Fit rule for all pods. Implements same filtering and +# prioritisation logic that is included by default in Kubernetes. +fit[node_name] = weight if { + scheduler_name[my_scheduler_name] + filter[node_id] + weight := prioritise[node_id] + node_name := nodes[node_id].metadata.name +} + +filter contains node_id if { + # Filtering for all pods except hollow node pods. + not hollow_node + not blacklisted[nodes[node_id].metadata.name] + not port_conflicts[node_id] + not disk_conflicts[node_id] + resources_available[node_id] +} { + # Filtering for hollow node pods. Force them all onto + # localhost node for testing purposes. + hollow_node + nodes[node_id].metadata.name == "127.0.0.1" +} + +port_conflicts contains node_id if { + node := nodes[node_id] + pods[i].spec.nodeName == node.metadata.name + container := pods[i].spec.containers[j] + port := container.ports[k].hostPort + req_container := req.spec.containers[l] + req_port := req_container.ports[m].hostPort + req_port == port +} + +disk_conflicts contains node_id if { + gce_persistent_disk_conflicts[node_id] + aws_ebs_conflicts[node_id] + rbd_conflicts[node_id] +} + +gce_persistent_disk_conflicts contains node_id if { + req_disk := req.spec.volumes[i].gcePersistentDisk + not req_disk.readOnly + node := nodes[node_id] + pod := pods[j] + pod.spec.nodeName == node.metadata.name + disk := pod.volumes[k].gcePersistentDisk + req_disk.pdName == disk.pdName +} { + req_disk := req.spec.volumes[i].gcePersistentDisk + req_disk.readOnly + node := nodes[node_id] + pod := pods[j] + pod.spec.nodeName == node.metadata.name + disk := pod.volumes[k].gcePersistentDisk + req_disk.pdName == disk.pdName + not disk.readOnly +} + +aws_ebs_conflicts contains node_id if { + req_disk := req.spec.volumes[i].awsElasticBlockStore + node := nodes[node_id] + pod := pods[j] + pod.spec.nodeName == node.metadata.name + disk := pod.volumes[k].awsElasticBlockStore + disk.volumeID == req_disk.volumeID +} + +rbd_conflicts contains node_id if { + req_disk := req.spec.volumes[i].rbd + node := nodes[node_id] + pod := pods[j] + pod.spec.nodeName == node.metadata.name + disk = pod.volumes[k].rbd + req_disk.image == disk.image + req_disk.pool == disk.pool + req_disk.monitors[l] == disk.monitors[m] +} + +pv_zone_label_match contains node_id if { + req_volume := req.spec.volumes[i] + req_claim_name := req_volume.persistentVolumeClaim.claimName + req_namespace := req.metadata.namespace + pvcs[j].metadata.namespace == req_namespace + pvcs[j].metadata.name == req_claim_name + pvs[k].metadata.name == pvcs[j].spec.volumeName + label := zone_labels[l] + value := pvs[k].metadata.labels[label] + nodes[node_id].metadata.labels[label] == value +} + +resources_available contains node_id if { + node := nodes[node_id] + not pods_exceeded[node_id] + not mem_exceeded[node_id] + not cpu_exceeded[node_id] +} + +pods_exceeded contains node_id if { + num_pods := count(pods_on_node[node_id]) + max_pods := to_number(nodes[node_id].status.allocatable.pods) + num_pods >= max_pods +} + +mem_exceeded contains node_id if { + alloc := allocatable_mem[node_id] + total := mem_total[node_id] + total >= alloc +} + +cpu_exceeded contains node_id if { + alloc := allocatable_cpu[node_id] + total := cpu_total[node_id] + total >= alloc +} + +cpu_total[node_id] = sum([cpu | cpu := req_cpu[_]]) + used_cpu[node_id] + +mem_total[node_id] = sum([mem | mem := req_mem[_]]) + used_mem[node_id] + +cpu_nonzero_total[node_id] = sum([cpu | cpu := req_cpu[_]]) + used_nonzero_cpu[node_id] + +mem_nonzero_total[node_id] = sum([mem | mem := req_mem[_]]) + used_nonzero_mem[node_id] + +req_cpu[name] = cpu if { + container := req.spec.containers[_] + name := container.name + cpu := container.resources.requests.cpu +} { + container := req.spec.containers[i] + name := container.name + not container.resources.requests.cpu + cpu := default_milli_cpu_req +} + +req_mem[name] = mem if { + container := req.spec.containers[_] + name := container.name + mem := container.resources.requests.memory +} { + container := req.spec.containers[_] + name := container.name + not container.resources.requests.memory + mem := default_memory_req +} + +allocatable_mem[node_id] = alloc if { + alloc := nodes[node_id].status.allocatable.memory +} + +allocatable_cpu[node_id] = alloc if { + alloc := nodes[node_id].status.allocatable.cpu +} + +used_mem[node_id] = used if { + node_pods := pods_on_node[node_id] + mem := [m | pod := node_pods[_] + container := pod.spec.containers[_] + requested := container.resources.requests + m := requested.memory] + used := sum(mem) +} + +used_cpu[node_id] = used if { + node_pods := pods_on_node[node_id] + cpu := [c | pod := node_pods[_] + container := pod.spec.containers[_] + requested := container.resources.requests + c := requested.cpu] + used := sum(cpu) +} + +used_nonzero_mem[node_id] = used if { + node_pods := pods_on_node[node_id] + mem := [m | pod := node_pods[_] + container := pod.spec.containers[_] + requested := container.resources.requests + m := requested.memory] + def := [m | pod := node_pods[_] + container := pod.spec.containers[_] + not container.resources.requests.memory + m := default_memory_req] + used := sum(mem) + sum(def) +} + +used_nonzero_cpu[node_id] = used if { + node_pods := pods_on_node[node_id] + cpu = [c | pod := node_pods[_] + container := pod.spec.containers[_] + requested := container.resources.requests + c := requested.cpu] + def = [c | pod := node_pods[_] + container := pod.spec.containers[_] + not container.resources.requests.cpu + c := default_milli_cpu_req] + used = sum(cpu) + sum(def) +} + +pods_on_node[node_id] = pds if { + node_name := nodes[node_id].metadata.name + pds := [p | pods[i].spec.nodeName == node_name; p := pods[i]] +} + +hollow_node if { + req.metadata.labels[i] == "hollow-node" +} + +blacklisted contains node_name if { + node_names := [ + "127.0.0.1" + ] + node_name := node_names[i] +} + +my_scheduler_name = "experimental" + +# This scheduler is responsible for pods annotated with the following scheduler names. +scheduler_name contains scheduler if { + scheduler := req.metadata.annotations[k8s_scheduler_annotations] +} + +# Scheduler annotation. This annotation indicates whether the scheduler is responsible +# for this pod. +k8s_scheduler_annotation = "scheduler.alpha.kubernetes.io/name" + +# The maximum number of EBS volumes +# See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/volume_limits.html#linux-specific-volume-limits +max_ebs_pd_volumes = 39 + +# The maximum number of GCE PersistentDisk volumes +# https://cloud.google.com/compute/docs/disks/#introduction +max_gce_pd_volumes = 16 + +zone_labels = [ + "failure-domain.beta.kubernetes.io/zone", + "failure-domain.beta.kubernetes.io/region" +] + +taint_annotation = "scheduler.alpha.kubernetes.io/taints" +toleration_annotation = "scheduler.alpha.kubernetes.io/tolerations" + +default_milli_cpu_req = 100 # 0.1 cores +default_memory_req = 209715200 # 200MB + +prioritise[node_id] = weight if { + weight := sum([ + selector_spreading[node_id], + balanced_allocation[node_id], + least_requested[node_id]]) / 3 +} + +least_requested[node_id] = weight if { + weight := (cpu_weight[node_id] + mem_weight[node_id]) / 2 +} + +cpu_weight[node_id] = weight if { + cpu_capacity := allocatable_cpu[node_id] + weight := ((cpu_capacity - cpu_nonzero_total[node_id]) * 10) / cpu_capacity +} + +mem_weight[node_id] = weight if { + mem_capacity := allocatable_mem[node_id] + weight := ((mem_capacity - mem_nonzero_total[node_id]) * 10) / mem_capacity +} + +balanced_allocation[node_id] = weight if { + mem_f := mem_fraction[node_id] + cpu_f := cpu_fraction[node_id] + mem_f < 1 + cpu_f < 1 + weight := (10 - (abs(cpu_f - mem_f) * 10)) +} { + mem_fraction[node_id] >= 1 + cpu_fraction[node_id] >= 1 + weight = 0 +} { + mem_fraction[node_id] < 1 + cpu_fraction[node_id] >= 1 + weight = 0 +} { + mem_fraction[node_id] >= 1 + cpu_fraction[node_id] < 1 + weight = 0 +} + +cpu_fraction[node_id] = f if { + f := cpu_nonzero_total[node_id] / allocatable_cpu[node_id] +} + +mem_fraction[node_id] = f if { + f := mem_nonzero_total[node_id] / allocatable_mem[node_id] +} + +selector_spreading[node_id] = weight if { + max_count := max_rc_match_count + weight := ((max_count - rc_match_count[node_id]) / max_count) * 10 +} + +max_rc_match_count = max_count if { + max([1, max([c | c := rc_match_count[_]])], max_count) +} + +rc_match_count[node_id] = cnt if { + nodes[node_id] + rcs_req_matches[rc_id] + cnt := count([1 | rcs_on_node[node_id][_] == rc_id]) +} + +rcs_on_node[node_id] = rc_ids if { + pods_on_node[node_id] = node_pods + rc_ids = [ rc_id | pod := node_pods[_] + rc_id := rcs_for_pod[pod.metadata.uid][_]] +} + +rcs_for_pod[pod_id] = rc_ids if { + pods[pod_id] + rc_ids = [rc_id | rcs[rc_id] + selector_matches[[pod_id, rc_id]]] +} + +selector_matches contains [pod_id, rc_id] if { + pods[pod_id] + rcs[rc_id] + x = [pod_id, rc_id] + not selector_not_matches[x] +} + +selector_not_matches contains [pod_id, rc_id] if { + pods[pod_id] = pod + rc := rcs[rc_id] + v := rc.spec.selector[k] + not pod.metadata.labels[k] = v +} + +rcs_req_matches contains rc_id if { + rcs[rc_id] + not rcs_req_not_matches[rc_id] +} + +rcs_req_not_matches contains rc_id if { + value := rcs[rc_id].spec.selector[label] + not req.metadata.labels[label] = value +} + +` +) diff --git a/third_party/opa/v1/test/scheduler/testdata/data_10nodes_30pods.json b/third_party/opa/v1/test/scheduler/testdata/data_10nodes_30pods.json new file mode 100644 index 000000000000..5e72e93bafd6 --- /dev/null +++ b/third_party/opa/v1/test/scheduler/testdata/data_10nodes_30pods.json @@ -0,0 +1,5569 @@ +{ + "services": { + "2dd1b25c-4525-11e6-bd6d-0800275521ee": { + "status": { + "loadBalancer": {} + }, + "spec": { + "clusterIP": "10.0.0.1", + "type": "ClusterIP", + "ports": [ + { + "targetPort": 443, + "protocol": "TCP", + "name": "https", + "port": 443 + } + ], + "sessionAffinity": "ClientIP" + }, + "metadata": { + "name": "kubernetes", + "labels": { + "component": "apiserver", + "provider": "kubernetes" + }, + "namespace": "default", + "resourceVersion": "8", + "creationTimestamp": "2016-07-08T16:01:06Z", + "selfLink": "/api/v1/namespaces/default/services/kubernetes", + "uid": "2dd1b25c-4525-11e6-bd6d-0800275521ee" + } + } + }, + "nodes": { + "83c691de-4525-11e6-bd6d-0800275521ee": { + "status": { + "capacity": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": "1", + "memory": "3840Mi" + }, + "addresses": [ + { + "type": "LegacyHostIP", + "address": "172.17.0.7" + }, + { + "type": "InternalIP", + "address": "172.17.0.7" + } + ], + "nodeInfo": { + "kernelVersion": "", + "kubeletVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "containerRuntimeVersion": "docker://1.8.1", + "machineID": "", + "kubeProxyVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "bootID": "", + "osImage": "", + "architecture": "amd64", + "systemUUID": "", + "operatingSystem": "linux" + }, + "allocatable": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": 1000, + "memory": 4026531840 + }, + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "conditions": [ + { + "status": "False", + "lastTransitionTime": "2016-07-09T18:26:49Z", + "lastHeartbeatTime": "2016-07-09T20:38:18Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available", + "type": "OutOfDisk" + }, + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:03:30Z", + "lastHeartbeatTime": "2016-07-09T20:38:18Z", + "reason": "KubeletHasSufficientMemory", + "message": "kubelet has sufficient memory available", + "type": "MemoryPressure" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T19:56:04Z", + "lastHeartbeatTime": "2016-07-09T20:38:18Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status", + "type": "Ready" + } + ] + }, + "kind": "Node", + "spec": { + "externalID": "172.17.0.7" + }, + "apiVersion": "v1", + "metadata": { + "uid": "83c691de-4525-11e6-bd6d-0800275521ee", + "labels": { + "kubernetes.io/hostname": "172.17.0.7", + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64" + }, + "resourceVersion": "96994", + "creationTimestamp": "2016-07-08T16:03:30Z", + "selfLink": "/api/v1/nodes/172.17.0.7", + "name": "172.17.0.7" + } + }, + "7f2f6c13-4525-11e6-bd6d-0800275521ee": { + "status": { + "capacity": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": "1", + "memory": "3840Mi" + }, + "addresses": [ + { + "type": "LegacyHostIP", + "address": "172.17.0.4" + }, + { + "type": "InternalIP", + "address": "172.17.0.4" + } + ], + "nodeInfo": { + "kernelVersion": "", + "kubeletVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "containerRuntimeVersion": "docker://1.8.1", + "machineID": "", + "kubeProxyVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "bootID": "", + "osImage": "", + "architecture": "amd64", + "systemUUID": "", + "operatingSystem": "linux" + }, + "allocatable": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": 1000, + "memory": 4026531840 + }, + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "conditions": [ + { + "status": "False", + "lastTransitionTime": "2016-07-09T18:26:48Z", + "lastHeartbeatTime": "2016-07-09T20:38:16Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available", + "type": "OutOfDisk" + }, + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:03:23Z", + "lastHeartbeatTime": "2016-07-09T20:38:16Z", + "reason": "KubeletHasSufficientMemory", + "message": "kubelet has sufficient memory available", + "type": "MemoryPressure" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T18:26:48Z", + "lastHeartbeatTime": "2016-07-09T20:38:16Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status", + "type": "Ready" + } + ] + }, + "kind": "Node", + "spec": { + "externalID": "172.17.0.4" + }, + "apiVersion": "v1", + "metadata": { + "uid": "7f2f6c13-4525-11e6-bd6d-0800275521ee", + "labels": { + "kubernetes.io/hostname": "172.17.0.4", + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64" + }, + "resourceVersion": "96991", + "creationTimestamp": "2016-07-08T16:03:23Z", + "selfLink": "/api/v1/nodes/172.17.0.4", + "name": "172.17.0.4" + } + }, + "82b4246b-4525-11e6-bd6d-0800275521ee": { + "status": { + "capacity": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": "1", + "memory": "3840Mi" + }, + "addresses": [ + { + "type": "LegacyHostIP", + "address": "172.17.0.5" + }, + { + "type": "InternalIP", + "address": "172.17.0.5" + } + ], + "nodeInfo": { + "kernelVersion": "", + "kubeletVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "containerRuntimeVersion": "docker://1.8.1", + "machineID": "", + "kubeProxyVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "bootID": "", + "osImage": "", + "architecture": "amd64", + "systemUUID": "", + "operatingSystem": "linux" + }, + "allocatable": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": 1000, + "memory": 4026531840 + }, + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "conditions": [ + { + "status": "False", + "lastTransitionTime": "2016-07-08T19:09:41Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available", + "type": "OutOfDisk" + }, + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:03:29Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletHasSufficientMemory", + "message": "kubelet has sufficient memory available", + "type": "MemoryPressure" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T19:09:41Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status", + "type": "Ready" + } + ] + }, + "kind": "Node", + "spec": { + "externalID": "172.17.0.5" + }, + "apiVersion": "v1", + "metadata": { + "uid": "82b4246b-4525-11e6-bd6d-0800275521ee", + "labels": { + "kubernetes.io/hostname": "172.17.0.5", + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64" + }, + "resourceVersion": "96999", + "creationTimestamp": "2016-07-08T16:03:29Z", + "selfLink": "/api/v1/nodes/172.17.0.5", + "name": "172.17.0.5" + } + }, + "7f030ed0-4525-11e6-bd6d-0800275521ee": { + "status": { + "capacity": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": "1", + "memory": "3840Mi" + }, + "addresses": [ + { + "type": "LegacyHostIP", + "address": "172.17.0.3" + }, + { + "type": "InternalIP", + "address": "172.17.0.3" + } + ], + "nodeInfo": { + "kernelVersion": "", + "kubeletVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "containerRuntimeVersion": "docker://1.8.1", + "machineID": "", + "kubeProxyVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "bootID": "", + "osImage": "", + "architecture": "amd64", + "systemUUID": "", + "operatingSystem": "linux" + }, + "allocatable": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": 1000, + "memory": 4026531840 + }, + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "conditions": [ + { + "status": "False", + "lastTransitionTime": "2016-07-09T19:55:58Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available", + "type": "OutOfDisk" + }, + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:03:22Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletHasSufficientMemory", + "message": "kubelet has sufficient memory available", + "type": "MemoryPressure" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T19:55:58Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status", + "type": "Ready" + } + ] + }, + "kind": "Node", + "spec": { + "externalID": "172.17.0.3" + }, + "apiVersion": "v1", + "metadata": { + "uid": "7f030ed0-4525-11e6-bd6d-0800275521ee", + "labels": { + "kubernetes.io/hostname": "172.17.0.3", + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64" + }, + "resourceVersion": "96996", + "creationTimestamp": "2016-07-08T16:03:22Z", + "selfLink": "/api/v1/nodes/172.17.0.3", + "name": "172.17.0.3" + } + }, + "8820a49a-4525-11e6-bd6d-0800275521ee": { + "status": { + "capacity": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": "1", + "memory": "3840Mi" + }, + "addresses": [ + { + "type": "LegacyHostIP", + "address": "172.17.0.8" + }, + { + "type": "InternalIP", + "address": "172.17.0.8" + } + ], + "nodeInfo": { + "kernelVersion": "", + "kubeletVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "containerRuntimeVersion": "docker://1.8.1", + "machineID": "", + "kubeProxyVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "bootID": "", + "osImage": "", + "architecture": "amd64", + "systemUUID": "", + "operatingSystem": "linux" + }, + "allocatable": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": 1000, + "memory": 4026531840 + }, + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "conditions": [ + { + "status": "False", + "lastTransitionTime": "2016-07-09T02:16:41Z", + "lastHeartbeatTime": "2016-07-09T20:38:16Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available", + "type": "OutOfDisk" + }, + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:03:38Z", + "lastHeartbeatTime": "2016-07-09T20:38:16Z", + "reason": "KubeletHasSufficientMemory", + "message": "kubelet has sufficient memory available", + "type": "MemoryPressure" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T02:16:41Z", + "lastHeartbeatTime": "2016-07-09T20:38:16Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status", + "type": "Ready" + } + ] + }, + "kind": "Node", + "spec": { + "externalID": "172.17.0.8" + }, + "apiVersion": "v1", + "metadata": { + "uid": "8820a49a-4525-11e6-bd6d-0800275521ee", + "labels": { + "kubernetes.io/hostname": "172.17.0.8", + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64" + }, + "resourceVersion": "96993", + "creationTimestamp": "2016-07-08T16:03:38Z", + "selfLink": "/api/v1/nodes/172.17.0.8", + "name": "172.17.0.8" + } + }, + "81b4ef97-4525-11e6-bd6d-0800275521ee": { + "status": { + "capacity": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": "1", + "memory": "3840Mi" + }, + "addresses": [ + { + "type": "LegacyHostIP", + "address": "172.17.0.2" + }, + { + "type": "InternalIP", + "address": "172.17.0.2" + } + ], + "nodeInfo": { + "kernelVersion": "", + "kubeletVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "containerRuntimeVersion": "docker://1.8.1", + "machineID": "", + "kubeProxyVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "bootID": "", + "osImage": "", + "architecture": "amd64", + "systemUUID": "", + "operatingSystem": "linux" + }, + "allocatable": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": 1000, + "memory": 4026531840 + }, + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "conditions": [ + { + "status": "False", + "lastTransitionTime": "2016-07-09T18:26:47Z", + "lastHeartbeatTime": "2016-07-09T20:38:26Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available", + "type": "OutOfDisk" + }, + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:03:27Z", + "lastHeartbeatTime": "2016-07-09T20:38:26Z", + "reason": "KubeletHasSufficientMemory", + "message": "kubelet has sufficient memory available", + "type": "MemoryPressure" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T18:26:47Z", + "lastHeartbeatTime": "2016-07-09T20:38:26Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status", + "type": "Ready" + } + ] + }, + "kind": "Node", + "spec": { + "externalID": "172.17.0.2" + }, + "apiVersion": "v1", + "metadata": { + "uid": "81b4ef97-4525-11e6-bd6d-0800275521ee", + "labels": { + "kubernetes.io/hostname": "172.17.0.2", + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64" + }, + "resourceVersion": "97001", + "creationTimestamp": "2016-07-08T16:03:27Z", + "selfLink": "/api/v1/nodes/172.17.0.2", + "name": "172.17.0.2" + } + }, + "86c0ba55-4525-11e6-bd6d-0800275521ee": { + "status": { + "capacity": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": "1", + "memory": "3840Mi" + }, + "addresses": [ + { + "type": "LegacyHostIP", + "address": "172.17.0.9" + }, + { + "type": "InternalIP", + "address": "172.17.0.9" + } + ], + "nodeInfo": { + "kernelVersion": "", + "kubeletVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "containerRuntimeVersion": "docker://1.8.1", + "machineID": "", + "kubeProxyVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "bootID": "", + "osImage": "", + "architecture": "amd64", + "systemUUID": "", + "operatingSystem": "linux" + }, + "allocatable": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": 1000, + "memory": 4026531840 + }, + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "conditions": [ + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:03:35Z", + "lastHeartbeatTime": "2016-07-09T20:38:16Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available", + "type": "OutOfDisk" + }, + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:03:35Z", + "lastHeartbeatTime": "2016-07-09T20:38:16Z", + "reason": "KubeletHasSufficientMemory", + "message": "kubelet has sufficient memory available", + "type": "MemoryPressure" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T19:56:03Z", + "lastHeartbeatTime": "2016-07-09T20:38:16Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status", + "type": "Ready" + } + ] + }, + "kind": "Node", + "spec": { + "externalID": "172.17.0.9" + }, + "apiVersion": "v1", + "metadata": { + "uid": "86c0ba55-4525-11e6-bd6d-0800275521ee", + "labels": { + "kubernetes.io/hostname": "172.17.0.9", + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64" + }, + "resourceVersion": "96992", + "creationTimestamp": "2016-07-08T16:03:35Z", + "selfLink": "/api/v1/nodes/172.17.0.9", + "name": "172.17.0.9" + } + }, + "803b2e59-4525-11e6-bd6d-0800275521ee": { + "status": { + "capacity": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": "1", + "memory": "3840Mi" + }, + "addresses": [ + { + "type": "LegacyHostIP", + "address": "172.17.0.6" + }, + { + "type": "InternalIP", + "address": "172.17.0.6" + } + ], + "nodeInfo": { + "kernelVersion": "", + "kubeletVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "containerRuntimeVersion": "docker://1.8.1", + "machineID": "", + "kubeProxyVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "bootID": "", + "osImage": "", + "architecture": "amd64", + "systemUUID": "", + "operatingSystem": "linux" + }, + "allocatable": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": 1000, + "memory": 4026531840 + }, + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "conditions": [ + { + "status": "False", + "lastTransitionTime": "2016-07-09T19:55:58Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available", + "type": "OutOfDisk" + }, + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:03:24Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletHasSufficientMemory", + "message": "kubelet has sufficient memory available", + "type": "MemoryPressure" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T19:55:58Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status", + "type": "Ready" + } + ] + }, + "kind": "Node", + "spec": { + "externalID": "172.17.0.6" + }, + "apiVersion": "v1", + "metadata": { + "uid": "803b2e59-4525-11e6-bd6d-0800275521ee", + "labels": { + "kubernetes.io/hostname": "172.17.0.6", + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64" + }, + "resourceVersion": "96998", + "creationTimestamp": "2016-07-08T16:03:24Z", + "selfLink": "/api/v1/nodes/172.17.0.6", + "name": "172.17.0.6" + } + }, + "8b371c02-4525-11e6-bd6d-0800275521ee": { + "status": { + "capacity": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": "1", + "memory": "3840Mi" + }, + "addresses": [ + { + "type": "LegacyHostIP", + "address": "172.17.0.11" + }, + { + "type": "InternalIP", + "address": "172.17.0.11" + } + ], + "nodeInfo": { + "kernelVersion": "", + "kubeletVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "containerRuntimeVersion": "docker://1.8.1", + "machineID": "", + "kubeProxyVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "bootID": "", + "osImage": "", + "architecture": "amd64", + "systemUUID": "", + "operatingSystem": "linux" + }, + "allocatable": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": 1000, + "memory": 4026531840 + }, + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "conditions": [ + { + "status": "False", + "lastTransitionTime": "2016-07-09T18:26:46Z", + "lastHeartbeatTime": "2016-07-09T20:38:25Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available", + "type": "OutOfDisk" + }, + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:03:43Z", + "lastHeartbeatTime": "2016-07-09T20:38:25Z", + "reason": "KubeletHasSufficientMemory", + "message": "kubelet has sufficient memory available", + "type": "MemoryPressure" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T18:26:46Z", + "lastHeartbeatTime": "2016-07-09T20:38:25Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status", + "type": "Ready" + } + ] + }, + "kind": "Node", + "spec": { + "externalID": "172.17.0.11" + }, + "apiVersion": "v1", + "metadata": { + "uid": "8b371c02-4525-11e6-bd6d-0800275521ee", + "labels": { + "kubernetes.io/hostname": "172.17.0.11", + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64" + }, + "resourceVersion": "97000", + "creationTimestamp": "2016-07-08T16:03:43Z", + "selfLink": "/api/v1/nodes/172.17.0.11", + "name": "172.17.0.11" + } + }, + "32f000e0-4525-11e6-bd6d-0800275521ee": { + "status": { + "capacity": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "110", + "cpu": "4", + "memory": "8175184Ki" + }, + "addresses": [ + { + "type": "LegacyHostIP", + "address": "127.0.0.1" + }, + { + "type": "InternalIP", + "address": "127.0.0.1" + } + ], + "nodeInfo": { + "kernelVersion": "4.4.0-28-generic", + "kubeletVersion": "v1.3.0-dirty", + "containerRuntimeVersion": "docker://1.11.1", + "machineID": "ef75482e68faffa3dd7fad1c573e455d", + "kubeProxyVersion": "v1.3.0-dirty", + "bootID": "8db61eba-b5c8-49a2-915e-348ad6f536db", + "osImage": "Ubuntu 16.04 LTS", + "architecture": "amd64", + "systemUUID": "EF392D34-4374-43FA-B4BC-F795BCF0FD21", + "operatingSystem": "linux" + }, + "allocatable": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "110", + "cpu": 4000, + "memory": 8371388416 + }, + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "images": [ + { + "sizeBytes": 2324692000, + "names": [ + "docker-dev:master" + ] + }, + { + "sizeBytes": 225400965, + "names": [ + "kubemark:latest" + ] + }, + { + "sizeBytes": 225396837, + "names": [ + ":", + "@" + ] + }, + { + "sizeBytes": 182790156, + "names": [ + ":", + "@" + ] + }, + { + "sizeBytes": 182786784, + "names": [ + "nginx:latest" + ] + }, + { + "sizeBytes": 182773270, + "names": [ + ":", + "@" + ] + }, + { + "sizeBytes": 125093399, + "names": [ + "debian:jessie" + ] + }, + { + "sizeBytes": 746888, + "names": [ + "gcr.io/google_containers/pause-amd64:3.0" + ] + }, + { + "sizeBytes": 967, + "names": [ + "hello-world:latest" + ] + } + ], + "conditions": [ + { + "status": "False", + "lastTransitionTime": "2016-07-09T18:26:46Z", + "lastHeartbeatTime": "2016-07-09T20:38:20Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available", + "type": "OutOfDisk" + }, + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:01:15Z", + "lastHeartbeatTime": "2016-07-09T20:38:20Z", + "reason": "KubeletHasSufficientMemory", + "message": "kubelet has sufficient memory available", + "type": "MemoryPressure" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T18:26:46Z", + "lastHeartbeatTime": "2016-07-09T20:38:20Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status", + "type": "Ready" + } + ] + }, + "kind": "Node", + "spec": { + "externalID": "127.0.0.1" + }, + "apiVersion": "v1", + "metadata": { + "name": "127.0.0.1", + "labels": { + "kubernetes.io/hostname": "127.0.0.1", + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64" + }, + "resourceVersion": "96995", + "creationTimestamp": "2016-07-08T16:01:15Z", + "annotations": { + "volumes.kubernetes.io/controller-managed-attach-detach": "true" + }, + "selfLink": "/api/v1/nodes/127.0.0.1", + "uid": "32f000e0-4525-11e6-bd6d-0800275521ee" + } + }, + "8775893b-4525-11e6-bd6d-0800275521ee": { + "status": { + "capacity": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": "1", + "memory": "3840Mi" + }, + "addresses": [ + { + "type": "LegacyHostIP", + "address": "172.17.0.10" + }, + { + "type": "InternalIP", + "address": "172.17.0.10" + } + ], + "nodeInfo": { + "kernelVersion": "", + "kubeletVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "containerRuntimeVersion": "docker://1.8.1", + "machineID": "", + "kubeProxyVersion": "v1.3.0-alpha.4.132+1cce15659750d9-dirty", + "bootID": "", + "osImage": "", + "architecture": "amd64", + "systemUUID": "", + "operatingSystem": "linux" + }, + "allocatable": { + "alpha.kubernetes.io/nvidia-gpu": "0", + "pods": "200", + "cpu": 1000, + "memory": 4026531840 + }, + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "conditions": [ + { + "status": "False", + "lastTransitionTime": "2016-07-09T19:55:58Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletHasSufficientDisk", + "message": "kubelet has sufficient disk space available", + "type": "OutOfDisk" + }, + { + "status": "False", + "lastTransitionTime": "2016-07-08T16:03:37Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletHasSufficientMemory", + "message": "kubelet has sufficient memory available", + "type": "MemoryPressure" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T19:55:58Z", + "lastHeartbeatTime": "2016-07-09T20:38:22Z", + "reason": "KubeletReady", + "message": "kubelet is posting ready status", + "type": "Ready" + } + ] + }, + "kind": "Node", + "spec": { + "externalID": "172.17.0.10" + }, + "apiVersion": "v1", + "metadata": { + "uid": "8775893b-4525-11e6-bd6d-0800275521ee", + "labels": { + "kubernetes.io/hostname": "172.17.0.10", + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64" + }, + "resourceVersion": "96997", + "creationTimestamp": "2016-07-08T16:03:37Z", + "selfLink": "/api/v1/nodes/172.17.0.10", + "name": "172.17.0.10" + } + } + }, + "replicationcontrollers": { + "7d7a87c8-4525-11e6-bd6d-0800275521ee": { + "status": { + "observedGeneration": 1, + "fullyLabeledReplicas": 10, + "replicas": 10 + }, + "spec": { + "selector": { + "name": "hollow-node" + }, + "template": { + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "volumes": [ + { + "secret": { + "secretName": "kubeconfig" + }, + "name": "kubeconfig-volume" + } + ], + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-kubelet", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=kubelet", + "--max-pods=200", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "ports": [ + { + "protocol": "TCP", + "containerPort": 4194 + }, + { + "protocol": "TCP", + "containerPort": 10250 + }, + { + "protocol": "TCP", + "containerPort": 10255 + } + ], + "resources": { + "requests": { + "cpu": "50m", + "memory": "100M" + } + } + }, + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-proxy", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=proxy", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "resources": { + "requests": { + "cpu": "20m", + "memory": "100M" + } + } + } + ] + }, + "metadata": { + "labels": { + "name": "hollow-node" + }, + "creationTimestamp": null, + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental" + } + } + }, + "replicas": 10 + }, + "metadata": { + "name": "hollow-node", + "generation": 1, + "labels": { + "name": "hollow-node" + }, + "namespace": "kubemark", + "resourceVersion": "1570", + "creationTimestamp": "2016-07-08T16:03:20Z", + "selfLink": "/api/v1/namespaces/kubemark/replicationcontrollers/hollow-node", + "uid": "7d7a87c8-4525-11e6-bd6d-0800275521ee" + } + }, + "e4b655d5-4614-11e6-bd6d-0800275521ee": { + "status": { + "observedGeneration": 1, + "fullyLabeledReplicas": 30, + "replicas": 30 + }, + "kind": "ReplicationController", + "spec": { + "selector": { + "app": "nginx30" + }, + "template": { + "spec": { + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "metadata": { + "labels": { + "app": "nginx30" + }, + "creationTimestamp": null, + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental" + }, + "name": "nginx30" + } + }, + "replicas": 30 + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30", + "generation": 1, + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96796", + "creationTimestamp": "2016-07-09T20:37:03Z", + "selfLink": "/api/v1/namespaces/kubemark/replicationcontrollers/nginx30", + "uid": "e4b655d5-4614-11e6-bd6d-0800275521ee" + } + } + }, + "pods": { + "e4b7c678-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:20Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-owxdt_kubemark_e4b7c678-4614-11e6-bd6d-0800275521ee_a63732e5" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:19Z", + "hostIP": "172.17.0.11", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:19Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:20Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:19Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.11", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-owxdt", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96884", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-owxdt", + "uid": "e4b7c678-4614-11e6-bd6d-0800275521ee" + } + }, + "7d7d6747-4525-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "hollow-kubelet", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:24Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://0e36e464ce22bc451edc56c032fce9010fefece9c84da2176cd1b4a2e5708762" + }, + { + "restartCount": 0, + "name": "hollow-proxy", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:25Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://980dd1eeb9bfbf08d1297e3cc4cfc3372f10a837763b193b4dd8a39b26c10c05" + } + ], + "podIP": "172.17.0.6", + "startTime": "2016-07-08T16:03:20Z", + "hostIP": "127.0.0.1", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:26Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "127.0.0.1", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "volumes": [ + { + "secret": { + "secretName": "kubeconfig" + }, + "name": "kubeconfig-volume" + } + ], + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-kubelet", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=kubelet", + "--max-pods=200", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "ports": [ + { + "protocol": "TCP", + "containerPort": 4194 + }, + { + "protocol": "TCP", + "containerPort": 10250 + }, + { + "protocol": "TCP", + "containerPort": 10255 + } + ], + "resources": { + "requests": { + "cpu": 50000, + "memory": 100000000 + } + } + }, + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-proxy", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=proxy", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "resources": { + "requests": { + "cpu": 20000, + "memory": 100000000 + } + } + } + ] + }, + "metadata": { + "name": "hollow-node-99ky5", + "labels": { + "name": "hollow-node" + }, + "namespace": "kubemark", + "resourceVersion": "89410", + "generateName": "hollow-node-", + "creationTimestamp": "2016-07-08T16:03:20Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"hollow-node\",\"uid\":\"7d7a87c8-4525-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"1550\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/hollow-node-99ky5", + "uid": "7d7d6747-4525-11e6-bd6d-0800275521ee" + } + }, + "e4b7cd1a-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:16Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-g9vdb_kubemark_e4b7cd1a-4614-11e6-bd6d-0800275521ee_b0bc6f88" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:15Z", + "hostIP": "172.17.0.7", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:15Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:17Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:15Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.7", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-g9vdb", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96876", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-g9vdb", + "uid": "e4b7cd1a-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b80135-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:58Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-qnmfs_kubemark_e4b80135-4614-11e6-bd6d-0800275521ee_39b808e1" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:57Z", + "hostIP": "172.17.0.9", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:57Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:59Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:57Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.9", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-qnmfs", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96963", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-qnmfs", + "uid": "e4b80135-4614-11e6-bd6d-0800275521ee" + } + }, + "7d7db2f3-4525-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "hollow-kubelet", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:31Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://ff44aa21b1e9875bfc8eb4797954b010639a77101585c65d5225bae0b40b6456" + }, + { + "restartCount": 0, + "name": "hollow-proxy", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:32Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://d5893fa4e75e8622ebe7f779f45c7206499eae9694c7d446a7c24bb59c57d351" + } + ], + "podIP": "172.17.0.10", + "startTime": "2016-07-08T16:03:20Z", + "hostIP": "127.0.0.1", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:32Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "127.0.0.1", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "volumes": [ + { + "secret": { + "secretName": "kubeconfig" + }, + "name": "kubeconfig-volume" + } + ], + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-kubelet", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=kubelet", + "--max-pods=200", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "ports": [ + { + "protocol": "TCP", + "containerPort": 4194 + }, + { + "protocol": "TCP", + "containerPort": 10250 + }, + { + "protocol": "TCP", + "containerPort": 10255 + } + ], + "resources": { + "requests": { + "cpu": 50000, + "memory": 100000000 + } + } + }, + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-proxy", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=proxy", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "resources": { + "requests": { + "cpu": 20000, + "memory": 100000000 + } + } + } + ] + }, + "metadata": { + "name": "hollow-node-dq9yz", + "labels": { + "name": "hollow-node" + }, + "namespace": "kubemark", + "resourceVersion": "89423", + "generateName": "hollow-node-", + "creationTimestamp": "2016-07-08T16:03:20Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"hollow-node\",\"uid\":\"7d7a87c8-4525-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"1550\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/hollow-node-dq9yz", + "uid": "7d7db2f3-4525-11e6-bd6d-0800275521ee" + } + }, + "e4b7d8fa-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:42Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-fbws2_kubemark_e4b7d8fa-4614-11e6-bd6d-0800275521ee_246df0a1" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:41Z", + "hostIP": "172.17.0.3", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:41Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:42Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:41Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.3", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-fbws2", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96934", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-fbws2", + "uid": "e4b7d8fa-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7a023-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:06Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-4lucv_kubemark_e4b7a023-4614-11e6-bd6d-0800275521ee_181bfb31" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:06Z", + "hostIP": "172.17.0.3", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:06Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:06Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:06Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.3", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-4lucv", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96841", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-4lucv", + "uid": "e4b7a023-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7663d-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:04Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-9mty4_kubemark_e4b7663d-4614-11e6-bd6d-0800275521ee_f4713bbd" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:03Z", + "hostIP": "172.17.0.9", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:03Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:04Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:03Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.9", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-9mty4", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96815", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-9mty4", + "uid": "e4b7663d-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7bef9-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:22Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-0jw5b_kubemark_e4b7bef9-4614-11e6-bd6d-0800275521ee_74e724db" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:21Z", + "hostIP": "172.17.0.3", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:21Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:22Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:21Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.3", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-0jw5b", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96891", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-0jw5b", + "uid": "e4b7bef9-4614-11e6-bd6d-0800275521ee" + } + }, + "7d7de016-4525-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "hollow-kubelet", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:30Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://e27b9f9d2d47bb7dfbf52b219479ac2678458ce7c6d9924841a79a08aef22c6d" + }, + { + "restartCount": 0, + "name": "hollow-proxy", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:31Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://ac378db11d19a672941c7049e52ba81d4b1b980e0c0afb0a5c55eb88aa59351e" + } + ], + "podIP": "172.17.0.9", + "startTime": "2016-07-08T16:03:20Z", + "hostIP": "127.0.0.1", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:31Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "127.0.0.1", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "volumes": [ + { + "secret": { + "secretName": "kubeconfig" + }, + "name": "kubeconfig-volume" + } + ], + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-kubelet", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=kubelet", + "--max-pods=200", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "ports": [ + { + "protocol": "TCP", + "containerPort": 4194 + }, + { + "protocol": "TCP", + "containerPort": 10250 + }, + { + "protocol": "TCP", + "containerPort": 10255 + } + ], + "resources": { + "requests": { + "cpu": 50000, + "memory": 100000000 + } + } + }, + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-proxy", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=proxy", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "resources": { + "requests": { + "cpu": 20000, + "memory": 100000000 + } + } + } + ] + }, + "metadata": { + "name": "hollow-node-jd2ep", + "labels": { + "name": "hollow-node" + }, + "namespace": "kubemark", + "resourceVersion": "89414", + "generateName": "hollow-node-", + "creationTimestamp": "2016-07-08T16:03:20Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"hollow-node\",\"uid\":\"7d7a87c8-4525-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"1550\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/hollow-node-jd2ep", + "uid": "7d7de016-4525-11e6-bd6d-0800275521ee" + } + }, + "e4b7e64e-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:32Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-xjqzw_kubemark_e4b7e64e-4614-11e6-bd6d-0800275521ee_c6cb3837" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:32Z", + "hostIP": "172.17.0.9", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:32Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:33Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:32Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.9", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-xjqzw", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96914", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-xjqzw", + "uid": "e4b7e64e-4614-11e6-bd6d-0800275521ee" + } + }, + "7d7c7610-4525-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "hollow-kubelet", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:22Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://7b3d4f21634f2930efded516a28a5c69aa544a6df7a46c379dda4e26e7a0ba25" + }, + { + "restartCount": 0, + "name": "hollow-proxy", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:23Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://46f00eda62ac092be5bc9e696da069106ba9eb14270d83548647010eda50d672" + } + ], + "podIP": "172.17.0.3", + "startTime": "2016-07-08T16:03:20Z", + "hostIP": "127.0.0.1", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:24Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "127.0.0.1", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "volumes": [ + { + "secret": { + "secretName": "kubeconfig" + }, + "name": "kubeconfig-volume" + } + ], + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-kubelet", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=kubelet", + "--max-pods=200", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "ports": [ + { + "protocol": "TCP", + "containerPort": 4194 + }, + { + "protocol": "TCP", + "containerPort": 10250 + }, + { + "protocol": "TCP", + "containerPort": 10255 + } + ], + "resources": { + "requests": { + "cpu": 50000, + "memory": 100000000 + } + } + }, + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-proxy", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=proxy", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "resources": { + "requests": { + "cpu": 20000, + "memory": 100000000 + } + } + } + ] + }, + "metadata": { + "name": "hollow-node-jwron", + "labels": { + "name": "hollow-node" + }, + "namespace": "kubemark", + "resourceVersion": "89417", + "generateName": "hollow-node-", + "creationTimestamp": "2016-07-08T16:03:20Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"hollow-node\",\"uid\":\"7d7a87c8-4525-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"1550\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/hollow-node-jwron", + "uid": "7d7c7610-4525-11e6-bd6d-0800275521ee" + } + }, + "7d81e9c2-4525-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "hollow-kubelet", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:25Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://3380558ee577276833fab5422c758c221f397d0cc8ed021d01ee11f45eda611e" + }, + { + "restartCount": 0, + "name": "hollow-proxy", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:26Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://d37f448a173fd24a818b314ad8a11b1a936c8b2a4b033c483d0de8271b425b4e" + } + ], + "podIP": "172.17.0.7", + "startTime": "2016-07-08T16:03:20Z", + "hostIP": "127.0.0.1", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:26Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "127.0.0.1", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "volumes": [ + { + "secret": { + "secretName": "kubeconfig" + }, + "name": "kubeconfig-volume" + } + ], + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-kubelet", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=kubelet", + "--max-pods=200", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "ports": [ + { + "protocol": "TCP", + "containerPort": 4194 + }, + { + "protocol": "TCP", + "containerPort": 10250 + }, + { + "protocol": "TCP", + "containerPort": 10255 + } + ], + "resources": { + "requests": { + "cpu": 50000, + "memory": 100000000 + } + } + }, + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-proxy", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=proxy", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "resources": { + "requests": { + "cpu": 20000, + "memory": 100000000 + } + } + } + ] + }, + "metadata": { + "name": "hollow-node-dyty5", + "labels": { + "name": "hollow-node" + }, + "namespace": "kubemark", + "resourceVersion": "89407", + "generateName": "hollow-node-", + "creationTimestamp": "2016-07-08T16:03:20Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"hollow-node\",\"uid\":\"7d7a87c8-4525-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"1550\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/hollow-node-dyty5", + "uid": "7d81e9c2-4525-11e6-bd6d-0800275521ee" + } + }, + "e4b79c04-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:07Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-efrhb_kubemark_e4b79c04-4614-11e6-bd6d-0800275521ee_82475418" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:06Z", + "hostIP": "172.17.0.7", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:06Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:08Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:06Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.7", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-efrhb", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96846", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-efrhb", + "uid": "e4b79c04-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7cb9d-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:18Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-mbly5_kubemark_e4b7cb9d-4614-11e6-bd6d-0800275521ee_983962ee" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:17Z", + "hostIP": "172.17.0.10", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:17Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:18Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:17Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.10", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-mbly5", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96880", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-mbly5", + "uid": "e4b7cb9d-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b78c34-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:09Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-hq2fk_kubemark_e4b78c34-4614-11e6-bd6d-0800275521ee_882eb29e" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:08Z", + "hostIP": "172.17.0.5", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:08Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:09Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:08Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.5", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-hq2fk", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96851", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-hq2fk", + "uid": "e4b78c34-4614-11e6-bd6d-0800275521ee" + } + }, + "7d7e079a-4525-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "hollow-kubelet", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:32Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://e322ed8a6a7b2c300ac6fd28e062be3a2ceffd5c5bf7ce04e6aef91d1b49217c" + }, + { + "restartCount": 0, + "name": "hollow-proxy", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:33Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://c6369e4048d62c17a9f048b320507069b6bde2728f876c38e9fa756bce3dff5e" + } + ], + "podIP": "172.17.0.11", + "startTime": "2016-07-08T16:03:20Z", + "hostIP": "127.0.0.1", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:34Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "127.0.0.1", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "volumes": [ + { + "secret": { + "secretName": "kubeconfig" + }, + "name": "kubeconfig-volume" + } + ], + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-kubelet", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=kubelet", + "--max-pods=200", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "ports": [ + { + "protocol": "TCP", + "containerPort": 4194 + }, + { + "protocol": "TCP", + "containerPort": 10250 + }, + { + "protocol": "TCP", + "containerPort": 10255 + } + ], + "resources": { + "requests": { + "cpu": 50000, + "memory": 100000000 + } + } + }, + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-proxy", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=proxy", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "resources": { + "requests": { + "cpu": 20000, + "memory": 100000000 + } + } + } + ] + }, + "metadata": { + "name": "hollow-node-j38ls", + "labels": { + "name": "hollow-node" + }, + "namespace": "kubemark", + "resourceVersion": "89409", + "generateName": "hollow-node-", + "creationTimestamp": "2016-07-08T16:03:20Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"hollow-node\",\"uid\":\"7d7a87c8-4525-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"1550\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/hollow-node-j38ls", + "uid": "7d7e079a-4525-11e6-bd6d-0800275521ee" + } + }, + "e4b7f58e-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:45Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-89882_kubemark_e4b7f58e-4614-11e6-bd6d-0800275521ee_37fa7f4f" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:45Z", + "hostIP": "172.17.0.10", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:45Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:46Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:45Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.10", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-89882", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96939", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-89882", + "uid": "e4b7f58e-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7fd46-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:38:02Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-d30n9_kubemark_e4b7fd46-4614-11e6-bd6d-0800275521ee_9a003524" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:38:02Z", + "hostIP": "172.17.0.4", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:38:02Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:38:03Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:38:02Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.4", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-d30n9", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96971", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-d30n9", + "uid": "e4b7fd46-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7bde9-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:24Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-h90c2_kubemark_e4b7bde9-4614-11e6-bd6d-0800275521ee_3117ff02" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:24Z", + "hostIP": "172.17.0.5", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:24Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:24Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:24Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.5", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-h90c2", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96894", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-h90c2", + "uid": "e4b7bde9-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b786dc-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:10Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-8otcx_kubemark_e4b786dc-4614-11e6-bd6d-0800275521ee_16f28cbf" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:10Z", + "hostIP": "172.17.0.4", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:10Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:11Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:10Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.4", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-8otcx", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96855", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-8otcx", + "uid": "e4b786dc-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b80e37-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:38:12Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-iuo9m_kubemark_e4b80e37-4614-11e6-bd6d-0800275521ee_e05f6f01" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:38:12Z", + "hostIP": "172.17.0.8", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:38:12Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:38:14Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:38:12Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.8", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-iuo9m", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96988", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-iuo9m", + "uid": "e4b80e37-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7acdc-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:05Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-nm3wu_kubemark_e4b7acdc-4614-11e6-bd6d-0800275521ee_b63ce19a" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:04Z", + "hostIP": "172.17.0.10", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:04Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:06Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:04Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.10", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-nm3wu", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96837", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-nm3wu", + "uid": "e4b7acdc-4614-11e6-bd6d-0800275521ee" + } + }, + "7d7dc45c-4525-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "hollow-kubelet", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:22Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://1a947c5b75c63679ff5f6e7c4c026a1f11972ae7d8bd64b8723e9b4a7d0e4a50" + }, + { + "restartCount": 0, + "name": "hollow-proxy", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:23Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://ed205b71bf7b0a041cb286b8e430c81ae36bd8ac2e3dc95eee26abce06ea8e75" + } + ], + "podIP": "172.17.0.4", + "startTime": "2016-07-08T16:03:20Z", + "hostIP": "127.0.0.1", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:24Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "127.0.0.1", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "volumes": [ + { + "secret": { + "secretName": "kubeconfig" + }, + "name": "kubeconfig-volume" + } + ], + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-kubelet", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=kubelet", + "--max-pods=200", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "ports": [ + { + "protocol": "TCP", + "containerPort": 4194 + }, + { + "protocol": "TCP", + "containerPort": 10250 + }, + { + "protocol": "TCP", + "containerPort": 10255 + } + ], + "resources": { + "requests": { + "cpu": 50000, + "memory": 100000000 + } + } + }, + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-proxy", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=proxy", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "resources": { + "requests": { + "cpu": 20000, + "memory": 100000000 + } + } + } + ] + }, + "metadata": { + "name": "hollow-node-afakc", + "labels": { + "name": "hollow-node" + }, + "namespace": "kubemark", + "resourceVersion": "89426", + "generateName": "hollow-node-", + "creationTimestamp": "2016-07-08T16:03:20Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"hollow-node\",\"uid\":\"7d7a87c8-4525-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"1550\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/hollow-node-afakc", + "uid": "7d7dc45c-4525-11e6-bd6d-0800275521ee" + } + }, + "e4b7f3d3-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:49Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-y3mcf_kubemark_e4b7f3d3-4614-11e6-bd6d-0800275521ee_97c34433" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:49Z", + "hostIP": "172.17.0.7", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:49Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:50Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:49Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.7", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-y3mcf", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96947", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-y3mcf", + "uid": "e4b7f3d3-4614-11e6-bd6d-0800275521ee" + } + }, + "7d7d26ae-4525-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "hollow-kubelet", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:21Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://b190df8ebc1c43204ba4e0a6d16d690916f6ca31e75823111de54906db95c816" + }, + { + "restartCount": 0, + "name": "hollow-proxy", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:22Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://35d8a87752707e335a7f081d20db044067e3750f1f27afefef90ee8107d7159c" + } + ], + "podIP": "172.17.0.2", + "startTime": "2016-07-08T16:03:20Z", + "hostIP": "127.0.0.1", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:23Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "127.0.0.1", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "volumes": [ + { + "secret": { + "secretName": "kubeconfig" + }, + "name": "kubeconfig-volume" + } + ], + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-kubelet", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=kubelet", + "--max-pods=200", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "ports": [ + { + "protocol": "TCP", + "containerPort": 4194 + }, + { + "protocol": "TCP", + "containerPort": 10250 + }, + { + "protocol": "TCP", + "containerPort": 10255 + } + ], + "resources": { + "requests": { + "cpu": 50000, + "memory": 100000000 + } + } + }, + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-proxy", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=proxy", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "resources": { + "requests": { + "cpu": 20000, + "memory": 100000000 + } + } + } + ] + }, + "metadata": { + "name": "hollow-node-ud6c0", + "labels": { + "name": "hollow-node" + }, + "namespace": "kubemark", + "resourceVersion": "89420", + "generateName": "hollow-node-", + "creationTimestamp": "2016-07-08T16:03:20Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"hollow-node\",\"uid\":\"7d7a87c8-4525-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"1550\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/hollow-node-ud6c0", + "uid": "7d7d26ae-4525-11e6-bd6d-0800275521ee" + } + }, + "e4b7a467-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:05Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-udrhb_kubemark_e4b7a467-4614-11e6-bd6d-0800275521ee_d799fe86" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:05Z", + "hostIP": "172.17.0.6", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:05Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:05Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:05Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.6", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-udrhb", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96833", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-udrhb", + "uid": "e4b7a467-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7fc44-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:38:07Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-dy04r_kubemark_e4b7fc44-4614-11e6-bd6d-0800275521ee_74e724db" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:38:07Z", + "hostIP": "172.17.0.6", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:38:07Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:38:08Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:38:07Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.6", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-dy04r", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96979", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-dy04r", + "uid": "e4b7fc44-4614-11e6-bd6d-0800275521ee" + } + }, + "7d7d38f7-4525-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "hollow-kubelet", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:27Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://d0cbfc07df19dc5d830ad81d07bbdb4d4b63571c50f11f5654fae6af9de90d10" + }, + { + "restartCount": 0, + "name": "hollow-proxy", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:28Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://c426b24710496c266ec27bc4a71a98230e79dad66a06be6e2622dd83fe16094b" + } + ], + "podIP": "172.17.0.8", + "startTime": "2016-07-08T16:03:20Z", + "hostIP": "127.0.0.1", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:29Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "127.0.0.1", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "volumes": [ + { + "secret": { + "secretName": "kubeconfig" + }, + "name": "kubeconfig-volume" + } + ], + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-kubelet", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=kubelet", + "--max-pods=200", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "ports": [ + { + "protocol": "TCP", + "containerPort": 4194 + }, + { + "protocol": "TCP", + "containerPort": 10250 + }, + { + "protocol": "TCP", + "containerPort": 10255 + } + ], + "resources": { + "requests": { + "cpu": 50000, + "memory": 100000000 + } + } + }, + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-proxy", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=proxy", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "resources": { + "requests": { + "cpu": 20000, + "memory": 100000000 + } + } + } + ] + }, + "metadata": { + "name": "hollow-node-462dy", + "labels": { + "name": "hollow-node" + }, + "namespace": "kubemark", + "resourceVersion": "89408", + "generateName": "hollow-node-", + "creationTimestamp": "2016-07-08T16:03:20Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"hollow-node\",\"uid\":\"7d7a87c8-4525-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"1550\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/hollow-node-462dy", + "uid": "7d7d38f7-4525-11e6-bd6d-0800275521ee" + } + }, + "e4b77e1e-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:11Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-kqytj_kubemark_e4b77e1e-4614-11e6-bd6d-0800275521ee_488d9800" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:11Z", + "hostIP": "172.17.0.2", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:11Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:12Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:11Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.2", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-kqytj", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96863", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-kqytj", + "uid": "e4b77e1e-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b76ced-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:13Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-lxk17_kubemark_e4b76ced-4614-11e6-bd6d-0800275521ee_ba218475" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:12Z", + "hostIP": "172.17.0.8", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:12Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:13Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:12Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.8", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-lxk17", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96865", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-lxk17", + "uid": "e4b76ced-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7af1f-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:04Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-omuyl_kubemark_e4b7af1f-4614-11e6-bd6d-0800275521ee_74e724db" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:04Z", + "hostIP": "172.17.0.8", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:04Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:05Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:04Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.8", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-omuyl", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96832", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-omuyl", + "uid": "e4b7af1f-4614-11e6-bd6d-0800275521ee" + } + }, + "7d7d55af-4525-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "hollow-kubelet", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:23Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://ac8c9d7668b2f274f798e95d77a9e52ed32d75b9070acb47b6b3fd0a270f093d" + }, + { + "restartCount": 0, + "name": "hollow-proxy", + "image": "kubemark:latest", + "imageID": "docker://sha256:9007bd447e31f12dd1d7fee7f9a1a6698405194711855d98ad6a0322363d6fde", + "state": { + "running": { + "startedAt": "2016-07-08T16:03:24Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker://3ab45c772bafa0575871c04f26ccf05733e7debd7fbddb63657bdf4e58bd5510" + } + ], + "podIP": "172.17.0.5", + "startTime": "2016-07-08T16:03:20Z", + "hostIP": "127.0.0.1", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:25Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-08T16:03:20Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "127.0.0.1", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "volumes": [ + { + "secret": { + "secretName": "kubeconfig" + }, + "name": "kubeconfig-volume" + } + ], + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-kubelet", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=kubelet", + "--max-pods=200", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "ports": [ + { + "protocol": "TCP", + "containerPort": 4194 + }, + { + "protocol": "TCP", + "containerPort": 10250 + }, + { + "protocol": "TCP", + "containerPort": 10255 + } + ], + "resources": { + "requests": { + "cpu": 50000, + "memory": 100000000 + } + } + }, + { + "terminationMessagePath": "/dev/termination-log", + "name": "hollow-proxy", + "image": "kubemark:latest", + "args": [ + "--v=3", + "--morph=proxy", + "$(CONTENT_TYPE)" + ], + "volumeMounts": [ + { + "mountPath": "/kubeconfig", + "name": "kubeconfig-volume" + } + ], + "command": [ + "./kubemark.sh" + ], + "env": [ + { + "valueFrom": { + "configMapKeyRef": { + "name": "node-configmap", + "key": "content.type" + } + }, + "name": "CONTENT_TYPE" + } + ], + "imagePullPolicy": "Never", + "resources": { + "requests": { + "cpu": 20000, + "memory": 100000000 + } + } + } + ] + }, + "metadata": { + "name": "hollow-node-sble6", + "labels": { + "name": "hollow-node" + }, + "namespace": "kubemark", + "resourceVersion": "89411", + "generateName": "hollow-node-", + "creationTimestamp": "2016-07-08T16:03:20Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"hollow-node\",\"uid\":\"7d7a87c8-4525-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"1550\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/hollow-node-sble6", + "uid": "7d7d55af-4525-11e6-bd6d-0800275521ee" + } + }, + "e4b7eec1-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:53Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-ug5x6_kubemark_e4b7eec1-4614-11e6-bd6d-0800275521ee_f2ce59f2" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:53Z", + "hostIP": "172.17.0.11", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:53Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:55Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:53Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.11", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-ug5x6", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96955", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-ug5x6", + "uid": "e4b7eec1-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b793f5-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:08Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-x6vkd_kubemark_e4b793f5-4614-11e6-bd6d-0800275521ee_c856f226" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:07Z", + "hostIP": "172.17.0.11", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:07Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:08Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:07Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.11", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-x6vkd", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96848", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-x6vkd", + "uid": "e4b793f5-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7e81d-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:29Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-d9v2y_kubemark_e4b7e81d-4614-11e6-bd6d-0800275521ee_0018b1d5" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:29Z", + "hostIP": "172.17.0.4", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:29Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:30Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:29Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.4", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-d9v2y", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96907", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-d9v2y", + "uid": "e4b7e81d-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7d41b-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:14Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-m0i9u_kubemark_e4b7d41b-4614-11e6-bd6d-0800275521ee_2b76e2d7" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:14Z", + "hostIP": "172.17.0.2", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:14Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:15Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:14Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.2", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-m0i9u", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96871", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-m0i9u", + "uid": "e4b7d41b-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7b5e4-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:27Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-j2xk8_kubemark_e4b7b5e4-4614-11e6-bd6d-0800275521ee_37fa7f4f" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:26Z", + "hostIP": "172.17.0.6", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:26Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:27Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:26Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.6", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-j2xk8", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96902", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-j2xk8", + "uid": "e4b7b5e4-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7e192-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:35Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-txekj_kubemark_e4b7e192-4614-11e6-bd6d-0800275521ee_fe318b9d" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:35Z", + "hostIP": "172.17.0.5", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:35Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:35Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:35Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.5", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-txekj", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96918", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-txekj", + "uid": "e4b7e192-4614-11e6-bd6d-0800275521ee" + } + }, + "e4b7da93-4614-11e6-bd6d-0800275521ee": { + "status": { + "containerStatuses": [ + { + "restartCount": 0, + "name": "nginx", + "image": "nginx", + "imageID": "docker://", + "state": { + "running": { + "startedAt": "2016-07-09T20:37:38Z" + } + }, + "ready": true, + "lastState": {}, + "containerID": "docker:///k8s_nginx.156efd59_nginx30-a8s7n_kubemark_e4b7da93-4614-11e6-bd6d-0800275521ee_39a4c410" + } + ], + "podIP": "2.3.4.5", + "startTime": "2016-07-09T20:37:38Z", + "hostIP": "172.17.0.2", + "phase": "Running", + "conditions": [ + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:38Z", + "lastProbeTime": null, + "type": "Initialized" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:40Z", + "lastProbeTime": null, + "type": "Ready" + }, + { + "status": "True", + "lastTransitionTime": "2016-07-09T20:37:38Z", + "lastProbeTime": null, + "type": "PodScheduled" + } + ] + }, + "kind": "Pod", + "spec": { + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "nodeName": "172.17.0.2", + "terminationGracePeriodSeconds": 30, + "restartPolicy": "Always", + "containers": [ + { + "terminationMessagePath": "/dev/termination-log", + "name": "nginx", + "image": "nginx", + "imagePullPolicy": "Always", + "ports": [ + { + "protocol": "TCP", + "containerPort": 80 + } + ], + "resources": {} + } + ] + }, + "apiVersion": "v1", + "metadata": { + "name": "nginx30-a8s7n", + "labels": { + "app": "nginx30" + }, + "namespace": "kubemark", + "resourceVersion": "96926", + "generateName": "nginx30-", + "creationTimestamp": "2016-07-09T20:37:03Z", + "annotations": { + "scheduler.alpha.kubernetes.io/name": "experimental", + "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"kubemark\",\"name\":\"nginx30\",\"uid\":\"e4b655d5-4614-11e6-bd6d-0800275521ee\",\"apiVersion\":\"v1\",\"resourceVersion\":\"96758\"}}\n" + }, + "selfLink": "/api/v1/namespaces/kubemark/pods/nginx30-a8s7n", + "uid": "e4b7da93-4614-11e6-bd6d-0800275521ee" + } + } + } +} diff --git a/third_party/opa/v1/test/wasm/assets/001_eq.yaml b/third_party/opa/v1/test/wasm/assets/001_eq.yaml new file mode 100644 index 000000000000..2b337e1a4cc5 --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/001_eq.yaml @@ -0,0 +1,89 @@ +cases: + - note: eq/number + query: "input.x = 1" + input: {"x": 1} + want_defined: true + - note: eq/number (negative) + query: "input.x = 1" + input: {"x": 2} + want_defined: false + - note: eq/float + query: "input.x = 2.5" + input: {"x": 2.5} + want_defined: true + - note: eq/float (negative) + query: "input.x = 2.5" + input: {"x": 2.4} + want_defined: false + - note: eq/string + query: input.x = "hello" + input: {"x": "hello"} + want_defined: true + - note: eq/string (negative) + query: input.x = "hello" + input: {"x": "world"} + want_defined: false + - note: eq/true + query: input.x = true + input: {"x": true} + want_defined: true + - note: eq/true (negative) + query: input.x = false + input: {"x": true} + want_defined: false + - note: eq/false + query: input.x = false + input: {"x": false} + want_defined: true + - note: eq/false (negative) + query: input.x = true + input: {"x": false} + want_defined: false + - note: eq/null + query: input.x = null + input: {"x": null} + want_defined: true + - note: eq/null (negative) + query: input.x = null + input: {"x": false} + want_defined: false + - note: eq/array-empty + query: input.x = [] + input: {"x": []} + want_defined: true + - note: eq/array-empty (negative) + query: input.x = [] + input: {"x": [1]} + want_defined: false + - note: eq/array-non-empty + query: input.x = [1,2,3] + input: {"x": [1,2,3]} + want_defined: true + - note: eq/array-non-empty (negative) + query: input.x = [1,2,3] + input: {"x": [1,3,2]} + want_defined: false + - note: eq/object-empty + query: 'input.x = {}' + input: {"x": {}} + want_defined: true + - note: eq/object-empty (negative) + query: 'input.x = {}' + input: {"x": {"a": 1}} + want_defined: false + - note: eq/object-non-empty + query: 'input.x = {"a": 1, "b": 2}' + input: {"x": {"a": 1, "b": 2}} + want_defined: true + - note: eq/object-non-empty (negative) + query: 'input.x = {"a": 1, "b": 2}' + input: {"x": {"a": 2, "b": 1}} + want_defined: false + - note: eq/set-non-empty + query: '{input.a, input.b} = {input.b, input.a}' + input: {"a": 1, "b": 2} + want_defined: true + - note: eq/set-non-empty (negative) + query: '{input.a, input.b} = {input.a, input.c}' + input: {"a": 1, "b": 2, "c": 3} + want_defined: false diff --git a/third_party/opa/v1/test/wasm/assets/002_iteration.yaml b/third_party/opa/v1/test/wasm/assets/002_iteration.yaml new file mode 100644 index 000000000000..45c758a1cd4b --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/002_iteration.yaml @@ -0,0 +1,75 @@ +cases: + - note: iteration + query: input.x[i] = 1 + input: {"x": [3,2,1]} + want_defined: true + - note: iteration (negative) + query: input.x[i] = 1 + input: {"x": [3,2,0]} + want_defined: false + - note: iteration nested + query: input.x[i] = 1; input.y[j] = "world" + input: {"x": [3,2,1], "y": ["hello", "world"]} + want_defined: true + - note: iteration nested (negative) + query: input.x[i] = 1; input.y[j] = "world" + input: {"x": [3,2,0], "y": ["hello", "universe"]} + want_defined: false + - note: iteration chained + query: input.x[i].y[j] = 1 + input: {"x": [{"y": []}, {"y": [0]}, {"y": [0, 1]}]} + want_defined: true + - note: iteration chained (negative) + query: input.x[i].y[j] = 1 + input: {"x": [{"y": []}, {"y": [0]}, {"y": [0, 2]}]} + want_defined: false + - note: iteration ground + query: input.x[i].y[i] = 2 + input: { + "x": [ + { + "y": [ + 1, + 2, + ] + }, + { + "y": [ + 1, + 2, + ] + }, + ] + } + want_defined: true + - note: iteration ground + query: input.x[i].y[i] = 1 + input: { + "x": [ + { + "y": [ + 2, + 1, + ] + }, + { + "y": [ + 1, + 2, + ] + }, + ] + } + want_defined: false + - note: iteration composite + query: a = {["t",1], ["u",2], ["v", 3]}; a[["v", v]]; v == 3 + want_defined: true + - note: iteration composite (negative) + query: a = {["t",1], ["u",2], ["v", 3]}; a[["u", u]]; u == 3 + want_defined: false + - note: no scan required + query: u = 2; a = {["t",1], ["u",2], ["v", 3]}; a[["u", u]] + want_defined: true + - note: no scan required (negative) + query: v = 2; a = {["t",1], ["u",2], ["v", 3]}; a[["v", v]] + want_defined: false \ No newline at end of file diff --git a/third_party/opa/v1/test/wasm/assets/003_comparison.yaml b/third_party/opa/v1/test/wasm/assets/003_comparison.yaml new file mode 100644 index 000000000000..16106630ca45 --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/003_comparison.yaml @@ -0,0 +1,89 @@ +cases: + - note: lt/number + query: "input.x < 2" + input: {"x": 1} + want_defined: true + - note: lt/number (negative) + query: "input.x < 0" + input: {"x": 1} + want_defined: false + - note: lt/number (negative) + query: "input.x < 1" + input: {"x": 1} + want_defined: false + - note: lte/number + query: "input.x <= 2" + input: {"x": 1} + want_defined: true + - note: lte/number + query: "input.x <= 1" + input: {"x": 1} + want_defined: true + - note: lte/number (negative) + query: "input.x <= 0" + input: {"x": 1} + want_defined: false + - note: gt/number + query: "input.x > 0" + input: {"x": 1} + want_defined: true + - note: gt/number (negative) + query: "input.x > 1" + input: {"x": 1} + want_defined: false + - note: gt/number (negative) + query: "input.x > 2" + input: {"x": 1} + want_defined: false + - note: gte/number + query: "input.x >= 0" + input: {"x": 1} + want_defined: true + - note: gte/number + query: "input.x >= 1" + input: {"x": 1} + want_defined: true + - note: gte/number (negative) + query: "input.x >= 2" + input: {"x": 1} + want_defined: false + - note: neq/number + query: "input.x != 1" + input: {"x": 0} + want_defined: true + - note: neq/number (negative) + query: "input.x != 1" + input: {"x": 1} + want_defined: false + - note: deq/number + query: "input.x == 1" + input: {"x": 1} + want_defined: true + - note: deq/number + query: "input.x == 1" + input: {"x": 0} + want_defined: false + - note: deq/array + query: "[input.x] == [1]" + input: {"x": 1} + want_defined: true + - note: deq/array (negative) + query: "[input.x] == [1]" + input: {"x": 2} + want_defined: false + - note: deq/array (undefined) + query: "[input.x] = [1]" + input: {} + want_defined: false + - note: deq/object + query: '{"a": input.x} == {"a": 1}' + input: {"x": 1} + want_defined: true + - note: deq/object (negative) + query: '{"a": input.x} == {"a": 1}' + input: {"x": 2} + want_defined: false + - note: deq/object (undefined) + query: '{"a": input.x} == {"a": 1}' + input: {} + want_defined: false diff --git a/third_party/opa/v1/test/wasm/assets/004_negation.yaml b/third_party/opa/v1/test/wasm/assets/004_negation.yaml new file mode 100644 index 000000000000..68f6274c549a --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/004_negation.yaml @@ -0,0 +1,21 @@ +cases: + - note: negate/eq + query: not input.x = 1 + input: {"x": 2} + want_defined: true + - note: negate/eq (negative) + query: not input.x = 1 + input: {"x": 1} + want_defined: false + - note: negate/undefined + query: not input.deadbeef + input: {} + want_defined: true + - note: negate/undefined (negative) + query: not input.x + input: {"x": 1} + want_defined: false + - note: negate/false + query: not input.x + input: {"x": false} + want_defined: true diff --git a/third_party/opa/v1/test/wasm/assets/005_references.yaml b/third_party/opa/v1/test/wasm/assets/005_references.yaml new file mode 100644 index 000000000000..c9b732258f88 --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/005_references.yaml @@ -0,0 +1,14 @@ +cases: + - note: defined + query: input.x + input: {"x": 1} + want_defined: true + - note: undefined + query: input.deadbeef + input: {} + want_defined: false + - note: "false" + query: input.x + input: {"x": false} + want_defined: false + diff --git a/third_party/opa/v1/test/wasm/assets/006_pattern_matching.yaml b/third_party/opa/v1/test/wasm/assets/006_pattern_matching.yaml new file mode 100644 index 000000000000..89b97778d7aa --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/006_pattern_matching.yaml @@ -0,0 +1,41 @@ +cases: + - note: match/array simple + query: "input.x = [y]; input.y = y" + input: {"x": [1], "y": 1} + want_defined: true + - note: match/array simple (negative) + query: "input.x = [y]; input.y = y" + input: {"x": [1], "y": 2} + want_defined: false + - note: match/array nested + query: "input.x = [[y], 1]; input.y = [y]" + input: {"x": [[0], 1], "y": [0]} + want_defined: true + - note: match/array nested (negative) + query: "input.x = [[y], 1]; input.y = [y]" + input: {"x": [[0], 1], "y": [1]} + want_defined: false + - note: match/object simple + query: 'input.x = {"a": y}; input.y = y' + input: {"x": {"a": 1}, "y": 1} + want_defined: true + - note: match/object simple (negative) + query: 'input.x = {"a": y}; input.y = y' + input: {"x": {"a": 1}, "y": 2} + want_defined: false + - note: match/object nested + query: 'input.x = {"a": {"b": y}, "c": 1}; input.y = {"a": y}' + input: {"x": {"a": {"b": 0}, "c": 1}, "y": {"a": 0}} + want_defined: true + - note: match/object nested (negative) + query: 'input.x = {"a": {"b": y}, "c": 1}; input.y = {"a": y}' + input: {"x": {"a": {"b": 0}, "c": 1}, "y": {"a": 1}} + want_defined: false + - note: match/reference operand + query: | + x = {[1, 1], [1, 2], [1, 3]}; x[[1, y]][1] == 2 + want_defined: true + - note: match/reference operand (negative) + query: | + x = {[1, 1], [1, 2], [1, 3]}; x[[1, y]][1] == "deadbeef" + want_defined: false diff --git a/third_party/opa/v1/test/wasm/assets/007_complete.yaml b/third_party/opa/v1/test/wasm/assets/007_complete.yaml new file mode 100644 index 000000000000..4cf7f8b18998 --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/007_complete.yaml @@ -0,0 +1,203 @@ +cases: + - note: constants + query: data.x.p = 1 + modules: + - | + package x + p = 1 + want_defined: true + - note: constants (negative) + query: data.x.p = 1 + modules: + - | + package x + p = 2 + want_defined: false + - note: variable + query: data.x.p = 1 + modules: + - | + package x + import rego.v1 + p = y if { x = 1; y = x } + want_defined: true + - note: variable (negative) + query: data.x.p = 1 + modules: + - | + package x + import rego.v1 + p = y if { x = 2; y = x } + want_defined: false + - note: composites + query: data.x.p = [1] + modules: + - | + package x + import rego.v1 + p = [x] if { x = 1 } + want_defined: true + - note: composites (negative) + query: data.x.p = [1] + modules: + - | + package x + import rego.v1 + p = [x] if { x = 2 } + want_defined: false + - note: conjunction + query: data.x.p = 1 + modules: + - | + package x + import rego.v1 + p = 1 if { q; r } + q if { true } + r if { true } + want_defined: true + - note: conjunction (negative) + query: data.x.p = 1 + modules: + - | + package x + import rego.v1 + p = 1 if { q; r } + q if { true } + r if { false } + want_defined: false + - note: disjunction + query: data.x.p = 2 + modules: + - | + package x + import rego.v1 + p = 1 if { false } + p = 2 if { true } + p = 3 if { false } + want_defined: true + - note: disjunction (negative) + query: data.x.p = 1 + modules: + - | + package x + import rego.v1 + p = 1 if { false } + p = 2 if { false } + p = 3 if { true } + want_defined: false + - note: negation + query: not data.x.p = 1 + modules: + - | + package x + import rego.v1 + p = 1 if { false } # undefined + want_defined: true + - note: negation (negative) + query: not data.x.p = 1 + modules: + - | + package x + p = 1 + want_defined: false + - note: chain + query: data.x.p = 1 + modules: + - | + package x + p = q + q = r + r = 1 + want_defined: true + - note: chain (negative) + query: data.x.p = 1 + modules: + - | + package x + p = q + q = r + r = 2 + want_defined: false + - note: chain input + query: data.x.p = true + modules: + - | + package x + import rego.v1 + p = q + q = r + r if { input.x = 1 } + input: {"x": 1} + want_defined: true + - note: chain input (negative) + query: data.x.p = true + modules: + - | + package x + import rego.v1 + p = q + q = r + r if { input.x = 2 } + input: {"x": 1} + want_defined: false + - note: iteration + query: data.x.p = 1 + modules: + - | + package x + import rego.v1 + p = 1 if { input[x] = 1 } + input: [3,2,1] + want_defined: true + - note: iteration (negative) + query: data.x.p = 1 + modules: + - | + package x + import rego.v1 + p = 1 if { input[x] = 4 } + input: [3,2,1] + want_defined: false + - note: conflict error + query: data.x.p = 1 + modules: + - | + package x + p = 1 + p = 2 + want_error: "module0.rego:3:1: var assignment conflict" + - note: packages + query: data.x.p = 1 + modules: + - | + package x + import data.y.p + p = p + - | + package y + p = 1 + want_defined: true + - note: dereference + query: data.x.p[0].a = 1 + modules: + - | + package x + p = [{ + "a": 1, + "b": 2 + }] + want_defined: true + - note: iteration embedded + query: data.x.p[i].a[j] = 1 + modules: + - | + package x + p = [{"a": [2, 3]}, {"a": [3, 1]}, {"a": []}] + want_defined: true + - note: iteration embedded (negative) + query: data.x.p[i].a[j] = 1 + modules: + - | + package x + p = [{"a": [2, 3]}, {"a": [3, 2]}, {"a": []}] + want_defined: false diff --git a/third_party/opa/v1/test/wasm/assets/008_functions.yaml b/third_party/opa/v1/test/wasm/assets/008_functions.yaml new file mode 100644 index 000000000000..14a94907c59c --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/008_functions.yaml @@ -0,0 +1,129 @@ +cases: + - note: identity + query: data.x.f(1, 1) + modules: + - | + package x + f(x) = x + want_defined: true + - note: identity (negative) + query: data.x.f(1, 2) + modules: + - | + package x + f(x) = x + want_defined: false + - note: identity implicit + query: data.x.f(1) = 1 + modules: + - | + package x + f(x) = x + want_defined: true + - note: identity implicit (negative) + query: data.x.f(1) = 2 + modules: + - | + package x + f(x) = x + want_defined: false + - note: composite arg + query: data.x.f([1]) = 1 + modules: + - | + package x + f(x) = x[0] + want_defined: true + - note: composite param + query: data.x.f([1]) = 1 + modules: + - | + package x + f([x]) = x + want_defined: true + - note: multiple params + query: data.x.f(1, 2) = [2, 1] + modules: + - | + package x + f(x, y) = [y, x] + want_defined: true + - note: multiple params (negative) + query: data.x.f(1, 2) = [2, 1] + modules: + - | + package x + f(x, y) = [x, x] + want_defined: false + - note: disjunction + query: data.x.f(1) = 0 + modules: + - | + package x + import rego.v1 + f(x) = 1 if { x <= 0 } + f(x) = 0 if { x > 0 } + want_defined: true + - note: disjunction (negative) + query: data.x.f(1) = 2 + modules: + - | + package x + import rego.v1 + f(x) = 1 if { x <= 0 } + f(x) = 0 if { x > 0 } + want_defined: false + - note: negation + query: not data.x.f(-1) + modules: + - | + package x + import rego.v1 + f(x) = x if { x >= 0 } + want_defined: true + - note: input + query: data.x.f(1) = 1 + modules: + - | + package x + import rego.v1 + f(x) = g(x) + g(x) = x if { input.x = x } + input: {"x": 1} + want_defined: true + - note: input (negative) + query: data.x.f(1) = 1 + modules: + - | + package x + import rego.v1 + f(x) = g(x) + g(x) = x if { input.x = x } + input: {"x": 2} + want_defined: false + - note: conflict error + query: data.x.f(1) = 1 + modules: + - | + package x + f(x) = 1 + f(x) = 2 + want_error: "module0.rego:3:1: var assignment conflict" + - note: 'false result' + query: data.test.p = x + modules: + - | + package test + import rego.v1 + f(x) = false + p = true if { f(1) } + want_defined: false + - note: 'negated false result' + query: data.test.p = x + modules: + - | + package test + import rego.v1 + f(x) = false + p = true if { not f(1) } + want_defined: true diff --git a/third_party/opa/v1/test/wasm/assets/009_default.yaml b/third_party/opa/v1/test/wasm/assets/009_default.yaml new file mode 100644 index 000000000000..ee726ddb1af6 --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/009_default.yaml @@ -0,0 +1,52 @@ +cases: + - note: default + query: data.x.p = 1 + modules: + - | + package x + default p = 1 + want_defined: true + - note: default fallback + query: data.x.p = 1 + modules: + - | + package x + import rego.v1 + default p = 1 + p = 2 if { false } + want_defined: true + - note: default fallback (negative) + query: data.x.p = 2 + modules: + - | + package x + import rego.v1 + default p = 1 + p = 2 if { false } + want_defined: false + - note: default skipped + query: data.x.p = 2 + modules: + - | + package x + default p = 1 + p = 2 + want_defined: true + - note: default requires eval + query: data.x.p = [] + modules: + - | + package x + import rego.v1 + default p = [1 | false] + p = "deadbeef" if { false } + want_defined: True + - note: default requires eval + query: data.x.p = [1] + modules: + - | + package x + import rego.v1 + default p = [1 | true] + p = "deadbeef" if { false } + want_defined: true \ No newline at end of file diff --git a/third_party/opa/v1/test/wasm/assets/010_else.yaml b/third_party/opa/v1/test/wasm/assets/010_else.yaml new file mode 100644 index 000000000000..5c2ecd6fac75 --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/010_else.yaml @@ -0,0 +1,123 @@ +cases: + - note: short circuit + query: data.x.p = 1 + modules: + - | + package x + import rego.v1 + p = 1 if { true } + else = 2 if { true } + want_defined: true + - note: fallthrough + query: data.x.p = 2 + modules: + - | + package x + import rego.v1 + p = 1 if { false } + else = 2 if { true } + else = 3 if { true } + want_defined: true + - note: fallthrough second + query: data.x.p = 3 + modules: + - | + package x + import rego.v1 + p = 1 if { false } + else = 2 if { false } + else = 3 if { true } + want_defined: true + - note: fallthrough multiple + query: data.x.p = 5 + modules: + - | + package x + import rego.v1 + p = 1 if { false } + else = 2 if { false } + else = 3 if { false } + p = 4 if { false } + else = 5 if { true } + want_defined: true + - note: short circuit (iteration) + query: data.x.p = 1 + modules: + - | + package x + import rego.v1 + p = 1 if { input.x[_] = 1 } + else = 2 if { true } + input: {"x": [3,2,1]} + want_defined: true + - note: fallthrough (iteration) + query: data.x.p = 1 + modules: + - | + package x + import rego.v1 + p = 1 if { input.x[_] = "deadbeef" } + else = 2 if { input.x[_] = 1 } + input: {"x": [3,2,1]} + want_defined: false + - note: chain + query: data.x.p = 3 + modules: + - | + package x + import rego.v1 + p = 1 if { false } + else = q if { true } + q = 2 if { false } + else = r if { true } + r = 3 + want_defined: true + - note: chain (input) + query: data.x.p = 2 + modules: + - | + package x + import rego.v1 + p = q + q = 1 if { input.deadbeef = 1 } + else = 2 if { input.x = 2 } + want_defined: true + input: {"x": 2} + - note: functions short circuit + query: data.x.f(1) = 1 + modules: + - | + package x + import rego.v1 + f(x) = 1 if { true } + else = 2 if { true } + want_defined: true + - note: functions fallthrough + query: data.x.f(1) = 3 + modules: + - | + package x + import rego.v1 + f(x) = 1 if { false } + else = 2 if { false } + else = 3 if { true } + want_defined: true + - note: short-circuit after failing + query: data.x.p = 1 + modules: + - | + package x + import rego.v1 + + # data.y[_] causes the planner to scan the virtual _and_ base document trees. + # The virtual document scan will succeed (because of the second module) + # but the base document scan will fail (because no base documents are loaded). + # + # This test ensures that the else keyword short-circuits as expected and does + # not evaluate the second block even though a failure has occurred. + p = x if { x = data.y[_] } + else = 2 if { true } + - | + package y + q = 1 + want_defined: true \ No newline at end of file diff --git a/third_party/opa/v1/test/wasm/assets/011_partialsets.yaml b/third_party/opa/v1/test/wasm/assets/011_partialsets.yaml new file mode 100644 index 000000000000..fe9f54164e74 --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/011_partialsets.yaml @@ -0,0 +1,149 @@ +cases: + - note: additive + query: data.x.p = {2,1} + modules: + - | + package x + import rego.v1 + p contains 1 + p contains 2 + want_defined: true + - note: additive (negative) + query: data.x.p = {2,1} + modules: + - | + package x + import rego.v1 + p contains 1 + p contains 2 + p contains 3 + want_defined: false + - note: input + query: data.x.p = {2,1} + modules: + - | + package x + import rego.v1 + p contains 1 if { input.x = 1 } + p contains 2 if { input.y = 2 } + want_defined: true + input: {"x": 1, "y": 2} + - note: input (negative) + query: data.x.p = {2,1} + modules: + - | + package x + import rego.v1 + p contains 1 if { input.x = 1 } + p contains 2 if { input.y = 2 } + p contains 3 if { input.z = 3 } + want_defined: true + input: {"x": 1, "y": 2} + - note: composites + query: data.x.p = {[2],[1]} + modules: + - | + package x + import rego.v1 + p contains [x] if { x = 1 } + p contains [y] if { y = 2 } + want_defined: true + - note: set membership + query: data.x.p[1] + modules: + - | + package x + import rego.v1 + p contains 1 + p contains 2 + want_defined: true + - note: set membership (negative) + query: data.x.p[3] + modules: + - | + package x + import rego.v1 + p contains 1 + p contains 2 + want_defined: false + - note: set membership (negation) + query: not data.x.p[3] + modules: + - | + package x + import rego.v1 + p contains 1 + p contains 2 + want_defined: true + - note: set iteration + query: data.x.p[x]; x > 1 + modules: + - | + package x + import rego.v1 + p contains 1 + p contains 2 + want_defined: true + - note: set iteration (negative) + query: data.x.p[x]; x > 2 + modules: + - | + package x + import rego.v1 + p contains 1 + p contains 2 + want_defined: false + - note: set composites + query: a := [1,x]; data.x.p[a]; x == "y" + modules: + - | + package x + import rego.v1 + p contains [1, "x"] + p contains [1, "y"] + p contains [2, "x"] + want_defined: true + - note: set dereference + query: data.x.p[x].a == 1; x.b == "y" + modules: + - | + package x + import rego.v1 + p contains {"a": 1, "b": "x"} + p contains {"a": 1, "b": "y"} + p contains {"a": 1, "b": "y"} + want_defined: true + - note: set dereference (negative) + query: data.x.p[x].a == 100 + modules: + - | + package x + import rego.v1 + p contains {"a": 1} + p contains {"a": 2} + p contains {"a": 3} + want_defined: false + - note: set chain + query: data.x.p[x]; x = 2 + modules: + - | + package x + import rego.v1 + p contains x if { q[x]; r[x] } + q contains 1 + q contains 2 + q contains 3 + r contains 2 + want_defined: true + - note: set chain (negative) + query: data.x.p[x]; x = 2 + modules: + - | + package x + import rego.v1 + p contains x if { q[x]; r[x] } + q contains 1 + q contains 2 + q contains 3 + r contains 3 + want_defined: false \ No newline at end of file diff --git a/third_party/opa/v1/test/wasm/assets/012_partialobjects.yaml b/third_party/opa/v1/test/wasm/assets/012_partialobjects.yaml new file mode 100644 index 000000000000..ed584e90211d --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/012_partialobjects.yaml @@ -0,0 +1,78 @@ +cases: + - note: additive + query: 'data.x.p = {"a": 1, "b": 2}' + modules: + - | + package x + p["a"] = 1 + p["b"] = 2 + want_defined: true + - note: additive (negative) + query: 'data.x.p = input' + input: {"a": 1, "b": 2} + modules: + - | + package x + p["a"] = 1 + p["b"] = 2 + p["c"] = 3 + want_defined: false + - note: input + query: 'data.x.p = {"b": 2, "a": 1}' + input: {"x": 1, "y": 2} + modules: + - | + package x + import rego.v1 + p["a"] = 1 if { input.x = 1 } + p["b"] = 2 if { input.y = 2 } + want_defined: true + - note: input (negative) + query: 'data.x.p = data.z' + data: + z: + a: 1 + b: 2 + modules: + - | + package x + import rego.v1 + p["a"] = 1 if { input.x = 1 } + p["b"] = 2 if { input.y = 2 } + p["c"] = 3 if { input.z = 3 } + want_defined: true + input: {"x": 1, "y": 2} + - note: composites + query: 'data.x.p = {"a": [1], "b": [2]}' + modules: + - | + package x + import rego.v1 + p[x] = [y] if { x = "a"; y = 1 } + p[x] = [y] if { x = "b"; y = 2 } + want_defined: true + - note: conflict error + data: + z: + a: 1 + query: 'data.x.p = data.z' + modules: + - | + package x + p["x"] = 1 + p["x"] = 2 + want_error: "module0.rego:3:1: var assignment conflict" + - note: object dereference + query: data.x.p.a.b = 1 + modules: + - | + package x + p["a"] = {"b": 1} + want_defined: true + - note: object dereference (negative) + query: data.x.p.a.b = 1 + modules: + - | + package x + p["a"] = {"b": 2} + want_defined: false diff --git a/third_party/opa/v1/test/wasm/assets/013_virtual.yaml b/third_party/opa/v1/test/wasm/assets/013_virtual.yaml new file mode 100644 index 000000000000..fd2de89de783 --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/013_virtual.yaml @@ -0,0 +1,245 @@ +cases: + - note: base data extent + query: | + data == {"x": {"y": 1, "z": 2}} + data: {"x": {"y": 1, "z": 2}} + want_defined: true + - note: base data extent (negative) + query: | + data == {"x": {"y": 1, "z": 2}} + data: {"x": {"y": 1, "z": "deadbeef"}} + want_defined: false + - note: base data extent path + query: | + data.x == {"y": 1, "z": 2} + data: {'x': {'y': 1, 'z': 2}} + want_defined: true + - note: base data extent path (negative) + query: | + data.deadbeef + data: {'x': {'y': 1, 'z': 2}} + want_defined: false + - note: base data extent path-2 + query: | + data.x.y == 1 + data: {'x': {'y': 1, 'z': 2}} + want_defined: true + - note: base data extent path-2 (negative) + query: | + data.x.deadbeef + data: {'x': {'y': 1, 'z': 2}} + want_defined: false + - note: base data iteration + query: data.foo[x] = y; x == "b"; y == 2 + data: {"foo": {"a": 1, "b": 2, "c": 3}} + want_defined: true + - note: base data iteration (negative) + query: data.foo[x] = y; x == "b"; y == 2 + data: {"foo": {"a": 1, "b": "deadbeef", "c": 3}} + want_defined: false + - note: virtual data extent + query: | + data == { + "x": { + "y": { + "p": 1, + "r": { + "a": 3 + }, + "s": {"elem1"} + } + } + } + modules: + - | + package x.y + import rego.v1 + p = 1 + q = 2 if { false } + r["a"] = 3 + r["b"] = 4 if { false } + s contains "elem1" + s contains "elem2" if { false } + want_defined: true + - note: package extent + query: | + data.x == {"y": {"p": 1}} + modules: + - | + package x.y + import rego.v1 + p = 1 + q = 2 if { false } + want_defined: true + - note: all undefined + query: | + data.x == {"y": {}} + modules: + - | + package x.y + import rego.v1 + p = 1 if { false } + want_defined: true + - note: skip functions + query: | + data.x == {"y": {"p": 1}} + modules: + - | + package x.y + p = 1 + f(x) = x + want_defined: true + - note: empty package + query: | + data.x == {"y": {}} + modules: + - | + package x.y + want_defined: true + - note: enumerate packages + query: data.test.p == {"a", "b"} + modules: + - | + package test + import rego.v1 + + p contains x if { + data.pkg[x] = _ + } + - | + package pkg.a + - | + package pkg.b + want_defined: true + - note: enumerate packages (negative) + query: data.test.p == {"a", "b"} + modules: + - | + package test + import rego.v1 + + p contains x if { + data.pkg[x] = _ + } + - | + package pkg.a + - | + package pkg.deadbeef + want_defined: false + - note: enumerate packages (complex) + query: | + data.test.p == { + ["a", "b", {"p": 1}], + ["b", "c", {"r": 3}], + } + modules: + - | + package test + import rego.v1 + + p contains [x, y, v] if { data.pkg[x].sub[y] = v } + - | + package pkg.a.sub.b + import rego.v1 + p = 1 + q = 2 if { false } + - | + package pkg.b.sub.c + import rego.v1 + r = 3 + s = 4 if { false } + want_defined: true + - note: merge + query: | + data.test == {"x": 1, "y": 2} + data: + { + "test": { + "x": 1 + } + } + modules: + - | + package test + y = 2 + want_defined: true + - note: merge (negative) + query: | + data.test == {"x": 1, "y": 2} + data: + { + "test": { + "x": "deadbeef" + } + } + modules: + - | + package test + y = 2 + want_defined: false + - note: merge conflict + query: | + data.test == {"x": {"y": 1}} + data: + { + "test": { + "x": { + "y": 1 + } + } + } + modules: + - | + package test + + x = [] + want_defined: true + - note: merge iteration + query: | + data.test[x].foo == {"q": 1, "p": 3}; x == "a" + data: + { + "test": { + "a": { + "foo": { + "q": 1 + } + }, + "b": { + "foo": { + "q": 1 + } + } + } + } + modules: + - | + package test.a.foo + + p = 3 + want_defined: true + - note: merge iteration (negative) + query: | + data.test[x].foo == {"q": 1, "p": 3}; x == "a" + data: + { + "test": { + "a": { + "foo": { + "q": 1 + } + }, + "b": { + "foo": { + "q": 1 + } + } + } + } + modules: + - | + package test.a.foo + + p = 3 + r = "deadbeef" + want_defined: false diff --git a/third_party/opa/v1/test/wasm/assets/014_comprehensions.yaml b/third_party/opa/v1/test/wasm/assets/014_comprehensions.yaml new file mode 100644 index 000000000000..6b1ad933e0cd --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/014_comprehensions.yaml @@ -0,0 +1,45 @@ +cases: +- note: set comprehension + query: | + {x | input[_] = x} == {1,2,3} + input: [1,2,3] + want_defined: true +- note: set comprehension (negative) + query: | + {x | input[_] = x; x > 1} == {1,2,3} + input: [1,2,3] + want_defined: false +- note: array comprehension + query: | + [x | input[_] = x] == [1,2,3] + input: [1,2,3] + want_defined: true +- note: array comprehension (negative) + query: | + [x | input[_] = x; x > 1] == [1,2,3] + input: [1,2,3] + want_defined: false +- note: array comprehension unify + query: | + [x | input[_] = x] = [1,y,3] + input: [1,2,3] + want_defined: true +- note: object comprehension + query: | + {k: v | input[k] = v} == {"a": 1, "b": 2} + input: {"a": 1, "b": 2} + want_defined: true +- note: object comprehension (negative) + query: | + {k: v | input[k] = v; v > 1} == {"a": 1, "b": 2} + input: {"a": 1, "b": 2} + want_defined: false +- note: object comprehension unify + query: | + {k: v | input[k] = v} = {"a": y, "b": z} + input: {"a": 1, "b": 2} + want_defined: true +- note: closure + query: | + a = [1,2,3]; b = 1; {x | a[_] = x; x > b} == {2,3} + want_defined: true \ No newline at end of file diff --git a/third_party/opa/v1/test/wasm/assets/015_results.yaml b/third_party/opa/v1/test/wasm/assets/015_results.yaml new file mode 100644 index 000000000000..74e6da83df0b --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/015_results.yaml @@ -0,0 +1,34 @@ +cases: +- note: one + query: x = 1 + want_result: [{'x': 1}] +- note: undefined + query: x = 1; not true + want_result: [] +- note: multiple vars + query: x = 1; y = 2 + want_result: [{'x': 1, 'y': 2}] +- note: multiple bindings + query: input[i] = 1 + input: [0,1,1] + want_result: [{'i': 1}, {'i': 2}] +- note: generated vars excluded + query: input[i] > 0 + input: [0,1,1] + want_result: [{'i': 1}, {'i': 2}] +- note: wildcard vars excluded + query: a = input[_]; a > 1 + input: [1,2,3] + want_result: [{'a': 2}, {'a': 3}] +- note: locally scoped vars rewritten (assignment operator) + query: a := input[_]; a > 1 + input: [1,2,3] + want_result: [{'a': 2}, {'a': 3}] +- note: locally scoped vars rewritten (some keyword) + query: some a; a = input[_]; a > 1 + input: [1,2,3] + want_result: [{'a': 2}, {'a': 3}] +- note: closure vars excluded + query: a = [x | x = input[i]; x > 1] + input: [1,2,3] + want_result: [{'a': [2,3]}] \ No newline at end of file diff --git a/third_party/opa/v1/test/wasm/assets/016_with.yaml b/third_party/opa/v1/test/wasm/assets/016_with.yaml new file mode 100644 index 000000000000..ff6e58888489 --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/016_with.yaml @@ -0,0 +1,337 @@ +cases: + - note: assignment + query: | + input = {"a": 1} with input as {"a": 1} + want_defined: true + - note: assignment (negative) + query: | + input = {"a": 1} with input as {"a": "deadbeef"} + want_defined: false + - note: assignment override + query: | + input = {"a": 2} with input as {"a": 2} + input: {"a": 1} + want_defined: true + - note: assignment override (negative) + query: | + input = {"a": 2} with input as {"a": "deadbeef"} + input: {"a": 2} + want_defined: false + - note: assignment undo + query: | + input = {"a": 1} with input as {"a": 1} + input = {"a": 2} + input: {"a": 2} + want_defined: true + - note: assignment iteration + query: | + input[i] = 1 with input as [1,2,1] + want_result: [{'i': 0}, {'i': 2}] + - note: assignment transitive + query: | + data.test.p = x with input as "p" + modules: + - | + package test + + import rego.v1 + + p = x if { + q = x with input as ["q", input] + } + + q = x if { + r = x # intentionally unmodified, with keyword applies transitively + } + + r = input + want_result: [{'x': ["q", "p"]}] + - note: assignment undo across queries + query: | + data.test.p[x] + modules: + - | + package test + + import rego.v1 + + p contains x if{ + x = input.a with input as {"a": 1} + } + + p contains y if { + y = input.b with input as {"b": 2} + } + + p contains t if { + t = input.b # expected to be undefined + } + + p contains u if { + u = input.a # expected to be undefined + } + want_result: [{'x': 1}, {'x': 2}] + - note: upsert + query: | + input = x with input.foo as 1 + want_result: [{'x': {'foo': 1}}] + - note: upsert make intermediate nodes + query: | + input = x with input.foo.bar.baz as [1,2,3] + want_result: [{'x': {'foo': {'bar': {'baz': [1,2,3]}}}}] + - note: upsert merge top-level + query: | + input = x with input.foo as 1 + input: {'bar': 2} + want_result: [{'x': {'foo': 1, 'bar': 2}}] + - note: upsert merge top-level make intermediate nodes + query: | + input = x with input.foo.bar as 1 + input: {'baz': 2} + want_result: [{'x': {'foo': {'bar': 1}, 'baz': 2}}] + - note: upsert merge intermediate nodes + query: | + input = x with input.foo.bar as 1 + input: {'foo': {'baz': 2}} + want_result: [{'x': {'foo': {'bar': 1, 'baz': 2}}}] + - note: upsert merge intermediate nodes with new node + query: | + input = x with input.foo.bar.qux as 1 + input: { 'foo': {'baz': 2}} + want_result: [{'x': {'foo': {'bar': {'qux': 1}, 'baz': 2}}}] + - note: upsert merge top-level multiple + query: | + input = x with input.foo as 1 with input.bar as 2 + want_result: [{'x': {'foo': 1, 'bar': 2}}] + - note: upsert merge intermediate multiple + query: | + input = x with input.foo.bar as 1 with input.foo.baz as 2 + want_result: [{'x': {'foo': {'bar': 1, 'baz': 2}}}] + - note: upsert iteration + query: | + input.foo[x] = y with input.foo.bar as 1 with input.foo.baz as 2 + want_result: [ + { + 'x': 'baz', + 'y': 2, + }, + { + 'x': 'bar', + 'y': 1, + }, + ] + - note: shadow rules + query: | + data = x with data.foo as 1 + want_result: [ + { + 'x': { + 'foo': 1 + } + } + ] + modules: + - | + package foo + + p = 1 + - note: shadow rules and merge + query: | + data = x with data.foo as 1 with data.bar.r as 3 + want_result: [ + { + 'x': { + 'foo': 1, + 'bar': { + 'q': 2, + 'r': 3, + } + } + } + ] + modules: + - | + package foo + + p = 1 + - | + package bar + q = 2 + - note: shadow cached data + query: | + data = x with data.foo as 1 + data: {'foo': 2} + want_result: [ + { + 'x': { + 'foo': 1 + } + } + ] + - note: shadow cached data and merge + query: | + data = x with data.foo as 1 with data.bar.qux as 4 + data: {'foo': 2, 'bar': {'baz': 3}} + want_result: [ + { + 'x': { + 'foo': 1, + 'bar': { + 'baz': 3, + 'qux': 4, + } + } + } + ] + - note: undo rule shadow + query: | + data.test.p = x; data.test.q = y with data.test.r as 2; data.test.r = z; data.test.q = t + modules: + - | + package test + + p = r + q = [r] + r = 1 + want_result: [ + { + 'x': 1, + 'y': [2], + 'z': 1, + 't': [1], + } + ] + - note: undo data shadow + query: | + data.test.p = x; data.test.q = y with data.test.r as 2; data.test.r = z; data.test.q = t + modules: + - | + package test + p = data.test.r + q = [data.test.r] + data: { + 'test': { + 'r': 1, + } + } + want_result: [ + { + 'x': 1, + 'y': [2], + 'z': 1, + 't': [1], + } + ] + - note: with negation + query: | + not input with input as false + want_result: [{}] + - note: with negation (negative) + query: | + not input with input as true + want_result: [] + - note: with conflict + query: | + input = x with input.foo as 1 with input.foo.bar as 2 + want_result: + - x: + foo: + bar: 2 + - note: with virtual doc iteration + query: | + x := data[i][j] with data.bar.p as 3 with data.bar.q as 4; y = data.bar.p; z = data.bar.q + modules: + - | + package foo + p = 0 + q = 1 + r = 2 + - | + package bar + p = -1 + data: { + 'bar': { + 'q': -2, + } + } + want_result: [ + { + "i": "foo", + "j": "p", + "x": 0, + "y": -1, + "z": -2, + }, + { + "i": "foo", + "j": "q", + "x": 1, + "y": -1, + "z": -2, + }, + { + "i": "foo", + "j": "r", + "x": 2, + "y": -1, + "z": -2, + }, + { + "i": "bar", + "j": "p", + "x": 3, + "y": -1, + "z": -2, + }, + { + "i": "bar", + "j": "q", + "x": 4, + "y": -1, + "z": -2, + }, + ] + - note: with invalidates memoization + query: data.test.p = x + modules: + - | + package test + + import rego.v1 + + p = [x, y, z] if { + x = q + y = q with input as 7 + z = r with input as 8 + } + + default q = 6 + + q = input + + r = [t, s] + + s = q + + t = x if { x = q with input as 9 } + want_result: [ + {"x": [6, 7, [9,8]]} + ] + - note: with and call_indirect gens + query: x := "b"; z := data.test[x] + modules: + - | + package test + + import rego.v1 + + a if { + true with data.one as 1 + } + + b if { + true + } + want_result: + - x: b + z: true diff --git a/third_party/opa/v1/test/wasm/assets/017_strings.yaml b/third_party/opa/v1/test/wasm/assets/017_strings.yaml new file mode 100644 index 000000000000..77bfe2e67dcd --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/017_strings.yaml @@ -0,0 +1,30 @@ +cases: + - note: literal quote escaping + modules: + - | + package test + + p = "\"xxx\"" + query: | + data.test.p = x + want_result: [ + { + 'x': "\"xxx\"" + } + ] + - note: input quote escaping + modules: + - | + package test + + p = input.string + input: { + "string": "\"xxx\"" + } + query: | + data.test.p = x + want_result: [ + { + 'x': "\"xxx\"" + } + ] \ No newline at end of file diff --git a/third_party/opa/v1/test/wasm/assets/018_builtins.yaml b/third_party/opa/v1/test/wasm/assets/018_builtins.yaml new file mode 100644 index 000000000000..c2a1e88c9225 --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/018_builtins.yaml @@ -0,0 +1,457 @@ +cases: + - note: equal built-in (true) + query: equal(1,1,x) + want_result: + - x: true + - # NOTE: This is a property of the planner/compiler interaction, more so + # than it is a property of the builtin implementation. Therefore, we only + # assert it once for equal/true, equal/false; instead of duplicating all + # the other comparison test cases. + note: equal built-in (true, result not captured) + query: equal(1,1) + want_defined: true + - note: equal built-in (false) + query: equal(1,2,x) + want_result: + - x: false + - note: equal built-in (false, result not captured) + query: equal(1,2) + want_defined: false + - note: gt built-in (true) + query: gt(1,0,x) + want_result: + - x: true + - note: gt built-in (false) + query: gt(1,2,x) + want_result: + - x: false + - note: gte built-in (true) + query: gte(1,0,x) + want_result: + - x: true + - note: gte built-in (true, equal) + query: gte(1,1,x) + want_result: + - x: true + - note: gte built-in (false) + query: gte(1,2,x) + want_result: + - x: false + - note: lt built-in (true) + query: lt(0,1,x) + want_result: + - x: true + - note: lt built-in (false) + query: lt(2,1,x) + want_result: + - x: false + - note: lte built-in (true) + query: lte(0,1,x) + want_result: + - x: true + - note: lte built-in (true, equal) + query: lte(0,0,x) + want_result: + - x: true + - note: lte built-in (false) + query: lte(2,1,x) + want_result: + - x: false + - note: neq built-in (true) + query: neq(0,1,x) + want_result: + - x: true + - note: neq built-in (false) + query: neq(1,1,x) + want_result: + - x: false + - note: abs built-in + query: abs(-1,x) + want_result: [{'x': 1}] + - note: round built-in + query: round(1.4,x) + want_result: [{'x': 1}] + - note: round built-in ("halfs up") + query: round(1.5,x) + want_result: [{'x': 2}] + - note: round built-in ("halfs up not to even") + query: round(2.5,x) + want_result: [{'x': 3}] + - note: plus built-in + query: plus(1,1,x) + want_result: [{'x': 2}] + - note: minus built-in + query: minus(1,1,x) + want_result: [{'x': 0}] + - note: multiply built-in + query: mul(2,3,x) + want_result: [{'x': 6}] + - note: divide built-in + query: div(2,3,x) + want_result: [{'x': 0.6666666666666666}] + - note: remainder built-in + query: rem(4,3,x) + want_result: [{'x': 1}] + - note: array concat built-in + query: array.concat([0],[1],x) + want_result: [{'x': [0,1]}] + - note: array slice built-in + query: array.slice([0,1,2,4],1,3,x) + want_result: [{'x': [1,2]}] + - note: set diff built-in + query: set_diff({0,1},{0},x) + want_result: [{'x': [1]}] + - note: and built-in + query: and({0,1},{0},x) + want_result: [{'x': [0]}] + - note: or built-in + query: or({0,1},{0},x) + want_result: [{'x': [1,0]}] + - note: intersection built-in + query: intersection({{0,1},{0}},x) + want_result: [{'x': [0]}] + - note: union built-in + query: union({{0,1},{0}},x) + want_result: [{'x': [1,0]}] + - note: is_number built-in + query: is_number(1,x) + want_result: [{'x': true}] + - note: is_string built-in + query: is_string("a",x) + want_result: [{'x': true}] + - note: is_boolean built-in + query: is_boolean(true,x) + want_result: [{'x': true}] + - note: is_array built-in + query: is_array([],x) + want_result: [{'x': true}] + - note: is_set built-in + query: is_set({"a"},x) + want_result: [{'x': true}] + - note: is_object built-in + query: is_object({},x) + want_result: [{'x': true}] + - note: is_null built-in + query: is_null(null,x) + want_result: [{'x': true}] + - note: type_name built-in + query: type_name(1,x) + want_result: [{'x': "number"}] + - note: bits.or built-in + query: bits.or(1,2,x) + want_result: [{'x': 3}] + - note: bits.and built-in + query: bits.and(5,3,x) + want_result: [{'x': 1}] + - note: bits.negate built-in + query: bits.negate(1,x) + want_result: [{'x': -2}] + - note: bits.xor built-in + query: bits.xor(1,1,x) + want_result: [{'x': 0}] + - note: bits.lsh built-in + query: bits.lsh(1,1,x) + want_result: [{'x': 2}] + - note: bits.rsh built-in + query: bits.rsh(2,1,x) + want_result: [{'x': 1}] + - note: count built-in + query: count([1,2,3],x) + want_result: [{'x': 3}] + - note: count built-in string + query: count("abc", x) + want_result: [{'x': 3}] + - note: count built-in string unicode + query: count("åäö", x) + want_result: [{'x': 3}] + - note: sum built-in + query: sum([1,2,3],x) + want_result: [{'x': 6}] + - note: product built-in + query: product([1,2,3],x) + want_result: [{'x': 6}] + - note: max built-in + query: max([1,2,3],x) + want_result: [{'x': 3}] + - note: min built-in + query: min([3,2,1],x) + want_result: [{'x': 1}] + - note: sort built-in + query: sort(["1","3","2"],x) + want_result: [{'x': ["1","2","3"]}] + - note: all built-in + query: all([true,true],x) + want_result: [{'x': true}] + - note: any built-in + query: any([false,true],x) + want_result: [{'x': true}] + - note: base64.is_valid built-in + query: base64.is_valid("SGVsbG8=",x) + want_result: [{'x': true}] + - note: base64.decode built-in + query: base64.decode("SGVsbG8=",x) + want_result: [{'x': "Hello"}] + - note: base64.encode built-in + query: base64.encode("Hello",x) + want_result: [{'x': "SGVsbG8="}] + - note: base64url.decode built-in + query: base64url.decode("SGVsbG8=",x) + want_result: [{'x': "Hello"}] + - note: base64url.encode built-in + query: base64url.encode("Hello",x) + want_result: [{'x': "SGVsbG8="}] + - note: net.cidr_contains + query: net.cidr_contains("172.17.0.0/24", "172.17.0.0/16", x) + want_result: [{'x': false}] + - note: net.cidr_contains + query: net.cidr_overlap("172.17.0.0/16", "172.17.0.1", x) + want_result: [{'x': true}] + - note: net.cidr_intersects + query: net.cidr_intersects("192.168.1.0/25", "192.168.1.64/25", x) + want_result: [{'x': true}] + - note: glob.match built-in + query: glob.match("*:github:com", [":"], "api:github:com", x) + want_result: [{'x': true}] + - note: glob.match built-in, multiple delimiters + query: glob.match("*.github.com:foo", [".", ":"], "api.github.com:foo", x) + want_result: [{'x': true}] + - note: glob.match built-in delimiters default + query: glob.match("*.github.com", [], "api.github.com", x) + want_result: [{'x': true}] + - note: glob.match built-in delimiters default, negative + query: glob.match("*foo*", [], "5.0 foo/90", x) + want_result: [{'x': false}] + - note: json.marshal built-in + query: json.marshal("string",x) + want_result: [{'x': '"string"'}] + - note: json.unmarshal built-in + query: json.unmarshal("\"string\"",x) + want_result: [{'x': "string"}] + - note: object.get built-in + query: 'object.get({"a": "b"}, "a", "c", x)' + want_result: [{'x': "b"}] + - note: object.get built-in + query: 'object.get({"a": "b"}, "c", "c", x)' + want_result: [{'x': "c"}] + - note: object.remove built-in + query: 'object.remove({"x": 0, "y": 1}, ["y"], x)' + want_result: [{'x': {'x': 0}}] + - note: object.remove built-in + query: 'object.remove({"x": 0, "y": 1}, {"z"}, x)' + want_result: [{'x': {'x': 0, 'y': 1}}] + - note: object.filter built-in + query: 'object.filter({"x": 0, "y": 1}, ["x"], x)' + want_result: [{'x': {'x': 0}}] + - note: object.union built-in + query: 'object.union({"a": 1}, {"b": 2}, x)' + want_result: [{'x': {'a': 1, 'b': 2}}] + - note: object.union built-in + query: 'object.union({"a": 1}, {"a": {"b": {"c": 1}}, "d": 7}, x)' + want_result: [{'x': {"a": {"b": {"c": 1}}, "d": 7}}] + - note: json.remove built-in + query: 'json.remove({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a/b/c"}, x)' + want_result: [{'x': {"a": {"b": {"d": 8}}, "e": 9}}] + - note: json.remove built-in + query: 'json.remove({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a/b/c", "e"}, x)' + want_result: [{'x': {"a": {"b": {"d": 8}}}}] + - note: json.filter built-in + query: 'json.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {"a/b/c"}, x)' + want_result: [{'x': {"a": {"b": {"c": 7}}}}] + - note: json.filter built-in + query: 'json.filter({"a": {"b": {"c": 7, "d": 8}}, "e": 9}, {["a", "b", "c"], ["e"]}, x)' + want_result: [{'x': {"a": {"b": {"c": 7}}, "e": 9}}] + - note: concat built-in + query: concat(",",["a","b"],x) + want_result: [{'x': "a,b"}] + - note: concat built-in unicode + query: concat("ä",["å","ö"],x) + want_result: [{'x': "åäö"}] + - note: contains built-in + query: contains("abc","b",x) + want_result: [{'x': true}] + - note: contains built-in unicode + query: contains("åäö","ä",x) + want_result: [{'x': true}] + - note: endswith built-in + query: endswith("abc","c",x) + want_result: [{'x': true}] + - note: endswith built-in unicode + query: endswith("åäö","ö",x) + want_result: [{'x': true}] + - note: format_int built-in + query: format_int(10,16,x) + want_result: [{'x': "a"}] + - note: indexof built-in + query: indexof("abc","b",x) + want_result: [{'x': 1}] + - note: indexof built-in unicode + query: indexof("åäö","ä",x) + want_result: [{'x': 1}] + - note: lower built-in + query: lower("A",x) + want_result: [{'x': "a"}] + - note: lower built-in unicode + query: lower("Å",x) + want_result: [{'x': "å"}] + - note: replace built-in + query: replace("abc","b","d",x) + want_result: [{'x': "adc"}] + - note: replace built-in unicode + query: replace("åäö","å","ö",x) + want_result: [{'x': "öäö"}] + - note: replace_n built-in + query: strings.replace_n({"b":"d"},"abc",x) + want_result: [{'x': "adc"}] + - note: replace_n built-in unicode + query: strings.replace_n({"b":"ö"},"abc",x) + want_result: [{'x': "aöc"}] + - note: split built-in + query: split("a,b",",",x) + want_result: [{'x': ["a","b"]}] + - note: split built-in unicode + query: split("aöb","ö",x) + want_result: [{'x': ["a","b"]}] + - note: startswith built-in + query: startswith("abc","a",x) + want_result: [{'x': true}] + - note: startswith built-in unicode + query: startswith("åäö","å",x) + want_result: [{'x': true}] + - note: substring built-in + query: substring("abcd",1,2,x) + want_result: [{'x': "bc"}] + - note: substring built-in unicode + query: substring("åäö",1,2,x) + want_result: [ { 'x': "äö" } ] + - note: trim built-in + query: trim("abc","ac",x) + want_result: [{'x': "b"}] + - note: trim built-in unicode + query: trim("åäö","åö",x) + want_result: [{'x': "ä"}] + - note: trim_left built-in + query: trim_left("abc","ba",x) + want_result: [{'x': "c"}] + - note: trim_left built-in unicode + query: trim_left("åäö","äå",x) + want_result: [{'x': "ö"}] + - note: trim_prefix built-in + query: trim_prefix("abc","ab",x) + want_result: [{'x': "c"}] + - note: trim_prefix built-in unicode + query: trim_prefix("åäö","åä",x) + want_result: [{'x': "ö"}] + - note: trim_right built-in + query: trim_right("abc","cb",x) + want_result: [{'x': "a"}] + - note: trim_right built-in unicode + query: trim_right("åäö","öä",x) + want_result: [{'x': "å"}] + - note: trim_suffix built-in + query: trim_suffix("abc","bc",x) + want_result: [{'x': "a"}] + - note: trim_suffix built-in unicode + query: trim_suffix("åäö","äö",x) + want_result: [{'x': "å"}] + - note: trim_space built-in + query: trim_space(" abc ",x) + want_result: [{'x': "abc"}] + - note: trim_space built-in unicode + query: trim_space(" åäö ",x) + want_result: [{'x': "åäö"}] + - note: upper built-in + query: upper("a",x) + want_result: [{'x': "A"}] + - note: upper built-in unicode + query: upper("å",x) + want_result: [{'x': "Å"}] + - note: numbers.range built-in + query: numbers.range(10, 12, x) + want_result: [{'x': [10, 11, 12]}] + - note: to_number + query: to_number("100", x) + want_result: [{'x': 100}] + - note: custom built-in + query: x = custom_builtin_test(100) + want_result: [{'x': 101}] + - note: impure built-in + query: x = custom_builtin_test_impure() + want_result: [{'x': "foo"}] + - note: nested + query: arr = ["foo","bar","baz","qux"]; x = arr[1+1+1] + want_result: [{'arr': ['foo', 'bar', 'baz', 'qux'], 'x': "qux"}] + - note: walk non-empty + query: 'data.test.p = x' + modules: + - | + package test + import rego.v1 + + p if { foo = {"a": {"b": [1]}}; walk(foo) } + want_result: [{x: true}] + - note: walk results + query: 'data.test.p[x]' + modules: + - | + package test + import rego.v1 + + p contains x if { foo = {"a": {"b": [1]}}; walk(foo, x) } + want_result: [ + { + x: [[], {"a": {"b": [1]}}], + }, + { + x: [["a"], {"b": [1]}], + }, + { + x: [["a", "b"], [1]], + }, + { + x: [["a", "b", 0], 1] + }, + ] + - note: walk results pattern matching + query: 'walk(data.test.obj, [["a", x, 1], y])' + modules: + - | + package test + + obj := {"a": {"b": [1, 3], "c": [2, 4]}, "d": {}} + want_result: [ + { + "x": "b", + "y": 3 + }, + { + "x": "c", + "y": 4, + } + ] + - note: graph.reachable empty + query: 'graph.reachable({}, {"a"}, x)' + want_result: [{'x': []}] + - note: graph.reachable cycle + query: 'x := sort(graph.reachable({"a": {"b"}, "b": {"c"}, "c": {"a"}}, {"a"}))' + want_result: [{'x': ["a", "b", "c"]}] + - note: graph.reachable components + query: 'x := sort(graph.reachable({"a": {"b", "c"}, "b": {"d"}, "c": {"d"}, "d": set(), "e": {"f"}, "f": {"e"}, "x": {"x"}}, {"b", "e"}))' + want_result: [{'x': ["b", "d", "e", "f"]}] + - note: regex.is_valid built-in + query: 'regex.is_valid(".*", x)' + want_result: [{'x': true}] + - note: regex.is_valid built-in + query: 'regex.match("foo.*bar", "fooxbar", x)' + want_result: [{'x': true}] + - note: regex.find_all_string_submatch_n built-in, all matches + query: 'regex.find_all_string_submatch_n("foo(.?)", "seefood fool foo", -1, x)' + want_result: [{'x': [["food","d"],["fool","l"],["foo", ""]]}] + - note: regex.find_all_string_submatch_n built-in, one match only + query: 'regex.find_all_string_submatch_n("foo(.?)", "seefood fool foo", 1, x)' + want_result: [{'x': [["food","d"]]}] + - note: regex.find_all_string_submatch_n built-in, two matches only + query: 'regex.find_all_string_submatch_n("foo(.?)", "seefood fool foo", 2, x)' + want_result: [{'x': [["food","d"],["fool","l"]]}] + - note: regex.find_all_string_submatch_n built-in, more matches than there are + query: 'regex.find_all_string_submatch_n("foo(.?)", "seefood fool foo", 10, x)' + want_result: [{'x': [["food","d"],["fool","l"],["foo", ""]]}] diff --git a/third_party/opa/v1/test/wasm/assets/019_call_indirect_optimization.yaml b/third_party/opa/v1/test/wasm/assets/019_call_indirect_optimization.yaml new file mode 100644 index 000000000000..01519ef950fa --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/019_call_indirect_optimization.yaml @@ -0,0 +1,203 @@ +cases: + - note: non_opt + query: | + z := data.foo.q + modules: + - | + package foo + q = 1 + want_result: + - z: 1 + - note: simplest + query: | + x := "q"; z := data.foo[x] + modules: + - | + package foo + q = 1 + want_result: + - x: q + z: 1 + - note: two packages, one irrelevant + query: | + x := "q"; z := data.foo[x] + modules: + - | + package foo + q = 1 + - | + package bar + q = 2 + want_result: + - x: q + z: 1 + - note: more layers + query: | + x := "aaa"; u := "ccc"; z := "q"; w := data.foo[x].bar[u].baz[z] + modules: + - | + package foo.aaa.bar.ccc.baz + q = 1 + - | + package foo.aaa.bar.ddd.baz + q = 2 + want_result: + - x: aaa + u: ccc + w: 1 + z: q + - note: leftover ref when optimization planned + query: | + x := "aaa"; z := "q"; w := data.foo[x].bar[z].foo.baz + modules: + - | + package foo.aaa.bar + q = { + "foo": { + "baz": 100 + } + } + want_result: + - x: aaa + z: q + w: 100 + - note: lookup involving 'with' + query: | + x := "q"; a := "bar"; z := data.foo[a][x] with data.foo.baz as 200 + modules: + - | + package foo.bar + q = 1 + want_result: + - x: q + a: bar + z: 1 + - note: memoization + query: | + x := "q"; z := data.foo[x]; w := data.foo[x] + modules: + - | + package foo + import rego.v1 + + q = 1 if { + custom_builtin_test_memoization() == 100 + } + want_result: + - x: q + z: 1 + w: 1 + - note: data ref used after failed lookup + query: data.foo.p = x + modules: + - | + package foo + import rego.v1 + + p = y if { + k := "q" + y := data.bar[k].baz + } + - | + package bar + p = { "baz": 100 } + data: + bar: + q: + baz: 8 + want_result: + - x: 8 + - note: data ref not used after successful lookup + query: data.foo.p = x + modules: + - | + package foo + import rego.v1 + + p = y if { + k := "p" + y := data.bar[k].baz + } + - | + package bar + p = { "baz": 100 } + data: + bar: + q: + baz: 8 + want_result: + - x: 100 + - note: func lookup unsuccessful, data deref unsuccessful + query: data.test.p = x + modules: + - | + package test + import rego.v1 + + p if { + x := "foo" + data.other[x].p + } + - | + package other.bar # not "foo", but existence triggers dynamic lookup + + p = true + data: {} + want_defined: false + - note: func lookup successful, but call yields undefined + query: data.test.p = x + modules: + - | + package test + import rego.v1 + + p if { + x := "foo" + data.other[x].p + } + - package other.foo # empty + - | + package other.bar # not foo + + p = true + data: {} + want_defined: false + - note: func lookup successful, but call yields undefined (in a loop) + query: data.test.p = x + modules: + - | + package test + import rego.v1 + + xs := {"foo", "baz", "123"} + p if { + xs[x] + data.other[x].p + } + - package other.foo # empty + - | + package other.bar # not foo + + p = true + data: {} + want_defined: false + - note: func lookup in a loop, one call yields a result + query: data.test.p = x + modules: + - | + package test + import rego.v1 + + xs := {"fox", "baz", "123", "xyz"} + p = y if { + xs[x] + y := data.other[x].p + } + - package other.foo # empty + - | + package other.xyz + + p = "yay" + data: {} + want_result: + - x: yay diff --git a/third_party/opa/v1/test/wasm/assets/test.js b/third_party/opa/v1/test/wasm/assets/test.js new file mode 100644 index 000000000000..dfa6aef68615 --- /dev/null +++ b/third_party/opa/v1/test/wasm/assets/test.js @@ -0,0 +1,389 @@ +const assert = require('assert'); + +const { readFileSync, readdirSync } = require('fs'); + +function stringDecoder(mem) { + return function (addr) { + const i8 = new Int8Array(mem.buffer); + var s = ""; + while (i8[addr] != 0) { + s += String.fromCharCode(i8[addr++]); + } + return s; + } +} + +function red(text) { + return '\x1b[0m\x1b[31m' + text + '\x1b[0m'; +} + +function green(text) { + return '\x1b[0m\x1b[32m' + text + '\x1b[0m'; +} + +function yellow(text) { + return '\x1b[0m\x1b[33m' + text + '\x1b[0m'; +} + +const PASS = 'PASS'; +const FAIL = 'FAIL'; +const ERROR = 'ERROR'; + +function report(state, name, msg, extra) { + if (state === PASS) { + if (process.env.VERBOSE === '1') { + console.log(green('PASS'), name); + return true; + } + return false; + } else if (state === FAIL) { + console.log(yellow('FAIL'), name + ':', msg); + } else { + console.log(red('ERROR'), name + ':', msg); + } + if (extra !== '') { + console.log(extra); + } + return true +} + +function skip(name, msg) { + if (process.env.VERBOSE === '1') { + console.log(yellow('SKIP'), name + ':', msg); + } +} + +function now() { + const [sec, nsec] = process.hrtime(); + return (sec * 1000 * 1000) + (nsec / 1000); +} + +function formatMicros(us) { + if (us <= 1000) { + return us + 'µs' + } else if (us <= 1000 * 1000) { + return (us / 1000).toFixed(4) + 'ms' + } else { + return (us / (1000 * 1000)).toFixed(4) + 's' + } +} + +function loadJSON(mod, value) { + + if (value === undefined) { + return 0; + } + + const str = JSON.stringify(value); + const rawAddr = mod.instance.exports.opa_malloc(str.length); + const buf = new Uint8Array(mod.instance.exports.memory.buffer); + + for (let i = 0; i < str.length; i++) { + buf[rawAddr + i] = str.charCodeAt(i); + } + + const parsedAddr = mod.instance.exports.opa_json_parse(rawAddr, str.length); + + if (parsedAddr == 0) { + throw "failed to parse json value"; + } + + return parsedAddr; +} + +function dumpJSON(mod, addr) { + const rawAddr = mod.instance.exports.opa_json_dump(addr); + return parseJSON(mod.instance.exports.memory, rawAddr); +} + +function parseJSON(memory, rawAddr) { + const buf = new Uint8Array(memory.buffer); + const idx = rawAddr + buf.slice(rawAddr).findIndex((elem) => elem === 0); + // TODO(sr): use TextDecoder and friends + return JSON.parse(decodeURIComponent(escape(String.fromCharCode.apply(null, buf.slice(rawAddr, idx))))); +} + +function builtinCustomTest(a) { + return a + 1; +} + +function builtinCustomTestImpure() { + return "foo"; +} + +var run = false; + +function builtinCustomTestMemoization() { + if (run) { + throw "should have been memoized"; + } + run = true + return 100; +} + +const builtinFuncs = { + custom_builtin_test: builtinCustomTest, + custom_builtin_test_impure: builtinCustomTestImpure, + custom_builtin_test_memoization: builtinCustomTestMemoization, +} + +// builtinCall dispatches the built-in function. Arguments are deserialized from +// JSON into JavaScript values and the result is serialized for passing back +// into Wasm. +function builtinCall(policy, func) { + + const impl = builtinFuncs[policy.builtins[func]]; + + if (impl === undefined) { + throw { message: "not implemented: built-in " + func + ": " + policy.builtins[func] } + } + + var argArray = Array.prototype.slice.apply(arguments); + let args = []; + + for (let i = 2; i < argArray.length; i++) { + const jsArg = dumpJSON(policy.module, argArray[i]); + args.push(jsArg); + } + + const result = impl(...args); + + return loadJSON(policy.module, result); +} + +async function instantiate(bytes, data) { + + const memory = new WebAssembly.Memory({initial: 5}); + let addr2string = () => console.warn("cannot call addr2string from Start function"); + const policy = {}; + + policy.module = await WebAssembly.instantiate(bytes, { + env: { + memory, + opa_abort: (addr) => { + throw { message: addr2string(addr) }; + }, + opa_println: (addr) => { + console.log(addr2string(addr)); + }, + opa_builtin0: (func, _ctx) => builtinCall(policy, func), + opa_builtin1: (func, _ctx, v1) => builtinCall(policy, func, v1), + opa_builtin2: (func, _ctx, v1, v2) => builtinCall(policy, func, v1, v2), + opa_builtin3: (func, _ctx, v1, v2, v3) => builtinCall(policy, func, v1, v2, v3), + opa_builtin4: (func, _ctx, v1, v2, v3, v4) => builtinCall(policy, func, v1, v2, v3, v4), + }, + }); + + addr2string = stringDecoder(policy.module.instance.exports.memory); + + builtins = dumpJSON(policy.module, policy.module.instance.exports.builtins()); + policy.builtins = {}; + + for (var key of Object.keys(builtins)) { + policy.builtins[builtins[key]] = key + } + + policy.dataAddr = loadJSON(policy.module, data); + policy.heapPtr = policy.module.instance.exports.opa_heap_ptr_get(); + + return policy; +} + +function evaluate(policy, input) { + + let inputLen = 0; + let inputAddr = 0; + if (input) { + const inp = JSON.stringify(input); + const buf = new Uint8Array(policy.module.instance.exports.memory.buffer); + inputAddr = policy.heapPtr; + inputLen = inp.length; + + for (let i = 0; i < inputLen; i++) { + buf[inputAddr + i] = inp.charCodeAt(i); + } + policy.heapPtr = inputAddr + inputLen; + } + + const addr = policy.module.instance.exports.opa_eval( + 0, // reserved + 0, // entrypoint + policy.dataAddr, + inputAddr, + inputLen, + policy.heapPtr, + 0, // json output + ); + + return { addr }; +} + +function namespace(cache, key) { + if (key in cache) { + cache[key] += 1; + return key + ' (' + cache[key] + ')' + } else { + cache[key] = 0; + return key; + } +} + +async function test() { + + const t0 = now(); + var testCases = []; + const files = readdirSync('.'); + let numFiles = 0; + + files.forEach(file => { + if (file.endsWith('.json')) { + numFiles++; + const testFile = JSON.parse(readFileSync(file)); + if (Array.isArray(testFile.cases)) { + testFile.cases.forEach(testCase => { + testCase.note = file + ': ' + testCase.note; + if (testCase.wasm !== undefined) { + testCase.wasmBytes = Buffer.from(testCase.wasm, 'base64'); + } + testCases.push(testCase); + }); + } + } + }) + + const t_load = now(); + const dt_load = t_load - t0; + console.log(`Found ${testCases.length} WASM test cases in ${numFiles} file(s). Took ${formatMicros(dt_load)}. Running now.\n`); + + let numSkipped = 0; + let numPassed = 0; + let numFailed = 0; + let numErrors = 0; + let dirty = false; + let cache = {}; + + for (let i = 0; i < testCases.length; i++) { + + let state = 'FAIL'; + let name = namespace(cache, testCases[i].note); + + if (testCases[i].skip === true) { + skip(name, testCases[i].skip_reason); + numSkipped++; + continue + } + + let msg = ''; + let extra = ''; + + try { + const policy = await instantiate(testCases[i].wasmBytes, testCases[i].data); + const result = evaluate(policy, testCases[i].input); + + const expDefined = testCases[i].want_defined; + const rs = parseJSON(policy.module.instance.exports.memory, result.addr); + + if (expDefined !== undefined) { + const len = rs.length + if (expDefined) { + if (len > 0) { + state = PASS; + } else { + msg = 'expected non-empty/defined result'; + } + } else { + if (len == 0) { + state = PASS; + } else { + msg = 'expected empty/undefined result'; + } + } + } + + const expResultSet = testCases[i].want_result; + + if (expResultSet !== undefined) { + + // Note: Resultset ordering does not matter. + if (rs.length === expResultSet.length) { + let found = 0 + expResultSet.forEach(expResult => { + for (let i = 0; i < rs.length; i++) { + try { + assert.deepStrictEqual(expResult, rs[i], "didn't match") + found++ + break + } catch (e) { + // Ignore the error + } + } + }) + if (expResultSet.length === found) { + state = PASS; + } + } + + if (state !== PASS) { + msg = 'unexpected result'; + extra = '\twant: ' + JSON.stringify(expResultSet) + '\n\tgot : ' + JSON.stringify(rs); + } + } + + } catch (e) { + const exp = testCases[i].want_error; + if (exp !== undefined && exp.length !== 0) { + if (e.message.includes(exp)) { + state = PASS; + } else { + state = ERROR; + msg = 'want: ' + yellow(exp) + ' but got: ' + red(e.message); + } + } else { + state = ERROR; + msg = e; + } + } + + if (state == PASS) { + numPassed++; + } else if (state === FAIL) { + numFailed++; + } else { + numErrors++; + } + + dirty = report(state, name, msg, extra) || dirty; + } + + const t_end = now(); + const dt_end = t_end - t_load; + + if (dirty) { + console.log(); + } + + console.log('SUMMARY:'); + console.log('--------'); + console.log('PASS:', numPassed + '/' + testCases.length); + + if (numFailed > 0) { + console.log('FAIL:', numFailed + '/' + testCases.length); + } + + if (numSkipped > 0) { + console.log('SKIP:', numSkipped + '/' + testCases.length); + } + + if (numErrors > 0) { + console.log('ERROR:', numErrors + '/' + testCases.length); + } + + console.log(); + console.log('TOOK:', formatMicros(dt_end)); + + if ((numFailed + numErrors) > 0) { + process.exit(1); + } +} + +test(); diff --git a/third_party/opa/v1/test/wasm/cmd/wasm-rego-testgen/main.go b/third_party/opa/v1/test/wasm/cmd/wasm-rego-testgen/main.go new file mode 100644 index 000000000000..ad694f1c5f98 --- /dev/null +++ b/third_party/opa/v1/test/wasm/cmd/wasm-rego-testgen/main.go @@ -0,0 +1,254 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package main + +import ( + "archive/tar" + "compress/gzip" + "context" + "encoding/json" + "fmt" + "io" + "os" + "path" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/test/cases" + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +type params struct { + Output string + InputDir string + TestFilePatterns []string + TestRunner string +} + +type compiledTestCaseSet struct { + Cases []compiledTestCase `json:"cases"` +} + +type compiledTestCase struct { + cases.TestCase + WASM []byte `json:"wasm,omitempty"` +} + +func compileTestCases(ctx context.Context, tests cases.Set) (*compiledTestCaseSet, error) { + result := make([]compiledTestCase, 0, len(tests.Cases)) + for _, tc := range tests.Cases { //nolint:gocritic + + var numExpects int + + if tc.WantDefined != nil { + numExpects++ + } + + if tc.WantError != nil { + numExpects++ + } + + if tc.WantResult != nil { + numExpects++ + } + + if numExpects != 1 { + return nil, fmt.Errorf("test case %v: must specify exactly one expectation (e.g., want_defined) but got %v", tc.Note, numExpects) + } + + args := []func(*rego.Rego){ + rego.Query(tc.Query), + rego.FunctionDecl(®o.Function{ + Name: "custom_builtin_test", + Decl: types.NewFunction( + []types.Type{types.N}, + types.N, + ), + }), + rego.FunctionDecl(®o.Function{ + Name: "custom_builtin_test_impure", + Decl: types.NewFunction( + []types.Type{}, + types.N, + ), + }), + rego.FunctionDecl(®o.Function{ + Name: "custom_builtin_test_memoization", + Decl: types.NewFunction( + []types.Type{}, + types.N, + ), + }), + } + + for idx, module := range tc.Modules { + args = append(args, rego.Module(fmt.Sprintf("module%d.rego", idx), module)) + } + + var bs []byte + + cr, err := rego.New(args...).Compile(ctx) + if err != nil { + return nil, err + } + + bs = cr.Bytes + + result = append(result, compiledTestCase{ + TestCase: tc, + WASM: bs, + }) + } + + return &compiledTestCaseSet{Cases: result}, nil +} + +func pathMatchesAny(patterns []string, p string) (bool, error) { + for i := range patterns { + if ok, err := path.Match(patterns[i], p); err != nil { + return false, err + } else if ok { + return true, nil + } + } + return false, nil +} + +func run(params params) error { + + ctx := context.Background() + + if err := os.MkdirAll(path.Dir(params.Output), 0755); err != nil { + return err + } + + f, err := os.Create(params.Output) + if err != nil { + return err + } + + defer f.Close() + + gw := gzip.NewWriter(f) + defer gw.Close() + tw := tar.NewWriter(gw) + defer tw.Close() + + files, err := os.ReadDir(params.InputDir) + if err != nil { + return err + } + + for i := range files { + if ok, err := pathMatchesAny(params.TestFilePatterns, files[i].Name()); ok { + err := func() error { + abspath := filepath.Join(params.InputDir, files[i].Name()) + + bs, err := os.ReadFile(abspath) + if err != nil { + return err + } + + var tcs cases.Set + + if err := util.Unmarshal(bs, &tcs); err != nil { + return err + } + + ctcs, err := compileTestCases(ctx, tcs) + if err != nil { + return err + } + + bs, err = json.Marshal(ctcs) + if err != nil { + return err + } + + dst := strings.ReplaceAll(files[i].Name(), ".yaml", ".json") + return writeFile(tw, dst, bs) + }() + if err != nil { + return fmt.Errorf("%s: %w", files[i].Name(), err) + } + } else if err != nil { + return fmt.Errorf("%s: %w", files[i].Name(), err) + } + } + + return copyFile(tw, "test.js", params.TestRunner) +} + +func writeFile(tw *tar.Writer, dst string, bs []byte) error { + hdr := &tar.Header{ + Name: strings.TrimLeft(dst, "/"), + Mode: 0600, + Typeflag: tar.TypeReg, + Size: int64(len(bs)), + } + + if err := tw.WriteHeader(hdr); err != nil { + return err + } + + _, err := tw.Write(bs) + return err +} + +func copyFile(tw *tar.Writer, dst, src string) error { + in, err := os.Open(src) + if err != nil { + return err + } + + defer in.Close() + + info, err := os.Stat(src) + if err != nil { + return err + } + + hdr := &tar.Header{ + Name: strings.TrimLeft(dst, "/"), + Mode: 0600, + Typeflag: tar.TypeReg, + Size: info.Size(), + } + + if err := tw.WriteHeader(hdr); err != nil { + return err + } + + _, err = io.Copy(tw, in) + return err + +} + +func main() { + + var params params + executable := path.Base(os.Args[0]) + + command := &cobra.Command{ + Use: executable, + Short: executable, + RunE: func(_ *cobra.Command, _ []string) error { + return run(params) + }, + } + + command.Flags().StringVarP(¶ms.Output, "output", "", "", "set path of output file") + command.Flags().StringVarP(¶ms.InputDir, "input-dir", "", "", "set path of input directory containing test files") + command.Flags().StringSliceVarP(¶ms.TestFilePatterns, "file-pattern", "", []string{"*.yaml", "*.json"}, "set filename patterns to match test files against") + command.Flags().StringVarP(¶ms.TestRunner, "runner", "", "", "set path of test runner") + + if err := command.Execute(); err != nil { + os.Exit(1) + } +} diff --git a/third_party/opa/v1/tester/fixture_test.go b/third_party/opa/v1/tester/fixture_test.go new file mode 100644 index 000000000000..e4bbfb4ec617 --- /dev/null +++ b/third_party/opa/v1/tester/fixture_test.go @@ -0,0 +1,63 @@ +package tester + +const fixtureReporterVerboseBenchmark = `FAILURES +-------------------------------------------------------------------------------- +data.foo.bar.test_corge: FAIL (0s) + + query:1 | Fail true = false + +data.foo.bar.test_cases_fail: FAIL (0s) + + query:1 | Fail true = false + + two: FAIL + +SUMMARY +-------------------------------------------------------------------------------- +data.foo.bar.test_baz 1000 123.0 ns/op +data.foo.bar.test_qux: ERROR (0s) + some err +data.foo.bar.test_corge: FAIL (0s) +data.foo.bar.test_cases_fail: FAIL (0s) + one: PASS + two: FAIL +data.foo.bar.test_cases_ok 2000 61.50 ns/op +-------------------------------------------------------------------------------- +PASS: 4/7 +FAIL: 2/7 +ERROR: 1/7 +` + +const fixtureReporterVerboseBenchmarkShowAllocations = `FAILURES +-------------------------------------------------------------------------------- +data.foo.bar.test_corge: FAIL (0s) + + +SUMMARY +-------------------------------------------------------------------------------- +data.foo.bar.test_baz 1000 123.0 ns/op 123.0 timer_rego_query_eval_ns/op 91 B/op 0 allocs/op +data.foo.bar.test_qux: ERROR (0s) + some err +data.foo.bar.test_corge: FAIL (0s) +-------------------------------------------------------------------------------- +PASS: 1/3 +FAIL: 1/3 +ERROR: 1/3 +` + +const fixtureReporterVerboseBenchmarkShowAllocationsGoBenchFormat = `FAILURES +-------------------------------------------------------------------------------- +data.foo.bar.test_corge: FAIL (0s) + + +SUMMARY +-------------------------------------------------------------------------------- +BenchmarkDataFooBarTestBaz 1000 123.0 ns/op 123.0 timer_rego_query_eval_ns/op 91 B/op 0 allocs/op +data.foo.bar.test_qux: ERROR (0s) + some err +data.foo.bar.test_corge: FAIL (0s) +-------------------------------------------------------------------------------- +PASS: 1/3 +FAIL: 1/3 +ERROR: 1/3 +` diff --git a/third_party/opa/v1/tester/reporter.go b/third_party/opa/v1/tester/reporter.go new file mode 100644 index 000000000000..cdfd682afc32 --- /dev/null +++ b/third_party/opa/v1/tester/reporter.go @@ -0,0 +1,363 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package tester + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/cover" + "github.com/open-policy-agent/opa/v1/topdown" +) + +// Reporter defines the interface for reporting test results. +type Reporter interface { + + // Report is called with a channel that will contain test results. + Report(chan *Result) error +} + +// PrettyReporter reports test results in a simple human readable format. +type PrettyReporter struct { + Output io.Writer + Verbose bool + FailureLine bool + LocalVars bool + BenchmarkResults bool + BenchMarkShowAllocations bool + BenchMarkGoBenchFormat bool +} + +func (r PrettyReporter) println(a ...any) { + _, _ = fmt.Fprintln(r.Output, a...) +} + +// Report prints the test report to the reporter's output. +func (r PrettyReporter) Report(ch chan *Result) error { + + dirty := false + var pass, fail, skip, errs int + results := make([]*Result, 0, len(ch)) + var failures []*Result + + for tr := range ch { + if tr.Skip { + skip++ + } else if tr.Error != nil { + errs++ + } else { + if tr.Fail { + failures = append(failures, tr) + } + + if len(tr.SubResults) > 0 { + for _, sr := range tr.SubResults.Iter { + if len(sr.SubResults) == 0 { + // Only count leaf results + if sr.Fail { + fail++ + } else { + pass++ + } + } + } + } else { + if tr.Pass() { + pass++ + } else if tr.Fail { + fail++ + } + } + } + results = append(results, tr) + } + + if fail > 0 && (r.Verbose || r.FailureLine) { + r.println("FAILURES") + r.hl() + + for _, failure := range failures { + _, _ = fmt.Fprint(r.Output, failure.string(false)) + r.println() + + if len(failure.SubResults) > 0 { + // Print trace collectively for all sub-results. + if err := printFailure(r.Output, failure.Trace, r.Verbose, false, r.LocalVars); err != nil { + return err + } + + if r.Verbose || r.FailureLine { + r.println() + } + + for fullName, sr := range failure.SubResults.Iter { + w := newIndentingWriter(r.Output) + + if sr.Fail { + if len(sr.SubResults) == 0 { + // Print full test-case lineage for every failed leaf sub-result for readability. + for _, n := range fullName { + _, _ = fmt.Fprintf(w, "%s: %s\n", n, sr.outcome()) + w = newIndentingWriter(w) + } + + if err := printFailure(w, sr.Trace, false, r.FailureLine, r.LocalVars); err != nil { + return err + } + } + } + } + } else { + if err := printFailure(r.Output, failure.Trace, r.Verbose, r.FailureLine, r.LocalVars); err != nil { + return err + } + } + + r.println() + } + + r.println("SUMMARY") + r.hl() + } + + // Report individual tests. + var lastFile string + for _, tr := range results { + + if tr.Pass() && r.BenchmarkResults { + dirty = true + r.println(r.fmtBenchmark(tr)) + } else if r.Verbose || !tr.Pass() { + if tr.Location != nil && tr.Location.File != lastFile { + if lastFile != "" { + r.println("") + } + _, _ = fmt.Fprintf(r.Output, "%s:\n", tr.Location.File) + lastFile = tr.Location.File + } + + dirty = true + r.println(tr.string(false)) + + w := newIndentingWriter(r.Output) + if srs := tr.SubResults; len(srs) > 0 { + for fullName, sr := range srs.Iter { + if sr.Fail || r.Verbose { + _, _ = fmt.Fprintf(w, "%s%s\n", + strings.Repeat(" ", len(fullName)-1), + sr.String(), + ) + } + } + } + + if len(tr.Output) > 0 { + r.println() + _, _ = fmt.Fprintln(newIndentingWriter(r.Output), strings.TrimSpace(string(tr.Output))) + r.println() + } + } + if tr.Error != nil { + _, _ = fmt.Fprintf(r.Output, " %v\n", tr.Error) + } + } + + // Report summary of test. + if dirty { + r.hl() + } + + total := pass + fail + skip + errs + + if pass != 0 { + r.println("PASS:", fmt.Sprintf("%d/%d", pass, total)) + } + + if fail != 0 { + r.println("FAIL:", fmt.Sprintf("%d/%d", fail, total)) + } + + if skip != 0 { + r.println("SKIPPED:", fmt.Sprintf("%d/%d", skip, total)) + } + + if errs != 0 { + r.println("ERROR:", fmt.Sprintf("%d/%d", errs, total)) + } + + return nil +} + +func printFailure(w io.Writer, trace []*topdown.Event, verbose bool, failureLine bool, localVars bool) error { + if verbose { + _, _ = fmt.Fprintln(w) + topdown.PrettyTraceWithOpts(newIndentingWriter(w), trace, topdown.PrettyTraceOptions{ + Locations: true, + ExprVariables: localVars, + }) + } + + if failureLine { + _, _ = fmt.Fprintln(w) + for i := len(trace) - 1; i >= 0; i-- { + e := trace[i] + if e.Op == topdown.FailOp && e.Location != nil && e.QueryID != 0 { + if expr, isExpr := e.Node.(*ast.Expr); isExpr { + if _, isEvery := expr.Terms.(*ast.Every); isEvery { + // We're interested in the failing expression inside the every body. + continue + } + } + _, _ = fmt.Fprintf(newIndentingWriter(w), "%s:%d:\n", e.Location.File, e.Location.Row) + if err := topdown.PrettyEvent(newIndentingWriter(w, 4), e, topdown.PrettyEventOpts{PrettyVars: localVars}); err != nil { + return err + } + _, _ = fmt.Fprintln(w) + break + } + } + } + + return nil +} + +func (r PrettyReporter) hl() { + fmt.Fprintln(r.Output, strings.Repeat("-", 80)) +} + +func (r PrettyReporter) fmtBenchmark(tr *Result) string { + if tr.BenchmarkResult == nil { + return "" + } + name := fmt.Sprintf("%v.%v", tr.Package, tr.Name) + if r.BenchMarkGoBenchFormat { + // The Golang benchmark data format requires the line start with "Benchmark" and then + // the next letter needs to be capitalized. + // https://go.googlesource.com/proposal/+/master/design/14313-benchmark-format.md + // + // This converts the test case name like data.foo.bar.test_auth to be more + // like BenchmarkDataFooBarTestAuth. + camelCaseName := "" + for _, part := range strings.Split(strings.ReplaceAll(name, "_", "."), ".") { + camelCaseName += strings.Title(part) //nolint:staticcheck // SA1019, no unicode here + } + name = "Benchmark" + camelCaseName + } + + result := fmt.Sprintf("%s\t%s", name, tr.BenchmarkResult.String()) + if r.BenchMarkShowAllocations { + result += "\t" + tr.BenchmarkResult.MemString() + } + + return result +} + +// JSONReporter reports test results as array of JSON objects. +type JSONReporter struct { + Output io.Writer +} + +// Report prints the test report to the reporter's output. +func (r JSONReporter) Report(ch chan *Result) error { + report := make([]*Result, 0, len(ch)) + for tr := range ch { + report = append(report, tr) + } + + bs, err := json.MarshalIndent(report, "", " ") + if err != nil { + return err + } + fmt.Fprintln(r.Output, string(bs)) + return nil +} + +// JSONCoverageReporter reports coverage as a JSON structure. +type JSONCoverageReporter struct { + Cover *cover.Cover + Modules map[string]*ast.Module + Output io.Writer + Threshold float64 + Verbose bool +} + +// Report prints the test report to the reporter's output. If any tests fail or +// encounter errors, this function returns an error. +func (r JSONCoverageReporter) Report(ch chan *Result) error { + for tr := range ch { + if !tr.Pass() { + if tr.Error != nil { + return tr.Error + } + return errors.New(tr.String()) + } + } + report := r.Cover.Report(r.Modules) + + if report.Coverage < r.Threshold { + err := cover.CoverageThresholdError{ + Coverage: report.Coverage, + Threshold: r.Threshold, + } + + if r.Verbose { + err.Report = &report + } + + return &err + } + + encoder := json.NewEncoder(r.Output) + encoder.SetIndent("", " ") + return encoder.Encode(report) +} + +type indentingWriter struct { + w io.Writer + indent int +} + +func newIndentingWriter(w io.Writer, indent ...int) indentingWriter { + i := 2 + if len(indent) > 0 { + i = indent[0] + } + + if iw, ok := w.(indentingWriter); ok { + i += iw.indent + w = iw.w + } + + return indentingWriter{ + w: w, + indent: i, + } +} + +func (w indentingWriter) Write(bs []byte) (int, error) { + var written int + // insert indentation at the start of every line. + indent := true + for _, b := range bs { + if indent { + wrote, err := w.w.Write([]byte(strings.Repeat(" ", w.indent))) + if err != nil { + return written, err + } + written += wrote + } + wrote, err := w.w.Write([]byte{b}) + if err != nil { + return written, err + } + written += wrote + indent = b == '\n' + } + return written, nil +} diff --git a/third_party/opa/v1/tester/reporter_test.go b/third_party/opa/v1/tester/reporter_test.go new file mode 100644 index 000000000000..a96e4c6322e7 --- /dev/null +++ b/third_party/opa/v1/tester/reporter_test.go @@ -0,0 +1,1494 @@ +package tester + +import ( + "bytes" + "errors" + "reflect" + "strconv" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/util" +) + +func getFakeTraceEvents() []*topdown.Event { + return getFakeTraceEventsFor(ast.MustParseExpr("true = false")) +} + +func getFakeTraceEventsFor(node ast.Node, modifiers ...func(e *topdown.Event)) []*topdown.Event { + es := []*topdown.Event{ + { + Op: topdown.FailOp, + Node: node, + Location: node.Loc(), + QueryID: 0, + ParentID: 0, + }, + } + + for _, modifier := range modifiers { + modifier(es[0]) + } + + return es +} + +func TestPrettyReporterVerbose(t *testing.T) { + var buf bytes.Buffer + + // supply fake trace events for each kind of event to ensure that only failures + // report traces. + ts := []*Result{ + { + Package: "data.foo.bar", + Name: "test_baz", + Trace: getFakeTraceEvents(), + Location: &ast.Location{ + File: "policy1.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "test_qux", + Error: errors.New("some err"), + Trace: getFakeTraceEvents(), + Location: &ast.Location{ + File: "policy1.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "test_corge", + Fail: true, + Trace: getFakeTraceEvents(), + Location: &ast.Location{ + File: "policy2.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "todo_test_qux", + Skip: true, + Trace: nil, + Location: &ast.Location{ + File: "policy2.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "test_contains_print", + Output: []byte("fake print output\n"), + Location: &ast.Location{ + File: "policy3.rego", + }, + }, + { + Package: "data.foo.baz", + Name: "p.q.r.test_quz", + Trace: getFakeTraceEvents(), + Location: &ast.Location{ + File: "policy4.rego", + }, + }, + { + Package: "data.foo.qux", + Name: "test_cases", + Trace: getFakeTraceEvents(), + Fail: true, + // Will be sorted to "bar", "baz", "foo" in output for stability + SubResults: SubResultMap{ + "foo": { + Name: "foo", + Fail: false, + }, + "bar": { + Name: "bar", + Fail: true, + }, + "baz": { + Name: "baz", + Fail: false, + }, + }, + Location: &ast.Location{ + File: "policy5.rego", + }, + }, + { + Package: "data.foo.qux", + Name: "test_cases_nested", + Trace: getFakeTraceEvents(), + Fail: true, + SubResults: SubResultMap{ + "one": { + Name: "one", + Fail: false, + SubResults: SubResultMap{ + "foo": { + Name: "foo", + Fail: false, + }, + "bar": { + Name: "bar", + Fail: false, + }, + }, + }, + "two": { + Name: "two", + Fail: true, + SubResults: SubResultMap{ + "foo": { + Name: "foo", + Fail: true, + }, + "bar": { + Name: "bar", + Fail: false, + }, + }, + }, + }, + Location: &ast.Location{ + File: "policy5.rego", + }, + }, + } + + r := PrettyReporter{ + Output: &buf, + Verbose: true, + } + + ch := resultsChan(ts) + if err := r.Report(ch); err != nil { + t.Fatal(err) + } + + exp := `FAILURES +-------------------------------------------------------------------------------- +data.foo.bar.test_corge: FAIL (0s) + + query:1 | Fail true = false + +data.foo.qux.test_cases: FAIL (0s) + + query:1 | Fail true = false + + bar: FAIL + +data.foo.qux.test_cases_nested: FAIL (0s) + + query:1 | Fail true = false + + two: FAIL + foo: FAIL + +SUMMARY +-------------------------------------------------------------------------------- +policy1.rego: +data.foo.bar.test_baz: PASS (0s) +data.foo.bar.test_qux: ERROR (0s) + some err + +policy2.rego: +data.foo.bar.test_corge: FAIL (0s) +data.foo.bar.todo_test_qux: SKIPPED + +policy3.rego: +data.foo.bar.test_contains_print: PASS (0s) + + fake print output + + +policy4.rego: +data.foo.baz.p.q.r.test_quz: PASS (0s) + +policy5.rego: +data.foo.qux.test_cases: FAIL (0s) + bar: FAIL + baz: PASS + foo: PASS +data.foo.qux.test_cases_nested: FAIL (0s) + one: PASS + bar: PASS + foo: PASS + two: FAIL + bar: PASS + foo: FAIL +-------------------------------------------------------------------------------- +PASS: 8/13 +FAIL: 3/13 +SKIPPED: 1/13 +ERROR: 1/13 +` + + str := buf.String() + + if exp != str { + t.Fatalf("Expected (%d bytes):\n\n%v\n\nGot (%d bytes):\n\n%v", len(exp), exp, len(str), str) + } +} + +func TestPrettyReporterFailureLine(t *testing.T) { + var buf bytes.Buffer + + // supply fake trace events to verify that traces are suppressed without verbose + // flag. + ts := []*Result{ + { + Package: "data.foo.bar", + Name: "test_baz", + Trace: getFakeTraceEvents(), + Location: &ast.Location{ + File: "policy1.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "test_qux", + Error: errors.New("some err"), + Trace: getFakeTraceEvents(), + Location: &ast.Location{ + File: "policy1.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "test_corge", + Fail: true, + Trace: getFakeTraceEventsFor( + ast.MustParseExpr("x == y + z"), + func(e *topdown.Event) { + // QueryID == 0 is not pretty-printed, as this is the base query to eval the test rule; not the test rule itself. + e.QueryID = 1 + }, + func(e *topdown.Event) { + e.Location.File = "policy1.rego" + e.Location.Row = 5 + }, + func(e *topdown.Event) { + e.Locals = ast.NewValueMap() + e.Locals.Put(ast.Var("x"), ast.Number("1")) + e.Locals.Put(ast.Var("y"), ast.Number("2")) + e.Locals.Put(ast.Var("z"), ast.Number("3")) + }, + func(e *topdown.Event) { + e.LocalMetadata = map[ast.Var]topdown.VarMetadata{ + "x": {Name: "x"}, + "y": {Name: "y"}, + "z": {Name: "z"}, + } + }), + Location: &ast.Location{ + File: "policy1.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "todo_test_qux", + Skip: true, + Trace: nil, + Location: &ast.Location{ + File: "policy1.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "test_contains_print_pass", + Output: []byte("fake print output\n"), + Location: &ast.Location{ + File: "policy1.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "test_contains_print_fail", + Fail: true, + Output: []byte("fake print output2\n"), + Location: &ast.Location{ + File: "policy2.rego", + }, + }, + { + Package: "data.foo.baz", + Name: "p.q.r.test_quz", + Fail: true, + Trace: getFakeTraceEvents(), + Location: &ast.Location{ + File: "policy3.rego", + }, + }, + { + Package: "data.foo.qux", + Name: "test_cases_nested", + Trace: getFakeTraceEvents(), + Fail: true, + SubResults: SubResultMap{ + "one": { + Name: "one", + Fail: false, + SubResults: SubResultMap{ + "foo": { + Name: "foo", + Fail: false, + }, + "bar": { + Name: "bar", + Fail: false, + }, + }, + }, + "two": { + Name: "two", + Fail: true, + SubResults: SubResultMap{ + "foo": { + Name: "foo", + Fail: true, + Trace: getFakeTraceEventsFor( + ast.MustParseExpr("x == y + z"), + func(e *topdown.Event) { + // QueryID == 0 is not pretty-printed, as this is the base query to eval the test rule; not the test rule itself. + e.QueryID = 1 + }, + func(e *topdown.Event) { + e.Location.File = "policy5.rego" + e.Location.Row = 5 + }, + func(e *topdown.Event) { + e.Locals = ast.NewValueMap() + e.Locals.Put(ast.Var("x"), ast.Number("1")) + e.Locals.Put(ast.Var("y"), ast.Number("2")) + e.Locals.Put(ast.Var("z"), ast.Number("3")) + }, + func(e *topdown.Event) { + e.LocalMetadata = map[ast.Var]topdown.VarMetadata{ + "x": {Name: "x"}, + "y": {Name: "y"}, + "z": {Name: "z"}, + } + }), + }, + "bar": { + Name: "bar", + Fail: false, + }, + }, + }, + }, + Location: &ast.Location{ + File: "policy5.rego", + }, + }, + } + + r := PrettyReporter{ + Output: &buf, + Verbose: false, + FailureLine: true, + LocalVars: true, + } + ch := resultsChan(ts) + if err := r.Report(ch); err != nil { + t.Fatal(err) + } + + exp := `FAILURES +-------------------------------------------------------------------------------- +data.foo.bar.test_corge: FAIL (0s) + + policy1.rego:5: + x == y + z + | | | + | | 3 + | 2 + 1 + +data.foo.bar.test_contains_print_fail: FAIL (0s) + + +data.foo.baz.p.q.r.test_quz: FAIL (0s) + + +data.foo.qux.test_cases_nested: FAIL (0s) + + two: FAIL + foo: FAIL + + policy5.rego:5: + x == y + z + | | | + | | 3 + | 2 + 1 + +SUMMARY +-------------------------------------------------------------------------------- +policy1.rego: +data.foo.bar.test_qux: ERROR (0s) + some err +data.foo.bar.test_corge: FAIL (0s) +data.foo.bar.todo_test_qux: SKIPPED + +policy2.rego: +data.foo.bar.test_contains_print_fail: FAIL (0s) + + fake print output2 + + +policy3.rego: +data.foo.baz.p.q.r.test_quz: FAIL (0s) + +policy5.rego: +data.foo.qux.test_cases_nested: FAIL (0s) + two: FAIL + foo: FAIL +-------------------------------------------------------------------------------- +PASS: 5/11 +FAIL: 4/11 +SKIPPED: 1/11 +ERROR: 1/11 +` + + if exp != buf.String() { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, buf.String()) + } +} + +func TestPrettyReporter(t *testing.T) { + var buf bytes.Buffer + + // supply fake trace events to verify that traces are suppressed without verbose + // flag. + ts := []*Result{ + { + Package: "data.foo.bar", + Name: "test_baz", + Trace: getFakeTraceEvents(), + Location: &ast.Location{ + File: "policy1.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "test_qux", + Error: errors.New("some err"), + Trace: getFakeTraceEvents(), + Location: &ast.Location{ + File: "policy1.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "test_corge", + Fail: true, + Trace: getFakeTraceEvents(), + Location: &ast.Location{ + File: "policy1.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "todo_test_qux", + Skip: true, + Trace: nil, + Location: &ast.Location{ + File: "policy1.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "test_contains_print_pass", + Output: []byte("fake print output\n"), + Location: &ast.Location{ + File: "policy1.rego", + }, + }, + { + Package: "data.foo.bar", + Name: "test_contains_print_fail", + Fail: true, + Output: []byte("fake print output2\n"), + Location: &ast.Location{ + File: "policy2.rego", + }, + }, + { + Package: "data.foo.baz", + Name: "p.q.r.test_quz", + Fail: true, + Trace: getFakeTraceEvents(), + Location: &ast.Location{ + File: "policy3.rego", + }, + }, + { + Package: "data.foo.qux", + Name: "test_cases", + Trace: getFakeTraceEvents(), + Fail: true, + // Will be sorted to "bar", "baz", "foo" in output for stability + SubResults: SubResultMap{ + "foo": { + Name: "foo", + Fail: false, + }, + "bar": { + Name: "bar", + Fail: true, + }, + "baz": { + Name: "baz", + Fail: false, + }, + }, + Location: &ast.Location{ + File: "policy4.rego", + }, + }, + { + Package: "data.foo.qux", + Name: "test_cases_nested", + Trace: getFakeTraceEvents(), + Fail: true, + SubResults: SubResultMap{ + "one": { + Name: "one", + Fail: false, + SubResults: SubResultMap{ + "foo": { + Name: "foo", + Fail: false, + }, + "bar": { + Name: "bar", + Fail: false, + }, + }, + }, + "two": { + Name: "two", + Fail: true, + SubResults: SubResultMap{ + "foo": { + Name: "foo", + Fail: true, + }, + "bar": { + Name: "bar", + Fail: false, + }, + }, + }, + }, + Location: &ast.Location{ + File: "policy4.rego", + }, + }, + } + + r := PrettyReporter{ + Output: &buf, + Verbose: false, + } + ch := resultsChan(ts) + if err := r.Report(ch); err != nil { + t.Fatal(err) + } + + exp := `policy1.rego: +data.foo.bar.test_qux: ERROR (0s) + some err +data.foo.bar.test_corge: FAIL (0s) +data.foo.bar.todo_test_qux: SKIPPED + +policy2.rego: +data.foo.bar.test_contains_print_fail: FAIL (0s) + + fake print output2 + + +policy3.rego: +data.foo.baz.p.q.r.test_quz: FAIL (0s) + +policy4.rego: +data.foo.qux.test_cases: FAIL (0s) + bar: FAIL +data.foo.qux.test_cases_nested: FAIL (0s) + two: FAIL + foo: FAIL +-------------------------------------------------------------------------------- +PASS: 7/14 +FAIL: 5/14 +SKIPPED: 1/14 +ERROR: 1/14 +` + + if exp != buf.String() { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, buf.String()) + } +} + +func TestJSONReporter(t *testing.T) { + var buf bytes.Buffer + ts := []*Result{ + { + Package: "data.foo.bar", + Name: "test_baz", + Trace: getFakeTraceEvents(), + }, + { + Package: "data.foo.bar", + Name: "test_qux", + Error: errors.New("some err"), + Trace: getFakeTraceEvents(), + }, + { + Package: "data.foo.bar", + Name: "test_corge", + Fail: true, + Trace: getFakeTraceEvents(), + }, + { + Package: "data.foo.bar", + Name: "todo_test_qux", + Skip: true, + Trace: nil, + }, + { + Package: "data.foo.bar", + Name: "test_contains_print", + Output: []byte("fake print output\n"), + }, + { + Package: "data.foo.baz", + Name: "p.q.r.test_quz", + }, + { + Package: "data.foo.qux", + Name: "test_cases_nested", + Trace: getFakeTraceEvents(), + Fail: true, + SubResults: SubResultMap{ + "one": { + Name: "one", + Fail: false, + SubResults: SubResultMap{ + "foo": { + Name: "foo", + Fail: false, + }, + "bar": { + Name: "bar", + Fail: false, + }, + }, + }, + "two": { + Name: "two", + Fail: true, + SubResults: SubResultMap{ + "foo": { + Name: "foo", + Fail: true, + }, + "bar": { + Name: "bar", + Fail: false, + }, + }, + }, + }, + Location: &ast.Location{ + File: "policy5.rego", + }, + }, + } + + r := JSONReporter{ + Output: &buf, + } + + ch := resultsChan(ts) + + if err := r.Report(ch); err != nil { + t.Fatal(err) + } + + exp := util.MustUnmarshalJSON([]byte(`[ { + "location" : null, + "package" : "data.foo.bar", + "name" : "test_baz", + "duration" : 0, + "trace" : [ { + "Op" : "Fail", + "Node" : { + "index" : 0, + "terms" : [ { + "type" : "ref", + "value" : [ { + "type" : "var", + "value" : "eq" + } ] + }, { + "type" : "boolean", + "value" : true + }, { + "type" : "boolean", + "value" : false + } ] + }, + "Location" : { + "file" : "", + "row" : 1, + "col" : 1 + }, + "QueryID" : 0, + "ParentID" : 0, + "Locals" : null, + "LocalMetadata" : null, + "Message" : "", + "Ref" : null + } ] +}, { + "location" : null, + "package" : "data.foo.bar", + "name" : "test_qux", + "error" : { }, + "duration" : 0, + "trace" : [ { + "Op" : "Fail", + "Node" : { + "index" : 0, + "terms" : [ { + "type" : "ref", + "value" : [ { + "type" : "var", + "value" : "eq" + } ] + }, { + "type" : "boolean", + "value" : true + }, { + "type" : "boolean", + "value" : false + } ] + }, + "Location" : { + "file" : "", + "row" : 1, + "col" : 1 + }, + "QueryID" : 0, + "ParentID" : 0, + "Locals" : null, + "LocalMetadata" : null, + "Message" : "", + "Ref" : null + } ] +}, { + "location" : null, + "package" : "data.foo.bar", + "name" : "test_corge", + "fail" : true, + "duration" : 0, + "trace" : [ { + "Op" : "Fail", + "Node" : { + "index" : 0, + "terms" : [ { + "type" : "ref", + "value" : [ { + "type" : "var", + "value" : "eq" + } ] + }, { + "type" : "boolean", + "value" : true + }, { + "type" : "boolean", + "value" : false + } ] + }, + "Location" : { + "file" : "", + "row" : 1, + "col" : 1 + }, + "QueryID" : 0, + "ParentID" : 0, + "Locals" : null, + "LocalMetadata" : null, + "Message" : "", + "Ref" : null + } ] +}, { + "location" : null, + "package" : "data.foo.bar", + "name" : "todo_test_qux", + "skip" : true, + "duration" : 0 +}, { + "location" : null, + "package" : "data.foo.bar", + "name" : "test_contains_print", + "duration" : 0, + "output" : "ZmFrZSBwcmludCBvdXRwdXQK" +}, { + "location" : null, + "package" : "data.foo.baz", + "name" : "p.q.r.test_quz", + "duration" : 0 +}, { + "location" : { + "file" : "policy5.rego", + "row" : 0, + "col" : 0 + }, + "package" : "data.foo.qux", + "name" : "test_cases_nested", + "fail" : true, + "duration" : 0, + "trace" : [ { + "Op" : "Fail", + "Node" : { + "index" : 0, + "terms" : [ { + "type" : "ref", + "value" : [ { + "type" : "var", + "value" : "eq" + } ] + }, { + "type" : "boolean", + "value" : true + }, { + "type" : "boolean", + "value" : false + } ] + }, + "Location" : { + "file" : "", + "row" : 1, + "col" : 1 + }, + "QueryID" : 0, + "ParentID" : 0, + "Locals" : null, + "LocalMetadata" : null, + "Message" : "", + "Ref" : null + } ], + "sub_results" : { + "one" : { + "name" : "one", + "sub_results" : { + "bar" : { + "name" : "bar" + }, + "foo" : { + "name" : "foo" + } + } + }, + "two" : { + "name" : "two", + "fail" : true, + "sub_results" : { + "bar" : { + "name" : "bar" + }, + "foo" : { + "name" : "foo", + "fail" : true + } + } + } + } +} ] +`)) + + result := util.MustUnmarshalJSON(buf.Bytes()) + + if !reflect.DeepEqual(result, exp) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, result) + } +} + +func TestPrettyReporterVerboseBenchmark(t *testing.T) { + var buf bytes.Buffer + + // supply fake trace events for each kind of event to ensure that only failures + // report traces. + ts := []*Result{ + { + Package: "data.foo.bar", + Name: "test_baz", + BenchmarkResult: &testing.BenchmarkResult{ + N: 1000, + T: 123000, + Bytes: 0, + MemAllocs: 0, + MemBytes: 0, + Extra: nil, + }, + }, + { + Package: "data.foo.bar", + Name: "test_qux", + Error: errors.New("some err"), + BenchmarkResult: nil, + }, + { + Package: "data.foo.bar", + Name: "test_corge", + Trace: getFakeTraceEvents(), + Fail: true, + BenchmarkResult: &testing.BenchmarkResult{ + N: 100, + T: 12300, + Bytes: 0, + MemAllocs: 567, + MemBytes: 890, + Extra: nil, + }, + }, + { + Package: "data.foo.bar", + Name: "test_cases_fail", + Fail: true, + Trace: getFakeTraceEvents(), + SubResults: SubResultMap{ + "one": { + Name: "one", + Fail: false, + }, + "two": { + Name: "two", + Fail: true, + }, + }, + BenchmarkResult: &testing.BenchmarkResult{ + N: 100, + T: 12300, + Bytes: 0, + MemAllocs: 567, + MemBytes: 890, + Extra: nil, + }, + }, + { + Package: "data.foo.bar", + Name: "test_cases_ok", + Fail: false, + SubResults: SubResultMap{ + "one": { + Name: "one", + Fail: false, + }, + "two": { + Name: "two", + Fail: false, + }, + }, + BenchmarkResult: &testing.BenchmarkResult{ + N: 2000, + T: 123000, + Bytes: 0, + MemAllocs: 567, + MemBytes: 890, + Extra: nil, + }, + }, + } + + r := PrettyReporter{ + Output: &buf, + Verbose: true, + BenchmarkResults: true, + } + + ch := resultsChan(ts) + if err := r.Report(ch); err != nil { + t.Fatal(err) + } + + exp := fixtureReporterVerboseBenchmark + if exp != buf.String() { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, buf.String()) + } +} + +func TestPrettyReporterVerboseBenchmarkShowAllocations(t *testing.T) { + var buf bytes.Buffer + + // supply fake trace events for each kind of event to ensure that only failures + // report traces. + ts := []*Result{ + { + Package: "data.foo.bar", + Name: "test_baz", + BenchmarkResult: &testing.BenchmarkResult{ + N: 1000, + T: 123000, + Bytes: 0, + MemAllocs: 678, + MemBytes: 91011, + Extra: map[string]float64{ + "timer_rego_query_eval_ns/op": 123, + }, + }, + }, + { + Package: "data.foo.bar", + Name: "test_qux", + Error: errors.New("some err"), + BenchmarkResult: nil, + }, + { + Package: "data.foo.bar", + Name: "test_corge", + Fail: true, + BenchmarkResult: &testing.BenchmarkResult{ + N: 100, + T: 12300, + Bytes: 0, + MemAllocs: 567, + MemBytes: 890, + Extra: map[string]float64{ + "timer_rego_query_eval_ns/op": 123, + }, + }, + }, + } + + r := PrettyReporter{ + Output: &buf, + Verbose: true, + BenchmarkResults: true, + BenchMarkShowAllocations: true, + } + + ch := resultsChan(ts) + if err := r.Report(ch); err != nil { + t.Fatal(err) + } + + exp := fixtureReporterVerboseBenchmarkShowAllocations + if exp != buf.String() { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, buf.String()) + } +} + +func TestPrettyReporterVerboseBenchmarkShowAllocationsGoBenchFormat(t *testing.T) { + var buf bytes.Buffer + + // supply fake trace events for each kind of event to ensure that only failures + // report traces. + ts := []*Result{ + { + Package: "data.foo.bar", + Name: "test_baz", + BenchmarkResult: &testing.BenchmarkResult{ + N: 1000, + T: 123000, + Bytes: 0, + MemAllocs: 678, + MemBytes: 91011, + Extra: map[string]float64{ + "timer_rego_query_eval_ns/op": 123, + }, + }, + }, + { + Package: "data.foo.bar", + Name: "test_qux", + Error: errors.New("some err"), + BenchmarkResult: nil, + }, + { + Package: "data.foo.bar", + Name: "test_corge", + Fail: true, + BenchmarkResult: &testing.BenchmarkResult{ + N: 100, + T: 12300, + Bytes: 0, + MemAllocs: 567, + MemBytes: 890, + Extra: map[string]float64{ + "timer_rego_query_eval_ns/op": 123, + }, + }, + }, + } + + r := PrettyReporter{ + Output: &buf, + Verbose: true, + BenchmarkResults: true, + BenchMarkShowAllocations: true, + BenchMarkGoBenchFormat: true, + } + + ch := resultsChan(ts) + if err := r.Report(ch); err != nil { + t.Fatal(err) + } + + exp := fixtureReporterVerboseBenchmarkShowAllocationsGoBenchFormat + if exp != buf.String() { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, buf.String()) + } +} + +func TestJSONReporterBenchmark(t *testing.T) { + var buf bytes.Buffer + ts := []*Result{ + { + Package: "data.foo.bar", + Name: "test_baz", + BenchmarkResult: &testing.BenchmarkResult{ + N: 1000, + T: 123000, + Bytes: 0, + MemAllocs: 678, + MemBytes: 91011, + Extra: map[string]float64{ + "timer_rego_query_eval_ns/op": 123, + }, + }, + }, + { + Package: "data.foo.bar", + Name: "test_qux", + Error: errors.New("some err"), + }, + { + Package: "data.foo.bar", + Name: "test_corge", + Fail: true, + }, + { + Package: "data.foo.bar", + Name: "todo_test_qux", + Skip: true, + }, + { + Package: "data.foo.bar", + Name: "test_cases_fail", + Fail: true, + SubResults: SubResultMap{ + "one": { + Name: "one", + Fail: false, + }, + "two": { + Name: "two", + Fail: true, + }, + }, + BenchmarkResult: &testing.BenchmarkResult{ + N: 100, + T: 12300, + Bytes: 0, + MemAllocs: 567, + MemBytes: 890, + Extra: nil, + }, + }, + { + Package: "data.foo.bar", + Name: "test_cases_ok", + Fail: false, + SubResults: SubResultMap{ + "one": { + Name: "one", + Fail: false, + }, + "two": { + Name: "two", + Fail: true, + }, + }, + BenchmarkResult: &testing.BenchmarkResult{ + N: 2000, + T: 123000, + Bytes: 0, + MemAllocs: 567, + MemBytes: 890, + Extra: nil, + }, + }, + } + + r := JSONReporter{ + Output: &buf, + } + + ch := resultsChan(ts) + + if err := r.Report(ch); err != nil { + t.Fatal(err) + } + + exp := util.MustUnmarshalJSON([]byte(`[ + { + "location": null, + "package": "data.foo.bar", + "name": "test_baz", + "duration": 0, + "benchmark_result": { + "N": 1000, + "T": 123000, + "Bytes": 0, + "MemAllocs": 678, + "MemBytes": 91011, + "Extra": { + "timer_rego_query_eval_ns/op": 123 + } + } + }, + { + "location": null, + "package": "data.foo.bar", + "name": "test_qux", + "error": {}, + "duration": 0 + }, + { + "location": null, + "package": "data.foo.bar", + "name": "test_corge", + "fail": true, + "duration": 0 + }, + { + "location": null, + "package": "data.foo.bar", + "name": "todo_test_qux", + "skip": true, + "duration": 0 + }, + { + "location": null, + "package": "data.foo.bar", + "name": "test_cases_fail", + "fail": true, + "duration": 0, + "benchmark_result": { + "N": 100, + "T": 12300, + "Bytes": 0, + "MemAllocs": 567, + "MemBytes": 890, + "Extra": null + }, + "sub_results": { + "one": { + "name": "one" + }, + "two": { + "name": "two", + "fail": true + } + } + }, + { + "location": null, + "package": "data.foo.bar", + "name": "test_cases_ok", + "duration": 0, + "benchmark_result": { + "N": 2000, + "T": 123000, + "Bytes": 0, + "MemAllocs": 567, + "MemBytes": 890, + "Extra": null + }, + "sub_results": { + "one": { + "name": "one" + }, + "two": { + "name": "two", + "fail": true + } + } + } +] +`)) + + result := util.MustUnmarshalJSON(buf.Bytes()) + + if !reflect.DeepEqual(result, exp) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", string(util.MustMarshalJSON(exp)), string(util.MustMarshalJSON(result))) + } +} + +func TestPrettyReporterFmtBenchmark(t *testing.T) { + benchResult := &testing.BenchmarkResult{ + N: 1000, + T: 1230000, + Bytes: 0, + MemAllocs: 10000, + MemBytes: 123456, + Extra: map[string]float64{ + "extra1": 99887766, + "extra2": 11223344, + }, + } + cases := []struct { + note string + tr *Result + goBenchFmt bool + showAllocations bool + expectedName string + }{ + { + note: "base", + tr: &Result{ + Package: "data.foo.bar", + Name: "test_baz", + BenchmarkResult: benchResult, + }, + expectedName: "data.foo.bar.test_baz", + }, + { + note: "with memory", + tr: &Result{ + Package: "data.foo.bar", + Name: "test_baz", + BenchmarkResult: benchResult, + }, + expectedName: "data.foo.bar.test_baz", + showAllocations: true, + }, + { + note: "gobench format", + tr: &Result{ + Package: "data.foo.bar", + Name: "test_baz", + BenchmarkResult: benchResult, + }, + expectedName: "BenchmarkDataFooBarTestBaz", + goBenchFmt: true, + }, + { + note: "gobench format with memory", + tr: &Result{ + Package: "data.foo.bar", + Name: "test_baz", + BenchmarkResult: benchResult, + }, + expectedName: "BenchmarkDataFooBarTestBaz", + goBenchFmt: true, + showAllocations: true, + }, + { + note: "gobench format extra underscores", + tr: &Result{ + Package: "data.foo.bar", + Name: "_test___baz__", + BenchmarkResult: benchResult, + }, + expectedName: "BenchmarkDataFooBarTestBaz", + goBenchFmt: true, + }, + { + note: "gobench format already camelcase", + tr: &Result{ + Package: "data.foo.bar", + Name: "test_fooBar", + BenchmarkResult: benchResult, + }, + expectedName: "BenchmarkDataFooBarTestFooBar", + goBenchFmt: true, + }, + + { + note: "gobench format underscore in path", + tr: &Result{ + Package: "data.foo_bar.test_thing__", + Name: "test_fooBar", + BenchmarkResult: benchResult, + }, + expectedName: "BenchmarkDataFooBarTestThingTestFooBar", + goBenchFmt: true, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + r := PrettyReporter{ + BenchmarkResults: true, + BenchMarkShowAllocations: tc.showAllocations, + BenchMarkGoBenchFormat: tc.goBenchFmt, + } + + actual := r.fmtBenchmark(tc.tr) + + fields := strings.Fields(actual) + + // Expect the first field to be the name + name := fields[0] + if name != tc.expectedName { + t.Fatalf("Expected first field of formatted result to be %s, got %s\n\n\t Full Result: %s", tc.expectedName, name, actual) + } + + // The next field should be the count (N) + n, err := strconv.Atoi(fields[1]) + if err != nil { + t.Fatalf("Unexpected error parsing count (N): %s", err) + } + if n != tc.tr.BenchmarkResult.N { + t.Fatalf("Expected N == %d, got %d", tc.tr.BenchmarkResult.N, n) + } + + // Every field after this is optional, and the order doesn't really matter. Expect pairs of fields + // with the first being the value and second being the name + results := map[string]float64{} + for i := 2; i < len(fields); i += 2 { + v, err := strconv.ParseFloat(fields[i], 64) + if err != nil { + t.Fatalf("Unexpected error parsing value '%s' for key '%s': %s", fields[i], fields[i+1], err) + } + results[fields[i+1]] = v + } + + requiredKeys := []string{ + "ns/op", + } + + for k := range tc.tr.BenchmarkResult.Extra { + requiredKeys = append(requiredKeys, k) + } + + if tc.showAllocations { + requiredKeys = append(requiredKeys, "B/op", "allocs/op") + } + + for _, k := range requiredKeys { + _, ok := results[k] + if !ok { + t.Errorf("Missing expected key %s in results, got %+v", k, results) + } + } + }) + } +} + +func resultsChan(ts []*Result) chan *Result { + ch := make(chan *Result) + go func() { + for _, tr := range ts { + ch <- tr + } + close(ch) + }() + return ch +} diff --git a/third_party/opa/v1/tester/runer_compile_test.go b/third_party/opa/v1/tester/runer_compile_test.go new file mode 100644 index 000000000000..15b59b075fd5 --- /dev/null +++ b/third_party/opa/v1/tester/runer_compile_test.go @@ -0,0 +1,474 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package tester + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestInjectTestCaseFunc(t *testing.T) { + testCases := []struct { + note string + module string + exp string + }{ + { + note: "no head-ref, assigned last in body", + module: `package test + test_foo if { + some tc in [ + {"note": "a", "x": 1}, + ] + tc.x == 1 + }`, + // func not injected + exp: `package test + test_foo if { + __local3__ = [{"note": "a", "x": 1}] + __local2__ = __local3__[__local1__] + __local2__.x = 1 + }`, + }, + + { + note: "manual use of internal.test_case", + module: `package test + test_foo[tc.note] if { + some tc in [ + {"note": "a", "x": 1}, + ] + tc.x == 1 + internal.test_case([tc.note, "foo", "bar"]) + }`, + // func not injected + exp: `package test + test_foo[__local0__] if { + __local4__ = [{"note": "a", "x": 1}] + __local3__ = __local4__[__local2__] # func would have been injected subsequent to here + __local3__.x = 1 + __local5__ = __local3__.note + internal.test_case([__local5__, "foo", "bar"]) # manual use of func + __local0__ = __local3__.note + }`, + }, + + { + note: "head-ref, assigned last in body", + module: `package test + test_foo.foo if { + some tc in [ + {"note": "a", "x": 1}, + ] + tc.x == 1 + }`, + // no var assignment in body, func injected first in body + exp: `package test + test_foo.foo if { + internal.test_case(["foo"]) # func injection + __local3__ = [{"note": "a", "x": 1}] + __local2__ = __local3__[__local1__] + __local2__.x = 1 + }`, + }, + { + note: "string in head-ref, assigned last in body", + module: `package test + test_foo["foo"] if { + some tc in [ + {"note": "a", "x": 1}, + ] + tc.x == 1 + }`, + // no var assignment in body, func injected first in body + exp: `package test + test_foo.foo if { + internal.test_case(["foo"]) # func injection + __local3__ = [{"note": "a", "x": 1}] + __local2__ = __local3__[__local1__] + __local2__.x = 1 + }`, + }, + + { + note: "const in head-ref, assigned last in body", + module: `package test + foo := "bar" + test_foo[foo] if { + some tc in [ + {"note": "a", "x": 1}, + ] + tc.x == 1 + }`, + // var assignment can be moved up the body, func injected after moved expr + exp: `package test + foo := "bar" if { true } + test_foo[__local0__] if { + __local0__ = data.test.foo # generated head-ref const/var assignment, moved up + internal.test_case([__local0__]) # func injection + __local4__ = [{"note": "a", "x": 1}] + __local3__ = __local4__[__local2__] + __local3__.x = 1 + }`, + }, + + { + note: "var in head-ref, assigned last in body", + module: `package test + test_foo[note] if { + some tc in [ + {"note": "a"}, + ] + note := tc.note + }`, + // var assignment cannot be moved up the body, func injected last in body + exp: `package test + test_foo[__local3__] if { + __local4__ = [{"note": "a"}] + __local2__ = __local4__[__local1__] + __local3__ = __local2__.note # head-ref var assignment + internal.test_case([__local3__]) # func injection + }`, + }, + { + note: "var in head-ref, assigned last in body, trailing assertions", + module: `package test + test_foo[note] if { + some tc in [ + {"note": "a", "x": 1}, + ] + note := tc.note + tc.x == 1 + }`, + // var assignment cannot be moved up the body, func injected last in body + exp: `package test + test_foo[__local3__] if { + __local4__ = [{"note": "a", "x": 1}] + __local2__ = __local4__[__local1__] + __local3__ = __local2__.note # head-ref var assignment + internal.test_case([__local3__]) # func injection + __local2__.x = 1 + }`, + }, + { + note: "var in head-ref, assigned mid-body", + module: `package test + test_foo[note] if { + some tc in [ + {"note": "a", "x": 1}, + ] + note := tc.note + tc.x == 1 + }`, + // var assignment cannot be moved up the body, func injected after assignment + exp: `package test + test_foo[__local3__] if { + __local4__ = [{"note": "a", "x": 1}] + __local2__ = __local4__[__local1__] + __local3__ = __local2__.note # head-ref var assignment + internal.test_case([__local3__]) # func injection + __local2__.x = 1 + }`, + }, + { + note: "var in head-ref, assigned after unrelated assertions", + module: `package test + test_foo[note] if { + some tc in [ + {"note": "a", "x": 1}, + ] + tc.x == 1 + note := tc.note + }`, + // var assignment can be moved up the body, func injected after assignment + exp: `package test + test_foo[__local3__] if { + __local4__ = [{"note": "a", "x": 1}] + __local2__ = __local4__[__local1__] + __local2__.x = 1 # non-generated expression, can't be moved + __local3__ = __local2__.note # head-ref var assignment + internal.test_case([__local3__]) # func injection + }`, + }, + { + note: "var in head-ref, non-assignment reference in body", + module: `package test + test_concat[note] if { + some note, tc in { # Compiled into roughly '__local__ = {...}; tc = __local__[note]' + "empty + empty": { + "a": [], + "b": [], + "exp": [], + }, + } + + act := array.concat(tc.a, tc.b) + act == tc.exp + }`, + exp: `package test + test_concat[__local0__] if { + __local3__ = {"empty + empty": {"a": [], "b": [], "exp": []}} + __local1__ = __local3__[__local0__] # head-ref var assignment + internal.test_case([__local0__]) # func injection + __local5__ = __local1__.a + __local6__ = __local1__.b + array.concat(__local5__, __local6__, __local4__) + __local2__ = __local4__ + __local2__ = __local1__.exp + }`, + }, + + { + note: "ref in head-ref", + module: `package test + test_foo[tc.note] if { + some tc in [ + {"note": "a"}, + ] + }`, + // var assignment cannot be moved up the body, func injected last in body + exp: `package test + test_foo[__local0__] if { + __local4__ = [{"note": "a"}] + __local3__ = __local4__[__local2__] + __local0__ = __local3__.note # generated head-ref var assignment + internal.test_case([__local0__]) # func injection + }`, + }, + { + note: "ref in head-ref, can move above unrelated assertions", + module: `package test + test_foo[tc.note] if { + some tc in [ + {"note": "a", "x": 1, "y": 2}, + ] + tc.x == 1 + tc.y == 2 + }`, + // var assignment can be moved up the body, func injected after assignment + exp: `package test + test_foo[__local0__] if { + __local4__ = [{"note": "a", "x": 1, "y": 2}] + __local3__ = __local4__[__local2__] + __local0__ = __local3__.note # generated head-ref var assignment + internal.test_case([__local0__]) # func injection + __local3__.x = 1 + __local3__.y = 2 + }`, + }, + + // Multiple head-ref test-case terms + + { + note: "multi-term head-ref, assigned last in body", + module: `package test + test_foo.foo.bar if { + some tc in [ + {"note": "a", "x": 1}, + ] + tc.x == 1 + }`, + // no var assignment in body, func injected first in body + exp: `package test + test_foo.foo.bar if { + internal.test_case(["foo", "bar"]) # func injection + __local3__ = [{"note": "a", "x": 1}] + __local2__ = __local3__[__local1__] + __local2__.x = 1 + }`, + }, + { + note: "multiple vars in head-ref", + module: `package test + test_foo[note1][note2] if { + some flag in [ + {"note": "on", "a": 1}, + ] + note1 := flag.note + some tc in [ + {"note": "a", "x": 1}, + ] + note2 := tc.note + flag.a == 1 + tc.x == 1 + }`, + // var assignment cannot be moved up the body, func injected after last head-ref assignment + exp: `package test + test_foo[__local3__][__local7__] = true if { + __local8__ = [{"a": 1, "note": "on"}]; + __local2__ = __local8__[__local1__]; + __local3__ = __local2__.note; # manual head-ref var assignment + __local9__ = [{"note": "a", "x": 1}]; + __local6__ = __local9__[__local5__]; + __local7__ = __local6__.note; # manual head-ref var assignment + internal.test_case([__local3__, __local7__]); # func injection, after last head-ref assignment + __local2__.a = 1; + __local6__.x = 1 + }`, + }, + { + note: "multiple vars in head-ref, manual assignment below unrelated assertion(s)", + module: `package test + test_foo[note1][note2] if { + some flag in [ + {"note": "on", "a": 1}, + ] + some tc in [ + {"note": "a", "x": 1}, + ] + note2 := tc.note + flag.a == 1 + note1 := flag.note + tc.x == 1 + }`, + // var assignment cannot be moved up the body, func injected after last head-ref assignment + exp: `package test + test_foo[__local7__][__local6__] if { + __local8__ = [{"a": 1, "note": "on"}] + __local2__ = __local8__[__local1__] + __local9__ = [{"note": "a", "x": 1}] + __local5__ = __local9__[__local4__] + __local6__ = __local5__.note # manual head-ref var assignment + __local2__.a = 1 + __local7__ = __local2__.note # manual head-ref var assignment, cannot be moved + internal.test_case([__local7__, __local6__]) # func injection, after last head-ref assignment + __local5__.x = 1 + }`, + }, + { + note: "multiple refs in head-ref", + module: `package test + test_foo[tc.note][flag.note] if { + some flag in [ + {"note": "on", "a": 1}, + ] + some tc in [ + {"note": "a", "x": 1}, + ] + flag.a == 1 + tc.x == 1 + }`, + // var assignment can be moved up the body, func injected after last head-ref assignment + exp: `package test + test_foo[__local0__][__local1__] if { + __local8__ = [{"a": 1, "note": "on"}] + __local4__ = __local8__[__local3__] + __local1__ = __local4__.note # generated head-ref var assignment, moved up + __local9__ = [{"note": "a", "x": 1}] + __local7__ = __local9__[__local6__] + __local0__ = __local7__.note # generated head-ref var assignment, moved up + internal.test_case([__local0__, __local1__]) # func injection, after last head-ref assignment + __local4__.a = 1; + __local7__.x = 1 + }`, + }, + { + note: "multiple refs in head-ref, mixed with ground terms", + module: `package test + test_foo[tc.note].bar[flag.note].baz if { + some flag in [ + {"note": "on", "a": 1}, + ] + some tc in [ + {"note": "a", "x": 1}, + ] + flag.a == 1 + tc.x == 1 + }`, + // var assignment cannot be moved up the body, func injected last in body + exp: `package test + test_foo[__local0__].bar[__local1__].baz if { + __local8__ = [{"a": 1, "note": "on"}] + __local4__ = __local8__[__local3__] + __local1__ = __local4__.note # generated head-ref var assignment, moved up + __local9__ = [{"note": "a", "x": 1}] + __local7__ = __local9__[__local6__] + __local0__ = __local7__.note # generated head-ref var assignment, moved up + internal.test_case([__local0__, "bar", __local1__, "baz"]) # func injection, after last head-ref assignment + __local4__.a = 1 + __local7__.x = 1 + }`, + }, + { + note: "multiple vars in head-ref, non-assignment reference in body", + module: `package example_test + test_sign_token[note][alg] if { + some note, tc in { + "claims": { + "claims": {"foo": "bar"}, + }, + "no claims": { + "claims": {}, + }, + } + + some alg in [ + "HS256", + "HS512", + ] + + secret := "foobar" + key := base64.encode(secret) + + token := io.jwt.encode_sign({ + "typ": "JWT", + "alg": alg + }, tc.claims, { + "kty": "oct", + "k": key + }) + + [valid, _, payload] := io.jwt.decode_verify(token, {"secret": secret}) + valid + payload = tc.claims + }`, + exp: `package example_test + test_sign_token[__local0__][__local4__] if { + __local11__ = {"claims": {"claims": {"foo": "bar"}}, "no claims": {"claims": {}}} + __local1__ = __local11__[__local0__] + __local12__ = ["HS256", "HS512"] + __local4__ = __local12__[__local3__] + internal.test_case([__local0__, __local4__]) # func injection + __local5__ = "foobar"; base64.encode(__local5__, __local13__) + __local6__ = __local13__ + __local16__ = __local1__.claims + io.jwt.encode_sign({"alg": __local4__, "typ": "JWT"}, __local16__, {"k": __local6__, "kty": "oct"}, __local14__) + __local7__ = __local14__ + io.jwt.decode_verify(__local7__, {"secret": __local5__}, __local15__) + [__local8__, __local9__, __local10__] = __local15__ + __local8__ + __local10__ = __local1__.claims + }`, + }, + } + + for _, tc := range testCases { + t.Run(tc.note, func(t *testing.T) { + modules := map[string]*ast.Module{ + "test.rego": ast.MustParseModule(tc.module), + } + + exp := ast.MustParseModule(tc.exp) + + c := ast.NewCompiler() + c.WithStageAfter("RewriteLocalVars", ast.CompilerStageDefinition{ + Name: "InjectTestCaseFunc", + MetricName: "inject_test_case_func", + Stage: injectTestCaseFunc, + }) + + c.Compile(modules) + if c.Failed() { + t.Fatalf("Unexpected error(s): %v", c.Errors) + } + + result := c.Modules["test.rego"] + if !result.Equal(exp) { + t.Fatalf("Expected:\n\n%v\n\nbut got:\n\n%v", exp, result) + } + }) + } +} diff --git a/third_party/opa/v1/tester/runner.go b/third_party/opa/v1/tester/runner.go new file mode 100644 index 000000000000..e6aaaa8de758 --- /dev/null +++ b/third_party/opa/v1/tester/runner.go @@ -0,0 +1,1305 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package tester contains utilities for executing Rego tests. +package tester + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "maps" + "regexp" + "runtime" + "slices" + "strconv" + "strings" + "testing" + "time" + + wasm_errors "github.com/open-policy-agent/opa/internal/wasm/sdk/opa/errors" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/bundle" + "github.com/open-policy-agent/opa/v1/loader" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/util" +) + +// TestPrefix declares the prefix for all test rules. +const TestPrefix = "test_" + +// SkipTestPrefix declares the prefix for tests that should be skipped. +const SkipTestPrefix = "todo_test_" + +// Run executes all test cases found under files in path. +func Run(ctx context.Context, paths ...string) ([]*Result, error) { + return RunWithFilter(ctx, nil, paths...) +} + +// RunWithFilter executes all test cases found under files in path. The filter +// will be applied to exclude files that should not be included. +func RunWithFilter(ctx context.Context, _ loader.Filter, paths ...string) ([]*Result, error) { + modules, store, err := Load(paths, nil) + if err != nil { + return nil, err + } + ch, err := NewRunner().SetStore(store).Run(ctx, modules) + if err != nil { + return nil, err + } + result := []*Result{} + for r := range ch { + result = append(result, r) + } + return result, nil +} + +type SubResult struct { + Name string `json:"name,omitempty"` + Fail bool `json:"fail,omitempty"` + Trace []*topdown.Event `json:"-"` + SubResults SubResultMap `json:"sub_results,omitempty"` +} + +type SubResultMap map[string]*SubResult + +func (srm SubResultMap) Update(path ast.Array, trace []*topdown.Event) bool { + strPath := make([]string, path.Len()) + for i := range path.Len() { + strPath[i] = termToString(path.Elem(i)) + } + return srm.update(strPath, 0, trace) +} + +func (srm SubResultMap) update(path []string, i int, trace []*topdown.Event) bool { + if i >= len(path) { + return true + } + + k := path[i] + entry, ok := srm[k] + if !ok { + entry = &SubResult{ + Name: path[i], + Fail: true, + SubResults: SubResultMap{}, + } + srm[k] = entry + } + + if i == len(path)-1 { + entry.Trace = trace + return entry.Fail + } + + fail := entry.SubResults.update(path, i+1, trace) + + if fail { + entry.Fail = true + } + + return fail +} + +type unknownResolver struct{} + +func (unknownResolver) Resolve(_ ast.Ref) (any, error) { + return "UNKNOWN", nil +} + +func termToString(t *ast.Term) string { + ti, err := ast.ValueToInterface(t.Value, unknownResolver{}) + if err != nil { + return "INVALID" + } + var str string + var ok bool + if str, ok = ti.(string); !ok { + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(ti); err != nil { + return "INVALID" + } + str = strings.TrimSpace(buf.String()) + } + + return str +} + +// Result represents a single test case result. +type Result struct { + Location *ast.Location `json:"location"` + Package string `json:"package"` + Name string `json:"name"` + Fail bool `json:"fail,omitempty"` + Error error `json:"error,omitempty"` + Skip bool `json:"skip,omitempty"` + Duration time.Duration `json:"duration"` + Trace []*topdown.Event `json:"trace,omitempty"` + Output []byte `json:"output,omitempty"` + FailedAt *ast.Expr `json:"failed_at,omitempty"` + BenchmarkResult *testing.BenchmarkResult `json:"benchmark_result,omitempty"` + SubResults SubResultMap `json:"sub_results,omitempty"` +} + +func newResult(loc *ast.Location, pkg, name string, duration time.Duration, trace []*topdown.Event, output []byte) *Result { + return &Result{ + Location: loc, + Package: pkg, + Name: name, + Duration: duration, + Trace: trace, + Output: output, + SubResults: SubResultMap{}, + } +} + +// Pass returns true if the test case passed. +func (r *Result) Pass() bool { + return !r.Fail && !r.Skip && r.Error == nil +} + +func (r *Result) String() string { + return r.string(true) +} + +func (r *Result) string(subResults bool) string { + if r.Skip { + return fmt.Sprintf("%v.%v: %v", r.Package, r.Name, r.outcome()) + } + var buf bytes.Buffer + + buf.WriteString(fmt.Sprintf("%v.%v: %v (%v)", r.Package, r.Name, r.outcome(), r.Duration)) + + if subResults { + buf.WriteString("\n") + buf.WriteString(r.SubResults.String()) + } + + return buf.String() +} + +func (r *Result) outcome() string { + if r.Pass() { + return "PASS" + } + if r.Fail { + return "FAIL" + } + if r.Skip { + return "SKIPPED" + } + return "ERROR" +} + +func (sr *SubResult) String() string { + return fmt.Sprintf("%v: %v", sr.Name, sr.outcome()) +} + +func (sr *SubResult) outcome() string { + if sr.Fail { + return "FAIL" + } + return "PASS" +} + +// Iter is a depth-first iterator over all sub-results. +func (srm SubResultMap) Iter(yield func([]string, *SubResult) bool) { + srm.iter(nil, yield) +} + +func (srm SubResultMap) iter(namePrefix []string, yield func([]string, *SubResult) bool) { + for _, k := range util.KeysSorted(srm) { + sr := srm[k] + + fullName := make([]string, len(namePrefix)+1) + copy(fullName, namePrefix) + fullName[len(fullName)-1] = k + + if !yield(fullName, sr) { + return + } + sr.SubResults.iter(fullName, yield) + } +} + +func (srm SubResultMap) String() string { + return srm.string(" ") +} + +func (srm SubResultMap) string(indent string) string { + var buf bytes.Buffer + for fullName, sr := range srm.Iter { + buf.WriteString(fmt.Sprintf("%s%s\n", + strings.Repeat(indent, len(fullName)-1), + sr.String(), + )) + } + return buf.String() +} + +// BenchmarkOptions defines options specific to benchmarking tests +type BenchmarkOptions struct { + ReportAllocations bool +} + +// Runner implements simple test discovery and execution. +type Runner struct { + compiler *ast.Compiler + store storage.Store + cover topdown.QueryTracer + trace bool + enablePrintStatements bool + raiseBuiltinErrors bool + runtime *ast.Term + timeout time.Duration + modules map[string]*ast.Module + bundles map[string]*bundle.Bundle + filter string + target string // target type (wasm, rego, etc.) + customBuiltins []*Builtin + defaultRegoVersion ast.RegoVersion + parallel int +} + +// NewRunner returns a new runner. +func NewRunner() *Runner { + return &Runner{ + timeout: 5 * time.Second, + defaultRegoVersion: ast.DefaultRegoVersion, + parallel: runtime.NumCPU(), + } +} + +// SetParallel sets the number of tests that can run in parallel +func (r *Runner) SetParallel(parallel int) *Runner { + if parallel < 1 { + parallel = runtime.NumCPU() + } + + r.parallel = parallel + + return r +} + +// SetDefaultRegoVersion sets the default Rego version to use when compiling modules. +// Not applicable if a custom [ast.Compiler] is set via [SetCompiler]. +func (r *Runner) SetDefaultRegoVersion(v ast.RegoVersion) *Runner { + r.defaultRegoVersion = v + return r +} + +// SetCompiler sets the compiler used by the runner. +func (r *Runner) SetCompiler(compiler *ast.Compiler) *Runner { + r.compiler = compiler + return r +} + +// RaiseBuiltinErrors sets the runner to raise errors encountered by builtins +// such as parsing input. +func (r *Runner) RaiseBuiltinErrors(enabled bool) *Runner { + r.raiseBuiltinErrors = enabled + return r +} + +type Builtin struct { + Decl *ast.Builtin + Func func(*rego.Rego) +} + +func (r *Runner) AddCustomBuiltins(builtinsList []*Builtin) *Runner { + r.customBuiltins = builtinsList + return r +} + +// SetStore sets the store to execute tests over. +func (r *Runner) SetStore(store storage.Store) *Runner { + r.store = store + return r +} + +// SetCoverageTracer sets the tracer to use to compute coverage. +// Deprecated: Use SetCoverageQueryTracer instead. +func (r *Runner) SetCoverageTracer(tracer topdown.Tracer) *Runner { + if tracer == nil { + return r + } + if qt, ok := tracer.(topdown.QueryTracer); ok { + r.cover = qt + } else { + r.cover = topdown.WrapLegacyTracer(tracer) + } + r.trace = false + return r +} + +// SetCoverageQueryTracer sets the tracer to use to compute coverage. +func (r *Runner) SetCoverageQueryTracer(tracer topdown.QueryTracer) *Runner { + if tracer == nil { + return r + } + r.cover = tracer + r.trace = false + return r +} + +// CapturePrintOutput captures print() call outputs during evaluation and +// includes the output in test results. +func (r *Runner) CapturePrintOutput(yes bool) *Runner { + r.enablePrintStatements = yes + return r +} + +// EnableTracing enables tracing of evaluation and includes traces in results. +// Tracing is currently mutually exclusive with coverage. +func (r *Runner) EnableTracing(yes bool) *Runner { + r.trace = yes + if r.trace { + r.cover = nil + } + return r +} + +// SetRuntime sets runtime information to expose to the evaluation engine. +func (r *Runner) SetRuntime(term *ast.Term) *Runner { + r.runtime = term + return r +} + +// SetTimeout sets the timeout for the individual test cases +func (r *Runner) SetTimeout(timout time.Duration) *Runner { + r.timeout = timout + return r +} + +// SetModules will add modules to the Runner which will be compiled then used +// for discovering and evaluating tests. +func (r *Runner) SetModules(modules map[string]*ast.Module) *Runner { + r.modules = modules + return r +} + +// SetBundles will add bundles to the Runner which will be compiled then used +// for discovering and evaluating tests. +func (r *Runner) SetBundles(bundles map[string]*bundle.Bundle) *Runner { + r.bundles = bundles + return r +} + +// Filter will set a test name regex filter for the test runner. Only test +// cases which match the filter will be run. +func (r *Runner) Filter(regex string) *Runner { + r.filter = regex + return r +} + +// Target sets the output target type to use. +func (r *Runner) Target(target string) *Runner { + r.target = target + return r +} + +// Run executes all tests contained in supplied modules. +// Deprecated: Use RunTests and the Runner#SetModules or Runner#SetBundles +// helpers instead. This will NOT use the modules or bundles set on the Runner. +func (r *Runner) Run(ctx context.Context, modules map[string]*ast.Module) (chan *Result, error) { + return r.SetModules(modules).RunTests(ctx, nil) +} + +// RunTests executes tests found in either modules or bundles loaded on the runner. +// The test results will be sent in file order. +func (r *Runner) RunTests(ctx context.Context, txn storage.Transaction) (chan *Result, error) { + return r.runTests(ctx, txn, true, r.runTest, r.parallel) +} + +// RunBenchmarks executes tests similar to tester.Runner#RunTests but will repeat +// a number of times to get stable performance metrics. +// Each benchmark test is run sequentially. +func (r *Runner) RunBenchmarks(ctx context.Context, txn storage.Transaction, options BenchmarkOptions) (chan *Result, error) { + return r.runTests(ctx, txn, false, func(ctx context.Context, txn storage.Transaction, module *ast.Module, rule *ast.Rule) (result *Result, b bool) { + return r.runBenchmark(ctx, txn, module, rule, options) + }, 1) +} + +type run func(context.Context, storage.Transaction, *ast.Module, *ast.Rule) (*Result, bool) + +func (r *Runner) setupTestRun(ctx context.Context, txn storage.Transaction, enablePrintStatements bool) (*regexp.Regexp, error) { + var testRegex *regexp.Regexp + var err error + + if r.filter != "" { + testRegex, err = regexp.Compile(r.filter) + if err != nil { + return nil, err + } + } + + if r.compiler == nil { + capabilities := ast.CapabilitiesForThisVersion() + + // Add custom builtins declarations to compiler + for _, builtin := range r.customBuiltins { + capabilities.Builtins = append(capabilities.Builtins, builtin.Decl) + } + + r.compiler = ast.NewCompiler(). + WithCapabilities(capabilities). + WithEnablePrintStatements(enablePrintStatements). + WithDefaultRegoVersion(r.defaultRegoVersion) + } + + // rewrite duplicate test_* rule names as we compile modules + r.compiler.WithStageAfter("RewriteRuleHeadRefs", ast.CompilerStageDefinition{ + Name: "RewriteDuplicateTestNames", + MetricName: "rewrite_duplicate_test_names", + Stage: rewriteDuplicateTestNames, + }) + + r.compiler.WithStageAfter("RewriteLocalVars", ast.CompilerStageDefinition{ + Name: "InjectTestCaseFunc", + MetricName: "inject_test_case_func", + Stage: injectTestCaseFunc, + }) + + if r.store == nil { + r.store = inmem.NewWithOpts(inmem.OptRoundTripOnWrite(false)) + } + + if len(r.bundles) > 0 { + if txn == nil { + return nil, errors.New("unable to activate bundles: storage transaction is nil") + } + + // Activate the bundle(s) to get their info and policies into the store + // the actual compiled policies will be overwritten later. + opts := &bundle.ActivateOpts{ + Ctx: ctx, + Store: r.store, + Txn: txn, + Compiler: r.compiler, + Metrics: metrics.New(), + Bundles: r.bundles, + ParserOptions: ast.ParserOptions{RegoVersion: r.defaultRegoVersion}, + } + err := bundle.Activate(opts) + if err != nil { + return nil, err + } + + // Aggregate the bundle modules with other ones provided + if r.modules == nil { + r.modules = map[string]*ast.Module{} + } + for path, b := range r.bundles { + maps.Copy(r.modules, b.ParsedModules(path)) + } + } + + if len(r.modules) > 0 { + if r.compiler.Compile(r.modules); r.compiler.Failed() { + return nil, r.compiler.Errors + } + } + + return testRegex, nil +} + +func (r *Runner) runTests(ctx context.Context, txn storage.Transaction, enablePrintStatements bool, runFunc run, parallel int) (chan *Result, error) { + testRegex, err := r.setupTestRun(ctx, txn, enablePrintStatements) + if err != nil { + return nil, err + } + + ch := make(chan *Result) + + go func() { + defer close(ch) + + semaphore := make(chan struct{}, parallel) + results := make(chan []*Result, len(r.compiler.Modules)) + stopCtx, cancelTests := context.WithCancel(ctx) + defer cancelTests() + + for _, module := range r.compiler.Modules { + + // group the test results together by file + modResult := make(chan *Result, len(module.Rules)) + go func() { + var testResults []*Result + for range len(module.Rules) { + tr := <-modResult + if tr != nil { + testResults = append(testResults, tr) + } + } + results <- testResults + }() + + for _, rule := range module.Rules { + go func() { + semaphore <- struct{}{} + defer func() { <-semaphore }() + + select { + case <-stopCtx.Done(): + modResult <- nil + return + default: + if !r.shouldRun(rule, testRegex) { + modResult <- nil + return + } + + tr, stop := func() (*Result, bool) { + runCtx, cancel := context.WithTimeout(ctx, r.timeout) + defer cancel() + return runFunc(runCtx, txn, module, rule) + }() + modResult <- tr + if stop { + cancelTests() + } + } + }() + } + } + + for range len(r.compiler.Modules) { + res := <-results + + for _, tr := range res { + ch <- tr + } + } + }() + + return ch, nil +} + +func (*Runner) shouldRun(rule *ast.Rule, testRegex *regexp.Regexp) bool { + var ref ast.Ref + + for _, term := range rule.Head.Ref().GroundPrefix() { + ref = ref.Append(term) + + var n string + switch v := term.Value.(type) { + case ast.Var: + n = string(v) + case ast.String: + n = string(v) + default: + n = "" + } + + if strings.HasPrefix(n, TestPrefix) || strings.HasPrefix(n, SkipTestPrefix) { + // Even with the prefix it needs to pass the regex (if applicable) + fullName := rule.Module.Package.Path.Extend(ref).String() + if testRegex != nil && !testRegex.MatchString(fullName) { + return false + } + + return true + } + } + + return false +} + +// rewriteDuplicateTestNames will rewrite duplicate test names to have a numbered suffix. +// This uses a global "count" of each to ensure compiling more than once as new modules +// are added can't introduce duplicates again. +func rewriteDuplicateTestNames(compiler *ast.Compiler) *ast.Error { + count := map[string]int{} + for _, mod := range compiler.Modules { + for _, rule := range mod.Rules { + name, ref := ruleName(rule.Head) + if !strings.HasPrefix(name, TestPrefix) { + continue + } + + key := mod.Package.Path.Extend(ref).String() + if k, ok := count[key]; ok { + dynamicSuffix := rule.Head.Ref()[len(ref):] + newName := fmt.Sprintf("%s#%02d", name, k) + if len(ref) == 1 { + ref[0] = ast.VarTerm(newName) + } else { + ref[len(ref)-1] = ast.StringTerm(newName) + } + rule.Head.SetRef(append(ref, dynamicSuffix...)) + } + count[key]++ + } + } + return nil +} + +var testCaseFuncRef = ast.InternalTestCase.Ref() + +// injectTestCaseFunc will inject a call to the 'internal.test_case' function into partial-object test rules. +// We attempt to find the earliest point in the rule body where we can inject the call, to ensure that the test-case +// function is called as early as possible so that we capture as many failed test cases as possible. +// This may require us to move generated assignment expressions up the body. +// We do not attempt to move non-generated expressions, as that could contradict author intent. +// +// Consider the test rule: +// +// test_concat[tc.note] if { +// some tc in [{ +// "note": "empty + empty", +// "a": [], +// "b": [], +// "exp": [], +// }] +// act := array.concat(tc.a, tc.b) +// act == tc.exp +// } +// +// The compiler will rewrite this rule to (mid-stage @ 'RewriteLocalVars'): +// +// test_concat[__local0__] := true if { +// __local3__ = [{"a": [], "b": [], "exp": [], "note": "empty + empty"}][__local2__] +// __local4__ = array.concat(__local3__.a, __local3__.b) +// __local4__ == __local3__.exp +// __local0__ = __local3__.note # generated var +// } +// +// We move the generated var assignment as far up the body as possible, and inject the test-case function below it: +// +// test_concat[__local0__] := true if { +// __local3__ = [{"a": [], "b": [], "exp": [], "note": "empty + empty"}][__local2__] +// __local0__ = __local3__.note # moved up +// internal.test_case([__local0__]) # injected +// __local4__ = array.concat(__local3__.a, __local3__.b) # this and below expressions can now fail eval and we will still have captured the test-case +// __local4__ == __local3__.exp +// } +func injectTestCaseFunc(compiler *ast.Compiler) *ast.Error { + for _, mod := range compiler.Modules { + for _, rule := range mod.Rules { + // Only apply to test rules + rName, rRef := ruleName(rule.Head) + if !strings.HasPrefix(rName, TestPrefix) { + continue + } + + // Only apply to rules that doesn't have manual use of the test-case function + manualCall := false + ast.WalkExprs(rule.Body, func(expr *ast.Expr) bool { + if expr.IsCall() && expr.Operator().Equal(testCaseFuncRef) { + manualCall = true + return true + } + return false + }) + + if manualCall { + continue + } + + // Construct test-case name + ref := rule.Head.Ref() + if len(ref) <= len(rRef) { + // We only inject the test-case function if there is a rule ref "tail" behind the rule name + continue + } + argsRef := ref[len(rRef):] + args := ast.NewArray(argsRef...) + + // + // Pass 1: Move generated assignment expressions up the body + // + + for _, term := range argsRef { + // We expect to find generated expressions - if any - at the tail of the body, so we start from the end + for i := len(rule.Body) - 1; i >= 0; { + expr := rule.Body[i] + moved := false + + // If the expression is a generated assignment of a var in the head ref, we attempt to move it as far + // up the body as possible. + // This is a shallow move, we don't attempt to detect multiple levels of indirection and don't move such expressions; in such case, we move the assigning expression up to the first reference. + // Once done for all vars in the head ref, we can inject the test case function below the last (possibly moved) such expr. + // Note: We don't move non-generated expressions, as that could contradict author intent. + if expr.Generated && (expr.IsEquality() || expr.IsAssignment()) && expr.Operand(0).Equal(term) { + // Based on the vars in the rhs of the expr, see if we can move it up the rule body + // FIXME: Can we get away with just placing it under the lowes first occurrence of any referenced var? + vars := ast.NewVarSet() + ast.WalkVars(expr.Operand(1), func(v ast.Var) bool { + // We only care about local vars + if isLocalVar(v) { + vars.Add(v) + } + return false + }) + + if len(vars) == 0 { + // No local vars referenced, can be moved to top of body + rule.Body, moved = moveExpr(rule.Body, i, 0) + } else { + // Find the lowest (highest up the body) individual index of each var referenced in the rhs, + // and select the highest (lowest down the body) of those + + // TODO: Use TypedValueMap once synced with main + lowest := ast.NewValueMap() + + for j := i - 1; j >= 0; j-- { + expr := rule.Body[j] + ast.WalkVars(expr, func(v ast.Var) bool { + if vars.Contains(v) { + // We override the value for each var, so we get the lowest index (line highest up the body) for each + lowest.Put(v, ast.Number(strconv.Itoa(j))) + return true + } + return false + }) + } + + highest := 0 + lowest.Iter(func(k, v ast.Value) bool { + if n, err := strconv.Atoi(string(v.(ast.Number))); err == nil { + if n > highest { + highest = n + } + } + return false + }) + + if highest < i { + // The expression is lower in the body than the lowes line of any expression that might contribute to its assignment + // Move the expression to just after the lowest line + moveTo := highest + 1 + rule.Body, moved = moveExpr(rule.Body, i, moveTo) + } + } + } + + // If the expression was moved, we need to re-evaluate the current index, as it contains a new expression + if !moved { + i-- + } + } + } + + // + // Pass 2: Inject the test-case function below the lowest first occurrence of any referenced var + // + + injectBelowMap := ast.NewValueMap() + for _, term := range argsRef { + for i := len(rule.Body) - 1; i >= 0; i-- { + expr := rule.Body[i] + + ast.WalkVars(expr, func(v ast.Var) bool { + if term.Value.Compare(v) == 0 { + injectBelowMap.Put(v, ast.Number(strconv.Itoa(i))) + } + return false + }) + } + } + + // Find the earliest point where the test case function can be injected + injectBelow := -1 + injectBelowMap.Iter(func(k, v ast.Value) bool { + if n, err := strconv.Atoi(string(v.(ast.Number))); err == nil { + if n > injectBelow { + injectBelow = n + } + } + return false + }) + + testCaseFuncExpr := ast.NewExpr([]*ast.Term{ + ast.NewTerm(ast.InternalTestCase.Ref()), + ast.NewTerm(args), + }) + + rule.Body = insertExpr(rule.Body, testCaseFuncExpr, injectBelow+1) + } + } + return nil +} + +func isLocalVar(v ast.Value) bool { + if v, ok := v.(ast.Var); ok { + if strings.HasPrefix(string(v), ast.LocalVarPrefix) { + return true + } + } + return false +} + +func insertExpr(body ast.Body, expr *ast.Expr, index int) ast.Body { + if index <= 0 { + return append(ast.Body{expr}, body...) + } + + if index >= len(body) { + return append(body, expr) + } + + return append(body[:index], append(ast.Body{expr}, body[index:]...)...) +} + +func moveExpr(body ast.Body, from int, to int) (ast.Body, bool) { + if from == to { + return body, false + } + + expr := body[from] // Save the expression to move + body = slices.Delete(body, from, from+1) // Remove the expression from the body + body = append(body[:to], append(ast.Body{expr}, body[to:]...)...) // Insert the expression at the new position + return body, true +} + +// ruleName is a helper to be used when checking if a function +// (a) is a test, or +// (b) needs to be skipped +// -- it'll resolve `p.q.r` to `r`. For representing results, we'll +// use rule.Head.Ref() +func ruleName(h *ast.Head) (string, ast.Ref) { + var n string + var ref ast.Ref + + for _, term := range h.Ref().GroundPrefix() { + ref = ref.Append(term) + switch v := term.Value.(type) { + case ast.Var: + n = string(v) + case ast.String: + n = string(v) + default: + n = "" + } + + if strings.HasPrefix(n, TestPrefix) || strings.HasPrefix(n, SkipTestPrefix) { + break + } + } + + return n, ref +} + +func (r *Runner) runTest(ctx context.Context, txn storage.Transaction, mod *ast.Module, rule *ast.Rule) (*Result, bool) { + ruleName, ruleRef := ruleName(rule.Head) + if strings.HasPrefix(ruleName, SkipTestPrefix) { // TODO(sr): add test + tr := newResult(rule.Loc(), mod.Package.Path.String(), ruleRef.String(), 0*time.Second, nil, nil) + tr.Skip = true + return tr, false + } + + var bufferTracer *topdown.BufferTracer + var tracers []topdown.QueryTracer + + if r.cover != nil { + t := NewTestQueryTracer() + tracers = append(tracers, r.cover, t) + bufferTracer = &t.BufferTracer + } else if r.trace { + bufferTracer = topdown.NewBufferTracer() + tracers = append(tracers, bufferTracer) + } else { + t := NewTestQueryTracer() + tracers = append(tracers, t) + bufferTracer = &t.BufferTracer + } + + printbuf := bytes.NewBuffer(nil) + var builtinErrors []topdown.Error + queryPath := rule.Module.Package.Path.Extend(ruleRef) + + opts := []func(*rego.Rego){ + rego.Store(r.store), + rego.Transaction(txn), + rego.Compiler(r.compiler), + rego.Query(queryPath.String()), + rego.Runtime(r.runtime), + rego.Target(r.target), + rego.PrintHook(topdown.NewPrintHook(printbuf)), + rego.BuiltinErrorList(&builtinErrors), + } + + for _, t := range tracers { + opts = append(opts, rego.QueryTracer(t)) + } + + rg := rego.New(opts...) + + // Register custom builtins on rego instance + for _, v := range r.customBuiltins { + v.Func(rg) + } + + ctx = ast.WithCompiler(ctx, r.compiler) + + t0 := time.Now() + rs, err := rg.Eval(ctx) + dt := time.Since(t0) + + var trace []*topdown.Event + if bufferTracer != nil { + trace = *bufferTracer + } + + tr := newResult(rule.Loc(), mod.Package.Path.String(), ruleRef.String(), dt, trace, printbuf.Bytes()) + + // If there was an error other than errors from builtins, prefer that error. + if err != nil { + tr.Error = err + } else if r.raiseBuiltinErrors && len(builtinErrors) > 0 { + if len(builtinErrors) == 1 { + tr.Error = &builtinErrors[0] + } else { + tr.Error = fmt.Errorf("%v", builtinErrors) + } + } + + var stop bool + if err != nil { + if topdown.IsCancel(err) || wasm_errors.IsCancel(err) { + stop = ctx.Err() != context.DeadlineExceeded + } + } else if len(rs) == 0 { + tr.Fail = true + } else if rule.Head.DocKind() == ast.PartialObjectDoc { + tr.Fail, tr.SubResults = subResults(rs[0].Expressions[0].Value, trace) + } else if b, ok := rs[0].Expressions[0].Value.(bool); !ok || !b { + tr.Fail = true + } + + return tr, stop +} + +func subResults(v any, trace []*topdown.Event) (bool, map[string]*SubResult) { + if v == nil { + return true, map[string]*SubResult{} + } + + var fail bool + result := SubResultMap{} + + switch x := v.(type) { + case map[string]any: + for k, v := range x { + sr := subResult(k, v) + result[k] = sr + if sr.Fail { + fail = true + } + } + } + + // Create failed sub-results and apply per-test-case traces. + // For each test-case event, we capture the trace from first event up until the next test-case event. + var testEvent *topdown.Event + for i, e := range trace { + if e.Op == topdown.TestCaseOp { + if testEvent != nil { + if p, ok := testCaseTerms(testEvent); ok { + if f := result.Update(*p, trace[:i]); f { + fail = true + } + } + } + + testEvent = e + } + } + if testEvent != nil { + if p, ok := testCaseTerms(testEvent); ok { + if f := result.Update(*p, trace); f { + fail = true + } + } + } + + return fail, result +} + +func testCaseTerms(e *topdown.Event) (*ast.Array, bool) { + if e == nil { + return nil, false + } + + if expr, ok := e.Node.(*ast.Expr); ok { + if arr, ok := expr.Operand(0).Value.(*ast.Array); ok { + return arr, true + } + } + + return nil, false +} + +func subResult(n string, v any) *SubResult { + if v == nil { + return &SubResult{} + } + + switch x := v.(type) { + case map[string]any: + fail, srs := subResults(x, nil) + return &SubResult{ + Name: n, + Fail: fail, + SubResults: srs, + } + case bool: + return &SubResult{ + Name: n, + Fail: !x, + } + default: + return &SubResult{ + Name: n, + Fail: true, + } + } +} + +func (r *Runner) runBenchmark(ctx context.Context, txn storage.Transaction, mod *ast.Module, rule *ast.Rule, options BenchmarkOptions) (*Result, bool) { + _, rf := ruleName(rule.Head) + + tr := &Result{ + Location: rule.Loc(), + Package: mod.Package.Path.String(), + Name: rf.String(), // TODO(sr): test + } + + var stop bool + + t0 := time.Now() + + br := testing.Benchmark(func(b *testing.B) { + + pq, err := rego.New( + rego.Store(r.store), + rego.Transaction(txn), + rego.Compiler(r.compiler), + rego.Query(rule.Path().String()), + rego.Runtime(r.runtime), + rego.Target(r.target), + ).PrepareForEval(ctx) + + if err != nil { + tr.Fail = true + b.Fatalf("Unexpected error: %s", err) + } + + m := metrics.New() + + // Track memory allocations + if options.ReportAllocations { + b.ReportAllocs() + } + + for range b.N { + opts := []rego.EvalOption{rego.EvalTransaction(txn), rego.EvalMetrics(m)} + + var tracer *TestQueryTracer + if rule.Head.DocKind() == ast.PartialObjectDoc { + tracer = NewTestQueryTracer() + opts = append(opts, rego.EvalQueryTracer(tracer)) + } + + rs, err := pq.Eval(ctx, opts...) + + if err != nil { + tr.Error = err + if topdown.IsCancel(err) && !(ctx.Err() == context.DeadlineExceeded) { + stop = true + } + b.Fatalf("Unexpected error: %s", err) + } else if len(rs) == 0 { + tr.Fail = true + b.Fatal("Expected boolean result, got `undefined`") + } else if rule.Head.DocKind() == ast.PartialObjectDoc { + tr.Fail, tr.SubResults = subResults(rs[0].Expressions[0].Value, tracer.Events()) + } else if pass, ok := rs[0].Expressions[0].Value.(bool); !ok || !pass { + tr.Fail = true + b.Fatal("Expected test to evaluate as true, got false") + } + } + + for k, v := range m.All() { + var val float64 + switch v := v.(type) { + case int64: + val = float64(v) + case uint64: + val = float64(v) + case float64: + val = v + default: + continue // skip this metric + } + fv := val / float64(b.N) + b.ReportMetric(fv, k+"/op") + } + }) + + tr.Duration = time.Since(t0) + tr.BenchmarkResult = &br + + return tr, stop +} + +// Load returns modules and an in-memory store for running tests. +func Load(args []string, filter loader.Filter) (map[string]*ast.Module, storage.Store, error) { + return LoadWithRegoVersion(args, filter, ast.DefaultRegoVersion) +} + +// LoadWithRegoVersion returns modules and an in-memory store for running tests. +// Modules are parsed in accordance with the given RegoVersion. +func LoadWithRegoVersion(args []string, filter loader.Filter, regoVersion ast.RegoVersion) (map[string]*ast.Module, storage.Store, error) { + if regoVersion == ast.RegoUndefined { + regoVersion = ast.DefaultRegoVersion + } + + loaded, err := loader.NewFileLoader(). + WithRegoVersion(regoVersion). + WithBundleLazyLoadingMode(bundle.HasExtension()). + WithProcessAnnotation(true). + Filtered(args, filter) + if err != nil { + return nil, nil, err + } + + var store storage.Store + if bundle.BundleExtStore != nil { + store = bundle.BundleExtStore() + // inline'd NewFromObject + if err := storage.WriteOne(context.Background(), store, storage.AddOp, storage.Path{}, loaded.Documents); err != nil { + return nil, nil, err + } + } else { + store = inmem.NewFromObject(loaded.Documents) + } + + modules := make(map[string]*ast.Module, len(loaded.Modules)) + ctx := context.Background() + err = storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + for _, loadedModule := range loaded.Modules { + modules[loadedModule.Name] = loadedModule.Parsed + + // Add the policies to the store to ensure that any future bundle + // activations will preserve them and re-compile the module with + // the bundle modules. + err := store.UpsertPolicy(ctx, txn, loadedModule.Name, loadedModule.Raw) + if err != nil { + return err + } + } + return nil + }) + return modules, store, err +} + +// LoadWithParserOptions returns modules and an in-memory store for running tests. +// Modules are parsed in accordance with the given [ast.ParserOptions]. +func LoadWithParserOptions(args []string, filter loader.Filter, popts ast.ParserOptions) (map[string]*ast.Module, storage.Store, error) { + loaded, err := loader.NewFileLoader(). + WithRegoVersion(popts.RegoVersion). + WithCapabilities(popts.Capabilities). + WithProcessAnnotation(popts.ProcessAnnotation). + WithBundleLazyLoadingMode(bundle.HasExtension()). + Filtered(args, filter) + if err != nil { + return nil, nil, err + } + var store storage.Store + // Plumb in storage for external bundle activation plugin, if registered with bundle.RegisterStore. + if bundle.BundleExtStore != nil { + store = bundle.BundleExtStore() + // inline'd NewFromObject + if err := storage.WriteOne(context.Background(), store, storage.AddOp, storage.Path{}, loaded.Documents); err != nil { + return nil, nil, err + } + } else { + store = inmem.NewFromObject(loaded.Documents) + } + + modules := make(map[string]*ast.Module, len(loaded.Modules)) + ctx := context.Background() + err = storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error { + for _, loadedModule := range loaded.Modules { + modules[loadedModule.Name] = loadedModule.Parsed + + // Add the policies to the store to ensure that any future bundle + // activations will preserve them and re-compile the module with + // the bundle modules. + err := store.UpsertPolicy(ctx, txn, loadedModule.Name, loadedModule.Raw) + if err != nil { + return err + } + } + return nil + }) + return modules, store, err +} + +// LoadBundles will load the given args as bundles, either tarball or directory is OK. +func LoadBundles(args []string, filter loader.Filter) (map[string]*bundle.Bundle, error) { + return LoadBundlesWithRegoVersion(args, filter, ast.RegoV0) +} + +// LoadBundlesWithRegoVersion will load the given args as bundles, either tarball or directory is OK. +// Bundles are parsed in accordance with the given RegoVersion. +func LoadBundlesWithRegoVersion(args []string, filter loader.Filter, regoVersion ast.RegoVersion) (map[string]*bundle.Bundle, error) { + if regoVersion == ast.RegoUndefined { + regoVersion = ast.DefaultRegoVersion + } + + bundles := make(map[string]*bundle.Bundle, len(args)) + for _, bundleDir := range args { + b, err := loader.NewFileLoader(). + WithRegoVersion(regoVersion). + WithProcessAnnotation(true). + WithSkipBundleVerification(true). + WithBundleLazyLoadingMode(bundle.HasExtension()). + WithFilter(filter). + AsBundle(bundleDir) + if err != nil { + return nil, fmt.Errorf("unable to load bundle %s: %s", bundleDir, err) + } + bundles[bundleDir] = b + } + + return bundles, nil +} + +// LoadBundlesWithParserOptions will load the given args as bundles, either tarball or directory is OK. +// Bundles are parsed in accordance with the given [ast.ParserOptions]. +func LoadBundlesWithParserOptions(args []string, filter loader.Filter, popts ast.ParserOptions) (map[string]*bundle.Bundle, storage.Store, error) { + if popts.RegoVersion == ast.RegoUndefined { + popts.RegoVersion = ast.DefaultRegoVersion + } + + bundles := make(map[string]*bundle.Bundle, len(args)) + for _, bundleDir := range args { + b, err := loader.NewFileLoader(). + WithRegoVersion(popts.RegoVersion). + WithCapabilities(popts.Capabilities). + WithProcessAnnotation(popts.ProcessAnnotation). + WithSkipBundleVerification(true). + WithBundleLazyLoadingMode(bundle.HasExtension()). + WithFilter(filter). + AsBundle(bundleDir) + if err != nil { + return nil, nil, fmt.Errorf("unable to load bundle %s: %s", bundleDir, err) + } + bundles[bundleDir] = b + } + // Plumb in storage for external bundle activation plugin, if registered with bundle.RegisterStore. + if bundle.BundleExtStore != nil { + return bundles, bundle.BundleExtStore(), nil + } + + return bundles, inmem.NewWithOpts(inmem.OptRoundTripOnWrite(false)), nil +} diff --git a/third_party/opa/v1/tester/runner_test.go b/third_party/opa/v1/tester/runner_test.go new file mode 100644 index 000000000000..5e532d49eb30 --- /dev/null +++ b/third_party/opa/v1/tester/runner_test.go @@ -0,0 +1,1095 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package tester_test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "maps" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/cover" + "github.com/open-policy-agent/opa/v1/rego" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/tester" + "github.com/open-policy-agent/opa/v1/topdown" + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func TestRun(t *testing.T) { + testRun(t, testRunConfig{}) +} + +func TestRunBenchmark(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + testRun(t, testRunConfig{bench: true}) +} + +func TestRunWithCoverage(t *testing.T) { + cov := cover.New() + modules := testRun(t, testRunConfig{coverTracer: cov}) + report := cov.Report(modules) + if len(report.Files) != len(modules) { + t.Errorf("Expected %d files in coverage report, got %d", len(modules), len(report.Files)) + } + if report.Coverage == 0 { + t.Error("Expected test coverage") + } +} + +type expectedTestResult struct { + wantErr bool + wantFail bool + // nolint: structcheck // The test doesn't check this value, but should. + wantSkip bool + cases map[string]expectedTestResult +} + +type testRunConfig struct { + bench bool + filter string + coverTracer topdown.QueryTracer +} + +type expectedTestResults map[[2]string]expectedTestResult + +func testRun(t *testing.T, conf testRunConfig) map[string]*ast.Module { + files := map[string]string{ + "/a.rego": `package foo + import rego.v1 + + allow if { true } + `, + "/a_test.rego": `package foo + import rego.v1 + + test_pass if { allow } + non_test if { true } + test_fail if { not allow } + test_fail_non_bool = 100 + test_err if { conflict } + conflict = true + conflict = false + test_duplicate if { false } + test_duplicate if { true } + test_duplicate if { true } + todo_test_skip if { true } + `, + "/b_test.rego": `package bar + import rego.v1 + + test_duplicate if { true }`, + "/c_test.rego": `package baz + import rego.v1 + + a.b.test_duplicate if { false } + a.b.test_duplicate if { true } + a.b.test_duplicate if { true }`, + // Regression test for issue #5496. + "/d_test.rego": `package test + import rego.v1 + + a[0] := 1 + test_pass if { true }`, + "/e_test.rego": `package qux + import rego.v1 + + test_cases_pass[x] if { some x in ["foo", "bar"] } + test_cases_fail[x] if { some x in ["foo", "bar"]; false } + test_cases_partial_fail[x] if { some x in ["foo", "bar", "baz"]; x != "bar" } + test_cases_nested[x][y] if { some x in ["foo", "bar"]; some y in ["do", "re", "mi"]; not f(x, y) } + f(x, y) if { x == "foo"; y == "re" }`, + } + + tests := expectedTestResults{ + {"data.foo", "test_pass"}: {false, false, false, nil}, + {"data.foo", "test_fail"}: {false, true, false, nil}, + {"data.foo", "test_fail_non_bool"}: {false, true, false, nil}, + {"data.foo", "test_duplicate"}: {false, true, false, nil}, + {"data.foo", "test_duplicate#01"}: {false, false, false, nil}, + {"data.foo", "test_duplicate#02"}: {false, false, false, nil}, + {"data.foo", "test_err"}: {true, false, false, nil}, + {"data.foo", "todo_test_skip"}: {false, false, true, nil}, + {"data.bar", "test_duplicate"}: {false, false, false, nil}, + {"data.baz", "a.b.test_duplicate"}: {false, true, false, nil}, + {"data.baz", "a.b[\"test_duplicate#01\"]"}: {false, false, false, nil}, + {"data.baz", "a.b[\"test_duplicate#02\"]"}: {false, false, false, nil}, + {"data.test", "test_pass"}: {false, false, false, nil}, + {"data.qux", "test_cases_pass"}: {false, false, false, map[string]expectedTestResult{ + "foo": {false, false, false, nil}, + "bar": {false, false, false, nil}, + }}, + {"data.qux", "test_cases_fail"}: {false, true, false, map[string]expectedTestResult{ + "foo": {false, true, false, nil}, + "bar": {false, true, false, nil}, + }}, + {"data.qux", "test_cases_partial_fail"}: {false, true, false, map[string]expectedTestResult{ + "foo": {false, false, false, nil}, + "bar": {false, true, false, nil}, + "baz": {false, false, false, nil}, + }}, + {"data.qux", "test_cases_nested"}: {false, true, false, map[string]expectedTestResult{ + "foo": {false, true, false, map[string]expectedTestResult{ + "do": {false, false, false, nil}, + "re": {false, true, false, nil}, + "mi": {false, false, false, nil}, + }}, + "bar": {false, false, false, map[string]expectedTestResult{ + "do": {false, false, false, nil}, + "re": {false, false, false, nil}, + "mi": {false, false, false, nil}, + }}, + }}, + } + + var modules map[string]*ast.Module + test.WithTempFS(files, func(d string) { + var rs []*tester.Result + rs, modules = doTestRunWithTmpDir(t, d, conf) + validateTestResults(t, tests, rs, conf) + }) + return modules +} + +func doTestRunWithTmpDir(t *testing.T, dir string, conf testRunConfig) ([]*tester.Result, map[string]*ast.Module) { + t.Helper() + + ctx := context.Background() + + paths := []string{dir} + modules, store, err := tester.Load(paths, nil) + if err != nil { + t.Fatal(err) + } + + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + runner := tester.NewRunner(). + SetStore(store). + SetModules(modules). + Filter(conf.filter). + SetTimeout(60 * time.Second). + SetCoverageQueryTracer(conf.coverTracer) + + var ch chan *tester.Result + if conf.bench { + ch, err = runner.RunBenchmarks(ctx, txn, tester.BenchmarkOptions{}) + } else { + ch, err = runner.RunTests(ctx, txn) + } + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + var rs []*tester.Result + for r := range ch { + rs = append(rs, r) + } + + return rs, modules +} + +func validateTestResults(t *testing.T, tests expectedTestResults, rs []*tester.Result, conf testRunConfig) { + t.Helper() + seen := map[[2]string]struct{}{} + for _, r := range rs { + k := [2]string{r.Package, r.Name} + seen[k] = struct{}{} + exp, ok := tests[k] + if !ok { + t.Errorf("Unexpected result for %v", k) + continue + } else if exp.wantErr != (r.Error != nil) || exp.wantFail != r.Fail { + t.Errorf("Expected %+v for %v but got: %v", exp, k, r) + } else { + // Test passed + if conf.bench && r.BenchmarkResult == nil { + t.Errorf("Expected BenchmarkResult for test %v, got nil", k) + } else if !conf.bench && r.BenchmarkResult != nil { + t.Errorf("Unexpected BenchmarkResult for test %v, expected nil", k) + } + } + + if exp.cases != nil { + validateSubTestResults(t, exp.cases, r.SubResults) + } + } + for k := range tests { + if _, ok := seen[k]; !ok { + t.Errorf("Expected result for %v", k) + } + } +} + +func validateSubTestResults(t *testing.T, tests map[string]expectedTestResult, srs tester.SubResultMap) { + t.Helper() + seen := map[string]struct{}{} + for k, exp := range tests { + seen[k] = struct{}{} + sr, ok := srs[k] + if !ok { + t.Errorf("Expected sub-result for %v", k) + continue + } + + if exp.wantFail != sr.Fail { + t.Errorf("Expected %+v for %v but got: %v", exp, k, sr) + } + } + for k, v := range srs { + if _, ok := seen[k]; !ok { + t.Errorf("Expected sub-result for %v", k) + } + + if v.SubResults != nil { + validateSubTestResults(t, tests[k].cases, v.SubResults) + } + } +} + +func TestRunWithFilterRegex(t *testing.T) { + files := map[string]string{ + "/a.rego": `package foo + import rego.v1 + + allow if { true } + `, + "/a_test.rego": `package foo + import rego.v1 + + test_pass if { allow } + non_test if { true } + test_fail if { not allow } + test_fail_non_bool = 100 + test_err if { conflict } + conflict = true + conflict = false + test_duplicate if { false } + test_duplicate if { true } + test_duplicate if { true } + todo_test_skip if { true } + todo_test_skip_too if { false } + test_cases[x][y] if { x := "foo"; y := "bar" } + test_duplicate.foo[y] if { x := "foo"; y := "bar" } + test_duplicate[x][y] if { x := "foo"; y := "bar" } + `, + "/b_test.rego": `package bar + import rego.v1 + + test_duplicate if { true }`, + "/c_test.rego": `package baz + import rego.v1 + + a.b.test_duplicate if { false } + a.b.test_duplicate if { true } + a.b.test_duplicate if { true }`, + } + + cases := []struct { + note string + regex string + tests expectedTestResults + }{ + { + note: "all tests match", + regex: ".*", + tests: expectedTestResults{ + {"data.foo", "test_pass"}: {false, false, false, nil}, + {"data.foo", "test_fail"}: {false, true, false, nil}, + {"data.foo", "test_fail_non_bool"}: {false, true, false, nil}, + {"data.foo", "test_duplicate"}: {false, true, false, nil}, + {"data.foo", "test_duplicate#01"}: {false, false, false, nil}, + {"data.foo", "test_duplicate#02"}: {false, false, false, nil}, + {"data.foo", "test_err"}: {true, false, false, nil}, + {"data.foo", "todo_test_skip"}: {false, false, true, nil}, + {"data.foo", "todo_test_skip_too"}: {false, false, true, nil}, + {"data.foo", "test_cases"}: {false, false, false, nil}, + {"data.foo", "test_duplicate#03"}: {false, false, false, nil}, + {"data.foo", "test_duplicate#04"}: {false, false, false, nil}, + {"data.bar", "test_duplicate"}: {false, false, false, nil}, + {"data.baz", "a.b.test_duplicate"}: {false, true, false, nil}, + {"data.baz", "a.b[\"test_duplicate#01\"]"}: {false, false, false, nil}, + {"data.baz", "a.b[\"test_duplicate#02\"]"}: {false, false, false, nil}, + }, + }, + { + note: "no filter", + regex: "", + tests: expectedTestResults{ + {"data.foo", "test_pass"}: {false, false, false, nil}, + {"data.foo", "test_fail"}: {false, true, false, nil}, + {"data.foo", "test_fail_non_bool"}: {false, true, false, nil}, + {"data.foo", "test_duplicate"}: {false, true, false, nil}, + {"data.foo", "test_duplicate#01"}: {false, false, false, nil}, + {"data.foo", "test_duplicate#02"}: {false, false, false, nil}, + {"data.foo", "test_err"}: {true, false, false, nil}, + {"data.foo", "todo_test_skip"}: {false, false, true, nil}, + {"data.foo", "todo_test_skip_too"}: {false, false, true, nil}, + {"data.foo", "test_cases"}: {false, false, false, nil}, + {"data.foo", "test_duplicate#03"}: {false, false, false, nil}, + {"data.foo", "test_duplicate#04"}: {false, false, false, nil}, + {"data.bar", "test_duplicate"}: {false, false, false, nil}, + {"data.baz", "a.b.test_duplicate"}: {false, true, false, nil}, + {"data.baz", "a.b[\"test_duplicate#01\"]"}: {false, false, false, nil}, + {"data.baz", "a.b[\"test_duplicate#02\"]"}: {false, false, false, nil}, + }, + }, + { + note: "no tests match", + regex: "^$", + tests: nil, + }, + { + note: "single package name", + regex: "bar", + tests: expectedTestResults{ + {"data.bar", "test_duplicate"}: {false, false, false, nil}, + }, + }, + { + note: "single package explicit", + regex: "data.bar.test_duplicate", + tests: expectedTestResults{ + {"data.bar", "test_duplicate"}: {false, false, false, nil}, + }, + }, + { + note: "single test", + regex: "test_pass", + tests: expectedTestResults{ + {"data.foo", "test_pass"}: {false, false, false, nil}, + }, + }, + { + note: "single test explicit", + regex: "data.foo.test_pass", + tests: expectedTestResults{ + {"data.foo", "test_pass"}: {false, false, false, nil}, + }, + }, + { + note: "single test skipped explicit", + regex: "data.foo.todo_test_skip_too", + tests: expectedTestResults{ + {"data.foo", "todo_test_skip_too"}: {false, false, true, nil}, + }, + }, + { + note: "wildcards", + regex: "^.*foo.*_fail.*$", + tests: expectedTestResults{ + {"data.foo", "test_fail"}: {false, true, false, nil}, + {"data.foo", "test_fail_non_bool"}: {false, true, false, nil}, + }, + }, + { + note: "mixed", + regex: "(bar|data.foo.test_pass)", + tests: expectedTestResults{ + {"data.foo", "test_pass"}: {false, false, false, nil}, + {"data.bar", "test_duplicate"}: {false, false, false, nil}, + }, + }, + { + note: "case insensitive", + regex: "(?i)DATA.BAR", + tests: expectedTestResults{ + {"data.bar", "test_duplicate"}: {false, false, false, nil}, + }, + }, + { + note: "matching ref rule halfways", + regex: "data.baz.a", + tests: expectedTestResults{ + {"data.baz", "a.b.test_duplicate"}: {false, true, false, nil}, + {"data.baz", "a.b[\"test_duplicate#01\"]"}: {false, false, false, nil}, + {"data.baz", "a.b[\"test_duplicate#02\"]"}: {false, false, false, nil}, + }, + }, + { + note: "matching sub-test rule", + regex: "data.foo.test_cases", + tests: expectedTestResults{ + {"data.foo", "test_cases"}: {false, false, false, nil}, + }, + }, + } + + test.WithTempFS(files, func(d string) { + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + conf := testRunConfig{filter: tc.regex} + rs, _ := doTestRunWithTmpDir(t, d, conf) + validateTestResults(t, tc.tests, rs, conf) + }) + } + }) +} + +func TestRunnerCancel(t *testing.T) { + testCancel(t, false) +} + +func TestRunnerCancelBenchmark(t *testing.T) { + testCancel(t, true) +} + +func testCancel(t *testing.T, bench bool) { + + registerSleepBuiltin() + + ctx, cancel := context.WithCancel(context.Background()) + + module := `package foo + import rego.v1 + + test_1 if { test.sleep("100ms") } + test_2 if { true }` + + files := map[string]string{ + "/a_test.rego": module, + } + + test.WithTempFS(files, func(d string) { + paths := []string{d} + modules, store, err := tester.Load(paths, nil) + if err != nil { + t.Fatal(err) + } + + txn := storage.NewTransactionOrDie(ctx, store) + runner := tester.NewRunner().SetStore(store).SetModules(modules) + + // Everything below uses a canceled context.. + cancel() + + var ch chan *tester.Result + if bench { + ch, err = runner.RunBenchmarks(ctx, txn, tester.BenchmarkOptions{}) + } else { + ch, err = runner.RunTests(ctx, txn) + } + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + var results []*tester.Result + for r := range ch { + results = append(results, r) + } + + if len(results) != 0 { + t.Fatalf("Expected no tests to be run but, got: %d", len(results)) + } + }) +} + +func TestRunnerTimeout(t *testing.T) { + test.Skip(t) + testTimeout(t, false) +} + +func TestRunnerTimeoutBenchmark(t *testing.T) { + testTimeout(t, true) +} + +func testTimeout(t *testing.T, bench bool) { + registerSleepBuiltin() + + ctx := context.Background() + + files := map[string]string{ + "/a_test.rego": `package foo + import rego.v1 + + test_1 if { test.sleep("100ms") } + + # 1ms is low enough for a single test to pass, + # but long enough for benchmark to timeout + test_2 if { test.sleep("1ms") }`, + } + + test.WithTempFS(files, func(d string) { + paths := []string{d} + modules, store, err := tester.Load(paths, nil) + if err != nil { + t.Fatal(err) + } + duration, err := time.ParseDuration("15ms") + if err != nil { + t.Fatal(err) + } + + txn := storage.NewTransactionOrDie(ctx, store) + runner := tester.NewRunner().SetTimeout(duration).SetStore(store).SetModules(modules) + + var ch chan *tester.Result + if bench { + ch, err = runner.RunBenchmarks(ctx, txn, tester.BenchmarkOptions{}) + } else { + ch, err = runner.RunTests(ctx, txn) + } + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + var results []*tester.Result + for r := range ch { + results = append(results, r) + } + + if bench { + if !topdown.IsCancel(results[1].Error) { + t.Fatalf("Expected cancel error for second test but got: %v", results[1].Error) + } + } else { + if !topdown.IsCancel(results[1].Error) { + t.Fatalf("Expected test to have timed out") + } + } + }) +} + +func TestRunnerPrintOutput(t *testing.T) { + + files := map[string]string{ + "/test.rego": `package test + import rego.v1 + + test_a if { print("A") } + test_b if { false; print("B") } + test_c if { print("C"); false } + p.q.r.test_d if { print("D") }`, + "/test2.rego": `package test + import rego.v1 + + test_d if { print("D") } + test_e if { false; print("E") } + test_f if { print("F"); false } + p.q.r.test_g if { print("G") }`, + "/test3.rego": `package test + import rego.v1 + + test_h if { print("H") } + test_i if { false; print("I") } + test_j if { print("J"); false } + p.q.r.test_k if { print("K") }`, + } + + ctx := context.Background() + + test.WithTempFS(files, func(d string) { + paths := []string{d} + modules, store, err := tester.Load(paths, nil) + if err != nil { + t.Fatal(err) + } + + txn := storage.NewTransactionOrDie(ctx, store) + runner := tester.NewRunner().SetStore(store).SetModules(modules).CapturePrintOutput(true) + ch, err := runner.RunTests(ctx, txn) + if err != nil { + t.Fatal(err) + } + + exp := map[string]map[string]string{ + "test.rego": { + "test_a": "A\n", + "test_b": "", + "test_c": "C\n", + "p.q.r.test_d": "D\n", + }, + "test2.rego": { + "test_d": "D\n", + "test_e": "", + "test_f": "F\n", + "p.q.r.test_g": "G\n", + }, + "test3.rego": { + "test_h": "H\n", + "test_i": "", + "test_j": "J\n", + "p.q.r.test_k": "K\n", + }, + } + + got := map[string]string{} + var lastFile string + for r := range ch { + if lastFile == "" { + lastFile = filepath.Base(r.Location.File) + } else if lastFile != filepath.Base(r.Location.File) { + // assert that all expected results for the file has been received + // the individual files could be out of order, but it has to be grouped by file + if !maps.Equal(exp[lastFile], got) { + t.Fatal("expected:", exp, "got:", got) + } + + // clear got for the next file + got = map[string]string{} + lastFile = filepath.Base(r.Location.File) + } + + got[r.Name] = string(r.Output) + } + + // check the last file + if !maps.Equal(exp[lastFile], got) { + t.Fatal("expected:", exp, "got:", got) + } + }) +} + +func registerSleepBuiltin() { + ast.RegisterBuiltin(&ast.Builtin{ + Name: "test.sleep", + Decl: types.NewFunction( + types.Args(types.S), + types.Nl, + ), + }) + + topdown.RegisterBuiltinFunc("test.sleep", func(_ topdown.BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + d, _ := time.ParseDuration(string(operands[0].Value.(ast.String))) + time.Sleep(d) + return iter(ast.NullTerm()) + }) +} + +func TestRunnerWithCustomBuiltin(t *testing.T) { + + var myBuiltinDecl = &ast.Builtin{ + Name: "my_sum", + Decl: types.NewFunction( + types.Args( + types.N, + types.N, + ), + types.N, + ), + } + + var myBuiltin = &tester.Builtin{ + Decl: myBuiltinDecl, + Func: rego.Function2( + ®o.Function{ + Name: myBuiltinDecl.Name, + Decl: myBuiltinDecl.Decl, + }, + func(_ rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error) { + var num1, num2 int + if err := ast.As(a.Value, &num1); err != nil { + return nil, err + } + if err := ast.As(b.Value, &num2); err != nil { + return nil, err + } + return ast.IntNumberTerm(num1 + num2), nil + }, + ), + } + + files := map[string]string{ + "/test.rego": `package test + import rego.v1 + + test_a if { my_sum(2,3) == 5 } + test_b if { my_sum(5,4) == 1 } + test_c if { my_sum(4,1.0) == 5 }`, + } + + ctx := context.Background() + + test.WithTempFS(files, func(d string) { + paths := []string{d} + modules, store, err := tester.Load(paths, nil) + if err != nil { + t.Fatal(err) + } + txn := storage.NewTransactionOrDie(ctx, store) + runner := tester.NewRunner().SetStore(store).SetModules(modules).AddCustomBuiltins([]*tester.Builtin{myBuiltin}) + ch, err := runner.RunTests(ctx, txn) + if err != nil { + t.Fatal(err) + } + + var results []*tester.Result + + for r := range ch { + results = append(results, r) + } + + exp := map[string]bool{ + "test_a": true, + "test_b": false, + "test_c": false, + } + + got := map[string]bool{} + + for _, tr := range results { + got[tr.Name] = tr.Pass() + } + + if !maps.Equal(exp, got) { + t.Fatal("expected:", exp, "got:", got) + } + }) +} + +func TestRunnerWithBuiltinErrors(t *testing.T) { + const ruleTemplate = `package test + import rego.v1 + + test_json_parsing if { + x := json.unmarshal("%s") + x.test == 123 + }` + + testCases := []struct { + desc string + json string + builtinErrors bool + wantErr bool + }{ + { + desc: "Valid JSON with flag enabled does not raise an error", + json: `{\"test\": 123}`, + builtinErrors: true, + }, + { + desc: "Invalid JSON with flag enabled raises an error", + json: `test: 123`, + builtinErrors: true, + wantErr: true, + }, + { + desc: "Invalid JSON with flag disabled does not raise an error", + json: `test: 123`, + }, + } + + ctx := context.Background() + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + files := map[string]string{ + "builtin_error_test.rego": fmt.Sprintf(ruleTemplate, tc.json), + } + + test.WithTempFS(files, func(d string) { + paths := []string{d} + modules, store, err := tester.Load(paths, nil) + if err != nil { + t.Fatal(err) + } + txn := storage.NewTransactionOrDie(ctx, store) + runner := tester. + NewRunner(). + SetStore(store). + SetModules(modules). + RaiseBuiltinErrors(tc.builtinErrors) + + ch, err := runner.RunTests(ctx, txn) + if err != nil { + t.Fatal(err) + } + for result := range ch { + if gotErr := result.Error != nil; gotErr != tc.wantErr { + t.Errorf("wantErr = %v, gotErr = %v", tc.wantErr, gotErr) + } + } + }) + }) + } +} + +func TestLoad_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module string + expErrs []string + }{ + { + note: "v0 module", // NOT default rego-version + module: `package test + +p[x] { + x = "a" +} + +test_p { + p["a"] +}`, + expErrs: []string{ + "test.rego:3: rego_parse_error: `if` keyword is required before rule body", + "test.rego:3: rego_parse_error: `contains` keyword is required for partial set rules", + "test.rego:7: rego_parse_error: `if` keyword is required before rule body", + }, + }, + { + note: "import rego.v1", + module: `package test +import rego.v1 + +p contains x if { + x := "a" +} + +test_p if { + "a" in p +}`, + }, + { + note: "v1 module", // default rego-version + module: `package test + +p contains x if { + x := "a" +} + +test_p if { + "a" in p +}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + files := map[string]string{ + "test.rego": tc.module, + } + + test.WithTempFS(files, func(root string) { + modules, store, err := tester.Load([]string{root}, nil) + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%q\n\nbut got:\n\n%v", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if modules == nil { + t.Fatalf("Expected modules to be non-nil") + } + + if store == nil { + t.Fatalf("Expected store to be non-nil") + } + } + }) + }) + } +} + +func TestRun_DefaultRegoVersion(t *testing.T) { + tests := []struct { + note string + module ast.Module + expErrs []string + }{ + { + note: "no v1 violations", + module: ast.Module{ + Package: ast.MustParsePackage(`package test`), + Rules: []*ast.Rule{ + ast.MustParseRule(`p[x] { x = "a" }`), + ast.MustParseRule(`test_p { p["a"] }`), + }, + }, + }, + { + note: "v1 violations", + module: ast.Module{ + Package: ast.MustParsePackage(`package test`), + Imports: ast.MustParseImports(` + import data.foo + import data.bar as foo + `), + Rules: []*ast.Rule{ + ast.MustParseRule(`p[x] { x = "a" }`), + ast.MustParseRule(`test_p { p["a"] }`), + }, + }, + expErrs: []string{ + "rego_compile_error: import must not shadow import data.foo", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + + modules := map[string]*ast.Module{ + "test": &tc.module, + } + + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + runner := tester.NewRunner(). + SetStore(store). + SetModules(modules). + SetTimeout(10 * time.Second) + + ch, err := runner.RunTests(ctx, txn) + + if len(tc.expErrs) > 0 { + if err == nil { + t.Fatalf("Expected error but got nil") + } + + for _, expErr := range tc.expErrs { + if !strings.Contains(err.Error(), expErr) { + t.Fatalf("Expected error to contain:\n\n%q\n\nbut got:\n\n%v", expErr, err) + } + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + var rs []*tester.Result + for r := range ch { + rs = append(rs, r) + } + + if len(rs) != 1 { + t.Fatalf("Expected exactly one result but got: %v", rs) + } + + if rs[0].Fail { + t.Fatalf("Expected test to pass but it failed") + } + } + }) + } +} + +func TestReporterFormatsWithExplicitParallel(t *testing.T) { + tests := []struct { + note string + parallel int + r func(writer io.Writer) tester.Reporter + exp func(string) + }{ + { + note: "Pretty Format", + parallel: 10, + r: func(w io.Writer) tester.Reporter { + return tester.PrettyReporter{ + Output: w, + } + }, + exp: func(output string) { + exp := `PASS: 4/4 +` + if exp != output { + t.Fatalf("Expected (%d bytes):\n\n%v\n\nGot (%d bytes):\n\n%v", len(exp), exp, len(output), output) + } + }, + }, + { + note: "JSON Format", + parallel: 10, + r: func(w io.Writer) tester.Reporter { + return tester.JSONReporter{ + Output: w, + } + }, + exp: func(output string) { + // the order of the tests and filepath and duration will be different each execution + var r []*tester.Result + if err := json.Unmarshal([]byte(output), &r); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if len(r) != 4 { + t.Fatalf("Expected exactly 4 results but got: %v", r) + } + }, + }, + { + note: "Go Bench Format", + parallel: 10, + r: func(w io.Writer) tester.Reporter { + return tester.PrettyReporter{ + Output: w, + BenchMarkGoBenchFormat: true, + } + }, + exp: func(output string) { + exp := `PASS: 4/4 +` + if exp != output { + t.Fatalf("Expected (%d bytes):\n\n%v\n\nGot (%d bytes):\n\n%v", len(exp), exp, len(output), output) + } + }, + }, + } + + files := map[string]string{ + "/test.rego": `package test + import rego.v1 + + test_a if { print("A") } + test_a if { print("A") } + test_a if { print("A") } + test_a if { print("A") }`, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + ctx := context.Background() + + test.WithTempFS(files, func(d string) { + paths := []string{d} + modules, store, err := tester.Load(paths, nil) + if err != nil { + t.Fatal(err) + } + + txn := storage.NewTransactionOrDie(ctx, store) + runner := tester.NewRunner().SetStore(store).SetModules(modules).CapturePrintOutput(true) + ch, err := runner.RunTests(ctx, txn) + if err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + + r := tc.r(&buf) + + if err := r.Report(ch); err != nil { + t.Fatal(err) + } + + str := buf.String() + + tc.exp(str) + }) + }) + } +} diff --git a/third_party/opa/v1/tester/test_tracer.go b/third_party/opa/v1/tester/test_tracer.go new file mode 100644 index 000000000000..1e0352aa5a3a --- /dev/null +++ b/third_party/opa/v1/tester/test_tracer.go @@ -0,0 +1,28 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package tester + +import "github.com/open-policy-agent/opa/v1/topdown" + +type TestQueryTracer struct { + topdown.BufferTracer +} + +func NewTestQueryTracer() *TestQueryTracer { + return &TestQueryTracer{} +} + +func (t *TestQueryTracer) TraceEvent(e topdown.Event) { + if e.Op == topdown.TestCaseOp { + t.BufferTracer.TraceEvent(e) + } +} + +func (t *TestQueryTracer) Events() []*topdown.Event { + if t == nil { + return nil + } + return t.BufferTracer +} diff --git a/third_party/opa/v1/topdown/aggregates.go b/third_party/opa/v1/topdown/aggregates.go new file mode 100644 index 000000000000..eec49f7b8867 --- /dev/null +++ b/third_party/opa/v1/topdown/aggregates.go @@ -0,0 +1,302 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "math/big" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func builtinCount(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch a := operands[0].Value.(type) { + case *ast.Array: + return iter(ast.InternedTerm(a.Len())) + case ast.Object: + return iter(ast.InternedTerm(a.Len())) + case ast.Set: + return iter(ast.InternedTerm(a.Len())) + case ast.String: + return iter(ast.InternedTerm(len([]rune(a)))) + } + return builtins.NewOperandTypeErr(1, operands[0].Value, "array", "object", "set", "string") +} + +func builtinSum(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch a := operands[0].Value.(type) { + case *ast.Array: + // Fast path for arrays of integers + is := 0 + nonInts := a.Until(func(x *ast.Term) bool { + if n, ok := x.Value.(ast.Number); ok { + if i, ok := n.Int(); ok { + is += i + return false + } + } + return true + }) + if !nonInts { + return iter(ast.InternedTerm(is)) + } + + // Non-integer values found, so we need to sum as floats. + sum := big.NewFloat(0) + err := a.Iter(func(x *ast.Term) error { + n, ok := x.Value.(ast.Number) + if !ok { + return builtins.NewOperandElementErr(1, a, x.Value, "number") + } + sum = new(big.Float).Add(sum, builtins.NumberToFloat(n)) + return nil + }) + if err != nil { + return err + } + return iter(ast.NewTerm(builtins.FloatToNumber(sum))) + case ast.Set: + // Fast path for sets of integers + is := 0 + nonInts := a.Until(func(x *ast.Term) bool { + if n, ok := x.Value.(ast.Number); ok { + if i, ok := n.Int(); ok { + is += i + return false + } + } + return true + }) + if !nonInts { + return iter(ast.InternedTerm(is)) + } + + sum := big.NewFloat(0) + err := a.Iter(func(x *ast.Term) error { + n, ok := x.Value.(ast.Number) + if !ok { + return builtins.NewOperandElementErr(1, a, x.Value, "number") + } + sum = new(big.Float).Add(sum, builtins.NumberToFloat(n)) + return nil + }) + if err != nil { + return err + } + return iter(ast.NewTerm(builtins.FloatToNumber(sum))) + } + return builtins.NewOperandTypeErr(1, operands[0].Value, "set", "array") +} + +func builtinProduct(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch a := operands[0].Value.(type) { + case *ast.Array: + product := big.NewFloat(1) + err := a.Iter(func(x *ast.Term) error { + n, ok := x.Value.(ast.Number) + if !ok { + return builtins.NewOperandElementErr(1, a, x.Value, "number") + } + product = new(big.Float).Mul(product, builtins.NumberToFloat(n)) + return nil + }) + if err != nil { + return err + } + return iter(ast.NewTerm(builtins.FloatToNumber(product))) + case ast.Set: + product := big.NewFloat(1) + err := a.Iter(func(x *ast.Term) error { + n, ok := x.Value.(ast.Number) + if !ok { + return builtins.NewOperandElementErr(1, a, x.Value, "number") + } + product = new(big.Float).Mul(product, builtins.NumberToFloat(n)) + return nil + }) + if err != nil { + return err + } + return iter(ast.NewTerm(builtins.FloatToNumber(product))) + } + return builtins.NewOperandTypeErr(1, operands[0].Value, "set", "array") +} + +func builtinMax(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch a := operands[0].Value.(type) { + case *ast.Array: + if a.Len() == 0 { + return nil + } + max := ast.InternedNullTerm.Value + a.Foreach(func(x *ast.Term) { + if ast.Compare(max, x.Value) <= 0 { + max = x.Value + } + }) + return iter(ast.NewTerm(max)) + case ast.Set: + if a.Len() == 0 { + return nil + } + max, err := a.Reduce(ast.InternedNullTerm, func(max *ast.Term, elem *ast.Term) (*ast.Term, error) { + if ast.Compare(max, elem) <= 0 { + return elem, nil + } + return max, nil + }) + if err != nil { + return err + } + return iter(max) + } + + return builtins.NewOperandTypeErr(1, operands[0].Value, "set", "array") +} + +func builtinMin(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch a := operands[0].Value.(type) { + case *ast.Array: + if a.Len() == 0 { + return nil + } + min := a.Elem(0).Value + a.Foreach(func(x *ast.Term) { + if ast.Compare(min, x.Value) >= 0 { + min = x.Value + } + }) + return iter(ast.NewTerm(min)) + case ast.Set: + if a.Len() == 0 { + return nil + } + min, err := a.Reduce(ast.InternedNullTerm, func(min *ast.Term, elem *ast.Term) (*ast.Term, error) { + // The null term is considered to be less than any other term, + // so in order for min of a set to make sense, we need to check + // for it. + if min.Value.Compare(ast.InternedNullTerm.Value) == 0 { + return elem, nil + } + + if ast.Compare(min, elem) >= 0 { + return elem, nil + } + return min, nil + }) + if err != nil { + return err + } + return iter(min) + } + + return builtins.NewOperandTypeErr(1, operands[0].Value, "set", "array") +} + +func builtinSort(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch a := operands[0].Value.(type) { + case *ast.Array: + return iter(ast.NewTerm(a.Sorted())) + case ast.Set: + return iter(ast.NewTerm(a.Sorted())) + } + return builtins.NewOperandTypeErr(1, operands[0].Value, "set", "array") +} + +func builtinAll(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch val := operands[0].Value.(type) { + case ast.Set: + res := true + match := ast.InternedTerm(true) + val.Until(func(term *ast.Term) bool { + if !match.Equal(term) { + res = false + return true + } + return false + }) + return iter(ast.InternedTerm(res)) + case *ast.Array: + res := true + match := ast.InternedTerm(true) + val.Until(func(term *ast.Term) bool { + if !match.Equal(term) { + res = false + return true + } + return false + }) + return iter(ast.InternedTerm(res)) + default: + return builtins.NewOperandTypeErr(1, operands[0].Value, "array", "set") + } +} + +func builtinAny(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch val := operands[0].Value.(type) { + case ast.Set: + res := val.Len() > 0 && val.Contains(ast.InternedTerm(true)) + return iter(ast.InternedTerm(res)) + case *ast.Array: + res := false + match := ast.InternedTerm(true) + val.Until(func(term *ast.Term) bool { + if match.Equal(term) { + res = true + return true + } + return false + }) + return iter(ast.InternedTerm(res)) + default: + return builtins.NewOperandTypeErr(1, operands[0].Value, "array", "set") + } +} + +func builtinMember(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + containee := operands[0] + switch c := operands[1].Value.(type) { + case ast.Set: + return iter(ast.InternedTerm(c.Contains(containee))) + case *ast.Array: + for i := range c.Len() { + if c.Elem(i).Value.Compare(containee.Value) == 0 { + return iter(ast.InternedTerm(true)) + } + } + return iter(ast.InternedTerm(false)) + case ast.Object: + return iter(ast.InternedTerm(c.Until(func(_, v *ast.Term) bool { + return v.Value.Compare(containee.Value) == 0 + }))) + } + return iter(ast.InternedTerm(false)) +} + +func builtinMemberWithKey(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + key, val := operands[0], operands[1] + switch c := operands[2].Value.(type) { + case interface{ Get(*ast.Term) *ast.Term }: + ret := false + if act := c.Get(key); act != nil { + ret = act.Value.Compare(val.Value) == 0 + } + return iter(ast.InternedTerm(ret)) + } + return iter(ast.InternedTerm(false)) +} + +func init() { + RegisterBuiltinFunc(ast.Count.Name, builtinCount) + RegisterBuiltinFunc(ast.Sum.Name, builtinSum) + RegisterBuiltinFunc(ast.Product.Name, builtinProduct) + RegisterBuiltinFunc(ast.Max.Name, builtinMax) + RegisterBuiltinFunc(ast.Min.Name, builtinMin) + RegisterBuiltinFunc(ast.Sort.Name, builtinSort) + RegisterBuiltinFunc(ast.Any.Name, builtinAny) + RegisterBuiltinFunc(ast.All.Name, builtinAll) + RegisterBuiltinFunc(ast.Member.Name, builtinMember) + RegisterBuiltinFunc(ast.MemberWithKey.Name, builtinMemberWithKey) +} diff --git a/third_party/opa/v1/topdown/aggregates_bench_test.go b/third_party/opa/v1/topdown/aggregates_bench_test.go new file mode 100644 index 000000000000..fed16a8788fa --- /dev/null +++ b/third_party/opa/v1/topdown/aggregates_bench_test.go @@ -0,0 +1,119 @@ +package topdown + +import ( + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// 36.80 ns/op 0 B/op 0 allocs/op +func BenchmarkSumIntArray(b *testing.B) { + bcx := BuiltinContext{} + arr := ast.ArrayTerm( + ast.InternedTerm(1), + ast.InternedTerm(2), + ast.InternedTerm(3), + ast.InternedTerm(4), + ast.InternedTerm(5), + ast.InternedTerm(6), + ) + exp := ast.InternedTerm(21) + + verify := func(x *ast.Term) error { + // Can do simple equality check since we are using interned terms + if x != exp { + return fmt.Errorf("expected %v, got %v", exp.Value, x.Value) + } + return nil + } + + for range b.N { + err := builtinSum(bcx, []*ast.Term{arr}, verify) + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + } +} + +// 1857 ns/op 2736 B/op 80 allocs/op +func BenchmarkSumFloatArray(b *testing.B) { + bcx := BuiltinContext{} + arr := ast.ArrayTerm( + ast.FloatNumberTerm(1.1), + ast.FloatNumberTerm(2.2), + ast.FloatNumberTerm(3.3), + ast.FloatNumberTerm(4.4), + ast.FloatNumberTerm(5.5), + ast.FloatNumberTerm(6.6), + ) + exp := ast.FloatNumberTerm(23.1) + + verify := func(x *ast.Term) error { + if x.Value != exp.Value { + return fmt.Errorf("expected %v, got %v", exp.Value, x.Value) + } + return nil + } + + for range b.N { + err := builtinSum(bcx, []*ast.Term{arr}, verify) + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + } +} + +func BenchmarkSumIntSet(b *testing.B) { + bcx := BuiltinContext{} + set := ast.SetTerm( + ast.InternedTerm(1), + ast.InternedTerm(2), + ast.InternedTerm(3), + ast.InternedTerm(4), + ast.InternedTerm(5), + ast.InternedTerm(6), + ) + exp := ast.InternedTerm(21) + + verify := func(x *ast.Term) error { + if x != exp { + return fmt.Errorf("expected %v, got %v", exp.Value, x.Value) + } + return nil + } + + for range b.N { + err := builtinSum(bcx, []*ast.Term{set}, verify) + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + } +} + +func BenchmarkSumFloatSet(b *testing.B) { + bcx := BuiltinContext{} + set := ast.SetTerm( + ast.FloatNumberTerm(1.1), + ast.FloatNumberTerm(2.2), + ast.FloatNumberTerm(3.3), + ast.FloatNumberTerm(4.4), + ast.FloatNumberTerm(5.5), + ast.FloatNumberTerm(6.6), + ) + exp := ast.FloatNumberTerm(23.1) + + verify := func(x *ast.Term) error { + if x.Value != exp.Value { + return fmt.Errorf("expected %v, got %v", exp.Value, x.Value) + } + return nil + } + + for range b.N { + err := builtinSum(bcx, []*ast.Term{set}, verify) + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + } +} diff --git a/third_party/opa/v1/topdown/arithmetic.go b/third_party/opa/v1/topdown/arithmetic.go new file mode 100644 index 000000000000..91190330fa32 --- /dev/null +++ b/third_party/opa/v1/topdown/arithmetic.go @@ -0,0 +1,240 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "errors" + "math/big" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +type arithArity1 func(a *big.Float) (*big.Float, error) +type arithArity2 func(a, b *big.Float) (*big.Float, error) + +func arithAbs(a *big.Float) (*big.Float, error) { + return a.Abs(a), nil +} + +var halfAwayFromZero = big.NewFloat(0.5) + +func arithRound(a *big.Float) (*big.Float, error) { + var i *big.Int + if a.Signbit() { + i, _ = new(big.Float).Sub(a, halfAwayFromZero).Int(nil) + } else { + i, _ = new(big.Float).Add(a, halfAwayFromZero).Int(nil) + } + return new(big.Float).SetInt(i), nil +} + +func arithCeil(a *big.Float) (*big.Float, error) { + i, _ := a.Int(nil) + f := new(big.Float).SetInt(i) + + if f.Signbit() || a.Cmp(f) == 0 { + return f, nil + } + + return new(big.Float).Add(f, big.NewFloat(1.0)), nil +} + +func arithFloor(a *big.Float) (*big.Float, error) { + i, _ := a.Int(nil) + f := new(big.Float).SetInt(i) + + if !f.Signbit() || a.Cmp(f) == 0 { + return f, nil + } + + return new(big.Float).Sub(f, big.NewFloat(1.0)), nil +} + +func builtinPlus(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + n1, err := builtins.NumberOperand(operands[0].Value, 1) + if err != nil { + return err + } + n2, err := builtins.NumberOperand(operands[1].Value, 2) + if err != nil { + return err + } + + x, ok1 := n1.Int() + y, ok2 := n2.Int() + + if ok1 && ok2 && inSmallIntRange(x) && inSmallIntRange(y) { + return iter(ast.InternedTerm(x + y)) + } + + f := new(big.Float).Add(builtins.NumberToFloat(n1), builtins.NumberToFloat(n2)) + + return iter(ast.NewTerm(builtins.FloatToNumber(f))) +} + +func builtinMultiply(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + n1, err := builtins.NumberOperand(operands[0].Value, 1) + if err != nil { + return err + } + n2, err := builtins.NumberOperand(operands[1].Value, 2) + if err != nil { + return err + } + + x, ok1 := n1.Int() + y, ok2 := n2.Int() + + if ok1 && ok2 && inSmallIntRange(x) && inSmallIntRange(y) { + return iter(ast.InternedTerm(x * y)) + } + + f := new(big.Float).Mul(builtins.NumberToFloat(n1), builtins.NumberToFloat(n2)) + + return iter(ast.NewTerm(builtins.FloatToNumber(f))) +} + +func arithDivide(a, b *big.Float) (*big.Float, error) { + i, acc := b.Int64() + if acc == big.Exact && i == 0 { + return nil, errors.New("divide by zero") + } + return new(big.Float).Quo(a, b), nil +} + +func arithRem(a, b *big.Int) (*big.Int, error) { + if b.Int64() == 0 { + return nil, errors.New("modulo by zero") + } + return new(big.Int).Rem(a, b), nil +} + +func builtinArithArity1(fn arithArity1) BuiltinFunc { + return func(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + n, err := builtins.NumberOperand(operands[0].Value, 1) + if err != nil { + return err + } + f, err := fn(builtins.NumberToFloat(n)) + if err != nil { + return err + } + return iter(ast.NewTerm(builtins.FloatToNumber(f))) + } +} + +func builtinArithArity2(fn arithArity2) BuiltinFunc { + return func(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + n1, err := builtins.NumberOperand(operands[0].Value, 1) + if err != nil { + return err + } + n2, err := builtins.NumberOperand(operands[1].Value, 2) + if err != nil { + return err + } + f, err := fn(builtins.NumberToFloat(n1), builtins.NumberToFloat(n2)) + if err != nil { + return err + } + return iter(ast.NewTerm(builtins.FloatToNumber(f))) + } +} + +func builtinMinus(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + n1, ok1 := operands[0].Value.(ast.Number) + n2, ok2 := operands[1].Value.(ast.Number) + + if ok1 && ok2 { + + x, okx := n1.Int() + y, oky := n2.Int() + + if okx && oky && inSmallIntRange(x) && inSmallIntRange(y) { + return iter(ast.InternedTerm(x - y)) + } + + f := new(big.Float).Sub(builtins.NumberToFloat(n1), builtins.NumberToFloat(n2)) + + return iter(ast.NewTerm(builtins.FloatToNumber(f))) + } + + s1, ok3 := operands[0].Value.(ast.Set) + s2, ok4 := operands[1].Value.(ast.Set) + + if ok3 && ok4 { + diff := s1.Diff(s2) + if diff.Len() == 0 { + return iter(ast.InternedEmptySet) + } + return iter(ast.NewTerm(diff)) + } + + if !ok1 && !ok3 { + return builtins.NewOperandTypeErr(1, operands[0].Value, "number", "set") + } + + if ok2 { + return builtins.NewOperandTypeErr(2, operands[1].Value, "set") + } + + return builtins.NewOperandTypeErr(2, operands[1].Value, "number") +} + +func builtinRem(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + n1, ok1 := operands[0].Value.(ast.Number) + n2, ok2 := operands[1].Value.(ast.Number) + + if ok1 && ok2 { + + x, okx := n1.Int() + y, oky := n2.Int() + + if okx && oky && inSmallIntRange(x) && inSmallIntRange(y) { + if y == 0 { + return errors.New("modulo by zero") + } + + return iter(ast.InternedTerm(x % y)) + } + + op1, err1 := builtins.NumberToInt(n1) + op2, err2 := builtins.NumberToInt(n2) + + if err1 != nil || err2 != nil { + return errors.New("modulo on floating-point number") + } + + i, err := arithRem(op1, op2) + if err != nil { + return err + } + return iter(ast.NewTerm(builtins.IntToNumber(i))) + } + + if !ok1 { + return builtins.NewOperandTypeErr(1, operands[0].Value, "number") + } + + return builtins.NewOperandTypeErr(2, operands[1].Value, "number") +} + +func inSmallIntRange(num int) bool { + return -1000 < num && num < 1000 +} + +func init() { + RegisterBuiltinFunc(ast.Abs.Name, builtinArithArity1(arithAbs)) + RegisterBuiltinFunc(ast.Round.Name, builtinArithArity1(arithRound)) + RegisterBuiltinFunc(ast.Ceil.Name, builtinArithArity1(arithCeil)) + RegisterBuiltinFunc(ast.Floor.Name, builtinArithArity1(arithFloor)) + RegisterBuiltinFunc(ast.Plus.Name, builtinPlus) + RegisterBuiltinFunc(ast.Minus.Name, builtinMinus) + RegisterBuiltinFunc(ast.Multiply.Name, builtinMultiply) + RegisterBuiltinFunc(ast.Divide.Name, builtinArithArity2(arithDivide)) + RegisterBuiltinFunc(ast.Rem.Name, builtinRem) +} diff --git a/third_party/opa/v1/topdown/array.go b/third_party/opa/v1/topdown/array.go new file mode 100644 index 000000000000..526e3ed26ded --- /dev/null +++ b/third_party/opa/v1/topdown/array.go @@ -0,0 +1,105 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func builtinArrayConcat(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + arrA, err := builtins.ArrayOperand(operands[0].Value, 1) + if err != nil { + return err + } + + arrB, err := builtins.ArrayOperand(operands[1].Value, 2) + if err != nil { + return err + } + + if arrA.Len() == 0 { + return iter(operands[1]) + } + if arrB.Len() == 0 { + return iter(operands[0]) + } + + arrC := make([]*ast.Term, arrA.Len()+arrB.Len()) + + i := 0 + arrA.Foreach(func(elemA *ast.Term) { + arrC[i] = elemA + i++ + }) + + arrB.Foreach(func(elemB *ast.Term) { + arrC[i] = elemB + i++ + }) + + return iter(ast.ArrayTerm(arrC...)) +} + +func builtinArraySlice(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + arr, err := builtins.ArrayOperand(operands[0].Value, 1) + if err != nil { + return err + } + + startIndex, err := builtins.IntOperand(operands[1].Value, 2) + if err != nil { + return err + } + + stopIndex, err := builtins.IntOperand(operands[2].Value, 3) + if err != nil { + return err + } + + // Clamp stopIndex to avoid out-of-range errors. If negative, clamp to zero. + // Otherwise, clamp to length of array. + if stopIndex < 0 { + stopIndex = 0 + } else if stopIndex > arr.Len() { + stopIndex = arr.Len() + } + + // Clamp startIndex to avoid out-of-range errors. If negative, clamp to zero. + // Otherwise, clamp to stopIndex to avoid to avoid cases like arr[1:0]. + if startIndex < 0 { + startIndex = 0 + } else if startIndex > stopIndex { + startIndex = stopIndex + } + + if startIndex == 0 && stopIndex >= arr.Len() { + return iter(operands[0]) + } + + return iter(ast.NewTerm(arr.Slice(startIndex, stopIndex))) +} + +func builtinArrayReverse(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + arr, err := builtins.ArrayOperand(operands[0].Value, 1) + if err != nil { + return err + } + + length := arr.Len() + reversedArr := make([]*ast.Term, length) + + for index := range length { + reversedArr[index] = arr.Elem(length - index - 1) + } + + return iter(ast.ArrayTerm(reversedArr...)) +} + +func init() { + RegisterBuiltinFunc(ast.ArrayConcat.Name, builtinArrayConcat) + RegisterBuiltinFunc(ast.ArraySlice.Name, builtinArraySlice) + RegisterBuiltinFunc(ast.ArrayReverse.Name, builtinArrayReverse) +} diff --git a/third_party/opa/v1/topdown/binary.go b/third_party/opa/v1/topdown/binary.go new file mode 100644 index 000000000000..05050dbf7d2a --- /dev/null +++ b/third_party/opa/v1/topdown/binary.go @@ -0,0 +1,50 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func builtinBinaryAnd(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + s1, err := builtins.SetOperand(operands[0].Value, 1) + if err != nil { + return err + } + + s2, err := builtins.SetOperand(operands[1].Value, 2) + if err != nil { + return err + } + + i := s1.Intersect(s2) + if i.Len() == 0 { + return iter(ast.InternedEmptySet) + } + + return iter(ast.NewTerm(i)) +} + +func builtinBinaryOr(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + s1, err := builtins.SetOperand(operands[0].Value, 1) + if err != nil { + return err + } + + s2, err := builtins.SetOperand(operands[1].Value, 2) + if err != nil { + return err + } + + return iter(ast.NewTerm(s1.Union(s2))) +} + +func init() { + RegisterBuiltinFunc(ast.And.Name, builtinBinaryAnd) + RegisterBuiltinFunc(ast.Or.Name, builtinBinaryOr) +} diff --git a/third_party/opa/v1/topdown/bindings.go b/third_party/opa/v1/topdown/bindings.go new file mode 100644 index 000000000000..9dd55f1ba741 --- /dev/null +++ b/third_party/opa/v1/topdown/bindings.go @@ -0,0 +1,401 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" +) + +type undo struct { + k *ast.Term + u *bindings +} + +func (u *undo) Undo() { + if u == nil { + // Allow call on zero value of Undo for ease-of-use. + return + } + if u.u == nil { + // Call on empty unifier undos a no-op unify operation. + return + } + u.u.delete(u.k) +} + +type bindings struct { + id uint64 + values bindingsArrayHashmap + instr *Instrumentation +} + +func newBindings(id uint64, instr *Instrumentation) *bindings { + values := newBindingsArrayHashmap() + return &bindings{id, values, instr} +} + +func (u *bindings) Iter(caller *bindings, iter func(*ast.Term, *ast.Term) error) error { + + var err error + + u.values.Iter(func(k *ast.Term, _ value) bool { + if err != nil { + return true + } + err = iter(k, u.PlugNamespaced(k, caller)) + + return false + }) + + return err +} + +func (u *bindings) Namespace(x ast.Node, caller *bindings) { + vis := namespacingVisitor{ + b: u, + caller: caller, + } + ast.NewGenericVisitor(vis.Visit).Walk(x) +} + +func (u *bindings) Plug(a *ast.Term) *ast.Term { + return u.PlugNamespaced(a, nil) +} + +func (u *bindings) PlugNamespaced(a *ast.Term, caller *bindings) *ast.Term { + if u != nil && u.instr != nil { + u.instr.startTimer(evalOpPlug) + t := u.plugNamespaced(a, caller) + u.instr.stopTimer(evalOpPlug) + return t + } + + return u.plugNamespaced(a, caller) +} + +func (u *bindings) plugNamespaced(a *ast.Term, caller *bindings) *ast.Term { + switch v := a.Value.(type) { + case ast.Var: + b, next := u.apply(a) + if a != b || u != next { + return next.plugNamespaced(b, caller) + } + return u.namespaceVar(b, caller) + case *ast.Array: + if a.IsGround() { + return a + } + cpy := *a + arr := make([]*ast.Term, v.Len()) + for i := range arr { + arr[i] = u.plugNamespaced(v.Elem(i), caller) + } + cpy.Value = ast.NewArray(arr...) + return &cpy + case ast.Object: + if a.IsGround() { + return a + } + cpy := *a + cpy.Value, _ = v.Map(func(k, v *ast.Term) (*ast.Term, *ast.Term, error) { + return u.plugNamespaced(k, caller), u.plugNamespaced(v, caller), nil + }) + return &cpy + case ast.Set: + if a.IsGround() { + return a + } + cpy := *a + cpy.Value, _ = v.Map(func(x *ast.Term) (*ast.Term, error) { + return u.plugNamespaced(x, caller), nil + }) + return &cpy + case ast.Ref: + cpy := *a + ref := make(ast.Ref, len(v)) + for i := range ref { + ref[i] = u.plugNamespaced(v[i], caller) + } + cpy.Value = ref + return &cpy + } + return a +} + +func (u *bindings) bind(a *ast.Term, b *ast.Term, other *bindings, und *undo) { + u.values.Put(a, value{ + u: other, + v: b, + }) + und.k = a + und.u = u +} + +func (u *bindings) apply(a *ast.Term) (*ast.Term, *bindings) { + // Early exit for non-var terms. Only vars are bound in the binding list, + // so the lookup below will always fail for non-var terms. In some cases, + // the lookup may be expensive as it has to hash the term (which for large + // inputs can be costly). + _, ok := a.Value.(ast.Var) + if !ok { + return a, u + } + val, ok := u.get(a) + if !ok { + return a, u + } + return val.u.apply(val.v) +} + +func (u *bindings) delete(v *ast.Term) { + u.values.Delete(v) +} + +func (u *bindings) get(v *ast.Term) (value, bool) { + if u == nil { + return value{}, false + } + return u.values.Get(v) +} + +func (u *bindings) String() string { + if u == nil { + return "()" + } + var buf []string + u.values.Iter(func(a *ast.Term, b value) bool { + buf = append(buf, fmt.Sprintf("%v: %v", a, b)) + return false + }) + return fmt.Sprintf("({%v}, %v)", strings.Join(buf, ", "), u.id) +} + +func (u *bindings) namespaceVar(v *ast.Term, caller *bindings) *ast.Term { + name, ok := v.Value.(ast.Var) + if !ok { + panic("illegal value") + } + if caller != nil && caller != u { + // Root documents (i.e., data, input) should never be namespaced because they + // are globally unique. + if !ast.RootDocumentNames.Contains(v) { + return ast.VarTerm(string(name) + strconv.FormatUint(u.id, 10)) + } + } + return v +} + +type value struct { + u *bindings + v *ast.Term +} + +func (v value) String() string { + return fmt.Sprintf("(%v, %d)", v.v, v.u.id) +} + +func (v value) equal(other *value) bool { + if v.u == other.u { + return v.v.Equal(other.v) + } + return false +} + +type namespacingVisitor struct { + b *bindings + caller *bindings +} + +func (vis namespacingVisitor) Visit(x any) bool { + switch x := x.(type) { + case *ast.ArrayComprehension: + x.Term = vis.namespaceTerm(x.Term) + ast.NewGenericVisitor(vis.Visit).Walk(x.Body) + return true + case *ast.SetComprehension: + x.Term = vis.namespaceTerm(x.Term) + ast.NewGenericVisitor(vis.Visit).Walk(x.Body) + return true + case *ast.ObjectComprehension: + x.Key = vis.namespaceTerm(x.Key) + x.Value = vis.namespaceTerm(x.Value) + ast.NewGenericVisitor(vis.Visit).Walk(x.Body) + return true + case *ast.Expr: + switch terms := x.Terms.(type) { + case []*ast.Term: + for i := 1; i < len(terms); i++ { + terms[i] = vis.namespaceTerm(terms[i]) + } + case *ast.Term: + x.Terms = vis.namespaceTerm(terms) + } + for _, w := range x.With { + w.Target = vis.namespaceTerm(w.Target) + w.Value = vis.namespaceTerm(w.Value) + } + } + return false +} + +func (vis namespacingVisitor) namespaceTerm(a *ast.Term) *ast.Term { + switch v := a.Value.(type) { + case ast.Var: + return vis.b.namespaceVar(a, vis.caller) + case *ast.Array: + if a.IsGround() { + return a + } + cpy := *a + arr := make([]*ast.Term, v.Len()) + for i := range arr { + arr[i] = vis.namespaceTerm(v.Elem(i)) + } + cpy.Value = ast.NewArray(arr...) + return &cpy + case ast.Object: + if a.IsGround() { + return a + } + cpy := *a + cpy.Value, _ = v.Map(func(k, v *ast.Term) (*ast.Term, *ast.Term, error) { + return vis.namespaceTerm(k), vis.namespaceTerm(v), nil + }) + return &cpy + case ast.Set: + if a.IsGround() { + return a + } + cpy := *a + cpy.Value, _ = v.Map(func(x *ast.Term) (*ast.Term, error) { + return vis.namespaceTerm(x), nil + }) + return &cpy + case ast.Ref: + cpy := *a + ref := make(ast.Ref, len(v)) + for i := range ref { + ref[i] = vis.namespaceTerm(v[i]) + } + cpy.Value = ref + return &cpy + } + return a +} + +const maxLinearScan = 16 + +// bindingsArrayHashMap uses an array with linear scan instead +// of a hash map for smaller # of entries. Hash maps start to +// show off their performance advantage only after 16 keys. +type bindingsArrayHashmap struct { + n int // Entries in the array. + a *[maxLinearScan]bindingArrayKeyValue + m map[ast.Var]bindingArrayKeyValue +} + +type bindingArrayKeyValue struct { + key *ast.Term + value value +} + +func newBindingsArrayHashmap() bindingsArrayHashmap { + return bindingsArrayHashmap{} +} + +func (b *bindingsArrayHashmap) Put(key *ast.Term, value value) { + if b.m == nil { + if b.a == nil { + b.a = new([maxLinearScan]bindingArrayKeyValue) + } else if i := b.find(key); i >= 0 { + b.a[i].value = value + return + } + + if b.n < maxLinearScan { + b.a[b.n] = bindingArrayKeyValue{key, value} + b.n++ + return + } + + // Array is full, revert to using the hash map instead. + + b.m = make(map[ast.Var]bindingArrayKeyValue, maxLinearScan+1) + for _, kv := range *b.a { + b.m[kv.key.Value.(ast.Var)] = bindingArrayKeyValue{kv.key, kv.value} + } + b.m[key.Value.(ast.Var)] = bindingArrayKeyValue{key, value} + + b.n = 0 + return + } + + b.m[key.Value.(ast.Var)] = bindingArrayKeyValue{key, value} +} + +func (b *bindingsArrayHashmap) Get(key *ast.Term) (value, bool) { + if b.m == nil { + if i := b.find(key); i >= 0 { + return b.a[i].value, true + } + + return value{}, false + } + + v, ok := b.m[key.Value.(ast.Var)] + if ok { + return v.value, true + } + + return value{}, false +} + +func (b *bindingsArrayHashmap) Delete(key *ast.Term) { + if b.m == nil { + if i := b.find(key); i >= 0 { + n := b.n - 1 + if i < n { + b.a[i] = b.a[n] + } + + b.n = n + } + return + } + + delete(b.m, key.Value.(ast.Var)) +} + +func (b *bindingsArrayHashmap) Iter(f func(k *ast.Term, v value) bool) { + if b.m == nil { + for i := range b.n { + if f(b.a[i].key, b.a[i].value) { + return + } + } + return + } + + for _, v := range b.m { + if f(v.key, v.value) { + return + } + } +} + +func (b *bindingsArrayHashmap) find(key *ast.Term) int { + v := key.Value.(ast.Var) + for i := range b.n { + if b.a[i].key.Value.(ast.Var) == v { + return i + } + } + + return -1 +} diff --git a/third_party/opa/v1/topdown/bindings_test.go b/third_party/opa/v1/topdown/bindings_test.go new file mode 100644 index 000000000000..a8204c95f018 --- /dev/null +++ b/third_party/opa/v1/topdown/bindings_test.go @@ -0,0 +1,103 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "strconv" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestBindingsZeroValues(t *testing.T) { + t.Parallel() + + var unifier *bindings + + // Plugging + result := unifier.Plug(term("x")) + exp := term("x") + if !result.Equal(exp) { + t.Fatalf("Expected %v but got %v", exp, result) + } + + // String + if unifier.String() != "()" { + t.Fatalf("Expected empty binding list but got: %v", unifier.String()) + } +} + +func term(s string) *ast.Term { + return ast.MustParseTerm(s) +} + +func TestBindingsArrayHashmap(t *testing.T) { + t.Parallel() + + var bindings bindings + b := newBindingsArrayHashmap() + keys := make(map[int]ast.Var) + + for i := range maxLinearScan + 1 { + b.Put(testBindingKey(i), testBindingValue(&bindings, i)) + keys[i] = testBindingKey(i).Value.(ast.Var) + + testBindingKeys(t, &bindings, &b, keys) + } + + for i := range maxLinearScan + 1 { + b.Delete(testBindingKey(i)) + delete(keys, i) + + testBindingKeys(t, &bindings, &b, keys) + } +} + +func testBindingKeys(t *testing.T, bindings *bindings, b *bindingsArrayHashmap, keys map[int]ast.Var) { + t.Helper() + + for k := range keys { + value := testBindingValue(bindings, k) + if v, ok := b.Get(testBindingKey(k)); !ok { + t.Errorf("value not found: %v", k) + } else if !v.equal(&value) { + t.Errorf("value not equal") + } + } + + var found []ast.Var + b.Iter(func(k *ast.Term, v value) bool { + key := k.Value.(ast.Var) + if i, _ := strconv.Atoi(string(key)); !testBindingValue(bindings, i).equal(&v) { + t.Errorf("iteration value note equal") + } + + found = append(found, key) + return false + }) + + if len(found) != len(keys) { + t.Errorf("all keys not found") + } + +next: + for _, a := range keys { + for _, b := range found { + if a == b { + continue next + } + } + + t.Errorf("key not found") + } +} + +func testBindingKey(key int) *ast.Term { + return ast.VarTerm(strconv.Itoa(key)) +} + +func testBindingValue(b *bindings, key int) value { + return value{b, ast.IntNumberTerm(key)} +} diff --git a/third_party/opa/v1/topdown/bits.go b/third_party/opa/v1/topdown/bits.go new file mode 100644 index 000000000000..e420ffe611ae --- /dev/null +++ b/third_party/opa/v1/topdown/bits.go @@ -0,0 +1,88 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "math/big" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +type bitsArity1 func(a *big.Int) (*big.Int, error) +type bitsArity2 func(a, b *big.Int) (*big.Int, error) + +func bitsOr(a, b *big.Int) (*big.Int, error) { + return new(big.Int).Or(a, b), nil +} + +func bitsAnd(a, b *big.Int) (*big.Int, error) { + return new(big.Int).And(a, b), nil +} + +func bitsNegate(a *big.Int) (*big.Int, error) { + return new(big.Int).Not(a), nil +} + +func bitsXOr(a, b *big.Int) (*big.Int, error) { + return new(big.Int).Xor(a, b), nil +} + +func bitsShiftLeft(a, b *big.Int) (*big.Int, error) { + if b.Sign() == -1 { + return nil, builtins.NewOperandErr(2, "must be an unsigned integer number but got a negative integer") + } + shift := uint(b.Uint64()) + return new(big.Int).Lsh(a, shift), nil +} + +func bitsShiftRight(a, b *big.Int) (*big.Int, error) { + if b.Sign() == -1 { + return nil, builtins.NewOperandErr(2, "must be an unsigned integer number but got a negative integer") + } + shift := uint(b.Uint64()) + return new(big.Int).Rsh(a, shift), nil +} + +func builtinBitsArity1(fn bitsArity1) BuiltinFunc { + return func(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + i, err := builtins.BigIntOperand(operands[0].Value, 1) + if err != nil { + return err + } + iOut, err := fn(i) + if err != nil { + return err + } + return iter(ast.NewTerm(builtins.IntToNumber(iOut))) + } +} + +func builtinBitsArity2(fn bitsArity2) BuiltinFunc { + return func(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + i1, err := builtins.BigIntOperand(operands[0].Value, 1) + if err != nil { + return err + } + i2, err := builtins.BigIntOperand(operands[1].Value, 2) + if err != nil { + return err + } + iOut, err := fn(i1, i2) + if err != nil { + return err + } + return iter(ast.NewTerm(builtins.IntToNumber(iOut))) + } +} + +func init() { + RegisterBuiltinFunc(ast.BitsOr.Name, builtinBitsArity2(bitsOr)) + RegisterBuiltinFunc(ast.BitsAnd.Name, builtinBitsArity2(bitsAnd)) + RegisterBuiltinFunc(ast.BitsNegate.Name, builtinBitsArity1(bitsNegate)) + RegisterBuiltinFunc(ast.BitsXOr.Name, builtinBitsArity2(bitsXOr)) + RegisterBuiltinFunc(ast.BitsShiftLeft.Name, builtinBitsArity2(bitsShiftLeft)) + RegisterBuiltinFunc(ast.BitsShiftRight.Name, builtinBitsArity2(bitsShiftRight)) +} diff --git a/third_party/opa/v1/topdown/builtins.go b/third_party/opa/v1/topdown/builtins.go new file mode 100644 index 000000000000..e0b893d477c0 --- /dev/null +++ b/third_party/opa/v1/topdown/builtins.go @@ -0,0 +1,224 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "encoding/binary" + "fmt" + "io" + "math/rand" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/print" + "github.com/open-policy-agent/opa/v1/tracing" +) + +type ( + // Deprecated: Functional-style builtins are deprecated. Use BuiltinFunc instead. + FunctionalBuiltin1 func(op1 ast.Value) (output ast.Value, err error) + + // Deprecated: Functional-style builtins are deprecated. Use BuiltinFunc instead. + FunctionalBuiltin2 func(op1, op2 ast.Value) (output ast.Value, err error) + + // Deprecated: Functional-style builtins are deprecated. Use BuiltinFunc instead. + FunctionalBuiltin3 func(op1, op2, op3 ast.Value) (output ast.Value, err error) + + // Deprecated: Functional-style builtins are deprecated. Use BuiltinFunc instead. + FunctionalBuiltin4 func(op1, op2, op3, op4 ast.Value) (output ast.Value, err error) + + // BuiltinContext contains context from the evaluator that may be used by + // built-in functions. + BuiltinContext struct { + Context context.Context // request context that was passed when query started + Metrics metrics.Metrics // metrics registry for recording built-in specific metrics + Seed io.Reader // randomization source + Time *ast.Term // wall clock time + Cancel Cancel // atomic value that signals evaluation to halt + Runtime *ast.Term // runtime information on the OPA instance + Cache builtins.Cache // built-in function state cache + InterQueryBuiltinCache cache.InterQueryCache // cross-query built-in function state cache + InterQueryBuiltinValueCache cache.InterQueryValueCache // cross-query built-in function state value cache. this cache is useful for scenarios where the entry size cannot be calculated + NDBuiltinCache builtins.NDBCache // cache for non-deterministic built-in state + Location *ast.Location // location of built-in call + Tracers []Tracer // Deprecated: Use QueryTracers instead + QueryTracers []QueryTracer // tracer objects for trace() built-in function + TraceEnabled bool // indicates whether tracing is enabled for the evaluation + QueryID uint64 // identifies query being evaluated + ParentID uint64 // identifies parent of query being evaluated + PrintHook print.Hook // provides callback function to use for printing + RoundTripper CustomizeRoundTripper // customize transport to use for HTTP requests + DistributedTracingOpts tracing.Options // options to be used by distributed tracing. + rand *rand.Rand // randomization source for non-security-sensitive operations + Capabilities *ast.Capabilities + } + + // BuiltinFunc defines an interface for implementing built-in functions. + // The built-in function is called with the plugged operands from the call + // (including the output operands.) The implementation should evaluate the + // operands and invoke the iterator for each successful/defined output + // value. + BuiltinFunc func(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error +) + +// Rand returns a random number generator based on the Seed for this built-in +// context. The random number will be re-used across multiple calls to this +// function. If a random number generator cannot be created, an error is +// returned. +func (bctx *BuiltinContext) Rand() (*rand.Rand, error) { + + if bctx.rand != nil { + return bctx.rand, nil + } + + seed, err := readInt64(bctx.Seed) + if err != nil { + return nil, err + } + + bctx.rand = rand.New(rand.NewSource(seed)) + return bctx.rand, nil +} + +// RegisterBuiltinFunc adds a new built-in function to the evaluation engine. +func RegisterBuiltinFunc(name string, f BuiltinFunc) { + builtinFunctions[name] = builtinErrorWrapper(name, f) +} + +// Deprecated: Functional-style builtins are deprecated. Use RegisterBuiltinFunc instead. +func RegisterFunctionalBuiltin1(name string, fun FunctionalBuiltin1) { + builtinFunctions[name] = functionalWrapper1(name, fun) +} + +// Deprecated: Functional-style builtins are deprecated. Use RegisterBuiltinFunc instead. +func RegisterFunctionalBuiltin2(name string, fun FunctionalBuiltin2) { + builtinFunctions[name] = functionalWrapper2(name, fun) +} + +// Deprecated: Functional-style builtins are deprecated. Use RegisterBuiltinFunc instead. +func RegisterFunctionalBuiltin3(name string, fun FunctionalBuiltin3) { + builtinFunctions[name] = functionalWrapper3(name, fun) +} + +// Deprecated: Functional-style builtins are deprecated. Use RegisterBuiltinFunc instead. +func RegisterFunctionalBuiltin4(name string, fun FunctionalBuiltin4) { + builtinFunctions[name] = functionalWrapper4(name, fun) +} + +// GetBuiltin returns a built-in function implementation, nil if no built-in found. +func GetBuiltin(name string) BuiltinFunc { + return builtinFunctions[name] +} + +// Deprecated: The BuiltinEmpty type is no longer needed. Use nil return values instead. +type BuiltinEmpty struct{} + +func (BuiltinEmpty) Error() string { + return "" +} + +var builtinFunctions = map[string]BuiltinFunc{} + +func builtinErrorWrapper(name string, fn BuiltinFunc) BuiltinFunc { + return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + err := fn(bctx, args, iter) + if err == nil { + return nil + } + return handleBuiltinErr(name, bctx.Location, err) + } +} + +func functionalWrapper1(name string, fn FunctionalBuiltin1) BuiltinFunc { + return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + result, err := fn(args[0].Value) + if err == nil { + return iter(ast.NewTerm(result)) + } + return handleBuiltinErr(name, bctx.Location, err) + } +} + +func functionalWrapper2(name string, fn FunctionalBuiltin2) BuiltinFunc { + return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + result, err := fn(args[0].Value, args[1].Value) + if err == nil { + return iter(ast.NewTerm(result)) + } + return handleBuiltinErr(name, bctx.Location, err) + } +} + +func functionalWrapper3(name string, fn FunctionalBuiltin3) BuiltinFunc { + return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + result, err := fn(args[0].Value, args[1].Value, args[2].Value) + if err == nil { + return iter(ast.NewTerm(result)) + } + return handleBuiltinErr(name, bctx.Location, err) + } +} + +func functionalWrapper4(name string, fn FunctionalBuiltin4) BuiltinFunc { + return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + result, err := fn(args[0].Value, args[1].Value, args[2].Value, args[3].Value) + if err == nil { + return iter(ast.NewTerm(result)) + } + if _, empty := err.(BuiltinEmpty); empty { + return nil + } + return handleBuiltinErr(name, bctx.Location, err) + } +} + +func handleBuiltinErr(name string, loc *ast.Location, err error) error { + switch err := err.(type) { + case BuiltinEmpty: + return nil + case *Error, Halt: + return err + case builtins.ErrOperand: + e := &Error{ + Code: TypeErr, + Message: fmt.Sprintf("%v: %v", name, err.Error()), + Location: loc, + } + return e.Wrap(err) + default: + e := &Error{ + Code: BuiltinErr, + Message: fmt.Sprintf("%v: %v", name, err.Error()), + Location: loc, + } + return e.Wrap(err) + } +} + +func readInt64(r io.Reader) (int64, error) { + bs := make([]byte, 8) + n, err := io.ReadFull(r, bs) + if n != len(bs) || err != nil { + return 0, err + } + return int64(binary.BigEndian.Uint64(bs)), nil +} + +// Used to get older-style (ast.Term, error) tuples out of newer functions. +func getResult(fn BuiltinFunc, operands ...*ast.Term) (*ast.Term, error) { + var result *ast.Term + extractionFn := func(r *ast.Term) error { + result = r + return nil + } + err := fn(BuiltinContext{}, operands, extractionFn) + if err != nil { + return nil, err + } + return result, nil +} diff --git a/third_party/opa/v1/topdown/builtins/builtins.go b/third_party/opa/v1/topdown/builtins/builtins.go new file mode 100644 index 000000000000..7a1bdede6b28 --- /dev/null +++ b/third_party/opa/v1/topdown/builtins/builtins.go @@ -0,0 +1,329 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package builtins contains utilities for implementing built-in functions. +package builtins + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" +) + +// Cache defines the built-in cache used by the top-down evaluation. The keys +// must be comparable and should not be of type string. +type Cache map[any]any + +// Put updates the cache for the named built-in. +func (c Cache) Put(k, v any) { + c[k] = v +} + +// Get returns the cached value for k. +func (c Cache) Get(k any) (any, bool) { + v, ok := c[k] + return v, ok +} + +// We use an ast.Object for the cached keys/values because a naive +// map[ast.Value]ast.Value will not correctly detect value equality of +// the member keys. +type NDBCache map[string]ast.Object + +func (c NDBCache) AsValue() ast.Value { + out := ast.NewObject() + for bname, obj := range c { + out.Insert(ast.InternedTerm(bname), ast.NewTerm(obj)) + } + return out +} + +// Put updates the cache for the named built-in. +// Automatically creates the 2-level hierarchy as needed. +func (c NDBCache) Put(name string, k, v ast.Value) { + if _, ok := c[name]; !ok { + c[name] = ast.NewObject() + } + c[name].Insert(ast.NewTerm(k), ast.NewTerm(v)) +} + +// Get returns the cached value for k for the named builtin. +func (c NDBCache) Get(name string, k ast.Value) (ast.Value, bool) { + if m, ok := c[name]; ok { + v := m.Get(ast.NewTerm(k)) + if v != nil { + return v.Value, true + } + return nil, false + } + return nil, false +} + +// Convenience functions for serializing the data structure. +func (c NDBCache) MarshalJSON() ([]byte, error) { + v, err := ast.JSON(c.AsValue()) + if err != nil { + return nil, err + } + return json.Marshal(v) +} + +func (c *NDBCache) UnmarshalJSON(data []byte) error { + out := map[string]ast.Object{} + var incoming any + + // Note: We use util.Unmarshal instead of json.Unmarshal to get + // correct deserialization of number types. + err := util.Unmarshal(data, &incoming) + if err != nil { + return err + } + + // Convert interface types back into ast.Value types. + nestedObject, err := ast.InterfaceToValue(incoming) + if err != nil { + return err + } + + // Reconstruct NDBCache from nested ast.Object structure. + if source, ok := nestedObject.(ast.Object); ok { + err = source.Iter(func(k, v *ast.Term) error { + if obj, ok := v.Value.(ast.Object); ok { + out[string(k.Value.(ast.String))] = obj + return nil + } + return errors.New("expected Object, got other Value type in conversion") + }) + if err != nil { + return err + } + } + + *c = out + + return nil +} + +// ErrOperand represents an invalid operand has been passed to a built-in +// function. Built-ins should return ErrOperand to indicate a type error has +// occurred. +type ErrOperand string + +func (err ErrOperand) Error() string { + return string(err) +} + +// NewOperandErr returns a generic operand error. +func NewOperandErr(pos int, f string, a ...any) error { + f = fmt.Sprintf("operand %v ", pos) + f + return ErrOperand(fmt.Sprintf(f, a...)) +} + +// NewOperandTypeErr returns an operand error indicating the operand's type was wrong. +func NewOperandTypeErr(pos int, got ast.Value, expected ...string) error { + + if len(expected) == 1 { + return NewOperandErr(pos, "must be %v but got %v", expected[0], ast.ValueName(got)) + } + + return NewOperandErr(pos, "must be one of {%v} but got %v", strings.Join(expected, ", "), ast.ValueName(got)) +} + +// NewOperandElementErr returns an operand error indicating an element in the +// composite operand was wrong. +func NewOperandElementErr(pos int, composite ast.Value, got ast.Value, expected ...string) error { + + tpe := ast.ValueName(composite) + + if len(expected) == 1 { + return NewOperandErr(pos, "must be %v of %vs but got %v containing %v", tpe, expected[0], tpe, ast.ValueName(got)) + } + + return NewOperandErr(pos, "must be %v of (any of) {%v} but got %v containing %v", tpe, strings.Join(expected, ", "), tpe, ast.ValueName(got)) +} + +// NewOperandEnumErr returns an operand error indicating a value was wrong. +func NewOperandEnumErr(pos int, expected ...string) error { + + if len(expected) == 1 { + return NewOperandErr(pos, "must be %v", expected[0]) + } + + return NewOperandErr(pos, "must be one of {%v}", strings.Join(expected, ", ")) +} + +// IntOperand converts x to an int. If the cast fails, a descriptive error is +// returned. +func IntOperand(x ast.Value, pos int) (int, error) { + n, ok := x.(ast.Number) + if !ok { + return 0, NewOperandTypeErr(pos, x, "number") + } + + i, ok := n.Int() + if !ok { + return 0, NewOperandErr(pos, "must be integer number but got floating-point number") + } + + return i, nil +} + +// BigIntOperand converts x to a big int. If the cast fails, a descriptive error +// is returned. +func BigIntOperand(x ast.Value, pos int) (*big.Int, error) { + n, err := NumberOperand(x, 1) + if err != nil { + return nil, NewOperandTypeErr(pos, x, "integer") + } + bi, err := NumberToInt(n) + if err != nil { + return nil, NewOperandErr(pos, "must be integer number but got floating-point number") + } + + return bi, nil +} + +// NumberOperand converts x to a number. If the cast fails, a descriptive error is +// returned. +func NumberOperand(x ast.Value, pos int) (ast.Number, error) { + n, ok := x.(ast.Number) + if !ok { + return ast.Number(""), NewOperandTypeErr(pos, x, "number") + } + return n, nil +} + +// SetOperand converts x to a set. If the cast fails, a descriptive error is +// returned. +func SetOperand(x ast.Value, pos int) (ast.Set, error) { + s, ok := x.(ast.Set) + if !ok { + return nil, NewOperandTypeErr(pos, x, "set") + } + return s, nil +} + +// StringOperand converts x to a string. If the cast fails, a descriptive error is +// returned. +func StringOperand(x ast.Value, pos int) (ast.String, error) { + s, ok := x.(ast.String) + if !ok { + return ast.String(""), NewOperandTypeErr(pos, x, "string") + } + return s, nil +} + +// ObjectOperand converts x to an object. If the cast fails, a descriptive +// error is returned. +func ObjectOperand(x ast.Value, pos int) (ast.Object, error) { + o, ok := x.(ast.Object) + if !ok { + return nil, NewOperandTypeErr(pos, x, "object") + } + return o, nil +} + +// ArrayOperand converts x to an array. If the cast fails, a descriptive +// error is returned. +func ArrayOperand(x ast.Value, pos int) (*ast.Array, error) { + a, ok := x.(*ast.Array) + if !ok { + return nil, NewOperandTypeErr(pos, x, "array") + } + return a, nil +} + +// NumberToFloat converts n to a big float. +func NumberToFloat(n ast.Number) *big.Float { + r, ok := new(big.Float).SetString(string(n)) + if !ok { + panic("illegal value") + } + return r +} + +// FloatToNumber converts f to a number. +func FloatToNumber(f *big.Float) ast.Number { + var format byte = 'g' + if f.IsInt() { + format = 'f' + } + return ast.Number(f.Text(format, -1)) +} + +// NumberToInt converts n to a big int. +// If n cannot be converted to an big int, an error is returned. +func NumberToInt(n ast.Number) (*big.Int, error) { + f := NumberToFloat(n) + r, accuracy := f.Int(nil) + if accuracy != big.Exact { + return nil, errors.New("illegal value") + } + return r, nil +} + +// IntToNumber converts i to a number. +func IntToNumber(i *big.Int) ast.Number { + return ast.Number(i.String()) +} + +// StringSliceOperand converts x to a []string. If the cast fails, a descriptive error is +// returned. +func StringSliceOperand(a ast.Value, pos int) ([]string, error) { + type iterable interface { + Iter(func(*ast.Term) error) error + Len() int + } + + strs, ok := a.(iterable) + if !ok { + return nil, NewOperandTypeErr(pos, a, "array", "set") + } + + var outStrs = make([]string, 0, strs.Len()) + if err := strs.Iter(func(x *ast.Term) error { + s, ok := x.Value.(ast.String) + if !ok { + return NewOperandElementErr(pos, a, x.Value, "string") + } + outStrs = append(outStrs, string(s)) + return nil + }); err != nil { + return nil, err + } + + return outStrs, nil +} + +// RuneSliceOperand converts x to a []rune. If the cast fails, a descriptive error is +// returned. +func RuneSliceOperand(x ast.Value, pos int) ([]rune, error) { + a, err := ArrayOperand(x, pos) + if err != nil { + return nil, err + } + + var f = make([]rune, a.Len()) + for k := range a.Len() { + b := a.Elem(k) + c, ok := b.Value.(ast.String) + if !ok { + return nil, NewOperandElementErr(pos, x, b.Value, "string") + } + + d := []rune(string(c)) + if len(d) != 1 { + return nil, NewOperandElementErr(pos, x, b.Value, "rune") + } + + f[k] = d[0] + } + + return f, nil +} diff --git a/third_party/opa/v1/topdown/builtins_test.go b/third_party/opa/v1/topdown/builtins_test.go new file mode 100644 index 000000000000..7638c33f355c --- /dev/null +++ b/third_party/opa/v1/topdown/builtins_test.go @@ -0,0 +1,45 @@ +package topdown + +import ( + "context" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/types" +) + +func TestCustomBuiltinIterator(t *testing.T) { + t.Parallel() + + query := NewQuery(ast.MustParseBody("test(1, x)")).WithBuiltins(map[string]*Builtin{ + "test": { + Decl: &ast.Builtin{ + Name: "test", + Decl: types.NewFunction(types.Args(types.N), types.N), + }, + Func: func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + if bctx.Context == nil { + t.Fatal("context must be non-nil") + } + n, ok := terms[0].Value.(ast.Number) + if ok { + if i, ok := n.Int(); ok { + return iter(ast.IntNumberTerm(i + 1)) + } + } + return nil + }, + }, + }) + + ctx := context.Background() + + rs, err := query.Run(ctx) + if err != nil { + t.Fatal(err) + } else if len(rs) != 1 { + t.Fatal("Expected one result but got:", rs) + } else if !rs[0][ast.Var("x")].Equal(ast.IntNumberTerm(2)) { + t.Fatal("Expected x to be 2 but got:", rs[0]) + } +} diff --git a/third_party/opa/v1/topdown/cache.go b/third_party/opa/v1/topdown/cache.go new file mode 100644 index 000000000000..a6c89b45378a --- /dev/null +++ b/third_party/opa/v1/topdown/cache.go @@ -0,0 +1,363 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "slices" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" +) + +// VirtualCache defines the interface for a cache that stores the results of +// evaluated virtual documents (rules). +// The cache is a stack of frames, where each frame is a mapping from references +// to values. +type VirtualCache interface { + // Push pushes a new, empty frame of value mappings onto the stack. + Push() + + // Pop pops the top frame of value mappings from the stack, removing all associated entries. + Pop() + + // Get returns the value associated with the given reference. The second return value + // indicates whether the reference has a recorded 'undefined' result. + Get(ref ast.Ref) (*ast.Term, bool) + + // Put associates the given reference with the given value. If the value is nil, the reference + // is marked as having an 'undefined' result. + Put(ref ast.Ref, value *ast.Term) + + // Keys returns the set of keys that have been cached for the active frame. + Keys() []ast.Ref +} + +// BaseCache defines the interface for a cache that stores cached base documents, i.e. data. +type BaseCache interface { + Get(ast.Ref) ast.Value + Put(ast.Ref, ast.Value) +} + +type virtualCache struct { + stack []*virtualCacheElem +} + +type virtualCacheElem struct { + value *ast.Term + children *util.HasherMap[*ast.Term, *virtualCacheElem] + undefined bool +} + +func NewVirtualCache() VirtualCache { + cache := &virtualCache{} + cache.Push() + return cache +} + +func (c *virtualCache) Push() { + c.stack = append(c.stack, newVirtualCacheElem()) +} + +func (c *virtualCache) Pop() { + c.stack = c.stack[:len(c.stack)-1] +} + +// Returns the resolved value of the AST term and a flag indicating if the value +// should be interpretted as undefined: +// +// nil, true indicates the ref is undefined +// ast.Term, false indicates the ref is defined +// nil, false indicates the ref has not been cached +// ast.Term, true is impossible +func (c *virtualCache) Get(ref ast.Ref) (*ast.Term, bool) { + node := c.stack[len(c.stack)-1] + for i := range ref { + x, ok := node.children.Get(ref[i]) + if !ok { + return nil, false + } + node = x + } + if node.undefined { + return nil, true + } + + return node.value, false +} + +// If value is a nil pointer, set the 'undefined' flag on the cache element to +// indicate that the Ref has resolved to undefined. +func (c *virtualCache) Put(ref ast.Ref, value *ast.Term) { + node := c.stack[len(c.stack)-1] + for i := range ref { + x, ok := node.children.Get(ref[i]) + if ok { + node = x + } else { + next := newVirtualCacheElem() + node.children.Put(ref[i], next) + node = next + } + } + if value != nil { + node.value = value + } else { + node.undefined = true + } +} + +func (c *virtualCache) Keys() []ast.Ref { + node := c.stack[len(c.stack)-1] + return keysRecursive(nil, node) +} + +func keysRecursive(root ast.Ref, node *virtualCacheElem) []ast.Ref { + var keys []ast.Ref + node.children.Iter(func(k *ast.Term, v *virtualCacheElem) bool { + ref := root.Append(k) + if v.value != nil { + keys = append(keys, ref) + } + if v.children.Len() > 0 { + keys = append(keys, keysRecursive(ref, v)...) + } + return false + }) + return keys +} + +func newVirtualCacheElem() *virtualCacheElem { + return &virtualCacheElem{children: newVirtualCacheHashMap()} +} + +func newVirtualCacheHashMap() *util.HasherMap[*ast.Term, *virtualCacheElem] { + return util.NewHasherMap[*ast.Term, *virtualCacheElem](ast.TermValueEqual) +} + +// baseCache implements a trie structure to cache base documents read out of +// storage. Values inserted into the cache may contain other values that were +// previously inserted. In this case, the previous values are erased from the +// structure. +type baseCache struct { + root *baseCacheElem +} + +func newBaseCache() *baseCache { + return &baseCache{ + root: newBaseCacheElem(), + } +} + +func (c *baseCache) Get(ref ast.Ref) ast.Value { + node := c.root + for i := range ref { + node = node.children[ref[i].Value] + if node == nil { + return nil + } else if node.value != nil { + if len(ref) == 1 && ast.IsScalar(node.value) { + // If the node is a scalar, return the value directly + // and avoid an allocation when calling Find. + return node.value + } + + result, err := node.value.Find(ref[i+1:]) + if err != nil { + return nil + } + return result + } + } + return nil +} + +func (c *baseCache) Put(ref ast.Ref, value ast.Value) { + node := c.root + for i := range ref { + if child, ok := node.children[ref[i].Value]; ok { + node = child + } else { + child := newBaseCacheElem() + node.children[ref[i].Value] = child + node = child + } + } + node.set(value) +} + +type baseCacheElem struct { + value ast.Value + children map[ast.Value]*baseCacheElem +} + +func newBaseCacheElem() *baseCacheElem { + return &baseCacheElem{ + children: map[ast.Value]*baseCacheElem{}, + } +} + +func (e *baseCacheElem) set(value ast.Value) { + e.value = value + e.children = map[ast.Value]*baseCacheElem{} +} + +type refStack struct { + sl []refStackElem +} + +type refStackElem struct { + refs []ast.Ref +} + +func newRefStack() *refStack { + return &refStack{} +} + +func (s *refStack) Push(refs []ast.Ref) { + s.sl = append(s.sl, refStackElem{refs: refs}) +} + +func (s *refStack) Pop() { + if s == nil { + return + } + s.sl = s.sl[:len(s.sl)-1] +} + +func (s *refStack) Prefixed(ref ast.Ref) bool { + if s != nil { + for i := len(s.sl) - 1; i >= 0; i-- { + if slices.ContainsFunc(s.sl[i].refs, ref.HasPrefix) { + return true + } + } + } + return false +} + +type comprehensionCache struct { + stack []map[*ast.Term]*comprehensionCacheElem +} + +type comprehensionCacheElem struct { + value *ast.Term + children *util.HasherMap[*ast.Term, *comprehensionCacheElem] +} + +func newComprehensionCache() *comprehensionCache { + cache := &comprehensionCache{} + cache.Push() + return cache +} + +func (c *comprehensionCache) Push() { + c.stack = append(c.stack, map[*ast.Term]*comprehensionCacheElem{}) +} + +func (c *comprehensionCache) Pop() { + c.stack = c.stack[:len(c.stack)-1] +} + +func (c *comprehensionCache) Elem(t *ast.Term) (*comprehensionCacheElem, bool) { + elem, ok := c.stack[len(c.stack)-1][t] + return elem, ok +} + +func (c *comprehensionCache) Set(t *ast.Term, elem *comprehensionCacheElem) { + c.stack[len(c.stack)-1][t] = elem +} + +func newComprehensionCacheElem() *comprehensionCacheElem { + return &comprehensionCacheElem{children: newComprehensionCacheHashMap()} +} + +func (c *comprehensionCacheElem) Get(key []*ast.Term) *ast.Term { + node := c + for i := range key { + x, ok := node.children.Get(key[i]) + if !ok { + return nil + } + node = x + } + return node.value +} + +func (c *comprehensionCacheElem) Put(key []*ast.Term, value *ast.Term) { + node := c + for i := range key { + x, ok := node.children.Get(key[i]) + if ok { + node = x + } else { + next := newComprehensionCacheElem() + node.children.Put(key[i], next) + node = next + } + } + node.value = value +} + +func newComprehensionCacheHashMap() *util.HasherMap[*ast.Term, *comprehensionCacheElem] { + return util.NewHasherMap[*ast.Term, *comprehensionCacheElem](ast.TermValueEqual) +} + +type functionMocksStack struct { + stack []*functionMocksElem +} + +type functionMocksElem []frame + +type frame map[string]*ast.Term + +func newFunctionMocksStack() *functionMocksStack { + stack := &functionMocksStack{} + stack.Push() + return stack +} + +func newFunctionMocksElem() *functionMocksElem { + return &functionMocksElem{} +} + +func (s *functionMocksStack) Push() { + s.stack = append(s.stack, newFunctionMocksElem()) +} + +func (s *functionMocksStack) Pop() { + s.stack = s.stack[:len(s.stack)-1] +} + +func (s *functionMocksStack) PopPairs() { + current := s.stack[len(s.stack)-1] + *current = (*current)[:len(*current)-1] +} + +func (s *functionMocksStack) PutPairs(mocks [][2]*ast.Term) { + el := frame{} + for i := range mocks { + el[mocks[i][0].Value.String()] = mocks[i][1] + } + s.Put(el) +} + +func (s *functionMocksStack) Put(el frame) { + current := s.stack[len(s.stack)-1] + *current = append(*current, el) +} + +func (s *functionMocksStack) Get(f ast.Ref) (*ast.Term, bool) { + if s == nil { + return nil, false + } + + current := *s.stack[len(s.stack)-1] + for i := len(current) - 1; i >= 0; i-- { + if r, ok := current[i][f.String()]; ok { + return r, true + } + } + return nil, false +} diff --git a/third_party/opa/v1/topdown/cache/cache.go b/third_party/opa/v1/topdown/cache/cache.go new file mode 100644 index 000000000000..60f38aaba2fb --- /dev/null +++ b/third_party/opa/v1/topdown/cache/cache.go @@ -0,0 +1,574 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package cache defines the inter-query cache interface that can cache data across queries +package cache + +import ( + "container/list" + "context" + "fmt" + "math" + "sync" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + defaultInterQueryBuiltinValueCacheSize = int(0) // unlimited + defaultMaxSizeBytes = int64(0) // unlimited + defaultForcedEvictionThresholdPercentage = int64(100) // trigger at max_size_bytes + defaultStaleEntryEvictionPeriodSeconds = int64(0) // never +) + +var interQueryBuiltinValueCacheDefaultConfigs = map[string]*NamedValueCacheConfig{} + +func getDefaultInterQueryBuiltinValueCacheConfig(name string) *NamedValueCacheConfig { + return interQueryBuiltinValueCacheDefaultConfigs[name] +} + +// RegisterDefaultInterQueryBuiltinValueCacheConfig registers a default configuration for the inter-query value cache; +// used when none has been explicitly configured. +// To disable a named cache when not configured, pass a nil config. +func RegisterDefaultInterQueryBuiltinValueCacheConfig(name string, config *NamedValueCacheConfig) { + interQueryBuiltinValueCacheDefaultConfigs[name] = config +} + +// Config represents the configuration for the inter-query builtin cache. +type Config struct { + InterQueryBuiltinCache InterQueryBuiltinCacheConfig `json:"inter_query_builtin_cache"` + InterQueryBuiltinValueCache InterQueryBuiltinValueCacheConfig `json:"inter_query_builtin_value_cache"` +} + +// NamedValueCacheConfig represents the configuration of a named cache that built-in functions can utilize. +// A default configuration to be used if not explicitly configured can be registered using RegisterDefaultInterQueryBuiltinValueCacheConfig. +type NamedValueCacheConfig struct { + MaxNumEntries *int `json:"max_num_entries,omitempty"` +} + +// InterQueryBuiltinValueCacheConfig represents the configuration of the inter-query value cache that built-in functions can utilize. +// MaxNumEntries - max number of cache entries +type InterQueryBuiltinValueCacheConfig struct { + MaxNumEntries *int `json:"max_num_entries,omitempty"` + NamedCacheConfigs map[string]*NamedValueCacheConfig `json:"named,omitempty"` +} + +// InterQueryBuiltinCacheConfig represents the configuration of the inter-query cache that built-in functions can utilize. +// MaxSizeBytes - max capacity of cache in bytes +// ForcedEvictionThresholdPercentage - capacity usage in percentage after which forced FIFO eviction starts +// StaleEntryEvictionPeriodSeconds - time period between end of previous and start of new stale entry eviction routine +type InterQueryBuiltinCacheConfig struct { + MaxSizeBytes *int64 `json:"max_size_bytes,omitempty"` + ForcedEvictionThresholdPercentage *int64 `json:"forced_eviction_threshold_percentage,omitempty"` + StaleEntryEvictionPeriodSeconds *int64 `json:"stale_entry_eviction_period_seconds,omitempty"` +} + +// ParseCachingConfig returns the config for the inter-query cache. +func ParseCachingConfig(raw []byte) (*Config, error) { + if raw == nil { + maxSize := new(int64) + *maxSize = defaultMaxSizeBytes + threshold := new(int64) + *threshold = defaultForcedEvictionThresholdPercentage + period := new(int64) + *period = defaultStaleEntryEvictionPeriodSeconds + + maxInterQueryBuiltinValueCacheSize := new(int) + *maxInterQueryBuiltinValueCacheSize = defaultInterQueryBuiltinValueCacheSize + + return &Config{ + InterQueryBuiltinCache: InterQueryBuiltinCacheConfig{ + MaxSizeBytes: maxSize, + ForcedEvictionThresholdPercentage: threshold, + StaleEntryEvictionPeriodSeconds: period, + }, + InterQueryBuiltinValueCache: InterQueryBuiltinValueCacheConfig{ + MaxNumEntries: maxInterQueryBuiltinValueCacheSize, + }, + }, nil + } + + var config Config + + if err := util.Unmarshal(raw, &config); err == nil { + if err = config.validateAndInjectDefaults(); err != nil { + return nil, err + } + } else { + return nil, err + } + + return &config, nil +} + +func (c *Config) validateAndInjectDefaults() error { + if c.InterQueryBuiltinCache.MaxSizeBytes == nil { + maxSize := new(int64) + *maxSize = defaultMaxSizeBytes + c.InterQueryBuiltinCache.MaxSizeBytes = maxSize + } + if c.InterQueryBuiltinCache.ForcedEvictionThresholdPercentage == nil { + threshold := new(int64) + *threshold = defaultForcedEvictionThresholdPercentage + c.InterQueryBuiltinCache.ForcedEvictionThresholdPercentage = threshold + } else { + threshold := *c.InterQueryBuiltinCache.ForcedEvictionThresholdPercentage + if threshold < 0 || threshold > 100 { + return fmt.Errorf("invalid forced_eviction_threshold_percentage %v", threshold) + } + } + if c.InterQueryBuiltinCache.StaleEntryEvictionPeriodSeconds == nil { + period := new(int64) + *period = defaultStaleEntryEvictionPeriodSeconds + c.InterQueryBuiltinCache.StaleEntryEvictionPeriodSeconds = period + } else { + period := *c.InterQueryBuiltinCache.StaleEntryEvictionPeriodSeconds + if period < 0 { + return fmt.Errorf("invalid stale_entry_eviction_period_seconds %v", period) + } + } + + if c.InterQueryBuiltinValueCache.MaxNumEntries == nil { + maxSize := new(int) + *maxSize = defaultInterQueryBuiltinValueCacheSize + c.InterQueryBuiltinValueCache.MaxNumEntries = maxSize + } else { + numEntries := *c.InterQueryBuiltinValueCache.MaxNumEntries + if numEntries < 0 { + return fmt.Errorf("invalid max_num_entries %v", numEntries) + } + } + + for name, namedConfig := range c.InterQueryBuiltinValueCache.NamedCacheConfigs { + numEntries := *namedConfig.MaxNumEntries + if numEntries < 0 { + return fmt.Errorf("invalid max_num_entries %v for named cache %v", numEntries, name) + } + } + + return nil +} + +// InterQueryCacheValue defines the interface for the data that the inter-query cache holds. +type InterQueryCacheValue interface { + SizeInBytes() int64 + Clone() (InterQueryCacheValue, error) +} + +// InterQueryCache defines the interface for the inter-query cache. +type InterQueryCache interface { + Get(key ast.Value) (value InterQueryCacheValue, found bool) + Insert(key ast.Value, value InterQueryCacheValue) int + InsertWithExpiry(key ast.Value, value InterQueryCacheValue, expiresAt time.Time) int + Delete(key ast.Value) + UpdateConfig(config *Config) + Clone(value InterQueryCacheValue) (InterQueryCacheValue, error) +} + +// NewInterQueryCache returns a new inter-query cache. +// The cache uses a FIFO eviction policy when it reaches the forced eviction threshold. +// Parameters: +// +// config - to configure the InterQueryCache +func NewInterQueryCache(config *Config) InterQueryCache { + return newCache(config) +} + +// NewInterQueryCacheWithContext returns a new inter-query cache with context. +// The cache uses a combination of FIFO eviction policy when it reaches the forced eviction threshold +// and a periodic cleanup routine to remove stale entries that exceed their expiration time, if specified. +// If configured with a zero stale_entry_eviction_period_seconds value, the stale entry cleanup routine is disabled. +// +// Parameters: +// +// ctx - used to control lifecycle of the stale entry cleanup routine +// config - to configure the InterQueryCache +func NewInterQueryCacheWithContext(ctx context.Context, config *Config) InterQueryCache { + iqCache := newCache(config) + if iqCache.staleEntryEvictionTimePeriodSeconds() > 0 { + go func() { + cleanupTicker := time.NewTicker(time.Duration(iqCache.staleEntryEvictionTimePeriodSeconds()) * time.Second) + for { + select { + case <-cleanupTicker.C: + // NOTE: We stop the ticker and create a new one here to ensure that applications + // get _at least_ staleEntryEvictionTimePeriodSeconds with the cache unlocked; + // see https://github.com/open-policy-agent/opa/pull/7188/files#r1855342998 + cleanupTicker.Stop() + iqCache.cleanStaleValues() + cleanupTicker = time.NewTicker(time.Duration(iqCache.staleEntryEvictionTimePeriodSeconds()) * time.Second) + case <-ctx.Done(): + cleanupTicker.Stop() + return + } + } + }() + } + + return iqCache +} + +type cacheItem struct { + value InterQueryCacheValue + expiresAt time.Time + keyElement *list.Element +} + +type cache struct { + items map[string]cacheItem + usage int64 + config *Config + l *list.List + mtx sync.Mutex +} + +func newCache(config *Config) *cache { + return &cache{ + items: map[string]cacheItem{}, + usage: 0, + config: config, + l: list.New(), + } +} + +// InsertWithExpiry inserts a key k into the cache with value v with an expiration time expiresAt. +// A zero time value for expiresAt indicates no expiry +func (c *cache) InsertWithExpiry(k ast.Value, v InterQueryCacheValue, expiresAt time.Time) (dropped int) { + c.mtx.Lock() + defer c.mtx.Unlock() + return c.unsafeInsert(k, v, expiresAt) +} + +// Insert inserts a key k into the cache with value v with no expiration time. +func (c *cache) Insert(k ast.Value, v InterQueryCacheValue) (dropped int) { + return c.InsertWithExpiry(k, v, time.Time{}) +} + +// Get returns the value in the cache for k. +func (c *cache) Get(k ast.Value) (InterQueryCacheValue, bool) { + c.mtx.Lock() + defer c.mtx.Unlock() + cacheItem, ok := c.unsafeGet(k) + + if ok { + return cacheItem.value, true + } + return nil, false +} + +// Delete deletes the value in the cache for k. +func (c *cache) Delete(k ast.Value) { + c.mtx.Lock() + defer c.mtx.Unlock() + c.unsafeDelete(k) +} + +func (c *cache) UpdateConfig(config *Config) { + if config == nil { + return + } + c.mtx.Lock() + defer c.mtx.Unlock() + c.config = config +} + +func (c *cache) Clone(value InterQueryCacheValue) (InterQueryCacheValue, error) { + c.mtx.Lock() + defer c.mtx.Unlock() + return c.unsafeClone(value) +} + +func (c *cache) unsafeInsert(k ast.Value, v InterQueryCacheValue, expiresAt time.Time) (dropped int) { + size := v.SizeInBytes() + limit := int64(math.Ceil(float64(c.forcedEvictionThresholdPercentage())/100.0) * (float64(c.maxSizeBytes()))) + if limit > 0 { + if size > limit { + dropped++ + return dropped + } + + for key := c.l.Front(); key != nil && (c.usage+size > limit); key = c.l.Front() { + dropKey := key.Value.(ast.Value) + c.unsafeDelete(dropKey) + dropped++ + } + } + + // By deleting the old value, if it exists, we ensure the usage variable stays correct + c.unsafeDelete(k) + + c.items[k.String()] = cacheItem{ + value: v, + expiresAt: expiresAt, + keyElement: c.l.PushBack(k), + } + c.usage += size + return dropped +} + +func (c *cache) unsafeGet(k ast.Value) (cacheItem, bool) { + value, ok := c.items[k.String()] + return value, ok +} + +func (c *cache) unsafeDelete(k ast.Value) { + cacheItem, ok := c.unsafeGet(k) + if !ok { + return + } + + c.usage -= cacheItem.value.SizeInBytes() + delete(c.items, k.String()) + c.l.Remove(cacheItem.keyElement) +} + +func (*cache) unsafeClone(value InterQueryCacheValue) (InterQueryCacheValue, error) { + return value.Clone() +} + +func (c *cache) maxSizeBytes() int64 { + if c.config == nil { + return defaultMaxSizeBytes + } + return *c.config.InterQueryBuiltinCache.MaxSizeBytes +} + +func (c *cache) forcedEvictionThresholdPercentage() int64 { + if c.config == nil { + return defaultForcedEvictionThresholdPercentage + } + return *c.config.InterQueryBuiltinCache.ForcedEvictionThresholdPercentage +} + +func (c *cache) staleEntryEvictionTimePeriodSeconds() int64 { + if c.config == nil { + return defaultStaleEntryEvictionPeriodSeconds + } + return *c.config.InterQueryBuiltinCache.StaleEntryEvictionPeriodSeconds +} + +func (c *cache) cleanStaleValues() (dropped int) { + c.mtx.Lock() + defer c.mtx.Unlock() + for key := c.l.Front(); key != nil; { + nextKey := key.Next() + // if expiresAt is zero, the item doesn't have an expiry + if ea := c.items[(key.Value.(ast.Value)).String()].expiresAt; !ea.IsZero() && ea.Before(time.Now()) { + c.unsafeDelete(key.Value.(ast.Value)) + dropped++ + } + key = nextKey + } + return dropped +} + +type InterQueryValueCacheBucket interface { + Get(key ast.Value) (value any, found bool) + Insert(key ast.Value, value any) int + Delete(key ast.Value) +} + +type interQueryValueCacheBucket struct { + items util.HasherMap[ast.Value, any] + config *NamedValueCacheConfig + mtx sync.RWMutex +} + +func newItemsMap() *util.HasherMap[ast.Value, any] { + return util.NewHasherMap[ast.Value, any](ast.ValueEqual) +} + +func (c *interQueryValueCacheBucket) Get(k ast.Value) (any, bool) { + c.mtx.RLock() + defer c.mtx.RUnlock() + return c.items.Get(k) +} + +func (c *interQueryValueCacheBucket) Insert(k ast.Value, v any) (dropped int) { + c.mtx.Lock() + defer c.mtx.Unlock() + + maxEntries := c.maxNumEntries() + if maxEntries > 0 { + l := c.items.Len() + if l >= maxEntries { + itemsToRemove := l - maxEntries + 1 + + // Delete a (semi-)random key to make room for the new one. + c.items.Iter(func(k ast.Value, _ any) bool { + c.items.Delete(k) + dropped++ + + return itemsToRemove == dropped + }) + } + } + + c.items.Put(k, v) + return dropped +} + +func (c *interQueryValueCacheBucket) Delete(k ast.Value) { + c.mtx.Lock() + defer c.mtx.Unlock() + c.items.Delete(k) +} + +func (c *interQueryValueCacheBucket) updateConfig(config *NamedValueCacheConfig) { + if config == nil { + return + } + c.mtx.Lock() + defer c.mtx.Unlock() + c.config = config +} + +func (c *interQueryValueCacheBucket) maxNumEntries() int { + if c.config == nil { + return defaultInterQueryBuiltinValueCacheSize + } + return *c.config.MaxNumEntries +} + +type InterQueryValueCache interface { + InterQueryValueCacheBucket + GetCache(name string) InterQueryValueCacheBucket + UpdateConfig(config *Config) +} + +func NewInterQueryValueCache(_ context.Context, config *Config) InterQueryValueCache { + var c *InterQueryBuiltinValueCacheConfig + var nc *NamedValueCacheConfig + if config != nil { + c = &config.InterQueryBuiltinValueCache + // NOTE: This is a side-effect of reusing the interQueryValueCacheBucket as the global cache. + // It's a hidden implementation detail that we can clean up in the future when revisiting the named caches + // to automatically apply them to any built-in instead of the global cache. + nc = &NamedValueCacheConfig{ + MaxNumEntries: c.MaxNumEntries, + } + } + + return &interQueryBuiltinValueCache{ + globalCache: interQueryValueCacheBucket{ + items: *newItemsMap(), + config: nc, + }, + namedCaches: map[string]*interQueryValueCacheBucket{}, + config: c, + } +} + +type interQueryBuiltinValueCache struct { + globalCache interQueryValueCacheBucket + namedCachesLock sync.RWMutex + namedCaches map[string]*interQueryValueCacheBucket + config *InterQueryBuiltinValueCacheConfig +} + +func (c *interQueryBuiltinValueCache) Get(k ast.Value) (any, bool) { + if c == nil { + return nil, false + } + + return c.globalCache.Get(k) +} + +func (c *interQueryBuiltinValueCache) Insert(k ast.Value, v any) int { + if c == nil { + return 0 + } + + return c.globalCache.Insert(k, v) +} + +func (c *interQueryBuiltinValueCache) Delete(k ast.Value) { + if c == nil { + return + } + + c.globalCache.Delete(k) +} + +func (c *interQueryBuiltinValueCache) GetCache(name string) InterQueryValueCacheBucket { + if c == nil { + return nil + } + + if c.namedCaches == nil { + return nil + } + + c.namedCachesLock.RLock() + nc, ok := c.namedCaches[name] + c.namedCachesLock.RUnlock() + + if !ok { + c.namedCachesLock.Lock() + defer c.namedCachesLock.Unlock() + + if nc, ok := c.namedCaches[name]; ok { + // Some other goroutine has created the cache while we were waiting for the lock. + return nc + } + + var config *NamedValueCacheConfig + if c.config != nil { + config = c.config.NamedCacheConfigs[name] + if config == nil { + config = getDefaultInterQueryBuiltinValueCacheConfig(name) + } + } + + if config == nil { + // No config, cache disabled. + return nil + } + + nc = &interQueryValueCacheBucket{ + items: *newItemsMap(), + config: config, + } + + c.namedCaches[name] = nc + } + + return nc +} + +func (c *interQueryBuiltinValueCache) UpdateConfig(config *Config) { + if c == nil { + return + } + + if config == nil { + c.globalCache.updateConfig(nil) + } else { + + c.globalCache.updateConfig(&NamedValueCacheConfig{ + MaxNumEntries: config.InterQueryBuiltinValueCache.MaxNumEntries, + }) + } + + c.namedCachesLock.Lock() + defer c.namedCachesLock.Unlock() + + c.config = &config.InterQueryBuiltinValueCache + + for name, nc := range c.namedCaches { + // For each named cache: if it has a config, update it; if no config, remove it. + namedConfig := c.config.NamedCacheConfigs[name] + if namedConfig == nil { + namedConfig = getDefaultInterQueryBuiltinValueCacheConfig(name) + } + + if namedConfig == nil { + delete(c.namedCaches, name) + } else { + nc.updateConfig(namedConfig) + } + } +} diff --git a/third_party/opa/v1/topdown/cache/cache_test.go b/third_party/opa/v1/topdown/cache/cache_test.go new file mode 100644 index 000000000000..5437f1f4826b --- /dev/null +++ b/third_party/opa/v1/topdown/cache/cache_test.go @@ -0,0 +1,855 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package cache + +import ( + "context" + "reflect" + "sync" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestParseCachingConfig(t *testing.T) { + t.Parallel() + + maxSize := new(int64) + *maxSize = defaultMaxSizeBytes + period := new(int64) + *period = defaultStaleEntryEvictionPeriodSeconds + threshold := new(int64) + *threshold = defaultForcedEvictionThresholdPercentage + maxNumEntriesInterQueryValueCache := new(int) + *maxNumEntriesInterQueryValueCache = defaultInterQueryBuiltinValueCacheSize + + expected := &Config{ + InterQueryBuiltinCache: InterQueryBuiltinCacheConfig{ + MaxSizeBytes: maxSize, + StaleEntryEvictionPeriodSeconds: period, + ForcedEvictionThresholdPercentage: threshold, + }, + InterQueryBuiltinValueCache: InterQueryBuiltinValueCacheConfig{ + MaxNumEntries: maxNumEntriesInterQueryValueCache, + }, + } + + tests := map[string]struct { + input []byte + wantErr bool + }{ + "empty_config": { + input: nil, + wantErr: false, + }, + "default_limit": { + input: []byte(`{"inter_query_builtin_cache": {},}`), + wantErr: false, + }, + "default_num_entries": { + input: []byte(`{"inter_query_builtin_value_cache": {},}`), + wantErr: false, + }, + "bad_limit": { + input: []byte(`{"inter_query_builtin_cache": {"max_size_bytes": "100"},}`), + wantErr: true, + }, + "bad_num_entries": { + input: []byte(`{"inter_query_builtin_value_cache": {"max_num_entries": "100"},}`), + wantErr: true, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + config, err := ParseCachingConfig(tc.input) + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + } else { + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + } + + if !tc.wantErr && !reflect.DeepEqual(config, expected) { + t.Fatalf("want %v got %v", expected, config) + } + }) + } + + // cache limit specified + in := `{"inter_query_builtin_cache": {"max_size_bytes": 100},}` + + config, err := ParseCachingConfig([]byte(in)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + limit := int64(100) + expected.InterQueryBuiltinCache.MaxSizeBytes = &limit + + if !reflect.DeepEqual(config, expected) { + t.Fatalf("want %v got %v", expected, config) + } +} + +func TestInterValueCache_DefaultConfiguration(t *testing.T) { + t.Run("default config not set", func(t *testing.T) { + config := Config{ + InterQueryBuiltinValueCache: InterQueryBuiltinValueCacheConfig{}, + } + + c := NewInterQueryValueCache(context.Background(), &config) + if c.GetCache("foo") != nil { + t.Fatal("Expected cache to be disabled") + } + }) + + t.Run("default config set", func(t *testing.T) { + config := Config{ + InterQueryBuiltinValueCache: InterQueryBuiltinValueCacheConfig{}, + } + + RegisterDefaultInterQueryBuiltinValueCacheConfig("bar", &NamedValueCacheConfig{ + MaxNumEntries: &[]int{5}[0], + }) + + c := NewInterQueryValueCache(context.Background(), &config) + if act := *c.GetCache("bar").(*interQueryValueCacheBucket).config.MaxNumEntries; act != 5 { + t.Fatalf("Expected 5 max entries, got %d", act) + } + }) + + t.Run("explicitly disabled", func(t *testing.T) { + cacheConfig := Config{ + InterQueryBuiltinValueCache: InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*NamedValueCacheConfig{ + "baz": nil, + }, + }, + } + + RegisterDefaultInterQueryBuiltinValueCacheConfig("baz", nil) + + c := NewInterQueryValueCache(context.Background(), &cacheConfig) + if c.GetCache("baz") != nil { + t.Fatal("Expected cache to be disabled") + } + }) + + t.Run("override", func(t *testing.T) { + cacheConfig := Config{ + InterQueryBuiltinValueCache: InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*NamedValueCacheConfig{ + "box": { + MaxNumEntries: &[]int{5}[0], + }, + }, + }, + } + + RegisterDefaultInterQueryBuiltinValueCacheConfig("box", &NamedValueCacheConfig{ + MaxNumEntries: &[]int{10}[0], + }) + + c := NewInterQueryValueCache(context.Background(), &cacheConfig) + if act := *c.GetCache("box").(*interQueryValueCacheBucket).config.MaxNumEntries; act != 5 { + t.Fatalf("Expected 5 max entries, got %d", act) + } + }) +} + +func TestInterValueCache_NamedCaches(t *testing.T) { + t.Parallel() + + t.Run("configured max is respected", func(t *testing.T) { + config := Config{ + InterQueryBuiltinValueCache: InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*NamedValueCacheConfig{ + "foo": { + MaxNumEntries: &[]int{2}[0], + }, + }, + }, + } + + c := NewInterQueryValueCache(context.Background(), &config) + + nc := c.GetCache("foo").(*interQueryValueCacheBucket) + if act := *nc.config.MaxNumEntries; act != 2 { + t.Fatalf("Expected 2 max entries, got %d", act) + } + + if nc.items.Len() != 0 { + t.Fatalf("Expected cache to be empty") + } + + nc.Insert(ast.StringTerm("a").Value, "b") + if nc.items.Len() != 1 { + t.Fatalf("Expected cache to have 1 entry") + } + if v, found := nc.Get(ast.StringTerm("a").Value); !found && v != "b" { + t.Fatalf("Expected cache hit") + } + + nc.Insert(ast.StringTerm("c").Value, "d") + if nc.items.Len() != 2 { + t.Fatalf("Expected cache to have 2 entries") + } + if v, found := nc.Get(ast.StringTerm("c").Value); !found && v != "d" { + t.Fatalf("Expected cache hit") + } + + nc.Insert(ast.StringTerm("e").Value, "f") + if nc.items.Len() != 2 { + t.Fatalf("Expected cache to still have 2 entries") + } + if v, found := nc.Get(ast.StringTerm("e").Value); !found && v != "f" { + t.Fatalf("Expected cache hit") + } + }) + + t.Run("named caches are separate", func(t *testing.T) { + config := Config{ + InterQueryBuiltinValueCache: InterQueryBuiltinValueCacheConfig{ + MaxNumEntries: &[]int{2}[0], + NamedCacheConfigs: map[string]*NamedValueCacheConfig{ + "foo": { + MaxNumEntries: &[]int{2}[0], + }, + "bar": { + MaxNumEntries: &[]int{2}[0], + }, + }, + }, + } + + c := NewInterQueryValueCache(context.Background(), &config) + + c.Insert(ast.StringTerm("foo").Value, "bar") + + nc1 := c.GetCache("foo").(*interQueryValueCacheBucket) + nc2 := c.GetCache("bar").(*interQueryValueCacheBucket) + + nc1.Insert(ast.StringTerm("a").Value, "b") + nc2.Insert(ast.StringTerm("c").Value, "d") + + if _, found := c.Get(ast.StringTerm("foo").Value); !found { + t.Fatal("Expected cache hit") + } + if _, found := c.Get(ast.StringTerm("a").Value); found { + t.Fatal("Expected cache miss") + } + if _, found := c.Get(ast.StringTerm("c").Value); found { + t.Fatal("Expected cache miss") + } + + if _, found := nc1.Get(ast.StringTerm("a").Value); !found { + t.Fatal("Expected cache hit") + } + if _, found := nc1.Get(ast.StringTerm("c").Value); found { + t.Fatal("Expected cache miss") + } + if _, found := nc1.Get(ast.StringTerm("foo").Value); found { + t.Fatal("Expected cache miss") + } + + if _, found := nc2.Get(ast.StringTerm("c").Value); !found { + t.Fatal("Expected cache hit") + } + if _, found := nc2.Get(ast.StringTerm("a").Value); found { + t.Fatal("Expected cache miss") + } + if _, found := nc2.Get(ast.StringTerm("foo").Value); found { + t.Fatal("Expected cache miss") + } + }) +} + +func TestInsert(t *testing.T) { + t.Parallel() + + in := `{"inter_query_builtin_cache": {"max_size_bytes": 20},}` // 20 byte limit for test purposes + + config, err := ParseCachingConfig([]byte(in)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + cache := NewInterQueryCache(config) + + // large cache value that exceeds limit + cacheValueLarge := newInterQueryCacheValue(ast.StringTerm("bar").Value, 40) + dropped := cache.Insert(ast.StringTerm("foo").Value, cacheValueLarge) + + if dropped != 1 { + t.Fatal("Expected dropped to be one") + } + + _, found := cache.Get(ast.StringTerm("foo").Value) + if found { + t.Fatal("Unexpected key \"foo\" in cache") + } + + cacheValue := newInterQueryCacheValue(ast.StringTerm("bar").Value, 20) + dropped = cache.Insert(ast.StringTerm("foo").Value, cacheValue) + + if dropped != 0 { + t.Fatal("Expected dropped to be zero") + } + + // exceed cache limit + cacheValue2 := newInterQueryCacheValue(ast.StringTerm("bar2").Value, 20) + dropped = cache.Insert(ast.StringTerm("foo2").Value, cacheValue2) + + if dropped != 1 { + t.Fatal("Expected dropped to be one") + } + + _, found = cache.Get(ast.StringTerm("foo2").Value) + if !found { + t.Fatal("Expected key \"foo2\" in cache") + } + + _, found = cache.Get(ast.StringTerm("foo").Value) + if found { + t.Fatal("Unexpected key \"foo\" in cache") + } + cacheValue3 := newInterQueryCacheValue(ast.StringTerm("bar3").Value, 10) + cache.Insert(ast.StringTerm("foo3").Value, cacheValue3) + cacheValue4 := newInterQueryCacheValue(ast.StringTerm("bar4").Value, 10) + cache.Insert(ast.StringTerm("foo4").Value, cacheValue4) + cacheValue5 := newInterQueryCacheValue(ast.StringTerm("bar5").Value, 20) + dropped = cache.Insert(ast.StringTerm("foo5").Value, cacheValue5) + if dropped != 2 { + t.Fatal("Expected dropped to be two") + } + _, found = cache.Get(ast.StringTerm("foo3").Value) + if found { + t.Fatal("Unexpected key \"foo3\" in cache") + } + _, found = cache.Get(ast.StringTerm("foo4").Value) + if found { + t.Fatal("Unexpected key \"foo4\" in cache") + } + _, found = cache.Get(ast.StringTerm("foo5").Value) + if !found { + t.Fatal("Expected key \"foo5\" in cache") + } + verifyCacheList(t, cache) + + // replacing an existing key should not affect cache size + cache = NewInterQueryCache(config) + + cacheValue6 := newInterQueryCacheValue(ast.String("bar6"), 10) + cache.Insert(ast.String("foo6"), cacheValue6) + cache.Insert(ast.String("foo6"), cacheValue6) + verifyCacheList(t, cache) + + cacheValue7 := newInterQueryCacheValue(ast.String("bar7"), 10) + dropped = cache.Insert(ast.StringTerm("foo7").Value, cacheValue7) + verifyCacheList(t, cache) + + if dropped != 0 { + t.Fatal("Expected dropped to be zero") + } +} + +func TestInterQueryValueCache(t *testing.T) { + t.Parallel() + + in := `{"inter_query_builtin_value_cache": {"max_num_entries": 4},}` + + config, err := ParseCachingConfig([]byte(in)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + cache := NewInterQueryValueCache(context.Background(), config) + + cache.Insert(ast.StringTerm("foo").Value, "bar") + cache.Insert(ast.StringTerm("foo2").Value, "bar2") + cache.Insert(ast.StringTerm("hello").Value, "world") + dropped := cache.Insert(ast.StringTerm("hello2").Value, "world2") + + if dropped != 0 { + t.Fatal("Expected dropped to be zero") + } + + value, found := cache.Get(ast.StringTerm("foo").Value) + if !found { + t.Fatal("Expected key \"foo\" in cache") + } + + actual, ok := value.(string) + if !ok { + t.Fatal("Expected string value") + } + + if actual != "bar" { + t.Fatalf("Expected value \"bar\" but got %v", actual) + } + + dropped = cache.Insert(ast.StringTerm("foo3").Value, "bar3") + if dropped != 1 { + t.Fatal("Expected dropped to be one") + } + + _, found = cache.Get(ast.StringTerm("foo3").Value) + if !found { + t.Fatal("Expected key \"foo3\" in cache") + } + + // update the cache config + in = `{"inter_query_builtin_value_cache": {"max_num_entries": 0},}` // unlimited + config, err = ParseCachingConfig([]byte(in)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + cache.UpdateConfig(config) + + cache.Insert(ast.StringTerm("a").Value, "b") + cache.Insert(ast.StringTerm("c").Value, "d") + cache.Insert(ast.StringTerm("e").Value, "f") + dropped = cache.Insert(ast.StringTerm("g").Value, "h") + + if dropped != 0 { + t.Fatal("Expected dropped to be zero") + } + + // at this point the cache should have 8 entries + // update the cache size and verify multiple items dropped + in = `{"inter_query_builtin_value_cache": {"max_num_entries": 6},}` + config, err = ParseCachingConfig([]byte(in)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + cache.UpdateConfig(config) + + dropped = cache.Insert(ast.StringTerm("i").Value, "j") + + if dropped != 3 { + t.Fatal("Expected dropped to be three") + } + + _, found = cache.Get(ast.StringTerm("i").Value) + if !found { + t.Fatal("Expected key \"i\" in cache") + } + + cache.Delete(ast.StringTerm("i").Value) + + _, found = cache.Get(ast.StringTerm("i").Value) + if found { + t.Fatal("Unexpected key \"i\" in cache") + } +} + +func TestConcurrentInsert(t *testing.T) { + t.Parallel() + + in := `{"inter_query_builtin_cache": {"max_size_bytes": 20},}` // 20 byte limit for test purposes + + config, err := ParseCachingConfig([]byte(in)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + cache := NewInterQueryCache(config) + + cacheValue := newInterQueryCacheValue(ast.String("bar"), 10) + cache.Insert(ast.String("foo"), cacheValue) + + wg := sync.WaitGroup{} + + for range 5 { + wg.Add(1) + + go func() { + defer wg.Done() + + cacheValue2 := newInterQueryCacheValue(ast.String("bar2"), 5) + cache.Insert(ast.String("foo2"), cacheValue2) + + }() + } + wg.Wait() + + cacheValue3 := newInterQueryCacheValue(ast.String("bar3"), 5) + dropped := cache.Insert(ast.String("foo3"), cacheValue3) + verifyCacheList(t, cache) + + if dropped != 0 { + t.Fatal("Expected dropped to be zero") + } + + _, found := cache.Get(ast.String("foo")) + if !found { + t.Fatal("Expected key \"foo\" in cache") + } + + _, found = cache.Get(ast.String("foo2")) + if !found { + t.Fatal("Expected key \"foo2\" in cache") + } + + _, found = cache.Get(ast.String("foo3")) + if !found { + t.Fatal("Expected key \"foo3\" in cache") + } +} + +func TestClone(t *testing.T) { + t.Parallel() + + in := `{"inter_query_builtin_cache": {"max_size_bytes": 40},}` + + config, err := ParseCachingConfig([]byte(in)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + cache := NewInterQueryCache(config) + + cacheValue := newInterQueryCacheValue(ast.StringTerm("bar").Value, 20) + dropped := cache.Insert(ast.StringTerm("foo").Value, cacheValue) + if dropped != 0 { + t.Fatal("Expected dropped to be zero") + } + + val, found := cache.Get(ast.StringTerm("foo").Value) + if !found { + t.Fatal("Expected key \"foo\" in cache") + } + + dup, err := cache.Clone(val) + if err != nil { + t.Fatal(err) + } + + original, ok := val.(*testInterQueryCacheValue) + if !ok { + t.Fatal("unexpected type") + } + + cloned, ok := dup.(*testInterQueryCacheValue) + if !ok { + t.Fatal("unexpected type") + } + + if !reflect.DeepEqual(*original, *cloned) { + t.Fatalf("Expected to get %v, but got %v", *original, *cloned) + } +} + +func TestDelete(t *testing.T) { + t.Parallel() + + config, err := ParseCachingConfig(nil) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + cache := NewInterQueryCache(config) + cacheValue := newInterQueryCacheValue(ast.StringTerm("bar").Value, 20) + + dropped := cache.Insert(ast.StringTerm("foo").Value, cacheValue) + + if dropped != 0 { + t.Fatal("Expected dropped to be zero") + } + verifyCacheList(t, cache) + + cache.Delete(ast.StringTerm("foo").Value) + + _, found := cache.Get(ast.StringTerm("foo").Value) + if found { + t.Fatal("Unexpected key \"foo\" in cache") + } + verifyCacheList(t, cache) +} + +func TestInsertWithExpiryAndEviction(t *testing.T) { + t.Parallel() + + // 50 byte max size + // 1s stale cleanup period + // 80% threshold to for FIFO eviction (eviction after 40 bytes) + in := `{"inter_query_builtin_cache": {"max_size_bytes": 50, "stale_entry_eviction_period_seconds": 1, "forced_eviction_threshold_percentage": 80},}` + + config, err := ParseCachingConfig([]byte(in)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // This starts a background ticker at stale_entry_eviction_period_seconds to clean up items. + ctx, cancel := context.WithCancel(context.Background()) + cache := NewInterQueryCacheWithContext(ctx, config) + t.Cleanup(cancel) + + cacheValue := newInterQueryCacheValue(ast.StringTerm("bar").Value, 20) + cache.InsertWithExpiry(ast.StringTerm("force_evicted_foo").Value, cacheValue, time.Now().Add(100*time.Second)) + if fetchedCacheValue, found := cache.Get(ast.StringTerm("force_evicted_foo").Value); !found { + t.Fatalf("Expected cache entry with value %v, found %v", cacheValue, fetchedCacheValue) + } + cache.InsertWithExpiry(ast.StringTerm("expired_foo").Value, cacheValue, time.Now().Add(900*time.Millisecond)) + if fetchedCacheValue, found := cache.Get(ast.StringTerm("expired_foo").Value); !found { + t.Fatalf("Expected cache entry with value %v, found %v", cacheValue, fetchedCacheValue) + } + cache.InsertWithExpiry(ast.StringTerm("foo").Value, cacheValue, time.Now().Add(10*time.Second)) + if fetchedCacheValue, found := cache.Get(ast.StringTerm("foo").Value); !found { + t.Fatalf("Expected cache entry with value %v, found %v", cacheValue, fetchedCacheValue) + } + + // Ensure stale entries clean up routine runs at least once + time.Sleep(1100 * time.Millisecond) + + // Entry deleted even though not expired because force evicted when foo is inserted + if fetchedCacheValue, found := cache.Get(ast.StringTerm("force_evicted_foo").Value); found { + t.Fatalf("Didn't expect cache entry for force_evicted_foo, found entry with value %v", fetchedCacheValue) + } + // Stale clean up routine runs and deletes expired entry + if fetchedCacheValue, found := cache.Get(ast.StringTerm("expired_foo").Value); found { + t.Fatalf("Didn't expect cache entry for expired_foo, found entry with value %v", fetchedCacheValue) + } + // Stale clean up routine runs but doesn't delete the entry + if fetchedCacheValue, found := cache.Get(ast.StringTerm("foo").Value); !found { + t.Fatalf("Expected cache entry with value %v for foo, found %v", cacheValue, fetchedCacheValue) + } +} + +func TestInsertHighTTLWithStaleEntryCleanup(t *testing.T) { + t.Parallel() + + // 40 byte max size + // 1s stale cleanup period + // 100% threshold to for FIFO eviction (eviction after 40 bytes) + in := `{"inter_query_builtin_cache": {"max_size_bytes": 40, "stale_entry_eviction_period_seconds": 1, "forced_eviction_threshold_percentage": 100},}` + + config, err := ParseCachingConfig([]byte(in)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // This starts a background ticker at stale_entry_eviction_period_seconds to clean up items. + ctx, cancel := context.WithCancel(context.Background()) + cache := NewInterQueryCacheWithContext(ctx, config) + t.Cleanup(cancel) + + cacheValue := newInterQueryCacheValue(ast.StringTerm("bar").Value, 20) + cache.InsertWithExpiry(ast.StringTerm("high_ttl_foo").Value, cacheValue, time.Now().Add(100*time.Second)) + if fetchedCacheValue, found := cache.Get(ast.StringTerm("high_ttl_foo").Value); !found { + t.Fatalf("Expected cache entry with value %v, found %v", cacheValue, fetchedCacheValue) + } + cache.InsertWithExpiry(ast.StringTerm("expired_foo").Value, cacheValue, time.Now().Add(900*time.Millisecond)) + if fetchedCacheValue, found := cache.Get(ast.StringTerm("expired_foo").Value); !found { + t.Fatalf("Expected cache entry with value %v, found no entry", fetchedCacheValue) + } + + // Ensure stale entries clean up routine runs at least once + time.Sleep(1100 * time.Millisecond) + + cache.InsertWithExpiry(ast.StringTerm("foo").Value, cacheValue, time.Now().Add(10*time.Second)) + if fetchedCacheValue, found := cache.Get(ast.StringTerm("foo").Value); !found { + t.Fatalf("Expected cache entry with value %v, found %v", cacheValue, fetchedCacheValue) + } + + // Since expired_foo is deleted by stale cleanup routine, high_ttl_foo is not evicted when foo is inserted + if fetchedCacheValue, found := cache.Get(ast.StringTerm("high_ttl_foo").Value); !found { + t.Fatalf("Expected cache entry with value %v for high_ttl_foo, found %v", cacheValue, fetchedCacheValue) + } + // Stale clean up routine runs and deletes expired entry + if fetchedCacheValue, found := cache.Get(ast.StringTerm("expired_foo").Value); found { + t.Fatalf("Didn't expect cache entry for expired_foo, found entry with value %v", fetchedCacheValue) + } +} + +func TestInsertHighTTLWithoutStaleEntryCleanup(t *testing.T) { + t.Parallel() + + // 40 byte max size + // 0s stale cleanup period -> no cleanup + // 100% threshold to for FIFO eviction (eviction after 40 bytes) + in := `{"inter_query_builtin_cache": {"max_size_bytes": 40, "stale_entry_eviction_period_seconds": 0, "forced_eviction_threshold_percentage": 100},}` + + config, err := ParseCachingConfig([]byte(in)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // This starts a background ticker at stale_entry_eviction_period_seconds to clean up items. + ctx, cancel := context.WithCancel(context.Background()) + cache := NewInterQueryCacheWithContext(ctx, config) + t.Cleanup(cancel) + + cacheValue := newInterQueryCacheValue(ast.StringTerm("bar").Value, 20) + cache.InsertWithExpiry(ast.StringTerm("high_ttl_foo").Value, cacheValue, time.Now().Add(100*time.Second)) + if fetchedCacheValue, found := cache.Get(ast.StringTerm("high_ttl_foo").Value); !found { + t.Fatalf("Expected cache entry with value %v for high_ttl_foo, found no entry", fetchedCacheValue) + } + cache.InsertWithExpiry(ast.StringTerm("expired_foo").Value, cacheValue, time.Now().Add(1*time.Second)) + if fetchedCacheValue, found := cache.Get(ast.StringTerm("expired_foo").Value); !found { + t.Fatalf("Expected cache entry with value %v for expired_foo, found no entry", fetchedCacheValue) + } + + cache.InsertWithExpiry(ast.StringTerm("foo").Value, cacheValue, time.Now().Add(10*time.Second)) + if fetchedCacheValue, found := cache.Get(ast.StringTerm("foo").Value); !found { + t.Fatalf("Expected cache entry with value %v for foo, found no entry", fetchedCacheValue) + } + + // Since stale cleanup routine is disabled, high_ttl_foo is evicted when foo is inserted + if fetchedCacheValue, found := cache.Get(ast.StringTerm("high_ttl_foo").Value); found { + t.Fatalf("Didn't expect cache entry for high_ttl_foo, found entry with value %v", fetchedCacheValue) + } + // Stale clean up disabled so expired entry exists + if fetchedCacheValue, found := cache.Get(ast.StringTerm("expired_foo").Value); !found { + t.Fatalf("Expected cache entry with value %v for expired_foo, found %v", cacheValue, fetchedCacheValue) + } +} + +func TestZeroExpiryTime(t *testing.T) { + t.Parallel() + + // 20 byte max size + // 1s stale cleanup period + // 100% threshold to for FIFO eviction (eviction after 40 bytes) + in := `{"inter_query_builtin_cache": {"max_size_bytes": 20, "stale_entry_eviction_period_seconds": 1, "forced_eviction_threshold_percentage": 100},}` + + config, err := ParseCachingConfig([]byte(in)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + // This starts a background ticker at stale_entry_eviction_period_seconds to clean up items. + ctx, cancel := context.WithCancel(context.Background()) + cache := NewInterQueryCacheWithContext(ctx, config) + t.Cleanup(cancel) + cacheValue := newInterQueryCacheValue(ast.StringTerm("bar").Value, 20) + cache.InsertWithExpiry(ast.StringTerm("foo").Value, cacheValue, time.Time{}) + if fetchedCacheValue, found := cache.Get(ast.StringTerm("foo").Value); !found { + t.Fatalf("Expected cache entry with value %v for foo, found %v", cacheValue, fetchedCacheValue) + } + + time.Sleep(1100 * time.Millisecond) + + // Stale entry cleanup routine skips zero time cache entries + if fetchedCacheValue, found := cache.Get(ast.StringTerm("foo").Value); !found { + t.Fatalf("Expected cache entry with value %v for foo, found %v", cacheValue, fetchedCacheValue) + } +} + +func TestCancelNewInterQueryCacheWithContext(t *testing.T) { + t.Parallel() + + // 40 byte max size + // 1s stale cleanup period + // 100% threshold to for FIFO eviction (eviction after 40 bytes) + in := `{"inter_query_builtin_cache": {"max_size_bytes": 40, "stale_entry_eviction_period_seconds": 1, "forced_eviction_threshold_percentage": 100},}` + + config, err := ParseCachingConfig([]byte(in)) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + ctx, cancel := context.WithCancel(context.Background()) + cache := NewInterQueryCacheWithContext(ctx, config) + cacheValue := newInterQueryCacheValue(ast.StringTerm("bar").Value, 20) + cache.InsertWithExpiry(ast.StringTerm("foo").Value, cacheValue, time.Now().Add(100*time.Millisecond)) + if fetchedCacheValue, found := cache.Get(ast.StringTerm("foo").Value); !found { + t.Fatalf("Expected cache entry with value %v for foo, found %v", cacheValue, fetchedCacheValue) + } + + cancel() + time.Sleep(1100 * time.Millisecond) + + // Stale entry cleanup routine stopped as context was cancelled + if fetchedCacheValue, found := cache.Get(ast.StringTerm("foo").Value); !found { + t.Fatalf("Expected cache entry with value %v for foo, found %v", cacheValue, fetchedCacheValue) + } + +} + +func TestUpdateConfig(t *testing.T) { + t.Parallel() + + config, err := ParseCachingConfig(nil) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + c := NewInterQueryCache(config) + actualC, ok := c.(*cache) + if !ok { + t.Fatal("Unexpected error converting InterQueryCache to cache struct") + } + if actualC.config != config { + t.Fatal("Cache config is different than expected") + } + actualC.UpdateConfig(nil) + if actualC.config != config { + t.Fatal("Cache config is different than expected after a nil update") + } + config2, err := ParseCachingConfig(nil) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + actualC.UpdateConfig(config2) + if actualC.config != config2 { + t.Fatal("Cache config is different than expected after update") + } +} + +func TestDefaultConfigValues(t *testing.T) { + t.Parallel() + + c := NewInterQueryCache(nil) + actualC, ok := c.(*cache) + if !ok { + t.Fatal("Unexpected error converting InterQueryCache to cache struct") + } + if actualC.maxSizeBytes() != defaultMaxSizeBytes { + t.Fatal("Expected maxSizeBytes() to return default when config is nil") + } + if actualC.forcedEvictionThresholdPercentage() != defaultForcedEvictionThresholdPercentage { + t.Fatal("Expected forcedEvictionThresholdPercentage() to return default when config is nil") + } + if actualC.staleEntryEvictionTimePeriodSeconds() != defaultStaleEntryEvictionPeriodSeconds { + t.Fatal("Expected staleEntryEvictionTimePeriodSeconds() to return default when config is nil") + } +} + +// Verifies that the size of c.l is identical to the size of c.items +// Since the size of c.items is limited by c.usage, this helps us +// avoid a situation where c.l can grow indefinitely causing a memory leak +func verifyCacheList(t *testing.T, c InterQueryCache) { + actualC, ok := c.(*cache) + if !ok { + t.Fatal("Unexpected error converting InterQueryCache to cache struct") + } + if len(actualC.items) != actualC.l.Len() { + t.Fatal("actualC.l should contain equally many elements as actualC.items") + } +} + +type testInterQueryCacheValue struct { + value ast.Value + size int +} + +func newInterQueryCacheValue(value ast.Value, size int) *testInterQueryCacheValue { + return &testInterQueryCacheValue{value: value, size: size} +} + +func (p testInterQueryCacheValue) SizeInBytes() int64 { + return int64(p.size) +} + +func (p testInterQueryCacheValue) Clone() (InterQueryCacheValue, error) { + return &testInterQueryCacheValue{value: p.value, size: p.size}, nil +} diff --git a/third_party/opa/v1/topdown/cache_bench_test.go b/third_party/opa/v1/topdown/cache_bench_test.go new file mode 100644 index 000000000000..7e39c189ae7a --- /dev/null +++ b/third_party/opa/v1/topdown/cache_bench_test.go @@ -0,0 +1,48 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func BenchmarkVirtualCache(b *testing.B) { + + n := 10 + max := n * n * n + + keys := make([]ast.Ref, 0, max) + values := make([]*ast.Term, 0, max) + + for i := range n { + k1 := ast.StringTerm(fmt.Sprintf("aaaa%v", i)) + for j := range n { + k2 := ast.StringTerm(fmt.Sprintf("bbbb%v", j)) + for k := range n { + k3 := ast.StringTerm(fmt.Sprintf("cccc%v", k)) + key := ast.Ref{ast.DefaultRootDocument, k1, k2, k3} + value := ast.ArrayTerm(k1, k2, k3) + keys = append(keys, key) + values = append(values, value) + } + } + } + + cache := NewVirtualCache() + b.ResetTimer() + + for i := range b.N { + idx := i % max + cache.Put(keys[idx], values[idx]) + result, _ := cache.Get(keys[idx]) + if !result.Equal(values[idx]) { + b.Fatal("expected equal") + } + } + +} diff --git a/third_party/opa/v1/topdown/cache_test.go b/third_party/opa/v1/topdown/cache_test.go new file mode 100644 index 000000000000..1a4be25bce12 --- /dev/null +++ b/third_party/opa/v1/topdown/cache_test.go @@ -0,0 +1,61 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestVirtualCacheCompositeKey(t *testing.T) { + t.Parallel() + + cache := NewVirtualCache() + ref := ast.MustParseRef("data.x.y[[1]].z") + cache.Put(ref, ast.BooleanTerm(true)) + result, _ := cache.Get(ref) + if !result.Equal(ast.BooleanTerm(true)) { + t.Fatalf("Expected true but got %v", result) + } +} + +func TestVirtualCacheInvalidate(t *testing.T) { + t.Parallel() + + cache := NewVirtualCache() + cache.Push() + cache.Put(ast.MustParseRef("data.x.p"), ast.BooleanTerm(true)) + cache.Pop() + result, _ := cache.Get(ast.MustParseRef("data.x.p")) + if result != nil { + t.Fatal("Expected nil result but got:", result) + } +} + +func TestSetAndRetriveUndefined(t *testing.T) { + t.Parallel() + + cache := NewVirtualCache() + cache.Put(ast.MustParseRef("data.foo.bar"), nil) + result, undefined := cache.Get(ast.MustParseRef("data.foo.bar")) + if result != nil { + t.Fatal("Expected nil result but got:", result) + } + if !undefined { + t.Fatal("Expected 'undefined' flag to be false got true") + } +} + +func TestBaseCacheGetExactMatch(t *testing.T) { + t.Parallel() + + cache := newBaseCache() + cache.Put(ast.MustParseRef("data.x.foo"), ast.StringTerm("bar").Value) + result := cache.Get(ast.MustParseRef("data.x.foo")) + if result != ast.StringTerm("bar").Value { + t.Fatalf("Expected bar but got %v", result) + } +} diff --git a/third_party/opa/v1/topdown/cancel.go b/third_party/opa/v1/topdown/cancel.go new file mode 100644 index 000000000000..534e0799a159 --- /dev/null +++ b/third_party/opa/v1/topdown/cancel.go @@ -0,0 +1,33 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "sync/atomic" +) + +// Cancel defines the interface for cancelling topdown queries. Cancel +// operations are thread-safe and idempotent. +type Cancel interface { + Cancel() + Cancelled() bool +} + +type cancel struct { + flag int32 +} + +// NewCancel returns a new Cancel object. +func NewCancel() Cancel { + return &cancel{} +} + +func (c *cancel) Cancel() { + atomic.StoreInt32(&c.flag, 1) +} + +func (c *cancel) Cancelled() bool { + return atomic.LoadInt32(&c.flag) != 0 +} diff --git a/third_party/opa/v1/topdown/casts.go b/third_party/opa/v1/topdown/casts.go new file mode 100644 index 000000000000..85e1a9c0151e --- /dev/null +++ b/third_party/opa/v1/topdown/casts.go @@ -0,0 +1,131 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "strconv" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func builtinToNumber(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch a := operands[0].Value.(type) { + case ast.Null: + return iter(ast.InternedTerm(0)) + case ast.Boolean: + if a { + return iter(ast.InternedTerm(1)) + } + return iter(ast.InternedTerm(0)) + case ast.Number: + return iter(operands[0]) + case ast.String: + strValue := string(a) + + if it := ast.InternedIntNumberTermFromString(strValue); it != nil { + return iter(it) + } + + trimmedVal := strings.TrimLeft(strValue, "+-") + lowerCaseVal := strings.ToLower(trimmedVal) + + if lowerCaseVal == "inf" || lowerCaseVal == "infinity" || lowerCaseVal == "nan" { + return builtins.NewOperandTypeErr(1, operands[0].Value, "valid number string") + } + + _, err := strconv.ParseFloat(strValue, 64) + if err != nil { + return err + } + return iter(ast.NewTerm(ast.Number(a))) + } + return builtins.NewOperandTypeErr(1, operands[0].Value, "null", "boolean", "number", "string") +} + +// Deprecated: deprecated in v0.13.0. +func builtinToArray(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch val := operands[0].Value.(type) { + case *ast.Array: + return iter(ast.NewTerm(val)) + case ast.Set: + arr := make([]*ast.Term, val.Len()) + i := 0 + val.Foreach(func(term *ast.Term) { + arr[i] = term + i++ + }) + return iter(ast.NewTerm(ast.NewArray(arr...))) + default: + return builtins.NewOperandTypeErr(1, operands[0].Value, "array", "set") + } +} + +// Deprecated: deprecated in v0.13.0. +func builtinToSet(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch val := operands[0].Value.(type) { + case *ast.Array: + s := ast.NewSet() + val.Foreach(func(v *ast.Term) { + s.Add(v) + }) + return iter(ast.NewTerm(s)) + case ast.Set: + return iter(ast.NewTerm(val)) + default: + return builtins.NewOperandTypeErr(1, operands[0].Value, "array", "set") + } +} + +// Deprecated: deprecated in v0.13.0. +func builtinToString(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch val := operands[0].Value.(type) { + case ast.String: + return iter(ast.NewTerm(val)) + default: + return builtins.NewOperandTypeErr(1, operands[0].Value, "string") + } +} + +// Deprecated: deprecated in v0.13.0. +func builtinToBoolean(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch val := operands[0].Value.(type) { + case ast.Boolean: + return iter(ast.NewTerm(val)) + default: + return builtins.NewOperandTypeErr(1, operands[0].Value, "boolean") + } +} + +// Deprecated: deprecated in v0.13.0. +func builtinToNull(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch val := operands[0].Value.(type) { + case ast.Null: + return iter(ast.NewTerm(val)) + default: + return builtins.NewOperandTypeErr(1, operands[0].Value, "null") + } +} + +// Deprecated: deprecated in v0.13.0. +func builtinToObject(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch val := operands[0].Value.(type) { + case ast.Object: + return iter(ast.NewTerm(val)) + default: + return builtins.NewOperandTypeErr(1, operands[0].Value, "object") + } +} + +func init() { + RegisterBuiltinFunc(ast.ToNumber.Name, builtinToNumber) + RegisterBuiltinFunc(ast.CastArray.Name, builtinToArray) + RegisterBuiltinFunc(ast.CastSet.Name, builtinToSet) + RegisterBuiltinFunc(ast.CastString.Name, builtinToString) + RegisterBuiltinFunc(ast.CastBoolean.Name, builtinToBoolean) + RegisterBuiltinFunc(ast.CastNull.Name, builtinToNull) + RegisterBuiltinFunc(ast.CastObject.Name, builtinToObject) +} diff --git a/third_party/opa/v1/topdown/cidr.go b/third_party/opa/v1/topdown/cidr.go new file mode 100644 index 000000000000..12a441496356 --- /dev/null +++ b/third_party/opa/v1/topdown/cidr.go @@ -0,0 +1,419 @@ +package topdown + +import ( + "bytes" + "errors" + "fmt" + "math/big" + "net" + "slices" + "sort" + + cidrMerge "github.com/open-policy-agent/opa/internal/cidr/merge" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func getNetFromOperand(v ast.Value) (*net.IPNet, error) { + subnetStringA, err := builtins.StringOperand(v, 1) + if err != nil { + return nil, err + } + + _, cidrnet, err := net.ParseCIDR(string(subnetStringA)) + if err != nil { + return nil, err + } + + return cidrnet, nil +} + +func getLastIP(cidr *net.IPNet) (net.IP, error) { + prefixLen, bits := cidr.Mask.Size() + if prefixLen == 0 && bits == 0 { + // non-standard mask, see https://golang.org/pkg/net/#IPMask.Size + return nil, errors.New("CIDR mask is in non-standard format") + } + var lastIP []byte + if prefixLen == bits { + // Special case for single ip address ranges ex: 192.168.1.1/32 + // We can just use the starting IP as the last IP + lastIP = cidr.IP + } else { + // Use big.Int's so we can handle ipv6 addresses + firstIPInt := new(big.Int) + firstIPInt.SetBytes(cidr.IP) + hostLen := uint(bits) - uint(prefixLen) + lastIPInt := big.NewInt(1) + lastIPInt.Lsh(lastIPInt, hostLen) + lastIPInt.Sub(lastIPInt, big.NewInt(1)) + lastIPInt.Or(lastIPInt, firstIPInt) + + ipBytes := lastIPInt.Bytes() + lastIP = make([]byte, bits/8) + + // Pack our IP bytes into the end of the return array, + // since big.Int.Bytes() removes front zero padding. + for i := 1; i <= len(lastIPInt.Bytes()); i++ { + lastIP[len(lastIP)-i] = ipBytes[len(ipBytes)-i] + } + } + + return lastIP, nil +} + +func builtinNetCIDRIntersects(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + cidrnetA, err := getNetFromOperand(operands[0].Value) + if err != nil { + return err + } + + cidrnetB, err := getNetFromOperand(operands[1].Value) + if err != nil { + return err + } + + // If either net contains the others starting IP they are overlapping + cidrsOverlap := cidrnetA.Contains(cidrnetB.IP) || cidrnetB.Contains(cidrnetA.IP) + + return iter(ast.InternedTerm(cidrsOverlap)) +} + +func builtinNetCIDRContains(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + cidrnetA, err := getNetFromOperand(operands[0].Value) + if err != nil { + return err + } + + // b could be either an IP addressor CIDR string, try to parse it as an IP first, fall back to CIDR + bStr, err := builtins.StringOperand(operands[1].Value, 1) + if err != nil { + return err + } + + ip := net.ParseIP(string(bStr)) + if ip != nil { + return iter(ast.InternedTerm(cidrnetA.Contains(ip))) + } + + // It wasn't an IP, try and parse it as a CIDR + cidrnetB, err := getNetFromOperand(operands[1].Value) + if err != nil { + return fmt.Errorf("not a valid textual representation of an IP address or CIDR: %s", string(bStr)) + } + + // We can determine if cidr A contains cidr B if and only if A contains + // the starting address of B and the last address in B. + cidrContained := false + if cidrnetA.Contains(cidrnetB.IP) { + // Only spend time calculating the last IP if the starting IP is already verified to be in cidr A + lastIP, err := getLastIP(cidrnetB) + if err != nil { + return err + } + cidrContained = cidrnetA.Contains(lastIP) + } + + return iter(ast.InternedTerm(cidrContained)) +} + +var errNetCIDRContainsMatchElementType = errors.New("element must be string or non-empty array") + +func getCIDRMatchTerm(a *ast.Term) (*ast.Term, error) { + switch v := a.Value.(type) { + case ast.String: + return a, nil + case *ast.Array: + if v.Len() == 0 { + return nil, errNetCIDRContainsMatchElementType + } + return v.Elem(0), nil + default: + return nil, errNetCIDRContainsMatchElementType + } +} + +func evalNetCIDRContainsMatchesOperand(operand int, a *ast.Term, iter func(cidr, index *ast.Term) error) error { + switch v := a.Value.(type) { + case ast.String: + return iter(a, a) + case *ast.Array: + for i := range v.Len() { + cidr, err := getCIDRMatchTerm(v.Elem(i)) + if err != nil { + return fmt.Errorf("operand %v: %v", operand, err) + } + if err := iter(cidr, ast.InternedTerm(i)); err != nil { + return err + } + } + return nil + case ast.Set: + return v.Iter(func(x *ast.Term) error { + cidr, err := getCIDRMatchTerm(x) + if err != nil { + return fmt.Errorf("operand %v: %v", operand, err) + } + return iter(cidr, x) + }) + case ast.Object: + return v.Iter(func(k, v *ast.Term) error { + cidr, err := getCIDRMatchTerm(v) + if err != nil { + return fmt.Errorf("operand %v: %v", operand, err) + } + return iter(cidr, k) + }) + } + return nil +} + +func builtinNetCIDRContainsMatches(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + result := ast.NewSet() + err := evalNetCIDRContainsMatchesOperand(1, operands[0], func(cidr1 *ast.Term, index1 *ast.Term) error { + return evalNetCIDRContainsMatchesOperand(2, operands[1], func(cidr2 *ast.Term, index2 *ast.Term) error { + if v, err := getResult(builtinNetCIDRContains, cidr1, cidr2); err != nil { + return err + } else if vb, ok := v.Value.(ast.Boolean); ok && bool(vb) { + result.Add(ast.ArrayTerm(index1, index2)) + } + return nil + }) + }) + if err == nil { + return iter(ast.NewTerm(result)) + } + return err +} + +func builtinNetCIDRExpand(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + ip, ipNet, err := net.ParseCIDR(string(s)) + if err != nil { + return err + } + + result := ast.NewSet() + + for ip := ip.Mask(ipNet.Mask); ipNet.Contains(ip); incIP(ip) { + + if bctx.Cancel != nil && bctx.Cancel.Cancelled() { + return Halt{ + Err: &Error{ + Code: CancelErr, + Message: "net.cidr_expand: timed out before generating all IP addresses", + }, + } + } + + result.Add(ast.StringTerm(ip.String())) + } + + return iter(ast.NewTerm(result)) +} + +func builtinNetCIDRIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + cidr, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return iter(ast.InternedTerm(false)) + } + + if _, _, err := net.ParseCIDR(string(cidr)); err != nil { + return iter(ast.InternedTerm(false)) + } + return iter(ast.InternedTerm(true)) +} + +type cidrBlockRange struct { + First *net.IP + Last *net.IP + Network *net.IPNet +} + +type cidrBlockRanges []*cidrBlockRange + +// Implement Sort interface +func (c cidrBlockRanges) Len() int { + return len(c) +} + +func (c cidrBlockRanges) Swap(i, j int) { + c[i], c[j] = c[j], c[i] +} + +func (c cidrBlockRanges) Less(i, j int) bool { + // Compare last IP. + cmp := bytes.Compare(*c[i].Last, *c[j].Last) + if cmp < 0 { + return true + } else if cmp > 0 { + return false + } + + // Then compare first IP. + cmp = bytes.Compare(*c[i].First, *c[j].First) + if cmp < 0 { + return true + } else if cmp > 0 { + return false + } + + // Ranges are Equal. + return false +} + +// builtinNetCIDRMerge merges the provided list of IP addresses and subnets into the smallest possible list of CIDRs. +// It merges adjacent subnets where possible, those contained within others and also removes any duplicates. +// Original Algorithm: https://github.com/netaddr/netaddr. +func builtinNetCIDRMerge(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + networks := []*net.IPNet{} + + switch v := operands[0].Value.(type) { + case *ast.Array: + for i := range v.Len() { + network, err := generateIPNet(v.Elem(i)) + if err != nil { + return err + } + networks = append(networks, network) + } + case ast.Set: + err := v.Iter(func(x *ast.Term) error { + network, err := generateIPNet(x) + if err != nil { + return err + } + networks = append(networks, network) + return nil + }) + if err != nil { + return err + } + default: + return errors.New("operand must be an array") + } + + merged := evalNetCIDRMerge(networks) + + result := ast.NewSet() + for _, network := range merged { + result.Add(ast.StringTerm(network.String())) + } + + return iter(ast.NewTerm(result)) +} + +func evalNetCIDRMerge(networks []*net.IPNet) []*net.IPNet { + if len(networks) == 0 { + return nil + } + + ranges := make(cidrBlockRanges, 0, len(networks)) + + // For each CIDR, create an IP range. Sort them and merge when possible. + for _, network := range networks { + firstIP, lastIP := cidrMerge.GetAddressRange(*network) + ranges = append(ranges, &cidrBlockRange{ + First: &firstIP, + Last: &lastIP, + Network: network, + }) + } + + // merge CIDRs. + merged := mergeCIDRs(ranges) + + // convert ranges into an equivalent list of net.IPNet. + result := []*net.IPNet{} + + for _, r := range merged { + // Not merged with any other CIDR. + if r.Network != nil { + result = append(result, r.Network) + } else { + // Find new network that represents the merged range. + rangeCIDRs := cidrMerge.RangeToCIDRs(*r.First, *r.Last) + result = append(result, rangeCIDRs...) + } + } + return result +} + +func generateIPNet(term *ast.Term) (*net.IPNet, error) { + e, ok := term.Value.(ast.String) + if !ok { + return nil, errors.New("element must be string") + } + + // try to parse element as an IP first, fall back to CIDR + ip := net.ParseIP(string(e)) + if ip == nil { + _, network, err := net.ParseCIDR(string(e)) + return network, err + } + + if ip.To4() != nil { + return &net.IPNet{ + IP: ip, + Mask: ip.DefaultMask(), + }, nil + } + return nil, errors.New("IPv6 invalid: needs prefix length") +} + +func mergeCIDRs(ranges cidrBlockRanges) cidrBlockRanges { + sort.Sort(ranges) + + // Merge adjacent CIDRs if possible. + for i := len(ranges) - 1; i > 0; i-- { + previousIP := cidrMerge.GetPreviousIP(*ranges[i].First) + + // If the previous IP of the current network overlaps + // with the last IP of the previous network in the + // list, then merge the two ranges together. + if bytes.Compare(previousIP, *ranges[i-1].Last) <= 0 { + var firstIP *net.IP + if bytes.Compare(*ranges[i-1].First, *ranges[i].First) < 0 { + firstIP = ranges[i-1].First + } else { + firstIP = ranges[i].First + } + + lastIPRange := make(net.IP, len(*ranges[i].Last)) + copy(lastIPRange, *ranges[i].Last) + + firstIPRange := make(net.IP, len(*firstIP)) + copy(firstIPRange, *firstIP) + + ranges[i-1] = &cidrBlockRange{First: &firstIPRange, Last: &lastIPRange, Network: nil} + + // Delete ranges[i] since merged with the previous. + ranges = slices.Delete(ranges, i, i+1) + } + } + return ranges +} + +func incIP(ip net.IP) { + for j := len(ip) - 1; j >= 0; j-- { + ip[j]++ + if ip[j] > 0 { + break + } + } +} + +func init() { + RegisterBuiltinFunc(ast.NetCIDROverlap.Name, builtinNetCIDRContains) + RegisterBuiltinFunc(ast.NetCIDRIntersects.Name, builtinNetCIDRIntersects) + RegisterBuiltinFunc(ast.NetCIDRContains.Name, builtinNetCIDRContains) + RegisterBuiltinFunc(ast.NetCIDRContainsMatches.Name, builtinNetCIDRContainsMatches) + RegisterBuiltinFunc(ast.NetCIDRExpand.Name, builtinNetCIDRExpand) + RegisterBuiltinFunc(ast.NetCIDRMerge.Name, builtinNetCIDRMerge) + RegisterBuiltinFunc(ast.NetCIDRIsValid.Name, builtinNetCIDRIsValid) +} diff --git a/third_party/opa/v1/topdown/cidr_test.go b/third_party/opa/v1/topdown/cidr_test.go new file mode 100644 index 000000000000..6e3c70866b7c --- /dev/null +++ b/third_party/opa/v1/topdown/cidr_test.go @@ -0,0 +1,46 @@ +package topdown + +import ( + "context" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" +) + +func TestNetCIDRExpandCancellation(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + compiler := compileModules([]string{ + ` + package test + + p if { net.cidr_expand("1.0.0.0/1") } # generating 2**31 hosts will take a while... + `, + }) + + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store) + cancel := NewCancel() + + query := NewQuery(ast.MustParseBody("data.test.p")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithCancel(cancel) + + go func() { + time.Sleep(time.Millisecond * 50) + cancel.Cancel() + }() + + qrs, err := query.Run(ctx) + + if err == nil || err.(*Error).Code != CancelErr { + t.Fatalf("Expected cancel error but got: %v (err: %v)", qrs, err) + } +} diff --git a/third_party/opa/v1/topdown/comparison.go b/third_party/opa/v1/topdown/comparison.go new file mode 100644 index 000000000000..6c10129faaf8 --- /dev/null +++ b/third_party/opa/v1/topdown/comparison.go @@ -0,0 +1,48 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import "github.com/open-policy-agent/opa/v1/ast" + +type compareFunc func(a, b ast.Value) bool + +func compareGreaterThan(a, b ast.Value) bool { + return ast.Compare(a, b) > 0 +} + +func compareGreaterThanEq(a, b ast.Value) bool { + return ast.Compare(a, b) >= 0 +} + +func compareLessThan(a, b ast.Value) bool { + return ast.Compare(a, b) < 0 +} + +func compareLessThanEq(a, b ast.Value) bool { + return ast.Compare(a, b) <= 0 +} + +func compareNotEq(a, b ast.Value) bool { + return ast.Compare(a, b) != 0 +} + +func compareEq(a, b ast.Value) bool { + return ast.Compare(a, b) == 0 +} + +func builtinCompare(cmp compareFunc) BuiltinFunc { + return func(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return iter(ast.InternedTerm(cmp(operands[0].Value, operands[1].Value))) + } +} + +func init() { + RegisterBuiltinFunc(ast.GreaterThan.Name, builtinCompare(compareGreaterThan)) + RegisterBuiltinFunc(ast.GreaterThanEq.Name, builtinCompare(compareGreaterThanEq)) + RegisterBuiltinFunc(ast.LessThan.Name, builtinCompare(compareLessThan)) + RegisterBuiltinFunc(ast.LessThanEq.Name, builtinCompare(compareLessThanEq)) + RegisterBuiltinFunc(ast.NotEqual.Name, builtinCompare(compareNotEq)) + RegisterBuiltinFunc(ast.Equal.Name, builtinCompare(compareEq)) +} diff --git a/third_party/opa/v1/topdown/copypropagation/copypropagation.go b/third_party/opa/v1/topdown/copypropagation/copypropagation.go new file mode 100644 index 000000000000..7767e7ff5207 --- /dev/null +++ b/third_party/opa/v1/topdown/copypropagation/copypropagation.go @@ -0,0 +1,497 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package copypropagation + +import ( + "fmt" + "sort" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// CopyPropagator implements a simple copy propagation optimization to remove +// intermediate variables in partial evaluation results. +// +// For example, given the query: input.x > 1 where 'input' is unknown, the +// compiled query would become input.x = a; a > 1 which would remain in the +// partial evaluation result. The CopyPropagator will remove the variable +// assignment so that partial evaluation simply outputs input.x > 1. +// +// In many cases, copy propagation can remove all variables from the result of +// partial evaluation which simplifies evaluation for non-OPA consumers. +// +// In some cases, copy propagation cannot remove all variables. If the output of +// a built-in call is subsequently used as a ref head, the output variable must +// be kept. For example. sort(input, x); x[0] == 1. In this case, copy +// propagation cannot replace x[0] == 1 with sort(input, x)[0] == 1 as this is +// not legal. +type CopyPropagator struct { + livevars ast.VarSet // vars that must be preserved in the resulting query + sorted []ast.Var // sorted copy of vars to ensure deterministic result + ensureNonEmptyBody bool + compiler *ast.Compiler + localvargen *localVarGenerator +} + +type localVarGenerator struct { + next int +} + +func (l *localVarGenerator) Generate() ast.Var { + result := ast.Var(fmt.Sprintf("__localcp%d__", l.next)) + l.next++ + return result + +} + +// New returns a new CopyPropagator that optimizes queries while preserving vars +// in the livevars set. +func New(livevars ast.VarSet) *CopyPropagator { + + sorted := make([]ast.Var, 0, len(livevars)) + for v := range livevars { + sorted = append(sorted, v) + } + + sort.Slice(sorted, func(i, j int) bool { + return sorted[i].Compare(sorted[j]) < 0 + }) + + return &CopyPropagator{livevars: livevars, sorted: sorted, localvargen: &localVarGenerator{}} +} + +// WithEnsureNonEmptyBody configures p to ensure that results are always non-empty. +func (p *CopyPropagator) WithEnsureNonEmptyBody(yes bool) *CopyPropagator { + p.ensureNonEmptyBody = yes + return p +} + +// WithCompiler configures the compiler to read from while processing the query. This +// should be the same compiler used to compile the original policy. +func (p *CopyPropagator) WithCompiler(c *ast.Compiler) *CopyPropagator { + p.compiler = c + return p +} + +// Apply executes the copy propagation optimization and returns a new query. +func (p *CopyPropagator) Apply(query ast.Body) ast.Body { + + result := ast.NewBody() + + uf, ok := makeDisjointSets(p.livevars, query) + if !ok { + return query + } + + // Compute set of vars that appear in the head of refs in the query. If a var + // is dereferenced, we can plug it with a constant value, but it is not always + // optimal to do so. + // TODO: Improve the algorithm for when we should plug constants/calls/etc + headvars := ast.NewVarSet() + ast.WalkRefs(query, func(x ast.Ref) bool { + if v, ok := x[0].Value.(ast.Var); ok { + if root, ok := uf.Find(v); ok { + root.constant = nil + headvars.Add(root.key.(ast.Var)) + } else { + headvars.Add(v) + } + } + return false + }) + + removedEqs := ast.NewValueMap() + + for _, expr := range query { + + pctx := &plugContext{ + removedEqs: removedEqs, + uf: uf, + negated: expr.Negated, + headvars: headvars, + } + + expr = p.plugBindings(pctx, expr) + + if p.updateBindings(pctx, expr) { + result.Append(expr) + } + } + + // Run post-processing step on the query to ensure that all live vars are bound + // in the result. The plugging that happens above substitutes all vars in the + // same set with the root. + // + // This step should run before the next step to prevent unnecessary bindings + // from being added to the result. For example: + // + // - Given the following result: + // - Given the following removed equalities: "x = input.x" and "y = input" + // - Given the following liveset: {x} + // + // If this step were to run AFTER the following step, the output would be: + // + // x = input.x; y = input + // + // Even though y = input is not required. + for _, v := range p.sorted { + if root, ok := uf.Find(v); ok { + if root.constant != nil { + result.Append(ast.Equality.Expr(ast.NewTerm(v), root.constant)) + } else if b := removedEqs.Get(root.key); b != nil { + result.Append(ast.Equality.Expr(ast.NewTerm(v), ast.NewTerm(b))) + } else if root.key != v { + result.Append(ast.Equality.Expr(ast.NewTerm(v), ast.NewTerm(root.key))) + } + } + } + + // Run post-processing step on query to ensure that all killed exprs are + // accounted for. There are several cases we look for: + // + // * If an expr is killed but the binding is never used, the query + // must still include the expr. For example, given the query 'input.x = a' and + // an empty livevar set, the result must include the ref input.x otherwise the + // query could be satisfied without input.x being defined. + // + // * If an expr is killed that provided safety to vars which are not + // otherwise being made safe by the current result. + // + // For any of these cases we re-add the removed equality expression + // to the current result. + + // Invariant: Live vars are bound (above) and reserved vars are implicitly ground. + safe := ast.NewVarSetOfSize(len(p.livevars) + len(ast.ReservedVars) + 6) + safe.Update(ast.ReservedVars) + safe.Update(p.livevars) + safe.Update(ast.OutputVarsFromBody(p.compiler, result, safe)) + unsafe := result.Vars(ast.SafetyCheckVisitorParams).Diff(safe) + + for _, b := range sortbindings(removedEqs) { + removedEq := ast.Equality.Expr(ast.NewTerm(b.k), ast.NewTerm(b.v)) + + providesSafety := false + outputVars := ast.OutputVarsFromExpr(p.compiler, removedEq, safe) + if unsafe.DiffCount(outputVars) < len(unsafe) { + unsafe = unsafe.Diff(outputVars) + providesSafety = true + } + + safevarRef := false // don't add something like `_ = input` + if r, ok := b.v.(ast.Ref); ok { + if len(r) == 1 { + if v, ok := r[0].Value.(ast.Var); ok { + safevarRef = safe.Contains(v) + } + } + } + + if providesSafety || (!safevarRef && !containedIn(b.v, result)) { + result.Append(removedEq) + safe.Update(outputVars) + } + } + + if len(unsafe) > 0 { + // NOTE(tsandall): This should be impossible but if it does occur, throw + // away the result rather than generating unsafe output. + return query + } + + if p.ensureNonEmptyBody && len(result) == 0 { + result = append(result, ast.NewExpr(ast.BooleanTerm(true))) + } + + return result +} + +// plugBindings applies the binding list and union-find to x. This process +// removes as many variables as possible. +func (*CopyPropagator) plugBindings(pctx *plugContext, expr *ast.Expr) *ast.Expr { + + xform := bindingPlugTransform{ + pctx: pctx, + } + + // Deep copy the expression as it may be mutated during the transform and + // the caller running copy propagation may have references to the + // expression. Note, the transform does not contain any error paths and + // should never return a non-expression value for the root so consider + // errors unreachable. + x, err := ast.Transform(xform, expr.Copy()) + + expr, ok := x.(*ast.Expr) + if !ok || err != nil { + panic("unreachable") + } + return expr +} + +type bindingPlugTransform struct { + pctx *plugContext +} + +func (t bindingPlugTransform) Transform(x any) (any, error) { + switch x := x.(type) { + case ast.Var: + return t.plugBindingsVar(t.pctx, x), nil + case ast.Ref: + return t.plugBindingsRef(t.pctx, x), nil + default: + return x, nil + } +} + +func (bindingPlugTransform) plugBindingsVar(pctx *plugContext, v ast.Var) ast.Value { + + var result ast.Value = v + + // Apply union-find to remove redundant variables from input. + root, ok := pctx.uf.Find(v) + if ok { + result = root.Value() + } + + // Apply binding list to substitute remaining vars. + v, ok = result.(ast.Var) + if !ok { + return result + } + b := pctx.removedEqs.Get(v) + if b == nil { + return result + } + if pctx.negated && !b.IsGround() { + return result + } + + if r, ok := b.(ast.Ref); ok && r.OutputVars().Contains(v) { + return result + } + + return b +} + +func (bindingPlugTransform) plugBindingsRef(pctx *plugContext, v ast.Ref) ast.Ref { + + // Apply union-find to remove redundant variables from input. + if root, ok := pctx.uf.Find(v[0].Value); ok { + v[0].Value = root.Value() + } + + result := v + + // Refs require special handling. If the head of the ref was killed, then + // the rest of the ref must be concatenated with the new base. + if b := pctx.removedEqs.Get(v[0].Value); b != nil { + if !pctx.negated || b.IsGround() { + var base ast.Ref + switch x := b.(type) { + case ast.Ref: + base = x + default: + base = ast.Ref{ast.NewTerm(x)} + } + result = base.Concat(v[1:]) + } + } + + return result +} + +// updateBindings returns false if the expression can be killed. If the +// expression is killed, the binding list is updated to map a var to value. +func (p *CopyPropagator) updateBindings(pctx *plugContext, expr *ast.Expr) bool { + switch { + case pctx.negated || len(expr.With) > 0: + return true + + case expr.IsEquality(): + a, b := expr.Operand(0), expr.Operand(1) + if a.Equal(b) { + if p.livevarRef(a) { + pctx.removedEqs.Put(p.localvargen.Generate(), a.Value) + } + return false + } + k, v, keep := p.updateBindingsEq(a, b) + if !keep { + if v != nil { + pctx.removedEqs.Put(k, v) + } + return false + } + + case expr.IsCall(): + terms := expr.Terms.([]*ast.Term) + if p.compiler.GetArity(expr.Operator()) == len(terms)-2 { // with captured output + output := terms[len(terms)-1] + if k, ok := output.Value.(ast.Var); ok && !p.livevars.Contains(k) && !pctx.headvars.Contains(k) { + pctx.removedEqs.Put(k, ast.CallTerm(terms[:len(terms)-1]...).Value) + return false + } + } + } + return !isNoop(expr) +} + +func (p *CopyPropagator) livevarRef(a *ast.Term) bool { + ref, ok := a.Value.(ast.Ref) + if !ok { + return false + } + + for _, v := range p.sorted { + if ref[0].Value.Compare(v) == 0 { + return true + } + } + + return false +} + +func (p *CopyPropagator) updateBindingsEq(a, b *ast.Term) (ast.Var, ast.Value, bool) { + k, v, keep := p.updateBindingsEqAsymmetric(a, b) + if !keep { + return k, v, keep + } + return p.updateBindingsEqAsymmetric(b, a) +} + +func (p *CopyPropagator) updateBindingsEqAsymmetric(a, b *ast.Term) (ast.Var, ast.Value, bool) { + k, ok := a.Value.(ast.Var) + if !ok || p.livevars.Contains(k) { + return "", nil, true + } + + switch b.Value.(type) { + case ast.Ref, ast.Call: + return k, b.Value, false + } + + return "", nil, true +} + +type plugContext struct { + removedEqs *ast.ValueMap + uf *unionFind + headvars ast.VarSet + negated bool +} + +type binding struct { + k, v ast.Value +} + +func containedIn(value ast.Value, x any) bool { + var stop bool + + var vis *ast.GenericVisitor + vis = ast.NewGenericVisitor(func(x any) bool { + switch x := x.(type) { + case *ast.Every: // skip body + vis.Walk(x.Key) + vis.Walk(x.Value) + vis.Walk(x.Domain) + return true + case *ast.ArrayComprehension, *ast.ObjectComprehension, *ast.SetComprehension: // skip + return true + case ast.Ref: + var match bool + if v, ok := value.(ast.Ref); ok { + match = x.HasPrefix(v) + } else { + match = x.Compare(value) == 0 + } + if stop || match { + stop = true + return stop + } + case ast.Value: + if stop || x.Compare(value) == 0 { + stop = true + return stop + } + } + return stop + }) + vis.Walk(x) + return stop +} + +func sortbindings(bindings *ast.ValueMap) []*binding { + sorted := make([]*binding, 0, bindings.Len()) + bindings.Iter(func(k ast.Value, v ast.Value) bool { + sorted = append(sorted, &binding{k, v}) + return false + }) + sort.Slice(sorted, func(i, j int) bool { + return sorted[i].k.Compare(sorted[j].k) > 0 + }) + return sorted +} + +// makeDisjointSets builds the union-find structure for the query. The structure +// is built by processing all of the equality exprs in the query. Sets represent +// vars that must be equal to each other. In addition to vars, each set can have +// at most one constant. If the query contains expressions that cannot be +// satisfied (e.g., because a set has multiple constants) this function returns +// false. +func makeDisjointSets(livevars ast.VarSet, query ast.Body) (*unionFind, bool) { + uf := newUnionFind(func(r1, r2 *unionFindRoot) (*unionFindRoot, *unionFindRoot) { + if v, ok := r1.key.(ast.Var); ok && livevars.Contains(v) { + return r1, r2 + } + return r2, r1 + }) + for _, expr := range query { + if expr.IsEquality() && !expr.Negated && len(expr.With) == 0 { + a, b := expr.Operand(0), expr.Operand(1) + varA, ok1 := a.Value.(ast.Var) + varB, ok2 := b.Value.(ast.Var) + + switch { + case ok1 && ok2: + if _, ok := uf.Merge(varA, varB); !ok { + return nil, false + } + + case ok1 && ast.IsConstant(b.Value): + root := uf.MakeSet(varA) + if root.constant != nil && !root.constant.Equal(b) { + return nil, false + } + root.constant = b + + case ok2 && ast.IsConstant(a.Value): + root := uf.MakeSet(varB) + if root.constant != nil && !root.constant.Equal(a) { + return nil, false + } + root.constant = a + } + } + } + + return uf, true +} + +func isNoop(expr *ast.Expr) bool { + + if !expr.IsCall() && !expr.IsEvery() { + term := expr.Terms.(*ast.Term) + if !ast.IsConstant(term.Value) { + return false + } + return !ast.Boolean(false).Equal(term.Value) + } + + // A==A can be ignored + if expr.Operator().Equal(ast.Equal.Ref()) { + return expr.Operand(0).Equal(expr.Operand(1)) + } + + return false +} diff --git a/third_party/opa/v1/topdown/copypropagation/unionfind.go b/third_party/opa/v1/topdown/copypropagation/unionfind.go new file mode 100644 index 000000000000..cac2a3009fda --- /dev/null +++ b/third_party/opa/v1/topdown/copypropagation/unionfind.go @@ -0,0 +1,131 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package copypropagation + +import ( + "fmt" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" +) + +type rankFunc func(*unionFindRoot, *unionFindRoot) (*unionFindRoot, *unionFindRoot) + +type unionFind struct { + roots *util.HasherMap[ast.Value, *unionFindRoot] + parents *ast.ValueMap + rank rankFunc +} + +func newUnionFind(rank rankFunc) *unionFind { + return &unionFind{ + roots: util.NewHasherMap[ast.Value, *unionFindRoot](ast.ValueEqual), + parents: ast.NewValueMap(), + rank: rank, + } +} + +func (uf *unionFind) MakeSet(v ast.Value) *unionFindRoot { + + root, ok := uf.Find(v) + if ok { + return root + } + + root = newUnionFindRoot(v) + uf.parents.Put(v, v) + uf.roots.Put(v, root) + return root +} + +func (uf *unionFind) Find(v ast.Value) (*unionFindRoot, bool) { + + parent := uf.parents.Get(v) + if parent == nil { + return nil, false + } + + if parent.Compare(v) == 0 { + r, ok := uf.roots.Get(v) + return r, ok + } + + return uf.Find(parent) +} + +func (uf *unionFind) Merge(a, b ast.Value) (*unionFindRoot, bool) { + + r1 := uf.MakeSet(a) + r2 := uf.MakeSet(b) + + if r1 != r2 { + + r1, r2 = uf.rank(r1, r2) + + uf.parents.Put(r2.key, r1.key) + uf.roots.Delete(r2.key) + + // Sets can have at most one constant value associated with them. When + // unioning, we must preserve this invariant. If a set has two constants, + // there will be no way to prove the query. + if r1.constant != nil && r2.constant != nil && !r1.constant.Equal(r2.constant) { + return nil, false + } else if r1.constant == nil { + r1.constant = r2.constant + } + } + + return r1, true +} + +func (uf *unionFind) String() string { + o := struct { + Roots map[string]any + Parents map[string]ast.Value + }{ + map[string]any{}, + map[string]ast.Value{}, + } + + uf.roots.Iter(func(k ast.Value, v *unionFindRoot) bool { + o.Roots[k.String()] = struct { + Constant *ast.Term + Key ast.Value + }{ + v.constant, + v.key, + } + return true + }) + + uf.parents.Iter(func(k ast.Value, v ast.Value) bool { + o.Parents[k.String()] = v + return true + }) + + return string(util.MustMarshalJSON(o)) +} + +type unionFindRoot struct { + key ast.Value + constant *ast.Term +} + +func newUnionFindRoot(key ast.Value) *unionFindRoot { + return &unionFindRoot{ + key: key, + } +} + +func (r *unionFindRoot) Value() ast.Value { + if r.constant != nil { + return r.constant.Value + } + return r.key +} + +func (r *unionFindRoot) String() string { + return fmt.Sprintf("{key: %s, constant: %s", r.key, r.constant) +} diff --git a/third_party/opa/v1/topdown/copypropagation/unionfind_test.go b/third_party/opa/v1/topdown/copypropagation/unionfind_test.go new file mode 100644 index 000000000000..77fb8bdd58eb --- /dev/null +++ b/third_party/opa/v1/topdown/copypropagation/unionfind_test.go @@ -0,0 +1,220 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package copypropagation + +import ( + "reflect" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestUnionFindRootValue(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + root unionFindRoot + expected ast.Value + }{ + { + name: "var only", + root: unionFindRoot{key: ast.Var("foo")}, + expected: ast.Var("foo"), + }, + { + name: "const only", + root: unionFindRoot{constant: ast.StringTerm("foo")}, + expected: ast.String("foo"), + }, + { + name: "const and var", + root: unionFindRoot{key: ast.Var("foo"), constant: ast.StringTerm("bar")}, + expected: ast.String("bar"), + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + r := &unionFindRoot{ + key: tc.root.key, + constant: tc.root.constant, + } + if got := r.Value(); tc.expected.Compare(got) != 0 { + t.Errorf("Value() = %v, expected %v", got, tc.expected) + } + }) + } +} + +func TestUnionFindMakeSet(t *testing.T) { + t.Parallel() + + uf := newUnionFind(nil) + + tests := []struct { + name string + v ast.Value + result *unionFindRoot + parents map[ast.Value]ast.Value + roots map[ast.Value]*unionFindRoot + }{ + { + name: "from empty", + v: ast.Var("a"), + result: &unionFindRoot{key: ast.Var("a")}, + }, + { + name: "add another var", + v: ast.Var("b"), + result: &unionFindRoot{key: ast.Var("b")}, + }, + { + name: "add existing", + v: ast.Var("b"), + result: &unionFindRoot{key: ast.Var("b")}, + }, + { + name: "add ref", + v: ast.Ref{ast.StringTerm("foo")}, + result: &unionFindRoot{key: ast.Ref{ast.StringTerm("foo")}}, + }, + { + name: "add ref existing", + v: ast.Ref{ast.StringTerm("foo")}, + result: &unionFindRoot{key: ast.Ref{ast.StringTerm("foo")}}, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + actual := uf.MakeSet(tc.v) + if !reflect.DeepEqual(actual, tc.result) { + t.Errorf("MakeSet(%v) = %v, expected %v", tc.v, actual, tc.result) + } + }) + } +} + +func TestUnionFindFindEmptyUF(t *testing.T) { + t.Parallel() + + uf := newUnionFind(noopUnionFindRank) + actual, found := uf.Find(ast.Var("a")) + if found || actual != nil { + t.Error("Expected Find() to return (nil, false)") + } +} + +func TestUnionFindFindIsParent(t *testing.T) { + t.Parallel() + + uf := newUnionFind(noopUnionFindRank) + + uf.MakeSet(ast.Var("a")) // "a" will have a parent "a" + + actual, found := uf.Find(ast.Var("a")) + + expected := newUnionFindRoot(ast.Var("a")) + if !found || actual.Value().Compare(expected.Value()) != 0 { + t.Errorf("Expected Find() to return (true, %+v)", expected) + } +} + +func TestUnionFindFindParent(t *testing.T) { + t.Parallel() + + fooBarRef := ast.Ref{ast.StringTerm("foo"), ast.StringTerm("bar"), ast.VarTerm("x")} + call := ast.Call{ast.RefTerm(ast.VarTerm("gt")), ast.NumberTerm("1"), ast.VarTerm("x")} + + uf := newUnionFind(noopUnionFindRank) + uf.Merge(ast.Var("a"), ast.Var("b")) + uf.Merge(ast.Var("b"), ast.Var("c")) + uf.Merge(ast.Var("c"), fooBarRef) + uf.Merge(fooBarRef, ast.Var("d")) + uf.Merge(ast.Var("d"), call) + uf.Merge(call, ast.Var("e")) + + actual, found := uf.Find(ast.Var("e")) + + expected := newUnionFindRoot(ast.Var("a")) + if !found || actual.Value().Compare(expected.Value()) != 0 { + t.Errorf("Expected Find() to return (true, %+v)", expected) + } +} + +func TestUnionFindMerge(t *testing.T) { + t.Parallel() + + uf := newUnionFind(noopUnionFindRank) + + tests := []struct { + name string + a ast.Value + b ast.Value + result *unionFindRoot + parents map[ast.Value]ast.Value + roots map[ast.Value]*unionFindRoot + }{ + { + name: "empty uf", + a: ast.Var("a"), + b: ast.Var("b"), + result: newUnionFindRoot(ast.Var("a")), + }, + { + name: "same values", + a: ast.Var("a"), + b: ast.Var("a"), + result: newUnionFindRoot(ast.Var("a")), + }, + { + name: "same values higher rank result", + a: ast.Var("b"), + b: ast.Var("b"), + result: newUnionFindRoot(ast.Var("a")), + }, + { + name: "transitive", + a: ast.Var("b"), + b: ast.Var("c"), + result: newUnionFindRoot(ast.Var("a")), + }, + { + name: "new roots", + a: ast.Var("d"), + b: ast.Var("e"), + result: newUnionFindRoot(ast.Var("d")), + }, + { + name: "combine roots", + a: ast.Var("a"), + b: ast.Var("e"), + result: newUnionFindRoot(ast.Var("a")), + }, + { + name: "new ref roots", + a: ast.Ref{ast.StringTerm("foo"), ast.StringTerm("bar")}, + b: ast.Var("x"), + result: newUnionFindRoot(ast.Ref{ast.StringTerm("foo"), ast.StringTerm("bar")}), + }, + { + name: "combine ref roots", + a: ast.Var("b"), + b: ast.Ref{ast.StringTerm("foo"), ast.StringTerm("bar")}, + result: newUnionFindRoot(ast.Var("a")), + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + actualRoot, canMerge := uf.Merge(tc.a, tc.b) + if !reflect.DeepEqual(actualRoot, tc.result) || !canMerge { + t.Errorf("Merge(%v, %v) got = (%v, %v), expected (%v, true)", tc.a, tc.b, actualRoot, canMerge, tc.result) + } + }) + } +} + +var noopUnionFindRank = func(a *unionFindRoot, b *unionFindRoot) (*unionFindRoot, *unionFindRoot) { + return a, b +} diff --git a/third_party/opa/v1/topdown/crypto.go b/third_party/opa/v1/topdown/crypto.go new file mode 100644 index 000000000000..2710d8a04a89 --- /dev/null +++ b/third_party/opa/v1/topdown/crypto.go @@ -0,0 +1,783 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "bytes" + "crypto" + "crypto/hmac" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "crypto/tls" + "crypto/x509" + "encoding/base64" + "encoding/hex" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "hash" + "os" + "strings" + "time" + + "github.com/open-policy-agent/opa/internal/jwx/jwk" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + // blockTypeCertificate indicates this PEM block contains the signed certificate. + // Exported for tests. + blockTypeCertificate = "CERTIFICATE" + // blockTypeCertificateRequest indicates this PEM block contains a certificate + // request. Exported for tests. + blockTypeCertificateRequest = "CERTIFICATE REQUEST" + // blockTypeRSAPrivateKey indicates this PEM block contains a RSA private key. + // Exported for tests. + blockTypeRSAPrivateKey = "RSA PRIVATE KEY" + // blockTypeRSAPrivateKey indicates this PEM block contains a RSA private key. + // Exported for tests. + blockTypePrivateKey = "PRIVATE KEY" + blockTypeEcPrivateKey = "EC PRIVATE KEY" +) + +func builtinCryptoX509ParseCertificates(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + input, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + certs, err := getX509CertsFromString(string(input)) + if err != nil { + return err + } + + v, err := ast.InterfaceToValue(extendCertificates(certs)) + if err != nil { + return err + } + + return iter(ast.NewTerm(v)) +} + +// extendedCert is a wrapper around x509.Certificate that adds additional fields for JSON serialization. +type extendedCert struct { + x509.Certificate + URIStrings []string +} + +func extendCertificates(certs []*x509.Certificate) []extendedCert { + // add a field to certs containing the URIs as strings + processedCerts := make([]extendedCert, len(certs)) + + for i, cert := range certs { + processedCerts[i].Certificate = *cert + if cert.URIs != nil { + processedCerts[i].URIStrings = make([]string, len(cert.URIs)) + for j, uri := range cert.URIs { + processedCerts[i].URIStrings[j] = uri.String() + } + } + } + return processedCerts +} + +func builtinCryptoX509ParseAndVerifyCertificates(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + a := operands[0].Value + input, err := builtins.StringOperand(a, 1) + if err != nil { + return err + } + + certs, err := getX509CertsFromString(string(input)) + if err != nil { + return iter(ast.ArrayTerm(ast.InternedTerm(false), ast.InternedEmptyArray)) + } + + verified, err := verifyX509CertificateChain(certs, x509.VerifyOptions{}) + if err != nil { + return iter(ast.ArrayTerm(ast.InternedTerm(false), ast.InternedEmptyArray)) + } + + value, err := ast.InterfaceToValue(extendCertificates(verified)) + if err != nil { + return err + } + + valid := ast.ArrayTerm(ast.InternedTerm(true), ast.NewTerm(value)) + + return iter(valid) +} + +var allowedKeyUsages = map[string]x509.ExtKeyUsage{ + "KeyUsageAny": x509.ExtKeyUsageAny, + "KeyUsageServerAuth": x509.ExtKeyUsageServerAuth, + "KeyUsageClientAuth": x509.ExtKeyUsageClientAuth, + "KeyUsageCodeSigning": x509.ExtKeyUsageCodeSigning, + "KeyUsageEmailProtection": x509.ExtKeyUsageEmailProtection, + "KeyUsageIPSECEndSystem": x509.ExtKeyUsageIPSECEndSystem, + "KeyUsageIPSECTunnel": x509.ExtKeyUsageIPSECTunnel, + "KeyUsageIPSECUser": x509.ExtKeyUsageIPSECUser, + "KeyUsageTimeStamping": x509.ExtKeyUsageTimeStamping, + "KeyUsageOCSPSigning": x509.ExtKeyUsageOCSPSigning, + "KeyUsageMicrosoftServerGatedCrypto": x509.ExtKeyUsageMicrosoftServerGatedCrypto, + "KeyUsageNetscapeServerGatedCrypto": x509.ExtKeyUsageNetscapeServerGatedCrypto, + "KeyUsageMicrosoftCommercialCodeSigning": x509.ExtKeyUsageMicrosoftCommercialCodeSigning, + "KeyUsageMicrosoftKernelCodeSigning": x509.ExtKeyUsageMicrosoftKernelCodeSigning, +} + +func builtinCryptoX509ParseAndVerifyCertificatesWithOptions(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + input, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + options, err := builtins.ObjectOperand(operands[1].Value, 2) + if err != nil { + return err + } + + certs, err := getX509CertsFromString(string(input)) + if err != nil { + return iter(ast.ArrayTerm(ast.InternedTerm(false), ast.InternedEmptyArray)) + } + + // Collect the cert verification options + verifyOpt, err := extractVerifyOpts(options) + if err != nil { + return err + } + + verified, err := verifyX509CertificateChain(certs, verifyOpt) + if err != nil { + return iter(ast.ArrayTerm(ast.InternedTerm(false), ast.InternedEmptyArray)) + } + + value, err := ast.InterfaceToValue(verified) + if err != nil { + return err + } + + return iter(ast.ArrayTerm(ast.InternedTerm(true), ast.NewTerm(value))) +} + +func extractVerifyOpts(options ast.Object) (verifyOpt x509.VerifyOptions, err error) { + + for _, key := range options.Keys() { + k, err := ast.JSON(key.Value) + if err != nil { + return verifyOpt, err + } + k, ok := k.(string) + if !ok { + continue + } + + switch k { + case "DNSName": + dns, ok := options.Get(key).Value.(ast.String) + if ok { + verifyOpt.DNSName = strings.Trim(string(dns), "\"") + } else { + return verifyOpt, errors.New("'DNSName' should be a string") + } + case "CurrentTime": + c, ok := options.Get(key).Value.(ast.Number) + if ok { + nanosecs, ok := c.Int64() + if ok { + verifyOpt.CurrentTime = time.Unix(0, nanosecs) + } else { + return verifyOpt, errors.New("'CurrentTime' should be a valid int64 number") + } + } else { + return verifyOpt, errors.New("'CurrentTime' should be a number") + } + case "MaxConstraintComparisons": + c, ok := options.Get(key).Value.(ast.Number) + if ok { + maxComparisons, ok := c.Int() + if ok { + verifyOpt.MaxConstraintComparisions = maxComparisons + } else { + return verifyOpt, errors.New("'MaxConstraintComparisons' should be a valid number") + } + } else { + return verifyOpt, errors.New("'MaxConstraintComparisons' should be a number") + } + case "KeyUsages": + type forEach interface { + Foreach(func(*ast.Term)) + } + var ks forEach + switch v := options.Get(key).Value.(type) { + case *ast.Array: + ks = v + case ast.Set: + ks = v + default: + return verifyOpt, errors.New("'KeyUsages' should be an Array or Set") + } + + // Collect the x509.ExtKeyUsage values by looking up the + // mapping of key usage strings to x509.ExtKeyUsage + var invalidKUsgs []string + ks.Foreach(func(t *ast.Term) { + u, ok := t.Value.(ast.String) + if ok { + v := strings.Trim(string(u), "\"") + if k, ok := allowedKeyUsages[v]; ok { + verifyOpt.KeyUsages = append(verifyOpt.KeyUsages, k) + } else { + invalidKUsgs = append(invalidKUsgs, v) + } + } + }) + if len(invalidKUsgs) > 0 { + return x509.VerifyOptions{}, fmt.Errorf("invalid entries for 'KeyUsages' found: %s", invalidKUsgs) + } + default: + return verifyOpt, errors.New("invalid key option") + } + + } + + return verifyOpt, nil +} + +func builtinCryptoX509ParseKeyPair(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + certificate, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + key, err := builtins.StringOperand(operands[1].Value, 1) + if err != nil { + return err + } + + certs, err := getTLSx509KeyPairFromString([]byte(certificate), []byte(key)) + if err != nil { + return err + } + v, err := ast.InterfaceToValue(certs) + if err != nil { + return err + } + + return iter(ast.NewTerm(v)) +} + +func builtinCryptoX509ParseCertificateRequest(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + input, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // data to be passed to x509.ParseCertificateRequest + bytes := []byte(input) + + // if the input is not a PEM string, attempt to decode b64 + if str := string(input); !strings.HasPrefix(str, "-----BEGIN CERTIFICATE REQUEST-----") { + bytes, err = base64.StdEncoding.DecodeString(str) + if err != nil { + return err + } + } + + p, _ := pem.Decode(bytes) + if p != nil && p.Type != blockTypeCertificateRequest { + return errors.New("invalid PEM-encoded certificate signing request") + } + if p != nil { + bytes = p.Bytes + } + + csr, err := x509.ParseCertificateRequest(bytes) + if err != nil { + return err + } + + bs, err := json.Marshal(csr) + if err != nil { + return err + } + + var x any + if err := util.UnmarshalJSON(bs, &x); err != nil { + return err + } + + v, err := ast.InterfaceToValue(x) + if err != nil { + return err + } + + return iter(ast.NewTerm(v)) +} + +func builtinCryptoJWKFromPrivateKey(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + var x any + + a := operands[0].Value + input, err := builtins.StringOperand(a, 1) + if err != nil { + return err + } + + // get the raw private key + pemDataString := string(input) + + if pemDataString == "" { + return errors.New("input PEM data was empty") + } + + // This built in must be supplied a valid PEM or base64 encoded string. + // If the input is not a PEM string, attempt to decode b64. + // If the base64 decode fails - this is an error + if !strings.HasPrefix(pemDataString, "-----BEGIN") { + bs, err := base64.StdEncoding.DecodeString(pemDataString) + if err != nil { + return err + } + pemDataString = string(bs) + } + + rawKeys, err := getPrivateKeysFromPEMData(pemDataString) + if err != nil { + return err + } + + if len(rawKeys) == 0 { + return iter(ast.InternedNullTerm) + } + + key, err := jwk.New(rawKeys[0]) + if err != nil { + return err + } + + jsonKey, err := json.Marshal(key) + if err != nil { + return err + } + + if err := util.UnmarshalJSON(jsonKey, &x); err != nil { + return err + } + + value, err := ast.InterfaceToValue(x) + if err != nil { + return err + } + + return iter(ast.NewTerm(value)) +} + +func builtinCryptoParsePrivateKeys(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + a := operands[0].Value + input, err := builtins.StringOperand(a, 1) + if err != nil { + return err + } + + if string(input) == "" { + return iter(ast.InternedNullTerm) + } + + // get the raw private key + rawKeys, err := getPrivateKeysFromPEMData(string(input)) + if err != nil { + return err + } + + if len(rawKeys) == 0 { + return iter(ast.InternedEmptyArray) + } + + bs, err := json.Marshal(rawKeys) + if err != nil { + return err + } + + var x any + if err := util.UnmarshalJSON(bs, &x); err != nil { + return err + } + + value, err := ast.InterfaceToValue(x) + if err != nil { + return err + } + + return iter(ast.NewTerm(value)) +} + +func toHexEncodedString(src []byte) string { + dst := make([]byte, hex.EncodedLen(len(src))) + hex.Encode(dst, src) + return util.ByteSliceToString(dst) +} + +func builtinCryptoMd5(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + md5sum := md5.Sum([]byte(s)) + + return iter(ast.StringTerm(toHexEncodedString(md5sum[:]))) +} + +func builtinCryptoSha1(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + sha1sum := sha1.Sum([]byte(s)) + + return iter(ast.StringTerm(toHexEncodedString(sha1sum[:]))) +} + +func builtinCryptoSha256(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + sha256sum := sha256.Sum256([]byte(s)) + + return iter(ast.StringTerm(toHexEncodedString(sha256sum[:]))) +} + +func hmacHelper(operands []*ast.Term, iter func(*ast.Term) error, h func() hash.Hash) error { + a1 := operands[0].Value + message, err := builtins.StringOperand(a1, 1) + if err != nil { + return err + } + + a2 := operands[1].Value + key, err := builtins.StringOperand(a2, 2) + if err != nil { + return err + } + + mac := hmac.New(h, []byte(key)) + mac.Write([]byte(message)) + messageDigest := mac.Sum(nil) + + return iter(ast.StringTerm(hex.EncodeToString(messageDigest))) +} + +func builtinCryptoHmacMd5(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return hmacHelper(operands, iter, md5.New) +} + +func builtinCryptoHmacSha1(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return hmacHelper(operands, iter, sha1.New) +} + +func builtinCryptoHmacSha256(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return hmacHelper(operands, iter, sha256.New) +} + +func builtinCryptoHmacSha512(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return hmacHelper(operands, iter, sha512.New) +} + +func builtinCryptoHmacEqual(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + a1 := operands[0].Value + mac1, err := builtins.StringOperand(a1, 1) + if err != nil { + return err + } + + a2 := operands[1].Value + mac2, err := builtins.StringOperand(a2, 2) + if err != nil { + return err + } + + res := hmac.Equal([]byte(mac1), []byte(mac2)) + + return iter(ast.InternedTerm(res)) +} + +func init() { + RegisterBuiltinFunc(ast.CryptoX509ParseCertificates.Name, builtinCryptoX509ParseCertificates) + RegisterBuiltinFunc(ast.CryptoX509ParseAndVerifyCertificates.Name, builtinCryptoX509ParseAndVerifyCertificates) + RegisterBuiltinFunc(ast.CryptoX509ParseAndVerifyCertificatesWithOptions.Name, builtinCryptoX509ParseAndVerifyCertificatesWithOptions) + RegisterBuiltinFunc(ast.CryptoMd5.Name, builtinCryptoMd5) + RegisterBuiltinFunc(ast.CryptoSha1.Name, builtinCryptoSha1) + RegisterBuiltinFunc(ast.CryptoSha256.Name, builtinCryptoSha256) + RegisterBuiltinFunc(ast.CryptoX509ParseCertificateRequest.Name, builtinCryptoX509ParseCertificateRequest) + RegisterBuiltinFunc(ast.CryptoX509ParseRSAPrivateKey.Name, builtinCryptoJWKFromPrivateKey) + RegisterBuiltinFunc(ast.CryptoParsePrivateKeys.Name, builtinCryptoParsePrivateKeys) + RegisterBuiltinFunc(ast.CryptoX509ParseKeyPair.Name, builtinCryptoX509ParseKeyPair) + RegisterBuiltinFunc(ast.CryptoHmacMd5.Name, builtinCryptoHmacMd5) + RegisterBuiltinFunc(ast.CryptoHmacSha1.Name, builtinCryptoHmacSha1) + RegisterBuiltinFunc(ast.CryptoHmacSha256.Name, builtinCryptoHmacSha256) + RegisterBuiltinFunc(ast.CryptoHmacSha512.Name, builtinCryptoHmacSha512) + RegisterBuiltinFunc(ast.CryptoHmacEqual.Name, builtinCryptoHmacEqual) +} + +func verifyX509CertificateChain(certs []*x509.Certificate, vo x509.VerifyOptions) ([]*x509.Certificate, error) { + if len(certs) < 2 { + return nil, builtins.NewOperandErr(1, "must supply at least two certificates to be able to verify") + } + + // first cert is the root + roots := x509.NewCertPool() + roots.AddCert(certs[0]) + + // all other certs except the last are intermediates + intermediates := x509.NewCertPool() + for i := 1; i < len(certs)-1; i++ { + intermediates.AddCert(certs[i]) + } + + // last cert is the leaf + leaf := certs[len(certs)-1] + + // verify the cert chain back to the root + verifyOpts := x509.VerifyOptions{ + Roots: roots, + Intermediates: intermediates, + DNSName: vo.DNSName, + CurrentTime: vo.CurrentTime, + KeyUsages: vo.KeyUsages, + MaxConstraintComparisions: vo.MaxConstraintComparisions, + } + chains, err := leaf.Verify(verifyOpts) + if err != nil { + return nil, err + } + + return chains[0], nil +} + +func getX509CertsFromString(certs string) ([]*x509.Certificate, error) { + // if the input is PEM handle that + if strings.HasPrefix(certs, "-----BEGIN") { + return getX509CertsFromPem([]byte(certs)) + } + + // assume input is base64 if not PEM + b64, err := base64.StdEncoding.DecodeString(certs) + if err != nil { + return nil, err + } + + // handle if the decoded base64 contains PEM rather than the expected DER + if bytes.HasPrefix(b64, []byte("-----BEGIN")) { + return getX509CertsFromPem(b64) + } + + // otherwise assume the contents are DER + return x509.ParseCertificates(b64) +} + +func getX509CertsFromPem(pemBlocks []byte) ([]*x509.Certificate, error) { + var decodedCerts []byte + for len(pemBlocks) > 0 { + p, r := pem.Decode(pemBlocks) + if p != nil && p.Type != blockTypeCertificate { + return nil, fmt.Errorf("PEM block type is '%s', expected %s", p.Type, blockTypeCertificate) + } + + if p == nil { + break + } + + pemBlocks = r + decodedCerts = append(decodedCerts, p.Bytes...) + } + + return x509.ParseCertificates(decodedCerts) +} + +func getPrivateKeysFromPEMData(pemData string) ([]crypto.PrivateKey, error) { + pemBlockString := pemData + + var validPrivateKeys []crypto.PrivateKey + + // if the input is base64, decode it + bs, err := base64.StdEncoding.DecodeString(pemBlockString) + if err == nil { + pemBlockString = string(bs) + } + bs = []byte(pemBlockString) + + for len(bs) > 0 { + inputLen := len(bs) + var block *pem.Block + block, bs = pem.Decode(bs) + if block == nil && len(bs) == 0 { + break + } + // should only happen if end of input is not a valid PEM block. See TestParseRSAPrivateKeyVariedPemInput. + if inputLen == len(bs) { + break + } + + if block == nil { + continue + } + + switch block.Type { + case blockTypeRSAPrivateKey: + parsedKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, err + } + validPrivateKeys = append(validPrivateKeys, parsedKey) + case blockTypePrivateKey: + parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, err + } + validPrivateKeys = append(validPrivateKeys, parsedKey) + case blockTypeEcPrivateKey: + parsedKey, err := x509.ParseECPrivateKey(block.Bytes) + if err != nil { + return nil, err + } + validPrivateKeys = append(validPrivateKeys, parsedKey) + } + } + return validPrivateKeys, nil +} + +// addCACertsFromFile adds CA certificates from filePath into the given pool. +// If pool is nil, it creates a new x509.CertPool. pool is returned. +func addCACertsFromFile(pool *x509.CertPool, filePath string) (*x509.CertPool, error) { + if pool == nil { + pool = x509.NewCertPool() + } + + caCert, err := readCertFromFile(filePath) + if err != nil { + return nil, err + } + + if ok := pool.AppendCertsFromPEM(caCert); !ok { + return nil, fmt.Errorf("could not append CA certificates from %q", filePath) + } + + return pool, nil +} + +// addCACertsFromBytes adds CA certificates from pemBytes into the given pool. +// If pool is nil, it creates a new x509.CertPool. pool is returned. +func addCACertsFromBytes(pool *x509.CertPool, pemBytes []byte) (*x509.CertPool, error) { + if pool == nil { + pool = x509.NewCertPool() + } + + if ok := pool.AppendCertsFromPEM(pemBytes); !ok { + return nil, errors.New("could not append certificates") + } + + return pool, nil +} + +// addCACertsFromEnv adds CA certificates from the environment variable named +// by envName into the given pool. If pool is nil, it creates a new x509.CertPool. +// pool is returned. +func addCACertsFromEnv(pool *x509.CertPool, envName string) (*x509.CertPool, error) { + pool, err := addCACertsFromBytes(pool, []byte(os.Getenv(envName))) + if err != nil { + return nil, fmt.Errorf("could not add CA certificates from envvar %q: %w", envName, err) + } + + return pool, err +} + +// ReadCertFromFile reads a cert from file +func readCertFromFile(localCertFile string) ([]byte, error) { + // Read in the cert file + certPEM, err := os.ReadFile(localCertFile) + if err != nil { + return nil, err + } + return certPEM, nil +} + +var beginPrefix = []byte("-----BEGIN ") + +func getTLSx509KeyPairFromString(certPemBlock []byte, keyPemBlock []byte) (*tls.Certificate, error) { + + if !bytes.HasPrefix(certPemBlock, beginPrefix) { + s, err := base64.StdEncoding.DecodeString(string(certPemBlock)) + if err != nil { + return nil, err + } + certPemBlock = s + } + + if !bytes.HasPrefix(keyPemBlock, beginPrefix) { + s, err := base64.StdEncoding.DecodeString(string(keyPemBlock)) + if err != nil { + return nil, err + } + keyPemBlock = s + } + + // we assume it a DER certificate and try to convert it to a PEM. + if !bytes.HasPrefix(certPemBlock, beginPrefix) { + + pemBlock := &pem.Block{ + Type: "CERTIFICATE", + Bytes: certPemBlock, + } + + var buf bytes.Buffer + if err := pem.Encode(&buf, pemBlock); err != nil { + return nil, err + } + certPemBlock = buf.Bytes() + + } + // we assume it a DER key and try to convert it to a PEM. + if !bytes.HasPrefix(keyPemBlock, []byte("-----BEGIN")) { + pemBlock := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: keyPemBlock, + } + var buf bytes.Buffer + if err := pem.Encode(&buf, pemBlock); err != nil { + return nil, err + } + keyPemBlock = buf.Bytes() + } + + cert, err := tls.X509KeyPair(certPemBlock, keyPemBlock) + if err != nil { + return nil, err + } + + return &cert, nil +} + +// ReadKeyFromFile reads a key from file +func readKeyFromFile(localKeyFile string) ([]byte, error) { + // Read in the cert file + key, err := os.ReadFile(localKeyFile) + if err != nil { + return nil, err + } + return key, nil +} diff --git a/third_party/opa/v1/topdown/crypto_test.go b/third_party/opa/v1/topdown/crypto_test.go new file mode 100644 index 000000000000..d91bf9b33b8f --- /dev/null +++ b/third_party/opa/v1/topdown/crypto_test.go @@ -0,0 +1,894 @@ +package topdown + +import ( + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "errors" + "fmt" + "strconv" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +var rootCA = `-----BEGIN CERTIFICATE----- +MIIBoDCCAUagAwIBAgIRAJXcMYZALXooNq/VV/grXhMwCgYIKoZIzj0EAwIwLjER +MA8GA1UEChMIT1BBIFRlc3QxGTAXBgNVBAMTEE9QQSBUZXN0IFJvb3QgQ0EwHhcN +MjEwNzAxMTc0MTUzWhcNMzEwNjI5MTc0MTUzWjAuMREwDwYDVQQKEwhPUEEgVGVz +dDEZMBcGA1UEAxMQT1BBIFRlc3QgUm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49 +AwEHA0IABFqhdZA5LjsJgzsBvhgzfayZFOk+C7PmGCi7xz6zOC3xWORJZSNOyZeJ +YzSKFmoMZkcFMfslTW1jp9fwe1xl3HWjRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNV +HRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTch60qxQvLl+AfDfcaXmjvT8GvpzAK +BggqhkjOPQQDAgNIADBFAiBqraIP0l2U0oNuH0+rf36hDks94wSB5EGlGH3lYNMR +ugIhANkbukX5hOP8pJDRWP/pYuv6MBnRY4BS8gpp9Vu31qOb +-----END CERTIFICATE-----` +var intermediateCA = `-----BEGIN CERTIFICATE----- +MIIByDCCAW6gAwIBAgIQC0k4DPGrh9me73EJX5zntTAKBggqhkjOPQQDAjAuMREw +DwYDVQQKEwhPUEEgVGVzdDEZMBcGA1UEAxMQT1BBIFRlc3QgUm9vdCBDQTAeFw0y +MTA3MDExNzQxNTNaFw0zMTA2MjkxNzQxNTNaMDYxETAPBgNVBAoTCE9QQSBUZXN0 +MSEwHwYDVQQDExhPUEEgVGVzdCBJbnRlcm1lZGlhdGUgQ0EwWTATBgcqhkjOPQIB +BggqhkjOPQMBBwNCAARvXQa7fy476gDI81nqLYb2SnD459WxBmU0hk2bA3ZuNtI+ +H20KXz6ISmxH3MZ2WBm6rOy7y4Gn+WMCJuxzcl5jo2YwZDAOBgNVHQ8BAf8EBAMC +AQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuslZNjJl0V8I1Gj17IID +ALy/9WEwHwYDVR0jBBgwFoAU3IetKsULy5fgHw33Gl5o70/Br6cwCgYIKoZIzj0E +AwIDSAAwRQIgUwsYApW9Tsm6AstWswaKGie0srB4FUkUbfKwWmUI2JgCIQCBTySN +MF+EiQAMKyz/N9KUuXEckC356WvKcyJaYYcV0w== +-----END CERTIFICATE-----` +var leaf = `-----BEGIN CERTIFICATE----- +MIIB8zCCAZqgAwIBAgIRAID4gPKg7DDiuOfzUYFSXLAwCgYIKoZIzj0EAwIwNjER +MA8GA1UEChMIT1BBIFRlc3QxITAfBgNVBAMTGE9QQSBUZXN0IEludGVybWVkaWF0 +ZSBDQTAeFw0yMTA3MDUxNzQ5NTBaFw0zNjA3MDExNzQ5NDdaMCUxIzAhBgNVBAMT +Gm5vdGFyZWFsc2l0ZS5vcGEubG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D +AQcDQgAE1YSXZXeaGGL+XeYyoPi/QdA39Ds4fgxSHJTMh+js393kByPm2PNtFkem +tUii3KCRJw3SEh3z0JWr/9y4+ua2L6OBmTCBljAOBgNVHQ8BAf8EBAMCB4AwHQYD +VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRL0P0g17viZHo9 +CnXe3ZQJm48LXTAfBgNVHSMEGDAWgBS6yVk2MmXRXwjUaPXsggMAvL/1YTAlBgNV +HREEHjAcghpub3RhcmVhbHNpdGUub3BhLmxvY2FsaG9zdDAKBggqhkjOPQQDAgNH +ADBEAiAtmZewL94ijN0YwUGaJM9BXCaoTQPwkzugqjCj+K912QIgKKFvbPu4asrE +nwy7dzejHmQUcZ/aUNbc4VTbiv15ESk= +-----END CERTIFICATE-----` + +var rsaPrivateKey = `-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA3Y8cXdK06ufUSP035jiwJk8IsuwGjJD/LSRvE2AhJL/Vp9mu +41z1bV5Mi/TTK/uZNqv6VdvTxFPZOUYycLXEchg8L6wrOLgAX0DleP+YTKGG4oyg +dTZZcqzwr4p7WhYzLFmpW8RCLgHJbV0fF1pejJKtV+9fpsdX8oQzKvqO39ne1hl+ +m/lq2LKBK0z03c4ay+bFzA8AFMndmzfB3uXl2fTFsNaoYxAkGwlcvFAXNegPKtaf +9Co5JpRlRejPYVSonCvCvBakGIDCRb0ZHQrcGBzDnqjZeZMDkfe0YKoRUR+JFn69 +C7a4tHheA0TerIDcv+IqadY7p2jwIom9di1oWwIDAQABAoIBAQDXEXGGvd+y20Gd +bHhTuZl8RmH6VNTypFmf92r/UuQ5aSI8Ijn7KKRw+wWxIgHPAxcyE/UYXSCOxpnp +V/Pkpv0/h7j8ydLW5v4teLCIKQws7ushhULJJO3lPG0S6Yld5IjeN1cH5lYblM5z +o95na+i16jfsUUf3fDAqERweT0Rbk7IlegTgXtXLjbvGpFWgjH7Oc8UPpy56i05h +NtdBvQhFV8LMckQAfEinBTPDHqZw6hGIfJtieRhwTzGh5H0fnDCRZanRKm2uxh4Z +9ciYZ/wa0Af23atGoax1YbQJFJK8h0vWcL1jJkaZ+CmVmRtYcWPTpDNGe2FQn9I2 +EwF5nB8BAoGBAPpAsZiFC00YJf1gN4G588+7hxMU2BaoTosImSD27sLLmE2XHBa+ +FrtLJR+t6pRtt7aQccGrNp2G234ucjitM2A1JmtzywPhtAXp+/VaguikdJ62zAjl +Sn6nl9W6ovOQ0NsHGmO7MFILrWXXpF7IqhXd/MdwMnxJABsKqZpBLB2BAoGBAOKl +uARPETauBRdQisEzHI1kosHigCVCSTwwTnFa8LXfinfFCq68SuuwqUdN5RaNUpGx +zTFxOgihcSlfOF0/VXROi6PI768pp2SOgbKjXsleZqxaSe5iZ61jt0uU0HlUsfoI +JXULgVweidZhlD0JJK2RGK2K7CVGTPluX07xO6vbAoGAPOPE0oF8sHNxuubQWqYu +JptQUFpAAbNN+RJMf/LVQVxcYHSmBvqVeVjdXYnpi9fuXWNj6mWIUmffvCH89MFf +wMbt5DM2cGlYbh/yiE5Pj9+D6KI9nuR7bbnFfeF9iJnx13kw+JcxOKVSuXbwrYdR +qyRqPvSTtB3nAq1jev7khwECgYBEgldHZicL4jpDu+LVV3/P9ZWFCdQ2bvz4Jpnv +hc+xCisu3O7Htr7m03W3ygHveTR2OcqOoW0rYrF0EgZVmWlZSMzI61oYFn001ia6 +OsvSDqj2fCxQ1IoGTVgAjrEdm85Yh9HauWmW0NxVYxWOBY+Cr5NIEfAjrEZkN0qz +8BNbdQKBgD4w2xm7jFMUgPzHp7L8RWMWLUTBudc981dOPQJ5kAR5n2oEhE1YJs+e +GjJuyhAhz5VdHn2H2+RptQ70RVM+ctDNKYZko2aH4uGZq/6X5MWGr1erLMgMbg5q ++oSLpOUiUobapGdl9fgHetyFw/N9TI1tl/4+2uFqW5knBQnXByPP +-----END RSA PRIVATE KEY-----` + +var rsaPrivateKeyPKCS8 = `-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDP7abKDTHtqGkk +6c/jxbZph17QcVz3NxcRrQ8RCLWHZd020oANIssGgZwGuy9hvQUfEYRy1+78wJmV +c7naeJ8qkLj1u0OsDLwofRaYXzkZUFitZr2Ygkzhy8/GVhdIMVnAV2u4LHvpw+dS +8hsnpWnIzF5Rdo3e7KNZbjZlCLBDrmorGsdvYKqwN/7aBd81YaS5dz67oacG0/bI +Bn2ox93OI+OQLrdtYG2aDMv9eEs8QQ8X10YI2Fsp2t2rAstwBGhsSbMPdBF82G9G +XIng4ZTO6P0G1ypYcXha4okhLO2ck15bYyd+EAY3QfyJ5MMcHMvr/iJpGCVeIyFm +m9qoyGqLAgMBAAECggEAdtocLYBvWq6aM1xm1YaNJzMW0kUKY9EcoaDvbMgyo0tp +sE2QnnGV5Ykue3aBtfeKtuCXeeHOHLGm2JPG14d9S6Jf5y58lxrMbsRZpw0/ISYZ +Gj0RANzyP1r10CQjuMNkzxnpW+QpjEzLrFDxjq7xkbKn8x62J4fSM2tZMlVOE9DV +1Mc45/1r3VgEdzkONSBykT51woTdcovUnP4gEg+REky1Wb1S1rk8m1MRAIq4T1Yu +cRyqpNNYhJbXofPwNMhrdo9fqhaCYTrxf8ZpiFDnZHqF28zQtSUm0YgFJR4vZkAd +esBWo++FVefIL3T6VkbOHKN4I4dk+EWlERHjVQz+uQKBgQDrebFo6qoAqr1O/c7d +CDTU4FXZcml7IPSLL3U196WB/MfsP+UVzgD4+DOHenQwHbbj7ta/rgF7iIHliqBX +WdGFgywPs7sNhq11av5ZEAXHD7r8eZBjKlV8IsvMA51MCp1/SK8McxVhBVEJgsGL +VSRxvRz9tVR7wlKcg7DE0aBF3wKBgQDiDUjUNU2HDDmYuCuEmsjH/c6f/P+BjLXp +LnKW0aUbvQl/nDTMTJTIu0zG0+OJhL4GWDkB9DW115kxCGFmZMvrk3LeDqg1QWDQ +d3cxgEdSSsRWBsiABvIn7Fno/MN2NrZd8Wdfk7HIIF0rGOy9ja5/PVl0FxUt4O1X +dRmQ3oq41QKBgHoD4djyl8qmrleLDrDburx/zhxRu7SQnAavPbYML9fOSy3w4dzN +lRVtTw4pdqEkFIvBS8eg+6WuU1jE31bD9NyQ3rj4MbnNin4oRcmSktvWG9cNirLH +0en0AdQiH1Syv2+gEwyJaY+PeLFL7swq/ypsiuQwHKnQRIxTdLpXwQvTAoGAS7+Z +3QpzjUKKdmOYqZnYmDOzrqbv07CcMKRQ37smsbHZ4fotMxyiatVgt+u+/pENwECF +8eKssN+rROQDB3XVY36IamLM+POMhq7RsTPEMo49Vnp1a3loYfpwcoNo2E8jMz22 +ny91zpMRxWRXyHkWtSqQtDcb8MDDp5/kzkfUgnUCgYEAv8CVWPKTuw83/nnqZg26 +URXJ/C7hN/1uU21BuyCTMV/fLiSAsV0ucDV2spqCl3VAXcsECavERVppluVylBcR +DFa6BZS0N0x374JRidFWV0a+Mz7pTqC0TO/M3+y6yaDd766J3bkdh2sq8pnhAnXc +qPYXB5U6tdTrexzaYBKr4gQ= +-----END PRIVATE KEY-----` + +var keyPemEC = `-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49 +AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q +EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA== +-----END EC PRIVATE KEY-----` + +var keyEd25519 = `-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIJHG93jlLLLTF6Stky5+8Q7mMpgCkYYTO12NDAzlJn3w +-----END PRIVATE KEY----- +` + +var partiallyValidPEMString = ` +something else +-----BEGIN PRIVATE KEY----- + MC4CAQAwBQYDK2VwBCIEIJHG93jlLLLTF6Stky5+8Q7mMpgCkYYTO12NDAzlJn3w +-----END PRIVATE KEY----- +something else +-----BEGIN CERTIFICATE----- +MIIBcDCCARagAwIBAgIJAMZmuGSIfvgzMAoGCCqGSM49BAMCMBMxETAPBgNVBAMM +CHdoYXRldmVyMB4XDTE4MDgxMDE0Mjg1NFoXDTE4MDkwOTE0Mjg1NFowEzERMA8G +A1UEAwwId2hhdGV2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATPwn3WCEXL +mjp/bFniDwuwsfu7bASlPae2PyWhqGeWwe23Xlyx+tSqxlkXYe4pZ23BkAAscpGj +yn5gXHExyDlKo1MwUTAdBgNVHQ4EFgQUElRjSoVgKjUqY5AXz2o74cLzzS8wHwYD +VR0jBBgwFoAUElRjSoVgKjUqY5AXz2o74cLzzS8wDwYDVR0TAQH/BAUwAwEB/zAK +BggqhkjOPQQDAgNIADBFAiEA4yQ/88ZrUX68c6kOe9G11u8NUaUzd8pLOtkKhniN +OHoCIHmNX37JOqTcTzGn2u9+c8NlnvZ0uDvsd1BmKPaUmjmm +-----END CERTIFICATE----- +something else +-----BEGIN PRIVATE KEY----- + MC4CAQAwBQYDK2VwBCIEIJHG93jlLLLTF6Stky5+8Q7mMpgCkYYTO12NDAzlJn3w +-----END PRIVATE KEY----- +something else +` +var invalidData = `nothingtoseehere` + +func TestX509ParseAndVerify(t *testing.T) { + t.Parallel() + + t.Run("TestFullChainPEM", func(t *testing.T) { + t.Parallel() + + chain := strings.Join([]string{rootCA, intermediateCA, leaf}, "\n") + + parsed, err := getX509CertsFromString(chain) + if err != nil { + t.Fatalf("failed to parse PEM cert chain: %v", err) + } + + if _, err := verifyX509CertificateChain(parsed, x509.VerifyOptions{}); err != nil { + t.Error("x509 verification failed when it was expected to succeed") + } + }) + + t.Run("TestFullChainBase64", func(t *testing.T) { + t.Parallel() + + chain := strings.Join([]string{rootCA, intermediateCA, leaf}, "\n") + b64 := base64.StdEncoding.EncodeToString([]byte(chain)) + + parsed, err := getX509CertsFromString(b64) + if err != nil { + t.Fatalf("failed to parse base64 cert chain: %v", err) + } + + if _, err := verifyX509CertificateChain(parsed, x509.VerifyOptions{}); err != nil { + t.Error("x509 verification failed when it was expected to succeed") + } + }) + + t.Run("TestWrongOrder", func(t *testing.T) { + t.Parallel() + + chain := strings.Join([]string{leaf, intermediateCA, rootCA}, "\n") + + parsed, err := getX509CertsFromString(chain) + if err != nil { + t.Fatalf("failed to parse PEM cert chain: %v", err) + } + + if _, err := verifyX509CertificateChain(parsed, x509.VerifyOptions{}); err == nil { + t.Error("x509 verification succeeded when it was expected to fail") + } + }) + + t.Run("TestMissingIntermediate", func(t *testing.T) { + t.Parallel() + + chain := rootCA + "\n" + leaf + + parsed, err := getX509CertsFromString(chain) + if err != nil { + t.Fatalf("failed to parse PEM cert chain: %v", err) + } + + if _, err := verifyX509CertificateChain(parsed, x509.VerifyOptions{}); err == nil { + t.Error("x509 verification succeeded when it was expected to fail") + } + }) + + t.Run("TestTooFewCerts", func(t *testing.T) { + t.Parallel() + + parsed, err := getX509CertsFromString(leaf) + if err != nil { + t.Fatalf("failed to parse leaf cert: %v", err) + } + + if _, err := verifyX509CertificateChain(parsed, x509.VerifyOptions{}); err == nil { + t.Error("x509 verification succeeded when it was expected to fail") + } + }) +} + +func Test_parsex509KeyPair(t *testing.T) { + t.Parallel() + + certPemEC := []byte(`-----BEGIN CERTIFICATE----- +MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw +DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow +EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d +7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B +5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr +BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1 +NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l +Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc +6MF9+Yw1Yy0t +-----END CERTIFICATE-----`) + keyPemEC := []byte(`-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49 +AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q +EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA== +-----END EC PRIVATE KEY-----`) + + certPemx509 := []byte(`-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIUdRHA+B0/ZgknKPlB2fswE98lzAcwDQYJKoZIhvcNAQEL +BQAwgYYxCzAJBgNVBAYTAlhYMRIwEAYDVQQIDAlTdGF0ZU5hbWUxETAPBgNVBAcM +CENpdHlOYW1lMRQwEgYDVQQKDAtDb21wYW55TmFtZTEbMBkGA1UECwwSQ29tcGFu +eVNlY3Rpb25OYW1lMR0wGwYDVQQDDBRDb21tb25OYW1lT3JIb3N0bmFtZTAeFw0y +MzA1MTAxMjUxMjhaFw0zMzA1MDcxMjUxMjhaMIGGMQswCQYDVQQGEwJYWDESMBAG +A1UECAwJU3RhdGVOYW1lMREwDwYDVQQHDAhDaXR5TmFtZTEUMBIGA1UECgwLQ29t +cGFueU5hbWUxGzAZBgNVBAsMEkNvbXBhbnlTZWN0aW9uTmFtZTEdMBsGA1UEAwwU +Q29tbW9uTmFtZU9ySG9zdG5hbWUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQC3N9jvicmKpGd1P3pXeA+MoiddNjJ5vM13KqaTc129mcmHHtIrjGnhoLrN +SLWcLGKNBbsBNi5loq41sJogymNxUsxQQErXn5DiEnEQf1Bq+nSGzIdGmbmJVWqH +t4tSIHdCIEaxHDHWkIoklR4t8CgNIvhKqlGGJlvLfPVZgWHjs9HA+6+G35tp/ZgA +vSA/Nutk5P9ALOjlysiySiX6maRPk+yrNfpqzA9clIeVQQCTSGHRPeOxg1SSqpHh +MukuY01JHwotBUQlNKuDWBwz13FOuaPc74g0psNQWeCIi7IZ6cPZWJfbCkRHB0eF +paOGcnyFFafAfNYZ1wZ87H6U3U4zwr0ILxYz6Dn8B4fIkkoY19PHEmn4trHm5wbR +nJVPieffAZzqK/zcaiBFVDdGQC/Ty3K5z9+VFbIzPWIR5S6weWtbA8h3aa/E/tHS +CYWvi+4RwqPxfWQ6JevxdWC08Rgy1IPz7vqkvD0bg7Rf01vIUohO3vboH7Jz2K0o +vEGiRdcPQfBxy/R6O4estPpTLE9CSSkcTAQcifCpn3OU8/vuRLDEy9At+mad+G5V +KiSno8D5ygljOHEQICZO0cENF5uVcrNNhQYS0Jd7F9eVCSO8HUCslMqF2ednFCpS +frYIQksRTgej4bXUxlTF3vfjEZn5idVsIgj2tVQoN0drn8MASQIDAQABo1MwUTAd +BgNVHQ4EFgQUlDXyP2Hr+VQM1aHlzcN9bDlMbJswHwYDVR0jBBgwFoAUlDXyP2Hr ++VQM1aHlzcN9bDlMbJswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AgEACzGrfXVYhfCmtFZN0Y6NKQ7g1HVGIX5V3Al4U+oo3y+9n4KVGkgV9jh/dZ8k +mzWmpXOGRfD1ufRcD9qoXy+GHRw/0bm2wkVcMvEsnMx+DkuhiVOIc0PYDNj87uK3 +vNFBM39uo1fiJ9yd9SKQsjB94lVMaF1089wevPYl5qmboku2qurX3uw5VvysHAAw +U2y0x8y/c3jw9MfK1X0GYhScZ0pV3y3xtlqrHnNjSRaZ3pfPl74ac0bgtE9q8Jt5 +o3D0gfZi08RkunnkgJ1/Pd5iaH/XGgTnz5wSEAzTRBXHKYYoFpFWL0rz0kJojfCw +Rut6Omn/L6nQW/R5zwsqvkADuo/KXY2N69/RxPw0z8Eq6YMTt8pW8e5dzRFQTKcv +KTt2B50uTUDRPQmovvF3FdjrYZbHrFXGQr2hezl56BDRJBZO8HCH44B1swz+t2If +ouv2PHd05WvCTr/3GE/fbTjZlUVx8Za2TYwVjHSS9w9kQCUvw3W3nNuQzNIGTbsa +DvT3ySt22dD40MYk4zNsz8wV0QV0vbyzz+1FTIreVWIIxi0uoygMhuFL7Izn1xsA +T8Mellt37m03evBLpQwOXqt7RWZSZ2YruT6rZdgdA9te+f7ULaaCFD2qc9hDTK8j +zhw2N5pcH1ZXdfAF3MQj5Va+HICDIZ/pmNcdVRgYlV7Hd9g= +-----END CERTIFICATE----- +`) + + certPemRSA := []byte(`-----BEGIN PRIVATE KEY----- +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 +1rJjQEsiZxH+QPR88tuByUVG000lpA== +-----END PRIVATE KEY----- +`) + + certDERx509 := []byte(`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 +`) + certDERRSA := []byte(`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 +`) + + certPemRSACrtB64 := []byte(`LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUY3ekNDQTllZ0F3SUJBZ0lVZFJIQStCMC9aZ2tuS1BsQjJmc3dFOThsekFjd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZWXhDekFKQmdOVkJBWVRBbGhZTVJJd0VBWURWUVFJREFsVGRHRjBaVTVoYldVeEVUQVBCZ05WQkFjTQpDRU5wZEhsT1lXMWxNUlF3RWdZRFZRUUtEQXREYjIxd1lXNTVUbUZ0WlRFYk1Ca0dBMVVFQ3d3U1EyOXRjR0Z1CmVWTmxZM1JwYjI1T1lXMWxNUjB3R3dZRFZRUUREQlJEYjIxdGIyNU9ZVzFsVDNKSWIzTjBibUZ0WlRBZUZ3MHkKTXpBMU1UQXhNalV4TWpoYUZ3MHpNekExTURjeE1qVXhNamhhTUlHR01Rc3dDUVlEVlFRR0V3SllXREVTTUJBRwpBMVVFQ0F3SlUzUmhkR1ZPWVcxbE1SRXdEd1lEVlFRSERBaERhWFI1VG1GdFpURVVNQklHQTFVRUNnd0xRMjl0CmNHRnVlVTVoYldVeEd6QVpCZ05WQkFzTUVrTnZiWEJoYm5sVFpXTjBhVzl1VG1GdFpURWRNQnNHQTFVRUF3d1UKUTI5dGJXOXVUbUZ0WlU5eVNHOXpkRzVoYldVd2dnSWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUNEd0F3Z2dJSwpBb0lDQVFDM045anZpY21LcEdkMVAzcFhlQStNb2lkZE5qSjV2TTEzS3FhVGMxMjltY21ISHRJcmpHbmhvTHJOClNMV2NMR0tOQmJzQk5pNWxvcTQxc0pvZ3ltTnhVc3hRUUVyWG41RGlFbkVRZjFCcStuU0d6SWRHbWJtSlZXcUgKdDR0U0lIZENJRWF4SERIV2tJb2tsUjR0OENnTkl2aEtxbEdHSmx2TGZQVlpnV0hqczlIQSs2K0czNXRwL1pnQQp2U0EvTnV0azVQOUFMT2pseXNpeVNpWDZtYVJQayt5ck5mcHF6QTljbEllVlFRQ1RTR0hSUGVPeGcxU1NxcEhoCk11a3VZMDFKSHdvdEJVUWxOS3VEV0J3ejEzRk91YVBjNzRnMHBzTlFXZUNJaTdJWjZjUFpXSmZiQ2tSSEIwZUYKcGFPR2NueUZGYWZBZk5ZWjF3Wjg3SDZVM1U0endyMElMeFl6NkRuOEI0Zklra29ZMTlQSEVtbjR0ckhtNXdiUgpuSlZQaWVmZkFaenFLL3pjYWlCRlZEZEdRQy9UeTNLNXo5K1ZGYkl6UFdJUjVTNndlV3RiQThoM2FhL0UvdEhTCkNZV3ZpKzRSd3FQeGZXUTZKZXZ4ZFdDMDhSZ3kxSVB6N3Zxa3ZEMGJnN1JmMDF2SVVvaE8zdmJvSDdKejJLMG8KdkVHaVJkY1BRZkJ4eS9SNk80ZXN0UHBUTEU5Q1NTa2NUQVFjaWZDcG4zT1U4L3Z1UkxERXk5QXQrbWFkK0c1VgpLaVNubzhENXlnbGpPSEVRSUNaTzBjRU5GNXVWY3JOTmhRWVMwSmQ3RjllVkNTTzhIVUNzbE1xRjJlZG5GQ3BTCmZyWUlRa3NSVGdlajRiWFV4bFRGM3ZmakVabjVpZFZzSWdqMnRWUW9OMGRybjhNQVNRSURBUUFCbzFNd1VUQWQKQmdOVkhRNEVGZ1FVbERYeVAySHIrVlFNMWFIbHpjTjliRGxNYkpzd0h3WURWUjBqQkJnd0ZvQVVsRFh5UDJIcgorVlFNMWFIbHpjTjliRGxNYkpzd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRc0ZBQU9DCkFnRUFDekdyZlhWWWhmQ210RlpOMFk2TktRN2cxSFZHSVg1VjNBbDRVK29vM3krOW40S1ZHa2dWOWpoL2RaOGsKbXpXbXBYT0dSZkQxdWZSY0Q5cW9YeStHSFJ3LzBibTJ3a1ZjTXZFc25NeCtEa3VoaVZPSWMwUFlETmo4N3VLMwp2TkZCTTM5dW8xZmlKOXlkOVNLUXNqQjk0bFZNYUYxMDg5d2V2UFlsNXFtYm9rdTJxdXJYM3V3NVZ2eXNIQUF3ClUyeTB4OHkvYzNqdzlNZksxWDBHWWhTY1owcFYzeTN4dGxxckhuTmpTUmFaM3BmUGw3NGFjMGJndEU5cThKdDUKbzNEMGdmWmkwOFJrdW5ua2dKMS9QZDVpYUgvWEdnVG56NXdTRUF6VFJCWEhLWVlvRnBGV0wwcnowa0pvamZDdwpSdXQ2T21uL0w2blFXL1I1endzcXZrQUR1by9LWFkyTjY5L1J4UHcwejhFcTZZTVR0OHBXOGU1ZHpSRlFUS2N2CktUdDJCNTB1VFVEUlBRbW92dkYzRmRqcllaYkhyRlhHUXIyaGV6bDU2QkRSSkJaTzhIQ0g0NEIxc3d6K3QySWYKb3V2MlBIZDA1V3ZDVHIvM0dFL2ZiVGpabFVWeDhaYTJUWXdWakhTUzl3OWtRQ1V2dzNXM25OdVF6TklHVGJzYQpEdlQzeVN0MjJkRDQwTVlrNHpOc3o4d1YwUVYwdmJ5enorMUZUSXJlVldJSXhpMHVveWdNaHVGTDdJem4xeHNBClQ4TWVsbHQzN20wM2V2QkxwUXdPWHF0N1JXWlNaMllydVQ2clpkZ2RBOXRlK2Y3VUxhYUNGRDJxYzloRFRLOGoKemh3Mk41cGNIMVpYZGZBRjNNUWo1VmErSElDRElaL3BtTmNkVlJnWWxWN0hkOWc9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K`) + certPemRSAKeyB64 := []byte(`LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRZ0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1N3d2dna29BZ0VBQW9JQ0FRQzNOOWp2aWNtS3BHZDEKUDNwWGVBK01vaWRkTmpKNXZNMTNLcWFUYzEyOW1jbUhIdElyakduaG9Mck5TTFdjTEdLTkJic0JOaTVsb3E0MQpzSm9neW1OeFVzeFFRRXJYbjVEaUVuRVFmMUJxK25TR3pJZEdtYm1KVldxSHQ0dFNJSGRDSUVheEhESFdrSW9rCmxSNHQ4Q2dOSXZoS3FsR0dKbHZMZlBWWmdXSGpzOUhBKzYrRzM1dHAvWmdBdlNBL051dGs1UDlBTE9qbHlzaXkKU2lYNm1hUlBrK3lyTmZwcXpBOWNsSWVWUVFDVFNHSFJQZU94ZzFTU3FwSGhNdWt1WTAxSkh3b3RCVVFsTkt1RApXQnd6MTNGT3VhUGM3NGcwcHNOUVdlQ0lpN0laNmNQWldKZmJDa1JIQjBlRnBhT0djbnlGRmFmQWZOWVoxd1o4CjdINlUzVTR6d3IwSUx4WXo2RG44QjRmSWtrb1kxOVBIRW1uNHRySG01d2JSbkpWUGllZmZBWnpxSy96Y2FpQkYKVkRkR1FDL1R5M0s1ejkrVkZiSXpQV0lSNVM2d2VXdGJBOGgzYWEvRS90SFNDWVd2aSs0UndxUHhmV1E2SmV2eApkV0MwOFJneTFJUHo3dnFrdkQwYmc3UmYwMXZJVW9oTzN2Ym9IN0p6Mkswb3ZFR2lSZGNQUWZCeHkvUjZPNGVzCnRQcFRMRTlDU1NrY1RBUWNpZkNwbjNPVTgvdnVSTERFeTlBdCttYWQrRzVWS2lTbm84RDV5Z2xqT0hFUUlDWk8KMGNFTkY1dVZjck5OaFFZUzBKZDdGOWVWQ1NPOEhVQ3NsTXFGMmVkbkZDcFNmcllJUWtzUlRnZWo0YlhVeGxURgozdmZqRVpuNWlkVnNJZ2oydFZRb04wZHJuOE1BU1FJREFRQUJBb0lDQUJKaUttUm9neDRqM2xTYm5Lc3Jtd1hOCm5GMEVLOTdlcEpBTktiOFlQNExmYkNMZ1l3Nm5EVldzQXFwSDNoOFFMZ2cvMTdKb3JSR2FGOWcvd3N0QSsyYmEKdTdEZXJwUEJpVEJCMFBIcWtGZFhqM3NhQ1FXNnRXelQ4dmN3b2F4SklTWXpwbHd0ZTh1dlg0a0pwRWhRTlRpUwpObThKZFZvY1BiQW1kdGkyL0dzME52cmgxZ3dXb2htcHJnZCs4bjRkUk5Pd0RYTnpQaUFXYjNwQ0tkcmg4U1JoCjc0aURSei9SZjBZWENoNmQ4ZENWWGVrNGlFRGVzRXp5QythWWJPQ3dhb2dJY3dVdTV0WkQyV1M1b2NUSzNHM2QKZnhWVFBHdXFBdVZzU3pUd0xWdmZ3bnlyb0xzRDVmTnBoZEhoVzQzSkxYak9BakcwWk9nZFZPT1NlQ1g0S1prbwptcWc4b3Y1TksxclVRVGQwNng2bmg0elBXdEdaTDIwbE9EMTZvRWgzUlZoRFU5akQ1U25aZ2M1Mzg2bVkxeTVJClV0QTBUUnh3MzNIcjRWT1ExaXVCZzlrNTU2MzFIcGl0clBWZ1QvLzVzYzdjMjhpQ2lBRnVFZGg3SDF5cG5NNkgKc1pjL2prRTJBOFF1NnpKSTV2NzRoYkFaTmxONVMrcDU4ajhrV2pPYTd4bnZwYk5saXc1a2JDK2Ezb25ZSVNHZApTNVZITzVEVkVaOEY3aUtUTS8zTEFZdDZScG0vdEZJOUhFZVRGWHVCVEVsYTlsb0srUWdXbjF1QVVMeGtKUHFyCkFXZ0tzL0FYYWpxVyt0d3ZUM2M5cUFDaGFTakhjV3lxem05WGh6RDRwNTg2cnpDZ3M5VUNSSStudlEvOGFIMWoKNm9CdkRqbUxpem9WUEVkR2k1VkJBb0lCQVFEM2pRZThEdVIxVjYveS9HcXRNRVcyVXptdTl2d1R4VXRBSFZtawpob25PbFo0MDVEL1B3b0s1dXBORjV2QWtxNDN1b1M2OXVuQWxxbGpraVNmOTEzU3pYZEE4dXRRYlQzVFczUmxWCkZ2dXZlN3YvcWtRZFdScm5rdXJhcWtFbnhUMnlKOGdlaG1Qcmo1STMwVHVRVXpSdmFRc2hyYkIwV3NCcGcrRmIKdWJnV29wU21hVTY5aEJpSGxwWFYrcjJRUXIrVllHT3JLUkNSWDRhMGxtbEJxbDdnemVZSTA2d2RpamszelF5cgorcVltYjRkdEFqd3huQ09CRG9qbVJBM2hGTmpVbC8wNlVvMmZzUEM1cjduWlA2cURUMUU2MU5tVDhPZ3kyUWE1Cm90Uk4wS1E3bThGRXAwUDA4aHByOTRxVlZLaHB2NzMzcGMwQkhCQXZ6VG9RdTdKcEFvSUJBUUM5ZUxZZzVSS3cKMDZQSEVmRlhMTnZ1NU12Q2I0T2NHY1BHUElLaHEyWExTbFJSSElVSkdueFB2RVRrcm93YXhDVGVVc2g0WUFtUApZUjZxKzE2aHRwN1JBZ1RWNkxvZUpnQ2hDNVRvTWlkZkR2MmxEUnZneVpMWWtsU0VIcEpaSXRWWTI4NzFWTkRaClhFQWwwOGFGcHlENDNseXdNc3UxaHYzSnRMVGJVTDNZdGtBZnBQVFJBZ0ZaaFlxbVJUdTJKeXN5dXVoWWFVbS8KeEw3WDcvYmlJZ2FPemR1YkF0QXBFTUlEbnFOdVdKOUVBQit4a1c5VUIyTFpRZjBuOTh2bG40aUowaDhsb1V5RAphWElPbWpDZFJXeGFzeVRRQkNEZUR0eXgxbHNvb1R1U2JWU1FFSHVUeUgzVno2UHZBZC9xNGxwRW5hY0UvMjE5CjRXUEh6NnArcDJMaEFvSUJBQ28yQXhhZkYzZW16eHJJemN2Z1NsTFBtQ3RzZEFsUEFBamJ1RmhrbElVRVlDaTIKcnViWFRRRXNma1pTSGFxekVnMlpzR1dycjhuTVpVSDYzVFhja2txdmVYMlJnZTl5T2dNVlNtZUc5cjJ5aEprUQp5SEtVcWhESXJZRkJ2TUJ5VXBYWlVMZGJ4UmY2c0QwU1VXekhzMDQ0QkN6bStBcXZHdFlqSmI5RlNNMmJSV3VtCjAwVmZpK3M2MHl2Y2lJeGJ4VjFNUlZKL094TCt6ZkpuSDJXU0RvR1l1bHZROUMxSlQzNWpXWUROeVowT01YSjIKQ2h1UGUwSmJYeDZjaGgxV042N3doNzUxS3k4S3RkR0QxRlhtRkVZMXRTMHA5RHZVdlZOR1RHNUZCSnlNTWlUego1eDIwdzlLMW9hbTlXUVVqbldBQzBQcTBhK04vakljS0lKZVAyZGtDZ2dFQVhPZzhKcFV0UFJnS1R5czFOSklDCnBubjZrRFV1Uy9VMlVwYUpWODA3OVJ0VmpSQjNDNmU1SFVBc2FCWlBEVER4QXpPRXFjSXQ3ZWlwcVIzcG9WSnoKUGZuSGRUelJSc2RMdDZ4K0wvMm40S3p4STJYeUxaK3FLaGhXNlJJMG9SQzduUDdyMU5EcU9DdE1LVUJYTUdKcgpnSjFJeGYyaWRqamphV3o2NGpBTlo1NjJnczNZWGtTbGRNaE8zSWxHWm1OK2d6bXpoT2JjQ3ZUbXYrd2pHMitqCjE1S0tCTkMwVWU2dHRDaXQ2d1g1MHRaY3RDMmtjWWZOcU1yNjRBWmFMUmExVlI5N3RuQUpuTWF2N3drY25ZSFYKU0FSZ0lNQmxmWDI4S2xmNkMwcEVjK0M0Zm93V2pMamJPMlM5OWd6dFI3Z0dtMjdTMzFpQTBDRWRWSFU0SFRMbgpBUUtDQVFFQXNzajBmRThqa0ZpdnpRMmxTZTBEeGhwUXFHZVF1UGROYkJKVnNBcVdmRXRKQ0h2R0JYaTZBeEIrClFnN015RE14dnVPYXQ1TVNUQ01GYzNYdVFUdEtxdlF3SGxXMXZpTnJxckNzRHVRNTVDa1ZQTUxmazk4VlFoeHkKMnpUd20zOUd5a1ZpLy9CaU5LQzRCU2ZtZlN6cGdiYlJnSEI2Ny9zcGIwejBMbXUrWG5uL0ROemxxS1pvQUZFQgpwcDdzNkVmWGdJN1Npbm94dGxuRHM4eDdFM2dlcEtENVVWbnQzM3FZbUhCOVZ3WHpUZDBaSkhrY0x1b1VpanJjCko2cENYVTcrRlpPd3pJQi9IcFFPZ2pwdklqQ0pJaHRGeFZ5Z2JqdXVjRThRS2Naaml5VXNGTHZkQzVkZTFNZVQKMXJKalFFc2laeEgrUVBSODh0dUJ5VVZHMDAwbHBBPT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=`) + + t.Run("ParseX509KeyPairEC", func(t *testing.T) { + t.Parallel() + + validKeyPairEc, err := getTLSx509KeyPairFromString(certPemEC, keyPemEC) + if err != nil { + t.Fatal("failed to parse x509 key pair") + } + if validKeyPairEc == nil { + t.Fatal("expected certificate but got nil") + } + + }) + + t.Run("ParseX509KeyPairRSA", func(t *testing.T) { + t.Parallel() + + validCertPair, err := getTLSx509KeyPairFromString(certPemx509, certPemRSA) + if err != nil { + t.Fatal("failed to parse x509 Pair with RSAkey") + } + if validCertPair == nil { + t.Fatal("expected certificate but got nil") + } + + }) + + t.Run("ParseX509KeyPairRSABase64", func(t *testing.T) { + t.Parallel() + + validCertPair, err := getTLSx509KeyPairFromString(certPemRSACrtB64, certPemRSAKeyB64) + if err != nil { + t.Fatal("failed to parse x509 Pair with RSAkey") + } + if validCertPair == nil { + t.Fatal("expected certificate but got nil") + } + + }) + + t.Run("ParseX509KeyPairDERx509", func(t *testing.T) { + t.Parallel() + + validCertPair, err := getTLSx509KeyPairFromString(certDERx509, certDERRSA) + if err != nil { + t.Fatal("failed to parse x509 Pair with RSAkey", err) + } + if validCertPair == nil { + t.Fatal("expected certificate but got nil") + } + }) + + t.Run("ParseX509KeyPairPEMstringB64Key", func(t *testing.T) { + t.Parallel() + + validCertPair, err := getTLSx509KeyPairFromString(certPemx509, certPemRSAKeyB64) + if err != nil { + t.Fatal("failed to parse x509 Pair with RSAkey") + } + if validCertPair == nil { + t.Fatal("expected certificate but got nil") + } + + }) + + t.Run("ParseX509KeyPairB64CRTBPEMKey", func(t *testing.T) { + t.Parallel() + + validCertPair, err := getTLSx509KeyPairFromString(certPemRSACrtB64, certPemRSA) + if err != nil { + t.Fatal("failed to parse x509 Pair with RSAkey") + } + if validCertPair == nil { + t.Fatal("expected certificate but got nil") + } + + }) + + t.Run("ParseX509KeyPairMisMatchedTypes", func(t *testing.T) { + t.Parallel() + + certPair, err := getTLSx509KeyPairFromString(certPemEC, certPemRSA) + if err == nil { + t.Fatal("expected error but got nil") + } + if certPair != nil { + t.Fatalf("expected no certificate but got %v\n", certPair) + } + }) + +} + +func Test_getPrivateKeyFromPEMData(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + input string + wantErr string + keyCheck func(t *testing.T, keys []crypto.PrivateKey) + }{ + "invalid data": { + input: invalidData, + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 0 { + t.Fatalf("expected no keys but got %d", len(keys)) + } + }, + }, + "rsa key": { + input: rsaPrivateKey, + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 1 { + t.Fatalf("expected 1 key but got %d", len(keys)) + } + if _, ok := keys[0].(*rsa.PrivateKey); !ok { + t.Fatalf("expected rsa key but got %T", keys[0]) + } + }, + }, + "base64 rsa key": { + input: base64.StdEncoding.EncodeToString([]byte(rsaPrivateKey)), + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 1 { + t.Fatalf("expected 1 key but got %d", len(keys)) + } + if _, ok := keys[0].(*rsa.PrivateKey); !ok { + t.Fatalf("expected rsa key but got %T", keys[0]) + } + }, + }, + "rsa key pkcs8": { + input: rsaPrivateKeyPKCS8, + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 1 { + t.Fatalf("expected 1 key but got %d", len(keys)) + } + if _, ok := keys[0].(*rsa.PrivateKey); !ok { + t.Fatalf("expected rsa key but got %T", keys[0]) + } + }, + }, + "base64 rsa key pkcs8": { + input: base64.StdEncoding.EncodeToString([]byte(rsaPrivateKeyPKCS8)), + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 1 { + t.Fatalf("expected 1 key but got %d", len(keys)) + } + if _, ok := keys[0].(*rsa.PrivateKey); !ok { + t.Fatalf("expected rsa key but got %T", keys[0]) + } + }, + }, + "ec key": { + input: keyPemEC, + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 1 { + t.Fatalf("expected 1 key but got %d", len(keys)) + } + if _, ok := keys[0].(*ecdsa.PrivateKey); !ok { + t.Fatalf("expected ecdsa key but got %T", keys[0]) + } + }, + }, + "base64 ec key": { + input: base64.StdEncoding.EncodeToString([]byte(keyPemEC)), + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 1 { + t.Fatalf("expected 1 key but got %d", len(keys)) + } + if _, ok := keys[0].(*ecdsa.PrivateKey); !ok { + t.Fatalf("expected ecdsa key but got %T", keys[0]) + } + }, + }, + "ed key": { + input: keyEd25519, + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 1 { + t.Fatalf("expected 1 key but got %d", len(keys)) + } + if _, ok := keys[0].(ed25519.PrivateKey); !ok { + t.Fatalf("expected ed25519 key but got %T", keys[0]) + } + }, + }, + "base64 ed key": { + input: base64.StdEncoding.EncodeToString([]byte(keyEd25519)), + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 1 { + t.Fatalf("expected 1 key but got %d", len(keys)) + } + if _, ok := keys[0].(ed25519.PrivateKey); !ok { + t.Fatalf("expected ed25519 key but got %T", keys[0]) + } + }, + }, + "other PEM data, no keys": { + input: fmt.Sprintf("%s\n%s\n%s\n", rootCA, intermediateCA, leaf), + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 0 { + t.Fatalf("expected no keys but got %d", len(keys)) + } + }, + }, + "partially valid PEM data": { + input: partiallyValidPEMString, + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 2 { + t.Fatalf("expected 1 key but got %d", len(keys)) + } + if _, ok := keys[0].(ed25519.PrivateKey); !ok { + t.Fatalf("expected ed25519 key but got %T", keys[0]) + } + if _, ok := keys[1].(ed25519.PrivateKey); !ok { + t.Fatalf("expected ed25519 key but got %T", keys[0]) + } + }, + }, + "mixed PEM data": { + input: fmt.Sprintf("%s\n%s\n%s\n%s", rootCA, intermediateCA, leaf, keyPemEC), + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 1 { + t.Fatalf("expected 1 key but got %d", len(keys)) + } + if _, ok := keys[0].(*ecdsa.PrivateKey); !ok { + t.Fatalf("expected ecdsa key but got %T", keys[0]) + } + }, + }, + "mixed PEM data, two keys": { + input: fmt.Sprintf("%s\n%s\n%s\n%s\n%s", rootCA, intermediateCA, leaf, keyPemEC, rsaPrivateKey), + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 2 { + t.Fatalf("expected 2 keys but got %d", len(keys)) + } + if _, ok := keys[0].(*ecdsa.PrivateKey); !ok { + t.Fatalf("expected ecdsa key but got %T", keys[0]) + } + if _, ok := keys[1].(*rsa.PrivateKey); !ok { + t.Fatalf("expected rsa key but got %T", keys[0]) + } + }, + }, + "corrupted key": { + input: `-----BEGIN PRIVATE KEY----- +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +-----END PRIVATE KEY----- +`, + wantErr: "asn1: structure error", + keyCheck: func(t *testing.T, keys []crypto.PrivateKey) { + if len(keys) != 0 { + t.Fatalf("expected no keys but got %d", len(keys)) + } + }, + }, + } + for name, testData := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + keys, err := getPrivateKeysFromPEMData(testData.input) + if testData.wantErr != "" { + if err != nil && !strings.Contains(err.Error(), testData.wantErr) { + t.Fatalf("got error: %v, want error: %v", err, testData.wantErr) + } else if err == nil { + t.Fatalf("expected error: %v", testData.wantErr) + } + } else { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + } + testData.keyCheck(t, keys) + }) + } +} + +func TestParseAndVerifyX509CertsWithOptions(t *testing.T) { + t.Parallel() + + chain := strings.Join([]string{rootCA, intermediateCA, leaf}, "\n") + + parsed, err := getX509CertsFromString(chain) + if err != nil { + t.Fatalf("failed to parse PEM cert chain: %v", err) + } + + dnsName := parsed[2].DNSNames[0] + notBefore, notAfter := parsed[2].NotBefore, parsed[2].NotAfter + invalidBefore := notBefore.Add(-time.Minute).UnixNano() + invalidAfter := notAfter.Add(time.Minute).UnixNano() + // This certificate has "KeyUsageServerAuth", "KeyUsageClientAuth" as key usages. So these are used + // in following tests. + + tests := []struct { + jsonOption *ast.Term + expectErr bool + }{ + { + jsonOption: ast.MustParseTerm(`{"DNSName": "bad.dns.com"}`), + expectErr: true, + }, + { + jsonOption: ast.MustParseTerm(`{"CurrentTime": ` + strconv.FormatInt(invalidBefore, 10) + `}`), + expectErr: true, + }, + { + jsonOption: ast.MustParseTerm(`{"CurrentTime": ` + strconv.FormatInt(invalidAfter, 10) + `}`), + expectErr: true, + }, + { + jsonOption: ast.MustParseTerm(`{"CurrentTime": ` + strconv.FormatInt(notBefore.UnixNano(), 10) + `}`), + expectErr: false, + }, + { + jsonOption: ast.MustParseTerm(`{"DNSName": "` + dnsName + `" }`), + expectErr: false, + }, + { + jsonOption: ast.MustParseTerm(`{"KeyUsages": ["KeyUsageServerAuth", "KeyUsageClientAuth", "KeyUsageCodeSigning"] }`), + expectErr: false, + }, + { + jsonOption: ast.MustParseTerm(`{"KeyUsages": ["KeyUsageCodeSigning"] }`), + expectErr: true, + }, + { + jsonOption: ast.MustParseTerm(`{"DNSName": "` + dnsName + `", "CurrentTime": ` + strconv.FormatInt(notBefore.UnixNano(), 10) + `, "KeyUsages": ["KeyUsageServerAuth", "KeyUsageCodeSigning"] }`), + expectErr: false, + }, + } + + for _, testCase := range tests { + options, _ := builtins.ObjectOperand(testCase.jsonOption.Value, 0) + vo, err := extractVerifyOpts(options) + if err != nil { + t.Fatalf("Unexpected error in extracting options: %s", err) + } + + _, err = verifyX509CertificateChain(parsed, vo) + if testCase.expectErr { + if err == nil { + t.Fatalf("expected error in verifying cert chain, but got nil error") + } + } else { + if err != nil { + t.Fatalf("did not expect error, but got error: %s", err) + } + } + } + +} + +func TestExtractX509VerifyOptions(t *testing.T) { + t.Parallel() + + tests := []struct { + jsonOption *ast.Term + expectErr error + expectVerifyOpt x509.VerifyOptions + }{ + { + jsonOption: ast.MustParseTerm(`{"DNSName": 1}`), + expectErr: errors.New("'DNSName' should be a string"), + }, + { + jsonOption: ast.MustParseTerm(`{CurrentTime: "string"}`), + expectErr: errors.New("'CurrentTime' should be a number"), + }, + { + jsonOption: ast.MustParseTerm(`{MaxConstraintComparisons: "string"}`), + expectErr: errors.New("'MaxConstraintComparisons' should be a number"), + }, + { + jsonOption: ast.MustParseTerm(`{"KeyUsages" : "true"}`), + expectErr: errors.New("'KeyUsages' should be an Array or Set"), + }, + { + jsonOption: ast.MustParseTerm(`{"DNSName": 1, CurrentTime: "string", "KeyUsages" : {1,2}}`), + expectErr: errors.New("'DNSName' should be a string"), + }, + { + jsonOption: ast.MustParseTerm(`{"InvalidKey": "test.com"}`), + expectErr: errors.New("invalid key option"), + }, + { + jsonOption: ast.MustParseTerm(`{}`), + expectVerifyOpt: x509.VerifyOptions{}, + }, + { + jsonOption: ast.MustParseTerm(`{"KeyUsages" : {"KeyUsageAny", "InvalidKeyUsage", "KeyUsageServerAuth"}}`), + expectErr: fmt.Errorf("invalid entries for 'KeyUsages' found: %s", []string{"InvalidKeyUsage"}), + }, + { + jsonOption: ast.MustParseTerm(`{"KeyUsages" : ["1","KeyUsageAny", "InvalidKeyUsage", "KeyUsageServerAuth", "2"]}`), + expectErr: fmt.Errorf("invalid entries for 'KeyUsages' found: %s", []string{"1", "InvalidKeyUsage", "2"}), + }, + { + jsonOption: ast.MustParseTerm(`{"DNSName": "test.com", "CurrentTime": 1708447636000000000, ` + + `"MaxConstraintComparisons": 5, "KeyUsages" : {"KeyUsageAny", "KeyUsageServerAuth","KeyUsageClientAuth"}}`), + expectVerifyOpt: x509.VerifyOptions{ + DNSName: "test.com", + CurrentTime: time.Unix(0, 1708447636000000000), + MaxConstraintComparisions: 5, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny, x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + }, + }, + { + jsonOption: ast.MustParseTerm(`{"DNSName": "test.com", "CurrentTime": 1708447636000000000, ` + + `"MaxConstraintComparisons": 5, "KeyUsages" : {"KeyUsageAny", "KeyUsageAny", 1, 2, "KeyUsageServerAuth","KeyUsageClientAuth"}}`), + expectVerifyOpt: x509.VerifyOptions{ + DNSName: "test.com", + CurrentTime: time.Unix(0, 1708447636000000000), + MaxConstraintComparisions: 5, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny, x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + }, + }, + { // KeyUsages as an array + jsonOption: ast.MustParseTerm(`{"DNSName": "test.com", "CurrentTime": 1708447636000000000, + "MaxConstraintComparisons": 5, + "KeyUsages" : ["KeyUsageAny", "KeyUsageAny", 1, 2, + "KeyUsageServerAuth","KeyUsageClientAuth"]}`), + expectVerifyOpt: x509.VerifyOptions{ + DNSName: "test.com", + CurrentTime: time.Unix(0, 1708447636000000000), + MaxConstraintComparisions: 5, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny, x509.ExtKeyUsageAny, x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + }, + }, + } + + for _, testCase := range tests { + options, _ := builtins.ObjectOperand(testCase.jsonOption.Value, 0) + if testCase.expectErr == nil { + vo, err := extractVerifyOpts(options) + if err != nil { + t.Fatalf("did not expect error but got %s", err) + } + // ignore the order of ExtKeyUsage values + if !cmp.Equal(vo, testCase.expectVerifyOpt, cmpopts.SortSlices( + func(ku1, ku2 x509.ExtKeyUsage) bool { + return ku1 < ku2 + }), cmpopts.IgnoreUnexported(x509.VerifyOptions{})) { + + t.Fatalf("expected x509.VerifyOptions: %+v \n"+ + "got: %+v", testCase.expectVerifyOpt, vo) + } + } else { + _, err := extractVerifyOpts(options) + if err == nil { + t.Fatalf("expected error: %s, got nil error", testCase.expectErr) + } + } + } +} + +// Before/after replacing sprintf("%x", ...) with hex.EncodeToString(...), and using +// util.ByteSliceToString to convert the resulting byte slice: +// BenchmarkMd5-10 3294998 435.2 ns/op 128 B/op 5 allocs/op +// BenchmarkMd5-10 6193455 180.9 ns/op 96 B/op 3 allocs/op +// ... +func BenchmarkMd5(b *testing.B) { + bctx := BuiltinContext{} + operands := []*ast.Term{ast.StringTerm("hello")} + expect := ast.String("5d41402abc4b2a76b9719d911017c592") + iter := func(result *ast.Term) error { + if !expect.Equal(result.Value) { + return fmt.Errorf("unexpected result: %v", result.Value) + } + return nil + } + + b.ResetTimer() + + for range b.N { + err := builtinCryptoMd5(bctx, operands, iter) + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + } +} diff --git a/third_party/opa/v1/topdown/doc.go b/third_party/opa/v1/topdown/doc.go new file mode 100644 index 000000000000..9aa7aa45c5aa --- /dev/null +++ b/third_party/opa/v1/topdown/doc.go @@ -0,0 +1,10 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package topdown provides low-level query evaluation support. +// +// The topdown implementation is a modified version of the standard top-down +// evaluation algorithm used in Datalog. References and comprehensions are +// evaluated eagerly while all other terms are evaluated lazily. +package topdown diff --git a/third_party/opa/v1/topdown/encoding.go b/third_party/opa/v1/topdown/encoding.go new file mode 100644 index 000000000000..541b50d0a96e --- /dev/null +++ b/third_party/opa/v1/topdown/encoding.go @@ -0,0 +1,406 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "net/url" + "strings" + + "sigs.k8s.io/yaml" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/util" +) + +func builtinJSONMarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + asJSON, err := ast.JSON(operands[0].Value) + if err != nil { + return err + } + + bs, err := json.Marshal(asJSON) + if err != nil { + return err + } + + return iter(ast.StringTerm(string(bs))) +} + +func builtinJSONMarshalWithOpts(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + asJSON, err := ast.JSON(operands[0].Value) + if err != nil { + return err + } + + indentWith := "\t" + prefixWith := "" + implicitPrettyPrint := false + userDeclaredExplicitPrettyPrint := false + shouldPrettyPrint := false + + marshalOpts, err := builtins.ObjectOperand(operands[1].Value, 2) + if err != nil { + return err + } + + for idx, k := range marshalOpts.Keys() { + + val := marshalOpts.Get(k) + + key, err := builtins.StringOperand(k.Value, idx) + if err != nil { + return builtins.NewOperandErr(2, "failed to stringify key %v at index %d: %v", k, idx, err) + } + + switch key { + + case "prefix": + prefixOpt, err := builtins.StringOperand(val.Value, idx) + if err != nil { + return builtins.NewOperandErr(2, "key %s failed cast to string: %v", key, err) + } + prefixWith = string(prefixOpt) + implicitPrettyPrint = true + + case "indent": + indentOpt, err := builtins.StringOperand(val.Value, idx) + if err != nil { + return builtins.NewOperandErr(2, "key %s failed cast to string: %v", key, err) + + } + indentWith = string(indentOpt) + implicitPrettyPrint = true + + case "pretty": + userDeclaredExplicitPrettyPrint = true + explicitPrettyPrint, ok := val.Value.(ast.Boolean) + if !ok { + return builtins.NewOperandErr(2, "key %s failed cast to bool", key) + } + + shouldPrettyPrint = bool(explicitPrettyPrint) + + default: + return builtins.NewOperandErr(2, "object contained unknown key %s", key) + } + + } + + if !userDeclaredExplicitPrettyPrint { + shouldPrettyPrint = implicitPrettyPrint + } + + var bs []byte + + if shouldPrettyPrint { + bs, err = json.MarshalIndent(asJSON, prefixWith, indentWith) + } else { + bs, err = json.Marshal(asJSON) + } + + if err != nil { + return err + } + + if shouldPrettyPrint { + // json.MarshalIndent() function will not prefix the first line of emitted JSON + return iter(ast.StringTerm(prefixWith + string(bs))) + } + + return iter(ast.StringTerm(string(bs))) + +} + +func builtinJSONUnmarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + var x any + + if err := util.UnmarshalJSON([]byte(str), &x); err != nil { + return err + } + v, err := ast.InterfaceToValue(x) + if err != nil { + return err + } + return iter(ast.NewTerm(v)) +} + +func builtinJSONIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return iter(ast.InternedTerm(false)) + } + + return iter(ast.InternedTerm(json.Valid([]byte(str)))) +} + +func builtinBase64Encode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + return iter(ast.StringTerm(base64.StdEncoding.EncodeToString([]byte(str)))) +} + +func builtinBase64Decode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + result, err := base64.StdEncoding.DecodeString(string(str)) + if err != nil { + return err + } + return iter(ast.InternedTerm(string(result))) +} + +func builtinBase64IsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return iter(ast.InternedTerm(false)) + } + + _, err = base64.StdEncoding.DecodeString(string(str)) + return iter(ast.InternedTerm(err == nil)) +} + +func builtinBase64UrlEncode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + return iter(ast.StringTerm(base64.URLEncoding.EncodeToString([]byte(str)))) +} + +func builtinBase64UrlEncodeNoPad(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + return iter(ast.StringTerm(base64.RawURLEncoding.EncodeToString([]byte(str)))) +} + +func builtinBase64UrlDecode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + s := string(str) + + // Some base64url encoders omit the padding at the end, so this case + // corrects such representations using the method given in RFC 7515 + // Appendix C: https://tools.ietf.org/html/rfc7515#appendix-C + if !strings.HasSuffix(s, "=") { + switch len(s) % 4 { + case 0: + case 2: + s += "==" + case 3: + s += "=" + default: + return fmt.Errorf("illegal base64url string: %s", s) + } + } + result, err := base64.URLEncoding.DecodeString(s) + if err != nil { + return err + } + return iter(ast.InternedTerm(string(result))) +} + +func builtinURLQueryEncode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + return iter(ast.StringTerm(url.QueryEscape(string(str)))) +} + +func builtinURLQueryDecode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + s, err := url.QueryUnescape(string(str)) + if err != nil { + return err + } + return iter(ast.StringTerm(s)) +} + +var encodeObjectErr = builtins.NewOperandErr(1, "values must be string, array[string], or set[string]") + +func builtinURLQueryEncodeObject(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + asJSON, err := ast.JSON(operands[0].Value) + if err != nil { + return err + } + + inputs, ok := asJSON.(map[string]any) + if !ok { + return builtins.NewOperandTypeErr(1, operands[0].Value, "object") + } + + query := url.Values{} + + for k, v := range inputs { + switch vv := v.(type) { + case string: + query.Set(k, vv) + case []any: + for _, val := range vv { + strVal, ok := val.(string) + if !ok { + return encodeObjectErr + } + query.Add(k, strVal) + } + default: + return encodeObjectErr + } + } + + return iter(ast.StringTerm(query.Encode())) +} + +func builtinURLQueryDecodeObject(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + query, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + queryParams, err := url.ParseQuery(string(query)) + if err != nil { + return err + } + + queryObject := ast.NewObject() + for k, v := range queryParams { + paramsArray := make([]*ast.Term, len(v)) + for i, param := range v { + paramsArray[i] = ast.StringTerm(param) + } + queryObject.Insert(ast.StringTerm(k), ast.ArrayTerm(paramsArray...)) + } + + return iter(ast.NewTerm(queryObject)) +} + +func builtinYAMLMarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + asJSON, err := ast.JSON(operands[0].Value) + if err != nil { + return err + } + + var buf bytes.Buffer + encoder := json.NewEncoder(&buf) + if err := encoder.Encode(asJSON); err != nil { + return err + } + + bs, err := yaml.JSONToYAML(buf.Bytes()) + if err != nil { + return err + } + + return iter(ast.StringTerm(string(bs))) +} + +func builtinYAMLUnmarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + bs, err := yaml.YAMLToJSON([]byte(str)) + if err != nil { + return err + } + + buf := bytes.NewBuffer(bs) + decoder := util.NewJSONDecoder(buf) + var val any + err = decoder.Decode(&val) + if err != nil { + return err + } + v, err := ast.InterfaceToValue(val) + if err != nil { + return err + } + return iter(ast.NewTerm(v)) +} + +func builtinYAMLIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return iter(ast.InternedTerm(false)) + } + + var x any + err = yaml.Unmarshal([]byte(str), &x) + return iter(ast.InternedTerm(err == nil)) +} + +func builtinHexEncode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + return iter(ast.StringTerm(hex.EncodeToString([]byte(str)))) +} + +func builtinHexDecode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + val, err := hex.DecodeString(string(str)) + if err != nil { + return err + } + return iter(ast.StringTerm(string(val))) +} + +func init() { + RegisterBuiltinFunc(ast.JSONMarshal.Name, builtinJSONMarshal) + RegisterBuiltinFunc(ast.JSONMarshalWithOptions.Name, builtinJSONMarshalWithOpts) + RegisterBuiltinFunc(ast.JSONUnmarshal.Name, builtinJSONUnmarshal) + RegisterBuiltinFunc(ast.JSONIsValid.Name, builtinJSONIsValid) + RegisterBuiltinFunc(ast.Base64Encode.Name, builtinBase64Encode) + RegisterBuiltinFunc(ast.Base64Decode.Name, builtinBase64Decode) + RegisterBuiltinFunc(ast.Base64IsValid.Name, builtinBase64IsValid) + RegisterBuiltinFunc(ast.Base64UrlEncode.Name, builtinBase64UrlEncode) + RegisterBuiltinFunc(ast.Base64UrlEncodeNoPad.Name, builtinBase64UrlEncodeNoPad) + RegisterBuiltinFunc(ast.Base64UrlDecode.Name, builtinBase64UrlDecode) + RegisterBuiltinFunc(ast.URLQueryDecode.Name, builtinURLQueryDecode) + RegisterBuiltinFunc(ast.URLQueryEncode.Name, builtinURLQueryEncode) + RegisterBuiltinFunc(ast.URLQueryEncodeObject.Name, builtinURLQueryEncodeObject) + RegisterBuiltinFunc(ast.URLQueryDecodeObject.Name, builtinURLQueryDecodeObject) + RegisterBuiltinFunc(ast.YAMLMarshal.Name, builtinYAMLMarshal) + RegisterBuiltinFunc(ast.YAMLUnmarshal.Name, builtinYAMLUnmarshal) + RegisterBuiltinFunc(ast.YAMLIsValid.Name, builtinYAMLIsValid) + RegisterBuiltinFunc(ast.HexEncode.Name, builtinHexEncode) + RegisterBuiltinFunc(ast.HexDecode.Name, builtinHexDecode) +} diff --git a/third_party/opa/v1/topdown/errors.go b/third_party/opa/v1/topdown/errors.go new file mode 100644 index 000000000000..cadd163198cd --- /dev/null +++ b/third_party/opa/v1/topdown/errors.go @@ -0,0 +1,149 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "errors" + "fmt" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// Halt is a special error type that built-in function implementations return to indicate +// that policy evaluation should stop immediately. +type Halt struct { + Err error +} + +func (h Halt) Error() string { + return h.Err.Error() +} + +func (h Halt) Unwrap() error { return h.Err } + +// Error is the error type returned by the Eval and Query functions when +// an evaluation error occurs. +type Error struct { + Code string `json:"code"` + Message string `json:"message"` + Location *ast.Location `json:"location,omitempty"` + err error `json:"-"` +} + +const ( + + // InternalErr represents an unknown evaluation error. + InternalErr string = "eval_internal_error" + + // CancelErr indicates the evaluation process was cancelled. + CancelErr string = "eval_cancel_error" + + // ConflictErr indicates a conflict was encountered during evaluation. For + // instance, a conflict occurs if a rule produces multiple, differing values + // for the same key in an object. Conflict errors indicate the policy does + // not account for the data loaded into the policy engine. + ConflictErr string = "eval_conflict_error" + + // TypeErr indicates evaluation stopped because an expression was applied to + // a value of an inappropriate type. + TypeErr string = "eval_type_error" + + // BuiltinErr indicates a built-in function received a semantically invalid + // input or encountered some kind of runtime error, e.g., connection + // timeout, connection refused, etc. + BuiltinErr string = "eval_builtin_error" + + // WithMergeErr indicates that the real and replacement data could not be merged. + WithMergeErr string = "eval_with_merge_error" +) + +// IsError returns true if the err is an Error. +func IsError(err error) bool { + var e *Error + return errors.As(err, &e) +} + +// IsCancel returns true if err was caused by cancellation. +func IsCancel(err error) bool { + return errors.Is(err, &Error{Code: CancelErr}) +} + +// Is allows matching topdown errors using errors.Is (see IsCancel). +func (e *Error) Is(target error) bool { + var t *Error + if errors.As(target, &t) { + return (t.Code == "" || e.Code == t.Code) && + (t.Message == "" || e.Message == t.Message) && + (t.Location == nil || t.Location.Compare(e.Location) == 0) + } + return false +} + +func (e *Error) Error() string { + msg := fmt.Sprintf("%v: %v", e.Code, e.Message) + + if e.Location != nil { + msg = e.Location.String() + ": " + msg + } + + return msg +} + +func (e *Error) Wrap(err error) *Error { + e.err = err + return e +} + +func (e *Error) Unwrap() error { + return e.err +} + +func functionConflictErr(loc *ast.Location) error { + return &Error{ + Code: ConflictErr, + Location: loc, + Message: "functions must not produce multiple outputs for same inputs", + } +} + +func completeDocConflictErr(loc *ast.Location) error { + return &Error{ + Code: ConflictErr, + Location: loc, + Message: "complete rules must not produce multiple outputs", + } +} + +func objectDocKeyConflictErr(loc *ast.Location) error { + return &Error{ + Code: ConflictErr, + Location: loc, + Message: "object keys must be unique", + } +} + +func unsupportedBuiltinErr(loc *ast.Location) error { + return &Error{ + Code: InternalErr, + Location: loc, + Message: "unsupported built-in", + } +} + +func mergeConflictErr(loc *ast.Location) error { + return &Error{ + Code: WithMergeErr, + Location: loc, + Message: "real and replacement data could not be merged", + } +} + +func internalErr(loc *ast.Location, msg string) error { + return &Error{ + Code: InternalErr, + Location: loc, + Message: msg, + } +} diff --git a/third_party/opa/v1/topdown/errors_test.go b/third_party/opa/v1/topdown/errors_test.go new file mode 100644 index 000000000000..7607d863b2bb --- /dev/null +++ b/third_party/opa/v1/topdown/errors_test.go @@ -0,0 +1,117 @@ +package topdown_test + +import ( + "errors" + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/ast/location" + "github.com/open-policy-agent/opa/v1/topdown" +) + +func TestErrorWrapping(t *testing.T) { + t.Parallel() + + isHalt := func(err error) bool { + return errors.As(err, &topdown.Halt{}) + } + + builtinErr := errors.New("builtin error") + loc := location.Location{ + File: "b.rego", + Col: 10, + Row: 12, + } + + e0 := (&topdown.Error{Code: topdown.BuiltinErr, + Message: "builtin error", + Location: &loc, + }).Wrap(builtinErr) + + tests := []struct { + note string + err error + check func(error) bool + }{ + { + note: "plain", + err: &topdown.Error{}, + check: topdown.IsError, + }, + { + note: "wrapped", + err: fmt.Errorf("meh: %w", &topdown.Error{}), + check: topdown.IsError, + }, + { + note: "wrapped in Halt", + err: topdown.Halt{Err: &topdown.Error{}}, + check: topdown.IsError, + }, + { + note: "check for Halt", + err: topdown.Halt{Err: &topdown.Error{}}, + check: isHalt, + }, + { + note: "check for Halt, wrapped", + err: fmt.Errorf("meh: %w", topdown.Halt{Err: &topdown.Error{}}), + check: isHalt, + }, + { + note: "plain cancel", + err: &topdown.Error{Code: topdown.CancelErr}, + check: topdown.IsCancel, + }, + { + note: "wrapped cancel", + err: fmt.Errorf("meh: %w", &topdown.Error{Code: topdown.CancelErr}), + check: topdown.IsCancel, + }, + { + note: "wrapped builtin error", + err: e0, + check: func(err error) bool { + return errors.Is(err, builtinErr) + }, + }, + { + note: "matching errors, code", + err: e0, + check: func(err error) bool { + return errors.Is(err, &topdown.Error{Code: topdown.BuiltinErr}) + }, + }, + { + note: "matching errors, code and message", + err: e0, + check: func(err error) bool { + return errors.Is(err, &topdown.Error{Code: topdown.BuiltinErr, Message: "builtin error"}) + }, + }, + { + note: "matching errors, code, message and location", + err: e0, + check: func(err error) bool { + return errors.Is(err, &topdown.Error{Code: topdown.BuiltinErr, Message: "builtin error", Location: &loc}) + }, + }, + { + note: "matching errors, code, message, location and builtin error", + err: e0, + check: func(err error) bool { + return errors.Is(err, e0) + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + if !tc.check(tc.err) { + t.Errorf("unexpected 'false'") + } + }) + } +} diff --git a/third_party/opa/v1/topdown/eval.go b/third_party/opa/v1/topdown/eval.go new file mode 100644 index 000000000000..f0f301e6a798 --- /dev/null +++ b/third_party/opa/v1/topdown/eval.go @@ -0,0 +1,4322 @@ +package topdown + +import ( + "context" + "errors" + "fmt" + "io" + "slices" + "strconv" + "strings" + "sync" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/copypropagation" + "github.com/open-policy-agent/opa/v1/topdown/print" + "github.com/open-policy-agent/opa/v1/tracing" + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +type evalIterator func(*eval) error + +type unifyIterator func() error + +type unifyRefIterator func(pos int) error + +type queryIDFactory struct { + curr uint64 +} + +// Note: The first call to Next() returns 0. +func (f *queryIDFactory) Next() uint64 { + curr := f.curr + f.curr++ + return curr +} + +type builtinErrors struct { + errs []error +} + +// earlyExitError is used to abort iteration where early exit is possible +type earlyExitError struct { + prev error + e *eval +} + +func (ee *earlyExitError) Error() string { + return fmt.Sprintf("%v: early exit", ee.e.query) +} + +type deferredEarlyExitError earlyExitError + +func (ee deferredEarlyExitError) Error() string { + return fmt.Sprintf("%v: deferred early exit", ee.e.query) +} + +// Note(æ): this struct is formatted for optimal alignment as it is big, internal and instantiated +// *very* frequently during evaluation. If you need to add fields here, please consider the alignment +// of the struct, and use something like betteralign (https://github.com/dkorunic/betteralign) if you +// need help with that. +type eval struct { + ctx context.Context + metrics metrics.Metrics + seed io.Reader + cancel Cancel + queryCompiler ast.QueryCompiler + store storage.Store + txn storage.Transaction + virtualCache VirtualCache + baseCache BaseCache + interQueryBuiltinCache cache.InterQueryCache + interQueryBuiltinValueCache cache.InterQueryValueCache + printHook print.Hook + time *ast.Term + queryIDFact *queryIDFactory + parent *eval + caller *eval + bindings *bindings + compiler *ast.Compiler + input *ast.Term + data *ast.Term + external *resolverTrie + targetStack *refStack + traceLastLocation *ast.Location // Last location of a trace event. + instr *Instrumentation + builtins map[string]*Builtin + builtinCache builtins.Cache + ndBuiltinCache builtins.NDBCache + functionMocks *functionMocksStack + comprehensionCache *comprehensionCache + saveSet *saveSet + saveStack *saveStack + saveSupport *saveSupport + saveNamespace *ast.Term + inliningControl *inliningControl + runtime *ast.Term + builtinErrors *builtinErrors + roundTripper CustomizeRoundTripper + genvarprefix string + query ast.Body + tracers []QueryTracer + tracingOpts tracing.Options + queryID uint64 + index int + genvarid int + indexing bool + earlyExit bool + traceEnabled bool + plugTraceVars bool + skipSaveNamespace bool + findOne bool + strictObjects bool + defined bool +} + +type evp struct { + pool sync.Pool +} + +func (ep *evp) Put(e *eval) { + ep.pool.Put(e) +} + +func (ep *evp) Get() *eval { + return ep.pool.Get().(*eval) +} + +var evalPool = evp{ + pool: sync.Pool{ + New: func() any { + return &eval{} + }, + }, +} + +func (e *eval) Run(iter evalIterator) error { + if !e.traceEnabled { + // avoid function literal escaping to heap if we don't need the trace + return e.eval(iter) + } + + e.traceEnter(e.query) + return e.eval(func(e *eval) error { + e.traceExit(e.query) + err := iter(e) + e.traceRedo(e.query) + return err + }) +} + +func (e *eval) String() string { + s := strings.Builder{} + e.string(&s) + return s.String() +} + +func (e *eval) string(s *strings.Builder) { + fmt.Fprintf(s, "') +} + +func (e *eval) builtinFunc(name string) (*ast.Builtin, BuiltinFunc, bool) { + decl, ok := ast.BuiltinMap[name] + if ok { + f, ok := builtinFunctions[name] + if ok { + return decl, f, true + } + } else { + bi, ok := e.builtins[name] + if ok { + return bi.Decl, bi.Func, true + } + } + return nil, nil, false +} + +func (e *eval) closure(query ast.Body, cpy *eval) { + *cpy = *e + cpy.index = 0 + cpy.query = query + cpy.queryID = cpy.queryIDFact.Next() + cpy.parent = e + cpy.findOne = false +} + +func (e *eval) child(query ast.Body, cpy *eval) { + *cpy = *e + cpy.index = 0 + cpy.query = query + cpy.queryID = cpy.queryIDFact.Next() + cpy.bindings = newBindings(cpy.queryID, e.instr) + cpy.parent = e + cpy.findOne = false +} + +func (e *eval) next(iter evalIterator) error { + e.index++ + err := e.evalExpr(iter) + e.index-- + return err +} + +func (e *eval) partial() bool { + return e.saveSet != nil +} + +func (e *eval) unknown(x any, b *bindings) bool { + if !e.partial() { + return false + } + + // If the caller provided an ast.Value directly (e.g., an ast.Ref) wrap + // it as an ast.Term because the saveSet Contains() function expects + // ast.Term. + if v, ok := x.(ast.Value); ok { + x = ast.NewTerm(v) + } + + return saveRequired(e.compiler, e.inliningControl, true, e.saveSet, b, x, false) +} + +// exactly like `unknown` above` but without the cost of `any` boxing when arg is known to be a ref +func (e *eval) unknownRef(ref ast.Ref, b *bindings) bool { + return e.partial() && saveRequired(e.compiler, e.inliningControl, true, e.saveSet, b, ast.NewTerm(ref), false) +} + +func (e *eval) traceEnter(x ast.Node) { + e.traceEvent(EnterOp, x, "", nil) +} + +func (e *eval) traceExit(x ast.Node) { + var msg string + if e.findOne { + msg = "early" + } + e.traceEvent(ExitOp, x, msg, nil) +} + +func (e *eval) traceEval(x ast.Node) { + e.traceEvent(EvalOp, x, "", nil) +} + +func (e *eval) traceDuplicate(x ast.Node) { + e.traceEvent(DuplicateOp, x, "", nil) +} + +func (e *eval) traceFail(x ast.Node) { + e.traceEvent(FailOp, x, "", nil) +} + +func (e *eval) traceRedo(x ast.Node) { + e.traceEvent(RedoOp, x, "", nil) +} + +func (e *eval) traceSave(x ast.Node) { + e.traceEvent(SaveOp, x, "", nil) +} + +func (e *eval) traceIndex(x ast.Node, msg string, target *ast.Ref) { + e.traceEvent(IndexOp, x, msg, target) +} + +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 { + return + } + + var parentID uint64 + if e.parent != nil { + parentID = e.parent.queryID + } + + location := x.Loc() + if location == nil { + location = e.traceLastLocation + } else { + e.traceLastLocation = location + } + + evt := Event{ + QueryID: e.queryID, + ParentID: parentID, + Op: op, + Node: x, + Location: location, + Message: msg, + Ref: target, + input: e.input, + bindings: e.bindings, + } + + // Skip plugging the local variables, unless any of the tracers + // had required it via their configuration. If any required the + // variable bindings then we will plug and give values for all + // tracers. + if e.plugTraceVars { + + 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) + rewritten, _ := e.rewrittenVar(original) + evt.LocalMetadata[original] = VarMetadata{ + Name: rewritten, + Location: k.Loc(), + } + + // For backwards compatibility save a copy of the values too.. + evt.Locals.Put(k.Value, v.Value) + return nil + }) // cannot return error + + ast.WalkTerms(x, func(term *ast.Term) bool { + 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 + }) + } + + for i := range e.tracers { + e.tracers[i].TraceEvent(evt) + } +} + +func (e *eval) eval(iter evalIterator) error { + return e.evalExpr(iter) +} + +func (e *eval) evalExpr(iter evalIterator) error { + wrapErr := func(err error) error { + if !e.findOne { + // The current rule/function doesn't support EE, but a caller (somewhere down the call stack) does. + return &deferredEarlyExitError{prev: err, e: e} + } + return &earlyExitError{prev: err, e: e} + } + + if e.cancel != nil && e.cancel.Cancelled() { + if e.ctx != nil && e.ctx.Err() != nil { + return &Error{ + Code: CancelErr, + Message: e.ctx.Err().Error(), + err: e.ctx.Err(), + } + } + return &Error{ + Code: CancelErr, + Message: "caller cancelled query execution", + } + } + + if e.index >= len(e.query) { + if err := iter(e); err != nil { + switch err := err.(type) { + case *deferredEarlyExitError, *earlyExitError: + return wrapErr(err) + default: + return err + } + } + + if e.findOne && !e.partial() { // we've found one! + return &earlyExitError{e: e} + } + return nil + } + expr := e.query[e.index] + + e.traceEval(expr) + + if len(expr.With) > 0 { + return e.evalWith(iter) + } + + return e.evalStep(func(e *eval) error { + return e.next(iter) + }) +} + +func (e *eval) evalStep(iter evalIterator) error { + expr := e.query[e.index] + + if expr.Negated { + return e.evalNot(iter) + } + + var err error + + // NOTE(æ): the reason why there's one branch for the tracing case and one almost + // identical branch below for when tracing is disabled is that the tracing case + // allocates wildly. These allocations are cause by the "defined" boolean variable + // escaping to the heap as its value is set from inside of closures. There may very + // well be more elegant solutions to this problem, but this is one that works, and + // saves several *million* allocations for some workloads. So feel free to refactor + // this, but do make sure that the common non-tracing case doesn't pay in allocations + // for something that is only needed when tracing is enabled. + if e.traceEnabled { + var defined bool + switch terms := expr.Terms.(type) { + case []*ast.Term: + switch { + case expr.IsEquality(): + err = e.unify(terms[1], terms[2], func() error { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + }) + default: + err = e.evalCall(terms, func() error { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + }) + } + case *ast.Term: + // generateVar inlined here to avoid extra allocations in hot path + rterm := ast.VarTerm(e.fmtVarTerm()) + + if e.partial() { + e.inliningControl.PushDisable(rterm.Value, true) + } + + err = e.unify(terms, rterm, func() error { + if e.saveSet.Contains(rterm, e.bindings) { + return e.saveExpr(ast.NewExpr(rterm), e.bindings, func() error { + return iter(e) + }) + } + if !e.bindings.Plug(rterm).Equal(ast.InternedTerm(false)) { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + } + return nil + }) + + if e.partial() { + e.inliningControl.PopDisable() + } + case *ast.Every: + eval := evalEvery{ + Every: terms, + e: e, + expr: expr, + } + err = eval.eval(func() error { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + }) + + default: // guard-rail for adding extra (Expr).Terms types + return fmt.Errorf("got %T terms: %[1]v", terms) + } + + if err != nil { + return err + } + + if !defined { + e.traceFail(expr) + } + + return nil + } + + switch terms := expr.Terms.(type) { + case []*ast.Term: + switch { + case expr.IsEquality(): + err = e.unify(terms[1], terms[2], func() error { + return iter(e) + }) + default: + err = e.evalCall(terms, func() error { + return iter(e) + }) + } + case *ast.Term: + // generateVar inlined here to avoid extra allocations in hot path + rterm := ast.VarTerm(e.fmtVarTerm()) + err = e.unify(terms, rterm, func() error { + if e.saveSet.Contains(rterm, e.bindings) { + return e.saveExpr(ast.NewExpr(rterm), e.bindings, func() error { + return iter(e) + }) + } + if !e.bindings.Plug(rterm).Equal(ast.InternedTerm(false)) { + return iter(e) + } + return nil + }) + case *ast.Every: + eval := evalEvery{ + Every: terms, + e: e, + expr: expr, + } + err = eval.eval(func() error { + return iter(e) + }) + + default: // guard-rail for adding extra (Expr).Terms types + return fmt.Errorf("got %T terms: %[1]v", terms) + } + + return err +} + +// Single-purpose fmt.Sprintf replacement for generating variable names with only +// one allocation performed instead of 4, and in 1/3 the time. +func (e *eval) fmtVarTerm() string { + buf := make([]byte, 0, len(e.genvarprefix)+util.NumDigitsUint(e.queryID)+util.NumDigitsInt(e.index)+7) + + buf = append(buf, e.genvarprefix...) + buf = append(buf, "_term_"...) + buf = strconv.AppendUint(buf, e.queryID, 10) + buf = append(buf, '_') + buf = strconv.AppendInt(buf, int64(e.index), 10) + + return util.ByteSliceToString(buf) +} + +func (e *eval) evalNot(iter evalIterator) error { + expr := e.query[e.index] + + if e.unknown(expr, e.bindings) { + return e.evalNotPartial(iter) + } + + negation := ast.NewBody(expr.ComplementNoWith()) + child := evalPool.Get() + defer evalPool.Put(child) + + e.closure(negation, child) + + if e.traceEnabled { + child.traceEnter(negation) + } + + if err := child.eval(func(*eval) error { + if e.traceEnabled { + child.traceExit(negation) + child.traceRedo(negation) + } + child.defined = true + + return nil + }); err != nil { + return err + } + + if !child.defined { + return iter(e) + } + + child.defined = false + + e.traceFail(expr) + return nil +} + +func (e *eval) evalWith(iter evalIterator) error { + + expr := e.query[e.index] + + var disable []ast.Ref + + if e.partial() { + // Avoid the `disable` var to escape to heap unless partial evaluation is enabled. + var disablePartial []ast.Ref + // Disable inlining on all references in the expression so the result of + // partial evaluation has the same semantics w/ the with statements + // preserved. + disableRef := func(x ast.Ref) bool { + disablePartial = append(disablePartial, x.GroundPrefix()) + return false + } + + // If the value is unknown the with statement cannot be evaluated and so + // the entire expression should be saved to be safe. In the future this + // could be relaxed in certain cases (e.g., if the with statement would + // have no effect.) + for _, with := range expr.With { + if isFunction(e.compiler.TypeEnv, with.Target) || // non-builtin function replaced + isOtherRef(with.Target) { // built-in replaced + + ast.WalkRefs(with.Value, disableRef) + continue + } + + // with target is data or input (not built-in) + if e.saveSet.ContainsRecursive(with.Value, e.bindings) { + return e.saveExprMarkUnknowns(expr, e.bindings, func() error { + return e.next(iter) + }) + } + ast.WalkRefs(with.Target, disableRef) + ast.WalkRefs(with.Value, disableRef) + } + + ast.WalkRefs(expr.NoWith(), disableRef) + + disable = disablePartial + } + + pairsInput := [][2]*ast.Term{} + pairsData := [][2]*ast.Term{} + targets := make([]ast.Ref, 0, len(expr.With)) + + var functionMocks [][2]*ast.Term + + for i := range expr.With { + target := expr.With[i].Target + plugged := e.bindings.Plug(expr.With[i].Value) + switch { + // NOTE(sr): ordering matters here: isFunction's ref is also covered by isDataRef + case isFunction(e.compiler.TypeEnv, target): + functionMocks = append(functionMocks, [...]*ast.Term{target, plugged}) + + case isInputRef(target): + pairsInput = append(pairsInput, [...]*ast.Term{target, plugged}) + + case isDataRef(target): + pairsData = append(pairsData, [...]*ast.Term{target, plugged}) + + default: // target must be builtin + if _, _, ok := e.builtinFunc(target.String()); ok { + functionMocks = append(functionMocks, [...]*ast.Term{target, plugged}) + continue // don't append to disabled targets below + } + } + targets = append(targets, target.Value.(ast.Ref)) + } + + input, err := mergeTermWithValues(e.input, pairsInput) + if err != nil { + return &Error{ + Code: ConflictErr, + Location: expr.Location, + Message: err.Error(), + } + } + + data, err := mergeTermWithValues(e.data, pairsData) + if err != nil { + return &Error{ + Code: ConflictErr, + Location: expr.Location, + Message: err.Error(), + } + } + + oldInput, oldData := e.evalWithPush(input, data, functionMocks, targets, disable) + + err = e.evalStep(func(e *eval) error { + e.evalWithPop(oldInput, oldData) + err := e.next(iter) + oldInput, oldData = e.evalWithPush(input, data, functionMocks, targets, disable) + return err + }) + + e.evalWithPop(oldInput, oldData) + + return err +} + +func (e *eval) evalWithPush(input, data *ast.Term, functionMocks [][2]*ast.Term, targets, disable []ast.Ref) (*ast.Term, *ast.Term) { + var oldInput *ast.Term + + if input != nil { + oldInput = e.input + e.input = input + } + + var oldData *ast.Term + + if data != nil { + oldData = e.data + e.data = data + } + + if e.comprehensionCache == nil { + e.comprehensionCache = newComprehensionCache() + } + + e.comprehensionCache.Push() + e.virtualCache.Push() + + if e.targetStack == nil { + e.targetStack = newRefStack() + } + + e.targetStack.Push(targets) + e.inliningControl.PushDisable(disable, true) + + if e.functionMocks == nil { + e.functionMocks = newFunctionMocksStack() + } + + e.functionMocks.PutPairs(functionMocks) + + return oldInput, oldData +} + +func (e *eval) evalWithPop(input, data *ast.Term) { + // NOTE(ae) no nil checks here as we assume evalWithPush always called first + e.inliningControl.PopDisable() + e.targetStack.Pop() + e.virtualCache.Pop() + e.comprehensionCache.Pop() + e.functionMocks.PopPairs() + e.data = data + e.input = input +} + +func (e *eval) evalNotPartial(iter evalIterator) error { + // Prepare query normally. + expr := e.query[e.index] + negation := expr.ComplementNoWith() + + child := evalPool.Get() + defer evalPool.Put(child) + + e.closure(ast.NewBody(negation), child) + + // Unknowns is the set of variables that are marked as unknown. The variables + // are namespaced with the query ID that they originate in. This ensures that + // variables across two or more queries are identified uniquely. + // + // NOTE(tsandall): this is greedy in the sense that we only need variable + // dependencies of the negation. + unknowns := e.saveSet.Vars(e.caller.bindings) + + // Run partial evaluation. Since the result may require support, push a new + // query onto the save stack to avoid mutating the current save query. If + // shallow inlining is not enabled, run copy propagation to further simplify + // the result. + var cp *copypropagation.CopyPropagator + + if !e.inliningControl.shallow { + cp = copypropagation.New(unknowns).WithEnsureNonEmptyBody(true).WithCompiler(e.compiler) + } + + var savedQueries []ast.Body + e.saveStack.PushQuery(nil) + + _ = child.eval(func(*eval) error { + query := e.saveStack.Peek() + plugged := query.Plug(e.caller.bindings) + // Skip this rule body if it fails to type-check. + // Type-checking failure means the rule body will never succeed. + if !e.compiler.PassesTypeCheck(plugged) { + return nil + } + if cp != nil { + plugged = applyCopyPropagation(cp, e.instr, plugged) + } + savedQueries = append(savedQueries, plugged) + return nil + }) // cannot return error + + e.saveStack.PopQuery() + + // If partial evaluation produced no results, the expression is always undefined + // so it does not have to be saved. + if len(savedQueries) == 0 { + return iter(e) + } + + // Check if the partial evaluation result can be inlined in this query. If not, + // generate support rules for the result. Depending on the size of the partial + // evaluation result and the contents, it may or may not be inlinable. We treat + // the unknowns as safe because vars in the save set will either be known to + // the caller or made safe by an expression on the save stack. + if !canInlineNegation(unknowns, savedQueries) { + return e.evalNotPartialSupport(child.queryID, expr, unknowns, savedQueries, iter) + } + + // If we can inline the result, we have to generate the cross product of the + // queries. For example: + // + // (A && B) || (C && D) + // + // Becomes: + // + // (!A && !C) || (!A && !D) || (!B && !C) || (!B && !D) + return complementedCartesianProduct(savedQueries, 0, nil, func(q ast.Body) error { + return e.saveInlinedNegatedExprs(q, func() error { + return iter(e) + }) + }) +} + +func (e *eval) evalNotPartialSupport(negationID uint64, expr *ast.Expr, unknowns ast.VarSet, queries []ast.Body, iter evalIterator) error { + + // Prepare support rule head. + supportName := fmt.Sprintf("__not%d_%d_%d__", e.queryID, e.index, negationID) + term := ast.RefTerm(ast.DefaultRootDocument, e.saveNamespace, ast.StringTerm(supportName)) + path := term.Value.(ast.Ref) + head := ast.NewHead(ast.Var(supportName), nil, ast.BooleanTerm(true)) + + bodyVars := ast.NewVarSet() + + for _, q := range queries { + bodyVars.Update(q.Vars(ast.VarVisitorParams{})) + } + + unknowns = unknowns.Intersect(bodyVars) + + // Make rule args. Sort them to ensure order is deterministic. + args := make([]*ast.Term, 0, len(unknowns)) + + for v := range unknowns { + args = append(args, ast.NewTerm(v)) + } + + slices.SortFunc(args, ast.TermValueCompare) + + if len(args) > 0 { + head.Args = args + } + + // Save support rules. + for _, query := range queries { + e.saveSupport.Insert(path, &ast.Rule{ + Head: head, + Body: query, + }) + } + + // Save expression that refers to support rule set. + cpy := expr.CopyWithoutTerms() + + if len(args) > 0 { + terms := make([]*ast.Term, len(args)+1) + terms[0] = term + copy(terms[1:], args) + cpy.Terms = terms + } else { + cpy.Terms = term + } + + return e.saveInlinedNegatedExprs([]*ast.Expr{cpy}, func() error { + return e.next(iter) + }) +} + +func (e *eval) evalCall(terms []*ast.Term, iter unifyIterator) error { + + ref := terms[0].Value.(ast.Ref) + + mock, mocked := e.functionMocks.Get(ref) + if mocked { + if m, ok := mock.Value.(ast.Ref); ok && isFunction(e.compiler.TypeEnv, m) { // builtin or data function + mockCall := append([]*ast.Term{ast.NewTerm(m)}, terms[1:]...) + + e.functionMocks.Push() + err := e.evalCall(mockCall, func() error { + e.functionMocks.Pop() + err := iter() + e.functionMocks.Push() + return err + }) + e.functionMocks.Pop() + return err + } + } + // 'mocked' true now indicates that the replacement is a value: if + // it was a ref to a function, we'd have called that above. + + if ref[0].Equal(ast.DefaultRootDocument) { + if mocked { + f := e.compiler.TypeEnv.Get(ref).(*types.Function) + return e.evalCallValue(f.Arity(), terms, mock, iter) + } + + var ir *ast.IndexResult + var err error + if e.partial() { + ir, err = e.getRules(ref, nil) + } else { + ir, err = e.getRules(ref, terms[1:]) + } + defer ast.IndexResultPool.Put(ir) + if err != nil { + return err + } + + eval := evalFunc{ + e: e, + terms: terms, + ir: ir, + } + return eval.eval(iter) + } + + builtinName := ref.String() + bi, f, ok := e.builtinFunc(builtinName) + if !ok { + return unsupportedBuiltinErr(e.query[e.index].Location) + } + + if mocked { // value replacement of built-in call + return e.evalCallValue(bi.Decl.Arity(), terms, mock, iter) + } + + if e.unknown(e.query[e.index], e.bindings) { + return e.saveCall(bi.Decl.Arity(), terms, iter) + } + + var bctx *BuiltinContext + + // Creating a BuiltinContext is expensive, so only do it if the builtin depends on it. + if bi.NeedsBuiltInContext() { + var parentID uint64 + if e.parent != nil { + parentID = e.parent.queryID + } + + var capabilities *ast.Capabilities + if e.compiler != nil { + capabilities = e.compiler.Capabilities() + } + + bctx = &BuiltinContext{ + Context: e.ctx, + Metrics: e.metrics, + Seed: e.seed, + Time: e.time, + Cancel: e.cancel, + Runtime: e.runtime, + Cache: e.builtinCache, + InterQueryBuiltinCache: e.interQueryBuiltinCache, + InterQueryBuiltinValueCache: e.interQueryBuiltinValueCache, + NDBuiltinCache: e.ndBuiltinCache, + Location: e.query[e.index].Location, + QueryTracers: e.tracers, + TraceEnabled: e.traceEnabled, + QueryID: e.queryID, + ParentID: parentID, + PrintHook: e.printHook, + DistributedTracingOpts: e.tracingOpts, + Capabilities: capabilities, + RoundTripper: e.roundTripper, + } + } + + eval := evalBuiltin{ + e: e, + bi: bi, + bctx: bctx, + f: f, + terms: terms[1:], + } + + return eval.eval(iter) +} + +func (e *eval) evalCallValue(arity int, terms []*ast.Term, mock *ast.Term, iter unifyIterator) error { + switch { + case len(terms) == arity+2: // captured var + return e.unify(terms[len(terms)-1], mock, iter) + + case len(terms) == arity+1: + if !ast.Boolean(false).Equal(mock.Value) { + return iter() + } + return nil + } + panic("unreachable") +} + +func (e *eval) unify(a, b *ast.Term, iter unifyIterator) error { + return e.biunify(a, b, e.bindings, e.bindings, iter) +} + +func (e *eval) biunify(a, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + a, b1 = b1.apply(a) + b, b2 = b2.apply(b) + if e.traceEnabled { + e.traceUnify(a, b) + } + switch vA := a.Value.(type) { + case ast.Var, ast.Ref, *ast.ArrayComprehension, *ast.SetComprehension, *ast.ObjectComprehension: + return e.biunifyValues(a, b, b1, b2, iter) + case ast.Null: + switch b.Value.(type) { + case ast.Var, ast.Null, ast.Ref: + return e.biunifyValues(a, b, b1, b2, iter) + } + case ast.Boolean: + switch b.Value.(type) { + case ast.Var, ast.Boolean, ast.Ref: + return e.biunifyValues(a, b, b1, b2, iter) + } + case ast.Number: + switch b.Value.(type) { + case ast.Var, ast.Number, ast.Ref: + return e.biunifyValues(a, b, b1, b2, iter) + } + case ast.String: + switch b.Value.(type) { + case ast.Var, ast.String, ast.Ref: + return e.biunifyValues(a, b, b1, b2, iter) + } + case *ast.Array: + switch vB := b.Value.(type) { + case ast.Var, ast.Ref, *ast.ArrayComprehension: + return e.biunifyValues(a, b, b1, b2, iter) + case *ast.Array: + return e.biunifyArrays(vA, vB, b1, b2, iter) + } + case ast.Object: + switch vB := b.Value.(type) { + case ast.Var, ast.Ref, *ast.ObjectComprehension: + return e.biunifyValues(a, b, b1, b2, iter) + case ast.Object: + return e.biunifyObjects(vA, vB, b1, b2, iter) + } + case ast.Set: + return e.biunifyValues(a, b, b1, b2, iter) + } + return nil +} + +func (e *eval) biunifyArrays(a, b *ast.Array, b1, b2 *bindings, iter unifyIterator) error { + if a.Len() != b.Len() { + return nil + } + return e.biunifyArraysRec(a, b, b1, b2, iter, 0) +} + +func (e *eval) biunifyArraysRec(a, b *ast.Array, b1, b2 *bindings, iter unifyIterator, idx int) error { + if idx == a.Len() { + return iter() + } + return e.biunify(a.Elem(idx), b.Elem(idx), b1, b2, func() error { + return e.biunifyArraysRec(a, b, b1, b2, iter, idx+1) + }) +} + +func (e *eval) biunifyTerms(a, b []*ast.Term, b1, b2 *bindings, iter unifyIterator) error { + if len(a) != len(b) { + return nil + } + return e.biunifyTermsRec(a, b, b1, b2, iter, 0) +} + +func (e *eval) biunifyTermsRec(a, b []*ast.Term, b1, b2 *bindings, iter unifyIterator, idx int) error { + if idx == len(a) { + return iter() + } + return e.biunify(a[idx], b[idx], b1, b2, func() error { + return e.biunifyTermsRec(a, b, b1, b2, iter, idx+1) + }) +} + +func (e *eval) biunifyObjects(a, b ast.Object, b1, b2 *bindings, iter unifyIterator) error { + if a.Len() != b.Len() { + return nil + } + + // Objects must not contain unbound variables as keys at this point as we + // cannot unify them. Similar to sets, plug both sides before comparing the + // keys and unifying the values. + if nonGroundKeys(a) { + a = plugKeys(a, b1) + } + + if nonGroundKeys(b) { + b = plugKeys(b, b2) + } + + return e.biunifyObjectsRec(a, b, b1, b2, iter, a, a.KeysIterator()) +} + +func (e *eval) biunifyObjectsRec(a, b ast.Object, b1, b2 *bindings, iter unifyIterator, keys ast.Object, oki ast.ObjectKeysIterator) error { + key, more := oki.Next() // Get next key from iterator. + if !more { + return iter() + } + v2 := b.Get(key) + if v2 == nil { + return nil + } + return e.biunify(a.Get(key), v2, b1, b2, func() error { + return e.biunifyObjectsRec(a, b, b1, b2, iter, keys, oki) + }) +} + +func (e *eval) biunifyValues(a, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + // Try to evaluate refs and comprehensions. If partial evaluation is + // enabled, then skip evaluation (and save the expression) if the term is + // in the save set. Currently, comprehensions are not evaluated during + // partial eval. This could be improved in the future. + + var saveA, saveB bool + + if _, ok := a.Value.(ast.Set); ok { + saveA = e.saveSet.ContainsRecursive(a, b1) + } else { + saveA = e.saveSet.Contains(a, b1) + if !saveA { + if _, refA := a.Value.(ast.Ref); refA { + return e.biunifyRef(a, b, b1, b2, iter) + } + } + } + + if _, ok := b.Value.(ast.Set); ok { + saveB = e.saveSet.ContainsRecursive(b, b2) + } else { + saveB = e.saveSet.Contains(b, b2) + if !saveB { + if _, refB := b.Value.(ast.Ref); refB { + return e.biunifyRef(b, a, b2, b1, iter) + } + } + } + + if saveA || saveB { + return e.saveUnify(a, b, b1, b2, iter) + } + + if ast.IsComprehension(a.Value) { + return e.biunifyComprehension(a, b, b1, b2, false, iter) + } else if ast.IsComprehension(b.Value) { + return e.biunifyComprehension(b, a, b2, b1, true, iter) + } + + // Perform standard unification. + _, varA := a.Value.(ast.Var) + _, varB := b.Value.(ast.Var) + + var undo undo + + if varA && varB { + if b1 == b2 && a.Equal(b) { + return iter() + } + b1.bind(a, b, b2, &undo) + err := iter() + undo.Undo() + return err + } else if varA && !varB { + b1.bind(a, b, b2, &undo) + err := iter() + undo.Undo() + return err + } else if varB && !varA { + b2.bind(b, a, b1, &undo) + err := iter() + undo.Undo() + return err + } + + // Sets must not contain unbound variables at this point as we cannot unify + // them. So simply plug both sides (to substitute any bound variables with + // values) and then check for equality. + switch a.Value.(type) { + case ast.Set: + a = b1.Plug(a) + b = b2.Plug(b) + } + + if a.Equal(b) { + return iter() + } + + return nil +} + +func (e *eval) biunifyRef(a, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + + ref := a.Value.(ast.Ref) + + if ref[0].Equal(ast.DefaultRootDocument) { + node := e.compiler.RuleTree.Child(ref[0].Value) + eval := evalTree{ + e: e, + ref: ref, + pos: 1, + plugged: ref.CopyNonGround(), + bindings: b1, + rterm: b, + rbindings: b2, + node: node, + } + return eval.eval(iter) + } + + var term *ast.Term + var termbindings *bindings + + if ref[0].Equal(ast.InputRootDocument) { + term = e.input + termbindings = b1 + } else { + term, termbindings = b1.apply(ref[0]) + if term == ref[0] { + term = nil + } + } + + if term == nil { + return nil + } + + eval := evalTerm{ + e: e, + ref: ref, + pos: 1, + bindings: b1, + term: term, + termbindings: termbindings, + rterm: b, + rbindings: b2, + } + + return eval.eval(iter) +} + +func (e *eval) biunifyComprehension(a, b *ast.Term, b1, b2 *bindings, swap bool, iter unifyIterator) error { + + if e.unknown(a, b1) { + return e.biunifyComprehensionPartial(a, b, b1, b2, swap, iter) + } + + value, err := e.buildComprehensionCache(a) + + if err != nil { + return err + } else if value != nil { + return e.biunify(value, b, b1, b2, iter) + } + + e.instr.counterIncr(evalOpComprehensionCacheMiss) + + switch a := a.Value.(type) { + case *ast.ArrayComprehension: + return e.biunifyComprehensionArray(a, b, b1, b2, iter) + case *ast.SetComprehension: + return e.biunifyComprehensionSet(a, b, b1, b2, iter) + case *ast.ObjectComprehension: + return e.biunifyComprehensionObject(a, b, b1, b2, iter) + } + + return internalErr(e.query[e.index].Location, "illegal comprehension type") +} + +func (e *eval) buildComprehensionCache(a *ast.Term) (*ast.Term, error) { + + index := e.comprehensionIndex(a) + if index == nil { + e.instr.counterIncr(evalOpComprehensionCacheSkip) + return nil, nil + } + + if e.comprehensionCache == nil { + e.comprehensionCache = newComprehensionCache() + } + + cache, ok := e.comprehensionCache.Elem(a) + if !ok { + var err error + switch x := a.Value.(type) { + case *ast.ArrayComprehension: + cache, err = e.buildComprehensionCacheArray(x, index.Keys) + case *ast.SetComprehension: + cache, err = e.buildComprehensionCacheSet(x, index.Keys) + case *ast.ObjectComprehension: + cache, err = e.buildComprehensionCacheObject(x, index.Keys) + default: + err = internalErr(e.query[e.index].Location, "illegal comprehension type") + } + if err != nil { + return nil, err + } + e.comprehensionCache.Set(a, cache) + e.instr.counterIncr(evalOpComprehensionCacheBuild) + } else { + e.instr.counterIncr(evalOpComprehensionCacheHit) + } + + values := make([]*ast.Term, len(index.Keys)) + + for i := range index.Keys { + values[i] = e.bindings.Plug(index.Keys[i]) + } + + return cache.Get(values), nil +} + +func (e *eval) buildComprehensionCacheArray(x *ast.ArrayComprehension, keys []*ast.Term) (*comprehensionCacheElem, error) { + child := evalPool.Get() + defer evalPool.Put(child) + + e.child(x.Body, child) + node := newComprehensionCacheElem() + return node, child.Run(func(child *eval) error { + values := make([]*ast.Term, len(keys)) + for i := range keys { + values[i] = child.bindings.Plug(keys[i]) + } + head := child.bindings.Plug(x.Term) + cached := node.Get(values) + if cached != nil { + cached.Value = cached.Value.(*ast.Array).Append(head) + } else { + node.Put(values, ast.ArrayTerm(head)) + } + return nil + }) +} + +func (e *eval) buildComprehensionCacheSet(x *ast.SetComprehension, keys []*ast.Term) (*comprehensionCacheElem, error) { + child := evalPool.Get() + defer evalPool.Put(child) + + e.child(x.Body, child) + node := newComprehensionCacheElem() + return node, child.Run(func(child *eval) error { + values := make([]*ast.Term, len(keys)) + for i := range keys { + values[i] = child.bindings.Plug(keys[i]) + } + head := child.bindings.Plug(x.Term) + cached := node.Get(values) + if cached != nil { + set := cached.Value.(ast.Set) + set.Add(head) + } else { + node.Put(values, ast.SetTerm(head)) + } + return nil + }) +} + +func (e *eval) buildComprehensionCacheObject(x *ast.ObjectComprehension, keys []*ast.Term) (*comprehensionCacheElem, error) { + child := evalPool.Get() + defer evalPool.Put(child) + + e.child(x.Body, child) + node := newComprehensionCacheElem() + return node, child.Run(func(child *eval) error { + values := make([]*ast.Term, len(keys)) + for i := range keys { + values[i] = child.bindings.Plug(keys[i]) + } + headKey := child.bindings.Plug(x.Key) + headValue := child.bindings.Plug(x.Value) + cached := node.Get(values) + if cached != nil { + obj := cached.Value.(ast.Object) + obj.Insert(headKey, headValue) + } else { + node.Put(values, ast.ObjectTerm(ast.Item(headKey, headValue))) + } + return nil + }) +} + +func (e *eval) biunifyComprehensionPartial(a, b *ast.Term, b1, b2 *bindings, swap bool, iter unifyIterator) error { + var err error + cpyA, err := e.amendComprehension(a, b1) + if err != nil { + return err + } + + if ast.IsComprehension(b.Value) { + b, err = e.amendComprehension(b, b2) + if err != nil { + return err + } + } + + // The other term might need to be plugged so include the bindings. The + // bindings for the comprehension term are saved (for compatibility) but + // the eventual plug operation on the comprehension will be a no-op. + if !swap { + return e.saveUnify(cpyA, b, b1, b2, iter) + } + + return e.saveUnify(b, cpyA, b2, b1, iter) +} + +// amendComprehension captures bindings available to the comprehension, +// and used within its term or body. +func (e *eval) amendComprehension(a *ast.Term, b1 *bindings) (*ast.Term, error) { + cpyA := a.Copy() + + // Namespace the variables in the body to avoid collision when the final + // queries returned by partial evaluation. + var body *ast.Body + + switch a := cpyA.Value.(type) { + case *ast.ArrayComprehension: + body = &a.Body + case *ast.SetComprehension: + body = &a.Body + case *ast.ObjectComprehension: + body = &a.Body + default: + return nil, fmt.Errorf("illegal comprehension %T", a) + } + + vars := a.Vars() + err := b1.Iter(e.caller.bindings, func(k, v *ast.Term) error { + if vars.Contains(k.Value.(ast.Var)) { + body.Append(ast.Equality.Expr(k, v)) + } + return nil + }) + if err != nil { + return nil, err + } + + b1.Namespace(cpyA, e.caller.bindings) + return cpyA, nil +} + +func (e *eval) biunifyComprehensionArray(x *ast.ArrayComprehension, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + result := ast.NewArray() + child := evalPool.Get() + + e.closure(x.Body, child) + defer evalPool.Put(child) + + err := child.Run(func(child *eval) error { + result = result.Append(child.bindings.Plug(x.Term)) + return nil + }) + if err != nil { + return err + } + return e.biunify(ast.NewTerm(result), b, b1, b2, iter) +} + +func (e *eval) biunifyComprehensionSet(x *ast.SetComprehension, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + result := ast.NewSet() + child := evalPool.Get() + + e.closure(x.Body, child) + defer evalPool.Put(child) + + err := child.Run(func(child *eval) error { + result.Add(child.bindings.Plug(x.Term)) + return nil + }) + if err != nil { + return err + } + return e.biunify(ast.NewTerm(result), b, b1, b2, iter) +} + +func (e *eval) biunifyComprehensionObject(x *ast.ObjectComprehension, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + child := evalPool.Get() + defer evalPool.Put(child) + + e.closure(x.Body, child) + + result := ast.NewObject() + + err := child.Run(func(child *eval) error { + key := child.bindings.Plug(x.Key) + value := child.bindings.Plug(x.Value) + exist := result.Get(key) + if exist != nil && !exist.Equal(value) { + return objectDocKeyConflictErr(x.Key.Location) + } + result.Insert(key, value) + return nil + }) + if err != nil { + return err + } + return e.biunify(ast.NewTerm(result), b, b1, b2, iter) +} + +func (e *eval) saveExpr(expr *ast.Expr, b *bindings, iter unifyIterator) error { + e.updateFromQuery(expr) + e.saveStack.Push(expr, b, b) + e.traceSave(expr) + err := iter() + e.saveStack.Pop() + return err +} + +func (e *eval) saveExprMarkUnknowns(expr *ast.Expr, b *bindings, iter unifyIterator) error { + e.updateFromQuery(expr) + declArgsLen, err := e.getDeclArgsLen(expr) + if err != nil { + return err + } + var pops int + if pairs := getSavePairsFromExpr(declArgsLen, expr, b, nil); len(pairs) > 0 { + pops += len(pairs) + for _, p := range pairs { + e.saveSet.Push([]*ast.Term{p.term}, p.b) + } + } + e.saveStack.Push(expr, b, b) + e.traceSave(expr) + err = iter() + e.saveStack.Pop() + for range pops { + e.saveSet.Pop() + } + return err +} + +func (e *eval) saveUnify(a, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + e.instr.startTimer(partialOpSaveUnify) + expr := ast.Equality.Expr(a, b) + e.updateFromQuery(expr) + pops := 0 + if pairs := getSavePairsFromTerm(a, b1, nil); len(pairs) > 0 { + pops += len(pairs) + for _, p := range pairs { + e.saveSet.Push([]*ast.Term{p.term}, p.b) + } + + } + if pairs := getSavePairsFromTerm(b, b2, nil); len(pairs) > 0 { + pops += len(pairs) + for _, p := range pairs { + e.saveSet.Push([]*ast.Term{p.term}, p.b) + } + } + e.saveStack.Push(expr, b1, b2) + e.traceSave(expr) + e.instr.stopTimer(partialOpSaveUnify) + err := iter() + + e.saveStack.Pop() + for range pops { + e.saveSet.Pop() + } + + return err +} + +func (e *eval) saveCall(declArgsLen int, terms []*ast.Term, iter unifyIterator) error { + expr := ast.NewExpr(terms) + e.updateFromQuery(expr) + + // If call-site includes output value then partial eval must add vars in output + // position to the save set. + pops := 0 + if declArgsLen == len(terms)-2 { + if pairs := getSavePairsFromTerm(terms[len(terms)-1], e.bindings, nil); len(pairs) > 0 { + pops += len(pairs) + for _, p := range pairs { + e.saveSet.Push([]*ast.Term{p.term}, p.b) + } + } + } + e.saveStack.Push(expr, e.bindings, nil) + e.traceSave(expr) + err := iter() + + e.saveStack.Pop() + for range pops { + e.saveSet.Pop() + } + return err +} + +func (e *eval) saveInlinedNegatedExprs(exprs []*ast.Expr, iter unifyIterator) error { + + with := make([]*ast.With, len(e.query[e.index].With)) + + for i := range e.query[e.index].With { + cpy := e.query[e.index].With[i].Copy() + cpy.Value = e.bindings.PlugNamespaced(cpy.Value, e.caller.bindings) + with[i] = cpy + } + + for _, expr := range exprs { + expr.With = e.updateSavedMocks(with) + e.saveStack.Push(expr, nil, nil) + e.traceSave(expr) + } + err := iter() + for range exprs { + e.saveStack.Pop() + } + return err +} + +func (e *eval) getRules(ref ast.Ref, args []*ast.Term) (*ast.IndexResult, error) { + e.instr.startTimer(evalOpRuleIndex) + defer e.instr.stopTimer(evalOpRuleIndex) + + index := e.compiler.RuleIndex(ref) + if index == nil { + return nil, nil + } + + resolver := resolverPool.Get().(*evalResolver) + defer func() { + resolver.e = nil + resolver.args = nil + resolverPool.Put(resolver) + }() + + var result *ast.IndexResult + var err error + if e.indexing { + resolver.e = e + resolver.args = args + result, err = index.Lookup(resolver) + } else { + resolver.e = e + result, err = index.AllRules(resolver) + } + if err != nil { + return nil, err + } + + result.EarlyExit = result.EarlyExit && e.earlyExit + + if e.traceEnabled { + var msg strings.Builder + if len(result.Rules) == 1 { + msg.WriteString("(matched 1 rule") + } else { + msg.Grow(len("(matched NNNN rules)")) + msg.WriteString("(matched ") + msg.WriteString(strconv.Itoa(len(result.Rules))) + msg.WriteString(" rules") + } + if result.EarlyExit { + msg.WriteString(", early exit") + } + msg.WriteRune(')') + + // Copy ref here as ref otherwise always escapes to the heap, + // whether tracing is enabled or not. + r := ref.Copy() + e.traceIndex(e.query[e.index], msg.String(), &r) + } + + return result, err +} + +func (e *eval) Resolve(ref ast.Ref) (ast.Value, error) { + return (&evalResolver{e: e}).Resolve(ref) +} + +type evalResolver struct { + e *eval + args []*ast.Term +} + +var ( + resolverPool = sync.Pool{ + New: func() any { + return &evalResolver{} + }, + } +) + +func (e *evalResolver) Resolve(ref ast.Ref) (ast.Value, error) { + e.e.instr.startTimer(evalOpResolve) + + // NOTE(ae): nil check on saveSet to avoid ast.NewTerm allocation when not needed + if e.e.inliningControl.Disabled(ref, true) || (e.e.saveSet != nil && + e.e.saveSet.Contains(ast.NewTerm(ref), nil)) { + e.e.instr.stopTimer(evalOpResolve) + return nil, ast.UnknownValueErr{} + } + + // Lookup of function argument values works by using the `args` ref[0], + // where the ast.Number in ref[1] references the function argument of + // that number. The callsite-local arguments are passed in e.args, + // indexed by argument index. + if ref[0].Equal(ast.FunctionArgRootDocument) { + v, ok := ref[1].Value.(ast.Number) + if ok { + i, ok := v.Int() + if ok && i >= 0 && i < len(e.args) { + e.e.instr.stopTimer(evalOpResolve) + plugged := e.e.bindings.PlugNamespaced(e.args[i], e.e.caller.bindings) + return plugged.Value, nil + } + } + e.e.instr.stopTimer(evalOpResolve) + return nil, ast.UnknownValueErr{} + } + + if ref[0].Equal(ast.InputRootDocument) { + if e.e.input != nil { + v, err := e.e.input.Value.Find(ref[1:]) + if err != nil { + v = nil + } + e.e.instr.stopTimer(evalOpResolve) + return v, nil + } + e.e.instr.stopTimer(evalOpResolve) + return nil, nil + } + + if ref[0].Equal(ast.DefaultRootDocument) { + + var repValue ast.Value + + if e.e.data != nil { + if v, err := e.e.data.Value.Find(ref[1:]); err == nil { + repValue = v + } + } + + if e.e.targetStack.Prefixed(ref) { + e.e.instr.stopTimer(evalOpResolve) + return repValue, nil + } + + var merged ast.Value + var err error + + // Converting large JSON values into AST values can be fairly expensive. For + // example, a 2MB JSON value can take upwards of 30 millisceonds to convert. + // We cache the result of conversion here in case the same base document is + // being read multiple times during evaluation. + realValue := e.e.baseCache.Get(ref) + if realValue != nil { + e.e.instr.counterIncr(evalOpBaseCacheHit) + if repValue == nil { + e.e.instr.stopTimer(evalOpResolve) + return realValue, nil + } + var ok bool + merged, ok = merge(repValue, realValue) + if !ok { + err = mergeConflictErr(ref[0].Location) + } + } else { // baseCache miss + e.e.instr.counterIncr(evalOpBaseCacheMiss) + merged, err = e.e.resolveReadFromStorage(ref, repValue) + } + e.e.instr.stopTimer(evalOpResolve) + return merged, err + } + e.e.instr.stopTimer(evalOpResolve) + return nil, errors.New("illegal ref") +} + +func (e *eval) resolveReadFromStorage(ref ast.Ref, a ast.Value) (ast.Value, error) { + if refContainsNonScalar(ref) { + return a, nil + } + + v, err := e.external.Resolve(e, ref) + if err != nil { + return nil, err + } + if v == nil { + path, err := storage.NewPathForRef(ref) + if err != nil { + if !storage.IsNotFound(err) { + return nil, err + } + return a, nil + } + + blob, err := e.store.Read(e.ctx, e.txn, path) + if err != nil { + if !storage.IsNotFound(err) { + return nil, err + } + return a, nil + } + + if len(path) == 0 { + switch obj := blob.(type) { + case map[string]any: + if len(obj) > 0 { + cpy := make(map[string]any, len(obj)-1) + for k, v := range obj { + if string(ast.SystemDocumentKey) != k { + cpy[k] = v + } + } + blob = cpy + } + case ast.Object: + if obj.Len() > 0 { + blob, _ = obj.Map(systemDocumentKeyRemoveMapper) + } + } + } + + switch blob := blob.(type) { + case ast.Value: + v = blob + default: + if blob, ok := blob.(map[string]any); ok && !e.strictObjects { + v = ast.LazyObject(blob) + break + } + v, err = ast.InterfaceToValue(blob) + if err != nil { + return nil, err + } + } + } + + e.baseCache.Put(ref, v) + if a == nil { + return v, nil + } + + merged, ok := merge(a, v) + if !ok { + return nil, mergeConflictErr(ref[0].Location) + } + + return merged, nil +} + +func systemDocumentKeyRemoveMapper(k, v *ast.Term) (*ast.Term, *ast.Term, error) { + if ast.SystemDocumentKey.Equal(k.Value) { + return nil, nil, nil + } + return k, v, nil +} + +func (e *eval) generateVar(suffix string) *ast.Term { + buf := make([]byte, 0, len(e.genvarprefix)+len(suffix)+1) + + buf = append(buf, e.genvarprefix...) + buf = append(buf, '_') + buf = append(buf, suffix...) + + return ast.VarTerm(util.ByteSliceToString(buf)) +} + +func (e *eval) rewrittenVar(v ast.Var) (ast.Var, bool) { + if e.compiler != nil { + if rw, ok := e.compiler.RewrittenVars[v]; ok { + return rw, true + } + } + if e.queryCompiler != nil { + if rw, ok := e.queryCompiler.RewrittenVars()[v]; ok { + return rw, true + } + } + return v, false +} + +func (e *eval) getDeclArgsLen(x *ast.Expr) (int, error) { + + if !x.IsCall() { + return -1, nil + } + + operator := x.Operator() + bi, _, ok := e.builtinFunc(operator.String()) + + if ok { + return bi.Decl.Arity(), nil + } + + ir, err := e.getRules(operator, nil) + defer ast.IndexResultPool.Put(ir) + if err != nil { + return -1, err + } else if ir == nil || ir.Empty() { + return -1, nil + } + + return len(ir.Rules[0].Head.Args), nil +} + +// updateFromQuery enriches the passed expression with Location and With +// fields of the currently looked-at query item (`e.query[e.index]`). +// With values are namespaced to ensure that replacement functions of +// mocked built-ins are properly referenced in the support module. +func (e *eval) updateFromQuery(expr *ast.Expr) { + expr.With = e.updateSavedMocks(e.query[e.index].With) + expr.Location = e.query[e.index].Location +} + +type evalBuiltin struct { + e *eval + bi *ast.Builtin + bctx *BuiltinContext + f BuiltinFunc + terms []*ast.Term +} + +// Is this builtin non-deterministic, and did the caller provide an NDBCache? +func (e *evalBuiltin) canUseNDBCache(bi *ast.Builtin) bool { + return bi.Nondeterministic && e.bctx != nil && e.bctx.NDBuiltinCache != nil +} + +func (e *evalBuiltin) eval(iter unifyIterator) error { + + operands := make([]*ast.Term, len(e.terms)) + + for i := range e.terms { + operands[i] = e.e.bindings.Plug(e.terms[i]) + } + + numDeclArgs := e.bi.Decl.Arity() + + e.e.instr.startTimer(evalOpBuiltinCall) + + // NOTE(philipc): We sometimes have to drop the very last term off + // the args list for cases where a builtin's result is used/assigned, + // because the last term will be a generated term, not an actual + // argument to the builtin. + endIndex := len(operands) + if len(operands) > numDeclArgs { + endIndex-- + } + + // We skip evaluation of the builtin entirely if the NDBCache is + // present, and we have a non-deterministic builtin already cached. + if e.canUseNDBCache(e.bi) { + e.e.instr.stopTimer(evalOpBuiltinCall) + + // Unify against the NDBCache result if present. + if v, ok := e.bctx.NDBuiltinCache.Get(e.bi.Name, ast.NewArray(operands[:endIndex]...)); ok { + switch { + case e.bi.Decl.Result() == nil: + return iter() + case len(operands) == numDeclArgs: + if ast.Boolean(false).Equal(v) { + return nil // nothing to do + } + return iter() + default: + return e.e.unify(e.terms[endIndex], ast.NewTerm(v), iter) + } + } + + // Otherwise, we'll need to go through the normal unify flow. + e.e.instr.startTimer(evalOpBuiltinCall) + } + + var bctx BuiltinContext + if e.bctx == nil { + bctx = BuiltinContext{ + // Location potentially needed for error reporting. + Location: e.e.query[e.e.index].Location, + } + } else { + bctx = *e.bctx + } + + // Normal unification flow for builtins: + err := e.f(bctx, operands, func(output *ast.Term) error { + + e.e.instr.stopTimer(evalOpBuiltinCall) + + var err error + + switch { + case e.bi.Decl.Result() == nil: + err = iter() + case len(operands) == numDeclArgs: + if !ast.Boolean(false).Equal(output.Value) { + err = iter() + } // else: nothing to do, don't iter() + default: + err = e.e.unify(e.terms[endIndex], output, iter) + } + + // If the NDBCache is present, we can assume this builtin + // call was not cached earlier. + if e.canUseNDBCache(e.bi) { + // Populate the NDBCache from the output term. + e.bctx.NDBuiltinCache.Put(e.bi.Name, ast.NewArray(operands[:endIndex]...), output.Value) + } + + if err != nil { + // NOTE(sr): We wrap the errors here into Halt{} because we don't want to + // record them into builtinErrors below. The errors set here are coming from + // the call to iter(), not from the builtin implementation. + err = Halt{Err: err} + } + + e.e.instr.startTimer(evalOpBuiltinCall) + return err + }) + + if err != nil { + if t, ok := err.(Halt); ok { + err = t.Err + } else { + e.e.builtinErrors.errs = append(e.e.builtinErrors.errs, err) + err = nil + } + } + + e.e.instr.stopTimer(evalOpBuiltinCall) + return err +} + +type evalFunc struct { + e *eval + ir *ast.IndexResult + terms []*ast.Term +} + +func (e evalFunc) eval(iter unifyIterator) error { + + if e.ir.Empty() { + return nil + } + + var argCount int + if len(e.ir.Rules) > 0 { + argCount = len(e.ir.Rules[0].Head.Args) + } else if e.ir.Default != nil { + argCount = len(e.ir.Default.Head.Args) + } + + if len(e.ir.Else) > 0 && e.e.unknown(e.e.query[e.e.index], e.e.bindings) { + // Partial evaluation of ordered rules is not supported currently. Save the + // expression and continue. This could be revisited in the future. + return e.e.saveCall(argCount, e.terms, iter) + } + + if e.e.partial() { + var mustGenerateSupport bool + + if defRule := e.ir.Default; defRule != nil { + // The presence of a default func might force us to generate support + if len(defRule.Head.Args) == len(e.terms)-1 { + // The function is called without collecting the result in an output term, + // therefore any successful evaluation of the function is of interest, including the default value ... + if ret := defRule.Head.Value; ret == nil || !ret.Equal(ast.InternedTerm(false)) { + // ... unless the default value is false, + mustGenerateSupport = true + } + } else { + // The function is called with an output term, therefore any successful evaluation of the function is of interest. + // NOTE: Because of how the compiler rewrites function calls, we can't know if the result value is compared + // to a constant value, so we can't be as clever as we are for rules. + mustGenerateSupport = true + } + } + + ref := e.terms[0].Value.(ast.Ref) + + if mustGenerateSupport || e.e.inliningControl.shallow || e.e.inliningControl.Disabled(ref, false) { + // check if the function definitions, or any of the arguments + // contain something unknown + unknown := e.e.unknownRef(ref, e.e.bindings) + for i := 1; !unknown && i <= argCount; i++ { + unknown = e.e.unknown(e.terms[i], e.e.bindings) + } + if unknown { + return e.partialEvalSupport(argCount, iter) + } + } + } + + return e.evalValue(iter, argCount, e.ir.EarlyExit) +} + +func (e evalFunc) evalValue(iter unifyIterator, argCount int, findOne bool) error { + var cacheKey ast.Ref + if !e.e.partial() { + var hit bool + var err error + cacheKey, hit, err = e.evalCache(argCount, iter) + if err != nil { + return err + } else if hit { + return nil + } + } + + // NOTE(anders): While it makes the code a bit more complex, reusing the + // args slice across each function increment saves a lot of resources + // compared to creating a new one inside each call to evalOneRule... so + // think twice before simplifying this :) + args := make([]*ast.Term, len(e.terms)-1) + + var prev *ast.Term + + return withSuppressEarlyExit(func() error { + var outerEe *deferredEarlyExitError + for _, rule := range e.ir.Rules { + copy(args, rule.Head.Args) + if len(args) == len(rule.Head.Args)+1 { + args[len(args)-1] = rule.Head.Value + } + + next, err := e.evalOneRule(iter, rule, args, cacheKey, prev, findOne) + if err != nil { + if oee, ok := err.(*deferredEarlyExitError); ok { + if outerEe == nil { + outerEe = oee + } + } else { + return err + } + } + if next == nil { + for _, erule := range e.ir.Else[rule] { + copy(args, erule.Head.Args) + if len(args) == len(erule.Head.Args)+1 { + args[len(args)-1] = erule.Head.Value + } + + next, err = e.evalOneRule(iter, erule, args, cacheKey, prev, findOne) + if err != nil { + if oee, ok := err.(*deferredEarlyExitError); ok { + if outerEe == nil { + outerEe = oee + } + } else { + return err + } + } + if next != nil { + break + } + } + } + if next != nil { + prev = next + } + } + + if e.ir.Default != nil && prev == nil { + copy(args, e.ir.Default.Head.Args) + if len(args) == len(e.ir.Default.Head.Args)+1 { + args[len(args)-1] = e.ir.Default.Head.Value + } + + _, err := e.evalOneRule(iter, e.ir.Default, args, cacheKey, prev, findOne) + + return err + } + + if outerEe != nil { + return outerEe + } + + return nil + }) +} + +func (e evalFunc) evalCache(argCount int, iter unifyIterator) (ast.Ref, bool, error) { + plen := len(e.terms) + if plen == argCount+2 { // func name + output = 2 + plen -= 1 + } + + cacheKey := make([]*ast.Term, plen) + for i := range plen { + if e.terms[i].IsGround() { + // Avoid expensive copying of ref if it is ground. + cacheKey[i] = e.terms[i] + } else { + cacheKey[i] = e.e.bindings.Plug(e.terms[i]) + } + } + + cached, _ := e.e.virtualCache.Get(cacheKey) + if cached != nil { + e.e.instr.counterIncr(evalOpVirtualCacheHit) + if argCount == len(e.terms)-1 { // f(x) + if ast.Boolean(false).Equal(cached.Value) { + return nil, true, nil + } + return nil, true, iter() + } + // f(x, y), y captured output value + return nil, true, e.e.unify(e.terms[len(e.terms)-1] /* y */, cached, iter) + } + e.e.instr.counterIncr(evalOpVirtualCacheMiss) + return cacheKey, false, nil +} + +func (e evalFunc) evalOneRule(iter unifyIterator, rule *ast.Rule, args []*ast.Term, cacheKey ast.Ref, prev *ast.Term, findOne bool) (*ast.Term, error) { + child := evalPool.Get() + defer evalPool.Put(child) + + e.e.child(rule.Body, child) + child.findOne = findOne + + var result *ast.Term + + child.traceEnter(rule) + + err := child.biunifyTerms(e.terms[1:], args, e.e.bindings, child.bindings, func() error { + return child.eval(func(child *eval) error { + child.traceExit(rule) + + // Partial evaluation must save an expression that tests the output value if the output value + // was not captured to handle the case where the output value may be `false`. + if len(rule.Head.Args) == len(e.terms)-1 && e.e.saveSet.Contains(rule.Head.Value, child.bindings) { + err := e.e.saveExpr(ast.NewExpr(rule.Head.Value), child.bindings, iter) + child.traceRedo(rule) + return err + } + + result = child.bindings.Plug(rule.Head.Value) + if cacheKey != nil { + e.e.virtualCache.Put(cacheKey, result) // the redos confirm this, or the evaluation is aborted + } + + if len(rule.Head.Args) == len(e.terms)-1 && ast.Boolean(false).Equal(result.Value) { + if prev != nil && !prev.Equal(result) { + return functionConflictErr(rule.Location) + } + prev = result + return nil + } + + // Partial evaluation should explore all rules and may not produce + // a ground result so we do not perform conflict detection or + // deduplication. See "ignore conflicts: functions" test case for + // an example. + if !e.e.partial() && prev != nil { + if !prev.Equal(result) { + return functionConflictErr(rule.Location) + } + child.traceRedo(rule) + return nil + } + + prev = result + + if err := iter(); err != nil { + return err + } + + child.traceRedo(rule) + return nil + }) + }) + + return result, err +} + +func (e evalFunc) partialEvalSupport(declArgsLen int, iter unifyIterator) error { + path := e.e.namespaceRef(e.terms[0].Value.(ast.Ref)) + + if !e.e.saveSupport.Exists(path) { + for _, rule := range e.ir.Rules { + err := e.partialEvalSupportRule(rule, path) + if err != nil { + return err + } + } + + if e.ir.Default != nil { + err := e.partialEvalSupportRule(e.ir.Default, path) + if err != nil { + return err + } + } + } + + if !e.e.saveSupport.Exists(path) { // we haven't saved anything, nothing to call + return nil + } + + term := ast.NewTerm(path) + + return e.e.saveCall(declArgsLen, append([]*ast.Term{term}, e.terms[1:]...), iter) +} + +func (e evalFunc) partialEvalSupportRule(rule *ast.Rule, path ast.Ref) error { + child := evalPool.Get() + defer evalPool.Put(child) + + e.e.child(rule.Body, child) + child.traceEnter(rule) + + e.e.saveStack.PushQuery(nil) + + // treat the function arguments as unknown during rule body evaluation + var args []*ast.Term + ast.WalkVars(rule.Head.Args, func(v ast.Var) bool { + args = append(args, ast.VarTerm(string(v))) + return false + }) + e.e.saveSet.Push(args, child.bindings) + + err := child.eval(func(child *eval) error { + child.traceExit(rule) + + current := e.e.saveStack.PopQuery() + plugged := current.Plug(e.e.caller.bindings) + + // Skip this rule body if it fails to type-check. + // Type-checking failure means the rule body will never succeed. + if e.e.compiler.PassesTypeCheck(plugged) { + head := &ast.Head{ + Name: rule.Head.Name, + Reference: rule.Head.Reference, + Value: child.bindings.PlugNamespaced(rule.Head.Value, e.e.caller.bindings), + Args: make([]*ast.Term, len(rule.Head.Args)), + } + for i, a := range rule.Head.Args { + head.Args[i] = child.bindings.PlugNamespaced(a, e.e.caller.bindings) + } + + e.e.saveSupport.Insert(path, &ast.Rule{ + Head: head, + Body: plugged, + Default: rule.Default, + }) + } + child.traceRedo(rule) + e.e.saveStack.PushQuery(current) + return nil + }) + + e.e.saveSet.Pop() + e.e.saveStack.PopQuery() + return err +} + +type deferredEarlyExitContainer struct { + deferred *deferredEarlyExitError +} + +func (dc *deferredEarlyExitContainer) handleErr(err error) error { + if err == nil { + return nil + } + + if dc.deferred == nil && errors.As(err, &dc.deferred) && dc.deferred != nil { + return nil + } + + return err +} + +// copyError returns a copy of the deferred early exit error if one is present. +// This exists only to allow the container to be reused. +func (dc *deferredEarlyExitContainer) copyError() *deferredEarlyExitError { + if dc.deferred == nil { + return nil + } + + cpy := *dc.deferred + return &cpy +} + +var deecPool = sync.Pool{ + New: func() any { + return &deferredEarlyExitContainer{} + }, +} + +type evalTree struct { + e *eval + bindings *bindings + rterm *ast.Term + rbindings *bindings + node *ast.TreeNode + ref ast.Ref + plugged ast.Ref + pos int +} + +func (e evalTree) eval(iter unifyIterator) error { + + if len(e.ref) == e.pos { + return e.finish(iter) + } + + plugged := e.bindings.Plug(e.ref[e.pos]) + + if plugged.IsGround() { + return e.next(iter, plugged) + } + + return e.enumerate(iter) +} + +func (e evalTree) finish(iter unifyIterator) error { + + // In some cases, it may not be possible to PE the ref. If the path refers + // to virtual docs that PE does not support or base documents where inlining + // has been disabled, then we have to save. + if e.e.partial() && e.e.unknownRef(e.plugged, e.e.bindings) { + return e.e.saveUnify(ast.NewTerm(e.plugged), e.rterm, e.bindings, e.rbindings, iter) + } + + v, err := e.extent() + if err != nil || v == nil { + return err + } + + return e.e.biunify(e.rterm, v, e.rbindings, e.bindings, iter) +} + +func (e evalTree) next(iter unifyIterator, plugged *ast.Term) error { + + var node *ast.TreeNode + + cpy := e + cpy.plugged[e.pos] = plugged + cpy.pos++ + + if !e.e.targetStack.Prefixed(cpy.plugged[:cpy.pos]) { + if e.node != nil { + node = e.node.Child(plugged.Value) + if node != nil && len(node.Values) > 0 { + r := evalVirtual{ + e: e.e, + ref: e.ref, + plugged: e.plugged, + pos: e.pos, + bindings: e.bindings, + rterm: e.rterm, + rbindings: e.rbindings, + } + r.plugged[e.pos] = plugged + return r.eval(iter) + } + } + } + + cpy.node = node + return cpy.eval(iter) +} + +func (e evalTree) enumerate(iter unifyIterator) error { + + if e.e.inliningControl.Disabled(e.plugged[:e.pos], true) { + return e.e.saveUnify(ast.NewTerm(e.plugged), e.rterm, e.bindings, e.rbindings, iter) + } + + doc, err := e.e.Resolve(e.plugged[:e.pos]) + if err != nil { + return err + } + + dc := deecPool.Get().(*deferredEarlyExitContainer) + dc.deferred = nil + defer deecPool.Put(dc) + + if doc != nil { + switch doc := doc.(type) { + case *ast.Array: + for i := range doc.Len() { + k := ast.InternedTerm(i) + err := e.e.biunify(k, e.ref[e.pos], e.bindings, e.bindings, func() error { + return e.next(iter, k) + }) + + if err := dc.handleErr(err); err != nil { + return err + } + } + case ast.Object: + ki := doc.KeysIterator() + for k, more := ki.Next(); more; k, more = ki.Next() { + err := e.e.biunify(k, e.ref[e.pos], e.bindings, e.bindings, func() error { + return e.next(iter, k) + }) + if err := dc.handleErr(err); err != nil { + return err + } + } + case ast.Set: + if err := doc.Iter(func(elem *ast.Term) error { + err := e.e.biunify(elem, e.ref[e.pos], e.bindings, e.bindings, func() error { + return e.next(iter, elem) + }) + return dc.handleErr(err) + }); err != nil { + return err + } + } + } + + if dc.deferred != nil { + return dc.copyError() + } + + if e.node == nil { + return nil + } + + for _, k := range e.node.Sorted { + key := ast.NewTerm(k) + if err := e.e.biunify(key, e.ref[e.pos], e.bindings, e.bindings, func() error { + return e.next(iter, key) + }); err != nil { + return err + } + } + + return nil +} + +func (e evalTree) extent() (*ast.Term, error) { + base, err := e.e.Resolve(e.plugged) + if err != nil { + return nil, err + } + + virtual, err := e.leaves(e.plugged, e.node) + if err != nil { + return nil, err + } + + if virtual == nil { + if base == nil { + return nil, nil + } + return ast.NewTerm(base), nil + } + + if base != nil { + merged, ok := merge(base, virtual) + if !ok { + return nil, mergeConflictErr(e.plugged[0].Location) + } + return ast.NewTerm(merged), nil + } + + return ast.NewTerm(virtual), nil +} + +// leaves builds a tree from evaluating the full rule tree extent, by recursing into all +// branches, and building up objects as it goes. +func (e evalTree) leaves(plugged ast.Ref, node *ast.TreeNode) (ast.Object, error) { + + if e.node == nil { + return nil, nil + } + + result := ast.NewObject() + + for _, k := range node.Sorted { + + child := node.Children[k] + + if child.Hide { + continue + } + + plugged = append(plugged, ast.NewTerm(child.Key)) + + var save ast.Value + var err error + + if len(child.Values) > 0 { + rterm := e.e.generateVar("leaf") + err = e.e.unify(ast.NewTerm(plugged), rterm, func() error { + save = e.e.bindings.Plug(rterm).Value + return nil + }) + } else { + save, err = e.leaves(plugged, child) + } + + if err != nil { + return nil, err + } + + if save != nil { + v := ast.NewObject([2]*ast.Term{plugged[len(plugged)-1], ast.NewTerm(save)}) + result, _ = result.Merge(v) + } + + plugged = plugged[:len(plugged)-1] + } + + return result, nil +} + +type evalVirtual struct { + e *eval + bindings *bindings + rterm *ast.Term + rbindings *bindings + ref ast.Ref + plugged ast.Ref + pos int +} + +func (e evalVirtual) eval(iter unifyIterator) error { + + ir, err := e.e.getRules(e.plugged[:e.pos+1], nil) + defer ast.IndexResultPool.Put(ir) + if err != nil { + return err + } + + // Partial evaluation of ordered rules is not supported currently. Save the + // expression and continue. This could be revisited in the future. + if len(ir.Else) > 0 && e.e.unknownRef(e.ref, e.bindings) { + return e.e.saveUnify(ast.NewTerm(e.ref), e.rterm, e.bindings, e.rbindings, iter) + } + + switch ir.Kind { + case ast.MultiValue: + var empty *ast.Term + if ir.OnlyGroundRefs { + // rule ref contains no vars, so we're building a set + empty = ast.SetTerm() + } else { + // rule ref contains vars, so we're building an object containing a set leaf + empty = ast.ObjectTerm() + } + eval := evalVirtualPartial{ + e: e.e, + ref: e.ref, + plugged: e.plugged, + pos: e.pos, + ir: ir, + bindings: e.bindings, + rterm: e.rterm, + rbindings: e.rbindings, + empty: empty, + } + return eval.eval(iter) + case ast.SingleValue: + if ir.OnlyGroundRefs { + eval := evalVirtualComplete{ + e: e.e, + ref: e.ref, + plugged: e.plugged, + pos: e.pos, + ir: ir, + bindings: e.bindings, + rterm: e.rterm, + rbindings: e.rbindings, + } + return eval.eval(iter) + } + eval := evalVirtualPartial{ + e: e.e, + ref: e.ref, + plugged: e.plugged, + pos: e.pos, + ir: ir, + bindings: e.bindings, + rterm: e.rterm, + rbindings: e.rbindings, + empty: ast.ObjectTerm(), + } + return eval.eval(iter) + default: + panic("unreachable") + } +} + +type evalVirtualPartial struct { + e *eval + ir *ast.IndexResult + bindings *bindings + rterm *ast.Term + rbindings *bindings + empty *ast.Term + ref ast.Ref + plugged ast.Ref + pos int +} + +type evalVirtualPartialCacheHint struct { + key ast.Ref + hit bool + full bool +} + +func (h *evalVirtualPartialCacheHint) keyWithoutScope() ast.Ref { + if h.key != nil { + if _, ok := h.key[len(h.key)-1].Value.(vcKeyScope); ok { + return h.key[:len(h.key)-1] + } + } + return h.key +} + +func (e evalVirtualPartial) eval(iter unifyIterator) error { + + unknown := e.e.unknown(e.ref[:e.pos+1], e.bindings) + + if len(e.ref) == e.pos+1 { + if unknown { + return e.partialEvalSupport(iter) + } + return e.evalAllRules(iter, e.ir.Rules) + } + + if (unknown && e.e.inliningControl.shallow) || e.e.inliningControl.Disabled(e.ref[:e.pos+1], false) { + return e.partialEvalSupport(iter) + } + + return e.evalEachRule(iter, unknown) +} + +// returns the maximum length a ref can be without being longer than the longest rule ref in rules. +func maxRefLength(rules []*ast.Rule, ceil int) int { + var l int + for _, r := range rules { + rl := len(r.Ref()) + if r.Head.RuleKind() == ast.MultiValue { + rl++ + } + if rl >= ceil { + return ceil + } else if rl > l { + l = rl + } + } + return l +} + +func (e evalVirtualPartial) evalEachRule(iter unifyIterator, unknown bool) error { + + if e.ir.Empty() { + return nil + } + + if e.e.partial() { + m := maxRefLength(e.ir.Rules, len(e.ref)) + if e.e.unknown(e.ref[e.pos+1:m], e.bindings) { + for _, rule := range e.ir.Rules { + if err := e.evalOneRulePostUnify(iter, rule); err != nil { + return err + } + } + return nil + } + } + + hint, err := e.evalCache(iter) + if err != nil { + return err + } else if hint.hit { + return nil + } + + if hint.full { + result, err := e.evalAllRulesNoCache(e.ir.Rules) + if err != nil { + return err + } + e.e.virtualCache.Put(hint.key, result) + return e.evalTerm(iter, e.pos+1, result, e.bindings) + } + + result := e.empty + var visitedRefs []ast.Ref + + for _, rule := range e.ir.Rules { + result, err = e.evalOneRulePreUnify(iter, rule, result, unknown, &visitedRefs) + if err != nil { + return err + } + } + + if hint.key != nil { + if v, err := result.Value.Find(hint.keyWithoutScope()[e.pos+1:]); err == nil && v != nil { + e.e.virtualCache.Put(hint.key, ast.NewTerm(v)) + } + } + + if !unknown { + return e.evalTerm(iter, e.pos+1, result, e.bindings) + } + + return nil +} + +func (e evalVirtualPartial) evalAllRules(iter unifyIterator, rules []*ast.Rule) error { + + cacheKey := e.plugged[:e.pos+1] + result, _ := e.e.virtualCache.Get(cacheKey) + if result != nil { + e.e.instr.counterIncr(evalOpVirtualCacheHit) + return e.e.biunify(result, e.rterm, e.bindings, e.rbindings, iter) + } + + e.e.instr.counterIncr(evalOpVirtualCacheMiss) + + result, err := e.evalAllRulesNoCache(rules) + if err != nil { + return err + } + + if cacheKey != nil { + e.e.virtualCache.Put(cacheKey, result) + } + + return e.e.biunify(result, e.rterm, e.bindings, e.rbindings, iter) +} + +func (e evalVirtualPartial) evalAllRulesNoCache(rules []*ast.Rule) (*ast.Term, error) { + result := e.empty + + var visitedRefs []ast.Ref + + child := evalPool.Get() + defer evalPool.Put(child) + + for _, rule := range rules { + e.e.child(rule.Body, child) + child.traceEnter(rule) + err := child.eval(func(*eval) error { + child.traceExit(rule) + var err error + result, _, err = e.reduce(rule, child.bindings, result, &visitedRefs) + if err != nil { + return err + } + + child.traceRedo(rule) + return nil + }) + + if err != nil { + return nil, err + } + } + + return result, nil +} + +func wrapInObjects(leaf *ast.Term, ref ast.Ref) *ast.Term { + // We build the nested objects leaf-to-root to preserve ground:ness + if len(ref) == 0 { + return leaf + } + key := ref[0] + val := wrapInObjects(leaf, ref[1:]) + return ast.ObjectTerm(ast.Item(key, val)) +} + +func (e evalVirtualPartial) evalOneRulePreUnify(iter unifyIterator, rule *ast.Rule, result *ast.Term, unknown bool, visitedRefs *[]ast.Ref) (*ast.Term, error) { + child := evalPool.Get() + defer evalPool.Put(child) + + e.e.child(rule.Body, child) + + child.traceEnter(rule) + var defined bool + + headKey := rule.Head.Key + if headKey == nil { + headKey = rule.Head.Reference[len(rule.Head.Reference)-1] + } + + // Walk the dynamic portion of rule ref and key to unify vars + err := child.biunifyRuleHead(e.pos+1, e.ref, rule, e.bindings, child.bindings, func(_ int) error { + defined = true + return child.eval(func(child *eval) error { + + child.traceExit(rule) + + term := rule.Head.Value + if term == nil { + term = headKey + } + + if unknown { + term, termbindings := child.bindings.apply(term) + + if rule.Head.RuleKind() == ast.MultiValue { + term = ast.SetTerm(term) + } + + objRef := rule.Ref()[e.pos+1:] + term = wrapInObjects(term, objRef) + + err := e.evalTerm(iter, e.pos+1, term, termbindings) + if err != nil { + return err + } + } else { + var dup bool + var err error + result, dup, err = e.reduce(rule, child.bindings, result, visitedRefs) + if err != nil { + return err + } else if !unknown && dup { + child.traceDuplicate(rule) + return nil + } + } + + child.traceRedo(rule) + + return nil + }) + }) + + if err != nil { + return nil, err + } + + if !defined { + child.traceFail(rule) + } + + return result, nil +} + +func (e *eval) biunifyRuleHead(pos int, ref ast.Ref, rule *ast.Rule, refBindings, ruleBindings *bindings, iter unifyRefIterator) error { + return e.biunifyDynamicRef(pos, ref, rule.Ref(), refBindings, ruleBindings, func(pos int) error { + // FIXME: Is there a simpler, more robust way of figuring out that we should biunify the rule key? + if rule.Head.RuleKind() == ast.MultiValue && pos < len(ref) && len(rule.Ref()) <= len(ref) { + headKey := rule.Head.Key + if headKey == nil { + headKey = rule.Head.Reference[len(rule.Head.Reference)-1] + } + return e.biunify(ref[pos], headKey, refBindings, ruleBindings, func() error { + return iter(pos + 1) + }) + } + return iter(pos) + }) +} + +func (e *eval) biunifyDynamicRef(pos int, a, b ast.Ref, b1, b2 *bindings, iter unifyRefIterator) error { + if pos >= len(a) || pos >= len(b) { + return iter(pos) + } + + return e.biunify(a[pos], b[pos], b1, b2, func() error { + return e.biunifyDynamicRef(pos+1, a, b, b1, b2, iter) + }) +} + +func (e evalVirtualPartial) evalOneRulePostUnify(iter unifyIterator, rule *ast.Rule) error { + child := evalPool.Get() + defer evalPool.Put(child) + + e.e.child(rule.Body, child) + + child.traceEnter(rule) + var defined bool + + err := child.eval(func(child *eval) error { + defined = true + return e.e.biunifyRuleHead(e.pos+1, e.ref, rule, e.bindings, child.bindings, func(_ int) error { + return e.evalOneRuleContinue(iter, rule, child) + }) + }) + + if err != nil { + return err + } + + if !defined { + child.traceFail(rule) + } + + return nil +} + +func (e evalVirtualPartial) evalOneRuleContinue(iter unifyIterator, rule *ast.Rule, child *eval) error { + + child.traceExit(rule) + + term := rule.Head.Value + if term == nil { + term = rule.Head.Key + } + + term, termbindings := child.bindings.apply(term) + + if rule.Head.RuleKind() == ast.MultiValue { + term = ast.SetTerm(term) + } + + objRef := rule.Ref()[e.pos+1:] + term = wrapInObjects(term, objRef) + + err := e.evalTerm(iter, e.pos+1, term, termbindings) + if err != nil { + return err + } + + child.traceRedo(rule) + return nil +} + +func (e evalVirtualPartial) partialEvalSupport(iter unifyIterator) error { + + path := e.e.namespaceRef(e.plugged[:e.pos+1]) + term := ast.NewTerm(e.e.namespaceRef(e.ref)) + + var defined bool + + if e.e.saveSupport.Exists(path) { + defined = true + } else { + for i := range e.ir.Rules { + ok, err := e.partialEvalSupportRule(e.ir.Rules[i], path) + if err != nil { + return err + } + if ok { + defined = true + } + } + } + + if !defined { + if len(e.ref) != e.pos+1 { + return nil + } + + // the entire partial set/obj was queried, e.g. data.a.q (not data.a.q[x]) + term = e.empty + } + + return e.e.saveUnify(term, e.rterm, e.bindings, e.rbindings, iter) +} + +func (e evalVirtualPartial) partialEvalSupportRule(rule *ast.Rule, _ ast.Ref) (bool, error) { + child := evalPool.Get() + defer evalPool.Put(child) + + e.e.child(rule.Body, child) + child.traceEnter(rule) + + e.e.saveStack.PushQuery(nil) + var defined bool + + err := child.eval(func(child *eval) error { + child.traceExit(rule) + defined = true + + current := e.e.saveStack.PopQuery() + plugged := current.Plug(e.e.caller.bindings) + // Skip this rule body if it fails to type-check. + // Type-checking failure means the rule body will never succeed. + if e.e.compiler.PassesTypeCheck(plugged) { + var value *ast.Term + + if rule.Head.Value != nil { + value = child.bindings.PlugNamespaced(rule.Head.Value, e.e.caller.bindings) + } + + ref := e.e.namespaceRef(rule.Ref()) + for i := 1; i < len(ref); i++ { + ref[i] = child.bindings.plugNamespaced(ref[i], e.e.caller.bindings) + } + pkg, ruleRef := splitPackageAndRule(ref) + + head := ast.RefHead(ruleRef, value) + + // key is also part of ref in single-value rules, and can be dropped + if rule.Head.Key != nil && rule.Head.RuleKind() == ast.MultiValue { + head.Key = child.bindings.PlugNamespaced(rule.Head.Key, e.e.caller.bindings) + } + + if rule.Head.RuleKind() == ast.SingleValue && len(ruleRef) == 2 { + head.Key = ruleRef[len(ruleRef)-1] + } + + if head.Name.Equal(ast.Var("")) && (len(ruleRef) == 1 || (len(ruleRef) == 2 && rule.Head.RuleKind() == ast.SingleValue)) { + head.Name = ruleRef[0].Value.(ast.Var) + } + + if !e.e.inliningControl.shallow { + cp := copypropagation.New(head.Vars()). + WithEnsureNonEmptyBody(true). + WithCompiler(e.e.compiler) + plugged = applyCopyPropagation(cp, e.e.instr, plugged) + } + + e.e.saveSupport.InsertByPkg(pkg, &ast.Rule{ + Head: head, + Body: plugged, + Default: rule.Default, + }) + } + child.traceRedo(rule) + e.e.saveStack.PushQuery(current) + return nil + }) + e.e.saveStack.PopQuery() + return defined, err +} + +func (e evalVirtualPartial) evalTerm(iter unifyIterator, pos int, term *ast.Term, termbindings *bindings) error { + eval := evalTerm{ + e: e.e, + ref: e.ref, + pos: pos, + bindings: e.bindings, + term: term, + termbindings: termbindings, + rterm: e.rterm, + rbindings: e.rbindings, + } + return eval.eval(iter) +} + +func (e evalVirtualPartial) evalCache(iter unifyIterator) (evalVirtualPartialCacheHint, error) { + + var hint evalVirtualPartialCacheHint + + if e.e.unknown(e.ref[:e.pos+1], e.bindings) { + // FIXME: Return empty hint if unknowns in any e.ref elem overlapping with applicable rule refs? + return hint, nil + } + + if cached, _ := e.e.virtualCache.Get(e.plugged[:e.pos+1]); cached != nil { // have full extent cached + e.e.instr.counterIncr(evalOpVirtualCacheHit) + hint.hit = true + return hint, e.evalTerm(iter, e.pos+1, cached, e.bindings) + } + + plugged := e.bindings.Plug(e.ref[e.pos+1]) + + if _, ok := plugged.Value.(ast.Var); ok { + // Note: we might have additional opportunity to optimize here, if we consider that ground values + // right of e.pos could create a smaller eval "scope" through ref bi-unification before evaluating rules. + hint.full = true + hint.key = e.plugged[:e.pos+1] + e.e.instr.counterIncr(evalOpVirtualCacheMiss) + return hint, nil + } + + m := maxRefLength(e.ir.Rules, len(e.ref)) + + // Creating the hint key by walking the ref and plugging vars until we hit a non-ground term. + // Any ground term right of this point will affect the scope of evaluation by ref unification, + // so we create a virtual-cache scope key to qualify the result stored in the cache. + // + // E.g. given the following rule: + // + // package example + // + // a[x][y][z] := x + y + z if { + // some x in [1, 2] + // some y in [3, 4] + // some z in [5, 6] + // } + // + // and the following ref (1): + // + // data.example.a[1][_][5] + // + // then the hint key will be: + // + // data.example.a[1][<_,5>] + // + // where <_,5> is the scope of the pre-eval unification. + // This part does not contribute to the "location" of the cached data. + // + // The following ref (2): + // + // data.example.a[1][_][6] + // + // will produce the same hint key "location" 'data.example.a[1]', but a different scope component + // '<_,6>', which will create a different entry in the cache. + scoping := false + hintKeyEnd := 0 + for i := e.pos + 1; i < m; i++ { + plugged = e.bindings.Plug(e.ref[i]) + + if plugged.IsGround() && !scoping { + hintKeyEnd = i + hint.key = append(e.plugged[:i], plugged) + } else { + scoping = true + hl := len(hint.key) + if hl == 0 { + break + } + if scope, ok := hint.key[hl-1].Value.(vcKeyScope); ok { + scope.Ref = append(scope.Ref, plugged) + hint.key[len(hint.key)-1] = ast.NewTerm(scope) + } else { + scope = vcKeyScope{} + scope.Ref = append(scope.Ref, plugged) + hint.key = append(hint.key, ast.NewTerm(scope)) + } + } + + if cached, _ := e.e.virtualCache.Get(hint.key); cached != nil { + e.e.instr.counterIncr(evalOpVirtualCacheHit) + hint.hit = true + return hint, e.evalTerm(iter, hintKeyEnd+1, cached, e.bindings) + } + } + + if hl := len(hint.key); hl > 0 { + if scope, ok := hint.key[hl-1].Value.(vcKeyScope); ok { + scope = scope.reduce() + if scope.empty() { + hint.key = hint.key[:hl-1] + } else { + hint.key[hl-1].Value = scope + } + } + } + + e.e.instr.counterIncr(evalOpVirtualCacheMiss) + + return hint, nil +} + +// vcKeyScope represents the scoping that pre-rule-eval ref unification imposes on a virtual cache entry. +type vcKeyScope struct { + ast.Ref +} + +func (q vcKeyScope) Compare(other ast.Value) int { + if q2, ok := other.(vcKeyScope); ok { + r1 := q.Ref + r2 := q2.Ref + if len(r1) != len(r2) { + return -1 + } + + for i := range r1 { + _, v1IsVar := r1[i].Value.(ast.Var) + _, v2IsVar := r2[i].Value.(ast.Var) + if v1IsVar && v2IsVar { + continue + } + if r1[i].Value.Compare(r2[i].Value) != 0 { + return -1 + } + } + + return 0 + } + return 1 +} + +func (vcKeyScope) Find(ast.Ref) (ast.Value, error) { + return nil, nil +} + +func (q vcKeyScope) Hash() int { + var hash int + for _, v := range q.Ref { + if _, ok := v.Value.(ast.Var); ok { + // all vars are equal + hash++ + } else { + hash += v.Value.Hash() + } + } + return hash +} + +func (vcKeyScope) IsGround() bool { + return false +} + +func (q vcKeyScope) String() string { + buf := make([]string, 0, len(q.Ref)) + for _, t := range q.Ref { + if _, ok := t.Value.(ast.Var); ok { + buf = append(buf, "_") + } else { + buf = append(buf, t.String()) + } + } + return fmt.Sprintf("<%s>", strings.Join(buf, ",")) +} + +// reduce removes vars from the tail of the ref. +func (q vcKeyScope) reduce() vcKeyScope { + ref := q.Ref.Copy() + var i int + for i = len(q.Ref) - 1; i >= 0; i-- { + if _, ok := q.Ref[i].Value.(ast.Var); !ok { + break + } + } + ref = ref[:i+1] + return vcKeyScope{ref} +} + +func (q vcKeyScope) empty() bool { + return len(q.Ref) == 0 +} + +func getNestedObject(ref ast.Ref, rootObj *ast.Object, b *bindings, l *ast.Location) (*ast.Object, error) { + current := rootObj + for _, term := range ref { + key := b.Plug(term) + if child := (*current).Get(key); child != nil { + if val, ok := child.Value.(ast.Object); ok { + current = &val + } else { + return nil, objectDocKeyConflictErr(l) + } + } else { + child := ast.NewObject() + (*current).Insert(key, ast.NewTerm(child)) + current = &child + } + } + + return current, nil +} + +func hasCollisions(path ast.Ref, visitedRefs *[]ast.Ref, b *bindings) bool { + collisionPathTerm := b.Plug(ast.NewTerm(path)) + collisionPath := collisionPathTerm.Value.(ast.Ref) + for _, c := range *visitedRefs { + if collisionPath.HasPrefix(c) && !collisionPath.Equal(c) { + return true + } + } + *visitedRefs = append(*visitedRefs, collisionPath) + return false +} + +func (e evalVirtualPartial) reduce(rule *ast.Rule, b *bindings, result *ast.Term, visitedRefs *[]ast.Ref) (*ast.Term, bool, error) { + + var exists bool + head := rule.Head + + switch v := result.Value.(type) { + case ast.Set: + key := b.Plug(head.Key) + exists = v.Contains(key) + v.Add(key) + case ast.Object: + // data.p.q[r].s.t := 42 {...} + // |----|-| + // ^ ^ + // | leafKey + // objPath + fullPath := rule.Ref() + + collisionPath := fullPath[e.pos+1:] + if hasCollisions(collisionPath, visitedRefs, b) { + return nil, false, objectDocKeyConflictErr(head.Location) + } + + objPath := fullPath[e.pos+1 : len(fullPath)-1] // the portion of the ref that generates nested objects + leafKey := b.Plug(fullPath[len(fullPath)-1]) // the portion of the ref that is the deepest nested key for the value + + leafObj, err := getNestedObject(objPath, &v, b, head.Location) + if err != nil { + return nil, false, err + } + + if kind := head.RuleKind(); kind == ast.SingleValue { + // We're inserting into an object + val := b.Plug(head.Value) // head.Value instance is shared between rule enumerations;but this is ok, as we don't allow rules to modify each others values. + + if curr := (*leafObj).Get(leafKey); curr != nil { + if !curr.Equal(val) { + return nil, false, objectDocKeyConflictErr(head.Location) + } + exists = true + } else { + (*leafObj).Insert(leafKey, val) + } + } else { + // We're inserting into a set + var set *ast.Set + if leaf := (*leafObj).Get(leafKey); leaf != nil { + if s, ok := leaf.Value.(ast.Set); ok { + set = &s + } else { + return nil, false, objectDocKeyConflictErr(head.Location) + } + } else { + s := ast.NewSet() + (*leafObj).Insert(leafKey, ast.NewTerm(s)) + set = &s + } + + key := b.Plug(head.Key) + exists = (*set).Contains(key) + (*set).Add(key) + } + } + + return result, exists, nil +} + +type evalVirtualComplete struct { + e *eval + ir *ast.IndexResult + bindings *bindings + rterm *ast.Term + rbindings *bindings + ref ast.Ref + plugged ast.Ref + pos int +} + +func (e evalVirtualComplete) eval(iter unifyIterator) error { + + if e.ir.Empty() { + return nil + } + + // When evaluating the full extent, skip functions. + if len(e.ir.Rules) > 0 && len(e.ir.Rules[0].Head.Args) > 0 || + e.ir.Default != nil && len(e.ir.Default.Head.Args) > 0 { + return nil + } + + if !e.e.unknownRef(e.ref, e.bindings) { + return e.evalValue(iter, e.ir.EarlyExit) + } + + var generateSupport bool + + if e.ir.Default != nil { + // If inlining has been disabled for the rterm, and the default rule has a 'false' result value, + // the default value is inconsequential, and support does not need to be generated. + if !(e.ir.Default.Head.Value.Equal(ast.InternedTerm(false)) && e.e.inliningControl.Disabled(e.rterm.Value, false)) { + // If the other term is not constant OR it's equal to the default value, then + // a support rule must be produced as the default value _may_ be required. On + // the other hand, if the other term is constant (i.e., it does not require + // evaluation) and it differs from the default value then the default value is + // _not_ required, so partially evaluate the rule normally. + rterm := e.rbindings.Plug(e.rterm) + generateSupport = !ast.IsConstant(rterm.Value) || e.ir.Default.Head.Value.Equal(rterm) + } + } + + if generateSupport || e.e.inliningControl.shallow || e.e.inliningControl.Disabled(e.plugged[:e.pos+1], false) { + return e.partialEvalSupport(iter) + } + + return e.partialEval(iter) +} + +func (e evalVirtualComplete) evalValue(iter unifyIterator, findOne bool) error { + cached, undefined := e.e.virtualCache.Get(e.plugged[:e.pos+1]) + if undefined { + e.e.instr.counterIncr(evalOpVirtualCacheHit) + return nil + } + + // a cached result won't generate any EE from evaluating the rule, so we exempt it from EE suppression to not + // drop EE generated by the caller (through `iter` invocation). + if cached != nil { + e.e.instr.counterIncr(evalOpVirtualCacheHit) + return e.evalTerm(iter, cached, e.bindings) + } + + return withSuppressEarlyExit(func() error { + e.e.instr.counterIncr(evalOpVirtualCacheMiss) + + var prev *ast.Term + var deferredEe *deferredEarlyExitError + + for _, rule := range e.ir.Rules { + next, err := e.evalValueRule(iter, rule, prev, findOne) + if err != nil { + if dee, ok := err.(*deferredEarlyExitError); ok { + if deferredEe == nil { + deferredEe = dee + } + } else { + return err + } + } + if next == nil { + for _, erule := range e.ir.Else[rule] { + next, err = e.evalValueRule(iter, erule, prev, findOne) + if err != nil { + if dee, ok := err.(*deferredEarlyExitError); ok { + if deferredEe == nil { + deferredEe = dee + } + } else { + return err + } + } + if next != nil { + break + } + } + } + if next != nil { + prev = next + } + } + + if e.ir.Default != nil && prev == nil { + _, err := e.evalValueRule(iter, e.ir.Default, prev, findOne) + return err + } + + if prev == nil { + e.e.virtualCache.Put(e.plugged[:e.pos+1], nil) + } + + if deferredEe != nil { + return deferredEe + } + + return nil + }) +} + +func (e evalVirtualComplete) evalValueRule(iter unifyIterator, rule *ast.Rule, prev *ast.Term, findOne bool) (*ast.Term, error) { + child := evalPool.Get() + defer evalPool.Put(child) + + e.e.child(rule.Body, child) + child.findOne = findOne + child.traceEnter(rule) + var result *ast.Term + err := child.eval(func(child *eval) error { + child.traceExit(rule) + + result = child.bindings.Plug(rule.Head.Value) + + if prev != nil { + if ast.Compare(result, prev) != 0 { + return completeDocConflictErr(rule.Location) + } + child.traceRedo(rule) + return nil + } + + prev = result + e.e.virtualCache.Put(e.plugged[:e.pos+1], result) + + term, termbindings := child.bindings.apply(rule.Head.Value) + err := e.evalTerm(iter, term, termbindings) + if err != nil { + return err + } + + // TODO: trace redo if EE-err && !findOne(?) + child.traceRedo(rule) + return nil + }) + + return result, err +} + +func (e evalVirtualComplete) partialEval(iter unifyIterator) error { + child := evalPool.Get() + defer evalPool.Put(child) + + for _, rule := range e.ir.Rules { + e.e.child(rule.Body, child) + child.traceEnter(rule) + + err := child.eval(func(child *eval) error { + child.traceExit(rule) + term, termbindings := child.bindings.apply(rule.Head.Value) + + err := e.evalTerm(iter, term, termbindings) + if err != nil { + return err + } + + child.traceRedo(rule) + return nil + }) + + if err != nil { + return err + } + } + + return nil +} + +func (e evalVirtualComplete) partialEvalSupport(iter unifyIterator) error { + + path := e.e.namespaceRef(e.plugged[:e.pos+1]) + term := ast.NewTerm(e.e.namespaceRef(e.ref)) + + var defined bool + + if e.e.saveSupport.Exists(path) { + defined = true + } else { + for i := range e.ir.Rules { + ok, err := e.partialEvalSupportRule(e.ir.Rules[i], path) + if err != nil { + return err + } + if ok { + defined = true + } + } + + if e.ir.Default != nil { + ok, err := e.partialEvalSupportRule(e.ir.Default, path) + if err != nil { + return err + } + if ok { + defined = true + } + } + } + + if !defined { + return nil + } + + return e.e.saveUnify(term, e.rterm, e.bindings, e.rbindings, iter) +} + +func (e evalVirtualComplete) partialEvalSupportRule(rule *ast.Rule, path ast.Ref) (bool, error) { + child := evalPool.Get() + defer evalPool.Put(child) + + e.e.child(rule.Body, child) + child.traceEnter(rule) + + e.e.saveStack.PushQuery(nil) + var defined bool + + err := child.eval(func(child *eval) error { + child.traceExit(rule) + defined = true + + current := e.e.saveStack.PopQuery() + plugged := current.Plug(e.e.caller.bindings) + // Skip this rule body if it fails to type-check. + // Type-checking failure means the rule body will never succeed. + if e.e.compiler.PassesTypeCheck(plugged) { + pkg, ruleRef := splitPackageAndRule(path) + head := ast.RefHead(ruleRef, child.bindings.PlugNamespaced(rule.Head.Value, e.e.caller.bindings)) + + if !e.e.inliningControl.shallow { + cp := copypropagation.New(head.Vars()). + WithEnsureNonEmptyBody(true). + WithCompiler(e.e.compiler) + plugged = applyCopyPropagation(cp, e.e.instr, plugged) + } + + e.e.saveSupport.InsertByPkg(pkg, &ast.Rule{ + Head: head, + Body: plugged, + Default: rule.Default, + }) + } + child.traceRedo(rule) + e.e.saveStack.PushQuery(current) + return nil + }) + e.e.saveStack.PopQuery() + return defined, err +} + +func (e evalVirtualComplete) evalTerm(iter unifyIterator, term *ast.Term, termbindings *bindings) error { + eval := evalTerm{ + e: e.e, + ref: e.ref, + pos: e.pos + 1, + bindings: e.bindings, + term: term, + termbindings: termbindings, + rterm: e.rterm, + rbindings: e.rbindings, + } + return eval.eval(iter) +} + +type evalTerm struct { + e *eval + bindings *bindings + term *ast.Term + termbindings *bindings + rterm *ast.Term + rbindings *bindings + ref ast.Ref + pos int +} + +func (e evalTerm) eval(iter unifyIterator) error { + + if len(e.ref) == e.pos { + return e.e.biunify(e.term, e.rterm, e.termbindings, e.rbindings, iter) + } + + if e.e.saveSet.Contains(e.term, e.termbindings) { + return e.save(iter) + } + + plugged := e.bindings.Plug(e.ref[e.pos]) + + if plugged.IsGround() { + return e.next(iter, plugged) + } + + return e.enumerate(iter) +} + +func (e evalTerm) next(iter unifyIterator, plugged *ast.Term) error { + + term, bindings := e.get(plugged) + if term == nil { + return nil + } + + cpy := e + cpy.term = term + cpy.termbindings = bindings + cpy.pos++ + return cpy.eval(iter) +} + +func (e evalTerm) enumerate(iter unifyIterator) error { + var deferredEe *deferredEarlyExitError + handleErr := func(err error) error { + var dee *deferredEarlyExitError + if errors.As(err, &dee) { + if deferredEe == nil { + deferredEe = dee + } + return nil + } + return err + } + + switch v := e.term.Value.(type) { + case *ast.Array: + // Note(anders): + // For this case (e.g. input.foo[_]), we can avoid the (quite expensive) overhead of a callback + // function literal escaping to the heap in each iteration by inlining the biunification logic, + // meaning a 10x reduction in both the number of allocations made as well as the memory consumed. + // It is possible that such inlining could be done for the set/object cases as well, and that's + // worth looking into later, as I imagine set iteration in particular would be an even greater + // win across most policies. Those cases are however much more complex, as we need to deal with + // any type on either side, not just int/var as is the case here. + for i := range v.Len() { + a := ast.InternedTerm(i) + b := e.ref[e.pos] + + if _, ok := b.Value.(ast.Var); ok { + if e.e.traceEnabled { + e.e.traceUnify(a, b) + } + var undo undo + b, e.bindings = e.bindings.apply(b) + e.bindings.bind(b, a, e.bindings, &undo) + + err := e.next(iter, a) + undo.Undo() + if err != nil { + if err := handleErr(err); err != nil { + return err + } + } + } + } + case ast.Object: + for _, k := range v.Keys() { + err := e.e.biunify(k, e.ref[e.pos], e.termbindings, e.bindings, func() error { + return e.next(iter, e.termbindings.Plug(k)) + }) + if err != nil { + if err := handleErr(err); err != nil { + return err + } + } + } + case ast.Set: + for _, elem := range v.Slice() { + err := e.e.biunify(elem, e.ref[e.pos], e.termbindings, e.bindings, func() error { + return e.next(iter, e.termbindings.Plug(elem)) + }) + if err != nil { + if err := handleErr(err); err != nil { + return err + } + } + } + } + + if deferredEe != nil { + return deferredEe + } + return nil +} + +func (e evalTerm) get(plugged *ast.Term) (*ast.Term, *bindings) { + switch v := e.term.Value.(type) { + case ast.Set: + if v.IsGround() { + if v.Contains(plugged) { + return e.termbindings.apply(plugged) + } + } else { + var t *ast.Term + var b *bindings + stop := v.Until(func(elem *ast.Term) bool { + if e.termbindings.Plug(elem).Equal(plugged) { + t, b = e.termbindings.apply(plugged) + return true + } + return false + }) + if stop { + return t, b + } + } + case ast.Object: + if v.IsGround() { + term := v.Get(plugged) + if term != nil { + return e.termbindings.apply(term) + } + } else { + var t *ast.Term + var b *bindings + stop := v.Until(func(k, v *ast.Term) bool { + if e.termbindings.Plug(k).Equal(plugged) { + t, b = e.termbindings.apply(v) + return true + } + return false + }) + if stop { + return t, b + } + } + case *ast.Array: + term := v.Get(plugged) + if term != nil { + return e.termbindings.apply(term) + } + } + return nil, nil +} + +func (e evalTerm) save(iter unifyIterator) error { + + v := e.e.generateVar(fmt.Sprintf("ref_%d", e.e.genvarid)) + e.e.genvarid++ + + return e.e.biunify(e.term, v, e.termbindings, e.bindings, func() error { + + suffix := e.ref[e.pos:] + ref := make(ast.Ref, len(suffix)+1) + ref[0] = v + copy(ref[1:], suffix) + + return e.e.biunify(ast.NewTerm(ref), e.rterm, e.bindings, e.rbindings, iter) + }) + +} + +type evalEvery struct { + *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.Domain, e.e.bindings) || e.e.unknown(e.Body, e.e.bindings) { + return e.save(iter) + } + + 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 := evalPool.Get() + defer evalPool.Put(domain) + + e.e.closure(generator, domain) + + all := true // all generator evaluations yield one successful body evaluation + + domain.traceEnter(e.expr) + + err := domain.eval(func(child *eval) error { + if !all { + // NOTE(sr): Is this good enough? We don't have a "fail EE". + // This would do extra work, like iterating needlessly if domain was a large array. + return nil + } + + body := evalPool.Get() + defer evalPool.Put(body) + + child.closure(e.Body, body) + body.findOne = true + body.traceEnter(e.Body) + done := false + err := body.eval(func(*eval) error { + body.traceExit(e.Body) + done = true + body.traceRedo(e.Body) + return nil + }) + if !done { + all = false + } + + child.traceRedo(e.expr) + + // We don't want to abort the generator domain enumeration with EE. + return suppressEarlyExit(err) + }) + + if err != nil { + return err + } + + if all { + err := iter() + domain.traceExit(e.expr) + return err + } + domain.traceFail(e.expr) + 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) +} + +func (e *evalEvery) plug(expr *ast.Expr) *ast.Expr { + cpy := expr.Copy() + every := cpy.Terms.(*ast.Every) + for i := range every.Body { + switch t := every.Body[i].Terms.(type) { + case *ast.Term: + every.Body[i].Terms = e.e.bindings.PlugNamespaced(t, e.e.caller.bindings) + case []*ast.Term: + for j := 1; j < len(t); j++ { // don't plug operator, t[0] + t[j] = e.e.bindings.PlugNamespaced(t[j], e.e.caller.bindings) + } + case *ast.Every: + every.Body[i] = e.plug(every.Body[i]) + } + } + + every.Key = e.e.bindings.PlugNamespaced(every.Key, e.e.caller.bindings) + every.Value = e.e.bindings.PlugNamespaced(every.Value, e.e.caller.bindings) + every.Domain = e.e.bindings.PlugNamespaced(every.Domain, e.e.caller.bindings) + cpy.Terms = every + return cpy +} + +func (e *eval) comprehensionIndex(term *ast.Term) *ast.ComprehensionIndex { + if e.queryCompiler != nil { + return e.queryCompiler.ComprehensionIndex(term) + } + return e.compiler.ComprehensionIndex(term) +} + +func (e *eval) namespaceRef(ref ast.Ref) ast.Ref { + if e.skipSaveNamespace { + return ref.Copy() + } + return ref.Insert(e.saveNamespace, 1) +} + +type savePair struct { + term *ast.Term + b *bindings +} + +func getSavePairsFromExpr(declArgsLen int, x *ast.Expr, b *bindings, result []savePair) []savePair { + switch terms := x.Terms.(type) { + case *ast.Term: + return getSavePairsFromTerm(terms, b, result) + case []*ast.Term: + if x.IsEquality() { + return getSavePairsFromTerm(terms[2], b, getSavePairsFromTerm(terms[1], b, result)) + } + if declArgsLen == len(terms)-2 { + return getSavePairsFromTerm(terms[len(terms)-1], b, result) + } + } + return result +} + +func getSavePairsFromTerm(x *ast.Term, b *bindings, result []savePair) []savePair { + if _, ok := x.Value.(ast.Var); ok { + result = append(result, savePair{x, b}) + return result + } + vis := ast.NewVarVisitor().WithParams(ast.VarVisitorParams{ + SkipClosures: true, + SkipRefHead: true, + }) + vis.Walk(x) + for v := range vis.Vars() { + y, next := b.apply(ast.NewTerm(v)) + result = getSavePairsFromTerm(y, next, result) + } + return result +} + +func applyCopyPropagation(p *copypropagation.CopyPropagator, instr *Instrumentation, body ast.Body) ast.Body { + instr.startTimer(partialOpCopyPropagation) + result := p.Apply(body) + instr.stopTimer(partialOpCopyPropagation) + return result +} + +func nonGroundKey(k, _ *ast.Term) bool { + return !k.IsGround() +} + +func nonGroundKeys(a ast.Object) bool { + return a.Until(nonGroundKey) +} + +func plugKeys(a ast.Object, b *bindings) ast.Object { + plugged, _ := a.Map(func(k, v *ast.Term) (*ast.Term, *ast.Term, error) { + return b.Plug(k), v, nil + }) + return plugged +} + +func canInlineNegation(safe ast.VarSet, queries []ast.Body) bool { + + size := 1 + vis := newNestedCheckVisitor() + + for _, query := range queries { + size *= len(query) + for _, expr := range query { + if containsNestedRefOrCall(vis, expr) { + // Expressions containing nested refs or calls cannot be trivially negated + // because the semantics would change. For example, the complement of `not f(input.x)` + // is _not_ `f(input.x)`--it is `not input.x` OR `f(input.x)`. + // + // NOTE(tsandall): Since this would require the complement function to undo the + // copy propagation optimization, just bail out here. If this becomes a problem + // in the future, we can handle more cases. + return false + } + if !expr.Negated { + // Positive expressions containing variables cannot be trivially negated + // because they become unsafe (e.g., "x = 1" negated is "not x = 1" making x + // unsafe.) We check if the vars in the expr are already safe. + vis := ast.NewVarVisitor().WithParams(ast.VarVisitorParams{ + SkipRefCallHead: true, + SkipClosures: true, + }) + vis.Walk(expr) + if vis.Vars().Diff(safe).DiffCount(ast.ReservedVars) > 0 { + return false + } + } + } + } + + // NOTE(tsandall): this limit is arbitrary–it's only in place to prevent the + // partial evaluation result from blowing up. In the future, we could make this + // configurable or do something more clever. + return size <= 16 +} + +type nestedCheckVisitor struct { + vis *ast.GenericVisitor + found bool +} + +func newNestedCheckVisitor() *nestedCheckVisitor { + v := &nestedCheckVisitor{} + v.vis = ast.NewGenericVisitor(v.visit) + return v +} + +func (v *nestedCheckVisitor) visit(x any) bool { + switch x.(type) { + case ast.Ref, ast.Call: + v.found = true + } + return v.found +} + +func containsNestedRefOrCall(vis *nestedCheckVisitor, expr *ast.Expr) bool { + + if expr.IsEquality() { + for _, term := range expr.Operands() { + if containsNestedRefOrCallInTerm(vis, term) { + return true + } + } + return false + } + + if expr.IsCall() { + for _, term := range expr.Operands() { + vis.vis.Walk(term) + if vis.found { + return true + } + } + return false + } + + return containsNestedRefOrCallInTerm(vis, expr.Terms.(*ast.Term)) +} + +func containsNestedRefOrCallInTerm(vis *nestedCheckVisitor, term *ast.Term) bool { + switch v := term.Value.(type) { + case ast.Ref: + for i := 1; i < len(v); i++ { + vis.vis.Walk(v[i]) + if vis.found { + return true + } + } + return false + default: + vis.vis.Walk(v) + if vis.found { + return true + } + return false + } +} + +func complementedCartesianProduct(queries []ast.Body, idx int, curr ast.Body, iter func(ast.Body) error) error { + if idx == len(queries) { + return iter(curr) + } + for _, expr := range queries[idx] { + curr = append(curr, expr.Complement()) + if err := complementedCartesianProduct(queries, idx+1, curr, iter); err != nil { + return err + } + curr = curr[:len(curr)-1] + } + return nil +} + +func isInputRef(term *ast.Term) bool { + if ref, ok := term.Value.(ast.Ref); ok { + if ref.HasPrefix(ast.InputRootRef) { + return true + } + } + return false +} + +func isDataRef(term *ast.Term) bool { + if ref, ok := term.Value.(ast.Ref); ok { + if ref.HasPrefix(ast.DefaultRootRef) { + return true + } + } + return false +} + +func isOtherRef(term *ast.Term) bool { + ref, ok := term.Value.(ast.Ref) + if !ok { + panic("unreachable") + } + return !ref.HasPrefix(ast.DefaultRootRef) && !ref.HasPrefix(ast.InputRootRef) +} + +func isFunction(env *ast.TypeEnv, ref any) bool { + var r ast.Ref + switch v := ref.(type) { + case ast.Ref: + r = v + case *ast.Term: + return isFunction(env, v.Value) + case ast.Value: + return false + default: + panic("expected ast.Value or *ast.Term") + } + _, ok := env.Get(r).(*types.Function) + return ok +} + +func merge(a, b ast.Value) (ast.Value, bool) { + aObj, ok1 := a.(ast.Object) + bObj, ok2 := b.(ast.Object) + + if ok1 && ok2 { + return mergeObjects(aObj, bObj) + } + + // nothing to merge, a wins + return a, true +} + +// mergeObjects returns a new Object containing the non-overlapping keys of +// the objA and objB. If there are overlapping keys between objA and objB, +// the values of associated with the keys are merged. Only +// objects can be merged with other objects. If the values cannot be merged, +// objB value will be overwritten by objA value. +func mergeObjects(objA, objB ast.Object) (result ast.Object, ok bool) { + result = ast.NewObject() + stop := objA.Until(func(k, v *ast.Term) bool { + if v2 := objB.Get(k); v2 == nil { + result.Insert(k, v) + } else { + obj1, ok1 := v.Value.(ast.Object) + obj2, ok2 := v2.Value.(ast.Object) + + if !ok1 || !ok2 { + result.Insert(k, v) + return false + } + obj3, ok := mergeObjects(obj1, obj2) + if !ok { + return true + } + result.Insert(k, ast.NewTerm(obj3)) + } + return false + }) + if stop { + return nil, false + } + objB.Foreach(func(k, v *ast.Term) { + if v2 := objA.Get(k); v2 == nil { + result.Insert(k, v) + } + }) + return result, true +} + +func refContainsNonScalar(ref ast.Ref) bool { + for _, term := range ref[1:] { + if !ast.IsScalar(term.Value) { + return true + } + } + return false +} + +func suppressEarlyExit(err error) error { + if ee, ok := err.(*earlyExitError); ok { + return ee.prev + } else if oee, ok := err.(*deferredEarlyExitError); ok { + return oee.prev + } + return err +} + +func withSuppressEarlyExit(f func() error) error { + if err := f(); err != nil { + return suppressEarlyExit(err) + } + return nil +} + +func (e *eval) updateSavedMocks(withs []*ast.With) []*ast.With { + ret := make([]*ast.With, 0, len(withs)) + for _, w := range withs { + if isOtherRef(w.Target) || isFunction(e.compiler.TypeEnv, w.Target) { + continue + } + ret = append(ret, w.Copy()) + } + return ret +} diff --git a/third_party/opa/v1/topdown/eval_test.go b/third_party/opa/v1/topdown/eval_test.go new file mode 100644 index 000000000000..ec28e805f43e --- /dev/null +++ b/third_party/opa/v1/topdown/eval_test.go @@ -0,0 +1,1704 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" +) + +func TestQueryIDFactory(t *testing.T) { + t.Parallel() + + f := &queryIDFactory{} + for i := range 10 { + if n := f.Next(); n != uint64(i) { + t.Errorf("expected %d, got %d", i, n) + } + } +} + +func TestMergeNonOverlappingKeys(t *testing.T) { + t.Parallel() + + realData := ast.MustParseTerm(`{"foo": "bar"}`).Value.(ast.Object) + mockData := ast.MustParseTerm(`{"baz": "blah"}`).Value.(ast.Object) + + result, ok := merge(mockData, realData) + if !ok { + t.Fatal("Unexpected error occurred") + } + + expected := ast.MustParseTerm(`{"foo": "bar", "baz": "blah"}`).Value + + if expected.Compare(result) != 0 { + t.Fatalf("Expected %v but got %v", expected, result) + } +} + +func TestMergeOverlappingKeys(t *testing.T) { + t.Parallel() + + realData := ast.MustParseTerm(`{"foo": "bar"}`).Value.(ast.Object) + mockData := ast.MustParseTerm(`{"foo": "blah"}`).Value.(ast.Object) + + result, ok := merge(mockData, realData) + if !ok { + t.Fatal("Unexpected error occurred") + } + + expected := ast.MustParseTerm(`{"foo": "blah"}`).Value + if expected.Compare(result) != 0 { + t.Fatalf("Expected %v but got %v", expected, result) + } + + realData = ast.MustParseTerm(`{"foo": {"foo1": {"foo11": [1,2,3], "foo12": "hello"}}, "bar": "baz"}`).Value.(ast.Object) + mockData = ast.MustParseTerm(`{"foo": {"foo1": [1,2,3], "foo12": "world", "foo13": 123}, "baz": "bar"}`).Value.(ast.Object) + + result, ok = merge(mockData, realData) + if !ok { + t.Fatal("Unexpected error occurred") + } + + expected = ast.MustParseTerm(`{"foo": {"foo1": [1,2,3], "foo12": "world", "foo13": 123}, "bar": "baz", "baz": "bar"}`).Value + if expected.Compare(result) != 0 { + t.Fatalf("Expected %v but got %v", expected, result) + } + +} + +func TestMergeWhenHittingNonObject(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + real, mock, exp *ast.Term + }{ + { + note: "real object, mock string", + real: ast.MustParseTerm(`{"foo": "bar"}`), + mock: ast.StringTerm("foo"), + exp: ast.StringTerm("foo"), + }, + { + note: "real string, mock object", + real: ast.StringTerm("foo"), + mock: ast.MustParseTerm(`{"foo": "bar"}`), + exp: ast.MustParseTerm(`{"foo": "bar"}`), + }, + { + note: "real object with string value, where mock has object-value", + real: ast.MustParseTerm(`{"foo": ["bar"], "quz": false}`), + mock: ast.MustParseTerm(`{"foo": {"bar": 123}}`), + exp: ast.MustParseTerm(`{"foo": {"bar": 123}, "quz": false}`), + }, + { + note: "real object with deeply-nested object value, where mock has number-value", + real: ast.MustParseTerm(`{"foo": {"bar": {"baz": "quz"}, "quz": true}}`), + mock: ast.MustParseTerm(`{"foo": {"bar": 10}}`), + exp: ast.MustParseTerm(`{"foo": {"bar": 10, "quz": true}}`), + }, + { + note: "real object with deeply-nested string value, where mock has object-value", + real: ast.MustParseTerm(`{"foo": {"bar": {"baz": "quz"}, "quz": true}}`), + mock: ast.MustParseTerm(`{"foo": {"bar": {"baz": {"foo": "bar"}}}}`), + exp: ast.MustParseTerm(`{"foo": {"bar": {"baz": {"foo": "bar"}}, "quz": true}}`), + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + merged, ok := merge(tc.mock.Value, tc.real.Value) + if !ok { + t.Fatal("expected no error") + } + if tc.exp.Value.Compare(merged) != 0 { + t.Errorf("Expected %v but got %v", tc.exp, merged) + } + }) + } +} + +func TestRefContainsNonScalar(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + ref ast.Ref + expected bool + }{ + { + note: "empty ref", + ref: ast.MustParseRef("data"), + expected: false, + }, + { + note: "string ref", + ref: ast.MustParseRef(`data.foo["bar"]`), + expected: false, + }, + { + note: "number ref", + ref: ast.MustParseRef("data.foo[1]"), + expected: false, + }, + { + note: "set ref", + ref: ast.MustParseRef("data.foo[{0}]"), + expected: true, + }, + { + note: "array ref", + ref: ast.MustParseRef(`data.foo[["bar"]]`), + expected: true, + }, + { + note: "object ref", + ref: ast.MustParseRef(`data.foo[{"bar": 1}]`), + expected: true, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + actual := refContainsNonScalar(tc.ref) + + if actual != tc.expected { + t.Errorf("Expected %t for %s", tc.expected, tc.ref) + } + }) + } + +} + +func TestContainsNestedRefOrCall(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + input string + want bool + }{ + { + note: "single term - negative", + input: "p[x]", + want: false, + }, + { + note: "single term - positive ref", + input: "p[q[x]]", + want: true, + }, + { + note: "single term - positive composite ref", + input: "[q[x]]", + want: true, + }, + { + note: "single term - positive composite call", + input: "[f(x)]", + want: true, + }, + { + note: "call expr - negative", + input: "f(x)", + want: false, + }, + { + note: "call expr - positive ref", + input: "f(p[x])", + want: true, + }, + { + note: "call expr - positive call", + input: "f(g(x))", + want: true, + }, + { + note: "call expr - positive composite", + input: "f([g(x)])", + want: true, + }, + { + note: "unify expr - negative", + input: "p[x] = q[y]", + want: false, + }, + { + note: "unify expr - positive ref", + input: "p[x] = q[r[y]]", + want: true, + }, + { + note: "unify expr - positive call", + input: "f(x) = g(h(y))", + want: true, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + vis := newNestedCheckVisitor() + expr := ast.MustParseExpr(tc.input) + result := containsNestedRefOrCall(vis, expr) + if result != tc.want { + t.Fatal("Expected", tc.want, "but got", result) + } + }) + } +} + +func TestTopdownVirtualCache(t *testing.T) { + t.Parallel() + + ctx := context.Background() + store := inmem.New() + + tests := []struct { + note string + module string + query string + hit, miss uint64 + exp any // if non-nil, check var `x` + }{ + { + note: "different args", + module: `package p + f(0) = 1 + f(x) = 12 if { x > 0 }`, + query: `data.p.f(0); data.p.f(1)`, + hit: 0, + miss: 2, + }, + { + note: "same args", + module: `package p + f(0) = 1 + f(x) = 12 if { x > 0 }`, + query: `data.p.f(1); data.p.f(1)`, + hit: 1, + miss: 1, + }, + { + note: "captured output", + module: `package p + f(0) = 1 + f(x) = 12 if { x > 0 }`, + query: `data.p.f(0); data.p.f(0, x)`, + hit: 1, + miss: 1, + exp: 1, + }, + { + note: "captured output, bool(false) result", + module: `package p + g(x) = true if { x > 0 } + g(x) = false if { x <= 0 }`, + query: `data.p.g(-1, x); data.p.g(-1, y)`, + hit: 1, + miss: 1, + exp: false, + }, + { + note: "same args, iteration case", + module: `package p + f(0) = 1 + f(x) = 12 if { x > 0 } + q = y if { + x := f(1) + y := f(1) + x == y + }`, + query: `x = data.p.q`, + hit: 1, + miss: 2, // one for q, one for f(1) + exp: 12, + }, + { + note: "cache invalidation", + module: `package p + f(x) = y if { x+input = y }`, + query: `data.p.f(1, z) with input as 7; data.p.f(1, z2) with input as 8`, + hit: 0, + miss: 2, + }, + { + note: "partial object: simple", + module: `package p + s["foo"] = true if { true } + s["bar"] = true if { true }`, + query: `data.p.s["foo"]; data.p.s["foo"]`, + hit: 1, + miss: 1, + }, + { + note: "partial object: query into object value", + module: `package p + s["foo"] = { "x": 42, "y": 43 } if { true } + s["bar"] = { "x": 42, "y": 43 } if { true }`, + query: `data.p.s["foo"].x = x; data.p.s["foo"].y`, + hit: 1, + miss: 1, + exp: 42, + }, + { + note: "partial object: simple, general ref", + module: `package p + s.t[u].v = true if { x = ["foo", "bar"]; u = x[_] }`, + query: `data.p.s.t["foo"].v = x; data.p.s.t["foo"].v`, + hit: 1, + miss: 1, + exp: true, + }, + { + note: "partial object: simple, general ref, multiple vars", + module: `package p + s.t[u].v[w] = true if { x = ["foo", "bar"]; u = x[_]; y = ["do", "re"]; w = y[_] }`, + query: `data.p.s.t = x; data.p.s.t`, + hit: 1, + miss: 1, + exp: map[string]any{ + "foo": map[string]any{ + "v": map[string]any{ + "do": true, + "re": true, + }, + }, + "bar": map[string]any{ + "v": map[string]any{ + "do": true, + "re": true, + }, + }, + }, + }, + { + note: "partial object: simple, general ref, multiple vars (2)", + module: `package p + s.t[u].v[w] = true if { x = ["foo", "bar"]; u = x[_]; y = ["do", "re"]; w = y[_] }`, + query: `data.p.s.t.foo = x; data.p.s.t["foo"]`, + hit: 1, + miss: 1, + exp: map[string]any{ + "v": map[string]any{ + "do": true, + "re": true, + }, + }, + }, + { + note: "partial object: simple, general ref, multiple vars (3)", + module: `package p + s.t[u].v[w] = true if { x = ["foo", "bar"]; u = x[_]; y = ["do", "re"]; w = y[_] }`, + query: `data.p.s.t.foo.v = x; data.p.s.t["foo"].v`, + hit: 1, + miss: 1, + exp: map[string]any{ + "do": true, + "re": true, + }, + }, + { + note: "partial object: simple, general ref, multiple vars (4)", + module: `package p + s.t[u].v[w] = true if { x = ["foo", "bar"]; u = x[_]; y = ["do", "re"]; w = y[_] }`, + query: `data.p.s.t.foo.v.re = x; data.p.s.t["foo"].v["re"]`, + hit: 1, + miss: 1, + exp: true, + }, + { + note: "partial object: simple, general ref, miss", + module: `package p + s.t[u].v[w] = true if { x = ["foo", "bar"]; u = x[_]; y = ["do", "re"]; w = y[_] }`, + query: `data.p.s.t.foo.v.re = x; data.p.s.t.foo.v.do`, + hit: 0, + miss: 2, + exp: true, + }, + { + note: "partial object: simple, general ref, miss (2)", + module: `package p + s.t[u].v[w] = i if { x = ["foo", "bar"]; u = x[_]; y = ["do", "re"]; w = y[i] }`, + query: `data.p.s.t.foo.v.re = x; data.p.s.t.foo.v.do; data.p.s.t.foo.v.re`, + hit: 1, + miss: 2, + exp: 1, + }, + { + note: "partial object: simple, general ref, miss (3)", + module: `package p + s.t[u].v[w] = i if { x = ["foo", "bar"]; u = x[_]; y = ["do", "re"]; w = y[i] }`, + query: `data.p.s.t.foo.v.re = x; data.p.s.t.foo.v.do; data.p.s.t.bar.v.re`, + hit: 0, + miss: 3, + exp: 1, + }, + { + note: "partial object: simple, general ref, miss (3)", + module: `package p + s.t[u].v[w] = i if { x = ["foo", "bar"]; u = x[_]; y = ["do", "re"]; w = y[i] }`, + query: `data.p.s.t.foo.v.re = x; data.p.s.t.foo.v.do; data.p.s.t.bar.v.re; data.p.s.t.foo.v.do`, + hit: 1, + miss: 3, + exp: 1, + }, + { + note: "partial object: simple, general ref, miss (4)", + module: `package p + s.t[u].v[w] = i if { x = ["foo", "bar"]; u = x[_]; y = ["do", "re"]; w = y[i] }`, + query: `data.p.s.t.foo = x; data.p.s.t.foo.v.do`, + hit: 1, + miss: 1, + exp: map[string]any{ + "v": map[string]any{ + "do": 0, + "re": 1, + }, + }, + }, + { + note: "partial object: simple, general ref, miss (5)", + module: `package p + s.t[u].v[w] = i if { x = ["foo", "bar"]; u = x[_]; y = ["do", "re"]; w = y[i] }`, + query: `data.p.s.t.foo; data.p.s.t.foo.v.do = x`, + hit: 1, + miss: 1, + exp: 0, + }, + { + note: "partial object: simple, general ref, miss (6)", + module: `package p + s.t[u].v[w] = i if { x = ["foo", "bar"]; u = x[_]; y = ["do", "re"]; w = y[i] }`, + query: `data.p.s.t.foo.v.do = x; data.p.s.t.foo`, + hit: 0, // Note: Could we be smart in query term eval order to gain an extra hit here? + miss: 2, + exp: 0, + }, + { + note: "partial object: simple, query into value", + module: `package p + s["foo"].t = { "x": 42, "y": 43 } if { true } + s["bar"].t = { "x": 42, "y": 43 } if { true }`, + query: `data.p.s["foo"].t.x = x; data.p.s["foo"].t.x`, + hit: 1, + miss: 1, + exp: 42, + }, + { + note: "partial set: simple", + module: `package p + s contains "foo" if { true } + s contains "bar" if { true }`, + query: `data.p.s["foo"]; data.p.s["foo"]`, + hit: 1, + miss: 1, + }, + { + note: "partial set: object", + module: `package p + s contains z if { z := {"foo": "bar"} }`, + query: `x = {"foo": "bar"}; data.p.s[x]; data.p.s[x]`, + hit: 1, + miss: 1, + }, + { + note: "partial set: miss", + module: `package p + s contains z if { z = true }`, + query: `data.p.s[true]; not data.p.s[false]`, + hit: 0, + miss: 2, + }, + { + note: "partial set: full extent cached", + module: `package test + p contains x if { x = 1 } + p contains x if { x = 2 } + `, + query: "data.test.p = x; data.test.p = y", + hit: 1, + miss: 1, + }, + { + note: "partial set: all rules + each rule (non-ground var) cached", + module: `package test + p = r if { data.test.q = x; data.test.q[y] = z; data.test.q[a] = b; r := true } + q contains x if { x = 1 } + q contains x if { x = 2 } + `, + query: "data.test.p = true", + hit: 3, // 'data.test.q[y] = z' + 2x 'data.test.q[a] = b' + miss: 2, // 'data.test.p = true' + 'data.test.q = x' + }, + { + note: "partial set: all rules + each rule (non-ground composite) cached", + module: `package test + p if { data.test.q = x; data.test.q[[y, 1]] = z; data.test.q[[a, 2]] = b } + q contains [x, x] if { x = 1 } + q contains [x, x] if { x = 2 } + `, + query: "data.test.p = true", + hit: 2, // 'data.test.q[[y,1]] = z' + 'data.test.q[[a, 2]] = b' + miss: 2, // 'data.test.p = true' + 'data.test.q = x' + }, + { + note: "partial set: each rule (non-ground var), full extent cached", + module: `package test + p = r if { data.test.q[y] = z; data.test.q = x; r := true } + q contains x if { x = 1 } + q contains x if { x = 2 } + `, + query: "data.test.p = x", + hit: 2, // 2x 'data.test.q = x' + miss: 2, // 'data.test.p = true' + 'data.test.q[y] = z' + }, + { + note: "partial set: each rule (non-ground composite), full extent cached", + module: `package test + p = y if { data.test.q[[y, 1]] = z; data.test.q = x } + q contains [x, x] if { x = 1 } + q contains [x, x] if { x = 2 } + `, + query: "data.test.p = x", + hit: 0, + miss: 3, // 'data.test.p = true' + 'data.test.q[[y, 1]] = z' + 'data.test.q = x' + exp: 1, + }, + { + note: "partial object, ref-head, ref with unification scope", + module: `package test + + a[x][y][z] := x + y + z if { + some x in [1, 2] + some y in [3, 4] + some z in [5, 6] + } + + p if { + x := a[1][_][5] # miss, cache key: data.test.a[1][<_,5>] + some foo + y := a[1][foo][5] # hit, cache key: data.test.a[1][<_,5>] + x == y + }`, + query: `data.test.p = x`, + hit: 1, // data.test.a[1][_][5] + miss: 2, // data.test.p + data.test.a[1][_][5] + }, + { + note: "partial object, ref-head, ref with unification scope, component order", + module: `package test + + a[x][y][a][b] := i if { + some x in [1, 2] + some y in [3, 4] + some a in ["foo", "bar"] + some i, b in ["foo", "bar"] + } + + p if { + x := a[1][_]["foo"]["bar"] # miss, cache key: data.test.a[1][<_,foo,bar>] + y := a[1][_]["bar"]["foo"] # miss, cache key: data.test.a[1][<_,bar,foo>] + x != y + }`, + query: `data.test.p = x`, + hit: 0, + miss: 3, // data.test.p + data.test.a[1][<_,foo,bar>] + data.test.a[1][<_,bar,foo>] + }, + { + note: "partial object, ref-head, ref with unification scope, diverging key scope", + module: `package test + + a[x][y][z] := x + y + z if { + some x in [1, 2] + some y in [3, 4] + some z in [5, 6] + } + + p if { + x := a[1][_][5] # miss, cache key: data.test.a[1][<_,5>] + y := a[1][_][6] # miss, cache key: data.test.a[1][<_,6>] + z := a[1][_][5] # hit, cache key: data.test.a[1][<_,5>] + x != y + x == z + }`, + query: `data.test.p = x`, + hit: 1, // data.test.a[1][_][5] + miss: 3, // data.test.p + data.test.a[1][_][5] + data.test.a[1][_][6] + }, + { + note: "partial object, ref-head, ref with unification scope, trailing vars don't contribute to key scope", + module: `package test + + a[x][y][z][x] := x + y + z if { + some x in [1, 2] + some y in [3, 4] + some z in [5, 6] + } + + p if { + x := a[1][_][5][_] # miss, cache key: data.test.a[1][<_,5>] + y := a[1][_][5] # hit, cache key: data.test.a[1][<_,5>] + x == y[_] + }`, + query: `data.test.p = x`, + hit: 1, // data.test.a[1][_][5] + miss: 2, // data.test.p + data.test.a[1][_][5] + }, + { + // Regression test for https://github.com/open-policy-agent/opa/issues/6926 + note: "partial object, ref-head, leaf set, ref with unification scope", + module: `package p + + obj.sub[x][x] contains x if some x in ["one", "two"] + + obj[x][x] contains x if x := "whatever" + + main contains x if { + [1 | obj.sub[_].one[_]] # miss, cache key: data.p.obj.sub[<_,one>] + x := obj.sub[_][_][_] # miss, cache key: data.p.obj.sub + }`, + query: `data.p.main = x`, + hit: 0, + miss: 3, // data.p.main + data.p.obj.sub[<_,one>] + data.p.obj.sub + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + compiler := compileModules([]string{tc.module}) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + m := metrics.New() + + query := NewQuery(ast.MustParseBody(tc.query)). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithInstrumentation(NewInstrumentation(m)) + qrs, err := query.Run(ctx) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if exp, act := 1, len(qrs); exp != act { + t.Fatalf("expected %d query result, got %d query results: %+v", exp, act, qrs) + } + if tc.exp != nil { + x := ast.Var("x") + if exp, act := ast.NewTerm(ast.MustInterfaceToValue(tc.exp)), qrs[0][x]; !exp.Equal(act) { + t.Errorf("unexpected query result: want = %v, got = %v", exp, act) + } + } + + // check metrics + if exp, act := tc.hit, m.Counter(evalOpVirtualCacheHit).Value().(uint64); exp != act { + t.Errorf("expected %d cache hits, got %d", exp, act) + } + if exp, act := tc.miss, m.Counter(evalOpVirtualCacheMiss).Value().(uint64); exp != act { + t.Errorf("expected %d cache misses, got %d", exp, act) + } + }) + } +} + +func TestPartialRule(t *testing.T) { + t.Parallel() + + ctx := context.Background() + store := inmem.New() + + tests := []struct { + note string + module string + query string + exp string + expErr string + }{ + { + note: "partial set", + module: `package test + p contains v if { + v := [1, 2, 3][_] + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": [1, 2, 3]}}}]`, + }, + { + note: "partial object", + module: `package test + p[i] := v if { + v := [1, 2, 3][i] + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"0": 1, "1": 2, "2": 3}}}}]`, + }, + { + note: "partial object (const key)", + module: `package test + p["foo"] := v if { + v := 42 + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"foo": 42}}}}]`, + }, + { + note: "ref head", + module: `package test + p.foo := v if { + v := 42 + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"foo": 42}}}}]`, + }, + { + note: "partial object (ref head)", + module: `package test + p.q.r[i] := v if { + v := ["a", "b", "c"][i] + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"q": {"r": {"0": "a", "1": "b", "2": "c"}}}}}}]`, + }, + { + note: "partial object (ref head), query to obj root", + module: `package test + p.q.r[i] := v if { + v := ["a", "b", "c"][i] + } + `, + query: `data.test.p.q.r = x`, + exp: `[{"x": {"0": "a", "1": "b", "2": "c"}}]`, + }, + { + note: "partial object (ref head), query to obj root, enumerating keys", + module: `package test + p.q.r[i] := v if { + v := ["a", "b", "c"][i] + } + `, + query: `data.test.p.q.r[x]`, + // NOTE: $_term_0_0 wildcard var is filtered from eval result output + exp: `[{"x": 0, "$_term_0_0": "a"}, {"x": 1, "$_term_0_0": "b"}, {"x": 2, "$_term_0_0": "c"}]`, + }, + { + note: "partial object (ref head), implicit 'true' value", + module: `package test + p.q.r[v] if { + v := [1, 2, 3][_] + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"q": {"r": {"1": true, "2": true, "3": true}}}}}}]`, + }, + { + note: "partial set (ref head)", + module: `package test + import future.keywords + p.q contains v if { + v := [1, 2, 3][_] + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"q": [1, 2, 3]}}}}]`, + }, + { + note: "partial set (general ref head)", + module: `package test + import future.keywords + p[j] contains v if { + v := [1, 2, 3][_] + j := ["a", "b", "c"][_] + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"a": [1, 2, 3], "b": [1, 2, 3], "c": [1, 2, 3]}}}}]`, + }, + { + note: "partial set (general ref head, static suffix)", + module: `package test + import future.keywords + p[q].r contains v if { + q := "foo" + v := [1, 2, 3][_] + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"foo": {"r": [1, 2, 3]}}}}}]`, + }, + { + note: "partial object (general ref head, multiple vars)", + module: `package test + p.q[x].r[i] := v if { + some i + v := [1, 2, 3][i] + x := ["a", "b", "c"][_] + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"q": {"a": {"r": {"0": 1, "1": 2, "2": 3}}, "b": {"r": {"0": 1, "1": 2, "2": 3}}, "c": {"r": {"0": 1, "1": 2, "2": 3}}}}}}}]`, + }, + { + note: "partial object (general ref head, multiple vars) #2", + module: `package test + p[j].foo[i] := v if { + v := [1, 2, 3][i] + j := ["a", "b", "c"][_] + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"a": {"foo": {"0": 1, "1": 2, "2": 3}}, "b": {"foo": {"0": 1, "1": 2, "2": 3}}, "c": {"foo": {"0": 1, "1": 2, "2": 3}}}}}}]`, + }, + { + note: "partial set (multiple vars in general ref head)", + module: `package test + import future.keywords + p[j][i] contains v if { + v := [1, 2, 3][_] + j := ["a", "b", "c"][_] + i := "foo" + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"a": {"foo": [1, 2, 3]}, "b": {"foo": [1, 2, 3]}, "c": {"foo": [1, 2, 3]}}}}}]`, + }, + // Overlapping rules + { + note: "partial object with overlapping rule (defining key/value in object)", + module: `package test + foo.bar[i] := v if { + v := ["a", "b", "c"][i] + } + foo.bar.baz := 42 + `, + query: `data = x`, + exp: `[{"x": {"test": {"foo": {"bar": {"0": "a", "1": "b", "2": "c", "baz": 42}}}}}]`, + }, + { + note: "partial object with overlapping rule (dee ref on overlap)", + module: `package test + p[k] := 1 if { + k := "foo" + } + p.q.r.s.t := 42 + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"foo": 1, "q": {"r": {"s": {"t": 42}}}}}}}]`, + }, + { + note: "partial object with overlapping rule (dee ref on overlap; conflict)", + module: `package test + p[k] := 1 if { + k := "q" + } + p.q.r.s.t := 42 + `, + query: `data = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object with overlapping rule (key conflict)", + module: `package test + foo.bar[k] := v if { + k := "a" + v := 43 + } + foo.bar["a"] := 42 + `, + query: `data = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object generating conflicting nested keys (different nested object depth)", + module: `package test + p.q.r if { + true + } + p.q[r].s.t if { + r := "foo" + }`, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"q": {"foo": {"s": {"t": true}}, "r": true}}}}}]`, + }, + { + note: "partial object generating conflicting nested keys (different nested object depth; key conflict)", + module: `package test + p.q[k].s := 1 if { + k := "r" + } + p.q[k].s.t := 1 if { + k := "r" + }`, + query: `data = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object (overlapping rules producing same values)", + module: `package test + p.foo.bar[i] := v if { + v := ["a", "b", "c"][i] + } + p.foo[i][j] := v if { + i := "bar" + v := ["a", "b", "c"][j] + } + p[q][i][j] := v if { + q := "foo" + i := "bar" + v := ["a", "b", "c"][j] + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"foo": {"bar": {"0": "a", "1": "b", "2": "c"}}}}}}]`, + }, + { + note: "partial object (overlapping rules, same depth, producing non-conflicting keys)", + module: `package test + p.foo[i].bar := v if { + v := ["a", "b", "c"][i] + } + p.foo.bar[i] := v if { + v := ["a", "b", "c"][i] + } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"foo": { + "0": {"bar": "a"}, + "1": {"bar": "b"}, + "2": {"bar": "c"}, + "bar": {"0": "a", "1": "b", "2": "c"}}}}}}]`, + }, + // Intersections with object values + { + note: "partial object NOT intersecting with object value of other rule", + module: `package test + p.foo := {"bar": {"baz": 1}} + p[k] := 2 if {k := "other"} + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"foo": {"bar": {"baz": 1}}, "other": 2}}}}]`, + }, + { + note: "partial object NOT intersecting with object value of other rule (nested object merge along rule refs)", + module: `package test + p.foo.bar := {"baz": 1} # p.foo.bar == {"baz": 1} + p[k].bar2 := v if {k := "foo"; v := {"other": 2}} # p.foo.bar2 == {"other": 2} + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"foo": {"bar": {"baz": 1}, "bar2": {"other": 2}}}}}}]`, + }, + { + note: "partial object intersecting with object value of other rule (not merging otherwise conflict-free obj values)", + module: `package test + p.foo := {"bar": {"baz": 1}} # p == {"foo": {"bar": {"baz": 1}}} + p[k] := v if {k := "foo"; v := {"bar": {"other": 2}}} # p == {"foo": {"bar": {"other": 2}}} + `, + query: `data = x`, + expErr: "eval_conflict_error: object keys must be unique", // conflict on key "bar" which is inside rule values, which may not be modified by other rule + }, + { + note: "partial object rules with overlapping known ref vars (no eval-time conflict)", + module: `package test + p[k].r1 := 1 if { k := "q" } + p[k].r2 := 2 if { k := "q" } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"q": {"r1": 1, "r2": 2}}}}}]`, + }, + { + note: "partial object rules with overlapping known ref vars (eval-time conflict)", + module: `package test + p[k].r := 1 if { k := "q" } + p[k].r := 2 if { k := "q" } + `, + query: `data = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object rules with overlapping known ref vars, non-overlapping object type values (eval-time conflict)", + module: `package test + p[k].r := {"s1": 1} if { k := "q" } + p[k].r := {"s2": 2} if { k := "q" } + `, + query: `data = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object rule with object value containing var", + module: `package test + p.foo.q[y] := {"x": z} if { y := ["bar", "baz"][_]; z := 4 } + `, + query: `data = x`, + exp: `[{"x": {"test": {"p": {"foo": {"q": {"bar": {"x": 4}, "baz": {"x": 4}}}}}}}]`, + }, + { + note: "partial object rule with object value and intersecting key override rule (regression test #6211)", + module: `package test + p.foo.q[y] := {"x": 7} if { y := ["bar", "baz"][_] } + p.foo.q.baz.y = 9 if { true } + `, + query: `data = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object rule with object value and intersecting key override rule (query up to partial object) (regression test #6211)", + module: `package test + p.foo.q[y] := {"x": 7} if { y := ["bar", "baz"][_] } + p.foo.q.baz.y = 9 if { true } + `, + query: `data = x.test.p.foo.q`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object rule with object value and intersecting key override rule (query into partial object) (regression test #6211)", + module: `package test + p.foo.q[y] := {"x": 7} if { y := ["bar", "baz"][_] } + p.foo.q.baz.y = 9 if { true } + `, + query: `data.test.p.foo.q.bar = x`, + exp: `[{"x": {"x": 7}}]`, + }, + { + note: "partial object rule with object value and intersecting key override rule (query into key override) (regression test #6211)", + module: `package test + p.foo.q[y] := {"x": 7} if { y := ["bar", "baz"][_] } + p.foo.q.baz.y = 9 if { true } + `, + query: `data.test.p.foo.q.baz = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object rule with object value and intersecting key override rule (query into partial object, enumeration) (regression test #6211)", + module: `package test + p.foo.q[y] := {"x": 7} if { y := ["bar", "baz"][_] } + p.foo.q.baz.y = 9 if { true } + `, + query: `data.test.p.foo.q[z] = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object rule with object value and intersecting key override rule (query into partial object, enumeration #2) (regression test #6211)", + module: `package test + p.foo.q[y] := {"x": 7} if { y := ["bar", "baz"][_] } + p.foo.q.baz.y = 9 if { true } + `, + query: `data.test.p.foo.q[z].x = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object rule with object value and intersecting key override rule (query into key override, enumeration) (regression test #6211)", + module: `package test + p.foo.q[y] := {"x": 7} if { y := ["bar", "baz"][_] } + p.foo.q.baz.y = 9 if { true } + `, + query: `data.test.p.foo.q.baz[z] = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + // Deep queries + { + note: "deep query into partial object (ref head)", + module: `package test + p.q[r] := 1 if { r := "foo" } + `, + query: `data.test.p.q.foo = x`, + exp: `[{"x": 1}]`, + }, + { + note: "deep query into partial object (ref head) and object value", + module: `package test + p.q[r] := x if { + r := "foo" + x := {"bar": {"baz": 1}} + } + `, + query: `data.test.p.q.foo.bar = x`, + exp: `[{"x": {"baz": 1}}]`, + }, + { + note: "deep query into partial object starting-point (general ref head) up to array value", + module: `package test + p.q[r].s[t].u := x if { + obj := { + "foo": { + "do": ["a", "b", "c"], + "re": ["d", "e", "f"], + }, + "bar": { + "mi": ["g", "h", "i"], + "fa": ["j", "k", "l"], + } + } + x := obj[r][t] + } + `, + query: `data.test.p.q = x`, + exp: `[{"x": {"bar": {"s": {"fa": {"u": ["j", "k", "l"]}, "mi": {"u": ["g", "h", "i"]}}}, "foo": {"s": {"do": {"u": ["a", "b", "c"]}, "re": {"u": ["d", "e", "f"]}}}}}]`, + }, + { + note: "deep query into partial object mid-point (general ref head) up to array value", + module: `package test + p.q[r].s[t].u := x if { + obj := { + "foo": { + "do": ["a", "b", "c"], + "re": ["d", "e", "f"], + }, + "bar": { + "mi": ["g", "h", "i"], + "fa": ["j", "k", "l"], + } + } + x := obj[r][t] + } + `, + query: `data.test.p.q.bar.s = x`, + exp: `[{"x": {"fa": {"u": ["j", "k", "l"]}, "mi": {"u": ["g", "h", "i"]}}}]`, + }, + { + note: "deep query into partial object (general ref head) up to array value", + module: `package test + p.q[r].s[t].u := x if { + obj := { + "foo": { + "do": ["a", "b", "c"], + "re": ["d", "e", "f"], + }, + "bar": { + "mi": ["g", "h", "i"], + "fa": ["j", "k", "l"], + } + } + x := obj[r][t] + } + `, + query: `data.test.p.q.bar.s.mi.u = x`, + exp: `[{"x": ["g", "h", "i"]}]`, + }, + { + note: "deep query into partial object (general ref head) and array value", + module: `package test + p.q[r].s[t].u := x if { + obj := { + "foo": { + "do": ["a", "b", "c"], + "re": ["d", "e", "f"], + }, + "bar": { + "mi": ["g", "h", "i"], + "fa": ["j", "k", "l"], + } + } + x := obj[r][t] + } + `, + query: `data.test.p.q.foo.s.re.u[1] = x`, + exp: `[{"x": "e"}]`, + }, + { + note: "query up to (ref head), but not into partial set", + module: `package test + import future.keywords + p.q.r contains s if { {"foo", "bar", "bax"}[s] } + `, + query: `data.test.p = x`, + exp: `[{"x": {"q": {"r": ["bar", "bax", "foo"]}}}]`, + }, + { + note: "deep query up to (ref mid-point), but not into partial set", + module: `package test + import future.keywords + p.q.r contains s if { {"foo", "bar", "bax"}[s] } + `, + query: `data.test.p.q = x`, + exp: `[{"x": {"r": ["bar", "bax", "foo"]}}]`, + }, + { + note: "deep query up to (ref tail), but not into partial set", + module: `package test + import future.keywords + p.q.r contains s if { {"foo", "bar", "bax"}[s] } + `, + query: `data.test.p.q.r = x`, + exp: `[{"x": ["bar", "bax", "foo"]}]`, + }, + { + note: "deep query into partial set", + module: `package test + import future.keywords + p.q contains r if { {"foo", "bar", "bax"}[r] } + `, + query: `data.test.p.q.foo = x`, + exp: `[{"x": "foo"}]`, + }, + { // enumeration + note: "deep query into partial object and object value, full depth, enumeration on object value", + module: `package test + p.q[r] := x if { + r := ["foo", "bar"][_] + x := {"s": {"do": 0, "re": 1, "mi": 2}} + } + `, + query: `data.test.p.q.bar.s[y] = z`, + exp: `[{"y": "do", "z": 0}, {"y": "re", "z": 1}, {"y": "mi", "z": 2}]`, + }, + { // enumeration + note: "deep query into partial object and object value, full depth, enumeration on rule path and object value", + module: `package test + p.q[r] := x if { + r := ["foo", "bar"][_] + x := {"s": {"do": 0, "re": 1, "mi": 2}} + } + `, + query: `data.test.p.q[x].s[y] = z`, + exp: `[{"x": "foo", "y": "do", "z": 0}, {"x": "foo", "y": "re", "z": 1}, {"x": "foo", "y": "mi", "z": 2}, {"x": "bar", "y": "do", "z": 0}, {"x": "bar", "y": "re", "z": 1}, {"x": "bar", "y": "mi", "z": 2}]`, + }, + { + note: "deep query into partial object (ref head) and set value", + module: `package test + import future.keywords + p.q contains t if { + {"do", "re", "mi"}[t] + } + `, + query: `data.test.p.q.re = x`, + exp: `[{"x": "re"}]`, + }, + { + note: "deep query into partial object (general ref head) and set value", + module: `package test + import future.keywords + p.q[r] contains t if { + r := ["foo", "bar"][_] + {"do", "re", "mi"}[t] + } + `, + query: `data.test.p.q.foo.re = x`, + exp: `[{"x": "re"}]`, + }, + { + note: "deep query into partial object (general ref head, static tail) and set value", + module: `package test + import future.keywords + p.q[r].s contains t if { + r := ["foo", "bar"][_] + {"do", "re", "mi"}[t] + } + `, + query: `data.test.p.q.foo.s.re = x`, + exp: `[{"x": "re"}]`, + }, + { + note: "deep query into general ref to set value", + module: `package test + import future.keywords + p.q[r].s contains t if { + r := ["foo", "bar"][_] + t := ["do", "re", "mi"][_] + } + `, + query: `data.test.p.q.foo.s = x`, + exp: `[{"x": ["do", "mi", "re"]}]`, // FIXME: set ordering makes this test brittle + }, + { + note: "deep query into general ref to object value", + module: `package test + p.q[r].s[t] := u if { + r := ["foo", "bar"][_] + t := ["do", "re", "mi"][u] + } + `, + query: `data.test.p.q.foo.s = x`, + exp: `[{"x": {"do": 0, "re": 1, "mi": 2}}]`, + }, + { + note: "deep query into general ref enumerating set values", + module: `package test + import future.keywords + p.q[r].s contains t if { + r := ["foo", "bar"][_] + {"do", "re", "mi"}[t] + } + `, + query: `data.test.p.q.foo.s[x]`, + // NOTE: $_term_0_0 wildcard var is filtered from eval result output + exp: `[{"$_term_0_0": "do", "x": "do"}, {"$_term_0_0": "re", "x": "re"}, {"$_term_0_0": "mi", "x": "mi"}]`, + }, + { + note: "deep query into partial object and object value, non-tail var", + module: `package test + p.q[r].s := x if { + r := "foo" + x := {"bar": {"baz": 1}} + } + `, + query: `data.test.p.q.foo.s.bar = x`, + exp: `[{"x": {"baz": 1}}]`, + }, + { + note: "deep query into partial object, on first var in ref", + module: `package test + p.q[r].s := 1 if { r := "foo" } + `, + query: `data.test.p.q.foo = x`, + exp: `[{"x": {"s": 1}}]`, + }, + { + note: "deep query into partial object, beyond first var in ref", + module: `package test + p.q[r].s := 1 if { r := "foo" } + `, + query: `data.test.p.q.foo.s = x`, + exp: `[{"x": 1}]`, + }, + { + note: "deep query into partial object, shallow rule ref", + module: `package test + p.q[r][s] := 1 if { r := "foo"; s := "bar" } + `, + query: `data.test.p.q.foo = x`, + exp: `[{"x": {"bar": 1}}]`, + }, + { + note: "deep query into partial object, shallow rule ref, multiple keys", + module: `package test + p.q[r][s] := t if { l := ["do", "re", "mi"]; r := "foo"; s := l[t] } + `, + query: `data.test.p.q.foo = x`, + exp: `[{"x": {"do": 0, "re": 1, "mi": 2}}]`, + }, + { + note: "deep query into partial object, beyond first var in ref, multiple vars", + module: `package test + p.q[r][s] := 1 if { r := "foo"; s := "bar" } + `, + query: `data.test.p.q.foo.bar = x`, + exp: `[{"x": 1}]`, + }, + { + note: "deep query into partial object, beyond first var in ref, multiple vars", + module: `package test + p.q[r][s].t := 1 if { r := "foo"; s := "bar" } + `, + query: `data.test.p.q.foo.bar = x`, + exp: `[{"x": {"t": 1}}]`, + }, + { + note: "deep query to partial object, overlapping rules (key override), no dynamic ref", + module: `package test + p.q[r] := 1 if { r := "foo" } + p.q.r := 2 + `, + query: `data.test.p.q = x`, + exp: `[{"x": {"foo": 1, "r": 2}}]`, + }, + { + note: "deep query into partial object, overlapping rules (key override), no dynamic ref", + module: `package test + p.q[r] := 1 if { r := "foo" } + p.q.r := 2 + `, + query: `data.test.p.q.r = x`, + exp: `[{"x": 2}]`, + }, + { + note: "deep query into partial object, overlapping rules, no dynamic ref", + module: `package test + p.q[r] := 1 if { r := "foo" } + p.q[r] := 2 if { r := "bar" } + `, + query: `data.test.p.q.foo = x`, + exp: `[{"x": 1}]`, + }, + { + note: "deep query into partial object, overlapping rules with same key/value, no dynamic ref", + module: `package test + p.q[r] := 1 if { r := "foo" } + p.q[r] := 1 if { r := "foo" } + `, + query: `data.test.p.q.foo = x`, + exp: `[{"x": 1}]`, + }, + { + note: "deep query into partial object, overlapping rules, dynamic ref", + module: `package test + p.q[r].s := 1 if { r := "r" } + p.q.r[s] := 2 if { s := "foo" } + `, + query: `data.test.p.q.r = x`, + exp: `[{"x": {"s": 1, "foo": 2}}]`, + }, + { + note: "deep query into partial object, overlapping rules with same key/value, dynamic ref", + module: `package test + p.q[r].s := 1 if { r := "r" } + p.q.r[s] := 1 if { s := "s" } + `, + query: `data.test.p.q.r = x`, + exp: `[{"x": {"s": 1}}]`, + }, + // Multiple results (enumeration) + { + note: "shallow query into general ref, key enumeration", + module: `package test + p.q[r].s[t] := u if { + r := ["a", "b", "c"][_] + t := ["d", "e", "f"][u] + }`, + query: `data.test.p.q[x] = y`, + exp: `[{"x": "a", "y": {"s": {"d": 0, "e": 1, "f": 2}}}, + {"x": "b", "y": {"s": {"d": 0, "e": 1, "f": 2}}}, + {"x": "c", "y": {"s": {"d": 0, "e": 1, "f": 2}}}]`, + }, + { + note: "query to partial object, overlapping rules, dynamic ref, key enumeration", + module: `package test + p.q[r].s := 1 if { r := "foo" } + p.q[r].s := 2 if { r := "bar" } + `, + query: `data.test.p.q[i] = x`, + exp: `[{"i": "bar", "x": {"s": 2}}, {"i": "foo", "x": {"s": 1}}]`, + }, + { + note: "deep query into partial object, overlapping rules, dynamic ref, key enumeration", + module: `package test + p.q[r].s := 1 if { r := "foo" } + p.q[r].s := 2 if { r := "bar" } + `, + query: `data.test.p.q[i].s = x`, + exp: `[{"i": "bar", "x": 2}, {"i": "foo", "x": 1}]`, + }, + // Errors + { + note: "partial object generating conflicting keys", + module: `package test + p[k] := x if { + k := "foo" + x := [1, 2][_] + }`, + query: `data = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object (ref head) generating conflicting keys (dots in head)", + module: `package test + p.q[k] := x if { + k := "foo" + x := [1, 2][_] + }`, + query: `data = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object (general ref head) generating conflicting nested keys", + module: `package test + p.q[k].s := x if { + k := "foo" + x := [1, 2][_] + }`, + query: `data = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + { + note: "partial object (general ref head) generating conflicting ref vars", + module: `package test + p.q[k].s := x if { + k := ["foo", "foo"][x] + }`, + query: `data = x`, + expErr: "eval_conflict_error: object keys must be unique", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + compiler := compileModules([]string{tc.module}) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + query := NewQuery(ast.MustParseBody(tc.query)). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn) + + qrs, err := query.Run(ctx) + if tc.expErr != "" { + if err == nil { + t.Fatalf("Expected error %v but got result: %v", tc.expErr, qrs) + } + if exp, act := tc.expErr, err.Error(); !strings.Contains(act, exp) { + t.Fatalf("Expected error %v but got: %v", exp, act) + } + } else { + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + var exp []map[string]any + if err := json.Unmarshal([]byte(tc.exp), &exp); err != nil { + t.Fatal("Failed to unmarshal exp") + } + if expLen, act := len(exp), len(qrs); expLen != act { + t.Fatalf("expected %d query result:\n\n%+v,\n\ngot %d query results:\n\n%+v", expLen, exp, act, qrs) + } + testAssertResultSet(t, exp, qrs, false) + } + }) + } +} + +type deadlineCtx struct{} + +func (*deadlineCtx) Err() error { + return context.DeadlineExceeded +} + +func (*deadlineCtx) Deadline() (time.Time, bool) { + return time.Now(), false +} + +func (*deadlineCtx) Value(_ any) any { + return nil +} + +func (*deadlineCtx) Done() <-chan struct{} { + return nil +} + +func TestContextErrorHandling(t *testing.T) { + t.Parallel() + + ctx := context.Background() + store := inmem.New() + + tests := []struct { + note string + before func() context.Context + module string + expErr string + expErrType error + }{ + { + note: "context deadline exceeded is handled", + before: func() context.Context { + var d deadlineCtx + return &d + }, + module: `package test + p contains v if { + v := [1, 2, 3][_] + } + `, + expErr: context.DeadlineExceeded.Error(), + expErrType: context.DeadlineExceeded, + }, + { + note: "context cancellation is handled", + before: func() context.Context { + ctx, cancel := context.WithCancel(ctx) + cancel() + return ctx + }, + module: `package test + p contains v if { + v := [1, 2, 3][_] + } + `, + expErr: context.Canceled.Error(), + expErrType: context.Canceled, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + compiler := compileModules([]string{tc.module}) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + c := NewCancel() + query := NewQuery(ast.MustParseBody("")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithCancel(c) + + testCtx := tc.before() + c.Cancel() + + qrs, err := query.Run(testCtx) + + if err == nil { + t.Fatalf("Expected error %v but got result: %v", tc.expErr, qrs) + } + if exp, act := tc.expErr, err.Error(); !strings.Contains(act, exp) { + t.Fatalf("Expected error %v but got: %v", exp, act) + } + + if et := tc.expErrType; et != nil && !errors.Is(err, tc.expErrType) { + t.Fatalf("Expected error to be of type %#v, but got %#v", et, err) + } + }) + } +} + +func TestFmtVarTerm(t *testing.T) { + e := &eval{ + genvarprefix: "foobar", + queryID: 12345, + index: 54321, + } + + res := e.fmtVarTerm() + + if res != "foobar_term_12345_54321" { + t.Fatalf("Expected foobar_term_12345_54321 but got %s", res) + } + + res = fmt.Sprintf("%s_term_%d_%d", e.genvarprefix, e.queryID, e.index) + + if res != "foobar_term_12345_54321" { + t.Fatalf("Expected foobar_term_12345_54321 but got %s", res) + } +} + +// Comparison with fmt.Sprintf: +// fmt.sprintf 8093799 159.41 ns/op 56 B/op 4 allocs/op +// formatVarTerm 20424126 50.95 ns/op 24 B/op 1 allocs/op +func BenchmarkFormatVarTerm(b *testing.B) { + e := &eval{ + genvarprefix: "foobar", + queryID: 12345, + index: 54321, + } + + for range b.N { + _ = e.fmtVarTerm() + } +} diff --git a/third_party/opa/v1/topdown/example_test.go b/third_party/opa/v1/topdown/example_test.go new file mode 100644 index 000000000000..7e676cea4053 --- /dev/null +++ b/third_party/opa/v1/topdown/example_test.go @@ -0,0 +1,303 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//revive:disable:empty-block + +package topdown_test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage/inmem" + "github.com/open-policy-agent/opa/v1/topdown" +) + +func ExampleQuery_Iter() { + // Initialize context for the example. Normally the caller would obtain the + // context from an input parameter or instantiate their own. + ctx := context.Background() + + compiler := ast.NewCompiler() + + // Define a dummy query and some data that the query will execute against. + query, err := compiler.QueryCompiler().Compile(ast.MustParseBody(`data.a[_] = x; x >= 2`)) + if err != nil { + // Handle error. + } + + var data map[string]any + + // OPA uses Go's standard JSON library but assumes that numbers have been + // decoded as json.Number instead of float64. You MUST decode with UseNumber + // enabled. + decoder := json.NewDecoder(bytes.NewBufferString(`{"a": [1,2,3,4]}`)) + decoder.UseNumber() + + if err := decoder.Decode(&data); err != nil { + // Handle error. + } + + // Instantiate the policy engine's storage layer. + store := inmem.NewFromObject(data) + + // Create a new transaction. Transactions allow the policy engine to + // evaluate the query over a consistent snapshot fo the storage layer. + txn, err := store.NewTransaction(ctx) + if err != nil { + // Handle error. + } + + defer store.Abort(ctx, txn) + + // Prepare the evaluation parameters. Evaluation executes against the policy + // engine's storage. In this case, we seed the storage with a single array + // of number. Other parameters such as the input, tracing configuration, + // etc. can be set on the query object. + q := topdown.NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn) + + result := []any{} + + // Execute the query and provide a callback function to accumulate the results. + err = q.Iter(ctx, func(qr topdown.QueryResult) error { + + // Each variable in the query will have an associated binding. + x := qr[ast.Var("x")] + + // The bindings are ast.Value types so we will convert to a native Go value here. + v, err := ast.JSON(x.Value) + if err != nil { + return err + } + + result = append(result, v) + return nil + }) + + // Inspect the query result. + fmt.Println("result:", result) + fmt.Println("err:", err) + + // Output: + // result: [2 3 4] + // err: +} + +func ExampleQuery_Run() { + // Initialize context for the example. Normally the caller would obtain the + // context from an input parameter or instantiate their own. + ctx := context.Background() + + compiler := ast.NewCompiler() + + // Define a dummy query and some data that the query will execute against. + query, err := compiler.QueryCompiler().Compile(ast.MustParseBody(`data.a[_] = x; x >= 2`)) + if err != nil { + // Handle error. + } + + var data map[string]any + + // OPA uses Go's standard JSON library but assumes that numbers have been + // decoded as json.Number instead of float64. You MUST decode with UseNumber + // enabled. + decoder := json.NewDecoder(bytes.NewBufferString(`{"a": [1,2,3,4]}`)) + decoder.UseNumber() + + if err := decoder.Decode(&data); err != nil { + // Handle error. + } + + // Instantiate the policy engine's storage layer. + store := inmem.NewFromObject(data) + + // Create a new transaction. Transactions allow the policy engine to + // evaluate the query over a consistent snapshot fo the storage layer. + txn, err := store.NewTransaction(ctx) + if err != nil { + // Handle error. + } + + defer store.Abort(ctx, txn) + + // Prepare the evaluation parameters. Evaluation executes against the policy + // engine's storage. In this case, we seed the storage with a single array + // of number. Other parameters such as the input, tracing configuration, + // etc. can be set on the query object. + q := topdown.NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn) + + rs, err := q.Run(ctx) + + // Inspect the query result set. + fmt.Println("len:", len(rs)) + for i := range rs { + fmt.Printf("rs[%d][\"x\"]: %v\n", i, rs[i]["x"]) + } + fmt.Println("err:", err) + + // Output: + // len: 3 + // rs[0]["x"]: 2 + // rs[1]["x"]: 3 + // rs[2]["x"]: 4 + // err: +} + +func ExampleQuery_PartialRun() { + + // Initialize context for the example. Normally the caller would obtain the + // context from an input parameter or instantiate their own. + ctx := context.Background() + + var data map[string]any + decoder := json.NewDecoder(bytes.NewBufferString(`{ + "roles": [ + { + "permissions": ["read_bucket"], + "groups": ["dev", "test", "sre"] + }, + { + "permissions": ["write_bucket", "delete_bucket"], + "groups": ["sre"] + } + ] + }`)) + if err := decoder.Decode(&data); err != nil { + // Handle error. + } + + // Instantiate the policy engine's storage layer. + store := inmem.NewFromObject(data) + + // Create a new transaction. Transactions allow the policy engine to + // evaluate the query over a consistent snapshot fo the storage layer. + txn, err := store.NewTransaction(ctx) + if err != nil { + // Handle error. + } + + defer store.Abort(ctx, txn) + + // Define policy that searches for roles that match input request. If no + // roles are found, allow is undefined and the caller will reject the + // request. This is the user supplied policy that OPA will partially + // evaluate. + modules := map[string]*ast.Module{ + "authz.rego": ast.MustParseModule(` + package example + import rego.v1 + + default allow = false + + allow if { + role = data.roles[i] + input.group = role.groups[j] + input.permission = role.permissions[k] + } + `), + } + + // Compile policy. + compiler := ast.NewCompiler() + if compiler.Compile(modules); compiler.Failed() { + // Handle error. + } + + // Construct query and mark the entire input document as partial. + q := topdown.NewQuery(ast.MustParseBody("data.example.allow = true")). + WithCompiler(compiler). + WithUnknowns([]*ast.Term{ + ast.MustParseTerm("input"), + }). + WithStore(store). + WithTransaction(txn) + + // Execute partial evaluation. + partial, _, err := q.PartialRun(ctx) + if err != nil { + // Handle error. + } + + // Show result of partially evaluating the policy. + fmt.Printf("# partial evaluation result (%d items):\n", len(partial)) + for i := range partial { + fmt.Println(partial[i]) + } + + // Construct a new policy to contain the result of partial evaluation. + module := ast.MustParseModule("package partial") + + for i := range partial { + rule := &ast.Rule{ + Head: &ast.Head{ + Name: ast.Var("allow"), + Value: ast.BooleanTerm(true), + }, + Body: partial[i], + Module: module, + } + module.Rules = append(module.Rules, rule) + } + + // Compile the partially evaluated policy with the original policy. + modules["partial"] = module + + if compiler.Compile(modules); compiler.Failed() { + // Handle error. + } + + // Test different inputs against partially evaluated policy. + inputs := []string{ + `{"group": "dev", "permission": "read_bucket"}`, // allow + `{"group": "dev", "permission": "write_bucket"}`, // deny + `{"group": "sre", "permission": "write_bucket"}`, // allow + } + + fmt.Println() + fmt.Println("# evaluation results:") + + for i := range inputs { + + // Query partially evaluated policy. + q = topdown.NewQuery(ast.MustParseBody("data.partial.allow = true")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithInput(ast.MustParseTerm(inputs[i])) + + qrs, err := q.Run(ctx) + if err != nil { + // Handle error. + } + + // Check if input is allowed. + allowed := len(qrs) == 1 + + fmt.Printf("input %d allowed: %v\n", i+1, allowed) + } + + // Output: + // + // # partial evaluation result (5 items): + // "dev" = input.group; "read_bucket" = input.permission + // "test" = input.group; "read_bucket" = input.permission + // "sre" = input.group; "read_bucket" = input.permission + // "sre" = input.group; "write_bucket" = input.permission + // "sre" = input.group; "delete_bucket" = input.permission + // + // # evaluation results: + // input 1 allowed: true + // input 2 allowed: false + // input 3 allowed: true + +} diff --git a/third_party/opa/v1/topdown/exported_test.go b/third_party/opa/v1/topdown/exported_test.go new file mode 100644 index 000000000000..86e34f854832 --- /dev/null +++ b/third_party/opa/v1/topdown/exported_test.go @@ -0,0 +1,212 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "fmt" + "os" + "sort" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/test/cases" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func TestRego(t *testing.T) { + t.Parallel() + + for _, tc := range cases.MustLoad("../test/cases/testdata/v0").Sorted().Cases { + t.Run("v0/"+tc.Note, func(t *testing.T) { + t.Parallel() + + testRun(t, tc, ast.RegoV0) + }) + } + for _, tc := range cases.MustLoad("../test/cases/testdata/v1").Sorted().Cases { + t.Run("v1/"+tc.Note, func(t *testing.T) { + t.Parallel() + + testRun(t, tc, ast.RegoV1) + }) + } +} + +func TestOPARego(t *testing.T) { + t.Parallel() + + for _, tc := range cases.MustLoad("testdata/cases").Sorted().Cases { + t.Run(tc.Note, func(t *testing.T) { + t.Parallel() + + testRun(t, tc, ast.RegoV0) + }) + } +} + +func TestRegoWithNDBCache(t *testing.T) { + t.Parallel() + + for _, tc := range cases.MustLoad("../test/cases/testdata/v0").Sorted().Cases { + t.Run("v0/"+tc.Note, func(t *testing.T) { + t.Parallel() + + testRun(t, tc, ast.RegoV0, func(q *Query) *Query { + return q.WithNDBuiltinCache(builtins.NDBCache{}) + }) + }) + } + for _, tc := range cases.MustLoad("../test/cases/testdata/v1").Sorted().Cases { + t.Run("v1/"+tc.Note, func(t *testing.T) { + t.Parallel() + + testRun(t, tc, ast.RegoV1, func(q *Query) *Query { + return q.WithNDBuiltinCache(builtins.NDBCache{}) + }) + }) + } +} + +type opt func(*Query) *Query + +func testRun(t *testing.T, tc cases.TestCase, regoVersion ast.RegoVersion, opts ...opt) { + + for k, v := range tc.Env { + t.Setenv(k, v) + } + + ctx := context.Background() + + modules := map[string]string{} + for i, module := range tc.Modules { + modules[fmt.Sprintf("test-%d.rego", i)] = module + } + + compiler := ast.MustCompileModulesWithOpts(modules, ast.CompileOpts{ + ParserOptions: ast.ParserOptions{ + RegoVersion: regoVersion, + }, + }) + query, err := compiler.QueryCompiler().Compile(ast.MustParseBody(tc.Query)) + + if err != nil { + t.Fatal(err) + } + + var store storage.Store + + if tc.Data != nil { + store = inmem.NewFromObject(*tc.Data) + } else { + store = inmem.New() + } + + txn := storage.NewTransactionOrDie(ctx, store) + + var input *ast.Term + + if tc.InputTerm != nil { + input = ast.MustParseTerm(*tc.InputTerm) + } else if tc.Input != nil { + input = ast.NewTerm(ast.MustInterfaceToValue(*tc.Input)) + } + + buf := NewBufferTracer() + q := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithInput(input). + WithStrictBuiltinErrors(tc.StrictError). + WithTracer(buf) + + for _, o := range opts { + q = o(q) + } + + rs, err := q.Run(ctx) + + if tc.WantError != nil { + testAssertErrorText(t, *tc.WantError, err) + } + + if tc.WantErrorCode != nil { + testAssertErrorCode(t, *tc.WantErrorCode, err) + } + + if err != nil && tc.WantErrorCode == nil && tc.WantError == nil { + t.Fatalf("unexpected error: %v", err) + } + + if tc.WantResult != nil { + testAssertResultSet(t, *tc.WantResult, rs, tc.SortBindings) + } + + if tc.WantResult == nil && tc.WantErrorCode == nil && tc.WantError == nil { + t.Fatal("expected one of: 'want_result', 'want_error_code', or 'want_error'") + } + + if testing.Verbose() { + PrettyTrace(os.Stderr, *buf) + } +} + +func testAssertResultSet(t *testing.T, wantResult []map[string]any, rs QueryResultSet, sortBindings bool) { + + exp := ast.NewSet() + + for _, b := range wantResult { + obj := ast.NewObject() + for k, v := range b { + obj.Insert(ast.StringTerm(k), ast.NewTerm(ast.MustInterfaceToValue(v))) + } + exp.Add(ast.NewTerm(obj)) + } + + got := ast.NewSet() + + for _, b := range rs { + obj := ast.NewObject() + for k, term := range b { + v, err := ast.JSON(term.Value) + if err != nil { + t.Fatal(err) + } + if sortBindings { + sort.Sort(resultSet(v.([]any))) + } + obj.Insert(ast.StringTerm(string(k)), ast.NewTerm(ast.MustInterfaceToValue(v))) + } + got.Add(ast.NewTerm(obj)) + } + + if exp.Compare(got) != 0 { + t.Fatalf("unexpected query result:\nexp: %v\ngot: %v", exp, got) + } +} + +func testAssertErrorCode(t *testing.T, wantErrorCode string, err error) { + e, ok := err.(*Error) + if !ok { + t.Fatal("expected topdown error but got:", err) + } + + if e.Code != wantErrorCode { + t.Fatalf("expected error code %q but got %q", wantErrorCode, e.Code) + } +} + +func testAssertErrorText(t *testing.T, wantText string, err error) { + if err == nil { + t.Fatal("expected error but got success") + } + if !strings.Contains(err.Error(), wantText) { + t.Fatalf("expected topdown error text %q but got: %q", wantText, err.Error()) + } +} diff --git a/third_party/opa/v1/topdown/glob.go b/third_party/opa/v1/topdown/glob.go new file mode 100644 index 000000000000..4e80c519ba92 --- /dev/null +++ b/third_party/opa/v1/topdown/glob.go @@ -0,0 +1,127 @@ +package topdown + +import ( + "strings" + "sync" + + "github.com/gobwas/glob" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +const globCacheMaxSize = 100 +const globInterQueryValueCacheHits = "rego_builtin_glob_interquery_value_cache_hits" + +var noDelimiters = []rune{} +var dotDelimiters = []rune{'.'} +var globCacheLock = sync.RWMutex{} +var globCache = map[string]glob.Glob{} + +func builtinGlobMatch(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + pattern, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + var delimiters []rune + switch operands[1].Value.(type) { + case ast.Null: + delimiters = noDelimiters + case *ast.Array: + delimiters, err = builtins.RuneSliceOperand(operands[1].Value, 2) + if err != nil { + return err + } + if len(delimiters) == 0 { + delimiters = dotDelimiters + } + default: + return builtins.NewOperandTypeErr(2, operands[1].Value, "array", "null") + } + + match, err := builtins.StringOperand(operands[2].Value, 3) + if err != nil { + return err + } + + builder := strings.Builder{} + builder.WriteString(string(pattern)) + builder.WriteRune('-') + for _, v := range delimiters { + builder.WriteRune(v) + } + id := builder.String() + + m, err := globCompileAndMatch(bctx, id, string(pattern), string(match), delimiters) + if err != nil { + return err + } + return iter(ast.InternedTerm(m)) +} + +func globCompileAndMatch(bctx BuiltinContext, id, pattern, match string, delimiters []rune) (bool, error) { + + if bctx.InterQueryBuiltinValueCache != nil { + // TODO: Use named cache + val, ok := bctx.InterQueryBuiltinValueCache.Get(ast.String(id)) + if ok { + pat, valid := val.(glob.Glob) + if !valid { + // The cache key may exist for a different value type (eg. regex). + // In this case, we calculate the glob and return the result w/o updating the cache. + var err error + if pat, err = glob.Compile(pattern, delimiters...); err != nil { + return false, err + } + return pat.Match(match), nil + } + bctx.Metrics.Counter(globInterQueryValueCacheHits).Incr() + out := pat.Match(match) + return out, nil + } + + res, err := glob.Compile(pattern, delimiters...) + if err != nil { + return false, err + } + bctx.InterQueryBuiltinValueCache.Insert(ast.String(id), res) + return res.Match(match), nil + } + + globCacheLock.RLock() + p, ok := globCache[id] + globCacheLock.RUnlock() + if !ok { + var err error + if p, err = glob.Compile(pattern, delimiters...); err != nil { + return false, err + } + globCacheLock.Lock() + if len(globCache) >= globCacheMaxSize { + // Delete a (semi-)random key to make room for the new one. + for k := range globCache { + delete(globCache, k) + break + } + } + globCache[id] = p + globCacheLock.Unlock() + } + + return p.Match(match), nil +} + +func builtinGlobQuoteMeta(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + pattern, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + return iter(ast.StringTerm(glob.QuoteMeta(string(pattern)))) +} + +func init() { + RegisterBuiltinFunc(ast.GlobMatch.Name, builtinGlobMatch) + RegisterBuiltinFunc(ast.GlobQuoteMeta.Name, builtinGlobQuoteMeta) +} diff --git a/third_party/opa/v1/topdown/glob_bench_test.go b/third_party/opa/v1/topdown/glob_bench_test.go new file mode 100644 index 000000000000..1e9ff3b20465 --- /dev/null +++ b/third_party/opa/v1/topdown/glob_bench_test.go @@ -0,0 +1,100 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + "sync" + "testing" + + "github.com/gobwas/glob" + "github.com/open-policy-agent/opa/v1/ast" +) + +func BenchmarkBuiltinGlobMatch(b *testing.B) { + iter := func(*ast.Term) error { return nil } + ctx := BuiltinContext{} + + for _, reusePattern := range []bool{true, false} { + for _, patternCount := range []int{10, 100, 1000} { + b.Run(fmt.Sprintf("reuse-pattern=%v, pattern-count=%d", reusePattern, patternCount), func(b *testing.B) { + b.ResetTimer() + for range b.N { + // Clearing the cache + globCache = make(map[string]glob.Glob) + + for i := range patternCount { + var operands []*ast.Term + if reusePattern { + operands = []*ast.Term{ + ast.NewTerm(ast.String("foo/*")), + ast.NullTerm(), + ast.NewTerm(ast.String("foo/bar")), + } + } else { + operands = []*ast.Term{ + ast.NewTerm(ast.String(fmt.Sprintf("foo/*/%d", i))), + ast.NullTerm(), + ast.NewTerm(ast.String(fmt.Sprintf("foo/bar/%d", i))), + } + } + if err := builtinGlobMatch(ctx, operands, iter); err != nil { + b.Fatal(err) + } + } + } + }) + } + } +} + +func BenchmarkBuiltinGlobMatchAsync(b *testing.B) { + iter := func(*ast.Term) error { return nil } + ctx := BuiltinContext{} + + for _, reusePattern := range []bool{true, false} { + for _, clientCount := range []int{100, 200} { + for _, patternCount := range []int{10, 100, 1000} { + b.Run(fmt.Sprintf("reuse-pattern=%v, clients=%d, pattern-count=%d", reusePattern, clientCount, patternCount), func(b *testing.B) { + b.ResetTimer() + for range b.N { + // Clearing the cache + globCache = make(map[string]glob.Glob) + + wg := sync.WaitGroup{} + for i := range clientCount { + clientID := i + wg.Add(1) + go func() { + for j := range patternCount { + var operands []*ast.Term + if reusePattern { + operands = []*ast.Term{ + ast.NewTerm(ast.String("foo/*")), + ast.NullTerm(), + ast.NewTerm(ast.String("foo/bar")), + } + } else { + operands = []*ast.Term{ + ast.NewTerm(ast.String(fmt.Sprintf("foo/*/%d/%d", clientID, j))), + ast.NullTerm(), + ast.NewTerm(ast.String(fmt.Sprintf("foo/bar/%d/%d", clientID, j))), + } + } + if err := builtinGlobMatch(ctx, operands, iter); err != nil { + b.Error(err) + return + } + } + wg.Done() + }() + } + wg.Wait() + } + }) + } + } + } +} diff --git a/third_party/opa/v1/topdown/glob_test.go b/third_party/opa/v1/topdown/glob_test.go new file mode 100644 index 000000000000..27bee6046657 --- /dev/null +++ b/third_party/opa/v1/topdown/glob_test.go @@ -0,0 +1,183 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/cache" +) + +func TestGlobBuiltinCache(t *testing.T) { + t.Parallel() + + ctx := BuiltinContext{} + iter := func(*ast.Term) error { return nil } + + // A novel glob pattern is cached. + glob1 := "foo/*" + operands := []*ast.Term{ + ast.NewTerm(ast.String(glob1)), + ast.NullTerm(), + ast.NewTerm(ast.String("foo/bar")), + } + err := builtinGlobMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // the glob id will have a trailing '-' rune. + if _, ok := globCache[glob1+"-"]; !ok { + t.Fatalf("Expected glob to be cached: %v", glob1) + } + + // Fill up the cache. + for i := range regexCacheMaxSize - 1 { + operands := []*ast.Term{ + ast.NewTerm(ast.String(fmt.Sprintf("foo/%d/*", i))), + ast.NullTerm(), + ast.NewTerm(ast.String(fmt.Sprintf("foo/%d/bar", i))), + } + err := builtinGlobMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + if len(globCache) != regexCacheMaxSize { + t.Fatalf("Expected cache to be full") + } + + // A new glob pattern is cached and a random pattern is evicted. + glob2 := "bar/*" + operands = []*ast.Term{ + ast.NewTerm(ast.String(glob2)), + ast.NullTerm(), + ast.NewTerm(ast.String("bar/baz")), + } + err = builtinGlobMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(globCache) != regexCacheMaxSize { + t.Fatalf("Expected cache be capped at %d, was %d", regexCacheMaxSize, len(globCache)) + } + + if _, ok := globCache[glob2+"-"]; !ok { + t.Fatalf("Expected glob to be cached: %v", glob2) + } +} + +func TestGlobBuiltinInterQueryValueCache(t *testing.T) { + t.Parallel() + + ip := []byte(`{"inter_query_builtin_value_cache": {"max_num_entries": "10"},}`) + config, _ := cache.ParseCachingConfig(ip) + interQueryValueCache := cache.NewInterQueryValueCache(context.Background(), config) + + ctx := BuiltinContext{InterQueryBuiltinValueCache: interQueryValueCache} + iter := func(*ast.Term) error { return nil } + + // A novel glob pattern is cached. + glob1 := "foo/*" + operands := []*ast.Term{ + ast.NewTerm(ast.String(glob1)), + ast.NullTerm(), + ast.NewTerm(ast.String("foo/bar")), + } + err := builtinGlobMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // the glob id will have a trailing '-' rune. + if _, ok := ctx.InterQueryBuiltinValueCache.Get(ast.StringTerm(glob1 + "-").Value); !ok { + t.Fatalf("Expected glob to be cached: %v", glob1) + } + + // Fill up the cache. + for i := range 9 { + operands := []*ast.Term{ + ast.NewTerm(ast.String(fmt.Sprintf("foo/%d/*", i))), + ast.NullTerm(), + ast.NewTerm(ast.String(fmt.Sprintf("foo/%d/bar", i))), + } + err := builtinGlobMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + // A new glob pattern is cached and a random pattern is evicted. + glob2 := "bar/*" + operands = []*ast.Term{ + ast.NewTerm(ast.String(glob2)), + ast.NullTerm(), + ast.NewTerm(ast.String("bar/baz")), + } + err = builtinGlobMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if _, ok := ctx.InterQueryBuiltinValueCache.Get(ast.StringTerm(glob2 + "-").Value); !ok { + t.Fatalf("Expected glob to be cached: %v", glob2) + } +} + +func TestGlobBuiltinInterQueryValueCacheTypeMismatch(t *testing.T) { + t.Parallel() + + ip := []byte(`{"inter_query_builtin_value_cache": {"max_num_entries": "10"},}`) + config, _ := cache.ParseCachingConfig(ip) + interQueryValueCache := cache.NewInterQueryValueCache(context.Background(), config) + + ctx := BuiltinContext{InterQueryBuiltinValueCache: interQueryValueCache} + iter := func(*ast.Term) error { return nil } + + key := "foo.*" + + operands := []*ast.Term{ + ast.NewTerm(ast.String(key)), + ast.NullTerm(), + ast.NewTerm(ast.String("foo/bar")), + } + err := builtinGlobMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // the glob id will have a trailing '-' rune. + if _, ok := ctx.InterQueryBuiltinValueCache.Get(ast.StringTerm(key + "-").Value); !ok { + t.Fatalf("Expected glob to be cached: %v", key) + } + + // update the cache entry + ctx.InterQueryBuiltinValueCache.Insert(ast.StringTerm(key+"-").Value, "bar") + + err = builtinGlobMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // verify the cache entry is unchanged + value, ok := ctx.InterQueryBuiltinValueCache.Get(ast.StringTerm(key + "-").Value) + if !ok { + t.Fatal("Expected key \"foo.*-\" in cache") + } + + actual, ok := value.(string) + if !ok { + t.Fatal("Expected string value") + } + + if actual != "bar" { + t.Fatalf("Expected value \"bar\" but got %v", actual) + } +} diff --git a/third_party/opa/v1/topdown/graphql.go b/third_party/opa/v1/topdown/graphql.go new file mode 100644 index 000000000000..178bbe845d02 --- /dev/null +++ b/third_party/opa/v1/topdown/graphql.go @@ -0,0 +1,692 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build ignore + +package topdown + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + + gqlast "github.com/vektah/gqlparser/v2/ast" + gqlparser "github.com/vektah/gqlparser/v2/parser" + gqlvalidator "github.com/vektah/gqlparser/v2/validator" + + // Side-effecting import. Triggers GraphQL library's validation rule init() functions. + _ "github.com/vektah/gqlparser/v2/validator/rules" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" +) + +// Parses a GraphQL schema, and returns the GraphQL AST for the schema. +func parseSchema(schema string) (*gqlast.SchemaDocument, error) { + // NOTE(philipc): We don't include the "built-in schema defs" from the + // underlying graphql parsing library here, because those definitions + // generate enormous AST blobs. In the future, if there is demand for + // a "full-spec" version of schema ASTs, we may need to provide a + // version of this function that includes the built-in schema + // definitions. + schemaAST, err := gqlparser.ParseSchema(&gqlast.Source{Input: schema}) + if err != nil { + return nil, formatGqlParserError(err) + } + return schemaAST, nil +} + +// Parses a GraphQL query, and returns the GraphQL AST for the query. +func parseQuery(query string) (*gqlast.QueryDocument, error) { + queryAST, err := gqlparser.ParseQuery(&gqlast.Source{Input: query}) + if err != nil { + return nil, formatGqlParserError(err) + } + return queryAST, nil +} + +// Validates a GraphQL query against a schema, and returns an error. +// In this case, we get a wrappered error list type, and pluck out +// just the first error message in the list. +func validateQuery(schema *gqlast.Schema, query *gqlast.QueryDocument) error { + // Validate the query against the schema, erroring if there's an issue. + err := gqlvalidator.Validate(schema, query) + if err != nil { + return formatGqlParserError(err) + } + return nil +} + +func getBuiltinSchema() *gqlast.SchemaDocument { + schema, err := gqlparser.ParseSchema(gqlvalidator.Prelude) + if err != nil { + panic(fmt.Errorf("Error in gqlparser Prelude (should be impossible): %w", err)) + } + return schema +} + +// NOTE(philipc): This function expects *validated* schema documents, and will break +// if it is fed arbitrary structures. +func mergeSchemaDocuments(docA *gqlast.SchemaDocument, docB *gqlast.SchemaDocument) *gqlast.SchemaDocument { + ast := &gqlast.SchemaDocument{} + ast.Merge(docA) + ast.Merge(docB) + return ast +} + +// Converts a SchemaDocument into a gqlast.Schema object that can be used for validation. +// It merges in the builtin schema typedefs exactly as gqltop.LoadSchema did internally. +func convertSchema(schemaDoc *gqlast.SchemaDocument) (*gqlast.Schema, error) { + // Merge builtin schema + schema we were provided. + builtinsSchemaDoc := getBuiltinSchema() + mergedSchemaDoc := mergeSchemaDocuments(builtinsSchemaDoc, schemaDoc) + schema, err := gqlvalidator.ValidateSchemaDocument(mergedSchemaDoc) + if err != nil { + return nil, fmt.Errorf("Error in gqlparser SchemaDocument to Schema conversion: %w", err) + } + return schema, nil +} + +// Converts an ast.Object into a gqlast.QueryDocument object. +func objectToQueryDocument(value ast.Object) (*gqlast.QueryDocument, error) { + // Convert ast.Term to any for JSON encoding below. + asJSON, err := ast.JSON(value) + if err != nil { + return nil, err + } + // Marshal to JSON. + bs, err := json.Marshal(asJSON) + if err != nil { + return nil, err + } + // Unmarshal from JSON -> gqlast.QueryDocument. + var result gqlast.QueryDocument + err = json.Unmarshal(bs, &result) + if err != nil { + return nil, err + } + return &result, nil +} + +// Converts an ast.Object into a gqlast.SchemaDocument object. +func objectToSchemaDocument(value ast.Object) (*gqlast.SchemaDocument, error) { + // Convert ast.Term to any for JSON encoding below. + asJSON, err := ast.JSON(value) + if err != nil { + return nil, err + } + // Marshal to JSON. + bs, err := json.Marshal(asJSON) + if err != nil { + return nil, err + } + // Unmarshal from JSON -> gqlast.SchemaDocument. + var result gqlast.SchemaDocument + err = json.Unmarshal(bs, &result) + if err != nil { + return nil, err + } + return &result, nil +} + +// Recursively traverses an AST that has been run through InterfaceToValue, +// and prunes away the fields with null or empty values, and all `Position` +// structs. +// NOTE(philipc): We currently prune away null values to reduce the level +// of clutter in the returned AST objects. In the future, if there is demand +// for ASTs that have a more regular/fixed structure, we may need to provide +// a "raw" version of the AST, where we still prune away the `Position` +// structs, but leave in the null fields. +func pruneIrrelevantGraphQLASTNodes(value ast.Value) ast.Value { + // We iterate over the Value we've been provided, and recurse down + // in the case of complex types, such as Arrays/Objects. + // We are guaranteed to only have to deal with standard JSON types, + // so this is much less ugly than what we'd need for supporting every + // extant ast type! + switch x := value.(type) { + case *ast.Array: + result := ast.NewArray() + // Iterate over the array's elements, and do the following: + // - Drop any Nulls + // - Drop any any empty object/array value (after running the pruner) + for i := range x.Len() { + vTerm := x.Elem(i) + switch v := vTerm.Value.(type) { + case ast.Null: + continue + case *ast.Array: + // Safe, because we knew the type before going to prune it. + va := pruneIrrelevantGraphQLASTNodes(v).(*ast.Array) + if va.Len() > 0 { + result = result.Append(ast.NewTerm(va)) + } + case ast.Object: + // Safe, because we knew the type before going to prune it. + vo := pruneIrrelevantGraphQLASTNodes(v).(ast.Object) + if vo.Len() > 0 { + result = result.Append(ast.NewTerm(vo)) + } + default: + result = result.Append(vTerm) + } + } + return result + case ast.Object: + result := ast.NewObject() + // Iterate over our object's keys, and do the following: + // - Drop "Position". + // - Drop any key with a Null value. + // - Drop any key with an empty object/array value (after running the pruner) + keys := x.Keys() + for _, k := range keys { + // We drop the "Position" objects because we don't need the + // source-backref/location info they provide for policy rules. + // Note that keys are ast.Strings. + if ast.String("Position").Equal(k.Value) { + continue + } + vTerm := x.Get(k) + switch v := vTerm.Value.(type) { + case ast.Null: + continue + case *ast.Array: + // Safe, because we knew the type before going to prune it. + va := pruneIrrelevantGraphQLASTNodes(v).(*ast.Array) + if va.Len() > 0 { + result.Insert(k, ast.NewTerm(va)) + } + case ast.Object: + // Safe, because we knew the type before going to prune it. + vo := pruneIrrelevantGraphQLASTNodes(v).(ast.Object) + if vo.Len() > 0 { + result.Insert(k, ast.NewTerm(vo)) + } + default: + result.Insert(k, vTerm) + } + } + return result + default: + return x + } +} + +func formatGqlParserError(err error) error { + // We use strings.TrimSuffix to remove the '.' characters that the library + // authors include on most of their validation errors. This should be safe, + // since variable names in their error messages are usually quoted, and + // this affects only the last character(s) in the string. + // NOTE(philipc): We know the error location will be in the query string, + // because schema validation always happens before this function is called. + // NOTE(rm): gqlparser does not _always_ return the error location + // so only populate location if it is available + if err == nil { + return nil + } + // If the error contains location information, format it nicely + errorParts := strings.SplitN(err.Error(), ":", 4) + if len(errorParts) >= 4 { + row, err := strconv.ParseUint(errorParts[1], 10, 64) + if err == nil { + col, err := strconv.ParseUint(errorParts[2], 10, 64) + if err == nil { + msg := strings.TrimSuffix(strings.TrimLeft(errorParts[len(errorParts)-1], " "), ".\n") + return fmt.Errorf("%s in GraphQL string at location %d:%d", msg, row, col) + } + } + } + // Wrap and return the full error if location information is not available + return fmt.Errorf("GraphQL parse error: %w", err) +} + +// Reports errors from parsing/validation. +func builtinGraphQLParse(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + var queryDoc *gqlast.QueryDocument + var schemaDoc *gqlast.SchemaDocument + var schemaASTValue ast.Value + var querySchema ast.Value + var err error + + // Parse/translate query if it's a string/object. + switch x := operands[0].Value.(type) { + case ast.String: + queryDoc, err = parseQuery(string(x)) + case ast.Object: + queryDoc, err = objectToQueryDocument(x) + default: + // Error if wrong type. + return builtins.NewOperandTypeErr(0, x, "string", "object") + } + if err != nil { + return err + } + + schemaCacheKey, schema := cacheGetSchema(bctx, operands[1]) + schemaASTCacheKey, querySchema := cacheGetSchemaAST(bctx, operands[1]) + if schema == nil || querySchema == nil { + // Parse/translate schema if it's a string/object. + switch x := operands[1].Value.(type) { + case ast.String: + schemaDoc, err = parseSchema(string(x)) + case ast.Object: + schemaDoc, err = objectToSchemaDocument(x) + default: + // Error if wrong type. + return builtins.NewOperandTypeErr(1, x, "string", "object") + } + if err != nil { + return err + } + + // Convert SchemaDoc to Object before validating and converting it to a Schema + // This precludes inclusion of extra definitions from the default GraphQL schema + if querySchema == nil { + schemaASTValue, err = ast.InterfaceToValue(schemaDoc) + if err != nil { + return err + } + querySchema = pruneIrrelevantGraphQLASTNodes(schemaASTValue.(ast.Object)) + cacheInsertSchemaAST(bctx, schemaASTCacheKey, querySchema) + } + + // Validate the query against the schema, erroring if there's an issue. + if schema == nil { + schema, err = convertSchema(schemaDoc) + if err != nil { + return err + } + cacheInsertSchema(bctx, schemaCacheKey, schema) + } + + } + // Transform the ASTs into Objects. + queryASTValue, err := ast.InterfaceToValue(queryDoc) + if err != nil { + return err + } + + if err := validateQuery(schema, queryDoc); err != nil { + return err + } + + // Recursively remove irrelevant AST structures. + queryResult := pruneIrrelevantGraphQLASTNodes(queryASTValue.(ast.Object)) + + // Construct return value. + verified := ast.ArrayTerm( + ast.NewTerm(queryResult), + ast.NewTerm(querySchema), + ) + + return iter(verified) +} + +// Returns default value when errors occur. +func builtinGraphQLParseAndVerify(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + var queryDoc *gqlast.QueryDocument + var schemaDoc *gqlast.SchemaDocument + var schemaASTValue ast.Value + var querySchema ast.Value + var err error + + unverified := ast.ArrayTerm( + ast.InternedTerm(false), + ast.NewTerm(ast.NewObject()), + ast.NewTerm(ast.NewObject()), + ) + + // Parse/translate query if it's a string/object. + switch x := operands[0].Value.(type) { + case ast.String: + queryDoc, err = parseQuery(string(x)) + case ast.Object: + queryDoc, err = objectToQueryDocument(x) + default: + // Error if wrong type. + return iter(unverified) + } + if err != nil { + return iter(unverified) + } + + // Transform the ASTs into Objects. + queryASTValue, err := ast.InterfaceToValue(queryDoc) + if err != nil { + return iter(unverified) + } + + schemaCacheKey, schema := cacheGetSchema(bctx, operands[1]) + schemaASTCacheKey, querySchema := cacheGetSchemaAST(bctx, operands[1]) + if schema == nil || querySchema == nil { + // Parse/translate schema if it's a string/object. + switch x := operands[1].Value.(type) { + case ast.String: + schemaDoc, err = parseSchema(string(x)) + case ast.Object: + schemaDoc, err = objectToSchemaDocument(x) + default: + // Error if wrong type. + return iter(unverified) + } + if err != nil { + return iter(unverified) + } + + // Convert SchemaDoc to Object before validating and converting it to a Schema + // This precludes inclusion of extra definitions from the default GraphQL schema + if querySchema == nil { + schemaASTValue, err = ast.InterfaceToValue(schemaDoc) + if err != nil { + return iter(unverified) + } + querySchema = pruneIrrelevantGraphQLASTNodes(schemaASTValue.(ast.Object)) + cacheInsertSchemaAST(bctx, schemaASTCacheKey, querySchema) + } + + if schema == nil { + schema, err = convertSchema(schemaDoc) + if err != nil { + return iter(unverified) + } + cacheInsertSchema(bctx, schemaCacheKey, schema) + } + + } + + // Validate the query against the schema, erroring if there's an issue. + if err := validateQuery(schema, queryDoc); err != nil { + return iter(unverified) + } + + // Recursively remove irrelevant AST structures. + queryResult := pruneIrrelevantGraphQLASTNodes(queryASTValue.(ast.Object)) + + // Construct return value. + verified := ast.ArrayTerm( + ast.InternedTerm(true), + ast.NewTerm(queryResult), + ast.NewTerm(querySchema), + ) + + return iter(verified) +} + +func builtinGraphQLParseQuery(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + raw, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Get the highly-nested AST struct, along with any errors generated. + query, err := parseQuery(string(raw)) + if err != nil { + return err + } + + // Transform the AST into an Object. + value, err := ast.InterfaceToValue(query) + if err != nil { + return err + } + + // Recursively remove irrelevant AST structures. + result := pruneIrrelevantGraphQLASTNodes(value.(ast.Object)) + + return iter(ast.NewTerm(result)) +} + +func builtinGraphQLParseSchema(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + schemaDocCacheKey, schemaDoc := cacheGetSchemaDoc(bctx, operands[0]) + if schemaDoc == nil { + raw, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Get the highly-nested AST struct, along with any errors generated. + schemaDoc, err = parseSchema(string(raw)) + if err != nil { + return err + } + // Note SchemaDoc is not validated + cacheInsertSchemaDoc(bctx, schemaDocCacheKey, schemaDoc) + } + + schemaASTCacheKey, schemaAST := cacheGetSchemaAST(bctx, operands[0]) + if schemaAST == nil { + + // Transform the AST into an Object. + value, err := ast.InterfaceToValue(schemaDoc) + if err != nil { + return err + } + + // Recursively remove irrelevant AST structures. + schemaAST = pruneIrrelevantGraphQLASTNodes(value.(ast.Object)) + cacheInsertSchemaAST(bctx, schemaASTCacheKey, schemaAST) + } + return iter(ast.NewTerm(schemaAST)) +} + +func builtinGraphQLIsValid(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + var queryDoc *gqlast.QueryDocument + var schemaDoc *gqlast.SchemaDocument + var schema *gqlast.Schema + var err error + + switch x := operands[0].Value.(type) { + case ast.String: + queryDoc, err = parseQuery(string(x)) + case ast.Object: + queryDoc, err = objectToQueryDocument(x) + default: + // Error if wrong type. + return iter(ast.InternedTerm(false)) + } + if err != nil { + return iter(ast.InternedTerm(false)) + } + + schemaCacheKey, schema := cacheGetSchema(bctx, operands[1]) + if schema == nil { + switch x := operands[1].Value.(type) { + case ast.String: + schemaDoc, err = parseSchema(string(x)) + case ast.Object: + schemaDoc, err = objectToSchemaDocument(x) + default: + // Error if wrong type. + return iter(ast.InternedTerm(false)) + } + if err != nil { + return iter(ast.InternedTerm(false)) + } + + // Validate the query against the schema, erroring if there's an issue. + schema, err = convertSchema(schemaDoc) + if err != nil { + return iter(ast.InternedTerm(false)) + } + cacheInsertSchema(bctx, schemaCacheKey, schema) + } + + if err := validateQuery(schema, queryDoc); err != nil { + return iter(ast.InternedTerm(false)) + } + + // If we got this far, the GraphQL query passed validation. + return iter(ast.InternedTerm(true)) +} + +func builtinGraphQLSchemaIsValid(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + var err error + + // Schemas are only cached if they are valid + schemaCacheKey, schema := cacheGetSchema(bctx, operands[0]) + if schema == nil { + var schemaDoc *gqlast.SchemaDocument + var validatedSchema *gqlast.Schema + + switch x := operands[0].Value.(type) { + case ast.String: + schemaDoc, err = parseSchema(string(x)) + case ast.Object: + schemaDoc, err = objectToSchemaDocument(x) + default: + // Error if wrong type. + return iter(ast.InternedTerm(false)) + } + if err != nil { + return iter(ast.InternedTerm(false)) + } + // Validate the schema, this determines the result + // and whether there is a schema to cache + validatedSchema, err = convertSchema(schemaDoc) + if err == nil { + cacheInsertSchema(bctx, schemaCacheKey, validatedSchema) + } + } + + return iter(ast.InternedTerm(err == nil)) +} + +// Insert Schema into cache +func cacheInsertSchema(bctx BuiltinContext, key string, schema *gqlast.Schema) { + if bctx.InterQueryBuiltinValueCache == nil || key == "" { + return + } + cacheKey := ast.String(key) + c := bctx.InterQueryBuiltinValueCache.GetCache(gqlCacheName) + if c == nil { + return + } + c.Insert(cacheKey, schema) +} + +// Insert SchemaAST into cache +func cacheInsertSchemaAST(bctx BuiltinContext, key string, schemaAST ast.Value) { + if bctx.InterQueryBuiltinValueCache == nil || key == "" { + return + } + cacheKeyAST := ast.String(key) + c := bctx.InterQueryBuiltinValueCache.GetCache(gqlCacheName) + if c == nil { + return + } + c.Insert(cacheKeyAST, schemaAST) +} + +// Insert SchemaDocument into cache +func cacheInsertSchemaDoc(bctx BuiltinContext, key string, schemaDoc *gqlast.SchemaDocument) { + if bctx.InterQueryBuiltinValueCache == nil || key == "" { + return + } + cacheKey := ast.String(key) + c := bctx.InterQueryBuiltinValueCache.GetCache(gqlCacheName) + if c == nil { + return + } + c.Insert(cacheKey, schemaDoc) +} + +// Returns the cache key and a Schema if this key already exists in the cache +func cacheGetSchema(bctx BuiltinContext, t *ast.Term) (string, *gqlast.Schema) { + if bctx.InterQueryBuiltinValueCache != nil { + if c := bctx.InterQueryBuiltinValueCache.GetCache(gqlCacheName); c != nil { + if key, keyOk := cacheKeyWithPrefix(bctx, t, "gql_schema-"); keyOk { + if val, ok := c.Get(ast.String(key)); ok { + if schema, isSchema := val.(*gqlast.Schema); isSchema { + return key, schema + } + } + return key, nil + } + } + } + return "", nil +} + +// Returns the cache key and a SchemaDocument if this key already exists in the cache +// Note: the SchemaDocument is not a validated Schema +func cacheGetSchemaDoc(bctx BuiltinContext, t *ast.Term) (string, *gqlast.SchemaDocument) { + if bctx.InterQueryBuiltinValueCache != nil { + if c := bctx.InterQueryBuiltinValueCache.GetCache(gqlCacheName); c != nil { + if key, keyOk := cacheKeyWithPrefix(bctx, t, "gql_schema_doc-"); keyOk { + if val, ok := c.Get(ast.String(key)); ok { + if schemaDoc, isSchemaDoc := val.(*gqlast.SchemaDocument); isSchemaDoc { + return key, schemaDoc + } + } + return key, nil + } + } + } + return "", nil +} + +// Returns the cache key and a SchemaDocument if this key already exists in the cache +// Note: the AST should be pruned +func cacheGetSchemaAST(bctx BuiltinContext, t *ast.Term) (string, ast.Value) { + if bctx.InterQueryBuiltinValueCache != nil { + if c := bctx.InterQueryBuiltinValueCache.GetCache(gqlCacheName); c != nil { + if key, keyOk := cacheKeyWithPrefix(bctx, t, "gql_schema_ast-"); keyOk { + if val, ok := c.Get(ast.String(key)); ok { + if schemaAST, isSchemaAST := val.(ast.Value); isSchemaAST { + return key, schemaAST + } + } + return key, nil + } + } + } + return "", nil +} + +// Compute a constant size key for use with the cache +func cacheKeyWithPrefix(bctx BuiltinContext, t *ast.Term, prefix string) (string, bool) { + var cacheKey ast.String + var ok = false + + if bctx.InterQueryBuiltinValueCache != nil { + switch t.Value.(type) { + case ast.String: + err := builtinCryptoSha256(bctx, []*ast.Term{t}, func(term *ast.Term) error { + cacheKey = term.Value.(ast.String) + return nil + }) + ok = (len(cacheKey) > 0) && (err == nil) + case ast.Object: + objTerm := ast.StringTerm(t.String()) + err := builtinCryptoSha256(bctx, []*ast.Term{objTerm}, func(term *ast.Term) error { + cacheKey = term.Value.(ast.String) + return nil + }) + ok = (len(cacheKey) > 0) && (err == nil) + default: + ok = false + } + } + + return prefix + string(cacheKey), ok +} + +const gqlCacheName = "graphql" + +func init() { + + var defaultCacheEntries int = 10 + var graphqlCacheConfig = cache.NamedValueCacheConfig{ + MaxNumEntries: &defaultCacheEntries, + } + cache.RegisterDefaultInterQueryBuiltinValueCacheConfig(gqlCacheName, &graphqlCacheConfig) + + RegisterBuiltinFunc(ast.GraphQLParse.Name, builtinGraphQLParse) + RegisterBuiltinFunc(ast.GraphQLParseAndVerify.Name, builtinGraphQLParseAndVerify) + RegisterBuiltinFunc(ast.GraphQLParseQuery.Name, builtinGraphQLParseQuery) + RegisterBuiltinFunc(ast.GraphQLParseSchema.Name, builtinGraphQLParseSchema) + RegisterBuiltinFunc(ast.GraphQLIsValid.Name, builtinGraphQLIsValid) + RegisterBuiltinFunc(ast.GraphQLSchemaIsValid.Name, builtinGraphQLSchemaIsValid) +} diff --git a/third_party/opa/v1/topdown/graphql_bench_test.go b/third_party/opa/v1/topdown/graphql_bench_test.go new file mode 100644 index 000000000000..1b7cec40f217 --- /dev/null +++ b/third_party/opa/v1/topdown/graphql_bench_test.go @@ -0,0 +1,400 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build ignore + +package topdown + +import ( + "context" + _ "embed" + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/cache" +) + +// The MaxNumEntries value for the named caches +const defaultCacheEntries = 10 + +// The number of types to add to the existing schema which already has one type definition +const extraTypes = 999 + +func BenchmarkGraphQLSchemaIsValid(b *testing.B) { + benches := []struct { + desc string + schema *ast.Term + cache cache.InterQueryValueCache + result *ast.Term + }{ + { + desc: "Trivial Schema - string", + schema: ast.StringTerm(employeeGQLSchema), + cache: nil, + result: ast.InternedTerm(true), + }, + { + desc: "Trivial Schema with cache - string", + schema: ast.StringTerm(employeeGQLSchema), + cache: valueCacheFactory(gqlCacheName, defaultCacheEntries), + result: ast.InternedTerm(true), + }, + { + desc: fmt.Sprintf("Schema w/ %d types - string", extraTypes+1), + schema: ast.StringTerm(schemaWithExtraEmployeeTypes(extraTypes)), + cache: nil, + result: ast.InternedTerm(true), + }, + { + desc: fmt.Sprintf("Schema w/ %d types with cache - string", extraTypes+1), + schema: ast.StringTerm(schemaWithExtraEmployeeTypes(extraTypes)), + cache: valueCacheFactory(gqlCacheName, defaultCacheEntries), + result: ast.InternedTerm(true), + }, + { + desc: "Trivial Schema - AST object", + schema: ast.NewTerm(ast.MustParseTerm(employeeGQLSchemaAST).Value.(ast.Object)), + cache: nil, + result: ast.InternedTerm(true), + }, + { + desc: "Trivial Schema with cache - AST object", + schema: ast.NewTerm(ast.MustParseTerm(employeeGQLSchemaAST).Value.(ast.Object)), + cache: valueCacheFactory(gqlCacheName, defaultCacheEntries), + result: ast.InternedTerm(true), + }, + } + + for _, bench := range benches { + b.Run(bench.desc, func(b *testing.B) { + for range b.N { + var result *ast.Term + err := builtinGraphQLSchemaIsValid( + BuiltinContext{ + InterQueryBuiltinValueCache: bench.cache, + }, + []*ast.Term{bench.schema}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if err != nil { + b.Fatalf("unexpected error: %s", err) + } + if !bench.result.Equal(result) { + b.Fatalf("unexpected result: wanted: %#v got: %#v", bench.result, result) + } + } + }) + } +} + +func BenchmarkGraphQLParseSchema(b *testing.B) { + benches := []struct { + desc string + schema *ast.Term + cache cache.InterQueryValueCache + result *ast.Term + }{ + { + desc: "Trivial Schema - string", + schema: ast.StringTerm(employeeGQLSchema), + cache: nil, + result: ast.NewTerm(employeeGQLSchemaASTObj), + }, + { + desc: "Trivial Schema with cache - string", + schema: ast.StringTerm(employeeGQLSchema), + cache: valueCacheFactory(gqlCacheName, defaultCacheEntries), + result: ast.NewTerm(employeeGQLSchemaASTObj), + }, + } + for _, bench := range benches { + b.Run(bench.desc, func(b *testing.B) { + for range b.N { + var result *ast.Term + err := builtinGraphQLParseSchema( + BuiltinContext{ + InterQueryBuiltinValueCache: bench.cache, + }, + []*ast.Term{bench.schema}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if err != nil { + b.Fatalf("unexpected error: %s", err) + } + if !bench.result.Equal(result) { + b.Errorf("Unexpected result, expected %#v, got %#v", bench.result, result) + return + } + } + }) + } +} + +func BenchmarkGraphQLParseQuery(b *testing.B) { + benches := []struct { + desc string + query *ast.Term + cache cache.InterQueryValueCache + result *ast.Term + }{ + { + desc: "Trivial Query - string", + query: ast.StringTerm(`{ employeeByID(id: "alice") { salary } }`), + cache: nil, + result: ast.NewTerm(employeeGQLQueryASTObj), + }, + { + desc: "Trivial Query with cache - string", + query: ast.StringTerm(`{ employeeByID(id: "alice") { salary } }`), + cache: valueCacheFactory(gqlCacheName, defaultCacheEntries), + result: ast.NewTerm(employeeGQLQueryASTObj), + }, + } + for _, bench := range benches { + b.Run(bench.desc, func(b *testing.B) { + for range b.N { + var result *ast.Term + err := builtinGraphQLParseQuery( + BuiltinContext{ + InterQueryBuiltinValueCache: bench.cache, + }, + []*ast.Term{bench.query}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if err != nil { + b.Fatalf("unexpected error: %s", err) + } + if !bench.result.Equal(result) { + b.Errorf("Unexpected result, expected %#v, got %#v", bench.result, result) + return + } + } + }) + } +} + +func BenchmarkGraphQLIsValid(b *testing.B) { + benches := []struct { + desc string + schema *ast.Term + cache cache.InterQueryValueCache + query *ast.Term + result *ast.Term + }{ + { + desc: "Trivial Schema - string", + cache: nil, + query: ast.StringTerm(`{ employeeByID(id: "alice") { salary } }`), + schema: ast.StringTerm(employeeGQLSchema), + result: ast.InternedTerm(true), + }, + { + desc: "Trivial Schema with cache - string", + cache: valueCacheFactory(gqlCacheName, defaultCacheEntries), + query: ast.StringTerm(`{ employeeByID(id: "alice") { salary } }`), + schema: ast.StringTerm(employeeGQLSchema), + result: ast.InternedTerm(true), + }, + { + desc: fmt.Sprintf("Schema w/ %d types - string", extraTypes+1), + cache: nil, + query: ast.StringTerm(`{ employeeByID(id: "alice") { salary } }`), + schema: ast.StringTerm(schemaWithExtraEmployeeTypes(extraTypes)), + result: ast.InternedTerm(true), + }, + { + desc: fmt.Sprintf("Schema w/ %d types with cache - string", extraTypes+1), + cache: valueCacheFactory(gqlCacheName, defaultCacheEntries), + query: ast.StringTerm(`{ employeeByID(id: "alice") { salary } }`), + schema: ast.StringTerm(schemaWithExtraEmployeeTypes(extraTypes)), + result: ast.InternedTerm(true), + }, + } + for _, bench := range benches { + b.Run(bench.desc, func(b *testing.B) { + for range b.N { + var result *ast.Term + err := builtinGraphQLIsValid( + BuiltinContext{ + InterQueryBuiltinValueCache: bench.cache, + }, + []*ast.Term{bench.query, bench.schema}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if err != nil { + b.Fatalf("unexpected error: %s", err) + } + if !bench.result.Equal(result) { + b.Errorf("Unexpected result, expected %#v, got %#v", bench.result, result) + return + } + } + }) + } +} + +func BenchmarkGraphQLParse(b *testing.B) { + // Use this to map result item position to purpose for better error messages + resultItemDescription := []string{"query_ast", "schema_ast"} + + benches := []struct { + desc string + schema *ast.Term + cache cache.InterQueryValueCache + query *ast.Term + result *ast.Term + }{ + { + desc: "Trivial Schema - string", + cache: nil, + query: ast.StringTerm(`{ employeeByID(id: "alice") { salary } }`), + schema: ast.StringTerm(employeeGQLSchema), + result: ast.ArrayTerm( + ast.NewTerm(employeeGQLQueryASTObj), + ast.NewTerm(employeeGQLSchemaASTObj), + ), + }, + { + desc: "Trivial Schema with cache - string", + cache: valueCacheFactory(gqlCacheName, defaultCacheEntries), + query: ast.StringTerm(`{ employeeByID(id: "alice") { salary } }`), + schema: ast.StringTerm(employeeGQLSchema), + result: ast.ArrayTerm( + ast.NewTerm(employeeGQLQueryASTObj), + ast.NewTerm(employeeGQLSchemaASTObj), + ), + }, + } + for _, bench := range benches { + b.Run(bench.desc, func(b *testing.B) { + for range b.N { + var result *ast.Term + err := builtinGraphQLParse( + BuiltinContext{ + InterQueryBuiltinValueCache: bench.cache, + }, + []*ast.Term{bench.query, bench.schema}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if err != nil { + b.Fatalf("unexpected error: %s", err) + } + if !bench.result.Equal(result) { + b.Errorf("Unexpected result, expected %#v, got %#v", bench.result, result) + return + } + // Check each item in array result + for i := range bench.result.Value.(*ast.Array).Len() { + expected := bench.result.Value.(*ast.Array).Elem(i) + actual := result.Value.(*ast.Array).Elem(i) + if !expected.Equal(actual) { + b.Errorf("Unexpected value at result[%d] (%s), expected %#v, got %#v", i, resultItemDescription[i], expected, actual) + return + } + } + } + }) + } +} + +func BenchmarkGraphQLParseAndVerify(b *testing.B) { + // Use this to map result item position to purpose for better error messages + resultItemDescription := []string{"is_valid", "query_ast", "schema_ast"} + + benches := []struct { + desc string + schema *ast.Term + cache cache.InterQueryValueCache + query *ast.Term + result *ast.Term + }{ + { + desc: "Trivial Schema - string", + cache: nil, + query: ast.StringTerm(`{ employeeByID(id: "alice") { salary } }`), + schema: ast.StringTerm(employeeGQLSchema), + result: ast.ArrayTerm( + ast.InternedTerm(true), + ast.NewTerm(employeeGQLQueryASTObj), + ast.NewTerm(employeeGQLSchemaASTObj), + ), + }, + { + desc: "Trivial Schema with cache - string", + cache: valueCacheFactory(gqlCacheName, defaultCacheEntries), + query: ast.StringTerm(`{ employeeByID(id: "alice") { salary } }`), + schema: ast.StringTerm(employeeGQLSchema), + result: ast.ArrayTerm( + ast.InternedTerm(true), + ast.NewTerm(employeeGQLQueryASTObj), + ast.NewTerm(employeeGQLSchemaASTObj), + ), + }, + } + for _, bench := range benches { + b.Run(bench.desc, func(b *testing.B) { + for range b.N { + var result *ast.Term + b.StartTimer() + err := builtinGraphQLParseAndVerify( + BuiltinContext{ + InterQueryBuiltinValueCache: bench.cache, + }, + []*ast.Term{bench.query, bench.schema}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + b.StopTimer() + if err != nil { + b.Fatalf("unexpected error: %s", err) + } + if !bench.result.Equal(result) { + b.Errorf("Unexpected result, expected %#v, got %#v", bench.result, result) + return + } + // Check each item in array result + for i := range bench.result.Value.(*ast.Array).Len() { + expected := bench.result.Value.(*ast.Array).Elem(i) + actual := result.Value.(*ast.Array).Elem(i) + if !expected.Equal(actual) { + b.Errorf("Unexpected value at result[%d] (%s), expected %#v, got %#v", i, resultItemDescription[i], expected, actual) + return + } + } + } + }) + } +} + +func valueCacheFactory(name string, maxEntries int) cache.InterQueryValueCache { + return cache.NewInterQueryValueCache( + context.Background(), + &cache.Config{ + InterQueryBuiltinValueCache: cache.InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*cache.NamedValueCacheConfig{ + name: { + MaxNumEntries: &[]int{maxEntries}[0], + }, + }, + }, + }) +} diff --git a/third_party/opa/v1/topdown/graphql_test.go b/third_party/opa/v1/topdown/graphql_test.go new file mode 100644 index 000000000000..6010ae8b3f85 --- /dev/null +++ b/third_party/opa/v1/topdown/graphql_test.go @@ -0,0 +1,1002 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build ignore + +package topdown + +import ( + "context" + "errors" + "fmt" + "os" + "runtime" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/cache" +) + +const employeeGQLSchema = ` + type Employee { + id: String! + salary: Int! + } + + schema { + query: Query + } + + type Query { + employeeByID(id: String!): Employee + }` + +const invalidEmployeeGQLSchema = ` + type Employee { + id: String! + salary: Int! + } + + schema { + query: Query + } + + type Broken { + fixme + + type Query { + employeeByID(id: String!): Employee + }` + +const employeeGQLQueryAST = `{"Operations":[{"Name":"","Operation":"query","SelectionSet":[{"Alias":"employeeByID","Arguments":[{"Name":"id","Value":{"Kind":3,"Raw":"alice"}}],"Name":"employeeByID","SelectionSet":[{"Alias":"salary","Name":"salary"}]}]}]}` + +const employeeGQLSchemaAST = `{"Definitions":[{"BuiltIn":false,"Description":"","Fields":[{"Description":"","Name":"id","Type":{"NamedType":"String","NonNull":true}},{"Description":"","Name":"salary","Type":{"NamedType":"Int","NonNull":true}}],"Kind":"OBJECT","Name":"Employee"},{"BuiltIn":false,"Description":"","Fields":[{"Arguments":[{"Description":"","Name":"id","Type":{"NamedType":"String","NonNull":true}}],"Description":"","Name":"employeeByID","Type":{"NamedType":"Employee","NonNull":false}}],"Kind":"OBJECT","Name":"Query"}],"Schema":[{"Description":"","OperationTypes":[{"Operation":"query","Type":"Query"}]}]}` + +var employeeGQLQueryASTObj = ast.MustParseTerm(employeeGQLQueryAST).Value.(ast.Object) +var employeeGQLSchemaASTObj = ast.MustParseTerm(employeeGQLSchemaAST).Value.(ast.Object) + +func TestGraphQLParseString(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + schema string + query string + result string + wantErr bool + }{ + { + note: "valid employee query and GQL schema", + schema: employeeGQLSchema, + query: `{ employeeByID(id: "alice") { salary } }`, + result: `[{"Operations": [{"Name": "", "Operation": "query", "SelectionSet": [{"Alias": "employeeByID", "Arguments": [{"Name": "id", "Value": {"Kind": 3, "Raw": "alice"}}], "Name": "employeeByID", "SelectionSet": [{"Alias": "salary", "Name": "salary"}]}]}]}, {"Definitions": [{"BuiltIn": false, "Description": "", "Fields": [{"Description": "", "Name": "id", "Type": {"NamedType": "String", "NonNull": true}}, {"Description": "", "Name": "salary", "Type": {"NamedType": "Int", "NonNull": true}}], "Kind": "OBJECT", "Name": "Employee"}, {"BuiltIn": false, "Description": "", "Fields": [{"Arguments": [{"Description": "", "Name": "id", "Type": {"NamedType": "String", "NonNull": true}}], "Description": "", "Name": "employeeByID", "Type": {"NamedType": "Employee", "NonNull": false}}], "Kind": "OBJECT", "Name": "Query"}], "Schema": [{"Description": "", "OperationTypes": [{"Operation": "query", "Type": "Query"}]}]}]`, + wantErr: false, + }, + { + note: "valid employee schema, invalid query", + schema: employeeGQLSchema, + query: `{employeeByID("alice"`, + result: "", + wantErr: true, + }, + { + note: "invalid", + schema: invalidEmployeeGQLSchema, + query: `{ employeeByID(id:"bob") } `, // missing fields + result: "", + wantErr: true, + }, + { + note: "empty", + schema: ``, + query: `{ employeeByID(id: "charlie") { id salary } }`, + result: "", + wantErr: true, + }, + } + + valueCache := cache.NewInterQueryValueCache( + context.Background(), + &cache.Config{ + InterQueryBuiltinValueCache: cache.InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*cache.NamedValueCacheConfig{ + gqlCacheName: { + MaxNumEntries: &[]int{10}[0], + }, + }, + }, + }) + + for _, tc := range cases { + + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var result *ast.Term + var err error + // Call function multiple times to hit the cache + for i := 1; i <= 3; i++ { + + err = builtinGraphQLParse( + BuiltinContext{ + InterQueryBuiltinValueCache: valueCache, + }, + []*ast.Term{ast.NewTerm(ast.String(tc.query)), ast.NewTerm(ast.String(tc.schema))}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + if (result != nil) && (tc.result != result.String()) { + t.Errorf("Unexpected result, expected %#v, got %s", tc.result, result.String()) + return + } + } + // Without the cache + err = builtinGraphQLParse( + BuiltinContext{ + InterQueryBuiltinValueCache: nil, + }, + []*ast.Term{ast.NewTerm(ast.String(tc.query)), ast.NewTerm(ast.String(tc.schema))}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + if (result != nil) && (tc.result != result.String()) { + t.Errorf("Unexpected result, expected %#v, got %s", tc.result, result.String()) + return + } + }) + } +} + +func TestGraphQLParseObject(t *testing.T) { + t.Parallel() + + // Create a default Term with the expected result for the happy path here + // so we can include it in the test case table + defaultExpectedResult := ast.ArrayTerm( + ast.NewTerm(employeeGQLQueryASTObj), + ast.NewTerm(employeeGQLSchemaASTObj), + ) + + cases := []struct { + note string + schema string + query string + result *ast.Term + wantErr bool + }{ + { + note: "valid employee schema, valid query", + schema: employeeGQLSchemaAST, + query: `{ employeeByID(id: "alice") { salary } }`, + result: defaultExpectedResult, + wantErr: false, + }, + { + note: "valid employee schema, invalid query", + schema: employeeGQLSchemaAST, + query: `{employeeByID("alice"`, + result: defaultExpectedResult, + wantErr: true, + }, + } + + valueCache := cache.NewInterQueryValueCache( + context.Background(), + &cache.Config{ + InterQueryBuiltinValueCache: cache.InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*cache.NamedValueCacheConfig{ + gqlCacheName: { + MaxNumEntries: &[]int{10}[0], + }, + }, + }, + }) + + for _, tc := range cases { + + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var result *ast.Term + var err error + inputTerm := ast.NewTerm(ast.MustParseTerm(tc.schema).Value.(ast.Object)) + + // Call function multiple times to hit the cache + for i := 1; i <= 3; i++ { + + err = builtinGraphQLParse( + BuiltinContext{ + InterQueryBuiltinValueCache: valueCache, + }, + []*ast.Term{ast.NewTerm(ast.String(tc.query)), inputTerm}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + if result != nil && !tc.wantErr { + if !tc.result.Equal(result) { + t.Errorf("Unexpected result, expected\n%s\ngot\n%s\n", tc.result.String(), result.String()) + return + } + } + result = nil + } + // Without the cache + err = builtinGraphQLParse( + BuiltinContext{ + InterQueryBuiltinValueCache: nil, + }, + []*ast.Term{ast.NewTerm(ast.String(tc.query)), inputTerm}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + if result != nil && !tc.wantErr { + if !tc.result.Equal(result) { + t.Errorf("Unexpected result, expected\n%s\ngot\n%s\n", tc.result.String(), result.String()) + return + } + } + result = nil + }) + } +} + +func TestGraphQLSchemaIsValid(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + schema *ast.Term + result ast.Value + wantErr bool + }{ + { + note: "valid employee", + schema: ast.NewTerm(ast.String(employeeGQLSchema)), + result: ast.Boolean(true), + wantErr: false, + }, + { + note: "invalid", + schema: ast.NewTerm(ast.String(invalidEmployeeGQLSchema)), + result: ast.Boolean(false), + wantErr: false, + }, + { + note: "empty", + schema: ast.NewTerm(ast.String(``)), + result: ast.Boolean(true), // An empty schema is valid because it is merged with the base schema + wantErr: false, + }, + { + note: "valid employee schema as object", + schema: ast.NewTerm(ast.MustParseTerm(employeeGQLSchemaAST).Value.(ast.Object)), + result: ast.Boolean(true), + wantErr: false, + }, + } + + valueCache := cache.NewInterQueryValueCache( + context.Background(), + &cache.Config{ + InterQueryBuiltinValueCache: cache.InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*cache.NamedValueCacheConfig{ + gqlCacheName: { + MaxNumEntries: &[]int{10}[0], + }, + }, + }, + }) + + for _, tc := range cases { + + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var result ast.Value + var err error + // Call function multiple times to hit the cache + for i := 1; i <= 3; i++ { + err = builtinGraphQLSchemaIsValid( + BuiltinContext{ + InterQueryBuiltinValueCache: valueCache, + }, + []*ast.Term{tc.schema}, + func(term *ast.Term) error { + result = term.Value + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + if tc.result != result { + t.Errorf("Unexpected result, expected %#v, got %#v", tc.result, result) + return + } + } + // Without the cache + err = builtinGraphQLSchemaIsValid( + BuiltinContext{ + InterQueryBuiltinValueCache: nil, + }, + []*ast.Term{tc.schema}, + func(term *ast.Term) error { + result = term.Value + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + if tc.result != result { + t.Errorf("Unexpected result, expected %#v, got %#v", tc.result, result) + return + } + }) + } +} + +func TestGraphQLParseAndVerify(t *testing.T) { + t.Parallel() + + // Use this to map result item position to purpose for better error messages + resultItemDescription := []string{"is_valid", "query_ast", "schema_ast"} + + failureResult := ast.ArrayTerm( + ast.BooleanTerm(false), + ast.MustParseTerm("{}"), + ast.MustParseTerm("{}"), + ) + + cases := []struct { + note string + schema string + query string + result *ast.Term + wantErr bool + }{ + { + note: "valid employee query and GQL schema", + schema: employeeGQLSchema, + query: `{ employeeByID(id: "alice") { salary } }`, + result: ast.ArrayTerm( + ast.BooleanTerm(true), + ast.NewTerm(employeeGQLQueryASTObj), + ast.NewTerm(employeeGQLSchemaASTObj), + ), + wantErr: false, + }, + { + note: "valid employee schema, invalid query", + schema: employeeGQLSchema, + query: `{employeeByID("alice"`, + result: failureResult, + wantErr: false, + }, + { + note: "invalid schema", + schema: invalidEmployeeGQLSchema, + query: `{ employeeByID(id: "alice") { salary } }`, + result: failureResult, + wantErr: false, + }, + { + note: "invalid query", + schema: employeeGQLSchema, + query: `{ employeeByID(id:"bob") } `, // missing fields + result: failureResult, + wantErr: false, + }, + { + note: "empty schema is not ok", + schema: ``, + query: `{ employeeByID(id: "charlie") { id salary } }`, + result: failureResult, + wantErr: false, + }, + { + note: "empty query is ok", + schema: employeeGQLSchema, + query: ``, + result: ast.ArrayTerm( + ast.BooleanTerm(true), + ast.MustParseTerm("{}"), + ast.NewTerm(employeeGQLSchemaASTObj), + ), + wantErr: false, + }, + } + + valueCache := cache.NewInterQueryValueCache( + context.Background(), + &cache.Config{ + InterQueryBuiltinValueCache: cache.InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*cache.NamedValueCacheConfig{ + gqlCacheName: { + MaxNumEntries: &[]int{10}[0], + }, + }, + }, + }) + + for _, tc := range cases { + + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var result *ast.Term + var err error + // Call function multiple times to hit the cache + for i := 1; i <= 3; i++ { + + err = builtinGraphQLParseAndVerify( + BuiltinContext{ + InterQueryBuiltinValueCache: valueCache, + }, + []*ast.Term{ast.NewTerm(ast.String(tc.query)), ast.NewTerm(ast.String(tc.schema))}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + // Check each item in array result + for i := range tc.result.Value.(*ast.Array).Len() { + expected := tc.result.Value.(*ast.Array).Elem(i) + actual := result.Value.(*ast.Array).Elem(i) + if !expected.Equal(actual) { + fmt.Fprintf(os.Stderr, "DEBUG: expected:\n%s\ngot:\n%s\n", expected.String(), actual.String()) + t.Errorf("Unexpected value at result[%d] (%s), expected %#v, got %#v", i, resultItemDescription[i], expected, actual) + return + } + } + } + // Without the cache + err = builtinGraphQLParseAndVerify( + BuiltinContext{ + InterQueryBuiltinValueCache: nil, + }, + []*ast.Term{ast.NewTerm(ast.String(tc.query)), ast.NewTerm(ast.String(tc.schema))}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + // Check each item in array result + for i := range tc.result.Value.(*ast.Array).Len() { + expected := tc.result.Value.(*ast.Array).Elem(i) + actual := result.Value.(*ast.Array).Elem(i) + if !expected.Equal(actual) { + t.Errorf("Unexpected value at result[%d] (%s), expected %#v, got %#v", i, resultItemDescription[i], expected, actual) + return + } + } + }) + } +} + +func TestGraphQLIsValid(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + query *ast.Term + schema *ast.Term + result ast.Value + wantErr bool + }{ + { + note: "valid employee - query as string", + query: ast.NewTerm(ast.String(`{ employeeByID(id: "alice") { salary } }`)), + schema: ast.NewTerm(ast.String(employeeGQLSchema)), + result: ast.Boolean(true), + wantErr: false, + }, + { + note: "valid employee - query as object", + query: ast.NewTerm(ast.MustParseTerm(employeeGQLQueryAST).Value.(ast.Object)), + schema: ast.NewTerm(ast.String(employeeGQLSchema)), + result: ast.Boolean(true), + wantErr: false, + }, + { + note: "invalid schema", + query: ast.NewTerm(ast.String(`{ employeeByID(id: "alice") { salary } }`)), + schema: ast.NewTerm(ast.String(invalidEmployeeGQLSchema)), + result: ast.Boolean(false), + wantErr: false, + }, + { + note: "invalid query", + query: ast.NewTerm(ast.String(`{ employeeByID(id: "bob") }`)), + schema: ast.NewTerm(ast.String(employeeGQLSchema)), + result: ast.Boolean(false), + wantErr: false, + }, + } + + valueCache := cache.NewInterQueryValueCache( + context.Background(), + &cache.Config{ + InterQueryBuiltinValueCache: cache.InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*cache.NamedValueCacheConfig{ + gqlCacheName: { + MaxNumEntries: &[]int{10}[0], + }, + }, + }, + }) + + for _, tc := range cases { + + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var result ast.Value + var err error + // Call function multiple times to hit the cache + for i := 1; i <= 3; i++ { + err = builtinGraphQLIsValid( + BuiltinContext{ + InterQueryBuiltinValueCache: valueCache, + }, + []*ast.Term{tc.query, tc.schema}, + func(term *ast.Term) error { + result = term.Value + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + if tc.result != result { + t.Errorf("Unexpected result, expected %#v, got %#v", tc.result, result) + return + } + } + // Without the cache + err = builtinGraphQLIsValid( + BuiltinContext{ + InterQueryBuiltinValueCache: nil, + }, + []*ast.Term{tc.query, tc.schema}, + func(term *ast.Term) error { + result = term.Value + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + if tc.result != result { + t.Errorf("Unexpected result, expected %#v, got %#v", tc.result, result) + return + } + }) + } +} + +func TestGraphQLParseQuery(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + query *ast.Term + result *ast.Term + wantErr bool + }{ + { + note: "valid employee - query as string", + query: ast.NewTerm(ast.String(`{ employeeByID(id: "alice") { salary } }`)), + result: ast.NewTerm(employeeGQLQueryASTObj), + wantErr: false, + }, + { + note: "invalid query", + query: ast.NewTerm(ast.String(`{ employeeByID("id: bob") }`)), + result: nil, + wantErr: true, + }, + { + note: "empty query is valid", + query: ast.NewTerm(ast.String(``)), + result: ast.MustParseTerm("{}"), + wantErr: false, + }, + } + + valueCache := cache.NewInterQueryValueCache( + context.Background(), + &cache.Config{ + InterQueryBuiltinValueCache: cache.InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*cache.NamedValueCacheConfig{ + gqlCacheName: { + MaxNumEntries: &[]int{10}[0], + }, + }, + }, + }) + + for _, tc := range cases { + + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var result *ast.Term + var err error + // Call function multiple times to hit the cache + for i := 1; i <= 3; i++ { + err = builtinGraphQLParseQuery( + BuiltinContext{ + InterQueryBuiltinValueCache: valueCache, + }, + []*ast.Term{tc.query}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + if !tc.result.Equal(result) { + t.Errorf("Unexpected result, expected %#v, got %#v", tc.result, result) + return + } + } + // Without the cache + err = builtinGraphQLParseQuery( + BuiltinContext{ + InterQueryBuiltinValueCache: nil, + }, + []*ast.Term{tc.query}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + if !tc.result.Equal(result) { + t.Errorf("Unexpected result, expected %#v, got %#v", tc.result, result) + return + } + }) + } +} + +func TestGraphQLParseSchema(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + schema *ast.Term + result *ast.Term + wantErr bool + }{ + { + note: "valid schema as string", + schema: ast.NewTerm(ast.String(employeeGQLSchema)), + result: ast.NewTerm(employeeGQLSchemaASTObj), + wantErr: false, + }, + { + note: "invalid schema as string", + schema: ast.NewTerm(ast.String(invalidEmployeeGQLSchema)), + result: nil, + wantErr: true, + }, + { + note: "empty schema is valid", + schema: ast.NewTerm(ast.String(``)), + result: ast.MustParseTerm("{}"), + wantErr: false, + }, + } + + valueCache := cache.NewInterQueryValueCache( + context.Background(), + &cache.Config{ + InterQueryBuiltinValueCache: cache.InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*cache.NamedValueCacheConfig{ + gqlCacheName: { + MaxNumEntries: &[]int{10}[0], + }, + }, + }, + }) + + for _, tc := range cases { + + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var result *ast.Term + var err error + // Call function multiple times to hit the cache + for i := 1; i <= 3; i++ { + err = builtinGraphQLParseSchema( + BuiltinContext{ + InterQueryBuiltinValueCache: valueCache, + }, + []*ast.Term{tc.schema}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + if !tc.result.Equal(result) { + t.Errorf("Unexpected result, expected %#v, got %#v", tc.result, result) + return + } + } + // Without the cache + err = builtinGraphQLParseSchema( + BuiltinContext{ + InterQueryBuiltinValueCache: nil, + }, + []*ast.Term{tc.schema}, + func(term *ast.Term) error { + result = term + return nil + }, + ) + if tc.wantErr && err == nil { + t.Errorf("Unexpected return value, expected error, got nil") + return + } + if !tc.wantErr && err != nil { + t.Errorf("Unexpected return value, expected nil, got error: %s", err) + return + } + if !tc.result.Equal(result) { + t.Errorf("Unexpected result, expected %#v, got %#v", tc.result, result) + return + } + }) + } +} + +func TestGraphQLParseSchemaAlloc(t *testing.T) { + cases := []struct { + note string + schema *ast.Term + maxAlloc uint64 + }{ + { + note: "default schema", + schema: ast.NewTerm(ast.String(schemaWithExtraEmployeeTypes(0))), + maxAlloc: 1 * 1024 * 1024, + }, + // Uncomment when https://github.com/open-policy-agent/opa/pull/7509 is merged + // { + // note: "default schema plus 100 additional types", + // schema: ast.NewTerm(ast.String(schemaWithExtraEmployeeTypes(100))), + // maxAlloc: 10 * 1024 * 1024, + // }, + // { + // note: "default schema plus 1,000 additional types", + // schema: ast.NewTerm(ast.String(schemaWithExtraEmployeeTypes(1000))), + // maxAlloc: 50 * 1024 * 1024, + // }, + // { + // note: "default schema plus 10,000 additional types", + // schema: ast.NewTerm(ast.String(schemaWithExtraEmployeeTypes(10000))), + // maxAlloc: 100 * 1024 * 1024, + // }, + } + + for _, tc := range cases { + + t.Run(tc.note, func(t *testing.T) { + + var startMemStats runtime.MemStats + runtime.ReadMemStats(&startMemStats) + + _ = builtinGraphQLParseSchema( + BuiltinContext{ + InterQueryBuiltinValueCache: nil, + }, + []*ast.Term{tc.schema}, + func(term *ast.Term) error { + return nil + }, + ) + + var finishMemStats runtime.MemStats + runtime.ReadMemStats(&finishMemStats) + allocDifference := finishMemStats.Alloc - startMemStats.Alloc + runtime.GC() + + if allocDifference > tc.maxAlloc { + t.Errorf("Parsing schema '%s' expected alloc < %d, got %d", tc.note, tc.maxAlloc, allocDifference) + return + } + }) + } +} + +func TestFormatGqlParserError(t *testing.T) { + testCases := []struct { + desc string + inErr error + outErr error + }{ + // Expected errors based on https://github.com/vektah/gqlparser/blob/master/gqlerror/error.go#L40-L67 + { + desc: "valid gqlparser error with filename and no location", + inErr: errors.New("filename.gql: error string with filename and no location"), + outErr: errors.New("GraphQL parse error: filename.gql: error string with filename and no location"), + }, + { + desc: "valid gqlparser error with filename and location", + inErr: errors.New("filename.gql:1:2: error string with filename and location"), + outErr: errors.New("error string with filename and location in GraphQL string at location 1:2"), + }, + { + desc: "valid gqlparser error without filename and no location", + inErr: errors.New("input: error string without filename and no location"), + outErr: errors.New("GraphQL parse error: input: error string without filename and no location"), + }, + { + desc: "valid gqlparser error without filename and with location", + inErr: errors.New("input:1:2: error string without filename and with location"), + outErr: errors.New("error string without filename and with location in GraphQL string at location 1:2"), + }, + // Unexpected errors + { + desc: "Handle nil even though it is unnecessary today", + inErr: nil, + outErr: nil, + }, + { + desc: "empty", + inErr: errors.New(""), + outErr: errors.New("GraphQL parse error: "), + }, + { + desc: "string with no :", + inErr: errors.New("test"), + outErr: errors.New("GraphQL parse error: test"), + }, + { + desc: "string with 2:", + inErr: errors.New("x:y:z"), + outErr: errors.New("GraphQL parse error: x:y:z"), + }, + { + desc: "string with 3: and alpha locations", + inErr: errors.New("input: b:c:d"), + outErr: errors.New("GraphQL parse error: input: b:c:d"), + }, + { + desc: "string with 8: and empty locations", + inErr: errors.New("::::::::"), + outErr: errors.New("GraphQL parse error: ::::::::"), + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + gotErr := formatGqlParserError(tc.inErr) + if gotErr == nil { + if tc.outErr != nil { + t.Errorf("gotErr = %v, wantErr %v", gotErr, tc.outErr) + return + } + } else if gotErr.Error() != tc.outErr.Error() { + t.Errorf("gotErr = %v, wantErr %v", gotErr, tc.outErr) + } + }) + } +} + +// Inflate GraphQL schema size with `count` extra types +func schemaWithExtraEmployeeTypes(count int) string { + + // build up `count` more types on basic schema + var builder strings.Builder + builder.WriteString(employeeGQLSchema) + + for i := range count { + fmt.Fprintf(&builder, "\ntype Employee%d {\n id: String!\n salary: Int!\n}\n", i) + } + + return builder.String() +} diff --git a/third_party/opa/v1/topdown/http.go b/third_party/opa/v1/topdown/http.go new file mode 100644 index 000000000000..36fa1572ec6f --- /dev/null +++ b/third_party/opa/v1/topdown/http.go @@ -0,0 +1,1640 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "bytes" + "context" + "crypto/tls" + "crypto/x509" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "mime" + "net" + "net/http" + "net/url" + "os" + "runtime" + "slices" + "strconv" + "strings" + "time" + + "github.com/open-policy-agent/opa/internal/version" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/tracing" + "github.com/open-policy-agent/opa/v1/util" +) + +type cachingMode string + +const ( + defaultHTTPRequestTimeoutEnv = "HTTP_SEND_TIMEOUT" + defaultCachingMode cachingMode = "serialized" + cachingModeDeserialized cachingMode = "deserialized" +) + +var defaultHTTPRequestTimeout = time.Second * 5 + +var allowedKeyNames = [...]string{ + "method", + "url", + "body", + "enable_redirect", + "force_json_decode", + "force_yaml_decode", + "headers", + "raw_body", + "tls_use_system_certs", + "tls_ca_cert", + "tls_ca_cert_file", + "tls_ca_cert_env_variable", + "tls_client_cert", + "tls_client_cert_file", + "tls_client_cert_env_variable", + "tls_client_key", + "tls_client_key_file", + "tls_client_key_env_variable", + "tls_insecure_skip_verify", + "tls_server_name", + "timeout", + "cache", + "force_cache", + "force_cache_duration_seconds", + "raise_error", + "caching_mode", + "max_retry_attempts", + "cache_ignored_headers", +} + +// ref: https://www.rfc-editor.org/rfc/rfc7231#section-6.1 +var cacheableHTTPStatusCodes = [...]int{ + http.StatusOK, + http.StatusNonAuthoritativeInfo, + http.StatusNoContent, + http.StatusPartialContent, + http.StatusMultipleChoices, + http.StatusMovedPermanently, + http.StatusNotFound, + http.StatusMethodNotAllowed, + http.StatusGone, + http.StatusRequestURITooLong, + http.StatusNotImplemented, +} + +var ( + httpSendNetworkErrTerm = ast.StringTerm(HTTPSendNetworkErr) + httpSendInternalErrTerm = ast.StringTerm(HTTPSendInternalErr) + + allowedKeys = ast.NewSet() + keyCache = make(map[string]*ast.Term, len(allowedKeyNames)) + cacheableCodes = ast.NewSet() + requiredKeys = ast.NewSet(ast.InternedTerm("method"), ast.InternedTerm("url")) + httpSendLatencyMetricKey = "rego_builtin_http_send" + httpSendInterQueryCacheHits = httpSendLatencyMetricKey + "_interquery_cache_hits" +) + +type httpSendKey string + +// CustomizeRoundTripper allows customizing an existing http.Transport, +// to the returned value, which could be the same Transport or a new one. +type CustomizeRoundTripper func(*http.Transport) http.RoundTripper + +const ( + // httpSendBuiltinCacheKey is the key in the builtin context cache that + // points to the http.send() specific cache resides at. + httpSendBuiltinCacheKey httpSendKey = "HTTP_SEND_CACHE_KEY" + + // HTTPSendInternalErr represents a runtime evaluation error. + HTTPSendInternalErr string = "eval_http_send_internal_error" + + // HTTPSendNetworkErr represents a network error. + HTTPSendNetworkErr string = "eval_http_send_network_error" + + // minRetryDelay is amount of time to backoff after the first failure. + minRetryDelay = time.Millisecond * 100 + + // maxRetryDelay is the upper bound of backoff delay. + maxRetryDelay = time.Second * 60 +) + +func builtinHTTPSend(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + obj, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return handleBuiltinErr(ast.HTTPSend.Name, bctx.Location, err) + } + + raiseError, err := getRaiseErrorValue(obj) + if err != nil { + return handleBuiltinErr(ast.HTTPSend.Name, bctx.Location, err) + } + + req, err := validateHTTPRequestOperand(operands[0], 1) + if err != nil { + if raiseError { + return handleHTTPSendErr(bctx, err) + } + + return iter(generateRaiseErrorResult(handleBuiltinErr(ast.HTTPSend.Name, bctx.Location, err))) + } + + result, err := getHTTPResponse(bctx, req) + if err != nil { + if raiseError { + return handleHTTPSendErr(bctx, err) + } + + result = generateRaiseErrorResult(err) + } + return iter(result) +} + +func generateRaiseErrorResult(err error) *ast.Term { + var errObj ast.Object + switch err.(type) { + case *url.Error: + errObj = ast.NewObject( + ast.Item(ast.InternedTerm("code"), httpSendNetworkErrTerm), + ast.Item(ast.InternedTerm("message"), ast.StringTerm(err.Error())), + ) + default: + errObj = ast.NewObject( + ast.Item(ast.InternedTerm("code"), httpSendInternalErrTerm), + ast.Item(ast.InternedTerm("message"), ast.StringTerm(err.Error())), + ) + } + + return ast.ObjectTerm( + ast.Item(ast.InternedTerm("status_code"), ast.InternedTerm(0)), + ast.Item(ast.InternedTerm("error"), ast.NewTerm(errObj)), + ) +} + +func getHTTPResponse(bctx BuiltinContext, req ast.Object) (*ast.Term, error) { + + bctx.Metrics.Timer(httpSendLatencyMetricKey).Start() + defer bctx.Metrics.Timer(httpSendLatencyMetricKey).Stop() + + 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 + } + + if resp == nil { + httpResp, err := reqExecutor.ExecuteHTTPRequest() + if err != nil { + reqExecutor.InsertErrorIntoCache(err) + return nil, err + } + defer util.Close(httpResp) + // Add result to intra/inter-query cache. + resp, err = reqExecutor.InsertIntoCache(httpResp) + if err != nil { + return nil, err + } + } + + 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(keyCache["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.InternedNullTerm) + return key, nil + } + var cacheIgnoredHeaders []string + err := ast.As(cacheIgnoredHeadersTerm.Value, &cacheIgnoredHeaders) + if err != nil { + return nil, err + } + var allHeaders map[string]any + err = ast.As(allHeadersTerm.Value, &allHeaders) + if err != nil { + return nil, err + } + for _, header := range cacheIgnoredHeaders { + delete(allHeaders, header) + } + val, err := ast.InterfaceToValue(allHeaders) + if err != nil { + return nil, err + } + key.Insert(keyCache["headers"], ast.NewTerm(val)) + // remove cache_ignored_headers key + key.Insert(keyCache["cache_ignored_headers"], ast.InternedNullTerm) + return key, nil +} + +func init() { + createKeys() + createCacheableHTTPStatusCodes() + initDefaults() + RegisterBuiltinFunc(ast.HTTPSend.Name, builtinHTTPSend) +} + +func handleHTTPSendErr(bctx BuiltinContext, err error) error { + // Return HTTP client timeout errors in a generic error message to avoid confusion about what happened. + // Do not do this if the builtin context was cancelled and is what caused the request to stop. + if urlErr, ok := err.(*url.Error); ok && urlErr.Timeout() && bctx.Context.Err() == nil { + err = fmt.Errorf("%s %s: request timed out", urlErr.Op, urlErr.URL) + } + if err := bctx.Context.Err(); err != nil { + return Halt{ + Err: &Error{ + Code: CancelErr, + Message: fmt.Sprintf("http.send: timed out (%s)", err.Error()), + }, + } + } + return handleBuiltinErr(ast.HTTPSend.Name, bctx.Location, err) +} + +func initDefaults() { + timeoutDuration := os.Getenv(defaultHTTPRequestTimeoutEnv) + if timeoutDuration != "" { + var err error + defaultHTTPRequestTimeout, err = time.ParseDuration(timeoutDuration) + if err != nil { + // If it is set to something not valid don't let the process continue in a state + // that will almost definitely give unexpected results by having it set at 0 + // which means no timeout.. + // This environment variable isn't considered part of the public API. + // TODO(patrick-east): Remove the environment variable + panic(fmt.Sprintf("invalid value for HTTP_SEND_TIMEOUT: %s", err)) + } + } +} + +func validateHTTPRequestOperand(term *ast.Term, pos int) (ast.Object, error) { + + obj, err := builtins.ObjectOperand(term.Value, pos) + if err != nil { + return nil, err + } + + requestKeys := ast.NewSet(obj.Keys()...) + + invalidKeys := requestKeys.Diff(allowedKeys) + if invalidKeys.Len() != 0 { + return nil, builtins.NewOperandErr(pos, "invalid request parameters(s): %v", invalidKeys) + } + + missingKeys := requiredKeys.Diff(requestKeys) + if missingKeys.Len() != 0 { + return nil, builtins.NewOperandErr(pos, "missing required request parameters(s): %v", missingKeys) + } + + return obj, nil + +} + +// canonicalizeHeaders returns a copy of the headers where the keys are in +// canonical HTTP form. +func canonicalizeHeaders(headers map[string]any) map[string]any { + canonicalized := map[string]any{} + + for k, v := range headers { + canonicalized[http.CanonicalHeaderKey(k)] = v + } + + return canonicalized +} + +// useSocket examines the url for "unix://" and returns a *http.Transport with +// a DialContext that opens a socket (specified in the http call). +// The url is expected to contain socket=/path/to/socket (url encoded) +// Ex. "unix://localhost/end/point?socket=%2Ftmp%2Fhttp.sock" +func useSocket(rawURL string, tlsConfig *tls.Config) (bool, string, *http.Transport) { + u, err := url.Parse(rawURL) + if err != nil { + return false, "", nil + } + + if u.Scheme != "unix" || u.RawQuery == "" { + return false, rawURL, nil + } + + v, err := url.ParseQuery(u.RawQuery) + if err != nil { + return false, rawURL, nil + } + + // Rewrite URL targeting the UNIX domain socket. + u.Scheme = "http" + + // Extract the path to the socket. + // Only retrieve the first value. Subsequent values are ignored and removed + // to prevent HTTP parameter pollution. + socket := v.Get("socket") + v.Del("socket") + u.RawQuery = v.Encode() + + tr := http.DefaultTransport.(*http.Transport).Clone() + tr.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) { + return http.DefaultTransport.(*http.Transport).DialContext(ctx, "unix", socket) + } + tr.TLSClientConfig = tlsConfig + tr.DisableKeepAlives = true + + return true, u.String(), tr +} + +func verifyHost(bctx BuiltinContext, host string) error { + if bctx.Capabilities == nil || bctx.Capabilities.AllowNet == nil { + return nil + } + + if slices.Contains(bctx.Capabilities.AllowNet, host) { + return nil + } + + return fmt.Errorf("unallowed host: %s", host) +} + +func verifyURLHost(bctx BuiltinContext, unverifiedURL string) error { + // Eager return to avoid unnecessary URL parsing + if bctx.Capabilities == nil || bctx.Capabilities.AllowNet == nil { + return nil + } + + parsedURL, err := url.Parse(unverifiedURL) + if err != nil { + return err + } + + host := strings.Split(parsedURL.Host, ":")[0] + + return verifyHost(bctx, host) +} + +func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *http.Client, error) { + var ( + url, method string + // Additional CA certificates loading options. + tlsCaCert []byte + tlsCaCertEnvVar, tlsCaCertFile string + // Client TLS certificate and key options. Each input source + // comes in a matched pair. + tlsClientCert, tlsClientKey []byte + tlsClientCertEnvVar, tlsClientKeyEnvVar string + tlsClientCertFile, tlsClientKeyFile, tlsServerName string + + body, rawBody *bytes.Buffer + enableRedirect, tlsInsecureSkipVerify bool + tlsUseSystemCerts *bool + tlsConfig tls.Config + customHeaders map[string]any + ) + + timeout := defaultHTTPRequestTimeout + + for _, val := range obj.Keys() { + key, err := ast.JSON(val.Value) + if err != nil { + return nil, nil, err + } + + key = key.(string) + + var strVal string + + if s, ok := obj.Get(val).Value.(ast.String); ok { + strVal = strings.Trim(string(s), "\"") + } else { + // Most parameters are strings, so consolidate the type checking. + switch key { + case "method", + "url", + "raw_body", + "tls_ca_cert", + "tls_ca_cert_file", + "tls_ca_cert_env_variable", + "tls_client_cert", + "tls_client_cert_file", + "tls_client_cert_env_variable", + "tls_client_key", + "tls_client_key_file", + "tls_client_key_env_variable", + "tls_server_name": + return nil, nil, fmt.Errorf("%q must be a string", key) + } + } + + switch key { + case "method": + method = strings.ToUpper(strVal) + case "url": + err := verifyURLHost(bctx, strVal) + if err != nil { + return nil, nil, err + } + url = strVal + case "enable_redirect": + enableRedirect, err = strconv.ParseBool(obj.Get(val).String()) + if err != nil { + return nil, nil, err + } + case "body": + bodyVal := obj.Get(val).Value + bodyValInterface, err := ast.JSON(bodyVal) + if err != nil { + return nil, nil, err + } + + bodyValBytes, err := json.Marshal(bodyValInterface) + if err != nil { + return nil, nil, err + } + body = bytes.NewBuffer(bodyValBytes) + case "raw_body": + rawBody = bytes.NewBufferString(strVal) + case "tls_use_system_certs": + tempTLSUseSystemCerts, err := strconv.ParseBool(obj.Get(val).String()) + if err != nil { + return nil, nil, err + } + tlsUseSystemCerts = &tempTLSUseSystemCerts + case "tls_ca_cert": + tlsCaCert = []byte(strVal) + case "tls_ca_cert_file": + tlsCaCertFile = strVal + case "tls_ca_cert_env_variable": + tlsCaCertEnvVar = strVal + case "tls_client_cert": + tlsClientCert = []byte(strVal) + case "tls_client_cert_file": + tlsClientCertFile = strVal + case "tls_client_cert_env_variable": + tlsClientCertEnvVar = strVal + case "tls_client_key": + tlsClientKey = []byte(strVal) + case "tls_client_key_file": + tlsClientKeyFile = strVal + case "tls_client_key_env_variable": + tlsClientKeyEnvVar = strVal + case "tls_server_name": + tlsServerName = strVal + case "headers": + headersVal := obj.Get(val).Value + headersValInterface, err := ast.JSON(headersVal) + if err != nil { + return nil, nil, err + } + var ok bool + customHeaders, ok = headersValInterface.(map[string]any) + if !ok { + return nil, nil, errors.New("invalid type for headers key") + } + case "tls_insecure_skip_verify": + tlsInsecureSkipVerify, err = strconv.ParseBool(obj.Get(val).String()) + if err != nil { + return nil, nil, err + } + case "timeout": + timeout, err = parseTimeout(obj.Get(val).Value) + if err != nil { + return nil, nil, err + } + case "cache", "caching_mode", + "force_cache", "force_cache_duration_seconds", + "force_json_decode", "force_yaml_decode", + "raise_error", "max_retry_attempts", "cache_ignored_headers": // no-op + default: + return nil, nil, fmt.Errorf("invalid parameter %q", key) + } + } + + isTLS := false + client := &http.Client{ + Timeout: timeout, + CheckRedirect: func(*http.Request, []*http.Request) error { + return http.ErrUseLastResponse + }, + } + + if tlsInsecureSkipVerify { + isTLS = true + tlsConfig.InsecureSkipVerify = tlsInsecureSkipVerify + } + + if len(tlsClientCert) > 0 && len(tlsClientKey) > 0 { + cert, err := tls.X509KeyPair(tlsClientCert, tlsClientKey) + if err != nil { + return nil, nil, err + } + + isTLS = true + tlsConfig.Certificates = append(tlsConfig.Certificates, cert) + } + + if tlsClientCertFile != "" && tlsClientKeyFile != "" { + cert, err := tls.LoadX509KeyPair(tlsClientCertFile, tlsClientKeyFile) + if err != nil { + return nil, nil, err + } + + isTLS = true + tlsConfig.Certificates = append(tlsConfig.Certificates, cert) + } + + if tlsClientCertEnvVar != "" && tlsClientKeyEnvVar != "" { + cert, err := tls.X509KeyPair( + []byte(os.Getenv(tlsClientCertEnvVar)), + []byte(os.Getenv(tlsClientKeyEnvVar))) + if err != nil { + return nil, nil, fmt.Errorf("cannot extract public/private key pair from envvars %q, %q: %w", + tlsClientCertEnvVar, tlsClientKeyEnvVar, err) + } + + isTLS = true + tlsConfig.Certificates = append(tlsConfig.Certificates, cert) + } + + // Use system certs if no CA cert is provided + // or system certs flag is not set + if len(tlsCaCert) == 0 && tlsCaCertFile == "" && tlsCaCertEnvVar == "" && tlsUseSystemCerts == nil { + trueValue := true + tlsUseSystemCerts = &trueValue + } + + // Check the system certificates config first so that we + // load additional certificated into the correct pool. + if tlsUseSystemCerts != nil && *tlsUseSystemCerts && runtime.GOOS != "windows" { + pool, err := x509.SystemCertPool() + if err != nil { + return nil, nil, err + } + + isTLS = true + tlsConfig.RootCAs = pool + } + + if len(tlsCaCert) != 0 { + tlsCaCert = bytes.ReplaceAll(tlsCaCert, []byte("\\n"), []byte("\n")) + pool, err := addCACertsFromBytes(tlsConfig.RootCAs, tlsCaCert) + if err != nil { + return nil, nil, err + } + + isTLS = true + tlsConfig.RootCAs = pool + } + + if tlsCaCertFile != "" { + pool, err := addCACertsFromFile(tlsConfig.RootCAs, tlsCaCertFile) + if err != nil { + return nil, nil, err + } + + isTLS = true + tlsConfig.RootCAs = pool + } + + if tlsCaCertEnvVar != "" { + pool, err := addCACertsFromEnv(tlsConfig.RootCAs, tlsCaCertEnvVar) + if err != nil { + return nil, nil, err + } + + isTLS = true + tlsConfig.RootCAs = pool + } + + var transport *http.Transport + if isTLS { + if ok, parsedURL, tr := useSocket(url, &tlsConfig); ok { + transport = tr + url = parsedURL + } else { + transport = http.DefaultTransport.(*http.Transport).Clone() + transport.TLSClientConfig = &tlsConfig + transport.DisableKeepAlives = true + } + } else { + if ok, parsedURL, tr := useSocket(url, nil); ok { + transport = tr + url = parsedURL + } + } + + if bctx.RoundTripper != nil { + client.Transport = bctx.RoundTripper(transport) + } else if transport != nil { + client.Transport = transport + } + + // check if redirects are enabled + if enableRedirect { + client.CheckRedirect = func(req *http.Request, _ []*http.Request) error { + return verifyURLHost(bctx, req.URL.String()) + } + } + + if rawBody != nil { + body = rawBody + } else if body == nil { + body = bytes.NewBufferString("") + } + + // create the http request, use the builtin context's context to ensure + // the request is cancelled if evaluation is cancelled. + req, err := http.NewRequest(method, url, body) + if err != nil { + return nil, nil, err + } + + req = req.WithContext(bctx.Context) + + // Add custom headers + if len(customHeaders) != 0 { + customHeaders = canonicalizeHeaders(customHeaders) + + for k, v := range customHeaders { + header, ok := v.(string) + if !ok { + return nil, nil, fmt.Errorf("invalid type for headers value %q", v) + } + + req.Header.Add(k, header) + } + + // Don't overwrite or append to one that was set in the custom headers + if _, hasUA := customHeaders["User-Agent"]; !hasUA { + req.Header.Add("User-Agent", version.UserAgent) + } + + // If the caller specifies the Host header, use it for the HTTP + // request host and the TLS server name. + if host, hasHost := customHeaders["Host"]; hasHost { + host := host.(string) // We already checked that it's a string. + req.Host = host + + // Only default the ServerName if the caller has + // specified the host. If we don't specify anything, + // Go will default to the target hostname. This name + // is not the same as the default that Go populates + // `req.Host` with, which is why we don't just set + // this unconditionally. + tlsConfig.ServerName = host + } + } + + if tlsServerName != "" { + tlsConfig.ServerName = tlsServerName + } + + if len(bctx.DistributedTracingOpts) > 0 { + client.Transport = tracing.NewTransport(client.Transport, bctx.DistributedTracingOpts) + } + + return req, client, nil +} + +func executeHTTPRequest(req *http.Request, client *http.Client, inputReqObj ast.Object) (*http.Response, error) { + var err error + var retry int + + retry, err = getNumberValFromReqObj(inputReqObj, keyCache["max_retry_attempts"]) + if err != nil { + return nil, err + } + + for i := 0; true; i++ { + + var resp *http.Response + resp, err = client.Do(req) + if err == nil { + return resp, nil + } + + // final attempt + if i == retry { + break + } + + if err == context.Canceled { + return nil, err + } + + delay := util.DefaultBackoff(float64(minRetryDelay), float64(maxRetryDelay), i) + timer, timerCancel := util.TimerWithCancel(delay) + select { + case <-timer.C: + case <-req.Context().Done(): + timerCancel() // explicitly cancel the timer. + return nil, context.Canceled + } + } + return nil, err +} + +func isJSONType(header http.Header) bool { + t, _, err := mime.ParseMediaType(header.Get("Content-Type")) + if err != nil { + return false + } + + mediaType := strings.Split(t, "/") + if len(mediaType) != 2 { + return false + } + + if mediaType[0] == "application" { + if mediaType[1] == "json" || strings.HasSuffix(mediaType[1], "+json") { + return true + } + } + + return false +} + +func isContentType(header http.Header, typ ...string) bool { + for _, t := range typ { + if strings.Contains(header.Get("Content-Type"), t) { + return true + } + } + return false +} + +type httpSendCacheEntry struct { + response *ast.Value + error error +} + +// The httpSendCache is used for intra-query caching of http.send results. +type httpSendCache struct { + entries *util.HasherMap[ast.Value, httpSendCacheEntry] +} + +func newHTTPSendCache() *httpSendCache { + return &httpSendCache{ + entries: util.NewHasherMap[ast.Value, httpSendCacheEntry](ast.ValueEqual), + } +} + +func (cache *httpSendCache) get(k ast.Value) *httpSendCacheEntry { + if v, ok := cache.entries.Get(k); ok { + return &v + } + return nil +} + +func (cache *httpSendCache) putResponse(k ast.Value, v *ast.Value) { + cache.entries.Put(k, httpSendCacheEntry{response: v}) +} + +func (cache *httpSendCache) putError(k ast.Value, v error) { + cache.entries.Put(k, httpSendCacheEntry{error: v}) +} + +// In the BuiltinContext cache we only store a single entry that points to +// our ValueMap which is the "real" http.send() cache. +func getHTTPSendCache(bctx BuiltinContext) *httpSendCache { + raw, ok := bctx.Cache.Get(httpSendBuiltinCacheKey) + if !ok { + // Initialize if it isn't there + c := newHTTPSendCache() + bctx.Cache.Put(httpSendBuiltinCacheKey, c) + return c + } + + c, ok := raw.(*httpSendCache) + if !ok { + return nil + } + return c +} + +// checkHTTPSendCache checks for the given key's value in the cache +func checkHTTPSendCache(bctx BuiltinContext, key ast.Object) (ast.Value, error) { + requestCache := getHTTPSendCache(bctx) + if requestCache == nil { + return nil, nil + } + + v := requestCache.get(key) + if v != nil { + if v.error != nil { + return nil, v.error + } + if v.response != nil { + return *v.response, nil + } + // This should never happen + } + + return nil, nil +} + +func insertIntoHTTPSendCache(bctx BuiltinContext, key ast.Object, value ast.Value) { + requestCache := getHTTPSendCache(bctx) + if requestCache == nil { + // Should never happen.. if it does just skip caching the value + // FIXME: return error instead, to prevent inconsistencies? + return + } + requestCache.putResponse(key, &value) +} + +func insertErrorIntoHTTPSendCache(bctx BuiltinContext, key ast.Object, err error) { + requestCache := getHTTPSendCache(bctx) + if requestCache == nil { + // Should never happen.. if it does just skip caching the value + // FIXME: return error instead, to prevent inconsistencies? + return + } + requestCache.putError(key, err) +} + +// checkHTTPSendInterQueryCache checks for the given key's value in the inter-query cache +func (c *interQueryCache) checkHTTPSendInterQueryCache() (ast.Value, error) { + requestCache := c.bctx.InterQueryBuiltinCache + + cachedValue, found := requestCache.Get(c.key) + if !found { + return nil, nil + } + + value, cerr := requestCache.Clone(cachedValue) + if cerr != nil { + return nil, handleHTTPSendErr(c.bctx, cerr) + } + + c.bctx.Metrics.Counter(httpSendInterQueryCacheHits).Incr() + var cachedRespData *interQueryCacheData + + switch v := value.(type) { + case *interQueryCacheValue: + var err error + cachedRespData, err = v.copyCacheData() + if err != nil { + return nil, err + } + case *interQueryCacheData: + cachedRespData = v + default: + return nil, nil + } + + if getCurrentTime(c.bctx).Before(cachedRespData.ExpiresAt) { + return cachedRespData.formatToAST(c.forceJSONDecode, c.forceYAMLDecode) + } + + var err error + c.httpReq, c.httpClient, err = createHTTPRequest(c.bctx, c.key) + if err != nil { + return nil, handleHTTPSendErr(c.bctx, err) + } + + headers := parseResponseHeaders(cachedRespData.Headers) + + // check with the server if the stale response is still up-to-date. + // If server returns a new response (ie. status_code=200), update the cache with the new response + // If server returns an unmodified response (ie. status_code=304), update the headers for the existing response + result, modified, err := revalidateCachedResponse(c.httpReq, c.httpClient, c.key, headers) + requestCache.Delete(c.key) + if err != nil || result == nil { + return nil, err + } + + defer result.Body.Close() + + if !modified { + // update the headers in the cached response with their corresponding values from the 304 (Not Modified) response + for headerName, values := range result.Header { + cachedRespData.Headers.Del(headerName) + for _, v := range values { + cachedRespData.Headers.Add(headerName, v) + } + } + + if forceCaching(c.forceCacheParams) { + createdAt := getCurrentTime(c.bctx) + cachedRespData.ExpiresAt = createdAt.Add(time.Second * time.Duration(c.forceCacheParams.forceCacheDurationSeconds)) + } else { + expiresAt, err := expiryFromHeaders(result.Header) + if err != nil { + return nil, err + } + cachedRespData.ExpiresAt = expiresAt + } + + cachingMode, err := getCachingMode(c.key) + if err != nil { + return nil, err + } + + var pcv cache.InterQueryCacheValue + + if cachingMode == defaultCachingMode { + pcv, err = cachedRespData.toCacheValue() + if err != nil { + return nil, err + } + } else { + pcv = cachedRespData + } + + c.bctx.InterQueryBuiltinCache.InsertWithExpiry(c.key, pcv, cachedRespData.ExpiresAt) + + return cachedRespData.formatToAST(c.forceJSONDecode, c.forceYAMLDecode) + } + + newValue, respBody, err := formatHTTPResponseToAST(result, c.forceJSONDecode, c.forceYAMLDecode) + if err != nil { + return nil, err + } + + if err := insertIntoHTTPSendInterQueryCache(c.bctx, c.key, result, respBody, c.forceCacheParams); err != nil { + return nil, err + } + + return newValue, nil +} + +// insertIntoHTTPSendInterQueryCache inserts given key and value in the inter-query cache +func insertIntoHTTPSendInterQueryCache(bctx BuiltinContext, key ast.Value, resp *http.Response, respBody []byte, cacheParams *forceCacheParams) error { + if resp == nil || (!forceCaching(cacheParams) && !canStore(resp.Header)) || !cacheableCodes.Contains(ast.InternedTerm(resp.StatusCode)) { + return nil + } + + requestCache := bctx.InterQueryBuiltinCache + + obj, ok := key.(ast.Object) + if !ok { + return errors.New("interface conversion error") + } + + cachingMode, err := getCachingMode(obj) + if err != nil { + return err + } + + var pcv cache.InterQueryCacheValue + var pcvData *interQueryCacheData + if cachingMode == defaultCachingMode { + pcv, pcvData, err = newInterQueryCacheValue(bctx, resp, respBody, cacheParams) + } else { + pcvData, err = newInterQueryCacheData(bctx, resp, respBody, cacheParams) + pcv = pcvData + } + + if err != nil { + return err + } + + requestCache.InsertWithExpiry(key, pcv, pcvData.ExpiresAt) + return nil +} + +func createKeys() { + for _, element := range allowedKeyNames { + term := ast.StringTerm(element) + + allowedKeys.Add(term) + keyCache[element] = term + } +} + +func createCacheableHTTPStatusCodes() { + for _, element := range cacheableHTTPStatusCodes { + cacheableCodes.Add(ast.InternedTerm(element)) + } +} + +func parseTimeout(timeoutVal ast.Value) (time.Duration, error) { + var timeout time.Duration + switch t := timeoutVal.(type) { + case ast.Number: + timeoutInt, ok := t.Int64() + if !ok { + return timeout, fmt.Errorf("invalid timeout number value %v, must be int64", timeoutVal) + } + return time.Duration(timeoutInt), nil + case ast.String: + // Support strings without a unit, treat them the same as just a number value (ns) + var err error + timeoutInt, err := strconv.ParseInt(string(t), 10, 64) + if err == nil { + return time.Duration(timeoutInt), nil + } + + // Try parsing it as a duration (requires a supported units suffix) + timeout, err = time.ParseDuration(string(t)) + if err != nil { + return timeout, fmt.Errorf("invalid timeout value %v: %s", timeoutVal, err) + } + return timeout, nil + default: + return timeout, builtins.NewOperandErr(1, "'timeout' must be one of {string, number} but got %s", ast.ValueName(t)) + } +} + +func getBoolValFromReqObj(req ast.Object, key *ast.Term) (bool, error) { + var b ast.Boolean + var ok bool + if v := req.Get(key); v != nil { + if b, ok = v.Value.(ast.Boolean); !ok { + return false, fmt.Errorf("invalid value for %v field", key.String()) + } + } + return bool(b), nil +} + +func getNumberValFromReqObj(req ast.Object, key *ast.Term) (int, error) { + term := req.Get(key) + if term == nil { + return 0, nil + } + + if t, ok := term.Value.(ast.Number); ok { + num, ok := t.Int() + if !ok || num < 0 { + return 0, fmt.Errorf("invalid value %v for field %v", t.String(), key.String()) + } + return num, nil + } + + return 0, fmt.Errorf("invalid value %v for field %v", term.String(), key.String()) +} + +func getCachingMode(req ast.Object) (cachingMode, error) { + key := keyCache["caching_mode"] + var s ast.String + var ok bool + if v := req.Get(key); v != nil { + if s, ok = v.Value.(ast.String); !ok { + return "", fmt.Errorf("invalid value for %v field", key.String()) + } + + switch cachingMode(s) { + case defaultCachingMode, cachingModeDeserialized: + return cachingMode(s), nil + default: + return "", fmt.Errorf("invalid value specified for %v field: %v", key.String(), string(s)) + } + } + return defaultCachingMode, nil +} + +type interQueryCacheValue struct { + Data []byte +} + +func newInterQueryCacheValue(bctx BuiltinContext, resp *http.Response, respBody []byte, cacheParams *forceCacheParams) (*interQueryCacheValue, *interQueryCacheData, error) { + data, err := newInterQueryCacheData(bctx, resp, respBody, cacheParams) + if err != nil { + return nil, nil, err + } + + b, err := json.Marshal(data) + if err != nil { + return nil, nil, err + } + return &interQueryCacheValue{Data: b}, data, nil +} + +func (cb interQueryCacheValue) Clone() (cache.InterQueryCacheValue, error) { + dup := make([]byte, len(cb.Data)) + copy(dup, cb.Data) + return &interQueryCacheValue{Data: dup}, nil +} + +func (cb interQueryCacheValue) SizeInBytes() int64 { + return int64(len(cb.Data)) +} + +func (cb *interQueryCacheValue) copyCacheData() (*interQueryCacheData, error) { + var res interQueryCacheData + err := util.UnmarshalJSON(cb.Data, &res) + if err != nil { + return nil, err + } + return &res, nil +} + +type interQueryCacheData struct { + RespBody []byte + Status string + StatusCode int + Headers http.Header + ExpiresAt time.Time +} + +func forceCaching(cacheParams *forceCacheParams) bool { + return cacheParams != nil && cacheParams.forceCacheDurationSeconds > 0 +} + +func expiryFromHeaders(headers http.Header) (time.Time, error) { + var expiresAt time.Time + maxAge, err := parseMaxAgeCacheDirective(parseCacheControlHeader(headers)) + if err != nil { + return time.Time{}, err + } + if maxAge != -1 { + createdAt, err := getResponseHeaderDate(headers) + if err != nil { + return time.Time{}, err + } + expiresAt = createdAt.Add(time.Second * time.Duration(maxAge)) + } else { + expiresAt = getResponseHeaderExpires(headers) + } + return expiresAt, nil +} + +func newInterQueryCacheData(bctx BuiltinContext, resp *http.Response, respBody []byte, cacheParams *forceCacheParams) (*interQueryCacheData, error) { + var expiresAt time.Time + + if forceCaching(cacheParams) { + createdAt := getCurrentTime(bctx) + expiresAt = createdAt.Add(time.Second * time.Duration(cacheParams.forceCacheDurationSeconds)) + } else { + var err error + expiresAt, err = expiryFromHeaders(resp.Header) + if err != nil { + return nil, err + } + } + + cv := interQueryCacheData{ + ExpiresAt: expiresAt, + RespBody: respBody, + Status: resp.Status, + StatusCode: resp.StatusCode, + Headers: resp.Header} + + return &cv, nil +} + +func (c *interQueryCacheData) formatToAST(forceJSONDecode, forceYAMLDecode bool) (ast.Value, error) { + return prepareASTResult(c.Headers, forceJSONDecode, forceYAMLDecode, c.RespBody, c.Status, c.StatusCode) +} + +func (c *interQueryCacheData) toCacheValue() (*interQueryCacheValue, error) { + b, err := json.Marshal(c) + if err != nil { + return nil, err + } + return &interQueryCacheValue{Data: b}, nil +} + +func (*interQueryCacheData) SizeInBytes() int64 { + return 0 +} + +func (c *interQueryCacheData) Clone() (cache.InterQueryCacheValue, error) { + dup := make([]byte, len(c.RespBody)) + copy(dup, c.RespBody) + + return &interQueryCacheData{ + ExpiresAt: c.ExpiresAt, + RespBody: dup, + Status: c.Status, + StatusCode: c.StatusCode, + Headers: c.Headers.Clone()}, nil +} + +type responseHeaders struct { + etag string // identifier for a specific version of the response + lastModified string // date and time response was last modified as per origin server +} + +// deltaSeconds specifies a non-negative integer, representing +// time in seconds: http://tools.ietf.org/html/rfc7234#section-1.2.1 +type deltaSeconds int32 + +func parseResponseHeaders(headers http.Header) *responseHeaders { + result := responseHeaders{} + + result.etag = headers.Get("etag") + + result.lastModified = headers.Get("last-modified") + + return &result +} + +func revalidateCachedResponse(req *http.Request, client *http.Client, inputReqObj ast.Object, headers *responseHeaders) (*http.Response, bool, error) { + etag := headers.etag + lastModified := headers.lastModified + + if etag == "" && lastModified == "" { + return nil, false, nil + } + + cloneReq := req.Clone(req.Context()) + + if etag != "" { + cloneReq.Header.Set("if-none-match", etag) + } + + if lastModified != "" { + cloneReq.Header.Set("if-modified-since", lastModified) + } + + response, err := executeHTTPRequest(cloneReq, client, inputReqObj) + if err != nil { + return nil, false, err + } + + switch response.StatusCode { + case http.StatusOK: + return response, true, nil + + case http.StatusNotModified: + return response, false, nil + } + util.Close(response) + return nil, false, nil +} + +func canStore(headers http.Header) bool { + ccHeaders := parseCacheControlHeader(headers) + + // Check "no-store" cache directive + // The "no-store" response directive indicates that a cache MUST NOT + // store any part of either the immediate request or response. + if _, ok := ccHeaders["no-store"]; ok { + return false + } + return true +} + +func getCurrentTime(bctx BuiltinContext) time.Time { + var current time.Time + + value, err := ast.JSON(bctx.Time.Value) + if err != nil { + return current + } + + valueNum, ok := value.(json.Number) + if !ok { + return current + } + + valueNumInt, err := valueNum.Int64() + if err != nil { + return current + } + + current = time.Unix(0, valueNumInt).UTC() + return current +} + +func parseCacheControlHeader(headers http.Header) map[string]string { + ccDirectives := map[string]string{} + ccHeader := headers.Get("cache-control") + + for _, part := range strings.Split(ccHeader, ",") { + part = strings.Trim(part, " ") + if part == "" { + continue + } + if strings.ContainsRune(part, '=') { + items := strings.Split(part, "=") + if len(items) != 2 { + continue + } + ccDirectives[strings.Trim(items[0], " ")] = strings.Trim(items[1], ",") + } else { + ccDirectives[part] = "" + } + } + + return ccDirectives +} + +func getResponseHeaderDate(headers http.Header) (date time.Time, err error) { + dateHeader := headers.Get("date") + if dateHeader == "" { + err = errors.New("no date header") + return + } + return http.ParseTime(dateHeader) +} + +func getResponseHeaderExpires(headers http.Header) time.Time { + expiresHeader := headers.Get("expires") + if expiresHeader == "" { + return time.Time{} + } + + date, err := http.ParseTime(expiresHeader) + if err != nil { + // servers can set `Expires: 0` which is an invalid date to indicate expired content + return time.Time{} + } + + return date +} + +// parseMaxAgeCacheDirective parses the max-age directive expressed in delta-seconds as per +// https://tools.ietf.org/html/rfc7234#section-1.2.1 +func parseMaxAgeCacheDirective(cc map[string]string) (deltaSeconds, error) { + maxAge, ok := cc["max-age"] + if !ok { + return deltaSeconds(-1), nil + } + + val, err := strconv.ParseUint(maxAge, 10, 32) + if err != nil { + if numError, ok := err.(*strconv.NumError); ok { + if numError.Err == strconv.ErrRange { + return deltaSeconds(math.MaxInt32), nil + } + } + return deltaSeconds(-1), err + } + + if val > math.MaxInt32 { + return deltaSeconds(math.MaxInt32), nil + } + return deltaSeconds(val), nil +} + +func formatHTTPResponseToAST(resp *http.Response, forceJSONDecode, forceYAMLDecode bool) (ast.Value, []byte, error) { + + resultRawBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, nil, err + } + + resultObj, err := prepareASTResult(resp.Header, forceJSONDecode, forceYAMLDecode, resultRawBody, resp.Status, resp.StatusCode) + if err != nil { + return nil, nil, err + } + + return resultObj, resultRawBody, nil +} + +func prepareASTResult(headers http.Header, forceJSONDecode, forceYAMLDecode bool, body []byte, status string, statusCode int) (ast.Value, error) { + var resultBody any + + // If the response body cannot be JSON/YAML decoded, + // an error will not be returned. Instead, the "body" field + // in the result will be null. + switch { + case forceJSONDecode || isJSONType(headers): + _ = util.UnmarshalJSON(body, &resultBody) + case forceYAMLDecode || isContentType(headers, "application/yaml", "application/x-yaml"): + _ = util.Unmarshal(body, &resultBody) + } + + result := make(map[string]any) + result["status"] = status + result["status_code"] = statusCode + result["body"] = resultBody + result["raw_body"] = string(body) + result["headers"] = getResponseHeaders(headers) + + resultObj, err := ast.InterfaceToValue(result) + if err != nil { + return nil, err + } + + return resultObj, nil +} + +func getResponseHeaders(headers http.Header) map[string]any { + respHeaders := map[string]any{} + for headerName, values := range headers { + var respValues []any + for _, v := range values { + respValues = append(respValues, v) + } + respHeaders[strings.ToLower(headerName)] = respValues + } + return respHeaders +} + +// httpRequestExecutor defines an interface for the http send cache +type httpRequestExecutor interface { + CheckCache() (ast.Value, error) + InsertIntoCache(value *http.Response) (ast.Value, error) + InsertErrorIntoCache(err error) + ExecuteHTTPRequest() (*http.Response, error) +} + +// newHTTPRequestExecutor returns a new HTTP request executor that wraps either an inter-query or +// intra-query cache implementation +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, req, key, forceCacheParams) + } + return newIntraQueryCache(bctx, req, key) +} + +type interQueryCache struct { + bctx BuiltinContext + req ast.Object + key ast.Object + httpReq *http.Request + httpClient *http.Client + forceJSONDecode bool + forceYAMLDecode bool + forceCacheParams *forceCacheParams +} + +func newInterQueryCache(bctx BuiltinContext, req ast.Object, key ast.Object, forceCacheParams *forceCacheParams) (*interQueryCache, error) { + return &interQueryCache{bctx: bctx, req: req, key: key, forceCacheParams: forceCacheParams}, nil +} + +// CheckCache checks the cache for the value of the key set on this object +func (c *interQueryCache) CheckCache() (ast.Value, error) { + var err error + + // Checking the intra-query cache first ensures consistency of errors and HTTP responses within a query. + resp, err := checkHTTPSendCache(c.bctx, c.key) + if err != nil { + return nil, err + } + if resp != nil { + return resp, nil + } + + c.forceJSONDecode, err = getBoolValFromReqObj(c.key, keyCache["force_json_decode"]) + if err != nil { + return nil, handleHTTPSendErr(c.bctx, err) + } + c.forceYAMLDecode, err = getBoolValFromReqObj(c.key, keyCache["force_yaml_decode"]) + if err != nil { + return nil, handleHTTPSendErr(c.bctx, err) + } + + resp, err = c.checkHTTPSendInterQueryCache() + // Always insert the result of the inter-query cache into the intra-query cache, to maintain consistency within the same query. + if err != nil { + insertErrorIntoHTTPSendCache(c.bctx, c.key, err) + } + if resp != nil { + insertIntoHTTPSendCache(c.bctx, c.key, resp) + } + return resp, err +} + +// InsertIntoCache inserts the key set on this object into the cache with the given value +func (c *interQueryCache) InsertIntoCache(value *http.Response) (ast.Value, error) { + result, respBody, err := formatHTTPResponseToAST(value, c.forceJSONDecode, c.forceYAMLDecode) + if err != nil { + return nil, handleHTTPSendErr(c.bctx, err) + } + + // Always insert into the intra-query cache, to maintain consistency within the same query. + insertIntoHTTPSendCache(c.bctx, c.key, result) + + // We ignore errors when populating the inter-query cache, because we've already populated the intra-cache, + // and query consistency is our primary concern. + _ = insertIntoHTTPSendInterQueryCache(c.bctx, c.key, value, respBody, c.forceCacheParams) + return result, nil +} + +func (c *interQueryCache) InsertErrorIntoCache(err error) { + insertErrorIntoHTTPSendCache(c.bctx, c.key, err) +} + +// ExecuteHTTPRequest executes a HTTP request +func (c *interQueryCache) ExecuteHTTPRequest() (*http.Response, error) { + var err error + c.httpReq, c.httpClient, err = createHTTPRequest(c.bctx, c.req) + if err != nil { + return nil, handleHTTPSendErr(c.bctx, err) + } + + return executeHTTPRequest(c.httpReq, c.httpClient, c.req) +} + +type intraQueryCache struct { + bctx BuiltinContext + req ast.Object + key ast.Object +} + +func newIntraQueryCache(bctx BuiltinContext, req ast.Object, key ast.Object) (*intraQueryCache, error) { + return &intraQueryCache{bctx: bctx, req: req, key: key}, nil +} + +// CheckCache checks the cache for the value of the key set on this object +func (c *intraQueryCache) CheckCache() (ast.Value, error) { + return checkHTTPSendCache(c.bctx, c.key) +} + +// InsertIntoCache inserts the key set on this object into the cache with the given value +func (c *intraQueryCache) InsertIntoCache(value *http.Response) (ast.Value, error) { + forceJSONDecode, err := getBoolValFromReqObj(c.key, keyCache["force_json_decode"]) + if err != nil { + return nil, handleHTTPSendErr(c.bctx, err) + } + forceYAMLDecode, err := getBoolValFromReqObj(c.key, keyCache["force_yaml_decode"]) + if err != nil { + return nil, handleHTTPSendErr(c.bctx, err) + } + + result, _, err := formatHTTPResponseToAST(value, forceJSONDecode, forceYAMLDecode) + if err != nil { + return nil, handleHTTPSendErr(c.bctx, err) + } + + if cacheableCodes.Contains(ast.InternedTerm(value.StatusCode)) { + insertIntoHTTPSendCache(c.bctx, c.key, result) + } + + return result, nil +} + +func (c *intraQueryCache) InsertErrorIntoCache(err error) { + insertErrorIntoHTTPSendCache(c.bctx, c.key, err) +} + +// ExecuteHTTPRequest executes a HTTP request +func (c *intraQueryCache) ExecuteHTTPRequest() (*http.Response, error) { + httpReq, httpClient, err := createHTTPRequest(c.bctx, c.req) + if err != nil { + return nil, handleHTTPSendErr(c.bctx, err) + } + return executeHTTPRequest(httpReq, httpClient, c.req) +} + +func useInterQueryCache(req ast.Object) (bool, *forceCacheParams, error) { + value, err := getBoolValFromReqObj(req, keyCache["cache"]) + if err != nil { + return false, nil, err + } + + valueForceCache, err := getBoolValFromReqObj(req, keyCache["force_cache"]) + if err != nil { + return false, nil, err + } + + if valueForceCache { + forceCacheParams, err := newForceCacheParams(req) + return true, forceCacheParams, err + } + + return value, nil, nil +} + +type forceCacheParams struct { + forceCacheDurationSeconds int32 +} + +func newForceCacheParams(req ast.Object) (*forceCacheParams, error) { + term := req.Get(keyCache["force_cache_duration_seconds"]) + if term == nil { + return nil, errors.New("'force_cache' set but 'force_cache_duration_seconds' parameter is missing") + } + + forceCacheDurationSeconds := term.String() + + value, err := strconv.ParseInt(forceCacheDurationSeconds, 10, 32) + if err != nil { + return nil, err + } + + return &forceCacheParams{forceCacheDurationSeconds: int32(value)}, nil +} + +func getRaiseErrorValue(req ast.Object) (bool, error) { + result := ast.Boolean(true) + var ok bool + if v := req.Get(keyCache["raise_error"]); v != nil { + if result, ok = v.Value.(ast.Boolean); !ok { + return false, errors.New("invalid value for raise_error field") + } + } + return bool(result), nil +} diff --git a/third_party/opa/v1/topdown/http_fixup.go b/third_party/opa/v1/topdown/http_fixup.go new file mode 100644 index 000000000000..1b9ffc2350a7 --- /dev/null +++ b/third_party/opa/v1/topdown/http_fixup.go @@ -0,0 +1,8 @@ +//go:build !go1.18 || !darwin +// +build !go1.18 !darwin + +package topdown + +func fixupDarwinGo118(x string, _ string) string { + return x +} diff --git a/third_party/opa/v1/topdown/http_fixup_darwin.go b/third_party/opa/v1/topdown/http_fixup_darwin.go new file mode 100644 index 000000000000..ff3058ef4072 --- /dev/null +++ b/third_party/opa/v1/topdown/http_fixup_darwin.go @@ -0,0 +1,13 @@ +//go:build go1.18 +// +build go1.18 + +package topdown + +func fixupDarwinGo118(x, y string) string { + switch x { + case "x509: certificate signed by unknown authority": + return y + default: + return x + } +} diff --git a/third_party/opa/v1/topdown/http_slow_test.go b/third_party/opa/v1/topdown/http_slow_test.go new file mode 100644 index 000000000000..6df27658d621 --- /dev/null +++ b/third_party/opa/v1/topdown/http_slow_test.go @@ -0,0 +1,223 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build slow +// +build slow + +package topdown + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "sync" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// Warning(philipc): This test modifies package variables, which means it cannot +// be run in parallel with other tests. +func TestHTTPSendTimeout(t *testing.T) { + // Each test can tweak the response delay, default is 0 with no delay + var responseDelay time.Duration + + tsMtx := sync.Mutex{} + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + tsMtx.Lock() + defer tsMtx.Unlock() + time.Sleep(responseDelay) + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`hello`)) + })) + // Note: We don't Close() the test server as it will block waiting for the + // timed out clients connections to shut down gracefully (they wont). + // We don't need to clean it up nicely for the unit test. + + tests := []struct { + note string + rule string + input string + defaultTimeout time.Duration + evalTimeout time.Duration + serverDelay time.Duration + expected any + }{ + { + note: "no timeout", + rule: `p = x { http.send({"method": "get", "url": "%URL%" }, resp); x := remove_headers(resp) }`, + expected: `{"body": null, "raw_body": "hello", "status": "200 OK", "status_code": 200}`, + }, + { + note: "default timeout", + rule: `p = x { http.send({"method": "get", "url": "%URL%" }, x) }`, + evalTimeout: 1 * time.Minute, + serverDelay: 5 * time.Second, + defaultTimeout: 500 * time.Millisecond, + expected: &Error{Code: BuiltinErr, Message: "request timed out"}, + }, + { + note: "eval timeout", + rule: `p = x { http.send({"method": "get", "url": "%URL%" }, x) }`, + evalTimeout: 500 * time.Millisecond, + serverDelay: 5 * time.Second, + defaultTimeout: 1 * time.Minute, + expected: &Error{Code: CancelErr, Message: "timed out (context deadline exceeded)"}, + }, + { + note: "param timeout less than default", + rule: `p = x { http.send({"method": "get", "url": "%URL%", "timeout": "500ms"}, x) }`, + evalTimeout: 1 * time.Minute, + serverDelay: 5 * time.Second, + defaultTimeout: 1 * time.Minute, + expected: &Error{Code: BuiltinErr, Message: "request timed out"}, + }, + { + note: "param timeout greater than default", + rule: `p = x { http.send({"method": "get", "url": "%URL%", "timeout": "500ms"}, x) }`, + evalTimeout: 1 * time.Minute, + serverDelay: 5 * time.Second, + defaultTimeout: 1 * time.Millisecond, + expected: &Error{Code: BuiltinErr, Message: "request timed out"}, + }, + { + note: "eval timeout less than param", + rule: `p = x { http.send({"method": "get", "url": "%URL%", "timeout": "1m" }, x) }`, + evalTimeout: 500 * time.Millisecond, + serverDelay: 5 * time.Second, + defaultTimeout: 1 * time.Minute, + expected: &Error{Code: CancelErr, Message: "timed out (context deadline exceeded)"}, + }, + } + + for _, tc := range tests { + tsMtx.Lock() + responseDelay = tc.serverDelay + tsMtx.Unlock() + + ctx := context.Background() + if tc.evalTimeout > 0 { + ctx, _ = context.WithTimeout(ctx, tc.evalTimeout) + } + + // TODO(patrick-east): Remove this along with the environment variable so that the "default" can't change + originalDefaultTimeout := defaultHTTPRequestTimeout + if tc.defaultTimeout > 0 { + defaultHTTPRequestTimeout = tc.defaultTimeout + } + + rule := strings.ReplaceAll(tc.rule, "%URL%", ts.URL) + if e, ok := tc.expected.(*Error); ok { + e.Message = strings.ReplaceAll(e.Message, "%URL%", ts.URL) + } + + runTopDownTestCaseWithContext(ctx, t, map[string]any{}, tc.note, append(httpSendHelperRules, rule), nil, tc.input, tc.expected) + + // Put back the default (may not have changed) + defaultHTTPRequestTimeout = originalDefaultTimeout + } +} + +func TestHTTPSendRetryRequest(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + query string + response string + evalTimeout time.Duration + cancel Cancel + wantErr bool + err error + }{ + { + note: "success", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "max_retry_attempts": 100, "timeout": "500ms"}, x)`, + response: `{"x": 1}`, + }, + { + note: "eval timeout", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "max_retry_attempts": 100, "timeout": "500ms"}, x)`, + evalTimeout: 2 * time.Second, + wantErr: true, + err: fmt.Errorf("eval_cancel_error: http.send: timed out (context deadline exceeded)"), + }, + { + note: "cancel query", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "max_retry_attempts": 100, "timeout": "500ms"}, x)`, + cancel: NewCancel(), + wantErr: true, + err: fmt.Errorf("eval_cancel_error: caller cancelled query execution"), + }, + } + + for _, tc := range tests { + tc := tc // copy for capturing loop variable (not needed in Go 1.22+) + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(tc.response)) + if err != nil { + t.Fatal(err) + } + })) + + defer ts.Close() + + // delay server start to exercise retry logic + go func() { + time.Sleep(time.Second * 5) + ts.Start() + }() + + ctx := context.Background() + if tc.evalTimeout > 0 { + var ctxCancel context.CancelFunc + ctx, ctxCancel = context.WithTimeout(ctx, tc.evalTimeout) + defer ctxCancel() + } + + q := newQuery(strings.ReplaceAll(tc.query, "%URL%", "http://"+ts.Listener.Addr().String()), time.Now()) + + if tc.cancel != nil { + q.WithCancel(tc.cancel) + + go func() { + time.Sleep(2 * time.Second) + tc.cancel.Cancel() + }() + } + + res, err := q.Run(ctx) + if tc.wantErr { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else { + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if len(res) != 1 { + t.Fatalf("Expected one result but got %v", len(res)) + } + + resResponse := res[0]["x"].Value.(ast.Object).Get(ast.StringTerm("raw_body")) + if ast.String(tc.response).Compare(resResponse.Value) != 0 { + t.Fatalf("Expected response %v but got %v", tc.response, resResponse.String()) + } + } + }) + } +} diff --git a/third_party/opa/v1/topdown/http_test.go b/third_party/opa/v1/topdown/http_test.go new file mode 100644 index 000000000000..1f089a9bd7fc --- /dev/null +++ b/third_party/opa/v1/topdown/http_test.go @@ -0,0 +1,3849 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: goconst // string duplication is for test readability. +package topdown + +import ( + "bytes" + "context" + "crypto/tls" + "crypto/x509" + "encoding/json" + "errors" + "fmt" + "io" + "maps" + "math" + "net" + "net/http" + "net/http/httptest" + "net/url" + "os" + "reflect" + "slices" + "strconv" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/internal/version" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/tracing" + "github.com/open-policy-agent/opa/v1/util" + + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + iCache "github.com/open-policy-agent/opa/v1/topdown/cache" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// The person Type +type Person struct { + ID string `json:"id,omitempty"` + Firstname string `json:"firstname,omitempty"` +} + +// TestHTTPGetRequest returns the list of persons +func TestHTTPGetRequest(t *testing.T) { + t.Parallel() + + var people []Person + + // test data + people = append(people, Person{ID: "1", Firstname: "John"}) + + // test server + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + headers := w.Header() + headers["test-header"] = []string{"test-value"} + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(people) + })) + + defer ts.Close() + + // expected result + expectedResult := make(map[string]any) + expectedResult["status"] = "200 OK" + expectedResult["status_code"] = http.StatusOK + + var body []any + bodyMap := map[string]string{"id": "1", "firstname": "John"} + body = append(body, bodyMap) + expectedResult["body"] = body + expectedResult["raw_body"] = "[{\"id\":\"1\",\"firstname\":\"John\"}]\n" + expectedResult["headers"] = map[string]any{ + "content-length": []any{"32"}, + "content-type": []any{"text/plain; charset=utf-8"}, + "test-header": []any{"test-value"}, + } + + resultObj := ast.MustInterfaceToValue(expectedResult) + + // run the test + tests := []struct { + note string + rules []string + expected any + }{ + {"http.send", []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": "%s", "force_json_decode": true}, resp); x := clean_headers(resp) }`, ts.URL)}, resultObj.String()}, + {"http.send skip verify no HTTPS", []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": "%s", "force_json_decode": true, "tls_insecure_skip_verify": true}, resp); x := clean_headers(resp) }`, ts.URL)}, resultObj.String()}, + } + + data := loadSmallTestData() + + for _, tc := range tests { + runTopDownTestCase(t, data, tc.note, append(tc.rules, httpSendHelperRules...), tc.expected) + } +} + +// TestHTTPGetRequest returns the list of persons +func TestHTTPGetRequestTlsInsecureSkipVerify(t *testing.T) { + t.Parallel() + + var people []Person + + // test data + people = append(people, Person{ID: "1", Firstname: "John"}) + + // test server + ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(people) + })) + defer ts.Close() + + // expected result + expectedResult := make(map[string]any) + expectedResult["status"] = "200 OK" + expectedResult["status_code"] = http.StatusOK + + var body []any + bodyMap := map[string]string{"id": "1", "firstname": "John"} + body = append(body, bodyMap) + expectedResult["body"] = body + expectedResult["raw_body"] = "[{\"id\":\"1\",\"firstname\":\"John\"}]\n" + expectedResult["headers"] = map[string]any{ + "content-length": []any{"32"}, + "content-type": []any{"text/plain; charset=utf-8"}, + } + + resultObj := ast.MustInterfaceToValue(expectedResult) + + type httpsStruct struct { + note string + rules []string + expected any + } + + // run the test + tests := []httpsStruct{} + tests = append(tests, httpsStruct{note: "http.send", rules: []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": "%s", "force_json_decode": true, "tls_insecure_skip_verify": true}, resp); x := clean_headers(resp) }`, ts.URL)}, expected: resultObj.String()}) + + // This case verifies that `tls_insecure_skip_verify` + // is still applied, even if other TLS settings are + // present. + tests = append(tests, httpsStruct{note: "http.send", rules: []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": "%s", "force_json_decode": true, "tls_insecure_skip_verify": true, "tls_use_system_certs": true,}, resp); x := clean_headers(resp) }`, ts.URL)}, expected: resultObj.String()}) + + data := loadSmallTestData() + + for _, tc := range tests { + runTopDownTestCase(t, data, tc.note, append(tc.rules, httpSendHelperRules...), tc.expected) + } +} + +func TestHTTPEnableJSONOrYAMLDecode(t *testing.T) { + t.Parallel() + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/json-no-header": + fmt.Fprintf(w, `{"foo":"bar"}`) + case "/yaml-no-header": + fmt.Fprintf(w, `foo: bar`) + case "/json": + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, `{"foo":"bar"}`) + case "/yaml": + w.Header().Set("Content-Type", "application/yaml") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, `foo: bar`) + case "/x-yaml": + w.Header().Set("Content-Type", "application/x-yaml") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, `foo: bar`) + case "/text-no-header": + fmt.Fprintf(w, "*Hello World®") + } + })) + + defer ts.Close() + + body := func(b any) func(map[string]any) { + return func(x map[string]any) { + x["body"] = b + } + } + rawBody := func(b any) func(map[string]any) { + return func(x map[string]any) { + x["raw_body"] = b + } + } + + headers := func(xs ...string) func(map[string]any) { + hdrs := map[string]any{} + for i := range len(xs) / 2 { + hdrs[xs[2*i]] = []any{xs[2*i+1]} + } + return func(x map[string]any) { + x["headers"] = hdrs + } + } + + ok := func(and ...func(map[string]any)) ast.Value { + o := map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + } + for _, a := range and { + a(o) + } + return ast.MustInterfaceToValue(o) + } + + resultObjText := ok( + body(nil), + rawBody("*Hello World®"), + headers("content-length", "14", "content-type", "text/plain; charset=utf-8"), + ) + + tests := []struct { + note string + rule string + expected ast.Value + }{ + { + note: "text response, force json", + rule: fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s/text-no-header", "force_json_decode": true}, resp); x := clean_headers(resp) }`, ts.URL), + expected: resultObjText, + }, + { + note: "text response, force yaml", + rule: fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s/text-no-header", "force_yaml_decode": true}, resp); x := clean_headers(resp) }`, ts.URL), + expected: resultObjText, + }, + { + note: "json response, proper header", + rule: fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s/json"}, resp); x := clean_headers(resp) }`, ts.URL), + expected: ok( + body(map[string]any{"foo": "bar"}), + rawBody(`{"foo":"bar"}`), + headers("content-length", "13", "content-type", "application/json"), + ), + }, + { + note: "yaml response, proper header", + rule: fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s/yaml"}, resp); x := clean_headers(resp) }`, ts.URL), + expected: ok( + body(map[string]any{"foo": "bar"}), + rawBody(`foo: bar`), + headers("content-length", "8", "content-type", "application/yaml"), + ), + }, + { + note: "yaml response, x-yaml header", + rule: fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s/x-yaml"}, resp); x := clean_headers(resp) }`, ts.URL), + expected: ok( + body(map[string]any{"foo": "bar"}), + rawBody(`foo: bar`), + headers("content-length", "8", "content-type", "application/x-yaml"), + ), + }, + { + note: "json response, no header", + rule: fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s/json-no-header", "force_json_decode": true}, resp); x := clean_headers(resp) }`, ts.URL), + expected: ok( + body(map[string]any{"foo": "bar"}), + rawBody(`{"foo":"bar"}`), + headers("content-length", "13", "content-type", "text/plain; charset=utf-8"), + ), + }, + { + note: "yaml response, no header", + rule: fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s/yaml-no-header", "force_yaml_decode": true}, resp); x := clean_headers(resp) }`, ts.URL), + expected: ok( + body(map[string]any{"foo": "bar"}), + rawBody(`foo: bar`), + headers("content-length", "8", "content-type", "text/plain; charset=utf-8"), + ), + }, + { + note: "json response, no header, yaml decode", + rule: fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s/json-no-header", "force_yaml_decode": true}, resp); x := clean_headers(resp) }`, ts.URL), + expected: ok( + body(map[string]any{"foo": "bar"}), + rawBody(`{"foo":"bar"}`), + headers("content-length", "13", "content-type", "text/plain; charset=utf-8"), + ), + }, + } + + for _, tc := range tests { + runTopDownTestCase(t, map[string]any{}, tc.note, append([]string{tc.rule}, httpSendHelperRules...), tc.expected.String()) + } +} + +func echoCustomHeaders(w http.ResponseWriter, r *http.Request) { + headers := make(map[string][]string) + w.Header().Set("Content-Type", "application/json") + for k, v := range r.Header { + if strings.HasPrefix(k, "X-") || k == "User-Agent" { + headers[k] = v + } + } + _ = json.NewEncoder(w).Encode(headers) +} + +// TestHTTPSendCustomRequestHeaders adds custom headers to request +func TestHTTPSendCustomRequestHeaders(t *testing.T) { + t.Parallel() + + // test server + ts := httptest.NewServer(http.HandlerFunc(echoCustomHeaders)) + defer ts.Close() + + // expected result with default User-Agent + expectedResult := make(map[string]any) + expectedResult["status"] = "200 OK" + expectedResult["status_code"] = http.StatusOK + + bodyMap := map[string][]string{"X-Foo": {"ISO-8859-1,utf-8;q=0.7,*;q=0.7"}, "X-Opa": {"server"}, "User-Agent": {version.UserAgent}} + expectedResult["body"] = bodyMap + expectedResult["raw_body"] = fmt.Sprintf("{\"User-Agent\":[\"%s\"],\"X-Foo\":[\"ISO-8859-1,utf-8;q=0.7,*;q=0.7\"],\"X-Opa\":[\"server\"]}\n", version.UserAgent) + + jsonString, err := json.Marshal(expectedResult) + if err != nil { + panic(err) + } + s := string(jsonString) + + // expected result with custom User-Agent + + bodyMap = map[string][]string{"X-Opa": {"server"}, "User-Agent": {"AuthZPolicy/0.0.1"}} + expectedResult["body"] = bodyMap + expectedResult["raw_body"] = "{\"User-Agent\":[\"AuthZPolicy/0.0.1\"],\"X-Opa\":[\"server\"]}\n" + + jsonString, err = json.Marshal(expectedResult) + if err != nil { + panic(err) + } + s2 := string(jsonString) + + // run the test + tests := []struct { + note string + rules []string + expected any + }{ + {"http.send custom headers", []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": "%s", "headers": {"X-Foo": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "X-Opa": "server"}}, resp); x := remove_headers(resp) }`, ts.URL)}, s}, + {"http.send custom UA", []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": "%s", "headers": {"User-Agent": "AuthZPolicy/0.0.1", "X-Opa": "server"}}, resp); x := remove_headers(resp) }`, ts.URL)}, s2}, + } + + data := loadSmallTestData() + + for _, tc := range tests { + runTopDownTestCase(t, data, tc.note, append(tc.rules, httpSendHelperRules...), tc.expected) + } +} + +// TestHTTPHostHeader tests Host header support +func TestHTTPHostHeader(t *testing.T) { + t.Parallel() + + // test server + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(r.Host) + })) + + defer ts.Close() + + expectedResult, err := json.Marshal(map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + "body": t.Name(), + "raw_body": fmt.Sprintf("\"%s\"\n", t.Name()), + "headers": map[string]any{ + "content-length": []any{"21"}, + "content-type": []any{"application/json"}, + }, + }) + if err != nil { + panic(err) + } + + data := loadSmallTestData() + + for _, h := range []string{"HOST", "Host", "host"} { + runTopDownTestCase(t, + data, + fmt.Sprintf("http.send custom Host header %q", h), + append(httpSendHelperRules, fmt.Sprintf( + `p = x { http.send({ "method": "get", "url": "%s", "headers": {"%s": "%s"}}, resp); x := clean_headers(resp) }`, ts.URL, h, t.Name()), + ), + string(expectedResult)) + } +} + +// TestHTTPPostRequest adds a new person +func TestHTTPPostRequest(t *testing.T) { + t.Parallel() + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + contentType := r.Header.Get("Content-Type") + + bs, err := io.ReadAll(r.Body) + if err != nil { + t.Fatal(err) + } + + w.Header().Set("Content-Type", contentType) + w.WriteHeader(http.StatusOK) + _, err = w.Write(bs) + if err != nil { + t.Fatal(err) + } + })) + + defer ts.Close() + + tests := []struct { + note string + params string + respHeaders string + expected any + }{ + { + note: "basic", + params: `{ + "method": "post", + "headers": {"Content-Type": "application/json"}, + "body": {"id": "2", "firstname": "Joe"} + }`, + expected: `{ + "status": "200 OK", + "status_code": 200, + "body": {"id": "2", "firstname": "Joe"}, + "raw_body": "{\"firstname\":\"Joe\",\"id\":\"2\"}", + "headers": {"content-type": ["application/json"], "content-length": ["28"]} + }`, + }, + { + note: "raw_body", + params: `{ + "method": "post", + "headers": {"content-type": "application/x-www-form-encoded"}, + "raw_body": "username=foobar&password=baz" + }`, + expected: `{ + "status": "200 OK", + "status_code": 200, + "body": null, + "raw_body": "username=foobar&password=baz", + "headers": {"content-type": ["application/x-www-form-encoded"], "content-length": ["28"]} + }`, + }, + { + note: "raw_body overrides body", + params: `{ + "method": "post", + "headers": {"content-type": "application/x-www-form-encoded"}, + "body": {"foo": 1}, + "raw_body": "username=foobar&password=baz" + }`, + expected: `{ + "status": "200 OK", + "status_code": 200, + "body": null, + "raw_body": "username=foobar&password=baz", + "headers": {"content-type": ["application/x-www-form-encoded"], "content-length": ["28"]} + }`, + }, + { + note: "raw_body bad type", + params: `{ + "method": "post", + "headers": {"content-type": "application/x-www-form-encoded"}, + "raw_body": {"bar": "bar"} + }`, + expected: &Error{Code: BuiltinErr, Message: "\"raw_body\" must be a string"}, + }, + } + + data := map[string]any{} + + for _, tc := range tests { + + // Automatically set the URL because it's generated when the test server + // is started. If needed, the test cases can override in the future. + term := ast.MustParseTerm(tc.params) + term.Value.(ast.Object).Insert(ast.StringTerm("url"), ast.StringTerm(ts.URL)) + + rules := append( + httpSendHelperRules, + fmt.Sprintf(`p = x { http.send(%s, resp); x := clean_headers(resp) }`, term), + ) + + runTopDownTestCase(t, data, tc.note, rules, tc.expected) + } +} + +func TestHTTPDeleteRequest(t *testing.T) { + t.Parallel() + + var people []Person + + // test data + people = append(people, Person{ID: "1", Firstname: "John"}, Person{ID: "2", Firstname: "Joe"}) + + // test server + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var person Person + if r.Body == nil { + http.Error(w, "Please send a request body", 400) + return + } + err := json.NewDecoder(r.Body).Decode(&person) + if err != nil { + http.Error(w, err.Error(), 400) + return + } + + // delete person + for index, item := range people { + if item.ID == person.ID { + people = slices.Delete(people, index, index+1) + break + } + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(people) + })) + + defer ts.Close() + + // expected result + expectedResult := make(map[string]any) + expectedResult["status"] = "200 OK" + expectedResult["status_code"] = http.StatusOK + + var body []any + bodyMap := map[string]string{"id": "1", "firstname": "John"} + body = append(body, bodyMap) + expectedResult["body"] = body + expectedResult["raw_body"] = "[{\"id\":\"1\",\"firstname\":\"John\"}]\n" + expectedResult["headers"] = map[string]any{ + "content-length": []any{"32"}, + "content-type": []any{"application/json"}, + } + + resultObj := ast.MustInterfaceToValue(expectedResult) + + // delete a new person + personToDelete := Person{ID: "2", Firstname: "Joe"} + b := new(bytes.Buffer) + _ = json.NewEncoder(b).Encode(personToDelete) + + // run the test + tests := []struct { + note string + rules []string + expected any + }{ + {"http.send", []string{fmt.Sprintf( + `p = x { http.send({"method": "delete", "url": "%s", "body": %s}, resp); x := clean_headers(resp) }`, ts.URL, b)}, resultObj.String()}, + } + + data := loadSmallTestData() + + for _, tc := range tests { + runTopDownTestCase(t, data, tc.note, append(tc.rules, httpSendHelperRules...), tc.expected) + } +} + +// TestInvalidKeyError returns an error when an invalid key is passed in the +// http.send builtin +func TestInvalidKeyError(t *testing.T) { + t.Parallel() + + // run the test + tests := []struct { + note string + rules []string + expected any + }{ + {"invalid keys", []string{`p = x { http.send({"method": "get", "url": "http://127.0.0.1:51113", "bad_key": "bad_value"}, x) }`}, &Error{Code: TypeErr, Message: `invalid request parameters(s): {"bad_key"}`}}, + {"missing keys", []string{`p = x { http.send({"method": "get"}, x) }`}, &Error{Code: TypeErr, Message: `missing required request parameters(s): {"url"}`}}, + } + + data := loadSmallTestData() + + for _, tc := range tests { + runTopDownTestCase(t, data, tc.note, tc.rules, tc.expected) + } +} + +func TestInvalidRetryParam(t *testing.T) { + t.Parallel() + + // run the test + tests := []struct { + note string + rules []string + expected any + }{ + {"invalid retry param", []string{`p = x { http.send({"method": "get", "url": "http://127.0.0.1:51113", "max_retry_attempts": "bad_value"}, x) }`}, &Error{Code: BuiltinErr, Message: `http.send: invalid value "bad_value" for field "max_retry_attempts"`}}, + {"invalid number", []string{`p = x { http.send({"method": "get", "url": "http://127.0.0.1:51113", "max_retry_attempts": 1.2}, x) }`}, &Error{Code: BuiltinErr, Message: `http.send: invalid value 1.2 for field "max_retry_attempts"`}}, + {"negative number", []string{`p = x { http.send({"method": "get", "url": "http://127.0.0.1:51113", "max_retry_attempts": -1000}, x) }`}, &Error{Code: BuiltinErr, Message: `http.send: invalid value -1000 for field "max_retry_attempts"`}}, + } + + data := loadSmallTestData() + + for _, tc := range tests { + runTopDownTestCase(t, data, tc.note, tc.rules, tc.expected) + } +} + +func TestParseTimeout(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + raw ast.Value + expected any + }{ + { + note: "zero string", + raw: ast.String("0"), + expected: time.Duration(0), + }, + { + note: "zero number", + raw: ast.Number(strconv.FormatInt(0, 10)), + expected: time.Duration(0), + }, + { + note: "number", + raw: ast.Number(strconv.FormatInt(1234, 10)), + expected: time.Duration(1234), + }, + { + note: "number with invalid float", + raw: ast.Number("1.234"), + expected: errors.New("invalid timeout number value"), + }, + { + note: "string no units", + raw: ast.String("1000"), + expected: time.Duration(1000), + }, + { + note: "string with units", + raw: ast.String("10ms"), + expected: time.Duration(10000000), + }, + { + note: "string with complex units", + raw: ast.String("1s10ms5us"), + expected: time.Second + (10 * time.Millisecond) + (5 * time.Microsecond), + }, + { + note: "string with invalid duration format", + raw: ast.String("1xyz 2"), + expected: errors.New("invalid timeout value"), + }, + { + note: "string with float", + raw: ast.String("1.234"), + expected: errors.New("invalid timeout value"), + }, + { + note: "invalid value type object", + raw: ast.NewObject(), + expected: builtins.NewOperandErr(1, "'timeout' must be one of {string, number} but got object"), + }, + { + note: "invalid value type set", + raw: ast.NewSet(), + expected: builtins.NewOperandErr(1, "'timeout' must be one of {string, number} but got set"), + }, + { + note: "invalid value type array", + raw: ast.NewArray(), + expected: builtins.NewOperandErr(1, "'timeout' must be one of {string, number} but got array"), + }, + { + note: "invalid value type boolean", + raw: ast.Boolean(true), + expected: builtins.NewOperandErr(1, "'timeout' must be one of {string, number} but got boolean"), + }, + { + note: "invalid value type null", + raw: ast.Null{}, + expected: builtins.NewOperandErr(1, "'timeout' must be one of {string, number} but got null"), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + actual, err := parseTimeout(tc.raw) + switch e := tc.expected.(type) { + case error: + assertError(t, tc.expected, err) + case time.Duration: + if e != actual { + t.Fatalf("Expected %d but got %d", e, actual) + } + } + }) + } +} + +// TestHTTPRedirectDisable tests redirects are not enabled by default +func TestHTTPRedirectDisable(t *testing.T) { + t.Parallel() + + // test server + baseURL, teardown := getTestServer() + defer teardown() + + // expected result + expectedResult := make(map[string]any) + expectedResult["body"] = nil + expectedResult["raw_body"] = "Moved Permanently.\n\n" + expectedResult["status"] = "301 Moved Permanently" + expectedResult["status_code"] = http.StatusMovedPermanently + expectedResult["headers"] = map[string]any{ + "content-length": []any{"40"}, + "content-type": []any{"text/html; charset=utf-8"}, + "location": []any{"/test"}, + } + + resultObj := ast.MustInterfaceToValue(expectedResult) + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s"}, resp); x := clean_headers(resp) }`, baseURL), + ) + + // run the test + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) +} + +// TestHTTPRedirectEnable tests redirects are enabled +func TestHTTPRedirectEnable(t *testing.T) { + t.Parallel() + + // test server + baseURL, teardown := getTestServer() + defer teardown() + + // expected result + expectedResult := make(map[string]any) + expectedResult["status"] = "200 OK" + expectedResult["status_code"] = http.StatusOK + expectedResult["body"] = nil + expectedResult["raw_body"] = "" + expectedResult["headers"] = map[string]any{ + "content-length": []any{"0"}, + } + + resultObj := ast.MustInterfaceToValue(expectedResult) + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s", "enable_redirect": true}, resp); x := clean_headers(resp) }`, baseURL), + ) + + // run the test + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) +} + +func TestHTTPRedirectAllowNet(t *testing.T) { + t.Parallel() + + // test server + baseURL, teardown := getTestServer() + defer teardown() + + // host + serverURL, err := url.Parse(baseURL) + if err != nil { + t.Fatal(err) + } + serverHost := strings.Split(serverURL.Host, ":")[0] + + // expected result + expectedResult := make(map[string]any) + expectedResult["status"] = "200 OK" + expectedResult["status_code"] = http.StatusOK + expectedResult["body"] = nil + expectedResult["raw_body"] = "" + + resultObj := ast.MustInterfaceToValue(expectedResult) + + expectedError := &Error{Code: "eval_builtin_error", Message: "http.send: unallowed host: " + serverHost} + + rules := []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": "%s", "enable_redirect": true, "force_json_decode": true}, resp); x := remove_headers(resp) }`, baseURL)} + + // run the test + tests := []struct { + note string + rules []string + options func(*Query) *Query + expected any + }{ + { + "http.send allow_net nil", + rules, + setAllowNet(nil), + resultObj.String(), + }, + { + "http.send allow_net match", + rules, + setAllowNet([]string{serverHost}), + resultObj.String(), + }, + { + "http.send allow_net empty", + rules, + setAllowNet([]string{}), + expectedError, + }, + { + "http.send allow_net no match", + rules, + setAllowNet([]string{"example.com"}), + expectedError, + }, + } + + data := loadSmallTestData() + + for _, tc := range tests { + runTopDownTestCase(t, data, tc.note, append(tc.rules, httpSendHelperRules...), tc.expected, tc.options) + } +} + +func TestHTTPSendRaiseError(t *testing.T) { + t.Parallel() + + // test server + baseURL, teardown := getTestServer() + defer teardown() + + networkErrObj := make(map[string]any) + networkErrObj["code"] = HTTPSendNetworkErr + networkErrObj["message"] = "Get \"foo://foo.com\": unsupported protocol scheme \"foo\"" + + networkErr := ast.MustInterfaceToValue(networkErrObj) + + internalErrObj := make(map[string]any) + internalErrObj["code"] = HTTPSendInternalErr + internalErrObj["message"] = fmt.Sprintf(`http.send({"method": "get", "url": "%s", "force_json_decode": true, "raise_error": false, "force_cache": true}): eval_builtin_error: http.send: 'force_cache' set but 'force_cache_duration_seconds' parameter is missing`, baseURL) + + internalErr := ast.MustInterfaceToValue(internalErrObj) + + responseObj := make(map[string]any) + responseObj["status_code"] = 0 + responseObj["error"] = internalErrObj + + response := ast.MustInterfaceToValue(responseObj) + + inputValidationErrObj := make(map[string]any) + inputValidationErrObj["code"] = HTTPSendInternalErr + inputValidationErrObj["message"] = fmt.Sprintf(`http.send({"url": "%s", "raise_error": false}): eval_type_error: http.send: operand 1 missing required request parameters(s): {"method"}`, baseURL) + + responseObjInputValidationErr := make(map[string]any) + responseObjInputValidationErr["status_code"] = 0 + responseObjInputValidationErr["error"] = inputValidationErrObj + + responseObjInputValidation := ast.MustInterfaceToValue(responseObjInputValidationErr) + + tests := []struct { + note string + ruleTemplate string + body string + response any + }{ + { + note: "http.send invalid url (don't raise error, check response body)", + ruleTemplate: `p = x { + r = http.send({"method": "get", "url": "%URL%.com", "force_json_decode": true, "raise_error": false}) + x = r.body + }`, + response: ``, + }, + { + note: "http.send invalid url (don't raise error, check response status code)", + ruleTemplate: `p = x { + r = http.send({"method": "get", "url": "%URL%.com", "force_json_decode": true, "raise_error": false}) + x = r.status_code + }`, + response: `0`, + }, + { + note: "http.send invalid url (don't raise error, network error)", + ruleTemplate: `p = x { + r = http.send({"method": "get", "url": "foo://foo.com", "force_json_decode": true, "raise_error": false}) + x = r.error + }`, + response: networkErr.String(), + }, + { + note: "http.send missing param (don't raise error, internal error)", + ruleTemplate: `p = x { + r = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "raise_error": false, "force_cache": true}) + x = r.error + }`, + response: internalErr.String(), + }, + { + note: "http.send missing param (don't raise error, check response)", + ruleTemplate: `p = x { + r = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "raise_error": false, "force_cache": true}) + x = r + }`, + response: response.String(), + }, + { + note: "http.send missing required input param (don't raise error, check response)", + ruleTemplate: `p = x { + r = http.send({"url": "%URL%", "raise_error": false}) + x = r + }`, + response: responseObjInputValidation.String(), + }, + { + note: "http.send missing required input param (raise error, undefined response)", + ruleTemplate: `p = x { + r = http.send({"url": "%URL%", "raise_error": true}) + x = r + }`, + response: &Error{ + Code: TypeErr, + Message: "eval_type_error: http.send: operand 1 missing required request parameters(s): {\"method\"}", + }, + }, + } + + data := loadSmallTestData() + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + runTopDownTestCase(t, data, tc.note, []string{strings.ReplaceAll(tc.ruleTemplate, "%URL%", baseURL)}, tc.response) + }) + } +} + +func TestHTTPSendCaching(t *testing.T) { + t.Parallel() + + // run the test + tests := []struct { + note string + ruleTemplate string + body string + response string + expectedReqCount int + }{ + { + note: "http.send GET single", + ruleTemplate: `p = x { http.send({"method": "get", "url": "%URL%", "force_json_decode": true}, r); x = r.body }`, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET cache hit", + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true}) # cached + r3 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true}) # cached + r1 == r2 + r2 == r3 + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET cache miss different method", + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true}) + r2 = http.send({"method": "post", "url": "%URL%", "force_json_decode": true}) + r1_2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true}) # cached + r2_2 = http.send({"method": "post", "url": "%URL%", "force_json_decode": true}) # cached + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 2, + }, + { + note: "http.send GET cache miss different url", + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%/foo", "force_json_decode": true}) + r2 = http.send({"method": "get", "url": "%URL%/bar", "force_json_decode": true}) + r1_2 = http.send({"method": "get", "url": "%URL%/foo", "force_json_decode": true}) # cached + r2_2 = http.send({"method": "get", "url": "%URL%/bar", "force_json_decode": true}) # cached + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 2, + }, + { + note: "http.send GET cache miss different decode opt", + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": false}) + r1_2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true}) # cached + r2_2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": false}) # cached + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 2, + }, + { + note: "http.send GET cache miss different headers", + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v2"}}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h2": "v2"}}) + r3 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h2": "v3"}}) + r1_2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v2"}}) # cached + r2_2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h2": "v2"}}) # cached + r3_2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h2": "v3"}}) # cached + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 3, + }, + { + note: "http.send GET different headers but still cached because ignored", + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v2"}, "cache_ignored_headers": ["h2"]}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v3"}, "cache_ignored_headers": ["h2"]}) # cached + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET cache miss different headers (force_cache enabled)", + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v2"}, "force_cache": true, "force_cache_duration_seconds": 300}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v3"}, "force_cache": true, "force_cache_duration_seconds": 300}) + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 2, + }, + { + note: "http.send GET cache miss different headers in cache key", + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v2", "h3": "v3"}, "cache_ignored_headers": ["h2"]}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v21"}, "cache_ignored_headers": ["h2"]}) + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 2, + }, + { + note: "http.send GET different headers but still cached because ignored (force_cache enabled)", + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v2"}, "force_cache": true, "force_cache_duration_seconds": 300, "cache_ignored_headers": ["h2"]}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v3"}, "force_cache": true, "force_cache_duration_seconds": 300, "cache_ignored_headers": ["h2"]}) # cached + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET different cache_ignored_headers but still cached (force_cache enabled)", + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v2"}, "force_cache": true, "force_cache_duration_seconds": 300, "cache_ignored_headers": ["h2"]}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v2", "h3": "v3"}, "force_cache": true, "force_cache_duration_seconds": 300, "cache_ignored_headers": ["h2", "h3"]}) # cached + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET different cache_ignored_headers (one of them is nil) but still cached (force_cache enabled)", + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1"}, "force_cache": true, "force_cache_duration_seconds": 300}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v2"}, "force_cache": true, "force_cache_duration_seconds": 300, "cache_ignored_headers": ["h2"]}) # cached + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET different cache_ignored_headers (one of them is empty) but still cached (force_cache enabled)", + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1"}, "force_cache": true, "force_cache_duration_seconds": 300, "cache_ignored_headers": []}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "headers": {"h1": "v1", "h2": "v2"}, "force_cache": true, "force_cache_duration_seconds": 300, "cache_ignored_headers": ["h2"]}) # cached + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send POST cache miss different body", + ruleTemplate: `p = x { + r1 = http.send({"method": "post", "url": "%URL%", "force_json_decode": true, "headers": {"h2": "v2"}, "body": "{\"foo\": 42}"}) + r2 = http.send({"method": "post", "url": "%URL%", "force_json_decode": true, "headers": {"h2": "v3"}, "body": "{\"foo\": 23}"}) + r1_2 = http.send({"method": "post", "url": "%URL%", "force_json_decode": true, "headers": {"h2": "v2"}, "body": "{\"foo\": 42}"}) # cached + r2_2 = http.send({"method": "post", "url": "%URL%", "force_json_decode": true, "headers": {"h2": "v3"}, "body": "{\"foo\": 23}"}) # cached + x = r1.body + }`, + response: `{"x": 1}`, + expectedReqCount: 2, + }, + } + + data := loadSmallTestData() + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(tc.response)) + if err != nil { + t.Fatal(err) + } + })) + defer ts.Close() + + runTopDownTestCase(t, data, tc.note, []string{strings.ReplaceAll(tc.ruleTemplate, "%URL%", ts.URL)}, tc.response) + + // Note: The runTopDownTestCase ends up evaluating twice (once with and once without partial + // eval first), so expect 2x the total request count the test case specified. + actualCount := len(requests) / 2 + if actualCount != tc.expectedReqCount { + t.Fatalf("Expected to get %d requests, got %d", tc.expectedReqCount, actualCount) + } + }) + } +} + +func TestHTTPSendIntraQueryCaching(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + request string + ruleTemplate string + headers map[string][]string + body string + response string + expectedReqCount int + expectedInterQueryCacheHit bool + }{ + { + note: "http.send GET single", + request: `{"method": "get", "url": "%URL%", "force_json_decode": true}`, + ruleTemplate: `p = x { http.send(%REQ%, r); x = r.body }`, + headers: map[string][]string{"Cache-Control": {"max-age=290304000, public"}}, + response: `{"x": 1}`, + expectedReqCount: 2, // Partial evaluation generates a second query, so expect 2 requests + expectedInterQueryCacheHit: false, + }, + { + note: "http.send GET multiple", + request: `{"method": "get", "url": "%URL%", "force_json_decode": true}`, + ruleTemplate: `p = x { + r1 = http.send(%REQ%) + r2 = http.send(%REQ%) # cached + r3 = http.send(%REQ%) # cached + r1 == r2 + r2 == r3 + x = r1.body + }`, + headers: map[string][]string{"Cache-Control": {"max-age=290304000, public"}}, + response: `{"x": 1}`, + expectedReqCount: 2, // Partial evaluation generates a second query, so expect 2 requests + expectedInterQueryCacheHit: false, + }, + { + note: "http.send GET multiple (inter-query cache enabled)", + request: `{"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}`, + ruleTemplate: `p = x { + r1 = http.send(%REQ%) + r2 = http.send(%REQ%) # cached; intra-query populated but ignored + r3 = http.send(%REQ%) # cached; intra-query populated but ignored + r1 == r2 + r2 == r3 + x = r1.body + }`, + headers: map[string][]string{"Cache-Control": {"max-age=290304000, public"}}, + response: `{"x": 1}`, + expectedReqCount: 1, // Inter-query cache applies across full and partial eval + expectedInterQueryCacheHit: true, + }, + { + note: "http.send GET multiple (inter-query cache enabled, server no-store)", + request: `{"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}`, + ruleTemplate: `p = x { + r1 = http.send(%REQ%) + r2 = http.send(%REQ%) # cached; intra-query not populated + r3 = http.send(%REQ%) # cached; intra-query not populated + r1 == r2 + r2 == r3 + x = r1.body + }`, + headers: map[string][]string{"Cache-Control": {"no-store"}}, + response: `{"x": 1}`, + expectedReqCount: 2, // no-store means the Partial evaluation generates a second query + expectedInterQueryCacheHit: false, + }, + } + + data := loadSmallTestData() + + t0 := time.Now() + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + headers := w.Header() + + maps.Copy(headers, tc.headers) + + headers.Set("Date", t0.Format(time.RFC850)) + + etag := w.Header().Get("etag") + lm := w.Header().Get("last-modified") + + if etag != "" { + if r.Header.Get("if-none-match") == etag { + w.WriteHeader(http.StatusNotModified) + } + } else if lm != "" { + if r.Header.Get("if-modified-since") == lm { + w.WriteHeader(http.StatusNotModified) + } + } else { + w.WriteHeader(http.StatusOK) + } + _, _ = w.Write([]byte(tc.response)) // ignore error + })) + defer ts.Close() + + config, _ := iCache.ParseCachingConfig([]byte(`{"inter_query_builtin_cache": {"max_size_bytes": 500, "stale_entry_eviction_period_seconds": 1, "forced_eviction_threshold_percentage": 80},}`)) + interQueryCache := iCache.NewInterQueryCacheWithContext(context.Background(), config) + + opts := []func(*Query) *Query{ + setTime(t0), + setInterQueryCache(interQueryCache), + } + + request := strings.ReplaceAll(tc.request, "%URL%", ts.URL) + rule := strings.ReplaceAll(tc.ruleTemplate, "%REQ%", request) + runTopDownTestCase(t, data, tc.note, []string{rule}, tc.response, opts...) + + // Note: The runTopDownTestCase ends up evaluating twice (once with and once without partial + // eval first); this affects inter-query caching enabled vs disabled. + actualCount := len(requests) + if actualCount != tc.expectedReqCount { + t.Fatalf("Expected to get %d requests, got %d", tc.expectedReqCount, actualCount) + } + + var x any + if err := util.UnmarshalJSON([]byte(request), &x); err != nil { + t.Fatalf("failed to unmarshal request: %v", err) + } + cacheKey, err := ast.InterfaceToValue(x) + if err != nil { + t.Fatalf("failed create request object: %v", err) + } + cacheKeyObj, _ := cacheKey.(ast.Object) + cacheKeyObj.Insert(ast.StringTerm("cache_ignored_headers"), ast.NullTerm()) + cacheKey, _ = cacheKeyObj.(ast.Value) + + if _, found := interQueryCache.Get(cacheKey); found != tc.expectedInterQueryCacheHit { + t.Fatalf("Expected inter-query cache hit: %v, got: %v", tc.expectedInterQueryCacheHit, found) + } + }) + } +} + +func TestHTTPSendInterQueryCaching(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + query string // each query is run three times + headers map[string][]string + body string + response string + expectedReqCount int + }{ + { + note: "http.send GET cache hit (max_age_response_fresh)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}, x)`, + headers: map[string][]string{"Cache-Control": {"max-age=290304000, public"}}, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET cache hit (expires_header_response_fresh)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}, x)`, + headers: map[string][]string{"Expires": {"Wed, 31 Dec 2115 07:28:00 GMT"}}, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET (expires_header_invalid_value)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}, x)`, + headers: map[string][]string{"Expires": {"0"}}, + response: `{"x": 1}`, + expectedReqCount: 3, + }, + { + note: "http.send GET no-store cache", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}, x)`, + headers: map[string][]string{"Cache-Control": {"no-store"}}, + response: `{"x": 1}`, + expectedReqCount: 3, + }, + { + note: "http.send GET (response_stale_revalidate_with_etag)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}, x)`, + headers: map[string][]string{"Cache-Control": {"max-age=0, public"}, "Etag": {"1234"}}, + response: `{"x": 1}`, + expectedReqCount: 3, + }, + { + note: "http.send GET (response_stale_revalidate_with_last_modified)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}, x)`, + headers: map[string][]string{"Cache-Control": {"max-age=0, public"}, "Last-Modified": {"Wed, 31 Dec 2115 07:28:00 GMT"}}, + response: `{"x": 1}`, + expectedReqCount: 3, + }, + { + note: "http.send GET (response_age_negative_duration)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}, x)`, + headers: map[string][]string{"Cache-Control": {"max-age=0, public"}, "Last-Modified": {"Wed, 31 Dec 2115 07:28:00 GMT"}, "Date": {"Wed, 31 Dec 2115 07:28:00 GMT"}}, + response: `{"x": 1}`, + expectedReqCount: 3, + }, + { + note: "http.send GET cache hit deserialized mode (max_age_response_fresh)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "caching_mode": "deserialized"}, x)`, + headers: map[string][]string{"Cache-Control": {"max-age=290304000, public"}}, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET cache hit serialized mode explicit (max_age_response_fresh)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "caching_mode": "serialized"}, x)`, + headers: map[string][]string{"Cache-Control": {"max-age=290304000, public"}}, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET cache hit serialized mode explicit (max_age_response_fresh), when parsing a yaml response", + query: `http.send({"method": "get", "url": "%URL%", "cache": true, "caching_mode": "serialized"}, x)`, + headers: map[string][]string{ + "Cache-Control": {"max-age=290304000, public"}, + "Content-Type": {"application/yaml"}, + }, + // NOTE: fed into runTopDownTestCase, so it has to be JSON; but we're making use of YAML being a superset of JSON + response: `{"x": 1}`, + expectedReqCount: 1, + }, + } + + t0 := time.Now() + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + headers := w.Header() + + maps.Copy(headers, tc.headers) + + headers.Set("Date", t0.Format(time.RFC850)) + + etag := w.Header().Get("etag") + lm := w.Header().Get("last-modified") + + if etag != "" { + if r.Header.Get("if-none-match") == etag { + w.WriteHeader(http.StatusNotModified) + } + } else if lm != "" { + if r.Header.Get("if-modified-since") == lm { + w.WriteHeader(http.StatusNotModified) + } + } else { + w.WriteHeader(http.StatusOK) + } + _, _ = w.Write([]byte(tc.response)) // ignore error + })) + defer ts.Close() + + qStr := strings.ReplaceAll(tc.query, "%URL%", ts.URL) + q := newQuery(qStr, t0) + + for i := range 3 { + res, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + resResponse := res[0]["x"].Value.(ast.Object).Get(ast.StringTerm("raw_body")) + if ast.String(tc.response).Compare(resResponse.Value) != 0 { + t.Fatalf("Expected response on query %d to be %v, got %v", i, tc.response, resResponse.String()) + } + } + + actualCount := len(requests) + if actualCount != tc.expectedReqCount { + t.Fatalf("Expected to get %d requests, got %d", tc.expectedReqCount, actualCount) + } + }) + } +} + +func TestHTTPSendInterQueryForceCaching(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + query string + ruleTemplate string + headers map[string][]string + body string + response string + expectedReqCount int + }{ + { + note: "http.send GET cache hit (force_cache_only)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": 300}, x)`, + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": 300}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": 300}) # cached and fresh + r3 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": 300}) # cached and fresh + r1 == r2 + r2 == r3 + x = r1.body + }`, + headers: map[string][]string{"Expires": {"Wed, 31 Dec 2005 07:28:00 GMT"}}, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET cache hit, empty headers (force_cache_only)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": 300}, x)`, + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": 300}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": 300}) # cached and fresh + r3 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": 300}) # cached and fresh + r1 == r2 + r2 == r3 + x = r1.body + }`, + headers: map[string][]string{}, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET cache hit (cache_param_override)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "force_cache": true, "force_cache_duration_seconds": 300}, x)`, + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "force_cache": true, "force_cache_duration_seconds": 300}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "force_cache": true, "force_cache_duration_seconds": 300}) # cached and fresh + r3 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "force_cache": true, "force_cache_duration_seconds": 300}) # cached and fresh + r1 == r2 + r2 == r3 + x = r1.body + }`, + headers: map[string][]string{"Expires": {"Wed, 31 Dec 2005 07:28:00 GMT"}}, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET cache hit (force_cache_only_no_store_override)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": 300}, x)`, + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": 300}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": 300}) # cached and fresh + r3 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": 300}) # cached and fresh + r1 == r2 + r2 == r3 + x = r1.body + }`, + headers: map[string][]string{ + "Expires": {"Wed, 31 Dec 2005 07:28:00 GMT"}, + "Cache-Control": {"no-store"}, + }, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET cache hit (cache_param_override_no_store_override)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "force_cache": true, "force_cache_duration_seconds": 300}, x)`, + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "force_cache": true, "force_cache_duration_seconds": 300}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "force_cache": true, "force_cache_duration_seconds": 300}) # cached and fresh + r3 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "force_cache": true, "force_cache_duration_seconds": 300}) # cached and fresh + r1 == r2 + r2 == r3 + x = r1.body + }`, + headers: map[string][]string{ + "Expires": {"Wed, 31 Dec 2005 07:28:00 GMT"}, + "Cache-Control": {"no-store", "no-cache", "max-age=0"}, + }, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + { + note: "http.send GET cache hit (cache_param_override_no_store_override_invalid_expires_header_value)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "force_cache": true, "force_cache_duration_seconds": 300}, x)`, + ruleTemplate: `p = x { + r1 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "force_cache": true, "force_cache_duration_seconds": 300}) + r2 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "force_cache": true, "force_cache_duration_seconds": 300}) # cached and fresh + r3 = http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "force_cache": true, "force_cache_duration_seconds": 300}) # cached and fresh + r1 == r2 + r2 == r3 + x = r1.body + }`, + headers: map[string][]string{ + "Expires": {"0"}, + "Cache-Control": {"no-store", "no-cache", "max-age=0"}, + }, + response: `{"x": 1}`, + expectedReqCount: 1, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + t0 := time.Now().UTC() + + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + headers := w.Header() + + maps.Copy(headers, tc.headers) + + headers.Set("Date", t0.Format(http.TimeFormat)) + + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(tc.response)) + if err != nil { + t.Fatal(err) + } + })) + defer ts.Close() + + qStr := strings.ReplaceAll(tc.query, "%URL%", ts.URL) + q := newQuery(qStr, t0) + + for i := range 3 { + res, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + resResponse := res[0]["x"].Value.(ast.Object).Get(ast.StringTerm("raw_body")) + if ast.String(tc.response).Compare(resResponse.Value) != 0 { + t.Fatalf("Expected response on query %d to be %v, got %v", i, tc.response, resResponse.String()) + } + } + + actualCount := len(requests) + if actualCount != tc.expectedReqCount { + t.Errorf("Expected to get %d requests, got %d", tc.expectedReqCount, actualCount) + } + }) + } +} + +func TestHTTPSendInterQueryForceCachingRefresh(t *testing.T) { + t.Parallel() + + cacheTime := 300 + tests := []struct { + note string + request string + headers map[string][]string + skipDate bool + response string + expectedReqCount int + }{ + { + note: "http.send GET cache expired, reloads normally", + request: `{"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": %CACHE%}`, + headers: map[string][]string{}, + expectedReqCount: 2, + response: `{"x": 1}`, + }, + { + note: "http.send GET cache expired, no date, reloads normally", + request: `{"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": %CACHE%}`, + headers: map[string][]string{}, + expectedReqCount: 2, + skipDate: true, + response: `{"x": 1}`, + }, + { + note: "http.send GET cache expired, returns not modified", + request: `{"method": "get", "url": "%URL%", "force_json_decode": true, "force_cache": true, "force_cache_duration_seconds": %CACHE%}`, + headers: map[string][]string{"Etag": {"1234"}}, + expectedReqCount: 2, + response: `{"x": 1}`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + t0 := time.Now().UTC() + + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + headers := w.Header() + + maps.Copy(headers, tc.headers) + + if tc.skipDate { + headers["Date"] = nil + } else { + headers.Set("Date", t0.Format(http.TimeFormat)) + } + + etag := w.Header().Get("etag") + + if r.Header.Get("if-none-match") != "" { + if r.Header.Get("if-none-match") == etag { + // add new headers and update existing header value + headers["Cache-Control"] = []string{"max-age=200, public"} + w.WriteHeader(http.StatusNotModified) + } + } else { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(tc.response)) + if err != nil { + t.Fatal(err) + } + } + })) + defer ts.Close() + + request := strings.ReplaceAll(tc.request, "%URL%", ts.URL) + request = strings.ReplaceAll(request, "%CACHE%", strconv.Itoa(cacheTime)) + full := fmt.Sprintf("http.send(%s, x)", request) + config, _ := iCache.ParseCachingConfig([]byte(`{"inter_query_builtin_cache": {"max_size_bytes": 500, "stale_entry_eviction_period_seconds": 1, "forced_eviction_threshold_percentage": 80},}`)) + interQueryCache := iCache.NewInterQueryCacheWithContext(context.Background(), config) + q := NewQuery(ast.MustParseBody(full)). + WithInterQueryBuiltinCache(interQueryCache). + WithTime(t0) + + /* Run tests twice once to populate the cache + then expire it out and run again to simulate an + expired cache + */ + for i := range 2 { + resp, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + // make sure we have a valid response + if len(resp) < 1 { + t.Fatalf("missing response on query %d: %v", i, resp) + } + + // check the body is what we expect + resResponse := resp[0]["x"].Value.(ast.Object).Get(ast.StringTerm("raw_body")) + if ast.String(tc.response).Compare(resResponse.Value) != 0 { + t.Fatalf("Expected response on query %d to be %v, got %v", i, tc.response, resResponse.String()) + } + + // pull the result out of the cache + var x any + if err := util.UnmarshalJSON([]byte(request), &x); err != nil { + t.Fatalf("failed to unmarshal request on query %d: %v", i, err) + } + cacheKey, err := ast.InterfaceToValue(x) + if err != nil { + t.Fatalf("failed create request object on query %d: %v", i, err) + } + cacheKeyObj, _ := cacheKey.(ast.Object) + cacheKeyObj.Insert(ast.StringTerm("cache_ignored_headers"), ast.NullTerm()) + cacheKey, _ = cacheKeyObj.(ast.Value) + + val, found := interQueryCache.Get(cacheKey) + if !found { + t.Fatalf("Expected inter-query cache hit on query %d", i) + } + + m, err := val.(*interQueryCacheValue).copyCacheData() + if err != nil { + t.Fatal(err) + } + + // Make sure the cache expires based on the force cache time setting + expectedExpiry := t0.Add(time.Second * time.Duration(cacheTime)) + if expectedExpiry.Sub(m.ExpiresAt).Abs() > time.Second*1 { + t.Fatalf("Expected cache to expire on query %d in %v secs got %s", i, cacheTime, t0.Sub(m.ExpiresAt).Abs()) + } + + // Push an expired entry back into the cache for the next run + m.ExpiresAt = t0.Add(-time.Hour * 1) + v, err := m.toCacheValue() + if err != nil { + t.Fatal(err) + } + + interQueryCache.InsertWithExpiry(cacheKey, v, m.ExpiresAt) + } + + actualCount := len(requests) + if actualCount != tc.expectedReqCount { + t.Errorf("Expected to get %d requests, got %d", tc.expectedReqCount, actualCount) + } + }) + } +} + +func TestHTTPSendInterQueryCachingModifiedResp(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + query string + headers map[string][]string + body string + response string + expectedReqCount int + }{ + { + note: "http.send GET (response_stale_revalidate_with_etag)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}, x)`, + headers: map[string][]string{"Cache-Control": {"max-age=0, public"}, "Etag": {"1234"}, "location": {"/test"}}, + response: `{"x": 1}`, + expectedReqCount: 2, + }, + { + note: "http.send GET cache deserialized mode (response_stale_revalidate_with_etag)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true, "caching_mode": "deserialized"}, x)`, + headers: map[string][]string{"Cache-Control": {"max-age=0, public"}, "Etag": {"1234"}, "location": {"/test"}}, + response: `{"x": 1}`, + expectedReqCount: 2, + }, + { + note: "http.send GET (response_stale_revalidate_with_no_etag)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}, x)`, + headers: map[string][]string{"Cache-Control": {"max-age=0, public"}}, + response: `{"x": 1}`, + expectedReqCount: 3, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + t0 := time.Now().UTC() + + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + headers := w.Header() + + maps.Copy(headers, tc.headers) + + headers.Set("Date", t0.Format(http.TimeFormat)) + + etag := w.Header().Get("etag") + + if r.Header.Get("if-none-match") != "" { + if r.Header.Get("if-none-match") == etag { + // add new headers and update existing header value + headers["Cache-Control"] = []string{"max-age=290304000, public"} + headers["foo"] = []string{"bar"} + w.WriteHeader(http.StatusNotModified) + } + } else { + w.WriteHeader(http.StatusOK) + } + _, _ = w.Write([]byte(tc.response)) // ignore error + })) + defer ts.Close() + + qStr := strings.ReplaceAll(tc.query, "%URL%", ts.URL) + q := newQuery(qStr, t0) + + for i := range 3 { + res, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + resResponse := res[0]["x"].Value.(ast.Object).Get(ast.StringTerm("raw_body")) + if ast.String(tc.response).Compare(resResponse.Value) != 0 { + t.Fatalf("Expected response on query %d to be %v, got %v", i, tc.response, resResponse.String()) + } + } + + // Note: The runTopDownTestCase ends up evaluating twice (once with and once without partial + // eval first), so expect 2x the total request count the test case specified. + actualCount := len(requests) + if actualCount != tc.expectedReqCount { + t.Fatalf("Expected to get %d requests, got %d", tc.expectedReqCount, actualCount) + } + }) + } +} + +func TestHTTPSendInterQueryCachingNewResp(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + query string // each query will be run three times + headers map[string][]string + body string + response string + expectedReqCount int + }{ + { + note: "http.send GET (response_stale_revalidate_with_etag)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}, x)`, + headers: map[string][]string{"Cache-Control": {"max-age=0, public"}, "Etag": {"1234"}, "location": {"/test"}}, + response: `{"x": 1}`, + expectedReqCount: 2, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + t0 := time.Now().UTC() + + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + headers := w.Header() + + maps.Copy(headers, tc.headers) + + headers.Set("Date", t0.Format(http.TimeFormat)) + + etag := w.Header().Get("etag") + + if r.Header.Get("if-none-match") != "" { + if r.Header.Get("if-none-match") == etag { + headers["Cache-Control"] = []string{"max-age=290304000, public"} + } + } + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(tc.response)) + if err != nil { + t.Fatal(err) + } + })) + defer ts.Close() + + qStr := strings.ReplaceAll(tc.query, "%URL%", ts.URL) + q := newQuery(qStr, t0) + + for i := range 3 { + res, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + resResponse := res[0]["x"].Value.(ast.Object).Get(ast.StringTerm("raw_body")) + if ast.String(tc.response).Compare(resResponse.Value) != 0 { + t.Fatalf("Expected response on query %d to be %v, got %v", i, tc.response, resResponse.String()) + } + } + + actualCount := len(requests) + if actualCount != tc.expectedReqCount { + t.Fatalf("Expected to get %d requests, got %d", tc.expectedReqCount, actualCount) + } + }) + } +} + +func newQuery(qStr string, t0 time.Time) *Query { + config, _ := iCache.ParseCachingConfig([]byte(`{"inter_query_builtin_cache": {"max_size_bytes": 500, "stale_entry_eviction_period_seconds": 1, "forced_eviction_threshold_percentage": 80},}`)) + interQueryCache := iCache.NewInterQueryCacheWithContext(context.Background(), config) + ctx := context.Background() + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store) + q := NewQuery(ast.MustParseBody(qStr)). + WithCompiler(ast.NewCompiler()). + WithInterQueryBuiltinCache(interQueryCache). + WithStore(store). + WithTransaction(txn). + WithTime(t0) + return q +} + +func TestInsertIntoHTTPSendInterQueryCacheError(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + query string + headers map[string][]string + body string + response string + expectedReqCount int + }{ + { + note: "http.send GET (bad_date_header_value)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}, x)`, + headers: map[string][]string{"Cache-Control": {"max-age=0, public"}, "Date": {"Wed, 32 Dec 2115 07:28:00 GMT"}}, + response: `{"x": 1}`, + expectedReqCount: 3, + }, + { + note: "http.send GET (bad_cache_control_header_value)", + query: `http.send({"method": "get", "url": "%URL%", "force_json_decode": true, "cache": true}, x)`, + headers: map[string][]string{"Cache-Control": {"max-age=\"foo\", public"}}, + response: `{"x": 1}`, + expectedReqCount: 3, + }, + } + + for _, tc := range tests { + t0 := time.Now().UTC() + + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + headers := w.Header() + + maps.Copy(headers, tc.headers) + + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(tc.response)) + if err != nil { + t.Fatal(err) + } + })) + defer ts.Close() + + qStr := strings.ReplaceAll(tc.query, "%URL%", ts.URL) + q := newQuery(qStr, t0) + + for i := range 3 { + res, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + resResponse := res[0]["x"].Value.(ast.Object).Get(ast.StringTerm("raw_body")) + if ast.String(tc.response).Compare(resResponse.Value) != 0 { + t.Fatalf("Expected response on query %d to be %v, got %v", i, tc.response, resResponse.String()) + } + } + + actualCount := len(requests) + if actualCount != tc.expectedReqCount { + t.Fatalf("Expected to get %d requests, got %d", tc.expectedReqCount, actualCount) + } + }) + } +} + +func TestGetCachingMode(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + input ast.Object + expected cachingMode + wantError bool + err error + }{ + { + note: "default caching mode", + input: ast.MustParseTerm(`{}`).Value.(ast.Object), + expected: defaultCachingMode, + wantError: false, + }, + { + note: "serialized caching mode", + input: ast.MustParseTerm(`{"caching_mode": "serialized"}`).Value.(ast.Object), + expected: defaultCachingMode, + wantError: false, + }, + { + note: "deserialized caching mode", + input: ast.MustParseTerm(`{"caching_mode": "deserialized"}`).Value.(ast.Object), + expected: cachingModeDeserialized, + wantError: false, + }, + { + note: "invalid caching mode type", + input: ast.MustParseTerm(`{"caching_mode": 1}`).Value.(ast.Object), + wantError: true, + err: errors.New("invalid value for \"caching_mode\" field"), + }, + { + note: "invalid caching mode value", + input: ast.MustParseTerm(`{"caching_mode": "foo"}`).Value.(ast.Object), + wantError: true, + err: errors.New("invalid value specified for \"caching_mode\" field: foo"), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + actual, err := getCachingMode(tc.input) + if tc.wantError { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else { + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if actual != tc.expected { + t.Fatalf("Expected caching mode %v but got %v", tc.expected, actual) + } + } + }) + } +} + +func TestGetResponseHeaderDateEmpty(t *testing.T) { + t.Parallel() + + _, err := getResponseHeaderDate(http.Header{"Date": {""}}) + if err == nil { + t.Fatal("Expected error but got nil") + } + + expected := "no date header" + if err.Error() != expected { + t.Fatalf("Expected error message %v but got %v", expected, err.Error()) + } +} + +func TestParseMaxAgeCacheDirective(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + input map[string]string + expected deltaSeconds + wantError bool + err error + }{ + { + note: "max age not set", + input: nil, + expected: deltaSeconds(-1), + wantError: false, + err: nil, + }, + { + note: "max age out of range", + input: map[string]string{"max-age": "214748364888"}, + expected: deltaSeconds(math.MaxInt32), + wantError: false, + err: nil, + }, + { + note: "max age greater than MaxInt32", + input: map[string]string{"max-age": "2147483648"}, + expected: deltaSeconds(math.MaxInt32), + wantError: false, + err: nil, + }, + { + note: "max age less than MaxInt32", + input: map[string]string{"max-age": "21"}, + expected: deltaSeconds(21), + wantError: false, + err: nil, + }, + { + note: "max age bad format", + input: map[string]string{"max-age": "21,21"}, + expected: deltaSeconds(-1), + wantError: true, + err: errors.New("strconv.ParseUint: parsing \"21,21\": invalid syntax"), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + actual, err := parseMaxAgeCacheDirective(tc.input) + if tc.wantError { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if actual != tc.expected { + t.Fatalf("Expected value for max-age %v but got %v", tc.expected, actual) + } + }) + } +} + +func TestNewForceCacheParams(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + input ast.Object + expected *forceCacheParams + wantError bool + err error + }{ + { + note: "non existent key", + input: ast.MustParseTerm(`{}`).Value.(ast.Object), + expected: nil, + wantError: true, + err: errors.New("'force_cache' set but 'force_cache_duration_seconds' parameter is missing"), + }, + { + note: "empty input", + input: ast.MustParseTerm(`{"force_cache_duration_seconds": ""}`).Value.(ast.Object), + expected: nil, + wantError: true, + err: errors.New("strconv.ParseInt: parsing \"\\\"\\\"\": invalid syntax"), + }, + { + note: "invalid input", + input: ast.MustParseTerm(`{"force_cache_duration_seconds": "foo"}`).Value.(ast.Object), + expected: nil, + wantError: true, + err: errors.New("strconv.ParseInt: parsing \"\\\"foo\\\"\": invalid syntax"), + }, + { + note: "valid input", + input: ast.MustParseTerm(`{"force_cache_duration_seconds": 300}`).Value.(ast.Object), + expected: &forceCacheParams{forceCacheDurationSeconds: int32(300)}, + wantError: false, + err: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + actual, err := newForceCacheParams(tc.input) + if tc.wantError { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else { + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if actual.forceCacheDurationSeconds != tc.expected.forceCacheDurationSeconds { + t.Fatalf("Expected force cache duration %v but got %v", tc.expected.forceCacheDurationSeconds, actual.forceCacheDurationSeconds) + } + } + }) + } +} + +func TestGetBoolValFromReqObj(t *testing.T) { + t.Parallel() + + validInput := ast.MustParseTerm(`{"cache": true}`) + validInputObj := validInput.Value.(ast.Object) + + invalidInput := ast.MustParseTerm(`{"cache": "true"}`) + invalidInputObj := invalidInput.Value.(ast.Object) + + tests := []struct { + note string + input ast.Object + key *ast.Term + expected bool + wantError bool + err error + }{ + { + note: "valid input", + input: validInputObj, + key: ast.StringTerm("cache"), + expected: true, + wantError: false, + err: nil, + }, + { + note: "invalid input", + input: invalidInputObj, + key: ast.StringTerm("cache"), + expected: false, + wantError: true, + err: errors.New("invalid value for \"cache\" field"), + }, + { + note: "non existent key", + input: validInputObj, + key: ast.StringTerm("foo"), + expected: false, + wantError: false, + err: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + actual, err := getBoolValFromReqObj(tc.input, tc.key) + if tc.wantError { + if err == nil { + t.Fatal("Expected error but got nil") + } + + if tc.err != nil && tc.err.Error() != err.Error() { + t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error()) + } + } else if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if actual != tc.expected { + t.Fatalf("Expected value for key %v is %v but got %v", tc.key, tc.expected, actual) + } + }) + } +} + +func TestInterQueryCheckCacheError(t *testing.T) { + t.Parallel() + + input := ast.MustParseTerm(`{"force_cache": true}`) + inputObj := input.Value.(ast.Object) + + _, err := newHTTPRequestExecutor(BuiltinContext{Context: context.Background()}, inputObj, inputObj) + if err == nil { + t.Fatal("expected error but got nil") + } + + errMsg := "eval_builtin_error: http.send: 'force_cache' set but 'force_cache_duration_seconds' parameter is missing" + if err.Error() != errMsg { + t.Fatalf("Expected error message %v but got %v", errMsg, err.Error()) + } +} + +func TestNewInterQueryCacheValue(t *testing.T) { + t.Parallel() + + date := "Wed, 31 Dec 2115 07:28:00 GMT" + maxAge := 290304000 + + headers := make(http.Header) + headers.Set("test-header", "test-value") + headers.Set("Cache-Control", fmt.Sprintf("max-age=%d, public", maxAge)) + headers.Set("Date", date) + + // test data + b := []byte(`[{"ID": "1", "Firstname": "John"}]`) + + response := &http.Response{ + Status: "200 OK", + StatusCode: http.StatusOK, + Header: headers, + Request: &http.Request{Method: "Get"}, + Body: io.NopCloser(bytes.NewBuffer(b)), + } + + result, _, err := newInterQueryCacheValue(BuiltinContext{}, response, b, &forceCacheParams{}) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + dateTime, _ := http.ParseTime(date) + + cvd := interQueryCacheData{ + RespBody: b, + Status: "200 OK", + StatusCode: http.StatusOK, + Headers: headers, + ExpiresAt: dateTime.Add(time.Duration(maxAge) * time.Second), + } + + cvdBytes, err := json.Marshal(cvd) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + expectedResult := &interQueryCacheValue{Data: cvdBytes} + + if !reflect.DeepEqual(result, expectedResult) { + t.Fatalf("Expected result %v but got %v", expectedResult, result) + } + + if int64(len(cvdBytes)) != result.SizeInBytes() { + t.Fatalf("Expected cache item size %v but got %v", len(cvdBytes), result.SizeInBytes()) + } +} + +func getTestServer() (baseURL string, teardownFn func()) { + mux := http.NewServeMux() + ts := httptest.NewServer(mux) + + mux.HandleFunc("/test", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { + http.Redirect(w, req, "/test", http.StatusMovedPermanently) + }) + + return ts.URL, ts.Close +} + +func getTLSTestServer() (ts *httptest.Server) { + mux := http.NewServeMux() + ts = httptest.NewUnstartedServer(mux) + + mux.HandleFunc("/test", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + mux.HandleFunc("/cert", func(w http.ResponseWriter, req *http.Request) { + clientCert := req.TLS.PeerCertificates[0] + commonName := clientCert.Issuer.CommonName + certificate := struct{ CommonName string }{commonName} + js, err := json.Marshal(certificate) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(js) + }) + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + return +} + +// Warning(philipc): This test cannot be run in parallel with other tests, due +// to the t.Setenv calls used to set up the server environment. +func TestHTTPSClient(t *testing.T) { + const ( + localClientCertFile = "testdata/client-cert.pem" + localClientCert2File = "testdata/client-cert-2.pem" + localClientKeyFile = "testdata/client-key.pem" + localCaFile = "testdata/ca.pem" + localServerCertFile = "testdata/server-cert.pem" + localServerKeyFile = "testdata/server-key.pem" + ) + + caCertPEM, err := os.ReadFile(localCaFile) + if err != nil { + t.Fatal(err) + } + caPool := x509.NewCertPool() + if ok := caPool.AppendCertsFromPEM(caCertPEM); !ok { + t.Fatal("failed to parse CA cert") + } + + cert, err := tls.LoadX509KeyPair(localServerCertFile, localServerKeyFile) + if err != nil { + t.Fatal(err) + } + + // Set up Environment + clientCert, err := readCertFromFile(localClientCertFile) + if err != nil { + t.Fatal(err) + } + t.Setenv("CLIENT_CERT_ENV", string(clientCert)) + + clientKey, err := readKeyFromFile(localClientKeyFile) + if err != nil { + t.Fatal(err) + } + t.Setenv("CLIENT_KEY_ENV", string(clientKey)) + t.Setenv("CLIENT_CA_ENV", string(caCertPEM)) + + // Replicating some of what happens in the server's HTTPS listener + s := getTLSTestServer() + s.TLS = &tls.Config{ + Certificates: []tls.Certificate{cert}, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: caPool, + } + s.StartTLS() + defer s.Close() + + t.Run("Server reflects Certificate CommonName", func(t *testing.T) { + // expected result + bodyMap := map[string]string{"CommonName": "my-ca"} + expectedResult := map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + "raw_body": "{\"CommonName\":\"my-ca\"}", + } + expectedResult["body"] = bodyMap + expectedResult["headers"] = map[string]any{ + "content-length": []any{"22"}, + "content-type": []any{"application/json"}, + } + + resultObj, err := ast.InterfaceToValue(expectedResult) + if err != nil { + t.Fatal(err) + } + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s", "tls_ca_cert_file": "%s", "tls_client_cert_file": "%s", "tls_client_key_file": "%s"}, resp); x := clean_headers(resp) }`, s.URL+"/cert", localCaFile, localClientCertFile, localClientKeyFile), + ) + // run the test + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) + }) + + t.Run("HTTPS Get with Inline Cert", func(t *testing.T) { + // expected result + expectedResult := map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + "body": nil, + "raw_body": "", + "headers": map[string]any{ + "content-length": []any{"0"}, + }, + } + + resultObj, err := ast.InterfaceToValue(expectedResult) + if err != nil { + t.Fatal(err) + } + + ca, err := os.ReadFile(localCaFile) + if err != nil { + t.Fatal(err) + } + + cert, err := os.ReadFile(localClientCertFile) + if err != nil { + t.Fatal(err) + } + + key, err := os.ReadFile(localClientKeyFile) + if err != nil { + t.Fatal(err) + } + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf( + "p = x { http.send({`method`: `get`, `url`: `%s`, `tls_ca_cert`: `%s`, `tls_client_cert`: `%s`, `tls_client_key`: `%s`}, resp); x := clean_headers(resp) }", + s.URL, ca, cert, key), + ) + + // run the test + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) + }) + + t.Run("HTTPS Get with File Cert", func(t *testing.T) { + // expected result + expectedResult := map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + "body": nil, + "raw_body": "", + "headers": map[string]any{ + "content-length": []any{"0"}, + }, + } + + resultObj, err := ast.InterfaceToValue(expectedResult) + if err != nil { + t.Fatal(err) + } + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s", "tls_ca_cert_file": "%s", "tls_client_cert_file": "%s", "tls_client_key_file": "%s"}, resp); x := clean_headers(resp) }`, s.URL, localCaFile, localClientCertFile, localClientKeyFile), + ) + + // run the test + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) + }) + + t.Run("HTTPS Get with Env Cert", func(t *testing.T) { + // expected result + expectedResult := map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + "body": nil, + "raw_body": "", + "headers": map[string]any{ + "content-length": []any{"0"}, + }, + } + + resultObj, err := ast.InterfaceToValue(expectedResult) + if err != nil { + t.Fatal(err) + } + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s", "tls_ca_cert_env_variable": "CLIENT_CA_ENV", "tls_client_cert_env_variable": "CLIENT_CERT_ENV", "tls_client_key_env_variable": "CLIENT_KEY_ENV"}, resp); x := clean_headers(resp) }`, s.URL), + ) + + // run the test + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) + }) + + t.Run("HTTPS Get with Env and File Cert", func(t *testing.T) { + // expected result + expectedResult := map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + "body": nil, + "raw_body": "", + "headers": map[string]any{ + "content-length": []any{"0"}, + }, + } + + resultObj, err := ast.InterfaceToValue(expectedResult) + if err != nil { + t.Fatal(err) + } + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s", "tls_ca_cert_env_variable": "CLIENT_CA_ENV", "tls_client_cert_env_variable": "CLIENT_CERT_ENV", "tls_client_key_env_variable": "CLIENT_KEY_ENV", "tls_ca_cert_file": "%s", "tls_client_cert_file": "%s", "tls_client_key_file": "%s"}, resp); x := clean_headers(resp) }`, s.URL, localCaFile, localClientCertFile, localClientKeyFile), + ) + + // run the test + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) + }) + + t.Run("HTTPS Get with System Certs, Env and File Cert", func(t *testing.T) { + // expected result + expectedResult := map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + "body": nil, + "raw_body": "", + "headers": map[string]any{ + "content-length": []any{"0"}, + }, + } + + resultObj, err := ast.InterfaceToValue(expectedResult) + if err != nil { + t.Fatal(err) + } + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf(`p = x { http.send({"method": "get", "url": %q, "tls_use_system_certs": true, "tls_ca_cert_env_variable": "CLIENT_CA_ENV", "tls_client_cert_env_variable": "CLIENT_CERT_ENV", "tls_client_key_env_variable": "CLIENT_KEY_ENV", "tls_ca_cert_file": "%s", "tls_client_cert_file": "%s", "tls_client_key_file": "%s"}, resp); x := clean_headers(resp) }`, s.URL, localCaFile, localClientCertFile, localClientKeyFile), + ) + + // run the test + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) + }) + + t.Run("Negative Test: No Root Ca", func(t *testing.T) { + expectedResult := &Error{Code: BuiltinErr, Message: fixupDarwinGo118("x509: certificate signed by unknown authority", `“my-server” certificate is not standards compliant`), Location: nil} + data := loadSmallTestData() + rule := []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": "%s", "tls_client_cert_file": "%s", "tls_client_key_file": "%s"}, x) }`, s.URL, localClientCertFile, localClientKeyFile)} + + // run the test + runTopDownTestCase(t, data, "http.send", rule, expectedResult) + }) + + t.Run("Negative Test: Wrong Cert/Key Pair", func(t *testing.T) { + expectedResult := &Error{Code: BuiltinErr, Message: "tls: private key does not match public key", Location: nil} + data := loadSmallTestData() + rule := []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": "%s", "tls_ca_cert_file": "%s", "tls_client_cert_file": "%s", "tls_client_key_file": "%s"}, x) }`, s.URL, localCaFile, localClientCert2File, localClientKeyFile)} + + // run the test + runTopDownTestCase(t, data, "http.send", rule, expectedResult) + }) + + t.Run("Negative Test: System Certs do not include local rootCA", func(t *testing.T) { + expectedResult := &Error{Code: BuiltinErr, Message: fixupDarwinGo118("x509: certificate signed by unknown authority", `“my-server” certificate is not standards compliant`), Location: nil} + data := loadSmallTestData() + rule := []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": %q, "tls_client_cert_file": %q, "tls_client_key_file": %q, "tls_use_system_certs": true}, x) }`, s.URL, localClientCertFile, localClientKeyFile)} + + // run the test + runTopDownTestCase(t, data, "http.send", rule, expectedResult) + }) + + // Expect that setting the Host header causes TLS server validation + // to fail because the server sends a different certificate. + t.Run("Client Host is also ServerName", func(t *testing.T) { + url := s.URL + "/cert" + hostname := "notpresent" + + expected := &Error{Code: BuiltinErr, Message: "x509: certificate is valid for localhost, not " + hostname} + + data := loadSmallTestData() + rule := []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": "%s", "tls_ca_cert_file": "%s", "tls_client_cert_file": "%s", "tls_client_key_file": "%s", "headers": {"host": "%s"}}, x) }`, url, localCaFile, localClientCertFile, localClientKeyFile, hostname)} + + // run the test + runTopDownTestCase(t, data, "http.send", rule, expected) + }) + + // Expect that setting `tls_server_name` causes TLS server validation + // to fail because the server sends a different certificate. + t.Run("Client can set ServerName", func(t *testing.T) { + url := s.URL + "/cert" + hostname := "notpresent" + + expected := &Error{Code: BuiltinErr, Message: "x509: certificate is valid for localhost, not " + hostname} + + data := loadSmallTestData() + rule := []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": %q, "tls_ca_cert_file": %q, "tls_client_cert_file": %q, "tls_client_key_file": %q, "tls_server_name": %q}, x) }`, url, localCaFile, localClientCertFile, localClientKeyFile, hostname)} + + // run the test + runTopDownTestCase(t, data, "http.send", rule, expected) + }) +} + +// Warning(philipc): This test cannot be run in parallel with other tests, due +// to the t.Setenv calls from one of its helper methods. +func TestHTTPSNoClientCerts(t *testing.T) { + const ( + localCaFile = "testdata/ca.pem" + localServerCertFile = "testdata/server-cert.pem" + localServerKeyFile = "testdata/server-key.pem" + ) + + caCertPEM, err := os.ReadFile(localCaFile) + if err != nil { + t.Fatal(err) + } + caPool := x509.NewCertPool() + if ok := caPool.AppendCertsFromPEM(caCertPEM); !ok { + t.Fatal("failed to parse CA cert") + } + + cert, err := tls.LoadX509KeyPair(localServerCertFile, localServerKeyFile) + if err != nil { + t.Fatal(err) + } + + t.Setenv("CLIENT_CA_ENV", string(caCertPEM)) + + // Replicating some of what happens in the server's HTTPS listener + s := getTLSTestServer() + s.TLS = &tls.Config{ + Certificates: []tls.Certificate{cert}, + ClientCAs: caPool, + } + s.StartTLS() + defer s.Close() + + t.Run("HTTPS Get with Broken CA Cert w/ File", func(t *testing.T) { + // `tls_ca_cert_file` is valid, but `tls_ca_cert` is not, so we + // expect and error building the TLS context. + expectedResult := &Error{Code: BuiltinErr, Message: "could not append certificates"} + + data := loadSmallTestData() + rule := []string{fmt.Sprintf( + "p = x { http.send({`method`: `get`, `url`: `%s`, `tls_ca_cert`: `%s`, `tls_ca_cert_file`: `%s`}, x) }", s.URL, "xxx", localCaFile)} + + runTopDownTestCase(t, data, "http.send", rule, expectedResult) + }) + + t.Run("HTTPS Get with Broken CA Cert w/ Env", func(t *testing.T) { + // `tls_ca_cert_env_variable` is valid, but `tls_ca_cert` is not, so we + // expect and error building the TLS context. + expectedResult := &Error{Code: BuiltinErr, Message: "could not append certificates"} + + data := loadSmallTestData() + rule := []string{fmt.Sprintf( + "p = x { http.send({`method`: `get`, `url`: `%s`, `tls_ca_cert`: `%s`, `tls_ca_cert_env_variable`: `CLIENT_CA_ENV`}, x) }", s.URL, "xxx")} + + runTopDownTestCase(t, data, "http.send", rule, expectedResult) + }) + + t.Run("HTTPS Get with Inline CA Cert", func(t *testing.T) { + expectedResult := map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + "body": nil, + "raw_body": "", + "headers": map[string]any{ + "content-length": []any{"0"}, + }, + } + + resultObj, err := ast.InterfaceToValue(expectedResult) + if err != nil { + t.Fatal(err) + } + + ca, err := os.ReadFile(localCaFile) + if err != nil { + t.Fatal(err) + } + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf("p = x { http.send({`method`: `get`, `url`: `%s`, `tls_ca_cert`: `%s`}, resp); x := clean_headers(resp) }", s.URL, ca), + ) + + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) + }) + + t.Run("HTTPS Get with CA Cert File", func(t *testing.T) { + // expected result + expectedResult := map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + "body": nil, + "raw_body": "", + "headers": map[string]any{ + "content-length": []any{"0"}, + }, + } + + resultObj, err := ast.InterfaceToValue(expectedResult) + if err != nil { + t.Fatal(err) + } + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s", "tls_ca_cert_file": "%s"}, resp); x := clean_headers(resp) }`, s.URL, localCaFile), + ) + + // run the test + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) + }) + + t.Run("HTTPS Get with CA Cert ENV", func(t *testing.T) { + // expected result + expectedResult := map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + "body": nil, + "raw_body": "", + "headers": map[string]any{ + "content-length": []any{"0"}, + }, + } + + resultObj, err := ast.InterfaceToValue(expectedResult) + if err != nil { + t.Fatal(err) + } + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s", "tls_ca_cert_env_variable": "CLIENT_CA_ENV"}, resp); x := clean_headers(resp) }`, s.URL), + ) + + // run the test + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) + }) + + t.Run("HTTPS Get with System CA Cert Pool", func(t *testing.T) { + // expected result + expectedResult := map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + "body": nil, + "raw_body": "", + "headers": map[string]any{ + "content-length": []any{"0"}, + }, + } + + resultObj, err := ast.InterfaceToValue(expectedResult) + if err != nil { + t.Fatal(err) + } + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s", "tls_ca_cert_env_variable": "CLIENT_CA_ENV"}, resp); x := clean_headers(resp) }`, s.URL), + ) + + // run the test + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) + }) + + t.Run("HTTPS Get with System Certs, Env and File Cert", func(t *testing.T) { + // expected result + expectedResult := map[string]any{ + "status": "200 OK", + "status_code": http.StatusOK, + "body": nil, + "raw_body": "", + "headers": map[string]any{ + "content-length": []any{"0"}, + }, + } + + resultObj, err := ast.InterfaceToValue(expectedResult) + if err != nil { + t.Fatal(err) + } + + data := loadSmallTestData() + rules := append( + httpSendHelperRules, + fmt.Sprintf(`p = x { http.send({"method": "get", "url": "%s", "tls_use_system_certs": true, "tls_ca_cert_env_variable": "CLIENT_CA_ENV", "tls_ca_cert_file": "%s"}, resp); x := clean_headers(resp) }`, s.URL, localCaFile), + ) + + // run the test + runTopDownTestCase(t, data, "http.send", rules, resultObj.String()) + }) + + t.Run("Negative Test: System Certs do not include local rootCA", func(t *testing.T) { + expectedResult := &Error{Code: BuiltinErr, Message: fixupDarwinGo118("x509: certificate signed by unknown authority", `“my-server” certificate is not standards compliant`), Location: nil} + data := loadSmallTestData() + rule := []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": %q, "tls_use_system_certs": true}, x) }`, s.URL)} + + // run the test + runTopDownTestCase(t, data, "http.send", rule, expectedResult) + }) +} + +// Note(philipc): In Go 1.18, the crypto/x509 package deprecated the +// (*CertPool).Subjects() function. The precise reasoning for why this was +// done traces back to: +// +// https://github.com/golang/go/issues/46287 +// +// For now, most projects seem to be working around this deprecation by +// changing how they verify certificates, and when CertPools are needed in +// tests, some larger projects have just slapped linter ignores on the +// offending callsites. Since we only use (*CertPool).Subjects() here for +// tests, we've gone with using linter ignores for now. +// +// Warning(philipc): This test cannot be run in parallel with other tests, due +// to the t.Setenv calls used to set up the server environment. +func TestCertSelectionLogic(t *testing.T) { + const ( + localCaFile = "testdata/ca.pem" + ) + + // Set up Environment + caCertPEM, err := os.ReadFile(localCaFile) + if err != nil { + t.Fatal(err) + } + + caPool := x509.NewCertPool() + if ok := caPool.AppendCertsFromPEM(caCertPEM); !ok { + t.Fatal("failed to parse CA cert") + } + + ca, err := os.ReadFile(localCaFile) + if err != nil { + t.Fatal(err) + } + + t.Setenv("CLIENT_CA_ENV", string(caCertPEM)) + + getClientTLSConfig := func(obj ast.Object) *tls.Config { + _, client, err := createHTTPRequest(BuiltinContext{Context: context.Background()}, obj) + if err != nil { + t.Fatalf("Unexpected error creating HTTP request %v", err) + } + if client.Transport == nil { + return nil + } + return client.Transport.(*http.Transport).TLSClientConfig + } + + systemCertsPool, err := x509.SystemCertPool() + if err != nil { + t.Fatalf("Unexpected error reading system certs %v", err) + } + + tempSystemCertsPool, err := x509.SystemCertPool() + if err != nil { + t.Fatalf("Unexpected error reading system certs %v", err) + } + systemCertsAndCaPool, err := addCACertsFromBytes(tempSystemCertsPool, ca) + if err != nil { + t.Fatalf("Unexpected error merging system certs and ca certs %v", err) + } + + tests := []struct { + note string + input map[*ast.Term]*ast.Term + expected [][]byte + msg string + }{ + { + note: "tls_use_system_certs set to true", + input: map[*ast.Term]*ast.Term{ast.StringTerm("tls_use_system_certs"): ast.BooleanTerm(true)}, + expected: systemCertsPool.Subjects(), // nolint:staticcheck // ignoring the deprecated (*CertPool).Subjects() call here because it's in a test. + msg: "Expected TLS config to use system certs", + }, + { + note: "tls_use_system_certs set to nil", + input: map[*ast.Term]*ast.Term{ast.StringTerm("tls_use_system_certs"): ast.BooleanTerm(false)}, + expected: nil, + msg: "Expected no TLS config", + }, + { + note: "no CAs specified", + input: nil, + expected: systemCertsPool.Subjects(), // nolint:staticcheck // ignoring the deprecated (*CertPool).Subjects() call here because it's in a test. + msg: "Expected TLS config to use system certs", + }, + { + note: "CA cert provided directly", + input: map[*ast.Term]*ast.Term{ast.StringTerm("tls_ca_cert"): ast.StringTerm(string(ca))}, + expected: caPool.Subjects(), // nolint:staticcheck // ignoring the deprecated (*CertPool).Subjects() call here because it's in a test. + msg: "Expected TLS config to use provided CA certs", + }, + { + note: "CA cert file path provided", + input: map[*ast.Term]*ast.Term{ast.StringTerm("tls_ca_cert_file"): ast.StringTerm(localCaFile)}, + expected: caPool.Subjects(), // nolint:staticcheck // ignoring the deprecated (*CertPool).Subjects() call here because it's in a test. + msg: "Expected TLS config to use provided CA certs in file", + }, + { + note: "CA cert provided in env variable", + input: map[*ast.Term]*ast.Term{ast.StringTerm("tls_ca_cert_env_variable"): ast.StringTerm("CLIENT_CA_ENV")}, + expected: caPool.Subjects(), // nolint:staticcheck // ignoring the deprecated (*CertPool).Subjects() call here because it's in a test. + msg: "Expected TLS config to use provided CA certs in env variable", + }, + { + note: "CA cert provided directly and tls_use_system_certs parameter set to false", + input: map[*ast.Term]*ast.Term{ + ast.StringTerm("tls_ca_cert"): ast.StringTerm(string(ca)), + ast.StringTerm("tls_use_system_certs"): ast.BooleanTerm(false), + }, + expected: caPool.Subjects(), // nolint:staticcheck // ignoring the deprecated (*CertPool).Subjects() call here because it's in a test. + msg: "Expected TLS config to use provided CA certs only", + }, + { + note: "CA cert provided directly and tls_use_system_certs parameter set to true", + input: map[*ast.Term]*ast.Term{ + ast.StringTerm("tls_ca_cert"): ast.StringTerm(string(ca)), + ast.StringTerm("tls_use_system_certs"): ast.BooleanTerm(true), + }, + expected: systemCertsAndCaPool.Subjects(), // nolint:staticcheck // ignoring the deprecated (*CertPool).Subjects() call here because it's in a test. + msg: "Expected TLS config to use provided CA certs and system certs", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + obj := ast.NewObject() + for key, value := range tc.input { + obj.Insert(key, value) + } + tlsConfig := getClientTLSConfig(obj) + if tc.expected == nil { + if tlsConfig != nil { + t.Fatal(tc.msg) + } + } else { + // nolint:staticcheck // ignoring the deprecated (*CertPool).Subjects() call here because it's in a test. + if !reflect.DeepEqual(tlsConfig.RootCAs.Subjects(), tc.expected) { + t.Fatal(tc.msg) + } + } + }) + } +} + +func TestHTTPSendCacheDefaultStatusCodesIntraQueryCache(t *testing.T) { + t.Parallel() + + // run test server + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + if len(requests)%2 == 0 { + headers := w.Header() + headers["Cache-Control"] = []string{"max-age=290304000, public"} + w.WriteHeader(http.StatusOK) + } else { + w.WriteHeader(http.StatusInternalServerError) + } + })) + + t.Cleanup(ts.Close) + + t.Run("non-cacheable status code: intra-query cache", func(t *testing.T) { + base := fmt.Sprintf(`http.send({"method": "get", "url": %q, "cache": true})`, ts.URL) + query := fmt.Sprintf("%v;%v;%v", base, base, base) + + q := NewQuery(ast.MustParseBody(query)) + + // Execute three http.send calls within a query. + // Since the server returns a http.StatusInternalServerError on the first request, this should NOT be cached as + // http.StatusInternalServerError is not a cacheable status code. The second request should result in OPA reaching + // out to the server again and getting a http.StatusOK response status code. + // The third request should now be served from the cache. + + _, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + expectedReqCount := 2 + if len(requests) != expectedReqCount { + t.Fatalf("Expected to get %d requests, got %d", expectedReqCount, len(requests)) + } + }) +} + +func TestHTTPSendCacheDefaultStatusCodesInterQueryCache(t *testing.T) { + t.Parallel() + + // run test server + var requests []*http.Request + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requests = append(requests, r) + if len(requests)%2 == 0 { + headers := w.Header() + headers["Cache-Control"] = []string{"max-age=290304000, public"} + w.WriteHeader(http.StatusOK) + } else { + w.WriteHeader(http.StatusInternalServerError) + } + })) + + defer ts.Close() + + t.Run("non-cacheable status code: inter-query cache", func(t *testing.T) { + + // add an inter-query cache + config, _ := iCache.ParseCachingConfig([]byte(`{"inter_query_builtin_cache": {"max_size_bytes": 500, "stale_entry_eviction_period_seconds": 1, "forced_eviction_threshold_percentage": 80},}`)) + interQueryCache := iCache.NewInterQueryCacheWithContext(context.Background(), config) + + m := metrics.New() + + q := NewQuery(ast.MustParseBody(fmt.Sprintf(`http.send({"method": "get", "url": %q, "cache": true})`, ts.URL))). + WithMetrics(m).WithInterQueryBuiltinCache(interQueryCache) + + // Execute three queries. + // Since the server returns a http.StatusInternalServerError on the first request, this should NOT be cached as + // http.StatusInternalServerError is not a cacheable status code. The second request should result in OPA reaching + // out to the server again and getting a http.StatusOK response status code. + // The third request should now be served from the cache. + + _, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + _, err = q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + _, err = q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + expectedReqCount := 2 + if len(requests) != expectedReqCount { + t.Fatalf("Expected to get %d requests, got %d", expectedReqCount, len(requests)) + } + + // verify http.send inter-query cache hit metric is incremented due to the third request. + if exp, act := uint64(1), m.Counter(httpSendInterQueryCacheHits).Value(); exp != act { + t.Fatalf("expected %d cache hits, got %d", exp, act) + } + }) +} + +type onlyOnceInterQueryCache struct { + value *interQueryCacheData + counter int +} + +func (c *onlyOnceInterQueryCache) Get(_ ast.Value) (value iCache.InterQueryCacheValue, found bool) { + c.counter++ + if c.counter == 1 { + return c.value, true + } + return nil, false +} + +func (*onlyOnceInterQueryCache) Insert(_ ast.Value, _ iCache.InterQueryCacheValue) int { + return 0 +} + +func (*onlyOnceInterQueryCache) InsertWithExpiry(_ ast.Value, _ iCache.InterQueryCacheValue, _ time.Time) int { + return 0 +} + +func (*onlyOnceInterQueryCache) Delete(_ ast.Value) {} + +func (*onlyOnceInterQueryCache) UpdateConfig(_ *iCache.Config) {} + +func (*onlyOnceInterQueryCache) Clone(val iCache.InterQueryCacheValue) (iCache.InterQueryCacheValue, error) { + return val, nil +} + +func TestInterQueryCacheConcurrentModification(t *testing.T) { + t.Parallel() + + // create an inter-query cache that'll return a value on first access, but none at subsequent accesses. + clock := time.Now() + req := ast.NewObject( + [2]*ast.Term{ast.StringTerm("method"), ast.StringTerm("get")}, + [2]*ast.Term{ast.StringTerm("url"), ast.StringTerm("foobar")}, + [2]*ast.Term{ast.StringTerm("cache"), ast.BooleanTerm(true)}, + ) + resp := interQueryCacheData{ + Headers: map[string][]string{ + "Date": {"Thu, 01 Jan 1970 00:00:00 GMT"}, + }, + ExpiresAt: clock.Add(time.Hour), + } + interQueryCache := onlyOnceInterQueryCache{value: &resp} + + reqStr := req.String() + rule := fmt.Sprintf(`package test + p := http.send(%s) + q := http.send(%s) +`, reqStr, reqStr) + c, err := compileRules([]string{}, []string{}, []string{rule}) + if err != nil { + t.Fatal(err) + } + + qStr := "x = data.test.p; y = data.test.q" + ctx := context.Background() + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store) + q := NewQuery(ast.MustParseBody(qStr)). + WithCompiler(c). + WithStore(store). + WithTransaction(txn). + WithInterQueryBuiltinCache(&interQueryCache). + WithTime(clock) + + res, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + if res[0]["x"].Value.Compare(res[0]["y"].Value) != 0 { + t.Fatalf("Expected x and y to be equal, got %v and %v", res[0]["x"].Value, res[0]["y"].Value) + } +} + +func TestInterQueryCacheDataClone(t *testing.T) { + t.Parallel() + + data := interQueryCacheData{ + Headers: map[string][]string{ + "Date": {"Thu, 01 Jan 1970 00:00:00 GMT"}, + }, + ExpiresAt: time.Now().Add(time.Hour), + StatusCode: 200, + Status: "200 OK", + RespBody: []byte("foo"), + } + + dup, err := data.Clone() + if err != nil { + t.Fatal(err) + } + + cloned, ok := dup.(*interQueryCacheData) + if !ok { + t.Fatal("unexpected type") + } + + if !reflect.DeepEqual(data, *cloned) { + t.Fatalf("Expected to get %v, but got %v", data, *cloned) + } +} + +func TestInterQueryCacheValueClone(t *testing.T) { + t.Parallel() + + cacheData := interQueryCacheData{ + Headers: map[string][]string{ + "Date": {"Thu, 01 Jan 1970 00:00:00 GMT"}, + }, + ExpiresAt: time.Now().Add(time.Hour), + StatusCode: 200, + Status: "200 OK", + RespBody: []byte("foo"), + } + + b, err := json.Marshal(cacheData) + if err != nil { + t.Fatal(err) + } + + cacheVal := interQueryCacheValue{Data: b} + + dup, err := cacheVal.Clone() + if err != nil { + t.Fatal(err) + } + + cloned, ok := dup.(*interQueryCacheValue) + if !ok { + t.Fatal("unexpected type") + } + + if !reflect.DeepEqual(cacheVal, *cloned) { + t.Fatal("inter-query cache element and its clone are not equal") + } +} + +func TestRaisingHTTPClientQueryError(t *testing.T) { + t.Parallel() + + data := loadSmallTestData() + + tests := []struct { + note string + rules []string + expected string + expectedError string + }{ + { + note: "raised errors with inter query cache", + rules: []string{`p["one"] { + not http.send({"method": "GET", "url": "bad_url", "cache": true}) +}`, + `p["two"] { + not http.send({"method": "GET", "url": "bad_url", "cache": true}) +}`}, + expected: `["one", "two"]`, + expectedError: `not http.send({"method": "GET", "url": "bad_url", "cache": true}): eval_builtin_error: http.send: Get "bad_url": unsupported protocol scheme ""`, + }, + { + note: "no raised errors with inter query cache", + rules: []string{`p["one"] { + r := http.send({"method": "GET", "url": "bad_url", "cache": true, "raise_error": false}) + r.error.code == "eval_http_send_network_error" +}`, + `p["two"] { + r := http.send({"method": "GET", "url": "bad_url", "cache": true, "raise_error": false}) + r.error.code == "eval_http_send_network_error" +}`}, + expected: `["one", "two"]`, + }, + { + note: "raised errors with intra query cache", + rules: []string{`p["one"] { + not http.send({"method": "GET", "url": "bad_url"}) +}`, + `p["two"] { + not http.send({"method": "GET", "url": "bad_url"}) +}`}, + expected: `["one", "two"]`, + expectedError: `not http.send({"method": "GET", "url": "bad_url"}): eval_builtin_error: http.send: Get "bad_url": unsupported protocol scheme ""`, + }, + { + note: "no raised errors with intra query cache", + rules: []string{`p["one"] { + r := http.send({"method": "GET", "url": "bad_url", "raise_error": false}) + r.error.code == "eval_http_send_network_error" +}`, + `p["two"] { + r := http.send({"method": "GET", "url": "bad_url", "raise_error": false}) + r.error.code == "eval_http_send_network_error" +}`}, + expected: `["one", "two"]`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var builtInErrList []Error + + runTopDownTestCase(t, data, tc.note, tc.rules, tc.expected, func(q *Query) *Query { + q.WithBuiltinErrorList(&builtInErrList) + return q + }) + + if tc.expectedError == "" && len(builtInErrList) > 0 { + t.Fatalf("builtInErrList shouldn't contain an error but it does: %v", builtInErrList) + } + + if tc.expectedError != "" && len(builtInErrList) == 0 { + t.Fatalf("builtInErrList did not contain errors but expected: %s", tc.expectedError) + } + + if tc.expectedError != "" && builtInErrList[0].Error() != tc.expectedError { + t.Errorf("Expected error %v, but got %v", tc.expectedError, builtInErrList[0].Error()) + } + }) + } +} + +func TestHTTPSendMetrics(t *testing.T) { + t.Parallel() + + // run test server + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + defer ts.Close() + + t.Run("latency", func(t *testing.T) { + // Execute query and verify http.send latency shows up in metrics registry. + m := metrics.New() + q := NewQuery(ast.MustParseBody(fmt.Sprintf(`http.send({"method": "get", "url": %q})`, ts.URL))).WithMetrics(m) + _, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + if m.Timer(httpSendLatencyMetricKey).Int64() == 0 { + t.Fatal("expected non-zero value for http.send latency metric") + } + }) + + t.Run("cache hits", func(t *testing.T) { + // add an inter-query cache + config, _ := iCache.ParseCachingConfig([]byte(`{"inter_query_builtin_cache": {"max_size_bytes": 500, "stale_entry_eviction_period_seconds": 1, "forced_eviction_threshold_percentage": 80},}`)) + interQueryCache := iCache.NewInterQueryCacheWithContext(context.Background(), config) + + // Execute query twice and verify http.send inter-query cache hit metric is incremented. + m := metrics.New() + q := NewQuery(ast.MustParseBody(fmt.Sprintf(`http.send({"method": "get", "url": %q, "cache": true})`, ts.URL))). + WithInterQueryBuiltinCache(interQueryCache). + WithMetrics(m) + _, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + // cache hit + _, err = q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + if exp, act := uint64(1), m.Counter(httpSendInterQueryCacheHits).Value(); exp != act { + t.Fatalf("expected %d cache hits, got %d", exp, act) + } + }) +} + +// Warning(philipc): This test cannot be run in parallel with other tests, due +// to the t.Setenv calls used to set up the server environment. +func TestInitDefaults(t *testing.T) { + t.Setenv("HTTP_SEND_TIMEOUT", "300mss") + + defer func() { + if r := recover(); r == nil { + t.Fatal("expected function to panic") + } + }() + initDefaults() +} + +var httpSendHelperRules = []string{ + `clean_headers(resp) = cleaned { + cleaned = json.remove(resp, ["headers/date"]) + }`, + `remove_headers(resp) = no_headers { + no_headers = object.remove(resp, ["headers"]) + }`, +} + +func TestSocketHTTPGetRequest(t *testing.T) { + t.Parallel() + + var people []Person + + // test data + people = append(people, Person{ID: "1", Firstname: "John"}) + + // Create a local socket + tmpF, err := os.CreateTemp(t.TempDir(), "") + if err != nil { + t.Fatal(err) + } + + socketPath := tmpF.Name() + tmpF.Close() + _ = os.Remove(socketPath) + + socket, err := net.Listen("unix", socketPath) + if err != nil { + t.Fatal(err) + } + + rs := http.Server{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + headers := w.Header() + headers["test-header"] = []string{"test-value"} + headers["echo-query-string"] = []string{r.URL.RawQuery} + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(people) + }), + } + + go func() { + _ = rs.Serve(socket) + }() + defer rs.Close() + + path := "socket=" + url.PathEscape(socketPath) + rawURL := fmt.Sprintf("unix://localhost/end/point?%s¶m1=value1¶m2=value2", path) // Send a request to the server over the socket + + // expected result + expectedResult := make(map[string]any) + expectedResult["status"] = "200 OK" + expectedResult["status_code"] = http.StatusOK + + var body []any + bodyMap := map[string]string{"id": "1", "firstname": "John"} + body = append(body, bodyMap) + expectedResult["body"] = body + expectedResult["raw_body"] = "[{\"id\":\"1\",\"firstname\":\"John\"}]\n" + expectedResult["headers"] = map[string]any{ + "content-length": []any{"32"}, + "content-type": []any{"text/plain; charset=utf-8"}, + "test-header": []any{"test-value"}, + "echo-query-string": []any{"param1=value1¶m2=value2"}, + } + + resultObj := ast.MustInterfaceToValue(expectedResult) + + // run the test + tests := []struct { + note string + rules []string + expected any + }{ + {"http.send", []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": %q, "force_json_decode": true}, resp); x := clean_headers(resp) }`, rawURL)}, resultObj.String()}, + {"http.send skip verify no HTTPS", []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": %q, "force_json_decode": true, "tls_insecure_skip_verify": true}, resp); x := clean_headers(resp) }`, rawURL)}, resultObj.String()}, + } + + data := loadSmallTestData() + + for _, tc := range tests { + runTopDownTestCase(t, data, tc.note, append(tc.rules, httpSendHelperRules...), tc.expected) + } +} + +type tracemock struct { + called int +} + +func (m *tracemock) NewTransport(rt http.RoundTripper, _ tracing.Options) http.RoundTripper { + m.called++ + return rt +} + +func (*tracemock) NewHandler(http.Handler, string, tracing.Options) http.Handler { + panic("unreachable") +} + +// Warning(philipc): This test modifies package variables in tracing, which +// means it cannot be run in parallel with other tests. +func TestDistributedTracingEnableDisable(t *testing.T) { + t.Run("TestDistributedTracingEnabled", func(t *testing.T) { + mock := tracemock{} + tracing.RegisterHTTPTracing(&mock) + + builtinContext := BuiltinContext{ + Context: context.Background(), + DistributedTracingOpts: tracing.NewOptions(true), // any option means it's enabled + } + + _, client, err := createHTTPRequest(builtinContext, ast.NewObject()) + if err != nil { + t.Fatalf("Unexpected error creating HTTP request %v", err) + } + if client.Transport == nil { + t.Fatal("No Transport defined") + } + + if exp, act := 1, mock.called; exp != act { + t.Errorf("calls to NewTransport: expected %d, got %d", exp, act) + } + }) + + t.Run("TestDistributedTracingDisabled", func(t *testing.T) { + mock := tracemock{} + tracing.RegisterHTTPTracing(&mock) + + builtinContext := BuiltinContext{ + Context: context.Background(), + } + + _, client, err := createHTTPRequest(builtinContext, ast.NewObject()) + if err != nil { + t.Fatalf("Unexpected error creating HTTP request %v", err) + } + if client.Transport == nil { + t.Fatal("No Transport defined") + } + + if exp, act := 0, mock.called; exp != act { + t.Errorf("calls to NewTransported: expected %d, got %d", exp, act) + } + }) +} + +func TestHTTPGetRequestAllowNet(t *testing.T) { + t.Parallel() + + // test data + body := map[string]bool{"ok": true} + + // test server + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(body) + })) + + defer ts.Close() + + // host + serverURL, err := url.Parse(ts.URL) + if err != nil { + t.Fatal(err) + } + serverHost := strings.Split(serverURL.Host, ":")[0] + + // expected result + expectedResult := make(map[string]any) + expectedResult["status"] = "200 OK" + expectedResult["status_code"] = http.StatusOK + + expectedResult["body"] = body + expectedResult["raw_body"] = "{\"ok\":true}\n" + + resultObj := ast.MustInterfaceToValue(expectedResult) + + expectedError := &Error{Code: "eval_builtin_error", Message: "http.send: unallowed host: " + serverHost} + + rules := []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": %q, "force_json_decode": true}, resp); x := remove_headers(resp) }`, ts.URL)} + + // run the test + tests := []struct { + note string + rules []string + options func(*Query) *Query + expected any + }{ + { + "http.send allow_net nil", + rules, + + setAllowNet(nil), + resultObj.String(), + }, + { + "http.send allow_net match", + rules, + setAllowNet([]string{serverHost}), + resultObj.String(), + }, + { + "http.send allow_net match + additional host", + rules, + setAllowNet([]string{serverHost, "example.com"}), + resultObj.String(), + }, + { + "http.send allow_net empty", + rules, + setAllowNet([]string{}), + expectedError, + }, + { + "http.send allow_net no match", + rules, + setAllowNet([]string{"example.com"}), + expectedError, + }, + } + + data := loadSmallTestData() + + for _, tc := range tests { + runTopDownTestCase(t, data, tc.note, append(tc.rules, httpSendHelperRules...), tc.expected, tc.options) + } +} + +type secretTransport struct { + extraRequestHeaders http.Header + + *http.Transport +} + +func (st *secretTransport) RoundTrip(req *http.Request) (*http.Response, error) { + // Set additional headers on the request not visible to the caller + maps.Copy(req.Header, st.extraRequestHeaders) + return st.Transport.RoundTrip(req) +} + +func (st *secretTransport) Transform(t *http.Transport) http.RoundTripper { + st.Transport = t.Clone() + return st +} + +func TestHTTPWithCustomTransport(t *testing.T) { + // test data + body := map[string]bool{"ok": true} + + // test server only returns answers when a custom header is set + var callCount int + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + callCount++ + if r.Header.Get("secret-header") != "secret-value" { + w.WriteHeader(http.StatusForbidden) + return + } + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(body) + })) + + defer ts.Close() + + // host + serverURL, err := url.Parse(ts.URL) + if err != nil { + t.Fatal(err) + } + serverHost := strings.Split(serverURL.Host, ":")[0] + + // expected result + expectedResult := make(map[string]any) + expectedResult["status"] = "200 OK" + expectedResult["status_code"] = http.StatusOK + + expectedResult["body"] = body + expectedResult["raw_body"] = "{\"ok\":true}\n" + + resultObj := ast.MustInterfaceToValue(expectedResult) + + hostError := &Error{Code: "eval_builtin_error", Message: "http.send: unallowed host: " + serverHost} + expectedError := map[string]any{"body": nil, "raw_body": "", "status": "403 Forbidden", "status_code": 403} + errorObj := ast.MustInterfaceToValue(expectedError) + + rules := []string{fmt.Sprintf( + `p = x { http.send({"method": "get", "url": "%s", "force_json_decode": true}, resp); x := remove_headers(resp) }`, ts.URL)} + + st := &secretTransport{ + extraRequestHeaders: http.Header{"secret-header": []string{"secret-value"}}, + } + + // run the test + tests := []struct { + note string + rules []string + options func(*Query) *Query + expected any + calls int + }{ + { + "http.send transport is default", + rules, + func(q *Query) *Query { + return q + }, + errorObj.String(), + 1, + }, + { + "http.send transport is nil", + rules, + setRoundTripper(nil), + errorObj.String(), + 1, + }, + { + "http.send transport adds secret header", + rules, + setRoundTripper(st.Transform), + resultObj.String(), + 1, + }, + { + "http.send allow_net empty, no call to endpoint", + rules, + setAllowNet([]string{}), + hostError, + 0, + }, + } + + data := loadSmallTestData() + + for _, tc := range tests { + startingCalls := callCount + runTopDownTestCase(t, data, tc.note, append(tc.rules, httpSendHelperRules...), tc.expected, tc.options) + // Note: The runTopDownTestCase ends up evaluating twice (once with and once without partial + // eval first), so expect 2x the total request count the test case specified. + serverCalls := (callCount - startingCalls) / 2 + if serverCalls != tc.calls { + t.Errorf("Expected %d calls to server, got %d", tc.calls, serverCalls) + } + } +} + +func TestIsJSONType(t *testing.T) { + tests := []struct { + name string + h http.Header + exp bool + }{ + { + h: http.Header{ + "Content-Type": []string{"application/json"}, + }, + exp: true, + }, + { + h: http.Header{ + "Content-Type": []string{"application/json; charset=utf-8"}, + }, + exp: true, + }, + { + h: http.Header{ + "Content-Type": []string{"application/scim+json; charset=utf-8"}, + }, + exp: true, + }, + { + h: http.Header{ + "Content-Type": []string{"application/thisisnotjson; charset=utf-8"}, + }, + exp: false, + }, + { + h: http.Header{ + "Content-Type": []string{"application/yaml"}, + }, + exp: false, + }, + { + h: http.Header{ + "Content-Type": []string{"application/x-yaml; charset=utf-8"}, + }, + exp: false, + }, + { + h: http.Header{ + "Content-Type": []string{"text/html; charset=ISO-8859-4"}, + }, + exp: false, + }, + } + + for _, tc := range tests { + if tc.exp != isJSONType(tc.h) { + t.Errorf("Expected %v for %v", tc.exp, tc.h) + } + } +} diff --git a/third_party/opa/v1/topdown/input.go b/third_party/opa/v1/topdown/input.go new file mode 100644 index 000000000000..ec37b3645103 --- /dev/null +++ b/third_party/opa/v1/topdown/input.go @@ -0,0 +1,100 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "errors" + + "github.com/open-policy-agent/opa/v1/ast" +) + +var errBadPath = errors.New("bad document path") + +func mergeTermWithValues(exist *ast.Term, pairs [][2]*ast.Term) (*ast.Term, error) { + + var result *ast.Term + var init bool + + for i, pair := range pairs { + + if err := ast.IsValidImportPath(pair[0].Value); err != nil { + return nil, errBadPath + } + + target := pair[0].Value.(ast.Ref) + + // Copy the value if subsequent pairs in the slice would modify it. + for j := i + 1; j < len(pairs); j++ { + other := pairs[j][0].Value.(ast.Ref) + if len(other) > len(target) && other.HasPrefix(target) { + pair[1] = pair[1].Copy() + break + } + } + + if len(target) == 1 { + result = pair[1] + init = true + } else { + if !init { + result = exist.Copy() + init = true + } + if result == nil { + result = ast.NewTerm(makeTree(target[1:], pair[1])) + } else { + node := result + done := false + for i := 1; i < len(target)-1 && !done; i++ { + obj, ok := node.Value.(ast.Object) + if !ok { + result = ast.NewTerm(makeTree(target[i:], pair[1])) + done = true + continue + } + if child := obj.Get(target[i]); !isObject(child) { + obj.Insert(target[i], ast.NewTerm(makeTree(target[i+1:], pair[1]))) + done = true + } else { // child is object + node = child + } + } + if !done { + if obj, ok := node.Value.(ast.Object); ok { + obj.Insert(target[len(target)-1], pair[1]) + } else { + result = ast.NewTerm(makeTree(target[len(target)-1:], pair[1])) + } + } + } + } + } + + if !init { + result = exist + } + + return result, nil +} + +// makeTree returns an object that represents a document where the value v is +// the leaf and elements in k represent intermediate objects. +func makeTree(k ast.Ref, v *ast.Term) ast.Object { + var obj ast.Object + for i := len(k) - 1; i >= 1; i-- { + obj = ast.NewObject(ast.Item(k[i], v)) + v = &ast.Term{Value: obj} + } + obj = ast.NewObject(ast.Item(k[0], v)) + return obj +} + +func isObject(x *ast.Term) bool { + if x == nil { + return false + } + _, ok := x.Value.(ast.Object) + return ok +} diff --git a/third_party/opa/v1/topdown/input_test.go b/third_party/opa/v1/topdown/input_test.go new file mode 100644 index 000000000000..b1645a477154 --- /dev/null +++ b/third_party/opa/v1/topdown/input_test.go @@ -0,0 +1,152 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestMergeTermWithValues(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + exist string + input [][2]string + expected any + }{ + { + note: "var", + input: [][2]string{{`input.hello`, `"world"`}}, + expected: `{"hello": "world"}`, + }, + { + note: "multiple vars", + input: [][2]string{{`input.a`, `"a"`}, {`input.b`, `"b"`}}, + expected: `{"a": "a", "b": "b"}`, + }, + { + note: "multiple overlapping vars", + input: [][2]string{{`input.a.b.c`, `"c"`}, {`input.a.b.d`, `"d"`}, {`input.x.y`, `[]`}}, + expected: `{"a": {"b": {"c": "c", "d": "d"}}, "x": {"y": []}}`, + }, + { + note: "ref value", + input: [][2]string{{"input.foo.bar", "data.com.example.widgets[i]"}}, + expected: `{"foo": {"bar": data.com.example.widgets[i]}}`, + }, + { + note: "non-object", + input: [][2]string{{"input", "[1,2,3]"}}, + expected: "[1,2,3]", + }, + { + note: "conflicting value", + input: [][2]string{{"input", "[1,2,3]"}, {"input.a", "true"}}, + expected: `{"a": true}`, + }, + { + note: "conflicting value, nested trailing terms", + input: [][2]string{{"input", "[1,2,3]"}, {"input.a.b", "true"}}, + expected: `{"a": {"b": true}}`, + }, + { + note: "conflicting merge", + input: [][2]string{{`input.a.b`, `"c"`}, {`input.a.b.d`, `"d"`}}, + expected: `{"a": {"b": {"d": "d"}}}`, + }, + { + note: "ordered roots", + input: [][2]string{{"input", `"a"`}, {"input", `"b"`}}, + expected: `"b"`, + }, + { + note: "bad import path", + input: [][2]string{{`input.a[1]`, `1`}}, + expected: errBadPath, + }, + { + note: "existing merge", + exist: `{"foo": {"bar": 1}}`, + input: [][2]string{{"input.foo.baz", "2"}}, + expected: `{"foo": {"bar": 1, "baz": 2}}`, + }, + { + note: "existing overwrite", + exist: `{"a": {"b": 1, "c": 2}}`, + input: [][2]string{{"input.a", `{"d": 3}`}}, + expected: `{"a": {"d": 3}}`, + }, + } + + for i, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + pairs := make([][2]*ast.Term, len(tc.input)) + + for j := range tc.input { + k := ast.MustParseTerm(tc.input[j][0]) + v := ast.MustParseTerm(tc.input[j][1]) + pairs[j] = [...]*ast.Term{k, v} + } + + var exist *ast.Term + + if tc.exist != "" { + exist = ast.MustParseTerm(tc.exist) + } + + input, err := mergeTermWithValues(exist, pairs) + + switch e := tc.expected.(type) { + case error: + if err == nil { + t.Fatalf("%v (#%d): Expected error %v but got: %v", tc.note, i+1, e, input) + } + if err.Error() != e.Error() { + t.Fatalf("%v (#%d): Expected error %v but got: %v", tc.note, i+1, e, err) + } + case string: + if err != nil { + t.Fatalf("%v (#%d): Unexpected error: %v", tc.note, i+1, err) + } + expected := ast.MustParseTerm(e) + if expected.Value.Compare(input.Value) != 0 { + t.Fatalf("%v (#%d): Expected input to equal %v but got: %v", tc.note, i+1, expected, input) + } + } + }) + } +} + +func TestMergeTermWithValuesInputsShouldBeImmutable(t *testing.T) { + t.Parallel() + + initial := ast.MustParseTerm(`{"foo": 1}`) + expInitial := initial.Copy() + two := ast.MustParseTerm(`2`) + + result, err := mergeTermWithValues(nil, [][2]*ast.Term{ + {ast.MustParseTerm("input"), initial}, + {ast.MustParseTerm("input.foo"), two}, + }) + + if err != nil { + t.Fatal(err) + } + + exp := ast.MustParseTerm(`{"foo": 2}`) + + if !result.Equal(exp) { + t.Fatalf("expected %v but got %v", exp, result) + } + + if !initial.Equal(expInitial) { + t.Fatalf("expected input value to be unchanged but got %v (expected: %v)", initial, expInitial) + } +} diff --git a/third_party/opa/v1/topdown/instrumentation.go b/third_party/opa/v1/topdown/instrumentation.go new file mode 100644 index 000000000000..93da1d002256 --- /dev/null +++ b/third_party/opa/v1/topdown/instrumentation.go @@ -0,0 +1,63 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import "github.com/open-policy-agent/opa/v1/metrics" + +const ( + evalOpPlug = "eval_op_plug" + evalOpResolve = "eval_op_resolve" + evalOpRuleIndex = "eval_op_rule_index" + evalOpBuiltinCall = "eval_op_builtin_call" + evalOpVirtualCacheHit = "eval_op_virtual_cache_hit" + evalOpVirtualCacheMiss = "eval_op_virtual_cache_miss" + evalOpBaseCacheHit = "eval_op_base_cache_hit" + evalOpBaseCacheMiss = "eval_op_base_cache_miss" + evalOpComprehensionCacheSkip = "eval_op_comprehension_cache_skip" + evalOpComprehensionCacheBuild = "eval_op_comprehension_cache_build" + evalOpComprehensionCacheHit = "eval_op_comprehension_cache_hit" + evalOpComprehensionCacheMiss = "eval_op_comprehension_cache_miss" + partialOpSaveUnify = "partial_op_save_unify" + partialOpSaveSetContains = "partial_op_save_set_contains" + partialOpSaveSetContainsRec = "partial_op_save_set_contains_rec" + partialOpCopyPropagation = "partial_op_copy_propagation" +) + +// Instrumentation implements helper functions to instrument query evaluation +// to diagnose performance issues. Instrumentation may be expensive in some +// cases, so it is disabled by default. +type Instrumentation struct { + m metrics.Metrics +} + +// NewInstrumentation returns a new Instrumentation object. Performance +// diagnostics recorded on this Instrumentation object will stored in m. +func NewInstrumentation(m metrics.Metrics) *Instrumentation { + return &Instrumentation{ + m: m, + } +} + +func (instr *Instrumentation) startTimer(name string) { + if instr == nil { + return + } + instr.m.Timer(name).Start() +} + +func (instr *Instrumentation) stopTimer(name string) { + if instr == nil { + return + } + delta := instr.m.Timer(name).Stop() + instr.m.Histogram(name).Update(delta) +} + +func (instr *Instrumentation) counterIncr(name string) { + if instr == nil { + return + } + instr.m.Counter(name).Incr() +} diff --git a/third_party/opa/v1/topdown/json.go b/third_party/opa/v1/topdown/json.go new file mode 100644 index 000000000000..2c7d64288336 --- /dev/null +++ b/third_party/opa/v1/topdown/json.go @@ -0,0 +1,405 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "errors" + "fmt" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + + "github.com/open-policy-agent/opa/internal/edittree" +) + +func builtinJSONRemove(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + // Expect an object and a string or array/set of strings + _, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Build a list of json pointers to remove + paths, err := getJSONPaths(operands[1].Value) + if err != nil { + return err + } + + newObj, err := jsonRemove(operands[0], ast.NewTerm(pathsToObject(paths))) + if err != nil { + return err + } + + if newObj == nil { + return nil + } + + return iter(newObj) +} + +// jsonRemove returns a new term that is the result of walking +// through a and omitting removing any values that are in b but +// have ast.Null values (ie leaf nodes for b). +func jsonRemove(a *ast.Term, b *ast.Term) (*ast.Term, error) { + if b == nil { + // The paths diverged, return a + return a, nil + } + + var bObj ast.Object + switch bValue := b.Value.(type) { + case ast.Object: + bObj = bValue + case ast.Null: + // Means we hit a leaf node on "b", dont add the value for a + return nil, nil + default: + // The paths diverged, return a + return a, nil + } + + switch aValue := a.Value.(type) { + case ast.String, ast.Number, ast.Boolean, ast.Null: + return a, nil + case ast.Object: + newObj := ast.NewObject() + err := aValue.Iter(func(k *ast.Term, v *ast.Term) error { + // recurse and add the diff of sub objects as needed + diffValue, err := jsonRemove(v, bObj.Get(k)) + if err != nil || diffValue == nil { + return err + } + newObj.Insert(k, diffValue) + return nil + }) + if err != nil { + return nil, err + } + return ast.NewTerm(newObj), nil + case ast.Set: + newSet := ast.NewSet() + err := aValue.Iter(func(v *ast.Term) error { + // recurse and add the diff of sub objects as needed + diffValue, err := jsonRemove(v, bObj.Get(v)) + if err != nil || diffValue == nil { + return err + } + newSet.Add(diffValue) + return nil + }) + if err != nil { + return nil, err + } + return ast.NewTerm(newSet), nil + case *ast.Array: + // When indexes are removed we shift left to close empty spots in the array + // as per the JSON patch spec. + newArray := ast.NewArray() + for i := range aValue.Len() { + v := aValue.Elem(i) + // recurse and add the diff of sub objects as needed + // Note: Keys in b will be strings for the index, eg path /a/1/b => {"a": {"1": {"b": null}}} + diffValue, err := jsonRemove(v, bObj.Get(ast.InternedIntegerString(i))) + if err != nil { + return nil, err + } + if diffValue != nil { + newArray = newArray.Append(diffValue) + } + } + return ast.NewTerm(newArray), nil + default: + return nil, fmt.Errorf("invalid value type %T", a) + } +} + +func builtinJSONFilter(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + // Ensure we have the right parameters, expect an object and a string or array/set of strings + obj, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Build a list of filter strings + filters, err := getJSONPaths(operands[1].Value) + if err != nil { + return err + } + + // Actually do the filtering + filterObj := pathsToObject(filters) + r, err := obj.Filter(filterObj) + if err != nil { + return err + } + + return iter(ast.NewTerm(r)) +} + +func getJSONPaths(operand ast.Value) ([]ast.Ref, error) { + var paths []ast.Ref + + switch v := operand.(type) { + case *ast.Array: + for i := range v.Len() { + filter, err := parsePath(v.Elem(i)) + if err != nil { + return nil, err + } + paths = append(paths, filter) + } + case ast.Set: + err := v.Iter(func(f *ast.Term) error { + filter, err := parsePath(f) + if err != nil { + return err + } + paths = append(paths, filter) + return nil + }) + if err != nil { + return nil, err + } + default: + return nil, builtins.NewOperandTypeErr(2, v, "set", "array") + } + + return paths, nil +} + +func parsePath(path *ast.Term) (ast.Ref, error) { + // paths can either be a `/` separated json path or + // an array or set of values + var pathSegments ast.Ref + switch p := path.Value.(type) { + case ast.String: + if p == "" { + return ast.Ref{}, nil + } + parts := strings.Split(strings.TrimLeft(string(p), "/"), "/") + for _, part := range parts { + part = strings.ReplaceAll(strings.ReplaceAll(part, "~1", "/"), "~0", "~") + pathSegments = append(pathSegments, ast.StringTerm(part)) + } + case *ast.Array: + p.Foreach(func(term *ast.Term) { + pathSegments = append(pathSegments, term) + }) + default: + return nil, builtins.NewOperandErr(2, "must be one of {set, array} containing string paths or array of path segments but got %v", ast.ValueName(p)) + } + + return pathSegments, nil +} + +func pathsToObject(paths []ast.Ref) ast.Object { + root := ast.NewObject() + + for _, path := range paths { + node := root + var done bool + + // If the path is an empty JSON path, skip all further processing. + if len(path) == 0 { + done = true + } + + // Otherwise, we should have 1+ path segments to work with. + for i := 0; i < len(path)-1 && !done; i++ { + + k := path[i] + child := node.Get(k) + + if child == nil { + obj := ast.NewObject() + node.Insert(k, ast.NewTerm(obj)) + node = obj + continue + } + + switch v := child.Value.(type) { + case ast.Null: + done = true + case ast.Object: + node = v + default: + panic("unreachable") + } + } + + if !done { + node.Insert(path[len(path)-1], ast.InternedNullTerm) + } + } + + return root +} + +type jsonPatch struct { + op string + path *ast.Term + from *ast.Term + value *ast.Term +} + +func getPatch(o ast.Object) (jsonPatch, error) { + validOps := map[string]struct{}{"add": {}, "remove": {}, "replace": {}, "move": {}, "copy": {}, "test": {}} + var out jsonPatch + var ok bool + getAttribute := func(attr string) (*ast.Term, error) { + if term := o.Get(ast.StringTerm(attr)); term != nil { + return term, nil + } + + return nil, fmt.Errorf("missing '%s' attribute", attr) + } + + opTerm, err := getAttribute("op") + if err != nil { + return out, err + } + op, ok := opTerm.Value.(ast.String) + if !ok { + return out, errors.New("attribute 'op' must be a string") + } + out.op = string(op) + if _, found := validOps[out.op]; !found { + out.op = "" + return out, fmt.Errorf("unrecognized op '%s'", string(op)) + } + + pathTerm, err := getAttribute("path") + if err != nil { + return out, err + } + out.path = pathTerm + + // Only fetch the "from" parameter for move/copy ops. + switch out.op { + case "move", "copy": + fromTerm, err := getAttribute("from") + if err != nil { + return out, err + } + out.from = fromTerm + } + + // Only fetch the "value" parameter for add/replace/test ops. + switch out.op { + case "add", "replace", "test": + valueTerm, err := getAttribute("value") + if err != nil { + return out, err + } + out.value = valueTerm + } + + return out, nil +} + +func applyPatches(source *ast.Term, operations *ast.Array) (*ast.Term, error) { + et := edittree.NewEditTree(source) + for i := range operations.Len() { + object, ok := operations.Elem(i).Value.(ast.Object) + if !ok { + return nil, errors.New("must be an array of JSON-Patch objects, but at least one element is not an object") + } + patch, err := getPatch(object) + if err != nil { + return nil, err + } + path, err := parsePath(patch.path) + if err != nil { + return nil, err + } + + switch patch.op { + case "add": + _, err = et.InsertAtPath(path, patch.value) + if err != nil { + return nil, err + } + case "remove": + _, err = et.DeleteAtPath(path) + if err != nil { + return nil, err + } + case "replace": + _, err = et.DeleteAtPath(path) + if err != nil { + return nil, err + } + _, err = et.InsertAtPath(path, patch.value) + if err != nil { + return nil, err + } + case "move": + from, err := parsePath(patch.from) + if err != nil { + return nil, err + } + chunk, err := et.RenderAtPath(from) + if err != nil { + return nil, err + } + _, err = et.DeleteAtPath(from) + if err != nil { + return nil, err + } + _, err = et.InsertAtPath(path, chunk) + if err != nil { + return nil, err + } + case "copy": + from, err := parsePath(patch.from) + if err != nil { + return nil, err + } + chunk, err := et.RenderAtPath(from) + if err != nil { + return nil, err + } + _, err = et.InsertAtPath(path, chunk) + if err != nil { + return nil, err + } + case "test": + chunk, err := et.RenderAtPath(path) + if err != nil { + return nil, err + } + if !chunk.Equal(patch.value) { + return nil, fmt.Errorf("value from EditTree != patch value.\n\nExpected: %v\n\nFound: %v", patch.value, chunk) + } + } + } + final := et.Render() + // TODO: Nil check here? + return final, nil +} + +func builtinJSONPatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + // JSON patch supports arrays, objects as well as values as the target. + target := ast.NewTerm(operands[0].Value) + + // Expect an array of operations. + operations, err := builtins.ArrayOperand(operands[1].Value, 2) + if err != nil { + return err + } + + patched, err := applyPatches(target, operations) + if err != nil { + return nil + } + return iter(patched) +} + +func init() { + RegisterBuiltinFunc(ast.JSONFilter.Name, builtinJSONFilter) + RegisterBuiltinFunc(ast.JSONRemove.Name, builtinJSONRemove) + RegisterBuiltinFunc(ast.JSONPatch.Name, builtinJSONPatch) +} diff --git a/third_party/opa/v1/topdown/json_bench_test.go b/third_party/opa/v1/topdown/json_bench_test.go new file mode 100644 index 000000000000..9765e51058a7 --- /dev/null +++ b/third_party/opa/v1/topdown/json_bench_test.go @@ -0,0 +1,512 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "fmt" + "math/rand" + "strconv" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" +) + +func BenchmarkJSONPatchAddShallowScalar(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 100, 1000, 10000} + + // Object case + for _, n := range sizes { + source := genTestObject(n) + for _, m := range sizes { + testName := fmt.Sprintf("object-%d-%d", n, m) + // Build dataset right before use: + plArrayObj := make([]*ast.Term, 0, m*2) + // add ops + for i := range m { + plArrayObj = append(plArrayObj, genTestJSONPatchObject("add", ast.StringTerm("/"+strconv.FormatInt(int64(i+n), 10)), nil, ast.IntNumberTerm(i+n))) + } + patchList := ast.NewArray(plArrayObj...) + + b.ResetTimer() + b.Run(testName, func(b *testing.B) { + runJSONPatchBenchmarkTest(ctx, b, source, patchList) + }) + } + } + + // Array case + for _, n := range sizes { + source := genTestArray(n) + for _, m := range sizes { + testName := fmt.Sprintf("array-%d-%d", n, m) + // Build dataset right before use: + plArrayObj := make([]*ast.Term, 0, m*2) + // add ops + for i := range m { + plArrayObj = append(plArrayObj, genTestJSONPatchObject("add", ast.StringTerm("/"+strconv.FormatInt(int64(i+n), 10)), nil, ast.IntNumberTerm(i+n))) + } + patchList := ast.NewArray(plArrayObj...) + + b.ResetTimer() + b.Run(testName, func(b *testing.B) { + runJSONPatchBenchmarkTest(ctx, b, source, patchList) + }) + } + } + // Set case + for _, n := range sizes { + source := genTestSet(n) + for _, m := range sizes { + testName := fmt.Sprintf("set-%d-%d", n, m) + // Build dataset right before use: + plSet := make([]*ast.Term, 0, m*2) + // add ops + for i := range m { + plSet = append(plSet, genTestJSONPatchObject("add", ast.ArrayTerm(ast.IntNumberTerm(i+n)), nil, ast.IntNumberTerm(i+n))) + } + patchList := ast.NewArray(plSet...) + + b.ResetTimer() + b.Run(testName, func(b *testing.B) { + runJSONPatchBenchmarkTest(ctx, b, source, patchList) + }) + } + } +} + +func BenchmarkJSONPatchAddShallowComposite(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 100, 1000, 10000} + + // Object case + for _, n := range sizes { + source := genTestObject(n) + for _, m := range sizes { + testName := fmt.Sprintf("object-%d-%d", n, m) + // Build dataset right before use: + plArrayObj := make([]*ast.Term, 0, m*2) + // add ops + for i := range m { + plArrayObj = append(plArrayObj, genTestJSONPatchObject("add", ast.StringTerm("/"+strconv.FormatInt(int64(i+n), 10)), nil, ast.ArrayTerm(ast.IntNumberTerm(i+n)))) + } + patchList := ast.NewArray(plArrayObj...) + + b.ResetTimer() + b.Run(testName, func(b *testing.B) { + runJSONPatchBenchmarkTest(ctx, b, source, patchList) + }) + } + } + + // Array case + for _, n := range sizes { + source := genTestArray(n) + for _, m := range sizes { + testName := fmt.Sprintf("array-%d-%d", n, m) + // Build dataset right before use: + plArrayObj := make([]*ast.Term, 0, m*2) + // add ops + for i := range m { + plArrayObj = append(plArrayObj, genTestJSONPatchObject("add", ast.StringTerm("/"+strconv.FormatInt(int64(i+n), 10)), nil, ast.ArrayTerm(ast.IntNumberTerm(i+n)))) + } + patchList := ast.NewArray(plArrayObj...) + + b.ResetTimer() + b.Run(testName, func(b *testing.B) { + runJSONPatchBenchmarkTest(ctx, b, source, patchList) + }) + } + } + // Set case + for _, n := range sizes { + source := genTestSet(n) + for _, m := range sizes { + testName := fmt.Sprintf("set-%d-%d", n, m) + // Build dataset right before use: + plSet := make([]*ast.Term, 0, m*2) + // add ops + for i := range m { + plSet = append(plSet, genTestJSONPatchObject("add", ast.ArrayTerm(ast.ArrayTerm(ast.IntNumberTerm(i+n))), nil, ast.ArrayTerm(ast.IntNumberTerm(i+n)))) + } + patchList := ast.NewArray(plSet...) + + b.ResetTimer() + b.Run(testName, func(b *testing.B) { + runJSONPatchBenchmarkTest(ctx, b, source, patchList) + }) + } + } +} + +func BenchmarkJSONPatchAddRemove(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 100, 1000, 10000} + + // Object case + for _, n := range sizes { + source := genTestObject(n) + for _, m := range sizes { + testName := fmt.Sprintf("object-%d-%d", n, m) + // Build dataset right before use: + plArrayObj := make([]*ast.Term, 0, m*2) + // add ops + for i := range m { + plArrayObj = append(plArrayObj, genTestJSONPatchObject("add", ast.StringTerm("/"+strconv.FormatInt(int64(i+n), 10)), nil, ast.IntNumberTerm(i+n))) + } + // remove ops + for i := m - 1; i >= 0; i-- { + plArrayObj = append(plArrayObj, genTestJSONPatchObject("remove", ast.StringTerm("/"+strconv.FormatInt(int64(i+n), 10)), nil, nil)) + } + patchList := ast.NewArray(plArrayObj...) + + b.ResetTimer() + b.Run(testName, func(b *testing.B) { + runJSONPatchBenchmarkTest(ctx, b, source, patchList) + }) + } + } + + // Array case + for _, n := range sizes { + source := genTestArray(n) + for _, m := range sizes { + testName := fmt.Sprintf("array-%d-%d", n, m) + // Build dataset right before use: + plArrayObj := make([]*ast.Term, 0, m*2) + // add ops + for i := range m { + plArrayObj = append(plArrayObj, genTestJSONPatchObject("add", ast.StringTerm("/"+strconv.FormatInt(int64(i+n), 10)), nil, ast.IntNumberTerm(i+n))) + } + // remove ops + for i := m - 1; i >= 0; i-- { + plArrayObj = append(plArrayObj, genTestJSONPatchObject("remove", ast.StringTerm("/"+strconv.FormatInt(int64(i+n), 10)), nil, nil)) + } + patchList := ast.NewArray(plArrayObj...) + + b.ResetTimer() + b.Run(testName, func(b *testing.B) { + runJSONPatchBenchmarkTest(ctx, b, source, patchList) + }) + } + } + + // Set case + for _, n := range sizes { + source := genTestSet(n) + for _, m := range sizes { + testName := fmt.Sprintf("set-%d-%d", n, m) + // Build dataset right before use: + plSet := make([]*ast.Term, 0, m*2) + // add ops + for i := range m { + plSet = append(plSet, genTestJSONPatchObject("add", ast.ArrayTerm(ast.IntNumberTerm(i+n)), nil, ast.IntNumberTerm(i+n))) + } + // remove ops + for i := m - 1; i >= 0; i-- { + plSet = append(plSet, genTestJSONPatchObject("remove", ast.ArrayTerm(ast.IntNumberTerm(i+n)), nil, nil)) + } + patchList := ast.NewArray(plSet...) + + b.ResetTimer() + b.Run(testName, func(b *testing.B) { + runJSONPatchBenchmarkTest(ctx, b, source, patchList) + }) + } + } +} + +func genTestJSONPatchObject(op string, path, from, value *ast.Term) *ast.Term { + patchObj := ast.NewObject( + [2]*ast.Term{ast.StringTerm("op"), ast.StringTerm(op)}, + [2]*ast.Term{ast.StringTerm("path"), path}, + ) + if from != nil { + patchObj.Insert(ast.StringTerm("from"), from) + } + if value != nil { + patchObj.Insert(ast.StringTerm("value"), value) + } + return ast.NewTerm(patchObj) +} + +func genTestObject(width int) ast.Value { + out := ast.NewObject() + for i := range width { + out.Insert(ast.IntNumberTerm(i), ast.IntNumberTerm(i)) + } + return out +} + +func genTestArray(width int) ast.Value { + out := ast.NewArray() + for i := range width { + out = out.Append(ast.IntNumberTerm(i)) + } + return out +} + +func genTestSet(width int) ast.Value { + out := ast.NewSet() + for i := range width { + out.Add(ast.IntNumberTerm(i)) + } + return out +} + +// For the purposes of addressing the original Github issue (#4409), a +// fairly shallow object with many keys ought to do the trick. +func gen3LayerObject(l1Keys, l2Keys, l3Keys int) ast.Value { + obj := ast.NewObject() + for i := range l1Keys { + l2Obj := ast.NewObject() + for j := range l2Keys { + l3Obj := ast.NewObject() + for k := range l3Keys { + l3Obj.Insert(ast.StringTerm(strconv.Itoa(k)), ast.InternedTerm(true)) + } + l2Obj.Insert(ast.StringTerm(strconv.Itoa(j)), ast.NewTerm(l3Obj)) + } + obj.Insert(ast.StringTerm(strconv.Itoa(i)), ast.NewTerm(l2Obj)) + } + return obj +} + +// Generates a list of paths for JSON operations. N keys per level, M levels. P patches. +// TODO: Generate non-conflicting paths. +func genRandom3LayerObjectJSONPatchListData(l1Keys, l2Keys, l3Keys, p int) ast.Value { + patchList := make([]*ast.Term, p) + numKeys := []int{l1Keys, l2Keys, l3Keys} + for i := range p { + patchObj := ast.NewObject( + [2]*ast.Term{ast.StringTerm("op"), ast.StringTerm("replace")}, + [2]*ast.Term{ast.StringTerm("value"), ast.IntNumberTerm(2)}, + ) + // Random path depth. + depth := rand.Intn(3) + 1 // (max - min) + min method of getting a random range. + + // Random values for each path segment. + segments := []string{} + for j := range depth { + pathSegment := strconv.FormatInt(int64(rand.Intn(numKeys[j])), 10) + segments = append(segments, "/", pathSegment) + } + path := strings.Join(segments, "") + patchObj.Insert(ast.StringTerm("path"), ast.StringTerm(path)) + patchList[i] = ast.NewTerm(patchObj) + } + return ast.NewArray(patchList...) +} + +func BenchmarkJSONPatchReplace(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 100, 1000} + + // Pre-generate the test datasets/patches. + testdata := map[string][2]ast.Value{} + for _, n := range sizes { + for _, m := range sizes { + testObj := gen3LayerObject(n, m, 10) + for _, p := range sizes { + testdata[fmt.Sprintf("%dx%dx10-%dp", n, m, p)] = [2]ast.Value{testObj, genRandom3LayerObjectJSONPatchListData(n, m, 10, p)} + } + } + } + + for _, n := range sizes { + for _, m := range sizes { + for _, p := range sizes { + testName := fmt.Sprintf("%dx%dx10-%dp", n, m, p) + b.Run(testName, func(b *testing.B) { + store := inmem.NewFromObject(map[string]any{ + "obj": testdata[testName][0], + "patches": testdata[testName][1], + }) + + module := `package test + + result := json.patch(data.obj, data.patches)` + + query := ast.MustParseBody("data.test.result") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + + q := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn) + + _, err := q.Run(ctx) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + b.Fatal(err) + } + } + }) + } + } + } +} + +func BenchmarkJSONPatchPathologicalNestedAddChainObject(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 100, 500, 1000, 5000, 10000} + // Pre-generate the test datasets/patches. + testdata := map[string]ast.Value{} + for _, n := range sizes { + patchList := make([]*ast.Term, n) + path := "" + for i := range n { + patchObj := ast.NewObject( + [2]*ast.Term{ast.InternedTerm("op"), ast.InternedTerm("add")}, + [2]*ast.Term{ast.InternedTerm("value"), ast.ObjectTerm()}, + ) + + path += "/a" + + patchObj.Insert(ast.InternedTerm("path"), ast.InternedTerm(path)) + patchList[i] = ast.NewTerm(patchObj) + } + testdata[strconv.Itoa(n)] = ast.NewArray(patchList...) + } + + for _, n := range sizes { + testName := strconv.Itoa(n) + b.Run(testName, func(b *testing.B) { + runJSONPatchBenchmarkTest(ctx, b, ast.NewObject(), testdata[testName]) + }) + } +} + +func BenchmarkJSONPatchPathologicalNestedAddChainArray(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 100, 500, 1000, 5000, 10000} + // Pre-generate the test datasets/patches. + testdata := map[string]ast.Value{} + for _, n := range sizes { + patchList := make([]*ast.Term, n) + path := "" + for i := range n { + patchObj := ast.NewObject( + [2]*ast.Term{ast.InternedTerm("op"), ast.InternedTerm("add")}, + [2]*ast.Term{ast.InternedTerm("value"), ast.ArrayTerm()}, + ) + + path += "/0" + + patchObj.Insert(ast.InternedTerm("path"), ast.StringTerm(path)) + patchList[i] = ast.NewTerm(patchObj) + } + testdata[strconv.Itoa(n)] = ast.NewArray(patchList...) + } + + for _, n := range sizes { + testName := strconv.Itoa(n) + b.Run(testName, func(b *testing.B) { + runJSONPatchBenchmarkTest(ctx, b, ast.NewArray(), testdata[testName]) + }) + } +} + +// This one is tricky, because sets used content-based addressing. +// That means our sets for the path have to be recursively constructed! +func BenchmarkJSONPatchPathologicalNestedAddChainSet(b *testing.B) { + ctx := context.Background() + sizes := []int{10, 100, 500, 1000} + + // Pre-generate the test datasets/patches. + testdata := map[string]ast.Value{} + for _, n := range sizes { + patchList := make([]*ast.Term, n) + for i := range n { + patchObj := ast.NewObject( + [2]*ast.Term{ast.InternedTerm("op"), ast.InternedTerm("add")}, + ) + value := ast.SetTerm(ast.InternedTerm("a")) + constructedPath := ast.NewArray(ast.SetTerm(ast.InternedTerm("a"))) + for range i { + constructedPath = constructedPath.Append(value) + value = ast.SetTerm(ast.InternedTerm("a"), value) + } + + // Reverse the ast.Array slice. + path := ast.NewArray() + pathLength := constructedPath.Len() - 1 + for j := range constructedPath.Len() { + path = path.Append(constructedPath.Elem(pathLength - j)) + } + + patchObj.Insert(ast.InternedTerm("value"), ast.SetTerm(ast.InternedTerm("a"))) + patchObj.Insert(ast.InternedTerm("path"), ast.NewTerm(path)) + patchList[i] = ast.NewTerm(patchObj) + } + testdata[strconv.Itoa(n)] = ast.NewArray(patchList...) + } + + for _, n := range sizes { + testName := strconv.Itoa(n) + b.Run(testName, func(b *testing.B) { + runJSONPatchBenchmarkTest(ctx, b, ast.NewSet(ast.StringTerm("a")), testdata[testName]) + }) + } +} + +func runJSONPatchBenchmarkTest(ctx context.Context, b *testing.B, source ast.Value, patches ast.Value) { + store := inmem.NewFromObject(map[string]any{ + "source": source, + "patches": patches, + }) + + module := `package test + + result := json.patch(data.source, data.patches)` + + query := ast.MustParseBody("data.test.result") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + _, err := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + Run(ctx) + + return err + }) + + if err != nil { + b.Fatal(err) + } + } + +} diff --git a/third_party/opa/v1/topdown/json_test.go b/third_party/opa/v1/topdown/json_test.go new file mode 100644 index 000000000000..253dfc2b0004 --- /dev/null +++ b/third_party/opa/v1/topdown/json_test.go @@ -0,0 +1,117 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestFiltersToObject(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + filters []string + expected string + }{ + { + note: "empty path", + filters: []string{`""`}, + expected: `{}`, + }, + { + note: "base", + filters: []string{`"a/b/c"`}, + expected: `{"a": {"b": {"c": null}}}`, + }, + { + note: "root prefixed", + filters: []string{`"a/b/c"`}, + expected: `{"a": {"b": {"c": null}}}`, + }, + { + note: "trailing slash", + filters: []string{`"a/b/c"`}, + expected: `{"a": {"b": {"c": null}}}`, + }, + { + note: "different roots", + filters: []string{`"a/b/c"`, `"d/e/f"`}, + expected: `{"a": {"b": {"c": null}}, "d": {"e": {"f": null}}}`, + }, + { + note: "shared root", + filters: []string{`"a/b/c"`, `"a/b/d"`}, + expected: `{"a": {"b": {"c": null, "d": null}}}`, + }, + { + note: "multiple shares at different points", + filters: []string{`"a/b/c"`, `"a/b/d"`, `"a/e/f"`}, + expected: `{"a": {"b": {"c": null, "d": null}, "e": {"f": null}}}`, + }, + { + note: "conflict with one ordering", + filters: []string{`"a"`, `"a/b"`}, + expected: `{"a": null}`, + }, + { + note: "conflict with reverse ordering", + filters: []string{`"a/b"`, `"a"`}, + expected: `{"a": null}`, + }, + { + note: "arrays", + filters: []string{`"a/1/c"`, `"a/1/b"`}, + expected: `{"a": {"1": {"c": null, "b": null}}}`, + }, + { + note: "non string keys", + filters: []string{`[[1], {2}]`, `"a/1/b"`}, + expected: `{"a": {"1": {"b": null}}, [1]: {{2}: null}}`, + }, + { + note: "escaped tilde", + filters: []string{`"a/~0b~0/c~0"`}, + expected: `{"a": {"~b~": {"c~": null}}}`, + }, + { + note: "escaped slash", + filters: []string{`"a/~1b~1c/d~1"`}, + expected: `{"a": {"/b/c": {"d/": null}}}`, + }, + { + note: "mixed escapes", + filters: []string{`"a/~0b~1c/d~1~0"`}, + expected: `{"a": {"~b/c": {"d/~": null}}}`, + }, + { + note: "empty strings mixed with normal paths", + filters: []string{`"a/b/c"`, `""`, `"a/b/d"`, `"a/e/f"`, `""`}, + expected: `{"a": {"b": {"c": null, "d": null}, "e": {"f": null}}}`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + var paths []ast.Ref + for _, path := range tc.filters { + parsedPath, err := parsePath(ast.MustParseTerm(path)) + if err != nil { + t.Errorf("unexpected error parsing path %s: %s", path, err) + } + paths = append(paths, parsedPath) + } + actual := pathsToObject(paths) + expected := ast.MustParseTerm(tc.expected) + if actual.Compare(expected.Value) != 0 { + t.Errorf("Unexpected object from filters:\n\nExpected:\n\t%s\n\nActual:\n\t%s\n\n", expected.Value.String(), actual.String()) + } + }) + } +} diff --git a/third_party/opa/v1/topdown/jsonschema.go b/third_party/opa/v1/topdown/jsonschema.go new file mode 100644 index 000000000000..699f1d0d9952 --- /dev/null +++ b/third_party/opa/v1/topdown/jsonschema.go @@ -0,0 +1,130 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "encoding/json" + "errors" + + "github.com/open-policy-agent/opa/internal/gojsonschema" + "github.com/open-policy-agent/opa/v1/ast" +) + +// astValueToJSONSchemaLoader converts a value to JSON Loader. +// Value can be ast.String or ast.Object. +func astValueToJSONSchemaLoader(value ast.Value) (gojsonschema.JSONLoader, error) { + var loader gojsonschema.JSONLoader + var err error + + // ast.Value type selector. + switch x := value.(type) { + case ast.String: + // In case of string pass it as is as a raw JSON string. + // Make pre-check that it's a valid JSON at all because gojsonschema won't do that. + if !json.Valid([]byte(x)) { + return nil, errors.New("invalid JSON string") + } + loader = gojsonschema.NewStringLoader(string(x)) + case ast.Object: + // In case of object serialize it to JSON representation. + var data any + data, err = ast.JSON(value) + if err != nil { + return nil, err + } + loader = gojsonschema.NewGoLoader(data) + default: + // Any other cases will produce an error. + return nil, errors.New("wrong type, expected string or object") + } + + return loader, nil +} + +func newResultTerm(valid bool, data *ast.Term) *ast.Term { + return ast.ArrayTerm(ast.InternedTerm(valid), data) +} + +// builtinJSONSchemaVerify accepts 1 argument which can be string or object and checks if it is valid JSON schema. +// Returns array [false, ] with error string at index 1, or [true, ""] with empty string at index 1 otherwise. +func builtinJSONSchemaVerify(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + // Take first argument and make JSON Loader from it. + loader, err := astValueToJSONSchemaLoader(operands[0].Value) + if err != nil { + return iter(newResultTerm(false, ast.StringTerm("jsonschema: "+err.Error()))) + } + + // Check that schema is correct and parses without errors. + if _, err = gojsonschema.NewSchema(loader); err != nil { + return iter(newResultTerm(false, ast.StringTerm("jsonschema: "+err.Error()))) + } + + return iter(newResultTerm(true, ast.InternedNullTerm)) +} + +// builtinJSONMatchSchema accepts 2 arguments both can be string or object and verifies if the document matches the JSON schema. +// Returns an array where first element is a boolean indicating a successful match, and the second is an array of errors that is empty on success and populated on failure. +// In case of internal error returns empty array. +func builtinJSONMatchSchema(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + var schema *gojsonschema.Schema + + if bctx.InterQueryBuiltinValueCache != nil { + if val, ok := bctx.InterQueryBuiltinValueCache.Get(operands[1].Value); ok { + if s, isSchema := val.(*gojsonschema.Schema); isSchema { + schema = s + } + } + } + + // Take first argument and make JSON Loader from it. + // This is a JSON document made from Rego JSON string or object. + documentLoader, err := astValueToJSONSchemaLoader(operands[0].Value) + if err != nil { + return err + } + + if schema == nil { + // Take second argument and make JSON Loader from it. + // This is a JSON schema made from Rego JSON string or object. + schemaLoader, err := astValueToJSONSchemaLoader(operands[1].Value) + if err != nil { + return err + } + + schema, err = gojsonschema.NewSchema(schemaLoader) + if err != nil { + return err + } + + if bctx.InterQueryBuiltinValueCache != nil { + bctx.InterQueryBuiltinValueCache.Insert(operands[1].Value, schema) + } + } + + // Use schema to validate document. + result, err := schema.Validate(documentLoader) + if err != nil { + return err + } + + // In case of validation errors produce Rego array of objects to describe the errors. + arr := ast.NewArray() + for _, re := range result.Errors() { + o := ast.NewObject( + [...]*ast.Term{ast.StringTerm("error"), ast.StringTerm(re.String())}, + [...]*ast.Term{ast.StringTerm("type"), ast.StringTerm(re.Type())}, + [...]*ast.Term{ast.StringTerm("field"), ast.StringTerm(re.Field())}, + [...]*ast.Term{ast.StringTerm("desc"), ast.StringTerm(re.Description())}, + ) + arr = arr.Append(ast.NewTerm(o)) + } + + return iter(newResultTerm(result.Valid(), ast.NewTerm(arr))) +} + +func init() { + RegisterBuiltinFunc(ast.JSONSchemaVerify.Name, builtinJSONSchemaVerify) + RegisterBuiltinFunc(ast.JSONMatchSchema.Name, builtinJSONMatchSchema) +} diff --git a/third_party/opa/v1/topdown/jsonschema_test.go b/third_party/opa/v1/topdown/jsonschema_test.go new file mode 100644 index 000000000000..8b94e284817a --- /dev/null +++ b/third_party/opa/v1/topdown/jsonschema_test.go @@ -0,0 +1,338 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "testing" + + "github.com/open-policy-agent/opa/v1/topdown/cache" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestAstValueToJSONSchemaLoader(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + schema ast.Value + valid bool + }{ + { + note: "string empty json object", + schema: ast.String(`{}`), + valid: true, + }, + { + note: "string broken json", + schema: ast.String(`{ "properties": { id: {} } }`), + valid: false, + }, + { + note: "string simple schema", + schema: ast.String(` + { + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + } + `), + valid: true, + }, + { + note: "object empty", + schema: ast.NewObject(), + valid: true, + }, + { + note: "object simple schema", + schema: ast.NewObject( + [...]*ast.Term{ + ast.StringTerm("properties"), + ast.NewTerm(ast.NewObject( + [...]*ast.Term{ + ast.StringTerm("id"), + ast.NewTerm(ast.NewObject( + [...]*ast.Term{ + ast.StringTerm("type"), + ast.StringTerm("integer"), + }, + )), + }, + )), + }, + ), + valid: true, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + _, err := astValueToJSONSchemaLoader(tc.schema) + if tc.valid && err != nil { + t.Errorf("Unexpected JSON Schema validation result, expected valid = true, got = false: %s", err) + return + } + if !tc.valid && err == nil { + t.Errorf("Unexpected JSON Schema validation result, expected valid = false, got = true") + return + } + }) + } +} + +func TestBuiltinJSONSchemaVerify(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + schema ast.Value + result ast.Value + err bool + }{ + { + note: "string empty schema", + schema: ast.String(`{}`), + result: ast.NewArray(ast.BooleanTerm(true), ast.NullTerm()), + err: false, + }, + { + note: "string broken JSON", + schema: ast.String(`{ "a": "`), + result: ast.NewArray(ast.BooleanTerm(false), ast.StringTerm("jsonschema: invalid JSON string")), + err: false, + }, + { + note: "string simple schema", + schema: ast.String(` + { + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + } + `), + result: ast.NewArray(ast.BooleanTerm(true), ast.NullTerm()), + err: false, + }, + { + note: "string broken schema", + schema: ast.String(` + { + "properties": { + "id": { + "type": "UNKNOWN" + } + }, + "required": ["id"] + } + `), + result: ast.NewArray(ast.BooleanTerm(false), ast.StringTerm("jsonschema: has a primitive type that is NOT VALID -- given: /UNKNOWN/ Expected valid values are:[array boolean integer number null object string]")), + err: false, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + result := ast.NullTerm().Value + err := builtinJSONSchemaVerify( + BuiltinContext{}, + []*ast.Term{ast.NewTerm(tc.schema)}, + func(term *ast.Term) error { + result = term.Value + return nil + }, + ) + + if tc.err && err == nil { + t.Errorf("Unexpected schema validation, expected error, got nil") + return + } + if !tc.err && err != nil { + t.Errorf("Unexpected schema validation, expected nil, got error: %s", err) + return + } + if tc.result.Compare(result) != 0 { + t.Errorf("Unexpected schema validation, expected result %s, got result %s", tc.result.String(), result.String()) + return + } + }) + } +} + +func TestBuiltinJSONMatchSchema(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + document ast.Value + schema ast.Value + result ast.Value + err bool + }{ + { + note: "string empty document, empty schema", + document: ast.String(`{}`), + schema: ast.String(`{}`), + result: ast.NewArray(ast.BooleanTerm(true), ast.ArrayTerm()), + err: false, + }, + { + note: "string empty document, broken schema", + document: ast.String(`{}`), + schema: ast.String(`{ "a": "`), + result: ast.NullTerm().Value, + err: true, + }, + { + note: "string broken document, empty schema", + document: ast.String(`{ "a": "`), + schema: ast.String(`{}`), + result: ast.NullTerm().Value, + err: true, + }, + { + note: "string correct document, simple schema", + document: ast.String(`{ "id": 5 }`), + schema: ast.String(` + { + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + } + `), + result: ast.NewArray(ast.BooleanTerm(true), ast.ArrayTerm()), + err: false, + }, + { + note: "string correct document, invalid schema", + document: ast.String(`{ "id": 5 }`), + schema: ast.String(` + { + "properties": { + "id": { + "type": "UNKNOWN" + } + }, + "required": ["id"] + } + `), + result: ast.NullTerm().Value, + err: true, + }, + { + note: "string invalid document, correct schema", + document: ast.String(`{ "id": "test" }`), + schema: ast.String(` + { + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + } + `), + result: ast.NewArray(ast.BooleanTerm(false), + ast.ArrayTerm(ast.NewTerm(ast.NewObject( + [...]*ast.Term{ast.StringTerm("error"), ast.StringTerm("id: Invalid type. Expected: integer, given: string")}, + [...]*ast.Term{ast.StringTerm("type"), ast.StringTerm("invalid_type")}, + [...]*ast.Term{ast.StringTerm("field"), ast.StringTerm("id")}, + [...]*ast.Term{ast.StringTerm("desc"), ast.StringTerm("Invalid type. Expected: integer, given: string")}, + )))), + err: false, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + result := ast.NullTerm().Value + err := builtinJSONMatchSchema( + BuiltinContext{}, + []*ast.Term{ast.NewTerm(tc.document), ast.NewTerm(tc.schema)}, + func(term *ast.Term) error { + result = term.Value + return nil + }, + ) + + if tc.err && err == nil { + t.Errorf("Unexpected schema validation, expected error, got nil") + return + } + if !tc.err && err != nil { + t.Errorf("Unexpected schema validation, expected nil, got error: %s", err) + return + } + if tc.result.Compare(result) != 0 { + t.Errorf("Unexpected schema validation, expected result %s, got result %s", tc.result.String(), result.String()) + return + } + }) + } +} + +func TestBuiltinJSONMatchSchemaCache(t *testing.T) { + t.Parallel() + + schema := ast.String(` +{ + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] +} +`) + + valueCache := cache.NewInterQueryValueCache(context.Background(), nil) + document := ast.String(`{ "id": 5 }`) + + var result ast.Value + err := builtinJSONMatchSchema( + BuiltinContext{ + InterQueryBuiltinValueCache: valueCache, + }, + []*ast.Term{ast.NewTerm(document), ast.NewTerm(schema)}, + func(term *ast.Term) error { + result = term.Value + return nil + }, + ) + if err != nil { + t.Fatalf("Unexpected schema validation error: %s", err) + } + + arr, ok := result.(*ast.Array) + if !ok { + t.Fatalf("Unexpected result type, expected array, got %T", result) + } + + expected := ast.NewArray(ast.BooleanTerm(true), ast.ArrayTerm()) + + if arr.Compare(expected) != 0 { + t.Fatalf("Unexpected result, expected %s, got %s", expected, arr) + } + + if _, found := valueCache.Get(schema); !found { + t.Fatalf("Expected document to be cached") + } +} diff --git a/third_party/opa/v1/topdown/lineage/lineage.go b/third_party/opa/v1/topdown/lineage/lineage.go new file mode 100644 index 000000000000..26e66eb5959b --- /dev/null +++ b/third_party/opa/v1/topdown/lineage/lineage.go @@ -0,0 +1,84 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package lineage + +import ( + "github.com/open-policy-agent/opa/v1/topdown" +) + +// Debug contains everything in the log. +func Debug(trace []*topdown.Event) []*topdown.Event { + return trace +} + +// Full returns a filtered trace that contains everything except Unify ops +func Full(trace []*topdown.Event) (result []*topdown.Event) { + // Do not use Filter since this event will only occur at the leaf positions. + for _, event := range trace { + if event.Op != topdown.UnifyOp { + result = append(result, event) + } + } + return +} + +// Notes returns a filtered trace that contains Note events and context to +// understand where the Note was emitted. +func Notes(trace []*topdown.Event) []*topdown.Event { + return Filter(trace, func(event *topdown.Event) bool { + return event.Op == topdown.NoteOp + }) +} + +// Fails returns a filtered trace that contains Fail events and context to +// understand where the Fail occurred. +func Fails(trace []*topdown.Event) []*topdown.Event { + return Filter(trace, func(event *topdown.Event) bool { + return event.Op == topdown.FailOp + }) +} + +// Filter will filter a given trace using the specified filter function. The +// filtering function should return true for events that should be kept, false +// for events that should be filtered out. +func Filter(trace []*topdown.Event, filter func(*topdown.Event) bool) (result []*topdown.Event) { + + qids := map[uint64]*topdown.Event{} + + for _, event := range trace { + + if filter(event) { + // Path will end with the Note event. + path := []*topdown.Event{event} + + // Construct path of recorded Enter/Redo events that lead to the + // Note event. The path is constructed in reverse order by iterating + // backwards through the Enter/Redo events from the Note event. + curr := qids[event.QueryID] + var prev *topdown.Event + + for curr != nil && curr != prev { + path = append(path, curr) + prev = curr + curr = qids[curr.ParentID] + } + + // Add the path to the result, reversing it in the process. + for i := len(path) - 1; i >= 0; i-- { + result = append(result, path[i]) + } + + qids = map[uint64]*topdown.Event{} + } + + if event.Op == topdown.EnterOp || event.Op == topdown.RedoOp { + if event.HasRule() || event.HasBody() { + qids[event.QueryID] = event + } + } + } + + return result +} diff --git a/third_party/opa/v1/topdown/lineage/lineage_test.go b/third_party/opa/v1/topdown/lineage/lineage_test.go new file mode 100644 index 000000000000..2fcadd8c1813 --- /dev/null +++ b/third_party/opa/v1/topdown/lineage/lineage_test.go @@ -0,0 +1,201 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package lineage + +import ( + "bytes" + "context" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown" +) + +func TestFilter(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + module string + exp string + }{ + { + note: "lineage", + module: `package test + import rego.v1 + + p if { q } + q if { r } + r if { trace("R") }`, + exp: ` +Enter data.test.p = x +| Enter data.test.p +| | Enter data.test.q +| | | Enter data.test.r +| | | | Note "R"`, + }, + { + note: "conjunction", + module: `package test + import rego.v1 + + p if { trace("P1"); trace("P2") }`, + exp: ` +Enter data.test.p = x +| Enter data.test.p +| | Note "P1" +| | Note "P2"`, + }, + { + note: "conjunction (multiple enters)", + module: `package test + import rego.v1 + + p if { q; r } + q if { trace("Q") } + r if { trace("Q") } + `, + exp: ` +Enter data.test.p = x +| Enter data.test.p +| | Enter data.test.q +| | | Note "Q" +| | Enter data.test.r +| | | Note "Q"`, + }, + { + note: "disjunction", + module: `package test + import rego.v1 + + p = x if { x := true; trace("P1") } + p = x if { x := true; false } + p = x if { x := true; trace("P2") } + `, + exp: ` +Enter data.test.p = x +| Enter data.test.p +| | Note "P1" +Redo data.test.p = x +| Enter data.test.p +| | Note "P2"`, + }, + { + note: "disjunction (failure)", + module: `package test + import rego.v1 + + p = x if { x := true; trace("P1") } + p = x if { x := true; trace("P2"); false } + p = x if { x := true; trace("P3") } + `, + exp: ` +Enter data.test.p = x +| Enter data.test.p +| | Note "P1" +Redo data.test.p = x +| Enter data.test.p +| | Note "P2" +| Enter data.test.p +| | Note "P3"`, + }, + { + note: "disjunction (iteration)", + module: `package test + import rego.v1 + + q contains 1 + q contains 2 + p if { q[x]; trace(sprintf("x=%d", [x])) }`, + exp: ` +Enter data.test.p = x +| Enter data.test.p +| | Note "x=1" +`, + }, + { + note: "parent child", + module: `package test + import rego.v1 + + p if { trace("P"); q } + q if { trace("Q") }`, + exp: ` +Enter data.test.p = x +| Enter data.test.p +| | Note "P" +| | Enter data.test.q +| | | Note "Q"`, + }, + { + note: "negation", + module: `package test + import rego.v1 + + p if { not q } + q = false if { trace("Q") }`, + exp: ` +Enter data.test.p = x +| Enter data.test.p +| | Enter data.test.q +| | | Enter data.test.q +| | | | Note "Q"`, + }, + { + note: "fail", + module: `package test + import rego.v1 + + p if { not q } + q if { trace("P"); 1 = 2 }`, + exp: ` +Enter data.test.p = x +| Enter data.test.p +| | Enter data.test.q +| | | Enter data.test.q +| | | | Note "P"`, + }, + { + note: "comprehensions", + module: `package test + import rego.v1 + + p if { [true | true; trace("X")] }`, + exp: ` +Enter data.test.p = x +| Enter data.test.p +| | Enter true; trace("X") +| | | Note "X"`, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + buf := topdown.NewBufferTracer() + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": tc.module, + }) + query := topdown.NewQuery(ast.MustParseBody("data.test.p = x")).WithCompiler(compiler).WithTracer(buf) + rs, err := query.Run(context.TODO()) + if err != nil { + t.Fatal(err) + } else if len(rs) != 1 || !rs[0][ast.Var("x")].Equal(ast.BooleanTerm(true)) { + t.Fatalf("Unexpected result: %v", rs) + } + + filtered := Notes(*buf) + + buffer := bytes.NewBuffer(nil) + topdown.PrettyTrace(buffer, filtered) + + if strings.TrimSpace(buffer.String()) != strings.TrimSpace(tc.exp) { + t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", tc.exp, buffer.String()) + } + }) + } +} diff --git a/third_party/opa/v1/topdown/net.go b/third_party/opa/v1/topdown/net.go new file mode 100644 index 000000000000..17ed77984492 --- /dev/null +++ b/third_party/opa/v1/topdown/net.go @@ -0,0 +1,64 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "net" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +type lookupIPAddrCacheKey string + +// resolv is the same as net.DefaultResolver -- this is for mocking it out in tests +var resolv = &net.Resolver{} + +func builtinLookupIPAddr(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + a, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + name := string(a) + + err = verifyHost(bctx, name) + if err != nil { + return err + } + + key := lookupIPAddrCacheKey(name) + if val, ok := bctx.Cache.Get(key); ok { + return iter(val.(*ast.Term)) + } + + addrs, err := resolv.LookupIPAddr(bctx.Context, name) + if err != nil { + // NOTE(sr): We can't do better than this right now, see https://github.com/golang/go/issues/36208 + if strings.Contains(err.Error(), "operation was canceled") || strings.Contains(err.Error(), "i/o timeout") { + return Halt{ + Err: &Error{ + Code: CancelErr, + Message: ast.NetLookupIPAddr.Name + ": " + err.Error(), + Location: bctx.Location, + }, + } + } + return err + } + + ret := ast.NewSet() + for _, a := range addrs { + ret.Add(ast.StringTerm(a.String())) + + } + t := ast.NewTerm(ret) + bctx.Cache.Put(key, t) + return iter(t) +} + +func init() { + RegisterBuiltinFunc(ast.NetLookupIPAddr.Name, builtinLookupIPAddr) +} diff --git a/third_party/opa/v1/topdown/net_test.go b/third_party/opa/v1/topdown/net_test.go new file mode 100644 index 000000000000..57ac7d72f46b --- /dev/null +++ b/third_party/opa/v1/topdown/net_test.go @@ -0,0 +1,222 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build !race +// +build !race + +package topdown + +import ( + "context" + "errors" + "fmt" + "testing" + "time" + + "github.com/foxcpp/go-mockdns" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +// TestNetLookupIPAddr replaces the resolver used by builtinLookupIPAddr. +// Due to some intricacies of the net/LookupIP internals, it seems impossible +// to do that in a way that passes the race detector. +func TestNetLookupIPAddr(t *testing.T) { + t.Parallel() + + srv, err := mockdns.NewServerWithLogger(map[string]mockdns.Zone{ + "v4.org.": { + A: []string{"1.2.3.4"}, + }, + "v6.org.": { + AAAA: []string{"1:2:3::4"}, + }, + "v4-v6.org.": { + A: []string{"1.2.3.4"}, + AAAA: []string{"1:2:3::4"}, + }, + "error.org.": { + Err: errors.New("OH NO"), + }, + }, sink{}, true) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { srv.Close() }) + + srvFail, err := mockdns.NewServerWithLogger(map[string]mockdns.Zone{}, sink{}, true) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { srvFail.Close() }) + t.Cleanup(func() { mockdns.UnpatchNet(resolv) }) + + for addr, exp := range map[string]ast.Set{ + "v4.org": ast.NewSet(ast.StringTerm("1.2.3.4")), + "v6.org": ast.NewSet(ast.StringTerm("1:2:3::4")), + "v4-v6.org": ast.NewSet(ast.StringTerm("1.2.3.4"), ast.StringTerm("1:2:3::4")), + } { + t.Run(addr, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + bctx := BuiltinContext{ + Context: ctx, + Cache: make(builtins.Cache), + } + srv.PatchNet(resolv) + err := builtinLookupIPAddr(bctx, []*ast.Term{ast.StringTerm(addr)}, func(act *ast.Term) error { + if exp.Compare(act.Value) != 0 { + t.Errorf("expected %v, got %v", exp, act) + } + return nil + }) + if err != nil { + t.Error(err) + } + + // check cache put + act, ok := bctx.Cache.Get(lookupIPAddrCacheKey(addr)) + if !ok { + t.Fatal("result not put into cache") + } + if exp.Compare(act.(*ast.Term).Value) != 0 { + t.Errorf("cache: expected %v, got %v", exp, act) + } + + // exercise cache hit + srvFail.PatchNet(resolv) + err = builtinLookupIPAddr(bctx, []*ast.Term{ast.StringTerm(addr)}, func(act *ast.Term) error { + if exp.Compare(act.Value) != 0 { + t.Errorf("expected %v, got %v", exp, act) + } + return nil + }) + if err != nil { + t.Error(err) + } + }) + } + + for _, addr := range []string{"error.org", "nosuch.org"} { + t.Run(addr, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + bctx := BuiltinContext{ + Context: ctx, + Cache: make(builtins.Cache), + } + srv.PatchNet(resolv) + err := builtinLookupIPAddr(bctx, []*ast.Term{ast.StringTerm(addr)}, func(*ast.Term) error { + t.Fatal("expected not to be called") + return nil + }) + if err == nil { + t.Error("expected error") + } + if testing.Verbose() { + t.Log(err) + } + }) + } + + cancelled := func() (context.Context, func()) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + return ctx, cancel + } + timedOut := func() (context.Context, func()) { + return context.WithTimeout(context.Background(), time.Nanosecond) + } + + for name, ctx := range map[string]func() (context.Context, func()){ + "cancelled": cancelled, + "timed out": timedOut, + } { + t.Run(name, func(t *testing.T) { + ctx, cancel := ctx() + defer cancel() + bctx := BuiltinContext{ + Context: ctx, + Cache: make(builtins.Cache), + } + srv.PatchNet(resolv) + err := builtinLookupIPAddr(bctx, []*ast.Term{ast.StringTerm("example.org")}, func(*ast.Term) error { + t.Fatal("expected not to be called") + return nil + }) + if err == nil { + t.Fatal("expected error") + } + _, ok := err.(Halt) + if !ok { + t.Errorf("expected Halt error, got %v (%[1]T)", err) + } + if !IsCancel(err) { + t.Errorf("expected wrapped Cancel error, got %v (%[1]T)", err) + } + }) + } + + addr := "v4.org" + exp := ast.NewSet(ast.StringTerm("1.2.3.4")) + for name, allowNet := range map[string][]string{ + "allow_net nil": nil, + "allow_net match": {addr}, + "allow_net match + additional host": {addr, "example.com"}, + } { + t.Run(name, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + capabilities := ast.CapabilitiesForThisVersion() + capabilities.AllowNet = allowNet + bctx := BuiltinContext{ + Context: ctx, + Cache: make(builtins.Cache), + Capabilities: capabilities, + } + srv.PatchNet(resolv) + err := builtinLookupIPAddr(bctx, []*ast.Term{ast.StringTerm(addr)}, func(act *ast.Term) error { + if exp.Compare(act.Value) != 0 { + t.Errorf("expected %v, got %v", exp, act) + } + return nil + }) + if err != nil { + t.Error(err) + } + }) + } + + expError := fmt.Errorf("unallowed host: %s", addr) + for name, allowNet := range map[string][]string{ + "allow_net empty": {}, + "allow_net no match": {"example.com"}, + } { + t.Run(name, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + capabilities := ast.CapabilitiesForThisVersion() + capabilities.AllowNet = allowNet + bctx := BuiltinContext{ + Context: ctx, + Cache: make(builtins.Cache), + Capabilities: capabilities, + } + srv.PatchNet(resolv) + err := builtinLookupIPAddr(bctx, []*ast.Term{ast.StringTerm(addr)}, func(_ *ast.Term) error { + t.Fatal("expected not to be called") + return nil + }) + if err == nil { + t.Error("expected error") + } + assertError(t, expError, err) + }) + } +} + +type sink struct{} + +func (sink) Printf(string, ...any) {} diff --git a/third_party/opa/v1/topdown/numbers.go b/third_party/opa/v1/topdown/numbers.go new file mode 100644 index 000000000000..a3f8f0854ff6 --- /dev/null +++ b/third_party/opa/v1/topdown/numbers.go @@ -0,0 +1,201 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "errors" + "fmt" + "math/big" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +type randIntCachingKey string + +var zero = big.NewInt(0) +var one = big.NewInt(1) + +func builtinNumbersRange(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + if canGenerateCheapRange(operands) { + return generateCheapRange(operands, 1, iter) + } + + x, err := builtins.BigIntOperand(operands[0].Value, 1) + if err != nil { + return err + } + + y, err := builtins.BigIntOperand(operands[1].Value, 2) + if err != nil { + return err + } + + ast, err := generateRange(bctx, x, y, one, "numbers.range") + if err != nil { + return err + } + + return iter(ast) +} + +func builtinNumbersRangeStep(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + if canGenerateCheapRangeStep(operands) { + step, _ := builtins.IntOperand(operands[2].Value, 3) + if step <= 0 { + return errors.New("numbers.range_step: step must be a positive number above zero") + } + return generateCheapRange(operands, step, iter) + } + + x, err := builtins.BigIntOperand(operands[0].Value, 1) + if err != nil { + return err + } + + y, err := builtins.BigIntOperand(operands[1].Value, 2) + if err != nil { + return err + } + + step, err := builtins.BigIntOperand(operands[2].Value, 3) + if err != nil { + return err + } + + if step.Cmp(zero) <= 0 { + return errors.New("numbers.range_step: step must be a positive number above zero") + } + + ast, err := generateRange(bctx, x, y, step, "numbers.range_step") + if err != nil { + return err + } + + return iter(ast) +} + +func canGenerateCheapRange(operands []*ast.Term) bool { + x, err := builtins.IntOperand(operands[0].Value, 1) + if err != nil || !ast.HasInternedIntNumberTerm(x) { + return false + } + + y, err := builtins.IntOperand(operands[1].Value, 2) + if err != nil || !ast.HasInternedIntNumberTerm(y) { + return false + } + + return true +} + +func canGenerateCheapRangeStep(operands []*ast.Term) bool { + if canGenerateCheapRange(operands) { + step, err := builtins.IntOperand(operands[1].Value, 3) + if err == nil && ast.HasInternedIntNumberTerm(step) { + return true + } + } + + return false +} + +func generateCheapRange(operands []*ast.Term, step int, iter func(*ast.Term) error) error { + x, err := builtins.IntOperand(operands[0].Value, 1) + if err != nil { + return err + } + + y, err := builtins.IntOperand(operands[1].Value, 2) + if err != nil { + return err + } + + terms := make([]*ast.Term, 0, y+1) + + if x <= y { + for i := x; i <= y; i += step { + terms = append(terms, ast.InternedTerm(i)) + } + } else { + for i := x; i >= y; i -= step { + terms = append(terms, ast.InternedTerm(i)) + } + } + + return iter(ast.ArrayTerm(terms...)) +} + +func generateRange(bctx BuiltinContext, x *big.Int, y *big.Int, step *big.Int, funcName string) (*ast.Term, error) { + cmp := x.Cmp(y) + + comp := func(i *big.Int, y *big.Int) bool { return i.Cmp(y) <= 0 } + iter := func(i *big.Int) *big.Int { return i.Add(i, step) } + + if cmp > 0 { + comp = func(i *big.Int, y *big.Int) bool { return i.Cmp(y) >= 0 } + iter = func(i *big.Int) *big.Int { return i.Sub(i, step) } + } + + result := ast.NewArray() + haltErr := Halt{ + Err: &Error{ + Code: CancelErr, + Message: funcName + ": timed out before generating all numbers in range", + }, + } + + for i := new(big.Int).Set(x); comp(i, y); i = iter(i) { + if bctx.Cancel != nil && bctx.Cancel.Cancelled() { + return nil, haltErr + } + result = result.Append(ast.NewTerm(builtins.IntToNumber(i))) + } + + return ast.NewTerm(result), nil +} + +func builtinRandIntn(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + strOp, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + + } + + n, err := builtins.IntOperand(operands[1].Value, 2) + if err != nil { + return err + } + + if n == 0 { + return iter(ast.InternedTerm(0)) + } + + if n < 0 { + n = -n + } + + var key = randIntCachingKey(fmt.Sprintf("%s-%d", strOp, n)) + + if val, ok := bctx.Cache.Get(key); ok { + return iter(val.(*ast.Term)) + } + + r, err := bctx.Rand() + if err != nil { + return err + } + result := ast.InternedTerm(r.Intn(n)) + bctx.Cache.Put(key, result) + + return iter(result) +} + +func init() { + RegisterBuiltinFunc(ast.NumbersRange.Name, builtinNumbersRange) + RegisterBuiltinFunc(ast.NumbersRangeStep.Name, builtinNumbersRangeStep) + RegisterBuiltinFunc(ast.RandIntn.Name, builtinRandIntn) +} diff --git a/third_party/opa/v1/topdown/numbers_bench_test.go b/third_party/opa/v1/topdown/numbers_bench_test.go new file mode 100644 index 000000000000..dc54ffdcd0c1 --- /dev/null +++ b/third_party/opa/v1/topdown/numbers_bench_test.go @@ -0,0 +1,78 @@ +package topdown + +import ( + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// BenchmarkNumbersRange/interned-12 824348 1443 ns/op 1880 B/op 4 allocs/op +// BenchmarkNumbersRange/not_interned-12 99547 12052 ns/op 15968 B/op 533 allocs/op +func BenchmarkNumbersRange(b *testing.B) { + bctx := BuiltinContext{} + expect100Items := expectCountIter(b, 100) + tests := []struct { + name string + operands []*ast.Term + }{ + { + name: "interned", + operands: []*ast.Term{ast.InternedTerm(0), ast.InternedTerm(99)}, + }, + { + name: "not interned", + operands: []*ast.Term{ast.IntNumberTerm(1000), ast.IntNumberTerm(1099)}, + }, + } + + for _, test := range tests { + b.Run(test.name, func(b *testing.B) { + for range b.N { + if err := builtinNumbersRange(bctx, test.operands, expect100Items); err != nil { + b.Fatal(err) + } + } + }) + } +} + +// Performs and should perform identically to BenchmarkNumbersRange +func BenchmarkNumbersRangeStep(b *testing.B) { + bctx := BuiltinContext{} + expect100Items := expectCountIter(b, 100) + step := ast.InternedTerm(2) + tests := []struct { + name string + operands []*ast.Term + }{ + { + name: "interned", + operands: []*ast.Term{ast.InternedTerm(0), ast.InternedTerm(199), step}, + }, + { + name: "not interned", + operands: []*ast.Term{ast.IntNumberTerm(1000), ast.IntNumberTerm(1199), step}, + }, + } + + for _, test := range tests { + b.Run(test.name, func(b *testing.B) { + for range b.N { + if err := builtinNumbersRangeStep(bctx, test.operands, expect100Items); err != nil { + b.Fatal(err) + } + } + }) + } +} + +func expectCountIter(b *testing.B, expected int) func(*ast.Term) error { + b.Helper() + return func(term *ast.Term) error { + if a, ok := term.Value.(*ast.Array); ok && a.Len() == expected { + return nil + } + return fmt.Errorf("expected an array of %d items, got %v", expected, term.Value) + } +} diff --git a/third_party/opa/v1/topdown/numbers_test.go b/third_party/opa/v1/topdown/numbers_test.go new file mode 100644 index 000000000000..af0820fe4bb4 --- /dev/null +++ b/third_party/opa/v1/topdown/numbers_test.go @@ -0,0 +1,107 @@ +package topdown + +import ( + "context" + "math/rand" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestRandIntnZero(t *testing.T) { + t.Parallel() + + qrs, err := NewQuery(ast.MustParseBody(`rand.intn("x", 0, out)`)).Run(context.Background()) + if err != nil { + t.Fatal(err) + } else if len(qrs) != 1 { + t.Fatal("expected one result") + } + + exp := ast.MustParseTerm(`{{out: 0}}`) + + result := queryResultSetToTerm(qrs) + + if !result.Equal(exp) { + t.Fatalf("expected %v but got %v", exp, result) + } +} + +func TestRandIntnNegative(t *testing.T) { + t.Parallel() + + qrs, err := NewQuery(ast.MustParseBody(`rand.intn("x", -100, out)`)).Run(context.Background()) + if err != nil { + t.Fatal(err) + } else if len(qrs) != 1 { + t.Fatal("expected one result") + } + + x, ok := qrs[0][ast.Var("out")].Value.(ast.Number).Int() + if !ok { + t.Fatal("expected int") + } + + if x < 0 || x >= 100 { + t.Fatal("expected x to be [0, 100)") + } +} + +func TestRandIntnSeedingAndCaching(t *testing.T) { + t.Parallel() + + query := `rand.intn("x", 100000, x); rand.intn("x", 1000, y); rand.intn("x", 100000, x2); rand.intn("y", 1000, z)` + + q := NewQuery(ast.MustParseBody(query)).WithSeed(rand.New(rand.NewSource(0))).WithCompiler(ast.NewCompiler()) + + ctx := context.Background() + + qrs, err := q.Run(ctx) + if err != nil { + t.Fatal(err) + } else if len(qrs) != 1 { + t.Fatal("expected exactly one result but got:", qrs) + } + + exp := ast.MustParseTerm(` + { + { + x: 88007, + x2: 88007, + y: 796, + z: 101 + } + } + `) + + result := queryResultSetToTerm(qrs) + + if !result.Equal(exp) { + t.Fatalf("expected %v but got %v", exp, result) + } + +} + +func TestRandIntnSavingDuringPartialEval(t *testing.T) { + t.Parallel() + + query := `x = "x"; y = 100; rand.intn(x, y, z)` + c := ast.NewCompiler(). + WithCapabilities(&ast.Capabilities{Builtins: []*ast.Builtin{ast.RandIntn}}) + c.Compile(nil) + + q := NewQuery(ast.MustParseBody(query)).WithSeed(rand.New(rand.NewSource(0))).WithCompiler(c) + + queries, modules, err := q.PartialRun(context.Background()) + if err != nil { + t.Fatal(err) + } else if len(modules) > 0 { + t.Fatal("expected no support") + } + + exp := ast.MustParseBody(`rand.intn("x", 100, z); x = "x"; y = 100`) + + if len(queries) != 1 || !queries[0].Equal(exp) { + t.Fatalf("expected %v but got: %v", exp, queries) + } +} diff --git a/third_party/opa/v1/topdown/object.go b/third_party/opa/v1/topdown/object.go new file mode 100644 index 000000000000..c6fbe7022fbc --- /dev/null +++ b/third_party/opa/v1/topdown/object.go @@ -0,0 +1,235 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/internal/ref" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func builtinObjectUnion(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + objA, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + + objB, err := builtins.ObjectOperand(operands[1].Value, 2) + if err != nil { + return err + } + + if objA.Len() == 0 { + return iter(operands[1]) + } + if objB.Len() == 0 { + return iter(operands[0]) + } + if objA.Compare(objB) == 0 { + return iter(operands[0]) + } + + r := mergeWithOverwrite(objA, objB) + + return iter(ast.NewTerm(r)) +} + +func builtinObjectUnionN(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + arr, err := builtins.ArrayOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Because we need merge-with-overwrite behavior, we can iterate + // back-to-front, and get a mostly correct set of key assignments that + // give us the "last assignment wins, with merges" behavior we want. + // However, if a non-object overwrites an object value anywhere in the + // chain of assignments for a key, we have to "freeze" that key to + // prevent accidentally picking up nested objects that could merge with + // it from earlier in the input array. + // Example: + // Input: [{"a": {"b": 2}}, {"a": 4}, {"a": {"c": 3}}] + // Want Output: {"a": {"c": 3}} + result := ast.NewObject() + frozenKeys := map[*ast.Term]struct{}{} + for i := arr.Len() - 1; i >= 0; i-- { + o, ok := arr.Elem(i).Value.(ast.Object) + if !ok { + return builtins.NewOperandElementErr(1, arr, arr.Elem(i).Value, "object") + } + mergewithOverwriteInPlace(result, o, frozenKeys) + } + + return iter(ast.NewTerm(result)) +} + +func builtinObjectRemove(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + // Expect an object and an array/set/object of keys + obj, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Build a set of keys to remove + keysToRemove, err := getObjectKeysParam(operands[1].Value) + if err != nil { + return err + } + r := ast.NewObject() + obj.Foreach(func(key *ast.Term, value *ast.Term) { + if !keysToRemove.Contains(key) { + r.Insert(key, value) + } + }) + + return iter(ast.NewTerm(r)) +} + +func builtinObjectFilter(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + // Expect an object and an array/set/object of keys + obj, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Build a new object from the supplied filter keys + keys, err := getObjectKeysParam(operands[1].Value) + if err != nil { + return err + } + + filterObj := ast.NewObject() + keys.Foreach(func(key *ast.Term) { + filterObj.Insert(key, ast.InternedNullTerm) + }) + + // Actually do the filtering + r, err := obj.Filter(filterObj) + if err != nil { + return err + } + + return iter(ast.NewTerm(r)) +} + +func builtinObjectGet(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + object, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // if the get key is not an array, attempt to get the top level key for the operand value in the object + path, ok := operands[1].Value.(*ast.Array) + if !ok { + if ret := object.Get(operands[1]); ret != nil { + return iter(ret) + } + + return iter(operands[2]) + } + + // if the path is empty, then we skip selecting nested keys and return the whole object + if path.Len() == 0 { + return iter(operands[0]) + } + + // build an ast.Ref from the array and see if it matches within the object + pathRef := ref.ArrayPath(path) + value, err := object.Find(pathRef) + if err != nil { + return iter(operands[2]) + } + + return iter(ast.NewTerm(value)) +} + +func builtinObjectKeys(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + object, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + if object.Len() == 0 { + return iter(ast.InternedEmptySet) + } + + return iter(ast.SetTerm(object.Keys()...)) +} + +// getObjectKeysParam returns a set of key values +// from a supplied ast array, object, set value +func getObjectKeysParam(arrayOrSet ast.Value) (ast.Set, error) { + switch v := arrayOrSet.(type) { + case *ast.Array: + keys := ast.NewSet() + v.Foreach(keys.Add) + return keys, nil + case ast.Set: + return ast.NewSet(v.Slice()...), nil + case ast.Object: + return ast.NewSet(v.Keys()...), nil + } + + return nil, builtins.NewOperandTypeErr(2, arrayOrSet, "object", "set", "array") +} + +func mergeWithOverwrite(objA, objB ast.Object) ast.Object { + merged, _ := objA.MergeWith(objB, func(v1, v2 *ast.Term) (*ast.Term, bool) { + originalValueObj, ok2 := v1.Value.(ast.Object) + updateValueObj, ok1 := v2.Value.(ast.Object) + if !ok1 || !ok2 { + // If we can't merge, stick with the right-hand value + return v2, false + } + + // Recursively update the existing value + merged := mergeWithOverwrite(originalValueObj, updateValueObj) + return ast.NewTerm(merged), false + }) + return merged +} + +// Modifies obj with any new keys from other, and recursively +// merges any keys where the values are both objects. +func mergewithOverwriteInPlace(obj, other ast.Object, frozenKeys map[*ast.Term]struct{}) { + other.Foreach(func(k, v *ast.Term) { + v2 := obj.Get(k) + // The key didn't exist in other, keep the original value. + if v2 == nil { + nestedObj, ok := v.Value.(ast.Object) + if !ok { + // v is not an object + obj.Insert(k, v) + } else { + // Copy the nested object so the original object would not be modified + nestedObjCopy := nestedObj.Copy() + obj.Insert(k, ast.NewTerm(nestedObjCopy)) + } + + return + } + // The key exists in both. Merge or reject change. + updateValueObj, ok2 := v.Value.(ast.Object) + originalValueObj, ok1 := v2.Value.(ast.Object) + // Both are objects? Merge recursively. + if ok1 && ok2 { + // Check to make sure that this key isn't frozen before merging. + if _, ok := frozenKeys[v2]; !ok { + mergewithOverwriteInPlace(originalValueObj, updateValueObj, frozenKeys) + } + } else { + // Else, original value wins. Freeze the key. + frozenKeys[v2] = struct{}{} + } + }) +} + +func init() { + RegisterBuiltinFunc(ast.ObjectUnion.Name, builtinObjectUnion) + RegisterBuiltinFunc(ast.ObjectUnionN.Name, builtinObjectUnionN) + RegisterBuiltinFunc(ast.ObjectRemove.Name, builtinObjectRemove) + RegisterBuiltinFunc(ast.ObjectFilter.Name, builtinObjectFilter) + RegisterBuiltinFunc(ast.ObjectGet.Name, builtinObjectGet) + RegisterBuiltinFunc(ast.ObjectKeys.Name, builtinObjectKeys) +} diff --git a/third_party/opa/v1/topdown/object_bench_test.go b/third_party/opa/v1/topdown/object_bench_test.go new file mode 100644 index 000000000000..d940189841b4 --- /dev/null +++ b/third_party/opa/v1/topdown/object_bench_test.go @@ -0,0 +1,110 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" +) + +func genNxMObjectBenchmarkData(n, m int) ast.Value { + objList := make([]*ast.Term, n) + for i := range n { + v := ast.NewObject() + for j := range m { + v.Insert(ast.StringTerm(fmt.Sprintf("%d,%d", i, j)), ast.BooleanTerm(true)) + } + objList[i] = ast.NewTerm(v) + } + return ast.NewArray(objList...) +} + +func BenchmarkObjectUnionN(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 100, 250} + + for _, n := range sizes { + for _, m := range sizes { + b.Run(fmt.Sprintf("%dx%d", n, m), func(b *testing.B) { + store := inmem.NewFromObject(map[string]any{"objs": genNxMObjectBenchmarkData(n, m)}) + module := `package test + + combined := object.union_n(data.objs)` + + query := ast.MustParseBody("data.test.combined") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + _, err := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + Run(ctx) + + return err + }) + + if err != nil { + b.Fatal(err) + } + } + }) + } + } +} + +func BenchmarkObjectUnionNSlow(b *testing.B) { + // This benchmarks the suggested means to implement union + // without using the builtin, to give us an idea of whether or not + // the builtin is actually making things any faster. + ctx := context.Background() + + sizes := []int{10, 100, 250} + + for _, n := range sizes { + for _, m := range sizes { + b.Run(fmt.Sprintf("%dx%d", n, m), func(b *testing.B) { + store := inmem.NewFromObject(map[string]any{"objs": genNxMObjectBenchmarkData(n, m)}) + module := `package test + + combined := {k: true | s := data.objs[_]; s[k]}` + + query := ast.MustParseBody("data.test.combined") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + _, err := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + Run(ctx) + + return err + }) + + if err != nil { + b.Fatal(err) + } + } + }) + } + } +} diff --git a/third_party/opa/v1/topdown/object_test.go b/third_party/opa/v1/topdown/object_test.go new file mode 100644 index 000000000000..40015a55bab2 --- /dev/null +++ b/third_party/opa/v1/topdown/object_test.go @@ -0,0 +1,93 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestObjectUnionNBuiltin(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + query string + input string + expected string + }{ + // NOTE(philipc): These tests assume that erroneous types are + // checked elsewhere, and focus only on functional correctness. + { + note: "Empty", + input: `[]`, + expected: `{}`, + }, + { + note: "Singletons", + input: `[{1: true}, {2: true}, {3: true}]`, + expected: `{1: true, 2: true, 3: true}`, + }, + { + note: "One object", + input: `[{1: true, 2: true, 3: true}]`, + expected: `{1: true, 2: true, 3: true}`, + }, + { + note: "One object + empty", + input: `[{1: true, 2: true, 3: true}, {}]`, + expected: `{1: true, 2: true, 3: true}`, + }, + { + note: "Multiple objects, with scalar duplicates", + input: `[{"A": 1, "B": 2, "C": 3}, {"A": 1, "B": 2}, {"C": 3}, {"D": 4, "E": 5}]`, + expected: `{"A": 1, "B": 2, "C": 3, "D": 4, "E": 5}`, + }, + { + note: "2x objects, with simple merge on key", + input: `[{"A": 1, "B": {"D": 4}, "C": 3}, {"B": 200}]`, + expected: `{"A": 1, "B": 200, "C": 3,}`, + }, + { + note: "2x objects, with simple merge on nested objects with different keys", + input: `[{"X": {"A": "a"}}, {"X": {"B": "b"}}]`, + expected: `{"X": {"A": "a", "B": "b"}}`, + }, + { + note: "2x objects, with complex merge on nested object", + input: `[{"A": 1, "B": {"N1": {"X": true, "Z": false}}, "C": 3}, {"B": {"N1": {"X": 49, "Z": 50}}}]`, + expected: `{"A": 1, "B": {"N1": {"X": 49, "Z": 50}}, "C": 3}`, + }, + { + note: "Multiple objects, with scalar, then object, overwrite on nested key", + input: `[{"A": 1, "B": {"N1": {"X": true, "Z": false}}, "C": 3}, {"B": {"N1": 23}}, {"B": {"N1": {"Z": 50}}}]`, + expected: `{"A": 1, "B": {"N1": {"Z": 50}}, "C": 3}`, + }, + { + note: "Multiple objects, with complex overwrite on nested key", + input: `[{"A": 1, "B": {"N1": {"X": true, "Z": false}}, "C": 3}, {"B": {"N1": 23}}, {"B": {"N1": {"Z": 50}}}, {"B": {"N1": {"Z": 35}}}]`, + expected: `{"A": 1, "B": {"N1": {"Z": 35}}, "C": 3}`, + }, + } + + for _, tc := range tests { + inputs := ast.MustParseTerm(tc.input) + inputsCopy := inputs.Copy() + result, err := getResult(builtinObjectUnionN, inputs) + if err != nil { + t.Fatal(err) + } + + if !inputsCopy.Equal(inputs) { + t.Fatal("Inputs were mutated") + } + + expected := ast.MustParseTerm(tc.expected) + if !result.Equal(expected) { + t.Fatalf("Expected %v but got %v", expected, result) + } + } +} diff --git a/third_party/opa/v1/topdown/parse.go b/third_party/opa/v1/topdown/parse.go new file mode 100644 index 000000000000..464e0141a267 --- /dev/null +++ b/third_party/opa/v1/topdown/parse.go @@ -0,0 +1,60 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func builtinRegoParseModule(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + filename, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + input, err := builtins.StringOperand(operands[1].Value, 1) + if err != nil { + return err + } + + // FIXME: Use configured rego-version? + module, err := ast.ParseModule(string(filename), string(input)) + if err != nil { + return err + } + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(module); err != nil { + return err + } + + term, err := ast.ParseTerm(buf.String()) + if err != nil { + return err + } + + return iter(term) +} + +func registerRegoMetadataBuiltinFunction(builtin *ast.Builtin) { + f := func(BuiltinContext, []*ast.Term, func(*ast.Term) error) error { + // The compiler should replace all usage of this function, so the only way to get here is within a query; + // which cannot define rules. + return fmt.Errorf("the %s function must only be called within the scope of a rule", builtin.Name) + } + RegisterBuiltinFunc(builtin.Name, f) +} + +func init() { + RegisterBuiltinFunc(ast.RegoParseModule.Name, builtinRegoParseModule) + registerRegoMetadataBuiltinFunction(ast.RegoMetadataChain) + registerRegoMetadataBuiltinFunction(ast.RegoMetadataRule) +} diff --git a/third_party/opa/v1/topdown/parse_bytes.go b/third_party/opa/v1/topdown/parse_bytes.go new file mode 100644 index 000000000000..cd36b87b178c --- /dev/null +++ b/third_party/opa/v1/topdown/parse_bytes.go @@ -0,0 +1,157 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + "math/big" + "strings" + "unicode" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +const ( + none uint64 = 1 << (10 * iota) + ki + mi + gi + ti + pi + ei + + kb uint64 = 1000 + mb = kb * 1000 + gb = mb * 1000 + tb = gb * 1000 + pb = tb * 1000 + eb = pb * 1000 +) + +func parseNumBytesError(msg string) error { + return fmt.Errorf("%s: %s", ast.UnitsParseBytes.Name, msg) +} + +func errBytesUnitNotRecognized(unit string) error { + return parseNumBytesError(fmt.Sprintf("byte unit %s not recognized", unit)) +} + +var ( + errBytesValueNoAmount = parseNumBytesError("no byte amount provided") + errBytesValueNumConv = parseNumBytesError("could not parse byte amount to a number") + errBytesValueIncludesSpaces = parseNumBytesError("spaces not allowed in resource strings") +) + +func builtinNumBytes(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + var m big.Float + + raw, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + s := formatString(raw) + + if strings.Contains(s, " ") { + return errBytesValueIncludesSpaces + } + + num, unit := extractNumAndUnit(s) + if num == "" { + return errBytesValueNoAmount + } + + switch unit { + case "": + m.SetUint64(none) + case "kb", "k": + m.SetUint64(kb) + case "kib", "ki": + m.SetUint64(ki) + case "mb", "m": + m.SetUint64(mb) + case "mib", "mi": + m.SetUint64(mi) + case "gb", "g": + m.SetUint64(gb) + case "gib", "gi": + m.SetUint64(gi) + case "tb", "t": + m.SetUint64(tb) + case "tib", "ti": + m.SetUint64(ti) + case "pb", "p": + m.SetUint64(pb) + case "pib", "pi": + m.SetUint64(pi) + case "eb", "e": + m.SetUint64(eb) + case "eib", "ei": + m.SetUint64(ei) + default: + return errBytesUnitNotRecognized(unit) + } + + numFloat, ok := new(big.Float).SetString(num) + if !ok { + return errBytesValueNumConv + } + + var total big.Int + numFloat.Mul(numFloat, &m).Int(&total) + return iter(ast.NewTerm(builtins.IntToNumber(&total))) +} + +// Makes the string lower case and removes quotation marks +func formatString(s ast.String) string { + str := string(s) + lower := strings.ToLower(str) + return strings.ReplaceAll(lower, "\"", "") +} + +// Splits the string into a number string à la "10" or "10.2" and a unit +// string à la "gb" or "MiB" or "foo". Either can be an empty string +// (error handling is provided elsewhere). +func extractNumAndUnit(s string) (string, string) { + isNum := func(r rune) bool { + return unicode.IsDigit(r) || r == '.' + } + + firstNonNumIdx := -1 + for idx := 0; idx < len(s); idx++ { + r := rune(s[idx]) + // Identify the first non-numeric character, marking the boundary between the number and the unit. + if !isNum(r) && r != 'e' && r != 'E' && r != '+' && r != '-' { + firstNonNumIdx = idx + break + } + if r == 'e' || r == 'E' { + // Check if the next character is a valid digit or +/- for scientific notation + if idx == len(s)-1 || (!unicode.IsDigit(rune(s[idx+1])) && rune(s[idx+1]) != '+' && rune(s[idx+1]) != '-') { + firstNonNumIdx = idx + break + } + // Skip the next character if it is '+' or '-' + if idx+1 < len(s) && (s[idx+1] == '+' || s[idx+1] == '-') { + idx++ + } + } + } + + if firstNonNumIdx == -1 { // only digits, '.', or valid scientific notation + return s, "" + } + if firstNonNumIdx == 0 { // only units (starts with non-digit) + return "", s + } + + // Return the number and the rest as the unit + return s[:firstNonNumIdx], s[firstNonNumIdx:] +} + +func init() { + RegisterBuiltinFunc(ast.UnitsParseBytes.Name, builtinNumBytes) +} diff --git a/third_party/opa/v1/topdown/parse_units.go b/third_party/opa/v1/topdown/parse_units.go new file mode 100644 index 000000000000..44aec8629985 --- /dev/null +++ b/third_party/opa/v1/topdown/parse_units.go @@ -0,0 +1,125 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "encoding/json" + "fmt" + "math/big" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +// Binary Si unit constants are borrowed from topdown/parse_bytes +const siMilli = 0.001 +const ( + siK uint64 = 1000 + siM = siK * 1000 + siG = siM * 1000 + siT = siG * 1000 + siP = siT * 1000 + siE = siP * 1000 +) + +func parseUnitsError(msg string) error { + return fmt.Errorf("%s: %s", ast.UnitsParse.Name, msg) +} + +func errUnitNotRecognized(unit string) error { + return parseUnitsError(fmt.Sprintf("unit %s not recognized", unit)) +} + +var ( + errNoAmount = parseUnitsError("no amount provided") + errNumConv = parseUnitsError("could not parse amount to a number") + errIncludesSpaces = parseUnitsError("spaces not allowed in resource strings") +) + +// Accepts both normal SI and binary SI units. +func builtinUnits(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + var x big.Rat + + raw, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // We remove escaped quotes from strings here to retain parity with units.parse_bytes. + s := string(raw) + s = strings.ReplaceAll(s, "\"", "") + + if strings.Contains(s, " ") { + return errIncludesSpaces + } + + num, unit := extractNumAndUnit(s) + if num == "" { + return errNoAmount + } + + // Unlike in units.parse_bytes, we only lowercase after the first letter, + // so that we can distinguish between 'm' and 'M'. + if len(unit) > 1 { + lower := strings.ToLower(unit[1:]) + unit = unit[:1] + lower + } + + switch unit { + case "m": + x.SetFloat64(siMilli) + case "": + x.SetUint64(none) + case "k", "K": + x.SetUint64(siK) + case "ki", "Ki": + x.SetUint64(ki) + case "M": + x.SetUint64(siM) + case "mi", "Mi": + x.SetUint64(mi) + case "g", "G": + x.SetUint64(siG) + case "gi", "Gi": + x.SetUint64(gi) + case "t", "T": + x.SetUint64(siT) + case "ti", "Ti": + x.SetUint64(ti) + case "p", "P": + x.SetUint64(siP) + case "pi", "Pi": + x.SetUint64(pi) + case "e", "E": + x.SetUint64(siE) + case "ei", "Ei": + x.SetUint64(ei) + default: + return errUnitNotRecognized(unit) + } + + numRat, ok := new(big.Rat).SetString(num) + if !ok { + return errNumConv + } + + numRat.Mul(numRat, &x) + + // Cleaner printout when we have a pure integer value. + if numRat.IsInt() { + return iter(ast.NumberTerm(json.Number(numRat.Num().String()))) + } + + // When using just big.Float, we had floating-point precision + // issues because quantities like 0.001 are not exactly representable. + // Rationals (such as big.Rat) do not suffer this problem, but are + // more expensive to compute with in general. + return iter(ast.NumberTerm(json.Number(numRat.FloatString(10)))) +} + +func init() { + RegisterBuiltinFunc(ast.UnitsParse.Name, builtinUnits) +} diff --git a/third_party/opa/v1/topdown/print.go b/third_party/opa/v1/topdown/print.go new file mode 100644 index 000000000000..f852f3e320ac --- /dev/null +++ b/third_party/opa/v1/topdown/print.go @@ -0,0 +1,86 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + "io" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/print" +) + +func NewPrintHook(w io.Writer) print.Hook { + return printHook{w: w} +} + +type printHook struct { + w io.Writer +} + +func (h printHook) Print(_ print.Context, msg string) error { + _, err := fmt.Fprintln(h.w, msg) + return err +} + +func builtinPrint(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + if bctx.PrintHook == nil { + return iter(nil) + } + + arr, err := builtins.ArrayOperand(operands[0].Value, 1) + if err != nil { + return err + } + + buf := make([]string, arr.Len()) + + err = builtinPrintCrossProductOperands(bctx, buf, arr, 0, func(buf []string) error { + pctx := print.Context{ + Context: bctx.Context, + Location: bctx.Location, + } + return bctx.PrintHook.Print(pctx, strings.Join(buf, " ")) + }) + if err != nil { + return err + } + + return iter(nil) +} + +func builtinPrintCrossProductOperands(bctx BuiltinContext, buf []string, operands *ast.Array, i int, f func([]string) error) error { + + if i >= operands.Len() { + return f(buf) + } + + xs, ok := operands.Elem(i).Value.(ast.Set) + if !ok { + return Halt{Err: internalErr(bctx.Location, fmt.Sprintf("illegal argument type: %v", ast.ValueName(operands.Elem(i).Value)))} + } + + if xs.Len() == 0 { + buf[i] = "" + return builtinPrintCrossProductOperands(bctx, buf, operands, i+1, f) + } + + return xs.Iter(func(x *ast.Term) error { + switch v := x.Value.(type) { + case ast.String: + buf[i] = string(v) + default: + buf[i] = v.String() + } + return builtinPrintCrossProductOperands(bctx, buf, operands, i+1, f) + }) +} + +func init() { + RegisterBuiltinFunc(ast.InternalPrint.Name, builtinPrint) +} diff --git a/third_party/opa/v1/topdown/print/print.go b/third_party/opa/v1/topdown/print/print.go new file mode 100644 index 000000000000..ce684ae945b9 --- /dev/null +++ b/third_party/opa/v1/topdown/print/print.go @@ -0,0 +1,21 @@ +package print + +import ( + "context" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// Context provides the Hook implementation context about the print() call. +type Context struct { + Context context.Context // request context passed when query executed + Location *ast.Location // location of print call +} + +// Hook defines the interface that callers can implement to receive print +// statement outputs. If the hook returns an error, it will be surfaced if +// strict builtin error checking is enabled (otherwise, it will not halt +// execution.) +type Hook interface { + Print(Context, string) error +} diff --git a/third_party/opa/v1/topdown/print_test.go b/third_party/opa/v1/topdown/print_test.go new file mode 100644 index 000000000000..c5640f25e40b --- /dev/null +++ b/third_party/opa/v1/topdown/print_test.go @@ -0,0 +1,238 @@ +package topdown + +import ( + "bytes" + "context" + "errors" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/print" +) + +func TestTopDownPrint(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + module string + exp string + }{ + { + note: "empty", + module: ` + package test + + p if { print() } + `, + exp: "\n", + }, + { + note: "strings", + module: ` + package test + + p if { + x := "world" + print("hello", x) + } + `, + exp: "hello world\n", + }, + { + note: "collections", + module: ` + package test + + xs := [1,2] + + p if { + print("the value of xs is:", xs) + } + `, + exp: "the value of xs is: [1, 2]\n", + }, + { + note: "undefined - does not affect rule evaluation and output contains marker", + module: ` + package test + + p if { + print("the value of foo is:", input.foo) + } + `, + exp: "the value of foo is: \n", + }, + { + note: "undefined nested term does not affect rule evaluation and output contains marker", + module: ` + package test + + p if { + print("the value of foo is:", [input.foo]) + } + `, + exp: "the value of foo is: \n", + }, + { + note: "built-in error as undefined", + module: ` + package test + + p if { + print("div by zero:", 1/0) # divide by zero will be undefined unless strict-builtin-errors are enabled + } + `, + exp: "div by zero: \n", + }, + { + note: "cross-product", + module: ` + package test + + xs := {1} + ys := {"a"} + + p if { + print(walk(xs), walk(ys)) + } + `, + exp: `[[], {1}] [[], {"a"}] +[[], {1}] [["a"], "a"] +[[1], 1] [[], {"a"}] +[[1], 1] [["a"], "a"] +`, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + c := ast.MustCompileModulesWithOpts(map[string]string{"test.rego": tc.module}, + ast.CompileOpts{ + EnablePrintStatements: true, + ParserOptions: ast.ParserOptions{ + AllFutureKeywords: true, + }, + }) + buf := bytes.NewBuffer(nil) + q := NewQuery(ast.MustParseBody("data.test.p = x")). + WithPrintHook(NewPrintHook(buf)). + WithCompiler(c) + + qrs, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + if buf.String() != tc.exp { + t.Fatalf("expected: %q but got: %q", tc.exp, buf.String()) + } + + exp := ast.MustParseTerm(`{{x: true}}`) + + if !queryResultSetToTerm(qrs).Equal(exp) { + t.Fatal("expected:", exp, "got:", qrs) + } + }) + } +} + +func TestTopDownPrintInternalError(t *testing.T) { + t.Parallel() + + buf := bytes.NewBuffer(nil) + + q := NewQuery(ast.MustParseBody("internal.print([1])")).WithPrintHook(NewPrintHook(buf)) + + _, err := q.Run(context.Background()) + if err == nil { + t.Fatal("expected error") + } + + asTopDownErr, ok := err.(*Error) + if !ok { + t.Fatal("expected topdown error but got:", err) + } else if asTopDownErr.Code != InternalErr || asTopDownErr.Message != "illegal argument type: number" { + t.Fatal("unexpected code or reason:", err) + } +} + +func TestTopDownPrintHookNotSupplied(t *testing.T) { + t.Parallel() + + // NOTE(tsandall): The built-in function implementation expects all inputs + // to be _sets_, even scalar values are wrapped. This expectation comes from + // the fact that the compiler rewrites all of the operands by wrapping them + // in set comprehensions to avoid short-circuiting on undefined. + q := NewQuery(ast.MustParseBody(`x = 1; internal.print({1})`)) + + qrs, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } + + result := queryResultSetToTerm(qrs) + exp := ast.MustParseTerm(`{{x: 1}}`) + + if result.Value.Compare(exp.Value) != 0 { + t.Fatal("expected:", exp, "but got:", result) + } +} + +func TestTopDownPrintWithStrictBuiltinErrors(t *testing.T) { + t.Parallel() + + buf := bytes.NewBuffer(nil) + + // NOTE(tsandall): See comment above about wrapping operands in sets. + q := NewQuery(ast.MustParseBody(`x = {1 | div(1, 0, y)}; internal.print([{"the value of 1/0 is:"}, x])`)). + WithPrintHook(NewPrintHook(buf)). + WithStrictBuiltinErrors(true). + WithCompiler(ast.NewCompiler()) + + _, err := q.Run(context.Background()) + if err == nil { + t.Fatal("expected error") + } + + asTopDownErr, ok := err.(*Error) + if !ok { + t.Fatal("expected topdown error but got:", err) + } else if asTopDownErr.Code != BuiltinErr || asTopDownErr.Message != "div: divide by zero" { + t.Fatal("unexpected code or reason:", err) + } + + exp := "the value of 1/0 is: \n" + + if buf.String() != exp { + t.Fatalf("expected: %q but got: %q", exp, buf.String()) + } + +} + +type erroringPrintHook struct{} + +func (erroringPrintHook) Print(print.Context, string) error { + return errors.New("print hook error") +} + +func TestTopDownPrintHookErrorPropagation(t *testing.T) { + t.Parallel() + + // NOTE(tsandall): See comment above about wrapping operands in sets. + q := NewQuery(ast.MustParseBody(`internal.print([{"some message"}])`)). + WithPrintHook(erroringPrintHook{}). + WithStrictBuiltinErrors(true). + WithCompiler(ast.NewCompiler()) + + _, err := q.Run(context.Background()) + if err == nil { + t.Fatal("expected error") + } else if !strings.Contains(err.Error(), "print hook error") { + t.Fatal("expected print hook error but got:", err) + } + +} diff --git a/third_party/opa/v1/topdown/providers.go b/third_party/opa/v1/topdown/providers.go new file mode 100644 index 000000000000..dd84026e4b33 --- /dev/null +++ b/third_party/opa/v1/topdown/providers.go @@ -0,0 +1,211 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "encoding/json" + "net/url" + "time" + + "github.com/open-policy-agent/opa/internal/providers/aws" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +var awsRequiredConfigKeyNames = ast.NewSet( + ast.StringTerm("aws_service"), + ast.StringTerm("aws_access_key"), + ast.StringTerm("aws_secret_access_key"), + ast.StringTerm("aws_region"), +) + +func stringFromTerm(t *ast.Term) string { + if v, ok := t.Value.(ast.String); ok { + return string(v) + } + return "" +} + +func getReqBodyBytes(body, rawBody *ast.Term) ([]byte, error) { + var out []byte + + switch { + case rawBody != nil: + out = []byte(stringFromTerm(rawBody)) + case body != nil: + bodyVal := body.Value + bodyValInterface, err := ast.JSON(bodyVal) + if err != nil { + return nil, err + } + bodyValBytes, err := json.Marshal(bodyValInterface) + if err != nil { + return nil, err + } + out = bodyValBytes + default: + out = []byte("") + } + + return out, nil +} + +func objectToMap(o ast.Object) map[string][]string { + out := make(map[string][]string, o.Len()) + o.Foreach(func(k, v *ast.Term) { + ks := stringFromTerm(k) + vs := stringFromTerm(v) + out[ks] = []string{vs} + }) + return out +} + +// Note(philipc): This is roughly the same approach used for http.send. +func validateAWSAuthParameters(o ast.Object) error { + awsKeys := ast.NewSet(o.Keys()...) + + missingKeys := awsRequiredConfigKeyNames.Diff(awsKeys) + if missingKeys.Len() != 0 { + return builtins.NewOperandErr(2, "missing required AWS config parameters(s): %v", missingKeys) + } + + invalidKeys := ast.NewSet() + awsRequiredConfigKeyNames.Foreach(func(t *ast.Term) { + if v := o.Get(t); v != nil { + if _, ok := v.Value.(ast.String); !ok { + invalidKeys.Add(t) + } + } + }) + if invalidKeys.Len() != 0 { + return builtins.NewOperandErr(2, "invalid values for required AWS config parameters(s): %v", invalidKeys) + } + + return nil +} + +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 { + return err + } + + // AWS SigV4 config info object. + awsConfigObj, err := builtins.ObjectOperand(operands[1].Value, 1) + if err != nil { + return err + } + // Make sure our required keys exist! + err = validateAWSAuthParameters(awsConfigObj) + if err != nil { + return err + } + service := stringFromTerm(awsConfigObj.Get(ast.StringTerm("aws_service"))) + awsCreds := aws.CredentialsFromObject(awsConfigObj) + + // Timestamp for signing. + var signingTimestamp time.Time + timestamp, err := builtins.NumberOperand(operands[2].Value, 1) + if err != nil { + return err + } + + ts, ok := timestamp.Int64() + if !ok { + return builtins.NewOperandErr(3, "could not convert time_ns value into a unix timestamp") + } + + signingTimestamp = time.Unix(0, ts) + + // Make sure our required keys exist! + // This check is stricter than required, but better to break here than downstream. + _, err = validateHTTPRequestOperand(operands[0], 1) + if err != nil { + return err + } + + // Prepare required fields from the HTTP request object. + var theURL *url.URL + var method string + reqURL := reqObj.Get(ast.StringTerm("url")) + reqMethod := reqObj.Get(ast.StringTerm("method")) + + headers := ast.NewObject() + headersTerm := reqObj.Get(ast.StringTerm("headers")) + if headersTerm != nil { + var ok bool + headers, ok = headersTerm.Value.(ast.Object) + if !ok { + return builtins.NewOperandTypeErr(0, headersTerm.Value, "object") + } + } + + // Check types on the request parameters. + invalidParameters := ast.NewSet() + if _, ok := reqURL.Value.(ast.String); !ok { + invalidParameters.Add(ast.StringTerm("url")) + } + if _, ok := reqMethod.Value.(ast.String); !ok { + invalidParameters.Add(ast.StringTerm("method")) + } + if invalidParameters.Len() > 0 { + return builtins.NewOperandErr(1, "invalid values for required request parameters(s): %v", invalidParameters) + } + + theURL, err = url.Parse(stringFromTerm(reqURL)) + if err != nil { + return err + } + method = stringFromTerm(reqMethod) + + bodyTerm := reqObj.Get(ast.StringTerm("body")) + rawBodyTerm := reqObj.Get(ast.StringTerm("raw_body")) + body, err := getReqBodyBytes(bodyTerm, rawBodyTerm) + if err != nil { + return err + } + + // Sign the request object's headers, and reconstruct the headers map. + headersMap := objectToMap(headers) + + // if payload signing config is set, pass it down to the signing method + disablePayloadSigning := false + t := awsConfigObj.Get(ast.StringTerm("disable_payload_signing")) + if t != nil { + if v, ok := t.Value.(ast.Boolean); ok { + disablePayloadSigning = bool(v) + } else { + return builtins.NewOperandErr(2, "invalid value for 'disable_payload_signing' in AWS config") + } + } + + authHeader, awsHeadersMap := aws.SignV4(headersMap, method, theURL, body, service, awsCreds, signingTimestamp, disablePayloadSigning) + signedHeadersObj := ast.NewObject() + // Restore original headers + for k, v := range headersMap { + // objectToMap doesn't support arrays + if len(v) == 1 { + signedHeadersObj.Insert(ast.StringTerm(k), ast.StringTerm(v[0])) + } + } + // Set authorization header + signedHeadersObj.Insert(ast.StringTerm("Authorization"), ast.StringTerm(authHeader)) + + // set aws signature headers + for k, v := range awsHeadersMap { + signedHeadersObj.Insert(ast.StringTerm(k), ast.StringTerm(v)) + } + + // Create new request object with updated headers. + out := reqObj.Copy() + out.Insert(ast.StringTerm("headers"), ast.NewTerm(signedHeadersObj)) + + return iter(ast.NewTerm(out)) +} + +func init() { + RegisterBuiltinFunc(ast.ProvidersAWSSignReqObj.Name, builtinAWSSigV4SignReq) +} diff --git a/third_party/opa/v1/topdown/query.go b/third_party/opa/v1/topdown/query.go new file mode 100644 index 000000000000..aee6ba12ebe8 --- /dev/null +++ b/third_party/opa/v1/topdown/query.go @@ -0,0 +1,639 @@ +package topdown + +import ( + "context" + "crypto/rand" + "io" + "sort" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/resolver" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" + "github.com/open-policy-agent/opa/v1/topdown/copypropagation" + "github.com/open-policy-agent/opa/v1/topdown/print" + "github.com/open-policy-agent/opa/v1/tracing" +) + +// QueryResultSet represents a collection of results returned by a query. +type QueryResultSet []QueryResult + +// QueryResult represents a single result returned by a query. The result +// contains bindings for all variables that appear in the query. +type QueryResult map[ast.Var]*ast.Term + +// Query provides a configurable interface for performing query evaluation. +type Query struct { + seed io.Reader + time time.Time + cancel Cancel + query ast.Body + queryCompiler ast.QueryCompiler + compiler *ast.Compiler + store storage.Store + txn storage.Transaction + input *ast.Term + external *resolverTrie + tracers []QueryTracer + plugTraceVars bool + unknowns []*ast.Term + partialNamespace string + skipSaveNamespace bool + metrics metrics.Metrics + instr *Instrumentation + disableInlining []ast.Ref + shallowInlining bool + nondeterministicBuiltins bool + genvarprefix string + runtime *ast.Term + builtins map[string]*Builtin + indexing bool + earlyExit bool + interQueryBuiltinCache cache.InterQueryCache + interQueryBuiltinValueCache cache.InterQueryValueCache + ndBuiltinCache builtins.NDBCache + strictBuiltinErrors bool + builtinErrorList *[]Error + strictObjects bool + roundTripper CustomizeRoundTripper + printHook print.Hook + tracingOpts tracing.Options + virtualCache VirtualCache + baseCache BaseCache +} + +// Builtin represents a built-in function that queries can call. +type Builtin struct { + Decl *ast.Builtin + Func BuiltinFunc +} + +// NewQuery returns a new Query object that can be run. +func NewQuery(query ast.Body) *Query { + return &Query{ + query: query, + genvarprefix: ast.WildcardPrefix, + indexing: true, + earlyExit: true, + } +} + +// WithQueryCompiler sets the queryCompiler used for the query. +func (q *Query) WithQueryCompiler(queryCompiler ast.QueryCompiler) *Query { + q.queryCompiler = queryCompiler + return q +} + +// WithCompiler sets the compiler to use for the query. +func (q *Query) WithCompiler(compiler *ast.Compiler) *Query { + q.compiler = compiler + return q +} + +// WithStore sets the store to use for the query. +func (q *Query) WithStore(store storage.Store) *Query { + q.store = store + return q +} + +// WithTransaction sets the transaction to use for the query. All queries +// should be performed over a consistent snapshot of the storage layer. +func (q *Query) WithTransaction(txn storage.Transaction) *Query { + q.txn = txn + return q +} + +// WithCancel sets the cancellation object to use for the query. Set this if +// you need to abort queries based on a deadline. This is optional. +func (q *Query) WithCancel(cancel Cancel) *Query { + q.cancel = cancel + return q +} + +// WithInput sets the input object to use for the query. References rooted at +// input will be evaluated against this value. This is optional. +func (q *Query) WithInput(input *ast.Term) *Query { + q.input = input + return q +} + +// WithTracer adds a query tracer to use during evaluation. This is optional. +// Deprecated: Use WithQueryTracer instead. +func (q *Query) WithTracer(tracer Tracer) *Query { + qt, ok := tracer.(QueryTracer) + if !ok { + qt = WrapLegacyTracer(tracer) + } + return q.WithQueryTracer(qt) +} + +// WithQueryTracer adds a query tracer to use during evaluation. This is optional. +// Disabled QueryTracers will be ignored. +func (q *Query) WithQueryTracer(tracer QueryTracer) *Query { + if !tracer.Enabled() { + return q + } + + q.tracers = append(q.tracers, tracer) + + // If *any* of the tracers require local variable metadata we need to + // enabled plugging local trace variables. + conf := tracer.Config() + if conf.PlugLocalVars { + q.plugTraceVars = true + } + + return q +} + +// WithMetrics sets the metrics collection to add evaluation metrics to. This +// is optional. +func (q *Query) WithMetrics(m metrics.Metrics) *Query { + q.metrics = m + return q +} + +// WithInstrumentation sets the instrumentation configuration to enable on the +// evaluation process. By default, instrumentation is turned off. +func (q *Query) WithInstrumentation(instr *Instrumentation) *Query { + q.instr = instr + return q +} + +// WithUnknowns sets the initial set of variables or references to treat as +// unknown during query evaluation. This is required for partial evaluation. +func (q *Query) WithUnknowns(terms []*ast.Term) *Query { + q.unknowns = terms + return q +} + +// WithPartialNamespace sets the namespace to use for supporting rules +// generated as part of the partial evaluation process. The ns value must be a +// valid package path component. +func (q *Query) WithPartialNamespace(ns string) *Query { + q.partialNamespace = ns + return q +} + +// WithSkipPartialNamespace disables namespacing of saved support rules that are generated +// from the original policy (rules which are completely synthetic are still namespaced.) +func (q *Query) WithSkipPartialNamespace(yes bool) *Query { + q.skipSaveNamespace = yes + return q +} + +// WithDisableInlining adds a set of paths to the query that should be excluded from +// inlining. Inlining during partial evaluation can be expensive in some cases +// (e.g., when a cross-product is computed.) Disabling inlining avoids expensive +// computation at the cost of generating support rules. +func (q *Query) WithDisableInlining(paths []ast.Ref) *Query { + q.disableInlining = paths + return q +} + +// WithShallowInlining disables aggressive inlining performed during partial evaluation. +// When shallow inlining is enabled rules that depend (transitively) on unknowns are not inlined. +// Only rules/values that are completely known will be inlined. +func (q *Query) WithShallowInlining(yes bool) *Query { + q.shallowInlining = yes + return q +} + +// WithRuntime sets the runtime data to execute the query with. The runtime data +// can be returned by the `opa.runtime` built-in function. +func (q *Query) WithRuntime(runtime *ast.Term) *Query { + q.runtime = runtime + return q +} + +// WithBuiltins adds a set of built-in functions that can be called by the +// query. +func (q *Query) WithBuiltins(builtins map[string]*Builtin) *Query { + q.builtins = builtins + return q +} + +// WithIndexing will enable or disable using rule indexing for the evaluation +// of the query. The default is enabled. +func (q *Query) WithIndexing(enabled bool) *Query { + q.indexing = enabled + return q +} + +// WithEarlyExit will enable or disable using 'early exit' for the evaluation +// of the query. The default is enabled. +func (q *Query) WithEarlyExit(enabled bool) *Query { + q.earlyExit = enabled + return q +} + +// WithSeed sets a reader that will seed randomization required by built-in functions. +// If a seed is not provided crypto/rand.Reader is used. +func (q *Query) WithSeed(r io.Reader) *Query { + q.seed = r + return q +} + +// WithTime sets the time that will be returned by the time.now_ns() built-in function. +func (q *Query) WithTime(x time.Time) *Query { + q.time = x + return q +} + +// WithInterQueryBuiltinCache sets the inter-query cache that built-in functions can utilize. +func (q *Query) WithInterQueryBuiltinCache(c cache.InterQueryCache) *Query { + q.interQueryBuiltinCache = c + return q +} + +// WithInterQueryBuiltinValueCache sets the inter-query value cache that built-in functions can utilize. +func (q *Query) WithInterQueryBuiltinValueCache(c cache.InterQueryValueCache) *Query { + q.interQueryBuiltinValueCache = c + return q +} + +// WithNDBuiltinCache sets the non-deterministic builtin cache. +func (q *Query) WithNDBuiltinCache(c builtins.NDBCache) *Query { + q.ndBuiltinCache = c + return q +} + +// WithStrictBuiltinErrors tells the evaluator to treat all built-in function errors as fatal errors. +func (q *Query) WithStrictBuiltinErrors(yes bool) *Query { + q.strictBuiltinErrors = yes + return q +} + +// WithBuiltinErrorList supplies a pointer to an Error slice to store built-in function errors +// encountered during evaluation. This error slice can be inspected after evaluation to determine +// which built-in function errors occurred. +func (q *Query) WithBuiltinErrorList(list *[]Error) *Query { + q.builtinErrorList = list + return q +} + +// WithResolver configures an external resolver to use for the given ref. +func (q *Query) WithResolver(ref ast.Ref, r resolver.Resolver) *Query { + if q.external == nil { + q.external = newResolverTrie() + } + q.external.Put(ref, r) + return q +} + +// WithHTTPRoundTripper configures a custom HTTP transport for built-in functions that make HTTP requests. +func (q *Query) WithHTTPRoundTripper(t CustomizeRoundTripper) *Query { + q.roundTripper = t + return q +} + +func (q *Query) WithPrintHook(h print.Hook) *Query { + q.printHook = h + return q +} + +// WithDistributedTracingOpts sets the options to be used by distributed tracing. +func (q *Query) WithDistributedTracingOpts(tr tracing.Options) *Query { + q.tracingOpts = tr + return q +} + +// WithStrictObjects tells the evaluator to avoid the "lazy object" optimization +// applied when reading objects from the store. It will result in higher memory +// usage and should only be used temporarily while adjusting code that breaks +// because of the optimization. +func (q *Query) WithStrictObjects(yes bool) *Query { + q.strictObjects = yes + return q +} + +// WithVirtualCache sets the VirtualCache to use during evaluation. This is +// optional, and if not set, the default cache is used. +func (q *Query) WithVirtualCache(vc VirtualCache) *Query { + q.virtualCache = vc + return q +} + +// WithBaseCache sets the BaseCache to use during evaluation. This is +// optional, and if not set, the default cache is used. +func (q *Query) WithBaseCache(bc BaseCache) *Query { + q.baseCache = bc + return q +} + +// WithNondeterministicBuiltins causes non-deterministic builtins to be evalued +// during partial evaluation. This is needed to pull in external data, or validate +// a JWT, during PE, so that the result informs what queries are returned. +func (q *Query) WithNondeterministicBuiltins(yes bool) *Query { + q.nondeterministicBuiltins = yes + return q +} + +// PartialRun executes partial evaluation on the query with respect to unknown +// values. Partial evaluation attempts to evaluate as much of the query as +// possible without requiring values for the unknowns set on the query. The +// result of partial evaluation is a new set of queries that can be evaluated +// once the unknown value is known. In addition to new queries, partial +// evaluation may produce additional support modules that should be used in +// conjunction with the partially evaluated queries. +func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support []*ast.Module, err error) { + if q.partialNamespace == "" { + q.partialNamespace = "partial" // lazily initialize partial namespace + } + if q.seed == nil { + q.seed = rand.Reader + } + if q.time.IsZero() { + q.time = time.Now() + } + if q.metrics == nil { + q.metrics = metrics.New() + } + + f := &queryIDFactory{} + b := newBindings(0, q.instr) + + var vc VirtualCache + if q.virtualCache != nil { + vc = q.virtualCache + } else { + vc = NewVirtualCache() + } + + var bc BaseCache + if q.baseCache != nil { + bc = q.baseCache + } else { + bc = newBaseCache() + } + + e := &eval{ + ctx: ctx, + metrics: q.metrics, + seed: q.seed, + time: ast.NumberTerm(int64ToJSONNumber(q.time.UnixNano())), + cancel: q.cancel, + query: q.query, + queryCompiler: q.queryCompiler, + queryIDFact: f, + queryID: f.Next(), + bindings: b, + compiler: q.compiler, + store: q.store, + baseCache: bc, + txn: q.txn, + input: q.input, + external: q.external, + tracers: q.tracers, + traceEnabled: len(q.tracers) > 0, + plugTraceVars: q.plugTraceVars, + instr: q.instr, + builtins: q.builtins, + builtinCache: builtins.Cache{}, + interQueryBuiltinCache: q.interQueryBuiltinCache, + interQueryBuiltinValueCache: q.interQueryBuiltinValueCache, + ndBuiltinCache: q.ndBuiltinCache, + virtualCache: vc, + saveSet: newSaveSet(q.unknowns, b, q.instr), + saveStack: newSaveStack(), + saveSupport: newSaveSupport(), + saveNamespace: ast.InternedTerm(q.partialNamespace), + skipSaveNamespace: q.skipSaveNamespace, + inliningControl: &inliningControl{ + shallow: q.shallowInlining, + nondeterministicBuiltins: q.nondeterministicBuiltins, + }, + genvarprefix: q.genvarprefix, + runtime: q.runtime, + indexing: q.indexing, + earlyExit: q.earlyExit, + builtinErrors: &builtinErrors{}, + printHook: q.printHook, + strictObjects: q.strictObjects, + } + + if len(q.disableInlining) > 0 { + e.inliningControl.PushDisable(q.disableInlining, false) + } + + e.caller = e + q.metrics.Timer(metrics.RegoPartialEval).Start() + defer q.metrics.Timer(metrics.RegoPartialEval).Stop() + + livevars := ast.NewVarSet() + for _, t := range q.unknowns { + switch v := t.Value.(type) { + case ast.Var: + livevars.Add(v) + case ast.Ref: + livevars.Add(v[0].Value.(ast.Var)) + } + } + + ast.WalkVars(q.query, func(x ast.Var) bool { + if !x.IsGenerated() { + livevars.Add(x) + } + return false + }) + + p := copypropagation.New(livevars).WithCompiler(q.compiler) + + err = e.Run(func(e *eval) error { + + // Build output from saved expressions. + body := ast.NewBody() + + for _, elem := range e.saveStack.Stack[len(e.saveStack.Stack)-1] { + body.Append(elem.Plug(e.bindings)) + } + + // Include bindings as exprs so that when caller evals the result, they + // can obtain values for the vars in their query. + bindingExprs := []*ast.Expr{} + _ = e.bindings.Iter(e.bindings, func(a, b *ast.Term) error { + bindingExprs = append(bindingExprs, ast.Equality.Expr(a, b)) + return nil + }) // cannot return error + + // Sort binding expressions so that results are deterministic. + sort.Slice(bindingExprs, func(i, j int) bool { + return bindingExprs[i].Compare(bindingExprs[j]) < 0 + }) + + for i := range bindingExprs { + body.Append(bindingExprs[i]) + } + + // Skip this rule body if it fails to type-check. + // Type-checking failure means the rule body will never succeed. + if !e.compiler.PassesTypeCheck(body) { + return nil + } + + if !q.shallowInlining { + body = applyCopyPropagation(p, e.instr, body) + } + + partials = append(partials, body) + return nil + }) + + support = e.saveSupport.List() + + if len(e.builtinErrors.errs) > 0 { + if q.strictBuiltinErrors { + err = e.builtinErrors.errs[0] + } else if q.builtinErrorList != nil { + // If a builtinErrorList has been supplied, we must use pointer indirection + // to append to it. builtinErrorList is a slice pointer so that errors can be + // appended to it without returning a new slice and changing the interface + // of PartialRun. + for _, err := range e.builtinErrors.errs { + if tdError, ok := err.(*Error); ok { + *(q.builtinErrorList) = append(*(q.builtinErrorList), *tdError) + } else { + *(q.builtinErrorList) = append(*(q.builtinErrorList), Error{ + Code: BuiltinErr, + Message: err.Error(), + }) + } + } + } + } + + for i, m := range support { + if regoVersion := q.compiler.DefaultRegoVersion(); regoVersion != ast.RegoUndefined { + ast.SetModuleRegoVersion(m, q.compiler.DefaultRegoVersion()) + } + + sort.Slice(support[i].Rules, func(j, k int) bool { + return support[i].Rules[j].Compare(support[i].Rules[k]) < 0 + }) + } + + return partials, support, err +} + +// Run is a wrapper around Iter that accumulates query results and returns them +// in one shot. +func (q *Query) Run(ctx context.Context) (QueryResultSet, error) { + qrs := QueryResultSet{} + return qrs, q.Iter(ctx, func(qr QueryResult) error { + qrs = append(qrs, qr) + return nil + }) +} + +// Iter executes the query and invokes the iter function with query results +// produced by evaluating the query. +func (q *Query) Iter(ctx context.Context, iter func(QueryResult) error) error { + // Query evaluation must not be allowed if the compiler has errors and is in an undefined, possibly inconsistent state + if q.compiler != nil && len(q.compiler.Errors) > 0 { + return &Error{ + Code: InternalErr, + Message: "compiler has errors", + } + } + + if q.seed == nil { + q.seed = rand.Reader + } + if q.time.IsZero() { + q.time = time.Now() + } + if q.metrics == nil { + q.metrics = metrics.New() + } + + f := &queryIDFactory{} + + var vc VirtualCache + if q.virtualCache != nil { + vc = q.virtualCache + } else { + vc = NewVirtualCache() + } + + var bc BaseCache + if q.baseCache != nil { + bc = q.baseCache + } else { + bc = newBaseCache() + } + + e := &eval{ + ctx: ctx, + metrics: q.metrics, + seed: q.seed, + time: ast.NumberTerm(int64ToJSONNumber(q.time.UnixNano())), + cancel: q.cancel, + query: q.query, + queryCompiler: q.queryCompiler, + queryIDFact: f, + queryID: f.Next(), + bindings: newBindings(0, q.instr), + compiler: q.compiler, + store: q.store, + baseCache: bc, + txn: q.txn, + input: q.input, + external: q.external, + tracers: q.tracers, + traceEnabled: len(q.tracers) > 0, + plugTraceVars: q.plugTraceVars, + instr: q.instr, + builtins: q.builtins, + builtinCache: builtins.Cache{}, + interQueryBuiltinCache: q.interQueryBuiltinCache, + interQueryBuiltinValueCache: q.interQueryBuiltinValueCache, + ndBuiltinCache: q.ndBuiltinCache, + virtualCache: vc, + genvarprefix: q.genvarprefix, + runtime: q.runtime, + indexing: q.indexing, + earlyExit: q.earlyExit, + builtinErrors: &builtinErrors{}, + printHook: q.printHook, + tracingOpts: q.tracingOpts, + strictObjects: q.strictObjects, + roundTripper: q.roundTripper, + } + e.caller = e + q.metrics.Timer(metrics.RegoQueryEval).Start() + err := e.Run(func(e *eval) error { + qr := QueryResult{} + _ = e.bindings.Iter(nil, func(k, v *ast.Term) error { + qr[k.Value.(ast.Var)] = v + return nil + }) // cannot return error + return iter(qr) + }) + + if len(e.builtinErrors.errs) > 0 { + if q.strictBuiltinErrors { + err = e.builtinErrors.errs[0] + } else if q.builtinErrorList != nil { + // If a builtinErrorList has been supplied, we must use pointer indirection + // to append to it. builtinErrorList is a slice pointer so that errors can be + // appended to it without returning a new slice and changing the interface + // of Iter. + for _, err := range e.builtinErrors.errs { + if tdError, ok := err.(*Error); ok { + *(q.builtinErrorList) = append(*(q.builtinErrorList), *tdError) + } else { + *(q.builtinErrorList) = append(*(q.builtinErrorList), Error{ + Code: BuiltinErr, + Message: err.Error(), + }) + } + } + } + } + + q.metrics.Timer(metrics.RegoQueryEval).Stop() + return err +} diff --git a/third_party/opa/v1/topdown/query_test.go b/third_party/opa/v1/topdown/query_test.go new file mode 100644 index 000000000000..69716b01810a --- /dev/null +++ b/third_party/opa/v1/topdown/query_test.go @@ -0,0 +1,320 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" +) + +func TestQueryTracerDontPlugLocalVars(t *testing.T) { + t.Parallel() + + cases := []struct { + note string + tracerConfs []TraceConfig + expectLocals bool + }{ + { + note: "plug locals single tracer", + tracerConfs: []TraceConfig{ + {PlugLocalVars: true}, + }, + expectLocals: true, + }, + { + note: "dont plug locals single tracer", + tracerConfs: []TraceConfig{ + {PlugLocalVars: false}, + }, + expectLocals: false, + }, + { + note: "plug locals multiple tracers", + tracerConfs: []TraceConfig{ + {PlugLocalVars: true}, + {PlugLocalVars: true}, + {PlugLocalVars: true}, + }, + expectLocals: true, + }, + { + note: "dont plug locals multiple tracers", + tracerConfs: []TraceConfig{ + {PlugLocalVars: false}, + {PlugLocalVars: false}, + {PlugLocalVars: false}, + }, + expectLocals: false, + }, + { + note: "plug locals multiple plugins mixed", + tracerConfs: []TraceConfig{ + {PlugLocalVars: false}, + {PlugLocalVars: true}, + {PlugLocalVars: false}, + }, + expectLocals: true, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + query := initTracerTestQuery() + + var tracers []*testQueryTracer + for _, conf := range tc.tracerConfs { + tt := &testQueryTracer{ + events: []*Event{}, + conf: conf, + enabled: true, + t: t, + } + tracers = append(tracers, tt) + + query = query.WithQueryTracer(tt) + } + + _, err := query.Run(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Even if the individual tracer didn't specify for local metadata + // they will _all_ either have it or not. + for _, tt := range tracers { + for _, e := range tt.events { + if !tc.expectLocals && e.LocalMetadata != nil { + t.Fatalf("Expected event LocalMetadata to nil") + } + if tc.expectLocals && e.LocalMetadata == nil { + t.Fatalf("Expected event LocalMetadata to be non-nil") + } + } + } + }) + } +} + +func TestLegacyTracerUpgrade(t *testing.T) { + t.Parallel() + + query := initTracerTestQuery() + + tracer := &testQueryTracer{ + events: []*Event{}, + conf: TraceConfig{PlugLocalVars: false}, + enabled: true, + t: t, + } + + // Call with older API, expect to be "upgraded" to QueryTracer + // If the deprecated Trace() API is called the test will fail. + query.WithTracer(tracer) + + _, err := query.Run(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } +} + +func TestLegacyTracerBackwardsCompatibility(t *testing.T) { + t.Parallel() + + query := initTracerTestQuery() + + // Using a tracer that does _not_ implement the newer + // QueryTracer interface, only the deprecated Tracer one. + tracer := &testLegacyTracer{ + events: []*Event{}, + } + + query.WithTracer(tracer) + + // For comparison use a buffer tracer and the new interface + bt := NewBufferTracer() + query.WithQueryTracer(bt) + + _, err := query.Run(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(*bt) != len(tracer.events) { + t.Fatalf("Expected %d events on the test tracer, got %d", len(*bt), len(tracer.events)) + } + + if !reflect.DeepEqual([]*Event(*bt), tracer.events) { + t.Fatalf("Expected same events on test tracer and BufferTracer") + } +} + +func TestDisabledTracer(t *testing.T) { + t.Parallel() + + query := initTracerTestQuery() + + tracer := &testQueryTracer{ + events: []*Event{}, + conf: TraceConfig{PlugLocalVars: false}, + enabled: false, + t: t, + } + + // Both API's should ignore the disabled tracer + query.WithTracer(tracer) + query.WithQueryTracer(tracer) + + _, err := query.Run(context.Background()) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(tracer.events) > 0 { + t.Fatalf("Expected no events on test tracer, got %d", len(tracer.events)) + } +} + +func TestRegoMetadataBuiltinCall(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + query string + expectedError string + }{ + { + note: "rego.metadata.chain() call", + query: "rego.metadata.chain()", + expectedError: "rego.metadata.chain(): eval_builtin_error: rego.metadata.chain: the rego.metadata.chain function must only be called within the scope of a rule", + }, + { + note: "rego.metadata.rule() call", + query: "rego.metadata.rule()", + expectedError: "rego.metadata.rule(): eval_builtin_error: rego.metadata.rule: the rego.metadata.rule function must only be called within the scope of a rule", + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + c := ast.NewCompiler() + q := NewQuery(ast.MustParseBody(tc.query)).WithCompiler(c). + WithStrictBuiltinErrors(true) + _, err := q.Run(context.Background()) + + if err == nil { + t.Fatalf("expected error") + } + + if tc.expectedError != err.Error() { + t.Fatalf("expected error:\n\n%s\n\ngot:\n\n%s", tc.expectedError, err.Error()) + } + }) + } +} + +func TestWithCompilerErrors(t *testing.T) { + t.Parallel() + + store := inmem.New() + ctx := context.Background() + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + // Policy with reference to non-existing function + modules := map[string]*ast.Module{ + "tst": ast.MustParseModule(`package test +p := data.q(42)`), + } + + c := ast.NewCompiler() + c.Compile(modules) + q := NewQuery(ast.MustParseBody("data.a = 1")). + WithCompiler(c). + WithStore(store). + WithTransaction(txn) + + _, err := q.Run(context.Background()) + if err == nil { + t.Fatalf("expected error, got nil") + } + expected := "eval_internal_error: compiler has errors" + if !strings.Contains(err.Error(), expected) { + t.Fatalf("expected error to contain '%s', got: %v", expected, err) + } +} + +func initTracerTestQuery() *Query { + ctx := context.Background() + store := inmem.New() + inputTerm := &ast.Term{} + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + compiler := compileModules([]string{ + `package x + + p if { + a := [1, 2, 3] + f(a[_]) + } + + f(x) if { + x == 3 + } + + `}) + + return NewQuery(ast.MustParseBody("data.x.p")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithInput(inputTerm) +} + +type testQueryTracer struct { + events []*Event + conf TraceConfig + enabled bool + t *testing.T +} + +func (n *testQueryTracer) Enabled() bool { + return n.enabled +} + +func (n *testQueryTracer) Trace(e *Event) { + n.t.Errorf("Unexpected call to Trace() with event %v", e) +} + +func (n *testQueryTracer) TraceEvent(e Event) { + n.events = append(n.events, &e) +} + +func (n *testQueryTracer) Config() TraceConfig { + return n.conf +} + +type testLegacyTracer struct { + events []*Event +} + +func (*testLegacyTracer) Enabled() bool { + return true +} + +func (n *testLegacyTracer) Trace(e *Event) { + n.events = append(n.events, e) +} diff --git a/third_party/opa/v1/topdown/reachable.go b/third_party/opa/v1/topdown/reachable.go new file mode 100644 index 000000000000..1c31019db9e7 --- /dev/null +++ b/third_party/opa/v1/topdown/reachable.go @@ -0,0 +1,151 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +// Helper: sets of vertices can be represented as Arrays or Sets. +func foreachVertex(collection *ast.Term, f func(*ast.Term)) { + switch v := collection.Value.(type) { + case ast.Set: + v.Foreach(f) + case *ast.Array: + v.Foreach(f) + } +} + +// numberOfEdges returns the number of elements of an array or a set (of edges) +func numberOfEdges(collection *ast.Term) int { + switch v := collection.Value.(type) { + case ast.Set: + return v.Len() + case *ast.Array: + return v.Len() + } + + return 0 +} + +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 { + return err + } + + var queue []*ast.Term + switch initial := operands[1].Value.(type) { + case *ast.Array, ast.Set: + foreachVertex(ast.NewTerm(initial), func(t *ast.Term) { + queue = append(queue, t) + }) + default: + return builtins.NewOperandTypeErr(2, initial, "{array, set}") + } + + // This is the set of nodes we have reached. + reached := ast.NewSet() + + // Keep going as long as we have nodes in the queue. + for len(queue) > 0 { + // Get the edges for this node. If the node was not in the graph, + // `edges` will be `nil` and we can ignore it. + node := queue[0] + if edges := graph.Get(node); edges != nil { + // Add all the newly discovered neighbors. + foreachVertex(edges, func(neighbor *ast.Term) { + if !reached.Contains(neighbor) { + queue = append(queue, neighbor) + } + }) + // Mark the node as reached. + reached.Add(node) + } + queue = queue[1:] + } + + return iter(ast.NewTerm(reached)) +} + +// pathBuilder is called recursively to build a Set of paths that are reachable from the root +func pathBuilder(graph ast.Object, root *ast.Term, path []*ast.Term, edgeRslt ast.Set, reached ast.Set) { + paths := []*ast.Term{} + + if edges := graph.Get(root); edges != nil { + path = append(path, root) + + if numberOfEdges(edges) >= 1 { + + foreachVertex(edges, func(neighbor *ast.Term) { + + if reached.Contains(neighbor) { + // If we've already reached this node, return current path (avoid infinite recursion) + paths = append(paths, path...) + edgeRslt.Add(ast.ArrayTerm(paths...)) + } else { + reached.Add(root) + pathBuilder(graph, neighbor, path, edgeRslt, reached) + + } + + }) + + } else { + paths = append(paths, path...) + edgeRslt.Add(ast.ArrayTerm(paths...)) + + } + } else { + // Node is nonexistent (not in graph). Commit the current path (without adding this root) + paths = append(paths, path...) + edgeRslt.Add(ast.ArrayTerm(paths...)) + + } + +} + +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) + if err != nil { + return err + } + + // This is a queue that holds all nodes we still need to visit. It is + // initialised to the initial set of nodes we start out with. + var queue []*ast.Term + switch initial := operands[1].Value.(type) { + case *ast.Array, ast.Set: + foreachVertex(ast.NewTerm(initial), func(t *ast.Term) { + queue = append(queue, t) + }) + default: + return builtins.NewOperandTypeErr(2, initial, "{array, set}") + } + + for _, node := range queue { + // Find reachable paths from edges in root node in queue and append arrays to the results set + if edges := graph.Get(node); edges != nil { + if numberOfEdges(edges) >= 1 { + foreachVertex(edges, func(neighbor *ast.Term) { + pathBuilder(graph, neighbor, []*ast.Term{node}, traceResult, ast.NewSet(node)) + }) + } else { + traceResult.Add(ast.ArrayTerm(node)) + } + } + } + + return iter(ast.NewTerm(traceResult)) +} + +func init() { + RegisterBuiltinFunc(ast.ReachableBuiltin.Name, builtinReachable) + RegisterBuiltinFunc(ast.ReachablePathsBuiltin.Name, builtinReachablePaths) +} diff --git a/third_party/opa/v1/topdown/regex.go b/third_party/opa/v1/topdown/regex.go new file mode 100644 index 000000000000..1d2906ee2ee0 --- /dev/null +++ b/third_party/opa/v1/topdown/regex.go @@ -0,0 +1,281 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + "regexp" + "sync" + + gintersect "github.com/yashtewari/glob-intersection" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +const regexCacheMaxSize = 100 +const regexInterQueryValueCacheHits = "rego_builtin_regex_interquery_value_cache_hits" + +var regexpCacheLock = sync.Mutex{} +var regexpCache map[string]*regexp.Regexp + +func builtinRegexIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return iter(ast.InternedTerm(false)) + } + + _, err = regexp.Compile(string(s)) + if err != nil { + return iter(ast.InternedTerm(false)) + } + + return iter(ast.InternedTerm(true)) +} + +func builtinRegexMatch(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s1, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + s2, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + re, err := getRegexp(bctx, string(s1)) + if err != nil { + return err + } + return iter(ast.InternedTerm(re.MatchString(string(s2)))) +} + +func builtinRegexMatchTemplate(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + pattern, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + match, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + start, err := builtins.StringOperand(operands[2].Value, 3) + if err != nil { + return err + } + end, err := builtins.StringOperand(operands[3].Value, 4) + if err != nil { + return err + } + if len(start) != 1 { + return fmt.Errorf("start delimiter has to be exactly one character long but is %d long", len(start)) + } + if len(end) != 1 { + return fmt.Errorf("end delimiter has to be exactly one character long but is %d long", len(start)) + } + re, err := getRegexpTemplate(string(pattern), string(start)[0], string(end)[0]) + if err != nil { + return err + } + return iter(ast.InternedTerm(re.MatchString(string(match)))) +} + +func builtinRegexSplit(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s1, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + s2, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + re, err := getRegexp(bctx, string(s1)) + if err != nil { + return err + } + + elems := re.Split(string(s2), -1) + arr := make([]*ast.Term, len(elems)) + for i := range elems { + arr[i] = ast.StringTerm(elems[i]) + } + return iter(ast.ArrayTerm(arr...)) +} + +func getRegexp(bctx BuiltinContext, pat string) (*regexp.Regexp, error) { + if bctx.InterQueryBuiltinValueCache != nil { + // TODO: Use named cache + val, ok := bctx.InterQueryBuiltinValueCache.Get(ast.String(pat)) + if ok { + res, valid := val.(*regexp.Regexp) + if !valid { + // The cache key may exist for a different value type (eg. glob). + // In this case, we calculate the regex and return the result w/o updating the cache. + return regexp.Compile(pat) + } + + bctx.Metrics.Counter(regexInterQueryValueCacheHits).Incr() + return res, nil + } + + re, err := regexp.Compile(pat) + if err != nil { + return nil, err + } + bctx.InterQueryBuiltinValueCache.Insert(ast.String(pat), re) + return re, nil + } + + regexpCacheLock.Lock() + defer regexpCacheLock.Unlock() + re, ok := regexpCache[pat] + if !ok { + var err error + re, err = regexp.Compile(pat) + if err != nil { + return nil, err + } + if len(regexpCache) >= regexCacheMaxSize { + // Delete a (semi-)random key to make room for the new one. + for k := range regexpCache { + delete(regexpCache, k) + break + } + } + regexpCache[pat] = re + } + return re, nil +} + +func getRegexpTemplate(pat string, delimStart, delimEnd byte) (*regexp.Regexp, error) { + regexpCacheLock.Lock() + defer regexpCacheLock.Unlock() + re, ok := regexpCache[pat] + if !ok { + var err error + re, err = compileRegexTemplate(pat, delimStart, delimEnd) + if err != nil { + return nil, err + } + regexpCache[pat] = re + } + return re, nil +} + +func builtinGlobsMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s1, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + s2, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + ne, err := gintersect.NonEmpty(string(s1), string(s2)) + if err != nil { + return err + } + return iter(ast.InternedTerm(ne)) +} + +func builtinRegexFind(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s1, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + s2, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + n, err := builtins.IntOperand(operands[2].Value, 3) + if err != nil { + return err + } + re, err := getRegexp(bctx, string(s1)) + if err != nil { + return err + } + + elems := re.FindAllString(string(s2), n) + arr := make([]*ast.Term, len(elems)) + for i := range elems { + arr[i] = ast.StringTerm(elems[i]) + } + return iter(ast.ArrayTerm(arr...)) +} + +func builtinRegexFindAllStringSubmatch(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s1, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + s2, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + n, err := builtins.IntOperand(operands[2].Value, 3) + if err != nil { + return err + } + + re, err := getRegexp(bctx, string(s1)) + if err != nil { + return err + } + matches := re.FindAllStringSubmatch(string(s2), n) + + outer := make([]*ast.Term, len(matches)) + for i := range matches { + inner := make([]*ast.Term, len(matches[i])) + for j := range matches[i] { + inner[j] = ast.StringTerm(matches[i][j]) + } + outer[i] = ast.ArrayTerm(inner...) + } + + return iter(ast.ArrayTerm(outer...)) +} + +func builtinRegexReplace(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + base, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + pattern, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + value, err := builtins.StringOperand(operands[2].Value, 3) + if err != nil { + return err + } + + re, err := getRegexp(bctx, string(pattern)) + if err != nil { + return err + } + + res := re.ReplaceAllString(string(base), string(value)) + if res == string(base) { + return iter(operands[0]) + } + + return iter(ast.InternedTerm(res)) +} + +func init() { + regexpCache = map[string]*regexp.Regexp{} + RegisterBuiltinFunc(ast.RegexIsValid.Name, builtinRegexIsValid) + RegisterBuiltinFunc(ast.RegexMatch.Name, builtinRegexMatch) + RegisterBuiltinFunc(ast.RegexMatchDeprecated.Name, builtinRegexMatch) + RegisterBuiltinFunc(ast.RegexSplit.Name, builtinRegexSplit) + RegisterBuiltinFunc(ast.GlobsMatch.Name, builtinGlobsMatch) + RegisterBuiltinFunc(ast.RegexTemplateMatch.Name, builtinRegexMatchTemplate) + RegisterBuiltinFunc(ast.RegexFind.Name, builtinRegexFind) + RegisterBuiltinFunc(ast.RegexFindAllStringSubmatch.Name, builtinRegexFindAllStringSubmatch) + RegisterBuiltinFunc(ast.RegexReplace.Name, builtinRegexReplace) +} diff --git a/third_party/opa/v1/topdown/regex_bench_test.go b/third_party/opa/v1/topdown/regex_bench_test.go new file mode 100644 index 000000000000..c9d63a0a3575 --- /dev/null +++ b/third_party/opa/v1/topdown/regex_bench_test.go @@ -0,0 +1,95 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + "regexp" + "sync" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +var reuseOperands = []*ast.Term{ + ast.NewTerm(ast.String("foo.*")), + ast.NewTerm(ast.String("foobar")), +} + +func BenchmarkBuiltinRegexMatch(b *testing.B) { + iter := func(*ast.Term) error { return nil } + ctx := BuiltinContext{} + + for _, reusePattern := range []bool{true, false} { + for _, patternCount := range []int{10, 100, 1000} { + b.Run(fmt.Sprintf("reuse-pattern=%v, pattern-count=%d", reusePattern, patternCount), func(b *testing.B) { + b.ResetTimer() + for range b.N { + // Clearing the cache + regexpCache = make(map[string]*regexp.Regexp) + + for i := range patternCount { + var operands []*ast.Term + if reusePattern { + operands = reuseOperands + } else { + operands = []*ast.Term{ + ast.NewTerm(ast.String(fmt.Sprintf("foo%d.*", i))), + ast.NewTerm(ast.String(fmt.Sprintf("foo%dbar", i))), + } + } + if err := builtinRegexMatch(ctx, operands, iter); err != nil { + b.Fatal(err) + } + } + } + }) + } + } +} + +func BenchmarkBuiltinRegexMatchAsync(b *testing.B) { + iter := func(*ast.Term) error { return nil } + ctx := BuiltinContext{} + + for _, reusePattern := range []bool{true, false} { + for _, clientCount := range []int{100, 200} { + for _, patternCount := range []int{10, 100, 1000} { + b.Run(fmt.Sprintf("reuse-pattern=%v, clients=%d, pattern-count=%d", reusePattern, clientCount, patternCount), func(b *testing.B) { + b.ResetTimer() + for range b.N { + // Clearing the cache + regexpCache = make(map[string]*regexp.Regexp) + + wg := sync.WaitGroup{} + for i := range clientCount { + clientID := i + wg.Add(1) + go func() { + for j := range patternCount { + var operands []*ast.Term + if reusePattern { + operands = reuseOperands + } else { + operands = []*ast.Term{ + ast.NewTerm(ast.String(fmt.Sprintf("foo%d_%d.*", clientID, j))), + ast.NewTerm(ast.String(fmt.Sprintf("foo%d_%dbar", clientID, j))), + } + } + if err := builtinRegexMatch(ctx, operands, iter); err != nil { + b.Error(err) + return + } + } + wg.Done() + }() + } + wg.Wait() + } + }) + } + } + } +} diff --git a/third_party/opa/v1/topdown/regex_template.go b/third_party/opa/v1/topdown/regex_template.go new file mode 100644 index 000000000000..a1d946fd59e6 --- /dev/null +++ b/third_party/opa/v1/topdown/regex_template.go @@ -0,0 +1,122 @@ +package topdown + +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license as follows: + +// Copyright (c) 2012 Rodrigo Moraes. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file was forked from https://github.com/gorilla/mux/commit/eac83ba2c004bb75 + +import ( + "bytes" + "fmt" + "regexp" +) + +// delimiterIndices returns the first level delimiter indices from a string. +// It returns an error in case of unbalanced delimiters. +func delimiterIndices(s string, delimiterStart, delimiterEnd byte) ([]int, error) { + var level, idx int + idxs := make([]int, 0) + for i := range len(s) { + switch s[i] { + case delimiterStart: + if level++; level == 1 { + idx = i + } + case delimiterEnd: + if level--; level == 0 { + idxs = append(idxs, idx, i+1) + } else if level < 0 { + return nil, fmt.Errorf(`unbalanced braces in %q`, s) + } + } + } + + if level != 0 { + return nil, fmt.Errorf(`unbalanced braces in %q`, s) + } + + return idxs, nil +} + +// compileRegexTemplate parses a template and returns a Regexp. +// +// You can define your own delimiters. It is e.g. common to use curly braces {} but I recommend using characters +// which have no special meaning in Regex, e.g.: <, > +// +// reg, err := compiler.CompileRegex("foo:bar.baz:<[0-9]{2,10}>", '<', '>') +// // if err != nil ... +// reg.MatchString("foo:bar.baz:123") +func compileRegexTemplate(tpl string, delimiterStart, delimiterEnd byte) (*regexp.Regexp, error) { + // Check if it is well-formed. + idxs, errBraces := delimiterIndices(tpl, delimiterStart, delimiterEnd) + if errBraces != nil { + return nil, errBraces + } + varsR := make([]*regexp.Regexp, len(idxs)/2) + pattern := bytes.NewBufferString("") + + // WriteByte's error value is always nil for bytes.Buffer, no need to check it. + pattern.WriteByte('^') + + var end int + var err error + for i := 0; i < len(idxs); i += 2 { + // Set all values we are interested in. + raw := tpl[end:idxs[i]] + end = idxs[i+1] + patt := tpl[idxs[i]+1 : end-1] + // Build the regexp pattern. + varIdx := i / 2 + fmt.Fprintf(pattern, "%s(%s)", regexp.QuoteMeta(raw), patt) + varsR[varIdx], err = regexp.Compile(fmt.Sprintf("^%s$", patt)) + if err != nil { + return nil, err + } + } + + // Add the remaining. + raw := tpl[end:] + + // WriteString's error value is always nil for bytes.Buffer, no need to check it. + pattern.WriteString(regexp.QuoteMeta(raw)) + + // WriteByte's error value is always nil for bytes.Buffer, no need to check it. + pattern.WriteByte('$') + + // Compile full regexp. + reg, errCompile := regexp.Compile(pattern.String()) + if errCompile != nil { + return nil, errCompile + } + + return reg, nil +} diff --git a/third_party/opa/v1/topdown/regex_template_test.go b/third_party/opa/v1/topdown/regex_template_test.go new file mode 100644 index 000000000000..33b94c6ee8c2 --- /dev/null +++ b/third_party/opa/v1/topdown/regex_template_test.go @@ -0,0 +1,49 @@ +package topdown + +import ( + "regexp" + "testing" +) + +func TestRegexCompiler(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + template string + delimiterStart byte + delimiterEnd byte + failCompile bool + matchAgainst string + failMatch bool + }{ + {"urn:foo:{.*}", '{', '}', false, "urn:foo:bar:baz", false}, + {"urn:foo.bar.com:{.*}", '{', '}', false, "urn:foo.bar.com:bar:baz", false}, + {"urn:foo.bar.com:{.*}", '{', '}', false, "urn:foo.com:bar:baz", true}, + {"urn:foo.bar.com:{.*}", '{', '}', false, "foobar", true}, + {"urn:foo.bar.com:{.{1,2}}", '{', '}', false, "urn:foo.bar.com:aa", false}, + {"urn:foo.bar.com:{.*{}", '{', '}', true, "", true}, + {"urn:foo:<.*>", '<', '>', false, "urn:foo:bar:baz", false}, + } { + t.Run("template="+tc.template, func(t *testing.T) { + t.Parallel() + + result, err := compileRegexTemplate(tc.template, tc.delimiterStart, tc.delimiterEnd) + if tc.failCompile != (err != nil) { + t.Fatalf("failed regex template compilation: %t != %t", tc.failCompile, err != nil) + } + + if tc.failCompile || err != nil { + return + } + + ok, err := regexp.MatchString(result.String(), tc.matchAgainst) + if err != nil { + t.Fatalf("unexpected error while matching string: %s", err) + } + + if !tc.failMatch != ok { + t.Logf("match result %t is not expected value %t", ok, !tc.failMatch) + } + }) + } +} diff --git a/third_party/opa/v1/topdown/regex_test.go b/third_party/opa/v1/topdown/regex_test.go new file mode 100644 index 000000000000..c38060bb757c --- /dev/null +++ b/third_party/opa/v1/topdown/regex_test.go @@ -0,0 +1,163 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/cache" +) + +func TestRegexBuiltinCache(t *testing.T) { + t.Parallel() + + ctx := BuiltinContext{} + iter := func(*ast.Term) error { return nil } + + // A novel regex pattern is cached. + regex1 := "foo.*" + operands := []*ast.Term{ + ast.NewTerm(ast.String(regex1)), + ast.NewTerm(ast.String("foobar")), + } + err := builtinRegexMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if _, ok := regexpCache[regex1]; !ok { + t.Fatalf("Expected regex to be cached: %v", regex1) + } + + // Fill up the cache. + for i := range regexCacheMaxSize - 1 { + operands := []*ast.Term{ + ast.NewTerm(ast.String(fmt.Sprintf("foo%d.*", i))), + ast.NewTerm(ast.String(fmt.Sprintf("foo%dbar", i))), + } + err := builtinRegexMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + if len(regexpCache) != regexCacheMaxSize { + t.Fatalf("Expected cache to be full") + } + + // A new regex pattern is cached and a random pattern is evicted. + regex2 := "bar.*" + operands = []*ast.Term{ + ast.NewTerm(ast.String(regex2)), + ast.NewTerm(ast.String("barbaz")), + } + err = builtinRegexMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(regexpCache) != regexCacheMaxSize { + t.Fatalf("Expected cache be capped at %d, was %d", regexCacheMaxSize, len(regexpCache)) + } + + if _, ok := regexpCache[regex2]; !ok { + t.Fatalf("Expected regex to be cached: %v", regex2) + } +} + +func TestRegexBuiltinInterQueryValueCache(t *testing.T) { + t.Parallel() + + ip := []byte(`{"inter_query_builtin_value_cache": {"max_num_entries": "10"},}`) + config, _ := cache.ParseCachingConfig(ip) + interQueryValueCache := cache.NewInterQueryValueCache(context.Background(), config) + + ctx := BuiltinContext{InterQueryBuiltinValueCache: interQueryValueCache} + iter := func(*ast.Term) error { return nil } + + // A novel regex pattern is cached. + regex1 := "foo.*" + operands := []*ast.Term{ + ast.NewTerm(ast.String(regex1)), + ast.NewTerm(ast.String("foobar")), + } + err := builtinRegexMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if _, ok := ctx.InterQueryBuiltinValueCache.Get(ast.StringTerm(regex1).Value); !ok { + t.Fatalf("Expected regex to be cached: %v", regex1) + } + + // Fill up the cache. + for i := range 9 { + operands := []*ast.Term{ + ast.NewTerm(ast.String(fmt.Sprintf("foo%d.*", i))), + ast.NewTerm(ast.String(fmt.Sprintf("foo%dbar", i))), + } + err := builtinRegexMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + } + + // A new regex pattern is cached and a random pattern is evicted. + regex2 := "bar.*" + operands = []*ast.Term{ + ast.NewTerm(ast.String(regex2)), + ast.NewTerm(ast.String("barbaz")), + } + err = builtinRegexMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if _, ok := ctx.InterQueryBuiltinValueCache.Get(ast.StringTerm(regex2).Value); !ok { + t.Fatalf("Expected regex to be cached: %v", regex2) + } +} + +func TestRegexBuiltinInterQueryValueCacheTypeMismatch(t *testing.T) { + t.Parallel() + + ip := []byte(`{"inter_query_builtin_value_cache": {"max_num_entries": "10"},}`) + config, _ := cache.ParseCachingConfig(ip) + interQueryValueCache := cache.NewInterQueryValueCache(context.Background(), config) + + ctx := BuiltinContext{InterQueryBuiltinValueCache: interQueryValueCache} + iter := func(*ast.Term) error { return nil } + + key := "foo.*" + + ctx.InterQueryBuiltinValueCache.Insert(ast.StringTerm(key).Value, "bar") + + operands := []*ast.Term{ + ast.NewTerm(ast.String(key)), + ast.NewTerm(ast.String("foobar")), + } + err := builtinRegexMatch(ctx, operands, iter) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // verify the original cache entry is unchanged + value, ok := ctx.InterQueryBuiltinValueCache.Get(ast.StringTerm(key).Value) + if !ok { + t.Fatal("Expected key \"foo.*\" in cache") + } + + actual, ok := value.(string) + if !ok { + t.Fatal("Expected string value") + } + + if actual != "bar" { + t.Fatalf("Expected value \"bar\" but got %v", actual) + } +} diff --git a/third_party/opa/v1/topdown/resolver.go b/third_party/opa/v1/topdown/resolver.go new file mode 100644 index 000000000000..8fff22b1d3ef --- /dev/null +++ b/third_party/opa/v1/topdown/resolver.go @@ -0,0 +1,118 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/resolver" +) + +type resolverTrie struct { + r resolver.Resolver + children map[ast.Value]*resolverTrie +} + +func newResolverTrie() *resolverTrie { + return &resolverTrie{children: map[ast.Value]*resolverTrie{}} +} + +func (t *resolverTrie) Put(ref ast.Ref, r resolver.Resolver) { + node := t + for _, t := range ref { + child, ok := node.children[t.Value] + if !ok { + child = &resolverTrie{children: map[ast.Value]*resolverTrie{}} + node.children[t.Value] = child + } + node = child + } + node.r = r +} + +func (t *resolverTrie) Resolve(e *eval, ref ast.Ref) (ast.Value, error) { + e.metrics.Timer(metrics.RegoExternalResolve).Start() + defer e.metrics.Timer(metrics.RegoExternalResolve).Stop() + + if t == nil { + return nil, nil + } + node := t + for i, t := range ref { + child, ok := node.children[t.Value] + if !ok { + return nil, nil + } + node = child + if node.r != nil { + in := resolver.Input{ + Ref: ref[:i+1], + Input: e.input, + Metrics: e.metrics, + } + if e.traceEnabled { + // avoid leaking pointer if trace is disabled + cpy := in.Ref + e.traceWasm(e.query[e.index], &cpy) + } + if e.data != nil { + return nil, errInScopeWithStmt + } + result, err := node.r.Eval(e.ctx, in) + if err != nil { + return nil, err + } + if result.Value == nil { + return nil, nil + } + val, err := result.Value.Find(ref[i+1:]) + if err != nil { + return nil, nil + } + return val, nil + } + } + return node.mktree(e, resolver.Input{ + Ref: ref, + Input: e.input, + Metrics: e.metrics, + }) +} + +func (t *resolverTrie) mktree(e *eval, in resolver.Input) (ast.Value, error) { + if t.r != nil { + if e.traceEnabled { + cpy := in.Ref + e.traceWasm(e.query[e.index], &cpy) + } + if e.data != nil { + return nil, errInScopeWithStmt + } + result, err := t.r.Eval(e.ctx, in) + if err != nil { + return nil, err + } + if result.Value == nil { + return nil, nil + } + return result.Value, nil + } + obj := ast.NewObject() + for k, child := range t.children { + v, err := child.mktree(e, resolver.Input{Ref: append(in.Ref, ast.NewTerm(k)), Input: in.Input, Metrics: in.Metrics}) + if err != nil { + return nil, err + } + if v != nil { + obj.Insert(ast.NewTerm(k), ast.NewTerm(v)) + } + } + return obj, nil +} + +var errInScopeWithStmt = &Error{ + Code: InternalErr, + Message: "wasm cannot be executed when 'with' statements are in-scope", +} diff --git a/third_party/opa/v1/topdown/runtime.go b/third_party/opa/v1/topdown/runtime.go new file mode 100644 index 000000000000..2bbfb43f39ba --- /dev/null +++ b/third_party/opa/v1/topdown/runtime.go @@ -0,0 +1,130 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "errors" + "fmt" + + "github.com/open-policy-agent/opa/v1/ast" +) + +var nothingResolver ast.Resolver = illegalResolver{} + +func builtinOPARuntime(bctx BuiltinContext, _ []*ast.Term, iter func(*ast.Term) error) error { + + if bctx.Runtime == nil { + return iter(ast.InternedEmptyObject) + } + + if bctx.Runtime.Get(ast.InternedTerm("config")) != nil { + iface, err := ast.ValueToInterface(bctx.Runtime.Value, nothingResolver) + if err != nil { + return err + } + if object, ok := iface.(map[string]any); ok { + if cfgRaw, ok := object["config"]; ok { + if config, ok := cfgRaw.(map[string]any); ok { + configPurged, err := activeConfig(config) + if err != nil { + return err + } + object["config"] = configPurged + value, err := ast.InterfaceToValue(object) + if err != nil { + return err + } + return iter(ast.NewTerm(value)) + } + } + } + } + + return iter(bctx.Runtime) +} + +func init() { + RegisterBuiltinFunc(ast.OPARuntime.Name, builtinOPARuntime) +} + +func activeConfig(config map[string]any) (any, error) { + + if config["services"] != nil { + err := removeServiceCredentials(config["services"]) + if err != nil { + return nil, err + } + } + + if config["keys"] != nil { + err := removeCryptoKeys(config["keys"]) + if err != nil { + return nil, err + } + } + + return config, nil +} + +func removeServiceCredentials(x any) error { + + switch x := x.(type) { + case []any: + for _, v := range x { + err := removeKey(v, "credentials") + if err != nil { + return err + } + } + + case map[string]any: + for _, v := range x { + err := removeKey(v, "credentials") + if err != nil { + return err + } + } + default: + return fmt.Errorf("illegal service config type: %T", x) + } + + return nil +} + +func removeCryptoKeys(x any) error { + + switch x := x.(type) { + case map[string]any: + for _, v := range x { + err := removeKey(v, "key", "private_key") + if err != nil { + return err + } + } + default: + return fmt.Errorf("illegal keys config type: %T", x) + } + + return nil +} + +func removeKey(x any, keys ...string) error { + val, ok := x.(map[string]any) + if !ok { + return errors.New("type assertion error") + } + + for _, key := range keys { + delete(val, key) + } + + return nil +} + +type illegalResolver struct{} + +func (illegalResolver) Resolve(ref ast.Ref) (any, error) { + return nil, fmt.Errorf("illegal value: %v", ref) +} diff --git a/third_party/opa/v1/topdown/runtime_test.go b/third_party/opa/v1/topdown/runtime_test.go new file mode 100644 index 000000000000..3641399f0ebc --- /dev/null +++ b/third_party/opa/v1/topdown/runtime_test.go @@ -0,0 +1,87 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. +package topdown + +import ( + "context" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestOPARuntime(t *testing.T) { + t.Parallel() + + ctx := context.Background() + q := NewQuery(ast.MustParseBody("opa.runtime(x)")) // no runtime info + rs, err := q.Run(ctx) + if err != nil { + t.Fatal(err) + } else if len(rs) != 1 { + t.Fatal("Expected result set to contain exactly one result") + } + + term := rs[0][ast.Var("x")] + exp := ast.ObjectTerm() + + if ast.Compare(term, exp) != 0 { + t.Fatalf("Expected %v but got %v", exp, term) + } + + q = NewQuery(ast.MustParseBody("opa.runtime(x)")).WithRuntime(ast.MustParseTerm(`{"config": {"a": 1}}`)) + rs, err = q.Run(ctx) + if err != nil { + t.Fatal(err) + } else if len(rs) != 1 { + t.Fatal("Expected result set to contain exactly one result") + } + + term = rs[0][ast.Var("x")] + exp = ast.MustParseTerm(`{"config": {"a": 1}}`) + + if ast.Compare(term, exp) != 0 { + t.Fatalf("Expected %v but got %v", exp, term) + } + +} + +func TestOPARuntimeConfigMasking(t *testing.T) { + t.Parallel() + + ctx := context.Background() + q := NewQuery(ast.MustParseBody("opa.runtime(x)")).WithRuntime(ast.MustParseTerm(`{"config": { + "labels": {"foo": "bar"}, + "services": { + "foo": { + "url": "https://remote.example.com", + "credentials": { + "oauth2": { + "client_id": "opa_client", + "client_secret": "sup3rs3cr3t" + } + } + } + } + }}`)) + rs, err := q.Run(ctx) + if err != nil { + t.Fatal(err) + } else if len(rs) != 1 { + t.Fatal("Expected result set to contain exactly one result") + } + + term := rs[0][ast.Var("x")] + exp := ast.MustParseTerm(`{"config": { + "labels": {"foo": "bar"}, + "services": { + "foo": { + "url": "https://remote.example.com" + } + } + }}`) + + if ast.Compare(term, exp) != 0 { + t.Fatalf("Expected %v but got %v", exp, term) + } +} diff --git a/third_party/opa/v1/topdown/save.go b/third_party/opa/v1/topdown/save.go new file mode 100644 index 000000000000..47bf7521b4a7 --- /dev/null +++ b/third_party/opa/v1/topdown/save.go @@ -0,0 +1,523 @@ +package topdown + +import ( + "cmp" + "container/list" + "fmt" + "slices" + "strings" + + "github.com/open-policy-agent/opa/v1/ast" +) + +// saveSet contains a stack of terms that are considered 'unknown' during +// partial evaluation. Only var and ref terms (rooted at one of the root +// documents) can be added to the save set. Vars added to the save set are +// namespaced by the binding list they are added with. This means the save set +// can be shared across queries. +type saveSet struct { + instr *Instrumentation + l *list.List +} + +func newSaveSet(ts []*ast.Term, b *bindings, instr *Instrumentation) *saveSet { + ss := &saveSet{ + l: list.New(), + instr: instr, + } + ss.Push(ts, b) + return ss +} + +func (ss *saveSet) Push(ts []*ast.Term, b *bindings) { + ss.l.PushBack(newSaveSetElem(ts, b)) +} + +func (ss *saveSet) Pop() { + ss.l.Remove(ss.l.Back()) +} + +// Contains returns true if the term t is contained in the save set. Non-var and +// non-ref terms are never contained. Ref terms are contained if they share a +// prefix with a ref that was added (in either direction). +func (ss *saveSet) Contains(t *ast.Term, b *bindings) bool { + if ss != nil { + ss.instr.startTimer(partialOpSaveSetContains) + ret := ss.contains(t, b) + ss.instr.stopTimer(partialOpSaveSetContains) + return ret + } + return false +} + +func (ss *saveSet) contains(t *ast.Term, b *bindings) bool { + for el := ss.l.Back(); el != nil; el = el.Prev() { + if el.Value.(*saveSetElem).Contains(t, b) { + return true + } + } + return false +} + +// ContainsRecursive returns true if the term t is or contains a term that is +// contained in the save set. This function will close over the binding list +// when it encounters vars. +func (ss *saveSet) ContainsRecursive(t *ast.Term, b *bindings) bool { + if ss != nil { + ss.instr.startTimer(partialOpSaveSetContainsRec) + ret := ss.containsrec(t, b) + ss.instr.stopTimer(partialOpSaveSetContainsRec) + return ret + } + return false +} + +func (ss *saveSet) containsrec(t *ast.Term, b *bindings) bool { + var found bool + ast.WalkTerms(t, func(x *ast.Term) bool { + if _, ok := x.Value.(ast.Var); ok { + x1, b1 := b.apply(x) + if x1 != x || b1 != b { + if ss.containsrec(x1, b1) { + found = true + } + } else if ss.contains(x1, b1) { + found = true + } + } + return found + }) + return found +} + +func (ss *saveSet) Vars(caller *bindings) ast.VarSet { + result := ast.NewVarSet() + for x := ss.l.Front(); x != nil; x = x.Next() { + elem := x.Value.(*saveSetElem) + for _, v := range elem.vars { + if v, ok := elem.b.PlugNamespaced(v, caller).Value.(ast.Var); ok { + result.Add(v) + } + } + } + return result +} + +func (ss *saveSet) String() string { + var buf []string + + for x := ss.l.Front(); x != nil; x = x.Next() { + buf = append(buf, x.Value.(*saveSetElem).String()) + } + + return "(" + strings.Join(buf, " ") + ")" +} + +type saveSetElem struct { + refs []ast.Ref + vars []*ast.Term + b *bindings +} + +func newSaveSetElem(ts []*ast.Term, b *bindings) *saveSetElem { + + var refs []ast.Ref + var vars []*ast.Term + + for _, t := range ts { + switch v := t.Value.(type) { + case ast.Var: + vars = append(vars, t) + case ast.Ref: + refs = append(refs, v) + default: + panic("illegal value") + } + } + + return &saveSetElem{ + b: b, + vars: vars, + refs: refs, + } +} + +func (sse *saveSetElem) Contains(t *ast.Term, b *bindings) bool { + switch other := t.Value.(type) { + case ast.Var: + return sse.containsVar(t, b) + case ast.Ref: + for _, ref := range sse.refs { + if ref.HasPrefix(other) || other.HasPrefix(ref) { + return true + } + } + return sse.containsVar(other[0], b) + } + return false +} + +func (sse *saveSetElem) String() string { + return fmt.Sprintf("(refs: %v, vars: %v, b: %v)", sse.refs, sse.vars, sse.b) +} + +func (sse *saveSetElem) containsVar(t *ast.Term, b *bindings) bool { + if b == sse.b { + for _, v := range sse.vars { + if v.Equal(t) { + return true + } + } + } + return false +} + +// saveStack contains a stack of queries that represent the result of partial +// evaluation. When partial evaluation completes, the top of the stack +// represents a complete, partially evaluated query that can be saved and +// evaluated later. +// +// The result is stored in a stack so that partial evaluation of a query can be +// paused and then resumed in cases where different queries make up the result +// of partial evaluation, such as when a rule with a default clause is +// partially evaluated. In this case, the partially evaluated rule will be +// output in the support module. +type saveStack struct { + Stack []saveStackQuery +} + +func newSaveStack() *saveStack { + return &saveStack{ + Stack: []saveStackQuery{ + {}, + }, + } +} + +func (s *saveStack) PushQuery(query saveStackQuery) { + s.Stack = append(s.Stack, query) +} + +func (s *saveStack) PopQuery() saveStackQuery { + last := s.Stack[len(s.Stack)-1] + s.Stack = s.Stack[:len(s.Stack)-1] + return last +} + +func (s *saveStack) Peek() saveStackQuery { + return s.Stack[len(s.Stack)-1] +} + +func (s *saveStack) Push(expr *ast.Expr, b1 *bindings, b2 *bindings) { + idx := len(s.Stack) - 1 + s.Stack[idx] = append(s.Stack[idx], saveStackElem{expr, b1, b2}) +} + +func (s *saveStack) Pop() { + idx := len(s.Stack) - 1 + query := s.Stack[idx] + s.Stack[idx] = query[:len(query)-1] +} + +type saveStackQuery []saveStackElem + +func (s saveStackQuery) Plug(b *bindings) ast.Body { + if len(s) == 0 { + return ast.NewBody(ast.NewExpr(ast.BooleanTerm(true))) + } + result := make(ast.Body, len(s)) + for i := range s { + expr := s[i].Plug(b) + result.Set(expr, i) + } + return result +} + +type saveStackElem struct { + Expr *ast.Expr + B1 *bindings + B2 *bindings +} + +func (e saveStackElem) Plug(caller *bindings) *ast.Expr { + if e.B1 == nil && e.B2 == nil { + return e.Expr + } + expr := e.Expr.Copy() + switch terms := expr.Terms.(type) { + case []*ast.Term: + if expr.IsEquality() { + terms[1] = e.B1.PlugNamespaced(terms[1], caller) + terms[2] = e.B2.PlugNamespaced(terms[2], caller) + } else { + for i := 1; i < len(terms); i++ { + terms[i] = e.B1.PlugNamespaced(terms[i], caller) + } + } + case *ast.Term: + expr.Terms = e.B1.PlugNamespaced(terms, caller) + } + for i := range expr.With { + expr.With[i].Value = e.B1.PlugNamespaced(expr.With[i].Value, caller) + } + return expr +} + +// saveSupport contains additional partially evaluated policies that are part +// of the output of partial evaluation. +// +// The support structure is accumulated as partial evaluation runs and then +// considered complete once partial evaluation finishes (but not before). This +// differs from partially evaluated queries which are considered complete as +// soon as each one finishes. +type saveSupport struct { + modules map[string]*ast.Module +} + +func newSaveSupport() *saveSupport { + return &saveSupport{ + modules: map[string]*ast.Module{}, + } +} + +func (s *saveSupport) List() []*ast.Module { + result := make([]*ast.Module, 0, len(s.modules)) + for _, module := range s.modules { + result = append(result, module) + } + return result +} + +func (s *saveSupport) Exists(path ast.Ref) bool { + pkg, ruleRef := splitPackageAndRule(path) + module, ok := s.modules[pkg.String()] + if !ok { + return false + } + + if len(ruleRef) == 1 { + name := ruleRef[0].Value.(ast.Var) + for _, rule := range module.Rules { + if rule.Head.Name.Equal(name) { + return true + } + } + return false + } + + for _, rule := range module.Rules { + if rule.Head.Ref().HasPrefix(ruleRef) { + return true + } + } + + return false +} + +func (s *saveSupport) Insert(path ast.Ref, rule *ast.Rule) { + pkg, _ := splitPackageAndRule(path) + s.InsertByPkg(pkg, rule) +} + +func (s *saveSupport) InsertByPkg(pkg ast.Ref, rule *ast.Rule) { + k := pkg.String() + module, ok := s.modules[k] + if !ok { + module = &ast.Module{ + Package: &ast.Package{ + Path: pkg, + }, + } + s.modules[k] = module + } + rule.Module = module + module.Rules = append(module.Rules, rule) +} + +func splitPackageAndRule(path ast.Ref) (ast.Ref, ast.Ref) { + p := path.Copy() + + ruleRefStart := 2 // path always contains at least 3 terms (data. + one term in package + rule name) + for i := ruleRefStart; i < len(p.StringPrefix()); i++ { + t := p[i] + if str, ok := t.Value.(ast.String); ok && ast.IsVarCompatibleString(string(str)) { + ruleRefStart = i + } else { + break + } + } + + pkg := p[:ruleRefStart] + rule := p[ruleRefStart:] + rule[0].Value = ast.Var(rule[0].Value.(ast.String)) + return pkg, rule +} + +// saveRequired returns true if the statement x will result in some expressions +// being saved. This check allows the evaluator to evaluate statements +// completely during partial evaluation as long as they do not depend on any +// kind of unknown value or statements that would generate saves. +func saveRequired(c *ast.Compiler, ic *inliningControl, icIgnoreInternal bool, ss *saveSet, b *bindings, x any, rec bool) bool { + + var found bool + + vis := ast.NewGenericVisitor(func(node any) bool { + if found { + return found + } + switch node := node.(type) { + case *ast.Expr: + found = len(node.With) > 0 + if found { + return found + } + if !ic.nondeterministicBuiltins { // skip evaluating non-det builtins for PE + found = ignoreExprDuringPartial(node) + } + case *ast.Term: + switch v := node.Value.(type) { + case ast.Var: + // Variables only need to be tested in the node from call site + // because once traversal recurses into a rule existing unknown + // variables are out-of-scope. + if !rec && ss.ContainsRecursive(node, b) { + found = true + } + case ast.Ref: + if ss.Contains(node, b) { + found = true + } else if ic.Disabled(v.ConstantPrefix(), icIgnoreInternal) { + found = true + } else { + for _, rule := range c.GetRulesDynamicWithOpts(v, ast.RulesOptions{IncludeHiddenModules: false}) { + if saveRequired(c, ic, icIgnoreInternal, ss, b, rule, true) { + found = true + break + } + } + } + } + } + return found + }) + + vis.Walk(x) + + return found +} + +func ignoreExprDuringPartial(expr *ast.Expr) bool { + if !expr.IsCall() { + return false + } + + bi, ok := ast.BuiltinMap[expr.Operator().String()] + + return ok && ignoreDuringPartial(bi) +} + +func ignoreDuringPartial(bi *ast.Builtin) bool { + // Note(philipc): We keep this legacy check around to avoid breaking + // existing library users. + //nolint:staticcheck // We specifically ignore our own linter warning here. + return cmp.Or(slices.Contains(ast.IgnoreDuringPartialEval, bi), bi.Nondeterministic) +} + +type inliningControl struct { + shallow bool + disable []disableInliningFrame + nondeterministicBuiltins bool // evaluate non-det builtins during PE (if args are known) +} + +type disableInliningFrame struct { + internal bool + refs []ast.Ref + v ast.Var +} + +func (i *inliningControl) PushDisable(x any, internal bool) { + if i == nil { + return + } + + switch x := x.(type) { + case []ast.Ref: + i.PushDisableRefs(x, internal) + case ast.Var: + i.PushDisableVar(x, internal) + } +} + +func (i *inliningControl) PushDisableRefs(refs []ast.Ref, internal bool) { + if i == nil { + return + } + + i.disable = append(i.disable, disableInliningFrame{ + internal: internal, + refs: refs, + }) +} + +func (i *inliningControl) PushDisableVar(v ast.Var, internal bool) { + if i == nil { + return + } + + i.disable = append(i.disable, disableInliningFrame{ + internal: internal, + v: v, + }) +} + +func (i *inliningControl) PopDisable() { + if i == nil { + return + } + i.disable = i.disable[:len(i.disable)-1] +} + +func (i *inliningControl) Disabled(x any, ignoreInternal bool) bool { + if i == nil { + return false + } + + switch x := x.(type) { + case ast.Ref: + return i.DisabledRef(x, ignoreInternal) + case ast.Var: + return i.DisabledVar(x, ignoreInternal) + } + + return false +} + +func (i *inliningControl) DisabledRef(ref ast.Ref, ignoreInternal bool) bool { + if i == nil { + return false + } + + for _, frame := range i.disable { + if !frame.internal || !ignoreInternal { + for _, other := range frame.refs { + if other.HasPrefix(ref) || ref.HasPrefix(other) { + return true + } + } + } + } + return false +} + +func (i *inliningControl) DisabledVar(v ast.Var, ignoreInternal bool) bool { + if i == nil { + return false + } + + for _, frame := range i.disable { + if (!frame.internal || !ignoreInternal) && frame.v.Equal(v) { + return true + } + } + return false +} diff --git a/third_party/opa/v1/topdown/save_test.go b/third_party/opa/v1/topdown/save_test.go new file mode 100644 index 000000000000..9a997f79dfa3 --- /dev/null +++ b/third_party/opa/v1/topdown/save_test.go @@ -0,0 +1,65 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestSaveSet(t *testing.T) { + t.Parallel() + + tests := []struct { + terms []string + input string + expected bool + }{ + { + terms: []string{}, + input: `input`, + expected: false, + }, + { + terms: []string{`input`}, + input: `data.x`, + expected: false, + }, + { + terms: []string{`input`}, + input: `input.x`, + expected: true, + }, + { + terms: []string{`input.x`, `input.y`}, + input: `input`, + expected: true, + }, + { + terms: []string{`input.x`, `input.y`}, + input: `input.z`, + expected: false, + }, + { + terms: []string{`input.x`, `input.y`}, + input: `input.x.foo`, + expected: true, + }, + } + + for _, tc := range tests { + terms := make([]*ast.Term, len(tc.terms)) + for i := range tc.terms { + terms[i] = ast.MustParseTerm(tc.terms[i]) + } + saveSet := newSaveSet(terms, nil, nil) + input := ast.MustParseTerm(tc.input) + if saveSet.Contains(input, nil) != tc.expected { + t.Errorf("Expected %v for %v contains %v", tc.expected, tc.terms, input) + } + } + +} diff --git a/third_party/opa/v1/topdown/semver.go b/third_party/opa/v1/topdown/semver.go new file mode 100644 index 000000000000..3b79ebd58643 --- /dev/null +++ b/third_party/opa/v1/topdown/semver.go @@ -0,0 +1,59 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + + "github.com/open-policy-agent/opa/internal/semver" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +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 + } + + versionStringB, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + versionA, err := semver.NewVersion(string(versionStringA)) + if err != nil { + return fmt.Errorf("operand 1: string %s is not a valid SemVer", versionStringA) + } + versionB, err := semver.NewVersion(string(versionStringB)) + if err != nil { + return fmt.Errorf("operand 2: string %s is not a valid SemVer", versionStringB) + } + + result := versionA.Compare(*versionB) + + return iter(ast.InternedTerm(result)) +} + +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.InternedTerm(false)) + } + + result := true + + _, err = semver.NewVersion(string(versionString)) + if err != nil { + result = false + } + + return iter(ast.InternedTerm(result)) +} + +func init() { + RegisterBuiltinFunc(ast.SemVerCompare.Name, builtinSemVerCompare) + RegisterBuiltinFunc(ast.SemVerIsValid.Name, builtinSemVerIsValid) +} diff --git a/third_party/opa/v1/topdown/sets.go b/third_party/opa/v1/topdown/sets.go new file mode 100644 index 000000000000..c50efe4a8018 --- /dev/null +++ b/third_party/opa/v1/topdown/sets.go @@ -0,0 +1,94 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +// Deprecated: deprecated in v0.4.2 in favour of minus/infix "-" operation. +func builtinSetDiff(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + s1, err := builtins.SetOperand(operands[0].Value, 1) + if err != nil { + return err + } + + s2, err := builtins.SetOperand(operands[1].Value, 2) + if err != nil { + return err + } + + return iter(ast.NewTerm(s1.Diff(s2))) +} + +// builtinSetIntersection returns the intersection of the given input sets +func builtinSetIntersection(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + inputSet, err := builtins.SetOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // empty input set + if inputSet.Len() == 0 { + return iter(ast.InternedEmptySet) + } + + var result ast.Set + + err = inputSet.Iter(func(x *ast.Term) error { + n, err := builtins.SetOperand(x.Value, 1) + if err != nil { + return err + } + + if result == nil { + result = n + } else { + result = result.Intersect(n) + } + return nil + }) + if err != nil { + return err + } + return iter(ast.NewTerm(result)) +} + +// builtinSetUnion returns the union of the given input sets +func builtinSetUnion(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + // The set union logic here is duplicated and manually inlined on + // purpose. By lifting this logic up a level, and not doing pairwise + // set unions, we avoid a number of heap allocations. This improves + // performance dramatically over the naive approach. + result := ast.NewSet() + + inputSet, err := builtins.SetOperand(operands[0].Value, 1) + if err != nil { + return err + } + + err = inputSet.Iter(func(x *ast.Term) error { + item, err := builtins.SetOperand(x.Value, 1) + if err != nil { + return err + } + item.Foreach(result.Add) + return nil + }) + if err != nil { + return err + } + + return iter(ast.NewTerm(result)) +} + +func init() { + RegisterBuiltinFunc(ast.SetDiff.Name, builtinSetDiff) + RegisterBuiltinFunc(ast.Intersection.Name, builtinSetIntersection) + RegisterBuiltinFunc(ast.Union.Name, builtinSetUnion) +} diff --git a/third_party/opa/v1/topdown/sets_bench_test.go b/third_party/opa/v1/topdown/sets_bench_test.go new file mode 100644 index 000000000000..d1e7d676c345 --- /dev/null +++ b/third_party/opa/v1/topdown/sets_bench_test.go @@ -0,0 +1,205 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "fmt" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" +) + +func genNxMSetBenchmarkData(n, m int) ast.Value { + setOfSets := ast.NewSet() + for i := range n { + v := ast.NewSet() + for j := range m { + v.Add(ast.StringTerm(fmt.Sprintf("%d,%d", i, j))) + } + setOfSets.Add(ast.NewTerm(v)) + } + return setOfSets +} + +func BenchmarkSetIntersection(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 100, 1000} + + for _, n := range sizes { + for _, m := range sizes { + b.Run(fmt.Sprintf("%dx%d", n, m), func(b *testing.B) { + store := inmem.NewFromObject(map[string]any{"sets": genNxMSetBenchmarkData(n, m)}) + + module := `package test + + combined := intersection({s | s := data.sets[_]})` + + query := ast.MustParseBody("data.test.combined") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + _, err := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + Run(ctx) + + return err + }) + + if err != nil { + b.Fatal(err) + } + } + }) + } + } +} + +func BenchmarkSetIntersectionSlow(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 50, 100} + + for _, n := range sizes { + for _, m := range sizes { + b.Run(fmt.Sprintf("%dx%d", n, m), func(b *testing.B) { + store := inmem.NewFromObject(map[string]any{"sets": genNxMSetBenchmarkData(n, m)}) + + module := `package test + + combined contains z if { + data.sets[m][z] + every ss in data.sets { + ss[z] + } + }` + + query := ast.MustParseBody("data.test.combined") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + _, err := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + Run(ctx) + + return err + }) + + if err != nil { + b.Fatal(err) + } + } + }) + } + } +} + +func BenchmarkSetUnion(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 100, 250} + + for _, n := range sizes { + for _, m := range sizes { + b.Run(fmt.Sprintf("%dx%d", n, m), func(b *testing.B) { + store := inmem.NewFromObject(map[string]any{"sets": genNxMSetBenchmarkData(n, m)}) + + // Code is lifted from here: + // https://github.com/open-policy-agent/opa/issues/4979#issue-1332019382 + + module := `package test + + combined := union({s | s := data.sets[_]})` + + query := ast.MustParseBody("data.test.combined") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + _, err := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + Run(ctx) + + return err + }) + + if err != nil { + b.Fatal(err) + } + } + }) + } + } +} + +func BenchmarkSetUnionSlow(b *testing.B) { + // This benchmarks the suggested means to implement union + // without using the builtin, to give us an idea of whether or not + // the builtin is actually making things any faster. + ctx := context.Background() + + sizes := []int{10, 100, 250} + + for _, n := range sizes { + for _, m := range sizes { + b.Run(fmt.Sprintf("%dx%d", n, m), func(b *testing.B) { + store := inmem.NewFromObject(map[string]any{"sets": genNxMSetBenchmarkData(n, m)}) + + // Code is lifted from here: + // https://github.com/open-policy-agent/opa/issues/4979#issue-1332019382 + + module := `package test + + combined := {t | s := data.sets[_]; s[t]}` + + query := ast.MustParseBody("data.test.combined") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + _, err := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + Run(ctx) + + return err + }) + + if err != nil { + b.Fatal(err) + } + } + }) + } + } +} diff --git a/third_party/opa/v1/topdown/sets_test.go b/third_party/opa/v1/topdown/sets_test.go new file mode 100644 index 000000000000..e1d56cbc5dac --- /dev/null +++ b/third_party/opa/v1/topdown/sets_test.go @@ -0,0 +1,63 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestSetUnionBuiltin(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + query string + input string + expected string + }{ + // NOTE(philipc): These tests assume that erroneous types are + // checked elsewhere, and focus only on functional correctness. + { + note: "Empty", + input: `{set()}`, + expected: `set()`, + }, + { + note: "Singletons", + input: `{{1}, {2}, {3}, {4}, {5}}`, + expected: `{1, 2, 3, 4, 5}`, + }, + { + note: "One set", + input: `{{1, 2, 3, 4, 5}}`, + expected: `{1, 2, 3, 4, 5}`, + }, + { + note: "One set + empty", + input: `{{1, 2, 3, 4, 5}, set()}`, + expected: `{1, 2, 3, 4, 5}`, + }, + { + note: "Multiple sets, with duplicates", + input: `{{1, 2, 3}, {1, 2}, {3}, {4, 5}}`, + expected: `{1, 2, 3, 4, 5}`, + }, + } + + for _, tc := range tests { + inputs := ast.MustParseTerm(tc.input) + result, err := getResult(builtinSetUnion, inputs) + if err != nil { + t.Fatal(err) + } + + expected := ast.MustParseTerm(tc.expected) + if !result.Equal(expected) { + t.Fatalf("Expected %v but got %v", expected, result) + } + } +} diff --git a/third_party/opa/v1/topdown/strings.go b/third_party/opa/v1/topdown/strings.go new file mode 100644 index 000000000000..53108ca0db07 --- /dev/null +++ b/third_party/opa/v1/topdown/strings.go @@ -0,0 +1,796 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "errors" + "fmt" + "math/big" + "sort" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/tchap/go-patricia/v2/patricia" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func builtinAnyPrefixMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + a, b := operands[0].Value, operands[1].Value + + var strs []string + switch a := a.(type) { + case ast.String: + strs = []string{string(a)} + case *ast.Array, ast.Set: + var err error + strs, err = builtins.StringSliceOperand(a, 1) + if err != nil { + return err + } + default: + return builtins.NewOperandTypeErr(1, a, "string", "set", "array") + } + + var prefixes []string + switch b := b.(type) { + case ast.String: + prefixes = []string{string(b)} + case *ast.Array, ast.Set: + var err error + prefixes, err = builtins.StringSliceOperand(b, 2) + if err != nil { + return err + } + default: + return builtins.NewOperandTypeErr(2, b, "string", "set", "array") + } + + return iter(ast.InternedTerm(anyStartsWithAny(strs, prefixes))) +} + +func builtinAnySuffixMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + a, b := operands[0].Value, operands[1].Value + + var strsReversed []string + switch a := a.(type) { + case ast.String: + strsReversed = []string{reverseString(string(a))} + case *ast.Array, ast.Set: + strs, err := builtins.StringSliceOperand(a, 1) + if err != nil { + return err + } + strsReversed = make([]string, len(strs)) + for i := range strs { + strsReversed[i] = reverseString(strs[i]) + } + default: + return builtins.NewOperandTypeErr(1, a, "string", "set", "array") + } + + var suffixesReversed []string + switch b := b.(type) { + case ast.String: + suffixesReversed = []string{reverseString(string(b))} + case *ast.Array, ast.Set: + suffixes, err := builtins.StringSliceOperand(b, 2) + if err != nil { + return err + } + suffixesReversed = make([]string, len(suffixes)) + for i := range suffixes { + suffixesReversed[i] = reverseString(suffixes[i]) + } + default: + return builtins.NewOperandTypeErr(2, b, "string", "set", "array") + } + + return iter(ast.InternedTerm(anyStartsWithAny(strsReversed, suffixesReversed))) +} + +func anyStartsWithAny(strs []string, prefixes []string) bool { + if len(strs) == 0 || len(prefixes) == 0 { + return false + } + if len(strs) == 1 && len(prefixes) == 1 { + return strings.HasPrefix(strs[0], prefixes[0]) + } + + trie := patricia.NewTrie() + for i := range strs { + trie.Insert([]byte(strs[i]), true) + } + + for i := range prefixes { + if trie.MatchSubtree([]byte(prefixes[i])) { + return true + } + } + + return false +} + +func builtinFormatInt(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + input, err := builtins.NumberOperand(operands[0].Value, 1) + if err != nil { + return err + } + + base, err := builtins.NumberOperand(operands[1].Value, 2) + if err != nil { + return err + } + + var format string + switch base { + case ast.Number("2"): + format = "%b" + case ast.Number("8"): + format = "%o" + case ast.Number("10"): + if i, ok := input.Int(); ok { + return iter(ast.InternedIntegerString(i)) + } + format = "%d" + case ast.Number("16"): + format = "%x" + default: + return builtins.NewOperandEnumErr(2, "2", "8", "10", "16") + } + + f := builtins.NumberToFloat(input) + i, _ := f.Int(nil) + + return iter(ast.InternedTerm(fmt.Sprintf(format, i))) +} + +func builtinConcat(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + join, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // fast path for empty or single string array/set, allocates no memory + if term, ok := zeroOrOneStringTerm(operands[1].Value); ok { + return iter(term) + } + + // NOTE(anderseknert): + // More or less Go's strings.Join implementation, but where we avoid + // creating an intermediate []string slice to pass to that function, + // as that's expensive (3.5x more space allocated). Instead we build + // the string directly using a strings.Builder to concatenate the string + // values from the array/set with the separator. + n := 0 + switch b := operands[1].Value.(type) { + case *ast.Array: + l := b.Len() + for i := range l { + s, ok := b.Elem(i).Value.(ast.String) + if !ok { + return builtins.NewOperandElementErr(2, b, b.Elem(i).Value, "string") + } + n += len(s) + } + sep := string(join) + n += len(sep) * (l - 1) + var sb strings.Builder + sb.Grow(n) + sb.WriteString(string(b.Elem(0).Value.(ast.String))) + if sep == "" { + for i := 1; i < l; i++ { + sb.WriteString(string(b.Elem(i).Value.(ast.String))) + } + } else if len(sep) == 1 { + // when the separator is a single byte, sb.WriteByte is substantially faster + bsep := sep[0] + for i := 1; i < l; i++ { + sb.WriteByte(bsep) + sb.WriteString(string(b.Elem(i).Value.(ast.String))) + } + } else { + // for longer separators, there is no such difference between WriteString and Write + for i := 1; i < l; i++ { + sb.WriteString(sep) + sb.WriteString(string(b.Elem(i).Value.(ast.String))) + } + } + return iter(ast.InternedTerm(sb.String())) + case ast.Set: + for _, v := range b.Slice() { + s, ok := v.Value.(ast.String) + if !ok { + return builtins.NewOperandElementErr(2, b, v.Value, "string") + } + n += len(s) + } + sep := string(join) + l := b.Len() + n += len(sep) * (l - 1) + var sb strings.Builder + sb.Grow(n) + for i, v := range b.Slice() { + sb.WriteString(string(v.Value.(ast.String))) + if i < l-1 { + sb.WriteString(sep) + } + } + return iter(ast.InternedTerm(sb.String())) + } + + return builtins.NewOperandTypeErr(2, operands[1].Value, "set", "array") +} + +func zeroOrOneStringTerm(a ast.Value) (*ast.Term, bool) { + switch b := a.(type) { + case *ast.Array: + if b.Len() == 0 { + return ast.InternedEmptyString, true + } + if b.Len() == 1 { + e := b.Elem(0) + if _, ok := e.Value.(ast.String); ok { + return e, true + } + } + case ast.Set: + if b.Len() == 0 { + return ast.InternedEmptyString, true + } + if b.Len() == 1 { + e := b.Slice()[0] + if _, ok := e.Value.(ast.String); ok { + return e, true + } + } + } + return nil, false +} + +func runesEqual(a, b []rune) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true +} + +func builtinIndexOf(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + base, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + search, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + if len(string(search)) == 0 { + return errors.New("empty search character") + } + + if isASCII(string(base)) && isASCII(string(search)) { + // this is a false positive in the indexAlloc rule that thinks + // we're converting byte arrays to strings + //nolint:gocritic + return iter(ast.InternedTerm(strings.Index(string(base), string(search)))) + } + + baseRunes := []rune(string(base)) + searchRunes := []rune(string(search)) + searchLen := len(searchRunes) + + for i, r := range baseRunes { + if len(baseRunes) >= i+searchLen { + if r == searchRunes[0] && runesEqual(baseRunes[i:i+searchLen], searchRunes) { + return iter(ast.InternedTerm(i)) + } + } else { + break + } + } + + return iter(ast.InternedTerm(-1)) +} + +func builtinIndexOfN(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + base, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + search, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + if len(string(search)) == 0 { + return errors.New("empty search character") + } + + baseRunes := []rune(string(base)) + searchRunes := []rune(string(search)) + searchLen := len(searchRunes) + + var arr []*ast.Term + for i, r := range baseRunes { + if len(baseRunes) >= i+searchLen { + if r == searchRunes[0] && runesEqual(baseRunes[i:i+searchLen], searchRunes) { + arr = append(arr, ast.InternedTerm(i)) + } + } else { + break + } + } + + return iter(ast.ArrayTerm(arr...)) +} + +func builtinSubstring(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + base, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + startIndex, err := builtins.IntOperand(operands[1].Value, 2) + if err != nil { + return err + } + + length, err := builtins.IntOperand(operands[2].Value, 3) + if err != nil { + return err + } + + if startIndex < 0 { + return errors.New("negative offset") + } + + sbase := string(base) + if sbase == "" { + return iter(ast.InternedEmptyString) + } + + // Optimized path for the likely common case of ASCII strings. + // This allocates less memory and runs in about 1/3 the time. + if isASCII(sbase) { + if startIndex >= len(sbase) { + return iter(ast.InternedEmptyString) + } + + if length < 0 { + return iter(ast.InternedTerm(sbase[startIndex:])) + } + + if startIndex == 0 && length >= len(sbase) { + return iter(operands[0]) + } + + upto := min(len(sbase), startIndex+length) + return iter(ast.InternedTerm(sbase[startIndex:upto])) + } + + if startIndex == 0 && length >= utf8.RuneCountInString(sbase) { + return iter(operands[0]) + } + + runes := []rune(base) + + if startIndex >= len(runes) { + return iter(ast.InternedEmptyString) + } + + var s string + if length < 0 { + s = string(runes[startIndex:]) + } else { + upto := min(len(runes), startIndex+length) + s = string(runes[startIndex:upto]) + } + + return iter(ast.InternedTerm(s)) +} + +func isASCII(s string) bool { + for i := range len(s) { + if s[i] > unicode.MaxASCII { + return false + } + } + return true +} + +func builtinContains(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + substr, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + return iter(ast.InternedTerm(strings.Contains(string(s), string(substr)))) +} + +func builtinStringCount(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + substr, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + baseTerm := string(s) + searchTerm := string(substr) + count := strings.Count(baseTerm, searchTerm) + + return iter(ast.InternedTerm(count)) +} + +func builtinStartsWith(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + prefix, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + return iter(ast.InternedTerm(strings.HasPrefix(string(s), string(prefix)))) +} + +func builtinEndsWith(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + suffix, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + return iter(ast.InternedTerm(strings.HasSuffix(string(s), string(suffix)))) +} + +func builtinLower(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + arg := string(s) + low := strings.ToLower(arg) + + if arg == low { + return iter(operands[0]) + } + + return iter(ast.InternedTerm(low)) +} + +func builtinUpper(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + arg := string(s) + upp := strings.ToUpper(arg) + + if arg == upp { + return iter(operands[0]) + } + + return iter(ast.InternedTerm(upp)) +} + +func builtinSplit(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + d, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + if !strings.Contains(string(s), string(d)) { + return iter(ast.ArrayTerm(operands[0])) + } + + elems := strings.Split(string(s), string(d)) + arr := make([]*ast.Term, len(elems)) + + for i := range elems { + arr[i] = ast.InternedTerm(elems[i]) + } + + return iter(ast.ArrayTerm(arr...)) +} + +func builtinReplace(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + old, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + n, err := builtins.StringOperand(operands[2].Value, 3) + if err != nil { + return err + } + + replaced := strings.ReplaceAll(string(s), string(old), string(n)) + if replaced == string(s) { + return iter(operands[0]) + } + + return iter(ast.InternedTerm(replaced)) +} + +func builtinReplaceN(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + patterns, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + keys := patterns.Keys() + sort.Slice(keys, func(i, j int) bool { return ast.Compare(keys[i].Value, keys[j].Value) < 0 }) + + s, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + oldnewArr := make([]string, 0, len(keys)*2) + for _, k := range keys { + keyVal, ok := k.Value.(ast.String) + if !ok { + return builtins.NewOperandErr(1, "non-string key found in pattern object") + } + val := patterns.Get(k) // cannot be nil + strVal, ok := val.Value.(ast.String) + if !ok { + return builtins.NewOperandErr(1, "non-string value found in pattern object") + } + oldnewArr = append(oldnewArr, string(keyVal), string(strVal)) + } + + return iter(ast.InternedTerm(strings.NewReplacer(oldnewArr...).Replace(string(s)))) +} + +func builtinTrim(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + c, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + trimmed := strings.Trim(string(s), string(c)) + if trimmed == string(s) { + return iter(operands[0]) + } + + return iter(ast.InternedTerm(strings.Trim(string(s), string(c)))) +} + +func builtinTrimLeft(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + c, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + trimmed := strings.TrimLeft(string(s), string(c)) + if trimmed == string(s) { + return iter(operands[0]) + } + + return iter(ast.InternedTerm(trimmed)) +} + +func builtinTrimPrefix(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + pre, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + trimmed := strings.TrimPrefix(string(s), string(pre)) + if trimmed == string(s) { + return iter(operands[0]) + } + + return iter(ast.InternedTerm(trimmed)) +} + +func builtinTrimRight(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + c, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + trimmed := strings.TrimRight(string(s), string(c)) + if trimmed == string(s) { + return iter(operands[0]) + } + + return iter(ast.InternedTerm(trimmed)) +} + +func builtinTrimSuffix(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + suf, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + trimmed := strings.TrimSuffix(string(s), string(suf)) + if trimmed == string(s) { + return iter(operands[0]) + } + + return iter(ast.InternedTerm(trimmed)) +} + +func builtinTrimSpace(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + trimmed := strings.TrimSpace(string(s)) + if trimmed == string(s) { + return iter(operands[0]) + } + + return iter(ast.InternedTerm(trimmed)) +} + +func builtinSprintf(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + astArr, ok := operands[1].Value.(*ast.Array) + if !ok { + return builtins.NewOperandTypeErr(2, operands[1].Value, "array") + } + + // Optimized path for where sprintf is used as a "to_string" function for + // a single integer, i.e. sprintf("%d", [x]) where x is an integer. + if s == "%d" && astArr.Len() == 1 { + if n, ok := astArr.Elem(0).Value.(ast.Number); ok { + if i, ok := n.Int(); ok { + if interned := ast.InternedIntegerString(i); interned != nil { + return iter(interned) + } + return iter(ast.StringTerm(strconv.Itoa(i))) + } + } + } + + args := make([]any, astArr.Len()) + + for i := range args { + switch v := astArr.Elem(i).Value.(type) { + case ast.Number: + if n, ok := v.Int(); ok { + args[i] = n + } else if b, ok := new(big.Int).SetString(v.String(), 10); ok { + args[i] = b + } else if f, ok := v.Float64(); ok { + args[i] = f + } else { + args[i] = v.String() + } + case ast.String: + args[i] = string(v) + default: + args[i] = astArr.Elem(i).String() + } + } + + return iter(ast.InternedTerm(fmt.Sprintf(string(s), args...))) +} + +func builtinReverse(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + return iter(ast.InternedTerm(reverseString(string(s)))) +} + +func reverseString(str string) string { + var buf []byte + var arr [255]byte + size := len(str) + + if size < 255 { + buf = arr[:size:size] + } else { + buf = make([]byte, size) + } + + for start := 0; start < size; { + r, n := utf8.DecodeRuneInString(str[start:]) + start += n + utf8.EncodeRune(buf[size-start:], r) + } + + return string(buf) +} + +func init() { + RegisterBuiltinFunc(ast.FormatInt.Name, builtinFormatInt) + RegisterBuiltinFunc(ast.Concat.Name, builtinConcat) + RegisterBuiltinFunc(ast.IndexOf.Name, builtinIndexOf) + RegisterBuiltinFunc(ast.IndexOfN.Name, builtinIndexOfN) + RegisterBuiltinFunc(ast.Substring.Name, builtinSubstring) + RegisterBuiltinFunc(ast.Contains.Name, builtinContains) + RegisterBuiltinFunc(ast.StringCount.Name, builtinStringCount) + RegisterBuiltinFunc(ast.StartsWith.Name, builtinStartsWith) + RegisterBuiltinFunc(ast.EndsWith.Name, builtinEndsWith) + RegisterBuiltinFunc(ast.Upper.Name, builtinUpper) + RegisterBuiltinFunc(ast.Lower.Name, builtinLower) + RegisterBuiltinFunc(ast.Split.Name, builtinSplit) + RegisterBuiltinFunc(ast.Replace.Name, builtinReplace) + RegisterBuiltinFunc(ast.ReplaceN.Name, builtinReplaceN) + RegisterBuiltinFunc(ast.Trim.Name, builtinTrim) + RegisterBuiltinFunc(ast.TrimLeft.Name, builtinTrimLeft) + RegisterBuiltinFunc(ast.TrimPrefix.Name, builtinTrimPrefix) + RegisterBuiltinFunc(ast.TrimRight.Name, builtinTrimRight) + RegisterBuiltinFunc(ast.TrimSuffix.Name, builtinTrimSuffix) + RegisterBuiltinFunc(ast.TrimSpace.Name, builtinTrimSpace) + RegisterBuiltinFunc(ast.Sprintf.Name, builtinSprintf) + RegisterBuiltinFunc(ast.AnyPrefixMatch.Name, builtinAnyPrefixMatch) + RegisterBuiltinFunc(ast.AnySuffixMatch.Name, builtinAnySuffixMatch) + RegisterBuiltinFunc(ast.StringReverse.Name, builtinReverse) +} diff --git a/third_party/opa/v1/topdown/strings_bench_test.go b/third_party/opa/v1/topdown/strings_bench_test.go new file mode 100644 index 000000000000..164940cc8132 --- /dev/null +++ b/third_party/opa/v1/topdown/strings_bench_test.go @@ -0,0 +1,395 @@ +package topdown + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + "github.com/open-policy-agent/opa/v1/storage/inmem" +) + +func BenchmarkBulkStartsWithNaive(b *testing.B) { + data := generateBulkStartsWithInput() + ctx := context.Background() + store := inmem.NewFromObject(data) + + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": ` +package test + +result if { + startswith(data.strings[_], data.prefixes[_]) +} +`, + }) + + query, err := compiler.QueryCompiler().Compile(ast.MustParseBody("data.test.result")) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + + for range b.N { + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + _, err := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + Run(ctx) + + return err + }) + + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkBulkStartsWithOptimized(b *testing.B) { + data := generateBulkStartsWithInput() + ctx := context.Background() + store := inmem.NewFromObject(data) + + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": ` +package test + +result if { + strings.any_prefix_match(data.strings, data.prefixes) +} +`, + }) + + query, err := compiler.QueryCompiler().Compile(ast.MustParseBody("data.test.result")) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + + for range b.N { + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + _, err := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + Run(ctx) + + return err + }) + + if err != nil { + b.Fatal(err) + } + } +} + +func generateBulkStartsWithInput() map[string]any { + strs := make([]string, 0, 1000) + for i := range strs { + strs = append(strs, fmt.Sprintf("aabbccddeeffgghhiijjkkllmmnnoopp_%d", i)) + } + prefixes := make([]string, 0, 100) + for i := range prefixes { + prefixes = append(prefixes, fmt.Sprintf("aabbccddeeffgghhiijjkkllmmnnoorr_%d", i)) + } + return map[string]any{ + "strings": strs, + "prefixes": prefixes, + } +} + +func BenchmarkSplit(b *testing.B) { + bctx := BuiltinContext{} + operands := []*ast.Term{ + ast.StringTerm("a.b.c.d.e"), + ast.StringTerm("."), + } + + exp := eqIter(ast.ArrayTerm( + ast.StringTerm("a"), + ast.StringTerm("b"), + ast.StringTerm("c"), + ast.StringTerm("d"), + ast.StringTerm("e"), + )) + + b.ResetTimer() + b.ReportAllocs() + for range b.N { + if err := builtinSplit(bctx, operands, exp); err != nil { + b.Fatal(err) + } + } +} + +// Now down to 2 allocations per iteration for ASCII strings, more for non-ASCII as that requires +// string/rune conversion. 2 allocations unavoidable - 1 for the new Term and 1 for its Value. +func BenchmarkSubstring(b *testing.B) { + operands := []*ast.Term{ + // insert any non-asci character to see the difference of that optimization + ast.StringTerm("The quick brown fox jumps over the lazy dog"), + ast.InternedTerm(6), + ast.InternedTerm(10), + } + + iter := eqIter(ast.StringTerm("ick brown ")) + + b.ResetTimer() + + for range b.N { + if err := builtinSubstring(BuiltinContext{}, operands, iter); err != nil { + b.Fatal(err) + } + } +} + +// Unicode +// BenchmarkIndexOf-10 10498884 114.0 ns/op 176 B/op 1 allocs/op +// +// ASCII +// BenchmarkIndexOf-10 36625468 31.57 ns/op 0 B/op 0 allocs/op +func BenchmarkIndexOf(b *testing.B) { + operands := []*ast.Term{ + ast.StringTerm("The quick brown fox jumps over the lazy dog"), + ast.StringTerm("dog"), + } + + b.ResetTimer() + + for range b.N { + if err := builtinIndexOf(BuiltinContext{}, operands, eqIter(ast.InternedTerm(40))); err != nil { + b.Fatal(err) + } + } +} + +func eqIter(a *ast.Term) func(*ast.Term) error { + return func(b *ast.Term) error { + if !a.Equal(b) { + return fmt.Errorf("expected %v equal to %v", a, b) + } + return nil + } +} + +// 0 allocs for numbers between 0 and 100 and base 10, 3 allocs for anything else. +func BenchmarkFormatInt(b *testing.B) { + operands := []*ast.Term{ + ast.InternedTerm(99), + ast.InternedTerm(10), + } + bctx := BuiltinContext{} + want := eqIter(ast.StringTerm("99")) + + b.ResetTimer() + + for range b.N { + if err := builtinFormatInt(bctx, operands, want); err != nil { + b.Fatal(err) + } + } +} + +// 0 allocs for numbers between 0 and 100, 3 allocs for anything else. +func BenchmarkSprintfSingleInteger(b *testing.B) { + operands := []*ast.Term{ + ast.StringTerm("%d"), + ast.ArrayTerm( + ast.InternedTerm(99), + ), + } + bctx := BuiltinContext{} + want := eqIter(ast.StringTerm("99")) + + b.ResetTimer() + + for range b.N { + if err := builtinSprintf(bctx, operands, want); err != nil { + b.Fatal(err) + } + } +} + +// This benchmark is not so much about trimming space, but the optimization of returning +// the operand as provided for string operations that don't change the string provided as +// input, like when trimming space around a string that doesn't have any, or replacing a +// substring that doesn't exist. Since string operations are often called in batch, this +// win can be significant. +// +// BenchmarkTrimSpace/trimmable-10 14425539 85.10 ns/op 64 B/op 2 allocs/op +// BenchmarkTrimSpace/not_trimmable-10 87051141 14.47 ns/op 0 B/op 0 allocs/op +func BenchmarkTrimSpace(b *testing.B) { + bctx := BuiltinContext{} + + cases := []struct { + input string + operands []*ast.Term + }{ + { + input: "trimmable", + operands: []*ast.Term{ast.StringTerm(" The quick brown fox jumps over the lazy dog ")}, + }, + { + input: "not trimmable", + operands: []*ast.Term{ast.StringTerm("The quick brown fox jumps over the lazy dog")}, + }, + } + + iter := eqIter(ast.StringTerm("The quick brown fox jumps over the lazy dog")) + + for _, c := range cases { + b.Run(c.input, func(b *testing.B) { + b.ResetTimer() + for range b.N { + if err := builtinTrimSpace(bctx, c.operands, iter); err != nil { + b.Fatal(err) + } + } + }) + } +} + +// Benchmark to demonstrate the performance difference when calling lower with a string +// that is already lowercase vs. one that is not. In the former case, the provided operand +// is returned as-is, while in the latter case a new string is allocated and returned. +// While this tests the 'lower' builtin, the same optimization applies to 'upper'. +// +// BenchmarkLower/not_lowercase-10 5960936 198.6 ns/op 88 B/op 3 allocs/op +// BenchmarkLower/lowercase-10 20954871 57.36 ns/op 0 B/op 0 allocs/op +func BenchmarkLower(b *testing.B) { + bctx := BuiltinContext{} + lower := ast.StringTerm("the quick brown fox jumps over the lazy dog") + cases := []struct { + name string + operands []*ast.Term + }{ + { + name: "not lowercase", + operands: []*ast.Term{ast.StringTerm("The Quick Brown Fox Jumps Over The Lazy Dog")}, + }, + { + name: "lowercase", + operands: []*ast.Term{lower}, + }, + } + + for _, c := range cases { + b.Run(c.name, func(b *testing.B) { + b.ResetTimer() + for range b.N { + if err := builtinLower(bctx, c.operands, eqIter(lower)); err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkConcat(b *testing.B) { + bctx := BuiltinContext{} + tests := []struct { + name string + operands []*ast.Term + expected *ast.Term + }{ + { + name: "0 elements '.' sep", + operands: []*ast.Term{ast.InternedTerm("."), ast.InternedEmptyArray}, + expected: ast.InternedEmptyString, + }, + { + name: "1 element '.' sep", + operands: []*ast.Term{ast.InternedTerm("."), ast.ArrayTerm(ast.InternedTerm("foobar"))}, + expected: ast.InternedTerm("foobar"), + }, + { + name: "100 elements ',' sep", + operands: []*ast.Term{ast.InternedTerm(","), repeatTerm(ast.InternedTerm("foobar"), 100)}, + expected: ast.StringTerm(strings.Repeat("foobar,", 99) + "foobar"), + }, + { + name: "100 elements ', ' sep", + operands: []*ast.Term{ast.InternedTerm(", "), repeatTerm(ast.InternedTerm("foobar"), 100)}, + expected: ast.StringTerm(strings.Repeat("foobar, ", 99) + "foobar"), + }, + { + name: "100 elements blank sep", + operands: []*ast.Term{ast.InternedEmptyString, repeatTerm(ast.InternedTerm("foobar"), 100)}, + expected: ast.StringTerm(strings.Repeat("foobar", 100)), + }, + } + + for _, test := range tests { + b.Run(test.name, func(b *testing.B) { + for range b.N { + if err := builtinConcat(bctx, test.operands, eqIter(test.expected)); err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkConcatVsSprintfSimple(b *testing.B) { + bctx := BuiltinContext{} + + foo := ast.InternedTerm("foo") + bar := ast.InternedTerm("bar") + expected := ast.InternedTerm("foobar") + + b.Run("concat foobar", func(b *testing.B) { + operands := []*ast.Term{ast.InternedEmptyString, ast.ArrayTerm(foo, bar)} + + for range b.N { + if err := builtinConcat(bctx, operands, eqIter(expected)); err != nil { + b.Fatal(err) + } + } + }) + + b.ResetTimer() + + b.Run("sprintf foobar", func(b *testing.B) { + operands := []*ast.Term{ast.InternedTerm("%s%s"), ast.ArrayTerm(foo, bar)} + + for range b.N { + if err := builtinSprintf(bctx, operands, eqIter(expected)); err != nil { + b.Fatal(err) + } + } + }) +} + +func repeatTerm(t *ast.Term, n int) *ast.Term { + terms := make([]*ast.Term, 0, n) + for range n { + terms = append(terms, t) + } + return ast.ArrayTerm(terms...) +} + +func BenchmarkSplitLenVsStringsCount(b *testing.B) { + str := "a.b.c.d.e" + + b.Run("split len", func(b *testing.B) { + for range b.N { + if len(strings.Split(str, ".")) != 5 { + b.Fatal("expected 5 elements") + } + } + }) + + b.Run("strings count", func(b *testing.B) { + for range b.N { + if strings.Count(str, ".")+1 != 5 { + b.Fatal("expected 5 elements") + } + } + }) +} diff --git a/third_party/opa/v1/topdown/subset.go b/third_party/opa/v1/topdown/subset.go new file mode 100644 index 000000000000..d50dc2db77ba --- /dev/null +++ b/third_party/opa/v1/topdown/subset.go @@ -0,0 +1,242 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func bothObjects(t1, t2 *ast.Term) (bool, ast.Object, ast.Object) { + if (t1 == nil) || (t2 == nil) { + return false, nil, nil + } + + obj1, ok := t1.Value.(ast.Object) + if !ok { + return false, nil, nil + } + + obj2, ok := t2.Value.(ast.Object) + if !ok { + return false, nil, nil + } + + return true, obj1, obj2 +} + +func bothSets(t1, t2 *ast.Term) (bool, ast.Set, ast.Set) { + if (t1 == nil) || (t2 == nil) { + return false, nil, nil + } + + set1, ok := t1.Value.(ast.Set) + if !ok { + return false, nil, nil + } + + set2, ok := t2.Value.(ast.Set) + if !ok { + return false, nil, nil + } + + return true, set1, set2 +} + +func bothArrays(t1, t2 *ast.Term) (bool, *ast.Array, *ast.Array) { + if (t1 == nil) || (t2 == nil) { + return false, nil, nil + } + + array1, ok := t1.Value.(*ast.Array) + if !ok { + return false, nil, nil + } + + array2, ok := t2.Value.(*ast.Array) + if !ok { + return false, nil, nil + } + + return true, array1, array2 +} + +func arraySet(t1, t2 *ast.Term) (bool, *ast.Array, ast.Set) { + if (t1 == nil) || (t2 == nil) { + return false, nil, nil + } + + array, ok := t1.Value.(*ast.Array) + if !ok { + return false, nil, nil + } + + set, ok := t2.Value.(ast.Set) + if !ok { + return false, nil, nil + } + + return true, array, set +} + +// objectSubset implements the subset operation on a pair of objects. +// +// This function will try to recursively apply the subset operation where it +// can, such as if both super and sub have an object or set as the value +// associated with a key. +func objectSubset(super ast.Object, sub ast.Object) bool { + var superTerm *ast.Term + + notSubset := sub.Until(func(key, subTerm *ast.Term) bool { + // This really wants to be a for loop, hence the somewhat + // weird internal structure. However, using Until() in this + // was is a performance optimization, as it avoids performing + // any key hashing on the sub-object. + + superTerm = super.Get(key) + + // subTerm can't be nil because we got it from Until(), so + // we only need to verify that super is non-nil. + if superTerm == nil { + return true // break, not a subset + } + + if subTerm.Equal(superTerm) { + return false // continue + } + + // If both of the terms are objects then we want to apply + // the subset operation recursively, otherwise we just compare + // them normally. If only one term is an object, then we + // do a normal comparison which will come up false. + if ok, superObj, subObj := bothObjects(superTerm, subTerm); ok { + return !objectSubset(superObj, subObj) + } + + if ok, superSet, subSet := bothSets(superTerm, subTerm); ok { + return !setSubset(superSet, subSet) + } + + if ok, superArray, subArray := bothArrays(superTerm, subTerm); ok { + return !arraySubset(superArray, subArray) + } + + // We have already checked for exact equality, as well as for + // all of the types of nested subsets we care about, so if we + // get here it means this isn't a subset. + return true // break, not a subset + }) + + return !notSubset +} + +// setSubset implements the subset operation on sets. +// +// Unlike in the object case, this is not recursive, we just compare values +// using ast.Set.Contains() because we have no well-defined way to "match up" +// objects that are in different sets. +func setSubset(super ast.Set, sub ast.Set) bool { + for _, elem := range sub.Slice() { + if !super.Contains(elem) { + return false + } + } + + return true +} + +// arraySubset implements the subset operation on arrays. +// +// This is defined to mean that the entire "sub" array must appear in +// the "super" array. For the same rationale as setSubset(), we do not attempt +// to recurse into values. +func arraySubset(super, sub *ast.Array) bool { + // Notice that this is essentially string search. The naive approach + // used here is O(n^2). This should probably be rewritten later to use + // Boyer-Moore or something. + + if sub.Len() > super.Len() { + return false + } + + if sub.Equal(super) { + return true + } + + superCursor := 0 + subCursor := 0 + for { + if subCursor == sub.Len() { + return true + } + + if superCursor+subCursor == super.Len() { + return false + } + + superElem := super.Elem(superCursor + subCursor) + if superElem == nil { + return false + } + + subElem := sub.Elem(subCursor) + if superElem.Value.Compare(subElem.Value) == 0 { + subCursor++ + } else { + superCursor++ + subCursor = 0 + } + } +} + +// arraySetSubset implements the subset operation on array and set. +// +// This is defined to mean that the entire "sub" set must appear in +// the "super" array with no consideration of ordering. +// For the same rationale as setSubset(), we do not attempt +// to recurse into values. +func arraySetSubset(super *ast.Array, sub ast.Set) bool { + unmatched := sub.Len() + return super.Until(func(t *ast.Term) bool { + if sub.Contains(t) { + unmatched-- + } + if unmatched == 0 { + return true + } + return false + }) +} + +func builtinObjectSubset(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + superTerm := operands[0] + subTerm := operands[1] + + if ok, superObj, subObj := bothObjects(superTerm, subTerm); ok { + // Both operands are objects. + return iter(ast.InternedTerm(objectSubset(superObj, subObj))) + } + + if ok, superSet, subSet := bothSets(superTerm, subTerm); ok { + // Both operands are sets. + return iter(ast.InternedTerm(setSubset(superSet, subSet))) + } + + if ok, superArray, subArray := bothArrays(superTerm, subTerm); ok { + // Both operands are sets. + return iter(ast.InternedTerm(arraySubset(superArray, subArray))) + } + + if ok, superArray, subSet := arraySet(superTerm, subTerm); ok { + // Super operand is array and sub operand is set + return iter(ast.InternedTerm(arraySetSubset(superArray, subSet))) + } + + return builtins.ErrOperand("both arguments object.subset must be of the same type or array and set") +} + +func init() { + RegisterBuiltinFunc(ast.ObjectSubset.Name, builtinObjectSubset) +} diff --git a/third_party/opa/v1/topdown/template.go b/third_party/opa/v1/topdown/template.go new file mode 100644 index 000000000000..c102dec05e57 --- /dev/null +++ b/third_party/opa/v1/topdown/template.go @@ -0,0 +1,47 @@ +//go:build ignore + +package topdown + +import ( + "bytes" + "text/template" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +func renderTemplate(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + preContentTerm, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + templateVariablesTerm, err := builtins.ObjectOperand(operands[1].Value, 2) + if err != nil { + return err + } + + var templateVariables map[string]any + + if err := ast.As(templateVariablesTerm, &templateVariables); err != nil { + return err + } + + tmpl, err := template.New("template").Parse(string(preContentTerm)) + if err != nil { + return err + } + + // Do not attempt to render if template variable keys are missing + tmpl.Option("missingkey=error") + var buf bytes.Buffer + if err := tmpl.Execute(&buf, templateVariables); err != nil { + return err + } + + return iter(ast.StringTerm(buf.String())) +} + +func init() { + RegisterBuiltinFunc(ast.RenderTemplate.Name, renderTemplate) +} diff --git a/third_party/opa/v1/topdown/test.go b/third_party/opa/v1/topdown/test.go new file mode 100644 index 000000000000..02958d22642d --- /dev/null +++ b/third_party/opa/v1/topdown/test.go @@ -0,0 +1,30 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import "github.com/open-policy-agent/opa/v1/ast" + +const TestCaseOp Op = "TestCase" + +func builtinTestCase(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + e := &Event{ + Op: TestCaseOp, + QueryID: bctx.QueryID, + Node: ast.NewExpr([]*ast.Term{ + ast.NewTerm(ast.InternalTestCase.Ref()), + ast.NewTerm(operands[0].Value), + }), + } + + for _, tracer := range bctx.QueryTracers { + tracer.TraceEvent(*e) + } + + return iter(ast.BooleanTerm(true)) +} + +func init() { + RegisterBuiltinFunc(ast.InternalTestCase.Name, builtinTestCase) +} diff --git a/third_party/opa/v1/topdown/testdata/.gitignore b/third_party/opa/v1/topdown/testdata/.gitignore new file mode 100644 index 000000000000..e7fe15b29bb4 --- /dev/null +++ b/third_party/opa/v1/topdown/testdata/.gitignore @@ -0,0 +1,4 @@ +*.srl +*.cnf +csr.pem +ca-key.pem diff --git a/third_party/opa/v1/topdown/testdata/cases/test-systemdocument-1069.yaml b/third_party/opa/v1/topdown/testdata/cases/test-systemdocument-1069.yaml new file mode 100644 index 000000000000..183c9856a752 --- /dev/null +++ b/third_party/opa/v1/topdown/testdata/cases/test-systemdocument-1069.yaml @@ -0,0 +1,28 @@ +cases: +- data: + com: + system: deadbeef + system: + somedata: + - a + - b + - c + input_term: '{}' + modules: + - | + package topdown.system + + bar = "goodbye" + - | + package system.somepolicy + + foo = "hello" + note: systemdocument/root query + query: data = x + want_result: + - x: + com: + system: deadbeef + topdown: + system: + bar: goodbye diff --git a/third_party/opa/v1/topdown/testdata/gencerts.sh b/third_party/opa/v1/topdown/testdata/gencerts.sh new file mode 100755 index 000000000000..40c084ed9ebf --- /dev/null +++ b/third_party/opa/v1/topdown/testdata/gencerts.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# taken from +# https://github.com/dexidp/dex/blob/2d1ac74ec0ca12ae4d36072525d976c1a596820a/examples/k8s/gencert.sh#L22 + +cat <req.cnf +[req] +req_extensions = v3_req +distinguished_name = req_distinguished_name + +[req_distinguished_name] + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = localhost +IP.1 = 127.0.0.1 +EOF + +openssl genrsa -out ca-key.pem 2048 +openssl req -x509 -new -nodes -key ca-key.pem -days 3650 -out ca.pem -subj "/CN=my-ca" + +openssl genrsa -out client-key.pem 2048 +openssl req -new -key client-key.pem -out csr.pem -subj "/CN=my-client" +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 3650 + +openssl genrsa -out client-key-2.pem 2048 +openssl req -new -key client-key-2.pem -out csr.pem -subj "/CN=my-client-2" +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert-2.pem -days 3650 + +openssl genrsa -out server-key.pem 2048 +openssl req -new -key server-key.pem -out csr.pem -subj "/CN=my-server" -config req.cnf +openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 3650 -extensions v3_req -extfile req.cnf diff --git a/third_party/opa/v1/topdown/time.go b/third_party/opa/v1/topdown/time.go new file mode 100644 index 000000000000..16eae3e0bd19 --- /dev/null +++ b/third_party/opa/v1/topdown/time.go @@ -0,0 +1,341 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "encoding/json" + "errors" + "math" + "math/big" + "strconv" + "sync" + "time" + _ "time/tzdata" // this is needed to have LoadLocation when no filesystem tzdata is available + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +var tzCache map[string]*time.Location +var tzCacheMutex *sync.Mutex + +// 1677-09-21T00:12:43.145224192-00:00 +var minDateAllowedForNsConversion = time.Unix(0, math.MinInt64) + +// 2262-04-11T23:47:16.854775807-00:00 +var maxDateAllowedForNsConversion = time.Unix(0, math.MaxInt64) + +func toSafeUnixNano(t time.Time, iter func(*ast.Term) error) error { + if t.Before(minDateAllowedForNsConversion) || t.After(maxDateAllowedForNsConversion) { + return errors.New("time outside of valid range") + } + + return iter(ast.NewTerm(ast.Number(int64ToJSONNumber(t.UnixNano())))) +} + +func builtinTimeNowNanos(bctx BuiltinContext, _ []*ast.Term, iter func(*ast.Term) error) error { + return iter(bctx.Time) +} + +func builtinTimeParseNanos(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + format, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + value, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + formatStr := string(format) + // look for the formatStr in our acceptedTimeFormats and + // use the constant instead if it matches + if f, ok := acceptedTimeFormats[formatStr]; ok { + formatStr = f + } + result, err := time.Parse(formatStr, string(value)) + if err != nil { + return err + } + + return toSafeUnixNano(result, iter) +} + +func builtinTimeParseRFC3339Nanos(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + value, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + result, err := time.Parse(time.RFC3339, string(value)) + if err != nil { + return err + } + + return toSafeUnixNano(result, iter) +} +func builtinParseDurationNanos(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + duration, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + value, err := time.ParseDuration(string(duration)) + if err != nil { + return err + } + return iter(ast.NumberTerm(int64ToJSONNumber(int64(value)))) +} + +// Represent exposed constants for formatting from the stdlib time pkg +var acceptedTimeFormats = map[string]string{ + "ANSIC": time.ANSIC, + "UnixDate": time.UnixDate, + "RubyDate": time.RubyDate, + "RFC822": time.RFC822, + "RFC822Z": time.RFC822Z, + "RFC850": time.RFC850, + "RFC1123": time.RFC1123, + "RFC1123Z": time.RFC1123Z, + "RFC3339": time.RFC3339, + "RFC3339Nano": time.RFC3339Nano, +} + +func builtinFormat(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + t, layout, err := tzTime(operands[0].Value) + if err != nil { + return err + } + // Using RFC3339Nano time formatting as default + if layout == "" { + layout = time.RFC3339Nano + } else if layoutStr, ok := acceptedTimeFormats[layout]; ok { + // if we can find a constant specified, use the constant + layout = layoutStr + } + // otherwise try to treat the fmt string as a datetime fmt string + + timestamp := t.Format(layout) + return iter(ast.StringTerm(timestamp)) +} + +func builtinDate(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + t, _, err := tzTime(operands[0].Value) + if err != nil { + return err + } + year, month, day := t.Date() + + return iter(ast.ArrayTerm(ast.InternedTerm(year), ast.InternedTerm(int(month)), ast.InternedTerm(day))) +} + +func builtinClock(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + t, _, err := tzTime(operands[0].Value) + if err != nil { + return err + } + hour, minute, second := t.Clock() + result := ast.NewArray(ast.InternedTerm(hour), ast.InternedTerm(minute), ast.InternedTerm(second)) + return iter(ast.NewTerm(result)) +} + +func builtinWeekday(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + t, _, err := tzTime(operands[0].Value) + if err != nil { + return err + } + weekday := t.Weekday().String() + return iter(ast.StringTerm(weekday)) +} + +func builtinAddDate(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + t, _, err := tzTime(operands[0].Value) + if err != nil { + return err + } + + years, err := builtins.IntOperand(operands[1].Value, 2) + if err != nil { + return err + } + + months, err := builtins.IntOperand(operands[2].Value, 3) + if err != nil { + return err + } + + days, err := builtins.IntOperand(operands[3].Value, 4) + if err != nil { + return err + } + + result := t.AddDate(years, months, days) + + return toSafeUnixNano(result, iter) +} + +func builtinDiff(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + t1, _, err := tzTime(operands[0].Value) + if err != nil { + return err + } + t2, _, err := tzTime(operands[1].Value) + if err != nil { + return err + } + + // The following implementation of this function is taken + // from https://github.com/icza/gox licensed under Apache 2.0. + // The only modification made is to variable names. + // + // For details, see https://stackoverflow.com/a/36531443/1705598 + // + // Copyright 2021 icza + // BEGIN REDISTRIBUTION FROM APACHE 2.0 LICENSED PROJECT + if t1.Location() != t2.Location() { + t2 = t2.In(t1.Location()) + } + if t1.After(t2) { + t1, t2 = t2, t1 + } + y1, M1, d1 := t1.Date() + y2, M2, d2 := t2.Date() + + h1, m1, s1 := t1.Clock() + h2, m2, s2 := t2.Clock() + + year := y2 - y1 + month := int(M2 - M1) + day := d2 - d1 + hour := h2 - h1 + min := m2 - m1 + sec := s2 - s1 + + // Normalize negative values + if sec < 0 { + sec += 60 + min-- + } + if min < 0 { + min += 60 + hour-- + } + if hour < 0 { + hour += 24 + day-- + } + if day < 0 { + // Days in month: + t := time.Date(y1, M1, 32, 0, 0, 0, 0, time.UTC) + day += 32 - t.Day() + month-- + } + if month < 0 { + month += 12 + year-- + } + // END REDISTRIBUTION FROM APACHE 2.0 LICENSED PROJECT + + return iter(ast.ArrayTerm(ast.InternedTerm(year), ast.InternedTerm(month), ast.InternedTerm(day), + ast.InternedTerm(hour), ast.InternedTerm(min), ast.InternedTerm(sec))) +} + +func tzTime(a ast.Value) (t time.Time, lay string, err error) { + var nVal ast.Value + loc := time.UTC + layout := "" + switch va := a.(type) { + case *ast.Array: + if va.Len() == 0 { + return time.Time{}, layout, builtins.NewOperandTypeErr(1, a, "either number (ns) or [number (ns), string (tz)]") + } + + nVal, err = builtins.NumberOperand(va.Elem(0).Value, 1) + if err != nil { + return time.Time{}, layout, err + } + + if va.Len() > 1 { + tzVal, err := builtins.StringOperand(va.Elem(1).Value, 1) + if err != nil { + return time.Time{}, layout, err + } + + tzName := string(tzVal) + + switch tzName { + case "", "UTC": + // loc is already UTC + + case "Local": + loc = time.Local + + default: + var ok bool + + tzCacheMutex.Lock() + loc, ok = tzCache[tzName] + + if !ok { + loc, err = time.LoadLocation(tzName) + if err != nil { + tzCacheMutex.Unlock() + return time.Time{}, layout, err + } + tzCache[tzName] = loc + } + tzCacheMutex.Unlock() + } + } + + if va.Len() > 2 { + lay, err := builtins.StringOperand(va.Elem(2).Value, 1) + if err != nil { + return time.Time{}, layout, err + } + layout = string(lay) + } + + case ast.Number: + nVal = a + + default: + return time.Time{}, layout, builtins.NewOperandTypeErr(1, a, "either number (ns) or [number (ns), string (tz)]") + } + + value, err := builtins.NumberOperand(nVal, 1) + if err != nil { + return time.Time{}, layout, err + } + + f := builtins.NumberToFloat(value) + i64, acc := f.Int64() + if acc != big.Exact { + return time.Time{}, layout, errors.New("timestamp too big") + } + + t = time.Unix(0, i64).In(loc) + + return t, layout, nil +} + +func int64ToJSONNumber(i int64) json.Number { + return json.Number(strconv.FormatInt(i, 10)) +} + +func init() { + RegisterBuiltinFunc(ast.NowNanos.Name, builtinTimeNowNanos) + RegisterBuiltinFunc(ast.ParseRFC3339Nanos.Name, builtinTimeParseRFC3339Nanos) + RegisterBuiltinFunc(ast.ParseNanos.Name, builtinTimeParseNanos) + RegisterBuiltinFunc(ast.ParseDurationNanos.Name, builtinParseDurationNanos) + RegisterBuiltinFunc(ast.Format.Name, builtinFormat) + RegisterBuiltinFunc(ast.Date.Name, builtinDate) + RegisterBuiltinFunc(ast.Clock.Name, builtinClock) + RegisterBuiltinFunc(ast.Weekday.Name, builtinWeekday) + RegisterBuiltinFunc(ast.AddDate.Name, builtinAddDate) + RegisterBuiltinFunc(ast.Diff.Name, builtinDiff) + tzCacheMutex = &sync.Mutex{} + tzCache = make(map[string]*time.Location) +} diff --git a/third_party/opa/v1/topdown/time_test.go b/third_party/opa/v1/topdown/time_test.go new file mode 100644 index 000000000000..37366a1dece0 --- /dev/null +++ b/third_party/opa/v1/topdown/time_test.go @@ -0,0 +1,46 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestTimeSeeding(t *testing.T) { + t.Parallel() + + query := `time.now_ns(x)` + clock := time.Now() + q := NewQuery(ast.MustParseBody(query)).WithTime(clock).WithCompiler(ast.NewCompiler()) + + ctx := context.Background() + + qrs, err := q.Run(ctx) + if err != nil { + t.Fatal(err) + } else if len(qrs) != 1 { + t.Fatal("expected exactly one result but got:", qrs) + } + + exp := ast.MustParseTerm(fmt.Sprintf(` + { + { + x: %v + } + } + `, clock.UnixNano())) + + result := queryResultSetToTerm(qrs) + + if !result.Equal(exp) { + t.Fatalf("expected %v but got %v", exp, result) + } + +} diff --git a/third_party/opa/v1/topdown/tokens.go b/third_party/opa/v1/topdown/tokens.go new file mode 100644 index 000000000000..831dc32b8767 --- /dev/null +++ b/third_party/opa/v1/topdown/tokens.go @@ -0,0 +1,1329 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "crypto" + "crypto/ecdsa" + "crypto/hmac" + "crypto/rsa" + "crypto/sha256" + "crypto/sha512" + "crypto/x509" + "encoding/hex" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "hash" + "math/big" + "strings" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jwk" + "github.com/open-policy-agent/opa/internal/jwx/jws" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/topdown/cache" +) + +const headerJwt = "JWT" + +// JSONWebToken represent the 3 parts (header, payload & signature) of +// +// a JWT in Base64. +type JSONWebToken struct { + header string + payload string + signature string + decodedHeader ast.Object +} + +// decodeHeader populates the decodedHeader field. +func (token *JSONWebToken) decodeHeader() error { + result, err := getResult(builtinBase64UrlDecode, ast.StringTerm(token.header)) + if err != nil { + return fmt.Errorf("JWT header had invalid encoding: %w", err) + } + decodedHeader, err := validateJWTHeader(string(result.Value.(ast.String))) + if err != nil { + return err + } + token.decodedHeader = decodedHeader + return nil +} + +// Implements JWT decoding/validation based on RFC 7519 Section 7.2: +// https://tools.ietf.org/html/rfc7519#section-7.2 +// It does no data validation, it merely checks that the given string +// represents a structurally valid JWT. It supports JWTs using JWS compact +// serialization. +func builtinJWTDecode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + token, err := decodeJWT(operands[0].Value) + if err != nil { + return err + } + + if err = token.decodeHeader(); err != nil { + return err + } + + p, err := getResult(builtinBase64UrlDecode, ast.StringTerm(token.payload)) + if err != nil { + return fmt.Errorf("JWT payload had invalid encoding: %v", err) + } + + if cty := token.decodedHeader.Get(ast.InternedTerm("cty")); cty != nil { + ctyVal := string(cty.Value.(ast.String)) + // It is possible for the contents of a token to be another + // token as a result of nested signing or encryption. To handle + // the case where we are given a token such as this, we check + // the content type and recurse on the payload if the content + // is "JWT". + // When the payload is itself another encoded JWT, then its + // contents are quoted (behavior of https://jwt.io/). To fix + // this, remove leading and trailing quotes. + if ctyVal == headerJwt { + p, err = getResult(builtinTrim, p, ast.StringTerm(`"'`)) + if err != nil { + panic("not reached") + } + result, err := getResult(builtinJWTDecode, p) + if err != nil { + return err + } + return iter(result) + } + } + + payload, err := extractJSONObject(string(p.Value.(ast.String))) + if err != nil { + return err + } + + s, err := getResult(builtinBase64UrlDecode, ast.StringTerm(token.signature)) + if err != nil { + return fmt.Errorf("JWT signature had invalid encoding: %v", err) + } + sign := hex.EncodeToString([]byte(s.Value.(ast.String))) + + arr := []*ast.Term{ + ast.NewTerm(token.decodedHeader), + ast.NewTerm(payload), + ast.StringTerm(sign), + } + + return iter(ast.ArrayTerm(arr...)) +} + +// Implements RS256 JWT signature verification +func builtinJWTVerifyRS256(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + result, err := builtinJWTVerifyRSA(bctx, operands[0].Value, operands[1].Value, sha256.New, func(publicKey *rsa.PublicKey, digest []byte, signature []byte) error { + return rsa.VerifyPKCS1v15( + publicKey, + crypto.SHA256, + digest, + signature) + }) + if err == nil { + return iter(ast.InternedTerm(result)) + } + return err +} + +// Implements RS384 JWT signature verification +func builtinJWTVerifyRS384(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + result, err := builtinJWTVerifyRSA(bctx, operands[0].Value, operands[1].Value, sha512.New384, func(publicKey *rsa.PublicKey, digest []byte, signature []byte) error { + return rsa.VerifyPKCS1v15( + publicKey, + crypto.SHA384, + digest, + signature) + }) + if err == nil { + return iter(ast.InternedTerm(result)) + } + return err +} + +// Implements RS512 JWT signature verification +func builtinJWTVerifyRS512(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + result, err := builtinJWTVerifyRSA(bctx, operands[0].Value, operands[1].Value, sha512.New, func(publicKey *rsa.PublicKey, digest []byte, signature []byte) error { + return rsa.VerifyPKCS1v15( + publicKey, + crypto.SHA512, + digest, + signature) + }) + if err == nil { + return iter(ast.InternedTerm(result)) + } + return err +} + +// Implements PS256 JWT signature verification +func builtinJWTVerifyPS256(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + result, err := builtinJWTVerifyRSA(bctx, operands[0].Value, operands[1].Value, sha256.New, func(publicKey *rsa.PublicKey, digest []byte, signature []byte) error { + return rsa.VerifyPSS( + publicKey, + crypto.SHA256, + digest, + signature, + nil) + }) + if err == nil { + return iter(ast.InternedTerm(result)) + } + return err +} + +// Implements PS384 JWT signature verification +func builtinJWTVerifyPS384(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + result, err := builtinJWTVerifyRSA(bctx, operands[0].Value, operands[1].Value, sha512.New384, func(publicKey *rsa.PublicKey, digest []byte, signature []byte) error { + return rsa.VerifyPSS( + publicKey, + crypto.SHA384, + digest, + signature, + nil) + }) + if err == nil { + return iter(ast.InternedTerm(result)) + } + return err +} + +// Implements PS512 JWT signature verification +func builtinJWTVerifyPS512(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + result, err := builtinJWTVerifyRSA(bctx, operands[0].Value, operands[1].Value, sha512.New, func(publicKey *rsa.PublicKey, digest []byte, signature []byte) error { + return rsa.VerifyPSS( + publicKey, + crypto.SHA512, + digest, + signature, + nil) + }) + if err == nil { + return iter(ast.InternedTerm(result)) + } + return err +} + +// Implements RSA JWT signature verification. +func builtinJWTVerifyRSA(bctx BuiltinContext, jwt ast.Value, keyStr ast.Value, hasher func() hash.Hash, verify func(publicKey *rsa.PublicKey, digest []byte, signature []byte) error) (bool, error) { + return builtinJWTVerify(bctx, jwt, keyStr, hasher, func(publicKey any, digest []byte, signature []byte) error { + publicKeyRsa, ok := publicKey.(*rsa.PublicKey) + if !ok { + return errors.New("incorrect public key type") + } + return verify(publicKeyRsa, digest, signature) + }) +} + +// Implements ES256 JWT signature verification. +func builtinJWTVerifyES256(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + result, err := builtinJWTVerify(bctx, operands[0].Value, operands[1].Value, sha256.New, verifyES) + if err == nil { + return iter(ast.InternedTerm(result)) + } + return err +} + +// Implements ES384 JWT signature verification +func builtinJWTVerifyES384(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + result, err := builtinJWTVerify(bctx, operands[0].Value, operands[1].Value, sha512.New384, verifyES) + if err == nil { + return iter(ast.InternedTerm(result)) + } + return err +} + +// Implements ES512 JWT signature verification +func builtinJWTVerifyES512(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + result, err := builtinJWTVerify(bctx, operands[0].Value, operands[1].Value, sha512.New, verifyES) + if err == nil { + return iter(ast.InternedTerm(result)) + } + return err +} + +func verifyES(publicKey any, digest []byte, signature []byte) (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("ECDSA signature verification error: %v", r) + } + }() + publicKeyEcdsa, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return errors.New("incorrect public key type") + } + r, s := &big.Int{}, &big.Int{} + n := len(signature) / 2 + r.SetBytes(signature[:n]) + s.SetBytes(signature[n:]) + if ecdsa.Verify(publicKeyEcdsa, digest, r, s) { + return nil + } + return errors.New("ECDSA signature verification error") +} + +type verificationKey struct { + alg string + kid string + key any +} + +// getKeysFromCertOrJWK returns the public key found in a X.509 certificate or JWK key(s). +// A valid PEM block is never valid JSON (and vice versa), hence can try parsing both. +// When provided a JWKS, each key additionally likely contains a key ID and the key algorithm. +func getKeysFromCertOrJWK(certificate string) ([]verificationKey, error) { + if block, rest := pem.Decode([]byte(certificate)); block != nil { + if len(rest) > 0 { + return nil, errors.New("extra data after a PEM certificate block") + } + + if block.Type == blockTypeCertificate { + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, fmt.Errorf("failed to parse a PEM certificate: %w", err) + } + return []verificationKey{{key: cert.PublicKey}}, nil + } + + if block.Type == "PUBLIC KEY" { + key, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("failed to parse a PEM public key: %w", err) + } + + return []verificationKey{{key: key}}, nil + } + + return nil, errors.New("failed to extract a Key from the PEM certificate") + } + + jwks, err := jwk.ParseString(certificate) + if err != nil { + return nil, fmt.Errorf("failed to parse a JWK key (set): %w", err) + } + + keys := make([]verificationKey, 0, len(jwks.Keys)) + for _, k := range jwks.Keys { + key, err := k.Materialize() + if err != nil { + return nil, err + } + keys = append(keys, verificationKey{ + alg: k.GetAlgorithm().String(), + kid: k.GetKeyID(), + key: key, + }) + } + + return keys, nil +} + +func getKeyByKid(kid string, keys []verificationKey) *verificationKey { + for _, key := range keys { + if key.kid == kid { + return &key + } + } + return nil +} + +// Implements JWT signature verification. +func builtinJWTVerify(bctx BuiltinContext, jwt ast.Value, keyStr ast.Value, hasher func() hash.Hash, verify func(publicKey any, digest []byte, signature []byte) error) (bool, error) { + if found, _, _, valid := getTokenFromCache(bctx, jwt, keyStr); found { + return valid, nil + } + + token, err := decodeJWT(jwt) + if err != nil { + return false, err + } + + s, err := builtins.StringOperand(keyStr, 2) + if err != nil { + return false, err + } + + keys, err := getKeysFromCertOrJWK(string(s)) + if err != nil { + return false, err + } + + signature, err := token.decodeSignature() + if err != nil { + return false, err + } + + err = token.decodeHeader() + if err != nil { + return false, err + } + header, err := parseTokenHeader(token) + if err != nil { + return false, err + } + + done := func(valid bool) (bool, error) { + putTokenInCache(bctx, jwt, keyStr, nil, nil, valid) + return valid, nil + } + + // Validate the JWT signature + + // First, check if there's a matching key ID (`kid`) in both token header and key(s). + // If a match is found, verify using only that key. Only applicable when a JWKS was provided. + if header.kid != "" { + if key := getKeyByKid(header.kid, keys); key != nil { + err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), []byte(signature)) + + return done(err == nil) + } + } + + // If no key ID matched, try to verify using any key in the set + // If an alg is present in both the JWT header and the key, skip verification unless they match + for _, key := range keys { + if key.alg == "" { + // No algorithm provided for the key - this is likely a certificate and not a JWKS, so + // we'll need to verify to find out + err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), []byte(signature)) + if err == nil { + return done(true) + } + } else { + if header.alg != key.alg { + continue + } + err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), []byte(signature)) + if err == nil { + return done(true) + } + } + } + + // None of the keys worked, return false + return done(false) +} + +// Implements HS256 (secret) JWT signature verification +func builtinJWTVerifyHS256(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return builtinJWTVerifyHS(bctx, operands, sha256.New, iter) +} + +// Implements HS384 JWT signature verification +func builtinJWTVerifyHS384(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return builtinJWTVerifyHS(bctx, operands, sha512.New384, iter) +} + +// Implements HS512 JWT signature verification +func builtinJWTVerifyHS512(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return builtinJWTVerifyHS(bctx, operands, sha512.New, iter) +} + +func builtinJWTVerifyHS(bctx BuiltinContext, operands []*ast.Term, hashF func() hash.Hash, iter func(*ast.Term) error) error { + jwt, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Process Secret input + astSecret, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + if found, _, _, valid := getTokenFromCache(bctx, jwt, astSecret); found { + return iter(ast.InternedTerm(valid)) + } + + // Decode the JSON Web Token + token, err := decodeJWT(jwt) + if err != nil { + return err + } + + secret := string(astSecret) + + mac := hmac.New(hashF, []byte(secret)) + _, err = mac.Write([]byte(token.header + "." + token.payload)) + if err != nil { + return err + } + + signature, err := token.decodeSignature() + if err != nil { + return err + } + + valid := hmac.Equal([]byte(signature), mac.Sum(nil)) + + putTokenInCache(bctx, jwt, astSecret, nil, nil, valid) + + return iter(ast.InternedTerm(valid)) +} + +// -- Full JWT verification and decoding -- + +// Verification constraints. See tokens_test.go for unit tests. + +// tokenConstraints holds decoded JWT verification constraints. +type tokenConstraints struct { + // The set of asymmetric keys we can verify with. + keys []verificationKey + + // The single symmetric key we will verify with. + secret string + + // The algorithm that must be used to verify. + // If "", any algorithm is acceptable. + alg string + + // The required issuer. + // If "", any issuer is acceptable. + iss string + + // The required audience. + // If "", no audience is acceptable. + aud string + + // The time to validate against, or -1 if no constraint set. + // (If unset, the current time will be used.) + time float64 +} + +// tokenConstraintHandler is the handler type for JWT verification constraints. +type tokenConstraintHandler func(value ast.Value, parameters *tokenConstraints) error + +// tokenConstraintTypes maps known JWT verification constraints to handlers. +var tokenConstraintTypes = map[string]tokenConstraintHandler{ + "cert": tokenConstraintCert, + "secret": func(value ast.Value, constraints *tokenConstraints) error { + return tokenConstraintString("secret", value, &constraints.secret) + }, + "alg": func(value ast.Value, constraints *tokenConstraints) error { + return tokenConstraintString("alg", value, &constraints.alg) + }, + "iss": func(value ast.Value, constraints *tokenConstraints) error { + return tokenConstraintString("iss", value, &constraints.iss) + }, + "aud": func(value ast.Value, constraints *tokenConstraints) error { + return tokenConstraintString("aud", value, &constraints.aud) + }, + "time": tokenConstraintTime, +} + +// tokenConstraintCert handles the `cert` constraint. +func tokenConstraintCert(value ast.Value, constraints *tokenConstraints) error { + s, ok := value.(ast.String) + if !ok { + return errors.New("cert constraint: must be a string") + } + + keys, err := getKeysFromCertOrJWK(string(s)) + if err != nil { + return err + } + + constraints.keys = keys + return nil +} + +// tokenConstraintTime handles the `time` constraint. +func tokenConstraintTime(value ast.Value, constraints *tokenConstraints) error { + t, err := timeFromValue(value) + if err != nil { + return err + } + constraints.time = t + return nil +} + +func timeFromValue(value ast.Value) (float64, error) { + time, ok := value.(ast.Number) + if !ok { + return 0, errors.New("token time constraint: must be a number") + } + timeFloat, ok := time.Float64() + if !ok { + return 0, errors.New("token time constraint: unvalid float64") + } + if timeFloat < 0 { + return 0, errors.New("token time constraint: must not be negative") + } + return timeFloat, nil +} + +// tokenConstraintString handles string constraints. +func tokenConstraintString(name string, value ast.Value, where *string) error { + av, ok := value.(ast.String) + if !ok { + return fmt.Errorf("%s constraint: must be a string", name) + } + *where = string(av) + return nil +} + +// parseTokenConstraints parses the constraints argument. +func parseTokenConstraints(o ast.Object, wallclock *ast.Term) (*tokenConstraints, error) { + constraints := tokenConstraints{ + time: -1, + } + if err := o.Iter(func(k *ast.Term, v *ast.Term) error { + name := string(k.Value.(ast.String)) + handler, ok := tokenConstraintTypes[name] + if ok { + return handler(v.Value, &constraints) + } + // Anything unknown is rejected. + return fmt.Errorf("unknown token validation constraint: %s", name) + }); err != nil { + return nil, err + } + if constraints.time == -1 { // no time provided in constraint object + t, err := timeFromValue(wallclock.Value) + if err != nil { + return nil, err + } + constraints.time = t + } + return &constraints, nil +} + +// validate validates the constraints argument. +func (constraints *tokenConstraints) validate() error { + keys := 0 + if constraints.keys != nil { + keys++ + } + if constraints.secret != "" { + keys++ + } + if keys > 1 { + return errors.New("duplicate key constraints") + } + if keys < 1 { + return errors.New("no key constraint") + } + return nil +} + +// verify verifies a JWT using the constraints and the algorithm from the header +func (constraints *tokenConstraints) verify(kid, alg, header, payload, signature string) error { + // Construct the payload + plaintext := []byte(header) + plaintext = append(plaintext, []byte(".")...) + plaintext = append(plaintext, payload...) + // Look up the algorithm + a, ok := tokenAlgorithms[alg] + if !ok { + return fmt.Errorf("unknown JWS algorithm: %s", alg) + } + // If we're configured with asymmetric key(s) then only trust that + if constraints.keys != nil { + if kid != "" { + if key := getKeyByKid(kid, constraints.keys); key != nil { + err := a.verify(key.key, a.hash, plaintext, []byte(signature)) + if err != nil { + return errSignatureNotVerified + } + return nil + } + } + + verified := false + for _, key := range constraints.keys { + if key.alg == "" { + err := a.verify(key.key, a.hash, plaintext, []byte(signature)) + if err == nil { + verified = true + break + } + } else { + if alg != key.alg { + continue + } + err := a.verify(key.key, a.hash, plaintext, []byte(signature)) + if err == nil { + verified = true + break + } + } + } + + if !verified { + return errSignatureNotVerified + } + return nil + } + if constraints.secret != "" { + return a.verify([]byte(constraints.secret), a.hash, plaintext, []byte(signature)) + } + // (*tokenConstraints)validate() should prevent this happening + return errors.New("unexpectedly found no keys to trust") +} + +// validAudience checks the audience of the JWT. +// It returns true if it meets the constraints and false otherwise. +func (constraints *tokenConstraints) validAudience(aud ast.Value) bool { + s, ok := aud.(ast.String) + if ok { + return string(s) == constraints.aud + } + a, ok := aud.(*ast.Array) + if !ok { + return false + } + return a.Until(func(elem *ast.Term) bool { + if s, ok := elem.Value.(ast.String); ok { + return string(s) == constraints.aud + } + return false + }) +} + +// JWT algorithms + +type ( + tokenVerifyFunction func(key any, hash crypto.Hash, payload []byte, signature []byte) error + tokenVerifyAsymmetricFunction func(key any, hash crypto.Hash, digest []byte, signature []byte) error +) + +// jwtAlgorithm describes a JWS 'alg' value +type tokenAlgorithm struct { + hash crypto.Hash + verify tokenVerifyFunction +} + +// tokenAlgorithms is the known JWT algorithms +var tokenAlgorithms = map[string]tokenAlgorithm{ + "RS256": {crypto.SHA256, verifyAsymmetric(verifyRSAPKCS)}, + "RS384": {crypto.SHA384, verifyAsymmetric(verifyRSAPKCS)}, + "RS512": {crypto.SHA512, verifyAsymmetric(verifyRSAPKCS)}, + "PS256": {crypto.SHA256, verifyAsymmetric(verifyRSAPSS)}, + "PS384": {crypto.SHA384, verifyAsymmetric(verifyRSAPSS)}, + "PS512": {crypto.SHA512, verifyAsymmetric(verifyRSAPSS)}, + "ES256": {crypto.SHA256, verifyAsymmetric(verifyECDSA)}, + "ES384": {crypto.SHA384, verifyAsymmetric(verifyECDSA)}, + "ES512": {crypto.SHA512, verifyAsymmetric(verifyECDSA)}, + "HS256": {crypto.SHA256, verifyHMAC}, + "HS384": {crypto.SHA384, verifyHMAC}, + "HS512": {crypto.SHA512, verifyHMAC}, +} + +// errSignatureNotVerified is returned when a signature cannot be verified. +var errSignatureNotVerified = errors.New("signature not verified") + +func verifyHMAC(key any, hash crypto.Hash, payload []byte, signature []byte) error { + macKey, ok := key.([]byte) + if !ok { + return errors.New("incorrect symmetric key type") + } + mac := hmac.New(hash.New, macKey) + if _, err := mac.Write(payload); err != nil { + return err + } + if !hmac.Equal(signature, mac.Sum([]byte{})) { + return errSignatureNotVerified + } + return nil +} + +func verifyAsymmetric(verify tokenVerifyAsymmetricFunction) tokenVerifyFunction { + return func(key any, hash crypto.Hash, payload []byte, signature []byte) error { + h := hash.New() + h.Write(payload) + return verify(key, hash, h.Sum([]byte{}), signature) + } +} + +func verifyRSAPKCS(key any, hash crypto.Hash, digest []byte, signature []byte) error { + publicKeyRsa, ok := key.(*rsa.PublicKey) + if !ok { + return errors.New("incorrect public key type") + } + if err := rsa.VerifyPKCS1v15(publicKeyRsa, hash, digest, signature); err != nil { + return errSignatureNotVerified + } + return nil +} + +func verifyRSAPSS(key any, hash crypto.Hash, digest []byte, signature []byte) error { + publicKeyRsa, ok := key.(*rsa.PublicKey) + if !ok { + return errors.New("incorrect public key type") + } + if err := rsa.VerifyPSS(publicKeyRsa, hash, digest, signature, nil); err != nil { + return errSignatureNotVerified + } + return nil +} + +func verifyECDSA(key any, _ crypto.Hash, digest []byte, signature []byte) (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("ECDSA signature verification error: %v", r) + } + }() + publicKeyEcdsa, ok := key.(*ecdsa.PublicKey) + if !ok { + return errors.New("incorrect public key type") + } + r, s := &big.Int{}, &big.Int{} + n := len(signature) / 2 + r.SetBytes(signature[:n]) + s.SetBytes(signature[n:]) + if ecdsa.Verify(publicKeyEcdsa, digest, r, s) { + return nil + } + return errSignatureNotVerified +} + +// JWT header parsing and parameters. See tokens_test.go for unit tests. + +// tokenHeaderType represents a recognized JWT header field +// tokenHeader is a parsed JWT header +type tokenHeader struct { + alg string + kid string + typ string + cty string + crit map[string]bool + unknown []string +} + +// tokenHeaderHandler handles a JWT header parameters +type tokenHeaderHandler func(header *tokenHeader, value ast.Value) error + +// tokenHeaderTypes maps known JWT header parameters to handlers +var tokenHeaderTypes = map[string]tokenHeaderHandler{ + "alg": func(header *tokenHeader, value ast.Value) error { + return tokenHeaderString("alg", &header.alg, value) + }, + "kid": func(header *tokenHeader, value ast.Value) error { + return tokenHeaderString("kid", &header.kid, value) + }, + "typ": func(header *tokenHeader, value ast.Value) error { + return tokenHeaderString("typ", &header.typ, value) + }, + "cty": func(header *tokenHeader, value ast.Value) error { + return tokenHeaderString("cty", &header.cty, value) + }, + "crit": tokenHeaderCrit, +} + +// tokenHeaderCrit handles the 'crit' header parameter +func tokenHeaderCrit(header *tokenHeader, value ast.Value) error { + v, ok := value.(*ast.Array) + if !ok { + return errors.New("crit: must be a list") + } + header.crit = map[string]bool{} + _ = v.Iter(func(elem *ast.Term) error { + tv, ok := elem.Value.(ast.String) + if !ok { + return errors.New("crit: must be a list of strings") + } + header.crit[string(tv)] = true + return nil + }) + if len(header.crit) == 0 { + return errors.New("crit: must be a nonempty list") // 'MUST NOT' use the empty list + } + return nil +} + +// tokenHeaderString handles string-format JWT header parameters +func tokenHeaderString(name string, where *string, value ast.Value) error { + v, ok := value.(ast.String) + if !ok { + return fmt.Errorf("%s: must be a string", name) + } + *where = string(v) + return nil +} + +// parseTokenHeader parses the JWT header. +func parseTokenHeader(token *JSONWebToken) (*tokenHeader, error) { + header := tokenHeader{ + unknown: []string{}, + } + if err := token.decodedHeader.Iter(func(k *ast.Term, v *ast.Term) error { + ks := string(k.Value.(ast.String)) + handler, ok := tokenHeaderTypes[ks] + if !ok { + header.unknown = append(header.unknown, ks) + return nil + } + return handler(&header, v.Value) + }); err != nil { + return nil, err + } + return &header, nil +} + +// validTokenHeader returns true if the JOSE header is valid, otherwise false. +func (header *tokenHeader) valid() bool { + // RFC7515 s4.1.1 alg MUST be present + if header.alg == "" { + return false + } + // RFC7515 4.1.11 JWS is invalid if there is a critical parameter that we did not recognize + for _, u := range header.unknown { + if header.crit[u] { + return false + } + } + return true +} + +func commonBuiltinJWTEncodeSign(bctx BuiltinContext, inputHeaders, jwsPayload, jwkSrc string, iter func(*ast.Term) error) error { + keys, err := jwk.ParseString(jwkSrc) + if err != nil { + return err + } + key, err := keys.Keys[0].Materialize() + if err != nil { + return err + } + if jwk.GetKeyTypeFromKey(key) != keys.Keys[0].GetKeyType() { + return errors.New("JWK derived key type and keyType parameter do not match") + } + + standardHeaders := &jws.StandardHeaders{} + jwsHeaders := []byte(inputHeaders) + err = json.Unmarshal(jwsHeaders, standardHeaders) + if err != nil { + return err + } + alg := standardHeaders.GetAlgorithm() + if alg == jwa.Unsupported { + return errors.New("unknown signature algorithm") + } + + if (standardHeaders.Type == "" || standardHeaders.Type == headerJwt) && !json.Valid([]byte(jwsPayload)) { + return errors.New("type is JWT but payload is not JSON") + } + + // process payload and sign + var jwsCompact []byte + jwsCompact, err = jws.SignLiteral([]byte(jwsPayload), alg, key, jwsHeaders, bctx.Seed) + if err != nil { + return err + } + + return iter(ast.StringTerm(string(jwsCompact))) +} + +func builtinJWTEncodeSign(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + inputHeadersAsJSON, err := ast.JSON(operands[0].Value) + if err != nil { + return fmt.Errorf("failed to prepare JWT headers for marshalling: %v", err) + } + + inputHeadersBs, err := json.Marshal(inputHeadersAsJSON) + if err != nil { + return fmt.Errorf("failed to marshal JWT headers: %v", err) + } + + payloadAsJSON, err := ast.JSON(operands[1].Value) + if err != nil { + return fmt.Errorf("failed to prepare JWT payload for marshalling: %v", err) + } + + payloadBs, err := json.Marshal(payloadAsJSON) + if err != nil { + return fmt.Errorf("failed to marshal JWT payload: %v", err) + } + + signatureAsJSON, err := ast.JSON(operands[2].Value) + if err != nil { + return fmt.Errorf("failed to prepare JWT signature for marshalling: %v", err) + } + + signatureBs, err := json.Marshal(signatureAsJSON) + if err != nil { + return fmt.Errorf("failed to marshal JWT signature: %v", err) + } + + return commonBuiltinJWTEncodeSign( + bctx, + string(inputHeadersBs), + string(payloadBs), + string(signatureBs), + iter, + ) +} + +func builtinJWTEncodeSignRaw(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + jwkSrc, err := builtins.StringOperand(operands[2].Value, 3) + if err != nil { + return err + } + inputHeaders, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + jwsPayload, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + return commonBuiltinJWTEncodeSign(bctx, string(inputHeaders), string(jwsPayload), string(jwkSrc), iter) +} + +// Implements full JWT decoding, validation and verification. +func builtinJWTDecodeVerify(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + // io.jwt.decode_verify(string, constraints, [valid, header, payload]) + // + // If valid is true then the signature verifies and all constraints are met. + // If valid is false then either the signature did not verify or some constrain + // was not met. + // + // Decoding errors etc are returned as errors. + a := operands[0].Value + + b, err := builtins.ObjectOperand(operands[1].Value, 2) + if err != nil { + return err + } + + unverified := ast.ArrayTerm( + ast.InternedTerm(false), + ast.InternedEmptyObject, + ast.InternedEmptyObject, + ) + constraints, err := parseTokenConstraints(b, bctx.Time) + if err != nil { + return err + } + if err := constraints.validate(); err != nil { + return err + } + var token *JSONWebToken + var payload ast.Object + var header ast.Object + + // FIXME: optimize + k, _ := b.Filter(ast.NewObject( + ast.Item(ast.InternedTerm("secret"), ast.InternedEmptyObject), + ast.Item(ast.InternedTerm("cert"), ast.InternedEmptyObject), + )) + + if found, th, tp, validSignature := getTokenFromCache(bctx, a, k); found { + if !validSignature { + // For the given token and key(s), the signature is invalid + return iter(unverified) + } + + if th != nil && tp != nil { + header = th + payload = tp + } else { + // Cache entry was created by one of the other built-ins that doesn't decode header/payload + + if token, err = decodeJWT(a); err != nil { + return err + } + + header = token.decodedHeader + + p, err := getResult(builtinBase64UrlDecode, ast.StringTerm(token.payload)) + if err != nil { + return fmt.Errorf("JWT payload had invalid encoding: %v", err) + } + + payload, err = extractJSONObject(string(p.Value.(ast.String))) + if err != nil { + return err + } + + putTokenInCache(bctx, a, k, header, payload, true) + } + } else { + var p *ast.Term + + for { + // RFC7519 7.2 #1-2 split into parts + if token, err = decodeJWT(a); err != nil { + return err + } + + // RFC7519 7.2 #3, #4, #6 + if err := token.decodeHeader(); err != nil { + return err + } + + // RFC7159 7.2 #5 (and RFC7159 5.2 #5) validate header fields + header, err := parseTokenHeader(token) + if err != nil { + return err + } + + if !header.valid() { + return iter(unverified) + } + + // Check constraints that impact signature verification. + if constraints.alg != "" && constraints.alg != header.alg { + return iter(unverified) + } + + // RFC7159 7.2 #7 verify the signature + signature, err := token.decodeSignature() + if err != nil { + return err + } + + if err := constraints.verify(header.kid, header.alg, token.header, token.payload, signature); err != nil { + if err == errSignatureNotVerified { + putTokenInCache(bctx, a, k, nil, nil, false) + return iter(unverified) + } + return err + } + + // RFC7159 7.2 #9-10 decode the payload + p, err = getResult(builtinBase64UrlDecode, ast.StringTerm(token.payload)) + if err != nil { + return fmt.Errorf("JWT payload had invalid encoding: %v", err) + } + + // RFC7159 7.2 #8 and 5.2 cty + if strings.EqualFold(header.cty, headerJwt) { + // Nested JWT, go round again with payload as first argument + a = p.Value + continue + } + + // Non-nested JWT (or we've reached the bottom of the nesting). + break + } + + payload, err = extractJSONObject(string(p.Value.(ast.String))) + if err != nil { + return err + } + + header = token.decodedHeader + + putTokenInCache(bctx, a, k, header, payload, true) + } + + // Check registered claim names against constraints or environment + // RFC7159 4.1.1 iss + if constraints.iss != "" { + if iss := payload.Get(ast.InternedTerm("iss")); iss != nil { + issVal := string(iss.Value.(ast.String)) + if constraints.iss != issVal { + return iter(unverified) + } + } else { + return iter(unverified) + } + } + // RFC7159 4.1.3 aud + if aud := payload.Get(ast.InternedTerm("aud")); aud != nil { + if !constraints.validAudience(aud.Value) { + return iter(unverified) + } + } else { + if constraints.aud != "" { + return iter(unverified) + } + } + // RFC7159 4.1.4 exp + if exp := payload.Get(ast.InternedTerm("exp")); exp != nil { + switch v := exp.Value.(type) { + case ast.Number: + // constraints.time is in nanoseconds but exp Value is in seconds + compareTime := ast.FloatNumberTerm(constraints.time / 1000000000) + if ast.Compare(compareTime, v) != -1 { + return iter(unverified) + } + default: + return errors.New("exp value must be a number") + } + } + // RFC7159 4.1.5 nbf + if nbf := payload.Get(ast.InternedTerm("nbf")); nbf != nil { + switch v := nbf.Value.(type) { + case ast.Number: + // constraints.time is in nanoseconds but nbf Value is in seconds + compareTime := ast.FloatNumberTerm(constraints.time / 1000000000) + if ast.Compare(compareTime, v) == -1 { + return iter(unverified) + } + default: + return errors.New("nbf value must be a number") + } + } + + verified := ast.ArrayTerm( + ast.InternedTerm(true), + ast.NewTerm(header), + ast.NewTerm(payload), + ) + return iter(verified) +} + +// -- Utilities -- + +func decodeJWT(a ast.Value) (*JSONWebToken, error) { + // Parse the JSON Web Token + astEncode, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + encoding := string(astEncode) + if !strings.Contains(encoding, ".") { + return nil, errors.New("encoded JWT had no period separators") + } + + parts := strings.Split(encoding, ".") + if len(parts) != 3 { + return nil, fmt.Errorf("encoded JWT must have 3 sections, found %d", len(parts)) + } + + return &JSONWebToken{header: parts[0], payload: parts[1], signature: parts[2]}, nil +} + +func (token *JSONWebToken) decodeSignature() (string, error) { + decodedSignature, err := getResult(builtinBase64UrlDecode, ast.StringTerm(token.signature)) + if err != nil { + return "", err + } + + signatureAst, err := builtins.StringOperand(decodedSignature.Value, 1) + if err != nil { + return "", err + } + return string(signatureAst), err +} + +// Extract, validate and return the JWT header as an ast.Object. +func validateJWTHeader(h string) (ast.Object, error) { + header, err := extractJSONObject(h) + if err != nil { + return nil, fmt.Errorf("bad JWT header: %v", err) + } + + // There are two kinds of JWT tokens, a JSON Web Signature (JWS) and + // a JSON Web Encryption (JWE). The latter is very involved, and we + // won't support it for now. + // This code checks which kind of JWT we are dealing with according to + // RFC 7516 Section 9: https://tools.ietf.org/html/rfc7516#section-9 + if header.Get(ast.InternedTerm("enc")) != nil { + return nil, errors.New("JWT is a JWE object, which is not supported") + } + + return header, nil +} + +func extractJSONObject(s string) (ast.Object, error) { + // XXX: This code relies on undocumented behavior of Go's + // json.Unmarshal using the last occurrence of duplicate keys in a JSON + // Object. If duplicate keys are present in a JWT, the last must be + // used or the token rejected. Since detecting duplicates is tantamount + // to parsing it ourselves, we're relying on the Go implementation + // using the last occurring instance of the key, which is the behavior + // as of Go 1.8.1. + v, err := getResult(builtinJSONUnmarshal, ast.StringTerm(s)) + if err != nil { + return nil, fmt.Errorf("invalid JSON: %v", err) + } + + o, ok := v.Value.(ast.Object) + if !ok { + return nil, errors.New("decoded JSON type was not an Object") + } + + return o, nil +} + +// getInputSha returns the SHA checksum of the input +func getInputSHA(input []byte, h func() hash.Hash) []byte { + hasher := h() + hasher.Write(input) + return hasher.Sum(nil) +} + +type jwtCacheEntry struct { + payload ast.Object + header ast.Object + validSignature bool +} + +const tokenCacheName = "io_jwt" + +func getTokenFromCache(bctx BuiltinContext, serializedJwt ast.Value, publicKey ast.Value) (bool, ast.Object, ast.Object, bool) { + if bctx.InterQueryBuiltinValueCache == nil { + return false, nil, nil, false + } + + c := bctx.InterQueryBuiltinValueCache.GetCache(tokenCacheName) + if c == nil { + return false, nil, nil, false + } + + key := createTokenCacheKey(serializedJwt, publicKey) + + entry, ok := c.Get(key) + if !ok { + return false, nil, nil, false + } + + if jwtEntry, ok := entry.(jwtCacheEntry); ok { + return true, jwtEntry.header, jwtEntry.payload, jwtEntry.validSignature + } + + return false, nil, nil, false +} + +func putTokenInCache(bctx BuiltinContext, serializedJwt ast.Value, publicKey ast.Value, header ast.Object, payload ast.Object, validSignature bool) { + if bctx.InterQueryBuiltinValueCache == nil { + return + } + + c := bctx.InterQueryBuiltinValueCache.GetCache(tokenCacheName) + if c == nil { + return + } + + key := createTokenCacheKey(serializedJwt, publicKey) + + c.Insert(key, jwtCacheEntry{header: header, payload: payload, validSignature: validSignature}) +} + +func createTokenCacheKey(serializedJwt ast.Value, publicKey ast.Value) ast.Value { + // We need to create a key that is unique to the serialized JWT (for lookup) and the public key used to verify it, + // so that we don't get a misleading cached validation result for a different, invalid key. + return ast.NewArray(ast.NewTerm(serializedJwt), ast.NewTerm(publicKey)) +} + +func init() { + // By default, the JWT cache is disabled. + cache.RegisterDefaultInterQueryBuiltinValueCacheConfig(tokenCacheName, nil) + + RegisterBuiltinFunc(ast.JWTDecode.Name, builtinJWTDecode) + RegisterBuiltinFunc(ast.JWTVerifyRS256.Name, builtinJWTVerifyRS256) + RegisterBuiltinFunc(ast.JWTVerifyRS384.Name, builtinJWTVerifyRS384) + RegisterBuiltinFunc(ast.JWTVerifyRS512.Name, builtinJWTVerifyRS512) + RegisterBuiltinFunc(ast.JWTVerifyPS256.Name, builtinJWTVerifyPS256) + RegisterBuiltinFunc(ast.JWTVerifyPS384.Name, builtinJWTVerifyPS384) + RegisterBuiltinFunc(ast.JWTVerifyPS512.Name, builtinJWTVerifyPS512) + RegisterBuiltinFunc(ast.JWTVerifyES256.Name, builtinJWTVerifyES256) + RegisterBuiltinFunc(ast.JWTVerifyES384.Name, builtinJWTVerifyES384) + RegisterBuiltinFunc(ast.JWTVerifyES512.Name, builtinJWTVerifyES512) + RegisterBuiltinFunc(ast.JWTVerifyHS256.Name, builtinJWTVerifyHS256) + RegisterBuiltinFunc(ast.JWTVerifyHS384.Name, builtinJWTVerifyHS384) + RegisterBuiltinFunc(ast.JWTVerifyHS512.Name, builtinJWTVerifyHS512) + RegisterBuiltinFunc(ast.JWTDecodeVerify.Name, builtinJWTDecodeVerify) + RegisterBuiltinFunc(ast.JWTEncodeSignRaw.Name, builtinJWTEncodeSignRaw) + RegisterBuiltinFunc(ast.JWTEncodeSign.Name, builtinJWTEncodeSign) +} diff --git a/third_party/opa/v1/topdown/tokens_bench_test.go b/third_party/opa/v1/topdown/tokens_bench_test.go new file mode 100644 index 000000000000..729757f38df3 --- /dev/null +++ b/third_party/opa/v1/topdown/tokens_bench_test.go @@ -0,0 +1,170 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/cache" +) + +const privateKey = `{ + "kty":"RSA", + "n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", + "e":"AQAB", + "d":"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ", + "p":"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc", + "q":"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc", + "dp":"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0", + "dq":"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU", + "qi":"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U" + }` + +const publicKey = `{ + "kty":"RSA", + "n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", + "e":"AQAB" + }` + +const keys = `{"keys": [` + publicKey + `]}` + +func BenchmarkTokens(b *testing.B) { + ctx := context.Background() + iter := func(*ast.Term) error { return nil } + + bctx := BuiltinContext{ + Context: ctx, + Time: ast.NumberTerm(int64ToJSONNumber(time.Now().UnixNano())), + } + + keysTerm := ast.ObjectTerm(ast.Item(ast.StringTerm("cert"), ast.StringTerm(keys))) + + worker := func(jobs <-chan string, results chan<- bool) { + for jwt := range jobs { + err := builtinJWTDecodeVerify(bctx, []*ast.Term{ast.NewTerm(ast.String(jwt)), keysTerm}, iter) + if err != nil { + results <- false + } + results <- true + } + } + + jwtCounts := []int{1, 5, 6, 10, 100} + concurrencyLevels := []int{1, 1000} + for _, jwtCount := range jwtCounts { + jwts := make([]string, jwtCount) + + for i := range jwtCount { + jwts[i] = createJwtB(b, fmt.Sprintf(`{"i": %d}`, i)) + } + + for _, concurrencyLevel := range concurrencyLevels { + b.Run(fmt.Sprintf("concurrency: %d, JWT count: %d", concurrencyLevel, jwtCount), func(b *testing.B) { + count := b.N + jobs := make(chan string, count) + results := make(chan bool, count) + + for range concurrencyLevel { + go worker(jobs, results) + } + + b.ResetTimer() + + for i := range count { + jobs <- jwts[i%jwtCount] + } + + close(jobs) + + for range count { + r := <-results + if !r { + b.Fatal("failed to verify JWT") + } + } + }) + } + } +} + +func BenchmarkTokens_Cache(b *testing.B) { + ctx := context.Background() + iter := func(*ast.Term) error { return nil } + + bctx := BuiltinContext{ + Context: ctx, + Time: ast.NumberTerm(int64ToJSONNumber(time.Now().UnixNano())), + InterQueryBuiltinValueCache: cache.NewInterQueryValueCache(ctx, &cache.Config{ + InterQueryBuiltinValueCache: cache.InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*cache.NamedValueCacheConfig{ + tokenCacheName: { + MaxNumEntries: &[]int{5}[0], + }, + }, + }, + }), + } + + keysTerm := ast.ObjectTerm(ast.Item(ast.StringTerm("cert"), ast.StringTerm(keys))) + + worker := func(jobs <-chan string, results chan<- bool) { + for jwt := range jobs { + err := builtinJWTDecodeVerify(bctx, []*ast.Term{ast.NewTerm(ast.String(jwt)), keysTerm}, iter) + if err != nil { + results <- false + } + results <- true + } + } + + jwtCounts := []int{1, 5, 6, 10, 100} + concurrencyLevels := []int{1, 1000} + for _, jwtCount := range jwtCounts { + jwts := make([]string, jwtCount) + + for i := range jwtCount { + jwts[i] = createJwtB(b, fmt.Sprintf(`{"i": %d}`, i)) + } + + for _, concurrencyLevel := range concurrencyLevels { + b.Run(fmt.Sprintf("concurrency: %d, JWT count: %d", concurrencyLevel, jwtCount), func(b *testing.B) { + count := b.N + jobs := make(chan string, count) + results := make(chan bool, count) + + for range concurrencyLevel { + go worker(jobs, results) + } + + b.ResetTimer() + + for i := range count { + jobs <- jwts[i%jwtCount] + } + + close(jobs) + + for range count { + <-results + } + }) + } + } +} + +func createJwtB(b *testing.B, payload string) string { + b.Helper() + + jwt, err := createJwt(payload, privateKey) + if err != nil { + b.Fatal(err) + } + + return jwt +} diff --git a/third_party/opa/v1/topdown/tokens_test.go b/third_party/opa/v1/topdown/tokens_test.go new file mode 100644 index 000000000000..31e8ebb1aa68 --- /dev/null +++ b/third_party/opa/v1/topdown/tokens_test.go @@ -0,0 +1,1157 @@ +package topdown + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "os" + "runtime" + "strings" + "testing" + "time" + + "github.com/open-policy-agent/opa/internal/jwx/jwa" + "github.com/open-policy-agent/opa/internal/jwx/jwk" + "github.com/open-policy-agent/opa/internal/jwx/jws" + "github.com/open-policy-agent/opa/internal/jwx/jws/sign" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/topdown/cache" +) + +func TestParseTokenConstraints(t *testing.T) { + t.Parallel() + + wallclock := ast.NumberTerm(int64ToJSONNumber(time.Now().UnixNano())) + t.Run("Empty", func(t *testing.T) { + t.Parallel() + + c := ast.NewObject() + constraints, err := parseTokenConstraints(c, wallclock) + if err != nil { + t.Fatalf("parseTokenConstraints: %v", err) + } + if constraints.alg != "" { + t.Errorf("alg: %v", constraints.alg) + } + if constraints.keys != nil { + t.Errorf("key: %v", constraints.keys) + } + }) + t.Run("Alg", func(t *testing.T) { + t.Parallel() + + c := ast.NewObject() + c.Insert(ast.StringTerm("alg"), ast.StringTerm("RS256")) + constraints, err := parseTokenConstraints(c, wallclock) + if err != nil { + t.Fatalf("parseTokenConstraints: %v", err) + } + if constraints.alg != "RS256" { + t.Errorf("alg: %v", constraints.alg) + } + }) + t.Run("Cert", func(t *testing.T) { + t.Parallel() + + c := ast.NewObject() + c.Insert(ast.StringTerm("cert"), ast.StringTerm(`-----BEGIN CERTIFICATE----- +MIIBcDCCARagAwIBAgIJAMZmuGSIfvgzMAoGCCqGSM49BAMCMBMxETAPBgNVBAMM +CHdoYXRldmVyMB4XDTE4MDgxMDE0Mjg1NFoXDTE4MDkwOTE0Mjg1NFowEzERMA8G +A1UEAwwId2hhdGV2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATPwn3WCEXL +mjp/bFniDwuwsfu7bASlPae2PyWhqGeWwe23Xlyx+tSqxlkXYe4pZ23BkAAscpGj +yn5gXHExyDlKo1MwUTAdBgNVHQ4EFgQUElRjSoVgKjUqY5AXz2o74cLzzS8wHwYD +VR0jBBgwFoAUElRjSoVgKjUqY5AXz2o74cLzzS8wDwYDVR0TAQH/BAUwAwEB/zAK +BggqhkjOPQQDAgNIADBFAiEA4yQ/88ZrUX68c6kOe9G11u8NUaUzd8pLOtkKhniN +OHoCIHmNX37JOqTcTzGn2u9+c8NlnvZ0uDvsd1BmKPaUmjmm +-----END CERTIFICATE-----`)) + constraints, err := parseTokenConstraints(c, wallclock) + if err != nil { + t.Fatalf("parseTokenConstraints: %v", err) + } + pubKey := constraints.keys[0].key.(*ecdsa.PublicKey) + if pubKey.Curve != elliptic.P256() { + t.Errorf("curve: %v", pubKey.Curve) + } + if pubKey.X.Text(16) != "cfc27dd60845cb9a3a7f6c59e20f0bb0b1fbbb6c04a53da7b63f25a1a86796c1" { + t.Errorf("x: %x", pubKey.X) + } + if pubKey.Y.Text(16) != "edb75e5cb1fad4aac6591761ee29676dc190002c7291a3ca7e605c7131c8394a" { + t.Errorf("y: %x", pubKey.Y) + } + }) + t.Run("Cert Multi Key", func(t *testing.T) { + t.Parallel() + + c := ast.NewObject() + c.Insert(ast.StringTerm("cert"), ast.StringTerm(`{ + "keys": [ + { + "kty": "EC", + "use": "sig", + "crv": "P-256", + "kid": "k1", + "x": "9Qq5S5VqMQoH-FOI4atcH6V3bua03C-5ZMZMG1rszwA", + "y": "LLbFxWkGBEBrTm1GMYZJy1OXCH1KLweJMCgIEPIsibU", + "alg": "ES256" + }, + { + "kty": "RSA", + "e": "AQAB", + "use": "enc", + "kid": "k2", + "alg": "RS256", + "n": "sGu-fYVE2nq2dPxJlqAMI0Z8G3FD0XcWDnD8mkfO1ddKRGuUQZmfj4gWeZGyIk3cnuoy7KJCEqa3daXc08QHuFZyfn0rH33t8_AFsvb0q0i7R2FK-Gdqs_E0-sGpYMsRJdZWfCioLkYjIHEuVnRbi3DEsWqe484rEGbKF60jNRgGC4b-8pz-E538ZkssWxcqHrYIj5bjGEU36onjS3M_yrTuNvzv_8wRioK4fbcwmGne9bDxu8LcoSReWpPn0CnUkWnfqroRcMJnC87ZuJagDW1ZWCmU3psdsVanmFFh0DP6z0fsA4h8G2n9-qp-LEKFaWwo3IWlOsIzU3MHdcEiGw" + } + ] +} +`)) + constraints, err := parseTokenConstraints(c, wallclock) + if err != nil { + t.Fatalf("parseTokenConstraints: %v", err) + } + elPubKey := constraints.keys[0].key.(*ecdsa.PublicKey) + if elPubKey.Curve != elliptic.P256() { + t.Errorf("curve: %v", elPubKey.Curve) + } + + rsaPubKey := constraints.keys[1].key.(*rsa.PublicKey) + if rsaPubKey.Size() != 256 { + t.Errorf("expected size 256 found %d", rsaPubKey.Size()) + } + }) + t.Run("Time", func(t *testing.T) { + t.Parallel() + + now := time.Now() + wallclock := ast.NumberTerm(int64ToJSONNumber(now.UnixNano())) + + t.Run("if provided, is parsed properly", func(t *testing.T) { + c := ast.NewObject() + c.Insert(ast.StringTerm("time"), wallclock) + constraints, err := parseTokenConstraints(c, ast.NumberTerm("12134")) + if err != nil { + t.Fatalf("parseTokenConstraints: %v", err) + } + if exp, act := float64(now.UnixNano()), constraints.time; exp != act { + t.Errorf("expected time constraint to be %f, got %f", exp, act) + } + }) + + t.Run("unset, defaults to wallclock", func(t *testing.T) { + t.Parallel() + + c := ast.NewObject() // 'time' constraint is unset + constraints, err := parseTokenConstraints(c, wallclock) + if err != nil { + t.Fatalf("parseTokenConstraints: %v", err) + } + if exp, act := float64(now.UnixNano()), constraints.time; exp != act { + t.Errorf("expected time constraint to be %f, got %f", exp, act) + } + }) + }) + + t.Run("Unrecognized", func(t *testing.T) { + t.Parallel() + + c := ast.NewObject() + c.Insert(ast.StringTerm("whatever"), ast.StringTerm("junk")) + _, err := parseTokenConstraints(c, wallclock) + if err == nil { + t.Fatalf("parseTokenConstraints: %v", err) + } + }) +} + +func TestParseTokenHeader(t *testing.T) { + t.Parallel() + + t.Run("Errors", func(t *testing.T) { + t.Parallel() + + token := &JSONWebToken{ + header: "", + } + if err := token.decodeHeader(); err == nil { + t.Fatalf("token.decodeHeader: %v", err) + } + token.header = "###" + if err := token.decodeHeader(); err == nil { + t.Fatalf("token.decodeHeader: %v", err) + } + token.header = base64.RawURLEncoding.EncodeToString([]byte(`{`)) + if err := token.decodeHeader(); err == nil { + t.Fatalf("token.decodeHeader: %v", err) + } + token.header = base64.RawURLEncoding.EncodeToString([]byte(`{}`)) + if err := token.decodeHeader(); err != nil { + t.Fatalf("token.decodeHeader: %v", err) + } + header, err := parseTokenHeader(token) + if err != nil { + t.Fatalf("parseTokenHeader: %v", err) + } + if header.valid() { + t.Fatalf("tokenHeader valid") + } + }) + t.Run("Alg", func(t *testing.T) { + t.Parallel() + + token := &JSONWebToken{ + header: base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"RS256"}`)), + } + if err := token.decodeHeader(); err != nil { + t.Fatalf("token.decodeHeader: %v", err) + } + header, err := parseTokenHeader(token) + if err != nil { + t.Fatalf("parseTokenHeader: %v", err) + } + if !header.valid() { + t.Fatalf("tokenHeader !valid") + } + if header.alg != "RS256" { + t.Fatalf("alg: %s", header.alg) + } + }) +} + +func TestTopDownJWTEncodeSignES256(t *testing.T) { + t.Parallel() + + const examplePayload = `{"iss":"joe",` + "\r\n" + ` "exp":1300819380,` + "\r\n" + ` "http://example.com/is_root":true}` + const es256Hdr = `{"alg":"ES256"}` + const ecKey = `{ + "kty":"EC", + "crv":"P-256", + "x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", + "d":"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI" + }` + + params := struct { + note string + input1 string + input2 string + input3 string + }{ + "https://tools.ietf.org/html/rfc7515#appendix-A.3", + "`" + es256Hdr + "`", + "`" + examplePayload + "`", + "`" + ecKey + "`", + } + type test struct { + note string + rules []string + } + + tc := test{ + params.note, + []string{fmt.Sprintf(`p = x { io.jwt.encode_sign_raw(%s, %s, %s, x) }`, params.input1, params.input2, params.input3)}, + } + + compiler, err := compileRules(nil, tc.rules, nil) + if err != nil { + t.Errorf("%v: Compiler error: %v", tc.note, err) + return + } + store := inmem.New() + path := []string{"generated", "p"} + var inputTerm *ast.Term + + ctx := context.Background() + txn := storage.NewTransactionOrDie(ctx, store) + + defer store.Abort(ctx, txn) + + var lhs *ast.Term + if len(path) == 0 { + lhs = ast.NewTerm(ast.DefaultRootRef) + } else { + lhs = ast.MustParseTerm("data." + strings.Join(path, ".")) + } + + rhs := ast.VarTerm(ast.WildcardPrefix + "result") + body := ast.NewBody(ast.Equality.Expr(lhs, rhs)) + + query := NewQuery(body). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithInput(inputTerm) + + var tracer BufferTracer + + if os.Getenv("OPA_TRACE_TEST") != "" { + query = query.WithTracer(&tracer) + } + + qrs, err := query.Run(ctx) + + if tracer != nil { + PrettyTrace(os.Stdout, tracer) + } + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(qrs) == 0 { + t.Fatal("Undefined result") + } + + result, err := ast.JSON(qrs[0][rhs.Value.(ast.Var)].Value) + if err != nil { + t.Fatal(err) + } + // Verification + + standardHeaders := &jws.StandardHeaders{} + err = json.Unmarshal([]byte(es256Hdr), standardHeaders) + if err != nil { + t.Fatal("Failed to parse header") + } + alg := standardHeaders.GetAlgorithm() + + keys, err := jwk.ParseString(ecKey) + if err != nil { + t.Fatal("Failed to parse JWK") + } + key, err := keys.Keys[0].Materialize() + if err != nil { + t.Fatal("Failed to create private key") + } + publicKey, err := jwk.GetPublicKey(key) + if err != nil { + t.Fatalf("failed to get public key: %v", err) + } + + // Verify with vendor library + + verifiedPayload, err := jws.Verify([]byte(result.(string)), alg, publicKey) + if err != nil || string(verifiedPayload) != examplePayload { + t.Fatal("Failed to verify message") + } +} + +// TestTopDownJWTEncodeSignEC needs to perform all tests inline because we do not know the +// expected values before hand +func TestTopDownJWTEncodeSignES512(t *testing.T) { + t.Parallel() + + const examplePayload = `{"iss":"joe",` + "\r\n" + ` "exp":1300819380,` + "\r\n" + ` "http://example.com/is_root":true}` + const es512Hdr = `{"alg":"ES512"}` + const ecKey = `{ +"kty":"EC", +"crv":"P-521", +"x":"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk", +"y":"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2", +"d":"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C" +}` + + params := struct { + note string + input1 string + input2 string + input3 string + }{ + "https://tools.ietf.org/html/rfc7515#appendix-A.4", + "`" + es512Hdr + "`", + "`" + examplePayload + "`", + "`" + ecKey + "`", + } + type test struct { + note string + rules []string + } + var tests []test + + tests = append(tests, test{ + params.note, + []string{fmt.Sprintf(`p = x { io.jwt.encode_sign_raw(%s, %s, %s, x) }`, params.input1, params.input2, params.input3)}, + }) + + tc := tests[0] + + compiler, err := compileRules(nil, tc.rules, nil) + if err != nil { + t.Errorf("%v: Compiler error: %v", tc.note, err) + return + } + store := inmem.New() + path := []string{"generated", "p"} + var inputTerm *ast.Term + + ctx := context.Background() + txn := storage.NewTransactionOrDie(ctx, store) + + defer store.Abort(ctx, txn) + + var lhs *ast.Term + if len(path) == 0 { + lhs = ast.NewTerm(ast.DefaultRootRef) + } else { + lhs = ast.MustParseTerm("data." + strings.Join(path, ".")) + } + + rhs := ast.VarTerm(ast.WildcardPrefix + "result") + body := ast.NewBody(ast.Equality.Expr(lhs, rhs)) + + query := NewQuery(body). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithInput(inputTerm) + + var tracer BufferTracer + + if os.Getenv("OPA_TRACE_TEST") != "" { + query = query.WithTracer(&tracer) + } + + qrs, err := query.Run(ctx) + + if tracer != nil { + PrettyTrace(os.Stdout, tracer) + } + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(qrs) == 0 { + t.Fatal("Undefined result") + } + + result, err := ast.JSON(qrs[0][rhs.Value.(ast.Var)].Value) + if err != nil { + t.Fatal(err) + } + // Verification + + standardHeaders := &jws.StandardHeaders{} + err = json.Unmarshal([]byte(es512Hdr), standardHeaders) + if err != nil { + t.Fatal("Failed to parse header") + } + alg := standardHeaders.GetAlgorithm() + + keys, err := jwk.ParseString(ecKey) + if err != nil { + t.Fatalf("Failed to parse JWK: %v", err) + } + key, err := keys.Keys[0].Materialize() + if err != nil { + t.Fatalf("Failed to create private key: %v", err) + } + publicKey, err := jwk.GetPublicKey(key) + if err != nil { + t.Fatalf("Failed to get public key: %v", err) + } + + // Verify with vendor library + + verifiedPayload, err := jws.Verify([]byte(result.(string)), alg, publicKey) + if err != nil || string(verifiedPayload) != examplePayload { + t.Fatal("Failed to verify message") + } +} + +// NOTE(sr): The stdlib ecdsa package will randomly read 1 byte from the source +// and discard it: so passing a fixed-seed `rand.New(rand.Source(seed))` via +// `rego.WithSeed` will not do the trick, the output would still randomly be +// one of two possible signatures. To fix that for testing, we're reaching +// deeper here, and use a "constant number generator". It doesn't matter if the +// first byte is discarded, the second one looks just the same. +type cng struct{} + +func (*cng) Read(p []byte) (int, error) { + for i := range p { + p[i] = 4 + } + return len(p), nil +} + +func TestTopdownJWTEncodeSignECWithSeedReturnsSameSignature(t *testing.T) { + t.Parallel() + + query := `io.jwt.encode_sign({"alg": "ES256"},{"pay": "load"}, + {"kty":"EC", + "crv":"P-256", + "x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", + "d":"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI" + }, x)` + + // NOTE(ae): the signature differs between Go 1.23 and 1.24, as the latter uses the rand/v2 package (or that's my take) + var encodedSigned string + if runtime.Version() < "go1.24" { + encodedSigned = "eyJhbGciOiJFUzI1NiJ9.eyJwYXkiOiJsb2FkIn0.wDU6G2XTYFP3QdVYhy-PBzkacEFNJwVT4HPQHOLtUmJu-OcVUaX9n-Ukv50AJwoF59L2wS5aOzoUwuru48Q4tw" + } else { + encodedSigned = "eyJhbGciOiJFUzI1NiJ9.eyJwYXkiOiJsb2FkIn0.WAh1ydGVRdVwXNQ9i71LqUJSrs3WVDZENdN58jCkecC2oCXEnqcviaADIwcZbYmns5IfHNV1Euo6vBm75o5l9A" + } + + for range 10 { + q := NewQuery(ast.MustParseBody(query)). + WithSeed(&cng{}). + WithStrictBuiltinErrors(true). + WithCompiler(ast.NewCompiler()) + + qrs, err := q.Run(context.Background()) + if err != nil { + t.Fatal(err) + } else if len(qrs) != 1 { + t.Fatal("expected exactly one result but got:", qrs) + } + + if exp, act := 1, len(qrs); exp != act { + t.Fatalf("expected %d results, got %d", exp, act) + } + + if exp, act := ast.String(encodedSigned), qrs[0][ast.Var("x")].Value; !exp.Equal(act) { + t.Fatalf("unexpected result: want %v, got %v", exp, act) + } + } +} + +func TestTopdownJWTUnknownAlgTypesDiscardedFromJWKS(t *testing.T) { + t.Parallel() + + cert := `{ + "keys": [ + { + "kty": "RSA", + "e": "AQAB", + "use": "enc", + "kid": "k3", + "alg": "RS256", + "n": "sGu-fYVE2nq2dPxJlqAMI0Z8G3FD0XcWDnD8mkfO1ddKRGuUQZmfj4gWeZGyIk3cnuoy7KJCEqa3daXc08QHuFZyfn0rH33t8_AFsvb0q0i7R2FK-Gdqs_E0-sGpYMsRJdZWfCioLkYjIHEuVnRbi3DEsWqe484rEGbKF60jNRgGC4b-8pz-E538ZkssWxcqHrYIj5bjGEU36onjS3M_yrTuNvzv_8wRioK4fbcwmGne9bDxu8LcoSReWpPn0CnUkWnfqroRcMJnC87ZuJagDW1ZWCmU3psdsVanmFFh0DP6z0fsA4h8G2n9-qp-LEKFaWwo3IWlOsIzU3MHdcEiGw" + }, + { + "kid": "encryption algorithm", + "kty": "RSA", + "alg": "RSA-OAEP", + "use": "enc", + "n": "onlqv4UZx5ZabJ3TCq-IO0s0xaOwo6fWl9o4SzLXPbGtvxonQhoYOeMlS0XkdEdLzB-eqh_hkQ", + "e": "AQAB", + "x5c": [ + "MIICnTCCAYUCBgGAmcG0xjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQ2YVaQn47Eew==" + ], + "x5t": "WKfdwdQkg", + "x5t#S256": "2_FidAwjlCQl20" + } + ] +} +` + keys, err := getKeysFromCertOrJWK(cert) + if err != nil { + t.Fatal(err) + } + + if len(keys) != 1 { + t.Errorf("expected only one key as inavlid one should have been discarded") + } + + if keys[0].alg != "RS256" { + t.Errorf("expected key with RS256 alg") + } +} + +func TestTopdownJWTVerifyOnlyVerifiesUsingApplicableKeys(t *testing.T) { + t.Parallel() + + cert := ast.MustInterfaceToValue(`{ + "keys": [ + { + "kty": "EC", + "use": "sig", + "crv": "P-256", + "kid": "k1", + "x": "9Qq5S5VqMQoH-FOI4atcH6V3bua03C-5ZMZMG1rszwA", + "y": "LLbFxWkGBEBrTm1GMYZJy1OXCH1KLweJMCgIEPIsibU", + "alg": "ES256" + }, + { + "kty": "RSA", + "e": "AQAB", + "use": "enc", + "kid": "k2", + "alg": "RS256", + "n": "sGu-fYVE2nq2dPxJlqAMI0Z8G3FD0XcWDnD8mkfO1ddKRGuUQZmfj4gWeZGyIk3cnuoy7KJCEqa3daXc08QHuFZyfn0rH33t8_AFsvb0q0i7R2FK-Gdqs_E0-sGpYMsRJdZWfCioLkYjIHEuVnRbi3DEsWqe484rEGbKF60jNRgGC4b-8pz-E538ZkssWxcqHrYIj5bjGEU36onjS3M_yrTuNvzv_8wRioK4fbcwmGne9bDxu8LcoSReWpPn0CnUkWnfqroRcMJnC87ZuJagDW1ZWCmU3psdsVanmFFh0DP6z0fsA4h8G2n9-qp-LEKFaWwo3IWlOsIzU3MHdcEiGw" + }, + { + "kty": "RSA", + "e": "AQAB", + "use": "enc", + "kid": "k3", + "alg": "RS256", + "n": "sGu-fYVE2nq2dPxJlqAMI0Z8G3FD0XcWDnD8mkfO1ddKRGuUQZmfj4gWeZGyIk3cnuoy7KJCEqa3daXc08QHuFZyfn0rH33t8_AFsvb0q0i7R2FK-Gdqs_E0-sGpYMsRJdZWfCioLkYjIHEuVnRbi3DEsWqe484rEGbKF60jNRgGC4b-8pz-E538ZkssWxcqHrYIj5bjGEU36onjS3M_yrTuNvzv_8wRioK4fbcwmGne9bDxu8LcoSReWpPn0CnUkWnfqroRcMJnC87ZuJagDW1ZWCmU3psdsVanmFFh0DP6z0fsA4h8G2n9-qp-LEKFaWwo3IWlOsIzU3MHdcEiGw" + }, + { + "kid": "unknown algorithm", + "kty": "RSA", + "alg": "RSA-OAEP", + "use": "enc", + "n": "onlqv4UZx5ZabJ3TCq-IO0s0xaOwo6fWl9o4SzLXPbGtvxonQhoYOeMlS0XkdEdLzB-eqh_hkQ", + "e": "AQAB", + "x5c": [ + "MIICnTCCAYUCBgGAmcG0xjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQ2YVaQn47Eew==" + ], + "x5t": "WKfdwdQkg", + "x5t#S256": "2_FidAw.....jlCQl20" + } + ] +} +`) + + cases := []struct { + note string + header string + expectVerifyCalls int + }{ + { + note: "verification considers only key with matching kid, if present", + header: `{"alg":"RS256", "kid": "k2"}`, + expectVerifyCalls: 1, + }, + { + note: "verification considers any key with matching alg, if no kid matches", + header: `{"alg":"RS256", "kid": "not-in-jwks"}`, + expectVerifyCalls: 2, + }, + { + note: "verification without kid considers only keys with alg matched from header", + header: `{"alg":"RS256"}`, + expectVerifyCalls: 2, + }, + { + note: "verification is is skipped if alg unknown", + header: `{"alg":"xyz"}`, + expectVerifyCalls: 0, + }, + } + + bctx := BuiltinContext{} + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + header := base64.RawURLEncoding.EncodeToString([]byte(tc.header)) + payload := base64.RawURLEncoding.EncodeToString([]byte("{}")) + signature := base64.RawURLEncoding.EncodeToString([]byte("ignored")) + + token := ast.MustInterfaceToValue(fmt.Sprintf("%s.%s.%s", header, payload, signature)) + + verifyCalls := 0 + verifier := func(_ any, _ []byte, _ []byte) error { + verifyCalls++ + return errors.New("fail") + } + + _, err := builtinJWTVerify(bctx, token, cert, sha256.New, verifier) + if err != nil { + t.Fatal(err) + } + + if verifyCalls != tc.expectVerifyCalls { + t.Errorf("expected %d calls to verify token, got %d", tc.expectVerifyCalls, verifyCalls) + } + }) + } +} + +func TestTopdownJWTDecodeVerifyIgnoresKeysOfUnknownAlgInJWKS(t *testing.T) { + t.Parallel() + + c := ast.NewObject() + c.Insert(ast.StringTerm("cert"), ast.StringTerm(`{ + "keys": [ + { + "kty": "EC", + "use": "sig", + "crv": "P-256", + "kid": "k1", + "x": "9Qq5S5VqMQoH-FOI4atcH6V3bua03C-5ZMZMG1rszwA", + "y": "LLbFxWkGBEBrTm1GMYZJy1OXCH1KLweJMCgIEPIsibU", + "alg": "ES256" + }, + { + "kty": "RSA", + "e": "AQAB", + "use": "enc", + "kid": "k2", + "alg": "RS256", + "n": "sGu-fYVE2nq2dPxJlqAMI0Z8G3FD0XcWDnD8mkfO1ddKRGuUQZmfj4gWeZGyIk3cnuoy7KJCEqa3daXc08QHuFZyfn0rH33t8_AFsvb0q0i7R2FK-Gdqs_E0-sGpYMsRJdZWfCioLkYjIHEuVnRbi3DEsWqe484rEGbKF60jNRgGC4b-8pz-E538ZkssWxcqHrYIj5bjGEU36onjS3M_yrTuNvzv_8wRioK4fbcwmGne9bDxu8LcoSReWpPn0CnUkWnfqroRcMJnC87ZuJagDW1ZWCmU3psdsVanmFFh0DP6z0fsA4h8G2n9-qp-LEKFaWwo3IWlOsIzU3MHdcEiGw" + }, + { + "kty": "RSA", + "e": "AQAB", + "use": "enc", + "kid": "k3", + "alg": "RS256", + "n": "sGu-fYVE2nq2dPxJlqAMI0Z8G3FD0XcWDnD8mkfO1ddKRGuUQZmfj4gWeZGyIk3cnuoy7KJCEqa3daXc08QHuFZyfn0rH33t8_AFsvb0q0i7R2FK-Gdqs_E0-sGpYMsRJdZWfCioLkYjIHEuVnRbi3DEsWqe484rEGbKF60jNRgGC4b-8pz-E538ZkssWxcqHrYIj5bjGEU36onjS3M_yrTuNvzv_8wRioK4fbcwmGne9bDxu8LcoSReWpPn0CnUkWnfqroRcMJnC87ZuJagDW1ZWCmU3psdsVanmFFh0DP6z0fsA4h8G2n9-qp-LEKFaWwo3IWlOsIzU3MHdcEiGw" + }, + { + "kid": "unknown algorithm", + "kty": "RSA", + "alg": "RSA-OAEP", + "use": "enc", + "n": "onlqv4UZx5ZabJ3TCq-IO0s0xaOwo6fWl9o4SzLXPbGtvxonQhoYOeMlS0XkdEdLzB-eqh_hkQ", + "e": "AQAB", + "x5c": [ + "MIICnTCCAYUCBgGAmcG0xjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQ2YVaQn47Eew==" + ], + "x5t": "WKfdwdQkg", + "x5t#S256": "2_FidAw.....jlCQl20" + } + ] +} +`)) + + wallclock := ast.NumberTerm(int64ToJSONNumber(time.Now().UnixNano())) + constraints, err := parseTokenConstraints(c, wallclock) + if err != nil { + t.Fatal(err) + } + + if len(constraints.keys) != 3 { + t.Errorf("expected 3 keys in JWKS, got %d", len(constraints.keys)) + } + + for _, key := range constraints.keys { + if key.alg == "RSA-OAEP" { + t.Errorf("expected alg: RSA-OAEP to be removed from key set") + } + } +} + +func TestBuiltinJWTDecodeVerify_TokenCache(t *testing.T) { + ctx := context.Background() + + const privateKey = `{ + "kty":"RSA", + "n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", + "e":"AQAB", + "d":"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ", + "p":"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc", + "q":"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc", + "dp":"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0", + "dq":"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU", + "qi":"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U" + }` + + const publicKey = `{ + "kty":"RSA", + "n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", + "e":"AQAB" + }` + + const keys = `{"keys": [` + publicKey + `]}` + + keysTerm := ast.ObjectTerm(ast.Item(ast.StringTerm("cert"), ast.StringTerm(keys))) + + jwt := createJwtT(t, `{"i": "foo"}`, privateKey) + jwtTerm := ast.NewTerm(ast.String(jwt)) + + t.Run("no cache", func(t *testing.T) { + var verified bool + iter := func(r *ast.Term) error { + verified = bool(r.Value.(*ast.Array).Get(ast.NumberTerm("0")).Value.(ast.Boolean)) + return nil + } + + bctx := BuiltinContext{ + Context: ctx, + Time: ast.NumberTerm(int64ToJSONNumber(time.Now().UnixNano())), + } + + err := builtinJWTDecodeVerify(bctx, []*ast.Term{jwtTerm, keysTerm}, iter) + if err != nil { + t.Fatalf("unexpected error: %q", err) + } + + if !verified { + t.Fatal("expected token to be successfully verified") + } + }) + + config := cache.Config{ + InterQueryBuiltinValueCache: cache.InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*cache.NamedValueCacheConfig{ + tokenCacheName: { + MaxNumEntries: &[]int{5}[0], + }, + }, + }, + } + + t.Run("cache", func(t *testing.T) { + var verified bool + iter := func(r *ast.Term) error { + verified = bool(r.Value.(*ast.Array).Get(ast.NumberTerm("0")).Value.(ast.Boolean)) + return nil + } + + bctx := BuiltinContext{ + Context: ctx, + Time: ast.NumberTerm(int64ToJSONNumber(time.Now().UnixNano())), + InterQueryBuiltinValueCache: cache.NewInterQueryValueCache(ctx, &config), + } + + t.Run("successful verification", func(t *testing.T) { + err := builtinJWTDecodeVerify(bctx, []*ast.Term{jwtTerm, keysTerm}, iter) + if err != nil { + t.Fatalf("unexpected error: %q", err) + } + + if !verified { + t.Fatal("expected token to be successfully verified") + } + + k := createTokenCacheKey(ast.String(jwt), keysTerm.Value) + if _, ok := bctx.InterQueryBuiltinValueCache.GetCache(tokenCacheName).Get(k); !ok { + t.Fatal("expected token to be cached") + } + }) + + t.Run("failed verification (bad signature)", func(t *testing.T) { + badJwt := createBadJwt(t, `{"i": "foo"}`) + badJwtTerm := ast.NewTerm(ast.String(badJwt)) + + err := builtinJWTDecodeVerify(bctx, []*ast.Term{badJwtTerm, keysTerm}, iter) + if err != nil { + t.Fatalf("unexpected error: %q", err) + } + + if verified { + t.Fatal("expected token to fail verification") + } + + k := createTokenCacheKey(ast.String(badJwt), keysTerm.Value) + if _, ok := bctx.InterQueryBuiltinValueCache.GetCache(tokenCacheName).Get(k); !ok { + t.Fatal("expected token to be cached") + } + }) + + t.Run("iss constraint check", func(t *testing.T) { + jwt := createJwtT(t, `{"i": "foo", "iss": "foo"}`, privateKey) + jwtTerm := ast.NewTerm(ast.String(jwt)) + + constraints := ast.ObjectTerm( + ast.Item(ast.StringTerm("cert"), ast.StringTerm(keys)), + ast.Item(ast.StringTerm("iss"), ast.StringTerm("bar")), + ) + + err := builtinJWTDecodeVerify(bctx, []*ast.Term{jwtTerm, constraints}, iter) + if err != nil { + t.Fatalf("unexpected error: %q", err) + } + + if verified { + t.Fatal("expected token to fail verification") + } + + k := createTokenCacheKey(ast.String(jwt), keysTerm.Value) + if _, ok := bctx.InterQueryBuiltinValueCache.GetCache(tokenCacheName).Get(k); !ok { + t.Fatal("expected token to be cached") + } + }) + + t.Run("nbf constraint check", func(t *testing.T) { + now := time.Second * 0 + + // Token's nbf is 1 sec in the future. + jwt := createJwtT(t, fmt.Sprintf(`{"i": "foo", "nbf": %d}`, 1), privateKey) + jwtTerm := ast.NewTerm(ast.String(jwt)) + + bctx.Time = ast.NumberTerm(int64ToJSONNumber(int64(now))) + + err := builtinJWTDecodeVerify(bctx, []*ast.Term{jwtTerm, keysTerm}, iter) + if err != nil { + t.Fatalf("unexpected error: %q", err) + } + + // Token's nbf is in the future, so it should not be verified. + if verified { + t.Fatal("expected token to fail verification") + } + + k := createTokenCacheKey(ast.String(jwt), keysTerm.Value) + if _, ok := bctx.InterQueryBuiltinValueCache.GetCache(tokenCacheName).Get(k); !ok { + t.Fatal("expected token to be cached") + } + + // Move time to the future, so the token is now valid. + now = time.Second * 2 + bctx.Time = ast.NumberTerm(int64ToJSONNumber(int64(now))) + + err = builtinJWTDecodeVerify(bctx, []*ast.Term{jwtTerm, keysTerm}, iter) + if err != nil { + t.Fatalf("unexpected error: %q", err) + } + + if !verified { + t.Fatal("expected token to be successfully verified") + } + }) + + t.Run("exp constraint check", func(t *testing.T) { + now := time.Second * 0 + + // Token's exp is 1 sec in the future. + jwt := createJwtT(t, fmt.Sprintf(`{"i": "foo", "exp": %d}`, 1), privateKey) + jwtTerm := ast.NewTerm(ast.String(jwt)) + + bctx.Time = ast.NumberTerm(int64ToJSONNumber(int64(now))) + + err := builtinJWTDecodeVerify(bctx, []*ast.Term{jwtTerm, keysTerm}, iter) + if err != nil { + t.Fatalf("unexpected error: %q", err) + } + + // Token's exp is in the future, so it should be verified. + if !verified { + t.Fatal("expected token to be successfully verified") + } + + k := createTokenCacheKey(ast.String(jwt), keysTerm.Value) + if _, ok := bctx.InterQueryBuiltinValueCache.GetCache(tokenCacheName).Get(k); !ok { + t.Fatal("expected token to be cached") + } + + // Move time to the future, so the token is now expired. + now = time.Second * 2 + bctx.Time = ast.NumberTerm(int64ToJSONNumber(int64(now))) + + err = builtinJWTDecodeVerify(bctx, []*ast.Term{jwtTerm, keysTerm}, iter) + if err != nil { + t.Fatalf("unexpected error: %q", err) + } + + if verified { + t.Fatal("expected token to fail verification") + } + }) + }) +} + +func createJwtT(t *testing.T, payload string, privateKey string) string { + t.Helper() + + jwt, err := createJwt(payload, privateKey) + if err != nil { + t.Fatal(err) + } + + return jwt +} + +func createBadJwt(t *testing.T, payload string) string { + t.Helper() + + return strings.Join( + []string{ + base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"RS256"}`)), + base64.RawURLEncoding.EncodeToString([]byte(payload)), + base64.RawURLEncoding.EncodeToString([]byte(`bad_signature`)), + }, ".", + ) +} + +func createJwt(payload string, privateKey string) (string, error) { + const hdr = `{"alg":"RS256"}` + + var jwkKeySet *jwk.Set + jwkKeySet, err := jwk.ParseString(privateKey) + if err != nil { + return "", fmt.Errorf("failed to parse JWK: %s", err.Error()) + } + signer, err := sign.New(jwa.RS256) + if err != nil { + return "", fmt.Errorf("failed to create signer: %s", err.Error()) + } + + hdrStr := base64.RawURLEncoding.EncodeToString([]byte(hdr)) + payloadStr := base64.RawURLEncoding.EncodeToString([]byte(payload)) + signingInput := hdrStr + "." + payloadStr + + pk, err := jwkKeySet.Keys[0].Materialize() + if err != nil { + return "", fmt.Errorf("failed to materialize key: %s", err.Error()) + } + signature, err := signer.Sign([]byte(signingInput), pk) + if err != nil { + return "", fmt.Errorf("failed to sign message: %s", err.Error()) + } + encSignature := base64.RawURLEncoding.EncodeToString(signature) + encoded := signingInput + "." + encSignature + + return encoded, nil +} + +func TestBuiltinJWTVerify_TokenCache(t *testing.T) { + tests := []struct { + note string + jwt string + key string + badKey string + builtin func(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error + }{ + { + note: "HS256", + jwt: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJhenAiOiJhbGljZSIsInN1Ym9yZGluYXRlcyI6W10sImhyIjpmYWxzZX0.rz3jTY033z-NrKfwrK89_dcLF7TN4gwCMj-fVBDyLoM`, + key: `secret`, + badKey: `bad_secret`, + builtin: builtinJWTVerifyHS256, + }, + { + note: "HS384", + jwt: `eyJhbGciOiJIUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.g98lHYzuqINVppLMoEZT7jlpX0IBSo9zKGoN9DhQg7Ua3YjLXbJMjzESjIHXOGLB`, + key: `secret`, + badKey: `bad_secret`, + builtin: builtinJWTVerifyHS384, + }, + { + note: "HS512", + jwt: `eyJhbGciOiJIUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.F6-xviRhK2OLcJJHFivhQqMN_dgX5boDrwbVKkdo9flQQNk-AaKpH3uYycFvBEd_erVefcsri_PkL4fjLSZ7ZA`, + key: `secret`, + badKey: `bad_secret`, + builtin: builtinJWTVerifyHS512, + }, + { + note: "RS256", + jwt: `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.N0-EVdv5pvUfZYFRzMGnsWpNLHgwMEgViPwpuLBEtt32682OgnOK-N4X-2gpQEjQIbUr0IFym8YsRQU9GZvqQP72Sd6yOQNGSNeE74DpUZCAjBa9SBIb1UlD2MxZB-e7YJiEyo7pZhimaqorXrgorlaXYGMvsCFWDYmBLzGaGYaGJyEpkZHzHb7ujsDrJJjdEtDV3kh13gTHzLPvqnoXuuxelXye_8LPIhvgDy52gT4shUEso71pJCMv_IqAR19ljVE17lJzoi6VhRn6ReNUE-yg4KfCO4Ypnuu-mcQr7XtmSYoWkX72L5UQ-EyWkoz-w0SYKoJTPzHkTL2thYStksVpeNkGuck25aUdtrQgmPbao0QOWBFlkg03e6mPCD2-aXOt1ofth9mZGjxWMHX-mUqHaNmaWM3WhRztJ73hWrmB1YOdYQtOEHejfvR_td5tqIw4W6ufRy2ScOypGQe7kNaUZxpgxZ1927ZGNiQgawIOAQwXOcFx1JNSEIeg55-cYJrHPxsXGOB9ZxW-qnswmFJp474iUVXjzGhLexJDXBwvKGs_O3JFjMsvyV9_hm7bnQU0vG_HgPYs5i9VOHRMujq1vFBcm52TFVOBGdWaGfb9RRdLLYvVkJLk0Poh19rsCWb7-Vc3mAaGGpvuk4Wv-PnGGNC-V-FQqIbijHDrn_g`, + key: "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", + badKey: "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkSgAwIBAgIBLjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEV\nMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMD\nRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDg1MTAzWhcNMjAwNTA3\nMTA1MTAzWjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4w\nDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3Qw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeRmygX/fOOUu5Wm91PFNo\nsHDG1CzG9a1iKBjUeMgi9bXXScUfatPmsNlxb56uSi0RXUsvJmY/yxkIIhRyapxW\n49j2idAM3SGGL1nOZf/XdpDHYsAFFZ237HGb8DOEk/p3xCFv0tH/iQ+kLP36EM1+\ntn6BfUXdJnVyvkSK2iMNeRY7A4DMX7sGX39LXsVJiCokIC8E0QUFrSjvrAm9ejKE\ntPojydo4c3VUxLfmFuyMXoD3bfk1Jv5i2J5RjtomjgK6zNCvgYzpspiodHChkzlU\nX8yk2YqlAHX3XdJA94LaDE2kNXiOQnFkUb8GsP7hmEbwGtMUEQie+jfgKplxJ49B\nAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAM\nBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQC9f2/kxT7DnQ94ownhHvd6\nrzk1WirI90rFM2MxhfkaDrOHhSGZL9nDf6TIZ4qeFKZXthpKxpiZm2Oxmn+vUsik\nW6bYjq1nX0GCchQLaaFf9Jh1IOLwkfoBdX55tV8xUGHRWgDlCuGbqiixz+Bm0Kap\nkmbyJynVcoiKhdLyYm/YTn/pC32SJW666reQ+0qCAoxzLQowBetHjwDam9RsDEf4\n+JRDjYPutNXyJ5X8BaBA6PzHanzMG/7RFYcx/2YhXwVxdfPHku4ALJcddIGAGNx2\n5yte+HY0aEu+06J67eD9+4fU7NixRMKigk9KbjqpeWD+0be+VgX8Dot4jaISgI/3\n-----END CERTIFICATE-----", + builtin: builtinJWTVerifyRS256, + }, + { + note: "RS384", + jwt: `eyJhbGciOiJSUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.b__y2zjqMoD7iWbHeQ0lNpnche3ph5-AwrIQICLMQQGtEz9WMBteHydkC5g01bm3TBX1d04Z5IEOsuK6btAtWma04c5NYqaUyNEUJKYCFoY02uH0jGdGfL6R5Kkv0lkNvN0s3Nex9jMaVVgqx8bcrOU0uRBFT67sXcm11LHaB9BwKFslolzHClxgXy5RIZb4OFk_7Yk7xTC6PcvEWkkGR9uXBhfDEig5WqdwOWPeulimvARDw14U35rzeh9xpGAPjBKeE-y20fXAk0cSF1H69C-Qa1jDQheYIrAJ6XMYGNZWuay5-smmeefe67eweEt1q-AD1NFepqkmZX382DGuYQ`, + key: "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkSgAwIBAgIBLjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEV\nMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMD\nRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDg1MTAzWhcNMjAwNTA3\nMTA1MTAzWjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4w\nDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3Qw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeRmygX/fOOUu5Wm91PFNo\nsHDG1CzG9a1iKBjUeMgi9bXXScUfatPmsNlxb56uSi0RXUsvJmY/yxkIIhRyapxW\n49j2idAM3SGGL1nOZf/XdpDHYsAFFZ237HGb8DOEk/p3xCFv0tH/iQ+kLP36EM1+\ntn6BfUXdJnVyvkSK2iMNeRY7A4DMX7sGX39LXsVJiCokIC8E0QUFrSjvrAm9ejKE\ntPojydo4c3VUxLfmFuyMXoD3bfk1Jv5i2J5RjtomjgK6zNCvgYzpspiodHChkzlU\nX8yk2YqlAHX3XdJA94LaDE2kNXiOQnFkUb8GsP7hmEbwGtMUEQie+jfgKplxJ49B\nAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAM\nBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQC9f2/kxT7DnQ94ownhHvd6\nrzk1WirI90rFM2MxhfkaDrOHhSGZL9nDf6TIZ4qeFKZXthpKxpiZm2Oxmn+vUsik\nW6bYjq1nX0GCchQLaaFf9Jh1IOLwkfoBdX55tV8xUGHRWgDlCuGbqiixz+Bm0Kap\nkmbyJynVcoiKhdLyYm/YTn/pC32SJW666reQ+0qCAoxzLQowBetHjwDam9RsDEf4\n+JRDjYPutNXyJ5X8BaBA6PzHanzMG/7RFYcx/2YhXwVxdfPHku4ALJcddIGAGNx2\n5yte+HY0aEu+06J67eD9+4fU7NixRMKigk9KbjqpeWD+0be+VgX8Dot4jaISgI/3\n-----END CERTIFICATE-----", + badKey: "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", + builtin: builtinJWTVerifyRS384, + }, + { + note: "RS512", + jwt: `eyJhbGciOiJSUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VSe3qK5Gp0Q0_5nRgMFu25yw74FIgX-kXPOemSi62l-AxeVdUw8rOpEFrSTCaVjd3mPfKb-B056a-gtrbpXK9sUQnFdqdsyt8gHK-umz5lVyWfoAgj51Ontv-9K_pRORD9wqKqdTLZjCxJ5tyKoO0gY3SwwqSqGrp85vUjvEcK3jbMKINGRUNnOokeSm7byUEJsfKVUbPboSX1TGyvjDOZxxSITj8-bzZZ3F21DJ23N2IiJN7FW8Xj-SYyphXo-ML50o5bjW9YlQ5BDk-RW1I4eE-KpsxhApPv_xIgE8d89PVtXFuoJtv0yLRaZ1q04Fl9KNoMyZrmr349yppn0JlQ`, + key: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3kZsoF/3zjlLuVpvdTxT\naLBwxtQsxvWtYigY1HjIIvW110nFH2rT5rDZcW+erkotEV1LLyZmP8sZCCIUcmqc\nVuPY9onQDN0hhi9ZzmX/13aQx2LABRWdt+xxm/AzhJP6d8Qhb9LR/4kPpCz9+hDN\nfrZ+gX1F3SZ1cr5EitojDXkWOwOAzF+7Bl9/S17FSYgqJCAvBNEFBa0o76wJvXoy\nhLT6I8naOHN1VMS35hbsjF6A9235NSb+YtieUY7aJo4CuszQr4GM6bKYqHRwoZM5\nVF/MpNmKpQB1913SQPeC2gxNpDV4jkJxZFG/BrD+4ZhG8BrTFBEInvo34CqZcSeP\nQQIDAQAB\n-----END PUBLIC KEY-----", + badKey: "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", + builtin: builtinJWTVerifyRS512, + }, + { + note: "PS256", + jwt: `eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJQUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiZm9vIjogImJhciJ9.i0F3MHWzOsBNLqjQzK1UVeQid9xPMowCoUsoM-C2BDxUY-FMKmCeJ1NJ4TGnS9HzFK1ftEvRnPT7EOxOkHPoCk1rz3feTFgtHtNzQqLM1IBTnz6aHHOrda_bKPHH9ZIYCRQUPXhpC90ivW_IJR-f7Z1WLrMXaJ71i1XteruENHrJJJDn0HedHG6N0VHugBHrak5k57cbE31utAdx83TEd8v2Y8wAkCJXKrdmTa-8419LNxW_yjkvoDD53n3X5CHhYkSymU77p0v6yWO38qDWeKJ-Fm_PrMAo72_rizDBj_yPa5LA3bT_EnsgZtC-sp8_SCDIH41bjiCGpRHhqgZmyw`, + key: `{"kty":"RSA","e":"AQAB","kid":"bf688c97-bf51-49ba-b9d3-115195bb0eb8","n":"uJApsyzFv-Y85M5JjezHvMDw_spgVCI7BqpYhnzK3xXw1dnkz1bWXGA9yF6AeADlE-1yc1ozrAURTnFSihIgj414i3MC2_0FkNcdAbnX7d9q9_jdCkHda4HER0zzXCaHlgnzoAz6edUU800-h0LleLnfgg4UST-0DFTCIGpfTbs7OPSy2WgT1vP6xbB45CUOJA7o0q6XE-hdhWWN0plrDiYD-0Y1SpOQYXmHhSmr-WVeKeoh5_0zeEVab6TQYec_16ByEyepaZB0g6WyGkFE6aG1NrpvDd24s_h7BAJg_S2mtu1lKWEqYjOgwzEl5XQQyXbpnq1USb12ArX16rZdew"}`, + badKey: "-----BEGIN CERTIFICATE-----\nMIIFiDCCA3ACCQCGV6XsfG/oRTANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEO\nMAwGA1UECgwFU3R5cmExDDAKBgNVBAsMA0RldjESMBAGA1UEAwwJbG9jYWxob3N0\nMRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5cmEwHhcNMTgwMzA2MDAxNTU5WhcNMTkw\nMzA2MDAxNTU5WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx\nFTATBgNVBAcMDFJlZHdvb2QgQ2l0eTEOMAwGA1UECgwFU3R5cmExDDAKBgNVBAsM\nA0RldjESMBAGA1UEAwwJbG9jYWxob3N0MRgwFgYJKoZIhvcNAQkBFglhc2hAc3R5\ncmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDucnAwTRA0zqDQ671L\nKWOVwhjhycFyzyhZUd7vhsnslOBiYM6TYIDXhETfAk2RQoRE/9xF16woMD8FOglc\nlSuhi+GNfFRif6LfArm84ZFj1ZS1MX2logikhXhRJQ7AOHe5+ED0re3KH5lWyqfz\nR6bQuPYwTQSBJy6Tq7T9RiOM29yadCX64OaCEbzEFmHtNlbb5px4zCVvgskg/fpV\nGGCMpAYjGDatbxE5eAloVs1EJuI5RSqWr1JRm6EejxM04BFdfGn1HgWrsKXtlvBa\n00/AC0zXL5n6LK7+L3WbRguVTZcE4Yu70gDwhmM+VsKeT9LKClX003BNj0NJDRB9\ndw9MaWxsXDNHNOWEfbnASXeP7ZRv3D81ftij6P8SL14ZnxyrRty8TAN4ij3wd41l\nastRQCtrJFi+HzO606XOp6HDzBoWT0DGl8Sn2hZ6RLPyBnD04vvvcSGeCVjHGOQ8\nc3OTroK58u5MR/q4T00sTkeeVAxuKoEWKsjIBYYrJTe/a2mEq9yiDGbPNYDnWnQZ\njSUZm+Us23Y2sm/agZ5zKXcEuoecGL6sYCixr/xeB9BPxEiTthH+0M8OY99qpIhz\nSmj41wdgQfzZi/6B8pIr77V/KywYKxJEmzw8Uy48aC/rZ8WsT8QdKwclo1aiNJhx\n79OvGbZFoeHD/w7igpx+ttpF/wIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQC3wWUs\nfXz+aSfFVz+O3mLFkr65NIgazbGAySgMgMNVuadheIkPL4k21atyflfpx4pg9FGv\n40vWCLMajpvynfz4oqah0BACnpqzQ8Dx6HYkmlXK8fLB+WtPrZBeUEsGPKuJYt4M\nd5TeY3VpNgWOPXmnE4lvxHZqh/8OwmOpjBfC9E3e2eqgwiwOkXnMaZEPgKP6JiWk\nEFaQ9jgMQqJZnNcv6NmiqqsZeI0/NNjBpkmEWQl+wLegVusHiQ0FMBMQ0taEo21r\nzUwHoNJR3h3wgGQiKxKOH1FUKHBV7hEqObLraD/hfG5xYucJfvvAAP1iH0ycPs+9\nhSccrn5/HY1c9AZnW8Kh7atp/wFP+sHjtECWK/lUmXfhASS293hprCpJk2n9pkmR\nziXKJhjwkxlC8NcHuiVfaxdfDa4+1Qta2gK7GEypbvLoEmIt/dsYUsxUg84lwJJ9\nnyC/pfZ5a8wFSf186JeVH4kHd3bnkzlQz460HndOMSJ/Xi1wSfuZlOVupFf8TVKl\np4j28MTLH2Wqx50NssKThdaX6hoCiMqreYa+EVaN1f/cIGQxZSCzdzMCKqdB8lKB\n3Eax+5zsIa/UyPwGxZcyXBRHAlz5ZnkjuRxInyiMkBWWz3IZXjTe6Fq8BNd2UWNc\nw35+2nO5n1LKXgR2+nzhZUOk8TPsi9WUywRluQ==\n-----END CERTIFICATE-----", + builtin: builtinJWTVerifyPS256, + }, + { + note: "PS384", + jwt: `eyJhbGciOiJQUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.EHPUvPr6uJOYqdza95WbM1SYD8atZHJEVRggpwOWnHGsjQBoEarJb8QgW7TY22OXwGw2HWluTiyT_MAz02NaHRzZv6AgrmxCLChMWkCHLwPxqjs0xSvVAMLzHHq2X2Bcujo9KORGudR7zKz8pOX5Mfnm7Z6OGtqPCPLaIdVJlddNsG6a571NOuVuDWbcg0omeRDANZpCZMJeAQN2M-4Q61ef6zcQHK1R-QqzBhw6HzMgqR1LRJ0xbrmD-L5o53JM3pV1e1juKNXVK3vWkDQRCQORFn1lyH5isfSsiiHW-x90sUC7TrU_cOji4MMmOCME6kkwxe57ZgpeXtdVTvldpw`, + key: "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkSgAwIBAgIBKjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEV\nMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMD\nRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDkxMjU2WhcNMjAwNTA3\nMTExMjU2WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4w\nDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3Qw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtyVWH2FE8cU8LRcArH4Tw\nDhBOFcmJF28LrvRObcbDYsae6rEwby0aRgSxjTEMgyGBjroBSl22wOSA93kwx4pu\npfXEqbwywn9FhyKBb/OXQSglPmwrpmzQtPJGzBHncL+PPjRhPfqimwf7ZIPKAAgI\nz9O6ppGhE/x4Ct444jthUIBZuG5cUXhgiPBQdIQ3K88QhgVwcufTZkNHj4iSDfhl\nVFDHVjXjd2B/yGODjyv0TyChV0YBNGjMv7YFLWmIFUFzK+6qNSxu4czPtRkyfwaV\n2GW/PBT5f8fc6fKgQZ6k6BLK6+pi0iPh5TUizyHtqtueWDbrJ+wfdJilQS8D+EGr\nAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAM\nBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQBgVAM50/0aBTBxESYKIKN4\nE+qbV6aE0C+wYJLet0EWPTxwmFZamq5LNEO/D6xyoY5WHY60EyHRMs0agSB/ATBX\n5ULdEwh9G0NjxqivCcoddQ1fuVS2PrrqNL7VlRnYbTpd8/Dh4qnyl5FltlyZ/29L\ny7BWOwlcBlZdhsfH8svNX4PUxjRD+jmnczCDi7XSOKT8htKUV2ih1c9JrWpIhCi/\nHzEXkaAxNdhBNdIsLQMo3qq9fkSgNZQk9/ecJNPeuJ/UYyr5Xa4PxIWl4U+P7yuI\n+Q3FSPmUbiVsSGqMhh6V/DN8M+T5/KiSB47gFOfxc2/RR5aw4HkSp3WxwbT9njbE\n-----END CERTIFICATE-----", + badKey: `{"kty":"RSA","e":"AQAB","kid":"bf688c97-bf51-49ba-b9d3-115195bb0eb8","n":"uJApsyzFv-Y85M5JjezHvMDw_spgVCI7BqpYhnzK3xXw1dnkz1bWXGA9yF6AeADlE-1yc1ozrAURTnFSihIgj414i3MC2_0FkNcdAbnX7d9q9_jdCkHda4HER0zzXCaHlgnzoAz6edUU800-h0LleLnfgg4UST-0DFTCIGpfTbs7OPSy2WgT1vP6xbB45CUOJA7o0q6XE-hdhWWN0plrDiYD-0Y1SpOQYXmHhSmr-WVeKeoh5_0zeEVab6TQYec_16ByEyepaZB0g6WyGkFE6aG1NrpvDd24s_h7BAJg_S2mtu1lKWEqYjOgwzEl5XQQyXbpnq1USb12ArX16rZdew"}`, + builtin: builtinJWTVerifyPS384, + }, + { + note: "PS512", + jwt: `eyJhbGciOiJQUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.VRlkPtiUq5MmBNgyuBqxv2_aX40STrWrBB2sSmGbxI78jVG_3hVoh7Mk-wUmFL389qpf05xNdn-gpMe-MSDUux7U7EuFspFZdYTUBo9wRvEBe4e1rHUCG00lVdYCG7eEgbAxM3cUhrHRwExBte30qBrFFUY9FgG-kJdYhgyh7VquMGuKgiS8CP_H0Gp1mIvTw6eEnSFAoKiryw9edUZ78pHELNn4y18YZvEndeNZh7f19LCtrB0G2bJUHGM4vPcwo2D-UAhEFBpSlnnqXDLSWOhUgLNLu0kZACXhT808KT6fdF6eFihdThmWN7_HUz2znjrjs71CqqDJgLhkGs8UvQ`, + key: "-----BEGIN CERTIFICATE-----\nMIIDXDCCAkSgAwIBAgIBKjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEV\nMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMD\nRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDkxMjU2WhcNMjAwNTA3\nMTExMjU2WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4w\nDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3Qw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtyVWH2FE8cU8LRcArH4Tw\nDhBOFcmJF28LrvRObcbDYsae6rEwby0aRgSxjTEMgyGBjroBSl22wOSA93kwx4pu\npfXEqbwywn9FhyKBb/OXQSglPmwrpmzQtPJGzBHncL+PPjRhPfqimwf7ZIPKAAgI\nz9O6ppGhE/x4Ct444jthUIBZuG5cUXhgiPBQdIQ3K88QhgVwcufTZkNHj4iSDfhl\nVFDHVjXjd2B/yGODjyv0TyChV0YBNGjMv7YFLWmIFUFzK+6qNSxu4czPtRkyfwaV\n2GW/PBT5f8fc6fKgQZ6k6BLK6+pi0iPh5TUizyHtqtueWDbrJ+wfdJilQS8D+EGr\nAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAM\nBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQBgVAM50/0aBTBxESYKIKN4\nE+qbV6aE0C+wYJLet0EWPTxwmFZamq5LNEO/D6xyoY5WHY60EyHRMs0agSB/ATBX\n5ULdEwh9G0NjxqivCcoddQ1fuVS2PrrqNL7VlRnYbTpd8/Dh4qnyl5FltlyZ/29L\ny7BWOwlcBlZdhsfH8svNX4PUxjRD+jmnczCDi7XSOKT8htKUV2ih1c9JrWpIhCi/\nHzEXkaAxNdhBNdIsLQMo3qq9fkSgNZQk9/ecJNPeuJ/UYyr5Xa4PxIWl4U+P7yuI\n+Q3FSPmUbiVsSGqMhh6V/DN8M+T5/KiSB47gFOfxc2/RR5aw4HkSp3WxwbT9njbE\n-----END CERTIFICATE-----", + badKey: `{"kty":"RSA","e":"AQAB","kid":"bf688c97-bf51-49ba-b9d3-115195bb0eb8","n":"uJApsyzFv-Y85M5JjezHvMDw_spgVCI7BqpYhnzK3xXw1dnkz1bWXGA9yF6AeADlE-1yc1ozrAURTnFSihIgj414i3MC2_0FkNcdAbnX7d9q9_jdCkHda4HER0zzXCaHlgnzoAz6edUU800-h0LleLnfgg4UST-0DFTCIGpfTbs7OPSy2WgT1vP6xbB45CUOJA7o0q6XE-hdhWWN0plrDiYD-0Y1SpOQYXmHhSmr-WVeKeoh5_0zeEVab6TQYec_16ByEyepaZB0g6WyGkFE6aG1NrpvDd24s_h7BAJg_S2mtu1lKWEqYjOgwzEl5XQQyXbpnq1USb12ArX16rZdew"}`, + builtin: builtinJWTVerifyPS512, + }, + { + note: "ES256", + jwt: `eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJFUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiaXNzIjogInh4eCJ9.lArczfN-pIL8oUU-7PU83u-zfXougXBZj6drFeKFsPEoVhy9WAyiZlRshYqjTSXdaw8yw2L-ovt4zTUZb2PWMg`, + key: `{"kty":"EC","crv":"P-256","x":"z8J91ghFy5o6f2xZ4g8LsLH7u2wEpT2ntj8loahnlsE","y":"7bdeXLH61KrGWRdh7ilnbcGQACxykaPKfmBccTHIOUo"}`, + badKey: "-----BEGIN CERTIFICATE-----\nMIICDDCCAZOgAwIBAgIBIzAKBggqhkjOPQQDAzBWMQswCQYDVQQGEwJVUzEVMBMG\nA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2\nMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDk0MzU1WhcNMjAwNTA3MTE0\nMzU1WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYD\nVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwdjAQ\nBgcqhkjOPQIBBgUrgQQAIgNiAARjcwW7g9wx4ePsuwcVzDJCVo4f8I1C1X5US4B1\nrWN+5zFSJoGCKaPTXMDhAdS08D1G20AIRmA0AlVVXRxrZYZ+Y282O6s+EGsB5T1W\nMCnUFk2Sa+xZiGPApYz4zSGbNEqjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE\nDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMAoGCCqGSM49BAMDA2cAMGQCMGSG\nVjx3DZP71ZGNDBw+AVdhNU3pgJW8kNpqjta3HFLb6pzqNOsfOn1ZeIWciEcyEgIw\nTGxli48W1AJ2s7Pw+3wOA6f9HAmczJPaiZ9CY038UiT8mk+pND5FEdqLhT/5lMEz\n-----END CERTIFICATE-----", + builtin: builtinJWTVerifyES256, + }, + { + note: "ES384", + jwt: `eyJhbGciOiJFUzM4NCJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.w85PzWrIQbJBOROnah0pa8or2LsXWnj88bwG1R-zf5Mm20CaYGPKPTQEsU_y-dzaWyDV1Na7nfaGaH3Khcvj8yS-bidZ0OZVVFDk9oabX7ZYvAHo2pTAOfxc11TeOYSF`, + key: "-----BEGIN CERTIFICATE-----\nMIICDDCCAZOgAwIBAgIBIzAKBggqhkjOPQQDAzBWMQswCQYDVQQGEwJVUzEVMBMG\nA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2\nMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MDk0MzU1WhcNMjAwNTA3MTE0\nMzU1WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYD\nVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwdjAQ\nBgcqhkjOPQIBBgUrgQQAIgNiAARjcwW7g9wx4ePsuwcVzDJCVo4f8I1C1X5US4B1\nrWN+5zFSJoGCKaPTXMDhAdS08D1G20AIRmA0AlVVXRxrZYZ+Y282O6s+EGsB5T1W\nMCnUFk2Sa+xZiGPApYz4zSGbNEqjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE\nDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMAoGCCqGSM49BAMDA2cAMGQCMGSG\nVjx3DZP71ZGNDBw+AVdhNU3pgJW8kNpqjta3HFLb6pzqNOsfOn1ZeIWciEcyEgIw\nTGxli48W1AJ2s7Pw+3wOA6f9HAmczJPaiZ9CY038UiT8mk+pND5FEdqLhT/5lMEz\n-----END CERTIFICATE-----", + badKey: `{"kty":"EC","crv":"P-256","x":"z8J91ghFy5o6f2xZ4g8LsLH7u2wEpT2ntj8loahnlsE","y":"7bdeXLH61KrGWRdh7ilnbcGQACxykaPKfmBccTHIOUo"}`, + builtin: builtinJWTVerifyES384, + }, + { + note: "ES512", + jwt: `eyJhbGciOiJFUzUxMiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sIm5iZiI6MTQ1MTYwNjQwMH0.AYpssEoEqq9We9aKsnRykpECAVEOBRJJu8UgDzoL-F8fmB2LPxpS4Gl7D-9wAO5AJt4-9YSsgOb5FLc20MrZN30AAFYopZf75T1pEJQFrdDmOKT45abbrorcR7G_AHDbhBdDNM_R6GojYFg_HPxHndof745Yq5Tfw9PpJc-9kSyk6kqO`, + key: "-----BEGIN CERTIFICATE-----\nMIICWDCCAbmgAwIBAgIBAjAKBggqhkjOPQQDBDBWMQswCQYDVQQGEwJVUzEVMBMG\nA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYDVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2\nMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAwNTA3MTA1NDM3WhcNMjAwNTA3MTI1\nNDM3WjBWMQswCQYDVQQGEwJVUzEVMBMGA1UEBxMMUmVkd29vZCBDaXR5MQ4wDAYD\nVQQKEwVTdHlyYTEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QwgZsw\nEAYHKoZIzj0CAQYFK4EEACMDgYYABAHLm3IMD/88vC/S1cCTyjrCjwHIGsjibFBw\nPBXt36YKCjUdS7jiJJR5YQVPypSv7gPaKKn1E8CqkfVdd3rrp1TocAEms4XvigtW\nZBZzffw9xyZCgmtQ2dTHsufi/5W/Yx8N3Uw+D2wl1LKcJraouo+qgamGfuou6WbA\noPEtdOg0+B4jF6M1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUF\nBwMBMAwGA1UdEwEB/wQCMAAwCgYIKoZIzj0EAwQDgYwAMIGIAkIAzAAYDqMghX3S\n8UbS8s5TPAztJy9oNXFra5V8pPlUdNFc2ov2LN++scW46wCb/cJUyEc58sY7xFuK\nI5sCOkv95N8CQgFXmu354LZJ31zIovuUA8druOPe3TDnxMGwEEm2Lt43JNuhzNyP\nhJYh9/QKfe2AiwrLXEG4VVOIXdjq7vexl87evg==\n-----END CERTIFICATE-----", + badKey: `{"kty":"EC","crv":"P-256","x":"z8J91ghFy5o6f2xZ4g8LsLH7u2wEpT2ntj8loahnlsE","y":"7bdeXLH61KrGWRdh7ilnbcGQACxykaPKfmBccTHIOUo"}`, + builtin: builtinJWTVerifyES512, + }, + } + + ctx := context.Background() + + cacheConfig := cache.Config{ + InterQueryBuiltinValueCache: cache.InterQueryBuiltinValueCacheConfig{ + NamedCacheConfigs: map[string]*cache.NamedValueCacheConfig{ + tokenCacheName: { + MaxNumEntries: &[]int{5}[0], + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + var verified bool + iter := func(r *ast.Term) error { + verified = bool(r.Value.(ast.Boolean)) + return nil + } + + bctx := BuiltinContext{ + Context: ctx, + Time: ast.NumberTerm(int64ToJSONNumber(time.Now().UnixNano())), + InterQueryBuiltinValueCache: cache.NewInterQueryValueCache(ctx, &cacheConfig), + } + + t.Run("successful verification", func(t *testing.T) { + operands := []*ast.Term{ast.StringTerm(tc.jwt), ast.StringTerm(tc.key)} + err := tc.builtin(bctx, operands, iter) + if err != nil { + t.Fatalf("unexpected error: %q", err) + } + + if !verified { + t.Fatal("expected token to be successfully verified") + } + + k := createTokenCacheKey(ast.String(tc.jwt), ast.String(tc.key)) + if _, ok := bctx.InterQueryBuiltinValueCache.GetCache(tokenCacheName).Get(k); !ok { + t.Fatal("expected token to be cached") + } + }) + + t.Run("failed verification", func(t *testing.T) { + operands := []*ast.Term{ast.StringTerm(tc.jwt), ast.StringTerm(tc.badKey)} + err := tc.builtin(bctx, operands, iter) + if err != nil { + t.Fatalf("unexpected error: %q", err) + } + + if verified { + t.Fatal("expected token to fail verification") + } + + k := createTokenCacheKey(ast.String(tc.jwt), ast.String(tc.badKey)) + if _, ok := bctx.InterQueryBuiltinValueCache.GetCache(tokenCacheName).Get(k); !ok { + t.Fatal("expected token to be cached") + } + }) + }) + } +} diff --git a/third_party/opa/v1/topdown/topdown_bench_test.go b/third_party/opa/v1/topdown/topdown_bench_test.go new file mode 100644 index 000000000000..eaff7d8b8bee --- /dev/null +++ b/third_party/opa/v1/topdown/topdown_bench_test.go @@ -0,0 +1,984 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "bytes" + "context" + "fmt" + "math/rand" + "strconv" + "strings" + "sync" + "testing" + "text/template" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/metrics" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/util/test" +) + +func BenchmarkArrayIteration(b *testing.B) { + sizes := []int{10, 100, 1000, 10000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + benchmarkIteration(b, test.ArrayIterationBenchmarkModule(n)) + }) + } +} + +func BenchmarkArrayPlugging(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 100, 1000, 10000} + + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + data := make([]any, n) + for i := range n { + data[i] = fmt.Sprintf("whatever%d", i) + } + store := inmem.NewFromObject(map[string]any{"fixture": data}) + module := `package test + fixture := data.fixture + main if { x := fixture }` + + query := ast.MustParseBody("data.test.main") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + + q := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn) + + _, err := q.Run(ctx) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkSetIteration(b *testing.B) { + sizes := []int{10, 100, 1000, 10000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + benchmarkIteration(b, test.SetIterationBenchmarkModule(n)) + }) + } +} + +func BenchmarkObjectIteration(b *testing.B) { + sizes := []int{10, 100, 1000, 10000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + benchmarkIteration(b, test.ObjectIterationBenchmarkModule(n)) + }) + } +} + +func benchmarkIteration(b *testing.B, module string) { + ctx := context.Background() + query := ast.MustParseBody("data.test.main") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + + q := NewQuery(query).WithCompiler(compiler) + _, err := q.Run(ctx) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkLargeJSON(b *testing.B) { + data := test.GenerateLargeJSONBenchmarkData() + ctx := context.Background() + store := inmem.NewFromObject(data) + compiler := ast.NewCompiler() + + if compiler.Compile(nil); compiler.Failed() { + b.Fatal(compiler.Errors) + } + + b.ResetTimer() + + // Read data.values N times inside query. + query := ast.MustParseBody("data.keys[_] = x; data.values = y") + + for range b.N { + + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + + q := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn) + + _, err := q.Run(ctx) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + b.Fatal(err) + } + + } +} + +func BenchmarkConcurrency1(b *testing.B) { + benchmarkConcurrency(b, getParams(1, 0)) +} + +func BenchmarkConcurrency2(b *testing.B) { + benchmarkConcurrency(b, getParams(2, 0)) +} + +func BenchmarkConcurrency4(b *testing.B) { + benchmarkConcurrency(b, getParams(4, 0)) +} + +func BenchmarkConcurrency8(b *testing.B) { + benchmarkConcurrency(b, getParams(8, 0)) +} + +func BenchmarkConcurrency4Readers1Writer(b *testing.B) { + benchmarkConcurrency(b, getParams(4, 1)) +} + +func BenchmarkConcurrency8Writers(b *testing.B) { + benchmarkConcurrency(b, getParams(0, 8)) +} + +func benchmarkConcurrency(b *testing.B, params []storage.TransactionParams) { + + mod, data := test.GenerateConcurrencyBenchmarkData() + ctx := context.Background() + store := inmem.NewFromObject(data) + mods := map[string]*ast.Module{"module": ast.MustParseModule(mod)} + compiler := ast.NewCompiler() + + if compiler.Compile(mods); compiler.Failed() { + b.Fatalf("Unexpected compiler error: %v", compiler.Errors) + } + + b.ResetTimer() + + for range b.N { + wg := new(sync.WaitGroup) + queriesPerCore := 1000 / len(params) + for j := range params { + param := params[j] // capture j'th params before goroutine + wg.Add(1) + go func() { + defer wg.Done() + for range queriesPerCore { + txn := storage.NewTransactionOrDie(ctx, store, param) + query := NewQuery(ast.MustParseBody("data.test.p = x")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn) + rs, err := query.Run(ctx) + if err != nil { + b.Errorf("Unexpected topdown query error: %v", err) + return + } + if len(rs) != 1 || !rs[0][ast.Var("x")].Equal(ast.BooleanTerm(true)) { + b.Errorf("Unexpected undefined/extra/bad result: %v", rs) + return + } + store.Abort(ctx, txn) + } + }() + } + + wg.Wait() + } +} + +func getParams(nReaders, nWriters int) (sl []storage.TransactionParams) { + for range nReaders { + sl = append(sl, storage.TransactionParams{}) + } + for range nWriters { + sl = append(sl, storage.WriteParams) + } + return sl +} + +func BenchmarkVirtualDocs1x1(b *testing.B) { + runVirtualDocsBenchmark(b, 1, 1) +} + +func BenchmarkVirtualDocs10x1(b *testing.B) { + runVirtualDocsBenchmark(b, 10, 1) +} + +func BenchmarkVirtualDocs100x1(b *testing.B) { + runVirtualDocsBenchmark(b, 100, 1) +} + +func BenchmarkVirtualDocs1000x1(b *testing.B) { + runVirtualDocsBenchmark(b, 1000, 1) +} + +func BenchmarkVirtualDocs10x10(b *testing.B) { + runVirtualDocsBenchmark(b, 10, 10) +} + +func BenchmarkVirtualDocs100x10(b *testing.B) { + runVirtualDocsBenchmark(b, 100, 10) +} + +func BenchmarkVirtualDocs1000x10(b *testing.B) { + runVirtualDocsBenchmark(b, 1000, 10) +} + +func BenchmarkVirtualDocs100x100(b *testing.B) { + runVirtualDocsBenchmark(b, 100, 100) +} + +func BenchmarkVirtualDocs1000x100(b *testing.B) { + runVirtualDocsBenchmark(b, 1000, 100) +} + +func BenchmarkVirtualDocs1000x1000(b *testing.B) { + runVirtualDocsBenchmark(b, 1000, 1000) +} + +func runVirtualDocsBenchmark(b *testing.B, numTotalRules, numHitRules int) { + + mod, inp := test.GenerateVirtualDocsBenchmarkData(numTotalRules, numHitRules) + ctx := context.Background() + compiler := ast.NewCompiler() + mods := map[string]*ast.Module{"module": ast.MustParseModule(mod)} + input := ast.NewTerm(ast.MustInterfaceToValue(inp)) + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store) + if compiler.Compile(mods); compiler.Failed() { + b.Fatalf("Unexpected compiler error: %v", compiler.Errors) + } + + query := NewQuery(ast.MustParseBody("data.a.b.c.allow = x")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithInput(input) + + b.ResetTimer() + + for range b.N { + rs, err := query.Run(ctx) + if err != nil { + b.Fatalf("Unexpected topdown query error: %v", err) + } + if len(rs) != 1 || !rs[0][ast.Var("x")].Equal(ast.BooleanTerm(true)) { + b.Fatalf("Unexpected undefined/extra/bad result: %v", rs) + } + } +} + +func BenchmarkPartialEval(b *testing.B) { + sizes := []int{1, 10, 100, 1000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + runPartialEvalBenchmark(b, n) + }) + } +} + +func BenchmarkPartialEvalCompile(b *testing.B) { + sizes := []int{1, 10, 100, 1000} + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + runPartialEvalCompileBenchmark(b, n) + }) + } +} + +func runPartialEvalBenchmark(b *testing.B, numRoles int) { + ctx := context.Background() + compiler := ast.NewCompiler() + + if compiler.Compile(map[string]*ast.Module{"authz": ast.MustParseModule(partialEvalBenchmarkPolicy)}); compiler.Failed() { + b.Fatal(compiler.Errors) + } + + var partials []ast.Body + var support []*ast.Module + data := generatePartialEvalBenchmarkData(numRoles) + store := inmem.NewFromObject(data) + + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + query := NewQuery(ast.MustParseBody("data.authz.allow = true")). + WithUnknowns([]*ast.Term{ast.MustParseTerm("input")}). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn) + var err error + partials, support, err = query.PartialRun(ctx) + return err + }) + if err != nil { + b.Fatal(err) + } + + if len(partials) != numRoles { + b.Fatal("Expected exactly one partial query result but got:", partials) + } else if len(support) != 0 { + b.Fatal("Expected no partial support results but got:", support) + } + + module := ast.MustParseModule(`package partial.authz`) + + for _, query := range partials { + rule := &ast.Rule{ + Head: ast.NewHead(ast.Var("allow"), nil, ast.BooleanTerm(true)), + Body: query, + Module: module, + } + module.Rules = append(module.Rules, rule) + } + + compiler = ast.NewCompiler() + compiler.Compile(map[string]*ast.Module{ + "partial": module, + }) + if compiler.Failed() { + b.Fatal(compiler.Errors) + } + + input := generatePartialEvalBenchmarkInput(numRoles) + + err = storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + query := NewQuery(ast.MustParseBody("data.partial.authz.allow = true")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithInput(input) + b.ResetTimer() + for range b.N { + qrs, err := query.Run(ctx) + if len(qrs) != 1 || err != nil { + b.Fatal("Unexpected query result:", qrs, "err:", err) + } + } + return nil + }) + if err != nil { + b.Fatal(err) + } +} + +func runPartialEvalCompileBenchmark(b *testing.B, numRoles int) { + + ctx := context.Background() + data := generatePartialEvalBenchmarkData(numRoles) + store := inmem.NewFromObject(data) + + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + + b.ResetTimer() + + for range b.N { + // compile original policy + compiler := ast.NewCompiler() + compiler.Compile(map[string]*ast.Module{ + "authz": ast.MustParseModule(partialEvalBenchmarkPolicy), + }) + if compiler.Failed() { + return compiler.Errors + } + + // run partial evaluation + var partials []ast.Body + var support []*ast.Module + query := NewQuery(ast.MustParseBody("data.authz.allow = true")). + WithUnknowns([]*ast.Term{ast.MustParseTerm("input")}). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn) + var err error + partials, support, err = query.PartialRun(ctx) + if err != nil { + return err + } + + if len(partials) != numRoles { + b.Fatal("Expected exactly one partial query result but got:", partials) + } else if len(support) != 0 { + b.Fatal("Expected no partial support results but got:", support) + } + + // recompile output + module := ast.MustParseModule(`package partial.authz`) + + for _, query := range partials { + rule := &ast.Rule{ + Head: ast.NewHead(ast.Var("allow"), nil, ast.BooleanTerm(true)), + Body: query, + Module: module, + } + module.Rules = append(module.Rules, rule) + } + + compiler = ast.NewCompiler() + compiler.Compile(map[string]*ast.Module{ + "test": module, + }) + + if compiler.Failed() { + b.Fatal(compiler.Errors) + } + } + + return nil + }) + + if err != nil { + b.Fatal(err) + } +} + +const partialEvalBenchmarkPolicy = `package authz + + default allow = false + + allow if { + user_has_role[role_name] + role_has_permission[role_name] + } + + user_has_role contains role_name if { + data.bindings[_] = binding + binding.iss = input.iss + binding.group = input.group + role_name = binding.role + } + + role_has_permission contains role_name if { + data.roles[_] = role + role.name = role_name + role.operation = input.operation + role.resource = input.resource + } + ` + +func generatePartialEvalBenchmarkData(numRoles int) map[string]any { + roles := make([]any, numRoles) + bindings := make([]any, numRoles) + for i := range numRoles { + role := map[string]any{ + "name": fmt.Sprintf("role-%d", i), + "operation": fmt.Sprintf("operation-%d", i), + "resource": fmt.Sprintf("resource-%d", i), + } + roles[i] = role + binding := map[string]any{ + "name": fmt.Sprintf("binding-%d", i), + "iss": fmt.Sprintf("iss-%d", i), + "group": fmt.Sprintf("group-%d", i), + "role": role["name"], + } + bindings[i] = binding + } + return map[string]any{ + "roles": roles, + "bindings": bindings, + } +} + +func generatePartialEvalBenchmarkInput(numRoles int) *ast.Term { + + tmpl, err := template.New("Test").Parse(`{ + "operation": "operation-{{ . }}", + "resource": "resource-{{ . }}", + "iss": "iss-{{ . }}", + "group": "group-{{ . }}" + }`) + if err != nil { + panic(err) + } + + var buf bytes.Buffer + + err = tmpl.Execute(&buf, numRoles-1) + if err != nil { + panic(err) + } + + return ast.MustParseTerm(buf.String()) +} + +func BenchmarkWalk(b *testing.B) { + + ctx := context.Background() + sizes := []int{100, 1000, 2000, 3000} + + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + data := genWalkBenchmarkData(n) + store := inmem.NewFromObject(data) + compiler := ast.NewCompiler() + query := ast.MustParseBody(fmt.Sprintf(`walk(data, [["arr", %v], x])`, n-1)) + compiledQuery, err := compiler.QueryCompiler().Compile(query) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + q := NewQuery(compiledQuery). + WithStore(store). + WithCompiler(compiler). + WithTransaction(txn) + rs, err := q.Run(ctx) + if err != nil || len(rs) != 1 || !rs[0][ast.Var("x")].Equal(ast.IntNumberTerm(n-1)) { + b.Fatal("Unexpected result:", rs, "err:", err) + } + return nil + }) + if err != nil { + b.Fatal(err) + } + } + }) + } + +} + +func genWalkBenchmarkData(n int) map[string]any { + sl := make([]any, n) + for i := range n { + sl[i] = i + } + return map[string]any{ + "arr": sl, + } +} + +func BenchmarkComprehensionIndexing(b *testing.B) { + ctx := context.Background() + cases := []struct { + note string + module string + query string + }{ + { + note: "arrays", + module: ` + package test + + bench_array if { + v := data.items[_] + ks := [k | some k; v == data.items[k]] + } + `, + query: `data.test.bench_array = true`, + }, + { + note: "sets", + module: ` + package test + + bench_set if { + v := data.items[_] + ks := {k | some k; v == data.items[k]} + } + `, + query: `data.test.bench_set = true`, + }, + { + note: "objects", + module: ` + package test + + bench_object if { + v := data.items[_] + ks := {k: 1 | some k; v == data.items[k]} + } + `, + query: `data.test.bench_object = true`, + }, + } + + sizes := []int{10, 100, 1000} + for _, tc := range cases { + for _, n := range sizes { + b.Run(fmt.Sprintf("%v_%v", tc.note, n), func(b *testing.B) { + data := genComprehensionIndexingData(n) + store := inmem.NewFromObject(data) + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": tc.module, + }) + query, err := compiler.QueryCompiler().Compile(ast.MustParseBody(tc.query)) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + err = storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + m := metrics.New() + instr := NewInstrumentation(m) + q := NewQuery(query).WithStore(store).WithCompiler(compiler).WithTransaction(txn).WithInstrumentation(instr) + rs, err := q.Run(ctx) + if m.Counter(evalOpComprehensionCacheMiss).Value().(uint64) > 0 { + b.Fatal("expected zero cache misses") + } + if err != nil || len(rs) != 1 { + b.Fatal("Unexpected result:", rs, "err:", err) + } + return nil + }) + if err != nil { + b.Fatal(err) + } + + } + }) + } + } +} + +func BenchmarkFunctionArgumentIndex(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 100, 1000} + + for _, n := range sizes { + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": moduleWithDefs(n), + }) + body := ast.MustParseBody(fmt.Sprintf("data.test.f(%d, x)", n)) + + b.Run(strconv.Itoa(n), func(b *testing.B) { + for range b.N { + q := NewQuery(body). + WithCompiler(compiler). + WithIndexing(true) + + res, err := q.Run(ctx) + if err != nil { + b.Fatal(err) + } + + if len(res) != 1 { + b.Fatalf("Expected one result, got %d", len(res)) + } + if !ast.Boolean(true).Equal(res[0][ast.Var("x")].Value) { + b.Errorf("expected x=>true, got %v", res[0]) + } + } + }) + } +} + +func moduleWithDefs(n int) string { + var b strings.Builder + + b.WriteString(`package test +`) + for i := 1; i <= n; i++ { + fmt.Fprintf(&b, `f(x) = y if { y := true; x == %[1]d } +`, i) + } + return b.String() +} + +func genComprehensionIndexingData(n int) map[string]any { + items := map[string]any{} + for i := range n { + items[strconv.Itoa(i)] = strconv.Itoa(i) + } + return map[string]any{"items": items} +} + +func BenchmarkObjectSubset(b *testing.B) { + ctx := context.Background() + + sizes := []int{10, 100, 1000, 10000} + + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + all := make(map[string]string) + evens := make(map[string]string) + + for i := range n { + all[strconv.Itoa(i)] = strconv.Itoa(i * 2) + if i%2 == 0 { + evens[strconv.Itoa(i)] = strconv.Itoa(i * 2) + } + } + + store := inmem.NewFromObject(map[string]any{"all": all, "evens": evens}) + + module := `package test + main if {object.subset(data.all, data.evens)}` + + query := ast.MustParseBody("data.test.main") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + + q := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn) + + _, err := q.Run(ctx) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkObjectSubsetSlow(b *testing.B) { + // This benchmarks the suggested means to implement object.subset + // without using the builtin, to give us an idea of whether or not + // the builtin is actually making things any faster. + ctx := context.Background() + + sizes := []int{10, 100, 1000, 10000} + + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + all := make(map[string]string) + evens := make(map[string]string) + + for i := range n { + all[strconv.Itoa(i)] = strconv.Itoa(i * 2) + if i%2 == 0 { + evens[strconv.Itoa(i)] = strconv.Itoa(i * 2) + } + } + + store := inmem.NewFromObject(map[string]any{"all": all, "evens": evens}) + + // Code is lifted from here: + // https://github.com/open-policy-agent/opa/issues/4358#issue-1141145857 + + module := `package test + path_matches contains match if { + [path, value] := walk(data.evens) + not is_object(value) + + match := object.get(data.all, path, null) == value + } + + main if { path_matches == {true} }` + + query := ast.MustParseBody("data.test.main") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + + q := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn) + + _, err := q.Run(ctx) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + b.Fatal(err) + } + } + }) + } +} + +// randomString constructs and returns a string of the specified length +// containing a random assortment of characters from the given symbols list. +func randomString(symbols []rune, length int) string { + builder := strings.Builder{} + for range length { + builder.WriteRune(symbols[rand.Intn(len(symbols))]) + } + return builder.String() +} + +func BenchmarkGlob(b *testing.B) { + ctx := context.Background() + + // Benchmark Strategy: + // + // We want to test both matching and non-matching, in case one is + // slower than the other. We also want to test a variety of different + // patterns, since (at time of writing), there is a pattern cache, so + // we want to exercise it but not rely on it excessively. + // + // For each individual test case, we generate size/2 random strings + // from the letters a,b,c,d as well as size/2 with those letters plus + // x. We test matching with the following globs: + // + // * "*x*" (should always have matches) + // * "*y*" (should never have matches) + // * the first half of a randomly chosen test case, followed by * + + sizes := []int{10, 100, 1000} + length := 32 + + for _, n := range sizes { + b.Run(strconv.Itoa(n), func(b *testing.B) { + haystack := make([]string, n) + for i := range n { + if i%2 == 0 { + haystack[i] = randomString([]rune{'a', 'b', 'c', 'd'}, length) + } else { + haystack[i] = randomString([]rune{'a', 'b', 'c', 'd', 'x'}, length) + } + } + + needleIndex := rand.Intn(len(haystack)) + needle := haystack[needleIndex] + needleGlob := needle[0:length/2] + "*" + + store := inmem.NewFromObject(map[string]any{ + "haystack": haystack, + "needleGlob": needleGlob, + }) + + module := `package test + main if { + needleMatches := {h | h := data.haystack[_]; glob.match(data.needleGlob, [], h)} + xMatches := {h | h := data.haystack[_]; glob.match("*x*", [], h)} + yMtches := {h | h := data.haystack[_]; glob.match("*y*", [], h)} + }` + + query := ast.MustParseBody("data.test.main") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": module, + }) + + b.ResetTimer() + + for range b.N { + + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + + q := NewQuery(query). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn) + + _, err := q.Run(ctx) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkMemberWithKeyFromBaseDoc(b *testing.B) { + store := inmem.NewFromObject(test.GenerateLargeJSONBenchmarkData()) + mod := `package test + main if { "key99", "value99" in data.values } + ` + + ctx := context.Background() + query := ast.MustParseBody("data.test.main") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": mod, + }) + + b.ResetTimer() + + for range b.N { + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + _, err := NewQuery(query).WithCompiler(compiler).WithStore(store).WithTransaction(txn).Run(ctx) + return err + }) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkObjectGetFromBaseDoc(b *testing.B) { + store := inmem.NewFromObject(test.GenerateLargeJSONBenchmarkData()) + mod := `package test + main if { object.get(data.values, "key99", false) == "value99" } + ` + + ctx := context.Background() + query := ast.MustParseBody("data.test.main") + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": mod, + }) + + b.ResetTimer() + + for range b.N { + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + _, err := NewQuery(query).WithCompiler(compiler).WithStore(store).WithTransaction(txn).Run(ctx) + return err + }) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/third_party/opa/v1/topdown/topdown_partial_bench_test.go b/third_party/opa/v1/topdown/topdown_partial_bench_test.go new file mode 100644 index 000000000000..f4da598238c2 --- /dev/null +++ b/third_party/opa/v1/topdown/topdown_partial_bench_test.go @@ -0,0 +1,80 @@ +package topdown + +import ( + "context" + "strconv" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" +) + +func BenchmarkInliningFullScan(b *testing.B) { + + ctx := context.Background() + body := ast.MustParseBody("data.test.p = true") + unknowns := []*ast.Term{ast.MustParseTerm("input")} + compiler := ast.MustCompileModules(map[string]string{ + "test.rego": ` + package test + + p if { + data.a[i] == input + } + `, + }) + + sizes := []int{1000, 10000, 300000} + + for _, n := range sizes { + + b.Run(strconv.Itoa(n), func(b *testing.B) { + + store := inmem.NewFromObject(generateInlineFullScanBenchmarkData(n)) + + b.ResetTimer() + + for range b.N { + + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + + q := NewQuery(body). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithUnknowns(unknowns) + + queries, support, err := q.PartialRun(ctx) + if err != nil { + b.Fatal(err) + } + + if len(queries) != n { + b.Fatal("Expected", n, "queries") + } else if len(support) != 0 { + b.Fatal("Unexpected support") + } + + return nil + }) + if err != nil { + b.Fatal(err) + } + } + }) + } + +} + +func generateInlineFullScanBenchmarkData(n int) map[string]any { + + sl := make([]any, n) + for i := range sl { + sl[i] = strconv.Itoa(i) + } + + return map[string]any{ + "a": sl, + } +} diff --git a/third_party/opa/v1/topdown/topdown_partial_test.go b/third_party/opa/v1/topdown/topdown_partial_test.go new file mode 100644 index 000000000000..e4f2870a8d61 --- /dev/null +++ b/third_party/opa/v1/topdown/topdown_partial_test.go @@ -0,0 +1,5248 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "os" + "strconv" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestTopDownPartialEval(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + unknowns []string + disableInlining []string + nondeterministicBuiltins bool + shallow bool + skipPartialNamespace bool + query string + modules []string + moduleASTs []*ast.Module + data string + input string + wantQueries []string + wantQueryASTs []ast.Body + wantSupport []string + wantSupportASTs []*ast.Module + ignoreOrder bool + }{ + { + note: "empty", + query: "true = true", + wantQueries: []string{``}, + }, + { + note: "query vars", + query: "x = 1", + wantQueries: []string{`x = 1`}, + }, + { + note: "trivial", + query: "input.x = 1", + wantQueries: []string{`input.x = 1`}, + }, + { + note: "trivial reverse", + query: "1 = input.x", + wantQueries: []string{`1 = input.x`}, + }, + { + note: "trivial both", + query: "input.x = input.y", + wantQueries: []string{ + `input.x = input.y`, + }, + }, + { + note: "transitive", + query: "input.x = y; y[0] = z; z = 1; plus(z, 2, 3)", + wantQueries: []string{`input.x = y; y[0] = 1; plus(1, 2, 3); z = 1`}, + }, + { + note: "vars", + query: "x = 1; y = 2; input.x = x; y = input.y", + wantQueries: []string{ + `input.x = 1; 2 = input.y; x = 1; y = 2`, + }, + }, + { + note: "complete: substitute", + query: "input.x = data.test.p; data.test.q = input.y", + modules: []string{ + `package test + p = x if { x = "foo" } + q = x if { x = "bar" }`, + }, + wantQueries: []string{ + `"foo" = input.x; "bar" = input.y`, + }, + }, + { + note: "iterate vars", + query: "a = [1,2]; a[i] = x", + wantQueries: []string{ + `a = [1, 2]; i = 0; x = 1`, + `a = [1, 2]; i = 1; x = 2`, + }, + }, + { + note: "iterate data", + query: "data.x[i] = input.x", + data: `{"x": [1,2,3]}`, + wantQueries: []string{ + `input.x = 1; i = 0`, + `input.x = 2; i = 1`, + `input.x = 3; i = 2`, + }, + }, + { + note: "iterate data - unknown key", + query: `data.test.p = true`, + data: `{"x": {"foo": 7, "bar": 7}}`, + modules: []string{ + ` + package test + + p if { + input.x = k + data.x[k] = 7 + } + `, + }, + wantQueries: []string{`input.x = "foo"`, `input.x = "bar"`}, + }, + { + note: "iterate data - unknown key undefined", + query: `data.test.p = true`, + data: `{"x": {"foo": 8, "bar": 8}}`, + modules: []string{ + ` + package test + + p if { + input.x = k + data.x[k] = 7 + } + `, + }, + wantQueries: []string{}, + }, + { // TODO: duplicate for general refs? + note: "iterate rules: partial object", + query: `data.test.p[x] = input.x`, + modules: []string{ + `package test + p["a"] = "b" + p["b"] = "c" + p["c"] = "d"`, + }, + wantQueries: []string{ + `"b" = input.x; x = "a"`, + `"c" = input.x; x = "b"`, + `"d" = input.x; x = "c"`, + }, + }, + { // TODO: duplicate for general refs? + note: "iterate rules: partial set", + query: `input.x = x; data.test.p[x]`, + modules: []string{ + `package test + p contains 1 + p contains 2 + p contains 3`, + }, + wantQueries: []string{ + `input.x = 1; x = 1`, + `input.x = 2; x = 2`, + `input.x = 3; x = 3`, + }, + }, + { + note: "iterate keys: sets", + query: `input = x; s = {1,2}; s[x] = y`, + wantQueries: []string{ + `input = 1; s = {1, 2}; x = 1; y = 1`, + `input = 2; s = {1, 2}; x = 2; y = 2`, + }, + }, + { + note: "iterate keys: objects", + query: `input = x; o = {"a": 1, "b": 2}; o[x] = y`, + wantQueries: []string{ + `input = "a"; o = {"a": 1, "b": 2}; x = "a"; y = 1`, + `input = "b"; o = {"a": 1, "b": 2}; x = "b"; y = 2`, + }, + }, + { + note: "iterate keys: saved", + query: `x = input; y = [x]; z = y[i][j] `, + wantQueries: []string{ + `x = input; x[j] = z; y = [x]; i = 0`, + }, + }, + { // TODO: duplicate for general refs? + note: "single term: save", + query: `input.x = x; data.test.p[x]`, + modules: []string{ + `package test + p contains y if { y = "foo" } + p contains z if { z = "bar" }`, + }, + wantQueries: []string{ + `input.x = "foo"; x = "foo"`, + `input.x = "bar"; x = "bar"`, + }, + }, + { + note: "single term: false save", + query: `input = x; x = false; x`, // last expression must be preserved + wantQueries: []string{ + `input = false; false; x = false`, + }, + }, + { + note: "reference: partial object", + query: "data.test.p[x].foo = 1", + modules: []string{ + `package test + p[x] = {y: z} if { x = input.x; y = "foo"; z = 1 } + p[x] = {y: z} if { x = input.y; y = "bar"; z = 2 }`, + }, + wantQueries: []string{ + `x = input.x`, + }, + }, + { + note: "reference: partial object, general ref", + query: "data.test.p[x].q.foo = 1", + modules: []string{ + `package test + p[a].q = {b: c} if { a = input.a; b = "foo"; c = 1 } + p[a].q = {b: c} if { a = input.b; b = "bar"; c = 2 }`, + }, + wantQueries: []string{ + `x = input.a`, + }, + }, + { + note: "reference: partial object, general ref (2)", + query: "data.test.p[x].q.foo", + modules: []string{ + `package test + p[a].q = {b: c} if { a = input.a; b = "foo"; c = 1 } + p[a].q = {b: c} if { a = input.b; b = "bar"; c = 2 }`, + }, + wantQueries: []string{ + `x = input.a`, + }, + }, + { + note: "reference: partial object, general ref (3)", + query: "data.test.p[x].q", + modules: []string{ + `package test + p[a].q = {b: c} if { a = input.a; b = "foo"; c = 1 } + p[a].q = {b: c} if { a = input.b; b = "bar"; c = 2 }`, + }, + wantQueries: []string{ + `x = input.a`, + `x = input.b`, + }, + }, + { + note: "reference: partial object, general ref (4)", + query: "data.test.p[x].q[y]", + modules: []string{ + `package test + p[a].q = {b: c} if { a = input.a; b = "foo"; c = 1 } + p[a].q = {b: c} if { a = input.b; b = "bar"; c = 2 }`, + }, + wantQueries: []string{ + `x = input.a; y = "foo"`, + `x = input.b; y = "bar"`, + }, + }, + { + note: "reference: partial object, general ref (5)", + query: "data.test.p[x].q[y] = z", + modules: []string{ + `package test + p[a].q = {b: c} if { a = input.a; b = "foo"; c = 1 } + p[a].q = {b: c} if { a = input.b; b = "bar"; c = 2 }`, + }, + wantQueries: []string{ + `x = input.a; y = "foo"; z = 1`, + `x = input.b; y = "bar"; z = 2`, + }, + }, + { + note: "reference: partial object, general ref (6)", + query: "data.test.p = z", + modules: []string{ + `package test + p[a].q = {b: c} if { a = input.a; b = "foo"; c = 1 } + p[a].q = {b: c} if { a = input.b; b = "bar"; c = 2 }`, + }, + wantQueries: []string{ + `data.partial.test.p = z`, + }, + wantSupport: []string{ + `package partial.test + p[a2].q = {"foo": 1} if { a2 = input.a } + p[a1].q = {"bar": 2} if { a1 = input.b }`, + }, + }, + { + note: "reference: partial object, general ref (7)", + query: "data.test.p[x] = z", + modules: []string{ + `package test + p[a].q = {b: c} if { a = input.a; b = "foo"; c = 1 } + p[a].q = {b: c} if { a = input.b; b = "bar"; c = 2 }`, + }, + wantQueries: []string{ + `x = input.a; z = {"q": {"foo": 1}}`, + `x = input.b; z = {"q": {"bar": 2}}`, + }, + }, + { + note: "reference: partial object, general ref (8)", + query: "data.test.p = z", + modules: []string{ + `package test + p[a].q = {b: c} if { a = input.a; b = "foo"; c = 1 } + p[a].q = {b: c} if { a = input.b; b = "bar"; c = 2 } + p.foo.r = a if { a = "baz" } + p.foo.s = a if { a = input.c }`, + }, + wantQueries: []string{ + `data.partial.test.p = z`, + }, + wantSupport: []string{ + `package partial.test + p[a4].q = {"foo": 1} if { a4 = input.a } + p[a3].q = {"bar": 2} if { a3 = input.b }`, + `package partial.test.p.foo + r = "baz" if { true } + s = a2 if { a2 = input.c }`, + }, + }, + { + note: "reference: partial object, general ref (9)", + query: "data.test.p[x] = z", + modules: []string{ + `package test + p[a].q = {b: c} if { a = input.a; b = "foo"; c = 1 } + p[a].q = {b: c} if { a = input.b; b = "bar"; c = 2 } + p.foo.r = a if { a = "baz" } + p.foo.s = a if { a = input.c }`, + }, + wantQueries: []string{ + `x = input.a; z = {"q": {"foo": 1}}`, + `x = input.b; z = {"q": {"bar": 2}}`, + `x = "foo"; z = {"r": "baz"}`, + `z = {"s": input.c}; x = "foo"`, + }, + }, + { + note: "reference: partial object, general ref, multiple vars", + query: `data.test.p = x`, + modules: []string{ + `package test + p[q].r[s] := v if { v := "foo"; q := 42; s := "bar" } + p[q].r[s].t := v if { v := input.x; q := input.y; s := "baz" }`, + }, + wantQueries: []string{ + `data.partial.test.p = x`, + }, + wantSupport: []string{ + `package partial.test + p[42].r.bar = "foo" if { true } + p[__local4__2].r.baz.t = __local3__2 if { __local3__2 = input.x; __local4__2 = input.y }`, + }, + }, + { + note: "reference: partial object, general ref, multiple vars (2)", + query: `data.test.p[42] = x`, + modules: []string{ + `package test + p[q].r[s] := v if { v := "foo"; q := 42; s := "bar" } + p[q].r[s].t := v if { v := input.x; q := input.y; s := "baz" }`, + }, + wantQueries: []string{ + `x = {"r": {"bar": "foo"}}`, + `42 = input.y; x = {"r": {"baz": {"t": input.x}}}`, + }, + }, + { + note: "reference: partial object, general ref, multiple vars (2) (shallow)", + query: `data.test.p[42] = x`, + shallow: true, + modules: []string{ + `package test + #p[q].r[s] := v if { v := "foo"; q := 42; s := "bar" } + #p[q].r[s].t := v if { v := input.x; q := input.y; s := "baz" } + p[q][r][s].t := v if { v := input.x; q := input.y; s := input.z; r := "known" }`, + }, + wantQueries: []string{ + `data.partial.test.p[42] = x`, + }, + wantSupport: []string{ + `package partial.test + p[__local1__1].known[__local2__1].t = __local0__1 if { __local0__1 = input.x; __local1__1 = input.y; __local2__1 = input.z }`, + }, + }, + { + note: "reference: partial set", + query: "data.test.p[x].foo = 1", + modules: []string{ + `package test + p contains x if { x = {y: z}; y = "foo"; z = input.x } + p contains x if { x = {y: z}; y = "bar"; z = input.x }`, + }, + wantQueries: []string{ + `1 = input.x; x = {"foo": 1}`, + }, + }, + { + note: "reference: partial set, general ref", + query: "data.test.p[x][y].foo = 1", + modules: []string{ + `package test + import future.keywords.contains + p[x] contains y if { y = {a: b}; a = "foo"; b = input.x; x := 42 } + p[x] contains y if { y = {a: b}; a = "bar"; b = input.x; x := input.y }`, + }, + wantQueries: []string{ + `1 = input.x; y = {"foo": 1}; x = 42`, + }, + }, + { + note: "reference: partial set, general ref (2)", + query: "data.test.p[x][y].bar = 1", + modules: []string{ + `package test + import future.keywords.contains + p[x] contains y if { y = {a: b}; a = "foo"; b = input.x; x = 42 } + p[x] contains y if { y = {a: b}; a = "bar"; b = input.x; x = input.y }`, + }, + wantQueries: []string{ + `1 = input.x; x = input.y; y = {"bar": 1}`, + }, + }, + { + note: "reference: partial set, general ref (3)", + query: "data.test.p[42][y].foo = 1", + modules: []string{ + `package test + import future.keywords.contains + p[x] contains y if { y = {a: b}; a = "foo"; b = input.x; x := 42 } + p[x] contains y if { y = {a: b}; a = "bar"; b = input.x; x := input.y }`, + }, + wantQueries: []string{ + `1 = input.x; y = {"foo": 1}`, + }, + }, + { + note: "reference: partial set, general ref (4)", + query: `data.test.p[x][y] = {"foo": 1}`, + modules: []string{ + `package test + import future.keywords.contains + p[x] contains y if { y = {a: b}; a = "foo"; b = input.x; x := 42 } + p[x] contains y if { y = {a: b}; a = "bar"; b = input.x; x := input.y }`, + }, + wantQueries: []string{ + `1 = input.x; y = {"foo": 1}; x = 42`, + }, + }, + { + note: "reference: partial set, general ref (5)", + query: `data.test.p[x] = {{"foo": 1}}`, + modules: []string{ + `package test + import future.keywords.contains + p[x] contains y if { y = {a: b}; a = "foo"; b = input.x; x := 42 } + p[x] contains y if { y = {a: b}; a = "bar"; b = input.x; x := input.y }`, + }, + wantQueries: []string{ + `{{"foo": input.x}} = {{"foo": 1}}; x = 42`, // `1 = input.x; x = 42` would be a more precise optimization (?) + }, + }, + { + note: "reference: partial set, general ref (6)", + query: `data.test.p`, + modules: []string{ + `package test + import future.keywords.contains + p[x] contains y if { y = {a: b}; a = "foo"; b = input.x; x := 42 } + p[x] contains y if { y = {a: b}; a = "bar"; b = input.x; x := input.y }`, + }, + wantQueries: []string{ + `data.partial.test.p`, + }, + wantSupport: []string{ + `package partial.test + import future.keywords.contains + p[42] contains {"foo": b1} if { b1 = input.x } + p[__local1__2] contains {"bar": b2} if { b2 = input.x; __local1__2 = input.y }`, + }, + }, + { + note: "reference: partial set, general ref (7)", + query: `data.test.p = x`, + modules: []string{ + `package test + import future.keywords.contains + p[x] contains y if { y = {a: b}; a = "foo"; b = input.x; x := 42 } + p[x] contains y if { y = {a: b}; a = "bar"; b = input.x; x := input.y }`, + }, + wantQueries: []string{ + `data.partial.test.p = x`, + }, + wantSupport: []string{ + `package partial.test + import future.keywords.contains + p[42] contains {"foo": b1} if { b1 = input.x } + p[__local1__2] contains {"bar": b2} if { b2 = input.x; __local1__2 = input.y }`, + }, + }, + { + note: "reference: partial set, general ref (8)", + query: `data.test.p = x`, + modules: []string{ + `package test + import future.keywords.contains + p[x].r contains y if { y = {a: b}; a = "foo"; b = input.x; x := 42 } + p[x].r contains y if { y = {a: b}; a = "bar"; b = input.x; x := input.y }`, + }, + wantQueries: []string{ + `data.partial.test.p = x`, + }, + wantSupport: []string{ + `package partial.test + import future.keywords.contains + p[42].r contains {"foo": b1} if { b1 = input.x } + p[__local1__2].r contains {"bar": b2} if { b2 = input.x; __local1__2 = input.y }`, + }, + }, + { + note: "reference: partial set, general ref, multiple vars", + query: `data.test.p = x`, + modules: []string{ + `package test + import future.keywords.contains + p[q].r[s] contains x if { x = "foo"; q := 42; s = "bar" } + p[q].r[s].t contains x if { x = input.x; q := input.y; s = "baz" }`, + }, + wantQueries: []string{ + `data.partial.test.p = x`, + }, + wantSupport: []string{ + `package partial.test + import future.keywords.contains + p[42].r.bar contains "foo" if { true } + p[__local1__2].r.baz.t contains x2 if { x2 = input.x; __local1__2 = input.y }`, + }, + }, + { + note: "reference: partial set, general ref, multiple vars (2)", + query: `data.test.p[42] = x`, + modules: []string{ + `package test + import future.keywords.contains + p[q].r[s] contains v if { v := "foo"; q := 42; s := "bar" } + p[q].r[s].t contains v if { v := input.x; q := input.y; s := "baz" }`, + }, + wantQueries: []string{ + `x = {"r": {"bar": {"foo"}}}`, + `42 = input.y; x = {"r": {"baz": {"t": {input.x}}}}`, + }, + }, + { + note: "reference: partial set, general ref, multiple vars (3)", + query: `data.test.p.foo = x`, + modules: []string{ + `package test + import future.keywords.contains + p[q].r[s] contains x if { x = "foo"; q := 42; s = "bar" } + p[q].r[s].t contains x if { x = input.x; q := input.y; s = "baz" }`, + }, + wantQueries: []string{ + `"foo" = input.y; x = {"r": {"baz": {"t": {input.x}}}}`, + }, + }, + { + note: "reference: partial object, unknown in query ref", + query: "data.test.p[input.x]", + modules: []string{ + `package test + p[q].r[s] = v if { q = {"foo", "bar"}[s]; v = "baz" } + p.q.r.s := 1`, + }, + wantQueries: []string{ + `"foo" = input.x`, + `"bar" = input.x`, + `"q" = input.x`, + }, + }, + { + note: "reference: partial object, unknown in query ref (2)", + query: "data.test.p.foo.r[input.x]", + modules: []string{ + `package test + p[q].r[s] = v if { q = {"foo", "bar"}[s]; v = "baz" } + p.q.r.s := 1`, + }, + wantQueries: []string{ + `"foo" = input.x`, + }, + }, + { + note: "reference: partial object, unknown in query ref (3)", + query: "data.test.p[input.x].r[input.y]", + modules: []string{ + `package test + p[q].r[s] = v if { q = {"foo", "bar"}[s]; v = "baz" } + p.q.r.s := 1`, + }, + wantQueries: []string{ + `"foo" = input.x; "foo" = input.y`, + `"bar" = input.x; "bar" = input.y`, + `"q" = input.x; "s" = input.y`, + }, + }, + { + note: "reference: partial object, unknown in query ref (4)", + query: "data.test.p[x].r[y][input.x]", + modules: []string{ + `package test + p[q].r[s] = {v: w} if { q = {"foo", "bar"}[s]; v = "baz"; w = "bax" } + p.q.r.s := {1: 2}`, + }, + wantQueries: []string{ + `"baz" = input.x; x = "foo"; y = "foo"`, + `"baz" = input.x; x = "bar"; y = "bar"`, + `1 = input.x; x = "q"; y = "s"`, + }, + }, + { + note: "reference: partial object, unknown in query ref (5)", + query: "data.test.p[x].r[y][input.x] = input.y", + modules: []string{ + `package test + p[q].r[s] = {v: w} if { q = {"foo", "bar"}[s]; v = "baz"; w = "bax" } + p.q.r.s := {1: 2}`, + }, + wantQueries: []string{ + `"baz" = input.x; "bax" = input.y; x = "foo"; y = "foo"`, + `"baz" = input.x; "bax" = input.y; x = "bar"; y = "bar"`, + `1 = input.x; 2 = input.y; x = "q"; y = "s"`, + }, + }, + { + note: "reference: partial object, unknown in query ref (6)", + query: `data.test.p[x].r[y][input.x] = "bax"`, + modules: []string{ + `package test + p[q].r[s] = {v: w} if { q = {"foo", "bar"}[s]; v = "baz"; w = "bax" } + p.q.r.s := {1: 2}`, + }, + wantQueries: []string{ + `"baz" = input.x; x = "foo"; y = "foo"`, + `"baz" = input.x; x = "bar"; y = "bar"`, + }, + }, + { + note: "reference: partial object, unknown in query ref (7)", + query: `data.test.p[x].r[y][input.x] = 2`, + modules: []string{ + `package test + p[q].r[s] = {v: w} if { q = {"foo", "bar"}[s]; v = "baz"; w = "bax" } + p.q.r.s := {1: 2}`, + }, + wantQueries: []string{ + `1 = input.x; x = "q"; y = "s"`, + }, + }, + { + note: "reference: complete", + query: "data.test.p = 1", + modules: []string{ + `package test + + p = x if { input.x = x }`, + }, + wantQueries: []string{ + `input.x = 1`, + }, + }, + { + note: "reference: complete, ref head", + query: "data.test.p.q = 1", + modules: []string{ + `package test + + p.q = x if { input.x = x }`, + }, + wantQueries: []string{ + `input.x = 1`, + }, + }, + { + note: "reference: complete: suffix", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + a = 1 + q[a] + } + + q = a if { + a = input + }`, + }, + wantQueries: []string{`input[1]`}, + }, + { + note: "reference: complete: suffix: ensure unique var", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + a = 1 + b = 2 + q[a] = r[b] + } + + q = a if { + a = input.a + } + + r = b if { + b = input.b + }`, + }, + wantQueries: []string{`input.b[2] = input.a[1]`}, + }, + { + note: "reference: head: from query", + query: "data.test.p[y] = 1", + modules: []string{ + `package test + + p[x] = 1 if { + input.foo[x] = z + x.bar = 1 + } + `, + }, + wantQueries: []string{ + `y.bar = 1; z1 = input.foo[y]`, + }, + }, + { + note: "reference: ref head: from query", + query: "data.test.p.q[y] = 1", + modules: []string{ + `package test + + p.q[x] = 1 if { + input.foo[x] = z + x.bar = 1 + } + `, + }, + wantQueries: []string{ + `y.bar = 1; z1 = input.foo[y]`, + }, + }, + { + note: "reference: general ref head: from query", + query: "data.test.p.q[y].s = 1", + modules: []string{ + `package test + + p.q[x].s = 1 if { + input.foo[x] = z + x.bar = 1 + } + `, + }, + wantQueries: []string{ + `y.bar = 1; z1 = input.foo[y]`, + }, + }, + { + note: "reference: head: applied", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + q[x] + x.a = 1 + } + + q contains x if { + input[x] + x.b = 2 + }`, + }, + // FIXME: is this a problem? + wantQueries: []string{` + input[x_ref_01] + x_ref_01.b = 2 + x_ref_01 + x_ref_01.a = 1 + `}, + }, + { + note: "reference: default not required", + query: "data.test.p = true", + modules: []string{ + `package test + + default p = false + p if { + input.x = 1 + }`, + }, + wantQueries: []string{`input.x = 1`}, + }, + { + note: "namespace: complete", + query: "data.test.p = x", + modules: []string{ + `package test + p = 1 if { input.y = x; x = 2 }`, + }, + wantQueries: []string{ + `input.y = 2; x = 1`, + }, + }, + { + note: "namespace: complete head", + query: "data.test.p = x", + modules: []string{ + `package test + p = x if { input.x = x }`, + }, + wantQueries: []string{ + `input.x = x`, + }, + }, + { + note: "namespace: partial set", + query: "data.test.p[[x, y]]", + modules: []string{ + `package test + p contains [y, x] if { input.z = z; z = y; a = input.a; a = x }`, + }, + wantQueries: []string{ + `input.z = x; y = input.a; x_term_0_0 = [x, y]`, + }, + }, + { + note: "namespace: partial object", + query: "input.x = x; data.test.p[x] = y; y = 2", + modules: []string{ + `package test + p[y] = x if { y = "foo"; x = 2 }`, + }, + wantQueries: []string{ + `input.x = "foo"; x = "foo"; y = 2`, + }, + }, + { + note: "namespace: partial object, ref head", + query: "input.x = x; data.test.p.q[x] = y; y = 2", + modules: []string{ + `package test + p.q[y] = x if { y = "foo"; x = 2 }`, + }, + wantQueries: []string{ + `input.x = "foo"; x = "foo"; y = 2`, + }, + }, + { + note: "namespace: partial object, general ref head", + query: "input.x = x; input.y = y; data.test.p.q[x][y] = z; z = 2", + modules: []string{ + `package test + p.q[x][y] = z if { x = "foo"; y = "bar"; z = 2 }`, + }, + wantQueries: []string{ + `input.x = "foo"; input.y = "bar"; x = "foo"; y = "bar"; z = 2`, + }, + }, + { + note: "namespace: embedding", + query: "data.test.p = x", + modules: []string{ + `package test + p = x if { input.x = [y]; y = x }`, + }, + wantQueries: []string{ + `input.x = [x]`, + }, + }, + { + note: "namespace: multiple", + query: "data.test.p = x", + modules: []string{ + `package test + p = [x, z] if { input.x = y; y = x; q = z } + q = x if { input.y = y; x = y }`, + }, + wantQueries: []string{ + `x = [input.x, input.y]`, + }, + }, + { + note: "namespace: calls", + query: "data.test.p = x", + modules: []string{ + `package test + + p if { + a = "a" + b = input.b + a != b + } + `, + }, + wantQueries: []string{ + `"a" != input.b; x = true`, + }, + }, + { + note: "namespace: reference head", + query: "data.test.p = x", + modules: []string{ + `package test + + p if { + input = x + x.foo = true + }`, + }, + wantQueries: []string{ + `input.foo = true; x = true`, + }, + }, + { + note: "namespace: reference head: from caller", + query: "data.test.p[x] = 1", + modules: []string{ + `package test + + p[x] = 1 if { + x = input + x[0] = 1 + } + `, + }, + wantQueries: []string{ + `x = input; x[0] = 1`, + }, + }, + { + note: "namespace: function with call composite result (array, nested)", + query: `data.test.foo(input, [[x, _]]); startswith(x, "foo")`, + modules: []string{ + `package test + foo(x) = o if { + o := [[x.x, x.y]] + } + `}, + wantQueries: []string{ + `x = input.x; _ = input.y; startswith(x, "foo")`, + }, + }, + { + note: "namespace: function with call composite result (object)", + query: `data.test.foo(input, {"x": x}); startswith(x, "foo")`, + modules: []string{ + `package test + foo(x) = o if { + o := { "x": x.y } + } + `}, + wantQueries: []string{ + `x = input.y; startswith(x, "foo")`, + }, + }, + { + note: "namespace: function with call composite result (object, nested)", + query: `data.test.foo(input, {"x": [y, z]}); startswith(y, "foo")`, + modules: []string{ + `package test + foo(y) = z if { + z := { "x": [y.y, y.z] } + } + `}, + wantQueries: []string{ + `y = input.y; z = input.z; startswith(y, "foo")`, + }, + }, + { + note: "namespace: function with call composite result (array/object, mixed)", + query: `data.test.foo(input, {"x": [ { "a": y }, _]}); startswith(y, "foo")`, + modules: []string{ + `package test + foo(y) = o if { + o := { "x": [ {"a": y.y }, y.z] } + } + `}, + wantQueries: []string{ + `y = input.y; _ = input.z; startswith(y, "foo")`, + }, + }, + { + note: "ignore conflicts: complete", + query: "data.test.p = x", + modules: []string{ + `package test + p = true if { input.x = 1 } + p = false if { input.x = 2 }`, + }, + wantQueries: []string{ + `input.x = 1; x = true`, + `input.x = 2; x = false`, + }, + }, + { + note: "ignore conflicts: functions", + query: "data.test.f(1, x)", + modules: []string{ + `package test + f(x) = true if { input.x = x } + f(x) = false if { input.y = x }`, + }, + wantQueries: []string{ + `input.x = 1; x = true`, + `input.y = 1; x = false`, + }, + }, + { + note: "ignore conflicts: functions: unknowns", + query: "data.test.f(input) = x", + modules: []string{ + `package test + f(x) = true if { x = 1 } + f(x) = false if { x = 2 } + `, + }, + wantQueries: []string{ + `1 = input; x = true`, + `2 = input; x = false`, + }, + }, + { + note: "comprehensions: evaluated", + query: `x = [true | true]; y = {true | true}; z = {a: true | a = "foo"}`, + wantQueries: []string{`x = [true]; y = {true}; z = {"foo": true}`}, + }, + { + note: "comprehensions: saved", + query: `x = [true | input.x = 1]`, + wantQueries: []string{`x = [true | input.x = 1]`}, + }, + { + note: "comprehensions: saved (with namespacing)", + query: "data.test.p = x; data.test.p = y", + modules: []string{ + `package test + + p = c if { + a = input + c = [1 | b = a[0]] + } + `}, + wantQueries: []string{`x = [1 | b1 = input[0]]; y = [1 | b2 = input[0]]`}, + }, + { + note: "comprehensions: closure", + query: `i = 1; xs = [x | x = data.foo[i]]`, + wantQueries: []string{`i = 1; xs = ["b"]`}, + data: `{"foo": ["a", "b", "c"]}`, + }, + { + note: "comprehensions: closure saved", + query: `i = 1; xs = [x | x = input.foo[i]]`, + wantQueries: []string{`xs = [x | x = input.foo[1]; 1 = 1]; i = 1`}, + }, + { + note: "tree: no unknown dependencies", + query: "data.test = x", + modules: []string{ + `package test.a + p = 1`, + `package test + q["a"] = 2`, + `package test.b + r contains 1`, + }, + wantQueries: []string{`x = {"a": {"p": 1}, "q": {"a": 2}, "b": {"r": {1,}}}`}, + }, + { + note: "with: disabled inlining", + query: "data.test.p = true", + modules: []string{ + `package test + p if { input.x = 1; q with input as {"y": 2} } + q if { input.y = r } + r = 2`, + }, + wantQueries: []string{ + `input.x = 1; data.partial.test.q = x_term_1_11 with input as {"y": 2}; x_term_1_11 with input as {"y": 2}`, + }, + wantSupport: []string{ + `package partial.test + q = true if { 2 = input.y }`, + }, + }, + { + note: "with: no unknowns", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { q[x] = y with input as 1 } + q contains y if { x = 1; y = x } + q contains 2`, + }, + wantQueries: []string{ + `data.partial.test.q[x1] = y1 with input as 1`, + }, + wantSupport: []string{ + `package partial.test + + q contains 1 + q contains 2`, + }, + }, + { + note: "with: iteration", + query: `data.test.p = true`, + modules: []string{ + `package test + + p if { q = true with input as 1 } + q if { r[x] = input } + r contains 1 + r contains 2`, + }, + wantQueries: []string{ + `data.partial.test.q = true with input as 1`, + }, + wantSupport: []string{ + `package partial.test + + q if { 1 = input } + q if { 2 = input }`, + }, + }, + { + note: "with: unknown value", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { input.x = z; [z] = x; q with data.foo as x } + q if { data.foo = [1] }`, + }, + wantQueries: []string{"data.test.q with data.foo as [input.x]"}, + }, + { + note: "with: unknown value propagates to outputs (eq)", + query: "data.test.p = z", + modules: []string{ + `package test + + q = 1 if { input.foo = 1 } + p = y if { x = q with data.bar as input.bar; plus(x, 1, y) }`, + }, + wantQueries: []string{"x1 = data.test.q with data.bar as input.bar; plus(x1, 1, z)"}, + }, + { + note: "with: unknown value propagates to outputs (ref)", + query: "data.test.p = z", + modules: []string{ + `package test + + q contains 1 if { input.foo = 1 } + p = y if { q[x] with data.bar as input.bar; plus(x, 1, y) }`, + }, + wantQueries: []string{"data.test.q[x1] with data.bar as input.bar; plus(x1, 1, z)"}, + }, + { + note: "with: unknown value propagates to outputs (call)", + query: "data.test.p = z", + modules: []string{ + `package test + + f(t) = 1 if { input.foo = t } + p = y if { f(1, x) with data.bar as input.bar; plus(x, 1, y) }`, + }, + wantQueries: []string{"data.test.f(1, x1) with data.bar as input.bar; plus(x1, 1, z)"}, + }, + { + note: "with: unknown value propagates to outputs (built-in)", + query: "data.test.p = z", + modules: []string{ + `package test + + p = y if { time.now_ns(x) with data.bar as input.bar; plus(x, 1, y) }`, + }, + wantQueries: []string{"time.now_ns(x1) with data.bar as input.bar; plus(x1, 1, z)"}, + }, + { + note: "with: ground prefix disabled", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { q[1] = 1 with input as 1 } + q contains x if { x = 1 }`, + }, + wantQueries: []string{`data.partial.test.q[1] = 1 with input as 1`}, + wantSupport: []string{ + `package partial.test + + q contains 1`, + }, + }, + { + note: "with: ground prefix disabled with var", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { q[x] = 1 with input as 1 } + q contains x if { x = 1 }`, + }, + wantQueries: []string{`data.partial.test.q[x1] = 1 with input as 1`}, + wantSupport: []string{ + `package partial.test + + q contains 1`, + }, + }, + { + note: "with+shallow: partial set elem", + shallow: true, + query: "data.test.p = a", + modules: []string{ + `package test + + p contains x if { q[x] } + q contains 7 if { false with input as 1 }`, + }, + wantQueries: []string{`set() = a`}, + }, + { + note: "with+shallow: partial obj key", + shallow: true, + query: "data.test.p = a", + modules: []string{ + `package test + + p[x] = y if { q[x] = y } + q[7] = 8 if { false with input as 1 }`, + }, + wantQueries: []string{`{} = a`}, + }, + { + note: "with+builtin: no unknowns", + query: "data.test.p = a", + modules: []string{ + `package test + + mock_concat(_, _) = "foo/bar" + p if { q with concat as mock_concat } + q if { concat("/", ["a", "b"], "foo/bar") }`, + }, + wantQueries: []string{`a = true`}, + }, + { + note: "with+builtin: value replacement", + query: "data.test.p = a", + modules: []string{ + `package test + + p if { q with concat as "foo/bar" } + q if { concat("/", ["a", "b"], "foo/bar") }`, + }, + wantQueries: []string{`a = true`}, + }, + { + note: "with+function: no unknowns", + query: "data.test.p = a", + modules: []string{ + `package test + f(_, _) = "x" + mock_f(_, _) = "foo/bar" + p if { q with f as mock_f } + q if { f("/", ["a", "b"], "foo/bar") }`, + }, + wantQueries: []string{`a = true`}, + }, + { + note: "with+function: value replacement", + query: "data.test.p = a", + modules: []string{ + `package test + f(_, _) = "x" + p if { q with f as "foo/bar" } + q if { f("/", ["a", "b"], "foo/bar") }`, + }, + wantQueries: []string{`a = true`}, + }, + { + note: "with+builtin: unknowns in replacement function", + query: "data.test.p = a", + modules: []string{ + `package test + + mock_concat(x, _) = concat(x, input) + p if { q with concat as mock_concat} + q if { concat("/", ["a", "b"], "foo/bar") }`, + }, + wantQueries: []string{`data.partial.test.mock_concat("/", ["a", "b"], "foo/bar"); a = true`}, + wantSupport: []string{ + `package partial.test + + mock_concat(__local0__3, __local1__3) = __local2__3 if { + __local3__3 = input + concat(__local0__3, __local3__3, __local2__3) + }`, + }, + }, + { + note: "with+function: unknowns in replacement function", + query: "data.test.p = a", + modules: []string{ + `package test + f(_) = "x/y" + mock_f(_) = "foo/bar" if { input.y } + p if { q with f as mock_f} + q if { f("/", "foo/bar") }`, + }, + wantQueries: []string{`data.partial.test.mock_f("/", "foo/bar"); a = true`}, + wantSupport: []string{ + `package partial.test + + mock_f(__local1__3) = "foo/bar" if { + input.y = x_term_3_03 + x_term_3_03 + }`, + }, + }, + { + note: "with+builtin: unknowns in replaced function's args", + query: "data.test.p = a", + modules: []string{ + `package test + + mock_concat(_, _) = ["foo", "bar"] + p if { + q with array.concat as mock_concat + } + q if { + array.concat(["foo"], input, ["foo", "bar"]) + }`, + }, + wantQueries: []string{` + data.partial.test.q + a = true + `}, + wantSupport: []string{`package partial.test + + q if { + data.partial.test.mock_concat(["foo"], input, ["foo", "bar"]) + } + mock_concat(__local0__3, __local1__3) = ["foo", "bar"] + `}, + }, + { + note: "with+function: unknowns in replaced function's args", + query: "data.test.p = a", + modules: []string{ + `package test + my_concat(x, y) = concat(x, y) + mock_concat(_, _) = "foo,bar" + p if { + q with my_concat as mock_concat + } + q if { + my_concat("/", input, "foo,bar") + }`, + }, + wantQueries: []string{` + data.partial.test.q + a = true + `}, + wantSupport: []string{`package partial.test + + q if { + data.partial.test.mock_concat("/", input, "foo,bar") + } + mock_concat(__local2__3, __local3__3) = "foo,bar" + `}, + }, + { + note: "with+builtin: unknowns in replacement function's bodies", + query: "data.test.p = a", + modules: []string{ + `package test + + mock_concat(_, _) = ["foo", "bar"] if { input.foo } + mock_concat(_, _) = ["bar", "baz"] if { input.bar } + + p if { q with array.concat as mock_concat } + q if { x := array.concat(["foo"], input) }`, + }, + wantQueries: []string{` + data.partial.test.q + a = true + `}, + wantSupport: []string{`package partial.test + + q if { + __local6__2 = input + data.partial.test.mock_concat(["foo"], __local6__2, __local5__2) + __local4__2 = __local5__2 + } + mock_concat(__local0__3, __local1__3) = ["foo", "bar"] if { + input.foo = x_term_3_03 + x_term_3_03 + } + mock_concat(__local2__4, __local3__4) = ["bar", "baz"] if { + input.bar = x_term_4_04 + x_term_4_04 + }`}, + }, + { + note: "with+function: unknowns in replacement function's bodies", + query: "data.test.p = a", + modules: []string{ + `package test + my_concat(x, y) = concat(x, y) + mock_concat(_, _) = "foo,bar" if { input.foo } + mock_concat(_, _) = "bar,baz" if { input.bar } + + p if { q with my_concat as mock_concat } + q if { x := my_concat(",", input) }`, + }, + wantQueries: []string{` + data.partial.test.q + a = true + `}, + wantSupport: []string{`package partial.test + + q if { + __local9__2 = input + data.partial.test.mock_concat(",", __local9__2, __local8__2) + __local6__2 = __local8__2 + } + mock_concat(__local2__3, __local3__3) = "foo,bar" if { + input.foo = x_term_3_03 + x_term_3_03 + } + mock_concat(__local4__4, __local5__4) = "bar,baz" if { + input.bar = x_term_4_04 + x_term_4_04 + }`}, + }, + { + note: "with+builtin+negation: when replacement has no unknowns (args, defs), save negated expr without replacement", + query: "data.test.p = true", + modules: []string{` + package test + + mock_count(_) = 100 + p if { + not q with input.x as 1 with count as mock_count + } + + q if { + count([1,2,3]) = input.x + } + `}, + wantQueries: []string{"not data.partial.test.q with input.x as 1"}, + wantSupport: []string{` + package partial.test + + q if { 100 = input.x } + `}, + }, + { + note: "with+function+negation: when replacement has no unknowns (args, defs), save negated expr without replacement", + query: "data.test.p = true", + modules: []string{` + package test + my_count(x) = count(x) + mock_count(_) = 100 + p if { + not q with input.x as 1 with my_count as mock_count + } + + q if { + my_count([1,2,3]) = input.x + } + `}, + wantQueries: []string{"not data.partial.test.q with input.x as 1"}, + wantSupport: []string{` + package partial.test + + q if { 100 = input.x } + `}, + }, + { + note: "with+builtin+negation: when replacement args have unknowns, save negated expr with replacement", + query: "data.test.p = true", + modules: []string{` + package test + + mock_count(_) = 100 + p if { + not q with input.x as 1 with count as mock_count + } + + q if { + count(input.y) = input.x # unknown arg for mocked func + } + `}, + wantQueries: []string{"not data.partial.test.q with input.x as 1"}, + wantSupport: []string{` + package partial.test + + q if { data.partial.test.mock_count(input.y, __local1__3); __local1__3 = input.x } + mock_count(__local0__4) = 100 + `}, + }, + { + note: "with+function+negation: when replacement args have unknowns, save negated expr with replacement", + query: "data.test.p = true", + modules: []string{` + package test + my_count(x) = count(x) + mock_count(_) = 100 + p if { + not q with input.x as 1 with my_count as mock_count + } + + q if { + my_count(input.y) = input.x # unknown arg for mocked func + } + `}, + wantQueries: []string{`not data.partial.test.q with input.x as 1`}, + wantSupport: []string{` + package partial.test + + q if { data.partial.test.mock_count(input.y, __local3__3); __local3__3 = input.x } + mock_count(__local1__4) = 100 + `}, + }, + { + note: "with+builtin+negation: when replacement defs have unknowns, save negated expr with replacement", + query: "data.test.p = true", + modules: []string{` + package test + + mock_count(_) = 100 if { input.y } + mock_count(_) = 101 if { input.z } + p if { + not q with input.x as 1 with count as mock_count + } + + q if { + count([1]) = input.x # unknown arg for mocked func + } + `}, + wantQueries: []string{"not data.partial.test.q with input.x as 1"}, + wantSupport: []string{` + package partial.test + + q if { data.partial.test.mock_count([1], __local2__3); __local2__3 = input.x } + mock_count(__local0__4) = 100 if { input.y = x_term_4_04; x_term_4_04 } + mock_count(__local1__5) = 101 if { input.z = x_term_5_05; x_term_5_05 } + `}, + }, + { + note: "with+function+negation: when replacement defs have unknowns, save negated expr with replacement", + query: "data.test.p = true", + modules: []string{` + package test + my_count(x) = count(x) + mock_count(_) = 100 if { input.y } + mock_count(_) = 101 if { input.z } + p if { + not q with input.x as 1 with my_count as mock_count + } + + q if { + my_count([1]) = input.x # unknown arg for mocked func + } + `}, + wantQueries: []string{"not data.partial.test.q with input.x as 1"}, + wantSupport: []string{` + package partial.test + + q if { data.partial.test.mock_count([1], __local4__3); __local4__3 = input.x } + mock_count(__local1__4) = 100 if { input.y = x_term_4_04; x_term_4_04 } + mock_count(__local2__5) = 101 if { input.z = x_term_5_05; x_term_5_05 } + `}, + }, + { + note: "save: sub path", + query: "input.x = 1; input.y = 2; input.z.a = 3; input.z.b = x", + input: `{"x": 1, "z": {"b": 4}}`, + wantQueries: []string{ + `input.y = 2; input.z.a = 3; x = 4`, + }, + unknowns: []string{ + "input.y", + "input.z.a", + }, + }, + { + note: "save: virtual doc", + query: "data.test.p = 1; data.test.q = 2", + modules: []string{ + `package test + p = x if { x = 1 } + q = y if { y = input.y }`, + }, + wantQueries: []string{ + `data.test.p = 1; 2 = input.y`, + }, + unknowns: []string{ + "input", + "data.test.p", + }, + }, + { + note: "automatic shallow inlining: full extent: partial set", + query: "data.test.p = x", + modules: []string{ + `package test + p contains x if { input.x = x } + p contains x if { input.y = x }`, + }, + wantQueries: []string{`data.partial.test.p = x`}, + wantSupport: []string{` + package partial.test + p contains x1 if { input.y = x1 } + p contains x2 if { input.x = x2 } + `}, + }, + { + note: "automatic shallow inlining: full extent: partial set, general ref head", + query: "data.test.p.q = x", + modules: []string{ + `package test + import future.keywords.contains + p.q contains x if { input.x = x } + p.q[r].s contains t if { input.r = r; input.t = t }`, + }, + wantQueries: []string{`data.partial.test.p.q = x`}, + wantSupport: []string{` + package partial.test.p + import future.keywords.contains + q contains x2 if { input.x = x2 } + q[r1].s contains t1 if { input.r = r1; input.t = t1 } + `}, + }, + { + note: "automatic shallow inlining: full extent: partial object", + query: "data.test.p = x", + modules: []string{ + `package test + p[x] = y if { x = input.x; y = input.y } + p[x] = y if { x = input.z; y = input.a }`, + }, + wantQueries: []string{`data.partial.test.p = x`}, + wantSupport: []string{` + package partial.test + p[x1] = y1 if { x1 = input.z; y1 = input.a } + p[x2] = y2 if { x2 = input.x; y2 = input.y } + `}, + }, + { + note: "automatic shallow inlining: full extent: partial object, general ref head", + query: "data.test.p.q = x", + modules: []string{ + `package test + p.q[x] = y if { x = input.x; y = input.y } + p.q[r].s[t] = y if { r = input.r; t = input.t; y = input.y }`, + }, + wantQueries: []string{`data.partial.test.p.q = x`}, + wantSupport: []string{` + package partial.test.p + q[x2] = y2 if { x2 = input.x; y2 = input.y } + q[r1].s[t1] = y1 if { r1 = input.r; t1 = input.t; y1 = input.y } + `}, + }, + { + note: "automatic shallow inlining: full extent: no solutions", + query: "data.test.p = x", + modules: []string{ + `package test + + p contains 1 if { input = 1; false }`, + }, + wantQueries: []string{`x = set()`}, + }, + { + note: "automatic shallow inlining: full extent: iteration", + query: "data.test[x] = y", + modules: []string{ + `package test + + s contains x if { x = input.x } + s2[x].u contains y if { x = input.x; y = input.y } + p[x] = y if { x = input.x; y = input.y } + p2[x].r[y] = z if { x = input.x; y = input.y; z = input.z } + r = x if { x = input.x }`, + }, + wantQueries: []string{ + `data.partial.test.s = y; x = "s"`, + `data.partial.test.s2 = y; x = "s2"`, + `data.partial.test.p = y; x = "p"`, + `data.partial.test.p2 = y; x = "p2"`, + `y = input.x; x = "r"`, + }, + wantSupport: []string{` + package partial.test + p[x1] = y1 if { x1 = input.x; y1 = input.y } + p2[x2].r[y2] = z2 if { x2 = input.x; y2 = input.y; z2 = input.z } + s contains x4 if { x4 = input.x } + s2[x5].u contains y5 if { x5 = input.x; y5 = input.y } + `}, + }, + { + note: "save: set embedded", + query: `data.test.p = true`, + modules: []string{` + package test + p if { x = input; {x} = {1} }`}, + wantQueries: []string{`{input} = {1}`}, + }, + { + note: "save: call embedded", + query: "x = input; a = [x]; count([a], n)", + wantQueries: []string{ + `x = input; count([[x]], n); a = [x]`, + }, + }, + { + note: "save: function with call composite result (array)", + query: `split(input, "@", [x]); startswith(x, "foo")`, + wantQueries: []string{ + `split(input, "@", [x]); startswith(x, "foo")`, + }, + }, + { + note: "save: function: ordered", + query: `input = x; data.test.f(x)`, + modules: []string{` + package test + f(x) = true if { x = 1 } + else = false if { x = 2 }`}, + wantQueries: []string{ + `input = x; data.test.f(x)`, + }, + }, + { + note: "save: with but no unknowns", + query: "data.test.p = {1,2}", + modules: []string{ + `package test + p contains 1 + p contains 2 if { 1 with data.foo as 1 }`, + }, + wantQueries: []string{`data.partial.test.p = {1,2}`}, // can't evaluate full extent of `p` because it depends on with statements that will be saved. + wantSupport: []string{` + package partial.test + + p contains 1 if { true } + p contains 2 if { true } # note: the expression containing 'with' gets partially evaluated because it does not depend on unknowns + `}, + }, + { + note: "else: no unknown dependencies", + query: "data.test.p = x", + modules: []string{ + `package test + p = x if { q = x } + q = 100 if { false } else = 200 if { true }`, + }, + wantQueries: []string{ + `x = 200`, + }, + }, + { + note: "else: saved", + query: "data.test.p = x", + modules: []string{ + `package test + p = x if { q = x } + q = 100 if { input.x = 1 } else = 200 if { true }`, + }, + wantQueries: []string{ + `data.test.q = x`, + }, + }, + { + note: "else: func args unknown transitive", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { input = z; [z] = x; f(x, true) } + f(x) if { x > 1 } else = false if { x < 0 }`, + }, + wantQueries: []string{ + `data.test.f([input], true)`, + }, + }, + { + note: "save: ignore ast", + query: "time.now_ns(x)", + wantQueries: []string{ + `time.now_ns(x)`, + }, + }, + { + note: "save: ignore ast transitive", + query: "data.test.p = true", + modules: []string{ + `package test + p if { q = x } + q contains 1 if { time.now_ns() == 1579276766010057000 }`, // full extent, must save caller because time.now_ns() should not be partially evaluated + }, + wantQueries: []string{"x1 = data.partial.test.q"}, + wantSupport: []string{` + package partial.test + q contains 1 if { time.now_ns(1579276766010057000) } + `}, + }, + { + note: "support: default trivial", + query: "data.test.p = x", + modules: []string{ + `package test + default p = false + p if { q } + q if { input.x = 1 } + `, + }, + wantQueries: []string{ + `data.partial.test.p = x`, + }, + wantSupport: []string{ + `package partial.test + p = true if { input.x = 1 } + default p = false`, + }, + }, + { + note: "support: default with iteration (disjunction)", + query: "data.test.p = x", + modules: []string{ + `package test + default p = false + p if { q } + q if { input.x = 1 } + q if { input.x = 2 } + `, + }, + wantQueries: []string{ + `data.partial.test.p = x`, + }, + wantSupport: []string{ + `package partial.test + p = true if { input.x = 1 } + p = true if { input.x = 2 } + default p = false + `, + }, + }, + { + note: "support: default with iteration (data)", + query: "data.test.p = x", + modules: []string{ + `package test + default p = false + p if { q } + q if { input.x = a[i] } + a = [1, 2] + `, + }, + wantQueries: []string{ + `data.partial.test.p = x`, + }, + wantSupport: []string{ + `package partial.test + p = true if { 1 = input.x } + p = true if { 2 = input.x } + default p = false`, + }, + }, + { + note: "support: default with disjunction", + query: "data.test.p = x", + modules: []string{ + `package test + default p = 0 + p = 1 if { q } + p = 2 if { r } + q if { input.x = 1 } + r if { input.x = 2 } + `, + }, + wantQueries: []string{ + `data.partial.test.p = x`, + }, + wantSupport: []string{ + `package partial.test + p = 1 if { input.x = 1 } + p = 2 if { input.x = 2 } + default p = 0 + `, + }, + }, + { + note: "support: default head vars", + query: "data.test.p = x", + modules: []string{ + `package test + default p = 0 + p = x if { x = 1; input.x = 1 } + p = x if { input.x = x } + `, + }, + wantQueries: []string{ + `data.partial.test.p = x`, + }, + wantSupport: []string{ + `package partial.test + p = 1 if { input.x = 1 } + p = x1 if { input.x = x1 } + default p = 0 + `, + }, + }, + { + note: "support: default multiple", + query: "data.test.p = x", + modules: []string{ + `package test + default p = false + p if { q = true; s } # using q = true syntax to avoid dealing with implicit != false expr + default q = true # same value as expr above so default must be kept + q if { r } + r if { input.x = 1 } + r if { input.y = 2 } + s if { input.z = 3 }`, + }, + wantQueries: []string{ + `data.partial.test.p = x`, + }, + wantSupport: []string{ + `package partial.test + q = true if { input.x = 1 } + q = true if { input.y = 2 } + default q = true + p = true if { data.partial.test.q = true; input.z = 3 } + default p = false + `, + }, + }, + { + note: "support: default bindings", + query: "data.test.p = x", + modules: []string{ + `package test + default p = false + p if { q[x]; not r[x] } + q contains 1 if { input.x = 1 } + q contains 2 if { input.y = 2 } + r contains 1 if { input.z = 3 }`, + }, + wantQueries: []string{ + `data.partial.test.p = x`, + }, + wantSupport: []string{ + `package partial.test + + p = true if { input.x = 1; not input.z = 3 } + p = true if { input.y = 2 } + default p = false + `, + }, + }, + { + note: "support: iterate default", + query: "data.test[x] = y", + modules: []string{ + `package test + default p = 0 + p = 1 if { q } + q if { input.x = 1 } + `, + }, + wantQueries: []string{ + `data.partial.test.p = y; x = "p"`, + `input.x = 1; x = "q"; y = true`, + }, + wantSupport: []string{ + `package partial.test + p = 1 if { input.x = 1 } + default p = 0`, + }, + }, + { + note: "support: default memoized", + query: "data.test.q[x] = y; data.test.p = z", + modules: []string{ + `package test + + q = [1,2] + + default p = false + p if { input.x = 1 }`, + }, + wantQueries: []string{ + `data.partial.test.p = z; x = 0; y = 1`, + `data.partial.test.p = z; x = 1; y = 2`, + }, + wantSupport: []string{ + `package partial.test + + p = true if { input.x = 1 } + default p = false`, + }, + }, + { + note: "copy propagation: basic", + query: "input.x > 1", + wantQueries: []string{ + "input.x > 1", + }, + }, + { + note: "copy propagation: call terms", + query: "input.x+1 > 1", + wantQueries: []string{ + "input.x+1 > 1", + }, + }, + { + note: "copy propagation: virtual", + query: "data.test.p > 1", + modules: []string{ + `package test + + p = x if { input.x = y; y = z; z = x }`, + }, + wantQueries: []string{ + `input.x > 1`, + }, + }, + { + note: "copy propagation: virtual: call", + query: "data.test.p > 1", + modules: []string{ + `package test + + p = y if { input.x = x; plus(x, 1, y) }`, + }, + wantQueries: []string{ + `input.x+1 > 1`, + }, + }, + { + note: "copy propagation: composite", + query: "data.test.p[0][0] = 1", + modules: []string{ + `package test + + p = x if { x = [input.x] } + `, + }, + wantQueries: []string{ + `input.x[0] = 1`, + }, + }, + { + note: "copy propagation: reference head", + query: "data.test.p[0] > 1", + modules: []string{ + `package test + + p = x if { input.x = x }`, + }, + wantQueries: []string{ + `input.x[0] > 1`, + }, + }, + { + note: "copy propagation: reference head: call", + query: "data.test.p[0] > 1", + modules: []string{ + `package test + + p = x if { sort(input.x, y); y = x }`, + }, + wantQueries: []string{ + // copy propagation cannot remove the intermediate variable currently because + // sort(input.x, y) is not killed (since y is ultimately used as a ref head.) + `sort(input.x, x_ref_0); x_ref_0[0] > 1`, + }, + }, + { + note: "copy propagation: var vs dot vs set", + query: `data.test.p = true`, + modules: []string{ + `package test + + p if { + input.x[i] = a; a.foo = 1 # same semantics as next line + input.y[j].bar = 2; + input.z[k]; k.baz = 3 # different semantics from previous two lines + }`, + }, + wantQueries: []string{` + input.x[i1].foo = 1; + input.y[j1].bar = 2; + input.z[k1]; k1.baz = 3`, + }, + }, + { + note: "copy propagation: reference head: call transitive with union-find", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + split(input, ":", x) + y = x + y[0] = "a" + }`, + }, + wantQueries: []string{ + `split(input, ":", x1); x1[0] = "a"`, + }, + }, + { + note: "copy propagation: live built-in output", + query: "plus(input, 1, x); x = y", + wantQueries: []string{ + `plus(input, 1, x); y = x`, + }, + }, + { + note: "copy propagation: declared var built-in output", + query: "some x; plus(input, 1, x); x = y", + wantQueries: []string{ + `plus(input, 1, y)`, + }, + }, + { + note: "copy propagation: no dependencies", + query: "data.test.p", + modules: []string{ + `package test + + p if { + input.x = ["foo", a] + input.y = a + }`, + }, + wantQueries: []string{ + `input.x = ["foo", a1]; a1 = input.y`, + }, + }, + { + note: "copy propagation: union-find replace head", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + input = y + x = y + x.foo = 1 + }`, + }, + wantQueries: []string{`input.foo = 1`}, + }, + { + note: "copy propagation: union-find skip ref head", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + input = y + x = y + x.foo = 1 + x = {"foo": 1} + }`, + }, + wantQueries: []string{`input.foo = 1; input = {"foo": 1}`}, + }, + { + note: "copy propagation: remove equal(A,A) nop", + query: "data.test.p == 100", + modules: []string{ + `package test + + p = x if { + input = x + x = 100 + }`, + }, + wantQueries: []string{ + "input = 100", + }, + }, + { + note: "copy propagation: apply to support rules", + query: `data.test.p = true`, + modules: []string{` + package test + + p if { + not q + } + + q if { + input.x = x + x = y + y = 1 + } + `}, + wantQueries: []string{`not input.x = 1`}, + }, + { + note: "copy propagation: apply to support rules: head vars are live", + query: `data.test.p = true`, + modules: []string{` + package test + + p if { + input.x = z; not q[z] + } + + q contains y if { + x = 1 + x = a + a = y + } + `}, + wantQueries: []string{`not input.x = 1`}, + }, + { + note: "copy propagation: negation safety", + query: `data.test.p = true`, + modules: []string{ + `package test + + p if { + input.x[i] = x + not f(x) + } + + f(x) if { + input.y = x + }`, + }, + wantQueries: []string{ + "not input.y = x1; x1 = input.x[i1]", + }, + }, + { + note: "copy propagation: negation safety needs extra expr", + query: `data.test.p = true`, + modules: []string{ + `package test + + p if { + x = data.y[c] + x.z = 1 + not x.z = 2 + } + `, + }, + unknowns: []string{`data.y`}, + wantQueries: []string{ + `data.y[c1].z = 1; not x1.z = 2; x1 = data.y[c1]`, + }, + }, + { + note: "copy propagation: negation safety needs extra expr - no live var overlap", + query: `data.test.p = true`, + modules: []string{ + `package test + + p if { + x = input.y[c] + x.z = 1 + not x.z = 2 + } + `, + }, + unknowns: []string{`input.y`}, + wantQueries: []string{ + `input.y[c1].z = 1; not x1.z = 2; x1 = input.y[c1]`, + }, + }, + { + note: "copy propagation: negation safety no extra expr", + query: `data.test.p = true`, + modules: []string{ + `package test + + p if { + x = data.y[c] + not x.z = 2 + } + `, + }, + unknowns: []string{`data.y`}, + wantQueries: []string{ + `not x1.z = 2; x1 = data.y[c1]`, + }, + }, + { + note: "copy propagation: rewrite object key (bug 1177)", + query: `data.test.p = true`, + modules: []string{ + ` + package test + + p if { + x = input.x + y = input.y + x = {y: 1} + } + `, + }, + wantQueries: []string{`input.x = {input.y: 1}`}, + }, + { + note: "copy propagation: single term test intact", + query: "data.test.p = true", + modules: []string{` + package test + + p if { + input = x + y = x == 1 + y + } + + `}, + wantQueryASTs: []ast.Body{ + ast.NewBody( + ast.NewExpr( + ast.CallTerm( + ast.NewTerm(ast.Equal.Ref()), + ast.NewTerm(ast.InputRootRef), + ast.IntNumberTerm(1), + ), + ), + ), + }, + }, + { + note: "copy propagation: circular reference (bug 3559)", + query: "data.test.p", + modules: []string{`package test + p if { + q[_] + } + q contains x if { + x = input[x] + }`, + }, + wantQueries: []string{`x_term_1_01; x_term_1_01 = input[x_term_1_01]`}, + }, + { + note: "copy propagation: circular reference (bug 3071)", + query: "data.test.p", + modules: []string{`package test + p contains y if { + s := { i | input[i] } + s & set() != s + y := sprintf("%v", [s]) + }`, + }, + wantQueries: []string{`data.partial.test.p`}, + wantSupport: []string{`package partial.test + p contains __local1__1 if { __local0__1 = {i1 | input[i1]}; neq(and(__local0__1, set()), __local0__1); sprintf("%v", [__local0__1], __local1__1) } + `}, + }, + { + note: "copy propagation: tautology in query, input ref", + query: "input.a == input.a", + wantQueries: []string{`__localq1__ = input.a`}, + }, + { + note: "copy propagation: tautology in query, var ref, var is input", + query: "x := input; x.a == x.a", + wantQueries: []string{`__localq2__ = input.a`}, + }, + { + note: "copy propagation: tautology, input ref", + query: "data.test.p", + modules: []string{`package test + p if { + input.a == input.a + }`, + }, + wantQueries: []string{`__localcp0__ = input.a`}, + }, + { + note: "copy propagation: tautology, var ref, ref is input", + query: "data.test.p", + modules: []string{`package test + p if { + x := input + x.a == x.a + }`, + }, + wantQueries: []string{`__localcp0__ = input.a`}, + }, + { + note: "copy propagation: tautology, var ref, ref is unknown data", + query: "data.test.p", + unknowns: []string{"data.bar.foo"}, + modules: []string{`package test + p if { + data.bar.foo.a == data.bar.foo.a + }`, + }, + wantQueries: []string{`__localcp0__ = data.bar.foo.a`}, + }, + { + note: "copy propagation: tautology, var ref, ref is input, via unknown", + // NOTE(sr): If we were having unkowns: [input.foo] and the rule body was + // input.a == input.a, we'd never reach copy-propagation -- partial eval would + // have failed before. + query: "data.test.p", + unknowns: []string{"input"}, + modules: []string{`package test + p if { + input.foo.a == input.foo.a + }`, + }, + wantQueries: []string{`__localcp0__ = input.foo.a`}, + }, + { + note: "copy propagation: tautology, var ref, ref is head var", + query: "data.test.p(input)", + modules: []string{`package test + p(x) if { + x.a == x.a + }`, + }, + wantQueries: []string{`__localcp1__ = input.a`}, + }, + { + note: "save set vars are namespaced", + query: "input = x; data.test.f(1)", + modules: []string{ + `package test + + f(x) if { x >= x }`, + }, + wantQueries: []string{ + `input = x`, + }, + }, + { + note: "negation: inline compound", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { not q } + q if { ((input.x + 7) / input.y) > 100 }`, + }, + wantQueries: []string{ + `not data.partial.__not1_0_2__`, + }, + wantSupport: []string{ + `package partial + + __not1_0_2__ if { + ((input.x + 7) / input.y) > 100 + }`, + }, + }, + { + note: "negation: inline conjunction", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { not q } + q if { a = input.x + 7; b = a / input.y; b > 100 }`, + }, + wantQueries: []string{ + `not data.partial.__not1_0_2__`, + }, + wantSupport: []string{ + `package partial + + __not1_0_2__ if { + ((input.x + 7) / input.y) > 100 + }`, + }, + }, + { + note: "negation: inline safety", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + input.x = 1; # no op + not q; # support + not r; # fail + not s; # inline (simple) + input.z = [z]; z1 = z; t(z1) # inline transitive + } + + q if { + input.a[i] = 1 + } + + r if { false } + + s if { input.y = 2 } + + t(z2) if { + z2 = z3 + z3[0] = 1 + } + `, + }, + wantQueries: []string{ + `input.x = 1; not data.partial.__not1_1_2__; not input.y = 2; input.z = [z38]; z38[0] = 1`, + }, + wantSupport: []string{ + `package partial + + __not1_1_2__ if { + input.a[i3] = 1 + }`, + }, + }, + { + note: "negation: support safety without args", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + q + not r + } + + q if { + input.x[i] = a + startswith(a, "foo") + } + + r if { + input.y[i] = 1 + }`, + }, + wantQueries: []string{`startswith(input.x[i2], "foo"); not data.partial.__not1_1_3__`}, + wantSupport: []string{ + `package partial + + __not1_1_3__ if { input.y[i4] = 1 }`, + }, + }, + { + note: "negation: support safety with args", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + input.x = x; not f(x) + } + + f(x) if { + input.y[i] = a + sort(x, z) + z[a] = 1 + }`, + }, + wantQueries: []string{`not data.partial.__not1_1_2__(input.x)`}, + wantSupport: []string{` + package partial + + __not1_1_2__(x1) if { + sort(x1, z3) + z3[input.y[i3]] = 1 + } + `}, + }, + { + note: "negation: inline safety with live var", + query: "input = x; not data.test.f(x)", + modules: []string{ + `package test + + f(x) if { + count(x) != 3 + }`, + }, + wantQueries: []string{ + `input = x; not data.partial.__not0_1_1__(x)`, + }, + wantSupport: []string{ + `package partial + + __not0_1_1__(x) if { + count(x) != 3 + }`, + }, + }, + { + note: "negation: inline namespacing", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + input = x; not f(x) + } + + f(x) if { + count(x) > 3 + }`, + }, + wantQueries: []string{ + `not data.partial.__not1_1_2__(input)`, + }, + wantSupport: []string{ + `package partial + + __not1_1_2__(x1) if { count(x1) > 3 }`, + }, + }, + { + note: "negation: inline namespacing embedded", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + y = input.y + z = y + x = [z, 1] + not f(x) + } + + f(x) if { + sum(x) > 3 + }`, + }, + wantQueries: []string{ + `not data.partial.__not1_3_2__(input.y)`, + }, + wantSupport: []string{ + `package partial + + __not1_3_2__(z1) if { + sum([z1, 1]) > 3 + }`, + }, + }, + { + note: "negation: inline disjunction", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { not q } + q if { input.x = 1 } + q if { input.x = 2 } + `, + }, + wantQueries: []string{ + `not input.x = 1; not input.x = 2`, + }, + ignoreOrder: true, + }, + { + note: "negation: inline disjunction with args", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { input.x = x; not q(x) } + q(x) if { x = 1 } + q(x) if { x = 2 }`, + }, + wantQueries: []string{ + `not input.x = 1; not input.x = 2`, + }, + ignoreOrder: true, + }, + { + note: "negation: inline double negation (for all or universal quantifier pattern)", + query: `data.test.p = true`, + modules: []string{` + package test + + p if { + x = input[i] + not f(x) + } + + f(x) if { + q[y] + not g(y, x) + } + + g(1, x) if { + x.a = "foo" + } + + g(2, x) if { + x.b < 7 + } + + q = { + 1, 2 + } + `}, + wantQueries: []string{ + `input[i1].a = "foo"; data.partial.__not3_1_8__(input[i1])`, + }, + wantSupport: []string{ + `package partial + + __not3_1_8__(__local0__3) if { __local0__3.b < 7 }`, + }, + }, + { + note: "negation: inline cross product", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + not q + } + + q if { + x = r[_] + not f(x) + } + + f({"key": "a", "values": values}) if { + input.x = values[_] + } + + f({"key": "b", "values": values}) if { + input.y = values[_] + } + + f({"key": "c", "values": values}) if { + input.z = values[_] + } + + r = [ + {"key": "a", "values": [1,2]}, + {"key": "b", "values": [3,4,5]}, + {"key": "c", "values": [6]}, + ]`, + }, + wantQueries: []string{ + `1 = input.x; 3 = input.y; 6 = input.z`, + `1 = input.x; 4 = input.y; 6 = input.z`, + `1 = input.x; 5 = input.y; 6 = input.z`, + `2 = input.x; 3 = input.y; 6 = input.z`, + `2 = input.x; 4 = input.y; 6 = input.z`, + `2 = input.x; 5 = input.y; 6 = input.z`, + }, + }, + { + note: "negation: inline cross product with live vars", + query: "input.x = x; input.y = y; not data.test.p[[x,y]]", + modules: []string{ + `package test + + p contains [0, 1] + p contains [2, 3]`, + }, + wantQueries: []string{ + `input.x = x; input.y = y; not x = 0; not x = 2`, + `input.x = x; input.y = y; not x = 0; not y = 3`, + `input.x = x; input.y = y; not y = 1; not x = 2`, + `input.x = x; input.y = y; not y = 1; not y = 3`, + }, + }, + { + note: "negation: cross product limit", + query: "data.test.p = true", + modules: []string{ + `package test + p if { + not q + } + q if { + # size of cross product is 27 which exceeds default limit + a = {1,2,3} + a[x] + input.x = x + input.y = x + input.z = 0 + } + `, + }, + wantQueries: []string{`not data.partial.__not1_0_2__`}, + wantSupport: []string{ + `package partial + + __not1_0_2__ if { input.x = 1; input.y = 1; input.z = 0 } + __not1_0_2__ if { input.x = 2; input.y = 2; input.z = 0 } + __not1_0_2__ if { input.x = 3; input.y = 3; input.z = 0 } + `, + }, + }, + { + note: "negation: inlining namespaced variables", + query: "data.test.p[x]", + modules: []string{ + `package test + + p contains y if { + y = input + not y = 1 + } + `, + }, + wantQueries: []string{ + `x = input; not x = 1; x`, + }, + }, + { + note: "negation: inlining transitive unknown", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { input = z; [z] = x; not q[x] } + + q contains [1] + q contains [2]`, + }, + wantQueries: []string{ + `not input = 1; not input = 2`, + }, + }, + { + note: "function inlining: output checked", + query: "data.test.p = true", + modules: []string{` + package test + f(x) = y if { + y = x == 1 + } + p if { + f(input) + } + `}, + wantQueryASTs: []ast.Body{ + ast.NewBody( + ast.NewExpr( + ast.CallTerm( + ast.NewTerm(ast.Equal.Ref()), + ast.NewTerm(ast.InputRootRef), + ast.IntNumberTerm(1), + ), + ), + ), + }, + }, + { + note: "disable inlining: complete doc", + query: "data.test.p = true", + modules: []string{` + package test + p if { q; r } + q if { s[input] } + q if { t[input] } + r if { s[input] } + s contains 1 + s contains 2 + t contains 3 + `}, + wantQueries: []string{ + "data.partial.test.q; 1 = input", + "data.partial.test.q; 2 = input", + }, + wantSupport: []string{ + `package partial.test + + q if { 1 = input } + q if { 2 = input } + q if { 3 = input } `, + }, + disableInlining: []string{`data.test.q`}, + }, + { + note: "disable inlining: complete doc with suffix", + query: "data.test.p = true", + modules: []string{` + package test + p if { s; q[x] } + q = ["a", "b"] if { r[_] = input } + r = [1, 2] + s if { r[_] = input } + `}, + wantQueries: []string{ + "1 = input; data.partial.test.q[x1]", + "2 = input; data.partial.test.q[x1]", + }, + wantSupport: []string{ + `package partial.test + + q = ["a", "b"] if { 1 = input } + q = ["a", "b"] if { 2 = input }`, + }, + disableInlining: []string{`data.test.q`}, + }, + { + note: "disable inlining: function", + query: "data.test.p = true", + modules: []string{` + package test + + p if { q[x]; f(x) } + q = {"a", "b"} + f(x) if { input = x } + `}, + wantQueries: []string{ + `data.partial.test.f("a")`, + `data.partial.test.f("b")`, + }, + wantSupport: []string{ + `package partial.test + + f(__local0__3) if { input = __local0__3 }`, + }, + disableInlining: []string{"data.test.f"}, + }, + { + note: "disable inlining: partial doc", + query: "data.test.p = true", + modules: []string{` + package test + p if { q[x]; r[x] } + q contains x if { s[x] = input } + r contains x if { s[x] = input } + s contains 1 + s contains 2 + `}, + wantQueries: []string{ + "data.partial.test.q[1]; 1 = input", + "data.partial.test.q[2]; 2 = input", + }, + wantSupport: []string{ + `package partial.test + + q contains 1 if { 1 = input } + q contains 2 if { 2 = input }`, + }, + disableInlining: []string{`data.test.q`}, + }, + { + note: "disable inlining: partial doc with suffix", + query: "data.test.p = true", + modules: []string{` + package test + p if { y = 0; q[x][y]; r } + q[x] = [1, 2] if { s[x] = input } + r if { input = 1 } + r if { input = 2 } + s["a"] = 3 + s["b"] = 4 + `}, + wantQueries: []string{ + "data.partial.test.q[x1][0]; input = 1", + "data.partial.test.q[x1][0]; input = 2", + }, + wantSupport: []string{ + `package partial.test.q + + a = [1, 2] if { 3 = input } + b = [1, 2] if { 4 = input }`, + }, + disableInlining: []string{`data.test.q`}, + }, + { + note: "disable inlining: partial rule namespaced variables (negation)", + query: "data.test.p[x]", + disableInlining: []string{"data.test.p"}, + modules: []string{ + `package test + + p contains y if { + y = input + not y = 1 + } + `, + }, + wantQueries: []string{ + `data.partial.test.p[x]`, + }, + wantSupport: []string{ + `package partial.test + + p contains y1 if { y1 = input; not y1 = 1 }`, + }, + }, + { + note: "disable inlining: complete rule namespaced variables (negation)", + query: "data.test.p = x", + disableInlining: []string{"data.test.p"}, + modules: []string{ + `package test + + p = y if { + y = input + not y = 1 + } + `, + }, + wantQueries: []string{ + `data.partial.test.p = x`, + }, + wantSupport: []string{ + `package partial.test + + p = y1 if { y1 = input; not y1 = 1 }`, + }, + }, + { + note: "disable inlining: disable on prefix", + query: "data.test.foo.p = true", + modules: []string{ + `package test.foo + + p if { + data.test.bar.q[input.x] + }`, + + `package test.bar + + q contains x if { data.test.baz.r[x] }`, + + `package test.baz + + r contains 1 + r contains 2`, + }, + disableInlining: []string{"data.test.bar"}, + wantQueries: []string{`data.partial.test.bar.q[input.x]`}, + wantSupport: []string{ + `package partial.test.bar + + q contains 1 + q contains 2`, + }, + }, + { + note: "disable inlining: base document enumeration", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { k = "foo"; m = "bar"; data.base[k][x][m] = 1 }`, + }, + disableInlining: []string{"data.base"}, + wantQueries: []string{"data.base.foo[x1].bar = 1"}, + }, + { + note: "disable inlining: base document extent", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { k = "bar"; data.base.foo[k].baz = 1 }`, + }, + disableInlining: []string{"data.base"}, + wantQueries: []string{"data.base.foo.bar.baz = 1"}, + }, + { + note: "disable inlining: negation treats as unknown", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { not q } + + q if { r } + + r = false`, + }, + disableInlining: []string{"data.test.r"}, + wantQueries: []string{"not data.partial.test.r"}, + wantSupport: []string{ + `package partial.test + + r = false`, + }, + }, + { + note: "disable inlining: comprehension treats as unknown", + query: "data.test.p = [1]", + modules: []string{ + `package test + + p = x if { x = [1 | q] } + + q if { r } + + r = true`, + }, + disableInlining: []string{"data.test.r"}, + wantQueries: []string{"[1] = [1 | data.test.q]"}, + }, + { + note: "disable inlining: partial rule full extent treats as unknown", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { q = {1,2,3} } + + q contains 1 + q contains 2 + q contains 3 if { r } + + r = true`, + }, + disableInlining: []string{"data.test.r"}, + wantQueries: []string{"data.partial.test.q = {1, 2, 3}"}, + wantSupport: []string{` + package partial.test + + q contains 1 + q contains 2 + q contains 3 if { data.partial.test.r } + r = true + `}, + }, + { + note: "disable inlining: ref prefix", + query: "data.test.p = true", + modules: []string{ + `package test + + p if { + q[input.x] + } + + q = {a | data.base[a]}`, + }, + disableInlining: []string{"data.base.foo.bar"}, + wantQueries: []string{`x_ref_01 = {a2 | data.base[a2]}; x_ref_01[input.x]`}, + }, + { + note: "shallow inlining: complete rules", + query: "data.test.p = true", + modules: []string{ + ` + package test + + p if { + q = 1 + } + + q = x if { + r # 'r' should be inlined completely + y = input.x + x = y + } + + r if { s } + + s = true + `, + }, + shallow: true, + wantQueries: []string{"data.partial.test.p = true"}, + wantSupport: []string{ + `package partial.test + + q = x2 if { y2 = input.x; x2 = y2 } + p if { data.partial.test.q = 1 } + `, + }, + }, + { + note: "shallow inlining: iteration and negation", + query: "data.test.p = true", + modules: []string{ + ` + package test + + p if { + r[x] + not input[x] + } + + r contains 1 + r contains 2 + `, + }, + shallow: true, + wantQueries: []string{"data.partial.test.p = true"}, + wantSupport: []string{ + ` + package partial.test + + p if { not data.partial.__not1_1_4__ } + p if { not data.partial.__not1_1_5__ } + `, + ` + package partial + + __not1_1_4__ if { input[1] = x_term_4_01; x_term_4_01 } + __not1_1_5__ if { input[2] = x_term_5_01; x_term_5_01 } + `, + }, + }, + { + note: "shallow inlining: function not inlined if no unknowns in rule bodies, but in args", + query: "data.test.p = true", + modules: []string{` + package test + + f(x) = y if { + y = x == 1 + } + f(x) = y if { + y = x == 2 + } + p if { + f(input) + } + `}, + shallow: true, + wantQueries: []string{"data.partial.test.p = true"}, + wantSupport: []string{ + `package partial.test + + p = true if { __local4__1 = input; data.partial.test.f(__local4__1) } + f(__local0__2) = y2 if { equal(__local0__2, 1, __local2__2); y2 = __local2__2 } + f(__local1__3) = y3 if { equal(__local1__3, 2, __local3__3); y3 = __local3__3 }`, + }, + }, + { + note: "shallow inlining: function with unknowns in rule body", + query: "data.test.f(1, x)", + shallow: true, + modules: []string{ + `package test + f(x) = true if { input.x = x } + f(x) = false if { input.y = x }`, + }, + wantQueries: []string{`data.partial.test.f(1, x)`}, + wantSupport: []string{ + `package partial.test + f(__local0__2) = true if { input.x = __local0__2 } + f(__local1__1) = false if { input.y = __local1__1 }`, + }, + }, + { + note: "shallow inlining: functions with no unknowns in rule body or output, always true", + query: "data.test.f(1, y)", + shallow: true, + modules: []string{ + `package test + f(x) = true if { x >= 1 } + f(x) = false if { x < 0 } + f(x) = "meow" if { false }`, + }, + wantQueries: []string{`y = true`}, + }, + { + note: "shallow inlining: functions with multiple args, no unknowns", + query: "data.test.f(1, [1,2,3], y)", + shallow: true, + modules: []string{ + `package test + f(x, y) = true if { x > 1 } + f(x, y) = false if { + x <= 0 + count(y) == 3 + }`, + }, + wantQueries: []string{}, + }, + { + note: "shallow inlining: functions that are always undefined", + query: "data.test.f(1, y)", + shallow: true, + modules: []string{ + `package test + f(x) = "uhm" if { input.x = "x"; false } + f(x) = "like" if { input.y = "y"; false } + f(x) = "whatever" if { false }`, + }, + wantQueries: []string{}, + }, + { + note: "shallow inlining: functions with non-var arguments", + query: "data.test.f(1, y)", + shallow: true, + modules: []string{ + `package test + f(true) = true + f(x) = false if { x != true }`, + }, + wantQueries: []string{`y = false`}, + }, + { + note: "shallow inlining: functions with unknown call-site arguments", + query: "input = x; data.test.f([1, x])", + shallow: true, + modules: []string{ + `package test + f([x, y]) if { + z = 7 + x > (y+z) + }`, + }, + wantQueries: []string{`input = x; data.partial.test.f([1, x])`}, + wantSupport: []string{ + `package partial.test + f([__local0__1, __local1__1]) = true if { + plus(__local1__1, 7, __local2__1) + gt(__local0__1, __local2__1) + }`, + }, + }, + { + note: "shallow inlining: function unknowns transitive", + query: "data.test.p = true", + shallow: true, + modules: []string{ + ` + package test + + p if { + f(1) + } + + f(x) if { + g(x) + } + + g(x) if { + x = input + } + `, + }, + wantQueries: []string{`data.partial.test.p = true`}, + wantSupport: []string{ + ` + package partial.test + + p if { data.partial.test.f(1) } + f(__local0__2) if { data.partial.test.g(__local0__2) } + g(__local1__3) if { __local1__3 = input } + `, + }, + }, + { + note: "shallow inlining: function unknowns transitive - mixed", + query: "data.test.p = true", + shallow: true, + modules: []string{ + ` + package test + + p if { + f(1) # unknown dependency so must be saved + h(8) # known so can be evaluated + } + + f(x) if { + g(x) + } + + g(x) if { + x = input + } + + h(x) if { + x > 7 + } + `, + }, + wantQueries: []string{`data.partial.test.p = true`}, + wantSupport: []string{ + ` + package partial.test + + p if { data.partial.test.f(1) } + f(__local0__2) if { data.partial.test.g(__local0__2) } + g(__local1__3) if { __local1__3 = input } + `, + }, + }, + { + note: "shallow inlining: functions with unknowns in body, result passed to builtin", + query: "data.test.p", + shallow: true, + modules: []string{ + `package test + p if { + y = f(1) + count(y) + } + + f(x) = [] if { + # NOTE(sr): if we use '_' here, we cannot ever have a match + # when comparing the actual and expected support modules. + _x = input # anything dependent on an unknown will do + }`, + }, + wantQueries: []string{`data.partial.test.p = x_term_0_0; x_term_0_0`}, + wantSupport: []string{ + `package partial.test + p if { + data.partial.test.f(1, __local1__1) + y1 = __local1__1 + count(y1) + } + f(__local0__2) = [] if { _x2 = input } + `, + }, + }, + { + note: "comprehensions: ref heads (with namespacing)", + query: "data.test.p = true; input.x = x", + modules: []string{ // include an unknown in the comprehension to force saving + `package test + + p if { + x = [0]; y = {true | x[0]; input.y = 1} + } + `}, + wantQueries: []string{`y1 = {true | x1[0]; input.y = 1; x1 = [0]}; input.x = x`}, + }, + { + note: "comprehensions: vars in scope, unused in comprehension", + query: `data.test.p`, + modules: []string{ + `package test + + p contains x if { q[x] } + q contains x if { + y = { 1 | input } + x = true + } + `}, + wantQueries: []string{`data.partial.test.p`}, + wantSupport: []string{`package partial.test + p contains true if { y2 = {1 | input} } + `}, + }, + { + note: "comprehensions: vars in scope, used in lhs body (set)", + query: `data.test.p`, + modules: []string{ + `package test + + p contains x if { q[x] } + q contains x if { + { 1 | input; x } = y + x = true + } + `}, + wantQueries: []string{`data.partial.test.p`}, + wantSupport: []string{`package partial.test + p contains true if { {1 | input; x2; x2 = true} = y2 } + `}, + }, + { + note: "comprehensions: vars in scope, used in lhs term (set)", + query: `data.test.p`, + modules: []string{ + `package test + + p contains x if { q[x] } + q contains x if { + { x | input } = y + x = true + } + `}, + wantQueries: []string{`data.partial.test.p`}, + wantSupport: []string{`package partial.test + p contains true if { {x2 | input; x2 = true} = y2 } + `}, + }, + { + // NOTE(sr): To actually have the vars in the rhs, we'll need to provide two + // comprehensions -- otherwise, the arguments would be flipped and we'd have + // the vars in lhs again. + note: "comprehensions: vars in scope, used in rhs body (set)", + query: `data.test.p`, + modules: []string{ + `package test + + p contains x if { q[x] } + q contains x if { + { false | input } = { true | input; x } + x = true + } + `}, + wantQueries: []string{`data.partial.test.p`}, + wantSupport: []string{`package partial.test + p contains true if { {false | input} = {true | input; x2; x2 = true} } + `}, + }, + { + note: "comprehensions: vars in scope, used in rhs term (set)", + query: `data.test.p`, + modules: []string{ + `package test + + p contains x if { q[x] } + q contains x if { + { false | input } = { x | input } + x = true + } + `}, + wantQueries: []string{`data.partial.test.p`}, + wantSupport: []string{`package partial.test + p contains true if { {false | input} = {x2 | input; x2 = true} } + `}, + }, + { + note: "comprehensions: vars in scope, used in rhs value (object)", + query: `data.test.p`, + modules: []string{ + `package test + + p contains x if { q[x] } + q contains x if { + { "foo": false | input } = { "foo": x | input } + x = true + } + `}, + wantQueries: []string{`data.partial.test.p`}, + wantSupport: []string{`package partial.test + p contains true if { {"foo": false | input} = {"foo": x2 | input; x2 = true} } + `}, + }, + { + note: "comprehensions: ref heads (with live vars)", + query: "x = [0]; y = {true | x[0]; input.y = 1}", // include an unknown in the comprehension to force saving + wantQueries: []string{`y = {true | x[0]; input.y = 1; x = [0]}; x = [0]`}, + }, + { + note: "negation: save inline negated with", + query: `not input with data.x as 2; data.x = 1`, + data: `{"x": 1}`, + wantQueries: []string{"not input with data.x as 2"}, + }, + { + note: "negation: save negated expr using plugged with value", + query: "data.test.p = true", + modules: []string{` + package test + + p if { + x = 1 + not q with input.x as x + } + + q if { + r[input.x] + } + + r contains 1 + r contains 2 + `}, + disableInlining: []string{"data.test.q"}, + wantQueries: []string{"not data.partial.test.q with input.x as 1"}, + wantSupport: []string{` + package partial.test + + q if { 1 = input.x } + q if { 2 = input.x } + `}, + }, + { + note: "negation: save inline negated with (undefined)", + query: `not input with data.x as 1; data.x = 1`, + wantQueries: []string{}, + }, + { + note: "multiple removed eqs", + query: "data.test.p", + modules: []string{` + package test + + p = x if { + a = input.foo1 + b = input.foo2 + c = input.foo3 + d = input.foo4 + e = input.foo5 + x = true + }`, + }, + wantQueries: []string{` + e1 = input.foo5 + d1 = input.foo4 + c1 = input.foo3 + b1 = input.foo2 + a1 = input.foo1`}, + }, + { + note: "partial object rules not memoized", + query: "data.test.p", + modules: []string{` + package test + + p if { q.foo } + p if { q.foo } + + q[x] = 1 if { input[x] }`, + }, + wantQueries: []string{`input.foo`, `input.foo`}, + }, + { + note: "partial set rules not memoized", + query: "data.test.p", + modules: []string{` + package test + + p if { q.foo } + p if { q.foo } + + q contains x if { input[x] }`, + }, + wantQueries: []string{`input.foo`, `input.foo`}, + }, + { + note: "package path copied when skip partial namespace enabled (bug 3302)", + query: "data.test.p = x", + modules: []string{` + package test + pkg = "foo" if { input.x = "foo" } + pkg = "bar" if { input.x = "bar" } + p = x if { k = pkg; x = data.other[k].p } + `, ` + package other.foo + p = 1 if { input = a } + `, ` + package other.bar + p = 2 if { input = a } + `}, + wantQueries: []string{"data.test.p = x"}, + wantSupport: []string{ + ` + package other.foo + + p = 1 if { input = a5 } + `, + ` + package other.bar + + p = 2 if { input = a4 } + `, + ` + package test + + pkg = "foo" if { input.x = "foo" } + + pkg = "bar" if { input.x = "bar" } + + p = x1 if { data.test.pkg = k1; "bar" = k1; data.other[k1].p = x1 } + p = x1 if { data.test.pkg = k1; "foo" = k1; data.other[k1].p = x1 } + `, + }, + shallow: true, + skipPartialNamespace: true, + }, + { + note: "every: empty domain, no unknowns", + query: "data.test.p", + modules: []string{`package test + p if { + every x in [] { x } + }`}, + wantQueries: []string{``}, + }, + { + note: "every: no unknowns", + query: "data.test.p", + modules: []string{`package test + p if { + every x in [1, 2, 3] { x != 4 } + }`}, + wantQueries: []string{``}, + }, + { + note: "every: empty domain, unknowns in body", + query: "data.test.p", + modules: []string{`package test + p if { + every x in [] { x > input } + }`}, + wantQueries: []string{`every __local0__1, __local1__1 in [] { + __local3__1 = input + __local1__1 > __local3__1 + }`}, + }, + { + note: "every: known domain, unknowns in body", + query: "data.test.p", + modules: []string{`package test + p if { + every x in [1, 2, 3] { x > input } + }`}, + wantQueries: []string{`every __local0__1, __local1__1 in [1, 2, 3] { + __local3__1 = input + __local1__1 > __local3__1 + }`}, + }, + { + note: "every: known domain, unknowns in body (with call+assignment)", + query: "data.test.p", + modules: []string{`package test + p if { + every x in [1, 2, 3] { y := x+10; y > input } + }`}, + wantQueries: []string{`every __local0__1, __local1__1 in [1, 2, 3] { + plus(__local1__1, 10, __local4__1) + __local2__1 = __local4__1 + __local5__1 = input + __local2__1 > __local5__1 + }`}, + }, + { + note: "every: known domain, unknowns in body, body impossible", + query: "data.test.p", + modules: []string{`package test + p if { + every x in [1, 2, 3] { false; x > input } + }`}, + wantQueries: []string{`every __local0__1, __local1__1 in [1, 2, 3] { + false + __local3__1 = input + __local1__1 > __local3__1 + }`}, + }, + { + note: "every: unknown domain", + query: "data.test.p", + modules: []string{`package test + p if { + every x in input { x > 1 } + }`}, + wantQueries: []string{`every __local0__1, __local1__1 in input { __local1__1 > 1 }`}, + }, + { + note: "every: in-scope var in body", + query: "data.test.p", + modules: []string{`package test + p if { + y := 3 + every x in [1, 2] { x != 0; input > y } + }`}, + wantQueries: []string{`every __local1__1, __local2__1 in [1, 2] { __local2__1 != 0; __local4__1 = input; __local4__1 > 3 }`}, + }, + { + note: "every: unknown domain, call in body", + query: "data.test.p", + modules: []string{`package test + p if { + every x in input { + y = concat(",", [x]) + } + }`}, + wantQueries: []string{`every __local0__1, __local1__1 in input { concat(",", [__local1__1], __local3__1); y1 = __local3__1 }`}, + }, + { + note: "every: closing over function args", + query: "data.test.p", + modules: []string{`package test + p if { + f(input) + } + f(x) if { + every y in [1] { + a = x + 1 == y + } + }`}, + wantQueries: []string{`every __local1__2, __local2__2 in [1] { a2 = input; 1 = __local2__2 }`}, + }, + { + note: "every: nested and closing over function args", + query: "data.test.p", + modules: []string{`package test + p if { + f(input) + } + f(x) if { + every y in [1] { + every z in [2] { + a = x + z > y + } + } + }`}, + wantQueries: []string{`every __local1__2, __local2__2 in [1] { + __local6__2 = [2] + every __local3__2, __local4__2 in __local6__2 { + a2 = input; __local4__2 > __local2__2 } + }`}, + }, + { // https://github.com/open-policy-agent/opa/issues/5367 + note: "copypropagation: keep equations that are only found in comprehensions, inlined function call", + query: "data.test.p", + modules: []string{`package test + key_exists(obj, k) if { x = obj[k] } + + p if { + key_exists(input, "foo") + { true | input.foo } + }`}, + wantQueries: []string{`{true | input.foo} = x_term_1_21; x_term_1_21; x2 = input.foo`}, + }, + { // condensed form of the test above + note: "copypropagation: keep equations that are only found in comprehensions", + query: "data.test.p", + modules: []string{`package test + p if { + x = input.foo + { true | input.foo } + }`}, + wantQueries: []string{`{true | input.foo} = x_term_1_11; x_term_1_11; x1 = input.foo`}, + }, + { + note: "copypropagation: keep equations that are only found in 'every' body", + query: "data.test.p", + modules: []string{`package test + p if { + x = input.foo + every y in input.ys { y = input.foo } + }`}, + wantQueries: []string{`every __local0__1, __local1__1 in input.ys { __local1__1 = input.foo }; x1 = input.foo`}, + }, + { // https://github.com/open-policy-agent/opa/issues/6027 + note: "ref heads: \"double\" unification, single-value rule", + query: "data.test.foo[input.a][input.b]", + modules: []string{`package test + foo.bar contains baz if { + baz := "baz" + }`}, + wantQueries: []string{`"bar" = input.a; "baz" = input.b`}, + }, + { + note: "general ref heads: \"triple\" unification, single-value rule", + query: "data.test.foo[input.a][input.b][input.c]", + modules: []string{`package test + foo.bar[baz] contains bax if { + baz := "baz" + bax := "bax" + }`}, + wantQueries: []string{`"bar" = input.a; "baz" = input.b; "bax" = input.c`}, + }, + { // https://github.com/open-policy-agent/opa/issues/6027 + note: "ref heads: \"double\" unification, multi-value rule", + query: "data.test.foo[input.a][input.b]", + modules: []string{`package test + import future.keywords.contains + foo.bar contains baz if { + baz := "baz" + }`}, + wantQueries: []string{`"bar" = input.a; "baz" = input.b`}, + }, + { + note: "general ref heads: \"triple\" unification, multi-value rule", + query: "data.test.foo[input.a][input.b][input.c]", + modules: []string{`package test + import future.keywords.contains + foo.bar[baz] contains bax if { + baz := "baz" + bax := "bax" + }`}, + wantQueries: []string{`"bar" = input.a; "baz" = input.b; "bax" = input.c`}, + }, + { + note: "ref heads: unknown rule value", + query: "data.test.p.q[x]", + shallow: false, + modules: []string{`package test + p.q[x] := y if { + x := "foo" + y := input.y + }`}, + wantQueries: []string{`input.y; x = "foo"`}, + }, + { + note: "ref heads: unknown ref var, unknown rule value", + query: "data.test.p.q[x]", + modules: []string{`package test + p.q[x] := y if { + x := input.x + y := input.y + }`}, + wantQueries: []string{`x = input.x; input.y`}, + }, + { + note: "ref heads: unknown rule value, shallow inlining", + query: "data.test.p.q.r[x]", + shallow: true, + modules: []string{`package test + p.q.r.s := y if { + y := input.y + }`}, + wantQueries: []string{`data.partial.test.p.q.r.s = x_term_0_0; x_term_0_0; x = "s"`}, + wantSupport: []string{`package partial.test.p.q.r + s = __local0__1 if { + __local0__1 = input.y + }`}, + }, + { + note: "ref heads: unknown rule value, part-way query, shallow inlining", + query: "y = data.test.p.q[x]", + shallow: true, + modules: []string{`package test + p.q.r.s := y if { + y := input.y + }`}, + wantQueries: []string{`data.test.p.q.r = y; x = "r"`}, + }, + { // https://github.com/open-policy-agent/opa/issues/6094 + note: "ref heads: ref var, unknown rule value, shallow inlining", + query: "data.test.p.q[x]", + shallow: true, + modules: []string{`package test + p.q[x] := y if { + x := "foo" + y := input.y + }`}, + wantQueries: []string{`data.partial.test.p.q[x] = x_term_0_0; x_term_0_0`}, + wantSupport: []string{`package partial.test.p.q + foo = __local1__1 if { + __local1__1 = input.y + }`}, + }, + { // https://github.com/open-policy-agent/opa/issues/6094 + note: "ref heads: unknown ref var, unknown rule value, shallow inlining", + query: "data.test.p.q[x]", + shallow: true, + modules: []string{`package test + p.q[x] := y if { + x := input.x + y := input.y + }`}, + wantQueries: []string{`data.partial.test.p.q[x] = x_term_0_0; x_term_0_0`}, + wantSupport: []string{`package partial.test.p + q[__local0__1] = __local1__1 if { + __local0__1 = input.x + __local1__1 = input.y + }`}, + }, + { // https://github.com/open-policy-agent/opa/issues/6094 + note: "ref heads: unknown ref var, unknown rule value, shallow inlining", + query: "data.test.p.q.r.s[x]", + shallow: true, + modules: []string{`package test + p.q.r.s[x] := y if { + x := input.x + y := input.y + }`}, + wantQueries: []string{`data.partial.test.p.q.r.s[x] = x_term_0_0; x_term_0_0`}, + wantSupport: []string{`package partial.test.p.q.r + s[__local0__1] = __local1__1 if { + __local0__1 = input.x + __local1__1 = input.y + }`}, + }, + { // https://github.com/open-policy-agent/opa/issues/6094 + note: "ref heads, partial set: unknown key, shallow inlining", + query: "data.test.p.q[x]", + shallow: true, + modules: []string{`package test + import future.keywords.contains + p.q contains y if { + y := input.y + }`}, + wantQueries: []string{`data.partial.test.p.q[x] = x_term_0_0; x_term_0_0`}, + wantSupport: []string{`package partial.test.p + q contains __local0__1 if { + __local0__1 = input.y + }`}, + }, + { + note: "ref heads: special characters in ref var", + query: `data.test.p.q[input.z]`, + modules: []string{`package test + p.q["foo/bar"][x] if { + x := "baz" + input.x == input.y + }`}, + wantQueries: []string{`"foo/bar" = input.z; data.partial.test.p.q["foo/bar"]`}, + wantSupport: []string{`package partial.test.p + q["foo/bar"].baz = true if { + input.x = input.y + }`}, + }, + { + note: "ref heads: special characters in ref var (multiple)", + query: `data.test.p.q[input.a][input.b]`, + modules: []string{`package test + p.q["do/re"]["mi/fa"][x] if { + x := "baz" + input.x == input.y + }`}, + wantQueries: []string{`"do/re" = input.a; "mi/fa" = input.b; data.partial.test.p.q["do/re"]["mi/fa"]`}, + wantSupport: []string{`package partial.test.p + q["do/re"]["mi/fa"].baz = true if { + input.x = input.y + }`}, + }, + { + note: "nondeterministic builtin evaluated in PE", + query: "data.test.p", + unknowns: []string{"input.x"}, + input: `{"a": 2}`, + nondeterministicBuiltins: true, + modules: []string{fmt.Sprintf(`package test + p if input.x == http.send({"method": "POST", "url": "%s", "body": input.a}).body.p`, testserver.URL), + }, + wantQueries: []string{`"x" = input.x`}, + }, + { + note: "nondeterministic builtin time.now_ns() properly initiated", + query: "data.test.p", + nondeterministicBuiltins: true, + modules: []string{`package test + p if time.now_ns() > 0`, + }, + wantQueries: []string{""}, // unconditional true + }, + + { + note: "default function, result not collected (non-false default value)", + query: "data.test.p = true", + modules: []string{`package test + default f(x) := true # return true if x.size is undefined + f(x) if { + x.size < 100 + } + p if { + f(input.x) + } + `}, + wantQueries: []string{"data.partial.test.f(input.x)"}, + wantSupport: []string{ + `package partial.test + + default f(__local0__3) = true + f(__local1__2) = true if { __local2__2 = __local1__2.size; lt(__local2__2, 100) }`, + }, + }, + { + note: "default function, result not collected (false default value)", + query: "data.test.p = true", + modules: []string{`package test + default f(x) := false + f(x) if { + x.size < 100 + } + p if { + f(input.x) + } + `}, + wantQueries: []string{"lt(input.x.size, 100)"}, + }, + { + note: "default function, result comparison (same as default)", + query: "data.test.p = true", + modules: []string{`package test + default f(x) := true # return true if x.size is undefined + f(x) if { + x.size < 100 + } + p if { + f(input.x) == true + } + `}, + wantQueries: []string{"data.partial.test.f(input.x, true)"}, + wantSupport: []string{ + `package partial.test + + default f(__local0__3) = true + f(__local1__2) = true if { __local3__2 = __local1__2.size; lt(__local3__2, 100) }`, + }, + }, + { + note: "default function, result comparison (not same as default)", + query: "data.test.p = true", + modules: []string{`package test + default f(x) := true # return true if x.size is undefined + f(x) := y if { + y := x.size < 100 + } + p if { + f(input.x) == false + } + `}, + wantQueries: []string{"data.partial.test.f(input.x, false)"}, + wantSupport: []string{ + `package partial.test + + default f(__local0__3) = true + f(__local1__2) = __local2__2 if { __local5__2 = __local1__2.size; lt(__local5__2, 100, __local3__2); __local2__2 = __local3__2 }`, + }, + }, + { + note: "default function, saved result", + query: "data.test.p = x", + modules: []string{`package test + default f(x) := true # return true if x.size is undefined + f(x) if { + x.size < 100 + } + p := x if { + x := f(input.x) + } + `}, + wantQueries: []string{"data.partial.test.f(input.x, x)"}, + wantSupport: []string{ + `package partial.test + + default f(__local0__3) = true + f(__local1__2) = true if { __local4__2 = __local1__2.size; lt(__local4__2, 100) }`, + }, + }, + { + // This test case is redundant, but serves as a counter example to the test above. + // Inlining can happen as there is no default function to consider + note: "default function (no default)", + query: "data.test.p = true", + modules: []string{`package test + f(x) if { + x.size < 100 + } + p if { + f(input) + } + `}, + wantQueries: []string{"lt(input.size, 100)"}, + }, + { + note: "default function, shallow inlining", + query: "data.test.p = true", + modules: []string{`package test + default f(x) := true # return true if x.size is undefined + f(x) if { + x.size < 100 + } + p if { + f(input) + } + `}, + shallow: true, + wantQueries: []string{"data.partial.test.p = true"}, + wantSupport: []string{ + `package partial.test + + p = true if { __local3__1 = input; data.partial.test.f(__local3__1) } + default f(__local0__3) = true + f(__local1__2) = true if { __local2__2 = __local1__2.size; lt(__local2__2, 100) }`, + }, + }, + + // Default rule values should be inlined if only definition + { + note: "default rule inlining, sole default rule (boolean false), result value presence", + query: "data.test.q", + modules: []string{`package test + default q := false + `}, + wantQueries: []string{}, // unconditionally false + }, + { + note: "default rule inlining, sole default rule (boolean false), result value unification (true)", + query: "data.test.q = true", + modules: []string{`package test + default q := false + `}, + wantQueries: []string{}, // unconditionally false + }, + { + note: "default rule inlining, sole default rule (boolean false), result value comparison (true)", + query: "data.test.q == true", + modules: []string{`package test + default q := false + `}, + wantQueries: []string{}, // unconditionally false + }, + { + note: "default rule inlining, sole default rule (boolean false), result value unification (false)", + query: "data.test.q = false", + modules: []string{`package test + default q := false + `}, + wantQueries: []string{""}, // unconditionally true + }, + { + note: "default rule inlining, sole default rule (boolean false), result value comparison (false)", + query: "data.test.q == false", + modules: []string{`package test + default q := false + `}, + wantQueries: []string{""}, // unconditionally true + }, + + { + note: "default rule inlining, sole default rule (boolean true), result value presence", + query: "data.test.q", + modules: []string{`package test + default q := true + `}, + wantQueries: []string{""}, // unconditionally true + }, + { + note: "default rule inlining, sole default rule (boolean true), result value comparison (true)", + query: "data.test.q == true", + modules: []string{`package test + default q := true + `}, + wantQueries: []string{""}, // unconditionally true + }, + { + note: "default rule inlining, sole default rule (boolean true), result value comparison (false)", + query: "data.test.q == false", + modules: []string{`package test + default q := true + `}, + wantQueries: []string{}, // unconditionally false + }, + + { + note: "default rule inlining, sole default rule (int), result value presence", + query: "data.test.q", + modules: []string{`package test + default q := 42 + `}, + wantQueries: []string{""}, // unconditionally true + }, + { + note: "default rule inlining, sole default rule (int), result value comparison (same value)", + query: "data.test.q == 42", + modules: []string{`package test + default q := 42 + `}, + wantQueries: []string{""}, // unconditionally true + }, + { + note: "default rule inlining, sole default rule (int), result value comparison (different value)", + query: "data.test.q == 40", + modules: []string{`package test + default q := 42 + `}, + wantQueries: []string{}, // unconditionally false + }, + + // Default rule values should be inlined if all other rules fail + { + note: "default rule inlining, default rule (boolean false), result value presence", + query: "data.test.q", + modules: []string{`package test + default q := false + + q if { false } + `}, + wantQueries: []string{}, // unconditionally false + }, + { + note: "default rule inlining, default rule (boolean true), result value presence", + query: "data.test.q", + modules: []string{`package test + default q := true + + q := false if { false } + `}, + wantQueries: []string{""}, // unconditionally true + }, + { + note: "default rule inlining, default rule (boolean false), result value unification (true)", + query: "data.test.q = true", + modules: []string{`package test + default q := false + + q if { false } + `}, + wantQueries: []string{}, // unconditionally false + }, + { + note: "default rule inlining, default rule (boolean false), result value unification (false)", + query: "data.test.q = false", + modules: []string{`package test + default q := false + + q if { false } + `}, + wantQueries: []string{""}, // unconditionally true + }, + { + note: "default rule inlining, default rule (boolean false), result value comparison (true)", + query: "data.test.q == true", + modules: []string{`package test + default q := false + + q if { false } + `}, + wantQueries: []string{}, // unconditionally false + }, + { + note: "default rule inlining, default rule (boolean false), result value comparison (false)", + query: "data.test.q == false", + modules: []string{`package test + default q := false + + q if { false } + `}, + wantQueries: []string{""}, // unconditionally true + }, + + { + note: "default rule inlining, default rule (int), result value presence", + query: "data.test.q", + modules: []string{`package test + default q := 42 + + q := 1 if { false } + `}, + wantQueries: []string{""}, // unconditionally true (42 != false) + }, + { + note: "default rule inlining, default rule (int), result value unification (different value)", + query: "data.test.q = 1", + modules: []string{`package test + default q := 42 + + q := 1 if { false } + `}, + wantQueries: []string{}, // unconditionally false + }, + { + note: "default rule inlining, default rule (int), result value unification (same value)", + query: "data.test.q = 42", + modules: []string{`package test + default q := 42 + + q := 1 if { false } + `}, + wantQueries: []string{""}, // unconditionally true + }, + { + note: "default rule inlining, default rule (int), result value comparison (different value)", + query: "data.test.q == 1", + modules: []string{`package test + default q := 42 + + q := 1 if { false } + `}, + wantQueries: []string{}, // unconditionally false + }, + { + note: "default rule inlining, default rule (int), result value comparison (same value)", + query: "data.test.q == 42", + modules: []string{`package test + default q := 42 + + q := 1 if { false } + `}, + wantQueries: []string{""}, // unconditionally true + }, + + { + note: "default rule inlining, default rule (boolean false), unknowns in undefined rule, result value presence", + query: "data.test.q", + modules: []string{`package test + default q := false + + q if { + input.x == 7 + false + } + `}, + wantQueries: []string{}, // unconditionally false + }, + { + note: "default rule inlining, default rule (boolean false), unknowns in undefined rule, result value unification (true)", + query: "data.test.q = true", + modules: []string{`package test + default q := false + + q if { + input.x == 7 + false + } + `}, + wantQueries: []string{}, // unconditionally false + }, + { + note: "default rule inlining, default rule (boolean false), unknowns in undefined rule, result value comparison (true)", + // Compiled query: + // __localq0__ = data.test.q; equal(__localq0__, true) + query: "data.test.q == true", + modules: []string{`package test + default q := false + + q if { + input.x == 7 + false + } + `}, + // at eval-time of data.test.q, we don't know that __localq0__ is later compared with 'true' and support generation is superfluous. + // Can possibly be optimized into a no-support state by updating the compiler for this condition. + // E.g. for scalar values, replace equality (==) with unification (=): 'data.test.q == true' -> 'data.test.q = true' + wantQueries: []string{"data.partial.test.q == true"}, + wantSupport: []string{`package partial.test + default q = false + `}, + }, + { + note: "default rule inlining, default rule (boolean false), unknowns in rule undefined, result value comparison (false)", + // Compiled query: + // __localq0__ = data.test.q; equal(__localq0__, false) + query: "data.test.q == false", + modules: []string{`package test + default q := false + + q if { + input.x == 7 + false + } + `}, + wantQueries: []string{"data.partial.test.q == false"}, + wantSupport: []string{`package partial.test + default q = false + `}, + }, + + { + note: "default rule inlining, default rule (boolean false), unknowns in rule, result value presence", + query: "data.test.q", + modules: []string{`package test + default q := false + + q if { + input.x == 7 + } + `}, + wantQueries: []string{"input.x = 7"}, + }, + { + note: "default rule inlining, default rule (boolean false), unknowns in rule, result value unification (true)", + query: "data.test.q = true", + modules: []string{`package test + default q := false + + q if { + input.x == 7 + } + `}, + wantQueries: []string{"input.x = 7"}, + }, + + { + note: "default rule inlining, default rule (boolean true), unknowns in rule, result value presence", + query: "data.test.q", + modules: []string{`package test + default q := true + + q := false if { + input.x == 7 + } + `}, + // Default rule is not inconsequential, support module must be generated + wantQueries: []string{"data.partial.test.q"}, + wantSupport: []string{`package partial.test + default q = true + + q = false if { + input.x = 7 + } + `}, + }, + { + note: "default rule inlining, default rule (boolean true), unknowns in rule, result value unification (true)", + query: "data.test.q = true", + modules: []string{`package test + default q := true + + q := false if { + input.x == 7 + } + `}, + // Default rule is not inconsequential, support module must be generated + wantQueries: []string{"data.partial.test.q = true"}, + wantSupport: []string{`package partial.test + default q = true + + q = false if { + input.x = 7 + } + `}, + }, + { + note: "default rule inlining, default rule (boolean true), unknowns in rule, result value unification (false)", + query: "data.test.q = false", + modules: []string{`package test + default q := true + + q := false if { + input.x == 7 + } + `}, + // Default rule is inconsequential + wantQueries: []string{"input.x = 7"}, + }, + { + note: "default rule inlining, default rule (boolean false), unknowns in rule, result value comparison (true)", + // Compiled query: + // __localq0__ = data.test.q; equal(__localq0__, true) + query: "data.test.q == true", + modules: []string{`package test + default q := false + + q if { + input.x == 7 + } + `}, + wantQueries: []string{"data.partial.test.q == true"}, + wantSupport: []string{`package partial.test + default q = false + + q = true if { + input.x = 7 + } + `}, + }, + + // indirect calls + + { + note: "default rule inlining (boolean false), indirect, unknowns in rule, result value presence", + query: "data.test.p", + modules: []string{`package test + p if { + q + } + + default q := false + + q := true if { input.x = 7 } + `}, + wantQueries: []string{"input.x = 7"}, + }, + { + note: "default rule inlining (boolean false), indirect, unknowns in rule, result value unification (true)", + query: "data.test.p", + modules: []string{`package test + p if { + q = true + } + + default q := false + + q := true if { input.x = 7 } + `}, + wantQueries: []string{"input.x = 7"}, + }, + { + note: "default rule inlining (boolean false), indirect, unknowns in rule, result value unification (false)", + query: "data.test.p", + modules: []string{`package test + p if { + q = false + } + + default q := false + + q := true if { input.x = 7 } + `}, + wantQueries: []string{"data.partial.test.q = false"}, + wantSupport: []string{`package partial.test + default q = false + q = true if { input.x = 7 } + `}, + }, + { + note: "default rule inlining (boolean false), indirect, unknowns in rule, result value comparison (true)", + query: "data.test.p", + modules: []string{`package test + p if { + q == true + } + + default q := false + + q := true if { input.x = 7 } + `}, + wantQueries: []string{"input.x = 7"}, + }, + { + note: "default rule inlining (boolean false), indirect, unknowns in rule, result value comparison (false)", + query: "data.test.p", + modules: []string{`package test + p if { + q == false + } + + default q := false + + q := true if { input.x = 7 } + `}, + wantQueries: []string{"data.partial.test.q = false"}, + wantSupport: []string{`package partial.test + default q = false + q = true if { input.x = 7 } + `}, + }, + + { + note: "default rule inlining (boolean true), indirect, unknowns in rule, result value presence", + query: "data.test.p", + modules: []string{`package test + p if { + q + } + + default q := true + + q := false if { input.x = 7 } + `}, + // Default rule is not inconsequential, support module must be generated + wantQueries: []string{"data.partial.test.q"}, + wantSupport: []string{`package partial.test + q = false if { input.x = 7 } + default q = true + `}, + }, + { + note: "default rule inlining (boolean true), indirect, unknowns in rule, result value unification (true)", + query: "data.test.p", + modules: []string{`package test + p if { + q = true + } + + default q := true + + q := false if { input.x = 7 } + `}, + // Default rule is not inconsequential, support module must be generated + wantQueries: []string{"data.partial.test.q = true"}, + wantSupport: []string{`package partial.test + q = false if { input.x = 7 } + default q = true + `}, + }, + { + note: "default rule inlining (boolean true), indirect, unknowns in rule, result value comparison (true)", + query: "data.test.p", + modules: []string{`package test + p if { + q == true + } + + default q := true + + q := false if { input.x = 7 } + `}, + // Default rule is not inconsequential, support module must be generated + wantQueries: []string{"data.partial.test.q = true"}, + wantSupport: []string{`package partial.test + q = false if { input.x = 7 } + default q = true + `}, + }, + + { + note: "default rule inlining (int), indirect, unknowns in rule, result value presence", + query: "data.test.p", + modules: []string{`package test + p if { + q + } + + default q := 42 + + q := 1 if { input.x = 7 } + `}, + // Default rule is not inconsequential, support module must be generated + wantQueries: []string{"data.partial.test.q"}, + wantSupport: []string{`package partial.test + q = 1 if { input.x = 7 } + default q = 42 + `}, + }, + { + note: "default rule inlining (int), indirect, unknowns in rule, result value unification (value eq non-default rule)", + query: "data.test.p", + modules: []string{`package test + p if { + q = 1 + } + + default q := 42 + + q := 1 if { input.x = 7 } + `}, + wantQueries: []string{"input.x = 7"}, + }, + { + note: "default rule inlining (int), indirect, unknowns in rule, result value unification (value eq default rule)", + query: "data.test.p", + modules: []string{`package test + p if { + q = 42 + } + + default q := 42 + + q := 1 if { input.x = 7 } + `}, + wantQueries: []string{"data.partial.test.q = 42"}, + wantSupport: []string{`package partial.test + q = 1 if { input.x = 7 } + default q = 42 + `}, + }, + { + note: "default rule inlining (int), indirect, unknowns in rule, result value unification (value eq no rule)", + query: "data.test.p", + modules: []string{`package test + p if { + q = 20 + } + + default q := 42 + + q := 1 if { input.x = 7 } + `}, + wantQueries: []string{}, // unconditionally false + }, + { + note: "default rule inlining (int), indirect, unknowns in rule, var result, result value unification", + query: "data.test.p", + modules: []string{`package test + p if { + q = 20 + } + + default q := 42 + + q := x if { input.x = 7; x := input.y } + `}, + wantQueries: []string{"input.x = 7; 20 = input.y"}, + }, + { + note: "default rule inlining (int), indirect, unknowns in rule, var result, result value unification (value eq default rule)", + query: "data.test.p", + modules: []string{`package test + p if { + q = 42 + } + + default q := 42 + + q := x if { + input.x = 7 + x := input.y + } + `}, + wantQueries: []string{"data.partial.test.q = 42"}, + wantSupport: []string{`package partial.test + default q = 42 + q = __local0__2 if { + input.x = 7 + __local0__2 = input.y + } + `}, + }, + + { + note: "default rule inlining (int), indirect, unknowns in multiple rules, result value presence", + query: "data.test.p", + modules: []string{`package test + p if { + q + } + + default q := 40 + + q := 41 if { input.x = 6 } + q := 42 if { input.x = 7 } + q := 43 if { input.x = 8 } + `}, + wantQueries: []string{"data.partial.test.q"}, // Should be unconditionally true? + wantSupport: []string{`package partial.test + default q = 40 + q = 41 if { input.x = 6 } + q = 42 if { input.x = 7 } + q = 43 if { input.x = 8 } + `}, + }, + { + note: "default rule inlining (int), indirect, unknowns in multiple rules, result value unification (same as default rule)", + query: "data.test.p", + modules: []string{`package test + p if { + q = 40 + } + + default q := 40 + + q := 41 if { input.x = 6 } + q := 42 if { input.x = 7 } + q := 43 if { input.x = 8 } + `}, + wantQueries: []string{"data.partial.test.q = 40"}, + wantSupport: []string{`package partial.test + default q = 40 + q = 41 if { input.x = 6 } + q = 42 if { input.x = 7 } + q = 43 if { input.x = 8 } + `}, + }, + { + note: "default rule inlining (int), indirect, unknowns in multiple rules, result value unification (same as rule)", + query: "data.test.p", + modules: []string{`package test + p if { + q = 42 + } + + default q := 40 + + q := 41 if { input.x = 6 } + q := 42 if { input.x = 7 } + q := 43 if { input.x = 8 } + `}, + wantQueries: []string{"input.x = 7"}, + }, + { + note: "default rule inlining (int), indirect, unknowns in multiple rules, result value unification (same as no rule)", + query: "data.test.p", + modules: []string{`package test + p if { + q = 44 + } + + default q := 40 + + q := 41 if { input.x = 6 } + q := 42 if { input.x = 7 } + q := 43 if { input.x = 8 } + `}, + wantQueries: []string{}, // unconditionally false + }, + { + note: "default rule inlining (int), indirect, unknowns in multiple rules, var result, result value unification", + query: "data.test.p", + modules: []string{`package test + p if { + q = 44 + } + + default q := 40 + + q := 41 if { input.x = 6 } + q := x if { input.x = 7; x := input.y } + q := x if { input.x = 8; x := input.y } + `}, + wantQueries: []string{ + "input.x = 7; 44 = input.y", + "input.x = 8; 44 = input.y", + }, + }, + + { + note: "default rule inlining, sole ref, not", + query: "data.test.p", + modules: []string{`package test + p if { + not q + } + + default q := false + + q if { input.x = 7 } + `}, + wantQueries: []string{"not input.x = 7"}, + }, + + { // Regression test for https://github.com/open-policy-agent/opa/issues/1418 + note: "regression, good", + query: "data.x.p_good", + modules: []string{`package x + +p_bad if { + q +} + +p_good if { + q == true +} + +default q := false + +q if { input.x = 7 }`}, + wantQueries: []string{"input.x = 7"}, + }, + { // Regression test for https://github.com/open-policy-agent/opa/issues/1418 + note: "regression, bad", + query: "data.x.p_bad", + modules: []string{`package x + +p_bad if { + q +} + +p_good if { + q == true +} + +default q := false + +q if { input.x = 7 }`}, + wantQueries: []string{"input.x = 7"}, + }, + } + + ctx := context.Background() + + for _, tc := range tests { + params := fixtureParams{ + note: tc.note, + query: tc.query, + modules: tc.modules, + moduleASTs: tc.moduleASTs, + data: tc.data, + input: tc.input, + } + prepareTest(ctx, t, params, func(ctx context.Context, t *testing.T, f fixture) { + + var save []string + + if tc.unknowns == nil { + save = []string{"input"} + } else { + save = tc.unknowns + } + + unknowns := make([]*ast.Term, len(save)) + for i, s := range save { + unknowns[i] = ast.MustParseTerm(s) + } + + disableInlining := make([]ast.Ref, len(tc.disableInlining)) + for i, s := range tc.disableInlining { + disableInlining[i] = ast.MustParseRef(s) + } + + var buf BufferTracer + + query := NewQuery(f.query). + WithCompiler(f.compiler). + WithStore(f.store). + WithTransaction(f.txn). + WithInput(f.input). + WithTracer(&buf). + WithUnknowns(unknowns). + WithDisableInlining(disableInlining). + WithSkipPartialNamespace(tc.skipPartialNamespace). + WithShallowInlining(tc.shallow). + WithNondeterministicBuiltins(tc.nondeterministicBuiltins) + + // Set genvarprefix so that tests can refer to vars in generated + // expressions. + query.genvarprefix = "x" + + partials, support, err := query.PartialRun(ctx) + + if err != nil { + if buf != nil { + PrettyTrace(os.Stdout, buf) + } + t.Fatal(err) + } + + var expectedQueries []ast.Body + + opts := ast.ParserOptions{AllFutureKeywords: true} + if len(tc.wantQueryASTs) > 0 { + expectedQueries = tc.wantQueryASTs + } else { + expectedQueries = make([]ast.Body, len(tc.wantQueries)) + for i := range tc.wantQueries { + expectedQueries[i] = ast.MustParseBodyWithOpts(tc.wantQueries[i], opts) + } + } + + queriesA, queriesB := bodySet(partials), bodySet(expectedQueries) + if !queriesB.Equal(queriesA, tc.ignoreOrder) { + missing := queriesB.Diff(queriesA, tc.ignoreOrder) + extra := queriesA.Diff(queriesB, tc.ignoreOrder) + t.Errorf("Partial evaluation results differ. Expected %d queries but got %d queries:\nMissing:\n%v\nExtra:\n%v", len(queriesB), len(queriesA), missing, extra) + } + + var expectedSupport []*ast.Module + if len(tc.wantSupportASTs) > 0 { + expectedSupport = tc.wantSupportASTs + } else { + for i := range tc.wantSupport { + expectedSupport = append(expectedSupport, ast.MustParseModuleWithOpts(tc.wantSupport[i], opts)) + } + } + supportA, supportB := moduleSet(support), moduleSet(expectedSupport) + if !supportA.Equal(supportB) { + missing := supportB.Diff(supportA) + extra := supportA.Diff(supportB) + t.Errorf("Partial evaluation results differ. Expected %d modules but got %d:\nMissing:\n%v\nExtra:\n%v", len(supportB), len(supportA), missing, extra) + } + }) + } +} + +type fixtureParams struct { + note string + data string + modules []string + moduleASTs []*ast.Module + query string + input string +} + +type fixture struct { + query ast.Body + compiler *ast.Compiler + store storage.Store + txn storage.Transaction + input *ast.Term +} + +func prepareTest(ctx context.Context, t *testing.T, params fixtureParams, f func(context.Context, *testing.T, fixture)) { + + t.Run(params.note, func(t *testing.T) { + + var store storage.Store + + if len(params.data) > 0 { + j := util.MustUnmarshalJSON([]byte(params.data)) + store = inmem.NewFromObject(j.(map[string]any)) + } else { + store = inmem.New() + } + + err := storage.Txn(ctx, store, storage.TransactionParams{}, func(txn storage.Transaction) error { + + compiler := ast.NewCompiler() + modules := map[string]*ast.Module{} + opts := ast.ParserOptions{AllFutureKeywords: true} + + if len(params.moduleASTs) > 0 { + for i, module := range params.moduleASTs { + modules[strconv.Itoa(i)] = module + } + } + for i, module := range params.modules { + j := len(params.moduleASTs) + i + modules[strconv.Itoa(j)] = ast.MustParseModuleWithOpts(module, opts) + } + + if compiler.Compile(modules); compiler.Failed() { + t.Fatal(compiler.Errors) + } + + var input *ast.Term + if len(params.input) > 0 { + input = ast.MustParseTerm(params.input) + } + + queryContext := ast.NewQueryContext() + + queryCompiler := compiler.QueryCompiler().WithContext(queryContext) + + compiledQuery, err := queryCompiler.Compile(ast.MustParseBody(params.query)) + if err != nil { + t.Fatal(err) + } + + f(ctx, t, fixture{ + query: compiledQuery, + compiler: compiler, + store: store, + txn: txn, + input: input, + }) + + return nil + }) + if err != nil { + t.Fatal(err) + } + }) +} + +type bodySet []ast.Body + +func (s bodySet) String() string { + buf := make([]string, len(s)) + for i := range s { + buf[i] = fmt.Sprintf("body %d: %v", i+1, s[i].String()) + } + return strings.Join(buf, "\n") +} + +func (s bodySet) Contains(b ast.Body, ignoreOrder bool) bool { + for i := range s { + if ignoreOrder { + if bodyEqualUnordered(b, s[i]) { + return true + } + } else { + if s[i].Equal(b) { + return true + } + } + } + return false +} + +func (s bodySet) Diff(other bodySet, ignoreOrder bool) (r bodySet) { + for i := range s { + if !other.Contains(s[i], ignoreOrder) { + r = append(r, s[i]) + } + } + return r +} + +func (s bodySet) Equal(other bodySet, ignoreOrder bool) bool { + return len(s.Diff(other, ignoreOrder)) == 0 && len(other.Diff(s, ignoreOrder)) == 0 +} + +func bodyEqualUnordered(a, b ast.Body) bool { + for i := range a { + found := false + for j := range b { + cpy := b[j].Copy() + cpy.Index = a[i].Index // overwrite index to ensure comparison is unordered. + if a[i].Compare(cpy) == 0 { + found = true + break + } + } + if !found { + return false + } + } + return true +} + +type moduleSet []*ast.Module + +func (s moduleSet) String() string { + buf := make([]string, len(s)) + for i := range s { + buf[i] = fmt.Sprintf("module %d: %v", i+1, s[i].String()) + } + return strings.Join(buf, "\n") +} + +func (s moduleSet) Contains(b *ast.Module) bool { + for i := range s { + if s[i].Package.Equal(b.Package) { + rs1 := ast.NewRuleSet(s[i].Rules...) + rs2 := ast.NewRuleSet(b.Rules...) + if rs1.Equal(rs2) { + return true + } + } + } + return false +} + +func (s moduleSet) Diff(other moduleSet) (r moduleSet) { + for i := range s { + if !other.Contains(s[i]) { + r = append(r, s[i]) + } + } + return r +} + +func (s moduleSet) Equal(other moduleSet) bool { + return len(s.Diff(other)) == 0 && len(other.Diff(s)) == 0 +} + +var testserver = srv(func(w http.ResponseWriter, _ *http.Request) error { + w.Header().Set("Content-Type", "application/json") + return json.NewEncoder(w).Encode(map[string]any{ + "p": "x", + }) +}) + +func srv(f func(http.ResponseWriter, *http.Request) error) *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if err := f(w, r); err != nil { + w.WriteHeader(500) + fmt.Fprintln(w, err.Error()) + } + })) +} diff --git a/third_party/opa/v1/topdown/topdown_test.go b/third_party/opa/v1/topdown/topdown_test.go new file mode 100644 index 000000000000..117abc890dfd --- /dev/null +++ b/third_party/opa/v1/topdown/topdown_test.go @@ -0,0 +1,2479 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "bytes" + "context" + "fmt" + "os" + "path" + "path/filepath" + "reflect" + "runtime" + "slices" + "sort" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/open-policy-agent/opa/v1/format" + "sigs.k8s.io/yaml" + + iCache "github.com/open-policy-agent/opa/v1/topdown/cache" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/types" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestTopDownQueryIDsUnique(t *testing.T) { + t.Parallel() + + ctx := context.Background() + store := inmem.New() + inputTerm := &ast.Term{} + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + compiler := compileModules([]string{ + `package x + p if { 1 } + p if { 2 }`}) + + tr := []*Event{} + + query := NewQuery(ast.MustParseBody("data.x.p")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer((*BufferTracer)(&tr)). + WithInput(inputTerm) + + _, err := query.Run(ctx) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + queryIDs := map[uint64]bool{} // set of seen queryIDs (in EnterOps) + for _, evt := range tr { + if evt.Op != EnterOp { + continue + } + if queryIDs[evt.QueryID] { + t.Errorf("duplicate queryID: %v", evt) + } + queryIDs[evt.QueryID] = true + } +} + +func TestTopDownIndexExpr(t *testing.T) { + t.Parallel() + + ctx := context.Background() + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + compiler := compileModules([]string{ + `package test + + p = true if { + 1 > 0 + q + } + + q = true if { true }`}) + + tr := []*Event{} + + query := NewQuery(ast.MustParseBody("data.test.p")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer((*BufferTracer)(&tr)) + + _, err := query.Run(ctx) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + exp := []*ast.Expr{ + ast.MustParseExpr("data.test.p"), + ast.MustParseExpr("data.test.q"), + } + + i := 0 + for _, evt := range tr { + if evt.Op != IndexOp { + continue + } + + expr, ok := evt.Node.(*ast.Expr) + if !ok { + t.Fatal("Expected expr node but got:", evt.Node) + } + + exp[i].Index = i + if ast.Compare(expr, exp[i]) != 0 { + t.Fatalf("Expected %v but got: %v", exp[i], expr) + } + i++ + } +} + +func TestTopDownWithKeyword(t *testing.T) { + t.Parallel() + + tests := []struct { + note string + rules []string + modules []string + input string + exp any + }{ + { + // NOTE(tsandall): This case assumes that partial sets are not memoized. + // If we change that, it'll be harder to test that the comprehension + // cache is invalidated. + note: "invalidate comprehension cache", + exp: `[[{"b": ["a", "c"]}], [{"b": ["a"]}]]`, + modules: []string{`package ex + s contains x if { + x = {v: ks | + v = input[i] + ks = {k | v = input[k]} + } + } + `}, + rules: []string{`p = [x, y] if { + x = data.ex.s with input as {"a": "b", "c": "b"} + y = data.ex.s with input as {"a": "b"} + }`}, + }, + } + + for _, tc := range tests { + runTopDownTestCaseWithModules(t, loadSmallTestData(), tc.note, tc.rules, tc.modules, tc.input, tc.exp) + } +} + +// Warning(philipc): This test modifies package variables in the ast package, +// which means it cannot be run in parallel with other tests. +func TestTopDownUnsupportedBuiltin(t *testing.T) { + ast.RegisterBuiltin(&ast.Builtin{ + Name: "unsupported_builtin", + }) + + body := ast.MustParseBody(`unsupported_builtin()`) + ctx := context.Background() + compiler := ast.NewCompiler() + store := inmem.New() + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + q := NewQuery(body).WithCompiler(compiler).WithStore(store).WithTransaction(txn) + _, err := q.Run(ctx) + + expected := unsupportedBuiltinErr(body[0].Location) + + if err.Error() != expected.Error() { + t.Fatalf("Expected %v but got: %v", expected, err) + } +} + +func TestTopDownQueryCancellation(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + compiler := compileModules([]string{ + ` + package test + + p if { data.arr[_] = x; test.sleep("10ms"); x == 999 } + `, + }) + + arr := make([]any, 1000) + for i := range 1000 { + arr[i] = i + } + data := map[string]any{ + "arr": arr, + } + + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + cancel := NewCancel() + buf := NewBufferTracer() + + query := NewQuery(ast.MustParseBody("data.test.p")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithCancel(cancel). + WithTracer(buf) + + done := make(chan struct{}) + go func() { + time.Sleep(time.Millisecond * 50) + cancel.Cancel() + close(done) + }() + + qrs, err := query.Run(ctx) + if err == nil || err.(*Error).Code != CancelErr { + t.Errorf("Expected cancel error but got: %v (err: %v)", qrs, err) + PrettyTrace(os.Stdout, []*Event(*buf)) + } + + <-done +} + +func TestTopDownQueryCancellationEvery(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + module := func(ev ast.Every, _ ...any) *ast.Module { + t.Helper() + m := ast.MustParseModuleWithOpts(`package test + p if { true }`, + ast.ParserOptions{AllFutureKeywords: true}) + m.Rules[0].Body = ast.NewBody(ast.NewExpr(&ev)) + return m + } + + tests := []struct { + note string + module *ast.Module + }{ + { + note: "large domain, simple body", + module: module(ast.Every{ // every x in data.arr { ... } + Value: ast.VarTerm("x"), + Domain: ast.RefTerm(ast.VarTerm("data"), ast.StringTerm("arr")), + Body: ast.MustParseBody(`print(x); test.sleep("10ms")`), + }), + }, + { + note: "simple domain, long evaluation time in body", + module: module(ast.Every{ // every x in [999] { ... } + Value: ast.VarTerm("x"), + Domain: ast.MustParseTerm(`[999]`), + Body: ast.MustParseBody(`data.arr[_] = y; test.sleep("10ms"); print(y); x == y`), + }), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + compiler := ast.NewCompiler().WithEnablePrintStatements(true) + compiler.Compile(map[string]*ast.Module{"test.rego": tc.module}) + if compiler.Failed() { + t.Fatalf("compiler: %v", compiler.Errors) + } + + arr := make([]any, 1000) + for i := range 1000 { + arr[i] = i + } + data := map[string]any{ + "arr": arr, + } + + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + cancel := NewCancel() + buf := bytes.Buffer{} + tr := NewBufferTracer() + ph := NewPrintHook(&buf) + query := NewQuery(ast.MustParseBody("data.test.p")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithCancel(cancel). + WithTracer(tr). + WithPrintHook(ph) + + done := make(chan struct{}) + go func() { + time.Sleep(time.Millisecond * 500) + cancel.Cancel() + close(done) + }() + + qrs, err := query.Run(ctx) + if err == nil || err.(*Error).Code != CancelErr { + t.Errorf("Expected cancel error but got: %v (err: %v)", qrs, err) + } + + notes := strings.Split(buf.String(), "\n") + notes = notes[:len(notes)-1] // last one is empty-string because each line ends in "\n" + if len(notes) == 0 { + t.Errorf("expected prints, got nothing") + } + if len(notes) == len(arr) { + t.Errorf("expected less than %d prints, got %d", len(arr), len(notes)) + } + t.Logf("got %d notes", len(notes)) + + if t.Failed() && testing.Verbose() { + PrettyTrace(os.Stdout, []*Event(*tr)) + } + + <-done + }) + } +} + +func TestTopDownEarlyExit(t *testing.T) { + t.Parallel() + + // NOTE(sr): There are two ways to early-exit: don't evaluate subsequent + // rule bodies, like + // + // p { + // true + // } + // p { + // # not evaluated + // } + // + // and not evaluating subsequent "rounds" of iteration: + // + // p { + // x[_] = "y" + // } + + n := func(ns ...string) []string { return ns } + + tests := []struct { + note string + module string + notes []string // expected note events + extraExit int // number of "extra" events expected, each test expects 1 note, 1 early exit + }{ + { + note: "complete doc", + module: ` + package test + p if { trace("a") } + p if { trace("b") }`, + notes: n("a"), + }, + { + note: "complete doc, nested, both exit early", + module: ` + package test + p if { q; trace("a") } + p if { q; trace("b") } + + q if { trace("c") } + q if { trace("d") }`, + extraExit: 1, // p + q + notes: n("a", "c"), + }, + { + note: "complete doc, nested, both exit early (else)", + module: ` + package test + p if { q; trace("a") } + p if { q; trace("b") } + + q if { trace("c"); false } + else = true if { trace("d")} + q if { trace("e") }`, + extraExit: 1, // p + q + notes: n("a", "c", "d"), + }, + { + note: "complete doc: other complete doc that cannot exit early", + module: ` + package test + p if { q } + + q = x if { x := true; trace("a") } + q = x if { x := true; trace("b") }`, + notes: n("a", "b"), + }, + { + note: "complete doc: other complete doc that cannot exit early, one undefined", + module: ` + package test + p if { q } + + q = x if { x := true; trace("a"); false } + q = x if { x := true; trace("b") }`, + notes: n("a", "b"), + }, + { + note: "complete doc: other complete doc that cannot exit early (else)", + module: ` + package test + p if { q } + + q = x if { x := true; trace("a"); false } + else = x if { x := true; trace("b") }`, + notes: n("a", "b"), + }, + { + note: "complete doc: other complete doc that cannot exit early, partial doc", + module: ` + package test + p if { q } + + q contains x if { + x := [1, 2, 3][_]; trace("a") + }`, + notes: n("a", "a", "a"), + }, + { + note: "complete doc, iteration: other partial doc that cannot exit early", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + q + } + + q contains x if { + x := [1, 2, 3][_]; trace("a") + }`, + notes: n("x", "a", "a", "a"), + }, + { + note: "complete doc, iteration: multiple other partial docs that cannot exit early", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + q + } + + q contains x if { + x := [1, 2, 3][_]; trace("a") + } + + q contains x if { + x := [4, 5, 6][_]; trace("b") + }`, + notes: n("x", "a", "a", "a", "b", "b", "b"), + }, + { + note: "complete doc: other function that cannot exit early", + module: ` + package test + p if { q(1) } + + q(_) = x if { x := true; trace("a") } + q(_) = x if { x := true; trace("b") }`, + notes: n("a", "b"), + }, + { + note: "complete doc: other function that cannot exit early (else)", + module: ` + package test + p if { q(1) } + + q(_) = x if { x := true; trace("a"); false } + else = true if { trace("b") } + + q(_) = x if { x := true; trace("c") }`, + notes: n("a", "b", "c"), + }, + { + note: "function", + module: ` + package test + p = f(1) + f(_) if { trace("a") } + f(_) if { trace("b") }`, + notes: n("a"), + }, + { + note: "function: other function, both exit early", + module: ` + package test + p = f(1) + f(_) if { g(1); trace("a") } + f(_) if { g(1); trace("b") } + g(_) if { trace("c") } + g(_) if { trace("d") }`, + notes: n("a", "c"), + extraExit: 1, // f() + g() + }, + { + note: "function: other function, both exit early (else)", + module: ` + package test + p = f(1) + + f(_) if { g(1); trace("a") } + f(_) if { g(1); trace("b") } + + g(_) if { trace("c"); false } + else = true if { trace("d") } + g(_) if { trace("e") }`, + notes: n("a", "c", "d"), + extraExit: 1, // f() + g() + }, + { + note: "function: other complete doc that cannot exit early", + module: ` + package test + p = f(1) + f(_) if { q } + q = x if { x := true; trace("a") } + q = x if { x := true; trace("b") }`, + notes: n("a", "b"), + }, + { + note: "function: other complete doc that cannot exit early (else)", + module: ` + package test + p = f(1) + f(_) if { q } + q = x if { x := true; trace("a"); false } + else = x if { x := true; trace("b") }`, + notes: n("a", "b"), + }, + { + note: "complete doc, array iteration", + module: ` + package test + p if { data.arr[_] = _; trace("x") } + `, + notes: n("x"), + }, + { + note: "complete doc, multiple array iteration", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + data.arr[_] = y; trace("y") + data.arr[_] = z; trace("z") + } + `, + notes: n("x", "y", "z"), + }, + { + note: "complete doc, multiple array iteration, ref to other complete doc with iteration", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + data.arr[_] = y; trace("y") + q; trace("q") + } + + q if { + data.arr[_] = a; trace("a") + data.arr[_] = b; trace("b") + } + `, + notes: n("a", "b", "x", "y", "q"), + extraExit: 1, // p + q + }, + { + note: "complete doc, multiple array iteration, ref to multiple other complete docs with iteration", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + data.arr[_] = y; trace("y") + q; trace("q") + } + + q if { + data.arr[_] = a; trace("a") + data.arr[_] = b; trace("b") + } + + # Not called because of EE + q if { + data.arr[_] = a; trace("c") + data.arr[_] = b; trace("d") + } + `, + notes: n("a", "b", "x", "y", "q"), + extraExit: 1, // p + q + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, multiple array iterations, ref to other complete doc with iteration and cached result", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + data.arr[_] = y; trace("y") + q; trace("q1") + q; trace("q2") # result of q in cache + } + + q if { + data.arr[_] = a; trace("a") + data.arr[_] = b; trace("b") + } + `, + notes: n("x", "y", "q1", "q2", "a", "b"), + extraExit: 1, // p + q + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, multiple array iterations, ref to multiple other complete docs with iteration and cached result", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + data.arr[_] = y; trace("y") + q; trace("q1") + q; trace("q2") # result of q in cache + } + + q if { + data.arr[_] = a; trace("a") + data.arr[_] = b; trace("b") + } + + # Not called because of EE + q if { + data.arr[_] = a; trace("c") + data.arr[_] = b; trace("d") + } + `, + notes: n("x", "y", "q1", "q2", "a", "b"), + extraExit: 1, // p + q + }, + { + note: "complete doc, multiple array iterations, ref to other multiple complete docs with iteration", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + data.arr[_] = y; trace("y") + q; trace("q") + r; trace("r") + } + + q if { + data.arr[_] = a; trace("a") + data.arr[_] = b; trace("b") + } + + r if { + data.arr[_] = c; trace("c") + data.arr[_] = d; trace("d") + } + `, + notes: n("x", "y", "a", "b", "q", "c", "d", "r"), + extraExit: 2, // p + q + r + }, + { + note: "complete doc, array iteration, package-local data", + module: ` + package test + arr := ["a", "b", "c"] + p if { + arr[_] = x; trace("x") + } + `, + notes: n("x"), + extraExit: 1, // p + arr + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, multiple array iterations, module-local data, cached result", + module: ` + package test + arr := ["a", "b", "c"] + p if { + arr[_] = x; trace("x") + arr[_] = y; trace("y") # arr in cache + } + `, + notes: n("x", "y"), + extraExit: 1, // p + arr + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, array iteration, ref to other complete doc without early exit", + module: `package test + p if { + data.arr[_]; trace("x") + q; trace("y") + } + + q := x if { + x := 1 + }`, + notes: n("x", "y"), + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, array iteration, ref to other complete doc without early exit (multiple rules)", + module: `package test + p if { + data.arr[_]; trace("x") + q; trace("y") + } + + q := x if { + x := 1; trace("a") + } + + q := x if { + x := 1; trace("b") + }`, + notes: n("x", "a", "b", "y"), + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, array iteration, multiple refs to other complete docs without early exit", + module: `package test + p if { + data.arr[_]; trace("x") + q; trace("y") + r; trace("z") + } + + q := x if { + x := 1 + } + + r := x if { + x := 2 + }`, + notes: n("x", "y", "z"), + }, + { + note: "complete doc, array iteration, func call with early exit", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + f(1) == 1 + } + + f(x) := 1 if { + trace("a") + } + `, + notes: n("x", "a"), + extraExit: 1, // p + f() + }, + { + note: "complete doc, multiple array iterations, func call multiple early exit", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + f(1) == 1 + } + + f(x) := 1 if { + trace("a") + } + + f(x) := 1 if { + trace("b") + } + `, + notes: n("x", "a"), + extraExit: 1, // p + f() + }, + { + note: "complete doc, multiple array iterations, func call without early exit", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + f(1) == 1 + } + + f(x) := 1 if { + trace("a") + } + + f(x) := 1 if { + trace("b") + } + + f(x) := 2 if { + trace("c") + false # to avoid eval_conflict_error error + } + `, + notes: n("x", "a", "b", "c"), + }, + { + note: "complete doc, multiple array iterations, func call with early exit and iteration", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + f(1) == 1 + } + + f(x) := 1 if { + data.arr[_] = a; trace("a") + } + `, + notes: n("x", "a"), + extraExit: 1, // p + f() + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, array iteration, func call without early exit, static arg", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + f(1) == 1 + } + + f(x) := x if { + trace("a") + } + `, + notes: n("x", "a"), + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, array iteration -> func call without early exit, dynamic arg", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + f(x) == x + } + + f(x) := x if { + trace("a") + } + `, + notes: n("x", "a"), + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, set iteration -> func call without early exit, dynamic arg", + module: ` + package test + s := { 1, 2, 3 } + + p if { + s[_] = x; trace("x") + f(x) == x + } + + f(x) := x if { + trace("a") + } + `, + notes: n("x", "a"), + extraExit: 1, // p + o + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, object iteration -> func call without early exit, dynamic arg", + module: ` + package test + o := { + "a": 1, + "b": 2, + "c": 3, + } + + p if { + o[_] = x; trace("x") + f(x) == x + } + + f(x) := x if { + trace("a") + } + `, + notes: n("x", "a"), + extraExit: 1, // p + o + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, array iteration -> func call without early exit, array iteration, dynamic arg", + module: ` + package test + p if { + arr[_] = x; trace("x") + f(x) == x + } + + arr := [1, 2, 3, 4, 2] + + f(x) := x if { + arr[_] = y; trace("a") + y == 2; trace("b") # y will have exactly two matches, so we expect two "b" notes, and an exhaustive number of "a" notes + } + `, + notes: n("x", "a", "a", "a", "a", "a", "b", "b"), + extraExit: 1, // p + arr + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, set iteration -> func call without early exit, set iteration, dynamic arg", + module: ` + package test + + s := { 1, 2, 3, 4, 5 } + + p if { + s[_] = x; trace("x") + f(x) == x + } + + f(x) := x if { + s[_] = y; trace("a") + y == 1; trace("b") # y will have exactly one match, so we expect one "b" note, and an exhaustive number of "a" notes + } + `, + notes: n("x", "a", "a", "a", "a", "a", "b"), + extraExit: 1, // p + arr + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, object iteration -> func call without early exit, object iteration, dynamic arg", + module: ` + package test + + o := { + "a": 1, + "b": 2, + "c": 3, + "d": 2, + } + + p if { + o[_] = x; trace("x") + f(x) == x + } + + f(x) := x if { + o[_] = y; trace("a") + y == 2; trace("b") # y will have exactly two matches, so we expect two "b" notes, and an exhaustive number of "a" notes + } + `, + notes: n("x", "a", "a", "a", "a", "b", "b"), + extraExit: 1, // p + arr + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, array iteration -> func call without early exit, virtual doc array iteration, dynamic arg", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + f(x) == x + } + +arr := [1, 2, 3, 4, 5] + + f(x) := x if { + arr[_] = y; trace("a") + y == 3; trace("b") + } + `, + notes: n("x", "a", "a", "a", "a", "a", "b"), + extraExit: 1, // p + arr + }, + { // Regression test for: https://github.com/open-policy-agent/opa/issues/6566 + note: "complete doc, array iteration -> func (multi) call without early exit, static arg", + module: ` + package test + p if { + data.arr[_] = x; trace("x") + f(1) == 1 + } + + f(x) := x if { + trace("a") + } + + f(x) := x if { + trace("b") + false + } + + f(x) := x if { + trace("c") + } + `, + notes: n("x", "a", "b", "c"), + }, + { + note: "complete doc, obj iteration", + module: ` + package test + p if { data.obj[_] = _; trace("x") } + `, + notes: n("x"), + }, + { + note: "complete doc, set iteration", + module: ` + package test + xs := { i | data.arr[i] } + p if { xs[_] = _; trace("x") } + `, + notes: n("x"), + }, + { + note: "function doc, array iteration", + module: ` + package test + p = f(1) + f(_) if { data.arr[_] = _; trace("x") } + `, + notes: n("x"), + }, + { + note: "complete doc, obj iteration", + module: ` + package test + p = f(1) + f(_) if { data.obj[_] = _; trace("x") } + `, + notes: n("x"), + }, + { + note: "complete doc, set iteration", + module: ` + package test + xs := { i | data.arr[i] } + p = f(1) + f(_) if { xs[_] = _; trace("x") } + `, + notes: n("x"), + }, + { + note: "ee -> ee -> ee", + module: `package test + p if { + data.arr[_] = x; trace("a") + q; trace("b") + } + + q if { + data.arr[_] = x; trace("c") + r; trace("d") + } + + r if { + data.arr[i] = x; trace("e") + } + `, + notes: n("a", "c", "e", "d", "b"), + extraExit: 2, // p + q + r + }, + { + note: "ee -> no ee -> ee", + module: `package test + p if { + data.arr[i] = x; trace("a") + q; trace("b") + } + + q contains x if { + [1, 2, 3][_] = x; trace("c") + r; trace("d") + } + + r if { + data.arr[i] = x; trace("e") + } + `, + notes: n("a", "c", "c", "c", "e", "d", "d", "d", "b"), + extraExit: 1, // p + r + }, + { + note: "ee -> no ee (multiple) -> ee", + module: `package test + p if { + data.arr[i] = x; trace("a") + q; trace("b") + } + + q contains x if { + [1, 2, 3][_] = x; trace("c") + r; trace("d") + } + + q contains x if { + [4, 5, 6][_] = x; trace("e") + r; trace("f") + } + + r if { + data.arr[i] = x; trace("g") + } + `, + notes: n("a", "c", "c", "c", "d", "d", "d", "e", "e", "e", "f", "f", "f", "g", "b"), + extraExit: 1, // p + r + }, + { + note: "ee -> (no ee, ee)", + module: `package test + p if { + data.arr[i] = x; trace("a") + q; trace("b") + r; trace("c") + } + + q contains x if { + [1, 2, 3][_] = x; trace("d") + } + + r if { + data.arr[i] = x; trace("e") + } + `, + notes: n("a", "d", "d", "d", "b", "e", "c"), + extraExit: 1, // p + r + }, + // every statements + { + note: "complete doc with every", + module: `package test + import future.keywords + p if { + data.arr[_] = x; trace("x") + every x in [1, 2, 3] { x; trace("a") } + } + `, + notes: n("x", "a", "a", "a"), + extraExit: 3, // p + every*3 + }, + { + note: "complete doc -> every, array iteration", + module: `package test + import future.keywords + p if { + data.arr[_] = x; trace("x") + every x in [1, 2, 3] { + data.arr[_] = y; trace("a") + x; trace("b") + } + } + `, + notes: n("x", "a", "a", "a", "b", "b", "b"), + extraExit: 3, // p + every*3 + }, + { + note: "complete doc -> every, array iteration -> complete doc with ee -> complete doc no ee", + module: `package test + import future.keywords + p if { + data.arr_small[_] = x; trace("x") + every x in [1, 2, 3] { + data.arr_small[_] = y; trace("e1") + x + q; trace("e2") + } + } + + q if { + data.arr_small[_] = x; trace("q1") + r; trace("q2") + } + + r := x if { + x := 1 + data.arr_small[_] = y; trace("r1") + } + `, + notes: n("x", "e1", "e1", "e1", "q1", "r1", "r1", "r1", "r1", "r1", "q2", "e2", "e2", "e2"), + extraExit: 4, // p + every*3 + q + }, + { + note: "complete doc -> every, array iteration -> complete doc no ee -> complete doc with ee", + module: `package test + import future.keywords + p if { + data.arr[_] = x; trace("x") + every x in [1, 2, 3] { + data.arr_small[_] = y; trace("e1") + x + q; trace("e2") + } + } + + q := x if { + x := 1 + data.arr_small[_] = y; trace("q1") + r; trace("q2") + } + + r if { + data.arr[_] = y; trace("r1") + } + `, + notes: n("x", "e1", "e1", "e1", "q1", "q1", "q1", "q1", "q1", "r1", "q2", "q2", "q2", "q2", "q2", "e2", "e2", "e2"), + extraExit: 4, // p + every*3 + r + }, + { + note: "complete doc -> complete doc, no ee, with every", + module: `package test + import future.keywords + p if { + data.arr[_] = x; trace("x") + q + } + + arr := [1, 2] + + q := x if { + x := 1 + arr[_] = y; trace("a") + every v in [1, 2, 3] { + v; trace("b") # we expect 3*len(arr)==6 "b" notes + } + } + `, + notes: n("x", "a", "a", "b", "b", "b", "b", "b", "b"), + extraExit: 7, // p + every*3*2 + arr + }, + { + note: "complete doc -> complete doc, no ee, with every -> complete doc ee", + module: `package test + import future.keywords + p if { + data.arr[_] = x; trace("x") + q + } + + arr := [1, 2] + + q := x if { + x := 1 + arr[_] = y; trace("a") + every v in [1, 2, 3] { + v; r; trace("b") # we expect 3*len(arr)==6 "b" notes + } + } + + r if { + data.arr[_] = x; trace("c") + } + `, + notes: n("x", "a", "a", "c", "b", "b", "b", "b", "b", "b"), + extraExit: 8, // p + every*3*2 + arr + r + }, + // array comprehensions + { + note: "complete doc, array iteration, ee -> array comprehension -> complete doc, ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + y := [v | v = data.arr[_]; q]; trace("p2") + } + + q if { + data.arr[_] = x; trace("q") + } + `, + notes: n("p1", "q", "p2"), + extraExit: 1, // p + q + }, + { + note: "complete doc, array iteration, ee -> array comprehension -> complete doc, ee -> complete doc, no ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + y := [v | v = data.arr[_]; q]; trace("p2") + } + + q if { + data.arr[_] = x; trace("q") + r + } + + r := v if { + v := 1 + data.arr_small[_] = x; trace("r") + } + `, + notes: n("p1", "q", "r", "r", "r", "r", "r", "p2"), + extraExit: 1, // p + q + }, + // set comprehensions + { + note: "complete doc, array iteration, ee -> set comprehension -> complete doc, ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + y := {v | v = data.arr[_]; q}; trace("p2") + } + + q if { + data.arr[_] = x; trace("q") + } + `, + notes: n("p1", "q", "p2"), + extraExit: 1, // p + q + }, + { + note: "complete doc, array iteration, ee -> set comprehension -> complete doc, ee -> complete doc, no ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + y := {v | v = data.arr[_]; q}; trace("p2") + } + + q if { + data.arr[_] = x; trace("q") + r + } + + r := v if { + v := 1 + data.arr_small[_] = x; trace("r") + } + `, + notes: n("p1", "q", "r", "r", "r", "r", "r", "p2"), + extraExit: 1, // p + q + }, + // object comprehensions + { + note: "complete doc, array iteration, ee -> object comprehension -> complete doc, ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + y := {k: v | v = data.arr[k]; q}; trace("p2") + } + + q if { + data.arr[_] = x; trace("q") + } + `, + notes: n("p1", "q", "p2"), + extraExit: 1, // p + q + }, + { + note: "complete doc, array iteration, ee -> object comprehension -> complete doc, ee -> complete doc, no ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + y := {k: v | v = data.arr[k]; q}; trace("p2") + } + + q if { + data.arr[_] = x; trace("q") + r + } + + r := v if { + v := 1 + data.arr_small[_] = x; trace("r") + } + `, + notes: n("p1", "q", "r", "r", "r", "r", "r", "p2"), + extraExit: 1, // p + q + }, + // with statements + { + note: "complete doc, array iteration, ee -> with -> complete doc, ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + q with input.x as data.arr; trace("p2") + } + + q if { + input.x[_] = x; trace("q") + } + `, + notes: n("p1", "q", "p2"), + extraExit: 1, // p + q + }, + { + note: "complete doc, array iteration, ee -> with -> complete doc, no ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + q with input.x as data.arr_small; trace("p2") + } + + q := v if { + v := 1 + input.x[_] = x; trace("q") + } + `, + notes: n("p1", "q", "q", "q", "q", "q", "p2"), + }, + { + note: "complete doc, array iteration, ee -> with -> complete doc, ee -> complete doc, no ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + q with input.x as data.arr_small; trace("p2") + } + + q if { + input.x[_] = x; trace("q1") + r; trace("q2") + } + + r := v if { + v := 1 + input.x[_] = x; trace("r") + } + `, + notes: n("p1", "q1", "r", "r", "r", "r", "r", "q2", "p2"), + extraExit: 1, // p + q + }, + { + note: "complete doc, array iteration, ee -> with -> complete doc, no ee -> complete doc, ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + q with input.x as data.arr_small; trace("p2") + } + + q := v if { + v := 1 + input.x[_] = x; trace("q1") + r; trace("q2") + } + + r if { + input.x[_] = x; trace("r") + } + `, + // data.test.r is evaluated twice, as 'with' in data.test.p will pop the virtual cache before redoes. + // Cache is however maintained through redo-sequence, so data.test.r result will be found in cache from there. + notes: n("p1", "q1", "q1", "q1", "q1", "q1", "q2", "q2", "q2", "q2", "q2", "r", "r", "p2"), + extraExit: 2, // p + r + r + }, + // negation + { + note: "complete doc, array iteration, ee -> negated complete doc, ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + not q; trace("p2") + } + + q := false if { + data.arr[_] = x; trace("q") + } + `, + notes: n("p1", "q", "p2"), + extraExit: 1, // p + q + }, + { + note: "complete doc, array iteration, ee -> negated complete doc, no ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + not q; trace("p2") + } + + q := x if { + x := false + data.arr_small[_] = y; trace("q") + } + `, + notes: n("p1", "q", "q", "q", "q", "q", "p2"), + }, + { + note: "complete doc, array iteration, ee -> negated complete doc, aborted ee", + module: ` + package test + p if { + data.arr[_] = x; trace("p1") + not q; trace("p2") + } + + q if { + data.arr_small[_] = x; trace("q") + false + } + `, + notes: n("p1", "q", "q", "q", "q", "q", "p2"), + }, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + countExit := 1 + tc.extraExit + ctx := context.Background() + compiler := compileModules([]string{tc.module}) + size := 1000 + arr := make([]any, size) + obj := make(map[string]any, size) + for i := range size { + arr[i] = i + obj[strconv.Itoa(i)] = i + } + data := map[string]any{ + "arr": arr, + "arr_small": []int{1, 2, 3, 4, 5}, + "obj": obj, + } + + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + buf := NewBufferTracer() + + query := NewQuery(ast.MustParseBody("data.test.p")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer(buf) + + _, err := query.Run(ctx) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + notes := []string{} + exits := map[string]int{} + + for _, ev := range []*Event(*buf) { + switch ev.Op { + case NoteOp: + notes = append(notes, ev.Message) + case ExitOp: + exits[ev.Message]++ + } + } + sort.Strings(notes) + sort.Strings(tc.notes) + if !slices.Equal(notes, tc.notes) { + t.Errorf("unexpected note traces, expected %v, got %v", tc.notes, notes) + } + if exp, act := countExit, exits["early"]; exp != act { + t.Errorf("expected %d early exit events, got %d", exp, act) + } + + if t.Failed() { + PrettyTrace(os.Stderr, *buf) + } + }) + } +} + +func TestTopDownEvery(t *testing.T) { + t.Parallel() + + n := func(ns ...string) []string { return ns } + + tests := []struct { + note string + module string + notes []string // expected note events, let's see if these are useful + fail bool + }{ + { + note: "domain empty", + module: `package test + p if { every x in [] { print(x) } } + `, + notes: n(), + }, + { + note: "domain undefined", + module: `package test + p if { every x in input { print(x) } } + `, + fail: true, + }, + { + note: "domain is call", + module: `package test + p if { + d := numbers.range(1, 5) + every x in d { x >= 1; print(x) } + }`, + notes: n("1", "2", "3", "4", "5"), + }, + { + note: "simple value", + module: `package test + p if { + every x in [1, 2] { print(x) } + }`, + notes: n("1", "2"), + }, + { + note: "simple key+value", + module: `package test + p if { + every k, v in [1, 2] { k < v; print(v) } + }`, + notes: n("1", "2"), + }, + { + note: "outer bindings", + module: `package test + p if { + i = "outer" + every x in [1, 2] { print(x); print(i) } + }`, + notes: n("1", "outer", "2", "outer"), + }, + { + note: "simple failure, last", + module: `package test + p if { + every x in [1, 2] { x < 2; print(x) } + }`, + notes: n("1"), + fail: true, + }, + { + note: "simple failure, first", + module: `package test + p if { + every x in [1, 2] { x > 1; print(x) } + }`, + notes: n(), + fail: true, + }, + { + note: "early exit in body eval on success", + module: `package test + p if { + every x in [1, 2] { y := [false, true, true][_]; print(x); y } + }`, + notes: n("1", "1", "2", "2"), // Would be triples if EE in the body didn't work + }, + { + note: "early exit suppressed in body eval", + module: `package test + q if { print("q") } + p if { + every x in [1, 2] { q; print(x) } + }`, + notes: n("q", "1", "2"), // Would be only "1" if the EE of q wasn't surppressed + }, + { + note: "with: domain", + module: `package test + p if { + every x in input { print(x) } with input as [1] + }`, + notes: n("1"), + }, + { + note: "with: body", + module: `package test + p if { + every x in [1, 2] { print(x); print(input) } with input as "input" + }`, + notes: n("1", "input", "2", "input"), + }, + } + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + c := ast.NewCompiler().WithEnablePrintStatements(true) + mod := ast.MustParseModuleWithOpts(tc.module, ast.ParserOptions{AllFutureKeywords: true}) + if c.Compile(map[string]*ast.Module{"test": mod}); c.Failed() { + t.Fatal(c.Errors) + } + if testing.Verbose() { + t.Log(c.Modules) + } + buf := bytes.Buffer{} + tr := NewBufferTracer() + ph := NewPrintHook(&buf) + query := NewQuery(ast.MustParseBody("data.test.p = x")). + WithCompiler(c). + WithPrintHook(ph). + WithTracer(tr) + + res, err := query.Run(ctx) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if !tc.fail { + if len(res) == 0 { + t.Errorf("unexpected failure, empty query result set") + } + } else { + if len(res) > 0 { + t.Errorf("unexpected results: %v, expected empty query result set", res) + } + } + + notes := strings.Split(buf.String(), "\n") + notes = notes[:len(notes)-1] // last one is empty-string because each line ends in "\n" + if len(tc.notes) != 0 || len(tc.notes) == 0 && len(notes) != 0 { + if !slices.Equal(notes, tc.notes) { + t.Errorf("unexpected prints, expected %q, got %q", tc.notes, notes) + } + } + + if t.Failed() || testing.Verbose() { + PrettyTrace(os.Stderr, *tr) + } + }) + } +} + +type contextPropagationMock struct{} + +// contextPropagationStore will accumulate values from the contexts provided to +// read calls so that the test can verify that contexts are being propagated as +// expected. +type contextPropagationStore struct { + storage.WritesNotSupported + storage.TriggersNotSupported + storage.PolicyNotSupported + calls []any +} + +func (*contextPropagationStore) NewTransaction(context.Context, ...storage.TransactionParams) (storage.Transaction, error) { + return nil, nil +} + +func (*contextPropagationStore) Commit(context.Context, storage.Transaction) error { + return nil +} + +func (*contextPropagationStore) Abort(context.Context, storage.Transaction) { +} + +func (*contextPropagationStore) Truncate(context.Context, storage.Transaction, storage.TransactionParams, storage.Iterator) error { + return nil +} + +func (m *contextPropagationStore) Read(ctx context.Context, _ storage.Transaction, _ storage.Path) (any, error) { + val := ctx.Value(contextPropagationMock{}) + m.calls = append(m.calls, val) + return nil, nil +} + +func TestTopDownContextPropagation(t *testing.T) { + t.Parallel() + + ctx := context.WithValue(context.Background(), contextPropagationMock{}, "bar") + + compiler := ast.NewCompiler() + compiler.Compile(map[string]*ast.Module{ + "mod1": ast.MustParseModule(`package ex +import rego.v1 +p contains x if { data.a[i] = x }`, + ), + }) + + mockStore := &contextPropagationStore{} + txn := storage.NewTransactionOrDie(ctx, mockStore) + query := NewQuery(ast.MustParseBody("data.ex.p")). + WithCompiler(compiler). + WithStore(mockStore). + WithTransaction(txn) + + _, err := query.Run(ctx) + if err != nil { + t.Fatalf("Unexpected query error: %v", err) + } + + expectedCalls := []any{"bar"} + + if !reflect.DeepEqual(expectedCalls, mockStore.calls) { + t.Fatalf("Expected %v but got: %v", expectedCalls, mockStore.calls) + } +} + +// astStore returns a fixed ast.Value for Read. +type astStore struct { + storage.WritesNotSupported + storage.TriggersNotSupported + storage.PolicyNotSupported + path string + value ast.Value +} + +func (*astStore) NewTransaction(context.Context, ...storage.TransactionParams) (storage.Transaction, error) { + return nil, nil +} + +func (*astStore) Commit(context.Context, storage.Transaction) error { + return nil +} + +func (*astStore) Abort(context.Context, storage.Transaction) {} + +func (*astStore) Truncate(context.Context, storage.Transaction, storage.TransactionParams, storage.Iterator) error { + return nil +} + +func (a *astStore) Read(_ context.Context, _ storage.Transaction, path storage.Path) (any, error) { + if path.String() == a.path { + return a.value, nil + } + + return nil, &storage.Error{ + Code: storage.NotFoundErr, + Message: "not found", + } +} + +func TestTopdownStoreAST(t *testing.T) { + t.Parallel() + + body := ast.MustParseBody(`data.stored = x`) + ctx := context.Background() + compiler := ast.NewCompiler() + store := &astStore{path: "/stored", value: ast.String("value")} + + txn := storage.NewTransactionOrDie(ctx, store) + q := NewQuery(body).WithCompiler(compiler).WithStore(store).WithTransaction(txn) + qrs, err := q.Run(ctx) + + result := queryResultSetToTerm(qrs) + exp := ast.MustParseTerm(` + { + { + x: "value" + } + } + `) + + if err != nil || !result.Equal(exp) { + t.Fatalf("expected %v but got %v (error: %v)", exp, result, err) + } +} + +func TestTopdownLazyObj(t *testing.T) { + t.Parallel() + + body := ast.MustParseBody(`data.stored = x`) + ctx := context.Background() + compiler := ast.NewCompiler() + foo := map[string]any{ + "foo": "bar", + } + store := inmem.NewFromObject(map[string]any{ + "stored": foo, + }) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + q := NewQuery(body).WithCompiler(compiler).WithStore(store).WithTransaction(txn) + qrs, err := q.Run(ctx) + if err != nil { + t.Fatalf("expected no error got %v", err) + } + act, ok := qrs[0]["x"].Value.(ast.Object) + if !ok { + t.Errorf("expected obj, got %T: %[1]v", qrs[0]["x"].Value) + } + // NOTE(sr): we're using DeepEqual here because we want to assert that the structs + // match -- as far as the interface `ast.Object` is concerned `*lazyObj` and `*object` + // should be indistinguishable. + if exp := ast.LazyObject(foo); !reflect.DeepEqual(act, exp) { + t.Errorf("expected %T, got %T", exp, act) + } +} + +func TestTopdownLazyObjOptOut(t *testing.T) { + t.Parallel() + + body := ast.MustParseBody(`data.stored = x`) + ctx := context.Background() + compiler := ast.NewCompiler() + foo := map[string]any{ + "foo": "bar", + } + store := inmem.NewFromObject(map[string]any{ + "stored": foo, + }) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + q := NewQuery(body).WithCompiler(compiler).WithStore(store).WithTransaction(txn).WithStrictObjects(true) + qrs, err := q.Run(ctx) + if err != nil { + t.Fatalf("expected no error got %v", err) + } + act, ok := qrs[0]["x"].Value.(ast.Object) + if !ok { + t.Errorf("expected %T, got %T: %[2]v", act, qrs[0]["x"].Value) + } + // NOTE(sr): We can't type-assert *ast.lazyObj because it's not exported -- but we can retry + // the assertion that we've done in the not-opt-out case, and see that it no longer holds: + if exp := ast.LazyObject(foo); reflect.DeepEqual(act, exp) { + t.Errorf("expected %T, got %T", exp, act) + } + if exp := ast.NewObject(ast.Item(ast.StringTerm("foo"), ast.StringTerm("bar"))); exp.Compare(act) != 0 { + t.Errorf("expected %v to be equal to %v", exp, act) + } +} + +func compileModules(input []string) *ast.Compiler { + + mods := map[string]*ast.Module{} + + for idx, i := range input { + id := fmt.Sprintf("testMod%d", idx) + mods[id] = ast.MustParseModuleWithOpts(i, ast.ParserOptions{AllFutureKeywords: true}) + } + + c := ast.NewCompiler() + if c.Compile(mods); c.Failed() { + panic(c.Errors) + } + + return c +} + +func compileRules(imports []string, input []string, modules []string) (*ast.Compiler, error) { + + is := []*ast.Import{} + for _, i := range imports { + is = append(is, &ast.Import{ + Path: ast.MustParseTerm(i), + }) + } + + popts := ast.ParserOptions{AllFutureKeywords: true} + + m := &ast.Module{ + Package: ast.MustParsePackage("package generated"), + Imports: is, + } + + rules := []*ast.Rule{} + for i := range input { + rules = append(rules, ast.MustParseRuleWithOpts(input[i], popts)) + rules[i].Module = m + } + + m.Rules = rules + + for i := range rules { + rules[i].Module = m + } + + mods := map[string]*ast.Module{"testMod": m} + + for i, s := range modules { + mods[fmt.Sprintf("testMod%d", i)] = ast.MustParseModuleWithOpts(s, popts) + } + + c := ast.NewCompiler() + + if c.Compile(mods); c.Failed() { + return nil, c.Errors + } + + return c, nil +} + +// loadSmallTestData returns base documents that are referenced +// throughout the topdown test suite. +// +// Avoid the following top-level keys: i, j, k, p, q, r, v, x, y, z. +// These are used for rule names, local variables, etc. +func loadSmallTestData() map[string]any { + var data map[string]any + err := util.UnmarshalJSON([]byte(`{ + "a": [1,2,3,4], + "b": { + "v1": "hello", + "v2": "goodbye" + }, + "c": [{ + "x": [true, false, "foo"], + "y": [null, 3.14159], + "z": {"p": true, "q": false} + }], + "d": { + "e": ["bar", "baz"] + }, + "f": [ + {"xs": [1.0], "ys": [2.0]}, + {"xs": [2.0], "ys": [3.0]} + ], + "g": { + "a": [1, 0, 0, 0], + "b": [0, 2, 0, 0], + "c": [0, 0, 0, 4] + }, + "h": [ + [1,2,3], + [2,3,4] + ], + "l": [ + { + "a": "bob", + "b": -1, + "c": [1,2,3,4] + }, + { + "a": "alice", + "b": 1, + "c": [2,3,4,5], + "d": null + } + ], + "strings": { + "foo": 1, + "bar": 2, + "baz": 3 + }, + "three": 3, + "m": [], + "numbers": [ + "1", + "2", + "3", + "4" + ] + }`), &data) + if err != nil { + panic(err) + } + return data +} + +func setTime(t time.Time) func(*Query) *Query { + return func(q *Query) *Query { + return q.WithTime(t) + } +} + +func setInterQueryCache(c iCache.InterQueryCache) func(*Query) *Query { + return func(q *Query) *Query { + return q.WithInterQueryBuiltinCache(c) + } +} + +func setAllowNet(a []string) func(*Query) *Query { + return func(q *Query) *Query { + c := q.compiler.Capabilities() + c.AllowNet = a + return q.WithCompiler(q.compiler.WithCapabilities(c)) + } +} + +func setRoundTripper(t CustomizeRoundTripper) func(*Query) *Query { + return func(q *Query) *Query { + return q.WithHTTPRoundTripper(t) + } +} + +func runTopDownTestCase(t *testing.T, data map[string]any, note string, rules []string, expected any, options ...func(*Query) *Query) { + t.Helper() + + runTopDownTestCaseWithContext(context.Background(), t, data, note, rules, nil, "", expected, options...) +} + +func runTopDownTestCaseWithModules(t *testing.T, data map[string]any, note string, rules []string, modules []string, input string, expected any) { + t.Helper() + + runTopDownTestCaseWithContext(context.Background(), t, data, note, rules, modules, input, expected) +} + +func runTopDownTestCaseWithContext(ctx context.Context, t *testing.T, data map[string]any, note string, rules []string, modules []string, input string, expected any, + options ...func(*Query) *Query) { + t.Helper() + + imports := []string{} + for k := range data { + imports = append(imports, "data."+k) + } + + compiler, err := compileRules(imports, rules, modules) + if err != nil { + if _, ok := expected.(error); ok { + assertError(t, expected, err) + } else { + t.Errorf("%v: Compiler error: %v", note, err) + } + return + } + + store := inmem.NewFromObject(data) + + assertTopDownWithPathAndContext(ctx, t, compiler, store, note, []string{"generated", "p"}, input, expected, options...) +} + +func assertTopDownWithPathAndContext(ctx context.Context, t *testing.T, compiler *ast.Compiler, store storage.Store, note string, path []string, input string, expected any, + options ...func(*Query) *Query) { + t.Helper() + + var inputTerm *ast.Term + + if len(input) > 0 { + inputTerm = ast.MustParseTerm(input) + } + + txn := storage.NewTransactionOrDie(ctx, store) + + defer store.Abort(ctx, txn) + + var lhs *ast.Term + if len(path) == 0 { + lhs = ast.NewTerm(ast.DefaultRootRef) + } else { + lhs = ast.MustParseTerm("data." + strings.Join(path, ".")) + } + + rhs := ast.VarTerm(ast.WildcardPrefix + "result") + body := ast.NewBody(ast.Equality.Expr(lhs, rhs)) + + var requiresSort bool + + if rules := compiler.GetRulesExact(lhs.Value.(ast.Ref)); len(rules) > 0 && rules[0].Head.DocKind() == ast.PartialSetDoc { + requiresSort = true + } + + if os.Getenv("OPA_DUMP_TEST") != "" { + + data, err := store.Read(ctx, txn, storage.MustParsePath("/")) + if err != nil { + t.Fatal(err) + } + + dump(note, compiler.Modules, data, path, inputTerm, expected, requiresSort) + } + + // add an inter-query cache + config, _ := iCache.ParseCachingConfig(nil) + interQueryCache := iCache.NewInterQueryCache(config) + interQueryValueCache := iCache.NewInterQueryValueCache(ctx, config) + + var strictBuiltinErrors bool + + switch expected.(type) { + case *Error, error: + strictBuiltinErrors = true + } + + query := NewQuery(body). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithInput(inputTerm). + WithInterQueryBuiltinCache(interQueryCache). + WithInterQueryBuiltinValueCache(interQueryValueCache). + WithStrictBuiltinErrors(strictBuiltinErrors) + + var tracer BufferTracer + + if os.Getenv("OPA_TRACE_TEST") != "" { + query = query.WithTracer(&tracer) + } + + for _, opt := range options { + query = opt(query) + } + + t.Run(note, func(t *testing.T) { + t.Helper() + + switch e := expected.(type) { + case *Error, error: + _, err := query.Run(ctx) + assertError(t, expected, err) + case string: + qrs, err := query.Run(ctx) + + if tracer != nil { + PrettyTrace(os.Stdout, tracer) + } + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if len(e) == 0 { + if len(qrs) != 0 { + t.Fatalf("Expected undefined result but got: %v", qrs) + } + return + } + + if len(qrs) == 0 { + t.Fatalf("Expected %v but got undefined", e) + } + + result, err := ast.JSON(qrs[0][rhs.Value.(ast.Var)].Value) + if err != nil { + t.Fatal(err) + } + + expected := util.MustUnmarshalJSON([]byte(e)) + + if requiresSort { + sort.Sort(resultSet(result.([]any))) + if sl, ok := expected.([]any); ok { + sort.Sort(resultSet(sl)) + } + } + + if util.Compare(expected, result) != 0 { + t.Fatalf("Unexpected result:\nGot: %+v\nExp:\n%+v", result, expected) + } + + // If the test case involved the input document, re-run it with partial + // evaluation enabled and input marked as unknown. Then replay the query and + // verify the partial evaluation result is the same. Note, we cannot evaluate + // the result of a query against `data` because the queries need to be + // converted into rules (which would result in recursion.) + if len(path) > 0 { + runTopDownPartialTestCase(ctx, t, compiler, store, txn, inputTerm, rhs, body, requiresSort, expected, options...) + } + default: + t.Fatalf("Unexpected expected value type: %+v", e) + } + }) +} + +func runTopDownPartialTestCase(ctx context.Context, t *testing.T, compiler *ast.Compiler, store storage.Store, txn storage.Transaction, input *ast.Term, output *ast.Term, body ast.Body, requiresSort bool, expected any, + options ...func(*Query) *Query) { + t.Helper() + + // add an inter-query cache + config, _ := iCache.ParseCachingConfig(nil) + interQueryCache := iCache.NewInterQueryCache(config) + interQueryValueCache := iCache.NewInterQueryValueCache(ctx, config) + + partialQuery := NewQuery(body). + WithCompiler(compiler). + WithStore(store). + WithUnknowns([]*ast.Term{ast.MustParseTerm("input")}). + WithTransaction(txn). + WithInterQueryBuiltinCache(interQueryCache). + WithInterQueryBuiltinValueCache(interQueryValueCache) + + partials, support, err := partialQuery.PartialRun(ctx) + + if err != nil { + t.Fatal("Unexpected error on partial evaluation comparison:", err) + } + + module := ast.MustParseModule("package topdown_test_partial") + module.Rules = make([]*ast.Rule, len(partials)) + for i, body := range partials { + module.Rules[i] = &ast.Rule{ + Head: ast.NewHead(ast.Var("__result__"), nil, output), + Body: body, + Module: module, + } + } + + compiler.Modules["topdown_test_partial"] = module + for i, module := range support { + compiler.Modules[fmt.Sprintf("topdown_test_support_%d", i)] = module + } + + compiler.Compile(compiler.Modules) + if compiler.Failed() { + t.Fatal("Unexpected error on partial evaluation result compile:", compiler.Errors) + } + + query := NewQuery(ast.MustParseBody("data.topdown_test_partial.__result__ = x")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithInput(input). + WithInterQueryBuiltinCache(interQueryCache). + WithInterQueryBuiltinValueCache(interQueryValueCache) + + for _, opt := range options { + query = opt(query) + } + + qrs, err := query.Run(ctx) + if err != nil { + t.Fatal("Unexpected error on query after partial evaluation:", err) + } + + if len(qrs) == 0 { + t.Fatalf("Expected %v but got undefined from query after partial evaluation", expected) + } + + result, err := ast.JSON(qrs[0][ast.Var("x")].Value) + if err != nil { + t.Fatal(err) + } + + if requiresSort { + sort.Sort(resultSet(result.([]any))) + if sl, ok := expected.([]any); ok { + sort.Sort(resultSet(sl)) + } + } + + if util.Compare(expected, result) != 0 { + t.Fatalf("Unexpected result after partial evaluation:\nGot:\n%v\nExp:\n%v", result, expected) + } +} + +type resultSet []any + +func (rs resultSet) Less(i, j int) bool { + return util.Compare(rs[i], rs[j]) < 0 +} + +func (rs resultSet) Swap(i, j int) { + rs[i], rs[j] = rs[j], rs[i] +} + +func (rs resultSet) Len() int { + return len(rs) +} + +func init() { + + ast.RegisterBuiltin(&ast.Builtin{ + Name: "test.sleep", + Decl: types.NewFunction( + types.Args(types.S), + types.Nl, + ), + }) + + RegisterBuiltinFunc("test.sleep", func(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + d, _ := time.ParseDuration(string(operands[0].Value.(ast.String))) + time.Sleep(d) + return iter(ast.NullTerm()) + }) + +} + +var testID = 0 +var testIDMutex sync.Mutex + +func getTestNamespace() string { + programCounters := make([]uintptr, 20) + n := runtime.Callers(0, programCounters) + if n > 0 { + frames := runtime.CallersFrames(programCounters[:n]) + for more := true; more; { + var f runtime.Frame + f, more = frames.Next() + if after, ok := strings.CutPrefix(f.Function, "github.com/open-policy-agent/opa/topdown.Test"); ok { + return strings.TrimPrefix(strings.ToLower(strings.TrimPrefix(after, "TopDown")), "builtin") + } + } + } + return "" +} + +func dump(note string, modules map[string]*ast.Module, data any, docpath []string, input *ast.Term, exp any, requiresSort bool) { + + moduleSet := []string{} + for _, module := range modules { + moduleSet = append(moduleSet, string(bytes.ReplaceAll(format.MustAst(module), []byte("\t"), []byte(" ")))) + } + + namespace := getTestNamespace() + + test := map[string]any{ + "note": namespace + "/" + note, + "data": data, + "modules": moduleSet, + "query": strings.Join(append([]string{"data"}, docpath...), ".") + " = x", + } + + if input != nil { + test["input_term"] = input.String() + } + + switch e := exp.(type) { + case string: + rs := []map[string]any{} + if len(e) > 0 { + exp := util.MustUnmarshalJSON([]byte(e)) + if requiresSort { + sl := exp.([]any) + sort.Sort(resultSet(sl)) + } + rs = append(rs, map[string]any{"x": exp}) + } + test["want_result"] = rs + if requiresSort { + test["sort_bindings"] = true + } + case error: + test["want_error_code"] = e.(*Error).Code + test["want_error"] = e.(*Error).Message + default: + panic("Unexpected test expectation. Cowardly refusing to generate test cases.") + } + + bs, err := yaml.Marshal(map[string]any{"cases": []any{test}}) + if err != nil { + panic(err) + } + + dir := path.Join(os.Getenv("OPA_DUMP_TEST"), namespace) + + if err := os.MkdirAll(dir, 0755); err != nil { + panic(err) + } + + testIDMutex.Lock() + testID++ + c := testID + testIDMutex.Unlock() + + filename := fmt.Sprintf("test-%v-%04d.yaml", namespace, c) + + if err := os.WriteFile(filepath.Join(dir, filename), bs, 0644); err != nil { + panic(err) + } + +} + +func assertError(t *testing.T, expected any, actual error) { + t.Helper() + if actual == nil { + t.Errorf("Expected error but got: %v", actual) + return + } + + errString := actual.Error() + + if reflect.TypeOf(expected) != reflect.TypeOf(actual) { + t.Errorf("Expected error of type '%T', got '%T'", expected, actual) + } + + switch e := expected.(type) { + case Error: + assertErrorContains(t, errString, e.Code) + assertErrorContains(t, errString, e.Message) + case *Error: + assertErrorContains(t, errString, e.Code) + assertErrorContains(t, errString, e.Message) + case *ast.Error: + assertErrorContains(t, errString, e.Code) + assertErrorContains(t, errString, e.Message) + case ast.Errors: + for _, astErr := range e { + assertErrorContains(t, errString, astErr.Code) + assertErrorContains(t, errString, astErr.Message) + } + case error: + assertErrorContains(t, errString, e.Error()) + } +} + +func assertErrorContains(t *testing.T, actualErrMsg string, expected string) { + t.Helper() + if !strings.Contains(actualErrMsg, expected) { + t.Errorf("Expected error '%v' but got: '%v'", expected, actualErrMsg) + } +} diff --git a/third_party/opa/v1/topdown/trace.go b/third_party/opa/v1/topdown/trace.go new file mode 100644 index 000000000000..c9df12b4c547 --- /dev/null +++ b/third_party/opa/v1/topdown/trace.go @@ -0,0 +1,895 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "bytes" + "fmt" + "io" + "slices" + "strings" + + iStrs "github.com/open-policy-agent/opa/internal/strings" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +const ( + minLocationWidth = 5 // len("query") + maxIdealLocationWidth = 64 + columnPadding = 4 + maxExprVarWidth = 32 + maxPrettyExprVarWidth = 64 +) + +// Op defines the types of tracing events. +type Op string + +const ( + // EnterOp is emitted when a new query is about to be evaluated. + EnterOp Op = "Enter" + + // ExitOp is emitted when a query has evaluated to true. + ExitOp Op = "Exit" + + // EvalOp is emitted when an expression is about to be evaluated. + EvalOp Op = "Eval" + + // RedoOp is emitted when an expression, rule, or query is being re-evaluated. + RedoOp Op = "Redo" + + // SaveOp is emitted when an expression is saved instead of evaluated + // during partial evaluation. + SaveOp Op = "Save" + + // FailOp is emitted when an expression evaluates to false. + FailOp Op = "Fail" + + // DuplicateOp is emitted when a query has produced a duplicate value. The search + // will stop at the point where the duplicate was emitted and backtrack. + DuplicateOp Op = "Duplicate" + + // NoteOp is emitted when an expression invokes a tracing built-in function. + NoteOp Op = "Note" + + // IndexOp is emitted during an expression evaluation to represent lookup + // matches. + IndexOp Op = "Index" + + // WasmOp is emitted when resolving a ref using an external + // Resolver. + WasmOp Op = "Wasm" + + // 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" + FailedAssertionOp Op = "FailedAssertion" +) + +// VarMetadata provides some user facing information about +// a variable in some policy. +type VarMetadata struct { + Name ast.Var `json:"name"` + Location *ast.Location `json:"location"` +} + +// Event contains state associated with a tracing event. +type Event struct { + Op Op // Identifies type of event. + Node ast.Node // Contains AST node relevant to the event. + Location *ast.Location // The location of the Node this event relates to. + QueryID uint64 // Identifies the query this event belongs to. + ParentID uint64 // Identifies the parent query this event belongs to. + Locals *ast.ValueMap // Contains local variable bindings from the query context. Nil if variables were not included in the trace event. + LocalMetadata map[ast.Var]VarMetadata // Contains metadata for the local variable bindings. Nil if variables were not included in the trace event. + 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 + localVirtualCacheSnapshot *ast.ValueMap +} + +func (evt *Event) WithInput(input *ast.Term) *Event { + evt.input = input + return evt +} + +// HasRule returns true if the Event contains an ast.Rule. +func (evt *Event) HasRule() bool { + _, ok := evt.Node.(*ast.Rule) + return ok +} + +// HasBody returns true if the Event contains an ast.Body. +func (evt *Event) HasBody() bool { + _, ok := evt.Node.(ast.Body) + return ok +} + +// HasExpr returns true if the Event contains an ast.Expr. +func (evt *Event) HasExpr() bool { + _, ok := evt.Node.(*ast.Expr) + return ok +} + +// Equal returns true if this event is equal to the other event. +func (evt *Event) Equal(other *Event) bool { + if evt.Op != other.Op { + return false + } + if evt.QueryID != other.QueryID { + return false + } + if evt.ParentID != other.ParentID { + return false + } + if !evt.equalNodes(other) { + return false + } + return evt.Locals.Equal(other.Locals) +} + +func (evt *Event) String() string { + return fmt.Sprintf("%v %v %v (qid=%v, pqid=%v)", evt.Op, evt.Node, evt.Locals, evt.QueryID, evt.ParentID) +} + +// Input returns the input object as it was at the event. +func (evt *Event) Input() *ast.Term { + return evt.input +} + +// Plug plugs event bindings into the provided ast.Term. Because bindings are mutable, this only makes sense to do when +// the event is emitted rather than on recorded trace events as the bindings are going to be different by then. +func (evt *Event) Plug(term *ast.Term) *ast.Term { + return evt.bindings.Plug(term) +} + +func (evt *Event) equalNodes(other *Event) bool { + switch a := evt.Node.(type) { + case ast.Body: + if b, ok := other.Node.(ast.Body); ok { + return a.Equal(b) + } + case *ast.Rule: + if b, ok := other.Node.(*ast.Rule); ok { + return a.Equal(b) + } + case *ast.Expr: + if b, ok := other.Node.(*ast.Expr); ok { + return a.Equal(b) + } + case nil: + return other.Node == nil + } + return false +} + +// Tracer defines the interface for tracing in the top-down evaluation engine. +// Deprecated: Use QueryTracer instead. +type Tracer interface { + Enabled() bool + Trace(*Event) +} + +// QueryTracer defines the interface for tracing in the top-down evaluation engine. +// The implementation can provide additional configuration to modify the tracing +// behavior for query evaluations. +type QueryTracer interface { + Enabled() bool + TraceEvent(Event) + Config() TraceConfig +} + +// TraceConfig defines some common configuration for Tracer implementations +type TraceConfig struct { + PlugLocalVars bool // Indicate whether to plug local variable bindings before calling into the tracer. +} + +// legacyTracer Implements the QueryTracer interface by wrapping an older Tracer instance. +type legacyTracer struct { + t Tracer +} + +func (l *legacyTracer) Enabled() bool { + return l.t.Enabled() +} + +func (*legacyTracer) Config() TraceConfig { + return TraceConfig{ + PlugLocalVars: true, // For backwards compatibility old tracers will plug local variables + } +} + +func (l *legacyTracer) TraceEvent(evt Event) { + l.t.Trace(&evt) +} + +// WrapLegacyTracer will create a new QueryTracer which wraps an +// older Tracer instance. +func WrapLegacyTracer(tracer Tracer) QueryTracer { + return &legacyTracer{t: tracer} +} + +// BufferTracer implements the Tracer and QueryTracer interface by +// simply buffering all events received. +type BufferTracer []*Event + +// NewBufferTracer returns a new BufferTracer. +func NewBufferTracer() *BufferTracer { + return &BufferTracer{} +} + +// Enabled always returns true if the BufferTracer is instantiated. +func (b *BufferTracer) Enabled() bool { + return b != nil +} + +// Trace adds the event to the buffer. +// Deprecated: Use TraceEvent instead. +func (b *BufferTracer) Trace(evt *Event) { + *b = append(*b, evt) +} + +// TraceEvent adds the event to the buffer. +func (b *BufferTracer) TraceEvent(evt Event) { + *b = append(*b, &evt) +} + +// Config returns the Tracers standard configuration +func (*BufferTracer) Config() TraceConfig { + return TraceConfig{PlugLocalVars: true} +} + +// PrettyTrace pretty prints the trace to the writer. +func PrettyTrace(w io.Writer, trace []*Event) { + PrettyTraceWithOpts(w, trace, PrettyTraceOptions{}) +} + +// PrettyTraceWithLocation prints the trace to the writer and includes location information +func PrettyTraceWithLocation(w io.Writer, trace []*Event) { + PrettyTraceWithOpts(w, trace, PrettyTraceOptions{Locations: true}) +} + +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{} + + // FIXME: Can we shorten each location as we process each trace event instead of beforehand? + filePathAliases, _ := getShortenedFileNames(trace) + + table := traceTable{} + + for _, event := range trace { + depth := depths.GetOrSet(event.QueryID, event.ParentID) + row := traceRow{} + + if opts.Locations { + location := formatLocation(event, filePathAliases) + row.add(location) + } + + row.add(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 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 { + padding := formatEventPadding(event, depth) + if event.Op == NoteOp { + return fmt.Sprintf("%v%v %q", padding, event.Op, event.Message) + } + + var details any + if node, ok := event.Node.(*ast.Rule); ok { + details = node.Path() + } else if event.Ref != nil { + details = event.Ref + } else { + details = rewrite(event).Node + } + + template := "%v%v %v" + opts := []any{padding, event.Op, details} + + if event.Message != "" { + template += " %v" + opts = append(opts, event.Message) + } + + return fmt.Sprintf(template, opts...) +} + +func formatEventPadding(event *Event, depth int) string { + spaces := formatEventSpaces(event, depth) + if spaces > 1 { + return strings.Repeat("| ", spaces-1) + } + return "" +} + +func formatEventSpaces(event *Event, depth int) int { + switch event.Op { + case EnterOp: + return depth + case RedoOp: + if _, ok := event.Node.(*ast.Expr); !ok { + return depth + } + } + return depth + 1 +} + +// getShortenedFileNames will return a map of file paths to shortened aliases +// that were found in the trace. It also returns the longest location expected +func getShortenedFileNames(trace []*Event) (map[string]string, int) { + // Get a deduplicated list of all file paths + // and the longest file path size + fpAliases := map[string]string{} + var canShorten []string + longestLocation := 0 + for _, event := range trace { + if event.Location != nil { + if event.Location.File != "" { + // length of ":" + curLen := len(event.Location.File) + numDigits10(event.Location.Row) + 1 + if curLen > longestLocation { + longestLocation = curLen + } + + if _, ok := fpAliases[event.Location.File]; ok { + continue + } + + canShorten = append(canShorten, event.Location.File) + + // Default to just alias their full path + fpAliases[event.Location.File] = event.Location.File + } else { + // length of ":" + curLen := minLocationWidth + numDigits10(event.Location.Row) + 1 + if curLen > longestLocation { + longestLocation = curLen + } + } + } + } + + if len(canShorten) > 0 && longestLocation > maxIdealLocationWidth { + fpAliases, longestLocation = iStrs.TruncateFilePaths(maxIdealLocationWidth, longestLocation, canShorten...) + } + + return fpAliases, longestLocation +} + +func numDigits10(n int) int { + if n < 10 { + return 1 + } + return numDigits10(n/10) + 1 +} + +func formatLocation(event *Event, fileAliases map[string]string) string { + + location := event.Location + if location == nil { + return "" + } + + if location.File == "" { + return fmt.Sprintf("query:%v", location.Row) + } + + return fmt.Sprintf("%v:%v", fileAliases[location.File], location.Row) +} + +// depths is a helper for computing the depth of an event. Events within the +// same query all have the same depth. The depth of query is +// depth(parent(query))+1. +type depths map[uint64]int + +func (ds depths) GetOrSet(qid uint64, pqid uint64) int { + depth := ds[qid] + if depth == 0 { + depth = ds[pqid] + depth++ + ds[qid] = depth + } + return depth +} + +func builtinTrace(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return handleBuiltinErr(ast.Trace.Name, bctx.Location, err) + } + + if !bctx.TraceEnabled { + return iter(ast.InternedTerm(true)) + } + + evt := Event{ + Op: NoteOp, + Location: bctx.Location, + QueryID: bctx.QueryID, + ParentID: bctx.ParentID, + Message: string(str), + } + + for i := range bctx.QueryTracers { + bctx.QueryTracers[i].TraceEvent(evt) + } + + return iter(ast.InternedTerm(true)) +} + +func rewrite(event *Event) *Event { + + cpy := *event + + var node ast.Node + + switch v := event.Node.(type) { + case *ast.Expr: + expr := v.Copy() + + // Hide generated local vars in 'key' position that have not been + // rewritten. + if ev, ok := v.Terms.(*ast.Every); ok { + if kv, ok := ev.Key.Value.(ast.Var); ok { + if rw, ok := cpy.LocalMetadata[kv]; !ok || rw.Name.IsGenerated() { + expr.Terms.(*ast.Every).Key = nil + } + } + } + node = expr + case ast.Body: + node = v.Copy() + case *ast.Rule: + node = v.Copy() + } + + _, _ = ast.TransformVars(node, func(v ast.Var) (ast.Value, error) { + if meta, ok := cpy.LocalMetadata[v]; ok { + return meta.Name, nil + } + return v, nil + }) + + cpy.Node = node + + 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 any, f func(*ast.Term) bool) { + var vis *ast.GenericVisitor + vis = ast.NewGenericVisitor(func(x any) 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]any) + 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 := range spaces { + tab := false + if slices.Contains(info.exprLoc.Tabs, j+prevCol+1) { + w.WriteString("\t") + tab = true + } + 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/third_party/opa/v1/topdown/trace_test.go b/third_party/opa/v1/topdown/trace_test.go new file mode 100644 index 000000000000..ce513530b0c3 --- /dev/null +++ b/third_party/opa/v1/topdown/trace_test.go @@ -0,0 +1,1515 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "bytes" + "context" + "fmt" + "maps" + "reflect" + "strings" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/storage" + inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test" + "github.com/open-policy-agent/opa/v1/util" +) + +func TestEventEqual(t *testing.T) { + t.Parallel() + + a := ast.NewValueMap() + a.Put(ast.String("foo"), ast.Number("1")) + b := ast.NewValueMap() + b.Put(ast.String("foo"), ast.Number("2")) + + tests := []struct { + a *Event + b *Event + equal bool + }{ + {&Event{}, &Event{}, true}, + {&Event{Op: EvalOp}, &Event{Op: EnterOp}, false}, + {&Event{QueryID: 1}, &Event{QueryID: 2}, false}, + {&Event{ParentID: 1}, &Event{ParentID: 2}, false}, + {&Event{Node: ast.MustParseBody("true")}, &Event{Node: ast.MustParseBody("false")}, false}, + {&Event{Node: ast.MustParseBody("true")[0]}, &Event{Node: ast.MustParseBody("false")[0]}, false}, + {&Event{Node: ast.MustParseRule(`p = true { true }`)}, &Event{Node: ast.MustParseRule(`p = true { false }`)}, false}, + } + + for _, tc := range tests { + if tc.a.Equal(tc.b) != tc.equal { + var s string + if tc.equal { + s = "==" + } else { + s = "!=" + } + t.Errorf("Expected %v %v %v", tc.a, s, tc.b) + } + } + +} + +func TestPrettyTrace(t *testing.T) { + t.Parallel() + + module := `package test + + p if { q[x]; plus(x, 1, n) } + q contains x if { x = data.a[_] }` + + ctx := context.Background() + compiler := compileModules([]string{module}) + data := loadSmallTestData() + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + tracer := NewBufferTracer() + query := NewQuery(ast.MustParseBody("data.test.p = _")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer(tracer) + + _, err := query.Run(ctx) + if err != nil { + panic(err) + } + + expected := `Enter data.test.p = _ +| Eval data.test.p = _ +| Index data.test.p (matched 1 rule, early exit) +| Enter data.test.p +| | Eval data.test.q[x] +| | Index data.test.q (matched 1 rule) +| | Enter data.test.q +| | | Eval x = data.a[_] +| | | Exit data.test.q +| | Redo data.test.q +| | | Redo x = data.a[_] +| | | Exit data.test.q +| | Redo data.test.q +| | | Redo x = data.a[_] +| | | Exit data.test.q +| | Redo data.test.q +| | | Redo x = data.a[_] +| | | Exit data.test.q +| | Redo data.test.q +| | | Redo x = data.a[_] +| | Eval plus(x, 1, n) +| | Exit data.test.p early +| Exit data.test.p = _ +Redo data.test.p = _ +| Redo data.test.p = _ +| Redo data.test.p +| | Redo plus(x, 1, n) +| | Redo data.test.q[x] +` + + var buf bytes.Buffer + PrettyTrace(&buf, removeUnifyOps(*tracer)) + compareBuffers(t, expected, buf.String()) +} + +func TestPrettyTraceWithLocation(t *testing.T) { + t.Parallel() + + module := `package test + + p if { q[x]; plus(x, 1, n) } + q contains x if { x = data.a[_] }` + + ctx := context.Background() + compiler := compileModules([]string{module}) + data := loadSmallTestData() + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + tracer := NewBufferTracer() + query := NewQuery(ast.MustParseBody("data.test.p = _")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer(tracer) + + _, err := query.Run(ctx) + if err != nil { + panic(err) + } + + expected := `query:1 Enter data.test.p = _ +query:1 | Eval data.test.p = _ +query:1 | Index data.test.p (matched 1 rule, early exit) +query:3 | Enter data.test.p +query:3 | | Eval data.test.q[x] +query:3 | | Index data.test.q (matched 1 rule) +query:4 | | Enter data.test.q +query:4 | | | Eval x = data.a[_] +query:4 | | | Exit data.test.q +query:4 | | Redo data.test.q +query:4 | | | Redo x = data.a[_] +query:4 | | | Exit data.test.q +query:4 | | Redo data.test.q +query:4 | | | Redo x = data.a[_] +query:4 | | | Exit data.test.q +query:4 | | Redo data.test.q +query:4 | | | Redo x = data.a[_] +query:4 | | | Exit data.test.q +query:4 | | Redo data.test.q +query:4 | | | Redo x = data.a[_] +query:3 | | Eval plus(x, 1, n) +query:3 | | Exit data.test.p early +query:1 | Exit data.test.p = _ +query:1 Redo data.test.p = _ +query:1 | Redo data.test.p = _ +query:3 | Redo data.test.p +query:3 | | Redo plus(x, 1, n) +query:3 | | Redo data.test.q[x] +` + + var buf bytes.Buffer + PrettyTraceWithLocation(&buf, removeUnifyOps(*tracer)) + compareBuffers(t, expected, buf.String()) +} + +func TestPrettyTraceWithLocationTruncatedPaths(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + compiler := ast.MustCompileModulesWithOpts(map[string]string{ + "authz_bundle/com/foo/bar/baz/qux/acme/corp/internal/authz/policies/abac/v1/beta/policy.rego": `package test + + import data.utils.q + + p = true if { q[x]; plus(x, 1, n) } + `, + "authz_bundle/com/foo/bar/baz/qux/acme/corp/internal/authz/policies/utils/utils.rego": `package utils + + q contains x if { x = data.a[_] } + `, + }, ast.CompileOpts{ + ParserOptions: ast.ParserOptions{ + AllFutureKeywords: true, + }, + }) + data := loadSmallTestData() + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + tracer := NewBufferTracer() + query := NewQuery(ast.MustParseBody("data.test.p = _")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer(tracer) + + _, err := query.Run(ctx) + if err != nil { + panic(err) + } + + expected := `query:1 Enter data.test.p = _ +query:1 | Eval data.test.p = _ +query:1 | Index data.test.p (matched 1 rule, early exit) +authz_bundle/...ternal/authz/policies/abac/v1/beta/policy.rego:5 | Enter data.test.p +authz_bundle/...ternal/authz/policies/abac/v1/beta/policy.rego:5 | | Eval data.utils.q[x] +authz_bundle/...ternal/authz/policies/abac/v1/beta/policy.rego:5 | | Index data.utils.q (matched 1 rule) +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | Enter data.utils.q +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | | Eval x = data.a[_] +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | | Exit data.utils.q +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | Redo data.utils.q +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | | Redo x = data.a[_] +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | | Exit data.utils.q +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | Redo data.utils.q +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | | Redo x = data.a[_] +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | | Exit data.utils.q +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | Redo data.utils.q +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | | Redo x = data.a[_] +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | | Exit data.utils.q +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | Redo data.utils.q +authz_bundle/...ternal/authz/policies/utils/utils.rego:3 | | | Redo x = data.a[_] +authz_bundle/...ternal/authz/policies/abac/v1/beta/policy.rego:5 | | Eval plus(x, 1, n) +authz_bundle/...ternal/authz/policies/abac/v1/beta/policy.rego:5 | | Exit data.test.p early +query:1 | Exit data.test.p = _ +query:1 Redo data.test.p = _ +query:1 | Redo data.test.p = _ +authz_bundle/...ternal/authz/policies/abac/v1/beta/policy.rego:5 | Redo data.test.p +authz_bundle/...ternal/authz/policies/abac/v1/beta/policy.rego:5 | | Redo plus(x, 1, n) +authz_bundle/...ternal/authz/policies/abac/v1/beta/policy.rego:5 | | Redo data.utils.q[x] +` + + var buf bytes.Buffer + PrettyTraceWithLocation(&buf, removeUnifyOps(*tracer)) + compareBuffers(t, expected, buf.String()) +} + +func TestPrettyTracePartialWithLocationTruncatedPaths(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + compiler := ast.MustCompileModulesWithOpts(map[string]string{ + "authz_bundle/com/foo/bar/baz/qux/acme/corp/internal/authz/policies/rbac/v1/beta/policy.rego": ` + package example_rbac + + default allow = false + + allow if { + data.utils.user_has_role[role_name] + + data.utils.role_has_permission[role_name] + } + + `, + "authz_bundle/com/foo/bar/baz/qux/acme/corp/internal/authz/policies/utils/user.rego": ` + package utils + + user_has_role contains role_name if { + role_binding = data.bindings[_] + role_binding.role = role_name + role_binding.user = input.subject.user + } + + role_has_permission contains role_name if { + role = data.roles[_] + role.name = role_name + role.operation = input.action.operation + role.resource = input.action.resource + } + `, + }, ast.CompileOpts{ + ParserOptions: ast.ParserOptions{ + AllFutureKeywords: true, + }, + }) + + var data map[string]any + err := util.UnmarshalJSON([]byte(`{ + "roles": [ + { + "operation": "read", + "resource": "widgets", + "name": "widget-reader" + }, + { + "operation": "write", + "resource": "widgets", + "name": "widget-writer" + } + ], + "bindings": [ + { + "user": "inspector-alice", + "role": "widget-reader" + }, + { + "user": "maker-bob", + "role": "widget-writer" + } + ] + }`), &data) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + tracer := NewBufferTracer() + query := NewQuery(ast.MustParseBody("data.example_rbac.allow")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithUnknowns([]*ast.Term{ast.MustParseTerm("input")}). + WithTracer(tracer) + + _, _, err = query.PartialRun(ctx) + if err != nil { + panic(err) + } + + expected := `query:1 Enter data.example_rbac.allow +query:1 | Eval data.example_rbac.allow +query:1 | Index data.example_rbac.allow (matched 1 rule, early exit) +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:6 | Enter data.example_rbac.allow +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:7 | | Eval data.utils.user_has_role[role_name] +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:7 | | Index data.utils.user_has_role (matched 1 rule) +authz_bundle/...ternal/authz/policies/utils/user.rego:4 | | Enter data.utils.user_has_role +authz_bundle/...ternal/authz/policies/utils/user.rego:5 | | | Eval role_binding = data.bindings[_] +authz_bundle/...ternal/authz/policies/utils/user.rego:6 | | | Eval role_binding.role = role_name +authz_bundle/...ternal/authz/policies/utils/user.rego:7 | | | Eval role_binding.user = input.subject.user +authz_bundle/...ternal/authz/policies/utils/user.rego:7 | | | Save "inspector-alice" = input.subject.user +authz_bundle/...ternal/authz/policies/utils/user.rego:4 | | | Exit data.utils.user_has_role +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:9 | | Eval data.utils.role_has_permission[role_name] +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:9 | | Index data.utils.role_has_permission (matched 1 rule) +authz_bundle/...ternal/authz/policies/utils/user.rego:10 | | Enter data.utils.role_has_permission +authz_bundle/...ternal/authz/policies/utils/user.rego:11 | | | Eval role = data.roles[_] +authz_bundle/...ternal/authz/policies/utils/user.rego:12 | | | Eval role.name = role_name +authz_bundle/...ternal/authz/policies/utils/user.rego:13 | | | Eval role.operation = input.action.operation +authz_bundle/...ternal/authz/policies/utils/user.rego:13 | | | Save "read" = input.action.operation +authz_bundle/...ternal/authz/policies/utils/user.rego:14 | | | Eval role.resource = input.action.resource +authz_bundle/...ternal/authz/policies/utils/user.rego:14 | | | Save "widgets" = input.action.resource +authz_bundle/...ternal/authz/policies/utils/user.rego:10 | | | Exit data.utils.role_has_permission +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:6 | | Exit data.example_rbac.allow +query:1 | Exit data.example_rbac.allow +query:1 Redo data.example_rbac.allow +query:1 | Redo data.example_rbac.allow +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:6 | Redo data.example_rbac.allow +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:9 | | Redo data.utils.role_has_permission[role_name] +authz_bundle/...ternal/authz/policies/utils/user.rego:10 | | Redo data.utils.role_has_permission +authz_bundle/...ternal/authz/policies/utils/user.rego:14 | | | Redo role.resource = input.action.resource +authz_bundle/...ternal/authz/policies/utils/user.rego:13 | | | Redo role.operation = input.action.operation +authz_bundle/...ternal/authz/policies/utils/user.rego:12 | | | Redo role.name = role_name +authz_bundle/...ternal/authz/policies/utils/user.rego:11 | | | Redo role = data.roles[_] +authz_bundle/...ternal/authz/policies/utils/user.rego:12 | | | Eval role.name = role_name +authz_bundle/...ternal/authz/policies/utils/user.rego:12 | | | Fail role.name = role_name +authz_bundle/...ternal/authz/policies/utils/user.rego:11 | | | Redo role = data.roles[_] +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:7 | | Redo data.utils.user_has_role[role_name] +authz_bundle/...ternal/authz/policies/utils/user.rego:4 | | Redo data.utils.user_has_role +authz_bundle/...ternal/authz/policies/utils/user.rego:7 | | | Redo role_binding.user = input.subject.user +authz_bundle/...ternal/authz/policies/utils/user.rego:6 | | | Redo role_binding.role = role_name +authz_bundle/...ternal/authz/policies/utils/user.rego:5 | | | Redo role_binding = data.bindings[_] +authz_bundle/...ternal/authz/policies/utils/user.rego:6 | | | Eval role_binding.role = role_name +authz_bundle/...ternal/authz/policies/utils/user.rego:7 | | | Eval role_binding.user = input.subject.user +authz_bundle/...ternal/authz/policies/utils/user.rego:7 | | | Save "maker-bob" = input.subject.user +authz_bundle/...ternal/authz/policies/utils/user.rego:4 | | | Exit data.utils.user_has_role +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:9 | | Eval data.utils.role_has_permission[role_name] +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:9 | | Index data.utils.role_has_permission (matched 1 rule) +authz_bundle/...ternal/authz/policies/utils/user.rego:10 | | Enter data.utils.role_has_permission +authz_bundle/...ternal/authz/policies/utils/user.rego:11 | | | Eval role = data.roles[_] +authz_bundle/...ternal/authz/policies/utils/user.rego:12 | | | Eval role.name = role_name +authz_bundle/...ternal/authz/policies/utils/user.rego:12 | | | Fail role.name = role_name +authz_bundle/...ternal/authz/policies/utils/user.rego:11 | | | Redo role = data.roles[_] +authz_bundle/...ternal/authz/policies/utils/user.rego:12 | | | Eval role.name = role_name +authz_bundle/...ternal/authz/policies/utils/user.rego:13 | | | Eval role.operation = input.action.operation +authz_bundle/...ternal/authz/policies/utils/user.rego:13 | | | Save "write" = input.action.operation +authz_bundle/...ternal/authz/policies/utils/user.rego:14 | | | Eval role.resource = input.action.resource +authz_bundle/...ternal/authz/policies/utils/user.rego:14 | | | Save "widgets" = input.action.resource +authz_bundle/...ternal/authz/policies/utils/user.rego:10 | | | Exit data.utils.role_has_permission +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:6 | | Exit data.example_rbac.allow +query:1 | Exit data.example_rbac.allow +query:1 Redo data.example_rbac.allow +query:1 | Redo data.example_rbac.allow +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:6 | Redo data.example_rbac.allow +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:9 | | Redo data.utils.role_has_permission[role_name] +authz_bundle/...ternal/authz/policies/utils/user.rego:10 | | Redo data.utils.role_has_permission +authz_bundle/...ternal/authz/policies/utils/user.rego:14 | | | Redo role.resource = input.action.resource +authz_bundle/...ternal/authz/policies/utils/user.rego:13 | | | Redo role.operation = input.action.operation +authz_bundle/...ternal/authz/policies/utils/user.rego:12 | | | Redo role.name = role_name +authz_bundle/...ternal/authz/policies/utils/user.rego:11 | | | Redo role = data.roles[_] +authz_bundle/...ternal/authz/policies/rbac/v1/beta/policy.rego:7 | | Redo data.utils.user_has_role[role_name] +authz_bundle/...ternal/authz/policies/utils/user.rego:4 | | Redo data.utils.user_has_role +authz_bundle/...ternal/authz/policies/utils/user.rego:7 | | | Redo role_binding.user = input.subject.user +authz_bundle/...ternal/authz/policies/utils/user.rego:6 | | | Redo role_binding.role = role_name +authz_bundle/...ternal/authz/policies/utils/user.rego:5 | | | Redo role_binding = data.bindings[_] +` + + var buf bytes.Buffer + PrettyTraceWithLocation(&buf, removeUnifyOps(*tracer)) + compareBuffers(t, expected, buf.String()) +} + +func TestTraceDuplicate(t *testing.T) { + t.Parallel() + + // NOTE(sr): We're explicitly bypassing a caching optimization here: + // When the first query for a partial is `p[x]`, and `x` is not ground, + // we'll have the evaluation eval the full extent of the partial and + // cache that. Thus the second `p[1]` here will not trigger a duplicate + // event, because the query eval uses a different code path. + // Having `p[1]` queried first will side-step the caching optimization. + module := `package test + + p contains 1 + p contains 2 + p contains 1 + ` + + ctx := context.Background() + compiler := compileModules([]string{module}) + data := loadSmallTestData() + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + tracer := NewBufferTracer() + query := NewQuery(ast.MustParseBody("data.test.p[1]; data.test.p[x] = _")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer(tracer) + + _, err := query.Run(ctx) + if err != nil { + panic(err) + } + + n := 0 + for _, event := range *tracer { + if event.Op == DuplicateOp { + n++ + } + } + + if n != 1 { + t.Fatalf("Expected one duplicate event but got %v", n) + } +} + +func TestTraceNote(t *testing.T) { + t.Parallel() + + module := `package test + + p if { q[x]; plus(x, 1, n); trace(sprintf("n=%v", [n])) } + q contains x if { x = data.a[_] }` + + ctx := context.Background() + compiler := compileModules([]string{module}) + data := loadSmallTestData() + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + tracer := NewBufferTracer() + query := NewQuery(ast.MustParseBody("data.test.p = _")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer(tracer) + + _, err := query.Run(ctx) + if err != nil { + panic(err) + } + + expected := `Enter data.test.p = _ +| Eval data.test.p = _ +| Index data.test.p (matched 1 rule, early exit) +| Enter data.test.p +| | Eval data.test.q[x] +| | Index data.test.q (matched 1 rule) +| | Enter data.test.q +| | | Eval x = data.a[_] +| | | Exit data.test.q +| | Redo data.test.q +| | | Redo x = data.a[_] +| | | Exit data.test.q +| | Redo data.test.q +| | | Redo x = data.a[_] +| | | Exit data.test.q +| | Redo data.test.q +| | | Redo x = data.a[_] +| | | Exit data.test.q +| | Redo data.test.q +| | | Redo x = data.a[_] +| | Eval plus(x, 1, n) +| | Eval sprintf("n=%v", [n], __local0__) +| | Eval trace(__local0__) +| | Note "n=2" +| | Exit data.test.p early +| Exit data.test.p = _ +Redo data.test.p = _ +| Redo data.test.p = _ +| Redo data.test.p +| | Redo trace(__local0__) +| | Redo sprintf("n=%v", [n], __local0__) +| | Redo plus(x, 1, n) +| | Redo data.test.q[x] +` + + var buf bytes.Buffer + PrettyTrace(&buf, removeUnifyOps(*tracer)) + compareBuffers(t, expected, buf.String()) +} + +func TestTraceNoteWithLocation(t *testing.T) { + t.Parallel() + + module := `package test + + p if { q[x]; plus(x, 1, n); trace(sprintf("n=%v", [n])) } + q contains x if { x = data.a[_] }` + + ctx := context.Background() + compiler := compileModules([]string{module}) + data := loadSmallTestData() + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + tracer := NewBufferTracer() + query := NewQuery(ast.MustParseBody("data.test.p = _")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer(tracer) + + _, err := query.Run(ctx) + if err != nil { + panic(err) + } + + expected := `query:1 Enter data.test.p = _ +query:1 | Eval data.test.p = _ +query:1 | Index data.test.p (matched 1 rule, early exit) +query:3 | Enter data.test.p +query:3 | | Eval data.test.q[x] +query:3 | | Index data.test.q (matched 1 rule) +query:4 | | Enter data.test.q +query:4 | | | Eval x = data.a[_] +query:4 | | | Exit data.test.q +query:4 | | Redo data.test.q +query:4 | | | Redo x = data.a[_] +query:4 | | | Exit data.test.q +query:4 | | Redo data.test.q +query:4 | | | Redo x = data.a[_] +query:4 | | | Exit data.test.q +query:4 | | Redo data.test.q +query:4 | | | Redo x = data.a[_] +query:4 | | | Exit data.test.q +query:4 | | Redo data.test.q +query:4 | | | Redo x = data.a[_] +query:3 | | Eval plus(x, 1, n) +query:3 | | Eval sprintf("n=%v", [n], __local0__) +query:3 | | Eval trace(__local0__) +query:3 | | Note "n=2" +query:3 | | Exit data.test.p early +query:1 | Exit data.test.p = _ +query:1 Redo data.test.p = _ +query:1 | Redo data.test.p = _ +query:3 | Redo data.test.p +query:3 | | Redo trace(__local0__) +query:3 | | Redo sprintf("n=%v", [n], __local0__) +query:3 | | Redo plus(x, 1, n) +query:3 | | Redo data.test.q[x] +` + + var buf bytes.Buffer + PrettyTraceWithLocation(&buf, removeUnifyOps(*tracer)) + compareBuffers(t, expected, buf.String()) +} + +func TestMultipleTracers(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + buf1 := NewBufferTracer() + buf2 := NewBufferTracer() + q := NewQuery(ast.MustParseBody("a = 1")). + WithTracer(buf1). + WithTracer(buf2) + + _, err := q.Run(ctx) + if err != nil { + t.Fatal(err) + } + + if len(*buf1) != len(*buf2) { + t.Fatalf("Expected buffer lengths to be equal but got: %d and %d", len(*buf1), len(*buf2)) + } + + for i := range *buf1 { + if !(*buf1)[i].Equal((*buf2)[i]) { + t.Fatalf("Expected all events to be equal but at index %d got %v and %v", i, (*buf1)[i], (*buf2)[i]) + } + } + +} + +func TestTraceRewrittenQueryVars(t *testing.T) { + t.Parallel() + + module := `package test + + y = [1, 2, 3]` + + ctx := context.Background() + compiler := compileModules([]string{module}) + queryCompiler := compiler.QueryCompiler() + data := loadSmallTestData() + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + compiledQuery, err := queryCompiler.Compile(ast.MustParseBody("z := {a | a := data.y[_]}")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + tracer := NewBufferTracer() + query := NewQuery(compiledQuery). + WithQueryCompiler(queryCompiler). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer(tracer) + + _, err = query.Run(ctx) + if err != nil { + panic(err) + } + + foundQueryVar := false + + for _, event := range *tracer { + if event.LocalMetadata != nil { + name, ok := event.LocalMetadata["__localq1__"] + if ok && name.Name == "z" { + foundQueryVar = true + break + } + } + } + + if !foundQueryVar { + t.Error("Expected to find trace with rewritten var 'z' -> '__localq__") + } + + // Rewrite the vars in the first event (which is a query) and verify that + // that vars have been mapped to user-provided names. + cpy := rewrite((*tracer)[0]) + node := cpy.Node.(ast.Body) + exp := ast.MustParseBody("z = {a | a = data.y[_]}") + + if !node.Equal(exp) { + t.Errorf("Expected %v but got %v", exp, node) + } +} + +func TestTraceRewrittenVars(t *testing.T) { + t.Parallel() + + mustParse := func(s string) *ast.Expr { + return ast.MustParseBodyWithOpts(s, ast.ParserOptions{FutureKeywords: []string{"every"}})[0] + } + everyCheck := func(stmt string) func(*testing.T, *Event, *Event) { + return func(t *testing.T, _ *Event, output *Event) { + exp := mustParse(stmt) + if !exp.Equal(output.Node.(*ast.Expr)) { + t.Errorf("expected %v to equal %v", output, exp) + } + } + } + + tests := []struct { + note string + evt *Event + exp func(*testing.T, *Event, *Event) + }{ + { + note: "issue 2022", + evt: &Event{ + Node: ast.NewExpr(ast.VarTerm("foo")), + LocalMetadata: map[ast.Var]VarMetadata{ + ast.Var("foo"): {Name: ast.Var("bar")}, + }, + }, + exp: func(t *testing.T, input *Event, output *Event) { + if input.Node == output.Node { + t.Fatal("expected node to have been copied") + } else if !output.Node.(*ast.Expr).Equal(ast.NewExpr(ast.VarTerm("bar"))) { + t.Fatal("expected copy to contain rewritten var") + } + }, + }, + { + note: "every: key/val rewritten", + evt: &Event{ + Node: mustParse(`every __local0__, __local1__ in __local2__ { __local1__ == __local0__ }`), + LocalMetadata: map[ast.Var]VarMetadata{ + ast.Var("__local0__"): {Name: ast.Var("k")}, + ast.Var("__local1__"): {Name: ast.Var("v")}, + }, + }, + exp: everyCheck(`every k, v in __local2__ { v == k }`), + }, + { + note: "every: key hidden if not rewritten", + evt: &Event{ + Node: mustParse(`every __local0__, __local1__ in __local2__ { __local1__ == 1 }`), + LocalMetadata: map[ast.Var]VarMetadata{ + ast.Var("__local1__"): {Name: ast.Var("v")}, + }, + }, + exp: everyCheck(`every v in __local2__ { v == 1 }`), + }, + { + note: "every: key hidden if rewritten to generated key", // NOTE(sr): this would happen for traceRedo + evt: &Event{ + Node: mustParse(`every __local0__, __local1__ in __local2__ { __local1__ == 1 }`), + LocalMetadata: map[ast.Var]VarMetadata{ + ast.Var("__local1__"): {Name: ast.Var("v")}, + ast.Var("__local0__"): {Name: ast.Var("__local0__")}, + }, + }, + exp: everyCheck(`every v in __local2__ { v == 1 }`), + }, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + output := rewrite(tc.evt) + tc.exp(t, tc.evt, output) + }) + } +} + +func TestTraceEveryEvaluation(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + events := func(es ...string) []string { + return es + } + + // NOTE(sr): String() on an *Event isn't stable, because iterating the underlying ast.ValueMap isn't. + // So we're stubbing out all captured events' value maps to be able to compare these as strings. + tests := []struct { + note string + query string + module string + exp []string // these need to be found, extra events captured are ignored + }{ + { + note: "empty domain", + query: "data.test.p = x", + module: `package test + p if { every k, v in [] { k != v } }`, + exp: events( + `Enter every __local0__, __local1__ in __local2__ { neq(__local0__, __local1__) } {} (qid=2, pqid=1)`, + `Exit every __local0__, __local1__ in __local2__ { neq(__local0__, __local1__) } {} (qid=2, pqid=1)`, + ), + }, + { + note: "successful eval", + query: "data.test.p = x", + module: `package test + p if { every k, v in [1] { k != v } }`, + exp: events( + `Enter every __local0__, __local1__ in __local2__ { neq(__local0__, __local1__) } {} (qid=2, pqid=1)`, + `Enter neq(__local0__, __local1__) {} (qid=3, pqid=2)`, + `Exit neq(__local0__, __local1__) {} (qid=3, pqid=2)`, + `Redo every __local0__, __local1__ in __local2__ { neq(__local0__, __local1__) } {} (qid=2, pqid=1)`, + `Exit every __local0__, __local1__ in __local2__ { neq(__local0__, __local1__) } {} (qid=2, pqid=1)`, + ), + }, + { + note: "failure in first body query", + query: "data.test.p = x", + module: `package test + p if { every v in [1, 2] { 1 != v } }`, + exp: events( + `Enter every __local0__, __local1__ in __local2__ { neq(1, __local1__) } {} (qid=2, pqid=1)`, + `Enter neq(1, __local1__) {} (qid=3, pqid=2)`, + `Fail neq(1, __local1__) {} (qid=3, pqid=2)`, + `Fail every __local0__, __local1__ in __local2__ { neq(1, __local1__) } {} (qid=2, pqid=1)`, + `Redo every __local0__, __local1__ in __local2__ { neq(1, __local1__) } {} (qid=2, pqid=1)`, + ), + }, + { + note: "failure in last body query", + query: "data.test.p = x", + module: `package test + p if { every v in [0, 1] { 1 != v } }`, + exp: events( + `Enter every __local0__, __local1__ in __local2__ { neq(1, __local1__) } {} (qid=2, pqid=1)`, + `Enter neq(1, __local1__) {} (qid=3, pqid=2)`, + `Exit neq(1, __local1__) {} (qid=3, pqid=2)`, + `Enter neq(1, __local1__) {} (qid=4, pqid=2)`, + `Fail neq(1, __local1__) {} (qid=4, pqid=2)`, + `Fail every __local0__, __local1__ in __local2__ { neq(1, __local1__) } {} (qid=2, pqid=1)`, + `Redo every __local0__, __local1__ in __local2__ { neq(1, __local1__) } {} (qid=2, pqid=1)`, + ), + }, + } + + for _, tc := range tests { + + opts := ast.CompileOpts{ParserOptions: ast.ParserOptions{AllFutureKeywords: true}} + compiler, err := ast.CompileModulesWithOpt(map[string]string{"test.rego": tc.module}, opts) + if err != nil { + t.Fatal(err) + } + queryCompiler := compiler.QueryCompiler() + + compiledQuery, err := queryCompiler.Compile(ast.MustParseBody(tc.query)) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + buf := NewBufferTracer() + query := NewQuery(compiledQuery). + WithQueryCompiler(queryCompiler). + WithCompiler(compiler). + WithStore(inmem.New()). + WithQueryTracer(buf) + + if _, err := query.Run(ctx); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + for _, exp := range tc.exp { + found := false + for _, act := range *buf { + act.Locals = nil + if act.String() == exp { + found = true + } + } + if !found { + t.Errorf("expected event %v, found none", exp) + } + } + if t.Failed() { + t.Log("captured events:") + for _, ev := range *buf { + t.Log(ev.String()) + } + } + } +} + +func TestShortTraceFileNames(t *testing.T) { + t.Parallel() + + longFilePath1 := "/really/long/file/path/longer/than/most/would/really/ever/be/policy.rego" + longFilePath1Similar := "/really/long/file/path/longer/than/most/policy.rego" + longFilePath2 := "GfjEjnMA6coNiPoMoRMVk7KeorGeRmjRkIYUsWtr564SQ7yDo4Yss2SoN8PMoe0TOfVaNFd1HQbC9NhK.rego" + longFilePath3 := "RqS50uWAOxqqHmzdKVM3OCVsZDb12FJikUYHhz9pNqMWx3wjeQBKY3UYXsJXzYGOzuYZbidag5SfKVdk.rego" + + cases := []struct { + note string + trace []*Event + expectedNames map[string]string + expectedLongest int + }{ + { + note: "empty trace", + trace: nil, + expectedNames: map[string]string{}, + expectedLongest: 0, + }, + { + note: "no locations", + trace: []*Event{ + {Op: EnterOp, Node: ast.MustParseBody("true")}, + {Op: EvalOp, Node: ast.MustParseBody("true")}, + }, + expectedNames: map[string]string{}, + expectedLongest: 0, + }, + { + note: "no file names", + trace: []*Event{ + {Location: ast.NewLocation([]byte("foo1"), "", 1, 1)}, + {Location: ast.NewLocation([]byte("foo2"), "", 2, 1)}, + {Location: ast.NewLocation([]byte("foo100"), "", 100, 1)}, + {Location: ast.NewLocation([]byte("foo3"), "", 3, 1)}, + {Location: ast.NewLocation([]byte("foo4"), "", 4, 1)}, + }, + expectedNames: map[string]string{}, + expectedLongest: minLocationWidth + len(":100"), + }, + { + note: "single file name not shortened", + trace: []*Event{ + {Location: ast.NewLocation([]byte("foo1"), "policy.rego", 1, 1)}, + }, + expectedNames: map[string]string{ + "policy.rego": "policy.rego", + }, + expectedLongest: len("policy.rego:1"), + }, + { + note: "single file name not shortened different rows", + trace: []*Event{ + {Location: ast.NewLocation([]byte("foo1"), "policy.rego", 1, 1)}, + {Location: ast.NewLocation([]byte("foo1234"), "policy.rego", 1234, 1)}, + {Location: ast.NewLocation([]byte("foo12"), "policy.rego", 12, 1)}, + {Location: ast.NewLocation([]byte("foo123"), "policy.rego", 123, 1)}, + }, + expectedNames: map[string]string{ + "policy.rego": "policy.rego", + }, + expectedLongest: len("policy.rego:1234"), + }, + { + note: "multiple files name not shortened", + trace: []*Event{ + {Location: ast.NewLocation([]byte("a1"), "a.rego", 1, 1)}, + {Location: ast.NewLocation([]byte("a1234"), "a.rego", 1234, 1)}, + {Location: ast.NewLocation([]byte("x1"), "x.rego", 12, 1)}, + {Location: ast.NewLocation([]byte("foo123"), "policy.rego", 123, 1)}, + }, + expectedNames: map[string]string{ + "a.rego": "a.rego", + "x.rego": "x.rego", + "policy.rego": "policy.rego", + }, + expectedLongest: len("policy.rego:123"), + }, + { + note: "single file name shortened", + trace: []*Event{ + {Location: ast.NewLocation([]byte("foo1"), longFilePath1, 1, 1)}, + }, + expectedNames: map[string]string{ + longFilePath1: "/really/...h/longer/than/most/would/really/ever/be/policy.rego", + }, + expectedLongest: maxIdealLocationWidth, + }, + { + note: "single file name shortened different rows", + trace: []*Event{ + {Location: ast.NewLocation([]byte("foo1"), longFilePath1, 1, 1)}, + {Location: ast.NewLocation([]byte("foo1234"), longFilePath1, 1234, 1)}, + {Location: ast.NewLocation([]byte("foo123"), longFilePath1, 123, 1)}, + {Location: ast.NewLocation([]byte("foo12"), longFilePath1, 12, 1)}, + }, + expectedNames: map[string]string{ + longFilePath1: "/really/...onger/than/most/would/really/ever/be/policy.rego", + }, + expectedLongest: maxIdealLocationWidth, + }, + { + note: "multiple files name shortened different rows", + trace: []*Event{ + {Location: ast.NewLocation([]byte("similar1"), longFilePath1Similar, 1, 1)}, + {Location: ast.NewLocation([]byte("foo1234"), longFilePath1, 1234, 1)}, + {Location: ast.NewLocation([]byte("similar12"), longFilePath1Similar, 12, 1)}, + {Location: ast.NewLocation([]byte("foo123"), longFilePath1, 123, 1)}, + }, + expectedNames: map[string]string{ + longFilePath1: "/really/...onger/than/most/would/really/ever/be/policy.rego", + longFilePath1Similar: "/really/...onger/than/most/policy.rego", + }, + expectedLongest: maxIdealLocationWidth, + }, + { + note: "multiple files name cannot be shortened", + trace: []*Event{ + {Location: ast.NewLocation([]byte("foo1"), longFilePath2, 1, 1)}, + {Location: ast.NewLocation([]byte("foo1234"), longFilePath3, 1234, 1)}, + }, + expectedNames: map[string]string{ + longFilePath2: longFilePath2, + longFilePath3: longFilePath3, + }, + expectedLongest: len(longFilePath3 + ":1234"), + }, + { + note: "single file name shortened no leading slash", + trace: []*Event{ + {Location: ast.NewLocation([]byte("foo1"), longFilePath1[1:], 1, 1)}, + }, + expectedNames: map[string]string{ + longFilePath1[1:]: "really/...th/longer/than/most/would/really/ever/be/policy.rego", + }, + expectedLongest: maxIdealLocationWidth, + }, + } + + for _, tc := range cases { + t.Run(tc.note, func(t *testing.T) { + t.Parallel() + + actualNames, actualLongest := getShortenedFileNames(tc.trace) + if actualLongest != tc.expectedLongest { + t.Errorf("Expected longest location to be %d, got %d", tc.expectedLongest, actualLongest) + } + + if !maps.Equal(actualNames, tc.expectedNames) { + t.Errorf("Expected %+v got %+v", tc.expectedNames, actualNames) + } + }) + } +} + +func TestBufferTracerTraceConfig(t *testing.T) { + t.Parallel() + + ct := QueryTracer(NewBufferTracer()) + conf := ct.Config() + + expected := TraceConfig{ + PlugLocalVars: true, + } + + if !reflect.DeepEqual(expected, conf) { + t.Fatalf("Expected config: %+v, got %+v", expected, conf) + } +} + +func TestTraceInput(t *testing.T) { + t.Parallel() + + ctx := context.Background() + module := ` + package test + + rule = x if { + x = input.v + } + ` + + compiler := compileModules([]string{module}) + queryCompiler := compiler.QueryCompiler() + + compiledQuery, err := queryCompiler.Compile(ast.MustParseBody("{x | v = [1, 2, 3][_]; x = data.test.rule with input.v as v}")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + tracer := NewBufferTracer() + query := NewQuery(compiledQuery). + WithQueryCompiler(queryCompiler). + WithCompiler(compiler). + WithStore(inmem.New()). + WithQueryTracer(tracer) + + if _, err := query.Run(ctx); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + num := 1 + for i, evt := range *tracer { + if evt.Op == ExitOp && evt.HasRule() { + input := evt.Input().Value + expected := ast.NewObject([2]*ast.Term{ast.StringTerm("v"), ast.IntNumberTerm(num)}) + if input.Compare(expected) != 0 { + t.Errorf("%v != %v at index %d", input, expected, i) + } + num++ + } + } +} + +func TestTracePlug(t *testing.T) { + t.Parallel() + + ctx := context.Background() + module := ` + package test + + rule contains [a, b] if { + a = [1, 2][_] + b = [2, 1][_] + } + ` + + compiler := compileModules([]string{module}) + queryCompiler := compiler.QueryCompiler() + + compiledQuery, err := queryCompiler.Compile(ast.MustParseBody("data.test.rule")) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + tracer := plugRuleHeadKeyRecorder{} + query := NewQuery(compiledQuery). + WithQueryCompiler(queryCompiler). + WithCompiler(compiler). + WithStore(inmem.New()). + WithQueryTracer(&tracer) + + if _, err := query.Run(ctx); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expected := []ast.Value{ + ast.NewArray(ast.NumberTerm("1"), ast.NumberTerm("2")), + ast.NewArray(ast.NumberTerm("1"), ast.NumberTerm("1")), + ast.NewArray(ast.NumberTerm("2"), ast.NumberTerm("2")), + ast.NewArray(ast.NumberTerm("2"), ast.NumberTerm("1")), + } + + if len(tracer) != len(expected) { + t.Fatalf("unexpected result length %d", len(tracer)) + } + + for i, value := range tracer { + if value.Compare(expected[i]) != 0 { + t.Errorf("%v != %v at index %d", value, expected[i], i) + } + } +} + +type plugRuleHeadKeyRecorder []ast.Value + +func (plugRuleHeadKeyRecorder) Enabled() bool { + return true +} + +func (pr *plugRuleHeadKeyRecorder) TraceEvent(evt Event) { + if evt.Op == ExitOp && evt.HasRule() { + *pr = append(*pr, evt.Plug(evt.Node.(*ast.Rule).Head.Key).Value) + } +} + +func (plugRuleHeadKeyRecorder) Config() TraceConfig { + return TraceConfig{PlugLocalVars: false} +} + +func compareBuffers(t *testing.T, expected, actual string) { + t.Helper() + a := strings.Split(expected, "\n") + b := strings.Split(actual, "\n") + min := len(a) + if min > len(b) { + min = len(b) + } + + for i := range min { + if a[i] != b[i] { + t.Errorf("Line %v in trace is incorrect. Expected %q but got: %q", i+1, a[i], b[i]) + } + } + + if len(a) < len(b) { + t.Errorf("Extra lines in trace:\n%v", strings.Join(b[min:], "\n")) + } else if len(b) < len(a) { + t.Errorf("Missing lines in trace:\n%v", strings.Join(a[min:], "\n")) + } + + if t.Failed() { + fmt.Println("Trace output:") + fmt.Println(actual) + } +} + +func TestPrettyTraceWithLocationForMetadataCall(t *testing.T) { + t.Parallel() + + module := `package test +rule_no_output_var := rego.metadata.rule() + +rule_with_output_var if { + foo := rego.metadata.rule() + foo == {} +} + +chain_no_output_var := rego.metadata.chain() + +chain_with_output_var if { + foo := rego.metadata.chain() + foo == [] +}` + + ctx := context.Background() + compiler := compileModules([]string{module}) + data := loadSmallTestData() + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + tracer := NewBufferTracer() + query := NewQuery(ast.MustParseBody("data.test = _")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer(tracer) + + _, err := query.Run(ctx) + if err != nil { + panic(err) + } + + expected := `query:1 Enter data.test = _ +query:1 | Eval data.test = _ +query:1 | Index data.test.chain_no_output_var (matched 1 rule) +query:9 | Enter data.test.chain_no_output_var +query:9 | | Eval __local8__ = [{"path": ["test", "chain_no_output_var"]}] +query:9 | | Eval true +query:9 | | Eval __local4__ = __local8__ +query:9 | | Exit data.test.chain_no_output_var +query:9 | Redo data.test.chain_no_output_var +query:9 | | Redo __local4__ = __local8__ +query:9 | | Redo true +query:9 | | Redo __local8__ = [{"path": ["test", "chain_no_output_var"]}] +query:1 | Index data.test.chain_with_output_var (matched 1 rule, early exit) +query:11 | Enter data.test.chain_with_output_var +query:12 | | Eval __local9__ = [{"path": ["test", "chain_with_output_var"]}] +query:12 | | Eval __local5__ = __local9__ +query:12 | | Eval foo = __local5__ +query:13 | | Eval foo = [] +query:13 | | Fail foo = [] +query:12 | | Redo foo = __local5__ +query:12 | | Redo __local5__ = __local9__ +query:12 | | Redo __local9__ = [{"path": ["test", "chain_with_output_var"]}] +query:1 | Index data.test.rule_no_output_var (matched 1 rule) +query:2 | Enter data.test.rule_no_output_var +query:2 | | Eval __local6__ = {} +query:2 | | Eval true +query:2 | | Eval __local2__ = __local6__ +query:2 | | Exit data.test.rule_no_output_var +query:2 | Redo data.test.rule_no_output_var +query:2 | | Redo __local2__ = __local6__ +query:2 | | Redo true +query:2 | | Redo __local6__ = {} +query:1 | Index data.test.rule_with_output_var (matched 1 rule, early exit) +query:4 | Enter data.test.rule_with_output_var +query:5 | | Eval __local7__ = {} +query:5 | | Eval __local3__ = __local7__ +query:5 | | Eval foo = __local3__ +query:6 | | Eval foo = {} +query:4 | | Exit data.test.rule_with_output_var early +query:4 | Redo data.test.rule_with_output_var +query:6 | | Redo foo = {} +query:5 | | Redo foo = __local3__ +query:5 | | Redo __local3__ = __local7__ +query:5 | | Redo __local7__ = {} +query:1 | Exit data.test = _ +query:1 Redo data.test = _ +query:1 | Redo data.test = _ +` + + var buf bytes.Buffer + PrettyTraceWithLocation(&buf, removeUnifyOps(*tracer)) + compareBuffers(t, expected, buf.String()) +} + +func TestPrettyTraceWithUnifyOps(t *testing.T) { + t.Parallel() + + module := `package test + + p contains x if { + x = 1 + }` + + ctx := context.Background() + compiler := compileModules([]string{module}) + store := inmem.NewFromObject(nil) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + tracer := NewBufferTracer() + query := NewQuery(ast.MustParseBody("data.test.p")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer(tracer) + + _, err := query.Run(ctx) + if err != nil { + panic(err) + } + + expected := `query:1 Enter data.test.p +query:1 | Eval data.test.p +query:1 | Unify data.test.p = _ +query:1 | Index data.test.p (matched 1 rule) +query:3 | Enter data.test.p +query:4 | | Eval x = 1 +query:4 | | Unify x = 1 +query:3 | | Exit data.test.p +query:3 | Redo data.test.p +query:4 | | Redo x = 1 +query:1 | Unify {1} = _ +query:1 | Exit data.test.p +query:1 Redo data.test.p +query:1 | Redo data.test.p +` + + var buf bytes.Buffer + PrettyTraceWithLocation(&buf, *tracer) + compareBuffers(t, expected, buf.String()) +} + +// removeUnifyOps removes all UnifyOp events from a trace, since this is +// too verbose to test everywhere. +func removeUnifyOps(trace []*Event) (result []*Event) { + for _, event := range trace { + if event.Op != UnifyOp { + result = append(result, event) + } + } + return +} + +func TestPrettyTraceWithLocalVars(t *testing.T) { + t.Parallel() + + { + module := `package test + +p if { + x := 1 + y := 2 + z := do_math(x, y) + z == 3 +} + +do_math(a, b) := c if { + c := a + b +} +` + + ctx := context.Background() + compiler := compileModules([]string{module}) + data := loadSmallTestData() + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + tracer := NewBufferTracer() + query := NewQuery(ast.MustParseBody("data.test = _")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer(tracer) + + _, err := query.Run(ctx) + if err != nil { + panic(err) + } + + expected := `Enter data.test = _ {} +| Eval data.test = _ {} +| Unify data.test = _ {} +| Unify data.test.p = _ {} +| Index data.test.do_math (matched 1 rule) {} +| Unify data.test.p = _ {} +| Index data.test.p (matched 1 rule, early exit) {} +| Enter data.test.p {} +| | Eval x = 1 {} +| | Unify x = 1 {} +| | Eval y = 2 {__local0__: 1} +| | Unify y = 2 {__local0__: 1} +| | Eval data.test.do_math(x, y, __local6__) {__local0__: 1, __local1__: 2} +| | Index data.test.do_math (matched 1 rule) {__local0__: 1, __local1__: 2} +| | Enter data.test.do_math {} +| | | Unify 1 = a {} +| | | Unify 2 = b {__local3__: 1} +| | | Unify __local6__ = c {__local3__: 1, __local4__: 2} +| | | Eval plus(a, b, __local7__) {__local3__: 1, __local4__: 2} +| | | Unify __local7__ = 3 {__local3__: 1, __local4__: 2} +| | | Eval c = __local7__ {__local3__: 1, __local4__: 2, __local7__: 3} +| | | Unify c = 3 {__local3__: 1, __local4__: 2, __local7__: 3} +| | | Exit data.test.do_math {__local3__: 1, __local4__: 2, __local5__: 3, __local7__: 3} +| | Eval z = __local6__ {__local0__: 1, __local1__: 2, __local6__: 3} +| | Unify z = 3 {__local0__: 1, __local1__: 2, __local6__: 3} +| | Eval z = 3 {__local0__: 1, __local1__: 2, __local2__: 3, __local6__: 3} +| | Unify 3 = 3 {__local0__: 1, __local1__: 2, __local2__: 3, __local6__: 3} +| | Exit data.test.p early {__local0__: 1, __local1__: 2, __local2__: 3, __local6__: 3} +| Unify true = _ {} +| Redo data.test.p {__local0__: 1, __local1__: 2, __local2__: 3, __local6__: 3} +| | Redo z = 3 {__local0__: 1, __local1__: 2, __local2__: 3, __local6__: 3} +| | Redo z = __local6__ {__local0__: 1, __local1__: 2, __local2__: 3, __local6__: 3} +| | Redo data.test.do_math(x, y, __local6__) {__local0__: 1, __local1__: 2, __local6__: 3} +| | | Redo c = __local7__ {__local3__: 1, __local4__: 2, __local5__: 3, __local7__: 3} +| | | Redo plus(a, b, __local7__) {__local3__: 1, __local4__: 2, __local7__: 3} +| | Redo y = 2 {__local0__: 1, __local1__: 2} +| | Redo x = 1 {__local0__: 1} +| Unify _ = {"p": true} {} +| Exit data.test = _ {_: {"p": true}} +Redo data.test = _ {_: {"p": true}} +| Redo data.test = _ {_: {"p": true}} +` + + var buf bytes.Buffer + PrettyTraceWithOpts(&buf, *tracer, PrettyTraceOptions{LocalVariables: true}) + compareBuffers(t, expected, buf.String()) + } +} + +func TestPrettyTraceExprVars(t *testing.T) { + t.Parallel() + + { + module := `package test + +p if { + x := 1 + y := 2 + z := do_math(x, y) + z == 3 +} + +do_math(a, b) := c if { + c := a + b +} +` + + ctx := context.Background() + compiler := compileModules([]string{module}) + data := loadSmallTestData() + store := inmem.NewFromObject(data) + txn := storage.NewTransactionOrDie(ctx, store) + defer store.Abort(ctx, txn) + + tracer := NewBufferTracer() + query := NewQuery(ast.MustParseBody("data.test = _")). + WithCompiler(compiler). + WithStore(store). + WithTransaction(txn). + WithTracer(tracer) + + _, err := query.Run(ctx) + if err != nil { + panic(err) + } + + expected := `Enter data.test = _ {} +| Eval data.test = _ {} +| Unify data.test = _ {} +| Unify data.test.p = _ {} +| Index data.test.do_math (matched 1 rule) {} +| Unify data.test.p = _ {} +| Index data.test.p (matched 1 rule, early exit) {} +| Enter data.test.p {} +| | Eval x = 1 {} +| | Unify x = 1 {} +| | Eval y = 2 {} +| | Unify y = 2 {} +| | Eval data.test.do_math(x, y, __local6__) {x: 1, y: 2} +| | Index data.test.do_math (matched 1 rule) {x: 1, y: 2} +| | Enter data.test.do_math {} +| | | Unify 1 = a {} +| | | Unify 2 = b {} +| | | Unify __local6__ = c {} +| | | Eval plus(a, b, __local7__) {a: 1, b: 2} +| | | Unify __local7__ = 3 {} +| | | Eval c = __local7__ {__local7__: 3} +| | | Unify c = 3 {} +| | | Exit data.test.do_math {a: 1, b: 2, c: 3} +| | Eval z = __local6__ {__local6__: 3} +| | Unify z = 3 {} +| | Eval z = 3 {z: 3} +| | Unify 3 = 3 {} +| | Exit data.test.p early {} +| Unify true = _ {} +| Redo data.test.p {} +| | Redo z = 3 {z: 3} +| | Redo z = __local6__ {__local6__: 3, z: 3} +| | Redo data.test.do_math(x, y, __local6__) {__local6__: 3, x: 1, y: 2} +| | | Redo c = __local7__ {__local7__: 3, c: 3} +| | | Redo plus(a, b, __local7__) {__local7__: 3, a: 1, b: 2} +| | Redo y = 2 {y: 2} +| | Redo x = 1 {x: 1} +| Unify _ = {"p": true} {} +| Exit data.test = _ {_: {"p": true}} +Redo data.test = _ {_: {"p": true}} +| Redo data.test = _ {_: {"p": true}} +` + + var buf bytes.Buffer + PrettyTraceWithOpts(&buf, *tracer, PrettyTraceOptions{ExprVariables: true}) + compareBuffers(t, expected, buf.String()) + } +} diff --git a/third_party/opa/v1/topdown/type.go b/third_party/opa/v1/topdown/type.go new file mode 100644 index 000000000000..0e23d2721bd9 --- /dev/null +++ b/third_party/opa/v1/topdown/type.go @@ -0,0 +1,82 @@ +// Copyright 2022 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/v1/ast" +) + +func builtinIsNumber(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch operands[0].Value.(type) { + case ast.Number: + return iter(ast.InternedTerm(true)) + default: + return iter(ast.InternedTerm(false)) + } +} + +func builtinIsString(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch operands[0].Value.(type) { + case ast.String: + return iter(ast.InternedTerm(true)) + default: + return iter(ast.InternedTerm(false)) + } +} + +func builtinIsBoolean(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch operands[0].Value.(type) { + case ast.Boolean: + return iter(ast.InternedTerm(true)) + default: + return iter(ast.InternedTerm(false)) + } +} + +func builtinIsArray(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch operands[0].Value.(type) { + case *ast.Array: + return iter(ast.InternedTerm(true)) + default: + return iter(ast.InternedTerm(false)) + } +} + +func builtinIsSet(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch operands[0].Value.(type) { + case ast.Set: + return iter(ast.InternedTerm(true)) + default: + return iter(ast.InternedTerm(false)) + } +} + +func builtinIsObject(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch operands[0].Value.(type) { + case ast.Object: + return iter(ast.InternedTerm(true)) + default: + return iter(ast.InternedTerm(false)) + } +} + +func builtinIsNull(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch operands[0].Value.(type) { + case ast.Null: + return iter(ast.InternedTerm(true)) + default: + return iter(ast.InternedTerm(false)) + } +} + +func init() { + RegisterBuiltinFunc(ast.IsNumber.Name, builtinIsNumber) + RegisterBuiltinFunc(ast.IsString.Name, builtinIsString) + RegisterBuiltinFunc(ast.IsBoolean.Name, builtinIsBoolean) + RegisterBuiltinFunc(ast.IsArray.Name, builtinIsArray) + RegisterBuiltinFunc(ast.IsSet.Name, builtinIsSet) + RegisterBuiltinFunc(ast.IsObject.Name, builtinIsObject) + RegisterBuiltinFunc(ast.IsNull.Name, builtinIsNull) +} diff --git a/third_party/opa/v1/topdown/type_name.go b/third_party/opa/v1/topdown/type_name.go new file mode 100644 index 000000000000..9c079500c25e --- /dev/null +++ b/third_party/opa/v1/topdown/type_name.go @@ -0,0 +1,36 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "errors" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func builtinTypeName(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + switch operands[0].Value.(type) { + case ast.Null: + return iter(ast.InternedTerm("null")) + case ast.Boolean: + return iter(ast.InternedTerm("boolean")) + case ast.Number: + return iter(ast.InternedTerm("number")) + case ast.String: + return iter(ast.InternedTerm("string")) + case *ast.Array: + return iter(ast.InternedTerm("array")) + case ast.Object: + return iter(ast.InternedTerm("object")) + case ast.Set: + return iter(ast.InternedTerm("set")) + } + + return errors.New("illegal value") +} + +func init() { + RegisterBuiltinFunc(ast.TypeNameBuiltin.Name, builtinTypeName) +} diff --git a/third_party/opa/v1/topdown/uuid.go b/third_party/opa/v1/topdown/uuid.go new file mode 100644 index 000000000000..141fb908bdfb --- /dev/null +++ b/third_party/opa/v1/topdown/uuid.go @@ -0,0 +1,56 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/internal/uuid" + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/topdown/builtins" +) + +type uuidCachingKey string + +func builtinUUIDRFC4122(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + var key = uuidCachingKey(operands[0].Value.String()) + + val, ok := bctx.Cache.Get(key) + if ok { + return iter(val.(*ast.Term)) + } + + s, err := uuid.New(bctx.Seed) + if err != nil { + return err + } + + result := ast.StringTerm(s) + bctx.Cache.Put(key, result) + + return iter(result) +} + +func builtinUUIDParse(_ BuiltinContext, operands []*ast.Term, iter func(term *ast.Term) error) error { + str, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + parsed, err := uuid.Parse(string(str)) + if err != nil { + return nil + } + val, err := ast.InterfaceToValue(parsed) + if err != nil { + return err + } + + return iter(ast.NewTerm(val)) +} + +func init() { + RegisterBuiltinFunc(ast.UUIDRFC4122.Name, builtinUUIDRFC4122) + RegisterBuiltinFunc(ast.UUIDParse.Name, builtinUUIDParse) +} diff --git a/third_party/opa/v1/topdown/uuid_test.go b/third_party/opa/v1/topdown/uuid_test.go new file mode 100644 index 000000000000..00b6ca04aa8e --- /dev/null +++ b/third_party/opa/v1/topdown/uuid_test.go @@ -0,0 +1,104 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "errors" + "math/rand" + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +func TestUUIDRFC4122SeedingAndCaching(t *testing.T) { + t.Parallel() + + query := `uuid.rfc4122("x",x); uuid.rfc4122("y", y); uuid.rfc4122("x",x2)` + + q := NewQuery(ast.MustParseBody(query)).WithSeed(rand.New(rand.NewSource(0))).WithCompiler(ast.NewCompiler()) + + ctx := context.Background() + + qrs, err := q.Run(ctx) + if err != nil { + t.Fatal(err) + } else if len(qrs) != 1 { + t.Fatal("expected exactly one result but got:", qrs) + } + + exp := ast.MustParseTerm(` + { + { + x: "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8", + x2: "0194fdc2-fa2f-4cc0-81d3-ff12045b73c8", + y: "6e4ff95f-f662-45ee-a82a-bdf44a2d0b75", + } + } + `) + + result := queryResultSetToTerm(qrs) + + if !result.Equal(exp) { + t.Fatalf("expected %v but got %v", exp, result) + } +} + +type fakeSeedErrorReader struct{} + +func (fakeSeedErrorReader) Read([]byte) (int, error) { + return 0, errors.New("xxx") +} + +func TestUUIDRFC4122SeedError(t *testing.T) { + t.Parallel() + + query := `uuid.rfc4122("x",x)` + + q := NewQuery(ast.MustParseBody(query)).WithSeed(fakeSeedErrorReader{}).WithCompiler(ast.NewCompiler()).WithStrictBuiltinErrors(true) + + _, err := q.Run(context.Background()) + + if topdownErr, ok := err.(*Error); !ok || topdownErr.Code != BuiltinErr { + t.Fatal("unexpected error (or lack of error):", err) + } +} + +func TestUUIDRFC4122SavingDuringPartialEval(t *testing.T) { + t.Parallel() + + query := `foo = "x"; uuid.rfc4122(foo,x)` + c := ast.NewCompiler(). + WithCapabilities(&ast.Capabilities{Builtins: []*ast.Builtin{ast.UUIDRFC4122}}) + // Must compile to initialize type environment after WithCapabilities + c.Compile(nil) + + q := NewQuery(ast.MustParseBody(query)).WithSeed(rand.New(rand.NewSource(0))).WithCompiler(c) + + queries, modules, err := q.PartialRun(context.Background()) + if err != nil { + t.Fatal(err) + } else if len(modules) > 0 { + t.Fatal("expected no support") + } + + exp := ast.MustParseBody(`uuid.rfc4122("x", x); foo = "x"`) + + if len(queries) != 1 || !queries[0].Equal(exp) { + t.Fatalf("expected %v but got: %v", exp, queries) + } +} + +func queryResultSetToTerm(qrs QueryResultSet) *ast.Term { + s := ast.NewSet() + for i := range qrs { + bindings := ast.NewObject() + for k := range qrs[i] { + bindings.Insert(ast.NewTerm(k), qrs[i][k]) + } + s.Add(ast.NewTerm(bindings)) + } + return ast.NewTerm(s) +} diff --git a/third_party/opa/v1/topdown/walk.go b/third_party/opa/v1/topdown/walk.go new file mode 100644 index 000000000000..1c8961e71f28 --- /dev/null +++ b/third_party/opa/v1/topdown/walk.go @@ -0,0 +1,163 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/v1/ast" +) + +func evalWalk(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + input := operands[0] + + if pathIsWildcard(operands) { + // When the path assignment is a wildcard: walk(input, [_, value]) + // we may skip the path construction entirely, and simply return + // same pointer in each iteration. This is a *much* more efficient + // path when only the values are needed. + return walkNoPath(ast.ArrayTerm(ast.InternedEmptyArray, input), iter) + } + + filter := getOutputPath(operands) + return walk(filter, nil, input, iter) +} + +func walk(filter, path *ast.Array, input *ast.Term, iter func(*ast.Term) error) error { + if filter == nil || filter.Len() == 0 { + var pathCopy *ast.Array + if path == nil { + pathCopy = ast.InternedEmptyArrayValue + } else { + // Shallow copy, as while the array is modified, the elements are not + pathCopy = copyShallow(path) + } + + // TODO(ae): I'd *really* like these terms to be retrieved from a sync.Pool, and + // returned after iter is called. However, all my atttempts to do this have failed + // as there seems to be something holding on to these references after the call, + // leading to modifications that entirely alter the results. Perhaps this is not + // possible to do, but if it is,it would be a huge performance win. + if err := iter(ast.ArrayTerm(ast.NewTerm(pathCopy), input)); err != nil { + return err + } + } + + if filter != nil && filter.Len() > 0 { + key := filter.Elem(0) + filter = filter.Slice(1, -1) + if key.IsGround() { + if term := input.Get(key); term != nil { + return walk(filter, pathAppend(path, key), term, iter) + } + return nil + } + } + + switch v := input.Value.(type) { + case *ast.Array: + for i := range v.Len() { + if err := walk(filter, pathAppend(path, ast.InternedTerm(i)), v.Elem(i), iter); err != nil { + return err + } + } + case ast.Object: + for _, k := range v.Keys() { + if err := walk(filter, pathAppend(path, k), v.Get(k), iter); err != nil { + return err + } + } + case ast.Set: + for _, elem := range v.Slice() { + if err := walk(filter, pathAppend(path, elem), elem, iter); err != nil { + return err + } + } + } + + return nil +} + +func walkNoPath(input *ast.Term, iter func(*ast.Term) error) error { + // Note: the path array is embedded in the input from the start here + // in order to avoid an extra allocation per iteration. This leads to + // a little convoluted code below in order to extract and set the value, + // but since walk is commonly used to traverse large data structures, + // the performance gain is worth it. + if err := iter(input); err != nil { + return err + } + + inputArray := input.Value.(*ast.Array) + value := inputArray.Get(ast.InternedTerm(1)).Value + + switch v := value.(type) { + case ast.Object: + for _, k := range v.Keys() { + inputArray.Set(1, v.Get(k)) + if err := walkNoPath(input, iter); err != nil { + return err + } + } + case *ast.Array: + for i := range v.Len() { + inputArray.Set(1, v.Elem(i)) + if err := walkNoPath(input, iter); err != nil { + return err + } + } + case ast.Set: + for _, elem := range v.Slice() { + inputArray.Set(1, elem) + if err := walkNoPath(input, iter); err != nil { + return err + } + } + } + + return nil +} + +func pathAppend(path *ast.Array, key *ast.Term) *ast.Array { + if path == nil { + return ast.NewArray(key) + } + + return path.Append(key) +} + +func getOutputPath(operands []*ast.Term) *ast.Array { + if len(operands) == 2 { + if arr, ok := operands[1].Value.(*ast.Array); ok && arr.Len() == 2 { + if path, ok := arr.Elem(0).Value.(*ast.Array); ok { + return path + } + } + } + return nil +} + +func pathIsWildcard(operands []*ast.Term) bool { + if len(operands) == 2 { + if arr, ok := operands[1].Value.(*ast.Array); ok && arr.Len() == 2 { + if v, ok := arr.Elem(0).Value.(ast.Var); ok { + return v.IsWildcard() + } + } + } + return false +} + +func copyShallow(arr *ast.Array) *ast.Array { + cpy := make([]*ast.Term, 0, arr.Len()) + + arr.Foreach(func(elem *ast.Term) { + cpy = append(cpy, elem) + }) + + return ast.NewArray(cpy...) +} + +func init() { + RegisterBuiltinFunc(ast.WalkBuiltin.Name, evalWalk) +} diff --git a/third_party/opa/v1/tracing/tracing.go b/third_party/opa/v1/tracing/tracing.go new file mode 100644 index 000000000000..df2fb434a688 --- /dev/null +++ b/third_party/opa/v1/tracing/tracing.go @@ -0,0 +1,55 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package tracing enables dependency-injection at runtime. When used +// together with an underscore-import of `github.com/open-policy-agent/opa/features/tracing`, +// the server and its runtime will emit OpenTelemetry spans to the +// configured sink. +package tracing + +import "net/http" + +// Options are options for the HTTPTracingService, passed along as-is. +type Options []any + +// NewOptions is a helper method for constructing `tracing.Options` +func NewOptions(opts ...any) Options { + return opts +} + +// HTTPTracingService defines how distributed tracing comes in, server- and client-side +type HTTPTracingService interface { + // NewTransport is used when setting up an HTTP client + NewTransport(http.RoundTripper, Options) http.RoundTripper + + // NewHandler is used to wrap an http.Handler in the server + NewHandler(http.Handler, string, Options) http.Handler +} + +var tracing HTTPTracingService + +// RegisterHTTPTracing enables a HTTPTracingService for further use. +func RegisterHTTPTracing(ht HTTPTracingService) { + tracing = ht +} + +// NewTransport returns another http.RoundTripper, instrumented to emit tracing +// spans according to Options. Provided by the HTTPTracingService registered with +// this package via RegisterHTTPTracing. +func NewTransport(tr http.RoundTripper, opts Options) http.RoundTripper { + if tracing == nil { + return tr + } + return tracing.NewTransport(tr, opts) +} + +// NewHandler returns another http.Handler, instrumented to emit tracing spans +// according to Options. Provided by the HTTPTracingService registered with +// this package via RegisterHTTPTracing. +func NewHandler(f http.Handler, label string, opts Options) http.Handler { + if tracing == nil { + return f + } + return tracing.NewHandler(f, label, opts) +} diff --git a/third_party/opa/v1/types/decode.go b/third_party/opa/v1/types/decode.go new file mode 100644 index 000000000000..367b64bffb19 --- /dev/null +++ b/third_party/opa/v1/types/decode.go @@ -0,0 +1,191 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package types + +import ( + "encoding/json" + "fmt" + + "github.com/open-policy-agent/opa/v1/util" +) + +const ( + typeNull = "null" + typeBoolean = "boolean" + typeNumber = "number" + typeString = "string" + typeArray = "array" + typeSet = "set" + typeObject = "object" + typeAny = "any" + typeFunction = "function" +) + +// Unmarshal deserializes bs and returns the resulting type. +func Unmarshal(bs []byte) (result Type, err error) { + + var hint rawtype + + if err = util.UnmarshalJSON(bs, &hint); err == nil { + switch hint.Type { + case typeNull: + result = Nl + case typeBoolean: + result = B + case typeNumber: + result = N + case typeString: + result = S + case typeArray: + var arr rawarray + if err = util.UnmarshalJSON(bs, &arr); err == nil { + var err error + var static []Type + var dynamic Type + if static, err = unmarshalSlice(arr.Static); err != nil { + return nil, err + } + if len(arr.Dynamic) != 0 { + if dynamic, err = Unmarshal(arr.Dynamic); err != nil { + return nil, err + } + } + result = NewArray(static, dynamic) + } + case typeObject: + var obj rawobject + if err = util.UnmarshalJSON(bs, &obj); err == nil { + var err error + var static []*StaticProperty + var dynamic *DynamicProperty + if static, err = unmarshalStaticPropertySlice(obj.Static); err != nil { + return nil, err + } + if dynamic, err = unmarshalDynamicProperty(obj.Dynamic); err != nil { + return nil, err + } + result = NewObject(static, dynamic) + } + case typeSet: + var set rawset + if err = util.UnmarshalJSON(bs, &set); err == nil { + var of Type + if of, err = Unmarshal(set.Of); err == nil { + result = NewSet(of) + } + } + case typeAny: + var union rawunion + if err = util.UnmarshalJSON(bs, &union); err == nil { + var of []Type + if of, err = unmarshalSlice(union.Of); err == nil { + result = NewAny(of...) + } + } + case typeFunction: + var decl rawdecl + if err = util.UnmarshalJSON(bs, &decl); err == nil { + args, err := unmarshalSlice(decl.Args) + if err != nil { + return nil, err + } + var ret Type + if len(decl.Result) > 0 { + ret, err = Unmarshal(decl.Result) + if err != nil { + return nil, err + } + } + if len(decl.Variadic) > 0 { + varargs, err := Unmarshal(decl.Variadic) + if err != nil { + return nil, err + } + result = NewVariadicFunction(args, varargs, ret) + } else { + result = NewFunction(args, ret) + } + } + default: + err = fmt.Errorf("unsupported type '%v'", hint.Type) + } + } + + return result, err +} + +type rawtype struct { + Type string `json:"type"` +} + +type rawarray struct { + Static []json.RawMessage `json:"static"` + Dynamic json.RawMessage `json:"dynamic"` +} + +type rawobject struct { + Static []rawstaticproperty `json:"static"` + Dynamic rawdynamicproperty `json:"dynamic"` +} + +type rawstaticproperty struct { + Key any `json:"key"` + Value json.RawMessage `json:"value"` +} + +type rawdynamicproperty struct { + Key json.RawMessage `json:"key"` + Value json.RawMessage `json:"value"` +} + +type rawset struct { + Of json.RawMessage `json:"of"` +} + +type rawunion struct { + Of []json.RawMessage `json:"of"` +} + +type rawdecl struct { + Args []json.RawMessage `json:"args"` + Result json.RawMessage `json:"result"` + Variadic json.RawMessage `json:"variadic"` +} + +func unmarshalSlice(elems []json.RawMessage) (result []Type, err error) { + result = make([]Type, len(elems)) + for i := range elems { + if result[i], err = Unmarshal(elems[i]); err != nil { + return nil, err + } + } + return result, err +} + +func unmarshalStaticPropertySlice(elems []rawstaticproperty) (result []*StaticProperty, err error) { + result = make([]*StaticProperty, len(elems)) + for i := range elems { + value, err := Unmarshal(elems[i].Value) + if err != nil { + return nil, err + } + result[i] = NewStaticProperty(elems[i].Key, value) + } + return result, err +} + +func unmarshalDynamicProperty(x rawdynamicproperty) (result *DynamicProperty, err error) { + if len(x.Key) == 0 { + return nil, nil + } + var key Type + if key, err = Unmarshal(x.Key); err == nil { + var value Type + if value, err = Unmarshal(x.Value); err == nil { + return NewDynamicProperty(key, value), nil + } + } + return nil, err +} diff --git a/third_party/opa/v1/types/types.go b/third_party/opa/v1/types/types.go new file mode 100644 index 000000000000..366903f0cb73 --- /dev/null +++ b/third_party/opa/v1/types/types.go @@ -0,0 +1,1204 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package types declares data types for Rego values and helper functions to +// operate on these types. +package types + +import ( + "encoding/json" + "errors" + "fmt" + "slices" + "sort" + "strings" + + "github.com/open-policy-agent/opa/v1/util" +) + +var ( + // Nl represents an instance of the null type. + Nl Type = NewNull() + // B represents an instance of the boolean type. + B Type = NewBoolean() + // S represents an instance of the string type. + S Type = NewString() + // N represents an instance of the number type. + N Type = NewNumber() + // A represents the superset of all types. + A Type = NewAny() + + // Boxed set types. + SetOfAny, SetOfStr, SetOfNum Type = NewSet(A), NewSet(S), NewSet(N) +) + +// Sprint returns the string representation of the type. +func Sprint(x Type) string { + if x == nil { + return "???" + } + return x.String() +} + +// Type represents a type of a term in the language. +type Type interface { + String() string + typeMarker() string + json.Marshaler +} + +func (Null) typeMarker() string { return typeNull } +func (Boolean) typeMarker() string { return typeBoolean } +func (Number) typeMarker() string { return typeNumber } +func (String) typeMarker() string { return typeString } +func (*Array) typeMarker() string { return typeArray } +func (*Object) typeMarker() string { return typeObject } +func (*Set) typeMarker() string { return typeSet } +func (Any) typeMarker() string { return typeAny } +func (Function) typeMarker() string { return typeFunction } + +// Null represents the null type. +type Null struct{} + +// NewNull returns a new Null type. +func NewNull() Null { + return Null{} +} + +// NamedType represents a type alias with an arbitrary name and description. +// This is useful for generating documentation for built-in functions. +type NamedType struct { + Name, Descr string + Type Type +} + +func (n *NamedType) typeMarker() string { return n.Type.typeMarker() } +func (n *NamedType) String() string { return n.Name + ": " + n.Type.String() } +func (n *NamedType) MarshalJSON() ([]byte, error) { + var obj map[string]any + switch x := n.Type.(type) { + case interface{ toMap() map[string]any }: + obj = x.toMap() + default: + obj = map[string]any{ + "type": n.Type.typeMarker(), + } + } + obj["name"] = n.Name + if n.Descr != "" { + obj["description"] = n.Descr + } + return json.Marshal(obj) +} + +func (n *NamedType) Description(d string) *NamedType { + n.Descr = d + return n +} + +// Named returns the passed type as a named type. +// Named types are only valid at the top level of built-in functions. +// Note that nested named types cause panic. +func Named(name string, t Type) *NamedType { + return &NamedType{ + Type: t, + Name: name, + } +} + +// MarshalJSON returns the JSON encoding of t. +func (t Null) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]any{ + "type": t.typeMarker(), + }) +} + +func unwrap(t Type) Type { + switch t := t.(type) { + case *NamedType: + return t.Type + default: + return t + } +} + +func (Null) String() string { + return typeNull +} + +// Boolean represents the boolean type. +type Boolean struct{} + +// NewBoolean returns a new Boolean type. +func NewBoolean() Boolean { + return Boolean{} +} + +// MarshalJSON returns the JSON encoding of t. +func (t Boolean) MarshalJSON() ([]byte, error) { + repr := map[string]any{ + "type": t.typeMarker(), + } + return json.Marshal(repr) +} + +func (t Boolean) String() string { + return t.typeMarker() +} + +// String represents the string type. +type String struct{} + +// NewString returns a new String type. +func NewString() String { + return String{} +} + +// MarshalJSON returns the JSON encoding of t. +func (t String) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]any{ + "type": t.typeMarker(), + }) +} + +func (String) String() string { + return typeString +} + +// Number represents the number type. +type Number struct{} + +// NewNumber returns a new Number type. +func NewNumber() Number { + return Number{} +} + +// MarshalJSON returns the JSON encoding of t. +func (t Number) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]any{ + "type": t.typeMarker(), + }) +} + +func (Number) String() string { + return typeNumber +} + +// Array represents the array type. +type Array struct { + static []Type // static items + dynamic Type // dynamic items +} + +// NewArray returns a new Array type. +func NewArray(static []Type, dynamic Type) *Array { + return &Array{ + static: static, + dynamic: dynamic, + } +} + +// MarshalJSON returns the JSON encoding of t. +func (t *Array) MarshalJSON() ([]byte, error) { + return json.Marshal(t.toMap()) +} + +func (t *Array) toMap() map[string]any { + repr := map[string]any{ + "type": t.typeMarker(), + } + if len(t.static) != 0 { + repr["static"] = t.static + } + if t.dynamic != nil { + repr["dynamic"] = t.dynamic + } + return repr +} + +func (t *Array) String() string { + prefix := "array" + buf := []string{} + for _, tpe := range t.static { + buf = append(buf, Sprint(tpe)) + } + repr := prefix + if len(buf) > 0 { + repr += "<" + strings.Join(buf, ", ") + ">" + } + if t.dynamic != nil { + repr += "[" + t.dynamic.String() + "]" + } + return repr +} + +// Dynamic returns the type of the array's dynamic elements. +func (t *Array) Dynamic() Type { + return t.dynamic +} + +// Len returns the number of static array elements. +func (t *Array) Len() int { + return len(t.static) +} + +// Select returns the type of element at the zero-based pos. +func (t *Array) Select(pos int) Type { + if pos >= 0 { + if len(t.static) > pos { + return t.static[pos] + } + if t.dynamic != nil { + return t.dynamic + } + } + return nil +} + +// Set represents the set type. +type Set struct { + of Type +} + +// NewSet returns a new Set type. +func NewSet(of Type) *Set { + return &Set{ + of: of, + } +} + +func (t *Set) Of() Type { + return t.of +} + +// MarshalJSON returns the JSON encoding of t. +func (t *Set) MarshalJSON() ([]byte, error) { + return json.Marshal(t.toMap()) +} + +func (t *Set) toMap() map[string]any { + repr := map[string]any{ + "type": t.typeMarker(), + } + if t.of != nil { + repr["of"] = t.of + } + return repr +} + +func (t *Set) String() string { + prefix := typeSet + return prefix + "[" + Sprint(t.of) + "]" +} + +// StaticProperty represents a static object property. +type StaticProperty struct { + Key any + Value Type +} + +// NewStaticProperty returns a new StaticProperty object. +func NewStaticProperty(key any, value Type) *StaticProperty { + return &StaticProperty{ + Key: key, + Value: value, + } +} + +// MarshalJSON returns the JSON encoding of p. +func (p *StaticProperty) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]any{ + "key": p.Key, + "value": p.Value, + }) +} + +// DynamicProperty represents a dynamic object property. +type DynamicProperty struct { + Key Type + Value Type +} + +// NewDynamicProperty returns a new DynamicProperty object. +func NewDynamicProperty(key, value Type) *DynamicProperty { + return &DynamicProperty{ + Key: key, + Value: value, + } +} + +// MarshalJSON returns the JSON encoding of p. +func (p *DynamicProperty) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]any{ + "key": p.Key, + "value": p.Value, + }) +} + +func (p *DynamicProperty) String() string { + return fmt.Sprintf("%s: %s", Sprint(p.Key), Sprint(p.Value)) +} + +// Object represents the object type. +type Object struct { + static []*StaticProperty // constant properties + dynamic *DynamicProperty // dynamic properties +} + +// NewObject returns a new Object type. +func NewObject(static []*StaticProperty, dynamic *DynamicProperty) *Object { + slices.SortFunc(static, func(a, b *StaticProperty) int { + return util.Compare(a.Key, b.Key) + }) + return &Object{ + static: static, + dynamic: dynamic, + } +} + +func (t *Object) String() string { + prefix := "object" + buf := make([]string, 0, len(t.static)) + for _, p := range t.static { + buf = append(buf, fmt.Sprintf("%v: %v", p.Key, Sprint(p.Value))) + } + repr := prefix + if len(buf) > 0 { + repr += "<" + strings.Join(buf, ", ") + ">" + } + if t.dynamic != nil { + repr += "[" + t.dynamic.String() + "]" + } + return repr +} + +// DynamicValue returns the type of the object's dynamic elements. +func (t *Object) DynamicValue() Type { + if t.dynamic == nil { + return nil + } + return t.dynamic.Value +} + +// DynamicProperties returns the type of the object's dynamic elements. +func (t *Object) DynamicProperties() *DynamicProperty { + return t.dynamic +} + +// StaticProperties returns the type of the object's static elements. +func (t *Object) StaticProperties() []*StaticProperty { + return t.static +} + +// Keys returns the keys of the object's static elements. +func (t *Object) Keys() []any { + sl := make([]any, 0, len(t.static)) + for _, p := range t.static { + sl = append(sl, p.Key) + } + return sl +} + +// MarshalJSON returns the JSON encoding of t. +func (t *Object) MarshalJSON() ([]byte, error) { + return json.Marshal(t.toMap()) +} + +func (t *Object) toMap() map[string]any { + repr := map[string]any{ + "type": t.typeMarker(), + } + if len(t.static) != 0 { + repr["static"] = t.static + } + if t.dynamic != nil { + repr["dynamic"] = t.dynamic + } + return repr +} + +// Select returns the type of the named property. +func (t *Object) Select(name any) Type { + pos := sort.Search(len(t.static), func(x int) bool { + return util.Compare(t.static[x].Key, name) >= 0 + }) + + if pos < len(t.static) && util.Compare(t.static[pos].Key, name) == 0 { + return t.static[pos].Value + } + + if t.dynamic != nil { + if Contains(t.dynamic.Key, TypeOf(name)) { + return t.dynamic.Value + } + } + + return nil +} + +func (t *Object) Merge(other Type) *Object { + if otherObj, ok := other.(*Object); ok { + return mergeObjects(t, otherObj) + } + + var typeK Type + var typeV Type + dynProps := t.DynamicProperties() + if dynProps != nil { + typeK = Or(Keys(other), dynProps.Key) + typeV = Or(Values(other), dynProps.Value) + dynProps = NewDynamicProperty(typeK, typeV) + } else { + typeK = Keys(other) + typeV = Values(other) + if typeK != nil && typeV != nil { + dynProps = NewDynamicProperty(typeK, typeV) + } + } + + return NewObject(t.StaticProperties(), dynProps) +} + +func mergeObjects(a, b *Object) *Object { + var dynamicProps *DynamicProperty + if a.dynamic != nil && b.dynamic != nil { + typeK := Or(a.dynamic.Key, b.dynamic.Key) + var typeV Type + aObj, aIsObj := a.dynamic.Value.(*Object) + bObj, bIsObj := b.dynamic.Value.(*Object) + if aIsObj && bIsObj { + typeV = mergeObjects(aObj, bObj) + } else { + typeV = Or(a.dynamic.Value, b.dynamic.Value) + } + dynamicProps = NewDynamicProperty(typeK, typeV) + } else if a.dynamic != nil { + dynamicProps = a.dynamic + } else { + dynamicProps = b.dynamic + } + + staticPropsMap := make(map[any]Type) + + for _, sp := range a.static { + staticPropsMap[sp.Key] = sp.Value + } + + for _, sp := range b.static { + currV := staticPropsMap[sp.Key] + if currV != nil { + currVObj, currVIsObj := currV.(*Object) + spVObj, spVIsObj := sp.Value.(*Object) + if currVIsObj && spVIsObj { + staticPropsMap[sp.Key] = mergeObjects(currVObj, spVObj) + } else { + staticPropsMap[sp.Key] = Or(currV, sp.Value) + } + } else { + staticPropsMap[sp.Key] = sp.Value + } + } + + staticProps := make([]*StaticProperty, 0, len(staticPropsMap)) + for k, v := range staticPropsMap { + staticProps = append(staticProps, NewStaticProperty(k, v)) + } + + return NewObject(staticProps, dynamicProps) +} + +// Any represents a dynamic type. +type Any []Type + +// NewAny returns a new Any type. +func NewAny(of ...Type) Any { + sl := make(Any, len(of)) + copy(sl, of) + sort.Sort(typeSlice(sl)) + return sl +} + +// Contains returns true if t is a superset of other. +func (t Any) Contains(other Type) bool { + if _, ok := other.(*Function); ok { + return false + } + // Note(philipc): We used to do this as a linear search. + // Since this is always sorted, we can use a binary search instead. + i := sort.Search(len(t), func(i int) bool { + return Compare(t[i], other) >= 0 + }) + if i < len(t) && Compare(t[i], other) == 0 { + // x is present at t[i] + return true + } + return len(t) == 0 +} + +// MarshalJSON returns the JSON encoding of t. +func (t Any) MarshalJSON() ([]byte, error) { + return json.Marshal(t.toMap()) +} + +func (t Any) toMap() map[string]any { + repr := map[string]any{ + "type": t.typeMarker(), + } + if len(t) != 0 { + repr["of"] = []Type(t) + } + return repr +} + +// Merge return a new Any type that is the superset of t and other. +func (t Any) Merge(other Type) Any { + if otherAny, ok := other.(Any); ok { + return t.Union(otherAny) + } + if t.Contains(other) { + return t + } + cpy := make(Any, len(t)+1) + idx := sort.Search(len(t), func(i int) bool { + return Compare(t[i], other) >= 0 + }) + copy(cpy, t[:idx]) + cpy[idx] = other + copy(cpy[idx+1:], t[idx:]) + return cpy +} + +// Union returns a new Any type that is the union of the two Any types. +// Note(philipc): The two Any slices MUST be sorted before running Union, +// or else this method will fail to merge the two slices correctly. +func (t Any) Union(other Any) Any { + lenT := len(t) + lenOther := len(other) + // Return the more general (blank) Any type if present. + if lenT == 0 { + return t + } + if lenOther == 0 { + return other + } + // Prealloc the output list. + maxLen := max(lenT, lenOther) + merged := make(Any, 0, maxLen) + // Note(philipc): Create a merged slice, doing the minimum number of + // comparisons along the way. We treat this as a problem of merging two + // sorted lists that might have duplicates. This specifically saves us + // from cases where one list might be *much* longer than the other. + // Algorithm: + // Assume: + // - List A + // - List B + // - List Output + // - Idx_a, Idx_b + // Procedure: + // - While Idx_a < len(A) and Idx_b < len(B) + // - Compare head(A) and head(B) + // - Cases: + // - A < B: Append head(A) to Output, advance Idx_a + // - A == B: Append head(A) to Output, advance Idx_a, Idx_b + // - A > B: Append head(B) to Output, advance Idx_b + // - Return output + idxA := 0 + idxB := 0 + for idxA < lenT || idxB < lenOther { + // Early-exit cases: + if idxA == lenT { + // Ran out of elements in t. Copy over what's left from other. + merged = append(merged, other[idxB:]...) + break + } else if idxB == lenOther { + // Ran out of elements in other. Copy over what's left from t. + merged = append(merged, t[idxA:]...) + break + } + // Normal selection of next element to merge: + switch Compare(t[idxA], other[idxB]) { + // A < B: + case -1: + merged = append(merged, t[idxA]) + idxA++ + // A == B: + case 0: + merged = append(merged, t[idxA]) + idxA++ + idxB++ + // A > B: + case 1: + merged = append(merged, other[idxB]) + idxB++ + } + } + return merged +} + +func (t Any) String() string { + prefix := "any" + if len(t) == 0 { + return prefix + } + buf := make([]string, len(t)) + for i := range t { + buf[i] = Sprint(t[i]) + } + return prefix + "<" + strings.Join(buf, ", ") + ">" +} + +// Function represents a function type. +type Function struct { + args []Type + result Type + variadic Type +} + +// Args returns an argument list. +func Args(x ...Type) []Type { + return x +} + +// Void returns true if the function has no return value. This function returns +// false if x is not a function. +func Void(x Type) bool { + f, ok := x.(*Function) + return ok && f.Result() == nil +} + +// Arity returns the number of arguments in the function signature or zero if x +// is not a function. If the type is unknown, this function returns -1. +func Arity(x Type) int { + if x == nil { + return -1 + } + f, ok := x.(*Function) + if !ok { + return 0 + } + return f.Arity() +} + +// NewFunction returns a new Function object of the given argument and result types. +func NewFunction(args []Type, result Type) *Function { + return &Function{ + args: args, + result: result, + } +} + +// NewVariadicFunction returns a new Function object. This function sets the +// variadic bit on the signature. Non-void variadic functions are not currently +// supported. +func NewVariadicFunction(args []Type, varargs Type, result Type) *Function { + if result != nil { + panic("illegal value: non-void variadic functions not supported") + } + return &Function{ + args: args, + variadic: varargs, + result: nil, + } +} + +// FuncArgs returns the function's arguments. +func (t *Function) FuncArgs() FuncArgs { + return FuncArgs{Args: t.Args(), Variadic: unwrap(t.variadic)} +} + +// NamedFuncArgs returns the function's arguments, with a name and +// description if available. +func (t *Function) NamedFuncArgs() FuncArgs { + args := make([]Type, len(t.args)) + copy(args, t.args) + return FuncArgs{Args: args, Variadic: t.variadic} +} + +// Args returns the function's arguments as a slice, ignoring variadic arguments. +// Deprecated: Use FuncArgs instead. +func (t *Function) Args() []Type { + cpy := make([]Type, len(t.args)) + for i := range t.args { + cpy[i] = unwrap(t.args[i]) + } + return cpy +} + +// Arity returns the number of arguments in the function signature. +func (t *Function) Arity() int { + return len(t.args) +} + +// Result returns the function's result type. +func (t *Function) Result() Type { + return unwrap(t.result) +} + +// Result returns the function's result type, without stripping name and description. +func (t *Function) NamedResult() Type { + return t.result +} + +func (t *Function) String() string { + return fmt.Sprintf("%v => %v", t.FuncArgs(), Sprint(t.Result())) +} + +// MarshalJSON returns the JSON encoding of t. +func (t *Function) MarshalJSON() ([]byte, error) { + repr := map[string]any{ + "type": t.typeMarker(), + } + if len(t.args) > 0 { + repr["args"] = t.args + } + if t.result != nil { + repr["result"] = t.result + } + if t.variadic != nil { + repr["variadic"] = t.variadic + } + return json.Marshal(repr) +} + +// UnmarshalJSON decodes the JSON serialized function declaration. +func (t *Function) UnmarshalJSON(bs []byte) error { + tpe, err := Unmarshal(bs) + if err != nil { + return err + } + + f, ok := tpe.(*Function) + if !ok { + return errors.New("invalid type") + } + + *t = *f + return nil +} + +// Union returns a new function representing the union of t and other. Functions +// must have the same arity to be unioned. +func (t *Function) Union(other *Function) *Function { + if other == nil { + return t + } + if t == nil { + return other + } + + if t.Arity() != other.Arity() { + return nil + } + + tfa := t.FuncArgs() + ofa := other.FuncArgs() + + aIsVariadic := tfa.Variadic != nil + bIsVariadic := ofa.Variadic != nil + + if aIsVariadic && !bIsVariadic { + return nil + } else if bIsVariadic && !aIsVariadic { + return nil + } + + a := t.Args() + b := other.Args() + + args := make([]Type, len(a)) + for i := range a { + args[i] = Or(a[i], b[i]) + } + + result := NewFunction(args, Or(t.Result(), other.Result())) + result.variadic = Or(tfa.Variadic, ofa.Variadic) + + return result +} + +// FuncArgs represents the arguments that can be passed to a function. +type FuncArgs struct { + Args []Type `json:"args,omitempty"` + Variadic Type `json:"variadic,omitempty"` +} + +func (a FuncArgs) String() string { + buf := make([]string, 0, len(a.Args)+1) + for i := range a.Args { + buf = append(buf, Sprint(a.Args[i])) + } + if a.Variadic != nil { + buf = append(buf, Sprint(a.Variadic)+"...") + } + return "(" + strings.Join(buf, ", ") + ")" +} + +// Arg returns the nth argument's type. +func (a FuncArgs) Arg(x int) Type { + if x < len(a.Args) { + return a.Args[x] + } + return a.Variadic +} + +// Compare returns -1, 0, 1 based on comparison between a and b. +func Compare(a, b Type) int { + a, b = unwrap(a), unwrap(b) + x := typeOrder(a) + y := typeOrder(b) + if x > y { + return 1 + } else if x < y { + return -1 + } + switch a.(type) { //nolint:gocritic + case nil, Null, Boolean, Number, String: + return 0 + case *Array: + arrA := a.(*Array) + arrB := b.(*Array) + if arrA.dynamic != nil && arrB.dynamic == nil { + return 1 + } else if arrB.dynamic != nil && arrA.dynamic == nil { + return -1 + } + if arrB.dynamic != nil && arrA.dynamic != nil { + if cmp := Compare(arrA.dynamic, arrB.dynamic); cmp != 0 { + return cmp + } + } + return typeSliceCompare(arrA.static, arrB.static) + case *Object: + objA := a.(*Object) + objB := b.(*Object) + if objA.dynamic != nil && objB.dynamic == nil { + return 1 + } else if objB.dynamic != nil && objA.dynamic == nil { + return -1 + } + if objA.dynamic != nil && objB.dynamic != nil { + if cmp := Compare(objA.dynamic.Key, objB.dynamic.Key); cmp != 0 { + return cmp + } + if cmp := Compare(objA.dynamic.Value, objB.dynamic.Value); cmp != 0 { + return cmp + } + } + + lenStaticA := len(objA.static) + lenStaticB := len(objB.static) + + minLen := min(lenStaticB, lenStaticA) + + for i := range minLen { + if cmp := util.Compare(objA.static[i].Key, objB.static[i].Key); cmp != 0 { + return cmp + } + if cmp := Compare(objA.static[i].Value, objB.static[i].Value); cmp != 0 { + return cmp + } + } + + if lenStaticA < lenStaticB { + return -1 + } else if lenStaticB < lenStaticA { + return 1 + } + + return 0 + case *Set: + setA := a.(*Set) + setB := b.(*Set) + if setA.of == nil && setB.of == nil { + return 0 + } else if setA.of == nil { + return -1 + } else if setB.of == nil { + return 1 + } + return Compare(setA.of, setB.of) + case Any: + sl1 := typeSlice(a.(Any)) + sl2 := typeSlice(b.(Any)) + return typeSliceCompare(sl1, sl2) + case *Function: + fA := a.(*Function) + fB := b.(*Function) + if len(fA.args) < len(fB.args) { + return -1 + } else if len(fA.args) > len(fB.args) { + return 1 + } + for i := range len(fA.args) { + if cmp := Compare(fA.args[i], fB.args[i]); cmp != 0 { + return cmp + } + } + if cmp := Compare(fA.result, fB.result); cmp != 0 { + return cmp + } + return Compare(fA.variadic, fB.variadic) + default: + panic("unreachable") + } +} + +// Contains returns true if a is a superset or equal to b. +func Contains(a, b Type) bool { + if x, ok := unwrap(a).(Any); ok { + return x.Contains(b) + } + return Compare(a, b) == 0 +} + +// Or returns a type that represents the union of a and b. If one type is a +// superset of the other, the superset is returned unchanged. +func Or(a, b Type) Type { + a, b = unwrap(a), unwrap(b) + if a == nil { + return b + } else if b == nil { + return a + } + fA, ok1 := a.(*Function) + fB, ok2 := b.(*Function) + if ok1 && ok2 { + return fA.Union(fB) + } else if ok1 || ok2 { + return nil + } + anyA, ok1 := a.(Any) + anyB, ok2 := b.(Any) + if ok1 { + return anyA.Merge(b) + } + if ok2 { + return anyB.Merge(a) + } + if Compare(a, b) == 0 { + return a + } + return NewAny(a, b) +} + +// Select returns a property or item of a. +func Select(a Type, x any) Type { + switch a := unwrap(a).(type) { + case *Array: + n, ok := x.(json.Number) + if !ok { + return nil + } + pos, err := n.Int64() + if err != nil { + return nil + } + return a.Select(int(pos)) + case *Object: + return a.Select(x) + case *Set: + tpe := TypeOf(x) + if Compare(a.of, tpe) == 0 { + return a.of + } + if x, ok := a.of.(Any); ok { + if x.Contains(tpe) { + return tpe + } + } + return nil + case Any: + if Compare(a, A) == 0 { + return A + } + var tpe Type + for i := range a { + // TODO(tsandall): test nil/nil + tpe = Or(Select(a[i], x), tpe) + } + return tpe + default: + return nil + } +} + +// Keys returns the type of keys that can be enumerated for a. For arrays, the +// keys are always number types, for objects the keys are always string types, +// and for sets the keys are always the type of the set element. +func Keys(a Type) Type { + switch a := unwrap(a).(type) { + case *Array: + return N + case *Object: + var tpe Type + for _, k := range a.Keys() { + tpe = Or(tpe, TypeOf(k)) + } + if a.dynamic != nil { + tpe = Or(tpe, a.dynamic.Key) + } + return tpe + case *Set: + return a.of + case Any: + // TODO(tsandall): ditto test + if Compare(a, A) == 0 { + return A + } + var tpe Type + for i := range a { + tpe = Or(Keys(a[i]), tpe) + } + return tpe + } + return nil +} + +// Values returns the type of values that can be enumerated for a. +func Values(a Type) Type { + switch a := unwrap(a).(type) { + case *Array: + var tpe Type + for i := range a.static { + tpe = Or(tpe, a.static[i]) + } + return Or(tpe, a.dynamic) + case *Object: + var tpe Type + for i := range a.static { + tpe = Or(tpe, a.static[i].Value) + } + if a.dynamic != nil { + tpe = Or(tpe, a.dynamic.Value) + } + return tpe + case *Set: + return a.of + case Any: + if Compare(a, A) == 0 { + return A + } + var tpe Type + for i := range a { + tpe = Or(Values(a[i]), tpe) + } + return tpe + } + return nil +} + +// Nil returns true if a's type is unknown. +func Nil(a Type) bool { + switch a := unwrap(a).(type) { + case nil: + return true + case *Function: + if slices.ContainsFunc(a.args, Nil) { + return true + } + return Nil(a.result) + case *Array: + if slices.ContainsFunc(a.static, Nil) { + return true + } + if a.dynamic != nil { + return Nil(a.dynamic) + } + case *Object: + for i := range a.static { + if Nil(a.static[i].Value) { + return true + } + } + if a.dynamic != nil { + return Nil(a.dynamic.Key) || Nil(a.dynamic.Value) + } + case *Set: + return Nil(a.of) + } + return false +} + +// TypeOf returns the type of the Golang native value. +func TypeOf(x any) Type { + switch x := x.(type) { + case nil: + return Nl + case bool: + return B + case string: + return S + case json.Number: + return N + case map[string]any: + // The ast.ValueToInterface() function returns ast.Object values as map[string]any + // so map[string]any must be handled here because the type checker uses the value + // to interface conversion when inferring object types. + static := make([]*StaticProperty, 0, len(x)) + for k, v := range x { + static = append(static, NewStaticProperty(k, TypeOf(v))) + } + return NewObject(static, nil) + case map[any]any: + static := make([]*StaticProperty, 0, len(x)) + for k, v := range x { + static = append(static, NewStaticProperty(k, TypeOf(v))) + } + return NewObject(static, nil) + case []any: + static := make([]Type, len(x)) + for i := range x { + static[i] = TypeOf(x[i]) + } + return NewArray(static, nil) + } + panic("unreachable") +} + +type typeSlice []Type + +func (s typeSlice) Less(i, j int) bool { return Compare(s[i], s[j]) < 0 } +func (s typeSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s typeSlice) Len() int { return len(s) } + +func typeSliceCompare(a, b []Type) int { + minLen := min(len(b), len(a)) + for i := range minLen { + if cmp := Compare(a[i], b[i]); cmp != 0 { + return cmp + } + } + if len(a) < len(b) { + return -1 + } else if len(b) < len(a) { + return 1 + } + return 0 +} + +func typeOrder(x Type) int { + switch unwrap(x).(type) { + case Null: + return 0 + case Boolean: + return 1 + case Number: + return 2 + case String: + return 3 + case *Array: + return 4 + case *Object: + return 5 + case *Set: + return 6 + case Any: + return 7 + case *Function: + return 8 + case nil: + return -1 + } + panic("unreachable") +} diff --git a/third_party/opa/v1/types/types_bench_test.go b/third_party/opa/v1/types/types_bench_test.go new file mode 100644 index 000000000000..77608350dd45 --- /dev/null +++ b/third_party/opa/v1/types/types_bench_test.go @@ -0,0 +1,95 @@ +// Copyright 2021 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package types + +import ( + "encoding/json" + "fmt" + "strconv" + "testing" +) + +func BenchmarkSelect(b *testing.B) { + sizes := []int{1000, 10000, 100000} + for _, size := range sizes { + b.Run(strconv.Itoa(size), func(b *testing.B) { + tpe := generateType(size) + runSelectBenchmark(b, tpe, json.Number(strconv.Itoa(size-1))) + }) + } +} + +func runSelectBenchmark(b *testing.B, tpe Type, key any) { + b.ResetTimer() + for range b.N { + if result := Select(tpe, key); result != nil { + if Compare(result, N) != 0 { + b.Fatal("expected number type") + } + } + } +} + +func generateType(n int) Type { + static := make([]*StaticProperty, n) + for i := range n { + static[i] = NewStaticProperty(json.Number(strconv.Itoa(i)), N) + } + return NewObject(static, nil) +} + +func generateTypeWithPrefix(n int, prefix string) Type { + static := make([]*StaticProperty, n) + for i := range n { + static[i] = NewStaticProperty(prefix+strconv.Itoa(i), S) + } + return NewObject(static, nil) +} + +func BenchmarkAnyMergeOne(b *testing.B) { + sizes := []int{100, 500, 1000, 5000, 10000} + for _, size := range sizes { + anyA := Any(make([]Type, 0, size)) + for i := range size { + tpe := generateType(i) + anyA = append(anyA, tpe) + } + tpeB := N + b.ResetTimer() + b.Run(strconv.Itoa(size), func(b *testing.B) { + result := anyA.Merge(tpeB) + if len(result) != len(anyA)+1 { + b.Fatalf("Expected length of merged result to be: %d, got: %d", len(anyA)+1, len(result)) + } + }) + } +} + +// Build up 2x Any type lists of unique and different types, then Union merge. +func BenchmarkAnyUnionAllUniqueTypes(b *testing.B) { + sizes := []int{100, 250, 500, 1000, 2500} + for _, sizeA := range sizes { + for _, sizeB := range sizes { + anyA := Any(make([]Type, 0, sizeA)) + for i := range sizeA { + tpe := generateType(i) + anyA = append(anyA, tpe) + } + anyB := Any(make([]Type, 0, sizeB)) + for i := range sizeB { + tpe := generateTypeWithPrefix(i, "B-") + anyB = append(anyB, tpe) + } + b.ResetTimer() + b.Run(fmt.Sprintf("%dx%d", sizeA, sizeB), func(b *testing.B) { + resultA2B := anyA.Union(anyB) + // Expect length to be A + B - 1, because the `object` type is present in both Any type sets. + if len(resultA2B) != (len(anyA) + len(anyB) - 1) { + b.Fatalf("Expected length of unioned result to be: %d, got: %d", len(anyA)+len(anyB), len(resultA2B)) + } + }) + } + } +} diff --git a/third_party/opa/v1/types/types_test.go b/third_party/opa/v1/types/types_test.go new file mode 100644 index 000000000000..215a1db654a3 --- /dev/null +++ b/third_party/opa/v1/types/types_test.go @@ -0,0 +1,491 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package types + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/open-policy-agent/opa/v1/util" +) + +var dynamicPropertyAnyAny = NewDynamicProperty(A, A) + +func TestAnySorted(t *testing.T) { + if Compare(NewAny(S, N)[0], N) != 0 { + t.Fatal("expected any type to be sorted") + } +} + +func TestAnyMerge(t *testing.T) { + x := NewAny(S, B) + + if Compare(x.Merge(N)[1], N) != 0 { + t.Fatal("expected number to be inserted into middle") + } + + if Compare(x.Merge(Nl)[0], Nl) != 0 { + t.Fatal("expected null to be inserted at front") + } + + if Compare(x.Merge(NewArray(nil, A))[2], NewArray(nil, A)) != 0 { + t.Fatal("expected array to be inserted at back") + } +} + +func TestAnyUnion(t *testing.T) { + x := NewAny(Nl, N) + y := NewAny(S, B) + z := x.Union(y) + exp := []Type{Nl, B, N, S} + if len(z) != len(exp) { + t.Fatalf("expected %v elements in result of union", len(exp)) + } + for i := range z { + if Compare(z[i], exp[i]) != 0 { + t.Fatal("expected", exp[i], "but got", z[i]) + } + } +} + +func TestStrings(t *testing.T) { + tpe := NewObject([]*StaticProperty{ + {"foo", Nl}, + {"bar", B}, + {"baz", N}, + {"qux", S}, + {"corge", NewArray( + []Type{ + A, + NewAny(Nl, S), + NewSet(S), + }, S, + )}, + {"nil", nil}, + }, NewDynamicProperty(S, N)) + + expected := `object, set[string]>[string], foo: null, nil: ???, qux: string>[string: number]` + + if tpe.String() != expected { + t.Fatalf("Expected %v but got: %v", expected, tpe) + } + + ftpe := NewFunction([]Type{S, S}, N) + expected = "(string, string) => number" + + if ftpe.String() != expected { + t.Fatalf("Expected %v but got: %v", expected, ftpe) + } + + ftpe = NewVariadicFunction([]Type{N}, S, nil) + expected = "(number, string...) => ???" + + if ftpe.String() != expected { + t.Fatal("expected", expected, "but got:", ftpe) + } +} + +func TestCompare(t *testing.T) { + tests := []struct { + a Type + b Type + cmp int + }{ + {Nl, Nl, 0}, + {Nl, B, -1}, + {B, Nl, 1}, + {B, B, 0}, + {B, N, -1}, + {N, N, 0}, + {N, S, -1}, + {S, S, 0}, + {S, NewArray(NewAny(), nil), -1}, + {NewArray(NewAny(), nil), NewArray(NewAny(), A), -1}, + {NewArray(NewAny(), A), NewArray(NewAny(), A), 0}, + {NewArray(NewAny(), A), NewArray(NewAny(), S), 1}, + {NewArray(NewAny(), A), NewArray(NewAny(), nil), 1}, + {NewArray([]Type{S}, nil), NewArray([]Type{N}, nil), 1}, + {NewObject(nil, nil), NewObject(nil, dynamicPropertyAnyAny), -1}, + {NewObject(nil, dynamicPropertyAnyAny), NewObject(nil, nil), 1}, + {NewObject(nil, dynamicPropertyAnyAny), NewObject(nil, dynamicPropertyAnyAny), 0}, + {NewObject(nil, NewDynamicProperty(S, NewAny(S, Nl))), NewObject(nil, dynamicPropertyAnyAny), -1}, + {NewSet(Nl), NewSet(NewAny()), -1}, + { + NewObject( + []*StaticProperty{{"foo", S}}, + nil), + NewObject( + []*StaticProperty{{"foo", S}, {"bar", N}}, + nil), + 1, + }, + { + NewObject( + []*StaticProperty{{"foo", S}, {"bar", N}}, + nil), + NewObject( + []*StaticProperty{{"foo", S}}, + nil), + -1, + }, + { + NewObject( + []*StaticProperty{{"foo", S}}, + nil), + NewObject( + []*StaticProperty{{"foo", Nl}}, + nil), + 1, + }, + { + NewObject( + []*StaticProperty{{"foo", S}}, + nil), + NewObject( + []*StaticProperty{{"foo", S}, {"foo-2", N}}, + nil), + -1, + }, + { + NewObject( + []*StaticProperty{{"foo", S}, {"foo-2", N}}, + nil), + NewObject( + []*StaticProperty{{"foo", S}}, + nil), + 1, + }, + {NewFunction(nil, nil), A, 1}, + {NewFunction([]Type{B}, N), NewFunction([]Type{S}, N), -1}, + {NewFunction(nil, S), NewFunction(nil, N), 1}, + {NewFunction(nil, S), NewFunction([]Type{N}, S), -1}, + {NewFunction([]Type{S}, N), NewFunction(nil, S), 1}, + {NewFunction([]Type{S}, N), NewFunction([]Type{S}, N), 0}, + } + + for _, tc := range tests { + result := Compare(tc.a, tc.b) + if result != tc.cmp { + t.Fatalf("For Compare(%v, %v) expected %v but got: %v", tc.a, tc.b, tc.cmp, result) + } + } +} + +func TestContains(t *testing.T) { + tests := []struct { + a Type + b Type + expected bool + }{ + {S, S, true}, + {A, S, true}, + {NewAny(N, B), S, false}, + {N, S, false}, + } + + for _, tc := range tests { + if Contains(tc.a, tc.b) != tc.expected { + t.Fatalf("Expected Contains(%v, %v) == %v", tc.a, tc.b, tc.expected) + } + } +} + +func TestOr(t *testing.T) { + tests := []struct { + a Type + b Type + expected Type + }{ + {nil, S, S}, + {S, nil, S}, + {Nl, Nl, Nl}, + {S, N, NewAny(N, S)}, + {A, Nl, A}, + {Nl, A, A}, + {Nl, NewAny(S, N), NewAny(S, N, Nl)}, + {A, A, A}, + {NewAny(Nl, N), A, A}, + {NewAny(N, S), NewAny(Nl, B), NewAny(Nl, B, S, N)}, + {NewAny(Nl, N), Nl, NewAny(Nl, N)}, + {NewFunction([]Type{S}, B), NewFunction([]Type{N}, B), NewFunction([]Type{NewAny(S, N)}, B)}, + } + + for _, tc := range tests { + c := Or(tc.a, tc.b) + if Compare(c, tc.expected) != 0 { + t.Fatalf("Expected Or(%v, %v) to be %v but got: %v", tc.a, tc.b, tc.expected, c) + } + } + +} + +func TestSelect(t *testing.T) { + + tests := []struct { + note string + a Type + k any + expected Type + }{ + {"static", NewArray([]Type{S}, nil), json.Number("0"), S}, + {"dynamic", NewArray(nil, S), json.Number("100"), S}, + {"out of range", NewArray([]Type{S, N, B}, nil), json.Number("4"), nil}, + {"out of range negative", NewArray([]Type{S, N, B}, nil), json.Number("-4"), nil}, + {"negative", NewArray([]Type{S, N, B}, nil), json.Number("-2"), nil}, + {"non int", NewArray([]Type{S, N, B}, nil), json.Number("1.5"), nil}, + {"non int-2", NewArray([]Type{S, N, B}, nil), 1, nil}, + {"static", NewObject([]*StaticProperty{NewStaticProperty("hello", S)}, nil), "hello", S}, + {"dynamic", NewObject([]*StaticProperty{NewStaticProperty("hello", S)}, NewDynamicProperty(S, N)), "goodbye", N}, + {"dynamic, different key types", NewObject([]*StaticProperty{NewStaticProperty("hello", S)}, NewDynamicProperty(N, N)), json.Number("2"), N}, + {"dynamic, different key types", NewObject([]*StaticProperty{NewStaticProperty("hello", S)}, NewDynamicProperty(N, N)), "hello", S}, + {"non exist", NewObject([]*StaticProperty{NewStaticProperty("hello", S)}, nil), "deadbeef", nil}, + {"non string", NewObject([]*StaticProperty{NewStaticProperty(json.Number("1"), S), NewStaticProperty(json.Number("2"), N)}, nil), json.Number("2"), N}, + {"member of", NewSet(N), json.Number("2"), N}, + {"non exist", NewSet(N), "foo", nil}, + {"superset", A, A, A}, + {"union", NewAny(NewArray(nil, N), NewArray(nil, S)), json.Number("10"), NewAny(N, S)}, + {"union set", NewSet(NewAny(S, N)), json.Number("1"), N}, + {"scalar", N, "1", nil}, + {"scalar-2", S, "1", nil}, + {"scalar-3", B, "1", nil}, + {"scalar-4", Nl, "1", nil}, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + result := Select(tc.a, tc.k) + if Compare(result, tc.expected) != 0 { + t.Fatalf("Expected Select(%v, %v) to be %v but got: %v", tc.a, tc.k, tc.expected, result) + } + }) + } +} + +func TestKeys(t *testing.T) { + tests := []struct { + note string + tpe Type + expected Type + }{ + {"array", NewArray(nil, nil), N}, + {"object", NewObject(nil, NewDynamicProperty(S, S)), S}, + {"set", NewSet(N), N}, + {"any", NewAny(NewArray(nil, nil), NewSet(S)), NewAny(S, N)}, + {"any", NewAny(NewArray(nil, nil), S), N}, + {"superset", A, A}, + {"scalar-1", N, nil}, + {"scalar-2", S, nil}, + {"scalar-3", B, nil}, + {"scalar-4", Nl, nil}, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + result := Keys(tc.tpe) + if Compare(result, tc.expected) != 0 { + t.Fatalf("Expected Keys(%v) to be %v but got: %v", tc.tpe, tc.expected, result) + } + }) + } +} + +func TestValues(t *testing.T) { + tests := []struct { + note string + tpe Type + expected Type + }{ + {"array", NewArray([]Type{N}, nil), N}, + {"array dynamic", NewArray([]Type{N, S}, B), NewAny(S, N, B)}, + {"object", NewObject([]*StaticProperty{NewStaticProperty("a", S), NewStaticProperty("b", N)}, nil), NewAny(S, N)}, + {"object dynamic", NewObject([]*StaticProperty{NewStaticProperty("a", S), NewStaticProperty("b", N)}, NewDynamicProperty(A, B)), NewAny(S, N, B)}, + {"set", NewSet(N), N}, + {"superset", A, A}, + {"any", NewAny(NewArray(nil, N), S), N}, + {"scalar-1", N, nil}, + {"scalar-2", S, nil}, + {"scalar-3", B, nil}, + {"scalar-4", Nl, nil}, + } + + for _, tc := range tests { + t.Run(tc.note, func(t *testing.T) { + result := Values(tc.tpe) + if Compare(result, tc.expected) != 0 { + t.Fatalf("Expected Keys(%v) to be %v but got: %v", tc.tpe, tc.expected, result) + } + }) + } +} + +func TestTypeOf(t *testing.T) { + tpe := TypeOf(map[any]any{ + "foo": []any{ + json.Number("1"), true, nil, "hello", + }, + }) + + exp := NewObject([]*StaticProperty{ + NewStaticProperty("foo", NewArray( + []Type{ + N, B, Nl, S, + }, nil, + )), + }, nil) + + if Compare(exp, tpe) != 0 { + t.Fatalf("Expected %v but got: %v", exp, tpe) + } +} + +func TestTypeOfMapOfString(t *testing.T) { + tpe := TypeOf(map[string]any{ + "foo": "bar", + "baz": "qux", + }) + + exp := NewObject([]*StaticProperty{ + NewStaticProperty("foo", S), + NewStaticProperty("baz", S), + }, nil) + + if Compare(exp, tpe) != 0 { + t.Fatalf("Expected %v but got: %v", exp, tpe) + } +} + +func TestNil(t *testing.T) { + + tpe := NewObject([]*StaticProperty{ + NewStaticProperty("foo", NewArray( + []Type{ + N, B, Nl, S, NewSet(nil), + }, nil, + )), + }, nil) + + if !Nil(tpe) { + t.Fatalf("Expected %v type to be unknown", tpe) + } + +} + +func TestMarshalJSON(t *testing.T) { + + tpe := NewAny( + NewObject( + []*StaticProperty{ + {"foo", N}, + {"func", NewFunction([]Type{S}, N)}, + }, + NewDynamicProperty(S, NewArray([]Type{NewSet(B)}, N)), + ), + ) + + bs, err := json.Marshal(tpe) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected := util.MustUnmarshalJSON([]byte(` + { + "type": "any", + "of": [ + { + "type": "object", + "static": [ + { + "key": "foo", + "value": {"type": "number"} + }, + { + "key": "func", + "value": { + "type": "function", + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + } + } + } + ], + "dynamic": { + "key": {"type": "string"}, + "value": { + "type": "array", + "static": [ + { + "type": "set", + "of": {"type": "boolean"} + } + ], + "dynamic": {"type": "number"} + } + } + } + ] + } + `)) + + result := util.MustUnmarshalJSON(bs) + + if !reflect.DeepEqual(expected, result) { + t.Fatalf("Expected:\n\n%s\n\nGot:\n\n%s", util.MustMarshalJSON(expected), util.MustMarshalJSON(result)) + } + +} + +func TestRoundtripJSON(t *testing.T) { + tpe := NewFunction([]Type{ + NewArray([]Type{S, Nl}, N), + NewObject( + []*StaticProperty{ + NewStaticProperty("foo", B), + }, + NewDynamicProperty(S, NewSet(N))), + NewObject( + []*StaticProperty{ + NewStaticProperty("bar", N), + }, + nil, + ), + }, NewAny(S, N)) + + bs, err := json.Marshal(tpe) + if err != nil { + t.Fatal(err) + } + + result, err := Unmarshal(bs) + if err != nil { + t.Fatal(err) + } + + if Compare(result, tpe) != 0 { + t.Fatalf("Got: %v\n\nExpected: %v", result, tpe) + } +} + +func TestRoundtripJSONVariadicFunction(t *testing.T) { + tpe := NewVariadicFunction([]Type{S}, N, nil) + bs, err := json.Marshal(tpe) + if err != nil { + t.Fatal(err) + } + + result, err := Unmarshal(bs) + if err != nil { + t.Fatal(err) + } + + if Compare(result, tpe) != 0 { + t.Fatalf("Got: %v\n\nExpected: %v", result, tpe) + } +} diff --git a/third_party/opa/v1/util/backoff.go b/third_party/opa/v1/util/backoff.go new file mode 100644 index 000000000000..1558f0cff8c9 --- /dev/null +++ b/third_party/opa/v1/util/backoff.go @@ -0,0 +1,42 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "math/rand" + "time" +) + +// DefaultBackoff returns a delay with an exponential backoff based on the +// number of retries. +func DefaultBackoff(base, maxNS float64, retries int) time.Duration { + return Backoff(base, maxNS, .2, 1.6, retries) +} + +// Backoff returns a delay with an exponential backoff based on the number of +// retries. Same algorithm used in gRPC. +func Backoff(base, maxNS, jitter, factor float64, retries int) time.Duration { + if retries == 0 { + return 0 + } + + backoff, maxNS := base, maxNS + for backoff < maxNS && retries > 0 { + backoff *= factor + retries-- + } + if backoff > maxNS { + backoff = maxNS + } + + // Randomize backoff delays so that if a cluster of requests start at + // the same time, they won't operate in lockstep. + backoff *= 1 + jitter*(rand.Float64()*2-1) + if backoff < 0 { + return 0 + } + + return time.Duration(backoff) +} diff --git a/third_party/opa/v1/util/channel.go b/third_party/opa/v1/util/channel.go new file mode 100644 index 000000000000..e2653ac7fdb0 --- /dev/null +++ b/third_party/opa/v1/util/channel.go @@ -0,0 +1,32 @@ +package util + +import ( + "github.com/open-policy-agent/opa/v1/metrics" +) + +// This prevents getting blocked forever writing to a full buffer, in case another routine fills the last space. +// Retrying maxEventRetry times to drop the oldest event. Dropping the incoming event if there still isn't room. +const maxEventRetry = 1000 + +// PushFIFO pushes data into a buffered channel without blocking when full, making room by dropping the oldest data. +// An optional metric can be recorded when data is dropped. +func PushFIFO[T any](buffer chan T, data T, metrics metrics.Metrics, metricName string) { + + for range maxEventRetry { + // non-blocking send to the buffer, to prevent blocking if buffer is full so room can be made. + select { + case buffer <- data: + return + default: + } + + // non-blocking drop from the buffer to make room for incoming event + select { + case <-buffer: + if metrics != nil && metricName != "" { + metrics.Counter(metricName).Incr() + } + default: + } + } +} diff --git a/third_party/opa/v1/util/close.go b/third_party/opa/v1/util/close.go new file mode 100644 index 000000000000..c3c177557be2 --- /dev/null +++ b/third_party/opa/v1/util/close.go @@ -0,0 +1,22 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "io" + "net/http" +) + +// Close reads the remaining bytes from the response and then closes it to +// ensure that the connection is freed. If the body is not read and closed, a +// leak can occur. +func Close(resp *http.Response) { + if resp != nil && resp.Body != nil { + if _, err := io.Copy(io.Discard, resp.Body); err != nil { + return + } + resp.Body.Close() + } +} diff --git a/third_party/opa/v1/util/compare.go b/third_party/opa/v1/util/compare.go new file mode 100644 index 000000000000..df78f6475506 --- /dev/null +++ b/third_party/opa/v1/util/compare.go @@ -0,0 +1,169 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "encoding/json" + "fmt" + "math/big" +) + +// Compare returns 0 if a equals b, -1 if a is less than b, and 1 if b is than a. +// +// For comparison between values of different types, the following ordering is used: +// nil < bool < int, float64 < string < []any < map[string]any. Slices and maps +// are compared recursively. If one slice or map is a subset of the other slice or map +// it is considered "less than". Nil is always equal to nil. +func Compare(a, b any) int { + aSortOrder := sortOrder(a) + bSortOrder := sortOrder(b) + if aSortOrder < bSortOrder { + return -1 + } else if bSortOrder < aSortOrder { + return 1 + } + switch a := a.(type) { + case nil: + return 0 + case bool: + switch b := b.(type) { + case bool: + if a == b { + return 0 + } + if !a { + return -1 + } + return 1 + } + case json.Number: + switch b := b.(type) { + case json.Number: + return compareJSONNumber(a, b) + } + case int: + switch b := b.(type) { + case int: + if a == b { + return 0 + } else if a < b { + return -1 + } + return 1 + } + case float64: + switch b := b.(type) { + case float64: + if a == b { + return 0 + } else if a < b { + return -1 + } + return 1 + } + case string: + switch b := b.(type) { + case string: + if a == b { + return 0 + } else if a < b { + return -1 + } + return 1 + } + case []any: + switch b := b.(type) { + case []any: + bLen := len(b) + aLen := len(a) + minLen := min(bLen, aLen) + for i := range minLen { + cmp := Compare(a[i], b[i]) + if cmp != 0 { + return cmp + } + } + if aLen == bLen { + return 0 + } else if aLen < bLen { + return -1 + } + return 1 + } + case map[string]any: + switch b := b.(type) { + case map[string]any: + aKeys := KeysSorted(a) + bKeys := KeysSorted(b) + aLen := len(aKeys) + bLen := len(bKeys) + minLen := min(bLen, aLen) + for i := range minLen { + if aKeys[i] < bKeys[i] { + return -1 + } else if bKeys[i] < aKeys[i] { + return 1 + } + aVal := a[aKeys[i]] + bVal := b[bKeys[i]] + cmp := Compare(aVal, bVal) + if cmp != 0 { + return cmp + } + } + if aLen == bLen { + return 0 + } else if aLen < bLen { + return -1 + } + return 1 + } + } + + panic(fmt.Sprintf("illegal arguments of type %T and type %T", a, b)) +} + +const ( + nilSort = iota + boolSort = iota + numberSort = iota + stringSort = iota + arraySort = iota + objectSort = iota +) + +func compareJSONNumber(a, b json.Number) int { + bigA, ok := new(big.Float).SetString(string(a)) + if !ok { + panic("illegal value") + } + bigB, ok := new(big.Float).SetString(string(b)) + if !ok { + panic("illegal value") + } + return bigA.Cmp(bigB) +} + +func sortOrder(v any) int { + switch v.(type) { + case nil: + return nilSort + case bool: + return boolSort + case json.Number: + return numberSort + case int: + return numberSort + case float64: + return numberSort + case string: + return stringSort + case []any: + return arraySort + case map[string]any: + return objectSort + } + panic(fmt.Sprintf("illegal argument of type %T", v)) +} diff --git a/third_party/opa/v1/util/compare_test.go b/third_party/opa/v1/util/compare_test.go new file mode 100644 index 000000000000..5d425a1eee86 --- /dev/null +++ b/third_party/opa/v1/util/compare_test.go @@ -0,0 +1,54 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "encoding/json" + "testing" +) + +func TestCompare(t *testing.T) { + + tests := []struct { + a any + b any + expected int + }{ + {nil, nil, 0}, + {nil, true, -1}, + {nil, false, -1}, + {false, false, 0}, + {false, true, -1}, + {true, true, 0}, + {true, false, 1}, + {true, json.Number("0"), -1}, + {json.Number("0"), json.Number("0"), 0}, + {json.Number("0"), json.Number("-1"), 1}, + {json.Number("-1"), json.Number("0"), -1}, + {json.Number("1.797693134862315708145274237317043567981e+308"), json.Number("4.940656458412465441765687928682213723651e-324"), 1}, + {json.Number("-1"), "", -1}, + {"", "", 0}, + {"hello", "", 1}, + {"hello world", "hello worldz", -1}, + {[]any{}, "", 1}, + {[]any{}, []any{}, 0}, + {[]any{true, false}, []any{true, nil}, 1}, + {[]any{true, true}, []any{true, true}, 0}, + {[]any{true, false}, []any{true, true}, -1}, + {map[string]any{}, []any{}, 1}, + {map[string]any{"foo": []any{true, false}, "bar": []any{true, true}}, map[string]any{"foo": []any{true, false}, "bar": []any{true, true}}, 0}, + {map[string]any{"foo": []any{true, false}, "bar": []any{true, nil}}, map[string]any{"foo": []any{true, false}, "bar": []any{true, true}}, -1}, + {map[string]any{"foo": []any{true, true}, "bar": []any{true, true}}, map[string]any{"foo": []any{true, false}, "bar": []any{true, true}}, 1}, + {map[string]any{"foo": true, "barr": false}, map[string]any{"foo": true, "bar": false}, 1}, + {map[string]any{"foo": true, "bar": false, "qux": false}, map[string]any{"foo": true, "bar": false}, 1}, + {map[string]any{"foo": true, "bar": false, "baz": false}, map[string]any{"foo": true, "bar": false}, -1}, + } + for i, tc := range tests { + result := Compare(tc.a, tc.b) + if result != tc.expected { + t.Errorf("Test case %d: expected %d but got: %d", i, tc.expected, result) + } + } +} diff --git a/third_party/opa/v1/util/decoding/context.go b/third_party/opa/v1/util/decoding/context.go new file mode 100644 index 000000000000..a817680f179b --- /dev/null +++ b/third_party/opa/v1/util/decoding/context.go @@ -0,0 +1,31 @@ +package decoding + +import "context" + +type requestContextKey string + +// Note(philipc): We can add functions later to add the max request body length +// to contexts, if we ever need to. +const ( + reqCtxKeyMaxLen = requestContextKey("server-decoding-plugin-context-max-length") + reqCtxKeyGzipMaxLen = requestContextKey("server-decoding-plugin-context-gzip-max-length") +) + +func AddServerDecodingMaxLen(ctx context.Context, maxLen int64) context.Context { + return context.WithValue(ctx, reqCtxKeyMaxLen, maxLen) +} + +func AddServerDecodingGzipMaxLen(ctx context.Context, maxLen int64) context.Context { + return context.WithValue(ctx, reqCtxKeyGzipMaxLen, maxLen) +} + +// Used for enforcing max body content limits when dealing with chunked requests. +func GetServerDecodingMaxLen(ctx context.Context) (int64, bool) { + maxLength, ok := ctx.Value(reqCtxKeyMaxLen).(int64) + return maxLength, ok +} + +func GetServerDecodingGzipMaxLen(ctx context.Context) (int64, bool) { + gzipMaxLength, ok := ctx.Value(reqCtxKeyGzipMaxLen).(int64) + return gzipMaxLength, ok +} diff --git a/third_party/opa/v1/util/doc.go b/third_party/opa/v1/util/doc.go new file mode 100644 index 000000000000..900dff8c1f0a --- /dev/null +++ b/third_party/opa/v1/util/doc.go @@ -0,0 +1,6 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package util provides generic utilities used throughout the policy engine. +package util diff --git a/third_party/opa/v1/util/enumflag.go b/third_party/opa/v1/util/enumflag.go new file mode 100644 index 000000000000..4796f0269640 --- /dev/null +++ b/third_party/opa/v1/util/enumflag.go @@ -0,0 +1,59 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "fmt" + "strings" +) + +// EnumFlag implements the pflag.Value interface to provide enumerated command +// line parameter values. +type EnumFlag struct { + defaultValue string + vs []string + i int +} + +// NewEnumFlag returns a new EnumFlag that has a defaultValue and vs enumerated +// values. +func NewEnumFlag(defaultValue string, vs []string) *EnumFlag { + f := &EnumFlag{ + i: -1, + vs: vs, + defaultValue: defaultValue, + } + return f +} + +// Type returns the valid enumeration values. +func (f *EnumFlag) Type() string { + return "{" + strings.Join(f.vs, ",") + "}" +} + +// String returns the EnumValue's value as string. +func (f *EnumFlag) String() string { + if f.i == -1 { + return f.defaultValue + } + return f.vs[f.i] +} + +// IsSet will return true if the EnumFlag has been set. +func (f *EnumFlag) IsSet() bool { + return f.i != -1 +} + +// Set sets the enum value. If s is not a valid enum value, an error is +// returned. +func (f *EnumFlag) Set(s string) error { + for i := range f.vs { + if f.vs[i] == s { + f.i = i + return nil + } + } + return fmt.Errorf("must be one of %v", f.Type()) +} diff --git a/third_party/opa/v1/util/enumflag_test.go b/third_party/opa/v1/util/enumflag_test.go new file mode 100644 index 000000000000..569486954ccc --- /dev/null +++ b/third_party/opa/v1/util/enumflag_test.go @@ -0,0 +1,43 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "strings" + "testing" +) + +func TestEnumFlag(t *testing.T) { + + flag := NewEnumFlag("foo", []string{"foo", "bar", "baz"}) + + if flag.String() != "foo" { + t.Fatalf("Expected default value to be foo but got: %v", flag.String()) + } + + if flag.IsSet() { + t.Fatalf("Expected IsSet() to be false") + } + + if err := flag.Set("bar"); err != nil { + t.Fatalf("Unexpected error on set: %v", err) + } + + if flag.String() != "bar" { + t.Fatalf("Expected value to be bar but got: %v", flag.String()) + } + + if !flag.IsSet() { + t.Fatalf("Expected IsSet() to be true") + } + + if !strings.Contains(flag.Type(), "foo,bar,baz") { + t.Fatalf("Expected flag type to contain foo,bar,baz but got: %v", flag.Type()) + } + + if err := flag.Set("deadbeef"); err == nil { + t.Fatalf("Expected error from set") + } +} diff --git a/third_party/opa/v1/util/graph.go b/third_party/opa/v1/util/graph.go new file mode 100644 index 000000000000..f0e824245494 --- /dev/null +++ b/third_party/opa/v1/util/graph.go @@ -0,0 +1,90 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +// Traversal defines a basic interface to perform traversals. +type Traversal interface { + + // Edges should return the neighbours of node "u". + Edges(u T) []T + + // Visited should return true if node "u" has already been visited in this + // traversal. If the same traversal is used multiple times, the state that + // tracks visited nodes should be reset. + Visited(u T) bool +} + +// Equals should return true if node "u" equals node "v". +type Equals func(u T, v T) bool + +// Iter should return true to indicate stop. +type Iter func(u T) bool + +// DFS performs a depth first traversal calling f for each node starting from u. +// If f returns true, traversal stops and DFS returns true. +func DFS(t Traversal, f Iter, u T) bool { + lifo := NewLIFO(u) + for lifo.Size() > 0 { + next, _ := lifo.Pop() + if t.Visited(next) { + continue + } + if f(next) { + return true + } + for _, v := range t.Edges(next) { + lifo.Push(v) + } + } + return false +} + +// BFS performs a breadth first traversal calling f for each node starting from +// u. If f returns true, traversal stops and BFS returns true. +func BFS(t Traversal, f Iter, u T) bool { + fifo := NewFIFO(u) + for fifo.Size() > 0 { + next, _ := fifo.Pop() + if t.Visited(next) { + continue + } + if f(next) { + return true + } + for _, v := range t.Edges(next) { + fifo.Push(v) + } + } + return false +} + +// DFSPath returns a path from node a to node z found by performing +// a depth first traversal. If no path is found, an empty slice is returned. +func DFSPath(t Traversal, eq Equals, a, z T) []T { + p := dfsRecursive(t, eq, a, z, []T{}) + for i := len(p)/2 - 1; i >= 0; i-- { + o := len(p) - i - 1 + p[i], p[o] = p[o], p[i] + } + return p +} + +func dfsRecursive(t Traversal, eq Equals, u, z T, path []T) []T { + if t.Visited(u) { + return path + } + for _, v := range t.Edges(u) { + if eq(v, z) { + path = append(path, z) + path = append(path, u) + return path + } + if p := dfsRecursive(t, eq, v, z, path); len(p) > 0 { + path = append(p, u) + return path + } + } + return path +} diff --git a/third_party/opa/v1/util/graph_test.go b/third_party/opa/v1/util/graph_test.go new file mode 100644 index 000000000000..9c05b3e31d78 --- /dev/null +++ b/third_party/opa/v1/util/graph_test.go @@ -0,0 +1,176 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "reflect" + "slices" + "testing" +) + +type testTraversal struct { + g map[int][]int + visited map[int]struct{} + ordered []int + stop *int +} + +func newTestTraversal(g map[int][]int) *testTraversal { + return &testTraversal{ + g: g, + visited: map[int]struct{}{}, + ordered: nil, + stop: nil, + } +} + +func (t *testTraversal) Edges(x T) []T { + r := []T{} + for _, v := range t.g[x.(int)] { + r = append(r, v) + } + return r +} + +func (*testTraversal) Equals(a, b T) bool { + return a.(int) == b.(int) +} + +func (t *testTraversal) Iter(x T) bool { + t.ordered = append(t.ordered, x.(int)) + return t.stop != nil && *t.stop == x.(int) +} + +func (t *testTraversal) Visited(x T) bool { + _, ok := t.visited[x.(int)] + t.visited[x.(int)] = struct{}{} + return ok +} + +func TestDFSStop(t *testing.T) { + g := map[int][]int{ + 1: {2, 3}, + 2: {4, 5}, + 3: {6, 7}, + 6: {2}, + } + + t1 := newTestTraversal(g) + stop := 6 + t1.stop = &stop + + stopped := DFS(t1, t1.Iter, 1) + + if !stopped { + t.Fatalf("Expected DFS to stop but got: %v", t1.ordered) + } + + expected := []int{1, 3, 7, 6} + + if !slices.Equal(expected, t1.ordered) { + t.Fatalf("Expected DFS ordering %v but got: %v", expected, t1.ordered) + } +} + +func TestBFSStop(t *testing.T) { + g := map[int][]int{ + 1: {2, 3}, + 2: {4, 5}, + 3: {6, 7}, + 6: {2}, + } + + t1 := newTestTraversal(g) + stop := 4 + t1.stop = &stop + + stopped := BFS(t1, t1.Iter, 1) + + if !stopped { + t.Fatalf("Expected DFS to stop but got: %v", t1.ordered) + } + + expected := []int{1, 2, 3, 4} + + if !slices.Equal(expected, t1.ordered) { + t.Fatalf("Expected DFS ordering %v but got: %v", expected, t1.ordered) + } +} + +func TestDFS(t *testing.T) { + g := map[int][]int{ + 1: {2, 3}, + 2: {4, 5}, + 3: {6, 7}, + 6: {2}, + } + + t1 := newTestTraversal(g) + + stopped := DFS(t1, t1.Iter, 1) + if stopped { + t.Fatalf("Did not expect traversal to stop") + } + + expected := []int{1, 3, 7, 6, 2, 5, 4} + + if !slices.Equal(expected, t1.ordered) { + t.Fatalf("Expected DFS ordering %v but got: %v", expected, t1.ordered) + } +} + +func TestBFS(t *testing.T) { + g := map[int][]int{ + 1: {2, 3}, + 2: {4, 5}, + 3: {6, 7}, + 6: {2}, + } + + t1 := newTestTraversal(g) + + stopped := BFS(t1, t1.Iter, 1) + if stopped { + t.Fatalf("Did not expect traversal to stop") + } + + expected := []int{1, 2, 3, 4, 5, 6, 7} + + if !slices.Equal(expected, t1.ordered) { + t.Fatalf("Expected DFS ordering %v but got: %v", expected, t1.ordered) + } + +} + +func TestDFSPath(t *testing.T) { + + g := map[int][]int{ + 1: {2}, + 2: {3, 4}, + 3: {2}, + 4: {1}, + } + + t1 := newTestTraversal(g) + p1 := DFSPath(t1, t1.Equals, 1, 2) + + if !reflect.DeepEqual(p1, []T{1, 2}) { + t.Errorf("Expected DFS(1,2) to equal {1,2} but got: %v", p1) + } + + t2 := newTestTraversal(g) + p2 := DFSPath(t2, t2.Equals, 1, 4) + + if !reflect.DeepEqual(p2, []T{1, 2, 4}) { + t.Errorf("Expected DFS(1,4) to equal {1,2,4} but got: %v", p2) + } + + t3 := newTestTraversal(g) + p3 := DFSPath(t3, t3.Equals, 1, 0xadbeef) + if len(p3) != 0 { + t.Errorf("Expected DFS(1,0xadbeef to be empty but got: %v", p3) + } + +} diff --git a/third_party/opa/v1/util/hashmap.go b/third_party/opa/v1/util/hashmap.go new file mode 100644 index 000000000000..69a90cbb53db --- /dev/null +++ b/third_party/opa/v1/util/hashmap.go @@ -0,0 +1,271 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "fmt" + "strings" +) + +// T is a concise way to refer to T. +type T any + +type Hasher interface { + Hash() int +} + +type hashEntry[K any, V any] struct { + k K + v V + next *hashEntry[K, V] +} + +// TypedHashMap represents a key/value map. +type TypedHashMap[K any, V any] struct { + keq func(K, K) bool + veq func(V, V) bool + khash func(K) int + vhash func(V) int + def V + table map[int]*hashEntry[K, V] + size int +} + +// NewTypedHashMap returns a new empty TypedHashMap. +func NewTypedHashMap[K any, V any](keq func(K, K) bool, veq func(V, V) bool, khash func(K) int, vhash func(V) int, def V) *TypedHashMap[K, V] { + return &TypedHashMap[K, V]{ + keq: keq, + veq: veq, + khash: khash, + vhash: vhash, + def: def, + table: make(map[int]*hashEntry[K, V]), + size: 0, + } +} + +// HashMap represents a key/value map. +type HashMap = TypedHashMap[T, T] + +// NewHashMap returns a new empty HashMap. +func NewHashMap(eq func(T, T) bool, hash func(T) int) *HashMap { + return &HashMap{ + keq: eq, + veq: eq, + khash: hash, + vhash: hash, + def: nil, + table: make(map[int]*hashEntry[T, T]), + size: 0, + } +} + +// Copy returns a shallow copy of this HashMap. +func (h *TypedHashMap[K, V]) Copy() *TypedHashMap[K, V] { + cpy := NewTypedHashMap(h.keq, h.veq, h.khash, h.vhash, h.def) + h.Iter(func(k K, v V) bool { + cpy.Put(k, v) + return false + }) + return cpy +} + +// Equal returns true if this HashMap equals the other HashMap. +// Two hash maps are equal if they contain the same key/value pairs. +func (h *TypedHashMap[K, V]) Equal(other *TypedHashMap[K, V]) bool { + if h.Len() != other.Len() { + return false + } + return !h.Iter(func(k K, v V) bool { + ov, ok := other.Get(k) + if !ok { + return true + } + return !h.veq(v, ov) + }) +} + +// Get returns the value for k. +func (h *TypedHashMap[K, V]) Get(k K) (V, bool) { + hash := h.khash(k) + for entry := h.table[hash]; entry != nil; entry = entry.next { + if h.keq(entry.k, k) { + return entry.v, true + } + } + return h.def, false +} + +// Delete removes the key k. +func (h *TypedHashMap[K, V]) Delete(k K) { + hash := h.khash(k) + var prev *hashEntry[K, V] + for entry := h.table[hash]; entry != nil; entry = entry.next { + if h.keq(entry.k, k) { + if prev != nil { + prev.next = entry.next + } else { + h.table[hash] = entry.next + } + h.size-- + return + } + prev = entry + } +} + +// Hash returns the hash code for this hash map. +func (h *TypedHashMap[K, V]) Hash() int { + var hash int + h.Iter(func(k K, v V) bool { + hash += h.khash(k) + h.vhash(v) + return false + }) + return hash +} + +// Iter invokes the iter function for each element in the HashMap. +// If the iter function returns true, iteration stops and the return value is true. +// If the iter function never returns true, iteration proceeds through all elements +// and the return value is false. +func (h *TypedHashMap[K, V]) Iter(iter func(K, V) bool) bool { + for _, entry := range h.table { + for ; entry != nil; entry = entry.next { + if iter(entry.k, entry.v) { + return true + } + } + } + return false +} + +// Len returns the current size of this HashMap. +func (h *TypedHashMap[K, V]) Len() int { + return h.size +} + +// Put inserts a key/value pair into this HashMap. If the key is already present, the existing +// value is overwritten. +func (h *TypedHashMap[K, V]) Put(k K, v V) { + hash := h.khash(k) + head := h.table[hash] + for entry := head; entry != nil; entry = entry.next { + if h.keq(entry.k, k) { + entry.v = v + return + } + } + h.table[hash] = &hashEntry[K, V]{k: k, v: v, next: head} + h.size++ +} + +func (h *TypedHashMap[K, V]) String() string { + var buf []string + h.Iter(func(k K, v V) bool { + buf = append(buf, fmt.Sprintf("%v: %v", k, v)) + return false + }) + return "{" + strings.Join(buf, ", ") + "}" +} + +// Update returns a new HashMap with elements from the other HashMap put into this HashMap. +// If the other HashMap contains elements with the same key as this HashMap, the value +// from the other HashMap overwrites the value from this HashMap. +func (h *TypedHashMap[K, V]) Update(other *TypedHashMap[K, V]) *TypedHashMap[K, V] { + updated := h.Copy() + other.Iter(func(k K, v V) bool { + updated.Put(k, v) + return false + }) + return updated +} + +type hasherEntry[K Hasher, V any] struct { + k K + v V + next *hasherEntry[K, V] +} + +// HasherMap represents a simpler version of TypedHashMap that uses Hasher's +// for keys, and requires only an equality function for keys. Ideally we'd have +// and Equal method for all key types too, and we could get rid of that requirement. +type HasherMap[K Hasher, V any] struct { + keq func(K, K) bool + table map[int]*hasherEntry[K, V] + size int +} + +// NewHasherMap returns a new empty HasherMap. +func NewHasherMap[K Hasher, V any](keq func(K, K) bool) *HasherMap[K, V] { + return &HasherMap[K, V]{ + keq: keq, + table: make(map[int]*hasherEntry[K, V]), + size: 0, + } +} + +// Get returns the value for k. +func (h *HasherMap[K, V]) Get(k K) (V, bool) { + for entry := h.table[k.Hash()]; entry != nil; entry = entry.next { + if h.keq(entry.k, k) { + return entry.v, true + } + } + var zero V + return zero, false +} + +// Put inserts a key/value pair into this HashMap. If the key is already present, the existing +// value is overwritten. +func (h *HasherMap[K, V]) Put(k K, v V) { + hash := k.Hash() + head := h.table[hash] + for entry := head; entry != nil; entry = entry.next { + if h.keq(entry.k, k) { + entry.v = v + return + } + } + h.table[hash] = &hasherEntry[K, V]{k: k, v: v, next: head} + h.size++ +} + +// Delete removes the key k. +func (h *HasherMap[K, V]) Delete(k K) { + hash := k.Hash() + var prev *hasherEntry[K, V] + for entry := h.table[hash]; entry != nil; entry = entry.next { + if h.keq(entry.k, k) { + if prev != nil { + prev.next = entry.next + } else { + h.table[hash] = entry.next + } + h.size-- + return + } + prev = entry + } +} + +// Iter invokes the iter function for each element in the HasherMap. +// If the iter function returns true, iteration stops and the return value is true. +// If the iter function never returns true, iteration proceeds through all elements +// and the return value is false. +func (h *HasherMap[K, V]) Iter(iter func(K, V) bool) bool { + for _, entry := range h.table { + for ; entry != nil; entry = entry.next { + if iter(entry.k, entry.v) { + return true + } + } + } + return false +} + +// Len returns the current size of this HashMap. +func (h *HasherMap[K, V]) Len() int { + return h.size +} diff --git a/third_party/opa/v1/util/hashmap_test.go b/third_party/opa/v1/util/hashmap_test.go new file mode 100644 index 000000000000..50dbf89832ba --- /dev/null +++ b/third_party/opa/v1/util/hashmap_test.go @@ -0,0 +1,182 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// nolint: goconst // string duplication is for test readability. +package util + +import ( + "fmt" + "hash/fnv" + "reflect" + "testing" +) + +func TestHashMapPutDelete(t *testing.T) { + m := stringHashMap() + m.Put("a", "b") + m.Put("b", "c") + m.Delete("b") + r, _ := m.Get("a") + if r != "b" { + t.Fatal("Expected a to be intact") + } + r, ok := m.Get("b") + if ok { + t.Fatalf("Expected b to be removed: %v", r) + } + m.Delete("b") + r, _ = m.Get("a") + if r != "b" { + t.Fatal("Expected a to be intact") + } +} + +func TestHashMapOverwrite(t *testing.T) { + m := stringHashMap() + key := "hello" + expected := "goodbye" + m.Put(key, "world") + m.Put(key, expected) + result, _ := m.Get(key) + if result != expected { + t.Errorf("Expected existing value to be overwritten but got %v for key %v", result, key) + } +} + +func TestHashMapIter(t *testing.T) { + m := NewHashMap(func(a, b T) bool { + n1 := a.(float64) + n2 := b.(float64) + return n1 == n2 + }, func(v T) int { + n := v.(float64) + return int(n) + }) + keys := []float64{1, 2, 1.4} + value := struct{}{} + for _, k := range keys { + m.Put(k, value) + } + // 1 and 1.4 should both hash to 1. + if len(m.table) != 2 { + panic(fmt.Sprintf("Expected collision: %v", m)) + } + results := map[T]T{} + m.Iter(func(k T, v T) bool { + results[k] = v + return false + }) + expected := map[T]T{ + float64(1): value, + float64(2): value, + float64(1.4): value, + } + if !reflect.DeepEqual(results, expected) { + t.Errorf("Expected %v but got %v", expected, results) + } +} + +func TestHashMapCompare(t *testing.T) { + m := stringHashMap() + n := stringHashMap() + k1 := "k1" + k2 := "k2" + k3 := "k3" + v1 := "hello" + v2 := "goodbye" + + m.Put(k1, v1) + if m.Equal(n) { + t.Errorf("Expected hash maps of different size to be non-equal for %v and %v", m, n) + return + } + n.Put(k1, v1) + if m.Hash() != n.Hash() { + t.Errorf("Expected hashes to equal for %v and %v", m, n) + return + } + if !m.Equal(n) { + t.Errorf("Expected hash maps to be equal for %v and %v", m, n) + return + } + m.Put(k2, v2) + n.Put(k3, v2) + if m.Hash() == n.Hash() { + t.Errorf("Did not expect hashes to equal for %v and %v", m, n) + return + } + if m.Equal(n) { + t.Errorf("Did not expect hash maps to be equal for %v and %v", m, n) + } +} + +func TestHashMapCopy(t *testing.T) { + m := stringHashMap() + + k1 := "k1" + k2 := "k2" + v1 := "hello" + v2 := "goodbye" + + m.Put(k1, v1) + m.Put(k2, v2) + + n := m.Copy() + + if !n.Equal(m) { + t.Errorf("Expected hash maps to be equal: %v != %v", n, m) + return + } + + m.Put(k2, "world") + + if n.Equal(m) { + t.Errorf("Expected hash maps to be non-equal: %v == %v", n, m) + } +} + +func TestHashMapUpdate(t *testing.T) { + m := stringHashMap() + n := stringHashMap() + x := stringHashMap() + + k1 := "k1" + k2 := "k2" + v1 := "hello" + v2 := "goodbye" + + m.Put(k1, v1) + n.Put(k2, v2) + x.Put(k1, v1) + x.Put(k2, v2) + + o := n.Update(m) + + if !x.Equal(o) { + t.Errorf("Expected update to merge hash maps: %v != %v", x, o) + } +} + +func TestHashMapString(t *testing.T) { + x := stringHashMap() + x.Put("x", "y") + str := x.String() + exp := "{x: y}" + if exp != str { + t.Errorf("expected x.String() == {x: y}: %v != %v", exp, str) + } +} + +func stringHashMap() *HashMap { + return NewHashMap(func(a, b T) bool { + s1 := a.(string) + s2 := b.(string) + return s1 == s2 + }, func(v T) int { + s := v.(string) + h := fnv.New64a() + h.Write([]byte(s)) + return int(h.Sum64()) + }) +} diff --git a/third_party/opa/v1/util/json.go b/third_party/opa/v1/util/json.go new file mode 100644 index 000000000000..fdb2626c7869 --- /dev/null +++ b/third_party/opa/v1/util/json.go @@ -0,0 +1,133 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "reflect" + + "sigs.k8s.io/yaml" + + "github.com/open-policy-agent/opa/v1/loader/extension" +) + +// UnmarshalJSON parses the JSON encoded data and stores the result in the value +// pointed to by x. +// +// This function is intended to be used in place of the standard json.Marshal +// function when json.Number is required. +func UnmarshalJSON(bs []byte, x any) error { + return unmarshalJSON(bs, x, true) +} + +func unmarshalJSON(bs []byte, x any, ext bool) error { + buf := bytes.NewBuffer(bs) + decoder := NewJSONDecoder(buf) + if err := decoder.Decode(x); err != nil { + if handler := extension.FindExtension(".json"); handler != nil && ext { + return handler(bs, x) + } + return err + } + + // Since decoder.Decode validates only the first json structure in bytes, + // check if decoder has more bytes to consume to validate whole input bytes. + tok, err := decoder.Token() + if tok != nil { + return fmt.Errorf("error: invalid character '%s' after top-level value", tok) + } + if err != nil && err != io.EOF { + return err + } + return nil +} + +// NewJSONDecoder returns a new decoder that reads from r. +// +// This function is intended to be used in place of the standard json.NewDecoder +// when json.Number is required. +func NewJSONDecoder(r io.Reader) *json.Decoder { + decoder := json.NewDecoder(r) + decoder.UseNumber() + return decoder +} + +// MustUnmarshalJSON parse the JSON encoded data and returns the result. +// +// If the data cannot be decoded, this function will panic. This function is for +// test purposes. +func MustUnmarshalJSON(bs []byte) any { + var x any + if err := UnmarshalJSON(bs, &x); err != nil { + panic(err) + } + return x +} + +// MustMarshalJSON returns the JSON encoding of x +// +// If the data cannot be encoded, this function will panic. This function is for +// test purposes. +func MustMarshalJSON(x any) []byte { + bs, err := json.Marshal(x) + if err != nil { + panic(err) + } + return bs +} + +// RoundTrip encodes to JSON, and decodes the result again. +// +// Thereby, it is converting its argument to the representation expected by +// rego.Input and inmem's Write operations. Works with both references and +// values. +func RoundTrip(x *any) error { + bs, err := json.Marshal(x) + if err != nil { + return err + } + return UnmarshalJSON(bs, x) +} + +// Reference returns a pointer to its argument unless the argument already is +// a pointer. If the argument is **t, or ***t, etc, it will return *t. +// +// Used for preparing Go types (including pointers to structs) into values to be +// put through util.RoundTrip(). +func Reference(x any) *any { + var y any + rv := reflect.ValueOf(x) + if rv.Kind() == reflect.Ptr { + return Reference(rv.Elem().Interface()) + } + if rv.Kind() != reflect.Invalid { + y = rv.Interface() + return &y + } + return &x +} + +// Unmarshal decodes a YAML, JSON or JSON extension value into the specified type. +func Unmarshal(bs []byte, v any) error { + if len(bs) > 2 && bs[0] == 0xef && bs[1] == 0xbb && bs[2] == 0xbf { + bs = bs[3:] // Strip UTF-8 BOM, see https://www.rfc-editor.org/rfc/rfc8259#section-8.1 + } + + if json.Valid(bs) { + return unmarshalJSON(bs, v, false) + } + nbs, err := yaml.YAMLToJSON(bs) + if err == nil { + return unmarshalJSON(nbs, v, false) + } + // not json or yaml: try extensions + if handler := extension.FindExtension(".json"); handler != nil { + return handler(bs, v) + } + return err +} diff --git a/third_party/opa/v1/util/json_test.go b/third_party/opa/v1/util/json_test.go new file mode 100644 index 000000000000..6f131eaa0efb --- /dev/null +++ b/third_party/opa/v1/util/json_test.go @@ -0,0 +1,138 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util_test + +import ( + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/open-policy-agent/opa/v1/util" +) + +func TestInvalidJSONInput(t *testing.T) { + cases := [][]byte{ + []byte("{ \"k\": 1 }\n{}}"), + []byte("{ \"k\": 1 }\n!!!}"), + } + for _, tc := range cases { + var x any + err := util.UnmarshalJSON(tc, &x) + if err == nil { + t.Errorf("should be an error") + } + } +} + +func TestRoundTrip(t *testing.T) { + cases := []any{ + nil, + 1, + 1.1, + false, + []int{1}, + []bool{true}, + []string{"foo"}, + map[string]string{"foo": "bar"}, + struct { + F string `json:"foo"` + B int `json:"bar"` + }{"x", 32}, + map[string][]int{ + "ones": {1, 1, 1}, + }, + } + for _, tc := range cases { + t.Run(fmt.Sprintf("input %v", tc), func(t *testing.T) { + err := util.RoundTrip(&tc) + if err != nil { + t.Errorf("expected error=nil, got %s", err.Error()) + } + switch x := tc.(type) { + // These are the output types we want, nothing else + case nil, bool, json.Number, int64, float64, int, string, []any, + []string, map[string]any, map[string]string: + default: + t.Errorf("unexpected type %T", x) + } + }) + } +} + +func TestReference(t *testing.T) { + cases := []any{ + nil, + func() any { f := any(nil); return &f }(), + 1, + func() any { f := 1; return &f }(), + 1.1, + func() any { f := 1.1; return &f }(), + false, + func() any { f := false; return &f }(), + []int{1}, + &[]int{1}, + func() any { f := &[]int{1}; return &f }(), + []bool{true}, + &[]bool{true}, + func() any { f := &[]bool{true}; return &f }(), + []string{"foo"}, + &[]string{"foo"}, + func() any { f := &[]string{"foo"}; return &f }(), + map[string]string{"foo": "bar"}, + &map[string]string{"foo": "bar"}, + func() any { f := &map[string]string{"foo": "bar"}; return &f }(), + struct { + F string `json:"foo"` + B int `json:"bar"` + }{"x", 32}, + &struct { + F string `json:"foo"` + B int `json:"bar"` + }{"x", 32}, + map[string][]int{ + "ones": {1, 1, 1}, + }, + &map[string][]int{ + "ones": {1, 1, 1}, + }, + } + for _, tc := range cases { + t.Run(fmt.Sprintf("input %v", tc), func(t *testing.T) { + ref := util.Reference(tc) + rv := reflect.ValueOf(ref) + if rv.Kind() != reflect.Ptr { + t.Fatalf("expected pointer, got %v", rv.Kind()) + } + if rv.Elem().Kind() == reflect.Ptr { + t.Error("expected non-pointer element") + } + }) + } +} + +// There's valid JSON that doesn't pass through yaml.YAMLToJSON. +// See https://github.com/open-policy-agent/opa/issues/4673 +func TestInvalidYAMLValidJSON(t *testing.T) { + x := []byte{0x22, 0x3a, 0xc2, 0x9a, 0x22} + y := "" + if err := util.Unmarshal(x, &y); err != nil { + t.Fatal(err) + } +} + +func TestUnmarshalJSONUTF8BOM(t *testing.T) { + bomFail := []byte{0xef, 0xbb, 0xbf, 0x22, 0x5c, 0x2f, 0x22, 0x0a} // "\/" preceded by UTF-8 BOM + + if json.Valid(bomFail) { + t.Fatal("expected invalid JSON") + } + + var x any + err := util.Unmarshal(bomFail, &x) + if err != nil { + t.Fatal("expected BOM to be stripped", err) + } +} diff --git a/third_party/opa/v1/util/maps.go b/third_party/opa/v1/util/maps.go new file mode 100644 index 000000000000..c56fbe98ac85 --- /dev/null +++ b/third_party/opa/v1/util/maps.go @@ -0,0 +1,34 @@ +package util + +import ( + "cmp" + "slices" +) + +// Keys returns a slice of keys from any map. +func Keys[M ~map[K]V, K comparable, V any](m M) []K { + r := make([]K, 0, len(m)) + for k := range m { + r = append(r, k) + } + return r +} + +// KeysSorted returns a slice of keys from any map, sorted in ascending order. +func KeysSorted[M ~map[K]V, K cmp.Ordered, V any](m M) []K { + r := make([]K, 0, len(m)) + for k := range m { + r = append(r, k) + } + slices.Sort(r) + return r +} + +// Values returns a slice of values from any map. Copied from golang.org/x/exp/maps. +func Values[M ~map[K]V, K comparable, V any](m M) []V { + r := make([]V, 0, len(m)) + for _, v := range m { + r = append(r, v) + } + return r +} diff --git a/third_party/opa/v1/util/maps_test.go b/third_party/opa/v1/util/maps_test.go new file mode 100644 index 000000000000..fabb04bf6f9d --- /dev/null +++ b/third_party/opa/v1/util/maps_test.go @@ -0,0 +1,18 @@ +package util + +import ( + "slices" + "testing" +) + +func TestValues(t *testing.T) { + testMap := map[string]int{"a": 1, "b": 2, "c": 3} + values := Values(testMap) + if len(values) != 3 { + t.Errorf("Expected 3 values, got %d", len(values)) + } + slices.Sort(values) + if values[0] != 1 || values[1] != 2 || values[2] != 3 { + t.Errorf("Expected [1, 2, 3], got %v", values) + } +} diff --git a/third_party/opa/v1/util/performance.go b/third_party/opa/v1/util/performance.go new file mode 100644 index 000000000000..e9b446818877 --- /dev/null +++ b/third_party/opa/v1/util/performance.go @@ -0,0 +1,75 @@ +package util + +import ( + "math" + "slices" + "unsafe" +) + +// NewPtrSlice returns a slice of pointers to T with length n, +// with only 2 allocations performed no matter the size of n. +// See: +// https://gist.github.com/CAFxX/e96e8a5c3841d152f16d266a1fe7f8bd#slices-of-pointers +func NewPtrSlice[T any](n int) []*T { + return GrowPtrSlice[T](nil, n) +} + +// GrowPtrSlice appends n elements to the slice, each pointing to +// a newly-allocated T. The resulting slice has length equal to len(s)+n. +// +// It performs at most 2 allocations, regardless of n. +func GrowPtrSlice[T any](s []*T, n int) []*T { + s = slices.Grow(s, n) + p := make([]T, n) + for i := range n { + s = append(s, &p[i]) + } + return s +} + +// Allocation free conversion from []byte to string (unsafe) +// Note that the byte slice must not be modified after conversion +func ByteSliceToString(bs []byte) string { + return unsafe.String(unsafe.SliceData(bs), len(bs)) +} + +// Allocation free conversion from ~string to []byte (unsafe) +// Note that the byte slice must not be modified after conversion +func StringToByteSlice[T ~string](s T) []byte { + return unsafe.Slice(unsafe.StringData(string(s)), len(s)) +} + +// NumDigitsInt returns the number of digits in n. +// This is useful for pre-allocating buffers for string conversion. +func NumDigitsInt(n int) int { + if n == 0 { + return 1 + } + + if n < 0 { + n = -n + } + + return int(math.Log10(float64(n))) + 1 +} + +// NumDigitsUint returns the number of digits in n. +// This is useful for pre-allocating buffers for string conversion. +func NumDigitsUint(n uint64) int { + if n == 0 { + return 1 + } + + return int(math.Log10(float64(n))) + 1 +} + +// KeysCount returns the number of keys in m that satisfy predicate p. +func KeysCount[K comparable, V any](m map[K]V, p func(K) bool) int { + count := 0 + for k := range m { + if p(k) { + count++ + } + } + return count +} diff --git a/third_party/opa/v1/util/performance_test.go b/third_party/opa/v1/util/performance_test.go new file mode 100644 index 000000000000..d04c10bccd79 --- /dev/null +++ b/third_party/opa/v1/util/performance_test.go @@ -0,0 +1,17 @@ +package util + +import "testing" + +type testStruct struct { + foo int +} + +func BenchmarkNewPtrSlice(b *testing.B) { + b.ReportAllocs() + for range b.N { + s := NewPtrSlice[testStruct](100) + for j := range 100 { + s[j].foo = j + } + } +} diff --git a/third_party/opa/v1/util/queue.go b/third_party/opa/v1/util/queue.go new file mode 100644 index 000000000000..63a2ffc16aa8 --- /dev/null +++ b/third_party/opa/v1/util/queue.go @@ -0,0 +1,113 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +// LIFO represents a simple LIFO queue. +type LIFO struct { + top *queueNode + size int +} + +type queueNode struct { + v T + next *queueNode +} + +// NewLIFO returns a new LIFO queue containing elements ts starting with the +// left-most argument at the bottom. +func NewLIFO(ts ...T) *LIFO { + s := &LIFO{} + for i := range ts { + s.Push(ts[i]) + } + return s +} + +// Push adds a new element onto the LIFO. +func (s *LIFO) Push(t T) { + node := &queueNode{v: t, next: s.top} + s.top = node + s.size++ +} + +// Peek returns the top of the LIFO. If LIFO is empty, returns nil, false. +func (s *LIFO) Peek() (T, bool) { + if s.top == nil { + return nil, false + } + return s.top.v, true +} + +// Pop returns the top of the LIFO and removes it. If LIFO is empty returns +// nil, false. +func (s *LIFO) Pop() (T, bool) { + if s.top == nil { + return nil, false + } + node := s.top + s.top = node.next + s.size-- + return node.v, true +} + +// Size returns the size of the LIFO. +func (s *LIFO) Size() int { + return s.size +} + +// FIFO represents a simple FIFO queue. +type FIFO struct { + front *queueNode + back *queueNode + size int +} + +// NewFIFO returns a new FIFO queue containing elements ts starting with the +// left-most argument at the front. +func NewFIFO(ts ...T) *FIFO { + s := &FIFO{} + for i := range ts { + s.Push(ts[i]) + } + return s +} + +// Push adds a new element onto the LIFO. +func (s *FIFO) Push(t T) { + node := &queueNode{v: t, next: nil} + if s.front == nil { + s.front = node + s.back = node + } else { + s.back.next = node + s.back = node + } + s.size++ +} + +// Peek returns the top of the LIFO. If LIFO is empty, returns nil, false. +func (s *FIFO) Peek() (T, bool) { + if s.front == nil { + return nil, false + } + return s.front.v, true +} + +// Pop returns the top of the LIFO and removes it. If LIFO is empty returns +// nil, false. +func (s *FIFO) Pop() (T, bool) { + if s.front == nil { + return nil, false + } + node := s.front + s.front = node.next + s.size-- + return node.v, true +} + +// Size returns the size of the LIFO. +func (s *FIFO) Size() int { + return s.size +} diff --git a/third_party/opa/v1/util/queue_test.go b/third_party/opa/v1/util/queue_test.go new file mode 100644 index 000000000000..a3f3707a033c --- /dev/null +++ b/third_party/opa/v1/util/queue_test.go @@ -0,0 +1,84 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import "testing" + +func TestLIFO(t *testing.T) { + + lifo := NewLIFO(1, 2, 3, 4) + + if lifo.Size() != 4 { + t.Fatalf("Expected LIFO size == 4 but got: %v", lifo.Size()) + } + + for i := 4; i >= 1; i-- { + x, ok := lifo.Peek() + if !ok || x != i { + t.Fatalf("Expected peek() == %v but got: %v (ok=%v)", i, x, ok) + } + x, ok = lifo.Pop() + if !ok || x != i { + t.Fatalf("Expected pop() == %v but got: %v (ok=%v)", i, x, ok) + } + } + + x, ok := lifo.Peek() + if ok || x != nil { + t.Fatalf("Expected peek() == nil, false but got: %v (ok=%v)", x, ok) + } + + x, ok = lifo.Pop() + if ok || x != nil { + t.Fatalf("Expected pop() == nil, false but got: %v (ok=%v)", x, ok) + } + + for i := 4; i >= 1; i-- { + lifo.Push(i) + x, ok = lifo.Peek() + if !ok || x != i { + t.Fatalf("Expected peek() == %v but got: %v (ok=%v)", i, x, ok) + } + } + +} + +func TestFIFO(t *testing.T) { + fifo := NewFIFO(1, 2, 3, 4) + + if fifo.Size() != 4 { + t.Fatalf("Expected FIFO size == 1 but got: %v", fifo.Size()) + } + + for i := 1; i <= 4; i++ { + x, ok := fifo.Peek() + if !ok || x != i { + t.Fatalf("Expected peek() == %v but got: %v (ok=%v)", i, x, ok) + } + x, ok = fifo.Pop() + if !ok || x != i { + t.Fatalf("Expected pop() == %v but got: %v (ok=%v)", i, x, ok) + } + } + + x, ok := fifo.Peek() + if ok || x != nil { + t.Fatalf("Expected peek() == nil, false but got: %v (ok=%v)", x, ok) + } + + x, ok = fifo.Pop() + if ok || x != nil { + t.Fatalf("Expected pop() == nil, false but got: %v (ok=%v)", x, ok) + } + + for i := 1; i <= 4; i++ { + fifo.Push(i) + x, ok = fifo.Peek() + if !ok || x != 1 { + t.Fatalf("Expected peek() == %v but got: %v (ok=%v)", 1, x, ok) + } + } + +} diff --git a/third_party/opa/v1/util/read_gzip_body.go b/third_party/opa/v1/util/read_gzip_body.go new file mode 100644 index 000000000000..ddffe2a4de45 --- /dev/null +++ b/third_party/opa/v1/util/read_gzip_body.go @@ -0,0 +1,81 @@ +package util + +import ( + "bytes" + "compress/gzip" + "encoding/binary" + "errors" + "io" + "net/http" + "strings" + "sync" + + "github.com/open-policy-agent/opa/v1/util/decoding" +) + +var gzipReaderPool = sync.Pool{ + New: func() any { + reader := new(gzip.Reader) + return reader + }, +} + +// Note(philipc): Originally taken from server/server.go +// The DecodingLimitHandler handles validating that the gzip payload is within the +// allowed max size limit. Thus, in the event of a forged payload size trailer, +// the worst that can happen is that we waste memory up to the allowed max gzip +// payload size, but not an unbounded amount of memory, as was potentially +// possible before. +func ReadMaybeCompressedBody(r *http.Request) ([]byte, error) { + var content *bytes.Buffer + // Note(philipc): If the request body is of unknown length (such as what + // happens when 'Transfer-Encoding: chunked' is set), we have to do an + // incremental read of the body. In this case, we can't be too clever, we + // just do the best we can with whatever is streamed over to us. + // Fetch gzip payload size limit from request context. + if maxLength, ok := decoding.GetServerDecodingMaxLen(r.Context()); ok { + bs, err := io.ReadAll(io.LimitReader(r.Body, maxLength)) + if err != nil { + return bs, err + } + content = bytes.NewBuffer(bs) + } else { + // Read content from the request body into a buffer of known size. + content = bytes.NewBuffer(make([]byte, 0, r.ContentLength)) + if _, err := io.CopyN(content, r.Body, r.ContentLength); err != nil { + return content.Bytes(), err + } + } + + // Decompress gzip content by reading from the buffer. + if strings.Contains(r.Header.Get("Content-Encoding"), "gzip") { + // Fetch gzip payload size limit from request context. + gzipMaxLength, _ := decoding.GetServerDecodingGzipMaxLen(r.Context()) + + // Note(philipc): The last 4 bytes of a well-formed gzip blob will + // always be a little-endian uint32, representing the decompressed + // content size, modulo 2^32. We validate that the size is safe, + // earlier in DecodingLimitHandler. + sizeTrailerField := binary.LittleEndian.Uint32(content.Bytes()[content.Len()-4:]) + if sizeTrailerField > uint32(gzipMaxLength) { + return content.Bytes(), errors.New("gzip payload too large") + } + // Pull a gzip decompressor from the pool, and assign it to the current + // buffer, using Reset(). Later, return it back to the pool for another + // request to use. + gzReader := gzipReaderPool.Get().(*gzip.Reader) + if err := gzReader.Reset(content); err != nil { + return nil, err + } + defer gzReader.Close() + defer gzipReaderPool.Put(gzReader) + decompressedContent := bytes.NewBuffer(make([]byte, 0, sizeTrailerField)) + if _, err := io.CopyN(decompressedContent, gzReader, int64(sizeTrailerField)); err != nil { + return decompressedContent.Bytes(), err + } + return decompressedContent.Bytes(), nil + } + + // Request was not compressed; return the content bytes. + return content.Bytes(), nil +} diff --git a/third_party/opa/v1/util/test/benchmark.go b/third_party/opa/v1/util/test/benchmark.go new file mode 100644 index 000000000000..0176b7f29d34 --- /dev/null +++ b/third_party/opa/v1/util/test/benchmark.go @@ -0,0 +1,265 @@ +package test + +// This file collects some helpers for generating data used in +// benchmarks, +// - topdown/topdown_bench_test.go + +import ( + "bytes" + "encoding/json" + "fmt" + "text/template" +) + +// PartialObjectBenchmarkCrossModule returns a module with n "bench_test_" prefixed rules +// that each refer to another "cond_bench_" prefixed rule +func PartialObjectBenchmarkCrossModule(n int) []string { + fooMod := `package test.foo + import data.test.bar + import data.test.baz + + output[key] := value if { + value := bar[key] + startswith("bench_test_", key) + }` + barMod := "package test.bar\n" + barMod += ` + cond_bench_0 if { + contains(lower(input.test_input_0), lower("input_01")) + } + cond_bench_1 if { + contains(lower(input.test_input_1), lower("input")) + } + cond_bench_2 if { + contains(lower(input.test_input_2), lower("input_10")) + } + bench_test_out_result := load_tests(test_collector) + + load_tests(i) := out if { + out := i + } + ` + + bazMod := "package test.baz\nimport data.test.bar\n" + ruleBuilder := "" + + for idx := 1; idx <= n; idx++ { + barMod += fmt.Sprintf(` + bench_test_%[1]d := result if { + input.bench_test_collector_mambo_number_%[3]d + result := input.bench_test_collector_mambo_number_%[3]d + } else := result if { + is_null(bench_test_out_result.mambo_number_%[3]d.error) + result := bench_test_out_result.mambo_number_%[3]d.result + } + + test_collector["mambo_number_%[3]d"] := result if { + cond_bench_%[2]d + not %[3]d == 2 + not %[3]d == 3 + not input.bench_test_collector_mambo_number_%[3]d + result := { "result": %[3]d, "error": null } + } + `, idx, idx%3, idx%5) + ruleBuilder += fmt.Sprintf(" bar.bench_test_%[1]d == %[1]d\n", idx) + if idx%10 == 0 { + bazMod += fmt.Sprintf(`rule_%d if { + %s + }`, idx, ruleBuilder) + fooMod += fmt.Sprintf(` + final_decision = "allow" if { + baz.rule_%d + } + `, idx) + ruleBuilder = "" + } + } + + return []string{fooMod, barMod, bazMod} +} + +// ArrayIterationBenchmarkModule returns a module that iterates an array +// with `n` elements +func ArrayIterationBenchmarkModule(n int) string { + return fmt.Sprintf(`package test + + fixture = [ x | x := numbers.range(1, %d)[_] ] + + main if { fixture[i] }`, n) +} + +// SetIterationBenchmarkModule returns a module that iterates a set +// with `n` elements +func SetIterationBenchmarkModule(n int) string { + return fmt.Sprintf(`package test + + fixture = { x | x := numbers.range(1, %d)[_] } + + main if { fixture[i] }`, n) +} + +// ObjectIterationBenchmarkModule returns a module that iterates an object +// with `n` key/val pairs +func ObjectIterationBenchmarkModule(n int) string { + return fmt.Sprintf(`package test + + fixture = { x: x | x := numbers.range(1, %d)[_] } + + main if { fixture[i] }`, n) +} + +// GenerateLargeJSONBenchmarkData returns a map of 100 keys and 100.000 key/value +// pairs. +func GenerateLargeJSONBenchmarkData() map[string]any { + return GenerateJSONBenchmarkData(100, 100*1000) +} + +// GenerateJSONBenchmarkData returns a map of `k` keys and `v` key/value pairs. +func GenerateJSONBenchmarkData(k, v int) map[string]any { + + // create array of null values that can be iterated over + keys := make([]any, k) + for i := range keys { + keys[i] = nil + } + + // create large JSON object value (100,000 entries is about 2MB on disk) + values := map[string]any{} + for i := range v { + values[fmt.Sprintf("key%d", i)] = fmt.Sprintf("value%d", i) + } + + return map[string]any{ + "keys": keys, + "values": values, + } +} + +// GenerateConcurrencyBenchmarkData returns a module and data; the module +// checks some input parameters against that data in a simple API authz +// scheme. +func GenerateConcurrencyBenchmarkData() (string, map[string]any) { + obj := []byte(` + { + "objs": [ + { + "attr1": "get", + "path": "/foo/bar", + "user": "bob" + }, + { + "attr1": "set", + "path": "/foo/bar/baz", + "user": "alice" + }, + { + "attr1": "get", + "path": "/foo", + "groups": [ + "admin", + "eng" + ] + }, + { + "path": "/foo/bar", + "user": "alice" + } + ] + } + `) + + var data map[string]any + if err := json.Unmarshal(obj, &data); err != nil { + panic(err) + } + mod := `package test + + import data.objs + + p if { + objs[i].attr1 = "get" + objs[i].groups[j] = "eng" + } + + p if { + objs[i].user = "alice" + } + ` + + return mod, data +} + +// GenerateVirtualDocsBenchmarkData generates a module and input; the +// numTotalRules and numHitRules create as many rules in the module to +// match/miss the returned input. +func GenerateVirtualDocsBenchmarkData(numTotalRules, numHitRules int) (string, map[string]any) { + + hitRule := ` + allow if { + input.method = "POST" + input.path = ["accounts", account_id] + input.user_id = account_id + } + ` + + missRule := ` + allow if { + input.method = "GET" + input.path = ["salaries", account_id] + input.user_id = account_id + } + ` + + testModuleTmpl := `package a.b.c + + {{range .MissRules }} + {{ . }} + {{end}} + + {{range .HitRules }} + {{ . }} + {{end}} + ` + + tmpl, err := template.New("Test").Parse(testModuleTmpl) + if err != nil { + panic(err) + } + + var buf bytes.Buffer + + var missRules []string + + if numTotalRules > numHitRules { + missRules = make([]string, numTotalRules-numHitRules) + for i := range missRules { + missRules[i] = missRule + } + } + + hitRules := make([]string, numHitRules) + for i := range hitRules { + hitRules[i] = hitRule + } + + params := struct { + MissRules []string + HitRules []string + }{ + MissRules: missRules, + HitRules: hitRules, + } + + err = tmpl.Execute(&buf, params) + if err != nil { + panic(err) + } + + input := map[string]any{ + "path": []any{"accounts", "alice"}, + "method": "POST", + "user_id": "alice", + } + + return buf.String(), input +} diff --git a/third_party/opa/v1/util/test/ci_skip.go b/third_party/opa/v1/util/test/ci_skip.go new file mode 100644 index 000000000000..3380c1592536 --- /dev/null +++ b/third_party/opa/v1/util/test/ci_skip.go @@ -0,0 +1,10 @@ +//go:build !darwin +// +build !darwin + +package test + +import "testing" + +// Skip will skip this test on pull request CI runs. +// Used for slow test runners on GHA's darwin machines. +func Skip(*testing.T) {} diff --git a/third_party/opa/v1/util/test/ci_skip_darwin.go b/third_party/opa/v1/util/test/ci_skip_darwin.go new file mode 100644 index 000000000000..ae440dfb39a9 --- /dev/null +++ b/third_party/opa/v1/util/test/ci_skip_darwin.go @@ -0,0 +1,15 @@ +package test + +import ( + "os" + "testing" +) + +// Skip will skip this test on pull request CI runs. +// Used for slow test runners on GHA's darwin machines. +func Skip(t *testing.T) { + if os.Getenv("GITHUB_ACTIONS") == "" { + return + } + t.Skip("skipped on Darwin, test runner is slow") +} diff --git a/third_party/opa/v1/util/test/doc.go b/third_party/opa/v1/util/test/doc.go new file mode 100644 index 000000000000..8f7051989141 --- /dev/null +++ b/third_party/opa/v1/util/test/doc.go @@ -0,0 +1,6 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package test contains utilities used in the policy engine's test suite. +package test diff --git a/third_party/opa/v1/util/test/tempfs.go b/third_party/opa/v1/util/test/tempfs.go new file mode 100644 index 000000000000..397564089918 --- /dev/null +++ b/third_party/opa/v1/util/test/tempfs.go @@ -0,0 +1,90 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package test + +import ( + "io/fs" + "os" + "path/filepath" + "testing/fstest" +) + +// WithTempFS creates a temporary directory structure and invokes f with the +// root directory path. +func WithTempFS(files map[string]string, f func(string)) { + rootDir, cleanup, err := MakeTempFS("", "opa_test", files) + if err != nil { + panic(err) + } + defer cleanup() + f(rootDir) +} + +// MakeTempFS creates a temporary directory structure for test purposes rooted at root. +// If root is empty, the dir is created in the default system temp location. +// If the creation fails, cleanup is nil and the caller does not have to invoke it. If +// creation succeeds, the caller should invoke cleanup when they are done. +func MakeTempFS(root, prefix string, files map[string]string) (rootDir string, cleanup func(), err error) { + + rootDir, err = os.MkdirTemp(root, prefix) + + if err != nil { + return "", nil, err + } + + cleanup = func() { + os.RemoveAll(rootDir) + } + + skipCleanup := false + + // Cleanup unless flag is unset. It will be unset if we succeed. + defer func() { + if !skipCleanup { + cleanup() + } + }() + + for path, content := range files { + dirname, filename := filepath.Split(path) + dirPath := filepath.Join(rootDir, dirname) + if err := os.MkdirAll(dirPath, 0777); err != nil { + return "", nil, err + } + + f, err := os.Create(filepath.Join(dirPath, filename)) + if err != nil { + return "", nil, err + } + + if _, err := f.WriteString(content); err != nil { + return "", nil, err + } + } + + skipCleanup = true + + return rootDir, cleanup, nil +} + +// WithTestFS creates a temporary file system of `files` in memory +// if `inMemoryFS` is true and invokes `f“ with that filesystem +func WithTestFS(files map[string]string, inMemoryFS bool, f func(string, fs.FS)) { + if inMemoryFS { + fsys := make(fstest.MapFS) + rootDir := "." + for k, v := range files { + fsys[filepath.Join(rootDir, k)] = &fstest.MapFile{Data: []byte(v)} + } + f(rootDir, fsys) + } else { + rootDir, cleanup, err := MakeTempFS("", "opa_test", files) + if err != nil { + panic(err) + } + defer cleanup() + f(rootDir, nil) + } +} diff --git a/third_party/opa/v1/util/test/tempus.go b/third_party/opa/v1/util/test/tempus.go new file mode 100644 index 000000000000..fa07c4cca8b6 --- /dev/null +++ b/third_party/opa/v1/util/test/tempus.go @@ -0,0 +1,55 @@ +// Copyright 2023 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package test + +import ( + "bytes" + "sync" + "testing" + "time" +) + +// FIXME: Abort long running tests +func Eventually(t *testing.T, timeout time.Duration, f func() bool) bool { + t.Helper() + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + if f() { + return true + } + time.Sleep(10 * time.Millisecond) + } + return false +} + +func EventuallyOrFatal(t *testing.T, timeout time.Duration, f func() bool) { + t.Helper() + if !Eventually(t, timeout, f) { + t.Fatal("Timeout") + } +} + +type BlockingWriter struct { + m sync.Mutex + buf bytes.Buffer +} + +func (w *BlockingWriter) Write(p []byte) (n int, err error) { + w.m.Lock() + defer w.m.Unlock() + return w.buf.Write(p) +} + +func (w *BlockingWriter) String() string { + w.m.Lock() + defer w.m.Unlock() + return w.buf.String() +} + +func (w *BlockingWriter) Reset() { + w.m.Lock() + defer w.m.Unlock() + w.buf.Reset() +} diff --git a/third_party/opa/v1/util/test/zeroreader.go b/third_party/opa/v1/util/test/zeroreader.go new file mode 100644 index 000000000000..df3664ca604d --- /dev/null +++ b/third_party/opa/v1/util/test/zeroreader.go @@ -0,0 +1,20 @@ +// Copyright 2025 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package test + +// ZeroReader is an io.Reader implementation that returns an infinite stream of zeros +type ZeroReader struct{} + +func (ZeroReader) Read(p []byte) (n int, err error) { + for i := range p { + p[i] = 0 + } + return len(p), nil +} + +// NewZeroReader creates a new ZeroReader +func NewZeroReader() *ZeroReader { + return &ZeroReader{} +} diff --git a/third_party/opa/v1/util/time.go b/third_party/opa/v1/util/time.go new file mode 100644 index 000000000000..93ef03939a42 --- /dev/null +++ b/third_party/opa/v1/util/time.go @@ -0,0 +1,48 @@ +package util + +import "time" + +// TimerWithCancel exists because of memory leaks when using +// time.After in select statements. Instead, we now manually create timers, +// wait on them, and manually free them. +// +// See this for more details: +// https://www.arangodb.com/2020/09/a-story-of-a-memory-leak-in-go-how-to-properly-use-time-after/ +// +// Note: This issue is fixed in Go 1.23, but this fix helps us until then. +// +// Warning: the cancel cannot be done concurrent to reading, everything should +// work in the same goroutine. +// +// Example: +// +// for retries := 0; true; retries++ { +// +// ...main logic... +// +// timer, cancel := utils.TimerWithCancel(utils.Backoff(retries)) +// select { +// case <-ctx.Done(): +// cancel() +// return ctx.Err() +// case <-timer.C: +// continue +// } +// } +func TimerWithCancel(delay time.Duration) (*time.Timer, func()) { + timer := time.NewTimer(delay) + + return timer, func() { + // Note: The Stop function returns: + // - true: if the timer is active. (no draining required) + // - false: if the timer was already stopped or fired/expired. + // In this case the channel should be drained to prevent memory + // leaks only if it is not empty. + // This operation is safe only if the cancel function is + // used in same goroutine. Concurrent reading or canceling may + // cause deadlock. + if !timer.Stop() && len(timer.C) > 0 { + <-timer.C + } + } +} diff --git a/third_party/opa/v1/util/wait.go b/third_party/opa/v1/util/wait.go new file mode 100644 index 000000000000..b1ea84fd5322 --- /dev/null +++ b/third_party/opa/v1/util/wait.go @@ -0,0 +1,34 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "errors" + "time" +) + +// WaitFunc will call passed function at an interval and return nil +// as soon this function returns true. +// If timeout is reached before the passed in function returns true +// an error is returned. +func WaitFunc(fun func() bool, interval, timeout time.Duration) error { + if fun() { + return nil + } + ticker := time.NewTicker(interval) + timer := time.NewTimer(timeout) + defer ticker.Stop() + defer timer.Stop() + for { + select { + case <-timer.C: + return errors.New("timeout") + case <-ticker.C: + if fun() { + return nil + } + } + } +} diff --git a/third_party/opa/v1/util/wait_test.go b/third_party/opa/v1/util/wait_test.go new file mode 100644 index 000000000000..b633df7244ff --- /dev/null +++ b/third_party/opa/v1/util/wait_test.go @@ -0,0 +1,43 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "testing" + "time" +) + +func TestWaitFunc(t *testing.T) { + trueAfter := func(after time.Duration) func() bool { + t := time.Now().Add(after) + return func() bool { + return time.Now().After(t) + } + } + + cases := []struct { + trueAfter time.Duration + interval time.Duration + timeout time.Duration + shouldFail bool + }{ + {0, 1 * time.Millisecond, 100 * time.Millisecond, false}, + {1 * time.Millisecond, 1 * time.Millisecond, 100 * time.Millisecond, false}, + {100 * time.Millisecond, 1 * time.Millisecond, 1 * time.Millisecond, true}, + {100 * time.Millisecond, 1000 * time.Millisecond, 1 * time.Millisecond, true}, + } + + for _, c := range cases { + err := WaitFunc(trueAfter(c.trueAfter), c.interval, c.timeout) + if err != nil && c.shouldFail { + continue + } + if err != nil { + t.Error(err) + } else if c.shouldFail { + t.Errorf("Expected error for case: %+v", c) + } + } +} diff --git a/third_party/opa/v1/version/version.go b/third_party/opa/v1/version/version.go new file mode 100644 index 000000000000..531a61694fc6 --- /dev/null +++ b/third_party/opa/v1/version/version.go @@ -0,0 +1,58 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package version contains version information that is set at build time. +package version + +import ( + "runtime" + "runtime/debug" +) + +var Version = "1.7.1" + +// GoVersion is the version of Go this was built with +var GoVersion = runtime.Version() + +// Platform is the runtime OS and architecture of this OPA binary +var Platform = runtime.GOOS + "/" + runtime.GOARCH + +// Additional version information that is displayed by the "version" command and used to +// identify the version of running instances of OPA. +var ( + Vcs = "" + Timestamp = "" + Hostname = "" +) + +func init() { + bi, ok := debug.ReadBuildInfo() + if !ok { + return + } + var dirty bool + var binTimestamp, binVcs string + + for _, s := range bi.Settings { + switch s.Key { + case "vcs.time": + binTimestamp = s.Value + case "vcs.revision": + binVcs = s.Value + case "vcs.modified": + dirty = s.Value == "true" + } + } + + if Timestamp == "" { + Timestamp = binTimestamp + } + + if Vcs == "" { + Vcs = binVcs + if dirty { + Vcs += "-dirty" + } + } +} diff --git a/third_party/opa/v1/version/wasm.go b/third_party/opa/v1/version/wasm.go new file mode 100644 index 000000000000..c274a7827af1 --- /dev/null +++ b/third_party/opa/v1/version/wasm.go @@ -0,0 +1,13 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package version + +import "github.com/open-policy-agent/opa/internal/rego/opa" + +// WasmRuntimeAvailable indicates if a wasm runtime is available in this OPA. +func WasmRuntimeAvailable() bool { + _, err := opa.LookupEngine("wasm") + return err == nil +} diff --git a/third_party/opa/version/doc.go b/third_party/opa/version/doc.go new file mode 100644 index 000000000000..5635dedc9325 --- /dev/null +++ b/third_party/opa/version/doc.go @@ -0,0 +1,8 @@ +// Copyright 2024 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended. +// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead. +// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information. +package version diff --git a/third_party/opa/version/version.go b/third_party/opa/version/version.go new file mode 100644 index 000000000000..bb64d8172cf8 --- /dev/null +++ b/third_party/opa/version/version.go @@ -0,0 +1,27 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package version contains version information that is set at build time. +package version + +import ( + v1 "github.com/open-policy-agent/opa/v1/version" +) + +// Version is the canonical version of OPA. +var Version = v1.Version + +// GoVersion is the version of Go this was built with +var GoVersion = v1.GoVersion + +// Platform is the runtime OS and architecture of this OPA binary +var Platform = v1.Platform + +// Additional version information that is displayed by the "version" command and used to +// identify the version of running instances of OPA. +var ( + Vcs = v1.Vcs + Timestamp = v1.Timestamp + Hostname = v1.Hostname +) diff --git a/third_party/opa/version/wasm.go b/third_party/opa/version/wasm.go new file mode 100644 index 000000000000..9f68bbbb18eb --- /dev/null +++ b/third_party/opa/version/wasm.go @@ -0,0 +1,14 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package version + +import ( + v1 "github.com/open-policy-agent/opa/v1/version" +) + +// WasmRuntimeAvailable indicates if a wasm runtime is available in this OPA. +func WasmRuntimeAvailable() bool { + return v1.WasmRuntimeAvailable() +} diff --git a/third_party/opa/wasm/Dockerfile b/third_party/opa/wasm/Dockerfile new file mode 100644 index 000000000000..e26c4924dc6b --- /dev/null +++ b/third_party/opa/wasm/Dockerfile @@ -0,0 +1,50 @@ +FROM ubuntu:20.04@sha256:0b897358ff6624825fb50d20ffb605ab0eaea77ced0adb8c6a4b756513dec6fc + +ARG WABT_VERSION=1.0.24 +ARG BINARYEN_VERSION=version_102 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y curl git build-essential python + +RUN bash -c 'echo -ne "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main\ndeb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main" > /etc/apt/sources.list.d/llvm.list' + +RUN curl -L https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - + +RUN apt-get update && \ + apt-get install -y \ + cmake \ + ninja-build \ + clang-13 \ + clang-format-13 \ + libc++-13-dev \ + libc++abi-13-dev \ + lld-13 && \ + update-alternatives --install /usr/bin/ld ld /usr/bin/lld-13 90 && \ + update-alternatives --install /usr/bin/cc cc /usr/bin/clang-13 90 && \ + update-alternatives --install /usr/bin/cpp cpp /usr/bin/clang++-13 90 && \ + update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-13 90 + +RUN ln -s /usr/bin/clang-13 /usr/bin/clang && \ + ln -s /usr/bin/clang++-13 /usr/bin/clang++ && \ + ln -s /usr/bin/clang-format-13 /usr/bin/clang-format && \ + ln -s /usr/bin/wasm-ld-13 /usr/bin/wasm-ld && \ + ln -s /usr/bin/clang-cpp-13 /usr/bin/clang-cpp + +RUN git clone https://github.com/WebAssembly/wabt && \ + cd wabt && \ + git checkout $WABT_VERSION && \ + git submodule update --init && \ + make + +RUN git clone https://github.com/WebAssembly/binaryen && \ + cd binaryen && \ + git checkout $BINARYEN_VERSION && \ + cmake . && \ + make + +ENV PATH="/binaryen/bin:/wabt/out/clang/Debug:${PATH}" + +ENV CC=clang-13 +ENV CXX=clang++-13 + +WORKDIR /src diff --git a/third_party/opa/wasm/Makefile b/third_party/opa/wasm/Makefile new file mode 100644 index 000000000000..d3f76e34444b --- /dev/null +++ b/third_party/opa/wasm/Makefile @@ -0,0 +1,171 @@ +DOCKER := docker +DOCKER_FLAGS := --rm -e DEBUG +DEBUG ?= 0 + +ifeq ($(shell tty > /dev/null && echo 1 || echo 0), 1) +DOCKER_FLAGS += -it +endif + +DOCKER_WASM_BUILDER_IMAGE ?= openpolicyagent/opa-wasm-builder +WASM_BUILDER_VERSION := 1.6 +WASM_BUILDER_IMAGE := $(DOCKER_WASM_BUILDER_IMAGE):$(WASM_BUILDER_VERSION) +WASM_OBJ_DIR := _obj + +CFLAGS += \ + -MD \ + -MP \ + -nodefaultlibs \ + --target=wasm32-unknown-unknown-wasm \ + -I src/lib \ + -I src/libmpdec \ + -DCONFIG_32 \ + -DANSI + +CPPFLAGS += \ + -std=c++17 \ + -MD \ + -MP \ + -nodefaultlibs \ + --target=wasm32-unknown-unknown-wasm \ + -fno-exceptions \ + -fno-rtti \ + -I src/lib \ + -I src/libc++ \ + -I /usr/lib/llvm-13/include/c++/v1 \ + -I /usr/lib/llvm-13/lib/clang/13.0.0/include \ + -I src/re2 \ + -D_LIBCPP_HAS_NO_THREADS \ + -D_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION + +ifeq ($(DEBUG), 1) +CFLAGS += -O1 -gdwarf -DDEBUG +CPPFLAGS += -O1 -gdwarf -DDEBUG +else +CFLAGS += -O3 +CPPFLAGS += -O3 +endif + +.PHONY: all +all: build test + +.PHONY: clean +clean: + rm -fr $(WASM_OBJ_DIR) + +.PHONY: builder +builder: Dockerfile + $(DOCKER) build -t $(WASM_BUILDER_IMAGE) -f Dockerfile . + +.PHONY: ensure-builder +ensure-builder: + @$(DOCKER) inspect $(WASM_BUILDER_IMAGE) > /dev/null || $(DOCKER) pull $(WASM_BUILDER_IMAGE) || $(MAKE) builder + +.PHONY: push-builder +push-builder: + $(DOCKER) pull $(WASM_BUILDER_IMAGE) || ($(MAKE) builder && $(DOCKER) push $(WASM_BUILDER_IMAGE)) + +.PHONY: build +build: + @$(DOCKER) run $(DOCKER_FLAGS) -v $(CURDIR):/src:Z $(WASM_BUILDER_IMAGE) \ + make --no-builtin-rules $(WASM_OBJ_DIR)/opa.wasm $(WASM_OBJ_DIR)/callgraph.csv + +.PHONY: test +test: + @$(DOCKER) run $(DOCKER_FLAGS) -v $(CURDIR):/src:Z $(WASM_BUILDER_IMAGE) make $(WASM_OBJ_DIR)/opa-test.wasm + @$(DOCKER) run $(DOCKER_FLAGS) -e VERBOSE -v $(CURDIR):/src:Z -w /src node:14 node test.js $(WASM_OBJ_DIR)/opa-test.wasm + +.PHONY: hack +hack: + @$(DOCKER) run $(DOCKER_FLAGS) -v $(CURDIR):/src:Z $(WASM_BUILDER_IMAGE) + +$(shell mkdir -p $(WASM_OBJ_DIR)/src/lib) +$(shell mkdir -p $(WASM_OBJ_DIR)/src/libmpdec) +$(shell mkdir -p $(WASM_OBJ_DIR)/src/libc++) +$(shell mkdir -p $(WASM_OBJ_DIR)/src/re2/re2) +$(shell mkdir -p $(WASM_OBJ_DIR)/src/re2/util) +$(shell mkdir -p $(WASM_OBJ_DIR)/src) +$(shell mkdir -p $(WASM_OBJ_DIR)/tests) + +SRCS := $(sort $(wildcard src/*.c)) +CPP_SRCS := $(sort $(wildcard src/*.cc)) +LIB_SRCS := $(sort $(wildcard src/lib/*.c)) +LIB_MPDEC_SRCS := $(sort $(wildcard src/libmpdec/*.c)) +LIB_CPP_SRCS := $(sort $(wildcard src/libc++/*.cc)) +RE2_RE2_SRCS := $(sort $(wildcard src/re2/re2/*.cc)) +RE2_UTIL_SRCS := $(sort $(wildcard src/re2/util/*.cc)) +TEST_SRCS := $(sort $(wildcard tests/*.c)) +TEST_CPP_SRCS := $(sort $(wildcard tests/*.cc)) + +-include $(patsubst %.c,$(WASM_OBJ_DIR)/%.d,$(SRCS)) +-include $(patsubst %.cc,$(WASM_OBJ_DIR)/%.d,$(CPP_SRCS)) +-include $(patsubst %.c,$(WASM_OBJ_DIR)/%.d,$(LIB_SRCS)) +-include $(patsubst %.c,$(WASM_OBJ_DIR)/%.d,$(LIB_MPDEC_SRCS)) +-include $(patsubst %.cc,$(WASM_OBJ_DIR)/%.d,$(LIB_CPP_SRCS)) +-include $(patsubst %.cc,$(WASM_OBJ_DIR)/%.d,$(RE2_RE2_SRCS)) +-include $(patsubst %.cc,$(WASM_OBJ_DIR)/%.d,$(RE2_UTIL_SRCS)) +-include $(patsubst %.c,$(WASM_OBJ_DIR)/%.d,$(TEST_SRCS)) +-include $(patsubst %.cc,$(WASM_OBJ_DIR)/%.d,$(TEST_CPP_SRCS)) + +OBJS := $(patsubst %.c, $(WASM_OBJ_DIR)/%.wasm, $(SRCS)) +CPP_OBJS := $(patsubst %.cc, $(WASM_OBJ_DIR)/%.wasm, $(CPP_SRCS)) +LIB_OBJS := $(patsubst %.c, $(WASM_OBJ_DIR)/%.wasm, $(LIB_SRCS)) +LIB_MPDEC_OBJS := $(patsubst %.c, $(WASM_OBJ_DIR)/%.wasm, $(LIB_MPDEC_SRCS)) +LIB_CPP_OBJS := $(patsubst %.cc, $(WASM_OBJ_DIR)/%.wasm, $(LIB_CPP_SRCS)) +RE2_RE2_OBJS := $(patsubst %.cc, $(WASM_OBJ_DIR)/%.wasm, $(RE2_RE2_SRCS)) +RE2_UTIL_OBJS := $(patsubst %.cc, $(WASM_OBJ_DIR)/%.wasm, $(RE2_UTIL_SRCS)) +TEST_OBJS := $(patsubst %.c, $(WASM_OBJ_DIR)/%.wasm, $(TEST_SRCS)) +TEST_CPP_OBJS := $(patsubst %.cc, $(WASM_OBJ_DIR)/%.wasm, $(TEST_CPP_SRCS)) + +$(OBJS): $(WASM_OBJ_DIR)/src/%.wasm: src/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(CPP_OBJS): $(WASM_OBJ_DIR)/src/%.wasm: src/%.cc + $(CXX) $(CPPFLAGS) -c -o $@ $< + +$(LIB_OBJS): $(WASM_OBJ_DIR)/src/lib/%.wasm: src/lib/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB_MPDEC_OBJS): $(WASM_OBJ_DIR)/src/libmpdec/%.wasm: src/libmpdec/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB_CPP_OBJS): $(WASM_OBJ_DIR)/src/libc++/%.wasm: src/libc++/%.cc + $(CXX) $(CPPFLAGS) -c -o $@ $< + +$(RE2_RE2_OBJS): $(WASM_OBJ_DIR)/src/re2/re2/%.wasm: src/re2/re2/%.cc + $(CXX) $(CPPFLAGS) -c -o $@ $< + +$(RE2_UTIL_OBJS): $(WASM_OBJ_DIR)/src/re2/util/%.wasm: src/re2/util/%.cc + $(CXX) $(CPPFLAGS) -c -o $@ $< + +$(TEST_OBJS): $(WASM_OBJ_DIR)/tests/%.wasm: tests/%.c + $(CC) $(CFLAGS) -I src -c -o $@ $< + +$(TEST_CPP_OBJS): $(WASM_OBJ_DIR)/tests/%.wasm: tests/%.cc + $(CXX) $(CPPFLAGS) -I src -c -o $@ $< + +$(WASM_OBJ_DIR)/opa.wasm: $(OBJS) $(CPP_OBJS) $(LIB_OBJS) $(LIB_MPDEC_OBJS) $(LIB_CPP_OBJS) $(RE2_RE2_OBJS) $(RE2_UTIL_OBJS) + wasm-ld \ + --allow-undefined-file=src/undefined.symbols \ + --no-entry \ + --export=__heap_base \ + --stack-first \ + -o $@ $^ + @wasm2wat $(WASM_OBJ_DIR)/opa.wasm > $(WASM_OBJ_DIR)/opa.wast + +$(WASM_OBJ_DIR)/opa-test.wasm: $(OBJS) $(CPP_OBJS) $(LIB_OBJS) $(LIB_MPDEC_OBJS) $(LIB_CPP_OBJS) $(RE2_RE2_OBJS) $(RE2_UTIL_OBJS) $(TEST_OBJS) $(TEST_CPP_OBJS) + @cat src/undefined.symbols tests/undefined.symbols > _obj/undefined.symbols + @wasm-ld \ + --allow-undefined-file=_obj/undefined.symbols \ + --no-entry \ + --stack-first \ + -o $@ $^ + +$(WASM_OBJ_DIR)/callgraph.csv: $(WASM_OBJ_DIR)/opa.wasm + # NOTE: wasm-opt will output "warning: no output file specified", + # because we're not actually optimizing the wasm, but only extract + # information. + build/gen-wasm-callgraph.sh $< > $@ + +# These are for canceling the implicit rules of GNU Make, see +# https://www.gnu.org/software/make/manual/html_node/Canceling-Rules.html#Canceling-Rules +src/libc++/mutex: src/libc++/mutex.cc diff --git a/third_party/opa/wasm/README.md b/third_party/opa/wasm/README.md new file mode 100644 index 000000000000..bc567096537a --- /dev/null +++ b/third_party/opa/wasm/README.md @@ -0,0 +1,51 @@ +# OPA-WASM + +This directory contains a library that implements various low-level +operations for policies compiled into WebAssembly (Wasm). Specifically, the +library implements most of the built-in functions provided by OPA. See the "Wasm" +tag next to each function listed in the +[built-in function reference](https://www.openpolicyagent.org/docs/latest/policy-reference/#built-in-functions) +for the complete list, or the +[builtin_metadata.json](https://github.com/open-policy-agent/opa/blob/main/builtin_metadata.json) file for programmatic purposes. + +This library does not make any backwards compatibility guarantees. + +## Documentation + +See the [OPA docs](https://www.openpolicyagent.org/docs/latest/wasm/) on Wasm for an overview of the integration, +compilation options, and more. + +## Development + +You should have Docker installed to build and test changes to the library. We +commit the output of the build (`opa.wasm`) into the repository, so it's +important for the build output to be reproducible. + +You can build the library by running `make build`. This will produce WASM +executables under the `_obj` directory. + +You can test the library by running `make test`. By default, the test runner +does not print messages when tests pass. If you run `make test VERBOSE=1` it +will log all of the tests that were run. + +You can run `make hack` to start a shell inside the builder image. This is +useful if you need to interact with low-level WASM tooling like +`wasm-objdump`, `wasm2wat`, etc. or LLVM itself. + +You must manually push the builder image if you make changes to it (run `make +builder` to produce a new Docker image). + +### Debug Builds + +Set the `DEBUG` environment variable to `1` to enable generating binaries with +debug symbols and a less aggressive optimization level. Eg: `DEBUG=1 make build`. + +## Vendoring + +If you make changes to the library, run the `make generate` in the parent +directory and commit the results back to the repository. The `generate` +target will: + +1. Build the OPA-WASM library +2. Copy the library into the [internal/compiler/wasm/opa](../internal/compiler/wasm/opa) directory. +3. Run the tool to generate the [internal/compiler/wasm/opa/opa.go](../internal/compiler/wasm/opa/opa.go) file. diff --git a/third_party/opa/wasm/src/aggregates.c b/third_party/opa/wasm/src/aggregates.c new file mode 100644 index 000000000000..0b811edba853 --- /dev/null +++ b/third_party/opa/wasm/src/aggregates.c @@ -0,0 +1,369 @@ +#include "aggregates.h" +#include "mpd.h" +#include "std.h" +#include "unicode.h" +#include "re2/util/utf.h" + +OPA_BUILTIN +opa_value *opa_agg_count(opa_value *v) +{ + switch (opa_value_type(v)) + { + case OPA_STRING: { + opa_string_t *s = opa_cast_string(v); + int units = 0; + Rune rune; + + for (int i = 0, len = 0; i < s->len; i += len) + { + len = chartorune(&rune, &s->v[i]); + units++; + } + + return opa_number_int(units); + } + case OPA_ARRAY: + return opa_number_int(opa_cast_array(v)->len); + case OPA_OBJECT: + return opa_number_int(opa_cast_object(v)->len); + case OPA_SET: + return opa_number_int(opa_cast_set(v)->len); + default: + return NULL; + } +} + +static mpd_t *mpd_int(int v) +{ + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + mpd_qset_i32(r, v, mpd_max_ctx(), &status); + if (status) + { + opa_abort("aggregates: int"); + } + + return r; +} + +OPA_BUILTIN +opa_value *opa_agg_sum(opa_value *v) +{ + switch (opa_value_type(v)) + { + case OPA_ARRAY: { + opa_array_t *a = opa_cast_array(v); + mpd_t *r = mpd_int(0); + + for (int i = 0; i < a->len; i++) + { + if (opa_value_type(a->elems[i].v) != OPA_NUMBER) + { + mpd_del(r); + return NULL; + } + + r = qadd(r, opa_number_to_bf(a->elems[i].v)); + } + + return opa_bf_to_number(r); + } + + case OPA_SET: { + opa_set_t *s = opa_cast_set(v); + mpd_t *r = mpd_int(0); + + for (int i = 0; i < s->n; i++) + { + for (opa_set_elem_t *elem = s->buckets[i]; elem != NULL; elem = elem->next) + { + if (opa_value_type(elem->v) != OPA_NUMBER) + { + mpd_del(r); + return NULL; + } + + r = qadd(r, opa_number_to_bf(elem->v)); + } + } + + return opa_bf_to_number(r); + } + + default: + return NULL; + } +} + +OPA_BUILTIN +opa_value *opa_agg_product(opa_value *v) +{ + switch (opa_value_type(v)) + { + case OPA_ARRAY: { + opa_array_t *a = opa_cast_array(v); + mpd_t *r = mpd_int(1); + + for (int i = 0; i < a->len; i++) + { + if (opa_value_type(a->elems[i].v) != OPA_NUMBER) + { + mpd_del(r); + return NULL; + } + + r = qmul(r, opa_number_to_bf(a->elems[i].v)); + } + + return opa_bf_to_number(r); + } + + case OPA_SET: { + opa_set_t *s = opa_cast_set(v); + mpd_t *r = mpd_int(1); + + for (int i = 0; i < s->n; i++) + { + for (opa_set_elem_t *elem = s->buckets[i]; elem != NULL; elem = elem->next) + { + if (opa_value_type(elem->v) != OPA_NUMBER) + { + mpd_del(r); + return NULL; + } + + r = qmul(r, opa_number_to_bf(elem->v)); + } + } + + return opa_bf_to_number(r); + } + + default: + return NULL; + } +} + +OPA_BUILTIN +opa_value *opa_agg_max(opa_value *v) +{ + switch (opa_value_type(v)) + { + case OPA_ARRAY: { + opa_array_t *a = opa_cast_array(v); + opa_value *max = NULL; + + for (int i = 0; i < a->len; i++) + { + if (max == NULL || opa_value_compare(max, a->elems[i].v) < 0) + { + max = a->elems[i].v; + } + } + + return max; + } + + case OPA_SET: { + opa_set_t *s = opa_cast_set(v); + if (s->len == 0) + { + return NULL; + } + + opa_value *max = NULL; + for (int i = 0; i < s->n; i++) + { + for (opa_set_elem_t *elem = s->buckets[i]; elem != NULL; elem = elem->next) + { + if (max == NULL || opa_value_compare(max, elem->v) < 0) + { + max = elem->v; + } + } + } + + return max; + } + + default: + return NULL; + } +} + +OPA_BUILTIN +opa_value *opa_agg_min(opa_value *v) +{ + switch (opa_value_type(v)) + { + case OPA_ARRAY: { + opa_array_t *a = opa_cast_array(v); + opa_value *min = NULL; + + for (int i = 0; i < a->len; i++) + { + if (min == NULL || opa_value_compare(min, a->elems[i].v) > 0) + { + min = a->elems[i].v; + } + } + + return min; + } + + case OPA_SET: { + opa_set_t *s = opa_cast_set(v); + if (s->len == 0) + { + return NULL; + } + + opa_value *min = NULL; + for (int i = 0; i < s->n; i++) + { + for (opa_set_elem_t *elem = s->buckets[i]; elem != NULL; elem = elem->next) + { + if (min == NULL || opa_value_compare(min, elem->v) > 0) + { + min = elem->v; + } + } + } + + return min; + } + + default: + return NULL; + } +} + +OPA_BUILTIN +opa_value *opa_agg_sort(opa_value *v) +{ + switch (opa_value_type(v)) + { + case OPA_ARRAY: { + opa_value *r = opa_value_shallow_copy(v); + opa_array_sort(opa_cast_array(r), opa_value_compare); + return r; + } + case OPA_SET: { + opa_set_t *s = opa_cast_set(v); + opa_array_t *r = opa_cast_array(opa_array_with_cap(s->len)); + + for (int i = 0; i < s->n; i++) + { + for (opa_set_elem_t *elem = s->buckets[i]; elem != NULL; elem = elem->next) + { + opa_array_append(r, elem->v); + } + } + + opa_array_sort(r, opa_value_compare); + return &r->hdr; + } + default: + return NULL; + } +} + +OPA_BUILTIN +opa_value *opa_agg_all(opa_value *v) +{ + switch (opa_value_type(v)) + { + case OPA_ARRAY: { + opa_array_t *a = opa_cast_array(v); + + for (int i = 0; i < a->len; i++) + { + if (opa_value_type(a->elems[i].v) != OPA_BOOLEAN || opa_cast_boolean(a->elems[i].v)->v == false) + { + return opa_boolean(false); + } + } + + return opa_boolean(true); + } + case OPA_SET: { + opa_set_t *s = opa_cast_set(v); + + for (int i = 0; i < s->n; i++) + { + for (opa_set_elem_t *elem = s->buckets[i]; elem != NULL; elem = elem->next) + { + if (opa_value_type(elem->v) != OPA_BOOLEAN || opa_cast_boolean(elem->v)->v == false) + { + return opa_boolean(false); + } + } + } + + return opa_boolean(true); + } + default: + return NULL; + } +} + +OPA_BUILTIN +opa_value *opa_agg_any(opa_value *v) +{ + switch (opa_value_type(v)) + { + case OPA_ARRAY: { + opa_array_t *a = opa_cast_array(v); + + for (int i = 0; i < a->len; i++) + { + if (opa_value_type(a->elems[i].v) == OPA_BOOLEAN && opa_cast_boolean(a->elems[i].v)->v == true) + { + return opa_boolean(true); + } + } + + return opa_boolean(false); + } + case OPA_SET: { + opa_set_t *s = opa_cast_set(v); + if (s->len == 0) + { + return opa_boolean(false); + } + + opa_boolean_t b = { .hdr.type = OPA_BOOLEAN, .v = true}; + return opa_boolean(opa_set_get(s, &b.hdr) == NULL ? false : true); + } + default: + return NULL; + } +} + +OPA_BUILTIN +opa_value *builtin_member(opa_value *v, opa_value *collection) +{ + opa_value *prev = NULL; + opa_value *curr = NULL; + while ((curr = opa_value_iter(collection, prev)) != NULL) + { + if (opa_value_compare(v, opa_value_get(collection, curr)) == 0) + { + return opa_boolean(true); + } + prev = curr; + } + return opa_boolean(false); +} + +OPA_BUILTIN +opa_value *builtin_member3(opa_value *key, opa_value *val, opa_value *collection) +{ + switch (opa_value_type(collection)) + { + case OPA_ARRAY: + case OPA_OBJECT: + return opa_boolean(opa_value_compare(val, opa_value_get(collection, key)) == 0); + } + return opa_boolean(false); +} \ No newline at end of file diff --git a/third_party/opa/wasm/src/aggregates.h b/third_party/opa/wasm/src/aggregates.h new file mode 100644 index 000000000000..38660256bea9 --- /dev/null +++ b/third_party/opa/wasm/src/aggregates.h @@ -0,0 +1,15 @@ +#ifndef OPA_AGGREGATES_H +#define OPA_AGGREGATES_H + +#include "value.h" + +opa_value *opa_agg_count(opa_value *v); +opa_value *opa_agg_sum(opa_value *v); +opa_value *opa_agg_product(opa_value *v); +opa_value *opa_agg_max(opa_value *v); +opa_value *opa_agg_min(opa_value *v); +opa_value *opa_agg_sort(opa_value *v); +opa_value *opa_agg_all(opa_value *v); +opa_value *opa_agg_any(opa_value *v); + +#endif diff --git a/third_party/opa/wasm/src/arithmetic.c b/third_party/opa/wasm/src/arithmetic.c new file mode 100644 index 000000000000..5e1918bd749e --- /dev/null +++ b/third_party/opa/wasm/src/arithmetic.c @@ -0,0 +1,246 @@ +#include +#include + +#include "mpd.h" +#include "set.h" +#include "std.h" +#include "str.h" +#include "value.h" + +OPA_BUILTIN +opa_value *opa_arith_abs(opa_value *v) +{ + mpd_t *n = opa_number_to_bf(v); + if (n == NULL) + { + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qabs(r, n, mpd_max_ctx(), &status); + mpd_del(n); + + if (status != 0) + { + opa_abort("opa_number_to_bf: invalid number"); + } + + return opa_bf_to_number(r); +} + +OPA_BUILTIN +opa_value *opa_arith_round(opa_value *v) +{ + mpd_t *n = opa_number_to_bf(v); + if (n == NULL) + { + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qround_to_int(r, n, mpd_max_ctx(), &status); + mpd_del(n); + + if (status != 0) + { + opa_abort("opa_arith_round: invalid number"); + } + + return opa_bf_to_number(r); +} + +OPA_BUILTIN +opa_value *opa_arith_ceil(opa_value *v) +{ + mpd_t *n = opa_number_to_bf(v); + if (n == NULL) + { + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qceil(r, n, mpd_max_ctx(), &status); + mpd_del(n); + + if (status) + { + return NULL; + } + + return opa_bf_to_number(r); +} + +OPA_BUILTIN +opa_value *opa_arith_floor(opa_value *v) +{ + mpd_t *n = opa_number_to_bf(v); + if (n == NULL) + { + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qfloor(r, n, mpd_max_ctx(), &status); + mpd_del(n); + + if (status) + { + return NULL; + } + + return opa_bf_to_number(r); +} + +OPA_BUILTIN +opa_value *opa_arith_plus(opa_value *a, opa_value *b) +{ + mpd_t *x = opa_number_to_bf(a); + mpd_t *y = opa_number_to_bf(b); + if (x == NULL || y == NULL) + { + opa_mpd_del(x); + opa_mpd_del(y); + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qadd(r, x, y, mpd_max_ctx(), &status); + mpd_del(x); + mpd_del(y); + + status &= ~(MPD_Rounded | MPD_Inexact); + if (status != 0) + { + opa_abort("opa_arith_plus: invalid number"); + } + + return opa_bf_to_number(r); +} + +OPA_BUILTIN +opa_value *opa_arith_minus(opa_value *a, opa_value *b) +{ + mpd_t *x = opa_number_to_bf(a); + mpd_t *y = opa_number_to_bf(b); + if (x != NULL && y != NULL) + { + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qsub(r, x, y, mpd_max_ctx(), &status); + mpd_del(x); + mpd_del(y); + + status &= ~(MPD_Rounded | MPD_Inexact); + if (status != 0) + { + opa_abort("opa_arith_minus: invalid number"); + } + + return opa_bf_to_number(r); + } + + opa_mpd_del(x); + opa_mpd_del(y); + + return opa_set_diff(a, b); +} + +OPA_BUILTIN +opa_value *opa_arith_multiply(opa_value *a, opa_value *b) +{ + mpd_t *x = opa_number_to_bf(a); + mpd_t *y = opa_number_to_bf(b); + if (x == NULL || y == NULL) + { + opa_mpd_del(x); + opa_mpd_del(y); + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qmul(r, x, y, mpd_max_ctx(), &status); + mpd_del(x); + mpd_del(y); + + status &= ~(MPD_Rounded | MPD_Inexact); + if (status != 0) + { + opa_abort("opa_arith_multiply: invalid number"); + } + + return opa_bf_to_number(r); +} + +OPA_BUILTIN +opa_value *opa_arith_divide(opa_value *a, opa_value *b) +{ + mpd_t *x = opa_number_to_bf(a); + mpd_t *y = opa_number_to_bf(b); + if (x == NULL || y == NULL) + { + opa_mpd_del(x); + opa_mpd_del(y); + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + // Use the default context to enforce rounding, similar to golang. + mpd_qdiv(r, x, y, mpd_default_ctx(), &status); + mpd_del(x); + mpd_del(y); + + if (status & MPD_Division_by_zero) + { + return NULL; + } + + status &= ~(MPD_Rounded | MPD_Inexact); + if (status != 0) + { + opa_abort("opa_arith_divide: invalid number"); // TODO(sr): when does this happen? + } + + return opa_bf_to_number(r); +} + +OPA_BUILTIN +opa_value *opa_arith_rem(opa_value *a, opa_value *b) +{ + mpd_t *x = opa_number_to_bf(a); + mpd_t *y = opa_number_to_bf(b); + if (x == NULL || y == NULL || !mpd_isinteger(x) || !mpd_isinteger(y)) + { + opa_mpd_del(x); + opa_mpd_del(y); + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qrem(r, x, y, mpd_max_ctx(), &status); + mpd_del(x); + mpd_del(y); + + if (status) + { + return NULL; + } + + return opa_bf_to_number(r); +} diff --git a/third_party/opa/wasm/src/arithmetic.h b/third_party/opa/wasm/src/arithmetic.h new file mode 100644 index 000000000000..569471feea13 --- /dev/null +++ b/third_party/opa/wasm/src/arithmetic.h @@ -0,0 +1,19 @@ +#ifndef OPA_ARITHMETIC_H +#define OPA_ARITHMETIC_H + +#include + +#include "value.h" + +opa_value *opa_arith_abs(opa_value *v); +opa_value *opa_arith_round(opa_value *v); +opa_value *opa_arith_ceil(opa_value *v); +opa_value *opa_arith_floor(opa_value *v); +opa_value *opa_arith_plus(opa_value *a, opa_value *b); +opa_value *opa_arith_minus(opa_value *a, opa_value *b); +opa_value *opa_arith_multiply(opa_value *a, opa_value *b); +opa_value *opa_arith_divide(opa_value *a, opa_value *b); +opa_value *opa_arith_rem(opa_value *a, opa_value *b); + + +#endif diff --git a/third_party/opa/wasm/src/array.c b/third_party/opa/wasm/src/array.c new file mode 100644 index 000000000000..595fb988db0d --- /dev/null +++ b/third_party/opa/wasm/src/array.c @@ -0,0 +1,89 @@ +#include "std.h" +#include "value.h" + +OPA_BUILTIN +opa_value *opa_array_concat(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_ARRAY || opa_value_type(b) != OPA_ARRAY) + { + return NULL; + } + + opa_array_t *x = opa_cast_array(a); + opa_array_t *y = opa_cast_array(b); + opa_array_t *r = opa_cast_array(opa_array_with_cap(x->len + y->len)); + + for (int i = 0; i < x->len; i++) + { + opa_array_append(r, x->elems[i].v); + } + + for (int i = 0; i < y->len; i++) + { + opa_array_append(r, y->elems[i].v); + } + + return &r->hdr; +} + +OPA_BUILTIN +opa_value *opa_array_slice(opa_value *a, opa_value *i, opa_value *j) +{ + if (opa_value_type(a) != OPA_ARRAY || opa_value_type(i) != OPA_NUMBER || opa_value_type(j) != OPA_NUMBER) + { + return NULL; + } + + opa_array_t *arr = opa_cast_array(a); + long long start; + long long stop; + + if (opa_number_try_int(opa_cast_number(i), &start) != 0 || + opa_number_try_int(opa_cast_number(j), &stop) != 0) + { + return NULL; + } + + if (stop < 0) + { + stop = 0; + } else if (stop > arr->len) { + stop = arr->len; + } + + if (start < 0) { + start = 0; + } else if (start > stop) { + start = stop; + } + + opa_array_t *r = opa_cast_array(opa_array_with_cap(stop-start)); + + for (int i = start; i < stop; i++) + { + opa_array_append(r, arr->elems[i].v); + } + + return &r->hdr; +} + +OPA_BUILTIN +opa_value *opa_array_reverse(opa_value *a) +{ + if (opa_value_type(a) != OPA_ARRAY) + { + return NULL; + } + + opa_array_t *arr = opa_cast_array(a); + + opa_array_t *reversed = opa_cast_array(opa_array_with_cap(arr->len)); + + int n = arr->len; + + for (int i = 0; i < n; i++) { + opa_array_append(reversed, arr->elems[n - 1 - i].v); + } + + return &reversed->hdr; +} diff --git a/third_party/opa/wasm/src/array.h b/third_party/opa/wasm/src/array.h new file mode 100644 index 000000000000..e1bcdce904e4 --- /dev/null +++ b/third_party/opa/wasm/src/array.h @@ -0,0 +1,8 @@ +#ifndef OPA_ARRAY_H +#define OPA_ARRAY_H + +opa_value *opa_array_concat(opa_value *a, opa_value *b); +opa_value *opa_array_slice(opa_value *a, opa_value *i, opa_value *j); +opa_value *opa_array_reverse(opa_value *a); + +#endif diff --git a/third_party/opa/wasm/src/bits-builtins.c b/third_party/opa/wasm/src/bits-builtins.c new file mode 100644 index 000000000000..d09468df9c7b --- /dev/null +++ b/third_party/opa/wasm/src/bits-builtins.c @@ -0,0 +1,243 @@ +#include "arithmetic.h" +#include "bits.h" +#include "mpd.h" +#include "std.h" + +#define swap(x, y) { mpd_t *v = x; x = y; y = v; } + +OPA_BUILTIN +opa_value *opa_bits_or(opa_value *a, opa_value *b) +{ + mpd_t *x = opa_number_to_bf(a); + mpd_t *y = opa_number_to_bf(b); + if (x == NULL || y == NULL || !mpd_isinteger(x) || !mpd_isinteger(y)) + { + opa_mpd_del(x); + opa_mpd_del(y); + return NULL; + } + + if (mpd_sign(x) == mpd_sign(y)) + { + if (mpd_sign(x)) // x neg + { + // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) + mpd_t *x1 = qsub_one(qabs(x)); + mpd_t *y1 = qsub_one(qabs(y)); + mpd_t *z = qadd_one(qand(x1, y1)); + return opa_bf_to_number(qneg(z)); + } + + // x | y == x | y + return opa_bf_to_number(qor(x, y)); + } + + // x.neg != y.neg + if (mpd_sign(x)) + { + // | is symmetric + swap(x, y); + } + + // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) + mpd_t *y1 = qsub_one(qabs(y)); + mpd_t *z = qadd_one(qand_not(y1, x)); + return opa_bf_to_number(qneg(z)); +} + +OPA_BUILTIN +opa_value *opa_bits_and(opa_value *a, opa_value *b) +{ + mpd_t *x = opa_number_to_bf(a); + mpd_t *y = opa_number_to_bf(b); + if (x == NULL || y == NULL || !mpd_isinteger(x) || !mpd_isinteger(y)) + { + opa_mpd_del(x); + opa_mpd_del(y); + return NULL; + } + + if (mpd_sign(x) == mpd_sign(y)) + { + if (mpd_sign(x)) // x neg + { + // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) + mpd_t *x1 = qsub_one(qabs(x)); + mpd_t *y1 = qsub_one(qabs(y)); + mpd_t *z = qadd_one(qor(x1, y1)); + return opa_bf_to_number(qneg(z)); + } + + // x & y == x & y + return opa_bf_to_number(qand(x, y)); + } + + // x.neg != y.neg + if (mpd_sign(x)) + { + // & is symmetric + swap(x, y); + } + + // x & (-y) == x & ^(y-1) == x &^ (y-1) + mpd_t *y1 = qsub_one(qabs(y)); + mpd_t *z = qand_not(x, y1); + return opa_bf_to_number(z); +} + +OPA_BUILTIN +opa_value *opa_bits_negate(opa_value *a) +{ + mpd_t *x = opa_number_to_bf(a); + if (x == NULL || !mpd_isinteger(x)) + { + return NULL; + } + + if (mpd_sign(x)) + { + // ^(-x) == ^(^(x-1)) == x-1 + + x = opa_bf_to_bf_bits(qabs(x)); + if (x == NULL) + { + // Not an integer. + return NULL; + } + + x = opa_bf_bits_to_bf(x); + mpd_t *z = qsub_one(x); + return opa_bf_to_number(z); + } + + // ^x == -x-1 == -(x+1) + return opa_bf_to_number(qneg(qadd_one(x))); +} + +OPA_BUILTIN +opa_value *opa_bits_xor(opa_value *a, opa_value *b) +{ + mpd_t *x = opa_number_to_bf(a); + mpd_t *y = opa_number_to_bf(b); + if (x == NULL || y == NULL || !mpd_isinteger(x) || !mpd_isinteger(y)) + { + opa_mpd_del(x); + opa_mpd_del(y); + return NULL; + } + + if (mpd_sign(x) == mpd_sign(y)) + { + if (mpd_sign(x)) // x neg + { + // (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1) + mpd_t *x1 = qsub_one(qabs(x)); + mpd_t *y1 = qsub_one(qabs(y)); + return opa_bf_to_number(qxor(x1, y1)); + } + + // x ^ y == x ^ y + return opa_bf_to_number(qxor(x, y)); + } + + // x.neg != y.neg + if (mpd_sign(x)) + { + // ^ is symmetric + swap(x, y); + } + + // x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1) + mpd_t *y1 = qsub_one(qabs(y)); + mpd_t *z = qadd_one(qxor(x, y1)); + return opa_bf_to_number(qneg(z)); +} + +OPA_BUILTIN +opa_value *opa_bits_shiftleft(opa_value *a, opa_value *b) +{ + mpd_t *x = opa_bf_to_bf_bits(opa_number_to_bf(a)); + if (x == NULL) + { + opa_mpd_del(x); + return NULL; + } + + if (opa_value_type(b) != OPA_NUMBER) + { + mpd_del(x); + return NULL; + } + + long long n; + if (opa_number_try_int(opa_cast_number(b), &n) || n < 0) + { + mpd_del(x); + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qshiftn(r, x, n, mpd_max_ctx(), &status); + mpd_del(x); + + if (status) + { + opa_abort("opa_bits_shift"); + } + + return opa_bf_to_number(opa_bf_bits_to_bf(r)); +} + +OPA_BUILTIN +opa_value *opa_bits_shiftright(opa_value *a, opa_value *b) +{ + mpd_t *x = opa_number_to_bf(a); + if (x == NULL || !mpd_isinteger(x)) + { + return NULL; + } + + if (opa_value_type(b) != OPA_NUMBER) + { + mpd_del(x); + return NULL; + } + + long long n; + if (opa_number_try_int(opa_cast_number(b), &n) || n < 0) + { + mpd_del(x); + return NULL; + } + + if (mpd_sign(x)) + { + // (-x) >> s == ^(x-1) >> s == ^((x-1) >> s) == -(((x-1) >> s) + 1) + mpd_t *t = qsub_one(qabs(x)); + + t = opa_bf_to_bf_bits(t); + if (t == NULL) + { + // Not an integer. + return NULL; + } + + mpd_qshiftr_inplace(t, n); + + mpd_t *z = qneg(qadd_one(opa_bf_bits_to_bf(t))); + return opa_bf_to_number(z); + } + + x = opa_bf_to_bf_bits(x); + if (x == NULL) + { + // Not an integer. + return NULL; + } + + mpd_qshiftr_inplace(x, n); + + return opa_bf_to_number(opa_bf_bits_to_bf(x)); +} diff --git a/third_party/opa/wasm/src/bits-builtins.h b/third_party/opa/wasm/src/bits-builtins.h new file mode 100644 index 000000000000..cdc13964159b --- /dev/null +++ b/third_party/opa/wasm/src/bits-builtins.h @@ -0,0 +1,13 @@ +#ifndef OPA_BITS_BUILTINS_H +#define OPA_BITS_BUILTINS_H + +#include "value.h" + +opa_value *opa_bits_or(opa_value *a, opa_value *b); +opa_value *opa_bits_and(opa_value *a, opa_value *b); +opa_value *opa_bits_negate(opa_value *v); +opa_value *opa_bits_xor(opa_value *a, opa_value *b); +opa_value *opa_bits_shiftleft(opa_value *a, opa_value *b); +opa_value *opa_bits_shiftright(opa_value *a, opa_value *b); + +#endif diff --git a/third_party/opa/wasm/src/cidr.c b/third_party/opa/wasm/src/cidr.c new file mode 100644 index 000000000000..462e0f2628d6 --- /dev/null +++ b/third_party/opa/wasm/src/cidr.c @@ -0,0 +1,340 @@ +#include +#include + +#include "std.h" +#include "str.h" +#include "value.h" + +typedef struct { + uint8_t len; + unsigned char ip[16]; + unsigned char mask[16]; +} ip_net; + +typedef unsigned char u_char; +typedef unsigned int u_int; + +static int inet_pton4(const char *src, const char *end, u_char *dst); +static int inet_pton6(const char *src, const char *end, u_char *dst); + +static bool parse_ip(const char *src, int n, ip_net *dst) +{ + for (int i = 0; i < n; i++) { + if (src[i] == '.') + { + dst->len = 4; + memset(dst->mask, 0xff, 4); + return inet_pton4(src, src + n, dst->ip); + } + else if (src[i] == ':') + { + dst->len = 16; + memset(dst->mask, 0xff, 16); + return inet_pton6(src, src + n, dst->ip); + } + } + + return false; +} + +static bool parse_cidr(const char *src, size_t n, ip_net *dst) +{ + const char *slash = NULL; + for (size_t i = 0; i < n; i++) + { + if (src[i] == '/') + { + slash = &src[i]; + break; + } + } + + if (slash == NULL) + { + return false; + } + + const char *addr = src; + const size_t len = slash - src; + if (!parse_ip(addr, len, dst)) + { + return false; + } + + const char *mask = slash + 1; + long long bits; + if (opa_atoi64(mask, n - len - 1, &bits) == -1 || bits < 0 || bits > dst->len*8) + { + return false; + } + + for (int i = 0; i < dst->len; i++) + { + if (bits >= 8) { + dst->mask[i] = 0xff; + bits -= 8; + continue; + } + + dst->mask[i] = ~((unsigned char)(0xff >> bits)); + bits = 0; + } + + for (int i = 0; i < dst->len; i++) + { + dst->ip[i] &= dst->mask[i]; + } + + return true; +} + +// returns true if a contains b. +static bool contains(ip_net *a, ip_net *b) +{ + if (a->len != b->len) + { + return false; + } + + for (int i = 0; i < a->len; i++) + { + if (a->mask[i] & ~b->mask[i]) + { + // If b mask is shorter, a can never contain b. For + // example, 192.168/16 (a) doesn't contain 192.168/15 (b). + // The above logical operation checks if the b mask is + // shorter. For example, consider the masks a and b: + // + // | mask + // -------+---------- + // a | 11111000 + // b | 11110000 + // ~b | 00001111 + // a & ~b | 00001000 + // + return false; + } + + // Since b mask may be longer than a, use the a mask to ignore + // the b bits not relevant for comparison. Note, ips are + // already masked at the construction time with their own + // masks (see L84 above) so they don't have bits beyond their + // mask length. + if (a->ip[i] != (b->ip[i] & a->mask[i])) + { + return false; + } + } + + return true; +} + +OPA_BUILTIN +opa_value *opa_cidr_contains(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + ip_net ip_a, ip_b; + + opa_string_t *s = opa_cast_string(a); + if (!parse_cidr(s->v, s->len, &ip_a)) { + return NULL; + } + + s = opa_cast_string(b); + if (!parse_ip(s->v, s->len, &ip_b) && !parse_cidr(s->v, s->len, &ip_b)) { + return NULL; + } + + return opa_boolean(contains(&ip_a, &ip_b)); +} + +OPA_BUILTIN +opa_value *opa_cidr_intersects(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + opa_string_t *as = opa_cast_string(a); + opa_string_t *bs = opa_cast_string(b); + ip_net ip_a, ip_b; + if (!parse_cidr(as->v, as->len, &ip_a) || !parse_cidr(bs->v, bs->len, &ip_b)) { + return NULL; + } + + return opa_boolean(contains(&ip_a, &ip_b) || contains(&ip_b, &ip_a)); +} + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* int + * inet_pton4(src, dst) + * like inet_aton() but without all the hexadecimal and shorthand. + * return: + * 1 if `src' is a valid dotted quad, else 0. + * notice: + * does not touch `dst' unless it's returning 1. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton4(const char *src, const char *stop, u_char *dst) +{ + static const char digits[] = "0123456789"; + int saw_digit, octets, ch; +#define NS_INADDRSZ 4 + u_char tmp[NS_INADDRSZ], *tp; + + saw_digit = 0; + octets = 0; + *(tp = tmp) = 0; + while (src != stop && (ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr(digits, ch)) != NULL) { + u_int new = *tp * 10 + (pch - digits); + + if (saw_digit && *tp == 0) + return (0); + if (new > 255) + return (0); + *tp = new; + if (!saw_digit) { + if (++octets > 4) + return (0); + saw_digit = 1; + } + } else if (ch == '.' && saw_digit) { + if (octets == 4) + return (0); + *++tp = 0; + saw_digit = 0; + } else + return (0); + } + if (octets < 4) + return (0); + memcpy(dst, tmp, NS_INADDRSZ); + return (1); +} + +/* int + * inet_pton6(src, dst) + * convert presentation level address to network order binary form. + * return: + * 1 if `src' is a valid [RFC1884 2.2] address, else 0. + * notice: + * (1) does not touch `dst' unless it's returning 1. + * (2) :: in a full address is silently ignored. + * credit: + * inspired by Mark Andrews. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton6(const char *src, const char *stop, u_char *dst) +{ + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; +#define NS_IN6ADDRSZ 16 +#define NS_INT16SZ 2 + u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; + const char *xdigits, *curtok, *end; + int ch, seen_xdigits; + u_int val; + + memset((tp = tmp), '\0', NS_IN6ADDRSZ); + endp = tp + NS_IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + return (0); + curtok = src; + seen_xdigits = 0; + val = 0; + while (src != stop && (ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (++seen_xdigits > 4) + return (0); + continue; + } + if (ch == ':') { + curtok = src; + if (!seen_xdigits) { + if (colonp) + return (0); + colonp = tp; + continue; + } else if (*src == '\0') { + return (0); + } + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + seen_xdigits = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && + inet_pton4(curtok, stop, tp) > 0) { + tp += NS_INADDRSZ; + seen_xdigits = 0; + break; /*%< '\\0' was seen by inet_pton4(). */ + } + return (0); + } + if (seen_xdigits) { + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + } + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + int i; + + if (tp == endp) + return (0); + for (i = 1; i <= n; i++) { + endp[- i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return (0); + memcpy(dst, tmp, NS_IN6ADDRSZ); + return (1); +} diff --git a/third_party/opa/wasm/src/cidr.h b/third_party/opa/wasm/src/cidr.h new file mode 100644 index 000000000000..6e4bbe43b506 --- /dev/null +++ b/third_party/opa/wasm/src/cidr.h @@ -0,0 +1,9 @@ +#ifndef OPA_CIDR_H +#define OPA_CIDR_H + +#include "value.h" + +opa_value *opa_cidr_contains(opa_value *net, opa_value *ip_or_net); +opa_value *opa_cidr_intersects(opa_value *a, opa_value *b); + +#endif diff --git a/third_party/opa/wasm/src/comparisons.c b/third_party/opa/wasm/src/comparisons.c new file mode 100644 index 000000000000..68b60191f06a --- /dev/null +++ b/third_party/opa/wasm/src/comparisons.c @@ -0,0 +1,38 @@ +#include "value.h" +#include "comparisons.h" + +OPA_BUILTIN +opa_value *opa_cmp_eq(opa_value *a, opa_value *b) +{ + return opa_boolean(opa_value_compare(a, b) == 0); +} + +OPA_BUILTIN +opa_value *opa_cmp_neq(opa_value *a, opa_value *b) +{ + return opa_boolean(opa_value_compare(a, b) != 0); +} + +OPA_BUILTIN +opa_value *opa_cmp_gt(opa_value *a, opa_value *b) +{ + return opa_boolean(opa_value_compare(a, b) > 0); +} + +OPA_BUILTIN +opa_value *opa_cmp_gte(opa_value *a, opa_value *b) +{ + return opa_boolean(opa_value_compare(a, b) >= 0); +} + +OPA_BUILTIN +opa_value *opa_cmp_lt(opa_value *a, opa_value *b) +{ + return opa_boolean(opa_value_compare(a, b) < 0); +} + +OPA_BUILTIN +opa_value *opa_cmp_lte(opa_value *a, opa_value *b) +{ + return opa_boolean(opa_value_compare(a, b) <= 0); +} diff --git a/third_party/opa/wasm/src/comparisons.h b/third_party/opa/wasm/src/comparisons.h new file mode 100644 index 000000000000..0f1e870a329b --- /dev/null +++ b/third_party/opa/wasm/src/comparisons.h @@ -0,0 +1,8 @@ +#include "value.h" + +opa_value *opa_cmp_eq(opa_value *a, opa_value *b); +opa_value *opa_cmp_neq(opa_value *a, opa_value *b); +opa_value *opa_cmp_gt(opa_value *a, opa_value *b); +opa_value *opa_cmp_gte(opa_value *a, opa_value *b); +opa_value *opa_cmp_lt(opa_value *a, opa_value *b); +opa_value *opa_cmp_lte(opa_value *a, opa_value *b); diff --git a/third_party/opa/wasm/src/context.c b/third_party/opa/wasm/src/context.c new file mode 100644 index 000000000000..5a7c2b8515af --- /dev/null +++ b/third_party/opa/wasm/src/context.c @@ -0,0 +1,82 @@ +#include "malloc.h" +#include "context.h" +#include "stdlib.h" +#include "value.h" +#include "json.h" + +WASM_EXPORT(opa_eval_ctx_new) +opa_eval_ctx_t *opa_eval_ctx_new() +{ + opa_eval_ctx_t *ctx = (opa_eval_ctx_t *)opa_malloc(sizeof(opa_eval_ctx_t)); + ctx->input = NULL; + ctx->data = NULL; + ctx->result = NULL; + ctx->entrypoint = 0; + return ctx; +} + +WASM_EXPORT(opa_eval_ctx_set_input) +void opa_eval_ctx_set_input(opa_eval_ctx_t *ctx, opa_value *v) +{ + ctx->input = v; +} + +WASM_EXPORT(opa_eval) +char *opa_eval(void *reserved, int entrypoint, opa_value *data, char *input, uint32_t input_len, uint32_t heap, bool want_value) +{ + if (reserved != NULL) { + opa_abort("invalid reserved argument"); + } + + opa_heap_ptr_set(heap); + opa_eval_ctx_t ctx = { + .entrypoint = entrypoint, + .data = data, + .input = opa_value_parse(input, input_len), + }; + + if (eval(&ctx) != 0) { + opa_abort("eval failed"); + } + if (want_value) { + return opa_value_dump(ctx.result); + } + return opa_json_dump(ctx.result); +} + +// NOTE(sr): Without this attribute set, LLVM would not let this function +// make it into the Wasm module unchanged. We need it there, so the wasm +// compiler in OPA can replace _this_ eval with _its_ eval, compiled from +// rego. +__attribute__((optnone)) +int32_t eval(opa_eval_ctx_t *ctx) { + return 0; +} + +WASM_EXPORT(opa_eval_ctx_set_data) +void opa_eval_ctx_set_data(opa_eval_ctx_t *ctx, opa_value *v) +{ + ctx->data = v; +} + +WASM_EXPORT(opa_eval_ctx_set_entrypoint) +void opa_eval_ctx_set_entrypoint(opa_eval_ctx_t *ctx, int entrypoint) +{ + ctx->entrypoint = entrypoint; +} + +WASM_EXPORT(opa_eval_ctx_get_result) +opa_value *opa_eval_ctx_get_result(opa_eval_ctx_t *ctx) +{ + return ctx->result; +} + +OPA_INTERNAL +void __force_import_opa_builtins() +{ + opa_builtin0(-1, NULL); + opa_builtin1(-1, NULL, NULL); + opa_builtin2(-1, NULL, NULL, NULL); + opa_builtin3(-1, NULL, NULL, NULL, NULL); + opa_builtin4(-1, NULL, NULL, NULL, NULL, NULL); +} diff --git a/third_party/opa/wasm/src/context.h b/third_party/opa/wasm/src/context.h new file mode 100644 index 000000000000..c0efe0f16b89 --- /dev/null +++ b/third_party/opa/wasm/src/context.h @@ -0,0 +1,28 @@ +#ifndef OPA_CONTEXT_H +#define OPA_CONTEXT_H + +#include "value.h" + +typedef struct +{ + opa_value *input; + opa_value *data; + opa_value *result; + int entrypoint; +} opa_eval_ctx_t; + +opa_eval_ctx_t *opa_eval_ctx_new(); +void opa_eval_ctx_set_input(opa_eval_ctx_t *ctx, opa_value *v); +void opa_eval_ctx_set_data(opa_eval_ctx_t *ctx, opa_value *v); +void opa_eval_ctx_set_entrypoint(opa_eval_ctx_t *ctx, int entrypoint); +opa_value *opa_eval_ctx_get_result(opa_eval_ctx_t *ctx); + +opa_value *opa_builtin0(int, void *); +opa_value *opa_builtin1(int, void *, opa_value *); +opa_value *opa_builtin2(int, void *, opa_value *, opa_value *); +opa_value *opa_builtin3(int, void *, opa_value *, opa_value *, opa_value *); +opa_value *opa_builtin4(int, void *, opa_value *, opa_value *, opa_value *, opa_value *); + +int32_t eval(opa_eval_ctx_t *ctx); + +#endif \ No newline at end of file diff --git a/third_party/opa/wasm/src/conversions.c b/third_party/opa/wasm/src/conversions.c new file mode 100644 index 000000000000..79734b11d1e0 --- /dev/null +++ b/third_party/opa/wasm/src/conversions.c @@ -0,0 +1,34 @@ +#include "std.h" +#include "str.h" +#include "conversions.h" + +OPA_BUILTIN +opa_value *opa_to_number(opa_value *v) +{ + switch (opa_value_type(v)) + { + case OPA_NULL: + return opa_number_int(0); + case OPA_BOOLEAN: + { + opa_boolean_t *a = opa_cast_boolean(v); + return opa_number_int(a->v ? 1 : 0); + } + case OPA_NUMBER: + return v; + case OPA_STRING: + { + opa_string_t *a = opa_cast_string(v); + double n; + // NOTE: we're only using opa_atof64 for validation here + if (opa_atof64(a->v, a->len, &n) != 0) + { + return NULL; + } + + return opa_number_ref(a->v, a->len); + } + default: + return NULL; + } +} diff --git a/third_party/opa/wasm/src/conversions.h b/third_party/opa/wasm/src/conversions.h new file mode 100644 index 000000000000..dd4d59cc5e8a --- /dev/null +++ b/third_party/opa/wasm/src/conversions.h @@ -0,0 +1,8 @@ +#ifndef OPA_CONVERSIONS_H +#define OPA_CONVERSIONS_H + +#include "value.h" + +opa_value *opa_to_number(opa_value *v); + +#endif \ No newline at end of file diff --git a/third_party/opa/wasm/src/encoding.c b/third_party/opa/wasm/src/encoding.c new file mode 100644 index 000000000000..9b22d7f64197 --- /dev/null +++ b/third_party/opa/wasm/src/encoding.c @@ -0,0 +1,340 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005-2019, Jouni Malinen + * + * + * This software may be distributed, used, and modified under the terms of + * BSD license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name(s) of the above-listed copyright holder(s) nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include + +#include "json.h" +#include "malloc.h" +#include "value.h" + +static const unsigned char base64_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const unsigned char base64_url_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +static unsigned char * base64_gen_encode(const unsigned char *src, size_t len, + size_t *out_len, + const unsigned char *table, + int add_pad) +{ + unsigned char *out, *pos; + const unsigned char *end, *in; + size_t olen; + + if (len >= SIZE_MAX / 4) + return NULL; + olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ + olen++; /* nul termination */ + if (olen < len) + return NULL; /* integer overflow */ + out = malloc(olen); + if (out == NULL) + return NULL; + + end = src + len; + in = src; + pos = out; + while (end - in >= 3) { + *pos++ = table[(in[0] >> 2) & 0x3f]; + *pos++ = table[(((in[0] & 0x03) << 4) | (in[1] >> 4)) & 0x3f]; + *pos++ = table[(((in[1] & 0x0f) << 2) | (in[2] >> 6)) & 0x3f]; + *pos++ = table[in[2] & 0x3f]; + in += 3; + } + + if (end - in) { + *pos++ = table[(in[0] >> 2) & 0x3f]; + if (end - in == 1) { + *pos++ = table[((in[0] & 0x03) << 4) & 0x3f]; + if (add_pad) + *pos++ = '='; + } else { + *pos++ = table[(((in[0] & 0x03) << 4) | + (in[1] >> 4)) & 0x3f]; + *pos++ = table[((in[1] & 0x0f) << 2) & 0x3f]; + } + if (add_pad) + *pos++ = '='; + } + + *pos = '\0'; + if (out_len) + *out_len = pos - out; + return out; +} + + +static unsigned char * base64_gen_decode(const unsigned char *src, size_t len, + size_t *out_len, + const unsigned char *table) +{ + unsigned char dtable[256], *out, *pos, block[4], tmp; + size_t i, count, olen; + int pad = 0; + size_t extra_pad; + + memset(dtable, 0x80, 256); + for (i = 0; i < sizeof(base64_table) - 1; i++) + dtable[table[i]] = (unsigned char) i; + dtable['='] = 0; + + count = 0; + for (i = 0; i < len; i++) { + if (dtable[src[i]] != 0x80) + count++; + } + + if (count == 0) + return NULL; + extra_pad = (4 - count % 4) % 4; + + olen = (count + extra_pad) / 4 * 3; + pos = out = malloc(olen); + if (out == NULL) + return NULL; + + count = 0; + for (i = 0; i < len + extra_pad; i++) { + unsigned char val; + + if (i >= len) + val = '='; + else + val = src[i]; + tmp = dtable[val]; + if (tmp == 0x80) + continue; + + if (val == '=') + pad++; + block[count] = tmp; + count++; + if (count == 4) { + *pos++ = (block[0] << 2) | (block[1] >> 4); + *pos++ = (block[1] << 4) | (block[2] >> 2); + *pos++ = (block[2] << 6) | block[3]; + count = 0; + if (pad) { + if (pad == 1) + pos--; + else if (pad == 2) + pos -= 2; + else { + /* Invalid padding */ + free(out); + return NULL; + } + break; + } + } + } + + *out_len = pos - out; + return out; +} + + +/** + * base64_encode - Base64 encode + * @src: Data to be encoded + * @len: Length of the data to be encoded + * @out_len: Pointer to output length variable, or %NULL if not used + * Returns: Allocated buffer of out_len bytes of encoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. Returned buffer is + * nul terminated to make it easier to use as a C string. The nul terminator is + * not included in out_len. + */ +static unsigned char * base64_encode(const unsigned char *src, size_t len, + size_t *out_len) +{ + return base64_gen_encode(src, len, out_len, base64_table, 1); +} + + +static unsigned char * base64_url_encode(const unsigned char *src, size_t len, + size_t *out_len, int add_pad) +{ + return base64_gen_encode(src, len, out_len, base64_url_table, add_pad); +} + + +/** + * base64_decode - Base64 decode + * @src: Data to be decoded + * @len: Length of the data to be decoded + * @out_len: Pointer to output length variable + * Returns: Allocated buffer of out_len bytes of decoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. + */ +static unsigned char * base64_decode(const unsigned char *src, size_t len, + size_t *out_len) +{ + return base64_gen_decode(src, len, out_len, base64_table); +} + + +static unsigned char * base64_url_decode(const unsigned char *src, size_t len, + size_t *out_len) +{ + return base64_gen_decode(src, len, out_len, base64_url_table); +} + +OPA_BUILTIN +opa_value *opa_base64_is_valid(opa_value *a) +{ + if (opa_value_type(a) != OPA_STRING) + { + return opa_boolean(false); + } + + opa_string_t *s = opa_cast_string(a); + size_t len; + unsigned char *dec = base64_decode((const unsigned char*)s->v, s->len, &len); + if (dec == NULL) + { + return opa_boolean(false); + } + + free(dec); + return opa_boolean(true); +} + +OPA_BUILTIN +opa_value *opa_base64_decode(opa_value *a) +{ + if (opa_value_type(a) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + size_t len; + char *dec = (char *)base64_decode((const unsigned char*)s->v, s->len, &len); + return dec == NULL ? NULL : opa_string_allocated(dec, len); +} + +OPA_BUILTIN +opa_value *opa_base64_encode(opa_value *a) +{ + if (opa_value_type(a) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + size_t len; + char *enc = (char *)base64_encode((const unsigned char*)s->v, s->len, &len); + return enc == NULL ? NULL : opa_string_allocated(enc, len); +} + +OPA_BUILTIN +opa_value *opa_base64_url_decode(opa_value *a) +{ + if (opa_value_type(a) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + size_t len; + char *dec = (char *)base64_url_decode((const unsigned char*)s->v, s->len, &len); + return dec == NULL ? NULL : opa_string_allocated(dec, len); +} + +OPA_BUILTIN +opa_value *opa_base64_url_encode(opa_value *a) +{ + if (opa_value_type(a) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + size_t len; + char *enc = (char *)base64_url_encode((const unsigned char*)s->v, s->len, &len, 1); + return enc == NULL ? NULL : opa_string_allocated(enc, len); +} + +OPA_BUILTIN +opa_value *opa_json_unmarshal(opa_value *a) +{ + if (opa_value_type(a) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + return opa_json_parse(s->v, s->len); +} + +OPA_BUILTIN +opa_value *opa_json_marshal(opa_value *a) +{ + const char *v = opa_json_dump(a); + if (v == NULL) + { + return NULL; + } + + return opa_string_allocated(v, strlen(v)); +} + +OPA_BUILTIN +opa_value *opa_json_is_valid(opa_value *a) +{ + if (opa_value_type(a) != OPA_STRING) + { + return opa_boolean(false); + } + + + opa_string_t *s = opa_cast_string(a); + opa_value *r = opa_json_parse(s->v, s->len); + + if (r == NULL) + { + return opa_boolean(false); + } + + opa_free(r); + return opa_boolean(true); +} \ No newline at end of file diff --git a/third_party/opa/wasm/src/encoding.h b/third_party/opa/wasm/src/encoding.h new file mode 100644 index 000000000000..95d1fc427b1e --- /dev/null +++ b/third_party/opa/wasm/src/encoding.h @@ -0,0 +1,15 @@ +#ifndef OPA_ENCODING_H +#define OPA_ENCODING_H + +#include "value.h" + +opa_value *opa_base64_is_valid(opa_value *a); +opa_value *opa_base64_decode(opa_value *a); +opa_value *opa_base64_encode(opa_value *a); +opa_value *opa_base64_url_decode(opa_value *a); +opa_value *opa_base64_url_encode(opa_value *a); +opa_value *opa_json_unmarshal(opa_value *a); +opa_value *opa_json_marshal(opa_value *a); +opa_value *opa_json_is_valid(opa_value *a); + +#endif diff --git a/third_party/opa/wasm/src/error.c b/third_party/opa/wasm/src/error.c new file mode 100644 index 000000000000..4a5762d329ba --- /dev/null +++ b/third_party/opa/wasm/src/error.c @@ -0,0 +1,21 @@ +#include "error.h" +#include "malloc.h" +#include "mpd.h" +#include "printf.h" +#include "std.h" +#include "str.h" + +OPA_INTERNAL +void opa_runtime_error(const char *loc, int row, int col, const char *msg) +{ + char row_str[sizeof(row)*8+1]; + char col_str[sizeof(col)*8+1]; + opa_itoa(row, row_str, 10); + opa_itoa(col, col_str, 10); + // 5 = ":" + ":" + ": " + \0 + size_t len = opa_strlen(loc)+opa_strlen(row_str)+opa_strlen(col_str)+opa_strlen(msg)+5; + char *err = (char *)opa_malloc(len); + snprintf(err, len, "%s:%s:%s: %s", loc, row_str, col_str, msg); + + opa_abort(err); +} diff --git a/third_party/opa/wasm/src/error.h b/third_party/opa/wasm/src/error.h new file mode 100644 index 000000000000..fa3c9dd6d080 --- /dev/null +++ b/third_party/opa/wasm/src/error.h @@ -0,0 +1,6 @@ +#ifndef OPA_STRINGS_H +#define OPA_STRINGS_H + +void opa_runtime_error(const char *loc, int row, int col, const char *msg); + +#endif diff --git a/third_party/opa/wasm/src/glob-compiler.cc b/third_party/opa/wasm/src/glob-compiler.cc new file mode 100644 index 000000000000..5b6aaad7850f --- /dev/null +++ b/third_party/opa/wasm/src/glob-compiler.cc @@ -0,0 +1,154 @@ +#include "glob-parser.h" +#include "unicode.h" +#include "std.h" + +static const char special_characters[] = ".,:\"=<>[]^/\\{}|*+?"; + +// escape any special re2 characters in a text. +static std::string escape(const std::string& s) +{ + std::string x; + for (int i = 0; i < s.length(); i++) + { + unsigned char c = s[i]; + for (int j = 0; special_characters[j] != '\0'; j++) + { + if (special_characters[j] == s[i]) + { + x += '\\'; + } + } + + x += c; + } + + return x; +} + +std::string node::re2(const std::string& single_mark) +{ + std::string s; + + if (parent == NULL) { + s = "^"; + } + + switch (kind) + { + case kind_pattern: + for (int i = 0; i < children.size(); i++) + { + s += children[i]->re2(single_mark); + } + break; + + case kind_list: + s += "["; + + if (not_) + { + s += "^"; + } + + s += escape(text); + s += "]"; + + break; + + case kind_range: + s += "["; + + if (not_) + { + s += "^"; + } + s += lo; + s += "-"; + s += hi; + + s += "]"; + + break; + + case kind_text: + s += escape(text); + break; + + case kind_any: + s += single_mark + "*"; + break; + + case kind_super: + s += ".*"; + break; + + case kind_single: + s += single_mark; + break; + + case kind_any_of: + s += "("; + + for (int i = 0; i < children.size(); i++) + { + if (i > 0) + { + s += "|"; + } + s += children[i]->re2(single_mark); + } + + s += ")"; + break; + + default: + break; + } + + if (parent == NULL) + { + s += "$"; + } + + return s; +} + +std::string glob_translate(const char *glob, size_t n, const std::vector& delimiters, std::string *re2) +{ + lexer *l = new lexer(glob, n); + node *root = NULL; + std::string error = glob_parse(l, &root); + if (error != "") + { + delete l; + return error; + } + + std::string single_mark; + + if (delimiters.empty()) + { + single_mark = "."; + } else { + single_mark = "[^"; + + for (int i = 0; i < delimiters.size(); i++) + { + int len; + if (opa_unicode_decode_utf8(delimiters[i].c_str(), 0, delimiters[i].length(), &len) < 0 || len != delimiters[i].length()) + { + return "delimiter is not a single character"; + } + + single_mark += escape(delimiters[i]); + } + + single_mark += "]"; + } + + *re2 = root->re2(single_mark); + delete(root); + delete(l); + return ""; +} + diff --git a/third_party/opa/wasm/src/glob-compiler.h b/third_party/opa/wasm/src/glob-compiler.h new file mode 100644 index 000000000000..6cff3455f156 --- /dev/null +++ b/third_party/opa/wasm/src/glob-compiler.h @@ -0,0 +1,9 @@ +#ifndef OPA_GLOB_COMPILER +#define OPA_GLOB_COMPILER + +#include +#include + +std::string glob_translate(const char *glob, size_t n, const std::vector& delimiters, std::string *re2); + +#endif diff --git a/third_party/opa/wasm/src/glob-lexer.cc b/third_party/opa/wasm/src/glob-lexer.cc new file mode 100644 index 000000000000..8c7f0eecc8b3 --- /dev/null +++ b/third_party/opa/wasm/src/glob-lexer.cc @@ -0,0 +1,304 @@ +#include + +#include "glob.h" +#include "re2/re2.h" +#include "malloc.h" +#include "str.h" +#include "unicode.h" + +#include +#include "glob-lexer.h" + +// The following is a re-implementation of lexer +// https://github.com/gobwas/glob/blob. +// +// The MIT License (MIT) +// +// Copyright (c) 2016 Sergey Kamardin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +const static int eof = 0; + +const static int char_any = '*'; +const static int char_comma = ','; +const static int char_single = '?'; +const static int char_escape = '\\'; +const static int char_range_open = '['; +const static int char_range_close = ']'; +const static int char_terms_open = '{'; +const static int char_terms_close = '}'; +const static int char_range_not = '!'; +const static int char_range_between = '-'; + +lexer::lexer(const char *source, size_t n_) + : data(source), pos(0), n(n_), error(NULL), tokens(), terms_level(0), has_rune(false), last_rune("", 0, eof) { } + +lexer::~lexer() +{ + for (int i = 0; i < tokens.size(); i++) + { + delete tokens[i]; + } +} + +void lexer::next(token *out) +{ + if (error) + { + *out = token(glob_lexer_token_error, error, strlen(error)); + return; + } + + if (!tokens.empty()) + { + *out = *tokens[0]; + delete tokens[0]; + + for (int i = 1; i < tokens.size(); i++) + { + tokens[i - 1] = tokens[i]; + } + + tokens.pop_back(); + return; + } + + fetch_item(); + next(out); +} + +void lexer::peek(rune *out) +{ + if (pos == n) + { + *out = rune(&data[pos], 0, eof); + return; + } + + int len; + int cp = opa_unicode_decode_utf8(data, pos, n, &len); + if (cp < 0) + { + *out = rune(&data[pos], 0, eof); + return; + } + + *out = rune(&data[pos], len, cp); +} + +void lexer::read(rune *out) { + if (has_rune) + { + has_rune = false; + seek(last_rune.n); + *out = last_rune; + return; + } + + peek(&last_rune); + seek(last_rune.n); + + *out = last_rune; +} + +void lexer::unread() +{ + if (has_rune) { + error = "could not unread rune"; + return; + } + + seek(-last_rune.n); + has_rune = true; +} + +void lexer::fetch_item() +{ + rune r("", 0, eof); + read(&r); + + if (r.cp == eof) + { + tokens.push_back(new token(glob_lexer_token_eof, r.s, 0)); + } + else if (r.cp == char_terms_open) + { + terms_enter(); + tokens.push_back(new token(glob_lexer_token_terms_open, r.s, r.n)); + } + else if (r.cp == char_comma && in_terms()) + { + tokens.push_back(new token(glob_lexer_token_separator, r.s, r.n)); + } + else if (r.cp == char_terms_close && in_terms()) + { + tokens.push_back(new token(glob_lexer_token_terms_close, r.s, r.n)); + terms_leave(); + } + else if (r.cp == char_range_open) + { + tokens.push_back(new token(glob_lexer_token_range_open, r.s, r.n)); + fetch_range(); + } + else if (r.cp == char_single) + { + tokens.push_back(new token(glob_lexer_token_single, r.s, r.n)); + } + else if (r.cp == char_any) + { + rune n("", 0, eof); + read(&n); + if (n.cp == char_any) + { + tokens.push_back(new token(glob_lexer_token_super, r.s, r.n + n.n)); + } else { + unread(); + tokens.push_back(new token(glob_lexer_token_any, r.s, r.n)); + } + } + else + { + const int in_text_breakers[] = {char_single, char_any, char_range_open, char_terms_open, 0}; + const int in_terms_breakers[] = {char_single, char_any, char_range_open, char_terms_open, char_terms_close, char_comma, 0}; + + unread(); + fetch_text(in_terms() ? in_terms_breakers : in_text_breakers); + } +} + +void lexer::fetch_range() +{ + bool want_hi = false; + bool want_close = false; + bool seen_not = false; + while (true) { + rune r("", 0, eof); + read(&r); + if (r.cp == eof) { + error = "unexpected end of input"; + return; + } + + if (want_close) + { + if (r.cp != char_range_close) { + error = "expected close range character"; + } else { + tokens.push_back(new token(glob_lexer_token_range_close, r.s, r.n)); + } + return; + } + + if (want_hi) + { + tokens.push_back(new token(glob_lexer_token_range_hi, r.s, r.n)); + want_close = true; + continue; + } + + if (!seen_not && r.cp == char_range_not) + { + tokens.push_back(new token(glob_lexer_token_not, r.s, r.n)); + seen_not = true; + continue; + } + + rune n("", 0, eof); + peek(&n); + if (n.cp == char_range_between) + { + seek(n.n); + tokens.push_back(new token(glob_lexer_token_range_lo, r.s, r.n)); + tokens.push_back(new token(glob_lexer_token_range_between, n.s, n.n)); + want_hi = true; + continue; + } + + unread(); // unread first peek and fetch as text + static const int breakers[] = {char_range_close, 0}; + fetch_text(breakers); + want_close = true; + } +} + +void lexer::fetch_text(const int *breakers) +{ + typedef std::pair str_offset; + std::vector arr; + bool escaped = false; + rune r("", 0, eof); + const char *s; + + for (read(&r), s = r.s; r.cp != eof; read(&r)) { + if (!escaped) + { + if (r.cp == char_escape) { + escaped = true; + + size_t n = static_cast(r.s - s); + if (n > 0) + { + arr.push_back(str_offset(s, n)); + } + + s = r.s + 1; + continue; + } + + for (int i = 0; breakers[i] != 0; i++) + { + if (breakers[i] == r.cp) + { + unread(); + goto done; + } + } + } + + escaped = false; + } + done: + size_t n = static_cast(r.s - s); + if (n > 0) + { + arr.push_back(str_offset(s, n)); + } + + if (arr.empty()) + { + return; + } + + n = 0; + for (size_t i = 0; i < arr.size(); i++) + { + n += arr[i].second; + } + + char *v = static_cast(opa_malloc(n)); + for (int i = 0, j = 0; i < arr.size(); i++) + { + memcpy(&v[j], arr[i].first, arr[i].second); + j += arr[i].second; + } + + tokens.push_back(new token(glob_lexer_token_text, v, n)); + opa_free(v); +} diff --git a/third_party/opa/wasm/src/glob-lexer.h b/third_party/opa/wasm/src/glob-lexer.h new file mode 100644 index 000000000000..5a53a9d353a1 --- /dev/null +++ b/third_party/opa/wasm/src/glob-lexer.h @@ -0,0 +1,69 @@ +#ifndef OPA_GLOB_LEXER_H +#define OPA_GLOB_LEXER_H + +#include +#include + +enum token_kind { + glob_lexer_token_eof = 0, + glob_lexer_token_error = 1, + glob_lexer_token_text = 2, + glob_lexer_token_char = 3, + glob_lexer_token_any = 4, + glob_lexer_token_super = 5, + glob_lexer_token_single = 6, + glob_lexer_token_not = 7, + glob_lexer_token_separator = 8, + glob_lexer_token_range_open = 9, + glob_lexer_token_range_close = 10, + glob_lexer_token_range_lo = 11, + glob_lexer_token_range_hi = 12, + glob_lexer_token_range_between = 13, + glob_lexer_token_terms_open = 14, + glob_lexer_token_terms_close = 15 +}; + +class rune { +public: + inline rune(const char *s, size_t n, int cp_) : s(s), n(n), cp(cp_) { } + const char *s; + size_t n; + int cp; +}; + +class token { +public: + inline token(int kind_, const char *s_, size_t n) : kind(kind_), s(s_, n) { } + int kind; + std::string s; +}; + +class lexer +{ +public: + lexer(const char *source, size_t n); + ~lexer(); + void next(token *token); +private: + void peek(rune *r); + void read(rune *r); + inline void seek(int w) { pos += w; } + void unread(); + inline bool in_terms() { return terms_level > 0; } + inline void terms_enter() { terms_level++; } + inline void terms_leave() { terms_level--; } + void fetch_item(); + void fetch_range(); + void fetch_text(const int *breakers); + + const char* data; + size_t pos; + size_t n; + const char *error; + std::vector tokens; + int terms_level; + bool has_rune; + rune last_rune; +}; + +#endif diff --git a/third_party/opa/wasm/src/glob-parser.cc b/third_party/opa/wasm/src/glob-parser.cc new file mode 100644 index 000000000000..795138eac1d6 --- /dev/null +++ b/third_party/opa/wasm/src/glob-parser.cc @@ -0,0 +1,269 @@ +#include +#include + +#include "glob-lexer.h" +#include "glob-parser.h" +#include "unicode.h" + +// The following is a re-implementation of parser in +// https://github.com/gobwas/glob. +// +// The MIT License (MIT) +// +// Copyright (c) 2016 Sergey Kamardin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +node::~node() +{ + for (int i = 0; i < children.size(); i++) + { + delete children[i]; + } +} + +node* node::insert(node *child) +{ + children.push_back(child); + child->parent = this; + return this; +} + +bool node::equal(node *other) +{ + if (kind != other->kind || + text != other->text || + lo != other->lo || + hi != other->hi || + not_ != other->not_) + { + return false; + } + + if (children.size() != other->children.size()) + { + return false; + } + + for (int i = 0; i < children.size(); i++) + { + if (!children[i]->equal(other->children[i])) + { + return false; + } + } + + return true; +} + +struct state; +typedef void (*parser)(state *s, lexer *lexer); + +struct state { + node *tree; + parser parser; + std::string error; +}; + +static void parser_main(state *s, lexer *lexer); +static void parser_range(state *s, lexer *lexer); + +std::string glob_parse(lexer *lexer, node **output) +{ + node *root = new node(kind_pattern); + + for (struct state s = {root, parser_main}; s.parser; ) + { + s.parser(&s, lexer); + if (s.error != "") + { + delete root; + return s.error; + } + } + + *output = root; + return ""; +} + +static void parser_main(state *s, lexer *lexer) { + token token(glob_lexer_token_eof, "", 0); + lexer->next(&token); + + switch (token.kind) + { + case glob_lexer_token_eof: + s->parser = NULL; + break; + + case glob_lexer_token_error: + s->parser = NULL; + s->error = token.s; + break; + + case glob_lexer_token_text: + s->tree->insert(new node(kind_text, token.s)); + s->parser = parser_main; + break; + + case glob_lexer_token_any: + s->tree->insert(new node(kind_any)); + s->parser = parser_main; + break; + + case glob_lexer_token_super: + s->tree->insert(new node(kind_super)); + s->parser = parser_main; + break; + + case glob_lexer_token_single: + s->tree->insert(new node(kind_single)); + s->parser = parser_main; + break; + + case glob_lexer_token_range_open: + s->parser = parser_range; + break; + + case glob_lexer_token_terms_open: { + node *a = new node(kind_any_of); + s->tree->insert(a); + + node *p = new node(kind_pattern); + a->insert(p); + + s->tree = p; + s->parser = parser_main; + break; + } + + case glob_lexer_token_separator: { + node *p = new node(kind_pattern); + s->tree->parent->insert(p); + s->tree = p; + s->parser = parser_main; + break; + } + + case glob_lexer_token_terms_close: + s->tree = s->tree->parent->parent; + s->parser = parser_main; + break; + + default: + s->parser = NULL; + s->error = "unexpected token"; + break; + } +} + +static void parser_range(state *s, lexer *lexer) +{ + bool not_ = false; + std::string lo, hi; + int lo_cp = 0, hi_cp = 0; + std::string chars; + + while (true) + { + token token(glob_lexer_token_eof, "", 0); + lexer->next(&token); + + switch (token.kind) + { + case glob_lexer_token_eof: + s->error = "unexpected end"; + s->parser = NULL; + return; + + case glob_lexer_token_error: + s->parser = NULL; + s->error = token.s; + return; + + case glob_lexer_token_not: + not_ = true; + break; + + case glob_lexer_token_range_lo: { + int len; + lo_cp = opa_unicode_decode_utf8(token.s.c_str(), 0, token.s.length(), &len); + if (lo_cp < 0 || len != token.s.length()) + { + s->parser = NULL; + s->error = "unexpected length of lo character"; + return; + } + + lo = token.s; + break; + } + + case glob_lexer_token_range_between: + break; + + case glob_lexer_token_range_hi: { + int len; + hi_cp = opa_unicode_decode_utf8(token.s.c_str(), 0, token.s.length(), &len); + if (hi_cp < 0 || len != token.s.length()) + { + s->parser = NULL; + s->error = "unexpected length of hi character"; + return; + } + + hi = token.s; + + if (hi < lo) + { + s->parser = NULL; + s->error = "hi character should be greater than lo character"; + return; + } + break; + } + + case glob_lexer_token_text: + chars = token.s; + break; + + case glob_lexer_token_range_close: { + const bool is_range = lo_cp != 0 && hi_cp != 0; + const bool is_chars = chars != ""; + + if (is_chars == is_range) + { + s->parser = NULL; + s->error = "could not parse range"; + return; + } + + if (is_range) + { + s->tree->insert(new node(kind_range, lo, hi, not_)); + } else { + s->tree->insert(new node(kind_list, chars, not_)); + } + + s->parser = parser_main; + return; + } + } + } +} diff --git a/third_party/opa/wasm/src/glob-parser.h b/third_party/opa/wasm/src/glob-parser.h new file mode 100644 index 000000000000..78ea2412379f --- /dev/null +++ b/third_party/opa/wasm/src/glob-parser.h @@ -0,0 +1,41 @@ +#ifndef OPA_GLOB_PARSER +#define OPA_GLOB_PARSER + +#include +#include "glob-lexer.h" + +enum kind { + kind_nothing = 0, + kind_pattern = 1, + kind_list = 2, + kind_range = 3, + kind_text = 4, + kind_any = 5, + kind_super = 6, + kind_single = 7, + kind_any_of = 8, +}; + +class node { +public: + inline node(kind kind_) : kind(kind_), parent(NULL), children(), text(""), lo(""), hi(""), not_(false) { } + inline node(kind kind_, const std::string& text_) : kind(kind_), parent(NULL), children(), text(text_), lo(""), hi(""), not_(false) { } + inline node(kind kind_, const std::string& lo_, const std::string& hi_, bool not__) : kind(kind_), parent(NULL), children(), text(""), lo(lo_), hi(hi_), not_(not__) { } + inline node(kind kind_, const std::string& chars_, bool not__) : kind(kind_), parent(NULL), children(), text(chars_), lo(""), hi(""), not_(not__) { } + ~node(); + node* insert(node *child); + bool equal(node *other); + std::string re2(const std::string& single_mark); + + kind kind; + node *parent; + std::vector children; + std::string text; + std::string lo; + std::string hi; + bool not_; +}; + +std::string glob_parse(lexer *lexer, node **output); + +#endif diff --git a/third_party/opa/wasm/src/glob.cc b/third_party/opa/wasm/src/glob.cc new file mode 100644 index 000000000000..e172888d5f29 --- /dev/null +++ b/third_party/opa/wasm/src/glob.cc @@ -0,0 +1,120 @@ +#include +#include + +#include "glob.h" +#include "glob-compiler.h" +#include "malloc.h" +#include "regex.h" +#include "std.h" +#include "value.h" + +static const int MAX_CACHE_SIZE = 100; + +struct cache_key { +public: + inline cache_key() : pattern(""), delimiters() { } + inline cache_key(const std::string& pattern_, const std::vector& delimiters_) : pattern(pattern_), delimiters(delimiters_) { } + inline bool operator==(const cache_key& key) const { + return pattern == key.pattern && delimiters == key.delimiters; + } + std::string pattern; + std::vector delimiters; +}; + +template <> +struct std::hash +{ + size_t operator()(const cache_key& key) const + { + std::hash hasher; + size_t seed = hasher(key.pattern); + + for (int i = 0; i < key.delimiters.size(); i++) + { + seed ^= hasher(key.delimiters[i]) + 0x9e3779b9 + (seed<<6) + (seed>>2); + } + + return seed; + } +}; + +typedef std::unordered_map glob_cache; + +static glob_cache* cache() +{ + glob_cache* c = static_cast(opa_builtin_cache_get(1)); + if (c == NULL) + { + c = new glob_cache(); + opa_builtin_cache_set(1, c); + } + + return c; +} + +OPA_BUILTIN +opa_value *opa_glob_match(opa_value *pattern, opa_value *delimiters, opa_value *match) +{ + if (opa_value_type(pattern) != OPA_STRING || + (opa_value_type(delimiters) != OPA_ARRAY && opa_value_type(delimiters) != OPA_NULL) || + opa_value_type(match) != OPA_STRING) + { + return NULL; + } + + opa_string_t *p = opa_cast_string(pattern); + + std::vector v; + + opa_value *prev = NULL; + opa_value *curr = NULL; + while ((curr = opa_value_iter(delimiters, prev)) != NULL) + { + opa_value *elem = opa_value_get(delimiters, curr); + if (opa_value_type(elem) != OPA_STRING) + { + return NULL; + } + opa_string_t *s = opa_cast_string(elem); + v.push_back(std::string(s->v, s->len)); + prev = curr; + } + + // NOTE(sr): If we're passed an empty array, use "." as default delimiter. + // If we're passed OPA_NULL, use no delimiter; but separate glob parts by '.*' + if (opa_value_type(delimiters) == OPA_ARRAY) { + if (v.empty()) + { + v.push_back(std::string(".")); + } + } + + glob_cache *c = cache(); + cache_key key = cache_key(std::string(p->v, p->len), v); + glob_cache::iterator i = c->find(key); + std::string re2; + if (i != c->end()) + { + re2 = i->second; + } else { + std::string error = glob_translate(p->v, p->len, v, &re2); + if (!error.empty()) + { + return NULL; + } + + if (c->size() >= MAX_CACHE_SIZE) + { + // Delete a (semi-)random key to make room for the new one. + auto i = c->begin(); + if (i != c->end()) + { + c->erase(c->begin()); + } + } + + cache()->insert(std::make_pair(key, re2)); + } + + return opa_regex_match(opa_string(re2.c_str(), re2.length()), match); +} diff --git a/third_party/opa/wasm/src/glob.h b/third_party/opa/wasm/src/glob.h new file mode 100644 index 000000000000..00b5188d8c1f --- /dev/null +++ b/third_party/opa/wasm/src/glob.h @@ -0,0 +1,16 @@ +#ifndef OPA_GLOB_H +#define OPA_GLOB_H + +#include "value.h" + +#ifdef __cplusplus +extern "C" { +#endif + +opa_value *opa_glob_match(opa_value *pattern, opa_value *delimiters, opa_value *match); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/graphs.c b/third_party/opa/wasm/src/graphs.c new file mode 100644 index 000000000000..6f29280403bf --- /dev/null +++ b/third_party/opa/wasm/src/graphs.c @@ -0,0 +1,86 @@ +#include "graphs.h" +#include "std.h" + +static void __builtin_graph_reachable(opa_value *edges, opa_array_t *queue, opa_set_t *reached) +{ + switch (opa_value_type(edges)) + { + case OPA_SET: + { + opa_set_t *x = opa_cast_set(edges); + + for (int i = 0; i < x->n; i++) + { + opa_set_elem_t *elem = x->buckets[i]; + + while (elem != NULL) + { + if (reached == NULL || opa_set_get(reached, elem->v) == NULL) + { + opa_array_append(queue, elem->v); + } + elem = elem->next; + } + } + break; + } + case OPA_ARRAY: + { + opa_array_t *y = opa_cast_array(edges); + + for (int i = 0; i < y->len; i++) + { + opa_value *elem = y->elems[i].v; + + if (reached == NULL || opa_set_get(reached, elem) == NULL) + { + opa_array_append(queue, elem); + } + } + break; + } + } +} + +OPA_BUILTIN +opa_value *builtin_graph_reachable(opa_value *graph, opa_value *initial) +{ + if (opa_value_type(graph) != OPA_OBJECT) + { + return NULL; + } + if (opa_value_type(initial) != OPA_SET && opa_value_type(initial) != OPA_ARRAY) + { + return NULL; + } + + // This is a queue that holds all nodes we still need to visit. It is + // initialized to the initial set of nodes we start out with. + opa_array_t *queue = opa_cast_array(opa_array()); + + if (initial != NULL) + { + __builtin_graph_reachable(initial, queue, NULL); + } + + // This is the set of nodes we have reached. + opa_set_t *reached = opa_cast_set(opa_set()); + + // Keep going as long as we have nodes in the queue. + for (int index = 0; index < queue->len; index++) + { + // Get the edges for this node. + opa_value *node = queue->elems[index].v; + opa_value *edges = opa_value_get(graph, node); + + if (edges != NULL) + { + __builtin_graph_reachable(edges, queue, reached); + + // Mark current node as reached. + opa_set_add(reached, node); + } + } + + return &reached->hdr; +} diff --git a/third_party/opa/wasm/src/graphs.h b/third_party/opa/wasm/src/graphs.h new file mode 100644 index 000000000000..a5555e9bfe28 --- /dev/null +++ b/third_party/opa/wasm/src/graphs.h @@ -0,0 +1,8 @@ +#ifndef OPA_GRAPHS_H +#define OPA_GRAPHS_H + +#include "value.h" + +opa_value *builtin_graph_reachable(opa_value *graph, opa_value *initial); + +#endif diff --git a/third_party/opa/wasm/src/json.c b/third_party/opa/wasm/src/json.c new file mode 100644 index 000000000000..56f9e89adf16 --- /dev/null +++ b/third_party/opa/wasm/src/json.c @@ -0,0 +1,1080 @@ +#include + +#include "stdlib.h" +#include "str.h" +#include "value.h" +#include "json.h" +#include "malloc.h" +#include "unicode.h" + +static opa_value *opa_json_parse_token(opa_json_lex *ctx, int token); + +int opa_json_lex_offset(opa_json_lex *ctx) +{ + return ctx->curr - ctx->input; +} + +int opa_json_lex_remaining(opa_json_lex *ctx) +{ + return ctx->len - opa_json_lex_offset(ctx); +} + +int opa_json_lex_eof(opa_json_lex *ctx) +{ + return (ctx->curr - ctx->input) >= ctx->len; +} + +int opa_json_lex_read_atom(opa_json_lex *ctx, const char *str, int n, int token) +{ + if (opa_json_lex_remaining(ctx) >= n) + { + if (opa_strncmp(str, ctx->curr, n) == 0) + { + ctx->curr += n; + return token; + } + } + return OPA_JSON_TOKEN_ERROR; +} + +void opa_json_lex_read_digits(opa_json_lex *ctx) +{ + while (!opa_json_lex_eof(ctx) && opa_isdigit(*ctx->curr)) + { + ctx->curr++; + } +} + +int opa_json_lex_read_unicode(opa_json_lex *ctx) +{ + if (opa_json_lex_remaining(ctx) >= 4) + { + for (int i = 0; i < 4; i++) + { + if (!opa_ishex(ctx->curr[i])) + { + return -1; + } + } + ctx->curr += 4; + return 0; + } + + return 1; +} + +int opa_json_lex_read_number(opa_json_lex *ctx) +{ + ctx->buf = ctx->curr; + + // Handle sign component. + if (*ctx->curr == '-') + { + ctx->curr++; + + if (opa_json_lex_eof(ctx)) + { + goto err; + } + } + + // Handle integer component. + if (*ctx->curr == '0') + { + ctx->curr++; + } + else if (opa_isdigit(*ctx->curr)) + { + opa_json_lex_read_digits(ctx); + } + else + { + goto err; + } + + if (opa_json_lex_eof(ctx)) + { + goto out; + } + + // Handle fraction component. + if (*ctx->curr == '.') + { + ctx->curr++; + opa_json_lex_read_digits(ctx); + + if (opa_json_lex_eof(ctx)) + { + goto out; + } + } + + // Handle exponent component. + if (*ctx->curr == 'e' || *ctx->curr == 'E') + { + ctx->curr++; + + if (opa_json_lex_eof(ctx)) + { + goto err; + } + + if (*ctx->curr == '+' || *ctx->curr == '-') + { + ctx->curr++; + + if (opa_json_lex_eof(ctx)) + { + goto err; + } + } + + opa_json_lex_read_digits(ctx); + } + +out: + ctx->buf_end = ctx->curr; + return OPA_JSON_TOKEN_NUMBER; + +err: + return OPA_JSON_TOKEN_ERROR; +} + +int opa_json_lex_read_string(opa_json_lex *ctx) +{ + if (*ctx->curr != '"') + { + goto err; + } + + ctx->buf = ++ctx->curr; + int escaped = 0; + + while (1) + { + if (opa_json_lex_eof(ctx)) + { + goto err; + } + + unsigned char b = *ctx->curr; + + switch (b) + { + case '\\': + escaped = 1; + ctx->curr++; + + if (opa_json_lex_eof(ctx)) + { + goto err; + } + + b = *ctx->curr; + + switch (b) + { + case '"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + ctx->curr++; + break; + case 'u': + ctx->curr++; + if (opa_json_lex_read_unicode(ctx) != 0) + { + goto err; + } + break; + default: + goto err; + } + + break; + case '"': + goto out; + + default: + if (b < ' ') { + goto err; + } + + if (b > '~') + { + // Revert to slow path to validate UTF-8 encoding. + escaped = 1; + } + ctx->curr++; + break; + } + } +out: + ctx->buf_end = ctx->curr++; + + if (escaped) + { + return OPA_JSON_TOKEN_STRING_ESCAPED; + } + + return OPA_JSON_TOKEN_STRING; + +err: + return OPA_JSON_TOKEN_ERROR; +} + +int opa_json_lex_read_empty_set(opa_json_lex *ctx) +{ + if (!ctx->set_literals_enabled) + { + return OPA_JSON_TOKEN_ERROR; + } + + int token = opa_json_lex_read_atom(ctx, "set(", 4, OPA_JSON_TOKEN_EMPTY_SET); + + if (token != OPA_JSON_TOKEN_EMPTY_SET) + { + return OPA_JSON_TOKEN_ERROR; + } + + while (opa_isspace(*ctx->curr)) + { + ctx->curr++; + } + + if (*ctx->curr != ')') + { + return OPA_JSON_TOKEN_ERROR; + } + + ctx->curr++; + return token; +} + +int opa_json_lex_read(opa_json_lex *ctx) +{ + while (!opa_json_lex_eof(ctx)) + { + char b = *ctx->curr; + switch (b) + { + case 'n': + return opa_json_lex_read_atom(ctx, "null", 4, OPA_JSON_TOKEN_NULL); + case 't': + return opa_json_lex_read_atom(ctx, "true", 4, OPA_JSON_TOKEN_TRUE); + case 'f': + return opa_json_lex_read_atom(ctx, "false", 5, OPA_JSON_TOKEN_FALSE); + case 's': + return opa_json_lex_read_empty_set(ctx); + case '"': + return opa_json_lex_read_string(ctx); + case '{': + ctx->curr++; + return OPA_JSON_TOKEN_OBJECT_START; + case '}': + ctx->curr++; + return OPA_JSON_TOKEN_OBJECT_END; + case '[': + ctx->curr++; + return OPA_JSON_TOKEN_ARRAY_START; + case ']': + ctx->curr++; + return OPA_JSON_TOKEN_ARRAY_END; + case ',': + ctx->curr++; + return OPA_JSON_TOKEN_COMMA; + case ':': + ctx->curr++; + return OPA_JSON_TOKEN_COLON; + default: + if (opa_isdigit(b) || b == '-') + { + return opa_json_lex_read_number(ctx); + } + else if (opa_isspace(b)) + { + ctx->curr++; + continue; + } + return OPA_JSON_TOKEN_ERROR; + } + } + + return OPA_JSON_TOKEN_EOF; +} + +void opa_json_lex_init(const char *input, size_t len, opa_json_lex *ctx) +{ + ctx->input = input; + ctx->len = len; + ctx->curr = input; + ctx->buf = NULL; + ctx->buf_end = NULL; + ctx->set_literals_enabled = 0; +} + +size_t opa_json_max_string_len(const char *buf, size_t len) +{ + // The lexer will catch invalid escaping, e.g., if the last char in the + // buffer is reverse solidus this will be caught ahead-of-time. + int skip = 0; + + for (int i = 0; i < len; i++) + { + if (buf[i] == '\\') + { + int codepoint; + + codepoint = opa_unicode_decode_unit(buf, i, len); + if (codepoint == -1) { + // If not a codepoint \uXXXX, must be a single + // character escaping. + skip++; + i++; + continue; + } + + i += 5; + + // Assume each UTF-16 encoded character to take full 4 + // bytes when encoded as UTF-8. However, if encoded as a + // surrogate pair, it's split to two 2 bytes. + if (!opa_unicode_surrogate(codepoint)) { + skip += 2; + continue; + } + + skip += 4; + } + } + + return len - skip; +} + +opa_value *opa_json_parse_string(int token, const char *buf, int len) +{ + if (token == OPA_JSON_TOKEN_STRING) + { + char *cpy = (char *)opa_malloc(len); + + for (int i = 0; i < len; i++) + { + cpy[i] = buf[i]; + } + + return opa_string_allocated(cpy, len); + } + + int max_len = opa_json_max_string_len(buf, len); + char *cpy = (char *)opa_malloc(max_len); + char *out = cpy; + + for (int i = 0; i < len;) + { + unsigned char c = buf[i]; + + if (c != '\\') + { + if (c < ' ' || c == '"') + { + opa_abort("illegal unescaped character"); + } + + if (c < 0x80) + { + *out++ = c; + i++; + } else { + int n; + int cp = opa_unicode_decode_utf8(buf, i, len, &n); + if (cp == -1) + { + opa_abort("illegal utf-8"); + } + + i += n; + + n = opa_unicode_encode_utf8(cp, out); + out += n; + } + + continue; + } + + char next = buf[i+1]; + + switch (next) + { + case '"': + case '\\': + case '/': + *out++ = next; + i += 2; + break; + case 'b': + *out++ = '\b'; + i += 2; + break; + case 'f': + *out++ = '\f'; + i += 2; + break; + case 'n': + *out++ = '\n'; + i += 2; + break; + case 'r': + *out++ = '\r'; + i += 2; + break; + case 't': + *out++ = '\t'; + i += 2; + break; + case 'u': + { + // JSON encodes unicode characters as UTF-16 that + // have either a single or two code units. If two + // code units, the character is represented as a + // pair of UTF-16 surrogates. Surrogates don't + // overlap with characters that can be encoded as + // a single value. + int u = opa_unicode_decode_unit(buf, i, len); + if (u == -1) { + opa_abort("illegal string escape character"); + } + + i += 6; + + if (opa_unicode_surrogate(u)) { + int v = opa_unicode_decode_unit(buf, i, len); + if (v == -1) { + opa_abort("illegal string escape character"); + } + + u = opa_unicode_decode_surrogate(u, v); + i += 6; + } + + out += opa_unicode_encode_utf8(u, out); + break; + } + default: + // this is unreachable. + opa_abort("illegal string escape character"); + } + } + + return opa_string_allocated(cpy, out-cpy); +} + +opa_value *opa_json_parse_number(const char *buf, int len) +{ + char *cpy = (char *)opa_malloc(len); + + for (int i = 0; i < len; i++) + { + cpy[i] = buf[i]; + } + + return opa_number_ref_allocated(cpy, len); +} + +opa_value *opa_json_parse_array(opa_json_lex *ctx) +{ + opa_value *ret = opa_array(); + opa_array_t *arr = opa_cast_array(ret); + int sep = 0; + + while (1) + { + int token = opa_json_lex_read(ctx); + + switch (token) + { + case OPA_JSON_TOKEN_ARRAY_END: + return ret; + case OPA_JSON_TOKEN_COMMA: + if (sep) + { + sep = 0; + continue; + } + } + + opa_value *elem = opa_json_parse_token(ctx, token); + + if (elem == NULL) + { + return NULL; + } + + opa_array_append(arr, elem); + sep = 1; + } +} + +opa_value *opa_json_parse_set(opa_json_lex *ctx, opa_value *elem, int token) +{ + if (!ctx->set_literals_enabled) + { + return NULL; + } + + opa_set_t *set = opa_cast_set(opa_set()); + opa_set_add(set, elem); + + if (token == OPA_JSON_TOKEN_OBJECT_END) + { + return &set->hdr; + } + + token = opa_json_lex_read(ctx); + + while (1) + { + elem = opa_json_parse_token(ctx, token); + + if (elem == NULL) + { + return NULL; + } + + opa_set_add(set, elem); + token = opa_json_lex_read(ctx); + + switch (token) + { + case OPA_JSON_TOKEN_COMMA: + token = opa_json_lex_read(ctx); + break; + case OPA_JSON_TOKEN_OBJECT_END: + return &set->hdr; + default: + return NULL; + } + } + + return NULL; +} + +opa_value *opa_json_parse_object(opa_json_lex *ctx, opa_value *key) +{ + int token = opa_json_lex_read(ctx); + opa_value *val = opa_json_parse_token(ctx, token); + + if (val == NULL) + { + return NULL; + } + + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, key, val); + token = opa_json_lex_read(ctx); + + switch (token) + { + case OPA_JSON_TOKEN_OBJECT_END: + return &obj->hdr; + case OPA_JSON_TOKEN_COMMA: + break; + default: + return NULL; + } + + token = opa_json_lex_read(ctx); + + while (1) + { + key = opa_json_parse_token(ctx, token); + + if (key == NULL) + { + return NULL; + } + + token = opa_json_lex_read(ctx); + + if (token != OPA_JSON_TOKEN_COLON) + { + return NULL; + } + + token = opa_json_lex_read(ctx); + val = opa_json_parse_token(ctx, token); + + if (val == NULL) + { + return NULL; + } + + opa_object_insert(obj, key, val); + token = opa_json_lex_read(ctx); + + switch (token) + { + case OPA_JSON_TOKEN_OBJECT_END: + return &obj->hdr; + case OPA_JSON_TOKEN_COMMA: + { + token = opa_json_lex_read(ctx); + break; + } + default: + return NULL; + } + } + + return NULL; +} + +opa_value *opa_json_parse_object_or_set(opa_json_lex *ctx) +{ + int token = opa_json_lex_read(ctx); + + if (token == OPA_JSON_TOKEN_OBJECT_END) + { + return opa_object(); + } + + opa_value *head = opa_json_parse_token(ctx, token); + + if (head == NULL) + { + return NULL; + } + + token = opa_json_lex_read(ctx); + + switch (token) + { + case OPA_JSON_TOKEN_OBJECT_END: + case OPA_JSON_TOKEN_COMMA: + return opa_json_parse_set(ctx, head, token); + case OPA_JSON_TOKEN_COLON: + return opa_json_parse_object(ctx, head); + } + + return NULL; +} + +opa_value *opa_json_parse_token(opa_json_lex *ctx, int token) +{ + switch (token) + { + case OPA_JSON_TOKEN_NULL: + return opa_null(); + case OPA_JSON_TOKEN_TRUE: + return opa_boolean(true); + case OPA_JSON_TOKEN_FALSE: + return opa_boolean(false); + case OPA_JSON_TOKEN_NUMBER: + return opa_json_parse_number(ctx->buf, ctx->buf_end - ctx->buf); + case OPA_JSON_TOKEN_STRING: + case OPA_JSON_TOKEN_STRING_ESCAPED: + return opa_json_parse_string(token, ctx->buf, ctx->buf_end - ctx->buf); + case OPA_JSON_TOKEN_ARRAY_START: + return opa_json_parse_array(ctx); + case OPA_JSON_TOKEN_OBJECT_START: + return opa_json_parse_object_or_set(ctx); + case OPA_JSON_TOKEN_EMPTY_SET: + return opa_set(); + default: + return NULL; + } +} + +OPA_INTERNAL +WASM_EXPORT(opa_json_parse) +opa_value *opa_json_parse(const char *input, size_t len) +{ + opa_json_lex ctx; + opa_json_lex_init(input, len, &ctx); + int token = opa_json_lex_read(&ctx); + return opa_json_parse_token(&ctx, token); +} + +OPA_INTERNAL +WASM_EXPORT(opa_value_parse) +opa_value *opa_value_parse(const char *input, size_t len) +{ + opa_json_lex ctx; + opa_json_lex_init(input, len, &ctx); + ctx.set_literals_enabled = 1; + int token = opa_json_lex_read(&ctx); + return opa_json_parse_token(&ctx, token); +} + +typedef struct { + char *buf; + char *next; + size_t len; + int set_literals_enabled; + int non_string_object_keys_enabled; +} opa_json_writer; + +void opa_json_writer_init(opa_json_writer *w) +{ + w->buf = NULL; + w->next = NULL; + w->len = 0; + w->set_literals_enabled = 0; + w->non_string_object_keys_enabled = 0; +} + +size_t opa_json_writer_offset(opa_json_writer *w) +{ + return w->next - w->buf; +} + +size_t opa_json_writer_space(opa_json_writer *w) +{ + return w->len - opa_json_writer_offset(w); +} + +int opa_json_writer_grow(opa_json_writer *w, size_t newlen, size_t copy) +{ + char *newbuf = (char *)opa_malloc(newlen); + + if (newbuf == NULL) + { + return -1; + } + + for (size_t i = 0; i < copy; i++) + { + newbuf[i] = w->buf[i]; + } + + size_t offset = opa_json_writer_offset(w); + + w->buf = newbuf; + w->next = newbuf + offset; + w->len = newlen; + + return 0; +} + +int opa_json_writer_emit_chars(opa_json_writer *w, const char *bs, size_t nb) +{ + size_t offset = opa_json_writer_offset(w); + + if (offset + nb > w->len) + { + int rc = opa_json_writer_grow(w, (offset + nb) * 2, w->len); + + if (rc != 0) + { + return rc; + } + } + + for(int i = 0; i < nb; i++) + { + w->next[i] = bs[i]; + } + + w->next += nb; + + return 0; +} + +int opa_json_writer_emit_char(opa_json_writer *w, char b) +{ + char bs[] = {b}; + + return opa_json_writer_emit_chars(w, bs, 1); +} + +int opa_json_writer_emit_null(opa_json_writer *w) +{ + char bs[] = "null"; + + return opa_json_writer_emit_chars(w, bs, sizeof(bs)-1); +} + +int opa_json_writer_emit_boolean(opa_json_writer *w, opa_boolean_t *b) +{ + if (b->v == 0) + { + char bs[] = "false"; + + return opa_json_writer_emit_chars(w, bs, sizeof(bs)-1); + } + + char bs[] = "true"; + + return opa_json_writer_emit_chars(w, bs, sizeof(bs)-1); +} + +int opa_json_writer_emit_integer(opa_json_writer *w, long long i) +{ + char str[sizeof(i)*8+1]; // once base=2 is supported we need 8 bits per byte. + opa_itoa(i, str, 10); + return opa_json_writer_emit_chars(w, str, opa_strlen(str)); +} + +int opa_json_writer_emit_number(opa_json_writer *w, opa_number_t *n) +{ + switch (n->repr) + { + case OPA_NUMBER_REPR_INT: + return opa_json_writer_emit_integer(w, n->v.i); + case OPA_NUMBER_REPR_REF: + return opa_json_writer_emit_chars(w, n->v.ref.s, n->v.ref.len); + default: + opa_abort("opa_json_writer_emit_number: illegal repr"); + return -1; + } +} + +int opa_json_writer_emit_string(opa_json_writer *w, opa_string_t *s) +{ + int rc = opa_json_writer_emit_char(w, '"'); + + if (rc != 0) + { + return rc; + } + + for (size_t i = 0; i < s->len; i++) + { + // Encode any character below 32 (space) with \u00XX, unless + // \n, \r or \t. including and above character 32, escape if + // \ or ". Anything else is expected to be valid UTF-8. + + unsigned char c = s->v[i]; + if (c >= ' ' && c != '\\' && c != '"') + { + rc = opa_json_writer_emit_char(w, c); + if (rc != 0) + { + return rc; + } + + continue; + } + + rc = opa_json_writer_emit_char(w, '\\'); + if (rc != 0) + { + return rc; + } + + if (c == '\\' || c == '"') { + rc = opa_json_writer_emit_char(w, c); + } else if (c == '\n') { + rc = opa_json_writer_emit_char(w, 'n'); + } else if (c == '\r') { + rc = opa_json_writer_emit_char(w, 'r'); + } else if (c == '\t') { + rc = opa_json_writer_emit_char(w, 't'); + } else { + rc = opa_json_writer_emit_chars(w, "u00", 3); + if (rc != 0) + { + return rc; + } + + char buf[3]; + snprintf(buf, 3, "%02x", c); + + rc = opa_json_writer_emit_chars(w, buf, 2); + if (rc != 0) + { + return rc; + } + } + + if (rc != 0) + { + return rc; + } + } + + rc = opa_json_writer_emit_char(w, '"'); + + if (rc != 0) + { + return rc; + } + + return 0; +} + +int opa_json_writer_emit_value(opa_json_writer *, opa_value *); + +int opa_json_writer_emit_array_element(opa_json_writer *w, opa_value *coll, opa_value *k) +{ + return opa_json_writer_emit_value(w, opa_value_get(coll, k)); +} + +int opa_json_writer_emit_set_element(opa_json_writer *w, opa_value *coll, opa_value *k) +{ + return opa_json_writer_emit_value(w, k); +} + +int opa_json_writer_emit_object_element(opa_json_writer *w, opa_value *coll, opa_value *k) +{ + if (w->non_string_object_keys_enabled || opa_value_type(k) == OPA_STRING) + { + int rc = opa_json_writer_emit_value(w, k); + + if (rc != 0) + { + return rc; + } + } + else + { + char *buf = opa_json_dump(k); + + if (buf == NULL) + { + return -3; + } + + opa_value *serialized = opa_string_terminated(buf); + int rc = opa_json_writer_emit_value(w, serialized); + opa_value_free(serialized); + opa_free(buf); + + if (rc != 0) + { + return rc; + } + } + + int rc = opa_json_writer_emit_char(w, ':'); + + if (rc != 0) + { + return rc; + } + + return opa_json_writer_emit_value(w, opa_value_get(coll, k)); +} + +int opa_json_writer_emit_collection(opa_json_writer *w, opa_value *v, char open, char close, int (*emitfunc)(opa_json_writer *, opa_value *, opa_value *)) +{ + int rc = opa_json_writer_emit_char(w, open); + + if (rc != 0) + { + return rc; + } + + opa_value *prev = NULL; + opa_value *curr = NULL; + + while ((curr = opa_value_iter(v, prev)) != NULL) + { + if (prev != NULL) + { + rc = opa_json_writer_emit_char(w, ','); + + if (rc != 0) + { + return rc; + } + } + + rc = emitfunc(w, v, curr); + + if (rc != 0) + { + return rc; + } + + prev = curr; + } + + return opa_json_writer_emit_char(w, close); +} + +int opa_json_writer_emit_set_literal(opa_json_writer *w, opa_set_t *set) +{ + if (opa_value_length(&set->hdr) == 0) + { + const char empty_set[] = "set()"; + + return opa_json_writer_emit_chars(w, empty_set, sizeof(empty_set)-1); + } + + return opa_json_writer_emit_collection(w, &set->hdr, '{', '}', opa_json_writer_emit_set_element); +} + +int opa_json_writer_emit_value(opa_json_writer *w, opa_value *v) +{ + switch (opa_value_type(v)) + { + case OPA_NULL: + return opa_json_writer_emit_null(w); + case OPA_BOOLEAN: + return opa_json_writer_emit_boolean(w, opa_cast_boolean(v)); + case OPA_STRING: + return opa_json_writer_emit_string(w, opa_cast_string(v)); + case OPA_NUMBER: + return opa_json_writer_emit_number(w, opa_cast_number(v)); + case OPA_ARRAY: + return opa_json_writer_emit_collection(w, v, '[', ']', opa_json_writer_emit_array_element); + case OPA_SET: + { + if (!w->set_literals_enabled) + { + return opa_json_writer_emit_collection(w, v, '[', ']', opa_json_writer_emit_set_element); + } + return opa_json_writer_emit_set_literal(w, opa_cast_set(v)); + } + case OPA_OBJECT: + return opa_json_writer_emit_collection(w, v, '{', '}', opa_json_writer_emit_object_element); + } + + return -2; +} + +char *opa_json_writer_write(opa_json_writer *w, opa_value *v) +{ + if (opa_json_writer_grow(w, 1024, 0) != 0) + { + goto errout; + } + + if (opa_json_writer_emit_value(w, v) != 0) + { + goto errout; + } + + if (opa_json_writer_emit_char(w, 0) != 0) + { + goto errout; + } + + return w->buf; + +errout: + opa_free(w->buf); + return NULL; +} + +WASM_EXPORT(opa_json_dump) +char *opa_json_dump(opa_value *v) +{ + opa_json_writer w; + opa_json_writer_init(&w); + return opa_json_writer_write(&w, v); +} + +WASM_EXPORT(opa_value_dump) +char *opa_value_dump(opa_value *v) +{ + opa_json_writer w; + opa_json_writer_init(&w); + w.set_literals_enabled = 1; + w.non_string_object_keys_enabled = 1; + return opa_json_writer_write(&w, v); +} diff --git a/third_party/opa/wasm/src/json.h b/third_party/opa/wasm/src/json.h new file mode 100644 index 000000000000..0e17fe546cf5 --- /dev/null +++ b/third_party/opa/wasm/src/json.h @@ -0,0 +1,50 @@ +#ifndef OPA_JSON_H +#define OPA_JSON_H + +#include "value.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + const char *input; + size_t len; + const char *buf; + const char *buf_end; + const char *curr; + int set_literals_enabled; +} opa_json_lex; + +#define OPA_JSON_TOKEN_ERROR 0 +#define OPA_JSON_TOKEN_EOF 1 +#define OPA_JSON_TOKEN_NULL 2 +#define OPA_JSON_TOKEN_TRUE 3 +#define OPA_JSON_TOKEN_FALSE 4 +#define OPA_JSON_TOKEN_NUMBER 5 +#define OPA_JSON_TOKEN_STRING 6 +#define OPA_JSON_TOKEN_STRING_ESCAPED 7 +#define OPA_JSON_TOKEN_OBJECT_START 8 +#define OPA_JSON_TOKEN_OBJECT_END 9 +#define OPA_JSON_TOKEN_ARRAY_START 10 +#define OPA_JSON_TOKEN_ARRAY_END 11 +#define OPA_JSON_TOKEN_COMMA 12 +#define OPA_JSON_TOKEN_COLON 13 +#define OPA_JSON_TOKEN_EMPTY_SET 14 + +void opa_json_lex_init(const char *input, size_t len, opa_json_lex *ctx); +int opa_json_lex_read(opa_json_lex *ctx); + +opa_value *opa_json_parse(const char *input, size_t len); +opa_value *opa_value_parse(const char *input, size_t len); +char *opa_json_dump(opa_value *v); +char *opa_value_dump(opa_value *v); + +size_t opa_json_max_string_len(const char *input, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/lib/assert.h b/third_party/opa/wasm/src/lib/assert.h new file mode 100644 index 000000000000..a006e06ca2b7 --- /dev/null +++ b/third_party/opa/wasm/src/lib/assert.h @@ -0,0 +1,8 @@ +#ifndef OPA_ASSERT_H +#define OPA_ASSERT_H + +#include "../std.h" + +#define assert(expr) ((expr) ? (void)0 : opa_abort(#expr)); + +#endif diff --git a/third_party/opa/wasm/src/lib/bits.h b/third_party/opa/wasm/src/lib/bits.h new file mode 100644 index 000000000000..8c6a9b11f7ba --- /dev/null +++ b/third_party/opa/wasm/src/lib/bits.h @@ -0,0 +1,12 @@ +#ifndef OPA_BITS_H +#define OPA_BITS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/lib/ctype.c b/third_party/opa/wasm/src/lib/ctype.c new file mode 100644 index 000000000000..05ec59560593 --- /dev/null +++ b/third_party/opa/wasm/src/lib/ctype.c @@ -0,0 +1,33 @@ +#include + +/* POSIX/C local implementations. */ + +int isalpha(int c) +{ + return isupper(c) || islower(c); +} + +int islower(int c) +{ + return c >= 'a' && c <= 'z' ? 1 : 0; +} + +int isspace(int c) +{ + return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'; +} + +int isupper(int c) +{ + return c >= 'A' && c <= 'Z' ? 1 : 0; +} + +int tolower(int c) +{ + if (isupper(c)) + { + return c + ('a' - 'A'); + } + + return c; +} diff --git a/third_party/opa/wasm/src/lib/ctype.h b/third_party/opa/wasm/src/lib/ctype.h new file mode 100644 index 000000000000..6b9850b21cb7 --- /dev/null +++ b/third_party/opa/wasm/src/lib/ctype.h @@ -0,0 +1,34 @@ +#ifndef OPA_CTYPE_H +#define OPA_CTYPE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define isascii(c) (((c) & ~0x7f) == 0) + +int isalpha(int c); +int islower(int c); +int isspace(int c); +int isupper(int c); +int tolower(int c); + +// not implemented: + +int isdigit(int c); +int toupper(int c); +int isalnum(int c); +int isblank(int c); +int iscntrl(int c); +int isgraph(int c); +int isprint(int c); +int ispunct(int c); +int isxdigit(int c); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/lib/errno.c b/third_party/opa/wasm/src/lib/errno.c new file mode 100644 index 000000000000..6e7bb62b51fc --- /dev/null +++ b/third_party/opa/wasm/src/lib/errno.c @@ -0,0 +1 @@ +int errno; diff --git a/third_party/opa/wasm/src/lib/errno.h b/third_party/opa/wasm/src/lib/errno.h new file mode 100644 index 000000000000..f6c73983399f --- /dev/null +++ b/third_party/opa/wasm/src/lib/errno.h @@ -0,0 +1,139 @@ +#ifndef OPA_ERRNO_H +#define OPA_ERRNO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)errno.h 8.5 (Berkeley) 1/21/94 + * $FreeBSD$ + */ + +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EDEADLK 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define ETXTBSY 26 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define ERANGE 34 +#define EAGAIN 35 +#define EWOULDBLOCK EAGAIN +#define EINPROGRESS 36 +#define EALREADY 37 +#define ENOTSOCK 38 +#define EDESTADDRREQ 39 +#define EMSGSIZE 40 +#define EPROTOTYPE 41 +#define ENOPROTOOPT 42 +#define EPROTONOSUPPORT 43 +#define ESOCKTNOSUPPORT 44 +#define EOPNOTSUPP 45 +#define ENOTSUP EOPNOTSUPP +#define EPFNOSUPPORT 46 +#define EAFNOSUPPORT 47 +#define EADDRINUSE 48 +#define EADDRNOTAVAIL 49 +#define ENETDOWN 50 +#define ENETUNREACH 51 +#define ENETRESET 52 +#define ECONNABORTED 53 +#define ECONNRESET 54 +#define ENOBUFS 55 +#define EISCONN 56 +#define ENOTCONN 57 +#define ESHUTDOWN 58 +#define ETOOMANYREFS 59 +#define ETIMEDOUT 60 +#define ECONNREFUSED 61 +#define ELOOP 62 +#define ENAMETOOLONG 63 +#define EHOSTDOWN 64 +#define EHOSTUNREACH 65 +#define ENOTEMPTY 66 +#define ENOLCK 77 +#define ENOSYS 78 +#define EFTYPE 79 +#define EAUTH 80 +#define ENEEDAUTH 81 +#define EIDRM 82 +#define ENOMSG 83 +#define EOVERFLOW 84 +#define ECANCELED 85 +#define EILSEQ 86 +#define ENOATTR 87 +#define EDOOFUS 88 +#define EBADMSG 89 +#define EMULTIHOP 90 +#define ENOLINK 91 +#define EPROTO 92 +#define ENOTRECOVERABLE 95 +#define EOWNERDEAD 96 + +extern int errno; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/lib/inttypes.h b/third_party/opa/wasm/src/lib/inttypes.h new file mode 100644 index 000000000000..76d8c1747bf3 --- /dev/null +++ b/third_party/opa/wasm/src/lib/inttypes.h @@ -0,0 +1,4 @@ +#ifndef OPA_INTTYPES_H +#define OPA_INTTYPES_H + +#endif diff --git a/third_party/opa/wasm/src/lib/locale.c b/third_party/opa/wasm/src/lib/locale.c new file mode 100644 index 000000000000..7ae91f197935 --- /dev/null +++ b/third_party/opa/wasm/src/lib/locale.c @@ -0,0 +1,13 @@ +#include + +static struct lconv lc; + +/* POSIX/C locale defaults. */ + +struct lconv *localeconv(void) +{ + lc.decimal_point = "."; + lc.thousands_sep = ""; + lc.grouping = "-1"; + return &lc; +} diff --git a/third_party/opa/wasm/src/lib/locale.h b/third_party/opa/wasm/src/lib/locale.h new file mode 100644 index 000000000000..9fd0ca092e89 --- /dev/null +++ b/third_party/opa/wasm/src/lib/locale.h @@ -0,0 +1,21 @@ +#ifndef OPA_LOCALE_H +#define OPA_LOCALE_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct lconv +{ + char *decimal_point; + char *thousands_sep; + char *grouping; +}; + +struct lconv *localeconv(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/lib/math.c b/third_party/opa/wasm/src/lib/math.c new file mode 100644 index 000000000000..2666ac8c5ac1 --- /dev/null +++ b/third_party/opa/wasm/src/lib/math.c @@ -0,0 +1,209 @@ +#include "math.h" + +#include + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +typedef union +{ + double value; + struct + { + uint32_t lsw; + uint32_t msw; + } parts; + struct + { + uint64_t w; + } xparts; +} ieee_double_shape_type; + +/* Get two 32 bit ints from a double. */ + +#define EXTRACT_WORDS(ix0,ix1,d) \ + do { \ + ieee_double_shape_type ew_u; \ + ew_u.value = (d); \ + (ix0) = ew_u.parts.msw; \ + (ix1) = ew_u.parts.lsw; \ + } while (0) + +/* Set a double from two 32 bit ints. */ + +#define INSERT_WORDS(d,ix0,ix1) \ + do { \ + ieee_double_shape_type iw_u; \ + iw_u.parts.msw = (ix0); \ + iw_u.parts.lsw = (ix1); \ + (d) = iw_u.value; \ + } while (0) + +/* Get the more significant 32 bit int from a double. */ + +#define GET_HIGH_WORD(i,d) \ + do { \ + ieee_double_shape_type gh_u; \ + gh_u.value = (d); \ + (i) = gh_u.parts.msw; \ + } while (0) + +/* Set the more significant 32 bits of a double from an int. */ + +#define SET_HIGH_WORD(d,v) \ + do { \ + ieee_double_shape_type sh_u; \ + sh_u.value = (d); \ + sh_u.parts.msw = (v); \ + (d) = sh_u.value; \ + } while (0) + +/* Set the less significant 32 bits of a double from an int. */ + +#define SET_LOW_WORD(d,v) \ + do { \ + ieee_double_shape_type sl_u; \ + sl_u.value = (d); \ + sl_u.parts.lsw = (v); \ + (d) = sl_u.value; \ + } while (0) + +static const double huge = 1.0e300; + +double ceil(double x) +{ + int32_t i0,i1,j0; + uint32_t i,j; + EXTRACT_WORDS(i0,i1,x); + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0<0) {i0=0x80000000;i1=0;} + else if((i0|i1)!=0) { i0=0x3ff00000;i1=0;} + } + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0>0) i0 += (0x00100000)>>j0; + i0 &= (~i); i1=0; + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((uint32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0>0) { + if(j0==20) i0+=1; + else { + j = i1 + (1<<(52-j0)); + if(j= 0x7ff00000) return x+x; + if (hx == 0x3ff00000 && lx == 0) + return zero; /* log(1) = +0 */ + k += (hx>>20)-1023; + hx &= 0x000fffff; + i = (hx+0x95f64)&0x100000; + SET_HIGH_WORD(x,hx|(i^0x3ff00000)); /* normalize x or x/2 */ + k += (i>>20); + y = (double)k; + f = x - 1.0; + hfsq = 0.5*f*f; + r = k_log1p(f); + + /* See e_log2.c for most details. */ + hi = f - hfsq; + SET_LOW_WORD(hi,0); + lo = (f - hi) - hfsq + r; + val_hi = hi*ivln10hi; + y2 = y*log10_2hi; + val_lo = y*log10_2lo + (lo+hi)*ivln10lo + lo*ivln10hi; + + /* + * Extra precision in for adding y*log10_2hi is not strictly needed + * since there is no very large cancellation near x = sqrt(2) or + * x = 1/sqrt(2), but we do it anyway since it costs little on CPUs + * with some parallelism and it reduces the error for many args. + */ + w = y2 + val_hi; + val_lo += (y2 - w) + val_hi; + val_hi = w; + + return val_lo + val_hi; +} + diff --git a/third_party/opa/wasm/src/lib/math.h b/third_party/opa/wasm/src/lib/math.h new file mode 100644 index 000000000000..a2c04490e412 --- /dev/null +++ b/third_party/opa/wasm/src/lib/math.h @@ -0,0 +1,261 @@ +#ifndef OPA_MATH_H +#define OPA_MATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef float float_t; +typedef double double_t; + +double ceil(double x); +double log10(double x); + +// not implemented: + +#define INFINITY (__builtin_inff()) + +double acos(double x); +float acosf(float x); +long double acosl(long double x); + +double acosh(double x); +float acoshf(float x); +long double acoshl(long double x); + +double asin(double x); +float asinf(float x); +long double asinhl(long double x); + +long double asinl(long double x); +double asinh(double x); +float asinhf(float x); + +double atan(double x); +float atanf(float x); +long double atanl( long double x); + +double atan2(double y, double x); +float atan2f(float y, float x); +long double atan2l(long double y, long double x); + +double atanh(double x); +float atanhf(float x); +long double atanhl(long double x); + +float ceilf(float x); +long double ceill(long double x); + +double cbrt(double x); +float cbrtf(float x); +long double cbrtl(long double x); + +double copysign(double x, double y); +float copysignf(float x, float y); +long double copysignl(long double x, long double y); + +double cos(double x); +float cosf(float x); +long double cosl(long double x); + +double cosh(double x); +float coshf(float x); +long double coshl(long double x); + +double erf(double x); +float erff(float x); +long double erfl(long double x); + +double erfc(double x); +float erfcf(float x); +long double erfcl(long double x); + +double exp(double x); +float expf(float x); +long double expl(long double x); + +double exp2(double x); +float exp2f(float x); +long double exp2l(long double x); + +double expm1(double x); +float expm1f(float x); +long double expm1l(long double x); + +double fabs(double x); +float fabsf(float x); +long double fabsl(long double x); + +double fdim(double x, double y); +float fdimf(float x, float y); +long double fdiml(long double x, long double y); + +double floor(double x); +float floorf(float x); +long double floorl(long double x); + +double fma(double x, double y, double z); +float fmaf(float x, float y, float z); +long double fmal(long double x, long double y, long double z); + +double fmax(double x, double y); +float fmaxf(float x, float y); +long double fmaxl(long double x, long double y); + +double fmin(double x, double y); +float fminf(float x, float y); +long double fminl(long double x, long double y); + +double fmod(double x, double y); +float fmodf(float x, float y); +long double fmodl(long double x, long double y); + +double frexp(double x, int *exp); +float frexpf(float x, int *exp); +long double frexpl(long double x, int *exp); + +double hypot(double x, double y); +float hypotf(float x, float y); +long double hypotl(long double x, long double y); + +int ilogb(double x); +int ilogbf(float x); +int ilogbl(long double x); + +double ldexp(double x, int exp); +float ldexpf(float x, int exp); +long double ldexpl(long double x, int exp); + +double lgamma(double x); +float lgammaf(float x); +long double lgammal(long double x); + +double log(double x); +float logf(float x); +long double logl(long double x); + +double log1p(double x); +float log1pf(float x); +long double log1pl(long double x); + +double log2(double x); +float log2f(float x); +long double log2l(long double x); + +float log10f(float x); +long double log10l(long double x); + +double logb(double x); +float logbf(float x); +long double logbl(long double x); + +long int lrint(double x); +long int lrintf(float x); +long int lrintl(long double x); + +long long int llrint(double x); +long long int llrintf(float x); +long long int llrintl(long double x); + +long int lround(double x); +long int lroundf(float x); +long int lroundl(long double x); + +long long int llround(double x); +long long int llroundf(float x); +long long int llroundl(long double x); + +double modf(double x, double *iptr); +float modff(float x, float *iptr); +long double modfl(long double x, long double *iptr); + +double nan(const char *tagp); +float nanf(const char *tagp); +long double nanl(const char *tagp); + +double nearbyint(double x); +float nearbyintf(float x); +long double nearbyintl(long double x); + +double nextafter(double x, double y); +float nextafterf(float x, float y); +long double nextafterl(long double x, long double y); + +double nexttoward(double x, long double y); +float nexttowardf(float x, long double y); +long double nexttowardl(long double x, long double y); + +double pow(double x, double y); +float powf(float x, float y); +long double powl(long double x, long double y); + +double remainder(double x, double y); +float remainderf(float x, float y); +long double remainderl(long double x, long double y); + +double remquo(double x, double y, int *quo); +float remquof(float x, float y, int *quo); +long double remquol(long double x, long double y, int *quo); + +double rint(double x); +float rintf(float x); +long double rintl(long double x); + +double round(double x); +float roundf(float x); +long double roundl(long double x); + +double scalbn(double x, int exp); +float scalbnf(float x, int exp); +long double scalbnl(long double x, int exp); + +double scalbln(double x, long int exp); +float scalblnf(float x, long int exp); +long double scalblnl(long double x, long int exp); + +double sin(double x); +float sinf(float x); +long double sinl(long double x); + +double sinh(double x); +float sinhf(float x); +long double sinhl(long double x); + +double sqrt(double x); +float sqrtf(float x); +long double sqrtl(long double x); + +double tan(double x); +float tanf(float x); +long double tanl(long double x); + +double tanh(double x); +float tanhf(float x); +long double tanhl(long double x); + +double tgamma(double x); +float tgammaf(float x); +long double tgammal(long double x); + +double trunc(double x); +float truncf(float x); +long double truncl(long double x); + +int fpclassify(float x); +int isfinite(float x); +int isgreater(float x, float y); +int isgreaterequal(float x, float y); +int isinf(float x); +int isless(float x, float y); +int islessequal(float x, float y); +int islessgreater(float x, float y); +int isnan(float x); +int isnormal(float x); +int isunordered(float x, float y); +int signbit(float x); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/lib/printf.c b/third_party/opa/wasm/src/lib/printf.c new file mode 100644 index 000000000000..fda6fb382aae --- /dev/null +++ b/third_party/opa/wasm/src/lib/printf.c @@ -0,0 +1,858 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include "printf.h" +#include "../std.h" +#include + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + + +// output function type +typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); + + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void* arg); + void* arg; +} out_fct_wrap_type; + + +// internal buffer output +static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) { + ((char*)buffer)[idx] = character; + } +} + + +// internal null output +static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)character; (void)buffer; (void)idx; (void)maxlen; +} + + +// internal output function wrapper +static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)idx; (void)maxlen; + if (character) { + // buffer is the output fct pointer + ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); + } +} + + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char* str, size_t maxsize) +{ + const char* s; + for (s = str; *s && maxsize--; ++s); + return (unsigned int)(s - str); +} + + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char** str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if (len && (base == 16U)) { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) { + } + else if ((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) { + if ((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } + else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch (*format) { + case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; + case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; + case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; + case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; + case '#': flags |= FLAGS_HASH; format++; n = 1U; break; + default : n = 0U; break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) { + width = _atoi(&format); + } + else if (*format == '*') { + const int w = va_arg(va, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) { + precision = _atoi(&format); + } + else if (*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) { + case 'l' : + flags |= FLAGS_LONG; + format++; + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h' : + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't' : + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z' : + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default : + break; + } + + // evaluate specifier + switch (*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') { + base = 16U; + } + else if (*format == 'o') { + base = 8U; + } + else if (*format == 'b') { + base = 2U; + } + else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else { + // unsigned + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f' : + case 'F' : + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c' : { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's' : { + const char* p = va_arg(va, char*); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p' : { + width = sizeof(void*) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); + } + else { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%' : + out('%', buffer, idx++, maxlen); + format++; + break; + + default : + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + + +/////////////////////////////////////////////////////////////////////////////// + +int snprintf_(char* buffer, size_t count, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + + +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + diff --git a/third_party/opa/wasm/src/lib/printf.h b/third_party/opa/wasm/src/lib/printf.h new file mode 100644 index 000000000000..84cc1c5f7f91 --- /dev/null +++ b/third_party/opa/wasm/src/lib/printf.h @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + * If the formatted string is truncated the buffer size (count) is returned + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ +int snprintf_(char* buffer, size_t count, const char* format, ...); +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); + +#ifdef __cplusplus +} +#endif + +#endif // _PRINTF_H_ diff --git a/third_party/opa/wasm/src/lib/signal.h b/third_party/opa/wasm/src/lib/signal.h new file mode 100644 index 000000000000..bf91973b850f --- /dev/null +++ b/third_party/opa/wasm/src/lib/signal.h @@ -0,0 +1,16 @@ +#ifndef OPA_SIGNAL_H +#define OPA_SIGNAL_H + +#include "../std.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define raise(signal) opa_abort("signal") + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/lib/stdio.c b/third_party/opa/wasm/src/lib/stdio.c new file mode 100644 index 000000000000..2698d3febfef --- /dev/null +++ b/third_party/opa/wasm/src/lib/stdio.c @@ -0,0 +1,51 @@ +#include "stdio.h" +#include "printf.h" + +#include "../std.h" +#include "stdlib.h" + +struct _FILE {}; + +FILE _stderr; +FILE _stdout; + +FILE *stderr = &_stderr; +FILE *stdout = &_stdout; + +int fprintf(FILE *stream, const char * format, ...) +{ + opa_abort("fprintf: not implemented"); + return 0; +} + +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + opa_abort("fwrite: not implemented"); + return 0; +} + +int fputc(int c, FILE *stream) +{ + opa_abort("fputc: not implemented"); + return 0; +} + +int fputs(const char *s, FILE *stream) +{ + for (size_t i = 0; s[i] != '\0'; i++) + { + fputc(s[i], stream); + } + + return 1; +} + +int puts(const char *s) +{ + for (size_t i = 0; s[i] != '\0'; i++) + { + fputc(s[i], stdout); + } + + return 1; +} diff --git a/third_party/opa/wasm/src/lib/stdio.h b/third_party/opa/wasm/src/lib/stdio.h new file mode 100644 index 000000000000..dad096df3299 --- /dev/null +++ b/third_party/opa/wasm/src/lib/stdio.h @@ -0,0 +1,79 @@ +#ifndef OPA_STDIO_H +#define OPA_STDIO_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define EOF (-1) + +struct _FILE; +typedef struct _FILE FILE; +extern FILE *stderr; +extern FILE *stdout; + +typedef struct +{ + int32_t __pos; + int32_t __state; +} fpos_t; + +int fprintf(FILE *stream, const char * format, ...); +int fputc(int c, FILE *stream); +int fputs(const char *s, FILE *stream); +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); +int puts(const char *s); + +// not implemented: + +void clearerr(FILE* stream); +int fclose(FILE *stream); +int feof(FILE* stream); +int ferror(FILE* stream); +FILE* fopen(const char* filename, const char* mode); +int fflush(FILE *stream); +int fgetc(FILE *stream); +int fgetpos(FILE* stream, fpos_t* pos); +int getc(FILE* stream); +char *fgets(char *s, int size, FILE *stream); +size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream); +FILE* freopen(const char* filename, const char * mode, FILE * stream); +int fscanf(FILE* stream, const char * format, ...); +int fseek(FILE* stream, long offset, int whence); +int fsetpos(FILE* stream, const fpos_t* pos); +long ftell(FILE* stream); +int getchar(void); +void perror(const char* s); +int printf(const char * format, ...); +int putc(int c, FILE* stream); +int putchar(int c); +int remove(const char* filename); +int rename(const char* old, const char* _new); +void rewind(FILE* stream); +int scanf(const char* format, ...); +void setbuf(FILE* stream, char* buf); +int setvbuf(FILE* stream, char* buf, int mode, size_t size); +int sprintf(char* s, const char* format, ...); +int sscanf(const char* s, const char* format, ...); +FILE* tmpfile(void); +char* tmpnam(char* s); +int ungetc(int c, FILE* stream); +int vfprintf(FILE* stream, const char* format, va_list arg); +int vfscanf(FILE* stream, const char* format, va_list arg); +int vprintf(const char* format, va_list arg); +int vscanf(const char* format, va_list arg); +int vsprintf(char* s, const char* format, va_list arg); +int vsscanf(const char* s, const char* format, va_list arg); + +#ifdef __cplusplus +} +#endif + +#include "printf.h" + +#endif + diff --git a/third_party/opa/wasm/src/lib/stdlib.c b/third_party/opa/wasm/src/lib/stdlib.c new file mode 100644 index 000000000000..801b57cb7023 --- /dev/null +++ b/third_party/opa/wasm/src/lib/stdlib.c @@ -0,0 +1,161 @@ +#include "stdlib.h" + +#include +#include +#include + +#include "../malloc.h" +#include "../std.h" + +void abort(void) +{ + while (true) + { + opa_abort(""); + } +} + +void opa_abort(const char *msg) +{ + opa_abort_(msg); + __builtin_unreachable(); +} + +void *malloc(size_t size) +{ + return opa_malloc(size); +} + +void free(void *ptr) +{ + opa_free(ptr); +} + +void *calloc(size_t nmemb, size_t size) +{ + void *v = malloc(size); + memset(v, 0, size); + return v; +} + +void *realloc(void *ptr, size_t size) +{ + return opa_realloc(ptr, size); +} + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Convert a string to a long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +long +strtol(const char *nptr, char **endptr, int base) +{ + const char *s = nptr; + unsigned long acc; + unsigned char c; + unsigned long cutoff; + int neg = 0, any, cutlim; + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + do { + c = *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX; + cutlim = cutoff % (unsigned long)base; + cutoff /= (unsigned long)base; + for (acc = 0, any = 0;; c = *s++) { + if (!isascii(c)) + break; + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = neg ? LONG_MIN : LONG_MAX; + } else if (neg) + acc = -acc; + if (endptr != NULL) + *endptr = (char *)(any ? s - 1 : nptr); + return (acc); +} diff --git a/third_party/opa/wasm/src/lib/stdlib.h b/third_party/opa/wasm/src/lib/stdlib.h new file mode 100644 index 000000000000..b5987b50b2a9 --- /dev/null +++ b/third_party/opa/wasm/src/lib/stdlib.h @@ -0,0 +1,90 @@ +#ifndef OPA_STDLIB_H +#define OPA_STDLIB_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void opa_abort(const char *msg); +__attribute__((import_name("opa_abort"))) void opa_abort_(const char *msg); +void abort(void); +void *malloc(size_t size); +void free(void *ptr); +void *calloc(size_t nmemb, size_t size); +void *realloc(void *ptr, size_t size); + +double strtod(const char *nptr, char **endptr); +float strtof(const char *nptr, char **endptr); +long int strtol(const char *nptr, char **endptr, int base); +long long int strtoll(const char *nptr, char **endptr, int base); +unsigned long int strtoul(const char *nptr, char **endptr, int base); +unsigned long long int strtoull(const char *nptr, char **endptr, int base); + +typedef struct +{ + int quot; + int rem; +} div_t; + +typedef struct +{ + long int quot; + long int rem; +} ldiv_t; + +typedef struct +{ + long long int quot; + long long int rem; +} lldiv_t; + +// not implemented: + +int abs(int j); +long int labs(long int j); +long long int llabs(long long int j); + +double atof(const char *nptr); +int atoi(const char *nptr); +long atol(const char *nptr); +long long atoll(const char *nptr); + +void *bsearch(const void *key, const void *base, + size_t nmemb, size_t size, + int (*compar)(const void *, const void *)); + +div_t div(int numerator, int denominator); +ldiv_t ldiv(long numerator, long denominator); +lldiv_t lldiv(long long numerator, long long denominator); + +void exit(int status); +int atexit(void (*function)(void)); +void _Exit(int status); + +char *getenv(const char *name); + +int mblen(const char *s, size_t n); +size_t mbstowcs(wchar_t *dest, const char *src, size_t n); +int mbtowc(wchar_t *pwc, const char *s, size_t n); + +void qsort(void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *)); + +int rand(void); +int rand_r(unsigned int *seedp); +void srand(unsigned int seed); + +long double strtold(const char *nptr, char **endptr); + +int system(const char *command); + +int wctomb(char *s, wchar_t wc); +size_t wcstombs(char *dest, const wchar_t *src, size_t n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/lib/string.c b/third_party/opa/wasm/src/lib/string.c new file mode 100644 index 000000000000..f2b94f36f99e --- /dev/null +++ b/third_party/opa/wasm/src/lib/string.c @@ -0,0 +1,118 @@ +#include + +#include "../malloc.h" + +void *memchr(const void *src, int c, size_t n) +{ + const unsigned char *s = src; + + while (n--) + { + if (*s == (unsigned char)c) + { + return (void *)s; + } + + s++; + } + + return NULL; +} + +int memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *p1 = s1; + const unsigned char *p2 = s2; + + if (p1 == p2) + { + return 0; + } + + while (n--) + { + if (*p1 != *p2) + { + return *p1 - *p2; + } + + p1++; + p2++; + } + + return 0; +} + +void *memcpy(void *dest, const void *src, size_t n) +{ + unsigned char *d = dest; + const unsigned char *s = src; + + for (size_t i = 0; i < n; i++) + { + d[i] = s[i]; + } + + return dest; +} + +void *memmove(void *dest, const void *src, size_t n) +{ + unsigned char *d = dest; + const unsigned char *s = src; + unsigned char *t = opa_malloc(n); + + for (size_t i = 0; i < n; i++) + { + t[i] = s[i]; + } + + for (size_t i = 0; i < n; i++) + { + d[i] = t[i]; + } + + opa_free(t); + + return dest; +} + +void *memset(void *s, int c, unsigned long n) +{ + unsigned char *p = (unsigned char *)s; + + while (n--) + { + *p++ = c; + } + + return s; +} + +char *strchr(const char *s, int c) +{ + while (1) + { + if (*s == (char)c) + { + return (char *)s; + } + else if (*s == '\0') + { + break; + } + + s++; + } + + return NULL; +} + +size_t strlen(const char *s) +{ + size_t i = 0; + + for (i = 0; s[i] != '\0'; i++); + + return i; +} diff --git a/third_party/opa/wasm/src/lib/string.h b/third_party/opa/wasm/src/lib/string.h new file mode 100644 index 000000000000..405a35be5ee0 --- /dev/null +++ b/third_party/opa/wasm/src/lib/string.h @@ -0,0 +1,40 @@ +#ifndef OPA_STRING_H +#define OPA_STRING_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void *memchr(const void *s, int c, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +char *strchr(const char *s, int c); +size_t strlen(const char *s); + +// not implemented: + +char *strcat(char *dest, const char *src); +int strcmp(const char *s1, const char *s2); +int strcoll(const char *s1, const char *s2); +char *strcpy(char *dest, const char *src); +size_t strcspn(const char *s, const char *reject); +char *strerror(int errnum); +char *strncat(char *dest, const char *src, size_t n); +int strncmp(const char *s1, const char *s2, size_t n); +char *strncpy(char *dest, const char *src, size_t n); +char *strpbrk(const char *s, const char *accept); +char *strrchr(const char *s, int c); +size_t strspn(const char *s, const char *accept); +char *strstr(const char *haystack, const char *needle); +char *strtok(char *str, const char *delim); +size_t strxfrm(char *dest, const char *src, size_t n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/lib/time.h b/third_party/opa/wasm/src/lib/time.h new file mode 100644 index 000000000000..3c7bc224f357 --- /dev/null +++ b/third_party/opa/wasm/src/lib/time.h @@ -0,0 +1,40 @@ +#ifndef OPA_TIME_H +#define OPA_TIME_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + int __internal; +} tm; + +typedef long int time_t; +typedef long int clock_t; + +struct timespec +{ + int __internal; +}; + +// not implemented: + +clock_t clock(); +double difftime(time_t time1, time_t time0); +time_t mktime(tm* timeptr); +time_t time(time_t* timer); +char* asctime(const tm* timeptr); +char* ctime(const time_t* timer); +tm* gmtime(const time_t* timer); +tm* localtime(const time_t* timer); +size_t strftime(char* s, size_t maxsize, const char* format, const tm* timeptr); +int timespec_get(struct timespec *ts, int base); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/lib/unistd.h b/third_party/opa/wasm/src/lib/unistd.h new file mode 100644 index 000000000000..d34f163d1e31 --- /dev/null +++ b/third_party/opa/wasm/src/lib/unistd.h @@ -0,0 +1,4 @@ +#ifndef OPA_UNISTD_H +#define OPA_UNISTD_H + +#endif diff --git a/third_party/opa/wasm/src/lib/wchar.c b/third_party/opa/wasm/src/lib/wchar.c new file mode 100644 index 000000000000..9e714deb5158 --- /dev/null +++ b/third_party/opa/wasm/src/lib/wchar.c @@ -0,0 +1,68 @@ +#include "string.h" + +wchar_t *wmemchr(const wchar_t *s, wchar_t c, size_t n) +{ + while (n--) + { + if (*s == (wchar_t)c) + { + return (wchar_t *)s; + } + + s++; + } + + return NULL; +} + +int wmemcmp(const wchar_t *s1, const wchar_t *s2, size_t n) +{ + if (s1 == s2) + { + return 0; + } + + while (n--) + { + if (*s1 != *s2) + { + return *s1 - *s2; + } + + s1++; + s2++; + } + + return 0; +} + +wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, size_t n) +{ + return memcpy(dest, src, n * sizeof(wchar_t)); +} + +wchar_t *wmemset(wchar_t *wcs, wchar_t wc, size_t n) +{ + wchar_t *p = (wchar_t *)wcs; + + while (n--) + { + *p++ = wc; + } + + return wcs; +} + +wchar_t *wmemmove(wchar_t *dest, const wchar_t *src, size_t n) +{ + return memmove(dest, src, n * sizeof(wchar_t)); +} + +size_t wcslen(const wchar_t *s) +{ + size_t i = 0; + + for (i = 0; s[i] != L'\0'; i++); + + return i; +} diff --git a/third_party/opa/wasm/src/lib/wchar.h b/third_party/opa/wasm/src/lib/wchar.h new file mode 100644 index 000000000000..17e3fd841393 --- /dev/null +++ b/third_party/opa/wasm/src/lib/wchar.h @@ -0,0 +1,92 @@ +#ifndef OPA_WCHAR_H +#define OPA_WCHAR_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef __WINT_TYPE__ wint_t; + +typedef struct +{ + int __internal; +} mbstate_t; + +#define WEOF (0xffffffffu) + +wchar_t *wmemchr(const wchar_t *s, wchar_t c, size_t n); +int wmemcmp(const wchar_t* s1, const wchar_t* s2, size_t n); +wchar_t *wmemmove(wchar_t *dest, const wchar_t *src, size_t n); +wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, size_t n); +wchar_t *wmemset(wchar_t *wcs, wchar_t wc, size_t n); +size_t wcslen(const wchar_t* s); + +// not implemented: + +wint_t btowc(int c); +wint_t fgetwc(FILE* stream); +wchar_t* fgetws(wchar_t* s, int n, FILE* stream); +int fwprintf(FILE* stream, const wchar_t* format, ...); +wint_t fputwc(wchar_t c, FILE* stream); +int fputws(const wchar_t* s, FILE* stream); +int fwide(FILE* stream, int mode); +int fwscanf(FILE* stream, const wchar_t* format, ...); +wint_t getwc(FILE* stream); +wint_t getwchar(); +int mbsinit(const mbstate_t *ps); +size_t mbrlen(const char *s, size_t n, mbstate_t *ps); +size_t mbrtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *ps); +size_t mbsrtowcs(wchar_t *dest, const char **src, size_t len, mbstate_t *ps); +wint_t putwc(wchar_t c, FILE* stream); +wint_t putwchar(wchar_t c); +int swprintf(wchar_t* s, size_t n, const wchar_t* format, ...); +int swscanf(const wchar_t* s, const wchar_t* format, ...); +wint_t ungetwc(wint_t c, FILE* stream); +int vfwprintf(FILE* stream, const wchar_t* format, va_list arg); +int vfwscanf(FILE* stream, const wchar_t* format, va_list arg); +int vswprintf(wchar_t* s, size_t n, const wchar_t* format, va_list arg); +int vswscanf(const wchar_t* s, const wchar_t* format, va_list arg); +int vwprintf(const wchar_t *format, va_list args); +int vwscanf(const wchar_t* format, va_list arg); +size_t wcrtomb(char *s, wchar_t wc, mbstate_t *ps); +wchar_t* wcscat(wchar_t* s1, const wchar_t* s2); +wchar_t *wcschr(const wchar_t *wcs, wchar_t wc); +int wcscoll(const wchar_t* s1, const wchar_t* s2); +int wcscmp(const wchar_t* s1, const wchar_t* s2); +wchar_t* wcscpy(wchar_t* s1, const wchar_t* s2); +size_t wcscspn(const wchar_t* s1, const wchar_t* s2); +size_t wcsftime(wchar_t* s, size_t maxsize, const wchar_t* format, const tm* timeptr); +wchar_t* wcsncat(wchar_t* s1, const wchar_t* s2, size_t n); +int wcsncmp(const wchar_t* s1, const wchar_t* s2, size_t n); +wchar_t* wcsncpy(wchar_t* s1, const wchar_t* s2, size_t n); +wchar_t *wcspbrk(const wchar_t *wcs, const wchar_t *accept); +wchar_t *wcsrchr(const wchar_t *wcs, wchar_t wc); +size_t wcsrtombs(char *dest, const wchar_t **src, size_t len, mbstate_t *ps); +size_t wcsspn(const wchar_t *wcs, const wchar_t *accept); +wchar_t *wcsstr(const wchar_t *haystack, const wchar_t *needle); +wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **ptr); +size_t wcsxfrm(wchar_t* s1, const wchar_t* s2, size_t n); +int wctob(wint_t c); +int wprintf(const wchar_t* format, ...); +int wscanf(const wchar_t* format, ...); + +float wcstof(const wchar_t* nptr, wchar_t** endptr); +double wcstod(const wchar_t* nptr, wchar_t** endptr); +long wcstol(const wchar_t* nptr, wchar_t** endptr, int base); + +long double wcstold(const wchar_t* nptr, wchar_t** endptr); +long long wcstoll(const wchar_t* nptr, wchar_t** endptr, int base); + +unsigned long wcstoul(const wchar_t* nptr, wchar_t** endptr, int base); +unsigned long long wcstoull(const wchar_t* nptr, wchar_t** endptr, int base); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/lib/wctype.h b/third_party/opa/wasm/src/lib/wctype.h new file mode 100644 index 000000000000..ad5b98d689d3 --- /dev/null +++ b/third_party/opa/wasm/src/lib/wctype.h @@ -0,0 +1,38 @@ +#ifndef OPA_WCTYPE_H +#define OPA_WCTYPE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint32_t wctype_t; +typedef const int32_t *wctrans_t; + +// not implemented: + +int iswalnum(wint_t wc); +int iswalpha(wint_t wc); +int iswblank(wint_t wc); +int iswcntrl(wint_t wc); +int iswctype(wint_t wc, wctype_t desc); +int iswdigit(wint_t wc); +int iswgraph(wint_t wc); +int iswlower(wint_t wc); +int iswprint(wint_t wc); +int iswpunct(wint_t wc); +int iswspace(wint_t wc); +int iswupper(wint_t wc); +int iswxdigit(wint_t wc); +wint_t towlower(wint_t wc); +wint_t towupper(wint_t wc); +wint_t towctrans(wint_t wc, wctrans_t desc); +wctype_t wctype(const char* property); +wctrans_t wctrans(const char* property); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/libc++/atomic b/third_party/opa/wasm/src/libc++/atomic new file mode 100644 index 000000000000..5448bc4d4e71 --- /dev/null +++ b/third_party/opa/wasm/src/libc++/atomic @@ -0,0 +1,32 @@ +#ifndef OPA_ATOMIC_H_ +#define OPA_ATOMIC_H_ + +namespace std { + +enum { + memory_order_relaxed, + memory_order_acquire, + memory_order_release, +}; + +// this is a minimal, no-op implementation of std::atomic. +template +class atomic { +public: + atomic() : value(NULL) { } + atomic(T v) : value(v) { } + + inline T load(int order) const { + return value; + } + + inline void store(T v, int order) { + value = v; + } +private: + T value; +}; + +} + +#endif // OPA_ATOMIC_H_ diff --git a/third_party/opa/wasm/src/libc++/hash.cc b/third_party/opa/wasm/src/libc++/hash.cc new file mode 100644 index 000000000000..89bb736c86c2 --- /dev/null +++ b/third_party/opa/wasm/src/libc++/hash.cc @@ -0,0 +1,561 @@ +//===-------------------------- hash.cpp ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "__hash_table" +#include "algorithm" +#include "stdexcept" +#include "type_traits" + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace { + +// handle all next_prime(i) for i in [1, 210), special case 0 +const unsigned small_primes[] = +{ + 0, + 2, + 3, + 5, + 7, + 11, + 13, + 17, + 19, + 23, + 29, + 31, + 37, + 41, + 43, + 47, + 53, + 59, + 61, + 67, + 71, + 73, + 79, + 83, + 89, + 97, + 101, + 103, + 107, + 109, + 113, + 127, + 131, + 137, + 139, + 149, + 151, + 157, + 163, + 167, + 173, + 179, + 181, + 191, + 193, + 197, + 199, + 211 +}; + +// potential primes = 210*k + indices[i], k >= 1 +// these numbers are not divisible by 2, 3, 5 or 7 +// (or any integer 2 <= j <= 10 for that matter). +const unsigned indices[] = +{ + 1, + 11, + 13, + 17, + 19, + 23, + 29, + 31, + 37, + 41, + 43, + 47, + 53, + 59, + 61, + 67, + 71, + 73, + 79, + 83, + 89, + 97, + 101, + 103, + 107, + 109, + 113, + 121, + 127, + 131, + 137, + 139, + 143, + 149, + 151, + 157, + 163, + 167, + 169, + 173, + 179, + 181, + 187, + 191, + 193, + 197, + 199, + 209 +}; + +} + +// Returns: If n == 0, returns 0. Else returns the lowest prime number that +// is greater than or equal to n. +// +// The algorithm creates a list of small primes, plus an open-ended list of +// potential primes. All prime numbers are potential prime numbers. However +// some potential prime numbers are not prime. In an ideal world, all potential +// prime numbers would be prime. Candidate prime numbers are chosen as the next +// highest potential prime. Then this number is tested for prime by dividing it +// by all potential prime numbers less than the sqrt of the candidate. +// +// This implementation defines potential primes as those numbers not divisible +// by 2, 3, 5, and 7. Other (common) implementations define potential primes +// as those not divisible by 2. A few other implementations define potential +// primes as those not divisible by 2 or 3. By raising the number of small +// primes which the potential prime is not divisible by, the set of potential +// primes more closely approximates the set of prime numbers. And thus there +// are fewer potential primes to search, and fewer potential primes to divide +// against. + +template +inline _LIBCPP_INLINE_VISIBILITY +typename enable_if<_Sz == 4, void>::type +__check_for_overflow(size_t N) +{ + if (N > 0xFFFFFFFB) + __throw_overflow_error("__next_prime overflow"); +} + +template +inline _LIBCPP_INLINE_VISIBILITY +typename enable_if<_Sz == 8, void>::type +__check_for_overflow(size_t N) +{ + if (N > 0xFFFFFFFFFFFFFFC5ull) + __throw_overflow_error("__next_prime overflow"); +} + +size_t +__next_prime(size_t n) +{ + const size_t L = 210; + const size_t N = sizeof(small_primes) / sizeof(small_primes[0]); + // If n is small enough, search in small_primes + if (n <= small_primes[N-1]) + return *std::lower_bound(small_primes, small_primes + N, n); + // Else n > largest small_primes + // Check for overflow + __check_for_overflow(n); + // Start searching list of potential primes: L * k0 + indices[in] + const size_t M = sizeof(indices) / sizeof(indices[0]); + // Select first potential prime >= n + // Known a-priori n >= L + size_t k0 = n / L; + size_t in = static_cast(std::lower_bound(indices, indices + M, n - k0 * L) + - indices); + n = L * k0 + indices[in]; + while (true) + { + // Divide n by all primes or potential primes (i) until: + // 1. The division is even, so try next potential prime. + // 2. The i > sqrt(n), in which case n is prime. + // It is known a-priori that n is not divisible by 2, 3, 5 or 7, + // so don't test those (j == 5 -> divide by 11 first). And the + // potential primes start with 211, so don't test against the last + // small prime. + for (size_t j = 5; j < N - 1; ++j) + { + const std::size_t p = small_primes[j]; + const std::size_t q = n / p; + if (q < p) + return n; + if (n == q * p) + goto next; + } + // n wasn't divisible by small primes, try potential primes + { + size_t i = 211; + while (true) + { + std::size_t q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 10; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 8; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 8; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 10; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + // This will loop i to the next "plane" of potential primes + i += 2; + } + } +next: + // n is not prime. Increment n to next potential prime. + if (++in == M) + { + ++k0; + in = 0; + } + n = L * k0 + indices[in]; + } +} + +_LIBCPP_END_NAMESPACE_STD diff --git a/third_party/opa/wasm/src/libc++/minimal.cc b/third_party/opa/wasm/src/libc++/minimal.cc new file mode 100644 index 000000000000..6fc4f3120319 --- /dev/null +++ b/third_party/opa/wasm/src/libc++/minimal.cc @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "../std.h" +#include "../malloc.h" + +void* operator new(size_t size) { + return opa_malloc(size); +} + +void operator delete(void *p) { + opa_free(p); +} + +void* operator new[](size_t size) { + return opa_malloc(size); +} + +void operator delete[](void *p) { + opa_free(p); +} + +extern "C" void __cxa_pure_virtual() { + opa_abort("pure virtual"); +} + +// Instantiate the minimum set of templates part of standard libc++ ABI. +// This is required because we do not link with the libc++. + +_LIBCPP_BEGIN_NAMESPACE_STD + +template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS __basic_string_common; +template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_string; +template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_string; + +template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS __vector_base_common; + +template void __sort<__less&, int*>(int*, int*, __less&); +template bool __insertion_sort_incomplete<__less&, int*>(int*, int*, __less&); + +_LIBCPP_END_NAMESPACE_STD diff --git a/third_party/opa/wasm/src/libc++/mutex b/third_party/opa/wasm/src/libc++/mutex new file mode 100644 index 000000000000..f2ffbba245bb --- /dev/null +++ b/third_party/opa/wasm/src/libc++/mutex @@ -0,0 +1,18 @@ +#ifndef OPA_MUTEX_H_ +#define OPA_MUTEX_H_ + +namespace std { + + // this is a minimal, no-op implementation of std::mutex. +class mutex { +public: + inline mutex() { } + inline void lock() { } + inline void unlock() { } +}; + +} + +#include_next + +#endif // OPA_MUTEX_H_ diff --git a/third_party/opa/wasm/src/libc++/mutex.cc b/third_party/opa/wasm/src/libc++/mutex.cc new file mode 100644 index 000000000000..ce75c8dcef23 --- /dev/null +++ b/third_party/opa/wasm/src/libc++/mutex.cc @@ -0,0 +1,74 @@ +//===------------------------- mutex.cpp ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include + +#include "limits" +#include "system_error" +#include "__undef_macros" + +_LIBCPP_BEGIN_NAMESPACE_STD + +void __call_once(volatile once_flag::_State_type& flag, void* arg, + void (*func)(void*)) +{ +#if defined(_LIBCPP_HAS_NO_THREADS) + if (flag == 0) + { +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + flag = 1; + func(arg); + flag = ~once_flag::_State_type(0); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + flag = 0; + throw; + } +#endif // _LIBCPP_NO_EXCEPTIONS + } +#else // !_LIBCPP_HAS_NO_THREADS + __libcpp_mutex_lock(&mut); + while (flag == 1) + __libcpp_condvar_wait(&cv, &mut); + if (flag == 0) + { +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + __libcpp_relaxed_store(&flag, once_flag::_State_type(1)); + __libcpp_mutex_unlock(&mut); + func(arg); + __libcpp_mutex_lock(&mut); + __libcpp_atomic_store(&flag, ~once_flag::_State_type(0), + _AO_Release); + __libcpp_mutex_unlock(&mut); + __libcpp_condvar_broadcast(&cv); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + __libcpp_mutex_lock(&mut); + __libcpp_relaxed_store(&flag, once_flag::_State_type(0)); + __libcpp_mutex_unlock(&mut); + __libcpp_condvar_broadcast(&cv); + throw; + } +#endif // _LIBCPP_NO_EXCEPTIONS + } + else + __libcpp_mutex_unlock(&mut); +#endif // !_LIBCPP_HAS_NO_THREADS +} + +_LIBCPP_END_NAMESPACE_STD diff --git a/third_party/opa/wasm/src/libmpdec/basearith.c b/third_party/opa/wasm/src/libmpdec/basearith.c new file mode 100644 index 000000000000..35de6b828491 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/basearith.c @@ -0,0 +1,658 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include +#include +#include +#include "constants.h" +#include "memory.h" +#include "typearith.h" +#include "basearith.h" + + +/*********************************************************************/ +/* Calculations in base MPD_RADIX */ +/*********************************************************************/ + + +/* + * Knuth, TAOCP, Volume 2, 4.3.1: + * w := sum of u (len m) and v (len n) + * n > 0 and m >= n + * The calling function has to handle a possible final carry. + */ +mpd_uint_t +_mpd_baseadd(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, + mpd_size_t m, mpd_size_t n) +{ + mpd_uint_t s; + mpd_uint_t carry = 0; + mpd_size_t i; + + assert(n > 0 && m >= n); + + /* add n members of u and v */ + for (i = 0; i < n; i++) { + s = u[i] + (v[i] + carry); + carry = (s < u[i]) | (s >= MPD_RADIX); + w[i] = carry ? s-MPD_RADIX : s; + } + /* if there is a carry, propagate it */ + for (; carry && i < m; i++) { + s = u[i] + carry; + carry = (s == MPD_RADIX); + w[i] = carry ? 0 : s; + } + /* copy the rest of u */ + for (; i < m; i++) { + w[i] = u[i]; + } + + return carry; +} + +/* + * Add the contents of u to w. Carries are propagated further. The caller + * has to make sure that w is big enough. + */ +void +_mpd_baseaddto(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n) +{ + mpd_uint_t s; + mpd_uint_t carry = 0; + mpd_size_t i; + + if (n == 0) return; + + /* add n members of u to w */ + for (i = 0; i < n; i++) { + s = w[i] + (u[i] + carry); + carry = (s < w[i]) | (s >= MPD_RADIX); + w[i] = carry ? s-MPD_RADIX : s; + } + /* if there is a carry, propagate it */ + for (; carry; i++) { + s = w[i] + carry; + carry = (s == MPD_RADIX); + w[i] = carry ? 0 : s; + } +} + +/* + * Add v to w (len m). The calling function has to handle a possible + * final carry. Assumption: m > 0. + */ +mpd_uint_t +_mpd_shortadd(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v) +{ + mpd_uint_t s; + mpd_uint_t carry; + mpd_size_t i; + + assert(m > 0); + + /* add v to w */ + s = w[0] + v; + carry = (s < v) | (s >= MPD_RADIX); + w[0] = carry ? s-MPD_RADIX : s; + + /* if there is a carry, propagate it */ + for (i = 1; carry && i < m; i++) { + s = w[i] + carry; + carry = (s == MPD_RADIX); + w[i] = carry ? 0 : s; + } + + return carry; +} + +/* Increment u. The calling function has to handle a possible carry. */ +mpd_uint_t +_mpd_baseincr(mpd_uint_t *u, mpd_size_t n) +{ + mpd_uint_t s; + mpd_uint_t carry = 1; + mpd_size_t i; + + assert(n > 0); + + /* if there is a carry, propagate it */ + for (i = 0; carry && i < n; i++) { + s = u[i] + carry; + carry = (s == MPD_RADIX); + u[i] = carry ? 0 : s; + } + + return carry; +} + +/* + * Knuth, TAOCP, Volume 2, 4.3.1: + * w := difference of u (len m) and v (len n). + * number in u >= number in v; + */ +void +_mpd_basesub(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, + mpd_size_t m, mpd_size_t n) +{ + mpd_uint_t d; + mpd_uint_t borrow = 0; + mpd_size_t i; + + assert(m > 0 && n > 0); + + /* subtract n members of v from u */ + for (i = 0; i < n; i++) { + d = u[i] - (v[i] + borrow); + borrow = (u[i] < d); + w[i] = borrow ? d + MPD_RADIX : d; + } + /* if there is a borrow, propagate it */ + for (; borrow && i < m; i++) { + d = u[i] - borrow; + borrow = (u[i] == 0); + w[i] = borrow ? MPD_RADIX-1 : d; + } + /* copy the rest of u */ + for (; i < m; i++) { + w[i] = u[i]; + } +} + +/* + * Subtract the contents of u from w. w is larger than u. Borrows are + * propagated further, but eventually w can absorb the final borrow. + */ +void +_mpd_basesubfrom(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n) +{ + mpd_uint_t d; + mpd_uint_t borrow = 0; + mpd_size_t i; + + if (n == 0) return; + + /* subtract n members of u from w */ + for (i = 0; i < n; i++) { + d = w[i] - (u[i] + borrow); + borrow = (w[i] < d); + w[i] = borrow ? d + MPD_RADIX : d; + } + /* if there is a borrow, propagate it */ + for (; borrow; i++) { + d = w[i] - borrow; + borrow = (w[i] == 0); + w[i] = borrow ? MPD_RADIX-1 : d; + } +} + +/* w := product of u (len n) and v (single word) */ +void +_mpd_shortmul(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v) +{ + mpd_uint_t hi, lo; + mpd_uint_t carry = 0; + mpd_size_t i; + + assert(n > 0); + + for (i=0; i < n; i++) { + + _mpd_mul_words(&hi, &lo, u[i], v); + lo = carry + lo; + if (lo < carry) hi++; + + _mpd_div_words_r(&carry, &w[i], hi, lo); + } + w[i] = carry; +} + +/* + * Knuth, TAOCP, Volume 2, 4.3.1: + * w := product of u (len m) and v (len n) + * w must be initialized to zero + */ +void +_mpd_basemul(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, + mpd_size_t m, mpd_size_t n) +{ + mpd_uint_t hi, lo; + mpd_uint_t carry; + mpd_size_t i, j; + + assert(m > 0 && n > 0); + + for (j=0; j < n; j++) { + carry = 0; + for (i=0; i < m; i++) { + + _mpd_mul_words(&hi, &lo, u[i], v[j]); + lo = w[i+j] + lo; + if (lo < w[i+j]) hi++; + lo = carry + lo; + if (lo < carry) hi++; + + _mpd_div_words_r(&carry, &w[i+j], hi, lo); + } + w[j+m] = carry; + } +} + +/* + * Knuth, TAOCP Volume 2, 4.3.1, exercise 16: + * w := quotient of u (len n) divided by a single word v + */ +mpd_uint_t +_mpd_shortdiv(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v) +{ + mpd_uint_t hi, lo; + mpd_uint_t rem = 0; + mpd_size_t i; + + assert(n > 0); + + for (i=n-1; i != MPD_SIZE_MAX; i--) { + + _mpd_mul_words(&hi, &lo, rem, MPD_RADIX); + lo = u[i] + lo; + if (lo < u[i]) hi++; + + _mpd_div_words(&w[i], &rem, hi, lo, v); + } + + return rem; +} + +/* + * Knuth, TAOCP Volume 2, 4.3.1: + * q, r := quotient and remainder of uconst (len nplusm) + * divided by vconst (len n) + * nplusm >= n + * + * If r is not NULL, r will contain the remainder. If r is NULL, the + * return value indicates if there is a remainder: 1 for true, 0 for + * false. A return value of -1 indicates an error. + */ +int +_mpd_basedivmod(mpd_uint_t *q, mpd_uint_t *r, + const mpd_uint_t *uconst, const mpd_uint_t *vconst, + mpd_size_t nplusm, mpd_size_t n) +{ + mpd_uint_t ustatic[MPD_MINALLOC_MAX]; + mpd_uint_t vstatic[MPD_MINALLOC_MAX]; + mpd_uint_t *u = ustatic; + mpd_uint_t *v = vstatic; + mpd_uint_t d, qhat, rhat, w2[2]; + mpd_uint_t hi, lo, x; + mpd_uint_t carry; + mpd_size_t i, j, m; + int retval = 0; + + assert(n > 1 && nplusm >= n); + m = sub_size_t(nplusm, n); + + /* D1: normalize */ + d = MPD_RADIX / (vconst[n-1] + 1); + + if (nplusm >= MPD_MINALLOC_MAX) { + if ((u = mpd_alloc(nplusm+1, sizeof *u)) == NULL) { + return -1; + } + } + if (n >= MPD_MINALLOC_MAX) { + if ((v = mpd_alloc(n+1, sizeof *v)) == NULL) { + mpd_free(u); + return -1; + } + } + + _mpd_shortmul(u, uconst, nplusm, d); + _mpd_shortmul(v, vconst, n, d); + + /* D2: loop */ + for (j=m; j != MPD_SIZE_MAX; j--) { + + /* D3: calculate qhat and rhat */ + rhat = _mpd_shortdiv(w2, u+j+n-1, 2, v[n-1]); + qhat = w2[1] * MPD_RADIX + w2[0]; + + while (1) { + if (qhat < MPD_RADIX) { + _mpd_singlemul(w2, qhat, v[n-2]); + if (w2[1] <= rhat) { + if (w2[1] != rhat || w2[0] <= u[j+n-2]) { + break; + } + } + } + qhat -= 1; + rhat += v[n-1]; + if (rhat < v[n-1] || rhat >= MPD_RADIX) { + break; + } + } + /* D4: multiply and subtract */ + carry = 0; + for (i=0; i <= n; i++) { + + _mpd_mul_words(&hi, &lo, qhat, v[i]); + + lo = carry + lo; + if (lo < carry) hi++; + + _mpd_div_words_r(&hi, &lo, hi, lo); + + x = u[i+j] - lo; + carry = (u[i+j] < x); + u[i+j] = carry ? x+MPD_RADIX : x; + carry += hi; + } + q[j] = qhat; + /* D5: test remainder */ + if (carry) { + q[j] -= 1; + /* D6: add back */ + (void)_mpd_baseadd(u+j, u+j, v, n+1, n); + } + } + + /* D8: unnormalize */ + if (r != NULL) { + _mpd_shortdiv(r, u, n, d); + /* we are not interested in the return value here */ + retval = 0; + } + else { + retval = !_mpd_isallzero(u, n); + } + + +if (u != ustatic) mpd_free(u); +if (v != vstatic) mpd_free(v); +return retval; +} + +/* + * Left shift of src by 'shift' digits; src may equal dest. + * + * dest := area of n mpd_uint_t with space for srcdigits+shift digits. + * src := coefficient with length m. + * + * The case splits in the function are non-obvious. The following + * equations might help: + * + * Let msdigits denote the number of digits in the most significant + * word of src. Then 1 <= msdigits <= rdigits. + * + * 1) shift = q * rdigits + r + * 2) srcdigits = qsrc * rdigits + msdigits + * 3) destdigits = shift + srcdigits + * = q * rdigits + r + qsrc * rdigits + msdigits + * = q * rdigits + (qsrc * rdigits + (r + msdigits)) + * + * The result has q zero words, followed by the coefficient that + * is left-shifted by r. The case r == 0 is trivial. For r > 0, it + * is important to keep in mind that we always read m source words, + * but write m+1 destination words if r + msdigits > rdigits, m words + * otherwise. + */ +void +_mpd_baseshiftl(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t n, mpd_size_t m, + mpd_size_t shift) +{ +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) + /* spurious uninitialized warnings */ + mpd_uint_t l=l, lprev=lprev, h=h; +#else + mpd_uint_t l, lprev, h; +#endif + mpd_uint_t q, r; + mpd_uint_t ph; + + assert(m > 0 && n >= m); + + _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS); + + if (r != 0) { + + ph = mpd_pow10[r]; + + --m; --n; + _mpd_divmod_pow10(&h, &lprev, src[m--], MPD_RDIGITS-r); + if (h != 0) { /* r + msdigits > rdigits <==> h != 0 */ + dest[n--] = h; + } + /* write m-1 shifted words */ + for (; m != MPD_SIZE_MAX; m--,n--) { + _mpd_divmod_pow10(&h, &l, src[m], MPD_RDIGITS-r); + dest[n] = ph * lprev + h; + lprev = l; + } + /* write least significant word */ + dest[q] = ph * lprev; + } + else { + while (--m != MPD_SIZE_MAX) { + dest[m+q] = src[m]; + } + } + + mpd_uint_zero(dest, q); +} + +/* + * Right shift of src by 'shift' digits; src may equal dest. + * Assumption: srcdigits-shift > 0. + * + * dest := area with space for srcdigits-shift digits. + * src := coefficient with length 'slen'. + * + * The case splits in the function rely on the following equations: + * + * Let msdigits denote the number of digits in the most significant + * word of src. Then 1 <= msdigits <= rdigits. + * + * 1) shift = q * rdigits + r + * 2) srcdigits = qsrc * rdigits + msdigits + * 3) destdigits = srcdigits - shift + * = qsrc * rdigits + msdigits - (q * rdigits + r) + * = (qsrc - q) * rdigits + msdigits - r + * + * Since destdigits > 0 and 1 <= msdigits <= rdigits: + * + * 4) qsrc >= q + * 5) qsrc == q ==> msdigits > r + * + * The result has slen-q words if msdigits > r, slen-q-1 words otherwise. + */ +mpd_uint_t +_mpd_baseshiftr(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t slen, + mpd_size_t shift) +{ +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) + /* spurious uninitialized warnings */ + mpd_uint_t l=l, h=h, hprev=hprev; /* low, high, previous high */ +#else + mpd_uint_t l, h, hprev; /* low, high, previous high */ +#endif + mpd_uint_t rnd, rest; /* rounding digit, rest */ + mpd_uint_t q, r; + mpd_size_t i, j; + mpd_uint_t ph; + + assert(slen > 0); + + _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS); + + rnd = rest = 0; + if (r != 0) { + + ph = mpd_pow10[MPD_RDIGITS-r]; + + _mpd_divmod_pow10(&hprev, &rest, src[q], r); + _mpd_divmod_pow10(&rnd, &rest, rest, r-1); + + if (rest == 0 && q > 0) { + rest = !_mpd_isallzero(src, q); + } + /* write slen-q-1 words */ + for (j=0,i=q+1; i 0) { + _mpd_divmod_pow10(&rnd, &rest, src[q-1], MPD_RDIGITS-1); + /* is there any non-zero digit below rnd? */ + if (rest == 0) rest = !_mpd_isallzero(src, q-1); + } + for (j = 0; j < slen-q; j++) { + dest[j] = src[q+j]; + } + } + + /* 0-4 ==> rnd+rest < 0.5 */ + /* 5 ==> rnd+rest == 0.5 */ + /* 6-9 ==> rnd+rest > 0.5 */ + return (rnd == 0 || rnd == 5) ? rnd + !!rest : rnd; +} + + +/*********************************************************************/ +/* Calculations in base b */ +/*********************************************************************/ + +/* + * Add v to w (len m). The calling function has to handle a possible + * final carry. Assumption: m > 0. + */ +mpd_uint_t +_mpd_shortadd_b(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v, mpd_uint_t b) +{ + mpd_uint_t s; + mpd_uint_t carry; + mpd_size_t i; + + assert(m > 0); + + /* add v to w */ + s = w[0] + v; + carry = (s < v) | (s >= b); + w[0] = carry ? s-b : s; + + /* if there is a carry, propagate it */ + for (i = 1; carry && i < m; i++) { + s = w[i] + carry; + carry = (s == b); + w[i] = carry ? 0 : s; + } + + return carry; +} + +/* w := product of u (len n) and v (single word). Return carry. */ +mpd_uint_t +_mpd_shortmul_c(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v) +{ + mpd_uint_t hi, lo; + mpd_uint_t carry = 0; + mpd_size_t i; + + assert(n > 0); + + for (i=0; i < n; i++) { + + _mpd_mul_words(&hi, &lo, u[i], v); + lo = carry + lo; + if (lo < carry) hi++; + + _mpd_div_words_r(&carry, &w[i], hi, lo); + } + + return carry; +} + +/* w := product of u (len n) and v (single word) */ +mpd_uint_t +_mpd_shortmul_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, + mpd_uint_t v, mpd_uint_t b) +{ + mpd_uint_t hi, lo; + mpd_uint_t carry = 0; + mpd_size_t i; + + assert(n > 0); + + for (i=0; i < n; i++) { + + _mpd_mul_words(&hi, &lo, u[i], v); + lo = carry + lo; + if (lo < carry) hi++; + + _mpd_div_words(&carry, &w[i], hi, lo, b); + } + + return carry; +} + +/* + * Knuth, TAOCP Volume 2, 4.3.1, exercise 16: + * w := quotient of u (len n) divided by a single word v + */ +mpd_uint_t +_mpd_shortdiv_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, + mpd_uint_t v, mpd_uint_t b) +{ + mpd_uint_t hi, lo; + mpd_uint_t rem = 0; + mpd_size_t i; + + assert(n > 0); + + for (i=n-1; i != MPD_SIZE_MAX; i--) { + + _mpd_mul_words(&hi, &lo, rem, b); + lo = u[i] + lo; + if (lo < u[i]) hi++; + + _mpd_div_words(&w[i], &rem, hi, lo, v); + } + + return rem; +} + + + diff --git a/third_party/opa/wasm/src/libmpdec/basearith.h b/third_party/opa/wasm/src/libmpdec/basearith.h new file mode 100644 index 000000000000..976358a110ec --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/basearith.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef BASEARITH_H +#define BASEARITH_H + + +#include "mpdecimal.h" +#include +#include "typearith.h" + + +/* Internal header file: all symbols have local scope in the DSO */ +MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) + + +mpd_uint_t _mpd_baseadd(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, + mpd_size_t m, mpd_size_t n); +void _mpd_baseaddto(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n); +mpd_uint_t _mpd_shortadd(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v); +mpd_uint_t _mpd_shortadd_b(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v, + mpd_uint_t b); +mpd_uint_t _mpd_baseincr(mpd_uint_t *u, mpd_size_t n); +void _mpd_basesub(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, + mpd_size_t m, mpd_size_t n); +void _mpd_basesubfrom(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n); +void _mpd_basemul(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, + mpd_size_t m, mpd_size_t n); +void _mpd_shortmul(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, + mpd_uint_t v); +mpd_uint_t _mpd_shortmul_c(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, + mpd_uint_t v); +mpd_uint_t _mpd_shortmul_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, + mpd_uint_t v, mpd_uint_t b); +mpd_uint_t _mpd_shortdiv(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, + mpd_uint_t v); +mpd_uint_t _mpd_shortdiv_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, + mpd_uint_t v, mpd_uint_t b); +int _mpd_basedivmod(mpd_uint_t *q, mpd_uint_t *r, const mpd_uint_t *uconst, + const mpd_uint_t *vconst, mpd_size_t nplusm, mpd_size_t n); +void _mpd_baseshiftl(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t n, + mpd_size_t m, mpd_size_t shift); +mpd_uint_t _mpd_baseshiftr(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t slen, + mpd_size_t shift); + + + +#ifdef CONFIG_64 +extern const mpd_uint_t mprime_rdx; + +/* + * Algorithm from: Division by Invariant Integers using Multiplication, + * T. Granlund and P. L. Montgomery, Proceedings of the SIGPLAN '94 + * Conference on Programming Language Design and Implementation. + * + * http://gmplib.org/~tege/divcnst-pldi94.pdf + * + * Variables from the paper and their translations (See section 8): + * + * N := 64 + * d := MPD_RADIX + * l := 64 + * m' := floor((2**(64+64) - 1)/MPD_RADIX) - 2**64 + * + * Since N-l == 0: + * + * dnorm := d + * n2 := hi + * n10 := lo + * + * ACL2 proof: mpd-div-words-r-correct + */ +static inline void +_mpd_div_words_r(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo) +{ + mpd_uint_t n_adj, h, l, t; + mpd_uint_t n1_neg; + + /* n1_neg = if lo >= 2**63 then MPD_UINT_MAX else 0 */ + n1_neg = (lo & (1ULL<<63)) ? MPD_UINT_MAX : 0; + /* n_adj = if lo >= 2**63 then lo+MPD_RADIX else lo */ + n_adj = lo + (n1_neg & MPD_RADIX); + + /* (h, l) = if lo >= 2**63 then m'*(hi+1) else m'*hi */ + _mpd_mul_words(&h, &l, mprime_rdx, hi-n1_neg); + l = l + n_adj; + if (l < n_adj) h++; + t = h + hi; + /* At this point t == qest, with q == qest or q == qest+1: + * 1) 0 <= 2**64*hi + lo - qest*MPD_RADIX < 2*MPD_RADIX + */ + + /* t = 2**64-1 - qest = 2**64 - (qest+1) */ + t = MPD_UINT_MAX - t; + + /* (h, l) = 2**64*MPD_RADIX - (qest+1)*MPD_RADIX */ + _mpd_mul_words(&h, &l, t, MPD_RADIX); + l = l + lo; + if (l < lo) h++; + h += hi; + h -= MPD_RADIX; + /* (h, l) = 2**64*hi + lo - (qest+1)*MPD_RADIX (mod 2**128) + * Case q == qest+1: + * a) h == 0, l == r + * b) q := h - t == qest+1 + * c) r := l + * Case q == qest: + * a) h == MPD_UINT_MAX, l == 2**64-(MPD_RADIX-r) + * b) q := h - t == qest + * c) r := l + MPD_RADIX = r + */ + + *q = (h - t); + *r = l + (MPD_RADIX & h); +} +#else +static inline void +_mpd_div_words_r(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo) +{ + _mpd_div_words(q, r, hi, lo, MPD_RADIX); +} +#endif + + +/* Multiply two single base MPD_RADIX words, store result in array w[2]. */ +static inline void +_mpd_singlemul(mpd_uint_t w[2], mpd_uint_t u, mpd_uint_t v) +{ + mpd_uint_t hi, lo; + + _mpd_mul_words(&hi, &lo, u, v); + _mpd_div_words_r(&w[1], &w[0], hi, lo); +} + +/* Multiply u (len 2) and v (len m, 1 <= m <= 2). */ +static inline void +_mpd_mul_2_le2(mpd_uint_t w[4], mpd_uint_t u[2], mpd_uint_t v[2], mpd_ssize_t m) +{ + mpd_uint_t hi, lo; + + _mpd_mul_words(&hi, &lo, u[0], v[0]); + _mpd_div_words_r(&w[1], &w[0], hi, lo); + + _mpd_mul_words(&hi, &lo, u[1], v[0]); + lo = w[1] + lo; + if (lo < w[1]) hi++; + _mpd_div_words_r(&w[2], &w[1], hi, lo); + if (m == 1) return; + + _mpd_mul_words(&hi, &lo, u[0], v[1]); + lo = w[1] + lo; + if (lo < w[1]) hi++; + _mpd_div_words_r(&w[3], &w[1], hi, lo); + + _mpd_mul_words(&hi, &lo, u[1], v[1]); + lo = w[2] + lo; + if (lo < w[2]) hi++; + lo = w[3] + lo; + if (lo < w[3]) hi++; + _mpd_div_words_r(&w[3], &w[2], hi, lo); +} + + +/* + * Test if all words from data[len-1] to data[0] are zero. If len is 0, nothing + * is tested and the coefficient is regarded as "all zero". + */ +static inline int +_mpd_isallzero(const mpd_uint_t *data, mpd_ssize_t len) +{ + while (--len >= 0) { + if (data[len] != 0) return 0; + } + return 1; +} + +/* + * Test if all full words from data[len-1] to data[0] are MPD_RADIX-1 + * (all nines). Return true if len == 0. + */ +static inline int +_mpd_isallnine(const mpd_uint_t *data, mpd_ssize_t len) +{ + while (--len >= 0) { + if (data[len] != MPD_RADIX-1) return 0; + } + return 1; +} + + +MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ + + +#endif /* BASEARITH_H */ + + + diff --git a/third_party/opa/wasm/src/libmpdec/bits.h b/third_party/opa/wasm/src/libmpdec/bits.h new file mode 100644 index 000000000000..b5eaa24976ae --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/bits.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef BITS_H +#define BITS_H + + +#include "mpdecimal.h" +#include + + +/* Check if n is a power of 2. */ +static inline int +ispower2(mpd_size_t n) +{ + return n != 0 && (n & (n-1)) == 0; +} + +#if defined(ANSI) +/* + * Return the most significant bit position of n from 0 to 31 (63). + * Assumptions: n != 0. + */ +static inline int +mpd_bsr(mpd_size_t n) +{ + int pos = 0; + mpd_size_t tmp; + +#ifdef CONFIG_64 + tmp = n >> 32; + if (tmp != 0) { n = tmp; pos += 32; } +#endif + tmp = n >> 16; + if (tmp != 0) { n = tmp; pos += 16; } + tmp = n >> 8; + if (tmp != 0) { n = tmp; pos += 8; } + tmp = n >> 4; + if (tmp != 0) { n = tmp; pos += 4; } + tmp = n >> 2; + if (tmp != 0) { n = tmp; pos += 2; } + tmp = n >> 1; + if (tmp != 0) { n = tmp; pos += 1; } + + return pos + (int)n - 1; +} + +/* + * Return the least significant bit position of n from 0 to 31 (63). + * Assumptions: n != 0. + */ +static inline int +mpd_bsf(mpd_size_t n) +{ + int pos; + +#ifdef CONFIG_64 + pos = 63; + if (n & 0x00000000FFFFFFFFULL) { pos -= 32; } else { n >>= 32; } + if (n & 0x000000000000FFFFULL) { pos -= 16; } else { n >>= 16; } + if (n & 0x00000000000000FFULL) { pos -= 8; } else { n >>= 8; } + if (n & 0x000000000000000FULL) { pos -= 4; } else { n >>= 4; } + if (n & 0x0000000000000003ULL) { pos -= 2; } else { n >>= 2; } + if (n & 0x0000000000000001ULL) { pos -= 1; } +#else + pos = 31; + if (n & 0x000000000000FFFFUL) { pos -= 16; } else { n >>= 16; } + if (n & 0x00000000000000FFUL) { pos -= 8; } else { n >>= 8; } + if (n & 0x000000000000000FUL) { pos -= 4; } else { n >>= 4; } + if (n & 0x0000000000000003UL) { pos -= 2; } else { n >>= 2; } + if (n & 0x0000000000000001UL) { pos -= 1; } +#endif + return pos; +} +/* END ANSI */ + +#elif defined(ASM) +/* + * Bit scan reverse. Assumptions: a != 0. + */ +static inline int +mpd_bsr(mpd_size_t a) +{ + mpd_size_t retval; + + __asm__ ( +#ifdef CONFIG_64 + "bsrq %1, %0\n\t" +#else + "bsr %1, %0\n\t" +#endif + :"=r" (retval) + :"r" (a) + :"cc" + ); + + return (int)retval; +} + +/* + * Bit scan forward. Assumptions: a != 0. + */ +static inline int +mpd_bsf(mpd_size_t a) +{ + mpd_size_t retval; + + __asm__ ( +#ifdef CONFIG_64 + "bsfq %1, %0\n\t" +#else + "bsf %1, %0\n\t" +#endif + :"=r" (retval) + :"r" (a) + :"cc" + ); + + return (int)retval; +} +/* END ASM */ + +#elif defined(MASM) +#include +/* + * Bit scan reverse. Assumptions: a != 0. + */ +static inline int __cdecl +mpd_bsr(mpd_size_t a) +{ + unsigned long retval; + +#ifdef CONFIG_64 + _BitScanReverse64(&retval, a); +#else + _BitScanReverse(&retval, a); +#endif + + return (int)retval; +} + +/* + * Bit scan forward. Assumptions: a != 0. + */ +static inline int __cdecl +mpd_bsf(mpd_size_t a) +{ + unsigned long retval; + +#ifdef CONFIG_64 + _BitScanForward64(&retval, a); +#else + _BitScanForward(&retval, a); +#endif + + return (int)retval; +} +/* END MASM (_MSC_VER) */ +#else + #error "missing preprocessor definitions" +#endif /* BSR/BSF */ + + +#endif /* BITS_H */ + + + diff --git a/third_party/opa/wasm/src/libmpdec/constants.c b/third_party/opa/wasm/src/libmpdec/constants.c new file mode 100644 index 000000000000..8eb4ee0da77f --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/constants.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include "constants.h" + + +#if defined(CONFIG_64) + + /* number-theory.c */ + const mpd_uint_t mpd_moduli[3] = { + 18446744069414584321ULL, 18446744056529682433ULL, 18446742974197923841ULL + }; + const mpd_uint_t mpd_roots[3] = {7ULL, 10ULL, 19ULL}; + + /* crt.c */ + const mpd_uint_t INV_P1_MOD_P2 = 18446744055098026669ULL; + const mpd_uint_t INV_P1P2_MOD_P3 = 287064143708160ULL; + const mpd_uint_t LH_P1P2 = 18446744052234715137ULL; /* (P1*P2) % 2^64 */ + const mpd_uint_t UH_P1P2 = 18446744052234715141ULL; /* (P1*P2) / 2^64 */ + + /* transpose.c */ + const mpd_size_t mpd_bits[64] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, + 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, + 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, + 2147483648ULL, 4294967296ULL, 8589934592ULL, 17179869184ULL, 34359738368ULL, + 68719476736ULL, 137438953472ULL, 274877906944ULL, 549755813888ULL, + 1099511627776ULL, 2199023255552ULL, 4398046511104, 8796093022208ULL, + 17592186044416ULL, 35184372088832ULL, 70368744177664ULL, 140737488355328ULL, + 281474976710656ULL, 562949953421312ULL, 1125899906842624ULL, + 2251799813685248ULL, 4503599627370496ULL, 9007199254740992ULL, + 18014398509481984ULL, 36028797018963968ULL, 72057594037927936ULL, + 144115188075855872ULL, 288230376151711744ULL, 576460752303423488ULL, + 1152921504606846976ULL, 2305843009213693952ULL, 4611686018427387904ULL, + 9223372036854775808ULL + }; + + /* mpdecimal.c */ + const mpd_uint_t mpd_pow10[MPD_RDIGITS+1] = { + 1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000, + 10000000000ULL,100000000000ULL,1000000000000ULL,10000000000000ULL, + 100000000000000ULL,1000000000000000ULL,10000000000000000ULL, + 100000000000000000ULL,1000000000000000000ULL,10000000000000000000ULL + }; + + /* magic number for constant division by MPD_RADIX */ + const mpd_uint_t mprime_rdx = 15581492618384294730ULL; + +#elif defined(CONFIG_32) + + /* number-theory.c */ + mpd_uint_t mpd_moduli[3] = {2113929217UL, 2013265921UL, 1811939329UL}; + mpd_uint_t mpd_roots[3] = {5UL, 31UL, 13UL}; + + /* PentiumPro modular multiplication: These constants have to be loaded as + * 80 bit long doubles, which are not supported by certain compilers. */ + uint32_t mpd_invmoduli[3][3] = { + {4293885170U, 2181570688U, 16352U}, /* ((long double) 1 / 2113929217UL) */ + {1698898177U, 2290649223U, 16352U}, /* ((long double) 1 / 2013265921UL) */ + {2716021846U, 2545165803U, 16352U} /* ((long double) 1 / 1811939329UL) */ + }; + + float MPD_TWO63 = 9223372036854775808.0; /* 2^63 */ + + /* crt.c */ + mpd_uint_t INV_P1_MOD_P2 = 2013265901UL; + mpd_uint_t INV_P1P2_MOD_P3 = 54UL; + mpd_uint_t LH_P1P2 = 4127195137UL; /* (P1*P2) % 2^32 */ + mpd_uint_t UH_P1P2 = 990904320UL; /* (P1*P2) / 2^32 */ + + /* transpose.c */ + mpd_size_t mpd_bits[32] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, + 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, + 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, + 2147483648UL + }; + + /* mpdecimal.c */ + mpd_uint_t mpd_pow10[MPD_RDIGITS+1] = { + 1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000 + }; + +#else + #error "CONFIG_64 or CONFIG_32 must be defined." +#endif + +const char *mpd_round_string[MPD_ROUND_GUARD] = { + "ROUND_UP", /* round away from 0 */ + "ROUND_DOWN", /* round toward 0 (truncate) */ + "ROUND_CEILING", /* round toward +infinity */ + "ROUND_FLOOR", /* round toward -infinity */ + "ROUND_HALF_UP", /* 0.5 is rounded up */ + "ROUND_HALF_DOWN", /* 0.5 is rounded down */ + "ROUND_HALF_EVEN", /* 0.5 is rounded to even */ + "ROUND_05UP", /* round zero or five away from 0 */ + "ROUND_TRUNC", /* truncate, but set infinity */ +}; + +const char *mpd_clamp_string[MPD_CLAMP_GUARD] = { + "CLAMP_DEFAULT", + "CLAMP_IEEE_754" +}; + + diff --git a/third_party/opa/wasm/src/libmpdec/constants.h b/third_party/opa/wasm/src/libmpdec/constants.h new file mode 100644 index 000000000000..2034bb45d621 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/constants.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef CONSTANTS_H +#define CONSTANTS_H + + +#include "mpdecimal.h" + + +/* Internal header file: all symbols have local scope in the DSO */ +MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) + + +/* choice of optimized functions */ +#if defined(CONFIG_64) +/* x64 */ + #define MULMOD(a, b) x64_mulmod(a, b, umod) + #define MULMOD2C(a0, a1, w) x64_mulmod2c(a0, a1, w, umod) + #define MULMOD2(a0, b0, a1, b1) x64_mulmod2(a0, b0, a1, b1, umod) + #define POWMOD(base, exp) x64_powmod(base, exp, umod) + #define SETMODULUS(modnum) std_setmodulus(modnum, &umod) + #define SIZE3_NTT(x0, x1, x2, w3table) std_size3_ntt(x0, x1, x2, w3table, umod) +#elif defined(PPRO) +/* PentiumPro (or later) gcc inline asm */ + #define MULMOD(a, b) ppro_mulmod(a, b, &dmod, dinvmod) + #define MULMOD2C(a0, a1, w) ppro_mulmod2c(a0, a1, w, &dmod, dinvmod) + #define MULMOD2(a0, b0, a1, b1) ppro_mulmod2(a0, b0, a1, b1, &dmod, dinvmod) + #define POWMOD(base, exp) ppro_powmod(base, exp, &dmod, dinvmod) + #define SETMODULUS(modnum) ppro_setmodulus(modnum, &umod, &dmod, dinvmod) + #define SIZE3_NTT(x0, x1, x2, w3table) ppro_size3_ntt(x0, x1, x2, w3table, umod, &dmod, dinvmod) +#else + /* ANSI C99 */ + #define MULMOD(a, b) std_mulmod(a, b, umod) + #define MULMOD2C(a0, a1, w) std_mulmod2c(a0, a1, w, umod) + #define MULMOD2(a0, b0, a1, b1) std_mulmod2(a0, b0, a1, b1, umod) + #define POWMOD(base, exp) std_powmod(base, exp, umod) + #define SETMODULUS(modnum) std_setmodulus(modnum, &umod) + #define SIZE3_NTT(x0, x1, x2, w3table) std_size3_ntt(x0, x1, x2, w3table, umod) +#endif + +/* PentiumPro (or later) gcc inline asm */ +extern float MPD_TWO63; +extern uint32_t mpd_invmoduli[3][3]; + +enum {P1, P2, P3}; + +extern mpd_uint_t mpd_moduli[]; +extern mpd_uint_t mpd_roots[]; +extern mpd_size_t mpd_bits[]; +extern mpd_uint_t mpd_pow10[]; + +extern mpd_uint_t INV_P1_MOD_P2; +extern mpd_uint_t INV_P1P2_MOD_P3; +extern mpd_uint_t LH_P1P2; +extern mpd_uint_t UH_P1P2; + + +MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ + + +#endif /* CONSTANTS_H */ + + + diff --git a/third_party/opa/wasm/src/libmpdec/context.c b/third_party/opa/wasm/src/libmpdec/context.c new file mode 100644 index 000000000000..24c7b890c1d9 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/context.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include +#include + + +void +mpd_dflt_traphandler(mpd_context_t *ctx UNUSED) +{ + raise(SIGFPE); +} + +void (* mpd_traphandler)(mpd_context_t *) = mpd_dflt_traphandler; + + +/* Set guaranteed minimum number of coefficient words. The function may + be used once at program start. Setting MPD_MINALLOC to out-of-bounds + values is a catastrophic error, so in that case the function exits rather + than relying on the user to check a return value. */ +void +mpd_setminalloc(mpd_ssize_t n) +{ + static int minalloc_is_set = 0; + + if (minalloc_is_set) { + mpd_err_warn("mpd_setminalloc: ignoring request to set " + "MPD_MINALLOC a second time\n"); + return; + } + if (n < MPD_MINALLOC_MIN || n > MPD_MINALLOC_MAX) { + mpd_err_fatal("illegal value for MPD_MINALLOC"); /* GCOV_NOT_REACHED */ + } + MPD_MINALLOC = n; + minalloc_is_set = 1; +} + +void +mpd_init(mpd_context_t *ctx, mpd_ssize_t prec) +{ + mpd_ssize_t ideal_minalloc; + + mpd_defaultcontext(ctx); + + if (!mpd_qsetprec(ctx, prec)) { + mpd_addstatus_raise(ctx, MPD_Invalid_context); + return; + } + + ideal_minalloc = 2 * ((prec+MPD_RDIGITS-1) / MPD_RDIGITS); + if (ideal_minalloc < MPD_MINALLOC_MIN) ideal_minalloc = MPD_MINALLOC_MIN; + if (ideal_minalloc > MPD_MINALLOC_MAX) ideal_minalloc = MPD_MINALLOC_MAX; + + mpd_setminalloc(ideal_minalloc); +} + +void +mpd_maxcontext(mpd_context_t *ctx) +{ + ctx->prec=MPD_MAX_PREC; + ctx->emax=MPD_MAX_EMAX; + ctx->emin=MPD_MIN_EMIN; + ctx->round=MPD_ROUND_HALF_EVEN; + ctx->traps=MPD_Traps; + ctx->status=0; + ctx->newtrap=0; + ctx->clamp=0; + ctx->allcr=1; +} + +void +mpd_defaultcontext(mpd_context_t *ctx) +{ + ctx->prec=2*MPD_RDIGITS; + ctx->emax=MPD_MAX_EMAX; + ctx->emin=MPD_MIN_EMIN; + ctx->round=MPD_ROUND_HALF_UP; + ctx->traps=MPD_Traps; + ctx->status=0; + ctx->newtrap=0; + ctx->clamp=0; + ctx->allcr=1; +} + +void +mpd_basiccontext(mpd_context_t *ctx) +{ + ctx->prec=9; + ctx->emax=MPD_MAX_EMAX; + ctx->emin=MPD_MIN_EMIN; + ctx->round=MPD_ROUND_HALF_UP; + ctx->traps=MPD_Traps|MPD_Clamped; + ctx->status=0; + ctx->newtrap=0; + ctx->clamp=0; + ctx->allcr=1; +} + +int +mpd_ieee_context(mpd_context_t *ctx, int bits) +{ + if (bits <= 0 || bits > MPD_IEEE_CONTEXT_MAX_BITS || bits % 32) { + return -1; + } + + ctx->prec = 9 * (bits/32) - 2; + ctx->emax = 3 * ((mpd_ssize_t)1<<(bits/16+3)); + ctx->emin = 1 - ctx->emax; + ctx->round=MPD_ROUND_HALF_EVEN; + ctx->traps=0; + ctx->status=0; + ctx->newtrap=0; + ctx->clamp=1; + ctx->allcr=1; + + return 0; +} + +mpd_ssize_t +mpd_getprec(const mpd_context_t *ctx) +{ + return ctx->prec; +} + +mpd_ssize_t +mpd_getemax(const mpd_context_t *ctx) +{ + return ctx->emax; +} + +mpd_ssize_t +mpd_getemin(const mpd_context_t *ctx) +{ + return ctx->emin; +} + +int +mpd_getround(const mpd_context_t *ctx) +{ + return ctx->round; +} + +uint32_t +mpd_gettraps(const mpd_context_t *ctx) +{ + return ctx->traps; +} + +uint32_t +mpd_getstatus(const mpd_context_t *ctx) +{ + return ctx->status; +} + +int +mpd_getclamp(const mpd_context_t *ctx) +{ + return ctx->clamp; +} + +int +mpd_getcr(const mpd_context_t *ctx) +{ + return ctx->allcr; +} + + +int +mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec) +{ + if (prec <= 0 || prec > MPD_MAX_PREC) { + return 0; + } + ctx->prec = prec; + return 1; +} + +int +mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax) +{ + if (emax < 0 || emax > MPD_MAX_EMAX) { + return 0; + } + ctx->emax = emax; + return 1; +} + +int +mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin) +{ + if (emin > 0 || emin < MPD_MIN_EMIN) { + return 0; + } + ctx->emin = emin; + return 1; +} + +int +mpd_qsetround(mpd_context_t *ctx, int round) +{ + if (!(0 <= round && round < MPD_ROUND_GUARD)) { + return 0; + } + ctx->round = round; + return 1; +} + +int +mpd_qsettraps(mpd_context_t *ctx, uint32_t traps) +{ + if (traps > MPD_Max_status) { + return 0; + } + ctx->traps = traps; + return 1; +} + +int +mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags) +{ + if (flags > MPD_Max_status) { + return 0; + } + ctx->status = flags; + return 1; +} + +int +mpd_qsetclamp(mpd_context_t *ctx, int c) +{ + if (c != 0 && c != 1) { + return 0; + } + ctx->clamp = c; + return 1; +} + +int +mpd_qsetcr(mpd_context_t *ctx, int c) +{ + if (c != 0 && c != 1) { + return 0; + } + ctx->allcr = c; + return 1; +} + + +void +mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags) +{ + ctx->status |= flags; + if (flags&ctx->traps) { + ctx->newtrap = (flags&ctx->traps); + mpd_traphandler(ctx); + } +} + + diff --git a/third_party/opa/wasm/src/libmpdec/convolute.c b/third_party/opa/wasm/src/libmpdec/convolute.c new file mode 100644 index 000000000000..4c62e8bd3abd --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/convolute.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include "bits.h" +#include "constants.h" +#include "fnt.h" +#include "fourstep.h" +#include "numbertheory.h" +#include "sixstep.h" +#include "umodarith.h" +#include "convolute.h" + + +/* Bignum: Fast convolution using the Number Theoretic Transform. Used for + the multiplication of very large coefficients. */ + + +/* Convolute the data in c1 and c2. Result is in c1. */ +int +fnt_convolute(mpd_uint_t *c1, mpd_uint_t *c2, mpd_size_t n, int modnum) +{ + int (*fnt)(mpd_uint_t *, mpd_size_t, int); + int (*inv_fnt)(mpd_uint_t *, mpd_size_t, int); +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t n_inv, umod; + mpd_size_t i; + + + SETMODULUS(modnum); + n_inv = POWMOD(n, (umod-2)); + + if (ispower2(n)) { + if (n > SIX_STEP_THRESHOLD) { + fnt = six_step_fnt; + inv_fnt = inv_six_step_fnt; + } + else { + fnt = std_fnt; + inv_fnt = std_inv_fnt; + } + } + else { + fnt = four_step_fnt; + inv_fnt = inv_four_step_fnt; + } + + if (!fnt(c1, n, modnum)) { + return 0; + } + if (!fnt(c2, n, modnum)) { + return 0; + } + for (i = 0; i < n-1; i += 2) { + mpd_uint_t x0 = c1[i]; + mpd_uint_t y0 = c2[i]; + mpd_uint_t x1 = c1[i+1]; + mpd_uint_t y1 = c2[i+1]; + MULMOD2(&x0, y0, &x1, y1); + c1[i] = x0; + c1[i+1] = x1; + } + + if (!inv_fnt(c1, n, modnum)) { + return 0; + } + for (i = 0; i < n-3; i += 4) { + mpd_uint_t x0 = c1[i]; + mpd_uint_t x1 = c1[i+1]; + mpd_uint_t x2 = c1[i+2]; + mpd_uint_t x3 = c1[i+3]; + MULMOD2C(&x0, &x1, n_inv); + MULMOD2C(&x2, &x3, n_inv); + c1[i] = x0; + c1[i+1] = x1; + c1[i+2] = x2; + c1[i+3] = x3; + } + + return 1; +} + +/* Autoconvolute the data in c1. Result is in c1. */ +int +fnt_autoconvolute(mpd_uint_t *c1, mpd_size_t n, int modnum) +{ + int (*fnt)(mpd_uint_t *, mpd_size_t, int); + int (*inv_fnt)(mpd_uint_t *, mpd_size_t, int); +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t n_inv, umod; + mpd_size_t i; + + + SETMODULUS(modnum); + n_inv = POWMOD(n, (umod-2)); + + if (ispower2(n)) { + if (n > SIX_STEP_THRESHOLD) { + fnt = six_step_fnt; + inv_fnt = inv_six_step_fnt; + } + else { + fnt = std_fnt; + inv_fnt = std_inv_fnt; + } + } + else { + fnt = four_step_fnt; + inv_fnt = inv_four_step_fnt; + } + + if (!fnt(c1, n, modnum)) { + return 0; + } + for (i = 0; i < n-1; i += 2) { + mpd_uint_t x0 = c1[i]; + mpd_uint_t x1 = c1[i+1]; + MULMOD2(&x0, x0, &x1, x1); + c1[i] = x0; + c1[i+1] = x1; + } + + if (!inv_fnt(c1, n, modnum)) { + return 0; + } + for (i = 0; i < n-3; i += 4) { + mpd_uint_t x0 = c1[i]; + mpd_uint_t x1 = c1[i+1]; + mpd_uint_t x2 = c1[i+2]; + mpd_uint_t x3 = c1[i+3]; + MULMOD2C(&x0, &x1, n_inv); + MULMOD2C(&x2, &x3, n_inv); + c1[i] = x0; + c1[i+1] = x1; + c1[i+2] = x2; + c1[i+3] = x3; + } + + return 1; +} + + diff --git a/third_party/opa/wasm/src/libmpdec/convolute.h b/third_party/opa/wasm/src/libmpdec/convolute.h new file mode 100644 index 000000000000..f30a177a6840 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/convolute.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef CONVOLUTE_H +#define CONVOLUTE_H + + +#include "mpdecimal.h" +#include + + +/* Internal header file: all symbols have local scope in the DSO */ +MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) + + +#define SIX_STEP_THRESHOLD 4096 + +int fnt_convolute(mpd_uint_t *c1, mpd_uint_t *c2, mpd_size_t n, int modnum); +int fnt_autoconvolute(mpd_uint_t *c1, mpd_size_t n, int modnum); + + +MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ + + +#endif diff --git a/third_party/opa/wasm/src/libmpdec/crt.c b/third_party/opa/wasm/src/libmpdec/crt.c new file mode 100644 index 000000000000..4a1e80a23228 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/crt.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include +#include "numbertheory.h" +#include "umodarith.h" +#include "crt.h" + + +/* Bignum: Chinese Remainder Theorem, extends the maximum transform length. */ + + +/* Multiply P1P2 by v, store result in w. */ +static inline void +_crt_mulP1P2_3(mpd_uint_t w[3], mpd_uint_t v) +{ + mpd_uint_t hi1, hi2, lo; + + _mpd_mul_words(&hi1, &lo, LH_P1P2, v); + w[0] = lo; + + _mpd_mul_words(&hi2, &lo, UH_P1P2, v); + lo = hi1 + lo; + if (lo < hi1) hi2++; + + w[1] = lo; + w[2] = hi2; +} + +/* Add 3 words from v to w. The result is known to fit in w. */ +static inline void +_crt_add3(mpd_uint_t w[3], mpd_uint_t v[3]) +{ + mpd_uint_t carry; + mpd_uint_t s; + + s = w[0] + v[0]; + carry = (s < w[0]); + w[0] = s; + + s = w[1] + (v[1] + carry); + carry = (s < w[1]); + w[1] = s; + + w[2] = w[2] + (v[2] + carry); +} + +/* Divide 3 words in u by v, store result in w, return remainder. */ +static inline mpd_uint_t +_crt_div3(mpd_uint_t *w, const mpd_uint_t *u, mpd_uint_t v) +{ + mpd_uint_t r1 = u[2]; + mpd_uint_t r2; + + if (r1 < v) { + w[2] = 0; + } + else { + _mpd_div_word(&w[2], &r1, u[2], v); /* GCOV_NOT_REACHED */ + } + + _mpd_div_words(&w[1], &r2, r1, u[1], v); + _mpd_div_words(&w[0], &r1, r2, u[0], v); + + return r1; +} + + +/* + * Chinese Remainder Theorem: + * Algorithm from Joerg Arndt, "Matters Computational", + * Chapter 37.4.1 [http://www.jjj.de/fxt/] + * + * See also Knuth, TAOCP, Volume 2, 4.3.2, exercise 7. + */ + +/* + * CRT with carry: x1, x2, x3 contain numbers modulo p1, p2, p3. For each + * triple of members of the arrays, find the unique z modulo p1*p2*p3, with + * zmax = p1*p2*p3 - 1. + * + * In each iteration of the loop, split z into result[i] = z % MPD_RADIX + * and carry = z / MPD_RADIX. Let N be the size of carry[] and cmax the + * maximum carry. + * + * Limits for the 32-bit build: + * + * N = 2**96 + * cmax = 7711435591312380274 + * + * Limits for the 64 bit build: + * + * N = 2**192 + * cmax = 627710135393475385904124401220046371710 + * + * The following statements hold for both versions: + * + * 1) cmax + zmax < N, so the addition does not overflow. + * + * 2) (cmax + zmax) / MPD_RADIX == cmax. + * + * 3) If c <= cmax, then c_next = (c + zmax) / MPD_RADIX <= cmax. + */ +void +crt3(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_size_t rsize) +{ + mpd_uint_t p1 = mpd_moduli[P1]; + mpd_uint_t umod; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t a1, a2, a3; + mpd_uint_t s; + mpd_uint_t z[3], t[3]; + mpd_uint_t carry[3] = {0,0,0}; + mpd_uint_t hi, lo; + mpd_size_t i; + + for (i = 0; i < rsize; i++) { + + a1 = x1[i]; + a2 = x2[i]; + a3 = x3[i]; + + SETMODULUS(P2); + s = ext_submod(a2, a1, umod); + s = MULMOD(s, INV_P1_MOD_P2); + + _mpd_mul_words(&hi, &lo, s, p1); + lo = lo + a1; + if (lo < a1) hi++; + + SETMODULUS(P3); + s = dw_submod(a3, hi, lo, umod); + s = MULMOD(s, INV_P1P2_MOD_P3); + + z[0] = lo; + z[1] = hi; + z[2] = 0; + + _crt_mulP1P2_3(t, s); + _crt_add3(z, t); + _crt_add3(carry, z); + + x1[i] = _crt_div3(carry, carry, MPD_RADIX); + } + + assert(carry[0] == 0 && carry[1] == 0 && carry[2] == 0); +} + + diff --git a/third_party/opa/wasm/src/libmpdec/crt.h b/third_party/opa/wasm/src/libmpdec/crt.h new file mode 100644 index 000000000000..f61e77293632 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/crt.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef CRT_H +#define CRT_H + + +#include "mpdecimal.h" +#include + + +/* Internal header file: all symbols have local scope in the DSO */ +MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) + + +void crt3(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_size_t nmemb); + + +MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ + + +#endif diff --git a/third_party/opa/wasm/src/libmpdec/difradix2.c b/third_party/opa/wasm/src/libmpdec/difradix2.c new file mode 100644 index 000000000000..06e5ab5e222e --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/difradix2.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include +#include "bits.h" +#include "numbertheory.h" +#include "umodarith.h" +#include "difradix2.h" + + +/* Bignum: The actual transform routine (decimation in frequency). */ + + +/* + * Generate index pairs (x, bitreverse(x)) and carry out the permutation. + * n must be a power of two. + * Algorithm due to Brent/Lehmann, see Joerg Arndt, "Matters Computational", + * Chapter 1.14.4. [http://www.jjj.de/fxt/] + */ +static inline void +bitreverse_permute(mpd_uint_t a[], mpd_size_t n) +{ + mpd_size_t x = 0; + mpd_size_t r = 0; + mpd_uint_t t; + + do { /* Invariant: r = bitreverse(x) */ + if (r > x) { + t = a[x]; + a[x] = a[r]; + a[r] = t; + } + /* Flip trailing consecutive 1 bits and the first zero bit + * that absorbs a possible carry. */ + x += 1; + /* Mirror the operation on r: Flip n_trailing_zeros(x)+1 + high bits of r. */ + r ^= (n - (n >> (mpd_bsf(x)+1))); + /* The loop invariant is preserved. */ + } while (x < n); +} + + +/* Fast Number Theoretic Transform, decimation in frequency. */ +void +fnt_dif2(mpd_uint_t a[], mpd_size_t n, struct fnt_params *tparams) +{ + mpd_uint_t *wtable = tparams->wtable; + mpd_uint_t umod; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t u0, u1, v0, v1; + mpd_uint_t w, w0, w1, wstep; + mpd_size_t m, mhalf; + mpd_size_t j, r; + + + assert(ispower2(n)); + assert(n >= 4); + + SETMODULUS(tparams->modnum); + + /* m == n */ + mhalf = n / 2; + for (j = 0; j < mhalf; j += 2) { + + w0 = wtable[j]; + w1 = wtable[j+1]; + + u0 = a[j]; + v0 = a[j+mhalf]; + + u1 = a[j+1]; + v1 = a[j+1+mhalf]; + + a[j] = addmod(u0, v0, umod); + v0 = submod(u0, v0, umod); + + a[j+1] = addmod(u1, v1, umod); + v1 = submod(u1, v1, umod); + + MULMOD2(&v0, w0, &v1, w1); + + a[j+mhalf] = v0; + a[j+1+mhalf] = v1; + + } + + wstep = 2; + for (m = n/2; m >= 2; m>>=1, wstep<<=1) { + + mhalf = m / 2; + + /* j == 0 */ + for (r = 0; r < n; r += 2*m) { + + u0 = a[r]; + v0 = a[r+mhalf]; + + u1 = a[m+r]; + v1 = a[m+r+mhalf]; + + a[r] = addmod(u0, v0, umod); + v0 = submod(u0, v0, umod); + + a[m+r] = addmod(u1, v1, umod); + v1 = submod(u1, v1, umod); + + a[r+mhalf] = v0; + a[m+r+mhalf] = v1; + } + + for (j = 1; j < mhalf; j++) { + + w = wtable[j*wstep]; + + for (r = 0; r < n; r += 2*m) { + + u0 = a[r+j]; + v0 = a[r+j+mhalf]; + + u1 = a[m+r+j]; + v1 = a[m+r+j+mhalf]; + + a[r+j] = addmod(u0, v0, umod); + v0 = submod(u0, v0, umod); + + a[m+r+j] = addmod(u1, v1, umod); + v1 = submod(u1, v1, umod); + + MULMOD2C(&v0, &v1, w); + + a[r+j+mhalf] = v0; + a[m+r+j+mhalf] = v1; + } + + } + + } + + bitreverse_permute(a, n); +} + + diff --git a/third_party/opa/wasm/src/libmpdec/difradix2.h b/third_party/opa/wasm/src/libmpdec/difradix2.h new file mode 100644 index 000000000000..5e22bcf324fa --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/difradix2.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef DIF_RADIX2_H +#define DIF_RADIX2_H + + +#include "mpdecimal.h" +#include +#include "numbertheory.h" + + +/* Internal header file: all symbols have local scope in the DSO */ +MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) + + +void fnt_dif2(mpd_uint_t a[], mpd_size_t n, struct fnt_params *tparams); + + +MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ + + +#endif diff --git a/third_party/opa/wasm/src/libmpdec/fnt.c b/third_party/opa/wasm/src/libmpdec/fnt.c new file mode 100644 index 000000000000..7e924c85242b --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/fnt.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include +#include +#include "bits.h" +#include "difradix2.h" +#include "numbertheory.h" +#include "fnt.h" + + +/* Bignum: Fast transform for medium-sized coefficients. */ + + +/* forward transform, sign = -1 */ +int +std_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) +{ + struct fnt_params *tparams; + + assert(ispower2(n)); + assert(n >= 4); + assert(n <= 3*MPD_MAXTRANSFORM_2N); + + if ((tparams = _mpd_init_fnt_params(n, -1, modnum)) == NULL) { + return 0; + } + fnt_dif2(a, n, tparams); + + mpd_free(tparams); + return 1; +} + +/* reverse transform, sign = 1 */ +int +std_inv_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) +{ + struct fnt_params *tparams; + + assert(ispower2(n)); + assert(n >= 4); + assert(n <= 3*MPD_MAXTRANSFORM_2N); + + if ((tparams = _mpd_init_fnt_params(n, 1, modnum)) == NULL) { + return 0; + } + fnt_dif2(a, n, tparams); + + mpd_free(tparams); + return 1; +} + + + diff --git a/third_party/opa/wasm/src/libmpdec/fnt.h b/third_party/opa/wasm/src/libmpdec/fnt.h new file mode 100644 index 000000000000..fa2154a798d4 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/fnt.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef FNT_H +#define FNT_H + + +#include "mpdecimal.h" +#include + + +/* Internal header file: all symbols have local scope in the DSO */ +MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) + + +int std_fnt(mpd_uint_t a[], mpd_size_t n, int modnum); +int std_inv_fnt(mpd_uint_t a[], mpd_size_t n, int modnum); + + +MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ + + +#endif + diff --git a/third_party/opa/wasm/src/libmpdec/fourstep.c b/third_party/opa/wasm/src/libmpdec/fourstep.c new file mode 100644 index 000000000000..21d3e7485df4 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/fourstep.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include "numbertheory.h" +#include "sixstep.h" +#include "transpose.h" +#include "umodarith.h" +#include "fourstep.h" + + +/* Bignum: Cache efficient Matrix Fourier Transform for arrays of the + form 3 * 2**n (See literature/matrix-transform.txt). */ + + +#ifndef PPRO +static inline void +std_size3_ntt(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, + mpd_uint_t w3table[3], mpd_uint_t umod) +{ + mpd_uint_t r1, r2; + mpd_uint_t w; + mpd_uint_t s, tmp; + + + /* k = 0 -> w = 1 */ + s = *x1; + s = addmod(s, *x2, umod); + s = addmod(s, *x3, umod); + + r1 = s; + + /* k = 1 */ + s = *x1; + + w = w3table[1]; + tmp = MULMOD(*x2, w); + s = addmod(s, tmp, umod); + + w = w3table[2]; + tmp = MULMOD(*x3, w); + s = addmod(s, tmp, umod); + + r2 = s; + + /* k = 2 */ + s = *x1; + + w = w3table[2]; + tmp = MULMOD(*x2, w); + s = addmod(s, tmp, umod); + + w = w3table[1]; + tmp = MULMOD(*x3, w); + s = addmod(s, tmp, umod); + + *x3 = s; + *x2 = r2; + *x1 = r1; +} +#else /* PPRO */ +static inline void +ppro_size3_ntt(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_uint_t w3table[3], + mpd_uint_t umod, double *dmod, uint32_t dinvmod[3]) +{ + mpd_uint_t r1, r2; + mpd_uint_t w; + mpd_uint_t s, tmp; + + + /* k = 0 -> w = 1 */ + s = *x1; + s = addmod(s, *x2, umod); + s = addmod(s, *x3, umod); + + r1 = s; + + /* k = 1 */ + s = *x1; + + w = w3table[1]; + tmp = ppro_mulmod(*x2, w, dmod, dinvmod); + s = addmod(s, tmp, umod); + + w = w3table[2]; + tmp = ppro_mulmod(*x3, w, dmod, dinvmod); + s = addmod(s, tmp, umod); + + r2 = s; + + /* k = 2 */ + s = *x1; + + w = w3table[2]; + tmp = ppro_mulmod(*x2, w, dmod, dinvmod); + s = addmod(s, tmp, umod); + + w = w3table[1]; + tmp = ppro_mulmod(*x3, w, dmod, dinvmod); + s = addmod(s, tmp, umod); + + *x3 = s; + *x2 = r2; + *x1 = r1; +} +#endif + + +/* forward transform, sign = -1; transform length = 3 * 2**n */ +int +four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) +{ + mpd_size_t R = 3; /* number of rows */ + mpd_size_t C = n / 3; /* number of columns */ + mpd_uint_t w3table[3]; + mpd_uint_t kernel, w0, w1, wstep; + mpd_uint_t *s, *p0, *p1, *p2; + mpd_uint_t umod; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_size_t i, k; + + + assert(n >= 48); + assert(n <= 3*MPD_MAXTRANSFORM_2N); + + + /* Length R transform on the columns. */ + SETMODULUS(modnum); + _mpd_init_w3table(w3table, -1, modnum); + for (p0=a, p1=p0+C, p2=p0+2*C; p0= 48); + assert(n <= 3*MPD_MAXTRANSFORM_2N); + + +#if 0 + /* An unordered transform is sufficient for convolution. */ + /* Transpose the matrix, producing an R*C matrix. */ + transpose_3xpow2(a, C, R); +#endif + + /* Length C transform on the rows. */ + for (s = a; s < a+n; s += C) { + if (!inv_six_step_fnt(s, C, modnum)) { + return 0; + } + } + + /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */ + SETMODULUS(modnum); + kernel = _mpd_getkernel(n, 1, modnum); + for (i = 1; i < R; i++) { + w0 = 1; + w1 = POWMOD(kernel, i); + wstep = MULMOD(w1, w1); + for (k = 0; k < C; k += 2) { + mpd_uint_t x0 = a[i*C+k]; + mpd_uint_t x1 = a[i*C+k+1]; + MULMOD2(&x0, w0, &x1, w1); + MULMOD2C(&w0, &w1, wstep); + a[i*C+k] = x0; + a[i*C+k+1] = x1; + } + } + + /* Length R transform on the columns. */ + _mpd_init_w3table(w3table, 1, modnum); + for (p0=a, p1=p0+C, p2=p0+2*C; p0 + + +/* Internal header file: all symbols have local scope in the DSO */ +MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) + + +int four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum); +int inv_four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum); + + +MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ + + +#endif diff --git a/third_party/opa/wasm/src/libmpdec/io.c b/third_party/opa/wasm/src/libmpdec/io.c new file mode 100644 index 000000000000..a45a429dbf1d --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/io.c @@ -0,0 +1,1578 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "bits.h" +#include "constants.h" +#include "memory.h" +#include "typearith.h" +#include "io.h" + + +/* This file contains functions for decimal <-> string conversions, including + PEP-3101 formatting for numeric types. */ + + +/* + * Work around the behavior of tolower() and strcasecmp() in certain + * locales. For example, in tr_TR.utf8: + * + * tolower((unsigned char)'I') == 'I' + * + * u is the exact uppercase version of l; n is strlen(l) or strlen(l)+1 + */ +static inline int +_mpd_strneq(const char *s, const char *l, const char *u, size_t n) +{ + while (--n != SIZE_MAX) { + if (*s != *l && *s != *u) { + return 0; + } + s++; u++; l++; + } + + return 1; +} + +static mpd_ssize_t +strtoexp(const char *s) +{ + char *end; + mpd_ssize_t retval; + + errno = 0; + retval = mpd_strtossize(s, &end, 10); + if (errno == 0 && !(*s != '\0' && *end == '\0')) + errno = EINVAL; + + return retval; +} + +/* + * Scan 'len' words. The most significant word contains 'r' digits, + * the remaining words are full words. Skip dpoint. The string 's' must + * consist of digits and an optional single decimal point at 'dpoint'. + */ +static void +string_to_coeff(mpd_uint_t *data, const char *s, const char *dpoint, int r, + size_t len) +{ + int j; + + if (r > 0) { + data[--len] = 0; + for (j = 0; j < r; j++, s++) { + if (s == dpoint) s++; + data[len] = 10 * data[len] + (*s - '0'); + } + } + + while (--len != SIZE_MAX) { + data[len] = 0; + for (j = 0; j < MPD_RDIGITS; j++, s++) { + if (s == dpoint) s++; + data[len] = 10 * data[len] + (*s - '0'); + } + } +} + +/* + * Partially verify a numeric string of the form: + * + * [cdigits][.][cdigits][eE][+-][edigits] + * + * If successful, return a pointer to the location of the first + * relevant coefficient digit. This digit is either non-zero or + * part of one of the following patterns: + * + * ["0\x00", "0.\x00", "0.E", "0.e", "0E", "0e"] + * + * The locations of a single optional dot or indicator are stored + * in 'dpoint' and 'exp'. + * + * The end of the string is stored in 'end'. If an indicator [eE] + * occurs without trailing [edigits], the condition is caught + * later by strtoexp(). + */ +static const char * +scan_dpoint_exp(const char *s, const char **dpoint, const char **exp, + const char **end) +{ + const char *coeff = NULL; + + *dpoint = NULL; + *exp = NULL; + for (; *s != '\0'; s++) { + switch (*s) { + case '.': + if (*dpoint != NULL || *exp != NULL) + return NULL; + *dpoint = s; + break; + case 'E': case 'e': + if (*exp != NULL) + return NULL; + *exp = s; + if (*(s+1) == '+' || *(s+1) == '-') + s++; + break; + default: + if (!isdigit((uchar)*s)) + return NULL; + if (coeff == NULL && *exp == NULL) { + if (*s == '0') { + if (!isdigit((uchar)*(s+1))) + if (!(*(s+1) == '.' && + isdigit((uchar)*(s+2)))) + coeff = s; + } + else { + coeff = s; + } + } + break; + + } + } + + *end = s; + return coeff; +} + +/* scan the payload of a NaN */ +static const char * +scan_payload(const char *s, const char **end) +{ + const char *coeff; + + while (*s == '0') + s++; + coeff = s; + + while (isdigit((uchar)*s)) + s++; + *end = s; + + return (*s == '\0') ? coeff : NULL; +} + +/* convert a character string to a decimal */ +void +mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_ssize_t q, r, len; + const char *coeff, *end; + const char *dpoint = NULL, *exp = NULL; + size_t digits; + uint8_t sign = MPD_POS; + + mpd_set_flags(dec, 0); + dec->len = 0; + dec->exp = 0; + + /* sign */ + if (*s == '+') { + s++; + } + else if (*s == '-') { + mpd_set_negative(dec); + sign = MPD_NEG; + s++; + } + + if (_mpd_strneq(s, "nan", "NAN", 3)) { /* NaN */ + s += 3; + mpd_setspecial(dec, sign, MPD_NAN); + if (*s == '\0') + return; + /* validate payload: digits only */ + if ((coeff = scan_payload(s, &end)) == NULL) + goto conversion_error; + /* payload consists entirely of zeros */ + if (*coeff == '\0') + return; + digits = end - coeff; + /* prec >= 1, clamp is 0 or 1 */ + if (digits > (size_t)(ctx->prec-ctx->clamp)) + goto conversion_error; + } /* sNaN */ + else if (_mpd_strneq(s, "snan", "SNAN", 4)) { + s += 4; + mpd_setspecial(dec, sign, MPD_SNAN); + if (*s == '\0') + return; + /* validate payload: digits only */ + if ((coeff = scan_payload(s, &end)) == NULL) + goto conversion_error; + /* payload consists entirely of zeros */ + if (*coeff == '\0') + return; + digits = end - coeff; + if (digits > (size_t)(ctx->prec-ctx->clamp)) + goto conversion_error; + } + else if (_mpd_strneq(s, "inf", "INF", 3)) { + s += 3; + if (*s == '\0' || _mpd_strneq(s, "inity", "INITY", 6)) { + /* numeric-value: infinity */ + mpd_setspecial(dec, sign, MPD_INF); + return; + } + goto conversion_error; + } + else { + /* scan for start of coefficient, decimal point, indicator, end */ + if ((coeff = scan_dpoint_exp(s, &dpoint, &exp, &end)) == NULL) + goto conversion_error; + + /* numeric-value: [exponent-part] */ + if (exp) { + /* exponent-part */ + end = exp; exp++; + dec->exp = strtoexp(exp); + if (errno) { + if (!(errno == ERANGE && + (dec->exp == MPD_SSIZE_MAX || + dec->exp == MPD_SSIZE_MIN))) + goto conversion_error; + } + } + + digits = end - coeff; + if (dpoint) { + size_t fracdigits = end-dpoint-1; + if (dpoint > coeff) digits--; + + if (fracdigits > MPD_MAX_PREC) { + goto conversion_error; + } + if (dec->exp < MPD_SSIZE_MIN+(mpd_ssize_t)fracdigits) { + dec->exp = MPD_SSIZE_MIN; + } + else { + dec->exp -= (mpd_ssize_t)fracdigits; + } + } + if (digits > MPD_MAX_PREC) { + goto conversion_error; + } + if (dec->exp > MPD_EXP_INF) { + dec->exp = MPD_EXP_INF; + } + if (dec->exp == MPD_SSIZE_MIN) { + dec->exp = MPD_SSIZE_MIN+1; + } + } + + _mpd_idiv_word(&q, &r, (mpd_ssize_t)digits, MPD_RDIGITS); + + len = (r == 0) ? q : q+1; + if (len == 0) { + goto conversion_error; /* GCOV_NOT_REACHED */ + } + if (!mpd_qresize(dec, len, status)) { + mpd_seterror(dec, MPD_Malloc_error, status); + return; + } + dec->len = len; + + string_to_coeff(dec->data, coeff, dpoint, (int)r, len); + + mpd_setdigits(dec); + mpd_qfinalize(dec, ctx, status); + return; + +conversion_error: + /* standard wants a positive NaN */ + mpd_seterror(dec, MPD_Conversion_syntax, status); +} + +/* Print word x with n decimal digits to string s. dot is either NULL + or the location of a decimal point. */ +#define EXTRACT_DIGIT(s, x, d, dot) \ + if (s == dot) *s++ = '.'; *s++ = '0' + (char)(x / d); x %= d +static inline char * +word_to_string(char *s, mpd_uint_t x, int n, char *dot) +{ + switch(n) { +#ifdef CONFIG_64 + case 20: EXTRACT_DIGIT(s, x, 10000000000000000000ULL, dot); /* GCOV_NOT_REACHED */ + case 19: EXTRACT_DIGIT(s, x, 1000000000000000000ULL, dot); + case 18: EXTRACT_DIGIT(s, x, 100000000000000000ULL, dot); + case 17: EXTRACT_DIGIT(s, x, 10000000000000000ULL, dot); + case 16: EXTRACT_DIGIT(s, x, 1000000000000000ULL, dot); + case 15: EXTRACT_DIGIT(s, x, 100000000000000ULL, dot); + case 14: EXTRACT_DIGIT(s, x, 10000000000000ULL, dot); + case 13: EXTRACT_DIGIT(s, x, 1000000000000ULL, dot); + case 12: EXTRACT_DIGIT(s, x, 100000000000ULL, dot); + case 11: EXTRACT_DIGIT(s, x, 10000000000ULL, dot); +#endif + case 10: EXTRACT_DIGIT(s, x, 1000000000UL, dot); + case 9: EXTRACT_DIGIT(s, x, 100000000UL, dot); + case 8: EXTRACT_DIGIT(s, x, 10000000UL, dot); + case 7: EXTRACT_DIGIT(s, x, 1000000UL, dot); + case 6: EXTRACT_DIGIT(s, x, 100000UL, dot); + case 5: EXTRACT_DIGIT(s, x, 10000UL, dot); + case 4: EXTRACT_DIGIT(s, x, 1000UL, dot); + case 3: EXTRACT_DIGIT(s, x, 100UL, dot); + case 2: EXTRACT_DIGIT(s, x, 10UL, dot); + default: if (s == dot) *s++ = '.'; *s++ = '0' + (char)x; + } + + *s = '\0'; + return s; +} + +/* Print exponent x to string s. Undefined for MPD_SSIZE_MIN. */ +static inline char * +exp_to_string(char *s, mpd_ssize_t x) +{ + char sign = '+'; + + if (x < 0) { + sign = '-'; + x = -x; + } + *s++ = sign; + + return word_to_string(s, x, mpd_word_digits(x), NULL); +} + +/* Print the coefficient of dec to string s. len(dec) > 0. */ +static inline char * +coeff_to_string(char *s, const mpd_t *dec) +{ + mpd_uint_t x; + mpd_ssize_t i; + + /* most significant word */ + x = mpd_msword(dec); + s = word_to_string(s, x, mpd_word_digits(x), NULL); + + /* remaining full words */ + for (i=dec->len-2; i >= 0; --i) { + x = dec->data[i]; + s = word_to_string(s, x, MPD_RDIGITS, NULL); + } + + return s; +} + +/* Print the coefficient of dec to string s. len(dec) > 0. dot is either + NULL or a pointer to the location of a decimal point. */ +static inline char * +coeff_to_string_dot(char *s, char *dot, const mpd_t *dec) +{ + mpd_uint_t x; + mpd_ssize_t i; + + /* most significant word */ + x = mpd_msword(dec); + s = word_to_string(s, x, mpd_word_digits(x), dot); + + /* remaining full words */ + for (i=dec->len-2; i >= 0; --i) { + x = dec->data[i]; + s = word_to_string(s, x, MPD_RDIGITS, dot); + } + + return s; +} + +/* Format type */ +#define MPD_FMT_LOWER 0x00000000 +#define MPD_FMT_UPPER 0x00000001 +#define MPD_FMT_TOSCI 0x00000002 +#define MPD_FMT_TOENG 0x00000004 +#define MPD_FMT_EXP 0x00000008 +#define MPD_FMT_FIXED 0x00000010 +#define MPD_FMT_PERCENT 0x00000020 +#define MPD_FMT_SIGN_SPACE 0x00000040 +#define MPD_FMT_SIGN_PLUS 0x00000080 + +/* Default place of the decimal point for MPD_FMT_TOSCI, MPD_FMT_EXP */ +#define MPD_DEFAULT_DOTPLACE 1 + +/* + * Set *result to the string representation of a decimal. Return the length + * of *result, not including the terminating '\0' character. + * + * Formatting is done according to 'flags'. A return value of -1 with *result + * set to NULL indicates MPD_Malloc_error. + * + * 'dplace' is the default place of the decimal point. It is always set to + * MPD_DEFAULT_DOTPLACE except for zeros in combination with MPD_FMT_EXP. + */ +static mpd_ssize_t +_mpd_to_string(char **result, const mpd_t *dec, int flags, mpd_ssize_t dplace) +{ + char *decstring = NULL, *cp = NULL; + mpd_ssize_t ldigits; + mpd_ssize_t mem = 0, k; + + if (mpd_isspecial(dec)) { + + mem = sizeof "-Infinity%"; + if (mpd_isnan(dec) && dec->len > 0) { + /* diagnostic code */ + mem += dec->digits; + } + cp = decstring = mpd_alloc(mem, sizeof *decstring); + if (cp == NULL) { + *result = NULL; + return -1; + } + + if (mpd_isnegative(dec)) { + *cp++ = '-'; + } + else if (flags&MPD_FMT_SIGN_SPACE) { + *cp++ = ' '; + } + else if (flags&MPD_FMT_SIGN_PLUS) { + *cp++ = '+'; + } + + if (mpd_isnan(dec)) { + if (mpd_isqnan(dec)) { + strcpy(cp, "NaN"); + cp += 3; + } + else { + strcpy(cp, "sNaN"); + cp += 4; + } + if (dec->len > 0) { /* diagnostic code */ + cp = coeff_to_string(cp, dec); + } + } + else if (mpd_isinfinite(dec)) { + strcpy(cp, "Infinity"); + cp += 8; + } + else { /* debug */ + abort(); /* GCOV_NOT_REACHED */ + } + } + else { + assert(dec->len > 0); + + /* + * For easier manipulation of the decimal point's location + * and the exponent that is finally printed, the number is + * rescaled to a virtual representation with exp = 0. Here + * ldigits denotes the number of decimal digits to the left + * of the decimal point and remains constant once initialized. + * + * dplace is the location of the decimal point relative to + * the start of the coefficient. Note that 3) always holds + * when dplace is shifted. + * + * 1) ldigits := dec->digits - dec->exp + * 2) dplace := ldigits (initially) + * 3) exp := ldigits - dplace (initially exp = 0) + * + * 0.00000_.____._____000000. + * ^ ^ ^ ^ + * | | | | + * | | | `- dplace >= digits + * | | `- dplace in the middle of the coefficient + * | ` dplace = 1 (after the first coefficient digit) + * `- dplace <= 0 + */ + + ldigits = dec->digits + dec->exp; + + if (flags&MPD_FMT_EXP) { + ; + } + else if (flags&MPD_FMT_FIXED || (dec->exp <= 0 && ldigits > -6)) { + /* MPD_FMT_FIXED: always use fixed point notation. + * MPD_FMT_TOSCI, MPD_FMT_TOENG: for a certain range, + * override exponent notation. */ + dplace = ldigits; + } + else if (flags&MPD_FMT_TOENG) { + if (mpd_iszero(dec)) { + /* If the exponent is divisible by three, + * dplace = 1. Otherwise, move dplace one + * or two places to the left. */ + dplace = -1 + mod_mpd_ssize_t(dec->exp+2, 3); + } + else { /* ldigits-1 is the adjusted exponent, which + * should be divisible by three. If not, move + * dplace one or two places to the right. */ + dplace += mod_mpd_ssize_t(ldigits-1, 3); + } + } + + /* + * Basic space requirements: + * + * [-][.][coeffdigits][E][-][expdigits+1][%]['\0'] + * + * If the decimal point lies outside of the coefficient digits, + * space is adjusted accordingly. + */ + if (dplace <= 0) { + mem = -dplace + dec->digits + 2; + } + else if (dplace >= dec->digits) { + mem = dplace; + } + else { + mem = dec->digits; + } + mem += (MPD_EXPDIGITS+1+6); + + cp = decstring = mpd_alloc(mem, sizeof *decstring); + if (cp == NULL) { + *result = NULL; + return -1; + } + + + if (mpd_isnegative(dec)) { + *cp++ = '-'; + } + else if (flags&MPD_FMT_SIGN_SPACE) { + *cp++ = ' '; + } + else if (flags&MPD_FMT_SIGN_PLUS) { + *cp++ = '+'; + } + + if (dplace <= 0) { + /* space: -dplace+dec->digits+2 */ + *cp++ = '0'; + *cp++ = '.'; + for (k = 0; k < -dplace; k++) { + *cp++ = '0'; + } + cp = coeff_to_string(cp, dec); + } + else if (dplace >= dec->digits) { + /* space: dplace */ + cp = coeff_to_string(cp, dec); + for (k = 0; k < dplace-dec->digits; k++) { + *cp++ = '0'; + } + } + else { + /* space: dec->digits+1 */ + cp = coeff_to_string_dot(cp, cp+dplace, dec); + } + + /* + * Conditions for printing an exponent: + * + * MPD_FMT_TOSCI, MPD_FMT_TOENG: only if ldigits != dplace + * MPD_FMT_FIXED: never (ldigits == dplace) + * MPD_FMT_EXP: always + */ + if (ldigits != dplace || flags&MPD_FMT_EXP) { + /* space: expdigits+2 */ + *cp++ = (flags&MPD_FMT_UPPER) ? 'E' : 'e'; + cp = exp_to_string(cp, ldigits-dplace); + } + } + + if (flags&MPD_FMT_PERCENT) { + *cp++ = '%'; + } + + assert(cp < decstring+mem); + assert(cp-decstring < MPD_SSIZE_MAX); + + *cp = '\0'; + *result = decstring; + return (mpd_ssize_t)(cp-decstring); +} + +char * +mpd_to_sci(const mpd_t *dec, int fmt) +{ + char *res; + int flags = MPD_FMT_TOSCI; + + flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; + (void)_mpd_to_string(&res, dec, flags, MPD_DEFAULT_DOTPLACE); + return res; +} + +char * +mpd_to_eng(const mpd_t *dec, int fmt) +{ + char *res; + int flags = MPD_FMT_TOENG; + + flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; + (void)_mpd_to_string(&res, dec, flags, MPD_DEFAULT_DOTPLACE); + return res; +} + +mpd_ssize_t +mpd_to_sci_size(char **res, const mpd_t *dec, int fmt) +{ + int flags = MPD_FMT_TOSCI; + + flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; + return _mpd_to_string(res, dec, flags, MPD_DEFAULT_DOTPLACE); +} + +mpd_ssize_t +mpd_to_eng_size(char **res, const mpd_t *dec, int fmt) +{ + int flags = MPD_FMT_TOENG; + + flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; + return _mpd_to_string(res, dec, flags, MPD_DEFAULT_DOTPLACE); +} + +/* Copy a single UTF-8 char to dest. See: The Unicode Standard, version 5.2, + chapter 3.9: Well-formed UTF-8 byte sequences. */ +static int +_mpd_copy_utf8(char dest[5], const char *s) +{ + const uchar *cp = (const uchar *)s; + uchar lb, ub; + int count, i; + + + if (*cp == 0) { + /* empty string */ + dest[0] = '\0'; + return 0; + } + else if (*cp <= 0x7f) { + /* ascii */ + dest[0] = *cp; + dest[1] = '\0'; + return 1; + } + else if (0xc2 <= *cp && *cp <= 0xdf) { + lb = 0x80; ub = 0xbf; + count = 2; + } + else if (*cp == 0xe0) { + lb = 0xa0; ub = 0xbf; + count = 3; + } + else if (*cp <= 0xec) { + lb = 0x80; ub = 0xbf; + count = 3; + } + else if (*cp == 0xed) { + lb = 0x80; ub = 0x9f; + count = 3; + } + else if (*cp <= 0xef) { + lb = 0x80; ub = 0xbf; + count = 3; + } + else if (*cp == 0xf0) { + lb = 0x90; ub = 0xbf; + count = 4; + } + else if (*cp <= 0xf3) { + lb = 0x80; ub = 0xbf; + count = 4; + } + else if (*cp == 0xf4) { + lb = 0x80; ub = 0x8f; + count = 4; + } + else { + /* invalid */ + goto error; + } + + dest[0] = *cp++; + if (*cp < lb || ub < *cp) { + goto error; + } + dest[1] = *cp++; + for (i = 2; i < count; i++) { + if (*cp < 0x80 || 0xbf < *cp) { + goto error; + } + dest[i] = *cp++; + } + dest[i] = '\0'; + + return count; + +error: + dest[0] = '\0'; + return -1; +} + +int +mpd_validate_lconv(mpd_spec_t *spec) +{ + size_t n; +#if CHAR_MAX == SCHAR_MAX + const char *cp = spec->grouping; + while (*cp != '\0') { + if (*cp++ < 0) { + return -1; + } + } +#endif + n = strlen(spec->dot); + if (n == 0 || n > 4) { + return -1; + } + if (strlen(spec->sep) > 4) { + return -1; + } + + return 0; +} + +int +mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps) +{ + char *cp = (char *)fmt; + int have_align = 0, n; + + /* defaults */ + spec->min_width = 0; + spec->prec = -1; + spec->type = caps ? 'G' : 'g'; + spec->align = '>'; + spec->sign = '-'; + spec->dot = ""; + spec->sep = ""; + spec->grouping = ""; + + + /* presume that the first character is a UTF-8 fill character */ + if ((n = _mpd_copy_utf8(spec->fill, cp)) < 0) { + return 0; + } + + /* alignment directive, prefixed by a fill character */ + if (*cp && (*(cp+n) == '<' || *(cp+n) == '>' || + *(cp+n) == '=' || *(cp+n) == '^')) { + cp += n; + spec->align = *cp++; + have_align = 1; + } /* alignment directive */ + else { + /* default fill character */ + spec->fill[0] = ' '; + spec->fill[1] = '\0'; + if (*cp == '<' || *cp == '>' || + *cp == '=' || *cp == '^') { + spec->align = *cp++; + have_align = 1; + } + } + + /* sign formatting */ + if (*cp == '+' || *cp == '-' || *cp == ' ') { + spec->sign = *cp++; + } + + /* zero padding */ + if (*cp == '0') { + /* zero padding implies alignment, which should not be + * specified twice. */ + if (have_align) { + return 0; + } + spec->align = 'z'; + spec->fill[0] = *cp++; + spec->fill[1] = '\0'; + } + + /* minimum width */ + if (isdigit((uchar)*cp)) { + if (*cp == '0') { + return 0; + } + errno = 0; + spec->min_width = mpd_strtossize(cp, &cp, 10); + if (errno == ERANGE || errno == EINVAL) { + return 0; + } + } + + /* thousands separator */ + if (*cp == ',') { + spec->dot = "."; + spec->sep = ","; + spec->grouping = "\003\003"; + cp++; + } + + /* fraction digits or significant digits */ + if (*cp == '.') { + cp++; + if (!isdigit((uchar)*cp)) { + return 0; + } + errno = 0; + spec->prec = mpd_strtossize(cp, &cp, 10); + if (errno == ERANGE || errno == EINVAL) { + return 0; + } + } + + /* type */ + if (*cp == 'E' || *cp == 'e' || *cp == 'F' || *cp == 'f' || + *cp == 'G' || *cp == 'g' || *cp == '%') { + spec->type = *cp++; + } + else if (*cp == 'N' || *cp == 'n') { + /* locale specific conversion */ + struct lconv *lc; + /* separator has already been specified */ + if (*spec->sep) { + return 0; + } + spec->type = *cp++; + spec->type = (spec->type == 'N') ? 'G' : 'g'; + lc = localeconv(); + spec->dot = lc->decimal_point; + spec->sep = lc->thousands_sep; + spec->grouping = lc->grouping; + if (mpd_validate_lconv(spec) < 0) { + return 0; /* GCOV_NOT_REACHED */ + } + } + + /* check correctness */ + if (*cp != '\0') { + return 0; + } + + return 1; +} + +/* + * The following functions assume that spec->min_width <= MPD_MAX_PREC, which + * is made sure in mpd_qformat_spec. Then, even with a spec that inserts a + * four-byte separator after each digit, nbytes in the following struct + * cannot overflow. + */ + +/* Multibyte string */ +typedef struct { + mpd_ssize_t nbytes; /* length in bytes */ + mpd_ssize_t nchars; /* length in chars */ + mpd_ssize_t cur; /* current write index */ + char *data; +} mpd_mbstr_t; + +static inline void +_mpd_bcopy(char *dest, const char *src, mpd_ssize_t n) +{ + while (--n >= 0) { + dest[n] = src[n]; + } +} + +static inline void +_mbstr_copy_char(mpd_mbstr_t *dest, const char *src, mpd_ssize_t n) +{ + dest->nbytes += n; + dest->nchars += (n > 0 ? 1 : 0); + dest->cur -= n; + + if (dest->data != NULL) { + _mpd_bcopy(dest->data+dest->cur, src, n); + } +} + +static inline void +_mbstr_copy_ascii(mpd_mbstr_t *dest, const char *src, mpd_ssize_t n) +{ + dest->nbytes += n; + dest->nchars += n; + dest->cur -= n; + + if (dest->data != NULL) { + _mpd_bcopy(dest->data+dest->cur, src, n); + } +} + +static inline void +_mbstr_copy_pad(mpd_mbstr_t *dest, mpd_ssize_t n) +{ + dest->nbytes += n; + dest->nchars += n; + dest->cur -= n; + + if (dest->data != NULL) { + char *cp = dest->data + dest->cur; + while (--n >= 0) { + cp[n] = '0'; + } + } +} + +/* + * Copy a numeric string to dest->data, adding separators in the integer + * part according to spec->grouping. If leading zero padding is enabled + * and the result is smaller than spec->min_width, continue adding zeros + * and separators until the minimum width is reached. + * + * The final length of dest->data is stored in dest->nbytes. The number + * of UTF-8 characters is stored in dest->nchars. + * + * First run (dest->data == NULL): determine the length of the result + * string and store it in dest->nbytes. + * + * Second run (write to dest->data): data is written in chunks and in + * reverse order, starting with the rest of the numeric string. + */ +static void +_mpd_add_sep_dot(mpd_mbstr_t *dest, + const char *sign, /* location of optional sign */ + const char *src, mpd_ssize_t n_src, /* integer part and length */ + const char *dot, /* location of optional decimal point */ + const char *rest, mpd_ssize_t n_rest, /* remaining part and length */ + const mpd_spec_t *spec) +{ + mpd_ssize_t n_sep, n_sign, consume; + const char *g; + int pad = 0; + + n_sign = sign ? 1 : 0; + n_sep = (mpd_ssize_t)strlen(spec->sep); + /* Initial write index: set to location of '\0' in the output string. + * Irrelevant for the first run. */ + dest->cur = dest->nbytes; + dest->nbytes = dest->nchars = 0; + + _mbstr_copy_ascii(dest, rest, n_rest); + + if (dot) { + _mbstr_copy_char(dest, dot, (mpd_ssize_t)strlen(dot)); + } + + g = spec->grouping; + consume = *g; + while (1) { + /* If the group length is 0 or CHAR_MAX or greater than the + * number of source bytes, consume all remaining bytes. */ + if (*g == 0 || *g == CHAR_MAX || consume > n_src) { + consume = n_src; + } + n_src -= consume; + if (pad) { + _mbstr_copy_pad(dest, consume); + } + else { + _mbstr_copy_ascii(dest, src+n_src, consume); + } + + if (n_src == 0) { + /* Either the real source of intpart digits or the virtual + * source of padding zeros is exhausted. */ + if (spec->align == 'z' && + dest->nchars + n_sign < spec->min_width) { + /* Zero padding is set and length < min_width: + * Generate n_src additional characters. */ + n_src = spec->min_width - (dest->nchars + n_sign); + /* Next iteration: + * case *g == 0 || *g == CHAR_MAX: + * consume all padding characters + * case consume < g*: + * fill remainder of current group + * case consume == g* + * copying is a no-op */ + consume = *g - consume; + /* Switch on virtual source of zeros. */ + pad = 1; + continue; + } + break; + } + + if (n_sep > 0) { + /* If padding is switched on, separators are counted + * as padding characters. This rule does not apply if + * the separator would be the first character of the + * result string. */ + if (pad && n_src > 1) n_src -= 1; + _mbstr_copy_char(dest, spec->sep, n_sep); + } + + /* If non-NUL, use the next value for grouping. */ + if (*g && *(g+1)) g++; + consume = *g; + } + + if (sign) { + _mbstr_copy_ascii(dest, sign, 1); + } + + if (dest->data) { + dest->data[dest->nbytes] = '\0'; + } +} + +/* + * Convert a numeric-string to its locale-specific appearance. + * The string must have one of these forms: + * + * 1) [sign] digits [exponent-part] + * 2) [sign] digits '.' [digits] [exponent-part] + * + * Not allowed, since _mpd_to_string() never returns this form: + * + * 3) [sign] '.' digits [exponent-part] + * + * Input: result->data := original numeric string (ASCII) + * result->bytes := strlen(result->data) + * result->nchars := strlen(result->data) + * + * Output: result->data := modified or original string + * result->bytes := strlen(result->data) + * result->nchars := number of characters (possibly UTF-8) + */ +static int +_mpd_apply_lconv(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status) +{ + const char *sign = NULL, *intpart = NULL, *dot = NULL; + const char *rest, *dp; + char *decstring; + mpd_ssize_t n_int, n_rest; + + /* original numeric string */ + dp = result->data; + + /* sign */ + if (*dp == '+' || *dp == '-' || *dp == ' ') { + sign = dp++; + } + /* integer part */ + assert(isdigit((uchar)*dp)); + intpart = dp++; + while (isdigit((uchar)*dp)) { + dp++; + } + n_int = (mpd_ssize_t)(dp-intpart); + /* decimal point */ + if (*dp == '.') { + dp++; dot = spec->dot; + } + /* rest */ + rest = dp; + n_rest = result->nbytes - (mpd_ssize_t)(dp-result->data); + + if (dot == NULL && (*spec->sep == '\0' || *spec->grouping == '\0')) { + /* _mpd_add_sep_dot() would not change anything */ + return 1; + } + + /* Determine the size of the new decimal string after inserting the + * decimal point, optional separators and optional padding. */ + decstring = result->data; + result->data = NULL; + _mpd_add_sep_dot(result, sign, intpart, n_int, dot, + rest, n_rest, spec); + + result->data = mpd_alloc(result->nbytes+1, 1); + if (result->data == NULL) { + *status |= MPD_Malloc_error; + mpd_free(decstring); + return 0; + } + + /* Perform actual writes. */ + _mpd_add_sep_dot(result, sign, intpart, n_int, dot, + rest, n_rest, spec); + + mpd_free(decstring); + return 1; +} + +/* Add padding to the formatted string if necessary. */ +static int +_mpd_add_pad(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status) +{ + if (result->nchars < spec->min_width) { + mpd_ssize_t add_chars, add_bytes; + size_t lpad = 0, rpad = 0; + size_t n_fill, len, i, j; + char align = spec->align; + uint8_t err = 0; + char *cp; + + n_fill = strlen(spec->fill); + add_chars = (spec->min_width - result->nchars); + /* max value: MPD_MAX_PREC * 4 */ + add_bytes = add_chars * (mpd_ssize_t)n_fill; + + cp = result->data = mpd_realloc(result->data, + result->nbytes+add_bytes+1, + sizeof *result->data, &err); + if (err) { + *status |= MPD_Malloc_error; + mpd_free(result->data); + return 0; + } + + if (align == 'z') { + align = '='; + } + + if (align == '<') { + rpad = add_chars; + } + else if (align == '>' || align == '=') { + lpad = add_chars; + } + else { /* align == '^' */ + lpad = add_chars/2; + rpad = add_chars-lpad; + } + + len = result->nbytes; + if (align == '=' && (*cp == '-' || *cp == '+' || *cp == ' ')) { + /* leave sign in the leading position */ + cp++; len--; + } + + memmove(cp+n_fill*lpad, cp, len); + for (i = 0; i < lpad; i++) { + for (j = 0; j < n_fill; j++) { + cp[i*n_fill+j] = spec->fill[j]; + } + } + cp += (n_fill*lpad + len); + for (i = 0; i < rpad; i++) { + for (j = 0; j < n_fill; j++) { + cp[i*n_fill+j] = spec->fill[j]; + } + } + + result->nbytes += add_bytes; + result->nchars += add_chars; + result->data[result->nbytes] = '\0'; + } + + return 1; +} + +/* Round a number to prec digits. The adjusted exponent stays the same + or increases by one if rounding up crosses a power of ten boundary. + If result->digits would exceed MPD_MAX_PREC+1, MPD_Invalid_operation + is set and the result is NaN. */ +static inline void +_mpd_round(mpd_t *result, const mpd_t *a, mpd_ssize_t prec, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_ssize_t exp = a->exp + a->digits - prec; + + if (prec <= 0) { + mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_NOT_REACHED */ + return; /* GCOV_NOT_REACHED */ + } + if (mpd_isspecial(a) || mpd_iszero(a)) { + mpd_qcopy(result, a, status); /* GCOV_NOT_REACHED */ + return; /* GCOV_NOT_REACHED */ + } + + mpd_qrescale_fmt(result, a, exp, ctx, status); + if (result->digits > prec) { + mpd_qrescale_fmt(result, result, exp+1, ctx, status); + } +} + +/* + * Return the string representation of an mpd_t, formatted according to 'spec'. + * The format specification is assumed to be valid. Memory errors are indicated + * as usual. This function is quiet. + */ +char * +mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_uint_t dt[MPD_MINALLOC_MAX]; + mpd_t tmp = {MPD_STATIC|MPD_STATIC_DATA,0,0,0,MPD_MINALLOC_MAX,dt}; + mpd_ssize_t dplace = MPD_DEFAULT_DOTPLACE; + mpd_mbstr_t result; + mpd_spec_t stackspec; + char type = spec->type; + int flags = 0; + + + if (spec->min_width > MPD_MAX_PREC) { + *status |= MPD_Invalid_operation; + return NULL; + } + + if (isupper((uchar)type)) { + type = tolower((uchar)type); + flags |= MPD_FMT_UPPER; + } + if (spec->sign == ' ') { + flags |= MPD_FMT_SIGN_SPACE; + } + else if (spec->sign == '+') { + flags |= MPD_FMT_SIGN_PLUS; + } + + if (mpd_isspecial(dec)) { + if (spec->align == 'z') { + stackspec = *spec; + stackspec.fill[0] = ' '; + stackspec.fill[1] = '\0'; + stackspec.align = '>'; + spec = &stackspec; + } + if (type == '%') { + flags |= MPD_FMT_PERCENT; + } + } + else { + uint32_t workstatus = 0; + mpd_ssize_t prec; + + switch (type) { + case 'g': flags |= MPD_FMT_TOSCI; break; + case 'e': flags |= MPD_FMT_EXP; break; + case '%': flags |= MPD_FMT_PERCENT; + if (!mpd_qcopy(&tmp, dec, status)) { + return NULL; + } + tmp.exp += 2; + dec = &tmp; + type = 'f'; /* fall through */ + case 'f': flags |= MPD_FMT_FIXED; break; + default: abort(); /* debug: GCOV_NOT_REACHED */ + } + + if (spec->prec >= 0) { + if (spec->prec > MPD_MAX_PREC) { + *status |= MPD_Invalid_operation; + goto error; + } + + switch (type) { + case 'g': + prec = (spec->prec == 0) ? 1 : spec->prec; + if (dec->digits > prec) { + _mpd_round(&tmp, dec, prec, ctx, + &workstatus); + dec = &tmp; + } + break; + case 'e': + if (mpd_iszero(dec)) { + dplace = 1-spec->prec; + } + else { + _mpd_round(&tmp, dec, spec->prec+1, ctx, + &workstatus); + dec = &tmp; + } + break; + case 'f': + mpd_qrescale(&tmp, dec, -spec->prec, ctx, + &workstatus); + dec = &tmp; + break; + } + } + + if (type == 'f') { + if (mpd_iszero(dec) && dec->exp > 0) { + mpd_qrescale(&tmp, dec, 0, ctx, &workstatus); + dec = &tmp; + } + } + + if (workstatus&MPD_Errors) { + *status |= (workstatus&MPD_Errors); + goto error; + } + } + + /* + * At this point, for all scaled or non-scaled decimals: + * 1) 1 <= digits <= MAX_PREC+1 + * 2) adjexp(scaled) = adjexp(orig) [+1] + * 3) case 'g': MIN_ETINY <= exp <= MAX_EMAX+1 + * case 'e': MIN_ETINY-MAX_PREC <= exp <= MAX_EMAX+1 + * case 'f': MIN_ETINY <= exp <= MAX_EMAX+1 + * 4) max memory alloc in _mpd_to_string: + * case 'g': MAX_PREC+36 + * case 'e': MAX_PREC+36 + * case 'f': 2*MPD_MAX_PREC+30 + */ + result.nbytes = _mpd_to_string(&result.data, dec, flags, dplace); + result.nchars = result.nbytes; + if (result.nbytes < 0) { + *status |= MPD_Malloc_error; + goto error; + } + + if (*spec->dot != '\0' && !mpd_isspecial(dec)) { + if (result.nchars > MPD_MAX_PREC+36) { + /* Since a group length of one is not explicitly + * disallowed, ensure that it is always possible to + * insert a four byte separator after each digit. */ + *status |= MPD_Invalid_operation; + mpd_free(result.data); + goto error; + } + if (!_mpd_apply_lconv(&result, spec, status)) { + goto error; + } + } + + if (spec->min_width) { + if (!_mpd_add_pad(&result, spec, status)) { + goto error; + } + } + + mpd_del(&tmp); + return result.data; + +error: + mpd_del(&tmp); + return NULL; +} + +char * +mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_spec_t spec; + + if (!mpd_parse_fmt_str(&spec, fmt, 1)) { + *status |= MPD_Invalid_operation; + return NULL; + } + + return mpd_qformat_spec(dec, &spec, ctx, status); +} + +/* + * The specification has a *condition* called Invalid_operation and an + * IEEE *signal* called Invalid_operation. The former corresponds to + * MPD_Invalid_operation, the latter to MPD_IEEE_Invalid_operation. + * MPD_IEEE_Invalid_operation comprises the following conditions: + * + * [MPD_Conversion_syntax, MPD_Division_impossible, MPD_Division_undefined, + * MPD_Fpu_error, MPD_Invalid_context, MPD_Invalid_operation, + * MPD_Malloc_error] + * + * In the following functions, 'flag' denotes the condition, 'signal' + * denotes the IEEE signal. + */ + +static const char *mpd_flag_string[MPD_NUM_FLAGS] = { + "Clamped", + "Conversion_syntax", + "Division_by_zero", + "Division_impossible", + "Division_undefined", + "Fpu_error", + "Inexact", + "Invalid_context", + "Invalid_operation", + "Malloc_error", + "Not_implemented", + "Overflow", + "Rounded", + "Subnormal", + "Underflow", +}; + +static const char *mpd_signal_string[MPD_NUM_FLAGS] = { + "Clamped", + "IEEE_Invalid_operation", + "Division_by_zero", + "IEEE_Invalid_operation", + "IEEE_Invalid_operation", + "IEEE_Invalid_operation", + "Inexact", + "IEEE_Invalid_operation", + "IEEE_Invalid_operation", + "IEEE_Invalid_operation", + "Not_implemented", + "Overflow", + "Rounded", + "Subnormal", + "Underflow", +}; + +/* print conditions to buffer, separated by spaces */ +int +mpd_snprint_flags(char *dest, int nmemb, uint32_t flags) +{ + char *cp; + int n, j; + + assert(nmemb >= MPD_MAX_FLAG_STRING); + + *dest = '\0'; cp = dest; + for (j = 0; j < MPD_NUM_FLAGS; j++) { + if (flags & (1U<= nmemb) return -1; + cp += n; nmemb -= n; + } + } + + if (cp != dest) { + *(--cp) = '\0'; + } + + return (int)(cp-dest); +} + +/* print conditions to buffer, in list form */ +int +mpd_lsnprint_flags(char *dest, int nmemb, uint32_t flags, const char *flag_string[]) +{ + char *cp; + int n, j; + + assert(nmemb >= MPD_MAX_FLAG_LIST); + if (flag_string == NULL) { + flag_string = mpd_flag_string; + } + + *dest = '['; + *(dest+1) = '\0'; + cp = dest+1; + --nmemb; + + for (j = 0; j < MPD_NUM_FLAGS; j++) { + if (flags & (1U<= nmemb) return -1; + cp += n; nmemb -= n; + } + } + + /* erase the last ", " */ + if (cp != dest+1) { + cp -= 2; + } + + *cp++ = ']'; + *cp = '\0'; + + return (int)(cp-dest); /* strlen, without NUL terminator */ +} + +/* print signals to buffer, in list form */ +int +mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[]) +{ + char *cp; + int n, j; + int ieee_invalid_done = 0; + + assert(nmemb >= MPD_MAX_SIGNAL_LIST); + if (signal_string == NULL) { + signal_string = mpd_signal_string; + } + + *dest = '['; + *(dest+1) = '\0'; + cp = dest+1; + --nmemb; + + for (j = 0; j < MPD_NUM_FLAGS; j++) { + uint32_t f = flags & (1U<= nmemb) return -1; + cp += n; nmemb -= n; + } + } + + /* erase the last ", " */ + if (cp != dest+1) { + cp -= 2; + } + + *cp++ = ']'; + *cp = '\0'; + + return (int)(cp-dest); /* strlen, without NUL terminator */ +} + +/* The following two functions are mainly intended for debugging. */ +void +mpd_fprint(FILE *file, const mpd_t *dec) +{ + char *decstring; + + decstring = mpd_to_sci(dec, 1); + if (decstring != NULL) { + fprintf(file, "%s\n", decstring); + mpd_free(decstring); + } + else { + fputs("mpd_fprint: output error\n", file); /* GCOV_NOT_REACHED */ + } +} + +void +mpd_print(const mpd_t *dec) +{ + char *decstring; + + decstring = mpd_to_sci(dec, 1); + if (decstring != NULL) { + printf("%s\n", decstring); + mpd_free(decstring); + } + else { + fputs("mpd_fprint: output error\n", stderr); /* GCOV_NOT_REACHED */ + } +} + + diff --git a/third_party/opa/wasm/src/libmpdec/io.h b/third_party/opa/wasm/src/libmpdec/io.h new file mode 100644 index 000000000000..de5486a00ca5 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/io.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef IO_H +#define IO_H + + +#include +#include "mpdecimal.h" + + +#if SIZE_MAX == MPD_SIZE_MAX + #define mpd_strtossize _mpd_strtossize +#else +static inline mpd_ssize_t +mpd_strtossize(const char *s, char **end, int base) +{ + int64_t retval; + + errno = 0; + retval = _mpd_strtossize(s, end, base); + if (errno == 0 && (retval > MPD_SSIZE_MAX || retval < MPD_SSIZE_MIN)) { + errno = ERANGE; + } + if (errno == ERANGE) { + return (retval < 0) ? MPD_SSIZE_MIN : MPD_SSIZE_MAX; + } + + return (mpd_ssize_t)retval; +} +#endif + + +#endif diff --git a/third_party/opa/wasm/src/libmpdec/memory.c b/third_party/opa/wasm/src/libmpdec/memory.c new file mode 100644 index 000000000000..61eb6336eb96 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/memory.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include +#include "typearith.h" +#include "memory.h" + + +#if defined(_MSC_VER) + #pragma warning(disable : 4232) +#endif + + +/* Guaranteed minimum allocation for a coefficient. May be changed once + at program start using mpd_setminalloc(). */ +mpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN; + +/* Custom allocation and free functions */ +void *(* mpd_mallocfunc)(size_t size) = malloc; +void *(* mpd_reallocfunc)(void *ptr, size_t size) = realloc; +void *(* mpd_callocfunc)(size_t nmemb, size_t size) = calloc; +void (* mpd_free)(void *ptr) = free; + + +/* emulate calloc if it is not available */ +void * +mpd_callocfunc_em(size_t nmemb, size_t size) +{ + void *ptr; + size_t req; + mpd_size_t overflow; + +#if MPD_SIZE_MAX < SIZE_MAX + /* full_coverage test only */ + if (nmemb > MPD_SIZE_MAX || size > MPD_SIZE_MAX) { + return NULL; + } +#endif + + req = mul_size_t_overflow((mpd_size_t)nmemb, (mpd_size_t)size, + &overflow); + if (overflow) { + return NULL; + } + + ptr = mpd_mallocfunc(req); + if (ptr == NULL) { + return NULL; + } + /* used on uint32_t or uint64_t */ + memset(ptr, 0, req); + + return ptr; +} + + +/* malloc with overflow checking */ +void * +mpd_alloc(mpd_size_t nmemb, mpd_size_t size) +{ + mpd_size_t req, overflow; + + req = mul_size_t_overflow(nmemb, size, &overflow); + if (overflow) { + return NULL; + } + + return mpd_mallocfunc(req); +} + +/* calloc with overflow checking */ +void * +mpd_calloc(mpd_size_t nmemb, mpd_size_t size) +{ + mpd_size_t overflow; + + (void)mul_size_t_overflow(nmemb, size, &overflow); + if (overflow) { + return NULL; + } + + return mpd_callocfunc(nmemb, size); +} + +/* realloc with overflow checking */ +void * +mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err) +{ + void *new; + mpd_size_t req, overflow; + + req = mul_size_t_overflow(nmemb, size, &overflow); + if (overflow) { + *err = 1; + return ptr; + } + + new = mpd_reallocfunc(ptr, req); + if (new == NULL) { + *err = 1; + return ptr; + } + + return new; +} + +/* struct hack malloc with overflow checking */ +void * +mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size) +{ + mpd_size_t req, overflow; + + req = mul_size_t_overflow(nmemb, size, &overflow); + if (overflow) { + return NULL; + } + + req = add_size_t_overflow(req, struct_size, &overflow); + if (overflow) { + return NULL; + } + + return mpd_mallocfunc(req); +} + + +/* Allocate a new decimal with a coefficient of length 'nwords'. In case + of an error the return value is NULL. */ +mpd_t * +mpd_qnew_size(mpd_ssize_t nwords) +{ + mpd_t *result; + + nwords = (nwords < MPD_MINALLOC) ? MPD_MINALLOC : nwords; + + result = mpd_alloc(1, sizeof *result); + if (result == NULL) { + return NULL; + } + + result->data = mpd_alloc(nwords, sizeof *result->data); + if (result->data == NULL) { + mpd_free(result); + return NULL; + } + + result->flags = 0; + result->exp = 0; + result->digits = 0; + result->len = 0; + result->alloc = nwords; + + return result; +} + +/* Allocate a new decimal with a coefficient of length MPD_MINALLOC. + In case of an error the return value is NULL. */ +mpd_t * +mpd_qnew(void) +{ + return mpd_qnew_size(MPD_MINALLOC); +} + +/* Allocate new decimal. Caller can check for NULL or MPD_Malloc_error. + Raises on error. */ +mpd_t * +mpd_new(mpd_context_t *ctx) +{ + mpd_t *result; + + result = mpd_qnew(); + if (result == NULL) { + mpd_addstatus_raise(ctx, MPD_Malloc_error); + } + return result; +} + +/* + * Input: 'result' is a static mpd_t with a static coefficient. + * Assumption: 'nwords' >= result->alloc. + * + * Resize the static coefficient to a larger dynamic one and copy the + * existing data. If successful, the value of 'result' is unchanged. + * Otherwise, set 'result' to NaN and update 'status' with MPD_Malloc_error. + */ +int +mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) +{ + mpd_uint_t *p = result->data; + + assert(nwords >= result->alloc); + + result->data = mpd_alloc(nwords, sizeof *result->data); + if (result->data == NULL) { + result->data = p; + mpd_set_qnan(result); + mpd_set_positive(result); + result->exp = result->digits = result->len = 0; + *status |= MPD_Malloc_error; + return 0; + } + + memcpy(result->data, p, result->alloc * (sizeof *result->data)); + result->alloc = nwords; + mpd_set_dynamic_data(result); + return 1; +} + +/* + * Input: 'result' is a static mpd_t with a static coefficient. + * + * Convert the coefficient to a dynamic one that is initialized to zero. If + * malloc fails, set 'result' to NaN and update 'status' with MPD_Malloc_error. + */ +int +mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) +{ + mpd_uint_t *p = result->data; + + result->data = mpd_calloc(nwords, sizeof *result->data); + if (result->data == NULL) { + result->data = p; + mpd_set_qnan(result); + mpd_set_positive(result); + result->exp = result->digits = result->len = 0; + *status |= MPD_Malloc_error; + return 0; + } + + result->alloc = nwords; + mpd_set_dynamic_data(result); + + return 1; +} + +/* + * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient. + * Resize the coefficient to length 'nwords': + * Case nwords > result->alloc: + * If realloc is successful: + * 'result' has a larger coefficient but the same value. Return 1. + * Otherwise: + * Set 'result' to NaN, update status with MPD_Malloc_error and return 0. + * Case nwords < result->alloc: + * If realloc is successful: + * 'result' has a smaller coefficient. result->len is undefined. Return 1. + * Otherwise (unlikely): + * 'result' is unchanged. Reuse the now oversized coefficient. Return 1. + */ +int +mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) +{ + uint8_t err = 0; + + result->data = mpd_realloc(result->data, nwords, sizeof *result->data, &err); + if (!err) { + result->alloc = nwords; + } + else if (nwords > result->alloc) { + mpd_set_qnan(result); + mpd_set_positive(result); + result->exp = result->digits = result->len = 0; + *status |= MPD_Malloc_error; + return 0; + } + + return 1; +} + + diff --git a/third_party/opa/wasm/src/libmpdec/memory.h b/third_party/opa/wasm/src/libmpdec/memory.h new file mode 100644 index 000000000000..9c98d1a4000d --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/memory.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef MEMORY_H +#define MEMORY_H + + +#include "mpdecimal.h" + + +/* Internal header file: all symbols have local scope in the DSO */ +MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) + + +int mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t size, uint32_t *status); +int mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t size, uint32_t *status); +int mpd_realloc_dyn(mpd_t *result, mpd_ssize_t size, uint32_t *status); + + +MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ + + +#endif + + + diff --git a/third_party/opa/wasm/src/libmpdec/mpdecimal.c b/third_party/opa/wasm/src/libmpdec/mpdecimal.c new file mode 100644 index 000000000000..593f9f5e03a5 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/mpdecimal.c @@ -0,0 +1,8411 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include +#include +#include +#include +#include "basearith.h" +#include "bits.h" +#include "convolute.h" +#include "crt.h" +#include "memory.h" +#include "typearith.h" +#include "umodarith.h" + +#ifdef PPRO + #if defined(_MSC_VER) + #include + #pragma float_control(precise, on) + #pragma fenv_access(on) + #elif !defined(__OpenBSD__) && !defined(__NetBSD__) + /* C99 */ + #include + #pragma STDC FENV_ACCESS ON + #endif +#endif + + +#if defined(_MSC_VER) + #define ALWAYS_INLINE __forceinline +#elif defined(LEGACY_COMPILER) + #define ALWAYS_INLINE + #undef inline + #define inline +#else + #ifdef TEST_COVERAGE + #define ALWAYS_INLINE + #else + #define ALWAYS_INLINE inline __attribute__ ((always_inline)) + #endif +#endif + + +#define MPD_NEWTONDIV_CUTOFF 1024L + +#define MPD_NEW_STATIC(name, flags, exp, digits, len) \ + mpd_uint_t name##_data[MPD_MINALLOC_MAX]; \ + mpd_t name = {flags|MPD_STATIC|MPD_STATIC_DATA, exp, digits, \ + len, MPD_MINALLOC_MAX, name##_data} + +#define MPD_NEW_CONST(name, flags, exp, digits, len, alloc, initval) \ + mpd_uint_t name##_data[alloc] = {initval}; \ + mpd_t name = {flags|MPD_STATIC|MPD_CONST_DATA, exp, digits, \ + len, alloc, name##_data} + +#define MPD_NEW_SHARED(name, a) \ + mpd_t name = {(a->flags&~MPD_DATAFLAGS)|MPD_STATIC|MPD_SHARED_DATA, \ + a->exp, a->digits, a->len, a->alloc, a->data} + + +static mpd_uint_t data_one[1] = {1}; +static mpd_uint_t data_zero[1] = {0}; +static const mpd_t one = {MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1, data_one}; +static const mpd_t minus_one = {MPD_NEG|MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1, + data_one}; +static const mpd_t zero = {MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1, data_zero}; + +static inline void _mpd_check_exp(mpd_t *dec, const mpd_context_t *ctx, + uint32_t *status); +static void _settriple(mpd_t *result, uint8_t sign, mpd_uint_t a, + mpd_ssize_t exp); +static inline mpd_ssize_t _mpd_real_size(mpd_uint_t *data, mpd_ssize_t size); + +static int _mpd_cmp_abs(const mpd_t *a, const mpd_t *b); + +static void _mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status); +static inline void _mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status); +static void _mpd_base_ndivmod(mpd_t *q, mpd_t *r, const mpd_t *a, + const mpd_t *b, uint32_t *status); +static inline void _mpd_qpow_uint(mpd_t *result, const mpd_t *base, + mpd_uint_t exp, uint8_t resultsign, + const mpd_context_t *ctx, uint32_t *status); + +static mpd_uint_t mpd_qsshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n); + + +/******************************************************************************/ +/* Version */ +/******************************************************************************/ + +const char * +mpd_version(void) +{ + return MPD_VERSION; +} + + +/******************************************************************************/ +/* Performance critical inline functions */ +/******************************************************************************/ + +#ifdef CONFIG_64 +/* Digits in a word, primarily useful for the most significant word. */ +ALWAYS_INLINE int +mpd_word_digits(mpd_uint_t word) +{ + if (word < mpd_pow10[9]) { + if (word < mpd_pow10[4]) { + if (word < mpd_pow10[2]) { + return (word < mpd_pow10[1]) ? 1 : 2; + } + return (word < mpd_pow10[3]) ? 3 : 4; + } + if (word < mpd_pow10[6]) { + return (word < mpd_pow10[5]) ? 5 : 6; + } + if (word < mpd_pow10[8]) { + return (word < mpd_pow10[7]) ? 7 : 8; + } + return 9; + } + if (word < mpd_pow10[14]) { + if (word < mpd_pow10[11]) { + return (word < mpd_pow10[10]) ? 10 : 11; + } + if (word < mpd_pow10[13]) { + return (word < mpd_pow10[12]) ? 12 : 13; + } + return 14; + } + if (word < mpd_pow10[18]) { + if (word < mpd_pow10[16]) { + return (word < mpd_pow10[15]) ? 15 : 16; + } + return (word < mpd_pow10[17]) ? 17 : 18; + } + + return (word < mpd_pow10[19]) ? 19 : 20; +} +#else +ALWAYS_INLINE int +mpd_word_digits(mpd_uint_t word) +{ + if (word < mpd_pow10[4]) { + if (word < mpd_pow10[2]) { + return (word < mpd_pow10[1]) ? 1 : 2; + } + return (word < mpd_pow10[3]) ? 3 : 4; + } + if (word < mpd_pow10[6]) { + return (word < mpd_pow10[5]) ? 5 : 6; + } + if (word < mpd_pow10[8]) { + return (word < mpd_pow10[7]) ? 7 : 8; + } + + return (word < mpd_pow10[9]) ? 9 : 10; +} +#endif + + +/* Adjusted exponent */ +ALWAYS_INLINE mpd_ssize_t +mpd_adjexp(const mpd_t *dec) +{ + return (dec->exp + dec->digits) - 1; +} + +/* Etiny */ +ALWAYS_INLINE mpd_ssize_t +mpd_etiny(const mpd_context_t *ctx) +{ + return ctx->emin - (ctx->prec - 1); +} + +/* Etop: used for folding down in IEEE clamping */ +ALWAYS_INLINE mpd_ssize_t +mpd_etop(const mpd_context_t *ctx) +{ + return ctx->emax - (ctx->prec - 1); +} + +/* Most significant word */ +ALWAYS_INLINE mpd_uint_t +mpd_msword(const mpd_t *dec) +{ + assert(dec->len > 0); + return dec->data[dec->len-1]; +} + +/* Most significant digit of a word */ +inline mpd_uint_t +mpd_msd(mpd_uint_t word) +{ + int n; + + n = mpd_word_digits(word); + return word / mpd_pow10[n-1]; +} + +/* Least significant digit of a word */ +ALWAYS_INLINE mpd_uint_t +mpd_lsd(mpd_uint_t word) +{ + return word % 10; +} + +/* Coefficient size needed to store 'digits' */ +ALWAYS_INLINE mpd_ssize_t +mpd_digits_to_size(mpd_ssize_t digits) +{ + mpd_ssize_t q, r; + + _mpd_idiv_word(&q, &r, digits, MPD_RDIGITS); + return (r == 0) ? q : q+1; +} + +/* Number of digits in the exponent. Not defined for MPD_SSIZE_MIN. */ +inline int +mpd_exp_digits(mpd_ssize_t exp) +{ + exp = (exp < 0) ? -exp : exp; + return mpd_word_digits(exp); +} + +/* Canonical */ +ALWAYS_INLINE int +mpd_iscanonical(const mpd_t *dec UNUSED) +{ + return 1; +} + +/* Finite */ +ALWAYS_INLINE int +mpd_isfinite(const mpd_t *dec) +{ + return !(dec->flags & MPD_SPECIAL); +} + +/* Infinite */ +ALWAYS_INLINE int +mpd_isinfinite(const mpd_t *dec) +{ + return dec->flags & MPD_INF; +} + +/* NaN */ +ALWAYS_INLINE int +mpd_isnan(const mpd_t *dec) +{ + return dec->flags & (MPD_NAN|MPD_SNAN); +} + +/* Negative */ +ALWAYS_INLINE int +mpd_isnegative(const mpd_t *dec) +{ + return dec->flags & MPD_NEG; +} + +/* Positive */ +ALWAYS_INLINE int +mpd_ispositive(const mpd_t *dec) +{ + return !(dec->flags & MPD_NEG); +} + +/* qNaN */ +ALWAYS_INLINE int +mpd_isqnan(const mpd_t *dec) +{ + return dec->flags & MPD_NAN; +} + +/* Signed */ +ALWAYS_INLINE int +mpd_issigned(const mpd_t *dec) +{ + return dec->flags & MPD_NEG; +} + +/* sNaN */ +ALWAYS_INLINE int +mpd_issnan(const mpd_t *dec) +{ + return dec->flags & MPD_SNAN; +} + +/* Special */ +ALWAYS_INLINE int +mpd_isspecial(const mpd_t *dec) +{ + return dec->flags & MPD_SPECIAL; +} + +/* Zero */ +ALWAYS_INLINE int +mpd_iszero(const mpd_t *dec) +{ + return !mpd_isspecial(dec) && mpd_msword(dec) == 0; +} + +/* Test for zero when specials have been ruled out already */ +ALWAYS_INLINE int +mpd_iszerocoeff(const mpd_t *dec) +{ + return mpd_msword(dec) == 0; +} + +/* Normal */ +inline int +mpd_isnormal(const mpd_t *dec, const mpd_context_t *ctx) +{ + if (mpd_isspecial(dec)) return 0; + if (mpd_iszerocoeff(dec)) return 0; + + return mpd_adjexp(dec) >= ctx->emin; +} + +/* Subnormal */ +inline int +mpd_issubnormal(const mpd_t *dec, const mpd_context_t *ctx) +{ + if (mpd_isspecial(dec)) return 0; + if (mpd_iszerocoeff(dec)) return 0; + + return mpd_adjexp(dec) < ctx->emin; +} + +/* Odd word */ +ALWAYS_INLINE int +mpd_isoddword(mpd_uint_t word) +{ + return word & 1; +} + +/* Odd coefficient */ +ALWAYS_INLINE int +mpd_isoddcoeff(const mpd_t *dec) +{ + return mpd_isoddword(dec->data[0]); +} + +/* 0 if dec is positive, 1 if dec is negative */ +ALWAYS_INLINE uint8_t +mpd_sign(const mpd_t *dec) +{ + return dec->flags & MPD_NEG; +} + +/* 1 if dec is positive, -1 if dec is negative */ +ALWAYS_INLINE int +mpd_arith_sign(const mpd_t *dec) +{ + return 1 - 2 * mpd_isnegative(dec); +} + +/* Radix */ +ALWAYS_INLINE long +mpd_radix(void) +{ + return 10; +} + +/* Dynamic decimal */ +ALWAYS_INLINE int +mpd_isdynamic(const mpd_t *dec) +{ + return !(dec->flags & MPD_STATIC); +} + +/* Static decimal */ +ALWAYS_INLINE int +mpd_isstatic(const mpd_t *dec) +{ + return dec->flags & MPD_STATIC; +} + +/* Data of decimal is dynamic */ +ALWAYS_INLINE int +mpd_isdynamic_data(const mpd_t *dec) +{ + return !(dec->flags & MPD_DATAFLAGS); +} + +/* Data of decimal is static */ +ALWAYS_INLINE int +mpd_isstatic_data(const mpd_t *dec) +{ + return dec->flags & MPD_STATIC_DATA; +} + +/* Data of decimal is shared */ +ALWAYS_INLINE int +mpd_isshared_data(const mpd_t *dec) +{ + return dec->flags & MPD_SHARED_DATA; +} + +/* Data of decimal is const */ +ALWAYS_INLINE int +mpd_isconst_data(const mpd_t *dec) +{ + return dec->flags & MPD_CONST_DATA; +} + + +/******************************************************************************/ +/* Inline memory handling */ +/******************************************************************************/ + +/* Fill destination with zeros */ +ALWAYS_INLINE void +mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len) +{ + mpd_size_t i; + + for (i = 0; i < len; i++) { + dest[i] = 0; + } +} + +/* Free a decimal */ +ALWAYS_INLINE void +mpd_del(mpd_t *dec) +{ + if (mpd_isdynamic_data(dec)) { + mpd_free(dec->data); + } + if (mpd_isdynamic(dec)) { + mpd_free(dec); + } +} + +/* + * Resize the coefficient. Existing data up to 'nwords' is left untouched. + * Return 1 on success, 0 otherwise. + * + * Input invariant: MPD_MINALLOC <= result->alloc. + * + * Case nwords == result->alloc: + * 'result' is unchanged. Return 1. + * + * Case nwords > result->alloc: + * Case realloc success: + * The value of 'result' does not change. Return 1. + * Case realloc failure: + * 'result' is NaN, status is updated with MPD_Malloc_error. Return 0. + * + * Case nwords < result->alloc: + * Case is_static_data or realloc failure [1]: + * 'result' is unchanged. Return 1. + * Case realloc success: + * The value of result is undefined (expected). Return 1. + * + * + * [1] In that case the old (now oversized) area is still valid. + */ +ALWAYS_INLINE int +mpd_qresize(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) +{ + assert(!mpd_isconst_data(result)); /* illegal operation for a const */ + assert(!mpd_isshared_data(result)); /* illegal operation for a shared */ + assert(MPD_MINALLOC <= result->alloc); + + nwords = (nwords <= MPD_MINALLOC) ? MPD_MINALLOC : nwords; + if (nwords == result->alloc) { + return 1; + } + if (mpd_isstatic_data(result)) { + if (nwords > result->alloc) { + return mpd_switch_to_dyn(result, nwords, status); + } + return 1; + } + + return mpd_realloc_dyn(result, nwords, status); +} + +/* Same as mpd_qresize, but the complete coefficient (including the old + * memory area!) is initialized to zero. */ +ALWAYS_INLINE int +mpd_qresize_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) +{ + assert(!mpd_isconst_data(result)); /* illegal operation for a const */ + assert(!mpd_isshared_data(result)); /* illegal operation for a shared */ + assert(MPD_MINALLOC <= result->alloc); + + nwords = (nwords <= MPD_MINALLOC) ? MPD_MINALLOC : nwords; + if (nwords != result->alloc) { + if (mpd_isstatic_data(result)) { + if (nwords > result->alloc) { + return mpd_switch_to_dyn_zero(result, nwords, status); + } + } + else if (!mpd_realloc_dyn(result, nwords, status)) { + return 0; + } + } + + mpd_uint_zero(result->data, nwords); + return 1; +} + +/* + * Reduce memory size for the coefficient to MPD_MINALLOC. In theory, + * realloc may fail even when reducing the memory size. But in that case + * the old memory area is always big enough, so checking for MPD_Malloc_error + * is not imperative. + */ +ALWAYS_INLINE void +mpd_minalloc(mpd_t *result) +{ + assert(!mpd_isconst_data(result)); /* illegal operation for a const */ + assert(!mpd_isshared_data(result)); /* illegal operation for a shared */ + + if (!mpd_isstatic_data(result) && result->alloc > MPD_MINALLOC) { + uint8_t err = 0; + result->data = mpd_realloc(result->data, MPD_MINALLOC, + sizeof *result->data, &err); + if (!err) { + result->alloc = MPD_MINALLOC; + } + } +} + +int +mpd_resize(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx) +{ + uint32_t status = 0; + if (!mpd_qresize(result, nwords, &status)) { + mpd_addstatus_raise(ctx, status); + return 0; + } + return 1; +} + +int +mpd_resize_zero(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx) +{ + uint32_t status = 0; + if (!mpd_qresize_zero(result, nwords, &status)) { + mpd_addstatus_raise(ctx, status); + return 0; + } + return 1; +} + + +/******************************************************************************/ +/* Set attributes of a decimal */ +/******************************************************************************/ + +/* Set digits. Assumption: result->len is initialized and > 0. */ +inline void +mpd_setdigits(mpd_t *result) +{ + mpd_ssize_t wdigits = mpd_word_digits(mpd_msword(result)); + result->digits = wdigits + (result->len-1) * MPD_RDIGITS; +} + +/* Set sign */ +ALWAYS_INLINE void +mpd_set_sign(mpd_t *result, uint8_t sign) +{ + result->flags &= ~MPD_NEG; + result->flags |= sign; +} + +/* Copy sign from another decimal */ +ALWAYS_INLINE void +mpd_signcpy(mpd_t *result, const mpd_t *a) +{ + uint8_t sign = a->flags&MPD_NEG; + + result->flags &= ~MPD_NEG; + result->flags |= sign; +} + +/* Set infinity */ +ALWAYS_INLINE void +mpd_set_infinity(mpd_t *result) +{ + result->flags &= ~MPD_SPECIAL; + result->flags |= MPD_INF; +} + +/* Set qNaN */ +ALWAYS_INLINE void +mpd_set_qnan(mpd_t *result) +{ + result->flags &= ~MPD_SPECIAL; + result->flags |= MPD_NAN; +} + +/* Set sNaN */ +ALWAYS_INLINE void +mpd_set_snan(mpd_t *result) +{ + result->flags &= ~MPD_SPECIAL; + result->flags |= MPD_SNAN; +} + +/* Set to negative */ +ALWAYS_INLINE void +mpd_set_negative(mpd_t *result) +{ + result->flags |= MPD_NEG; +} + +/* Set to positive */ +ALWAYS_INLINE void +mpd_set_positive(mpd_t *result) +{ + result->flags &= ~MPD_NEG; +} + +/* Set to dynamic */ +ALWAYS_INLINE void +mpd_set_dynamic(mpd_t *result) +{ + result->flags &= ~MPD_STATIC; +} + +/* Set to static */ +ALWAYS_INLINE void +mpd_set_static(mpd_t *result) +{ + result->flags |= MPD_STATIC; +} + +/* Set data to dynamic */ +ALWAYS_INLINE void +mpd_set_dynamic_data(mpd_t *result) +{ + result->flags &= ~MPD_DATAFLAGS; +} + +/* Set data to static */ +ALWAYS_INLINE void +mpd_set_static_data(mpd_t *result) +{ + result->flags &= ~MPD_DATAFLAGS; + result->flags |= MPD_STATIC_DATA; +} + +/* Set data to shared */ +ALWAYS_INLINE void +mpd_set_shared_data(mpd_t *result) +{ + result->flags &= ~MPD_DATAFLAGS; + result->flags |= MPD_SHARED_DATA; +} + +/* Set data to const */ +ALWAYS_INLINE void +mpd_set_const_data(mpd_t *result) +{ + result->flags &= ~MPD_DATAFLAGS; + result->flags |= MPD_CONST_DATA; +} + +/* Clear flags, preserving memory attributes. */ +ALWAYS_INLINE void +mpd_clear_flags(mpd_t *result) +{ + result->flags &= (MPD_STATIC|MPD_DATAFLAGS); +} + +/* Set flags, preserving memory attributes. */ +ALWAYS_INLINE void +mpd_set_flags(mpd_t *result, uint8_t flags) +{ + result->flags &= (MPD_STATIC|MPD_DATAFLAGS); + result->flags |= flags; +} + +/* Copy flags, preserving memory attributes of result. */ +ALWAYS_INLINE void +mpd_copy_flags(mpd_t *result, const mpd_t *a) +{ + uint8_t aflags = a->flags; + result->flags &= (MPD_STATIC|MPD_DATAFLAGS); + result->flags |= (aflags & ~(MPD_STATIC|MPD_DATAFLAGS)); +} + +/* Initialize a workcontext from ctx. Set traps, flags and newtrap to 0. */ +static inline void +mpd_workcontext(mpd_context_t *workctx, const mpd_context_t *ctx) +{ + workctx->prec = ctx->prec; + workctx->emax = ctx->emax; + workctx->emin = ctx->emin; + workctx->round = ctx->round; + workctx->traps = 0; + workctx->status = 0; + workctx->newtrap = 0; + workctx->clamp = ctx->clamp; + workctx->allcr = ctx->allcr; +} + + +/******************************************************************************/ +/* Getting and setting parts of decimals */ +/******************************************************************************/ + +/* Flip the sign of a decimal */ +static inline void +_mpd_negate(mpd_t *dec) +{ + dec->flags ^= MPD_NEG; +} + +/* Set coefficient to zero */ +void +mpd_zerocoeff(mpd_t *result) +{ + mpd_minalloc(result); + result->digits = 1; + result->len = 1; + result->data[0] = 0; +} + +/* Set the coefficient to all nines. */ +void +mpd_qmaxcoeff(mpd_t *result, const mpd_context_t *ctx, uint32_t *status) +{ + mpd_ssize_t len, r; + + _mpd_idiv_word(&len, &r, ctx->prec, MPD_RDIGITS); + len = (r == 0) ? len : len+1; + + if (!mpd_qresize(result, len, status)) { + return; + } + + result->len = len; + result->digits = ctx->prec; + + --len; + if (r > 0) { + result->data[len--] = mpd_pow10[r]-1; + } + for (; len >= 0; --len) { + result->data[len] = MPD_RADIX-1; + } +} + +/* + * Cut off the most significant digits so that the rest fits in ctx->prec. + * Cannot fail. + */ +static void +_mpd_cap(mpd_t *result, const mpd_context_t *ctx) +{ + uint32_t dummy; + mpd_ssize_t len, r; + + if (result->len > 0 && result->digits > ctx->prec) { + _mpd_idiv_word(&len, &r, ctx->prec, MPD_RDIGITS); + len = (r == 0) ? len : len+1; + + if (r != 0) { + result->data[len-1] %= mpd_pow10[r]; + } + + len = _mpd_real_size(result->data, len); + /* resize to fewer words cannot fail */ + mpd_qresize(result, len, &dummy); + result->len = len; + mpd_setdigits(result); + } + if (mpd_iszero(result)) { + _settriple(result, mpd_sign(result), 0, result->exp); + } +} + +/* + * Cut off the most significant digits of a NaN payload so that the rest + * fits in ctx->prec - ctx->clamp. Cannot fail. + */ +static void +_mpd_fix_nan(mpd_t *result, const mpd_context_t *ctx) +{ + uint32_t dummy; + mpd_ssize_t prec; + mpd_ssize_t len, r; + + prec = ctx->prec - ctx->clamp; + if (result->len > 0 && result->digits > prec) { + if (prec == 0) { + mpd_minalloc(result); + result->len = result->digits = 0; + } + else { + _mpd_idiv_word(&len, &r, prec, MPD_RDIGITS); + len = (r == 0) ? len : len+1; + + if (r != 0) { + result->data[len-1] %= mpd_pow10[r]; + } + + len = _mpd_real_size(result->data, len); + /* resize to fewer words cannot fail */ + mpd_qresize(result, len, &dummy); + result->len = len; + mpd_setdigits(result); + if (mpd_iszerocoeff(result)) { + /* NaN0 is not a valid representation */ + result->len = result->digits = 0; + } + } + } +} + +/* + * Get n most significant digits from a decimal, where 0 < n <= MPD_UINT_DIGITS. + * Assumes MPD_UINT_DIGITS == MPD_RDIGITS+1, which is true for 32 and 64 bit + * machines. + * + * The result of the operation will be in lo. If the operation is impossible, + * hi will be nonzero. This is used to indicate an error. + */ +static inline void +_mpd_get_msdigits(mpd_uint_t *hi, mpd_uint_t *lo, const mpd_t *dec, + unsigned int n) +{ + mpd_uint_t r, tmp; + + assert(0 < n && n <= MPD_RDIGITS+1); + + _mpd_div_word(&tmp, &r, dec->digits, MPD_RDIGITS); + r = (r == 0) ? MPD_RDIGITS : r; /* digits in the most significant word */ + + *hi = 0; + *lo = dec->data[dec->len-1]; + if (n <= r) { + *lo /= mpd_pow10[r-n]; + } + else if (dec->len > 1) { + /* at this point 1 <= r < n <= MPD_RDIGITS+1 */ + _mpd_mul_words(hi, lo, *lo, mpd_pow10[n-r]); + tmp = dec->data[dec->len-2] / mpd_pow10[MPD_RDIGITS-(n-r)]; + *lo = *lo + tmp; + if (*lo < tmp) (*hi)++; + } +} + + +/******************************************************************************/ +/* Gathering information about a decimal */ +/******************************************************************************/ + +/* The real size of the coefficient without leading zero words. */ +static inline mpd_ssize_t +_mpd_real_size(mpd_uint_t *data, mpd_ssize_t size) +{ + while (size > 1 && data[size-1] == 0) { + size--; + } + + return size; +} + +/* Return number of trailing zeros. No errors are possible. */ +mpd_ssize_t +mpd_trail_zeros(const mpd_t *dec) +{ + mpd_uint_t word; + mpd_ssize_t i, tz = 0; + + for (i=0; i < dec->len; ++i) { + if (dec->data[i] != 0) { + word = dec->data[i]; + tz = i * MPD_RDIGITS; + while (word % 10 == 0) { + word /= 10; + tz++; + } + break; + } + } + + return tz; +} + +/* Integer: Undefined for specials */ +static int +_mpd_isint(const mpd_t *dec) +{ + mpd_ssize_t tz; + + if (mpd_iszerocoeff(dec)) { + return 1; + } + + tz = mpd_trail_zeros(dec); + return (dec->exp + tz >= 0); +} + +/* Integer */ +int +mpd_isinteger(const mpd_t *dec) +{ + if (mpd_isspecial(dec)) { + return 0; + } + return _mpd_isint(dec); +} + +/* Word is a power of 10 */ +static int +mpd_word_ispow10(mpd_uint_t word) +{ + int n; + + n = mpd_word_digits(word); + if (word == mpd_pow10[n-1]) { + return 1; + } + + return 0; +} + +/* Coefficient is a power of 10 */ +static int +mpd_coeff_ispow10(const mpd_t *dec) +{ + if (mpd_word_ispow10(mpd_msword(dec))) { + if (_mpd_isallzero(dec->data, dec->len-1)) { + return 1; + } + } + + return 0; +} + +/* All digits of a word are nines */ +static int +mpd_word_isallnine(mpd_uint_t word) +{ + int n; + + n = mpd_word_digits(word); + if (word == mpd_pow10[n]-1) { + return 1; + } + + return 0; +} + +/* All digits of the coefficient are nines */ +static int +mpd_coeff_isallnine(const mpd_t *dec) +{ + if (mpd_word_isallnine(mpd_msword(dec))) { + if (_mpd_isallnine(dec->data, dec->len-1)) { + return 1; + } + } + + return 0; +} + +/* Odd decimal: Undefined for non-integers! */ +int +mpd_isodd(const mpd_t *dec) +{ + mpd_uint_t q, r; + assert(mpd_isinteger(dec)); + if (mpd_iszerocoeff(dec)) return 0; + if (dec->exp < 0) { + _mpd_div_word(&q, &r, -dec->exp, MPD_RDIGITS); + q = dec->data[q] / mpd_pow10[r]; + return mpd_isoddword(q); + } + return dec->exp == 0 && mpd_isoddword(dec->data[0]); +} + +/* Even: Undefined for non-integers! */ +int +mpd_iseven(const mpd_t *dec) +{ + return !mpd_isodd(dec); +} + +/******************************************************************************/ +/* Getting and setting decimals */ +/******************************************************************************/ + +/* Internal function: Set a static decimal from a triple, no error checking. */ +static void +_ssettriple(mpd_t *result, uint8_t sign, mpd_uint_t a, mpd_ssize_t exp) +{ + mpd_set_flags(result, sign); + result->exp = exp; + _mpd_div_word(&result->data[1], &result->data[0], a, MPD_RADIX); + result->len = (result->data[1] == 0) ? 1 : 2; + mpd_setdigits(result); +} + +/* Internal function: Set a decimal from a triple, no error checking. */ +static void +_settriple(mpd_t *result, uint8_t sign, mpd_uint_t a, mpd_ssize_t exp) +{ + mpd_minalloc(result); + mpd_set_flags(result, sign); + result->exp = exp; + _mpd_div_word(&result->data[1], &result->data[0], a, MPD_RADIX); + result->len = (result->data[1] == 0) ? 1 : 2; + mpd_setdigits(result); +} + +/* Set a special number from a triple */ +void +mpd_setspecial(mpd_t *result, uint8_t sign, uint8_t type) +{ + mpd_minalloc(result); + result->flags &= ~(MPD_NEG|MPD_SPECIAL); + result->flags |= (sign|type); + result->exp = result->digits = result->len = 0; +} + +/* Set result of NaN with an error status */ +void +mpd_seterror(mpd_t *result, uint32_t flags, uint32_t *status) +{ + mpd_minalloc(result); + mpd_set_qnan(result); + mpd_set_positive(result); + result->exp = result->digits = result->len = 0; + *status |= flags; +} + +/* quietly set a static decimal from an mpd_ssize_t */ +void +mpd_qsset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_uint_t u; + uint8_t sign = MPD_POS; + + if (a < 0) { + if (a == MPD_SSIZE_MIN) { + u = (mpd_uint_t)MPD_SSIZE_MAX + + (-(MPD_SSIZE_MIN+MPD_SSIZE_MAX)); + } + else { + u = -a; + } + sign = MPD_NEG; + } + else { + u = a; + } + _ssettriple(result, sign, u, 0); + mpd_qfinalize(result, ctx, status); +} + +/* quietly set a static decimal from an mpd_uint_t */ +void +mpd_qsset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, + uint32_t *status) +{ + _ssettriple(result, MPD_POS, a, 0); + mpd_qfinalize(result, ctx, status); +} + +/* quietly set a static decimal from an int32_t */ +void +mpd_qsset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_qsset_ssize(result, a, ctx, status); +} + +/* quietly set a static decimal from a uint32_t */ +void +mpd_qsset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_qsset_uint(result, a, ctx, status); +} + +#ifdef CONFIG_64 +/* quietly set a static decimal from an int64_t */ +void +mpd_qsset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_qsset_ssize(result, a, ctx, status); +} + +/* quietly set a static decimal from a uint64_t */ +void +mpd_qsset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_qsset_uint(result, a, ctx, status); +} +#endif + +/* quietly set a decimal from an mpd_ssize_t */ +void +mpd_qset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_minalloc(result); + mpd_qsset_ssize(result, a, ctx, status); +} + +/* quietly set a decimal from an mpd_uint_t */ +void +mpd_qset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, + uint32_t *status) +{ + _settriple(result, MPD_POS, a, 0); + mpd_qfinalize(result, ctx, status); +} + +/* quietly set a decimal from an int32_t */ +void +mpd_qset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_qset_ssize(result, a, ctx, status); +} + +/* quietly set a decimal from a uint32_t */ +void +mpd_qset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_qset_uint(result, a, ctx, status); +} + +#if defined(CONFIG_32) && !defined(LEGACY_COMPILER) +/* set a decimal from a uint64_t */ +static void +_c32setu64(mpd_t *result, uint64_t u, uint8_t sign, uint32_t *status) +{ + mpd_uint_t w[3]; + uint64_t q; + int i, len; + + len = 0; + do { + q = u / MPD_RADIX; + w[len] = (mpd_uint_t)(u - q * MPD_RADIX); + u = q; len++; + } while (u != 0); + + if (!mpd_qresize(result, len, status)) { + return; + } + for (i = 0; i < len; i++) { + result->data[i] = w[i]; + } + + mpd_set_sign(result, sign); + result->exp = 0; + result->len = len; + mpd_setdigits(result); +} + +static void +_c32_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, + uint32_t *status) +{ + _c32setu64(result, a, MPD_POS, status); + mpd_qfinalize(result, ctx, status); +} + +/* set a decimal from an int64_t */ +static void +_c32_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, + uint32_t *status) +{ + uint64_t u; + uint8_t sign = MPD_POS; + + if (a < 0) { + if (a == INT64_MIN) { + u = (uint64_t)INT64_MAX + (-(INT64_MIN+INT64_MAX)); + } + else { + u = -a; + } + sign = MPD_NEG; + } + else { + u = a; + } + _c32setu64(result, u, sign, status); + mpd_qfinalize(result, ctx, status); +} +#endif /* CONFIG_32 && !LEGACY_COMPILER */ + +#ifndef LEGACY_COMPILER +/* quietly set a decimal from an int64_t */ +void +mpd_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, + uint32_t *status) +{ +#ifdef CONFIG_64 + mpd_qset_ssize(result, a, ctx, status); +#else + _c32_qset_i64(result, a, ctx, status); +#endif +} + +/* quietly set a decimal from a uint64_t */ +void +mpd_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, + uint32_t *status) +{ +#ifdef CONFIG_64 + mpd_qset_uint(result, a, ctx, status); +#else + _c32_qset_u64(result, a, ctx, status); +#endif +} +#endif /* !LEGACY_COMPILER */ + + +/* + * Quietly get an mpd_uint_t from a decimal. Assumes + * MPD_UINT_DIGITS == MPD_RDIGITS+1, which is true for + * 32 and 64 bit machines. + * + * If the operation is impossible, MPD_Invalid_operation is set. + */ +static mpd_uint_t +_mpd_qget_uint(int use_sign, const mpd_t *a, uint32_t *status) +{ + mpd_t tmp; + mpd_uint_t tmp_data[2]; + mpd_uint_t lo, hi; + + if (mpd_isspecial(a)) { + *status |= MPD_Invalid_operation; + return MPD_UINT_MAX; + } + if (mpd_iszero(a)) { + return 0; + } + if (use_sign && mpd_isnegative(a)) { + *status |= MPD_Invalid_operation; + return MPD_UINT_MAX; + } + + if (a->digits+a->exp > MPD_RDIGITS+1) { + *status |= MPD_Invalid_operation; + return MPD_UINT_MAX; + } + + if (a->exp < 0) { + if (!_mpd_isint(a)) { + *status |= MPD_Invalid_operation; + return MPD_UINT_MAX; + } + /* At this point a->digits+a->exp <= MPD_RDIGITS+1, + * so the shift fits. */ + tmp.data = tmp_data; + tmp.flags = MPD_STATIC|MPD_STATIC_DATA; + tmp.alloc = 2; + mpd_qsshiftr(&tmp, a, -a->exp); + tmp.exp = 0; + a = &tmp; + } + + _mpd_get_msdigits(&hi, &lo, a, MPD_RDIGITS+1); + if (hi) { + *status |= MPD_Invalid_operation; + return MPD_UINT_MAX; + } + + if (a->exp > 0) { + _mpd_mul_words(&hi, &lo, lo, mpd_pow10[a->exp]); + if (hi) { + *status |= MPD_Invalid_operation; + return MPD_UINT_MAX; + } + } + + return lo; +} + +/* + * Sets Invalid_operation for: + * - specials + * - negative numbers (except negative zero) + * - non-integers + * - overflow + */ +mpd_uint_t +mpd_qget_uint(const mpd_t *a, uint32_t *status) +{ + return _mpd_qget_uint(1, a, status); +} + +/* Same as above, but gets the absolute value, i.e. the sign is ignored. */ +mpd_uint_t +mpd_qabs_uint(const mpd_t *a, uint32_t *status) +{ + return _mpd_qget_uint(0, a, status); +} + +/* quietly get an mpd_ssize_t from a decimal */ +mpd_ssize_t +mpd_qget_ssize(const mpd_t *a, uint32_t *status) +{ + mpd_uint_t u; + int isneg; + + u = mpd_qabs_uint(a, status); + if (*status&MPD_Invalid_operation) { + return MPD_SSIZE_MAX; + } + + isneg = mpd_isnegative(a); + if (u <= MPD_SSIZE_MAX) { + return isneg ? -((mpd_ssize_t)u) : (mpd_ssize_t)u; + } + else if (isneg && u+(MPD_SSIZE_MIN+MPD_SSIZE_MAX) == MPD_SSIZE_MAX) { + return MPD_SSIZE_MIN; + } + + *status |= MPD_Invalid_operation; + return MPD_SSIZE_MAX; +} + +#if defined(CONFIG_32) && !defined(LEGACY_COMPILER) +/* + * Quietly get a uint64_t from a decimal. If the operation is impossible, + * MPD_Invalid_operation is set. + */ +static uint64_t +_c32_qget_u64(int use_sign, const mpd_t *a, uint32_t *status) +{ + MPD_NEW_STATIC(tmp,0,0,20,3); + mpd_context_t maxcontext; + uint64_t ret; + + tmp_data[0] = 709551615; + tmp_data[1] = 446744073; + tmp_data[2] = 18; + + if (mpd_isspecial(a)) { + *status |= MPD_Invalid_operation; + return UINT64_MAX; + } + if (mpd_iszero(a)) { + return 0; + } + if (use_sign && mpd_isnegative(a)) { + *status |= MPD_Invalid_operation; + return UINT64_MAX; + } + if (!_mpd_isint(a)) { + *status |= MPD_Invalid_operation; + return UINT64_MAX; + } + + if (_mpd_cmp_abs(a, &tmp) > 0) { + *status |= MPD_Invalid_operation; + return UINT64_MAX; + } + + mpd_maxcontext(&maxcontext); + mpd_qrescale(&tmp, a, 0, &maxcontext, &maxcontext.status); + maxcontext.status &= ~MPD_Rounded; + if (maxcontext.status != 0) { + *status |= (maxcontext.status|MPD_Invalid_operation); /* GCOV_NOT_REACHED */ + return UINT64_MAX; /* GCOV_NOT_REACHED */ + } + + ret = 0; + switch (tmp.len) { + case 3: + ret += (uint64_t)tmp_data[2] * 1000000000000000000ULL; + case 2: + ret += (uint64_t)tmp_data[1] * 1000000000ULL; + case 1: + ret += tmp_data[0]; + break; + default: + abort(); /* GCOV_NOT_REACHED */ + } + + return ret; +} + +static int64_t +_c32_qget_i64(const mpd_t *a, uint32_t *status) +{ + uint64_t u; + int isneg; + + u = _c32_qget_u64(0, a, status); + if (*status&MPD_Invalid_operation) { + return INT64_MAX; + } + + isneg = mpd_isnegative(a); + if (u <= INT64_MAX) { + return isneg ? -((int64_t)u) : (int64_t)u; + } + else if (isneg && u+(INT64_MIN+INT64_MAX) == INT64_MAX) { + return INT64_MIN; + } + + *status |= MPD_Invalid_operation; + return INT64_MAX; +} +#endif /* CONFIG_32 && !LEGACY_COMPILER */ + +#ifdef CONFIG_64 +/* quietly get a uint64_t from a decimal */ +uint64_t +mpd_qget_u64(const mpd_t *a, uint32_t *status) +{ + return mpd_qget_uint(a, status); +} + +/* quietly get an int64_t from a decimal */ +int64_t +mpd_qget_i64(const mpd_t *a, uint32_t *status) +{ + return mpd_qget_ssize(a, status); +} + +/* quietly get a uint32_t from a decimal */ +uint32_t +mpd_qget_u32(const mpd_t *a, uint32_t *status) +{ + uint64_t x = mpd_qget_uint(a, status); + + if (*status&MPD_Invalid_operation) { + return UINT32_MAX; + } + if (x > UINT32_MAX) { + *status |= MPD_Invalid_operation; + return UINT32_MAX; + } + + return (uint32_t)x; +} + +/* quietly get an int32_t from a decimal */ +int32_t +mpd_qget_i32(const mpd_t *a, uint32_t *status) +{ + int64_t x = mpd_qget_ssize(a, status); + + if (*status&MPD_Invalid_operation) { + return INT32_MAX; + } + if (x < INT32_MIN || x > INT32_MAX) { + *status |= MPD_Invalid_operation; + return INT32_MAX; + } + + return (int32_t)x; +} +#else +#ifndef LEGACY_COMPILER +/* quietly get a uint64_t from a decimal */ +uint64_t +mpd_qget_u64(const mpd_t *a, uint32_t *status) +{ + return _c32_qget_u64(1, a, status); +} + +/* quietly get an int64_t from a decimal */ +int64_t +mpd_qget_i64(const mpd_t *a, uint32_t *status) +{ + return _c32_qget_i64(a, status); +} +#endif + +/* quietly get a uint32_t from a decimal */ +uint32_t +mpd_qget_u32(const mpd_t *a, uint32_t *status) +{ + return mpd_qget_uint(a, status); +} + +/* quietly get an int32_t from a decimal */ +int32_t +mpd_qget_i32(const mpd_t *a, uint32_t *status) +{ + return mpd_qget_ssize(a, status); +} +#endif + + +/******************************************************************************/ +/* Filtering input of functions, finalizing output of functions */ +/******************************************************************************/ + +/* + * Check if the operand is NaN, copy to result and return 1 if this is + * the case. Copying can fail since NaNs are allowed to have a payload that + * does not fit in MPD_MINALLOC. + */ +int +mpd_qcheck_nan(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + if (mpd_isnan(a)) { + *status |= mpd_issnan(a) ? MPD_Invalid_operation : 0; + mpd_qcopy(result, a, status); + mpd_set_qnan(result); + _mpd_fix_nan(result, ctx); + return 1; + } + return 0; +} + +/* + * Check if either operand is NaN, copy to result and return 1 if this + * is the case. Copying can fail since NaNs are allowed to have a payload + * that does not fit in MPD_MINALLOC. + */ +int +mpd_qcheck_nans(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + if ((a->flags|b->flags)&(MPD_NAN|MPD_SNAN)) { + const mpd_t *choice = b; + if (mpd_issnan(a)) { + choice = a; + *status |= MPD_Invalid_operation; + } + else if (mpd_issnan(b)) { + *status |= MPD_Invalid_operation; + } + else if (mpd_isqnan(a)) { + choice = a; + } + mpd_qcopy(result, choice, status); + mpd_set_qnan(result); + _mpd_fix_nan(result, ctx); + return 1; + } + return 0; +} + +/* + * Check if one of the operands is NaN, copy to result and return 1 if this + * is the case. Copying can fail since NaNs are allowed to have a payload + * that does not fit in MPD_MINALLOC. + */ +static int +mpd_qcheck_3nans(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, + const mpd_context_t *ctx, uint32_t *status) +{ + if ((a->flags|b->flags|c->flags)&(MPD_NAN|MPD_SNAN)) { + const mpd_t *choice = c; + if (mpd_issnan(a)) { + choice = a; + *status |= MPD_Invalid_operation; + } + else if (mpd_issnan(b)) { + choice = b; + *status |= MPD_Invalid_operation; + } + else if (mpd_issnan(c)) { + *status |= MPD_Invalid_operation; + } + else if (mpd_isqnan(a)) { + choice = a; + } + else if (mpd_isqnan(b)) { + choice = b; + } + mpd_qcopy(result, choice, status); + mpd_set_qnan(result); + _mpd_fix_nan(result, ctx); + return 1; + } + return 0; +} + +/* Check if rounding digit 'rnd' leads to an increment. */ +static inline int +_mpd_rnd_incr(const mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx) +{ + int ld; + + switch (ctx->round) { + case MPD_ROUND_DOWN: case MPD_ROUND_TRUNC: + return 0; + case MPD_ROUND_HALF_UP: + return (rnd >= 5); + case MPD_ROUND_HALF_EVEN: + return (rnd > 5) || ((rnd == 5) && mpd_isoddcoeff(dec)); + case MPD_ROUND_CEILING: + return !(rnd == 0 || mpd_isnegative(dec)); + case MPD_ROUND_FLOOR: + return !(rnd == 0 || mpd_ispositive(dec)); + case MPD_ROUND_HALF_DOWN: + return (rnd > 5); + case MPD_ROUND_UP: + return !(rnd == 0); + case MPD_ROUND_05UP: + ld = (int)mpd_lsd(dec->data[0]); + return (!(rnd == 0) && (ld == 0 || ld == 5)); + default: + /* Without a valid context, further results will be undefined. */ + return 0; /* GCOV_NOT_REACHED */ + } +} + +/* + * Apply rounding to a decimal that has been right-shifted into a full + * precision decimal. If an increment leads to an overflow of the precision, + * adjust the coefficient and the exponent and check the new exponent for + * overflow. + */ +static inline void +_mpd_apply_round(mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx, + uint32_t *status) +{ + if (_mpd_rnd_incr(dec, rnd, ctx)) { + /* We have a number with exactly ctx->prec digits. The increment + * can only lead to an overflow if the decimal is all nines. In + * that case, the result is a power of ten with prec+1 digits. + * + * If the precision is a multiple of MPD_RDIGITS, this situation is + * detected by _mpd_baseincr returning a carry. + * If the precision is not a multiple of MPD_RDIGITS, we have to + * check if the result has one digit too many. + */ + mpd_uint_t carry = _mpd_baseincr(dec->data, dec->len); + if (carry) { + dec->data[dec->len-1] = mpd_pow10[MPD_RDIGITS-1]; + dec->exp += 1; + _mpd_check_exp(dec, ctx, status); + return; + } + mpd_setdigits(dec); + if (dec->digits > ctx->prec) { + mpd_qshiftr_inplace(dec, 1); + dec->exp += 1; + dec->digits = ctx->prec; + _mpd_check_exp(dec, ctx, status); + } + } +} + +/* + * Apply rounding to a decimal. Allow overflow of the precision. + */ +static inline void +_mpd_apply_round_excess(mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx, + uint32_t *status) +{ + if (_mpd_rnd_incr(dec, rnd, ctx)) { + mpd_uint_t carry = _mpd_baseincr(dec->data, dec->len); + if (carry) { + if (!mpd_qresize(dec, dec->len+1, status)) { + return; + } + dec->data[dec->len] = 1; + dec->len += 1; + } + mpd_setdigits(dec); + } +} + +/* + * Apply rounding to a decimal that has been right-shifted into a decimal + * with full precision or less. Return failure if an increment would + * overflow the precision. + */ +static inline int +_mpd_apply_round_fit(mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx, + uint32_t *status) +{ + if (_mpd_rnd_incr(dec, rnd, ctx)) { + mpd_uint_t carry = _mpd_baseincr(dec->data, dec->len); + if (carry) { + if (!mpd_qresize(dec, dec->len+1, status)) { + return 0; + } + dec->data[dec->len] = 1; + dec->len += 1; + } + mpd_setdigits(dec); + if (dec->digits > ctx->prec) { + mpd_seterror(dec, MPD_Invalid_operation, status); + return 0; + } + } + return 1; +} + +/* Check a normal number for overflow, underflow, clamping. If the operand + is modified, it will be zero, special or (sub)normal with a coefficient + that fits into the current context precision. */ +static inline void +_mpd_check_exp(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status) +{ + mpd_ssize_t adjexp, etiny, shift; + int rnd; + + adjexp = mpd_adjexp(dec); + if (adjexp > ctx->emax) { + + if (mpd_iszerocoeff(dec)) { + dec->exp = ctx->emax; + if (ctx->clamp) { + dec->exp -= (ctx->prec-1); + } + mpd_zerocoeff(dec); + *status |= MPD_Clamped; + return; + } + + switch (ctx->round) { + case MPD_ROUND_HALF_UP: case MPD_ROUND_HALF_EVEN: + case MPD_ROUND_HALF_DOWN: case MPD_ROUND_UP: + case MPD_ROUND_TRUNC: + mpd_setspecial(dec, mpd_sign(dec), MPD_INF); + break; + case MPD_ROUND_DOWN: case MPD_ROUND_05UP: + mpd_qmaxcoeff(dec, ctx, status); + dec->exp = ctx->emax - ctx->prec + 1; + break; + case MPD_ROUND_CEILING: + if (mpd_isnegative(dec)) { + mpd_qmaxcoeff(dec, ctx, status); + dec->exp = ctx->emax - ctx->prec + 1; + } + else { + mpd_setspecial(dec, MPD_POS, MPD_INF); + } + break; + case MPD_ROUND_FLOOR: + if (mpd_ispositive(dec)) { + mpd_qmaxcoeff(dec, ctx, status); + dec->exp = ctx->emax - ctx->prec + 1; + } + else { + mpd_setspecial(dec, MPD_NEG, MPD_INF); + } + break; + default: /* debug */ + abort(); /* GCOV_NOT_REACHED */ + } + + *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded; + + } /* fold down */ + else if (ctx->clamp && dec->exp > mpd_etop(ctx)) { + /* At this point adjexp=exp+digits-1 <= emax and exp > etop=emax-prec+1: + * (1) shift = exp -emax+prec-1 > 0 + * (2) digits+shift = exp+digits-1 - emax + prec <= prec */ + shift = dec->exp - mpd_etop(ctx); + if (!mpd_qshiftl(dec, dec, shift, status)) { + return; + } + dec->exp -= shift; + *status |= MPD_Clamped; + if (!mpd_iszerocoeff(dec) && adjexp < ctx->emin) { + /* Underflow is impossible, since exp < etiny=emin-prec+1 + * and exp > etop=emax-prec+1 would imply emax < emin. */ + *status |= MPD_Subnormal; + } + } + else if (adjexp < ctx->emin) { + + etiny = mpd_etiny(ctx); + + if (mpd_iszerocoeff(dec)) { + if (dec->exp < etiny) { + dec->exp = etiny; + mpd_zerocoeff(dec); + *status |= MPD_Clamped; + } + return; + } + + *status |= MPD_Subnormal; + if (dec->exp < etiny) { + /* At this point adjexp=exp+digits-1 < emin and exp < etiny=emin-prec+1: + * (1) shift = emin-prec+1 - exp > 0 + * (2) digits-shift = exp+digits-1 - emin + prec < prec */ + shift = etiny - dec->exp; + rnd = (int)mpd_qshiftr_inplace(dec, shift); + dec->exp = etiny; + /* We always have a spare digit in case of an increment. */ + _mpd_apply_round_excess(dec, rnd, ctx, status); + *status |= MPD_Rounded; + if (rnd) { + *status |= (MPD_Inexact|MPD_Underflow); + if (mpd_iszerocoeff(dec)) { + mpd_zerocoeff(dec); + *status |= MPD_Clamped; + } + } + } + /* Case exp >= etiny=emin-prec+1: + * (1) adjexp=exp+digits-1 < emin + * (2) digits < emin-exp+1 <= prec */ + } +} + +/* Transcendental functions do not always set Underflow reliably, + * since they only use as much precision as is necessary for correct + * rounding. If a result like 1.0000000000e-101 is finalized, there + * is no rounding digit that would trigger Underflow. But we can + * assume Inexact, so a short check suffices. */ +static inline void +mpd_check_underflow(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status) +{ + if (mpd_adjexp(dec) < ctx->emin && !mpd_iszero(dec) && + dec->exp < mpd_etiny(ctx)) { + *status |= MPD_Underflow; + } +} + +/* Check if a normal number must be rounded after the exponent has been checked. */ +static inline void +_mpd_check_round(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status) +{ + mpd_uint_t rnd; + mpd_ssize_t shift; + + /* must handle specials: _mpd_check_exp() can produce infinities or NaNs */ + if (mpd_isspecial(dec)) { + return; + } + + if (dec->digits > ctx->prec) { + shift = dec->digits - ctx->prec; + rnd = mpd_qshiftr_inplace(dec, shift); + dec->exp += shift; + _mpd_apply_round(dec, rnd, ctx, status); + *status |= MPD_Rounded; + if (rnd) { + *status |= MPD_Inexact; + } + } +} + +/* Finalize all operations. */ +void +mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status) +{ + if (mpd_isspecial(result)) { + if (mpd_isnan(result)) { + _mpd_fix_nan(result, ctx); + } + return; + } + + _mpd_check_exp(result, ctx, status); + _mpd_check_round(result, ctx, status); +} + + +/******************************************************************************/ +/* Copying */ +/******************************************************************************/ + +/* Internal function: Copy a decimal, share data with src: USE WITH CARE! */ +static inline void +_mpd_copy_shared(mpd_t *dest, const mpd_t *src) +{ + dest->flags = src->flags; + dest->exp = src->exp; + dest->digits = src->digits; + dest->len = src->len; + dest->alloc = src->alloc; + dest->data = src->data; + + mpd_set_shared_data(dest); +} + +/* + * Copy a decimal. In case of an error, status is set to MPD_Malloc_error. + */ +int +mpd_qcopy(mpd_t *result, const mpd_t *a, uint32_t *status) +{ + if (result == a) return 1; + + if (!mpd_qresize(result, a->len, status)) { + return 0; + } + + mpd_copy_flags(result, a); + result->exp = a->exp; + result->digits = a->digits; + result->len = a->len; + memcpy(result->data, a->data, a->len * (sizeof *result->data)); + + return 1; +} + +/* + * Copy to a decimal with a static buffer. The caller has to make sure that + * the buffer is big enough. Cannot fail. + */ +static void +mpd_qcopy_static(mpd_t *result, const mpd_t *a) +{ + if (result == a) return; + + memcpy(result->data, a->data, a->len * (sizeof *result->data)); + + mpd_copy_flags(result, a); + result->exp = a->exp; + result->digits = a->digits; + result->len = a->len; +} + +/* + * Return a newly allocated copy of the operand. In case of an error, + * status is set to MPD_Malloc_error and the return value is NULL. + */ +mpd_t * +mpd_qncopy(const mpd_t *a) +{ + mpd_t *result; + + if ((result = mpd_qnew_size(a->len)) == NULL) { + return NULL; + } + memcpy(result->data, a->data, a->len * (sizeof *result->data)); + mpd_copy_flags(result, a); + result->exp = a->exp; + result->digits = a->digits; + result->len = a->len; + + return result; +} + +/* + * Copy a decimal and set the sign to positive. In case of an error, the + * status is set to MPD_Malloc_error. + */ +int +mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status) +{ + if (!mpd_qcopy(result, a, status)) { + return 0; + } + mpd_set_positive(result); + return 1; +} + +/* + * Copy a decimal and negate the sign. In case of an error, the + * status is set to MPD_Malloc_error. + */ +int +mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status) +{ + if (!mpd_qcopy(result, a, status)) { + return 0; + } + _mpd_negate(result); + return 1; +} + +/* + * Copy a decimal, setting the sign of the first operand to the sign of the + * second operand. In case of an error, the status is set to MPD_Malloc_error. + */ +int +mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status) +{ + uint8_t sign_b = mpd_sign(b); /* result may equal b! */ + + if (!mpd_qcopy(result, a, status)) { + return 0; + } + mpd_set_sign(result, sign_b); + return 1; +} + + +/******************************************************************************/ +/* Comparisons */ +/******************************************************************************/ + +/* + * For all functions that compare two operands and return an int the usual + * convention applies to the return value: + * + * -1 if op1 < op2 + * 0 if op1 == op2 + * 1 if op1 > op2 + * + * INT_MAX for error + */ + + +/* Convenience macro. If a and b are not equal, return from the calling + * function with the correct comparison value. */ +#define CMP_EQUAL_OR_RETURN(a, b) \ + if (a != b) { \ + if (a < b) { \ + return -1; \ + } \ + return 1; \ + } + +/* + * Compare the data of big and small. This function does the equivalent + * of first shifting small to the left and then comparing the data of + * big and small, except that no allocation for the left shift is needed. + */ +static int +_mpd_basecmp(mpd_uint_t *big, mpd_uint_t *small, mpd_size_t n, mpd_size_t m, + mpd_size_t shift) +{ +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) + /* spurious uninitialized warnings */ + mpd_uint_t l=l, lprev=lprev, h=h; +#else + mpd_uint_t l, lprev, h; +#endif + mpd_uint_t q, r; + mpd_uint_t ph, x; + + assert(m > 0 && n >= m && shift > 0); + + _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS); + + if (r != 0) { + + ph = mpd_pow10[r]; + + --m; --n; + _mpd_divmod_pow10(&h, &lprev, small[m--], MPD_RDIGITS-r); + if (h != 0) { + CMP_EQUAL_OR_RETURN(big[n], h) + --n; + } + for (; m != MPD_SIZE_MAX; m--,n--) { + _mpd_divmod_pow10(&h, &l, small[m], MPD_RDIGITS-r); + x = ph * lprev + h; + CMP_EQUAL_OR_RETURN(big[n], x) + lprev = l; + } + x = ph * lprev; + CMP_EQUAL_OR_RETURN(big[q], x) + } + else { + while (--m != MPD_SIZE_MAX) { + CMP_EQUAL_OR_RETURN(big[m+q], small[m]) + } + } + + return !_mpd_isallzero(big, q); +} + +/* Compare two decimals with the same adjusted exponent. */ +static int +_mpd_cmp_same_adjexp(const mpd_t *a, const mpd_t *b) +{ + mpd_ssize_t shift, i; + + if (a->exp != b->exp) { + /* Cannot wrap: a->exp + a->digits = b->exp + b->digits, so + * a->exp - b->exp = b->digits - a->digits. */ + shift = a->exp - b->exp; + if (shift > 0) { + return -1 * _mpd_basecmp(b->data, a->data, b->len, a->len, shift); + } + else { + return _mpd_basecmp(a->data, b->data, a->len, b->len, -shift); + } + } + + /* + * At this point adjexp(a) == adjexp(b) and a->exp == b->exp, + * so a->digits == b->digits, therefore a->len == b->len. + */ + for (i = a->len-1; i >= 0; --i) { + CMP_EQUAL_OR_RETURN(a->data[i], b->data[i]) + } + + return 0; +} + +/* Compare two numerical values. */ +static int +_mpd_cmp(const mpd_t *a, const mpd_t *b) +{ + mpd_ssize_t adjexp_a, adjexp_b; + + /* equal pointers */ + if (a == b) { + return 0; + } + + /* infinities */ + if (mpd_isinfinite(a)) { + if (mpd_isinfinite(b)) { + return mpd_isnegative(b) - mpd_isnegative(a); + } + return mpd_arith_sign(a); + } + if (mpd_isinfinite(b)) { + return -mpd_arith_sign(b); + } + + /* zeros */ + if (mpd_iszerocoeff(a)) { + if (mpd_iszerocoeff(b)) { + return 0; + } + return -mpd_arith_sign(b); + } + if (mpd_iszerocoeff(b)) { + return mpd_arith_sign(a); + } + + /* different signs */ + if (mpd_sign(a) != mpd_sign(b)) { + return mpd_sign(b) - mpd_sign(a); + } + + /* different adjusted exponents */ + adjexp_a = mpd_adjexp(a); + adjexp_b = mpd_adjexp(b); + if (adjexp_a != adjexp_b) { + if (adjexp_a < adjexp_b) { + return -1 * mpd_arith_sign(a); + } + return mpd_arith_sign(a); + } + + /* same adjusted exponents */ + return _mpd_cmp_same_adjexp(a, b) * mpd_arith_sign(a); +} + +/* Compare the absolutes of two numerical values. */ +static int +_mpd_cmp_abs(const mpd_t *a, const mpd_t *b) +{ + mpd_ssize_t adjexp_a, adjexp_b; + + /* equal pointers */ + if (a == b) { + return 0; + } + + /* infinities */ + if (mpd_isinfinite(a)) { + if (mpd_isinfinite(b)) { + return 0; + } + return 1; + } + if (mpd_isinfinite(b)) { + return -1; + } + + /* zeros */ + if (mpd_iszerocoeff(a)) { + if (mpd_iszerocoeff(b)) { + return 0; + } + return -1; + } + if (mpd_iszerocoeff(b)) { + return 1; + } + + /* different adjusted exponents */ + adjexp_a = mpd_adjexp(a); + adjexp_b = mpd_adjexp(b); + if (adjexp_a != adjexp_b) { + if (adjexp_a < adjexp_b) { + return -1; + } + return 1; + } + + /* same adjusted exponents */ + return _mpd_cmp_same_adjexp(a, b); +} + +/* Compare two values and return an integer result. */ +int +mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status) +{ + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_isnan(a) || mpd_isnan(b)) { + *status |= MPD_Invalid_operation; + return INT_MAX; + } + } + + return _mpd_cmp(a, b); +} + +/* + * Compare a and b, convert the usual integer result to a decimal and + * store it in 'result'. For convenience, the integer result of the comparison + * is returned. Comparisons involving NaNs return NaN/INT_MAX. + */ +int +mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + int c; + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return INT_MAX; + } + } + + c = _mpd_cmp(a, b); + _settriple(result, (c < 0), (c != 0), 0); + return c; +} + +/* Same as mpd_compare(), but signal for all NaNs, i.e. also for quiet NaNs. */ +int +mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + int c; + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + *status |= MPD_Invalid_operation; + return INT_MAX; + } + } + + c = _mpd_cmp(a, b); + _settriple(result, (c < 0), (c != 0), 0); + return c; +} + +/* Compare the operands using a total order. */ +int +mpd_cmp_total(const mpd_t *a, const mpd_t *b) +{ + mpd_t aa, bb; + int nan_a, nan_b; + int c; + + if (mpd_sign(a) != mpd_sign(b)) { + return mpd_sign(b) - mpd_sign(a); + } + + + if (mpd_isnan(a)) { + c = 1; + if (mpd_isnan(b)) { + nan_a = (mpd_isqnan(a)) ? 1 : 0; + nan_b = (mpd_isqnan(b)) ? 1 : 0; + if (nan_b == nan_a) { + if (a->len > 0 && b->len > 0) { + _mpd_copy_shared(&aa, a); + _mpd_copy_shared(&bb, b); + aa.exp = bb.exp = 0; + /* compare payload */ + c = _mpd_cmp_abs(&aa, &bb); + } + else { + c = (a->len > 0) - (b->len > 0); + } + } + else { + c = nan_a - nan_b; + } + } + } + else if (mpd_isnan(b)) { + c = -1; + } + else { + c = _mpd_cmp_abs(a, b); + if (c == 0 && a->exp != b->exp) { + c = (a->exp < b->exp) ? -1 : 1; + } + } + + return c * mpd_arith_sign(a); +} + +/* + * Compare a and b according to a total order, convert the usual integer result + * to a decimal and store it in 'result'. For convenience, the integer result + * of the comparison is returned. + */ +int +mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b) +{ + int c; + + c = mpd_cmp_total(a, b); + _settriple(result, (c < 0), (c != 0), 0); + return c; +} + +/* Compare the magnitude of the operands using a total order. */ +int +mpd_cmp_total_mag(const mpd_t *a, const mpd_t *b) +{ + mpd_t aa, bb; + + _mpd_copy_shared(&aa, a); + _mpd_copy_shared(&bb, b); + + mpd_set_positive(&aa); + mpd_set_positive(&bb); + + return mpd_cmp_total(&aa, &bb); +} + +/* + * Compare the magnitude of a and b according to a total order, convert the + * the usual integer result to a decimal and store it in 'result'. + * For convenience, the integer result of the comparison is returned. + */ +int +mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b) +{ + int c; + + c = mpd_cmp_total_mag(a, b); + _settriple(result, (c < 0), (c != 0), 0); + return c; +} + +/* Determine an ordering for operands that are numerically equal. */ +static inline int +_mpd_cmp_numequal(const mpd_t *a, const mpd_t *b) +{ + int sign_a, sign_b; + int c; + + sign_a = mpd_sign(a); + sign_b = mpd_sign(b); + if (sign_a != sign_b) { + c = sign_b - sign_a; + } + else { + c = (a->exp < b->exp) ? -1 : 1; + c *= mpd_arith_sign(a); + } + + return c; +} + + +/******************************************************************************/ +/* Shifting the coefficient */ +/******************************************************************************/ + +/* + * Shift the coefficient of the operand to the left, no check for specials. + * Both operands may be the same pointer. If the result length has to be + * increased, mpd_qresize() might fail with MPD_Malloc_error. + */ +int +mpd_qshiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status) +{ + mpd_ssize_t size; + + assert(!mpd_isspecial(a)); + assert(n >= 0); + + if (mpd_iszerocoeff(a) || n == 0) { + return mpd_qcopy(result, a, status); + } + + size = mpd_digits_to_size(a->digits+n); + if (!mpd_qresize(result, size, status)) { + return 0; /* result is NaN */ + } + + _mpd_baseshiftl(result->data, a->data, size, a->len, n); + + mpd_copy_flags(result, a); + result->exp = a->exp; + result->digits = a->digits+n; + result->len = size; + + return 1; +} + +/* Determine the rounding indicator if all digits of the coefficient are shifted + * out of the picture. */ +static mpd_uint_t +_mpd_get_rnd(const mpd_uint_t *data, mpd_ssize_t len, int use_msd) +{ + mpd_uint_t rnd = 0, rest = 0, word; + + word = data[len-1]; + /* special treatment for the most significant digit if shift == digits */ + if (use_msd) { + _mpd_divmod_pow10(&rnd, &rest, word, mpd_word_digits(word)-1); + if (len > 1 && rest == 0) { + rest = !_mpd_isallzero(data, len-1); + } + } + else { + rest = !_mpd_isallzero(data, len); + } + + return (rnd == 0 || rnd == 5) ? rnd + !!rest : rnd; +} + +/* + * Same as mpd_qshiftr(), but 'result' is an mpd_t with a static coefficient. + * It is the caller's responsibility to ensure that the coefficient is big + * enough. The function cannot fail. + */ +static mpd_uint_t +mpd_qsshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n) +{ + mpd_uint_t rnd; + mpd_ssize_t size; + + assert(!mpd_isspecial(a)); + assert(n >= 0); + + if (mpd_iszerocoeff(a) || n == 0) { + mpd_qcopy_static(result, a); + return 0; + } + + if (n >= a->digits) { + rnd = _mpd_get_rnd(a->data, a->len, (n==a->digits)); + mpd_zerocoeff(result); + } + else { + result->digits = a->digits-n; + size = mpd_digits_to_size(result->digits); + rnd = _mpd_baseshiftr(result->data, a->data, a->len, n); + result->len = size; + } + + mpd_copy_flags(result, a); + result->exp = a->exp; + + return rnd; +} + +/* + * Inplace shift of the coefficient to the right, no check for specials. + * Returns the rounding indicator for mpd_rnd_incr(). + * The function cannot fail. + */ +mpd_uint_t +mpd_qshiftr_inplace(mpd_t *result, mpd_ssize_t n) +{ + uint32_t dummy; + mpd_uint_t rnd; + mpd_ssize_t size; + + assert(!mpd_isspecial(result)); + assert(n >= 0); + + if (mpd_iszerocoeff(result) || n == 0) { + return 0; + } + + if (n >= result->digits) { + rnd = _mpd_get_rnd(result->data, result->len, (n==result->digits)); + mpd_zerocoeff(result); + } + else { + rnd = _mpd_baseshiftr(result->data, result->data, result->len, n); + result->digits -= n; + size = mpd_digits_to_size(result->digits); + /* reducing the size cannot fail */ + mpd_qresize(result, size, &dummy); + result->len = size; + } + + return rnd; +} + +/* + * Shift the coefficient of the operand to the right, no check for specials. + * Both operands may be the same pointer. Returns the rounding indicator to + * be used by mpd_rnd_incr(). If the result length has to be increased, + * mpd_qcopy() or mpd_qresize() might fail with MPD_Malloc_error. In those + * cases, MPD_UINT_MAX is returned. + */ +mpd_uint_t +mpd_qshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status) +{ + mpd_uint_t rnd; + mpd_ssize_t size; + + assert(!mpd_isspecial(a)); + assert(n >= 0); + + if (mpd_iszerocoeff(a) || n == 0) { + if (!mpd_qcopy(result, a, status)) { + return MPD_UINT_MAX; + } + return 0; + } + + if (n >= a->digits) { + rnd = _mpd_get_rnd(a->data, a->len, (n==a->digits)); + mpd_zerocoeff(result); + } + else { + result->digits = a->digits-n; + size = mpd_digits_to_size(result->digits); + if (result == a) { + rnd = _mpd_baseshiftr(result->data, a->data, a->len, n); + /* reducing the size cannot fail */ + mpd_qresize(result, size, status); + } + else { + if (!mpd_qresize(result, size, status)) { + return MPD_UINT_MAX; + } + rnd = _mpd_baseshiftr(result->data, a->data, a->len, n); + } + result->len = size; + } + + mpd_copy_flags(result, a); + result->exp = a->exp; + + return rnd; +} + + +/******************************************************************************/ +/* Miscellaneous operations */ +/******************************************************************************/ + +/* Logical And */ +void +mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + const mpd_t *big = a, *small = b; + mpd_uint_t x, y, z, xbit, ybit; + int k, mswdigits; + mpd_ssize_t i; + + if (mpd_isspecial(a) || mpd_isspecial(b) || + mpd_isnegative(a) || mpd_isnegative(b) || + a->exp != 0 || b->exp != 0) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (b->digits > a->digits) { + big = b; + small = a; + } + if (!mpd_qresize(result, big->len, status)) { + return; + } + + + /* full words */ + for (i = 0; i < small->len-1; i++) { + x = small->data[i]; + y = big->data[i]; + z = 0; + for (k = 0; k < MPD_RDIGITS; k++) { + xbit = x % 10; + x /= 10; + ybit = y % 10; + y /= 10; + if (xbit > 1 || ybit > 1) { + goto invalid_operation; + } + z += (xbit&ybit) ? mpd_pow10[k] : 0; + } + result->data[i] = z; + } + /* most significant word of small */ + x = small->data[i]; + y = big->data[i]; + z = 0; + mswdigits = mpd_word_digits(x); + for (k = 0; k < mswdigits; k++) { + xbit = x % 10; + x /= 10; + ybit = y % 10; + y /= 10; + if (xbit > 1 || ybit > 1) { + goto invalid_operation; + } + z += (xbit&ybit) ? mpd_pow10[k] : 0; + } + result->data[i++] = z; + + /* scan the rest of y for digits > 1 */ + for (; k < MPD_RDIGITS; k++) { + ybit = y % 10; + y /= 10; + if (ybit > 1) { + goto invalid_operation; + } + } + /* scan the rest of big for digits > 1 */ + for (; i < big->len; i++) { + y = big->data[i]; + for (k = 0; k < MPD_RDIGITS; k++) { + ybit = y % 10; + y /= 10; + if (ybit > 1) { + goto invalid_operation; + } + } + } + + mpd_clear_flags(result); + result->exp = 0; + result->len = _mpd_real_size(result->data, small->len); + mpd_qresize(result, result->len, status); + mpd_setdigits(result); + _mpd_cap(result, ctx); + return; + +invalid_operation: + mpd_seterror(result, MPD_Invalid_operation, status); +} + +/* Class of an operand. Returns a pointer to the constant name. */ +const char * +mpd_class(const mpd_t *a, const mpd_context_t *ctx) +{ + if (mpd_isnan(a)) { + if (mpd_isqnan(a)) + return "NaN"; + else + return "sNaN"; + } + else if (mpd_ispositive(a)) { + if (mpd_isinfinite(a)) + return "+Infinity"; + else if (mpd_iszero(a)) + return "+Zero"; + else if (mpd_isnormal(a, ctx)) + return "+Normal"; + else + return "+Subnormal"; + } + else { + if (mpd_isinfinite(a)) + return "-Infinity"; + else if (mpd_iszero(a)) + return "-Zero"; + else if (mpd_isnormal(a, ctx)) + return "-Normal"; + else + return "-Subnormal"; + } +} + +/* Logical Xor */ +void +mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_uint_t x, z, xbit; + mpd_ssize_t i, digits, len; + mpd_ssize_t q, r; + int k; + + if (mpd_isspecial(a) || mpd_isnegative(a) || a->exp != 0) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + digits = (a->digits < ctx->prec) ? ctx->prec : a->digits; + _mpd_idiv_word(&q, &r, digits, MPD_RDIGITS); + len = (r == 0) ? q : q+1; + if (!mpd_qresize(result, len, status)) { + return; + } + + for (i = 0; i < len; i++) { + x = (i < a->len) ? a->data[i] : 0; + z = 0; + for (k = 0; k < MPD_RDIGITS; k++) { + xbit = x % 10; + x /= 10; + if (xbit > 1) { + goto invalid_operation; + } + z += !xbit ? mpd_pow10[k] : 0; + } + result->data[i] = z; + } + + mpd_clear_flags(result); + result->exp = 0; + result->len = _mpd_real_size(result->data, len); + mpd_qresize(result, result->len, status); + mpd_setdigits(result); + _mpd_cap(result, ctx); + return; + +invalid_operation: + mpd_seterror(result, MPD_Invalid_operation, status); +} + +/* Exponent of the magnitude of the most significant digit of the operand. */ +void +mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + mpd_setspecial(result, MPD_POS, MPD_INF); + } + else if (mpd_iszerocoeff(a)) { + mpd_setspecial(result, MPD_NEG, MPD_INF); + *status |= MPD_Division_by_zero; + } + else { + mpd_qset_ssize(result, mpd_adjexp(a), ctx, status); + } +} + +/* Logical Or */ +void +mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + const mpd_t *big = a, *small = b; + mpd_uint_t x, y, z, xbit, ybit; + int k, mswdigits; + mpd_ssize_t i; + + if (mpd_isspecial(a) || mpd_isspecial(b) || + mpd_isnegative(a) || mpd_isnegative(b) || + a->exp != 0 || b->exp != 0) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (b->digits > a->digits) { + big = b; + small = a; + } + if (!mpd_qresize(result, big->len, status)) { + return; + } + + + /* full words */ + for (i = 0; i < small->len-1; i++) { + x = small->data[i]; + y = big->data[i]; + z = 0; + for (k = 0; k < MPD_RDIGITS; k++) { + xbit = x % 10; + x /= 10; + ybit = y % 10; + y /= 10; + if (xbit > 1 || ybit > 1) { + goto invalid_operation; + } + z += (xbit|ybit) ? mpd_pow10[k] : 0; + } + result->data[i] = z; + } + /* most significant word of small */ + x = small->data[i]; + y = big->data[i]; + z = 0; + mswdigits = mpd_word_digits(x); + for (k = 0; k < mswdigits; k++) { + xbit = x % 10; + x /= 10; + ybit = y % 10; + y /= 10; + if (xbit > 1 || ybit > 1) { + goto invalid_operation; + } + z += (xbit|ybit) ? mpd_pow10[k] : 0; + } + + /* scan for digits > 1 and copy the rest of y */ + for (; k < MPD_RDIGITS; k++) { + ybit = y % 10; + y /= 10; + if (ybit > 1) { + goto invalid_operation; + } + z += ybit*mpd_pow10[k]; + } + result->data[i++] = z; + /* scan for digits > 1 and copy the rest of big */ + for (; i < big->len; i++) { + y = big->data[i]; + for (k = 0; k < MPD_RDIGITS; k++) { + ybit = y % 10; + y /= 10; + if (ybit > 1) { + goto invalid_operation; + } + } + result->data[i] = big->data[i]; + } + + mpd_clear_flags(result); + result->exp = 0; + result->len = _mpd_real_size(result->data, big->len); + mpd_qresize(result, result->len, status); + mpd_setdigits(result); + _mpd_cap(result, ctx); + return; + +invalid_operation: + mpd_seterror(result, MPD_Invalid_operation, status); +} + +/* + * Rotate the coefficient of 'a' by 'b' digits. 'b' must be an integer with + * exponent 0. + */ +void +mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + MPD_NEW_STATIC(tmp,0,0,0,0); + MPD_NEW_STATIC(big,0,0,0,0); + MPD_NEW_STATIC(small,0,0,0,0); + mpd_ssize_t n, lshift, rshift; + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + } + if (b->exp != 0 || mpd_isinfinite(b)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + n = mpd_qget_ssize(b, &workstatus); + if (workstatus&MPD_Invalid_operation) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (n > ctx->prec || n < -ctx->prec) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (mpd_isinfinite(a)) { + mpd_qcopy(result, a, status); + return; + } + + if (n >= 0) { + lshift = n; + rshift = ctx->prec-n; + } + else { + lshift = ctx->prec+n; + rshift = -n; + } + + if (a->digits > ctx->prec) { + if (!mpd_qcopy(&tmp, a, status)) { + mpd_seterror(result, MPD_Malloc_error, status); + goto finish; + } + _mpd_cap(&tmp, ctx); + a = &tmp; + } + + if (!mpd_qshiftl(&big, a, lshift, status)) { + mpd_seterror(result, MPD_Malloc_error, status); + goto finish; + } + _mpd_cap(&big, ctx); + + if (mpd_qshiftr(&small, a, rshift, status) == MPD_UINT_MAX) { + mpd_seterror(result, MPD_Malloc_error, status); + goto finish; + } + _mpd_qadd(result, &big, &small, ctx, status); + + +finish: + mpd_del(&tmp); + mpd_del(&big); + mpd_del(&small); +} + +/* + * b must be an integer with exponent 0 and in the range +-2*(emax + prec). + * XXX: In my opinion +-(2*emax + prec) would be more sensible. + * The result is a with the value of b added to its exponent. + */ +void +mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + mpd_uint_t n, maxjump; +#ifndef LEGACY_COMPILER + int64_t exp; +#else + mpd_uint_t x; + int x_sign, n_sign; + mpd_ssize_t exp; +#endif + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + } + if (b->exp != 0 || mpd_isinfinite(b)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + n = mpd_qabs_uint(b, &workstatus); + /* the spec demands this */ + maxjump = 2 * (mpd_uint_t)(ctx->emax + ctx->prec); + + if (n > maxjump || workstatus&MPD_Invalid_operation) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (mpd_isinfinite(a)) { + mpd_qcopy(result, a, status); + return; + } + +#ifndef LEGACY_COMPILER + exp = a->exp + (int64_t)n * mpd_arith_sign(b); + exp = (exp > MPD_EXP_INF) ? MPD_EXP_INF : exp; + exp = (exp < MPD_EXP_CLAMP) ? MPD_EXP_CLAMP : exp; +#else + x = (a->exp < 0) ? -a->exp : a->exp; + x_sign = (a->exp < 0) ? 1 : 0; + n_sign = mpd_isnegative(b) ? 1 : 0; + + if (x_sign == n_sign) { + x = x + n; + if (x < n) x = MPD_UINT_MAX; + } + else { + x_sign = (x >= n) ? x_sign : n_sign; + x = (x >= n) ? x - n : n - x; + } + if (!x_sign && x > MPD_EXP_INF) x = MPD_EXP_INF; + if (x_sign && x > -MPD_EXP_CLAMP) x = -MPD_EXP_CLAMP; + exp = x_sign ? -((mpd_ssize_t)x) : (mpd_ssize_t)x; +#endif + + mpd_qcopy(result, a, status); + result->exp = (mpd_ssize_t)exp; + + mpd_qfinalize(result, ctx, status); +} + +/* + * Shift the coefficient by n digits, positive n is a left shift. In the case + * of a left shift, the result is decapitated to fit the context precision. If + * you don't want that, use mpd_shiftl(). + */ +void +mpd_qshiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, const mpd_context_t *ctx, + uint32_t *status) +{ + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + mpd_qcopy(result, a, status); + return; + } + + if (n >= 0 && n <= ctx->prec) { + mpd_qshiftl(result, a, n, status); + _mpd_cap(result, ctx); + } + else if (n < 0 && n >= -ctx->prec) { + if (!mpd_qcopy(result, a, status)) { + return; + } + _mpd_cap(result, ctx); + mpd_qshiftr_inplace(result, -n); + } + else { + mpd_seterror(result, MPD_Invalid_operation, status); + } +} + +/* + * Same as mpd_shiftn(), but the shift is specified by the decimal b, which + * must be an integer with a zero exponent. Infinities remain infinities. + */ +void +mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, + uint32_t *status) +{ + uint32_t workstatus = 0; + mpd_ssize_t n; + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + } + if (b->exp != 0 || mpd_isinfinite(b)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + n = mpd_qget_ssize(b, &workstatus); + if (workstatus&MPD_Invalid_operation) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (n > ctx->prec || n < -ctx->prec) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (mpd_isinfinite(a)) { + mpd_qcopy(result, a, status); + return; + } + + if (n >= 0) { + mpd_qshiftl(result, a, n, status); + _mpd_cap(result, ctx); + } + else { + if (!mpd_qcopy(result, a, status)) { + return; + } + _mpd_cap(result, ctx); + mpd_qshiftr_inplace(result, -n); + } +} + +/* Logical Xor */ +void +mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + const mpd_t *big = a, *small = b; + mpd_uint_t x, y, z, xbit, ybit; + int k, mswdigits; + mpd_ssize_t i; + + if (mpd_isspecial(a) || mpd_isspecial(b) || + mpd_isnegative(a) || mpd_isnegative(b) || + a->exp != 0 || b->exp != 0) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (b->digits > a->digits) { + big = b; + small = a; + } + if (!mpd_qresize(result, big->len, status)) { + return; + } + + + /* full words */ + for (i = 0; i < small->len-1; i++) { + x = small->data[i]; + y = big->data[i]; + z = 0; + for (k = 0; k < MPD_RDIGITS; k++) { + xbit = x % 10; + x /= 10; + ybit = y % 10; + y /= 10; + if (xbit > 1 || ybit > 1) { + goto invalid_operation; + } + z += (xbit^ybit) ? mpd_pow10[k] : 0; + } + result->data[i] = z; + } + /* most significant word of small */ + x = small->data[i]; + y = big->data[i]; + z = 0; + mswdigits = mpd_word_digits(x); + for (k = 0; k < mswdigits; k++) { + xbit = x % 10; + x /= 10; + ybit = y % 10; + y /= 10; + if (xbit > 1 || ybit > 1) { + goto invalid_operation; + } + z += (xbit^ybit) ? mpd_pow10[k] : 0; + } + + /* scan for digits > 1 and copy the rest of y */ + for (; k < MPD_RDIGITS; k++) { + ybit = y % 10; + y /= 10; + if (ybit > 1) { + goto invalid_operation; + } + z += ybit*mpd_pow10[k]; + } + result->data[i++] = z; + /* scan for digits > 1 and copy the rest of big */ + for (; i < big->len; i++) { + y = big->data[i]; + for (k = 0; k < MPD_RDIGITS; k++) { + ybit = y % 10; + y /= 10; + if (ybit > 1) { + goto invalid_operation; + } + } + result->data[i] = big->data[i]; + } + + mpd_clear_flags(result); + result->exp = 0; + result->len = _mpd_real_size(result->data, big->len); + mpd_qresize(result, result->len, status); + mpd_setdigits(result); + _mpd_cap(result, ctx); + return; + +invalid_operation: + mpd_seterror(result, MPD_Invalid_operation, status); +} + + +/******************************************************************************/ +/* Arithmetic operations */ +/******************************************************************************/ + +/* + * The absolute value of a. If a is negative, the result is the same + * as the result of the minus operation. Otherwise, the result is the + * result of the plus operation. + */ +void +mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + } + + if (mpd_isnegative(a)) { + mpd_qminus(result, a, ctx, status); + } + else { + mpd_qplus(result, a, ctx, status); + } +} + +static inline void +_mpd_ptrswap(const mpd_t **a, const mpd_t **b) +{ + const mpd_t *t = *a; + *a = *b; + *b = t; +} + +/* Add or subtract infinities. */ +static void +_mpd_qaddsub_inf(mpd_t *result, const mpd_t *a, const mpd_t *b, uint8_t sign_b, + uint32_t *status) +{ + if (mpd_isinfinite(a)) { + if (mpd_sign(a) != sign_b && mpd_isinfinite(b)) { + mpd_seterror(result, MPD_Invalid_operation, status); + } + else { + mpd_setspecial(result, mpd_sign(a), MPD_INF); + } + return; + } + assert(mpd_isinfinite(b)); + mpd_setspecial(result, sign_b, MPD_INF); +} + +/* Add or subtract non-special numbers. */ +static void +_mpd_qaddsub(mpd_t *result, const mpd_t *a, const mpd_t *b, uint8_t sign_b, + const mpd_context_t *ctx, uint32_t *status) +{ + const mpd_t *big, *small; + MPD_NEW_STATIC(big_aligned,0,0,0,0); + MPD_NEW_CONST(tiny,0,0,1,1,1,1); + mpd_uint_t carry; + mpd_ssize_t newsize, shift; + mpd_ssize_t exp, i; + int swap = 0; + + + /* compare exponents */ + big = a; small = b; + if (big->exp != small->exp) { + if (small->exp > big->exp) { + _mpd_ptrswap(&big, &small); + swap++; + } + /* align the coefficients */ + if (!mpd_iszerocoeff(big)) { + exp = big->exp - 1; + exp += (big->digits > ctx->prec) ? 0 : big->digits-ctx->prec-1; + if (mpd_adjexp(small) < exp) { + /* + * Avoid huge shifts by substituting a value for small that is + * guaranteed to produce the same results. + * + * adjexp(small) < exp if and only if: + * + * bdigits <= prec AND + * bdigits+shift >= prec+2+sdigits AND + * exp = bexp+bdigits-prec-2 + * + * 1234567000000000 -> bdigits + shift + * ----------XX1234 -> sdigits + * ----------X1 -> tiny-digits + * |- prec -| + * + * OR + * + * bdigits > prec AND + * shift > sdigits AND + * exp = bexp-1 + * + * 1234567892100000 -> bdigits + shift + * ----------XX1234 -> sdigits + * ----------X1 -> tiny-digits + * |- prec -| + * + * If tiny is zero, adding or subtracting is a no-op. + * Otherwise, adding tiny generates a non-zero digit either + * below the rounding digit or the least significant digit + * of big. When subtracting, tiny is in the same position as + * the carry that would be generated by subtracting sdigits. + */ + mpd_copy_flags(&tiny, small); + tiny.exp = exp; + tiny.digits = 1; + tiny.len = 1; + tiny.data[0] = mpd_iszerocoeff(small) ? 0 : 1; + small = &tiny; + } + /* This cannot wrap: the difference is positive and <= maxprec */ + shift = big->exp - small->exp; + if (!mpd_qshiftl(&big_aligned, big, shift, status)) { + mpd_seterror(result, MPD_Malloc_error, status); + goto finish; + } + big = &big_aligned; + } + } + result->exp = small->exp; + + + /* compare length of coefficients */ + if (big->len < small->len) { + _mpd_ptrswap(&big, &small); + swap++; + } + + newsize = big->len; + if (!mpd_qresize(result, newsize, status)) { + goto finish; + } + + if (mpd_sign(a) == sign_b) { + + carry = _mpd_baseadd(result->data, big->data, small->data, + big->len, small->len); + + if (carry) { + newsize = big->len + 1; + if (!mpd_qresize(result, newsize, status)) { + goto finish; + } + result->data[newsize-1] = carry; + } + + result->len = newsize; + mpd_set_flags(result, sign_b); + } + else { + if (big->len == small->len) { + for (i=big->len-1; i >= 0; --i) { + if (big->data[i] != small->data[i]) { + if (big->data[i] < small->data[i]) { + _mpd_ptrswap(&big, &small); + swap++; + } + break; + } + } + } + + _mpd_basesub(result->data, big->data, small->data, + big->len, small->len); + newsize = _mpd_real_size(result->data, big->len); + /* resize to smaller cannot fail */ + (void)mpd_qresize(result, newsize, status); + + result->len = newsize; + sign_b = (swap & 1) ? sign_b : mpd_sign(a); + mpd_set_flags(result, sign_b); + + if (mpd_iszerocoeff(result)) { + mpd_set_positive(result); + if (ctx->round == MPD_ROUND_FLOOR) { + mpd_set_negative(result); + } + } + } + + mpd_setdigits(result); + +finish: + mpd_del(&big_aligned); +} + +/* Add a and b. No specials, no finalizing. */ +static void +_mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + _mpd_qaddsub(result, a, b, mpd_sign(b), ctx, status); +} + +/* Subtract b from a. No specials, no finalizing. */ +static void +_mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + _mpd_qaddsub(result, a, b, !mpd_sign(b), ctx, status); +} + +/* Add a and b. */ +void +mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + _mpd_qaddsub_inf(result, a, b, mpd_sign(b), status); + return; + } + + _mpd_qaddsub(result, a, b, mpd_sign(b), ctx, status); + mpd_qfinalize(result, ctx, status); +} + +/* Add a and b. Set NaN/Invalid_operation if the result is inexact. */ +static void +_mpd_qadd_exact(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + + mpd_qadd(result, a, b, ctx, &workstatus); + *status |= workstatus; + if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) { + mpd_seterror(result, MPD_Invalid_operation, status); + } +} + +/* Subtract b from a. */ +void +mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + _mpd_qaddsub_inf(result, a, b, !mpd_sign(b), status); + return; + } + + _mpd_qaddsub(result, a, b, !mpd_sign(b), ctx, status); + mpd_qfinalize(result, ctx, status); +} + +/* Subtract b from a. Set NaN/Invalid_operation if the result is inexact. */ +static void +_mpd_qsub_exact(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + + mpd_qsub(result, a, b, ctx, &workstatus); + *status |= workstatus; + if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) { + mpd_seterror(result, MPD_Invalid_operation, status); + } +} + +/* Add decimal and mpd_ssize_t. */ +void +mpd_qadd_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_ssize(&bb, b, &maxcontext, status); + mpd_qadd(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Add decimal and mpd_uint_t. */ +void +mpd_qadd_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_uint(&bb, b, &maxcontext, status); + mpd_qadd(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Subtract mpd_ssize_t from decimal. */ +void +mpd_qsub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_ssize(&bb, b, &maxcontext, status); + mpd_qsub(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Subtract mpd_uint_t from decimal. */ +void +mpd_qsub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_uint(&bb, b, &maxcontext, status); + mpd_qsub(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Add decimal and int32_t. */ +void +mpd_qadd_i32(mpd_t *result, const mpd_t *a, int32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qadd_ssize(result, a, b, ctx, status); +} + +/* Add decimal and uint32_t. */ +void +mpd_qadd_u32(mpd_t *result, const mpd_t *a, uint32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qadd_uint(result, a, b, ctx, status); +} + +#ifdef CONFIG_64 +/* Add decimal and int64_t. */ +void +mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qadd_ssize(result, a, b, ctx, status); +} + +/* Add decimal and uint64_t. */ +void +mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qadd_uint(result, a, b, ctx, status); +} +#elif !defined(LEGACY_COMPILER) +/* Add decimal and int64_t. */ +void +mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qset_i64(&bb, b, &maxcontext, status); + mpd_qadd(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Add decimal and uint64_t. */ +void +mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qset_u64(&bb, b, &maxcontext, status); + mpd_qadd(result, a, &bb, ctx, status); + mpd_del(&bb); +} +#endif + +/* Subtract int32_t from decimal. */ +void +mpd_qsub_i32(mpd_t *result, const mpd_t *a, int32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qsub_ssize(result, a, b, ctx, status); +} + +/* Subtract uint32_t from decimal. */ +void +mpd_qsub_u32(mpd_t *result, const mpd_t *a, uint32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qsub_uint(result, a, b, ctx, status); +} + +#ifdef CONFIG_64 +/* Subtract int64_t from decimal. */ +void +mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qsub_ssize(result, a, b, ctx, status); +} + +/* Subtract uint64_t from decimal. */ +void +mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qsub_uint(result, a, b, ctx, status); +} +#elif !defined(LEGACY_COMPILER) +/* Subtract int64_t from decimal. */ +void +mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qset_i64(&bb, b, &maxcontext, status); + mpd_qsub(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Subtract uint64_t from decimal. */ +void +mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qset_u64(&bb, b, &maxcontext, status); + mpd_qsub(result, a, &bb, ctx, status); + mpd_del(&bb); +} +#endif + + +/* Divide infinities. */ +static void +_mpd_qdiv_inf(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + if (mpd_isinfinite(a)) { + if (mpd_isinfinite(b)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF); + return; + } + assert(mpd_isinfinite(b)); + _settriple(result, mpd_sign(a)^mpd_sign(b), 0, mpd_etiny(ctx)); + *status |= MPD_Clamped; +} + +enum {NO_IDEAL_EXP, SET_IDEAL_EXP}; +/* Divide a by b. */ +static void +_mpd_qdiv(int action, mpd_t *q, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + MPD_NEW_STATIC(aligned,0,0,0,0); + mpd_uint_t ld; + mpd_ssize_t shift, exp, tz; + mpd_ssize_t newsize; + mpd_ssize_t ideal_exp; + mpd_uint_t rem; + uint8_t sign_a = mpd_sign(a); + uint8_t sign_b = mpd_sign(b); + + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(q, a, b, ctx, status)) { + return; + } + _mpd_qdiv_inf(q, a, b, ctx, status); + return; + } + if (mpd_iszerocoeff(b)) { + if (mpd_iszerocoeff(a)) { + mpd_seterror(q, MPD_Division_undefined, status); + } + else { + mpd_setspecial(q, sign_a^sign_b, MPD_INF); + *status |= MPD_Division_by_zero; + } + return; + } + if (mpd_iszerocoeff(a)) { + exp = a->exp - b->exp; + _settriple(q, sign_a^sign_b, 0, exp); + mpd_qfinalize(q, ctx, status); + return; + } + + shift = (b->digits - a->digits) + ctx->prec + 1; + ideal_exp = a->exp - b->exp; + exp = ideal_exp - shift; + if (shift > 0) { + if (!mpd_qshiftl(&aligned, a, shift, status)) { + mpd_seterror(q, MPD_Malloc_error, status); + goto finish; + } + a = &aligned; + } + else if (shift < 0) { + shift = -shift; + if (!mpd_qshiftl(&aligned, b, shift, status)) { + mpd_seterror(q, MPD_Malloc_error, status); + goto finish; + } + b = &aligned; + } + + + newsize = a->len - b->len + 1; + if ((q != b && q != a) || (q == b && newsize > b->len)) { + if (!mpd_qresize(q, newsize, status)) { + mpd_seterror(q, MPD_Malloc_error, status); + goto finish; + } + } + + + if (b->len == 1) { + rem = _mpd_shortdiv(q->data, a->data, a->len, b->data[0]); + } + else if (b->len <= MPD_NEWTONDIV_CUTOFF) { + int ret = _mpd_basedivmod(q->data, NULL, a->data, b->data, + a->len, b->len); + if (ret < 0) { + mpd_seterror(q, MPD_Malloc_error, status); + goto finish; + } + rem = ret; + } + else { + MPD_NEW_STATIC(r,0,0,0,0); + _mpd_base_ndivmod(q, &r, a, b, status); + if (mpd_isspecial(q) || mpd_isspecial(&r)) { + mpd_setspecial(q, MPD_POS, MPD_NAN); + mpd_del(&r); + goto finish; + } + rem = !mpd_iszerocoeff(&r); + mpd_del(&r); + newsize = q->len; + } + + newsize = _mpd_real_size(q->data, newsize); + /* resize to smaller cannot fail */ + mpd_qresize(q, newsize, status); + mpd_set_flags(q, sign_a^sign_b); + q->len = newsize; + mpd_setdigits(q); + + shift = ideal_exp - exp; + if (rem) { + ld = mpd_lsd(q->data[0]); + if (ld == 0 || ld == 5) { + q->data[0] += 1; + } + } + else if (action == SET_IDEAL_EXP && shift > 0) { + tz = mpd_trail_zeros(q); + shift = (tz > shift) ? shift : tz; + mpd_qshiftr_inplace(q, shift); + exp += shift; + } + + q->exp = exp; + + +finish: + mpd_del(&aligned); + mpd_qfinalize(q, ctx, status); +} + +/* Divide a by b. */ +void +mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + _mpd_qdiv(SET_IDEAL_EXP, q, a, b, ctx, status); +} + +/* Internal function. */ +static void +_mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + MPD_NEW_STATIC(aligned,0,0,0,0); + mpd_ssize_t qsize, rsize; + mpd_ssize_t ideal_exp, expdiff, shift; + uint8_t sign_a = mpd_sign(a); + uint8_t sign_ab = mpd_sign(a)^mpd_sign(b); + + + ideal_exp = (a->exp > b->exp) ? b->exp : a->exp; + if (mpd_iszerocoeff(a)) { + if (!mpd_qcopy(r, a, status)) { + goto nanresult; /* GCOV_NOT_REACHED */ + } + r->exp = ideal_exp; + _settriple(q, sign_ab, 0, 0); + return; + } + + expdiff = mpd_adjexp(a) - mpd_adjexp(b); + if (expdiff < 0) { + if (a->exp > b->exp) { + /* positive and less than b->digits - a->digits */ + shift = a->exp - b->exp; + if (!mpd_qshiftl(r, a, shift, status)) { + goto nanresult; + } + r->exp = ideal_exp; + } + else { + if (!mpd_qcopy(r, a, status)) { + goto nanresult; + } + } + _settriple(q, sign_ab, 0, 0); + return; + } + if (expdiff > ctx->prec) { + *status |= MPD_Division_impossible; + goto nanresult; + } + + + /* + * At this point we have: + * (1) 0 <= a->exp + a->digits - b->exp - b->digits <= prec + * (2) a->exp - b->exp >= b->digits - a->digits + * (3) a->exp - b->exp <= prec + b->digits - a->digits + */ + if (a->exp != b->exp) { + shift = a->exp - b->exp; + if (shift > 0) { + /* by (3), after the shift a->digits <= prec + b->digits */ + if (!mpd_qshiftl(&aligned, a, shift, status)) { + goto nanresult; + } + a = &aligned; + } + else { + shift = -shift; + /* by (2), after the shift b->digits <= a->digits */ + if (!mpd_qshiftl(&aligned, b, shift, status)) { + goto nanresult; + } + b = &aligned; + } + } + + + qsize = a->len - b->len + 1; + if (!(q == a && qsize < a->len) && !(q == b && qsize < b->len)) { + if (!mpd_qresize(q, qsize, status)) { + goto nanresult; + } + } + + rsize = b->len; + if (!(r == a && rsize < a->len)) { + if (!mpd_qresize(r, rsize, status)) { + goto nanresult; + } + } + + if (b->len == 1) { + if (a->len == 1) { + _mpd_div_word(&q->data[0], &r->data[0], a->data[0], b->data[0]); + } + else { + r->data[0] = _mpd_shortdiv(q->data, a->data, a->len, b->data[0]); + } + } + else if (b->len <= MPD_NEWTONDIV_CUTOFF) { + int ret; + ret = _mpd_basedivmod(q->data, r->data, a->data, b->data, + a->len, b->len); + if (ret == -1) { + *status |= MPD_Malloc_error; + goto nanresult; + } + } + else { + _mpd_base_ndivmod(q, r, a, b, status); + if (mpd_isspecial(q) || mpd_isspecial(r)) { + goto nanresult; + } + qsize = q->len; + rsize = r->len; + } + + qsize = _mpd_real_size(q->data, qsize); + /* resize to smaller cannot fail */ + mpd_qresize(q, qsize, status); + q->len = qsize; + mpd_setdigits(q); + mpd_set_flags(q, sign_ab); + q->exp = 0; + if (q->digits > ctx->prec) { + *status |= MPD_Division_impossible; + goto nanresult; + } + + rsize = _mpd_real_size(r->data, rsize); + /* resize to smaller cannot fail */ + mpd_qresize(r, rsize, status); + r->len = rsize; + mpd_setdigits(r); + mpd_set_flags(r, sign_a); + r->exp = ideal_exp; + +out: + mpd_del(&aligned); + return; + +nanresult: + mpd_setspecial(q, MPD_POS, MPD_NAN); + mpd_setspecial(r, MPD_POS, MPD_NAN); + goto out; +} + +/* Integer division with remainder. */ +void +mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + uint8_t sign = mpd_sign(a)^mpd_sign(b); + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(q, a, b, ctx, status)) { + mpd_qcopy(r, q, status); + return; + } + if (mpd_isinfinite(a)) { + if (mpd_isinfinite(b)) { + mpd_setspecial(q, MPD_POS, MPD_NAN); + } + else { + mpd_setspecial(q, sign, MPD_INF); + } + mpd_setspecial(r, MPD_POS, MPD_NAN); + *status |= MPD_Invalid_operation; + return; + } + if (mpd_isinfinite(b)) { + if (!mpd_qcopy(r, a, status)) { + mpd_seterror(q, MPD_Malloc_error, status); + return; + } + mpd_qfinalize(r, ctx, status); + _settriple(q, sign, 0, 0); + return; + } + /* debug */ + abort(); /* GCOV_NOT_REACHED */ + } + if (mpd_iszerocoeff(b)) { + if (mpd_iszerocoeff(a)) { + mpd_setspecial(q, MPD_POS, MPD_NAN); + mpd_setspecial(r, MPD_POS, MPD_NAN); + *status |= MPD_Division_undefined; + } + else { + mpd_setspecial(q, sign, MPD_INF); + mpd_setspecial(r, MPD_POS, MPD_NAN); + *status |= (MPD_Division_by_zero|MPD_Invalid_operation); + } + return; + } + + _mpd_qdivmod(q, r, a, b, ctx, status); + mpd_qfinalize(q, ctx, status); + mpd_qfinalize(r, ctx, status); +} + +void +mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + MPD_NEW_STATIC(r,0,0,0,0); + uint8_t sign = mpd_sign(a)^mpd_sign(b); + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(q, a, b, ctx, status)) { + return; + } + if (mpd_isinfinite(a) && mpd_isinfinite(b)) { + mpd_seterror(q, MPD_Invalid_operation, status); + return; + } + if (mpd_isinfinite(a)) { + mpd_setspecial(q, sign, MPD_INF); + return; + } + if (mpd_isinfinite(b)) { + _settriple(q, sign, 0, 0); + return; + } + /* debug */ + abort(); /* GCOV_NOT_REACHED */ + } + if (mpd_iszerocoeff(b)) { + if (mpd_iszerocoeff(a)) { + mpd_seterror(q, MPD_Division_undefined, status); + } + else { + mpd_setspecial(q, sign, MPD_INF); + *status |= MPD_Division_by_zero; + } + return; + } + + + _mpd_qdivmod(q, &r, a, b, ctx, status); + mpd_del(&r); + mpd_qfinalize(q, ctx, status); +} + +/* Divide decimal by mpd_ssize_t. */ +void +mpd_qdiv_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_ssize(&bb, b, &maxcontext, status); + mpd_qdiv(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Divide decimal by mpd_uint_t. */ +void +mpd_qdiv_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_uint(&bb, b, &maxcontext, status); + mpd_qdiv(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Divide decimal by int32_t. */ +void +mpd_qdiv_i32(mpd_t *result, const mpd_t *a, int32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qdiv_ssize(result, a, b, ctx, status); +} + +/* Divide decimal by uint32_t. */ +void +mpd_qdiv_u32(mpd_t *result, const mpd_t *a, uint32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qdiv_uint(result, a, b, ctx, status); +} + +#ifdef CONFIG_64 +/* Divide decimal by int64_t. */ +void +mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qdiv_ssize(result, a, b, ctx, status); +} + +/* Divide decimal by uint64_t. */ +void +mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qdiv_uint(result, a, b, ctx, status); +} +#elif !defined(LEGACY_COMPILER) +/* Divide decimal by int64_t. */ +void +mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qset_i64(&bb, b, &maxcontext, status); + mpd_qdiv(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Divide decimal by uint64_t. */ +void +mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qset_u64(&bb, b, &maxcontext, status); + mpd_qdiv(result, a, &bb, ctx, status); + mpd_del(&bb); +} +#endif + +/* Pad the result with trailing zeros if it has fewer digits than prec. */ +static void +_mpd_zeropad(mpd_t *result, const mpd_context_t *ctx, uint32_t *status) +{ + if (!mpd_isspecial(result) && !mpd_iszero(result) && + result->digits < ctx->prec) { + mpd_ssize_t shift = ctx->prec - result->digits; + mpd_qshiftl(result, result, shift, status); + result->exp -= shift; + } +} + +/* Check if the result is guaranteed to be one. */ +static int +_mpd_qexp_check_one(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + MPD_NEW_CONST(lim,0,-(ctx->prec+1),1,1,1,9); + MPD_NEW_SHARED(aa, a); + + mpd_set_positive(&aa); + + /* abs(a) <= 9 * 10**(-prec-1) */ + if (_mpd_cmp(&aa, &lim) <= 0) { + _settriple(result, 0, 1, 0); + *status |= MPD_Rounded|MPD_Inexact; + return 1; + } + + return 0; +} + +/* + * Get the number of iterations for the Horner scheme in _mpd_qexp(). + */ +static inline mpd_ssize_t +_mpd_get_exp_iterations(const mpd_t *r, mpd_ssize_t p) +{ + mpd_ssize_t log10pbyr; /* lower bound for log10(p / abs(r)) */ + mpd_ssize_t n; + + assert(p >= 10); + assert(!mpd_iszero(r)); + assert(-p < mpd_adjexp(r) && mpd_adjexp(r) <= -1); + +#ifdef CONFIG_64 + if (p > (mpd_ssize_t)(1ULL<<52)) { + return MPD_SSIZE_MAX; + } +#endif + + /* + * Lower bound for log10(p / abs(r)): adjexp(p) - (adjexp(r) + 1) + * At this point (for CONFIG_64, CONFIG_32 is not problematic): + * 1) 10 <= p <= 2**52 + * 2) -p < adjexp(r) <= -1 + * 3) 1 <= log10pbyr <= 2**52 + 14 + */ + log10pbyr = (mpd_word_digits(p)-1) - (mpd_adjexp(r)+1); + + /* + * The numerator in the paper is 1.435 * p - 1.182, calculated + * exactly. We compensate for rounding errors by using 1.43503. + * ACL2 proofs: + * 1) exp-iter-approx-lower-bound: The term below evaluated + * in 53-bit floating point arithmetic is greater than or + * equal to the exact term used in the paper. + * 2) exp-iter-approx-upper-bound: The term below is less than + * or equal to 3/2 * p <= 3/2 * 2**52. + */ + n = (mpd_ssize_t)ceil((1.43503*(double)p - 1.182) / (double)log10pbyr); + return n >= 3 ? n : 3; +} + +/* + * Internal function, specials have been dealt with. Apart from Overflow + * and Underflow, two cases must be considered for the error of the result: + * + * 1) abs(a) <= 9 * 10**(-prec-1) ==> result == 1 + * + * Absolute error: abs(1 - e**x) < 10**(-prec) + * ------------------------------------------- + * + * 2) abs(a) > 9 * 10**(-prec-1) + * + * Relative error: abs(result - e**x) < 0.5 * 10**(-prec) * e**x + * ------------------------------------------------------------- + * + * The algorithm is from Hull&Abrham, Variable Precision Exponential Function, + * ACM Transactions on Mathematical Software, Vol. 12, No. 2, June 1986. + * + * Main differences: + * + * - The number of iterations for the Horner scheme is calculated using + * 53-bit floating point arithmetic. + * + * - In the error analysis for ER (relative error accumulated in the + * evaluation of the truncated series) the reduced operand r may + * have any number of digits. + * ACL2 proof: exponent-relative-error + * + * - The analysis for early abortion has been adapted for the mpd_t + * ranges. + */ +static void +_mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t workctx; + MPD_NEW_STATIC(tmp,0,0,0,0); + MPD_NEW_STATIC(sum,0,0,0,0); + MPD_NEW_CONST(word,0,0,1,1,1,1); + mpd_ssize_t j, n, t; + + assert(!mpd_isspecial(a)); + + if (mpd_iszerocoeff(a)) { + _settriple(result, MPD_POS, 1, 0); + return; + } + + /* + * We are calculating e^x = e^(r*10^t) = (e^r)^(10^t), where abs(r) < 1 and t >= 0. + * + * If t > 0, we have: + * + * (1) 0.1 <= r < 1, so e^0.1 <= e^r. If t > MAX_T, overflow occurs: + * + * MAX-EMAX+1 < log10(e^(0.1*10*t)) <= log10(e^(r*10^t)) < adjexp(e^(r*10^t))+1 + * + * (2) -1 < r <= -0.1, so e^r <= e^-0.1. If t > MAX_T, underflow occurs: + * + * adjexp(e^(r*10^t)) <= log10(e^(r*10^t)) <= log10(e^(-0.1*10^t)) < MIN-ETINY + */ +#if defined(CONFIG_64) + #define MPD_EXP_MAX_T 19 +#elif defined(CONFIG_32) + #define MPD_EXP_MAX_T 10 +#endif + t = a->digits + a->exp; + t = (t > 0) ? t : 0; + if (t > MPD_EXP_MAX_T) { + if (mpd_ispositive(a)) { + mpd_setspecial(result, MPD_POS, MPD_INF); + *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded; + } + else { + _settriple(result, MPD_POS, 0, mpd_etiny(ctx)); + *status |= (MPD_Inexact|MPD_Rounded|MPD_Subnormal| + MPD_Underflow|MPD_Clamped); + } + return; + } + + /* abs(a) <= 9 * 10**(-prec-1) */ + if (_mpd_qexp_check_one(result, a, ctx, status)) { + return; + } + + mpd_maxcontext(&workctx); + workctx.prec = ctx->prec + t + 2; + workctx.prec = (workctx.prec < 10) ? 10 : workctx.prec; + workctx.round = MPD_ROUND_HALF_EVEN; + + if (!mpd_qcopy(result, a, status)) { + return; + } + result->exp -= t; + + /* + * At this point: + * 1) 9 * 10**(-prec-1) < abs(a) + * 2) 9 * 10**(-prec-t-1) < abs(r) + * 3) log10(9) - prec - t - 1 < log10(abs(r)) < adjexp(abs(r)) + 1 + * 4) - prec - t - 2 < adjexp(abs(r)) <= -1 + */ + n = _mpd_get_exp_iterations(result, workctx.prec); + if (n == MPD_SSIZE_MAX) { + mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_UNLIKELY */ + return; /* GCOV_UNLIKELY */ + } + + _settriple(&sum, MPD_POS, 1, 0); + + for (j = n-1; j >= 1; j--) { + word.data[0] = j; + mpd_setdigits(&word); + mpd_qdiv(&tmp, result, &word, &workctx, &workctx.status); + mpd_qfma(&sum, &sum, &tmp, &one, &workctx, &workctx.status); + } + +#ifdef CONFIG_64 + _mpd_qpow_uint(result, &sum, mpd_pow10[t], MPD_POS, &workctx, status); +#else + if (t <= MPD_MAX_POW10) { + _mpd_qpow_uint(result, &sum, mpd_pow10[t], MPD_POS, &workctx, status); + } + else { + t -= MPD_MAX_POW10; + _mpd_qpow_uint(&tmp, &sum, mpd_pow10[MPD_MAX_POW10], MPD_POS, + &workctx, status); + _mpd_qpow_uint(result, &tmp, mpd_pow10[t], MPD_POS, &workctx, status); + } +#endif + + mpd_del(&tmp); + mpd_del(&sum); + *status |= (workctx.status&MPD_Errors); + *status |= (MPD_Inexact|MPD_Rounded); +} + +/* exp(a) */ +void +mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t workctx; + + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + if (mpd_isnegative(a)) { + _settriple(result, MPD_POS, 0, 0); + } + else { + mpd_setspecial(result, MPD_POS, MPD_INF); + } + return; + } + if (mpd_iszerocoeff(a)) { + _settriple(result, MPD_POS, 1, 0); + return; + } + + workctx = *ctx; + workctx.round = MPD_ROUND_HALF_EVEN; + + if (ctx->allcr) { + MPD_NEW_STATIC(t1, 0,0,0,0); + MPD_NEW_STATIC(t2, 0,0,0,0); + MPD_NEW_STATIC(ulp, 0,0,0,0); + MPD_NEW_STATIC(aa, 0,0,0,0); + mpd_ssize_t prec; + mpd_ssize_t ulpexp; + uint32_t workstatus; + + if (result == a) { + if (!mpd_qcopy(&aa, a, status)) { + mpd_seterror(result, MPD_Malloc_error, status); + return; + } + a = &aa; + } + + workctx.clamp = 0; + prec = ctx->prec + 3; + while (1) { + workctx.prec = prec; + workstatus = 0; + + _mpd_qexp(result, a, &workctx, &workstatus); + *status |= workstatus; + + ulpexp = result->exp + result->digits - workctx.prec; + if (workstatus & MPD_Underflow) { + /* The effective work precision is result->digits. */ + ulpexp = result->exp; + } + _ssettriple(&ulp, MPD_POS, 1, ulpexp); + + /* + * At this point [1]: + * 1) abs(result - e**x) < 0.5 * 10**(-prec) * e**x + * 2) result - ulp < e**x < result + ulp + * 3) result - ulp < result < result + ulp + * + * If round(result-ulp)==round(result+ulp), then + * round(result)==round(e**x). Therefore the result + * is correctly rounded. + * + * [1] If abs(a) <= 9 * 10**(-prec-1), use the absolute + * error for a similar argument. + */ + workctx.prec = ctx->prec; + mpd_qadd(&t1, result, &ulp, &workctx, &workctx.status); + mpd_qsub(&t2, result, &ulp, &workctx, &workctx.status); + if (mpd_isspecial(result) || mpd_iszerocoeff(result) || + mpd_qcmp(&t1, &t2, status) == 0) { + workctx.clamp = ctx->clamp; + _mpd_zeropad(result, &workctx, status); + mpd_check_underflow(result, &workctx, status); + mpd_qfinalize(result, &workctx, status); + break; + } + prec += MPD_RDIGITS; + } + mpd_del(&t1); + mpd_del(&t2); + mpd_del(&ulp); + mpd_del(&aa); + } + else { + _mpd_qexp(result, a, &workctx, status); + _mpd_zeropad(result, &workctx, status); + mpd_check_underflow(result, &workctx, status); + mpd_qfinalize(result, &workctx, status); + } +} + +/* Fused multiply-add: (a * b) + c, with a single final rounding. */ +void +mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + mpd_t *cc = NULL; + + if (result == c) { + if ((cc = mpd_qncopy(c)) == NULL) { + mpd_seterror(result, MPD_Malloc_error, status); + return; + } + c = cc; + } + + _mpd_qmul(result, a, b, ctx, &workstatus); + if (!(workstatus&MPD_Invalid_operation)) { + mpd_qadd(result, result, c, ctx, &workstatus); + } + + if (cc) mpd_del(cc); + *status |= workstatus; +} + +/* + * Schedule the optimal precision increase for the Newton iteration. + * v := input operand + * z_0 := initial approximation + * initprec := natural number such that abs(log(v) - z_0) < 10**-initprec + * maxprec := target precision + * + * For convenience the output klist contains the elements in reverse order: + * klist := [k_n-1, ..., k_0], where + * 1) k_0 <= initprec and + * 2) abs(log(v) - result) < 10**(-2*k_n-1 + 1) <= 10**-maxprec. + */ +static inline int +ln_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2], mpd_ssize_t maxprec, + mpd_ssize_t initprec) +{ + mpd_ssize_t k; + int i; + + assert(maxprec >= 2 && initprec >= 2); + if (maxprec <= initprec) return -1; + + i = 0; k = maxprec; + do { + k = (k+2) / 2; + klist[i++] = k; + } while (k > initprec); + + return i-1; +} + +/* The constants have been verified with both decimal.py and mpfr. */ +#ifdef CONFIG_64 +#if MPD_RDIGITS != 19 + #error "mpdecimal.c: MPD_RDIGITS must be 19." +#endif +static const mpd_uint_t mpd_ln10_data[MPD_MINALLOC_MAX] = { + 6983716328982174407ULL, 9089704281976336583ULL, 1515961135648465461ULL, + 4416816335727555703ULL, 2900988039194170265ULL, 2307925037472986509ULL, + 107598438319191292ULL, 3466624107184669231ULL, 4450099781311469159ULL, + 9807828059751193854ULL, 7713456862091670584ULL, 1492198849978748873ULL, + 6528728696511086257ULL, 2385392051446341972ULL, 8692180205189339507ULL, + 6518769751037497088ULL, 2375253577097505395ULL, 9095610299291824318ULL, + 982748238504564801ULL, 5438635917781170543ULL, 7547331541421808427ULL, + 752371033310119785ULL, 3171643095059950878ULL, 9785265383207606726ULL, + 2932258279850258550ULL, 5497347726624257094ULL, 2976979522110718264ULL, + 9221477656763693866ULL, 1979650047149510504ULL, 6674183485704422507ULL, + 9702766860595249671ULL, 9278096762712757753ULL, 9314848524948644871ULL, + 6826928280848118428ULL, 754403708474699401ULL, 230105703089634572ULL, + 1929203337658714166ULL, 7589402567763113569ULL, 4208241314695689016ULL, + 2922455440575892572ULL, 9356734206705811364ULL, 2684916746550586856ULL, + 644507064800027750ULL, 9476834636167921018ULL, 5659121373450747856ULL, + 2835522011480466371ULL, 6470806855677432162ULL, 7141748003688084012ULL, + 9619404400222105101ULL, 5504893431493939147ULL, 6674744042432743651ULL, + 2287698219886746543ULL, 7773262884616336622ULL, 1985283935053089653ULL, + 4680843799894826233ULL, 8168948290720832555ULL, 8067566662873690987ULL, + 6248633409525465082ULL, 9829834196778404228ULL, 3524802359972050895ULL, + 3327900967572609677ULL, 110148862877297603ULL, 179914546843642076ULL, + 2302585092994045684ULL +}; +#else +#if MPD_RDIGITS != 9 + #error "mpdecimal.c: MPD_RDIGITS must be 9." +#endif +static const mpd_uint_t mpd_ln10_data[MPD_MINALLOC_MAX] = { + 401682692UL, 708474699UL, 720754403UL, 30896345UL, 602301057UL, 765871416UL, + 192920333UL, 763113569UL, 589402567UL, 956890167UL, 82413146UL, 589257242UL, + 245544057UL, 811364292UL, 734206705UL, 868569356UL, 167465505UL, 775026849UL, + 706480002UL, 18064450UL, 636167921UL, 569476834UL, 734507478UL, 156591213UL, + 148046637UL, 283552201UL, 677432162UL, 470806855UL, 880840126UL, 417480036UL, + 210510171UL, 940440022UL, 939147961UL, 893431493UL, 436515504UL, 440424327UL, + 654366747UL, 821988674UL, 622228769UL, 884616336UL, 537773262UL, 350530896UL, + 319852839UL, 989482623UL, 468084379UL, 720832555UL, 168948290UL, 736909878UL, + 675666628UL, 546508280UL, 863340952UL, 404228624UL, 834196778UL, 508959829UL, + 23599720UL, 967735248UL, 96757260UL, 603332790UL, 862877297UL, 760110148UL, + 468436420UL, 401799145UL, 299404568UL, 230258509UL +}; +#endif +/* _mpd_ln10 is used directly for precisions smaller than MINALLOC_MAX*RDIGITS. + Otherwise, it serves as the initial approximation for calculating ln(10). */ +static const mpd_t _mpd_ln10 = { + MPD_STATIC|MPD_CONST_DATA, -(MPD_MINALLOC_MAX*MPD_RDIGITS-1), + MPD_MINALLOC_MAX*MPD_RDIGITS, MPD_MINALLOC_MAX, MPD_MINALLOC_MAX, + (mpd_uint_t *)mpd_ln10_data +}; + +/* + * Set 'result' to log(10). + * Ulp error: abs(result - log(10)) < ulp(log(10)) + * Relative error: abs(result - log(10)) < 5 * 10**-prec * log(10) + * + * NOTE: The relative error is not derived from the ulp error, but + * calculated separately using the fact that 23/10 < log(10) < 24/10. + */ +void +mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status) +{ + mpd_context_t varcontext, maxcontext; + MPD_NEW_STATIC(tmp, 0,0,0,0); + MPD_NEW_CONST(static10, 0,0,2,1,1,10); + mpd_ssize_t klist[MPD_MAX_PREC_LOG2]; + mpd_uint_t rnd; + mpd_ssize_t shift; + int i; + + assert(prec >= 1); + + shift = MPD_MINALLOC_MAX*MPD_RDIGITS-prec; + shift = shift < 0 ? 0 : shift; + + rnd = mpd_qshiftr(result, &_mpd_ln10, shift, status); + if (rnd == MPD_UINT_MAX) { + mpd_seterror(result, MPD_Malloc_error, status); + return; + } + result->exp = -(result->digits-1); + + mpd_maxcontext(&maxcontext); + if (prec < MPD_MINALLOC_MAX*MPD_RDIGITS) { + maxcontext.prec = prec; + _mpd_apply_round_excess(result, rnd, &maxcontext, status); + *status |= (MPD_Inexact|MPD_Rounded); + return; + } + + mpd_maxcontext(&varcontext); + varcontext.round = MPD_ROUND_TRUNC; + + i = ln_schedule_prec(klist, prec+2, -result->exp); + for (; i >= 0; i--) { + varcontext.prec = 2*klist[i]+3; + result->flags ^= MPD_NEG; + _mpd_qexp(&tmp, result, &varcontext, status); + result->flags ^= MPD_NEG; + mpd_qmul(&tmp, &static10, &tmp, &varcontext, status); + mpd_qsub(&tmp, &tmp, &one, &maxcontext, status); + mpd_qadd(result, result, &tmp, &maxcontext, status); + if (mpd_isspecial(result)) { + break; + } + } + + mpd_del(&tmp); + maxcontext.prec = prec; + mpd_qfinalize(result, &maxcontext, status); +} + +/* + * Initial approximations for the ln() iteration. The values have the + * following properties (established with both decimal.py and mpfr): + * + * Index 0 - 400, logarithms of x in [1.00, 5.00]: + * abs(lnapprox[i] * 10**-3 - log((i+100)/100)) < 10**-2 + * abs(lnapprox[i] * 10**-3 - log((i+1+100)/100)) < 10**-2 + * + * Index 401 - 899, logarithms of x in (0.500, 0.999]: + * abs(-lnapprox[i] * 10**-3 - log((i+100)/1000)) < 10**-2 + * abs(-lnapprox[i] * 10**-3 - log((i+1+100)/1000)) < 10**-2 + */ +static const uint16_t lnapprox[900] = { + /* index 0 - 400: log((i+100)/100) * 1000 */ + 0, 10, 20, 30, 39, 49, 58, 68, 77, 86, 95, 104, 113, 122, 131, 140, 148, 157, + 166, 174, 182, 191, 199, 207, 215, 223, 231, 239, 247, 255, 262, 270, 278, + 285, 293, 300, 308, 315, 322, 329, 336, 344, 351, 358, 365, 372, 378, 385, + 392, 399, 406, 412, 419, 425, 432, 438, 445, 451, 457, 464, 470, 476, 482, + 489, 495, 501, 507, 513, 519, 525, 531, 536, 542, 548, 554, 560, 565, 571, + 577, 582, 588, 593, 599, 604, 610, 615, 621, 626, 631, 637, 642, 647, 652, + 658, 663, 668, 673, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, + 732, 737, 742, 747, 751, 756, 761, 766, 770, 775, 779, 784, 788, 793, 798, + 802, 806, 811, 815, 820, 824, 829, 833, 837, 842, 846, 850, 854, 859, 863, + 867, 871, 876, 880, 884, 888, 892, 896, 900, 904, 908, 912, 916, 920, 924, + 928, 932, 936, 940, 944, 948, 952, 956, 959, 963, 967, 971, 975, 978, 982, + 986, 990, 993, 997, 1001, 1004, 1008, 1012, 1015, 1019, 1022, 1026, 1030, + 1033, 1037, 1040, 1044, 1047, 1051, 1054, 1058, 1061, 1065, 1068, 1072, 1075, + 1078, 1082, 1085, 1089, 1092, 1095, 1099, 1102, 1105, 1109, 1112, 1115, 1118, + 1122, 1125, 1128, 1131, 1135, 1138, 1141, 1144, 1147, 1151, 1154, 1157, 1160, + 1163, 1166, 1169, 1172, 1176, 1179, 1182, 1185, 1188, 1191, 1194, 1197, 1200, + 1203, 1206, 1209, 1212, 1215, 1218, 1221, 1224, 1227, 1230, 1233, 1235, 1238, + 1241, 1244, 1247, 1250, 1253, 1256, 1258, 1261, 1264, 1267, 1270, 1273, 1275, + 1278, 1281, 1284, 1286, 1289, 1292, 1295, 1297, 1300, 1303, 1306, 1308, 1311, + 1314, 1316, 1319, 1322, 1324, 1327, 1330, 1332, 1335, 1338, 1340, 1343, 1345, + 1348, 1351, 1353, 1356, 1358, 1361, 1364, 1366, 1369, 1371, 1374, 1376, 1379, + 1381, 1384, 1386, 1389, 1391, 1394, 1396, 1399, 1401, 1404, 1406, 1409, 1411, + 1413, 1416, 1418, 1421, 1423, 1426, 1428, 1430, 1433, 1435, 1437, 1440, 1442, + 1445, 1447, 1449, 1452, 1454, 1456, 1459, 1461, 1463, 1466, 1468, 1470, 1472, + 1475, 1477, 1479, 1482, 1484, 1486, 1488, 1491, 1493, 1495, 1497, 1500, 1502, + 1504, 1506, 1509, 1511, 1513, 1515, 1517, 1520, 1522, 1524, 1526, 1528, 1530, + 1533, 1535, 1537, 1539, 1541, 1543, 1545, 1548, 1550, 1552, 1554, 1556, 1558, + 1560, 1562, 1564, 1567, 1569, 1571, 1573, 1575, 1577, 1579, 1581, 1583, 1585, + 1587, 1589, 1591, 1593, 1595, 1597, 1599, 1601, 1603, 1605, 1607, 1609, + /* index 401 - 899: -log((i+100)/1000) * 1000 */ + 691, 689, 687, 685, 683, 681, 679, 677, 675, 673, 671, 669, 668, 666, 664, + 662, 660, 658, 656, 654, 652, 650, 648, 646, 644, 642, 641, 639, 637, 635, + 633, 631, 629, 627, 626, 624, 622, 620, 618, 616, 614, 612, 611, 609, 607, + 605, 603, 602, 600, 598, 596, 594, 592, 591, 589, 587, 585, 583, 582, 580, + 578, 576, 574, 573, 571, 569, 567, 566, 564, 562, 560, 559, 557, 555, 553, + 552, 550, 548, 546, 545, 543, 541, 540, 538, 536, 534, 533, 531, 529, 528, + 526, 524, 523, 521, 519, 518, 516, 514, 512, 511, 509, 508, 506, 504, 502, + 501, 499, 498, 496, 494, 493, 491, 489, 488, 486, 484, 483, 481, 480, 478, + 476, 475, 473, 472, 470, 468, 467, 465, 464, 462, 460, 459, 457, 456, 454, + 453, 451, 449, 448, 446, 445, 443, 442, 440, 438, 437, 435, 434, 432, 431, + 429, 428, 426, 425, 423, 422, 420, 419, 417, 416, 414, 412, 411, 410, 408, + 406, 405, 404, 402, 400, 399, 398, 396, 394, 393, 392, 390, 389, 387, 386, + 384, 383, 381, 380, 378, 377, 375, 374, 372, 371, 370, 368, 367, 365, 364, + 362, 361, 360, 358, 357, 355, 354, 352, 351, 350, 348, 347, 345, 344, 342, + 341, 340, 338, 337, 336, 334, 333, 331, 330, 328, 327, 326, 324, 323, 322, + 320, 319, 318, 316, 315, 313, 312, 311, 309, 308, 306, 305, 304, 302, 301, + 300, 298, 297, 296, 294, 293, 292, 290, 289, 288, 286, 285, 284, 282, 281, + 280, 278, 277, 276, 274, 273, 272, 270, 269, 268, 267, 265, 264, 263, 261, + 260, 259, 258, 256, 255, 254, 252, 251, 250, 248, 247, 246, 245, 243, 242, + 241, 240, 238, 237, 236, 234, 233, 232, 231, 229, 228, 227, 226, 224, 223, + 222, 221, 219, 218, 217, 216, 214, 213, 212, 211, 210, 208, 207, 206, 205, + 203, 202, 201, 200, 198, 197, 196, 195, 194, 192, 191, 190, 189, 188, 186, + 185, 184, 183, 182, 180, 179, 178, 177, 176, 174, 173, 172, 171, 170, 168, + 167, 166, 165, 164, 162, 161, 160, 159, 158, 157, 156, 154, 153, 152, 151, + 150, 148, 147, 146, 145, 144, 143, 142, 140, 139, 138, 137, 136, 135, 134, + 132, 131, 130, 129, 128, 127, 126, 124, 123, 122, 121, 120, 119, 118, 116, + 115, 114, 113, 112, 111, 110, 109, 108, 106, 105, 104, 103, 102, 101, 100, + 99, 98, 97, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 84, 83, 82, 81, 80, 79, + 78, 77, 76, 75, 74, 73, 72, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, + 58, 57, 56, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, + 38, 37, 36, 35, 34, 33, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, + 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 +}; + +/* + * Internal ln() function that does not check for specials, zero or one. + * Relative error: abs(result - log(a)) < 0.1 * 10**-prec * abs(log(a)) + */ +static void +_mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t varcontext, maxcontext; + mpd_t *z = (mpd_t *) result; + MPD_NEW_STATIC(v,0,0,0,0); + MPD_NEW_STATIC(vtmp,0,0,0,0); + MPD_NEW_STATIC(tmp,0,0,0,0); + mpd_ssize_t klist[MPD_MAX_PREC_LOG2]; + mpd_ssize_t maxprec, shift, t; + mpd_ssize_t a_digits, a_exp; + mpd_uint_t dummy, x; + int i; + + assert(!mpd_isspecial(a) && !mpd_iszerocoeff(a)); + + /* + * We are calculating ln(a) = ln(v * 10^t) = ln(v) + t*ln(10), + * where 0.5 < v <= 5. + */ + if (!mpd_qcopy(&v, a, status)) { + mpd_seterror(result, MPD_Malloc_error, status); + goto finish; + } + + /* Initial approximation: we have at least one non-zero digit */ + _mpd_get_msdigits(&dummy, &x, &v, 3); + if (x < 10) x *= 10; + if (x < 100) x *= 10; + x -= 100; + + /* a may equal z */ + a_digits = a->digits; + a_exp = a->exp; + + mpd_minalloc(z); + mpd_clear_flags(z); + z->data[0] = lnapprox[x]; + z->len = 1; + z->exp = -3; + mpd_setdigits(z); + + if (x <= 400) { + /* Reduce the input operand to 1.00 <= v <= 5.00. Let y = x + 100, + * so 100 <= y <= 500. Since y contains the most significant digits + * of v, y/100 <= v < (y+1)/100 and abs(z - log(v)) < 10**-2. */ + v.exp = -(a_digits - 1); + t = a_exp + a_digits - 1; + } + else { + /* Reduce the input operand to 0.500 < v <= 0.999. Let y = x + 100, + * so 500 < y <= 999. Since y contains the most significant digits + * of v, y/1000 <= v < (y+1)/1000 and abs(z - log(v)) < 10**-2. */ + v.exp = -a_digits; + t = a_exp + a_digits; + mpd_set_negative(z); + } + + mpd_maxcontext(&maxcontext); + mpd_maxcontext(&varcontext); + varcontext.round = MPD_ROUND_TRUNC; + + maxprec = ctx->prec + 2; + if (t == 0 && (x <= 15 || x >= 800)) { + /* 0.900 <= v <= 1.15: Estimate the magnitude of the logarithm. + * If ln(v) will underflow, skip the loop. Otherwise, adjust the + * precision upwards in order to obtain a sufficient number of + * significant digits. + * + * Case v > 1: + * abs((v-1)/10) < abs((v-1)/v) < abs(ln(v)) < abs(v-1) + * Case v < 1: + * abs(v-1) < abs(ln(v)) < abs((v-1)/v) < abs((v-1)*10) + */ + int cmp = _mpd_cmp(&v, &one); + + /* Upper bound (assume v > 1): abs(v-1), unrounded */ + _mpd_qsub(&tmp, &v, &one, &maxcontext, &maxcontext.status); + if (maxcontext.status & MPD_Errors) { + mpd_seterror(result, MPD_Malloc_error, status); + goto finish; + } + + if (cmp < 0) { + /* v < 1: abs((v-1)*10) */ + tmp.exp += 1; + } + if (mpd_adjexp(&tmp) < mpd_etiny(ctx)) { + /* The upper bound is less than etiny: Underflow to zero */ + _settriple(result, (cmp<0), 1, mpd_etiny(ctx)-1); + goto finish; + } + /* Lower bound: abs((v-1)/10) or abs(v-1) */ + tmp.exp -= 1; + if (mpd_adjexp(&tmp) < 0) { + /* Absolute error of the loop: abs(z - log(v)) < 10**-p. If + * p = ctx->prec+2-adjexp(lower), then the relative error of + * the result is (using 10**adjexp(x) <= abs(x)): + * + * abs(z - log(v)) / abs(log(v)) < 10**-p / abs(log(v)) + * <= 10**(-ctx->prec-2) + */ + maxprec = maxprec - mpd_adjexp(&tmp); + } + } + + i = ln_schedule_prec(klist, maxprec, 2); + for (; i >= 0; i--) { + varcontext.prec = 2*klist[i]+3; + z->flags ^= MPD_NEG; + _mpd_qexp(&tmp, z, &varcontext, status); + z->flags ^= MPD_NEG; + + if (v.digits > varcontext.prec) { + shift = v.digits - varcontext.prec; + mpd_qshiftr(&vtmp, &v, shift, status); + vtmp.exp += shift; + mpd_qmul(&tmp, &vtmp, &tmp, &varcontext, status); + } + else { + mpd_qmul(&tmp, &v, &tmp, &varcontext, status); + } + + mpd_qsub(&tmp, &tmp, &one, &maxcontext, status); + mpd_qadd(z, z, &tmp, &maxcontext, status); + if (mpd_isspecial(z)) { + break; + } + } + + /* + * Case t == 0: + * t * log(10) == 0, the result does not change and the analysis + * above applies. If v < 0.900 or v > 1.15, the relative error is + * less than 10**(-ctx.prec-1). + * Case t != 0: + * z := approx(log(v)) + * y := approx(log(10)) + * p := maxprec = ctx->prec + 2 + * Absolute errors: + * 1) abs(z - log(v)) < 10**-p + * 2) abs(y - log(10)) < 10**-p + * The multiplication is exact, so: + * 3) abs(t*y - t*log(10)) < t*10**-p + * The sum is exact, so: + * 4) abs((z + t*y) - (log(v) + t*log(10))) < (abs(t) + 1) * 10**-p + * Bounds for log(v) and log(10): + * 5) -7/10 < log(v) < 17/10 + * 6) 23/10 < log(10) < 24/10 + * Using 4), 5), 6) and t != 0, the relative error is: + * + * 7) relerr < ((abs(t) + 1)*10**-p) / abs(log(v) + t*log(10)) + * < 0.5 * 10**(-p + 1) = 0.5 * 10**(-ctx->prec-1) + */ + mpd_qln10(&v, maxprec+1, status); + mpd_qmul_ssize(&tmp, &v, t, &maxcontext, status); + mpd_qadd(result, &tmp, z, &maxcontext, status); + + +finish: + *status |= (MPD_Inexact|MPD_Rounded); + mpd_del(&v); + mpd_del(&vtmp); + mpd_del(&tmp); +} + +/* ln(a) */ +void +mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t workctx; + mpd_ssize_t adjexp, t; + + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + if (mpd_isnegative(a)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + mpd_setspecial(result, MPD_POS, MPD_INF); + return; + } + if (mpd_iszerocoeff(a)) { + mpd_setspecial(result, MPD_NEG, MPD_INF); + return; + } + if (mpd_isnegative(a)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (_mpd_cmp(a, &one) == 0) { + _settriple(result, MPD_POS, 0, 0); + return; + } + /* + * Check if the result will overflow (0 < x, x != 1): + * 1) log10(x) < 0 iff adjexp(x) < 0 + * 2) 0 < x /\ x <= y ==> adjexp(x) <= adjexp(y) + * 3) 0 < x /\ x != 1 ==> 2 * abs(log10(x)) < abs(log(x)) + * 4) adjexp(x) <= log10(x) < adjexp(x) + 1 + * + * Case adjexp(x) >= 0: + * 5) 2 * adjexp(x) < abs(log(x)) + * Case adjexp(x) > 0: + * 6) adjexp(2 * adjexp(x)) <= adjexp(abs(log(x))) + * Case adjexp(x) == 0: + * mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered) + * + * Case adjexp(x) < 0: + * 7) 2 * (-adjexp(x) - 1) < abs(log(x)) + * Case adjexp(x) < -1: + * 8) adjexp(2 * (-adjexp(x) - 1)) <= adjexp(abs(log(x))) + * Case adjexp(x) == -1: + * mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered) + */ + adjexp = mpd_adjexp(a); + t = (adjexp < 0) ? -adjexp-1 : adjexp; + t *= 2; + if (mpd_exp_digits(t)-1 > ctx->emax) { + *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded; + mpd_setspecial(result, (adjexp<0), MPD_INF); + return; + } + + workctx = *ctx; + workctx.round = MPD_ROUND_HALF_EVEN; + + if (ctx->allcr) { + MPD_NEW_STATIC(t1, 0,0,0,0); + MPD_NEW_STATIC(t2, 0,0,0,0); + MPD_NEW_STATIC(ulp, 0,0,0,0); + MPD_NEW_STATIC(aa, 0,0,0,0); + mpd_ssize_t prec; + + if (result == a) { + if (!mpd_qcopy(&aa, a, status)) { + mpd_seterror(result, MPD_Malloc_error, status); + return; + } + a = &aa; + } + + workctx.clamp = 0; + prec = ctx->prec + 3; + while (1) { + workctx.prec = prec; + _mpd_qln(result, a, &workctx, status); + _ssettriple(&ulp, MPD_POS, 1, + result->exp + result->digits-workctx.prec); + + workctx.prec = ctx->prec; + mpd_qadd(&t1, result, &ulp, &workctx, &workctx.status); + mpd_qsub(&t2, result, &ulp, &workctx, &workctx.status); + if (mpd_isspecial(result) || mpd_iszerocoeff(result) || + mpd_qcmp(&t1, &t2, status) == 0) { + workctx.clamp = ctx->clamp; + mpd_check_underflow(result, &workctx, status); + mpd_qfinalize(result, &workctx, status); + break; + } + prec += MPD_RDIGITS; + } + mpd_del(&t1); + mpd_del(&t2); + mpd_del(&ulp); + mpd_del(&aa); + } + else { + _mpd_qln(result, a, &workctx, status); + mpd_check_underflow(result, &workctx, status); + mpd_qfinalize(result, &workctx, status); + } +} + +/* + * Internal log10() function that does not check for specials, zero or one. + * Case SKIP_FINALIZE: + * Relative error: abs(result - log10(a)) < 0.1 * 10**-prec * abs(log10(a)) + * Case DO_FINALIZE: + * Ulp error: abs(result - log10(a)) < ulp(log10(a)) + */ +enum {SKIP_FINALIZE, DO_FINALIZE}; +static void +_mpd_qlog10(int action, mpd_t *result, const mpd_t *a, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t workctx; + MPD_NEW_STATIC(ln10,0,0,0,0); + + mpd_maxcontext(&workctx); + workctx.prec = ctx->prec + 3; + /* relative error: 0.1 * 10**(-p-3). The specific underflow shortcut + * in _mpd_qln() does not change the final result. */ + _mpd_qln(result, a, &workctx, status); + /* relative error: 5 * 10**(-p-3) */ + mpd_qln10(&ln10, workctx.prec, status); + + if (action == DO_FINALIZE) { + workctx = *ctx; + workctx.round = MPD_ROUND_HALF_EVEN; + } + /* SKIP_FINALIZE: relative error: 5 * 10**(-p-3) */ + _mpd_qdiv(NO_IDEAL_EXP, result, result, &ln10, &workctx, status); + + mpd_del(&ln10); +} + +/* log10(a) */ +void +mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t workctx; + mpd_ssize_t adjexp, t; + + workctx = *ctx; + workctx.round = MPD_ROUND_HALF_EVEN; + + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + if (mpd_isnegative(a)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + mpd_setspecial(result, MPD_POS, MPD_INF); + return; + } + if (mpd_iszerocoeff(a)) { + mpd_setspecial(result, MPD_NEG, MPD_INF); + return; + } + if (mpd_isnegative(a)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (mpd_coeff_ispow10(a)) { + uint8_t sign = 0; + adjexp = mpd_adjexp(a); + if (adjexp < 0) { + sign = 1; + adjexp = -adjexp; + } + _settriple(result, sign, adjexp, 0); + mpd_qfinalize(result, &workctx, status); + return; + } + /* + * Check if the result will overflow (0 < x, x != 1): + * 1) log10(x) < 0 iff adjexp(x) < 0 + * 2) 0 < x /\ x <= y ==> adjexp(x) <= adjexp(y) + * 3) adjexp(x) <= log10(x) < adjexp(x) + 1 + * + * Case adjexp(x) >= 0: + * 4) adjexp(x) <= abs(log10(x)) + * Case adjexp(x) > 0: + * 5) adjexp(adjexp(x)) <= adjexp(abs(log10(x))) + * Case adjexp(x) == 0: + * mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered) + * + * Case adjexp(x) < 0: + * 6) -adjexp(x) - 1 < abs(log10(x)) + * Case adjexp(x) < -1: + * 7) adjexp(-adjexp(x) - 1) <= adjexp(abs(log(x))) + * Case adjexp(x) == -1: + * mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered) + */ + adjexp = mpd_adjexp(a); + t = (adjexp < 0) ? -adjexp-1 : adjexp; + if (mpd_exp_digits(t)-1 > ctx->emax) { + *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded; + mpd_setspecial(result, (adjexp<0), MPD_INF); + return; + } + + if (ctx->allcr) { + MPD_NEW_STATIC(t1, 0,0,0,0); + MPD_NEW_STATIC(t2, 0,0,0,0); + MPD_NEW_STATIC(ulp, 0,0,0,0); + MPD_NEW_STATIC(aa, 0,0,0,0); + mpd_ssize_t prec; + + if (result == a) { + if (!mpd_qcopy(&aa, a, status)) { + mpd_seterror(result, MPD_Malloc_error, status); + return; + } + a = &aa; + } + + workctx.clamp = 0; + prec = ctx->prec + 3; + while (1) { + workctx.prec = prec; + _mpd_qlog10(SKIP_FINALIZE, result, a, &workctx, status); + _ssettriple(&ulp, MPD_POS, 1, + result->exp + result->digits-workctx.prec); + + workctx.prec = ctx->prec; + mpd_qadd(&t1, result, &ulp, &workctx, &workctx.status); + mpd_qsub(&t2, result, &ulp, &workctx, &workctx.status); + if (mpd_isspecial(result) || mpd_iszerocoeff(result) || + mpd_qcmp(&t1, &t2, status) == 0) { + workctx.clamp = ctx->clamp; + mpd_check_underflow(result, &workctx, status); + mpd_qfinalize(result, &workctx, status); + break; + } + prec += MPD_RDIGITS; + } + mpd_del(&t1); + mpd_del(&t2); + mpd_del(&ulp); + mpd_del(&aa); + } + else { + _mpd_qlog10(DO_FINALIZE, result, a, &workctx, status); + mpd_check_underflow(result, &workctx, status); + } +} + +/* + * Maximum of the two operands. Attention: If one operand is a quiet NaN and the + * other is numeric, the numeric operand is returned. This may not be what one + * expects. + */ +void +mpd_qmax(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + int c; + + if (mpd_isqnan(a) && !mpd_isnan(b)) { + mpd_qcopy(result, b, status); + } + else if (mpd_isqnan(b) && !mpd_isnan(a)) { + mpd_qcopy(result, a, status); + } + else if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + else { + c = _mpd_cmp(a, b); + if (c == 0) { + c = _mpd_cmp_numequal(a, b); + } + + if (c < 0) { + mpd_qcopy(result, b, status); + } + else { + mpd_qcopy(result, a, status); + } + } + + mpd_qfinalize(result, ctx, status); +} + +/* + * Maximum magnitude: Same as mpd_max(), but compares the operands with their + * sign ignored. + */ +void +mpd_qmax_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + int c; + + if (mpd_isqnan(a) && !mpd_isnan(b)) { + mpd_qcopy(result, b, status); + } + else if (mpd_isqnan(b) && !mpd_isnan(a)) { + mpd_qcopy(result, a, status); + } + else if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + else { + c = _mpd_cmp_abs(a, b); + if (c == 0) { + c = _mpd_cmp_numequal(a, b); + } + + if (c < 0) { + mpd_qcopy(result, b, status); + } + else { + mpd_qcopy(result, a, status); + } + } + + mpd_qfinalize(result, ctx, status); +} + +/* + * Minimum of the two operands. Attention: If one operand is a quiet NaN and the + * other is numeric, the numeric operand is returned. This may not be what one + * expects. + */ +void +mpd_qmin(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + int c; + + if (mpd_isqnan(a) && !mpd_isnan(b)) { + mpd_qcopy(result, b, status); + } + else if (mpd_isqnan(b) && !mpd_isnan(a)) { + mpd_qcopy(result, a, status); + } + else if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + else { + c = _mpd_cmp(a, b); + if (c == 0) { + c = _mpd_cmp_numequal(a, b); + } + + if (c < 0) { + mpd_qcopy(result, a, status); + } + else { + mpd_qcopy(result, b, status); + } + } + + mpd_qfinalize(result, ctx, status); +} + +/* + * Minimum magnitude: Same as mpd_min(), but compares the operands with their + * sign ignored. + */ +void +mpd_qmin_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + int c; + + if (mpd_isqnan(a) && !mpd_isnan(b)) { + mpd_qcopy(result, b, status); + } + else if (mpd_isqnan(b) && !mpd_isnan(a)) { + mpd_qcopy(result, a, status); + } + else if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + else { + c = _mpd_cmp_abs(a, b); + if (c == 0) { + c = _mpd_cmp_numequal(a, b); + } + + if (c < 0) { + mpd_qcopy(result, a, status); + } + else { + mpd_qcopy(result, b, status); + } + } + + mpd_qfinalize(result, ctx, status); +} + +/* Minimum space needed for the result array in _karatsuba_rec(). */ +static inline mpd_size_t +_kmul_resultsize(mpd_size_t la, mpd_size_t lb) +{ + mpd_size_t n, m; + + n = add_size_t(la, lb); + n = add_size_t(n, 1); + + m = (la+1)/2 + 1; + m = mul_size_t(m, 3); + + return (m > n) ? m : n; +} + +/* Work space needed in _karatsuba_rec(). lim >= 4 */ +static inline mpd_size_t +_kmul_worksize(mpd_size_t n, mpd_size_t lim) +{ + mpd_size_t m; + + if (n <= lim) { + return 0; + } + + m = (n+1)/2 + 1; + + return add_size_t(mul_size_t(m, 2), _kmul_worksize(m, lim)); +} + + +#define MPD_KARATSUBA_BASECASE 16 /* must be >= 4 */ + +/* + * Add the product of a and b to c. + * c must be _kmul_resultsize(la, lb) in size. + * w is used as a work array and must be _kmul_worksize(a, lim) in size. + * Roman E. Maeder, Storage Allocation for the Karatsuba Integer Multiplication + * Algorithm. In "Design and implementation of symbolic computation systems", + * Springer, 1993, ISBN 354057235X, 9783540572350. + */ +static void +_karatsuba_rec(mpd_uint_t *c, const mpd_uint_t *a, const mpd_uint_t *b, + mpd_uint_t *w, mpd_size_t la, mpd_size_t lb) +{ + mpd_size_t m, lt; + + assert(la >= lb && lb > 0); + assert(la <= MPD_KARATSUBA_BASECASE || w != NULL); + + if (la <= MPD_KARATSUBA_BASECASE) { + _mpd_basemul(c, a, b, la, lb); + return; + } + + m = (la+1)/2; /* ceil(la/2) */ + + /* lb <= m < la */ + if (lb <= m) { + + /* lb can now be larger than la-m */ + if (lb > la-m) { + lt = lb + lb + 1; /* space needed for result array */ + mpd_uint_zero(w, lt); /* clear result array */ + _karatsuba_rec(w, b, a+m, w+lt, lb, la-m); /* b*ah */ + } + else { + lt = (la-m) + (la-m) + 1; /* space needed for result array */ + mpd_uint_zero(w, lt); /* clear result array */ + _karatsuba_rec(w, a+m, b, w+lt, la-m, lb); /* ah*b */ + } + _mpd_baseaddto(c+m, w, (la-m)+lb); /* add ah*b*B**m */ + + lt = m + m + 1; /* space needed for the result array */ + mpd_uint_zero(w, lt); /* clear result array */ + _karatsuba_rec(w, a, b, w+lt, m, lb); /* al*b */ + _mpd_baseaddto(c, w, m+lb); /* add al*b */ + + return; + } + + /* la >= lb > m */ + memcpy(w, a, m * sizeof *w); + w[m] = 0; + _mpd_baseaddto(w, a+m, la-m); + + memcpy(w+(m+1), b, m * sizeof *w); + w[m+1+m] = 0; + _mpd_baseaddto(w+(m+1), b+m, lb-m); + + _karatsuba_rec(c+m, w, w+(m+1), w+2*(m+1), m+1, m+1); + + lt = (la-m) + (la-m) + 1; + mpd_uint_zero(w, lt); + + _karatsuba_rec(w, a+m, b+m, w+lt, la-m, lb-m); + + _mpd_baseaddto(c+2*m, w, (la-m) + (lb-m)); + _mpd_basesubfrom(c+m, w, (la-m) + (lb-m)); + + lt = m + m + 1; + mpd_uint_zero(w, lt); + + _karatsuba_rec(w, a, b, w+lt, m, m); + _mpd_baseaddto(c, w, m+m); + _mpd_basesubfrom(c+m, w, m+m); + + return; +} + +/* + * Multiply u and v, using Karatsuba multiplication. Returns a pointer + * to the result or NULL in case of failure (malloc error). + * Conditions: ulen >= vlen, ulen >= 4 + */ +static mpd_uint_t * +_mpd_kmul(const mpd_uint_t *u, const mpd_uint_t *v, + mpd_size_t ulen, mpd_size_t vlen, + mpd_size_t *rsize) +{ + mpd_uint_t *result = NULL, *w = NULL; + mpd_size_t m; + + assert(ulen >= 4); + assert(ulen >= vlen); + + *rsize = _kmul_resultsize(ulen, vlen); + if ((result = mpd_calloc(*rsize, sizeof *result)) == NULL) { + return NULL; + } + + m = _kmul_worksize(ulen, MPD_KARATSUBA_BASECASE); + if (m && ((w = mpd_calloc(m, sizeof *w)) == NULL)) { + mpd_free(result); + return NULL; + } + + _karatsuba_rec(result, u, v, w, ulen, vlen); + + + if (w) mpd_free(w); + return result; +} + + +/* + * Determine the minimum length for the number theoretic transform. Valid + * transform lengths are 2**n or 3*2**n, where 2**n <= MPD_MAXTRANSFORM_2N. + * The function finds the shortest length m such that rsize <= m. + */ +static inline mpd_size_t +_mpd_get_transform_len(mpd_size_t rsize) +{ + mpd_size_t log2rsize; + mpd_size_t x, step; + + assert(rsize >= 4); + log2rsize = mpd_bsr(rsize); + + if (rsize <= 1024) { + /* 2**n is faster in this range. */ + x = ((mpd_size_t)1)<>1; + x += step; + return (rsize <= x) ? x : x + step; + } + else if (rsize <= MPD_MAXTRANSFORM_2N+MPD_MAXTRANSFORM_2N/2) { + return MPD_MAXTRANSFORM_2N+MPD_MAXTRANSFORM_2N/2; + } + else if (rsize <= 3*MPD_MAXTRANSFORM_2N) { + return 3*MPD_MAXTRANSFORM_2N; + } + else { + return MPD_SIZE_MAX; + } +} + +#ifdef PPRO +#ifndef _MSC_VER +static inline unsigned short +_mpd_get_control87(void) +{ + unsigned short cw; + + __asm__ __volatile__ ("fnstcw %0" : "=m" (cw)); + return cw; +} + +static inline void +_mpd_set_control87(unsigned short cw) +{ + __asm__ __volatile__ ("fldcw %0" : : "m" (cw)); +} +#endif + +static unsigned int +mpd_set_fenv(void) +{ + unsigned int cw; +#ifdef _MSC_VER + unsigned int flags = + _EM_INVALID|_EM_DENORMAL|_EM_ZERODIVIDE|_EM_OVERFLOW| + _EM_UNDERFLOW|_EM_INEXACT|_RC_CHOP|_PC_64; + unsigned int mask = _MCW_EM|_MCW_RC|_MCW_PC; + unsigned int dummy; + + __control87_2(0, 0, &cw, NULL); + __control87_2(flags, mask, &dummy, NULL); +#else + cw = _mpd_get_control87(); + _mpd_set_control87(cw|0xF3F); +#endif + return cw; +} + +static void +mpd_restore_fenv(unsigned int cw) +{ +#ifdef _MSC_VER + unsigned int mask = _MCW_EM|_MCW_RC|_MCW_PC; + unsigned int dummy; + + __control87_2(cw, mask, &dummy, NULL); +#else + _mpd_set_control87((unsigned short)cw); +#endif +} +#endif /* PPRO */ + +/* + * Multiply u and v, using the fast number theoretic transform. Returns + * a pointer to the result or NULL in case of failure (malloc error). + */ +static mpd_uint_t * +_mpd_fntmul(const mpd_uint_t *u, const mpd_uint_t *v, + mpd_size_t ulen, mpd_size_t vlen, + mpd_size_t *rsize) +{ + mpd_uint_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *vtmp = NULL; + mpd_size_t n; + +#ifdef PPRO + unsigned int cw; + cw = mpd_set_fenv(); +#endif + + *rsize = add_size_t(ulen, vlen); + if ((n = _mpd_get_transform_len(*rsize)) == MPD_SIZE_MAX) { + goto malloc_error; + } + + if ((c1 = mpd_calloc(n, sizeof *c1)) == NULL) { + goto malloc_error; + } + if ((c2 = mpd_calloc(n, sizeof *c2)) == NULL) { + goto malloc_error; + } + if ((c3 = mpd_calloc(n, sizeof *c3)) == NULL) { + goto malloc_error; + } + + memcpy(c1, u, ulen * (sizeof *c1)); + memcpy(c2, u, ulen * (sizeof *c2)); + memcpy(c3, u, ulen * (sizeof *c3)); + + if (u == v) { + if (!fnt_autoconvolute(c1, n, P1) || + !fnt_autoconvolute(c2, n, P2) || + !fnt_autoconvolute(c3, n, P3)) { + goto malloc_error; + } + } + else { + if ((vtmp = mpd_calloc(n, sizeof *vtmp)) == NULL) { + goto malloc_error; + } + + memcpy(vtmp, v, vlen * (sizeof *vtmp)); + if (!fnt_convolute(c1, vtmp, n, P1)) { + mpd_free(vtmp); + goto malloc_error; + } + + memcpy(vtmp, v, vlen * (sizeof *vtmp)); + mpd_uint_zero(vtmp+vlen, n-vlen); + if (!fnt_convolute(c2, vtmp, n, P2)) { + mpd_free(vtmp); + goto malloc_error; + } + + memcpy(vtmp, v, vlen * (sizeof *vtmp)); + mpd_uint_zero(vtmp+vlen, n-vlen); + if (!fnt_convolute(c3, vtmp, n, P3)) { + mpd_free(vtmp); + goto malloc_error; + } + + mpd_free(vtmp); + } + + crt3(c1, c2, c3, *rsize); + +out: +#ifdef PPRO + mpd_restore_fenv(cw); +#endif + if (c2) mpd_free(c2); + if (c3) mpd_free(c3); + return c1; + +malloc_error: + if (c1) mpd_free(c1); + c1 = NULL; + goto out; +} + + +/* + * Karatsuba multiplication with FNT/basemul as the base case. + */ +static int +_karatsuba_rec_fnt(mpd_uint_t *c, const mpd_uint_t *a, const mpd_uint_t *b, + mpd_uint_t *w, mpd_size_t la, mpd_size_t lb) +{ + mpd_size_t m, lt; + + assert(la >= lb && lb > 0); + assert(la <= 3*(MPD_MAXTRANSFORM_2N/2) || w != NULL); + + if (la <= 3*(MPD_MAXTRANSFORM_2N/2)) { + + if (lb <= 192) { + _mpd_basemul(c, b, a, lb, la); + } + else { + mpd_uint_t *result; + mpd_size_t dummy; + + if ((result = _mpd_fntmul(a, b, la, lb, &dummy)) == NULL) { + return 0; + } + memcpy(c, result, (la+lb) * (sizeof *result)); + mpd_free(result); + } + return 1; + } + + m = (la+1)/2; /* ceil(la/2) */ + + /* lb <= m < la */ + if (lb <= m) { + + /* lb can now be larger than la-m */ + if (lb > la-m) { + lt = lb + lb + 1; /* space needed for result array */ + mpd_uint_zero(w, lt); /* clear result array */ + if (!_karatsuba_rec_fnt(w, b, a+m, w+lt, lb, la-m)) { /* b*ah */ + return 0; /* GCOV_UNLIKELY */ + } + } + else { + lt = (la-m) + (la-m) + 1; /* space needed for result array */ + mpd_uint_zero(w, lt); /* clear result array */ + if (!_karatsuba_rec_fnt(w, a+m, b, w+lt, la-m, lb)) { /* ah*b */ + return 0; /* GCOV_UNLIKELY */ + } + } + _mpd_baseaddto(c+m, w, (la-m)+lb); /* add ah*b*B**m */ + + lt = m + m + 1; /* space needed for the result array */ + mpd_uint_zero(w, lt); /* clear result array */ + if (!_karatsuba_rec_fnt(w, a, b, w+lt, m, lb)) { /* al*b */ + return 0; /* GCOV_UNLIKELY */ + } + _mpd_baseaddto(c, w, m+lb); /* add al*b */ + + return 1; + } + + /* la >= lb > m */ + memcpy(w, a, m * sizeof *w); + w[m] = 0; + _mpd_baseaddto(w, a+m, la-m); + + memcpy(w+(m+1), b, m * sizeof *w); + w[m+1+m] = 0; + _mpd_baseaddto(w+(m+1), b+m, lb-m); + + if (!_karatsuba_rec_fnt(c+m, w, w+(m+1), w+2*(m+1), m+1, m+1)) { + return 0; /* GCOV_UNLIKELY */ + } + + lt = (la-m) + (la-m) + 1; + mpd_uint_zero(w, lt); + + if (!_karatsuba_rec_fnt(w, a+m, b+m, w+lt, la-m, lb-m)) { + return 0; /* GCOV_UNLIKELY */ + } + + _mpd_baseaddto(c+2*m, w, (la-m) + (lb-m)); + _mpd_basesubfrom(c+m, w, (la-m) + (lb-m)); + + lt = m + m + 1; + mpd_uint_zero(w, lt); + + if (!_karatsuba_rec_fnt(w, a, b, w+lt, m, m)) { + return 0; /* GCOV_UNLIKELY */ + } + _mpd_baseaddto(c, w, m+m); + _mpd_basesubfrom(c+m, w, m+m); + + return 1; +} + +/* + * Multiply u and v, using Karatsuba multiplication with the FNT as the + * base case. Returns a pointer to the result or NULL in case of failure + * (malloc error). Conditions: ulen >= vlen, ulen >= 4. + */ +static mpd_uint_t * +_mpd_kmul_fnt(const mpd_uint_t *u, const mpd_uint_t *v, + mpd_size_t ulen, mpd_size_t vlen, + mpd_size_t *rsize) +{ + mpd_uint_t *result = NULL, *w = NULL; + mpd_size_t m; + + assert(ulen >= 4); + assert(ulen >= vlen); + + *rsize = _kmul_resultsize(ulen, vlen); + if ((result = mpd_calloc(*rsize, sizeof *result)) == NULL) { + return NULL; + } + + m = _kmul_worksize(ulen, 3*(MPD_MAXTRANSFORM_2N/2)); + if (m && ((w = mpd_calloc(m, sizeof *w)) == NULL)) { + mpd_free(result); /* GCOV_UNLIKELY */ + return NULL; /* GCOV_UNLIKELY */ + } + + if (!_karatsuba_rec_fnt(result, u, v, w, ulen, vlen)) { + mpd_free(result); + result = NULL; + } + + + if (w) mpd_free(w); + return result; +} + + +/* Deal with the special cases of multiplying infinities. */ +static void +_mpd_qmul_inf(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status) +{ + if (mpd_isinfinite(a)) { + if (mpd_iszero(b)) { + mpd_seterror(result, MPD_Invalid_operation, status); + } + else { + mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF); + } + return; + } + assert(mpd_isinfinite(b)); + if (mpd_iszero(a)) { + mpd_seterror(result, MPD_Invalid_operation, status); + } + else { + mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF); + } +} + +/* + * Internal function: Multiply a and b. _mpd_qmul deals with specials but + * does NOT finalize the result. This is for use in mpd_fma(). + */ +static inline void +_mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + const mpd_t *big = a, *small = b; + mpd_uint_t *rdata = NULL; + mpd_uint_t rbuf[MPD_MINALLOC_MAX]; + mpd_size_t rsize, i; + + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + _mpd_qmul_inf(result, a, b, status); + return; + } + + if (small->len > big->len) { + _mpd_ptrswap(&big, &small); + } + + rsize = big->len + small->len; + + if (big->len == 1) { + _mpd_singlemul(result->data, big->data[0], small->data[0]); + goto finish; + } + if (rsize <= (mpd_size_t)MPD_MINALLOC_MAX) { + if (big->len == 2) { + _mpd_mul_2_le2(rbuf, big->data, small->data, small->len); + } + else { + mpd_uint_zero(rbuf, rsize); + if (small->len == 1) { + _mpd_shortmul(rbuf, big->data, big->len, small->data[0]); + } + else { + _mpd_basemul(rbuf, small->data, big->data, small->len, big->len); + } + } + if (!mpd_qresize(result, rsize, status)) { + return; + } + for(i = 0; i < rsize; i++) { + result->data[i] = rbuf[i]; + } + goto finish; + } + + + if (small->len <= 256) { + rdata = mpd_calloc(rsize, sizeof *rdata); + if (rdata != NULL) { + if (small->len == 1) { + _mpd_shortmul(rdata, big->data, big->len, small->data[0]); + } + else { + _mpd_basemul(rdata, small->data, big->data, small->len, big->len); + } + } + } + else if (rsize <= 1024) { + rdata = _mpd_kmul(big->data, small->data, big->len, small->len, &rsize); + } + else if (rsize <= 3*MPD_MAXTRANSFORM_2N) { + rdata = _mpd_fntmul(big->data, small->data, big->len, small->len, &rsize); + } + else { + rdata = _mpd_kmul_fnt(big->data, small->data, big->len, small->len, &rsize); + } + + if (rdata == NULL) { + mpd_seterror(result, MPD_Malloc_error, status); + return; + } + + if (mpd_isdynamic_data(result)) { + mpd_free(result->data); + } + result->data = rdata; + result->alloc = rsize; + mpd_set_dynamic_data(result); + + +finish: + mpd_set_flags(result, mpd_sign(a)^mpd_sign(b)); + result->exp = big->exp + small->exp; + result->len = _mpd_real_size(result->data, rsize); + /* resize to smaller cannot fail */ + mpd_qresize(result, result->len, status); + mpd_setdigits(result); +} + +/* Multiply a and b. */ +void +mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + _mpd_qmul(result, a, b, ctx, status); + mpd_qfinalize(result, ctx, status); +} + +/* Multiply a and b. Set NaN/Invalid_operation if the result is inexact. */ +static void +_mpd_qmul_exact(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + + mpd_qmul(result, a, b, ctx, &workstatus); + *status |= workstatus; + if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) { + mpd_seterror(result, MPD_Invalid_operation, status); + } +} + +/* Multiply decimal and mpd_ssize_t. */ +void +mpd_qmul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_ssize(&bb, b, &maxcontext, status); + mpd_qmul(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Multiply decimal and mpd_uint_t. */ +void +mpd_qmul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_uint(&bb, b, &maxcontext, status); + mpd_qmul(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +void +mpd_qmul_i32(mpd_t *result, const mpd_t *a, int32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qmul_ssize(result, a, b, ctx, status); +} + +void +mpd_qmul_u32(mpd_t *result, const mpd_t *a, uint32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qmul_uint(result, a, b, ctx, status); +} + +#ifdef CONFIG_64 +void +mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qmul_ssize(result, a, b, ctx, status); +} + +void +mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qmul_uint(result, a, b, ctx, status); +} +#elif !defined(LEGACY_COMPILER) +/* Multiply decimal and int64_t. */ +void +mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qset_i64(&bb, b, &maxcontext, status); + mpd_qmul(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Multiply decimal and uint64_t. */ +void +mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qset_u64(&bb, b, &maxcontext, status); + mpd_qmul(result, a, &bb, ctx, status); + mpd_del(&bb); +} +#endif + +/* Like the minus operator. */ +void +mpd_qminus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + } + + if (mpd_iszero(a) && ctx->round != MPD_ROUND_FLOOR) { + mpd_qcopy_abs(result, a, status); + } + else { + mpd_qcopy_negate(result, a, status); + } + + mpd_qfinalize(result, ctx, status); +} + +/* Like the plus operator. */ +void +mpd_qplus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + } + + if (mpd_iszero(a) && ctx->round != MPD_ROUND_FLOOR) { + mpd_qcopy_abs(result, a, status); + } + else { + mpd_qcopy(result, a, status); + } + + mpd_qfinalize(result, ctx, status); +} + +/* The largest representable number that is smaller than the operand. */ +void +mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t workctx; + MPD_NEW_CONST(tiny,MPD_POS,mpd_etiny(ctx)-1,1,1,1,1); + + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + + assert(mpd_isinfinite(a)); + if (mpd_isnegative(a)) { + mpd_qcopy(result, a, status); + return; + } + else { + mpd_clear_flags(result); + mpd_qmaxcoeff(result, ctx, status); + if (mpd_isnan(result)) { + return; + } + result->exp = mpd_etop(ctx); + return; + } + } + + mpd_workcontext(&workctx, ctx); + workctx.round = MPD_ROUND_FLOOR; + + if (!mpd_qcopy(result, a, status)) { + return; + } + + mpd_qfinalize(result, &workctx, &workctx.status); + if (workctx.status&(MPD_Inexact|MPD_Errors)) { + *status |= (workctx.status&MPD_Errors); + return; + } + + workctx.status = 0; + mpd_qsub(result, a, &tiny, &workctx, &workctx.status); + *status |= (workctx.status&MPD_Errors); +} + +/* The smallest representable number that is larger than the operand. */ +void +mpd_qnext_plus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t workctx; + MPD_NEW_CONST(tiny,MPD_POS,mpd_etiny(ctx)-1,1,1,1,1); + + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + + assert(mpd_isinfinite(a)); + if (mpd_ispositive(a)) { + mpd_qcopy(result, a, status); + } + else { + mpd_clear_flags(result); + mpd_qmaxcoeff(result, ctx, status); + if (mpd_isnan(result)) { + return; + } + mpd_set_flags(result, MPD_NEG); + result->exp = mpd_etop(ctx); + } + return; + } + + mpd_workcontext(&workctx, ctx); + workctx.round = MPD_ROUND_CEILING; + + if (!mpd_qcopy(result, a, status)) { + return; + } + + mpd_qfinalize(result, &workctx, &workctx.status); + if (workctx.status & (MPD_Inexact|MPD_Errors)) { + *status |= (workctx.status&MPD_Errors); + return; + } + + workctx.status = 0; + mpd_qadd(result, a, &tiny, &workctx, &workctx.status); + *status |= (workctx.status&MPD_Errors); +} + +/* + * The number closest to the first operand that is in the direction towards + * the second operand. + */ +void +mpd_qnext_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + int c; + + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + + c = _mpd_cmp(a, b); + if (c == 0) { + mpd_qcopy_sign(result, a, b, status); + return; + } + + if (c < 0) { + mpd_qnext_plus(result, a, ctx, status); + } + else { + mpd_qnext_minus(result, a, ctx, status); + } + + if (mpd_isinfinite(result)) { + *status |= (MPD_Overflow|MPD_Rounded|MPD_Inexact); + } + else if (mpd_adjexp(result) < ctx->emin) { + *status |= (MPD_Underflow|MPD_Subnormal|MPD_Rounded|MPD_Inexact); + if (mpd_iszero(result)) { + *status |= MPD_Clamped; + } + } +} + +/* + * Internal function: Integer power with mpd_uint_t exponent. The function + * can fail with MPD_Malloc_error. + * + * The error is equal to the error incurred in k-1 multiplications. Assuming + * the upper bound for the relative error in each operation: + * + * abs(err) = 5 * 10**-prec + * result = x**k * (1 + err)**(k-1) + */ +static inline void +_mpd_qpow_uint(mpd_t *result, const mpd_t *base, mpd_uint_t exp, + uint8_t resultsign, const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + mpd_uint_t n; + + if (exp == 0) { + _settriple(result, resultsign, 1, 0); /* GCOV_NOT_REACHED */ + return; /* GCOV_NOT_REACHED */ + } + + if (!mpd_qcopy(result, base, status)) { + return; + } + + n = mpd_bits[mpd_bsr(exp)]; + while (n >>= 1) { + mpd_qmul(result, result, result, ctx, &workstatus); + if (exp & n) { + mpd_qmul(result, result, base, ctx, &workstatus); + } + if (mpd_isspecial(result) || + (mpd_iszerocoeff(result) && (workstatus & MPD_Clamped))) { + break; + } + } + + *status |= workstatus; + mpd_set_sign(result, resultsign); +} + +/* + * Internal function: Integer power with mpd_t exponent, tbase and texp + * are modified!! Function can fail with MPD_Malloc_error. + * + * The error is equal to the error incurred in k multiplications. Assuming + * the upper bound for the relative error in each operation: + * + * abs(err) = 5 * 10**-prec + * result = x**k * (1 + err)**k + */ +static inline void +_mpd_qpow_mpd(mpd_t *result, mpd_t *tbase, mpd_t *texp, uint8_t resultsign, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + mpd_context_t maxctx; + MPD_NEW_CONST(two,0,0,1,1,1,2); + + + mpd_maxcontext(&maxctx); + + /* resize to smaller cannot fail */ + mpd_qcopy(result, &one, status); + + while (!mpd_iszero(texp)) { + if (mpd_isodd(texp)) { + mpd_qmul(result, result, tbase, ctx, &workstatus); + *status |= workstatus; + if (mpd_isspecial(result) || + (mpd_iszerocoeff(result) && (workstatus & MPD_Clamped))) { + break; + } + } + mpd_qmul(tbase, tbase, tbase, ctx, &workstatus); + mpd_qdivint(texp, texp, &two, &maxctx, &workstatus); + if (mpd_isnan(tbase) || mpd_isnan(texp)) { + mpd_seterror(result, workstatus&MPD_Errors, status); + return; + } + } + mpd_set_sign(result, resultsign); +} + +/* + * The power function for integer exponents. Relative error _before_ the + * final rounding to prec: + * abs(result - base**exp) < 0.1 * 10**-prec * abs(base**exp) + */ +static void +_mpd_qpow_int(mpd_t *result, const mpd_t *base, const mpd_t *exp, + uint8_t resultsign, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t workctx; + MPD_NEW_STATIC(tbase,0,0,0,0); + MPD_NEW_STATIC(texp,0,0,0,0); + mpd_ssize_t n; + + + mpd_workcontext(&workctx, ctx); + workctx.prec += (exp->digits + exp->exp + 2); + workctx.round = MPD_ROUND_HALF_EVEN; + workctx.clamp = 0; + if (mpd_isnegative(exp)) { + workctx.prec += 1; + mpd_qdiv(&tbase, &one, base, &workctx, status); + if (*status&MPD_Errors) { + mpd_setspecial(result, MPD_POS, MPD_NAN); + goto finish; + } + } + else { + if (!mpd_qcopy(&tbase, base, status)) { + mpd_setspecial(result, MPD_POS, MPD_NAN); + goto finish; + } + } + + n = mpd_qabs_uint(exp, &workctx.status); + if (workctx.status&MPD_Invalid_operation) { + if (!mpd_qcopy(&texp, exp, status)) { + mpd_setspecial(result, MPD_POS, MPD_NAN); /* GCOV_UNLIKELY */ + goto finish; /* GCOV_UNLIKELY */ + } + _mpd_qpow_mpd(result, &tbase, &texp, resultsign, &workctx, status); + } + else { + _mpd_qpow_uint(result, &tbase, n, resultsign, &workctx, status); + } + + if (mpd_isinfinite(result)) { + /* for ROUND_DOWN, ROUND_FLOOR, etc. */ + _settriple(result, resultsign, 1, MPD_EXP_INF); + } + +finish: + mpd_del(&tbase); + mpd_del(&texp); + mpd_qfinalize(result, ctx, status); +} + +/* + * If the exponent is infinite and base equals one, the result is one + * with a coefficient of length prec. Otherwise, result is undefined. + * Return the value of the comparison against one. + */ +static int +_qcheck_pow_one_inf(mpd_t *result, const mpd_t *base, uint8_t resultsign, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_ssize_t shift; + int cmp; + + if ((cmp = _mpd_cmp(base, &one)) == 0) { + shift = ctx->prec-1; + mpd_qshiftl(result, &one, shift, status); + result->exp = -shift; + mpd_set_flags(result, resultsign); + *status |= (MPD_Inexact|MPD_Rounded); + } + + return cmp; +} + +/* + * If abs(base) equals one, calculate the correct power of one result. + * Otherwise, result is undefined. Return the value of the comparison + * against 1. + * + * This is an internal function that does not check for specials. + */ +static int +_qcheck_pow_one(mpd_t *result, const mpd_t *base, const mpd_t *exp, + uint8_t resultsign, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + mpd_ssize_t shift; + int cmp; + + if ((cmp = _mpd_cmp_abs(base, &one)) == 0) { + if (_mpd_isint(exp)) { + if (mpd_isnegative(exp)) { + _settriple(result, resultsign, 1, 0); + return 0; + } + /* 1.000**3 = 1.000000000 */ + mpd_qmul_ssize(result, exp, -base->exp, ctx, &workstatus); + if (workstatus&MPD_Errors) { + *status |= (workstatus&MPD_Errors); + return 0; + } + /* digits-1 after exponentiation */ + shift = mpd_qget_ssize(result, &workstatus); + /* shift is MPD_SSIZE_MAX if result is too large */ + if (shift > ctx->prec-1) { + shift = ctx->prec-1; + *status |= MPD_Rounded; + } + } + else if (mpd_ispositive(base)) { + shift = ctx->prec-1; + *status |= (MPD_Inexact|MPD_Rounded); + } + else { + return -2; /* GCOV_NOT_REACHED */ + } + if (!mpd_qshiftl(result, &one, shift, status)) { + return 0; + } + result->exp = -shift; + mpd_set_flags(result, resultsign); + } + + return cmp; +} + +/* + * Detect certain over/underflow of x**y. + * ACL2 proof: pow-bounds.lisp. + * + * Symbols: + * + * e: EXP_INF or EXP_CLAMP + * x: base + * y: exponent + * + * omega(e) = log10(abs(e)) + * zeta(x) = log10(abs(log10(x))) + * theta(y) = log10(abs(y)) + * + * Upper and lower bounds: + * + * ub_omega(e) = ceil(log10(abs(e))) + * lb_theta(y) = floor(log10(abs(y))) + * + * | floor(log10(floor(abs(log10(x))))) if x < 1/10 or x >= 10 + * lb_zeta(x) = | floor(log10(abs(x-1)/10)) if 1/10 <= x < 1 + * | floor(log10(abs((x-1)/100))) if 1 < x < 10 + * + * ub_omega(e) and lb_theta(y) are obviously upper and lower bounds + * for omega(e) and theta(y). + * + * lb_zeta is a lower bound for zeta(x): + * + * x < 1/10 or x >= 10: + * + * abs(log10(x)) >= 1, so the outer log10 is well defined. Since log10 + * is strictly increasing, the end result is a lower bound. + * + * 1/10 <= x < 1: + * + * We use: log10(x) <= (x-1)/log(10) + * abs(log10(x)) >= abs(x-1)/log(10) + * abs(log10(x)) >= abs(x-1)/10 + * + * 1 < x < 10: + * + * We use: (x-1)/(x*log(10)) < log10(x) + * abs((x-1)/100) < abs(log10(x)) + * + * XXX: abs((x-1)/10) would work, need ACL2 proof. + * + * + * Let (0 < x < 1 and y < 0) or (x > 1 and y > 0). (H1) + * Let ub_omega(exp_inf) < lb_zeta(x) + lb_theta(y) (H2) + * + * Then: + * log10(abs(exp_inf)) < log10(abs(log10(x))) + log10(abs(y)). (1) + * exp_inf < log10(x) * y (2) + * 10**exp_inf < x**y (3) + * + * Let (0 < x < 1 and y > 0) or (x > 1 and y < 0). (H3) + * Let ub_omega(exp_clamp) < lb_zeta(x) + lb_theta(y) (H4) + * + * Then: + * log10(abs(exp_clamp)) < log10(abs(log10(x))) + log10(abs(y)). (4) + * log10(x) * y < exp_clamp (5) + * x**y < 10**exp_clamp (6) + * + */ +static mpd_ssize_t +_lower_bound_zeta(const mpd_t *x, uint32_t *status) +{ + mpd_context_t maxctx; + MPD_NEW_STATIC(scratch,0,0,0,0); + mpd_ssize_t t, u; + + t = mpd_adjexp(x); + if (t > 0) { + /* x >= 10 -> floor(log10(floor(abs(log10(x))))) */ + return mpd_exp_digits(t) - 1; + } + else if (t < -1) { + /* x < 1/10 -> floor(log10(floor(abs(log10(x))))) */ + return mpd_exp_digits(t+1) - 1; + } + else { + mpd_maxcontext(&maxctx); + mpd_qsub(&scratch, x, &one, &maxctx, status); + if (mpd_isspecial(&scratch)) { + mpd_del(&scratch); + return MPD_SSIZE_MAX; + } + u = mpd_adjexp(&scratch); + mpd_del(&scratch); + + /* t == -1, 1/10 <= x < 1 -> floor(log10(abs(x-1)/10)) + * t == 0, 1 < x < 10 -> floor(log10(abs(x-1)/100)) */ + return (t == 0) ? u-2 : u-1; + } +} + +/* + * Detect cases of certain overflow/underflow in the power function. + * Assumptions: x != 1, y != 0. The proof above is for positive x. + * If x is negative and y is an odd integer, x**y == -(abs(x)**y), + * so the analysis does not change. + */ +static int +_qcheck_pow_bounds(mpd_t *result, const mpd_t *x, const mpd_t *y, + uint8_t resultsign, + const mpd_context_t *ctx, uint32_t *status) +{ + MPD_NEW_SHARED(abs_x, x); + mpd_ssize_t ub_omega, lb_zeta, lb_theta; + uint8_t sign; + + mpd_set_positive(&abs_x); + + lb_theta = mpd_adjexp(y); + lb_zeta = _lower_bound_zeta(&abs_x, status); + if (lb_zeta == MPD_SSIZE_MAX) { + mpd_seterror(result, MPD_Malloc_error, status); + return 1; + } + + sign = (mpd_adjexp(&abs_x) < 0) ^ mpd_sign(y); + if (sign == 0) { + /* (0 < |x| < 1 and y < 0) or (|x| > 1 and y > 0) */ + ub_omega = mpd_exp_digits(ctx->emax); + if (ub_omega < lb_zeta + lb_theta) { + _settriple(result, resultsign, 1, MPD_EXP_INF); + mpd_qfinalize(result, ctx, status); + return 1; + } + } + else { + /* (0 < |x| < 1 and y > 0) or (|x| > 1 and y < 0). */ + ub_omega = mpd_exp_digits(mpd_etiny(ctx)); + if (ub_omega < lb_zeta + lb_theta) { + _settriple(result, resultsign, 1, mpd_etiny(ctx)-1); + mpd_qfinalize(result, ctx, status); + return 1; + } + } + + return 0; +} + +/* + * TODO: Implement algorithm for computing exact powers from decimal.py. + * In order to prevent infinite loops, this has to be called before + * using Ziv's strategy for correct rounding. + */ +/* +static int +_mpd_qpow_exact(mpd_t *result, const mpd_t *base, const mpd_t *exp, + const mpd_context_t *ctx, uint32_t *status) +{ + return 0; +} +*/ + +/* + * The power function for real exponents. + * Relative error: abs(result - e**y) < e**y * 1/5 * 10**(-prec - 1) + */ +static void +_mpd_qpow_real(mpd_t *result, const mpd_t *base, const mpd_t *exp, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t workctx; + MPD_NEW_STATIC(texp,0,0,0,0); + + if (!mpd_qcopy(&texp, exp, status)) { + mpd_seterror(result, MPD_Malloc_error, status); + return; + } + + mpd_maxcontext(&workctx); + workctx.prec = (base->digits > ctx->prec) ? base->digits : ctx->prec; + workctx.prec += (4 + MPD_EXPDIGITS); + workctx.round = MPD_ROUND_HALF_EVEN; + workctx.allcr = ctx->allcr; + + /* + * extra := MPD_EXPDIGITS = MPD_EXP_MAX_T + * wp := prec + 4 + extra + * abs(err) < 5 * 10**-wp + * y := log(base) * exp + * Calculate: + * 1) e**(y * (1 + err)**2) * (1 + err) + * = e**y * e**(y * (2*err + err**2)) * (1 + err) + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Relative error of the underlined term: + * 2) abs(e**(y * (2*err + err**2)) - 1) + * Case abs(y) >= 10**extra: + * 3) adjexp(y)+1 > log10(abs(y)) >= extra + * This triggers the Overflow/Underflow shortcut in _mpd_qexp(), + * so no further analysis is necessary. + * Case abs(y) < 10**extra: + * 4) abs(y * (2*err + err**2)) < 1/5 * 10**(-prec - 2) + * Use (see _mpd_qexp): + * 5) abs(x) <= 9/10 * 10**-p ==> abs(e**x - 1) < 10**-p + * With 2), 4) and 5): + * 6) abs(e**(y * (2*err + err**2)) - 1) < 10**(-prec - 2) + * The complete relative error of 1) is: + * 7) abs(result - e**y) < e**y * 1/5 * 10**(-prec - 1) + */ + mpd_qln(result, base, &workctx, &workctx.status); + mpd_qmul(result, result, &texp, &workctx, &workctx.status); + mpd_qexp(result, result, &workctx, status); + + mpd_del(&texp); + *status |= (workctx.status&MPD_Errors); + *status |= (MPD_Inexact|MPD_Rounded); +} + +/* The power function: base**exp */ +void +mpd_qpow(mpd_t *result, const mpd_t *base, const mpd_t *exp, + const mpd_context_t *ctx, uint32_t *status) +{ + uint8_t resultsign = 0; + int intexp = 0; + int cmp; + + if (mpd_isspecial(base) || mpd_isspecial(exp)) { + if (mpd_qcheck_nans(result, base, exp, ctx, status)) { + return; + } + } + if (mpd_isinteger(exp)) { + intexp = 1; + resultsign = mpd_isnegative(base) && mpd_isodd(exp); + } + + if (mpd_iszero(base)) { + if (mpd_iszero(exp)) { + mpd_seterror(result, MPD_Invalid_operation, status); + } + else if (mpd_isnegative(exp)) { + mpd_setspecial(result, resultsign, MPD_INF); + } + else { + _settriple(result, resultsign, 0, 0); + } + return; + } + if (mpd_isnegative(base)) { + if (!intexp || mpd_isinfinite(exp)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + } + if (mpd_isinfinite(exp)) { + /* power of one */ + cmp = _qcheck_pow_one_inf(result, base, resultsign, ctx, status); + if (cmp == 0) { + return; + } + else { + cmp *= mpd_arith_sign(exp); + if (cmp < 0) { + _settriple(result, resultsign, 0, 0); + } + else { + mpd_setspecial(result, resultsign, MPD_INF); + } + } + return; + } + if (mpd_isinfinite(base)) { + if (mpd_iszero(exp)) { + _settriple(result, resultsign, 1, 0); + } + else if (mpd_isnegative(exp)) { + _settriple(result, resultsign, 0, 0); + } + else { + mpd_setspecial(result, resultsign, MPD_INF); + } + return; + } + if (mpd_iszero(exp)) { + _settriple(result, resultsign, 1, 0); + return; + } + if (_qcheck_pow_one(result, base, exp, resultsign, ctx, status) == 0) { + return; + } + if (_qcheck_pow_bounds(result, base, exp, resultsign, ctx, status)) { + return; + } + + if (intexp) { + _mpd_qpow_int(result, base, exp, resultsign, ctx, status); + } + else { + _mpd_qpow_real(result, base, exp, ctx, status); + if (!mpd_isspecial(result) && _mpd_cmp(result, &one) == 0) { + mpd_ssize_t shift = ctx->prec-1; + mpd_qshiftl(result, &one, shift, status); + result->exp = -shift; + } + if (mpd_isinfinite(result)) { + /* for ROUND_DOWN, ROUND_FLOOR, etc. */ + _settriple(result, MPD_POS, 1, MPD_EXP_INF); + } + mpd_qfinalize(result, ctx, status); + } +} + +/* + * Internal function: Integer powmod with mpd_uint_t exponent, base is modified! + * Function can fail with MPD_Malloc_error. + */ +static inline void +_mpd_qpowmod_uint(mpd_t *result, mpd_t *base, mpd_uint_t exp, + const mpd_t *mod, uint32_t *status) +{ + mpd_context_t maxcontext; + + mpd_maxcontext(&maxcontext); + + /* resize to smaller cannot fail */ + mpd_qcopy(result, &one, status); + + while (exp > 0) { + if (exp & 1) { + _mpd_qmul_exact(result, result, base, &maxcontext, status); + mpd_qrem(result, result, mod, &maxcontext, status); + } + _mpd_qmul_exact(base, base, base, &maxcontext, status); + mpd_qrem(base, base, mod, &maxcontext, status); + exp >>= 1; + } +} + +/* The powmod function: (base**exp) % mod */ +void +mpd_qpowmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, + const mpd_t *mod, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(tbase,0,0,0,0); + MPD_NEW_STATIC(texp,0,0,0,0); + MPD_NEW_STATIC(tmod,0,0,0,0); + MPD_NEW_STATIC(tmp,0,0,0,0); + MPD_NEW_CONST(two,0,0,1,1,1,2); + mpd_ssize_t tbase_exp, texp_exp; + mpd_ssize_t i; + mpd_t t; + mpd_uint_t r; + uint8_t sign; + + + if (mpd_isspecial(base) || mpd_isspecial(exp) || mpd_isspecial(mod)) { + if (mpd_qcheck_3nans(result, base, exp, mod, ctx, status)) { + return; + } + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + + if (!_mpd_isint(base) || !_mpd_isint(exp) || !_mpd_isint(mod)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (mpd_iszerocoeff(mod)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (mod->digits+mod->exp > ctx->prec) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + sign = (mpd_isnegative(base)) && (mpd_isodd(exp)); + if (mpd_iszerocoeff(exp)) { + if (mpd_iszerocoeff(base)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + r = (_mpd_cmp_abs(mod, &one)==0) ? 0 : 1; + _settriple(result, sign, r, 0); + return; + } + if (mpd_isnegative(exp)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (mpd_iszerocoeff(base)) { + _settriple(result, sign, 0, 0); + return; + } + + mpd_maxcontext(&maxcontext); + + mpd_qrescale(&tmod, mod, 0, &maxcontext, &maxcontext.status); + if (maxcontext.status&MPD_Errors) { + mpd_seterror(result, maxcontext.status&MPD_Errors, status); + goto out; + } + maxcontext.status = 0; + mpd_set_positive(&tmod); + + mpd_qround_to_int(&tbase, base, &maxcontext, status); + mpd_set_positive(&tbase); + tbase_exp = tbase.exp; + tbase.exp = 0; + + mpd_qround_to_int(&texp, exp, &maxcontext, status); + texp_exp = texp.exp; + texp.exp = 0; + + /* base = (base.int % modulo * pow(10, base.exp, modulo)) % modulo */ + mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status); + mpd_qshiftl(result, &one, tbase_exp, status); + mpd_qrem(result, result, &tmod, &maxcontext, status); + _mpd_qmul_exact(&tbase, &tbase, result, &maxcontext, status); + mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status); + if (mpd_isspecial(&tbase) || + mpd_isspecial(&texp) || + mpd_isspecial(&tmod)) { + goto mpd_errors; + } + + for (i = 0; i < texp_exp; i++) { + _mpd_qpowmod_uint(&tmp, &tbase, 10, &tmod, status); + t = tmp; + tmp = tbase; + tbase = t; + } + if (mpd_isspecial(&tbase)) { + goto mpd_errors; /* GCOV_UNLIKELY */ + } + + /* resize to smaller cannot fail */ + mpd_qcopy(result, &one, status); + while (mpd_isfinite(&texp) && !mpd_iszero(&texp)) { + if (mpd_isodd(&texp)) { + _mpd_qmul_exact(result, result, &tbase, &maxcontext, status); + mpd_qrem(result, result, &tmod, &maxcontext, status); + } + _mpd_qmul_exact(&tbase, &tbase, &tbase, &maxcontext, status); + mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status); + mpd_qdivint(&texp, &texp, &two, &maxcontext, status); + } + if (mpd_isspecial(&texp) || mpd_isspecial(&tbase) || + mpd_isspecial(&tmod) || mpd_isspecial(result)) { + /* MPD_Malloc_error */ + goto mpd_errors; + } + else { + mpd_set_sign(result, sign); + } + +out: + mpd_del(&tbase); + mpd_del(&texp); + mpd_del(&tmod); + mpd_del(&tmp); + return; + +mpd_errors: + mpd_setspecial(result, MPD_POS, MPD_NAN); + goto out; +} + +void +mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + mpd_ssize_t b_exp = b->exp; + mpd_ssize_t expdiff, shift; + mpd_uint_t rnd; + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + if (mpd_isinfinite(a) && mpd_isinfinite(b)) { + mpd_qcopy(result, a, status); + return; + } + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + if (b->exp > ctx->emax || b->exp < mpd_etiny(ctx)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + if (mpd_iszero(a)) { + _settriple(result, mpd_sign(a), 0, b->exp); + mpd_qfinalize(result, ctx, status); + return; + } + + + expdiff = a->exp - b->exp; + if (a->digits + expdiff > ctx->prec) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + if (expdiff >= 0) { + shift = expdiff; + if (!mpd_qshiftl(result, a, shift, status)) { + return; + } + result->exp = b_exp; + } + else { + /* At this point expdiff < 0 and a->digits+expdiff <= prec, + * so the shift before an increment will fit in prec. */ + shift = -expdiff; + rnd = mpd_qshiftr(result, a, shift, status); + if (rnd == MPD_UINT_MAX) { + return; + } + result->exp = b_exp; + if (!_mpd_apply_round_fit(result, rnd, ctx, status)) { + return; + } + workstatus |= MPD_Rounded; + if (rnd) { + workstatus |= MPD_Inexact; + } + } + + if (mpd_adjexp(result) > ctx->emax || + mpd_adjexp(result) < mpd_etiny(ctx)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + *status |= workstatus; + mpd_qfinalize(result, ctx, status); +} + +void +mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_ssize_t shift, maxexp, maxshift; + uint8_t sign_a = mpd_sign(a); + + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + mpd_qcopy(result, a, status); + return; + } + + if (!mpd_qcopy(result, a, status)) { + return; + } + mpd_qfinalize(result, ctx, status); + if (mpd_isspecial(result)) { + return; + } + if (mpd_iszero(result)) { + _settriple(result, sign_a, 0, 0); + return; + } + + shift = mpd_trail_zeros(result); + maxexp = (ctx->clamp) ? mpd_etop(ctx) : ctx->emax; + /* After the finalizing above result->exp <= maxexp. */ + maxshift = maxexp - result->exp; + shift = (shift > maxshift) ? maxshift : shift; + + mpd_qshiftr_inplace(result, shift); + result->exp += shift; +} + +void +mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, + uint32_t *status) +{ + MPD_NEW_STATIC(q,0,0,0,0); + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(r, a, b, ctx, status)) { + return; + } + if (mpd_isinfinite(a)) { + mpd_seterror(r, MPD_Invalid_operation, status); + return; + } + if (mpd_isinfinite(b)) { + mpd_qcopy(r, a, status); + mpd_qfinalize(r, ctx, status); + return; + } + /* debug */ + abort(); /* GCOV_NOT_REACHED */ + } + if (mpd_iszerocoeff(b)) { + if (mpd_iszerocoeff(a)) { + mpd_seterror(r, MPD_Division_undefined, status); + } + else { + mpd_seterror(r, MPD_Invalid_operation, status); + } + return; + } + + _mpd_qdivmod(&q, r, a, b, ctx, status); + mpd_del(&q); + mpd_qfinalize(r, ctx, status); +} + +void +mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t workctx; + MPD_NEW_STATIC(btmp,0,0,0,0); + MPD_NEW_STATIC(q,0,0,0,0); + mpd_ssize_t expdiff, qdigits; + int cmp, isodd, allnine; + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(r, a, b, ctx, status)) { + return; + } + if (mpd_isinfinite(a)) { + mpd_seterror(r, MPD_Invalid_operation, status); + return; + } + if (mpd_isinfinite(b)) { + mpd_qcopy(r, a, status); + mpd_qfinalize(r, ctx, status); + return; + } + /* debug */ + abort(); /* GCOV_NOT_REACHED */ + } + if (mpd_iszerocoeff(b)) { + if (mpd_iszerocoeff(a)) { + mpd_seterror(r, MPD_Division_undefined, status); + } + else { + mpd_seterror(r, MPD_Invalid_operation, status); + } + return; + } + + if (r == b) { + if (!mpd_qcopy(&btmp, b, status)) { + mpd_seterror(r, MPD_Malloc_error, status); + return; + } + b = &btmp; + } + + _mpd_qdivmod(&q, r, a, b, ctx, status); + if (mpd_isnan(&q) || mpd_isnan(r)) { + goto finish; + } + if (mpd_iszerocoeff(r)) { + goto finish; + } + + expdiff = mpd_adjexp(b) - mpd_adjexp(r); + if (-1 <= expdiff && expdiff <= 1) { + + allnine = mpd_coeff_isallnine(&q); + qdigits = q.digits; + isodd = mpd_isodd(&q); + + mpd_maxcontext(&workctx); + if (mpd_sign(a) == mpd_sign(b)) { + /* sign(r) == sign(b) */ + _mpd_qsub(&q, r, b, &workctx, &workctx.status); + } + else { + /* sign(r) != sign(b) */ + _mpd_qadd(&q, r, b, &workctx, &workctx.status); + } + + if (workctx.status&MPD_Errors) { + mpd_seterror(r, workctx.status&MPD_Errors, status); + goto finish; + } + + cmp = _mpd_cmp_abs(&q, r); + if (cmp < 0 || (cmp == 0 && isodd)) { + /* abs(r) > abs(b)/2 or abs(r) == abs(b)/2 and isodd(quotient) */ + if (allnine && qdigits == ctx->prec) { + /* abs(quotient) + 1 == 10**prec */ + mpd_seterror(r, MPD_Division_impossible, status); + goto finish; + } + mpd_qcopy(r, &q, status); + } + } + + +finish: + mpd_del(&btmp); + mpd_del(&q); + mpd_qfinalize(r, ctx, status); +} + +static void +_mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_ssize_t expdiff, shift; + mpd_uint_t rnd; + + if (mpd_isspecial(a)) { + mpd_qcopy(result, a, status); + return; + } + + if (mpd_iszero(a)) { + _settriple(result, mpd_sign(a), 0, exp); + return; + } + + expdiff = a->exp - exp; + if (expdiff >= 0) { + shift = expdiff; + if (a->digits + shift > MPD_MAX_PREC+1) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (!mpd_qshiftl(result, a, shift, status)) { + return; + } + result->exp = exp; + } + else { + shift = -expdiff; + rnd = mpd_qshiftr(result, a, shift, status); + if (rnd == MPD_UINT_MAX) { + return; + } + result->exp = exp; + _mpd_apply_round_excess(result, rnd, ctx, status); + *status |= MPD_Rounded; + if (rnd) { + *status |= MPD_Inexact; + } + } + + if (mpd_issubnormal(result, ctx)) { + *status |= MPD_Subnormal; + } +} + +/* + * Rescale a number so that it has exponent 'exp'. Does not regard context + * precision, emax, emin, but uses the rounding mode. Special numbers are + * quietly copied. Restrictions: + * + * MPD_MIN_ETINY <= exp <= MPD_MAX_EMAX+1 + * result->digits <= MPD_MAX_PREC+1 + */ +void +mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, + const mpd_context_t *ctx, uint32_t *status) +{ + if (exp > MPD_MAX_EMAX+1 || exp < MPD_MIN_ETINY) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + _mpd_qrescale(result, a, exp, ctx, status); +} + +/* + * Same as mpd_qrescale, but with relaxed restrictions. The result of this + * function should only be used for formatting a number and never as input + * for other operations. + * + * MPD_MIN_ETINY-MPD_MAX_PREC <= exp <= MPD_MAX_EMAX+1 + * result->digits <= MPD_MAX_PREC+1 + */ +void +mpd_qrescale_fmt(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, + const mpd_context_t *ctx, uint32_t *status) +{ + if (exp > MPD_MAX_EMAX+1 || exp < MPD_MIN_ETINY-MPD_MAX_PREC) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + _mpd_qrescale(result, a, exp, ctx, status); +} + +/* Round to an integer according to 'action' and ctx->round. */ +enum {TO_INT_EXACT, TO_INT_SILENT, TO_INT_TRUNC}; +static void +_mpd_qround_to_integral(int action, mpd_t *result, const mpd_t *a, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_uint_t rnd; + + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + mpd_qcopy(result, a, status); + return; + } + if (a->exp >= 0) { + mpd_qcopy(result, a, status); + return; + } + if (mpd_iszerocoeff(a)) { + _settriple(result, mpd_sign(a), 0, 0); + return; + } + + rnd = mpd_qshiftr(result, a, -a->exp, status); + if (rnd == MPD_UINT_MAX) { + return; + } + result->exp = 0; + + if (action == TO_INT_EXACT || action == TO_INT_SILENT) { + _mpd_apply_round_excess(result, rnd, ctx, status); + if (action == TO_INT_EXACT) { + *status |= MPD_Rounded; + if (rnd) { + *status |= MPD_Inexact; + } + } + } +} + +void +mpd_qround_to_intx(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + (void)_mpd_qround_to_integral(TO_INT_EXACT, result, a, ctx, status); +} + +void +mpd_qround_to_int(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + (void)_mpd_qround_to_integral(TO_INT_SILENT, result, a, ctx, status); +} + +void +mpd_qtrunc(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + (void)_mpd_qround_to_integral(TO_INT_TRUNC, result, a, ctx, status); +} + +void +mpd_qfloor(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t workctx = *ctx; + workctx.round = MPD_ROUND_FLOOR; + (void)_mpd_qround_to_integral(TO_INT_SILENT, result, a, + &workctx, status); +} + +void +mpd_qceil(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t workctx = *ctx; + workctx.round = MPD_ROUND_CEILING; + (void)_mpd_qround_to_integral(TO_INT_SILENT, result, a, + &workctx, status); +} + +int +mpd_same_quantum(const mpd_t *a, const mpd_t *b) +{ + if (mpd_isspecial(a) || mpd_isspecial(b)) { + return ((mpd_isnan(a) && mpd_isnan(b)) || + (mpd_isinfinite(a) && mpd_isinfinite(b))); + } + + return a->exp == b->exp; +} + +/* Schedule the increase in precision for the Newton iteration. */ +static inline int +recpr_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2], + mpd_ssize_t maxprec, mpd_ssize_t initprec) +{ + mpd_ssize_t k; + int i; + + assert(maxprec > 0 && initprec > 0); + if (maxprec <= initprec) return -1; + + i = 0; k = maxprec; + do { + k = (k+1) / 2; + klist[i++] = k; + } while (k > initprec); + + return i-1; +} + +/* + * Initial approximation for the reciprocal: + * k_0 := MPD_RDIGITS-2 + * z_0 := 10**(-k_0) * floor(10**(2*k_0 + 2) / floor(v * 10**(k_0 + 2))) + * Absolute error: + * |1/v - z_0| < 10**(-k_0) + * ACL2 proof: maxerror-inverse-approx + */ +static void +_mpd_qreciprocal_approx(mpd_t *z, const mpd_t *v, uint32_t *status) +{ + mpd_uint_t p10data[2] = {0, mpd_pow10[MPD_RDIGITS-2]}; + mpd_uint_t dummy, word; + int n; + + assert(v->exp == -v->digits); + + _mpd_get_msdigits(&dummy, &word, v, MPD_RDIGITS); + n = mpd_word_digits(word); + word *= mpd_pow10[MPD_RDIGITS-n]; + + mpd_qresize(z, 2, status); + (void)_mpd_shortdiv(z->data, p10data, 2, word); + + mpd_clear_flags(z); + z->exp = -(MPD_RDIGITS-2); + z->len = (z->data[1] == 0) ? 1 : 2; + mpd_setdigits(z); +} + +/* + * Reciprocal, calculated with Newton's Method. Assumption: result != a. + * NOTE: The comments in the function show that certain operations are + * exact. The proof for the maximum error is too long to fit in here. + * ACL2 proof: maxerror-inverse-complete + */ +static void +_mpd_qreciprocal(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t varcontext, maxcontext; + mpd_t *z = result; /* current approximation */ + mpd_t *v; /* a, normalized to a number between 0.1 and 1 */ + MPD_NEW_SHARED(vtmp, a); /* v shares data with a */ + MPD_NEW_STATIC(s,0,0,0,0); /* temporary variable */ + MPD_NEW_STATIC(t,0,0,0,0); /* temporary variable */ + MPD_NEW_CONST(two,0,0,1,1,1,2); /* const 2 */ + mpd_ssize_t klist[MPD_MAX_PREC_LOG2]; + mpd_ssize_t adj, maxprec, initprec; + uint8_t sign = mpd_sign(a); + int i; + + assert(result != a); + + v = &vtmp; + mpd_clear_flags(v); + adj = v->digits + v->exp; + v->exp = -v->digits; + + /* Initial approximation */ + _mpd_qreciprocal_approx(z, v, status); + + mpd_maxcontext(&varcontext); + mpd_maxcontext(&maxcontext); + varcontext.round = maxcontext.round = MPD_ROUND_TRUNC; + varcontext.emax = maxcontext.emax = MPD_MAX_EMAX + 100; + varcontext.emin = maxcontext.emin = MPD_MIN_EMIN - 100; + maxcontext.prec = MPD_MAX_PREC + 100; + + maxprec = ctx->prec; + maxprec += 2; + initprec = MPD_RDIGITS-3; + + i = recpr_schedule_prec(klist, maxprec, initprec); + for (; i >= 0; i--) { + /* Loop invariant: z->digits <= klist[i]+7 */ + /* Let s := z**2, exact result */ + _mpd_qmul_exact(&s, z, z, &maxcontext, status); + varcontext.prec = 2*klist[i] + 5; + if (v->digits > varcontext.prec) { + /* Let t := v, truncated to n >= 2*k+5 fraction digits */ + mpd_qshiftr(&t, v, v->digits-varcontext.prec, status); + t.exp = -varcontext.prec; + /* Let t := trunc(v)*s, truncated to n >= 2*k+1 fraction digits */ + mpd_qmul(&t, &t, &s, &varcontext, status); + } + else { /* v->digits <= 2*k+5 */ + /* Let t := v*s, truncated to n >= 2*k+1 fraction digits */ + mpd_qmul(&t, v, &s, &varcontext, status); + } + /* Let s := 2*z, exact result */ + _mpd_qmul_exact(&s, z, &two, &maxcontext, status); + /* s.digits < t.digits <= 2*k+5, |adjexp(s)-adjexp(t)| <= 1, + * so the subtraction generates at most 2*k+6 <= klist[i+1]+7 + * digits. The loop invariant is preserved. */ + _mpd_qsub_exact(z, &s, &t, &maxcontext, status); + } + + if (!mpd_isspecial(z)) { + z->exp -= adj; + mpd_set_flags(z, sign); + } + + mpd_del(&s); + mpd_del(&t); + mpd_qfinalize(z, ctx, status); +} + +/* + * Internal function for large numbers: + * + * q, r = divmod(coeff(a), coeff(b)) + * + * Strategy: Multiply the dividend by the reciprocal of the divisor. The + * inexact result is fixed by a small loop, using at most one iteration. + * + * ACL2 proofs: + * ------------ + * 1) q is a natural number. (ndivmod-quotient-natp) + * 2) r is a natural number. (ndivmod-remainder-natp) + * 3) a = q * b + r (ndivmod-q*b+r==a) + * 4) r < b (ndivmod-remainder-<-b) + */ +static void +_mpd_base_ndivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, + uint32_t *status) +{ + mpd_context_t workctx; + mpd_t *qq = q, *rr = r; + mpd_t aa, bb; + int k; + + _mpd_copy_shared(&aa, a); + _mpd_copy_shared(&bb, b); + + mpd_set_positive(&aa); + mpd_set_positive(&bb); + aa.exp = 0; + bb.exp = 0; + + if (q == a || q == b) { + if ((qq = mpd_qnew()) == NULL) { + *status |= MPD_Malloc_error; + goto nanresult; + } + } + if (r == a || r == b) { + if ((rr = mpd_qnew()) == NULL) { + *status |= MPD_Malloc_error; + goto nanresult; + } + } + + mpd_maxcontext(&workctx); + + /* Let prec := adigits - bdigits + 4 */ + workctx.prec = a->digits - b->digits + 1 + 3; + if (a->digits > MPD_MAX_PREC || workctx.prec > MPD_MAX_PREC) { + *status |= MPD_Division_impossible; + goto nanresult; + } + + /* Let x := _mpd_qreciprocal(b, prec) + * Then x is bounded by: + * 1) 1/b - 10**(-prec - bdigits) < x < 1/b + 10**(-prec - bdigits) + * 2) 1/b - 10**(-adigits - 4) < x < 1/b + 10**(-adigits - 4) + */ + _mpd_qreciprocal(rr, &bb, &workctx, &workctx.status); + + /* Get an estimate for the quotient. Let q := a * x + * Then q is bounded by: + * 3) a/b - 10**-4 < q < a/b + 10**-4 + */ + _mpd_qmul(qq, &aa, rr, &workctx, &workctx.status); + /* Truncate q to an integer: + * 4) a/b - 2 < trunc(q) < a/b + 1 + */ + mpd_qtrunc(qq, qq, &workctx, &workctx.status); + + workctx.prec = aa.digits + 3; + workctx.emax = MPD_MAX_EMAX + 3; + workctx.emin = MPD_MIN_EMIN - 3; + /* Multiply the estimate for q by b: + * 5) a - 2 * b < trunc(q) * b < a + b + */ + _mpd_qmul(rr, &bb, qq, &workctx, &workctx.status); + /* Get the estimate for r such that a = q * b + r. */ + _mpd_qsub_exact(rr, &aa, rr, &workctx, &workctx.status); + + /* Fix the result. At this point -b < r < 2*b, so the correction loop + takes at most one iteration. */ + for (k = 0;; k++) { + if (mpd_isspecial(qq) || mpd_isspecial(rr)) { + *status |= (workctx.status&MPD_Errors); + goto nanresult; + } + if (k > 2) { /* Allow two iterations despite the proof. */ + mpd_err_warn("libmpdec: internal error in " /* GCOV_NOT_REACHED */ + "_mpd_base_ndivmod: please report"); /* GCOV_NOT_REACHED */ + *status |= MPD_Invalid_operation; /* GCOV_NOT_REACHED */ + goto nanresult; /* GCOV_NOT_REACHED */ + } + /* r < 0 */ + else if (_mpd_cmp(&zero, rr) == 1) { + _mpd_qadd_exact(rr, rr, &bb, &workctx, &workctx.status); + _mpd_qadd_exact(qq, qq, &minus_one, &workctx, &workctx.status); + } + /* 0 <= r < b */ + else if (_mpd_cmp(rr, &bb) == -1) { + break; + } + /* r >= b */ + else { + _mpd_qsub_exact(rr, rr, &bb, &workctx, &workctx.status); + _mpd_qadd_exact(qq, qq, &one, &workctx, &workctx.status); + } + } + + if (qq != q) { + if (!mpd_qcopy(q, qq, status)) { + goto nanresult; /* GCOV_UNLIKELY */ + } + mpd_del(qq); + } + if (rr != r) { + if (!mpd_qcopy(r, rr, status)) { + goto nanresult; /* GCOV_UNLIKELY */ + } + mpd_del(rr); + } + + *status |= (workctx.status&MPD_Errors); + return; + + +nanresult: + if (qq && qq != q) mpd_del(qq); + if (rr && rr != r) mpd_del(rr); + mpd_setspecial(q, MPD_POS, MPD_NAN); + mpd_setspecial(r, MPD_POS, MPD_NAN); +} + +/* LIBMPDEC_ONLY */ +/* + * Schedule the optimal precision increase for the Newton iteration. + * v := input operand + * z_0 := initial approximation + * initprec := natural number such that abs(sqrt(v) - z_0) < 10**-initprec + * maxprec := target precision + * + * For convenience the output klist contains the elements in reverse order: + * klist := [k_n-1, ..., k_0], where + * 1) k_0 <= initprec and + * 2) abs(sqrt(v) - result) < 10**(-2*k_n-1 + 2) <= 10**-maxprec. + */ +static inline int +invroot_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2], + mpd_ssize_t maxprec, mpd_ssize_t initprec) +{ + mpd_ssize_t k; + int i; + + assert(maxprec >= 3 && initprec >= 3); + if (maxprec <= initprec) return -1; + + i = 0; k = maxprec; + do { + k = (k+3) / 2; + klist[i++] = k; + } while (k > initprec); + + return i-1; +} + +/* + * Initial approximation for the inverse square root function. + * Input: + * v := rational number, with 1 <= v < 100 + * vhat := floor(v * 10**6) + * Output: + * z := approximation to 1/sqrt(v), such that abs(z - 1/sqrt(v)) < 10**-3. + */ +static inline void +_invroot_init_approx(mpd_t *z, mpd_uint_t vhat) +{ + mpd_uint_t lo = 1000; + mpd_uint_t hi = 10000; + mpd_uint_t a, sq; + + assert(lo*lo <= vhat && vhat < (hi+1)*(hi+1)); + + for(;;) { + a = (lo + hi) / 2; + sq = a * a; + if (vhat >= sq) { + if (vhat < sq + 2*a + 1) { + break; + } + lo = a + 1; + } + else { + hi = a - 1; + } + } + + /* + * After the binary search we have: + * 1) a**2 <= floor(v * 10**6) < (a + 1)**2 + * This implies: + * 2) a**2 <= v * 10**6 < (a + 1)**2 + * 3) a <= sqrt(v) * 10**3 < a + 1 + * Since 10**3 <= a: + * 4) 0 <= 10**prec/a - 1/sqrt(v) < 10**-prec + * We have: + * 5) 10**3/a - 10**-3 < floor(10**9/a) * 10**-6 <= 10**3/a + * Merging 4) and 5): + * 6) abs(floor(10**9/a) * 10**-6 - 1/sqrt(v)) < 10**-3 + */ + mpd_minalloc(z); + mpd_clear_flags(z); + z->data[0] = 1000000000UL / a; + z->len = 1; + z->exp = -6; + mpd_setdigits(z); +} + +/* + * Set 'result' to 1/sqrt(a). + * Relative error: abs(result - 1/sqrt(a)) < 10**-prec * 1/sqrt(a) + */ +static void +_mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + uint32_t workstatus = 0; + mpd_context_t varcontext, maxcontext; + mpd_t *z = result; /* current approximation */ + mpd_t *v; /* a, normalized to a number between 1 and 100 */ + MPD_NEW_SHARED(vtmp, a); /* by default v will share data with a */ + MPD_NEW_STATIC(s,0,0,0,0); /* temporary variable */ + MPD_NEW_STATIC(t,0,0,0,0); /* temporary variable */ + MPD_NEW_CONST(one_half,0,-1,1,1,1,5); + MPD_NEW_CONST(three,0,0,1,1,1,3); + mpd_ssize_t klist[MPD_MAX_PREC_LOG2]; + mpd_ssize_t ideal_exp, shift; + mpd_ssize_t adj, tz; + mpd_ssize_t maxprec, fracdigits; + mpd_uint_t vhat, dummy; + int i, n; + + + ideal_exp = -(a->exp - (a->exp & 1)) / 2; + + v = &vtmp; + if (result == a) { + if ((v = mpd_qncopy(a)) == NULL) { + mpd_seterror(result, MPD_Malloc_error, status); + return; + } + } + + /* normalize a to 1 <= v < 100 */ + if ((v->digits+v->exp) & 1) { + fracdigits = v->digits - 1; + v->exp = -fracdigits; + n = (v->digits > 7) ? 7 : (int)v->digits; + /* Let vhat := floor(v * 10**(2*initprec)) */ + _mpd_get_msdigits(&dummy, &vhat, v, n); + if (n < 7) { + vhat *= mpd_pow10[7-n]; + } + } + else { + fracdigits = v->digits - 2; + v->exp = -fracdigits; + n = (v->digits > 8) ? 8 : (int)v->digits; + /* Let vhat := floor(v * 10**(2*initprec)) */ + _mpd_get_msdigits(&dummy, &vhat, v, n); + if (n < 8) { + vhat *= mpd_pow10[8-n]; + } + } + adj = (a->exp-v->exp) / 2; + + /* initial approximation */ + _invroot_init_approx(z, vhat); + + mpd_maxcontext(&maxcontext); + mpd_maxcontext(&varcontext); + varcontext.round = MPD_ROUND_TRUNC; + maxprec = ctx->prec + 1; + + /* initprec == 3 */ + i = invroot_schedule_prec(klist, maxprec, 3); + for (; i >= 0; i--) { + varcontext.prec = 2*klist[i]+2; + mpd_qmul(&s, z, z, &maxcontext, &workstatus); + if (v->digits > varcontext.prec) { + shift = v->digits - varcontext.prec; + mpd_qshiftr(&t, v, shift, &workstatus); + t.exp += shift; + mpd_qmul(&t, &t, &s, &varcontext, &workstatus); + } + else { + mpd_qmul(&t, v, &s, &varcontext, &workstatus); + } + mpd_qsub(&t, &three, &t, &maxcontext, &workstatus); + mpd_qmul(z, z, &t, &varcontext, &workstatus); + mpd_qmul(z, z, &one_half, &maxcontext, &workstatus); + } + + z->exp -= adj; + + tz = mpd_trail_zeros(result); + shift = ideal_exp - result->exp; + shift = (tz > shift) ? shift : tz; + if (shift > 0) { + mpd_qshiftr_inplace(result, shift); + result->exp += shift; + } + + + mpd_del(&s); + mpd_del(&t); + if (v != &vtmp) mpd_del(v); + *status |= (workstatus&MPD_Errors); + *status |= (MPD_Rounded|MPD_Inexact); +} + +void +mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t workctx; + + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + if (mpd_isnegative(a)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + /* positive infinity */ + _settriple(result, MPD_POS, 0, mpd_etiny(ctx)); + *status |= MPD_Clamped; + return; + } + if (mpd_iszero(a)) { + mpd_setspecial(result, mpd_sign(a), MPD_INF); + *status |= MPD_Division_by_zero; + return; + } + if (mpd_isnegative(a)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + workctx = *ctx; + workctx.prec += 2; + workctx.round = MPD_ROUND_HALF_EVEN; + _mpd_qinvroot(result, a, &workctx, status); + mpd_qfinalize(result, ctx, status); +} +/* END LIBMPDEC_ONLY */ + +/* Algorithm from decimal.py */ +void +mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(c,0,0,0,0); + MPD_NEW_STATIC(q,0,0,0,0); + MPD_NEW_STATIC(r,0,0,0,0); + MPD_NEW_CONST(two,0,0,1,1,1,2); + mpd_ssize_t prec, ideal_exp; + mpd_ssize_t l, shift; + int exact = 0; + + + ideal_exp = (a->exp - (a->exp & 1)) / 2; + + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + if (mpd_isnegative(a)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + mpd_setspecial(result, MPD_POS, MPD_INF); + return; + } + if (mpd_iszero(a)) { + _settriple(result, mpd_sign(a), 0, ideal_exp); + mpd_qfinalize(result, ctx, status); + return; + } + if (mpd_isnegative(a)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + mpd_maxcontext(&maxcontext); + prec = ctx->prec + 1; + + if (!mpd_qcopy(&c, a, status)) { + goto malloc_error; + } + c.exp = 0; + + if (a->exp & 1) { + if (!mpd_qshiftl(&c, &c, 1, status)) { + goto malloc_error; + } + l = (a->digits >> 1) + 1; + } + else { + l = (a->digits + 1) >> 1; + } + + shift = prec - l; + if (shift >= 0) { + if (!mpd_qshiftl(&c, &c, 2*shift, status)) { + goto malloc_error; + } + exact = 1; + } + else { + exact = !mpd_qshiftr_inplace(&c, -2*shift); + } + + ideal_exp -= shift; + + /* find result = floor(sqrt(c)) using Newton's method */ + if (!mpd_qshiftl(result, &one, prec, status)) { + goto malloc_error; + } + + while (1) { + _mpd_qdivmod(&q, &r, &c, result, &maxcontext, &maxcontext.status); + if (mpd_isspecial(result) || mpd_isspecial(&q)) { + mpd_seterror(result, maxcontext.status&MPD_Errors, status); + goto out; + } + if (_mpd_cmp(result, &q) <= 0) { + break; + } + _mpd_qadd_exact(result, result, &q, &maxcontext, &maxcontext.status); + if (mpd_isspecial(result)) { + mpd_seterror(result, maxcontext.status&MPD_Errors, status); + goto out; + } + _mpd_qdivmod(result, &r, result, &two, &maxcontext, &maxcontext.status); + } + + if (exact) { + _mpd_qmul_exact(&r, result, result, &maxcontext, &maxcontext.status); + if (mpd_isspecial(&r)) { + mpd_seterror(result, maxcontext.status&MPD_Errors, status); + goto out; + } + exact = (_mpd_cmp(&r, &c) == 0); + } + + if (exact) { + if (shift >= 0) { + mpd_qshiftr_inplace(result, shift); + } + else { + if (!mpd_qshiftl(result, result, -shift, status)) { + goto malloc_error; + } + } + ideal_exp += shift; + } + else { + int lsd = (int)mpd_lsd(result->data[0]); + if (lsd == 0 || lsd == 5) { + result->data[0] += 1; + } + } + + result->exp = ideal_exp; + + +out: + mpd_del(&c); + mpd_del(&q); + mpd_del(&r); + maxcontext = *ctx; + maxcontext.round = MPD_ROUND_HALF_EVEN; + mpd_qfinalize(result, &maxcontext, status); + return; + +malloc_error: + mpd_seterror(result, MPD_Malloc_error, status); + goto out; +} + + +/******************************************************************************/ +/* Base conversions */ +/******************************************************************************/ + +/* Space needed to represent an integer mpd_t in base 'base'. */ +size_t +mpd_sizeinbase(const mpd_t *a, uint32_t base) +{ + double x; + size_t digits; + + assert(mpd_isinteger(a)); + assert(base >= 2); + + if (mpd_iszero(a)) { + return 1; + } + + digits = a->digits+a->exp; + assert(digits > 0); + +#ifdef CONFIG_64 + /* ceil(2711437152599294 / log10(2)) + 4 == 2**53 */ + if (digits > 2711437152599294ULL) { + return SIZE_MAX; + } +#endif + + x = (double)digits / log10(base); + return (x > SIZE_MAX-1) ? SIZE_MAX : (size_t)x + 1; +} + +/* Space needed to import a base 'base' integer of length 'srclen'. */ +static mpd_ssize_t +_mpd_importsize(size_t srclen, uint32_t base) +{ + double x; + + assert(srclen > 0); + assert(base >= 2); + +#if SIZE_MAX == UINT64_MAX + if (srclen > (1ULL<<53)) { + return MPD_SSIZE_MAX; + } +#endif + + x = (double)srclen * (log10(base)/MPD_RDIGITS); + return (x >= MPD_MAXIMPORT) ? MPD_SSIZE_MAX : (mpd_ssize_t)x + 1; +} + +static uint8_t +mpd_resize_u16(uint16_t **w, size_t nmemb) +{ + uint8_t err = 0; + *w = mpd_realloc(*w, nmemb, sizeof **w, &err); + return !err; +} + +static uint8_t +mpd_resize_u32(uint32_t **w, size_t nmemb) +{ + uint8_t err = 0; + *w = mpd_realloc(*w, nmemb, sizeof **w, &err); + return !err; +} + +static size_t +_baseconv_to_u16(uint16_t **w, size_t wlen, mpd_uint_t wbase, + mpd_uint_t *u, mpd_ssize_t ulen) +{ + size_t n = 0; + + assert(wlen > 0 && ulen > 0); + assert(wbase <= (1U<<16)); + + do { + if (n >= wlen) { + if (!mpd_resize_u16(w, n+1)) { + return SIZE_MAX; + } + wlen = n+1; + } + (*w)[n++] = (uint16_t)_mpd_shortdiv(u, u, ulen, wbase); + /* ulen is at least 1. u[ulen-1] can only be zero if ulen == 1. */ + ulen = _mpd_real_size(u, ulen); + + } while (u[ulen-1] != 0); + + return n; +} + +static size_t +_coeff_from_u16(mpd_t *w, mpd_ssize_t wlen, + const mpd_uint_t *u, size_t ulen, uint32_t ubase, + uint32_t *status) +{ + mpd_ssize_t n = 0; + mpd_uint_t carry; + + assert(wlen > 0 && ulen > 0); + assert(ubase <= (1U<<16)); + + w->data[n++] = u[--ulen]; + while (--ulen != SIZE_MAX) { + carry = _mpd_shortmul_c(w->data, w->data, n, ubase); + if (carry) { + if (n >= wlen) { + if (!mpd_qresize(w, n+1, status)) { + return SIZE_MAX; + } + wlen = n+1; + } + w->data[n++] = carry; + } + carry = _mpd_shortadd(w->data, n, u[ulen]); + if (carry) { + if (n >= wlen) { + if (!mpd_qresize(w, n+1, status)) { + return SIZE_MAX; + } + wlen = n+1; + } + w->data[n++] = carry; + } + } + + return n; +} + +/* target base wbase < source base ubase */ +static size_t +_baseconv_to_smaller(uint32_t **w, size_t wlen, uint32_t wbase, + mpd_uint_t *u, mpd_ssize_t ulen, mpd_uint_t ubase) +{ + size_t n = 0; + + assert(wlen > 0 && ulen > 0); + assert(wbase < ubase); + + do { + if (n >= wlen) { + if (!mpd_resize_u32(w, n+1)) { + return SIZE_MAX; + } + wlen = n+1; + } + (*w)[n++] = (uint32_t)_mpd_shortdiv_b(u, u, ulen, wbase, ubase); + /* ulen is at least 1. u[ulen-1] can only be zero if ulen == 1. */ + ulen = _mpd_real_size(u, ulen); + + } while (u[ulen-1] != 0); + + return n; +} + +#ifdef CONFIG_32 +/* target base 'wbase' == source base 'ubase' */ +static size_t +_copy_equal_base(uint32_t **w, size_t wlen, + const uint32_t *u, size_t ulen) +{ + if (wlen < ulen) { + if (!mpd_resize_u32(w, ulen)) { + return SIZE_MAX; + } + } + + memcpy(*w, u, ulen * (sizeof **w)); + return ulen; +} + +/* target base 'wbase' > source base 'ubase' */ +static size_t +_baseconv_to_larger(uint32_t **w, size_t wlen, mpd_uint_t wbase, + const mpd_uint_t *u, size_t ulen, mpd_uint_t ubase) +{ + size_t n = 0; + mpd_uint_t carry; + + assert(wlen > 0 && ulen > 0); + assert(ubase < wbase); + + (*w)[n++] = u[--ulen]; + while (--ulen != SIZE_MAX) { + carry = _mpd_shortmul_b(*w, *w, n, ubase, wbase); + if (carry) { + if (n >= wlen) { + if (!mpd_resize_u32(w, n+1)) { + return SIZE_MAX; + } + wlen = n+1; + } + (*w)[n++] = carry; + } + carry = _mpd_shortadd_b(*w, n, u[ulen], wbase); + if (carry) { + if (n >= wlen) { + if (!mpd_resize_u32(w, n+1)) { + return SIZE_MAX; + } + wlen = n+1; + } + (*w)[n++] = carry; + } + } + + return n; +} + +/* target base wbase < source base ubase */ +static size_t +_coeff_from_larger_base(mpd_t *w, size_t wlen, mpd_uint_t wbase, + mpd_uint_t *u, mpd_ssize_t ulen, mpd_uint_t ubase, + uint32_t *status) +{ + size_t n = 0; + + assert(wlen > 0 && ulen > 0); + assert(wbase < ubase); + + do { + if (n >= wlen) { + if (!mpd_qresize(w, n+1, status)) { + return SIZE_MAX; + } + wlen = n+1; + } + w->data[n++] = (uint32_t)_mpd_shortdiv_b(u, u, ulen, wbase, ubase); + /* ulen is at least 1. u[ulen-1] can only be zero if ulen == 1. */ + ulen = _mpd_real_size(u, ulen); + + } while (u[ulen-1] != 0); + + return n; +} +#endif + +/* target base 'wbase' > source base 'ubase' */ +static size_t +_coeff_from_smaller_base(mpd_t *w, mpd_ssize_t wlen, mpd_uint_t wbase, + const uint32_t *u, size_t ulen, mpd_uint_t ubase, + uint32_t *status) +{ + mpd_ssize_t n = 0; + mpd_uint_t carry; + + assert(wlen > 0 && ulen > 0); + assert(wbase > ubase); + + w->data[n++] = u[--ulen]; + while (--ulen != SIZE_MAX) { + carry = _mpd_shortmul_b(w->data, w->data, n, ubase, wbase); + if (carry) { + if (n >= wlen) { + if (!mpd_qresize(w, n+1, status)) { + return SIZE_MAX; + } + wlen = n+1; + } + w->data[n++] = carry; + } + carry = _mpd_shortadd_b(w->data, n, u[ulen], wbase); + if (carry) { + if (n >= wlen) { + if (!mpd_qresize(w, n+1, status)) { + return SIZE_MAX; + } + wlen = n+1; + } + w->data[n++] = carry; + } + } + + return n; +} + +/* + * Convert an integer mpd_t to a multiprecision integer with base <= 2**16. + * The least significant word of the result is (*rdata)[0]. + * + * If rdata is NULL, space is allocated by the function and rlen is irrelevant. + * In case of an error any allocated storage is freed and rdata is set back to + * NULL. + * + * If rdata is non-NULL, it MUST be allocated by one of libmpdec's allocation + * functions and rlen MUST be correct. If necessary, the function will resize + * rdata. In case of an error the caller must free rdata. + * + * Return value: In case of success, the exact length of rdata, SIZE_MAX + * otherwise. + */ +size_t +mpd_qexport_u16(uint16_t **rdata, size_t rlen, uint32_t rbase, + const mpd_t *src, uint32_t *status) +{ + MPD_NEW_STATIC(tsrc,0,0,0,0); + int alloc = 0; /* rdata == NULL */ + size_t n; + + assert(rbase <= (1U<<16)); + + if (mpd_isspecial(src) || !_mpd_isint(src)) { + *status |= MPD_Invalid_operation; + return SIZE_MAX; + } + + if (*rdata == NULL) { + rlen = mpd_sizeinbase(src, rbase); + if (rlen == SIZE_MAX) { + *status |= MPD_Invalid_operation; + return SIZE_MAX; + } + *rdata = mpd_alloc(rlen, sizeof **rdata); + if (*rdata == NULL) { + goto malloc_error; + } + alloc = 1; + } + + if (mpd_iszero(src)) { + **rdata = 0; + return 1; + } + + if (src->exp >= 0) { + if (!mpd_qshiftl(&tsrc, src, src->exp, status)) { + goto malloc_error; + } + } + else { + if (mpd_qshiftr(&tsrc, src, -src->exp, status) == MPD_UINT_MAX) { + goto malloc_error; + } + } + + n = _baseconv_to_u16(rdata, rlen, rbase, tsrc.data, tsrc.len); + if (n == SIZE_MAX) { + goto malloc_error; + } + + +out: + mpd_del(&tsrc); + return n; + +malloc_error: + if (alloc) { + mpd_free(*rdata); + *rdata = NULL; + } + n = SIZE_MAX; + *status |= MPD_Malloc_error; + goto out; +} + +/* + * Convert an integer mpd_t to a multiprecision integer with base<=UINT32_MAX. + * The least significant word of the result is (*rdata)[0]. + * + * If rdata is NULL, space is allocated by the function and rlen is irrelevant. + * In case of an error any allocated storage is freed and rdata is set back to + * NULL. + * + * If rdata is non-NULL, it MUST be allocated by one of libmpdec's allocation + * functions and rlen MUST be correct. If necessary, the function will resize + * rdata. In case of an error the caller must free rdata. + * + * Return value: In case of success, the exact length of rdata, SIZE_MAX + * otherwise. + */ +size_t +mpd_qexport_u32(uint32_t **rdata, size_t rlen, uint32_t rbase, + const mpd_t *src, uint32_t *status) +{ + MPD_NEW_STATIC(tsrc,0,0,0,0); + int alloc = 0; /* rdata == NULL */ + size_t n; + + if (mpd_isspecial(src) || !_mpd_isint(src)) { + *status |= MPD_Invalid_operation; + return SIZE_MAX; + } + + if (*rdata == NULL) { + rlen = mpd_sizeinbase(src, rbase); + if (rlen == SIZE_MAX) { + *status |= MPD_Invalid_operation; + return SIZE_MAX; + } + *rdata = mpd_alloc(rlen, sizeof **rdata); + if (*rdata == NULL) { + goto malloc_error; + } + alloc = 1; + } + + if (mpd_iszero(src)) { + **rdata = 0; + return 1; + } + + if (src->exp >= 0) { + if (!mpd_qshiftl(&tsrc, src, src->exp, status)) { + goto malloc_error; + } + } + else { + if (mpd_qshiftr(&tsrc, src, -src->exp, status) == MPD_UINT_MAX) { + goto malloc_error; + } + } + +#ifdef CONFIG_64 + n = _baseconv_to_smaller(rdata, rlen, rbase, + tsrc.data, tsrc.len, MPD_RADIX); +#else + if (rbase == MPD_RADIX) { + n = _copy_equal_base(rdata, rlen, tsrc.data, tsrc.len); + } + else if (rbase < MPD_RADIX) { + n = _baseconv_to_smaller(rdata, rlen, rbase, + tsrc.data, tsrc.len, MPD_RADIX); + } + else { + n = _baseconv_to_larger(rdata, rlen, rbase, + tsrc.data, tsrc.len, MPD_RADIX); + } +#endif + + if (n == SIZE_MAX) { + goto malloc_error; + } + + +out: + mpd_del(&tsrc); + return n; + +malloc_error: + if (alloc) { + mpd_free(*rdata); + *rdata = NULL; + } + n = SIZE_MAX; + *status |= MPD_Malloc_error; + goto out; +} + + +/* + * Converts a multiprecision integer with base <= UINT16_MAX+1 to an mpd_t. + * The least significant word of the source is srcdata[0]. + */ +void +mpd_qimport_u16(mpd_t *result, + const uint16_t *srcdata, size_t srclen, + uint8_t srcsign, uint32_t srcbase, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_uint_t *usrc; /* uint16_t src copied to an mpd_uint_t array */ + mpd_ssize_t rlen; /* length of the result */ + size_t n; + + assert(srclen > 0); + assert(srcbase <= (1U<<16)); + + rlen = _mpd_importsize(srclen, srcbase); + if (rlen == MPD_SSIZE_MAX) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + usrc = mpd_alloc((mpd_size_t)srclen, sizeof *usrc); + if (usrc == NULL) { + mpd_seterror(result, MPD_Malloc_error, status); + return; + } + for (n = 0; n < srclen; n++) { + usrc[n] = srcdata[n]; + } + + if (!mpd_qresize(result, rlen, status)) { + goto finish; + } + + n = _coeff_from_u16(result, rlen, usrc, srclen, srcbase, status); + if (n == SIZE_MAX) { + goto finish; + } + + mpd_set_flags(result, srcsign); + result->exp = 0; + result->len = n; + mpd_setdigits(result); + + mpd_qresize(result, result->len, status); + mpd_qfinalize(result, ctx, status); + + +finish: + mpd_free(usrc); +} + +/* + * Converts a multiprecision integer with base <= UINT32_MAX to an mpd_t. + * The least significant word of the source is srcdata[0]. + */ +void +mpd_qimport_u32(mpd_t *result, + const uint32_t *srcdata, size_t srclen, + uint8_t srcsign, uint32_t srcbase, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_ssize_t rlen; /* length of the result */ + size_t n; + + assert(srclen > 0); + + rlen = _mpd_importsize(srclen, srcbase); + if (rlen == MPD_SSIZE_MAX) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + if (!mpd_qresize(result, rlen, status)) { + return; + } + +#ifdef CONFIG_64 + n = _coeff_from_smaller_base(result, rlen, MPD_RADIX, + srcdata, srclen, srcbase, + status); +#else + if (srcbase == MPD_RADIX) { + if (!mpd_qresize(result, srclen, status)) { + return; + } + memcpy(result->data, srcdata, srclen * (sizeof *srcdata)); + n = srclen; + } + else if (srcbase < MPD_RADIX) { + n = _coeff_from_smaller_base(result, rlen, MPD_RADIX, + srcdata, srclen, srcbase, + status); + } + else { + mpd_uint_t *usrc = mpd_alloc((mpd_size_t)srclen, sizeof *usrc); + if (usrc == NULL) { + mpd_seterror(result, MPD_Malloc_error, status); + return; + } + for (n = 0; n < srclen; n++) { + usrc[n] = srcdata[n]; + } + + n = _coeff_from_larger_base(result, rlen, MPD_RADIX, + usrc, (mpd_ssize_t)srclen, srcbase, + status); + mpd_free(usrc); + } +#endif + + if (n == SIZE_MAX) { + return; + } + + mpd_set_flags(result, srcsign); + result->exp = 0; + result->len = n; + mpd_setdigits(result); + + mpd_qresize(result, result->len, status); + mpd_qfinalize(result, ctx, status); +} + + + diff --git a/third_party/opa/wasm/src/libmpdec/mpdecimal.h b/third_party/opa/wasm/src/libmpdec/mpdecimal.h new file mode 100644 index 000000000000..0611eb940765 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/mpdecimal.h @@ -0,0 +1,812 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef MPDECIMAL_H +#define MPDECIMAL_H + + +#ifdef __cplusplus +extern "C" { + #ifndef __STDC_LIMIT_MACROS + #define __STDC_LIMIT_MACROS + #define MPD_CLEAR_STDC_LIMIT_MACROS + #endif +#endif + + +#include +#include +#include +#include +#include +#include +#include + + +#ifndef __GNUC_STDC_INLINE__ + #define __GNUC_STDC_INLINE__ 1 +#endif +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) + #define UNUSED __attribute__((unused)) +#else + #define UNUSED +#endif +#if (defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)) && \ + defined(__GNUC__) && __GNUC__ >= 4 && !defined(__INTEL_COMPILER) + #define MPD_PRAGMA(x) _Pragma(x) + #define MPD_HIDE_SYMBOLS_START "GCC visibility push(hidden)" + #define MPD_HIDE_SYMBOLS_END "GCC visibility pop" +#else + #define MPD_PRAGMA(x) + #define MPD_HIDE_SYMBOLS_START + #define MPD_HIDE_SYMBOLS_END +#endif + + +#if !defined(LEGACY_COMPILER) + #if !defined(UINT64_MAX) + /* The following #error is just a warning. If the compiler indeed does + * not have uint64_t, it is perfectly safe to comment out the #error. */ + #error "Warning: Compiler without uint64_t. Comment out this line." + #define LEGACY_COMPILER + #endif +#endif + + +/******************************************************************************/ +/* Version */ +/******************************************************************************/ + +#define MPD_MAJOR_VERSION 2 +#define MPD_MINOR_VERSION 4 +#define MPD_MICRO_VERSION 2 + +#define MPD_VERSION "2.4.2" + +#define MPD_VERSION_HEX ((MPD_MAJOR_VERSION << 24) | \ + (MPD_MINOR_VERSION << 16) | \ + (MPD_MICRO_VERSION << 8)) + +const char *mpd_version(void); + + +/******************************************************************************/ +/* Configuration */ +/******************************************************************************/ + +/* ABI: 32-bit */ +#ifdef CONFIG_64 + #error "cannot use CONFIG_64 with 32-bit header." +#endif + +#ifndef CONFIG_32 + #define CONFIG_32 +#endif + + +/* BEGIN CONFIG_64 */ +#if defined(CONFIG_64) +/* types for modular and base arithmetic */ +#define MPD_UINT_MAX UINT64_MAX +#define MPD_BITS_PER_UINT 64 +typedef uint64_t mpd_uint_t; /* unsigned mod type */ + +#define MPD_SIZE_MAX SIZE_MAX +typedef size_t mpd_size_t; /* unsigned size type */ + +/* type for exp, digits, len, prec */ +#define MPD_SSIZE_MAX INT64_MAX +#define MPD_SSIZE_MIN INT64_MIN +typedef int64_t mpd_ssize_t; +#define _mpd_strtossize strtoll + +/* decimal arithmetic */ +#define MPD_RADIX 10000000000000000000ULL /* 10**19 */ +#define MPD_RDIGITS 19 +#define MPD_MAX_POW10 19 +#define MPD_EXPDIGITS 19 /* MPD_EXPDIGITS <= MPD_RDIGITS+1 */ + +#define MPD_MAXTRANSFORM_2N 4294967296ULL /* 2**32 */ +#define MPD_MAX_PREC 999999999999999999LL +#define MPD_MAX_PREC_LOG2 64 +#define MPD_ELIMIT 1000000000000000000LL +#define MPD_MAX_EMAX 999999999999999999LL /* ELIMIT-1 */ +#define MPD_MIN_EMIN (-999999999999999999LL) /* -EMAX */ +#define MPD_MIN_ETINY (MPD_MIN_EMIN-(MPD_MAX_PREC-1)) +#define MPD_EXP_INF 2000000000000000001LL +#define MPD_EXP_CLAMP (-4000000000000000001LL) +#define MPD_MAXIMPORT 105263157894736842L /* ceil((2*MPD_MAX_PREC)/MPD_RDIGITS) */ + +/* conversion specifiers */ +#define PRI_mpd_uint_t PRIu64 +#define PRI_mpd_ssize_t PRIi64 +/* END CONFIG_64 */ + + +/* BEGIN CONFIG_32 */ +#elif defined(CONFIG_32) +/* types for modular and base arithmetic */ +#define MPD_UINT_MAX UINT32_MAX +#define MPD_BITS_PER_UINT 32 +typedef uint32_t mpd_uint_t; /* unsigned mod type */ + +#ifndef LEGACY_COMPILER +#define MPD_UUINT_MAX UINT64_MAX +typedef uint64_t mpd_uuint_t; /* double width unsigned mod type */ +#endif + +#define MPD_SIZE_MAX SIZE_MAX +typedef size_t mpd_size_t; /* unsigned size type */ + +/* type for dec->len, dec->exp, ctx->prec */ +#define MPD_SSIZE_MAX INT32_MAX +#define MPD_SSIZE_MIN INT32_MIN +typedef int32_t mpd_ssize_t; +#define _mpd_strtossize strtol + +/* decimal arithmetic */ +#define MPD_RADIX 1000000000UL /* 10**9 */ +#define MPD_RDIGITS 9 +#define MPD_MAX_POW10 9 +#define MPD_EXPDIGITS 10 /* MPD_EXPDIGITS <= MPD_RDIGITS+1 */ + +#define MPD_MAXTRANSFORM_2N 33554432UL /* 2**25 */ +#define MPD_MAX_PREC 425000000L +#define MPD_MAX_PREC_LOG2 32 +#define MPD_ELIMIT 425000001L +#define MPD_MAX_EMAX 425000000L /* ELIMIT-1 */ +#define MPD_MIN_EMIN (-425000000L) /* -EMAX */ +#define MPD_MIN_ETINY (MPD_MIN_EMIN-(MPD_MAX_PREC-1)) +#define MPD_EXP_INF 1000000001L /* allows for emax=999999999 in the tests */ +#define MPD_EXP_CLAMP (-2000000001L) /* allows for emin=-999999999 in the tests */ +#define MPD_MAXIMPORT 94444445L /* ceil((2*MPD_MAX_PREC)/MPD_RDIGITS) */ + +/* conversion specifiers */ +#define PRI_mpd_uint_t PRIu32 +#define PRI_mpd_ssize_t PRIi32 +/* END CONFIG_32 */ + +#else + #error "define CONFIG_64 or CONFIG_32" +#endif +/* END CONFIG */ + + +#if MPD_SIZE_MAX != MPD_UINT_MAX + #error "unsupported platform: need mpd_size_t == mpd_uint_t" +#endif + + +/******************************************************************************/ +/* Context */ +/******************************************************************************/ + +enum { + MPD_ROUND_UP, /* round away from 0 */ + MPD_ROUND_DOWN, /* round toward 0 (truncate) */ + MPD_ROUND_CEILING, /* round toward +infinity */ + MPD_ROUND_FLOOR, /* round toward -infinity */ + MPD_ROUND_HALF_UP, /* 0.5 is rounded up */ + MPD_ROUND_HALF_DOWN, /* 0.5 is rounded down */ + MPD_ROUND_HALF_EVEN, /* 0.5 is rounded to even */ + MPD_ROUND_05UP, /* round zero or five away from 0 */ + MPD_ROUND_TRUNC, /* truncate, but set infinity */ + MPD_ROUND_GUARD +}; + +enum { MPD_CLAMP_DEFAULT, MPD_CLAMP_IEEE_754, MPD_CLAMP_GUARD }; + +extern const char *mpd_round_string[MPD_ROUND_GUARD]; +extern const char *mpd_clamp_string[MPD_CLAMP_GUARD]; + + +typedef struct mpd_context_t { + mpd_ssize_t prec; /* precision */ + mpd_ssize_t emax; /* max positive exp */ + mpd_ssize_t emin; /* min negative exp */ + uint32_t traps; /* status events that should be trapped */ + uint32_t status; /* status flags */ + uint32_t newtrap; /* set by mpd_addstatus_raise() */ + int round; /* rounding mode */ + int clamp; /* clamp mode */ + int allcr; /* all functions correctly rounded */ +} mpd_context_t; + + +/* Status flags */ +#define MPD_Clamped 0x00000001U +#define MPD_Conversion_syntax 0x00000002U +#define MPD_Division_by_zero 0x00000004U +#define MPD_Division_impossible 0x00000008U +#define MPD_Division_undefined 0x00000010U +#define MPD_Fpu_error 0x00000020U +#define MPD_Inexact 0x00000040U +#define MPD_Invalid_context 0x00000080U +#define MPD_Invalid_operation 0x00000100U +#define MPD_Malloc_error 0x00000200U +#define MPD_Not_implemented 0x00000400U +#define MPD_Overflow 0x00000800U +#define MPD_Rounded 0x00001000U +#define MPD_Subnormal 0x00002000U +#define MPD_Underflow 0x00004000U +#define MPD_Max_status (0x00008000U-1U) + +/* Conditions that result in an IEEE 754 exception */ +#define MPD_IEEE_Invalid_operation (MPD_Conversion_syntax | \ + MPD_Division_impossible | \ + MPD_Division_undefined | \ + MPD_Fpu_error | \ + MPD_Invalid_context | \ + MPD_Invalid_operation | \ + MPD_Malloc_error) \ + +/* Errors that require the result of an operation to be set to NaN */ +#define MPD_Errors (MPD_IEEE_Invalid_operation | \ + MPD_Division_by_zero) + +/* Default traps */ +#define MPD_Traps (MPD_IEEE_Invalid_operation | \ + MPD_Division_by_zero | \ + MPD_Overflow | \ + MPD_Underflow) + +/* Official name */ +#define MPD_Insufficient_storage MPD_Malloc_error + +/* IEEE 754 interchange format contexts */ +#define MPD_IEEE_CONTEXT_MAX_BITS 512 /* 16*(log2(MPD_MAX_EMAX / 3)-3) */ +#define MPD_DECIMAL32 32 +#define MPD_DECIMAL64 64 +#define MPD_DECIMAL128 128 + + +#define MPD_MINALLOC_MIN 2 +#define MPD_MINALLOC_MAX 64 +extern mpd_ssize_t MPD_MINALLOC; +extern void (* mpd_traphandler)(mpd_context_t *); +void mpd_dflt_traphandler(mpd_context_t *); + +void mpd_setminalloc(mpd_ssize_t n); +void mpd_init(mpd_context_t *ctx, mpd_ssize_t prec); + +void mpd_maxcontext(mpd_context_t *ctx); +void mpd_defaultcontext(mpd_context_t *ctx); +void mpd_basiccontext(mpd_context_t *ctx); +int mpd_ieee_context(mpd_context_t *ctx, int bits); + +mpd_ssize_t mpd_getprec(const mpd_context_t *ctx); +mpd_ssize_t mpd_getemax(const mpd_context_t *ctx); +mpd_ssize_t mpd_getemin(const mpd_context_t *ctx); +int mpd_getround(const mpd_context_t *ctx); +uint32_t mpd_gettraps(const mpd_context_t *ctx); +uint32_t mpd_getstatus(const mpd_context_t *ctx); +int mpd_getclamp(const mpd_context_t *ctx); +int mpd_getcr(const mpd_context_t *ctx); + +int mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec); +int mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax); +int mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin); +int mpd_qsetround(mpd_context_t *ctx, int newround); +int mpd_qsettraps(mpd_context_t *ctx, uint32_t flags); +int mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags); +int mpd_qsetclamp(mpd_context_t *ctx, int c); +int mpd_qsetcr(mpd_context_t *ctx, int c); +void mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags); + + +/******************************************************************************/ +/* Decimal Arithmetic */ +/******************************************************************************/ + +/* mpd_t flags */ +#define MPD_POS ((uint8_t)0) +#define MPD_NEG ((uint8_t)1) +#define MPD_INF ((uint8_t)2) +#define MPD_NAN ((uint8_t)4) +#define MPD_SNAN ((uint8_t)8) +#define MPD_SPECIAL (MPD_INF|MPD_NAN|MPD_SNAN) +#define MPD_STATIC ((uint8_t)16) +#define MPD_STATIC_DATA ((uint8_t)32) +#define MPD_SHARED_DATA ((uint8_t)64) +#define MPD_CONST_DATA ((uint8_t)128) +#define MPD_DATAFLAGS (MPD_STATIC_DATA|MPD_SHARED_DATA|MPD_CONST_DATA) + +/* mpd_t */ +typedef struct mpd_t { + uint8_t flags; + mpd_ssize_t exp; + mpd_ssize_t digits; + mpd_ssize_t len; + mpd_ssize_t alloc; + mpd_uint_t *data; +} mpd_t; + + +typedef unsigned char uchar; + + +/******************************************************************************/ +/* Quiet, thread-safe functions */ +/******************************************************************************/ + +/* format specification */ +typedef struct mpd_spec_t { + mpd_ssize_t min_width; /* minimum field width */ + mpd_ssize_t prec; /* fraction digits or significant digits */ + char type; /* conversion specifier */ + char align; /* alignment */ + char sign; /* sign printing/alignment */ + char fill[5]; /* fill character */ + const char *dot; /* decimal point */ + const char *sep; /* thousands separator */ + const char *grouping; /* grouping of digits */ +} mpd_spec_t; + +/* output to a string */ +char *mpd_to_sci(const mpd_t *dec, int fmt); +char *mpd_to_eng(const mpd_t *dec, int fmt); +mpd_ssize_t mpd_to_sci_size(char **res, const mpd_t *dec, int fmt); +mpd_ssize_t mpd_to_eng_size(char **res, const mpd_t *dec, int fmt); +int mpd_validate_lconv(mpd_spec_t *spec); +int mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps); +char *mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec, const mpd_context_t *ctx, uint32_t *status); +char *mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx, uint32_t *status); + +#define MPD_NUM_FLAGS 15 +#define MPD_MAX_FLAG_STRING 208 +#define MPD_MAX_FLAG_LIST (MPD_MAX_FLAG_STRING+18) +#define MPD_MAX_SIGNAL_LIST 121 +int mpd_snprint_flags(char *dest, int nmemb, uint32_t flags); +int mpd_lsnprint_flags(char *dest, int nmemb, uint32_t flags, const char *flag_string[]); +int mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[]); + +/* output to a file */ +void mpd_fprint(FILE *file, const mpd_t *dec); +void mpd_print(const mpd_t *dec); + +/* assignment from a string */ +void mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx, uint32_t *status); + +/* set to NaN with error flags */ +void mpd_seterror(mpd_t *result, uint32_t flags, uint32_t *status); +/* set a special with sign and type */ +void mpd_setspecial(mpd_t *dec, uint8_t sign, uint8_t type); +/* set coefficient to zero or all nines */ +void mpd_zerocoeff(mpd_t *result); +void mpd_qmaxcoeff(mpd_t *result, const mpd_context_t *ctx, uint32_t *status); + +/* quietly assign a C integer type to an mpd_t */ +void mpd_qset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status); +#ifndef LEGACY_COMPILER +void mpd_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, uint32_t *status); +#endif + +/* quietly assign a C integer type to an mpd_t with a static coefficient */ +void mpd_qsset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status); + +/* quietly get a C integer type from an mpd_t */ +mpd_ssize_t mpd_qget_ssize(const mpd_t *dec, uint32_t *status); +mpd_uint_t mpd_qget_uint(const mpd_t *dec, uint32_t *status); +mpd_uint_t mpd_qabs_uint(const mpd_t *dec, uint32_t *status); + +int32_t mpd_qget_i32(const mpd_t *dec, uint32_t *status); +uint32_t mpd_qget_u32(const mpd_t *dec, uint32_t *status); +#ifndef LEGACY_COMPILER +int64_t mpd_qget_i64(const mpd_t *dec, uint32_t *status); +uint64_t mpd_qget_u64(const mpd_t *dec, uint32_t *status); +#endif + +/* quiet functions */ +int mpd_qcheck_nan(mpd_t *nanresult, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +int mpd_qcheck_nans(mpd_t *nanresult, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status); + +const char *mpd_class(const mpd_t *a, const mpd_context_t *ctx); + +int mpd_qcopy(mpd_t *result, const mpd_t *a, uint32_t *status); +mpd_t *mpd_qncopy(const mpd_t *a); +int mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status); +int mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status); +int mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status); + +void mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +int mpd_same_quantum(const mpd_t *a, const mpd_t *b); + +void mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +int mpd_qshiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status); +mpd_uint_t mpd_qshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status); +mpd_uint_t mpd_qshiftr_inplace(mpd_t *result, mpd_ssize_t n); +void mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qshiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, const mpd_context_t *ctx, uint32_t *status); + +int mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status); +int mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +int mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +int mpd_cmp_total(const mpd_t *a, const mpd_t *b); +int mpd_cmp_total_mag(const mpd_t *a, const mpd_t *b); +int mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b); +int mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b); + +void mpd_qround_to_intx(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qround_to_int(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qtrunc(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qfloor(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qceil(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); + +void mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmax(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmax_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmin(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmin_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qminus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qplus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qnext_plus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qnext_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status); +void mpd_qrescale_fmt(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status); +void mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qadd_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qadd_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qadd_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qadd_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsub_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsub_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmul_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmul_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdiv_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdiv_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdiv_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdiv_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qpow(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_context_t *ctx, uint32_t *status); +void mpd_qpowmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, const mpd_context_t *ctx, uint32_t *status); +void mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status); +void mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); + +#ifndef LEGACY_COMPILER +void mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status); +#endif + + +size_t mpd_sizeinbase(const mpd_t *a, uint32_t base); +void mpd_qimport_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen, + uint8_t srcsign, uint32_t srcbase, + const mpd_context_t *ctx, uint32_t *status); +void mpd_qimport_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen, + uint8_t srcsign, uint32_t srcbase, + const mpd_context_t *ctx, uint32_t *status); +size_t mpd_qexport_u16(uint16_t **rdata, size_t rlen, uint32_t base, + const mpd_t *src, uint32_t *status); +size_t mpd_qexport_u32(uint32_t **rdata, size_t rlen, uint32_t base, + const mpd_t *src, uint32_t *status); + + +/******************************************************************************/ +/* Signalling functions */ +/******************************************************************************/ + +char *mpd_format(const mpd_t *dec, const char *fmt, mpd_context_t *ctx); +void mpd_import_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen, uint8_t srcsign, uint32_t base, mpd_context_t *ctx); +void mpd_import_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen, uint8_t srcsign, uint32_t base, mpd_context_t *ctx); +size_t mpd_export_u16(uint16_t **rdata, size_t rlen, uint32_t base, const mpd_t *src, mpd_context_t *ctx); +size_t mpd_export_u32(uint32_t **rdata, size_t rlen, uint32_t base, const mpd_t *src, mpd_context_t *ctx); +void mpd_finalize(mpd_t *result, mpd_context_t *ctx); +int mpd_check_nan(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +int mpd_check_nans(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_set_string(mpd_t *result, const char *s, mpd_context_t *ctx); +void mpd_maxcoeff(mpd_t *result, mpd_context_t *ctx); +void mpd_sset_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx); +void mpd_sset_i32(mpd_t *result, int32_t a, mpd_context_t *ctx); +void mpd_sset_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx); +void mpd_sset_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx); +void mpd_set_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx); +void mpd_set_i32(mpd_t *result, int32_t a, mpd_context_t *ctx); +void mpd_set_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx); +void mpd_set_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx); +#ifndef LEGACY_COMPILER +void mpd_set_i64(mpd_t *result, int64_t a, mpd_context_t *ctx); +void mpd_set_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx); +#endif +mpd_ssize_t mpd_get_ssize(const mpd_t *a, mpd_context_t *ctx); +mpd_uint_t mpd_get_uint(const mpd_t *a, mpd_context_t *ctx); +mpd_uint_t mpd_abs_uint(const mpd_t *a, mpd_context_t *ctx); +int32_t mpd_get_i32(const mpd_t *a, mpd_context_t *ctx); +uint32_t mpd_get_u32(const mpd_t *a, mpd_context_t *ctx); +#ifndef LEGACY_COMPILER +int64_t mpd_get_i64(const mpd_t *a, mpd_context_t *ctx); +uint64_t mpd_get_u64(const mpd_t *a, mpd_context_t *ctx); +#endif +void mpd_and(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_copy(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_canonical(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_copy_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_copy_negate(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_copy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_invert(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_logb(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_or(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_rotate(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_scaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_shiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx); +mpd_uint_t mpd_shiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx); +void mpd_shiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx); +void mpd_shift(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_xor(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +int mpd_cmp(const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +int mpd_compare(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +int mpd_compare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_add(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_add_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx); +void mpd_add_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx); +void mpd_add_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx); +void mpd_add_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx); +void mpd_sub(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_sub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx); +void mpd_sub_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx); +void mpd_sub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx); +void mpd_sub_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx); +void mpd_div(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_div_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx); +void mpd_div_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx); +void mpd_div_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx); +void mpd_div_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx); +void mpd_divmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_divint(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_exp(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_fma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, mpd_context_t *ctx); +void mpd_ln(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_log10(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_max(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_max_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_min(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_min_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_mul(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_mul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx); +void mpd_mul_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx); +void mpd_mul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx); +void mpd_mul_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx); +void mpd_next_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_next_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_next_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_pow(mpd_t *result, const mpd_t *base, const mpd_t *exp, mpd_context_t *ctx); +void mpd_powmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, mpd_context_t *ctx); +void mpd_quantize(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_rescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, mpd_context_t *ctx); +void mpd_reduce(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_rem(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_rem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_round_to_intx(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_round_to_int(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_trunc(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_floor(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_ceil(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_sqrt(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); +void mpd_invroot(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); + +#ifndef LEGACY_COMPILER +void mpd_add_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx); +void mpd_add_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx); +void mpd_sub_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx); +void mpd_sub_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx); +void mpd_div_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx); +void mpd_div_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx); +void mpd_mul_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx); +void mpd_mul_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx); +#endif + + +/******************************************************************************/ +/* Configuration specific */ +/******************************************************************************/ + +#ifdef CONFIG_64 +void mpd_qsset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_sset_i64(mpd_t *result, int64_t a, mpd_context_t *ctx); +void mpd_sset_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx); +#endif + + +/******************************************************************************/ +/* Get attributes of a decimal */ +/******************************************************************************/ + +mpd_ssize_t mpd_adjexp(const mpd_t *dec); +mpd_ssize_t mpd_etiny(const mpd_context_t *ctx); +mpd_ssize_t mpd_etop(const mpd_context_t *ctx); +mpd_uint_t mpd_msword(const mpd_t *dec); +int mpd_word_digits(mpd_uint_t word); +/* most significant digit of a word */ +mpd_uint_t mpd_msd(mpd_uint_t word); +/* least significant digit of a word */ +mpd_uint_t mpd_lsd(mpd_uint_t word); +/* coefficient size needed to store 'digits' */ +mpd_ssize_t mpd_digits_to_size(mpd_ssize_t digits); +/* number of digits in the exponent, undefined for MPD_SSIZE_MIN */ +int mpd_exp_digits(mpd_ssize_t exp); +int mpd_iscanonical(const mpd_t *dec UNUSED); +int mpd_isfinite(const mpd_t *dec); +int mpd_isinfinite(const mpd_t *dec); +int mpd_isinteger(const mpd_t *dec); +int mpd_isnan(const mpd_t *dec); +int mpd_isnegative(const mpd_t *dec); +int mpd_ispositive(const mpd_t *dec); +int mpd_isqnan(const mpd_t *dec); +int mpd_issigned(const mpd_t *dec); +int mpd_issnan(const mpd_t *dec); +int mpd_isspecial(const mpd_t *dec); +int mpd_iszero(const mpd_t *dec); +/* undefined for special numbers */ +int mpd_iszerocoeff(const mpd_t *dec); +int mpd_isnormal(const mpd_t *dec, const mpd_context_t *ctx); +int mpd_issubnormal(const mpd_t *dec, const mpd_context_t *ctx); +/* odd word */ +int mpd_isoddword(mpd_uint_t word); +/* odd coefficient */ +int mpd_isoddcoeff(const mpd_t *dec); +/* odd decimal, only defined for integers */ +int mpd_isodd(const mpd_t *dec); +/* even decimal, only defined for integers */ +int mpd_iseven(const mpd_t *dec); +/* 0 if dec is positive, 1 if dec is negative */ +uint8_t mpd_sign(const mpd_t *dec); +/* 1 if dec is positive, -1 if dec is negative */ +int mpd_arith_sign(const mpd_t *dec); +long mpd_radix(void); +int mpd_isdynamic(const mpd_t *dec); +int mpd_isstatic(const mpd_t *dec); +int mpd_isdynamic_data(const mpd_t *dec); +int mpd_isstatic_data(const mpd_t *dec); +int mpd_isshared_data(const mpd_t *dec); +int mpd_isconst_data(const mpd_t *dec); +mpd_ssize_t mpd_trail_zeros(const mpd_t *dec); + + +/******************************************************************************/ +/* Set attributes of a decimal */ +/******************************************************************************/ + +/* set number of decimal digits in the coefficient */ +void mpd_setdigits(mpd_t *result); +void mpd_set_sign(mpd_t *result, uint8_t sign); +/* copy sign from another decimal */ +void mpd_signcpy(mpd_t *result, const mpd_t *a); +void mpd_set_infinity(mpd_t *result); +void mpd_set_qnan(mpd_t *result); +void mpd_set_snan(mpd_t *result); +void mpd_set_negative(mpd_t *result); +void mpd_set_positive(mpd_t *result); +void mpd_set_dynamic(mpd_t *result); +void mpd_set_static(mpd_t *result); +void mpd_set_dynamic_data(mpd_t *result); +void mpd_set_static_data(mpd_t *result); +void mpd_set_shared_data(mpd_t *result); +void mpd_set_const_data(mpd_t *result); +void mpd_clear_flags(mpd_t *result); +void mpd_set_flags(mpd_t *result, uint8_t flags); +void mpd_copy_flags(mpd_t *result, const mpd_t *a); + + +/******************************************************************************/ +/* Error Macros */ +/******************************************************************************/ + +#define mpd_err_fatal(...) \ + do {fprintf(stderr, "%s:%d: error: ", __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); fputc('\n', stderr); \ + abort(); \ + } while (0) +#define mpd_err_warn(...) \ + do {fprintf(stderr, "%s:%d: warning: ", __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); fputc('\n', stderr); \ + } while (0) + + +/******************************************************************************/ +/* Memory handling */ +/******************************************************************************/ + +extern void *(* mpd_mallocfunc)(size_t size); +extern void *(* mpd_callocfunc)(size_t nmemb, size_t size); +extern void *(* mpd_reallocfunc)(void *ptr, size_t size); +extern void (* mpd_free)(void *ptr); + +void *mpd_callocfunc_em(size_t nmemb, size_t size); + +void *mpd_alloc(mpd_size_t nmemb, mpd_size_t size); +void *mpd_calloc(mpd_size_t nmemb, mpd_size_t size); +void *mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err); +void *mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size); + +mpd_t *mpd_qnew(void); +mpd_t *mpd_new(mpd_context_t *ctx); +mpd_t *mpd_qnew_size(mpd_ssize_t size); +void mpd_del(mpd_t *dec); + +void mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len); +int mpd_qresize(mpd_t *result, mpd_ssize_t size, uint32_t *status); +int mpd_qresize_zero(mpd_t *result, mpd_ssize_t size, uint32_t *status); +void mpd_minalloc(mpd_t *result); + +int mpd_resize(mpd_t *result, mpd_ssize_t size, mpd_context_t *ctx); +int mpd_resize_zero(mpd_t *result, mpd_ssize_t size, mpd_context_t *ctx); + + +#ifdef __cplusplus + #ifdef MPD_CLEAR_STDC_LIMIT_MACROS + #undef MPD_CLEAR_STDC_LIMIT_MACROS + #undef __STDC_LIMIT_MACROS + #endif +} /* END extern "C" */ +#endif + + +#endif /* MPDECIMAL_H */ + + + diff --git a/third_party/opa/wasm/src/libmpdec/numbertheory.c b/third_party/opa/wasm/src/libmpdec/numbertheory.c new file mode 100644 index 000000000000..4e035477e280 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/numbertheory.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include +#include "bits.h" +#include "umodarith.h" +#include "numbertheory.h" + + +/* Bignum: Initialize the Number Theoretic Transform. */ + + +/* + * Return the nth root of unity in F(p). This corresponds to e**((2*pi*i)/n) + * in the Fourier transform. We have w**n == 1 (mod p). + * n := transform length. + * sign := -1 for forward transform, 1 for backward transform. + * modnum := one of {P1, P2, P3}. + */ +mpd_uint_t +_mpd_getkernel(mpd_uint_t n, int sign, int modnum) +{ + mpd_uint_t umod, p, r, xi; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + + SETMODULUS(modnum); + r = mpd_roots[modnum]; /* primitive root of F(p) */ + p = umod; + xi = (p-1) / n; + + if (sign == -1) + return POWMOD(r, (p-1-xi)); + else + return POWMOD(r, xi); +} + +/* + * Initialize and return transform parameters. + * n := transform length. + * sign := -1 for forward transform, 1 for backward transform. + * modnum := one of {P1, P2, P3}. + */ +struct fnt_params * +_mpd_init_fnt_params(mpd_size_t n, int sign, int modnum) +{ + struct fnt_params *tparams; + mpd_uint_t umod; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t kernel, w; + mpd_uint_t i; + mpd_size_t nhalf; + + assert(ispower2(n)); + assert(sign == -1 || sign == 1); + assert(P1 <= modnum && modnum <= P3); + + nhalf = n/2; + tparams = mpd_sh_alloc(sizeof *tparams, nhalf, sizeof (mpd_uint_t)); + if (tparams == NULL) { + return NULL; + } + + SETMODULUS(modnum); + kernel = _mpd_getkernel(n, sign, modnum); + + tparams->modnum = modnum; + tparams->modulus = umod; + tparams->kernel = kernel; + + /* wtable[] := w**0, w**1, ..., w**(nhalf-1) */ + w = 1; + for (i = 0; i < nhalf; i++) { + tparams->wtable[i] = w; + w = MULMOD(w, kernel); + } + + return tparams; +} + +/* Initialize wtable of size three. */ +void +_mpd_init_w3table(mpd_uint_t w3table[3], int sign, int modnum) +{ + mpd_uint_t umod; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t kernel; + + SETMODULUS(modnum); + kernel = _mpd_getkernel(3, sign, modnum); + + w3table[0] = 1; + w3table[1] = kernel; + w3table[2] = POWMOD(kernel, 2); +} + + diff --git a/third_party/opa/wasm/src/libmpdec/numbertheory.h b/third_party/opa/wasm/src/libmpdec/numbertheory.h new file mode 100644 index 000000000000..e94c157910c8 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/numbertheory.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef NUMBER_THEORY_H +#define NUMBER_THEORY_H + + +#include "constants.h" +#include "mpdecimal.h" + + +/* Internal header file: all symbols have local scope in the DSO */ +MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) + + +/* transform parameters */ +struct fnt_params { + int modnum; + mpd_uint_t modulus; + mpd_uint_t kernel; + mpd_uint_t wtable[]; +}; + + +mpd_uint_t _mpd_getkernel(mpd_uint_t n, int sign, int modnum); +struct fnt_params *_mpd_init_fnt_params(mpd_size_t n, int sign, int modnum); +void _mpd_init_w3table(mpd_uint_t w3table[3], int sign, int modnum); + + +#ifdef PPRO +static inline void +ppro_setmodulus(int modnum, mpd_uint_t *umod, double *dmod, uint32_t dinvmod[3]) +{ + *dmod = *umod = mpd_moduli[modnum]; + dinvmod[0] = mpd_invmoduli[modnum][0]; + dinvmod[1] = mpd_invmoduli[modnum][1]; + dinvmod[2] = mpd_invmoduli[modnum][2]; +} +#else +static inline void +std_setmodulus(int modnum, mpd_uint_t *umod) +{ + *umod = mpd_moduli[modnum]; +} +#endif + + +MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ + + +#endif + + diff --git a/third_party/opa/wasm/src/libmpdec/sixstep.c b/third_party/opa/wasm/src/libmpdec/sixstep.c new file mode 100644 index 000000000000..92d513ebe182 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/sixstep.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include +#include +#include "bits.h" +#include "difradix2.h" +#include "numbertheory.h" +#include "transpose.h" +#include "umodarith.h" +#include "sixstep.h" + + +/* Bignum: Cache efficient Matrix Fourier Transform for arrays of the + form 2**n (See literature/six-step.txt). */ + + +/* forward transform with sign = -1 */ +int +six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) +{ + struct fnt_params *tparams; + mpd_size_t log2n, C, R; + mpd_uint_t kernel; + mpd_uint_t umod; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t *x, w0, w1, wstep; + mpd_size_t i, k; + + + assert(ispower2(n)); + assert(n >= 16); + assert(n <= MPD_MAXTRANSFORM_2N); + + log2n = mpd_bsr(n); + C = ((mpd_size_t)1) << (log2n / 2); /* number of columns */ + R = ((mpd_size_t)1) << (log2n - (log2n / 2)); /* number of rows */ + + + /* Transpose the matrix. */ + if (!transpose_pow2(a, R, C)) { + return 0; + } + + /* Length R transform on the rows. */ + if ((tparams = _mpd_init_fnt_params(R, -1, modnum)) == NULL) { + return 0; + } + for (x = a; x < a+n; x += R) { + fnt_dif2(x, R, tparams); + } + + /* Transpose the matrix. */ + if (!transpose_pow2(a, C, R)) { + mpd_free(tparams); + return 0; + } + + /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */ + SETMODULUS(modnum); + kernel = _mpd_getkernel(n, -1, modnum); + for (i = 1; i < R; i++) { + w0 = 1; /* r**(i*0): initial value for k=0 */ + w1 = POWMOD(kernel, i); /* r**(i*1): initial value for k=1 */ + wstep = MULMOD(w1, w1); /* r**(2*i) */ + for (k = 0; k < C; k += 2) { + mpd_uint_t x0 = a[i*C+k]; + mpd_uint_t x1 = a[i*C+k+1]; + MULMOD2(&x0, w0, &x1, w1); + MULMOD2C(&w0, &w1, wstep); /* r**(i*(k+2)) = r**(i*k) * r**(2*i) */ + a[i*C+k] = x0; + a[i*C+k+1] = x1; + } + } + + /* Length C transform on the rows. */ + if (C != R) { + mpd_free(tparams); + if ((tparams = _mpd_init_fnt_params(C, -1, modnum)) == NULL) { + return 0; + } + } + for (x = a; x < a+n; x += C) { + fnt_dif2(x, C, tparams); + } + mpd_free(tparams); + +#if 0 + /* An unordered transform is sufficient for convolution. */ + /* Transpose the matrix. */ + if (!transpose_pow2(a, R, C)) { + return 0; + } +#endif + + return 1; +} + + +/* reverse transform, sign = 1 */ +int +inv_six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) +{ + struct fnt_params *tparams; + mpd_size_t log2n, C, R; + mpd_uint_t kernel; + mpd_uint_t umod; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t *x, w0, w1, wstep; + mpd_size_t i, k; + + + assert(ispower2(n)); + assert(n >= 16); + assert(n <= MPD_MAXTRANSFORM_2N); + + log2n = mpd_bsr(n); + C = ((mpd_size_t)1) << (log2n / 2); /* number of columns */ + R = ((mpd_size_t)1) << (log2n - (log2n / 2)); /* number of rows */ + + +#if 0 + /* An unordered transform is sufficient for convolution. */ + /* Transpose the matrix, producing an R*C matrix. */ + if (!transpose_pow2(a, C, R)) { + return 0; + } +#endif + + /* Length C transform on the rows. */ + if ((tparams = _mpd_init_fnt_params(C, 1, modnum)) == NULL) { + return 0; + } + for (x = a; x < a+n; x += C) { + fnt_dif2(x, C, tparams); + } + + /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */ + SETMODULUS(modnum); + kernel = _mpd_getkernel(n, 1, modnum); + for (i = 1; i < R; i++) { + w0 = 1; + w1 = POWMOD(kernel, i); + wstep = MULMOD(w1, w1); + for (k = 0; k < C; k += 2) { + mpd_uint_t x0 = a[i*C+k]; + mpd_uint_t x1 = a[i*C+k+1]; + MULMOD2(&x0, w0, &x1, w1); + MULMOD2C(&w0, &w1, wstep); + a[i*C+k] = x0; + a[i*C+k+1] = x1; + } + } + + /* Transpose the matrix. */ + if (!transpose_pow2(a, R, C)) { + mpd_free(tparams); + return 0; + } + + /* Length R transform on the rows. */ + if (R != C) { + mpd_free(tparams); + if ((tparams = _mpd_init_fnt_params(R, 1, modnum)) == NULL) { + return 0; + } + } + for (x = a; x < a+n; x += R) { + fnt_dif2(x, R, tparams); + } + mpd_free(tparams); + + /* Transpose the matrix. */ + if (!transpose_pow2(a, C, R)) { + return 0; + } + + return 1; +} + + diff --git a/third_party/opa/wasm/src/libmpdec/sixstep.h b/third_party/opa/wasm/src/libmpdec/sixstep.h new file mode 100644 index 000000000000..4a8b015e3a9b --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/sixstep.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef SIX_STEP_H +#define SIX_STEP_H + + +#include "mpdecimal.h" +#include + + +/* Internal header file: all symbols have local scope in the DSO */ +MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) + + +int six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum); +int inv_six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum); + + +MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ + + +#endif diff --git a/third_party/opa/wasm/src/libmpdec/transpose.c b/third_party/opa/wasm/src/libmpdec/transpose.c new file mode 100644 index 000000000000..55d6d8992279 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/transpose.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include "mpdecimal.h" +#include +#include +#include +#include +#include +#include "bits.h" +#include "constants.h" +#include "typearith.h" +#include "transpose.h" + + +#define BUFSIZE 4096 +#define SIDE 128 + + +/* Bignum: The transpose functions are used for very large transforms + in sixstep.c and fourstep.c. */ + + +/* Definition of the matrix transpose */ +void +std_trans(mpd_uint_t dest[], mpd_uint_t src[], mpd_size_t rows, mpd_size_t cols) +{ + mpd_size_t idest, isrc; + mpd_size_t r, c; + + for (r = 0; r < rows; r++) { + isrc = r * cols; + idest = r; + for (c = 0; c < cols; c++) { + dest[idest] = src[isrc]; + isrc += 1; + idest += rows; + } + } +} + +/* + * Swap half-rows of 2^n * (2*2^n) matrix. + * FORWARD_CYCLE: even/odd permutation of the halfrows. + * BACKWARD_CYCLE: reverse the even/odd permutation. + */ +static int +swap_halfrows_pow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols, int dir) +{ + mpd_uint_t buf1[BUFSIZE]; + mpd_uint_t buf2[BUFSIZE]; + mpd_uint_t *readbuf, *writebuf, *hp; + mpd_size_t *done, dbits; + mpd_size_t b = BUFSIZE, stride; + mpd_size_t hn, hmax; /* halfrow number */ + mpd_size_t m, r=0; + mpd_size_t offset; + mpd_size_t next; + + + assert(cols == mul_size_t(2, rows)); + + if (dir == FORWARD_CYCLE) { + r = rows; + } + else if (dir == BACKWARD_CYCLE) { + r = 2; + } + else { + abort(); /* GCOV_NOT_REACHED */ + } + + m = cols - 1; + hmax = rows; /* cycles start at odd halfrows */ + dbits = 8 * sizeof *done; + if ((done = mpd_calloc(hmax/(sizeof *done) + 1, sizeof *done)) == NULL) { + return 0; + } + + for (hn = 1; hn <= hmax; hn += 2) { + + if (done[hn/dbits] & mpd_bits[hn%dbits]) { + continue; + } + + readbuf = buf1; writebuf = buf2; + + for (offset = 0; offset < cols/2; offset += b) { + + stride = (offset + b < cols/2) ? b : cols/2-offset; + + hp = matrix + hn*cols/2; + memcpy(readbuf, hp+offset, stride*(sizeof *readbuf)); + pointerswap(&readbuf, &writebuf); + + next = mulmod_size_t(hn, r, m); + hp = matrix + next*cols/2; + + while (next != hn) { + + memcpy(readbuf, hp+offset, stride*(sizeof *readbuf)); + memcpy(hp+offset, writebuf, stride*(sizeof *writebuf)); + pointerswap(&readbuf, &writebuf); + + done[next/dbits] |= mpd_bits[next%dbits]; + + next = mulmod_size_t(next, r, m); + hp = matrix + next*cols/2; + + } + + memcpy(hp+offset, writebuf, stride*(sizeof *writebuf)); + + done[hn/dbits] |= mpd_bits[hn%dbits]; + } + } + + mpd_free(done); + return 1; +} + +/* In-place transpose of a square matrix */ +static inline void +squaretrans(mpd_uint_t *buf, mpd_size_t cols) +{ + mpd_uint_t tmp; + mpd_size_t idest, isrc; + mpd_size_t r, c; + + for (r = 0; r < cols; r++) { + c = r+1; + isrc = r*cols + c; + idest = c*cols + r; + for (c = r+1; c < cols; c++) { + tmp = buf[isrc]; + buf[isrc] = buf[idest]; + buf[idest] = tmp; + isrc += 1; + idest += cols; + } + } +} + +/* + * Transpose 2^n * 2^n matrix. For cache efficiency, the matrix is split into + * square blocks with side length 'SIDE'. First, the blocks are transposed, + * then a square transposition is done on each individual block. + */ +static void +squaretrans_pow2(mpd_uint_t *matrix, mpd_size_t size) +{ + mpd_uint_t buf1[SIDE*SIDE]; + mpd_uint_t buf2[SIDE*SIDE]; + mpd_uint_t *to, *from; + mpd_size_t b = size; + mpd_size_t r, c; + mpd_size_t i; + + while (b > SIDE) b >>= 1; + + for (r = 0; r < size; r += b) { + + for (c = r; c < size; c += b) { + + from = matrix + r*size + c; + to = buf1; + for (i = 0; i < b; i++) { + memcpy(to, from, b*(sizeof *to)); + from += size; + to += b; + } + squaretrans(buf1, b); + + if (r == c) { + to = matrix + r*size + c; + from = buf1; + for (i = 0; i < b; i++) { + memcpy(to, from, b*(sizeof *to)); + from += b; + to += size; + } + continue; + } + else { + from = matrix + c*size + r; + to = buf2; + for (i = 0; i < b; i++) { + memcpy(to, from, b*(sizeof *to)); + from += size; + to += b; + } + squaretrans(buf2, b); + + to = matrix + c*size + r; + from = buf1; + for (i = 0; i < b; i++) { + memcpy(to, from, b*(sizeof *to)); + from += b; + to += size; + } + + to = matrix + r*size + c; + from = buf2; + for (i = 0; i < b; i++) { + memcpy(to, from, b*(sizeof *to)); + from += b; + to += size; + } + } + } + } + +} + +/* + * In-place transposition of a 2^n x 2^n or a 2^n x (2*2^n) + * or a (2*2^n) x 2^n matrix. + */ +int +transpose_pow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols) +{ + mpd_size_t size = mul_size_t(rows, cols); + + assert(ispower2(rows)); + assert(ispower2(cols)); + + if (cols == rows) { + squaretrans_pow2(matrix, rows); + } + else if (cols == mul_size_t(2, rows)) { + if (!swap_halfrows_pow2(matrix, rows, cols, FORWARD_CYCLE)) { + return 0; + } + squaretrans_pow2(matrix, rows); + squaretrans_pow2(matrix+(size/2), rows); + } + else if (rows == mul_size_t(2, cols)) { + squaretrans_pow2(matrix, cols); + squaretrans_pow2(matrix+(size/2), cols); + if (!swap_halfrows_pow2(matrix, cols, rows, BACKWARD_CYCLE)) { + return 0; + } + } + else { + abort(); /* GCOV_NOT_REACHED */ + } + + return 1; +} + + diff --git a/third_party/opa/wasm/src/libmpdec/transpose.h b/third_party/opa/wasm/src/libmpdec/transpose.h new file mode 100644 index 000000000000..e1cd1fa17dd7 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/transpose.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef TRANSPOSE_H +#define TRANSPOSE_H + + +#include "mpdecimal.h" +#include + + +/* Internal header file: all symbols have local scope in the DSO */ +MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) + + +enum {FORWARD_CYCLE, BACKWARD_CYCLE}; + + +void std_trans(mpd_uint_t dest[], mpd_uint_t src[], mpd_size_t rows, mpd_size_t cols); +int transpose_pow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols); +void transpose_3xpow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols); + + +static inline void pointerswap(mpd_uint_t **a, mpd_uint_t **b) +{ + mpd_uint_t *tmp; + + tmp = *b; + *b = *a; + *a = tmp; +} + + +MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ + + +#endif diff --git a/third_party/opa/wasm/src/libmpdec/typearith.h b/third_party/opa/wasm/src/libmpdec/typearith.h new file mode 100644 index 000000000000..405237dac516 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/typearith.h @@ -0,0 +1,669 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef TYPEARITH_H +#define TYPEARITH_H + + +#include "mpdecimal.h" + + +/*****************************************************************************/ +/* Low level native arithmetic on basic types */ +/*****************************************************************************/ + + +/** ------------------------------------------------------------ + ** Double width multiplication and division + ** ------------------------------------------------------------ + */ + +#if defined(CONFIG_64) +#if defined(ANSI) +#if defined(HAVE_UINT128_T) +static inline void +_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) +{ + __uint128_t hl; + + hl = (__uint128_t)a * b; + + *hi = hl >> 64; + *lo = (mpd_uint_t)hl; +} + +static inline void +_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo, + mpd_uint_t d) +{ + __uint128_t hl; + + hl = ((__uint128_t)hi<<64) + lo; + *q = (mpd_uint_t)(hl / d); /* quotient is known to fit */ + *r = (mpd_uint_t)(hl - (__uint128_t)(*q) * d); +} +#else +static inline void +_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) +{ + uint32_t w[4], carry; + uint32_t ah, al, bh, bl; + uint64_t hl; + + ah = (uint32_t)(a>>32); al = (uint32_t)a; + bh = (uint32_t)(b>>32); bl = (uint32_t)b; + + hl = (uint64_t)al * bl; + w[0] = (uint32_t)hl; + carry = (uint32_t)(hl>>32); + + hl = (uint64_t)ah * bl + carry; + w[1] = (uint32_t)hl; + w[2] = (uint32_t)(hl>>32); + + hl = (uint64_t)al * bh + w[1]; + w[1] = (uint32_t)hl; + carry = (uint32_t)(hl>>32); + + hl = ((uint64_t)ah * bh + w[2]) + carry; + w[2] = (uint32_t)hl; + w[3] = (uint32_t)(hl>>32); + + *hi = ((uint64_t)w[3]<<32) + w[2]; + *lo = ((uint64_t)w[1]<<32) + w[0]; +} + +/* + * By Henry S. Warren: http://www.hackersdelight.org/HDcode/divlu.c.txt + * http://www.hackersdelight.org/permissions.htm: + * "You are free to use, copy, and distribute any of the code on this web + * site, whether modified by you or not. You need not give attribution." + * + * Slightly modified, comments are mine. + */ +static inline int +nlz(uint64_t x) +{ + int n; + + if (x == 0) return(64); + + n = 0; + if (x <= 0x00000000FFFFFFFF) {n = n +32; x = x <<32;} + if (x <= 0x0000FFFFFFFFFFFF) {n = n +16; x = x <<16;} + if (x <= 0x00FFFFFFFFFFFFFF) {n = n + 8; x = x << 8;} + if (x <= 0x0FFFFFFFFFFFFFFF) {n = n + 4; x = x << 4;} + if (x <= 0x3FFFFFFFFFFFFFFF) {n = n + 2; x = x << 2;} + if (x <= 0x7FFFFFFFFFFFFFFF) {n = n + 1;} + + return n; +} + +static inline void +_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t u1, mpd_uint_t u0, + mpd_uint_t v) +{ + const mpd_uint_t b = 4294967296; + mpd_uint_t un1, un0, + vn1, vn0, + q1, q0, + un32, un21, un10, + rhat, t; + int s; + + assert(u1 < v); + + s = nlz(v); + v = v << s; + vn1 = v >> 32; + vn0 = v & 0xFFFFFFFF; + + t = (s == 0) ? 0 : u0 >> (64 - s); + un32 = (u1 << s) | t; + un10 = u0 << s; + + un1 = un10 >> 32; + un0 = un10 & 0xFFFFFFFF; + + q1 = un32 / vn1; + rhat = un32 - q1*vn1; +again1: + if (q1 >= b || q1*vn0 > b*rhat + un1) { + q1 = q1 - 1; + rhat = rhat + vn1; + if (rhat < b) goto again1; + } + + /* + * Before again1 we had: + * (1) q1*vn1 + rhat = un32 + * (2) q1*vn1*b + rhat*b + un1 = un32*b + un1 + * + * The statements inside the if-clause do not change the value + * of the left-hand side of (2), and the loop is only exited + * if q1*vn0 <= rhat*b + un1, so: + * + * (3) q1*vn1*b + q1*vn0 <= un32*b + un1 + * (4) q1*v <= un32*b + un1 + * (5) 0 <= un32*b + un1 - q1*v + * + * By (5) we are certain that the possible add-back step from + * Knuth's algorithm D is never required. + * + * Since the final quotient is less than 2**64, the following + * must be true: + * + * (6) un32*b + un1 - q1*v <= UINT64_MAX + * + * This means that in the following line, the high words + * of un32*b and q1*v can be discarded without any effect + * on the result. + */ + un21 = un32*b + un1 - q1*v; + + q0 = un21 / vn1; + rhat = un21 - q0*vn1; +again2: + if (q0 >= b || q0*vn0 > b*rhat + un0) { + q0 = q0 - 1; + rhat = rhat + vn1; + if (rhat < b) goto again2; + } + + *q = q1*b + q0; + *r = (un21*b + un0 - q0*v) >> s; +} +#endif + +/* END ANSI */ +#elif defined(ASM) +static inline void +_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) +{ + mpd_uint_t h, l; + + __asm__ ( "mulq %3\n\t" + : "=d" (h), "=a" (l) + : "%a" (a), "rm" (b) + : "cc" + ); + + *hi = h; + *lo = l; +} + +static inline void +_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo, + mpd_uint_t d) +{ + mpd_uint_t qq, rr; + + __asm__ ( "divq %4\n\t" + : "=a" (qq), "=d" (rr) + : "a" (lo), "d" (hi), "rm" (d) + : "cc" + ); + + *q = qq; + *r = rr; +} +/* END GCC ASM */ +#elif defined(MASM) +#include +#pragma intrinsic(_umul128) + +static inline void +_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) +{ + *lo = _umul128(a, b, hi); +} + +void _mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo, + mpd_uint_t d); + +/* END MASM (_MSC_VER) */ +#else + #error "need platform specific 128 bit multiplication and division" +#endif + +#define DIVMOD(q, r, v, d) *q = v / d; *r = v - *q * d +static inline void +_mpd_divmod_pow10(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t v, mpd_uint_t exp) +{ + assert(exp <= 19); + + if (exp <= 9) { + if (exp <= 4) { + switch (exp) { + case 0: *q = v; *r = 0; break; + case 1: DIVMOD(q, r, v, 10UL); break; + case 2: DIVMOD(q, r, v, 100UL); break; + case 3: DIVMOD(q, r, v, 1000UL); break; + case 4: DIVMOD(q, r, v, 10000UL); break; + } + } + else { + switch (exp) { + case 5: DIVMOD(q, r, v, 100000UL); break; + case 6: DIVMOD(q, r, v, 1000000UL); break; + case 7: DIVMOD(q, r, v, 10000000UL); break; + case 8: DIVMOD(q, r, v, 100000000UL); break; + case 9: DIVMOD(q, r, v, 1000000000UL); break; + } + } + } + else { + if (exp <= 14) { + switch (exp) { + case 10: DIVMOD(q, r, v, 10000000000ULL); break; + case 11: DIVMOD(q, r, v, 100000000000ULL); break; + case 12: DIVMOD(q, r, v, 1000000000000ULL); break; + case 13: DIVMOD(q, r, v, 10000000000000ULL); break; + case 14: DIVMOD(q, r, v, 100000000000000ULL); break; + } + } + else { + switch (exp) { + case 15: DIVMOD(q, r, v, 1000000000000000ULL); break; + case 16: DIVMOD(q, r, v, 10000000000000000ULL); break; + case 17: DIVMOD(q, r, v, 100000000000000000ULL); break; + case 18: DIVMOD(q, r, v, 1000000000000000000ULL); break; + case 19: DIVMOD(q, r, v, 10000000000000000000ULL); break; /* GCOV_NOT_REACHED */ + } + } + } +} + +/* END CONFIG_64 */ +#elif defined(CONFIG_32) +#if defined(ANSI) +#if !defined(LEGACY_COMPILER) +static inline void +_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) +{ + mpd_uuint_t hl; + + hl = (mpd_uuint_t)a * b; + + *hi = hl >> 32; + *lo = (mpd_uint_t)hl; +} + +static inline void +_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo, + mpd_uint_t d) +{ + mpd_uuint_t hl; + + hl = ((mpd_uuint_t)hi<<32) + lo; + *q = (mpd_uint_t)(hl / d); /* quotient is known to fit */ + *r = (mpd_uint_t)(hl - (mpd_uuint_t)(*q) * d); +} +/* END ANSI + uint64_t */ +#else +static inline void +_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) +{ + uint16_t w[4], carry; + uint16_t ah, al, bh, bl; + uint32_t hl; + + ah = (uint16_t)(a>>16); al = (uint16_t)a; + bh = (uint16_t)(b>>16); bl = (uint16_t)b; + + hl = (uint32_t)al * bl; + w[0] = (uint16_t)hl; + carry = (uint16_t)(hl>>16); + + hl = (uint32_t)ah * bl + carry; + w[1] = (uint16_t)hl; + w[2] = (uint16_t)(hl>>16); + + hl = (uint32_t)al * bh + w[1]; + w[1] = (uint16_t)hl; + carry = (uint16_t)(hl>>16); + + hl = ((uint32_t)ah * bh + w[2]) + carry; + w[2] = (uint16_t)hl; + w[3] = (uint16_t)(hl>>16); + + *hi = ((uint32_t)w[3]<<16) + w[2]; + *lo = ((uint32_t)w[1]<<16) + w[0]; +} + +/* + * By Henry S. Warren: http://www.hackersdelight.org/HDcode/divlu.c.txt + * http://www.hackersdelight.org/permissions.htm: + * "You are free to use, copy, and distribute any of the code on this web + * site, whether modified by you or not. You need not give attribution." + * + * Slightly modified, comments are mine. + */ +static inline int +nlz(uint32_t x) +{ + int n; + + if (x == 0) return(32); + + n = 0; + if (x <= 0x0000FFFF) {n = n +16; x = x <<16;} + if (x <= 0x00FFFFFF) {n = n + 8; x = x << 8;} + if (x <= 0x0FFFFFFF) {n = n + 4; x = x << 4;} + if (x <= 0x3FFFFFFF) {n = n + 2; x = x << 2;} + if (x <= 0x7FFFFFFF) {n = n + 1;} + + return n; +} + +static inline void +_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t u1, mpd_uint_t u0, + mpd_uint_t v) +{ + const mpd_uint_t b = 65536; + mpd_uint_t un1, un0, + vn1, vn0, + q1, q0, + un32, un21, un10, + rhat, t; + int s; + + assert(u1 < v); + + s = nlz(v); + v = v << s; + vn1 = v >> 16; + vn0 = v & 0xFFFF; + + t = (s == 0) ? 0 : u0 >> (32 - s); + un32 = (u1 << s) | t; + un10 = u0 << s; + + un1 = un10 >> 16; + un0 = un10 & 0xFFFF; + + q1 = un32 / vn1; + rhat = un32 - q1*vn1; +again1: + if (q1 >= b || q1*vn0 > b*rhat + un1) { + q1 = q1 - 1; + rhat = rhat + vn1; + if (rhat < b) goto again1; + } + + /* + * Before again1 we had: + * (1) q1*vn1 + rhat = un32 + * (2) q1*vn1*b + rhat*b + un1 = un32*b + un1 + * + * The statements inside the if-clause do not change the value + * of the left-hand side of (2), and the loop is only exited + * if q1*vn0 <= rhat*b + un1, so: + * + * (3) q1*vn1*b + q1*vn0 <= un32*b + un1 + * (4) q1*v <= un32*b + un1 + * (5) 0 <= un32*b + un1 - q1*v + * + * By (5) we are certain that the possible add-back step from + * Knuth's algorithm D is never required. + * + * Since the final quotient is less than 2**32, the following + * must be true: + * + * (6) un32*b + un1 - q1*v <= UINT32_MAX + * + * This means that in the following line, the high words + * of un32*b and q1*v can be discarded without any effect + * on the result. + */ + un21 = un32*b + un1 - q1*v; + + q0 = un21 / vn1; + rhat = un21 - q0*vn1; +again2: + if (q0 >= b || q0*vn0 > b*rhat + un0) { + q0 = q0 - 1; + rhat = rhat + vn1; + if (rhat < b) goto again2; + } + + *q = q1*b + q0; + *r = (un21*b + un0 - q0*v) >> s; +} +#endif /* END ANSI + LEGACY_COMPILER */ + +/* END ANSI */ +#elif defined(ASM) +static inline void +_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) +{ + mpd_uint_t h, l; + + __asm__ ( "mull %3\n\t" + : "=d" (h), "=a" (l) + : "%a" (a), "rm" (b) + : "cc" + ); + + *hi = h; + *lo = l; +} + +static inline void +_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo, + mpd_uint_t d) +{ + mpd_uint_t qq, rr; + + __asm__ ( "divl %4\n\t" + : "=a" (qq), "=d" (rr) + : "a" (lo), "d" (hi), "rm" (d) + : "cc" + ); + + *q = qq; + *r = rr; +} +/* END GCC ASM */ +#elif defined(MASM) +static inline void __cdecl +_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) +{ + mpd_uint_t h, l; + + __asm { + mov eax, a + mul b + mov h, edx + mov l, eax + } + + *hi = h; + *lo = l; +} + +static inline void __cdecl +_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo, + mpd_uint_t d) +{ + mpd_uint_t qq, rr; + + __asm { + mov eax, lo + mov edx, hi + div d + mov qq, eax + mov rr, edx + } + + *q = qq; + *r = rr; +} +/* END MASM (_MSC_VER) */ +#else + #error "need platform specific 64 bit multiplication and division" +#endif + +#define DIVMOD(q, r, v, d) *q = v / d; *r = v - *q * d +static inline void +_mpd_divmod_pow10(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t v, mpd_uint_t exp) +{ + assert(exp <= 9); + + if (exp <= 4) { + switch (exp) { + case 0: *q = v; *r = 0; break; + case 1: DIVMOD(q, r, v, 10UL); break; + case 2: DIVMOD(q, r, v, 100UL); break; + case 3: DIVMOD(q, r, v, 1000UL); break; + case 4: DIVMOD(q, r, v, 10000UL); break; + } + } + else { + switch (exp) { + case 5: DIVMOD(q, r, v, 100000UL); break; + case 6: DIVMOD(q, r, v, 1000000UL); break; + case 7: DIVMOD(q, r, v, 10000000UL); break; + case 8: DIVMOD(q, r, v, 100000000UL); break; + case 9: DIVMOD(q, r, v, 1000000000UL); break; /* GCOV_NOT_REACHED */ + } + } +} +/* END CONFIG_32 */ + +/* NO CONFIG */ +#else + #error "define CONFIG_64 or CONFIG_32" +#endif /* CONFIG */ + + +static inline void +_mpd_div_word(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t v, mpd_uint_t d) +{ + *q = v / d; + *r = v - *q * d; +} + +static inline void +_mpd_idiv_word(mpd_ssize_t *q, mpd_ssize_t *r, mpd_ssize_t v, mpd_ssize_t d) +{ + *q = v / d; + *r = v - *q * d; +} + + +/** ------------------------------------------------------------ + ** Arithmetic with overflow checking + ** ------------------------------------------------------------ + */ + +/* The following macros do call exit() in case of an overflow. + If the library is used correctly (i.e. with valid context + parameters), such overflows cannot occur. The macros are used + as sanity checks in a couple of strategic places and should + be viewed as a handwritten version of gcc's -ftrapv option. */ + +static inline mpd_size_t +add_size_t(mpd_size_t a, mpd_size_t b) +{ + if (a > MPD_SIZE_MAX - b) { + mpd_err_fatal("add_size_t(): overflow: check the context"); /* GCOV_NOT_REACHED */ + } + return a + b; +} + +static inline mpd_size_t +sub_size_t(mpd_size_t a, mpd_size_t b) +{ + if (b > a) { + mpd_err_fatal("sub_size_t(): overflow: check the context"); /* GCOV_NOT_REACHED */ + } + return a - b; +} + +#if MPD_SIZE_MAX != MPD_UINT_MAX + #error "adapt mul_size_t() and mulmod_size_t()" +#endif + +static inline mpd_size_t +mul_size_t(mpd_size_t a, mpd_size_t b) +{ + mpd_uint_t hi, lo; + + _mpd_mul_words(&hi, &lo, (mpd_uint_t)a, (mpd_uint_t)b); + if (hi) { + mpd_err_fatal("mul_size_t(): overflow: check the context"); /* GCOV_NOT_REACHED */ + } + return lo; +} + +static inline mpd_size_t +add_size_t_overflow(mpd_size_t a, mpd_size_t b, mpd_size_t *overflow) +{ + mpd_size_t ret; + + *overflow = 0; + ret = a + b; + if (ret < a) *overflow = 1; + return ret; +} + +static inline mpd_size_t +mul_size_t_overflow(mpd_size_t a, mpd_size_t b, mpd_size_t *overflow) +{ + mpd_uint_t lo; + + _mpd_mul_words((mpd_uint_t *)overflow, &lo, (mpd_uint_t)a, + (mpd_uint_t)b); + return lo; +} + +static inline mpd_ssize_t +mod_mpd_ssize_t(mpd_ssize_t a, mpd_ssize_t m) +{ + mpd_ssize_t r = a % m; + return (r < 0) ? r + m : r; +} + +static inline mpd_size_t +mulmod_size_t(mpd_size_t a, mpd_size_t b, mpd_size_t m) +{ + mpd_uint_t hi, lo; + mpd_uint_t q, r; + + _mpd_mul_words(&hi, &lo, (mpd_uint_t)a, (mpd_uint_t)b); + _mpd_div_words(&q, &r, hi, lo, (mpd_uint_t)m); + + return r; +} + + +#endif /* TYPEARITH_H */ + + + diff --git a/third_party/opa/wasm/src/libmpdec/umodarith.h b/third_party/opa/wasm/src/libmpdec/umodarith.h new file mode 100644 index 000000000000..68d15188cb39 --- /dev/null +++ b/third_party/opa/wasm/src/libmpdec/umodarith.h @@ -0,0 +1,650 @@ +/* + * Copyright (c) 2008-2016 Stefan Krah. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#ifndef UMODARITH_H +#define UMODARITH_H + + +#include "constants.h" +#include "mpdecimal.h" +#include "typearith.h" + + +/* Bignum: Low level routines for unsigned modular arithmetic. These are + used in the fast convolution functions for very large coefficients. */ + + +/**************************************************************************/ +/* ANSI modular arithmetic */ +/**************************************************************************/ + + +/* + * Restrictions: a < m and b < m + * ACL2 proof: umodarith.lisp: addmod-correct + */ +static inline mpd_uint_t +addmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m) +{ + mpd_uint_t s; + + s = a + b; + s = (s < a) ? s - m : s; + s = (s >= m) ? s - m : s; + + return s; +} + +/* + * Restrictions: a < m and b < m + * ACL2 proof: umodarith.lisp: submod-2-correct + */ +static inline mpd_uint_t +submod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m) +{ + mpd_uint_t d; + + d = a - b; + d = (a < b) ? d + m : d; + + return d; +} + +/* + * Restrictions: a < 2m and b < 2m + * ACL2 proof: umodarith.lisp: section ext-submod + */ +static inline mpd_uint_t +ext_submod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m) +{ + mpd_uint_t d; + + a = (a >= m) ? a - m : a; + b = (b >= m) ? b - m : b; + + d = a - b; + d = (a < b) ? d + m : d; + + return d; +} + +/* + * Reduce double word modulo m. + * Restrictions: m != 0 + * ACL2 proof: umodarith.lisp: section dw-reduce + */ +static inline mpd_uint_t +dw_reduce(mpd_uint_t hi, mpd_uint_t lo, mpd_uint_t m) +{ + mpd_uint_t r1, r2, w; + + _mpd_div_word(&w, &r1, hi, m); + _mpd_div_words(&w, &r2, r1, lo, m); + + return r2; +} + +/* + * Subtract double word from a. + * Restrictions: a < m + * ACL2 proof: umodarith.lisp: section dw-submod + */ +static inline mpd_uint_t +dw_submod(mpd_uint_t a, mpd_uint_t hi, mpd_uint_t lo, mpd_uint_t m) +{ + mpd_uint_t d, r; + + r = dw_reduce(hi, lo, m); + d = a - r; + d = (a < r) ? d + m : d; + + return d; +} + +#ifdef CONFIG_64 + +/**************************************************************************/ +/* 64-bit modular arithmetic */ +/**************************************************************************/ + +/* + * A proof of the algorithm is in literature/mulmod-64.txt. An ACL2 + * proof is in umodarith.lisp: section "Fast modular reduction". + * + * Algorithm: calculate (a * b) % p: + * + * a) hi, lo <- a * b # Calculate a * b. + * + * b) hi, lo <- R(hi, lo) # Reduce modulo p. + * + * c) Repeat step b) until 0 <= hi * 2**64 + lo < 2*p. + * + * d) If the result is less than p, return lo. Otherwise return lo - p. + */ + +static inline mpd_uint_t +x64_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m) +{ + mpd_uint_t hi, lo, x, y; + + + _mpd_mul_words(&hi, &lo, a, b); + + if (m & (1ULL<<32)) { /* P1 */ + + /* first reduction */ + x = y = hi; + hi >>= 32; + + x = lo - x; + if (x > lo) hi--; + + y <<= 32; + lo = y + x; + if (lo < y) hi++; + + /* second reduction */ + x = y = hi; + hi >>= 32; + + x = lo - x; + if (x > lo) hi--; + + y <<= 32; + lo = y + x; + if (lo < y) hi++; + + return (hi || lo >= m ? lo - m : lo); + } + else if (m & (1ULL<<34)) { /* P2 */ + + /* first reduction */ + x = y = hi; + hi >>= 30; + + x = lo - x; + if (x > lo) hi--; + + y <<= 34; + lo = y + x; + if (lo < y) hi++; + + /* second reduction */ + x = y = hi; + hi >>= 30; + + x = lo - x; + if (x > lo) hi--; + + y <<= 34; + lo = y + x; + if (lo < y) hi++; + + /* third reduction */ + x = y = hi; + hi >>= 30; + + x = lo - x; + if (x > lo) hi--; + + y <<= 34; + lo = y + x; + if (lo < y) hi++; + + return (hi || lo >= m ? lo - m : lo); + } + else { /* P3 */ + + /* first reduction */ + x = y = hi; + hi >>= 24; + + x = lo - x; + if (x > lo) hi--; + + y <<= 40; + lo = y + x; + if (lo < y) hi++; + + /* second reduction */ + x = y = hi; + hi >>= 24; + + x = lo - x; + if (x > lo) hi--; + + y <<= 40; + lo = y + x; + if (lo < y) hi++; + + /* third reduction */ + x = y = hi; + hi >>= 24; + + x = lo - x; + if (x > lo) hi--; + + y <<= 40; + lo = y + x; + if (lo < y) hi++; + + return (hi || lo >= m ? lo - m : lo); + } +} + +static inline void +x64_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m) +{ + *a = x64_mulmod(*a, w, m); + *b = x64_mulmod(*b, w, m); +} + +static inline void +x64_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1, + mpd_uint_t m) +{ + *a0 = x64_mulmod(*a0, b0, m); + *a1 = x64_mulmod(*a1, b1, m); +} + +static inline mpd_uint_t +x64_powmod(mpd_uint_t base, mpd_uint_t exp, mpd_uint_t umod) +{ + mpd_uint_t r = 1; + + while (exp > 0) { + if (exp & 1) + r = x64_mulmod(r, base, umod); + base = x64_mulmod(base, base, umod); + exp >>= 1; + } + + return r; +} + +/* END CONFIG_64 */ +#else /* CONFIG_32 */ + + +/**************************************************************************/ +/* 32-bit modular arithmetic */ +/**************************************************************************/ + +#if defined(ANSI) +#if !defined(LEGACY_COMPILER) +/* HAVE_UINT64_T */ +static inline mpd_uint_t +std_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m) +{ + return ((mpd_uuint_t) a * b) % m; +} + +static inline void +std_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m) +{ + *a = ((mpd_uuint_t) *a * w) % m; + *b = ((mpd_uuint_t) *b * w) % m; +} + +static inline void +std_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1, + mpd_uint_t m) +{ + *a0 = ((mpd_uuint_t) *a0 * b0) % m; + *a1 = ((mpd_uuint_t) *a1 * b1) % m; +} +/* END HAVE_UINT64_T */ +#else +/* LEGACY_COMPILER */ +static inline mpd_uint_t +std_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m) +{ + mpd_uint_t hi, lo, q, r; + _mpd_mul_words(&hi, &lo, a, b); + _mpd_div_words(&q, &r, hi, lo, m); + return r; +} + +static inline void +std_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m) +{ + *a = std_mulmod(*a, w, m); + *b = std_mulmod(*b, w, m); +} + +static inline void +std_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1, + mpd_uint_t m) +{ + *a0 = std_mulmod(*a0, b0, m); + *a1 = std_mulmod(*a1, b1, m); +} +/* END LEGACY_COMPILER */ +#endif + +static inline mpd_uint_t +std_powmod(mpd_uint_t base, mpd_uint_t exp, mpd_uint_t umod) +{ + mpd_uint_t r = 1; + + while (exp > 0) { + if (exp & 1) + r = std_mulmod(r, base, umod); + base = std_mulmod(base, base, umod); + exp >>= 1; + } + + return r; +} +#endif /* ANSI CONFIG_32 */ + + +/**************************************************************************/ +/* Pentium Pro modular arithmetic */ +/**************************************************************************/ + +/* + * A proof of the algorithm is in literature/mulmod-ppro.txt. The FPU + * control word must be set to 64-bit precision and truncation mode + * prior to using these functions. + * + * Algorithm: calculate (a * b) % p: + * + * p := prime < 2**31 + * pinv := (long double)1.0 / p (precalculated) + * + * a) n = a * b # Calculate exact product. + * b) qest = n * pinv # Calculate estimate for q = n / p. + * c) q = (qest+2**63)-2**63 # Truncate qest to the exact quotient. + * d) r = n - q * p # Calculate remainder. + * + * Remarks: + * + * - p = dmod and pinv = dinvmod. + * - dinvmod points to an array of three uint32_t, which is interpreted + * as an 80 bit long double by fldt. + * - Intel compilers prior to version 11 do not seem to handle the + * __GNUC__ inline assembly correctly. + * - random tests are provided in tests/extended/ppro_mulmod.c + */ + +#if defined(PPRO) +#if defined(ASM) + +/* Return (a * b) % dmod */ +static inline mpd_uint_t +ppro_mulmod(mpd_uint_t a, mpd_uint_t b, double *dmod, uint32_t *dinvmod) +{ + mpd_uint_t retval; + + __asm__ ( + "fildl %2\n\t" + "fildl %1\n\t" + "fmulp %%st, %%st(1)\n\t" + "fldt (%4)\n\t" + "fmul %%st(1), %%st\n\t" + "flds %5\n\t" + "fadd %%st, %%st(1)\n\t" + "fsubrp %%st, %%st(1)\n\t" + "fldl (%3)\n\t" + "fmulp %%st, %%st(1)\n\t" + "fsubrp %%st, %%st(1)\n\t" + "fistpl %0\n\t" + : "=m" (retval) + : "m" (a), "m" (b), "r" (dmod), "r" (dinvmod), "m" (MPD_TWO63) + : "st", "memory" + ); + + return retval; +} + +/* + * Two modular multiplications in parallel: + * *a0 = (*a0 * w) % dmod + * *a1 = (*a1 * w) % dmod + */ +static inline void +ppro_mulmod2c(mpd_uint_t *a0, mpd_uint_t *a1, mpd_uint_t w, + double *dmod, uint32_t *dinvmod) +{ + __asm__ ( + "fildl %2\n\t" + "fildl (%1)\n\t" + "fmul %%st(1), %%st\n\t" + "fxch %%st(1)\n\t" + "fildl (%0)\n\t" + "fmulp %%st, %%st(1) \n\t" + "fldt (%4)\n\t" + "flds %5\n\t" + "fld %%st(2)\n\t" + "fmul %%st(2)\n\t" + "fadd %%st(1)\n\t" + "fsub %%st(1)\n\t" + "fmull (%3)\n\t" + "fsubrp %%st, %%st(3)\n\t" + "fxch %%st(2)\n\t" + "fistpl (%0)\n\t" + "fmul %%st(2)\n\t" + "fadd %%st(1)\n\t" + "fsubp %%st, %%st(1)\n\t" + "fmull (%3)\n\t" + "fsubrp %%st, %%st(1)\n\t" + "fistpl (%1)\n\t" + : : "r" (a0), "r" (a1), "m" (w), + "r" (dmod), "r" (dinvmod), + "m" (MPD_TWO63) + : "st", "memory" + ); +} + +/* + * Two modular multiplications in parallel: + * *a0 = (*a0 * b0) % dmod + * *a1 = (*a1 * b1) % dmod + */ +static inline void +ppro_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1, + double *dmod, uint32_t *dinvmod) +{ + __asm__ ( + "fildl %3\n\t" + "fildl (%2)\n\t" + "fmulp %%st, %%st(1)\n\t" + "fildl %1\n\t" + "fildl (%0)\n\t" + "fmulp %%st, %%st(1)\n\t" + "fldt (%5)\n\t" + "fld %%st(2)\n\t" + "fmul %%st(1), %%st\n\t" + "fxch %%st(1)\n\t" + "fmul %%st(2), %%st\n\t" + "flds %6\n\t" + "fldl (%4)\n\t" + "fxch %%st(3)\n\t" + "fadd %%st(1), %%st\n\t" + "fxch %%st(2)\n\t" + "fadd %%st(1), %%st\n\t" + "fxch %%st(2)\n\t" + "fsub %%st(1), %%st\n\t" + "fxch %%st(2)\n\t" + "fsubp %%st, %%st(1)\n\t" + "fxch %%st(1)\n\t" + "fmul %%st(2), %%st\n\t" + "fxch %%st(1)\n\t" + "fmulp %%st, %%st(2)\n\t" + "fsubrp %%st, %%st(3)\n\t" + "fsubrp %%st, %%st(1)\n\t" + "fxch %%st(1)\n\t" + "fistpl (%2)\n\t" + "fistpl (%0)\n\t" + : : "r" (a0), "m" (b0), "r" (a1), "m" (b1), + "r" (dmod), "r" (dinvmod), + "m" (MPD_TWO63) + : "st", "memory" + ); +} +/* END PPRO GCC ASM */ +#elif defined(MASM) + +/* Return (a * b) % dmod */ +static inline mpd_uint_t __cdecl +ppro_mulmod(mpd_uint_t a, mpd_uint_t b, double *dmod, uint32_t *dinvmod) +{ + mpd_uint_t retval; + + __asm { + mov eax, dinvmod + mov edx, dmod + fild b + fild a + fmulp st(1), st + fld TBYTE PTR [eax] + fmul st, st(1) + fld MPD_TWO63 + fadd st(1), st + fsubp st(1), st + fld QWORD PTR [edx] + fmulp st(1), st + fsubp st(1), st + fistp retval + } + + return retval; +} + +/* + * Two modular multiplications in parallel: + * *a0 = (*a0 * w) % dmod + * *a1 = (*a1 * w) % dmod + */ +static inline mpd_uint_t __cdecl +ppro_mulmod2c(mpd_uint_t *a0, mpd_uint_t *a1, mpd_uint_t w, + double *dmod, uint32_t *dinvmod) +{ + __asm { + mov ecx, dmod + mov edx, a1 + mov ebx, dinvmod + mov eax, a0 + fild w + fild DWORD PTR [edx] + fmul st, st(1) + fxch st(1) + fild DWORD PTR [eax] + fmulp st(1), st + fld TBYTE PTR [ebx] + fld MPD_TWO63 + fld st(2) + fmul st, st(2) + fadd st, st(1) + fsub st, st(1) + fmul QWORD PTR [ecx] + fsubp st(3), st + fxch st(2) + fistp DWORD PTR [eax] + fmul st, st(2) + fadd st, st(1) + fsubrp st(1), st + fmul QWORD PTR [ecx] + fsubp st(1), st + fistp DWORD PTR [edx] + } +} + +/* + * Two modular multiplications in parallel: + * *a0 = (*a0 * b0) % dmod + * *a1 = (*a1 * b1) % dmod + */ +static inline void __cdecl +ppro_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1, + double *dmod, uint32_t *dinvmod) +{ + __asm { + mov ecx, dmod + mov edx, a1 + mov ebx, dinvmod + mov eax, a0 + fild b1 + fild DWORD PTR [edx] + fmulp st(1), st + fild b0 + fild DWORD PTR [eax] + fmulp st(1), st + fld TBYTE PTR [ebx] + fld st(2) + fmul st, st(1) + fxch st(1) + fmul st, st(2) + fld DWORD PTR MPD_TWO63 + fld QWORD PTR [ecx] + fxch st(3) + fadd st, st(1) + fxch st(2) + fadd st, st(1) + fxch st(2) + fsub st, st(1) + fxch st(2) + fsubrp st(1), st + fxch st(1) + fmul st, st(2) + fxch st(1) + fmulp st(2), st + fsubp st(3), st + fsubp st(1), st + fxch st(1) + fistp DWORD PTR [edx] + fistp DWORD PTR [eax] + } +} +#endif /* PPRO MASM (_MSC_VER) */ + + +/* Return (base ** exp) % dmod */ +static inline mpd_uint_t +ppro_powmod(mpd_uint_t base, mpd_uint_t exp, double *dmod, uint32_t *dinvmod) +{ + mpd_uint_t r = 1; + + while (exp > 0) { + if (exp & 1) + r = ppro_mulmod(r, base, dmod, dinvmod); + base = ppro_mulmod(base, base, dmod, dinvmod); + exp >>= 1; + } + + return r; +} +#endif /* PPRO */ +#endif /* CONFIG_32 */ + + +#endif /* UMODARITH_H */ + + + diff --git a/third_party/opa/wasm/src/malloc.c b/third_party/opa/wasm/src/malloc.c new file mode 100644 index 000000000000..17194f836542 --- /dev/null +++ b/third_party/opa/wasm/src/malloc.c @@ -0,0 +1,720 @@ +#include +#include "std.h" +#include "stdlib.h" + +#define WASM_PAGE_SIZE (65536) + +#define ARRAY_SIZE(ARRAY) (sizeof(ARRAY) / sizeof((ARRAY)[0])) + +static unsigned int heap_ptr; +static unsigned int heap_top; +extern unsigned char __heap_base; // set by lld +static void *builtin_cache[8]; + +struct heap_block { + size_t size; + struct heap_block *prev; // unset if block allocated + struct heap_block *next; // unset if block allocated + unsigned char data[0]; +}; + +// free blocks, ordered per their memory address. +struct heap_blocks { + bool fixed_size; + size_t size; // if fixed size, this indicates the block size. + // if not fixed, this indicates the minimum block size. + struct heap_block start; + struct heap_block end; +}; + +// all the free blocks: fixed size blocks of 4, 8, 16 and 64 bytes and then one free +// list for varying sized blocks of 128 bytes or more. +static struct heap_blocks heap_free[5] = { + {true, 4}, + {true, 8}, + {true, 16}, + {true, 64}, + {false, 128}, +}; + +static struct heap_blocks heap_stash[5] = { + {true, 4}, + {true, 8}, + {true, 16}, + {true, 64}, + {false, 128}, +}; + + +/* + * Currently, there is one variable sized blocklist. If there were more, + * we'd need to track each one here in heap_bulk_blocks. + */ +#define VARIABLE_SIZED_BLOCK_IDX (ARRAY_SIZE(heap_free)-1) +static struct heap_blocks heap_bulk_blocks = { false, 128 }; +static bool variable_block_update_required = false; + + +#ifdef DEBUG +#define HEAP_CHECK(blocks) heap_check(__FUNCTION__, blocks) +#else +#define HEAP_CHECK(blocks) +#endif + +static void init_free() +{ + for (int i = 0; i < ARRAY_SIZE(heap_free); i++) { + heap_free[i].start = (struct heap_block) { 0, NULL, &heap_free[i].end }; + heap_free[i].end = (struct heap_block) { 0, &heap_free[i].start, NULL }; + } + heap_bulk_blocks.start = (struct heap_block) { 0, NULL, &heap_bulk_blocks.end }; + heap_bulk_blocks.end = (struct heap_block) { 0, &heap_bulk_blocks.start, NULL }; + variable_block_update_required = false; + + for (int i = 0; i < ARRAY_SIZE(builtin_cache); i++) + { + builtin_cache[i] = NULL; + } +} + +static void init_stash() +{ + for (int i = 0; i < ARRAY_SIZE(heap_stash); i++) { + heap_stash[i].start = + (struct heap_block) { 0, NULL, &heap_stash[i].end }; + heap_stash[i].end = + (struct heap_block) { 0, &heap_stash[i].start, NULL }; + } +} + +static void heap_check(const char *name, struct heap_blocks *blocks) +{ + struct heap_block *start = &blocks->start; + struct heap_block *end = &blocks->end; + + for (struct heap_block *b = start->next, *prev = start; b != end; prev = b, b = b->next) { + if (prev == NULL || b == NULL || b->prev != prev) { + opa_abort(name); + } + } + + for (struct heap_block *b = end->prev, *next = end; b != start; next = b, b = b->prev) { + if (next == NULL || b == NULL || b->next != next) { + opa_abort(name); + } + } +} + + +// NOTE(sr): In internal/compiler/wasm, we append segments to the data section. +// Since our memory layout is +// | <-- stack | -- data -- | heap --> | +// we need to adjust the border between data and heap, i.e., where the heap +// starts. When initializing a module, the Start function emitted by the +// compiler will call this function with the new heap base. +OPA_INTERNAL +void opa_malloc_init(unsigned int heap_base) +{ + heap_ptr = heap_base; + heap_top = __builtin_wasm_memory_grow(0, 0) * WASM_PAGE_SIZE; + init_free(); + init_stash(); +} + +void opa_malloc_init_test(void) +{ + opa_malloc_init(__heap_base); +} + +static struct heap_block * __opa_malloc_reuse_fixed(struct heap_blocks *blocks); +static struct heap_block * __opa_malloc_reuse_varying(struct heap_blocks *blocks, size_t size); +static void move_blocks(struct heap_blocks *dst, struct heap_blocks *src); +void opa_free_bulk_commit(void); + +WASM_EXPORT(opa_heap_ptr_get) +unsigned int opa_heap_ptr_get(void) +{ + return heap_ptr; +} + +unsigned int opa_heap_top_get(void) +{ + return heap_top; +} + +WASM_EXPORT(opa_heap_ptr_set) +void opa_heap_ptr_set(unsigned int ptr) +{ + heap_ptr = ptr; + init_free(); +} + +OPA_INTERNAL +void move_freelists(struct heap_blocks *dst_block_list, + struct heap_blocks *src_block_list, + const char *caller_fail_msg) +{ + /* + * First verify that dst freelists are empty and + * all blocks in the src are below the current heap pointer. + */ + for (int i = 0; i < ARRAY_SIZE(heap_free); i++) + { + struct heap_blocks *dst = &dst_block_list[i]; + struct heap_blocks *src = &src_block_list[i]; + + if (dst->start.next != &dst->end || dst->end.prev != &dst->start) + opa_abort(caller_fail_msg); + + if (src->end.prev != &src->start) { + struct heap_block *b = src->end.prev; + if ((unsigned int)b + b->size + sizeof(struct heap_block) > heap_ptr) + opa_abort(caller_fail_msg); + } + } + + /* Now move the blocks en masse from one freelist to the other. */ + for (int i = 0; i < ARRAY_SIZE(heap_free); i++) + move_blocks(&dst_block_list[i], &src_block_list[i]); +} + +WASM_EXPORT(opa_heap_blocks_stash) +void opa_heap_blocks_stash(void) +{ + move_freelists(heap_stash, heap_free, + "opa_heap_blocks_stash() consistency check failed"); + /* clean up dangling references */ + init_free(); +} + +WASM_EXPORT(opa_heap_stash_clear) +void opa_heap_stash_clear(void) +{ + init_stash(); +} + +WASM_EXPORT(opa_heap_blocks_restore) +void opa_heap_blocks_restore(void) +{ + move_freelists(heap_free, heap_stash, + "opa_heap_blocks_restore() consistency check failed"); + /* clean up dangling references */ + init_stash(); +} + +void opa_heap_top_set(unsigned int top) +{ + heap_top = top; + init_free(); +} + +// returns the free list applicable for the requested size. +static struct heap_blocks * __opa_blocks(size_t size) { + for (int i = 0; i < ARRAY_SIZE(heap_free)-1; i++) { + struct heap_blocks *candidate = &heap_free[i]; + + if (size <= candidate->size) + { + return candidate; + } + } + + return &heap_free[ARRAY_SIZE(heap_free)-1]; +} + +static void *__opa_malloc_new_allocation(size_t size) +{ + unsigned int ptr = heap_ptr; + size_t block_size = sizeof(struct heap_block) + size; + heap_ptr += block_size; + + if (heap_ptr >= heap_top) + { + unsigned int pages = (block_size / WASM_PAGE_SIZE) + 1; + if (__builtin_wasm_memory_grow(0, pages) == -1 ) + { + opa_abort("opa_malloc: failed"); + }; + heap_top += (pages * WASM_PAGE_SIZE); + } + + struct heap_block *b = (void *)ptr; + b->size = size; + b->prev = NULL; + b->next = NULL; + + return b->data; +} + +WASM_EXPORT(opa_malloc) +void *opa_malloc(size_t size) +{ + // Look for the first free block that is large enough. Split the found block if necessary. + + struct heap_blocks *blocks = __opa_blocks(size); + HEAP_CHECK(blocks); + + struct heap_block *b = blocks->fixed_size ? + __opa_malloc_reuse_fixed(blocks) : __opa_malloc_reuse_varying(blocks, size); + if (b != NULL) + { + return b->data; + } + + // Allocate a new block. + + if (blocks->fixed_size) + { + size = blocks->size; + } + + return __opa_malloc_new_allocation(size); +} + +// returns a free block from the list, if available. +static struct heap_block * __opa_malloc_reuse_fixed(struct heap_blocks *blocks) +{ + struct heap_block *end = &blocks->end; + struct heap_block *b = blocks->start.next; + + if (b != end) + { + b->prev->next = b->next; + b->next->prev = b->prev; + b->prev = NULL; + b->next = NULL; + + HEAP_CHECK(blocks); + + return b; + } + + return NULL; +} + +// finds a free block at least of given size, splitting the found block if the remaining block exceeds the minimum size. +static struct heap_block * __opa_malloc_reuse_varying(struct heap_blocks *blocks, size_t size) +{ + struct heap_block *start = &blocks->start; + struct heap_block *end = &blocks->end; + size_t min_size = blocks->size; + + if (variable_block_update_required) + opa_free_bulk_commit(); + + for (struct heap_block *b = start->next; b != end; b = b->next) + { + if (b->size >= (sizeof(struct heap_block) + min_size + size)) + { + struct heap_block *remaining = (void *)(&b->data[0]) + size; + remaining->size = b->size - (sizeof(struct heap_block) + size); + remaining->prev = b->prev; + remaining->next = b->next; + remaining->prev->next = remaining; + remaining->next->prev = remaining; + + b->size = size; + b->prev = NULL; + b->next = NULL; + + HEAP_CHECK(blocks); + + return b; + } else if (b->size >= size) + { + b->prev->next = b->next; + b->next->prev = b->prev; + b->prev = NULL; + b->next = NULL; + + HEAP_CHECK(blocks); + + return b; + } + } + return NULL; +} + +WASM_EXPORT(opa_free) +void opa_free(void *ptr) +{ + struct heap_block *block = ptr - sizeof(struct heap_block); + +#ifdef DEBUG + if (ptr == NULL) + { + opa_abort("opa_free: null pointer"); + } + + if (block->prev != NULL || block->next != NULL) + { + opa_abort("opa_free: double free"); + } +#endif + + struct heap_blocks *blocks = __opa_blocks(block->size); + struct heap_block *start = &blocks->start; + struct heap_block *end = &blocks->end; + bool fixed_size = blocks->fixed_size; + + HEAP_CHECK(blocks); + + // Find the free block available just before this block and try to + // defragment, by trying to merge with this block with the found + // block and the one after. + + struct heap_block *prev = start; + + if (!fixed_size) + { + for (struct heap_block *b = prev->next; b < block && b != end; prev = b, b = b->next); + + struct heap_block *prev_end = (void *)(&prev->data[0]) + prev->size; + struct heap_block *block_end = (void *)(&block->data[0]) + block->size; + + if (prev_end == block) + { + prev->size += sizeof(struct heap_block) + block->size; + prev_end = (void *)(&prev->data[0]) + prev->size; + if (prev_end == prev->next) { + struct heap_block *next = prev->next; + prev->size += sizeof(struct heap_block) + next->size; + prev->next = next->next; + prev->next->prev = prev; + } + return; + } + + if (block_end == prev->next) + { + struct heap_block *next = prev->next; + block->prev = prev; + block->next = next->next; + block->size += sizeof(struct heap_block) + next->size; + + prev->next = block; + block->next->prev = block; + return; + } + } + + // List the block as free. + + block->prev = prev; + block->next = prev->next; + prev->next = block; + block->next->prev = block; +} + +void *opa_realloc(void *ptr, size_t size) +{ + struct heap_block *block = ptr - sizeof(struct heap_block); + void *p = opa_malloc(size); + + memcpy(p, ptr, block->size < size ? block->size : size); + opa_free(ptr); + return p; +} + +static void **__opa_builtin_cache(size_t i) +{ + if (i >= ARRAY_SIZE(builtin_cache)) + { + opa_abort("opa_malloc: illegal builtin cache index"); + } + + return &builtin_cache[i]; +} + +void *opa_builtin_cache_get(size_t i) +{ + return *__opa_builtin_cache(i); +} + +void opa_builtin_cache_set(size_t i, void *p) +{ + *__opa_builtin_cache(i) = p; +} + +// Count the number of free blocks. This is for testing only. +size_t opa_heap_free_blocks(void) +{ + size_t blocks1 = 0, blocks2 = 0; + + for (int i = 0; i < ARRAY_SIZE(heap_free); i++) + { + for (struct heap_block *b = heap_free[i].start.next; b != &heap_free[i].end; b = b->next, blocks1++); + for (struct heap_block *b = heap_free[i].end.prev; b != &heap_free[i].start; b = b->prev, blocks2++); + + if (blocks1 != blocks2) + { + opa_abort("opa_malloc: corrupted heap"); + } + + HEAP_CHECK(&heap_free[i]); + } + + return blocks1; +} + +static bool blocks_empty(struct heap_blocks *blocks) +{ + return blocks->start.next == &blocks->end; +} + +static void init_blocks(struct heap_blocks *blocks) +{ + blocks->start = (struct heap_block) { 0, NULL, &blocks->end }; + blocks->end = (struct heap_block) { 0, &blocks->start, NULL }; +} + +static void remove_block(struct heap_block *block) +{ + block->prev->next = block->next; + block->next->prev = block->prev; + block->prev = NULL; + block->next = NULL; +} + +static void append_block(struct heap_blocks *blocks, struct heap_block *block) +{ + block->prev = blocks->end.prev; + block->next = &blocks->end; + block->prev->next = block; + block->next->prev = block; +} + +static void prepend_block(struct heap_blocks *blocks, struct heap_block *block) +{ + block->prev = &blocks->start; + block->next = blocks->start.next; + block->prev->next = block; + block->next->prev = block; +} + +static void move_blocks(struct heap_blocks *dst, struct heap_blocks *src) +{ + dst->start.prev = NULL; /* unnecessary, but safe */ + dst->start.next = src->start.next; + dst->start.next->prev = &dst->start; + + dst->end.prev = src->end.prev; + dst->end.next = NULL; /* unnecessary, but safe */ + dst->end.prev->next = &dst->end; + + /* Fix dangling references in src for consistency */ + src->start.next = &src->end; + src->end.prev = &src->start; +} + +static void merge_or_append_block(struct heap_blocks *dst, struct heap_block *block) +{ + struct heap_block *last = dst->end.prev; + struct heap_block *last_end = (void *)(&last->data[0]) + last->size; + if (last != &dst->start && last_end == block) + last->size += sizeof(struct heap_block) + block->size; + else + append_block(dst, block); +} + +static void merge_or_append_blocks(struct heap_blocks *dst, struct heap_blocks *src) +{ + while (!blocks_empty(src)) + { + struct heap_block *block = src->start.next; + remove_block(block); + merge_or_append_block(dst, block); + } +} + +/* + * Assumes list1 and list2 are in order. Merge them into dst in order. + * dst, list1 and list2 must all be different block lists. Assumes dst has + * been initialized. + * + * As a special case, if two blocks being merged are adjacent, combine them + * into a single block. + */ +static void merge_blocks(struct heap_blocks *dst, struct heap_blocks *list1, + struct heap_blocks *list2) +{ + while (!blocks_empty(list1) && !blocks_empty(list2)) { + struct heap_block *b1 = list1->start.next; + struct heap_block *b2 = list2->start.next; + struct heap_block *min = (unsigned int)b1 < (unsigned int)b2 ? b1 : b2; + remove_block(min); + merge_or_append_block(dst, min); + } + + /* at most one list still has blocks */ + if (!blocks_empty(list1)) + merge_or_append_blocks(dst, list1); + else + merge_or_append_blocks(dst, list2); +} + +/* + * Split a list of blocks into two by alternately appending the blocks + * to two separate block lists. Assumes dst[0] and dst[1] have been initialized. + */ +static void split_blocks(struct heap_blocks dst[2], struct heap_blocks *src) +{ + unsigned int i = 0; + while (!blocks_empty(src)) { + struct heap_block *block = src->start.next; + remove_block(block); + append_block(&dst[i], block); + i ^= 1; + } +} + +/* Merge sort the blocks on a list in ascending address order */ +void merge_sort_blocks(struct heap_blocks *blocks) +{ + struct heap_blocks hold[2]; + struct heap_block *first; + struct heap_block *second; + + /* list length == 0: done */ + if (blocks_empty(blocks)) + return; + + first = blocks->start.next; + second = first->next; + + /* list length == 1: done */ + if (second == &blocks->end) + return; + + /* list length == 2: optimization -- fast block swap+merge */ + if (second->next == &blocks->end) + { + if ((unsigned int)first > (unsigned int)second) + { + remove_block(first); + /* blocks now just has 'second' to which we append or merge 'first' */ + merge_or_append_block(blocks, first); + } + /* first and second are in order. See if we can merge them. */ + else if (((void *)(&first->data[0]) + first->size == second)) + { + remove_block(second); + first->size += sizeof(struct heap_block) + second->size; + } + + /* one way or the other, we're done */ + return; + } + + /* list length > 2: recursive case */ + for (int i = 0; i < 2; i++) + init_blocks(&hold[i]); + split_blocks(hold, blocks); + merge_sort_blocks(&hold[0]); + merge_sort_blocks(&hold[1]); + merge_blocks(blocks, &hold[0], &hold[1]); +} + +static void block_order_check(struct heap_blocks *blocks) +{ + struct heap_block *b; + struct heap_block *prev; + struct heap_block *prev_end; + + for (prev = NULL, b = blocks->start.next ; b != &blocks->end; prev = b, b = b->next) + { + if (prev == NULL) + continue; + prev_end = (void *)(&prev->data[0]) + prev->size; + if ((unsigned int)prev >= (unsigned int)b) + opa_abort("block_order_check() out of order blocks detected"); + if (prev_end > b) + opa_abort("block_order_check() overlapping blocks detected"); + if (!blocks->fixed_size && prev_end == b) + opa_abort("block_order_check() unmerged block detected"); + } +} + +#ifndef DEBUG +#define BLOCK_ORDER_CHECK(blocks) +#else /* DEBUG */ +#define BLOCK_ORDER_CHECK(blocks) block_order_check(blocks) +#endif /* DEBUG */ + +/* + * Save heap blocks to temporary block lists in arbitrary order. + * Later, in opa_free_bulk_commit() release them correctly back to the heap. + */ +void opa_free_bulk(void *ptr) +{ + struct heap_block *block = ptr - sizeof(struct heap_block); + struct heap_blocks *blocks = __opa_blocks(block->size); + +#ifdef DEBUG + if (ptr == NULL) + { + opa_abort("opa_free_bulk: null pointer"); + } + + if (block->prev != NULL || block->next != NULL) + { + opa_abort("opa_free_bulk: double free"); + } +#endif + + if (blocks->fixed_size) { + prepend_block(blocks, block); + HEAP_CHECK(blocks); + } else { + prepend_block(&heap_bulk_blocks, block); + HEAP_CHECK(&heap_bulk_blocks); + variable_block_update_required = true; + } +} + +/* + * Return the variable-sized blocks released by opa_free_bulk() to the heap. + * opa_free_bulk() placed the blocks on a list but disregarded + * address order unlike what is done in the heap. This makes freeing K objects + * take O(K) time. Now, to return them to the heap, we need to put them + * in address order along with the other blocks on the heap. This takes + * O(K log K) time where K == N + . + * + * This is in contrast to iterative calls to opa_free(). Each call to opa_free() + * takes a worst-case of O(N) time due to the time it takes to linearly insert + * the block into the list. Calling opa_free() iteratively over N objects, + * threfore, takes time that grows in O(N^2). (Even with an empty freelist, + * the average length of the search is O(N/2)). + * + * This function should generally be private. However it is exposed in the + * malloc.h header in case there is a specific desire to ensure predictability + * of allocation time after some bulk free operations. It is also useful + * for tests. + */ +void opa_free_bulk_commit(void) +{ + struct heap_blocks *var_blocks = &heap_free[VARIABLE_SIZED_BLOCK_IDX]; + struct heap_blocks hold; + + merge_sort_blocks(&heap_bulk_blocks); + BLOCK_ORDER_CHECK(&heap_bulk_blocks); + + /* + * Need to move variable blocks to new list because merge_blocks() + * expects three distinct lists. + */ + init_blocks(&hold); + move_blocks(&hold, var_blocks); + merge_blocks(var_blocks, &heap_bulk_blocks, &hold); + +#ifdef DEBUG + if (!blocks_empty(&heap_bulk_blocks)) + opa_abort("Unmerged bulk blocks in heap_bulk_blocks"); + if (!blocks_empty(&hold)) + opa_abort("Unmerged heap blocks in heap_bulk_blocks"); +#endif /* DEBUG */ + HEAP_CHECK(var_blocks); + BLOCK_ORDER_CHECK(var_blocks); + + variable_block_update_required = false; +} diff --git a/third_party/opa/wasm/src/malloc.h b/third_party/opa/wasm/src/malloc.h new file mode 100644 index 000000000000..3169f425f147 --- /dev/null +++ b/third_party/opa/wasm/src/malloc.h @@ -0,0 +1,38 @@ +#ifndef OPA_MALLOC_H +#define OPA_MALLOC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void *opa_malloc(size_t size); +void opa_free(void *ptr); +void *opa_realloc(void *ptr, size_t size); +void opa_free_bulk(void *ptr); +void opa_free_bulk_commit(void); + +unsigned int opa_heap_ptr_get(void); +unsigned int opa_heap_top_get(void); +void opa_heap_ptr_set(unsigned int); +void opa_heap_top_set(unsigned int); + +void opa_malloc_init(unsigned int); + +void opa_malloc_init_test(void); + +void *opa_builtin_cache_get(size_t i); +void opa_builtin_cache_set(size_t i, void *p); + +size_t opa_heap_free_blocks(void); + +void opa_heap_blocks_stash(void); +void opa_heap_blocks_restore(void); +void opa_heap_stash_clear(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/memoize.c b/third_party/opa/wasm/src/memoize.c new file mode 100644 index 000000000000..a9ddb0a1e634 --- /dev/null +++ b/third_party/opa/wasm/src/memoize.c @@ -0,0 +1,57 @@ +#include "memoize.h" +#include "malloc.h" +#include "std.h" + +struct memoize { + struct memoize *prev; + opa_object_t *table; +}; + +static struct memoize *m = NULL; + +struct memoize *opa_memoize_alloc(struct memoize *prev) +{ + struct memoize *result = (struct memoize *)opa_malloc(sizeof(struct memoize)); + result->prev = prev; + result->table = opa_cast_object(opa_object()); + return result; +} + +OPA_INTERNAL +void opa_memoize_init(void) +{ + m = opa_memoize_alloc(NULL); +} + +OPA_INTERNAL +void opa_memoize_push(void) +{ + m = opa_memoize_alloc(m); +} + +OPA_INTERNAL +void opa_memoize_pop(void) +{ + // NOTE(tsandall): free() is not called because we assume the heap will be + // reset on the next eval() call. + m = m->prev; +} + +OPA_INTERNAL +void opa_memoize_insert(int32_t index, opa_value *value) +{ + // NOTE(tsandall): allocating a number is suboptimal but worst-case is ~1 per + // planned rule so overhead should be minimal compared to the rest of evaluation. + opa_value *key = opa_number_int(index); + opa_object_insert(m->table, key, value); +} + +OPA_INTERNAL +opa_value *opa_memoize_get(int32_t index) +{ + opa_number_t key; + opa_number_init_int(&key, index); + opa_object_elem_t *elem = opa_object_get(m->table, &key.hdr); + + return elem == NULL ? NULL : elem->v; +} diff --git a/third_party/opa/wasm/src/memoize.h b/third_party/opa/wasm/src/memoize.h new file mode 100644 index 000000000000..c158d6cbc95f --- /dev/null +++ b/third_party/opa/wasm/src/memoize.h @@ -0,0 +1,12 @@ +#ifndef OPA_MEMOIZE_H +#define OPA_MEMOIZE_H + +#include "value.h" + +void opa_memoize_init(void); +void opa_memoize_push(void); +void opa_memoize_pop(void); +void opa_memoize_insert(int32_t, opa_value *); +opa_value *opa_memoize_get(int32_t); + +#endif \ No newline at end of file diff --git a/third_party/opa/wasm/src/mpd.c b/third_party/opa/wasm/src/mpd.c new file mode 100644 index 000000000000..8642dc021c6f --- /dev/null +++ b/third_party/opa/wasm/src/mpd.c @@ -0,0 +1,542 @@ +#include + +#include "str.h" +#include "value.h" + +static int initialized; +static mpd_context_t default_ctx; +static mpd_context_t max_ctx; +static mpd_t *one; + +OPA_INTERNAL +void opa_mpd_init(void) +{ + if (!initialized) + { + mpd_defaultcontext(&default_ctx); + default_ctx.traps = 0; + + mpd_maxcontext(&max_ctx); + max_ctx.traps = 0; + max_ctx.round = MPD_ROUND_HALF_UP; // .5 always rounded up + + one = mpd_qnew(); + + uint32_t status = 0; + mpd_qset_i32(one, 1, &max_ctx, &status); + if (status) + { + opa_abort("mpd: init"); + } + + initialized = 1; + } +} + +mpd_context_t *mpd_default_ctx(void) +{ + return &default_ctx; +} + +mpd_context_t *mpd_max_ctx(void) +{ + return &max_ctx; +} + +static mpd_t *mpd_one(void) +{ + return one; +} + +void opa_mpd_del(mpd_t *v) +{ + if (v != NULL) + { + mpd_del(v); + } +} + +mpd_t *opa_number_to_bf(opa_value *v) +{ + if (opa_value_type(v) != OPA_NUMBER) + { + return NULL; + } + + opa_number_t *n = opa_cast_number(v); + mpd_t *r = NULL; + uint32_t status = 0; + + switch (n->repr) + { + case OPA_NUMBER_REPR_REF: + r = mpd_qnew(); + + // Guarantee the existence of '\0' terminator. The string may + // be pointer to a longer buffer (allocated in JSON parsing). + char *s = malloc(n->v.ref.len+1); + memcpy(s, n->v.ref.s, n->v.ref.len); + s[n->v.ref.len] = 0; + mpd_qset_string(r, s, mpd_max_ctx(), &status); + if (status != 0) + { + opa_abort("opa_number_to_bf: invalid number"); + } + + free(s); + break; + + case OPA_NUMBER_REPR_INT: + r = mpd_qnew(); + + if (n->v.i >= INT32_MIN && n->v.i <= INT32_MAX) + { + mpd_qset_i32(r, (int32_t)n->v.i, mpd_default_ctx(), &status); + } else { + char buf[32]; // PRINTF_NTOA_BUFFER_SIZE + if (snprintf(buf, sizeof(buf), "%d", n->v.i) == sizeof(buf)) + { + opa_abort("opa_number_to_bf: overflow"); + } + + r = mpd_qnew(); + mpd_qset_string(r, buf, mpd_default_ctx(), &status); + } + break; + + default: + opa_abort("opa_number_to_bf: illegal repr"); + return NULL; + } + + if (status != 0) + { + opa_abort("opa_number_to_bf: invalid number x"); + } + + return r; +} + +/* converts a bignum n to an ast value and frees the bignum n. */ +opa_value *opa_bf_to_number(mpd_t *n) +{ + if (n == NULL) + { + return NULL; + } + + uint32_t status = 0; + int32_t i = mpd_qget_i32(n, &status); + + if (status == 0) + { + mpd_del(n); + return opa_number_int(i); + } + + char *s = mpd_to_sci(n, 0); + mpd_del(n); + return opa_number_ref(s, opa_strlen(s)); +} + +/* converts a bignum n to an ast value without freeing the bignum n. */ +opa_value *opa_bf_to_number_no_free(mpd_t *n) +{ + if (n == NULL) + { + return NULL; + } + + uint32_t status = 0; + int32_t i = mpd_qget_i32(n, &status); + + if (status == 0) + { + return opa_number_int(i); + } + + char *s = mpd_to_sci(n, 0); + return opa_number_ref(s, opa_strlen(s)); +} + + +/* converts an big number to a bigint with base of 10 and digits of 0 and 1. */ +mpd_t *opa_bf_to_bf_bits(mpd_t *v) +{ + if (v == NULL) + { + return NULL; + } + + mpd_t *i = mpd_qnew(); + uint32_t status = 0; + + mpd_qround_to_intx(i, v, mpd_max_ctx(), &status); + if (status) + { + mpd_del(i); + return NULL; + } + + int c = mpd_qcmp(i, v, &status); + if (status) + { + opa_abort("opa_bits: bits conversion"); + } + + mpd_del(v); + + if (c != 0) + { + // Not an integer value. + mpd_del(i); + return NULL; + } + + uint8_t sign = MPD_POS; + if (mpd_sign(i)) + { + sign = MPD_NEG; + + v = mpd_qnew(); + mpd_qabs(v, i, mpd_max_ctx(), &status); + if (status) + { + opa_abort("opa_bits: bits conversion"); + } + + mpd_del(i); + i = v; + } + + size_t rlen = mpd_sizeinbase(i, 2); + uint16_t *rdata = malloc(rlen * sizeof(uint16_t)); + size_t digits = mpd_qexport_u16(&rdata, rlen, 2, i, &status); + if (status) + { + opa_abort("opa_bits: bits conversion"); + } + + mpd_del(i); + + mpd_t *bits = mpd_qnew(); + mpd_qimport_u16(bits, rdata, digits, sign, 10, mpd_max_ctx(), &status); + if (status) + { + opa_abort("opa_bits: bits conversion"); + } + + free(rdata); + + return bits; +} + +mpd_t *opa_bf_bits_to_bf(mpd_t *v) +{ + if (v == NULL) + { + return NULL; + } + + uint8_t sign = MPD_POS; + uint32_t status = 0; + + if (mpd_sign(v)) + { + mpd_t *abs = mpd_qnew(); + mpd_qabs(abs, v, mpd_max_ctx(), &status); + if (status) + { + opa_abort("opa_bits: bits conversion"); + } + + mpd_del(v); + v = abs; + + sign = MPD_NEG; + } + + size_t rlen = mpd_sizeinbase(v, 10); + uint16_t *rdata = malloc(rlen * sizeof(uint16_t)); + size_t digits = mpd_qexport_u16(&rdata, rlen, 10, v, &status); + if (status) + { + opa_abort("opa_bits: bits conversion"); + } + + mpd_del(v); + + mpd_t *i = mpd_qnew(); + mpd_qimport_u16(i, rdata, digits, sign, 2, mpd_max_ctx(), &status); + if (status) + { + opa_abort("opa_bits: bits conversion"); + } + + free(rdata); + + return i; +} + +mpd_t *qabs(mpd_t *v) +{ + if (v == NULL) + { + return NULL; + } + + mpd_t *a = mpd_qnew(); + uint32_t status = 0; + + mpd_qabs(a, v, mpd_max_ctx(), &status); + if (status) + { + opa_abort("opa_bits: abs conversion"); + } + + mpd_del(v); + return a; +} + +mpd_t *qadd_one(mpd_t *v) +{ + if (v == NULL) + { + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qadd(r, v, mpd_one(), mpd_max_ctx(), &status); + if (status) + { + opa_abort("opa_bits: add one"); + } + + mpd_del(v); + return r; +} + +mpd_t *qadd(mpd_t *a, mpd_t *b) +{ + if (a == NULL || b == NULL) + { + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qadd(r, a, b, mpd_max_ctx(), &status); + if (status) + { + opa_abort("opa_bits: add"); + } + + mpd_del(a); + mpd_del(b); + return r; +} + +mpd_t *qsub_one(mpd_t *v) +{ + if (v == NULL) + { + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qsub(r, v, mpd_one(), mpd_max_ctx(), &status); + if (status) + { + opa_abort("opa_bits: minus one"); + } + + mpd_del(v); + return r; +} + +mpd_t *qmul(mpd_t *a, mpd_t *b) +{ + if (a == NULL || b == NULL) + { + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qmul(r, a, b, mpd_max_ctx(), &status); + if (status) + { + opa_abort("opa_bits: mul"); + } + + mpd_del(a); + mpd_del(b); + return r; +} + +mpd_t *qand(mpd_t *x, mpd_t *y) +{ + x = opa_bf_to_bf_bits(x); + y = opa_bf_to_bf_bits(y); + if (x == NULL || y == NULL) + { + opa_mpd_del(x); + opa_mpd_del(y); + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qand(r, x, y, mpd_max_ctx(), &status); + mpd_del(x); + mpd_del(y); + + if (status) + { + opa_abort("opa_bits_and"); + } + + return opa_bf_bits_to_bf(r); +} + +mpd_t *qand_not(mpd_t *x, mpd_t *y) +{ + x = opa_bf_to_bf_bits(x); + y = opa_bf_to_bf_bits(y); + if (x == NULL || y == NULL) + { + opa_mpd_del(x); + opa_mpd_del(y); + return NULL; + } + + uint32_t status = 0; + + // ^y = y ^ 1111... + size_t rlenx = mpd_sizeinbase(x, 10); + size_t rleny = mpd_sizeinbase(y, 10); + size_t rlen = rlenx < rleny ? rleny : rlenx; + uint16_t *rdata = malloc(rlen * sizeof(uint16_t)); + size_t digits = mpd_qexport_u16(&rdata, rlen, 10, y, &status); + if (status) + { + opa_abort("opa_bits: bits conversion"); + } + + for (int i = 0; i < rlen; i++) + { + rdata[i] = 1; + } + + mpd_t *mask = mpd_qnew(); + mpd_qimport_u16(mask, rdata, rlen, MPD_POS, 10, mpd_max_ctx(), &status); + if (status) + { + opa_abort("opa_bits: bits conversion"); + } + + free(rdata); + + mpd_t *ny = mpd_qnew(); + mpd_qxor(ny, y, mask, mpd_max_ctx(), &status); + if (status) + { + opa_abort("opa_bits_negate"); + } + + mpd_del(y); + mpd_del(mask); + + mpd_t *r = mpd_qnew(); + mpd_qand(r, x, ny, mpd_max_ctx(), &status); + mpd_del(x); + mpd_del(ny); + + if (status) + { + opa_abort("opa_bits_and_not"); + } + + return opa_bf_bits_to_bf(r); +} + +mpd_t *qor(mpd_t *x, mpd_t *y) +{ + x = opa_bf_to_bf_bits(x); + y = opa_bf_to_bf_bits(y); + if (x == NULL || y == NULL) + { + opa_mpd_del(x); + opa_mpd_del(y); + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qor(r, x, y, mpd_max_ctx(), &status); + mpd_del(x); + mpd_del(y); + + if (status) + { + opa_abort("opa_bits_or"); + } + + return opa_bf_bits_to_bf(r); +} + +mpd_t *qxor(mpd_t *x, mpd_t *y) +{ + x = opa_bf_to_bf_bits(x); + y = opa_bf_to_bf_bits(y); + if (x == NULL || y == NULL) + { + opa_mpd_del(x); + opa_mpd_del(y); + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qxor(r, x, y, mpd_max_ctx(), &status); + mpd_del(x); + mpd_del(y); + + if (status) + { + opa_abort("opa_bits_xor"); + } + + return opa_bf_bits_to_bf(r); +} + +mpd_t *qneg(mpd_t *x) +{ + x = opa_bf_to_bf_bits(x); + if (x == NULL) + { + return NULL; + } + + mpd_t *r = mpd_qnew(); + uint32_t status = 0; + + mpd_qminus(r, x, mpd_max_ctx(), &status); + mpd_del(x); + + if (status) + { + opa_abort("opa_bits_neg"); + } + + return opa_bf_bits_to_bf(r); +} diff --git a/third_party/opa/wasm/src/mpd.h b/third_party/opa/wasm/src/mpd.h new file mode 100644 index 000000000000..330995b78b15 --- /dev/null +++ b/third_party/opa/wasm/src/mpd.h @@ -0,0 +1,29 @@ +#ifndef OPA_MPD_H +#define OPA_MPD_H + +#include + +typedef struct opa_value opa_value; + +void opa_mpd_init(void); +mpd_context_t *mpd_default_ctx(void); +mpd_context_t *mpd_max_ctx(void); +void opa_mpd_del(mpd_t *v); +mpd_t *opa_number_to_bf(opa_value *v); +opa_value *opa_bf_to_number(mpd_t *n); +opa_value *opa_bf_to_number_no_free (mpd_t *n); +mpd_t *qabs(mpd_t *v); +mpd_t *qadd_one(mpd_t *v); +mpd_t *qadd(mpd_t *a, mpd_t *b); +mpd_t *qsub_one(mpd_t *v); +mpd_t *qmul(mpd_t *a, mpd_t *b); +mpd_t *qand(mpd_t *x, mpd_t *y); +mpd_t *qand_not(mpd_t *x, mpd_t *y); +mpd_t *qor(mpd_t *x, mpd_t *y); +mpd_t *qxor(mpd_t *x, mpd_t *y); +mpd_t *qneg(mpd_t *x); + +mpd_t *opa_bf_to_bf_bits(mpd_t *v); +mpd_t *opa_bf_bits_to_bf(mpd_t *v); + +#endif diff --git a/third_party/opa/wasm/src/numbers.c b/third_party/opa/wasm/src/numbers.c new file mode 100644 index 000000000000..436590cdccef --- /dev/null +++ b/third_party/opa/wasm/src/numbers.c @@ -0,0 +1,103 @@ +#include "numbers.h" +#include "mpd.h" +#include "std.h" + +OPA_BUILTIN +opa_value *opa_numbers_range(opa_value *v1, opa_value *v2) +{ + mpd_t *i1 = NULL; + mpd_t *i2 = NULL; + opa_value *result = NULL; + + i1 = opa_number_to_bf(v1); + + if (i1 == NULL) + { + goto cleanup; + } + else if (!mpd_isinteger(i1)) + { + goto cleanup; + } + + i2 = opa_number_to_bf(v2); + + if (i2 == NULL) + { + goto cleanup; + } + else if (!mpd_isinteger(i2)) + { + goto cleanup; + } + + uint32_t status = 0; + int cmp = mpd_qcmp(i1, i2, &status); + + if (status) + { + opa_abort("opa_numbers_range: comparison"); + } + + result = opa_array(); + opa_array_t *arr = opa_cast_array(result); + + if (cmp <= 0) + { + mpd_t *curr = i1; + i1 = NULL; + + while (cmp <= 0) + { + opa_value *add = opa_bf_to_number_no_free(curr); + + if (add == NULL) + { + opa_abort("opa_numbers_range: conversion"); + } + + opa_array_append(arr, add); + curr = qadd_one(curr); + cmp = mpd_qcmp(curr, i2, &status); + + if (status) + { + opa_abort("opa_numbers_range: comparison"); + } + } + + opa_mpd_del(curr); + } + else + { + mpd_t *curr = i1; + i1 = NULL; + + while (cmp >= 0) + { + opa_value *add = opa_bf_to_number_no_free(curr); + + if (add == NULL) + { + opa_abort("opa_numbers_range: conversion"); + } + + opa_array_append(arr, add); + curr = qsub_one(curr); + cmp = mpd_qcmp(curr, i2, &status); + + if (status) + { + opa_abort("opa_numbers_range: comparison"); + } + } + + opa_mpd_del(curr); + } + +cleanup: + opa_mpd_del(i1); + opa_mpd_del(i2); + + return result; +} diff --git a/third_party/opa/wasm/src/numbers.h b/third_party/opa/wasm/src/numbers.h new file mode 100644 index 000000000000..1d995db21b18 --- /dev/null +++ b/third_party/opa/wasm/src/numbers.h @@ -0,0 +1,8 @@ +#ifndef OPA_NUMBERS_H +#define OPA_NUMBERS_H + +#include "value.h" + +opa_value *opa_numbers_range(opa_value *v1, opa_value *v2); + +#endif \ No newline at end of file diff --git a/third_party/opa/wasm/src/object.c b/third_party/opa/wasm/src/object.c new file mode 100644 index 000000000000..02ac66ab690a --- /dev/null +++ b/third_party/opa/wasm/src/object.c @@ -0,0 +1,651 @@ +#include "std.h" +#include "object.h" + +static opa_value *__merge(opa_value *a, opa_value *b); +static opa_value *__merge_with_overwrite(opa_value *a, opa_value *b); +static void __copy_object_elem(opa_object_t *result, opa_value *a, opa_value *b); +opa_array_t *__get_json_paths(opa_value *a); +opa_object_t *__paths_to_object(opa_value *a); +opa_array_t *__parse_path(opa_value *a); +opa_value *__json_remove(opa_value *a, opa_value *b); +opa_value *__json_filter(opa_value *a, opa_value *b); + +opa_value *__merge(opa_value *a, opa_value *b) +{ + + opa_object_t *merged = opa_cast_object(opa_object()); + opa_object_t *obj = opa_cast_object(a); + opa_object_t *other = opa_cast_object(b); + + for (opa_value *key = opa_value_iter(a, NULL); key != NULL; + key = opa_value_iter(a, key)) + { + + opa_object_elem_t *original = opa_object_get(obj, key); + opa_object_elem_t *elem = opa_object_get(other, key); + + // The key didn't exist in other, keep the original value. + if (elem == NULL) + { + opa_object_insert(merged, key, original->v); + continue; + } + + // The key exists in both, resolve the conflict. + opa_value *merged_value = __merge_with_overwrite(original->v, elem->v); + opa_object_insert(merged, key, merged_value); + + } + + // Copy in any values from other for keys that don't exist in obj. + __copy_object_elem(merged, a, b); + + return &merged->hdr; +} + +opa_value *__merge_with_overwrite(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_OBJECT || opa_value_type(b) != OPA_OBJECT) + { + // If we can't merge, stick with the right-hand value. + return b; + } + + return __merge(a, b); +} + +static void __copy_object_elem(opa_object_t *result, opa_value *a, opa_value *b) +{ + opa_object_t *obj = opa_cast_object(b); + + for (int i = 0; i < obj->n; i++) + { + opa_object_elem_t *elem = obj->buckets[i]; + + while (elem != NULL) + { + opa_value *other = opa_value_get(a, elem->k); + + if (other == NULL) + { + opa_object_insert(result, elem->k, elem->v); + } + + elem = elem->next; + } + } +} + +opa_object_t *__paths_to_object(opa_value *a) +{ + opa_object_t *root = opa_cast_object(opa_object()); + opa_array_t *paths = opa_cast_array(a); + + for (int i = 0; i < paths->len; i++) + { + opa_object_t *node = root; + uint32_t done = 0; + + opa_array_t *terms = opa_cast_array(paths->elems[i].v); + + for (int j = 0; j < terms->len - 1 && !done; j++) { + + opa_value *k = terms->elems[j].v; + + opa_value *child = opa_value_get(&node->hdr, k); + + if (child == NULL) { + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(node, k, &obj->hdr); + node = obj; + continue; + } + + switch (opa_value_type(child)) + { + case OPA_NULL: + done = 1; + break; + case OPA_OBJECT: + node = opa_cast_object(child); + } + } + + if (!done) + { + opa_value *key = terms->elems[terms->len - 1].v; + opa_object_insert(node, key, opa_null()); + } + } + + return root; + +} + +opa_array_t *__parse_path(opa_value *a) +{ + // paths can either be a `/` separated json path or + // an array or set of values + + opa_array_t *path_segments = opa_cast_array(opa_array()); + + switch (opa_value_type(a)) + { + case OPA_STRING: + { + opa_string_t *x = opa_cast_string(a); + + if (x->len == 0) + { + return path_segments; + } + + opa_value *s = opa_strings_split(opa_strings_trim_left(a, opa_string_terminated("/")), opa_string_terminated("/")); + + opa_array_t *parts = opa_cast_array(s); + + for (int i = 0; i < parts->len; i++) + { + opa_value *s = opa_strings_replace(parts->elems[i].v, opa_string_terminated("~1"), opa_string_terminated("/")); + opa_value *part = opa_strings_replace(s, opa_string_terminated("~0"), opa_string_terminated("~")); + opa_array_append(path_segments, part); + } + + return path_segments; + } + case OPA_ARRAY: + { + opa_array_t *y = opa_cast_array(a); + + for (int i = 0; i < y->len; i++) + { + opa_array_append(path_segments, y->elems[i].v); + } + + return path_segments; + } + } + + return NULL; +} + +opa_array_t *__get_json_paths(opa_value *a) +{ + opa_array_t *paths = opa_cast_array(opa_array()); + + for (opa_value *key = opa_value_iter(a, NULL); key != NULL; key = opa_value_iter(a, key)) + { + opa_value* k; + switch (opa_value_type(a)) + { + case OPA_SET: + k = key; + break; + case OPA_ARRAY: + k = opa_value_get(a, key); + } + + opa_array_t* path = __parse_path(k); + + if (path == NULL) + { + return NULL; + } + opa_array_append(paths, &path->hdr); + } + + return paths; +} + +opa_value *__json_remove(opa_value *a, opa_value *b) +{ + if (b == NULL) + { + // The paths diverged, return a + return a; + } + + opa_value* bObj; + switch (opa_value_type(b)) + { + case OPA_OBJECT: + { + bObj = b; + break; + } + case OPA_NULL: + { + // Means we hit a leaf node on "b", dont add the value for a + return NULL; + } + default: + // The paths diverged, return a + return a; + } + + switch (opa_value_type(a)) + { + case OPA_STRING: + case OPA_NUMBER: + case OPA_BOOLEAN: + case OPA_NULL: + { + return a; + } + case OPA_OBJECT: + { + opa_object_t *new_obj = opa_cast_object(opa_object()); + + for (opa_value *key = opa_value_iter(a, NULL); key != NULL; key = opa_value_iter(a, key)) + { + opa_value *value = opa_value_get(a, key); + opa_value *diff_value = __json_remove(value, opa_value_get(bObj, key)); + + if (diff_value != NULL) + { + opa_object_insert(new_obj, key, diff_value); + } + } + return &new_obj->hdr; + } + case OPA_SET: + { + opa_set_t *new_set = opa_cast_set(opa_set()); + opa_set_t *set = opa_cast_set(a); + + for (int i = 0; i < set->n; i++) + { + opa_set_elem_t *elem = set->buckets[i]; + + while (elem != NULL) + { + opa_value *diff_value = __json_remove(elem->v, opa_value_get(bObj, elem->v)); + + if (diff_value != NULL) + { + opa_set_add(new_set, diff_value); + } + elem = elem->next; + } + } + return &new_set->hdr; + } + case OPA_ARRAY: + { + opa_array_t *new_array = opa_cast_array(opa_array()); + opa_array_t *array = opa_cast_array(a); + + for (int i = 0; i < array->len; i++) + { + opa_value *value = array->elems[i].v; + + opa_value *diff_value = __json_remove(value, opa_value_get(bObj, opa_strings_format_int(opa_number_int(i), opa_number_int(10)))); + + if (diff_value != NULL) + { + opa_array_append(new_array, diff_value); + } + } + return &new_array->hdr; + } + } + + return NULL; +} + +opa_value *__json_filter(opa_value *a, opa_value *b) +{ + + if (opa_value_compare(b, opa_null()) == 0) + { + return a; + } + + if (opa_value_type(b) != OPA_OBJECT) + { + return NULL; + } + + switch (opa_value_type(a)) + { + case OPA_STRING: + case OPA_NUMBER: + case OPA_BOOLEAN: + case OPA_NULL: + { + return a; + } + case OPA_OBJECT: + { + opa_object_t *new_obj = opa_cast_object(opa_object()); + + opa_object_t *iter_obj = opa_cast_object(a); + opa_object_t *other = opa_cast_object(b); + + if (iter_obj->len < other->len) + { + iter_obj = opa_cast_object(b); + other = opa_cast_object(a); + } + + for (opa_value *key = opa_value_iter(&iter_obj->hdr, NULL); key != NULL; key = opa_value_iter(&iter_obj->hdr, key)) + { + + if (opa_value_get(&other->hdr, key) != NULL) + { + opa_value *filtered_value = __json_filter(opa_value_get(a, key), opa_value_get(b, key)); + + if (filtered_value != NULL) + { + opa_object_insert(new_obj, key, filtered_value); + } + } + } + return &new_obj->hdr; + } + case OPA_SET: + { + opa_set_t *new_set = opa_cast_set(opa_set()); + opa_set_t *set = opa_cast_set(a); + + for (int i = 0; i < set->n; i++) + { + opa_set_elem_t *elem = set->buckets[i]; + + while (elem != NULL) + { + opa_value *filtered_value = __json_filter(elem->v, opa_value_get(b, elem->v)); + + if (filtered_value != NULL) + { + opa_set_add(new_set, filtered_value); + } + elem = elem->next; + } + } + return &new_set->hdr; + } + case OPA_ARRAY: + { + opa_array_t *new_array = opa_cast_array(opa_array()); + opa_array_t *array = opa_cast_array(a); + + for (int i = 0; i < array->len; i++) + { + opa_value *value = array->elems[i].v; + + opa_value *filtered_value = __json_filter(value, opa_value_get(b, opa_strings_format_int(opa_number_int(i), opa_number_int(10)))); + + if (filtered_value != NULL) + { + opa_array_append(new_array, filtered_value); + } + } + return &new_array->hdr; + } + } + + return NULL; +} + +OPA_BUILTIN +opa_value *builtin_object_filter(opa_value *obj, opa_value *keys) +{ + if (opa_value_type(obj) != OPA_OBJECT) + { + return NULL; + } + + if (opa_value_type(keys) != OPA_OBJECT && opa_value_type(keys) != OPA_ARRAY && + opa_value_type(keys) != OPA_SET) + { + return NULL; + } + + opa_object_t *r = opa_cast_object(opa_object()); + + for (opa_value *key = opa_value_iter(keys, NULL); key != NULL; key = opa_value_iter(keys, key)) + { + opa_value* k; + switch (opa_value_type(keys)) + { + case OPA_OBJECT: + case OPA_SET: + k = key; + break; + case OPA_ARRAY: + k = opa_value_get(keys, key); + } + + opa_object_elem_t *elem = opa_object_get(opa_cast_object(obj), k); + if (elem != NULL) + { + opa_object_insert(r, k, elem->v); + } + } + + return &r->hdr; +} + +OPA_BUILTIN +opa_value *builtin_object_get(opa_value *obj, opa_value *key, opa_value *value) +{ + if (opa_value_type(obj) != OPA_OBJECT) + { + return NULL; + } + + opa_value *elem; + + // if the key is not an array, then we get that top level key from the object/array or return the default value + if (opa_value_type(key) != OPA_ARRAY) { + elem = opa_value_get(obj, key); + if (elem != NULL) + { + return elem; + } + + return value; + } + + size_t path_len = opa_cast_array(key)->len; + // if the path is empty, then we skip selecting nested keys and return the default + if (path_len == 0) { + return obj; + } + + for (int i = 0; i < path_len; i++) + { + opa_value *path_component = opa_cast_array(key)->elems[i].v; + + elem = opa_value_get(obj, path_component); + + if (elem == NULL) + { + return value; + } + + if (i == path_len-1) + { + return elem; + } + + obj = elem; + } + + return value; +} + +OPA_BUILTIN +opa_value *builtin_object_keys(opa_value *a) +{ + if (opa_value_type(a) != OPA_OBJECT) + { + return NULL; + } + + opa_object_t *obj = opa_cast_object(a); + opa_set_t *keys = opa_cast_set(opa_set_with_cap(obj->len)); + + for (int i = 0; i < obj->n; i++) + { + opa_object_elem_t *elem = obj->buckets[i]; + + while (elem != NULL) + { + opa_set_add(keys, elem->k); + elem = elem->next; + } + } + + return &keys->hdr; +} + +OPA_BUILTIN +opa_value *builtin_object_remove(opa_value *obj, opa_value *keys) +{ + if (opa_value_type(obj) != OPA_OBJECT || + (opa_value_type(keys) != OPA_OBJECT && + opa_value_type(keys) != OPA_ARRAY && + opa_value_type(keys) != OPA_SET)) + { + return NULL; + } + + opa_set_t *keys_to_remove = opa_cast_set(opa_set()); + + for (opa_value *key = opa_value_iter(keys, NULL); key != NULL; + key = opa_value_iter(keys, key)) + { + opa_value* k; + switch (opa_value_type(keys)) + { + case OPA_OBJECT: + case OPA_SET: + k = key; + break; + case OPA_ARRAY: + k = opa_value_get(keys, key); + } + opa_set_add(keys_to_remove, k); + } + + opa_object_t *r = opa_cast_object(opa_object()); + + for (opa_value *key = opa_value_iter(obj, NULL); key != NULL; + key = opa_value_iter(obj, key)) + { + if (opa_set_get(keys_to_remove, key) == NULL) + { + opa_object_elem_t *elem = opa_object_get(opa_cast_object(obj), key); + if (elem != NULL) + { + opa_object_insert(r, key, elem->v); + } + } + } + + return &r->hdr; +} + +OPA_BUILTIN +opa_value *builtin_object_union(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_OBJECT) + { + return NULL; + } + + if (opa_value_type(b) != OPA_OBJECT) + { + return NULL; + } + + opa_value *r = __merge(a, b); + + return r; +} + +OPA_BUILTIN +opa_value *builtin_object_union_n(opa_value *a) +{ + opa_value *r = NULL; + + for (opa_value *key = opa_value_iter(a, NULL); key != NULL; + key = opa_value_iter(a, key)){ + opa_value *next_obj = opa_value_get(a, key); + if (opa_value_type(next_obj) != OPA_OBJECT) { + return NULL; + } + + if (r == NULL) { + r = next_obj; + } else { + opa_value *merged = builtin_object_union(r, next_obj); + + if (merged == NULL) { + return NULL; + } + + r = merged; + } + } + + return r; +} + +OPA_BUILTIN +opa_value *builtin_json_remove(opa_value *obj, opa_value *paths) +{ + if (opa_value_type(obj) != OPA_OBJECT) + { + return NULL; + } + + if (opa_value_type(paths) != OPA_ARRAY && opa_value_type(paths) != OPA_SET) + { + return NULL; + } + + // Build a list of json pointers to remove + opa_array_t *json_paths = __get_json_paths(paths); + + if (json_paths == NULL) + { + return NULL; + } + + opa_object_t *json_paths_obj = __paths_to_object(&json_paths->hdr); + + opa_value *r = __json_remove(obj, &json_paths_obj->hdr); + + return r; +} + +OPA_BUILTIN +opa_value *builtin_json_filter(opa_value *obj, opa_value *paths) +{ + if (opa_value_type(obj) != OPA_OBJECT) + { + return NULL; + } + + if (opa_value_type(paths) != OPA_ARRAY && opa_value_type(paths) != OPA_SET) + { + return NULL; + } + + // Build a list of filter strings + opa_array_t *json_paths = __get_json_paths(paths); + + if (json_paths == NULL) + { + return NULL; + } + + opa_object_t *json_paths_obj = __paths_to_object(&json_paths->hdr); + + opa_value *r = __json_filter(obj, &json_paths_obj->hdr); + + return r; +} diff --git a/third_party/opa/wasm/src/object.h b/third_party/opa/wasm/src/object.h new file mode 100644 index 000000000000..4cca8e7ad6fc --- /dev/null +++ b/third_party/opa/wasm/src/object.h @@ -0,0 +1,16 @@ +#ifndef OPA_OBJECT_H +#define OPA_OBJECT_H + +#include "value.h" +#include "strings.h" + +opa_value *builtin_object_filter(opa_value *obj, opa_value *keys); +opa_value *builtin_object_get(opa_value *obj, opa_value *key, opa_value *value); +opa_value *builtin_object_keys(opa_value *obj); +opa_value *builtin_object_remove(opa_value *obj, opa_value *keys); +opa_value *builtin_object_union(opa_value *a, opa_value *b); +opa_value *builtin_object_union_n(opa_value *a); +opa_value *builtin_json_remove(opa_value *obj, opa_value *paths); +opa_value *builtin_json_filter(opa_value *obj, opa_value *paths); + +#endif diff --git a/third_party/opa/wasm/src/re2/re2/bitmap256.h b/third_party/opa/wasm/src/re2/re2/bitmap256.h new file mode 100644 index 000000000000..4899379e4d99 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/bitmap256.h @@ -0,0 +1,117 @@ +// Copyright 2016 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef RE2_BITMAP256_H_ +#define RE2_BITMAP256_H_ + +#ifdef _MSC_VER +#include +#endif +#include +#include + +#include "util/util.h" +#include "util/logging.h" + +namespace re2 { + +class Bitmap256 { + public: + Bitmap256() { + Clear(); + } + + // Clears all of the bits. + void Clear() { + memset(words_, 0, sizeof words_); + } + + // Tests the bit with index c. + bool Test(int c) const { + DCHECK_GE(c, 0); + DCHECK_LE(c, 255); + + return (words_[c / 64] & (uint64_t{1} << (c % 64))) != 0; + } + + // Sets the bit with index c. + void Set(int c) { + DCHECK_GE(c, 0); + DCHECK_LE(c, 255); + + words_[c / 64] |= (uint64_t{1} << (c % 64)); + } + + // Finds the next non-zero bit with index >= c. + // Returns -1 if no such bit exists. + int FindNextSetBit(int c) const; + + private: + // Finds the least significant non-zero bit in n. + static int FindLSBSet(uint64_t n) { + DCHECK_NE(n, 0); +#if defined(__GNUC__) + return __builtin_ctzll(n); +#elif defined(_MSC_VER) && defined(_M_X64) + unsigned long c; + _BitScanForward64(&c, n); + return static_cast(c); +#elif defined(_MSC_VER) && defined(_M_IX86) + unsigned long c; + if (static_cast(n) != 0) { + _BitScanForward(&c, static_cast(n)); + return static_cast(c); + } else { + _BitScanForward(&c, static_cast(n >> 32)); + return static_cast(c) + 32; + } +#else + int c = 63; + for (int shift = 1 << 5; shift != 0; shift >>= 1) { + uint64_t word = n << shift; + if (word != 0) { + n = word; + c -= shift; + } + } + return c; +#endif + } + + uint64_t words_[4]; +}; + +int Bitmap256::FindNextSetBit(int c) const { + DCHECK_GE(c, 0); + DCHECK_LE(c, 255); + + // Check the word that contains the bit. Mask out any lower bits. + int i = c / 64; + uint64_t word = words_[i] & (~uint64_t{0} << (c % 64)); + if (word != 0) + return (i * 64) + FindLSBSet(word); + + // Check any following words. + i++; + switch (i) { + case 1: + if (words_[1] != 0) + return (1 * 64) + FindLSBSet(words_[1]); + FALLTHROUGH_INTENDED; + case 2: + if (words_[2] != 0) + return (2 * 64) + FindLSBSet(words_[2]); + FALLTHROUGH_INTENDED; + case 3: + if (words_[3] != 0) + return (3 * 64) + FindLSBSet(words_[3]); + FALLTHROUGH_INTENDED; + default: + return -1; + } +} + +} // namespace re2 + +#endif // RE2_BITMAP256_H_ diff --git a/third_party/opa/wasm/src/re2/re2/bitstate.cc b/third_party/opa/wasm/src/re2/re2/bitstate.cc new file mode 100644 index 000000000000..cf5815db62fb --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/bitstate.cc @@ -0,0 +1,389 @@ +// Copyright 2008 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Tested by search_test.cc, exhaustive_test.cc, tester.cc + +// Prog::SearchBitState is a regular expression search with submatch +// tracking for small regular expressions and texts. Similarly to +// testing/backtrack.cc, it allocates a bitmap with (count of +// lists) * (length of text) bits to make sure it never explores the +// same (instruction list, character position) multiple times. This +// limits the search to run in time linear in the length of the text. +// +// Unlike testing/backtrack.cc, SearchBitState is not recursive +// on the text. +// +// SearchBitState is a fast replacement for the NFA code on small +// regexps and texts when SearchOnePass cannot be used. + +#include +#include +#include +#include +#include + +#include "util/logging.h" +#include "re2/pod_array.h" +#include "re2/prog.h" +#include "re2/regexp.h" + +namespace re2 { + +struct Job { + int id; + int rle; // run length encoding + const char* p; +}; + +class BitState { + public: + explicit BitState(Prog* prog); + + // The usual Search prototype. + // Can only call Search once per BitState. + bool Search(const StringPiece& text, const StringPiece& context, + bool anchored, bool longest, + StringPiece* submatch, int nsubmatch); + + private: + inline bool ShouldVisit(int id, const char* p); + void Push(int id, const char* p); + void GrowStack(); + bool TrySearch(int id, const char* p); + + // Search parameters + Prog* prog_; // program being run + StringPiece text_; // text being searched + StringPiece context_; // greater context of text being searched + bool anchored_; // whether search is anchored at text.begin() + bool longest_; // whether search wants leftmost-longest match + bool endmatch_; // whether match must end at text.end() + StringPiece* submatch_; // submatches to fill in + int nsubmatch_; // # of submatches to fill in + + // Search state + static constexpr int kVisitedBits = 64; + PODArray visited_; // bitmap: (list ID, char*) pairs visited + PODArray cap_; // capture registers + PODArray job_; // stack of text positions to explore + int njob_; // stack size + + BitState(const BitState&) = delete; + BitState& operator=(const BitState&) = delete; +}; + +BitState::BitState(Prog* prog) + : prog_(prog), + anchored_(false), + longest_(false), + endmatch_(false), + submatch_(NULL), + nsubmatch_(0), + njob_(0) { +} + +// Given id, which *must* be a list head, we can look up its list ID. +// Then the question is: Should the search visit the (list ID, p) pair? +// If so, remember that it was visited so that the next time, +// we don't repeat the visit. +bool BitState::ShouldVisit(int id, const char* p) { + int n = prog_->list_heads()[id] * static_cast(text_.size()+1) + + static_cast(p-text_.data()); + if (visited_[n/kVisitedBits] & (uint64_t{1} << (n & (kVisitedBits-1)))) + return false; + visited_[n/kVisitedBits] |= uint64_t{1} << (n & (kVisitedBits-1)); + return true; +} + +// Grow the stack. +void BitState::GrowStack() { + PODArray tmp(2*job_.size()); + memmove(tmp.data(), job_.data(), njob_*sizeof job_[0]); + job_ = std::move(tmp); +} + +// Push (id, p) onto the stack, growing it if necessary. +void BitState::Push(int id, const char* p) { + if (njob_ >= job_.size()) { + GrowStack(); + if (njob_ >= job_.size()) { +#if 0 + LOG(DFATAL) << "GrowStack() failed: " + << "njob_ = " << njob_ << ", " + << "job_.size() = " << job_.size(); +#endif + return; + } + } + + // If id < 0, it's undoing a Capture, + // so we mustn't interfere with that. + if (id >= 0 && njob_ > 0) { + Job* top = &job_[njob_-1]; + if (id == top->id && + p == top->p + top->rle + 1 && + top->rle < std::numeric_limits::max()) { + ++top->rle; + return; + } + } + + Job* top = &job_[njob_++]; + top->id = id; + top->rle = 0; + top->p = p; +} + +// Try a search from instruction id0 in state p0. +// Return whether it succeeded. +bool BitState::TrySearch(int id0, const char* p0) { + bool matched = false; + const char* end = text_.data() + text_.size(); + njob_ = 0; + // Push() no longer checks ShouldVisit(), + // so we must perform the check ourselves. + if (ShouldVisit(id0, p0)) + Push(id0, p0); + while (njob_ > 0) { + // Pop job off stack. + --njob_; + int id = job_[njob_].id; + int& rle = job_[njob_].rle; + const char* p = job_[njob_].p; + + if (id < 0) { + // Undo the Capture. + cap_[prog_->inst(-id)->cap()] = p; + continue; + } + + if (rle > 0) { + p += rle; + // Revivify job on stack. + --rle; + ++njob_; + } + + Loop: + // Visit id, p. + Prog::Inst* ip = prog_->inst(id); + switch (ip->opcode()) { + default: +#if 0 + LOG(DFATAL) << "Unexpected opcode: " << ip->opcode(); +#endif + return false; + + case kInstFail: + break; + + case kInstAltMatch: + if (ip->greedy(prog_)) { + // out1 is the Match instruction. + id = ip->out1(); + p = end; + goto Loop; + } + if (longest_) { + // ip must be non-greedy... + // out is the Match instruction. + id = ip->out(); + p = end; + goto Loop; + } + goto Next; + + case kInstByteRange: { + int c = -1; + if (p < end) + c = *p & 0xFF; + if (!ip->Matches(c)) + goto Next; + + if (ip->hint() != 0) + Push(id+ip->hint(), p); // try the next when we're done + id = ip->out(); + p++; + goto CheckAndLoop; + } + + case kInstCapture: + if (!ip->last()) + Push(id+1, p); // try the next when we're done + + if (0 <= ip->cap() && ip->cap() < cap_.size()) { + // Capture p to register, but save old value first. + Push(-id, cap_[ip->cap()]); // undo when we're done + cap_[ip->cap()] = p; + } + + id = ip->out(); + goto CheckAndLoop; + + case kInstEmptyWidth: + if (ip->empty() & ~Prog::EmptyFlags(context_, p)) + goto Next; + + if (!ip->last()) + Push(id+1, p); // try the next when we're done + id = ip->out(); + goto CheckAndLoop; + + case kInstNop: + if (!ip->last()) + Push(id+1, p); // try the next when we're done + id = ip->out(); + + CheckAndLoop: + // Sanity check: id is the head of its list, which must + // be the case if id-1 is the last of *its* list. :) + DCHECK(id == 0 || prog_->inst(id-1)->last()); + if (ShouldVisit(id, p)) + goto Loop; + break; + + case kInstMatch: { + if (endmatch_ && p != end) + goto Next; + + // We found a match. If the caller doesn't care + // where the match is, no point going further. + if (nsubmatch_ == 0) + return true; + + // Record best match so far. + // Only need to check end point, because this entire + // call is only considering one start position. + matched = true; + cap_[1] = p; + if (submatch_[0].data() == NULL || + (longest_ && p > submatch_[0].data() + submatch_[0].size())) { + for (int i = 0; i < nsubmatch_; i++) + submatch_[i] = + StringPiece(cap_[2 * i], + static_cast(cap_[2 * i + 1] - cap_[2 * i])); + } + + // If going for first match, we're done. + if (!longest_) + return true; + + // If we used the entire text, no longer match is possible. + if (p == end) + return true; + + // Otherwise, continue on in hope of a longer match. + // Note the absence of the ShouldVisit() check here + // due to execution remaining in the same list. + Next: + if (!ip->last()) { + id++; + goto Loop; + } + break; + } + } + } + return matched; +} + +// Search text (within context) for prog_. +bool BitState::Search(const StringPiece& text, const StringPiece& context, + bool anchored, bool longest, + StringPiece* submatch, int nsubmatch) { + // Search parameters. + text_ = text; + context_ = context; + if (context_.data() == NULL) + context_ = text; + if (prog_->anchor_start() && context_.begin() != text.begin()) + return false; + if (prog_->anchor_end() && context_.end() != text.end()) + return false; + anchored_ = anchored || prog_->anchor_start(); + longest_ = longest || prog_->anchor_end(); + endmatch_ = prog_->anchor_end(); + submatch_ = submatch; + nsubmatch_ = nsubmatch; + for (int i = 0; i < nsubmatch_; i++) + submatch_[i] = StringPiece(); + + // Allocate scratch space. + int nvisited = prog_->list_count() * static_cast(text.size()+1); + nvisited = (nvisited + kVisitedBits-1) / kVisitedBits; + visited_ = PODArray(nvisited); + memset(visited_.data(), 0, nvisited*sizeof visited_[0]); + + int ncap = 2*nsubmatch; + if (ncap < 2) + ncap = 2; + cap_ = PODArray(ncap); + memset(cap_.data(), 0, ncap*sizeof cap_[0]); + + // When sizeof(Job) == 16, we start with a nice round 1KiB. :) + job_ = PODArray(64); + + // Anchored search must start at text.begin(). + if (anchored_) { + cap_[0] = text.data(); + return TrySearch(prog_->start(), text.data()); + } + + // Unanchored search, starting from each possible text position. + // Notice that we have to try the empty string at the end of + // the text, so the loop condition is p <= text.end(), not p < text.end(). + // This looks like it's quadratic in the size of the text, + // but we are not clearing visited_ between calls to TrySearch, + // so no work is duplicated and it ends up still being linear. + const char* etext = text.data() + text.size(); + for (const char* p = text.data(); p <= etext; p++) { + // Try to use prefix accel (e.g. memchr) to skip ahead. + if (p < etext && prog_->can_prefix_accel()) { + p = reinterpret_cast(prog_->PrefixAccel(p, etext - p)); + if (p == NULL) + p = etext; + } + + cap_[0] = p; + if (TrySearch(prog_->start(), p)) // Match must be leftmost; done. + return true; + // Avoid invoking undefined behavior (arithmetic on a null pointer) + // by simply not continuing the loop. + if (p == NULL) + break; + } + return false; +} + +// Bit-state search. +bool Prog::SearchBitState(const StringPiece& text, + const StringPiece& context, + Anchor anchor, + MatchKind kind, + StringPiece* match, + int nmatch) { + // If full match, we ask for an anchored longest match + // and then check that match[0] == text. + // So make sure match[0] exists. + StringPiece sp0; + if (kind == kFullMatch) { + anchor = kAnchored; + if (nmatch < 1) { + match = &sp0; + nmatch = 1; + } + } + + // Run the search. + BitState b(this); + bool anchored = anchor == kAnchored; + bool longest = kind != kFirstMatch; + if (!b.Search(text, context, anchored, longest, match, nmatch)) + return false; + if (kind == kFullMatch && match[0].end() != text.end()) + return false; + return true; +} + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/compile.cc b/third_party/opa/wasm/src/re2/re2/compile.cc new file mode 100644 index 000000000000..fe7b1f88607c --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/compile.cc @@ -0,0 +1,1253 @@ +// Copyright 2007 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Compile regular expression to Prog. +// +// Prog and Inst are defined in prog.h. +// This file's external interface is just Regexp::CompileToProg. +// The Compiler class defined in this file is private. + +#include +#include +#include +#include + +#include "util/logging.h" +#include "util/utf.h" +#include "re2/pod_array.h" +#include "re2/prog.h" +#include "re2/re2.h" +#include "re2/regexp.h" +#include "re2/walker-inl.h" + +namespace re2 { + +// List of pointers to Inst* that need to be filled in (patched). +// Because the Inst* haven't been filled in yet, +// we can use the Inst* word to hold the list's "next" pointer. +// It's kind of sleazy, but it works well in practice. +// See http://swtch.com/~rsc/regexp/regexp1.html for inspiration. +// +// Because the out and out1 fields in Inst are no longer pointers, +// we can't use pointers directly here either. Instead, head refers +// to inst_[head>>1].out (head&1 == 0) or inst_[head>>1].out1 (head&1 == 1). +// head == 0 represents the NULL list. This is okay because instruction #0 +// is always the fail instruction, which never appears on a list. +struct PatchList { + // Returns patch list containing just p. + static PatchList Mk(uint32_t p) { + return {p, p}; + } + + // Patches all the entries on l to have value p. + // Caller must not ever use patch list again. + static void Patch(Prog::Inst* inst0, PatchList l, uint32_t p) { + while (l.head != 0) { + Prog::Inst* ip = &inst0[l.head>>1]; + if (l.head&1) { + l.head = ip->out1(); + ip->out1_ = p; + } else { + l.head = ip->out(); + ip->set_out(p); + } + } + } + + // Appends two patch lists and returns result. + static PatchList Append(Prog::Inst* inst0, PatchList l1, PatchList l2) { + if (l1.head == 0) + return l2; + if (l2.head == 0) + return l1; + Prog::Inst* ip = &inst0[l1.tail>>1]; + if (l1.tail&1) + ip->out1_ = l2.head; + else + ip->set_out(l2.head); + return {l1.head, l2.tail}; + } + + uint32_t head; + uint32_t tail; // for constant-time append +}; + +static const PatchList kNullPatchList = {0, 0}; + +// Compiled program fragment. +struct Frag { + uint32_t begin; + PatchList end; + + Frag() : begin(0) { end.head = 0; } // needed so Frag can go in vector + Frag(uint32_t begin, PatchList end) : begin(begin), end(end) {} +}; + +// Input encodings. +enum Encoding { + kEncodingUTF8 = 1, // UTF-8 (0-10FFFF) + kEncodingLatin1, // Latin-1 (0-FF) +}; + +class Compiler : public Regexp::Walker { + public: + explicit Compiler(); + ~Compiler(); + + // Compiles Regexp to a new Prog. + // Caller is responsible for deleting Prog when finished with it. + // If reversed is true, compiles for walking over the input + // string backward (reverses all concatenations). + static Prog *Compile(Regexp* re, bool reversed, int64_t max_mem); + + // Compiles alternation of all the re to a new Prog. + // Each re has a match with an id equal to its index in the vector. + static Prog* CompileSet(Regexp* re, RE2::Anchor anchor, int64_t max_mem); + + // Interface for Regexp::Walker, which helps traverse the Regexp. + // The walk is purely post-recursive: given the machines for the + // children, PostVisit combines them to create the machine for + // the current node. The child_args are Frags. + // The Compiler traverses the Regexp parse tree, visiting + // each node in depth-first order. It invokes PreVisit before + // visiting the node's children and PostVisit after visiting + // the children. + Frag PreVisit(Regexp* re, Frag parent_arg, bool* stop); + Frag PostVisit(Regexp* re, Frag parent_arg, Frag pre_arg, Frag* child_args, + int nchild_args); + Frag ShortVisit(Regexp* re, Frag parent_arg); + Frag Copy(Frag arg); + + // Given fragment a, returns a+ or a+?; a* or a*?; a? or a?? + Frag Plus(Frag a, bool nongreedy); + Frag Star(Frag a, bool nongreedy); + Frag Quest(Frag a, bool nongreedy); + + // Given fragment a, returns (a) capturing as \n. + Frag Capture(Frag a, int n); + + // Given fragments a and b, returns ab; a|b + Frag Cat(Frag a, Frag b); + Frag Alt(Frag a, Frag b); + + // Returns a fragment that can't match anything. + Frag NoMatch(); + + // Returns a fragment that matches the empty string. + Frag Match(int32_t id); + + // Returns a no-op fragment. + Frag Nop(); + + // Returns a fragment matching the byte range lo-hi. + Frag ByteRange(int lo, int hi, bool foldcase); + + // Returns a fragment matching an empty-width special op. + Frag EmptyWidth(EmptyOp op); + + // Adds n instructions to the program. + // Returns the index of the first one. + // Returns -1 if no more instructions are available. + int AllocInst(int n); + + // Rune range compiler. + + // Begins a new alternation. + void BeginRange(); + + // Adds a fragment matching the rune range lo-hi. + void AddRuneRange(Rune lo, Rune hi, bool foldcase); + void AddRuneRangeLatin1(Rune lo, Rune hi, bool foldcase); + void AddRuneRangeUTF8(Rune lo, Rune hi, bool foldcase); + void Add_80_10ffff(); + + // New suffix that matches the byte range lo-hi, then goes to next. + int UncachedRuneByteSuffix(uint8_t lo, uint8_t hi, bool foldcase, int next); + int CachedRuneByteSuffix(uint8_t lo, uint8_t hi, bool foldcase, int next); + + // Returns true iff the suffix is cached. + bool IsCachedRuneByteSuffix(int id); + + // Adds a suffix to alternation. + void AddSuffix(int id); + + // Adds a suffix to the trie starting from the given root node. + // Returns zero iff allocating an instruction fails. Otherwise, returns + // the current root node, which might be different from what was given. + int AddSuffixRecursive(int root, int id); + + // Finds the trie node for the given suffix. Returns a Frag in order to + // distinguish between pointing at the root node directly (end.head == 0) + // and pointing at an Alt's out1 or out (end.head&1 == 1 or 0, respectively). + Frag FindByteRange(int root, int id); + + // Compares two ByteRanges and returns true iff they are equal. + bool ByteRangeEqual(int id1, int id2); + + // Returns the alternation of all the added suffixes. + Frag EndRange(); + + // Single rune. + Frag Literal(Rune r, bool foldcase); + + void Setup(Regexp::ParseFlags flags, int64_t max_mem, RE2::Anchor anchor); + Prog* Finish(Regexp* re); + + // Returns .* where dot = any byte + Frag DotStar(); + + private: + Prog* prog_; // Program being built. + bool failed_; // Did we give up compiling? + Encoding encoding_; // Input encoding + bool reversed_; // Should program run backward over text? + + PODArray inst_; + int ninst_; // Number of instructions used. + int max_ninst_; // Maximum number of instructions. + + int64_t max_mem_; // Total memory budget. + + std::unordered_map rune_cache_; + Frag rune_range_; + + RE2::Anchor anchor_; // anchor mode for RE2::Set + + Compiler(const Compiler&) = delete; + Compiler& operator=(const Compiler&) = delete; +}; + +Compiler::Compiler() { + prog_ = new Prog(); + failed_ = false; + encoding_ = kEncodingUTF8; + reversed_ = false; + ninst_ = 0; + max_ninst_ = 1; // make AllocInst for fail instruction okay + max_mem_ = 0; + int fail = AllocInst(1); + inst_[fail].InitFail(); + max_ninst_ = 0; // Caller must change +} + +Compiler::~Compiler() { + delete prog_; +} + +int Compiler::AllocInst(int n) { + if (failed_ || ninst_ + n > max_ninst_) { + failed_ = true; + return -1; + } + + if (ninst_ + n > inst_.size()) { + int cap = inst_.size(); + if (cap == 0) + cap = 8; + while (ninst_ + n > cap) + cap *= 2; + PODArray inst(cap); + if (inst_.data() != NULL) + memmove(inst.data(), inst_.data(), ninst_*sizeof inst_[0]); + memset(inst.data() + ninst_, 0, (cap - ninst_)*sizeof inst_[0]); + inst_ = std::move(inst); + } + int id = ninst_; + ninst_ += n; + return id; +} + +// These routines are somewhat hard to visualize in text -- +// see http://swtch.com/~rsc/regexp/regexp1.html for +// pictures explaining what is going on here. + +// Returns an unmatchable fragment. +Frag Compiler::NoMatch() { + return Frag(0, kNullPatchList); +} + +// Is a an unmatchable fragment? +static bool IsNoMatch(Frag a) { + return a.begin == 0; +} + +// Given fragments a and b, returns fragment for ab. +Frag Compiler::Cat(Frag a, Frag b) { + if (IsNoMatch(a) || IsNoMatch(b)) + return NoMatch(); + + // Elide no-op. + Prog::Inst* begin = &inst_[a.begin]; + if (begin->opcode() == kInstNop && + a.end.head == (a.begin << 1) && + begin->out() == 0) { + // in case refs to a somewhere + PatchList::Patch(inst_.data(), a.end, b.begin); + return b; + } + + // To run backward over string, reverse all concatenations. + if (reversed_) { + PatchList::Patch(inst_.data(), b.end, a.begin); + return Frag(b.begin, a.end); + } + + PatchList::Patch(inst_.data(), a.end, b.begin); + return Frag(a.begin, b.end); +} + +// Given fragments for a and b, returns fragment for a|b. +Frag Compiler::Alt(Frag a, Frag b) { + // Special case for convenience in loops. + if (IsNoMatch(a)) + return b; + if (IsNoMatch(b)) + return a; + + int id = AllocInst(1); + if (id < 0) + return NoMatch(); + + inst_[id].InitAlt(a.begin, b.begin); + return Frag(id, PatchList::Append(inst_.data(), a.end, b.end)); +} + +// When capturing submatches in like-Perl mode, a kOpAlt Inst +// treats out_ as the first choice, out1_ as the second. +// +// For *, +, and ?, if out_ causes another repetition, +// then the operator is greedy. If out1_ is the repetition +// (and out_ moves forward), then the operator is non-greedy. + +// Given a fragment a, returns a fragment for a* or a*? (if nongreedy) +Frag Compiler::Star(Frag a, bool nongreedy) { + int id = AllocInst(1); + if (id < 0) + return NoMatch(); + inst_[id].InitAlt(0, 0); + PatchList::Patch(inst_.data(), a.end, id); + if (nongreedy) { + inst_[id].out1_ = a.begin; + return Frag(id, PatchList::Mk(id << 1)); + } else { + inst_[id].set_out(a.begin); + return Frag(id, PatchList::Mk((id << 1) | 1)); + } +} + +// Given a fragment for a, returns a fragment for a+ or a+? (if nongreedy) +Frag Compiler::Plus(Frag a, bool nongreedy) { + // a+ is just a* with a different entry point. + Frag f = Star(a, nongreedy); + return Frag(a.begin, f.end); +} + +// Given a fragment for a, returns a fragment for a? or a?? (if nongreedy) +Frag Compiler::Quest(Frag a, bool nongreedy) { + if (IsNoMatch(a)) + return Nop(); + int id = AllocInst(1); + if (id < 0) + return NoMatch(); + PatchList pl; + if (nongreedy) { + inst_[id].InitAlt(0, a.begin); + pl = PatchList::Mk(id << 1); + } else { + inst_[id].InitAlt(a.begin, 0); + pl = PatchList::Mk((id << 1) | 1); + } + return Frag(id, PatchList::Append(inst_.data(), pl, a.end)); +} + +// Returns a fragment for the byte range lo-hi. +Frag Compiler::ByteRange(int lo, int hi, bool foldcase) { + int id = AllocInst(1); + if (id < 0) + return NoMatch(); + inst_[id].InitByteRange(lo, hi, foldcase, 0); + return Frag(id, PatchList::Mk(id << 1)); +} + +// Returns a no-op fragment. Sometimes unavoidable. +Frag Compiler::Nop() { + int id = AllocInst(1); + if (id < 0) + return NoMatch(); + inst_[id].InitNop(0); + return Frag(id, PatchList::Mk(id << 1)); +} + +// Returns a fragment that signals a match. +Frag Compiler::Match(int32_t match_id) { + int id = AllocInst(1); + if (id < 0) + return NoMatch(); + inst_[id].InitMatch(match_id); + return Frag(id, kNullPatchList); +} + +// Returns a fragment matching a particular empty-width op (like ^ or $) +Frag Compiler::EmptyWidth(EmptyOp empty) { + int id = AllocInst(1); + if (id < 0) + return NoMatch(); + inst_[id].InitEmptyWidth(empty, 0); + return Frag(id, PatchList::Mk(id << 1)); +} + +// Given a fragment a, returns a fragment with capturing parens around a. +Frag Compiler::Capture(Frag a, int n) { + if (IsNoMatch(a)) + return NoMatch(); + int id = AllocInst(2); + if (id < 0) + return NoMatch(); + inst_[id].InitCapture(2*n, a.begin); + inst_[id+1].InitCapture(2*n+1, 0); + PatchList::Patch(inst_.data(), a.end, id+1); + + return Frag(id, PatchList::Mk((id+1) << 1)); +} + +// A Rune is a name for a Unicode code point. +// Returns maximum rune encoded by UTF-8 sequence of length len. +static int MaxRune(int len) { + int b; // number of Rune bits in len-byte UTF-8 sequence (len < UTFmax) + if (len == 1) + b = 7; + else + b = 8-(len+1) + 6*(len-1); + return (1<::const_iterator it = rune_cache_.find(key); + if (it != rune_cache_.end()) + return it->second; + int id = UncachedRuneByteSuffix(lo, hi, foldcase, next); + rune_cache_[key] = id; + return id; +} + +bool Compiler::IsCachedRuneByteSuffix(int id) { + uint8_t lo = inst_[id].lo_; + uint8_t hi = inst_[id].hi_; + bool foldcase = inst_[id].foldcase() != 0; + int next = inst_[id].out(); + + uint64_t key = MakeRuneCacheKey(lo, hi, foldcase, next); + return rune_cache_.find(key) != rune_cache_.end(); +} + +void Compiler::AddSuffix(int id) { + if (failed_) + return; + + if (rune_range_.begin == 0) { + rune_range_.begin = id; + return; + } + + if (encoding_ == kEncodingUTF8) { + // Build a trie in order to reduce fanout. + rune_range_.begin = AddSuffixRecursive(rune_range_.begin, id); + return; + } + + int alt = AllocInst(1); + if (alt < 0) { + rune_range_.begin = 0; + return; + } + inst_[alt].InitAlt(rune_range_.begin, id); + rune_range_.begin = alt; +} + +int Compiler::AddSuffixRecursive(int root, int id) { + DCHECK(inst_[root].opcode() == kInstAlt || + inst_[root].opcode() == kInstByteRange); + + Frag f = FindByteRange(root, id); + if (IsNoMatch(f)) { + int alt = AllocInst(1); + if (alt < 0) + return 0; + inst_[alt].InitAlt(root, id); + return alt; + } + + int br; + if (f.end.head == 0) + br = root; + else if (f.end.head&1) + br = inst_[f.begin].out1(); + else + br = inst_[f.begin].out(); + + if (IsCachedRuneByteSuffix(br)) { + // We can't fiddle with cached suffixes, so make a clone of the head. + int byterange = AllocInst(1); + if (byterange < 0) + return 0; + inst_[byterange].InitByteRange(inst_[br].lo(), inst_[br].hi(), + inst_[br].foldcase(), inst_[br].out()); + + // Ensure that the parent points to the clone, not to the original. + // Note that this could leave the head unreachable except via the cache. + br = byterange; + if (f.end.head == 0) + root = br; + else if (f.end.head&1) + inst_[f.begin].out1_ = br; + else + inst_[f.begin].set_out(br); + } + + int out = inst_[id].out(); + if (!IsCachedRuneByteSuffix(id)) { + // The head should be the instruction most recently allocated, so free it + // instead of leaving it unreachable. + DCHECK_EQ(id, ninst_-1); + inst_[id].out_opcode_ = 0; + inst_[id].out1_ = 0; + ninst_--; + } + + out = AddSuffixRecursive(inst_[br].out(), out); + if (out == 0) + return 0; + + inst_[br].set_out(out); + return root; +} + +bool Compiler::ByteRangeEqual(int id1, int id2) { + return inst_[id1].lo() == inst_[id2].lo() && + inst_[id1].hi() == inst_[id2].hi() && + inst_[id1].foldcase() == inst_[id2].foldcase(); +} + +Frag Compiler::FindByteRange(int root, int id) { + if (inst_[root].opcode() == kInstByteRange) { + if (ByteRangeEqual(root, id)) + return Frag(root, kNullPatchList); + else + return NoMatch(); + } + + while (inst_[root].opcode() == kInstAlt) { + int out1 = inst_[root].out1(); + if (ByteRangeEqual(out1, id)) + return Frag(root, PatchList::Mk((root << 1) | 1)); + + // CharClass is a sorted list of ranges, so if out1 of the root Alt wasn't + // what we're looking for, then we can stop immediately. Unfortunately, we + // can't short-circuit the search in reverse mode. + if (!reversed_) + return NoMatch(); + + int out = inst_[root].out(); + if (inst_[out].opcode() == kInstAlt) + root = out; + else if (ByteRangeEqual(out, id)) + return Frag(root, PatchList::Mk(root << 1)); + else + return NoMatch(); + } + +#if 0 + LOG(DFATAL) << "should never happen"; +#endif + return NoMatch(); +} + +Frag Compiler::EndRange() { + return rune_range_; +} + +// Converts rune range lo-hi into a fragment that recognizes +// the bytes that would make up those runes in the current +// encoding (Latin 1 or UTF-8). +// This lets the machine work byte-by-byte even when +// using multibyte encodings. + +void Compiler::AddRuneRange(Rune lo, Rune hi, bool foldcase) { + switch (encoding_) { + default: + case kEncodingUTF8: + AddRuneRangeUTF8(lo, hi, foldcase); + break; + case kEncodingLatin1: + AddRuneRangeLatin1(lo, hi, foldcase); + break; + } +} + +void Compiler::AddRuneRangeLatin1(Rune lo, Rune hi, bool foldcase) { + // Latin-1 is easy: runes *are* bytes. + if (lo > hi || lo > 0xFF) + return; + if (hi > 0xFF) + hi = 0xFF; + AddSuffix(UncachedRuneByteSuffix(static_cast(lo), + static_cast(hi), foldcase, 0)); +} + +void Compiler::Add_80_10ffff() { + // The 80-10FFFF (Runeself-Runemax) rune range occurs frequently enough + // (for example, for /./ and /[^a-z]/) that it is worth simplifying: by + // permitting overlong encodings in E0 and F0 sequences and code points + // over 10FFFF in F4 sequences, the size of the bytecode and the number + // of equivalence classes are reduced significantly. + int id; + if (reversed_) { + // Prefix factoring matters, but we don't have to handle it here + // because the rune range trie logic takes care of that already. + id = UncachedRuneByteSuffix(0xC2, 0xDF, false, 0); + id = UncachedRuneByteSuffix(0x80, 0xBF, false, id); + AddSuffix(id); + + id = UncachedRuneByteSuffix(0xE0, 0xEF, false, 0); + id = UncachedRuneByteSuffix(0x80, 0xBF, false, id); + id = UncachedRuneByteSuffix(0x80, 0xBF, false, id); + AddSuffix(id); + + id = UncachedRuneByteSuffix(0xF0, 0xF4, false, 0); + id = UncachedRuneByteSuffix(0x80, 0xBF, false, id); + id = UncachedRuneByteSuffix(0x80, 0xBF, false, id); + id = UncachedRuneByteSuffix(0x80, 0xBF, false, id); + AddSuffix(id); + } else { + // Suffix factoring matters - and we do have to handle it here. + int cont1 = UncachedRuneByteSuffix(0x80, 0xBF, false, 0); + id = UncachedRuneByteSuffix(0xC2, 0xDF, false, cont1); + AddSuffix(id); + + int cont2 = UncachedRuneByteSuffix(0x80, 0xBF, false, cont1); + id = UncachedRuneByteSuffix(0xE0, 0xEF, false, cont2); + AddSuffix(id); + + int cont3 = UncachedRuneByteSuffix(0x80, 0xBF, false, cont2); + id = UncachedRuneByteSuffix(0xF0, 0xF4, false, cont3); + AddSuffix(id); + } +} + +void Compiler::AddRuneRangeUTF8(Rune lo, Rune hi, bool foldcase) { + if (lo > hi) + return; + + // Pick off 80-10FFFF as a common special case. + if (lo == 0x80 && hi == 0x10ffff) { + Add_80_10ffff(); + return; + } + + // Split range into same-length sized ranges. + for (int i = 1; i < UTFmax; i++) { + Rune max = MaxRune(i); + if (lo <= max && max < hi) { + AddRuneRangeUTF8(lo, max, foldcase); + AddRuneRangeUTF8(max+1, hi, foldcase); + return; + } + } + + // ASCII range is always a special case. + if (hi < Runeself) { + AddSuffix(UncachedRuneByteSuffix(static_cast(lo), + static_cast(hi), foldcase, 0)); + return; + } + + // Split range into sections that agree on leading bytes. + for (int i = 1; i < UTFmax; i++) { + uint32_t m = (1<<(6*i)) - 1; // last i bytes of a UTF-8 sequence + if ((lo & ~m) != (hi & ~m)) { + if ((lo & m) != 0) { + AddRuneRangeUTF8(lo, lo|m, foldcase); + AddRuneRangeUTF8((lo|m)+1, hi, foldcase); + return; + } + if ((hi & m) != m) { + AddRuneRangeUTF8(lo, (hi&~m)-1, foldcase); + AddRuneRangeUTF8(hi&~m, hi, foldcase); + return; + } + } + } + + // Finally. Generate byte matching equivalent for lo-hi. + uint8_t ulo[UTFmax], uhi[UTFmax]; + int n = runetochar(reinterpret_cast(ulo), &lo); + int m = runetochar(reinterpret_cast(uhi), &hi); + (void)m; // USED(m) + DCHECK_EQ(n, m); + + // The logic below encodes this thinking: + // + // 1. When we have built the whole suffix, we know that it cannot + // possibly be a suffix of anything longer: in forward mode, nothing + // else can occur before the leading byte; in reverse mode, nothing + // else can occur after the last continuation byte or else the leading + // byte would have to change. Thus, there is no benefit to caching + // the first byte of the suffix whereas there is a cost involved in + // cloning it if it begins a common prefix, which is fairly likely. + // + // 2. Conversely, the last byte of the suffix cannot possibly be a + // prefix of anything because next == 0, so we will never want to + // clone it, but it is fairly likely to be a common suffix. Perhaps + // more so in reverse mode than in forward mode because the former is + // "converging" towards lower entropy, but caching is still worthwhile + // for the latter in cases such as 80-BF. + // + // 3. Handling the bytes between the first and the last is less + // straightforward and, again, the approach depends on whether we are + // "converging" towards lower entropy: in forward mode, a single byte + // is unlikely to be part of a common suffix whereas a byte range + // is more likely so; in reverse mode, a byte range is unlikely to + // be part of a common suffix whereas a single byte is more likely + // so. The same benefit versus cost argument applies here. + int id = 0; + if (reversed_) { + for (int i = 0; i < n; i++) { + // In reverse UTF-8 mode: cache the leading byte; don't cache the last + // continuation byte; cache anything else iff it's a single byte (XX-XX). + if (i == 0 || (ulo[i] == uhi[i] && i != n-1)) + id = CachedRuneByteSuffix(ulo[i], uhi[i], false, id); + else + id = UncachedRuneByteSuffix(ulo[i], uhi[i], false, id); + } + } else { + for (int i = n-1; i >= 0; i--) { + // In forward UTF-8 mode: don't cache the leading byte; cache the last + // continuation byte; cache anything else iff it's a byte range (XX-YY). + if (i == n-1 || (ulo[i] < uhi[i] && i != 0)) + id = CachedRuneByteSuffix(ulo[i], uhi[i], false, id); + else + id = UncachedRuneByteSuffix(ulo[i], uhi[i], false, id); + } + } + AddSuffix(id); +} + +// Should not be called. +Frag Compiler::Copy(Frag arg) { + // We're using WalkExponential; there should be no copying. +#if 0 + LOG(DFATAL) << "Compiler::Copy called!"; +#endif + failed_ = true; + return NoMatch(); +} + +// Visits a node quickly; called once WalkExponential has +// decided to cut this walk short. +Frag Compiler::ShortVisit(Regexp* re, Frag) { + failed_ = true; + return NoMatch(); +} + +// Called before traversing a node's children during the walk. +Frag Compiler::PreVisit(Regexp* re, Frag, bool* stop) { + // Cut off walk if we've already failed. + if (failed_) + *stop = true; + + return Frag(); // not used by caller +} + +Frag Compiler::Literal(Rune r, bool foldcase) { + switch (encoding_) { + default: + return Frag(); + + case kEncodingLatin1: + return ByteRange(r, r, foldcase); + + case kEncodingUTF8: { + if (r < Runeself) // Make common case fast. + return ByteRange(r, r, foldcase); + uint8_t buf[UTFmax]; + int n = runetochar(reinterpret_cast(buf), &r); + Frag f = ByteRange((uint8_t)buf[0], buf[0], false); + for (int i = 1; i < n; i++) + f = Cat(f, ByteRange((uint8_t)buf[i], buf[i], false)); + return f; + } + } +} + +// Called after traversing the node's children during the walk. +// Given their frags, build and return the frag for this re. +Frag Compiler::PostVisit(Regexp* re, Frag, Frag, Frag* child_frags, + int nchild_frags) { + // If a child failed, don't bother going forward, especially + // since the child_frags might contain Frags with NULLs in them. + if (failed_) + return NoMatch(); + + // Given the child fragments, return the fragment for this node. + switch (re->op()) { + case kRegexpRepeat: + // Should not see; code at bottom of function will print error + break; + + case kRegexpNoMatch: + return NoMatch(); + + case kRegexpEmptyMatch: + return Nop(); + + case kRegexpHaveMatch: { + Frag f = Match(re->match_id()); + if (anchor_ == RE2::ANCHOR_BOTH) { + // Append \z or else the subexpression will effectively be unanchored. + // Complemented by the UNANCHORED case in CompileSet(). + f = Cat(EmptyWidth(kEmptyEndText), f); + } + return f; + } + + case kRegexpConcat: { + Frag f = child_frags[0]; + for (int i = 1; i < nchild_frags; i++) + f = Cat(f, child_frags[i]); + return f; + } + + case kRegexpAlternate: { + Frag f = child_frags[0]; + for (int i = 1; i < nchild_frags; i++) + f = Alt(f, child_frags[i]); + return f; + } + + case kRegexpStar: + return Star(child_frags[0], (re->parse_flags()&Regexp::NonGreedy) != 0); + + case kRegexpPlus: + return Plus(child_frags[0], (re->parse_flags()&Regexp::NonGreedy) != 0); + + case kRegexpQuest: + return Quest(child_frags[0], (re->parse_flags()&Regexp::NonGreedy) != 0); + + case kRegexpLiteral: + return Literal(re->rune(), (re->parse_flags()&Regexp::FoldCase) != 0); + + case kRegexpLiteralString: { + // Concatenation of literals. + if (re->nrunes() == 0) + return Nop(); + Frag f; + for (int i = 0; i < re->nrunes(); i++) { + Frag f1 = Literal(re->runes()[i], + (re->parse_flags()&Regexp::FoldCase) != 0); + if (i == 0) + f = f1; + else + f = Cat(f, f1); + } + return f; + } + + case kRegexpAnyChar: + BeginRange(); + AddRuneRange(0, Runemax, false); + return EndRange(); + + case kRegexpAnyByte: + return ByteRange(0x00, 0xFF, false); + + case kRegexpCharClass: { + CharClass* cc = re->cc(); + if (cc->empty()) { + // This can't happen. +#if 0 + LOG(DFATAL) << "No ranges in char class"; +#endif + failed_ = true; + return NoMatch(); + } + + // ASCII case-folding optimization: if the char class + // behaves the same on A-Z as it does on a-z, + // discard any ranges wholly contained in A-Z + // and mark the other ranges as foldascii. + // This reduces the size of a program for + // (?i)abc from 3 insts per letter to 1 per letter. + bool foldascii = cc->FoldsASCII(); + + // Character class is just a big OR of the different + // character ranges in the class. + BeginRange(); + for (CharClass::iterator i = cc->begin(); i != cc->end(); ++i) { + // ASCII case-folding optimization (see above). + if (foldascii && 'A' <= i->lo && i->hi <= 'Z') + continue; + + // If this range contains all of A-Za-z or none of it, + // the fold flag is unnecessary; don't bother. + bool fold = foldascii; + if ((i->lo <= 'A' && 'z' <= i->hi) || i->hi < 'A' || 'z' < i->lo || + ('Z' < i->lo && i->hi < 'a')) + fold = false; + + AddRuneRange(i->lo, i->hi, fold); + } + return EndRange(); + } + + case kRegexpCapture: + // If this is a non-capturing parenthesis -- (?:foo) -- + // just use the inner expression. + if (re->cap() < 0) + return child_frags[0]; + return Capture(child_frags[0], re->cap()); + + case kRegexpBeginLine: + return EmptyWidth(reversed_ ? kEmptyEndLine : kEmptyBeginLine); + + case kRegexpEndLine: + return EmptyWidth(reversed_ ? kEmptyBeginLine : kEmptyEndLine); + + case kRegexpBeginText: + return EmptyWidth(reversed_ ? kEmptyEndText : kEmptyBeginText); + + case kRegexpEndText: + return EmptyWidth(reversed_ ? kEmptyBeginText : kEmptyEndText); + + case kRegexpWordBoundary: + return EmptyWidth(kEmptyWordBoundary); + + case kRegexpNoWordBoundary: + return EmptyWidth(kEmptyNonWordBoundary); + } +#if 0 + LOG(DFATAL) << "Missing case in Compiler: " << re->op(); +#endif + failed_ = true; + return NoMatch(); +} + +// Is this regexp required to start at the beginning of the text? +// Only approximate; can return false for complicated regexps like (\Aa|\Ab), +// but handles (\A(a|b)). Could use the Walker to write a more exact one. +static bool IsAnchorStart(Regexp** pre, int depth) { + Regexp* re = *pre; + Regexp* sub; + // The depth limit makes sure that we don't overflow + // the stack on a deeply nested regexp. As the comment + // above says, IsAnchorStart is conservative, so returning + // a false negative is okay. The exact limit is somewhat arbitrary. + if (re == NULL || depth >= 4) + return false; + switch (re->op()) { + default: + break; + case kRegexpConcat: + if (re->nsub() > 0) { + sub = re->sub()[0]->Incref(); + if (IsAnchorStart(&sub, depth+1)) { + PODArray subcopy(re->nsub()); + subcopy[0] = sub; // already have reference + for (int i = 1; i < re->nsub(); i++) + subcopy[i] = re->sub()[i]->Incref(); + *pre = Regexp::Concat(subcopy.data(), re->nsub(), re->parse_flags()); + re->Decref(); + return true; + } + sub->Decref(); + } + break; + case kRegexpCapture: + sub = re->sub()[0]->Incref(); + if (IsAnchorStart(&sub, depth+1)) { + *pre = Regexp::Capture(sub, re->parse_flags(), re->cap()); + re->Decref(); + return true; + } + sub->Decref(); + break; + case kRegexpBeginText: + *pre = Regexp::LiteralString(NULL, 0, re->parse_flags()); + re->Decref(); + return true; + } + return false; +} + +// Is this regexp required to start at the end of the text? +// Only approximate; can return false for complicated regexps like (a\z|b\z), +// but handles ((a|b)\z). Could use the Walker to write a more exact one. +static bool IsAnchorEnd(Regexp** pre, int depth) { + Regexp* re = *pre; + Regexp* sub; + // The depth limit makes sure that we don't overflow + // the stack on a deeply nested regexp. As the comment + // above says, IsAnchorEnd is conservative, so returning + // a false negative is okay. The exact limit is somewhat arbitrary. + if (re == NULL || depth >= 4) + return false; + switch (re->op()) { + default: + break; + case kRegexpConcat: + if (re->nsub() > 0) { + sub = re->sub()[re->nsub() - 1]->Incref(); + if (IsAnchorEnd(&sub, depth+1)) { + PODArray subcopy(re->nsub()); + subcopy[re->nsub() - 1] = sub; // already have reference + for (int i = 0; i < re->nsub() - 1; i++) + subcopy[i] = re->sub()[i]->Incref(); + *pre = Regexp::Concat(subcopy.data(), re->nsub(), re->parse_flags()); + re->Decref(); + return true; + } + sub->Decref(); + } + break; + case kRegexpCapture: + sub = re->sub()[0]->Incref(); + if (IsAnchorEnd(&sub, depth+1)) { + *pre = Regexp::Capture(sub, re->parse_flags(), re->cap()); + re->Decref(); + return true; + } + sub->Decref(); + break; + case kRegexpEndText: + *pre = Regexp::LiteralString(NULL, 0, re->parse_flags()); + re->Decref(); + return true; + } + return false; +} + +void Compiler::Setup(Regexp::ParseFlags flags, int64_t max_mem, + RE2::Anchor anchor) { + if (flags & Regexp::Latin1) + encoding_ = kEncodingLatin1; + max_mem_ = max_mem; + if (max_mem <= 0) { + max_ninst_ = 100000; // more than enough + } else if (static_cast(max_mem) <= sizeof(Prog)) { + // No room for anything. + max_ninst_ = 0; + } else { + int64_t m = (max_mem - sizeof(Prog)) / sizeof(Prog::Inst); + // Limit instruction count so that inst->id() fits nicely in an int. + // SparseArray also assumes that the indices (inst->id()) are ints. + // The call to WalkExponential uses 2*max_ninst_ below, + // and other places in the code use 2 or 3 * prog->size(). + // Limiting to 2^24 should avoid overflow in those places. + // (The point of allowing more than 32 bits of memory is to + // have plenty of room for the DFA states, not to use it up + // on the program.) + if (m >= 1<<24) + m = 1<<24; + // Inst imposes its own limit (currently bigger than 2^24 but be safe). + if (m > Prog::Inst::kMaxInst) + m = Prog::Inst::kMaxInst; + max_ninst_ = static_cast(m); + } + anchor_ = anchor; +} + +// Compiles re, returning program. +// Caller is responsible for deleting prog_. +// If reversed is true, compiles a program that expects +// to run over the input string backward (reverses all concatenations). +// The reversed flag is also recorded in the returned program. +Prog* Compiler::Compile(Regexp* re, bool reversed, int64_t max_mem) { + Compiler c; + c.Setup(re->parse_flags(), max_mem, RE2::UNANCHORED /* unused */); + c.reversed_ = reversed; + + // Simplify to remove things like counted repetitions + // and character classes like \d. + Regexp* sre = re->Simplify(); + if (sre == NULL) + return NULL; + + // Record whether prog is anchored, removing the anchors. + // (They get in the way of other optimizations.) + bool is_anchor_start = IsAnchorStart(&sre, 0); + bool is_anchor_end = IsAnchorEnd(&sre, 0); + + // Generate fragment for entire regexp. + Frag all = c.WalkExponential(sre, Frag(), 2*c.max_ninst_); + sre->Decref(); + if (c.failed_) + return NULL; + + // Success! Finish by putting Match node at end, and record start. + // Turn off c.reversed_ (if it is set) to force the remaining concatenations + // to behave normally. + c.reversed_ = false; + all = c.Cat(all, c.Match(0)); + + c.prog_->set_reversed(reversed); + if (c.prog_->reversed()) { + c.prog_->set_anchor_start(is_anchor_end); + c.prog_->set_anchor_end(is_anchor_start); + } else { + c.prog_->set_anchor_start(is_anchor_start); + c.prog_->set_anchor_end(is_anchor_end); + } + + c.prog_->set_start(all.begin); + if (!c.prog_->anchor_start()) { + // Also create unanchored version, which starts with a .*? loop. + all = c.Cat(c.DotStar(), all); + } + c.prog_->set_start_unanchored(all.begin); + + // Hand ownership of prog_ to caller. + return c.Finish(re); +} + +Prog* Compiler::Finish(Regexp* re) { + if (failed_) + return NULL; + + if (prog_->start() == 0 && prog_->start_unanchored() == 0) { + // No possible matches; keep Fail instruction only. + ninst_ = 1; + } + + // Hand off the array to Prog. + prog_->inst_ = std::move(inst_); + prog_->size_ = ninst_; + + prog_->Optimize(); + prog_->Flatten(); + prog_->ComputeByteMap(); + + if (!prog_->reversed()) { + std::string prefix; + bool prefix_foldcase; + if (re->RequiredPrefixForAccel(&prefix, &prefix_foldcase) && + !prefix_foldcase) { + prog_->prefix_size_ = prefix.size(); + prog_->prefix_front_ = prefix.front(); + prog_->prefix_back_ = prefix.back(); + } + } + + // Record remaining memory for DFA. + if (max_mem_ <= 0) { + prog_->set_dfa_mem(1<<20); + } else { + int64_t m = max_mem_ - sizeof(Prog); + m -= prog_->size_*sizeof(Prog::Inst); // account for inst_ + if (prog_->CanBitState()) + m -= prog_->size_*sizeof(uint16_t); // account for list_heads_ + if (m < 0) + m = 0; + prog_->set_dfa_mem(m); + } + + Prog* p = prog_; + prog_ = NULL; + return p; +} + +// Converts Regexp to Prog. +Prog* Regexp::CompileToProg(int64_t max_mem) { + return Compiler::Compile(this, false, max_mem); +} + +Prog* Regexp::CompileToReverseProg(int64_t max_mem) { + return Compiler::Compile(this, true, max_mem); +} + +Frag Compiler::DotStar() { + return Star(ByteRange(0x00, 0xff, false), true); +} + +// Compiles RE set to Prog. +Prog* Compiler::CompileSet(Regexp* re, RE2::Anchor anchor, int64_t max_mem) { + Compiler c; + c.Setup(re->parse_flags(), max_mem, anchor); + + Regexp* sre = re->Simplify(); + if (sre == NULL) + return NULL; + + Frag all = c.WalkExponential(sre, Frag(), 2*c.max_ninst_); + sre->Decref(); + if (c.failed_) + return NULL; + + c.prog_->set_anchor_start(true); + c.prog_->set_anchor_end(true); + + if (anchor == RE2::UNANCHORED) { + // Prepend .* or else the expression will effectively be anchored. + // Complemented by the ANCHOR_BOTH case in PostVisit(). + all = c.Cat(c.DotStar(), all); + } + c.prog_->set_start(all.begin); + c.prog_->set_start_unanchored(all.begin); + + Prog* prog = c.Finish(re); + if (prog == NULL) + return NULL; + + // Make sure DFA has enough memory to operate, + // since we're not going to fall back to the NFA. + bool dfa_failed = false; + StringPiece sp = "hello, world"; + prog->SearchDFA(sp, sp, Prog::kAnchored, Prog::kManyMatch, + NULL, &dfa_failed, NULL); + if (dfa_failed) { + delete prog; + return NULL; + } + + return prog; +} + +Prog* Prog::CompileSet(Regexp* re, RE2::Anchor anchor, int64_t max_mem) { + return Compiler::CompileSet(re, anchor, max_mem); +} + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/dfa.cc b/third_party/opa/wasm/src/re2/re2/dfa.cc new file mode 100644 index 000000000000..b55adf28440b --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/dfa.cc @@ -0,0 +1,2135 @@ +// Copyright 2008 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// A DFA (deterministic finite automaton)-based regular expression search. +// +// The DFA search has two main parts: the construction of the automaton, +// which is represented by a graph of State structures, and the execution +// of the automaton over a given input string. +// +// The basic idea is that the State graph is constructed so that the +// execution can simply start with a state s, and then for each byte c in +// the input string, execute "s = s->next[c]", checking at each point whether +// the current s represents a matching state. +// +// The simple explanation just given does convey the essence of this code, +// but it omits the details of how the State graph gets constructed as well +// as some performance-driven optimizations to the execution of the automaton. +// All these details are explained in the comments for the code following +// the definition of class DFA. +// +// See http://swtch.com/~rsc/regexp/ for a very bare-bones equivalent. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/logging.h" +#include "util/mix.h" +#include "util/mutex.h" +#include "util/strutil.h" +#include "re2/pod_array.h" +#include "re2/prog.h" +#include "re2/re2.h" +#include "re2/sparse_set.h" +#include "re2/stringpiece.h" + +// Silence "zero-sized array in struct/union" warning for DFA::State::next_. +#ifdef _MSC_VER +#pragma warning(disable: 4200) +#endif + +namespace re2 { + +// Controls whether the DFA should bail out early if the NFA would be faster. +static bool dfa_should_bail_when_slow = true; + +// Changing this to true compiles in prints that trace execution of the DFA. +// Generates a lot of output -- only useful for debugging. +static const bool ExtraDebug = false; + +// A DFA implementation of a regular expression program. +// Since this is entirely a forward declaration mandated by C++, +// some of the comments here are better understood after reading +// the comments in the sections that follow the DFA definition. +class DFA { + public: + DFA(Prog* prog, Prog::MatchKind kind, int64_t max_mem); + ~DFA(); + bool ok() const { return !init_failed_; } + Prog::MatchKind kind() { return kind_; } + + // Searches for the regular expression in text, which is considered + // as a subsection of context for the purposes of interpreting flags + // like ^ and $ and \A and \z. + // Returns whether a match was found. + // If a match is found, sets *ep to the end point of the best match in text. + // If "anchored", the match must begin at the start of text. + // If "want_earliest_match", the match that ends first is used, not + // necessarily the best one. + // If "run_forward" is true, the DFA runs from text.begin() to text.end(). + // If it is false, the DFA runs from text.end() to text.begin(), + // returning the leftmost end of the match instead of the rightmost one. + // If the DFA cannot complete the search (for example, if it is out of + // memory), it sets *failed and returns false. + bool Search(const StringPiece& text, const StringPiece& context, + bool anchored, bool want_earliest_match, bool run_forward, + bool* failed, const char** ep, SparseSet* matches); + + // Builds out all states for the entire DFA. + // If cb is not empty, it receives one callback per state built. + // Returns the number of states built. + // FOR TESTING OR EXPERIMENTAL PURPOSES ONLY. + int BuildAllStates(const Prog::DFAStateCallback& cb); + + // Computes min and max for matching strings. Won't return strings + // bigger than maxlen. + bool PossibleMatchRange(std::string* min, std::string* max, int maxlen); + + // These data structures are logically private, but C++ makes it too + // difficult to mark them as such. + class RWLocker; + class StateSaver; + class Workq; + + // A single DFA state. The DFA is represented as a graph of these + // States, linked by the next_ pointers. If in state s and reading + // byte c, the next state should be s->next_[c]. + struct State { + inline bool IsMatch() const { return (flag_ & kFlagMatch) != 0; } + + int* inst_; // Instruction pointers in the state. + int ninst_; // # of inst_ pointers. + uint32_t flag_; // Empty string bitfield flags in effect on the way + // into this state, along with kFlagMatch if this + // is a matching state. + +// Work around the bug affecting flexible array members in GCC 6.x (for x >= 1). +// (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70932) +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ == 6 && __GNUC_MINOR__ >= 1 + std::atomic next_[0]; // Outgoing arrows from State, +#else + std::atomic next_[]; // Outgoing arrows from State, +#endif + + // one per input byte class + }; + + enum { + kByteEndText = 256, // imaginary byte at end of text + + kFlagEmptyMask = 0xFF, // State.flag_: bits holding kEmptyXXX flags + kFlagMatch = 0x0100, // State.flag_: this is a matching state + kFlagLastWord = 0x0200, // State.flag_: last byte was a word char + kFlagNeedShift = 16, // needed kEmpty bits are or'ed in shifted left + }; + + struct StateHash { + size_t operator()(const State* a) const { + DCHECK(a != NULL); + HashMix mix(a->flag_); + for (int i = 0; i < a->ninst_; i++) + mix.Mix(a->inst_[i]); + mix.Mix(0); + return mix.get(); + } + }; + + struct StateEqual { + bool operator()(const State* a, const State* b) const { + DCHECK(a != NULL); + DCHECK(b != NULL); + if (a == b) + return true; + if (a->flag_ != b->flag_) + return false; + if (a->ninst_ != b->ninst_) + return false; + for (int i = 0; i < a->ninst_; i++) + if (a->inst_[i] != b->inst_[i]) + return false; + return true; + } + }; + + typedef std::unordered_set StateSet; + + private: + enum { + // Indices into start_ for unanchored searches. + // Add kStartAnchored for anchored searches. + kStartBeginText = 0, // text at beginning of context + kStartBeginLine = 2, // text at beginning of line + kStartAfterWordChar = 4, // text follows a word character + kStartAfterNonWordChar = 6, // text follows non-word character + kMaxStart = 8, + + kStartAnchored = 1, + }; + + // Resets the DFA State cache, flushing all saved State* information. + // Releases and reacquires cache_mutex_ via cache_lock, so any + // State* existing before the call are not valid after the call. + // Use a StateSaver to preserve important states across the call. + // cache_mutex_.r <= L < mutex_ + // After: cache_mutex_.w <= L < mutex_ + void ResetCache(RWLocker* cache_lock); + + // Looks up and returns the State corresponding to a Workq. + // L >= mutex_ + State* WorkqToCachedState(Workq* q, Workq* mq, uint32_t flag); + + // Looks up and returns a State matching the inst, ninst, and flag. + // L >= mutex_ + State* CachedState(int* inst, int ninst, uint32_t flag); + + // Clear the cache entirely. + // Must hold cache_mutex_.w or be in destructor. + void ClearCache(); + + // Converts a State into a Workq: the opposite of WorkqToCachedState. + // L >= mutex_ + void StateToWorkq(State* s, Workq* q); + + // Runs a State on a given byte, returning the next state. + State* RunStateOnByteUnlocked(State*, int); // cache_mutex_.r <= L < mutex_ + State* RunStateOnByte(State*, int); // L >= mutex_ + + // Runs a Workq on a given byte followed by a set of empty-string flags, + // producing a new Workq in nq. If a match instruction is encountered, + // sets *ismatch to true. + // L >= mutex_ + void RunWorkqOnByte(Workq* q, Workq* nq, + int c, uint32_t flag, bool* ismatch); + + // Runs a Workq on a set of empty-string flags, producing a new Workq in nq. + // L >= mutex_ + void RunWorkqOnEmptyString(Workq* q, Workq* nq, uint32_t flag); + + // Adds the instruction id to the Workq, following empty arrows + // according to flag. + // L >= mutex_ + void AddToQueue(Workq* q, int id, uint32_t flag); + + // For debugging, returns a text representation of State. + static std::string DumpState(State* state); + + // For debugging, returns a text representation of a Workq. + static std::string DumpWorkq(Workq* q); + + // Search parameters + struct SearchParams { + SearchParams(const StringPiece& text, const StringPiece& context, + RWLocker* cache_lock) + : text(text), + context(context), + anchored(false), + can_prefix_accel(false), + want_earliest_match(false), + run_forward(false), + start(NULL), + cache_lock(cache_lock), + failed(false), + ep(NULL), + matches(NULL) {} + + StringPiece text; + StringPiece context; + bool anchored; + bool can_prefix_accel; + bool want_earliest_match; + bool run_forward; + State* start; + RWLocker* cache_lock; + bool failed; // "out" parameter: whether search gave up + const char* ep; // "out" parameter: end pointer for match + SparseSet* matches; + + private: + SearchParams(const SearchParams&) = delete; + SearchParams& operator=(const SearchParams&) = delete; + }; + + // Before each search, the parameters to Search are analyzed by + // AnalyzeSearch to determine the state in which to start. + struct StartInfo { + StartInfo() : start(NULL) {} + std::atomic start; + }; + + // Fills in params->start and params->can_prefix_accel using + // the other search parameters. Returns true on success, + // false on failure. + // cache_mutex_.r <= L < mutex_ + bool AnalyzeSearch(SearchParams* params); + bool AnalyzeSearchHelper(SearchParams* params, StartInfo* info, + uint32_t flags); + + // The generic search loop, inlined to create specialized versions. + // cache_mutex_.r <= L < mutex_ + // Might unlock and relock cache_mutex_ via params->cache_lock. + template + inline bool InlinedSearchLoop(SearchParams* params); + + // The specialized versions of InlinedSearchLoop. The three letters + // at the ends of the name denote the true/false values used as the + // last three parameters of InlinedSearchLoop. + // cache_mutex_.r <= L < mutex_ + // Might unlock and relock cache_mutex_ via params->cache_lock. + bool SearchFFF(SearchParams* params); + bool SearchFFT(SearchParams* params); + bool SearchFTF(SearchParams* params); + bool SearchFTT(SearchParams* params); + bool SearchTFF(SearchParams* params); + bool SearchTFT(SearchParams* params); + bool SearchTTF(SearchParams* params); + bool SearchTTT(SearchParams* params); + + // The main search loop: calls an appropriate specialized version of + // InlinedSearchLoop. + // cache_mutex_.r <= L < mutex_ + // Might unlock and relock cache_mutex_ via params->cache_lock. + bool FastSearchLoop(SearchParams* params); + + + // Looks up bytes in bytemap_ but handles case c == kByteEndText too. + int ByteMap(int c) { + if (c == kByteEndText) + return prog_->bytemap_range(); + return prog_->bytemap()[c]; + } + + // Constant after initialization. + Prog* prog_; // The regular expression program to run. + Prog::MatchKind kind_; // The kind of DFA. + bool init_failed_; // initialization failed (out of memory) + + Mutex mutex_; // mutex_ >= cache_mutex_.r + + // Scratch areas, protected by mutex_. + Workq* q0_; // Two pre-allocated work queues. + Workq* q1_; + PODArray stack_; // Pre-allocated stack for AddToQueue + + // State* cache. Many threads use and add to the cache simultaneously, + // holding cache_mutex_ for reading and mutex_ (above) when adding. + // If the cache fills and needs to be discarded, the discarding is done + // while holding cache_mutex_ for writing, to avoid interrupting other + // readers. Any State* pointers are only valid while cache_mutex_ + // is held. + Mutex cache_mutex_; + int64_t mem_budget_; // Total memory budget for all States. + int64_t state_budget_; // Amount of memory remaining for new States. + StateSet state_cache_; // All States computed so far. + StartInfo start_[kMaxStart]; + + DFA(const DFA&) = delete; + DFA& operator=(const DFA&) = delete; +}; + +// Shorthand for casting to uint8_t*. +static inline const uint8_t* BytePtr(const void* v) { + return reinterpret_cast(v); +} + +// Work queues + +// Marks separate thread groups of different priority +// in the work queue when in leftmost-longest matching mode. +#define Mark (-1) + +// Separates the match IDs from the instructions in inst_. +// Used only for "many match" DFA states. +#define MatchSep (-2) + +// Internally, the DFA uses a sparse array of +// program instruction pointers as a work queue. +// In leftmost longest mode, marks separate sections +// of workq that started executing at different +// locations in the string (earlier locations first). +class DFA::Workq : public SparseSet { + public: + // Constructor: n is number of normal slots, maxmark number of mark slots. + Workq(int n, int maxmark) : + SparseSet(n+maxmark), + n_(n), + maxmark_(maxmark), + nextmark_(n), + last_was_mark_(true) { + } + + bool is_mark(int i) { return i >= n_; } + + int maxmark() { return maxmark_; } + + void clear() { + SparseSet::clear(); + nextmark_ = n_; + } + + void mark() { + if (last_was_mark_) + return; + last_was_mark_ = false; + SparseSet::insert_new(nextmark_++); + } + + int size() { + return n_ + maxmark_; + } + + void insert(int id) { + if (contains(id)) + return; + insert_new(id); + } + + void insert_new(int id) { + last_was_mark_ = false; + SparseSet::insert_new(id); + } + + private: + int n_; // size excluding marks + int maxmark_; // maximum number of marks + int nextmark_; // id of next mark + bool last_was_mark_; // last inserted was mark + + Workq(const Workq&) = delete; + Workq& operator=(const Workq&) = delete; +}; + +DFA::DFA(Prog* prog, Prog::MatchKind kind, int64_t max_mem) + : prog_(prog), + kind_(kind), + init_failed_(false), + q0_(NULL), + q1_(NULL), + mem_budget_(max_mem) { + if (ExtraDebug) + fprintf(stderr, "\nkind %d\n%s\n", kind_, prog_->DumpUnanchored().c_str()); + int nmark = 0; + if (kind_ == Prog::kLongestMatch) + nmark = prog_->size(); + // See DFA::AddToQueue() for why this is so. + int nstack = prog_->inst_count(kInstCapture) + + prog_->inst_count(kInstEmptyWidth) + + prog_->inst_count(kInstNop) + + nmark + 1; // + 1 for start inst + + // Account for space needed for DFA, q0, q1, stack. + mem_budget_ -= sizeof(DFA); + mem_budget_ -= (prog_->size() + nmark) * + (sizeof(int)+sizeof(int)) * 2; // q0, q1 + mem_budget_ -= nstack * sizeof(int); // stack + if (mem_budget_ < 0) { + init_failed_ = true; + return; + } + + state_budget_ = mem_budget_; + + // Make sure there is a reasonable amount of working room left. + // At minimum, the search requires room for two states in order + // to limp along, restarting frequently. We'll get better performance + // if there is room for a larger number of states, say 20. + // Note that a state stores list heads only, so we use the program + // list count for the upper bound, not the program size. + int nnext = prog_->bytemap_range() + 1; // + 1 for kByteEndText slot + int64_t one_state = sizeof(State) + nnext*sizeof(std::atomic) + + (prog_->list_count()+nmark)*sizeof(int); + if (state_budget_ < 20*one_state) { + init_failed_ = true; + return; + } + + q0_ = new Workq(prog_->size(), nmark); + q1_ = new Workq(prog_->size(), nmark); + stack_ = PODArray(nstack); +} + +DFA::~DFA() { + delete q0_; + delete q1_; + ClearCache(); +} + +// In the DFA state graph, s->next[c] == NULL means that the +// state has not yet been computed and needs to be. We need +// a different special value to signal that s->next[c] is a +// state that can never lead to a match (and thus the search +// can be called off). Hence DeadState. +#define DeadState reinterpret_cast(1) + +// Signals that the rest of the string matches no matter what it is. +#define FullMatchState reinterpret_cast(2) + +#define SpecialStateMax FullMatchState + +// Debugging printouts + +// For debugging, returns a string representation of the work queue. +std::string DFA::DumpWorkq(Workq* q) { + std::string s; + const char* sep = ""; + for (Workq::iterator it = q->begin(); it != q->end(); ++it) { + if (q->is_mark(*it)) { + s += "|"; + sep = ""; + } else { + s += StringPrintf("%s%d", sep, *it); + sep = ","; + } + } + return s; +} + +// For debugging, returns a string representation of the state. +std::string DFA::DumpState(State* state) { + if (state == NULL) + return "_"; + if (state == DeadState) + return "X"; + if (state == FullMatchState) + return "*"; + std::string s; + const char* sep = ""; + s += StringPrintf("(%p)", state); + for (int i = 0; i < state->ninst_; i++) { + if (state->inst_[i] == Mark) { + s += "|"; + sep = ""; + } else if (state->inst_[i] == MatchSep) { + s += "||"; + sep = ""; + } else { + s += StringPrintf("%s%d", sep, state->inst_[i]); + sep = ","; + } + } + s += StringPrintf(" flag=%#x", state->flag_); + return s; +} + +////////////////////////////////////////////////////////////////////// +// +// DFA state graph construction. +// +// The DFA state graph is a heavily-linked collection of State* structures. +// The state_cache_ is a set of all the State structures ever allocated, +// so that if the same state is reached by two different paths, +// the same State structure can be used. This reduces allocation +// requirements and also avoids duplication of effort across the two +// identical states. +// +// A State is defined by an ordered list of instruction ids and a flag word. +// +// The choice of an ordered list of instructions differs from a typical +// textbook DFA implementation, which would use an unordered set. +// Textbook descriptions, however, only care about whether +// the DFA matches, not where it matches in the text. To decide where the +// DFA matches, we need to mimic the behavior of the dominant backtracking +// implementations like PCRE, which try one possible regular expression +// execution, then another, then another, stopping when one of them succeeds. +// The DFA execution tries these many executions in parallel, representing +// each by an instruction id. These pointers are ordered in the State.inst_ +// list in the same order that the executions would happen in a backtracking +// search: if a match is found during execution of inst_[2], inst_[i] for i>=3 +// can be discarded. +// +// Textbooks also typically do not consider context-aware empty string operators +// like ^ or $. These are handled by the flag word, which specifies the set +// of empty-string operators that should be matched when executing at the +// current text position. These flag bits are defined in prog.h. +// The flag word also contains two DFA-specific bits: kFlagMatch if the state +// is a matching state (one that reached a kInstMatch in the program) +// and kFlagLastWord if the last processed byte was a word character, for the +// implementation of \B and \b. +// +// The flag word also contains, shifted up 16 bits, the bits looked for by +// any kInstEmptyWidth instructions in the state. These provide a useful +// summary indicating when new flags might be useful. +// +// The permanent representation of a State's instruction ids is just an array, +// but while a state is being analyzed, these instruction ids are represented +// as a Workq, which is an array that allows iteration in insertion order. + +// NOTE(rsc): The choice of State construction determines whether the DFA +// mimics backtracking implementations (so-called leftmost first matching) or +// traditional DFA implementations (so-called leftmost longest matching as +// prescribed by POSIX). This implementation chooses to mimic the +// backtracking implementations, because we want to replace PCRE. To get +// POSIX behavior, the states would need to be considered not as a simple +// ordered list of instruction ids, but as a list of unordered sets of instruction +// ids. A match by a state in one set would inhibit the running of sets +// farther down the list but not other instruction ids in the same set. Each +// set would correspond to matches beginning at a given point in the string. +// This is implemented by separating different sets with Mark pointers. + +// Looks in the State cache for a State matching q, flag. +// If one is found, returns it. If one is not found, allocates one, +// inserts it in the cache, and returns it. +// If mq is not null, MatchSep and the match IDs in mq will be appended +// to the State. +DFA::State* DFA::WorkqToCachedState(Workq* q, Workq* mq, uint32_t flag) { + //mutex_.AssertHeld(); + + // Construct array of instruction ids for the new state. + // Only ByteRange, EmptyWidth, and Match instructions are useful to keep: + // those are the only operators with any effect in + // RunWorkqOnEmptyString or RunWorkqOnByte. + PODArray inst(q->size()); + int n = 0; + uint32_t needflags = 0; // flags needed by kInstEmptyWidth instructions + bool sawmatch = false; // whether queue contains guaranteed kInstMatch + bool sawmark = false; // whether queue contains a Mark + if (ExtraDebug) + fprintf(stderr, "WorkqToCachedState %s [%#x]", DumpWorkq(q).c_str(), flag); + for (Workq::iterator it = q->begin(); it != q->end(); ++it) { + int id = *it; + if (sawmatch && (kind_ == Prog::kFirstMatch || q->is_mark(id))) + break; + if (q->is_mark(id)) { + if (n > 0 && inst[n-1] != Mark) { + sawmark = true; + inst[n++] = Mark; + } + continue; + } + Prog::Inst* ip = prog_->inst(id); + switch (ip->opcode()) { + case kInstAltMatch: + // This state will continue to a match no matter what + // the rest of the input is. If it is the highest priority match + // being considered, return the special FullMatchState + // to indicate that it's all matches from here out. + if (kind_ != Prog::kManyMatch && + (kind_ != Prog::kFirstMatch || + (it == q->begin() && ip->greedy(prog_))) && + (kind_ != Prog::kLongestMatch || !sawmark) && + (flag & kFlagMatch)) { + if (ExtraDebug) + fprintf(stderr, " -> FullMatchState\n"); + return FullMatchState; + } + FALLTHROUGH_INTENDED; + default: + // Record iff id is the head of its list, which must + // be the case if id-1 is the last of *its* list. :) + if (prog_->inst(id-1)->last()) + inst[n++] = *it; + if (ip->opcode() == kInstEmptyWidth) + needflags |= ip->empty(); + if (ip->opcode() == kInstMatch && !prog_->anchor_end()) + sawmatch = true; + break; + } + } + DCHECK_LE(n, q->size()); + if (n > 0 && inst[n-1] == Mark) + n--; + + // If there are no empty-width instructions waiting to execute, + // then the extra flag bits will not be used, so there is no + // point in saving them. (Discarding them reduces the number + // of distinct states.) + if (needflags == 0) + flag &= kFlagMatch; + + // NOTE(rsc): The code above cannot do flag &= needflags, + // because if the right flags were present to pass the current + // kInstEmptyWidth instructions, new kInstEmptyWidth instructions + // might be reached that in turn need different flags. + // The only sure thing is that if there are no kInstEmptyWidth + // instructions at all, no flags will be needed. + // We could do the extra work to figure out the full set of + // possibly needed flags by exploring past the kInstEmptyWidth + // instructions, but the check above -- are any flags needed + // at all? -- handles the most common case. More fine-grained + // analysis can only be justified by measurements showing that + // too many redundant states are being allocated. + + // If there are no Insts in the list, it's a dead state, + // which is useful to signal with a special pointer so that + // the execution loop can stop early. This is only okay + // if the state is *not* a matching state. + if (n == 0 && flag == 0) { + if (ExtraDebug) + fprintf(stderr, " -> DeadState\n"); + return DeadState; + } + + // If we're in longest match mode, the state is a sequence of + // unordered state sets separated by Marks. Sort each set + // to canonicalize, to reduce the number of distinct sets stored. + if (kind_ == Prog::kLongestMatch) { + int* ip = inst.data(); + int* ep = ip + n; + while (ip < ep) { + int* markp = ip; + while (markp < ep && *markp != Mark) + markp++; + std::sort(ip, markp); + if (markp < ep) + markp++; + ip = markp; + } + } + + // If we're in many match mode, canonicalize for similar reasons: + // we have an unordered set of states (i.e. we don't have Marks) + // and sorting will reduce the number of distinct sets stored. + if (kind_ == Prog::kManyMatch) { + int* ip = inst.data(); + int* ep = ip + n; + std::sort(ip, ep); + } + + // Append MatchSep and the match IDs in mq if necessary. + if (mq != NULL) { + inst[n++] = MatchSep; + for (Workq::iterator i = mq->begin(); i != mq->end(); ++i) { + int id = *i; + Prog::Inst* ip = prog_->inst(id); + if (ip->opcode() == kInstMatch) + inst[n++] = ip->match_id(); + } + } + + // Save the needed empty-width flags in the top bits for use later. + flag |= needflags << kFlagNeedShift; + + State* state = CachedState(inst.data(), n, flag); + return state; +} + +// Looks in the State cache for a State matching inst, ninst, flag. +// If one is found, returns it. If one is not found, allocates one, +// inserts it in the cache, and returns it. +DFA::State* DFA::CachedState(int* inst, int ninst, uint32_t flag) { + //mutex_.AssertHeld(); + + // Look in the cache for a pre-existing state. + // We have to initialise the struct like this because otherwise + // MSVC will complain about the flexible array member. :( + State state; + state.inst_ = inst; + state.ninst_ = ninst; + state.flag_ = flag; + StateSet::iterator it = state_cache_.find(&state); + if (it != state_cache_.end()) { + if (ExtraDebug) + fprintf(stderr, " -cached-> %s\n", DumpState(*it).c_str()); + return *it; + } + + // Must have enough memory for new state. + // In addition to what we're going to allocate, + // the state cache hash table seems to incur about 40 bytes per + // State*, empirically. + const int kStateCacheOverhead = 40; + int nnext = prog_->bytemap_range() + 1; // + 1 for kByteEndText slot + int mem = sizeof(State) + nnext*sizeof(std::atomic) + + ninst*sizeof(int); + if (mem_budget_ < mem + kStateCacheOverhead) { + mem_budget_ = -1; + return NULL; + } + mem_budget_ -= mem + kStateCacheOverhead; + + // Allocate new state along with room for next_ and inst_. + char* space = std::allocator().allocate(mem); + State* s = new (space) State; + (void) new (s->next_) std::atomic[nnext]; + // Work around a unfortunate bug in older versions of libstdc++. + // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64658) + for (int i = 0; i < nnext; i++) + (void) new (s->next_ + i) std::atomic(NULL); + s->inst_ = new (s->next_ + nnext) int[ninst]; + memmove(s->inst_, inst, ninst*sizeof s->inst_[0]); + s->ninst_ = ninst; + s->flag_ = flag; + if (ExtraDebug) + fprintf(stderr, " -> %s\n", DumpState(s).c_str()); + + // Put state in cache and return it. + state_cache_.insert(s); + return s; +} + +// Clear the cache. Must hold cache_mutex_.w or be in destructor. +void DFA::ClearCache() { + StateSet::iterator begin = state_cache_.begin(); + StateSet::iterator end = state_cache_.end(); + while (begin != end) { + StateSet::iterator tmp = begin; + ++begin; + // Deallocate the blob of memory that we allocated in DFA::CachedState(). + // We recompute mem in order to benefit from sized delete where possible. + int ninst = (*tmp)->ninst_; + int nnext = prog_->bytemap_range() + 1; // + 1 for kByteEndText slot + int mem = sizeof(State) + nnext*sizeof(std::atomic) + + ninst*sizeof(int); + std::allocator().deallocate(reinterpret_cast(*tmp), mem); + } + state_cache_.clear(); +} + +// Copies insts in state s to the work queue q. +void DFA::StateToWorkq(State* s, Workq* q) { + q->clear(); + for (int i = 0; i < s->ninst_; i++) { + if (s->inst_[i] == Mark) { + q->mark(); + } else if (s->inst_[i] == MatchSep) { + // Nothing after this is an instruction! + break; + } else { + // Explore from the head of the list. + AddToQueue(q, s->inst_[i], s->flag_ & kFlagEmptyMask); + } + } +} + +// Adds ip to the work queue, following empty arrows according to flag. +void DFA::AddToQueue(Workq* q, int id, uint32_t flag) { + + // Use stack_ to hold our stack of instructions yet to process. + // It was preallocated as follows: + // one entry per Capture; + // one entry per EmptyWidth; and + // one entry per Nop. + // This reflects the maximum number of stack pushes that each can + // perform. (Each instruction can be processed at most once.) + // When using marks, we also added nmark == prog_->size(). + // (Otherwise, nmark == 0.) + int* stk = stack_.data(); + int nstk = 0; + + stk[nstk++] = id; + while (nstk > 0) { + DCHECK_LE(nstk, stack_.size()); + id = stk[--nstk]; + + Loop: + if (id == Mark) { + q->mark(); + continue; + } + + if (id == 0) + continue; + + // If ip is already on the queue, nothing to do. + // Otherwise add it. We don't actually keep all the + // ones that get added, but adding all of them here + // increases the likelihood of q->contains(id), + // reducing the amount of duplicated work. + if (q->contains(id)) + continue; + q->insert_new(id); + + // Process instruction. + Prog::Inst* ip = prog_->inst(id); + switch (ip->opcode()) { + default: +#if 0 + LOG(DFATAL) << "unhandled opcode: " << ip->opcode(); +#endif + break; + + case kInstByteRange: // just save these on the queue + case kInstMatch: + if (ip->last()) + break; + id = id+1; + goto Loop; + + case kInstCapture: // DFA treats captures as no-ops. + case kInstNop: + if (!ip->last()) + stk[nstk++] = id+1; + + // If this instruction is the [00-FF]* loop at the beginning of + // a leftmost-longest unanchored search, separate with a Mark so + // that future threads (which will start farther to the right in + // the input string) are lower priority than current threads. + if (ip->opcode() == kInstNop && q->maxmark() > 0 && + id == prog_->start_unanchored() && id != prog_->start()) + stk[nstk++] = Mark; + id = ip->out(); + goto Loop; + + case kInstAltMatch: + DCHECK(!ip->last()); + id = id+1; + goto Loop; + + case kInstEmptyWidth: + if (!ip->last()) + stk[nstk++] = id+1; + + // Continue on if we have all the right flag bits. + if (ip->empty() & ~flag) + break; + id = ip->out(); + goto Loop; + } + } +} + +// Running of work queues. In the work queue, order matters: +// the queue is sorted in priority order. If instruction i comes before j, +// then the instructions that i produces during the run must come before +// the ones that j produces. In order to keep this invariant, all the +// work queue runners have to take an old queue to process and then +// also a new queue to fill in. It's not acceptable to add to the end of +// an existing queue, because new instructions will not end up in the +// correct position. + +// Runs the work queue, processing the empty strings indicated by flag. +// For example, flag == kEmptyBeginLine|kEmptyEndLine means to match +// both ^ and $. It is important that callers pass all flags at once: +// processing both ^ and $ is not the same as first processing only ^ +// and then processing only $. Doing the two-step sequence won't match +// ^$^$^$ but processing ^ and $ simultaneously will (and is the behavior +// exhibited by existing implementations). +void DFA::RunWorkqOnEmptyString(Workq* oldq, Workq* newq, uint32_t flag) { + newq->clear(); + for (Workq::iterator i = oldq->begin(); i != oldq->end(); ++i) { + if (oldq->is_mark(*i)) + AddToQueue(newq, Mark, flag); + else + AddToQueue(newq, *i, flag); + } +} + +// Runs the work queue, processing the single byte c followed by any empty +// strings indicated by flag. For example, c == 'a' and flag == kEmptyEndLine, +// means to match c$. Sets the bool *ismatch to true if the end of the +// regular expression program has been reached (the regexp has matched). +void DFA::RunWorkqOnByte(Workq* oldq, Workq* newq, + int c, uint32_t flag, bool* ismatch) { + //mutex_.AssertHeld(); + + newq->clear(); + for (Workq::iterator i = oldq->begin(); i != oldq->end(); ++i) { + if (oldq->is_mark(*i)) { + if (*ismatch) + return; + newq->mark(); + continue; + } + int id = *i; + Prog::Inst* ip = prog_->inst(id); + switch (ip->opcode()) { + default: +#if 0 + LOG(DFATAL) << "unhandled opcode: " << ip->opcode(); +#endif + break; + + case kInstFail: // never succeeds + case kInstCapture: // already followed + case kInstNop: // already followed + case kInstAltMatch: // already followed + case kInstEmptyWidth: // already followed + break; + + case kInstByteRange: // can follow if c is in range + if (!ip->Matches(c)) + break; + AddToQueue(newq, ip->out(), flag); + if (ip->hint() != 0) { + // We have a hint, but we must cancel out the + // increment that will occur after the break. + i += ip->hint() - 1; + } else { + // We have no hint, so we must find the end + // of the current list and then skip to it. + Prog::Inst* ip0 = ip; + while (!ip->last()) + ++ip; + i += ip - ip0; + } + break; + + case kInstMatch: + if (prog_->anchor_end() && c != kByteEndText && + kind_ != Prog::kManyMatch) + break; + *ismatch = true; + if (kind_ == Prog::kFirstMatch) { + // Can stop processing work queue since we found a match. + return; + } + break; + } + } + + if (ExtraDebug) + fprintf(stderr, "%s on %d[%#x] -> %s [%d]\n", + DumpWorkq(oldq).c_str(), c, flag, DumpWorkq(newq).c_str(), *ismatch); +} + +// Processes input byte c in state, returning new state. +// Caller does not hold mutex. +DFA::State* DFA::RunStateOnByteUnlocked(State* state, int c) { + // Keep only one RunStateOnByte going + // even if the DFA is being run by multiple threads. + MutexLock l(&mutex_); + return RunStateOnByte(state, c); +} + +// Processes input byte c in state, returning new state. +DFA::State* DFA::RunStateOnByte(State* state, int c) { + //mutex_.AssertHeld(); + + if (state <= SpecialStateMax) { + if (state == FullMatchState) { + // It is convenient for routines like PossibleMatchRange + // if we implement RunStateOnByte for FullMatchState: + // once you get into this state you never get out, + // so it's pretty easy. + return FullMatchState; + } + if (state == DeadState) { +#if 0 + LOG(DFATAL) << "DeadState in RunStateOnByte"; +#endif + return NULL; + } + if (state == NULL) { +#if 0 + LOG(DFATAL) << "NULL state in RunStateOnByte"; +#endif + return NULL; + } +#if 0 + LOG(DFATAL) << "Unexpected special state in RunStateOnByte"; +#endif + return NULL; + } + + // If someone else already computed this, return it. + State* ns = state->next_[ByteMap(c)].load(std::memory_order_relaxed); + if (ns != NULL) + return ns; + + // Convert state into Workq. + StateToWorkq(state, q0_); + + // Flags marking the kinds of empty-width things (^ $ etc) + // around this byte. Before the byte we have the flags recorded + // in the State structure itself. After the byte we have + // nothing yet (but that will change: read on). + uint32_t needflag = state->flag_ >> kFlagNeedShift; + uint32_t beforeflag = state->flag_ & kFlagEmptyMask; + uint32_t oldbeforeflag = beforeflag; + uint32_t afterflag = 0; + + if (c == '\n') { + // Insert implicit $ and ^ around \n + beforeflag |= kEmptyEndLine; + afterflag |= kEmptyBeginLine; + } + + if (c == kByteEndText) { + // Insert implicit $ and \z before the fake "end text" byte. + beforeflag |= kEmptyEndLine | kEmptyEndText; + } + + // The state flag kFlagLastWord says whether the last + // byte processed was a word character. Use that info to + // insert empty-width (non-)word boundaries. + bool islastword = (state->flag_ & kFlagLastWord) != 0; + bool isword = c != kByteEndText && Prog::IsWordChar(static_cast(c)); + if (isword == islastword) + beforeflag |= kEmptyNonWordBoundary; + else + beforeflag |= kEmptyWordBoundary; + + // Okay, finally ready to run. + // Only useful to rerun on empty string if there are new, useful flags. + if (beforeflag & ~oldbeforeflag & needflag) { + RunWorkqOnEmptyString(q0_, q1_, beforeflag); + using std::swap; + swap(q0_, q1_); + } + bool ismatch = false; + RunWorkqOnByte(q0_, q1_, c, afterflag, &ismatch); + using std::swap; + swap(q0_, q1_); + + // Save afterflag along with ismatch and isword in new state. + uint32_t flag = afterflag; + if (ismatch) + flag |= kFlagMatch; + if (isword) + flag |= kFlagLastWord; + + if (ismatch && kind_ == Prog::kManyMatch) + ns = WorkqToCachedState(q0_, q1_, flag); + else + ns = WorkqToCachedState(q0_, NULL, flag); + + // Flush ns before linking to it. + // Write barrier before updating state->next_ so that the + // main search loop can proceed without any locking, for speed. + // (Otherwise it would need one mutex operation per input byte.) + state->next_[ByteMap(c)].store(ns, std::memory_order_release); + return ns; +} + + +////////////////////////////////////////////////////////////////////// +// DFA cache reset. + +// Reader-writer lock helper. +// +// The DFA uses a reader-writer mutex to protect the state graph itself. +// Traversing the state graph requires holding the mutex for reading, +// and discarding the state graph and starting over requires holding the +// lock for writing. If a search needs to expand the graph but is out +// of memory, it will need to drop its read lock and then acquire the +// write lock. Since it cannot then atomically downgrade from write lock +// to read lock, it runs the rest of the search holding the write lock. +// (This probably helps avoid repeated contention, but really the decision +// is forced by the Mutex interface.) It's a bit complicated to keep +// track of whether the lock is held for reading or writing and thread +// that through the search, so instead we encapsulate it in the RWLocker +// and pass that around. + +class DFA::RWLocker { + public: + explicit RWLocker(Mutex* mu); + ~RWLocker(); + + // If the lock is only held for reading right now, + // drop the read lock and re-acquire for writing. + // Subsequent calls to LockForWriting are no-ops. + // Notice that the lock is *released* temporarily. + void LockForWriting(); + + private: + Mutex* mu_; + bool writing_; + + RWLocker(const RWLocker&) = delete; + RWLocker& operator=(const RWLocker&) = delete; +}; + +DFA::RWLocker::RWLocker(Mutex* mu) : mu_(mu), writing_(false) { + mu_->ReaderLock(); +} + +// This function is marked as NO_THREAD_SAFETY_ANALYSIS because +// the annotations don't support lock upgrade. +void DFA::RWLocker::LockForWriting() NO_THREAD_SAFETY_ANALYSIS { + if (!writing_) { + mu_->ReaderUnlock(); + mu_->WriterLock(); + writing_ = true; + } +} + +DFA::RWLocker::~RWLocker() { + if (!writing_) + mu_->ReaderUnlock(); + else + mu_->WriterUnlock(); +} + + +// When the DFA's State cache fills, we discard all the states in the +// cache and start over. Many threads can be using and adding to the +// cache at the same time, so we synchronize using the cache_mutex_ +// to keep from stepping on other threads. Specifically, all the +// threads using the current cache hold cache_mutex_ for reading. +// When a thread decides to flush the cache, it drops cache_mutex_ +// and then re-acquires it for writing. That ensures there are no +// other threads accessing the cache anymore. The rest of the search +// runs holding cache_mutex_ for writing, avoiding any contention +// with or cache pollution caused by other threads. + +void DFA::ResetCache(RWLocker* cache_lock) { + // Re-acquire the cache_mutex_ for writing (exclusive use). + cache_lock->LockForWriting(); + + hooks::GetDFAStateCacheResetHook()({ + state_budget_, + state_cache_.size(), + }); + + // Clear the cache, reset the memory budget. + for (int i = 0; i < kMaxStart; i++) + start_[i].start.store(NULL, std::memory_order_relaxed); + ClearCache(); + mem_budget_ = state_budget_; +} + +// Typically, a couple States do need to be preserved across a cache +// reset, like the State at the current point in the search. +// The StateSaver class helps keep States across cache resets. +// It makes a copy of the state's guts outside the cache (before the reset) +// and then can be asked, after the reset, to recreate the State +// in the new cache. For example, in a DFA method ("this" is a DFA): +// +// StateSaver saver(this, s); +// ResetCache(cache_lock); +// s = saver.Restore(); +// +// The saver should always have room in the cache to re-create the state, +// because resetting the cache locks out all other threads, and the cache +// is known to have room for at least a couple states (otherwise the DFA +// constructor fails). + +class DFA::StateSaver { + public: + explicit StateSaver(DFA* dfa, State* state); + ~StateSaver(); + + // Recreates and returns a state equivalent to the + // original state passed to the constructor. + // Returns NULL if the cache has filled, but + // since the DFA guarantees to have room in the cache + // for a couple states, should never return NULL + // if used right after ResetCache. + State* Restore(); + + private: + DFA* dfa_; // the DFA to use + int* inst_; // saved info from State + int ninst_; + uint32_t flag_; + bool is_special_; // whether original state was special + State* special_; // if is_special_, the original state + + StateSaver(const StateSaver&) = delete; + StateSaver& operator=(const StateSaver&) = delete; +}; + +DFA::StateSaver::StateSaver(DFA* dfa, State* state) { + dfa_ = dfa; + if (state <= SpecialStateMax) { + inst_ = NULL; + ninst_ = 0; + flag_ = 0; + is_special_ = true; + special_ = state; + return; + } + is_special_ = false; + special_ = NULL; + flag_ = state->flag_; + ninst_ = state->ninst_; + inst_ = new int[ninst_]; + memmove(inst_, state->inst_, ninst_*sizeof inst_[0]); +} + +DFA::StateSaver::~StateSaver() { + if (!is_special_) + delete[] inst_; +} + +DFA::State* DFA::StateSaver::Restore() { + if (is_special_) + return special_; + MutexLock l(&dfa_->mutex_); + State* s = dfa_->CachedState(inst_, ninst_, flag_); +#if 0 + if (s == NULL) + LOG(DFATAL) << "StateSaver failed to restore state."; +#endif + return s; +} + + +////////////////////////////////////////////////////////////////////// +// +// DFA execution. +// +// The basic search loop is easy: start in a state s and then for each +// byte c in the input, s = s->next[c]. +// +// This simple description omits a few efficiency-driven complications. +// +// First, the State graph is constructed incrementally: it is possible +// that s->next[c] is null, indicating that that state has not been +// fully explored. In this case, RunStateOnByte must be invoked to +// determine the next state, which is cached in s->next[c] to save +// future effort. An alternative reason for s->next[c] to be null is +// that the DFA has reached a so-called "dead state", in which any match +// is no longer possible. In this case RunStateOnByte will return NULL +// and the processing of the string can stop early. +// +// Second, a 256-element pointer array for s->next_ makes each State +// quite large (2kB on 64-bit machines). Instead, dfa->bytemap_[] +// maps from bytes to "byte classes" and then next_ only needs to have +// as many pointers as there are byte classes. A byte class is simply a +// range of bytes that the regexp never distinguishes between. +// A regexp looking for a[abc] would have four byte ranges -- 0 to 'a'-1, +// 'a', 'b' to 'c', and 'c' to 0xFF. The bytemap slows us a little bit +// but in exchange we typically cut the size of a State (and thus our +// memory footprint) by about 5-10x. The comments still refer to +// s->next[c] for simplicity, but code should refer to s->next_[bytemap_[c]]. +// +// Third, it is common for a DFA for an unanchored match to begin in a +// state in which only one particular byte value can take the DFA to a +// different state. That is, s->next[c] != s for only one c. In this +// situation, the DFA can do better than executing the simple loop. +// Instead, it can call memchr to search very quickly for the byte c. +// Whether the start state has this property is determined during a +// pre-compilation pass and the "can_prefix_accel" argument is set. +// +// Fourth, the desired behavior is to search for the leftmost-best match +// (approximately, the same one that Perl would find), which is not +// necessarily the match ending earliest in the string. Each time a +// match is found, it must be noted, but the DFA must continue on in +// hope of finding a higher-priority match. In some cases, the caller only +// cares whether there is any match at all, not which one is found. +// The "want_earliest_match" flag causes the search to stop at the first +// match found. +// +// Fifth, one algorithm that uses the DFA needs it to run over the +// input string backward, beginning at the end and ending at the beginning. +// Passing false for the "run_forward" flag causes the DFA to run backward. +// +// The checks for these last three cases, which in a naive implementation +// would be performed once per input byte, slow the general loop enough +// to merit specialized versions of the search loop for each of the +// eight possible settings of the three booleans. Rather than write +// eight different functions, we write one general implementation and then +// inline it to create the specialized ones. +// +// Note that matches are delayed by one byte, to make it easier to +// accommodate match conditions depending on the next input byte (like $ and \b). +// When s->next[c]->IsMatch(), it means that there is a match ending just +// *before* byte c. + +// The generic search loop. Searches text for a match, returning +// the pointer to the end of the chosen match, or NULL if no match. +// The bools are equal to the same-named variables in params, but +// making them function arguments lets the inliner specialize +// this function to each combination (see two paragraphs above). +template +inline bool DFA::InlinedSearchLoop(SearchParams* params) { + State* start = params->start; + const uint8_t* bp = BytePtr(params->text.data()); // start of text + const uint8_t* p = bp; // text scanning point + const uint8_t* ep = BytePtr(params->text.data() + + params->text.size()); // end of text + const uint8_t* resetp = NULL; // p at last cache reset + if (!run_forward) { + using std::swap; + swap(p, ep); + } + + const uint8_t* bytemap = prog_->bytemap(); + const uint8_t* lastmatch = NULL; // most recent matching position in text + bool matched = false; + + State* s = start; + if (ExtraDebug) + fprintf(stderr, "@stx: %s\n", DumpState(s).c_str()); + + if (s->IsMatch()) { + matched = true; + lastmatch = p; + if (ExtraDebug) + fprintf(stderr, "match @stx! [%s]\n", DumpState(s).c_str()); + if (params->matches != NULL && kind_ == Prog::kManyMatch) { + for (int i = s->ninst_ - 1; i >= 0; i--) { + int id = s->inst_[i]; + if (id == MatchSep) + break; + params->matches->insert(id); + } + } + if (want_earliest_match) { + params->ep = reinterpret_cast(lastmatch); + return true; + } + } + + while (p != ep) { + if (ExtraDebug) + fprintf(stderr, "@%td: %s\n", p - bp, DumpState(s).c_str()); + + if (can_prefix_accel && s == start) { + // In start state, only way out is to find the prefix, + // so we use prefix accel (e.g. memchr) to skip ahead. + // If not found, we can skip to the end of the string. + p = BytePtr(prog_->PrefixAccel(p, ep - p)); + if (p == NULL) { + p = ep; + break; + } + } + + int c; + if (run_forward) + c = *p++; + else + c = *--p; + + // Note that multiple threads might be consulting + // s->next_[bytemap[c]] simultaneously. + // RunStateOnByte takes care of the appropriate locking, + // including a memory barrier so that the unlocked access + // (sometimes known as "double-checked locking") is safe. + // The alternative would be either one DFA per thread + // or one mutex operation per input byte. + // + // ns == DeadState means the state is known to be dead + // (no more matches are possible). + // ns == NULL means the state has not yet been computed + // (need to call RunStateOnByteUnlocked). + // RunStateOnByte returns ns == NULL if it is out of memory. + // ns == FullMatchState means the rest of the string matches. + // + // Okay to use bytemap[] not ByteMap() here, because + // c is known to be an actual byte and not kByteEndText. + + State* ns = s->next_[bytemap[c]].load(std::memory_order_acquire); + if (ns == NULL) { + ns = RunStateOnByteUnlocked(s, c); + if (ns == NULL) { + // After we reset the cache, we hold cache_mutex exclusively, + // so if resetp != NULL, it means we filled the DFA state + // cache with this search alone (without any other threads). + // Benchmarks show that doing a state computation on every + // byte runs at about 0.2 MB/s, while the NFA (nfa.cc) can do the + // same at about 2 MB/s. Unless we're processing an average + // of 10 bytes per state computation, fail so that RE2 can + // fall back to the NFA. However, RE2::Set cannot fall back, + // so we just have to keep on keeping on in that case. + if (dfa_should_bail_when_slow && resetp != NULL && + static_cast(p - resetp) < 10*state_cache_.size() && + kind_ != Prog::kManyMatch) { + params->failed = true; + return false; + } + resetp = p; + + // Prepare to save start and s across the reset. + StateSaver save_start(this, start); + StateSaver save_s(this, s); + + // Discard all the States in the cache. + ResetCache(params->cache_lock); + + // Restore start and s so we can continue. + if ((start = save_start.Restore()) == NULL || + (s = save_s.Restore()) == NULL) { + // Restore already did LOG(DFATAL). + params->failed = true; + return false; + } + ns = RunStateOnByteUnlocked(s, c); + if (ns == NULL) { +#if 0 + LOG(DFATAL) << "RunStateOnByteUnlocked failed after ResetCache"; +#endif + params->failed = true; + return false; + } + } + } + if (ns <= SpecialStateMax) { + if (ns == DeadState) { + params->ep = reinterpret_cast(lastmatch); + return matched; + } + // FullMatchState + params->ep = reinterpret_cast(ep); + return true; + } + + s = ns; + if (s->IsMatch()) { + matched = true; + // The DFA notices the match one byte late, + // so adjust p before using it in the match. + if (run_forward) + lastmatch = p - 1; + else + lastmatch = p + 1; + if (ExtraDebug) + fprintf(stderr, "match @%td! [%s]\n", lastmatch - bp, DumpState(s).c_str()); + if (params->matches != NULL && kind_ == Prog::kManyMatch) { + for (int i = s->ninst_ - 1; i >= 0; i--) { + int id = s->inst_[i]; + if (id == MatchSep) + break; + params->matches->insert(id); + } + } + if (want_earliest_match) { + params->ep = reinterpret_cast(lastmatch); + return true; + } + } + } + + // Process one more byte to see if it triggers a match. + // (Remember, matches are delayed one byte.) + if (ExtraDebug) + fprintf(stderr, "@etx: %s\n", DumpState(s).c_str()); + + int lastbyte; + if (run_forward) { + if (params->text.end() == params->context.end()) + lastbyte = kByteEndText; + else + lastbyte = params->text.end()[0] & 0xFF; + } else { + if (params->text.begin() == params->context.begin()) + lastbyte = kByteEndText; + else + lastbyte = params->text.begin()[-1] & 0xFF; + } + + State* ns = s->next_[ByteMap(lastbyte)].load(std::memory_order_acquire); + if (ns == NULL) { + ns = RunStateOnByteUnlocked(s, lastbyte); + if (ns == NULL) { + StateSaver save_s(this, s); + ResetCache(params->cache_lock); + if ((s = save_s.Restore()) == NULL) { + params->failed = true; + return false; + } + ns = RunStateOnByteUnlocked(s, lastbyte); + if (ns == NULL) { +#if 0 + LOG(DFATAL) << "RunStateOnByteUnlocked failed after Reset"; +#endif + params->failed = true; + return false; + } + } + } + if (ns <= SpecialStateMax) { + if (ns == DeadState) { + params->ep = reinterpret_cast(lastmatch); + return matched; + } + // FullMatchState + params->ep = reinterpret_cast(ep); + return true; + } + + s = ns; + if (s->IsMatch()) { + matched = true; + lastmatch = p; + if (ExtraDebug) + fprintf(stderr, "match @etx! [%s]\n", DumpState(s).c_str()); + if (params->matches != NULL && kind_ == Prog::kManyMatch) { + for (int i = s->ninst_ - 1; i >= 0; i--) { + int id = s->inst_[i]; + if (id == MatchSep) + break; + params->matches->insert(id); + } + } + } + + params->ep = reinterpret_cast(lastmatch); + return matched; +} + +// Inline specializations of the general loop. +bool DFA::SearchFFF(SearchParams* params) { + return InlinedSearchLoop(params); +} +bool DFA::SearchFFT(SearchParams* params) { + return InlinedSearchLoop(params); +} +bool DFA::SearchFTF(SearchParams* params) { + return InlinedSearchLoop(params); +} +bool DFA::SearchFTT(SearchParams* params) { + return InlinedSearchLoop(params); +} +bool DFA::SearchTFF(SearchParams* params) { + return InlinedSearchLoop(params); +} +bool DFA::SearchTFT(SearchParams* params) { + return InlinedSearchLoop(params); +} +bool DFA::SearchTTF(SearchParams* params) { + return InlinedSearchLoop(params); +} +bool DFA::SearchTTT(SearchParams* params) { + return InlinedSearchLoop(params); +} + +// For performance, calls the appropriate specialized version +// of InlinedSearchLoop. +bool DFA::FastSearchLoop(SearchParams* params) { + // Because the methods are private, the Searches array + // cannot be declared at top level. + static bool (DFA::*Searches[])(SearchParams*) = { + &DFA::SearchFFF, + &DFA::SearchFFT, + &DFA::SearchFTF, + &DFA::SearchFTT, + &DFA::SearchTFF, + &DFA::SearchTFT, + &DFA::SearchTTF, + &DFA::SearchTTT, + }; + + int index = 4 * params->can_prefix_accel + + 2 * params->want_earliest_match + + 1 * params->run_forward; + return (this->*Searches[index])(params); +} + + +// The discussion of DFA execution above ignored the question of how +// to determine the initial state for the search loop. There are two +// factors that influence the choice of start state. +// +// The first factor is whether the search is anchored or not. +// The regexp program (Prog*) itself has +// two different entry points: one for anchored searches and one for +// unanchored searches. (The unanchored version starts with a leading ".*?" +// and then jumps to the anchored one.) +// +// The second factor is where text appears in the larger context, which +// determines which empty-string operators can be matched at the beginning +// of execution. If text is at the very beginning of context, \A and ^ match. +// Otherwise if text is at the beginning of a line, then ^ matches. +// Otherwise it matters whether the character before text is a word character +// or a non-word character. +// +// The two cases (unanchored vs not) and four cases (empty-string flags) +// combine to make the eight cases recorded in the DFA's begin_text_[2], +// begin_line_[2], after_wordchar_[2], and after_nonwordchar_[2] cached +// StartInfos. The start state for each is filled in the first time it +// is used for an actual search. + +// Examines text, context, and anchored to determine the right start +// state for the DFA search loop. Fills in params and returns true on success. +// Returns false on failure. +bool DFA::AnalyzeSearch(SearchParams* params) { + const StringPiece& text = params->text; + const StringPiece& context = params->context; + + // Sanity check: make sure that text lies within context. + if (text.begin() < context.begin() || text.end() > context.end()) { +#if 0 + LOG(DFATAL) << "context does not contain text"; +#endif + params->start = DeadState; + return true; + } + + // Determine correct search type. + int start; + uint32_t flags; + if (params->run_forward) { + if (text.begin() == context.begin()) { + start = kStartBeginText; + flags = kEmptyBeginText|kEmptyBeginLine; + } else if (text.begin()[-1] == '\n') { + start = kStartBeginLine; + flags = kEmptyBeginLine; + } else if (Prog::IsWordChar(text.begin()[-1] & 0xFF)) { + start = kStartAfterWordChar; + flags = kFlagLastWord; + } else { + start = kStartAfterNonWordChar; + flags = 0; + } + } else { + if (text.end() == context.end()) { + start = kStartBeginText; + flags = kEmptyBeginText|kEmptyBeginLine; + } else if (text.end()[0] == '\n') { + start = kStartBeginLine; + flags = kEmptyBeginLine; + } else if (Prog::IsWordChar(text.end()[0] & 0xFF)) { + start = kStartAfterWordChar; + flags = kFlagLastWord; + } else { + start = kStartAfterNonWordChar; + flags = 0; + } + } + if (params->anchored) + start |= kStartAnchored; + StartInfo* info = &start_[start]; + + // Try once without cache_lock for writing. + // Try again after resetting the cache + // (ResetCache will relock cache_lock for writing). + if (!AnalyzeSearchHelper(params, info, flags)) { + ResetCache(params->cache_lock); + if (!AnalyzeSearchHelper(params, info, flags)) { +#if 0 + LOG(DFATAL) << "Failed to analyze start state."; +#endif + params->failed = true; + return false; + } + } + + params->start = info->start.load(std::memory_order_acquire); + + // Even if we could prefix accel, we cannot do so when anchored and, + // less obviously, we cannot do so when we are going to need flags. + // This trick works only when there is a single byte that leads to a + // different state! + if (prog_->can_prefix_accel() && + !params->anchored && + params->start > SpecialStateMax && + params->start->flag_ >> kFlagNeedShift == 0) + params->can_prefix_accel = true; + + if (ExtraDebug) + fprintf(stderr, "anchored=%d fwd=%d flags=%#x state=%s can_prefix_accel=%d\n", + params->anchored, params->run_forward, flags, + DumpState(params->start).c_str(), params->can_prefix_accel); + + return true; +} + +// Fills in info if needed. Returns true on success, false on failure. +bool DFA::AnalyzeSearchHelper(SearchParams* params, StartInfo* info, + uint32_t flags) { + // Quick check. + State* start = info->start.load(std::memory_order_acquire); + if (start != NULL) + return true; + + MutexLock l(&mutex_); + start = info->start.load(std::memory_order_relaxed); + if (start != NULL) + return true; + + q0_->clear(); + AddToQueue(q0_, + params->anchored ? prog_->start() : prog_->start_unanchored(), + flags); + start = WorkqToCachedState(q0_, NULL, flags); + if (start == NULL) + return false; + + // Synchronize with "quick check" above. + info->start.store(start, std::memory_order_release); + return true; +} + +// The actual DFA search: calls AnalyzeSearch and then FastSearchLoop. +bool DFA::Search(const StringPiece& text, + const StringPiece& context, + bool anchored, + bool want_earliest_match, + bool run_forward, + bool* failed, + const char** epp, + SparseSet* matches) { + *epp = NULL; + if (!ok()) { + *failed = true; + return false; + } + *failed = false; + + if (ExtraDebug) { + fprintf(stderr, "\nprogram:\n%s\n", prog_->DumpUnanchored().c_str()); + fprintf(stderr, "text %s anchored=%d earliest=%d fwd=%d kind %d\n", + std::string(text).c_str(), anchored, want_earliest_match, run_forward, kind_); + } + + RWLocker l(&cache_mutex_); + SearchParams params(text, context, &l); + params.anchored = anchored; + params.want_earliest_match = want_earliest_match; + params.run_forward = run_forward; + params.matches = matches; + + if (!AnalyzeSearch(¶ms)) { + *failed = true; + return false; + } + if (params.start == DeadState) + return false; + if (params.start == FullMatchState) { + if (run_forward == want_earliest_match) + *epp = text.data(); + else + *epp = text.data() + text.size(); + return true; + } + if (ExtraDebug) + fprintf(stderr, "start %s\n", DumpState(params.start).c_str()); + bool ret = FastSearchLoop(¶ms); + if (params.failed) { + *failed = true; + return false; + } + *epp = params.ep; + return ret; +} + +DFA* Prog::GetDFA(MatchKind kind) { + // For a forward DFA, half the memory goes to each DFA. + // However, if it is a "many match" DFA, then there is + // no counterpart with which the memory must be shared. + // + // For a reverse DFA, all the memory goes to the + // "longest match" DFA, because RE2 never does reverse + // "first match" searches. + if (kind == kFirstMatch) { + std::call_once(dfa_first_once_, [](Prog* prog) { + prog->dfa_first_ = new DFA(prog, kFirstMatch, prog->dfa_mem_ / 2); + }, this); + return dfa_first_; + } else if (kind == kManyMatch) { + std::call_once(dfa_first_once_, [](Prog* prog) { + prog->dfa_first_ = new DFA(prog, kManyMatch, prog->dfa_mem_); + }, this); + return dfa_first_; + } else { + std::call_once(dfa_longest_once_, [](Prog* prog) { + if (!prog->reversed_) + prog->dfa_longest_ = new DFA(prog, kLongestMatch, prog->dfa_mem_ / 2); + else + prog->dfa_longest_ = new DFA(prog, kLongestMatch, prog->dfa_mem_); + }, this); + return dfa_longest_; + } +} + +void Prog::DeleteDFA(DFA* dfa) { + delete dfa; +} + +// Executes the regexp program to search in text, +// which itself is inside the larger context. (As a convenience, +// passing a NULL context is equivalent to passing text.) +// Returns true if a match is found, false if not. +// If a match is found, fills in match0->end() to point at the end of the match +// and sets match0->begin() to text.begin(), since the DFA can't track +// where the match actually began. +// +// This is the only external interface (class DFA only exists in this file). +// +bool Prog::SearchDFA(const StringPiece& text, const StringPiece& const_context, + Anchor anchor, MatchKind kind, StringPiece* match0, + bool* failed, SparseSet* matches) { + *failed = false; + + StringPiece context = const_context; + if (context.data() == NULL) + context = text; + bool caret = anchor_start(); + bool dollar = anchor_end(); + if (reversed_) { + using std::swap; + swap(caret, dollar); + } + if (caret && context.begin() != text.begin()) + return false; + if (dollar && context.end() != text.end()) + return false; + + // Handle full match by running an anchored longest match + // and then checking if it covers all of text. + bool anchored = anchor == kAnchored || anchor_start() || kind == kFullMatch; + bool endmatch = false; + if (kind == kManyMatch) { + // This is split out in order to avoid clobbering kind. + } else if (kind == kFullMatch || anchor_end()) { + endmatch = true; + kind = kLongestMatch; + } + + // If the caller doesn't care where the match is (just whether one exists), + // then we can stop at the very first match we find, the so-called + // "earliest match". + bool want_earliest_match = false; + if (kind == kManyMatch) { + // This is split out in order to avoid clobbering kind. + if (matches == NULL) { + want_earliest_match = true; + } + } else if (match0 == NULL && !endmatch) { + want_earliest_match = true; + kind = kLongestMatch; + } + + DFA* dfa = GetDFA(kind); + const char* ep; + bool matched = dfa->Search(text, context, anchored, + want_earliest_match, !reversed_, + failed, &ep, matches); + if (*failed) { + hooks::GetDFASearchFailureHook()({ + // Nothing yet... + }); + return false; + } + if (!matched) + return false; + if (endmatch && ep != (reversed_ ? text.data() : text.data() + text.size())) + return false; + + // If caller cares, record the boundary of the match. + // We only know where it ends, so use the boundary of text + // as the beginning. + if (match0) { + if (reversed_) + *match0 = + StringPiece(ep, static_cast(text.data() + text.size() - ep)); + else + *match0 = + StringPiece(text.data(), static_cast(ep - text.data())); + } + return true; +} + +// Build out all states in DFA. Returns number of states. +int DFA::BuildAllStates(const Prog::DFAStateCallback& cb) { + if (!ok()) + return 0; + + // Pick out start state for unanchored search + // at beginning of text. + RWLocker l(&cache_mutex_); + SearchParams params(StringPiece(), StringPiece(), &l); + params.anchored = false; + if (!AnalyzeSearch(¶ms) || + params.start == NULL || + params.start == DeadState) + return 0; + + // Add start state to work queue. + // Note that any State* that we handle here must point into the cache, + // so we can simply depend on pointer-as-a-number hashing and equality. + std::unordered_map m; + std::deque q; + m.emplace(params.start, static_cast(m.size())); + q.push_back(params.start); + + // Compute the input bytes needed to cover all of the next pointers. + int nnext = prog_->bytemap_range() + 1; // + 1 for kByteEndText slot + std::vector input(nnext); + for (int c = 0; c < 256; c++) { + int b = prog_->bytemap()[c]; + while (c < 256-1 && prog_->bytemap()[c+1] == b) + c++; + input[b] = c; + } + input[prog_->bytemap_range()] = kByteEndText; + + // Scratch space for the output. + std::vector output(nnext); + + // Flood to expand every state. + bool oom = false; + while (!q.empty()) { + State* s = q.front(); + q.pop_front(); + for (int c : input) { + State* ns = RunStateOnByteUnlocked(s, c); + if (ns == NULL) { + oom = true; + break; + } + if (ns == DeadState) { + output[ByteMap(c)] = -1; + continue; + } + if (m.find(ns) == m.end()) { + m.emplace(ns, static_cast(m.size())); + q.push_back(ns); + } + output[ByteMap(c)] = m[ns]; + } + if (cb) + cb(oom ? NULL : output.data(), + s == FullMatchState || s->IsMatch()); + if (oom) + break; + } + + return static_cast(m.size()); +} + +// Build out all states in DFA for kind. Returns number of states. +int Prog::BuildEntireDFA(MatchKind kind, const DFAStateCallback& cb) { + return GetDFA(kind)->BuildAllStates(cb); +} + +void Prog::TEST_dfa_should_bail_when_slow(bool b) { + dfa_should_bail_when_slow = b; +} + +// Computes min and max for matching string. +// Won't return strings bigger than maxlen. +bool DFA::PossibleMatchRange(std::string* min, std::string* max, int maxlen) { + if (!ok()) + return false; + + // NOTE: if future users of PossibleMatchRange want more precision when + // presented with infinitely repeated elements, consider making this a + // parameter to PossibleMatchRange. + static int kMaxEltRepetitions = 0; + + // Keep track of the number of times we've visited states previously. We only + // revisit a given state if it's part of a repeated group, so if the value + // portion of the map tuple exceeds kMaxEltRepetitions we bail out and set + // |*max| to |PrefixSuccessor(*max)|. + // + // Also note that previously_visited_states[UnseenStatePtr] will, in the STL + // tradition, implicitly insert a '0' value at first use. We take advantage + // of that property below. + std::unordered_map previously_visited_states; + + // Pick out start state for anchored search at beginning of text. + RWLocker l(&cache_mutex_); + SearchParams params(StringPiece(), StringPiece(), &l); + params.anchored = true; + if (!AnalyzeSearch(¶ms)) + return false; + if (params.start == DeadState) { // No matching strings + *min = ""; + *max = ""; + return true; + } + if (params.start == FullMatchState) // Every string matches: no max + return false; + + // The DFA is essentially a big graph rooted at params.start, + // and paths in the graph correspond to accepted strings. + // Each node in the graph has potentially 256+1 arrows + // coming out, one for each byte plus the magic end of + // text character kByteEndText. + + // To find the smallest possible prefix of an accepted + // string, we just walk the graph preferring to follow + // arrows with the lowest bytes possible. To find the + // largest possible prefix, we follow the largest bytes + // possible. + + // The test for whether there is an arrow from s on byte j is + // ns = RunStateOnByteUnlocked(s, j); + // if (ns == NULL) + // return false; + // if (ns != DeadState && ns->ninst > 0) + // The RunStateOnByteUnlocked call asks the DFA to build out the graph. + // It returns NULL only if the DFA has run out of memory, + // in which case we can't be sure of anything. + // The second check sees whether there was graph built + // and whether it is interesting graph. Nodes might have + // ns->ninst == 0 if they exist only to represent the fact + // that a match was found on the previous byte. + + // Build minimum prefix. + State* s = params.start; + min->clear(); + MutexLock lock(&mutex_); + for (int i = 0; i < maxlen; i++) { + if (previously_visited_states[s] > kMaxEltRepetitions) + break; + previously_visited_states[s]++; + + // Stop if min is a match. + State* ns = RunStateOnByte(s, kByteEndText); + if (ns == NULL) // DFA out of memory + return false; + if (ns != DeadState && (ns == FullMatchState || ns->IsMatch())) + break; + + // Try to extend the string with low bytes. + bool extended = false; + for (int j = 0; j < 256; j++) { + ns = RunStateOnByte(s, j); + if (ns == NULL) // DFA out of memory + return false; + if (ns == FullMatchState || + (ns > SpecialStateMax && ns->ninst_ > 0)) { + extended = true; + min->append(1, static_cast(j)); + s = ns; + break; + } + } + if (!extended) + break; + } + + // Build maximum prefix. + previously_visited_states.clear(); + s = params.start; + max->clear(); + for (int i = 0; i < maxlen; i++) { + if (previously_visited_states[s] > kMaxEltRepetitions) + break; + previously_visited_states[s] += 1; + + // Try to extend the string with high bytes. + bool extended = false; + for (int j = 255; j >= 0; j--) { + State* ns = RunStateOnByte(s, j); + if (ns == NULL) + return false; + if (ns == FullMatchState || + (ns > SpecialStateMax && ns->ninst_ > 0)) { + extended = true; + max->append(1, static_cast(j)); + s = ns; + break; + } + } + if (!extended) { + // Done, no need for PrefixSuccessor. + return true; + } + } + + // Stopped while still adding to *max - round aaaaaaaaaa... to aaaa...b + PrefixSuccessor(max); + + // If there are no bytes left, we have no way to say "there is no maximum + // string". We could make the interface more complicated and be able to + // return "there is no maximum but here is a minimum", but that seems like + // overkill -- the most common no-max case is all possible strings, so not + // telling the caller that the empty string is the minimum match isn't a + // great loss. + if (max->empty()) + return false; + + return true; +} + +// PossibleMatchRange for a Prog. +bool Prog::PossibleMatchRange(std::string* min, std::string* max, int maxlen) { + // Have to use dfa_longest_ to get all strings for full matches. + // For example, (a|aa) never matches aa in first-match mode. + return GetDFA(kLongestMatch)->PossibleMatchRange(min, max, maxlen); +} + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/nfa.cc b/third_party/opa/wasm/src/re2/re2/nfa.cc new file mode 100644 index 000000000000..2a8392f1496f --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/nfa.cc @@ -0,0 +1,725 @@ +// Copyright 2006-2007 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Tested by search_test.cc. +// +// Prog::SearchNFA, an NFA search. +// This is an actual NFA like the theorists talk about, +// not the pseudo-NFA found in backtracking regexp implementations. +// +// IMPLEMENTATION +// +// This algorithm is a variant of one that appeared in Rob Pike's sam editor, +// which is a variant of the one described in Thompson's 1968 CACM paper. +// See http://swtch.com/~rsc/regexp/ for various history. The main feature +// over the DFA implementation is that it tracks submatch boundaries. +// +// When the choice of submatch boundaries is ambiguous, this particular +// implementation makes the same choices that traditional backtracking +// implementations (in particular, Perl and PCRE) do. +// Note that unlike in Perl and PCRE, this algorithm *cannot* take exponential +// time in the length of the input. +// +// Like Thompson's original machine and like the DFA implementation, this +// implementation notices a match only once it is one byte past it. + +#include +#include +#include +#include +#include +#include +#include + +#include "util/logging.h" +#include "util/strutil.h" +#include "re2/pod_array.h" +#include "re2/prog.h" +#include "re2/regexp.h" +#include "re2/sparse_array.h" +#include "re2/sparse_set.h" + +namespace re2 { + +static const bool ExtraDebug = false; + +class NFA { + public: + NFA(Prog* prog); + ~NFA(); + + // Searches for a matching string. + // * If anchored is true, only considers matches starting at offset. + // Otherwise finds lefmost match at or after offset. + // * If longest is true, returns the longest match starting + // at the chosen start point. Otherwise returns the so-called + // left-biased match, the one traditional backtracking engines + // (like Perl and PCRE) find. + // Records submatch boundaries in submatch[1..nsubmatch-1]. + // Submatch[0] is the entire match. When there is a choice in + // which text matches each subexpression, the submatch boundaries + // are chosen to match what a backtracking implementation would choose. + bool Search(const StringPiece& text, const StringPiece& context, + bool anchored, bool longest, + StringPiece* submatch, int nsubmatch); + + private: + struct Thread { + union { + int ref; + Thread* next; // when on free list + }; + const char** capture; + }; + + // State for explicit stack in AddToThreadq. + struct AddState { + int id; // Inst to process + Thread* t; // if not null, set t0 = t before processing id + }; + + // Threadq is a list of threads. The list is sorted by the order + // in which Perl would explore that particular state -- the earlier + // choices appear earlier in the list. + typedef SparseArray Threadq; + + inline Thread* AllocThread(); + inline Thread* Incref(Thread* t); + inline void Decref(Thread* t); + + // Follows all empty arrows from id0 and enqueues all the states reached. + // Enqueues only the ByteRange instructions that match byte c. + // context is used (with p) for evaluating empty-width specials. + // p is the current input position, and t0 is the current thread. + void AddToThreadq(Threadq* q, int id0, int c, const StringPiece& context, + const char* p, Thread* t0); + + // Run runq on byte c, appending new states to nextq. + // Updates matched_ and match_ as new, better matches are found. + // context is used (with p) for evaluating empty-width specials. + // p is the position of byte c in the input string for AddToThreadq; + // p-1 will be used when processing Match instructions. + // Frees all the threads on runq. + // If there is a shortcut to the end, returns that shortcut. + int Step(Threadq* runq, Threadq* nextq, int c, const StringPiece& context, + const char* p); + + // Returns text version of capture information, for debugging. + std::string FormatCapture(const char** capture); + + void CopyCapture(const char** dst, const char** src) { + memmove(dst, src, ncapture_*sizeof src[0]); + } + + Prog* prog_; // underlying program + int start_; // start instruction in program + int ncapture_; // number of submatches to track + bool longest_; // whether searching for longest match + bool endmatch_; // whether match must end at text.end() + const char* btext_; // beginning of text (for FormatSubmatch) + const char* etext_; // end of text (for endmatch_) + Threadq q0_, q1_; // pre-allocated for Search. + PODArray stack_; // pre-allocated for AddToThreadq + std::deque arena_; // thread arena + Thread* freelist_; // thread freelist + const char** match_; // best match so far + bool matched_; // any match so far? + + NFA(const NFA&) = delete; + NFA& operator=(const NFA&) = delete; +}; + +NFA::NFA(Prog* prog) { + prog_ = prog; + start_ = prog_->start(); + ncapture_ = 0; + longest_ = false; + endmatch_ = false; + btext_ = NULL; + etext_ = NULL; + q0_.resize(prog_->size()); + q1_.resize(prog_->size()); + // See NFA::AddToThreadq() for why this is so. + int nstack = 2*prog_->inst_count(kInstCapture) + + prog_->inst_count(kInstEmptyWidth) + + prog_->inst_count(kInstNop) + 1; // + 1 for start inst + stack_ = PODArray(nstack); + freelist_ = NULL; + match_ = NULL; + matched_ = false; +} + +NFA::~NFA() { + delete[] match_; + for (const Thread& t : arena_) + delete[] t.capture; +} + +NFA::Thread* NFA::AllocThread() { + Thread* t = freelist_; + if (t != NULL) { + freelist_ = t->next; + t->ref = 1; + // We don't need to touch t->capture because + // the caller will immediately overwrite it. + return t; + } + arena_.emplace_back(); + t = &arena_.back(); + t->ref = 1; + t->capture = new const char*[ncapture_]; + return t; +} + +NFA::Thread* NFA::Incref(Thread* t) { + DCHECK(t != NULL); + t->ref++; + return t; +} + +void NFA::Decref(Thread* t) { + DCHECK(t != NULL); + t->ref--; + if (t->ref > 0) + return; + DCHECK_EQ(t->ref, 0); + t->next = freelist_; + freelist_ = t; +} + +// Follows all empty arrows from id0 and enqueues all the states reached. +// Enqueues only the ByteRange instructions that match byte c. +// context is used (with p) for evaluating empty-width specials. +// p is the current input position, and t0 is the current thread. +void NFA::AddToThreadq(Threadq* q, int id0, int c, const StringPiece& context, + const char* p, Thread* t0) { + if (id0 == 0) + return; + + // Use stack_ to hold our stack of instructions yet to process. + // It was preallocated as follows: + // two entries per Capture; + // one entry per EmptyWidth; and + // one entry per Nop. + // This reflects the maximum number of stack pushes that each can + // perform. (Each instruction can be processed at most once.) + AddState* stk = stack_.data(); + int nstk = 0; + + stk[nstk++] = {id0, NULL}; + while (nstk > 0) { + DCHECK_LE(nstk, stack_.size()); + AddState a = stk[--nstk]; + + Loop: + if (a.t != NULL) { + // t0 was a thread that we allocated and copied in order to + // record the capture, so we must now decref it. + Decref(t0); + t0 = a.t; + } + + int id = a.id; + if (id == 0) + continue; + if (q->has_index(id)) { + if (ExtraDebug) + fprintf(stderr, " [%d%s]\n", id, FormatCapture(t0->capture).c_str()); + continue; + } + + // Create entry in q no matter what. We might fill it in below, + // or we might not. Even if not, it is necessary to have it, + // so that we don't revisit id0 during the recursion. + q->set_new(id, NULL); + Thread** tp = &q->get_existing(id); + int j; + Thread* t; + Prog::Inst* ip = prog_->inst(id); + switch (ip->opcode()) { + default: +#if 0 + LOG(DFATAL) << "unhandled " << ip->opcode() << " in AddToThreadq"; +#endif + break; + + case kInstFail: + break; + + case kInstAltMatch: + // Save state; will pick up at next byte. + t = Incref(t0); + *tp = t; + + DCHECK(!ip->last()); + a = {id+1, NULL}; + goto Loop; + + case kInstNop: + if (!ip->last()) + stk[nstk++] = {id+1, NULL}; + + // Continue on. + a = {ip->out(), NULL}; + goto Loop; + + case kInstCapture: + if (!ip->last()) + stk[nstk++] = {id+1, NULL}; + + if ((j=ip->cap()) < ncapture_) { + // Push a dummy whose only job is to restore t0 + // once we finish exploring this possibility. + stk[nstk++] = {0, t0}; + + // Record capture. + t = AllocThread(); + CopyCapture(t->capture, t0->capture); + t->capture[j] = p; + t0 = t; + } + a = {ip->out(), NULL}; + goto Loop; + + case kInstByteRange: + if (!ip->Matches(c)) + goto Next; + + // Save state; will pick up at next byte. + t = Incref(t0); + *tp = t; + if (ExtraDebug) + fprintf(stderr, " + %d%s\n", id, FormatCapture(t0->capture).c_str()); + + if (ip->hint() == 0) + break; + a = {id+ip->hint(), NULL}; + goto Loop; + + case kInstMatch: + // Save state; will pick up at next byte. + t = Incref(t0); + *tp = t; + if (ExtraDebug) + fprintf(stderr, " ! %d%s\n", id, FormatCapture(t0->capture).c_str()); + + Next: + if (ip->last()) + break; + a = {id+1, NULL}; + goto Loop; + + case kInstEmptyWidth: + if (!ip->last()) + stk[nstk++] = {id+1, NULL}; + + // Continue on if we have all the right flag bits. + if (ip->empty() & ~Prog::EmptyFlags(context, p)) + break; + a = {ip->out(), NULL}; + goto Loop; + } + } +} + +// Run runq on byte c, appending new states to nextq. +// Updates matched_ and match_ as new, better matches are found. +// context is used (with p) for evaluating empty-width specials. +// p is the position of byte c in the input string for AddToThreadq; +// p-1 will be used when processing Match instructions. +// Frees all the threads on runq. +// If there is a shortcut to the end, returns that shortcut. +int NFA::Step(Threadq* runq, Threadq* nextq, int c, const StringPiece& context, + const char* p) { + nextq->clear(); + + for (Threadq::iterator i = runq->begin(); i != runq->end(); ++i) { + Thread* t = i->value(); + if (t == NULL) + continue; + + if (longest_) { + // Can skip any threads started after our current best match. + if (matched_ && match_[0] < t->capture[0]) { + Decref(t); + continue; + } + } + + int id = i->index(); + Prog::Inst* ip = prog_->inst(id); + + switch (ip->opcode()) { + default: + // Should only see the values handled below. +#if 0 + LOG(DFATAL) << "Unhandled " << ip->opcode() << " in step"; +#endif + break; + + case kInstByteRange: + AddToThreadq(nextq, ip->out(), c, context, p, t); + break; + + case kInstAltMatch: + if (i != runq->begin()) + break; + // The match is ours if we want it. + if (ip->greedy(prog_) || longest_) { + CopyCapture(match_, t->capture); + matched_ = true; + + Decref(t); + for (++i; i != runq->end(); ++i) { + if (i->value() != NULL) + Decref(i->value()); + } + runq->clear(); + if (ip->greedy(prog_)) + return ip->out1(); + return ip->out(); + } + break; + + case kInstMatch: { + // Avoid invoking undefined behavior (arithmetic on a null pointer) + // by storing p instead of p-1. (What would the latter even mean?!) + // This complements the special case in NFA::Search(). + if (p == NULL) { + CopyCapture(match_, t->capture); + match_[1] = p; + matched_ = true; + break; + } + + if (endmatch_ && p-1 != etext_) + break; + + if (longest_) { + // Leftmost-longest mode: save this match only if + // it is either farther to the left or at the same + // point but longer than an existing match. + if (!matched_ || t->capture[0] < match_[0] || + (t->capture[0] == match_[0] && p-1 > match_[1])) { + CopyCapture(match_, t->capture); + match_[1] = p-1; + matched_ = true; + } + } else { + // Leftmost-biased mode: this match is by definition + // better than what we've already found (see next line). + CopyCapture(match_, t->capture); + match_[1] = p-1; + matched_ = true; + + // Cut off the threads that can only find matches + // worse than the one we just found: don't run the + // rest of the current Threadq. + Decref(t); + for (++i; i != runq->end(); ++i) { + if (i->value() != NULL) + Decref(i->value()); + } + runq->clear(); + return 0; + } + break; + } + } + Decref(t); + } + runq->clear(); + return 0; +} + +std::string NFA::FormatCapture(const char** capture) { + std::string s; + for (int i = 0; i < ncapture_; i+=2) { + if (capture[i] == NULL) + s += "(?,?)"; + else if (capture[i+1] == NULL) + s += StringPrintf("(%td,?)", + capture[i] - btext_); + else + s += StringPrintf("(%td,%td)", + capture[i] - btext_, + capture[i+1] - btext_); + } + return s; +} + +bool NFA::Search(const StringPiece& text, const StringPiece& const_context, + bool anchored, bool longest, + StringPiece* submatch, int nsubmatch) { + if (start_ == 0) + return false; + + StringPiece context = const_context; + if (context.data() == NULL) + context = text; + + // Sanity check: make sure that text lies within context. + if (text.begin() < context.begin() || text.end() > context.end()) { +#if 0 + LOG(DFATAL) << "context does not contain text"; +#endif + return false; + } + + if (prog_->anchor_start() && context.begin() != text.begin()) + return false; + if (prog_->anchor_end() && context.end() != text.end()) + return false; + anchored |= prog_->anchor_start(); + if (prog_->anchor_end()) { + longest = true; + endmatch_ = true; + } + + if (nsubmatch < 0) { +#if 0 + LOG(DFATAL) << "Bad args: nsubmatch=" << nsubmatch; +#endif + return false; + } + + // Save search parameters. + ncapture_ = 2*nsubmatch; + longest_ = longest; + + if (nsubmatch == 0) { + // We need to maintain match[0], both to distinguish the + // longest match (if longest is true) and also to tell + // whether we've seen any matches at all. + ncapture_ = 2; + } + + match_ = new const char*[ncapture_]; + memset(match_, 0, ncapture_*sizeof match_[0]); + matched_ = false; + + // For debugging prints. + btext_ = context.data(); + // For convenience. + etext_ = text.data() + text.size(); + + if (ExtraDebug) + fprintf(stderr, "NFA::Search %s (context: %s) anchored=%d longest=%d\n", + std::string(text).c_str(), std::string(context).c_str(), anchored, longest); + + // Set up search. + Threadq* runq = &q0_; + Threadq* nextq = &q1_; + runq->clear(); + nextq->clear(); + + // Loop over the text, stepping the machine. + for (const char* p = text.data();; p++) { + if (ExtraDebug) { + int c = 0; + if (p == btext_) + c = '^'; + else if (p > etext_) + c = '$'; + else if (p < etext_) + c = p[0] & 0xFF; + + fprintf(stderr, "%c:", c); + for (Threadq::iterator i = runq->begin(); i != runq->end(); ++i) { + Thread* t = i->value(); + if (t == NULL) + continue; + fprintf(stderr, " %d%s", i->index(), FormatCapture(t->capture).c_str()); + } + fprintf(stderr, "\n"); + } + + // This is a no-op the first time around the loop because runq is empty. + int id = Step(runq, nextq, p < etext_ ? p[0] & 0xFF : -1, context, p); + DCHECK_EQ(runq->size(), 0); + using std::swap; + swap(nextq, runq); + nextq->clear(); + if (id != 0) { + // We're done: full match ahead. + p = etext_; + for (;;) { + Prog::Inst* ip = prog_->inst(id); + switch (ip->opcode()) { + default: +#if 0 + LOG(DFATAL) << "Unexpected opcode in short circuit: " << ip->opcode(); +#endif + break; + + case kInstCapture: + if (ip->cap() < ncapture_) + match_[ip->cap()] = p; + id = ip->out(); + continue; + + case kInstNop: + id = ip->out(); + continue; + + case kInstMatch: + match_[1] = p; + matched_ = true; + break; + } + break; + } + break; + } + + if (p > etext_) + break; + + // Start a new thread if there have not been any matches. + // (No point in starting a new thread if there have been + // matches, since it would be to the right of the match + // we already found.) + if (!matched_ && (!anchored || p == text.data())) { + // Try to use prefix accel (e.g. memchr) to skip ahead. + // The search must be unanchored and there must be zero + // possible matches already. + if (!anchored && runq->size() == 0 && + p < etext_ && prog_->can_prefix_accel()) { + p = reinterpret_cast(prog_->PrefixAccel(p, etext_ - p)); + if (p == NULL) + p = etext_; + } + + Thread* t = AllocThread(); + CopyCapture(t->capture, match_); + t->capture[0] = p; + AddToThreadq(runq, start_, p < etext_ ? p[0] & 0xFF : -1, context, p, + t); + Decref(t); + } + + // If all the threads have died, stop early. + if (runq->size() == 0) { + if (ExtraDebug) + fprintf(stderr, "dead\n"); + break; + } + + // Avoid invoking undefined behavior (arithmetic on a null pointer) + // by simply not continuing the loop. + // This complements the special case in NFA::Step(). + if (p == NULL) { + (void)Step(runq, nextq, p < etext_ ? p[0] & 0xFF : -1, context, p); + DCHECK_EQ(runq->size(), 0); + using std::swap; + swap(nextq, runq); + nextq->clear(); + break; + } + } + + for (Threadq::iterator i = runq->begin(); i != runq->end(); ++i) { + if (i->value() != NULL) + Decref(i->value()); + } + + if (matched_) { + for (int i = 0; i < nsubmatch; i++) + submatch[i] = + StringPiece(match_[2 * i], + static_cast(match_[2 * i + 1] - match_[2 * i])); + if (ExtraDebug) + fprintf(stderr, "match (%td,%td)\n", + match_[0] - btext_, + match_[1] - btext_); + return true; + } + return false; +} + +bool +Prog::SearchNFA(const StringPiece& text, const StringPiece& context, + Anchor anchor, MatchKind kind, + StringPiece* match, int nmatch) { + if (ExtraDebug) + Dump(); + + NFA nfa(this); + StringPiece sp; + if (kind == kFullMatch) { + anchor = kAnchored; + if (nmatch == 0) { + match = &sp; + nmatch = 1; + } + } + if (!nfa.Search(text, context, anchor == kAnchored, kind != kFirstMatch, match, nmatch)) + return false; + if (kind == kFullMatch && match[0].end() != text.end()) + return false; + return true; +} + +// For each instruction i in the program reachable from the start, compute the +// number of instructions reachable from i by following only empty transitions +// and record that count as fanout[i]. +// +// fanout holds the results and is also the work queue for the outer iteration. +// reachable holds the reached nodes for the inner iteration. +void Prog::Fanout(SparseArray* fanout) { + DCHECK_EQ(fanout->max_size(), size()); + SparseSet reachable(size()); + fanout->clear(); + fanout->set_new(start(), 0); + for (SparseArray::iterator i = fanout->begin(); i != fanout->end(); ++i) { + int* count = &i->value(); + reachable.clear(); + reachable.insert(i->index()); + for (SparseSet::iterator j = reachable.begin(); j != reachable.end(); ++j) { + int id = *j; + Prog::Inst* ip = inst(id); + switch (ip->opcode()) { + default: +#if 0 + LOG(DFATAL) << "unhandled " << ip->opcode() << " in Prog::Fanout()"; +#endif + break; + + case kInstByteRange: + if (!ip->last()) + reachable.insert(id+1); + + (*count)++; + if (!fanout->has_index(ip->out())) { + fanout->set_new(ip->out(), 0); + } + break; + + case kInstAltMatch: + DCHECK(!ip->last()); + reachable.insert(id+1); + break; + + case kInstCapture: + case kInstEmptyWidth: + case kInstNop: + if (!ip->last()) + reachable.insert(id+1); + + reachable.insert(ip->out()); + break; + + case kInstMatch: + if (!ip->last()) + reachable.insert(id+1); + break; + + case kInstFail: + break; + } + } + } +} + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/onepass.cc b/third_party/opa/wasm/src/re2/re2/onepass.cc new file mode 100644 index 000000000000..d82524a2940a --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/onepass.cc @@ -0,0 +1,639 @@ +// Copyright 2008 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Tested by search_test.cc. +// +// Prog::SearchOnePass is an efficient implementation of +// regular expression search with submatch tracking for +// what I call "one-pass regular expressions". (An alternate +// name might be "backtracking-free regular expressions".) +// +// One-pass regular expressions have the property that +// at each input byte during an anchored match, there may be +// multiple alternatives but only one can proceed for any +// given input byte. +// +// For example, the regexp /x*yx*/ is one-pass: you read +// x's until a y, then you read the y, then you keep reading x's. +// At no point do you have to guess what to do or back up +// and try a different guess. +// +// On the other hand, /x*x/ is not one-pass: when you're +// looking at an input "x", it's not clear whether you should +// use it to extend the x* or as the final x. +// +// More examples: /([^ ]*) (.*)/ is one-pass; /(.*) (.*)/ is not. +// /(\d+)-(\d+)/ is one-pass; /(\d+).(\d+)/ is not. +// +// A simple intuition for identifying one-pass regular expressions +// is that it's always immediately obvious when a repetition ends. +// It must also be immediately obvious which branch of an | to take: +// +// /x(y|z)/ is one-pass, but /(xy|xz)/ is not. +// +// The NFA-based search in nfa.cc does some bookkeeping to +// avoid the need for backtracking and its associated exponential blowup. +// But if we have a one-pass regular expression, there is no +// possibility of backtracking, so there is no need for the +// extra bookkeeping. Hence, this code. +// +// On a one-pass regular expression, the NFA code in nfa.cc +// runs at about 1/20 of the backtracking-based PCRE speed. +// In contrast, the code in this file runs at about the same +// speed as PCRE. +// +// One-pass regular expressions get used a lot when RE is +// used for parsing simple strings, so it pays off to +// notice them and handle them efficiently. +// +// See also Anne Brüggemann-Klein and Derick Wood, +// "One-unambiguous regular languages", Information and Computation 142(2). + +#include +#include +#include +#include +#include +#include + +#include "util/util.h" +#include "util/logging.h" +#include "util/strutil.h" +#include "util/utf.h" +#include "re2/pod_array.h" +#include "re2/prog.h" +#include "re2/sparse_set.h" +#include "re2/stringpiece.h" + +// Silence "zero-sized array in struct/union" warning for OneState::action. +#ifdef _MSC_VER +#pragma warning(disable: 4200) +#endif + +namespace re2 { + +static const bool ExtraDebug = false; + +// The key insight behind this implementation is that the +// non-determinism in an NFA for a one-pass regular expression +// is contained. To explain what that means, first a +// refresher about what regular expression programs look like +// and how the usual NFA execution runs. +// +// In a regular expression program, only the kInstByteRange +// instruction processes an input byte c and moves on to the +// next byte in the string (it does so if c is in the given range). +// The kInstByteRange instructions correspond to literal characters +// and character classes in the regular expression. +// +// The kInstAlt instructions are used as wiring to connect the +// kInstByteRange instructions together in interesting ways when +// implementing | + and *. +// The kInstAlt instruction forks execution, like a goto that +// jumps to ip->out() and ip->out1() in parallel. Each of the +// resulting computation paths is called a thread. +// +// The other instructions -- kInstEmptyWidth, kInstMatch, kInstCapture -- +// are interesting in their own right but like kInstAlt they don't +// advance the input pointer. Only kInstByteRange does. +// +// The automaton execution in nfa.cc runs all the possible +// threads of execution in lock-step over the input. To process +// a particular byte, each thread gets run until it either dies +// or finds a kInstByteRange instruction matching the byte. +// If the latter happens, the thread stops just past the +// kInstByteRange instruction (at ip->out()) and waits for +// the other threads to finish processing the input byte. +// Then, once all the threads have processed that input byte, +// the whole process repeats. The kInstAlt state instruction +// might create new threads during input processing, but no +// matter what, all the threads stop after a kInstByteRange +// and wait for the other threads to "catch up". +// Running in lock step like this ensures that the NFA reads +// the input string only once. +// +// Each thread maintains its own set of capture registers +// (the string positions at which it executed the kInstCapture +// instructions corresponding to capturing parentheses in the +// regular expression). Repeated copying of the capture registers +// is the main performance bottleneck in the NFA implementation. +// +// A regular expression program is "one-pass" if, no matter what +// the input string, there is only one thread that makes it +// past a kInstByteRange instruction at each input byte. This means +// that there is in some sense only one active thread throughout +// the execution. Other threads might be created during the +// processing of an input byte, but they are ephemeral: only one +// thread is left to start processing the next input byte. +// This is what I meant above when I said the non-determinism +// was "contained". +// +// To execute a one-pass regular expression program, we can build +// a DFA (no non-determinism) that has at most as many states as +// the NFA (compare this to the possibly exponential number of states +// in the general case). Each state records, for each possible +// input byte, the next state along with the conditions required +// before entering that state -- empty-width flags that must be true +// and capture operations that must be performed. It also records +// whether a set of conditions required to finish a match at that +// point in the input rather than process the next byte. + +// A state in the one-pass NFA - just an array of actions indexed +// by the bytemap_[] of the next input byte. (The bytemap +// maps next input bytes into equivalence classes, to reduce +// the memory footprint.) +struct OneState { + uint32_t matchcond; // conditions to match right now. + uint32_t action[]; +}; + +// The uint32_t conditions in the action are a combination of +// condition and capture bits and the next state. The bottom 16 bits +// are the condition and capture bits, and the top 16 are the index of +// the next state. +// +// Bits 0-5 are the empty-width flags from prog.h. +// Bit 6 is kMatchWins, which means the match takes +// priority over moving to next in a first-match search. +// The remaining bits mark capture registers that should +// be set to the current input position. The capture bits +// start at index 2, since the search loop can take care of +// cap[0], cap[1] (the overall match position). +// That means we can handle up to 5 capturing parens: $1 through $4, plus $0. +// No input position can satisfy both kEmptyWordBoundary +// and kEmptyNonWordBoundary, so we can use that as a sentinel +// instead of needing an extra bit. + +static const int kIndexShift = 16; // number of bits below index +static const int kEmptyShift = 6; // number of empty flags in prog.h +static const int kRealCapShift = kEmptyShift + 1; +static const int kRealMaxCap = (kIndexShift - kRealCapShift) / 2 * 2; + +// Parameters used to skip over cap[0], cap[1]. +static const int kCapShift = kRealCapShift - 2; +static const int kMaxCap = kRealMaxCap + 2; + +static const uint32_t kMatchWins = 1 << kEmptyShift; +static const uint32_t kCapMask = ((1 << kRealMaxCap) - 1) << kRealCapShift; + +static const uint32_t kImpossible = kEmptyWordBoundary | kEmptyNonWordBoundary; + +// Check, at compile time, that prog.h agrees with math above. +// This function is never called. +void OnePass_Checks() { + static_assert((1<(nodes + statesize*nodeindex); +} + +bool Prog::SearchOnePass(const StringPiece& text, + const StringPiece& const_context, + Anchor anchor, MatchKind kind, + StringPiece* match, int nmatch) { + if (anchor != kAnchored && kind != kFullMatch) { +#if 0 + LOG(DFATAL) << "Cannot use SearchOnePass for unanchored matches."; +#endif + return false; + } + + // Make sure we have at least cap[1], + // because we use it to tell if we matched. + int ncap = 2*nmatch; + if (ncap < 2) + ncap = 2; + + const char* cap[kMaxCap]; + for (int i = 0; i < ncap; i++) + cap[i] = NULL; + + const char* matchcap[kMaxCap]; + for (int i = 0; i < ncap; i++) + matchcap[i] = NULL; + + StringPiece context = const_context; + if (context.data() == NULL) + context = text; + if (anchor_start() && context.begin() != text.begin()) + return false; + if (anchor_end() && context.end() != text.end()) + return false; + if (anchor_end()) + kind = kFullMatch; + + uint8_t* nodes = onepass_nodes_.data(); + int statesize = sizeof(OneState) + bytemap_range()*sizeof(uint32_t); + // start() is always mapped to the zeroth OneState. + OneState* state = IndexToNode(nodes, statesize, 0); + uint8_t* bytemap = bytemap_; + const char* bp = text.data(); + const char* ep = text.data() + text.size(); + const char* p; + bool matched = false; + matchcap[0] = bp; + cap[0] = bp; + uint32_t nextmatchcond = state->matchcond; + for (p = bp; p < ep; p++) { + int c = bytemap[*p & 0xFF]; + uint32_t matchcond = nextmatchcond; + uint32_t cond = state->action[c]; + + // Determine whether we can reach act->next. + // If so, advance state and nextmatchcond. + if ((cond & kEmptyAllFlags) == 0 || Satisfy(cond, context, p)) { + uint32_t nextindex = cond >> kIndexShift; + state = IndexToNode(nodes, statesize, nextindex); + nextmatchcond = state->matchcond; + } else { + state = NULL; + nextmatchcond = kImpossible; + } + + // This code section is carefully tuned. + // The goto sequence is about 10% faster than the + // obvious rewrite as a large if statement in the + // ASCIIMatchRE2 and DotMatchRE2 benchmarks. + + // Saving the match capture registers is expensive. + // Is this intermediate match worth thinking about? + + // Not if we want a full match. + if (kind == kFullMatch) + goto skipmatch; + + // Not if it's impossible. + if (matchcond == kImpossible) + goto skipmatch; + + // Not if the possible match is beaten by the certain + // match at the next byte. When this test is useless + // (e.g., HTTPPartialMatchRE2) it slows the loop by + // about 10%, but when it avoids work (e.g., DotMatchRE2), + // it cuts the loop execution by about 45%. + if ((cond & kMatchWins) == 0 && (nextmatchcond & kEmptyAllFlags) == 0) + goto skipmatch; + + // Finally, the match conditions must be satisfied. + if ((matchcond & kEmptyAllFlags) == 0 || Satisfy(matchcond, context, p)) { + for (int i = 2; i < 2*nmatch; i++) + matchcap[i] = cap[i]; + if (nmatch > 1 && (matchcond & kCapMask)) + ApplyCaptures(matchcond, p, matchcap, ncap); + matchcap[1] = p; + matched = true; + + // If we're in longest match mode, we have to keep + // going and see if we find a longer match. + // In first match mode, we can stop if the match + // takes priority over the next state for this input byte. + // That bit is per-input byte and thus in cond, not matchcond. + if (kind == kFirstMatch && (cond & kMatchWins)) + goto done; + } + + skipmatch: + if (state == NULL) + goto done; + if ((cond & kCapMask) && nmatch > 1) + ApplyCaptures(cond, p, cap, ncap); + } + + // Look for match at end of input. + { + uint32_t matchcond = state->matchcond; + if (matchcond != kImpossible && + ((matchcond & kEmptyAllFlags) == 0 || Satisfy(matchcond, context, p))) { + if (nmatch > 1 && (matchcond & kCapMask)) + ApplyCaptures(matchcond, p, cap, ncap); + for (int i = 2; i < ncap; i++) + matchcap[i] = cap[i]; + matchcap[1] = p; + matched = true; + } + } + +done: + if (!matched) + return false; + for (int i = 0; i < nmatch; i++) + match[i] = + StringPiece(matchcap[2 * i], + static_cast(matchcap[2 * i + 1] - matchcap[2 * i])); + return true; +} + + +// Analysis to determine whether a given regexp program is one-pass. + +// If ip is not on workq, adds ip to work queue and returns true. +// If ip is already on work queue, does nothing and returns false. +// If ip is NULL, does nothing and returns true (pretends to add it). +typedef SparseSet Instq; +static bool AddQ(Instq *q, int id) { + if (id == 0) + return true; + if (q->contains(id)) + return false; + q->insert(id); + return true; +} + +struct InstCond { + int id; + uint32_t cond; +}; + +// Returns whether this is a one-pass program; that is, +// returns whether it is safe to use SearchOnePass on this program. +// These conditions must be true for any instruction ip: +// +// (1) for any other Inst nip, there is at most one input-free +// path from ip to nip. +// (2) there is at most one kInstByte instruction reachable from +// ip that matches any particular byte c. +// (3) there is at most one input-free path from ip to a kInstMatch +// instruction. +// +// This is actually just a conservative approximation: it might +// return false when the answer is true, when kInstEmptyWidth +// instructions are involved. +// Constructs and saves corresponding one-pass NFA on success. +bool Prog::IsOnePass() { + if (did_onepass_) + return onepass_nodes_.data() != NULL; + did_onepass_ = true; + + if (start() == 0) // no match + return false; + + // Steal memory for the one-pass NFA from the overall DFA budget. + // Willing to use at most 1/4 of the DFA budget (heuristic). + // Limit max node count to 65000 as a conservative estimate to + // avoid overflowing 16-bit node index in encoding. + int maxnodes = 2 + inst_count(kInstByteRange); + int statesize = sizeof(OneState) + bytemap_range()*sizeof(uint32_t); + if (maxnodes >= 65000 || dfa_mem_ / 4 / statesize < maxnodes) + return false; + + // Flood the graph starting at the start state, and check + // that in each reachable state, each possible byte leads + // to a unique next state. + int stacksize = inst_count(kInstCapture) + + inst_count(kInstEmptyWidth) + + inst_count(kInstNop) + 1; // + 1 for start inst + PODArray stack(stacksize); + + int size = this->size(); + PODArray nodebyid(size); // indexed by ip + memset(nodebyid.data(), 0xFF, size*sizeof nodebyid[0]); + + // Originally, nodes was a uint8_t[maxnodes*statesize], but that was + // unnecessarily optimistic: why allocate a large amount of memory + // upfront for a large program when it is unlikely to be one-pass? + std::vector nodes; + + Instq tovisit(size), workq(size); + AddQ(&tovisit, start()); + nodebyid[start()] = 0; + int nalloc = 1; + nodes.insert(nodes.end(), statesize, 0); + for (Instq::iterator it = tovisit.begin(); it != tovisit.end(); ++it) { + int id = *it; + int nodeindex = nodebyid[id]; + OneState* node = IndexToNode(nodes.data(), statesize, nodeindex); + + // Flood graph using manual stack, filling in actions as found. + // Default is none. + for (int b = 0; b < bytemap_range_; b++) + node->action[b] = kImpossible; + node->matchcond = kImpossible; + + workq.clear(); + bool matched = false; + int nstack = 0; + stack[nstack].id = id; + stack[nstack++].cond = 0; + while (nstack > 0) { + int id = stack[--nstack].id; + uint32_t cond = stack[nstack].cond; + + Loop: + Prog::Inst* ip = inst(id); + switch (ip->opcode()) { + default: +#if 0 + LOG(DFATAL) << "unhandled opcode: " << ip->opcode(); +#endif + break; + + case kInstAltMatch: + // TODO(rsc): Ignoring kInstAltMatch optimization. + // Should implement it in this engine, but it's subtle. + DCHECK(!ip->last()); + // If already on work queue, (1) is violated: bail out. + if (!AddQ(&workq, id+1)) + goto fail; + id = id+1; + goto Loop; + + case kInstByteRange: { + int nextindex = nodebyid[ip->out()]; + if (nextindex == -1) { + if (nalloc >= maxnodes) { +#if 0 + if (ExtraDebug) + LOG(ERROR) << StringPrintf( + "Not OnePass: hit node limit %d >= %d", nalloc, maxnodes); +#endif + goto fail; + } + nextindex = nalloc; + AddQ(&tovisit, ip->out()); + nodebyid[ip->out()] = nalloc; + nalloc++; + nodes.insert(nodes.end(), statesize, 0); + // Update node because it might have been invalidated. + node = IndexToNode(nodes.data(), statesize, nodeindex); + } + for (int c = ip->lo(); c <= ip->hi(); c++) { + int b = bytemap_[c]; + // Skip any bytes immediately after c that are also in b. + while (c < 256-1 && bytemap_[c+1] == b) + c++; + uint32_t act = node->action[b]; + uint32_t newact = (nextindex << kIndexShift) | cond; + if (matched) + newact |= kMatchWins; + if ((act & kImpossible) == kImpossible) { + node->action[b] = newact; + } else if (act != newact) { +#if 0 + if (ExtraDebug) + LOG(ERROR) << StringPrintf( + "Not OnePass: conflict on byte %#x at state %d", c, *it); +#endif + goto fail; + } + } + if (ip->foldcase()) { + Rune lo = std::max(ip->lo(), 'a') + 'A' - 'a'; + Rune hi = std::min(ip->hi(), 'z') + 'A' - 'a'; + for (int c = lo; c <= hi; c++) { + int b = bytemap_[c]; + // Skip any bytes immediately after c that are also in b. + while (c < 256-1 && bytemap_[c+1] == b) + c++; + uint32_t act = node->action[b]; + uint32_t newact = (nextindex << kIndexShift) | cond; + if (matched) + newact |= kMatchWins; + if ((act & kImpossible) == kImpossible) { + node->action[b] = newact; + } else if (act != newact) { +#if 0 + if (ExtraDebug) + LOG(ERROR) << StringPrintf( + "Not OnePass: conflict on byte %#x at state %d", c, *it); +#endif + goto fail; + } + } + } + + if (ip->last()) + break; + // If already on work queue, (1) is violated: bail out. + if (!AddQ(&workq, id+1)) + goto fail; + id = id+1; + goto Loop; + } + + case kInstCapture: + case kInstEmptyWidth: + case kInstNop: + if (!ip->last()) { + // If already on work queue, (1) is violated: bail out. + if (!AddQ(&workq, id+1)) + goto fail; + stack[nstack].id = id+1; + stack[nstack++].cond = cond; + } + + if (ip->opcode() == kInstCapture && ip->cap() < kMaxCap) + cond |= (1 << kCapShift) << ip->cap(); + if (ip->opcode() == kInstEmptyWidth) + cond |= ip->empty(); + + // kInstCapture and kInstNop always proceed to ip->out(). + // kInstEmptyWidth only sometimes proceeds to ip->out(), + // but as a conservative approximation we assume it always does. + // We could be a little more precise by looking at what c + // is, but that seems like overkill. + + // If already on work queue, (1) is violated: bail out. + if (!AddQ(&workq, ip->out())) { +#if 0 + if (ExtraDebug) + LOG(ERROR) << StringPrintf( + "Not OnePass: multiple paths %d -> %d", *it, ip->out()); +#endif + goto fail; + } + id = ip->out(); + goto Loop; + + case kInstMatch: + if (matched) { + // (3) is violated +#if 0 + if (ExtraDebug) + LOG(ERROR) << StringPrintf( + "Not OnePass: multiple matches from %d", *it); +#endif + goto fail; + } + matched = true; + node->matchcond = cond; + + if (ip->last()) + break; + // If already on work queue, (1) is violated: bail out. + if (!AddQ(&workq, id+1)) + goto fail; + id = id+1; + goto Loop; + + case kInstFail: + break; + } + } + } + +#if 0 + if (ExtraDebug) { // For debugging, dump one-pass NFA to LOG(ERROR). + LOG(ERROR) << "bytemap:\n" << DumpByteMap(); + LOG(ERROR) << "prog:\n" << Dump(); + + std::map idmap; + for (int i = 0; i < size; i++) + if (nodebyid[i] != -1) + idmap[nodebyid[i]] = i; + + std::string dump; + for (Instq::iterator it = tovisit.begin(); it != tovisit.end(); ++it) { + int id = *it; + int nodeindex = nodebyid[id]; + if (nodeindex == -1) + continue; + OneState* node = IndexToNode(nodes.data(), statesize, nodeindex); + dump += StringPrintf("node %d id=%d: matchcond=%#x\n", + nodeindex, id, node->matchcond); + for (int i = 0; i < bytemap_range_; i++) { + if ((node->action[i] & kImpossible) == kImpossible) + continue; + dump += StringPrintf(" %d cond %#x -> %d id=%d\n", + i, node->action[i] & 0xFFFF, + node->action[i] >> kIndexShift, + idmap[node->action[i] >> kIndexShift]); + } + } + LOG(ERROR) << "nodes:\n" << dump; + } +#endif + + dfa_mem_ -= nalloc*statesize; + onepass_nodes_ = PODArray(nalloc*statesize); + memmove(onepass_nodes_.data(), nodes.data(), nalloc*statesize); + return true; + +fail: + return false; +} + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/parse.cc b/third_party/opa/wasm/src/re2/re2/parse.cc new file mode 100644 index 000000000000..007aba16a035 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/parse.cc @@ -0,0 +1,2482 @@ +// Copyright 2006 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Regular expression parser. + +// The parser is a simple precedence-based parser with a +// manual stack. The parsing work is done by the methods +// of the ParseState class. The Regexp::Parse function is +// essentially just a lexer that calls the ParseState method +// for each token. + +// The parser recognizes POSIX extended regular expressions +// excluding backreferences, collating elements, and collating +// classes. It also allows the empty string as a regular expression +// and recognizes the Perl escape sequences \d, \s, \w, \D, \S, and \W. +// See regexp.h for rationale. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/util.h" +#include "util/logging.h" +#include "util/strutil.h" +#include "util/utf.h" +#include "re2/pod_array.h" +#include "re2/regexp.h" +#include "re2/stringpiece.h" +#include "re2/unicode_casefold.h" +#include "re2/unicode_groups.h" +#include "re2/walker-inl.h" + +#if defined(RE2_USE_ICU) +#include "unicode/uniset.h" +#include "unicode/unistr.h" +#include "unicode/utypes.h" +#endif + +namespace re2 { + +// Reduce the maximum repeat count by an order of magnitude when fuzzing. +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +static const int kMaxRepeat = 100; +#else +static const int kMaxRepeat = 1000; +#endif + +// Regular expression parse state. +// The list of parsed regexps so far is maintained as a vector of +// Regexp pointers called the stack. Left parenthesis and vertical +// bar markers are also placed on the stack, as Regexps with +// non-standard opcodes. +// Scanning a left parenthesis causes the parser to push a left parenthesis +// marker on the stack. +// Scanning a vertical bar causes the parser to pop the stack until it finds a +// vertical bar or left parenthesis marker (not popping the marker), +// concatenate all the popped results, and push them back on +// the stack (DoConcatenation). +// Scanning a right parenthesis causes the parser to act as though it +// has seen a vertical bar, which then leaves the top of the stack in the +// form LeftParen regexp VerticalBar regexp VerticalBar ... regexp VerticalBar. +// The parser pops all this off the stack and creates an alternation of the +// regexps (DoAlternation). + +class Regexp::ParseState { + public: + ParseState(ParseFlags flags, const StringPiece& whole_regexp, + RegexpStatus* status); + ~ParseState(); + + ParseFlags flags() { return flags_; } + int rune_max() { return rune_max_; } + + // Parse methods. All public methods return a bool saying + // whether parsing should continue. If a method returns + // false, it has set fields in *status_, and the parser + // should return NULL. + + // Pushes the given regular expression onto the stack. + // Could check for too much memory used here. + bool PushRegexp(Regexp* re); + + // Pushes the literal rune r onto the stack. + bool PushLiteral(Rune r); + + // Pushes a regexp with the given op (and no args) onto the stack. + bool PushSimpleOp(RegexpOp op); + + // Pushes a ^ onto the stack. + bool PushCaret(); + + // Pushes a \b (word == true) or \B (word == false) onto the stack. + bool PushWordBoundary(bool word); + + // Pushes a $ onto the stack. + bool PushDollar(); + + // Pushes a . onto the stack + bool PushDot(); + + // Pushes a repeat operator regexp onto the stack. + // A valid argument for the operator must already be on the stack. + // s is the name of the operator, for use in error messages. + bool PushRepeatOp(RegexpOp op, const StringPiece& s, bool nongreedy); + + // Pushes a repetition regexp onto the stack. + // A valid argument for the operator must already be on the stack. + bool PushRepetition(int min, int max, const StringPiece& s, bool nongreedy); + + // Checks whether a particular regexp op is a marker. + bool IsMarker(RegexpOp op); + + // Processes a left parenthesis in the input. + // Pushes a marker onto the stack. + bool DoLeftParen(const StringPiece& name); + bool DoLeftParenNoCapture(); + + // Processes a vertical bar in the input. + bool DoVerticalBar(); + + // Processes a right parenthesis in the input. + bool DoRightParen(); + + // Processes the end of input, returning the final regexp. + Regexp* DoFinish(); + + // Finishes the regexp if necessary, preparing it for use + // in a more complicated expression. + // If it is a CharClassBuilder, converts into a CharClass. + Regexp* FinishRegexp(Regexp*); + + // These routines don't manipulate the parse stack + // directly, but they do need to look at flags_. + // ParseCharClass also manipulates the internals of Regexp + // while creating *out_re. + + // Parse a character class into *out_re. + // Removes parsed text from s. + bool ParseCharClass(StringPiece* s, Regexp** out_re, + RegexpStatus* status); + + // Parse a character class character into *rp. + // Removes parsed text from s. + bool ParseCCCharacter(StringPiece* s, Rune *rp, + const StringPiece& whole_class, + RegexpStatus* status); + + // Parse a character class range into rr. + // Removes parsed text from s. + bool ParseCCRange(StringPiece* s, RuneRange* rr, + const StringPiece& whole_class, + RegexpStatus* status); + + // Parse a Perl flag set or non-capturing group from s. + bool ParsePerlFlags(StringPiece* s); + + + // Finishes the current concatenation, + // collapsing it into a single regexp on the stack. + void DoConcatenation(); + + // Finishes the current alternation, + // collapsing it to a single regexp on the stack. + void DoAlternation(); + + // Generalized DoAlternation/DoConcatenation. + void DoCollapse(RegexpOp op); + + // Maybe concatenate Literals into LiteralString. + bool MaybeConcatString(int r, ParseFlags flags); + +private: + ParseFlags flags_; + StringPiece whole_regexp_; + RegexpStatus* status_; + Regexp* stacktop_; + int ncap_; // number of capturing parens seen + int rune_max_; // maximum char value for this encoding + + ParseState(const ParseState&) = delete; + ParseState& operator=(const ParseState&) = delete; +}; + +// Pseudo-operators - only on parse stack. +const RegexpOp kLeftParen = static_cast(kMaxRegexpOp+1); +const RegexpOp kVerticalBar = static_cast(kMaxRegexpOp+2); + +Regexp::ParseState::ParseState(ParseFlags flags, + const StringPiece& whole_regexp, + RegexpStatus* status) + : flags_(flags), whole_regexp_(whole_regexp), + status_(status), stacktop_(NULL), ncap_(0) { + if (flags_ & Latin1) + rune_max_ = 0xFF; + else + rune_max_ = Runemax; +} + +// Cleans up by freeing all the regexps on the stack. +Regexp::ParseState::~ParseState() { + Regexp* next; + for (Regexp* re = stacktop_; re != NULL; re = next) { + next = re->down_; + re->down_ = NULL; + if (re->op() == kLeftParen) + delete re->name_; + re->Decref(); + } +} + +// Finishes the regexp if necessary, preparing it for use in +// a more complex expression. +// If it is a CharClassBuilder, converts into a CharClass. +Regexp* Regexp::ParseState::FinishRegexp(Regexp* re) { + if (re == NULL) + return NULL; + re->down_ = NULL; + + if (re->op_ == kRegexpCharClass && re->ccb_ != NULL) { + CharClassBuilder* ccb = re->ccb_; + re->ccb_ = NULL; + re->cc_ = ccb->GetCharClass(); + delete ccb; + } + + return re; +} + +// Pushes the given regular expression onto the stack. +// Could check for too much memory used here. +bool Regexp::ParseState::PushRegexp(Regexp* re) { + MaybeConcatString(-1, NoParseFlags); + + // Special case: a character class of one character is just + // a literal. This is a common idiom for escaping + // single characters (e.g., [.] instead of \.), and some + // analysis does better with fewer character classes. + // Similarly, [Aa] can be rewritten as a literal A with ASCII case folding. + if (re->op_ == kRegexpCharClass && re->ccb_ != NULL) { + re->ccb_->RemoveAbove(rune_max_); + if (re->ccb_->size() == 1) { + Rune r = re->ccb_->begin()->lo; + re->Decref(); + re = new Regexp(kRegexpLiteral, flags_); + re->rune_ = r; + } else if (re->ccb_->size() == 2) { + Rune r = re->ccb_->begin()->lo; + if ('A' <= r && r <= 'Z' && re->ccb_->Contains(r + 'a' - 'A')) { + re->Decref(); + re = new Regexp(kRegexpLiteral, flags_ | FoldCase); + re->rune_ = r + 'a' - 'A'; + } + } + } + + if (!IsMarker(re->op())) + re->simple_ = re->ComputeSimple(); + re->down_ = stacktop_; + stacktop_ = re; + return true; +} + +// Searches the case folding tables and returns the CaseFold* that contains r. +// If there isn't one, returns the CaseFold* with smallest f->lo bigger than r. +// If there isn't one, returns NULL. +const CaseFold* LookupCaseFold(const CaseFold *f, int n, Rune r) { + const CaseFold* ef = f + n; + + // Binary search for entry containing r. + while (n > 0) { + int m = n/2; + if (f[m].lo <= r && r <= f[m].hi) + return &f[m]; + if (r < f[m].lo) { + n = m; + } else { + f += m+1; + n -= m+1; + } + } + + // There is no entry that contains r, but f points + // where it would have been. Unless f points at + // the end of the array, it points at the next entry + // after r. + if (f < ef) + return f; + + // No entry contains r; no entry contains runes > r. + return NULL; +} + +// Returns the result of applying the fold f to the rune r. +Rune ApplyFold(const CaseFold *f, Rune r) { + switch (f->delta) { + default: + return r + f->delta; + + case EvenOddSkip: // even <-> odd but only applies to every other + if ((r - f->lo) % 2) + return r; + FALLTHROUGH_INTENDED; + case EvenOdd: // even <-> odd + if (r%2 == 0) + return r + 1; + return r - 1; + + case OddEvenSkip: // odd <-> even but only applies to every other + if ((r - f->lo) % 2) + return r; + FALLTHROUGH_INTENDED; + case OddEven: // odd <-> even + if (r%2 == 1) + return r + 1; + return r - 1; + } +} + +// Returns the next Rune in r's folding cycle (see unicode_casefold.h). +// Examples: +// CycleFoldRune('A') = 'a' +// CycleFoldRune('a') = 'A' +// +// CycleFoldRune('K') = 'k' +// CycleFoldRune('k') = 0x212A (Kelvin) +// CycleFoldRune(0x212A) = 'K' +// +// CycleFoldRune('?') = '?' +Rune CycleFoldRune(Rune r) { + const CaseFold* f = LookupCaseFold(unicode_casefold, num_unicode_casefold, r); + if (f == NULL || r < f->lo) + return r; + return ApplyFold(f, r); +} + +// Add lo-hi to the class, along with their fold-equivalent characters. +// If lo-hi is already in the class, assume that the fold-equivalent +// chars are there too, so there's no work to do. +static void AddFoldedRange(CharClassBuilder* cc, Rune lo, Rune hi, int depth) { + // AddFoldedRange calls itself recursively for each rune in the fold cycle. + // Most folding cycles are small: there aren't any bigger than four in the + // current Unicode tables. make_unicode_casefold.py checks that + // the cycles are not too long, and we double-check here using depth. + if (depth > 10) { +#if 0 + LOG(DFATAL) << "AddFoldedRange recurses too much."; +#endif + return; + } + + if (!cc->AddRange(lo, hi)) // lo-hi was already there? we're done + return; + + while (lo <= hi) { + const CaseFold* f = LookupCaseFold(unicode_casefold, num_unicode_casefold, lo); + if (f == NULL) // lo has no fold, nor does anything above lo + break; + if (lo < f->lo) { // lo has no fold; next rune with a fold is f->lo + lo = f->lo; + continue; + } + + // Add in the result of folding the range lo - f->hi + // and that range's fold, recursively. + Rune lo1 = lo; + Rune hi1 = std::min(hi, f->hi); + switch (f->delta) { + default: + lo1 += f->delta; + hi1 += f->delta; + break; + case EvenOdd: + if (lo1%2 == 1) + lo1--; + if (hi1%2 == 0) + hi1++; + break; + case OddEven: + if (lo1%2 == 0) + lo1--; + if (hi1%2 == 1) + hi1++; + break; + } + AddFoldedRange(cc, lo1, hi1, depth+1); + + // Pick up where this fold left off. + lo = f->hi + 1; + } +} + +// Pushes the literal rune r onto the stack. +bool Regexp::ParseState::PushLiteral(Rune r) { + // Do case folding if needed. + if ((flags_ & FoldCase) && CycleFoldRune(r) != r) { + Regexp* re = new Regexp(kRegexpCharClass, flags_ & ~FoldCase); + re->ccb_ = new CharClassBuilder; + Rune r1 = r; + do { + if (!(flags_ & NeverNL) || r != '\n') { + re->ccb_->AddRange(r, r); + } + r = CycleFoldRune(r); + } while (r != r1); + return PushRegexp(re); + } + + // Exclude newline if applicable. + if ((flags_ & NeverNL) && r == '\n') + return PushRegexp(new Regexp(kRegexpNoMatch, flags_)); + + // No fancy stuff worked. Ordinary literal. + if (MaybeConcatString(r, flags_)) + return true; + + Regexp* re = new Regexp(kRegexpLiteral, flags_); + re->rune_ = r; + return PushRegexp(re); +} + +// Pushes a ^ onto the stack. +bool Regexp::ParseState::PushCaret() { + if (flags_ & OneLine) { + return PushSimpleOp(kRegexpBeginText); + } + return PushSimpleOp(kRegexpBeginLine); +} + +// Pushes a \b or \B onto the stack. +bool Regexp::ParseState::PushWordBoundary(bool word) { + if (word) + return PushSimpleOp(kRegexpWordBoundary); + return PushSimpleOp(kRegexpNoWordBoundary); +} + +// Pushes a $ onto the stack. +bool Regexp::ParseState::PushDollar() { + if (flags_ & OneLine) { + // Clumsy marker so that MimicsPCRE() can tell whether + // this kRegexpEndText was a $ and not a \z. + Regexp::ParseFlags oflags = flags_; + flags_ = flags_ | WasDollar; + bool ret = PushSimpleOp(kRegexpEndText); + flags_ = oflags; + return ret; + } + return PushSimpleOp(kRegexpEndLine); +} + +// Pushes a . onto the stack. +bool Regexp::ParseState::PushDot() { + if ((flags_ & DotNL) && !(flags_ & NeverNL)) + return PushSimpleOp(kRegexpAnyChar); + // Rewrite . into [^\n] + Regexp* re = new Regexp(kRegexpCharClass, flags_ & ~FoldCase); + re->ccb_ = new CharClassBuilder; + re->ccb_->AddRange(0, '\n' - 1); + re->ccb_->AddRange('\n' + 1, rune_max_); + return PushRegexp(re); +} + +// Pushes a regexp with the given op (and no args) onto the stack. +bool Regexp::ParseState::PushSimpleOp(RegexpOp op) { + Regexp* re = new Regexp(op, flags_); + return PushRegexp(re); +} + +// Pushes a repeat operator regexp onto the stack. +// A valid argument for the operator must already be on the stack. +// The char c is the name of the operator, for use in error messages. +bool Regexp::ParseState::PushRepeatOp(RegexpOp op, const StringPiece& s, + bool nongreedy) { + if (stacktop_ == NULL || IsMarker(stacktop_->op())) { + status_->set_code(kRegexpRepeatArgument); + status_->set_error_arg(s); + return false; + } + Regexp::ParseFlags fl = flags_; + if (nongreedy) + fl = fl ^ NonGreedy; + + // Squash **, ++ and ??. Regexp::Star() et al. handle this too, but + // they're mostly for use during simplification, not during parsing. + if (op == stacktop_->op() && fl == stacktop_->parse_flags()) + return true; + + // Squash *+, *?, +*, +?, ?* and ?+. They all squash to *, so because + // op is a repeat, we just have to check that stacktop_->op() is too, + // then adjust stacktop_. + if ((stacktop_->op() == kRegexpStar || + stacktop_->op() == kRegexpPlus || + stacktop_->op() == kRegexpQuest) && + fl == stacktop_->parse_flags()) { + stacktop_->op_ = kRegexpStar; + return true; + } + + Regexp* re = new Regexp(op, fl); + re->AllocSub(1); + re->down_ = stacktop_->down_; + re->sub()[0] = FinishRegexp(stacktop_); + re->simple_ = re->ComputeSimple(); + stacktop_ = re; + return true; +} + +// RepetitionWalker reports whether the repetition regexp is valid. +// Valid means that the combination of the top-level repetition +// and any inner repetitions does not exceed n copies of the +// innermost thing. +// This rewalks the regexp tree and is called for every repetition, +// so we have to worry about inducing quadratic behavior in the parser. +// We avoid this by only using RepetitionWalker when min or max >= 2. +// In that case the depth of any >= 2 nesting can only get to 9 without +// triggering a parse error, so each subtree can only be rewalked 9 times. +class RepetitionWalker : public Regexp::Walker { + public: + RepetitionWalker() {} + virtual int PreVisit(Regexp* re, int parent_arg, bool* stop); + virtual int PostVisit(Regexp* re, int parent_arg, int pre_arg, + int* child_args, int nchild_args); + virtual int ShortVisit(Regexp* re, int parent_arg); + + private: + RepetitionWalker(const RepetitionWalker&) = delete; + RepetitionWalker& operator=(const RepetitionWalker&) = delete; +}; + +int RepetitionWalker::PreVisit(Regexp* re, int parent_arg, bool* stop) { + int arg = parent_arg; + if (re->op() == kRegexpRepeat) { + int m = re->max(); + if (m < 0) { + m = re->min(); + } + if (m > 0) { + arg /= m; + } + } + return arg; +} + +int RepetitionWalker::PostVisit(Regexp* re, int parent_arg, int pre_arg, + int* child_args, int nchild_args) { + int arg = pre_arg; + for (int i = 0; i < nchild_args; i++) { + if (child_args[i] < arg) { + arg = child_args[i]; + } + } + return arg; +} + +int RepetitionWalker::ShortVisit(Regexp* re, int parent_arg) { + // Should never be called: we use Walk(), not WalkExponential(). +#if 0 +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + LOG(DFATAL) << "RepetitionWalker::ShortVisit called"; +#endif +#endif + return 0; +} + +// Pushes a repetition regexp onto the stack. +// A valid argument for the operator must already be on the stack. +bool Regexp::ParseState::PushRepetition(int min, int max, + const StringPiece& s, + bool nongreedy) { + if ((max != -1 && max < min) || min > kMaxRepeat || max > kMaxRepeat) { + status_->set_code(kRegexpRepeatSize); + status_->set_error_arg(s); + return false; + } + if (stacktop_ == NULL || IsMarker(stacktop_->op())) { + status_->set_code(kRegexpRepeatArgument); + status_->set_error_arg(s); + return false; + } + Regexp::ParseFlags fl = flags_; + if (nongreedy) + fl = fl ^ NonGreedy; + Regexp* re = new Regexp(kRegexpRepeat, fl); + re->min_ = min; + re->max_ = max; + re->AllocSub(1); + re->down_ = stacktop_->down_; + re->sub()[0] = FinishRegexp(stacktop_); + re->simple_ = re->ComputeSimple(); + stacktop_ = re; + if (min >= 2 || max >= 2) { + RepetitionWalker w; + if (w.Walk(stacktop_, kMaxRepeat) == 0) { + status_->set_code(kRegexpRepeatSize); + status_->set_error_arg(s); + return false; + } + } + return true; +} + +// Checks whether a particular regexp op is a marker. +bool Regexp::ParseState::IsMarker(RegexpOp op) { + return op >= kLeftParen; +} + +// Processes a left parenthesis in the input. +// Pushes a marker onto the stack. +bool Regexp::ParseState::DoLeftParen(const StringPiece& name) { + Regexp* re = new Regexp(kLeftParen, flags_); + re->cap_ = ++ncap_; + if (name.data() != NULL) + re->name_ = new std::string(name); + return PushRegexp(re); +} + +// Pushes a non-capturing marker onto the stack. +bool Regexp::ParseState::DoLeftParenNoCapture() { + Regexp* re = new Regexp(kLeftParen, flags_); + re->cap_ = -1; + return PushRegexp(re); +} + +// Processes a vertical bar in the input. +bool Regexp::ParseState::DoVerticalBar() { + MaybeConcatString(-1, NoParseFlags); + DoConcatenation(); + + // Below the vertical bar is a list to alternate. + // Above the vertical bar is a list to concatenate. + // We just did the concatenation, so either swap + // the result below the vertical bar or push a new + // vertical bar on the stack. + Regexp* r1; + Regexp* r2; + if ((r1 = stacktop_) != NULL && + (r2 = r1->down_) != NULL && + r2->op() == kVerticalBar) { + Regexp* r3; + if ((r3 = r2->down_) != NULL && + (r1->op() == kRegexpAnyChar || r3->op() == kRegexpAnyChar)) { + // AnyChar is above or below the vertical bar. Let it subsume + // the other when the other is Literal, CharClass or AnyChar. + if (r3->op() == kRegexpAnyChar && + (r1->op() == kRegexpLiteral || + r1->op() == kRegexpCharClass || + r1->op() == kRegexpAnyChar)) { + // Discard r1. + stacktop_ = r2; + r1->Decref(); + return true; + } + if (r1->op() == kRegexpAnyChar && + (r3->op() == kRegexpLiteral || + r3->op() == kRegexpCharClass || + r3->op() == kRegexpAnyChar)) { + // Rearrange the stack and discard r3. + r1->down_ = r3->down_; + r2->down_ = r1; + stacktop_ = r2; + r3->Decref(); + return true; + } + } + // Swap r1 below vertical bar (r2). + r1->down_ = r2->down_; + r2->down_ = r1; + stacktop_ = r2; + return true; + } + return PushSimpleOp(kVerticalBar); +} + +// Processes a right parenthesis in the input. +bool Regexp::ParseState::DoRightParen() { + // Finish the current concatenation and alternation. + DoAlternation(); + + // The stack should be: LeftParen regexp + // Remove the LeftParen, leaving the regexp, + // parenthesized. + Regexp* r1; + Regexp* r2; + if ((r1 = stacktop_) == NULL || + (r2 = r1->down_) == NULL || + r2->op() != kLeftParen) { + status_->set_code(kRegexpUnexpectedParen); + status_->set_error_arg(whole_regexp_); + return false; + } + + // Pop off r1, r2. Will Decref or reuse below. + stacktop_ = r2->down_; + + // Restore flags from when paren opened. + Regexp* re = r2; + flags_ = re->parse_flags(); + + // Rewrite LeftParen as capture if needed. + if (re->cap_ > 0) { + re->op_ = kRegexpCapture; + // re->cap_ is already set + re->AllocSub(1); + re->sub()[0] = FinishRegexp(r1); + re->simple_ = re->ComputeSimple(); + } else { + re->Decref(); + re = r1; + } + return PushRegexp(re); +} + +// Processes the end of input, returning the final regexp. +Regexp* Regexp::ParseState::DoFinish() { + DoAlternation(); + Regexp* re = stacktop_; + if (re != NULL && re->down_ != NULL) { + status_->set_code(kRegexpMissingParen); + status_->set_error_arg(whole_regexp_); + return NULL; + } + stacktop_ = NULL; + return FinishRegexp(re); +} + +// Returns the leading regexp that re starts with. +// The returned Regexp* points into a piece of re, +// so it must not be used after the caller calls re->Decref(). +Regexp* Regexp::LeadingRegexp(Regexp* re) { + if (re->op() == kRegexpEmptyMatch) + return NULL; + if (re->op() == kRegexpConcat && re->nsub() >= 2) { + Regexp** sub = re->sub(); + if (sub[0]->op() == kRegexpEmptyMatch) + return NULL; + return sub[0]; + } + return re; +} + +// Removes LeadingRegexp(re) from re and returns what's left. +// Consumes the reference to re and may edit it in place. +// If caller wants to hold on to LeadingRegexp(re), +// must have already Incref'ed it. +Regexp* Regexp::RemoveLeadingRegexp(Regexp* re) { + if (re->op() == kRegexpEmptyMatch) + return re; + if (re->op() == kRegexpConcat && re->nsub() >= 2) { + Regexp** sub = re->sub(); + if (sub[0]->op() == kRegexpEmptyMatch) + return re; + sub[0]->Decref(); + sub[0] = NULL; + if (re->nsub() == 2) { + // Collapse concatenation to single regexp. + Regexp* nre = sub[1]; + sub[1] = NULL; + re->Decref(); + return nre; + } + // 3 or more -> 2 or more. + re->nsub_--; + memmove(sub, sub + 1, re->nsub_ * sizeof sub[0]); + return re; + } + Regexp::ParseFlags pf = re->parse_flags(); + re->Decref(); + return new Regexp(kRegexpEmptyMatch, pf); +} + +// Returns the leading string that re starts with. +// The returned Rune* points into a piece of re, +// so it must not be used after the caller calls re->Decref(). +Rune* Regexp::LeadingString(Regexp* re, int *nrune, + Regexp::ParseFlags *flags) { + while (re->op() == kRegexpConcat && re->nsub() > 0) + re = re->sub()[0]; + + *flags = static_cast(re->parse_flags_ & Regexp::FoldCase); + + if (re->op() == kRegexpLiteral) { + *nrune = 1; + return &re->rune_; + } + + if (re->op() == kRegexpLiteralString) { + *nrune = re->nrunes_; + return re->runes_; + } + + *nrune = 0; + return NULL; +} + +// Removes the first n leading runes from the beginning of re. +// Edits re in place. +void Regexp::RemoveLeadingString(Regexp* re, int n) { + // Chase down concats to find first string. + // For regexps generated by parser, nested concats are + // flattened except when doing so would overflow the 16-bit + // limit on the size of a concatenation, so we should never + // see more than two here. + Regexp* stk[4]; + size_t d = 0; + while (re->op() == kRegexpConcat) { + if (d < arraysize(stk)) + stk[d++] = re; + re = re->sub()[0]; + } + + // Remove leading string from re. + if (re->op() == kRegexpLiteral) { + re->rune_ = 0; + re->op_ = kRegexpEmptyMatch; + } else if (re->op() == kRegexpLiteralString) { + if (n >= re->nrunes_) { + delete[] re->runes_; + re->runes_ = NULL; + re->nrunes_ = 0; + re->op_ = kRegexpEmptyMatch; + } else if (n == re->nrunes_ - 1) { + Rune rune = re->runes_[re->nrunes_ - 1]; + delete[] re->runes_; + re->runes_ = NULL; + re->nrunes_ = 0; + re->rune_ = rune; + re->op_ = kRegexpLiteral; + } else { + re->nrunes_ -= n; + memmove(re->runes_, re->runes_ + n, re->nrunes_ * sizeof re->runes_[0]); + } + } + + // If re is now empty, concatenations might simplify too. + while (d > 0) { + re = stk[--d]; + Regexp** sub = re->sub(); + if (sub[0]->op() == kRegexpEmptyMatch) { + sub[0]->Decref(); + sub[0] = NULL; + // Delete first element of concat. + switch (re->nsub()) { + case 0: + case 1: + // Impossible. +#if 0 + LOG(DFATAL) << "Concat of " << re->nsub(); +#endif + re->submany_ = NULL; + re->op_ = kRegexpEmptyMatch; + break; + + case 2: { + // Replace re with sub[1]. + Regexp* old = sub[1]; + sub[1] = NULL; + re->Swap(old); + old->Decref(); + break; + } + + default: + // Slide down. + re->nsub_--; + memmove(sub, sub + 1, re->nsub_ * sizeof sub[0]); + break; + } + } + } +} + +// In the context of factoring alternations, a Splice is: a factored prefix or +// merged character class computed by one iteration of one round of factoring; +// the span of subexpressions of the alternation to be "spliced" (i.e. removed +// and replaced); and, for a factored prefix, the number of suffixes after any +// factoring that might have subsequently been performed on them. For a merged +// character class, there are no suffixes, of course, so the field is ignored. +struct Splice { + Splice(Regexp* prefix, Regexp** sub, int nsub) + : prefix(prefix), + sub(sub), + nsub(nsub), + nsuffix(-1) {} + + Regexp* prefix; + Regexp** sub; + int nsub; + int nsuffix; +}; + +// Named so because it is used to implement an explicit stack, a Frame is: the +// span of subexpressions of the alternation to be factored; the current round +// of factoring; any Splices computed; and, for a factored prefix, an iterator +// to the next Splice to be factored (i.e. in another Frame) because suffixes. +struct Frame { + Frame(Regexp** sub, int nsub) + : sub(sub), + nsub(nsub), + round(0) {} + + Regexp** sub; + int nsub; + int round; + std::vector splices; + int spliceidx; +}; + +// Bundled into a class for friend access to Regexp without needing to declare +// (or define) Splice in regexp.h. +class FactorAlternationImpl { + public: + static void Round1(Regexp** sub, int nsub, + Regexp::ParseFlags flags, + std::vector* splices); + static void Round2(Regexp** sub, int nsub, + Regexp::ParseFlags flags, + std::vector* splices); + static void Round3(Regexp** sub, int nsub, + Regexp::ParseFlags flags, + std::vector* splices); +}; + +// Factors common prefixes from alternation. +// For example, +// ABC|ABD|AEF|BCX|BCY +// simplifies to +// A(B(C|D)|EF)|BC(X|Y) +// and thence to +// A(B[CD]|EF)|BC[XY] +// +// Rewrites sub to contain simplified list to alternate and returns +// the new length of sub. Adjusts reference counts accordingly +// (incoming sub[i] decremented, outgoing sub[i] incremented). +int Regexp::FactorAlternation(Regexp** sub, int nsub, ParseFlags flags) { + std::vector stk; + stk.emplace_back(sub, nsub); + + for (;;) { + auto& sub = stk.back().sub; + auto& nsub = stk.back().nsub; + auto& round = stk.back().round; + auto& splices = stk.back().splices; + auto& spliceidx = stk.back().spliceidx; + + if (splices.empty()) { + // Advance to the next round of factoring. Note that this covers + // the initialised state: when splices is empty and round is 0. + round++; + } else if (spliceidx < static_cast(splices.size())) { + // We have at least one more Splice to factor. Recurse logically. + stk.emplace_back(splices[spliceidx].sub, splices[spliceidx].nsub); + continue; + } else { + // We have no more Splices to factor. Apply them. + auto iter = splices.begin(); + int out = 0; + for (int i = 0; i < nsub; ) { + // Copy until we reach where the next Splice begins. + while (sub + i < iter->sub) + sub[out++] = sub[i++]; + switch (round) { + case 1: + case 2: { + // Assemble the Splice prefix and the suffixes. + Regexp* re[2]; + re[0] = iter->prefix; + re[1] = Regexp::AlternateNoFactor(iter->sub, iter->nsuffix, flags); + sub[out++] = Regexp::Concat(re, 2, flags); + i += iter->nsub; + break; + } + case 3: + // Just use the Splice prefix. + sub[out++] = iter->prefix; + i += iter->nsub; + break; + default: +#if 0 + LOG(DFATAL) << "unknown round: " << round; +#endif + break; + } + // If we are done, copy until the end of sub. + if (++iter == splices.end()) { + while (i < nsub) + sub[out++] = sub[i++]; + } + } + splices.clear(); + nsub = out; + // Advance to the next round of factoring. + round++; + } + + switch (round) { + case 1: + FactorAlternationImpl::Round1(sub, nsub, flags, &splices); + break; + case 2: + FactorAlternationImpl::Round2(sub, nsub, flags, &splices); + break; + case 3: + FactorAlternationImpl::Round3(sub, nsub, flags, &splices); + break; + case 4: + if (stk.size() == 1) { + // We are at the top of the stack. Just return. + return nsub; + } else { + // Pop the stack and set the number of suffixes. + // (Note that references will be invalidated!) + int nsuffix = nsub; + stk.pop_back(); + stk.back().splices[stk.back().spliceidx].nsuffix = nsuffix; + ++stk.back().spliceidx; + continue; + } + default: +#if 0 + LOG(DFATAL) << "unknown round: " << round; +#endif + break; + } + + // Set spliceidx depending on whether we have Splices to factor. + if (splices.empty() || round == 3) { + spliceidx = static_cast(splices.size()); + } else { + spliceidx = 0; + } + } +} + +void FactorAlternationImpl::Round1(Regexp** sub, int nsub, + Regexp::ParseFlags flags, + std::vector* splices) { + // Round 1: Factor out common literal prefixes. + int start = 0; + Rune* rune = NULL; + int nrune = 0; + Regexp::ParseFlags runeflags = Regexp::NoParseFlags; + for (int i = 0; i <= nsub; i++) { + // Invariant: sub[start:i] consists of regexps that all + // begin with rune[0:nrune]. + Rune* rune_i = NULL; + int nrune_i = 0; + Regexp::ParseFlags runeflags_i = Regexp::NoParseFlags; + if (i < nsub) { + rune_i = Regexp::LeadingString(sub[i], &nrune_i, &runeflags_i); + if (runeflags_i == runeflags) { + int same = 0; + while (same < nrune && same < nrune_i && rune[same] == rune_i[same]) + same++; + if (same > 0) { + // Matches at least one rune in current range. Keep going around. + nrune = same; + continue; + } + } + } + + // Found end of a run with common leading literal string: + // sub[start:i] all begin with rune[0:nrune], + // but sub[i] does not even begin with rune[0]. + if (i == start) { + // Nothing to do - first iteration. + } else if (i == start+1) { + // Just one: don't bother factoring. + } else { + Regexp* prefix = Regexp::LiteralString(rune, nrune, runeflags); + for (int j = start; j < i; j++) + Regexp::RemoveLeadingString(sub[j], nrune); + splices->emplace_back(prefix, sub + start, i - start); + } + + // Prepare for next iteration (if there is one). + if (i < nsub) { + start = i; + rune = rune_i; + nrune = nrune_i; + runeflags = runeflags_i; + } + } +} + +void FactorAlternationImpl::Round2(Regexp** sub, int nsub, + Regexp::ParseFlags flags, + std::vector* splices) { + // Round 2: Factor out common simple prefixes, + // just the first piece of each concatenation. + // This will be good enough a lot of the time. + // + // Complex subexpressions (e.g. involving quantifiers) + // are not safe to factor because that collapses their + // distinct paths through the automaton, which affects + // correctness in some cases. + int start = 0; + Regexp* first = NULL; + for (int i = 0; i <= nsub; i++) { + // Invariant: sub[start:i] consists of regexps that all + // begin with first. + Regexp* first_i = NULL; + if (i < nsub) { + first_i = Regexp::LeadingRegexp(sub[i]); + if (first != NULL && + // first must be an empty-width op + // OR a char class, any char or any byte + // OR a fixed repeat of a literal, char class, any char or any byte. + (first->op() == kRegexpBeginLine || + first->op() == kRegexpEndLine || + first->op() == kRegexpWordBoundary || + first->op() == kRegexpNoWordBoundary || + first->op() == kRegexpBeginText || + first->op() == kRegexpEndText || + first->op() == kRegexpCharClass || + first->op() == kRegexpAnyChar || + first->op() == kRegexpAnyByte || + (first->op() == kRegexpRepeat && + first->min() == first->max() && + (first->sub()[0]->op() == kRegexpLiteral || + first->sub()[0]->op() == kRegexpCharClass || + first->sub()[0]->op() == kRegexpAnyChar || + first->sub()[0]->op() == kRegexpAnyByte))) && + Regexp::Equal(first, first_i)) + continue; + } + + // Found end of a run with common leading regexp: + // sub[start:i] all begin with first, + // but sub[i] does not. + if (i == start) { + // Nothing to do - first iteration. + } else if (i == start+1) { + // Just one: don't bother factoring. + } else { + Regexp* prefix = first->Incref(); + for (int j = start; j < i; j++) + sub[j] = Regexp::RemoveLeadingRegexp(sub[j]); + splices->emplace_back(prefix, sub + start, i - start); + } + + // Prepare for next iteration (if there is one). + if (i < nsub) { + start = i; + first = first_i; + } + } +} + +void FactorAlternationImpl::Round3(Regexp** sub, int nsub, + Regexp::ParseFlags flags, + std::vector* splices) { + // Round 3: Merge runs of literals and/or character classes. + int start = 0; + Regexp* first = NULL; + for (int i = 0; i <= nsub; i++) { + // Invariant: sub[start:i] consists of regexps that all + // are either literals (i.e. runes) or character classes. + Regexp* first_i = NULL; + if (i < nsub) { + first_i = sub[i]; + if (first != NULL && + (first->op() == kRegexpLiteral || + first->op() == kRegexpCharClass) && + (first_i->op() == kRegexpLiteral || + first_i->op() == kRegexpCharClass)) + continue; + } + + // Found end of a run of Literal/CharClass: + // sub[start:i] all are either one or the other, + // but sub[i] is not. + if (i == start) { + // Nothing to do - first iteration. + } else if (i == start+1) { + // Just one: don't bother factoring. + } else { + CharClassBuilder ccb; + for (int j = start; j < i; j++) { + Regexp* re = sub[j]; + if (re->op() == kRegexpCharClass) { + CharClass* cc = re->cc(); + for (CharClass::iterator it = cc->begin(); it != cc->end(); ++it) + ccb.AddRange(it->lo, it->hi); + } else if (re->op() == kRegexpLiteral) { + ccb.AddRangeFlags(re->rune(), re->rune(), re->parse_flags()); + } else { +#if 0 + LOG(DFATAL) << "RE2: unexpected op: " << re->op() << " " + << re->ToString(); +#endif + } + re->Decref(); + } + Regexp* re = Regexp::NewCharClass(ccb.GetCharClass(), flags); + splices->emplace_back(re, sub + start, i - start); + } + + // Prepare for next iteration (if there is one). + if (i < nsub) { + start = i; + first = first_i; + } + } +} + +// Collapse the regexps on top of the stack, down to the +// first marker, into a new op node (op == kRegexpAlternate +// or op == kRegexpConcat). +void Regexp::ParseState::DoCollapse(RegexpOp op) { + // Scan backward to marker, counting children of composite. + int n = 0; + Regexp* next = NULL; + Regexp* sub; + for (sub = stacktop_; sub != NULL && !IsMarker(sub->op()); sub = next) { + next = sub->down_; + if (sub->op_ == op) + n += sub->nsub_; + else + n++; + } + + // If there's just one child, leave it alone. + // (Concat of one thing is that one thing; alternate of one thing is same.) + if (stacktop_ != NULL && stacktop_->down_ == next) + return; + + // Construct op (alternation or concatenation), flattening op of op. + PODArray subs(n); + next = NULL; + int i = n; + for (sub = stacktop_; sub != NULL && !IsMarker(sub->op()); sub = next) { + next = sub->down_; + if (sub->op_ == op) { + Regexp** sub_subs = sub->sub(); + for (int k = sub->nsub_ - 1; k >= 0; k--) + subs[--i] = sub_subs[k]->Incref(); + sub->Decref(); + } else { + subs[--i] = FinishRegexp(sub); + } + } + + Regexp* re = ConcatOrAlternate(op, subs.data(), n, flags_, true); + re->simple_ = re->ComputeSimple(); + re->down_ = next; + stacktop_ = re; +} + +// Finishes the current concatenation, +// collapsing it into a single regexp on the stack. +void Regexp::ParseState::DoConcatenation() { + Regexp* r1 = stacktop_; + if (r1 == NULL || IsMarker(r1->op())) { + // empty concatenation is special case + Regexp* re = new Regexp(kRegexpEmptyMatch, flags_); + PushRegexp(re); + } + DoCollapse(kRegexpConcat); +} + +// Finishes the current alternation, +// collapsing it to a single regexp on the stack. +void Regexp::ParseState::DoAlternation() { + DoVerticalBar(); + // Now stack top is kVerticalBar. + Regexp* r1 = stacktop_; + stacktop_ = r1->down_; + r1->Decref(); + DoCollapse(kRegexpAlternate); +} + +// Incremental conversion of concatenated literals into strings. +// If top two elements on stack are both literal or string, +// collapse into single string. +// Don't walk down the stack -- the parser calls this frequently +// enough that below the bottom two is known to be collapsed. +// Only called when another regexp is about to be pushed +// on the stack, so that the topmost literal is not being considered. +// (Otherwise ab* would turn into (ab)*.) +// If r >= 0, consider pushing a literal r on the stack. +// Return whether that happened. +bool Regexp::ParseState::MaybeConcatString(int r, ParseFlags flags) { + Regexp* re1; + Regexp* re2; + if ((re1 = stacktop_) == NULL || (re2 = re1->down_) == NULL) + return false; + + if (re1->op_ != kRegexpLiteral && re1->op_ != kRegexpLiteralString) + return false; + if (re2->op_ != kRegexpLiteral && re2->op_ != kRegexpLiteralString) + return false; + if ((re1->parse_flags_ & FoldCase) != (re2->parse_flags_ & FoldCase)) + return false; + + if (re2->op_ == kRegexpLiteral) { + // convert into string + Rune rune = re2->rune_; + re2->op_ = kRegexpLiteralString; + re2->nrunes_ = 0; + re2->runes_ = NULL; + re2->AddRuneToString(rune); + } + + // push re1 into re2. + if (re1->op_ == kRegexpLiteral) { + re2->AddRuneToString(re1->rune_); + } else { + for (int i = 0; i < re1->nrunes_; i++) + re2->AddRuneToString(re1->runes_[i]); + re1->nrunes_ = 0; + delete[] re1->runes_; + re1->runes_ = NULL; + } + + // reuse re1 if possible + if (r >= 0) { + re1->op_ = kRegexpLiteral; + re1->rune_ = r; + re1->parse_flags_ = static_cast(flags); + return true; + } + + stacktop_ = re2; + re1->Decref(); + return false; +} + +// Lexing routines. + +// Parses a decimal integer, storing it in *np. +// Sets *s to span the remainder of the string. +static bool ParseInteger(StringPiece* s, int* np) { + if (s->empty() || !isdigit((*s)[0] & 0xFF)) + return false; + // Disallow leading zeros. + if (s->size() >= 2 && (*s)[0] == '0' && isdigit((*s)[1] & 0xFF)) + return false; + int n = 0; + int c; + while (!s->empty() && isdigit(c = (*s)[0] & 0xFF)) { + // Avoid overflow. + if (n >= 100000000) + return false; + n = n*10 + c - '0'; + s->remove_prefix(1); // digit + } + *np = n; + return true; +} + +// Parses a repetition suffix like {1,2} or {2} or {2,}. +// Sets *s to span the remainder of the string on success. +// Sets *lo and *hi to the given range. +// In the case of {2,}, the high number is unbounded; +// sets *hi to -1 to signify this. +// {,2} is NOT a valid suffix. +// The Maybe in the name signifies that the regexp parse +// doesn't fail even if ParseRepetition does, so the StringPiece +// s must NOT be edited unless MaybeParseRepetition returns true. +static bool MaybeParseRepetition(StringPiece* sp, int* lo, int* hi) { + StringPiece s = *sp; + if (s.empty() || s[0] != '{') + return false; + s.remove_prefix(1); // '{' + if (!ParseInteger(&s, lo)) + return false; + if (s.empty()) + return false; + if (s[0] == ',') { + s.remove_prefix(1); // ',' + if (s.empty()) + return false; + if (s[0] == '}') { + // {2,} means at least 2 + *hi = -1; + } else { + // {2,4} means 2, 3, or 4. + if (!ParseInteger(&s, hi)) + return false; + } + } else { + // {2} means exactly two + *hi = *lo; + } + if (s.empty() || s[0] != '}') + return false; + s.remove_prefix(1); // '}' + *sp = s; + return true; +} + +// Removes the next Rune from the StringPiece and stores it in *r. +// Returns number of bytes removed from sp. +// Behaves as though there is a terminating NUL at the end of sp. +// Argument order is backwards from usual Google style +// but consistent with chartorune. +static int StringPieceToRune(Rune *r, StringPiece *sp, RegexpStatus* status) { + // fullrune() takes int, not size_t. However, it just looks + // at the leading byte and treats any length >= 4 the same. + if (fullrune(sp->data(), static_cast(std::min(size_t{4}, sp->size())))) { + int n = chartorune(r, sp->data()); + // Some copies of chartorune have a bug that accepts + // encodings of values in (10FFFF, 1FFFFF] as valid. + // Those values break the character class algorithm, + // which assumes Runemax is the largest rune. + if (*r > Runemax) { + n = 1; + *r = Runeerror; + } + if (!(n == 1 && *r == Runeerror)) { // no decoding error + sp->remove_prefix(n); + return n; + } + } + + status->set_code(kRegexpBadUTF8); + status->set_error_arg(StringPiece()); + return -1; +} + +// Return whether name is valid UTF-8. +// If not, set status to kRegexpBadUTF8. +static bool IsValidUTF8(const StringPiece& s, RegexpStatus* status) { + StringPiece t = s; + Rune r; + while (!t.empty()) { + if (StringPieceToRune(&r, &t, status) < 0) + return false; + } + return true; +} + +// Is c a hex digit? +static int IsHex(int c) { + return ('0' <= c && c <= '9') || + ('A' <= c && c <= 'F') || + ('a' <= c && c <= 'f'); +} + +// Convert hex digit to value. +static int UnHex(int c) { + if ('0' <= c && c <= '9') + return c - '0'; + if ('A' <= c && c <= 'F') + return c - 'A' + 10; + if ('a' <= c && c <= 'f') + return c - 'a' + 10; +#if 0 + LOG(DFATAL) << "Bad hex digit " << c; +#endif + return 0; +} + +// Parse an escape sequence (e.g., \n, \{). +// Sets *s to span the remainder of the string. +// Sets *rp to the named character. +static bool ParseEscape(StringPiece* s, Rune* rp, + RegexpStatus* status, int rune_max) { + const char* begin = s->data(); + if (s->empty() || (*s)[0] != '\\') { + // Should not happen - caller always checks. + status->set_code(kRegexpInternalError); + status->set_error_arg(StringPiece()); + return false; + } + if (s->size() == 1) { + status->set_code(kRegexpTrailingBackslash); + status->set_error_arg(StringPiece()); + return false; + } + Rune c, c1; + s->remove_prefix(1); // backslash + if (StringPieceToRune(&c, s, status) < 0) + return false; + int code; + switch (c) { + default: + if (c < Runeself && !isalpha(c) && !isdigit(c)) { + // Escaped non-word characters are always themselves. + // PCRE is not quite so rigorous: it accepts things like + // \q, but we don't. We once rejected \_, but too many + // programs and people insist on using it, so allow \_. + *rp = c; + return true; + } + goto BadEscape; + + // Octal escapes. + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + // Single non-zero octal digit is a backreference; not supported. + if (s->empty() || (*s)[0] < '0' || (*s)[0] > '7') + goto BadEscape; + FALLTHROUGH_INTENDED; + case '0': + // consume up to three octal digits; already have one. + code = c - '0'; + if (!s->empty() && '0' <= (c = (*s)[0]) && c <= '7') { + code = code * 8 + c - '0'; + s->remove_prefix(1); // digit + if (!s->empty()) { + c = (*s)[0]; + if ('0' <= c && c <= '7') { + code = code * 8 + c - '0'; + s->remove_prefix(1); // digit + } + } + } + if (code > rune_max) + goto BadEscape; + *rp = code; + return true; + + // Hexadecimal escapes + case 'x': + if (s->empty()) + goto BadEscape; + if (StringPieceToRune(&c, s, status) < 0) + return false; + if (c == '{') { + // Any number of digits in braces. + // Update n as we consume the string, so that + // the whole thing gets shown in the error message. + // Perl accepts any text at all; it ignores all text + // after the first non-hex digit. We require only hex digits, + // and at least one. + if (StringPieceToRune(&c, s, status) < 0) + return false; + int nhex = 0; + code = 0; + while (IsHex(c)) { + nhex++; + code = code * 16 + UnHex(c); + if (code > rune_max) + goto BadEscape; + if (s->empty()) + goto BadEscape; + if (StringPieceToRune(&c, s, status) < 0) + return false; + } + if (c != '}' || nhex == 0) + goto BadEscape; + *rp = code; + return true; + } + // Easy case: two hex digits. + if (s->empty()) + goto BadEscape; + if (StringPieceToRune(&c1, s, status) < 0) + return false; + if (!IsHex(c) || !IsHex(c1)) + goto BadEscape; + *rp = UnHex(c) * 16 + UnHex(c1); + return true; + + // C escapes. + case 'n': + *rp = '\n'; + return true; + case 'r': + *rp = '\r'; + return true; + case 't': + *rp = '\t'; + return true; + + // Less common C escapes. + case 'a': + *rp = '\a'; + return true; + case 'f': + *rp = '\f'; + return true; + case 'v': + *rp = '\v'; + return true; + + // This code is disabled to avoid misparsing + // the Perl word-boundary \b as a backspace + // when in POSIX regexp mode. Surprisingly, + // in Perl, \b means word-boundary but [\b] + // means backspace. We don't support that: + // if you want a backspace embed a literal + // backspace character or use \x08. + // + // case 'b': + // *rp = '\b'; + // return true; + } + +#if 0 + LOG(DFATAL) << "Not reached in ParseEscape."; +#endif + +BadEscape: + // Unrecognized escape sequence. + status->set_code(kRegexpBadEscape); + status->set_error_arg( + StringPiece(begin, static_cast(s->data() - begin))); + return false; +} + +// Add a range to the character class, but exclude newline if asked. +// Also handle case folding. +void CharClassBuilder::AddRangeFlags( + Rune lo, Rune hi, Regexp::ParseFlags parse_flags) { + + // Take out \n if the flags say so. + bool cutnl = !(parse_flags & Regexp::ClassNL) || + (parse_flags & Regexp::NeverNL); + if (cutnl && lo <= '\n' && '\n' <= hi) { + if (lo < '\n') + AddRangeFlags(lo, '\n' - 1, parse_flags); + if (hi > '\n') + AddRangeFlags('\n' + 1, hi, parse_flags); + return; + } + + // If folding case, add fold-equivalent characters too. + if (parse_flags & Regexp::FoldCase) + AddFoldedRange(this, lo, hi, 0); + else + AddRange(lo, hi); +} + +// Look for a group with the given name. +static const UGroup* LookupGroup(const StringPiece& name, + const UGroup *groups, int ngroups) { + // Simple name lookup. + for (int i = 0; i < ngroups; i++) + if (StringPiece(groups[i].name) == name) + return &groups[i]; + return NULL; +} + +// Look for a POSIX group with the given name (e.g., "[:^alpha:]") +static const UGroup* LookupPosixGroup(const StringPiece& name) { + return LookupGroup(name, posix_groups, num_posix_groups); +} + +static const UGroup* LookupPerlGroup(const StringPiece& name) { + return LookupGroup(name, perl_groups, num_perl_groups); +} + +#if !defined(RE2_USE_ICU) +// Fake UGroup containing all Runes +static URange16 any16[] = { { 0, 65535 } }; +static URange32 any32[] = { { 65536, Runemax } }; +static UGroup anygroup = { "Any", +1, any16, 1, any32, 1 }; + +// Look for a Unicode group with the given name (e.g., "Han") +static const UGroup* LookupUnicodeGroup(const StringPiece& name) { + // Special case: "Any" means any. + if (name == StringPiece("Any")) + return &anygroup; + return LookupGroup(name, unicode_groups, num_unicode_groups); +} +#endif + +// Add a UGroup or its negation to the character class. +static void AddUGroup(CharClassBuilder *cc, const UGroup *g, int sign, + Regexp::ParseFlags parse_flags) { + if (sign == +1) { + for (int i = 0; i < g->nr16; i++) { + cc->AddRangeFlags(g->r16[i].lo, g->r16[i].hi, parse_flags); + } + for (int i = 0; i < g->nr32; i++) { + cc->AddRangeFlags(g->r32[i].lo, g->r32[i].hi, parse_flags); + } + } else { + if (parse_flags & Regexp::FoldCase) { + // Normally adding a case-folded group means + // adding all the extra fold-equivalent runes too. + // But if we're adding the negation of the group, + // we have to exclude all the runes that are fold-equivalent + // to what's already missing. Too hard, so do in two steps. + CharClassBuilder ccb1; + AddUGroup(&ccb1, g, +1, parse_flags); + // If the flags say to take out \n, put it in, so that negating will take it out. + // Normally AddRangeFlags does this, but we're bypassing AddRangeFlags. + bool cutnl = !(parse_flags & Regexp::ClassNL) || + (parse_flags & Regexp::NeverNL); + if (cutnl) { + ccb1.AddRange('\n', '\n'); + } + ccb1.Negate(); + cc->AddCharClass(&ccb1); + return; + } + int next = 0; + for (int i = 0; i < g->nr16; i++) { + if (next < g->r16[i].lo) + cc->AddRangeFlags(next, g->r16[i].lo - 1, parse_flags); + next = g->r16[i].hi + 1; + } + for (int i = 0; i < g->nr32; i++) { + if (next < g->r32[i].lo) + cc->AddRangeFlags(next, g->r32[i].lo - 1, parse_flags); + next = g->r32[i].hi + 1; + } + if (next <= Runemax) + cc->AddRangeFlags(next, Runemax, parse_flags); + } +} + +// Maybe parse a Perl character class escape sequence. +// Only recognizes the Perl character classes (\d \s \w \D \S \W), +// not the Perl empty-string classes (\b \B \A \Z \z). +// On success, sets *s to span the remainder of the string +// and returns the corresponding UGroup. +// The StringPiece must *NOT* be edited unless the call succeeds. +const UGroup* MaybeParsePerlCCEscape(StringPiece* s, Regexp::ParseFlags parse_flags) { + if (!(parse_flags & Regexp::PerlClasses)) + return NULL; + if (s->size() < 2 || (*s)[0] != '\\') + return NULL; + // Could use StringPieceToRune, but there aren't + // any non-ASCII Perl group names. + StringPiece name(s->data(), 2); + const UGroup *g = LookupPerlGroup(name); + if (g == NULL) + return NULL; + s->remove_prefix(name.size()); + return g; +} + +enum ParseStatus { + kParseOk, // Did some parsing. + kParseError, // Found an error. + kParseNothing, // Decided not to parse. +}; + +// Maybe parses a Unicode character group like \p{Han} or \P{Han} +// (the latter is a negated group). +ParseStatus ParseUnicodeGroup(StringPiece* s, Regexp::ParseFlags parse_flags, + CharClassBuilder *cc, + RegexpStatus* status) { + // Decide whether to parse. + if (!(parse_flags & Regexp::UnicodeGroups)) + return kParseNothing; + if (s->size() < 2 || (*s)[0] != '\\') + return kParseNothing; + Rune c = (*s)[1]; + if (c != 'p' && c != 'P') + return kParseNothing; + + // Committed to parse. Results: + int sign = +1; // -1 = negated char class + if (c == 'P') + sign = -sign; + StringPiece seq = *s; // \p{Han} or \pL + StringPiece name; // Han or L + s->remove_prefix(2); // '\\', 'p' + + if (!StringPieceToRune(&c, s, status)) + return kParseError; + if (c != '{') { + // Name is the bit of string we just skipped over for c. + const char* p = seq.data() + 2; + name = StringPiece(p, static_cast(s->data() - p)); + } else { + // Name is in braces. Look for closing } + size_t end = s->find('}', 0); + if (end == StringPiece::npos) { + if (!IsValidUTF8(seq, status)) + return kParseError; + status->set_code(kRegexpBadCharRange); + status->set_error_arg(seq); + return kParseError; + } + name = StringPiece(s->data(), end); // without '}' + s->remove_prefix(end + 1); // with '}' + if (!IsValidUTF8(name, status)) + return kParseError; + } + + // Chop seq where s now begins. + seq = StringPiece(seq.data(), static_cast(s->data() - seq.data())); + + if (!name.empty() && name[0] == '^') { + sign = -sign; + name.remove_prefix(1); // '^' + } + +#if !defined(RE2_USE_ICU) + // Look up the group in the RE2 Unicode data. + const UGroup *g = LookupUnicodeGroup(name); + if (g == NULL) { + status->set_code(kRegexpBadCharRange); + status->set_error_arg(seq); + return kParseError; + } + + AddUGroup(cc, g, sign, parse_flags); +#else + // Look up the group in the ICU Unicode data. Because ICU provides full + // Unicode properties support, this could be more than a lookup by name. + ::icu::UnicodeString ustr = ::icu::UnicodeString::fromUTF8( + std::string("\\p{") + std::string(name) + std::string("}")); + UErrorCode uerr = U_ZERO_ERROR; + ::icu::UnicodeSet uset(ustr, uerr); + if (U_FAILURE(uerr)) { + status->set_code(kRegexpBadCharRange); + status->set_error_arg(seq); + return kParseError; + } + + // Convert the UnicodeSet to a URange32 and UGroup that we can add. + int nr = uset.getRangeCount(); + PODArray r(nr); + for (int i = 0; i < nr; i++) { + r[i].lo = uset.getRangeStart(i); + r[i].hi = uset.getRangeEnd(i); + } + UGroup g = {"", +1, 0, 0, r.data(), nr}; + AddUGroup(cc, &g, sign, parse_flags); +#endif + + return kParseOk; +} + +// Parses a character class name like [:alnum:]. +// Sets *s to span the remainder of the string. +// Adds the ranges corresponding to the class to ranges. +static ParseStatus ParseCCName(StringPiece* s, Regexp::ParseFlags parse_flags, + CharClassBuilder *cc, + RegexpStatus* status) { + // Check begins with [: + const char* p = s->data(); + const char* ep = s->data() + s->size(); + if (ep - p < 2 || p[0] != '[' || p[1] != ':') + return kParseNothing; + + // Look for closing :]. + const char* q; + for (q = p+2; q <= ep-2 && (*q != ':' || *(q+1) != ']'); q++) + ; + + // If no closing :], then ignore. + if (q > ep-2) + return kParseNothing; + + // Got it. Check that it's valid. + q += 2; + StringPiece name(p, static_cast(q - p)); + + const UGroup *g = LookupPosixGroup(name); + if (g == NULL) { + status->set_code(kRegexpBadCharRange); + status->set_error_arg(name); + return kParseError; + } + + s->remove_prefix(name.size()); + AddUGroup(cc, g, g->sign, parse_flags); + return kParseOk; +} + +// Parses a character inside a character class. +// There are fewer special characters here than in the rest of the regexp. +// Sets *s to span the remainder of the string. +// Sets *rp to the character. +bool Regexp::ParseState::ParseCCCharacter(StringPiece* s, Rune *rp, + const StringPiece& whole_class, + RegexpStatus* status) { + if (s->empty()) { + status->set_code(kRegexpMissingBracket); + status->set_error_arg(whole_class); + return false; + } + + // Allow regular escape sequences even though + // many need not be escaped in this context. + if ((*s)[0] == '\\') + return ParseEscape(s, rp, status, rune_max_); + + // Otherwise take the next rune. + return StringPieceToRune(rp, s, status) >= 0; +} + +// Parses a character class character, or, if the character +// is followed by a hyphen, parses a character class range. +// For single characters, rr->lo == rr->hi. +// Sets *s to span the remainder of the string. +// Sets *rp to the character. +bool Regexp::ParseState::ParseCCRange(StringPiece* s, RuneRange* rr, + const StringPiece& whole_class, + RegexpStatus* status) { + StringPiece os = *s; + if (!ParseCCCharacter(s, &rr->lo, whole_class, status)) + return false; + // [a-] means (a|-), so check for final ]. + if (s->size() >= 2 && (*s)[0] == '-' && (*s)[1] != ']') { + s->remove_prefix(1); // '-' + if (!ParseCCCharacter(s, &rr->hi, whole_class, status)) + return false; + if (rr->hi < rr->lo) { + status->set_code(kRegexpBadCharRange); + status->set_error_arg( + StringPiece(os.data(), static_cast(s->data() - os.data()))); + return false; + } + } else { + rr->hi = rr->lo; + } + return true; +} + +// Parses a possibly-negated character class expression like [^abx-z[:digit:]]. +// Sets *s to span the remainder of the string. +// Sets *out_re to the regexp for the class. +bool Regexp::ParseState::ParseCharClass(StringPiece* s, + Regexp** out_re, + RegexpStatus* status) { + StringPiece whole_class = *s; + if (s->empty() || (*s)[0] != '[') { + // Caller checked this. + status->set_code(kRegexpInternalError); + status->set_error_arg(StringPiece()); + return false; + } + bool negated = false; + Regexp* re = new Regexp(kRegexpCharClass, flags_ & ~FoldCase); + re->ccb_ = new CharClassBuilder; + s->remove_prefix(1); // '[' + if (!s->empty() && (*s)[0] == '^') { + s->remove_prefix(1); // '^' + negated = true; + if (!(flags_ & ClassNL) || (flags_ & NeverNL)) { + // If NL can't match implicitly, then pretend + // negated classes include a leading \n. + re->ccb_->AddRange('\n', '\n'); + } + } + bool first = true; // ] is okay as first char in class + while (!s->empty() && ((*s)[0] != ']' || first)) { + // - is only okay unescaped as first or last in class. + // Except that Perl allows - anywhere. + if ((*s)[0] == '-' && !first && !(flags_&PerlX) && + (s->size() == 1 || (*s)[1] != ']')) { + StringPiece t = *s; + t.remove_prefix(1); // '-' + Rune r; + int n = StringPieceToRune(&r, &t, status); + if (n < 0) { + re->Decref(); + return false; + } + status->set_code(kRegexpBadCharRange); + status->set_error_arg(StringPiece(s->data(), 1+n)); + re->Decref(); + return false; + } + first = false; + + // Look for [:alnum:] etc. + if (s->size() > 2 && (*s)[0] == '[' && (*s)[1] == ':') { + switch (ParseCCName(s, flags_, re->ccb_, status)) { + case kParseOk: + continue; + case kParseError: + re->Decref(); + return false; + case kParseNothing: + break; + } + } + + // Look for Unicode character group like \p{Han} + if (s->size() > 2 && + (*s)[0] == '\\' && + ((*s)[1] == 'p' || (*s)[1] == 'P')) { + switch (ParseUnicodeGroup(s, flags_, re->ccb_, status)) { + case kParseOk: + continue; + case kParseError: + re->Decref(); + return false; + case kParseNothing: + break; + } + } + + // Look for Perl character class symbols (extension). + const UGroup *g = MaybeParsePerlCCEscape(s, flags_); + if (g != NULL) { + AddUGroup(re->ccb_, g, g->sign, flags_); + continue; + } + + // Otherwise assume single character or simple range. + RuneRange rr; + if (!ParseCCRange(s, &rr, whole_class, status)) { + re->Decref(); + return false; + } + // AddRangeFlags is usually called in response to a class like + // \p{Foo} or [[:foo:]]; for those, it filters \n out unless + // Regexp::ClassNL is set. In an explicit range or singleton + // like we just parsed, we do not filter \n out, so set ClassNL + // in the flags. + re->ccb_->AddRangeFlags(rr.lo, rr.hi, flags_ | Regexp::ClassNL); + } + if (s->empty()) { + status->set_code(kRegexpMissingBracket); + status->set_error_arg(whole_class); + re->Decref(); + return false; + } + s->remove_prefix(1); // ']' + + if (negated) + re->ccb_->Negate(); + + *out_re = re; + return true; +} + +// Is this a valid capture name? [A-Za-z0-9_]+ +// PCRE limits names to 32 bytes. +// Python rejects names starting with digits. +// We don't enforce either of those. +static bool IsValidCaptureName(const StringPiece& name) { + if (name.empty()) + return false; + for (size_t i = 0; i < name.size(); i++) { + int c = name[i]; + if (('0' <= c && c <= '9') || + ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + c == '_') + continue; + return false; + } + return true; +} + +// Parses a Perl flag setting or non-capturing group or both, +// like (?i) or (?: or (?i:. Removes from s, updates parse state. +// The caller must check that s begins with "(?". +// Returns true on success. If the Perl flag is not +// well-formed or not supported, sets status_ and returns false. +bool Regexp::ParseState::ParsePerlFlags(StringPiece* s) { + StringPiece t = *s; + + // Caller is supposed to check this. + if (!(flags_ & PerlX) || t.size() < 2 || t[0] != '(' || t[1] != '?') { +#if 0 + LOG(DFATAL) << "Bad call to ParseState::ParsePerlFlags"; +#endif + status_->set_code(kRegexpInternalError); + return false; + } + + t.remove_prefix(2); // "(?" + + // Check for named captures, first introduced in Python's regexp library. + // As usual, there are three slightly different syntaxes: + // + // (?Pexpr) the original, introduced by Python + // (?expr) the .NET alteration, adopted by Perl 5.10 + // (?'name'expr) another .NET alteration, adopted by Perl 5.10 + // + // Perl 5.10 gave in and implemented the Python version too, + // but they claim that the last two are the preferred forms. + // PCRE and languages based on it (specifically, PHP and Ruby) + // support all three as well. EcmaScript 4 uses only the Python form. + // + // In both the open source world (via Code Search) and the + // Google source tree, (?Pname) is the dominant form, + // so that's the one we implement. One is enough. + if (t.size() > 2 && t[0] == 'P' && t[1] == '<') { + // Pull out name. + size_t end = t.find('>', 2); + if (end == StringPiece::npos) { + if (!IsValidUTF8(*s, status_)) + return false; + status_->set_code(kRegexpBadNamedCapture); + status_->set_error_arg(*s); + return false; + } + + // t is "P...", t[end] == '>' + StringPiece capture(t.data()-2, end+3); // "(?P" + StringPiece name(t.data()+2, end-2); // "name" + if (!IsValidUTF8(name, status_)) + return false; + if (!IsValidCaptureName(name)) { + status_->set_code(kRegexpBadNamedCapture); + status_->set_error_arg(capture); + return false; + } + + if (!DoLeftParen(name)) { + // DoLeftParen's failure set status_. + return false; + } + + s->remove_prefix( + static_cast(capture.data() + capture.size() - s->data())); + return true; + } + + bool negated = false; + bool sawflags = false; + int nflags = flags_; + Rune c; + for (bool done = false; !done; ) { + if (t.empty()) + goto BadPerlOp; + if (StringPieceToRune(&c, &t, status_) < 0) + return false; + switch (c) { + default: + goto BadPerlOp; + + // Parse flags. + case 'i': + sawflags = true; + if (negated) + nflags &= ~FoldCase; + else + nflags |= FoldCase; + break; + + case 'm': // opposite of our OneLine + sawflags = true; + if (negated) + nflags |= OneLine; + else + nflags &= ~OneLine; + break; + + case 's': + sawflags = true; + if (negated) + nflags &= ~DotNL; + else + nflags |= DotNL; + break; + + case 'U': + sawflags = true; + if (negated) + nflags &= ~NonGreedy; + else + nflags |= NonGreedy; + break; + + // Negation + case '-': + if (negated) + goto BadPerlOp; + negated = true; + sawflags = false; + break; + + // Open new group. + case ':': + if (!DoLeftParenNoCapture()) { + // DoLeftParenNoCapture's failure set status_. + return false; + } + done = true; + break; + + // Finish flags. + case ')': + done = true; + break; + } + } + + if (negated && !sawflags) + goto BadPerlOp; + + flags_ = static_cast(nflags); + *s = t; + return true; + +BadPerlOp: + status_->set_code(kRegexpBadPerlOp); + status_->set_error_arg( + StringPiece(s->data(), static_cast(t.data() - s->data()))); + return false; +} + +// Converts latin1 (assumed to be encoded as Latin1 bytes) +// into UTF8 encoding in string. +// Can't use EncodingUtils::EncodeLatin1AsUTF8 because it is +// deprecated and because it rejects code points 0x80-0x9F. +void ConvertLatin1ToUTF8(const StringPiece& latin1, std::string* utf) { + char buf[UTFmax]; + + utf->clear(); + for (size_t i = 0; i < latin1.size(); i++) { + Rune r = latin1[i] & 0xFF; + int n = runetochar(buf, &r); + utf->append(buf, n); + } +} + +// Parses the regular expression given by s, +// returning the corresponding Regexp tree. +// The caller must Decref the return value when done with it. +// Returns NULL on error. +Regexp* Regexp::Parse(const StringPiece& s, ParseFlags global_flags, + RegexpStatus* status) { + // Make status non-NULL (easier on everyone else). + RegexpStatus xstatus; + if (status == NULL) + status = &xstatus; + + ParseState ps(global_flags, s, status); + StringPiece t = s; + + // Convert regexp to UTF-8 (easier on the rest of the parser). + if (global_flags & Latin1) { + std::string* tmp = new std::string; + ConvertLatin1ToUTF8(t, tmp); + status->set_tmp(tmp); + t = *tmp; + } + + if (global_flags & Literal) { + // Special parse loop for literal string. + while (!t.empty()) { + Rune r; + if (StringPieceToRune(&r, &t, status) < 0) + return NULL; + if (!ps.PushLiteral(r)) + return NULL; + } + return ps.DoFinish(); + } + + StringPiece lastunary = StringPiece(); + while (!t.empty()) { + StringPiece isunary = StringPiece(); + switch (t[0]) { + default: { + Rune r; + if (StringPieceToRune(&r, &t, status) < 0) + return NULL; + if (!ps.PushLiteral(r)) + return NULL; + break; + } + + case '(': + // "(?" introduces Perl escape. + if ((ps.flags() & PerlX) && (t.size() >= 2 && t[1] == '?')) { + // Flag changes and non-capturing groups. + if (!ps.ParsePerlFlags(&t)) + return NULL; + break; + } + if (ps.flags() & NeverCapture) { + if (!ps.DoLeftParenNoCapture()) + return NULL; + } else { + if (!ps.DoLeftParen(StringPiece())) + return NULL; + } + t.remove_prefix(1); // '(' + break; + + case '|': + if (!ps.DoVerticalBar()) + return NULL; + t.remove_prefix(1); // '|' + break; + + case ')': + if (!ps.DoRightParen()) + return NULL; + t.remove_prefix(1); // ')' + break; + + case '^': // Beginning of line. + if (!ps.PushCaret()) + return NULL; + t.remove_prefix(1); // '^' + break; + + case '$': // End of line. + if (!ps.PushDollar()) + return NULL; + t.remove_prefix(1); // '$' + break; + + case '.': // Any character (possibly except newline). + if (!ps.PushDot()) + return NULL; + t.remove_prefix(1); // '.' + break; + + case '[': { // Character class. + Regexp* re; + if (!ps.ParseCharClass(&t, &re, status)) + return NULL; + if (!ps.PushRegexp(re)) + return NULL; + break; + } + + case '*': { // Zero or more. + RegexpOp op; + op = kRegexpStar; + goto Rep; + case '+': // One or more. + op = kRegexpPlus; + goto Rep; + case '?': // Zero or one. + op = kRegexpQuest; + goto Rep; + Rep: + StringPiece opstr = t; + bool nongreedy = false; + t.remove_prefix(1); // '*' or '+' or '?' + if (ps.flags() & PerlX) { + if (!t.empty() && t[0] == '?') { + nongreedy = true; + t.remove_prefix(1); // '?' + } + if (!lastunary.empty()) { + // In Perl it is not allowed to stack repetition operators: + // a** is a syntax error, not a double-star. + // (and a++ means something else entirely, which we don't support!) + status->set_code(kRegexpRepeatOp); + status->set_error_arg(StringPiece( + lastunary.data(), + static_cast(t.data() - lastunary.data()))); + return NULL; + } + } + opstr = StringPiece(opstr.data(), + static_cast(t.data() - opstr.data())); + if (!ps.PushRepeatOp(op, opstr, nongreedy)) + return NULL; + isunary = opstr; + break; + } + + case '{': { // Counted repetition. + int lo, hi; + StringPiece opstr = t; + if (!MaybeParseRepetition(&t, &lo, &hi)) { + // Treat like a literal. + if (!ps.PushLiteral('{')) + return NULL; + t.remove_prefix(1); // '{' + break; + } + bool nongreedy = false; + if (ps.flags() & PerlX) { + if (!t.empty() && t[0] == '?') { + nongreedy = true; + t.remove_prefix(1); // '?' + } + if (!lastunary.empty()) { + // Not allowed to stack repetition operators. + status->set_code(kRegexpRepeatOp); + status->set_error_arg(StringPiece( + lastunary.data(), + static_cast(t.data() - lastunary.data()))); + return NULL; + } + } + opstr = StringPiece(opstr.data(), + static_cast(t.data() - opstr.data())); + if (!ps.PushRepetition(lo, hi, opstr, nongreedy)) + return NULL; + isunary = opstr; + break; + } + + case '\\': { // Escaped character or Perl sequence. + // \b and \B: word boundary or not + if ((ps.flags() & Regexp::PerlB) && + t.size() >= 2 && (t[1] == 'b' || t[1] == 'B')) { + if (!ps.PushWordBoundary(t[1] == 'b')) + return NULL; + t.remove_prefix(2); // '\\', 'b' + break; + } + + if ((ps.flags() & Regexp::PerlX) && t.size() >= 2) { + if (t[1] == 'A') { + if (!ps.PushSimpleOp(kRegexpBeginText)) + return NULL; + t.remove_prefix(2); // '\\', 'A' + break; + } + if (t[1] == 'z') { + if (!ps.PushSimpleOp(kRegexpEndText)) + return NULL; + t.remove_prefix(2); // '\\', 'z' + break; + } + // Do not recognize \Z, because this library can't + // implement the exact Perl/PCRE semantics. + // (This library treats "(?-m)$" as \z, even though + // in Perl and PCRE it is equivalent to \Z.) + + if (t[1] == 'C') { // \C: any byte [sic] + if (!ps.PushSimpleOp(kRegexpAnyByte)) + return NULL; + t.remove_prefix(2); // '\\', 'C' + break; + } + + if (t[1] == 'Q') { // \Q ... \E: the ... is always literals + t.remove_prefix(2); // '\\', 'Q' + while (!t.empty()) { + if (t.size() >= 2 && t[0] == '\\' && t[1] == 'E') { + t.remove_prefix(2); // '\\', 'E' + break; + } + Rune r; + if (StringPieceToRune(&r, &t, status) < 0) + return NULL; + if (!ps.PushLiteral(r)) + return NULL; + } + break; + } + } + + if (t.size() >= 2 && (t[1] == 'p' || t[1] == 'P')) { + Regexp* re = new Regexp(kRegexpCharClass, ps.flags() & ~FoldCase); + re->ccb_ = new CharClassBuilder; + switch (ParseUnicodeGroup(&t, ps.flags(), re->ccb_, status)) { + case kParseOk: + if (!ps.PushRegexp(re)) + return NULL; + goto Break2; + case kParseError: + re->Decref(); + return NULL; + case kParseNothing: + re->Decref(); + break; + } + } + + const UGroup *g = MaybeParsePerlCCEscape(&t, ps.flags()); + if (g != NULL) { + Regexp* re = new Regexp(kRegexpCharClass, ps.flags() & ~FoldCase); + re->ccb_ = new CharClassBuilder; + AddUGroup(re->ccb_, g, g->sign, ps.flags()); + if (!ps.PushRegexp(re)) + return NULL; + break; + } + + Rune r; + if (!ParseEscape(&t, &r, status, ps.rune_max())) + return NULL; + if (!ps.PushLiteral(r)) + return NULL; + break; + } + } + Break2: + lastunary = isunary; + } + return ps.DoFinish(); +} + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/perl_groups.cc b/third_party/opa/wasm/src/re2/re2/perl_groups.cc new file mode 100644 index 000000000000..422b3882d494 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/perl_groups.cc @@ -0,0 +1,119 @@ +// GENERATED BY make_perl_groups.pl; DO NOT EDIT. +// make_perl_groups.pl >perl_groups.cc + +#include "re2/unicode_groups.h" + +namespace re2 { + +static const URange16 code1[] = { /* \d */ + { 0x30, 0x39 }, +}; +static const URange16 code2[] = { /* \s */ + { 0x9, 0xa }, + { 0xc, 0xd }, + { 0x20, 0x20 }, +}; +static const URange16 code3[] = { /* \w */ + { 0x30, 0x39 }, + { 0x41, 0x5a }, + { 0x5f, 0x5f }, + { 0x61, 0x7a }, +}; +const UGroup perl_groups[] = { + { "\\d", +1, code1, 1 }, + { "\\D", -1, code1, 1 }, + { "\\s", +1, code2, 3 }, + { "\\S", -1, code2, 3 }, + { "\\w", +1, code3, 4 }, + { "\\W", -1, code3, 4 }, +}; +const int num_perl_groups = 6; +static const URange16 code4[] = { /* [:alnum:] */ + { 0x30, 0x39 }, + { 0x41, 0x5a }, + { 0x61, 0x7a }, +}; +static const URange16 code5[] = { /* [:alpha:] */ + { 0x41, 0x5a }, + { 0x61, 0x7a }, +}; +static const URange16 code6[] = { /* [:ascii:] */ + { 0x0, 0x7f }, +}; +static const URange16 code7[] = { /* [:blank:] */ + { 0x9, 0x9 }, + { 0x20, 0x20 }, +}; +static const URange16 code8[] = { /* [:cntrl:] */ + { 0x0, 0x1f }, + { 0x7f, 0x7f }, +}; +static const URange16 code9[] = { /* [:digit:] */ + { 0x30, 0x39 }, +}; +static const URange16 code10[] = { /* [:graph:] */ + { 0x21, 0x7e }, +}; +static const URange16 code11[] = { /* [:lower:] */ + { 0x61, 0x7a }, +}; +static const URange16 code12[] = { /* [:print:] */ + { 0x20, 0x7e }, +}; +static const URange16 code13[] = { /* [:punct:] */ + { 0x21, 0x2f }, + { 0x3a, 0x40 }, + { 0x5b, 0x60 }, + { 0x7b, 0x7e }, +}; +static const URange16 code14[] = { /* [:space:] */ + { 0x9, 0xd }, + { 0x20, 0x20 }, +}; +static const URange16 code15[] = { /* [:upper:] */ + { 0x41, 0x5a }, +}; +static const URange16 code16[] = { /* [:word:] */ + { 0x30, 0x39 }, + { 0x41, 0x5a }, + { 0x5f, 0x5f }, + { 0x61, 0x7a }, +}; +static const URange16 code17[] = { /* [:xdigit:] */ + { 0x30, 0x39 }, + { 0x41, 0x46 }, + { 0x61, 0x66 }, +}; +const UGroup posix_groups[] = { + { "[:alnum:]", +1, code4, 3 }, + { "[:^alnum:]", -1, code4, 3 }, + { "[:alpha:]", +1, code5, 2 }, + { "[:^alpha:]", -1, code5, 2 }, + { "[:ascii:]", +1, code6, 1 }, + { "[:^ascii:]", -1, code6, 1 }, + { "[:blank:]", +1, code7, 2 }, + { "[:^blank:]", -1, code7, 2 }, + { "[:cntrl:]", +1, code8, 2 }, + { "[:^cntrl:]", -1, code8, 2 }, + { "[:digit:]", +1, code9, 1 }, + { "[:^digit:]", -1, code9, 1 }, + { "[:graph:]", +1, code10, 1 }, + { "[:^graph:]", -1, code10, 1 }, + { "[:lower:]", +1, code11, 1 }, + { "[:^lower:]", -1, code11, 1 }, + { "[:print:]", +1, code12, 1 }, + { "[:^print:]", -1, code12, 1 }, + { "[:punct:]", +1, code13, 4 }, + { "[:^punct:]", -1, code13, 4 }, + { "[:space:]", +1, code14, 2 }, + { "[:^space:]", -1, code14, 2 }, + { "[:upper:]", +1, code15, 1 }, + { "[:^upper:]", -1, code15, 1 }, + { "[:word:]", +1, code16, 4 }, + { "[:^word:]", -1, code16, 4 }, + { "[:xdigit:]", +1, code17, 3 }, + { "[:^xdigit:]", -1, code17, 3 }, +}; +const int num_posix_groups = 28; + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/pod_array.h b/third_party/opa/wasm/src/re2/re2/pod_array.h new file mode 100644 index 000000000000..f234e976f40d --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/pod_array.h @@ -0,0 +1,55 @@ +// Copyright 2018 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef RE2_POD_ARRAY_H_ +#define RE2_POD_ARRAY_H_ + +#include +#include + +namespace re2 { + +template +class PODArray { + public: + static_assert(std::is_trivial::value && std::is_standard_layout::value, + "T must be POD"); + + PODArray() + : ptr_() {} + explicit PODArray(int len) + : ptr_(std::allocator().allocate(len), Deleter(len)) {} + + T* data() const { + return ptr_.get(); + } + + int size() const { + return ptr_.get_deleter().len_; + } + + T& operator[](int pos) const { + return ptr_[pos]; + } + + private: + struct Deleter { + Deleter() + : len_(0) {} + explicit Deleter(int len) + : len_(len) {} + + void operator()(T* ptr) const { + std::allocator().deallocate(ptr, len_); + } + + int len_; + }; + + std::unique_ptr ptr_; +}; + +} // namespace re2 + +#endif // RE2_POD_ARRAY_H_ diff --git a/third_party/opa/wasm/src/re2/re2/prog.cc b/third_party/opa/wasm/src/re2/re2/prog.cc new file mode 100644 index 000000000000..05d4b0dcd046 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/prog.cc @@ -0,0 +1,998 @@ +// Copyright 2007 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Compiled regular expression representation. +// Tested by compile_test.cc + +#include "re2/prog.h" + +#if defined(__AVX2__) +#include +#ifdef _MSC_VER +#include +#endif +#endif +#include +#include +#include +#include +#include + +#include "util/util.h" +#include "util/logging.h" +#include "util/strutil.h" +#include "re2/bitmap256.h" +#include "re2/stringpiece.h" + +namespace re2 { + +// Constructors per Inst opcode + +void Prog::Inst::InitAlt(uint32_t out, uint32_t out1) { + DCHECK_EQ(out_opcode_, 0); + set_out_opcode(out, kInstAlt); + out1_ = out1; +} + +void Prog::Inst::InitByteRange(int lo, int hi, int foldcase, uint32_t out) { + DCHECK_EQ(out_opcode_, 0); + set_out_opcode(out, kInstByteRange); + lo_ = lo & 0xFF; + hi_ = hi & 0xFF; + hint_foldcase_ = foldcase&1; +} + +void Prog::Inst::InitCapture(int cap, uint32_t out) { + DCHECK_EQ(out_opcode_, 0); + set_out_opcode(out, kInstCapture); + cap_ = cap; +} + +void Prog::Inst::InitEmptyWidth(EmptyOp empty, uint32_t out) { + DCHECK_EQ(out_opcode_, 0); + set_out_opcode(out, kInstEmptyWidth); + empty_ = empty; +} + +void Prog::Inst::InitMatch(int32_t id) { + DCHECK_EQ(out_opcode_, 0); + set_opcode(kInstMatch); + match_id_ = id; +} + +void Prog::Inst::InitNop(uint32_t out) { + DCHECK_EQ(out_opcode_, 0); + set_opcode(kInstNop); +} + +void Prog::Inst::InitFail() { + DCHECK_EQ(out_opcode_, 0); + set_opcode(kInstFail); +} + +std::string Prog::Inst::Dump() { + switch (opcode()) { + default: + return StringPrintf("opcode %d", static_cast(opcode())); + + case kInstAlt: + return StringPrintf("alt -> %d | %d", out(), out1_); + + case kInstAltMatch: + return StringPrintf("altmatch -> %d | %d", out(), out1_); + + case kInstByteRange: + return StringPrintf("byte%s [%02x-%02x] %d -> %d", + foldcase() ? "/i" : "", + lo_, hi_, hint(), out()); + + case kInstCapture: + return StringPrintf("capture %d -> %d", cap_, out()); + + case kInstEmptyWidth: + return StringPrintf("emptywidth %#x -> %d", + static_cast(empty_), out()); + + case kInstMatch: + return StringPrintf("match! %d", match_id()); + + case kInstNop: + return StringPrintf("nop -> %d", out()); + + case kInstFail: + return StringPrintf("fail"); + } +} + +Prog::Prog() + : anchor_start_(false), + anchor_end_(false), + reversed_(false), + did_flatten_(false), + did_onepass_(false), + start_(0), + start_unanchored_(0), + size_(0), + bytemap_range_(0), + prefix_size_(0), + prefix_front_(-1), + prefix_back_(-1), + list_count_(0), + dfa_mem_(0), + dfa_first_(NULL), + dfa_longest_(NULL) { +} + +Prog::~Prog() { + DeleteDFA(dfa_longest_); + DeleteDFA(dfa_first_); +} + +typedef SparseSet Workq; + +static inline void AddToQueue(Workq* q, int id) { + if (id != 0) + q->insert(id); +} + +static std::string ProgToString(Prog* prog, Workq* q) { + std::string s; + for (Workq::iterator i = q->begin(); i != q->end(); ++i) { + int id = *i; + Prog::Inst* ip = prog->inst(id); + s += StringPrintf("%d. %s\n", id, ip->Dump().c_str()); + AddToQueue(q, ip->out()); + if (ip->opcode() == kInstAlt || ip->opcode() == kInstAltMatch) + AddToQueue(q, ip->out1()); + } + return s; +} + +static std::string FlattenedProgToString(Prog* prog, int start) { + std::string s; + for (int id = start; id < prog->size(); id++) { + Prog::Inst* ip = prog->inst(id); + if (ip->last()) + s += StringPrintf("%d. %s\n", id, ip->Dump().c_str()); + else + s += StringPrintf("%d+ %s\n", id, ip->Dump().c_str()); + } + return s; +} + +std::string Prog::Dump() { + if (did_flatten_) + return FlattenedProgToString(this, start_); + + Workq q(size_); + AddToQueue(&q, start_); + return ProgToString(this, &q); +} + +std::string Prog::DumpUnanchored() { + if (did_flatten_) + return FlattenedProgToString(this, start_unanchored_); + + Workq q(size_); + AddToQueue(&q, start_unanchored_); + return ProgToString(this, &q); +} + +std::string Prog::DumpByteMap() { + std::string map; + for (int c = 0; c < 256; c++) { + int b = bytemap_[c]; + int lo = c; + while (c < 256-1 && bytemap_[c+1] == b) + c++; + int hi = c; + map += StringPrintf("[%02x-%02x] -> %d\n", lo, hi, b); + } + return map; +} + +// Is ip a guaranteed match at end of text, perhaps after some capturing? +static bool IsMatch(Prog* prog, Prog::Inst* ip) { + for (;;) { + switch (ip->opcode()) { + default: +#if 0 + LOG(DFATAL) << "Unexpected opcode in IsMatch: " << ip->opcode(); +#endif + return false; + + case kInstAlt: + case kInstAltMatch: + case kInstByteRange: + case kInstFail: + case kInstEmptyWidth: + return false; + + case kInstCapture: + case kInstNop: + ip = prog->inst(ip->out()); + break; + + case kInstMatch: + return true; + } + } +} + +// Peep-hole optimizer. +void Prog::Optimize() { + Workq q(size_); + + // Eliminate nops. Most are taken out during compilation + // but a few are hard to avoid. + q.clear(); + AddToQueue(&q, start_); + for (Workq::iterator i = q.begin(); i != q.end(); ++i) { + int id = *i; + + Inst* ip = inst(id); + int j = ip->out(); + Inst* jp; + while (j != 0 && (jp=inst(j))->opcode() == kInstNop) { + j = jp->out(); + } + ip->set_out(j); + AddToQueue(&q, ip->out()); + + if (ip->opcode() == kInstAlt) { + j = ip->out1(); + while (j != 0 && (jp=inst(j))->opcode() == kInstNop) { + j = jp->out(); + } + ip->out1_ = j; + AddToQueue(&q, ip->out1()); + } + } + + // Insert kInstAltMatch instructions + // Look for + // ip: Alt -> j | k + // j: ByteRange [00-FF] -> ip + // k: Match + // or the reverse (the above is the greedy one). + // Rewrite Alt to AltMatch. + q.clear(); + AddToQueue(&q, start_); + for (Workq::iterator i = q.begin(); i != q.end(); ++i) { + int id = *i; + Inst* ip = inst(id); + AddToQueue(&q, ip->out()); + if (ip->opcode() == kInstAlt) + AddToQueue(&q, ip->out1()); + + if (ip->opcode() == kInstAlt) { + Inst* j = inst(ip->out()); + Inst* k = inst(ip->out1()); + if (j->opcode() == kInstByteRange && j->out() == id && + j->lo() == 0x00 && j->hi() == 0xFF && + IsMatch(this, k)) { + ip->set_opcode(kInstAltMatch); + continue; + } + if (IsMatch(this, j) && + k->opcode() == kInstByteRange && k->out() == id && + k->lo() == 0x00 && k->hi() == 0xFF) { + ip->set_opcode(kInstAltMatch); + } + } + } +} + +uint32_t Prog::EmptyFlags(const StringPiece& text, const char* p) { + int flags = 0; + + // ^ and \A + if (p == text.data()) + flags |= kEmptyBeginText | kEmptyBeginLine; + else if (p[-1] == '\n') + flags |= kEmptyBeginLine; + + // $ and \z + if (p == text.data() + text.size()) + flags |= kEmptyEndText | kEmptyEndLine; + else if (p < text.data() + text.size() && p[0] == '\n') + flags |= kEmptyEndLine; + + // \b and \B + if (p == text.data() && p == text.data() + text.size()) { + // no word boundary here + } else if (p == text.data()) { + if (IsWordChar(p[0])) + flags |= kEmptyWordBoundary; + } else if (p == text.data() + text.size()) { + if (IsWordChar(p[-1])) + flags |= kEmptyWordBoundary; + } else { + if (IsWordChar(p[-1]) != IsWordChar(p[0])) + flags |= kEmptyWordBoundary; + } + if (!(flags & kEmptyWordBoundary)) + flags |= kEmptyNonWordBoundary; + + return flags; +} + +// ByteMapBuilder implements a coloring algorithm. +// +// The first phase is a series of "mark and merge" batches: we mark one or more +// [lo-hi] ranges, then merge them into our internal state. Batching is not for +// performance; rather, it means that the ranges are treated indistinguishably. +// +// Internally, the ranges are represented using a bitmap that stores the splits +// and a vector that stores the colors; both of them are indexed by the ranges' +// last bytes. Thus, in order to merge a [lo-hi] range, we split at lo-1 and at +// hi (if not already split), then recolor each range in between. The color map +// (i.e. from the old color to the new color) is maintained for the lifetime of +// the batch and so underpins this somewhat obscure approach to set operations. +// +// The second phase builds the bytemap from our internal state: we recolor each +// range, then store the new color (which is now the byte class) in each of the +// corresponding array elements. Finally, we output the number of byte classes. +class ByteMapBuilder { + public: + ByteMapBuilder() { + // Initial state: the [0-255] range has color 256. + // This will avoid problems during the second phase, + // in which we assign byte classes numbered from 0. + splits_.Set(255); + colors_[255] = 256; + nextcolor_ = 257; + } + + void Mark(int lo, int hi); + void Merge(); + void Build(uint8_t* bytemap, int* bytemap_range); + + private: + int Recolor(int oldcolor); + + Bitmap256 splits_; + int colors_[256]; + int nextcolor_; + std::vector> colormap_; + std::vector> ranges_; + + ByteMapBuilder(const ByteMapBuilder&) = delete; + ByteMapBuilder& operator=(const ByteMapBuilder&) = delete; +}; + +void ByteMapBuilder::Mark(int lo, int hi) { + DCHECK_GE(lo, 0); + DCHECK_GE(hi, 0); + DCHECK_LE(lo, 255); + DCHECK_LE(hi, 255); + DCHECK_LE(lo, hi); + + // Ignore any [0-255] ranges. They cause us to recolor every range, which + // has no effect on the eventual result and is therefore a waste of time. + if (lo == 0 && hi == 255) + return; + + ranges_.emplace_back(lo, hi); +} + +void ByteMapBuilder::Merge() { + for (std::vector>::const_iterator it = ranges_.begin(); + it != ranges_.end(); + ++it) { + int lo = it->first-1; + int hi = it->second; + + if (0 <= lo && !splits_.Test(lo)) { + splits_.Set(lo); + int next = splits_.FindNextSetBit(lo+1); + colors_[lo] = colors_[next]; + } + if (!splits_.Test(hi)) { + splits_.Set(hi); + int next = splits_.FindNextSetBit(hi+1); + colors_[hi] = colors_[next]; + } + + int c = lo+1; + while (c < 256) { + int next = splits_.FindNextSetBit(c); + colors_[next] = Recolor(colors_[next]); + if (next == hi) + break; + c = next+1; + } + } + colormap_.clear(); + ranges_.clear(); +} + +void ByteMapBuilder::Build(uint8_t* bytemap, int* bytemap_range) { + // Assign byte classes numbered from 0. + nextcolor_ = 0; + + int c = 0; + while (c < 256) { + int next = splits_.FindNextSetBit(c); + uint8_t b = static_cast(Recolor(colors_[next])); + while (c <= next) { + bytemap[c] = b; + c++; + } + } + + *bytemap_range = nextcolor_; +} + +int ByteMapBuilder::Recolor(int oldcolor) { + // Yes, this is a linear search. There can be at most 256 + // colors and there will typically be far fewer than that. + // Also, we need to consider keys *and* values in order to + // avoid recoloring a given range more than once per batch. + std::vector>::const_iterator it = + std::find_if(colormap_.begin(), colormap_.end(), + [=](const std::pair& kv) -> bool { + return kv.first == oldcolor || kv.second == oldcolor; + }); + if (it != colormap_.end()) + return it->second; + int newcolor = nextcolor_; + nextcolor_++; + colormap_.emplace_back(oldcolor, newcolor); + return newcolor; +} + +void Prog::ComputeByteMap() { + // Fill in bytemap with byte classes for the program. + // Ranges of bytes that are treated indistinguishably + // will be mapped to a single byte class. + ByteMapBuilder builder; + + // Don't repeat the work for ^ and $. + bool marked_line_boundaries = false; + // Don't repeat the work for \b and \B. + bool marked_word_boundaries = false; + + for (int id = 0; id < size(); id++) { + Inst* ip = inst(id); + if (ip->opcode() == kInstByteRange) { + int lo = ip->lo(); + int hi = ip->hi(); + builder.Mark(lo, hi); + if (ip->foldcase() && lo <= 'z' && hi >= 'a') { + int foldlo = lo; + int foldhi = hi; + if (foldlo < 'a') + foldlo = 'a'; + if (foldhi > 'z') + foldhi = 'z'; + if (foldlo <= foldhi) { + foldlo += 'A' - 'a'; + foldhi += 'A' - 'a'; + builder.Mark(foldlo, foldhi); + } + } + // If this Inst is not the last Inst in its list AND the next Inst is + // also a ByteRange AND the Insts have the same out, defer the merge. + if (!ip->last() && + inst(id+1)->opcode() == kInstByteRange && + ip->out() == inst(id+1)->out()) + continue; + builder.Merge(); + } else if (ip->opcode() == kInstEmptyWidth) { + if (ip->empty() & (kEmptyBeginLine|kEmptyEndLine) && + !marked_line_boundaries) { + builder.Mark('\n', '\n'); + builder.Merge(); + marked_line_boundaries = true; + } + if (ip->empty() & (kEmptyWordBoundary|kEmptyNonWordBoundary) && + !marked_word_boundaries) { + // We require two batches here: the first for ranges that are word + // characters, the second for ranges that are not word characters. + for (bool isword : {true, false}) { + int j; + for (int i = 0; i < 256; i = j) { + for (j = i + 1; j < 256 && + Prog::IsWordChar(static_cast(i)) == + Prog::IsWordChar(static_cast(j)); + j++) + ; + if (Prog::IsWordChar(static_cast(i)) == isword) + builder.Mark(i, j - 1); + } + builder.Merge(); + } + marked_word_boundaries = true; + } + } + } + + builder.Build(bytemap_, &bytemap_range_); + +#if 0 + if (0) { // For debugging, use trivial bytemap. + LOG(ERROR) << "Using trivial bytemap."; + for (int i = 0; i < 256; i++) + bytemap_[i] = static_cast(i); + bytemap_range_ = 256; + } +#endif +} + +// Prog::Flatten() implements a graph rewriting algorithm. +// +// The overall process is similar to epsilon removal, but retains some epsilon +// transitions: those from Capture and EmptyWidth instructions; and those from +// nullable subexpressions. (The latter avoids quadratic blowup in transitions +// in the worst case.) It might be best thought of as Alt instruction elision. +// +// In conceptual terms, it divides the Prog into "trees" of instructions, then +// traverses the "trees" in order to produce "lists" of instructions. A "tree" +// is one or more instructions that grow from one "root" instruction to one or +// more "leaf" instructions; if a "tree" has exactly one instruction, then the +// "root" is also the "leaf". In most cases, a "root" is the successor of some +// "leaf" (i.e. the "leaf" instruction's out() returns the "root" instruction) +// and is considered a "successor root". A "leaf" can be a ByteRange, Capture, +// EmptyWidth or Match instruction. However, this is insufficient for handling +// nested nullable subexpressions correctly, so in some cases, a "root" is the +// dominator of the instructions reachable from some "successor root" (i.e. it +// has an unreachable predecessor) and is considered a "dominator root". Since +// only Alt instructions can be "dominator roots" (other instructions would be +// "leaves"), only Alt instructions are required to be marked as predecessors. +// +// Dividing the Prog into "trees" comprises two passes: marking the "successor +// roots" and the predecessors; and marking the "dominator roots". Sorting the +// "successor roots" by their bytecode offsets enables iteration in order from +// greatest to least during the second pass; by working backwards in this case +// and flooding the graph no further than "leaves" and already marked "roots", +// it becomes possible to mark "dominator roots" without doing excessive work. +// +// Traversing the "trees" is just iterating over the "roots" in order of their +// marking and flooding the graph no further than "leaves" and "roots". When a +// "leaf" is reached, the instruction is copied with its successor remapped to +// its "root" number. When a "root" is reached, a Nop instruction is generated +// with its successor remapped similarly. As each "list" is produced, its last +// instruction is marked as such. After all of the "lists" have been produced, +// a pass over their instructions remaps their successors to bytecode offsets. +void Prog::Flatten() { + if (did_flatten_) + return; + did_flatten_ = true; + + // Scratch structures. It's important that these are reused by functions + // that we call in loops because they would thrash the heap otherwise. + SparseSet reachable(size()); + std::vector stk; + stk.reserve(size()); + + // First pass: Marks "successor roots" and predecessors. + // Builds the mapping from inst-ids to root-ids. + SparseArray rootmap(size()); + SparseArray predmap(size()); + std::vector> predvec; + MarkSuccessors(&rootmap, &predmap, &predvec, &reachable, &stk); + + // Second pass: Marks "dominator roots". + SparseArray sorted(rootmap); + std::sort(sorted.begin(), sorted.end(), sorted.less); + for (SparseArray::const_iterator i = sorted.end() - 1; + i != sorted.begin(); + --i) { + if (i->index() != start_unanchored() && i->index() != start()) + MarkDominator(i->index(), &rootmap, &predmap, &predvec, &reachable, &stk); + } + + // Third pass: Emits "lists". Remaps outs to root-ids. + // Builds the mapping from root-ids to flat-ids. + std::vector flatmap(rootmap.size()); + std::vector flat; + flat.reserve(size()); + for (SparseArray::const_iterator i = rootmap.begin(); + i != rootmap.end(); + ++i) { + flatmap[i->value()] = static_cast(flat.size()); + EmitList(i->index(), &rootmap, &flat, &reachable, &stk); + flat.back().set_last(); + // We have the bounds of the "list", so this is the + // most convenient point at which to compute hints. + ComputeHints(&flat, flatmap[i->value()], static_cast(flat.size())); + } + + list_count_ = static_cast(flatmap.size()); + for (int i = 0; i < kNumInst; i++) + inst_count_[i] = 0; + + // Fourth pass: Remaps outs to flat-ids. + // Counts instructions by opcode. + for (int id = 0; id < static_cast(flat.size()); id++) { + Inst* ip = &flat[id]; + if (ip->opcode() != kInstAltMatch) // handled in EmitList() + ip->set_out(flatmap[ip->out()]); + inst_count_[ip->opcode()]++; + } + + int total = 0; + for (int i = 0; i < kNumInst; i++) + total += inst_count_[i]; + DCHECK_EQ(total, static_cast(flat.size())); + + // Remap start_unanchored and start. + if (start_unanchored() == 0) { + DCHECK_EQ(start(), 0); + } else if (start_unanchored() == start()) { + set_start_unanchored(flatmap[1]); + set_start(flatmap[1]); + } else { + set_start_unanchored(flatmap[1]); + set_start(flatmap[2]); + } + + // Finally, replace the old instructions with the new instructions. + size_ = static_cast(flat.size()); + inst_ = PODArray(size_); + memmove(inst_.data(), flat.data(), size_*sizeof inst_[0]); + + // Populate the list heads for BitState. + // 512 instructions limits the memory footprint to 1KiB. + if (size_ <= 512) { + list_heads_ = PODArray(size_); + // 0xFF makes it more obvious if we try to look up a non-head. + memset(list_heads_.data(), 0xFF, size_*sizeof list_heads_[0]); + for (int i = 0; i < list_count_; ++i) + list_heads_[flatmap[i]] = i; + } +} + +void Prog::MarkSuccessors(SparseArray* rootmap, + SparseArray* predmap, + std::vector>* predvec, + SparseSet* reachable, std::vector* stk) { + // Mark the kInstFail instruction. + rootmap->set_new(0, rootmap->size()); + + // Mark the start_unanchored and start instructions. + if (!rootmap->has_index(start_unanchored())) + rootmap->set_new(start_unanchored(), rootmap->size()); + if (!rootmap->has_index(start())) + rootmap->set_new(start(), rootmap->size()); + + reachable->clear(); + stk->clear(); + stk->push_back(start_unanchored()); + while (!stk->empty()) { + int id = stk->back(); + stk->pop_back(); + Loop: + if (reachable->contains(id)) + continue; + reachable->insert_new(id); + + Inst* ip = inst(id); + switch (ip->opcode()) { + default: +#if 0 + LOG(DFATAL) << "unhandled opcode: " << ip->opcode(); +#endif + break; + + case kInstAltMatch: + case kInstAlt: + // Mark this instruction as a predecessor of each out. + for (int out : {ip->out(), ip->out1()}) { + if (!predmap->has_index(out)) { + predmap->set_new(out, static_cast(predvec->size())); + predvec->emplace_back(); + } + (*predvec)[predmap->get_existing(out)].emplace_back(id); + } + stk->push_back(ip->out1()); + id = ip->out(); + goto Loop; + + case kInstByteRange: + case kInstCapture: + case kInstEmptyWidth: + // Mark the out of this instruction as a "root". + if (!rootmap->has_index(ip->out())) + rootmap->set_new(ip->out(), rootmap->size()); + id = ip->out(); + goto Loop; + + case kInstNop: + id = ip->out(); + goto Loop; + + case kInstMatch: + case kInstFail: + break; + } + } +} + +void Prog::MarkDominator(int root, SparseArray* rootmap, + SparseArray* predmap, + std::vector>* predvec, + SparseSet* reachable, std::vector* stk) { + reachable->clear(); + stk->clear(); + stk->push_back(root); + while (!stk->empty()) { + int id = stk->back(); + stk->pop_back(); + Loop: + if (reachable->contains(id)) + continue; + reachable->insert_new(id); + + if (id != root && rootmap->has_index(id)) { + // We reached another "tree" via epsilon transition. + continue; + } + + Inst* ip = inst(id); + switch (ip->opcode()) { + default: +#if 0 + LOG(DFATAL) << "unhandled opcode: " << ip->opcode(); +#endif + break; + + case kInstAltMatch: + case kInstAlt: + stk->push_back(ip->out1()); + id = ip->out(); + goto Loop; + + case kInstByteRange: + case kInstCapture: + case kInstEmptyWidth: + break; + + case kInstNop: + id = ip->out(); + goto Loop; + + case kInstMatch: + case kInstFail: + break; + } + } + + for (SparseSet::const_iterator i = reachable->begin(); + i != reachable->end(); + ++i) { + int id = *i; + if (predmap->has_index(id)) { + for (int pred : (*predvec)[predmap->get_existing(id)]) { + if (!reachable->contains(pred)) { + // id has a predecessor that cannot be reached from root! + // Therefore, id must be a "root" too - mark it as such. + if (!rootmap->has_index(id)) + rootmap->set_new(id, rootmap->size()); + } + } + } + } +} + +void Prog::EmitList(int root, SparseArray* rootmap, + std::vector* flat, + SparseSet* reachable, std::vector* stk) { + reachable->clear(); + stk->clear(); + stk->push_back(root); + while (!stk->empty()) { + int id = stk->back(); + stk->pop_back(); + Loop: + if (reachable->contains(id)) + continue; + reachable->insert_new(id); + + if (id != root && rootmap->has_index(id)) { + // We reached another "tree" via epsilon transition. Emit a kInstNop + // instruction so that the Prog does not become quadratically larger. + flat->emplace_back(); + flat->back().set_opcode(kInstNop); + flat->back().set_out(rootmap->get_existing(id)); + continue; + } + + Inst* ip = inst(id); + switch (ip->opcode()) { + default: +#if 0 + LOG(DFATAL) << "unhandled opcode: " << ip->opcode(); +#endif + break; + + case kInstAltMatch: + flat->emplace_back(); + flat->back().set_opcode(kInstAltMatch); + flat->back().set_out(static_cast(flat->size())); + flat->back().out1_ = static_cast(flat->size())+1; + FALLTHROUGH_INTENDED; + + case kInstAlt: + stk->push_back(ip->out1()); + id = ip->out(); + goto Loop; + + case kInstByteRange: + case kInstCapture: + case kInstEmptyWidth: + flat->emplace_back(); + memmove(&flat->back(), ip, sizeof *ip); + flat->back().set_out(rootmap->get_existing(ip->out())); + break; + + case kInstNop: + id = ip->out(); + goto Loop; + + case kInstMatch: + case kInstFail: + flat->emplace_back(); + memmove(&flat->back(), ip, sizeof *ip); + break; + } + } +} + +// For each ByteRange instruction in [begin, end), computes a hint to execution +// engines: the delta to the next instruction (in flat) worth exploring iff the +// current instruction matched. +// +// Implements a coloring algorithm related to ByteMapBuilder, but in this case, +// colors are instructions and recoloring ranges precisely identifies conflicts +// between instructions. Iterating backwards over [begin, end) is guaranteed to +// identify the nearest conflict (if any) with only linear complexity. +void Prog::ComputeHints(std::vector* flat, int begin, int end) { + Bitmap256 splits; + int colors[256]; + + bool dirty = false; + for (int id = end; id >= begin; --id) { + if (id == end || + (*flat)[id].opcode() != kInstByteRange) { + if (dirty) { + dirty = false; + splits.Clear(); + } + splits.Set(255); + colors[255] = id; + // At this point, the [0-255] range is colored with id. + // Thus, hints cannot point beyond id; and if id == end, + // hints that would have pointed to id will be 0 instead. + continue; + } + dirty = true; + + // We recolor the [lo-hi] range with id. Note that first ratchets backwards + // from end to the nearest conflict (if any) during recoloring. + int first = end; + auto Recolor = [&](int lo, int hi) { + // Like ByteMapBuilder, we split at lo-1 and at hi. + --lo; + + if (0 <= lo && !splits.Test(lo)) { + splits.Set(lo); + int next = splits.FindNextSetBit(lo+1); + colors[lo] = colors[next]; + } + if (!splits.Test(hi)) { + splits.Set(hi); + int next = splits.FindNextSetBit(hi+1); + colors[hi] = colors[next]; + } + + int c = lo+1; + while (c < 256) { + int next = splits.FindNextSetBit(c); + // Ratchet backwards... + first = std::min(first, colors[next]); + // Recolor with id - because it's the new nearest conflict! + colors[next] = id; + if (next == hi) + break; + c = next+1; + } + }; + + Inst* ip = &(*flat)[id]; + int lo = ip->lo(); + int hi = ip->hi(); + Recolor(lo, hi); + if (ip->foldcase() && lo <= 'z' && hi >= 'a') { + int foldlo = lo; + int foldhi = hi; + if (foldlo < 'a') + foldlo = 'a'; + if (foldhi > 'z') + foldhi = 'z'; + if (foldlo <= foldhi) { + foldlo += 'A' - 'a'; + foldhi += 'A' - 'a'; + Recolor(foldlo, foldhi); + } + } + + if (first != end) { + uint16_t hint = static_cast(std::min(first - id, 32767)); + ip->hint_foldcase_ |= hint<<1; + } + } +} + +#if defined(__AVX2__) +// Finds the least significant non-zero bit in n. +static int FindLSBSet(uint32_t n) { + DCHECK_NE(n, 0); +#if defined(__GNUC__) + return __builtin_ctz(n); +#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) + unsigned long c; + _BitScanForward(&c, n); + return static_cast(c); +#else + int c = 31; + for (int shift = 1 << 4; shift != 0; shift >>= 1) { + uint32_t word = n << shift; + if (word != 0) { + n = word; + c -= shift; + } + } + return c; +#endif +} +#endif + +const void* Prog::PrefixAccel_FrontAndBack(const void* data, size_t size) { + DCHECK_GE(prefix_size_, 2); + if (size < prefix_size_) + return NULL; + // Don't bother searching the last prefix_size_-1 bytes for prefix_front_. + // This also means that probing for prefix_back_ doesn't go out of bounds. + size -= prefix_size_-1; + +#if defined(__AVX2__) + // Use AVX2 to look for prefix_front_ and prefix_back_ 32 bytes at a time. + if (size >= sizeof(__m256i)) { + const __m256i* fp = reinterpret_cast( + reinterpret_cast(data)); + const __m256i* bp = reinterpret_cast( + reinterpret_cast(data) + prefix_size_-1); + const __m256i* endfp = fp + size/sizeof(__m256i); + const __m256i f_set1 = _mm256_set1_epi8(prefix_front_); + const __m256i b_set1 = _mm256_set1_epi8(prefix_back_); + while (fp != endfp) { + const __m256i f_loadu = _mm256_loadu_si256(fp++); + const __m256i b_loadu = _mm256_loadu_si256(bp++); + const __m256i f_cmpeq = _mm256_cmpeq_epi8(f_set1, f_loadu); + const __m256i b_cmpeq = _mm256_cmpeq_epi8(b_set1, b_loadu); + const int fb_testz = _mm256_testz_si256(f_cmpeq, b_cmpeq); + if (fb_testz == 0) { // ZF: 1 means zero, 0 means non-zero. + const __m256i fb_and = _mm256_and_si256(f_cmpeq, b_cmpeq); + const int fb_movemask = _mm256_movemask_epi8(fb_and); + const int fb_ctz = FindLSBSet(fb_movemask); + return reinterpret_cast(fp-1) + fb_ctz; + } + } + data = fp; + size = size%sizeof(__m256i); + } +#endif + + const char* p0 = reinterpret_cast(data); + for (const char* p = p0;; p++) { + DCHECK_GE(size, static_cast(p-p0)); + p = reinterpret_cast(memchr(p, prefix_front_, size - (p-p0))); + if (p == NULL || p[prefix_size_-1] == prefix_back_) + return p; + } +} + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/prog.h b/third_party/opa/wasm/src/re2/re2/prog.h new file mode 100644 index 000000000000..e9ce682d992a --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/prog.h @@ -0,0 +1,436 @@ +// Copyright 2007 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef RE2_PROG_H_ +#define RE2_PROG_H_ + +// Compiled representation of regular expressions. +// See regexp.h for the Regexp class, which represents a regular +// expression symbolically. + +#include +#include +#include +#include +#include +#include + +#include "util/util.h" +#include "util/logging.h" +#include "re2/pod_array.h" +#include "re2/re2.h" +#include "re2/sparse_array.h" +#include "re2/sparse_set.h" + +namespace re2 { + +// Opcodes for Inst +enum InstOp { + kInstAlt = 0, // choose between out_ and out1_ + kInstAltMatch, // Alt: out_ is [00-FF] and back, out1_ is match; or vice versa. + kInstByteRange, // next (possible case-folded) byte must be in [lo_, hi_] + kInstCapture, // capturing parenthesis number cap_ + kInstEmptyWidth, // empty-width special (^ $ ...); bit(s) set in empty_ + kInstMatch, // found a match! + kInstNop, // no-op; occasionally unavoidable + kInstFail, // never match; occasionally unavoidable + kNumInst, +}; + +// Bit flags for empty-width specials +enum EmptyOp { + kEmptyBeginLine = 1<<0, // ^ - beginning of line + kEmptyEndLine = 1<<1, // $ - end of line + kEmptyBeginText = 1<<2, // \A - beginning of text + kEmptyEndText = 1<<3, // \z - end of text + kEmptyWordBoundary = 1<<4, // \b - word boundary + kEmptyNonWordBoundary = 1<<5, // \B - not \b + kEmptyAllFlags = (1<<6)-1, +}; + +class DFA; +class Regexp; + +// Compiled form of regexp program. +class Prog { + public: + Prog(); + ~Prog(); + + // Single instruction in regexp program. + class Inst { + public: + // See the assertion below for why this is so. + Inst() = default; + + // Copyable. + Inst(const Inst&) = default; + Inst& operator=(const Inst&) = default; + + // Constructors per opcode + void InitAlt(uint32_t out, uint32_t out1); + void InitByteRange(int lo, int hi, int foldcase, uint32_t out); + void InitCapture(int cap, uint32_t out); + void InitEmptyWidth(EmptyOp empty, uint32_t out); + void InitMatch(int id); + void InitNop(uint32_t out); + void InitFail(); + + // Getters + int id(Prog* p) { return static_cast(this - p->inst_.data()); } + InstOp opcode() { return static_cast(out_opcode_&7); } + int last() { return (out_opcode_>>3)&1; } + int out() { return out_opcode_>>4; } + int out1() { DCHECK(opcode() == kInstAlt || opcode() == kInstAltMatch); return out1_; } + int cap() { DCHECK_EQ(opcode(), kInstCapture); return cap_; } + int lo() { DCHECK_EQ(opcode(), kInstByteRange); return lo_; } + int hi() { DCHECK_EQ(opcode(), kInstByteRange); return hi_; } + int foldcase() { DCHECK_EQ(opcode(), kInstByteRange); return hint_foldcase_&1; } + int hint() { DCHECK_EQ(opcode(), kInstByteRange); return hint_foldcase_>>1; } + int match_id() { DCHECK_EQ(opcode(), kInstMatch); return match_id_; } + EmptyOp empty() { DCHECK_EQ(opcode(), kInstEmptyWidth); return empty_; } + + bool greedy(Prog* p) { + DCHECK_EQ(opcode(), kInstAltMatch); + return p->inst(out())->opcode() == kInstByteRange || + (p->inst(out())->opcode() == kInstNop && + p->inst(p->inst(out())->out())->opcode() == kInstByteRange); + } + + // Does this inst (an kInstByteRange) match c? + inline bool Matches(int c) { + DCHECK_EQ(opcode(), kInstByteRange); + if (foldcase() && 'A' <= c && c <= 'Z') + c += 'a' - 'A'; + return lo_ <= c && c <= hi_; + } + + // Returns string representation for debugging. + std::string Dump(); + + // Maximum instruction id. + // (Must fit in out_opcode_. PatchList/last steal another bit.) + static const int kMaxInst = (1<<28) - 1; + + private: + void set_opcode(InstOp opcode) { + out_opcode_ = (out()<<4) | (last()<<3) | opcode; + } + + void set_last() { + out_opcode_ = (out()<<4) | (1<<3) | opcode(); + } + + void set_out(int out) { + out_opcode_ = (out<<4) | (last()<<3) | opcode(); + } + + void set_out_opcode(int out, InstOp opcode) { + out_opcode_ = (out<<4) | (last()<<3) | opcode; + } + + uint32_t out_opcode_; // 28 bits: out, 1 bit: last, 3 (low) bits: opcode + union { // additional instruction arguments: + uint32_t out1_; // opcode == kInstAlt + // alternate next instruction + + int32_t cap_; // opcode == kInstCapture + // Index of capture register (holds text + // position recorded by capturing parentheses). + // For \n (the submatch for the nth parentheses), + // the left parenthesis captures into register 2*n + // and the right one captures into register 2*n+1. + + int32_t match_id_; // opcode == kInstMatch + // Match ID to identify this match (for re2::Set). + + struct { // opcode == kInstByteRange + uint8_t lo_; // byte range is lo_-hi_ inclusive + uint8_t hi_; // + uint16_t hint_foldcase_; // 15 bits: hint, 1 (low) bit: foldcase + // hint to execution engines: the delta to the + // next instruction (in the current list) worth + // exploring iff this instruction matched; 0 + // means there are no remaining possibilities, + // which is most likely for character classes. + // foldcase: A-Z -> a-z before checking range. + }; + + EmptyOp empty_; // opcode == kInstEmptyWidth + // empty_ is bitwise OR of kEmpty* flags above. + }; + + friend class Compiler; + friend struct PatchList; + friend class Prog; + }; + + // Inst must be trivial so that we can freely clear it with memset(3). + // Arrays of Inst are initialised by copying the initial elements with + // memmove(3) and then clearing any remaining elements with memset(3). + static_assert(std::is_trivial::value, "Inst must be trivial"); + + // Whether to anchor the search. + enum Anchor { + kUnanchored, // match anywhere + kAnchored, // match only starting at beginning of text + }; + + // Kind of match to look for (for anchor != kFullMatch) + // + // kLongestMatch mode finds the overall longest + // match but still makes its submatch choices the way + // Perl would, not in the way prescribed by POSIX. + // The POSIX rules are much more expensive to implement, + // and no one has needed them. + // + // kFullMatch is not strictly necessary -- we could use + // kLongestMatch and then check the length of the match -- but + // the matching code can run faster if it knows to consider only + // full matches. + enum MatchKind { + kFirstMatch, // like Perl, PCRE + kLongestMatch, // like egrep or POSIX + kFullMatch, // match only entire text; implies anchor==kAnchored + kManyMatch // for SearchDFA, records set of matches + }; + + Inst *inst(int id) { return &inst_[id]; } + int start() { return start_; } + void set_start(int start) { start_ = start; } + int start_unanchored() { return start_unanchored_; } + void set_start_unanchored(int start) { start_unanchored_ = start; } + int size() { return size_; } + bool reversed() { return reversed_; } + void set_reversed(bool reversed) { reversed_ = reversed; } + int list_count() { return list_count_; } + int inst_count(InstOp op) { return inst_count_[op]; } + uint16_t* list_heads() { return list_heads_.data(); } + int64_t dfa_mem() { return dfa_mem_; } + void set_dfa_mem(int64_t dfa_mem) { dfa_mem_ = dfa_mem; } + bool anchor_start() { return anchor_start_; } + void set_anchor_start(bool b) { anchor_start_ = b; } + bool anchor_end() { return anchor_end_; } + void set_anchor_end(bool b) { anchor_end_ = b; } + int bytemap_range() { return bytemap_range_; } + const uint8_t* bytemap() { return bytemap_; } + bool can_prefix_accel() { return prefix_size_ != 0; } + + // Accelerates to the first likely occurrence of the prefix. + // Returns a pointer to the first byte or NULL if not found. + const void* PrefixAccel(const void* data, size_t size) { + DCHECK_GE(prefix_size_, 1); + return prefix_size_ == 1 ? memchr(data, prefix_front_, size) + : PrefixAccel_FrontAndBack(data, size); + } + + // An implementation of prefix accel that looks for prefix_front_ and + // prefix_back_ to return fewer false positives than memchr(3) alone. + const void* PrefixAccel_FrontAndBack(const void* data, size_t size); + + // Returns string representation of program for debugging. + std::string Dump(); + std::string DumpUnanchored(); + std::string DumpByteMap(); + + // Returns the set of kEmpty flags that are in effect at + // position p within context. + static uint32_t EmptyFlags(const StringPiece& context, const char* p); + + // Returns whether byte c is a word character: ASCII only. + // Used by the implementation of \b and \B. + // This is not right for Unicode, but: + // - it's hard to get right in a byte-at-a-time matching world + // (the DFA has only one-byte lookahead). + // - even if the lookahead were possible, the Progs would be huge. + // This crude approximation is the same one PCRE uses. + static bool IsWordChar(uint8_t c) { + return ('A' <= c && c <= 'Z') || + ('a' <= c && c <= 'z') || + ('0' <= c && c <= '9') || + c == '_'; + } + + // Execution engines. They all search for the regexp (run the prog) + // in text, which is in the larger context (used for ^ $ \b etc). + // Anchor and kind control the kind of search. + // Returns true if match found, false if not. + // If match found, fills match[0..nmatch-1] with submatch info. + // match[0] is overall match, match[1] is first set of parens, etc. + // If a particular submatch is not matched during the regexp match, + // it is set to NULL. + // + // Matching text == StringPiece(NULL, 0) is treated as any other empty + // string, but note that on return, it will not be possible to distinguish + // submatches that matched that empty string from submatches that didn't + // match anything. Either way, match[i] == NULL. + + // Search using NFA: can find submatches but kind of slow. + bool SearchNFA(const StringPiece& text, const StringPiece& context, + Anchor anchor, MatchKind kind, + StringPiece* match, int nmatch); + + // Search using DFA: much faster than NFA but only finds + // end of match and can use a lot more memory. + // Returns whether a match was found. + // If the DFA runs out of memory, sets *failed to true and returns false. + // If matches != NULL and kind == kManyMatch and there is a match, + // SearchDFA fills matches with the match IDs of the final matching state. + bool SearchDFA(const StringPiece& text, const StringPiece& context, + Anchor anchor, MatchKind kind, StringPiece* match0, + bool* failed, SparseSet* matches); + + // The callback issued after building each DFA state with BuildEntireDFA(). + // If next is null, then the memory budget has been exhausted and building + // will halt. Otherwise, the state has been built and next points to an array + // of bytemap_range()+1 slots holding the next states as per the bytemap and + // kByteEndText. The number of the state is implied by the callback sequence: + // the first callback is for state 0, the second callback is for state 1, ... + // match indicates whether the state is a matching state. + using DFAStateCallback = std::function; + + // Build the entire DFA for the given match kind. + // Usually the DFA is built out incrementally, as needed, which + // avoids lots of unnecessary work. + // If cb is not empty, it receives one callback per state built. + // Returns the number of states built. + // FOR TESTING OR EXPERIMENTAL PURPOSES ONLY. + int BuildEntireDFA(MatchKind kind, const DFAStateCallback& cb); + + // Controls whether the DFA should bail out early if the NFA would be faster. + // FOR TESTING ONLY. + static void TEST_dfa_should_bail_when_slow(bool b); + + // Compute bytemap. + void ComputeByteMap(); + + // Run peep-hole optimizer on program. + void Optimize(); + + // One-pass NFA: only correct if IsOnePass() is true, + // but much faster than NFA (competitive with PCRE) + // for those expressions. + bool IsOnePass(); + bool SearchOnePass(const StringPiece& text, const StringPiece& context, + Anchor anchor, MatchKind kind, + StringPiece* match, int nmatch); + + // Bit-state backtracking. Fast on small cases but uses memory + // proportional to the product of the list count and the text size. + bool CanBitState() { return list_heads_.data() != NULL; } + bool SearchBitState(const StringPiece& text, const StringPiece& context, + Anchor anchor, MatchKind kind, + StringPiece* match, int nmatch); + + static const int kMaxOnePassCapture = 5; // $0 through $4 + + // Backtracking search: the gold standard against which the other + // implementations are checked. FOR TESTING ONLY. + // It allocates a ton of memory to avoid running forever. + // It is also recursive, so can't use in production (will overflow stacks). + // The name "Unsafe" here is supposed to be a flag that + // you should not be using this function. + bool UnsafeSearchBacktrack(const StringPiece& text, + const StringPiece& context, + Anchor anchor, MatchKind kind, + StringPiece* match, int nmatch); + + // Computes range for any strings matching regexp. The min and max can in + // some cases be arbitrarily precise, so the caller gets to specify the + // maximum desired length of string returned. + // + // Assuming PossibleMatchRange(&min, &max, N) returns successfully, any + // string s that is an anchored match for this regexp satisfies + // min <= s && s <= max. + // + // Note that PossibleMatchRange() will only consider the first copy of an + // infinitely repeated element (i.e., any regexp element followed by a '*' or + // '+' operator). Regexps with "{N}" constructions are not affected, as those + // do not compile down to infinite repetitions. + // + // Returns true on success, false on error. + bool PossibleMatchRange(std::string* min, std::string* max, int maxlen); + + // EXPERIMENTAL! SUBJECT TO CHANGE! + // Outputs the program fanout into the given sparse array. + void Fanout(SparseArray* fanout); + + // Compiles a collection of regexps to Prog. Each regexp will have + // its own Match instruction recording the index in the output vector. + static Prog* CompileSet(Regexp* re, RE2::Anchor anchor, int64_t max_mem); + + // Flattens the Prog from "tree" form to "list" form. This is an in-place + // operation in the sense that the old instructions are lost. + void Flatten(); + + // Walks the Prog; the "successor roots" or predecessors of the reachable + // instructions are marked in rootmap or predmap/predvec, respectively. + // reachable and stk are preallocated scratch structures. + void MarkSuccessors(SparseArray* rootmap, + SparseArray* predmap, + std::vector>* predvec, + SparseSet* reachable, std::vector* stk); + + // Walks the Prog from the given "root" instruction; the "dominator root" + // of the reachable instructions (if such exists) is marked in rootmap. + // reachable and stk are preallocated scratch structures. + void MarkDominator(int root, SparseArray* rootmap, + SparseArray* predmap, + std::vector>* predvec, + SparseSet* reachable, std::vector* stk); + + // Walks the Prog from the given "root" instruction; the reachable + // instructions are emitted in "list" form and appended to flat. + // reachable and stk are preallocated scratch structures. + void EmitList(int root, SparseArray* rootmap, + std::vector* flat, + SparseSet* reachable, std::vector* stk); + + // Computes hints for ByteRange instructions in [begin, end). + void ComputeHints(std::vector* flat, int begin, int end); + + private: + friend class Compiler; + + DFA* GetDFA(MatchKind kind); + void DeleteDFA(DFA* dfa); + + bool anchor_start_; // regexp has explicit start anchor + bool anchor_end_; // regexp has explicit end anchor + bool reversed_; // whether program runs backward over input + bool did_flatten_; // has Flatten been called? + bool did_onepass_; // has IsOnePass been called? + + int start_; // entry point for program + int start_unanchored_; // unanchored entry point for program + int size_; // number of instructions + int bytemap_range_; // bytemap_[x] < bytemap_range_ + size_t prefix_size_; // size of prefix (0 if no prefix) + int prefix_front_; // first byte of prefix (-1 if no prefix) + int prefix_back_; // last byte of prefix (-1 if no prefix) + + int list_count_; // count of lists (see above) + int inst_count_[kNumInst]; // count of instructions by opcode + PODArray list_heads_; // sparse array enumerating list heads + // not populated if size_ is overly large + + PODArray inst_; // pointer to instruction array + PODArray onepass_nodes_; // data for OnePass nodes + + int64_t dfa_mem_; // Maximum memory for DFAs. + DFA* dfa_first_; // DFA cached for kFirstMatch/kManyMatch + DFA* dfa_longest_; // DFA cached for kLongestMatch/kFullMatch + + uint8_t bytemap_[256]; // map from input bytes to byte classes + + std::once_flag dfa_first_once_; + std::once_flag dfa_longest_once_; + + Prog(const Prog&) = delete; + Prog& operator=(const Prog&) = delete; +}; + +} // namespace re2 + +#endif // RE2_PROG_H_ diff --git a/third_party/opa/wasm/src/re2/re2/re2.cc b/third_party/opa/wasm/src/re2/re2/re2.cc new file mode 100644 index 000000000000..5b9fd43197af --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/re2.cc @@ -0,0 +1,1369 @@ +// Copyright 2003-2009 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Regular expression interface RE2. +// +// Originally the PCRE C++ wrapper, but adapted to use +// the new automata-based regular expression engines. + +#include "re2/re2.h" + +#include +#include +#include +#ifdef _MSC_VER +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/util.h" +#include "util/logging.h" +#include "util/strutil.h" +#include "util/utf.h" +#include "re2/prog.h" +#include "re2/regexp.h" +#include "re2/sparse_array.h" + +namespace re2 { + +// Maximum number of args we can set +static const int kMaxArgs = 16; +static const int kVecSize = 1+kMaxArgs; + +const int RE2::Options::kDefaultMaxMem; // initialized in re2.h + +RE2::Options::Options(RE2::CannedOptions opt) + : encoding_(opt == RE2::Latin1 ? EncodingLatin1 : EncodingUTF8), + posix_syntax_(opt == RE2::POSIX), + longest_match_(opt == RE2::POSIX), + log_errors_(opt != RE2::Quiet), + max_mem_(kDefaultMaxMem), + literal_(false), + never_nl_(false), + dot_nl_(false), + never_capture_(false), + case_sensitive_(true), + perl_classes_(false), + word_boundary_(false), + one_line_(false) { +} + +// static empty objects for use as const references. +// To avoid global constructors, allocated in RE2::Init(). +static const std::string* empty_string; +static const std::map* empty_named_groups; +static const std::map* empty_group_names; + +// Converts from Regexp error code to RE2 error code. +// Maybe some day they will diverge. In any event, this +// hides the existence of Regexp from RE2 users. +static RE2::ErrorCode RegexpErrorToRE2(re2::RegexpStatusCode code) { + switch (code) { + case re2::kRegexpSuccess: + return RE2::NoError; + case re2::kRegexpInternalError: + return RE2::ErrorInternal; + case re2::kRegexpBadEscape: + return RE2::ErrorBadEscape; + case re2::kRegexpBadCharClass: + return RE2::ErrorBadCharClass; + case re2::kRegexpBadCharRange: + return RE2::ErrorBadCharRange; + case re2::kRegexpMissingBracket: + return RE2::ErrorMissingBracket; + case re2::kRegexpMissingParen: + return RE2::ErrorMissingParen; + case re2::kRegexpUnexpectedParen: + return RE2::ErrorUnexpectedParen; + case re2::kRegexpTrailingBackslash: + return RE2::ErrorTrailingBackslash; + case re2::kRegexpRepeatArgument: + return RE2::ErrorRepeatArgument; + case re2::kRegexpRepeatSize: + return RE2::ErrorRepeatSize; + case re2::kRegexpRepeatOp: + return RE2::ErrorRepeatOp; + case re2::kRegexpBadPerlOp: + return RE2::ErrorBadPerlOp; + case re2::kRegexpBadUTF8: + return RE2::ErrorBadUTF8; + case re2::kRegexpBadNamedCapture: + return RE2::ErrorBadNamedCapture; + } + return RE2::ErrorInternal; +} + +static std::string trunc(const StringPiece& pattern) { + if (pattern.size() < 100) + return std::string(pattern); + return std::string(pattern.substr(0, 100)) + "..."; +} + + +RE2::RE2(const char* pattern) { + Init(pattern, DefaultOptions); +} + +RE2::RE2(const std::string& pattern) { + Init(pattern, DefaultOptions); +} + +RE2::RE2(const StringPiece& pattern) { + Init(pattern, DefaultOptions); +} + +RE2::RE2(const StringPiece& pattern, const Options& options) { + Init(pattern, options); +} + +int RE2::Options::ParseFlags() const { + int flags = Regexp::ClassNL; + switch (encoding()) { + default: +#if 0 + if (log_errors()) + LOG(ERROR) << "Unknown encoding " << encoding(); +#endif + break; + case RE2::Options::EncodingUTF8: + break; + case RE2::Options::EncodingLatin1: + flags |= Regexp::Latin1; + break; + } + + if (!posix_syntax()) + flags |= Regexp::LikePerl; + + if (literal()) + flags |= Regexp::Literal; + + if (never_nl()) + flags |= Regexp::NeverNL; + + if (dot_nl()) + flags |= Regexp::DotNL; + + if (never_capture()) + flags |= Regexp::NeverCapture; + + if (!case_sensitive()) + flags |= Regexp::FoldCase; + + if (perl_classes()) + flags |= Regexp::PerlClasses; + + if (word_boundary()) + flags |= Regexp::PerlB; + + if (one_line()) + flags |= Regexp::OneLine; + + return flags; +} + +void RE2::Init(const StringPiece& pattern, const Options& options) { + static std::once_flag empty_once; + std::call_once(empty_once, []() { + empty_string = new std::string; + empty_named_groups = new std::map; + empty_group_names = new std::map; + }); + + pattern_.assign(pattern.data(), pattern.size()); + options_.Copy(options); + entire_regexp_ = NULL; + error_ = empty_string; + error_code_ = NoError; + error_arg_.clear(); + prefix_.clear(); + prefix_foldcase_ = false; + suffix_regexp_ = NULL; + prog_ = NULL; + num_captures_ = -1; + is_one_pass_ = false; + + rprog_ = NULL; + named_groups_ = NULL; + group_names_ = NULL; + + RegexpStatus status; + entire_regexp_ = Regexp::Parse( + pattern_, + static_cast(options_.ParseFlags()), + &status); + if (entire_regexp_ == NULL) { +#if 0 + if (options_.log_errors()) { + LOG(ERROR) << "Error parsing '" << trunc(pattern_) << "': " + << status.Text(); + } +#endif + error_ = new std::string(status.Text()); + error_code_ = RegexpErrorToRE2(status.code()); + error_arg_ = std::string(status.error_arg()); + return; + } + + re2::Regexp* suffix; + if (entire_regexp_->RequiredPrefix(&prefix_, &prefix_foldcase_, &suffix)) + suffix_regexp_ = suffix; + else + suffix_regexp_ = entire_regexp_->Incref(); + + // Two thirds of the memory goes to the forward Prog, + // one third to the reverse prog, because the forward + // Prog has two DFAs but the reverse prog has one. + prog_ = suffix_regexp_->CompileToProg(options_.max_mem()*2/3); + if (prog_ == NULL) { +#if 0 + if (options_.log_errors()) + LOG(ERROR) << "Error compiling '" << trunc(pattern_) << "'"; +#endif + error_ = new std::string("pattern too large - compile failed"); + error_code_ = RE2::ErrorPatternTooLarge; + return; + } + + // We used to compute this lazily, but it's used during the + // typical control flow for a match call, so we now compute + // it eagerly, which avoids the overhead of std::once_flag. + num_captures_ = suffix_regexp_->NumCaptures(); + + // Could delay this until the first match call that + // cares about submatch information, but the one-pass + // machine's memory gets cut from the DFA memory budget, + // and that is harder to do if the DFA has already + // been built. + is_one_pass_ = prog_->IsOnePass(); +} + +// Returns rprog_, computing it if needed. +re2::Prog* RE2::ReverseProg() const { + std::call_once(rprog_once_, [](const RE2* re) { + re->rprog_ = + re->suffix_regexp_->CompileToReverseProg(re->options_.max_mem() / 3); + if (re->rprog_ == NULL) { +#if 0 + if (re->options_.log_errors()) + LOG(ERROR) << "Error reverse compiling '" << trunc(re->pattern_) << "'"; +#endif + // We no longer touch error_ and error_code_ because failing to compile + // the reverse Prog is not a showstopper: falling back to NFA execution + // is fine. More importantly, an RE2 object is supposed to be logically + // immutable: whatever ok() would have returned after Init() completed, + // it should continue to return that no matter what ReverseProg() does. + } + }, this); + return rprog_; +} + +RE2::~RE2() { + if (suffix_regexp_) + suffix_regexp_->Decref(); + if (entire_regexp_) + entire_regexp_->Decref(); + delete prog_; + delete rprog_; + if (error_ != empty_string) + delete error_; + if (named_groups_ != NULL && named_groups_ != empty_named_groups) + delete named_groups_; + if (group_names_ != NULL && group_names_ != empty_group_names) + delete group_names_; +} + +int RE2::ProgramSize() const { + if (prog_ == NULL) + return -1; + return prog_->size(); +} + +int RE2::ReverseProgramSize() const { + if (prog_ == NULL) + return -1; + Prog* prog = ReverseProg(); + if (prog == NULL) + return -1; + return prog->size(); +} + +// Finds the most significant non-zero bit in n. +static int FindMSBSet(uint32_t n) { + DCHECK_NE(n, 0); +#if defined(__GNUC__) + return 31 ^ __builtin_clz(n); +#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) + unsigned long c; + _BitScanReverse(&c, n); + return static_cast(c); +#else + int c = 0; + for (int shift = 1 << 4; shift != 0; shift >>= 1) { + uint32_t word = n >> shift; + if (word != 0) { + n = word; + c += shift; + } + } + return c; +#endif +} + +static int Fanout(Prog* prog, std::vector* histogram) { + SparseArray fanout(prog->size()); + prog->Fanout(&fanout); + int data[32] = {}; + int size = 0; + for (SparseArray::iterator i = fanout.begin(); i != fanout.end(); ++i) { + if (i->value() == 0) + continue; + uint32_t value = i->value(); + int bucket = FindMSBSet(value); + bucket += value & (value-1) ? 1 : 0; + ++data[bucket]; + size = std::max(size, bucket+1); + } + if (histogram != NULL) + histogram->assign(data, data+size); + return size-1; +} + +int RE2::ProgramFanout(std::vector* histogram) const { + if (prog_ == NULL) + return -1; + return Fanout(prog_, histogram); +} + +int RE2::ReverseProgramFanout(std::vector* histogram) const { + if (prog_ == NULL) + return -1; + Prog* prog = ReverseProg(); + if (prog == NULL) + return -1; + return Fanout(prog, histogram); +} + +// Returns named_groups_, computing it if needed. +const std::map& RE2::NamedCapturingGroups() const { + std::call_once(named_groups_once_, [](const RE2* re) { + if (re->suffix_regexp_ != NULL) + re->named_groups_ = re->suffix_regexp_->NamedCaptures(); + if (re->named_groups_ == NULL) + re->named_groups_ = empty_named_groups; + }, this); + return *named_groups_; +} + +// Returns group_names_, computing it if needed. +const std::map& RE2::CapturingGroupNames() const { + std::call_once(group_names_once_, [](const RE2* re) { + if (re->suffix_regexp_ != NULL) + re->group_names_ = re->suffix_regexp_->CaptureNames(); + if (re->group_names_ == NULL) + re->group_names_ = empty_group_names; + }, this); + return *group_names_; +} + +/***** Convenience interfaces *****/ + +bool RE2::FullMatchN(const StringPiece& text, const RE2& re, + const Arg* const args[], int n) { + return re.DoMatch(text, ANCHOR_BOTH, NULL, args, n); +} + +bool RE2::PartialMatchN(const StringPiece& text, const RE2& re, + const Arg* const args[], int n) { + return re.DoMatch(text, UNANCHORED, NULL, args, n); +} + +bool RE2::ConsumeN(StringPiece* input, const RE2& re, + const Arg* const args[], int n) { + size_t consumed; + if (re.DoMatch(*input, ANCHOR_START, &consumed, args, n)) { + input->remove_prefix(consumed); + return true; + } else { + return false; + } +} + +bool RE2::FindAndConsumeN(StringPiece* input, const RE2& re, + const Arg* const args[], int n) { + size_t consumed; + if (re.DoMatch(*input, UNANCHORED, &consumed, args, n)) { + input->remove_prefix(consumed); + return true; + } else { + return false; + } +} + +bool RE2::Replace(std::string* str, + const RE2& re, + const StringPiece& rewrite) { + StringPiece vec[kVecSize]; + int nvec = 1 + MaxSubmatch(rewrite); + if (nvec > 1 + re.NumberOfCapturingGroups()) + return false; + if (nvec > static_cast(arraysize(vec))) + return false; + if (!re.Match(*str, 0, str->size(), UNANCHORED, vec, nvec)) + return false; + + std::string s; + if (!re.Rewrite(&s, rewrite, vec, nvec)) + return false; + + assert(vec[0].data() >= str->data()); + assert(vec[0].data() + vec[0].size() <= str->data() + str->size()); + str->replace(vec[0].data() - str->data(), vec[0].size(), s); + return true; +} + +int RE2::GlobalReplace(std::string* str, + const RE2& re, + const StringPiece& rewrite) { + StringPiece vec[kVecSize]; + int nvec = 1 + MaxSubmatch(rewrite); + if (nvec > 1 + re.NumberOfCapturingGroups()) + return false; + if (nvec > static_cast(arraysize(vec))) + return false; + + const char* p = str->data(); + const char* ep = p + str->size(); + const char* lastend = NULL; + std::string out; + int count = 0; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // Iterate just once when fuzzing. Otherwise, we easily get bogged down + // and coverage is unlikely to improve despite significant expense. + while (p == str->data()) { +#else + while (p <= ep) { +#endif + if (!re.Match(*str, static_cast(p - str->data()), + str->size(), UNANCHORED, vec, nvec)) + break; + if (p < vec[0].data()) + out.append(p, vec[0].data() - p); + if (vec[0].data() == lastend && vec[0].empty()) { + // Disallow empty match at end of last match: skip ahead. + // + // fullrune() takes int, not ptrdiff_t. However, it just looks + // at the leading byte and treats any length >= 4 the same. + if (re.options().encoding() == RE2::Options::EncodingUTF8 && + fullrune(p, static_cast(std::min(ptrdiff_t{4}, ep - p)))) { + // re is in UTF-8 mode and there is enough left of str + // to allow us to advance by up to UTFmax bytes. + Rune r; + int n = chartorune(&r, p); + // Some copies of chartorune have a bug that accepts + // encodings of values in (10FFFF, 1FFFFF] as valid. + if (r > Runemax) { + n = 1; + r = Runeerror; + } + if (!(n == 1 && r == Runeerror)) { // no decoding error + out.append(p, n); + p += n; + continue; + } + } + // Most likely, re is in Latin-1 mode. If it is in UTF-8 mode, + // we fell through from above and the GIGO principle applies. + if (p < ep) + out.append(p, 1); + p++; + continue; + } + re.Rewrite(&out, rewrite, vec, nvec); + p = vec[0].data() + vec[0].size(); + lastend = p; + count++; + } + + if (count == 0) + return 0; + + if (p < ep) + out.append(p, ep - p); + using std::swap; + swap(out, *str); + return count; +} + +bool RE2::Extract(const StringPiece& text, + const RE2& re, + const StringPiece& rewrite, + std::string* out) { + StringPiece vec[kVecSize]; + int nvec = 1 + MaxSubmatch(rewrite); + if (nvec > 1 + re.NumberOfCapturingGroups()) + return false; + if (nvec > static_cast(arraysize(vec))) + return false; + if (!re.Match(text, 0, text.size(), UNANCHORED, vec, nvec)) + return false; + + out->clear(); + return re.Rewrite(out, rewrite, vec, nvec); +} + +std::string RE2::QuoteMeta(const StringPiece& unquoted) { + std::string result; + result.reserve(unquoted.size() << 1); + + // Escape any ascii character not in [A-Za-z_0-9]. + // + // Note that it's legal to escape a character even if it has no + // special meaning in a regular expression -- so this function does + // that. (This also makes it identical to the perl function of the + // same name except for the null-character special case; + // see `perldoc -f quotemeta`.) + for (size_t ii = 0; ii < unquoted.size(); ++ii) { + // Note that using 'isalnum' here raises the benchmark time from + // 32ns to 58ns: + if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') && + (unquoted[ii] < 'A' || unquoted[ii] > 'Z') && + (unquoted[ii] < '0' || unquoted[ii] > '9') && + unquoted[ii] != '_' && + // If this is the part of a UTF8 or Latin1 character, we need + // to copy this byte without escaping. Experimentally this is + // what works correctly with the regexp library. + !(unquoted[ii] & 128)) { + if (unquoted[ii] == '\0') { // Special handling for null chars. + // Note that this special handling is not strictly required for RE2, + // but this quoting is required for other regexp libraries such as + // PCRE. + // Can't use "\\0" since the next character might be a digit. + result += "\\x00"; + continue; + } + result += '\\'; + } + result += unquoted[ii]; + } + + return result; +} + +bool RE2::PossibleMatchRange(std::string* min, std::string* max, + int maxlen) const { + if (prog_ == NULL) + return false; + + int n = static_cast(prefix_.size()); + if (n > maxlen) + n = maxlen; + + // Determine initial min max from prefix_ literal. + *min = prefix_.substr(0, n); + *max = prefix_.substr(0, n); + if (prefix_foldcase_) { + // prefix is ASCII lowercase; change *min to uppercase. + for (int i = 0; i < n; i++) { + char& c = (*min)[i]; + if ('a' <= c && c <= 'z') + c += 'A' - 'a'; + } + } + + // Add to prefix min max using PossibleMatchRange on regexp. + std::string dmin, dmax; + maxlen -= n; + if (maxlen > 0 && prog_->PossibleMatchRange(&dmin, &dmax, maxlen)) { + min->append(dmin); + max->append(dmax); + } else if (!max->empty()) { + // prog_->PossibleMatchRange has failed us, + // but we still have useful information from prefix_. + // Round up *max to allow any possible suffix. + PrefixSuccessor(max); + } else { + // Nothing useful. + *min = ""; + *max = ""; + return false; + } + + return true; +} + +// Avoid possible locale nonsense in standard strcasecmp. +// The string a is known to be all lowercase. +static int ascii_strcasecmp(const char* a, const char* b, size_t len) { + const char* ae = a + len; + + for (; a < ae; a++, b++) { + uint8_t x = *a; + uint8_t y = *b; + if ('A' <= y && y <= 'Z') + y += 'a' - 'A'; + if (x != y) + return x - y; + } + return 0; +} + + +/***** Actual matching and rewriting code *****/ + +bool RE2::Match(const StringPiece& text, + size_t startpos, + size_t endpos, + Anchor re_anchor, + StringPiece* submatch, + int nsubmatch) const { + if (!ok()) { +#if 0 + if (options_.log_errors()) + LOG(ERROR) << "Invalid RE2: " << *error_; +#endif + return false; + } + + if (startpos > endpos || endpos > text.size()) { +#if 0 + if (options_.log_errors()) + LOG(ERROR) << "RE2: invalid startpos, endpos pair. [" + << "startpos: " << startpos << ", " + << "endpos: " << endpos << ", " + << "text size: " << text.size() << "]"; +#endif + return false; + } + + StringPiece subtext = text; + subtext.remove_prefix(startpos); + subtext.remove_suffix(text.size() - endpos); + + // Use DFAs to find exact location of match, filter out non-matches. + + // Don't ask for the location if we won't use it. + // SearchDFA can do extra optimizations in that case. + StringPiece match; + StringPiece* matchp = &match; + if (nsubmatch == 0) + matchp = NULL; + + int ncap = 1 + NumberOfCapturingGroups(); + if (ncap > nsubmatch) + ncap = nsubmatch; + + // If the regexp is anchored explicitly, must not be in middle of text. + if (prog_->anchor_start() && startpos != 0) + return false; + if (prog_->anchor_end() && endpos != text.size()) + return false; + + // If the regexp is anchored explicitly, update re_anchor + // so that we can potentially fall into a faster case below. + if (prog_->anchor_start() && prog_->anchor_end()) + re_anchor = ANCHOR_BOTH; + else if (prog_->anchor_start() && re_anchor != ANCHOR_BOTH) + re_anchor = ANCHOR_START; + + // Check for the required prefix, if any. + size_t prefixlen = 0; + if (!prefix_.empty()) { + if (startpos != 0) + return false; + prefixlen = prefix_.size(); + if (prefixlen > subtext.size()) + return false; + if (prefix_foldcase_) { + if (ascii_strcasecmp(&prefix_[0], subtext.data(), prefixlen) != 0) + return false; + } else { + if (memcmp(&prefix_[0], subtext.data(), prefixlen) != 0) + return false; + } + subtext.remove_prefix(prefixlen); + // If there is a required prefix, the anchor must be at least ANCHOR_START. + if (re_anchor != ANCHOR_BOTH) + re_anchor = ANCHOR_START; + } + + Prog::Anchor anchor = Prog::kUnanchored; + Prog::MatchKind kind = Prog::kFirstMatch; + if (options_.longest_match()) + kind = Prog::kLongestMatch; + + bool can_one_pass = (is_one_pass_ && ncap <= Prog::kMaxOnePassCapture); + + // BitState allocates a bitmap of size prog_->list_count() * text.size(). + // It also allocates a stack of 3-word structures which could potentially + // grow as large as prog_->list_count() * text.size(), but in practice is + // much smaller. + const int kMaxBitStateBitmapSize = 256*1024; // bitmap size <= max (bits) + bool can_bit_state = prog_->CanBitState(); + size_t bit_state_text_max = kMaxBitStateBitmapSize / prog_->list_count(); + +#ifdef RE2_HAVE_THREAD_LOCAL + hooks::context = this; +#endif + bool dfa_failed = false; + bool skipped_test = false; + switch (re_anchor) { + default: +#if 0 + LOG(DFATAL) << "Unexpected re_anchor value: " << re_anchor; +#endif + return false; + + case UNANCHORED: { + if (prog_->anchor_end()) { + // This is a very special case: we don't need the forward DFA because + // we already know where the match must end! Instead, the reverse DFA + // can say whether there is a match and (optionally) where it starts. + Prog* prog = ReverseProg(); + if (prog == NULL) { + // Fall back to NFA below. + skipped_test = true; + break; + } + if (!prog->SearchDFA(subtext, text, Prog::kAnchored, + Prog::kLongestMatch, matchp, &dfa_failed, NULL)) { + if (dfa_failed) { +#if 0 + if (options_.log_errors()) + LOG(ERROR) << "DFA out of memory: " + << "pattern length " << pattern_.size() << ", " + << "program size " << prog->size() << ", " + << "list count " << prog->list_count() << ", " + << "bytemap range " << prog->bytemap_range(); +#endif + // Fall back to NFA below. + skipped_test = true; + break; + } + return false; + } + if (matchp == NULL) // Matched. Don't care where. + return true; + break; + } + + if (!prog_->SearchDFA(subtext, text, anchor, kind, + matchp, &dfa_failed, NULL)) { + if (dfa_failed) { +#if 0 + if (options_.log_errors()) + LOG(ERROR) << "DFA out of memory: " + << "pattern length " << pattern_.size() << ", " + << "program size " << prog_->size() << ", " + << "list count " << prog_->list_count() << ", " + << "bytemap range " << prog_->bytemap_range(); +#endif + // Fall back to NFA below. + skipped_test = true; + break; + } + return false; + } + if (matchp == NULL) // Matched. Don't care where. + return true; + // SearchDFA set match.end() but didn't know where the + // match started. Run the regexp backward from match.end() + // to find the longest possible match -- that's where it started. + Prog* prog = ReverseProg(); + if (prog == NULL) { + // Fall back to NFA below. + skipped_test = true; + break; + } + if (!prog->SearchDFA(match, text, Prog::kAnchored, + Prog::kLongestMatch, &match, &dfa_failed, NULL)) { + if (dfa_failed) { +#if 0 + if (options_.log_errors()) + LOG(ERROR) << "DFA out of memory: " + << "pattern length " << pattern_.size() << ", " + << "program size " << prog->size() << ", " + << "list count " << prog->list_count() << ", " + << "bytemap range " << prog->bytemap_range(); +#endif + // Fall back to NFA below. + skipped_test = true; + break; + } +#if 0 + if (options_.log_errors()) + LOG(ERROR) << "SearchDFA inconsistency"; +#endif + return false; + } + break; + } + + case ANCHOR_BOTH: + case ANCHOR_START: + if (re_anchor == ANCHOR_BOTH) + kind = Prog::kFullMatch; + anchor = Prog::kAnchored; + + // If only a small amount of text and need submatch + // information anyway and we're going to use OnePass or BitState + // to get it, we might as well not even bother with the DFA: + // OnePass or BitState will be fast enough. + // On tiny texts, OnePass outruns even the DFA, and + // it doesn't have the shared state and occasional mutex that + // the DFA does. + if (can_one_pass && text.size() <= 4096 && + (ncap > 1 || text.size() <= 8)) { + skipped_test = true; + break; + } + if (can_bit_state && text.size() <= bit_state_text_max && ncap > 1) { + skipped_test = true; + break; + } + if (!prog_->SearchDFA(subtext, text, anchor, kind, + &match, &dfa_failed, NULL)) { + if (dfa_failed) { +#if 0 + if (options_.log_errors()) + LOG(ERROR) << "DFA out of memory: " + << "pattern length " << pattern_.size() << ", " + << "program size " << prog_->size() << ", " + << "list count " << prog_->list_count() << ", " + << "bytemap range " << prog_->bytemap_range(); +#endif + // Fall back to NFA below. + skipped_test = true; + break; + } + return false; + } + break; + } + + if (!skipped_test && ncap <= 1) { + // We know exactly where it matches. That's enough. + if (ncap == 1) + submatch[0] = match; + } else { + StringPiece subtext1; + if (skipped_test) { + // DFA ran out of memory or was skipped: + // need to search in entire original text. + subtext1 = subtext; + } else { + // DFA found the exact match location: + // let NFA run an anchored, full match search + // to find submatch locations. + subtext1 = match; + anchor = Prog::kAnchored; + kind = Prog::kFullMatch; + } + + if (can_one_pass && anchor != Prog::kUnanchored) { + if (!prog_->SearchOnePass(subtext1, text, anchor, kind, submatch, ncap)) { +#if 0 + if (!skipped_test && options_.log_errors()) + LOG(ERROR) << "SearchOnePass inconsistency"; +#endif + return false; + } + } else if (can_bit_state && subtext1.size() <= bit_state_text_max) { + if (!prog_->SearchBitState(subtext1, text, anchor, + kind, submatch, ncap)) { +#if 0 + if (!skipped_test && options_.log_errors()) + LOG(ERROR) << "SearchBitState inconsistency"; +#endif + return false; + } + } else { + if (!prog_->SearchNFA(subtext1, text, anchor, kind, submatch, ncap)) { +#if 0 + if (!skipped_test && options_.log_errors()) + LOG(ERROR) << "SearchNFA inconsistency"; +#endif + return false; + } + } + } + + // Adjust overall match for required prefix that we stripped off. + if (prefixlen > 0 && nsubmatch > 0) + submatch[0] = StringPiece(submatch[0].data() - prefixlen, + submatch[0].size() + prefixlen); + + // Zero submatches that don't exist in the regexp. + for (int i = ncap; i < nsubmatch; i++) + submatch[i] = StringPiece(); + return true; +} + +// Internal matcher - like Match() but takes Args not StringPieces. +bool RE2::DoMatch(const StringPiece& text, + Anchor re_anchor, + size_t* consumed, + const Arg* const* args, + int n) const { + if (!ok()) { +#if 0 + if (options_.log_errors()) + LOG(ERROR) << "Invalid RE2: " << *error_; +#endif + return false; + } + + if (NumberOfCapturingGroups() < n) { + // RE has fewer capturing groups than number of Arg pointers passed in. + return false; + } + + // Count number of capture groups needed. + int nvec; + if (n == 0 && consumed == NULL) + nvec = 0; + else + nvec = n+1; + + StringPiece* vec; + StringPiece stkvec[kVecSize]; + StringPiece* heapvec = NULL; + + if (nvec <= static_cast(arraysize(stkvec))) { + vec = stkvec; + } else { + vec = new StringPiece[nvec]; + heapvec = vec; + } + + if (!Match(text, 0, text.size(), re_anchor, vec, nvec)) { + delete[] heapvec; + return false; + } + + if (consumed != NULL) + *consumed = static_cast(vec[0].end() - text.begin()); + + if (n == 0 || args == NULL) { + // We are not interested in results + delete[] heapvec; + return true; + } + + // If we got here, we must have matched the whole pattern. + for (int i = 0; i < n; i++) { + const StringPiece& s = vec[i+1]; + if (!args[i]->Parse(s.data(), s.size())) { + // TODO: Should we indicate what the error was? + delete[] heapvec; + return false; + } + } + + delete[] heapvec; + return true; +} + +// Checks that the rewrite string is well-formed with respect to this +// regular expression. +bool RE2::CheckRewriteString(const StringPiece& rewrite, + std::string* error) const { + int max_token = -1; + for (const char *s = rewrite.data(), *end = s + rewrite.size(); + s < end; s++) { + int c = *s; + if (c != '\\') { + continue; + } + if (++s == end) { + *error = "Rewrite schema error: '\\' not allowed at end."; + return false; + } + c = *s; + if (c == '\\') { + continue; + } + if (!isdigit(c)) { + *error = "Rewrite schema error: " + "'\\' must be followed by a digit or '\\'."; + return false; + } + int n = (c - '0'); + if (max_token < n) { + max_token = n; + } + } + + if (max_token > NumberOfCapturingGroups()) { + *error = StringPrintf( + "Rewrite schema requests %d matches, but the regexp only has %d " + "parenthesized subexpressions.", + max_token, NumberOfCapturingGroups()); + return false; + } + return true; +} + +// Returns the maximum submatch needed for the rewrite to be done by Replace(). +// E.g. if rewrite == "foo \\2,\\1", returns 2. +int RE2::MaxSubmatch(const StringPiece& rewrite) { + int max = 0; + for (const char *s = rewrite.data(), *end = s + rewrite.size(); + s < end; s++) { + if (*s == '\\') { + s++; + int c = (s < end) ? *s : -1; + if (isdigit(c)) { + int n = (c - '0'); + if (n > max) + max = n; + } + } + } + return max; +} + +// Append the "rewrite" string, with backslash subsitutions from "vec", +// to string "out". +bool RE2::Rewrite(std::string* out, + const StringPiece& rewrite, + const StringPiece* vec, + int veclen) const { + for (const char *s = rewrite.data(), *end = s + rewrite.size(); + s < end; s++) { + if (*s != '\\') { + out->push_back(*s); + continue; + } + s++; + int c = (s < end) ? *s : -1; + if (isdigit(c)) { + int n = (c - '0'); + if (n >= veclen) { +#if 0 + if (options_.log_errors()) { + LOG(ERROR) << "invalid substitution \\" << n + << " from " << veclen << " groups"; + } +#endif + return false; + } + StringPiece snip = vec[n]; + if (!snip.empty()) + out->append(snip.data(), snip.size()); + } else if (c == '\\') { + out->push_back('\\'); + } else { +#if 0 + if (options_.log_errors()) + LOG(ERROR) << "invalid rewrite pattern: " << rewrite.data(); +#endif + return false; + } + } + return true; +} + +#if 0 +/***** Parsers for various types *****/ + +namespace re2_internal { + +template <> +bool Parse(const char* str, size_t n, void* dest) { + // We fail if somebody asked us to store into a non-NULL void* pointer + return (dest == NULL); +} + +template <> +bool Parse(const char* str, size_t n, std::string* dest) { + if (dest == NULL) return true; + dest->assign(str, n); + return true; +} + +template <> +bool Parse(const char* str, size_t n, StringPiece* dest) { + if (dest == NULL) return true; + *dest = StringPiece(str, n); + return true; +} + +template <> +bool Parse(const char* str, size_t n, char* dest) { + if (n != 1) return false; + if (dest == NULL) return true; + *dest = str[0]; + return true; +} + +template <> +bool Parse(const char* str, size_t n, signed char* dest) { + if (n != 1) return false; + if (dest == NULL) return true; + *dest = str[0]; + return true; +} + +template <> +bool Parse(const char* str, size_t n, unsigned char* dest) { + if (n != 1) return false; + if (dest == NULL) return true; + *dest = str[0]; + return true; +} + +// Largest number spec that we are willing to parse +static const int kMaxNumberLength = 32; + +// REQUIRES "buf" must have length at least nbuf. +// Copies "str" into "buf" and null-terminates. +// Overwrites *np with the new length. +static const char* TerminateNumber(char* buf, size_t nbuf, const char* str, + size_t* np, bool accept_spaces) { + size_t n = *np; + if (n == 0) return ""; + if (n > 0 && isspace(*str)) { + // We are less forgiving than the strtoxxx() routines and do not + // allow leading spaces. We do allow leading spaces for floats. + if (!accept_spaces) { + return ""; + } + while (n > 0 && isspace(*str)) { + n--; + str++; + } + } + + // Although buf has a fixed maximum size, we can still handle + // arbitrarily large integers correctly by omitting leading zeros. + // (Numbers that are still too long will be out of range.) + // Before deciding whether str is too long, + // remove leading zeros with s/000+/00/. + // Leaving the leading two zeros in place means that + // we don't change 0000x123 (invalid) into 0x123 (valid). + // Skip over leading - before replacing. + bool neg = false; + if (n >= 1 && str[0] == '-') { + neg = true; + n--; + str++; + } + + if (n >= 3 && str[0] == '0' && str[1] == '0') { + while (n >= 3 && str[2] == '0') { + n--; + str++; + } + } + + if (neg) { // make room in buf for - + n++; + str--; + } + + if (n > nbuf-1) return ""; + + memmove(buf, str, n); + if (neg) { + buf[0] = '-'; + } + buf[n] = '\0'; + *np = n; + return buf; +} + +template <> +bool Parse(const char* str, size_t n, float* dest) { + if (n == 0) return false; + static const int kMaxLength = 200; + char buf[kMaxLength+1]; + str = TerminateNumber(buf, sizeof buf, str, &n, true); + char* end; + errno = 0; + float r = strtof(str, &end); + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *dest = r; + return true; +} + +template <> +bool Parse(const char* str, size_t n, double* dest) { + if (n == 0) return false; + static const int kMaxLength = 200; + char buf[kMaxLength+1]; + str = TerminateNumber(buf, sizeof buf, str, &n, true); + char* end; + errno = 0; + double r = strtod(str, &end); + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *dest = r; + return true; +} + +template <> +bool Parse(const char* str, size_t n, long* dest, int radix) { + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, sizeof buf, str, &n, false); + char* end; + errno = 0; + long r = strtol(str, &end, radix); + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *dest = r; + return true; +} + +template <> +bool Parse(const char* str, size_t n, unsigned long* dest, int radix) { + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, sizeof buf, str, &n, false); + if (str[0] == '-') { + // strtoul() will silently accept negative numbers and parse + // them. This module is more strict and treats them as errors. + return false; + } + + char* end; + errno = 0; + unsigned long r = strtoul(str, &end, radix); + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *dest = r; + return true; +} + +template <> +bool Parse(const char* str, size_t n, short* dest, int radix) { + long r; + if (!Parse(str, n, &r, radix)) return false; // Could not parse + if ((short)r != r) return false; // Out of range + if (dest == NULL) return true; + *dest = (short)r; + return true; +} + +template <> +bool Parse(const char* str, size_t n, unsigned short* dest, int radix) { + unsigned long r; + if (!Parse(str, n, &r, radix)) return false; // Could not parse + if ((unsigned short)r != r) return false; // Out of range + if (dest == NULL) return true; + *dest = (unsigned short)r; + return true; +} + +template <> +bool Parse(const char* str, size_t n, int* dest, int radix) { + long r; + if (!Parse(str, n, &r, radix)) return false; // Could not parse + if ((int)r != r) return false; // Out of range + if (dest == NULL) return true; + *dest = (int)r; + return true; +} + +template <> +bool Parse(const char* str, size_t n, unsigned int* dest, int radix) { + unsigned long r; + if (!Parse(str, n, &r, radix)) return false; // Could not parse + if ((unsigned int)r != r) return false; // Out of range + if (dest == NULL) return true; + *dest = (unsigned int)r; + return true; +} + +template <> +bool Parse(const char* str, size_t n, long long* dest, int radix) { + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, sizeof buf, str, &n, false); + char* end; + errno = 0; + long long r = strtoll(str, &end, radix); + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *dest = r; + return true; +} + +template <> +bool Parse(const char* str, size_t n, unsigned long long* dest, int radix) { + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, sizeof buf, str, &n, false); + if (str[0] == '-') { + // strtoull() will silently accept negative numbers and parse + // them. This module is more strict and treats them as errors. + return false; + } + char* end; + errno = 0; + unsigned long long r = strtoull(str, &end, radix); + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *dest = r; + return true; +} + +} // namespace re2_internal +#endif + +namespace hooks { + +#ifdef RE2_HAVE_THREAD_LOCAL +thread_local const RE2* context = NULL; +#endif + +template +union Hook { + void Store(T* cb) { cb_.store(cb, std::memory_order_release); } + T* Load() const { return cb_.load(std::memory_order_acquire); } + +#if !defined(__clang__) && defined(_MSC_VER) + // Citing https://github.com/protocolbuffers/protobuf/pull/4777 as precedent, + // this is a gross hack to make std::atomic constant-initialized on MSVC. + static_assert(ATOMIC_POINTER_LOCK_FREE == 2, + "std::atomic must be always lock-free"); + T* cb_for_constinit_; +#endif + + std::atomic cb_; +}; + +template +static void DoNothing(const T&) {} + +#define DEFINE_HOOK(type, name) \ + static Hook name##_hook = {{&DoNothing}}; \ + void Set##type##Hook(type##Callback* cb) { name##_hook.Store(cb); } \ + type##Callback* Get##type##Hook() { return name##_hook.Load(); } + +DEFINE_HOOK(DFAStateCacheReset, dfa_state_cache_reset) +DEFINE_HOOK(DFASearchFailure, dfa_search_failure) + +#undef DEFINE_HOOK + +} // namespace hooks + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/re2.h b/third_party/opa/wasm/src/re2/re2/re2.h new file mode 100644 index 000000000000..1a4fff225891 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/re2.h @@ -0,0 +1,1013 @@ +// Copyright 2003-2009 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef RE2_RE2_H_ +#define RE2_RE2_H_ + +// C++ interface to the re2 regular-expression library. +// RE2 supports Perl-style regular expressions (with extensions like +// \d, \w, \s, ...). +// +// ----------------------------------------------------------------------- +// REGEXP SYNTAX: +// +// This module uses the re2 library and hence supports +// its syntax for regular expressions, which is similar to Perl's with +// some of the more complicated things thrown away. In particular, +// backreferences and generalized assertions are not available, nor is \Z. +// +// See https://github.com/google/re2/wiki/Syntax for the syntax +// supported by RE2, and a comparison with PCRE and PERL regexps. +// +// For those not familiar with Perl's regular expressions, +// here are some examples of the most commonly used extensions: +// +// "hello (\\w+) world" -- \w matches a "word" character +// "version (\\d+)" -- \d matches a digit +// "hello\\s+world" -- \s matches any whitespace character +// "\\b(\\w+)\\b" -- \b matches non-empty string at word boundary +// "(?i)hello" -- (?i) turns on case-insensitive matching +// "/\\*(.*?)\\*/" -- .*? matches . minimum no. of times possible +// +// The double backslashes are needed when writing C++ string literals. +// However, they should NOT be used when writing C++11 raw string literals: +// +// R"(hello (\w+) world)" -- \w matches a "word" character +// R"(version (\d+))" -- \d matches a digit +// R"(hello\s+world)" -- \s matches any whitespace character +// R"(\b(\w+)\b)" -- \b matches non-empty string at word boundary +// R"((?i)hello)" -- (?i) turns on case-insensitive matching +// R"(/\*(.*?)\*/)" -- .*? matches . minimum no. of times possible +// +// When using UTF-8 encoding, case-insensitive matching will perform +// simple case folding, not full case folding. +// +// ----------------------------------------------------------------------- +// MATCHING INTERFACE: +// +// The "FullMatch" operation checks that supplied text matches a +// supplied pattern exactly. +// +// Example: successful match +// CHECK(RE2::FullMatch("hello", "h.*o")); +// +// Example: unsuccessful match (requires full match): +// CHECK(!RE2::FullMatch("hello", "e")); +// +// ----------------------------------------------------------------------- +// UTF-8 AND THE MATCHING INTERFACE: +// +// By default, the pattern and input text are interpreted as UTF-8. +// The RE2::Latin1 option causes them to be interpreted as Latin-1. +// +// Example: +// CHECK(RE2::FullMatch(utf8_string, RE2(utf8_pattern))); +// CHECK(RE2::FullMatch(latin1_string, RE2(latin1_pattern, RE2::Latin1))); +// +// ----------------------------------------------------------------------- +// MATCHING WITH SUBSTRING EXTRACTION: +// +// You can supply extra pointer arguments to extract matched substrings. +// On match failure, none of the pointees will have been modified. +// On match success, the substrings will be converted (as necessary) and +// their values will be assigned to their pointees until all conversions +// have succeeded or one conversion has failed. +// On conversion failure, the pointees will be in an indeterminate state +// because the caller has no way of knowing which conversion failed. +// However, conversion cannot fail for types like string and StringPiece +// that do not inspect the substring contents. Hence, in the common case +// where all of the pointees are of such types, failure is always due to +// match failure and thus none of the pointees will have been modified. +// +// Example: extracts "ruby" into "s" and 1234 into "i" +// int i; +// std::string s; +// CHECK(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", &s, &i)); +// +// Example: fails because string cannot be stored in integer +// CHECK(!RE2::FullMatch("ruby", "(.*)", &i)); +// +// Example: fails because there aren't enough sub-patterns +// CHECK(!RE2::FullMatch("ruby:1234", "\\w+:\\d+", &s)); +// +// Example: does not try to extract any extra sub-patterns +// CHECK(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", &s)); +// +// Example: does not try to extract into NULL +// CHECK(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", NULL, &i)); +// +// Example: integer overflow causes failure +// CHECK(!RE2::FullMatch("ruby:1234567891234", "\\w+:(\\d+)", &i)); +// +// NOTE(rsc): Asking for substrings slows successful matches quite a bit. +// This may get a little faster in the future, but right now is slower +// than PCRE. On the other hand, failed matches run *very* fast (faster +// than PCRE), as do matches without substring extraction. +// +// ----------------------------------------------------------------------- +// PARTIAL MATCHES +// +// You can use the "PartialMatch" operation when you want the pattern +// to match any substring of the text. +// +// Example: simple search for a string: +// CHECK(RE2::PartialMatch("hello", "ell")); +// +// Example: find first number in a string +// int number; +// CHECK(RE2::PartialMatch("x*100 + 20", "(\\d+)", &number)); +// CHECK_EQ(number, 100); +// +// ----------------------------------------------------------------------- +// PRE-COMPILED REGULAR EXPRESSIONS +// +// RE2 makes it easy to use any string as a regular expression, without +// requiring a separate compilation step. +// +// If speed is of the essence, you can create a pre-compiled "RE2" +// object from the pattern and use it multiple times. If you do so, +// you can typically parse text faster than with sscanf. +// +// Example: precompile pattern for faster matching: +// RE2 pattern("h.*o"); +// while (ReadLine(&str)) { +// if (RE2::FullMatch(str, pattern)) ...; +// } +// +// ----------------------------------------------------------------------- +// SCANNING TEXT INCREMENTALLY +// +// The "Consume" operation may be useful if you want to repeatedly +// match regular expressions at the front of a string and skip over +// them as they match. This requires use of the "StringPiece" type, +// which represents a sub-range of a real string. +// +// Example: read lines of the form "var = value" from a string. +// std::string contents = ...; // Fill string somehow +// StringPiece input(contents); // Wrap a StringPiece around it +// +// std::string var; +// int value; +// while (RE2::Consume(&input, "(\\w+) = (\\d+)\n", &var, &value)) { +// ...; +// } +// +// Each successful call to "Consume" will set "var/value", and also +// advance "input" so it points past the matched text. Note that if the +// regular expression matches an empty string, input will advance +// by 0 bytes. If the regular expression being used might match +// an empty string, the loop body must check for this case and either +// advance the string or break out of the loop. +// +// The "FindAndConsume" operation is similar to "Consume" but does not +// anchor your match at the beginning of the string. For example, you +// could extract all words from a string by repeatedly calling +// RE2::FindAndConsume(&input, "(\\w+)", &word) +// +// ----------------------------------------------------------------------- +// USING VARIABLE NUMBER OF ARGUMENTS +// +// The above operations require you to know the number of arguments +// when you write the code. This is not always possible or easy (for +// example, the regular expression may be calculated at run time). +// You can use the "N" version of the operations when the number of +// match arguments are determined at run time. +// +// Example: +// const RE2::Arg* args[10]; +// int n; +// // ... populate args with pointers to RE2::Arg values ... +// // ... set n to the number of RE2::Arg objects ... +// bool match = RE2::FullMatchN(input, pattern, args, n); +// +// The last statement is equivalent to +// +// bool match = RE2::FullMatch(input, pattern, +// *args[0], *args[1], ..., *args[n - 1]); +// +// ----------------------------------------------------------------------- +// PARSING HEX/OCTAL/C-RADIX NUMBERS +// +// By default, if you pass a pointer to a numeric value, the +// corresponding text is interpreted as a base-10 number. You can +// instead wrap the pointer with a call to one of the operators Hex(), +// Octal(), or CRadix() to interpret the text in another base. The +// CRadix operator interprets C-style "0" (base-8) and "0x" (base-16) +// prefixes, but defaults to base-10. +// +// Example: +// int a, b, c, d; +// CHECK(RE2::FullMatch("100 40 0100 0x40", "(.*) (.*) (.*) (.*)", +// RE2::Octal(&a), RE2::Hex(&b), RE2::CRadix(&c), RE2::CRadix(&d)); +// will leave 64 in a, b, c, and d. + +#include +#include +//#include +#include +#include +#include +#include +#include + +#if defined(__APPLE__) +#include +#endif + +#include "re2/stringpiece.h" + +namespace re2 { +class Prog; +class Regexp; +} // namespace re2 + +namespace re2 { + +// Interface for regular expression matching. Also corresponds to a +// pre-compiled regular expression. An "RE2" object is safe for +// concurrent use by multiple threads. +class RE2 { + public: + // We convert user-passed pointers into special Arg objects + class Arg; + class Options; + + // Defined in set.h. + class Set; + + enum ErrorCode { + NoError = 0, + + // Unexpected error + ErrorInternal, + + // Parse errors + ErrorBadEscape, // bad escape sequence + ErrorBadCharClass, // bad character class + ErrorBadCharRange, // bad character class range + ErrorMissingBracket, // missing closing ] + ErrorMissingParen, // missing closing ) + ErrorUnexpectedParen, // unexpected closing ) + ErrorTrailingBackslash, // trailing \ at end of regexp + ErrorRepeatArgument, // repeat argument missing, e.g. "*" + ErrorRepeatSize, // bad repetition argument + ErrorRepeatOp, // bad repetition operator + ErrorBadPerlOp, // bad perl operator + ErrorBadUTF8, // invalid UTF-8 in regexp + ErrorBadNamedCapture, // bad named capture group + ErrorPatternTooLarge // pattern too large (compile failed) + }; + + // Predefined common options. + // If you need more complicated things, instantiate + // an Option class, possibly passing one of these to + // the Option constructor, change the settings, and pass that + // Option class to the RE2 constructor. + enum CannedOptions { + DefaultOptions = 0, + Latin1, // treat input as Latin-1 (default UTF-8) + POSIX, // POSIX syntax, leftmost-longest match + Quiet // do not log about regexp parse errors + }; + + // Need to have the const char* and const std::string& forms for implicit + // conversions when passing string literals to FullMatch and PartialMatch. + // Otherwise the StringPiece form would be sufficient. +#ifndef SWIG + RE2(const char* pattern); + RE2(const std::string& pattern); +#endif + RE2(const StringPiece& pattern); + RE2(const StringPiece& pattern, const Options& options); + ~RE2(); + + // Returns whether RE2 was created properly. + bool ok() const { return error_code() == NoError; } + + // The string specification for this RE2. E.g. + // RE2 re("ab*c?d+"); + // re.pattern(); // "ab*c?d+" + const std::string& pattern() const { return pattern_; } + + // If RE2 could not be created properly, returns an error string. + // Else returns the empty string. + const std::string& error() const { return *error_; } + + // If RE2 could not be created properly, returns an error code. + // Else returns RE2::NoError (== 0). + ErrorCode error_code() const { return error_code_; } + + // If RE2 could not be created properly, returns the offending + // portion of the regexp. + const std::string& error_arg() const { return error_arg_; } + + // Returns the program size, a very approximate measure of a regexp's "cost". + // Larger numbers are more expensive than smaller numbers. + int ProgramSize() const; + int ReverseProgramSize() const; + + // If histogram is not null, outputs the program fanout + // as a histogram bucketed by powers of 2. + // Returns the number of the largest non-empty bucket. + int ProgramFanout(std::vector* histogram) const; + int ReverseProgramFanout(std::vector* histogram) const; + + // Returns the underlying Regexp; not for general use. + // Returns entire_regexp_ so that callers don't need + // to know about prefix_ and prefix_foldcase_. + re2::Regexp* Regexp() const { return entire_regexp_; } + + /***** The array-based matching interface ******/ + + // The functions here have names ending in 'N' and are used to implement + // the functions whose names are the prefix before the 'N'. It is sometimes + // useful to invoke them directly, but the syntax is awkward, so the 'N'-less + // versions should be preferred. + static bool FullMatchN(const StringPiece& text, const RE2& re, + const Arg* const args[], int n); + static bool PartialMatchN(const StringPiece& text, const RE2& re, + const Arg* const args[], int n); + static bool ConsumeN(StringPiece* input, const RE2& re, + const Arg* const args[], int n); + static bool FindAndConsumeN(StringPiece* input, const RE2& re, + const Arg* const args[], int n); + +#ifndef SWIG + private: + template + static inline bool Apply(F f, SP sp, const RE2& re) { + return f(sp, re, NULL, 0); + } + + template + static inline bool Apply(F f, SP sp, const RE2& re, const A&... a) { + const Arg* const args[] = {&a...}; + const int n = sizeof...(a); + return f(sp, re, args, n); + } + + public: + // In order to allow FullMatch() et al. to be called with a varying number + // of arguments of varying types, we use two layers of variadic templates. + // The first layer constructs the temporary Arg objects. The second layer + // (above) constructs the array of pointers to the temporary Arg objects. + + /***** The useful part: the matching interface *****/ + + // Matches "text" against "re". If pointer arguments are + // supplied, copies matched sub-patterns into them. + // + // You can pass in a "const char*" or a "std::string" for "text". + // You can pass in a "const char*" or a "std::string" or a "RE2" for "re". + // + // The provided pointer arguments can be pointers to any scalar numeric + // type, or one of: + // std::string (matched piece is copied to string) + // StringPiece (StringPiece is mutated to point to matched piece) + // T (where "bool T::ParseFrom(const char*, size_t)" exists) + // (void*)NULL (the corresponding matched sub-pattern is not copied) + // + // Returns true iff all of the following conditions are satisfied: + // a. "text" matches "re" fully - from the beginning to the end of "text". + // b. The number of matched sub-patterns is >= number of supplied pointers. + // c. The "i"th argument has a suitable type for holding the + // string captured as the "i"th sub-pattern. If you pass in + // NULL for the "i"th argument, or pass fewer arguments than + // number of sub-patterns, the "i"th captured sub-pattern is + // ignored. + // + // CAVEAT: An optional sub-pattern that does not exist in the + // matched string is assigned the empty string. Therefore, the + // following will return false (because the empty string is not a + // valid number): + // int number; + // RE2::FullMatch("abc", "[a-z]+(\\d+)?", &number); + template + static bool FullMatch(const StringPiece& text, const RE2& re, A&&... a) { + return Apply(FullMatchN, text, re, Arg(std::forward(a))...); + } + + // Like FullMatch(), except that "re" is allowed to match a substring + // of "text". + // + // Returns true iff all of the following conditions are satisfied: + // a. "text" matches "re" partially - for some substring of "text". + // b. The number of matched sub-patterns is >= number of supplied pointers. + // c. The "i"th argument has a suitable type for holding the + // string captured as the "i"th sub-pattern. If you pass in + // NULL for the "i"th argument, or pass fewer arguments than + // number of sub-patterns, the "i"th captured sub-pattern is + // ignored. + template + static bool PartialMatch(const StringPiece& text, const RE2& re, A&&... a) { + return Apply(PartialMatchN, text, re, Arg(std::forward(a))...); + } + + // Like FullMatch() and PartialMatch(), except that "re" has to match + // a prefix of the text, and "input" is advanced past the matched + // text. Note: "input" is modified iff this routine returns true + // and "re" matched a non-empty substring of "input". + // + // Returns true iff all of the following conditions are satisfied: + // a. "input" matches "re" partially - for some prefix of "input". + // b. The number of matched sub-patterns is >= number of supplied pointers. + // c. The "i"th argument has a suitable type for holding the + // string captured as the "i"th sub-pattern. If you pass in + // NULL for the "i"th argument, or pass fewer arguments than + // number of sub-patterns, the "i"th captured sub-pattern is + // ignored. + template + static bool Consume(StringPiece* input, const RE2& re, A&&... a) { + return Apply(ConsumeN, input, re, Arg(std::forward(a))...); + } + + // Like Consume(), but does not anchor the match at the beginning of + // the text. That is, "re" need not start its match at the beginning + // of "input". For example, "FindAndConsume(s, "(\\w+)", &word)" finds + // the next word in "s" and stores it in "word". + // + // Returns true iff all of the following conditions are satisfied: + // a. "input" matches "re" partially - for some substring of "input". + // b. The number of matched sub-patterns is >= number of supplied pointers. + // c. The "i"th argument has a suitable type for holding the + // string captured as the "i"th sub-pattern. If you pass in + // NULL for the "i"th argument, or pass fewer arguments than + // number of sub-patterns, the "i"th captured sub-pattern is + // ignored. + template + static bool FindAndConsume(StringPiece* input, const RE2& re, A&&... a) { + return Apply(FindAndConsumeN, input, re, Arg(std::forward(a))...); + } +#endif + + // Replace the first match of "re" in "str" with "rewrite". + // Within "rewrite", backslash-escaped digits (\1 to \9) can be + // used to insert text matching corresponding parenthesized group + // from the pattern. \0 in "rewrite" refers to the entire matching + // text. E.g., + // + // std::string s = "yabba dabba doo"; + // CHECK(RE2::Replace(&s, "b+", "d")); + // + // will leave "s" containing "yada dabba doo" + // + // Returns true if the pattern matches and a replacement occurs, + // false otherwise. + static bool Replace(std::string* str, + const RE2& re, + const StringPiece& rewrite); + + // Like Replace(), except replaces successive non-overlapping occurrences + // of the pattern in the string with the rewrite. E.g. + // + // std::string s = "yabba dabba doo"; + // CHECK(RE2::GlobalReplace(&s, "b+", "d")); + // + // will leave "s" containing "yada dada doo" + // Replacements are not subject to re-matching. + // + // Because GlobalReplace only replaces non-overlapping matches, + // replacing "ana" within "banana" makes only one replacement, not two. + // + // Returns the number of replacements made. + static int GlobalReplace(std::string* str, + const RE2& re, + const StringPiece& rewrite); + + // Like Replace, except that if the pattern matches, "rewrite" + // is copied into "out" with substitutions. The non-matching + // portions of "text" are ignored. + // + // Returns true iff a match occurred and the extraction happened + // successfully; if no match occurs, the string is left unaffected. + // + // REQUIRES: "text" must not alias any part of "*out". + static bool Extract(const StringPiece& text, + const RE2& re, + const StringPiece& rewrite, + std::string* out); + + // Escapes all potentially meaningful regexp characters in + // 'unquoted'. The returned string, used as a regular expression, + // will match exactly the original string. For example, + // 1.5-2.0? + // may become: + // 1\.5\-2\.0\? + static std::string QuoteMeta(const StringPiece& unquoted); + + // Computes range for any strings matching regexp. The min and max can in + // some cases be arbitrarily precise, so the caller gets to specify the + // maximum desired length of string returned. + // + // Assuming PossibleMatchRange(&min, &max, N) returns successfully, any + // string s that is an anchored match for this regexp satisfies + // min <= s && s <= max. + // + // Note that PossibleMatchRange() will only consider the first copy of an + // infinitely repeated element (i.e., any regexp element followed by a '*' or + // '+' operator). Regexps with "{N}" constructions are not affected, as those + // do not compile down to infinite repetitions. + // + // Returns true on success, false on error. + bool PossibleMatchRange(std::string* min, std::string* max, + int maxlen) const; + + // Generic matching interface + + // Type of match. + enum Anchor { + UNANCHORED, // No anchoring + ANCHOR_START, // Anchor at start only + ANCHOR_BOTH // Anchor at start and end + }; + + // Return the number of capturing subpatterns, or -1 if the + // regexp wasn't valid on construction. The overall match ($0) + // does not count: if the regexp is "(a)(b)", returns 2. + int NumberOfCapturingGroups() const { return num_captures_; } + + // Return a map from names to capturing indices. + // The map records the index of the leftmost group + // with the given name. + // Only valid until the re is deleted. + const std::map& NamedCapturingGroups() const; + + // Return a map from capturing indices to names. + // The map has no entries for unnamed groups. + // Only valid until the re is deleted. + const std::map& CapturingGroupNames() const; + + // General matching routine. + // Match against text starting at offset startpos + // and stopping the search at offset endpos. + // Returns true if match found, false if not. + // On a successful match, fills in submatch[] (up to nsubmatch entries) + // with information about submatches. + // I.e. matching RE2("(foo)|(bar)baz") on "barbazbla" will return true, with + // submatch[0] = "barbaz", submatch[1].data() = NULL, submatch[2] = "bar", + // submatch[3].data() = NULL, ..., up to submatch[nsubmatch-1].data() = NULL. + // Caveat: submatch[] may be clobbered even on match failure. + // + // Don't ask for more match information than you will use: + // runs much faster with nsubmatch == 1 than nsubmatch > 1, and + // runs even faster if nsubmatch == 0. + // Doesn't make sense to use nsubmatch > 1 + NumberOfCapturingGroups(), + // but will be handled correctly. + // + // Passing text == StringPiece(NULL, 0) will be handled like any other + // empty string, but note that on return, it will not be possible to tell + // whether submatch i matched the empty string or did not match: + // either way, submatch[i].data() == NULL. + bool Match(const StringPiece& text, + size_t startpos, + size_t endpos, + Anchor re_anchor, + StringPiece* submatch, + int nsubmatch) const; + + // Check that the given rewrite string is suitable for use with this + // regular expression. It checks that: + // * The regular expression has enough parenthesized subexpressions + // to satisfy all of the \N tokens in rewrite + // * The rewrite string doesn't have any syntax errors. E.g., + // '\' followed by anything other than a digit or '\'. + // A true return value guarantees that Replace() and Extract() won't + // fail because of a bad rewrite string. + bool CheckRewriteString(const StringPiece& rewrite, + std::string* error) const; + + // Returns the maximum submatch needed for the rewrite to be done by + // Replace(). E.g. if rewrite == "foo \\2,\\1", returns 2. + static int MaxSubmatch(const StringPiece& rewrite); + + // Append the "rewrite" string, with backslash subsitutions from "vec", + // to string "out". + // Returns true on success. This method can fail because of a malformed + // rewrite string. CheckRewriteString guarantees that the rewrite will + // be successful. + bool Rewrite(std::string* out, + const StringPiece& rewrite, + const StringPiece* vec, + int veclen) const; + + // Constructor options + class Options { + public: + // The options are (defaults in parentheses): + // + // utf8 (true) text and pattern are UTF-8; otherwise Latin-1 + // posix_syntax (false) restrict regexps to POSIX egrep syntax + // longest_match (false) search for longest match, not first match + // log_errors (true) log syntax and execution errors to ERROR + // max_mem (see below) approx. max memory footprint of RE2 + // literal (false) interpret string as literal, not regexp + // never_nl (false) never match \n, even if it is in regexp + // dot_nl (false) dot matches everything including new line + // never_capture (false) parse all parens as non-capturing + // case_sensitive (true) match is case-sensitive (regexp can override + // with (?i) unless in posix_syntax mode) + // + // The following options are only consulted when posix_syntax == true. + // When posix_syntax == false, these features are always enabled and + // cannot be turned off; to perform multi-line matching in that case, + // begin the regexp with (?m). + // perl_classes (false) allow Perl's \d \s \w \D \S \W + // word_boundary (false) allow Perl's \b \B (word boundary and not) + // one_line (false) ^ and $ only match beginning and end of text + // + // The max_mem option controls how much memory can be used + // to hold the compiled form of the regexp (the Prog) and + // its cached DFA graphs. Code Search placed limits on the number + // of Prog instructions and DFA states: 10,000 for both. + // In RE2, those limits would translate to about 240 KB per Prog + // and perhaps 2.5 MB per DFA (DFA state sizes vary by regexp; RE2 does a + // better job of keeping them small than Code Search did). + // Each RE2 has two Progs (one forward, one reverse), and each Prog + // can have two DFAs (one first match, one longest match). + // That makes 4 DFAs: + // + // forward, first-match - used for UNANCHORED or ANCHOR_START searches + // if opt.longest_match() == false + // forward, longest-match - used for all ANCHOR_BOTH searches, + // and the other two kinds if + // opt.longest_match() == true + // reverse, first-match - never used + // reverse, longest-match - used as second phase for unanchored searches + // + // The RE2 memory budget is statically divided between the two + // Progs and then the DFAs: two thirds to the forward Prog + // and one third to the reverse Prog. The forward Prog gives half + // of what it has left over to each of its DFAs. The reverse Prog + // gives it all to its longest-match DFA. + // + // Once a DFA fills its budget, it flushes its cache and starts over. + // If this happens too often, RE2 falls back on the NFA implementation. + + // For now, make the default budget something close to Code Search. + static const int kDefaultMaxMem = 8<<20; + + enum Encoding { + EncodingUTF8 = 1, + EncodingLatin1 + }; + + Options() : + encoding_(EncodingUTF8), + posix_syntax_(false), + longest_match_(false), + log_errors_(true), + max_mem_(kDefaultMaxMem), + literal_(false), + never_nl_(false), + dot_nl_(false), + never_capture_(false), + case_sensitive_(true), + perl_classes_(false), + word_boundary_(false), + one_line_(false) { + } + + /*implicit*/ Options(CannedOptions); + + Encoding encoding() const { return encoding_; } + void set_encoding(Encoding encoding) { encoding_ = encoding; } + + bool posix_syntax() const { return posix_syntax_; } + void set_posix_syntax(bool b) { posix_syntax_ = b; } + + bool longest_match() const { return longest_match_; } + void set_longest_match(bool b) { longest_match_ = b; } + + bool log_errors() const { return log_errors_; } + void set_log_errors(bool b) { log_errors_ = b; } + + int64_t max_mem() const { return max_mem_; } + void set_max_mem(int64_t m) { max_mem_ = m; } + + bool literal() const { return literal_; } + void set_literal(bool b) { literal_ = b; } + + bool never_nl() const { return never_nl_; } + void set_never_nl(bool b) { never_nl_ = b; } + + bool dot_nl() const { return dot_nl_; } + void set_dot_nl(bool b) { dot_nl_ = b; } + + bool never_capture() const { return never_capture_; } + void set_never_capture(bool b) { never_capture_ = b; } + + bool case_sensitive() const { return case_sensitive_; } + void set_case_sensitive(bool b) { case_sensitive_ = b; } + + bool perl_classes() const { return perl_classes_; } + void set_perl_classes(bool b) { perl_classes_ = b; } + + bool word_boundary() const { return word_boundary_; } + void set_word_boundary(bool b) { word_boundary_ = b; } + + bool one_line() const { return one_line_; } + void set_one_line(bool b) { one_line_ = b; } + + void Copy(const Options& src) { + *this = src; + } + + int ParseFlags() const; + + private: + Encoding encoding_; + bool posix_syntax_; + bool longest_match_; + bool log_errors_; + int64_t max_mem_; + bool literal_; + bool never_nl_; + bool dot_nl_; + bool never_capture_; + bool case_sensitive_; + bool perl_classes_; + bool word_boundary_; + bool one_line_; + }; + + // Returns the options set in the constructor. + const Options& options() const { return options_; } + + // Argument converters; see below. + template + static Arg CRadix(T* ptr); + template + static Arg Hex(T* ptr); + template + static Arg Octal(T* ptr); + + private: + void Init(const StringPiece& pattern, const Options& options); + + bool DoMatch(const StringPiece& text, + Anchor re_anchor, + size_t* consumed, + const Arg* const args[], + int n) const; + + re2::Prog* ReverseProg() const; + + std::string pattern_; // string regular expression + Options options_; // option flags + re2::Regexp* entire_regexp_; // parsed regular expression + const std::string* error_; // error indicator (or points to empty string) + ErrorCode error_code_; // error code + std::string error_arg_; // fragment of regexp showing error + std::string prefix_; // required prefix (before suffix_regexp_) + bool prefix_foldcase_; // prefix_ is ASCII case-insensitive + re2::Regexp* suffix_regexp_; // parsed regular expression, prefix_ removed + re2::Prog* prog_; // compiled program for regexp + int num_captures_; // number of capturing groups + bool is_one_pass_; // can use prog_->SearchOnePass? + + // Reverse Prog for DFA execution only + mutable re2::Prog* rprog_; + // Map from capture names to indices + mutable const std::map* named_groups_; + // Map from capture indices to names + mutable const std::map* group_names_; + + mutable std::once_flag rprog_once_; + mutable std::once_flag named_groups_once_; + mutable std::once_flag group_names_once_; + + RE2(const RE2&) = delete; + RE2& operator=(const RE2&) = delete; +}; + +/***** Implementation details *****/ + +namespace re2_internal { + +// Types for which the 3-ary Parse() function template has specializations. +template struct Parse3ary : public std::false_type {}; +template <> struct Parse3ary : public std::true_type {}; +template <> struct Parse3ary : public std::true_type {}; +template <> struct Parse3ary : public std::true_type {}; +template <> struct Parse3ary : public std::true_type {}; +template <> struct Parse3ary : public std::true_type {}; +template <> struct Parse3ary : public std::true_type {}; +template <> struct Parse3ary : public std::true_type {}; +template <> struct Parse3ary : public std::true_type {}; + +template +bool Parse(const char* str, size_t n, T* dest); + +// Types for which the 4-ary Parse() function template has specializations. +template struct Parse4ary : public std::false_type {}; +template <> struct Parse4ary : public std::true_type {}; +template <> struct Parse4ary : public std::true_type {}; +template <> struct Parse4ary : public std::true_type {}; +template <> struct Parse4ary : public std::true_type {}; +template <> struct Parse4ary : public std::true_type {}; +template <> struct Parse4ary : public std::true_type {}; +template <> struct Parse4ary : public std::true_type {}; +template <> struct Parse4ary : public std::true_type {}; + +template +bool Parse(const char* str, size_t n, T* dest, int radix); + +} // namespace re2_internal + +class RE2::Arg { + private: + template + using CanParse3ary = typename std::enable_if< + re2_internal::Parse3ary::value, + int>::type; + + template + using CanParse4ary = typename std::enable_if< + re2_internal::Parse4ary::value, + int>::type; + +#if !defined(_MSC_VER) + template + using CanParseFrom = typename std::enable_if< + std::is_member_function_pointer::value, + int>::type; +#endif + + public: + Arg() : Arg(nullptr) {} + Arg(std::nullptr_t ptr) : arg_(ptr), parser_(DoNothing) {} + + template = 0> + Arg(T* ptr) : arg_(ptr), parser_(DoParse3ary) {} + + template = 0> + Arg(T* ptr) : arg_(ptr), parser_(DoParse4ary) {} + +#if !defined(_MSC_VER) + template = 0> + Arg(T* ptr) : arg_(ptr), parser_(DoParseFrom) {} +#endif + + typedef bool (*Parser)(const char* str, size_t n, void* dest); + + template + Arg(T* ptr, Parser parser) : arg_(ptr), parser_(parser) {} + + bool Parse(const char* str, size_t n) const { + return (*parser_)(str, n, arg_); + } + + private: + static bool DoNothing(const char* /*str*/, size_t /*n*/, void* /*dest*/) { + return true; + } + + template + static bool DoParse3ary(const char* str, size_t n, void* dest) { + return re2_internal::Parse(str, n, reinterpret_cast(dest)); + } + + template + static bool DoParse4ary(const char* str, size_t n, void* dest) { + return re2_internal::Parse(str, n, reinterpret_cast(dest), 10); + } + +#if !defined(_MSC_VER) + template + static bool DoParseFrom(const char* str, size_t n, void* dest) { + if (dest == NULL) return true; + return reinterpret_cast(dest)->ParseFrom(str, n); + } +#endif + + void* arg_; + Parser parser_; +}; + +template +inline RE2::Arg RE2::CRadix(T* ptr) { + return RE2::Arg(ptr, [](const char* str, size_t n, void* dest) -> bool { + return re2_internal::Parse(str, n, reinterpret_cast(dest), 0); + }); +} + +template +inline RE2::Arg RE2::Hex(T* ptr) { + return RE2::Arg(ptr, [](const char* str, size_t n, void* dest) -> bool { + return re2_internal::Parse(str, n, reinterpret_cast(dest), 16); + }); +} + +template +inline RE2::Arg RE2::Octal(T* ptr) { + return RE2::Arg(ptr, [](const char* str, size_t n, void* dest) -> bool { + return re2_internal::Parse(str, n, reinterpret_cast(dest), 8); + }); +} + +#ifndef SWIG +// Silence warnings about missing initializers for members of LazyRE2. +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 6 +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +// Helper for writing global or static RE2s safely. +// Write +// static LazyRE2 re = {".*"}; +// and then use *re instead of writing +// static RE2 re(".*"); +// The former is more careful about multithreaded +// situations than the latter. +// +// N.B. This class never deletes the RE2 object that +// it constructs: that's a feature, so that it can be used +// for global and function static variables. +class LazyRE2 { + private: + struct NoArg {}; + + public: + typedef RE2 element_type; // support std::pointer_traits + + // Constructor omitted to preserve braced initialization in C++98. + + // Pretend to be a pointer to Type (never NULL due to on-demand creation): + RE2& operator*() const { return *get(); } + RE2* operator->() const { return get(); } + + // Named accessor/initializer: + RE2* get() const { + std::call_once(once_, &LazyRE2::Init, this); + return ptr_; + } + + // All data fields must be public to support {"foo"} initialization. + const char* pattern_; + RE2::CannedOptions options_; + NoArg barrier_against_excess_initializers_; + + mutable RE2* ptr_; + mutable std::once_flag once_; + + private: + static void Init(const LazyRE2* lazy_re2) { + lazy_re2->ptr_ = new RE2(lazy_re2->pattern_, lazy_re2->options_); + } + + void operator=(const LazyRE2&); // disallowed +}; +#endif + +namespace hooks { + +// Most platforms support thread_local. Older versions of iOS don't support +// thread_local, but for the sake of brevity, we lump together all versions +// of Apple platforms that aren't macOS. If an iOS application really needs +// the context pointee someday, we can get more specific then... +#define RE2_HAVE_THREAD_LOCAL +#if defined(__APPLE__) && !TARGET_OS_OSX +#undef RE2_HAVE_THREAD_LOCAL +#endif + +// A hook must not make any assumptions regarding the lifetime of the context +// pointee beyond the current invocation of the hook. Pointers and references +// obtained via the context pointee should be considered invalidated when the +// hook returns. Hence, any data about the context pointee (e.g. its pattern) +// would have to be copied in order for it to be kept for an indefinite time. +// +// A hook must not use RE2 for matching. Control flow reentering RE2::Match() +// could result in infinite mutual recursion. To discourage that possibility, +// RE2 will not maintain the context pointer correctly when used in that way. +#ifdef RE2_HAVE_THREAD_LOCAL +extern thread_local const RE2* context; +#endif + +struct DFAStateCacheReset { + int64_t state_budget; + size_t state_cache_size; +}; + +struct DFASearchFailure { + // Nothing yet... +}; + +#define DECLARE_HOOK(type) \ + using type##Callback = void(const type&); \ + void Set##type##Hook(type##Callback* cb); \ + type##Callback* Get##type##Hook(); + +DECLARE_HOOK(DFAStateCacheReset) +DECLARE_HOOK(DFASearchFailure) + +#undef DECLARE_HOOK + +} // namespace hooks + +} // namespace re2 + +using re2::RE2; +using re2::LazyRE2; + +#endif // RE2_RE2_H_ diff --git a/third_party/opa/wasm/src/re2/re2/regexp.cc b/third_party/opa/wasm/src/re2/re2/regexp.cc new file mode 100644 index 000000000000..a614cbf8e522 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/regexp.cc @@ -0,0 +1,993 @@ +// Copyright 2006 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Regular expression representation. +// Tested by parse_test.cc + +#include "re2/regexp.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/util.h" +#include "util/logging.h" +#include "util/mutex.h" +#include "util/utf.h" +#include "re2/pod_array.h" +#include "re2/stringpiece.h" +#include "re2/walker-inl.h" + +namespace re2 { + +// Constructor. Allocates vectors as appropriate for operator. +Regexp::Regexp(RegexpOp op, ParseFlags parse_flags) + : op_(static_cast(op)), + simple_(false), + parse_flags_(static_cast(parse_flags)), + ref_(1), + nsub_(0), + down_(NULL) { + subone_ = NULL; + memset(the_union_, 0, sizeof the_union_); +} + +// Destructor. Assumes already cleaned up children. +// Private: use Decref() instead of delete to destroy Regexps. +// Can't call Decref on the sub-Regexps here because +// that could cause arbitrarily deep recursion, so +// required Decref() to have handled them for us. +Regexp::~Regexp() { +#if 0 + if (nsub_ > 0) + LOG(DFATAL) << "Regexp not destroyed."; +#endif + + switch (op_) { + default: + break; + case kRegexpCapture: + delete name_; + break; + case kRegexpLiteralString: + delete[] runes_; + break; + case kRegexpCharClass: + if (cc_) + cc_->Delete(); + delete ccb_; + break; + } +} + +// If it's possible to destroy this regexp without recurring, +// do so and return true. Else return false. +bool Regexp::QuickDestroy() { + if (nsub_ == 0) { + delete this; + return true; + } + return false; +} + +// Lazily allocated. +static Mutex* ref_mutex; +static std::map* ref_map; + +int Regexp::Ref() { + if (ref_ < kMaxRef) + return ref_; + + MutexLock l(ref_mutex); + return (*ref_map)[this]; +} + +// Increments reference count, returns object as convenience. +Regexp* Regexp::Incref() { + if (ref_ >= kMaxRef-1) { + static std::once_flag ref_once; + std::call_once(ref_once, []() { + ref_mutex = new Mutex; + ref_map = new std::map; + }); + + // Store ref count in overflow map. + MutexLock l(ref_mutex); + if (ref_ == kMaxRef) { + // already overflowed + (*ref_map)[this]++; + } else { + // overflowing now + (*ref_map)[this] = kMaxRef; + ref_ = kMaxRef; + } + return this; + } + + ref_++; + return this; +} + +// Decrements reference count and deletes this object if count reaches 0. +void Regexp::Decref() { + if (ref_ == kMaxRef) { + // Ref count is stored in overflow map. + MutexLock l(ref_mutex); + int r = (*ref_map)[this] - 1; + if (r < kMaxRef) { + ref_ = static_cast(r); + ref_map->erase(this); + } else { + (*ref_map)[this] = r; + } + return; + } + ref_--; + if (ref_ == 0) + Destroy(); +} + +// Deletes this object; ref count has count reached 0. +void Regexp::Destroy() { + if (QuickDestroy()) + return; + + // Handle recursive Destroy with explicit stack + // to avoid arbitrarily deep recursion on process stack [sigh]. + down_ = NULL; + Regexp* stack = this; + while (stack != NULL) { + Regexp* re = stack; + stack = re->down_; +#if 0 + if (re->ref_ != 0) + LOG(DFATAL) << "Bad reference count " << re->ref_; +#endif + if (re->nsub_ > 0) { + Regexp** subs = re->sub(); + for (int i = 0; i < re->nsub_; i++) { + Regexp* sub = subs[i]; + if (sub == NULL) + continue; + if (sub->ref_ == kMaxRef) + sub->Decref(); + else + --sub->ref_; + if (sub->ref_ == 0 && !sub->QuickDestroy()) { + sub->down_ = stack; + stack = sub; + } + } + if (re->nsub_ > 1) + delete[] subs; + re->nsub_ = 0; + } + delete re; + } +} + +void Regexp::AddRuneToString(Rune r) { + DCHECK(op_ == kRegexpLiteralString); + if (nrunes_ == 0) { + // start with 8 + runes_ = new Rune[8]; + } else if (nrunes_ >= 8 && (nrunes_ & (nrunes_ - 1)) == 0) { + // double on powers of two + Rune *old = runes_; + runes_ = new Rune[nrunes_ * 2]; + for (int i = 0; i < nrunes_; i++) + runes_[i] = old[i]; + delete[] old; + } + + runes_[nrunes_++] = r; +} + +Regexp* Regexp::HaveMatch(int match_id, ParseFlags flags) { + Regexp* re = new Regexp(kRegexpHaveMatch, flags); + re->match_id_ = match_id; + return re; +} + +Regexp* Regexp::StarPlusOrQuest(RegexpOp op, Regexp* sub, ParseFlags flags) { + // Squash **, ++ and ??. + if (op == sub->op() && flags == sub->parse_flags()) + return sub; + + // Squash *+, *?, +*, +?, ?* and ?+. They all squash to *, so because + // op is Star/Plus/Quest, we just have to check that sub->op() is too. + if ((sub->op() == kRegexpStar || + sub->op() == kRegexpPlus || + sub->op() == kRegexpQuest) && + flags == sub->parse_flags()) { + // If sub is Star, no need to rewrite it. + if (sub->op() == kRegexpStar) + return sub; + + // Rewrite sub to Star. + Regexp* re = new Regexp(kRegexpStar, flags); + re->AllocSub(1); + re->sub()[0] = sub->sub()[0]->Incref(); + sub->Decref(); // We didn't consume the reference after all. + return re; + } + + Regexp* re = new Regexp(op, flags); + re->AllocSub(1); + re->sub()[0] = sub; + return re; +} + +Regexp* Regexp::Plus(Regexp* sub, ParseFlags flags) { + return StarPlusOrQuest(kRegexpPlus, sub, flags); +} + +Regexp* Regexp::Star(Regexp* sub, ParseFlags flags) { + return StarPlusOrQuest(kRegexpStar, sub, flags); +} + +Regexp* Regexp::Quest(Regexp* sub, ParseFlags flags) { + return StarPlusOrQuest(kRegexpQuest, sub, flags); +} + +Regexp* Regexp::ConcatOrAlternate(RegexpOp op, Regexp** sub, int nsub, + ParseFlags flags, bool can_factor) { + if (nsub == 1) + return sub[0]; + + if (nsub == 0) { + if (op == kRegexpAlternate) + return new Regexp(kRegexpNoMatch, flags); + else + return new Regexp(kRegexpEmptyMatch, flags); + } + + PODArray subcopy; + if (op == kRegexpAlternate && can_factor) { + // Going to edit sub; make a copy so we don't step on caller. + subcopy = PODArray(nsub); + memmove(subcopy.data(), sub, nsub * sizeof sub[0]); + sub = subcopy.data(); + nsub = FactorAlternation(sub, nsub, flags); + if (nsub == 1) { + Regexp* re = sub[0]; + return re; + } + } + + if (nsub > kMaxNsub) { + // Too many subexpressions to fit in a single Regexp. + // Make a two-level tree. Two levels gets us to 65535^2. + int nbigsub = (nsub+kMaxNsub-1)/kMaxNsub; + Regexp* re = new Regexp(op, flags); + re->AllocSub(nbigsub); + Regexp** subs = re->sub(); + for (int i = 0; i < nbigsub - 1; i++) + subs[i] = ConcatOrAlternate(op, sub+i*kMaxNsub, kMaxNsub, flags, false); + subs[nbigsub - 1] = ConcatOrAlternate(op, sub+(nbigsub-1)*kMaxNsub, + nsub - (nbigsub-1)*kMaxNsub, flags, + false); + return re; + } + + Regexp* re = new Regexp(op, flags); + re->AllocSub(nsub); + Regexp** subs = re->sub(); + for (int i = 0; i < nsub; i++) + subs[i] = sub[i]; + return re; +} + +Regexp* Regexp::Concat(Regexp** sub, int nsub, ParseFlags flags) { + return ConcatOrAlternate(kRegexpConcat, sub, nsub, flags, false); +} + +Regexp* Regexp::Alternate(Regexp** sub, int nsub, ParseFlags flags) { + return ConcatOrAlternate(kRegexpAlternate, sub, nsub, flags, true); +} + +Regexp* Regexp::AlternateNoFactor(Regexp** sub, int nsub, ParseFlags flags) { + return ConcatOrAlternate(kRegexpAlternate, sub, nsub, flags, false); +} + +Regexp* Regexp::Capture(Regexp* sub, ParseFlags flags, int cap) { + Regexp* re = new Regexp(kRegexpCapture, flags); + re->AllocSub(1); + re->sub()[0] = sub; + re->cap_ = cap; + return re; +} + +Regexp* Regexp::Repeat(Regexp* sub, ParseFlags flags, int min, int max) { + Regexp* re = new Regexp(kRegexpRepeat, flags); + re->AllocSub(1); + re->sub()[0] = sub; + re->min_ = min; + re->max_ = max; + return re; +} + +Regexp* Regexp::NewLiteral(Rune rune, ParseFlags flags) { + Regexp* re = new Regexp(kRegexpLiteral, flags); + re->rune_ = rune; + return re; +} + +Regexp* Regexp::LiteralString(Rune* runes, int nrunes, ParseFlags flags) { + if (nrunes <= 0) + return new Regexp(kRegexpEmptyMatch, flags); + if (nrunes == 1) + return NewLiteral(runes[0], flags); + Regexp* re = new Regexp(kRegexpLiteralString, flags); + for (int i = 0; i < nrunes; i++) + re->AddRuneToString(runes[i]); + return re; +} + +Regexp* Regexp::NewCharClass(CharClass* cc, ParseFlags flags) { + Regexp* re = new Regexp(kRegexpCharClass, flags); + re->cc_ = cc; + return re; +} + +void Regexp::Swap(Regexp* that) { + // Regexp is not trivially copyable, so we cannot freely copy it with + // memmove(3), but swapping objects like so is safe for our purposes. + char tmp[sizeof *this]; + void* vthis = reinterpret_cast(this); + void* vthat = reinterpret_cast(that); + memmove(tmp, vthis, sizeof *this); + memmove(vthis, vthat, sizeof *this); + memmove(vthat, tmp, sizeof *this); +} + +// Tests equality of all top-level structure but not subregexps. +static bool TopEqual(Regexp* a, Regexp* b) { + if (a->op() != b->op()) + return false; + + switch (a->op()) { + case kRegexpNoMatch: + case kRegexpEmptyMatch: + case kRegexpAnyChar: + case kRegexpAnyByte: + case kRegexpBeginLine: + case kRegexpEndLine: + case kRegexpWordBoundary: + case kRegexpNoWordBoundary: + case kRegexpBeginText: + return true; + + case kRegexpEndText: + // The parse flags remember whether it's \z or (?-m:$), + // which matters when testing against PCRE. + return ((a->parse_flags() ^ b->parse_flags()) & Regexp::WasDollar) == 0; + + case kRegexpLiteral: + return a->rune() == b->rune() && + ((a->parse_flags() ^ b->parse_flags()) & Regexp::FoldCase) == 0; + + case kRegexpLiteralString: + return a->nrunes() == b->nrunes() && + ((a->parse_flags() ^ b->parse_flags()) & Regexp::FoldCase) == 0 && + memcmp(a->runes(), b->runes(), + a->nrunes() * sizeof a->runes()[0]) == 0; + + case kRegexpAlternate: + case kRegexpConcat: + return a->nsub() == b->nsub(); + + case kRegexpStar: + case kRegexpPlus: + case kRegexpQuest: + return ((a->parse_flags() ^ b->parse_flags()) & Regexp::NonGreedy) == 0; + + case kRegexpRepeat: + return ((a->parse_flags() ^ b->parse_flags()) & Regexp::NonGreedy) == 0 && + a->min() == b->min() && + a->max() == b->max(); + + case kRegexpCapture: + return a->cap() == b->cap() && a->name() == b->name(); + + case kRegexpHaveMatch: + return a->match_id() == b->match_id(); + + case kRegexpCharClass: { + CharClass* acc = a->cc(); + CharClass* bcc = b->cc(); + return acc->size() == bcc->size() && + acc->end() - acc->begin() == bcc->end() - bcc->begin() && + memcmp(acc->begin(), bcc->begin(), + (acc->end() - acc->begin()) * sizeof acc->begin()[0]) == 0; + } + } + +#if 0 + LOG(DFATAL) << "Unexpected op in Regexp::Equal: " << a->op(); +#endif + return 0; +} + +bool Regexp::Equal(Regexp* a, Regexp* b) { + if (a == NULL || b == NULL) + return a == b; + + if (!TopEqual(a, b)) + return false; + + // Fast path: + // return without allocating vector if there are no subregexps. + switch (a->op()) { + case kRegexpAlternate: + case kRegexpConcat: + case kRegexpStar: + case kRegexpPlus: + case kRegexpQuest: + case kRegexpRepeat: + case kRegexpCapture: + break; + + default: + return true; + } + + // Committed to doing real work. + // The stack (vector) has pairs of regexps waiting to + // be compared. The regexps are only equal if + // all the pairs end up being equal. + std::vector stk; + + for (;;) { + // Invariant: TopEqual(a, b) == true. + Regexp* a2; + Regexp* b2; + switch (a->op()) { + default: + break; + case kRegexpAlternate: + case kRegexpConcat: + for (int i = 0; i < a->nsub(); i++) { + a2 = a->sub()[i]; + b2 = b->sub()[i]; + if (!TopEqual(a2, b2)) + return false; + stk.push_back(a2); + stk.push_back(b2); + } + break; + + case kRegexpStar: + case kRegexpPlus: + case kRegexpQuest: + case kRegexpRepeat: + case kRegexpCapture: + a2 = a->sub()[0]; + b2 = b->sub()[0]; + if (!TopEqual(a2, b2)) + return false; + // Really: + // stk.push_back(a2); + // stk.push_back(b2); + // break; + // but faster to assign directly and loop. + a = a2; + b = b2; + continue; + } + + size_t n = stk.size(); + if (n == 0) + break; + + DCHECK_GE(n, 2); + a = stk[n-2]; + b = stk[n-1]; + stk.resize(n-2); + } + + return true; +} + +// Keep in sync with enum RegexpStatusCode in regexp.h +static const char *kErrorStrings[] = { + "no error", + "unexpected error", + "invalid escape sequence", + "invalid character class", + "invalid character class range", + "missing ]", + "missing )", + "unexpected )", + "trailing \\", + "no argument for repetition operator", + "invalid repetition size", + "bad repetition operator", + "invalid perl operator", + "invalid UTF-8", + "invalid named capture group", +}; + +std::string RegexpStatus::CodeText(enum RegexpStatusCode code) { + if (code < 0 || code >= arraysize(kErrorStrings)) + code = kRegexpInternalError; + return kErrorStrings[code]; +} + +std::string RegexpStatus::Text() const { + if (error_arg_.empty()) + return CodeText(code_); + std::string s; + s.append(CodeText(code_)); + s.append(": "); + s.append(error_arg_.data(), error_arg_.size()); + return s; +} + +void RegexpStatus::Copy(const RegexpStatus& status) { + code_ = status.code_; + error_arg_ = status.error_arg_; +} + +typedef int Ignored; // Walker doesn't exist + +// Walker subclass to count capturing parens in regexp. +class NumCapturesWalker : public Regexp::Walker { + public: + NumCapturesWalker() : ncapture_(0) {} + int ncapture() { return ncapture_; } + + virtual Ignored PreVisit(Regexp* re, Ignored ignored, bool* stop) { + if (re->op() == kRegexpCapture) + ncapture_++; + return ignored; + } + + virtual Ignored ShortVisit(Regexp* re, Ignored ignored) { + // Should never be called: we use Walk(), not WalkExponential(). +#if 0 +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + LOG(DFATAL) << "NumCapturesWalker::ShortVisit called"; +#endif +#endif + return ignored; + } + + private: + int ncapture_; + + NumCapturesWalker(const NumCapturesWalker&) = delete; + NumCapturesWalker& operator=(const NumCapturesWalker&) = delete; +}; + +int Regexp::NumCaptures() { + NumCapturesWalker w; + w.Walk(this, 0); + return w.ncapture(); +} + +// Walker class to build map of named capture groups and their indices. +class NamedCapturesWalker : public Regexp::Walker { + public: + NamedCapturesWalker() : map_(NULL) {} + ~NamedCapturesWalker() { delete map_; } + + std::map* TakeMap() { + std::map* m = map_; + map_ = NULL; + return m; + } + + virtual Ignored PreVisit(Regexp* re, Ignored ignored, bool* stop) { + if (re->op() == kRegexpCapture && re->name() != NULL) { + // Allocate map once we find a name. + if (map_ == NULL) + map_ = new std::map; + + // Record first occurrence of each name. + // (The rule is that if you have the same name + // multiple times, only the leftmost one counts.) + if (map_->find(*re->name()) == map_->end()) + (*map_)[*re->name()] = re->cap(); + } + return ignored; + } + + virtual Ignored ShortVisit(Regexp* re, Ignored ignored) { + // Should never be called: we use Walk(), not WalkExponential(). +#if 0 +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + LOG(DFATAL) << "NamedCapturesWalker::ShortVisit called"; +#endif +#endif + return ignored; + } + + private: + std::map* map_; + + NamedCapturesWalker(const NamedCapturesWalker&) = delete; + NamedCapturesWalker& operator=(const NamedCapturesWalker&) = delete; +}; + +std::map* Regexp::NamedCaptures() { + NamedCapturesWalker w; + w.Walk(this, 0); + return w.TakeMap(); +} + +// Walker class to build map from capture group indices to their names. +class CaptureNamesWalker : public Regexp::Walker { + public: + CaptureNamesWalker() : map_(NULL) {} + ~CaptureNamesWalker() { delete map_; } + + std::map* TakeMap() { + std::map* m = map_; + map_ = NULL; + return m; + } + + virtual Ignored PreVisit(Regexp* re, Ignored ignored, bool* stop) { + if (re->op() == kRegexpCapture && re->name() != NULL) { + // Allocate map once we find a name. + if (map_ == NULL) + map_ = new std::map; + + (*map_)[re->cap()] = *re->name(); + } + return ignored; + } + + virtual Ignored ShortVisit(Regexp* re, Ignored ignored) { + // Should never be called: we use Walk(), not WalkExponential(). +#if 0 +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + LOG(DFATAL) << "CaptureNamesWalker::ShortVisit called"; +#endif +#endif + return ignored; + } + + private: + std::map* map_; + + CaptureNamesWalker(const CaptureNamesWalker&) = delete; + CaptureNamesWalker& operator=(const CaptureNamesWalker&) = delete; +}; + +std::map* Regexp::CaptureNames() { + CaptureNamesWalker w; + w.Walk(this, 0); + return w.TakeMap(); +} + +void ConvertRunesToBytes(bool latin1, Rune* runes, int nrunes, + std::string* bytes) { + if (latin1) { + bytes->resize(nrunes); + for (int i = 0; i < nrunes; i++) + (*bytes)[i] = static_cast(runes[i]); + } else { + bytes->resize(nrunes * UTFmax); // worst case + char* p = &(*bytes)[0]; + for (int i = 0; i < nrunes; i++) + p += runetochar(p, &runes[i]); + bytes->resize(p - &(*bytes)[0]); + bytes->shrink_to_fit(); + } +} + +// Determines whether regexp matches must be anchored +// with a fixed string prefix. If so, returns the prefix and +// the regexp that remains after the prefix. The prefix might +// be ASCII case-insensitive. +bool Regexp::RequiredPrefix(std::string* prefix, bool* foldcase, + Regexp** suffix) { + prefix->clear(); + *foldcase = false; + *suffix = NULL; + + // No need for a walker: the regexp must be of the form + // 1. some number of ^ anchors + // 2. a literal char or string + // 3. the rest + if (op_ != kRegexpConcat) + return false; + int i = 0; + while (i < nsub_ && sub()[i]->op_ == kRegexpBeginText) + i++; + if (i == 0 || i >= nsub_) + return false; + Regexp* re = sub()[i]; + if (re->op_ != kRegexpLiteral && + re->op_ != kRegexpLiteralString) + return false; + i++; + if (i < nsub_) { + for (int j = i; j < nsub_; j++) + sub()[j]->Incref(); + *suffix = Concat(sub() + i, nsub_ - i, parse_flags()); + } else { + *suffix = new Regexp(kRegexpEmptyMatch, parse_flags()); + } + + bool latin1 = (re->parse_flags() & Latin1) != 0; + Rune* runes = re->op_ == kRegexpLiteral ? &re->rune_ : re->runes_; + int nrunes = re->op_ == kRegexpLiteral ? 1 : re->nrunes_; + ConvertRunesToBytes(latin1, runes, nrunes, prefix); + *foldcase = (re->parse_flags() & FoldCase) != 0; + return true; +} + +// Determines whether regexp matches must be unanchored +// with a fixed string prefix. If so, returns the prefix. +// The prefix might be ASCII case-insensitive. +bool Regexp::RequiredPrefixForAccel(std::string* prefix, bool* foldcase) { + prefix->clear(); + *foldcase = false; + + // No need for a walker: the regexp must either begin with or be + // a literal char or string. + Regexp* re = op_ == kRegexpConcat && nsub_ > 0 ? sub()[0] : this; + if (re->op_ != kRegexpLiteral && + re->op_ != kRegexpLiteralString) + return false; + + bool latin1 = (re->parse_flags() & Latin1) != 0; + Rune* runes = re->op_ == kRegexpLiteral ? &re->rune_ : re->runes_; + int nrunes = re->op_ == kRegexpLiteral ? 1 : re->nrunes_; + ConvertRunesToBytes(latin1, runes, nrunes, prefix); + *foldcase = (re->parse_flags() & FoldCase) != 0; + return true; +} + +// Character class builder is a balanced binary tree (STL set) +// containing non-overlapping, non-abutting RuneRanges. +// The less-than operator used in the tree treats two +// ranges as equal if they overlap at all, so that +// lookups for a particular Rune are possible. + +CharClassBuilder::CharClassBuilder() { + nrunes_ = 0; + upper_ = 0; + lower_ = 0; +} + +// Add lo-hi to the class; return whether class got bigger. +bool CharClassBuilder::AddRange(Rune lo, Rune hi) { + if (hi < lo) + return false; + + if (lo <= 'z' && hi >= 'A') { + // Overlaps some alpha, maybe not all. + // Update bitmaps telling which ASCII letters are in the set. + Rune lo1 = std::max(lo, 'A'); + Rune hi1 = std::min(hi, 'Z'); + if (lo1 <= hi1) + upper_ |= ((1 << (hi1 - lo1 + 1)) - 1) << (lo1 - 'A'); + + lo1 = std::max(lo, 'a'); + hi1 = std::min(hi, 'z'); + if (lo1 <= hi1) + lower_ |= ((1 << (hi1 - lo1 + 1)) - 1) << (lo1 - 'a'); + } + + { // Check whether lo, hi is already in the class. + iterator it = ranges_.find(RuneRange(lo, lo)); + if (it != end() && it->lo <= lo && hi <= it->hi) + return false; + } + + // Look for a range abutting lo on the left. + // If it exists, take it out and increase our range. + if (lo > 0) { + iterator it = ranges_.find(RuneRange(lo-1, lo-1)); + if (it != end()) { + lo = it->lo; + if (it->hi > hi) + hi = it->hi; + nrunes_ -= it->hi - it->lo + 1; + ranges_.erase(it); + } + } + + // Look for a range abutting hi on the right. + // If it exists, take it out and increase our range. + if (hi < Runemax) { + iterator it = ranges_.find(RuneRange(hi+1, hi+1)); + if (it != end()) { + hi = it->hi; + nrunes_ -= it->hi - it->lo + 1; + ranges_.erase(it); + } + } + + // Look for ranges between lo and hi. Take them out. + // This is only safe because the set has no overlapping ranges. + // We've already removed any ranges abutting lo and hi, so + // any that overlap [lo, hi] must be contained within it. + for (;;) { + iterator it = ranges_.find(RuneRange(lo, hi)); + if (it == end()) + break; + nrunes_ -= it->hi - it->lo + 1; + ranges_.erase(it); + } + + // Finally, add [lo, hi]. + nrunes_ += hi - lo + 1; + ranges_.insert(RuneRange(lo, hi)); + return true; +} + +void CharClassBuilder::AddCharClass(CharClassBuilder *cc) { + for (iterator it = cc->begin(); it != cc->end(); ++it) + AddRange(it->lo, it->hi); +} + +bool CharClassBuilder::Contains(Rune r) { + return ranges_.find(RuneRange(r, r)) != end(); +} + +// Does the character class behave the same on A-Z as on a-z? +bool CharClassBuilder::FoldsASCII() { + return ((upper_ ^ lower_) & AlphaMask) == 0; +} + +CharClassBuilder* CharClassBuilder::Copy() { + CharClassBuilder* cc = new CharClassBuilder; + for (iterator it = begin(); it != end(); ++it) + cc->ranges_.insert(RuneRange(it->lo, it->hi)); + cc->upper_ = upper_; + cc->lower_ = lower_; + cc->nrunes_ = nrunes_; + return cc; +} + + + +void CharClassBuilder::RemoveAbove(Rune r) { + if (r >= Runemax) + return; + + if (r < 'z') { + if (r < 'a') + lower_ = 0; + else + lower_ &= AlphaMask >> ('z' - r); + } + + if (r < 'Z') { + if (r < 'A') + upper_ = 0; + else + upper_ &= AlphaMask >> ('Z' - r); + } + + for (;;) { + + iterator it = ranges_.find(RuneRange(r + 1, Runemax)); + if (it == end()) + break; + RuneRange rr = *it; + ranges_.erase(it); + nrunes_ -= rr.hi - rr.lo + 1; + if (rr.lo <= r) { + rr.hi = r; + ranges_.insert(rr); + nrunes_ += rr.hi - rr.lo + 1; + } + } +} + +void CharClassBuilder::Negate() { + // Build up negation and then copy in. + // Could edit ranges in place, but C++ won't let me. + std::vector v; + v.reserve(ranges_.size() + 1); + + // In negation, first range begins at 0, unless + // the current class begins at 0. + iterator it = begin(); + if (it == end()) { + v.push_back(RuneRange(0, Runemax)); + } else { + int nextlo = 0; + if (it->lo == 0) { + nextlo = it->hi + 1; + ++it; + } + for (; it != end(); ++it) { + v.push_back(RuneRange(nextlo, it->lo - 1)); + nextlo = it->hi + 1; + } + if (nextlo <= Runemax) + v.push_back(RuneRange(nextlo, Runemax)); + } + + ranges_.clear(); + for (size_t i = 0; i < v.size(); i++) + ranges_.insert(v[i]); + + upper_ = AlphaMask & ~upper_; + lower_ = AlphaMask & ~lower_; + nrunes_ = Runemax+1 - nrunes_; +} + +// Character class is a sorted list of ranges. +// The ranges are allocated in the same block as the header, +// necessitating a special allocator and Delete method. + +CharClass* CharClass::New(int maxranges) { + CharClass* cc; + uint8_t* data = new uint8_t[sizeof *cc + maxranges*sizeof cc->ranges_[0]]; + cc = reinterpret_cast(data); + cc->ranges_ = reinterpret_cast(data + sizeof *cc); + cc->nranges_ = 0; + cc->folds_ascii_ = false; + cc->nrunes_ = 0; + return cc; +} + +void CharClass::Delete() { + uint8_t* data = reinterpret_cast(this); + delete[] data; +} + +CharClass* CharClass::Negate() { + CharClass* cc = CharClass::New(nranges_+1); + cc->folds_ascii_ = folds_ascii_; + cc->nrunes_ = Runemax + 1 - nrunes_; + int n = 0; + int nextlo = 0; + for (CharClass::iterator it = begin(); it != end(); ++it) { + if (it->lo == nextlo) { + nextlo = it->hi + 1; + } else { + cc->ranges_[n++] = RuneRange(nextlo, it->lo - 1); + nextlo = it->hi + 1; + } + } + if (nextlo <= Runemax) + cc->ranges_[n++] = RuneRange(nextlo, Runemax); + cc->nranges_ = n; + return cc; +} + +bool CharClass::Contains(Rune r) { + RuneRange* rr = ranges_; + int n = nranges_; + while (n > 0) { + int m = n/2; + if (rr[m].hi < r) { + rr += m+1; + n -= m+1; + } else if (r < rr[m].lo) { + n = m; + } else { // rr[m].lo <= r && r <= rr[m].hi + return true; + } + } + return false; +} + +CharClass* CharClassBuilder::GetCharClass() { + CharClass* cc = CharClass::New(static_cast(ranges_.size())); + int n = 0; + for (iterator it = begin(); it != end(); ++it) + cc->ranges_[n++] = *it; + cc->nranges_ = n; + DCHECK_LE(n, static_cast(ranges_.size())); + cc->nrunes_ = nrunes_; + cc->folds_ascii_ = FoldsASCII(); + return cc; +} + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/regexp.h b/third_party/opa/wasm/src/re2/re2/regexp.h new file mode 100644 index 000000000000..9ea7a07733ce --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/regexp.h @@ -0,0 +1,660 @@ +// Copyright 2006 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef RE2_REGEXP_H_ +#define RE2_REGEXP_H_ + +// --- SPONSORED LINK -------------------------------------------------- +// If you want to use this library for regular expression matching, +// you should use re2/re2.h, which provides a class RE2 that +// mimics the PCRE interface provided by PCRE's C++ wrappers. +// This header describes the low-level interface used to implement RE2 +// and may change in backwards-incompatible ways from time to time. +// In contrast, RE2's interface will not. +// --------------------------------------------------------------------- + +// Regular expression library: parsing, execution, and manipulation +// of regular expressions. +// +// Any operation that traverses the Regexp structures should be written +// using Regexp::Walker (see walker-inl.h), not recursively, because deeply nested +// regular expressions such as x++++++++++++++++++++... might cause recursive +// traversals to overflow the stack. +// +// It is the caller's responsibility to provide appropriate mutual exclusion +// around manipulation of the regexps. RE2 does this. +// +// PARSING +// +// Regexp::Parse parses regular expressions encoded in UTF-8. +// The default syntax is POSIX extended regular expressions, +// with the following changes: +// +// 1. Backreferences (optional in POSIX EREs) are not supported. +// (Supporting them precludes the use of DFA-based +// matching engines.) +// +// 2. Collating elements and collation classes are not supported. +// (No one has needed or wanted them.) +// +// The exact syntax accepted can be modified by passing flags to +// Regexp::Parse. In particular, many of the basic Perl additions +// are available. The flags are documented below (search for LikePerl). +// +// If parsed with the flag Regexp::Latin1, both the regular expression +// and the input to the matching routines are assumed to be encoded in +// Latin-1, not UTF-8. +// +// EXECUTION +// +// Once Regexp has parsed a regular expression, it provides methods +// to search text using that regular expression. These methods are +// implemented via calling out to other regular expression libraries. +// (Let's call them the sublibraries.) +// +// To call a sublibrary, Regexp does not simply prepare a +// string version of the regular expression and hand it to the +// sublibrary. Instead, Regexp prepares, from its own parsed form, the +// corresponding internal representation used by the sublibrary. +// This has the drawback of needing to know the internal representation +// used by the sublibrary, but it has two important benefits: +// +// 1. The syntax and meaning of regular expressions is guaranteed +// to be that used by Regexp's parser, not the syntax expected +// by the sublibrary. Regexp might accept a restricted or +// expanded syntax for regular expressions as compared with +// the sublibrary. As long as Regexp can translate from its +// internal form into the sublibrary's, clients need not know +// exactly which sublibrary they are using. +// +// 2. The sublibrary parsers are bypassed. For whatever reason, +// sublibrary regular expression parsers often have security +// problems. For example, plan9grep's regular expression parser +// has a buffer overflow in its handling of large character +// classes, and PCRE's parser has had buffer overflow problems +// in the past. Security-team requires sandboxing of sublibrary +// regular expression parsers. Avoiding the sublibrary parsers +// avoids the sandbox. +// +// The execution methods we use now are provided by the compiled form, +// Prog, described in prog.h +// +// MANIPULATION +// +// Unlike other regular expression libraries, Regexp makes its parsed +// form accessible to clients, so that client code can analyze the +// parsed regular expressions. + +#include +#include +#include +#include + +#include "util/util.h" +#include "util/logging.h" +#include "util/utf.h" +#include "re2/stringpiece.h" + +namespace re2 { + +// Keep in sync with string list kOpcodeNames[] in testing/dump.cc +enum RegexpOp { + // Matches no strings. + kRegexpNoMatch = 1, + + // Matches empty string. + kRegexpEmptyMatch, + + // Matches rune_. + kRegexpLiteral, + + // Matches runes_. + kRegexpLiteralString, + + // Matches concatenation of sub_[0..nsub-1]. + kRegexpConcat, + // Matches union of sub_[0..nsub-1]. + kRegexpAlternate, + + // Matches sub_[0] zero or more times. + kRegexpStar, + // Matches sub_[0] one or more times. + kRegexpPlus, + // Matches sub_[0] zero or one times. + kRegexpQuest, + + // Matches sub_[0] at least min_ times, at most max_ times. + // max_ == -1 means no upper limit. + kRegexpRepeat, + + // Parenthesized (capturing) subexpression. Index is cap_. + // Optionally, capturing name is name_. + kRegexpCapture, + + // Matches any character. + kRegexpAnyChar, + + // Matches any byte [sic]. + kRegexpAnyByte, + + // Matches empty string at beginning of line. + kRegexpBeginLine, + // Matches empty string at end of line. + kRegexpEndLine, + + // Matches word boundary "\b". + kRegexpWordBoundary, + // Matches not-a-word boundary "\B". + kRegexpNoWordBoundary, + + // Matches empty string at beginning of text. + kRegexpBeginText, + // Matches empty string at end of text. + kRegexpEndText, + + // Matches character class given by cc_. + kRegexpCharClass, + + // Forces match of entire expression right now, + // with match ID match_id_ (used by RE2::Set). + kRegexpHaveMatch, + + kMaxRegexpOp = kRegexpHaveMatch, +}; + +// Keep in sync with string list in regexp.cc +enum RegexpStatusCode { + // No error + kRegexpSuccess = 0, + + // Unexpected error + kRegexpInternalError, + + // Parse errors + kRegexpBadEscape, // bad escape sequence + kRegexpBadCharClass, // bad character class + kRegexpBadCharRange, // bad character class range + kRegexpMissingBracket, // missing closing ] + kRegexpMissingParen, // missing closing ) + kRegexpUnexpectedParen, // unexpected closing ) + kRegexpTrailingBackslash, // at end of regexp + kRegexpRepeatArgument, // repeat argument missing, e.g. "*" + kRegexpRepeatSize, // bad repetition argument + kRegexpRepeatOp, // bad repetition operator + kRegexpBadPerlOp, // bad perl operator + kRegexpBadUTF8, // invalid UTF-8 in regexp + kRegexpBadNamedCapture, // bad named capture +}; + +// Error status for certain operations. +class RegexpStatus { + public: + RegexpStatus() : code_(kRegexpSuccess), tmp_(NULL) {} + ~RegexpStatus() { delete tmp_; } + + void set_code(RegexpStatusCode code) { code_ = code; } + void set_error_arg(const StringPiece& error_arg) { error_arg_ = error_arg; } + void set_tmp(std::string* tmp) { delete tmp_; tmp_ = tmp; } + RegexpStatusCode code() const { return code_; } + const StringPiece& error_arg() const { return error_arg_; } + bool ok() const { return code() == kRegexpSuccess; } + + // Copies state from status. + void Copy(const RegexpStatus& status); + + // Returns text equivalent of code, e.g.: + // "Bad character class" + static std::string CodeText(RegexpStatusCode code); + + // Returns text describing error, e.g.: + // "Bad character class: [z-a]" + std::string Text() const; + + private: + RegexpStatusCode code_; // Kind of error + StringPiece error_arg_; // Piece of regexp containing syntax error. + std::string* tmp_; // Temporary storage, possibly where error_arg_ is. + + RegexpStatus(const RegexpStatus&) = delete; + RegexpStatus& operator=(const RegexpStatus&) = delete; +}; + +// Compiled form; see prog.h +class Prog; + +struct RuneRange { + RuneRange() : lo(0), hi(0) { } + RuneRange(int l, int h) : lo(l), hi(h) { } + Rune lo; + Rune hi; +}; + +// Less-than on RuneRanges treats a == b if they overlap at all. +// This lets us look in a set to find the range covering a particular Rune. +struct RuneRangeLess { + bool operator()(const RuneRange& a, const RuneRange& b) const { + return a.hi < b.lo; + } +}; + +class CharClassBuilder; + +class CharClass { + public: + void Delete(); + + typedef RuneRange* iterator; + iterator begin() { return ranges_; } + iterator end() { return ranges_ + nranges_; } + + int size() { return nrunes_; } + bool empty() { return nrunes_ == 0; } + bool full() { return nrunes_ == Runemax+1; } + bool FoldsASCII() { return folds_ascii_; } + + bool Contains(Rune r); + CharClass* Negate(); + + private: + CharClass(); // not implemented + ~CharClass(); // not implemented + static CharClass* New(int maxranges); + + friend class CharClassBuilder; + + bool folds_ascii_; + int nrunes_; + RuneRange *ranges_; + int nranges_; + + CharClass(const CharClass&) = delete; + CharClass& operator=(const CharClass&) = delete; +}; + +class Regexp { + public: + + // Flags for parsing. Can be ORed together. + enum ParseFlags { + NoParseFlags = 0, + FoldCase = 1<<0, // Fold case during matching (case-insensitive). + Literal = 1<<1, // Treat s as literal string instead of a regexp. + ClassNL = 1<<2, // Allow char classes like [^a-z] and \D and \s + // and [[:space:]] to match newline. + DotNL = 1<<3, // Allow . to match newline. + MatchNL = ClassNL | DotNL, + OneLine = 1<<4, // Treat ^ and $ as only matching at beginning and + // end of text, not around embedded newlines. + // (Perl's default) + Latin1 = 1<<5, // Regexp and text are in Latin1, not UTF-8. + NonGreedy = 1<<6, // Repetition operators are non-greedy by default. + PerlClasses = 1<<7, // Allow Perl character classes like \d. + PerlB = 1<<8, // Allow Perl's \b and \B. + PerlX = 1<<9, // Perl extensions: + // non-capturing parens - (?: ) + // non-greedy operators - *? +? ?? {}? + // flag edits - (?i) (?-i) (?i: ) + // i - FoldCase + // m - !OneLine + // s - DotNL + // U - NonGreedy + // line ends: \A \z + // \Q and \E to disable/enable metacharacters + // (?Pexpr) for named captures + // \C to match any single byte + UnicodeGroups = 1<<10, // Allow \p{Han} for Unicode Han group + // and \P{Han} for its negation. + NeverNL = 1<<11, // Never match NL, even if the regexp mentions + // it explicitly. + NeverCapture = 1<<12, // Parse all parens as non-capturing. + + // As close to Perl as we can get. + LikePerl = ClassNL | OneLine | PerlClasses | PerlB | PerlX | + UnicodeGroups, + + // Internal use only. + WasDollar = 1<<13, // on kRegexpEndText: was $ in regexp text + AllParseFlags = (1<<14)-1, + }; + + // Get. No set, Regexps are logically immutable once created. + RegexpOp op() { return static_cast(op_); } + int nsub() { return nsub_; } + bool simple() { return simple_ != 0; } + ParseFlags parse_flags() { return static_cast(parse_flags_); } + int Ref(); // For testing. + + Regexp** sub() { + if(nsub_ <= 1) + return &subone_; + else + return submany_; + } + + int min() { DCHECK_EQ(op_, kRegexpRepeat); return min_; } + int max() { DCHECK_EQ(op_, kRegexpRepeat); return max_; } + Rune rune() { DCHECK_EQ(op_, kRegexpLiteral); return rune_; } + CharClass* cc() { DCHECK_EQ(op_, kRegexpCharClass); return cc_; } + int cap() { DCHECK_EQ(op_, kRegexpCapture); return cap_; } + const std::string* name() { DCHECK_EQ(op_, kRegexpCapture); return name_; } + Rune* runes() { DCHECK_EQ(op_, kRegexpLiteralString); return runes_; } + int nrunes() { DCHECK_EQ(op_, kRegexpLiteralString); return nrunes_; } + int match_id() { DCHECK_EQ(op_, kRegexpHaveMatch); return match_id_; } + + // Increments reference count, returns object as convenience. + Regexp* Incref(); + + // Decrements reference count and deletes this object if count reaches 0. + void Decref(); + + // Parses string s to produce regular expression, returned. + // Caller must release return value with re->Decref(). + // On failure, sets *status (if status != NULL) and returns NULL. + static Regexp* Parse(const StringPiece& s, ParseFlags flags, + RegexpStatus* status); + + // Returns a _new_ simplified version of the current regexp. + // Does not edit the current regexp. + // Caller must release return value with re->Decref(). + // Simplified means that counted repetition has been rewritten + // into simpler terms and all Perl/POSIX features have been + // removed. The result will capture exactly the same + // subexpressions the original did, unless formatted with ToString. + Regexp* Simplify(); + friend class CoalesceWalker; + friend class SimplifyWalker; + + // Parses the regexp src and then simplifies it and sets *dst to the + // string representation of the simplified form. Returns true on success. + // Returns false and sets *status (if status != NULL) on parse error. + static bool SimplifyRegexp(const StringPiece& src, ParseFlags flags, + std::string* dst, RegexpStatus* status); + + // Returns the number of capturing groups in the regexp. + int NumCaptures(); + friend class NumCapturesWalker; + + // Returns a map from names to capturing group indices, + // or NULL if the regexp contains no named capture groups. + // The caller is responsible for deleting the map. + std::map* NamedCaptures(); + + // Returns a map from capturing group indices to capturing group + // names or NULL if the regexp contains no named capture groups. The + // caller is responsible for deleting the map. + std::map* CaptureNames(); + + // Returns a string representation of the current regexp, + // using as few parentheses as possible. + std::string ToString(); + + // Convenience functions. They consume the passed reference, + // so in many cases you should use, e.g., Plus(re->Incref(), flags). + // They do not consume allocated arrays like subs or runes. + static Regexp* Plus(Regexp* sub, ParseFlags flags); + static Regexp* Star(Regexp* sub, ParseFlags flags); + static Regexp* Quest(Regexp* sub, ParseFlags flags); + static Regexp* Concat(Regexp** subs, int nsubs, ParseFlags flags); + static Regexp* Alternate(Regexp** subs, int nsubs, ParseFlags flags); + static Regexp* Capture(Regexp* sub, ParseFlags flags, int cap); + static Regexp* Repeat(Regexp* sub, ParseFlags flags, int min, int max); + static Regexp* NewLiteral(Rune rune, ParseFlags flags); + static Regexp* NewCharClass(CharClass* cc, ParseFlags flags); + static Regexp* LiteralString(Rune* runes, int nrunes, ParseFlags flags); + static Regexp* HaveMatch(int match_id, ParseFlags flags); + + // Like Alternate but does not factor out common prefixes. + static Regexp* AlternateNoFactor(Regexp** subs, int nsubs, ParseFlags flags); + + // Debugging function. Returns string format for regexp + // that makes structure clear. Does NOT use regexp syntax. + std::string Dump(); + + // Helper traversal class, defined fully in walker-inl.h. + template class Walker; + + // Compile to Prog. See prog.h + // Reverse prog expects to be run over text backward. + // Construction and execution of prog will + // stay within approximately max_mem bytes of memory. + // If max_mem <= 0, a reasonable default is used. + Prog* CompileToProg(int64_t max_mem); + Prog* CompileToReverseProg(int64_t max_mem); + + // Whether to expect this library to find exactly the same answer as PCRE + // when running this regexp. Most regexps do mimic PCRE exactly, but a few + // obscure cases behave differently. Technically this is more a property + // of the Prog than the Regexp, but the computation is much easier to do + // on the Regexp. See mimics_pcre.cc for the exact conditions. + bool MimicsPCRE(); + + // Benchmarking function. + void NullWalk(); + + // Whether every match of this regexp must be anchored and + // begin with a non-empty fixed string (perhaps after ASCII + // case-folding). If so, returns the prefix and the sub-regexp that + // follows it. + // Callers should expect *prefix, *foldcase and *suffix to be "zeroed" + // regardless of the return value. + bool RequiredPrefix(std::string* prefix, bool* foldcase, + Regexp** suffix); + + // Whether every match of this regexp must be unanchored and + // begin with a non-empty fixed string (perhaps after ASCII + // case-folding). If so, returns the prefix. + // Callers should expect *prefix and *foldcase to be "zeroed" + // regardless of the return value. + bool RequiredPrefixForAccel(std::string* prefix, bool* foldcase); + + private: + // Constructor allocates vectors as appropriate for operator. + explicit Regexp(RegexpOp op, ParseFlags parse_flags); + + // Use Decref() instead of delete to release Regexps. + // This is private to catch deletes at compile time. + ~Regexp(); + void Destroy(); + bool QuickDestroy(); + + // Helpers for Parse. Listed here so they can edit Regexps. + class ParseState; + + friend class ParseState; + friend bool ParseCharClass(StringPiece* s, Regexp** out_re, + RegexpStatus* status); + + // Helper for testing [sic]. + friend bool RegexpEqualTestingOnly(Regexp*, Regexp*); + + // Computes whether Regexp is already simple. + bool ComputeSimple(); + + // Constructor that generates a Star, Plus or Quest, + // squashing the pair if sub is also a Star, Plus or Quest. + static Regexp* StarPlusOrQuest(RegexpOp op, Regexp* sub, ParseFlags flags); + + // Constructor that generates a concatenation or alternation, + // enforcing the limit on the number of subexpressions for + // a particular Regexp. + static Regexp* ConcatOrAlternate(RegexpOp op, Regexp** subs, int nsubs, + ParseFlags flags, bool can_factor); + + // Returns the leading string that re starts with. + // The returned Rune* points into a piece of re, + // so it must not be used after the caller calls re->Decref(). + static Rune* LeadingString(Regexp* re, int* nrune, ParseFlags* flags); + + // Removes the first n leading runes from the beginning of re. + // Edits re in place. + static void RemoveLeadingString(Regexp* re, int n); + + // Returns the leading regexp in re's top-level concatenation. + // The returned Regexp* points at re or a sub-expression of re, + // so it must not be used after the caller calls re->Decref(). + static Regexp* LeadingRegexp(Regexp* re); + + // Removes LeadingRegexp(re) from re and returns the remainder. + // Might edit re in place. + static Regexp* RemoveLeadingRegexp(Regexp* re); + + // Simplifies an alternation of literal strings by factoring out + // common prefixes. + static int FactorAlternation(Regexp** sub, int nsub, ParseFlags flags); + friend class FactorAlternationImpl; + + // Is a == b? Only efficient on regexps that have not been through + // Simplify yet - the expansion of a kRegexpRepeat will make this + // take a long time. Do not call on such regexps, hence private. + static bool Equal(Regexp* a, Regexp* b); + + // Allocate space for n sub-regexps. + void AllocSub(int n) { + DCHECK(n >= 0 && static_cast(n) == n); + if (n > 1) + submany_ = new Regexp*[n]; + nsub_ = static_cast(n); + } + + // Add Rune to LiteralString + void AddRuneToString(Rune r); + + // Swaps this with that, in place. + void Swap(Regexp *that); + + // Operator. See description of operators above. + // uint8_t instead of RegexpOp to control space usage. + uint8_t op_; + + // Is this regexp structure already simple + // (has it been returned by Simplify)? + // uint8_t instead of bool to control space usage. + uint8_t simple_; + + // Flags saved from parsing and used during execution. + // (Only FoldCase is used.) + // uint16_t instead of ParseFlags to control space usage. + uint16_t parse_flags_; + + // Reference count. Exists so that SimplifyRegexp can build + // regexp structures that are dags rather than trees to avoid + // exponential blowup in space requirements. + // uint16_t to control space usage. + // The standard regexp routines will never generate a + // ref greater than the maximum repeat count (kMaxRepeat), + // but even so, Incref and Decref consult an overflow map + // when ref_ reaches kMaxRef. + uint16_t ref_; + static const uint16_t kMaxRef = 0xffff; + + // Subexpressions. + // uint16_t to control space usage. + // Concat and Alternate handle larger numbers of subexpressions + // by building concatenation or alternation trees. + // Other routines should call Concat or Alternate instead of + // filling in sub() by hand. + uint16_t nsub_; + static const uint16_t kMaxNsub = 0xffff; + union { + Regexp** submany_; // if nsub_ > 1 + Regexp* subone_; // if nsub_ == 1 + }; + + // Extra space for parse and teardown stacks. + Regexp* down_; + + // Arguments to operator. See description of operators above. + union { + struct { // Repeat + int max_; + int min_; + }; + struct { // Capture + int cap_; + std::string* name_; + }; + struct { // LiteralString + int nrunes_; + Rune* runes_; + }; + struct { // CharClass + // These two could be in separate union members, + // but it wouldn't save any space (there are other two-word structs) + // and keeping them separate avoids confusion during parsing. + CharClass* cc_; + CharClassBuilder* ccb_; + }; + Rune rune_; // Literal + int match_id_; // HaveMatch + void *the_union_[2]; // as big as any other element, for memset + }; + + Regexp(const Regexp&) = delete; + Regexp& operator=(const Regexp&) = delete; +}; + +// Character class set: contains non-overlapping, non-abutting RuneRanges. +typedef std::set RuneRangeSet; + +class CharClassBuilder { + public: + CharClassBuilder(); + + typedef RuneRangeSet::iterator iterator; + iterator begin() { return ranges_.begin(); } + iterator end() { return ranges_.end(); } + + int size() { return nrunes_; } + bool empty() { return nrunes_ == 0; } + bool full() { return nrunes_ == Runemax+1; } + + bool Contains(Rune r); + bool FoldsASCII(); + bool AddRange(Rune lo, Rune hi); // returns whether class changed + CharClassBuilder* Copy(); + void AddCharClass(CharClassBuilder* cc); + void Negate(); + void RemoveAbove(Rune r); + CharClass* GetCharClass(); + void AddRangeFlags(Rune lo, Rune hi, Regexp::ParseFlags parse_flags); + + private: + static const uint32_t AlphaMask = (1<<26) - 1; + uint32_t upper_; // bitmap of A-Z + uint32_t lower_; // bitmap of a-z + int nrunes_; + RuneRangeSet ranges_; + + CharClassBuilder(const CharClassBuilder&) = delete; + CharClassBuilder& operator=(const CharClassBuilder&) = delete; +}; + +// Bitwise ops on ParseFlags produce ParseFlags. +inline Regexp::ParseFlags operator|(Regexp::ParseFlags a, + Regexp::ParseFlags b) { + return static_cast( + static_cast(a) | static_cast(b)); +} + +inline Regexp::ParseFlags operator^(Regexp::ParseFlags a, + Regexp::ParseFlags b) { + return static_cast( + static_cast(a) ^ static_cast(b)); +} + +inline Regexp::ParseFlags operator&(Regexp::ParseFlags a, + Regexp::ParseFlags b) { + return static_cast( + static_cast(a) & static_cast(b)); +} + +inline Regexp::ParseFlags operator~(Regexp::ParseFlags a) { + // Attempting to produce a value out of enum's range has undefined behaviour. + return static_cast( + ~static_cast(a) & static_cast(Regexp::AllParseFlags)); +} + +} // namespace re2 + +#endif // RE2_REGEXP_H_ diff --git a/third_party/opa/wasm/src/re2/re2/simplify.cc b/third_party/opa/wasm/src/re2/re2/simplify.cc new file mode 100644 index 000000000000..d1798c47e2cb --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/simplify.cc @@ -0,0 +1,679 @@ +// Copyright 2006 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Rewrite POSIX and other features in re +// to use simple extended regular expression features. +// Also sort and simplify character classes. + +#include + +#include "util/util.h" +#include "util/logging.h" +#include "util/utf.h" +#include "re2/pod_array.h" +#include "re2/regexp.h" +#include "re2/walker-inl.h" + +namespace re2 { + +// Parses the regexp src and then simplifies it and sets *dst to the +// string representation of the simplified form. Returns true on success. +// Returns false and sets *error (if error != NULL) on error. +bool Regexp::SimplifyRegexp(const StringPiece& src, ParseFlags flags, + std::string* dst, RegexpStatus* status) { + Regexp* re = Parse(src, flags, status); + if (re == NULL) + return false; + Regexp* sre = re->Simplify(); + re->Decref(); + if (sre == NULL) { + if (status) { + status->set_code(kRegexpInternalError); + status->set_error_arg(src); + } + return false; + } + *dst = sre->ToString(); + sre->Decref(); + return true; +} + +// Assuming the simple_ flags on the children are accurate, +// is this Regexp* simple? +bool Regexp::ComputeSimple() { + Regexp** subs; + switch (op_) { + case kRegexpNoMatch: + case kRegexpEmptyMatch: + case kRegexpLiteral: + case kRegexpLiteralString: + case kRegexpBeginLine: + case kRegexpEndLine: + case kRegexpBeginText: + case kRegexpWordBoundary: + case kRegexpNoWordBoundary: + case kRegexpEndText: + case kRegexpAnyChar: + case kRegexpAnyByte: + case kRegexpHaveMatch: + return true; + case kRegexpConcat: + case kRegexpAlternate: + // These are simple as long as the subpieces are simple. + subs = sub(); + for (int i = 0; i < nsub_; i++) + if (!subs[i]->simple()) + return false; + return true; + case kRegexpCharClass: + // Simple as long as the char class is not empty, not full. + if (ccb_ != NULL) + return !ccb_->empty() && !ccb_->full(); + return !cc_->empty() && !cc_->full(); + case kRegexpCapture: + subs = sub(); + return subs[0]->simple(); + case kRegexpStar: + case kRegexpPlus: + case kRegexpQuest: + subs = sub(); + if (!subs[0]->simple()) + return false; + switch (subs[0]->op_) { + case kRegexpStar: + case kRegexpPlus: + case kRegexpQuest: + case kRegexpEmptyMatch: + case kRegexpNoMatch: + return false; + default: + break; + } + return true; + case kRegexpRepeat: + return false; + } +#if 0 + LOG(DFATAL) << "Case not handled in ComputeSimple: " << op_; +#endif + return false; +} + +// Walker subclass used by Simplify. +// Coalesces runs of star/plus/quest/repeat of the same literal along with any +// occurrences of that literal into repeats of that literal. It also works for +// char classes, any char and any byte. +// PostVisit creates the coalesced result, which should then be simplified. +class CoalesceWalker : public Regexp::Walker { + public: + CoalesceWalker() {} + virtual Regexp* PostVisit(Regexp* re, Regexp* parent_arg, Regexp* pre_arg, + Regexp** child_args, int nchild_args); + virtual Regexp* Copy(Regexp* re); + virtual Regexp* ShortVisit(Regexp* re, Regexp* parent_arg); + + private: + // These functions are declared inside CoalesceWalker so that + // they can edit the private fields of the Regexps they construct. + + // Returns true if r1 and r2 can be coalesced. In particular, ensures that + // the parse flags are consistent. (They will not be checked again later.) + static bool CanCoalesce(Regexp* r1, Regexp* r2); + + // Coalesces *r1ptr and *r2ptr. In most cases, the array elements afterwards + // will be empty match and the coalesced op. In other cases, where part of a + // literal string was removed to be coalesced, the array elements afterwards + // will be the coalesced op and the remainder of the literal string. + static void DoCoalesce(Regexp** r1ptr, Regexp** r2ptr); + + CoalesceWalker(const CoalesceWalker&) = delete; + CoalesceWalker& operator=(const CoalesceWalker&) = delete; +}; + +// Walker subclass used by Simplify. +// The simplify walk is purely post-recursive: given the simplified children, +// PostVisit creates the simplified result. +// The child_args are simplified Regexp*s. +class SimplifyWalker : public Regexp::Walker { + public: + SimplifyWalker() {} + virtual Regexp* PreVisit(Regexp* re, Regexp* parent_arg, bool* stop); + virtual Regexp* PostVisit(Regexp* re, Regexp* parent_arg, Regexp* pre_arg, + Regexp** child_args, int nchild_args); + virtual Regexp* Copy(Regexp* re); + virtual Regexp* ShortVisit(Regexp* re, Regexp* parent_arg); + + private: + // These functions are declared inside SimplifyWalker so that + // they can edit the private fields of the Regexps they construct. + + // Creates a concatenation of two Regexp, consuming refs to re1 and re2. + // Caller must Decref return value when done with it. + static Regexp* Concat2(Regexp* re1, Regexp* re2, Regexp::ParseFlags flags); + + // Simplifies the expression re{min,max} in terms of *, +, and ?. + // Returns a new regexp. Does not edit re. Does not consume reference to re. + // Caller must Decref return value when done with it. + static Regexp* SimplifyRepeat(Regexp* re, int min, int max, + Regexp::ParseFlags parse_flags); + + // Simplifies a character class by expanding any named classes + // into rune ranges. Does not edit re. Does not consume ref to re. + // Caller must Decref return value when done with it. + static Regexp* SimplifyCharClass(Regexp* re); + + SimplifyWalker(const SimplifyWalker&) = delete; + SimplifyWalker& operator=(const SimplifyWalker&) = delete; +}; + +// Simplifies a regular expression, returning a new regexp. +// The new regexp uses traditional Unix egrep features only, +// plus the Perl (?:) non-capturing parentheses. +// Otherwise, no POSIX or Perl additions. The new regexp +// captures exactly the same subexpressions (with the same indices) +// as the original. +// Does not edit current object. +// Caller must Decref() return value when done with it. + +Regexp* Regexp::Simplify() { + CoalesceWalker cw; + Regexp* cre = cw.Walk(this, NULL); + if (cre == NULL) + return NULL; + if (cw.stopped_early()) { + cre->Decref(); + return NULL; + } + SimplifyWalker sw; + Regexp* sre = sw.Walk(cre, NULL); + cre->Decref(); + if (sre == NULL) + return NULL; + if (sw.stopped_early()) { + sre->Decref(); + return NULL; + } + return sre; +} + +#define Simplify DontCallSimplify // Avoid accidental recursion + +// Utility function for PostVisit implementations that compares re->sub() with +// child_args to determine whether any child_args changed. In the common case, +// where nothing changed, calls Decref() for all child_args and returns false, +// so PostVisit must return re->Incref(). Otherwise, returns true. +static bool ChildArgsChanged(Regexp* re, Regexp** child_args) { + for (int i = 0; i < re->nsub(); i++) { + Regexp* sub = re->sub()[i]; + Regexp* newsub = child_args[i]; + if (newsub != sub) + return true; + } + for (int i = 0; i < re->nsub(); i++) { + Regexp* newsub = child_args[i]; + newsub->Decref(); + } + return false; +} + +Regexp* CoalesceWalker::Copy(Regexp* re) { + return re->Incref(); +} + +Regexp* CoalesceWalker::ShortVisit(Regexp* re, Regexp* parent_arg) { + // Should never be called: we use Walk(), not WalkExponential(). +#if 0 +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + LOG(DFATAL) << "CoalesceWalker::ShortVisit called"; +#endif +#endif + return re->Incref(); +} + +Regexp* CoalesceWalker::PostVisit(Regexp* re, + Regexp* parent_arg, + Regexp* pre_arg, + Regexp** child_args, + int nchild_args) { + if (re->nsub() == 0) + return re->Incref(); + + if (re->op() != kRegexpConcat) { + if (!ChildArgsChanged(re, child_args)) + return re->Incref(); + + // Something changed. Build a new op. + Regexp* nre = new Regexp(re->op(), re->parse_flags()); + nre->AllocSub(re->nsub()); + Regexp** nre_subs = nre->sub(); + for (int i = 0; i < re->nsub(); i++) + nre_subs[i] = child_args[i]; + // Repeats and Captures have additional data that must be copied. + if (re->op() == kRegexpRepeat) { + nre->min_ = re->min(); + nre->max_ = re->max(); + } else if (re->op() == kRegexpCapture) { + nre->cap_ = re->cap(); + } + return nre; + } + + bool can_coalesce = false; + for (int i = 0; i < re->nsub(); i++) { + if (i+1 < re->nsub() && + CanCoalesce(child_args[i], child_args[i+1])) { + can_coalesce = true; + break; + } + } + if (!can_coalesce) { + if (!ChildArgsChanged(re, child_args)) + return re->Incref(); + + // Something changed. Build a new op. + Regexp* nre = new Regexp(re->op(), re->parse_flags()); + nre->AllocSub(re->nsub()); + Regexp** nre_subs = nre->sub(); + for (int i = 0; i < re->nsub(); i++) + nre_subs[i] = child_args[i]; + return nre; + } + + for (int i = 0; i < re->nsub(); i++) { + if (i+1 < re->nsub() && + CanCoalesce(child_args[i], child_args[i+1])) + DoCoalesce(&child_args[i], &child_args[i+1]); + } + // Determine how many empty matches were left by DoCoalesce. + int n = 0; + for (int i = n; i < re->nsub(); i++) { + if (child_args[i]->op() == kRegexpEmptyMatch) + n++; + } + // Build a new op. + Regexp* nre = new Regexp(re->op(), re->parse_flags()); + nre->AllocSub(re->nsub() - n); + Regexp** nre_subs = nre->sub(); + for (int i = 0, j = 0; i < re->nsub(); i++) { + if (child_args[i]->op() == kRegexpEmptyMatch) { + child_args[i]->Decref(); + continue; + } + nre_subs[j] = child_args[i]; + j++; + } + return nre; +} + +bool CoalesceWalker::CanCoalesce(Regexp* r1, Regexp* r2) { + // r1 must be a star/plus/quest/repeat of a literal, char class, any char or + // any byte. + if ((r1->op() == kRegexpStar || + r1->op() == kRegexpPlus || + r1->op() == kRegexpQuest || + r1->op() == kRegexpRepeat) && + (r1->sub()[0]->op() == kRegexpLiteral || + r1->sub()[0]->op() == kRegexpCharClass || + r1->sub()[0]->op() == kRegexpAnyChar || + r1->sub()[0]->op() == kRegexpAnyByte)) { + // r2 must be a star/plus/quest/repeat of the same literal, char class, + // any char or any byte. + if ((r2->op() == kRegexpStar || + r2->op() == kRegexpPlus || + r2->op() == kRegexpQuest || + r2->op() == kRegexpRepeat) && + Regexp::Equal(r1->sub()[0], r2->sub()[0]) && + // The parse flags must be consistent. + ((r1->parse_flags() & Regexp::NonGreedy) == + (r2->parse_flags() & Regexp::NonGreedy))) { + return true; + } + // ... OR an occurrence of that literal, char class, any char or any byte + if (Regexp::Equal(r1->sub()[0], r2)) { + return true; + } + // ... OR a literal string that begins with that literal. + if (r1->sub()[0]->op() == kRegexpLiteral && + r2->op() == kRegexpLiteralString && + r2->runes()[0] == r1->sub()[0]->rune() && + // The parse flags must be consistent. + ((r1->sub()[0]->parse_flags() & Regexp::FoldCase) == + (r2->parse_flags() & Regexp::FoldCase))) { + return true; + } + } + return false; +} + +void CoalesceWalker::DoCoalesce(Regexp** r1ptr, Regexp** r2ptr) { + Regexp* r1 = *r1ptr; + Regexp* r2 = *r2ptr; + + Regexp* nre = Regexp::Repeat( + r1->sub()[0]->Incref(), r1->parse_flags(), 0, 0); + + switch (r1->op()) { + case kRegexpStar: + nre->min_ = 0; + nre->max_ = -1; + break; + + case kRegexpPlus: + nre->min_ = 1; + nre->max_ = -1; + break; + + case kRegexpQuest: + nre->min_ = 0; + nre->max_ = 1; + break; + + case kRegexpRepeat: + nre->min_ = r1->min(); + nre->max_ = r1->max(); + break; + + default: +#if 0 + LOG(DFATAL) << "DoCoalesce failed: r1->op() is " << r1->op(); +#endif + nre->Decref(); + return; + } + + switch (r2->op()) { + case kRegexpStar: + nre->max_ = -1; + goto LeaveEmpty; + + case kRegexpPlus: + nre->min_++; + nre->max_ = -1; + goto LeaveEmpty; + + case kRegexpQuest: + if (nre->max() != -1) + nre->max_++; + goto LeaveEmpty; + + case kRegexpRepeat: + nre->min_ += r2->min(); + if (r2->max() == -1) + nre->max_ = -1; + else if (nre->max() != -1) + nre->max_ += r2->max(); + goto LeaveEmpty; + + case kRegexpLiteral: + case kRegexpCharClass: + case kRegexpAnyChar: + case kRegexpAnyByte: + nre->min_++; + if (nre->max() != -1) + nre->max_++; + goto LeaveEmpty; + + LeaveEmpty: + *r1ptr = new Regexp(kRegexpEmptyMatch, Regexp::NoParseFlags); + *r2ptr = nre; + break; + + case kRegexpLiteralString: { + Rune r = r1->sub()[0]->rune(); + // Determine how much of the literal string is removed. + // We know that we have at least one rune. :) + int n = 1; + while (n < r2->nrunes() && r2->runes()[n] == r) + n++; + nre->min_ += n; + if (nre->max() != -1) + nre->max_ += n; + if (n == r2->nrunes()) + goto LeaveEmpty; + *r1ptr = nre; + *r2ptr = Regexp::LiteralString( + &r2->runes()[n], r2->nrunes() - n, r2->parse_flags()); + break; + } + + default: +#if 0 + LOG(DFATAL) << "DoCoalesce failed: r2->op() is " << r2->op(); +#endif + nre->Decref(); + return; + } + + r1->Decref(); + r2->Decref(); +} + +Regexp* SimplifyWalker::Copy(Regexp* re) { + return re->Incref(); +} + +Regexp* SimplifyWalker::ShortVisit(Regexp* re, Regexp* parent_arg) { + // Should never be called: we use Walk(), not WalkExponential(). +#if 0 +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + LOG(DFATAL) << "SimplifyWalker::ShortVisit called"; +#endif +#endif + return re->Incref(); +} + +Regexp* SimplifyWalker::PreVisit(Regexp* re, Regexp* parent_arg, bool* stop) { + if (re->simple()) { + *stop = true; + return re->Incref(); + } + return NULL; +} + +Regexp* SimplifyWalker::PostVisit(Regexp* re, + Regexp* parent_arg, + Regexp* pre_arg, + Regexp** child_args, + int nchild_args) { + switch (re->op()) { + case kRegexpNoMatch: + case kRegexpEmptyMatch: + case kRegexpLiteral: + case kRegexpLiteralString: + case kRegexpBeginLine: + case kRegexpEndLine: + case kRegexpBeginText: + case kRegexpWordBoundary: + case kRegexpNoWordBoundary: + case kRegexpEndText: + case kRegexpAnyChar: + case kRegexpAnyByte: + case kRegexpHaveMatch: + // All these are always simple. + re->simple_ = true; + return re->Incref(); + + case kRegexpConcat: + case kRegexpAlternate: { + // These are simple as long as the subpieces are simple. + if (!ChildArgsChanged(re, child_args)) { + re->simple_ = true; + return re->Incref(); + } + Regexp* nre = new Regexp(re->op(), re->parse_flags()); + nre->AllocSub(re->nsub()); + Regexp** nre_subs = nre->sub(); + for (int i = 0; i < re->nsub(); i++) + nre_subs[i] = child_args[i]; + nre->simple_ = true; + return nre; + } + + case kRegexpCapture: { + Regexp* newsub = child_args[0]; + if (newsub == re->sub()[0]) { + newsub->Decref(); + re->simple_ = true; + return re->Incref(); + } + Regexp* nre = new Regexp(kRegexpCapture, re->parse_flags()); + nre->AllocSub(1); + nre->sub()[0] = newsub; + nre->cap_ = re->cap(); + nre->simple_ = true; + return nre; + } + + case kRegexpStar: + case kRegexpPlus: + case kRegexpQuest: { + Regexp* newsub = child_args[0]; + // Special case: repeat the empty string as much as + // you want, but it's still the empty string. + if (newsub->op() == kRegexpEmptyMatch) + return newsub; + + // These are simple as long as the subpiece is simple. + if (newsub == re->sub()[0]) { + newsub->Decref(); + re->simple_ = true; + return re->Incref(); + } + + // These are also idempotent if flags are constant. + if (re->op() == newsub->op() && + re->parse_flags() == newsub->parse_flags()) + return newsub; + + Regexp* nre = new Regexp(re->op(), re->parse_flags()); + nre->AllocSub(1); + nre->sub()[0] = newsub; + nre->simple_ = true; + return nre; + } + + case kRegexpRepeat: { + Regexp* newsub = child_args[0]; + // Special case: repeat the empty string as much as + // you want, but it's still the empty string. + if (newsub->op() == kRegexpEmptyMatch) + return newsub; + + Regexp* nre = SimplifyRepeat(newsub, re->min_, re->max_, + re->parse_flags()); + newsub->Decref(); + nre->simple_ = true; + return nre; + } + + case kRegexpCharClass: { + Regexp* nre = SimplifyCharClass(re); + nre->simple_ = true; + return nre; + } + } + +#if 0 + LOG(ERROR) << "Simplify case not handled: " << re->op(); +#endif + return re->Incref(); +} + +// Creates a concatenation of two Regexp, consuming refs to re1 and re2. +// Returns a new Regexp, handing the ref to the caller. +Regexp* SimplifyWalker::Concat2(Regexp* re1, Regexp* re2, + Regexp::ParseFlags parse_flags) { + Regexp* re = new Regexp(kRegexpConcat, parse_flags); + re->AllocSub(2); + Regexp** subs = re->sub(); + subs[0] = re1; + subs[1] = re2; + return re; +} + +// Simplifies the expression re{min,max} in terms of *, +, and ?. +// Returns a new regexp. Does not edit re. Does not consume reference to re. +// Caller must Decref return value when done with it. +// The result will *not* necessarily have the right capturing parens +// if you call ToString() and re-parse it: (x){2} becomes (x)(x), +// but in the Regexp* representation, both (x) are marked as $1. +Regexp* SimplifyWalker::SimplifyRepeat(Regexp* re, int min, int max, + Regexp::ParseFlags f) { + // x{n,} means at least n matches of x. + if (max == -1) { + // Special case: x{0,} is x* + if (min == 0) + return Regexp::Star(re->Incref(), f); + + // Special case: x{1,} is x+ + if (min == 1) + return Regexp::Plus(re->Incref(), f); + + // General case: x{4,} is xxxx+ + PODArray nre_subs(min); + for (int i = 0; i < min-1; i++) + nre_subs[i] = re->Incref(); + nre_subs[min-1] = Regexp::Plus(re->Incref(), f); + return Regexp::Concat(nre_subs.data(), min, f); + } + + // Special case: (x){0} matches only empty string. + if (min == 0 && max == 0) + return new Regexp(kRegexpEmptyMatch, f); + + // Special case: x{1} is just x. + if (min == 1 && max == 1) + return re->Incref(); + + // General case: x{n,m} means n copies of x and m copies of x?. + // The machine will do less work if we nest the final m copies, + // so that x{2,5} = xx(x(x(x)?)?)? + + // Build leading prefix: xx. Capturing only on the last one. + Regexp* nre = NULL; + if (min > 0) { + PODArray nre_subs(min); + for (int i = 0; i < min; i++) + nre_subs[i] = re->Incref(); + nre = Regexp::Concat(nre_subs.data(), min, f); + } + + // Build and attach suffix: (x(x(x)?)?)? + if (max > min) { + Regexp* suf = Regexp::Quest(re->Incref(), f); + for (int i = min+1; i < max; i++) + suf = Regexp::Quest(Concat2(re->Incref(), suf, f), f); + if (nre == NULL) + nre = suf; + else + nre = Concat2(nre, suf, f); + } + + if (nre == NULL) { + // Some degenerate case, like min > max, or min < max < 0. + // This shouldn't happen, because the parser rejects such regexps. +#if 0 + LOG(DFATAL) << "Malformed repeat " << re->ToString() << " " << min << " " << max; +#endif + return new Regexp(kRegexpNoMatch, f); + } + + return nre; +} + +// Simplifies a character class. +// Caller must Decref return value when done with it. +Regexp* SimplifyWalker::SimplifyCharClass(Regexp* re) { + CharClass* cc = re->cc(); + + // Special cases + if (cc->empty()) + return new Regexp(kRegexpNoMatch, re->parse_flags()); + if (cc->full()) + return new Regexp(kRegexpAnyChar, re->parse_flags()); + + return re->Incref(); +} + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/sparse_array.h b/third_party/opa/wasm/src/re2/re2/sparse_array.h new file mode 100644 index 000000000000..09ffe086b7e1 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/sparse_array.h @@ -0,0 +1,392 @@ +// Copyright 2006 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef RE2_SPARSE_ARRAY_H_ +#define RE2_SPARSE_ARRAY_H_ + +// DESCRIPTION +// +// SparseArray(m) is a map from integers in [0, m) to T values. +// It requires (sizeof(T)+sizeof(int))*m memory, but it provides +// fast iteration through the elements in the array and fast clearing +// of the array. The array has a concept of certain elements being +// uninitialized (having no value). +// +// Insertion and deletion are constant time operations. +// +// Allocating the array is a constant time operation +// when memory allocation is a constant time operation. +// +// Clearing the array is a constant time operation (unusual!). +// +// Iterating through the array is an O(n) operation, where n +// is the number of items in the array (not O(m)). +// +// The array iterator visits entries in the order they were first +// inserted into the array. It is safe to add items to the array while +// using an iterator: the iterator will visit indices added to the array +// during the iteration, but will not re-visit indices whose values +// change after visiting. Thus SparseArray can be a convenient +// implementation of a work queue. +// +// The SparseArray implementation is NOT thread-safe. It is up to the +// caller to make sure only one thread is accessing the array. (Typically +// these arrays are temporary values and used in situations where speed is +// important.) +// +// The SparseArray interface does not present all the usual STL bells and +// whistles. +// +// Implemented with reference to Briggs & Torczon, An Efficient +// Representation for Sparse Sets, ACM Letters on Programming Languages +// and Systems, Volume 2, Issue 1-4 (March-Dec. 1993), pp. 59-69. +// +// Briggs & Torczon popularized this technique, but it had been known +// long before their paper. They point out that Aho, Hopcroft, and +// Ullman's 1974 Design and Analysis of Computer Algorithms and Bentley's +// 1986 Programming Pearls both hint at the technique in exercises to the +// reader (in Aho & Hopcroft, exercise 2.12; in Bentley, column 1 +// exercise 8). +// +// Briggs & Torczon describe a sparse set implementation. I have +// trivially generalized it to create a sparse array (actually the original +// target of the AHU and Bentley exercises). + +// IMPLEMENTATION +// +// SparseArray is an array dense_ and an array sparse_ of identical size. +// At any point, the number of elements in the sparse array is size_. +// +// The array dense_ contains the size_ elements in the sparse array (with +// their indices), +// in the order that the elements were first inserted. This array is dense: +// the size_ pairs are dense_[0] through dense_[size_-1]. +// +// The array sparse_ maps from indices in [0,m) to indices in [0,size_). +// For indices present in the array, dense_[sparse_[i]].index_ == i. +// For indices not present in the array, sparse_ can contain any value at all, +// perhaps outside the range [0, size_) but perhaps not. +// +// The lax requirement on sparse_ values makes clearing the array very easy: +// set size_ to 0. Lookups are slightly more complicated. +// An index i has a value in the array if and only if: +// sparse_[i] is in [0, size_) AND +// dense_[sparse_[i]].index_ == i. +// If both these properties hold, only then it is safe to refer to +// dense_[sparse_[i]].value_ +// as the value associated with index i. +// +// To insert a new entry, set sparse_[i] to size_, +// initialize dense_[size_], and then increment size_. +// +// To make the sparse array as efficient as possible for non-primitive types, +// elements may or may not be destroyed when they are deleted from the sparse +// array through a call to resize(). They immediately become inaccessible, but +// they are only guaranteed to be destroyed when the SparseArray destructor is +// called. +// +// A moved-from SparseArray will be empty. + +// Doing this simplifies the logic below. +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#include +#include +#if __has_feature(memory_sanitizer) +#include +#endif +#include +#include +#include + +#include "re2/pod_array.h" + +namespace re2 { + +template +class SparseArray { + public: + SparseArray(); + explicit SparseArray(int max_size); + ~SparseArray(); + + // IndexValue pairs: exposed in SparseArray::iterator. + class IndexValue; + + typedef IndexValue* iterator; + typedef const IndexValue* const_iterator; + + SparseArray(const SparseArray& src); + SparseArray(SparseArray&& src); + + SparseArray& operator=(const SparseArray& src); + SparseArray& operator=(SparseArray&& src); + + // Return the number of entries in the array. + int size() const { + return size_; + } + + // Indicate whether the array is empty. + int empty() const { + return size_ == 0; + } + + // Iterate over the array. + iterator begin() { + return dense_.data(); + } + iterator end() { + return dense_.data() + size_; + } + + const_iterator begin() const { + return dense_.data(); + } + const_iterator end() const { + return dense_.data() + size_; + } + + // Change the maximum size of the array. + // Invalidates all iterators. + void resize(int new_max_size); + + // Return the maximum size of the array. + // Indices can be in the range [0, max_size). + int max_size() const { + if (dense_.data() != NULL) + return dense_.size(); + else + return 0; + } + + // Clear the array. + void clear() { + size_ = 0; + } + + // Check whether index i is in the array. + bool has_index(int i) const; + + // Comparison function for sorting. + // Can sort the sparse array so that future iterations + // will visit indices in increasing order using + // std::sort(arr.begin(), arr.end(), arr.less); + static bool less(const IndexValue& a, const IndexValue& b); + + public: + // Set the value at index i to v. + iterator set(int i, const Value& v) { + return SetInternal(true, i, v); + } + + // Set the value at new index i to v. + // Fast but unsafe: only use if has_index(i) is false. + iterator set_new(int i, const Value& v) { + return SetInternal(false, i, v); + } + + // Set the value at index i to v. + // Fast but unsafe: only use if has_index(i) is true. + iterator set_existing(int i, const Value& v) { + return SetExistingInternal(i, v); + } + + // Get the value at index i. + // Fast but unsafe: only use if has_index(i) is true. + Value& get_existing(int i) { + assert(has_index(i)); + return dense_[sparse_[i]].value_; + } + const Value& get_existing(int i) const { + assert(has_index(i)); + return dense_[sparse_[i]].value_; + } + + private: + iterator SetInternal(bool allow_existing, int i, const Value& v) { + DebugCheckInvariants(); + if (static_cast(i) >= static_cast(max_size())) { + assert(false && "illegal index"); + // Semantically, end() would be better here, but we already know + // the user did something stupid, so begin() insulates them from + // dereferencing an invalid pointer. + return begin(); + } + if (!allow_existing) { + assert(!has_index(i)); + create_index(i); + } else { + if (!has_index(i)) + create_index(i); + } + return SetExistingInternal(i, v); + } + + iterator SetExistingInternal(int i, const Value& v) { + DebugCheckInvariants(); + assert(has_index(i)); + dense_[sparse_[i]].value_ = v; + DebugCheckInvariants(); + return dense_.data() + sparse_[i]; + } + + // Add the index i to the array. + // Only use if has_index(i) is known to be false. + // Since it doesn't set the value associated with i, + // this function is private, only intended as a helper + // for other methods. + void create_index(int i); + + // In debug mode, verify that some invariant properties of the class + // are being maintained. This is called at the end of the constructor + // and at the beginning and end of all public non-const member functions. + void DebugCheckInvariants() const; + + // Initializes memory for elements [min, max). + void MaybeInitializeMemory(int min, int max) { +#if __has_feature(memory_sanitizer) + __msan_unpoison(sparse_.data() + min, (max - min) * sizeof sparse_[0]); +#elif defined(RE2_ON_VALGRIND) + for (int i = min; i < max; i++) { + sparse_[i] = 0xababababU; + } +#endif + } + + int size_ = 0; + PODArray sparse_; + PODArray dense_; +}; + +template +SparseArray::SparseArray() = default; + +template +SparseArray::SparseArray(const SparseArray& src) + : size_(src.size_), + sparse_(src.max_size()), + dense_(src.max_size()) { + std::copy_n(src.sparse_.data(), src.max_size(), sparse_.data()); + std::copy_n(src.dense_.data(), src.max_size(), dense_.data()); +} + +template +SparseArray::SparseArray(SparseArray&& src) + : size_(src.size_), + sparse_(std::move(src.sparse_)), + dense_(std::move(src.dense_)) { + src.size_ = 0; +} + +template +SparseArray& SparseArray::operator=(const SparseArray& src) { + // Construct these first for exception safety. + PODArray a(src.max_size()); + PODArray b(src.max_size()); + + size_ = src.size_; + sparse_ = std::move(a); + dense_ = std::move(b); + std::copy_n(src.sparse_.data(), src.max_size(), sparse_.data()); + std::copy_n(src.dense_.data(), src.max_size(), dense_.data()); + return *this; +} + +template +SparseArray& SparseArray::operator=(SparseArray&& src) { + size_ = src.size_; + sparse_ = std::move(src.sparse_); + dense_ = std::move(src.dense_); + src.size_ = 0; + return *this; +} + +// IndexValue pairs: exposed in SparseArray::iterator. +template +class SparseArray::IndexValue { + public: + int index() const { return index_; } + Value& value() { return value_; } + const Value& value() const { return value_; } + + private: + friend class SparseArray; + int index_; + Value value_; +}; + +// Change the maximum size of the array. +// Invalidates all iterators. +template +void SparseArray::resize(int new_max_size) { + DebugCheckInvariants(); + if (new_max_size > max_size()) { + const int old_max_size = max_size(); + + // Construct these first for exception safety. + PODArray a(new_max_size); + PODArray b(new_max_size); + + std::copy_n(sparse_.data(), old_max_size, a.data()); + std::copy_n(dense_.data(), old_max_size, b.data()); + + sparse_ = std::move(a); + dense_ = std::move(b); + + MaybeInitializeMemory(old_max_size, new_max_size); + } + if (size_ > new_max_size) + size_ = new_max_size; + DebugCheckInvariants(); +} + +// Check whether index i is in the array. +template +bool SparseArray::has_index(int i) const { + assert(i >= 0); + assert(i < max_size()); + if (static_cast(i) >= static_cast(max_size())) { + return false; + } + // Unsigned comparison avoids checking sparse_[i] < 0. + return (uint32_t)sparse_[i] < (uint32_t)size_ && + dense_[sparse_[i]].index_ == i; +} + +template +void SparseArray::create_index(int i) { + assert(!has_index(i)); + assert(size_ < max_size()); + sparse_[i] = size_; + dense_[size_].index_ = i; + size_++; +} + +template SparseArray::SparseArray(int max_size) : + sparse_(max_size), dense_(max_size) { + MaybeInitializeMemory(size_, max_size); + DebugCheckInvariants(); +} + +template SparseArray::~SparseArray() { + DebugCheckInvariants(); +} + +template void SparseArray::DebugCheckInvariants() const { + assert(0 <= size_); + assert(size_ <= max_size()); +} + +// Comparison function for sorting. +template bool SparseArray::less(const IndexValue& a, + const IndexValue& b) { + return a.index_ < b.index_; +} + +} // namespace re2 + +#endif // RE2_SPARSE_ARRAY_H_ diff --git a/third_party/opa/wasm/src/re2/re2/sparse_set.h b/third_party/opa/wasm/src/re2/re2/sparse_set.h new file mode 100644 index 000000000000..f7b2341a1716 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/sparse_set.h @@ -0,0 +1,264 @@ +// Copyright 2006 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef RE2_SPARSE_SET_H_ +#define RE2_SPARSE_SET_H_ + +// DESCRIPTION +// +// SparseSet(m) is a set of integers in [0, m). +// It requires sizeof(int)*m memory, but it provides +// fast iteration through the elements in the set and fast clearing +// of the set. +// +// Insertion and deletion are constant time operations. +// +// Allocating the set is a constant time operation +// when memory allocation is a constant time operation. +// +// Clearing the set is a constant time operation (unusual!). +// +// Iterating through the set is an O(n) operation, where n +// is the number of items in the set (not O(m)). +// +// The set iterator visits entries in the order they were first +// inserted into the set. It is safe to add items to the set while +// using an iterator: the iterator will visit indices added to the set +// during the iteration, but will not re-visit indices whose values +// change after visiting. Thus SparseSet can be a convenient +// implementation of a work queue. +// +// The SparseSet implementation is NOT thread-safe. It is up to the +// caller to make sure only one thread is accessing the set. (Typically +// these sets are temporary values and used in situations where speed is +// important.) +// +// The SparseSet interface does not present all the usual STL bells and +// whistles. +// +// Implemented with reference to Briggs & Torczon, An Efficient +// Representation for Sparse Sets, ACM Letters on Programming Languages +// and Systems, Volume 2, Issue 1-4 (March-Dec. 1993), pp. 59-69. +// +// This is a specialization of sparse array; see sparse_array.h. + +// IMPLEMENTATION +// +// See sparse_array.h for implementation details. + +// Doing this simplifies the logic below. +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#include +#include +#if __has_feature(memory_sanitizer) +#include +#endif +//#include +#include +#include + +#include "re2/pod_array.h" + +namespace re2 { + +template +class SparseSetT { + public: + SparseSetT(); + explicit SparseSetT(int max_size); + ~SparseSetT(); + + typedef int* iterator; + typedef const int* const_iterator; + + // Return the number of entries in the set. + int size() const { + return size_; + } + + // Indicate whether the set is empty. + int empty() const { + return size_ == 0; + } + + // Iterate over the set. + iterator begin() { + return dense_.data(); + } + iterator end() { + return dense_.data() + size_; + } + + const_iterator begin() const { + return dense_.data(); + } + const_iterator end() const { + return dense_.data() + size_; + } + + // Change the maximum size of the set. + // Invalidates all iterators. + void resize(int new_max_size); + + // Return the maximum size of the set. + // Indices can be in the range [0, max_size). + int max_size() const { + if (dense_.data() != NULL) + return dense_.size(); + else + return 0; + } + + // Clear the set. + void clear() { + size_ = 0; + } + + // Check whether index i is in the set. + bool contains(int i) const; + + // Comparison function for sorting. + // Can sort the sparse set so that future iterations + // will visit indices in increasing order using + // std::sort(arr.begin(), arr.end(), arr.less); + static bool less(int a, int b); + + public: + // Insert index i into the set. + iterator insert(int i) { + return InsertInternal(true, i); + } + + // Insert index i into the set. + // Fast but unsafe: only use if contains(i) is false. + iterator insert_new(int i) { + return InsertInternal(false, i); + } + + private: + iterator InsertInternal(bool allow_existing, int i) { + DebugCheckInvariants(); + if (static_cast(i) >= static_cast(max_size())) { + assert(false && "illegal index"); + // Semantically, end() would be better here, but we already know + // the user did something stupid, so begin() insulates them from + // dereferencing an invalid pointer. + return begin(); + } + if (!allow_existing) { + assert(!contains(i)); + create_index(i); + } else { + if (!contains(i)) + create_index(i); + } + DebugCheckInvariants(); + return dense_.data() + sparse_[i]; + } + + // Add the index i to the set. + // Only use if contains(i) is known to be false. + // This function is private, only intended as a helper + // for other methods. + void create_index(int i); + + // In debug mode, verify that some invariant properties of the class + // are being maintained. This is called at the end of the constructor + // and at the beginning and end of all public non-const member functions. + void DebugCheckInvariants() const; + + // Initializes memory for elements [min, max). + void MaybeInitializeMemory(int min, int max) { +#if __has_feature(memory_sanitizer) + __msan_unpoison(sparse_.data() + min, (max - min) * sizeof sparse_[0]); +#elif defined(RE2_ON_VALGRIND) + for (int i = min; i < max; i++) { + sparse_[i] = 0xababababU; + } +#endif + } + + int size_ = 0; + PODArray sparse_; + PODArray dense_; +}; + +template +SparseSetT::SparseSetT() = default; + +// Change the maximum size of the set. +// Invalidates all iterators. +template +void SparseSetT::resize(int new_max_size) { + DebugCheckInvariants(); + if (new_max_size > max_size()) { + const int old_max_size = max_size(); + + // Construct these first for exception safety. + PODArray a(new_max_size); + PODArray b(new_max_size); + + std::copy_n(sparse_.data(), old_max_size, a.data()); + std::copy_n(dense_.data(), old_max_size, b.data()); + + sparse_ = std::move(a); + dense_ = std::move(b); + + MaybeInitializeMemory(old_max_size, new_max_size); + } + if (size_ > new_max_size) + size_ = new_max_size; + DebugCheckInvariants(); +} + +// Check whether index i is in the set. +template +bool SparseSetT::contains(int i) const { + assert(i >= 0); + assert(i < max_size()); + if (static_cast(i) >= static_cast(max_size())) { + return false; + } + // Unsigned comparison avoids checking sparse_[i] < 0. + return (uint32_t)sparse_[i] < (uint32_t)size_ && + dense_[sparse_[i]] == i; +} + +template +void SparseSetT::create_index(int i) { + assert(!contains(i)); + assert(size_ < max_size()); + sparse_[i] = size_; + dense_[size_] = i; + size_++; +} + +template SparseSetT::SparseSetT(int max_size) : + sparse_(max_size), dense_(max_size) { + MaybeInitializeMemory(size_, max_size); + DebugCheckInvariants(); +} + +template SparseSetT::~SparseSetT() { + DebugCheckInvariants(); +} + +template void SparseSetT::DebugCheckInvariants() const { + assert(0 <= size_); + assert(size_ <= max_size()); +} + +// Comparison function for sorting. +template bool SparseSetT::less(int a, int b) { + return a < b; +} + +typedef SparseSetT SparseSet; + +} // namespace re2 + +#endif // RE2_SPARSE_SET_H_ diff --git a/third_party/opa/wasm/src/re2/re2/stringpiece.cc b/third_party/opa/wasm/src/re2/re2/stringpiece.cc new file mode 100644 index 000000000000..01472c9e0269 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/stringpiece.cc @@ -0,0 +1,67 @@ +// Copyright 2004 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "re2/stringpiece.h" + +//#include + +#include "util/util.h" + +namespace re2 { + +const StringPiece::size_type StringPiece::npos; // initialized in stringpiece.h + +StringPiece::size_type StringPiece::copy(char* buf, size_type n, + size_type pos) const { + size_type ret = std::min(size_ - pos, n); + memcpy(buf, data_ + pos, ret); + return ret; +} + +StringPiece StringPiece::substr(size_type pos, size_type n) const { + if (pos > size_) pos = size_; + if (n > size_ - pos) n = size_ - pos; + return StringPiece(data_ + pos, n); +} + +StringPiece::size_type StringPiece::find(const StringPiece& s, + size_type pos) const { + if (pos > size_) return npos; + const_pointer result = std::search(data_ + pos, data_ + size_, + s.data_, s.data_ + s.size_); + size_type xpos = result - data_; + return xpos + s.size_ <= size_ ? xpos : npos; +} + +StringPiece::size_type StringPiece::find(char c, size_type pos) const { + if (size_ <= 0 || pos >= size_) return npos; + const_pointer result = std::find(data_ + pos, data_ + size_, c); + return result != data_ + size_ ? result - data_ : npos; +} + +StringPiece::size_type StringPiece::rfind(const StringPiece& s, + size_type pos) const { + if (size_ < s.size_) return npos; + if (s.size_ == 0) return std::min(size_, pos); + const_pointer last = data_ + std::min(size_ - s.size_, pos) + s.size_; + const_pointer result = std::find_end(data_, last, s.data_, s.data_ + s.size_); + return result != last ? result - data_ : npos; +} + +StringPiece::size_type StringPiece::rfind(char c, size_type pos) const { + if (size_ <= 0) return npos; + for (size_t i = std::min(pos + 1, size_); i != 0;) { + if (data_[--i] == c) return i; + } + return npos; +} + +#if 0 +std::ostream& operator<<(std::ostream& o, const StringPiece& p) { + o.write(p.data(), p.size()); + return o; +} +#endif + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/stringpiece.h b/third_party/opa/wasm/src/re2/re2/stringpiece.h new file mode 100644 index 000000000000..1d9c2d3d2c34 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/stringpiece.h @@ -0,0 +1,210 @@ +// Copyright 2001-2010 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef RE2_STRINGPIECE_H_ +#define RE2_STRINGPIECE_H_ + +// A string-like object that points to a sized piece of memory. +// +// Functions or methods may use const StringPiece& parameters to accept either +// a "const char*" or a "string" value that will be implicitly converted to +// a StringPiece. The implicit conversion means that it is often appropriate +// to include this .h file in other files rather than forward-declaring +// StringPiece as would be appropriate for most other Google classes. +// +// Systematic usage of StringPiece is encouraged as it will reduce unnecessary +// conversions from "const char*" to "string" and back again. +// +// +// Arghh! I wish C++ literals were "string". + +// Doing this simplifies the logic below. +#ifndef __has_include +#define __has_include(x) 0 +#endif + +#include +#include +#include +#include +#include +#include +#if __has_include() && __cplusplus >= 201703L +#include +#endif + +namespace re2 { + +class StringPiece { + public: + typedef std::char_traits traits_type; + typedef char value_type; + typedef char* pointer; + typedef const char* const_pointer; + typedef char& reference; + typedef const char& const_reference; + typedef const char* const_iterator; + typedef const_iterator iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef const_reverse_iterator reverse_iterator; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + static const size_type npos = static_cast(-1); + + // We provide non-explicit singleton constructors so users can pass + // in a "const char*" or a "string" wherever a "StringPiece" is + // expected. + StringPiece() + : data_(NULL), size_(0) {} +#if __has_include() && __cplusplus >= 201703L + StringPiece(const std::string_view& str) + : data_(str.data()), size_(str.size()) {} +#endif + StringPiece(const std::string& str) + : data_(str.data()), size_(str.size()) {} + StringPiece(const char* str) + : data_(str), size_(str == NULL ? 0 : strlen(str)) {} + StringPiece(const char* str, size_type len) + : data_(str), size_(len) {} + + const_iterator begin() const { return data_; } + const_iterator end() const { return data_ + size_; } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(data_ + size_); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(data_); + } + + size_type size() const { return size_; } + size_type length() const { return size_; } + bool empty() const { return size_ == 0; } + + const_reference operator[](size_type i) const { return data_[i]; } + const_pointer data() const { return data_; } + + void remove_prefix(size_type n) { + data_ += n; + size_ -= n; + } + + void remove_suffix(size_type n) { + size_ -= n; + } + + void set(const char* str) { + data_ = str; + size_ = str == NULL ? 0 : strlen(str); + } + + void set(const char* str, size_type len) { + data_ = str; + size_ = len; + } + + // Converts to `std::basic_string`. + template + explicit operator std::basic_string() const { + if (!data_) return {}; + return std::basic_string(data_, size_); + } + + std::string as_string() const { + return std::string(data_, size_); + } + + // We also define ToString() here, since many other string-like + // interfaces name the routine that converts to a C++ string + // "ToString", and it's confusing to have the method that does that + // for a StringPiece be called "as_string()". We also leave the + // "as_string()" method defined here for existing code. + std::string ToString() const { + return std::string(data_, size_); + } + + void CopyToString(std::string* target) const { + target->assign(data_, size_); + } + + void AppendToString(std::string* target) const { + target->append(data_, size_); + } + + size_type copy(char* buf, size_type n, size_type pos = 0) const; + StringPiece substr(size_type pos = 0, size_type n = npos) const; + + int compare(const StringPiece& x) const { + size_type min_size = std::min(size(), x.size()); + if (min_size > 0) { + int r = memcmp(data(), x.data(), min_size); + if (r < 0) return -1; + if (r > 0) return 1; + } + if (size() < x.size()) return -1; + if (size() > x.size()) return 1; + return 0; + } + + // Does "this" start with "x"? + bool starts_with(const StringPiece& x) const { + return x.empty() || + (size() >= x.size() && memcmp(data(), x.data(), x.size()) == 0); + } + + // Does "this" end with "x"? + bool ends_with(const StringPiece& x) const { + return x.empty() || + (size() >= x.size() && + memcmp(data() + (size() - x.size()), x.data(), x.size()) == 0); + } + + bool contains(const StringPiece& s) const { + return find(s) != npos; + } + + size_type find(const StringPiece& s, size_type pos = 0) const; + size_type find(char c, size_type pos = 0) const; + size_type rfind(const StringPiece& s, size_type pos = npos) const; + size_type rfind(char c, size_type pos = npos) const; + + private: + const_pointer data_; + size_type size_; +}; + +inline bool operator==(const StringPiece& x, const StringPiece& y) { + StringPiece::size_type len = x.size(); + if (len != y.size()) return false; + return x.data() == y.data() || len == 0 || + memcmp(x.data(), y.data(), len) == 0; +} + +inline bool operator!=(const StringPiece& x, const StringPiece& y) { + return !(x == y); +} + +inline bool operator<(const StringPiece& x, const StringPiece& y) { + StringPiece::size_type min_size = std::min(x.size(), y.size()); + int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size); + return (r < 0) || (r == 0 && x.size() < y.size()); +} + +inline bool operator>(const StringPiece& x, const StringPiece& y) { + return y < x; +} + +inline bool operator<=(const StringPiece& x, const StringPiece& y) { + return !(x > y); +} + +inline bool operator>=(const StringPiece& x, const StringPiece& y) { + return !(x < y); +} + +// Allow StringPiece to be logged. +std::ostream& operator<<(std::ostream& o, const StringPiece& p); + +} // namespace re2 + +#endif // RE2_STRINGPIECE_H_ diff --git a/third_party/opa/wasm/src/re2/re2/tostring.cc b/third_party/opa/wasm/src/re2/re2/tostring.cc new file mode 100644 index 000000000000..475a8c2bbb2f --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/tostring.cc @@ -0,0 +1,355 @@ +// Copyright 2006 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Format a regular expression structure as a string. +// Tested by parse_test.cc + +#include +#include + +#include "util/util.h" +#include "util/logging.h" +#include "util/strutil.h" +#include "util/utf.h" +#include "re2/regexp.h" +#include "re2/walker-inl.h" + +namespace re2 { + +enum { + PrecAtom, + PrecUnary, + PrecConcat, + PrecAlternate, + PrecEmpty, + PrecParen, + PrecToplevel, +}; + +// Helper function. See description below. +static void AppendCCRange(std::string* t, Rune lo, Rune hi); + +// Walker to generate string in s_. +// The arg pointers are actually integers giving the +// context precedence. +// The child_args are always NULL. +class ToStringWalker : public Regexp::Walker { + public: + explicit ToStringWalker(std::string* t) : t_(t) {} + + virtual int PreVisit(Regexp* re, int parent_arg, bool* stop); + virtual int PostVisit(Regexp* re, int parent_arg, int pre_arg, + int* child_args, int nchild_args); + virtual int ShortVisit(Regexp* re, int parent_arg) { + return 0; + } + + private: + std::string* t_; // The string the walker appends to. + + ToStringWalker(const ToStringWalker&) = delete; + ToStringWalker& operator=(const ToStringWalker&) = delete; +}; + +std::string Regexp::ToString() { + std::string t; + ToStringWalker w(&t); + w.WalkExponential(this, PrecToplevel, 100000); + if (w.stopped_early()) + t += " [truncated]"; + return t; +} + +#define ToString DontCallToString // Avoid accidental recursion. + +// Visits re before children are processed. +// Appends ( if needed and passes new precedence to children. +int ToStringWalker::PreVisit(Regexp* re, int parent_arg, bool* stop) { + int prec = parent_arg; + int nprec = PrecAtom; + + switch (re->op()) { + case kRegexpNoMatch: + case kRegexpEmptyMatch: + case kRegexpLiteral: + case kRegexpAnyChar: + case kRegexpAnyByte: + case kRegexpBeginLine: + case kRegexpEndLine: + case kRegexpBeginText: + case kRegexpEndText: + case kRegexpWordBoundary: + case kRegexpNoWordBoundary: + case kRegexpCharClass: + case kRegexpHaveMatch: + nprec = PrecAtom; + break; + + case kRegexpConcat: + case kRegexpLiteralString: + if (prec < PrecConcat) + t_->append("(?:"); + nprec = PrecConcat; + break; + + case kRegexpAlternate: + if (prec < PrecAlternate) + t_->append("(?:"); + nprec = PrecAlternate; + break; + + case kRegexpCapture: + t_->append("("); +#if 0 + if (re->cap() == 0) + LOG(DFATAL) << "kRegexpCapture cap() == 0"; +#endif + if (re->name()) { + t_->append("?P<"); + t_->append(*re->name()); + t_->append(">"); + } + nprec = PrecParen; + break; + + case kRegexpStar: + case kRegexpPlus: + case kRegexpQuest: + case kRegexpRepeat: + if (prec < PrecUnary) + t_->append("(?:"); + // The subprecedence here is PrecAtom instead of PrecUnary + // because PCRE treats two unary ops in a row as a parse error. + nprec = PrecAtom; + break; + } + + return nprec; +} + +static void AppendLiteral(std::string *t, Rune r, bool foldcase) { + if (r != 0 && r < 0x80 && strchr("(){}[]*+?|.^$\\", r)) { + t->append(1, '\\'); + t->append(1, static_cast(r)); + } else if (foldcase && 'a' <= r && r <= 'z') { + r -= 'a' - 'A'; + t->append(1, '['); + t->append(1, static_cast(r)); + t->append(1, static_cast(r) + 'a' - 'A'); + t->append(1, ']'); + } else { + AppendCCRange(t, r, r); + } +} + +// Visits re after children are processed. +// For childless regexps, all the work is done here. +// For regexps with children, append any unary suffixes or ). +int ToStringWalker::PostVisit(Regexp* re, int parent_arg, int pre_arg, + int* child_args, int nchild_args) { + int prec = parent_arg; + switch (re->op()) { + case kRegexpNoMatch: + // There's no simple symbol for "no match", but + // [^0-Runemax] excludes everything. + t_->append("[^\\x00-\\x{10ffff}]"); + break; + + case kRegexpEmptyMatch: + // Append (?:) to make empty string visible, + // unless this is already being parenthesized. + if (prec < PrecEmpty) + t_->append("(?:)"); + break; + + case kRegexpLiteral: + AppendLiteral(t_, re->rune(), + (re->parse_flags() & Regexp::FoldCase) != 0); + break; + + case kRegexpLiteralString: + for (int i = 0; i < re->nrunes(); i++) + AppendLiteral(t_, re->runes()[i], + (re->parse_flags() & Regexp::FoldCase) != 0); + if (prec < PrecConcat) + t_->append(")"); + break; + + case kRegexpConcat: + if (prec < PrecConcat) + t_->append(")"); + break; + + case kRegexpAlternate: + // Clumsy but workable: the children all appended | + // at the end of their strings, so just remove the last one. + if ((*t_)[t_->size()-1] == '|') + t_->erase(t_->size()-1); +#if 0 + else + LOG(DFATAL) << "Bad final char: " << t_; +#endif + if (prec < PrecAlternate) + t_->append(")"); + break; + + case kRegexpStar: + t_->append("*"); + if (re->parse_flags() & Regexp::NonGreedy) + t_->append("?"); + if (prec < PrecUnary) + t_->append(")"); + break; + + case kRegexpPlus: + t_->append("+"); + if (re->parse_flags() & Regexp::NonGreedy) + t_->append("?"); + if (prec < PrecUnary) + t_->append(")"); + break; + + case kRegexpQuest: + t_->append("?"); + if (re->parse_flags() & Regexp::NonGreedy) + t_->append("?"); + if (prec < PrecUnary) + t_->append(")"); + break; + + case kRegexpRepeat: + if (re->max() == -1) + t_->append(StringPrintf("{%d,}", re->min())); + else if (re->min() == re->max()) + t_->append(StringPrintf("{%d}", re->min())); + else + t_->append(StringPrintf("{%d,%d}", re->min(), re->max())); + if (re->parse_flags() & Regexp::NonGreedy) + t_->append("?"); + if (prec < PrecUnary) + t_->append(")"); + break; + + case kRegexpAnyChar: + t_->append("."); + break; + + case kRegexpAnyByte: + t_->append("\\C"); + break; + + case kRegexpBeginLine: + t_->append("^"); + break; + + case kRegexpEndLine: + t_->append("$"); + break; + + case kRegexpBeginText: + t_->append("(?-m:^)"); + break; + + case kRegexpEndText: + if (re->parse_flags() & Regexp::WasDollar) + t_->append("(?-m:$)"); + else + t_->append("\\z"); + break; + + case kRegexpWordBoundary: + t_->append("\\b"); + break; + + case kRegexpNoWordBoundary: + t_->append("\\B"); + break; + + case kRegexpCharClass: { + if (re->cc()->size() == 0) { + t_->append("[^\\x00-\\x{10ffff}]"); + break; + } + t_->append("["); + // Heuristic: show class as negated if it contains the + // non-character 0xFFFE and yet somehow isn't full. + CharClass* cc = re->cc(); + if (cc->Contains(0xFFFE) && !cc->full()) { + cc = cc->Negate(); + t_->append("^"); + } + for (CharClass::iterator i = cc->begin(); i != cc->end(); ++i) + AppendCCRange(t_, i->lo, i->hi); + if (cc != re->cc()) + cc->Delete(); + t_->append("]"); + break; + } + + case kRegexpCapture: + t_->append(")"); + break; + + case kRegexpHaveMatch: + // There's no syntax accepted by the parser to generate + // this node (it is generated by RE2::Set) so make something + // up that is readable but won't compile. + t_->append("(?HaveMatch:%d)", re->match_id()); + break; + } + + // If the parent is an alternation, append the | for it. + if (prec == PrecAlternate) + t_->append("|"); + + return 0; +} + +// Appends a rune for use in a character class to the string t. +static void AppendCCChar(std::string* t, Rune r) { + if (0x20 <= r && r <= 0x7E) { + if (strchr("[]^-\\", r)) + t->append("\\"); + t->append(1, static_cast(r)); + return; + } + switch (r) { + default: + break; + + case '\r': + t->append("\\r"); + return; + + case '\t': + t->append("\\t"); + return; + + case '\n': + t->append("\\n"); + return; + + case '\f': + t->append("\\f"); + return; + } + + if (r < 0x100) { + *t += StringPrintf("\\x%02x", static_cast(r)); + return; + } + *t += StringPrintf("\\x{%x}", static_cast(r)); +} + +static void AppendCCRange(std::string* t, Rune lo, Rune hi) { + if (lo > hi) + return; + AppendCCChar(t, lo); + if (lo < hi) { + t->append("-"); + AppendCCChar(t, hi); + } +} + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/re2/unicode_casefold.cc b/third_party/opa/wasm/src/re2/re2/unicode_casefold.cc new file mode 100644 index 000000000000..8424107814c9 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/unicode_casefold.cc @@ -0,0 +1,582 @@ + +// GENERATED BY make_unicode_casefold.py; DO NOT EDIT. +// make_unicode_casefold.py >unicode_casefold.cc + +#include "re2/unicode_casefold.h" + +namespace re2 { + + +// 1384 groups, 2798 pairs, 358 ranges +const CaseFold unicode_casefold[] = { + { 65, 90, 32 }, + { 97, 106, -32 }, + { 107, 107, 8383 }, + { 108, 114, -32 }, + { 115, 115, 268 }, + { 116, 122, -32 }, + { 181, 181, 743 }, + { 192, 214, 32 }, + { 216, 222, 32 }, + { 223, 223, 7615 }, + { 224, 228, -32 }, + { 229, 229, 8262 }, + { 230, 246, -32 }, + { 248, 254, -32 }, + { 255, 255, 121 }, + { 256, 303, EvenOdd }, + { 306, 311, EvenOdd }, + { 313, 328, OddEven }, + { 330, 375, EvenOdd }, + { 376, 376, -121 }, + { 377, 382, OddEven }, + { 383, 383, -300 }, + { 384, 384, 195 }, + { 385, 385, 210 }, + { 386, 389, EvenOdd }, + { 390, 390, 206 }, + { 391, 392, OddEven }, + { 393, 394, 205 }, + { 395, 396, OddEven }, + { 398, 398, 79 }, + { 399, 399, 202 }, + { 400, 400, 203 }, + { 401, 402, OddEven }, + { 403, 403, 205 }, + { 404, 404, 207 }, + { 405, 405, 97 }, + { 406, 406, 211 }, + { 407, 407, 209 }, + { 408, 409, EvenOdd }, + { 410, 410, 163 }, + { 412, 412, 211 }, + { 413, 413, 213 }, + { 414, 414, 130 }, + { 415, 415, 214 }, + { 416, 421, EvenOdd }, + { 422, 422, 218 }, + { 423, 424, OddEven }, + { 425, 425, 218 }, + { 428, 429, EvenOdd }, + { 430, 430, 218 }, + { 431, 432, OddEven }, + { 433, 434, 217 }, + { 435, 438, OddEven }, + { 439, 439, 219 }, + { 440, 441, EvenOdd }, + { 444, 445, EvenOdd }, + { 447, 447, 56 }, + { 452, 452, EvenOdd }, + { 453, 453, OddEven }, + { 454, 454, -2 }, + { 455, 455, OddEven }, + { 456, 456, EvenOdd }, + { 457, 457, -2 }, + { 458, 458, EvenOdd }, + { 459, 459, OddEven }, + { 460, 460, -2 }, + { 461, 476, OddEven }, + { 477, 477, -79 }, + { 478, 495, EvenOdd }, + { 497, 497, OddEven }, + { 498, 498, EvenOdd }, + { 499, 499, -2 }, + { 500, 501, EvenOdd }, + { 502, 502, -97 }, + { 503, 503, -56 }, + { 504, 543, EvenOdd }, + { 544, 544, -130 }, + { 546, 563, EvenOdd }, + { 570, 570, 10795 }, + { 571, 572, OddEven }, + { 573, 573, -163 }, + { 574, 574, 10792 }, + { 575, 576, 10815 }, + { 577, 578, OddEven }, + { 579, 579, -195 }, + { 580, 580, 69 }, + { 581, 581, 71 }, + { 582, 591, EvenOdd }, + { 592, 592, 10783 }, + { 593, 593, 10780 }, + { 594, 594, 10782 }, + { 595, 595, -210 }, + { 596, 596, -206 }, + { 598, 599, -205 }, + { 601, 601, -202 }, + { 603, 603, -203 }, + { 604, 604, 42319 }, + { 608, 608, -205 }, + { 609, 609, 42315 }, + { 611, 611, -207 }, + { 613, 613, 42280 }, + { 614, 614, 42308 }, + { 616, 616, -209 }, + { 617, 617, -211 }, + { 618, 618, 42308 }, + { 619, 619, 10743 }, + { 620, 620, 42305 }, + { 623, 623, -211 }, + { 625, 625, 10749 }, + { 626, 626, -213 }, + { 629, 629, -214 }, + { 637, 637, 10727 }, + { 640, 640, -218 }, + { 642, 642, 42307 }, + { 643, 643, -218 }, + { 647, 647, 42282 }, + { 648, 648, -218 }, + { 649, 649, -69 }, + { 650, 651, -217 }, + { 652, 652, -71 }, + { 658, 658, -219 }, + { 669, 669, 42261 }, + { 670, 670, 42258 }, + { 837, 837, 84 }, + { 880, 883, EvenOdd }, + { 886, 887, EvenOdd }, + { 891, 893, 130 }, + { 895, 895, 116 }, + { 902, 902, 38 }, + { 904, 906, 37 }, + { 908, 908, 64 }, + { 910, 911, 63 }, + { 913, 929, 32 }, + { 931, 931, 31 }, + { 932, 939, 32 }, + { 940, 940, -38 }, + { 941, 943, -37 }, + { 945, 945, -32 }, + { 946, 946, 30 }, + { 947, 948, -32 }, + { 949, 949, 64 }, + { 950, 951, -32 }, + { 952, 952, 25 }, + { 953, 953, 7173 }, + { 954, 954, 54 }, + { 955, 955, -32 }, + { 956, 956, -775 }, + { 957, 959, -32 }, + { 960, 960, 22 }, + { 961, 961, 48 }, + { 962, 962, EvenOdd }, + { 963, 965, -32 }, + { 966, 966, 15 }, + { 967, 968, -32 }, + { 969, 969, 7517 }, + { 970, 971, -32 }, + { 972, 972, -64 }, + { 973, 974, -63 }, + { 975, 975, 8 }, + { 976, 976, -62 }, + { 977, 977, 35 }, + { 981, 981, -47 }, + { 982, 982, -54 }, + { 983, 983, -8 }, + { 984, 1007, EvenOdd }, + { 1008, 1008, -86 }, + { 1009, 1009, -80 }, + { 1010, 1010, 7 }, + { 1011, 1011, -116 }, + { 1012, 1012, -92 }, + { 1013, 1013, -96 }, + { 1015, 1016, OddEven }, + { 1017, 1017, -7 }, + { 1018, 1019, EvenOdd }, + { 1021, 1023, -130 }, + { 1024, 1039, 80 }, + { 1040, 1071, 32 }, + { 1072, 1073, -32 }, + { 1074, 1074, 6222 }, + { 1075, 1075, -32 }, + { 1076, 1076, 6221 }, + { 1077, 1085, -32 }, + { 1086, 1086, 6212 }, + { 1087, 1088, -32 }, + { 1089, 1090, 6210 }, + { 1091, 1097, -32 }, + { 1098, 1098, 6204 }, + { 1099, 1103, -32 }, + { 1104, 1119, -80 }, + { 1120, 1122, EvenOdd }, + { 1123, 1123, 6180 }, + { 1124, 1153, EvenOdd }, + { 1162, 1215, EvenOdd }, + { 1216, 1216, 15 }, + { 1217, 1230, OddEven }, + { 1231, 1231, -15 }, + { 1232, 1327, EvenOdd }, + { 1329, 1366, 48 }, + { 1377, 1414, -48 }, + { 4256, 4293, 7264 }, + { 4295, 4295, 7264 }, + { 4301, 4301, 7264 }, + { 4304, 4346, 3008 }, + { 4349, 4351, 3008 }, + { 5024, 5103, 38864 }, + { 5104, 5109, 8 }, + { 5112, 5117, -8 }, + { 7296, 7296, -6254 }, + { 7297, 7297, -6253 }, + { 7298, 7298, -6244 }, + { 7299, 7299, -6242 }, + { 7300, 7300, EvenOdd }, + { 7301, 7301, -6243 }, + { 7302, 7302, -6236 }, + { 7303, 7303, -6181 }, + { 7304, 7304, 35266 }, + { 7312, 7354, -3008 }, + { 7357, 7359, -3008 }, + { 7545, 7545, 35332 }, + { 7549, 7549, 3814 }, + { 7566, 7566, 35384 }, + { 7680, 7776, EvenOdd }, + { 7777, 7777, 58 }, + { 7778, 7829, EvenOdd }, + { 7835, 7835, -59 }, + { 7838, 7838, -7615 }, + { 7840, 7935, EvenOdd }, + { 7936, 7943, 8 }, + { 7944, 7951, -8 }, + { 7952, 7957, 8 }, + { 7960, 7965, -8 }, + { 7968, 7975, 8 }, + { 7976, 7983, -8 }, + { 7984, 7991, 8 }, + { 7992, 7999, -8 }, + { 8000, 8005, 8 }, + { 8008, 8013, -8 }, + { 8017, 8017, 8 }, + { 8019, 8019, 8 }, + { 8021, 8021, 8 }, + { 8023, 8023, 8 }, + { 8025, 8025, -8 }, + { 8027, 8027, -8 }, + { 8029, 8029, -8 }, + { 8031, 8031, -8 }, + { 8032, 8039, 8 }, + { 8040, 8047, -8 }, + { 8048, 8049, 74 }, + { 8050, 8053, 86 }, + { 8054, 8055, 100 }, + { 8056, 8057, 128 }, + { 8058, 8059, 112 }, + { 8060, 8061, 126 }, + { 8064, 8071, 8 }, + { 8072, 8079, -8 }, + { 8080, 8087, 8 }, + { 8088, 8095, -8 }, + { 8096, 8103, 8 }, + { 8104, 8111, -8 }, + { 8112, 8113, 8 }, + { 8115, 8115, 9 }, + { 8120, 8121, -8 }, + { 8122, 8123, -74 }, + { 8124, 8124, -9 }, + { 8126, 8126, -7289 }, + { 8131, 8131, 9 }, + { 8136, 8139, -86 }, + { 8140, 8140, -9 }, + { 8144, 8145, 8 }, + { 8152, 8153, -8 }, + { 8154, 8155, -100 }, + { 8160, 8161, 8 }, + { 8165, 8165, 7 }, + { 8168, 8169, -8 }, + { 8170, 8171, -112 }, + { 8172, 8172, -7 }, + { 8179, 8179, 9 }, + { 8184, 8185, -128 }, + { 8186, 8187, -126 }, + { 8188, 8188, -9 }, + { 8486, 8486, -7549 }, + { 8490, 8490, -8415 }, + { 8491, 8491, -8294 }, + { 8498, 8498, 28 }, + { 8526, 8526, -28 }, + { 8544, 8559, 16 }, + { 8560, 8575, -16 }, + { 8579, 8580, OddEven }, + { 9398, 9423, 26 }, + { 9424, 9449, -26 }, + { 11264, 11310, 48 }, + { 11312, 11358, -48 }, + { 11360, 11361, EvenOdd }, + { 11362, 11362, -10743 }, + { 11363, 11363, -3814 }, + { 11364, 11364, -10727 }, + { 11365, 11365, -10795 }, + { 11366, 11366, -10792 }, + { 11367, 11372, OddEven }, + { 11373, 11373, -10780 }, + { 11374, 11374, -10749 }, + { 11375, 11375, -10783 }, + { 11376, 11376, -10782 }, + { 11378, 11379, EvenOdd }, + { 11381, 11382, OddEven }, + { 11390, 11391, -10815 }, + { 11392, 11491, EvenOdd }, + { 11499, 11502, OddEven }, + { 11506, 11507, EvenOdd }, + { 11520, 11557, -7264 }, + { 11559, 11559, -7264 }, + { 11565, 11565, -7264 }, + { 42560, 42570, EvenOdd }, + { 42571, 42571, -35267 }, + { 42572, 42605, EvenOdd }, + { 42624, 42651, EvenOdd }, + { 42786, 42799, EvenOdd }, + { 42802, 42863, EvenOdd }, + { 42873, 42876, OddEven }, + { 42877, 42877, -35332 }, + { 42878, 42887, EvenOdd }, + { 42891, 42892, OddEven }, + { 42893, 42893, -42280 }, + { 42896, 42899, EvenOdd }, + { 42900, 42900, 48 }, + { 42902, 42921, EvenOdd }, + { 42922, 42922, -42308 }, + { 42923, 42923, -42319 }, + { 42924, 42924, -42315 }, + { 42925, 42925, -42305 }, + { 42926, 42926, -42308 }, + { 42928, 42928, -42258 }, + { 42929, 42929, -42282 }, + { 42930, 42930, -42261 }, + { 42931, 42931, 928 }, + { 42932, 42943, EvenOdd }, + { 42946, 42947, EvenOdd }, + { 42948, 42948, -48 }, + { 42949, 42949, -42307 }, + { 42950, 42950, -35384 }, + { 42951, 42954, OddEven }, + { 42997, 42998, OddEven }, + { 43859, 43859, -928 }, + { 43888, 43967, -38864 }, + { 65313, 65338, 32 }, + { 65345, 65370, -32 }, + { 66560, 66599, 40 }, + { 66600, 66639, -40 }, + { 66736, 66771, 40 }, + { 66776, 66811, -40 }, + { 68736, 68786, 64 }, + { 68800, 68850, -64 }, + { 71840, 71871, 32 }, + { 71872, 71903, -32 }, + { 93760, 93791, 32 }, + { 93792, 93823, -32 }, + { 125184, 125217, 34 }, + { 125218, 125251, -34 }, +}; +const int num_unicode_casefold = 358; + +// 1384 groups, 1414 pairs, 200 ranges +const CaseFold unicode_tolower[] = { + { 65, 90, 32 }, + { 181, 181, 775 }, + { 192, 214, 32 }, + { 216, 222, 32 }, + { 256, 302, EvenOddSkip }, + { 306, 310, EvenOddSkip }, + { 313, 327, OddEvenSkip }, + { 330, 374, EvenOddSkip }, + { 376, 376, -121 }, + { 377, 381, OddEvenSkip }, + { 383, 383, -268 }, + { 385, 385, 210 }, + { 386, 388, EvenOddSkip }, + { 390, 390, 206 }, + { 391, 391, OddEven }, + { 393, 394, 205 }, + { 395, 395, OddEven }, + { 398, 398, 79 }, + { 399, 399, 202 }, + { 400, 400, 203 }, + { 401, 401, OddEven }, + { 403, 403, 205 }, + { 404, 404, 207 }, + { 406, 406, 211 }, + { 407, 407, 209 }, + { 408, 408, EvenOdd }, + { 412, 412, 211 }, + { 413, 413, 213 }, + { 415, 415, 214 }, + { 416, 420, EvenOddSkip }, + { 422, 422, 218 }, + { 423, 423, OddEven }, + { 425, 425, 218 }, + { 428, 428, EvenOdd }, + { 430, 430, 218 }, + { 431, 431, OddEven }, + { 433, 434, 217 }, + { 435, 437, OddEvenSkip }, + { 439, 439, 219 }, + { 440, 440, EvenOdd }, + { 444, 444, EvenOdd }, + { 452, 452, 2 }, + { 453, 453, OddEven }, + { 455, 455, 2 }, + { 456, 456, EvenOdd }, + { 458, 458, 2 }, + { 459, 475, OddEvenSkip }, + { 478, 494, EvenOddSkip }, + { 497, 497, 2 }, + { 498, 500, EvenOddSkip }, + { 502, 502, -97 }, + { 503, 503, -56 }, + { 504, 542, EvenOddSkip }, + { 544, 544, -130 }, + { 546, 562, EvenOddSkip }, + { 570, 570, 10795 }, + { 571, 571, OddEven }, + { 573, 573, -163 }, + { 574, 574, 10792 }, + { 577, 577, OddEven }, + { 579, 579, -195 }, + { 580, 580, 69 }, + { 581, 581, 71 }, + { 582, 590, EvenOddSkip }, + { 837, 837, 116 }, + { 880, 882, EvenOddSkip }, + { 886, 886, EvenOdd }, + { 895, 895, 116 }, + { 902, 902, 38 }, + { 904, 906, 37 }, + { 908, 908, 64 }, + { 910, 911, 63 }, + { 913, 929, 32 }, + { 931, 939, 32 }, + { 962, 962, EvenOdd }, + { 975, 975, 8 }, + { 976, 976, -30 }, + { 977, 977, -25 }, + { 981, 981, -15 }, + { 982, 982, -22 }, + { 984, 1006, EvenOddSkip }, + { 1008, 1008, -54 }, + { 1009, 1009, -48 }, + { 1012, 1012, -60 }, + { 1013, 1013, -64 }, + { 1015, 1015, OddEven }, + { 1017, 1017, -7 }, + { 1018, 1018, EvenOdd }, + { 1021, 1023, -130 }, + { 1024, 1039, 80 }, + { 1040, 1071, 32 }, + { 1120, 1152, EvenOddSkip }, + { 1162, 1214, EvenOddSkip }, + { 1216, 1216, 15 }, + { 1217, 1229, OddEvenSkip }, + { 1232, 1326, EvenOddSkip }, + { 1329, 1366, 48 }, + { 4256, 4293, 7264 }, + { 4295, 4295, 7264 }, + { 4301, 4301, 7264 }, + { 5112, 5117, -8 }, + { 7296, 7296, -6222 }, + { 7297, 7297, -6221 }, + { 7298, 7298, -6212 }, + { 7299, 7300, -6210 }, + { 7301, 7301, -6211 }, + { 7302, 7302, -6204 }, + { 7303, 7303, -6180 }, + { 7304, 7304, 35267 }, + { 7312, 7354, -3008 }, + { 7357, 7359, -3008 }, + { 7680, 7828, EvenOddSkip }, + { 7835, 7835, -58 }, + { 7838, 7838, -7615 }, + { 7840, 7934, EvenOddSkip }, + { 7944, 7951, -8 }, + { 7960, 7965, -8 }, + { 7976, 7983, -8 }, + { 7992, 7999, -8 }, + { 8008, 8013, -8 }, + { 8025, 8025, -8 }, + { 8027, 8027, -8 }, + { 8029, 8029, -8 }, + { 8031, 8031, -8 }, + { 8040, 8047, -8 }, + { 8072, 8079, -8 }, + { 8088, 8095, -8 }, + { 8104, 8111, -8 }, + { 8120, 8121, -8 }, + { 8122, 8123, -74 }, + { 8124, 8124, -9 }, + { 8126, 8126, -7173 }, + { 8136, 8139, -86 }, + { 8140, 8140, -9 }, + { 8152, 8153, -8 }, + { 8154, 8155, -100 }, + { 8168, 8169, -8 }, + { 8170, 8171, -112 }, + { 8172, 8172, -7 }, + { 8184, 8185, -128 }, + { 8186, 8187, -126 }, + { 8188, 8188, -9 }, + { 8486, 8486, -7517 }, + { 8490, 8490, -8383 }, + { 8491, 8491, -8262 }, + { 8498, 8498, 28 }, + { 8544, 8559, 16 }, + { 8579, 8579, OddEven }, + { 9398, 9423, 26 }, + { 11264, 11310, 48 }, + { 11360, 11360, EvenOdd }, + { 11362, 11362, -10743 }, + { 11363, 11363, -3814 }, + { 11364, 11364, -10727 }, + { 11367, 11371, OddEvenSkip }, + { 11373, 11373, -10780 }, + { 11374, 11374, -10749 }, + { 11375, 11375, -10783 }, + { 11376, 11376, -10782 }, + { 11378, 11378, EvenOdd }, + { 11381, 11381, OddEven }, + { 11390, 11391, -10815 }, + { 11392, 11490, EvenOddSkip }, + { 11499, 11501, OddEvenSkip }, + { 11506, 11506, EvenOdd }, + { 42560, 42604, EvenOddSkip }, + { 42624, 42650, EvenOddSkip }, + { 42786, 42798, EvenOddSkip }, + { 42802, 42862, EvenOddSkip }, + { 42873, 42875, OddEvenSkip }, + { 42877, 42877, -35332 }, + { 42878, 42886, EvenOddSkip }, + { 42891, 42891, OddEven }, + { 42893, 42893, -42280 }, + { 42896, 42898, EvenOddSkip }, + { 42902, 42920, EvenOddSkip }, + { 42922, 42922, -42308 }, + { 42923, 42923, -42319 }, + { 42924, 42924, -42315 }, + { 42925, 42925, -42305 }, + { 42926, 42926, -42308 }, + { 42928, 42928, -42258 }, + { 42929, 42929, -42282 }, + { 42930, 42930, -42261 }, + { 42931, 42931, 928 }, + { 42932, 42942, EvenOddSkip }, + { 42946, 42946, EvenOdd }, + { 42948, 42948, -48 }, + { 42949, 42949, -42307 }, + { 42950, 42950, -35384 }, + { 42951, 42953, OddEvenSkip }, + { 42997, 42997, OddEven }, + { 43888, 43967, -38864 }, + { 65313, 65338, 32 }, + { 66560, 66599, 40 }, + { 66736, 66771, 40 }, + { 68736, 68786, 64 }, + { 71840, 71871, 32 }, + { 93760, 93791, 32 }, + { 125184, 125217, 34 }, +}; +const int num_unicode_tolower = 200; + + + +} // namespace re2 + + diff --git a/third_party/opa/wasm/src/re2/re2/unicode_casefold.h b/third_party/opa/wasm/src/re2/re2/unicode_casefold.h new file mode 100644 index 000000000000..8bdbb42fbc12 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/unicode_casefold.h @@ -0,0 +1,78 @@ +// Copyright 2008 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef RE2_UNICODE_CASEFOLD_H_ +#define RE2_UNICODE_CASEFOLD_H_ + +// Unicode case folding tables. + +// The Unicode case folding tables encode the mapping from one Unicode point +// to the next largest Unicode point with equivalent folding. The largest +// point wraps back to the first. For example, the tables map: +// +// 'A' -> 'a' +// 'a' -> 'A' +// +// 'K' -> 'k' +// 'k' -> 'K' (Kelvin symbol) +// 'K' -> 'K' +// +// Like everything Unicode, these tables are big. If we represent the table +// as a sorted list of uint32_t pairs, it has 2049 entries and is 16 kB. +// Most table entries look like the ones around them: +// 'A' maps to 'A'+32, 'B' maps to 'B'+32, etc. +// Instead of listing all the pairs explicitly, we make a list of ranges +// and deltas, so that the table entries for 'A' through 'Z' can be represented +// as a single entry { 'A', 'Z', +32 }. +// +// In addition to blocks that map to each other (A-Z mapping to a-z) +// there are blocks of pairs that individually map to each other +// (for example, 0100<->0101, 0102<->0103, 0104<->0105, ...). +// For those, the special delta value EvenOdd marks even/odd pairs +// (if even, add 1; if odd, subtract 1), and OddEven marks odd/even pairs. +// +// In this form, the table has 274 entries, about 3kB. If we were to split +// the table into one for 16-bit codes and an overflow table for larger ones, +// we could get it down to about 1.5kB, but that's not worth the complexity. +// +// The grouped form also allows for efficient fold range calculations +// rather than looping one character at a time. + +#include + +#include "util/util.h" +#include "util/utf.h" + +namespace re2 { + +enum { + EvenOdd = 1, + OddEven = -1, + EvenOddSkip = 1<<30, + OddEvenSkip, +}; + +struct CaseFold { + Rune lo; + Rune hi; + int32_t delta; +}; + +extern const CaseFold unicode_casefold[]; +extern const int num_unicode_casefold; + +extern const CaseFold unicode_tolower[]; +extern const int num_unicode_tolower; + +// Returns the CaseFold* in the tables that contains rune. +// If rune is not in the tables, returns the first CaseFold* after rune. +// If rune is larger than any value in the tables, returns NULL. +extern const CaseFold* LookupCaseFold(const CaseFold*, int, Rune rune); + +// Returns the result of applying the fold f to the rune r. +extern Rune ApplyFold(const CaseFold *f, Rune r); + +} // namespace re2 + +#endif // RE2_UNICODE_CASEFOLD_H_ diff --git a/third_party/opa/wasm/src/re2/re2/unicode_groups.cc b/third_party/opa/wasm/src/re2/re2/unicode_groups.cc new file mode 100644 index 000000000000..7b7a3c6a5649 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/unicode_groups.cc @@ -0,0 +1,6269 @@ + +// GENERATED BY make_unicode_groups.py; DO NOT EDIT. +// make_unicode_groups.py >unicode_groups.cc + +#include "re2/unicode_groups.h" + +namespace re2 { + + +static const URange16 C_range16[] = { + { 0, 31 }, + { 127, 159 }, + { 173, 173 }, + { 1536, 1541 }, + { 1564, 1564 }, + { 1757, 1757 }, + { 1807, 1807 }, + { 2274, 2274 }, + { 6158, 6158 }, + { 8203, 8207 }, + { 8234, 8238 }, + { 8288, 8292 }, + { 8294, 8303 }, + { 55296, 63743 }, + { 65279, 65279 }, + { 65529, 65531 }, +}; +static const URange32 C_range32[] = { + { 69821, 69821 }, + { 69837, 69837 }, + { 78896, 78904 }, + { 113824, 113827 }, + { 119155, 119162 }, + { 917505, 917505 }, + { 917536, 917631 }, + { 983040, 1048573 }, + { 1048576, 1114109 }, +}; +static const URange16 Cc_range16[] = { + { 0, 31 }, + { 127, 159 }, +}; +static const URange16 Cf_range16[] = { + { 173, 173 }, + { 1536, 1541 }, + { 1564, 1564 }, + { 1757, 1757 }, + { 1807, 1807 }, + { 2274, 2274 }, + { 6158, 6158 }, + { 8203, 8207 }, + { 8234, 8238 }, + { 8288, 8292 }, + { 8294, 8303 }, + { 65279, 65279 }, + { 65529, 65531 }, +}; +static const URange32 Cf_range32[] = { + { 69821, 69821 }, + { 69837, 69837 }, + { 78896, 78904 }, + { 113824, 113827 }, + { 119155, 119162 }, + { 917505, 917505 }, + { 917536, 917631 }, +}; +static const URange16 Co_range16[] = { + { 57344, 63743 }, +}; +static const URange32 Co_range32[] = { + { 983040, 1048573 }, + { 1048576, 1114109 }, +}; +static const URange16 Cs_range16[] = { + { 55296, 57343 }, +}; +static const URange16 L_range16[] = { + { 65, 90 }, + { 97, 122 }, + { 170, 170 }, + { 181, 181 }, + { 186, 186 }, + { 192, 214 }, + { 216, 246 }, + { 248, 705 }, + { 710, 721 }, + { 736, 740 }, + { 748, 748 }, + { 750, 750 }, + { 880, 884 }, + { 886, 887 }, + { 890, 893 }, + { 895, 895 }, + { 902, 902 }, + { 904, 906 }, + { 908, 908 }, + { 910, 929 }, + { 931, 1013 }, + { 1015, 1153 }, + { 1162, 1327 }, + { 1329, 1366 }, + { 1369, 1369 }, + { 1376, 1416 }, + { 1488, 1514 }, + { 1519, 1522 }, + { 1568, 1610 }, + { 1646, 1647 }, + { 1649, 1747 }, + { 1749, 1749 }, + { 1765, 1766 }, + { 1774, 1775 }, + { 1786, 1788 }, + { 1791, 1791 }, + { 1808, 1808 }, + { 1810, 1839 }, + { 1869, 1957 }, + { 1969, 1969 }, + { 1994, 2026 }, + { 2036, 2037 }, + { 2042, 2042 }, + { 2048, 2069 }, + { 2074, 2074 }, + { 2084, 2084 }, + { 2088, 2088 }, + { 2112, 2136 }, + { 2144, 2154 }, + { 2208, 2228 }, + { 2230, 2247 }, + { 2308, 2361 }, + { 2365, 2365 }, + { 2384, 2384 }, + { 2392, 2401 }, + { 2417, 2432 }, + { 2437, 2444 }, + { 2447, 2448 }, + { 2451, 2472 }, + { 2474, 2480 }, + { 2482, 2482 }, + { 2486, 2489 }, + { 2493, 2493 }, + { 2510, 2510 }, + { 2524, 2525 }, + { 2527, 2529 }, + { 2544, 2545 }, + { 2556, 2556 }, + { 2565, 2570 }, + { 2575, 2576 }, + { 2579, 2600 }, + { 2602, 2608 }, + { 2610, 2611 }, + { 2613, 2614 }, + { 2616, 2617 }, + { 2649, 2652 }, + { 2654, 2654 }, + { 2674, 2676 }, + { 2693, 2701 }, + { 2703, 2705 }, + { 2707, 2728 }, + { 2730, 2736 }, + { 2738, 2739 }, + { 2741, 2745 }, + { 2749, 2749 }, + { 2768, 2768 }, + { 2784, 2785 }, + { 2809, 2809 }, + { 2821, 2828 }, + { 2831, 2832 }, + { 2835, 2856 }, + { 2858, 2864 }, + { 2866, 2867 }, + { 2869, 2873 }, + { 2877, 2877 }, + { 2908, 2909 }, + { 2911, 2913 }, + { 2929, 2929 }, + { 2947, 2947 }, + { 2949, 2954 }, + { 2958, 2960 }, + { 2962, 2965 }, + { 2969, 2970 }, + { 2972, 2972 }, + { 2974, 2975 }, + { 2979, 2980 }, + { 2984, 2986 }, + { 2990, 3001 }, + { 3024, 3024 }, + { 3077, 3084 }, + { 3086, 3088 }, + { 3090, 3112 }, + { 3114, 3129 }, + { 3133, 3133 }, + { 3160, 3162 }, + { 3168, 3169 }, + { 3200, 3200 }, + { 3205, 3212 }, + { 3214, 3216 }, + { 3218, 3240 }, + { 3242, 3251 }, + { 3253, 3257 }, + { 3261, 3261 }, + { 3294, 3294 }, + { 3296, 3297 }, + { 3313, 3314 }, + { 3332, 3340 }, + { 3342, 3344 }, + { 3346, 3386 }, + { 3389, 3389 }, + { 3406, 3406 }, + { 3412, 3414 }, + { 3423, 3425 }, + { 3450, 3455 }, + { 3461, 3478 }, + { 3482, 3505 }, + { 3507, 3515 }, + { 3517, 3517 }, + { 3520, 3526 }, + { 3585, 3632 }, + { 3634, 3635 }, + { 3648, 3654 }, + { 3713, 3714 }, + { 3716, 3716 }, + { 3718, 3722 }, + { 3724, 3747 }, + { 3749, 3749 }, + { 3751, 3760 }, + { 3762, 3763 }, + { 3773, 3773 }, + { 3776, 3780 }, + { 3782, 3782 }, + { 3804, 3807 }, + { 3840, 3840 }, + { 3904, 3911 }, + { 3913, 3948 }, + { 3976, 3980 }, + { 4096, 4138 }, + { 4159, 4159 }, + { 4176, 4181 }, + { 4186, 4189 }, + { 4193, 4193 }, + { 4197, 4198 }, + { 4206, 4208 }, + { 4213, 4225 }, + { 4238, 4238 }, + { 4256, 4293 }, + { 4295, 4295 }, + { 4301, 4301 }, + { 4304, 4346 }, + { 4348, 4680 }, + { 4682, 4685 }, + { 4688, 4694 }, + { 4696, 4696 }, + { 4698, 4701 }, + { 4704, 4744 }, + { 4746, 4749 }, + { 4752, 4784 }, + { 4786, 4789 }, + { 4792, 4798 }, + { 4800, 4800 }, + { 4802, 4805 }, + { 4808, 4822 }, + { 4824, 4880 }, + { 4882, 4885 }, + { 4888, 4954 }, + { 4992, 5007 }, + { 5024, 5109 }, + { 5112, 5117 }, + { 5121, 5740 }, + { 5743, 5759 }, + { 5761, 5786 }, + { 5792, 5866 }, + { 5873, 5880 }, + { 5888, 5900 }, + { 5902, 5905 }, + { 5920, 5937 }, + { 5952, 5969 }, + { 5984, 5996 }, + { 5998, 6000 }, + { 6016, 6067 }, + { 6103, 6103 }, + { 6108, 6108 }, + { 6176, 6264 }, + { 6272, 6276 }, + { 6279, 6312 }, + { 6314, 6314 }, + { 6320, 6389 }, + { 6400, 6430 }, + { 6480, 6509 }, + { 6512, 6516 }, + { 6528, 6571 }, + { 6576, 6601 }, + { 6656, 6678 }, + { 6688, 6740 }, + { 6823, 6823 }, + { 6917, 6963 }, + { 6981, 6987 }, + { 7043, 7072 }, + { 7086, 7087 }, + { 7098, 7141 }, + { 7168, 7203 }, + { 7245, 7247 }, + { 7258, 7293 }, + { 7296, 7304 }, + { 7312, 7354 }, + { 7357, 7359 }, + { 7401, 7404 }, + { 7406, 7411 }, + { 7413, 7414 }, + { 7418, 7418 }, + { 7424, 7615 }, + { 7680, 7957 }, + { 7960, 7965 }, + { 7968, 8005 }, + { 8008, 8013 }, + { 8016, 8023 }, + { 8025, 8025 }, + { 8027, 8027 }, + { 8029, 8029 }, + { 8031, 8061 }, + { 8064, 8116 }, + { 8118, 8124 }, + { 8126, 8126 }, + { 8130, 8132 }, + { 8134, 8140 }, + { 8144, 8147 }, + { 8150, 8155 }, + { 8160, 8172 }, + { 8178, 8180 }, + { 8182, 8188 }, + { 8305, 8305 }, + { 8319, 8319 }, + { 8336, 8348 }, + { 8450, 8450 }, + { 8455, 8455 }, + { 8458, 8467 }, + { 8469, 8469 }, + { 8473, 8477 }, + { 8484, 8484 }, + { 8486, 8486 }, + { 8488, 8488 }, + { 8490, 8493 }, + { 8495, 8505 }, + { 8508, 8511 }, + { 8517, 8521 }, + { 8526, 8526 }, + { 8579, 8580 }, + { 11264, 11310 }, + { 11312, 11358 }, + { 11360, 11492 }, + { 11499, 11502 }, + { 11506, 11507 }, + { 11520, 11557 }, + { 11559, 11559 }, + { 11565, 11565 }, + { 11568, 11623 }, + { 11631, 11631 }, + { 11648, 11670 }, + { 11680, 11686 }, + { 11688, 11694 }, + { 11696, 11702 }, + { 11704, 11710 }, + { 11712, 11718 }, + { 11720, 11726 }, + { 11728, 11734 }, + { 11736, 11742 }, + { 11823, 11823 }, + { 12293, 12294 }, + { 12337, 12341 }, + { 12347, 12348 }, + { 12353, 12438 }, + { 12445, 12447 }, + { 12449, 12538 }, + { 12540, 12543 }, + { 12549, 12591 }, + { 12593, 12686 }, + { 12704, 12735 }, + { 12784, 12799 }, + { 13312, 19903 }, + { 19968, 40956 }, + { 40960, 42124 }, + { 42192, 42237 }, + { 42240, 42508 }, + { 42512, 42527 }, + { 42538, 42539 }, + { 42560, 42606 }, + { 42623, 42653 }, + { 42656, 42725 }, + { 42775, 42783 }, + { 42786, 42888 }, + { 42891, 42943 }, + { 42946, 42954 }, + { 42997, 43009 }, + { 43011, 43013 }, + { 43015, 43018 }, + { 43020, 43042 }, + { 43072, 43123 }, + { 43138, 43187 }, + { 43250, 43255 }, + { 43259, 43259 }, + { 43261, 43262 }, + { 43274, 43301 }, + { 43312, 43334 }, + { 43360, 43388 }, + { 43396, 43442 }, + { 43471, 43471 }, + { 43488, 43492 }, + { 43494, 43503 }, + { 43514, 43518 }, + { 43520, 43560 }, + { 43584, 43586 }, + { 43588, 43595 }, + { 43616, 43638 }, + { 43642, 43642 }, + { 43646, 43695 }, + { 43697, 43697 }, + { 43701, 43702 }, + { 43705, 43709 }, + { 43712, 43712 }, + { 43714, 43714 }, + { 43739, 43741 }, + { 43744, 43754 }, + { 43762, 43764 }, + { 43777, 43782 }, + { 43785, 43790 }, + { 43793, 43798 }, + { 43808, 43814 }, + { 43816, 43822 }, + { 43824, 43866 }, + { 43868, 43881 }, + { 43888, 44002 }, + { 44032, 55203 }, + { 55216, 55238 }, + { 55243, 55291 }, + { 63744, 64109 }, + { 64112, 64217 }, + { 64256, 64262 }, + { 64275, 64279 }, + { 64285, 64285 }, + { 64287, 64296 }, + { 64298, 64310 }, + { 64312, 64316 }, + { 64318, 64318 }, + { 64320, 64321 }, + { 64323, 64324 }, + { 64326, 64433 }, + { 64467, 64829 }, + { 64848, 64911 }, + { 64914, 64967 }, + { 65008, 65019 }, + { 65136, 65140 }, + { 65142, 65276 }, + { 65313, 65338 }, + { 65345, 65370 }, + { 65382, 65470 }, + { 65474, 65479 }, + { 65482, 65487 }, + { 65490, 65495 }, + { 65498, 65500 }, +}; +static const URange32 L_range32[] = { + { 65536, 65547 }, + { 65549, 65574 }, + { 65576, 65594 }, + { 65596, 65597 }, + { 65599, 65613 }, + { 65616, 65629 }, + { 65664, 65786 }, + { 66176, 66204 }, + { 66208, 66256 }, + { 66304, 66335 }, + { 66349, 66368 }, + { 66370, 66377 }, + { 66384, 66421 }, + { 66432, 66461 }, + { 66464, 66499 }, + { 66504, 66511 }, + { 66560, 66717 }, + { 66736, 66771 }, + { 66776, 66811 }, + { 66816, 66855 }, + { 66864, 66915 }, + { 67072, 67382 }, + { 67392, 67413 }, + { 67424, 67431 }, + { 67584, 67589 }, + { 67592, 67592 }, + { 67594, 67637 }, + { 67639, 67640 }, + { 67644, 67644 }, + { 67647, 67669 }, + { 67680, 67702 }, + { 67712, 67742 }, + { 67808, 67826 }, + { 67828, 67829 }, + { 67840, 67861 }, + { 67872, 67897 }, + { 67968, 68023 }, + { 68030, 68031 }, + { 68096, 68096 }, + { 68112, 68115 }, + { 68117, 68119 }, + { 68121, 68149 }, + { 68192, 68220 }, + { 68224, 68252 }, + { 68288, 68295 }, + { 68297, 68324 }, + { 68352, 68405 }, + { 68416, 68437 }, + { 68448, 68466 }, + { 68480, 68497 }, + { 68608, 68680 }, + { 68736, 68786 }, + { 68800, 68850 }, + { 68864, 68899 }, + { 69248, 69289 }, + { 69296, 69297 }, + { 69376, 69404 }, + { 69415, 69415 }, + { 69424, 69445 }, + { 69552, 69572 }, + { 69600, 69622 }, + { 69635, 69687 }, + { 69763, 69807 }, + { 69840, 69864 }, + { 69891, 69926 }, + { 69956, 69956 }, + { 69959, 69959 }, + { 69968, 70002 }, + { 70006, 70006 }, + { 70019, 70066 }, + { 70081, 70084 }, + { 70106, 70106 }, + { 70108, 70108 }, + { 70144, 70161 }, + { 70163, 70187 }, + { 70272, 70278 }, + { 70280, 70280 }, + { 70282, 70285 }, + { 70287, 70301 }, + { 70303, 70312 }, + { 70320, 70366 }, + { 70405, 70412 }, + { 70415, 70416 }, + { 70419, 70440 }, + { 70442, 70448 }, + { 70450, 70451 }, + { 70453, 70457 }, + { 70461, 70461 }, + { 70480, 70480 }, + { 70493, 70497 }, + { 70656, 70708 }, + { 70727, 70730 }, + { 70751, 70753 }, + { 70784, 70831 }, + { 70852, 70853 }, + { 70855, 70855 }, + { 71040, 71086 }, + { 71128, 71131 }, + { 71168, 71215 }, + { 71236, 71236 }, + { 71296, 71338 }, + { 71352, 71352 }, + { 71424, 71450 }, + { 71680, 71723 }, + { 71840, 71903 }, + { 71935, 71942 }, + { 71945, 71945 }, + { 71948, 71955 }, + { 71957, 71958 }, + { 71960, 71983 }, + { 71999, 71999 }, + { 72001, 72001 }, + { 72096, 72103 }, + { 72106, 72144 }, + { 72161, 72161 }, + { 72163, 72163 }, + { 72192, 72192 }, + { 72203, 72242 }, + { 72250, 72250 }, + { 72272, 72272 }, + { 72284, 72329 }, + { 72349, 72349 }, + { 72384, 72440 }, + { 72704, 72712 }, + { 72714, 72750 }, + { 72768, 72768 }, + { 72818, 72847 }, + { 72960, 72966 }, + { 72968, 72969 }, + { 72971, 73008 }, + { 73030, 73030 }, + { 73056, 73061 }, + { 73063, 73064 }, + { 73066, 73097 }, + { 73112, 73112 }, + { 73440, 73458 }, + { 73648, 73648 }, + { 73728, 74649 }, + { 74880, 75075 }, + { 77824, 78894 }, + { 82944, 83526 }, + { 92160, 92728 }, + { 92736, 92766 }, + { 92880, 92909 }, + { 92928, 92975 }, + { 92992, 92995 }, + { 93027, 93047 }, + { 93053, 93071 }, + { 93760, 93823 }, + { 93952, 94026 }, + { 94032, 94032 }, + { 94099, 94111 }, + { 94176, 94177 }, + { 94179, 94179 }, + { 94208, 100343 }, + { 100352, 101589 }, + { 101632, 101640 }, + { 110592, 110878 }, + { 110928, 110930 }, + { 110948, 110951 }, + { 110960, 111355 }, + { 113664, 113770 }, + { 113776, 113788 }, + { 113792, 113800 }, + { 113808, 113817 }, + { 119808, 119892 }, + { 119894, 119964 }, + { 119966, 119967 }, + { 119970, 119970 }, + { 119973, 119974 }, + { 119977, 119980 }, + { 119982, 119993 }, + { 119995, 119995 }, + { 119997, 120003 }, + { 120005, 120069 }, + { 120071, 120074 }, + { 120077, 120084 }, + { 120086, 120092 }, + { 120094, 120121 }, + { 120123, 120126 }, + { 120128, 120132 }, + { 120134, 120134 }, + { 120138, 120144 }, + { 120146, 120485 }, + { 120488, 120512 }, + { 120514, 120538 }, + { 120540, 120570 }, + { 120572, 120596 }, + { 120598, 120628 }, + { 120630, 120654 }, + { 120656, 120686 }, + { 120688, 120712 }, + { 120714, 120744 }, + { 120746, 120770 }, + { 120772, 120779 }, + { 123136, 123180 }, + { 123191, 123197 }, + { 123214, 123214 }, + { 123584, 123627 }, + { 124928, 125124 }, + { 125184, 125251 }, + { 125259, 125259 }, + { 126464, 126467 }, + { 126469, 126495 }, + { 126497, 126498 }, + { 126500, 126500 }, + { 126503, 126503 }, + { 126505, 126514 }, + { 126516, 126519 }, + { 126521, 126521 }, + { 126523, 126523 }, + { 126530, 126530 }, + { 126535, 126535 }, + { 126537, 126537 }, + { 126539, 126539 }, + { 126541, 126543 }, + { 126545, 126546 }, + { 126548, 126548 }, + { 126551, 126551 }, + { 126553, 126553 }, + { 126555, 126555 }, + { 126557, 126557 }, + { 126559, 126559 }, + { 126561, 126562 }, + { 126564, 126564 }, + { 126567, 126570 }, + { 126572, 126578 }, + { 126580, 126583 }, + { 126585, 126588 }, + { 126590, 126590 }, + { 126592, 126601 }, + { 126603, 126619 }, + { 126625, 126627 }, + { 126629, 126633 }, + { 126635, 126651 }, + { 131072, 173789 }, + { 173824, 177972 }, + { 177984, 178205 }, + { 178208, 183969 }, + { 183984, 191456 }, + { 194560, 195101 }, + { 196608, 201546 }, +}; +static const URange16 Ll_range16[] = { + { 97, 122 }, + { 181, 181 }, + { 223, 246 }, + { 248, 255 }, + { 257, 257 }, + { 259, 259 }, + { 261, 261 }, + { 263, 263 }, + { 265, 265 }, + { 267, 267 }, + { 269, 269 }, + { 271, 271 }, + { 273, 273 }, + { 275, 275 }, + { 277, 277 }, + { 279, 279 }, + { 281, 281 }, + { 283, 283 }, + { 285, 285 }, + { 287, 287 }, + { 289, 289 }, + { 291, 291 }, + { 293, 293 }, + { 295, 295 }, + { 297, 297 }, + { 299, 299 }, + { 301, 301 }, + { 303, 303 }, + { 305, 305 }, + { 307, 307 }, + { 309, 309 }, + { 311, 312 }, + { 314, 314 }, + { 316, 316 }, + { 318, 318 }, + { 320, 320 }, + { 322, 322 }, + { 324, 324 }, + { 326, 326 }, + { 328, 329 }, + { 331, 331 }, + { 333, 333 }, + { 335, 335 }, + { 337, 337 }, + { 339, 339 }, + { 341, 341 }, + { 343, 343 }, + { 345, 345 }, + { 347, 347 }, + { 349, 349 }, + { 351, 351 }, + { 353, 353 }, + { 355, 355 }, + { 357, 357 }, + { 359, 359 }, + { 361, 361 }, + { 363, 363 }, + { 365, 365 }, + { 367, 367 }, + { 369, 369 }, + { 371, 371 }, + { 373, 373 }, + { 375, 375 }, + { 378, 378 }, + { 380, 380 }, + { 382, 384 }, + { 387, 387 }, + { 389, 389 }, + { 392, 392 }, + { 396, 397 }, + { 402, 402 }, + { 405, 405 }, + { 409, 411 }, + { 414, 414 }, + { 417, 417 }, + { 419, 419 }, + { 421, 421 }, + { 424, 424 }, + { 426, 427 }, + { 429, 429 }, + { 432, 432 }, + { 436, 436 }, + { 438, 438 }, + { 441, 442 }, + { 445, 447 }, + { 454, 454 }, + { 457, 457 }, + { 460, 460 }, + { 462, 462 }, + { 464, 464 }, + { 466, 466 }, + { 468, 468 }, + { 470, 470 }, + { 472, 472 }, + { 474, 474 }, + { 476, 477 }, + { 479, 479 }, + { 481, 481 }, + { 483, 483 }, + { 485, 485 }, + { 487, 487 }, + { 489, 489 }, + { 491, 491 }, + { 493, 493 }, + { 495, 496 }, + { 499, 499 }, + { 501, 501 }, + { 505, 505 }, + { 507, 507 }, + { 509, 509 }, + { 511, 511 }, + { 513, 513 }, + { 515, 515 }, + { 517, 517 }, + { 519, 519 }, + { 521, 521 }, + { 523, 523 }, + { 525, 525 }, + { 527, 527 }, + { 529, 529 }, + { 531, 531 }, + { 533, 533 }, + { 535, 535 }, + { 537, 537 }, + { 539, 539 }, + { 541, 541 }, + { 543, 543 }, + { 545, 545 }, + { 547, 547 }, + { 549, 549 }, + { 551, 551 }, + { 553, 553 }, + { 555, 555 }, + { 557, 557 }, + { 559, 559 }, + { 561, 561 }, + { 563, 569 }, + { 572, 572 }, + { 575, 576 }, + { 578, 578 }, + { 583, 583 }, + { 585, 585 }, + { 587, 587 }, + { 589, 589 }, + { 591, 659 }, + { 661, 687 }, + { 881, 881 }, + { 883, 883 }, + { 887, 887 }, + { 891, 893 }, + { 912, 912 }, + { 940, 974 }, + { 976, 977 }, + { 981, 983 }, + { 985, 985 }, + { 987, 987 }, + { 989, 989 }, + { 991, 991 }, + { 993, 993 }, + { 995, 995 }, + { 997, 997 }, + { 999, 999 }, + { 1001, 1001 }, + { 1003, 1003 }, + { 1005, 1005 }, + { 1007, 1011 }, + { 1013, 1013 }, + { 1016, 1016 }, + { 1019, 1020 }, + { 1072, 1119 }, + { 1121, 1121 }, + { 1123, 1123 }, + { 1125, 1125 }, + { 1127, 1127 }, + { 1129, 1129 }, + { 1131, 1131 }, + { 1133, 1133 }, + { 1135, 1135 }, + { 1137, 1137 }, + { 1139, 1139 }, + { 1141, 1141 }, + { 1143, 1143 }, + { 1145, 1145 }, + { 1147, 1147 }, + { 1149, 1149 }, + { 1151, 1151 }, + { 1153, 1153 }, + { 1163, 1163 }, + { 1165, 1165 }, + { 1167, 1167 }, + { 1169, 1169 }, + { 1171, 1171 }, + { 1173, 1173 }, + { 1175, 1175 }, + { 1177, 1177 }, + { 1179, 1179 }, + { 1181, 1181 }, + { 1183, 1183 }, + { 1185, 1185 }, + { 1187, 1187 }, + { 1189, 1189 }, + { 1191, 1191 }, + { 1193, 1193 }, + { 1195, 1195 }, + { 1197, 1197 }, + { 1199, 1199 }, + { 1201, 1201 }, + { 1203, 1203 }, + { 1205, 1205 }, + { 1207, 1207 }, + { 1209, 1209 }, + { 1211, 1211 }, + { 1213, 1213 }, + { 1215, 1215 }, + { 1218, 1218 }, + { 1220, 1220 }, + { 1222, 1222 }, + { 1224, 1224 }, + { 1226, 1226 }, + { 1228, 1228 }, + { 1230, 1231 }, + { 1233, 1233 }, + { 1235, 1235 }, + { 1237, 1237 }, + { 1239, 1239 }, + { 1241, 1241 }, + { 1243, 1243 }, + { 1245, 1245 }, + { 1247, 1247 }, + { 1249, 1249 }, + { 1251, 1251 }, + { 1253, 1253 }, + { 1255, 1255 }, + { 1257, 1257 }, + { 1259, 1259 }, + { 1261, 1261 }, + { 1263, 1263 }, + { 1265, 1265 }, + { 1267, 1267 }, + { 1269, 1269 }, + { 1271, 1271 }, + { 1273, 1273 }, + { 1275, 1275 }, + { 1277, 1277 }, + { 1279, 1279 }, + { 1281, 1281 }, + { 1283, 1283 }, + { 1285, 1285 }, + { 1287, 1287 }, + { 1289, 1289 }, + { 1291, 1291 }, + { 1293, 1293 }, + { 1295, 1295 }, + { 1297, 1297 }, + { 1299, 1299 }, + { 1301, 1301 }, + { 1303, 1303 }, + { 1305, 1305 }, + { 1307, 1307 }, + { 1309, 1309 }, + { 1311, 1311 }, + { 1313, 1313 }, + { 1315, 1315 }, + { 1317, 1317 }, + { 1319, 1319 }, + { 1321, 1321 }, + { 1323, 1323 }, + { 1325, 1325 }, + { 1327, 1327 }, + { 1376, 1416 }, + { 4304, 4346 }, + { 4349, 4351 }, + { 5112, 5117 }, + { 7296, 7304 }, + { 7424, 7467 }, + { 7531, 7543 }, + { 7545, 7578 }, + { 7681, 7681 }, + { 7683, 7683 }, + { 7685, 7685 }, + { 7687, 7687 }, + { 7689, 7689 }, + { 7691, 7691 }, + { 7693, 7693 }, + { 7695, 7695 }, + { 7697, 7697 }, + { 7699, 7699 }, + { 7701, 7701 }, + { 7703, 7703 }, + { 7705, 7705 }, + { 7707, 7707 }, + { 7709, 7709 }, + { 7711, 7711 }, + { 7713, 7713 }, + { 7715, 7715 }, + { 7717, 7717 }, + { 7719, 7719 }, + { 7721, 7721 }, + { 7723, 7723 }, + { 7725, 7725 }, + { 7727, 7727 }, + { 7729, 7729 }, + { 7731, 7731 }, + { 7733, 7733 }, + { 7735, 7735 }, + { 7737, 7737 }, + { 7739, 7739 }, + { 7741, 7741 }, + { 7743, 7743 }, + { 7745, 7745 }, + { 7747, 7747 }, + { 7749, 7749 }, + { 7751, 7751 }, + { 7753, 7753 }, + { 7755, 7755 }, + { 7757, 7757 }, + { 7759, 7759 }, + { 7761, 7761 }, + { 7763, 7763 }, + { 7765, 7765 }, + { 7767, 7767 }, + { 7769, 7769 }, + { 7771, 7771 }, + { 7773, 7773 }, + { 7775, 7775 }, + { 7777, 7777 }, + { 7779, 7779 }, + { 7781, 7781 }, + { 7783, 7783 }, + { 7785, 7785 }, + { 7787, 7787 }, + { 7789, 7789 }, + { 7791, 7791 }, + { 7793, 7793 }, + { 7795, 7795 }, + { 7797, 7797 }, + { 7799, 7799 }, + { 7801, 7801 }, + { 7803, 7803 }, + { 7805, 7805 }, + { 7807, 7807 }, + { 7809, 7809 }, + { 7811, 7811 }, + { 7813, 7813 }, + { 7815, 7815 }, + { 7817, 7817 }, + { 7819, 7819 }, + { 7821, 7821 }, + { 7823, 7823 }, + { 7825, 7825 }, + { 7827, 7827 }, + { 7829, 7837 }, + { 7839, 7839 }, + { 7841, 7841 }, + { 7843, 7843 }, + { 7845, 7845 }, + { 7847, 7847 }, + { 7849, 7849 }, + { 7851, 7851 }, + { 7853, 7853 }, + { 7855, 7855 }, + { 7857, 7857 }, + { 7859, 7859 }, + { 7861, 7861 }, + { 7863, 7863 }, + { 7865, 7865 }, + { 7867, 7867 }, + { 7869, 7869 }, + { 7871, 7871 }, + { 7873, 7873 }, + { 7875, 7875 }, + { 7877, 7877 }, + { 7879, 7879 }, + { 7881, 7881 }, + { 7883, 7883 }, + { 7885, 7885 }, + { 7887, 7887 }, + { 7889, 7889 }, + { 7891, 7891 }, + { 7893, 7893 }, + { 7895, 7895 }, + { 7897, 7897 }, + { 7899, 7899 }, + { 7901, 7901 }, + { 7903, 7903 }, + { 7905, 7905 }, + { 7907, 7907 }, + { 7909, 7909 }, + { 7911, 7911 }, + { 7913, 7913 }, + { 7915, 7915 }, + { 7917, 7917 }, + { 7919, 7919 }, + { 7921, 7921 }, + { 7923, 7923 }, + { 7925, 7925 }, + { 7927, 7927 }, + { 7929, 7929 }, + { 7931, 7931 }, + { 7933, 7933 }, + { 7935, 7943 }, + { 7952, 7957 }, + { 7968, 7975 }, + { 7984, 7991 }, + { 8000, 8005 }, + { 8016, 8023 }, + { 8032, 8039 }, + { 8048, 8061 }, + { 8064, 8071 }, + { 8080, 8087 }, + { 8096, 8103 }, + { 8112, 8116 }, + { 8118, 8119 }, + { 8126, 8126 }, + { 8130, 8132 }, + { 8134, 8135 }, + { 8144, 8147 }, + { 8150, 8151 }, + { 8160, 8167 }, + { 8178, 8180 }, + { 8182, 8183 }, + { 8458, 8458 }, + { 8462, 8463 }, + { 8467, 8467 }, + { 8495, 8495 }, + { 8500, 8500 }, + { 8505, 8505 }, + { 8508, 8509 }, + { 8518, 8521 }, + { 8526, 8526 }, + { 8580, 8580 }, + { 11312, 11358 }, + { 11361, 11361 }, + { 11365, 11366 }, + { 11368, 11368 }, + { 11370, 11370 }, + { 11372, 11372 }, + { 11377, 11377 }, + { 11379, 11380 }, + { 11382, 11387 }, + { 11393, 11393 }, + { 11395, 11395 }, + { 11397, 11397 }, + { 11399, 11399 }, + { 11401, 11401 }, + { 11403, 11403 }, + { 11405, 11405 }, + { 11407, 11407 }, + { 11409, 11409 }, + { 11411, 11411 }, + { 11413, 11413 }, + { 11415, 11415 }, + { 11417, 11417 }, + { 11419, 11419 }, + { 11421, 11421 }, + { 11423, 11423 }, + { 11425, 11425 }, + { 11427, 11427 }, + { 11429, 11429 }, + { 11431, 11431 }, + { 11433, 11433 }, + { 11435, 11435 }, + { 11437, 11437 }, + { 11439, 11439 }, + { 11441, 11441 }, + { 11443, 11443 }, + { 11445, 11445 }, + { 11447, 11447 }, + { 11449, 11449 }, + { 11451, 11451 }, + { 11453, 11453 }, + { 11455, 11455 }, + { 11457, 11457 }, + { 11459, 11459 }, + { 11461, 11461 }, + { 11463, 11463 }, + { 11465, 11465 }, + { 11467, 11467 }, + { 11469, 11469 }, + { 11471, 11471 }, + { 11473, 11473 }, + { 11475, 11475 }, + { 11477, 11477 }, + { 11479, 11479 }, + { 11481, 11481 }, + { 11483, 11483 }, + { 11485, 11485 }, + { 11487, 11487 }, + { 11489, 11489 }, + { 11491, 11492 }, + { 11500, 11500 }, + { 11502, 11502 }, + { 11507, 11507 }, + { 11520, 11557 }, + { 11559, 11559 }, + { 11565, 11565 }, + { 42561, 42561 }, + { 42563, 42563 }, + { 42565, 42565 }, + { 42567, 42567 }, + { 42569, 42569 }, + { 42571, 42571 }, + { 42573, 42573 }, + { 42575, 42575 }, + { 42577, 42577 }, + { 42579, 42579 }, + { 42581, 42581 }, + { 42583, 42583 }, + { 42585, 42585 }, + { 42587, 42587 }, + { 42589, 42589 }, + { 42591, 42591 }, + { 42593, 42593 }, + { 42595, 42595 }, + { 42597, 42597 }, + { 42599, 42599 }, + { 42601, 42601 }, + { 42603, 42603 }, + { 42605, 42605 }, + { 42625, 42625 }, + { 42627, 42627 }, + { 42629, 42629 }, + { 42631, 42631 }, + { 42633, 42633 }, + { 42635, 42635 }, + { 42637, 42637 }, + { 42639, 42639 }, + { 42641, 42641 }, + { 42643, 42643 }, + { 42645, 42645 }, + { 42647, 42647 }, + { 42649, 42649 }, + { 42651, 42651 }, + { 42787, 42787 }, + { 42789, 42789 }, + { 42791, 42791 }, + { 42793, 42793 }, + { 42795, 42795 }, + { 42797, 42797 }, + { 42799, 42801 }, + { 42803, 42803 }, + { 42805, 42805 }, + { 42807, 42807 }, + { 42809, 42809 }, + { 42811, 42811 }, + { 42813, 42813 }, + { 42815, 42815 }, + { 42817, 42817 }, + { 42819, 42819 }, + { 42821, 42821 }, + { 42823, 42823 }, + { 42825, 42825 }, + { 42827, 42827 }, + { 42829, 42829 }, + { 42831, 42831 }, + { 42833, 42833 }, + { 42835, 42835 }, + { 42837, 42837 }, + { 42839, 42839 }, + { 42841, 42841 }, + { 42843, 42843 }, + { 42845, 42845 }, + { 42847, 42847 }, + { 42849, 42849 }, + { 42851, 42851 }, + { 42853, 42853 }, + { 42855, 42855 }, + { 42857, 42857 }, + { 42859, 42859 }, + { 42861, 42861 }, + { 42863, 42863 }, + { 42865, 42872 }, + { 42874, 42874 }, + { 42876, 42876 }, + { 42879, 42879 }, + { 42881, 42881 }, + { 42883, 42883 }, + { 42885, 42885 }, + { 42887, 42887 }, + { 42892, 42892 }, + { 42894, 42894 }, + { 42897, 42897 }, + { 42899, 42901 }, + { 42903, 42903 }, + { 42905, 42905 }, + { 42907, 42907 }, + { 42909, 42909 }, + { 42911, 42911 }, + { 42913, 42913 }, + { 42915, 42915 }, + { 42917, 42917 }, + { 42919, 42919 }, + { 42921, 42921 }, + { 42927, 42927 }, + { 42933, 42933 }, + { 42935, 42935 }, + { 42937, 42937 }, + { 42939, 42939 }, + { 42941, 42941 }, + { 42943, 42943 }, + { 42947, 42947 }, + { 42952, 42952 }, + { 42954, 42954 }, + { 42998, 42998 }, + { 43002, 43002 }, + { 43824, 43866 }, + { 43872, 43880 }, + { 43888, 43967 }, + { 64256, 64262 }, + { 64275, 64279 }, + { 65345, 65370 }, +}; +static const URange32 Ll_range32[] = { + { 66600, 66639 }, + { 66776, 66811 }, + { 68800, 68850 }, + { 71872, 71903 }, + { 93792, 93823 }, + { 119834, 119859 }, + { 119886, 119892 }, + { 119894, 119911 }, + { 119938, 119963 }, + { 119990, 119993 }, + { 119995, 119995 }, + { 119997, 120003 }, + { 120005, 120015 }, + { 120042, 120067 }, + { 120094, 120119 }, + { 120146, 120171 }, + { 120198, 120223 }, + { 120250, 120275 }, + { 120302, 120327 }, + { 120354, 120379 }, + { 120406, 120431 }, + { 120458, 120485 }, + { 120514, 120538 }, + { 120540, 120545 }, + { 120572, 120596 }, + { 120598, 120603 }, + { 120630, 120654 }, + { 120656, 120661 }, + { 120688, 120712 }, + { 120714, 120719 }, + { 120746, 120770 }, + { 120772, 120777 }, + { 120779, 120779 }, + { 125218, 125251 }, +}; +static const URange16 Lm_range16[] = { + { 688, 705 }, + { 710, 721 }, + { 736, 740 }, + { 748, 748 }, + { 750, 750 }, + { 884, 884 }, + { 890, 890 }, + { 1369, 1369 }, + { 1600, 1600 }, + { 1765, 1766 }, + { 2036, 2037 }, + { 2042, 2042 }, + { 2074, 2074 }, + { 2084, 2084 }, + { 2088, 2088 }, + { 2417, 2417 }, + { 3654, 3654 }, + { 3782, 3782 }, + { 4348, 4348 }, + { 6103, 6103 }, + { 6211, 6211 }, + { 6823, 6823 }, + { 7288, 7293 }, + { 7468, 7530 }, + { 7544, 7544 }, + { 7579, 7615 }, + { 8305, 8305 }, + { 8319, 8319 }, + { 8336, 8348 }, + { 11388, 11389 }, + { 11631, 11631 }, + { 11823, 11823 }, + { 12293, 12293 }, + { 12337, 12341 }, + { 12347, 12347 }, + { 12445, 12446 }, + { 12540, 12542 }, + { 40981, 40981 }, + { 42232, 42237 }, + { 42508, 42508 }, + { 42623, 42623 }, + { 42652, 42653 }, + { 42775, 42783 }, + { 42864, 42864 }, + { 42888, 42888 }, + { 43000, 43001 }, + { 43471, 43471 }, + { 43494, 43494 }, + { 43632, 43632 }, + { 43741, 43741 }, + { 43763, 43764 }, + { 43868, 43871 }, + { 43881, 43881 }, + { 65392, 65392 }, + { 65438, 65439 }, +}; +static const URange32 Lm_range32[] = { + { 92992, 92995 }, + { 94099, 94111 }, + { 94176, 94177 }, + { 94179, 94179 }, + { 123191, 123197 }, + { 125259, 125259 }, +}; +static const URange16 Lo_range16[] = { + { 170, 170 }, + { 186, 186 }, + { 443, 443 }, + { 448, 451 }, + { 660, 660 }, + { 1488, 1514 }, + { 1519, 1522 }, + { 1568, 1599 }, + { 1601, 1610 }, + { 1646, 1647 }, + { 1649, 1747 }, + { 1749, 1749 }, + { 1774, 1775 }, + { 1786, 1788 }, + { 1791, 1791 }, + { 1808, 1808 }, + { 1810, 1839 }, + { 1869, 1957 }, + { 1969, 1969 }, + { 1994, 2026 }, + { 2048, 2069 }, + { 2112, 2136 }, + { 2144, 2154 }, + { 2208, 2228 }, + { 2230, 2247 }, + { 2308, 2361 }, + { 2365, 2365 }, + { 2384, 2384 }, + { 2392, 2401 }, + { 2418, 2432 }, + { 2437, 2444 }, + { 2447, 2448 }, + { 2451, 2472 }, + { 2474, 2480 }, + { 2482, 2482 }, + { 2486, 2489 }, + { 2493, 2493 }, + { 2510, 2510 }, + { 2524, 2525 }, + { 2527, 2529 }, + { 2544, 2545 }, + { 2556, 2556 }, + { 2565, 2570 }, + { 2575, 2576 }, + { 2579, 2600 }, + { 2602, 2608 }, + { 2610, 2611 }, + { 2613, 2614 }, + { 2616, 2617 }, + { 2649, 2652 }, + { 2654, 2654 }, + { 2674, 2676 }, + { 2693, 2701 }, + { 2703, 2705 }, + { 2707, 2728 }, + { 2730, 2736 }, + { 2738, 2739 }, + { 2741, 2745 }, + { 2749, 2749 }, + { 2768, 2768 }, + { 2784, 2785 }, + { 2809, 2809 }, + { 2821, 2828 }, + { 2831, 2832 }, + { 2835, 2856 }, + { 2858, 2864 }, + { 2866, 2867 }, + { 2869, 2873 }, + { 2877, 2877 }, + { 2908, 2909 }, + { 2911, 2913 }, + { 2929, 2929 }, + { 2947, 2947 }, + { 2949, 2954 }, + { 2958, 2960 }, + { 2962, 2965 }, + { 2969, 2970 }, + { 2972, 2972 }, + { 2974, 2975 }, + { 2979, 2980 }, + { 2984, 2986 }, + { 2990, 3001 }, + { 3024, 3024 }, + { 3077, 3084 }, + { 3086, 3088 }, + { 3090, 3112 }, + { 3114, 3129 }, + { 3133, 3133 }, + { 3160, 3162 }, + { 3168, 3169 }, + { 3200, 3200 }, + { 3205, 3212 }, + { 3214, 3216 }, + { 3218, 3240 }, + { 3242, 3251 }, + { 3253, 3257 }, + { 3261, 3261 }, + { 3294, 3294 }, + { 3296, 3297 }, + { 3313, 3314 }, + { 3332, 3340 }, + { 3342, 3344 }, + { 3346, 3386 }, + { 3389, 3389 }, + { 3406, 3406 }, + { 3412, 3414 }, + { 3423, 3425 }, + { 3450, 3455 }, + { 3461, 3478 }, + { 3482, 3505 }, + { 3507, 3515 }, + { 3517, 3517 }, + { 3520, 3526 }, + { 3585, 3632 }, + { 3634, 3635 }, + { 3648, 3653 }, + { 3713, 3714 }, + { 3716, 3716 }, + { 3718, 3722 }, + { 3724, 3747 }, + { 3749, 3749 }, + { 3751, 3760 }, + { 3762, 3763 }, + { 3773, 3773 }, + { 3776, 3780 }, + { 3804, 3807 }, + { 3840, 3840 }, + { 3904, 3911 }, + { 3913, 3948 }, + { 3976, 3980 }, + { 4096, 4138 }, + { 4159, 4159 }, + { 4176, 4181 }, + { 4186, 4189 }, + { 4193, 4193 }, + { 4197, 4198 }, + { 4206, 4208 }, + { 4213, 4225 }, + { 4238, 4238 }, + { 4352, 4680 }, + { 4682, 4685 }, + { 4688, 4694 }, + { 4696, 4696 }, + { 4698, 4701 }, + { 4704, 4744 }, + { 4746, 4749 }, + { 4752, 4784 }, + { 4786, 4789 }, + { 4792, 4798 }, + { 4800, 4800 }, + { 4802, 4805 }, + { 4808, 4822 }, + { 4824, 4880 }, + { 4882, 4885 }, + { 4888, 4954 }, + { 4992, 5007 }, + { 5121, 5740 }, + { 5743, 5759 }, + { 5761, 5786 }, + { 5792, 5866 }, + { 5873, 5880 }, + { 5888, 5900 }, + { 5902, 5905 }, + { 5920, 5937 }, + { 5952, 5969 }, + { 5984, 5996 }, + { 5998, 6000 }, + { 6016, 6067 }, + { 6108, 6108 }, + { 6176, 6210 }, + { 6212, 6264 }, + { 6272, 6276 }, + { 6279, 6312 }, + { 6314, 6314 }, + { 6320, 6389 }, + { 6400, 6430 }, + { 6480, 6509 }, + { 6512, 6516 }, + { 6528, 6571 }, + { 6576, 6601 }, + { 6656, 6678 }, + { 6688, 6740 }, + { 6917, 6963 }, + { 6981, 6987 }, + { 7043, 7072 }, + { 7086, 7087 }, + { 7098, 7141 }, + { 7168, 7203 }, + { 7245, 7247 }, + { 7258, 7287 }, + { 7401, 7404 }, + { 7406, 7411 }, + { 7413, 7414 }, + { 7418, 7418 }, + { 8501, 8504 }, + { 11568, 11623 }, + { 11648, 11670 }, + { 11680, 11686 }, + { 11688, 11694 }, + { 11696, 11702 }, + { 11704, 11710 }, + { 11712, 11718 }, + { 11720, 11726 }, + { 11728, 11734 }, + { 11736, 11742 }, + { 12294, 12294 }, + { 12348, 12348 }, + { 12353, 12438 }, + { 12447, 12447 }, + { 12449, 12538 }, + { 12543, 12543 }, + { 12549, 12591 }, + { 12593, 12686 }, + { 12704, 12735 }, + { 12784, 12799 }, + { 13312, 19903 }, + { 19968, 40956 }, + { 40960, 40980 }, + { 40982, 42124 }, + { 42192, 42231 }, + { 42240, 42507 }, + { 42512, 42527 }, + { 42538, 42539 }, + { 42606, 42606 }, + { 42656, 42725 }, + { 42895, 42895 }, + { 42999, 42999 }, + { 43003, 43009 }, + { 43011, 43013 }, + { 43015, 43018 }, + { 43020, 43042 }, + { 43072, 43123 }, + { 43138, 43187 }, + { 43250, 43255 }, + { 43259, 43259 }, + { 43261, 43262 }, + { 43274, 43301 }, + { 43312, 43334 }, + { 43360, 43388 }, + { 43396, 43442 }, + { 43488, 43492 }, + { 43495, 43503 }, + { 43514, 43518 }, + { 43520, 43560 }, + { 43584, 43586 }, + { 43588, 43595 }, + { 43616, 43631 }, + { 43633, 43638 }, + { 43642, 43642 }, + { 43646, 43695 }, + { 43697, 43697 }, + { 43701, 43702 }, + { 43705, 43709 }, + { 43712, 43712 }, + { 43714, 43714 }, + { 43739, 43740 }, + { 43744, 43754 }, + { 43762, 43762 }, + { 43777, 43782 }, + { 43785, 43790 }, + { 43793, 43798 }, + { 43808, 43814 }, + { 43816, 43822 }, + { 43968, 44002 }, + { 44032, 55203 }, + { 55216, 55238 }, + { 55243, 55291 }, + { 63744, 64109 }, + { 64112, 64217 }, + { 64285, 64285 }, + { 64287, 64296 }, + { 64298, 64310 }, + { 64312, 64316 }, + { 64318, 64318 }, + { 64320, 64321 }, + { 64323, 64324 }, + { 64326, 64433 }, + { 64467, 64829 }, + { 64848, 64911 }, + { 64914, 64967 }, + { 65008, 65019 }, + { 65136, 65140 }, + { 65142, 65276 }, + { 65382, 65391 }, + { 65393, 65437 }, + { 65440, 65470 }, + { 65474, 65479 }, + { 65482, 65487 }, + { 65490, 65495 }, + { 65498, 65500 }, +}; +static const URange32 Lo_range32[] = { + { 65536, 65547 }, + { 65549, 65574 }, + { 65576, 65594 }, + { 65596, 65597 }, + { 65599, 65613 }, + { 65616, 65629 }, + { 65664, 65786 }, + { 66176, 66204 }, + { 66208, 66256 }, + { 66304, 66335 }, + { 66349, 66368 }, + { 66370, 66377 }, + { 66384, 66421 }, + { 66432, 66461 }, + { 66464, 66499 }, + { 66504, 66511 }, + { 66640, 66717 }, + { 66816, 66855 }, + { 66864, 66915 }, + { 67072, 67382 }, + { 67392, 67413 }, + { 67424, 67431 }, + { 67584, 67589 }, + { 67592, 67592 }, + { 67594, 67637 }, + { 67639, 67640 }, + { 67644, 67644 }, + { 67647, 67669 }, + { 67680, 67702 }, + { 67712, 67742 }, + { 67808, 67826 }, + { 67828, 67829 }, + { 67840, 67861 }, + { 67872, 67897 }, + { 67968, 68023 }, + { 68030, 68031 }, + { 68096, 68096 }, + { 68112, 68115 }, + { 68117, 68119 }, + { 68121, 68149 }, + { 68192, 68220 }, + { 68224, 68252 }, + { 68288, 68295 }, + { 68297, 68324 }, + { 68352, 68405 }, + { 68416, 68437 }, + { 68448, 68466 }, + { 68480, 68497 }, + { 68608, 68680 }, + { 68864, 68899 }, + { 69248, 69289 }, + { 69296, 69297 }, + { 69376, 69404 }, + { 69415, 69415 }, + { 69424, 69445 }, + { 69552, 69572 }, + { 69600, 69622 }, + { 69635, 69687 }, + { 69763, 69807 }, + { 69840, 69864 }, + { 69891, 69926 }, + { 69956, 69956 }, + { 69959, 69959 }, + { 69968, 70002 }, + { 70006, 70006 }, + { 70019, 70066 }, + { 70081, 70084 }, + { 70106, 70106 }, + { 70108, 70108 }, + { 70144, 70161 }, + { 70163, 70187 }, + { 70272, 70278 }, + { 70280, 70280 }, + { 70282, 70285 }, + { 70287, 70301 }, + { 70303, 70312 }, + { 70320, 70366 }, + { 70405, 70412 }, + { 70415, 70416 }, + { 70419, 70440 }, + { 70442, 70448 }, + { 70450, 70451 }, + { 70453, 70457 }, + { 70461, 70461 }, + { 70480, 70480 }, + { 70493, 70497 }, + { 70656, 70708 }, + { 70727, 70730 }, + { 70751, 70753 }, + { 70784, 70831 }, + { 70852, 70853 }, + { 70855, 70855 }, + { 71040, 71086 }, + { 71128, 71131 }, + { 71168, 71215 }, + { 71236, 71236 }, + { 71296, 71338 }, + { 71352, 71352 }, + { 71424, 71450 }, + { 71680, 71723 }, + { 71935, 71942 }, + { 71945, 71945 }, + { 71948, 71955 }, + { 71957, 71958 }, + { 71960, 71983 }, + { 71999, 71999 }, + { 72001, 72001 }, + { 72096, 72103 }, + { 72106, 72144 }, + { 72161, 72161 }, + { 72163, 72163 }, + { 72192, 72192 }, + { 72203, 72242 }, + { 72250, 72250 }, + { 72272, 72272 }, + { 72284, 72329 }, + { 72349, 72349 }, + { 72384, 72440 }, + { 72704, 72712 }, + { 72714, 72750 }, + { 72768, 72768 }, + { 72818, 72847 }, + { 72960, 72966 }, + { 72968, 72969 }, + { 72971, 73008 }, + { 73030, 73030 }, + { 73056, 73061 }, + { 73063, 73064 }, + { 73066, 73097 }, + { 73112, 73112 }, + { 73440, 73458 }, + { 73648, 73648 }, + { 73728, 74649 }, + { 74880, 75075 }, + { 77824, 78894 }, + { 82944, 83526 }, + { 92160, 92728 }, + { 92736, 92766 }, + { 92880, 92909 }, + { 92928, 92975 }, + { 93027, 93047 }, + { 93053, 93071 }, + { 93952, 94026 }, + { 94032, 94032 }, + { 94208, 100343 }, + { 100352, 101589 }, + { 101632, 101640 }, + { 110592, 110878 }, + { 110928, 110930 }, + { 110948, 110951 }, + { 110960, 111355 }, + { 113664, 113770 }, + { 113776, 113788 }, + { 113792, 113800 }, + { 113808, 113817 }, + { 123136, 123180 }, + { 123214, 123214 }, + { 123584, 123627 }, + { 124928, 125124 }, + { 126464, 126467 }, + { 126469, 126495 }, + { 126497, 126498 }, + { 126500, 126500 }, + { 126503, 126503 }, + { 126505, 126514 }, + { 126516, 126519 }, + { 126521, 126521 }, + { 126523, 126523 }, + { 126530, 126530 }, + { 126535, 126535 }, + { 126537, 126537 }, + { 126539, 126539 }, + { 126541, 126543 }, + { 126545, 126546 }, + { 126548, 126548 }, + { 126551, 126551 }, + { 126553, 126553 }, + { 126555, 126555 }, + { 126557, 126557 }, + { 126559, 126559 }, + { 126561, 126562 }, + { 126564, 126564 }, + { 126567, 126570 }, + { 126572, 126578 }, + { 126580, 126583 }, + { 126585, 126588 }, + { 126590, 126590 }, + { 126592, 126601 }, + { 126603, 126619 }, + { 126625, 126627 }, + { 126629, 126633 }, + { 126635, 126651 }, + { 131072, 173789 }, + { 173824, 177972 }, + { 177984, 178205 }, + { 178208, 183969 }, + { 183984, 191456 }, + { 194560, 195101 }, + { 196608, 201546 }, +}; +static const URange16 Lt_range16[] = { + { 453, 453 }, + { 456, 456 }, + { 459, 459 }, + { 498, 498 }, + { 8072, 8079 }, + { 8088, 8095 }, + { 8104, 8111 }, + { 8124, 8124 }, + { 8140, 8140 }, + { 8188, 8188 }, +}; +static const URange16 Lu_range16[] = { + { 65, 90 }, + { 192, 214 }, + { 216, 222 }, + { 256, 256 }, + { 258, 258 }, + { 260, 260 }, + { 262, 262 }, + { 264, 264 }, + { 266, 266 }, + { 268, 268 }, + { 270, 270 }, + { 272, 272 }, + { 274, 274 }, + { 276, 276 }, + { 278, 278 }, + { 280, 280 }, + { 282, 282 }, + { 284, 284 }, + { 286, 286 }, + { 288, 288 }, + { 290, 290 }, + { 292, 292 }, + { 294, 294 }, + { 296, 296 }, + { 298, 298 }, + { 300, 300 }, + { 302, 302 }, + { 304, 304 }, + { 306, 306 }, + { 308, 308 }, + { 310, 310 }, + { 313, 313 }, + { 315, 315 }, + { 317, 317 }, + { 319, 319 }, + { 321, 321 }, + { 323, 323 }, + { 325, 325 }, + { 327, 327 }, + { 330, 330 }, + { 332, 332 }, + { 334, 334 }, + { 336, 336 }, + { 338, 338 }, + { 340, 340 }, + { 342, 342 }, + { 344, 344 }, + { 346, 346 }, + { 348, 348 }, + { 350, 350 }, + { 352, 352 }, + { 354, 354 }, + { 356, 356 }, + { 358, 358 }, + { 360, 360 }, + { 362, 362 }, + { 364, 364 }, + { 366, 366 }, + { 368, 368 }, + { 370, 370 }, + { 372, 372 }, + { 374, 374 }, + { 376, 377 }, + { 379, 379 }, + { 381, 381 }, + { 385, 386 }, + { 388, 388 }, + { 390, 391 }, + { 393, 395 }, + { 398, 401 }, + { 403, 404 }, + { 406, 408 }, + { 412, 413 }, + { 415, 416 }, + { 418, 418 }, + { 420, 420 }, + { 422, 423 }, + { 425, 425 }, + { 428, 428 }, + { 430, 431 }, + { 433, 435 }, + { 437, 437 }, + { 439, 440 }, + { 444, 444 }, + { 452, 452 }, + { 455, 455 }, + { 458, 458 }, + { 461, 461 }, + { 463, 463 }, + { 465, 465 }, + { 467, 467 }, + { 469, 469 }, + { 471, 471 }, + { 473, 473 }, + { 475, 475 }, + { 478, 478 }, + { 480, 480 }, + { 482, 482 }, + { 484, 484 }, + { 486, 486 }, + { 488, 488 }, + { 490, 490 }, + { 492, 492 }, + { 494, 494 }, + { 497, 497 }, + { 500, 500 }, + { 502, 504 }, + { 506, 506 }, + { 508, 508 }, + { 510, 510 }, + { 512, 512 }, + { 514, 514 }, + { 516, 516 }, + { 518, 518 }, + { 520, 520 }, + { 522, 522 }, + { 524, 524 }, + { 526, 526 }, + { 528, 528 }, + { 530, 530 }, + { 532, 532 }, + { 534, 534 }, + { 536, 536 }, + { 538, 538 }, + { 540, 540 }, + { 542, 542 }, + { 544, 544 }, + { 546, 546 }, + { 548, 548 }, + { 550, 550 }, + { 552, 552 }, + { 554, 554 }, + { 556, 556 }, + { 558, 558 }, + { 560, 560 }, + { 562, 562 }, + { 570, 571 }, + { 573, 574 }, + { 577, 577 }, + { 579, 582 }, + { 584, 584 }, + { 586, 586 }, + { 588, 588 }, + { 590, 590 }, + { 880, 880 }, + { 882, 882 }, + { 886, 886 }, + { 895, 895 }, + { 902, 902 }, + { 904, 906 }, + { 908, 908 }, + { 910, 911 }, + { 913, 929 }, + { 931, 939 }, + { 975, 975 }, + { 978, 980 }, + { 984, 984 }, + { 986, 986 }, + { 988, 988 }, + { 990, 990 }, + { 992, 992 }, + { 994, 994 }, + { 996, 996 }, + { 998, 998 }, + { 1000, 1000 }, + { 1002, 1002 }, + { 1004, 1004 }, + { 1006, 1006 }, + { 1012, 1012 }, + { 1015, 1015 }, + { 1017, 1018 }, + { 1021, 1071 }, + { 1120, 1120 }, + { 1122, 1122 }, + { 1124, 1124 }, + { 1126, 1126 }, + { 1128, 1128 }, + { 1130, 1130 }, + { 1132, 1132 }, + { 1134, 1134 }, + { 1136, 1136 }, + { 1138, 1138 }, + { 1140, 1140 }, + { 1142, 1142 }, + { 1144, 1144 }, + { 1146, 1146 }, + { 1148, 1148 }, + { 1150, 1150 }, + { 1152, 1152 }, + { 1162, 1162 }, + { 1164, 1164 }, + { 1166, 1166 }, + { 1168, 1168 }, + { 1170, 1170 }, + { 1172, 1172 }, + { 1174, 1174 }, + { 1176, 1176 }, + { 1178, 1178 }, + { 1180, 1180 }, + { 1182, 1182 }, + { 1184, 1184 }, + { 1186, 1186 }, + { 1188, 1188 }, + { 1190, 1190 }, + { 1192, 1192 }, + { 1194, 1194 }, + { 1196, 1196 }, + { 1198, 1198 }, + { 1200, 1200 }, + { 1202, 1202 }, + { 1204, 1204 }, + { 1206, 1206 }, + { 1208, 1208 }, + { 1210, 1210 }, + { 1212, 1212 }, + { 1214, 1214 }, + { 1216, 1217 }, + { 1219, 1219 }, + { 1221, 1221 }, + { 1223, 1223 }, + { 1225, 1225 }, + { 1227, 1227 }, + { 1229, 1229 }, + { 1232, 1232 }, + { 1234, 1234 }, + { 1236, 1236 }, + { 1238, 1238 }, + { 1240, 1240 }, + { 1242, 1242 }, + { 1244, 1244 }, + { 1246, 1246 }, + { 1248, 1248 }, + { 1250, 1250 }, + { 1252, 1252 }, + { 1254, 1254 }, + { 1256, 1256 }, + { 1258, 1258 }, + { 1260, 1260 }, + { 1262, 1262 }, + { 1264, 1264 }, + { 1266, 1266 }, + { 1268, 1268 }, + { 1270, 1270 }, + { 1272, 1272 }, + { 1274, 1274 }, + { 1276, 1276 }, + { 1278, 1278 }, + { 1280, 1280 }, + { 1282, 1282 }, + { 1284, 1284 }, + { 1286, 1286 }, + { 1288, 1288 }, + { 1290, 1290 }, + { 1292, 1292 }, + { 1294, 1294 }, + { 1296, 1296 }, + { 1298, 1298 }, + { 1300, 1300 }, + { 1302, 1302 }, + { 1304, 1304 }, + { 1306, 1306 }, + { 1308, 1308 }, + { 1310, 1310 }, + { 1312, 1312 }, + { 1314, 1314 }, + { 1316, 1316 }, + { 1318, 1318 }, + { 1320, 1320 }, + { 1322, 1322 }, + { 1324, 1324 }, + { 1326, 1326 }, + { 1329, 1366 }, + { 4256, 4293 }, + { 4295, 4295 }, + { 4301, 4301 }, + { 5024, 5109 }, + { 7312, 7354 }, + { 7357, 7359 }, + { 7680, 7680 }, + { 7682, 7682 }, + { 7684, 7684 }, + { 7686, 7686 }, + { 7688, 7688 }, + { 7690, 7690 }, + { 7692, 7692 }, + { 7694, 7694 }, + { 7696, 7696 }, + { 7698, 7698 }, + { 7700, 7700 }, + { 7702, 7702 }, + { 7704, 7704 }, + { 7706, 7706 }, + { 7708, 7708 }, + { 7710, 7710 }, + { 7712, 7712 }, + { 7714, 7714 }, + { 7716, 7716 }, + { 7718, 7718 }, + { 7720, 7720 }, + { 7722, 7722 }, + { 7724, 7724 }, + { 7726, 7726 }, + { 7728, 7728 }, + { 7730, 7730 }, + { 7732, 7732 }, + { 7734, 7734 }, + { 7736, 7736 }, + { 7738, 7738 }, + { 7740, 7740 }, + { 7742, 7742 }, + { 7744, 7744 }, + { 7746, 7746 }, + { 7748, 7748 }, + { 7750, 7750 }, + { 7752, 7752 }, + { 7754, 7754 }, + { 7756, 7756 }, + { 7758, 7758 }, + { 7760, 7760 }, + { 7762, 7762 }, + { 7764, 7764 }, + { 7766, 7766 }, + { 7768, 7768 }, + { 7770, 7770 }, + { 7772, 7772 }, + { 7774, 7774 }, + { 7776, 7776 }, + { 7778, 7778 }, + { 7780, 7780 }, + { 7782, 7782 }, + { 7784, 7784 }, + { 7786, 7786 }, + { 7788, 7788 }, + { 7790, 7790 }, + { 7792, 7792 }, + { 7794, 7794 }, + { 7796, 7796 }, + { 7798, 7798 }, + { 7800, 7800 }, + { 7802, 7802 }, + { 7804, 7804 }, + { 7806, 7806 }, + { 7808, 7808 }, + { 7810, 7810 }, + { 7812, 7812 }, + { 7814, 7814 }, + { 7816, 7816 }, + { 7818, 7818 }, + { 7820, 7820 }, + { 7822, 7822 }, + { 7824, 7824 }, + { 7826, 7826 }, + { 7828, 7828 }, + { 7838, 7838 }, + { 7840, 7840 }, + { 7842, 7842 }, + { 7844, 7844 }, + { 7846, 7846 }, + { 7848, 7848 }, + { 7850, 7850 }, + { 7852, 7852 }, + { 7854, 7854 }, + { 7856, 7856 }, + { 7858, 7858 }, + { 7860, 7860 }, + { 7862, 7862 }, + { 7864, 7864 }, + { 7866, 7866 }, + { 7868, 7868 }, + { 7870, 7870 }, + { 7872, 7872 }, + { 7874, 7874 }, + { 7876, 7876 }, + { 7878, 7878 }, + { 7880, 7880 }, + { 7882, 7882 }, + { 7884, 7884 }, + { 7886, 7886 }, + { 7888, 7888 }, + { 7890, 7890 }, + { 7892, 7892 }, + { 7894, 7894 }, + { 7896, 7896 }, + { 7898, 7898 }, + { 7900, 7900 }, + { 7902, 7902 }, + { 7904, 7904 }, + { 7906, 7906 }, + { 7908, 7908 }, + { 7910, 7910 }, + { 7912, 7912 }, + { 7914, 7914 }, + { 7916, 7916 }, + { 7918, 7918 }, + { 7920, 7920 }, + { 7922, 7922 }, + { 7924, 7924 }, + { 7926, 7926 }, + { 7928, 7928 }, + { 7930, 7930 }, + { 7932, 7932 }, + { 7934, 7934 }, + { 7944, 7951 }, + { 7960, 7965 }, + { 7976, 7983 }, + { 7992, 7999 }, + { 8008, 8013 }, + { 8025, 8025 }, + { 8027, 8027 }, + { 8029, 8029 }, + { 8031, 8031 }, + { 8040, 8047 }, + { 8120, 8123 }, + { 8136, 8139 }, + { 8152, 8155 }, + { 8168, 8172 }, + { 8184, 8187 }, + { 8450, 8450 }, + { 8455, 8455 }, + { 8459, 8461 }, + { 8464, 8466 }, + { 8469, 8469 }, + { 8473, 8477 }, + { 8484, 8484 }, + { 8486, 8486 }, + { 8488, 8488 }, + { 8490, 8493 }, + { 8496, 8499 }, + { 8510, 8511 }, + { 8517, 8517 }, + { 8579, 8579 }, + { 11264, 11310 }, + { 11360, 11360 }, + { 11362, 11364 }, + { 11367, 11367 }, + { 11369, 11369 }, + { 11371, 11371 }, + { 11373, 11376 }, + { 11378, 11378 }, + { 11381, 11381 }, + { 11390, 11392 }, + { 11394, 11394 }, + { 11396, 11396 }, + { 11398, 11398 }, + { 11400, 11400 }, + { 11402, 11402 }, + { 11404, 11404 }, + { 11406, 11406 }, + { 11408, 11408 }, + { 11410, 11410 }, + { 11412, 11412 }, + { 11414, 11414 }, + { 11416, 11416 }, + { 11418, 11418 }, + { 11420, 11420 }, + { 11422, 11422 }, + { 11424, 11424 }, + { 11426, 11426 }, + { 11428, 11428 }, + { 11430, 11430 }, + { 11432, 11432 }, + { 11434, 11434 }, + { 11436, 11436 }, + { 11438, 11438 }, + { 11440, 11440 }, + { 11442, 11442 }, + { 11444, 11444 }, + { 11446, 11446 }, + { 11448, 11448 }, + { 11450, 11450 }, + { 11452, 11452 }, + { 11454, 11454 }, + { 11456, 11456 }, + { 11458, 11458 }, + { 11460, 11460 }, + { 11462, 11462 }, + { 11464, 11464 }, + { 11466, 11466 }, + { 11468, 11468 }, + { 11470, 11470 }, + { 11472, 11472 }, + { 11474, 11474 }, + { 11476, 11476 }, + { 11478, 11478 }, + { 11480, 11480 }, + { 11482, 11482 }, + { 11484, 11484 }, + { 11486, 11486 }, + { 11488, 11488 }, + { 11490, 11490 }, + { 11499, 11499 }, + { 11501, 11501 }, + { 11506, 11506 }, + { 42560, 42560 }, + { 42562, 42562 }, + { 42564, 42564 }, + { 42566, 42566 }, + { 42568, 42568 }, + { 42570, 42570 }, + { 42572, 42572 }, + { 42574, 42574 }, + { 42576, 42576 }, + { 42578, 42578 }, + { 42580, 42580 }, + { 42582, 42582 }, + { 42584, 42584 }, + { 42586, 42586 }, + { 42588, 42588 }, + { 42590, 42590 }, + { 42592, 42592 }, + { 42594, 42594 }, + { 42596, 42596 }, + { 42598, 42598 }, + { 42600, 42600 }, + { 42602, 42602 }, + { 42604, 42604 }, + { 42624, 42624 }, + { 42626, 42626 }, + { 42628, 42628 }, + { 42630, 42630 }, + { 42632, 42632 }, + { 42634, 42634 }, + { 42636, 42636 }, + { 42638, 42638 }, + { 42640, 42640 }, + { 42642, 42642 }, + { 42644, 42644 }, + { 42646, 42646 }, + { 42648, 42648 }, + { 42650, 42650 }, + { 42786, 42786 }, + { 42788, 42788 }, + { 42790, 42790 }, + { 42792, 42792 }, + { 42794, 42794 }, + { 42796, 42796 }, + { 42798, 42798 }, + { 42802, 42802 }, + { 42804, 42804 }, + { 42806, 42806 }, + { 42808, 42808 }, + { 42810, 42810 }, + { 42812, 42812 }, + { 42814, 42814 }, + { 42816, 42816 }, + { 42818, 42818 }, + { 42820, 42820 }, + { 42822, 42822 }, + { 42824, 42824 }, + { 42826, 42826 }, + { 42828, 42828 }, + { 42830, 42830 }, + { 42832, 42832 }, + { 42834, 42834 }, + { 42836, 42836 }, + { 42838, 42838 }, + { 42840, 42840 }, + { 42842, 42842 }, + { 42844, 42844 }, + { 42846, 42846 }, + { 42848, 42848 }, + { 42850, 42850 }, + { 42852, 42852 }, + { 42854, 42854 }, + { 42856, 42856 }, + { 42858, 42858 }, + { 42860, 42860 }, + { 42862, 42862 }, + { 42873, 42873 }, + { 42875, 42875 }, + { 42877, 42878 }, + { 42880, 42880 }, + { 42882, 42882 }, + { 42884, 42884 }, + { 42886, 42886 }, + { 42891, 42891 }, + { 42893, 42893 }, + { 42896, 42896 }, + { 42898, 42898 }, + { 42902, 42902 }, + { 42904, 42904 }, + { 42906, 42906 }, + { 42908, 42908 }, + { 42910, 42910 }, + { 42912, 42912 }, + { 42914, 42914 }, + { 42916, 42916 }, + { 42918, 42918 }, + { 42920, 42920 }, + { 42922, 42926 }, + { 42928, 42932 }, + { 42934, 42934 }, + { 42936, 42936 }, + { 42938, 42938 }, + { 42940, 42940 }, + { 42942, 42942 }, + { 42946, 42946 }, + { 42948, 42951 }, + { 42953, 42953 }, + { 42997, 42997 }, + { 65313, 65338 }, +}; +static const URange32 Lu_range32[] = { + { 66560, 66599 }, + { 66736, 66771 }, + { 68736, 68786 }, + { 71840, 71871 }, + { 93760, 93791 }, + { 119808, 119833 }, + { 119860, 119885 }, + { 119912, 119937 }, + { 119964, 119964 }, + { 119966, 119967 }, + { 119970, 119970 }, + { 119973, 119974 }, + { 119977, 119980 }, + { 119982, 119989 }, + { 120016, 120041 }, + { 120068, 120069 }, + { 120071, 120074 }, + { 120077, 120084 }, + { 120086, 120092 }, + { 120120, 120121 }, + { 120123, 120126 }, + { 120128, 120132 }, + { 120134, 120134 }, + { 120138, 120144 }, + { 120172, 120197 }, + { 120224, 120249 }, + { 120276, 120301 }, + { 120328, 120353 }, + { 120380, 120405 }, + { 120432, 120457 }, + { 120488, 120512 }, + { 120546, 120570 }, + { 120604, 120628 }, + { 120662, 120686 }, + { 120720, 120744 }, + { 120778, 120778 }, + { 125184, 125217 }, +}; +static const URange16 M_range16[] = { + { 768, 879 }, + { 1155, 1161 }, + { 1425, 1469 }, + { 1471, 1471 }, + { 1473, 1474 }, + { 1476, 1477 }, + { 1479, 1479 }, + { 1552, 1562 }, + { 1611, 1631 }, + { 1648, 1648 }, + { 1750, 1756 }, + { 1759, 1764 }, + { 1767, 1768 }, + { 1770, 1773 }, + { 1809, 1809 }, + { 1840, 1866 }, + { 1958, 1968 }, + { 2027, 2035 }, + { 2045, 2045 }, + { 2070, 2073 }, + { 2075, 2083 }, + { 2085, 2087 }, + { 2089, 2093 }, + { 2137, 2139 }, + { 2259, 2273 }, + { 2275, 2307 }, + { 2362, 2364 }, + { 2366, 2383 }, + { 2385, 2391 }, + { 2402, 2403 }, + { 2433, 2435 }, + { 2492, 2492 }, + { 2494, 2500 }, + { 2503, 2504 }, + { 2507, 2509 }, + { 2519, 2519 }, + { 2530, 2531 }, + { 2558, 2558 }, + { 2561, 2563 }, + { 2620, 2620 }, + { 2622, 2626 }, + { 2631, 2632 }, + { 2635, 2637 }, + { 2641, 2641 }, + { 2672, 2673 }, + { 2677, 2677 }, + { 2689, 2691 }, + { 2748, 2748 }, + { 2750, 2757 }, + { 2759, 2761 }, + { 2763, 2765 }, + { 2786, 2787 }, + { 2810, 2815 }, + { 2817, 2819 }, + { 2876, 2876 }, + { 2878, 2884 }, + { 2887, 2888 }, + { 2891, 2893 }, + { 2901, 2903 }, + { 2914, 2915 }, + { 2946, 2946 }, + { 3006, 3010 }, + { 3014, 3016 }, + { 3018, 3021 }, + { 3031, 3031 }, + { 3072, 3076 }, + { 3134, 3140 }, + { 3142, 3144 }, + { 3146, 3149 }, + { 3157, 3158 }, + { 3170, 3171 }, + { 3201, 3203 }, + { 3260, 3260 }, + { 3262, 3268 }, + { 3270, 3272 }, + { 3274, 3277 }, + { 3285, 3286 }, + { 3298, 3299 }, + { 3328, 3331 }, + { 3387, 3388 }, + { 3390, 3396 }, + { 3398, 3400 }, + { 3402, 3405 }, + { 3415, 3415 }, + { 3426, 3427 }, + { 3457, 3459 }, + { 3530, 3530 }, + { 3535, 3540 }, + { 3542, 3542 }, + { 3544, 3551 }, + { 3570, 3571 }, + { 3633, 3633 }, + { 3636, 3642 }, + { 3655, 3662 }, + { 3761, 3761 }, + { 3764, 3772 }, + { 3784, 3789 }, + { 3864, 3865 }, + { 3893, 3893 }, + { 3895, 3895 }, + { 3897, 3897 }, + { 3902, 3903 }, + { 3953, 3972 }, + { 3974, 3975 }, + { 3981, 3991 }, + { 3993, 4028 }, + { 4038, 4038 }, + { 4139, 4158 }, + { 4182, 4185 }, + { 4190, 4192 }, + { 4194, 4196 }, + { 4199, 4205 }, + { 4209, 4212 }, + { 4226, 4237 }, + { 4239, 4239 }, + { 4250, 4253 }, + { 4957, 4959 }, + { 5906, 5908 }, + { 5938, 5940 }, + { 5970, 5971 }, + { 6002, 6003 }, + { 6068, 6099 }, + { 6109, 6109 }, + { 6155, 6157 }, + { 6277, 6278 }, + { 6313, 6313 }, + { 6432, 6443 }, + { 6448, 6459 }, + { 6679, 6683 }, + { 6741, 6750 }, + { 6752, 6780 }, + { 6783, 6783 }, + { 6832, 6848 }, + { 6912, 6916 }, + { 6964, 6980 }, + { 7019, 7027 }, + { 7040, 7042 }, + { 7073, 7085 }, + { 7142, 7155 }, + { 7204, 7223 }, + { 7376, 7378 }, + { 7380, 7400 }, + { 7405, 7405 }, + { 7412, 7412 }, + { 7415, 7417 }, + { 7616, 7673 }, + { 7675, 7679 }, + { 8400, 8432 }, + { 11503, 11505 }, + { 11647, 11647 }, + { 11744, 11775 }, + { 12330, 12335 }, + { 12441, 12442 }, + { 42607, 42610 }, + { 42612, 42621 }, + { 42654, 42655 }, + { 42736, 42737 }, + { 43010, 43010 }, + { 43014, 43014 }, + { 43019, 43019 }, + { 43043, 43047 }, + { 43052, 43052 }, + { 43136, 43137 }, + { 43188, 43205 }, + { 43232, 43249 }, + { 43263, 43263 }, + { 43302, 43309 }, + { 43335, 43347 }, + { 43392, 43395 }, + { 43443, 43456 }, + { 43493, 43493 }, + { 43561, 43574 }, + { 43587, 43587 }, + { 43596, 43597 }, + { 43643, 43645 }, + { 43696, 43696 }, + { 43698, 43700 }, + { 43703, 43704 }, + { 43710, 43711 }, + { 43713, 43713 }, + { 43755, 43759 }, + { 43765, 43766 }, + { 44003, 44010 }, + { 44012, 44013 }, + { 64286, 64286 }, + { 65024, 65039 }, + { 65056, 65071 }, +}; +static const URange32 M_range32[] = { + { 66045, 66045 }, + { 66272, 66272 }, + { 66422, 66426 }, + { 68097, 68099 }, + { 68101, 68102 }, + { 68108, 68111 }, + { 68152, 68154 }, + { 68159, 68159 }, + { 68325, 68326 }, + { 68900, 68903 }, + { 69291, 69292 }, + { 69446, 69456 }, + { 69632, 69634 }, + { 69688, 69702 }, + { 69759, 69762 }, + { 69808, 69818 }, + { 69888, 69890 }, + { 69927, 69940 }, + { 69957, 69958 }, + { 70003, 70003 }, + { 70016, 70018 }, + { 70067, 70080 }, + { 70089, 70092 }, + { 70094, 70095 }, + { 70188, 70199 }, + { 70206, 70206 }, + { 70367, 70378 }, + { 70400, 70403 }, + { 70459, 70460 }, + { 70462, 70468 }, + { 70471, 70472 }, + { 70475, 70477 }, + { 70487, 70487 }, + { 70498, 70499 }, + { 70502, 70508 }, + { 70512, 70516 }, + { 70709, 70726 }, + { 70750, 70750 }, + { 70832, 70851 }, + { 71087, 71093 }, + { 71096, 71104 }, + { 71132, 71133 }, + { 71216, 71232 }, + { 71339, 71351 }, + { 71453, 71467 }, + { 71724, 71738 }, + { 71984, 71989 }, + { 71991, 71992 }, + { 71995, 71998 }, + { 72000, 72000 }, + { 72002, 72003 }, + { 72145, 72151 }, + { 72154, 72160 }, + { 72164, 72164 }, + { 72193, 72202 }, + { 72243, 72249 }, + { 72251, 72254 }, + { 72263, 72263 }, + { 72273, 72283 }, + { 72330, 72345 }, + { 72751, 72758 }, + { 72760, 72767 }, + { 72850, 72871 }, + { 72873, 72886 }, + { 73009, 73014 }, + { 73018, 73018 }, + { 73020, 73021 }, + { 73023, 73029 }, + { 73031, 73031 }, + { 73098, 73102 }, + { 73104, 73105 }, + { 73107, 73111 }, + { 73459, 73462 }, + { 92912, 92916 }, + { 92976, 92982 }, + { 94031, 94031 }, + { 94033, 94087 }, + { 94095, 94098 }, + { 94180, 94180 }, + { 94192, 94193 }, + { 113821, 113822 }, + { 119141, 119145 }, + { 119149, 119154 }, + { 119163, 119170 }, + { 119173, 119179 }, + { 119210, 119213 }, + { 119362, 119364 }, + { 121344, 121398 }, + { 121403, 121452 }, + { 121461, 121461 }, + { 121476, 121476 }, + { 121499, 121503 }, + { 121505, 121519 }, + { 122880, 122886 }, + { 122888, 122904 }, + { 122907, 122913 }, + { 122915, 122916 }, + { 122918, 122922 }, + { 123184, 123190 }, + { 123628, 123631 }, + { 125136, 125142 }, + { 125252, 125258 }, + { 917760, 917999 }, +}; +static const URange16 Mc_range16[] = { + { 2307, 2307 }, + { 2363, 2363 }, + { 2366, 2368 }, + { 2377, 2380 }, + { 2382, 2383 }, + { 2434, 2435 }, + { 2494, 2496 }, + { 2503, 2504 }, + { 2507, 2508 }, + { 2519, 2519 }, + { 2563, 2563 }, + { 2622, 2624 }, + { 2691, 2691 }, + { 2750, 2752 }, + { 2761, 2761 }, + { 2763, 2764 }, + { 2818, 2819 }, + { 2878, 2878 }, + { 2880, 2880 }, + { 2887, 2888 }, + { 2891, 2892 }, + { 2903, 2903 }, + { 3006, 3007 }, + { 3009, 3010 }, + { 3014, 3016 }, + { 3018, 3020 }, + { 3031, 3031 }, + { 3073, 3075 }, + { 3137, 3140 }, + { 3202, 3203 }, + { 3262, 3262 }, + { 3264, 3268 }, + { 3271, 3272 }, + { 3274, 3275 }, + { 3285, 3286 }, + { 3330, 3331 }, + { 3390, 3392 }, + { 3398, 3400 }, + { 3402, 3404 }, + { 3415, 3415 }, + { 3458, 3459 }, + { 3535, 3537 }, + { 3544, 3551 }, + { 3570, 3571 }, + { 3902, 3903 }, + { 3967, 3967 }, + { 4139, 4140 }, + { 4145, 4145 }, + { 4152, 4152 }, + { 4155, 4156 }, + { 4182, 4183 }, + { 4194, 4196 }, + { 4199, 4205 }, + { 4227, 4228 }, + { 4231, 4236 }, + { 4239, 4239 }, + { 4250, 4252 }, + { 6070, 6070 }, + { 6078, 6085 }, + { 6087, 6088 }, + { 6435, 6438 }, + { 6441, 6443 }, + { 6448, 6449 }, + { 6451, 6456 }, + { 6681, 6682 }, + { 6741, 6741 }, + { 6743, 6743 }, + { 6753, 6753 }, + { 6755, 6756 }, + { 6765, 6770 }, + { 6916, 6916 }, + { 6965, 6965 }, + { 6971, 6971 }, + { 6973, 6977 }, + { 6979, 6980 }, + { 7042, 7042 }, + { 7073, 7073 }, + { 7078, 7079 }, + { 7082, 7082 }, + { 7143, 7143 }, + { 7146, 7148 }, + { 7150, 7150 }, + { 7154, 7155 }, + { 7204, 7211 }, + { 7220, 7221 }, + { 7393, 7393 }, + { 7415, 7415 }, + { 12334, 12335 }, + { 43043, 43044 }, + { 43047, 43047 }, + { 43136, 43137 }, + { 43188, 43203 }, + { 43346, 43347 }, + { 43395, 43395 }, + { 43444, 43445 }, + { 43450, 43451 }, + { 43454, 43456 }, + { 43567, 43568 }, + { 43571, 43572 }, + { 43597, 43597 }, + { 43643, 43643 }, + { 43645, 43645 }, + { 43755, 43755 }, + { 43758, 43759 }, + { 43765, 43765 }, + { 44003, 44004 }, + { 44006, 44007 }, + { 44009, 44010 }, + { 44012, 44012 }, +}; +static const URange32 Mc_range32[] = { + { 69632, 69632 }, + { 69634, 69634 }, + { 69762, 69762 }, + { 69808, 69810 }, + { 69815, 69816 }, + { 69932, 69932 }, + { 69957, 69958 }, + { 70018, 70018 }, + { 70067, 70069 }, + { 70079, 70080 }, + { 70094, 70094 }, + { 70188, 70190 }, + { 70194, 70195 }, + { 70197, 70197 }, + { 70368, 70370 }, + { 70402, 70403 }, + { 70462, 70463 }, + { 70465, 70468 }, + { 70471, 70472 }, + { 70475, 70477 }, + { 70487, 70487 }, + { 70498, 70499 }, + { 70709, 70711 }, + { 70720, 70721 }, + { 70725, 70725 }, + { 70832, 70834 }, + { 70841, 70841 }, + { 70843, 70846 }, + { 70849, 70849 }, + { 71087, 71089 }, + { 71096, 71099 }, + { 71102, 71102 }, + { 71216, 71218 }, + { 71227, 71228 }, + { 71230, 71230 }, + { 71340, 71340 }, + { 71342, 71343 }, + { 71350, 71350 }, + { 71456, 71457 }, + { 71462, 71462 }, + { 71724, 71726 }, + { 71736, 71736 }, + { 71984, 71989 }, + { 71991, 71992 }, + { 71997, 71997 }, + { 72000, 72000 }, + { 72002, 72002 }, + { 72145, 72147 }, + { 72156, 72159 }, + { 72164, 72164 }, + { 72249, 72249 }, + { 72279, 72280 }, + { 72343, 72343 }, + { 72751, 72751 }, + { 72766, 72766 }, + { 72873, 72873 }, + { 72881, 72881 }, + { 72884, 72884 }, + { 73098, 73102 }, + { 73107, 73108 }, + { 73110, 73110 }, + { 73461, 73462 }, + { 94033, 94087 }, + { 94192, 94193 }, + { 119141, 119142 }, + { 119149, 119154 }, +}; +static const URange16 Me_range16[] = { + { 1160, 1161 }, + { 6846, 6846 }, + { 8413, 8416 }, + { 8418, 8420 }, + { 42608, 42610 }, +}; +static const URange16 Mn_range16[] = { + { 768, 879 }, + { 1155, 1159 }, + { 1425, 1469 }, + { 1471, 1471 }, + { 1473, 1474 }, + { 1476, 1477 }, + { 1479, 1479 }, + { 1552, 1562 }, + { 1611, 1631 }, + { 1648, 1648 }, + { 1750, 1756 }, + { 1759, 1764 }, + { 1767, 1768 }, + { 1770, 1773 }, + { 1809, 1809 }, + { 1840, 1866 }, + { 1958, 1968 }, + { 2027, 2035 }, + { 2045, 2045 }, + { 2070, 2073 }, + { 2075, 2083 }, + { 2085, 2087 }, + { 2089, 2093 }, + { 2137, 2139 }, + { 2259, 2273 }, + { 2275, 2306 }, + { 2362, 2362 }, + { 2364, 2364 }, + { 2369, 2376 }, + { 2381, 2381 }, + { 2385, 2391 }, + { 2402, 2403 }, + { 2433, 2433 }, + { 2492, 2492 }, + { 2497, 2500 }, + { 2509, 2509 }, + { 2530, 2531 }, + { 2558, 2558 }, + { 2561, 2562 }, + { 2620, 2620 }, + { 2625, 2626 }, + { 2631, 2632 }, + { 2635, 2637 }, + { 2641, 2641 }, + { 2672, 2673 }, + { 2677, 2677 }, + { 2689, 2690 }, + { 2748, 2748 }, + { 2753, 2757 }, + { 2759, 2760 }, + { 2765, 2765 }, + { 2786, 2787 }, + { 2810, 2815 }, + { 2817, 2817 }, + { 2876, 2876 }, + { 2879, 2879 }, + { 2881, 2884 }, + { 2893, 2893 }, + { 2901, 2902 }, + { 2914, 2915 }, + { 2946, 2946 }, + { 3008, 3008 }, + { 3021, 3021 }, + { 3072, 3072 }, + { 3076, 3076 }, + { 3134, 3136 }, + { 3142, 3144 }, + { 3146, 3149 }, + { 3157, 3158 }, + { 3170, 3171 }, + { 3201, 3201 }, + { 3260, 3260 }, + { 3263, 3263 }, + { 3270, 3270 }, + { 3276, 3277 }, + { 3298, 3299 }, + { 3328, 3329 }, + { 3387, 3388 }, + { 3393, 3396 }, + { 3405, 3405 }, + { 3426, 3427 }, + { 3457, 3457 }, + { 3530, 3530 }, + { 3538, 3540 }, + { 3542, 3542 }, + { 3633, 3633 }, + { 3636, 3642 }, + { 3655, 3662 }, + { 3761, 3761 }, + { 3764, 3772 }, + { 3784, 3789 }, + { 3864, 3865 }, + { 3893, 3893 }, + { 3895, 3895 }, + { 3897, 3897 }, + { 3953, 3966 }, + { 3968, 3972 }, + { 3974, 3975 }, + { 3981, 3991 }, + { 3993, 4028 }, + { 4038, 4038 }, + { 4141, 4144 }, + { 4146, 4151 }, + { 4153, 4154 }, + { 4157, 4158 }, + { 4184, 4185 }, + { 4190, 4192 }, + { 4209, 4212 }, + { 4226, 4226 }, + { 4229, 4230 }, + { 4237, 4237 }, + { 4253, 4253 }, + { 4957, 4959 }, + { 5906, 5908 }, + { 5938, 5940 }, + { 5970, 5971 }, + { 6002, 6003 }, + { 6068, 6069 }, + { 6071, 6077 }, + { 6086, 6086 }, + { 6089, 6099 }, + { 6109, 6109 }, + { 6155, 6157 }, + { 6277, 6278 }, + { 6313, 6313 }, + { 6432, 6434 }, + { 6439, 6440 }, + { 6450, 6450 }, + { 6457, 6459 }, + { 6679, 6680 }, + { 6683, 6683 }, + { 6742, 6742 }, + { 6744, 6750 }, + { 6752, 6752 }, + { 6754, 6754 }, + { 6757, 6764 }, + { 6771, 6780 }, + { 6783, 6783 }, + { 6832, 6845 }, + { 6847, 6848 }, + { 6912, 6915 }, + { 6964, 6964 }, + { 6966, 6970 }, + { 6972, 6972 }, + { 6978, 6978 }, + { 7019, 7027 }, + { 7040, 7041 }, + { 7074, 7077 }, + { 7080, 7081 }, + { 7083, 7085 }, + { 7142, 7142 }, + { 7144, 7145 }, + { 7149, 7149 }, + { 7151, 7153 }, + { 7212, 7219 }, + { 7222, 7223 }, + { 7376, 7378 }, + { 7380, 7392 }, + { 7394, 7400 }, + { 7405, 7405 }, + { 7412, 7412 }, + { 7416, 7417 }, + { 7616, 7673 }, + { 7675, 7679 }, + { 8400, 8412 }, + { 8417, 8417 }, + { 8421, 8432 }, + { 11503, 11505 }, + { 11647, 11647 }, + { 11744, 11775 }, + { 12330, 12333 }, + { 12441, 12442 }, + { 42607, 42607 }, + { 42612, 42621 }, + { 42654, 42655 }, + { 42736, 42737 }, + { 43010, 43010 }, + { 43014, 43014 }, + { 43019, 43019 }, + { 43045, 43046 }, + { 43052, 43052 }, + { 43204, 43205 }, + { 43232, 43249 }, + { 43263, 43263 }, + { 43302, 43309 }, + { 43335, 43345 }, + { 43392, 43394 }, + { 43443, 43443 }, + { 43446, 43449 }, + { 43452, 43453 }, + { 43493, 43493 }, + { 43561, 43566 }, + { 43569, 43570 }, + { 43573, 43574 }, + { 43587, 43587 }, + { 43596, 43596 }, + { 43644, 43644 }, + { 43696, 43696 }, + { 43698, 43700 }, + { 43703, 43704 }, + { 43710, 43711 }, + { 43713, 43713 }, + { 43756, 43757 }, + { 43766, 43766 }, + { 44005, 44005 }, + { 44008, 44008 }, + { 44013, 44013 }, + { 64286, 64286 }, + { 65024, 65039 }, + { 65056, 65071 }, +}; +static const URange32 Mn_range32[] = { + { 66045, 66045 }, + { 66272, 66272 }, + { 66422, 66426 }, + { 68097, 68099 }, + { 68101, 68102 }, + { 68108, 68111 }, + { 68152, 68154 }, + { 68159, 68159 }, + { 68325, 68326 }, + { 68900, 68903 }, + { 69291, 69292 }, + { 69446, 69456 }, + { 69633, 69633 }, + { 69688, 69702 }, + { 69759, 69761 }, + { 69811, 69814 }, + { 69817, 69818 }, + { 69888, 69890 }, + { 69927, 69931 }, + { 69933, 69940 }, + { 70003, 70003 }, + { 70016, 70017 }, + { 70070, 70078 }, + { 70089, 70092 }, + { 70095, 70095 }, + { 70191, 70193 }, + { 70196, 70196 }, + { 70198, 70199 }, + { 70206, 70206 }, + { 70367, 70367 }, + { 70371, 70378 }, + { 70400, 70401 }, + { 70459, 70460 }, + { 70464, 70464 }, + { 70502, 70508 }, + { 70512, 70516 }, + { 70712, 70719 }, + { 70722, 70724 }, + { 70726, 70726 }, + { 70750, 70750 }, + { 70835, 70840 }, + { 70842, 70842 }, + { 70847, 70848 }, + { 70850, 70851 }, + { 71090, 71093 }, + { 71100, 71101 }, + { 71103, 71104 }, + { 71132, 71133 }, + { 71219, 71226 }, + { 71229, 71229 }, + { 71231, 71232 }, + { 71339, 71339 }, + { 71341, 71341 }, + { 71344, 71349 }, + { 71351, 71351 }, + { 71453, 71455 }, + { 71458, 71461 }, + { 71463, 71467 }, + { 71727, 71735 }, + { 71737, 71738 }, + { 71995, 71996 }, + { 71998, 71998 }, + { 72003, 72003 }, + { 72148, 72151 }, + { 72154, 72155 }, + { 72160, 72160 }, + { 72193, 72202 }, + { 72243, 72248 }, + { 72251, 72254 }, + { 72263, 72263 }, + { 72273, 72278 }, + { 72281, 72283 }, + { 72330, 72342 }, + { 72344, 72345 }, + { 72752, 72758 }, + { 72760, 72765 }, + { 72767, 72767 }, + { 72850, 72871 }, + { 72874, 72880 }, + { 72882, 72883 }, + { 72885, 72886 }, + { 73009, 73014 }, + { 73018, 73018 }, + { 73020, 73021 }, + { 73023, 73029 }, + { 73031, 73031 }, + { 73104, 73105 }, + { 73109, 73109 }, + { 73111, 73111 }, + { 73459, 73460 }, + { 92912, 92916 }, + { 92976, 92982 }, + { 94031, 94031 }, + { 94095, 94098 }, + { 94180, 94180 }, + { 113821, 113822 }, + { 119143, 119145 }, + { 119163, 119170 }, + { 119173, 119179 }, + { 119210, 119213 }, + { 119362, 119364 }, + { 121344, 121398 }, + { 121403, 121452 }, + { 121461, 121461 }, + { 121476, 121476 }, + { 121499, 121503 }, + { 121505, 121519 }, + { 122880, 122886 }, + { 122888, 122904 }, + { 122907, 122913 }, + { 122915, 122916 }, + { 122918, 122922 }, + { 123184, 123190 }, + { 123628, 123631 }, + { 125136, 125142 }, + { 125252, 125258 }, + { 917760, 917999 }, +}; +static const URange16 N_range16[] = { + { 48, 57 }, + { 178, 179 }, + { 185, 185 }, + { 188, 190 }, + { 1632, 1641 }, + { 1776, 1785 }, + { 1984, 1993 }, + { 2406, 2415 }, + { 2534, 2543 }, + { 2548, 2553 }, + { 2662, 2671 }, + { 2790, 2799 }, + { 2918, 2927 }, + { 2930, 2935 }, + { 3046, 3058 }, + { 3174, 3183 }, + { 3192, 3198 }, + { 3302, 3311 }, + { 3416, 3422 }, + { 3430, 3448 }, + { 3558, 3567 }, + { 3664, 3673 }, + { 3792, 3801 }, + { 3872, 3891 }, + { 4160, 4169 }, + { 4240, 4249 }, + { 4969, 4988 }, + { 5870, 5872 }, + { 6112, 6121 }, + { 6128, 6137 }, + { 6160, 6169 }, + { 6470, 6479 }, + { 6608, 6618 }, + { 6784, 6793 }, + { 6800, 6809 }, + { 6992, 7001 }, + { 7088, 7097 }, + { 7232, 7241 }, + { 7248, 7257 }, + { 8304, 8304 }, + { 8308, 8313 }, + { 8320, 8329 }, + { 8528, 8578 }, + { 8581, 8585 }, + { 9312, 9371 }, + { 9450, 9471 }, + { 10102, 10131 }, + { 11517, 11517 }, + { 12295, 12295 }, + { 12321, 12329 }, + { 12344, 12346 }, + { 12690, 12693 }, + { 12832, 12841 }, + { 12872, 12879 }, + { 12881, 12895 }, + { 12928, 12937 }, + { 12977, 12991 }, + { 42528, 42537 }, + { 42726, 42735 }, + { 43056, 43061 }, + { 43216, 43225 }, + { 43264, 43273 }, + { 43472, 43481 }, + { 43504, 43513 }, + { 43600, 43609 }, + { 44016, 44025 }, + { 65296, 65305 }, +}; +static const URange32 N_range32[] = { + { 65799, 65843 }, + { 65856, 65912 }, + { 65930, 65931 }, + { 66273, 66299 }, + { 66336, 66339 }, + { 66369, 66369 }, + { 66378, 66378 }, + { 66513, 66517 }, + { 66720, 66729 }, + { 67672, 67679 }, + { 67705, 67711 }, + { 67751, 67759 }, + { 67835, 67839 }, + { 67862, 67867 }, + { 68028, 68029 }, + { 68032, 68047 }, + { 68050, 68095 }, + { 68160, 68168 }, + { 68221, 68222 }, + { 68253, 68255 }, + { 68331, 68335 }, + { 68440, 68447 }, + { 68472, 68479 }, + { 68521, 68527 }, + { 68858, 68863 }, + { 68912, 68921 }, + { 69216, 69246 }, + { 69405, 69414 }, + { 69457, 69460 }, + { 69573, 69579 }, + { 69714, 69743 }, + { 69872, 69881 }, + { 69942, 69951 }, + { 70096, 70105 }, + { 70113, 70132 }, + { 70384, 70393 }, + { 70736, 70745 }, + { 70864, 70873 }, + { 71248, 71257 }, + { 71360, 71369 }, + { 71472, 71483 }, + { 71904, 71922 }, + { 72016, 72025 }, + { 72784, 72812 }, + { 73040, 73049 }, + { 73120, 73129 }, + { 73664, 73684 }, + { 74752, 74862 }, + { 92768, 92777 }, + { 93008, 93017 }, + { 93019, 93025 }, + { 93824, 93846 }, + { 119520, 119539 }, + { 119648, 119672 }, + { 120782, 120831 }, + { 123200, 123209 }, + { 123632, 123641 }, + { 125127, 125135 }, + { 125264, 125273 }, + { 126065, 126123 }, + { 126125, 126127 }, + { 126129, 126132 }, + { 126209, 126253 }, + { 126255, 126269 }, + { 127232, 127244 }, + { 130032, 130041 }, +}; +static const URange16 Nd_range16[] = { + { 48, 57 }, + { 1632, 1641 }, + { 1776, 1785 }, + { 1984, 1993 }, + { 2406, 2415 }, + { 2534, 2543 }, + { 2662, 2671 }, + { 2790, 2799 }, + { 2918, 2927 }, + { 3046, 3055 }, + { 3174, 3183 }, + { 3302, 3311 }, + { 3430, 3439 }, + { 3558, 3567 }, + { 3664, 3673 }, + { 3792, 3801 }, + { 3872, 3881 }, + { 4160, 4169 }, + { 4240, 4249 }, + { 6112, 6121 }, + { 6160, 6169 }, + { 6470, 6479 }, + { 6608, 6617 }, + { 6784, 6793 }, + { 6800, 6809 }, + { 6992, 7001 }, + { 7088, 7097 }, + { 7232, 7241 }, + { 7248, 7257 }, + { 42528, 42537 }, + { 43216, 43225 }, + { 43264, 43273 }, + { 43472, 43481 }, + { 43504, 43513 }, + { 43600, 43609 }, + { 44016, 44025 }, + { 65296, 65305 }, +}; +static const URange32 Nd_range32[] = { + { 66720, 66729 }, + { 68912, 68921 }, + { 69734, 69743 }, + { 69872, 69881 }, + { 69942, 69951 }, + { 70096, 70105 }, + { 70384, 70393 }, + { 70736, 70745 }, + { 70864, 70873 }, + { 71248, 71257 }, + { 71360, 71369 }, + { 71472, 71481 }, + { 71904, 71913 }, + { 72016, 72025 }, + { 72784, 72793 }, + { 73040, 73049 }, + { 73120, 73129 }, + { 92768, 92777 }, + { 93008, 93017 }, + { 120782, 120831 }, + { 123200, 123209 }, + { 123632, 123641 }, + { 125264, 125273 }, + { 130032, 130041 }, +}; +static const URange16 Nl_range16[] = { + { 5870, 5872 }, + { 8544, 8578 }, + { 8581, 8584 }, + { 12295, 12295 }, + { 12321, 12329 }, + { 12344, 12346 }, + { 42726, 42735 }, +}; +static const URange32 Nl_range32[] = { + { 65856, 65908 }, + { 66369, 66369 }, + { 66378, 66378 }, + { 66513, 66517 }, + { 74752, 74862 }, +}; +static const URange16 No_range16[] = { + { 178, 179 }, + { 185, 185 }, + { 188, 190 }, + { 2548, 2553 }, + { 2930, 2935 }, + { 3056, 3058 }, + { 3192, 3198 }, + { 3416, 3422 }, + { 3440, 3448 }, + { 3882, 3891 }, + { 4969, 4988 }, + { 6128, 6137 }, + { 6618, 6618 }, + { 8304, 8304 }, + { 8308, 8313 }, + { 8320, 8329 }, + { 8528, 8543 }, + { 8585, 8585 }, + { 9312, 9371 }, + { 9450, 9471 }, + { 10102, 10131 }, + { 11517, 11517 }, + { 12690, 12693 }, + { 12832, 12841 }, + { 12872, 12879 }, + { 12881, 12895 }, + { 12928, 12937 }, + { 12977, 12991 }, + { 43056, 43061 }, +}; +static const URange32 No_range32[] = { + { 65799, 65843 }, + { 65909, 65912 }, + { 65930, 65931 }, + { 66273, 66299 }, + { 66336, 66339 }, + { 67672, 67679 }, + { 67705, 67711 }, + { 67751, 67759 }, + { 67835, 67839 }, + { 67862, 67867 }, + { 68028, 68029 }, + { 68032, 68047 }, + { 68050, 68095 }, + { 68160, 68168 }, + { 68221, 68222 }, + { 68253, 68255 }, + { 68331, 68335 }, + { 68440, 68447 }, + { 68472, 68479 }, + { 68521, 68527 }, + { 68858, 68863 }, + { 69216, 69246 }, + { 69405, 69414 }, + { 69457, 69460 }, + { 69573, 69579 }, + { 69714, 69733 }, + { 70113, 70132 }, + { 71482, 71483 }, + { 71914, 71922 }, + { 72794, 72812 }, + { 73664, 73684 }, + { 93019, 93025 }, + { 93824, 93846 }, + { 119520, 119539 }, + { 119648, 119672 }, + { 125127, 125135 }, + { 126065, 126123 }, + { 126125, 126127 }, + { 126129, 126132 }, + { 126209, 126253 }, + { 126255, 126269 }, + { 127232, 127244 }, +}; +static const URange16 P_range16[] = { + { 33, 35 }, + { 37, 42 }, + { 44, 47 }, + { 58, 59 }, + { 63, 64 }, + { 91, 93 }, + { 95, 95 }, + { 123, 123 }, + { 125, 125 }, + { 161, 161 }, + { 167, 167 }, + { 171, 171 }, + { 182, 183 }, + { 187, 187 }, + { 191, 191 }, + { 894, 894 }, + { 903, 903 }, + { 1370, 1375 }, + { 1417, 1418 }, + { 1470, 1470 }, + { 1472, 1472 }, + { 1475, 1475 }, + { 1478, 1478 }, + { 1523, 1524 }, + { 1545, 1546 }, + { 1548, 1549 }, + { 1563, 1563 }, + { 1566, 1567 }, + { 1642, 1645 }, + { 1748, 1748 }, + { 1792, 1805 }, + { 2039, 2041 }, + { 2096, 2110 }, + { 2142, 2142 }, + { 2404, 2405 }, + { 2416, 2416 }, + { 2557, 2557 }, + { 2678, 2678 }, + { 2800, 2800 }, + { 3191, 3191 }, + { 3204, 3204 }, + { 3572, 3572 }, + { 3663, 3663 }, + { 3674, 3675 }, + { 3844, 3858 }, + { 3860, 3860 }, + { 3898, 3901 }, + { 3973, 3973 }, + { 4048, 4052 }, + { 4057, 4058 }, + { 4170, 4175 }, + { 4347, 4347 }, + { 4960, 4968 }, + { 5120, 5120 }, + { 5742, 5742 }, + { 5787, 5788 }, + { 5867, 5869 }, + { 5941, 5942 }, + { 6100, 6102 }, + { 6104, 6106 }, + { 6144, 6154 }, + { 6468, 6469 }, + { 6686, 6687 }, + { 6816, 6822 }, + { 6824, 6829 }, + { 7002, 7008 }, + { 7164, 7167 }, + { 7227, 7231 }, + { 7294, 7295 }, + { 7360, 7367 }, + { 7379, 7379 }, + { 8208, 8231 }, + { 8240, 8259 }, + { 8261, 8273 }, + { 8275, 8286 }, + { 8317, 8318 }, + { 8333, 8334 }, + { 8968, 8971 }, + { 9001, 9002 }, + { 10088, 10101 }, + { 10181, 10182 }, + { 10214, 10223 }, + { 10627, 10648 }, + { 10712, 10715 }, + { 10748, 10749 }, + { 11513, 11516 }, + { 11518, 11519 }, + { 11632, 11632 }, + { 11776, 11822 }, + { 11824, 11855 }, + { 11858, 11858 }, + { 12289, 12291 }, + { 12296, 12305 }, + { 12308, 12319 }, + { 12336, 12336 }, + { 12349, 12349 }, + { 12448, 12448 }, + { 12539, 12539 }, + { 42238, 42239 }, + { 42509, 42511 }, + { 42611, 42611 }, + { 42622, 42622 }, + { 42738, 42743 }, + { 43124, 43127 }, + { 43214, 43215 }, + { 43256, 43258 }, + { 43260, 43260 }, + { 43310, 43311 }, + { 43359, 43359 }, + { 43457, 43469 }, + { 43486, 43487 }, + { 43612, 43615 }, + { 43742, 43743 }, + { 43760, 43761 }, + { 44011, 44011 }, + { 64830, 64831 }, + { 65040, 65049 }, + { 65072, 65106 }, + { 65108, 65121 }, + { 65123, 65123 }, + { 65128, 65128 }, + { 65130, 65131 }, + { 65281, 65283 }, + { 65285, 65290 }, + { 65292, 65295 }, + { 65306, 65307 }, + { 65311, 65312 }, + { 65339, 65341 }, + { 65343, 65343 }, + { 65371, 65371 }, + { 65373, 65373 }, + { 65375, 65381 }, +}; +static const URange32 P_range32[] = { + { 65792, 65794 }, + { 66463, 66463 }, + { 66512, 66512 }, + { 66927, 66927 }, + { 67671, 67671 }, + { 67871, 67871 }, + { 67903, 67903 }, + { 68176, 68184 }, + { 68223, 68223 }, + { 68336, 68342 }, + { 68409, 68415 }, + { 68505, 68508 }, + { 69293, 69293 }, + { 69461, 69465 }, + { 69703, 69709 }, + { 69819, 69820 }, + { 69822, 69825 }, + { 69952, 69955 }, + { 70004, 70005 }, + { 70085, 70088 }, + { 70093, 70093 }, + { 70107, 70107 }, + { 70109, 70111 }, + { 70200, 70205 }, + { 70313, 70313 }, + { 70731, 70735 }, + { 70746, 70747 }, + { 70749, 70749 }, + { 70854, 70854 }, + { 71105, 71127 }, + { 71233, 71235 }, + { 71264, 71276 }, + { 71484, 71486 }, + { 71739, 71739 }, + { 72004, 72006 }, + { 72162, 72162 }, + { 72255, 72262 }, + { 72346, 72348 }, + { 72350, 72354 }, + { 72769, 72773 }, + { 72816, 72817 }, + { 73463, 73464 }, + { 73727, 73727 }, + { 74864, 74868 }, + { 92782, 92783 }, + { 92917, 92917 }, + { 92983, 92987 }, + { 92996, 92996 }, + { 93847, 93850 }, + { 94178, 94178 }, + { 113823, 113823 }, + { 121479, 121483 }, + { 125278, 125279 }, +}; +static const URange16 Pc_range16[] = { + { 95, 95 }, + { 8255, 8256 }, + { 8276, 8276 }, + { 65075, 65076 }, + { 65101, 65103 }, + { 65343, 65343 }, +}; +static const URange16 Pd_range16[] = { + { 45, 45 }, + { 1418, 1418 }, + { 1470, 1470 }, + { 5120, 5120 }, + { 6150, 6150 }, + { 8208, 8213 }, + { 11799, 11799 }, + { 11802, 11802 }, + { 11834, 11835 }, + { 11840, 11840 }, + { 12316, 12316 }, + { 12336, 12336 }, + { 12448, 12448 }, + { 65073, 65074 }, + { 65112, 65112 }, + { 65123, 65123 }, + { 65293, 65293 }, +}; +static const URange32 Pd_range32[] = { + { 69293, 69293 }, +}; +static const URange16 Pe_range16[] = { + { 41, 41 }, + { 93, 93 }, + { 125, 125 }, + { 3899, 3899 }, + { 3901, 3901 }, + { 5788, 5788 }, + { 8262, 8262 }, + { 8318, 8318 }, + { 8334, 8334 }, + { 8969, 8969 }, + { 8971, 8971 }, + { 9002, 9002 }, + { 10089, 10089 }, + { 10091, 10091 }, + { 10093, 10093 }, + { 10095, 10095 }, + { 10097, 10097 }, + { 10099, 10099 }, + { 10101, 10101 }, + { 10182, 10182 }, + { 10215, 10215 }, + { 10217, 10217 }, + { 10219, 10219 }, + { 10221, 10221 }, + { 10223, 10223 }, + { 10628, 10628 }, + { 10630, 10630 }, + { 10632, 10632 }, + { 10634, 10634 }, + { 10636, 10636 }, + { 10638, 10638 }, + { 10640, 10640 }, + { 10642, 10642 }, + { 10644, 10644 }, + { 10646, 10646 }, + { 10648, 10648 }, + { 10713, 10713 }, + { 10715, 10715 }, + { 10749, 10749 }, + { 11811, 11811 }, + { 11813, 11813 }, + { 11815, 11815 }, + { 11817, 11817 }, + { 12297, 12297 }, + { 12299, 12299 }, + { 12301, 12301 }, + { 12303, 12303 }, + { 12305, 12305 }, + { 12309, 12309 }, + { 12311, 12311 }, + { 12313, 12313 }, + { 12315, 12315 }, + { 12318, 12319 }, + { 64830, 64830 }, + { 65048, 65048 }, + { 65078, 65078 }, + { 65080, 65080 }, + { 65082, 65082 }, + { 65084, 65084 }, + { 65086, 65086 }, + { 65088, 65088 }, + { 65090, 65090 }, + { 65092, 65092 }, + { 65096, 65096 }, + { 65114, 65114 }, + { 65116, 65116 }, + { 65118, 65118 }, + { 65289, 65289 }, + { 65341, 65341 }, + { 65373, 65373 }, + { 65376, 65376 }, + { 65379, 65379 }, +}; +static const URange16 Pf_range16[] = { + { 187, 187 }, + { 8217, 8217 }, + { 8221, 8221 }, + { 8250, 8250 }, + { 11779, 11779 }, + { 11781, 11781 }, + { 11786, 11786 }, + { 11789, 11789 }, + { 11805, 11805 }, + { 11809, 11809 }, +}; +static const URange16 Pi_range16[] = { + { 171, 171 }, + { 8216, 8216 }, + { 8219, 8220 }, + { 8223, 8223 }, + { 8249, 8249 }, + { 11778, 11778 }, + { 11780, 11780 }, + { 11785, 11785 }, + { 11788, 11788 }, + { 11804, 11804 }, + { 11808, 11808 }, +}; +static const URange16 Po_range16[] = { + { 33, 35 }, + { 37, 39 }, + { 42, 42 }, + { 44, 44 }, + { 46, 47 }, + { 58, 59 }, + { 63, 64 }, + { 92, 92 }, + { 161, 161 }, + { 167, 167 }, + { 182, 183 }, + { 191, 191 }, + { 894, 894 }, + { 903, 903 }, + { 1370, 1375 }, + { 1417, 1417 }, + { 1472, 1472 }, + { 1475, 1475 }, + { 1478, 1478 }, + { 1523, 1524 }, + { 1545, 1546 }, + { 1548, 1549 }, + { 1563, 1563 }, + { 1566, 1567 }, + { 1642, 1645 }, + { 1748, 1748 }, + { 1792, 1805 }, + { 2039, 2041 }, + { 2096, 2110 }, + { 2142, 2142 }, + { 2404, 2405 }, + { 2416, 2416 }, + { 2557, 2557 }, + { 2678, 2678 }, + { 2800, 2800 }, + { 3191, 3191 }, + { 3204, 3204 }, + { 3572, 3572 }, + { 3663, 3663 }, + { 3674, 3675 }, + { 3844, 3858 }, + { 3860, 3860 }, + { 3973, 3973 }, + { 4048, 4052 }, + { 4057, 4058 }, + { 4170, 4175 }, + { 4347, 4347 }, + { 4960, 4968 }, + { 5742, 5742 }, + { 5867, 5869 }, + { 5941, 5942 }, + { 6100, 6102 }, + { 6104, 6106 }, + { 6144, 6149 }, + { 6151, 6154 }, + { 6468, 6469 }, + { 6686, 6687 }, + { 6816, 6822 }, + { 6824, 6829 }, + { 7002, 7008 }, + { 7164, 7167 }, + { 7227, 7231 }, + { 7294, 7295 }, + { 7360, 7367 }, + { 7379, 7379 }, + { 8214, 8215 }, + { 8224, 8231 }, + { 8240, 8248 }, + { 8251, 8254 }, + { 8257, 8259 }, + { 8263, 8273 }, + { 8275, 8275 }, + { 8277, 8286 }, + { 11513, 11516 }, + { 11518, 11519 }, + { 11632, 11632 }, + { 11776, 11777 }, + { 11782, 11784 }, + { 11787, 11787 }, + { 11790, 11798 }, + { 11800, 11801 }, + { 11803, 11803 }, + { 11806, 11807 }, + { 11818, 11822 }, + { 11824, 11833 }, + { 11836, 11839 }, + { 11841, 11841 }, + { 11843, 11855 }, + { 11858, 11858 }, + { 12289, 12291 }, + { 12349, 12349 }, + { 12539, 12539 }, + { 42238, 42239 }, + { 42509, 42511 }, + { 42611, 42611 }, + { 42622, 42622 }, + { 42738, 42743 }, + { 43124, 43127 }, + { 43214, 43215 }, + { 43256, 43258 }, + { 43260, 43260 }, + { 43310, 43311 }, + { 43359, 43359 }, + { 43457, 43469 }, + { 43486, 43487 }, + { 43612, 43615 }, + { 43742, 43743 }, + { 43760, 43761 }, + { 44011, 44011 }, + { 65040, 65046 }, + { 65049, 65049 }, + { 65072, 65072 }, + { 65093, 65094 }, + { 65097, 65100 }, + { 65104, 65106 }, + { 65108, 65111 }, + { 65119, 65121 }, + { 65128, 65128 }, + { 65130, 65131 }, + { 65281, 65283 }, + { 65285, 65287 }, + { 65290, 65290 }, + { 65292, 65292 }, + { 65294, 65295 }, + { 65306, 65307 }, + { 65311, 65312 }, + { 65340, 65340 }, + { 65377, 65377 }, + { 65380, 65381 }, +}; +static const URange32 Po_range32[] = { + { 65792, 65794 }, + { 66463, 66463 }, + { 66512, 66512 }, + { 66927, 66927 }, + { 67671, 67671 }, + { 67871, 67871 }, + { 67903, 67903 }, + { 68176, 68184 }, + { 68223, 68223 }, + { 68336, 68342 }, + { 68409, 68415 }, + { 68505, 68508 }, + { 69461, 69465 }, + { 69703, 69709 }, + { 69819, 69820 }, + { 69822, 69825 }, + { 69952, 69955 }, + { 70004, 70005 }, + { 70085, 70088 }, + { 70093, 70093 }, + { 70107, 70107 }, + { 70109, 70111 }, + { 70200, 70205 }, + { 70313, 70313 }, + { 70731, 70735 }, + { 70746, 70747 }, + { 70749, 70749 }, + { 70854, 70854 }, + { 71105, 71127 }, + { 71233, 71235 }, + { 71264, 71276 }, + { 71484, 71486 }, + { 71739, 71739 }, + { 72004, 72006 }, + { 72162, 72162 }, + { 72255, 72262 }, + { 72346, 72348 }, + { 72350, 72354 }, + { 72769, 72773 }, + { 72816, 72817 }, + { 73463, 73464 }, + { 73727, 73727 }, + { 74864, 74868 }, + { 92782, 92783 }, + { 92917, 92917 }, + { 92983, 92987 }, + { 92996, 92996 }, + { 93847, 93850 }, + { 94178, 94178 }, + { 113823, 113823 }, + { 121479, 121483 }, + { 125278, 125279 }, +}; +static const URange16 Ps_range16[] = { + { 40, 40 }, + { 91, 91 }, + { 123, 123 }, + { 3898, 3898 }, + { 3900, 3900 }, + { 5787, 5787 }, + { 8218, 8218 }, + { 8222, 8222 }, + { 8261, 8261 }, + { 8317, 8317 }, + { 8333, 8333 }, + { 8968, 8968 }, + { 8970, 8970 }, + { 9001, 9001 }, + { 10088, 10088 }, + { 10090, 10090 }, + { 10092, 10092 }, + { 10094, 10094 }, + { 10096, 10096 }, + { 10098, 10098 }, + { 10100, 10100 }, + { 10181, 10181 }, + { 10214, 10214 }, + { 10216, 10216 }, + { 10218, 10218 }, + { 10220, 10220 }, + { 10222, 10222 }, + { 10627, 10627 }, + { 10629, 10629 }, + { 10631, 10631 }, + { 10633, 10633 }, + { 10635, 10635 }, + { 10637, 10637 }, + { 10639, 10639 }, + { 10641, 10641 }, + { 10643, 10643 }, + { 10645, 10645 }, + { 10647, 10647 }, + { 10712, 10712 }, + { 10714, 10714 }, + { 10748, 10748 }, + { 11810, 11810 }, + { 11812, 11812 }, + { 11814, 11814 }, + { 11816, 11816 }, + { 11842, 11842 }, + { 12296, 12296 }, + { 12298, 12298 }, + { 12300, 12300 }, + { 12302, 12302 }, + { 12304, 12304 }, + { 12308, 12308 }, + { 12310, 12310 }, + { 12312, 12312 }, + { 12314, 12314 }, + { 12317, 12317 }, + { 64831, 64831 }, + { 65047, 65047 }, + { 65077, 65077 }, + { 65079, 65079 }, + { 65081, 65081 }, + { 65083, 65083 }, + { 65085, 65085 }, + { 65087, 65087 }, + { 65089, 65089 }, + { 65091, 65091 }, + { 65095, 65095 }, + { 65113, 65113 }, + { 65115, 65115 }, + { 65117, 65117 }, + { 65288, 65288 }, + { 65339, 65339 }, + { 65371, 65371 }, + { 65375, 65375 }, + { 65378, 65378 }, +}; +static const URange16 S_range16[] = { + { 36, 36 }, + { 43, 43 }, + { 60, 62 }, + { 94, 94 }, + { 96, 96 }, + { 124, 124 }, + { 126, 126 }, + { 162, 166 }, + { 168, 169 }, + { 172, 172 }, + { 174, 177 }, + { 180, 180 }, + { 184, 184 }, + { 215, 215 }, + { 247, 247 }, + { 706, 709 }, + { 722, 735 }, + { 741, 747 }, + { 749, 749 }, + { 751, 767 }, + { 885, 885 }, + { 900, 901 }, + { 1014, 1014 }, + { 1154, 1154 }, + { 1421, 1423 }, + { 1542, 1544 }, + { 1547, 1547 }, + { 1550, 1551 }, + { 1758, 1758 }, + { 1769, 1769 }, + { 1789, 1790 }, + { 2038, 2038 }, + { 2046, 2047 }, + { 2546, 2547 }, + { 2554, 2555 }, + { 2801, 2801 }, + { 2928, 2928 }, + { 3059, 3066 }, + { 3199, 3199 }, + { 3407, 3407 }, + { 3449, 3449 }, + { 3647, 3647 }, + { 3841, 3843 }, + { 3859, 3859 }, + { 3861, 3863 }, + { 3866, 3871 }, + { 3892, 3892 }, + { 3894, 3894 }, + { 3896, 3896 }, + { 4030, 4037 }, + { 4039, 4044 }, + { 4046, 4047 }, + { 4053, 4056 }, + { 4254, 4255 }, + { 5008, 5017 }, + { 5741, 5741 }, + { 6107, 6107 }, + { 6464, 6464 }, + { 6622, 6655 }, + { 7009, 7018 }, + { 7028, 7036 }, + { 8125, 8125 }, + { 8127, 8129 }, + { 8141, 8143 }, + { 8157, 8159 }, + { 8173, 8175 }, + { 8189, 8190 }, + { 8260, 8260 }, + { 8274, 8274 }, + { 8314, 8316 }, + { 8330, 8332 }, + { 8352, 8383 }, + { 8448, 8449 }, + { 8451, 8454 }, + { 8456, 8457 }, + { 8468, 8468 }, + { 8470, 8472 }, + { 8478, 8483 }, + { 8485, 8485 }, + { 8487, 8487 }, + { 8489, 8489 }, + { 8494, 8494 }, + { 8506, 8507 }, + { 8512, 8516 }, + { 8522, 8525 }, + { 8527, 8527 }, + { 8586, 8587 }, + { 8592, 8967 }, + { 8972, 9000 }, + { 9003, 9254 }, + { 9280, 9290 }, + { 9372, 9449 }, + { 9472, 10087 }, + { 10132, 10180 }, + { 10183, 10213 }, + { 10224, 10626 }, + { 10649, 10711 }, + { 10716, 10747 }, + { 10750, 11123 }, + { 11126, 11157 }, + { 11159, 11263 }, + { 11493, 11498 }, + { 11856, 11857 }, + { 11904, 11929 }, + { 11931, 12019 }, + { 12032, 12245 }, + { 12272, 12283 }, + { 12292, 12292 }, + { 12306, 12307 }, + { 12320, 12320 }, + { 12342, 12343 }, + { 12350, 12351 }, + { 12443, 12444 }, + { 12688, 12689 }, + { 12694, 12703 }, + { 12736, 12771 }, + { 12800, 12830 }, + { 12842, 12871 }, + { 12880, 12880 }, + { 12896, 12927 }, + { 12938, 12976 }, + { 12992, 13311 }, + { 19904, 19967 }, + { 42128, 42182 }, + { 42752, 42774 }, + { 42784, 42785 }, + { 42889, 42890 }, + { 43048, 43051 }, + { 43062, 43065 }, + { 43639, 43641 }, + { 43867, 43867 }, + { 43882, 43883 }, + { 64297, 64297 }, + { 64434, 64449 }, + { 65020, 65021 }, + { 65122, 65122 }, + { 65124, 65126 }, + { 65129, 65129 }, + { 65284, 65284 }, + { 65291, 65291 }, + { 65308, 65310 }, + { 65342, 65342 }, + { 65344, 65344 }, + { 65372, 65372 }, + { 65374, 65374 }, + { 65504, 65510 }, + { 65512, 65518 }, + { 65532, 65533 }, +}; +static const URange32 S_range32[] = { + { 65847, 65855 }, + { 65913, 65929 }, + { 65932, 65934 }, + { 65936, 65948 }, + { 65952, 65952 }, + { 66000, 66044 }, + { 67703, 67704 }, + { 68296, 68296 }, + { 71487, 71487 }, + { 73685, 73713 }, + { 92988, 92991 }, + { 92997, 92997 }, + { 113820, 113820 }, + { 118784, 119029 }, + { 119040, 119078 }, + { 119081, 119140 }, + { 119146, 119148 }, + { 119171, 119172 }, + { 119180, 119209 }, + { 119214, 119272 }, + { 119296, 119361 }, + { 119365, 119365 }, + { 119552, 119638 }, + { 120513, 120513 }, + { 120539, 120539 }, + { 120571, 120571 }, + { 120597, 120597 }, + { 120629, 120629 }, + { 120655, 120655 }, + { 120687, 120687 }, + { 120713, 120713 }, + { 120745, 120745 }, + { 120771, 120771 }, + { 120832, 121343 }, + { 121399, 121402 }, + { 121453, 121460 }, + { 121462, 121475 }, + { 121477, 121478 }, + { 123215, 123215 }, + { 123647, 123647 }, + { 126124, 126124 }, + { 126128, 126128 }, + { 126254, 126254 }, + { 126704, 126705 }, + { 126976, 127019 }, + { 127024, 127123 }, + { 127136, 127150 }, + { 127153, 127167 }, + { 127169, 127183 }, + { 127185, 127221 }, + { 127245, 127405 }, + { 127462, 127490 }, + { 127504, 127547 }, + { 127552, 127560 }, + { 127568, 127569 }, + { 127584, 127589 }, + { 127744, 128727 }, + { 128736, 128748 }, + { 128752, 128764 }, + { 128768, 128883 }, + { 128896, 128984 }, + { 128992, 129003 }, + { 129024, 129035 }, + { 129040, 129095 }, + { 129104, 129113 }, + { 129120, 129159 }, + { 129168, 129197 }, + { 129200, 129201 }, + { 129280, 129400 }, + { 129402, 129483 }, + { 129485, 129619 }, + { 129632, 129645 }, + { 129648, 129652 }, + { 129656, 129658 }, + { 129664, 129670 }, + { 129680, 129704 }, + { 129712, 129718 }, + { 129728, 129730 }, + { 129744, 129750 }, + { 129792, 129938 }, + { 129940, 129994 }, +}; +static const URange16 Sc_range16[] = { + { 36, 36 }, + { 162, 165 }, + { 1423, 1423 }, + { 1547, 1547 }, + { 2046, 2047 }, + { 2546, 2547 }, + { 2555, 2555 }, + { 2801, 2801 }, + { 3065, 3065 }, + { 3647, 3647 }, + { 6107, 6107 }, + { 8352, 8383 }, + { 43064, 43064 }, + { 65020, 65020 }, + { 65129, 65129 }, + { 65284, 65284 }, + { 65504, 65505 }, + { 65509, 65510 }, +}; +static const URange32 Sc_range32[] = { + { 73693, 73696 }, + { 123647, 123647 }, + { 126128, 126128 }, +}; +static const URange16 Sk_range16[] = { + { 94, 94 }, + { 96, 96 }, + { 168, 168 }, + { 175, 175 }, + { 180, 180 }, + { 184, 184 }, + { 706, 709 }, + { 722, 735 }, + { 741, 747 }, + { 749, 749 }, + { 751, 767 }, + { 885, 885 }, + { 900, 901 }, + { 8125, 8125 }, + { 8127, 8129 }, + { 8141, 8143 }, + { 8157, 8159 }, + { 8173, 8175 }, + { 8189, 8190 }, + { 12443, 12444 }, + { 42752, 42774 }, + { 42784, 42785 }, + { 42889, 42890 }, + { 43867, 43867 }, + { 43882, 43883 }, + { 64434, 64449 }, + { 65342, 65342 }, + { 65344, 65344 }, + { 65507, 65507 }, +}; +static const URange32 Sk_range32[] = { + { 127995, 127999 }, +}; +static const URange16 Sm_range16[] = { + { 43, 43 }, + { 60, 62 }, + { 124, 124 }, + { 126, 126 }, + { 172, 172 }, + { 177, 177 }, + { 215, 215 }, + { 247, 247 }, + { 1014, 1014 }, + { 1542, 1544 }, + { 8260, 8260 }, + { 8274, 8274 }, + { 8314, 8316 }, + { 8330, 8332 }, + { 8472, 8472 }, + { 8512, 8516 }, + { 8523, 8523 }, + { 8592, 8596 }, + { 8602, 8603 }, + { 8608, 8608 }, + { 8611, 8611 }, + { 8614, 8614 }, + { 8622, 8622 }, + { 8654, 8655 }, + { 8658, 8658 }, + { 8660, 8660 }, + { 8692, 8959 }, + { 8992, 8993 }, + { 9084, 9084 }, + { 9115, 9139 }, + { 9180, 9185 }, + { 9655, 9655 }, + { 9665, 9665 }, + { 9720, 9727 }, + { 9839, 9839 }, + { 10176, 10180 }, + { 10183, 10213 }, + { 10224, 10239 }, + { 10496, 10626 }, + { 10649, 10711 }, + { 10716, 10747 }, + { 10750, 11007 }, + { 11056, 11076 }, + { 11079, 11084 }, + { 64297, 64297 }, + { 65122, 65122 }, + { 65124, 65126 }, + { 65291, 65291 }, + { 65308, 65310 }, + { 65372, 65372 }, + { 65374, 65374 }, + { 65506, 65506 }, + { 65513, 65516 }, +}; +static const URange32 Sm_range32[] = { + { 120513, 120513 }, + { 120539, 120539 }, + { 120571, 120571 }, + { 120597, 120597 }, + { 120629, 120629 }, + { 120655, 120655 }, + { 120687, 120687 }, + { 120713, 120713 }, + { 120745, 120745 }, + { 120771, 120771 }, + { 126704, 126705 }, +}; +static const URange16 So_range16[] = { + { 166, 166 }, + { 169, 169 }, + { 174, 174 }, + { 176, 176 }, + { 1154, 1154 }, + { 1421, 1422 }, + { 1550, 1551 }, + { 1758, 1758 }, + { 1769, 1769 }, + { 1789, 1790 }, + { 2038, 2038 }, + { 2554, 2554 }, + { 2928, 2928 }, + { 3059, 3064 }, + { 3066, 3066 }, + { 3199, 3199 }, + { 3407, 3407 }, + { 3449, 3449 }, + { 3841, 3843 }, + { 3859, 3859 }, + { 3861, 3863 }, + { 3866, 3871 }, + { 3892, 3892 }, + { 3894, 3894 }, + { 3896, 3896 }, + { 4030, 4037 }, + { 4039, 4044 }, + { 4046, 4047 }, + { 4053, 4056 }, + { 4254, 4255 }, + { 5008, 5017 }, + { 5741, 5741 }, + { 6464, 6464 }, + { 6622, 6655 }, + { 7009, 7018 }, + { 7028, 7036 }, + { 8448, 8449 }, + { 8451, 8454 }, + { 8456, 8457 }, + { 8468, 8468 }, + { 8470, 8471 }, + { 8478, 8483 }, + { 8485, 8485 }, + { 8487, 8487 }, + { 8489, 8489 }, + { 8494, 8494 }, + { 8506, 8507 }, + { 8522, 8522 }, + { 8524, 8525 }, + { 8527, 8527 }, + { 8586, 8587 }, + { 8597, 8601 }, + { 8604, 8607 }, + { 8609, 8610 }, + { 8612, 8613 }, + { 8615, 8621 }, + { 8623, 8653 }, + { 8656, 8657 }, + { 8659, 8659 }, + { 8661, 8691 }, + { 8960, 8967 }, + { 8972, 8991 }, + { 8994, 9000 }, + { 9003, 9083 }, + { 9085, 9114 }, + { 9140, 9179 }, + { 9186, 9254 }, + { 9280, 9290 }, + { 9372, 9449 }, + { 9472, 9654 }, + { 9656, 9664 }, + { 9666, 9719 }, + { 9728, 9838 }, + { 9840, 10087 }, + { 10132, 10175 }, + { 10240, 10495 }, + { 11008, 11055 }, + { 11077, 11078 }, + { 11085, 11123 }, + { 11126, 11157 }, + { 11159, 11263 }, + { 11493, 11498 }, + { 11856, 11857 }, + { 11904, 11929 }, + { 11931, 12019 }, + { 12032, 12245 }, + { 12272, 12283 }, + { 12292, 12292 }, + { 12306, 12307 }, + { 12320, 12320 }, + { 12342, 12343 }, + { 12350, 12351 }, + { 12688, 12689 }, + { 12694, 12703 }, + { 12736, 12771 }, + { 12800, 12830 }, + { 12842, 12871 }, + { 12880, 12880 }, + { 12896, 12927 }, + { 12938, 12976 }, + { 12992, 13311 }, + { 19904, 19967 }, + { 42128, 42182 }, + { 43048, 43051 }, + { 43062, 43063 }, + { 43065, 43065 }, + { 43639, 43641 }, + { 65021, 65021 }, + { 65508, 65508 }, + { 65512, 65512 }, + { 65517, 65518 }, + { 65532, 65533 }, +}; +static const URange32 So_range32[] = { + { 65847, 65855 }, + { 65913, 65929 }, + { 65932, 65934 }, + { 65936, 65948 }, + { 65952, 65952 }, + { 66000, 66044 }, + { 67703, 67704 }, + { 68296, 68296 }, + { 71487, 71487 }, + { 73685, 73692 }, + { 73697, 73713 }, + { 92988, 92991 }, + { 92997, 92997 }, + { 113820, 113820 }, + { 118784, 119029 }, + { 119040, 119078 }, + { 119081, 119140 }, + { 119146, 119148 }, + { 119171, 119172 }, + { 119180, 119209 }, + { 119214, 119272 }, + { 119296, 119361 }, + { 119365, 119365 }, + { 119552, 119638 }, + { 120832, 121343 }, + { 121399, 121402 }, + { 121453, 121460 }, + { 121462, 121475 }, + { 121477, 121478 }, + { 123215, 123215 }, + { 126124, 126124 }, + { 126254, 126254 }, + { 126976, 127019 }, + { 127024, 127123 }, + { 127136, 127150 }, + { 127153, 127167 }, + { 127169, 127183 }, + { 127185, 127221 }, + { 127245, 127405 }, + { 127462, 127490 }, + { 127504, 127547 }, + { 127552, 127560 }, + { 127568, 127569 }, + { 127584, 127589 }, + { 127744, 127994 }, + { 128000, 128727 }, + { 128736, 128748 }, + { 128752, 128764 }, + { 128768, 128883 }, + { 128896, 128984 }, + { 128992, 129003 }, + { 129024, 129035 }, + { 129040, 129095 }, + { 129104, 129113 }, + { 129120, 129159 }, + { 129168, 129197 }, + { 129200, 129201 }, + { 129280, 129400 }, + { 129402, 129483 }, + { 129485, 129619 }, + { 129632, 129645 }, + { 129648, 129652 }, + { 129656, 129658 }, + { 129664, 129670 }, + { 129680, 129704 }, + { 129712, 129718 }, + { 129728, 129730 }, + { 129744, 129750 }, + { 129792, 129938 }, + { 129940, 129994 }, +}; +static const URange16 Z_range16[] = { + { 32, 32 }, + { 160, 160 }, + { 5760, 5760 }, + { 8192, 8202 }, + { 8232, 8233 }, + { 8239, 8239 }, + { 8287, 8287 }, + { 12288, 12288 }, +}; +static const URange16 Zl_range16[] = { + { 8232, 8232 }, +}; +static const URange16 Zp_range16[] = { + { 8233, 8233 }, +}; +static const URange16 Zs_range16[] = { + { 32, 32 }, + { 160, 160 }, + { 5760, 5760 }, + { 8192, 8202 }, + { 8239, 8239 }, + { 8287, 8287 }, + { 12288, 12288 }, +}; +static const URange32 Adlam_range32[] = { + { 125184, 125259 }, + { 125264, 125273 }, + { 125278, 125279 }, +}; +static const URange32 Ahom_range32[] = { + { 71424, 71450 }, + { 71453, 71467 }, + { 71472, 71487 }, +}; +static const URange32 Anatolian_Hieroglyphs_range32[] = { + { 82944, 83526 }, +}; +static const URange16 Arabic_range16[] = { + { 1536, 1540 }, + { 1542, 1547 }, + { 1549, 1562 }, + { 1564, 1564 }, + { 1566, 1566 }, + { 1568, 1599 }, + { 1601, 1610 }, + { 1622, 1647 }, + { 1649, 1756 }, + { 1758, 1791 }, + { 1872, 1919 }, + { 2208, 2228 }, + { 2230, 2247 }, + { 2259, 2273 }, + { 2275, 2303 }, + { 64336, 64449 }, + { 64467, 64829 }, + { 64848, 64911 }, + { 64914, 64967 }, + { 65008, 65021 }, + { 65136, 65140 }, + { 65142, 65276 }, +}; +static const URange32 Arabic_range32[] = { + { 69216, 69246 }, + { 126464, 126467 }, + { 126469, 126495 }, + { 126497, 126498 }, + { 126500, 126500 }, + { 126503, 126503 }, + { 126505, 126514 }, + { 126516, 126519 }, + { 126521, 126521 }, + { 126523, 126523 }, + { 126530, 126530 }, + { 126535, 126535 }, + { 126537, 126537 }, + { 126539, 126539 }, + { 126541, 126543 }, + { 126545, 126546 }, + { 126548, 126548 }, + { 126551, 126551 }, + { 126553, 126553 }, + { 126555, 126555 }, + { 126557, 126557 }, + { 126559, 126559 }, + { 126561, 126562 }, + { 126564, 126564 }, + { 126567, 126570 }, + { 126572, 126578 }, + { 126580, 126583 }, + { 126585, 126588 }, + { 126590, 126590 }, + { 126592, 126601 }, + { 126603, 126619 }, + { 126625, 126627 }, + { 126629, 126633 }, + { 126635, 126651 }, + { 126704, 126705 }, +}; +static const URange16 Armenian_range16[] = { + { 1329, 1366 }, + { 1369, 1418 }, + { 1421, 1423 }, + { 64275, 64279 }, +}; +static const URange32 Avestan_range32[] = { + { 68352, 68405 }, + { 68409, 68415 }, +}; +static const URange16 Balinese_range16[] = { + { 6912, 6987 }, + { 6992, 7036 }, +}; +static const URange16 Bamum_range16[] = { + { 42656, 42743 }, +}; +static const URange32 Bamum_range32[] = { + { 92160, 92728 }, +}; +static const URange32 Bassa_Vah_range32[] = { + { 92880, 92909 }, + { 92912, 92917 }, +}; +static const URange16 Batak_range16[] = { + { 7104, 7155 }, + { 7164, 7167 }, +}; +static const URange16 Bengali_range16[] = { + { 2432, 2435 }, + { 2437, 2444 }, + { 2447, 2448 }, + { 2451, 2472 }, + { 2474, 2480 }, + { 2482, 2482 }, + { 2486, 2489 }, + { 2492, 2500 }, + { 2503, 2504 }, + { 2507, 2510 }, + { 2519, 2519 }, + { 2524, 2525 }, + { 2527, 2531 }, + { 2534, 2558 }, +}; +static const URange32 Bhaiksuki_range32[] = { + { 72704, 72712 }, + { 72714, 72758 }, + { 72760, 72773 }, + { 72784, 72812 }, +}; +static const URange16 Bopomofo_range16[] = { + { 746, 747 }, + { 12549, 12591 }, + { 12704, 12735 }, +}; +static const URange32 Brahmi_range32[] = { + { 69632, 69709 }, + { 69714, 69743 }, + { 69759, 69759 }, +}; +static const URange16 Braille_range16[] = { + { 10240, 10495 }, +}; +static const URange16 Buginese_range16[] = { + { 6656, 6683 }, + { 6686, 6687 }, +}; +static const URange16 Buhid_range16[] = { + { 5952, 5971 }, +}; +static const URange16 Canadian_Aboriginal_range16[] = { + { 5120, 5759 }, + { 6320, 6389 }, +}; +static const URange32 Carian_range32[] = { + { 66208, 66256 }, +}; +static const URange32 Caucasian_Albanian_range32[] = { + { 66864, 66915 }, + { 66927, 66927 }, +}; +static const URange32 Chakma_range32[] = { + { 69888, 69940 }, + { 69942, 69959 }, +}; +static const URange16 Cham_range16[] = { + { 43520, 43574 }, + { 43584, 43597 }, + { 43600, 43609 }, + { 43612, 43615 }, +}; +static const URange16 Cherokee_range16[] = { + { 5024, 5109 }, + { 5112, 5117 }, + { 43888, 43967 }, +}; +static const URange32 Chorasmian_range32[] = { + { 69552, 69579 }, +}; +static const URange16 Common_range16[] = { + { 0, 64 }, + { 91, 96 }, + { 123, 169 }, + { 171, 185 }, + { 187, 191 }, + { 215, 215 }, + { 247, 247 }, + { 697, 735 }, + { 741, 745 }, + { 748, 767 }, + { 884, 884 }, + { 894, 894 }, + { 901, 901 }, + { 903, 903 }, + { 1541, 1541 }, + { 1548, 1548 }, + { 1563, 1563 }, + { 1567, 1567 }, + { 1600, 1600 }, + { 1757, 1757 }, + { 2274, 2274 }, + { 2404, 2405 }, + { 3647, 3647 }, + { 4053, 4056 }, + { 4347, 4347 }, + { 5867, 5869 }, + { 5941, 5942 }, + { 6146, 6147 }, + { 6149, 6149 }, + { 7379, 7379 }, + { 7393, 7393 }, + { 7401, 7404 }, + { 7406, 7411 }, + { 7413, 7415 }, + { 7418, 7418 }, + { 8192, 8203 }, + { 8206, 8292 }, + { 8294, 8304 }, + { 8308, 8318 }, + { 8320, 8334 }, + { 8352, 8383 }, + { 8448, 8485 }, + { 8487, 8489 }, + { 8492, 8497 }, + { 8499, 8525 }, + { 8527, 8543 }, + { 8585, 8587 }, + { 8592, 9254 }, + { 9280, 9290 }, + { 9312, 10239 }, + { 10496, 11123 }, + { 11126, 11157 }, + { 11159, 11263 }, + { 11776, 11858 }, + { 12272, 12283 }, + { 12288, 12292 }, + { 12294, 12294 }, + { 12296, 12320 }, + { 12336, 12343 }, + { 12348, 12351 }, + { 12443, 12444 }, + { 12448, 12448 }, + { 12539, 12540 }, + { 12688, 12703 }, + { 12736, 12771 }, + { 12832, 12895 }, + { 12927, 13007 }, + { 13055, 13055 }, + { 13144, 13311 }, + { 19904, 19967 }, + { 42752, 42785 }, + { 42888, 42890 }, + { 43056, 43065 }, + { 43310, 43310 }, + { 43471, 43471 }, + { 43867, 43867 }, + { 43882, 43883 }, + { 64830, 64831 }, + { 65040, 65049 }, + { 65072, 65106 }, + { 65108, 65126 }, + { 65128, 65131 }, + { 65279, 65279 }, + { 65281, 65312 }, + { 65339, 65344 }, + { 65371, 65381 }, + { 65392, 65392 }, + { 65438, 65439 }, + { 65504, 65510 }, + { 65512, 65518 }, + { 65529, 65533 }, +}; +static const URange32 Common_range32[] = { + { 65792, 65794 }, + { 65799, 65843 }, + { 65847, 65855 }, + { 65936, 65948 }, + { 66000, 66044 }, + { 66273, 66299 }, + { 94178, 94179 }, + { 113824, 113827 }, + { 118784, 119029 }, + { 119040, 119078 }, + { 119081, 119142 }, + { 119146, 119162 }, + { 119171, 119172 }, + { 119180, 119209 }, + { 119214, 119272 }, + { 119520, 119539 }, + { 119552, 119638 }, + { 119648, 119672 }, + { 119808, 119892 }, + { 119894, 119964 }, + { 119966, 119967 }, + { 119970, 119970 }, + { 119973, 119974 }, + { 119977, 119980 }, + { 119982, 119993 }, + { 119995, 119995 }, + { 119997, 120003 }, + { 120005, 120069 }, + { 120071, 120074 }, + { 120077, 120084 }, + { 120086, 120092 }, + { 120094, 120121 }, + { 120123, 120126 }, + { 120128, 120132 }, + { 120134, 120134 }, + { 120138, 120144 }, + { 120146, 120485 }, + { 120488, 120779 }, + { 120782, 120831 }, + { 126065, 126132 }, + { 126209, 126269 }, + { 126976, 127019 }, + { 127024, 127123 }, + { 127136, 127150 }, + { 127153, 127167 }, + { 127169, 127183 }, + { 127185, 127221 }, + { 127232, 127405 }, + { 127462, 127487 }, + { 127489, 127490 }, + { 127504, 127547 }, + { 127552, 127560 }, + { 127568, 127569 }, + { 127584, 127589 }, + { 127744, 128727 }, + { 128736, 128748 }, + { 128752, 128764 }, + { 128768, 128883 }, + { 128896, 128984 }, + { 128992, 129003 }, + { 129024, 129035 }, + { 129040, 129095 }, + { 129104, 129113 }, + { 129120, 129159 }, + { 129168, 129197 }, + { 129200, 129201 }, + { 129280, 129400 }, + { 129402, 129483 }, + { 129485, 129619 }, + { 129632, 129645 }, + { 129648, 129652 }, + { 129656, 129658 }, + { 129664, 129670 }, + { 129680, 129704 }, + { 129712, 129718 }, + { 129728, 129730 }, + { 129744, 129750 }, + { 129792, 129938 }, + { 129940, 129994 }, + { 130032, 130041 }, + { 917505, 917505 }, + { 917536, 917631 }, +}; +static const URange16 Coptic_range16[] = { + { 994, 1007 }, + { 11392, 11507 }, + { 11513, 11519 }, +}; +static const URange32 Cuneiform_range32[] = { + { 73728, 74649 }, + { 74752, 74862 }, + { 74864, 74868 }, + { 74880, 75075 }, +}; +static const URange32 Cypriot_range32[] = { + { 67584, 67589 }, + { 67592, 67592 }, + { 67594, 67637 }, + { 67639, 67640 }, + { 67644, 67644 }, + { 67647, 67647 }, +}; +static const URange16 Cyrillic_range16[] = { + { 1024, 1156 }, + { 1159, 1327 }, + { 7296, 7304 }, + { 7467, 7467 }, + { 7544, 7544 }, + { 11744, 11775 }, + { 42560, 42655 }, + { 65070, 65071 }, +}; +static const URange32 Deseret_range32[] = { + { 66560, 66639 }, +}; +static const URange16 Devanagari_range16[] = { + { 2304, 2384 }, + { 2389, 2403 }, + { 2406, 2431 }, + { 43232, 43263 }, +}; +static const URange32 Dives_Akuru_range32[] = { + { 71936, 71942 }, + { 71945, 71945 }, + { 71948, 71955 }, + { 71957, 71958 }, + { 71960, 71989 }, + { 71991, 71992 }, + { 71995, 72006 }, + { 72016, 72025 }, +}; +static const URange32 Dogra_range32[] = { + { 71680, 71739 }, +}; +static const URange32 Duployan_range32[] = { + { 113664, 113770 }, + { 113776, 113788 }, + { 113792, 113800 }, + { 113808, 113817 }, + { 113820, 113823 }, +}; +static const URange32 Egyptian_Hieroglyphs_range32[] = { + { 77824, 78894 }, + { 78896, 78904 }, +}; +static const URange32 Elbasan_range32[] = { + { 66816, 66855 }, +}; +static const URange32 Elymaic_range32[] = { + { 69600, 69622 }, +}; +static const URange16 Ethiopic_range16[] = { + { 4608, 4680 }, + { 4682, 4685 }, + { 4688, 4694 }, + { 4696, 4696 }, + { 4698, 4701 }, + { 4704, 4744 }, + { 4746, 4749 }, + { 4752, 4784 }, + { 4786, 4789 }, + { 4792, 4798 }, + { 4800, 4800 }, + { 4802, 4805 }, + { 4808, 4822 }, + { 4824, 4880 }, + { 4882, 4885 }, + { 4888, 4954 }, + { 4957, 4988 }, + { 4992, 5017 }, + { 11648, 11670 }, + { 11680, 11686 }, + { 11688, 11694 }, + { 11696, 11702 }, + { 11704, 11710 }, + { 11712, 11718 }, + { 11720, 11726 }, + { 11728, 11734 }, + { 11736, 11742 }, + { 43777, 43782 }, + { 43785, 43790 }, + { 43793, 43798 }, + { 43808, 43814 }, + { 43816, 43822 }, +}; +static const URange16 Georgian_range16[] = { + { 4256, 4293 }, + { 4295, 4295 }, + { 4301, 4301 }, + { 4304, 4346 }, + { 4348, 4351 }, + { 7312, 7354 }, + { 7357, 7359 }, + { 11520, 11557 }, + { 11559, 11559 }, + { 11565, 11565 }, +}; +static const URange16 Glagolitic_range16[] = { + { 11264, 11310 }, + { 11312, 11358 }, +}; +static const URange32 Glagolitic_range32[] = { + { 122880, 122886 }, + { 122888, 122904 }, + { 122907, 122913 }, + { 122915, 122916 }, + { 122918, 122922 }, +}; +static const URange32 Gothic_range32[] = { + { 66352, 66378 }, +}; +static const URange32 Grantha_range32[] = { + { 70400, 70403 }, + { 70405, 70412 }, + { 70415, 70416 }, + { 70419, 70440 }, + { 70442, 70448 }, + { 70450, 70451 }, + { 70453, 70457 }, + { 70460, 70468 }, + { 70471, 70472 }, + { 70475, 70477 }, + { 70480, 70480 }, + { 70487, 70487 }, + { 70493, 70499 }, + { 70502, 70508 }, + { 70512, 70516 }, +}; +static const URange16 Greek_range16[] = { + { 880, 883 }, + { 885, 887 }, + { 890, 893 }, + { 895, 895 }, + { 900, 900 }, + { 902, 902 }, + { 904, 906 }, + { 908, 908 }, + { 910, 929 }, + { 931, 993 }, + { 1008, 1023 }, + { 7462, 7466 }, + { 7517, 7521 }, + { 7526, 7530 }, + { 7615, 7615 }, + { 7936, 7957 }, + { 7960, 7965 }, + { 7968, 8005 }, + { 8008, 8013 }, + { 8016, 8023 }, + { 8025, 8025 }, + { 8027, 8027 }, + { 8029, 8029 }, + { 8031, 8061 }, + { 8064, 8116 }, + { 8118, 8132 }, + { 8134, 8147 }, + { 8150, 8155 }, + { 8157, 8175 }, + { 8178, 8180 }, + { 8182, 8190 }, + { 8486, 8486 }, + { 43877, 43877 }, +}; +static const URange32 Greek_range32[] = { + { 65856, 65934 }, + { 65952, 65952 }, + { 119296, 119365 }, +}; +static const URange16 Gujarati_range16[] = { + { 2689, 2691 }, + { 2693, 2701 }, + { 2703, 2705 }, + { 2707, 2728 }, + { 2730, 2736 }, + { 2738, 2739 }, + { 2741, 2745 }, + { 2748, 2757 }, + { 2759, 2761 }, + { 2763, 2765 }, + { 2768, 2768 }, + { 2784, 2787 }, + { 2790, 2801 }, + { 2809, 2815 }, +}; +static const URange32 Gunjala_Gondi_range32[] = { + { 73056, 73061 }, + { 73063, 73064 }, + { 73066, 73102 }, + { 73104, 73105 }, + { 73107, 73112 }, + { 73120, 73129 }, +}; +static const URange16 Gurmukhi_range16[] = { + { 2561, 2563 }, + { 2565, 2570 }, + { 2575, 2576 }, + { 2579, 2600 }, + { 2602, 2608 }, + { 2610, 2611 }, + { 2613, 2614 }, + { 2616, 2617 }, + { 2620, 2620 }, + { 2622, 2626 }, + { 2631, 2632 }, + { 2635, 2637 }, + { 2641, 2641 }, + { 2649, 2652 }, + { 2654, 2654 }, + { 2662, 2678 }, +}; +static const URange16 Han_range16[] = { + { 11904, 11929 }, + { 11931, 12019 }, + { 12032, 12245 }, + { 12293, 12293 }, + { 12295, 12295 }, + { 12321, 12329 }, + { 12344, 12347 }, + { 13312, 19903 }, + { 19968, 40956 }, + { 63744, 64109 }, + { 64112, 64217 }, +}; +static const URange32 Han_range32[] = { + { 94192, 94193 }, + { 131072, 173789 }, + { 173824, 177972 }, + { 177984, 178205 }, + { 178208, 183969 }, + { 183984, 191456 }, + { 194560, 195101 }, + { 196608, 201546 }, +}; +static const URange16 Hangul_range16[] = { + { 4352, 4607 }, + { 12334, 12335 }, + { 12593, 12686 }, + { 12800, 12830 }, + { 12896, 12926 }, + { 43360, 43388 }, + { 44032, 55203 }, + { 55216, 55238 }, + { 55243, 55291 }, + { 65440, 65470 }, + { 65474, 65479 }, + { 65482, 65487 }, + { 65490, 65495 }, + { 65498, 65500 }, +}; +static const URange32 Hanifi_Rohingya_range32[] = { + { 68864, 68903 }, + { 68912, 68921 }, +}; +static const URange16 Hanunoo_range16[] = { + { 5920, 5940 }, +}; +static const URange32 Hatran_range32[] = { + { 67808, 67826 }, + { 67828, 67829 }, + { 67835, 67839 }, +}; +static const URange16 Hebrew_range16[] = { + { 1425, 1479 }, + { 1488, 1514 }, + { 1519, 1524 }, + { 64285, 64310 }, + { 64312, 64316 }, + { 64318, 64318 }, + { 64320, 64321 }, + { 64323, 64324 }, + { 64326, 64335 }, +}; +static const URange16 Hiragana_range16[] = { + { 12353, 12438 }, + { 12445, 12447 }, +}; +static const URange32 Hiragana_range32[] = { + { 110593, 110878 }, + { 110928, 110930 }, + { 127488, 127488 }, +}; +static const URange32 Imperial_Aramaic_range32[] = { + { 67648, 67669 }, + { 67671, 67679 }, +}; +static const URange16 Inherited_range16[] = { + { 768, 879 }, + { 1157, 1158 }, + { 1611, 1621 }, + { 1648, 1648 }, + { 2385, 2388 }, + { 6832, 6848 }, + { 7376, 7378 }, + { 7380, 7392 }, + { 7394, 7400 }, + { 7405, 7405 }, + { 7412, 7412 }, + { 7416, 7417 }, + { 7616, 7673 }, + { 7675, 7679 }, + { 8204, 8205 }, + { 8400, 8432 }, + { 12330, 12333 }, + { 12441, 12442 }, + { 65024, 65039 }, + { 65056, 65069 }, +}; +static const URange32 Inherited_range32[] = { + { 66045, 66045 }, + { 66272, 66272 }, + { 70459, 70459 }, + { 119143, 119145 }, + { 119163, 119170 }, + { 119173, 119179 }, + { 119210, 119213 }, + { 917760, 917999 }, +}; +static const URange32 Inscriptional_Pahlavi_range32[] = { + { 68448, 68466 }, + { 68472, 68479 }, +}; +static const URange32 Inscriptional_Parthian_range32[] = { + { 68416, 68437 }, + { 68440, 68447 }, +}; +static const URange16 Javanese_range16[] = { + { 43392, 43469 }, + { 43472, 43481 }, + { 43486, 43487 }, +}; +static const URange32 Kaithi_range32[] = { + { 69760, 69825 }, + { 69837, 69837 }, +}; +static const URange16 Kannada_range16[] = { + { 3200, 3212 }, + { 3214, 3216 }, + { 3218, 3240 }, + { 3242, 3251 }, + { 3253, 3257 }, + { 3260, 3268 }, + { 3270, 3272 }, + { 3274, 3277 }, + { 3285, 3286 }, + { 3294, 3294 }, + { 3296, 3299 }, + { 3302, 3311 }, + { 3313, 3314 }, +}; +static const URange16 Katakana_range16[] = { + { 12449, 12538 }, + { 12541, 12543 }, + { 12784, 12799 }, + { 13008, 13054 }, + { 13056, 13143 }, + { 65382, 65391 }, + { 65393, 65437 }, +}; +static const URange32 Katakana_range32[] = { + { 110592, 110592 }, + { 110948, 110951 }, +}; +static const URange16 Kayah_Li_range16[] = { + { 43264, 43309 }, + { 43311, 43311 }, +}; +static const URange32 Kharoshthi_range32[] = { + { 68096, 68099 }, + { 68101, 68102 }, + { 68108, 68115 }, + { 68117, 68119 }, + { 68121, 68149 }, + { 68152, 68154 }, + { 68159, 68168 }, + { 68176, 68184 }, +}; +static const URange32 Khitan_Small_Script_range32[] = { + { 94180, 94180 }, + { 101120, 101589 }, +}; +static const URange16 Khmer_range16[] = { + { 6016, 6109 }, + { 6112, 6121 }, + { 6128, 6137 }, + { 6624, 6655 }, +}; +static const URange32 Khojki_range32[] = { + { 70144, 70161 }, + { 70163, 70206 }, +}; +static const URange32 Khudawadi_range32[] = { + { 70320, 70378 }, + { 70384, 70393 }, +}; +static const URange16 Lao_range16[] = { + { 3713, 3714 }, + { 3716, 3716 }, + { 3718, 3722 }, + { 3724, 3747 }, + { 3749, 3749 }, + { 3751, 3773 }, + { 3776, 3780 }, + { 3782, 3782 }, + { 3784, 3789 }, + { 3792, 3801 }, + { 3804, 3807 }, +}; +static const URange16 Latin_range16[] = { + { 65, 90 }, + { 97, 122 }, + { 170, 170 }, + { 186, 186 }, + { 192, 214 }, + { 216, 246 }, + { 248, 696 }, + { 736, 740 }, + { 7424, 7461 }, + { 7468, 7516 }, + { 7522, 7525 }, + { 7531, 7543 }, + { 7545, 7614 }, + { 7680, 7935 }, + { 8305, 8305 }, + { 8319, 8319 }, + { 8336, 8348 }, + { 8490, 8491 }, + { 8498, 8498 }, + { 8526, 8526 }, + { 8544, 8584 }, + { 11360, 11391 }, + { 42786, 42887 }, + { 42891, 42943 }, + { 42946, 42954 }, + { 42997, 43007 }, + { 43824, 43866 }, + { 43868, 43876 }, + { 43878, 43881 }, + { 64256, 64262 }, + { 65313, 65338 }, + { 65345, 65370 }, +}; +static const URange16 Lepcha_range16[] = { + { 7168, 7223 }, + { 7227, 7241 }, + { 7245, 7247 }, +}; +static const URange16 Limbu_range16[] = { + { 6400, 6430 }, + { 6432, 6443 }, + { 6448, 6459 }, + { 6464, 6464 }, + { 6468, 6479 }, +}; +static const URange32 Linear_A_range32[] = { + { 67072, 67382 }, + { 67392, 67413 }, + { 67424, 67431 }, +}; +static const URange32 Linear_B_range32[] = { + { 65536, 65547 }, + { 65549, 65574 }, + { 65576, 65594 }, + { 65596, 65597 }, + { 65599, 65613 }, + { 65616, 65629 }, + { 65664, 65786 }, +}; +static const URange16 Lisu_range16[] = { + { 42192, 42239 }, +}; +static const URange32 Lisu_range32[] = { + { 73648, 73648 }, +}; +static const URange32 Lycian_range32[] = { + { 66176, 66204 }, +}; +static const URange32 Lydian_range32[] = { + { 67872, 67897 }, + { 67903, 67903 }, +}; +static const URange32 Mahajani_range32[] = { + { 69968, 70006 }, +}; +static const URange32 Makasar_range32[] = { + { 73440, 73464 }, +}; +static const URange16 Malayalam_range16[] = { + { 3328, 3340 }, + { 3342, 3344 }, + { 3346, 3396 }, + { 3398, 3400 }, + { 3402, 3407 }, + { 3412, 3427 }, + { 3430, 3455 }, +}; +static const URange16 Mandaic_range16[] = { + { 2112, 2139 }, + { 2142, 2142 }, +}; +static const URange32 Manichaean_range32[] = { + { 68288, 68326 }, + { 68331, 68342 }, +}; +static const URange32 Marchen_range32[] = { + { 72816, 72847 }, + { 72850, 72871 }, + { 72873, 72886 }, +}; +static const URange32 Masaram_Gondi_range32[] = { + { 72960, 72966 }, + { 72968, 72969 }, + { 72971, 73014 }, + { 73018, 73018 }, + { 73020, 73021 }, + { 73023, 73031 }, + { 73040, 73049 }, +}; +static const URange32 Medefaidrin_range32[] = { + { 93760, 93850 }, +}; +static const URange16 Meetei_Mayek_range16[] = { + { 43744, 43766 }, + { 43968, 44013 }, + { 44016, 44025 }, +}; +static const URange32 Mende_Kikakui_range32[] = { + { 124928, 125124 }, + { 125127, 125142 }, +}; +static const URange32 Meroitic_Cursive_range32[] = { + { 68000, 68023 }, + { 68028, 68047 }, + { 68050, 68095 }, +}; +static const URange32 Meroitic_Hieroglyphs_range32[] = { + { 67968, 67999 }, +}; +static const URange32 Miao_range32[] = { + { 93952, 94026 }, + { 94031, 94087 }, + { 94095, 94111 }, +}; +static const URange32 Modi_range32[] = { + { 71168, 71236 }, + { 71248, 71257 }, +}; +static const URange16 Mongolian_range16[] = { + { 6144, 6145 }, + { 6148, 6148 }, + { 6150, 6158 }, + { 6160, 6169 }, + { 6176, 6264 }, + { 6272, 6314 }, +}; +static const URange32 Mongolian_range32[] = { + { 71264, 71276 }, +}; +static const URange32 Mro_range32[] = { + { 92736, 92766 }, + { 92768, 92777 }, + { 92782, 92783 }, +}; +static const URange32 Multani_range32[] = { + { 70272, 70278 }, + { 70280, 70280 }, + { 70282, 70285 }, + { 70287, 70301 }, + { 70303, 70313 }, +}; +static const URange16 Myanmar_range16[] = { + { 4096, 4255 }, + { 43488, 43518 }, + { 43616, 43647 }, +}; +static const URange32 Nabataean_range32[] = { + { 67712, 67742 }, + { 67751, 67759 }, +}; +static const URange32 Nandinagari_range32[] = { + { 72096, 72103 }, + { 72106, 72151 }, + { 72154, 72164 }, +}; +static const URange16 New_Tai_Lue_range16[] = { + { 6528, 6571 }, + { 6576, 6601 }, + { 6608, 6618 }, + { 6622, 6623 }, +}; +static const URange32 Newa_range32[] = { + { 70656, 70747 }, + { 70749, 70753 }, +}; +static const URange16 Nko_range16[] = { + { 1984, 2042 }, + { 2045, 2047 }, +}; +static const URange32 Nushu_range32[] = { + { 94177, 94177 }, + { 110960, 111355 }, +}; +static const URange32 Nyiakeng_Puachue_Hmong_range32[] = { + { 123136, 123180 }, + { 123184, 123197 }, + { 123200, 123209 }, + { 123214, 123215 }, +}; +static const URange16 Ogham_range16[] = { + { 5760, 5788 }, +}; +static const URange16 Ol_Chiki_range16[] = { + { 7248, 7295 }, +}; +static const URange32 Old_Hungarian_range32[] = { + { 68736, 68786 }, + { 68800, 68850 }, + { 68858, 68863 }, +}; +static const URange32 Old_Italic_range32[] = { + { 66304, 66339 }, + { 66349, 66351 }, +}; +static const URange32 Old_North_Arabian_range32[] = { + { 68224, 68255 }, +}; +static const URange32 Old_Permic_range32[] = { + { 66384, 66426 }, +}; +static const URange32 Old_Persian_range32[] = { + { 66464, 66499 }, + { 66504, 66517 }, +}; +static const URange32 Old_Sogdian_range32[] = { + { 69376, 69415 }, +}; +static const URange32 Old_South_Arabian_range32[] = { + { 68192, 68223 }, +}; +static const URange32 Old_Turkic_range32[] = { + { 68608, 68680 }, +}; +static const URange16 Oriya_range16[] = { + { 2817, 2819 }, + { 2821, 2828 }, + { 2831, 2832 }, + { 2835, 2856 }, + { 2858, 2864 }, + { 2866, 2867 }, + { 2869, 2873 }, + { 2876, 2884 }, + { 2887, 2888 }, + { 2891, 2893 }, + { 2901, 2903 }, + { 2908, 2909 }, + { 2911, 2915 }, + { 2918, 2935 }, +}; +static const URange32 Osage_range32[] = { + { 66736, 66771 }, + { 66776, 66811 }, +}; +static const URange32 Osmanya_range32[] = { + { 66688, 66717 }, + { 66720, 66729 }, +}; +static const URange32 Pahawh_Hmong_range32[] = { + { 92928, 92997 }, + { 93008, 93017 }, + { 93019, 93025 }, + { 93027, 93047 }, + { 93053, 93071 }, +}; +static const URange32 Palmyrene_range32[] = { + { 67680, 67711 }, +}; +static const URange32 Pau_Cin_Hau_range32[] = { + { 72384, 72440 }, +}; +static const URange16 Phags_Pa_range16[] = { + { 43072, 43127 }, +}; +static const URange32 Phoenician_range32[] = { + { 67840, 67867 }, + { 67871, 67871 }, +}; +static const URange32 Psalter_Pahlavi_range32[] = { + { 68480, 68497 }, + { 68505, 68508 }, + { 68521, 68527 }, +}; +static const URange16 Rejang_range16[] = { + { 43312, 43347 }, + { 43359, 43359 }, +}; +static const URange16 Runic_range16[] = { + { 5792, 5866 }, + { 5870, 5880 }, +}; +static const URange16 Samaritan_range16[] = { + { 2048, 2093 }, + { 2096, 2110 }, +}; +static const URange16 Saurashtra_range16[] = { + { 43136, 43205 }, + { 43214, 43225 }, +}; +static const URange32 Sharada_range32[] = { + { 70016, 70111 }, +}; +static const URange32 Shavian_range32[] = { + { 66640, 66687 }, +}; +static const URange32 Siddham_range32[] = { + { 71040, 71093 }, + { 71096, 71133 }, +}; +static const URange32 SignWriting_range32[] = { + { 120832, 121483 }, + { 121499, 121503 }, + { 121505, 121519 }, +}; +static const URange16 Sinhala_range16[] = { + { 3457, 3459 }, + { 3461, 3478 }, + { 3482, 3505 }, + { 3507, 3515 }, + { 3517, 3517 }, + { 3520, 3526 }, + { 3530, 3530 }, + { 3535, 3540 }, + { 3542, 3542 }, + { 3544, 3551 }, + { 3558, 3567 }, + { 3570, 3572 }, +}; +static const URange32 Sinhala_range32[] = { + { 70113, 70132 }, +}; +static const URange32 Sogdian_range32[] = { + { 69424, 69465 }, +}; +static const URange32 Sora_Sompeng_range32[] = { + { 69840, 69864 }, + { 69872, 69881 }, +}; +static const URange32 Soyombo_range32[] = { + { 72272, 72354 }, +}; +static const URange16 Sundanese_range16[] = { + { 7040, 7103 }, + { 7360, 7367 }, +}; +static const URange16 Syloti_Nagri_range16[] = { + { 43008, 43052 }, +}; +static const URange16 Syriac_range16[] = { + { 1792, 1805 }, + { 1807, 1866 }, + { 1869, 1871 }, + { 2144, 2154 }, +}; +static const URange16 Tagalog_range16[] = { + { 5888, 5900 }, + { 5902, 5908 }, +}; +static const URange16 Tagbanwa_range16[] = { + { 5984, 5996 }, + { 5998, 6000 }, + { 6002, 6003 }, +}; +static const URange16 Tai_Le_range16[] = { + { 6480, 6509 }, + { 6512, 6516 }, +}; +static const URange16 Tai_Tham_range16[] = { + { 6688, 6750 }, + { 6752, 6780 }, + { 6783, 6793 }, + { 6800, 6809 }, + { 6816, 6829 }, +}; +static const URange16 Tai_Viet_range16[] = { + { 43648, 43714 }, + { 43739, 43743 }, +}; +static const URange32 Takri_range32[] = { + { 71296, 71352 }, + { 71360, 71369 }, +}; +static const URange16 Tamil_range16[] = { + { 2946, 2947 }, + { 2949, 2954 }, + { 2958, 2960 }, + { 2962, 2965 }, + { 2969, 2970 }, + { 2972, 2972 }, + { 2974, 2975 }, + { 2979, 2980 }, + { 2984, 2986 }, + { 2990, 3001 }, + { 3006, 3010 }, + { 3014, 3016 }, + { 3018, 3021 }, + { 3024, 3024 }, + { 3031, 3031 }, + { 3046, 3066 }, +}; +static const URange32 Tamil_range32[] = { + { 73664, 73713 }, + { 73727, 73727 }, +}; +static const URange32 Tangut_range32[] = { + { 94176, 94176 }, + { 94208, 100343 }, + { 100352, 101119 }, + { 101632, 101640 }, +}; +static const URange16 Telugu_range16[] = { + { 3072, 3084 }, + { 3086, 3088 }, + { 3090, 3112 }, + { 3114, 3129 }, + { 3133, 3140 }, + { 3142, 3144 }, + { 3146, 3149 }, + { 3157, 3158 }, + { 3160, 3162 }, + { 3168, 3171 }, + { 3174, 3183 }, + { 3191, 3199 }, +}; +static const URange16 Thaana_range16[] = { + { 1920, 1969 }, +}; +static const URange16 Thai_range16[] = { + { 3585, 3642 }, + { 3648, 3675 }, +}; +static const URange16 Tibetan_range16[] = { + { 3840, 3911 }, + { 3913, 3948 }, + { 3953, 3991 }, + { 3993, 4028 }, + { 4030, 4044 }, + { 4046, 4052 }, + { 4057, 4058 }, +}; +static const URange16 Tifinagh_range16[] = { + { 11568, 11623 }, + { 11631, 11632 }, + { 11647, 11647 }, +}; +static const URange32 Tirhuta_range32[] = { + { 70784, 70855 }, + { 70864, 70873 }, +}; +static const URange32 Ugaritic_range32[] = { + { 66432, 66461 }, + { 66463, 66463 }, +}; +static const URange16 Vai_range16[] = { + { 42240, 42539 }, +}; +static const URange32 Wancho_range32[] = { + { 123584, 123641 }, + { 123647, 123647 }, +}; +static const URange32 Warang_Citi_range32[] = { + { 71840, 71922 }, + { 71935, 71935 }, +}; +static const URange32 Yezidi_range32[] = { + { 69248, 69289 }, + { 69291, 69293 }, + { 69296, 69297 }, +}; +static const URange16 Yi_range16[] = { + { 40960, 42124 }, + { 42128, 42182 }, +}; +static const URange32 Zanabazar_Square_range32[] = { + { 72192, 72263 }, +}; +// 4001 16-bit ranges, 1602 32-bit ranges +const UGroup unicode_groups[] = { + { "Adlam", +1, 0, 0, Adlam_range32, 3 }, + { "Ahom", +1, 0, 0, Ahom_range32, 3 }, + { "Anatolian_Hieroglyphs", +1, 0, 0, Anatolian_Hieroglyphs_range32, 1 }, + { "Arabic", +1, Arabic_range16, 22, Arabic_range32, 35 }, + { "Armenian", +1, Armenian_range16, 4, 0, 0 }, + { "Avestan", +1, 0, 0, Avestan_range32, 2 }, + { "Balinese", +1, Balinese_range16, 2, 0, 0 }, + { "Bamum", +1, Bamum_range16, 1, Bamum_range32, 1 }, + { "Bassa_Vah", +1, 0, 0, Bassa_Vah_range32, 2 }, + { "Batak", +1, Batak_range16, 2, 0, 0 }, + { "Bengali", +1, Bengali_range16, 14, 0, 0 }, + { "Bhaiksuki", +1, 0, 0, Bhaiksuki_range32, 4 }, + { "Bopomofo", +1, Bopomofo_range16, 3, 0, 0 }, + { "Brahmi", +1, 0, 0, Brahmi_range32, 3 }, + { "Braille", +1, Braille_range16, 1, 0, 0 }, + { "Buginese", +1, Buginese_range16, 2, 0, 0 }, + { "Buhid", +1, Buhid_range16, 1, 0, 0 }, + { "C", +1, C_range16, 16, C_range32, 9 }, + { "Canadian_Aboriginal", +1, Canadian_Aboriginal_range16, 2, 0, 0 }, + { "Carian", +1, 0, 0, Carian_range32, 1 }, + { "Caucasian_Albanian", +1, 0, 0, Caucasian_Albanian_range32, 2 }, + { "Cc", +1, Cc_range16, 2, 0, 0 }, + { "Cf", +1, Cf_range16, 13, Cf_range32, 7 }, + { "Chakma", +1, 0, 0, Chakma_range32, 2 }, + { "Cham", +1, Cham_range16, 4, 0, 0 }, + { "Cherokee", +1, Cherokee_range16, 3, 0, 0 }, + { "Chorasmian", +1, 0, 0, Chorasmian_range32, 1 }, + { "Co", +1, Co_range16, 1, Co_range32, 2 }, + { "Common", +1, Common_range16, 91, Common_range32, 82 }, + { "Coptic", +1, Coptic_range16, 3, 0, 0 }, + { "Cs", +1, Cs_range16, 1, 0, 0 }, + { "Cuneiform", +1, 0, 0, Cuneiform_range32, 4 }, + { "Cypriot", +1, 0, 0, Cypriot_range32, 6 }, + { "Cyrillic", +1, Cyrillic_range16, 8, 0, 0 }, + { "Deseret", +1, 0, 0, Deseret_range32, 1 }, + { "Devanagari", +1, Devanagari_range16, 4, 0, 0 }, + { "Dives_Akuru", +1, 0, 0, Dives_Akuru_range32, 8 }, + { "Dogra", +1, 0, 0, Dogra_range32, 1 }, + { "Duployan", +1, 0, 0, Duployan_range32, 5 }, + { "Egyptian_Hieroglyphs", +1, 0, 0, Egyptian_Hieroglyphs_range32, 2 }, + { "Elbasan", +1, 0, 0, Elbasan_range32, 1 }, + { "Elymaic", +1, 0, 0, Elymaic_range32, 1 }, + { "Ethiopic", +1, Ethiopic_range16, 32, 0, 0 }, + { "Georgian", +1, Georgian_range16, 10, 0, 0 }, + { "Glagolitic", +1, Glagolitic_range16, 2, Glagolitic_range32, 5 }, + { "Gothic", +1, 0, 0, Gothic_range32, 1 }, + { "Grantha", +1, 0, 0, Grantha_range32, 15 }, + { "Greek", +1, Greek_range16, 33, Greek_range32, 3 }, + { "Gujarati", +1, Gujarati_range16, 14, 0, 0 }, + { "Gunjala_Gondi", +1, 0, 0, Gunjala_Gondi_range32, 6 }, + { "Gurmukhi", +1, Gurmukhi_range16, 16, 0, 0 }, + { "Han", +1, Han_range16, 11, Han_range32, 8 }, + { "Hangul", +1, Hangul_range16, 14, 0, 0 }, + { "Hanifi_Rohingya", +1, 0, 0, Hanifi_Rohingya_range32, 2 }, + { "Hanunoo", +1, Hanunoo_range16, 1, 0, 0 }, + { "Hatran", +1, 0, 0, Hatran_range32, 3 }, + { "Hebrew", +1, Hebrew_range16, 9, 0, 0 }, + { "Hiragana", +1, Hiragana_range16, 2, Hiragana_range32, 3 }, + { "Imperial_Aramaic", +1, 0, 0, Imperial_Aramaic_range32, 2 }, + { "Inherited", +1, Inherited_range16, 20, Inherited_range32, 8 }, + { "Inscriptional_Pahlavi", +1, 0, 0, Inscriptional_Pahlavi_range32, 2 }, + { "Inscriptional_Parthian", +1, 0, 0, Inscriptional_Parthian_range32, 2 }, + { "Javanese", +1, Javanese_range16, 3, 0, 0 }, + { "Kaithi", +1, 0, 0, Kaithi_range32, 2 }, + { "Kannada", +1, Kannada_range16, 13, 0, 0 }, + { "Katakana", +1, Katakana_range16, 7, Katakana_range32, 2 }, + { "Kayah_Li", +1, Kayah_Li_range16, 2, 0, 0 }, + { "Kharoshthi", +1, 0, 0, Kharoshthi_range32, 8 }, + { "Khitan_Small_Script", +1, 0, 0, Khitan_Small_Script_range32, 2 }, + { "Khmer", +1, Khmer_range16, 4, 0, 0 }, + { "Khojki", +1, 0, 0, Khojki_range32, 2 }, + { "Khudawadi", +1, 0, 0, Khudawadi_range32, 2 }, + { "L", +1, L_range16, 380, L_range32, 242 }, + { "Lao", +1, Lao_range16, 11, 0, 0 }, + { "Latin", +1, Latin_range16, 32, 0, 0 }, + { "Lepcha", +1, Lepcha_range16, 3, 0, 0 }, + { "Limbu", +1, Limbu_range16, 5, 0, 0 }, + { "Linear_A", +1, 0, 0, Linear_A_range32, 3 }, + { "Linear_B", +1, 0, 0, Linear_B_range32, 7 }, + { "Lisu", +1, Lisu_range16, 1, Lisu_range32, 1 }, + { "Ll", +1, Ll_range16, 611, Ll_range32, 34 }, + { "Lm", +1, Lm_range16, 55, Lm_range32, 6 }, + { "Lo", +1, Lo_range16, 290, Lo_range32, 199 }, + { "Lt", +1, Lt_range16, 10, 0, 0 }, + { "Lu", +1, Lu_range16, 601, Lu_range32, 37 }, + { "Lycian", +1, 0, 0, Lycian_range32, 1 }, + { "Lydian", +1, 0, 0, Lydian_range32, 2 }, + { "M", +1, M_range16, 187, M_range32, 103 }, + { "Mahajani", +1, 0, 0, Mahajani_range32, 1 }, + { "Makasar", +1, 0, 0, Makasar_range32, 1 }, + { "Malayalam", +1, Malayalam_range16, 7, 0, 0 }, + { "Mandaic", +1, Mandaic_range16, 2, 0, 0 }, + { "Manichaean", +1, 0, 0, Manichaean_range32, 2 }, + { "Marchen", +1, 0, 0, Marchen_range32, 3 }, + { "Masaram_Gondi", +1, 0, 0, Masaram_Gondi_range32, 7 }, + { "Mc", +1, Mc_range16, 109, Mc_range32, 66 }, + { "Me", +1, Me_range16, 5, 0, 0 }, + { "Medefaidrin", +1, 0, 0, Medefaidrin_range32, 1 }, + { "Meetei_Mayek", +1, Meetei_Mayek_range16, 3, 0, 0 }, + { "Mende_Kikakui", +1, 0, 0, Mende_Kikakui_range32, 2 }, + { "Meroitic_Cursive", +1, 0, 0, Meroitic_Cursive_range32, 3 }, + { "Meroitic_Hieroglyphs", +1, 0, 0, Meroitic_Hieroglyphs_range32, 1 }, + { "Miao", +1, 0, 0, Miao_range32, 3 }, + { "Mn", +1, Mn_range16, 210, Mn_range32, 117 }, + { "Modi", +1, 0, 0, Modi_range32, 2 }, + { "Mongolian", +1, Mongolian_range16, 6, Mongolian_range32, 1 }, + { "Mro", +1, 0, 0, Mro_range32, 3 }, + { "Multani", +1, 0, 0, Multani_range32, 5 }, + { "Myanmar", +1, Myanmar_range16, 3, 0, 0 }, + { "N", +1, N_range16, 67, N_range32, 66 }, + { "Nabataean", +1, 0, 0, Nabataean_range32, 2 }, + { "Nandinagari", +1, 0, 0, Nandinagari_range32, 3 }, + { "Nd", +1, Nd_range16, 37, Nd_range32, 24 }, + { "New_Tai_Lue", +1, New_Tai_Lue_range16, 4, 0, 0 }, + { "Newa", +1, 0, 0, Newa_range32, 2 }, + { "Nko", +1, Nko_range16, 2, 0, 0 }, + { "Nl", +1, Nl_range16, 7, Nl_range32, 5 }, + { "No", +1, No_range16, 29, No_range32, 42 }, + { "Nushu", +1, 0, 0, Nushu_range32, 2 }, + { "Nyiakeng_Puachue_Hmong", +1, 0, 0, Nyiakeng_Puachue_Hmong_range32, 4 }, + { "Ogham", +1, Ogham_range16, 1, 0, 0 }, + { "Ol_Chiki", +1, Ol_Chiki_range16, 1, 0, 0 }, + { "Old_Hungarian", +1, 0, 0, Old_Hungarian_range32, 3 }, + { "Old_Italic", +1, 0, 0, Old_Italic_range32, 2 }, + { "Old_North_Arabian", +1, 0, 0, Old_North_Arabian_range32, 1 }, + { "Old_Permic", +1, 0, 0, Old_Permic_range32, 1 }, + { "Old_Persian", +1, 0, 0, Old_Persian_range32, 2 }, + { "Old_Sogdian", +1, 0, 0, Old_Sogdian_range32, 1 }, + { "Old_South_Arabian", +1, 0, 0, Old_South_Arabian_range32, 1 }, + { "Old_Turkic", +1, 0, 0, Old_Turkic_range32, 1 }, + { "Oriya", +1, Oriya_range16, 14, 0, 0 }, + { "Osage", +1, 0, 0, Osage_range32, 2 }, + { "Osmanya", +1, 0, 0, Osmanya_range32, 2 }, + { "P", +1, P_range16, 132, P_range32, 53 }, + { "Pahawh_Hmong", +1, 0, 0, Pahawh_Hmong_range32, 5 }, + { "Palmyrene", +1, 0, 0, Palmyrene_range32, 1 }, + { "Pau_Cin_Hau", +1, 0, 0, Pau_Cin_Hau_range32, 1 }, + { "Pc", +1, Pc_range16, 6, 0, 0 }, + { "Pd", +1, Pd_range16, 17, Pd_range32, 1 }, + { "Pe", +1, Pe_range16, 72, 0, 0 }, + { "Pf", +1, Pf_range16, 10, 0, 0 }, + { "Phags_Pa", +1, Phags_Pa_range16, 1, 0, 0 }, + { "Phoenician", +1, 0, 0, Phoenician_range32, 2 }, + { "Pi", +1, Pi_range16, 11, 0, 0 }, + { "Po", +1, Po_range16, 129, Po_range32, 52 }, + { "Ps", +1, Ps_range16, 75, 0, 0 }, + { "Psalter_Pahlavi", +1, 0, 0, Psalter_Pahlavi_range32, 3 }, + { "Rejang", +1, Rejang_range16, 2, 0, 0 }, + { "Runic", +1, Runic_range16, 2, 0, 0 }, + { "S", +1, S_range16, 148, S_range32, 81 }, + { "Samaritan", +1, Samaritan_range16, 2, 0, 0 }, + { "Saurashtra", +1, Saurashtra_range16, 2, 0, 0 }, + { "Sc", +1, Sc_range16, 18, Sc_range32, 3 }, + { "Sharada", +1, 0, 0, Sharada_range32, 1 }, + { "Shavian", +1, 0, 0, Shavian_range32, 1 }, + { "Siddham", +1, 0, 0, Siddham_range32, 2 }, + { "SignWriting", +1, 0, 0, SignWriting_range32, 3 }, + { "Sinhala", +1, Sinhala_range16, 12, Sinhala_range32, 1 }, + { "Sk", +1, Sk_range16, 29, Sk_range32, 1 }, + { "Sm", +1, Sm_range16, 53, Sm_range32, 11 }, + { "So", +1, So_range16, 112, So_range32, 70 }, + { "Sogdian", +1, 0, 0, Sogdian_range32, 1 }, + { "Sora_Sompeng", +1, 0, 0, Sora_Sompeng_range32, 2 }, + { "Soyombo", +1, 0, 0, Soyombo_range32, 1 }, + { "Sundanese", +1, Sundanese_range16, 2, 0, 0 }, + { "Syloti_Nagri", +1, Syloti_Nagri_range16, 1, 0, 0 }, + { "Syriac", +1, Syriac_range16, 4, 0, 0 }, + { "Tagalog", +1, Tagalog_range16, 2, 0, 0 }, + { "Tagbanwa", +1, Tagbanwa_range16, 3, 0, 0 }, + { "Tai_Le", +1, Tai_Le_range16, 2, 0, 0 }, + { "Tai_Tham", +1, Tai_Tham_range16, 5, 0, 0 }, + { "Tai_Viet", +1, Tai_Viet_range16, 2, 0, 0 }, + { "Takri", +1, 0, 0, Takri_range32, 2 }, + { "Tamil", +1, Tamil_range16, 16, Tamil_range32, 2 }, + { "Tangut", +1, 0, 0, Tangut_range32, 4 }, + { "Telugu", +1, Telugu_range16, 12, 0, 0 }, + { "Thaana", +1, Thaana_range16, 1, 0, 0 }, + { "Thai", +1, Thai_range16, 2, 0, 0 }, + { "Tibetan", +1, Tibetan_range16, 7, 0, 0 }, + { "Tifinagh", +1, Tifinagh_range16, 3, 0, 0 }, + { "Tirhuta", +1, 0, 0, Tirhuta_range32, 2 }, + { "Ugaritic", +1, 0, 0, Ugaritic_range32, 2 }, + { "Vai", +1, Vai_range16, 1, 0, 0 }, + { "Wancho", +1, 0, 0, Wancho_range32, 2 }, + { "Warang_Citi", +1, 0, 0, Warang_Citi_range32, 2 }, + { "Yezidi", +1, 0, 0, Yezidi_range32, 3 }, + { "Yi", +1, Yi_range16, 2, 0, 0 }, + { "Z", +1, Z_range16, 8, 0, 0 }, + { "Zanabazar_Square", +1, 0, 0, Zanabazar_Square_range32, 1 }, + { "Zl", +1, Zl_range16, 1, 0, 0 }, + { "Zp", +1, Zp_range16, 1, 0, 0 }, + { "Zs", +1, Zs_range16, 7, 0, 0 }, +}; +const int num_unicode_groups = 192; + + +} // namespace re2 + + diff --git a/third_party/opa/wasm/src/re2/re2/unicode_groups.h b/third_party/opa/wasm/src/re2/re2/unicode_groups.h new file mode 100644 index 000000000000..75f55daa6198 --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/unicode_groups.h @@ -0,0 +1,67 @@ +// Copyright 2008 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef RE2_UNICODE_GROUPS_H_ +#define RE2_UNICODE_GROUPS_H_ + +// Unicode character groups. + +// The codes get split into ranges of 16-bit codes +// and ranges of 32-bit codes. It would be simpler +// to use only 32-bit ranges, but these tables are large +// enough to warrant extra care. +// +// Using just 32-bit ranges gives 27 kB of data. +// Adding 16-bit ranges gives 18 kB of data. +// Adding an extra table of 16-bit singletons would reduce +// to 16.5 kB of data but make the data harder to use; +// we don't bother. + +#include + +#include "util/util.h" +#include "util/utf.h" + +namespace re2 { + +struct URange16 +{ + uint16_t lo; + uint16_t hi; +}; + +struct URange32 +{ + Rune lo; + Rune hi; +}; + +struct UGroup +{ + const char *name; + int sign; // +1 for [abc], -1 for [^abc] + const URange16 *r16; + int nr16; + const URange32 *r32; + int nr32; +}; + +// Named by property or script name (e.g., "Nd", "N", "Han"). +// Negated groups are not included. +extern const UGroup unicode_groups[]; +extern const int num_unicode_groups; + +// Named by POSIX name (e.g., "[:alpha:]", "[:^lower:]"). +// Negated groups are included. +extern const UGroup posix_groups[]; +extern const int num_posix_groups; + +// Named by Perl name (e.g., "\\d", "\\D"). +// Negated groups are included. +extern const UGroup perl_groups[]; +extern const int num_perl_groups; + +} // namespace re2 + +#endif // RE2_UNICODE_GROUPS_H_ diff --git a/third_party/opa/wasm/src/re2/re2/walker-inl.h b/third_party/opa/wasm/src/re2/re2/walker-inl.h new file mode 100644 index 000000000000..8a37f87155cf --- /dev/null +++ b/third_party/opa/wasm/src/re2/re2/walker-inl.h @@ -0,0 +1,250 @@ +// Copyright 2006 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef RE2_WALKER_INL_H_ +#define RE2_WALKER_INL_H_ + +// Helper class for traversing Regexps without recursion. +// Clients should declare their own subclasses that override +// the PreVisit and PostVisit methods, which are called before +// and after visiting the subexpressions. + +// Not quite the Visitor pattern, because (among other things) +// the Visitor pattern is recursive. + +#include + +#include "util/logging.h" +#include "re2/regexp.h" + +namespace re2 { + +template struct WalkState; + +template class Regexp::Walker { + public: + Walker(); + virtual ~Walker(); + + // Virtual method called before visiting re's children. + // PreVisit passes ownership of its return value to its caller. + // The Arg* that PreVisit returns will be passed to PostVisit as pre_arg + // and passed to the child PreVisits and PostVisits as parent_arg. + // At the top-most Regexp, parent_arg is arg passed to walk. + // If PreVisit sets *stop to true, the walk does not recurse + // into the children. Instead it behaves as though the return + // value from PreVisit is the return value from PostVisit. + // The default PreVisit returns parent_arg. + virtual T PreVisit(Regexp* re, T parent_arg, bool* stop); + + // Virtual method called after visiting re's children. + // The pre_arg is the T that PreVisit returned. + // The child_args is a vector of the T that the child PostVisits returned. + // PostVisit takes ownership of pre_arg. + // PostVisit takes ownership of the Ts + // in *child_args, but not the vector itself. + // PostVisit passes ownership of its return value + // to its caller. + // The default PostVisit simply returns pre_arg. + virtual T PostVisit(Regexp* re, T parent_arg, T pre_arg, + T* child_args, int nchild_args); + + // Virtual method called to copy a T, + // when Walk notices that more than one child is the same re. + virtual T Copy(T arg); + + // Virtual method called to do a "quick visit" of the re, + // but not its children. Only called once the visit budget + // has been used up and we're trying to abort the walk + // as quickly as possible. Should return a value that + // makes sense for the parent PostVisits still to be run. + // This function is (hopefully) only called by + // WalkExponential, but must be implemented by all clients, + // just in case. + virtual T ShortVisit(Regexp* re, T parent_arg) = 0; + + // Walks over a regular expression. + // Top_arg is passed as parent_arg to PreVisit and PostVisit of re. + // Returns the T returned by PostVisit on re. + T Walk(Regexp* re, T top_arg); + + // Like Walk, but doesn't use Copy. This can lead to + // exponential runtimes on cross-linked Regexps like the + // ones generated by Simplify. To help limit this, + // at most max_visits nodes will be visited and then + // the walk will be cut off early. + // If the walk *is* cut off early, ShortVisit(re) + // will be called on regexps that cannot be fully + // visited rather than calling PreVisit/PostVisit. + T WalkExponential(Regexp* re, T top_arg, int max_visits); + + // Clears the stack. Should never be necessary, since + // Walk always enters and exits with an empty stack. + // Logs DFATAL if stack is not already clear. + void Reset(); + + // Returns whether walk was cut off. + bool stopped_early() { return stopped_early_; } + + private: + // Walk state for the entire traversal. + std::stack> stack_; + bool stopped_early_; + int max_visits_; + + T WalkInternal(Regexp* re, T top_arg, bool use_copy); + + Walker(const Walker&) = delete; + Walker& operator=(const Walker&) = delete; +}; + +template T Regexp::Walker::PreVisit(Regexp* re, + T parent_arg, + bool* stop) { + return parent_arg; +} + +template T Regexp::Walker::PostVisit(Regexp* re, + T parent_arg, + T pre_arg, + T* child_args, + int nchild_args) { + return pre_arg; +} + +template T Regexp::Walker::Copy(T arg) { + return arg; +} + +// State about a single level in the traversal. +template struct WalkState { + WalkState(Regexp* re, T parent) + : re(re), + n(-1), + parent_arg(parent), + child_args(NULL) { } + + Regexp* re; // The regexp + int n; // The index of the next child to process; -1 means need to PreVisit + T parent_arg; // Accumulated arguments. + T pre_arg; + T child_arg; // One-element buffer for child_args. + T* child_args; +}; + +template Regexp::Walker::Walker() { + stopped_early_ = false; +} + +template Regexp::Walker::~Walker() { + Reset(); +} + +// Clears the stack. Should never be necessary, since +// Walk always enters and exits with an empty stack. +// Logs DFATAL if stack is not already clear. +template void Regexp::Walker::Reset() { + if (!stack_.empty()) { +#if 0 + LOG(DFATAL) << "Stack not empty."; +#endif + while (!stack_.empty()) { + delete[] stack_.top().child_args; + stack_.pop(); + } + } +} + +template T Regexp::Walker::WalkInternal(Regexp* re, T top_arg, + bool use_copy) { + Reset(); + + if (re == NULL) { +#if 0 + LOG(DFATAL) << "Walk NULL"; +#endif + return top_arg; + } + + stack_.push(WalkState(re, top_arg)); + + WalkState* s; + for (;;) { + T t; + s = &stack_.top(); + Regexp* re = s->re; + switch (s->n) { + case -1: { + if (--max_visits_ < 0) { + stopped_early_ = true; + t = ShortVisit(re, s->parent_arg); + break; + } + bool stop = false; + s->pre_arg = PreVisit(re, s->parent_arg, &stop); + if (stop) { + t = s->pre_arg; + break; + } + s->n = 0; + s->child_args = NULL; + if (re->nsub_ == 1) + s->child_args = &s->child_arg; + else if (re->nsub_ > 1) + s->child_args = new T[re->nsub_]; + FALLTHROUGH_INTENDED; + } + default: { + if (re->nsub_ > 0) { + Regexp** sub = re->sub(); + if (s->n < re->nsub_) { + if (use_copy && s->n > 0 && sub[s->n - 1] == sub[s->n]) { + s->child_args[s->n] = Copy(s->child_args[s->n - 1]); + s->n++; + } else { + stack_.push(WalkState(sub[s->n], s->pre_arg)); + } + continue; + } + } + + t = PostVisit(re, s->parent_arg, s->pre_arg, s->child_args, s->n); + if (re->nsub_ > 1) + delete[] s->child_args; + break; + } + } + + // We've finished stack_.top(). + // Update next guy down. + stack_.pop(); + if (stack_.empty()) + return t; + s = &stack_.top(); + if (s->child_args != NULL) + s->child_args[s->n] = t; + else + s->child_arg = t; + s->n++; + } +} + +template T Regexp::Walker::Walk(Regexp* re, T top_arg) { + // Without the exponential walking behavior, + // this budget should be more than enough for any + // regexp, and yet not enough to get us in trouble + // as far as CPU time. + max_visits_ = 1000000; + return WalkInternal(re, top_arg, true); +} + +template T Regexp::Walker::WalkExponential(Regexp* re, T top_arg, + int max_visits) { + max_visits_ = max_visits; + return WalkInternal(re, top_arg, false); +} + +} // namespace re2 + +#endif // RE2_WALKER_INL_H_ diff --git a/third_party/opa/wasm/src/re2/util/logging.h b/third_party/opa/wasm/src/re2/util/logging.h new file mode 100644 index 000000000000..b389b3bb5278 --- /dev/null +++ b/third_party/opa/wasm/src/re2/util/logging.h @@ -0,0 +1,114 @@ +// Copyright 2009 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef UTIL_LOGGING_H_ +#define UTIL_LOGGING_H_ + +// Simplified version of Google's logging. + +#include +#include +#include +#if 0 +#include +#include +#endif + +#include "util/util.h" + +// Debug-only checking. +#define DCHECK(condition) assert(condition) +#define DCHECK_EQ(val1, val2) assert((val1) == (val2)) +#define DCHECK_NE(val1, val2) assert((val1) != (val2)) +#define DCHECK_LE(val1, val2) assert((val1) <= (val2)) +#define DCHECK_LT(val1, val2) assert((val1) < (val2)) +#define DCHECK_GE(val1, val2) assert((val1) >= (val2)) +#define DCHECK_GT(val1, val2) assert((val1) > (val2)) + +// Always-on checking +#define CHECK(x) if(x){}else LogMessageFatal(__FILE__, __LINE__).stream() << "Check failed: " #x +#define CHECK_LT(x, y) CHECK((x) < (y)) +#define CHECK_GT(x, y) CHECK((x) > (y)) +#define CHECK_LE(x, y) CHECK((x) <= (y)) +#define CHECK_GE(x, y) CHECK((x) >= (y)) +#define CHECK_EQ(x, y) CHECK((x) == (y)) +#define CHECK_NE(x, y) CHECK((x) != (y)) + +#define LOG_INFO LogMessage(__FILE__, __LINE__) +#define LOG_WARNING LogMessage(__FILE__, __LINE__) +#define LOG_ERROR LogMessage(__FILE__, __LINE__) +#define LOG_FATAL LogMessageFatal(__FILE__, __LINE__) +#define LOG_QFATAL LOG_FATAL + +// It seems that one of the Windows header files defines ERROR as 0. +#ifdef _WIN32 +#define LOG_0 LOG_INFO +#endif + +#ifdef NDEBUG +#define LOG_DFATAL LOG_ERROR +#else +#define LOG_DFATAL LOG_FATAL +#endif + +#define LOG(severity) LOG_ ## severity.stream() + +#define VLOG(x) if((x)>0){}else LOG_INFO.stream() + +#if 0 +class LogMessage { + public: + LogMessage(const char* file, int line) + : flushed_(false) { + stream() << file << ":" << line << ": "; + } + void Flush() { + stream() << "\n"; + std::string s = str_.str(); + size_t n = s.size(); + if (fwrite(s.data(), 1, n, stderr) < n) {} // shut up gcc + flushed_ = true; + } + ~LogMessage() { + if (!flushed_) { + Flush(); + } + } + std::ostream& stream() { return str_; } + + private: + bool flushed_; + std::ostringstream str_; + + LogMessage(const LogMessage&) = delete; + LogMessage& operator=(const LogMessage&) = delete; +}; + +// Silence "destructor never returns" warning for ~LogMessageFatal(). +// Since this is a header file, push and then pop to limit the scope. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4722) +#endif + +class LogMessageFatal : public LogMessage { + public: + LogMessageFatal(const char* file, int line) + : LogMessage(file, line) {} + ATTRIBUTE_NORETURN ~LogMessageFatal() { + Flush(); + abort(); + } + private: + LogMessageFatal(const LogMessageFatal&) = delete; + LogMessageFatal& operator=(const LogMessageFatal&) = delete; +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif + +#endif // UTIL_LOGGING_H_ diff --git a/third_party/opa/wasm/src/re2/util/mix.h b/third_party/opa/wasm/src/re2/util/mix.h new file mode 100644 index 000000000000..d85c172ab0e3 --- /dev/null +++ b/third_party/opa/wasm/src/re2/util/mix.h @@ -0,0 +1,41 @@ +// Copyright 2016 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef UTIL_MIX_H_ +#define UTIL_MIX_H_ + +#include +#include + +namespace re2 { + +// Silence "truncation of constant value" warning for kMul in 32-bit mode. +// Since this is a header file, push and then pop to limit the scope. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4309) +#endif + +class HashMix { + public: + HashMix() : hash_(1) {} + explicit HashMix(size_t val) : hash_(val + 83) {} + void Mix(size_t val) { + static const size_t kMul = static_cast(0xdc3eb94af8ab4c93ULL); + hash_ *= kMul; + hash_ = ((hash_ << 19) | + (hash_ >> (std::numeric_limits::digits - 19))) + val; + } + size_t get() const { return hash_; } + private: + size_t hash_; +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +} // namespace re2 + +#endif // UTIL_MIX_H_ diff --git a/third_party/opa/wasm/src/re2/util/mutex.h b/third_party/opa/wasm/src/re2/util/mutex.h new file mode 100644 index 000000000000..e2a8715edd4c --- /dev/null +++ b/third_party/opa/wasm/src/re2/util/mutex.h @@ -0,0 +1,148 @@ +// Copyright 2007 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef UTIL_MUTEX_H_ +#define UTIL_MUTEX_H_ + +/* + * A simple mutex wrapper, supporting locks and read-write locks. + * You should assume the locks are *not* re-entrant. + */ + +#ifdef _WIN32 +// Requires Windows Vista or Windows Server 2008 at minimum. +#if defined(WINVER) && WINVER >= 0x0600 +#define MUTEX_IS_WIN32_SRWLOCK +#endif +#else +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif +#include +#if defined(_POSIX_READER_WRITER_LOCKS) && _POSIX_READER_WRITER_LOCKS > 0 +#define MUTEX_IS_PTHREAD_RWLOCK +#endif +#endif + +#if defined(MUTEX_IS_WIN32_SRWLOCK) +#include +typedef SRWLOCK MutexType; +#elif defined(MUTEX_IS_PTHREAD_RWLOCK) +#include +#include +typedef pthread_rwlock_t MutexType; +#else +#include +typedef std::mutex MutexType; +#endif + +namespace re2 { + +class Mutex { + public: + inline Mutex(); + inline ~Mutex(); + inline void Lock(); // Block if needed until free then acquire exclusively + inline void Unlock(); // Release a lock acquired via Lock() + // Note that on systems that don't support read-write locks, these may + // be implemented as synonyms to Lock() and Unlock(). So you can use + // these for efficiency, but don't use them anyplace where being able + // to do shared reads is necessary to avoid deadlock. + inline void ReaderLock(); // Block until free or shared then acquire a share + inline void ReaderUnlock(); // Release a read share of this Mutex + inline void WriterLock() { Lock(); } // Acquire an exclusive lock + inline void WriterUnlock() { Unlock(); } // Release a lock from WriterLock() + + private: + MutexType mutex_; + + // Catch the error of writing Mutex when intending MutexLock. + Mutex(Mutex *ignored); + + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; +}; + +#if defined(MUTEX_IS_WIN32_SRWLOCK) + +Mutex::Mutex() { InitializeSRWLock(&mutex_); } +Mutex::~Mutex() { } +void Mutex::Lock() { AcquireSRWLockExclusive(&mutex_); } +void Mutex::Unlock() { ReleaseSRWLockExclusive(&mutex_); } +void Mutex::ReaderLock() { AcquireSRWLockShared(&mutex_); } +void Mutex::ReaderUnlock() { ReleaseSRWLockShared(&mutex_); } + +#elif defined(MUTEX_IS_PTHREAD_RWLOCK) + +#define SAFE_PTHREAD(fncall) \ + do { \ + if ((fncall) != 0) abort(); \ + } while (0) + +Mutex::Mutex() { SAFE_PTHREAD(pthread_rwlock_init(&mutex_, NULL)); } +Mutex::~Mutex() { SAFE_PTHREAD(pthread_rwlock_destroy(&mutex_)); } +void Mutex::Lock() { SAFE_PTHREAD(pthread_rwlock_wrlock(&mutex_)); } +void Mutex::Unlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&mutex_)); } +void Mutex::ReaderLock() { SAFE_PTHREAD(pthread_rwlock_rdlock(&mutex_)); } +void Mutex::ReaderUnlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&mutex_)); } + +#undef SAFE_PTHREAD + +#else + +Mutex::Mutex() { } +Mutex::~Mutex() { } +void Mutex::Lock() { mutex_.lock(); } +void Mutex::Unlock() { mutex_.unlock(); } +void Mutex::ReaderLock() { Lock(); } // C++11 doesn't have std::shared_mutex. +void Mutex::ReaderUnlock() { Unlock(); } + +#endif + +// -------------------------------------------------------------------------- +// Some helper classes + +// MutexLock(mu) acquires mu when constructed and releases it when destroyed. +class MutexLock { + public: + explicit MutexLock(Mutex *mu) : mu_(mu) { mu_->Lock(); } + ~MutexLock() { mu_->Unlock(); } + private: + Mutex * const mu_; + + MutexLock(const MutexLock&) = delete; + MutexLock& operator=(const MutexLock&) = delete; +}; + +// ReaderMutexLock and WriterMutexLock do the same, for rwlocks +class ReaderMutexLock { + public: + explicit ReaderMutexLock(Mutex *mu) : mu_(mu) { mu_->ReaderLock(); } + ~ReaderMutexLock() { mu_->ReaderUnlock(); } + private: + Mutex * const mu_; + + ReaderMutexLock(const ReaderMutexLock&) = delete; + ReaderMutexLock& operator=(const ReaderMutexLock&) = delete; +}; + +class WriterMutexLock { + public: + explicit WriterMutexLock(Mutex *mu) : mu_(mu) { mu_->WriterLock(); } + ~WriterMutexLock() { mu_->WriterUnlock(); } + private: + Mutex * const mu_; + + WriterMutexLock(const WriterMutexLock&) = delete; + WriterMutexLock& operator=(const WriterMutexLock&) = delete; +}; + +// Catch bug where variable name is omitted, e.g. MutexLock (&mu); +#define MutexLock(x) static_assert(false, "MutexLock declaration missing variable name") +#define ReaderMutexLock(x) static_assert(false, "ReaderMutexLock declaration missing variable name") +#define WriterMutexLock(x) static_assert(false, "WriterMutexLock declaration missing variable name") + +} // namespace re2 + +#endif // UTIL_MUTEX_H_ diff --git a/third_party/opa/wasm/src/re2/util/rune.cc b/third_party/opa/wasm/src/re2/util/rune.cc new file mode 100644 index 000000000000..4f625ea380f4 --- /dev/null +++ b/third_party/opa/wasm/src/re2/util/rune.cc @@ -0,0 +1,260 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +#include +#include + +#include "util/utf.h" + +namespace re2 { + +enum +{ + Bit1 = 7, + Bitx = 6, + Bit2 = 5, + Bit3 = 4, + Bit4 = 3, + Bit5 = 2, + + T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */ + Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */ + T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */ + T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */ + T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */ + T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */ + + Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */ + Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */ + Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */ + Rune4 = (1<<(Bit4+3*Bitx))-1, + /* 0001 1111 1111 1111 1111 1111 */ + + Maskx = (1< T1 + */ + c = *(unsigned char*)str; + if(c < Tx) { + *rune = c; + return 1; + } + + /* + * two character sequence + * 0080-07FF => T2 Tx + */ + c1 = *(unsigned char*)(str+1) ^ Tx; + if(c1 & Testx) + goto bad; + if(c < T3) { + if(c < T2) + goto bad; + l = ((c << Bitx) | c1) & Rune2; + if(l <= Rune1) + goto bad; + *rune = l; + return 2; + } + + /* + * three character sequence + * 0800-FFFF => T3 Tx Tx + */ + c2 = *(unsigned char*)(str+2) ^ Tx; + if(c2 & Testx) + goto bad; + if(c < T4) { + l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; + if(l <= Rune2) + goto bad; + *rune = l; + return 3; + } + + /* + * four character sequence (21-bit value) + * 10000-1FFFFF => T4 Tx Tx Tx + */ + c3 = *(unsigned char*)(str+3) ^ Tx; + if (c3 & Testx) + goto bad; + if (c < T5) { + l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4; + if (l <= Rune3) + goto bad; + *rune = l; + return 4; + } + + /* + * Support for 5-byte or longer UTF-8 would go here, but + * since we don't have that, we'll just fall through to bad. + */ + + /* + * bad decoding + */ +bad: + *rune = Bad; + return 1; +} + +int +runetochar(char *str, const Rune *rune) +{ + /* Runes are signed, so convert to unsigned for range check. */ + unsigned long c; + + /* + * one character sequence + * 00000-0007F => 00-7F + */ + c = *rune; + if(c <= Rune1) { + str[0] = static_cast(c); + return 1; + } + + /* + * two character sequence + * 0080-07FF => T2 Tx + */ + if(c <= Rune2) { + str[0] = T2 | static_cast(c >> 1*Bitx); + str[1] = Tx | (c & Maskx); + return 2; + } + + /* + * If the Rune is out of range, convert it to the error rune. + * Do this test here because the error rune encodes to three bytes. + * Doing it earlier would duplicate work, since an out of range + * Rune wouldn't have fit in one or two bytes. + */ + if (c > Runemax) + c = Runeerror; + + /* + * three character sequence + * 0800-FFFF => T3 Tx Tx + */ + if (c <= Rune3) { + str[0] = T3 | static_cast(c >> 2*Bitx); + str[1] = Tx | ((c >> 1*Bitx) & Maskx); + str[2] = Tx | (c & Maskx); + return 3; + } + + /* + * four character sequence (21-bit value) + * 10000-1FFFFF => T4 Tx Tx Tx + */ + str[0] = T4 | static_cast(c >> 3*Bitx); + str[1] = Tx | ((c >> 2*Bitx) & Maskx); + str[2] = Tx | ((c >> 1*Bitx) & Maskx); + str[3] = Tx | (c & Maskx); + return 4; +} + +int +runelen(Rune rune) +{ + char str[10]; + + return runetochar(str, &rune); +} + +int +fullrune(const char *str, int n) +{ + if (n > 0) { + int c = *(unsigned char*)str; + if (c < Tx) + return 1; + if (n > 1) { + if (c < T3) + return 1; + if (n > 2) { + if (c < T4 || n > 3) + return 1; + } + } + } + return 0; +} + + +int +utflen(const char *s) +{ + int c; + long n; + Rune rune; + + n = 0; + for(;;) { + c = *(unsigned char*)s; + if(c < Runeself) { + if(c == 0) + return n; + s++; + } else + s += chartorune(&rune, s); + n++; + } + return 0; +} + +char* +utfrune(const char *s, Rune c) +{ + long c1; + Rune r; + int n; + + if(c < Runesync) /* not part of utf sequence */ + return strchr((char*)s, c); + + for(;;) { + c1 = *(unsigned char*)s; + if(c1 < Runeself) { /* one byte rune */ + if(c1 == 0) + return 0; + if(c1 == c) + return (char*)s; + s++; + continue; + } + n = chartorune(&r, s); + if(r == c) + return (char*)s; + s += n; + } + return 0; +} + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/util/strutil.cc b/third_party/opa/wasm/src/re2/util/strutil.cc new file mode 100644 index 000000000000..fb7e6b1b0c77 --- /dev/null +++ b/third_party/opa/wasm/src/re2/util/strutil.cc @@ -0,0 +1,149 @@ +// Copyright 1999-2005 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include + +#include "util/strutil.h" + +#ifdef _WIN32 +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif + +namespace re2 { + +// ---------------------------------------------------------------------- +// CEscapeString() +// Copies 'src' to 'dest', escaping dangerous characters using +// C-style escape sequences. 'src' and 'dest' should not overlap. +// Returns the number of bytes written to 'dest' (not including the \0) +// or (size_t)-1 if there was insufficient space. +// ---------------------------------------------------------------------- +static size_t CEscapeString(const char* src, size_t src_len, + char* dest, size_t dest_len) { + const char* src_end = src + src_len; + size_t used = 0; + + for (; src < src_end; src++) { + if (dest_len - used < 2) // space for two-character escape + return (size_t)-1; + + unsigned char c = *src; + switch (c) { + case '\n': dest[used++] = '\\'; dest[used++] = 'n'; break; + case '\r': dest[used++] = '\\'; dest[used++] = 'r'; break; + case '\t': dest[used++] = '\\'; dest[used++] = 't'; break; + case '\"': dest[used++] = '\\'; dest[used++] = '\"'; break; + case '\'': dest[used++] = '\\'; dest[used++] = '\''; break; + case '\\': dest[used++] = '\\'; dest[used++] = '\\'; break; + default: + // Note that if we emit \xNN and the src character after that is a hex + // digit then that digit must be escaped too to prevent it being + // interpreted as part of the character code by C. + if (c < ' ' || c > '~') { + if (dest_len - used < 5) // space for four-character escape + \0 + return (size_t)-1; + snprintf(dest + used, 5, "\\%03o", c); + used += 4; + } else { + dest[used++] = c; break; + } + } + } + + if (dest_len - used < 1) // make sure that there is room for \0 + return (size_t)-1; + + dest[used] = '\0'; // doesn't count towards return value though + return used; +} + +// ---------------------------------------------------------------------- +// CEscape() +// Copies 'src' to result, escaping dangerous characters using +// C-style escape sequences. 'src' and 'dest' should not overlap. +// ---------------------------------------------------------------------- +std::string CEscape(const StringPiece& src) { + const size_t dest_len = src.size() * 4 + 1; // Maximum possible expansion + char* dest = new char[dest_len]; + const size_t used = CEscapeString(src.data(), src.size(), + dest, dest_len); + std::string s = std::string(dest, used); + delete[] dest; + return s; +} + +void PrefixSuccessor(std::string* prefix) { + // We can increment the last character in the string and be done + // unless that character is 255, in which case we have to erase the + // last character and increment the previous character, unless that + // is 255, etc. If the string is empty or consists entirely of + // 255's, we just return the empty string. + while (!prefix->empty()) { + char& c = prefix->back(); + if (c == '\xff') { // char literal avoids signed/unsigned. + prefix->pop_back(); + } else { + ++c; + break; + } + } +} + +static void StringAppendV(std::string* dst, const char* format, va_list ap) { + // First try with a small fixed size buffer + char space[1024]; + + // It's possible for methods that use a va_list to invalidate + // the data in it upon use. The fix is to make a copy + // of the structure before using it and use that copy instead. + va_list backup_ap; + va_copy(backup_ap, ap); + int result = vsnprintf(space, sizeof(space), format, backup_ap); + va_end(backup_ap); + + if ((result >= 0) && (static_cast(result) < sizeof(space))) { + // It fit + dst->append(space, result); + return; + } + + // Repeatedly increase buffer size until it fits + int length = sizeof(space); + while (true) { + if (result < 0) { + // Older behavior: just try doubling the buffer size + length *= 2; + } else { + // We need exactly "result+1" characters + length = result+1; + } + char* buf = new char[length]; + + // Restore the va_list before we use it again + va_copy(backup_ap, ap); + result = vsnprintf(buf, length, format, backup_ap); + va_end(backup_ap); + + if ((result >= 0) && (result < length)) { + // It fit + dst->append(buf, result); + delete[] buf; + return; + } + delete[] buf; + } +} + +std::string StringPrintf(const char* format, ...) { + va_list ap; + va_start(ap, format); + std::string result; + StringAppendV(&result, format, ap); + va_end(ap); + return result; +} + +} // namespace re2 diff --git a/third_party/opa/wasm/src/re2/util/strutil.h b/third_party/opa/wasm/src/re2/util/strutil.h new file mode 100644 index 000000000000..a69908a0dd94 --- /dev/null +++ b/third_party/opa/wasm/src/re2/util/strutil.h @@ -0,0 +1,21 @@ +// Copyright 2016 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef UTIL_STRUTIL_H_ +#define UTIL_STRUTIL_H_ + +#include + +#include "re2/stringpiece.h" +#include "util/util.h" + +namespace re2 { + +std::string CEscape(const StringPiece& src); +void PrefixSuccessor(std::string* prefix); +std::string StringPrintf(const char* format, ...); + +} // namespace re2 + +#endif // UTIL_STRUTIL_H_ diff --git a/third_party/opa/wasm/src/re2/util/utf.h b/third_party/opa/wasm/src/re2/util/utf.h new file mode 100644 index 000000000000..1572909713e2 --- /dev/null +++ b/third_party/opa/wasm/src/re2/util/utf.h @@ -0,0 +1,50 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + * This file and rune.cc have been converted to compile as C++ code + * in name space re2. + */ + +#ifndef UTIL_UTF_H_ +#define UTIL_UTF_H_ + +#include + +#ifdef __cplusplus +extern "C" { +namespace re2 { +#endif + +typedef signed int Rune; /* Code-point values in Unicode 4.0 are 21 bits wide.*/ + +enum +{ + UTFmax = 4, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0xFFFD, /* decoding error in UTF */ + Runemax = 0x10FFFF, /* maximum rune value */ +}; + +int runetochar(char* s, const Rune* r); +int chartorune(Rune* r, const char* s); +int fullrune(const char* s, int n); +int utflen(const char* s); +char* utfrune(const char*, Rune); + +#ifdef __cplusplus +} // namespace re2 +} // extern "C" +#endif + +#endif // UTIL_UTF_H_ diff --git a/third_party/opa/wasm/src/re2/util/util.h b/third_party/opa/wasm/src/re2/util/util.h new file mode 100644 index 000000000000..56e46c1a3385 --- /dev/null +++ b/third_party/opa/wasm/src/re2/util/util.h @@ -0,0 +1,42 @@ +// Copyright 2009 The RE2 Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef UTIL_UTIL_H_ +#define UTIL_UTIL_H_ + +#define arraysize(array) (sizeof(array)/sizeof((array)[0])) + +#ifndef ATTRIBUTE_NORETURN +#if defined(__GNUC__) +#define ATTRIBUTE_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define ATTRIBUTE_NORETURN __declspec(noreturn) +#else +#define ATTRIBUTE_NORETURN +#endif +#endif + +#ifndef ATTRIBUTE_UNUSED +#if defined(__GNUC__) +#define ATTRIBUTE_UNUSED __attribute__((unused)) +#else +#define ATTRIBUTE_UNUSED +#endif +#endif + +#ifndef FALLTHROUGH_INTENDED +#if defined(__clang__) +#define FALLTHROUGH_INTENDED [[clang::fallthrough]] +#elif defined(__GNUC__) && __GNUC__ >= 7 +#define FALLTHROUGH_INTENDED [[gnu::fallthrough]] +#else +#define FALLTHROUGH_INTENDED do {} while (0) +#endif +#endif + +#ifndef NO_THREAD_SAFETY_ANALYSIS +#define NO_THREAD_SAFETY_ANALYSIS +#endif + +#endif // UTIL_UTIL_H_ diff --git a/third_party/opa/wasm/src/regex.cc b/third_party/opa/wasm/src/regex.cc new file mode 100644 index 000000000000..7ce797bb554c --- /dev/null +++ b/third_party/opa/wasm/src/regex.cc @@ -0,0 +1,198 @@ +#include "regex.h" +#include "re2/re2.h" +#include "malloc.h" +#include "std.h" +#include "str.h" +#include "unicode.h" +#include "util/utf.h" + +#include + +static const int MAX_CACHE_SIZE = 100; + +typedef std::unordered_map re_cache; + +OPA_BUILTIN +opa_value *opa_regex_is_valid(opa_value *pattern) +{ + if (opa_value_type(pattern) != OPA_STRING) + { + return opa_boolean(false); + } + + std::string pat(opa_cast_string(pattern)->v, opa_cast_string(pattern)->len); + re2::RE2::Options options; + re2::RE2 re(pat, options); + return opa_boolean(re.ok()); +} + +static re_cache* cache() +{ + re_cache* c = static_cast(opa_builtin_cache_get(0)); + if (c == NULL) + { + c = new re_cache(); + opa_builtin_cache_set(0, c); + } + + return c; +} + +// compile compiles a pattern, using an earlier compilation if possible. +static re2::RE2* compile(const char *pattern) +{ + re_cache* c = cache(); + re_cache::iterator i = c->find(pattern); + if (i != c->end()) + { + return i->second; + } + + re2::RE2::Options options; + options.set_log_errors(false); + re2::RE2 *re = new re2::RE2(pattern, options); + if (!re->ok()) + { + delete(re); + return NULL; + } + + return re; +} + +// reuse returns the precompiled pattern to the cache. +static void reuse(re2::RE2 *re) +{ + if (cache()->size() >= MAX_CACHE_SIZE) + { + // Delete a (semi-)random key to make room for the new one. + auto i = cache()->begin(); + if (i != cache()->end()) + { + delete i->second; + cache()->erase(i); + } + } + cache()->insert(std::make_pair(re->pattern(), re)); +} + +OPA_BUILTIN +opa_value *opa_regex_match(opa_value *pattern, opa_value *value) +{ + if (opa_value_type(pattern) != OPA_STRING || opa_value_type(value) != OPA_STRING) + { + return NULL; + } + std::string pat(opa_cast_string(pattern)->v, opa_cast_string(pattern)->len); + re2::RE2* re = compile(pat.c_str()); + if (re == NULL) + { + // TODO: return an error. + return NULL; + } + + std::string v(opa_cast_string(value)->v, opa_cast_string(value)->len); + bool match = re2::RE2::PartialMatch(v, *re); + + reuse(re); + return opa_boolean(match); +} + +OPA_BUILTIN +opa_value *opa_regex_find_all_string_submatch(opa_value *pattern, opa_value *value, opa_value *number) +{ + if (opa_value_type(pattern) != OPA_STRING || opa_value_type(value) != OPA_STRING || opa_value_type(number) != OPA_NUMBER) + { + return NULL; + } + + long long num_results; + if (opa_number_try_int(opa_cast_number(number), &num_results)) + { + return NULL; + } + + std::string pat(opa_cast_string(pattern)->v, opa_cast_string(pattern)->len); + re2::RE2* re = compile(pat.c_str()); + if (re == NULL) + { + // TODO: return an error. + return NULL; + } + + std::string val(opa_cast_string(value)->v, opa_cast_string(value)->len); + opa_array_t *result = opa_cast_array(opa_array()); + int nsubmatch = re->NumberOfCapturingGroups() + 1; + re2::StringPiece submatches[nsubmatch]; + + // The following is effectively refactored RE2::GlobalReplace: + + const char* beginpos = val.c_str(); + const char* p = beginpos; + const char* ep = p + val.size(); + const char* lastend = NULL; + int pos = 0; + + while (p <= ep && (num_results == -1 || result->len < num_results)) { + if (!re->Match(val, static_cast(p - beginpos), val.size(), re2::RE2::UNANCHORED, submatches, nsubmatch)) + { + break; + } + + if (p < submatches[0].data()) + { + pos += submatches[0].data() - p; + } + + if (submatches[0].data() == lastend && submatches[0].empty()) { + // Disallow empty match at end of last match: skip ahead. + // + // fullrune() takes int, not ptrdiff_t. However, it just looks + // at the leading byte and treats any length >= 4 the same. + if (re->options().encoding() == RE2::Options::EncodingUTF8 && + re2::fullrune(p, static_cast(std::min(ptrdiff_t{4}, ep - p)))) { + // re is in UTF-8 mode and there is enough left of str + // to allow us to advance by up to UTFmax bytes. + re2::Rune r; + int n = re2::chartorune(&r, p); + // Some copies of chartorune have a bug that accepts + // encodings of values in (10FFFF, 1FFFFF] as valid. + if (r > re2::Runemax) { + n = 1; + r = re2::Runeerror; + } + + if (!(n == 1 && r == re2::Runeerror)) { // no decoding error + pos += n; + p += n; + continue; + } + } + + // Most likely, re is in Latin-1 mode. If it is in UTF-8 mode, + // we fell through from above and the GIGO principle applies. + pos++; + p++; + continue; + } + + opa_array_t *r = opa_cast_array(opa_array_with_cap(nsubmatch)); + + for (int i = 0; i < nsubmatch; i++) { + const size_t length = submatches[i].length(); + char *str = (char *)opa_malloc(length + 1); + + memcpy(str, submatches[i].data(), length); + str[length] = '\0'; + opa_array_append(r, opa_string_allocated(str, length)); + } + + opa_array_append(result, &r->hdr); + + p = submatches[0].data() + submatches[0].size(); + lastend = p; + } + + reuse(re); + return &result->hdr; +} diff --git a/third_party/opa/wasm/src/regex.h b/third_party/opa/wasm/src/regex.h new file mode 100644 index 000000000000..dc043752262e --- /dev/null +++ b/third_party/opa/wasm/src/regex.h @@ -0,0 +1,18 @@ +#ifndef OPA_REGEX_H +#define OPA_REGEX_H + +#include "value.h" + +#ifdef __cplusplus +extern "C" { +#endif + +opa_value *opa_regex_is_valid(opa_value *v); +opa_value *opa_regex_match(opa_value *pattern, opa_value *value); +opa_value *opa_regex_find_all_string_submatch(opa_value *pattern, opa_value *string, opa_value *number); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/set.c b/third_party/opa/wasm/src/set.c new file mode 100644 index 000000000000..d2d426cbfdfd --- /dev/null +++ b/third_party/opa/wasm/src/set.c @@ -0,0 +1,189 @@ +#include "set.h" +#include "std.h" + +OPA_BUILTIN +opa_value *opa_set_diff(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_SET || opa_value_type(b) != OPA_SET) + { + return NULL; + } + + opa_set_t *x = opa_cast_set(a); + opa_set_t *y = opa_cast_set(b); + opa_set_t *r = opa_cast_set(opa_set()); + + for (int i = 0; i < x->n; i++) + { + opa_set_elem_t *elem = x->buckets[i]; + + while (elem != NULL) + { + if (opa_set_get(y, elem->v) == NULL) + { + opa_set_add(r, elem->v); + } + elem = elem->next; + } + } + + return &r->hdr; +} + +OPA_BUILTIN +opa_value *opa_set_intersection(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_SET || opa_value_type(b) != OPA_SET) + { + return NULL; + } + + opa_set_t *x = opa_cast_set(a); + opa_set_t *y = opa_cast_set(b); + opa_set_t *r = opa_cast_set(opa_set_with_cap(x->len < y->len ? x->len : y->len)); + + if (y->len < x->len) + { + x = opa_cast_set(b); + y = opa_cast_set(a); + } + + for (int i = 0; i < x->n; i++) + { + opa_set_elem_t *elem = x->buckets[i]; + + while (elem != NULL) + { + if (opa_set_get(y, elem->v) != NULL) + { + opa_set_add(r, elem->v); + } + elem = elem->next; + } + } + + return &r->hdr; +} + +OPA_BUILTIN +opa_value *opa_sets_intersection(opa_value *v) +{ + if (opa_value_type(v) != OPA_SET) + { + return NULL; + } + + opa_set_t *s = opa_cast_set(v); + + if (s->len == 0) + { + return opa_set(); + } + + opa_value *r = NULL; + + for (int i = 0; i < s->n; i++) + { + opa_set_elem_t *elem = s->buckets[i]; + + while (elem != NULL) + { + if (opa_value_type(elem->v) != OPA_SET) + { + return NULL; + } + + if (r == NULL) + { + r = opa_set_union(opa_set(), elem->v); + } else { + opa_value *x = opa_set_intersection(r, elem->v); + opa_value_free_shallow(r); + if (x == NULL) + { + return NULL; + } + + r = x; + } + + elem = elem->next; + } + } + + return r; +} + +OPA_BUILTIN +opa_value *opa_set_union(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_SET || opa_value_type(b) != OPA_SET) + { + return NULL; + } + + opa_set_t *x = opa_cast_set(a); + opa_set_t *y = opa_cast_set(b); + opa_set_t *r = opa_cast_set(opa_set()); + + for (int i = 0; i < x->n; i++) + { + opa_set_elem_t *elem = x->buckets[i]; + + while (elem != NULL) + { + opa_set_add(r, elem->v); + elem = elem->next; + } + } + + for (int i = 0; i < y->n; i++) + { + opa_set_elem_t *elem = y->buckets[i]; + + while (elem != NULL) + { + opa_set_add(r, elem->v); + elem = elem->next; + } + } + + return &r->hdr; +} + +OPA_BUILTIN +opa_value *opa_sets_union(opa_value *v) +{ + if (opa_value_type(v) != OPA_SET) + { + return NULL; + } + + opa_set_t *s = opa_cast_set(v); + opa_value *r = opa_set(); + + for (int i = 0; i < s->n; i++) + { + opa_set_elem_t *elem = s->buckets[i]; + + while (elem != NULL) + { + if (opa_value_type(elem->v) != OPA_SET) + { + return NULL; + } + + opa_value *x = opa_set_union(r, elem->v); + opa_value_free_shallow(r); + if (x == NULL) + { + return NULL; + } + + r = x; + elem = elem->next; + } + } + + return r; +} diff --git a/third_party/opa/wasm/src/set.h b/third_party/opa/wasm/src/set.h new file mode 100644 index 000000000000..2490c41d82f3 --- /dev/null +++ b/third_party/opa/wasm/src/set.h @@ -0,0 +1,13 @@ +#ifndef OPA_SET_H +#define OPA_SET_H + +#include "value.h" + +opa_value *opa_set_diff(opa_value *a, opa_value *b); +opa_value *opa_set_intersection(opa_value *a, opa_value *b); +opa_value *opa_set_union(opa_value *a, opa_value *b); + +opa_value *opa_sets_intersection(opa_value *v); +opa_value *opa_sets_union(opa_value *v); + +#endif diff --git a/third_party/opa/wasm/src/std.h b/third_party/opa/wasm/src/std.h new file mode 100644 index 000000000000..72b9656222cc --- /dev/null +++ b/third_party/opa/wasm/src/std.h @@ -0,0 +1,47 @@ +#ifndef OPA_STD_H +#define OPA_STD_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define container_of(ptr, type, member) \ + ((type *)(void *)( ((char *)(ptr) - offsetof(type, member) ))) + +void opa_println(const char *msg); + +#ifdef DEBUG +#include "printf.h" +#define TRACE(...) \ + do { \ + char __trace_buf[256]; \ + snprintf(__trace_buf, 256, __VA_ARGS__); \ + opa_println(__trace_buf); \ + } while (0) +#else +#define TRACE(...) +#endif + +// Functions to be exported from the WASM module +#define WASM_EXPORT(NAME) __attribute__((export_name(#NAME))) +// functions that implement builtins +#define OPA_BUILTIN __attribute__((used)) +// functions that may be called from the generated WASM code +#define OPA_INTERNAL __attribute__((used)) + +// OPA WASM API Error Codes +#define OPA_ERR_OK 0 +#define OPA_ERR_INTERNAL 1 +#define OPA_ERR_INVALID_TYPE 2 +#define OPA_ERR_INVALID_PATH 3 + +typedef int opa_errc; +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/third_party/opa/wasm/src/str.c b/third_party/opa/wasm/src/str.c new file mode 100644 index 000000000000..14c3c840662b --- /dev/null +++ b/third_party/opa/wasm/src/str.c @@ -0,0 +1,282 @@ +#include "string.h" +#include "limits.h" + +size_t opa_strlen(const char *s) +{ + const char *ptr = s; + + while (1) + { + if (*ptr == '\0') + { + return ptr - s; + } + + ptr += 1; + } +} + +int opa_strncmp(const char *a, const char *b, int num) +{ + unsigned char *a1 = (unsigned char *)a; + unsigned char *b1 = (unsigned char *)b; + + while (num--) + { + if (*a1 < *b1) + { + return -1; + } + else if (*a1 > *b1) + { + return 1; + } + a1++; + b1++; + } + + return 0; +} + +int opa_strcmp(const char *a, const char *b) +{ + size_t len_a = opa_strlen(a); + size_t len_b = opa_strlen(b); + size_t min = len_a; + + if (len_b < min) + { + min = len_b; + } + + unsigned char *a1 = (unsigned char *)a; + unsigned char *b1 = (unsigned char *)b; + + for (int i = 0; i < min; i++) + { + if (a1[i] < b1[i]) + { + return -1; + } + else if (a[i] > b[i]) + { + return 1; + } + } + + if (len_a < len_b) + { + return -1; + } + else if (len_a > len_b) + { + return 1; + } + return 0; +} + +int opa_isdigit(char b) +{ + return b >= '0' && b <= '9'; +} + +int opa_isspace(char b) +{ + return b == ' ' || b == '\r' || b == '\n' || b == '\t'; +} + +int opa_ishex(char b) +{ + return opa_isdigit(b) || (b >= 'A' && b <= 'F') || (b >= 'a' && b <= 'f'); +} + +char *opa_reverse(char *str) +{ + size_t n = opa_strlen(str)-1; + + if (n <= 0) + { + return str; + } + + int i = 0; + + while (i < n) + { + char tmp = str[i]; + str[i] = str[n]; + str[n] = tmp; + + i++; + n--; + } + + return str; +} + +const char *digits = "0123456789abcdef"; + +char *opa_itoa(long long i, char *str, int base) +{ + char *buf = str; + int is_negative = 0; + + if (i < 0) + { + is_negative = 1; + i = -i; + } + + do + { + int x = i % base; + *buf++ = digits[x]; + i /= base; + } + while (i > 0); + + if (is_negative) + { + *buf++ = '-'; + } + + *buf++ = 0; + + return opa_reverse(str); +} + +int opa_atoi64(const char *str, int len, long long *result) +{ + if (len <= 0) + { + return -1; + } + + int i = 0; + int sign = 1; + + if (str[i] == '-') + { + sign = -1; + i++; + } + + long long n = 0; + + for (; i < len; i++) + { + if (!opa_isdigit(str[i])) + { + return -2; + } + + if (n > (LLONG_MAX - (str[i] - '0')) / 10) + { + return -1; + } + + n = (n * 10) + (long long)(str[i] - '0'); + } + + *result = n * sign; + + return 0; +} + +int opa_atof64(const char *str, int len, double *result) +{ + if (len <= 0) + { + return -1; + } + + // Handle sign. + double sign = 1.0; + int i = 0; + + if (str[i] == '-') + { + sign = -1.0; + i++; + } + + // Handle integer component. + double d = 0.0; + + for (; i < len && opa_isdigit(str[i]); i++) + { + d = (10.0 * d) + (double)(str[i] - '0'); + } + + d *= sign; + + if (i == len) + { + *result = d; + return 0; + } + + // Handle fraction component. + if (str[i] == '.') + { + i++; + + double b = 0.1; + double frac = 0; + + for (; i < len && opa_isdigit(str[i]); i++) + { + frac += b * (str[i] - '0'); + b /= 10.0; + } + + d += (frac * sign); + + if (i == len) + { + *result = d; + return 0; + } + + } + + // Handle exponent component. + if (str[i] == 'e' || str[i] == 'E') + { + i++; + int exp_sign = 1; + + if (str[i] == '-') + { + exp_sign = -1; + i++; + } + else if (str[i] == '+') + { + i++; + } + + int e = 0; + + for (; i < len && opa_isdigit(str[i]); i++) + { + e = 10 * e + (int)(str[i] - '0'); + } + + if (i == len) + { + // Calculate pow(10, e). + int x = 1; + + for (; e > 0; e--) + { + x *= 10; + } + + *result = d * (double)(exp_sign * x); + return 0; + } + } + + return -2; +} diff --git a/third_party/opa/wasm/src/str.h b/third_party/opa/wasm/src/str.h new file mode 100644 index 000000000000..e7330a7abe2c --- /dev/null +++ b/third_party/opa/wasm/src/str.h @@ -0,0 +1,25 @@ +#ifndef OPA_STR_H +#define OPA_STR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +size_t opa_strlen(const char *s); +int opa_strncmp(const char *a, const char *b, int num); +int opa_strcmp(const char *a, const char *b); +int opa_isdigit(char b); +int opa_isspace(char b); +int opa_ishex(char b); +char *opa_itoa(long long i, char *str, int base); +char *opa_reverse(char *str); +int opa_atoi64(const char *str, int len, long long *i); +int opa_atof64(const char *str, int len, double *d); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/strings.c b/third_party/opa/wasm/src/strings.c new file mode 100644 index 000000000000..1c621c92b082 --- /dev/null +++ b/third_party/opa/wasm/src/strings.c @@ -0,0 +1,1129 @@ +#include "malloc.h" +#include "mpd.h" +#include "std.h" +#include "str.h" +#include "string.h" +#include "strings.h" +#include "unicode.h" + +OPA_BUILTIN +opa_value *opa_strings_any_prefix_match(opa_value *a, opa_value *b) +{ + // If the first argument is a string, continue to matching. + // Otherwise, if it's an array or set, recur for each element. + // In other words if opa_strings_any_prefix_match(["test", "test2"], x) is called, + // then this will result in two recurrent calls: + // - opa_strings_any_prefix_match("test", x) + // - opa_strings_any_prefix_match("test2", x) + switch (opa_value_type(a)) + { + case OPA_STRING: { + break; + } + case OPA_ARRAY: + case OPA_SET: { + opa_value *prev = NULL; + opa_value *curr = NULL; + while ((curr = opa_value_iter(a, prev)) != NULL) + { + opa_value *elem = opa_value_get(a, curr); + if (opa_value_type(elem) != OPA_STRING) + { + return NULL; + } + + opa_value *res = opa_strings_any_prefix_match(elem, b); + if (res == NULL) { + return NULL; + } + opa_boolean_t *res_b = opa_cast_boolean(res); + if (res_b->v) { + return res; + } + opa_value_free(res); + + prev = curr; + } + return opa_boolean(false); + } + default: + return NULL; + } + + // If the second argument is a string, continue to matching. + // Otherwise, if it's an array or set, recur for each element. + // In other words if opa_strings_any_prefix_match(x, ["test", "test2"]) is called, + // then this will result in two recurrent calls: + // - opa_strings_any_prefix_match(x, "test") + // - opa_strings_any_prefix_match(x, "test2") + switch (opa_value_type(b)) + { + case OPA_STRING: { + break; + } + case OPA_ARRAY: + case OPA_SET: { + opa_value *prev = NULL; + opa_value *curr = NULL; + while ((curr = opa_value_iter(b, prev)) != NULL) + { + opa_value *elem = opa_value_get(b, curr); + if (opa_value_type(elem) != OPA_STRING) + { + return NULL; + } + + opa_value *res = opa_strings_any_prefix_match(a, elem); + if (res == NULL) { + return NULL; + } + opa_boolean_t *res_b = opa_cast_boolean(res); + if (res_b->v) { + return res; + } + opa_value_free(res); + + prev = curr; + } + return opa_boolean(false); + } + default: + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + opa_string_t *prefix = opa_cast_string(b); + + if (s->len < prefix->len) + { + return opa_boolean(false); + } + + return opa_boolean(opa_strncmp(s->v, prefix->v, prefix->len) == 0); +} + +OPA_BUILTIN +opa_value *opa_strings_any_suffix_match(opa_value *a, opa_value *b) +{ + + // If the first argument is a string, continue to matching. + // Otherwise, if it's an array or set, recur for each element. + // In other words if opa_strings_any_suffix_match(["test", "test2"], x) is called, + // then this will result in two recurrent calls: + // - opa_strings_any_suffix_match("test", x) + // - opa_strings_any_suffix_match("test2", x) + switch (opa_value_type(a)) + { + case OPA_STRING: { + break; + } + case OPA_ARRAY: + case OPA_SET: { + opa_value *prev = NULL; + opa_value *curr = NULL; + while ((curr = opa_value_iter(a, prev)) != NULL) + { + opa_value *elem = opa_value_get(a, curr); + if (opa_value_type(elem) != OPA_STRING) + { + return NULL; + } + + opa_value *res = opa_strings_any_suffix_match(elem, b); + if (res == NULL) { + return NULL; + } + opa_boolean_t *res_b = opa_cast_boolean(res); + if (res_b->v) { + return res; + } + opa_value_free(res); + + prev = curr; + } + return opa_boolean(false); + } + default: + return NULL; + } + + // If the second argument is a string, continue to matching. + // Otherwise, if it's an array or set, recur for each element. + // In other words if opa_strings_any_suffix_match(x, ["test", "test2"]) is called, + // then this will result in two recurrent calls: + // - opa_strings_any_suffix_match(x, "test") + // - opa_strings_any_suffix_match(x, "test2") + switch (opa_value_type(b)) + { + case OPA_STRING: { + break; + } + case OPA_ARRAY: + case OPA_SET: { + opa_value *prev = NULL; + opa_value *curr = NULL; + while ((curr = opa_value_iter(b, prev)) != NULL) + { + opa_value *elem = opa_value_get(b, curr); + if (opa_value_type(elem) != OPA_STRING) + { + return NULL; + } + + opa_value *res = opa_strings_any_suffix_match(a, elem); + if (res == NULL) { + return NULL; + } + opa_boolean_t *res_b = opa_cast_boolean(res); + if (res_b->v) { + return res; + } + opa_value_free(res); + + prev = curr; + } + return opa_boolean(false); + } + default: + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + opa_string_t *suffix = opa_cast_string(b); + + if (s->len < suffix->len) + { + return opa_boolean(false); + } + + for (int i = 0; i < suffix->len; i++) + { + if (s->v[s->len - suffix->len + i] != suffix->v[i]) + { + return opa_boolean(false); + } + } + + return opa_boolean(true); +} + +OPA_BUILTIN +opa_value *opa_strings_concat(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING) + { + return NULL; + } + + opa_string_t *join = opa_cast_string(a); + size_t len = 1; // 1 for '\0' + + switch (opa_value_type(b)) + { + case OPA_ARRAY: { + opa_array_t *a = opa_cast_array(b); + + for (int i = 0; i < a->len; i++) + { + opa_value *v = a->elems[i].v; + if (opa_value_type(v) != OPA_STRING) { + return NULL; + } + + len += opa_cast_string(v)->len; + } + + if (a->len > 0) + { + len += (a->len - 1) * join->len; + } + + char *str = opa_malloc(len); + size_t j = 0; + + for (int i = 0; i < a->len; i++) + { + if (i > 0) + { + memcpy(&str[j], join->v, join->len); + j += join->len; + } + + opa_string_t *s = opa_cast_string(a->elems[i].v); + memcpy(&str[j], s->v, s->len); + j += s->len; + } + + str[len - 1] = '\0'; + return opa_string_allocated(str, len - 1); + } + + case OPA_SET: { + opa_set_t *s = opa_cast_set(b); + + for (int i = 0; i < s->n; i++) + { + opa_set_elem_t *elem = s->buckets[i]; + + while (elem != NULL) + { + opa_value *v = elem->v; + if (opa_value_type(v) != OPA_STRING) + { + return NULL; + } + + len += opa_cast_string(v)->len; + elem = elem->next; + } + } + + if (s->len > 0) + { + len += (s->len - 1) * join->len; + } + + char *str = opa_malloc(len); + int j = -1; + + for (int i = 0; i < s->n; i++) + { + opa_set_elem_t *elem = s->buckets[i]; + + while (elem != NULL) + { + if (j < 0) + { + j = 0; // no separator before the first element written. + } else { + memcpy(&str[j], join->v, join->len); + j += join->len; + } + + opa_string_t *s = opa_cast_string(elem->v); + memcpy(&str[j], s->v, s->len); + j += s->len; + + elem = elem->next; + } + } + + str[len - 1] = '\0'; + return opa_string_allocated(str, len - 1); + } + + default: + return NULL; + } +} + +static int strings_indexof(opa_string_t *s, int pos, opa_string_t *substr) +{ + // TODO: Implement Karp-Rabin string search. + + for (int i = pos; i <= (int)s->len - (int)substr->len; i++) + { + if (opa_strncmp(&s->v[i], substr->v, substr->len) == 0) + { + return i; + } + } + + return -1; +} + +OPA_BUILTIN +opa_value *opa_strings_contains(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + opa_string_t *substr = opa_cast_string(b); + + return opa_boolean(strings_indexof(s, 0, substr) >= 0); +} + +OPA_BUILTIN +opa_value *opa_strings_endswith(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + opa_string_t *suffix = opa_cast_string(b); + + if (s->len < suffix->len) + { + return opa_boolean(false); + } + + for (int i = 0; i < suffix->len; i++) + { + if (s->v[s->len - suffix->len + i] != suffix->v[i]) + { + return opa_boolean(false); + } + } + + return opa_boolean(true); +} + +OPA_BUILTIN +opa_value *opa_strings_format_int(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_NUMBER || opa_value_type(b) != OPA_NUMBER) + { + return NULL; + } + + opa_number_t *base = opa_cast_number(b); + + long long v; + if (opa_number_try_int(base, &v) != 0) + { + return NULL; + } + + const char *format; + switch (v) { + case 2: + format = "%b"; + break; + case 8: + format = "%o"; + break; + case 10: + format = "%d"; + break; + case 16: + format = "%x"; + break; + default: + return NULL; + } + + mpd_t *input = opa_number_to_bf(a); + if (input == NULL) + { + return NULL; + } + + mpd_t *i = mpd_qnew(); + uint32_t status = 0; + mpd_qtrunc(i, input, mpd_max_ctx(), &status); + if (status != 0) + { + opa_abort("strings: truncate failed"); + } + + int32_t w = mpd_qget_i32(i, &status); + if (status != 0) + { + opa_abort("strings: get uint failed"); + } + + char *str = opa_malloc(21); // enough for int_t (with sign). + + if (w < 0) + { + str[0] = '-'; + snprintf(&str[1], 21, format, -w); + } else { + snprintf(str, 21, format, w); + } + + return opa_string_allocated(str, opa_strlen(str)); +} + +OPA_BUILTIN +opa_value *opa_strings_indexof(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + opa_string_t *substr = opa_cast_string(b); + int n = strings_indexof(s, 0, substr); + + if (n < 0) + { + return opa_number_int(n); + } + + int units = 0; + for (int i = 0, len = 0; i < n; units++, i += len) + { + if (opa_unicode_decode_utf8(s->v, i, s->len, &len) == -1) + { + opa_abort("string: invalid unicode"); + } + } + + return opa_number_int(units); +} + +OPA_BUILTIN +opa_value *opa_strings_replace(opa_value *a, opa_value *b, opa_value *c) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING || opa_value_type(c) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + opa_string_t *old = opa_cast_string(b); + opa_string_t *new = opa_cast_string(c); + + int cap = s->len + 1; + char *r = opa_malloc(cap); + + int j = 0; + for (int i = 0; i < s->len; ) { + int match = strings_indexof(s, i, old); + int copy_len = match == -1 ? s->len - i : match - i; + int new_r_len = j + copy_len + new->len + 1; // Optimistic allocation for new string. + + if (cap < new_r_len) + { + cap = new_r_len; + r = opa_realloc(r, cap); + } + + memcpy(&r[j], &s->v[i], copy_len); + i += copy_len + old->len; + j += copy_len; + + if (match != -1) + { + memcpy(&r[j], new->v, new->len); + j += new->len; + } + } + + r[j] = '\0'; + + return opa_string_allocated(r, j); +} + +OPA_BUILTIN +opa_value *opa_strings_replace_n(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_OBJECT || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + opa_object_t *old_new = opa_cast_object(a); + opa_string_t *s = opa_cast_string(b); + + char *buf = opa_malloc(s->len + 1); + memcpy(buf, s->v, s->len + 1); + opa_value *result = opa_string_allocated(buf, s->len); + + for (int i = 0; i < old_new->n; i++) + { + opa_object_elem_t *elem = old_new->buckets[i]; + + while (elem != NULL) + { + opa_value *old = elem->k; + opa_value *new = elem->v; + if (opa_value_type(old) != OPA_STRING || opa_value_type(new) != OPA_STRING) + { + opa_value_free(result); + return NULL; + } + + opa_value *r = opa_strings_replace(result, old, new); + opa_value_free(result); + result = r; + + elem = elem->next; + } + } + + return result; +} + +OPA_BUILTIN +opa_value *opa_strings_reverse(opa_value *a) +{ + if (opa_value_type(a) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + + char *reversed = opa_malloc(s->len + 1); + + for (int i = 0; i < s->len; ) + { + int len = 0; + if (opa_unicode_decode_utf8(s->v, i, s->len, &len) == -1) + { + opa_abort("string: invalid unicode"); + } + memcpy(&reversed[s->len - i - len], &s->v[i], len); + i += len; + } + reversed[s->len] = '\0'; + + return opa_string_allocated(reversed, s->len); +} + +OPA_BUILTIN +opa_value *opa_strings_split(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + opa_string_t *d = opa_cast_string(b); + opa_array_t *arr = opa_cast_array(opa_array()); + + if (d->len == 0) + { + // Split at UTF-8 character boundaries. + for (int i = 0; i < s->len; ) + { + int len = 0; + if (opa_unicode_decode_utf8(s->v, i, s->len, &len) == -1) + { + opa_abort("string: invalid unicode"); + } + + char *str = opa_malloc(len + 1); + memcpy(str, &s->v[i], len); + str[len] = '\0'; + + opa_array_append(arr, opa_string_allocated(str, len)); + + i += len; + } + + return &arr->hdr; + } + + int j = 0; + for (int i = 0; s->len >= d->len && i <= (s->len - d->len); ) + { + if (opa_strncmp(&s->v[i], d->v, d->len) == 0) + { + char *str = opa_malloc(i - j + 1); + memcpy(str, &s->v[j], i - j); + str[i - j] = '\0'; + + opa_array_append(arr, opa_string_allocated(str, i - j)); + + i += d->len; + j = i; + } + else + { + i++; + } + } + + char *str = opa_malloc(s->len - j + 1); + memcpy(str, &s->v[j], s->len - j); + str[s->len - j] = '\0'; + + opa_array_append(arr, opa_string_allocated(str, s->len - j)); + + return &arr->hdr; +} + +OPA_BUILTIN +opa_value *opa_strings_startswith(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + opa_string_t *prefix = opa_cast_string(b); + + if (s->len < prefix->len) + { + return opa_boolean(false); + } + + return opa_boolean(opa_strncmp(s->v, prefix->v, prefix->len) == 0); +} + +OPA_BUILTIN +opa_value *opa_strings_substring(opa_value *a, opa_value *b, opa_value *c) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_NUMBER || opa_value_type(c) != OPA_NUMBER) + { + return NULL; + } + + opa_string_t *base = opa_cast_string(a); + + long long start, length; + if (opa_number_try_int(opa_cast_number(b), &start)) + { + return NULL; + } + + if (opa_number_try_int(opa_cast_number(c), &length)) + { + return NULL; + } + + if (start < 0) + { + return NULL; + } + + if (length == 0) + { + return opa_string_terminated(""); + } + + size_t spos = base->len, epos = base->len; + for (int i = 0, units = 0, len = 0; i < base->len; units++, i += len) + { + if (units == start) + { + spos = i; + } + + if (opa_unicode_decode_utf8(base->v, i, base->len, &len) == -1) + { + opa_abort("string: invalid unicode"); + } + + if (units < start) + { + // Start index not reached yet. + continue; + } + + if (length < 0) + { + // Everything from start to end. + break; + } + + if (length == (units - start)) + { + epos = i; + break; + } + } + + char *str = opa_malloc(epos - spos + 1); + memcpy(str, &base->v[spos], epos - spos); + str[epos - spos] = 0; + return opa_string_allocated(str, epos - spos); +} + +OPA_BUILTIN +opa_value *opa_strings_trim(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + opa_value *s = opa_strings_trim_left(a, b); + opa_value *r = opa_strings_trim_right(s, b); + opa_value_free(s); + return r; +} + +OPA_BUILTIN +opa_value *opa_strings_trim_left(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + opa_string_t *l = opa_cast_string(b); + + int j = 0; + while (j < s->len) + { + int i = 0; + while (i < l->len) + { + int len; + if (opa_unicode_decode_utf8(l->v, i, l->len, &len) == -1) + { + opa_abort("string: invalid unicode"); + } + + if ((j + len) <= s->len && opa_strncmp(&l->v[i], &s->v[j], len) == 0) + { + j += len; // trim codepoint. + break; + } + + i += len; + } + + if (i == l->len) { + // Nothing to trim. + break; + } + } + + char *str = opa_malloc(s->len - j + 1); + memcpy(str, &s->v[j], s->len - j + 1); + return opa_string_allocated(str, s->len - j); +} + +OPA_BUILTIN +opa_value *opa_strings_trim_prefix(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + opa_string_t *pre = opa_cast_string(b); + int start = 0; + + if (s->len >= pre->len && opa_strncmp(s->v, pre->v, pre->len) == 0) + { + start = pre->len; + } + + const int len = s->len - start; + char *str = opa_malloc(len + 1); + memcpy(str, &s->v[start], len + 1); + return opa_string_allocated(str, len); +} + +OPA_BUILTIN +opa_value *opa_strings_trim_right(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + opa_string_t *r = opa_cast_string(b); + + int j = s->len; + while (j > 0) + { + int last = opa_unicode_last_utf8(s->v, 0, j); + if (last == -1) + { + opa_abort("string: invalid unicode"); + } + + int i = 0; + while (i < r->len) + { + int len; + if (opa_unicode_decode_utf8(r->v, i, r->len, &len) == -1) + { + opa_abort("string: invalid unicode"); + } + + if ((last + len) <= s->len && opa_strncmp(&r->v[i], &s->v[last], len) == 0) + { + j -= len; // trim codepoint. + break; + } + + i += len; + } + + if (i == r->len) { + // Nothing to trim. + break; + } + } + + char *str = opa_malloc(j + 1); + memcpy(str, s->v, j); + str[j] = '\0'; + return opa_string_allocated(str, j); +} + +OPA_BUILTIN +opa_value *opa_strings_trim_suffix(opa_value *a, opa_value *b) +{ + if (opa_value_type(a) != OPA_STRING || opa_value_type(b) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + opa_string_t *suf = opa_cast_string(b); + int len = s->len; + + if (s->len >= suf->len && opa_strncmp(&s->v[s->len - suf->len], suf->v, suf->len) == 0) + { + len -= suf->len; + } + + char *str = opa_malloc(len + 1); + memcpy(str, s->v, len); + str[len] = '\0'; + return opa_string_allocated(str, len); +} + +static opa_value *trim_space(const char *s, int start, int end) +{ + while (start < end) + { + int len = 0; + int cp = opa_unicode_decode_utf8(s, start, end, &len); + if (cp == -1) + { + opa_abort("string: invalid unicode"); + } + + if (!opa_unicode_is_space(cp)) + { + break; + } + + start += len; + } + + while (start < end) + { + int last = opa_unicode_last_utf8(s, start, end); + if (last == -1) + { + opa_abort("string: invalid unicode"); + } + + int len; + int cp = opa_unicode_decode_utf8(s, last, end, &len); + if (cp == -1) + { + opa_abort("string: invalid unicode"); + } + + if (!opa_unicode_is_space(cp)) + { + break; + } + + end = last; + } + + char *str = opa_malloc(end - start + 1); + memcpy(str, &s[start], end - start); + str[end - start] = '\0'; + return opa_string_allocated(str, end - start); +} + +OPA_BUILTIN +opa_value *opa_strings_trim_space(opa_value *a) +{ + if (opa_value_type(a) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + + int start = 0; + for (; start < s->len; start++) + { + unsigned char c = s->v[start]; + if (c >= 0x80) { + // If we run into a non-ASCII byte, fall back to the + // slower unicode-aware method on the remaining bytes + return trim_space(s->v, start, s->len); + } + + if (!(c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r' || c == ' ')) + { + break; + } + } + + int stop = s->len; + for (; stop > start; stop--) { + unsigned char c = s->v[stop-1]; + if (c >= 0x80) + { + return trim_space(s->v, start, stop); + } + + if (!(c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r' || c == ' ')) + { + break; + } + } + + char *str = opa_malloc(stop - start + 1); + memcpy(str, &s->v[start], stop - start); + str[stop - start] = '\0'; + return opa_string_allocated(str, stop - start); +} + +OPA_BUILTIN +opa_value *opa_strings_lower(opa_value *a) +{ + if (opa_value_type(a) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + int is_ascii = true; + + for (int i = 0; i < s->len && is_ascii; i++) + { + unsigned char c = s->v[i]; + is_ascii = c < 0x80; + } + + if (is_ascii) + { + char *str = opa_malloc(s->len + 1); + + for (int i = 0; i < s->len; i++) + { + unsigned char c = s->v[i]; + if ('A' <= c && c <= 'Z') + { + c += 'a' - 'A'; + } + + str[i] = c; + } + + str[s->len] = '\0'; + return opa_string_allocated(str, s->len); + } + + int j = 0; + char *out = malloc(s->len + 1); + int cap = s->len; + + for (int i = 0; i < s->len; ) + { + int len; + int cp = opa_unicode_decode_utf8(s->v, i, s->len, &len); + if (cp == -1) + { + opa_abort("string: invalid unicode"); + } + + cp = opa_unicode_to_lower(cp); + if (cp == -1) + { + opa_abort("string: invalid unicode"); + } + + if (cap < (j + 4)) // Space for the longest possible UTF-8 character (4 bytes). + { + cap *= 2; + + if (cap < (j + 4)) + { + cap = j + 4; + } + + out = opa_realloc(out, cap + 1); + } + + j += opa_unicode_encode_utf8(cp, &out[j]); + i += len; + } + + out[j] = '\0'; + return opa_string_allocated(out, j); +} + +OPA_BUILTIN +opa_value *opa_strings_upper(opa_value *a) +{ + if (opa_value_type(a) != OPA_STRING) + { + return NULL; + } + + opa_string_t *s = opa_cast_string(a); + int is_ascii = true; + + for (int i = 0; i < s->len && is_ascii; i++) + { + unsigned char c = s->v[i]; + is_ascii = c < 0x80; + } + + if (is_ascii) + { + char *str = opa_malloc(s->len + 1); + + for (int i = 0; i < s->len; i++) + { + unsigned char c = s->v[i]; + if ('a' <= c && c <= 'z') + { + c -= 'a' - 'A'; + } + + str[i] = c; + } + + str[s->len] = '\0'; + return opa_string_allocated(str, s->len); + } + + int j = 0; + char *out = malloc(s->len + 1); + int cap = s->len; + + for (int i = 0; i < s->len; ) + { + int len; + int cp = opa_unicode_decode_utf8(s->v, i, s->len, &len); + if (cp == -1) + { + opa_abort("string: invalid unicode"); + } + + cp = opa_unicode_to_upper(cp); + if (cp == -1) + { + opa_abort("string: invalid unicode"); + } + + if (cap < (j + 4)) // Space for the longest possible UTF-8 character (4 bytes). + { + cap *= 2; + + if (cap < (j + 4)) + { + cap = j + 4; + } + + out = opa_realloc(out, cap + 1); + } + + j += opa_unicode_encode_utf8(cp, &out[j]); + i += len; + } + + out[j] = '\0'; + return opa_string_allocated(out, j); +} diff --git a/third_party/opa/wasm/src/strings.h b/third_party/opa/wasm/src/strings.h new file mode 100644 index 000000000000..19c08a1a24ee --- /dev/null +++ b/third_party/opa/wasm/src/strings.h @@ -0,0 +1,28 @@ +#ifndef OPA_STRINGS_H +#define OPA_STRINGS_H + +#include "value.h" + +opa_value *opa_strings_any_prefix_match(opa_value *a, opa_value *b); +opa_value *opa_strings_any_suffix_match(opa_value *a, opa_value *b); +opa_value *opa_strings_concat(opa_value *a, opa_value *b); +opa_value *opa_strings_contains(opa_value *a, opa_value *b); +opa_value *opa_strings_endswith(opa_value *a, opa_value *b); +opa_value *opa_strings_format_int(opa_value *a, opa_value *b); +opa_value *opa_strings_indexof(opa_value *a, opa_value *b); +opa_value *opa_strings_lower(opa_value *a); +opa_value *opa_strings_replace(opa_value *a, opa_value *b, opa_value *c); +opa_value *opa_strings_replace_n(opa_value *a, opa_value *b); +opa_value *opa_strings_reverse(opa_value *a); +opa_value *opa_strings_split(opa_value *a, opa_value *b); +opa_value *opa_strings_startswith(opa_value *a, opa_value *b); +opa_value *opa_strings_substring(opa_value *a, opa_value *b, opa_value *c); +opa_value *opa_strings_trim(opa_value *a, opa_value *b); +opa_value *opa_strings_trim_left(opa_value *a, opa_value *b); +opa_value *opa_strings_trim_prefix(opa_value *a, opa_value *b); +opa_value *opa_strings_trim_right(opa_value *a, opa_value *b); +opa_value *opa_strings_trim_suffix(opa_value *a, opa_value *b); +opa_value *opa_strings_trim_space(opa_value *a); +opa_value *opa_strings_upper(opa_value *a); + +#endif diff --git a/third_party/opa/wasm/src/types.c b/third_party/opa/wasm/src/types.c new file mode 100644 index 000000000000..6614d77d59dc --- /dev/null +++ b/third_party/opa/wasm/src/types.c @@ -0,0 +1,68 @@ +#include "std.h" +#include "types.h" + +OPA_BUILTIN +opa_value *opa_types_is_number(opa_value *v) +{ + return opa_boolean(opa_value_type(v) == OPA_NUMBER); +} + +OPA_BUILTIN +opa_value *opa_types_is_string(opa_value *v) +{ + return opa_boolean(opa_value_type(v) == OPA_STRING); +} + +OPA_BUILTIN +opa_value *opa_types_is_boolean(opa_value *v) +{ + return opa_boolean(opa_value_type(v) == OPA_BOOLEAN); +} + +OPA_BUILTIN +opa_value *opa_types_is_array(opa_value *v) +{ + return opa_boolean(opa_value_type(v) == OPA_ARRAY); +} + +OPA_BUILTIN +opa_value *opa_types_is_set(opa_value *v) +{ + return opa_boolean(opa_value_type(v) == OPA_SET); +} + +OPA_BUILTIN +opa_value *opa_types_is_object(opa_value *v) +{ + return opa_boolean(opa_value_type(v) == OPA_OBJECT); +} + +OPA_BUILTIN +opa_value *opa_types_is_null(opa_value *v) +{ + return opa_boolean(opa_value_type(v) == OPA_NULL); +} + +OPA_BUILTIN +opa_value *opa_types_name(opa_value *v) +{ + switch (opa_value_type(v)) + { + case OPA_NULL: + return opa_string("null", 4); + case OPA_BOOLEAN: + return opa_string("boolean", 7); + case OPA_NUMBER: + return opa_string("number", 6); + case OPA_STRING: + return opa_string("string", 6); + case OPA_ARRAY: + return opa_string("array", 5); + case OPA_OBJECT: + return opa_string("object", 6); + case OPA_SET: + return opa_string("set", 3); + default: + return NULL; + } +} diff --git a/third_party/opa/wasm/src/types.h b/third_party/opa/wasm/src/types.h new file mode 100644 index 000000000000..18fbbf929429 --- /dev/null +++ b/third_party/opa/wasm/src/types.h @@ -0,0 +1,15 @@ +#ifndef OPA_TYPES_H +#define OPA_TYPES_H + +#include "value.h" + +opa_value *opa_types_is_number(opa_value *v); +opa_value *opa_types_is_string(opa_value *v); +opa_value *opa_types_is_boolean(opa_value *v); +opa_value *opa_types_is_array(opa_value *v); +opa_value *opa_types_is_set(opa_value *v); +opa_value *opa_types_is_object(opa_value *v); +opa_value *opa_types_is_null(opa_value *v); +opa_value *opa_types_name(opa_value *v); + +#endif diff --git a/third_party/opa/wasm/src/undefined.symbols b/third_party/opa/wasm/src/undefined.symbols new file mode 100644 index 000000000000..8ed7e08cef4c --- /dev/null +++ b/third_party/opa/wasm/src/undefined.symbols @@ -0,0 +1,6 @@ +opa_println +opa_builtin0 +opa_builtin1 +opa_builtin2 +opa_builtin3 +opa_builtin4 diff --git a/third_party/opa/wasm/src/unicode.c b/third_party/opa/wasm/src/unicode.c new file mode 100644 index 000000000000..a72b37d6f730 --- /dev/null +++ b/third_party/opa/wasm/src/unicode.c @@ -0,0 +1,667 @@ +#include "unicode.h" + +#include +#include +#include "std.h" + +// Tests whether the code point is an utf-16 surrogate (encoded +// representation of low or high bits). +bool opa_unicode_surrogate(int codepoint) +{ + return 0xd800 <= codepoint && codepoint < 0xe000; +} + +// Reads the unicode UTF-16 code unit \uXXXX escaping. +int opa_unicode_decode_unit(const char *in, int i, int len) +{ + if (i+6 > len) + { + return -1; + } + + if (in[i] != '\\' || in[i+1] != 'u') + { + return -1; + } + + int codepoint = 0; + + for (int j = i+2; j < (i+6); j++) + { + char next = in[j]; + + if ( '0' <= next && next <= '9') { + next = next - '0'; + } else if ('a' <= next && next <= 'f') { + next = next - 'a' + 10; + } else if ('A' <= next && next <= 'F') { + next = next - 'A' + 10; + } else { + return -1; + } + + codepoint = codepoint * 16 + (int)next; + } + + return codepoint; +} + +// Translates an utf-16 surrogate pair to a code point. +int opa_unicode_decode_surrogate(int codepoint1, int codepoint2) +{ + if (!opa_unicode_surrogate(codepoint1) || !opa_unicode_surrogate(codepoint2)) + { + return 0xfffd; // replacement char + } + + return (codepoint1 - 0xd800) << 10 | (codepoint2 - 0xdc00) + 0x10000; +} + +// Decodes UTF-8 character to a code point. +int opa_unicode_decode_utf8(const char *in, int i, int len, int *olen) +{ + if (i >= len) + { + return -1; + } + + // For details, see https://en.wikipedia.org/wiki/UTF-8 and + // https://lemire.me/blog/2018/05/09/how-quickly-can-you-check-that-a-string-is-valid-unicode-utf-8/ + + unsigned char c0 = in[i]; + if ((c0 & 0b10000000) == 0) + { + // 1 byte UTF-8 character. + *olen = 1; + return (int)c0; + } + + if ((c0 & 0b11100000) == 0b11000000) + { + // 2 byte UTF-8 character. + if ((i+1) >= len) + { + return -1; + } + + // 0xc0 and 0xc1 are illegal UTF-8 first bytes, considered + // overlong encodings. + if (c0 == 0xc0 || c0 == 0xc1) + { + return -1; + } + + unsigned char c1 = in[i+1]; + if (!(c1 >= 0x80 && c1 <= 0xbf)) + { + return -1; + } + + *olen = 2; + return (int)(c0 & 0b00011111) << 6 | (int)(c1 & 0b00111111); + } + + if ((c0 & 0b11110000) == 0b11100000) + { + // 3 byte UTF-8 character. + if ((i+2) >= len) + { + return -1; + } + + unsigned char c1 = in[i+1]; + unsigned char c2 = in[i+2]; + + if (!((c0 == 0xe0 && c1 >= 0xa0 && c1 <= 0xbf && c2 >= 0x80 && c2 <= 0xbf) || + (c0 >= 0xe1 && c0 <= 0xec && c1 >= 0x80 && c1 <= 0xbf && c2 >= 0x80 && c2 <= 0xbf) || + (c0 == 0xed && c1 >= 0x80 && c1 <= 0x9f && c2 >= 0x80 && c2 <= 0xbf) || + (c0 >= 0xee && c0 <= 0xef && c1 >= 0x80 && c1 <= 0xbf && c2 >= 0x80 && c2 <= 0xbf))) + { + return -1; + } + + *olen = 3; + return (int)(c0 & 0b00001111) << 12 | (int)(c1 & 0b00111111) << 6 | (int)(c2 & 0b00111111); + } + + if ((c0 & 0b11111000) == 0b11110000) + { + // 4 byte UTF-8 character. + if ((i+3) >= len) + { + return -1; + } + + unsigned char c1 = in[i+1]; + unsigned char c2 = in[i+2]; + unsigned char c3 = in[i+3]; + + if (!((c0 == 0xf0 && c1 >= 0x90 && c1 <= 0xbf && c2 >= 0x80 && c2 <= 0xbf && c3 >= 0x80 && c3 <= 0xbf) || + (c0 >= 0xf1 && c0 <= 0xf3 && c1 >= 0x80 && c1 <= 0xbf && c2 >= 0x80 && c2 <= 0xbf && c3 >= 0x80 && c3 <= 0xbf) || + (c0 == 0xf4 && c1 >= 0x80 && c1 <= 0x8f && c2 >= 0x80 && c2 <= 0xbf && c3 >= 0x80 && c3 <= 0xbf))) + { + return -1; + } + + *olen = 4; + return (int)(c0 & 0b00000111) << 18 | (int)(c1 & 0b00111111) << 12 | (int)(c2 & 0b00111111) << 6 | (int)(c3 & 0b00111111); + } + + return -1; +} + +// Writes the code point as UTF-8. +int opa_unicode_encode_utf8(int codepoint, char *out) +{ + size_t i = (size_t)codepoint; + + if (i <= ((1<<7) - 1)) + { + out[0] = i; + return 1; + } + + if (i <= ((1<<11) - 1)) + { + out[0] = 0b11000000 | (i >> 6); + out[1] = 0b10000000 | (i & 0b00111111); + return 2; + } + + if (i <= ((1<<16) - 1)) + { + out[0] = 0b11100000 | (i >> 12); + out[1] = 0b10000000 | ((i >> 6) & 0b00111111); + out[2] = 0b10000000 | (i & 0b00111111); + return 3; + } + + out[0] = 0b11110000 | (i >> 18); + out[1] = 0b10000000 | ((i >> 12) & 0b00111111); + out[2] = 0b10000000 | ((i >> 6) & 0b00111111); + out[3] = 0b10000000 | (i & 0b00111111); + return 4; +} + +// Returns the index to the last UTF-8 code point. +int opa_unicode_last_utf8(const char *in, int start, int end) +{ + if (start >= end || end == 0) + { + return -1; + } + + int i = end - 1; + unsigned char c = in[i]; + if (c < 0x80) + { + return i; + } + + for (i = i - 1; i >= start && i >= end - 4; i--) // UTF-8 character is at most 4 bytes. + { + c = in[i]; + if ((c & 0xc0) != 0x80) // Bytes after the first have always the two bits set to 10. + { + break; + } + } + + if (i < start) { + i = start; + } + + return i; +} + +// The following tables mirror the implementation of golang unicode +// white space detection and case conversion routines. + +typedef struct { + uint16_t lo; + uint16_t hi; + uint16_t stride; +} range16_t; + +// unicode white spaces expressed as ranges, per +// https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt. +const static range16_t white_spaces[] = { + {0x0009, 0x000d, 1}, + {0x0020, 0x0085, 101}, + {0x00a0, 0x1680, 5600}, + {0x2000, 0x200a, 1}, + {0x2028, 0x2029, 1}, + {0x202f, 0x205f, 48}, + {0x3000, 0x3000, 1}, +}; + +// is16 reports whether codepoint is in the sorted slice of 16-bit ranges. +bool is16(const range16_t *ranges, int n, uint16_t cp) +{ + for (int i = 0; i < n; i++) + { + if (cp < ranges[i].lo) + { + return false; + } + + if (cp <= ranges[i].hi) + { + return ranges[i].stride == 1 || (cp-ranges[i].lo)%ranges[i].stride == 0; + } + } + + return false; +} + +// is returns true if the codepoint is in the range table. +static bool is(const range16_t *r16, int l16, int cp) +{ + if (l16 > 0 && cp <= r16[l16-1].hi) + { + return is16(r16, l16, (uint16_t)cp); + } + + // TODO: Check for 32-bit ranges here, if such tables needed. + // TODO: For any future larger tables, implement binary search. + + return false; +} + +// Returns true if the codepoint is a whitespace. +bool opa_unicode_is_space(int cp) +{ + if (cp <= 0xff) { // Latin1 + return cp == '\t' || cp == '\n' || cp == '\v' || cp == '\f' || cp == '\r' || cp == ' ' || cp == 0x85 || cp == 0xa0; + } + + return is(white_spaces, sizeof(white_spaces) / sizeof(range16_t), cp); +} + +typedef struct { + uint32_t lo; + uint32_t hi; + int d[3]; // delta +} case_range_t; + +// Indices to d(elta) above. +#define UPPER_CASE 0 +#define LOWER_CASE 1 +#define TITLE_CASE 2 + +// These are the case conversion ranges from golang 1.14 (unicode +// 12.0.0). + +static const int upper_lower = 0x10ffff + 1; +static const case_range_t case_ranges[] = { + {0x0041, 0x005A, {0, 32, 0}}, + {0x0061, 0x007A, {-32, 0, -32}}, + {0x00B5, 0x00B5, {743, 0, 743}}, + {0x00C0, 0x00D6, {0, 32, 0}}, + {0x00D8, 0x00DE, {0, 32, 0}}, + {0x00E0, 0x00F6, {-32, 0, -32}}, + {0x00F8, 0x00FE, {-32, 0, -32}}, + {0x00FF, 0x00FF, {121, 0, 121}}, + {0x0100, 0x012F, {upper_lower, upper_lower, upper_lower}}, + {0x0130, 0x0130, {0, -199, 0}}, + {0x0131, 0x0131, {-232, 0, -232}}, + {0x0132, 0x0137, {upper_lower, upper_lower, upper_lower}}, + {0x0139, 0x0148, {upper_lower, upper_lower, upper_lower}}, + {0x014A, 0x0177, {upper_lower, upper_lower, upper_lower}}, + {0x0178, 0x0178, {0, -121, 0}}, + {0x0179, 0x017E, {upper_lower, upper_lower, upper_lower}}, + {0x017F, 0x017F, {-300, 0, -300}}, + {0x0180, 0x0180, {195, 0, 195}}, + {0x0181, 0x0181, {0, 210, 0}}, + {0x0182, 0x0185, {upper_lower, upper_lower, upper_lower}}, + {0x0186, 0x0186, {0, 206, 0}}, + {0x0187, 0x0188, {upper_lower, upper_lower, upper_lower}}, + {0x0189, 0x018A, {0, 205, 0}}, + {0x018B, 0x018C, {upper_lower, upper_lower, upper_lower}}, + {0x018E, 0x018E, {0, 79, 0}}, + {0x018F, 0x018F, {0, 202, 0}}, + {0x0190, 0x0190, {0, 203, 0}}, + {0x0191, 0x0192, {upper_lower, upper_lower, upper_lower}}, + {0x0193, 0x0193, {0, 205, 0}}, + {0x0194, 0x0194, {0, 207, 0}}, + {0x0195, 0x0195, {97, 0, 97}}, + {0x0196, 0x0196, {0, 211, 0}}, + {0x0197, 0x0197, {0, 209, 0}}, + {0x0198, 0x0199, {upper_lower, upper_lower, upper_lower}}, + {0x019A, 0x019A, {163, 0, 163}}, + {0x019C, 0x019C, {0, 211, 0}}, + {0x019D, 0x019D, {0, 213, 0}}, + {0x019E, 0x019E, {130, 0, 130}}, + {0x019F, 0x019F, {0, 214, 0}}, + {0x01A0, 0x01A5, {upper_lower, upper_lower, upper_lower}}, + {0x01A6, 0x01A6, {0, 218, 0}}, + {0x01A7, 0x01A8, {upper_lower, upper_lower, upper_lower}}, + {0x01A9, 0x01A9, {0, 218, 0}}, + {0x01AC, 0x01AD, {upper_lower, upper_lower, upper_lower}}, + {0x01AE, 0x01AE, {0, 218, 0}}, + {0x01AF, 0x01B0, {upper_lower, upper_lower, upper_lower}}, + {0x01B1, 0x01B2, {0, 217, 0}}, + {0x01B3, 0x01B6, {upper_lower, upper_lower, upper_lower}}, + {0x01B7, 0x01B7, {0, 219, 0}}, + {0x01B8, 0x01B9, {upper_lower, upper_lower, upper_lower}}, + {0x01BC, 0x01BD, {upper_lower, upper_lower, upper_lower}}, + {0x01BF, 0x01BF, {56, 0, 56}}, + {0x01C4, 0x01C4, {0, 2, 1}}, + {0x01C5, 0x01C5, {-1, 1, 0}}, + {0x01C6, 0x01C6, {-2, 0, -1}}, + {0x01C7, 0x01C7, {0, 2, 1}}, + {0x01C8, 0x01C8, {-1, 1, 0}}, + {0x01C9, 0x01C9, {-2, 0, -1}}, + {0x01CA, 0x01CA, {0, 2, 1}}, + {0x01CB, 0x01CB, {-1, 1, 0}}, + {0x01CC, 0x01CC, {-2, 0, -1}}, + {0x01CD, 0x01DC, {upper_lower, upper_lower, upper_lower}}, + {0x01DD, 0x01DD, {-79, 0, -79}}, + {0x01DE, 0x01EF, {upper_lower, upper_lower, upper_lower}}, + {0x01F1, 0x01F1, {0, 2, 1}}, + {0x01F2, 0x01F2, {-1, 1, 0}}, + {0x01F3, 0x01F3, {-2, 0, -1}}, + {0x01F4, 0x01F5, {upper_lower, upper_lower, upper_lower}}, + {0x01F6, 0x01F6, {0, -97, 0}}, + {0x01F7, 0x01F7, {0, -56, 0}}, + {0x01F8, 0x021F, {upper_lower, upper_lower, upper_lower}}, + {0x0220, 0x0220, {0, -130, 0}}, + {0x0222, 0x0233, {upper_lower, upper_lower, upper_lower}}, + {0x023A, 0x023A, {0, 10795, 0}}, + {0x023B, 0x023C, {upper_lower, upper_lower, upper_lower}}, + {0x023D, 0x023D, {0, -163, 0}}, + {0x023E, 0x023E, {0, 10792, 0}}, + {0x023F, 0x0240, {10815, 0, 10815}}, + {0x0241, 0x0242, {upper_lower, upper_lower, upper_lower}}, + {0x0243, 0x0243, {0, -195, 0}}, + {0x0244, 0x0244, {0, 69, 0}}, + {0x0245, 0x0245, {0, 71, 0}}, + {0x0246, 0x024F, {upper_lower, upper_lower, upper_lower}}, + {0x0250, 0x0250, {10783, 0, 10783}}, + {0x0251, 0x0251, {10780, 0, 10780}}, + {0x0252, 0x0252, {10782, 0, 10782}}, + {0x0253, 0x0253, {-210, 0, -210}}, + {0x0254, 0x0254, {-206, 0, -206}}, + {0x0256, 0x0257, {-205, 0, -205}}, + {0x0259, 0x0259, {-202, 0, -202}}, + {0x025B, 0x025B, {-203, 0, -203}}, + {0x025C, 0x025C, {42319, 0, 42319}}, + {0x0260, 0x0260, {-205, 0, -205}}, + {0x0261, 0x0261, {42315, 0, 42315}}, + {0x0263, 0x0263, {-207, 0, -207}}, + {0x0265, 0x0265, {42280, 0, 42280}}, + {0x0266, 0x0266, {42308, 0, 42308}}, + {0x0268, 0x0268, {-209, 0, -209}}, + {0x0269, 0x0269, {-211, 0, -211}}, + {0x026A, 0x026A, {42308, 0, 42308}}, + {0x026B, 0x026B, {10743, 0, 10743}}, + {0x026C, 0x026C, {42305, 0, 42305}}, + {0x026F, 0x026F, {-211, 0, -211}}, + {0x0271, 0x0271, {10749, 0, 10749}}, + {0x0272, 0x0272, {-213, 0, -213}}, + {0x0275, 0x0275, {-214, 0, -214}}, + {0x027D, 0x027D, {10727, 0, 10727}}, + {0x0280, 0x0280, {-218, 0, -218}}, + {0x0282, 0x0282, {42307, 0, 42307}}, + {0x0283, 0x0283, {-218, 0, -218}}, + {0x0287, 0x0287, {42282, 0, 42282}}, + {0x0288, 0x0288, {-218, 0, -218}}, + {0x0289, 0x0289, {-69, 0, -69}}, + {0x028A, 0x028B, {-217, 0, -217}}, + {0x028C, 0x028C, {-71, 0, -71}}, + {0x0292, 0x0292, {-219, 0, -219}}, + {0x029D, 0x029D, {42261, 0, 42261}}, + {0x029E, 0x029E, {42258, 0, 42258}}, + {0x0345, 0x0345, {84, 0, 84}}, + {0x0370, 0x0373, {upper_lower, upper_lower, upper_lower}}, + {0x0376, 0x0377, {upper_lower, upper_lower, upper_lower}}, + {0x037B, 0x037D, {130, 0, 130}}, + {0x037F, 0x037F, {0, 116, 0}}, + {0x0386, 0x0386, {0, 38, 0}}, + {0x0388, 0x038A, {0, 37, 0}}, + {0x038C, 0x038C, {0, 64, 0}}, + {0x038E, 0x038F, {0, 63, 0}}, + {0x0391, 0x03A1, {0, 32, 0}}, + {0x03A3, 0x03AB, {0, 32, 0}}, + {0x03AC, 0x03AC, {-38, 0, -38}}, + {0x03AD, 0x03AF, {-37, 0, -37}}, + {0x03B1, 0x03C1, {-32, 0, -32}}, + {0x03C2, 0x03C2, {-31, 0, -31}}, + {0x03C3, 0x03CB, {-32, 0, -32}}, + {0x03CC, 0x03CC, {-64, 0, -64}}, + {0x03CD, 0x03CE, {-63, 0, -63}}, + {0x03CF, 0x03CF, {0, 8, 0}}, + {0x03D0, 0x03D0, {-62, 0, -62}}, + {0x03D1, 0x03D1, {-57, 0, -57}}, + {0x03D5, 0x03D5, {-47, 0, -47}}, + {0x03D6, 0x03D6, {-54, 0, -54}}, + {0x03D7, 0x03D7, {-8, 0, -8}}, + {0x03D8, 0x03EF, {upper_lower, upper_lower, upper_lower}}, + {0x03F0, 0x03F0, {-86, 0, -86}}, + {0x03F1, 0x03F1, {-80, 0, -80}}, + {0x03F2, 0x03F2, {7, 0, 7}}, + {0x03F3, 0x03F3, {-116, 0, -116}}, + {0x03F4, 0x03F4, {0, -60, 0}}, + {0x03F5, 0x03F5, {-96, 0, -96}}, + {0x03F7, 0x03F8, {upper_lower, upper_lower, upper_lower}}, + {0x03F9, 0x03F9, {0, -7, 0}}, + {0x03FA, 0x03FB, {upper_lower, upper_lower, upper_lower}}, + {0x03FD, 0x03FF, {0, -130, 0}}, + {0x0400, 0x040F, {0, 80, 0}}, + {0x0410, 0x042F, {0, 32, 0}}, + {0x0430, 0x044F, {-32, 0, -32}}, + {0x0450, 0x045F, {-80, 0, -80}}, + {0x0460, 0x0481, {upper_lower, upper_lower, upper_lower}}, + {0x048A, 0x04BF, {upper_lower, upper_lower, upper_lower}}, + {0x04C0, 0x04C0, {0, 15, 0}}, + {0x04C1, 0x04CE, {upper_lower, upper_lower, upper_lower}}, + {0x04CF, 0x04CF, {-15, 0, -15}}, + {0x04D0, 0x052F, {upper_lower, upper_lower, upper_lower}}, + {0x0531, 0x0556, {0, 48, 0}}, + {0x0561, 0x0586, {-48, 0, -48}}, + {0x10A0, 0x10C5, {0, 7264, 0}}, + {0x10C7, 0x10C7, {0, 7264, 0}}, + {0x10CD, 0x10CD, {0, 7264, 0}}, + {0x10D0, 0x10FA, {3008, 0, 0}}, + {0x10FD, 0x10FF, {3008, 0, 0}}, + {0x13A0, 0x13EF, {0, 38864, 0}}, + {0x13F0, 0x13F5, {0, 8, 0}}, + {0x13F8, 0x13FD, {-8, 0, -8}}, + {0x1C80, 0x1C80, {-6254, 0, -6254}}, + {0x1C81, 0x1C81, {-6253, 0, -6253}}, + {0x1C82, 0x1C82, {-6244, 0, -6244}}, + {0x1C83, 0x1C84, {-6242, 0, -6242}}, + {0x1C85, 0x1C85, {-6243, 0, -6243}}, + {0x1C86, 0x1C86, {-6236, 0, -6236}}, + {0x1C87, 0x1C87, {-6181, 0, -6181}}, + {0x1C88, 0x1C88, {35266, 0, 35266}}, + {0x1C90, 0x1CBA, {0, -3008, 0}}, + {0x1CBD, 0x1CBF, {0, -3008, 0}}, + {0x1D79, 0x1D79, {35332, 0, 35332}}, + {0x1D7D, 0x1D7D, {3814, 0, 3814}}, + {0x1D8E, 0x1D8E, {35384, 0, 35384}}, + {0x1E00, 0x1E95, {upper_lower, upper_lower, upper_lower}}, + {0x1E9B, 0x1E9B, {-59, 0, -59}}, + {0x1E9E, 0x1E9E, {0, -7615, 0}}, + {0x1EA0, 0x1EFF, {upper_lower, upper_lower, upper_lower}}, + {0x1F00, 0x1F07, {8, 0, 8}}, + {0x1F08, 0x1F0F, {0, -8, 0}}, + {0x1F10, 0x1F15, {8, 0, 8}}, + {0x1F18, 0x1F1D, {0, -8, 0}}, + {0x1F20, 0x1F27, {8, 0, 8}}, + {0x1F28, 0x1F2F, {0, -8, 0}}, + {0x1F30, 0x1F37, {8, 0, 8}}, + {0x1F38, 0x1F3F, {0, -8, 0}}, + {0x1F40, 0x1F45, {8, 0, 8}}, + {0x1F48, 0x1F4D, {0, -8, 0}}, + {0x1F51, 0x1F51, {8, 0, 8}}, + {0x1F53, 0x1F53, {8, 0, 8}}, + {0x1F55, 0x1F55, {8, 0, 8}}, + {0x1F57, 0x1F57, {8, 0, 8}}, + {0x1F59, 0x1F59, {0, -8, 0}}, + {0x1F5B, 0x1F5B, {0, -8, 0}}, + {0x1F5D, 0x1F5D, {0, -8, 0}}, + {0x1F5F, 0x1F5F, {0, -8, 0}}, + {0x1F60, 0x1F67, {8, 0, 8}}, + {0x1F68, 0x1F6F, {0, -8, 0}}, + {0x1F70, 0x1F71, {74, 0, 74}}, + {0x1F72, 0x1F75, {86, 0, 86}}, + {0x1F76, 0x1F77, {100, 0, 100}}, + {0x1F78, 0x1F79, {128, 0, 128}}, + {0x1F7A, 0x1F7B, {112, 0, 112}}, + {0x1F7C, 0x1F7D, {126, 0, 126}}, + {0x1F80, 0x1F87, {8, 0, 8}}, + {0x1F88, 0x1F8F, {0, -8, 0}}, + {0x1F90, 0x1F97, {8, 0, 8}}, + {0x1F98, 0x1F9F, {0, -8, 0}}, + {0x1FA0, 0x1FA7, {8, 0, 8}}, + {0x1FA8, 0x1FAF, {0, -8, 0}}, + {0x1FB0, 0x1FB1, {8, 0, 8}}, + {0x1FB3, 0x1FB3, {9, 0, 9}}, + {0x1FB8, 0x1FB9, {0, -8, 0}}, + {0x1FBA, 0x1FBB, {0, -74, 0}}, + {0x1FBC, 0x1FBC, {0, -9, 0}}, + {0x1FBE, 0x1FBE, {-7205, 0, -7205}}, + {0x1FC3, 0x1FC3, {9, 0, 9}}, + {0x1FC8, 0x1FCB, {0, -86, 0}}, + {0x1FCC, 0x1FCC, {0, -9, 0}}, + {0x1FD0, 0x1FD1, {8, 0, 8}}, + {0x1FD8, 0x1FD9, {0, -8, 0}}, + {0x1FDA, 0x1FDB, {0, -100, 0}}, + {0x1FE0, 0x1FE1, {8, 0, 8}}, + {0x1FE5, 0x1FE5, {7, 0, 7}}, + {0x1FE8, 0x1FE9, {0, -8, 0}}, + {0x1FEA, 0x1FEB, {0, -112, 0}}, + {0x1FEC, 0x1FEC, {0, -7, 0}}, + {0x1FF3, 0x1FF3, {9, 0, 9}}, + {0x1FF8, 0x1FF9, {0, -128, 0}}, + {0x1FFA, 0x1FFB, {0, -126, 0}}, + {0x1FFC, 0x1FFC, {0, -9, 0}}, + {0x2126, 0x2126, {0, -7517, 0}}, + {0x212A, 0x212A, {0, -8383, 0}}, + {0x212B, 0x212B, {0, -8262, 0}}, + {0x2132, 0x2132, {0, 28, 0}}, + {0x214E, 0x214E, {-28, 0, -28}}, + {0x2160, 0x216F, {0, 16, 0}}, + {0x2170, 0x217F, {-16, 0, -16}}, + {0x2183, 0x2184, {upper_lower, upper_lower, upper_lower}}, + {0x24B6, 0x24CF, {0, 26, 0}}, + {0x24D0, 0x24E9, {-26, 0, -26}}, + {0x2C00, 0x2C2E, {0, 48, 0}}, + {0x2C30, 0x2C5E, {-48, 0, -48}}, + {0x2C60, 0x2C61, {upper_lower, upper_lower, upper_lower}}, + {0x2C62, 0x2C62, {0, -10743, 0}}, + {0x2C63, 0x2C63, {0, -3814, 0}}, + {0x2C64, 0x2C64, {0, -10727, 0}}, + {0x2C65, 0x2C65, {-10795, 0, -10795}}, + {0x2C66, 0x2C66, {-10792, 0, -10792}}, + {0x2C67, 0x2C6C, {upper_lower, upper_lower, upper_lower}}, + {0x2C6D, 0x2C6D, {0, -10780, 0}}, + {0x2C6E, 0x2C6E, {0, -10749, 0}}, + {0x2C6F, 0x2C6F, {0, -10783, 0}}, + {0x2C70, 0x2C70, {0, -10782, 0}}, + {0x2C72, 0x2C73, {upper_lower, upper_lower, upper_lower}}, + {0x2C75, 0x2C76, {upper_lower, upper_lower, upper_lower}}, + {0x2C7E, 0x2C7F, {0, -10815, 0}}, + {0x2C80, 0x2CE3, {upper_lower, upper_lower, upper_lower}}, + {0x2CEB, 0x2CEE, {upper_lower, upper_lower, upper_lower}}, + {0x2CF2, 0x2CF3, {upper_lower, upper_lower, upper_lower}}, + {0x2D00, 0x2D25, {-7264, 0, -7264}}, + {0x2D27, 0x2D27, {-7264, 0, -7264}}, + {0x2D2D, 0x2D2D, {-7264, 0, -7264}}, + {0xA640, 0xA66D, {upper_lower, upper_lower, upper_lower}}, + {0xA680, 0xA69B, {upper_lower, upper_lower, upper_lower}}, + {0xA722, 0xA72F, {upper_lower, upper_lower, upper_lower}}, + {0xA732, 0xA76F, {upper_lower, upper_lower, upper_lower}}, + {0xA779, 0xA77C, {upper_lower, upper_lower, upper_lower}}, + {0xA77D, 0xA77D, {0, -35332, 0}}, + {0xA77E, 0xA787, {upper_lower, upper_lower, upper_lower}}, + {0xA78B, 0xA78C, {upper_lower, upper_lower, upper_lower}}, + {0xA78D, 0xA78D, {0, -42280, 0}}, + {0xA790, 0xA793, {upper_lower, upper_lower, upper_lower}}, + {0xA794, 0xA794, {48, 0, 48}}, + {0xA796, 0xA7A9, {upper_lower, upper_lower, upper_lower}}, + {0xA7AA, 0xA7AA, {0, -42308, 0}}, + {0xA7AB, 0xA7AB, {0, -42319, 0}}, + {0xA7AC, 0xA7AC, {0, -42315, 0}}, + {0xA7AD, 0xA7AD, {0, -42305, 0}}, + {0xA7AE, 0xA7AE, {0, -42308, 0}}, + {0xA7B0, 0xA7B0, {0, -42258, 0}}, + {0xA7B1, 0xA7B1, {0, -42282, 0}}, + {0xA7B2, 0xA7B2, {0, -42261, 0}}, + {0xA7B3, 0xA7B3, {0, 928, 0}}, + {0xA7B4, 0xA7BF, {upper_lower, upper_lower, upper_lower}}, + {0xA7C2, 0xA7C3, {upper_lower, upper_lower, upper_lower}}, + {0xA7C4, 0xA7C4, {0, -48, 0}}, + {0xA7C5, 0xA7C5, {0, -42307, 0}}, + {0xA7C6, 0xA7C6, {0, -35384, 0}}, + {0xAB53, 0xAB53, {-928, 0, -928}}, + {0xAB70, 0xABBF, {-38864, 0, -38864}}, + {0xFF21, 0xFF3A, {0, 32, 0}}, + {0xFF41, 0xFF5A, {-32, 0, -32}}, + {0x10400, 0x10427, {0, 40, 0}}, + {0x10428, 0x1044F, {-40, 0, -40}}, + {0x104B0, 0x104D3, {0, 40, 0}}, + {0x104D8, 0x104FB, {-40, 0, -40}}, + {0x10C80, 0x10CB2, {0, 64, 0}}, + {0x10CC0, 0x10CF2, {-64, 0, -64}}, + {0x118A0, 0x118BF, {0, 32, 0}}, + {0x118C0, 0x118DF, {-32, 0, -32}}, + {0x16E40, 0x16E5F, {0, 32, 0}}, + {0x16E60, 0x16E7F, {-32, 0, -32}}, + {0x1E900, 0x1E921, {0, 34, 0}}, + {0x1E922, 0x1E943, {-34, 0, -34}}, +}; + +// convert a codepoint to lower, upper, or title case, by binary +// searching the conversion instruction. +static int to(int case_, uint32_t cp) +{ + const case_range_t *range = case_ranges; + + for (int lo = 0, hi = sizeof(case_ranges) / sizeof(case_range_t); lo < hi; ) + { + int m = lo + (hi-lo) / 2; + + if (range[m].lo <= cp && cp <= range[m].hi) + { + int delta = range[m].d[case_]; + + if (delta > 0x10FFFF) + { + // In an Upper-Lower sequence, which always starts with + // an UpperCase letter, the real deltas always look like: + // {0, 1, 0} UpperCase (Lower is next) + // {-1, 0, -1} LowerCase (Upper, Title are previous) + // The characters at even offsets from the beginning of the + // sequence are upper case; the ones at odd offsets are lower. + // The correct mapping can be done by clearing or setting the low + // bit in the sequence offset. + // The constants UpperCase and TitleCase are even while LowerCase + // is odd so we take the low bit from _case. + return range[m].lo + (((cp-range[m].lo) & ~1) | (case_ & 1)); + } + + return cp + delta; + } + + if (cp < range[m].lo) + { + hi = m; + } else { + lo = m + 1; + } + } + + return cp; +} + +int opa_unicode_to_lower(int codepoint) +{ + return to(LOWER_CASE, codepoint); +} + +int opa_unicode_to_upper(int codepoint) +{ + return to(UPPER_CASE, codepoint); +} diff --git a/third_party/opa/wasm/src/unicode.h b/third_party/opa/wasm/src/unicode.h new file mode 100644 index 000000000000..87f7a4dd46b0 --- /dev/null +++ b/third_party/opa/wasm/src/unicode.h @@ -0,0 +1,24 @@ +#ifndef OPA_UNICODE_H +#define OPA_UNICODE_H + +#include "std.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int opa_unicode_decode_surrogate(int codepoint1, int codepoint2); +int opa_unicode_decode_unit(const char *in, int i, int len); +int opa_unicode_decode_utf8(const char *in, int i, int len, int *olen); +int opa_unicode_encode_utf8(int codepoint, char *out); +bool opa_unicode_is_space(int codepoint); +int opa_unicode_last_utf8(const char *in, int start, int end); +bool opa_unicode_surrogate(int codepoint); +int opa_unicode_to_lower(int codepoint); +int opa_unicode_to_upper(int codepoint); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/src/value.c b/third_party/opa/wasm/src/value.c new file mode 100644 index 000000000000..3ba865d58171 --- /dev/null +++ b/third_party/opa/wasm/src/value.c @@ -0,0 +1,1759 @@ +#include + +#include "json.h" +#include "malloc.h" +#include "mpd.h" +#include "str.h" +#include "value.h" + +#define OPA_ARRAY_INITIAL_CAP (10) +#define OPA_OBJECT_MIN_BUCKETS (8) +#define OPA_OBJECT_LOAD_FACTOR (0.7) +#define OPA_SET_MIN_BUCKETS (8) +#define OPA_SET_LOAD_FACTOR (0.7) + +static opa_value *__opa_object_with_buckets(size_t buckets); +static opa_value *__opa_set_with_buckets(size_t buckets); +static opa_array_t *__opa_set_values(opa_set_t *set); +static void __opa_object_insert_elem(opa_object_t *obj, opa_object_elem_t *new, size_t hash); +static void __opa_set_add_elem(opa_set_t *set, opa_set_elem_t *new, size_t hash); + +static void __opa_free_maybe_bulk(void *ptr, bool bulk) +{ + if (bulk) + opa_free_bulk(ptr); + else + opa_free(ptr); +} + +OPA_INTERNAL +int opa_value_type(opa_value *node) +{ + // For all intents and purposes, interned strings are strings, + // interned booleans are booleans. + // Only opa_value_free and opa_value_shallow_copy handle them + // separately, by referring to node->type directly. + switch (node->type) + { + case OPA_STRING_INTERNED: + return OPA_STRING; + case OPA_BOOLEAN_INTERNED: + return OPA_BOOLEAN; + default: + return node->type; + } +} + +opa_value *opa_value_get_object(opa_object_t *obj, opa_value *key) +{ + opa_object_elem_t *elem = opa_object_get(obj, key); + return elem == NULL ? NULL : elem->v; +} + +opa_value *opa_value_get_set(opa_set_t *set, opa_value *key) +{ + opa_set_elem_t *elem = opa_set_get(set, key); + return elem == NULL ? NULL : elem->v; +} + +opa_value *opa_value_get_array_native(opa_array_t *arr, long long i) +{ + return i >= arr->len ? NULL : arr->elems[i].v; +} + +opa_value *opa_value_get_array(opa_array_t *arr, opa_value *key) +{ + if (key->type != OPA_NUMBER) + { + return NULL; + } + + opa_number_t *num = opa_cast_number(key); + + long long i; + + if (opa_number_try_int(num, &i) != 0) + { + return NULL; + } + + if (i < 0) + { + return NULL; + } + + return opa_value_get_array_native(arr, i); +} + +OPA_INTERNAL +opa_value *opa_value_get(opa_value *node, opa_value *key) +{ + if (node != NULL) + { + switch (node->type) + { + case OPA_ARRAY: + return opa_value_get_array(opa_cast_array(node), key); + case OPA_OBJECT: + return opa_value_get_object(opa_cast_object(node), key); + case OPA_SET: + return opa_value_get_set(opa_cast_set(node), key); + } + } + return NULL; +} + +opa_object_elem_t *__opa_object_next_bucket(opa_object_t *obj, size_t i) +{ + for (; i < obj->n; i++) { + opa_object_elem_t *elem = obj->buckets[i]; + if (elem != NULL) { + return elem; + } + } + + return NULL; +} + +opa_object_elem_t *__opa_object_get_bucket_elem(opa_object_elem_t *bucket, opa_value *key) { + for (opa_object_elem_t *curr = bucket; curr != NULL; curr = curr->next) + { + if (opa_value_compare(curr->k, key) == 0) + { + return curr; + } + } + + return NULL; +} + +opa_value *opa_value_iter_object(opa_object_t *obj, opa_value *prev) +{ + if (prev == NULL) + { + opa_object_elem_t *first = __opa_object_next_bucket(obj, 0); + if (first != NULL) { + return first->k; + } + + return NULL; + } + + size_t i = opa_value_hash(prev) % obj->n; + opa_object_elem_t *elem = __opa_object_get_bucket_elem(obj->buckets[i], prev); + opa_object_elem_t *next = elem->next; + if (next != NULL) { + return next->k; + } + + next = __opa_object_next_bucket(obj, i+1); + if (next != NULL) { + return next->k; + } + + return NULL; +} + +opa_set_elem_t *__opa_set_next_bucket(opa_set_t *set, size_t i) +{ + for (; i < set->n; i++) { + opa_set_elem_t *elem = set->buckets[i]; + if (elem != NULL) { + return elem; + } + } + + return NULL; +} + +opa_set_elem_t *__opa_set_get_bucket_elem(opa_set_elem_t *bucket, opa_value *v) { + for (opa_set_elem_t *curr = bucket; curr != NULL; curr = curr->next) + { + if (opa_value_compare(curr->v, v) == 0) + { + return curr; + } + } + + return NULL; +} + +opa_value *opa_value_iter_set(opa_set_t *set, opa_value *prev) +{ + if (prev == NULL) + { + opa_set_elem_t *first = __opa_set_next_bucket(set, 0); + if (first != NULL) { + return first->v; + } + + return NULL; + } + + size_t i = opa_value_hash(prev) % set->n; + opa_set_elem_t *elem = __opa_set_get_bucket_elem(set->buckets[i], prev); + opa_set_elem_t *next = elem->next; + if (next != NULL) { + return next->v; + } + + next = __opa_set_next_bucket(set, i+1); + if (next != NULL) { + return next->v; + } + + return NULL; +} + +opa_value *opa_value_iter_array(opa_array_t *arr, opa_value *prev) +{ + if (prev == NULL) + { + if (arr->len == 0) + { + return NULL; + } + + return arr->elems[0].i; + } + + if (prev->type != OPA_NUMBER) + { + return NULL; + } + + opa_number_t *num = opa_cast_number(prev); + + long long i; + + if (opa_number_try_int(num, &i) != 0) + { + return NULL; + } + + i++; + + if (i < 0 || i >= arr->len) + { + return NULL; + } + + return arr->elems[i].i; +} + +opa_value *opa_value_iter(opa_value *node, opa_value *prev) +{ + if (node != NULL) + { + switch (node->type) + { + case OPA_ARRAY: + return opa_value_iter_array(opa_cast_array(node), prev); + case OPA_OBJECT: + return opa_value_iter_object(opa_cast_object(node), prev); + case OPA_SET: + return opa_value_iter_set(opa_cast_set(node), prev); + } + } + + return NULL; +} + +size_t opa_value_length_object(opa_object_t *obj) +{ + return obj->len; +} + +size_t opa_value_length_set(opa_set_t *set) +{ + return set->len; +} + +size_t opa_value_length_array(opa_array_t *arr) +{ + return arr->len; +} + +size_t opa_value_length_string(opa_string_t *str) +{ + return str->len; +} + +OPA_INTERNAL +size_t opa_value_length(opa_value *node) +{ + switch (opa_value_type(node)) + { + case OPA_ARRAY: + return opa_value_length_array(opa_cast_array(node)); + case OPA_OBJECT: + return opa_value_length_object(opa_cast_object(node)); + case OPA_SET: + return opa_value_length_set(opa_cast_set(node)); + case OPA_STRING: + return opa_value_length_string(opa_cast_string(node)); + default: + return 0; + } +} + +int opa_value_compare_float(double a, double b) +{ + if (a < b) + { + return -1; + } + else if (a > b) + { + return 1; + } + return 0; +} + +int opa_value_compare_number(opa_number_t *a, opa_number_t *b) +{ + long long la, lb; + + if (opa_number_try_int(a, &la) == 0 && opa_number_try_int(b, &lb) == 0) + { + if (la < lb) + { + return -1; + } + else if (la > lb) + { + return 1; + } + return 0; + } + + mpd_t *ba = opa_number_to_bf(&a->hdr); + mpd_t *bb = opa_number_to_bf(&b->hdr); + + uint32_t status = 0; + int c = mpd_qcmp(ba, bb, &status); + if (status) + { + opa_abort("opa_value_compare_number"); + } + + mpd_del(ba); + mpd_del(bb); + + return c; +} + +int opa_value_compare_string(opa_string_t *a, opa_string_t *b) +{ + size_t min = a->len; + + if (b->len < min) + { + min = b->len; + } + + int cmp = opa_strncmp(a->v, b->v, min); + + if (cmp != 0) + { + return cmp; + } + + if (a->len < b->len) + { + return -1; + } + else if (a->len > b->len) + { + return 1; + } + return 0; +} + +int opa_value_compare_array(opa_array_t *a, opa_array_t *b) +{ + size_t a_len = opa_value_length_array(a); + size_t b_len = opa_value_length_array(b); + + size_t min = a_len; + + if (b_len < min) + { + min = b_len; + } + + for (long long i = 0; i < min; i++) + { + opa_value *e1 = opa_value_get_array_native(a, i); + opa_value *e2 = opa_value_get_array_native(b, i); + int cmp = opa_value_compare(e1, e2); + + if (cmp != 0) + { + return cmp; + } + } + + if (a_len < b_len) + { + return -1; + } + else if (a_len > b_len) + { + return 1; + } + return 0; +} + +int opa_value_compare_object(opa_object_t *a, opa_object_t *b) +{ + opa_array_t *a_keys = opa_object_keys(a); + opa_array_t *b_keys = opa_object_keys(b); + size_t a_len = opa_value_length_array(a_keys); + size_t b_len = opa_value_length_array(b_keys); + size_t min = a_len; + + if (b_len < min) + { + min = b_len; + } + + int cmp; + + for (size_t i = 0; i < min; i++) + { + cmp = opa_value_compare(a_keys->elems[i].v, b_keys->elems[i].v); + + if (cmp != 0) + { + goto finish; + } + + opa_value *a_val = opa_value_get_object(a, a_keys->elems[i].v); + opa_value *b_val = opa_value_get_object(b, b_keys->elems[i].v); + + cmp = opa_value_compare(a_val, b_val); + + if (cmp != 0) + { + goto finish; + } + } + + if (a_len < b_len) + { + return -1; + } + else if (a_len > b_len) + { + return 1; + } + +finish: + opa_array_free(a_keys, false, false); + opa_array_free(b_keys, false, false); + return cmp; +} + +int opa_value_compare_set(opa_set_t *a, opa_set_t *b) +{ + opa_array_t *va = __opa_set_values(a); + opa_array_t *vb = __opa_set_values(b); + + for (size_t i = 0; i < va->len && i < vb->len; i++) + { + int cmp = opa_value_compare(opa_value_get_array_native(va, i), opa_value_get_array_native(vb, i)); + + if (cmp != 0) + { + return cmp; + } + } + + if (va->len < vb->len) { + return -1; + } else if (va->len > vb->len) { + return 1; + } + + return 0; +} + +OPA_INTERNAL +int opa_value_compare(opa_value *a, opa_value *b) +{ + if (a == b) + { + return 0; + } + if (b == NULL) + { + return 1; + } + if (a == NULL) + { + return -1; + } + if (opa_value_type(a) < opa_value_type(b)) + { + return -1; + } + if (opa_value_type(b) < opa_value_type(a)) + { + return 1; + } + + switch (opa_value_type(a)) + { + case OPA_NULL: + return 0; + case OPA_BOOLEAN: + { + opa_boolean_t *a1 = opa_cast_boolean(a); + opa_boolean_t *b1 = opa_cast_boolean(b); + return a1->v - b1->v; + } + case OPA_NUMBER: + { + opa_number_t *a1 = opa_cast_number(a); + opa_number_t *b1 = opa_cast_number(b); + return opa_value_compare_number(a1, b1); + } + case OPA_STRING: + { + opa_string_t *a1 = opa_cast_string(a); + opa_string_t *b1 = opa_cast_string(b); + return opa_value_compare_string(a1, b1); + } + case OPA_ARRAY: + { + opa_array_t *a1 = opa_cast_array(a); + opa_array_t *b1 = opa_cast_array(b); + return opa_value_compare_array(a1, b1); + } + case OPA_OBJECT: + { + opa_object_t *a1 = opa_cast_object(a); + opa_object_t *b1 = opa_cast_object(b); + return opa_value_compare_object(a1, b1); + } + case OPA_SET: + { + opa_set_t *a1 = opa_cast_set(a); + opa_set_t *b1 = opa_cast_set(b); + return opa_value_compare_set(a1, b1); + } + default: + opa_abort("illegal value"); + return 0; + } +} + +#define FNV32_INIT ((size_t)0x811c9dc5) + +static size_t +fnv1a32(size_t hash, const void *input, size_t len) +{ + const unsigned char *data = input; + const unsigned char *end = data + len; + + for (; data != end; ++data) + { + hash += (hash<<1) + (hash<<4) + (hash<<7) + (hash<<8) + (hash<<24); // *= 0x01000193 + hash ^= *data; + } + + return hash; +} + +size_t opa_boolean_hash(opa_boolean_t *b) { + return b->v ? 0 : 1; +} + +size_t opa_number_hash(opa_number_t *n) { + double d = opa_number_as_float(n); + return fnv1a32(FNV32_INIT, &d, sizeof(d)); +} + +size_t opa_string_hash(opa_string_t *s) { + return fnv1a32(FNV32_INIT, s->v, s->len); +} + +size_t opa_array_hash(opa_array_t *a) { + size_t len = opa_value_length_array(a); + size_t hash = 0; + + for (long long i = 0; i < len; i++) + { + hash += opa_value_hash(opa_value_get_array_native(a, i)); + } + + return hash; +} + +size_t opa_object_hash(opa_object_t *o) { + size_t hash = 0; + + for (int i = 0; i < o->n; i++) + { + opa_object_elem_t *elem = o->buckets[i]; + + while (elem != NULL) + { + hash += opa_value_hash(elem->k); + hash += opa_value_hash(elem->v); + elem = elem->next; + } + } + + return hash; +} + +size_t opa_set_hash(opa_set_t *o) { + size_t hash = 0; + + for (int i = 0; i < o->n; i++) + { + opa_set_elem_t *elem = o->buckets[i]; + + while (elem != NULL) + { + hash += opa_value_hash(elem->v); + elem = elem->next; + } + } + + return hash; +} + +size_t opa_value_hash(opa_value *node) { + switch (opa_value_type(node)) + { + case OPA_NULL: + return 0; + case OPA_BOOLEAN: + return opa_boolean_hash(opa_cast_boolean(node)); + case OPA_NUMBER: + return opa_number_hash(opa_cast_number(node)); + case OPA_STRING: + return opa_string_hash(opa_cast_string(node)); + case OPA_ARRAY: + return opa_array_hash(opa_cast_array(node)); + case OPA_OBJECT: + return opa_object_hash(opa_cast_object(node)); + case OPA_SET: + return opa_set_hash(opa_cast_set(node)); + } + + return 0; +} + +OPA_INTERNAL +void __opa_value_free(opa_value *node, bool deep, bool bulk) +{ + switch (node->type) // bypass opa_value_type: don't free OPA_STRING_INTERNED + { + case OPA_NULL: + __opa_free_maybe_bulk(node, bulk); + return; + case OPA_BOOLEAN: + __opa_free_maybe_bulk(opa_cast_boolean(node), bulk); + return; + case OPA_NUMBER: + opa_number_free(opa_cast_number(node), bulk); + return; + case OPA_STRING: + opa_string_free(opa_cast_string(node), bulk); + return; + case OPA_ARRAY: + opa_array_free(opa_cast_array(node), deep, bulk); + return; + case OPA_OBJECT: + opa_object_free(opa_cast_object(node), deep, bulk); + return; + case OPA_SET: + opa_set_free(opa_cast_set(node), deep, bulk); + return; + } +} + +OPA_INTERNAL +WASM_EXPORT(opa_value_free) +void opa_value_free(opa_value *node) +{ + __opa_value_free(node, true, false); +} + +OPA_INTERNAL +void opa_value_free_shallow(opa_value *node) +{ + __opa_value_free(node, false, false); +} + +OPA_INTERNAL +opa_value *opa_value_merge(opa_value *a, opa_value *b) +{ + if (a == NULL) + { + return b; + } + if (opa_value_type(a) != OPA_OBJECT || opa_value_type(b) != OPA_OBJECT) + { + return a; + } + + opa_object_t *obj = opa_cast_object(a); + opa_object_t *result = opa_cast_object(opa_object()); + + for (int i = 0; i < obj->n; i++) + { + opa_object_elem_t *elem = obj->buckets[i]; + + while (elem != NULL) + { + opa_value *other = opa_value_get(b, elem->k); + + if (other == NULL) + { + opa_object_insert(result, elem->k, elem->v); + } + else + { + opa_value *merged = opa_value_merge(elem->v, other); + + if (merged == NULL) + { + return NULL; + } + + opa_object_insert(result, elem->k, merged); + } + + elem = elem->next; + } + } + + obj = opa_cast_object(b); + for (int i = 0; i < obj->n; i++) + { + opa_object_elem_t *elem = obj->buckets[i]; + + while (elem != NULL) + { + opa_value *other = opa_value_get(a, elem->k); + + if (other == NULL) + { + opa_object_insert(result, elem->k, elem->v); + } + + elem = elem->next; + } + } + + return &result->hdr; +} + +opa_value *opa_value_shallow_copy_boolean(opa_boolean_t *b) +{ + return opa_boolean(b->v); +} + +opa_value *opa_value_shallow_copy_number(opa_number_t *n) +{ + switch (n->repr) + { + case OPA_NUMBER_REPR_REF: + return opa_number_ref(n->v.ref.s, n->v.ref.len); + case OPA_NUMBER_REPR_INT: + return opa_number_int(n->v.i); + default: + opa_abort("opa_value_shallow_copy_number: illegal repr"); + return NULL; + } +} + +opa_value *opa_value_shallow_copy_string(opa_string_t *s) +{ + return opa_string(s->v, s->len); +} + +opa_value *opa_value_shallow_copy_array(opa_array_t *a) +{ + opa_array_elem_t *cpy = (opa_array_elem_t *)opa_malloc(sizeof(opa_array_elem_t) * a->cap); + + for (size_t idx = 0; idx < a->cap; idx++) + { + cpy[idx] = a->elems[idx]; + } + + return opa_array_with_elems(cpy, a->len, a->cap); +} + +opa_value *opa_value_shallow_copy_object(opa_object_t *o) +{ + opa_value *node = &o->hdr; + opa_object_t *cpy = opa_cast_object(__opa_object_with_buckets(o->n)); + opa_value *prev = NULL; + opa_value *curr = NULL; + + while ((curr = opa_value_iter(node, prev)) != NULL) + { + opa_value *v = opa_value_get(node, curr); + opa_object_insert(cpy, curr, v); + prev = curr; + } + + return &cpy->hdr; +} + +opa_value *opa_value_shallow_copy_set(opa_set_t *s) +{ + opa_value *node = &s->hdr; + opa_set_t *cpy = opa_cast_set(__opa_set_with_buckets(s->n)); + opa_value *prev = NULL; + opa_value *curr = NULL; + + while ((curr = opa_value_iter(node, prev)) != NULL) + { + opa_set_add(cpy, curr); + prev = curr; + } + + return &cpy->hdr; +} + +opa_value *opa_value_shallow_copy(opa_value *node) +{ + switch (node->type) // bypass opa_value_type: pass OPA_STRING_INTERNED along as-is + { + case OPA_NULL: + return node; + case OPA_BOOLEAN: + return opa_value_shallow_copy_boolean(opa_cast_boolean(node)); + case OPA_NUMBER: + return opa_value_shallow_copy_number(opa_cast_number(node)); + case OPA_STRING: + return opa_value_shallow_copy_string(opa_cast_string(node)); + case OPA_ARRAY: + return opa_value_shallow_copy_array(opa_cast_array(node)); + case OPA_OBJECT: + return opa_value_shallow_copy_object(opa_cast_object(node)); + case OPA_SET: + return opa_value_shallow_copy_set(opa_cast_set(node)); + case OPA_STRING_INTERNED: + case OPA_BOOLEAN_INTERNED: + return node; + } + + return NULL; +} + +static opa_value *__opa_tuple(opa_value *a, opa_value *b) +{ + opa_value *ret = opa_array_with_cap(2); + opa_array_t *tuple = opa_cast_array(ret); + opa_array_append(tuple, a); + opa_array_append(tuple, b); + return ret; +} + +static void __opa_value_transitive_closure(opa_array_t *result, opa_array_t *path, opa_value *node) +{ + opa_array_append(result, __opa_tuple(&path->hdr, node)); + opa_value *prev = NULL; + opa_value *curr = NULL; + + while ((curr = opa_value_iter(node, prev)) != NULL) + { + opa_array_t *cpy = opa_cast_array(opa_value_shallow_copy_array(path)); + opa_array_append(cpy, curr); + opa_value *child = opa_value_get(node, curr); + __opa_value_transitive_closure(result, cpy, child); + prev = curr; + } +} + +OPA_BUILTIN +opa_value *opa_value_transitive_closure(opa_value *v) +{ + opa_array_t *result = opa_cast_array(opa_array()); + opa_array_t *path = opa_cast_array(opa_array()); + __opa_value_transitive_closure(result, path, v); + return &result->hdr; +} + + +OPA_INTERNAL +opa_value *opa_null() +{ + opa_value *ret = (opa_value *)opa_malloc(sizeof(opa_value)); + ret->type = OPA_NULL; + return ret; +} + +opa_value *opa_boolean_allocated(bool v) +{ + opa_boolean_t *ret = (opa_boolean_t *)opa_malloc(sizeof(opa_boolean_t)); + ret->hdr.type = OPA_BOOLEAN; + ret->v = v; + return &ret->hdr; +} + +opa_value *opa_boolean(bool v) +{ + return opa_boolean_allocated(v); +} + +OPA_INTERNAL +opa_value *opa_number_size(size_t v) +{ + opa_number_t *ret = (opa_number_t *)opa_malloc(sizeof(opa_number_t)); + ret->hdr.type = OPA_NUMBER; + ret->repr = OPA_NUMBER_REPR_INT; + ret->v.i = (long long)v; + return &ret->hdr; +} + +OPA_INTERNAL +opa_value *opa_number_int(long long v) +{ + opa_number_t *ret = (opa_number_t *)opa_malloc(sizeof(opa_number_t)); + ret->hdr.type = OPA_NUMBER; + ret->repr = OPA_NUMBER_REPR_INT; + ret->v.i = v; + return &ret->hdr; +} + +OPA_INTERNAL +opa_value *opa_number_ref(const char *s, size_t len) +{ + opa_number_t *ret = (opa_number_t *)opa_malloc(sizeof(opa_number_t)); + ret->hdr.type = OPA_NUMBER; + ret->repr = OPA_NUMBER_REPR_REF; + ret->v.ref.s = s; + ret->v.ref.len = len; + ret->v.ref.free = 0; + return &ret->hdr; +} + +opa_value *opa_number_ref_allocated(const char *s, size_t len) +{ + opa_number_t *ret = (opa_number_t *)opa_malloc(sizeof(opa_number_t)); + ret->hdr.type = OPA_NUMBER; + ret->repr = OPA_NUMBER_REPR_REF; + ret->v.ref.s = s; + ret->v.ref.len = len; + ret->v.ref.free = 1; + return &ret->hdr; +} + +void opa_number_init_int(opa_number_t *n, long long v) +{ + n->hdr.type = OPA_NUMBER; + n->repr = OPA_NUMBER_REPR_INT; + n->v.i = v; +} + +void opa_number_free(opa_number_t *n, bool bulk) +{ + if (n->repr == OPA_NUMBER_REPR_REF) + { + if (n->v.ref.free) + { + __opa_free_maybe_bulk((void *)n->v.ref.s, bulk); + } + } + + __opa_free_maybe_bulk(n, bulk); +} + +int opa_number_try_int(opa_number_t *n, long long *i) +{ + switch (n->repr) + { + case OPA_NUMBER_REPR_INT: + *i = n->v.i; + return 0; + case OPA_NUMBER_REPR_REF: + if (opa_atoi64(n->v.ref.s, n->v.ref.len, i) == 0) + { + if (*i == LLONG_MIN || *i == LLONG_MAX) + { + return -1; + } + return 0; + } + return -1; + default: + opa_abort("opa_number_try_int: illegal repr"); + return -1; + } +} + +double opa_number_as_float(opa_number_t *n) +{ + switch (n->repr) + { + case OPA_NUMBER_REPR_INT: + return (double)n->v.i; + case OPA_NUMBER_REPR_REF: + { + double d; + int rc = opa_atof64(n->v.ref.s, n->v.ref.len, &d); + if (rc != 0) + { + opa_abort("opa_number_as_float: illegal ref"); + } + return d; + } + default: + opa_abort("opa_number_as_float: illegal repr"); + return 0.0; + } +} + +opa_value *opa_string(const char *v, size_t len) +{ + opa_string_t *ret = (opa_string_t *)opa_malloc(sizeof(opa_string_t)); + ret->hdr.type = OPA_STRING; + ret->free = 0; + ret->len = len; + ret->v = v; + return &ret->hdr; +} + +OPA_INTERNAL +opa_value *opa_string_terminated(const char *v) +{ + opa_string_t *ret = (opa_string_t *)opa_malloc(sizeof(opa_string_t)); + ret->hdr.type = OPA_STRING; + ret->free = 0; + ret->len = opa_strlen(v); + ret->v = v; + return &ret->hdr; +} + +opa_value *opa_string_allocated(const char *v, size_t len) +{ + opa_string_t *ret = (opa_string_t *)opa_malloc(sizeof(opa_string_t)); + ret->hdr.type = OPA_STRING; + ret->free = 1; + ret->len = len; + ret->v = v; + return &ret->hdr; +} + +void opa_string_free(opa_string_t *s, bool bulk) +{ + if (s->free) + { + __opa_free_maybe_bulk((void *)s->v, bulk); + } + + __opa_free_maybe_bulk(s, bulk); +} + +void __opa_array_grow(opa_array_t *arr) +{ + if (arr->cap == 0) + { + arr->cap = OPA_ARRAY_INITIAL_CAP; + } + else + { + arr->cap *= 2; + } + + opa_array_elem_t *elems = (opa_array_elem_t *)opa_malloc(arr->cap * sizeof(opa_array_elem_t)); + + for (int i = 0; i < arr->len; i++) + { + elems[i] = arr->elems[i]; + } + + if (arr->elems != NULL) + { + opa_free(arr->elems); + } + arr->elems = elems; +} + +opa_value *opa_array() +{ + return opa_array_with_cap(0); +} + +OPA_INTERNAL +opa_value *opa_array_with_cap(size_t cap) +{ + opa_array_t *ret = (opa_array_t *)opa_malloc(sizeof(opa_array_t)); + ret->hdr.type = OPA_ARRAY; + ret->len = 0; + ret->cap = cap; + ret->elems = NULL; + + if (ret->cap != 0) + { + __opa_array_grow(ret); + } + + return &ret->hdr; +} + +opa_value *opa_array_with_elems(opa_array_elem_t *elems, size_t len, size_t cap) +{ + opa_array_t *ret = (opa_array_t *)opa_malloc(sizeof(opa_array_t)); + + ret->hdr.type = OPA_ARRAY; + ret->len = len; + ret->cap = cap; + ret->elems = elems; + + return &ret->hdr; +} + +static opa_value *__opa_object_with_buckets(size_t buckets) +{ + opa_object_t *ret = (opa_object_t *)opa_malloc(sizeof(opa_object_t)); + ret->hdr.type = OPA_OBJECT; + ret->buckets = (opa_object_elem_t **)opa_malloc(sizeof(opa_object_elem_t *) * buckets); + ret->n = buckets; + ret->len = 0; + + for (size_t i = 0; i < buckets; i++) { + ret->buckets[i] = NULL; + } + + return &ret->hdr; +} + +opa_value *opa_object() +{ + return __opa_object_with_buckets(OPA_OBJECT_MIN_BUCKETS); +} + +static opa_value *__opa_set_with_buckets(size_t buckets) +{ + opa_set_t *ret = (opa_set_t *)opa_malloc(sizeof(opa_set_t)); + ret->hdr.type = OPA_SET; + ret->buckets = (opa_set_elem_t **)opa_malloc(sizeof(opa_set_elem_t *) * buckets); + ret->n = buckets; + ret->len = 0; + + for (size_t i = 0; i < buckets; i++) { + ret->buckets[i] = NULL; + } + + return &ret->hdr; +} + +OPA_INTERNAL +opa_value *opa_set() +{ + return __opa_set_with_buckets(OPA_SET_MIN_BUCKETS); +} + +opa_value *opa_set_with_cap(size_t n) +{ + size_t buckets = OPA_SET_MIN_BUCKETS; + + while (n > (buckets * OPA_SET_LOAD_FACTOR)) + { + buckets *= 2; + } + + return __opa_set_with_buckets(buckets); +} + +OPA_INTERNAL +void opa_value_number_set_int(opa_value *v, long long i) +{ + opa_number_t *ret = opa_cast_number(v); + ret->repr = OPA_NUMBER_REPR_INT; + ret->v.i = i; +} + +void opa_array_free(opa_array_t *arr, bool deep, bool bulk) +{ + if (arr->elems != NULL) + { + for (size_t i = 0; i < arr->len; i++) + { + if (deep) { + __opa_value_free(arr->elems[i].i, deep, bulk); + __opa_value_free(arr->elems[i].v, deep, bulk); + } else { + __opa_free_maybe_bulk(arr->elems[i].i, bulk); + } + } + + __opa_free_maybe_bulk(arr->elems, bulk); + } + + __opa_free_maybe_bulk(arr, bulk); +} + +OPA_INTERNAL +void opa_array_append(opa_array_t *arr, opa_value *v) +{ + if (arr->len >= arr->cap) + { + __opa_array_grow(arr); + } + + size_t i = arr->len++; + arr->elems[i].i = opa_number_int(i); + arr->elems[i].v = v; +} + +void opa_array_sort(opa_array_t *arr, opa_compare_fn cmp_fn) +{ + for (size_t i = 1; i < arr->len; i++) + { + opa_value *elem = arr->elems[i].v; + size_t j = i - 1; + + while (j >= 0 && cmp_fn(arr->elems[j].v, elem) > 0) + { + arr->elems[j + 1].v = arr->elems[j].v; + j = j - 1; + } + + arr->elems[j + 1].v = elem; + } +} + +void __opa_object_buckets_free(opa_object_t *obj, bool deep, bool bulk) +{ + for (int i = 0; i < obj->n; i++) + { + opa_object_elem_t *prev = NULL; + + for (opa_object_elem_t *curr = obj->buckets[i]; curr != NULL; curr = curr->next) + { + if (prev != NULL) + { + if (deep) { + __opa_value_free(prev->k, deep, bulk); + __opa_value_free(prev->v, deep, bulk); + } + __opa_free_maybe_bulk(prev, bulk); + } + + prev = curr; + } + + if (prev != NULL) + { + if (deep) { + __opa_value_free(prev->k, deep, bulk); + __opa_value_free(prev->v, deep, bulk); + } + __opa_free_maybe_bulk(prev, bulk); + } + } + + __opa_free_maybe_bulk(obj->buckets, bulk); +} + +void opa_object_free(opa_object_t *obj, bool deep, bool bulk) +{ + __opa_object_buckets_free(obj, deep, bulk); + __opa_free_maybe_bulk(obj, bulk); +} + +opa_array_t *opa_object_keys(opa_object_t *obj) +{ + opa_array_t *keys = opa_cast_array(opa_array_with_cap(opa_value_length_object(obj))); + + for (int i = 0; i < obj->n; i++) + { + opa_object_elem_t *elem = obj->buckets[i]; + + while (elem != NULL) + { + opa_array_append(keys, elem->k); + elem = elem->next; + } + } + + opa_array_sort(keys, opa_value_compare); + return keys; +} + +opa_object_elem_t *__opa_object_elem_alloc(opa_value *k, opa_value *v) +{ + opa_object_elem_t *elem = (opa_object_elem_t *)opa_malloc(sizeof(opa_object_elem_t)); + elem->next = NULL; + elem->k = k; + elem->v = v; + return elem; +} + +void __opa_object_grow(opa_object_t *obj, size_t n) { + if (n <= (obj->n * OPA_OBJECT_LOAD_FACTOR)) + { + return; + } + + opa_object_t *dst = opa_cast_object(__opa_object_with_buckets(obj->n * 2)); + + for (int i = 0; i < obj->n; i++) + { + opa_object_elem_t *elem = obj->buckets[i]; + + while (elem != NULL) + { + opa_object_elem_t *next = elem->next; + __opa_object_insert_elem(dst, elem, opa_value_hash(elem->k)); + elem = next; + } + } + + opa_free(obj->buckets); + obj->buckets = dst->buckets; + obj->n = dst->n; + opa_free(dst); +} + +OPA_INTERNAL +void __opa_object_insert(opa_object_t *obj, opa_value *k, opa_value *v, + int free_dup_key) +{ + size_t hash = opa_value_hash(k); + + for (opa_object_elem_t *curr = obj->buckets[hash % obj->n]; curr != NULL; curr = curr->next) + { + if (opa_value_compare(curr->k, k) == 0) + { + if (free_dup_key) + opa_value_free(k); + curr->v = v; + return; + } + } + + __opa_object_grow(obj, obj->len+1); + __opa_object_insert_elem(obj, __opa_object_elem_alloc(k, v), hash); +} + +OPA_INTERNAL +void opa_object_insert(opa_object_t *obj, opa_value *k, opa_value *v) +{ + __opa_object_insert(obj, k, v, 0); +} + +static void __opa_object_insert_elem(opa_object_t *obj, opa_object_elem_t *new, size_t hash) +{ + size_t i = hash % obj->n; + opa_object_elem_t **prev = &obj->buckets[i]; + opa_object_elem_t *curr = obj->buckets[i]; + + while (1) { + if (curr == NULL || opa_value_compare(new->k, curr->k) < 0) { + *prev = new; + new->next = curr; + break; + } + + prev = &curr->next; + curr = curr->next; + } + + obj->len++; +} + +void opa_object_remove(opa_object_t *obj, opa_value *k, bool bulk) +{ + size_t hash = opa_value_hash(k); + + size_t i = hash % obj->n; + opa_object_elem_t **prev = &obj->buckets[i]; + opa_object_elem_t *curr = obj->buckets[i]; + while (curr != NULL) + { + if (opa_value_compare(curr->k, k) == 0) + { + *prev = curr->next; + obj->len--; + + __opa_value_free(curr->k, true, bulk); + __opa_value_free(curr->v, true, bulk); + __opa_free_maybe_bulk(curr, bulk); + + // TODO: Consider shrinking the object size. For now it will remain + // with its current size. + + return; + } + prev = &curr->next; + curr = curr->next; + } + return; // Key wasn't found, consider it deleted. +} + +opa_object_elem_t *opa_object_get(opa_object_t *obj, opa_value *key) +{ + size_t hash = opa_value_hash(key) % obj->n; + + for (opa_object_elem_t *curr = obj->buckets[hash]; curr != NULL; curr = curr->next) + { + if (opa_value_compare(curr->k, key) == 0) + { + return curr; + } + } + + return NULL; +} + +void __opa_set_buckets_free(opa_set_t *set, bool deep, bool bulk) +{ + for (int i = 0; i < set->n; i++) + { + opa_set_elem_t *prev = NULL; + + for (opa_set_elem_t *curr = set->buckets[i]; curr != NULL; curr = curr->next) + { + if (prev != NULL) + { + if (deep) + __opa_value_free(prev->v, deep, bulk); + __opa_free_maybe_bulk(prev, bulk); + } + + prev = curr; + } + + if (prev != NULL) + { + if (deep) + __opa_value_free(prev->v, deep, bulk); + __opa_free_maybe_bulk(prev, bulk); + } + } + + __opa_free_maybe_bulk(set->buckets, bulk); +} + +void opa_set_free(opa_set_t *set, bool deep, bool bulk) +{ + __opa_set_buckets_free(set, deep, bulk); + __opa_free_maybe_bulk(set, bulk); +} + +opa_array_t *__opa_set_values(opa_set_t *set) +{ + opa_array_t *values = opa_cast_array(opa_array_with_cap(opa_value_length_set(set))); + + for (int i = 0; i < set->n; i++) + { + opa_set_elem_t *elem = set->buckets[i]; + + while (elem != NULL) + { + opa_array_append(values, elem->v); + elem = elem->next; + } + } + + opa_array_sort(values, opa_value_compare); + return values; +} + +opa_set_elem_t *__opa_set_elem_alloc(opa_value *v) +{ + opa_set_elem_t *elem = (opa_set_elem_t *)opa_malloc(sizeof(opa_set_elem_t)); + elem->next = NULL; + elem->v = v; + return elem; +} + +void __opa_set_grow(opa_set_t *set, size_t n) { + if (n <= (set->n * OPA_SET_LOAD_FACTOR)) + { + return; + } + + opa_set_t *dst = opa_cast_set(__opa_set_with_buckets(set->n * 2)); + + for (int i = 0; i < set->n; i++) + { + opa_set_elem_t *elem = set->buckets[i]; + + while (elem != NULL) + { + opa_set_elem_t *next = elem->next; + __opa_set_add_elem(dst, elem, opa_value_hash(elem->v)); + elem = next; + } + } + + opa_free(set->buckets); + set->buckets = dst->buckets; + set->n = dst->n; + opa_free(dst); +} + +OPA_INTERNAL +void opa_set_add(opa_set_t *set, opa_value *v) +{ + size_t hash = opa_value_hash(v); + + for (opa_set_elem_t *curr = set->buckets[hash % set->n]; curr != NULL; curr = curr->next) + { + if (opa_value_compare(curr->v, v) == 0) + { + return; + } + } + + __opa_set_grow(set, set->len+1); + __opa_set_add_elem(set, __opa_set_elem_alloc(v), hash); +} + +static void __opa_set_add_elem(opa_set_t *set, opa_set_elem_t *new, size_t hash) +{ + size_t i = hash % set->n; + opa_set_elem_t **prev = &set->buckets[i]; + opa_set_elem_t *curr = set->buckets[i]; + + while (1) { + if (curr == NULL || opa_value_compare(new->v, curr->v) < 0) { + *prev = new; + new->next = curr; + break; + } + + prev = &curr->next; + curr = curr->next; + } + + set->len++; +} + +opa_set_elem_t *opa_set_get(opa_set_t *set, opa_value *v) +{ + size_t hash = opa_value_hash(v) % set->n; + + for (opa_set_elem_t *curr = set->buckets[hash]; curr != NULL; curr = curr->next) + { + if (opa_value_compare(curr->v, v) == 0) + { + return curr; + } + } + + return NULL; +} + +// Validate that a path is non-null, an array value type, +// has a length >0, and only contains strings. +// Returns the size of the path, or -1 for an invalid path +int _validate_json_path(opa_value *path) +{ + if (path == NULL || opa_value_type(path) != OPA_ARRAY) + { + return -1; + } + + int path_len = opa_value_length(path); + if (path_len == 0) + { + return -1; + } + + for (int i = 0; i < path_len-1; i++) + { + opa_value *v = opa_value_get_array_native(opa_cast_array(path), i); + if (opa_value_type(v) != OPA_STRING) + { + return -1; + } + } + + return path_len; +} + +OPA_INTERNAL +opa_value *opa_string_copy(opa_string_t *src) +{ + char *s = (char *)opa_malloc(src->len); + + for (int i = 0; i < src->len; i++) + { + s[i] = src->v[i]; + } + + return opa_string_allocated(s, src->len); +} + +// For the given `data` value set the provided value `v` at +// the specified `path`. Requires objects for containers, +// any portion of the path that is missing will be created. +// The `path` must be an `opa_array_t` with at least one +// element. +// +// Replaced objects will be freed. +WASM_EXPORT(opa_value_add_path) +opa_errc opa_value_add_path(opa_value *data, opa_value *path, opa_value *v) +{ + int path_len = _validate_json_path(path); + + if (path_len < 1) + { + return OPA_ERR_INVALID_PATH; + } + + // Follow the path, creating objects as needed. + opa_array_t *p = opa_cast_array(path); + opa_value *curr = data; + + for (int i = 0; i < path_len-1; i++) + { + opa_value *k = opa_value_get_array_native(p, i); + + opa_value *next = opa_value_get(curr, k); + + if (next == NULL) + { + switch (curr->type) + { + case OPA_OBJECT: + next = opa_object(); + /* REMOVE (left in for readability) + opa_object_insert(opa_cast_object(curr), k, next); + */ + __opa_object_insert(opa_cast_object(curr), + opa_string_copy(opa_cast_string(k)), + next, 1); + break; + default: + return OPA_ERR_INVALID_TYPE; + } + } + + curr = next; + } + + opa_value *k = opa_value_get_array_native(p, path_len-1); + + opa_value *old = opa_value_get(curr, k); + + switch (curr->type) + { + case OPA_OBJECT: + /* REMOVE (left in for readability) + opa_object_insert(opa_cast_object(curr), k, v); + */ + __opa_object_insert(opa_cast_object(curr), + opa_string_copy(opa_cast_string(k)), v, 1); + break; + default: + return OPA_ERR_INVALID_TYPE; + } + + if (old != NULL) + { + __opa_value_free(old, true, true); + } + + return OPA_ERR_OK; +} + +// For the given `data` object delete the entry specified by `path`. +// The `path` must be an `opa_array_t` with at least one +// element. +// +// Deleted values will be freed. +WASM_EXPORT(opa_value_remove_path) +opa_errc opa_value_remove_path(opa_value *data, opa_value *path) +{ + int path_len = _validate_json_path(path); + + if (path_len < 1) + { + return OPA_ERR_INVALID_PATH; + } + + // Follow the path into data + opa_array_t *p = opa_cast_array(path); + opa_value *curr = data; + + for (int i = 0; i < path_len-1; i++) + { + opa_value *k = opa_value_get_array_native(p, i); + + opa_value *next = opa_value_get(curr, k); + + if (next == NULL) + { + // We were unable to follow the full + // path, consider the target deleted + return OPA_ERR_OK; + } + + curr = next; + } + + opa_object_remove(opa_cast_object(curr), opa_value_get_array_native(p, path_len-1), true); + + return OPA_ERR_OK; +} + +// Lookup path in the passed mapping object. Returns 0 if it can't +// be found, or of there's no function index leaf when we've run out +// of path pieces. +int opa_lookup(opa_value *mapping, opa_value *path) { + if (path == NULL || opa_value_type(path) != OPA_ARRAY) + { + return 0; + } + + int path_len = opa_value_length(path); + if (path_len == 0) + { + return 0; + } + + opa_value *curr = mapping; + + for (opa_value *idx = opa_value_iter(path, NULL); idx != NULL; idx = opa_value_iter(path, idx)) + { + opa_value *key = opa_value_get(path, idx); + opa_value *next = opa_value_get(curr, key); + if (next == NULL) + { + return 0; + } + curr = next; + } + if (curr->type == OPA_NUMBER) { + long long i; + if (opa_number_try_int(opa_cast_number(curr), &i) == 0) { + return i; + } + } + return 0; +} + +// global variable used for storing the parsed mapping JSON +static opa_value *mapping; + +// Called from the WASM-generated '_initialize' function with the +// address of the mapping string and its length. Parses the JSON +// string it expects, sets the *mapping variable accordingly. +OPA_INTERNAL +void opa_mapping_init(const char *s, const int l) { + if (mapping == NULL) { + mapping = opa_json_parse(s, l); + } +} + +// Lookup mapped function index from global mapping (initialized by +// opa_mapping_init). +OPA_INTERNAL +int opa_mapping_lookup(opa_value *path) { + return opa_lookup(mapping, path); +} diff --git a/third_party/opa/wasm/src/value.h b/third_party/opa/wasm/src/value.h new file mode 100644 index 000000000000..0e39acdef8e4 --- /dev/null +++ b/third_party/opa/wasm/src/value.h @@ -0,0 +1,181 @@ +#ifndef OPA_VALUE_H +#define OPA_VALUE_H + +#include + +#include "std.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define OPA_NULL (1) +#define OPA_BOOLEAN (2) +#define OPA_NUMBER (3) +#define OPA_STRING (4) +#define OPA_ARRAY (5) +#define OPA_OBJECT (6) +#define OPA_SET (7) +#define OPA_STRING_INTERNED (8) +#define OPA_BOOLEAN_INTERNED (9) // TODO(sr): make an "interned" bitmask? + +#define OPA_NUMBER_REPR_INT (1) +#define OPA_NUMBER_REPR_REF (2) + +typedef struct opa_value opa_value; + +struct opa_value +{ + unsigned char type; +}; + +typedef struct +{ + opa_value hdr; + bool v; +} opa_boolean_t; + +typedef struct +{ + const char *s; + size_t len; + unsigned char free; // if set 's' is not a reference and should be freed +} opa_number_ref_t; + +typedef struct +{ + opa_value hdr; + unsigned char repr; + union { + long long i; + opa_number_ref_t ref; + } v; +} opa_number_t; + +typedef struct +{ + opa_value hdr; + unsigned char free; // if set 'v' is not a reference and should be freed + size_t len; + const char *v; +} opa_string_t; + +typedef struct +{ + opa_value *i; + opa_value *v; +} opa_array_elem_t; + +typedef struct +{ + opa_value hdr; + opa_array_elem_t *elems; + size_t len; + size_t cap; +} opa_array_t; + +typedef struct opa_object_elem_t opa_object_elem_t; + +struct opa_object_elem_t +{ + opa_value *k; + opa_value *v; + opa_object_elem_t *next; +}; + +typedef struct +{ + opa_value hdr; + opa_object_elem_t **buckets; + size_t n; + size_t len; +} opa_object_t; + +typedef struct opa_set_elem_t opa_set_elem_t; + +struct opa_set_elem_t +{ + opa_value *v; + opa_set_elem_t *next; +}; + +typedef struct +{ + opa_value hdr; + opa_set_elem_t **buckets; + size_t n; + size_t len; +} opa_set_t; + +typedef int (*opa_compare_fn)(opa_value *, opa_value *t); + +#define opa_cast_boolean(v) container_of(v, opa_boolean_t, hdr) +#define opa_cast_number(v) container_of(v, opa_number_t, hdr) +#define opa_cast_string(v) container_of(v, opa_string_t, hdr) +#define opa_cast_array(v) container_of(v, opa_array_t, hdr) +#define opa_cast_object(v) container_of(v, opa_object_t, hdr) +#define opa_cast_set(v) container_of(v, opa_set_t, hdr) + +int opa_value_type(opa_value *node); +int opa_value_compare(opa_value *a, opa_value *b); +size_t opa_value_hash(opa_value *node); +opa_value *opa_value_get(opa_value *node, opa_value *key); +opa_value *opa_value_iter(opa_value *node, opa_value *prev); +size_t opa_value_length(opa_value *node); +void opa_value_free(opa_value *node); +void opa_value_free_shallow(opa_value *node); +opa_value *opa_value_merge(opa_value *a, opa_value *b); +opa_value *opa_value_shallow_copy(opa_value *node); +opa_value *opa_value_transitive_closure(opa_value *node); +opa_errc opa_value_add_path(opa_value *data, opa_value *path, opa_value *v); +opa_errc opa_value_remove_path(opa_value *data, opa_value *path); + +opa_value *opa_null(); +opa_value *opa_boolean(bool v); +opa_value *opa_number_size(size_t v); +opa_value *opa_number_int(long long v); +opa_value *opa_number_float(double v); +opa_value *opa_number_ref(const char *s, size_t len); +opa_value *opa_number_ref_allocated(const char *s, size_t len); +void opa_number_init_int(opa_number_t *n, long long v); +opa_value *opa_string(const char *v, size_t len); +opa_value *opa_string_terminated(const char *v); +opa_value *opa_string_allocated(const char *v, size_t len); +opa_value *opa_array(); +opa_value *opa_array_with_cap(size_t cap); +opa_value *opa_array_with_elems(opa_array_elem_t *elems, size_t len, size_t cap); +opa_value *opa_object(); +opa_value *opa_set(); +opa_value *opa_set_with_cap(size_t cap); + +void opa_value_number_set_int(opa_value *v, long long i); + +int opa_number_try_int(opa_number_t *n, long long *i); +double opa_number_as_float(opa_number_t *n); +void opa_number_free(opa_number_t *n, bool bulk); + +void opa_string_free(opa_string_t *s, bool bulk); + +void opa_array_free(opa_array_t *arr, bool deep, bool bulk); +void opa_array_append(opa_array_t *arr, opa_value *v); +void opa_array_sort(opa_array_t *arr, opa_compare_fn cmp_fn); + +void opa_object_free(opa_object_t *obj, bool deep, bool bulk); +opa_array_t *opa_object_keys(opa_object_t *obj); +void opa_object_insert(opa_object_t *obj, opa_value *k, opa_value *v); +void opa_object_remove(opa_object_t *obj, opa_value *k, bool bulk); +opa_object_elem_t *opa_object_get(opa_object_t *obj, opa_value *key); + +void opa_set_free(opa_set_t *set, bool deep, bool bulk); +void opa_set_add(opa_set_t *set, opa_value *v); +opa_set_elem_t *opa_set_get(opa_set_t *set, opa_value *v); + +int opa_lookup(opa_value *mapping, opa_value *path); +int opa_mapping_lookup(opa_value *path); +void opa_mapping_init(const char *s, const int l); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/test.js b/third_party/opa/wasm/test.js new file mode 100644 index 000000000000..2a09e52d979f --- /dev/null +++ b/third_party/opa/wasm/test.js @@ -0,0 +1,124 @@ +const { readFileSync } = require('fs'); + +function stringDecoder(mem) { + return function(addr) { + const i8 = new Int8Array(mem.buffer); + var s = ""; + while (i8[addr] != 0) { + s += String.fromCharCode(i8[addr++]); + } + return s; + } +} + +function red(text) { + return '\x1b[0m\x1b[31m' + text + '\x1b[0m'; +} + +function green(text) { + return '\x1b[0m\x1b[32m' + text + '\x1b[0m'; +} + +function yellow(text) { + return '\x1b[0m\x1b[33m' + text + '\x1b[0m'; +} + +function namespace(cache, func, note) { + let key = func + note; + if (key in cache) { + cache[key] += 1; + note = note + ' (' + cache[key] + ')' + } else { + cache[key] = 0; + } + return note; +} + +function report(passed, error, msg) { + if (passed === true) { + if (process.env.VERBOSE === '1') { + console.log(green('PASS'), msg); + } + } else if (error === undefined) { + console.log(yellow('FAIL'), msg); + } else { + console.log(red('ERROR'), msg, error); + } +} + +async function test(executable) { + + const memory = new WebAssembly.Memory({ initial: 3 }); + let addr2string; + let cache = {}; + let failedOrErrored = 0; + let seenFuncs = {}; + + const module = await WebAssembly.instantiate(readFileSync(executable), { + env: { + memory, + opa_builtin0: () => 0, + opa_builtin1: () => 0, + opa_builtin2: () => 0, + opa_builtin3: () => 0, + opa_builtin4: () => 0, + opa_println: (msg) => { + console.log(addr2string(msg)); + }, + opa_abort: (msg) => { + throw 'abort: ' + addr2string(msg); + }, + opa_test_pass: (note, func) => { + note = addr2string(note); + func = addr2string(func); + note = namespace(cache, func, note); + seenFuncs[func] = true; + let key = func + '/' + note + report(true, undefined, key); + }, + opa_test_fail: (note, func, file, line) => { + note = addr2string(note); + func = addr2string(func); + note = namespace(cache, func, note); + seenFuncs[func] = true; + let key = func + '/' + note; + failedOrErrored++; + report(false, undefined, key + ' ' + addr2string(file) + ':' + line); + }, + } + }); + + addr2string = stringDecoder(module.instance.exports.memory); + + for (let key in module.instance.exports) { + if (key.startsWith("test_")) { + try { + module.instance.exports[key](); + if (!(key in seenFuncs)) { + report(true, undefined, key); + } + } catch (e) { + report(false, e, key) + } + } + } + + // NOTE(sr): seenFuncs will not contain all tests run, but only those that + // actually call opa_test_{pass,fail}. However, if it's empty, something is + // definitely wrong. + if (Object.keys(seenFuncs).length == 0) { + console.log(red('ERROR'), "no tests executed"); + process.exit(2); + } + + if (failedOrErrored > 0) { + process.exit(1); + } +} + +if (process.argv.length != 3) { + console.log(process.argv[1] + " "); + process.exit(1); +} + +test(process.argv[2]); diff --git a/third_party/opa/wasm/tests/test-glob.cc b/third_party/opa/wasm/tests/test-glob.cc new file mode 100644 index 000000000000..c4075ff29088 --- /dev/null +++ b/third_party/opa/wasm/tests/test-glob.cc @@ -0,0 +1,276 @@ +#include + +#include "glob-compiler.h" +#include "glob-lexer.h" +#include "glob-parser.h" +#include "malloc.h" +#include "test.h" +#include "re2/re2.h" +#include "std.h" +#include "glob.h" + +WASM_EXPORT(test_glob_cache) +extern "C" +void test_glob_cache() +{ + // Reset the cache + opa_builtin_cache_set(0, NULL); + + for (int i = 0; i < 100; i++) + { + char pattern[20]; + snprintf(pattern, sizeof(pattern), "foo/%d/*", i); + opa_glob_match(opa_string_terminated(pattern), opa_null(), opa_string_terminated("foo/bar")); + } + + std::unordered_map* c = static_cast*>(opa_builtin_cache_get(1)); + + test("glob cache size", c->size() == 100); + + opa_glob_match(opa_string_terminated("bar/*"), opa_null(), opa_string_terminated("bar/baz")); + + test("glob cache size doesn't surpass max", c->size() == 100); +} + +// The following is a re-implementation of tests in +// https://github.com/gobwas/glob/blob. +// +// The MIT License (MIT) +// +// Copyright (c) 2016 Sergey Kamardin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +WASM_EXPORT(test_glob_lexer) +extern "C" +void test_glob_lexer() +{ +#define TEST(test_case, pattern, ...) { \ + token expected[] = {__VA_ARGS__}; \ + lexer *l = new lexer(pattern, strlen(pattern)); \ + for (int i = 0; i < sizeof(expected)/sizeof(expected[0]); i++) \ + { \ + token token(glob_lexer_token_eof, "", 0); \ + l->next(&token); \ + test(test_case, token.kind == expected[i].kind && \ + token.s == expected[i].s); \ + } \ + delete l; \ + } + + TEST("glob/lexer", "", {glob_lexer_token_eof, "", 0}); + TEST("glob/lexer", "hello", {glob_lexer_token_text, "hello", 5}, {glob_lexer_token_eof, "", 0}); + TEST("glob/lexer", "/{rate,[0-9]]}*", + {glob_lexer_token_text, "/", 1}, + {glob_lexer_token_terms_open, "{", 1}, + {glob_lexer_token_text, "rate", 4}, + {glob_lexer_token_separator, ",", 1}, + {glob_lexer_token_range_open, "[", 1}, + {glob_lexer_token_range_lo, "0", 1}, + {glob_lexer_token_range_between, "-", 1}, + {glob_lexer_token_range_hi, "9", 1}, + {glob_lexer_token_range_close, "]", 1}, + {glob_lexer_token_text, "]", 1}, + {glob_lexer_token_terms_close, "}", 1}, + {glob_lexer_token_any, "*", 1}, + {glob_lexer_token_eof, "", 0}, + ); + TEST("glob/lexer", "hello,world", {glob_lexer_token_text, "hello,world", 11}, {glob_lexer_token_eof, "", 0}); + TEST("glob/lexer", "hello\\,world", {glob_lexer_token_text, "hello,world", 11}, {glob_lexer_token_eof, "", 0}); + TEST("glob/lexer", "hello\\{world", {glob_lexer_token_text, "hello{world", 11}, {glob_lexer_token_eof, "", 0}); + TEST("glob/lexer", "hello?", {glob_lexer_token_text, "hello", 5}, {glob_lexer_token_single, "?", 1}, {glob_lexer_token_eof, "", 0}); + TEST("glob/lexer", "hellof*", {glob_lexer_token_text, "hellof", 6}, {glob_lexer_token_any, "*", 1}, {glob_lexer_token_eof, "", 0}); + TEST("glob/lexer", "hello**", {glob_lexer_token_text, "hello", 5}, {glob_lexer_token_super, "**", 2}, {glob_lexer_token_eof, "", 0}); + TEST("glob/lexer", "[日-語]", + {glob_lexer_token_range_open, "[", 1}, + {glob_lexer_token_range_lo, "日", 3}, + {glob_lexer_token_range_between, "-", 1}, + {glob_lexer_token_range_hi, "語", 3}, + {glob_lexer_token_range_close, "]", 1}, + {glob_lexer_token_eof, "", 0}, + ); + TEST("glob/lexer", "[!日-語]", + {glob_lexer_token_range_open, "[", 1}, + {glob_lexer_token_not, "!", 1}, + {glob_lexer_token_range_lo, "日", 3}, + {glob_lexer_token_range_between, "-", 1}, + {glob_lexer_token_range_hi, "語", 3}, + {glob_lexer_token_range_close, "]", 1}, + {glob_lexer_token_eof, "", 0}, + ); + TEST("glob/lexer", "[!日本語]", + {glob_lexer_token_range_open, "[", 1}, + {glob_lexer_token_not, "!", 1}, + {glob_lexer_token_text, "日本語", 9}, + {glob_lexer_token_range_close, "]", 1}, + {glob_lexer_token_eof, "", 0}, + ); + TEST("glob/lexer", "{a,b}", + {glob_lexer_token_terms_open, "{", 1}, + {glob_lexer_token_text, "a", 1}, + {glob_lexer_token_separator, ",", 1}, + {glob_lexer_token_text, "b", 1}, + {glob_lexer_token_terms_close, "}", 1}, + {glob_lexer_token_eof, "", 0}, + ); + TEST("glob/lexer", "/{z,ab}*", + {glob_lexer_token_text, "/", 1}, + {glob_lexer_token_terms_open, "{", 1}, + {glob_lexer_token_text, "z", 1}, + {glob_lexer_token_separator, ",", 1}, + {glob_lexer_token_text, "ab", 2}, + {glob_lexer_token_terms_close, "}", 1}, + {glob_lexer_token_any, "*", 1}, + {glob_lexer_token_eof, "", 0}, + ); + TEST("glob/lexer", "{[!日-語],*,?,{a,b,\\c}}", + {glob_lexer_token_terms_open, "{", 1}, + {glob_lexer_token_range_open, "[", 1}, + {glob_lexer_token_not, "!", 1}, + {glob_lexer_token_range_lo, "日", 3}, + {glob_lexer_token_range_between, "-", 1}, + {glob_lexer_token_range_hi, "語", 3}, + {glob_lexer_token_range_close, "]", 1}, + {glob_lexer_token_separator, ",", 1}, + {glob_lexer_token_any, "*", 1}, + {glob_lexer_token_separator, ",", 1}, + {glob_lexer_token_single, "?", 1}, + {glob_lexer_token_separator, ",", 1}, + {glob_lexer_token_terms_open, "{", 1}, + {glob_lexer_token_text, "a", 1}, + {glob_lexer_token_separator, ",", 1}, + {glob_lexer_token_text, "b", 1}, + {glob_lexer_token_separator, ",", 1}, + {glob_lexer_token_text, "c", 1}, + {glob_lexer_token_terms_close, "}", 1}, + {glob_lexer_token_terms_close, "}", 1}, + {glob_lexer_token_eof, "", 0}, + ); +#undef TEST +} + +WASM_EXPORT(test_glob_parser) +extern "C" +void test_glob_parser() +{ +#define TEST(test_case, pattern, expected) { \ + node *e = expected; \ + lexer *l = new lexer(pattern, strlen(pattern)); \ + node *n = NULL; \ + std::string error = glob_parse(l, &n); \ + test(test_case, e->equal(n)); \ + delete n; \ + delete l; \ + delete e; \ + } + + TEST("glob/parser", "abc", (new node(kind_pattern))-> + insert(new node(kind_text, "abc"))); + TEST("glob/parser", "a*c", + (new node(kind_pattern))-> + insert(new node(kind_text, "a"))-> + insert(new node(kind_any))-> + insert(new node(kind_text, "c"))); + TEST("glob/parser", "a**c", + (new node(kind_pattern))-> + insert(new node(kind_text, "a"))-> + insert(new node(kind_super))-> + insert(new node(kind_text, "c"))); + TEST("glob/parser", "a?c", + (new node(kind_pattern))-> + insert(new node(kind_text, "a"))-> + insert(new node(kind_single))-> + insert(new node(kind_text, "c"))); + TEST("glob/parser", "[!a-z]", + (new node(kind_pattern))-> + insert(new node(kind_range, "a", "z", true))); + TEST("glob/parser", "[az]", + (new node(kind_pattern))-> + insert(new node(kind_list, "az", false))); + TEST("glob/parser", "{a,z}", + (new node(kind_pattern))-> + insert((new node(kind_any_of))-> + insert((new node(kind_pattern))-> + insert(new node(kind_text, "a")))-> + insert((new node(kind_pattern))-> + insert(new node(kind_text, "z"))))); + TEST("glob/parser", "/{z,ab}*", + (new node(kind_pattern))-> + insert(new node(kind_text, "/"))-> + insert((new node(kind_any_of))-> + insert((new node(kind_pattern))-> + insert(new node(kind_text, "z")))-> + insert((new node(kind_pattern))-> + insert(new node(kind_text, "ab"))))-> + insert(new node(kind_any))); + TEST("glob/parser", "{a,{x,y},?,[a-z],[!qwe]}", + (new node(kind_pattern))-> + insert((new node(kind_any_of))-> + insert((new node(kind_pattern))-> + insert(new node(kind_text, "a")))-> + insert((new node(kind_pattern))-> + insert((new node(kind_any_of))-> + insert((new node(kind_pattern))-> + insert(new node(kind_text, "x")))-> + insert((new node(kind_pattern))-> + insert(new node(kind_text, "y")))))-> + insert((new node(kind_pattern))-> + insert(new node(kind_single)))-> + insert((new node(kind_pattern))-> + insert(new node(kind_range, "a", "z", false)))-> + insert((new node(kind_pattern))-> + insert(new node(kind_list, "qwe", true))))); +#undef TEST +} + +WASM_EXPORT(test_glob_translate) +extern "C" +void test_glob_translate() +{ +#define TEST(test_case, pattern, expected, ...) { \ + std::string re2; \ + const char *delimiters[] = {__VA_ARGS__}; \ + std::vector v; \ + for (int i = 0; i < sizeof(delimiters)/sizeof(const char*); i++) { \ + v.push_back(delimiters[i]); \ + } \ + if (v.empty()) { v.push_back(std::string(".")); } \ + glob_translate(pattern, strlen(pattern), v, &re2); \ + test_str_eq(test_case, expected, re2.c_str()); \ + re2::RE2::Options options; \ + options.set_log_errors(false); \ + re2::RE2 compiled(std::string(re2.c_str(),strlen(re2.c_str())), options); \ + test(test_case, compiled.ok()); \ + } + + TEST("glob/translate", "[a-z][!a-x]*cat*[h][!b]*eyes*", "^[a-z][^a-x][^\\.]*cat[^\\.]*[h][^b][^\\.]*eyes[^\\.]*$"); + TEST("glob/translate", "https://*.google.*", "^https\\:\\/\\/[^\\.]*\\.google\\.[^\\.]*$"); + TEST("glob/translate", "https://*.google.*", "^https\\:\\/\\/[^\\.]*\\.google\\.[^\\.]*$", "."); // "." is the default + TEST("glob/translate", "{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}", "^(https\\:\\/\\/[^\\.]*\\.google\\.[^\\.]*|[^\\.]*yandex\\.[^\\.]*|[^\\.]*yahoo\\.[^\\.]*|[^\\.]*mail\\.ru)$"); + TEST("glob/translate", "{https://*gobwas.com,http://exclude.gobwas.com}", "^(https\\:\\/\\/[^\\.]*gobwas\\.com|http\\:\\/\\/exclude\\.gobwas\\.com)$"); + TEST("glob/translate", "abc*", "^abc[^\\.]*$"); + TEST("glob/translate", "*def", "^[^\\.]*def$"); + TEST("glob/translate", "*def", "^[^\\.]*def$", "."); // "." is the default + TEST("glob/translate", "ab*ef", "^ab[^\\.]*ef$"); + TEST("glob/translate", "api.*.com", "^api\\.[^\\.\\,]*\\.com$", ".", ","); + TEST("glob/translate", "api.**.com", "^api\\..*\\.com$"); + TEST("glob/translate", "api.**.com", "^api\\..*\\.com$", "."); // "." is the default... + TEST("glob/translate", "api.**.com", "^api\\..*\\.com$", ".", ","); // and "," does not matter here +#undef TEST +} diff --git a/third_party/opa/wasm/tests/test-regex.cc b/third_party/opa/wasm/tests/test-regex.cc new file mode 100644 index 000000000000..4ab1f6748f18 --- /dev/null +++ b/third_party/opa/wasm/tests/test-regex.cc @@ -0,0 +1,29 @@ +#include + +#include "malloc.h" +#include "regex.h" +#include "test.h" + + +WASM_EXPORT(test_regex_cache) +extern "C" +void test_regex_cache(void) +{ + // Reset the cache + opa_builtin_cache_set(0, NULL); + + for (int i = 0; i < 100; i++) + { + char pattern[20]; + snprintf(pattern, sizeof(pattern), "foo%d.*", i); + opa_regex_match(opa_string_terminated(pattern), opa_string_terminated("foobar")); + } + + std::unordered_map* c = static_cast*>(opa_builtin_cache_get(0)); + + test("regex cache size", c->size() == 100); + + opa_regex_match(opa_string_terminated("bar.*"), opa_string_terminated("barbaz")); + + test("regex cache size doesn't surpass max", c->size() == 100); +} \ No newline at end of file diff --git a/third_party/opa/wasm/tests/test.c b/third_party/opa/wasm/tests/test.c new file mode 100644 index 000000000000..758c57c9c916 --- /dev/null +++ b/third_party/opa/wasm/tests/test.c @@ -0,0 +1,3729 @@ +#include + +#include "aggregates.h" +#include "arithmetic.h" +#include "array.h" +#include "bits-builtins.h" +#include "cidr.h" +#include "conversions.h" +#include "encoding.h" +#include "glob.h" +#include "graphs.h" +#include "json.h" +#include "malloc.h" +#include "memoize.h" +#include "mpd.h" +#include "numbers.h" +#include "object.h" +#include "regex.h" +#include "set.h" +#include "str.h" +#include "strings.h" +#include "test.h" +#include "types.h" + +// NOTE(sr): we've removed the float number representation, so this helper +// is to make our tests less annoying: +#define opa_number_float(f) opa_number_ref(#f, sizeof(#f)) + +void reset_heap(void) +{ + // This will leak memory!! + // TODO: How should we safely reset it if we don't know the original starting ptr? + opa_heap_ptr_set(opa_heap_top_get()); +} + +WASM_EXPORT(test_opa_malloc) +void test_opa_malloc(void) +{ + opa_malloc_init_test(); + + reset_heap(); + + // NOTE(tsandall): These numbers are not particularly important. They're + // sized to cause opa_malloc to call grow.memory. The tester initializes + // memory with 2 pages so we allocate ~4 pages of memory here. + const int N = 256; + const int S = 1024; + + for(int i = 0; i < N; i++) + { + char *buf = opa_malloc(S); + + for(int x = 0; x < S; x++) + { + buf[x] = x % 255; + } + } +} + +WASM_EXPORT(test_opa_malloc_min_size) +void test_opa_malloc_min_size(void) +{ + reset_heap(); + + // Ensure that allocations less than the minimum size + // are creating blocks large enough to be re-used by + // the minimum size. + void *too_small = opa_malloc(2); + test("allocated min block", too_small != NULL); + + void *barrier = opa_malloc(0); + + opa_free(too_small); + + test("new free block", opa_heap_free_blocks() == 1); + + void *min_sized = opa_malloc(4); + test("reused block", opa_heap_free_blocks() == 0); + opa_free(min_sized); + opa_free(barrier); +} + +WASM_EXPORT(test_opa_malloc_split_threshold_small_block) +void test_opa_malloc_split_threshold_small_block(void) +{ + reset_heap(); + + // Ensure that free blocks larger than the requested + // allocation, but too small to leave a sufficiently + // sized remainder, are left intact. + size_t heap_block_size = 12; + void *too_small = opa_malloc(2 * 128 + heap_block_size - 1); + test("allocated too_small block", too_small != NULL); + + void *barrier = opa_malloc(256); + + opa_free(too_small); + + test("new small free block", opa_heap_free_blocks() == 1); + + // Expect the smaller allocation to use the bigger block + // without splitting. + void *new = opa_malloc(128); + test("unable to split block", opa_heap_free_blocks() == 0); + opa_free(new); +} + +WASM_EXPORT(test_opa_malloc_split_threshold_big_block) +void test_opa_malloc_split_threshold_big_block(void) +{ + reset_heap(); + + // Ensure that free blocks large enough to be split are split up + // until they are too small: have space almost to allocate three + // separate 128 blocks. + size_t heap_block_size = 12; + void *splittable = opa_malloc(3 * 128 + 2 * heap_block_size - 1); + test("allocated splittable block", splittable != NULL); + + void *barrier = opa_malloc(128); + + opa_free(splittable); + test("new large free block", opa_heap_free_blocks() == 1); + + // Expect to be able to get multiple blocks out of the new free one without + // new allocations. + unsigned int high = opa_heap_ptr_get(); + + void *split1 = opa_malloc(128); + void *split2 = opa_malloc(128); // Too big to split remaining bytes, should take oversized block. + + test("heap ptr", high == opa_heap_ptr_get()); + test("remaining free blocks", opa_heap_free_blocks() == 0); + + opa_free(split1); + opa_free(split2); + opa_free(barrier); +} + +WASM_EXPORT(test_opa_free) +void test_opa_free(void) +{ + reset_heap(); + + + // check the heap shrinks with a single malloc and free. + + size_t blocks = opa_heap_free_blocks(); + opa_free(opa_malloc(0)); + + test("free blocks", blocks == 0 && opa_heap_free_blocks() == 1); + + // check the double malloc, followed with frees in identical order + // results in eventual heap shrinking. + + void *p1 = opa_malloc(0); + void *p2 = opa_malloc(0); + test("free blocks", opa_heap_free_blocks() == 0); + + opa_free(p1); + test("free blocks", opa_heap_free_blocks() == 1); + + opa_free(p2); + test("free blocks", opa_heap_free_blocks() == 2); + + // check the double malloc, followed with frees in reverse order + // results in gradual heap shrinking. + + p1 = opa_malloc(0); + p2 = opa_malloc(0); + test("free blocks", opa_heap_free_blocks() == 0); + + opa_free(p2); + test("free blocks", opa_heap_free_blocks() == 1); + + opa_free(p1); + test("free blocks", opa_heap_free_blocks() == 2); + + // check the free re-use (without splitting). + + p1 = opa_malloc(1); + p2 = opa_malloc(1); + + opa_free(p1); + test("free blocks", opa_heap_free_blocks() == 1); + + p1 = opa_malloc(1); + test("free blocks", opa_heap_free_blocks() == 0); + + opa_free(p2); + opa_free(p1); + test("free blocks", opa_heap_free_blocks() == 2); + + // check the free re-use (with splitting). + + p1 = opa_malloc(512); + p2 = opa_malloc(512); + + opa_free(p1); + test("free blocks", opa_heap_free_blocks() == 3); + // 2 small, 1 large + + p1 = opa_malloc(128); + test("free blocks", opa_heap_free_blocks() == 3); + // 2 small, 1 large (split p1) + + opa_free(p2); + test("free blocks", opa_heap_free_blocks() == 3); + // 2 small, 1 large (merged end-p1, p2) + + opa_free(p1); + test("free blocks", opa_heap_free_blocks() == 3); + // 2 small, 1 large (merged start-p1,end-p1+p2) +} + +WASM_EXPORT(test_opa_heap_blocks_stash) +void test_opa_heap_blocks_stash(void) +{ + reset_heap(); + + unsigned int base = opa_heap_ptr_get(); + void *p1; + void *p2; + + // check basic save / restore + p1 = opa_malloc(128); + p2 = opa_malloc(128); + opa_free(p1); + test("free blocks", opa_heap_free_blocks() == 1); + opa_heap_blocks_stash(); + test("free blocks", opa_heap_free_blocks() == 0); + opa_heap_blocks_restore(); + test("free blocks", opa_heap_free_blocks() == 1); + opa_free(p2); + test("free blocks", opa_heap_free_blocks() == 1); + // 1 merged block remaining + + // check opa_heap_stash_clear() + p1 = opa_malloc(128); + p2 = opa_malloc(128); + opa_free(p1); + opa_heap_blocks_stash(); + opa_heap_stash_clear(); + opa_heap_blocks_restore(); + test("free blocks", opa_heap_free_blocks() == 0); + opa_heap_ptr_set(base); // p2 is dangling, but we'll discard it +} + +#define MAX_TEST_BLOCKS 100 +static void *sort_blocks[MAX_TEST_BLOCKS]; +unsigned int prng_state = 1; +#define AX 65537 +#define CX 123 + +/* Use a pseudo-random number generator to ensure test repeatability */ +static void prng_seed(unsigned int n) +{ + prng_state = n; +} + +static unsigned int prng(unsigned int max) +{ + unsigned int v = (prng_state * AX + CX) % max; + prng_state = v; + return v; +} + +static void test_opa_block_sort(unsigned int nblocks) +{ + int i, j; + void *h; + + test("valid nblocks", nblocks <= MAX_TEST_BLOCKS); + + reset_heap(); + + /* Allocate N blocks */ + for (i = 0; i < nblocks; i++) { + sort_blocks[i] = opa_malloc(128); + test("available blocks", sort_blocks[i] != NULL); + } + + /* Randomize the blocks */ + prng_seed(nblocks); + for (i = 0; i < nblocks; i++) + { + j = prng(nblocks - i) + i; + h = sort_blocks[i]; + sort_blocks[i] = sort_blocks[j]; + sort_blocks[j] = h; + } + + /* Bulk free all the blocks */ + for (i = 0; i < nblocks; i++) + opa_free_bulk(sort_blocks[i]); + + /* This will abort when compiled with DEBUG if order is violated */ + opa_free_bulk_commit(); + + test("total heap blocks", opa_heap_free_blocks() == 1); +} + +WASM_EXPORT(test_opa_block_sorting) +void test_opa_block_sorting(void) +{ + test_opa_block_sort(1); + test_opa_block_sort(2); + test_opa_block_sort(3); + test_opa_block_sort(4); + test_opa_block_sort(10); + test_opa_block_sort(30); + test_opa_block_sort(100); +} + +WASM_EXPORT(test_opa_free_bulk) +void test_opa_free_bulk(void) +{ + reset_heap(); + + void *p1; + void *p2; + void *p3; + void *p4; + + p1 = opa_malloc(128); + p2 = opa_malloc(128); + p3 = opa_malloc(128); + p4 = opa_malloc(128); + + opa_free_bulk(p1); + opa_free_bulk(p3); + + // blocks are on the bulk freelist and won't be reclaimed + // until the next malloc(). + test("free blocks", opa_heap_free_blocks() == 0); + p1 = opa_malloc(128); + test("free blocks", opa_heap_free_blocks() == 1); + + // now free them all and allocate 1. Check others aggregated + opa_free_bulk(p1); + opa_free_bulk(p2); + opa_free_bulk(p4); + + p1 = opa_malloc(128); + test("free blocks", opa_heap_free_blocks() == 1); + opa_free(p1); + test("free blocks", opa_heap_free_blocks() == 1); + + // Check that small blocks are released to the heap immediately + p1 = opa_malloc(32); + opa_free_bulk(p1); + test("free blocks", opa_heap_free_blocks() == 2); +} + +WASM_EXPORT(test_opa_memoize) +void test_opa_memoize(void) +{ + opa_memoize_init(); + + opa_memoize_insert(100, opa_number_int(1)); + opa_memoize_insert(200, opa_number_int(2)); + opa_value *a = opa_memoize_get(100); + opa_value *b = opa_memoize_get(200); + opa_memoize_push(); + opa_value *c = opa_memoize_get(100); + opa_value *d = opa_memoize_get(200); + opa_memoize_insert(100, opa_number_int(3)); + opa_memoize_pop(); + opa_value *e = opa_memoize_get(100); + + opa_value *exp_a = opa_number_int(1); + opa_value *exp_b = opa_number_int(2); + opa_value *exp_c = NULL; + opa_value *exp_d = NULL; + opa_value *exp_e = opa_number_int(1); + + test("insert-a", opa_value_compare(a, exp_a) == 0); + test("insert-b", opa_value_compare(b, exp_b) == 0); + test("get-a-after-push", opa_value_compare(c, exp_c) == 0); + test("get-b-after-push", opa_value_compare(d, exp_d) == 0); + test("get-a-after-pop", opa_value_compare(e, exp_e) == 0); +} + +// NOTE(sr): These tests are run in order. If they weren't, every test that +// depends on mpd's state being initialized would have to call `opa_mpd_init` +// first. When the Wasm module is used, the `Start` function (`_initialize`, +// emitted from the Wasm compiler) takes care of that. +WASM_EXPORT(test_opa_mpd) +void test_opa_mpd(void) +{ + // NOTE(sr): This call also initializes mpd_one, which is used under the + // hood for `qadd_one`. + opa_mpd_init(); + opa_value *zero = opa_number_int(0); + opa_value *two = opa_bf_to_number(qadd_one(qadd_one(opa_number_to_bf(zero)))); + test("0+1+1 is 2", opa_value_compare(opa_number_int(2), two) == 0); +} + +WASM_EXPORT(test_opa_strlen) +void test_opa_strlen(void) +{ + test("empty", opa_strlen("") == 0); + test("non-empty", opa_strlen("1234") == 4); +} + +WASM_EXPORT(test_opa_strncmp) +void test_opa_strncmp(void) +{ + test("empty", opa_strncmp("", "", 0) == 0); + test("equal", opa_strncmp("1234", "1234", 4) == 0); + test("less than", opa_strncmp("1234", "1243", 4) < 0); + test("greater than", opa_strncmp("1243", "1234", 4) > 0); +} + +WASM_EXPORT(test_opa_strcmp) +void test_opa_strcmp(void) +{ + test("empty", opa_strcmp("", "") == 0); + test("equal", opa_strcmp("abcd", "abcd") == 0); + test("less than", opa_strcmp("1234", "1243") < 0); + test("greater than", opa_strcmp("1243", "1234") > 0); + test("shorter", opa_strcmp("123", "1234") < 0); + test("longer", opa_strcmp("1234", "123") > 0); +} + +WASM_EXPORT(test_opa_itoa) +void test_opa_itoa(void) +{ + char buf[sizeof(long long)*8+1]; + + test("itoa", opa_strcmp(opa_itoa(0, buf, 10), "0") == 0); + test("itoa", opa_strcmp(opa_itoa(-128, buf, 10), "-128") == 0); + test("itoa", opa_strcmp(opa_itoa(127, buf, 10), "127") == 0); + test("itoa", opa_strcmp(opa_itoa(0x7FFFFFFFFFFFFFFF, buf, 10), "9223372036854775807") == 0); + test("itoa", opa_strcmp(opa_itoa(0x8000000000000001, buf, 10), "-9223372036854775807") == 0); + test("itoa", opa_strcmp(opa_itoa(0xFFFFFFFFFFFFFFFF, buf, 10), "-1") == 0); + + test("itoa/base2", opa_strcmp(opa_itoa(0, buf, 2), "0") == 0); + test("itoa/base2", opa_strcmp(opa_itoa(-128, buf, 2), "-10000000") == 0); + test("itoa/base2", opa_strcmp(opa_itoa(127, buf, 2), "1111111") == 0); + test("itoa/base2", opa_strcmp(opa_itoa(0x7FFFFFFFFFFFFFFF, buf, 2), "111111111111111111111111111111111111111111111111111111111111111") == 0); + test("itoa/base2", opa_strcmp(opa_itoa(0x8000000000000001, buf, 2), "-111111111111111111111111111111111111111111111111111111111111111") == 0); + test("itoa/base2", opa_strcmp(opa_itoa(0xFFFFFFFFFFFFFFFF, buf, 2), "-1") == 0); + + test("itoa/base16", opa_strcmp(opa_itoa(0, buf, 16), "0") == 0); + test("itoa/base16", opa_strcmp(opa_itoa(-128, buf, 16), "-80") == 0); + test("itoa/base16", opa_strcmp(opa_itoa(127, buf, 16), "7f") == 0); + test("itoa/base16", opa_strcmp(opa_itoa(0x7FFFFFFFFFFFFFFF, buf,16), "7fffffffffffffff") == 0); + test("itoa/base16", opa_strcmp(opa_itoa(0x8000000000000001, buf, 16), "-7fffffffffffffff") == 0); + test("itoa/base16", opa_strcmp(opa_itoa(0xFFFFFFFFFFFFFFFF, buf, 16), "-1") == 0); +} + + +int crunch_opa_atoi64(const char *str, long long exp, int exp_rc) +{ + long long result; + int rc; + + if ((rc = opa_atoi64(str, opa_strlen(str), &result)) != exp_rc) + { + return 0; + } + + return exp_rc != 0 || result == exp; +} + +WASM_EXPORT(test_opa_atoi64) +void test_opa_atoi64(void) +{ + test("integer", crunch_opa_atoi64("127", 127, 0)); + test("negative integer", crunch_opa_atoi64("-128", -128, 0)); + test("non integer", crunch_opa_atoi64("-128.3", 0, -2)); + test("empty", crunch_opa_atoi64("", 0, -1)); +} + +int crunch_opa_atof64(const char *str, double exp, int exp_rc) +{ + double result; + int rc; + + if ((rc = opa_atof64(str, opa_strlen(str), &result)) != exp_rc) + { + return 0; + } + + return exp_rc != 0 || result == exp; +} + +WASM_EXPORT(test_opa_atof64) +void test_opa_atof64(void) +{ + test("empty", crunch_opa_atof64("", 0, -1)); + test("bad integer", crunch_opa_atof64("1234-6", 0, -2)); + test("bad fraction", crunch_opa_atof64("1234.5-6", 0, -2)); + test("bad exponent", crunch_opa_atof64("1234.5e6-", 0, -2)); + test("bad exponent", crunch_opa_atof64("12345e6-", 0, -2)); + test("integer", crunch_opa_atof64("127", 127, 0)); + test("negative integer", crunch_opa_atof64("-128", -128, 0)); + test("fraction", crunch_opa_atof64("16.7", 16.7, 0)); + test("exponent", crunch_opa_atof64("6e7", 6e7, 0)); +} + +WASM_EXPORT(test_memchr) +void test_memchr(void) +{ + char s[] = { 1, 2, 2, 3 }; + + test("memchr", memchr(s, 2, 1) == NULL); + test("memchr", memchr(s, 2, sizeof(s)) == &s[1]); + test("memchr", memchr(s, 4, sizeof(s)) == NULL); +} + +WASM_EXPORT(test_memcmp) +void test_memcmp(void) +{ + char a[] = { 1, 2, 3, 4 }, b[] = { 1, 2, 3, 3 }; + + test("memcmp", memcmp(a, b, 3) == 0); + test("memcmp", memcmp(a, b, 4) == 1); + test("memcmp", memcmp(b, a, 4) == -1); +} + +WASM_EXPORT(test_memcpy) +void test_memcpy(void) +{ + char dest[] = { 1, 2, 3, 4 }, src[] = { 9, 8, 7 }; + char expected[] = { 9, 8, 3, 4 }; + memcpy(dest, src, 2); + + test("memcpy", memcmp(dest, expected, sizeof(expected)) == 0); +} + +WASM_EXPORT(test_memset) +void test_memset(void) +{ + char s[] = { 9, 8, 7, 6 }; + char expected[] = { 1, 1, 1, 6 }; + memset(s, 1, 3); + + test("memset", memcmp(s, expected, sizeof(expected)) == 0); +} + +int lex_crunch(const char *s) +{ + opa_json_lex ctx; + opa_json_lex_init(s, opa_strlen(s), &ctx); + return opa_json_lex_read(&ctx); +} + +WASM_EXPORT(test_opa_lex_tokens) +void test_opa_lex_tokens(void) +{ + test("empty", lex_crunch("") == OPA_JSON_TOKEN_EOF); + test("space", lex_crunch(" ") == OPA_JSON_TOKEN_EOF); + test("tab", lex_crunch("\t") == OPA_JSON_TOKEN_EOF); + test("newline", lex_crunch("\n") == OPA_JSON_TOKEN_EOF); + test("carriage return", lex_crunch("\r") == OPA_JSON_TOKEN_EOF); + test("null", lex_crunch("null") == OPA_JSON_TOKEN_NULL); + test("true", lex_crunch("true") == OPA_JSON_TOKEN_TRUE); + test("false", lex_crunch("false") == OPA_JSON_TOKEN_FALSE); + + test("bad unicode", lex_crunch("\" \\uabcx \"") == OPA_JSON_TOKEN_ERROR); // not hex + test("escape not closed", lex_crunch("\"a\\\"") == OPA_JSON_TOKEN_ERROR); // unmatched escape + test("bad escape character", lex_crunch("\"\\Q\"") == OPA_JSON_TOKEN_ERROR); // invalid escape character Q + test("object start", lex_crunch(" { ") == OPA_JSON_TOKEN_OBJECT_START); + test("object end", lex_crunch(" } ") == OPA_JSON_TOKEN_OBJECT_END); + test("array start", lex_crunch(" [ ") == OPA_JSON_TOKEN_ARRAY_START); + test("array end", lex_crunch(" ] ") == OPA_JSON_TOKEN_ARRAY_END); + test("element separator", lex_crunch(" , ") == OPA_JSON_TOKEN_COMMA); + test("item separator", lex_crunch(" : ") == OPA_JSON_TOKEN_COLON); +} + +int lex_buffer_crunch(const char *s, const char *exp, int token) +{ + opa_json_lex ctx; + opa_json_lex_init(s, opa_strlen(s), &ctx); + + if (opa_json_lex_read(&ctx) != token) + { + return -1; + } + + size_t exp_len = opa_strlen(exp); + size_t buf_len = ctx.buf_end - ctx.buf; + + if (exp_len != buf_len) + { + return -2; + } + + if (opa_strncmp(ctx.buf, exp, buf_len) != 0) + { + return -3; + } + + return 0; +} + +#define test_lex_buffer(note, s, exp, token) test(note, (lex_buffer_crunch(s, exp, token) == 0)) + +WASM_EXPORT(test_opa_lex_buffer) +void test_opa_lex_buffer(void) +{ + test_lex_buffer("zero", "0", "0", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("signed zero", "-0", "-0", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("integers", "1234567890", "1234567890", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("signed integers", "-1234567890", "-1234567890", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("floats", "0.1234567890", "0.1234567890", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("signed floats", "-0.1234567890", "-0.1234567890", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("exponents", "-0.1234567890e0", "-0.1234567890e0", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("exponents", "-0.1234567890E+1000", "-0.1234567890E+1000", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("exponents", "-0.1234567890E-1000", "-0.1234567890E-1000", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("empty string", "\"\"", "", OPA_JSON_TOKEN_STRING); + test_lex_buffer("escaped buffer", "\"a\\\"b\"", "a\\\"b", OPA_JSON_TOKEN_STRING_ESCAPED); + test_lex_buffer("escaped quote", "\"\\\"\"", "\\\"", OPA_JSON_TOKEN_STRING_ESCAPED); + test_lex_buffer("escaped reverse solidus", "\"\\\\\"", "\\\\", OPA_JSON_TOKEN_STRING_ESCAPED); + test_lex_buffer("escaped solidus", "\"\\/\"", "\\/", OPA_JSON_TOKEN_STRING_ESCAPED); + test_lex_buffer("escaped backspace", "\"\\b\"", "\\b", OPA_JSON_TOKEN_STRING_ESCAPED); + test_lex_buffer("escaped feed forward", "\"\\f\"", "\\f", OPA_JSON_TOKEN_STRING_ESCAPED); + test_lex_buffer("escaped line feed", "\"\\n\"", "\\n", OPA_JSON_TOKEN_STRING_ESCAPED); + test_lex_buffer("escaped carriage return", "\"\\r\"", "\\r", OPA_JSON_TOKEN_STRING_ESCAPED); + test_lex_buffer("escaped tab", "\"\\t\"", "\\t", OPA_JSON_TOKEN_STRING_ESCAPED); + test_lex_buffer("plain", "\"abcdefg\"", "abcdefg", OPA_JSON_TOKEN_STRING); +} + +WASM_EXPORT(test_opa_value_compare) +void test_opa_value_compare(void) +{ + test("none", opa_value_compare(NULL, NULL) == 0); + test("none/some", opa_value_compare(NULL, opa_null()) < 0); + test("some/none", opa_value_compare(opa_null(), NULL) > 0); + test("null", opa_value_compare(opa_null(), opa_null()) == 0); + test("null/boolean", opa_value_compare(opa_boolean(true), opa_null()) > 0); + test("true/true", opa_value_compare(opa_boolean(true), opa_boolean(true)) == 0); + test("true/false", opa_value_compare(opa_boolean(true), opa_boolean(false)) > 0); + test("false/true", opa_value_compare(opa_boolean(false), opa_boolean(true)) < 0); + test("false/false", opa_value_compare(opa_boolean(false), opa_boolean(false)) == 0); + test("number/boolean", opa_value_compare(opa_number_int(100), opa_boolean(true)) > 0); + test("integers", opa_value_compare(opa_number_int(100), opa_number_int(99)) > 0); + test("integers", opa_value_compare(opa_number_int(100), opa_number_int(101)) < 0); + test("integers", opa_value_compare(opa_number_int(100), opa_number_int(100)) == 0); + test("integers", opa_value_compare(opa_number_int(-100), opa_number_int(100)) < 0); + test("integers", opa_value_compare(opa_number_int(-100), opa_number_int(-101)) > 0); + test("integer/float", opa_value_compare(opa_number_int(100), opa_number_float(100.1)) < 0); + test("floats", opa_value_compare(opa_number_float(100.2), opa_number_float(100.1)) > 0); + test("floats", opa_value_compare(opa_number_float(100.2), opa_number_float(100.3)) < 0); + test("floats", opa_value_compare(opa_number_float(100.3), opa_number_float(100.3)) == 0); + test("string/number", opa_value_compare(opa_string_terminated("foo"), opa_number_float(100)) > 0); + test("strings", opa_value_compare(opa_string_terminated("foo"), opa_string_terminated("foo")) == 0); + test("strings", opa_value_compare(opa_string_terminated("foo"), opa_string_terminated("bar")) > 0); + test("strings", opa_value_compare(opa_string_terminated("bar"), opa_string_terminated("baz")) < 0); + test("strings", opa_value_compare(opa_string_terminated("foobar"), opa_string_terminated("foo")) > 0); + test("strings", opa_value_compare(opa_string_terminated("foo"), opa_string_terminated("foobar")) < 0); + + opa_array_t *arr1 = opa_cast_array(opa_array()); + opa_array_append(arr1, opa_number_int(1)); + opa_array_append(arr1, opa_number_int(2)); + opa_array_append(arr1, opa_number_int(3)); + + opa_array_t *arr2 = opa_cast_array(opa_array()); + opa_array_append(arr2, opa_number_int(1)); + opa_array_append(arr2, opa_number_int(3)); + opa_array_append(arr2, opa_number_int(2)); + + opa_array_t *arr3 = opa_cast_array(opa_array()); + opa_array_append(arr2, opa_number_int(1)); + opa_array_append(arr2, opa_number_int(3)); + + opa_value *v1, *v2, *v3; + v1 = &arr1->hdr; + v2 = &arr2->hdr; + v3 = &arr3->hdr; + + test("array/string", opa_value_compare(v1, opa_string_terminated("a")) > 0); + test("arrays", opa_value_compare(v1, v1) == 0); + test("arrays", opa_value_compare(v1, v2) < 0); + test("arrays", opa_value_compare(v2, v1) > 0); + test("arrays", opa_value_compare(v3, v2) < 0); + test("arrays", opa_value_compare(v2, v3) > 0); + + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj1, opa_string_terminated("b"), opa_number_int(2)); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj2, opa_string_terminated("b"), opa_number_int(3)); + + opa_object_t *obj3 = opa_cast_object(opa_object()); + opa_object_insert(obj3, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj3, opa_string_terminated("c"), opa_number_int(3)); + + opa_object_t *obj4 = opa_cast_object(opa_object()); + opa_object_insert(obj4, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj4, opa_string_terminated("b"), opa_number_int(2)); + opa_object_insert(obj4, opa_string_terminated("c"), opa_number_int(3)); + + v1 = &obj1->hdr; + v2 = &obj2->hdr; + v3 = &obj3->hdr; + opa_value *v4 = &obj4->hdr; + + test("object/array", opa_value_compare(v1, opa_array()) > 0); + test("objects", opa_value_compare(v1, v1) == 0); + test("objects", opa_value_compare(v1, v2) < 0); + test("objects", opa_value_compare(v2, v3) < 0); + test("objects", opa_value_compare(v4, v1) > 0); + test("objects", opa_value_compare(v4, v2) < 0); + + opa_set_t *set1 = opa_cast_set(opa_set()); + opa_set_add(set1, opa_string_terminated("a")); + opa_set_add(set1, opa_string_terminated("b")); + + opa_set_t *set2 = opa_cast_set(opa_set()); + opa_set_add(set2, opa_string_terminated("a")); + opa_set_add(set2, opa_string_terminated("c")); + + opa_set_t *set3 = opa_cast_set(opa_set()); + opa_set_add(set3, opa_string_terminated("a")); + opa_set_add(set3, opa_string_terminated("b")); + opa_set_add(set3, opa_string_terminated("c")); + + v1 = &set1->hdr; + v2 = &set2->hdr; + v3 = &set3->hdr; + + test("set/object", opa_value_compare(v1, opa_object()) > 0); + test("sets", opa_value_compare(v1, v1) == 0); + test("sets", opa_value_compare(v1, v2) < 0); + test("sets", opa_value_compare(v2, v3) > 0); // because c > b + test("sets", opa_value_compare(v3, v1) > 0); +} + +int parse_crunch(const char *s, opa_value *exp) +{ + opa_value *ret = opa_json_parse(s, opa_strlen(s)); + if (ret == NULL) + { + return 0; + } + return opa_value_compare(exp, ret) == 0; +} + +int value_parse_crunch(const char *s, opa_value *exp) +{ + opa_value *ret = opa_value_parse(s, opa_strlen(s)); + if (ret == NULL) + { + return 0; + } + return opa_value_compare(exp, ret) == 0; +} + +WASM_EXPORT(test_opa_json_parse_scalar) +void test_opa_json_parse_scalar(void) +{ + test("null", parse_crunch("null", opa_null())); + test("true", parse_crunch("true", opa_boolean(true))); + test("false", parse_crunch("false", opa_boolean(false))); + test("strings", parse_crunch("\"hello\"", opa_string_terminated("hello"))); + test("strings: escaped quote", parse_crunch("\"a\\\"b\"", opa_string_terminated("a\"b"))); + test("strings: escaped reverse solidus", parse_crunch("\"a\\\\b\"", opa_string_terminated("a\\b"))); + test("strings: escaped solidus", parse_crunch("\"a\\/b\"", opa_string_terminated("a/b"))); + test("strings: escaped backspace", parse_crunch("\"a\\bb\"", opa_string_terminated("a\bb"))); + test("strings: escaped feed forward", parse_crunch("\"a\\fb\"", opa_string_terminated("a\fb"))); + test("strings: escaped line feed", parse_crunch("\"a\\nb\"", opa_string_terminated("a\nb"))); + test("strings: escaped carriage return", parse_crunch("\"a\\rb\"", opa_string_terminated("a\rb"))); + test("strings: escaped tab", parse_crunch("\"a\\tb\"", opa_string_terminated("a\tb"))); + test("strings: utf-8 2 bytes", parse_crunch("\"\xc2\xa2\"", opa_string_terminated("\xc2\xa2"))); + test("strings: utf-8 3 bytes", parse_crunch("\"\xe0\xb8\x81\"", opa_string_terminated("\xe0\xb8\x81"))); + test("strings: utf-8 3 bytes", parse_crunch("\"\xe2\x82\xac\"", opa_string_terminated("\xe2\x82\xac"))); + test("strings: utf-8 3 bytes", parse_crunch("\"\xed\x9e\xb0\"", opa_string_terminated("\xed\x9e\xb0"))); + test("strings: utf-8 3 bytes", parse_crunch("\"\xef\xa4\x80\"", opa_string_terminated("\xef\xa4\x80"))); + test("strings: utf-8 4 bytes", parse_crunch("\"\xf0\x90\x8d\x88\"", opa_string_terminated("\xf0\x90\x8d\x88"))); + test("strings: utf-8 4 bytes", parse_crunch("\"\xf3\xa0\x80\x81\"", opa_string_terminated("\xf3\xa0\x80\x81"))); + test("strings: utf-8 4 bytes", parse_crunch("\"\xf4\x80\x80\x80\"", opa_string_terminated("\xf4\x80\x80\x80"))); + test("strings: utf-16 no surrogate pair", parse_crunch("\" \\u20AC \"", opa_string_terminated(" \xe2\x82\xac "))); + test("strings: utf-16 surrogate pair", parse_crunch("\" \\ud801\\udc37 \"", opa_string_terminated(" \xf0\x90\x90\xb7 "))); + test("integers", parse_crunch("0", opa_number_int(0))); + test("integers", parse_crunch("123456789", opa_number_int(123456789))); + test("signed integers", parse_crunch("-0", opa_number_int(0))); + test("signed integers", parse_crunch("-123456789", opa_number_int(-123456789))); + test("floats", parse_crunch("16.7", opa_number_float(16.7))); + test("signed floats", parse_crunch("-16.7", opa_number_float(-16.7))); + test("exponents", parse_crunch("6e7", opa_number_float(6e7))); +} + +WASM_EXPORT(test_opa_json_max_str_len) +void test_opa_json_max_str_len(void) +{ + test("max str len: a char", opa_json_max_string_len("a", 1) == 1); + test("max str len: chars", opa_json_max_string_len("ab", 2) == 2); + test("max str len: single char escape", opa_json_max_string_len("ab\nd", 4) == 4); + test("max str len: 2 byte utf-8", opa_json_max_string_len("\xc2\xa2", 2) == 2); + test("max str len: 3 byte utf-8", opa_json_max_string_len("\xe0\xb8\x81", 3) == 3); + test("max str len: 4 byte utf-8", opa_json_max_string_len("\xf0\x90\x8d\x88", 4) == 4); + test("max str len: utf-16 no surrogate pair", opa_json_max_string_len(" \\u20AC ", 8) == 6); + test("max str len: utf-16 surrogate pair", opa_json_max_string_len(" \\ud801\\udc37 ", 14) == 6); +} + +opa_array_t *fixture_array1(void) +{ + opa_array_t *arr = opa_cast_array(opa_array()); + opa_array_append(arr, opa_number_int(1)); + opa_array_append(arr, opa_number_int(2)); + opa_array_append(arr, opa_number_int(3)); + opa_array_append(arr, opa_number_int(4)); + return arr; +} + +opa_array_t *fixture_array2(void) +{ + opa_array_t *arr1 = opa_cast_array(opa_array()); + opa_array_append(arr1, opa_number_int(1)); + opa_array_append(arr1, opa_number_int(2)); + opa_array_append(arr1, opa_number_int(3)); + opa_array_append(arr1, opa_number_int(4)); + + opa_array_t *arr2 = opa_cast_array(opa_array()); + opa_array_append(arr2, opa_number_int(5)); + opa_array_append(arr2, opa_number_int(6)); + opa_array_append(arr2, opa_number_int(7)); + opa_array_append(arr2, opa_number_int(8)); + + opa_array_t *arr = opa_cast_array(opa_array()); + opa_array_append(arr, &arr1->hdr); + opa_array_append(arr, &arr2->hdr); + + return arr; +} + +opa_object_t *fixture_object1(void) +{ + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj, opa_string_terminated("b"), opa_number_int(2)); + return obj; +} + +opa_object_t *fixture_object2(void) +{ + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("c"), opa_number_int(1)); + opa_object_insert(obj1, opa_string_terminated("d"), opa_number_int(2)); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("e"), opa_number_int(3)); + opa_object_insert(obj2, opa_string_terminated("f"), opa_number_int(4)); + + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), &obj1->hdr); + opa_object_insert(obj, opa_string_terminated("b"), &obj2->hdr); + return obj; +} + +opa_set_t *fixture_set1(void) +{ + opa_set_t *set = opa_cast_set(opa_set()); + opa_set_add(set, opa_string_terminated("a")); + opa_set_add(set, opa_string_terminated("b")); + return set; +} + +WASM_EXPORT(test_opa_value_length) +void test_opa_value_length(void) +{ + opa_array_t *arr = fixture_array1(); + opa_object_t *obj = fixture_object1(); + opa_set_t *set = fixture_set1(); + + test("arrays", opa_value_length(&arr->hdr) == 4); + test("objects", opa_value_length(&obj->hdr) == 2); + test("sets", opa_value_length(&set->hdr) == 2); +} + +WASM_EXPORT(test_opa_value_get_array) +void test_opa_value_get_array(void) +{ + opa_array_t *arr = fixture_array1(); + + for (int i = 0; i < 4; i++) + { + opa_value *result = opa_value_get(&arr->hdr, opa_number_int(i)); + + if (result == NULL) + { + test_fatal("array get failed"); + } + + if (opa_value_compare(result, opa_number_int(i + 1)) != 0) + { + test_fatal("array get returned bad value"); + } + } + + opa_value *result = opa_value_get(&arr->hdr, opa_string_terminated("foo")); + + if (result != NULL) + { + test_fatal("array get returned unexpected result"); + } + + result = opa_value_get(&arr->hdr, opa_number_float(3.14)); + + if (result != NULL) + { + test_fatal("array get returned unexpected result"); + } + + result = opa_value_get(&arr->hdr, opa_number_int(-1)); + + if (result != NULL) + { + test_fatal("array get returned unexpected result"); + } + + result = opa_value_get(&arr->hdr, opa_number_int(4)); + + if (result != NULL) + { + test_fatal("array get returned unexpected result"); + } +} + +WASM_EXPORT(test_opa_array_sort) +void test_opa_array_sort(void) +{ + opa_array_t *arr = opa_cast_array(opa_array()); + + opa_array_append(arr, opa_number_int(4)); + opa_array_append(arr, opa_number_int(3)); + opa_array_append(arr, opa_number_int(2)); + opa_array_append(arr, opa_number_int(1)); + + opa_array_sort(arr, opa_value_compare); + + // iterate through the array to verify both the indices and values. + + opa_value *res = opa_array(); + opa_value *exp = &fixture_array1()->hdr; + + for (opa_value *prev = NULL, *curr = NULL; (curr = opa_value_iter(&arr->hdr, prev)) != NULL; prev = curr) + { + opa_array_append(opa_cast_array(res), opa_value_get(&arr->hdr, curr)); + } + + if (opa_value_compare(res, exp) != 0) + { + test_fatal("array sort returned unexpected result"); + } +} + +WASM_EXPORT(test_opa_value_get_object) +void test_opa_value_get_object(void) +{ + opa_object_t *obj = fixture_object1(); + + const char *keys[2] = { + "a", + "b", + }; + + long long values[2] = { + 1, + 2, + }; + + for (int i = 0; i < sizeof(keys) / sizeof(const char *); i++) + { + opa_value *result = opa_value_get(&obj->hdr, opa_string_terminated(keys[i])); + + if (result == NULL) + { + test_fatal("object get failed"); + } + + if (opa_value_compare(result, opa_number_int(values[i])) != 0) + { + test_fatal("object get returned bad value"); + } + } + + opa_value *result = opa_value_get(&obj->hdr, opa_string_terminated("non-existent")); + + if (result != NULL) + { + test_fatal("object get returned unexpected result"); + } +} + +WASM_EXPORT(test_opa_json_parse_composites) +void test_opa_json_parse_composites(void) +{ + + opa_value *empty_arr = opa_array(); + + test("empty array", parse_crunch("[]", empty_arr)); + test("array", parse_crunch("[1,2,3,4]", &fixture_array1()->hdr)); + test("array nested", parse_crunch("[[1,2,3,4],[5,6,7,8]]", &fixture_array2()->hdr)); + + opa_value *empty_obj = opa_object(); + + test("empty object", parse_crunch("{}", empty_obj)); + test("object", parse_crunch("{\"a\": 1, \"b\": 2}", &fixture_object1()->hdr)); + test("object nested", parse_crunch("{\"a\": {\"c\": 1, \"d\": 2}, \"b\": {\"e\": 3, \"f\": 4}}", &fixture_object2()->hdr)); +} + +WASM_EXPORT(test_opa_value_parse) +void test_opa_value_parse(void) +{ + opa_set_t *set = opa_cast_set(opa_set()); + opa_set_add(set, opa_number_int(1)); + + test("set of one", value_parse_crunch("{1}", &set->hdr)); + test("set of one - dupes", value_parse_crunch("{1,1}", &set->hdr)); + + opa_set_add(set, opa_number_int(2)); + opa_set_add(set, opa_number_int(3)); + + test("set multiple", value_parse_crunch("{1,2,3}", &set->hdr)); + + opa_value *empty_set = opa_set(); + + test("empty", value_parse_crunch("set()", empty_set)); + test("empty whitespace", value_parse_crunch("set( )", empty_set)); +} + +WASM_EXPORT(test_opa_value_free) +void test_opa_value_free(void) +{ + char *s; + opa_value *ret; + int i; + + reset_heap(); + unsigned int base; + + // This is tricky because some of these dynamic allocations + // leave memory in the heap because the blocks they allocate + // come from different lists. If they don't get freed in + // reverse order of allocation, the heap pointer won't go + // down. Some of these objects don't free the sub-objects + // in reverse order of allocation. + // + // However, what we can do is + // * do a small number of parse/free combinations to prime the heap + // * grab the new heap pointer + // * do a few more parse/free cycles checking each time that the + // pointer doesn't increase. + // + // This isn't perfect because for larger, variable-sized blocks + // order of allocation matters and can lead to the heap bumping + // up a bit although this would stabalize in the long run. But + // to a void this, keep the value elements (strings, numbers, ...) + // small to keep the allocations in the fixed-sized block regions. + + // Null test + s = "null"; + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + base = opa_heap_ptr_get(); + for (i = 0; i < 5; i++) { + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + test("heap ptr", base == opa_heap_ptr_get()); + } + + // Boolean test + s = "true"; + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + base = opa_heap_ptr_get(); + for (i = 0; i < 5; i++) { + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + test("heap ptr", base == opa_heap_ptr_get()); + } + + // Integer test + s = "0"; + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + base = opa_heap_ptr_get(); + for (i = 0; i < 5; i++) { + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + test("heap ptr", base == opa_heap_ptr_get()); + } + + // String test + s = "\"hello\""; + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + base = opa_heap_ptr_get(); + for (i = 0; i < 5; i++) { + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + test("heap ptr", base == opa_heap_ptr_get()); + } + + // Array test + s = "[\"a\", \"b\", \"c\"]"; + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + base = opa_heap_ptr_get(); + for (i = 0; i < 5; i++) { + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + test("heap ptr", base == opa_heap_ptr_get()); + } + + // Object test + s = "{\"a\": 1, \"b\": 2}"; + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + base = opa_heap_ptr_get(); + for (i = 0; i < 5; i++) { + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + test("heap ptr", base == opa_heap_ptr_get()); + } + + // Set test + // + // The bucket sizes seem to be just enough to mix elements, + // buckets and set objects. This makes one parse/free + // cycle insufficient to stabalize the heap. So do two + // instead before validating that the heap will no longer + // increase. + s = "{\"a\", \"b\", \"c\"}"; + for (i = 0; i < 2; i++) { + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + } + base = opa_heap_ptr_get(); + for (i = 0; i < 5; i++) { + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + test("heap ptr", base == opa_heap_ptr_get()); + } + + // Compound object / array test + s = "{[1,2],[3,4]}"; + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + base = opa_heap_ptr_get(); + for (i = 0; i < 5; i++) { + ret = opa_value_parse(s, opa_strlen(s)); + test("parse", ret != NULL); + opa_value_free(ret); + test("heap ptr", base == opa_heap_ptr_get()); + } +} + + +WASM_EXPORT(test_opa_json_parse_memory_ownership) +void test_opa_json_parse_memory_ownership(void) +{ + char s[] = "[1,\"a\"]"; + + opa_value *result = opa_json_parse(s, sizeof(s)); + + opa_value *exp = opa_array(); + opa_array_t *arr = opa_cast_array(exp); + opa_array_append(arr, opa_number_int(1)); + opa_array_append(arr, opa_string("a", 1)); + + test("expected value", opa_value_compare(result, exp) == 0); + + for (int i = 0; i < sizeof(s); i++) + { + s[i] = 0; + } + + test("expected value after overwriting buffer", opa_value_compare(result, exp) == 0); +} + +WASM_EXPORT(test_opa_object_insert) +void test_opa_object_insert(void) +{ + + opa_object_t *obj = opa_cast_object(opa_object()); + + opa_object_insert(obj, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj, opa_string_terminated("b"), opa_number_int(2)); + opa_object_insert(obj, opa_string_terminated("a"), opa_number_int(3)); + + opa_value *v1 = opa_value_get(&obj->hdr, opa_string_terminated("a")); + + if (opa_value_compare(v1, opa_number_int(3)) != 0) + { + test_fatal("object insert did not replace value") + } + + opa_object_insert(obj, opa_string_terminated("b"), opa_number_int(4)); + + opa_value *v2 = opa_value_get(&obj->hdr, opa_string_terminated("b")); + + if (opa_value_compare(v2, opa_number_int(4)) != 0) + { + test_fatal("object insert did not replace value") + } +} + +WASM_EXPORT(test_opa_object_growth) +void test_opa_object_growth(void) +{ + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), opa_string_terminated("1")); + opa_object_insert(obj, opa_string_terminated("b"), opa_string_terminated("2")); + opa_object_insert(obj, opa_string_terminated("c"), opa_string_terminated("3")); + opa_object_insert(obj, opa_string_terminated("d"), opa_string_terminated("4")); + opa_object_insert(obj, opa_string_terminated("e"), opa_string_terminated("5")); + opa_object_insert(obj, opa_string_terminated("e"), opa_string_terminated("5'")); + + if (obj->len != 5) + { + test_fatal("object is missing key-value pairs") + } + + if (obj->n != 8) + { + test_fatal("object capacity did double") + } + + opa_object_insert(obj, opa_string_terminated("f"), opa_string_terminated("6")); + + if (obj->len != 6) + { + test_fatal("object is missing key-value pairs") + } + + if (obj->n != 16) + { + test_fatal("object capacity did not double") + } +} + +WASM_EXPORT(test_opa_set_add_and_get) +void test_opa_set_add_and_get(void) +{ + opa_set_t *set = fixture_set1(); + opa_set_add(set, opa_string_terminated("a")); + + opa_set_t *cpy = fixture_set1(); + + if (opa_value_compare(&set->hdr, &cpy->hdr) != 0) + { + test_fatal("set was modified by add with duplicate element"); + } + + opa_set_add(set, opa_string_terminated("c")); + + if (opa_value_compare(&set->hdr, &cpy->hdr) <= 0) + { + test_fatal("set should be greater than cpy") + } + + if (opa_value_get(&set->hdr, opa_string_terminated("c")) == NULL) + { + test_fatal("set should contain string term c") + } + + opa_set_t *order = opa_cast_set(opa_set()); + opa_set_add(order, opa_string_terminated("b")); + opa_set_add(order, opa_string_terminated("c")); + opa_set_add(order, opa_string_terminated("a")); + + if (opa_value_compare(&set->hdr, &order->hdr) != 0) + { + test_fatal("sets should be equal") + } +} + +WASM_EXPORT(test_opa_set_growth) +void test_opa_set_growth(void) +{ + opa_set_t *set = opa_cast_set(opa_set()); + opa_set_add(set, opa_string_terminated("a")); + opa_set_add(set, opa_string_terminated("b")); + opa_set_add(set, opa_string_terminated("c")); + opa_set_add(set, opa_string_terminated("d")); + opa_set_add(set, opa_string_terminated("e")); + opa_set_add(set, opa_string_terminated("e")); + + if (set->len != 5) + { + test_fatal("set is missing elements") + } + + if (set->n != 8) + { + test_fatal("set capacity did double") + } + + opa_set_add(set, opa_string_terminated("f")); + + if (set->len != 6) + { + test_fatal("set is missing elements") + } + + if (set->n != 16) + { + test_fatal("set capacity did not double") + } +} + +WASM_EXPORT(test_opa_value_iter_object) +void test_opa_value_iter_object(void) +{ + opa_object_t *obj = fixture_object1(); + + opa_value *k1 = opa_value_iter(&obj->hdr, NULL); + opa_value *k2 = opa_value_iter(&obj->hdr, k1); + opa_value *k3 = opa_value_iter(&obj->hdr, k2); + + opa_value *exp1 = opa_string_terminated("b"); + opa_value *exp2 = opa_string_terminated("a"); + opa_value *exp3 = NULL; + + if (opa_value_compare(k1, exp1) != 0) + { + test_fatal("object iter start did not return expected value"); + } + + if (opa_value_compare(k2, exp2) != 0) + { + test_fatal("object iter second did not return expected value"); + } + + if (opa_value_compare(k3, exp3) != 0) + { + test_fatal("object iter third did not return expected value"); + } +} + +WASM_EXPORT(test_opa_value_iter_array) +void test_opa_value_iter_array(void) +{ + opa_array_t *arr = opa_cast_array(opa_array()); + + opa_array_append(arr, opa_number_int(1)); + opa_array_append(arr, opa_number_int(2)); + + opa_value *k1 = opa_value_iter(&arr->hdr, NULL); + opa_value *k2 = opa_value_iter(&arr->hdr, k1); + opa_value *k3 = opa_value_iter(&arr->hdr, k2); + + opa_value *exp1 = opa_number_int(0); + opa_value *exp2 = opa_number_int(1); + opa_value *exp3 = NULL; + + if (opa_value_compare(k1, exp1) != 0) + { + test_fatal("array iter start did not return expected value"); + } + + if (opa_value_compare(k2, exp2) != 0) + { + test_fatal("array iter second did not return expected value"); + } + + if (opa_value_compare(k3, exp3) != 0) + { + test_fatal("array iter third did not return expected value"); + } +} + +WASM_EXPORT(test_opa_value_iter_set) +void test_opa_value_iter_set(void) +{ + opa_set_t *set = opa_cast_set(opa_set()); + + opa_set_add(set, opa_number_int(1)); + opa_set_add(set, opa_number_int(2)); + + opa_value *v1 = opa_value_iter(&set->hdr, NULL); + opa_value *v2 = opa_value_iter(&set->hdr, v1); + opa_value *v3 = opa_value_iter(&set->hdr, v2); + + opa_value *exp1 = opa_number_int(1); + opa_value *exp2 = opa_number_int(2); + opa_value *exp3 = NULL; + + if (opa_value_compare(v1, exp1) != 0) + { + test_fatal("set iter did not return expected value"); + } + + if (opa_value_compare(v2, exp2) != 0) + { + test_fatal("set iter second did not return expected value"); + } + + if (opa_value_compare(v3, exp3) != 0) + { + test_fatal("set iter third did not return expected value"); + } +} + +WASM_EXPORT(test_opa_value_merge_scalars) +void test_opa_value_merge_scalars(void) +{ + opa_value *result = opa_value_merge(opa_number_int(1), opa_string_terminated("foo")); + + if (result == NULL) + { + test_fatal("merge of two scalars failed"); + } + else if (opa_value_compare(result, opa_number_int(1)) != 0) + { + test_fatal("scalar merge returned unexpected result"); + } +} + +WASM_EXPORT(test_opa_value_merge_first_operand_null) +void test_opa_value_merge_first_operand_null(void) +{ + test_str_eq("second operand string returns string", "\"foo\"", opa_json_dump(opa_value_merge(NULL, opa_string_terminated("foo")))); + test_str_eq("second operand object returns object", "{}", opa_json_dump(opa_value_merge(NULL, opa_object()))); + test_str_eq("second operand number returns number", "1", opa_json_dump(opa_value_merge(NULL, opa_number_int(1)))); + test_str_eq("second operand array returns array", "[]", opa_json_dump(opa_value_merge(NULL, opa_array()))); + test_str_eq("second operand set returns set", "set()", opa_value_dump(opa_value_merge(NULL, opa_set()))); + test_str_eq("second operand null returns null", "null", opa_json_dump(opa_value_merge(NULL, opa_null()))); +} + +WASM_EXPORT(test_opa_value_merge_simple) +void test_opa_value_merge_simple(void) +{ + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_t *obj2 = opa_cast_object(opa_object()); + + opa_object_insert(obj1, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj2, opa_string_terminated("b"), opa_number_int(2)); + + opa_object_t *exp1 = opa_cast_object(opa_object()); + opa_object_insert(exp1, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(exp1, opa_string_terminated("b"), opa_number_int(2)); + + opa_value *result = opa_value_merge(&obj1->hdr, &obj2->hdr); + + if (result == NULL) + { + test_fatal("object merge failed"); + } + else if (opa_value_compare(result, &exp1->hdr) != 0) + { + test_fatal("object merge returned unexpected result"); + } +} + + +WASM_EXPORT(test_opa_value_merge_nested) +void test_opa_value_merge_nested(void) +{ + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_t *obj1a = opa_cast_object(opa_object()); + + opa_object_insert(obj1a, opa_string_terminated("b"), opa_number_int(1)); + opa_object_insert(obj1, opa_string_terminated("a"), &obj1a->hdr); + opa_object_insert(obj1, opa_string_terminated("c"), opa_number_int(2)); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_t *obj2a = opa_cast_object(opa_object()); + + opa_object_insert(obj2a, opa_string_terminated("d"), opa_number_int(3)); + opa_object_insert(obj2, opa_string_terminated("a"), &obj2a->hdr); + opa_object_insert(obj2, opa_string_terminated("e"), opa_number_int(4)); + + opa_object_t *exp1 = opa_cast_object(opa_object()); + opa_object_t *exp1a = opa_cast_object(opa_object()); + + opa_object_insert(exp1a, opa_string_terminated("b"), opa_number_int(1)); + opa_object_insert(exp1a, opa_string_terminated("d"), opa_number_int(3)); + opa_object_insert(exp1, opa_string_terminated("a"), &exp1a->hdr); + opa_object_insert(exp1, opa_string_terminated("c"), opa_number_int(2)); + opa_object_insert(exp1, opa_string_terminated("e"), opa_number_int(4)); + + opa_value *result = opa_value_merge(&obj1->hdr, &obj2->hdr); + + if (result == NULL) + { + test_fatal("object merge failed"); + } + else if (opa_value_compare(&exp1->hdr, result) != 0) + { + test_fatal("object merge returned unexpected result"); + } +} + +WASM_EXPORT(test_opa_value_shallow_copy) +void test_opa_value_shallow_copy(void) +{ + // construct a value that has one of each type + char str[] = "{\"a\": [1, true, null, 2.5]}"; + opa_value *obj = opa_json_parse(str, sizeof(str)); + opa_set_t *set = opa_cast_set(opa_set()); + opa_set_add(set, obj); + + opa_value *cpy = opa_value_shallow_copy(&set->hdr); + + if (opa_value_compare(cpy, &set->hdr) != 0) + { + test_fatal("expected original and shallow copy to be equal"); + } +} + +WASM_EXPORT(test_opa_json_dump) +void test_opa_json_dump(void) +{ + test("null", opa_strcmp(opa_json_dump(opa_null()), "null") == 0); + test("false", opa_strcmp(opa_json_dump(opa_boolean(false)), "false") == 0); + test("true", opa_strcmp(opa_json_dump(opa_boolean(true)), "true") == 0); + test("strings", opa_strcmp(opa_json_dump(opa_string_terminated("hello\"world")), "\"hello\\\"world\"") == 0); + test("strings utf-8", opa_strcmp(opa_json_dump(opa_string_terminated("\xed\xba\xad")), "\"\xed\xba\xad\"") == 0); + test("numbers", opa_strcmp(opa_json_dump(opa_number_int(127)), "127") == 0); + + test_str_eq("numbers/float", "12345.678", opa_json_dump(opa_number_float(12345.678))); + test_str_eq("numbers/float", "10.5", opa_json_dump(opa_number_float(10.5))); + + opa_value *arr = opa_array(); + test("arrays", opa_strcmp(opa_json_dump(arr), "[]") == 0); + + opa_array_append(opa_cast_array(arr), opa_string_terminated("hello")); + test("arrays", opa_strcmp(opa_json_dump(arr), "[\"hello\"]") == 0); + + opa_array_append(opa_cast_array(arr), opa_string_terminated("world")); + test("arrays", opa_strcmp(opa_json_dump(arr), "[\"hello\",\"world\"]") == 0); + + opa_value *set = opa_set(); + test("sets", opa_strcmp(opa_json_dump(set), "[]") == 0); + + opa_set_add(opa_cast_set(set), opa_string_terminated("hello")); + test("sets", opa_strcmp(opa_json_dump(set), "[\"hello\"]") == 0); + + opa_set_add(opa_cast_set(set), opa_string_terminated("world")); + test("sets", opa_strcmp(opa_json_dump(set), "[\"hello\",\"world\"]") == 0); + + opa_value *obj = opa_object(); + test("objects", opa_strcmp(opa_json_dump(obj), "{}") == 0); + + opa_object_insert(opa_cast_object(obj), opa_string_terminated("k1"), opa_string_terminated("v1")); + test("objects", opa_strcmp(opa_json_dump(obj), "{\"k1\":\"v1\"}") == 0); + + opa_object_insert(opa_cast_object(obj), opa_string_terminated("k2"), opa_string_terminated("v2")); + test("objects", opa_strcmp(opa_json_dump(obj), "{\"k1\":\"v1\",\"k2\":\"v2\"}") == 0); + + opa_value *terminators = opa_array(); + opa_array_append(opa_cast_array(terminators), opa_boolean(true)); + opa_array_append(opa_cast_array(terminators), opa_boolean(false)); + opa_array_append(opa_cast_array(terminators), opa_null()); + + test("bool/null terminators", opa_strcmp(opa_json_dump(terminators), "[true,false,null]") == 0); + + opa_value *non_string_keys = opa_object(); + opa_array_t *arrk = opa_cast_array(opa_array()); + opa_array_append(arrk, opa_number_int(1)); + opa_object_insert(opa_cast_object(non_string_keys), &arrk->hdr, opa_number_int(1)); + test_str_eq("objects/non string keys", opa_json_dump(non_string_keys), "{\"[1]\":1}"); +} + +WASM_EXPORT(test_opa_value_dump) +void test_opa_value_dump(void) +{ + test("empty sets", opa_strcmp(opa_value_dump(opa_set()), "set()") == 0); + + opa_set_t *set = opa_cast_set(opa_set()); + opa_set_add(set, opa_number_int(1)); + + test("sets of one", opa_strcmp(opa_value_dump(&set->hdr), "{1}") == 0); + + opa_set_add(set, opa_number_int(2)); + test("sets", opa_strcmp(opa_value_dump(&set->hdr), "{1,2}") == 0); + + opa_value *non_string_keys = opa_object(); + opa_array_t *arrk = opa_cast_array(opa_array()); + opa_array_append(arrk, opa_number_int(1)); + opa_object_insert(opa_cast_object(non_string_keys), &arrk->hdr, opa_number_int(1)); + test_str_eq("objects/non string keys", opa_value_dump(non_string_keys), "{[1]:1}"); +} + +WASM_EXPORT(test_arithmetic) +void test_arithmetic(void) +{ + long long i = 0; + + test("abs +1", opa_number_try_int(opa_cast_number(opa_arith_abs(opa_number_int(1))), &i) == 0 && i == 1); + test("abs -1", opa_number_try_int(opa_cast_number(opa_arith_abs(opa_number_int(-1))), &i) == 0 && i == 1); + test("abs 1.5 (float)", opa_number_as_float(opa_cast_number(opa_arith_abs(opa_number_float(1.5)))) == 1.5); + test("abs -1.5 (float)", opa_number_as_float(opa_cast_number(opa_arith_abs(opa_number_float(-1.5)))) == 1.5); + test("abs 1.5 (ref)", opa_number_as_float(opa_cast_number(opa_arith_abs(opa_number_ref("1.5", 3)))) == 1.5); + test("abs -1.5 (ref)", opa_number_as_float(opa_cast_number(opa_arith_abs(opa_number_ref("-1.5", 4)))) == 1.5); + test("round 1", opa_number_try_int(opa_cast_number(opa_arith_round(opa_number_int(1))), &i) == 0 && i == 1); + test("round -1", opa_number_try_int(opa_cast_number(opa_arith_round(opa_number_int(-1))), &i) == 0 && i == -1); + test("round 1.4 (float)", opa_number_as_float(opa_cast_number(opa_arith_round(opa_number_float(1.4)))) == 1); + test("round -1.4 (float)", opa_number_as_float(opa_cast_number(opa_arith_round(opa_number_float(-1.4)))) == -1); + test("round 1.5 (float)", opa_number_as_float(opa_cast_number(opa_arith_round(opa_number_float(1.5)))) == 2); + test("round -1.5 (float)", opa_number_as_float(opa_cast_number(opa_arith_round(opa_number_float(-1.5)))) == -2); + test("round 2.5 (float)", opa_number_as_float(opa_cast_number(opa_arith_round(opa_number_float(2.5)))) == 3); + test("round -2.5 (float)", opa_number_as_float(opa_cast_number(opa_arith_round(opa_number_float(-2.5)))) == -3); + test("ceil 1", opa_number_as_float(opa_cast_number(opa_arith_ceil(opa_number_int(1)))) == 1); + test("ceil 1.01 (float)", opa_number_as_float(opa_cast_number(opa_arith_ceil(opa_number_float(1.01)))) == 2); + test("ceil -1.99999 (float)", opa_number_as_float(opa_cast_number(opa_arith_ceil(opa_number_float(-1.99999)))) == -1); + test("floor 1", opa_number_as_float(opa_cast_number(opa_arith_floor(opa_number_int(1)))) == 1); + test("floor 1.01 (float)", opa_number_as_float(opa_cast_number(opa_arith_floor(opa_number_float(1.01)))) == 1); + test("floor -1.99999 (float)", opa_number_as_float(opa_cast_number(opa_arith_floor(opa_number_float(-1.99999)))) == -2); + test("plus 1+2", opa_number_as_float(opa_cast_number(opa_arith_plus(opa_number_float(1), opa_number_float(2)))) == 3); + test("minus 3-2", opa_number_as_float(opa_cast_number(opa_arith_minus(opa_number_float(3), opa_number_float(2)))) == 1); + + opa_set_t *s1 = opa_cast_set(opa_set()); + opa_set_add(s1, opa_number_int(0)); + opa_set_add(s1, opa_number_int(1)); + opa_set_add(s1, opa_number_int(2)); + + opa_set_t *s2 = opa_cast_set(opa_set()); + opa_set_add(s2, opa_number_int(0)); + opa_set_add(s2, opa_number_int(2)); + + opa_set_t *s3 = opa_cast_set(opa_arith_minus(&s1->hdr, &s2->hdr)); + test("minus set", s3->len == 1 && opa_set_get(s3, opa_number_int(1)) != NULL); + test("multiply 3*2", opa_number_as_float(opa_cast_number(opa_arith_multiply(opa_number_float(3), opa_number_float(2)))) == 6); + test("divide 3/2", opa_number_as_float(opa_cast_number(opa_arith_divide(opa_number_float(3), opa_number_float(2)))) == 1.5); + test("divide 3/0", opa_arith_divide(opa_number_float(3), opa_number_float(0)) == NULL); + test("remainder 5 % 2", opa_number_as_float(opa_cast_number(opa_arith_rem(opa_number_float(5), opa_number_float(2)))) == 1); + test("remainder 1.1 % 1", opa_arith_rem(opa_number_float(1.1), opa_number_float(1)) == NULL); + test("remainder 1 % 1.1", opa_arith_rem(opa_number_float(1), opa_number_float(1.1)) == NULL); + test("remainder 1 % 0", opa_arith_rem(opa_number_float(1), opa_number_float(0)) == NULL); +} + +WASM_EXPORT(test_set_diff) +void test_set_diff(void) +{ + // test_arithmetic covers the diff. +} + +WASM_EXPORT(test_set_intersection_union) +void test_set_intersection_union(void) +{ + opa_set_t *s1 = opa_cast_set(opa_set()); + opa_set_add(s1, opa_number_int(0)); + opa_set_add(s1, opa_number_int(1)); + opa_set_add(s1, opa_number_int(2)); + + opa_set_t *s2 = opa_cast_set(opa_set()); + opa_set_add(s2, opa_number_int(0)); + opa_set_add(s2, opa_number_int(1)); + + opa_set_t *r = opa_cast_set(opa_set_intersection(&s1->hdr, &s2->hdr)); + test("set/intersection", r->len == 2 && opa_set_get(r, opa_number_int(0)) != NULL && opa_set_get(r, opa_number_int(1)) != NULL); + + r = opa_cast_set(opa_set_union(&s1->hdr, &s2->hdr)); + test("set/union", r->len == 3 && + opa_set_get(r, opa_number_int(0)) != NULL && + opa_set_get(r, opa_number_int(1)) != NULL && + opa_set_get(r, opa_number_int(2)) != NULL); +} + + +WASM_EXPORT(test_sets_intersection_union) +void test_sets_intersection_union(void) +{ + opa_set_t *s1 = opa_cast_set(opa_set()); + opa_set_add(s1, opa_number_int(0)); + opa_set_add(s1, opa_number_int(1)); + opa_set_add(s1, opa_number_int(2)); + + opa_set_t *s2 = opa_cast_set(opa_set()); + opa_set_add(s2, opa_number_int(0)); + opa_set_add(s2, opa_number_int(1)); + + opa_set_t *s3 = opa_cast_set(opa_set()); + opa_set_add(s3, opa_number_int(0)); + + opa_set_t *sets = opa_cast_set(opa_set()); + opa_set_add(sets, &s1->hdr); + opa_set_add(sets, &s2->hdr); + opa_set_add(sets, &s3->hdr); + + opa_set_t *r = opa_cast_set(opa_sets_intersection(&sets->hdr)); + test("sets/intersection", r->len == 1 && opa_set_get(r, opa_number_int(0)) != NULL); + + r = opa_cast_set(opa_sets_union(&sets->hdr)); + test("sets/union", r->len == 3 && + opa_set_get(r, opa_number_int(0)) != NULL && + opa_set_get(r, opa_number_int(1)) != NULL && + opa_set_get(r, opa_number_int(2)) != NULL); +} + +WASM_EXPORT(test_array) +void test_array(void) +{ + opa_array_t *arr1 = opa_cast_array(opa_array()); + opa_array_append(arr1, opa_number_int(0)); + opa_array_append(arr1, opa_number_int(1)); + + opa_array_t *arr2 = opa_cast_array(opa_array()); + opa_array_append(arr2, opa_number_int(2)); + opa_array_append(arr2, opa_number_int(3)); + + opa_array_t *r = opa_cast_array(opa_array_concat(&arr1->hdr, &arr2->hdr)); + + test("array_concat", r->len == 4 && + opa_value_compare(r->elems[0].v, opa_number_int(0)) == 0 && + opa_value_compare(r->elems[1].v, opa_number_int(1)) == 0 && + opa_value_compare(r->elems[2].v, opa_number_int(2)) == 0 && + opa_value_compare(r->elems[3].v, opa_number_int(3)) == 0); + + r = opa_cast_array(opa_array_slice(&r->hdr, opa_number_int(1), opa_number_int(3))); + + test("array_slice", r->len == 2 && + opa_value_compare(r->elems[0].v, opa_number_int(1)) == 0 && + opa_value_compare(r->elems[1].v, opa_number_int(2)) == 0); + + opa_array_t *arr3 = opa_cast_array(opa_array()); + opa_array_append(arr3, opa_number_int(0)); + opa_array_append(arr3, opa_number_int(1)); + opa_array_append(arr3, opa_number_int(2)); + + r = opa_cast_array(opa_array_reverse(&arr3->hdr)); + test("array_reverse", r->len == 3 && + opa_value_compare(r->elems[0].v, opa_number_int(2)) == 0 && + opa_value_compare(r->elems[1].v, opa_number_int(1)) == 0 && + opa_value_compare(r->elems[2].v, opa_number_int(0)) == 0); +} + +WASM_EXPORT(test_types) +void test_types(void) +{ + test("is_number", opa_value_compare(opa_types_is_number(opa_number_int(0)), opa_boolean(true)) == 0); + test("is_number", opa_value_compare(opa_types_is_number(opa_null()), opa_boolean(false)) == 0); + test("is_string", opa_value_compare(opa_types_is_string(opa_string("a", 1)), opa_boolean(true)) == 0); + test("is_string", opa_value_compare(opa_types_is_string(opa_null()), opa_boolean(false)) == 0); + test("is_boolean", opa_value_compare(opa_types_is_boolean(opa_boolean(true)), opa_boolean(true)) == 0); + test("is_boolean", opa_value_compare(opa_types_is_boolean(opa_null()), opa_boolean(false)) == 0); + test("is_array", opa_value_compare(opa_types_is_array(opa_array()), opa_boolean(true)) == 0); + test("is_array", opa_value_compare(opa_types_is_array(opa_null()), opa_boolean(false)) == 0); + test("is_set", opa_value_compare(opa_types_is_set(opa_set()), opa_boolean(true)) == 0); + test("is_set", opa_value_compare(opa_types_is_set(opa_null()), opa_boolean(false)) == 0); + test("is_object", opa_value_compare(opa_types_is_object(opa_object()), opa_boolean(true)) == 0); + test("is_object", opa_value_compare(opa_types_is_object(opa_null()), opa_boolean(false)) == 0); + test("is_null", opa_value_compare(opa_types_is_null(opa_null()), opa_boolean(true)) == 0); + test("is_null", opa_value_compare(opa_types_is_null(opa_number_int(0)), opa_boolean(false)) == 0); + + test("name/null", opa_value_compare(opa_types_name(opa_null()), opa_string("null", 4)) == 0); + test("name/boolean", opa_value_compare(opa_types_name(opa_boolean(true)), opa_string("boolean", 7)) == 0); + test("name/number", opa_value_compare(opa_types_name(opa_number_int(0)), opa_string("number", 6)) == 0); + test("name/string", opa_value_compare(opa_types_name(opa_string("a", 1)), opa_string("string", 6)) == 0); + test("name/array", opa_value_compare(opa_types_name(opa_array()), opa_string("array", 5)) == 0); + test("name/object", opa_value_compare(opa_types_name(opa_object()), opa_string("object", 6)) == 0); + test("name/set", opa_value_compare(opa_types_name(opa_set()), opa_string("set", 3)) == 0); +} + +static opa_value *number(const char *s) +{ + size_t n = strlen(s); + uint8_t sign = MPD_POS; + size_t pos = 2; + + if (s[0] == '-') + { + sign = MPD_NEG; + pos = 3; + } + + int digits = n - pos; + uint16_t rdata[digits]; + + for (int i = 0; i < digits; i++) + { + int c = s[pos+i] & 0xff; + if (isdigit(c)) + { + c -= '0'; + } else if (isalpha(c)) { + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + } + + rdata[digits - i - 1] = c; + } + + uint32_t status = 0; + mpd_t *r = mpd_qnew(); + mpd_qimport_u16(r, &rdata[0], digits, sign, 16, mpd_max_ctx(), &status); + return opa_bf_to_number(r); +} + +WASM_EXPORT(test_bits) +void test_bits(void) +{ + // tests from https://golang.org/src/math/big/int_test.go L1193 + + struct and_or_xor_test + { + const char *x; + const char *y; + const char *and; + const char *or; + const char *xor; + }; + + struct and_or_xor_test tests1[] = { + {"0x00", "0x00", "0x00", "0x00", "0x00"}, + {"0x00", "0x01", "0x00", "0x01", "0x01"}, + {"0x01", "0x00", "0x00", "0x01", "0x01"}, + {"-0x01", "0x00", "0x00", "-0x01", "-0x01"}, + {"-0xaf", "-0x50", "-0xf0", "-0x0f", "0xe1"}, + {"0x00", "-0x01", "0x00", "-0x01", "-0x01"}, + {"0x01", "0x01", "0x01", "0x01", "0x00"}, + {"-0x01", "-0x01", "-0x01", "-0x01", "0x00"}, + {"0x07", "0x08", "0x00", "0x0f", "0x0f"}, + {"0x05", "0x0f", "0x05", "0x0f", "0x0a"}, + {"0xff", "-0x0a", "0xf6", "-0x01", "-0xf7"}, + {"0x013ff6", "0x9a4e", "0x1a46", "0x01bffe", "0x01a5b8"}, + {"-0x013ff6", "0x9a4e", "0x800a", "-0x0125b2", "-0x01a5bc"}, + {"-0x013ff6", "-0x9a4e", "-0x01bffe", "-0x1a46", "0x01a5b8"}, + { + "0x1000009dc6e3d9822cba04129bcbe3401", + "0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", + "0x1000001186210100001000009048c2001", + "0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd", + "0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc", + }, + { + "0x1000009dc6e3d9822cba04129bcbe3401", + "-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", + "0x8c40c2d8822caa04120b8321401", + "-0xb9bd7d543685789d57ca918e82229142459020483cd2014001fd", + "-0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fe", + }, + { + "-0x1000009dc6e3d9822cba04129bcbe3401", + "-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", + "-0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd", + "-0x1000001186210100001000009048c2001", + "0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc", + }, + }; + + for (int i = 0; i < sizeof(tests1)/sizeof(tests1[0]); i++) { + test("and", opa_value_compare(number(tests1[i].and), opa_bits_and(number(tests1[i].x), number(tests1[i].y))) == 0); + test("or", opa_value_compare(number(tests1[i].or), opa_bits_or(number(tests1[i].x), number(tests1[i].y))) == 0); + test("xor", opa_value_compare(number(tests1[i].xor), opa_bits_xor(number(tests1[i].x), number(tests1[i].y))) == 0); + } + + // tests from https://golang.org/src/math/big/int_test.go L1496 + + struct negate_test + { + const char *input; + const char *output; + }; + + struct negate_test tests2[] = { + {"0", "-1"}, + {"1", "-2"}, + {"7", "-8"}, + {"0", "-1"}, + {"-81910", "81909"}, + { + "298472983472983471903246121093472394872319615612417471234712061", + "-298472983472983471903246121093472394872319615612417471234712062", + }, + }; + + for (int i = 0; i < sizeof(tests2)/sizeof(tests2[0]); i++) { + test("negate", opa_value_compare(opa_number_ref(tests2[i].output, strlen(tests2[i].output)), + opa_bits_negate(opa_number_ref(tests2[i].input, strlen(tests2[i].input)))) == 0); + test("negate", opa_value_compare(opa_number_ref(tests2[i].input, strlen(tests2[i].input)), + opa_bits_negate(opa_number_ref(tests2[i].output, strlen(tests2[i].output)))) == 0); + } + + // tests from https://golang.org/src/math/big/int_test.go L883 + + struct shift_test + { + const char *input; + int shift; + const char *output; + }; + + struct shift_test tests3[] = { + {"0", 0, "0"}, + {"-0", 0, "0"}, + {"0", 1, "0"}, + {"0", 2, "0"}, + {"1", 0, "1"}, + {"1", 1, "0"}, + {"1", 2, "0"}, + {"2", 0, "2"}, + {"2", 1, "1"}, + {"-1", 0, "-1"}, + {"-1", 1, "-1"}, + {"-1", 10, "-1"}, + {"-100", 2, "-25"}, + {"-100", 3, "-13"}, + {"-100", 100, "-1"}, + {"4294967296", 0, "4294967296"}, + {"4294967296", 1, "2147483648"}, + {"4294967296", 2, "1073741824"}, + {"18446744073709551616", 0, "18446744073709551616"}, + {"18446744073709551616", 1, "9223372036854775808"}, + {"18446744073709551616", 2, "4611686018427387904"}, + {"18446744073709551616", 64, "1"}, + {"340282366920938463463374607431768211456", 64, "18446744073709551616"}, + {"340282366920938463463374607431768211456", 128, "1"}, + }; + + for (int i = 0; i < sizeof(tests3)/sizeof(tests3[0]); i++) { + test("right shift", opa_value_compare(opa_number_ref(tests3[i].output, strlen(tests3[i].output)), + opa_bits_shiftright(opa_number_ref(tests3[i].input, strlen(tests3[i].input)), + opa_number_int(tests3[i].shift))) == 0); + }; + + // tests from https://golang.org/src/math/big/int_test.go L940 + + struct shift_test tests4[] = { + {"0", 0, "0"}, + {"0", 1, "0"}, + {"0", 2, "0"}, + {"1", 0, "1"}, + {"1", 1, "2"}, + {"1", 2, "4"}, + {"2", 0, "2"}, + {"2", 1, "4"}, + {"2", 2, "8"}, + {"-87", 1, "-174"}, + {"4294967296", 0, "4294967296"}, + {"4294967296", 1, "8589934592"}, + {"4294967296", 2, "17179869184"}, + {"18446744073709551616", 0, "18446744073709551616"}, + {"9223372036854775808", 1, "18446744073709551616"}, + {"4611686018427387904", 2, "18446744073709551616"}, + {"1", 64, "18446744073709551616"}, + {"18446744073709551616", 64, "340282366920938463463374607431768211456"}, + {"1", 128, "340282366920938463463374607431768211456"}, + }; + + for (int i = 0; i < sizeof(tests4)/sizeof(tests4[0]); i++) { + test("left shift", opa_value_compare(opa_number_ref(tests4[i].output, strlen(tests4[i].output)), + opa_bits_shiftleft(opa_number_ref(tests4[i].input, strlen(tests4[i].input)), + opa_number_int(tests4[i].shift))) == 0); + }; +} + +WASM_EXPORT(test_aggregates) +void test_aggregates(void) +{ + opa_array_t *arr = opa_cast_array(opa_array()); + opa_array_append(arr, opa_number_int(2)); + opa_array_append(arr, opa_number_int(1)); + opa_array_append(arr, opa_number_int(4)); + + opa_array_t *arr_sorted = opa_cast_array(opa_array()); + opa_array_append(arr_sorted, opa_number_int(1)); + opa_array_append(arr_sorted, opa_number_int(2)); + opa_array_append(arr_sorted, opa_number_int(4)); + + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("b"), opa_number_int(2)); + opa_object_insert(obj, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj, opa_string_terminated("c"), opa_number_int(4)); + + opa_set_t *set = opa_cast_set(opa_set()); + opa_set_add(set, opa_number_int(2)); + opa_set_add(set, opa_number_int(1)); + opa_set_add(set, opa_number_int(4)); + + test("count/string", opa_value_compare(opa_agg_count(opa_string("foo", 3)), opa_number_int(3)) == 0); + test("count/unicode string", opa_value_compare(opa_agg_count(opa_string("\xC3\xA5\xC3\xA4\xC3\xB6", 6)), opa_number_int(3)) == 0); + test("count/array", opa_value_compare(opa_agg_count(&arr->hdr), opa_number_int(3)) == 0); + test("count/object", opa_value_compare(opa_agg_count(&obj->hdr), opa_number_int(3)) == 0); + test("count/set", opa_value_compare(opa_agg_count(&set->hdr), opa_number_int(3)) == 0); + + test("sum/array", opa_value_compare(opa_agg_sum(&arr->hdr), opa_number_int(7)) == 0); + test("sum/set", opa_value_compare(opa_agg_sum(&set->hdr), opa_number_int(7)) == 0); + + test("product/array", opa_value_compare(opa_agg_product(&arr->hdr), opa_number_int(8)) == 0); + test("product/set", opa_value_compare(opa_agg_product(&set->hdr), opa_number_int(8)) == 0); + + test("max/array", opa_value_compare(opa_agg_max(&arr->hdr), opa_number_int(4)) == 0); + test("max/set", opa_value_compare(opa_agg_max(&set->hdr), opa_number_int(4)) == 0); + + test("min/array", opa_value_compare(opa_agg_min(&arr->hdr), opa_number_int(1)) == 0); + test("min/set", opa_value_compare(opa_agg_min(&set->hdr), opa_number_int(1)) == 0); + + test("sort/array", opa_value_compare(opa_agg_sort(&arr->hdr), &arr_sorted->hdr) == 0); + test("sort/set", opa_value_compare(opa_agg_sort(&set->hdr), &arr_sorted->hdr) == 0); + + opa_array_t *arr_trues = opa_cast_array(opa_array()); + opa_array_append(arr_trues, opa_boolean(true)); + opa_array_append(arr_trues, opa_boolean(true)); + + opa_array_t *arr_mixed = opa_cast_array(opa_array()); + opa_array_append(arr_mixed, opa_boolean(true)); + opa_array_append(arr_mixed, opa_boolean(false)); + + opa_array_t *arr_falses = opa_cast_array(opa_array()); + opa_array_append(arr_falses, opa_boolean(false)); + opa_array_append(arr_falses, opa_boolean(false)); + + test("all/array trues", opa_value_compare(opa_agg_all(&arr_trues->hdr), opa_boolean(true)) == 0); + test("all/array mixed", opa_value_compare(opa_agg_all(&arr_mixed->hdr), opa_boolean(false)) == 0); + test("all/array falses", opa_value_compare(opa_agg_all(&arr_falses->hdr), opa_boolean(false)) == 0); + test("any/array trues", opa_value_compare(opa_agg_any(&arr_trues->hdr), opa_boolean(true)) == 0); + test("any/array mixed", opa_value_compare(opa_agg_any(&arr_mixed->hdr), opa_boolean(true)) == 0); + test("any/array falses", opa_value_compare(opa_agg_any(&arr_falses->hdr), opa_boolean(false)) == 0); + + opa_set_t *set_trues = opa_cast_set(opa_set()); + opa_set_add(set_trues, opa_boolean(true)); + opa_set_add(set_trues, opa_boolean(true)); + + opa_set_t *set_mixed = opa_cast_set(opa_set()); + opa_set_add(set_mixed, opa_boolean(true)); + opa_set_add(set_mixed, opa_boolean(false)); + + opa_set_t *set_falses = opa_cast_set(opa_set()); + opa_set_add(set_falses, opa_boolean(false)); + opa_set_add(set_falses, opa_boolean(false)); + + test("all/set trues", opa_value_compare(opa_agg_all(&set_trues->hdr), opa_boolean(true)) == 0); + test("all/set mixed", opa_value_compare(opa_agg_all(&set_mixed->hdr), opa_boolean(false)) == 0); + test("all/set falses", opa_value_compare(opa_agg_all(&set_falses->hdr), opa_boolean(false)) == 0); + test("any/set trues", opa_value_compare(opa_agg_any(&set_trues->hdr), opa_boolean(true)) == 0); + test("any/set mixed", opa_value_compare(opa_agg_any(&set_mixed->hdr), opa_boolean(true)) == 0); + test("any/set falses", opa_value_compare(opa_agg_any(&set_falses->hdr), opa_boolean(false)) == 0); +} + +WASM_EXPORT(test_base64) +void test_base64(void) +{ + test("base64/is_valid", opa_value_compare(opa_base64_is_valid(opa_string_terminated("YWJjMTIzIT8kKiYoKSctPUB+")), opa_boolean(true)) == 0); + test("base64/encode", opa_value_compare(opa_base64_encode(opa_string_terminated("abc123!?$*&()'-=@~")), opa_string_terminated("YWJjMTIzIT8kKiYoKSctPUB+")) == 0); + test("base64/encode", opa_value_compare(opa_base64_encode(opa_string_terminated("This is a long string that should not be split to many lines")), + opa_string_terminated("VGhpcyBpcyBhIGxvbmcgc3RyaW5nIHRoYXQgc2hvdWxkIG5vdCBiZSBzcGxpdCB0byBtYW55IGxpbmVz")) == 0); + test("base64/decode", opa_value_compare(opa_base64_decode(opa_string_terminated("YWJjMTIzIT8kKiYoKSctPUB+")), opa_string_terminated("abc123!?$*&()'-=@~")) == 0); + test("base64/decode", opa_value_compare(opa_base64_decode(opa_string_terminated("VGhpcyBpcyBhIGxvbmcgc3RyaW5nIHRoYXQgc2hvdWxkIG5vdCBiZSBzcGxpdCB0byBtYW55IGxpbmVz")), + opa_string_terminated("This is a long string that should not be split to many lines")) == 0); + test("base64/decode", opa_value_compare(opa_base64_decode(opa_string_terminated("VGhpcyBpcyBhIGxvbmcgc3RyaW5nIHRoYXQgY2FuIGJlIHBhcnNlZCBldmVuIGlmIHNwbGl0IHRv\nIG1hbnkgbGluZXM=")), + opa_string_terminated("This is a long string that can be parsed even if split to many lines")) == 0); + test("base64/url_encode", opa_value_compare(opa_base64_url_encode(opa_string_terminated("abc123!?$*&()'-=@~")), opa_string_terminated("YWJjMTIzIT8kKiYoKSctPUB-")) == 0); + test("base64/url_decode", opa_value_compare(opa_base64_url_decode(opa_string_terminated("YWJjMTIzIT8kKiYoKSctPUB-")), opa_string_terminated("abc123!?$*&()'-=@~")) == 0); +} + +WASM_EXPORT(test_json) +void test_json(void) +{ + test("json/marshal", opa_value_compare(opa_json_marshal(opa_string_terminated("string")), opa_string_terminated("\"string\"")) == 0); + test("json/unmarshal", opa_value_compare(opa_json_unmarshal(opa_string_terminated("\"string\"")), opa_string_terminated("string")) == 0); + test("json/is_valid_true", opa_cast_boolean(opa_json_is_valid(opa_string_terminated("\"string\"")))->v); + test("json/is_valid_false", !opa_cast_boolean(opa_json_is_valid(opa_string_terminated("\"string")))->v); +} + +WASM_EXPORT(test_object) +void test_object(void) +{ + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj, opa_string_terminated("b"), opa_number_int(2)); + opa_object_insert(obj, opa_string_terminated("c"), opa_number_int(3)); + + test("object/get (key found)", opa_value_compare(builtin_object_get(&obj->hdr, opa_string_terminated("a"), opa_number_int(2)), opa_number_int(1)) == 0); + test("object/get (string key not found)", opa_value_compare(builtin_object_get(&obj->hdr, opa_string_terminated("d"), opa_number_int(2)), opa_number_int(2)) == 0); + test("object/get (integer key not found)", opa_value_compare(builtin_object_get(&obj->hdr, opa_number_int(1), opa_number_int(2)), opa_number_int(2)) == 0); + test("object/get (boolean default value)", opa_value_compare(builtin_object_get(&obj->hdr, opa_number_int(1), opa_boolean(true)), opa_boolean(true)) == 0); + test("object/get (non-object operand)", opa_value_compare(builtin_object_get(opa_string_terminated("a"), opa_number_int(1), opa_boolean(true)), NULL) == 0); + + opa_object_t *obj_keys = opa_cast_object(opa_object()); + opa_object_insert(obj_keys, opa_string_terminated("a"), opa_number_int(0)); + opa_object_insert(obj_keys, opa_string_terminated("c"), opa_number_int(0)); + + opa_object_t *expected = opa_cast_object(opa_object()); + opa_object_insert(expected, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(expected, opa_string_terminated("c"), opa_number_int(3)); + test("object/filter (object keys)", opa_value_compare(builtin_object_filter(&obj->hdr, &obj_keys->hdr), &expected->hdr) == 0); + + opa_set_t *set_keys = opa_cast_set(opa_set()); + opa_set_add(set_keys, opa_string_terminated("a")); + opa_set_add(set_keys, opa_string_terminated("c")); + test("object/filter (set keys)", opa_value_compare(builtin_object_filter(&obj->hdr, &set_keys->hdr), &expected->hdr) == 0); + + opa_array_t *arr_keys = opa_cast_array(opa_array()); + opa_array_append(arr_keys, opa_string_terminated("a")); + opa_array_append(arr_keys, opa_string_terminated("c")); + test("object/filter (array keys)", opa_value_compare(builtin_object_filter(&obj->hdr, &arr_keys->hdr), &expected->hdr) == 0); +} + +WASM_EXPORT(test_object_keys) +void test_object_keys(void) +{ + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj1, opa_string_terminated("b"), opa_number_int(2)); + opa_object_insert(obj1, opa_string_terminated("c"), opa_number_int(3)); + + opa_set_t *expected_keys1 = opa_cast_set(opa_set()); + opa_set_add(expected_keys1, opa_string_terminated("a")); + opa_set_add(expected_keys1, opa_string_terminated("b")); + opa_set_add(expected_keys1, opa_string_terminated("c")); + + test("object/keys (string keys)", opa_value_compare(builtin_object_keys(&obj1->hdr), &expected_keys1->hdr) == 0); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_number_int(1), opa_number_int(2)); + opa_object_insert(obj2, opa_number_int(3), opa_number_int(4)); + + opa_set_t *expected_keys2 = opa_cast_set(opa_set()); + opa_set_add(expected_keys2, opa_number_int(1)); + opa_set_add(expected_keys2, opa_number_int(3)); + + test("object/keys (number keys)", opa_value_compare(builtin_object_keys(&obj2->hdr), &expected_keys2->hdr) == 0); + + opa_set_t *set_key = opa_cast_set(opa_set()); + opa_set_add(set_key, opa_number_int(1)); + opa_set_add(set_key, opa_number_int(2)); + + opa_object_t *obj3 = opa_cast_object(opa_object()); + opa_object_insert(obj3, &set_key->hdr, opa_number_int(1)); + + opa_set_t *expected_keys3 = opa_cast_set(opa_set()); + opa_set_add(expected_keys3, &set_key->hdr); + + test("object/keys (set keys)", opa_value_compare(builtin_object_keys(&obj3->hdr), &expected_keys3->hdr) == 0); + + opa_object_t *object_key = opa_cast_object(opa_object()); + opa_object_insert(object_key, opa_string_terminated("a"), opa_number_int(1)); + + opa_object_t *obj4 = opa_cast_object(opa_object()); + opa_object_insert(obj4, &object_key->hdr, opa_number_int(1)); + + opa_set_t *expected_keys4 = opa_cast_set(opa_set()); + opa_set_add(expected_keys4, &object_key->hdr); + + test("object/keys (object keys)", opa_value_compare(builtin_object_keys(&obj4->hdr), &expected_keys4->hdr) == 0); + + opa_array_t *array_key = opa_cast_array(opa_array()); + opa_array_append(array_key, opa_number_int(1)); + opa_array_append(array_key, opa_number_int(2)); + + opa_object_t *obj5 = opa_cast_object(opa_object()); + opa_object_insert(obj5, &array_key->hdr, opa_number_int(1)); + + opa_set_t *expected_keys5 = opa_cast_set(opa_set()); + opa_set_add(expected_keys5, &array_key->hdr); + + test("object/keys (array keys)", opa_value_compare(builtin_object_keys(&obj5->hdr), &expected_keys5->hdr) == 0); + + opa_object_t *obj6 = opa_cast_object(opa_object()); + opa_set_t *expected_keys6 = opa_cast_set(opa_set()); + test("object/keys (empty)", opa_value_compare(builtin_object_keys(&obj6->hdr), &expected_keys6->hdr) == 0); + + test("object/keys (null on non-object)", opa_value_compare(builtin_object_keys(opa_number_int(3)), NULL) == 0); +} + +WASM_EXPORT(test_object_remove) +void test_object_remove(void) +{ + opa_object_t *o = opa_cast_object(opa_object()); + opa_object_insert(o, opa_string_terminated("c"), opa_number_int(3)); + + // input -> {"a": 1, "b": {"c": 3}} + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj1, opa_string_terminated("b"), &o->hdr); + + opa_set_t *set_keys1 = opa_cast_set(opa_set()); + opa_set_add(set_keys1, opa_string_terminated("a")); + + opa_object_t *expected1 = opa_cast_object(opa_object()); + opa_object_insert(expected1, opa_string_terminated("b"), &o->hdr); + test("object/remove (base)", opa_value_compare(builtin_object_remove(&obj1->hdr, &set_keys1->hdr), &expected1->hdr) == 0); + + // input -> {"a": 1, "b": {"c": 3}, "d": 4} + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj2, opa_string_terminated("b"), &o->hdr); + opa_object_insert(obj2, opa_string_terminated("d"), opa_number_int(4)); + + opa_set_t *set_keys2 = opa_cast_set(opa_set()); + opa_set_add(set_keys2, opa_string_terminated("b")); + opa_set_add(set_keys2, opa_string_terminated("d")); + + opa_object_t *expected2 = opa_cast_object(opa_object()); + opa_object_insert(expected2, opa_string_terminated("a"), opa_number_int(1)); + test("object/remove (multiple keys set)", opa_value_compare(builtin_object_remove(&obj2->hdr, &set_keys2->hdr), &expected2->hdr) == 0); + + opa_array_t *arr_keys = opa_cast_array(opa_array()); + opa_array_append(arr_keys, opa_string_terminated("b")); + opa_array_append(arr_keys, opa_string_terminated("d")); + test("object/remove (multiple keys array)", opa_value_compare(builtin_object_remove(&obj2->hdr, &arr_keys->hdr), &expected2->hdr) == 0); + + opa_object_t *obj_keys = opa_cast_object(opa_object()); + opa_object_insert(obj_keys, opa_string_terminated("b"), opa_number_int(1)); + opa_object_insert(obj_keys, opa_string_terminated("d"), opa_string_terminated("")); + test("object/remove (multiple keys object)", opa_value_compare(builtin_object_remove(&obj2->hdr, &obj_keys->hdr), &expected2->hdr) == 0); + + // input -> {"a": {"b": {"c": 3}}, "x": 123} + opa_object_t *o2 = opa_cast_object(opa_object()); + opa_object_insert(o2, opa_string_terminated("b"), &o->hdr); + + opa_object_t *obj3 = opa_cast_object(opa_object()); + opa_object_insert(obj3, opa_string_terminated("a"), &o2->hdr); + opa_object_insert(obj3, opa_string_terminated("x"), opa_number_int(123)); + + opa_object_t *o_keys1 = opa_cast_object(opa_object()); + opa_object_insert(o_keys1, opa_string_terminated("foo"), opa_string_terminated("bar")); + + opa_object_t *o_keys2 = opa_cast_object(opa_object()); + opa_object_insert(o_keys2, opa_string_terminated("b"), &o_keys1->hdr); + + opa_object_t *obj_keys2 = opa_cast_object(opa_object()); + opa_object_insert(obj_keys2, opa_string_terminated("a"), &o_keys2->hdr); + + opa_object_t *expected3 = opa_cast_object(opa_object()); + opa_object_insert(expected3, opa_string_terminated("x"), opa_number_int(123)); + test("object/remove (multiple keys object nested)", opa_value_compare(builtin_object_remove(&obj3->hdr, &obj_keys2->hdr), &expected3->hdr) == 0); + + test("object/remove (empty object)", opa_value_compare(builtin_object_remove(opa_object(), &obj_keys2->hdr), opa_object()) == 0); + + test("object/remove (empty keys set)", opa_value_compare(builtin_object_remove(&obj3->hdr, opa_set()), &obj3->hdr) == 0); + + test("object/remove (empty keys array)", opa_value_compare(builtin_object_remove(&obj3->hdr, opa_array()), &obj3->hdr) == 0); + + test("object/remove (empty keys object)", opa_value_compare(builtin_object_remove(&obj3->hdr, opa_object()), &obj3->hdr) == 0); + + test("object/remove (non-object first operand)", opa_value_compare(builtin_object_remove(opa_string_terminated("a"), opa_object()), NULL) == 0); + + opa_set_t *set_keys3 = opa_cast_set(opa_set()); + opa_set_add(set_keys3, opa_string_terminated("foo")); + test("object/remove (key does not exist)", opa_value_compare(builtin_object_remove(&obj3->hdr, &set_keys3->hdr), &obj3->hdr) == 0); + + test("object/remove (second operand not object/set/array)", opa_value_compare(builtin_object_remove(&obj3->hdr, opa_string_terminated("a")), NULL) == 0); +} + +WASM_EXPORT(test_object_union) +void test_object_union(void) +{ + test("object/union (both empty)", opa_value_compare(builtin_object_union(opa_object(), opa_object()), opa_object()) == 0); + + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("a"), opa_number_int(1)); + test("object/union (left empty)", opa_value_compare(builtin_object_union(opa_object(), &obj1->hdr), &obj1->hdr) == 0); + + test("object/union (right empty)", opa_value_compare(builtin_object_union(&obj1->hdr, opa_object()), &obj1->hdr) == 0); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("b"), opa_number_int(2)); + + opa_object_t *expected1 = opa_cast_object(opa_object()); + opa_object_insert(expected1, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(expected1, opa_string_terminated("b"), opa_number_int(2)); + + test("object/union (base)", opa_value_compare(builtin_object_union(&obj1->hdr, &obj2->hdr), &expected1->hdr) == 0); + + opa_object_t *o = opa_cast_object(opa_object()); + opa_object_insert(o, opa_string_terminated("c"), opa_number_int(3)); + opa_object_t *o2 = opa_cast_object(opa_object()); + opa_object_insert(o2, opa_string_terminated("b"), &o->hdr); + opa_object_t *obj3 = opa_cast_object(opa_object()); + opa_object_insert(obj3, opa_string_terminated("a"), &o2->hdr); + + opa_object_t *expected2 = opa_cast_object(opa_object()); + opa_object_insert(expected2, opa_string_terminated("a"),&o2->hdr); + opa_object_insert(expected2, opa_string_terminated("b"), opa_number_int(2)); + + test("object/union (nested)", opa_value_compare(builtin_object_union(&obj3->hdr, &obj2->hdr), &expected2->hdr) == 0); + test("object/union (nested reverse)", opa_value_compare(builtin_object_union(&obj2->hdr, &obj3->hdr), &expected2->hdr) == 0); + + opa_object_t *obj4 = opa_cast_object(opa_object()); + opa_object_insert(obj4, opa_string_terminated("a"), opa_number_int(2)); + + test("object/union (conflict simple)", opa_value_compare(builtin_object_union(&obj1->hdr, &obj4->hdr), &obj4->hdr) == 0); + + opa_object_insert(obj3, opa_string_terminated("d"), opa_number_int(7)); + + test("object/union (conflict nested and extra field)", opa_value_compare(builtin_object_union(&obj1->hdr, &obj3->hdr), &obj3->hdr) == 0); + + // Operand 1 -> {"a": {"b": {"c": 1}}, "e": 1} + opa_object_t *o3 = opa_cast_object(opa_object()); + opa_object_insert(o3, opa_string_terminated("c"), opa_number_int(1)); + opa_object_t *o4 = opa_cast_object(opa_object()); + opa_object_insert(o4, opa_string_terminated("b"), &o3->hdr); + opa_object_t *obj5 = opa_cast_object(opa_object()); + opa_object_insert(obj5, opa_string_terminated("a"), &o4->hdr); + opa_object_insert(obj5, opa_string_terminated("e"), opa_number_int(1)); + + // Operand 2 -> {"a": {"b": "foo", "b1": "bar"}, "d": 7, "e": 17} + opa_object_t *o5 = opa_cast_object(opa_object()); + opa_object_insert(o5, opa_string_terminated("b"), opa_string_terminated("foo")); + opa_object_insert(o5, opa_string_terminated("b1"), opa_string_terminated("bar")); + opa_object_t *obj6 = opa_cast_object(opa_object()); + opa_object_insert(obj6, opa_string_terminated("a"), &o5->hdr); + opa_object_insert(obj6, opa_string_terminated("d"), opa_number_int(7)); + opa_object_insert(obj6, opa_string_terminated("e"), opa_number_int(17)); + + // Expected -> {"a": {"b": "foo", "b1": "bar"}, "d": 7, "e": 17} + opa_object_t *o6 = opa_cast_object(opa_object()); + opa_object_insert(o6, opa_string_terminated("b"), opa_string_terminated("foo")); + opa_object_insert(o6, opa_string_terminated("b1"), opa_string_terminated("bar")); + opa_object_t *expected3 = opa_cast_object(opa_object()); + opa_object_insert(expected3, opa_string_terminated("a"), &o6->hdr); + opa_object_insert(expected3, opa_string_terminated("d"), opa_number_int(7)); + opa_object_insert(expected3, opa_string_terminated("e"), opa_number_int(17)); + + test("object/union (conflict multiple-1)", opa_value_compare(builtin_object_union(&obj5->hdr, &obj6->hdr), &expected3->hdr) == 0); + + // Operand 1 -> {"a": {"b": {"c": 1, "d": 2}}, "e": 1} + opa_object_t *o7 = opa_cast_object(opa_object()); + opa_object_insert(o7, opa_string_terminated("c"), opa_number_int(1)); + opa_object_insert(o7, opa_string_terminated("d"), opa_number_int(2)); + opa_object_t *o8 = opa_cast_object(opa_object()); + opa_object_insert(o8, opa_string_terminated("b"), &o7->hdr); + opa_object_t *obj7 = opa_cast_object(opa_object()); + opa_object_insert(obj7, opa_string_terminated("a"), &o8->hdr); + opa_object_insert(obj7, opa_string_terminated("e"), opa_number_int(1)); + + // Operand 2 -> {"a": {"b": {"c": "foo"}, "b1": "bar"}, "d": 7, "e": 17} + opa_object_t *o9 = opa_cast_object(opa_object()); + opa_object_insert(o9, opa_string_terminated("c"), opa_string_terminated("foo")); + opa_object_t *o10 = opa_cast_object(opa_object()); + opa_object_insert(o10, opa_string_terminated("b"), &o9->hdr); + opa_object_insert(o10, opa_string_terminated("b1"), opa_string_terminated("bar")); + opa_object_t *obj8 = opa_cast_object(opa_object()); + opa_object_insert(obj8, opa_string_terminated("a"), &o10->hdr); + opa_object_insert(obj8, opa_string_terminated("d"), opa_number_int(7)); + opa_object_insert(obj8, opa_string_terminated("e"), opa_number_int(17)); + + // Expected -> {"a": {"b": {"c": "foo", "d": 2}, "b1": "bar"}, "d": 7, "e": 17} + opa_object_t *o11 = opa_cast_object(opa_object()); + opa_object_insert(o11, opa_string_terminated("c"), opa_string_terminated("foo")); + opa_object_insert(o11, opa_string_terminated("d"), opa_number_int(2)); + opa_object_t *o12 = opa_cast_object(opa_object()); + opa_object_insert(o12, opa_string_terminated("b"), &o11->hdr); + opa_object_insert(o12, opa_string_terminated("b1"), opa_string_terminated("bar")); + opa_object_t *expected4 = opa_cast_object(opa_object()); + opa_object_insert(expected4, opa_string_terminated("a"), &o12->hdr); + opa_object_insert(expected4, opa_string_terminated("d"), opa_number_int(7)); + opa_object_insert(expected4, opa_string_terminated("e"), opa_number_int(17)); + + test("object/union (conflict multiple-2)", opa_value_compare(builtin_object_union(&obj7->hdr, &obj8->hdr), &expected4->hdr) == 0); + + opa_object_t *obj9 = opa_cast_object(opa_object()); + opa_object_insert(obj9, opa_string_terminated("a"), opa_string_terminated("foo")); + opa_object_insert(obj9, opa_string_terminated("b"), opa_string_terminated("bar")); + + opa_object_t *obj10 = opa_cast_object(opa_object()); + opa_object_insert(obj10, opa_string_terminated("a"), opa_string_terminated("baz")); + + opa_object_t *expected5 = opa_cast_object(opa_object()); + opa_object_insert(expected5, opa_string_terminated("a"), opa_string_terminated("baz")); + opa_object_insert(expected5, opa_string_terminated("b"), opa_string_terminated("bar")); + + test("object/union (conflict multiple-3)", opa_value_compare(builtin_object_union(&obj9->hdr, &obj10->hdr), &expected5->hdr) == 0); + + test("object/union (non-object first operand)", opa_value_compare(builtin_object_union(opa_string_terminated("a"), opa_object()), NULL) == 0); + + test("object/union (non-object second operand)", opa_value_compare(builtin_object_union(opa_object(), opa_string_terminated("a")), NULL) == 0); +} + +opa_object_t *json_test_fixture_object1(void) +{ + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("c"), opa_number_int(7)); + opa_object_insert(obj1, opa_string_terminated("d"), opa_number_int(8)); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("b"), &obj1->hdr); + + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), &obj2->hdr); + opa_object_insert(obj, opa_string_terminated("e"), opa_number_int(9)); + return obj; +} + +opa_object_t *json_test_fixture_object2(void) +{ + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("c"), opa_number_int(7)); + opa_object_insert(obj1, opa_string_terminated("d"), opa_number_int(8)); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("b"), &obj1->hdr); + opa_object_insert(obj2, opa_string_terminated("e"), opa_number_int(9)); + + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), &obj2->hdr); + + return obj; +} + +opa_object_t *json_test_fixture_object3(void) +{ + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("b"), opa_number_int(7)); + + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), &obj1->hdr); + opa_object_insert(obj, opa_string_terminated("c"), opa_number_int(1)); + + return obj; +} + +opa_object_t *json_test_fixture_object4(void) +{ + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("b"), opa_number_int(7)); + opa_object_insert(obj1, opa_string_terminated("c"), opa_number_int(8)); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("d"), opa_number_int(9)); + + opa_array_t *arr1 = opa_cast_array(opa_array()); + opa_array_append(arr1, &obj1->hdr); + opa_array_append(arr1, &obj2->hdr); + + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), &arr1->hdr); + + return obj; +} + +opa_object_t *json_test_fixture_object5(void) +{ + opa_array_t *arr1 = opa_cast_array(opa_array()); + opa_array_append(arr1, opa_string_terminated("b")); + opa_array_append(arr1, opa_string_terminated("c")); + opa_array_append(arr1, opa_string_terminated("d")); + + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("1"), &arr1->hdr); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("x"), opa_string_terminated("y")); + + opa_array_t *arr2 = opa_cast_array(opa_array()); + opa_array_append(arr2, &obj1->hdr); + opa_array_append(arr2, &obj2->hdr); + + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), &arr2->hdr); + + return obj; +} + +opa_object_t *json_test_fixture_object6(void) +{ + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("c"), opa_number_int(7)); + opa_object_insert(obj1, opa_string_terminated("d"), opa_number_int(8)); + opa_object_insert(obj1, opa_string_terminated("x"), opa_number_int(0)); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("b"), &obj1->hdr); + + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), &obj2->hdr); + opa_object_insert(obj, opa_string_terminated("e"), opa_number_int(9)); + return obj; +} + +opa_object_t *json_remove_get_exp_object1(void) +{ + opa_array_t *arr1 = opa_cast_array(opa_array()); + opa_array_append(arr1, opa_string_terminated("b")); + opa_array_append(arr1, opa_string_terminated("c")); + + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("1"), &arr1->hdr); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("x"), opa_string_terminated("y")); + + opa_array_t *arr2 = opa_cast_array(opa_array()); + opa_array_append(arr2, &obj1->hdr); + opa_array_append(arr2, &obj2->hdr); + + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), &arr2->hdr); + + return obj; +} + +opa_object_t *json_remove_get_exp_object2(void) +{ + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("x"), opa_number_int(0)); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("b"), &obj1->hdr); + + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), &obj2->hdr); + opa_object_insert(obj, opa_string_terminated("e"), opa_number_int(9)); + return obj; +} + +WASM_EXPORT(test_json_remove) +void test_json_remove(void) +{ + opa_object_t *obj1 = json_test_fixture_object1(); + + opa_set_t *set_paths1 = opa_cast_set(opa_set()); + opa_set_add(set_paths1, opa_string_terminated("a/b/c")); + + opa_object_t *o1 = opa_cast_object(opa_object()); + opa_object_insert(o1, opa_string_terminated("d"), opa_number_int(8)); + opa_object_t *o2 = opa_cast_object(opa_object()); + opa_object_insert(o2, opa_string_terminated("b"), &o1->hdr); + + opa_object_t *expected1 = opa_cast_object(opa_object()); + opa_object_insert(expected1, opa_string_terminated("a"), &o2->hdr); + opa_object_insert(expected1, opa_string_terminated("e"), opa_number_int(9)); + + test("jsonremove/base", opa_value_compare(builtin_json_remove(&obj1->hdr, &set_paths1->hdr), &expected1->hdr) == 0); + + opa_set_add(set_paths1, opa_string_terminated("e")); + + opa_object_t *expected2 = opa_cast_object(opa_object()); + opa_object_insert(expected2, opa_string_terminated("a"), &o2->hdr); + + test("jsonremove/multiple roots", opa_value_compare(builtin_json_remove(&obj1->hdr, &set_paths1->hdr), &expected2->hdr) == 0); + + opa_array_t *array_paths1 = opa_cast_array(opa_array()); + opa_array_append(array_paths1, opa_string_terminated("a/b/c")); + opa_array_append(array_paths1, opa_string_terminated("e")); + + test("jsonremove/multiple roots array", opa_value_compare(builtin_json_remove(&obj1->hdr, &array_paths1->hdr), &expected2->hdr) == 0); + + opa_object_t *obj2 = json_test_fixture_object2(); + + opa_set_t *set_paths2 = opa_cast_set(opa_set()); + opa_set_add(set_paths2, opa_string_terminated("a/b/c")); + opa_set_add(set_paths2, opa_string_terminated("a/e")); + + test("jsonremove/shared roots", opa_value_compare(builtin_json_remove(&obj2->hdr, &set_paths2->hdr), &expected2->hdr) == 0); + + opa_object_t *obj3 = json_test_fixture_object3(); + opa_set_t *set_paths3 = opa_cast_set(opa_set()); + opa_set_add(set_paths3, opa_string_terminated("a")); + opa_set_add(set_paths3, opa_string_terminated("a/b")); + + opa_object_t *expected3 = opa_cast_object(opa_object()); + opa_object_insert(expected3, opa_string_terminated("c"), opa_number_int(1)); + + test("jsonremove/conflict", opa_value_compare(builtin_json_remove(&obj3->hdr, &set_paths3->hdr), &expected3->hdr) == 0); + + opa_object_t *obj4 = opa_cast_object(opa_object()); + opa_object_insert(obj4, opa_string_terminated("a"), opa_number_int(7)); + + test("jsonremove/empty list", opa_value_compare(builtin_json_remove(&obj4->hdr, opa_set()), &obj4->hdr) == 0); + + test("jsonremove/empty object", opa_value_compare(builtin_json_remove(opa_object(), &set_paths3->hdr), opa_object()) == 0); + + opa_object_t *obj5 = json_test_fixture_object1(); + + opa_set_t *set_paths4 = opa_cast_set(opa_set()); + opa_set_add(set_paths4, opa_string_terminated("a")); + opa_set_add(set_paths4, opa_string_terminated("e")); + + test("jsonremove/delete all", opa_value_compare(builtin_json_remove(&obj5->hdr, &set_paths4->hdr), opa_object()) == 0); + + opa_object_t *obj6 = json_test_fixture_object4(); + + opa_set_t *set_paths5 = opa_cast_set(opa_set()); + opa_set_add(set_paths5, opa_string_terminated("a/0/b")); + opa_set_add(set_paths5, opa_string_terminated("a/1")); + + opa_object_t *o3 = opa_cast_object(opa_object()); + opa_object_insert(o3, opa_string_terminated("c"), opa_number_int(8)); + opa_array_t *a3 = opa_cast_array(opa_array()); + opa_array_append(a3, &o3->hdr); + opa_object_t *expected4 = opa_cast_object(opa_object()); + opa_object_insert(expected4, opa_string_terminated("a"), &a3->hdr); + + test("jsonremove/arrays", opa_value_compare(builtin_json_remove(&obj6->hdr, &set_paths5->hdr), &expected4->hdr) == 0); + + opa_object_t *obj7 = json_test_fixture_object5(); + + opa_set_t *set_paths6 = opa_cast_set(opa_set()); + opa_set_add(set_paths6, opa_string_terminated("a/0/1/2")); + + opa_object_t *expected5 = json_remove_get_exp_object1(); + + test("jsonremove/object with number keys", opa_value_compare(builtin_json_remove(&obj7->hdr, &set_paths6->hdr), &expected5->hdr) == 0); + + opa_object_t *obj8 = json_test_fixture_object1(); + + opa_array_t *a4 = opa_cast_array(opa_array()); + opa_array_append(a4, opa_string_terminated("a")); + opa_array_append(a4, opa_string_terminated("b")); + opa_array_append(a4, opa_string_terminated("c")); + + opa_array_t *a5 = opa_cast_array(opa_array()); + opa_array_append(a5, opa_string_terminated("e")); + + opa_set_t *set_paths7 = opa_cast_set(opa_set()); + opa_set_add(set_paths7, &a4->hdr); + opa_set_add(set_paths7, &a5->hdr); + + test("jsonremove/arrays of roots", opa_value_compare(builtin_json_remove(&obj8->hdr, &set_paths7->hdr), &expected2->hdr) == 0); + + opa_object_t *obj9 = json_test_fixture_object6(); + + opa_set_t *set_paths8 = opa_cast_set(opa_set()); + opa_set_add(set_paths8, opa_string_terminated("a/b/d")); + opa_set_add(set_paths8, &a4->hdr); + + opa_object_t *expected6 = json_remove_get_exp_object2(); + + test("jsonremove/mixed root types", opa_value_compare(builtin_json_remove(&obj9->hdr, &set_paths8->hdr), &expected6->hdr) == 0); + + test("jsonremove/error (invalid first operand - string)", opa_value_compare(builtin_json_remove(opa_string_terminated("a"), opa_set()), NULL) == 0); + + test("jsonremove/error (invalid first operand - number)", opa_value_compare(builtin_json_remove(opa_number_int(22), opa_set()), NULL) == 0); + + test("jsonremove/error (invalid first operand - boolean)", opa_value_compare(builtin_json_remove(opa_boolean(true), opa_set()), NULL) == 0); + + test("jsonremove/error (invalid first operand - array)", opa_value_compare(builtin_json_remove(opa_array(), opa_set()), NULL) == 0); + + test("jsonremove/error (invalid second operand - string)", opa_value_compare(builtin_json_remove(opa_object(), opa_string_terminated("a")), NULL) == 0); + + test("jsonremove/error (invalid second operand - number)", opa_value_compare(builtin_json_remove(opa_object(), opa_number_int(22)), NULL) == 0); + + test("jsonremove/error (invalid second operand - boolean)", opa_value_compare(builtin_json_remove(opa_object(), opa_boolean(true)), NULL) == 0); + + test("jsonremove/error (invalid second operand - object)", opa_value_compare(builtin_json_remove(opa_object(), opa_object()), NULL) == 0); + + opa_set_t *set_paths9 = opa_cast_set(opa_set()); + opa_set_add(set_paths9, opa_number_int(1)); + opa_set_add(set_paths9, opa_string_terminated("a")); + + test("jsonremove/error invalid paths type set with numbers", opa_value_compare(builtin_json_remove(opa_object(), &set_paths9->hdr), NULL) == 0); + + opa_set_t *set_paths10 = opa_cast_set(opa_set()); + opa_set_add(set_paths10, opa_string_terminated("a")); + opa_set_add(set_paths10, &obj9->hdr); + + test("jsonremove/error invalid paths type set with objects", opa_value_compare(builtin_json_remove(opa_object(), &set_paths10->hdr), NULL) == 0); + + opa_array_t *array_paths2 = opa_cast_array(opa_array()); + opa_array_append(array_paths2, opa_string_terminated("a")); + opa_array_append(array_paths2, opa_number_int(1)); + opa_array_append(array_paths2, opa_number_int(2)); + + test("jsonremove/error invalid paths type array with numbers", opa_value_compare(builtin_json_remove(opa_object(), &array_paths2->hdr), NULL) == 0); + + opa_array_t *array_paths3 = opa_cast_array(opa_array()); + opa_array_append(array_paths3, opa_string_terminated("a")); + opa_array_append(array_paths3, opa_object()); + + test("jsonremove/error invalid paths type array with objects", opa_value_compare(builtin_json_remove(opa_object(), &array_paths3->hdr), NULL) == 0); + + opa_set_t *set_paths11 = opa_cast_set(opa_set()); + opa_set_add(set_paths11, opa_string_terminated("a/b")); + opa_set_add(set_paths11, opa_string_terminated("e")); + + opa_object_t *expected7 = opa_cast_object(opa_object()); + opa_object_insert(expected7, opa_string_terminated("a"), opa_object()); + + test("jsonremove/delete last in object", opa_value_compare(builtin_json_remove(&obj5->hdr, &set_paths11->hdr), &expected7->hdr) == 0); +} + +WASM_EXPORT(test_json_filter) +void test_json_filter(void) +{ + opa_object_t *obj1 = json_test_fixture_object1(); + + opa_set_t *set_paths1 = opa_cast_set(opa_set()); + opa_set_add(set_paths1, opa_string_terminated("a/b/c")); + + opa_object_t *o1 = opa_cast_object(opa_object()); + opa_object_insert(o1, opa_string_terminated("c"), opa_number_int(7)); + opa_object_t *o2 = opa_cast_object(opa_object()); + opa_object_insert(o2, opa_string_terminated("b"), &o1->hdr); + + opa_object_t *expected1 = opa_cast_object(opa_object()); + opa_object_insert(expected1, opa_string_terminated("a"), &o2->hdr); + + test("jsonfilter/base", opa_value_compare(builtin_json_filter(&obj1->hdr, &set_paths1->hdr), &expected1->hdr) == 0); + + opa_set_add(set_paths1, opa_string_terminated("e")); + opa_object_insert(expected1, opa_string_terminated("e"), opa_number_int(9)); + + test("jsonfilter/multiple roots", opa_value_compare(builtin_json_filter(&obj1->hdr, &set_paths1->hdr), &expected1->hdr) == 0); + + opa_array_t *array_paths1 = opa_cast_array(opa_array()); + opa_array_append(array_paths1, opa_string_terminated("a/b/c")); + opa_array_append(array_paths1, opa_string_terminated("e")); + + test("jsonfilter/multiple roots array", opa_value_compare(builtin_json_filter(&obj1->hdr, &array_paths1->hdr), &expected1->hdr) == 0); + + opa_object_t *obj2 = json_test_fixture_object2(); + + opa_set_t *set_paths2 = opa_cast_set(opa_set()); + opa_set_add(set_paths2, opa_string_terminated("a/b/c")); + opa_set_add(set_paths2, opa_string_terminated("a/e")); + + opa_object_t *o3 = opa_cast_object(opa_object()); + opa_object_insert(o3, opa_string_terminated("b"), &o1->hdr); + opa_object_insert(o3, opa_string_terminated("e"), opa_number_int(9)); + + opa_object_t *expected2 = opa_cast_object(opa_object()); + opa_object_insert(expected2, opa_string_terminated("a"), &o3->hdr); + + test("jsonfilter/shared roots", opa_value_compare(builtin_json_filter(&obj2->hdr, &set_paths2->hdr), &expected2->hdr) == 0); + + opa_object_t *o4 = opa_cast_object(opa_object()); + opa_object_insert(o4, opa_string_terminated("b"), opa_number_int(7)); + + opa_object_t *obj3 = opa_cast_object(opa_object()); + opa_object_insert(obj3, opa_string_terminated("a"), &o4->hdr); + + opa_set_t *set_paths3 = opa_cast_set(opa_set()); + opa_set_add(set_paths3, opa_string_terminated("a")); + opa_set_add(set_paths3, opa_string_terminated("a/b")); + + test("jsonfilter/conflict", opa_value_compare(builtin_json_filter(&obj3->hdr, &set_paths3->hdr), &obj3->hdr) == 0); + + test("jsonfilter/empty list", opa_value_compare(builtin_json_filter(&obj3->hdr, opa_set()), opa_object()) == 0); + + test("jsonfilter/empty object", opa_value_compare(builtin_json_filter(opa_object(), &set_paths3->hdr), opa_object()) == 0); + + opa_object_t *obj4 = json_test_fixture_object4(); + opa_set_t *set_paths4 = opa_cast_set(opa_set()); + opa_set_add(set_paths4, opa_string_terminated("a/0/b")); + opa_set_add(set_paths4, opa_string_terminated("a/1")); + + opa_object_t *o5 = opa_cast_object(opa_object()); + opa_object_insert(o5, opa_string_terminated("b"), opa_number_int(7)); + + opa_object_t *o6 = opa_cast_object(opa_object()); + opa_object_insert(o6, opa_string_terminated("d"), opa_number_int(9)); + + opa_array_t *a1 = opa_cast_array(opa_array()); + opa_array_append(a1, &o5->hdr); + opa_array_append(a1, &o6->hdr); + + opa_object_t *expected3 = opa_cast_object(opa_object()); + opa_object_insert(expected3, opa_string_terminated("a"), &a1->hdr); + + test("jsonfilter/arrays", opa_value_compare(builtin_json_filter(&obj4->hdr, &set_paths4->hdr), &expected3->hdr) == 0); + + opa_object_t *obj5 = json_test_fixture_object5(); + opa_set_t *set_paths5 = opa_cast_set(opa_set()); + opa_set_add(set_paths5, opa_string_terminated("a/0/1/2")); + + opa_array_t *a2 = opa_cast_array(opa_array()); + opa_array_append(a2, opa_string_terminated("d")); + + opa_object_t *o7 = opa_cast_object(opa_object()); + opa_object_insert(o7, opa_string_terminated("1"), &a2->hdr); + + opa_array_t *a3 = opa_cast_array(opa_array()); + opa_array_append(a3, &o7->hdr); + + opa_object_t *expected4 = opa_cast_object(opa_object()); + opa_object_insert(expected4, opa_string_terminated("a"), &a3->hdr); + + test("jsonfilter/object with number keys", opa_value_compare(builtin_json_filter(&obj5->hdr, &set_paths5->hdr), &expected4->hdr) == 0); + + opa_array_t *a4 = opa_cast_array(opa_array()); + opa_array_append(a4, opa_string_terminated("a")); + opa_array_append(a4, opa_string_terminated("b")); + opa_array_append(a4, opa_string_terminated("c")); + + opa_array_t *a5 = opa_cast_array(opa_array()); + opa_array_append(a5, opa_string_terminated("e")); + + opa_set_t *set_paths6 = opa_cast_set(opa_set()); + opa_set_add(set_paths6, &a4->hdr); + opa_set_add(set_paths6, &a5->hdr); + + test("jsonfilter/arrays of roots", opa_value_compare(builtin_json_filter(&obj1->hdr, &set_paths6->hdr), &expected1->hdr) == 0); + + opa_object_t *obj6 = json_test_fixture_object6(); + + opa_set_t *set_paths7 = opa_cast_set(opa_set()); + opa_set_add(set_paths7, opa_string_terminated("a/b/d")); + opa_set_add(set_paths7, &a4->hdr); + + opa_object_t *o8 = opa_cast_object(opa_object()); + opa_object_insert(o8, opa_string_terminated("c"), opa_number_int(7)); + opa_object_insert(o8, opa_string_terminated("d"), opa_number_int(8)); + + opa_object_t *o9 = opa_cast_object(opa_object()); + opa_object_insert(o9, opa_string_terminated("b"), &o8->hdr); + + opa_object_t *expected5 = opa_cast_object(opa_object()); + opa_object_insert(expected5, opa_string_terminated("a"), &o9->hdr); + + test("jsonfilter/mixed root types", opa_value_compare(builtin_json_filter(&obj6->hdr, &set_paths7->hdr), &expected5->hdr) == 0); + + test("jsonfilter/error (invalid first operand - string)", opa_value_compare(builtin_json_filter(opa_string_terminated("a"), opa_set()), NULL) == 0); + + test("jsonfilter/error (invalid first operand - number)", opa_value_compare(builtin_json_filter(opa_number_int(22), opa_set()), NULL) == 0); + + test("jsonfilter/error (invalid first operand - boolean)", opa_value_compare(builtin_json_filter(opa_boolean(true), opa_set()), NULL) == 0); + + test("jsonfilter/error (invalid first operand - array)", opa_value_compare(builtin_json_filter(opa_array(), opa_set()), NULL) == 0); + + test("jsonfilter/error (invalid second operand - string)", opa_value_compare(builtin_json_filter(opa_object(), opa_string_terminated("a")), NULL) == 0); + + test("jsonfilter/error (invalid second operand - number)", opa_value_compare(builtin_json_filter(opa_object(), opa_number_int(22)), NULL) == 0); + + test("jsonfilter/error (invalid second operand - boolean)", opa_value_compare(builtin_json_filter(opa_object(), opa_boolean(true)), NULL) == 0); + + test("jsonfilter/error (invalid second operand - object)", opa_value_compare(builtin_json_filter(opa_object(), opa_object()), NULL) == 0); +} + +WASM_EXPORT(test_builtin_graph_reachable) +void test_builtin_graph_reachable(void) +{ + + test("reachable/malformed graph", opa_value_compare(builtin_graph_reachable(opa_set(), opa_set()), NULL) == 0); + + opa_object_t *graph1 = opa_cast_object(opa_object()); + opa_set_t *initial1 = opa_cast_set(opa_set()); + opa_set_add(initial1, opa_string_terminated("a")); + + test("reachable/empty", opa_value_compare(builtin_graph_reachable(&graph1->hdr, &initial1->hdr), opa_set()) == 0); + + // graph -> {"a": {"b"}, "b": {"c"}, "c": {"a"}} + opa_set_t *vertex1_1 = opa_cast_set(opa_set()); + opa_set_add(vertex1_1, opa_string_terminated("b")); + opa_object_insert(graph1, opa_string_terminated("a"), &vertex1_1->hdr); + + opa_set_t *vertex2_1 = opa_cast_set(opa_set()); + opa_set_add(vertex2_1, opa_string_terminated("c")); + opa_object_insert(graph1, opa_string_terminated("b"), &vertex2_1->hdr); + + opa_set_t *vertex3_1 = opa_cast_set(opa_set()); + opa_set_add(vertex3_1, opa_string_terminated("a")); + opa_object_insert(graph1, opa_string_terminated("c"), &vertex3_1->hdr); + + opa_set_t *expected1 = opa_cast_set(opa_set()); + opa_set_add(expected1, opa_string_terminated("a")); + opa_set_add(expected1, opa_string_terminated("b")); + opa_set_add(expected1, opa_string_terminated("c")); + + test("reachable/cycle", opa_value_compare(builtin_graph_reachable(&graph1->hdr, &initial1->hdr), &expected1->hdr) == 0); + + // graph -> {"a": {"b", "c"}, "b": {"d"}, "c": {"d"}, "d": {}, "e": {"f"}, "f": {"e"}, "x": {"x"}} + opa_object_t *graph2 = opa_cast_object(opa_object()); + opa_set_t *initial2 = opa_cast_set(opa_set()); + opa_set_add(initial2, opa_string_terminated("b")); + opa_set_add(initial2, opa_string_terminated("e")); + + opa_set_t *vertex1_2 = opa_cast_set(opa_set()); + opa_set_add(vertex1_2, opa_string_terminated("b")); + opa_set_add(vertex1_2, opa_string_terminated("c")); + opa_object_insert(graph2, opa_string_terminated("a"), &vertex1_2->hdr); + + opa_set_t *vertex2_2 = opa_cast_set(opa_set()); + opa_set_add(vertex2_2, opa_string_terminated("d")); + opa_object_insert(graph2, opa_string_terminated("b"), &vertex2_2->hdr); + opa_object_insert(graph2, opa_string_terminated("c"), &vertex2_2->hdr); + + opa_object_insert(graph2, opa_string_terminated("d"), opa_set()); + + opa_set_t *vertex3_2 = opa_cast_set(opa_set()); + opa_set_add(vertex3_2, opa_string_terminated("f")); + opa_object_insert(graph2, opa_string_terminated("e"), &vertex3_2->hdr); + + opa_set_t *vertex4_2 = opa_cast_set(opa_set()); + opa_set_add(vertex4_2, opa_string_terminated("e")); + opa_object_insert(graph2, opa_string_terminated("f"), &vertex4_2->hdr); + + opa_set_t *vertex5_2 = opa_cast_set(opa_set()); + opa_set_add(vertex5_2, opa_string_terminated("x")); + opa_object_insert(graph2, opa_string_terminated("x"), &vertex5_2->hdr); + + opa_set_t *expected2 = opa_cast_set(opa_set()); + opa_set_add(expected2, opa_string_terminated("b")); + opa_set_add(expected2, opa_string_terminated("d")); + opa_set_add(expected2, opa_string_terminated("e")); + opa_set_add(expected2, opa_string_terminated("f")); + + test("reachable/components", opa_value_compare(builtin_graph_reachable(&graph2->hdr, &initial2->hdr), &expected2->hdr) == 0); + + // graph -> {"a": ["b"], "b": ["c"], "c": ["a"]} + opa_object_t *graph3 = opa_cast_object(opa_object()); + opa_array_t *initial3 = opa_cast_array(opa_array()); + opa_array_append(initial3, opa_string_terminated("a")); + + opa_array_t *vertex1_3 = opa_cast_array(opa_array()); + opa_array_append(vertex1_3, opa_string_terminated("b")); + opa_object_insert(graph3, opa_string_terminated("a"), &vertex1_3->hdr); + + opa_array_t *vertex2_3 = opa_cast_array(opa_array()); + opa_array_append(vertex2_3, opa_string_terminated("c")); + opa_object_insert(graph3, opa_string_terminated("b"), &vertex2_3->hdr); + + opa_array_t *vertex3_3 = opa_cast_array(opa_array()); + opa_array_append(vertex3_3, opa_string_terminated("a")); + opa_object_insert(graph3, opa_string_terminated("c"), &vertex3_3->hdr); + + opa_set_t *expected3 = opa_cast_set(opa_set()); + opa_set_add(expected3, opa_string_terminated("a")); + opa_set_add(expected3, opa_string_terminated("b")); + opa_set_add(expected3, opa_string_terminated("c")); + + test("reachable/arrays", opa_value_compare(builtin_graph_reachable(&graph3->hdr, &initial3->hdr), &expected3->hdr) == 0); + + test("reachable/malformed initial nodes", opa_value_compare(builtin_graph_reachable(&graph3->hdr, opa_string_terminated("foo")), NULL) == 0); + + // graph -> {"a": null} + opa_object_t *graph4 = opa_cast_object(opa_object()); + opa_object_insert(graph4, opa_string_terminated("a"), opa_null()); + + opa_set_t *expected4 = opa_cast_set(opa_set()); + opa_set_add(expected4, opa_string_terminated("a")); + + test("reachable/null edge", opa_value_compare(builtin_graph_reachable(&graph4->hdr, &initial3->hdr), &expected4->hdr) == 0); +} + +WASM_EXPORT(test_strings) +void test_strings(void) +{ + opa_array_t *any_prefix_match_string_arr_1 = opa_cast_array(opa_array()); + opa_array_append(any_prefix_match_string_arr_1, opa_string_terminated("a/b/c")); + opa_array_append(any_prefix_match_string_arr_1, opa_string_terminated("e/f/g")); + + opa_array_t *any_prefix_match_prefixes_arr_11 = opa_cast_array(opa_array()); + opa_array_append(any_prefix_match_prefixes_arr_11, opa_string_terminated("g/b")); + opa_array_append(any_prefix_match_prefixes_arr_11, opa_string_terminated("a/")); + + opa_array_t *any_prefix_match_prefixes_arr_12 = opa_cast_array(opa_array()); + opa_array_append(any_prefix_match_prefixes_arr_12, opa_string_terminated("g/b")); + opa_array_append(any_prefix_match_prefixes_arr_12, opa_string_terminated("b/")); + + opa_array_t *any_prefix_match_string_arr_2 = opa_cast_array(opa_array()); + opa_array_t *any_prefix_match_prefixes_arr_2 = opa_cast_array(opa_array()); + + opa_set_t *any_prefix_match_string_set_1 = opa_cast_set(opa_set()); + opa_set_add(any_prefix_match_string_set_1, opa_string_terminated("a/b/c")); + opa_set_add(any_prefix_match_string_set_1, opa_string_terminated("e/f/g")); + + opa_set_t *any_prefix_match_prefixes_set_11 = opa_cast_set(opa_set()); + opa_set_add(any_prefix_match_prefixes_set_11, opa_string_terminated("g/b")); + opa_set_add(any_prefix_match_prefixes_set_11, opa_string_terminated("a/")); + + opa_set_t *any_prefix_match_prefixes_set_12 = opa_cast_set(opa_set()); + opa_set_add(any_prefix_match_prefixes_set_12, opa_string_terminated("g/b")); + opa_set_add(any_prefix_match_prefixes_set_12, opa_string_terminated("b/")); + + opa_set_t *any_prefix_match_string_set_2 = opa_cast_set(opa_set()); + opa_set_t *any_prefix_match_prefixes_set_2 = opa_cast_set(opa_set()); + + test("any_prefix_match/__", opa_value_compare(opa_strings_any_prefix_match(opa_string_terminated(""), opa_string_terminated("")), opa_boolean(true)) == 0); + test("any_prefix_match/_a", opa_value_compare(opa_strings_any_prefix_match(opa_string_terminated(""), opa_string_terminated("a")), opa_boolean(false)) == 0); + test("any_prefix_match/a_", opa_value_compare(opa_strings_any_prefix_match(opa_string_terminated("a"), opa_string_terminated("")), opa_boolean(true)) == 0); + test("any_prefix_match/aa", opa_value_compare(opa_strings_any_prefix_match(opa_string_terminated("a"), opa_string_terminated("a")), opa_boolean(true)) == 0); + test("any_prefix_match/ab", opa_value_compare(opa_strings_any_prefix_match(opa_string_terminated("a"), opa_string_terminated("b")), opa_boolean(false)) == 0); + test("any_prefix_match/aab", opa_value_compare(opa_strings_any_prefix_match(opa_string_terminated("a"), opa_string_terminated("ab")), opa_boolean(false)) == 0); + test("any_prefix_match/aba", opa_value_compare(opa_strings_any_prefix_match(opa_string_terminated("ab"), opa_string_terminated("a")), opa_boolean(true)) == 0); + test("any_prefix_match/aab", opa_value_compare(opa_strings_any_prefix_match(opa_string_terminated("aa"), opa_string_terminated("b")), opa_boolean(false)) == 0); + test("any_prefix_match/abab", opa_value_compare(opa_strings_any_prefix_match(opa_string_terminated("ab"), opa_string_terminated("ab")), opa_boolean(true)) == 0); + test("any_prefix_match/abaa", opa_value_compare(opa_strings_any_prefix_match(opa_string_terminated("ab"), opa_string_terminated("aa")), opa_boolean(false)) == 0); + test("any_prefix_match/abcab", opa_value_compare(opa_strings_any_prefix_match(opa_string_terminated("abc"), opa_string_terminated("ab")), opa_boolean(true)) == 0); + test("any_prefix_match/abcac", opa_value_compare(opa_strings_any_prefix_match(opa_string_terminated("abc"), opa_string_terminated("ac")), opa_boolean(false)) == 0); + test("any_prefix_match/arr11", opa_value_compare(opa_strings_any_prefix_match(&any_prefix_match_string_arr_1->hdr, &any_prefix_match_prefixes_arr_11->hdr), opa_boolean(true)) == 0); + test("any_prefix_match/arr12", opa_value_compare(opa_strings_any_prefix_match(&any_prefix_match_string_arr_1->hdr, &any_prefix_match_prefixes_arr_12->hdr), opa_boolean(false)) == 0); + test("any_prefix_match/arr2", opa_value_compare(opa_strings_any_prefix_match(&any_prefix_match_string_arr_2->hdr, &any_prefix_match_prefixes_arr_2->hdr), opa_boolean(false)) == 0); + test("any_prefix_match/set11", opa_value_compare(opa_strings_any_prefix_match(&any_prefix_match_string_set_1->hdr, &any_prefix_match_prefixes_set_11->hdr), opa_boolean(true)) == 0); + test("any_prefix_match/set12", opa_value_compare(opa_strings_any_prefix_match(&any_prefix_match_string_set_1->hdr, &any_prefix_match_prefixes_set_12->hdr), opa_boolean(false)) == 0); + test("any_prefix_match/set2", opa_value_compare(opa_strings_any_prefix_match(&any_prefix_match_string_set_2->hdr, &any_prefix_match_prefixes_set_2->hdr), opa_boolean(false)) == 0); + + opa_array_t *any_suffix_match_string_arr_1 = opa_cast_array(opa_array()); + opa_array_append(any_suffix_match_string_arr_1, opa_string_terminated("a/b/c")); + opa_array_append(any_suffix_match_string_arr_1, opa_string_terminated("e/f/g")); + + opa_array_t *any_suffix_match_suffixes_arr_11 = opa_cast_array(opa_array()); + opa_array_append(any_suffix_match_suffixes_arr_11, opa_string_terminated("g/b")); + opa_array_append(any_suffix_match_suffixes_arr_11, opa_string_terminated("/c")); + + opa_array_t *any_suffix_match_suffixes_arr_12 = opa_cast_array(opa_array()); + opa_array_append(any_suffix_match_suffixes_arr_12, opa_string_terminated("g/b")); + opa_array_append(any_suffix_match_suffixes_arr_12, opa_string_terminated("/b")); + + opa_array_t *any_suffix_match_string_arr_2 = opa_cast_array(opa_array()); + opa_array_t *any_suffix_match_suffixes_arr_2 = opa_cast_array(opa_array()); + + opa_set_t *any_suffix_match_string_set_1 = opa_cast_set(opa_set()); + opa_set_add(any_suffix_match_string_set_1, opa_string_terminated("a/b/c")); + opa_set_add(any_suffix_match_string_set_1, opa_string_terminated("e/f/g")); + + opa_set_t *any_suffix_match_suffixes_set_11 = opa_cast_set(opa_set()); + opa_set_add(any_suffix_match_suffixes_set_11, opa_string_terminated("g/b")); + opa_set_add(any_suffix_match_suffixes_set_11, opa_string_terminated("/c")); + + opa_set_t *any_suffix_match_suffixes_set_12 = opa_cast_set(opa_set()); + opa_set_add(any_suffix_match_suffixes_set_12, opa_string_terminated("g/b")); + opa_set_add(any_suffix_match_suffixes_set_12, opa_string_terminated("/b")); + + opa_set_t *any_suffix_match_string_set_2 = opa_cast_set(opa_set()); + opa_set_t *any_suffix_match_suffixes_set_2 = opa_cast_set(opa_set()); + + test("any_suffix_match/__", opa_value_compare(opa_strings_any_suffix_match(opa_string_terminated(""), opa_string_terminated("")), opa_boolean(true)) == 0); + test("any_suffix_match/_a", opa_value_compare(opa_strings_any_suffix_match(opa_string_terminated(""), opa_string_terminated("a")), opa_boolean(false)) == 0); + test("any_suffix_match/a_", opa_value_compare(opa_strings_any_suffix_match(opa_string_terminated("a"), opa_string_terminated("")), opa_boolean(true)) == 0); + test("any_suffix_match/aa", opa_value_compare(opa_strings_any_suffix_match(opa_string_terminated("a"), opa_string_terminated("a")), opa_boolean(true)) == 0); + test("any_suffix_match/ab", opa_value_compare(opa_strings_any_suffix_match(opa_string_terminated("a"), opa_string_terminated("b")), opa_boolean(false)) == 0); + test("any_suffix_match/aab", opa_value_compare(opa_strings_any_suffix_match(opa_string_terminated("a"), opa_string_terminated("ab")), opa_boolean(false)) == 0); + test("any_suffix_match/abb", opa_value_compare(opa_strings_any_suffix_match(opa_string_terminated("ab"), opa_string_terminated("b")), opa_boolean(true)) == 0); + test("any_suffix_match/aab", opa_value_compare(opa_strings_any_suffix_match(opa_string_terminated("aa"), opa_string_terminated("b")), opa_boolean(false)) == 0); + test("any_suffix_match/abab", opa_value_compare(opa_strings_any_suffix_match(opa_string_terminated("ab"), opa_string_terminated("ab")), opa_boolean(true)) == 0); + test("any_suffix_match/abaa", opa_value_compare(opa_strings_any_suffix_match(opa_string_terminated("ab"), opa_string_terminated("aa")), opa_boolean(false)) == 0); + test("any_suffix_match/abcbc", opa_value_compare(opa_strings_any_suffix_match(opa_string_terminated("abc"), opa_string_terminated("bc")), opa_boolean(true)) == 0); + test("any_suffix_match/abcbd", opa_value_compare(opa_strings_any_suffix_match(opa_string_terminated("abc"), opa_string_terminated("bd")), opa_boolean(false)) == 0); + test("any_suffix_match/arr11", opa_value_compare(opa_strings_any_suffix_match(&any_suffix_match_string_arr_1->hdr, &any_suffix_match_suffixes_arr_11->hdr), opa_boolean(true)) == 0); + test("any_suffix_match/arr12", opa_value_compare(opa_strings_any_suffix_match(&any_suffix_match_string_arr_1->hdr, &any_suffix_match_suffixes_arr_12->hdr), opa_boolean(false)) == 0); + test("any_suffix_match/arr2", opa_value_compare(opa_strings_any_suffix_match(&any_suffix_match_string_arr_2->hdr, &any_suffix_match_suffixes_arr_2->hdr), opa_boolean(false)) == 0); + test("any_suffix_match/set11", opa_value_compare(opa_strings_any_suffix_match(&any_suffix_match_string_set_1->hdr, &any_suffix_match_suffixes_set_11->hdr), opa_boolean(true)) == 0); + test("any_suffix_match/set12", opa_value_compare(opa_strings_any_suffix_match(&any_suffix_match_string_set_1->hdr, &any_suffix_match_suffixes_set_12->hdr), opa_boolean(false)) == 0); + test("any_suffix_match/set2", opa_value_compare(opa_strings_any_suffix_match(&any_suffix_match_string_set_2->hdr, &any_suffix_match_suffixes_set_2->hdr), opa_boolean(false)) == 0); + + opa_value *join = opa_string_terminated("--"); + + opa_array_t *arr0 = opa_cast_array(opa_array()); + + opa_array_t *arr1 = opa_cast_array(opa_array()); + opa_array_append(arr1, opa_string_terminated("foo")); + + opa_array_t *arr2 = opa_cast_array(opa_array()); + opa_array_append(arr2, opa_string_terminated("foo")); + opa_array_append(arr2, opa_string_terminated("bar")); + + opa_set_t *set0 = opa_cast_set(opa_set()); + + opa_set_t *set1 = opa_cast_set(opa_set()); + opa_set_add(set1, opa_string_terminated("foo")); + + opa_set_t *set2 = opa_cast_set(opa_set()); + opa_set_add(set2, opa_string_terminated("foo")); + opa_set_add(set2, opa_string_terminated("bar")); + + test("concat/array0", opa_value_compare(opa_strings_concat(join, &arr0->hdr), opa_string_terminated("")) == 0); + test("concat/array1", opa_value_compare(opa_strings_concat(join, &arr1->hdr), opa_string_terminated("foo")) == 0); + test("concat/array2", opa_value_compare(opa_strings_concat(join, &arr2->hdr), opa_string_terminated("foo--bar")) == 0); + test("concat/set0", opa_value_compare(opa_strings_concat(join, &set0->hdr), opa_string_terminated("")) == 0); + test("concat/set1", opa_value_compare(opa_strings_concat(join, &set1->hdr), opa_string_terminated("foo")) == 0); + test("concat/set2", opa_value_compare(opa_strings_concat(join, &set2->hdr), opa_string_terminated("bar--foo")) == 0); + + test("contains/__", opa_value_compare(opa_strings_contains(opa_string_terminated(""), opa_string_terminated("")), opa_boolean(true)) == 0); + test("contains/_a", opa_value_compare(opa_strings_contains(opa_string_terminated(""), opa_string_terminated("a")), opa_boolean(false)) == 0); + test("contains/a_", opa_value_compare(opa_strings_contains(opa_string_terminated("a"), opa_string_terminated("")), opa_boolean(true)) == 0); + test("contains/aa", opa_value_compare(opa_strings_contains(opa_string_terminated("a"), opa_string_terminated("a")), opa_boolean(true)) == 0); + test("contains/ab", opa_value_compare(opa_strings_contains(opa_string_terminated("a"), opa_string_terminated("b")), opa_boolean(false)) == 0); + test("contains/aab", opa_value_compare(opa_strings_contains(opa_string_terminated("a"), opa_string_terminated("ab")), opa_boolean(false)) == 0); + test("contains/abb", opa_value_compare(opa_strings_contains(opa_string_terminated("ab"), opa_string_terminated("b")), opa_boolean(true)) == 0); + test("contains/aab", opa_value_compare(opa_strings_contains(opa_string_terminated("aa"), opa_string_terminated("b")), opa_boolean(false)) == 0); + test("contains/abab", opa_value_compare(opa_strings_contains(opa_string_terminated("ab"), opa_string_terminated("ab")), opa_boolean(true)) == 0); + test("contains/abaa", opa_value_compare(opa_strings_contains(opa_string_terminated("ab"), opa_string_terminated("aa")), opa_boolean(false)) == 0); + test("contains/abcbc", opa_value_compare(opa_strings_contains(opa_string_terminated("abc"), opa_string_terminated("bc")), opa_boolean(true)) == 0); + test("contains/abcbd", opa_value_compare(opa_strings_contains(opa_string_terminated("abc"), opa_string_terminated("bd")), opa_boolean(false)) == 0); + + test("endswith/__", opa_value_compare(opa_strings_endswith(opa_string_terminated(""), opa_string_terminated("")), opa_boolean(true)) == 0); + test("endswith/_a", opa_value_compare(opa_strings_endswith(opa_string_terminated(""), opa_string_terminated("a")), opa_boolean(false)) == 0); + test("endswith/a_", opa_value_compare(opa_strings_endswith(opa_string_terminated("a"), opa_string_terminated("")), opa_boolean(true)) == 0); + test("endswith/aa", opa_value_compare(opa_strings_endswith(opa_string_terminated("a"), opa_string_terminated("a")), opa_boolean(true)) == 0); + test("endswith/ab", opa_value_compare(opa_strings_endswith(opa_string_terminated("a"), opa_string_terminated("b")), opa_boolean(false)) == 0); + test("endswith/aab", opa_value_compare(opa_strings_endswith(opa_string_terminated("a"), opa_string_terminated("ab")), opa_boolean(false)) == 0); + test("endswith/abb", opa_value_compare(opa_strings_endswith(opa_string_terminated("ab"), opa_string_terminated("b")), opa_boolean(true)) == 0); + test("endswith/aab", opa_value_compare(opa_strings_endswith(opa_string_terminated("aa"), opa_string_terminated("b")), opa_boolean(false)) == 0); + test("endswith/abab", opa_value_compare(opa_strings_endswith(opa_string_terminated("ab"), opa_string_terminated("ab")), opa_boolean(true)) == 0); + test("endswith/abaa", opa_value_compare(opa_strings_endswith(opa_string_terminated("ab"), opa_string_terminated("aa")), opa_boolean(false)) == 0); + test("endswith/abcbc", opa_value_compare(opa_strings_endswith(opa_string_terminated("abc"), opa_string_terminated("bc")), opa_boolean(true)) == 0); + test("endswith/abcbd", opa_value_compare(opa_strings_endswith(opa_string_terminated("abc"), opa_string_terminated("bd")), opa_boolean(false)) == 0); + + test("format_int/2_0", opa_value_compare(opa_strings_format_int(opa_number_float(0), opa_number_int(2)), opa_string_terminated("0")) == 0); + test("format_int/2_1", opa_value_compare(opa_strings_format_int(opa_number_float(1), opa_number_int(2)), opa_string_terminated("1")) == 0); + test("format_int/2_-1", opa_value_compare(opa_strings_format_int(opa_number_float(-1), opa_number_int(2)), opa_string_terminated("-1")) == 0); + test("format_int/2_2", opa_value_compare(opa_strings_format_int(opa_number_float(2), opa_number_int(2)), opa_string_terminated("10")) == 0); + test("format_int/2_7", opa_value_compare(opa_strings_format_int(opa_number_float(7), opa_number_int(2)), opa_string_terminated("111")) == 0); + test("format_int/8_0", opa_value_compare(opa_strings_format_int(opa_number_float(0), opa_number_int(8)), opa_string_terminated("0")) == 0); + test("format_int/8_1", opa_value_compare(opa_strings_format_int(opa_number_float(1), opa_number_int(8)), opa_string_terminated("1")) == 0); + test("format_int/8_-1", opa_value_compare(opa_strings_format_int(opa_number_float(-1), opa_number_int(8)), opa_string_terminated("-1")) == 0); + test("format_int/8_8", opa_value_compare(opa_strings_format_int(opa_number_float(8), opa_number_int(8)), opa_string_terminated("10")) == 0); + test("format_int/8_9", opa_value_compare(opa_strings_format_int(opa_number_float(9), opa_number_int(8)), opa_string_terminated("11")) == 0); + test("format_int/10_0", opa_value_compare(opa_strings_format_int(opa_number_float(0), opa_number_int(10)), opa_string_terminated("0")) == 0); + test("format_int/10_1", opa_value_compare(opa_strings_format_int(opa_number_float(1), opa_number_int(10)), opa_string_terminated("1")) == 0); + test("format_int/10_-1", opa_value_compare(opa_strings_format_int(opa_number_float(-1), opa_number_int(10)), opa_string_terminated("-1")) == 0); + test("format_int/10_10", opa_value_compare(opa_strings_format_int(opa_number_float(10), opa_number_int(10)), opa_string_terminated("10")) == 0); + test("format_int/10_11", opa_value_compare(opa_strings_format_int(opa_number_float(11), opa_number_int(10)), opa_string_terminated("11")) == 0); + test("format_int/16_0", opa_value_compare(opa_strings_format_int(opa_number_float(0), opa_number_int(16)), opa_string_terminated("0")) == 0); + test("format_int/16_1", opa_value_compare(opa_strings_format_int(opa_number_float(1), opa_number_int(16)), opa_string_terminated("1")) == 0); + test("format_int/16_-1", opa_value_compare(opa_strings_format_int(opa_number_float(-1), opa_number_int(16)), opa_string_terminated("-1")) == 0); + test("format_int/16_15.5", opa_value_compare(opa_strings_format_int(opa_number_float(15.5), opa_number_int(16)), opa_string_terminated("f")) == 0); + test("format_int/16_-15.5", opa_value_compare(opa_strings_format_int(opa_number_float(-15.5), opa_number_int(16)), opa_string_terminated("-f")) == 0); + test("format_int/16_16", opa_value_compare(opa_strings_format_int(opa_number_float(16), opa_number_int(16)), opa_string_terminated("10")) == 0); + test("format_int/16_31", opa_value_compare(opa_strings_format_int(opa_number_float(31), opa_number_int(16)), opa_string_terminated("1f")) == 0); + + test("indexof/__", opa_value_compare(opa_strings_indexof(opa_string_terminated(""), opa_string_terminated("")), opa_number_int(0)) == 0); + test("indexof/_a", opa_value_compare(opa_strings_indexof(opa_string_terminated(""), opa_string_terminated("a")), opa_number_int(-1)) == 0); + test("indexof/a_", opa_value_compare(opa_strings_indexof(opa_string_terminated("a"), opa_string_terminated("")), opa_number_int(0)) == 0); + test("indexof/aa", opa_value_compare(opa_strings_indexof(opa_string_terminated("a"), opa_string_terminated("a")), opa_number_int(0)) == 0); + test("indexof/ab", opa_value_compare(opa_strings_indexof(opa_string_terminated("a"), opa_string_terminated("b")), opa_number_int(-1)) == 0); + test("indexof/aab", opa_value_compare(opa_strings_indexof(opa_string_terminated("a"), opa_string_terminated("ab")), opa_number_int(-1)) == 0); + test("indexof/abb", opa_value_compare(opa_strings_indexof(opa_string_terminated("ab"), opa_string_terminated("b")), opa_number_int(1)) == 0); + test("indexof/aab", opa_value_compare(opa_strings_indexof(opa_string_terminated("aa"), opa_string_terminated("b")), opa_number_int(-1)) == 0); + test("indexof/abab", opa_value_compare(opa_strings_indexof(opa_string_terminated("ab"), opa_string_terminated("ab")), opa_number_int(0)) == 0); + test("indexof/abaa", opa_value_compare(opa_strings_indexof(opa_string_terminated("ab"), opa_string_terminated("aa")), opa_number_int(-1)) == 0); + test("indexof/abcbc", opa_value_compare(opa_strings_indexof(opa_string_terminated("abc"), opa_string_terminated("bc")), opa_number_int(1)) == 0); + test("indexof/abcbd", opa_value_compare(opa_strings_indexof(opa_string_terminated("abc"), opa_string_terminated("bd")), opa_number_int(-1)) == 0); + test("indexof/unicode", opa_value_compare(opa_strings_indexof(opa_string_terminated("\xC3\xA5\xC3\xA4\xC3\xB6"), opa_string_terminated("\xC3\xB6")), opa_number_int(2)) == 0); + + test("replace/___", opa_value_compare(opa_strings_replace(opa_string_terminated(""), opa_string_terminated(""), opa_string_terminated("")), opa_string_terminated("")) == 0); + test("replace/_ab", opa_value_compare(opa_strings_replace(opa_string_terminated(""), opa_string_terminated("a"), opa_string_terminated("b")), opa_string_terminated("")) == 0); + test("replace/aab", opa_value_compare(opa_strings_replace(opa_string_terminated("a"), opa_string_terminated("a"), opa_string_terminated("b")), opa_string_terminated("b")) == 0); + test("replace/cab", opa_value_compare(opa_strings_replace(opa_string_terminated("c"), opa_string_terminated("a"), opa_string_terminated("b")), opa_string_terminated("c")) == 0); + test("replace/aaab", opa_value_compare(opa_strings_replace(opa_string_terminated("aa"), opa_string_terminated("a"), opa_string_terminated("b")), opa_string_terminated("bb")) == 0); + test("replace/acaab", opa_value_compare(opa_strings_replace(opa_string_terminated("aca"), opa_string_terminated("a"), opa_string_terminated("b")), opa_string_terminated("bcb")) == 0); + test("replace/acaabd", opa_value_compare(opa_strings_replace(opa_string_terminated("aca"), opa_string_terminated("a"), opa_string_terminated("bd")), opa_string_terminated("bdcbd")) == 0); + test("replace/cacab", opa_value_compare(opa_strings_replace(opa_string_terminated("cac"), opa_string_terminated("a"), opa_string_terminated("b")), opa_string_terminated("cbc")) == 0); + test("replace/cacabd", opa_value_compare(opa_strings_replace(opa_string_terminated("cac"), opa_string_terminated("a"), opa_string_terminated("bd")), opa_string_terminated("cbdc")) == 0); + + test("reverse/abc", opa_value_compare(opa_strings_reverse(opa_string_terminated("abc")), opa_string_terminated("cba")) == 0); + test("reverse/unicode", opa_value_compare(opa_strings_reverse(opa_string_terminated("1😀𝛾")), opa_string_terminated("𝛾😀1"))== 0); + test("reverse/___", opa_value_compare(opa_strings_reverse(opa_string_terminated("")), opa_string_terminated("")) == 0); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("a"), opa_string_terminated("b")); + opa_object_insert(obj2, opa_string_terminated("c"), opa_string_terminated("d")); + + test("replace_n/empty", opa_value_compare(opa_strings_replace_n(opa_object(), opa_string_terminated("a")), opa_string_terminated("a")) == 0); + test("replace_n/two", opa_value_compare(opa_strings_replace_n(&obj2->hdr, opa_string_terminated("ac")), opa_string_terminated("bd")) == 0); + + opa_array_t *arr2b = opa_cast_array(opa_array()); + opa_array_append(arr2b, opa_string_terminated("")); + opa_array_append(arr2b, opa_string_terminated("foo")); + + opa_array_t *arr2c = opa_cast_array(opa_array()); + opa_array_append(arr2c, opa_string_terminated("foo")); + opa_array_append(arr2c, opa_string_terminated("")); + + opa_array_t *arr3 = opa_cast_array(opa_array()); + opa_array_append(arr3, opa_string_terminated("foo")); + opa_array_append(arr3, opa_string_terminated("bar")); + opa_array_append(arr3, opa_string_terminated("baz")); + + test("split/one", opa_value_compare(opa_strings_split(opa_string_terminated("foo"), opa_string_terminated(",")), &arr1->hdr) == 0); + test("split/two_a", opa_value_compare(opa_strings_split(opa_string_terminated("foo,bar"), opa_string_terminated(",")), &arr2->hdr) == 0); + test("split/two_b", opa_value_compare(opa_strings_split(opa_string_terminated(",,foo"), opa_string_terminated(",,")), &arr2b->hdr) == 0); + test("split/two_c", opa_value_compare(opa_strings_split(opa_string_terminated("foo,,"), opa_string_terminated(",,")), &arr2c->hdr) == 0); + test("split/three", opa_value_compare(opa_strings_split(opa_string_terminated("foo,,bar,,baz"), opa_string_terminated(",,")), &arr3->hdr) == 0); + + opa_array_t *arr4 = opa_cast_array(opa_array()); + opa_array_append(arr4, opa_string_terminated("f")); + opa_array_append(arr4, opa_string_terminated("o")); + opa_array_append(arr4, opa_string_terminated("o")); + + opa_array_t *arr5 = opa_cast_array(opa_array()); + opa_array_append(arr5, opa_string_terminated("f")); + opa_array_append(arr5, opa_string_terminated("\xE2\x82\xAC")); // euro symbol + opa_array_append(arr5, opa_string_terminated("o")); + + test("split/ascii", opa_value_compare(opa_strings_split(opa_string_terminated("foo"), opa_string_terminated("")), &arr4->hdr) == 0); + test("split/utf8", opa_value_compare(opa_strings_split(opa_string_terminated("f\xE2\x82\xACo"), opa_string_terminated("")), &arr5->hdr) == 0); + + opa_array_t *arr6 = opa_cast_array(opa_array()); + opa_array_append(arr6, opa_string_terminated("")); + test("split/empty", opa_value_compare(opa_strings_split(opa_string_terminated(""), opa_string_terminated(",")), &arr6->hdr) == 0); + + test("startswith/__", opa_value_compare(opa_strings_startswith(opa_string_terminated(""), opa_string_terminated("")), opa_boolean(true)) == 0); + test("startswith/_a", opa_value_compare(opa_strings_startswith(opa_string_terminated(""), opa_string_terminated("a")), opa_boolean(false)) == 0); + test("startswith/a_", opa_value_compare(opa_strings_startswith(opa_string_terminated("a"), opa_string_terminated("")), opa_boolean(true)) == 0); + test("startswith/aa", opa_value_compare(opa_strings_startswith(opa_string_terminated("a"), opa_string_terminated("a")), opa_boolean(true)) == 0); + test("startswith/ab", opa_value_compare(opa_strings_startswith(opa_string_terminated("a"), opa_string_terminated("b")), opa_boolean(false)) == 0); + test("startswith/aab", opa_value_compare(opa_strings_startswith(opa_string_terminated("a"), opa_string_terminated("ab")), opa_boolean(false)) == 0); + test("startswith/aba", opa_value_compare(opa_strings_startswith(opa_string_terminated("ab"), opa_string_terminated("a")), opa_boolean(true)) == 0); + test("startswith/aab", opa_value_compare(opa_strings_startswith(opa_string_terminated("aa"), opa_string_terminated("b")), opa_boolean(false)) == 0); + test("startswith/abab", opa_value_compare(opa_strings_startswith(opa_string_terminated("ab"), opa_string_terminated("ab")), opa_boolean(true)) == 0); + test("startswith/abaa", opa_value_compare(opa_strings_startswith(opa_string_terminated("ab"), opa_string_terminated("aa")), opa_boolean(false)) == 0); + test("startswith/abcab", opa_value_compare(opa_strings_startswith(opa_string_terminated("abc"), opa_string_terminated("ab")), opa_boolean(true)) == 0); + test("startswith/abcac", opa_value_compare(opa_strings_startswith(opa_string_terminated("abc"), opa_string_terminated("ac")), opa_boolean(false)) == 0); + + test("substring/_00", opa_value_compare(opa_strings_substring(opa_string_terminated(""), opa_number_int(0), opa_number_int(0)), opa_string_terminated("")) == 0); + test("substring/_0-1", opa_value_compare(opa_strings_substring(opa_string_terminated(""), opa_number_int(0), opa_number_int(-1)), opa_string_terminated("")) == 0); + test("substring/_10", opa_value_compare(opa_strings_substring(opa_string_terminated(""), opa_number_int(1), opa_number_int(0)), opa_string_terminated("")) == 0); + test("substring/_1-1", opa_value_compare(opa_strings_substring(opa_string_terminated(""), opa_number_int(1), opa_number_int(-1)), opa_string_terminated("")) == 0); + test("substring/abc1-1", opa_value_compare(opa_strings_substring(opa_string_terminated("abc"), opa_number_int(1), opa_number_int(-1)), opa_string_terminated("bc")) == 0); + test("substring/abc10", opa_value_compare(opa_strings_substring(opa_string_terminated("abc"), opa_number_int(1), opa_number_int(0)), opa_string_terminated("")) == 0); + test("substring/abc11", opa_value_compare(opa_strings_substring(opa_string_terminated("abc"), opa_number_int(1), opa_number_int(1)), opa_string_terminated("b")) == 0); + test("substring/abc12", opa_value_compare(opa_strings_substring(opa_string_terminated("abc"), opa_number_int(1), opa_number_int(2)), opa_string_terminated("bc")) == 0); + test("substring/abc41", opa_value_compare(opa_strings_substring(opa_string_terminated("abc"), opa_number_int(4), opa_number_int(1)), opa_string_terminated("")) == 0); + test("substring/unicode", opa_value_compare(opa_strings_substring(opa_string_terminated("\xC3\xA5\xC3\xA4\xC3\xB6\x7A"), opa_number_int(1), opa_number_int(1)), opa_string_terminated("\xC3\xA4")) == 0); + test("substring/unicode", opa_value_compare(opa_strings_substring(opa_string_terminated("\xC3\xA5\xC3\xA4\xC3\xB6\x7A"), opa_number_int(1), opa_number_int(2)), opa_string_terminated("\xC3\xA4\xC3\xB6")) == 0); + test("substring/unicode", opa_value_compare(opa_strings_substring(opa_string_terminated("\xC3\xA5\xC3\xA4\xC3\xB6\x7A"), opa_number_int(2), opa_number_int(-1)), opa_string_terminated("\xC3\xB6\x7A")) == 0); + + test("trim/__", opa_value_compare(opa_strings_trim(opa_string_terminated(""), opa_string_terminated("")), opa_string_terminated("")) == 0); + test("trim/abcba", opa_value_compare(opa_strings_trim(opa_string_terminated("abc"), opa_string_terminated("ba")), opa_string_terminated("c")) == 0); + + test("trim_left/__", opa_value_compare(opa_strings_trim_left(opa_string_terminated(""), opa_string_terminated("")), opa_string_terminated("")) == 0); + test("trim_left/_a", opa_value_compare(opa_strings_trim_left(opa_string_terminated(""), opa_string_terminated("a")), opa_string_terminated("")) == 0); + test("trim_left/a_", opa_value_compare(opa_strings_trim_left(opa_string_terminated("a"), opa_string_terminated("")), opa_string_terminated("a")) == 0); + test("trim_left/aa", opa_value_compare(opa_strings_trim_left(opa_string_terminated("a"), opa_string_terminated("a")), opa_string_terminated("")) == 0); + test("trim_left/ab", opa_value_compare(opa_strings_trim_left(opa_string_terminated("a"), opa_string_terminated("b")), opa_string_terminated("a")) == 0); + test("trim_left/aba", opa_value_compare(opa_strings_trim_left(opa_string_terminated("ab"), opa_string_terminated("a")), opa_string_terminated("b")) == 0); + test("trim_left/abcba", opa_value_compare(opa_strings_trim_left(opa_string_terminated("abc"), opa_string_terminated("ba")), opa_string_terminated("c")) == 0); + test("trim_left/aeuro dceuro ", opa_value_compare(opa_strings_trim_left(opa_string_terminated("a\xE2\x82\xAC d"), opa_string_terminated("ca\xE2\x82\xAC ")), opa_string_terminated("d")) == 0); + + test("trim_prefix/__", opa_value_compare(opa_strings_trim_prefix(opa_string_terminated(""), opa_string_terminated("")), opa_string_terminated("")) == 0); + test("trim_prefix/_a", opa_value_compare(opa_strings_trim_prefix(opa_string_terminated(""), opa_string_terminated("a")), opa_string_terminated("")) == 0); + test("trim_prefix/a_", opa_value_compare(opa_strings_trim_prefix(opa_string_terminated("a"), opa_string_terminated("")), opa_string_terminated("a")) == 0); + test("trim_prefix/aa", opa_value_compare(opa_strings_trim_prefix(opa_string_terminated("a"), opa_string_terminated("a")), opa_string_terminated("")) == 0); + test("trim_prefix/ab", opa_value_compare(opa_strings_trim_prefix(opa_string_terminated("a"), opa_string_terminated("b")), opa_string_terminated("a")) == 0); + test("trim_prefix/aba", opa_value_compare(opa_strings_trim_prefix(opa_string_terminated("ab"), opa_string_terminated("a")), opa_string_terminated("b")) == 0); + + test("trim_right/__", opa_value_compare(opa_strings_trim_right(opa_string_terminated(""), opa_string_terminated("")), opa_string_terminated("")) == 0); + test("trim_right/_a", opa_value_compare(opa_strings_trim_right(opa_string_terminated(""), opa_string_terminated("a")), opa_string_terminated("")) == 0); + test("trim_right/a_", opa_value_compare(opa_strings_trim_right(opa_string_terminated("a"), opa_string_terminated("")), opa_string_terminated("a")) == 0); + test("trim_right/aa", opa_value_compare(opa_strings_trim_right(opa_string_terminated("a"), opa_string_terminated("a")), opa_string_terminated("")) == 0); + test("trim_right/ab", opa_value_compare(opa_strings_trim_right(opa_string_terminated("a"), opa_string_terminated("b")), opa_string_terminated("a")) == 0); + test("trim_right/abb", opa_value_compare(opa_strings_trim_right(opa_string_terminated("ab"), opa_string_terminated("b")), opa_string_terminated("a")) == 0); + test("trim_right/abccb", opa_value_compare(opa_strings_trim_right(opa_string_terminated("abc"), opa_string_terminated("cb")), opa_string_terminated("a")) == 0); + test("trim_right/daeuro ceuro ", opa_value_compare(opa_strings_trim_right(opa_string_terminated("da\xE2\x82\xAC "), opa_string_terminated("ca\xE2\x82\xAC ")), opa_string_terminated("d")) == 0); + + test("trim_suffix/__", opa_value_compare(opa_strings_trim_suffix(opa_string_terminated(""), opa_string_terminated("")), opa_string_terminated("")) == 0); + test("trim_suffix/_a", opa_value_compare(opa_strings_trim_suffix(opa_string_terminated(""), opa_string_terminated("a")), opa_string_terminated("")) == 0); + test("trim_suffix/a_", opa_value_compare(opa_strings_trim_suffix(opa_string_terminated("a"), opa_string_terminated("")), opa_string_terminated("a")) == 0); + test("trim_suffix/aa", opa_value_compare(opa_strings_trim_suffix(opa_string_terminated("a"), opa_string_terminated("a")), opa_string_terminated("")) == 0); + test("trim_suffix/ab", opa_value_compare(opa_strings_trim_suffix(opa_string_terminated("a"), opa_string_terminated("b")), opa_string_terminated("a")) == 0); + test("trim_suffix/abb", opa_value_compare(opa_strings_trim_suffix(opa_string_terminated("ab"), opa_string_terminated("b")), opa_string_terminated("a")) == 0); + + test("trim_space/_", opa_value_compare(opa_strings_trim_space(opa_string_terminated("")), opa_string_terminated("")) == 0); + test("trim_space/a", opa_value_compare(opa_strings_trim_space(opa_string_terminated("a")), opa_string_terminated("a")) == 0); + test("trim_space/_a", opa_value_compare(opa_strings_trim_space(opa_string_terminated(" a")), opa_string_terminated("a")) == 0); + test("trim_space/a_", opa_value_compare(opa_strings_trim_space(opa_string_terminated("a ")), opa_string_terminated("a")) == 0); + test("trim_space/_a_", opa_value_compare(opa_strings_trim_space(opa_string_terminated(" a ")), opa_string_terminated("a")) == 0); + test("trim_space/______a_b_c______", opa_value_compare(opa_strings_trim_space(opa_string_terminated("\t\n\v\f\r a b c \r\f\v\n\t")), opa_string_terminated("a b c")) == 0); + test("trim_space/euro", opa_value_compare(opa_strings_trim_space(opa_string_terminated("\xE2\x82\xAC")), opa_string_terminated("\xE2\x82\xAC")) == 0); + test("trim_space/_euro_", opa_value_compare(opa_strings_trim_space(opa_string_terminated(" \xE2\x82\xAC ")), opa_string_terminated("\xE2\x82\xAC")) == 0); + test("trim_space/a_euro_", opa_value_compare(opa_strings_trim_space(opa_string_terminated("a \xE2\x82\xAC ")), opa_string_terminated("a \xE2\x82\xAC")) == 0); + test("trim_space/_euro_a", opa_value_compare(opa_strings_trim_space(opa_string_terminated(" \xE2\x82\xAC a")), opa_string_terminated("\xE2\x82\xAC a")) == 0); + test("trim_space/ogham_a", opa_value_compare(opa_strings_trim_space(opa_string_terminated("\xe1\x9a\x80 a")), opa_string_terminated("a")) == 0); + test("trim_space/oghamenspace_a", opa_value_compare(opa_strings_trim_space(opa_string_terminated("\xe1\x9a\x80\xe2\x80\x82 a")), opa_string_terminated("a")) == 0); + test("trim_space/a_oghamenspace_a", opa_value_compare(opa_strings_trim_space(opa_string_terminated("a \xe1\x9a\x80\xe2\x80\x82")), opa_string_terminated("a")) == 0); + + test("lower/_", opa_value_compare(opa_strings_lower(opa_string_terminated("")), opa_string_terminated("")) == 0); + test("lower/a", opa_value_compare(opa_strings_lower(opa_string_terminated("a")), opa_string_terminated("a")) == 0); + test("lower/A", opa_value_compare(opa_strings_lower(opa_string_terminated("A")), opa_string_terminated("a")) == 0); + test("lower/AbCd", opa_value_compare(opa_strings_lower(opa_string_terminated("AbCd")), opa_string_terminated("abcd")) == 0); + test("lower/utf-8", opa_value_compare(opa_strings_lower(opa_string_terminated("\xc4\x80")), opa_string_terminated("\xc4\x81")) == 0); + test("lower/utf-8", opa_value_compare(opa_strings_lower(opa_string_terminated("\xc9\x83")), opa_string_terminated("\xc6\x80")) == 0); + + test("upper/_", opa_value_compare(opa_strings_upper(opa_string_terminated("")), opa_string_terminated("")) == 0); + test("upper/a", opa_value_compare(opa_strings_upper(opa_string_terminated("a")), opa_string_terminated("A")) == 0); + test("upper/A", opa_value_compare(opa_strings_upper(opa_string_terminated("A")), opa_string_terminated("A")) == 0); + test("upper/AbCd", opa_value_compare(opa_strings_upper(opa_string_terminated("AbCd")), opa_string_terminated("ABCD")) == 0); + test("upper/utf-8", opa_value_compare(opa_strings_upper(opa_string_terminated("\xc4\x81")), opa_string_terminated("\xc4\x80")) == 0); + test("upper/utf-8", opa_value_compare(opa_strings_upper(opa_string_terminated("\xc6\x80")), opa_string_terminated("\xc9\x83")) == 0); +} + +WASM_EXPORT(test_numbers_range) +void test_numbers_range(void) +{ + opa_value *a = opa_number_int(10); + opa_value *b = opa_number_int(12); + + opa_value *exp = opa_array(); + opa_array_t *arr = opa_cast_array(exp); + opa_array_append(arr, opa_number_int(10)); + opa_array_append(arr, opa_number_int(11)); + opa_array_append(arr, opa_number_int(12)); + + test("number.range/ascending", opa_value_compare(opa_numbers_range(a, b), exp) == 0); + + opa_value *reversed = opa_array(); + arr = opa_cast_array(reversed); + opa_array_append(arr, opa_number_int(12)); + opa_array_append(arr, opa_number_int(11)); + opa_array_append(arr, opa_number_int(10)); + + test("numbers.range/descending", opa_value_compare(opa_numbers_range(b, a), reversed) == 0); + test("numbers.range/bad operand", opa_numbers_range(opa_string_terminated("foo"), opa_number_int(10)) == NULL); + test("numbers.range/bad operand", opa_numbers_range(opa_number_int(10), opa_string_terminated("foo")) == NULL); +} + +WASM_EXPORT(test_to_number) +void test_to_number(void) +{ + test("to_number/null", opa_value_compare(opa_to_number(opa_null()), opa_number_int(0)) == 0); + test("to_number/false", opa_value_compare(opa_to_number(opa_boolean(false)), opa_number_int(0)) == 0); + test("to_number/true", opa_value_compare(opa_to_number(opa_boolean(true)), opa_number_int(1)) == 0); + test("to_number/nop", opa_value_compare(opa_to_number(opa_number_int(1)), opa_number_int(1)) == 0); + test("to_number/integer", opa_value_compare(opa_to_number(opa_string_terminated("10")), opa_number_int(10)) == 0); + test("to_number/float", opa_value_compare(opa_to_number(opa_string_terminated("3.5")), opa_number_float(3.5)) == 0); + test("to_number/bad string", opa_to_number(opa_string_terminated("deadbeef")) == NULL); + test("to_number/bad value", opa_to_number(opa_array()) == NULL); +} + +WASM_EXPORT(test_cidr_contains) +void test_cidr_contains(void) +{ + test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("10.0.0.0/8"), opa_string_terminated("10.1.0.0/24")), opa_boolean(true)) == 0); + test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("172.17.0.0/24"), opa_string_terminated("172.17.0.0/16")), opa_boolean(false)) == 0); + test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("10.0.0.0/8"), opa_string_terminated("192.168.1.0/24")), opa_boolean(false)) == 0); + test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("10.0.0.0/8"), opa_string_terminated("10.1.1.1/32")), opa_boolean(true)) == 0); + test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("2001:4860:4860::8888/32"), opa_string_terminated("2001:4860:4860:1234::8888/40")), opa_boolean(true)) == 0); + test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("2001:4860:4860::8888/32"), opa_string_terminated("2001:4860:4860:1234:5678:1234:5678:8888/128")), opa_boolean(true)) == 0); + test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("2001:4860::/96"), opa_string_terminated("2001:4860::/32")), opa_boolean(false)) == 0); + test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("2001:4860::/32"), opa_string_terminated("fd1e:5bfe:8af3:9ddc::/64")), opa_boolean(false)) == 0); + test("cidr/contains", opa_cidr_contains(opa_string_terminated("not-a-cidr"), opa_string_terminated("192.168.1.67")) == NULL); + test("cidr/contains", opa_cidr_contains(opa_string_terminated("192.168.1.0/28"), opa_string_terminated("not-a-cidr")) == NULL); + test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("10.0.0.0/8"), opa_string_terminated("10.1.2.3")), opa_boolean(true)) == 0); + test("cidr/contains", opa_value_compare(opa_cidr_contains(opa_string_terminated("10.0.0.0/8"), opa_string_terminated("192.168.1.1")), opa_boolean(false)) == 0); + + test("cidr/contains", opa_value_compare(opa_cidr_intersects(opa_string_terminated("192.168.1.0/25"), opa_string_terminated("192.168.1.64/25")), opa_boolean(true)) == 0); + test("cidr/contains", opa_value_compare(opa_cidr_intersects(opa_string_terminated("192.168.1.0/24"), opa_string_terminated("192.168.2.0/24")), opa_boolean(false)) == 0); + test("cidr/contains", opa_value_compare(opa_cidr_intersects(opa_string_terminated("fd1e:5bfe:8af3:9ddc::/64"), opa_string_terminated("fd1e:5bfe:8af3:9ddc:1111::/72")), opa_boolean(true)) == 0); + test("cidr/contains", opa_value_compare(opa_cidr_intersects(opa_string_terminated("fd1e:5bfe:8af3:9ddc::/64"), opa_string_terminated("2001:4860:4860::8888/32")), opa_boolean(false)) == 0); + test("cidr/contains", opa_cidr_intersects(opa_string_terminated("not-a-cidr"), opa_string_terminated("192.168.1.0/24")) == NULL); + test("cidr/contains", opa_cidr_intersects(opa_string_terminated("192.168.1.0/28"), opa_string_terminated("not-a-cidr")) == NULL); +} + +opa_value *__new_value_path(int sz, ...) +{ + va_list ap; + opa_value *path = opa_array(); + + va_start(ap, sz); + + for (int i = 0; i < sz; i++) + { + const char* p = va_arg(ap, const char*); + opa_array_append(opa_cast_array(path), opa_string_terminated(p)); + } + + va_end(ap); + + return path; +} + +WASM_EXPORT(test_opa_value_add_path) +void test_opa_value_add_path() { + opa_value *data = opa_object(); + opa_value *update = opa_object(); + opa_value *path; + opa_errc rc; + + opa_object_insert(opa_cast_object(update), opa_string_terminated("a"), opa_number_int(1)); + + rc = opa_value_add_path(data, opa_array(), update); + test_errc_eq("empty_path_rc", OPA_ERR_INVALID_PATH, rc); + + // Setup base document + data = opa_object(); + opa_object_insert(opa_cast_object(data), opa_string_terminated("b"), opa_number_int(2)); + opa_object_insert(opa_cast_object(data), opa_string_terminated("c"), opa_number_int(3)); + opa_object_insert(opa_cast_object(data), opa_string_terminated("d"), opa_number_int(4)); + + // Upsert into existing object path + update = opa_object(); + opa_object_insert(opa_cast_object(update), opa_string_terminated("x"), opa_number_int(5)); + + path = __new_value_path(1, "b"); + rc = opa_value_add_path(data, path, update); + test_errc_eq("overwrite_sub_path_rc", OPA_ERR_OK, rc); + test_str_eq("overwrite_sub_path", "{\"d\":4,\"c\":3,\"b\":{\"x\":5}}", opa_json_dump(data)) + + // Upsert w/ creating nested path + update = opa_object(); + opa_object_insert(opa_cast_object(update), opa_string_terminated("foo"), opa_number_int(123)); + + path = __new_value_path(5, "b", "y", "z", "p", "q"); + rc = opa_value_add_path(data, path, update); + test_errc_eq("mkdir_rc", OPA_ERR_OK, rc); + char *exp = "{\"d\":4,\"c\":3,\"b\":{\"y\":{\"z\":{\"p\":{\"q\":{\"foo\":123}}}},\"x\":5}}"; + test_str_eq("mkdir", exp, opa_json_dump(data)); + + // NULL path + update = opa_object(); + rc = opa_value_add_path(update, NULL, opa_object()); + test_errc_eq("null_path_rc", OPA_ERR_INVALID_PATH, rc); + test_str_eq("null_path_unchanged", exp, opa_json_dump(data)); + + // non-array path types + rc = opa_value_add_path(update, opa_string_terminated("foo"), opa_object()); + test_errc_eq("invalid_string_path_rc", OPA_ERR_INVALID_PATH, rc); + test_str_eq("invalid_string_path_unchanged", exp, opa_json_dump(data)); + + rc = opa_value_add_path(update, opa_number_int(1), opa_object()); + test_errc_eq("invalid_number_path_rc", OPA_ERR_INVALID_PATH, rc); + test_str_eq("invalid_number_path_unchanged", exp, opa_json_dump(data)); + + rc = opa_value_add_path(update, opa_set(), opa_object()); + test_errc_eq("invalid_set_path_rc", OPA_ERR_INVALID_PATH, rc); + test_str_eq("invalid_set_path_unchanged", exp, opa_json_dump(data)); + + // invalid nested object type + opa_value *base = opa_object(); + opa_value *invalid_node = opa_set(); + opa_set_add(opa_cast_set(invalid_node), opa_string_terminated("y")); + opa_object_insert(opa_cast_object(base), opa_string_terminated("x"), invalid_node); + + exp = "{\"x\":[\"y\"]}"; + path = __new_value_path(3, "x", "y", "z"); + + rc = opa_value_add_path(base, path, opa_object()); + test_errc_eq("invalid_nested_object_in_path_rc", OPA_ERR_INVALID_TYPE, rc); + test_str_eq("invalid_nested_object_in_path_unchanged", exp, opa_json_dump(base)); + + // invalid nested object type leaf + base = opa_object(); + opa_object_insert(opa_cast_object(base), opa_string_terminated("x"), opa_number_int(5)); + exp = "{\"x\":5}"; + path = __new_value_path(3, "x", "y", "z"); + + rc = opa_value_add_path(base, path, opa_object()); + test_errc_eq("invalid_nested_object_at_path_end_rc", OPA_ERR_INVALID_TYPE, rc); + test_str_eq("invalid_nested_object_at_path_end_unchanged", exp, opa_json_dump(base)); +} + +WASM_EXPORT(test_opa_object_delete) +void test_opa_object_delete(void) +{ + opa_value *data = opa_object(); + + opa_object_insert(opa_cast_object(data), opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(opa_cast_object(data), opa_string_terminated("b"), opa_number_int(2)); + opa_object_insert(opa_cast_object(data), opa_string_terminated("c"), opa_number_int(3)); + + opa_object_remove(opa_cast_object(data), opa_string_terminated("a"), false); + test_str_eq("remove_key", "{\"c\":3,\"b\":2}", opa_json_dump(data)); + + opa_object_remove(opa_cast_object(data), opa_string_terminated("bad key"), false); + test_str_eq("remove_unknown_key", "{\"c\":3,\"b\":2}", opa_json_dump(data)); + + opa_object_remove(opa_cast_object(data), opa_string_terminated("b"), false); + opa_object_remove(opa_cast_object(data), opa_string_terminated("c"), false); + test_str_eq("remove_all_keys", "{}", opa_json_dump(data)); + + opa_object_remove(opa_cast_object(data), opa_string_terminated("bad key"), false); + test_str_eq("remove_on_empty_obj", "{}", opa_json_dump(data)); +} + +WASM_EXPORT(test_opa_value_remove_path) +void test_opa_value_remove_path(void) +{ + opa_value *path; + opa_errc rc; + + char *raw = "{\"a\":{\"b\":{\"c\":{\"d\":123}}},\"x\":[1,{\"y\":{\"z\":{}}}]}"; + opa_value *data = opa_json_parse(raw, strlen(raw)); + + path = opa_array(); + rc = opa_value_remove_path(data, path); + test_errc_eq("empty_path", OPA_ERR_INVALID_PATH, rc); + + // Reset back to full data doc + data = opa_json_parse(raw, strlen(raw)); + + path = __new_value_path(1, "foo"); + rc = opa_value_remove_path(data, path); + test_errc_eq("path_doesnt_exist_rc", OPA_ERR_OK, rc); + test_str_eq("path_doesnt_exist", raw, opa_json_dump(data)); + + path = __new_value_path(3, "a", "b", "foo"); + rc = opa_value_remove_path(data, path); + test_errc_eq("path_doesnt_exist_nested_rc", OPA_ERR_OK, rc); + test_str_eq("path_doesnt_exist_nested", raw, opa_json_dump(data)); + + path = __new_value_path(4, "a", "b", "c", "d"); + rc = opa_value_remove_path(data, path); + test_errc_eq("leaf_rc", OPA_ERR_OK, rc); + test_str_eq("leaf", "{\"a\":{\"b\":{\"c\":{}}},\"x\":[1,{\"y\":{\"z\":{}}}]}", opa_json_dump(data)); + + path = __new_value_path(2, "a", "b"); + rc = opa_value_remove_path(data, path); + test_errc_eq("branch_rc", OPA_ERR_OK, rc); + test_str_eq("branch", "{\"a\":{},\"x\":[1,{\"y\":{\"z\":{}}}]}", opa_json_dump(data)); + + path = __new_value_path(1, "a"); + rc = opa_value_remove_path(data, path); + test_errc_eq("branch_root_rc", OPA_ERR_OK, rc); + test_str_eq("branch_root", "{\"x\":[1,{\"y\":{\"z\":{}}}]}", opa_json_dump(data)); + + path = __new_value_path(3, "x", "1", "y"); + rc = opa_value_remove_path(data, path); + test_errc_eq("invalid_array_path_rc", OPA_ERR_OK, rc); + test_str_eq("invalid_array_path", "{\"x\":[1,{\"y\":{\"z\":{}}}]}", opa_json_dump(data)); + + path = opa_array(); + opa_array_append(opa_cast_array(path), opa_string_terminated("x")); + opa_array_append(opa_cast_array(path), opa_number_int(1)); + opa_array_append(opa_cast_array(path), opa_string_terminated("y")); + rc = opa_value_remove_path(data, path); + test_errc_eq("invalid_array_path_rc", OPA_ERR_INVALID_PATH, rc); + test_str_eq("array_index_path", "{\"x\":[1,{\"y\":{\"z\":{}}}]}", opa_json_dump(data)); + + rc = opa_value_remove_path(opa_object(), NULL); + test_errc_eq("null_path_rc", OPA_ERR_INVALID_PATH, rc); + test_str_eq("array_index_path", "{\"x\":[1,{\"y\":{\"z\":{}}}]}", opa_json_dump(data)); +} + +#define ARGS_N(...) ((int)(sizeof((int[]){ __VA_ARGS__ })/sizeof(int))) +#define BUILD(N, ...) build(N, ARGS_N(__VA_ARGS__), __VA_ARGS__) + +typedef struct { + int length; + int *positions; +} sequence; + +typedef struct { + int n; + sequence *sequences; +} sequences; + +static sequences* build(int n, int args, ...) +{ + va_list valist; + va_start(valist, args); + + sequences *s = malloc(sizeof(sequences)); + s->n = n; + s->sequences = malloc(n * sizeof(sequence)); + + for (int i = 0; i < n; i++) { + int run_length = args / n; + + int *positions = s->sequences[i].positions = malloc(sizeof(int) * run_length); + int j = 0; + for (; j < run_length; j++) + { + positions[j] = va_arg(valist, int); + } + s->sequences[i].length = j; + } + + va_end(valist); + return s; +} + +static void test_submatch_string(const char *s, sequence *seq, opa_value *result) +{ + for (int i = 0; i < seq->length; i += 2) + { + int start = seq->positions[i]; + int end = seq->positions[i+1]; + + opa_string_t *match = opa_cast_string(opa_value_get(result, opa_number_int(i/2))); + test("regex/find_all_string_submatch", opa_strncmp(&s[start], match->v, end-start) == 0); + } +} + +WASM_EXPORT(test_regex) +void test_regex(void) +{ + test("regex/is_valid", opa_value_compare(opa_regex_is_valid(opa_string_terminated(".*")), opa_boolean(true)) == 0); + test("regex/is_valid_non_string", opa_value_compare(opa_regex_is_valid(opa_number_int(123)), opa_boolean(false)) == 0); + + typedef struct { + const char *pat; + const char *text; + sequences *sequences; + } testcase; + + // golang find test cases from https://golang.org/src/regexp/find_test.go + testcase tests[] = { + {"", "", BUILD(1, 0, 0)}, + {"^abcdefg", "abcdefg", BUILD(1, 0, 7)}, + {"a+", "baaab", BUILD(1, 1, 4)}, + {"abcd..", "abcdef", BUILD(1, 0, 6)}, + {"a", "a", BUILD(1, 0, 1)}, + {"x", "y", NULL}, + {"b", "abc", BUILD(1, 1, 2)}, + {".", "a", BUILD(1, 0, 1)}, + {".*", "abcdef", BUILD(1, 0, 6)}, + {"^a", "abcde", BUILD(1, 0, 1)}, + {"^", "abcde", BUILD(1, 0, 0)}, + {"$", "abcde", BUILD(1, 5, 5)}, + {"^abcd$", "abcd", BUILD(1, 0, 4)}, + {"^bcd'", "abcdef", NULL}, + {"^abcd$", "abcde", NULL}, + {"a+", "baaab", BUILD(1, 1, 4)}, + {"a*", "baaab", BUILD(3, 0, 0, 1, 4, 5, 5)}, + {"[a-z]+", "abcd", BUILD(1, 0, 4)}, + {"[^a-z]+", "ab1234cd", BUILD(1, 2, 6)}, + {"[a\\-\\]z]+", "az]-bcz", BUILD(2, 0, 4, 6, 7)}, + {"[^\\n]+", "abcd\n", BUILD(1, 0, 4)}, + {"[日本語]+", "日本語日本語", BUILD(1, 0, 18)}, + {"日本語+", "日本語", BUILD(1, 0, 9)}, + {"日本語+", "日本語語語語", BUILD(1, 0, 18)}, + {"()", "", BUILD(1, 0, 0, 0, 0)}, + {"(a)", "a", BUILD(1, 0, 1, 0, 1)}, + {"(.)(.)", "日a", BUILD(1, 0, 4, 0, 3, 3, 4)}, + {"(.*)", "", BUILD(1, 0, 0, 0, 0)}, + {"(.*)", "abcd", BUILD(1, 0, 4, 0, 4)}, + {"(..)(..)", "abcd", BUILD(1, 0, 4, 0, 2, 2, 4)}, + {"(([^xyz]*)(d))", "abcd", BUILD(1, 0, 4, 0, 4, 0, 3, 3, 4)}, + {"((a|b|c)*(d))", "abcd", BUILD(1, 0, 4, 0, 4, 2, 3, 3, 4)}, + {"(((a|b|c)*)(d))", "abcd", BUILD(1, 0, 4, 0, 4, 0, 3, 2, 3, 3, 4)}, + {"\\a\\f\\n\\r\\t\\v", "\a\f\n\r\t\v", BUILD(1, 0, 6)}, + {"[\\a\\f\\n\\r\\t\\v]+", "\a\f\n\r\t\v", BUILD(1, 0, 6)}, + + {"a*(|(b))c*", "aacc", BUILD(1, 0, 4, 2, 2, -1, -1)}, + {"(.*).*", "ab", BUILD(1, 0, 2, 0, 2)}, + {"[.]", ".", BUILD(1, 0, 1)}, + {"/$", "/abc/", BUILD(1, 4, 5)}, + {"/$", "/abc", NULL}, + + // multiple matches + {".", "abc", BUILD(3, 0, 1, 1, 2, 2, 3)}, + {"(.)", "abc", BUILD(3, 0, 1, 0, 1, 1, 2, 1, 2, 2, 3, 2, 3)}, + {".(.)", "abcd", BUILD(2, 0, 2, 1, 2, 2, 4, 3, 4)}, + {"ab*", "abbaab", BUILD(3, 0, 3, 3, 4, 4, 6)}, + {"a(b*)", "abbaab", BUILD(3, 0, 3, 1, 3, 3, 4, 4, 4, 4, 6, 5, 6)}, + + // fixed bugs + {"ab$", "cab", BUILD(1, 1, 3)}, + {"axxb$", "axxcb", NULL}, + {"data", "daXY data", BUILD(1, 5, 9)}, + {"da(.)a$", "daXY data", BUILD(1, 5, 9, 7, 8)}, + {"zx+", "zzx", BUILD(1, 1, 3)}, + {"ab$", "abcab", BUILD(1, 3, 5)}, + {"(aa)*$", "a", BUILD(1, 1, 1, -1, -1)}, + {"(?:.|(?:.a))", "", NULL}, + {"(?:A(?:A|a))", "Aa", BUILD(1, 0, 2)}, + {"(?:A|(?:A|a))", "a", BUILD(1, 0, 1)}, + {"(a){0}", "", BUILD(1, 0, 0, -1, -1)}, + {"(?-s)(?:(?:^).)", "\n", NULL}, + {"(?s)(?:(?:^).)", "\n", BUILD(1, 0, 1)}, + {"(?:(?:^).)", "\n", NULL}, + {"\\b", "x", BUILD(2, 0, 0, 1, 1)}, + {"\\b", "xx", BUILD(2, 0, 0, 2, 2)}, + {"\\b", "x y", BUILD(4, 0, 0, 1, 1, 2, 2, 3, 3)}, + {"\\b", "xx yy", BUILD(4, 0, 0, 2, 2, 3, 3, 5, 5)}, + {"\\B", "x", NULL}, + {"\\B", "xx", BUILD(1, 1, 1)}, + {"\\B", "x y", NULL}, + {"\\B", "xx yy", BUILD(2, 1, 1, 4, 4)}, + + // RE2 tests + {"[^\\S\\s]", "abcd", NULL}, + {"[^\\S[:space:]]", "abcd", NULL}, + {"[^\\D\\d]", "abcd", NULL}, + {"[^\\D[:digit:]]", "abcd", NULL}, + {"(?i)\\W", "x", NULL}, + {"(?i)\\W", "k", NULL}, + {"(?i)\\W", "s", NULL}, + + // long set of matches (longer than startSize) + { + ".", + "qwertyuiopasdfghjklzxcvbnm1234567890", + BUILD(36, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, + 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, + 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, + 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36), + }, + }; + + for (int i = 0; i < sizeof(tests) / sizeof(testcase); i++) + { + test("regex/match", opa_value_compare(opa_regex_match(opa_string_terminated(tests[i].pat), opa_string_terminated(tests[i].text)), opa_boolean(tests[i].sequences != NULL)) == 0); + } + + for (int i = 0; i < sizeof(tests) / sizeof(testcase); i++) + { + opa_value *result = opa_regex_find_all_string_submatch(opa_string_terminated(tests[i].pat), opa_string_terminated(tests[i].text), opa_number_int(-1)); + opa_array_t *arr = opa_cast_array(result); + + if (tests[i].sequences == NULL) + { + test("regex/find_all_string_submatch (len)", arr->len == 0); + continue; + } + + test("regex/find_all_string_submatch (len)", arr->len == tests[i].sequences->n); + for (int j = 0; j < tests[i].sequences->n; j++) + { + test_submatch_string(tests[i].text, &tests[i].sequences->sequences[j], opa_value_get(result, opa_number_int(j))); + } + } +} + +WASM_EXPORT(test_opa_lookup) +void test_opa_lookup(void) +{ + opa_array_t *path1 = opa_cast_array(opa_array()); + opa_array_append(path1, opa_string_terminated("foo")); + opa_array_append(path1, opa_string_terminated("bar")); + opa_array_append(path1, opa_string_terminated("baz")); + + opa_object_t *mock_mapping = opa_cast_object(opa_object()); + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("baz"), opa_number_int(1)); + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("bar"), &obj1->hdr); + opa_object_insert(mock_mapping, opa_string_terminated("foo"), &obj2->hdr); + + opa_value *empty_mapping = opa_object(); + + opa_object_t *smaller_mapping = opa_cast_object(opa_object()); + opa_object_t *obj3 = opa_cast_object(opa_object()); + opa_object_insert(obj3, opa_string_terminated("bar"), opa_number_int(2)); + opa_object_insert(smaller_mapping, opa_string_terminated("foo"), &obj3->hdr); + + test("opa_lookup/hit", opa_lookup(&mock_mapping->hdr, &path1->hdr) == 1); + test("opa_lookup/miss", opa_lookup(empty_mapping, &path1->hdr) == 0); + test("opa_lookup/miss/less", opa_lookup(&smaller_mapping->hdr, &path1->hdr) == 0); +} + +WASM_EXPORT(test_opa_mapping_init) +void test_opa_mapping_init(void) +{ + opa_string_t *s = opa_cast_string(opa_string_terminated("{\"foo\": {\"bar\": 123}}")); + opa_mapping_init(s->v, s->len); + + opa_array_t *path1 = opa_cast_array(opa_array()); + opa_array_append(path1, opa_string_terminated("foo")); + opa_array_append(path1, opa_string_terminated("bar")); + test("opa_mapping_init/opa_lookup_works", opa_mapping_lookup(&path1->hdr) == 123); +} diff --git a/third_party/opa/wasm/tests/test.h b/third_party/opa/wasm/tests/test.h new file mode 100644 index 000000000000..7b0aeebc5bc3 --- /dev/null +++ b/third_party/opa/wasm/tests/test.h @@ -0,0 +1,51 @@ +#ifndef OPA_TEST_H +#define OPA_TEST_H + +#include "str.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void opa_test_fail(const char *note, const char *func, const char *file, int line); +void opa_test_pass(const char *note, const char *func); + +#define test_fatal(note) \ + { \ + opa_test_fail(note, __func__, __FILE__, __LINE__); \ + return; \ + } + +#define test(note, expr) \ + if (!(expr)) \ + { \ + opa_test_fail(note, __func__, __FILE__, __LINE__); \ + } \ + else \ + { \ + opa_test_pass(note, __func__); \ + } + +#define FAIL_TEMPLATE_STR "%s: expected: '%s' actual: '%s'" +#define FAIL_TEMPLATE_ERRC "%s: expected error: %d actual error: %d" + +#define test_with_exp(note, expr, exp, actual, template) \ + if (expr) \ + { \ + opa_test_pass(note, __func__); \ + } \ + else \ + { \ + char msg[256]; \ + snprintf(msg, 256, template, note, exp, actual); \ + opa_test_fail(msg, __func__, __FILE__, __LINE__); \ + } + +#define test_str_eq(note, exp, actual) test_with_exp(note, opa_strcmp(exp, actual) == 0, exp, actual, FAIL_TEMPLATE_STR) +#define test_errc_eq(note, exp, actual) test_with_exp(note, exp == actual, exp, actual, FAIL_TEMPLATE_ERRC) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/opa/wasm/tests/undefined.symbols b/third_party/opa/wasm/tests/undefined.symbols new file mode 100644 index 000000000000..1460c38e4683 --- /dev/null +++ b/third_party/opa/wasm/tests/undefined.symbols @@ -0,0 +1,2 @@ +opa_test_pass +opa_test_fail From 4d9639eb272ccc5cd63167b81ba1a3756bc0ae5b Mon Sep 17 00:00:00 2001 From: "SUSE Observability AI (POC)" Date: Thu, 1 Oct 2026 10:53:08 +0000 Subject: [PATCH 4/9] Preserve upstream fixtures and tidy standalone backport modules Retain OPA source tools and public test-only TLS keys/fixtures that the agent's root ignore rules otherwise omit. These files come unchanged from DataDog/opa d2e1e78e081663d4b11109032715b68eb9b9d17a. All three smaller owner suites pass with GOWORK=off. OPA AST, util, loader, configuration and strvals suites pass. Bundle fixtures pass with vet off; its two fatal-format vet errors reproduce on unmodified Datadog source. Tidy each independent backport without changing non-parser dependency versions. Cobra's documentation generator retains a test/tool legacy v3 requirement in standalone OPA, distinct from selected product packages. Tracking: https://github.com/StackVista/stackstate/issues/717 --- third_party/go-cfclient/go.mod | 2 +- third_party/go-cfclient/go.sum | 5 +- third_party/go-ordered-map/go.mod | 5 - third_party/go-ordered-map/go.sum | 21 +- third_party/goflow2/go.mod | 7 +- third_party/goflow2/go.sum | 20 +- third_party/opa/build/binary-smoke-test.sh | 72 + third_party/opa/build/build-release.sh | 61 + third_party/opa/build/changelog.py | 173 + third_party/opa/build/check-working-copy.sh | 31 + third_party/opa/build/commit-cli-docs.sh | 19 + third_party/opa/build/commit-wasm-bins.sh | 27 + .../opa/build/ensure-linux-toolchain.sh | 52 + .../opa/build/ensure-windows-toolchain.sh | 10 + third_party/opa/build/gen-cli-docs.sh | 5 + third_party/opa/build/gen-deb.sh | 63 + third_party/opa/build/gen-dev-patch.sh | 66 + third_party/opa/build/gen-man.sh | 3 + third_party/opa/build/gen-release-patch.sh | 93 + third_party/opa/build/gen-run-go.sh | 5 + .../opa/build/gen-windows-versioninfo.sh | 26 + .../opa/build/generate-cli-docs/generate.go | 98 + .../opa/build/generate-man/generate.go | 40 + third_party/opa/build/get-build-hostname.sh | 3 + third_party/opa/build/get-build-version.sh | 3 + third_party/opa/build/github-release.sh | 53 + third_party/opa/build/latest-release-notes.sh | 61 + third_party/opa/build/policy/files/files.rego | 68 + .../opa/build/policy/files/files_test.rego | 25 + .../opa/build/policy/schema/files.json | 84 + third_party/opa/build/run-wasm-rego-tests.sh | 85 + third_party/opa/build/time-bound.sh | 23 + third_party/opa/build/update-version.sh | 6 + third_party/opa/build/utils.sh | 34 + third_party/opa/docs/bin/smoke-test.sh | 30 + third_party/opa/docs/package-lock.json | 20220 ++++++++++++++++ third_party/opa/go.mod | 3 +- third_party/opa/go.sum | 4 +- .../opa/v1/rego/testdata/bundle.tar.gz | Bin 0 -> 48259 bytes .../v1/server/identifier/testdata/cn-cert.pem | 18 + .../opa/v1/server/identifier/testdata/key.pem | 27 + .../v1/server/identifier/testdata/ou-cert.pem | 18 + .../identifier/testdata/spiffe-svid-cert.pem | 14 + .../identifier/testdata/spiffe-svid-key.pem | 5 + .../v1/test/e2e/certrefresh/testdata/ca.pem | 19 + .../certrefresh/testdata/server-cert-new.pem | 18 + .../e2e/certrefresh/testdata/server-cert.pem | 18 + .../certrefresh/testdata/server-key-new.pem | 27 + .../e2e/certrefresh/testdata/server-key.pem | 27 + .../opa/v1/test/e2e/tls/testdata/ca.pem | 19 + .../test/e2e/tls/testdata/client-cert-2.pem | 17 + .../v1/test/e2e/tls/testdata/client-cert.pem | 17 + .../v1/test/e2e/tls/testdata/client-key-2.pem | 27 + .../v1/test/e2e/tls/testdata/client-key.pem | 27 + .../v1/test/e2e/tls/testdata/server-cert.pem | 18 + .../v1/test/e2e/tls/testdata/server-key.pem | 27 + third_party/opa/v1/topdown/testdata/ca.pem | 19 + .../opa/v1/topdown/testdata/client-cert-2.pem | 17 + .../opa/v1/topdown/testdata/client-cert.pem | 17 + .../opa/v1/topdown/testdata/client-key-2.pem | 27 + .../opa/v1/topdown/testdata/client-key.pem | 27 + .../opa/v1/topdown/testdata/server-cert.pem | 18 + .../opa/v1/topdown/testdata/server-key.pem | 27 + .../opa/wasm/build/gen-wasm-callgraph.sh | 5 + 64 files changed, 22058 insertions(+), 48 deletions(-) create mode 100755 third_party/opa/build/binary-smoke-test.sh create mode 100755 third_party/opa/build/build-release.sh create mode 100755 third_party/opa/build/changelog.py create mode 100755 third_party/opa/build/check-working-copy.sh create mode 100755 third_party/opa/build/commit-cli-docs.sh create mode 100755 third_party/opa/build/commit-wasm-bins.sh create mode 100755 third_party/opa/build/ensure-linux-toolchain.sh create mode 100755 third_party/opa/build/ensure-windows-toolchain.sh create mode 100755 third_party/opa/build/gen-cli-docs.sh create mode 100755 third_party/opa/build/gen-deb.sh create mode 100755 third_party/opa/build/gen-dev-patch.sh create mode 100755 third_party/opa/build/gen-man.sh create mode 100755 third_party/opa/build/gen-release-patch.sh create mode 100755 third_party/opa/build/gen-run-go.sh create mode 100755 third_party/opa/build/gen-windows-versioninfo.sh create mode 100644 third_party/opa/build/generate-cli-docs/generate.go create mode 100644 third_party/opa/build/generate-man/generate.go create mode 100755 third_party/opa/build/get-build-hostname.sh create mode 100755 third_party/opa/build/get-build-version.sh create mode 100755 third_party/opa/build/github-release.sh create mode 100755 third_party/opa/build/latest-release-notes.sh create mode 100644 third_party/opa/build/policy/files/files.rego create mode 100644 third_party/opa/build/policy/files/files_test.rego create mode 100644 third_party/opa/build/policy/schema/files.json create mode 100755 third_party/opa/build/run-wasm-rego-tests.sh create mode 100755 third_party/opa/build/time-bound.sh create mode 100755 third_party/opa/build/update-version.sh create mode 100644 third_party/opa/build/utils.sh create mode 100755 third_party/opa/docs/bin/smoke-test.sh create mode 100644 third_party/opa/docs/package-lock.json create mode 100644 third_party/opa/v1/rego/testdata/bundle.tar.gz create mode 100644 third_party/opa/v1/server/identifier/testdata/cn-cert.pem create mode 100644 third_party/opa/v1/server/identifier/testdata/key.pem create mode 100644 third_party/opa/v1/server/identifier/testdata/ou-cert.pem create mode 100644 third_party/opa/v1/server/identifier/testdata/spiffe-svid-cert.pem create mode 100644 third_party/opa/v1/server/identifier/testdata/spiffe-svid-key.pem create mode 100644 third_party/opa/v1/test/e2e/certrefresh/testdata/ca.pem create mode 100644 third_party/opa/v1/test/e2e/certrefresh/testdata/server-cert-new.pem create mode 100644 third_party/opa/v1/test/e2e/certrefresh/testdata/server-cert.pem create mode 100644 third_party/opa/v1/test/e2e/certrefresh/testdata/server-key-new.pem create mode 100644 third_party/opa/v1/test/e2e/certrefresh/testdata/server-key.pem create mode 100644 third_party/opa/v1/test/e2e/tls/testdata/ca.pem create mode 100644 third_party/opa/v1/test/e2e/tls/testdata/client-cert-2.pem create mode 100644 third_party/opa/v1/test/e2e/tls/testdata/client-cert.pem create mode 100644 third_party/opa/v1/test/e2e/tls/testdata/client-key-2.pem create mode 100644 third_party/opa/v1/test/e2e/tls/testdata/client-key.pem create mode 100644 third_party/opa/v1/test/e2e/tls/testdata/server-cert.pem create mode 100644 third_party/opa/v1/test/e2e/tls/testdata/server-key.pem create mode 100644 third_party/opa/v1/topdown/testdata/ca.pem create mode 100644 third_party/opa/v1/topdown/testdata/client-cert-2.pem create mode 100644 third_party/opa/v1/topdown/testdata/client-cert.pem create mode 100644 third_party/opa/v1/topdown/testdata/client-key-2.pem create mode 100644 third_party/opa/v1/topdown/testdata/client-key.pem create mode 100644 third_party/opa/v1/topdown/testdata/server-cert.pem create mode 100644 third_party/opa/v1/topdown/testdata/server-key.pem create mode 100755 third_party/opa/wasm/build/gen-wasm-callgraph.sh diff --git a/third_party/go-cfclient/go.mod b/third_party/go-cfclient/go.mod index c95add18395c..1bc95082cf4c 100644 --- a/third_party/go-cfclient/go.mod +++ b/third_party/go-cfclient/go.mod @@ -12,8 +12,8 @@ require ( github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 // indirect github.com/pkg/errors v0.8.1 github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a + go.yaml.in/yaml/v2 v2.4.4 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1 google.golang.org/protobuf v1.28.0 // indirect - go.yaml.in/yaml/v2 v2.4.4 // indirect ) diff --git a/third_party/go-cfclient/go.sum b/third_party/go-cfclient/go.sum index 215107b64d96..415e910d6b0b 100644 --- a/third_party/go-cfclient/go.sum +++ b/third_party/go-cfclient/go.sum @@ -26,6 +26,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -50,6 +52,3 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/third_party/go-ordered-map/go.mod b/third_party/go-ordered-map/go.mod index 8df1cec92a3f..09c8bba109cb 100644 --- a/third_party/go-ordered-map/go.mod +++ b/third_party/go-ordered-map/go.mod @@ -9,8 +9,3 @@ require ( github.com/stretchr/testify v1.12.1 go.yaml.in/yaml/v3 v3.0.5 ) - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect -) diff --git a/third_party/go-ordered-map/go.sum b/third_party/go-ordered-map/go.sum index 6a094c83a3c4..4a7ac8d054da 100644 --- a/third_party/go-ordered-map/go.sum +++ b/third_party/go-ordered-map/go.sum @@ -2,23 +2,10 @@ github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPn github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= diff --git a/third_party/goflow2/go.mod b/third_party/goflow2/go.mod index 6053df6d82d6..978611faa0c2 100644 --- a/third_party/goflow2/go.mod +++ b/third_party/goflow2/go.mod @@ -11,8 +11,8 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.12.1 github.com/xdg-go/scram v1.1.2 - google.golang.org/protobuf v1.30.0 go.yaml.in/yaml/v2 v2.4.4 + google.golang.org/protobuf v1.30.0 ) require ( @@ -32,21 +32,18 @@ require ( github.com/jcmturner/gokrb5/v8 v8.4.3 // indirect github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/klauspost/compress v1.15.14 // indirect - github.com/kr/text v0.2.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pierrec/lz4/v4 v4.1.17 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/text v0.7.0 // indirect - go.yaml.in/yaml/v3 v3.0.5 // indirect ) diff --git a/third_party/goflow2/go.sum b/third_party/goflow2/go.sum index ebde078ae1c1..8e7d9950a292 100644 --- a/third_party/goflow2/go.sum +++ b/third_party/goflow2/go.sum @@ -5,7 +5,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -48,9 +47,6 @@ github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZ github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/klauspost/compress v1.15.14 h1:i7WCKDToww0wA+9qrUZ1xOjp218vfFo3nTU6UHp+gOc= github.com/klauspost/compress v1.15.14/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560= github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -61,7 +57,6 @@ github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd918 github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= @@ -73,19 +68,16 @@ github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJf github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= @@ -93,6 +85,10 @@ github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3k github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= @@ -138,10 +134,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/third_party/opa/build/binary-smoke-test.sh b/third_party/opa/build/binary-smoke-test.sh new file mode 100755 index 000000000000..827a2fb72ca8 --- /dev/null +++ b/third_party/opa/build/binary-smoke-test.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +set -eo pipefail +OPA_EXEC="$1" +TARGET="$2" + +PATH_SEPARATOR="/" +BASE_PATH=$(pwd) +TEST_PATH="${BASE_PATH}/v1/test/cli/smoke/namespace/data.json" +if [[ $OPA_EXEC == *".exe" ]]; then + PATH_SEPARATOR="\\" + BASE_PATH=$(pwd -W) + TEST_PATH="$(echo ${BASE_PATH}/v1/test/cli/smoke/namespace/data.json | sed 's/^\///' | sed 's/\//\\\\/g')" + BASE_PATH=$(echo ${BASE_PATH} | sed 's/^\///' | sed 's/\//\\/g') +fi + +github_actions_group() { + local args="$*" + echo "::group::$args" + $args + echo "::endgroup::" +} + +opa() { + local args="$*" + github_actions_group $OPA_EXEC $args +} + +# assert_contains checks if the actual string contains the expected string. +assert_contains() { + local expected="$1" + local actual="$2" + if [[ "$actual" != *"$expected"* ]]; then + echo "Expected '$expected' but got '$actual'" + exit 1 + fi +} + +# assert_not_contains checks if the actual string does not contain the expected string. +assert_not_contains() { + local expected="$1" + local actual="$2" + if [[ "$actual" == *"$expected"* ]]; then + echo "Didn't expect '$expected' in '$actual'" + exit 0 + fi +} + +opa version +opa eval -t $TARGET 'time.now_ns()' +opa eval --format pretty --bundle v1/test/cli/smoke/golden-bundle.tar.gz --input v1/test/cli/smoke/input.json data.test.result --fail +opa exec --bundle v1/test/cli/smoke/golden-bundle.tar.gz --decision test/result v1/test/cli/smoke/input.json +opa build --output o0.tar.gz v1/test/cli/smoke/data.yaml v1/test/cli/smoke/test.rego +echo '{"yay": "bar"}' | opa eval --format pretty --bundle o0.tar.gz -I data.test.result --fail +opa build --optimize 1 --output o1.tar.gz v1/test/cli/smoke/data.yaml v1/test/cli/smoke/test.rego +echo '{"yay": "bar"}' | opa eval --format pretty --bundle o1.tar.gz -I data.test.result --fail +opa build --optimize 2 --output o2.tar.gz v1/test/cli/smoke/data.yaml v1/test/cli/smoke/test.rego +echo '{"yay": "bar"}' | opa eval --format pretty --bundle o2.tar.gz -I data.test.result --fail + +# Tar paths +opa build --output o3.tar.gz v1/test/cli/smoke +github_actions_group assert_contains '/v1/test/cli/smoke/test.rego' "$(tar -tf o3.tar.gz /v1/test/cli/smoke/test.rego)" + +# Data files - correct namespaces +echo "::group:: Data files - correct namespaces" +assert_contains "data.namespace | v1${PATH_SEPARATOR}test${PATH_SEPARATOR}cli${PATH_SEPARATOR}smoke${PATH_SEPARATOR}namespace${PATH_SEPARATOR}data.json" "$(opa inspect v1/test/cli/smoke)" +echo "::endgroup::" + +# Data files - correct root path +echo "::group:: Data files - correct root path" +assert_contains "${TEST_PATH}" "$(opa inspect ${BASE_PATH}/v1/test/cli/smoke -f json)" +assert_not_contains "\\\\${TEST_PATH}" "$(opa inspect ${BASE_PATH}/v1/test/cli/smoke -f json)" +echo "::endgroup::" \ No newline at end of file diff --git a/third_party/opa/build/build-release.sh b/third_party/opa/build/build-release.sh new file mode 100755 index 000000000000..86f8ca231133 --- /dev/null +++ b/third_party/opa/build/build-release.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# Script to build OPA releases. Assumes execution environment is golang Docker container. + +set -e + +OPA_DIR=/go/src/github.com/open-policy-agent/opa +BUILD_DIR=$OPA_DIR/build + +usage() { + echo "build-release.sh --output-dir=" + echo " --source-url=" + echo " [--version=]" +} + +for i in "$@"; do + case $i in + --source-url=*) + SOURCE_URL="${i#*=}" + shift + ;; + --output-dir=*) + OUTPUT_DIR="${i#*=}" + shift + ;; + --version=*) + VERSION="${i#*=}" + shift + ;; + *) + usage + exit 1 + ;; + esac +done + +if [ -z "$OUTPUT_DIR" ]; then + usage + exit 1 +elif [ -z "$SOURCE_URL" ]; then + usage + exit 1 +fi + +build_release() { + make build-all-platforms RELEASE_DIR="${OUTPUT_DIR}" +} + +clone_repo() { + git clone $SOURCE_URL /go/src/github.com/open-policy-agent/opa + cd /go/src/github.com/open-policy-agent/opa + if [ -n "$VERSION" ]; then + git checkout v${VERSION} + fi +} + +main() { + clone_repo + build_release +} + +main diff --git a/third_party/opa/build/changelog.py b/third_party/opa/build/changelog.py new file mode 100755 index 000000000000..75195a0e45b9 --- /dev/null +++ b/third_party/opa/build/changelog.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +""" +changelog.py helps generate the CHANGELOG.md message for a particular release. +""" + +import argparse +import os +import subprocess +import shlex +import re +import urllib.request, urllib.error, urllib.parse +import sys +import json + + +def run(cmd, *args, **kwargs): + return subprocess.check_output(shlex.split(cmd), *args, **kwargs).decode('utf-8') + + +def get_commit_ids(from_commit, to_commit): + cmd = "git log --format=%H --no-merges {from_commit}..{to_commit}" + commit_ids = run(cmd.format(from_commit=from_commit, + to_commit=to_commit)).splitlines() + return commit_ids + + +def get_commit_message(commit_id): + cmd = "git log --format=%B --max-count=1 {commit_id}".format( + commit_id=commit_id) + return run(cmd) + + +def fetch(url, token): + req = urllib.request.Request(url) + if token: + req.add_header('Authorization', "token {}".format(token)) + try: + rsp = urllib.request.urlopen(req) + result = json.loads(rsp.read()) + except Exception as e: + if hasattr(e, 'reason'): + print('Failed to fetch URL {}: {}'.format(url, e.reason), file=sys.stderr) + elif hasattr(e, 'code'): + print('Failed to fetch URL {}: Code {}'.format(url, e.code), file=sys.stderr) + return {} + else: + return result + +org_members_usernames = [] +def get_org_members(token): + url = "https://api.github.com/orgs/open-policy-agent/members?per_page=100" + r = fetch(url, token) + for m in r: + user_url = m.get('url', '') + user_info = fetch(user_url, token) + email = user_info.get('email', '') + login = user_info.get('login', '') + if login: + org_members_usernames.append(str(login)) + if email: + github_ids[email]=login + +def author_email(commit_message): + match = re.search(r"<(.*@.*)>", commit_message) + if match: + author = match.group(1) + return str(author) + return "" + +github_ids = {} +def get_github_id(commit_message, commit_id, token): + email = author_email(commit_message) + if github_ids.get(email, ""): + return github_ids[email] + url = "https://api.github.com/repos/open-policy-agent/opa/commits/{}".format(commit_id) + r = fetch(url, token) + author = r.get('author', {}) + if author is None: + return "" + login = author.get('login', '') + if login: + github_ids[email]=login + return login + return "" + + +def mention_author(commit_message, commit_id, token): + username = get_github_id(commit_message, commit_id, token) + return "authored by @{author}".format(author=username) + +def get_issue_reporter(issue_id, token): + url = "https://api.github.com/repos/open-policy-agent/opa/issues/{issue_id}".format(issue_id=issue_id) + issue_data = fetch(url, token) + username = issue_data.get("user", "").get("login", "") + if username not in org_members_usernames: + return "reported by @{reporter}".format(reporter=username) + return "" + +def fixes_issue_id(commit_message): + match = re.search(r"Fixes:?\s*#(\d+)", commit_message) + if match: + return match.group(1) + + +def get_subject(commit_message): + return commit_message.splitlines()[0] + +def get_changelog_message(commit_message, issue_id, mention, reporter, repo_url): + subject = get_subject(commit_message) + if issue_id: + if mention: + mention = " "+mention + if reporter: + reporter = " "+reporter + return "Fixes", "{subject} ([#{issue_id}]({repo_url}/issues/{issue_id})){mention}{reporter}".format(subject=subject, issue_id=issue_id, repo_url=repo_url, mention=mention, reporter=reporter) + if mention: + mention = " (" + mention + ")" + return None, "{subject}{mention}".format(subject=subject, mention=mention) + + +def get_latest_tag(): + cmd = "git describe --tags --first-parent" + return run(cmd).split('-')[0] + + +def parse_args(): + if "GITHUB_TOKEN" in os.environ: + default_token = os.environ["GITHUB_TOKEN"] + parser = argparse.ArgumentParser() + parser.add_argument( + "--repo_url", default="https://github.com/open-policy-agent/opa") + parser.add_argument("--token", default=default_token, help="GitHub API token") + parser.add_argument("from_version", nargs="?", + default=get_latest_tag(), help="start of changes") + parser.add_argument("to_commit", nargs="?", + default="HEAD", help="end of changes") + return parser.parse_args() + + +def main(): + + args = parse_args() + changelog = {} + get_org_members(args.token) + for commit_id in get_commit_ids(args.from_version, args.to_commit): + mention = "" + reporter = "" + commit_message = get_commit_message(commit_id) + mention = mention_author(commit_message, commit_id, args.token) + issue_id = fixes_issue_id(commit_message) + if issue_id: + reporter = get_issue_reporter(issue_id, args.token) + if mention.split("/")[-1] == reporter.split("/")[-1]: + reporter = "" + group, line = get_changelog_message(commit_message, issue_id, mention, reporter, args.repo_url) + changelog.setdefault(group, []).append(line) + + if "Fixes" in changelog: + print("### Fixes") + print("") + for line in sorted(changelog["Fixes"]): + print("- {}".format(line)) + print("") + + if None in changelog: + print("### Miscellaneous") + print("") + for line in sorted(changelog[None]): + print("- {}".format(line)) + + +if __name__ == "__main__": + main() diff --git a/third_party/opa/build/check-working-copy.sh b/third_party/opa/build/check-working-copy.sh new file mode 100755 index 000000000000..c4bff64ca16e --- /dev/null +++ b/third_party/opa/build/check-working-copy.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +EXCEPTIONS=( + "internal/compiler/wasm/opa/opa.wasm" + "internal/compiler/wasm/opa/callgraph.csv" +) + +STATUS=$(git status --porcelain) + +HAS_CHANGES=0 + +if [[ -z "${STATUS}" ]]; then + exit 0 +else + for file in $(echo -E "${STATUS}" | awk '{print $2}'); do + if [[ "${EXCEPTIONS[@]}" =~ "${file}" ]]; then + echo "Ignoring changed file: ${file}" + else + HAS_CHANGES=1 + fi + done +fi + +if [[ "${HAS_CHANGES}" == "1" ]]; then + echo "" + echo "git status" + git status + echo "git diff" + git diff + exit 1 +fi diff --git a/third_party/opa/build/commit-cli-docs.sh b/third_party/opa/build/commit-cli-docs.sh new file mode 100755 index 000000000000..a1c00e8f3d88 --- /dev/null +++ b/third_party/opa/build/commit-cli-docs.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +OPA_DIR=$(dirname "${BASH_SOURCE}")/.. + +cd "${OPA_DIR}" + +git add docs/content/cli.md + +if [[ -z "$(git diff --name-only --cached)" ]]; then + echo "No CLI doc changes to commit" + exit 1 +fi + +git commit -m "docs: Update generated CLI docs" + +echo "" +echo "Committed changes for files:" +git diff-tree --no-commit-id --name-only -r HEAD +echo "" diff --git a/third_party/opa/build/commit-wasm-bins.sh b/third_party/opa/build/commit-wasm-bins.sh new file mode 100755 index 000000000000..af138513b93d --- /dev/null +++ b/third_party/opa/build/commit-wasm-bins.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +OPA_DIR=$(dirname "${BASH_SOURCE}")/.. + +cd ${OPA_DIR} + +WASMFILES=( + "internal/compiler/wasm/opa/opa.go" + "internal/compiler/wasm/opa/opa.wasm" + "internal/compiler/wasm/opa/callgraph.csv" +) + +for file in "${WASMFILES[@]}"; do + git add ${file} +done + +if [[ -z "$(git diff --name-only --cached)" ]]; then + echo "No Wasm changes to commit!" + exit 1 +fi + +git commit -m "wasm: Update generated binaries" + +echo "" +echo "Committed changes for files:" +git diff-tree --no-commit-id --name-only -r HEAD +echo "" diff --git a/third_party/opa/build/ensure-linux-toolchain.sh b/third_party/opa/build/ensure-linux-toolchain.sh new file mode 100755 index 000000000000..ee687ffb3f3f --- /dev/null +++ b/third_party/opa/build/ensure-linux-toolchain.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -eo pipefail + +case "$(uname -m | tr '[:upper:]' '[:lower:]')" in + amd64 | x86_64 | x64) + HOST_ARCH=amd64 + ;; + arm64 | aarch64) + HOST_ARCH=arm64 + ;; + *) + echo "Error: Host architecture not supported." >&2 + exit 1 + ;; +esac + +# Native build +if [ "${GOARCH}" = "${HOST_ARCH}" ]; then + if ! [ -x "$(command -v gcc)" ]; then + echo "Error: gcc not found." >&2 + exit 1 + fi + exit 0 +fi + +# Cross-compile +case "${GOARCH}" in + amd64) + PKG=gcc-x86-64-linux-gnu + CC=x86_64-linux-gnu-gcc + ;; + arm64) + PKG=gcc-aarch64-linux-gnu + CC=aarch64-linux-gnu-gcc + ;; + *) + echo "Error: Target architecture ${GOARCH} not supported." >&2 + exit 1 + ;; +esac + +type -f ${CC} 2>/dev/null && exit 0 + +if ! [ -x "$(command -v apt-get)" ]; then + echo "Error: apt-get not found. Could not install missing toolchain." >&2 + exit 1 +fi + +apt-get update >/dev/null && \ + apt-get install -y ${PKG} >/dev/null + +echo ${CC} diff --git a/third_party/opa/build/ensure-windows-toolchain.sh b/third_party/opa/build/ensure-windows-toolchain.sh new file mode 100755 index 000000000000..e3ad5549d509 --- /dev/null +++ b/third_party/opa/build/ensure-windows-toolchain.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -exo pipefail +CC=x86_64-w64-mingw32-gcc +PKG=gcc-mingw-w64-x86-64 + +type -f ${CC} 2>/dev/null && exit 0 + +apt-get update && \ + apt-get install --no-install-recommends -y ${PKG} diff --git a/third_party/opa/build/gen-cli-docs.sh b/third_party/opa/build/gen-cli-docs.sh new file mode 100755 index 000000000000..1412b4f77cac --- /dev/null +++ b/third_party/opa/build/gen-cli-docs.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +SCRIPT_DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd) + +GOOS="" GOARCH="" go run "$SCRIPT_DIR"/generate-cli-docs/generate.go "$@" diff --git a/third_party/opa/build/gen-deb.sh b/third_party/opa/build/gen-deb.sh new file mode 100755 index 000000000000..9a747c9b41b0 --- /dev/null +++ b/third_party/opa/build/gen-deb.sh @@ -0,0 +1,63 @@ +set -e + +if [ ! -f "opa_linux_amd64" ]; then + echo "ERROR: File 'opa_linux_amd64' not found. First build it 'make build-linux'." + exit 1 +fi + +if [ ! -d "man" ]; then + echo "ERROR: The man pages not found. First build them 'make man'." + exit 1 +fi + +if [ -z "$VERSION" ]; then + echo "ERROR: Need to have VERSION environment variable set." + exit 1 +fi + +if ! command -v dpkg-deb >/dev/null 2>/dev/null; then + echo "ERROR: 'dpkg-deb' was not found and is required." + exit 1 +fi + +rm -rf tmp_working_dir_opa +mkdir tmp_working_dir_opa +cd tmp_working_dir_opa + +mkdir -p ./usr/bin/ +cp ../opa_linux_amd64 ./usr/bin/opa +chmod +x ./usr/bin/opa + +mkdir -p ./usr/share/man/man1/ +for MAN_PAGE in ../man/*.1; do + gzip --keep --best $MAN_PAGE +done +mv ../man/*.1.gz ./usr/share/man/man1/ + +echo "Package: opa" > control +echo "Version: $VERSION" >> control +cat << 'EOF' >> control +Architecture: amd64 +Maintainer: OPA Maintainers +Depends: libc6 (>= 2.2.5) +Section: admin +Priority: optional +Homepage: https://openpolicyagent.org +Description: An open source, general-purpose policy engine. + The Open Policy Agent (OPA) is an open source, general-purpose + policy engine that enables unified, context-aware policy + enforcement across the entire stack. +EOF + +md5sum ../opa_linux_amd64 > md5sums + +mkdir -p opa_$VERSION/DEBIAN +mv control opa_$VERSION/DEBIAN/control +mv md5sums opa_$VERSION/DEBIAN/md5sums +mv ./usr opa_$VERSION/usr + +dpkg-deb -b opa_$VERSION/ + +mv opa_$VERSION.deb ../ +cd .. +rm -rf tmp_working_dir_opa diff --git a/third_party/opa/build/gen-dev-patch.sh b/third_party/opa/build/gen-dev-patch.sh new file mode 100755 index 000000000000..e8ff007fce19 --- /dev/null +++ b/third_party/opa/build/gen-dev-patch.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +stty -onlcr # don't translate newline to carriage return-newline, as these break patch tool +set -e + +OPA_DIR=/go/src/github.com/open-policy-agent/opa + +usage() { + echo "gen-dev-patch.sh --source-url=" + echo " --version=" +} + +for i in "$@"; do + case $i in + --source-url=*) + SOURCE_URL="${i#*=}" + shift + ;; + --version=*) + VERSION="${i#*=}" + shift + ;; + *) + usage + exit 1 + ;; + esac +done + +if [ -z "$SOURCE_URL" ]; then + usage + exit 1 +elif [ -z "$VERSION" ]; then + usage + exit 1 +fi + +git clone $SOURCE_URL $OPA_DIR +cd $OPA_DIR + +LAST_VERSION=$(git describe --abbrev=0 --tags | cut -c 2-) + +update_version() { + ./build/update-version.sh "$VERSION-dev" +} + +update_changelog() { + cat >_CHANGELOG.md <" + echo " --version=" +} + +for i in "$@"; do + case $i in + --source-url=*) + SOURCE_URL="${i#*=}" + shift + ;; + --version=*) + VERSION="${i#*=}" + shift + ;; + *) + usage + exit 1 + ;; + esac +done + +if [ -z "$SOURCE_URL" ]; then + usage + exit 1 +elif [ -z "$VERSION" ]; then + usage + exit 1 +fi + +git clone $SOURCE_URL $OPA_DIR +cd $OPA_DIR + +if [ -z "$LAST_VERSION" ]; then + LAST_VERSION=$(git describe --abbrev=0 --tags) +fi + +update_version() { + ./build/update-version.sh "$VERSION" +} + +update_changelog() { + if $(grep -q '## Unreleased' CHANGELOG.md) ; then + cat >_CHANGELOG.md <_CHANGELOG.md < /dev/null; then + # If goversioninfo isn't on the path, print an error message + echo "Error: goversioninfo command not found" >&2 + exit 1 +fi + +goversioninfo "${FLAGS[@]}" \ + -product-name "$NAME" \ + -product-version "$VERSION" \ + -skip-versioninfo \ + -icon=logo/logo.ico \ + -64=true \ + -o resource.syso \ No newline at end of file diff --git a/third_party/opa/build/generate-cli-docs/generate.go b/third_party/opa/build/generate-cli-docs/generate.go new file mode 100644 index 000000000000..54f4c8dfbfe0 --- /dev/null +++ b/third_party/opa/build/generate-cli-docs/generate.go @@ -0,0 +1,98 @@ +package main + +import ( + "encoding/json" + "log" + "os" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/open-policy-agent/opa/cmd" +) + +func main() { + command := cmd.Command(nil, "opa") + command.Use = "opa [command]" + command.DisableAutoGenTag = true + + cmdData := make([]map[string]any, 0) + + for _, c := range command.Commands() { + if !showCommand(c) { + continue + } + + cmdData = append(cmdData, cmdToData(c)) + } + + err := json.NewEncoder(os.Stdout).Encode(cmdData) + if err != nil { + log.Fatal(err) + } +} + +func showCommand(c *cobra.Command) bool { + if !c.IsAvailableCommand() || + c.IsAdditionalHelpTopicCommand() || + c.Hidden { + return false + } + + return true +} + +func cmdToID(c *cobra.Command) string { + parts := strings.Split(c.Use, " ") + + if len(parts) == 0 { + return "" + } + + return parts[0] +} + +func extractFlags(flagSet *pflag.FlagSet) []map[string]any { + var result []map[string]any + + flagSet.VisitAll(func(f *pflag.Flag) { + flagInfo := map[string]any{ + "name": "--" + f.Name, + "shorthand": "", + "type": f.Value.Type(), + "default": f.DefValue, + "description": f.Usage, + } + + if f.Shorthand != "" { + flagInfo["shorthand"] = "-" + f.Shorthand + } + + result = append(result, flagInfo) + }) + + return result +} + +func cmdToData(c *cobra.Command) map[string]any { + childData := make([]map[string]any, 0) + for _, childCmd := range c.Commands() { + if !showCommand(childCmd) { + continue + } + childData = append(childData, cmdToData(childCmd)) + } + + return map[string]any{ + "id": cmdToID(c), + "use": c.Use, + "useline": c.UseLine(), + "short": c.Short, + "long": c.Long, + "example": c.Example, + "flags": extractFlags(c.NonInheritedFlags()), + "parent_flags": extractFlags(c.InheritedFlags()), + "children": childData, + } +} diff --git a/third_party/opa/build/generate-man/generate.go b/third_party/opa/build/generate-man/generate.go new file mode 100644 index 000000000000..cc14db0c3431 --- /dev/null +++ b/third_party/opa/build/generate-man/generate.go @@ -0,0 +1,40 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package main + +import ( + "log" + "os" + + "github.com/spf13/cobra/doc" + + "github.com/open-policy-agent/opa/cmd" +) + +func main() { + if len(os.Args) != 2 { + log.Fatal("Required argument: man pages output directory") + } + out := os.Args[1] + err := os.MkdirAll(out, os.ModePerm) + if err != nil { + log.Fatal(err) + } + + cmd := cmd.RootCommand + cmd.Use = "opa [command]" + cmd.DisableAutoGenTag = true + + header := &doc.GenManHeader{ + Title: "Open Policy Agent", + Section: "1", + Source: " ", + } + + err = doc.GenManTree(cmd, header, out) + if err != nil { + log.Fatal(err) + } +} diff --git a/third_party/opa/build/get-build-hostname.sh b/third_party/opa/build/get-build-hostname.sh new file mode 100755 index 000000000000..0ed5c926ceda --- /dev/null +++ b/third_party/opa/build/get-build-hostname.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +hostname -f diff --git a/third_party/opa/build/get-build-version.sh b/third_party/opa/build/get-build-version.sh new file mode 100755 index 000000000000..50b9d73d32e8 --- /dev/null +++ b/third_party/opa/build/get-build-version.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +awk -F'"' '/^var Version/{print $2}' v1/version/version.go \ No newline at end of file diff --git a/third_party/opa/build/github-release.sh b/third_party/opa/build/github-release.sh new file mode 100755 index 000000000000..b46962f2b5f3 --- /dev/null +++ b/third_party/opa/build/github-release.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# Script to draft and edit OPA GitHub releases. Assumes execution environment is Github Action runner. + +set -x + +usage() { + echo "github-release.sh [--asset-dir=] [--tag=]" + echo " Default --asset-dir is $PWD and --tag $TAG_NAME " +} + +TAG_NAME=${TAG_NAME} +ASSET_DIR=${PWD:-"./"} + +for i in "$@"; do + case $i in + --asset-dir=*) + ASSET_DIR="${i#*=}" + shift + ;; + --tag=*) + TAG_NAME="${i#*=}" + shift + ;; + *) + usage + exit 1 + ;; + esac +done + +# Collect a list of opa binaries (expect binaries in the form: opa__[extension]) +ASSETS=() +for asset in "${ASSET_DIR}"/opa_*_*; do + ASSETS+=("$asset") +done + +# Gather the release notes from the CHANGELOG for the latest version +RELEASE_NOTES="release-notes.md" + +# The hub CLI expects the first line to be the title +echo -e "${TAG_NAME}\n" > "${RELEASE_NOTES}" + +# Fill in the description +./build/latest-release-notes.sh --output="${RELEASE_NOTES}" + +# Update or create a release on github +if gh release view "${TAG_NAME}" --repo open-policy-agent/opa > /dev/null; then + # Occurs when the tag is created via GitHub UI w/ a release + gh release upload "${TAG_NAME}" "${ASSETS[@]}" --repo open-policy-agent/opa +else + # Create a draft release + gh release create "${TAG_NAME}" "${ASSETS[@]}" -F ${RELEASE_NOTES} --draft --title "${TAG_NAME}" --repo open-policy-agent/opa +fi diff --git a/third_party/opa/build/latest-release-notes.sh b/third_party/opa/build/latest-release-notes.sh new file mode 100755 index 000000000000..999021cf5c15 --- /dev/null +++ b/third_party/opa/build/latest-release-notes.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +set -e + +OPA_DIR=$(dirname "${BASH_SOURCE}")/.. +CHANGELOG="${OPA_DIR}/CHANGELOG.md" + +usage() { + echo "latest-release-notes.sh --output=" +} + +OUTPUT="" + +for i in "$@"; do + case $i in + --output=*) + OUTPUT="${i#*=}" + shift + ;; + *) + usage + exit 1 + ;; + esac +done + +if [ -z "${OUTPUT}" ]; then + usage + exit 1 +fi + +# Versions start with a h2 (## ), find the latest two for start and stop +# positions in the CHANGELOG +LATEST_VERSION=$(grep '## [0-9]' "${CHANGELOG}" | head -n 1) +STOP_VERSION=$(grep '## [0-9]' "${CHANGELOG}" | head -n 2 | tail -n 1) + +STARTED=false + +while IFS= read -r line +do + # Skip lines until the first version header is found + if [[ "${STARTED}" == false ]]; then + if [[ "${line}" == "${LATEST_VERSION}" ]]; then + STARTED=true + fi + continue + fi + + # Stop reading after we see the stopping point + if [[ "${line}" == "${STOP_VERSION}" ]]; then + break + fi + + # Append each line between the two onto the release notes + echo -e "${line}" >> "${OUTPUT}" + +done < "${CHANGELOG}" + +# Delete all leading blank lines at top of file +sed -i.bak '/./,$!d' "${OUTPUT}" +rm "${OUTPUT}.bak" diff --git a/third_party/opa/build/policy/files/files.rego b/third_party/opa/build/policy/files/files.rego new file mode 100644 index 000000000000..16883264c38f --- /dev/null +++ b/third_party/opa/build/policy/files/files.rego @@ -0,0 +1,68 @@ +# METADATA +# description: | +# Expects policy input as provided by: +# https://api.github.com/repos/open-policy-agent/opa/pulls/${PR_ID}/files +# +# Note that the "filename" here refers to the full, relative path of the file, +# like docs/foo/bar.yaml - since that's how it's named in the input we'll use +# the same convention here. +# schemas: +# - input: schema.files +package files + +# METADATA +# entrypoint: true +deny contains sprintf("%s is an invalid YAML file: %s", [filename, content]) if { + some filename, content in yaml_file_contents + changes[filename].status in {"added", "modified"} + not yaml.is_valid(content) +} + +deny contains sprintf("%s is an invalid JSON file: %s", [filename, content]) if { + some filename, content in json_file_contents + changes[filename].status in {"added", "modified"} + not json.is_valid(content) +} + +yaml_file_contents[filename] := file_in_pr(filename) if { + some filename in filenames + extension(filename) in {"yml", "yaml"} +} + +json_file_contents[filename] := file_in_pr(filename) if { + some filename in filenames + extension(filename) == "json" +} + +filenames contains f.filename if some f in input + +changes[filename] := attributes if { + some change in input + filename := change.filename + attributes := object.remove(change, ["filename"]) +} + +http_error(response) if response.status_code == 0 +http_error(response) if response.status_code >= 400 + +dump_response_on_error(response) := response if { + http_error(response) + print("unexpected error in response", response) # regal ignore:print-or-trace-call +} + +dump_response_on_error(response) := response if not http_error(response) + +file_in_pr(filename) := dump_response_on_error(http.send({ + "url": changes[filename].raw_url, + "method": "GET", + "headers": {"Authorization": sprintf("Bearer %v", [opa.runtime().env.GITHUB_TOKEN])}, + "cache": true, + "enable_redirect": true, + "raise_error": false, +})).raw_body + +default last_indexof(_, _) := -1 + +last_indexof(string, search) := indices[count(indices) - 1] if indices := indexof_n(string, search) + +extension(filename) := substring(filename, last_indexof(filename, ".") + 1, count(filename) - 1) diff --git a/third_party/opa/build/policy/files/files_test.rego b/third_party/opa/build/policy/files/files_test.rego new file mode 100644 index 000000000000..009135c1ce23 --- /dev/null +++ b/third_party/opa/build/policy/files/files_test.rego @@ -0,0 +1,25 @@ +package files_test + +import data.files + +test_deny_invalid_yaml_file if { + expected := "invalid.yaml is an invalid YAML file: {null{}}" + expected in files.deny with files.yaml_file_contents as {"invalid.yaml": "{null{}}"} + with files.changes as {"invalid.yaml": {"status": "modified"}} +} + +test_allow_valid_yaml_file if { + count(files.deny) == 0 with files.yaml_file_contents as {"valid.yaml": "foo: bar"} + with files.changes as {"valid.yaml": {"status": "modified"}} +} + +test_deny_invalid_json_file if { + expected := "invalid.json is an invalid JSON file: }}}" + expected in files.deny with files.json_file_contents as {"invalid.json": "}}}"} + with files.changes as {"invalid.json": {"status": "modified"}} +} + +test_allow_valid_json_file if { + count(files.deny) == 0 with files.json_file_contents as {"valid.json": "{\"foo\": \"bar\"}"} + with files.changes as {"valid.json": {"status": "modified"}} +} diff --git a/third_party/opa/build/policy/schema/files.json b/third_party/opa/build/policy/schema/files.json new file mode 100644 index 000000000000..26102ecdb29a --- /dev/null +++ b/third_party/opa/build/policy/schema/files.json @@ -0,0 +1,84 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "files", + "$ref": "#/$defs/files", + "$defs": { + "files": { + "type": "array", + "items": { + "title": "Diff Entry", + "description": "Diff Entry", + "type": "object", + "properties": { + "sha": { + "type": "string", + "example": "bbcd538c8e72b8c175046e27cc8f907076331401" + }, + "filename": { + "type": "string", + "example": "file1.txt" + }, + "status": { + "type": "string", + "enum": [ + "added", + "removed", + "modified", + "renamed", + "copied", + "changed", + "unchanged" + ], + "example": "added" + }, + "additions": { + "type": "integer", + "example": 103 + }, + "deletions": { + "type": "integer", + "example": 21 + }, + "changes": { + "type": "integer", + "example": 124 + }, + "blob_url": { + "type": "string", + "format": "uri", + "example": "https://github.com/octocat/Hello-World/blob/6dcb09b5b57875f334f61aebed695e2e4193db5e/file1.txt" + }, + "raw_url": { + "type": "string", + "format": "uri", + "example": "https://github.com/octocat/Hello-World/raw/6dcb09b5b57875f334f61aebed695e2e4193db5e/file1.txt" + }, + "contents_url": { + "type": "string", + "format": "uri", + "example": "https://api.github.com/repos/octocat/Hello-World/contents/file1.txt?ref=6dcb09b5b57875f334f61aebed695e2e4193db5e" + }, + "patch": { + "type": "string", + "example": "@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test" + }, + "previous_filename": { + "type": "string", + "example": "file.txt" + } + }, + "required": [ + "additions", + "blob_url", + "changes", + "contents_url", + "deletions", + "filename", + "raw_url", + "sha", + "status" + ] + } + } + } +} diff --git a/third_party/opa/build/run-wasm-rego-tests.sh b/third_party/opa/build/run-wasm-rego-tests.sh new file mode 100755 index 000000000000..51bbdaf3f86b --- /dev/null +++ b/third_party/opa/build/run-wasm-rego-tests.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +# This script executes the Wasm Rego test cases. The script uses Docker to run +# the test generation program and then again to run the test cases inside of a +# Node JS container. The script caches the test generation program build +# results in the $PWD/.go directory so that it can be re-used across runs. The +# volumes from the test generation container are shared with the Node JS +# container to avoid copying the generated test cases more than necessary. + +set -ex + +GOVERSION=${GOVERSION:?"You must set the GOVERSION environment variable."} +DOCKER_UID=${DOCKER_UID:-$(id -u)} +DOCKER_GID=${DOCKER_GID:-$(id -g)} +ASSETS=${ASSETS:-"$PWD/v1/test/wasm/assets"} +VERBOSE=${VERBOSE:-"0"} +TESTGEN_CONTAINER_NAME="opa-wasm-testgen-container" +TESTRUN_CONTAINER_NAME="opa-wasm-testrun-container" +WASM_BUILD_ONLY=${WASM_BUILD_ONLY:-"false"} + +function main { + trap interrupt SIGINT SIGTERM + mkdir -p $PWD/.go/cache/go-build + mkdir -p $PWD/.go/bin + generate_testcases + if [[ "${WASM_BUILD_ONLY}" != "true" ]]; then + run_testcases + else + echo "Running wasm tests disabled by environment variable." + fi +} + +function interrupt { + echo "caught interrupt: exiting" + purge_testgen_container + purge_testrun_container + exit 1 +} + +function purge_testgen_container { + docker kill $TESTGEN_CONTAINER_NAME >/dev/null 2>&1 || true + docker rm $TESTGEN_CONTAINER_NAME >/dev/null 2>&1 || true +} + +function purge_testrun_container { + docker kill $TESTRUN_CONTAINER_NAME >/dev/null 2>&1 || true + docker rm $TESTRUN_CONTAINER_NAME >/dev/null 2>&1 || true +} + +function generate_testcases { + purge_testgen_container + docker run \ + --name $TESTGEN_CONTAINER_NAME \ + -u $DOCKER_UID:$DOCKER_GID \ + -v $PWD/.go/bin:/go/bin:Z \ + -v $PWD:/src:z \ + -v $ASSETS:/assets:Z \ + -e GOCACHE=/src/.go/cache \ + -w /src \ + golang:$GOVERSION \ + sh -c 'git config --global --add safe.directory /src && make wasm-rego-testgen-install \ + && wasm-rego-testgen \ + --input-dir=/assets \ + --runner=/src/v1/test/wasm/assets/test.js \ + --output=/src/.go/cache/testcases.tar.gz' +} + +function run_testcases { + # NOTE(tsandall): background the container because the interrupt trap does + # not run otherwise. + purge_testrun_container + docker run \ + --rm \ + --name $TESTRUN_CONTAINER_NAME \ + --volumes-from $TESTGEN_CONTAINER_NAME:z \ + -e VERBOSE=$VERBOSE \ + -w /scratch \ + node:14 \ + sh -c 'tar xzf \ + /src/.go/cache/testcases.tar.gz \ + && node test.js opa.wasm' & + wait $! +} + +main diff --git a/third_party/opa/build/time-bound.sh b/third_party/opa/build/time-bound.sh new file mode 100755 index 000000000000..bd6b788631af --- /dev/null +++ b/third_party/opa/build/time-bound.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +usage() { + echo "time-bound.sh

Z6s(kF)(SB%x-|UT3v4O}zRZEX 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